summaryrefslogblamecommitdiff
path: root/sos-code-article2/extra/bootsect.S
blob: f01ca2055484a3777aad0041008e8b955faf0c6e (plain) (tree)








































































































































































































































































































































































































                                                                                
/*
 * @(#) $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