summaryrefslogtreecommitdiff
path: root/sos-code-article4/extra/bootsect.S
diff options
context:
space:
mode:
Diffstat (limited to 'sos-code-article4/extra/bootsect.S')
-rw-r--r--sos-code-article4/extra/bootsect.S393
1 files changed, 393 insertions, 0 deletions
diff --git a/sos-code-article4/extra/bootsect.S b/sos-code-article4/extra/bootsect.S
new file mode 100644
index 0000000..f01ca20
--- /dev/null
+++ b/sos-code-article4/extra/bootsect.S
@@ -0,0 +1,393 @@
+
+/*
+ * @(#) $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 définie par es:0, où es vaut
+ initialement 0x100 (ie correspond alors à l'adresse 256*16, soit 4
+ ko). Chaque itération incrémente ce registre es de 32, ce qui
+ correspond à un bond de 32*16 en mémoire, soit la taille d'un
+ secteur. De cette façon, puisqu'on joue sur les segments plutôt que
+ sur les offsets, la taille du noyau n'est pas limitée à 64 ko. Elle
+ est limitée par contre à la taille de la mé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éparation du flat mode */
+ lgdt gdtr
+
+GoPMode:
+ /* Passage en mode protégé */
+ 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 dé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 "-= 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/données 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, forcément). On aurait pu la
+ définir directement dans le sos_bsect.lds, ou dans un fichier .c
+ auxiliaire pour plus de clarté */
+.comm stack, BOOT_STACK_SIZE