diff --git a/cpu/i386/gdt.c b/cpu/i386/gdt.c index 76cbf83..640d647 100644 --- a/cpu/i386/gdt.c +++ b/cpu/i386/gdt.c @@ -1,6 +1,8 @@ #include #include "seg_upd.h" -#define NUM_ENTRIES 5 +#define NUM_ENTRIES 6 + +extern uint32_t int_stack_top; typedef struct { uint16_t limit_low16; @@ -16,9 +18,39 @@ typedef struct { uint32_t address; } __attribute__((packed)) gdt_description; +typedef struct { + uint32_t prev_tss; // The previous TSS - if we used hardware task switching this would form a linked list. + uint32_t esp0; // The stack pointer to load when we change to kernel mode. + uint32_t ss0; // The stack segment to load when we change to kernel mode. + uint32_t esp1; // Unused... + uint32_t ss1; + uint32_t esp2; + uint32_t ss2; + uint32_t cr3; + uint32_t eip; + uint32_t eflags; + uint32_t eax; + uint32_t ecx; + uint32_t edx; + uint32_t ebx; + uint32_t esp; + uint32_t ebp; + uint32_t esi; + uint32_t edi; + uint32_t es; // The value to load into ES when we change to kernel mode. + uint32_t cs; // The value to load into CS when we change to kernel mode. + uint32_t ss; // The value to load into SS when we change to kernel mode. + uint32_t ds; // The value to load into DS when we change to kernel mode. + uint32_t fs; // The value to load into FS when we change to kernel mode. + uint32_t gs; // The value to load into GS when we change to kernel mode. + uint32_t ldt; // Unused... + uint16_t trap; + uint16_t iomap_base; +} __attribute__((packed)) tss_entry; + gdt_entry gdt[NUM_ENTRIES]; gdt_description gdt_desc; - +tss_entry tss; void gdt_init() { set_entry(0,0,0,0); @@ -26,10 +58,14 @@ void gdt_init() { set_entry(2,0,0xFFFFF,0x92); set_entry(3,0,0xFFFFF,0xFA); set_entry(4,0,0xFFFFF,0xF2); + write_tss(5,0x10,int_stack_top+0xC0000000); gdt_desc.size=(sizeof(gdt_entry)*NUM_ENTRIES)-1; gdt_desc.address=&gdt; asm volatile("lgdt (%%eax)"::"a"((uint32_t)&gdt_desc)); seg_upd(); + asm volatile("mov $0x2B, %ax; \ + ltr %ax; \ + "); } void set_entry(int i,uint32_t base,uint32_t limit,uint8_t access) { @@ -41,3 +77,31 @@ void set_entry(int i,uint32_t base,uint32_t limit,uint8_t access) { gdt[i].limit_flags=0xC0|limit_high4; gdt[i].base_high8=(base&0xFF000000)>>24; } + +void write_tss(int32_t num, uint16_t ss0, uint32_t esp0) { + // Firstly, let's compute the base and limit of our entry into the GDT. + uint32_t base = (uint32_t) &tss; + uint32_t limit = base + sizeof(tss_entry); + + // Now, add our TSS descriptor's address to the GDT. + gdt[num].limit_low16=limit&0xFFFF; + gdt[num].base_low16=base&0xFFFFF; + gdt[num].base_mid8=(base&0xFF0000)>>16; + gdt[num].access=0xe9; + gdt[num].limit_flags=(limit&0xF0000)>>16; + gdt[num].base_high8=(base&0xFF000000)>>24; + + // Ensure the descriptor is initially zero. + memset((void*)&tss,0,sizeof(tss)); + tss.ss0 = ss0; // Set the kernel stack segment. + tss.esp0 = esp0; // Set the kernel stack pointer. + + // Here we set the cs, ss, ds, es, fs and gs entries in the TSS. These specify what + // segments should be loaded when the processor switches to kernel mode. Therefore + // they are just our normal kernel code/data segments - 0x08 and 0x10 respectively, + // but with the last two bits set, making 0x0b and 0x13. The setting of these bits + // sets the RPL (requested privilege level) to 3, meaning that this TSS can be used + // to switch to kernel mode from ring 3. + tss.cs = 0x0b; + tss.ss = tss.ds = tss.es = tss.fs = tss.gs = 0x13; +} diff --git a/kernel/kernel.c b/kernel/kernel.c index 0cd1d23..35139a2 100644 --- a/kernel/kernel.c +++ b/kernel/kernel.c @@ -30,6 +30,9 @@ void kmain(multiboot_info_t* header) { pushl $0x23; \ pushl %eax; \ pushf; \ + pop %eax; \ + or $0x200,%eax; \ + push %eax; \ pushl $0x1B; \ push $1f; \ iret; \