/** @file gdt.h * @brief Configuration de la Table de Description Globale ou GDT. Utilisée * pour pouvoir gerer la memoire sous forme de segments. * * 1. Il faut en premier initialiser le registre @c gdtr avec l'adresse et la * taille de la GDT. * 2. Charger ensuite dans les registres @c cs et @c ds des sélecteurs de * segments * 3. Charger dans la GDT des descripteurs de segments. Le premier est toujours * nul, le deuxième est le segment pour le code du noyau et le troisième pour * les données. * * Références: * - Documentation Intel (volume 3A): * https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html * - De manière globale : les parties 2 et 3 * - Partie 3.4.3 : registres de segments * - Figure 3-11 : structure à charger dans le registre @c gdtr * - Figure 3-5 : illustration de la transformation d'une adresse logique * en adresse linéaire * - Figure 3-6 : anatomie d'un sélecteur de segment * - Figure 3-8 : anatomie d'un descripteur de segment * - Table 3-1 : type de segment (donnée et code) * - chargement de @c gdtr https://www.felixcloutier.com/x86/lgdt:lidt * - explication sur les sélecteurs de segments * https://wiki.osdev.org/Segment_Selector * - explication sur la segmentation et plus particulièrement les registres de * segmentation : https://wiki.osdev.org/Segmentation * - tutoriel pour inialiser la GDT : https://wiki.osdev.org/GDT_Tutorial * - documentation en français (rapidement survolée) : * https://wiki.osdev.org/GDT_Tutorial */ /** @struct gdt_entry * * Structure qui sera passée à la fonction load_gdt(). Elle est utilisée pour * rassembler la taille et l'adresse de la GDT en un seul type de 6 bytes. * * Voir section 3.5.1 du volume 3A du manuel d'Intel. */ struct gdt_entry { /** * La taille de la GDT en bytes. Elle doit être éguale à 8N - 1, à cause de * la taille des segments (1 segment = 8 bytes). */ unsigned short size; /** * L'adresse de la GDT, doit être alignée sur un multiple de 8 pour avoir * de meilleures performances. */ unsigned int address; }__attribute__((packed)); /** @struct segment_descriptor * * Un descripteur de segment est chargé dans la GDT. Il contient 8 bytes, * séparés en 2 @c unsigned @c int. * * Premier int : * | 0 - 15 | 16 - 31 | * |----------------------------|--------------------------| * | Limite du segment (0 - 15) | Adresse de base (0 - 15) | * * Deuxième int : * | 0 - 7 | 8 - 11 | 12 | 13 - 14 | 15 | 16 - 19 | 20 | 21 | 22 | 23 | 24 - 31 | * |---------------------------|--------------|-----------------|------------------------------------|-----------------|-----------------------------|---------------|--------------------|------------------------|-------------|----------------------------| * | Adresse de base (16 - 23) | Segment type | Descriptor Type | Niveau de privilège du descripteur | Segment present | Limite du segment (16 - 19) | Disponibilité | 64bit code semgent | Default operation size | Granularité | Adresse de base (24 - -31) | * * @note Cette structure a été codée pour répondre à un besoin : comment * simplifier la création de descripteurs de segments. Il s'agit d'un choix * d'implémentation que j'ai fait et qui n'est pas indiqué dans la * documentation Intel ou dans The little book about OS development . */ struct segment_descriptor { /** Contient les bytes 0 à 3 (base + limite). */ unsigned int first_bytes; /** Contient les bytes 4 à 7 (base + limite + drapeaux). */ unsigned int last_bytes; }__attribute__((packed)); /** * Créer une valeur de sélecteur de segment. * * @param index l'index du segment dans la GDT * @param type le type de table (0 = GDT, 1 = LDT) * @param rpl le niveau de privilège (0 = le +, 3 = le -) */ unsigned short create_selector(unsigned short index, unsigned char type, unsigned char rpl); /** * Va charger la structure passée en paramètre comme GDT au processeur. Appelle * l'instruction @c lgdt, qui prend en paramètre 6 bytes, d'où l'utilisation de * la structure. * * @param entry les informations de la GDT à charger */ void load_gdt(struct gdt_entry *entry); /** * Charge dans le registre @c cs le sélecteur 0x08. */ void load_cs(void); /** * Charge dans les registres @c ds, @c es et @c ss un sélecteur de segment. * * @param selector le sélecteur à charger */ void load_registers(void); /** * Créer une structure gdt_entry. * * @param start : l'adresse de départ * @param n : le nombre d'éléments dedans */ struct gdt_entry create_gdt_entry(unsigned int start, unsigned short n); /** * Créer un descripteur de segment. * * @param base l'adresse de base en mémoire * @param limit la limite du segment * @param dpl niveau de privilège du semgent (0 le +, 3 le -) * @param segment_type les autorisations en écriture / exécution du segment * (cf. table 3-1) */ struct segment_descriptor create_descriptor(unsigned int base, unsigned int limit, unsigned char dpl, unsigned char segment_type); /** * Ajoute un descripteur de segment dans la GDT. * * @param i : l'index dans la GDT (sera calculé pour l'insérer au bon endroit) * @param start : l'adresse du début de la GDT * @param desc : le descripteur à insérer */ void add_in_gdt(unsigned int i, unsigned int start, struct segment_descriptor desc); void config_gdt(void);