Bienvenidos de nuevo a esta serie de posts titulada «Emulando a Linus Torvalds». Hoy veremos la GDT. Primero tenemos que ver que es la GDT. Según Wikipedia:
The Global Descriptor Table or GDT is a data structure used by Intel x86-family processors starting with the 80286 in order to define the characteristics of the various memory areas used during program execution, including the base address, the size and access privileges like executability and writability
Que traducido sería una Tabla de Descriptores Global, una estructura de datos usada en los procesadores Intel x86 desde el 80286 para definir las características de varias áreas de memoria usadas durante la ejecución del programa.
Resumiendo, si estamos en un procesador Intel x86 deberemos definir una GDT para un correcto uso de la memoria. Nosotros no vamos a hacer mucha complicación y vamos a definir 3 entradas en la tabla:
- Una entrada NULL, obligatoria para todas las tablas.
- Una entrada para la sección data, usaremos el máximo, que en 32 bits son 4 GB.
- Una entrada para la sección code, usaremos el máximo, que en 32 bits son 4 GB.
Como veis data y code usarán el mismo espacio. Bien, ahora vamos a implementarlo. Para ello usaremos dos estructuras, la primera se encargará de contener un puntero hacia los datos reales de nuestra GDT. Y la segunda será un array con las entradas de la GDT. Primero vamos a definirlas
struct Entry{
uint16_t limit_low;
uint16_t base_low;
uint8_t base_middle;
uint8_t access;
uint8_t granularity;
uint8_t base_high;
} __attribute__((packed));
struct Ptr{
uint16_t limit;
uint32_t base;
} __attribute__((packed));
Habrán observado un curioso __attribute__((packed)) al final de las estructuras. Esto le dice al GCC que no optimice las estructuras porque lo que queremos es pasar los datos tal cual al procesador. Ahora vamos a hacer una función para instalar la GDT. Antes deberemos haber declarado las estructuras, ahora vamos a inicializarlas.
struct ND::GDT::Entry gdt[3];
struct ND::GDT::Ptr gp;
void ND::GDT::Install()
{
gp.limit=(sizeof(struct ND::GDT::Entry)*3)-1;
gp.base=(uint32_t)&gdt;
}
Así conseguimos el construir el puntero que va hacia nuestra tabla de 3 entradas.
void ND::GDT::SetGate(int num, uint32_t base, uint32_t limit, uint8_t access,uint8_t gran)
{
gdt[num].base_low=(base & 0xFFFF);
gdt[num].base_middle=(base >> 16) & 0xFF;
gdt[num].base_high=(base >> 24) & 0xFF;
gdt[num].limit_low=(limit & 0xFFFF);
gdt[num].granularity=(limit >> 16) & 0x0F;
gdt[num].granularity |= (gran & 0xF0);
gdt[num].access=access;
}
Y la llamamos 3 veces desde la función de instalar
ND::GDT::SetGate(0,0,0,0,0); /* NULL segmente entry */
ND::GDT::SetGate(1,0,0xFFFFFFFF,0x9A,0xCF); /* 4 GiB for Code Segment */
ND::GDT::SetGate(2,0,0xFFFFFFFF,0x92,0xCF); /* 4 GiB for Data segment */
Por último debemos decirle al procesador que tenemos una GDT, para que la cargue, y en nuestro caso al cargar el kernel con GRUB, sobreescribir la GDT de GRUB. Para cargar la GDT existe una instrucción en asm llamada lgdt (o lgdtl dependiendo de la sintaxis), vamos a usarla.
asm volatile("lgdtl (gp)");
asm volatile(
"movw $0x10, %ax \n"
"movw %ax, %ds \n"
"movw %ax, %es \n"
"movw %ax, %fs \n"
"movw %ax, %gs \n"
"movw %ax, %ss \n"
"ljmp $0x08, $next \n"
"next: \n"
);
Bien una vez hayamos terminado esto nuestro sistema ya contará con GDT. En el siguiente capítulo veremos la IDT, una tabla muy parecida a la GDT pero con interrupciones. Yo he puesto unos mensajes de estado y confirmación con la GDT así que NextDivel ahora luce así:
5 comentarios, deja el tuyo
Quizás una estructura de 64 bits sea mas adecuada para los tiempos que corren, es un atraso seguir usando el 8086.
He estado buscando información sobre la GDT en x86_64 y creo que sigue el modelo antiguo con una flag especial. Se sigue usando una dirección de 32 bits. Ahora bien no sé exactamente como realizarlo correctamente. Unos links:
http://wiki.osdev.org/Entering_Long_Mode_Directly
http://f.osdev.org/viewtopic.php?f=1&t=16275
Lo primero muy buenos tus aportes ,, pero creo que el titulo deveria ser
«emulando a Richard Stallman » o por lo menos eso creo ,,,
Saludos
Linus creo el nucleo de Linux, Stallman creo GNU que son las herramientas y comandos Unix .
El titulo es adecuado porque estan creando un nucleo.
Un saludo!
Muchas gracias por responder a todas mis preguntas y tenerme paciencia, de ensamblador solo se lo básico y de C casi nada, pero me gusta mucho, ahora estoy un poco confundido con el GDT, a ver si entendí.
El GDT va a tener las los ‘descriptores’ globales a los cuales se puede acceder siempre y cualquier programa, y estos descriptores apuntan a la secciones donde esta (el programa) que se va a ejecutar? o es de otra forma.