print · rss · source

< Réaliser un secteur de boot qui affiche un message | TutoOS | Réaliser un secteur de boot qui passe en mode protégé >


Réaliser un secteur de boot qui charge et exécute un noyau

Sources

Le package contenant les sources est téléchargeable ici : bootsect.tgz.
Pour naviguer dans l'arborescence : BootSector


Le programme du boot loader

La partie précedente explique par l'exemple les principes de fonctionnement d'un programme de boot. Dans cette partie, nous allons voir un programme de boot plus raffiné qui, après avoir affiché un message, charge en mémoire un noyau très rudimentaire et lui passe la main. Là encore, nous allons faire au plus simple et le noyau se contentera seulement d'afficher un message.

Ce programme ressemble beaucoup à celui du chapitre précédent. En fait, c'est le même avec juste quelques lignes de code en plus qui copient une partie de la disquette, contenant le noyau, en mémoire :

%define BASE   0x100  ; 0x0100:0x0 = 0x1000
%define KSIZE  1  ; nombre de secteurs de 512 octets a charger

[BITS 16]
[ORG 0x0]

jmp start
%include "UTIL.INC"
start:

; initialisation des segments en 0x07C0
    mov ax, 0x07C0
    mov ds, ax
    mov es, ax
    mov ax, 0x8000  ; stack en 0xFFFF
    mov ss, ax
    mov sp, 0xf000

; recuparation de l'unite de boot
    mov [bootdrv], dl  

; affiche un msg
    mov si, msgDebut
    call afficher

; charger le noyau
    xor ax, ax
    int 0x13

    push es
    mov ax, BASE
    mov es, ax
    mov bx, 0

    mov ah, 2
    mov al, KSIZE
    mov ch, 0
    mov cl, 2
    mov dh, 0
    mov dl, [bootdrv]
    int 0x13
    pop es

; saut vers le kernel
    jmp dword BASE:0


msgDebut: db "Chargement du kernel", 13, 10, 0

bootdrv: db 0

;; NOP jusqu'a 510
times 510-($-$$) db 144
dw 0xAA55

Que fait exactement ce programme ?

jmp start
    %include "UTIL.INC"
    start:

Le programme commence par un saut à l'adresse start. La directive include ajoute au code du noyau le contenu du fichier UTIL.INC qui contient le code de la fonction afficher vue précedement.

; recuparation de l'unite de boot
    mov [bootdrv], dl

Cette instruction met dans une variable un nombre servant à identifier le périphérique de boot (ici le lecteur de disquettes). Cette variable sera réutilisée plus tard pour indiquer à partir de quel périphérique doit être chargé le noyau.

; charger le noyau
    xor ax, ax
    int 0x13

    push es
    mov ax, BASE
    mov es, ax
    mov bx, 0

    mov ah, 2
    mov al, KSIZE
    mov ch, 0
    mov cl, 2
    mov dh, 0
    mov dl, [bootdrv]
    int 0x13
    pop es

Le bout de code ci-dessus charge le noyau en mémoire en faisant appel à l'interruption 0x13 du BIOS qui permet de copier un ou plusieurs secteurs d'une disquette en mémoire. Dans notre cas, le noyau se situe au début du second secteur de la disquette et le programme recopie ce secteur à l'adresse 0x1000 en RAM. Notez que le choix de cette adresse est arbitraire et on aurait très bien pu choisir une autre valeur.

L'adresse physique 0x1000 est adressée ici en mettant le sélecteur à 0x0100. Mais on aurait très bien pu procéder autrement, en utilisant un sélecteur à 0 et un offset de 0x1000, ça aurait aussi fonctionné !

La variable KSIZE définit le nombre de secteurs à charger pour que tout le noyau soit bien recopié en mémoire. Ce premier noyau, qui est décrit dans la partie suivante, est très court (pas plus de 100 octets). On peut donc se contenter de mettre la valeur de KSIZE à 1 pour copier un seul secteur.

; saut vers le kernel
    jmp dword BASE:0

Ensuite, une instruction de saut donne la main au code du noyau.

Organiser la mémoire

Programmer un secteur de boot et un noyau, cela signifie entre autres organiser l'occupation en mémoire des différents composants. Pour ne pas vous y perdre, je vous conseille d'utiliser des petits schémas. Les 5 minutes passées à crayonner sur un bout de papier vous ferons parfois économiser des heures de débogage ! Dans notre cas, la mémoire est occupée de la façon suivante après le chargement du noyau :

Un premier noyau

Voici le programme du noyau :

[BITS 16]
[ORG 0x0]

jmp start

%include "UTIL.INC"

start:
; initialisation des segments en 0x100
    mov ax, 0x100
    mov ds, ax
    mov es, ax

; initialisation du segment de pile
    mov ax, 0x8000
    mov ss, ax
    mov sp, 0xf000

; affiche un msg
    mov si, msg00
    call afficher

end:
    jmp end


msg00: db 'Kernel is speaking !', 10, 0

Ce programme initialise le registre de code et les registres de données afin qu'ils pointent sur la bonne zone mémoire, en 0x1000. Ensuite, un message est affiché pour attester de la réussite des opérations. A la ligne suivante, le noyau ne fait vraiment pas grand chose : il boucle indéfiniment.

Compiler et tester le boot loader et le noyau

Le package contenant les sources est téléchargeable ici : bootsect.tgz.
Le code se décompose en :

  • un fichier librairie, UTIL.INC, qui contient la fonction afficher
  • un fichier qui contient le code du secteur de boot
  • un fichier qui contient le code du noyau.
$ ls 
UTIL.INC bootsect.asm kernel.asm

On compile le boot loader et le noyau séparement :

$ nasm -f bin -o bootsect bootsect.asm
$ nasm -f bin -o kernel kernel.asm

On remarque que le binaire du secteur de boot fait comme prévu 512 octets :

$ ls -l
total 14
-rw-r--r--   1 am       users        492 Jan 17 17:20 UTIL.INC
-rw-r--r--   1 am       users        512 Jan 19 18:16 bootsect
-rw-r--r--   1 am       users        715 Jan 17 17:22 bootsect.asm
-rw-r--r--   1 am       users        297 Jan 17 17:50 kernel.asm
-rw-r--r--   1 am       users         69 Jan 19 18:16 kernel

On remarque aussi que le kernel fait seulement 69 octets, soit moins d'un secteur de disquette (512 octets), ce qui permet d'utiliser une valeur très basse pour KSIZE. Au cas où le kernel occupe davantage de place, il faut augmenter cette valeur. Malheureusement, celà fonctionne dans une certaine limite car la lecture de données sur disque est complexe. Pour en savoir plus :

La disquette que nous allons faire aura le noyau placé sur le deuxieme secteur. On réalise une image de la disquette avec la commande suivante :

$ cat bootsect kernel /dev/zero | dd of=floppyA bs=512 count=2880
$ ls -l floppyA
-rw-r--r--   1 am       users    1474117 Jan 19 18:27 floppyA

< Réaliser un secteur de boot qui affiche un message | TutoOS | Réaliser un secteur de boot qui passe en mode protégé >

print · rss · source
Page last modified on September 22, 2010, at 09:43 AM