Le mode protégé est inclus dans les processeurs depuis le 286 et a été beaucoup amélioré quand est arrivé le 386.
Il permet, en outre, d'accéder à 4Go de RAM. En effet, les adresses mémoires sont sur 32bits.
On l'appelle protégé car dans ce mode, les données sont protégées. Un code ne peut pas avoir accès à toute la RAM (sauf le kernel).
Grâce à celui-ci, les processus sont désormais possibles et donc le multi-tâche. Les processus auront un espace mémoire alloué et certains droits.
Mais derrière cette image de paradis se cache la complexité du mode protégé.
En PMode, les interruptions du BIOS ne seront malheureusement plus valides, nous devront donc reprogrammez les drivers.
Si on veut quand même utiliser des fonctions du mode réel, on pourra soit repasser en mode réel le temps de l'utilisation (à éviter), soit utiliser le mode virtuel que nous verrons plus tard.
Vous remarquerez vite la différence de facilité de programmation avec le mode reél, mais le mode protégé reste cependant un outil fantastique.
Pour passer en mode protégé, rien de plus facile : il suffit de mettre le bit 0 du registre cr0 à 1.
Pour cela, nous allons utiliser la mnémonique or comme ceci :
mov eax,cr0
Explication : on récupère d'abord le contenu de cr0 dans eax.
or eax,1
mov cr0,eax
Puis, on fait eax or 1, les bits de gauche de eax resteront inchangés (0 or 0 = 0 et 1 or 0 = 1). Quant au bit 0, il va être inversé (0 or 1 = 1).
Finalement, on replace eax dans cr0.
Nous sommes maintenant en mode protégé, mais cela ne suffit pas à le faire fonctionner correctement.
Nous allons maintenant configurer la GDT.
La GDT (Global Descriptor Table) est une table contenant des descripteurs de segment.
Ces descripteurs de segments sont des blocs de 8 octets qui decrivent une zone de la mémoire RAM : son type et d'autres informations.
En mode protégé, pour accéder à un endroit de la RAM, on va utiliser comme segment un sélecteur de segment et comme offset l'adresse linéaire de l'octet à pointer.
Un sélecteur de segment tient sur 16 bits :

L'index est le nuémro du descripteur à utiliser. Ici, on utilisera toujours la GDT, nous verrons la LDT plus tard. Le RPL tient sur 2 bits, il y aura donc 4 niveaux de privilèges possibles.
Le niveau 0 est le plus élevé, il sera réservé au noyau. Certaines mnémoniques (instructions) ne pourront être éxécutées que par le noyau.
Le niveau 1 et 2 sont généralement utilisés pour les drivers.
Et enfin, le niveau 4, le plus bas, est lui utilisé pour les applications.
Les descripteurs de segment :
Voici le schéma d'un descripteur de segment :

L'adresse de base est l'adresse du début de la zone décrite.
La limite est la taille de la zone décrite. Si le bit G est à 0, la limite sera interprétée comme étant le nombre d'octets de la zone. Si il est à 1, elle sera interprétée comme étant le nombres de bloc de 4Ko.
On pourra donc allez jusqu'à 1Mo pour G = 0 et jusqu'à 4Go pour G = 1.
Le bit D/B sera à 1 pour du code ou des données de 32 bits et à 0 pour du 16 bits.
Le bit AVL est libre.
Le bit P sera à 1 si la zone est en mémoire.
Le DPL indique le niveau de privilège de la zone pointée. Si un code essaie d'accéder à une zone dont le niveau de privilège est inférieur (donc ayant plus de droit) au sien, l'accès sera refusé.
Le bit S spécifie si le segment (la zone) est un segment système (1) ou un segment de code ou de données (0).
Le type définira si le segment est du code, des données, ... (1011b pour du code, 0011b pour des données).
Remarque : Le premier descripteur de la GDT devra obligatoirement être à 0
Le registre GDTR :
Certains commencent déjà à se demander comment le processeur sait où se trouvent ces fameux descripteurs de segment.
D'où l'utilité du registre GDTR.
Le registre GDTR fait 6 octets : les deux premiers définissent la limite de la GDT (la taille en octet). Suivent les quatre octets qui définissent la base de la GDT (adresse de départ).
On chargera ce registre grâce à lgdt (sgdt sert à récupérer GDTR).
Exemple :
"Enfin", se disent certains. Je vous bassine depuis tantôt avec la théorie, voici un code qui charge la GDT :
;initialisation du registre GTDR
mov ax,gdtend ;calcul de la limite
mov bx,gdt
sub ax,bx ;gdtend - gdt = limite
mov word [gdtr],ax ;on la place dans les deux premiers octets
xor eax,eax ;calcul de l'adresse linéaire du début de la GDT
xor ebx,ebx
mov ax,ds
mov ecx,eax
shl ecx,4
mov bx,gdt
add ecx,ebx
mov dword [gdtr+2],ecx ;chargement dans les quatres octets de fin
lgdt [gdtr] ;on charge la GDT
;données
gdt:
db 0,0,0,0,0,0,0,0 ;premier descripteur vide
gdtcode:
db 0xFF,0xFF,0,0,0,10011011b,11001111b,0
gdtdata:
db 0xFF,0xFF,0,0,0,10010011b,11001111b,0
gdtend:
gdtr:
dw 0 ;un word pour la limite
dd 0 ;un dword pour la base
Voici un bootloader qui charge un kernel passe en PMode et lance le kernel.
Le kernel est un peu miteux. Il tapisse l'écran en gris (0x77, le 0x20 est le code ASCII d'un espace).
Le bootloader : pmode_bootloader.zip
Et comme d'habitude, si vous avez un problème : Le forum.
Vous pouvez retrouver cet article sur Informatips.