From c36b35adc21fe19c9d61dc494e66f4db1efe5f69 Mon Sep 17 00:00:00 2001 From: Jonas 'Sortie' Termansen Date: Thu, 20 Jun 2013 02:51:54 +0200 Subject: [PATCH] Refactor kernel GDT code. --- sortix/Makefile | 1 - sortix/x64/gdt.s | 64 ------- sortix/x86-family/gdt.cpp | 350 ++++++++++++++++++++++++-------------- sortix/x86-family/gdt.h | 105 +----------- sortix/x86/gdt.s | 65 ------- 5 files changed, 230 insertions(+), 355 deletions(-) delete mode 100644 sortix/x64/gdt.s delete mode 100644 sortix/x86/gdt.s diff --git a/sortix/Makefile b/sortix/Makefile index 0fbf63da..8645d9cb 100644 --- a/sortix/Makefile +++ b/sortix/Makefile @@ -46,7 +46,6 @@ ifdef X86FAMILY $(CPU)/memorymanagement.o \ x86-family/memorymanagement.o \ $(CPU)/interrupt.o \ - $(CPU)/gdt.o \ x86-family/gdt.o \ x86-family/idt.o \ $(CPU)/syscall.o \ diff --git a/sortix/x64/gdt.s b/sortix/x64/gdt.s deleted file mode 100644 index 5ad62cf2..00000000 --- a/sortix/x64/gdt.s +++ /dev/null @@ -1,64 +0,0 @@ -/******************************************************************************* - - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. - - 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.s - 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 - mov %ax, %ss - - # Far jump to our new code segment! - movq $GDT_FLUSH_POSTJMP, %rax - ljmp *(%rax) -gdt_flush_postjmp: - ret -.size gdt_flush, . - gdt_flush - -.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 -.size tss_flush, . - tss_flush - -.section .data -GDT_FLUSH_POSTJMP: - .long gdt_flush_postjmp - .word 0x08 # 0x08 is the offset to our code segment diff --git a/sortix/x86-family/gdt.cpp b/sortix/x86-family/gdt.cpp index fa2e0650..a4361b73 100644 --- a/sortix/x86-family/gdt.cpp +++ b/sortix/x86-family/gdt.cpp @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013. This file is part of Sortix. @@ -22,139 +22,233 @@ *******************************************************************************/ -#include +#include #include + +#include + #include "gdt.h" -namespace Sortix +namespace Sortix { +namespace GDT { + +struct gdt_entry { - namespace GDT - { - extern "C" void gdt_flush(addr_t); - extern "C" void tss_flush(); + uint16_t limit_low; + uint16_t base_low; + uint8_t base_middle; + uint8_t access; + uint8_t granularity; + uint8_t base_high; +}; - 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; +struct gdt_entry64 +{ + 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 reserved0; +} __attribute__((packed)); - 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) * 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, 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(); - } - - // 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_entry_t* entry = (gdt_entry_t*) (&gdt_entries[num]); - - 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, addr_t stack0) - { - // 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. -#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. - memset(&tss_entry, 0, sizeof(tss_entry)); - -#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; -#elif defined(PLATFORM_X64) - (void) ss0; - tss_entry.stack0 = stack0; -#endif - } - - void SetKernelStack(addr_t stacklower, size_t stacksize, addr_t stackhigher) - { -#ifdef PLATFORM_X86 - (void) stacklower; - (void) stacksize; - tss_entry.esp0 = (uint32_t) stackhigher; -#elif defined(PLATFORM_X64) - (void) stacklower; - (void) stacksize; - tss_entry.stack0 = (uint64_t) stackhigher; +struct gdt_ptr +{ + uint16_t limit; +#if defined(__i386__) + uint32_t base; #else - #warning "TSS is not yet supported on this arch!" - while(true); + uint64_t base; #endif - } - } +} __attribute__((packed)); + +#if defined(__i386__) +struct tss_entry +{ + 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)); +#elif defined(__x86_64__) +struct tss_entry +{ + uint32_t reserved0; + 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 + +const size_t GDT_NUM_ENTRIES = 7; +static struct gdt_entry gdt_entries[GDT_NUM_ENTRIES]; + +static struct tss_entry 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 SetGate(int32_t num, uint32_t base, uint32_t limit, uint8_t access, uint8_t gran) +{ + struct gdt_entry* entry = (struct gdt_entry*) &gdt_entries[num]; + + entry->base_low = base >> 0 & 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; } + +void SetGate64(int32_t num, uint64_t base, uint32_t limit, uint8_t access, uint8_t gran) +{ + struct gdt_entry64* entry = (struct gdt_entry64*) &gdt_entries[num]; + + entry->base_low = base >> 0 & 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->reserved0 = 0; +} + +void Init() +{ + +#if defined(__i386__) + const uint8_t gran = GRAN_4KIB_BLOCKS | GRAN_32_BIT_MODE; +#elif defined(__x86_64__) + 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, 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); + + // Reload the Global Descriptor Table. + volatile struct gdt_ptr gdt_ptr; + gdt_ptr.limit = (sizeof(struct gdt_entry) * GDT_NUM_ENTRIES) - 1; + gdt_ptr.base = (uintptr_t) &gdt_entries; + asm volatile ("lgdt (%0)" : : "r"(&gdt_ptr)); + + // Switch the current data segment. + asm volatile ("mov %0, %%ds\n" + "mov %0, %%es\n" + "mov %0, %%fs\n" + "mov %0, %%gs\n" + "mov %0, %%ss\n" : : + "r"(KDS)); + + // Switch the current code segment. + #if defined(__i386__) + asm volatile ("push %0\n" + "push $1f\n" + "retf\n" + "1:\n" : : + "r"(KCS)); + #elif defined(__x86_64__) + asm volatile ("push %0\n" + "push $1f\n" + "retfq\n" + "1:\n" : : + "r"(KCS)); + #endif + + // Load the task state register - 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. + asm volatile ("ltr %%ax" : : "a"(0x2B)); +} + +// Initialise our task state segment structure. +void WriteTSS(int32_t num, uint16_t ss0, uintptr_t stack0) +{ + // First, let's compute the base and limit of our entry in the GDT. + uintptr_t base = (uintptr_t) &tss_entry; + uint32_t limit = base + sizeof(tss_entry); + + // Now, add our TSS descriptor's address to the GDT. +#if defined(__i386__) + SetGate(num, base, limit, 0xE9, 0x00); +#elif defined(__x86_64__) + SetGate64(num, base, limit, 0xE9, 0x00); +#endif + + // Ensure the descriptor is initially zero. + memset(&tss_entry, 0, sizeof(tss_entry)); + +#if defined(__i386__) + 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 = KCS | 0x3; + tss_entry.ss = tss_entry.ds = tss_entry.es = tss_entry.fs = tss_entry.gs = KDS | 0x3; +#elif defined(__x86_64__) + (void) ss0; + tss_entry.stack0 = stack0; +#endif +} + +void SetKernelStack(uintptr_t stacklower, size_t stacksize, uintptr_t stackhigher) +{ +#if defined(__i386__) + (void) stacklower; + (void) stacksize; + tss_entry.esp0 = (uint32_t) stackhigher; +#elif defined(__x86_64__) + (void) stacklower; + (void) stacksize; + tss_entry.stack0 = (uint64_t) stackhigher; +#endif +} + +} // namespace GDT +} // namespace Sortix diff --git a/sortix/x86-family/gdt.h b/sortix/x86-family/gdt.h index 634b1614..48a84cd7 100644 --- a/sortix/x86-family/gdt.h +++ b/sortix/x86-family/gdt.h @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013. This file is part of Sortix. @@ -25,103 +25,14 @@ #ifndef SORTIX_X86_FAMILY_GDT_H #define SORTIX_X86_FAMILY_GDT_H -namespace Sortix -{ - namespace GDT - { - // This structure contains the value of one GDT entry. - // We use the attribute 'packed' to tell GCC not to change - // any of the alignment in the structure. - struct gdt_entry_struct - { - uint16_t limit_low; // The lower 16 bits of the limit. - uint16_t base_low; // The lower 16 bits of the base. - uint8_t base_middle; // The next 8 bits of the base. - uint8_t access; // Access flags, determine what ring this segment can be used in. - uint8_t granularity; - uint8_t base_high; // The last 8 bits of the base. - } __attribute__((packed)); +namespace Sortix { +namespace GDT { - 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)); +void Init(); +void WriteTSS(int32_t num, uint16_t ss0, uintptr_t stack0); +void SetKernelStack(uintptr_t stacklower, size_t stacksize, uintptr_t stackhigher); - 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 - // lgdt instruction. - struct gdt_ptr_struct - { - uint16_t limit; // The upper 16 bits of all selector limits. - addr_t base; // The address of the first gdt_entry_t struct. - } __attribute__((packed)); - - 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; - } __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, addr_t stack0); - void SetKernelStack(addr_t stacklower, size_t stacksize, addr_t stackhigher); - } -} +} // namespace GDT +} // namespace Sortix #endif diff --git a/sortix/x86/gdt.s b/sortix/x86/gdt.s deleted file mode 100644 index e5fd4f81..00000000 --- a/sortix/x86/gdt.s +++ /dev/null @@ -1,65 +0,0 @@ -/******************************************************************************* - - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. - - 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 . - - x86/gdt.s - Handles initialization of the 32-bit global descriptor table. - -*******************************************************************************/ - -.section .text - -.global gdt_flush -.type gdt_flush, @function -gdt_flush: - # Load the new GDT pointer - mov 4(%esp), %eax - lgdtl (%eax) - - # 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 - mov %ax, %ss - - # Far jump to our new code segment! - movl $GDT_FLUSH_POSTJMP, %eax - ljmp *(%eax) -gdt_flush_postjmp: - ret -.size gdt_flush, . - gdt_flush - -.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 -.size tss_flush, . - tss_flush - -.section .data -GDT_FLUSH_POSTJMP: - .long gdt_flush_postjmp - .word 0x08 # 0x08 is the offset to our code segment