439 lines
13 KiB
ArmAsm
439 lines
13 KiB
ArmAsm
|
|
|||
|
/*
|
|||
|
* @(#) $Id: bootsect.S 1310 2005-08-13 13:47:31Z d2 $
|
|||
|
* Description : Bootsecteur en syntaxe AT&T
|
|||
|
* Auteurs : Thomas Petazzoni & Fabrice Gautier & Emmanuel Marty
|
|||
|
* Jerome Petazzoni & Bernard Cassagne & coffeeman
|
|||
|
* David Decotigny (SOS integration for kernel size detection)
|
|||
|
* Christopher Goyet (RAM size determination through BIOS int 15H)
|
|||
|
* Bug reports to kos-misc@enix.org
|
|||
|
*/
|
|||
|
|
|||
|
/*
|
|||
|
* But global de ce bootsecteur :
|
|||
|
*
|
|||
|
* - Initialiser la becane
|
|||
|
* - Charger le kernel
|
|||
|
* - Passer en mode protege
|
|||
|
* - Executer le kernel
|
|||
|
*
|
|||
|
* Taille restante : Je vous rappelle qu'un bootsecteur ne peut faire
|
|||
|
* qu'au maximum 512 octets dont 2 octets obligatoires 0xAA55. Sur
|
|||
|
* les 510 octets reellement utilisables, il reste 3 octets dispo (60
|
|||
|
* si on decide d'enlever le BPB un jour) !!!
|
|||
|
*
|
|||
|
* thomas_petazzoni : - detection des codes d'erreurs de chargement
|
|||
|
* David_Decotigny : - Passage en GNU as
|
|||
|
* David_Decotigny : - Chargement du noyau au-dela du 1er Mega (taille
|
|||
|
* max = 0x9e000 octets = 632ko), pour avoir le
|
|||
|
* meme noyau sous grub et avec le bootsecteur
|
|||
|
*/
|
|||
|
|
|||
|
/*
|
|||
|
* Sequence d'operations :
|
|||
|
* - Le BIOS charge le bootsect en 0x7c00 (BOOT_ADRESS). On choisit
|
|||
|
* la representation 0x7c0:0000 pour que le .org 0 reste valide
|
|||
|
* - Le bootsect se deplace de lui-meme en 0x9f000 (COPY_ADRESS). On
|
|||
|
* choisit la representation 0x9f00:0000 pour que le .org 0 reste
|
|||
|
* valide
|
|||
|
* - Le bootsect verifie que le processeur est du type 386+
|
|||
|
* - Il charge le noyau depuis la disquette en memoire a partir de
|
|||
|
* 0x1000 (LOAD_ADRESS). La place dispo est donc 0x9f000 - 0x1000 , soit
|
|||
|
* 0x9E000, soit encore 1264 secteurs de 512 octets
|
|||
|
* - Il passe en pmode flat (apres ouverture a20)
|
|||
|
* - Il recopie le noyau (situe en LOAD_ADRESS) vers son adresse
|
|||
|
* finale (FINAL_ADDRESS = 2Mo). La recopie se fait sur tout l'espace
|
|||
|
* LOAD_ADRESS ---> COPY_ADRESS, c'est a dire sur 0x9e000 octets =
|
|||
|
* 632ko. Le noyau peut donc au max faire 632ko. Le nombre max de
|
|||
|
* secteurs de disquette qu'on peut charger est donc 1264
|
|||
|
*/
|
|||
|
|
|||
|
|
|||
|
/* La taille de la pile */
|
|||
|
#define BOOT_STACK_SIZE 0x4000
|
|||
|
|
|||
|
.file "bootsect.S"
|
|||
|
|
|||
|
/* Tout est place dans une seule section */
|
|||
|
.section ".bootsect"
|
|||
|
|
|||
|
/* L'essentiel du bootsector (sauf les 1eres instructions)
|
|||
|
sont a un offset 0. On fait en sorte que le compilo soit
|
|||
|
d'accord la-dessus. Quand on a des adresse realm exotiques
|
|||
|
(0x7c00, 0x9f000, ...), on s'arrange toujours pour avoir un
|
|||
|
offset de 0 => on choisira le segment adapte (0x7c0,
|
|||
|
0x9f00, ...). Il ne faut pas oublier le ld -Ttext 0 */
|
|||
|
.org 0
|
|||
|
|
|||
|
/* Pour que gas genere du 16bits, afin que ca marche en realm */
|
|||
|
.code16
|
|||
|
|
|||
|
/*
|
|||
|
* Parametres de la disquette. Comme c'est chiant de faire une
|
|||
|
* procedure de detection auto, et que ca prend de la place, on fait
|
|||
|
* ca "a la main". Par exemple, une DD 720 Ko a 9 secteurs/piste, une
|
|||
|
* 1.44 Mo a 18 secteurs/pistes
|
|||
|
*/
|
|||
|
#define CYLS 80
|
|||
|
#define HEADS 1
|
|||
|
#define SECTS 18
|
|||
|
|
|||
|
#define BOOT_ADRESS 0x07C00 /* Adresse de demarrage (lineaire) */
|
|||
|
#define BOOT_SEG (BOOT_ADRESS>>4) /* Segment de Boot */
|
|||
|
#define BOOT_SIZE 512 /* Taille bu bootsecteur */
|
|||
|
#define COPY_ADRESS 0x9F000 /* La ou on va copier le
|
|||
|
bootsecteur (lineaire) */
|
|||
|
#define COPY_SEG (COPY_ADRESS>>4) /* Segment de la ou on va
|
|||
|
copier le bootsecteur */
|
|||
|
#define LOAD_ADRESS 0x01000 /* 1er chargement du systeme */
|
|||
|
#define LOAD_SEG (LOAD_ADRESS>>4) /* Segment du 1er chargement du */
|
|||
|
#define MAX_KERN_LEN (COPY_ADRESS-LOAD_ADRESS) /* Taille noyau maxi */
|
|||
|
#define MAX_KERN_SECTS ((MAX_KERN_LEN + 511) >> 9) /* Nbre de secteurs maxi */
|
|||
|
|
|||
|
/* IMPORTANT : Cette valeur DOIT etre identique a l'adresse presente
|
|||
|
dans sos.lds ! */
|
|||
|
#define FINAL_ADDRESS 0x200000 /* Adresse finale (physique de 0 a 4G)
|
|||
|
ou est charge le noyau */
|
|||
|
|
|||
|
#define OP16 .byte 0x66 ;
|
|||
|
#define OP32 .byte 0x66 ;
|
|||
|
|
|||
|
/*
|
|||
|
* Procedure qui vide le buffer clavier.
|
|||
|
*/
|
|||
|
#define WAITKB \
|
|||
|
1: ;\
|
|||
|
.word 0xeb ;\
|
|||
|
.word 0xeb ;\
|
|||
|
inb $0x64, %al ;\
|
|||
|
andb $0x2, %al ;\
|
|||
|
jnz 1b
|
|||
|
|
|||
|
/* Le point d'entree dans le bootsect */
|
|||
|
.globl _bsect
|
|||
|
_bsect:
|
|||
|
|
|||
|
/*
|
|||
|
* La portion qui suit est situee a un offset 0x7c00 en
|
|||
|
* memoire. Attention donc aux references memoire dans cette
|
|||
|
* partie. On choisit de rester en offset 0 (.org 0), mais on
|
|||
|
* charge correctement les segments a 0x7c0.
|
|||
|
*/
|
|||
|
|
|||
|
movw $BOOT_SEG, %ax /* le bootsecteur est a 0x7C00 en lineaire */
|
|||
|
movw %ax, %ds /* on le copie a l'adresse COPY_ADRESS */
|
|||
|
xorw %si, %si /* comme cette adresse est la plus haute de la mem */
|
|||
|
xorw %di, %di /* on pourra charger un kernel + gros */
|
|||
|
movw $(BOOT_SIZE>>1), %cx
|
|||
|
movw $COPY_SEG, %ax
|
|||
|
movw %ax, %es
|
|||
|
cld
|
|||
|
rep ; movsw
|
|||
|
|
|||
|
/* on continue a executer le bootsecteur, mais maintenant a
|
|||
|
partir de 0x9F000, qu'on represente sous la forme
|
|||
|
0x9f00:offset */
|
|||
|
ljmp $COPY_SEG, $here
|
|||
|
|
|||
|
/*
|
|||
|
* A partir de maintenant, on est a un offset 0 en memoire
|
|||
|
* (segment 0x9f00), conformement a ce que veut le compilo.
|
|||
|
*/
|
|||
|
here:
|
|||
|
movw %ax, %ds
|
|||
|
|
|||
|
/* Petite pile temporaire (1k - 3.84k en RAM ; les adresses 0-1k
|
|||
|
correspondent au vecteur d'interruptions). */
|
|||
|
movw %ax, %ss
|
|||
|
movw $(LOAD_ADRESS - 0x10), %sp
|
|||
|
|
|||
|
/* Efface l'ecran */
|
|||
|
movb $0x0, %ah
|
|||
|
movb $0x3, %al
|
|||
|
int $0x10
|
|||
|
|
|||
|
/* Verifie que le noyau n'est pas trop gros a charger */
|
|||
|
cmpw $(MAX_KERN_SECTS), (load_size)
|
|||
|
jb sizeOk
|
|||
|
movw $toobig, %si
|
|||
|
call message
|
|||
|
call halt
|
|||
|
|
|||
|
sizeOk:
|
|||
|
/* Recupere la taille de la RAM */
|
|||
|
mov $0xE801, %ax
|
|||
|
int $0x15
|
|||
|
movw %ax, (memsize1)
|
|||
|
movw %bx, (memsize2)
|
|||
|
|
|||
|
/* Affiche les messages d'attente */
|
|||
|
movw $loadkern, %si
|
|||
|
call message
|
|||
|
movw $check, %si
|
|||
|
call message
|
|||
|
|
|||
|
check386:
|
|||
|
/*
|
|||
|
* la attention, plus complexe : on teste si le proc est un
|
|||
|
* 386+ pour cela, on va essayer de modifier les bits 12 ? 14
|
|||
|
* du registre E-flag si la modification reste, alors le proc
|
|||
|
* est un 386+, sinon, c'est =< 286
|
|||
|
*
|
|||
|
* Merci a Emmanuel Marty pour la compatibilite avec les 386
|
|||
|
* "pre-jurassique"
|
|||
|
*/
|
|||
|
|
|||
|
pushf /* on sauvegarde le E-Flag */
|
|||
|
movb $0x70, %ah
|
|||
|
pushw %ax
|
|||
|
popf
|
|||
|
pushf
|
|||
|
popw %ax
|
|||
|
orb %ah, %ah
|
|||
|
je no386 /* si la modif n'est pas valable, alors on saute a
|
|||
|
no386 */
|
|||
|
popf /* on les restaure ? la fin ... */
|
|||
|
|
|||
|
/* Message de confirmation de 386+ et d'attente */
|
|||
|
movw $found386, %si
|
|||
|
call message
|
|||
|
movw $loading, %si
|
|||
|
call message
|
|||
|
|
|||
|
/* Copie du noyau disquette => RAM a partir de 0x1000
|
|||
|
L'adresse de destination est d<EFBFBD>finie par es:0, o<EFBFBD> es vaut
|
|||
|
initialement 0x100 (ie correspond alors <EFBFBD> l'adresse 256*16, soit 4
|
|||
|
ko). Chaque it<EFBFBD>ration incr<EFBFBD>mente ce registre es de 32, ce qui
|
|||
|
correspond <EFBFBD> un bond de 32*16 en m<EFBFBD>moire, soit la taille d'un
|
|||
|
secteur. De cette fa<EFBFBD>on, puisqu'on joue sur les segments plut<EFBFBD>t que
|
|||
|
sur les offsets, la taille du noyau n'est pas limit<EFBFBD>e <EFBFBD> 64 ko. Elle
|
|||
|
est limit<EFBFBD>e par contre <EFBFBD> la taille de la m<EFBFBD>moire disponible sous
|
|||
|
les 1Mo, \ie 640 ko (0x9f000 - 0x1000). */
|
|||
|
copyKernel:
|
|||
|
/* Chargement du noyau en LOAD_SEG:0 */
|
|||
|
/* 3 iterateurs :
|
|||
|
- load_size : le nbre de secteurs a charger
|
|||
|
- cl : le secteur ou on en est pour le
|
|||
|
cylindre en cours (<= SECTS)
|
|||
|
- dh : la tete en cours (0/1)
|
|||
|
*/
|
|||
|
movb $0, %dl
|
|||
|
movw $LOAD_SEG, %ax
|
|||
|
movw %ax, %es
|
|||
|
|
|||
|
xorw %bx, %bx
|
|||
|
xorw %dx, %dx
|
|||
|
movw $1, %cx /* premier secteur */
|
|||
|
|
|||
|
.nextsector: /* prochain secteur */
|
|||
|
incb %cl /* en incrementant CL */
|
|||
|
cmpb $SECTS, %cl /* si CL =< SECTS (=nbre de secteurs/pistes)
|
|||
|
alors on charge */
|
|||
|
jbe .sector
|
|||
|
movb $1, %cl /* sinon on revient au secteur 1 */
|
|||
|
incb %dh /* mais sur l'autre tete */
|
|||
|
cmpb $1, %dh /* on recompare, si DH =< 1 */
|
|||
|
je .sector /* on charge */
|
|||
|
movb $0, %dh /* sinon on repasse a la tete 0 */
|
|||
|
incb %ch /* mais on change de cylindre */
|
|||
|
|
|||
|
.sector:
|
|||
|
pushw %es
|
|||
|
movw $0x0201, %ax /* service 0x2, chargement 0x1 seecteur */
|
|||
|
int $0x13 /* Go ! */
|
|||
|
jc halt /* erreur */
|
|||
|
popw %ax
|
|||
|
addw $32, %ax /* on a charge un secteur, donc on doit
|
|||
|
charger 512 bytes plus loin */
|
|||
|
movw %ax, %es /* on avance donc le segment du buffer de
|
|||
|
32bytes, ie 1 secteur en RAM (car 32*16=512) */
|
|||
|
|
|||
|
movw $(0x0E*256+'.'), %ax /* affiche un point */
|
|||
|
int $0x10
|
|||
|
|
|||
|
decw (load_size) /* et on repart pour le prochain secteur
|
|||
|
tant qu'on n'a pas fini ! */
|
|||
|
jnz .nextsector
|
|||
|
|
|||
|
after:
|
|||
|
movw $0x03f2, %dx
|
|||
|
inb %dx, %al /* stoppe le moteur */
|
|||
|
andb $0x0f, %al
|
|||
|
outb %al, %dx
|
|||
|
|
|||
|
cli /* on interdit les interruptions */
|
|||
|
|
|||
|
fincopie:
|
|||
|
pushw %cs
|
|||
|
popw %ds
|
|||
|
|
|||
|
/* on ouvre la porte A20 */
|
|||
|
WAITKB /* on vide le buffer */
|
|||
|
movb $0xd1, %al /* on met a jour le port */
|
|||
|
outb %al, $0x64
|
|||
|
WAITKB
|
|||
|
movb $0xdf, %al /* bit 2 = ouverture/fermeture */
|
|||
|
outb %al, $0x60
|
|||
|
|
|||
|
/*
|
|||
|
* init gdt
|
|||
|
*/
|
|||
|
InitGDT:
|
|||
|
/* Pr<50>paration du flat mode */
|
|||
|
lgdt gdtr
|
|||
|
|
|||
|
GoPMode:
|
|||
|
/* Passage en mode prot<6F>g<EFBFBD> */
|
|||
|
movl %cr0, %eax
|
|||
|
orb $1, %al /* set PE bit to 1 */
|
|||
|
movl %eax, %cr0
|
|||
|
|
|||
|
/* we are not yet in Pmode jump 'in' pmode clearing prefetch
|
|||
|
* queue and loading a new selector */
|
|||
|
movw $0x10, %ax
|
|||
|
movw %ax, %ds
|
|||
|
movw %ax, %es
|
|||
|
movw %ax, %fs
|
|||
|
movw %ax, %gs
|
|||
|
|
|||
|
/*
|
|||
|
* Code 32 bits ============================================================
|
|||
|
*/
|
|||
|
.code32
|
|||
|
|
|||
|
JumpToHere32: /* Se deplace a l'endroit actuel, en passant en 32bits
|
|||
|
et en utilisant la gdt, et vide la prefetch queue */
|
|||
|
.byte 0x66 /* Prefixe 32bits : en realite, jusqu'au jmp, on est
|
|||
|
encore en 16 bits */
|
|||
|
ljmp $0x8, $(COPY_ADRESS+(Here32))
|
|||
|
Here32:
|
|||
|
/* Et voila : On est en 32 bits vrai */
|
|||
|
|
|||
|
MoveKernelToFinalAddr: /* Deplace le noyau (en LOAD_ADDRESS) vers sa
|
|||
|
destination finale (FINAL_ADDRESS) */
|
|||
|
movl $0x10, %eax
|
|||
|
movl %eax, %ds /* Seg Src = DSeg */
|
|||
|
movl %eax, %es /* Sed Dest = DSeg */
|
|||
|
cld
|
|||
|
movl $LOAD_ADRESS, %esi /* On commence la copie au debut du noyau */
|
|||
|
movl $FINAL_ADDRESS, %edi /* On copie vers cette adresse */
|
|||
|
movl $MAX_KERN_LEN, %ecx /* Taille recopie */
|
|||
|
shrl $2, %ecx
|
|||
|
rep
|
|||
|
movsl
|
|||
|
|
|||
|
LaunchKernel:
|
|||
|
/* Met en place une pile au niveau du symbole "stack" */
|
|||
|
movl %eax, %ss
|
|||
|
movl $(stack + BOOT_STACK_SIZE), %ebp
|
|||
|
movl %ebp, %esp
|
|||
|
|
|||
|
/* passe les arguments a sos */
|
|||
|
xor %eax, %eax
|
|||
|
xor %ebx, %ebx
|
|||
|
movw (COPY_ADRESS+(memsize2)), %ax /*eax = num de block de 64KB apres 16MB*/
|
|||
|
movw (COPY_ADRESS+(memsize1)), %bx /*ebx = num de block de 1KB entre 1MB et 16MB*/
|
|||
|
movl $0x40, %ecx /*ecx=64 */
|
|||
|
mul %ecx
|
|||
|
add %ebx, %eax
|
|||
|
pushl %eax /* valeur de addr */
|
|||
|
pushl $0x42244224 /* valeur de magic pour indiquer qu'on a pousse
|
|||
|
la taille de la RAM sur la pile */
|
|||
|
pushl $0 /* normalement call fait un push eip, mais la on a un jmp*/
|
|||
|
|
|||
|
/* Saut vers le noyau. La GDT est en place (flat mode), les
|
|||
|
* selecteurs aussi, a20 est ouverte, et les interruptions sont
|
|||
|
* cli + pas de idt. Le PIC n'est pas programme */
|
|||
|
ljmp $0x8, $sos_main
|
|||
|
|
|||
|
/*
|
|||
|
* Utilities ============================================================
|
|||
|
*/
|
|||
|
.code16
|
|||
|
|
|||
|
message:
|
|||
|
lodsb /* charge ds:si dans al et incremente si */
|
|||
|
orb %al, %al /* si al = 0 */
|
|||
|
jz 1f
|
|||
|
movb $0x0e, %ah /* service 0Eh (affichage d'un caractere) */
|
|||
|
movw $0x0007, %bx /* Parametres : blanc sur fond noir */
|
|||
|
int $0x10 /* Appel de l'interruption 10h */
|
|||
|
jmp message /* On repart au d<EFBFBD>but ... */
|
|||
|
1: ret /* si la chaine est finie alors on retourne
|
|||
|
dans la fonction appelante */
|
|||
|
|
|||
|
halt:
|
|||
|
pushw %cs
|
|||
|
popw %es
|
|||
|
movw $haltmsg, %si
|
|||
|
call message
|
|||
|
cli
|
|||
|
1: jmp 1b
|
|||
|
ret
|
|||
|
|
|||
|
no386:
|
|||
|
movw $need386, %si
|
|||
|
call message
|
|||
|
call halt
|
|||
|
|
|||
|
/*
|
|||
|
* GDT
|
|||
|
*/
|
|||
|
|
|||
|
gdt:
|
|||
|
gdtr:
|
|||
|
NULL_Desc:
|
|||
|
.word (EndGDT)-(gdt)-1 /* Taille GDT */
|
|||
|
.long (gdt)+COPY_ADRESS
|
|||
|
unused:
|
|||
|
.word 0
|
|||
|
|
|||
|
CS_Desc: /* 0x8 */
|
|||
|
.word 0xFFFF, 0
|
|||
|
.byte 0, 0x9B, 0xCF, 0
|
|||
|
|
|||
|
DS_Desc: /* 0x10 */
|
|||
|
.word 0xFFFF, 0
|
|||
|
.byte 0, 0x93, 0xCF, 0
|
|||
|
|
|||
|
EndGDT:
|
|||
|
|
|||
|
/* quelques messages */
|
|||
|
|
|||
|
loadkern: .string "This is SOS\r\n"
|
|||
|
toobig: .string "Image too big\r\n"
|
|||
|
check: .string "Checking 386+ processor... "
|
|||
|
found386: .string " [OK]\r\n"
|
|||
|
need386: .string " [FAILED]\r\n"
|
|||
|
diskerror: .string "Disk Error\r\n"
|
|||
|
loading: .string "Loading... "
|
|||
|
haltmsg: .string "System Halted\r\n"
|
|||
|
|
|||
|
/* Variables pour stocker la taille de la RAM (int 0x15) */
|
|||
|
memsize1: .long 0
|
|||
|
memsize2: .long 0
|
|||
|
|
|||
|
/*** Les code/donn<EFBFBD>es du boot secteur se terminent ICI. le marqueur de
|
|||
|
* fin (aa55) est ajout<EFBFBD> automatiquement par le script ld
|
|||
|
* sos_bsect.lds ***/
|
|||
|
|
|||
|
/* La pile de 16k qu'on utilise au niveau de LaunchKernel se trouve
|
|||
|
declaree avec le noyau, dans sa section ".init_stack", cad HORS du boot
|
|||
|
secteur ! (sinon ca depasserait 512B, forc<EFBFBD>ment). On aurait pu la
|
|||
|
d<EFBFBD>finir directement dans le sos_bsect.lds, ou dans un fichier .c
|
|||
|
auxiliaire pour plus de clart<EFBFBD> */
|
|||
|
/* Here is the stack */
|
|||
|
.section ".init_stack", "aw", @nobits
|
|||
|
.p2align 4
|
|||
|
.size stack, BOOT_STACK_SIZE
|
|||
|
stack:
|
|||
|
.space BOOT_STACK_SIZE
|
|||
|
|
|||
|
/* Some data characterizing the stack addresses */
|
|||
|
.data
|
|||
|
.globl bootstrap_stack_bottom
|
|||
|
bootstrap_stack_bottom: .long stack
|
|||
|
|
|||
|
.globl bootstrap_stack_size
|
|||
|
bootstrap_stack_size: .long BOOT_STACK_SIZE
|