Écrivez votre mini OS (Partie 3)

Gazette Linux n°082 — Septembre 2002

Chitkala

Sébastien Marbrier

Adaptation française 

Prénom Nom du relecteur

Relecture de la version française 

Article paru dans le n°082 de la Gazette Linux de septembre 2002.

Cet article est publié selon les termes de la Open Publication License. La Linux Gazette n'est ni produite, ni sponsorisée, ni avalisée par notre hébergeur principal, SSC, Inc.


Table des matières

1. Qu'est-ce que le Mode Protégé ?
2. Activation du Mode Protégé
3. Nos besoins
4. Le code qui fait tout !
Fonctions utilisées
C'est ici que tout commence !

[Note de l'Éditeur : En raison d'autres engagements, Krishnakumar n'est plus en mesure d'achever cette série d'articles, il a donc passé la main à ses jeunes collègues, Raghu et Chitkala qui ont écrit cette partie. ]

Dans les parties I et II, nous avons étudié l'utilisation d'outils disponibles sous Linux pour fabriquer un petit secteur d'amorçage et accéder aux BIOS du système Notre mini système d'exploitation sera basé sur le modèle d'un noyau Linux 'historique', nous basculerons très bientôt dans le mode protégé ! Cet article vous expliquera comment faire.

1. Qu'est-ce que le Mode Protégé ?

Les 80386 et supérieurs apportent de nombreuses fonctionnalités nouvelles pour remédier aux défauts du 8086 qui ne possède presqu'aucune fonctionnalité pour la protection de la mémoire, la mémoire virtuelle, le multitâche, ou la mémoire au-delà des 640K — tout en restant compatible avec la famille 8086. Le 386 a hérité de toutes les caractéristiques du 8086 et du 286, avec beaucoup d'améliorations . Tout comme les processeurs précédents il dispose du mode réel. Et comme le 286, le 386 peut fonctionner en mode protégé. Cependant, la mécanique interne du mode protégé du 386 est extrêmement différente. Le mode protégé du 386 apporte au programme une meilleure protection et plus de mémoire que le 286. La raison d'être du mode protégé n'est pas de protéger votre programme mais protéger tout le reste (y compris le système d'exploitation) de votre programme.

1.1 Différences entre le Mode Protégé et le Mode Réel

D'un point de vue superficiel, les modes réel et protégé ne semblent pas très différents. Les deux utilisent la segmentation mémoire, les interruptions et les pilotes de périphériques pour gérer le matériel. Mais il y a des différences qui justifient l'existence de deux modes distincts. Avec le mode réel, on peut représenter la mémoire sous la forme de 64 000 segments séparés d'au moins 16 bits. La segmentation est prise en charge par un mécanisme interne, utilisant des registres de segment. Le contenu de ces registres de segments (CS, DS, SS… ) forme l'adresse physique que l'unité centrale écrit sur le bus d'adresses. L'adresse physique est générée en multipliant le registre de segment par 16 et en ajoutant ensuite un décalage de 16 bits. C'est ce décalage de 16 bits qui nous limite à 64 000 segments.

fig 1 : Adressage en Mode Réel

Dans le mode protégé, la segmentation est définie au moyen d'un ensemble de tables appelés tables de descripteurs. Les registres de segment contiennent les pointeurs sur ces tables. Deux types de tables sont utilisés pour définir la segmentation de la mémoire : La Table des Descripteur Généraux et la Table des Descripteurs Locaux. La TDG contient les descripteurs élémentaires auxquels toutes les applications ont accès. Dans le mode réel, un segment a une taille de 64Ko et est séparé du suivant par 16 octets. Dans le mode protégé, la taille des segments peut aller jusqu'à 64Go et nous pouvons le positionner où nous le voulons. La TDL contient les informations de segmentation propres à une tâche ou à un programme. Par exemple, un système d'exploitation pourrait définir une TDG avec ses descripteurs systèmes et une TDL avec les descripteurs appropriés pour chaque tâche. Chaque descripteur à une taille de 8 octets. Le format donné plus bas (fig 3). Chaque fois qu'un registre de segment est chargé, l'adresse de base est récupérée de l'entrée appropriée de la table. Le contenu du descripteur est stocké dans un registre invisible pour le programmeur appelé registre fantôme, ainsi les futures références au même segment peuvent utiliser cette information au lieu d'accéder à chaque fois à la table. L'adresse physique est formée en ajoutant le décalage de 16 ou de 32 bits à l'adresse de base dans le registre fantôme. Ces différences sont mises en évidences dans les figures 1 et 2.

fig 2 : Adressage en Mode Protégé

fig 3 : Format du Descripteur de Segment

Il y a une autre table appelée Table des Descripteurs d'interruption ou TDI. La TDI contient les descripteurs d'interruptions. Ils sont utilisés pour indiquer au processeur où trouver les routines d'interruption. Elle contient une entrée par interruption, exactement comme dans le Mode Réel, cependant le format de ces entrées est complètement différent. Comme nous n'utiliserons pas la TDI dans notre code pour basculer dans le Mode Protégé, nous n'entrerons pas plus dans les détails.

2. Activation du Mode Protégé

Le 386 dispose de quatre registres de contrôle de 32 bits; ces registres sont appelés CR0, CR1, CR2 et CR3. CR1 est réservé pour les futures générations de processeurs et n'est pas défini pour le 386. CR0 contient les bits qui activent ou non la pagination et les protections ainsi que les bits qui contrôlent fonctionnement du coprocesseur de calcul en virgule flottante. CR2 et CR3 sont utilisés par le mécanisme de pagination. Nous nous intéressons au bit 0 du registre CR0 que l'on appelle bit PE (Protection Enable). Lorsque PE = 1, le processeur sait que le mode protégé est activé avec le mécanisme de pagination que nous avons décrit plus haut. Si PE = 0, le processeur fonctionne en mode réel. Le 386 dispose également de tables de registres de segmentation telles que la TDGR, la TDLR et la TDIR. Ces registres de segments d'adresse contiennent les descripteurs de table. La TDGR pointe sur la TDG, les 48 bits de la TDGR définissent directement le début et la fin de la TDG au moyen d'un adressage linéaire de 32 bits et d'une limite de 16 bits.

Le basculement vers le mode protégé implique principalement que nous activions le bit PE, mais il y a quelque autres petites choses que nous devons faire. Le programme doit initialiser les segments système et les registres de contrôle. Immédiatement après avoir mis le bit PE à 1 nous devons exécuter une instruction de saut pour vider la file d'exécution de toute instruction qui pourrait avoir été chargé dans le mode réel. Ce saut se fait habituellement sur la prochaine instruction. Les étapes pour basculer sur le mode protégé se résument alors à :

  1. Construire la TDG.

  2. Activer le mode protégé en positionnant à 1 le bit PE de CR0.

  3. Faire un saut pour vider la file.

Nous donnons à présent le code pour effectuer le basculement.

3. Nos besoins

  • une disquette vierge

  • l'assembleur NASM

Cliquez ici pour télécharger le code. Saisissez le code dans un fichier nommé abc.asm. Assemblez-le en tapant la commande nasm abc.asm. Ceci produira un fichier nommé abc. Insérez ensuite la disquette et tapez la commande suivante : dd if=abc of/dev/fd0. Cette commande écrira le fichier abc dans le premier secteur de la disquette. Redémarrez ensuite le système. Vous devriez voir la séquence de messages suivante:

  • Chargement du secteur d'amorçage de notre OS ......

  • A (de couleur marron)

  • Basculement en mode protégé ....

  • A (de couleur blanche)

4. Le code qui fait tout !

Nous donnons d'abord le code pour effectuer le basculement. C'est suivi par une explication détaillée

Comme nous l'avions indiqué dans l'article précédent (1ère partie), le BIOS sélectionne le périphérique d'amorçage et place le secteur de démarrage à l'adresse 0x7c00. Nous avons ainsi écrit notre code à l'adresse 0x7c00. La directive org implique cela.

Fonctions utilisées

print_mesg :

Cette routine utilise la sous fonction 13h de l'interruption 10h du BIOS pour écrire une chaîne à l'écran. Les attributs sont spécifiés en plaçant les bonnes valeurs dans divers registres. L'interruption 10h est utilisée pour les manipulations de chaînes de caractères. On place 10h, le numéro de la sous fonction dans ah, ce qui signifie que l'on souhaite afficher une chaîne de caractères. Le bit 0 du registre al détermine la prochaine position du curseur ; s'il est à 0 on retourne au début de la prochaine ligne après l'appel de la fonction, s'il est à 1 le curseur est placé après le dernier caractère affiché.

La mémoire vidéo est divisée en plusieurs pages que l'on appelle pages d'affichage vidéo. Une seule page peut être affichée à la fois (allez voir la première partie pour plus de détails sur la mémoire vidéo). Le contenu de bh indique le numéro de la page, bl précise la couleur du caractère à afficher. cx contient la longueur de la chaîne à afficher. Le registre dx donne la position du curseur. Une fois que tous les attributs ont étés initialisés, on appelle l'interruption BIOS 10h.

get_key:

On utilise l'interruption BIOS 16h dont la sous fonction 00h est utilisée pour lire le dernier caractère lu au clavier. Le registre ah contient le numéro de la sous fonction. as86 est converti dans le langage de la machine réelle par cet outil. Le langage machine sera dans une forme compréhensible que le 8086 comprend.

clrscr:

Cette fonction utilise également une sous fonction de l'interruption 10h. La sous-fonction 06h permet d'effacer l'écran avant d'afficher une chaîne de caractères. On initialise le registre al à 0. Les registres cx et dx donne la taille de la fenêtre à effacer ; dans notre cas, c'est tout l'écran. Le registre bh donne la couleur de fond d'écran, ici c'est le noir.

C'est ici que tout commence !

La première instruction en langage assembleur est un saut court vers le code begin_boot. On souhaite imprimer un caractère 'A' de couleur marron dans le mode réel, configurer la TDG, basculer dans le mode protégé et afficher un 'A' blanc. Chaque mode possède sa propre méthode d'adressage.

Dans le Mode Réel

On utilise le registre de segment gs pour pointer sur la mémoire vidéo. On utilise un adaptateur CGA (adresse de base par défaut 0xB8000)  ; on constate qu'il manque un 0 dans le code. En fait l'unité de segmentation du mode réel fournit le zéro supplémentaire. Le 8086 effectue habituellement une manipulation d'adresse sur 20 bits ; par commodité, cela a été transposé dans l'adressage du mode réel du 386. La valeur A.S.C.I.I. pour 'A' est 0x41 ; 0x06 indique que l'on veut une couleur de caractère marron. L'affichage ne change pas tant que l'on n'appuie pas sur une touche. Ensuite on affiche un message sur l'écran indiquant que l'on va basculer sur le mode protégé. On fait alors pointer le registre bp (pointeur de base) sur le message à afficher.

Lancement du mode protégé

Nous n'avons plus besoin de nous embêter avec les interruptions lorsque que nous sommes en mode protégé. Nous pouvons alors les désactiver ; c'est que fait ci. Nous les réactiverons plus tard. Initialisons à présent la TDG, on initialise quatre descripteurs dans notre essais de basculement dans le mode protégé. Ces descripteurs initialisent notre segment de code (code_gdt), les segments de données et de pile (data_gdt) et le segment vidéo afin d'accéder à la mémoire vidéo. Un descripteur factice et également initialisé bien qu'il n'est jamais utilisé sauf si bien sûr vous voulez une triple faute. C'est un descripteur vide. Examinons quelques champs du descripteur de segment.

  • La première instruction définit la taille du segment; par simplicité est fixée à 0xFFFF qui est la valeur maximum (soit 4Go). Pour le segment vidéo, nous avons utilisé une valeur prédéfinie de 3999 (80 colonnes * 25 lignes * 2 octets - 1).

  • L'adresse de base des segments de code et de donnes est fixée à 0x0000A. Le segment vidéo est à 0xb8000 (adresse de base de la mémoire vidéo)

L'adresse de base de la TDG doit être chargée dans le registre système TDGR. Le segment TDGR est chargé avec la taille de la TDG dans le premier mot et l'adresse de base dans le second. L'instruction lgdt charge ensuite le segment TDG dans le registre TDGR. Nous sommes à présent prêts à basculer dans le mode protégé. On commence par positionner le bit le moins significatif de CR0 à 1 (i.e. le bit PE). Nous ne sommes pas tout à fait dans le mode protégé.

La section 10.3 du manuel de référence du programmeur de l'intel 80386 de 1986 indique : immédiatement après avoir positionné le drapeau de PE, le code d'initialisation doit purger la file de préchargement en exécutant une instruction JMP. Le 386 récupère et décode les instructions avant qu'elles ne soit exécutées ; cependant un basculement dans le mode protégé, les informations sur les instructions préchargées (qui dépendent du mode réel) ne sont plus valides. Un JMP force le processeur à éliminer les informations invalides.

Nous somme à présent dans le mode protégé. Comment le vérifier ? Regardons le 'A' blanc. Nous avons initialisé le segment de données et le segment supplémentaire (es) avec le sélecteur de segment de données (datasel), initialisé gs avec le sélecteur de segment vidéo (videosel). Pour afficher un 'A' blanc, nous avons chargé un mot contenant la valeur A.S.C.I.I. et l'attribut à destination [gs:0000] ie b8000:0000. La boucle sans fin conserve le texte à l'écran jusqu'au redémarrage du système.

Les instructions temporelles sont utilisées pour remplir les octets inutilisés du secteur. Nous écrivons 0xAA55 dans les octets 511 et 512 pour indiquer qu'il s'agit du secteur amorçable. Et c'est tout.

Raghu et Chitkala.

Raghu et Chitkalaun sont des étudiants en septième semestre à l'Université publique Thissur Kerala, en Inde. L'objectif de leur projet de dernière année est de porter le Mode Utilisateur de Linux sur BSD. Leurs centres d'intérêts incluent les Systèmes d'Exploitation, le Réseau et les Microcontrôlleurs.

Adaptation française de la Gazette Linux

L'adaptation française de ce document a été réalisée dans le cadre du Projet de traduction de la Gazette Linux.

Vous pourrez lire d'autres articles traduits et en apprendre plus sur ce projet en visitant notre site : http://www.traduc.org/Gazette_Linux.

Si vous souhaitez apporter votre contribution, n'hésitez pas à nous rejoindre, nous serons heureux de vous accueillir.