You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

393 lines
12 KiB

/*
* @(#) $Id: bootsect.S,v 1.6 2004/06/18 07:43:51 d2 Exp $
* Description : Bootsecteur en syntaxe AT&T
* Auteurs : Thomas Petazzoni & Fabrice Gautier & Emmanuel Marty
* Jerome Petazzoni & Bernard Cassagne & coffeeman
* David Decotigny
* 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). Le noyau peut au max tenir sur
* SECTORS_TO_LOAD secteurs
* - 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
#define SECTORS_TO_LOAD 128 /* 64 ko */ /* MAX=1264 */
/*
* 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 */
/* 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
/* 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 dfinie par es:0, o es vaut
initialement 0x100 (ie correspond alors l'adresse 256*16, soit 4
ko). Chaque itration incrmente ce registre es de 32, ce qui
correspond un bond de 32*16 en mmoire, soit la taille d'un
secteur. De cette faon, puisqu'on joue sur les segments plutt que
sur les offsets, la taille du noyau n'est pas limite 64 ko. Elle
est limite par contre la taille de la mmoire 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:
/* Prparation du flat mode */
lgdt gdtr
GoPMode:
/* Passage en mode protg */
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
/* 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 dbut ... */
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 "-= S O S =- : The Simple Operating System \r\n"
check: .string "Checking for a 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"
/*** Les code/donnes du boot secteur se terminent ICI. le marqueur de
* fin (aa55) est ajout 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 ".bss", cad HORS du boot
secteur ! (sinon ca depasserait 512B, forcment). On aurait pu la
dfinir directement dans le sos_bsect.lds, ou dans un fichier .c
auxiliaire pour plus de clart */
.comm stack, BOOT_STACK_SIZE