2009
07.19

Pour inaugurer l’ouverture de mon nouveau blog, je vous expose mon projet actuel qui est porté sur le développement d’un système d’exploitation (minimaliste), sur ce post je vous parlerais du bootloader.

Me revoilà sur mon nouveau blog, mettez à jour vos rss ;).

Ca fait bientôt presque deux mois que je n’ai rien posté (comme me le fait remarquer dilem) ..et bien oui j’ai eu moi aussi mes projets/rapports à rendre, et une période de partiel, mais à présent je suis _enfin_ en vacances.

Après mure réflexion, je décide de scinder mon article prévu en deux ou plus ; tout simplement pour peut-être détaillé un peu plus ce que j’ai pu réaliser, mais aussi pour montrer l’avancement du projet.
Durant quelques articles (je ne compte pas m’éterniser sur ce sujet), je vais vous parlez du développement de mon petit système d’exploitation.

L’idée m’est venu après une conversation avec deimos, en effet il avait lui aussi dans son passé tenté l’expérience et qu’au final il avait appris énormément de choses ; que ce soit au niveau des mécanismes / fonctionnement d’un OS basique, mais aussi sur les fonctionnalités proposés par le CPU.
Le projet s’annonce plutôt long, je vous exposerais ainsi mon avancement au fur et à mesure du projet, cependant je dévoilerais les sources seulement quand je l’aurais achevé : il sera d’ailleurs peut-être released avec une petite surprise à voir :).

Pour la suite de l’article je considère qu’il ne faut pas vraiment de connaissances pré-requises.
Il faut cependant énormément lire, aller voir à droite à gauche, les implémentations possibles etc en bref faut vraiment être motiver.

Concernant le projet, je ne me suis pas fixer de limite, ou objectif spécial à atteindre, le projet va peut-être s’éteindre très vite, ou au contraire perdurer quelques temps.

Concernant l’environnement mis en œuvre pour le développement d’hydropon-1K, j’utilise nasm/gcc/ld pour la génération de mon code, ainsi que objcopy pour la génération au format COM, et copy pour copier le bootloader et le noyau dans un même fichier.
J’ai ensuite choisis Bochs qui est un émulateur d’architecture x86, pourquoi Bochs ? et bien j’ai constaté qu’il possédait une interface de debug intégrée :).
Et tout cela sous windows bien sûr :p.

Je pense que j’en ai finis avec le blabla d’introduction etc, place à l’action.

I] Bootloader
Le bootloader ou secteur de boot est le premier programme chargé par votre ordinateur (après le bios), en effet le bios va scanner les périphériques de votre machine à la recherche d’image bootable.

Pour indiquer qu’elle est bootable, elle doit posséder tout simplement une signature (0xAA55 <-> 44 55) à la fin de son code, et la seconde contrainte est que l’image doit être codé sur 1 secteur : 512octets donc (Si le code du bootloader est inférieur à cette taille, on devra ajouter du bourrage bien sûr).
Concernant la priorité de boot des périphériques, elle est bien sur interchangeable dans les configurations du bios.

Votre bios va alors charger cette image à l’adresse 0x00007c00 (adresse est physique bien sûr) pour enfin lui laisser la main.
Celui-ci devra être en mode 16bits, et respecter le format COM (dans les options de génération de code on l’appelle souvent « binary« ) ! Ce format s’obtient facilement que ce soit avec nasm/MASM, ce qui vous laisse complètement libre dans le choix de vos binaire.

Une fois la main passé à votre bootloader, il est libre d’utiliser les interruptions proposées par le BIOS, en ce qui me concerne j’ai utilisé seulement l’interruption 0x10 et 0x13.
L’une pour l’affichage d’un message, et l’autre pour le chargement du kernel en mémoire (le rôle principal du bootloader).

Les interruptions du BIOS sont à priori « normées« , le bootloader n’a donc pas besoin de penser portabilités au niveau de l’utilisation des interruptions.

La première étape, est d’initialiser les segments qui vont nous être utile (les interruptions les sollicitent souvent). Cependant, nous ne sommes pas (encore) en mode protégé, nous somme en mode réel, je dois donc vous parler un peu de l’adressage en mode réel.

L’adressage dans ce mode est un peu particulier, car il faut savoir que lorsque nous sommes en real mode nous ne pouvons accéder à des adresses beaucoup plus grande que 1Mo (0x100000). La taille de ces adresses sera donc de 2,5 octets.

Les adresses étant de la forme Segment:Offset avec Segment codé sur 2 octets, et Offset codé sur 2 octets ce qui nous forme une adresse de 4octets, alors qu’il nous en faut une de 2,5 octets.
L’astuce pour se retrouver avec une adresse final de 20bitsest la suivante: l’adresse pointée par Segment:Offset sera Segment*16+offset (on décale d’un nibbles vers la gauche, on rajoute donc un 0).
Ou encore (((Segment<<4)+offset)&0xFFFFF). Avec un exemple ça sera plus simple : 0x1337:0x1002 nous donnera l’adresse suivante : 0x1337*16+0x1002 soit 0x13370+0x1002=0x14372dont la taille est de 2,5 octets :).

Et dans le cas limite, nous avons 0xFFFF:0xFFFF soit 0xFFFF<<4=0xFFFF0, 0xFFFF0+0xFFFF=0x10FFEF (> 1Mo).
Dans ce cas, le système ne pouvant pas adresser une adresse supérieur a 2,5octet il va arrondir l’adresse à 0x0FFEF ; celle-ci étant de 2,5octets.

Tant que nous y sommes, il faut que je vous parle de l’A20 gates, c’est un procédé qui a été mis en place afin de garder compatible les programmes (entres le 80286 et le 80186). A l’origine le processeur 8086 était capable d’adresser sur 20bits, seulement lors de la sortis du 80186 celui ci était capable d’adresser sur 24bits (plus de 16mo) ; dans une perspective de compatibilité le 20 ème bit (A20) fut mis à 0 par défaut, il suffit de le ré-activer pour permettre un adressage supérieur à 1Mo.
Pour bien comprendre, imaginons que nous passons dans le mode protégé sans activer l’A20 ; nous pouvons maintenant adresser 4go : 0xFF133700 deviendrait 0xFF033700 (le bit 20 à 0)..vous comprenez tout de suite que toute la mémoire ne serait pas adressable, et nous nous retrouverons avec des « arrondis » de nos adresses.

Lorsque votre BIOS démarre, il active lui même l’A20 car son point d’entrée se situe en 0xFFFFFFF0, avec l’A20 de désactive 0xFFFFFFF0 serait arrondis en 0xFFEFFFF0.
En revanche, une fois son code exécute, il le désactive par soucis de compatibilité, c’est donc à votre système de le ré-activer.

Mais il se trouve que Bochs active lui même l’A20, nous aurons donc pas à nous soucier de comment l’activer :).

Bon c’est surement mal expliqué (je fais de mon mieux), mais c’est un point qui est important dans le mode réel, c’est l’une de ses caractéristiques d’ailleurs.

Comme je le disais plus haut, tant que nous sommes dans le mode réel le bios met à notre disposition un lot d’interruptions utilisables.
Pour montrer un exemple, avec l’interruption n° 0x10 et le service n° 0xE nous pouvons afficher un caractère, la fonction suivante illustre l’affichage d’une chaine :

;La fonction affiche une chaine de caractère null-terminated
;Il faut que DS:SI pointe sur la chaine que l'on veut afficher, aucun retour
printf:
push ax
 
debut_printf:
lodsb                      ;ds:(e)si dans al
cmp al, 0
je fin_printf
 
mov ah, 0xE ;Appel du service n° 0xE, intéruption n°16 du bios
int 0x10     ;#  INT 0x10, AH = 0xE -- display char
jmp debut_printf
 
fin_printf:
pop ax
ret

Nous voyons que l’instruction lodsb par exemple sollicite le segment DS ; en effet DS:SI doit pointé sur le caractère que l’on veut placer dans la partie basse du registre AX (AL donc).

Mais l’interruption qui va le plus nous intéressée est la n° 0x13 service n° 2 : celle-ci va nous permettre de lire x secteurs (1secteur = 512octets) d’un disque par exemple pour chargé ces x secteurs a une adresse précise.

Pour ma part, j’ai choisis de générer un binaire composé du bootloader et du noyau, je me retrouve au final avec un fichier composé du bootloader (les 512 premiers octets, le 1er secteur), et enfin du noyau ; il me reste alors à chargé en mémoire les données qui se situent à partir du second secteur pour chargé le noyau en mémoire.

Le problème ici, est de savoir sur quel périphérique nous allons lire le noyau, il faut savoir que lorsque le bios donne la main à votre bootloader, il place dans le registre DL l’identifiant du périphérique de boot. Étant donné que notre bootloader et noyau se trouvent sur le même périphérique, j’aurais juste à spécifier cette identifiant lors de l’appel à l’interruption 0x13 (l’identifiant du périphérique doit être placé dans le registre DL).

mov ax, AdresseBase
mov es, ax                 ;ES:BX Pointe sur la zone où le kernel va être copié
mov bx, 0
 
mov ah, 2                  ;Read Sectors From Drive
mov al, NbSecteur       ;Nombre de secteur à lire
mov ch, 0                  ;
mov cl, 2                   ;
mov dh, 0                  ;
mov dl, [bootPeriphId]
int 0x13

Nous y voilà, un bootloader qui charge notre noyau en mémoire.
La prochaine étape est de passé en mode protected ; ce mode nous permet tous d’abord d’adresser jusqu’à 4go, va nous permettre de mettre en place une gestion des niveaux de privilèges (ring processeur), organiser l’espace mémoire avec de la segmentation, et enfin faire abstraction des adresses physiques pour mettre en place de la mémoire virtuelle avec de la pagination.
C’est vraiment ici, que l’aventure commence :)).

Pour activer ce mode, il suffit de mettre à 1 un bit du registre de contrôle n°0 (CR0) ; c’est le PE.cr0 bit n°0 (LSB).

C’est à priori pas le rôle du bootloader, en revanche j’ai choisis de l’intégré dans le mien ; cependant il faut savoir que si cet os était destiné à être chargé par un quelconque autre bootloader que le mien, il faudrait qu’il intègre lui même une partie qui se charge de passé en mode protégé.

Continuons, il faut savoir que si l’on active le mode protégé, nous sommes obligé de mettre en place (plus ou moins) de la segmentation.
Il faut forger alors notre propre GDT, c’est une table qui contient des descripteurs de segments, ceux ci vont décrire une partie de la mémoire.
J’ai choisis de mettre en place seulement deux segments :

  • un segment de code pour le noyau qui cloisonne l’exécution du code dans la zone 0x1000-0xYYYY (yyyy evolue en fonction de la taille du noyau).
  • un segment de données pour le noyau, qui lui, est de type flat model (il décrit toute la mémoire).

Une fois respecté les structures des descripteurs de segment, nous devons charger dans le registre GDTR l’adresse de notre GDT, et ceci grâce à l’instruction LGDT.

Concernant d’amples détails techniques sur l’élaboration des descripteurs de segments, je vous renvois au manuel intel volume 3A, en parler ici ne ferait que répéter ce qui a déjà été mis sur papier de façon clair.

Nous pouvons maintenant activer le mode protégé :

mov eax, cr0
or ax, 1
mov cr0, eax

Petite précision, il faut bien sur masque les interruptions avant de passer dans le mode protégé (instruction cli), le système d’adressage va changé, la gdt aussi, les fonctions chargés de traité les interruptions ne seront donc plus utilisable (nous ne pouvons plus utilisé les fonctions proposé par le bios).
Nous les démasquerons (instruction sti) dans le noyau, une fois que nous aurons mis en place notre IDT.

Il nous reste maintenant plus qu’a initialiser nos sélecteur de segments, le sélecteur 0x8 pour le segment de code, et le sélecteur 0x10 pour le segment de données (qui nous servira pour la pile). Petite précision, pour pouvoir modifier la valeur du segment CS, il faut faire un saut inter-segment, un jmp far donc, nous le réaliserons pour sauter sur notre kernel en mémoire.

mov ax, 0000000000010000b ;Segment de donnée, indice 2 dans la GDT, RPL=0
mov ds, ax
mov fs, ax
mov gs, ax
mov es, ax
 
mov ss, ax
mov esp, 0x8f00
mov ebp, 0x8000
 
; saut vers le kernel, reinitialisation du segment selectors de code (ds)
jmp dword 0000000000001000b:0 ;Segment de code, indice 1 dans la GDT, RPL=0

Pour résumer un peu le tout, nous avons survolé la mise en place de la segmentation avec la GDT, le chargement du noyau en mémoire, le passage en mode protégé, le problème de l’a20 gates en gros vous avez toutes les notions en main pour coder votre bootloader :).

Concernant, l’avancement du projet, j’ai donc développer le bootloader, le noyau en C ; le noyau configure les contrôleurs d’interruptions, forge l’IDT, et j’ai aussi codé la gestion des interruptions relative à la pression des touches du clavier.
Je vais m’attaquer à la mise en place de la pagination dorénavant :).

Je pense en avoir finis avec ce premier post, j’ai essayé vraiment d’être précis et en même temps synthétique pour ne pas tergiverser sur des détails qui n’en valent pas le coup.
Sinon voici un peu de lecture, en vrac avec pas mal de documentation sur l’os dev :

Have fun, o/.

Update: merci à tlk pour avoir signaler des petites boulettes!

14 commentaires pour le moment

Ajoutez votre commentaire
  1. Ah!

    Je désespérais! Enfin un nouvel article! Et pas des moindes! Très interessant! Toujours aussi mon dans le domaine mon cher Overcl0k!

  2. Ha, tu fais bien de m’y faire penser, faut que je retourne sur mon kernel moi aussi, j’en étais aux processus en ring, je posterais tout ça sur mon site bientôt ma poule.

  3. Super intéressant, et ça tombe bien j’ai lu y’a quelques jours un billet sur le bootloader de Vista. Sinon tu constates que je reste fidèle ! Je check très souvent, voir si y’a de nouvelles entrée.

    Et là comme c’est les vacances, je commence à m’y remettre.. dur
    Mais j’en profite car l’année prochaine je vais pas chômer (je vais à l’ENSIMAG ! Si tu passes un jour par Grenoble, dis-le!).

    A+

  4. Hey taron,
    Ca me fait plaisir de te voir ici :p, en effet performant le monsieur, le blog est tout neuf et il est déjà là !:]
    Ha le bootloader sur vista, l’article de b0l0k non ?

    Concernant, ton entrée à l’ENSIMAG..w00000t c’est super, tu pouvais pas chopper mieux :p.
    Et pour grenible..c’est noté :]]].
    Allez cya l’ami à la prochaine !

    Cordialement 0vercl0k.

  5. Oui c’était l’article de b0l0k.. faudrait se rencontrer un jour, si tu passes pas vers Grenoble, peut-être à une conf du genre NuitDuHack (jamais allé)..

  6. Ha bah waip j’y ai jamais mis les pieds moi aussi, ça pourrait être une bonne occasion en effet, je note ça de côté :].

    Cordialement 0vercl0k.

  7. Bon article. Faut que je m’y penche (mais y’a d’autres trucs à faire… J’ai pas 36 Threads, moi !)

    Continue comme ça, pendule !

    o/

    _Geo_

  8. Je souhaite que ton projet puisse aboutir :]

  9. Ah ba enfin =P

    Ouaaa tu m’as même posté un lien vers mon site dans ton article.
    Je vais rougir =$

    Nice l’article en tout cas ! Bon courage

  10. C’est pas mal les sites de H@cker mais si j’ai des questions au sujet du hooking je peux les poser ou ? Y’a meme pas de About ou contact me…

  11. nice tuto bro

  12. Yo dude,
    Tu peux tout simplement m’écrire un mail à 0vercl0k [a-t] tuxfamily [d-o-t] org, ou rejoindre mon canal irc #carib0u sur le serveur irc.worldnet.net.
    Cya ;).

  13. Hehe thanks ;).

  14. […] 0vercl0k’s w0rld. This entry was posted in Code and tagged 0vercl0k’s, Hydropon1K, Operating, part, SYSTEM. Bookmark the permalink. ← If you Want Free 10 $ Be Quick . Come on Friends […]

Get Adobe Flash player