From 4579384312966418757c1a4fb02f2d209b468df0 Mon Sep 17 00:00:00 2001 From: Jonas 'Sortie' Termansen Date: Sun, 2 Oct 2011 16:00:02 +0200 Subject: [PATCH] Ported the GDT and TSS to x64. --- sortix/descriptor_tables.cpp | 114 +++++++++++++++++++++++++---------- sortix/descriptor_tables.h | 84 +++++++++++++++++--------- sortix/x64/gdt.asm | 43 ------------- sortix/x64/gdt.s | 71 ++++++++++++++++++++++ 4 files changed, 208 insertions(+), 104 deletions(-) delete mode 100644 sortix/x64/gdt.asm create mode 100644 sortix/x64/gdt.s diff --git a/sortix/descriptor_tables.cpp b/sortix/descriptor_tables.cpp index 575333fe..af71d234 100644 --- a/sortix/descriptor_tables.cpp +++ b/sortix/descriptor_tables.cpp @@ -33,26 +33,45 @@ namespace Sortix { namespace GDT { - // Lets us access our ASM functions from our C++ code. extern "C" void gdt_flush(addr_t); extern "C" void tss_flush(); - gdt_entry_t gdt_entries[6]; + const size_t GDT_NUM_ENTRIES = 7; + gdt_entry_t gdt_entries[GDT_NUM_ENTRIES]; gdt_ptr_t gdt_ptr; tss_entry_t tss_entry; + + const uint8_t GRAN_64_BIT_MODE = 1<<5; + const uint8_t GRAN_32_BIT_MODE = 1<<6; + const uint8_t GRAN_4KIB_BLOCKS = 1<<7; void Init() { - gdt_ptr.limit = (sizeof(gdt_entry_t) * 6) - 1; + gdt_ptr.limit = (sizeof(gdt_entry_t) * GDT_NUM_ENTRIES) - 1; gdt_ptr.base = (addr_t) &gdt_entries; +#ifdef PLATFORM_X86 + const uint8_t gran = GRAN_4KIB_BLOCKS | GRAN_32_BIT_MODE; +#elif defined(PLATFORM_X64) + const uint8_t gran = GRAN_4KIB_BLOCKS | GRAN_64_BIT_MODE; +#endif + SetGate(0, 0, 0, 0, 0); // Null segment - SetGate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF); // Code segment - SetGate(2, 0, 0xFFFFFFFF, 0x92, 0xCF); // Data segment - SetGate(3, 0, 0xFFFFFFFF, 0xFA, 0xCF); // User mode code segment - SetGate(4, 0, 0xFFFFFFFF, 0xF2, 0xCF); // User mode data segment + SetGate(1, 0, 0xFFFFFFFF, 0x9A, gran); // Code segment + SetGate(2, 0, 0xFFFFFFFF, 0x92, gran); // Data segment + SetGate(3, 0, 0xFFFFFFFF, 0xFA, gran); // User mode code segment + SetGate(4, 0, 0xFFFFFFFF, 0xF2, gran); // User mode data segment + WriteTSS(5, 0x10, 0x0); + if ( gdt_ptr.base != (addr_t) &gdt_entries ) + { + // If this happens, then either there is a bug in the GDT entry + // writing code - or the struct gdt_entries above is too small. + Panic("Whoops, someone overwrote the GDT pointer while writing " + "to the GDT entries!"); + } + gdt_flush((addr_t) &gdt_ptr); tss_flush(); } @@ -60,47 +79,76 @@ namespace Sortix // Set the value of a GDT entry. void SetGate(int32_t num, uint32_t base, uint32_t limit, uint8_t access, uint8_t gran) { - gdt_entries[num].base_low = (base & 0xFFFF); - gdt_entries[num].base_middle = (base >> 16) & 0xFF; - gdt_entries[num].base_high = (base >> 24) & 0xFF; + gdt_entry_t* entry = (gdt_entry_t*) (&gdt_entries[num]); - gdt_entries[num].limit_low = (limit & 0xFFFF); - gdt_entries[num].granularity = (limit >> 16) & 0x0F; - - gdt_entries[num].granularity |= gran & 0xF0; - gdt_entries[num].access = access; + entry->base_low = (base & 0xFFFF); + entry->base_middle = (base >> 16) & 0xFF; + entry->base_high = (base >> 24) & 0xFF; + + entry->limit_low = (limit & 0xFFFF); + entry->granularity = ((limit >> 16) & 0x0F) | (gran & 0xF0); + + entry->access = access; + } + + // Set the value of a GDT entry. + void SetGate64(int32_t num, uint64_t base, uint32_t limit, uint8_t access, uint8_t gran) + { + gdt_entry64_t* entry = (gdt_entry64_t*) (&gdt_entries[num]); + + entry->base_low = (base & 0xFFFF); + entry->base_middle = (base >> 16) & 0xFF; + entry->base_high = (base >> 24) & 0xFF; + entry->base_highest = (base >> 32); + + entry->limit_low = (limit & 0xFFFF); + entry->granularity = ((limit >> 16) & 0x0F) | (gran & 0xF0); + + entry->access = access; + entry->zero1 = 0; } // Initialise our task state segment structure. - void WriteTSS(int32_t num, uint16_t ss0, uint32_t esp0) + void WriteTSS(int32_t num, uint16_t ss0, addr_t stack0) { - // Firstly, let's compute the base and limit of our entry into the GDT. - addr_t base = (addr_t) &tss_entry; - uint32_t limit = base + sizeof(tss_entry); + // First, let's compute the base and limit of our entry in the GDT. + addr_t base = (addr_t) &tss_entry; + uint32_t limit = base + sizeof(tss_entry); - // Now, add our TSS descriptor's address to the GDT. - SetGate(num, base, limit, 0xE9, 0x00); + // Now, add our TSS descriptor's address to the GDT. +#ifdef PLATFORM_X86 + SetGate(num, base, limit, 0xE9, 0x00); +#elif defined(PLATFORM_X64) + SetGate64(num, base, limit, 0xE9, 0x00); +#endif - // Ensure the descriptor is initially zero. - Memory::Set(&tss_entry, 0, sizeof(tss_entry)); + // Ensure the descriptor is initially zero. + Memory::Set(&tss_entry, 0, sizeof(tss_entry)); - tss_entry.ss0 = ss0; // Set the kernel stack segment. - tss_entry.esp0 = esp0; // Set the kernel stack pointer. +#ifdef PLATFORM_X86 + tss_entry.ss0 = ss0; // Set the kernel stack segment. + tss_entry.esp0 = stack0; // 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_entry.cs = 0x08 | 0x3; - tss_entry.ss = tss_entry.ds = tss_entry.es = tss_entry.fs = tss_entry.gs = 0x10 | 0x3; + // 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_entry.cs = 0x08 | 0x3; + tss_entry.ss = tss_entry.ds = tss_entry.es = tss_entry.fs = tss_entry.gs = 0x10 | 0x3; +#elif defined(PLATFORM_X64) + tss_entry.stack0 = stack0; +#endif } void SetKernelStack(size_t* stack) { #ifdef PLATFORM_X86 tss_entry.esp0 = (uint32_t) stack; +#elif defined(PLATFORM_X64) + tss_entry.stack0 = (uint64_t) stack; #else #warning "TSS is not yet supported on this arch!" while(true); diff --git a/sortix/descriptor_tables.h b/sortix/descriptor_tables.h index fc99d5cb..a52486cd 100644 --- a/sortix/descriptor_tables.h +++ b/sortix/descriptor_tables.h @@ -42,7 +42,20 @@ namespace Sortix uint8_t base_high; // The last 8 bits of the base. } __attribute__((packed)); + struct gdt_entry64_struct + { + uint16_t limit_low; + uint16_t base_low; + uint8_t base_middle; + uint8_t access; + uint8_t granularity; + uint8_t base_high; + uint32_t base_highest; + uint32_t zero1; + } __attribute__((packed)); + typedef struct gdt_entry_struct gdt_entry_t; + typedef struct gdt_entry64_struct gdt_entry64_t; // This struct describes a GDT pointer. It points to the start of // our array of GDT entries, and is in the format required by the @@ -56,42 +69,57 @@ namespace Sortix typedef struct gdt_ptr_struct gdt_ptr_t; // A struct describing a Task State Segment. +#ifdef PLATFORM_X86 struct tss_entry_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; + 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)); +#else + struct tss_entry_struct + { + uint32_t reserved1; + uint64_t stack0; + uint64_t stack1; + uint64_t stack2; + uint64_t reserved2; + uint64_t ist[7]; + uint64_t reserved3; + uint16_t reserved4; + uint16_t iomap_base; + } __attribute__((packed)); +#endif typedef struct tss_entry_struct tss_entry_t; void Init(); void SetGate(int32_t num, uint32_t base, uint32_t limit, uint8_t access, uint8_t gran); - void WriteTSS(int32_t num, uint16_t ss0, uint32_t esp0); + void WriteTSS(int32_t num, uint16_t ss0, addr_t stack0); void SetKernelStack(size_t* stack); } diff --git a/sortix/x64/gdt.asm b/sortix/x64/gdt.asm deleted file mode 100644 index bb6cb84f..00000000 --- a/sortix/x64/gdt.asm +++ /dev/null @@ -1,43 +0,0 @@ -; -; Gdt.s -- contains global descriptor table and interrupt descriptor table -; setup code. -; Based on code from Bran's kernel development tutorials. -; Rewritten for JamesM's kernel development tutorials. - -[GLOBAL gdt_flush] ; Allows the C code to call gdt_flush(). - -gdt_flush: - - ; TODO: Actually make this stuff work! - - mov eax, [esp+4] ; Get the pointer to the GDT, passed as a parameter. - lgdt [eax] ; Load the new GDT pointer - - mov ax, 0x10 ; 0x10 is the offset in the GDT to our data segment - mov ds, ax ; Load all data segment selectors - mov es, ax - ; mov fs, ax ; TODO: Not on 64-bit! - mov gs, ax - mov ss, ax - ; TODO: Not on 64-bit! - ; jmp 0x08:.flush ; 0x08 is the offset to our code segment: Far jump! -.flush: - ret - -[GLOBAL idt_flush] ; Allows the C code to call idt_flush(). - -idt_flush: - mov eax, [esp+4] ; Get the pointer to the IDT, passed as a parameter. - lidt [eax] ; Load the IDT pointer. - sti - ret - -[GLOBAL tss_flush] ; Allows our C code to call tss_flush(). -tss_flush: - mov ax, 0x2B ; Load the index of our TSS structure - The index is - ; 0x28, as it is the 5th selector and each is 8 bytes - ; long, but we set the bottom two bits (making 0x2B) - ; so that it has an RPL of 3, not zero. - ltr ax ; Load 0x2B into the task state register. - ret - diff --git a/sortix/x64/gdt.s b/sortix/x64/gdt.s new file mode 100644 index 00000000..51ee946a --- /dev/null +++ b/sortix/x64/gdt.s @@ -0,0 +1,71 @@ +/****************************************************************************** + + COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along + with Sortix. If not, see . + + x64/gdt.cpp + Handles initialization of the 64-bit global descriptor table. + +******************************************************************************/ + +.section .text + +.global gdt_flush +.type gdt_flush, @function +gdt_flush: + # Load the new GDT pointer + lgdtq (%rdi) + + # 0x10 is the offset in the GDT to our data segment + mov $0x10, %ax + mov %ax, %ds + mov %ax, %es + mov %ax, %fs + mov %ax, %gs + + # 0x08 is the offset to our code segment: Far jump! + ljmp *GDT_FLUSH_POSTJMP +gdt_flush_postjmp: + ret + +.global idt_flush +.type idt_flush, @function +idt_flush: + # Load the IDT pointer. + lidt (%rdi) + + # Enable interrupts + sti + ret + +.global tss_flush +.type tss_flush, @function +tss_flush: + # Load the index of our TSS structure - The index is 0x28, as it is the 5th + # selector and each is 8 bytes long, but we set the bottom two bits (making + # 0x2B) so that it has an RPL of 3, not zero. + mov $0x2B, %ax + + # Load the task state register. + ltr %ax + ret + +.section .data +GDT_FLUSH_POSTJMP: + .long gdt_flush_postjmp + .word 0x08 +