Triple fault sur implémentation de la GDT
Bonjour à tous,
Je suis actuellement, pour raisons ludique et pédagogique, de développer un petit kernel sans autre ambition que d'en savoir plus sur la manière dont fonctionnent ces derniers (et développer un peu mes compétences en programmation). Pour "simplifier" la chose, j'ai choisi de booter avec GRUB dans un premier temps ; je me pencherais sur ce sujet plus tard.
Je tourne sous Linux, compile avec GCC et AS, link avec LD, le tout dans un makefile approprié. Mes essais se font sous virtual-box.
Mon code est écrit en C et ASM (GNU/AT&T), et je m'inspire des différentes sources d'infos que je trouve ça et là. Je précise aussi que mes cours d'assembleur et architecture sont assez lointains et se faisaient plutôt sous la forme INTEL (MASM/NASM).
Mon problème est le suivant : impossible de charger les segments code, data ou encore stack (CS,DS,SS) lors-que je souhaite charger la GDT.
J'en ai donc déduit que cela venait très certainement de la forme de ma gdt, mais je ne trouve pas mon erreur.
Code de formation des descripteurs de segment et de la GDT Registres (*.h et *.c) :
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
|
#ifndef __GDT_H
#define __GDT_H
#include "types.h"
#define GDTSIZE 0xFF
#define GDTBASE 0x0
//typedef struct segDesc segDesc;
//typedef struct segPtr segPtr;
//segment descriptor
struct segDesc {
uint16_t limit_lb;
uint16_t base_lb;
uint8_t base_hb;
uint8_t type;
uint8_t granul; //sinon uint8_t limit:4 et uint8_t flags:4
uint8_t base_vhb;
} __attribute__((packed));
//Global Register Descriptor Table
struct segPtr {
uint16_t limit; //uint32_t limit();
uint32_t base; //uint32_t base();
}__attribute__((packed));
//déclaration des segment descriptors...
struct segDesc tSeg[3];
//déclaration des seg registers.
struct segPtr gdtr;
// initialisation des segments (code, data, stack)
void initSegmentDesc(uint8_t i, uint32_t base, uint32_t limit, uint8_t type, uint8_t flags_hb);
//init de la gdt globale avec registres
void init_gdt(void);
extern void gdt_load(void); //fonction présente dans le loader.s
#endif |
...
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
|
void initSegmentDesc(uint8_t i, uint32_t base, uint32_t limit,
uint8_t type, uint8_t flags)
{
tSeg[i].limit_lb = (limit & 0xFFFF);
tSeg[i].base_lb = (base & 0xFFFF);
tSeg[i].base_hb = (base >> 16) & 0xFF;
tSeg[i].type = type;
tSeg[i].granul = (limit >> 16) & 0x0F; //tSeg[i].limit = (limit >> 16) & 0x0F;
tSeg[i].granul |= (flags & 0xF0); //tSeg[i].flags = (flags & 0xF);
tSeg[i].base_vhb = (base >> 24) & 0xFF;
return;
}
void init_gdt(void)
{
initSegmentDesc(0, 0, 0, 0, 0); //null
initSegmentDesc(1, 0, 0xFFFFFFFF, 0x9A, 0xCF); //code
initSegmentDesc(2, 0, 0xFFFFFFFF, 0x92, 0xCF); //data
//initSegmentDesc(3, 0x0, 0x0, 0x96, 0x0D); //stack
gdtr.limit = (sizeof(struct segDesc)*3) - 1;
gdtr.base = (uint32_t)&tSeg;
//gdtr.limit = GDTSIZE*8;
//gdtr.base = GDTBASE;
//memcpy((uint8_t*)gdtr.base, (uint8_t*)tSeg, gdtr.limit);
gdt_load(); // <-- bug !!! |
Le sections commentées sont les différents essais que j'ai fait en m'inspirant de ce que j'ai trouvé sur le net.
loader.s, avec la fonction gdt_load :
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
|
.set MAGIC, 0x1badb002 #adresse où doit commencer le kernel (par grub)
.set FLAGS, (1<<0 | 1<<1) #init à 0x0 marche aussi...
.set CHKSUM, -(MAGIC + FLAGS)
.section .multiboot
.long MAGIC
.long FLAGS
.long CHKSUM
.section .text
.extern kernelStart
.extern kerr
.extern gdtr
.extern idtP
.global loader
.global gdt_load
.global idt_load
loader:
mov $kernel_stack , %esp
push %eax
push %ebx
movl %cr0, %eax
or $1, %eax
movl %eax, %cr0 #passage ne mode protégé... useless.
call kernelStart
gdt_load:
cli # <-- ne change rien
lgdtl (gdtr)
movw $0x10, %ax
movw %ax, %ds #data segment
movw %ax, %es #extra segment
movw %ax, %fs #gp - general purpose
movw %ax, %gs #gp - general purpose
ljmp $0x08, $next #colle le cs à 0x08
next:
movw $0x18, %ax
movw %ax, %ss #colle le ss à 0x18
movl $0x20000, %esp
ret
idt_load:
lidt (idtP)
ret
_stop:
cli
hlt
jmp _stop
.section .bss
.space 4096 #Mbi
kernel_stack: |
Vous remarquerez que j'ai essayé de forcer le passage en mode protégé (bien qu'inutile car GRUB passe la main sous ce mode). J'ai aussi essayé de limiter les potentiels interupts lors du chargement de la GDT (cli). Celà fait presque une semaine que je refais mon code dans tous les sens donc je ne peut pas vous faire la liste de ce que j'ai testé.
Voilà mon "kernel" se lance, m'affiche mes deux lignes de présentations et.. TRIPLE_FAULT....
Quelqu'un voit d'où vient mon erreur ? Je commence à vraiment sécher...
Merci beaucoup d'avance !