Refactor kernel GDT code.

This commit is contained in:
Jonas 'Sortie' Termansen 2013-06-20 02:51:54 +02:00
parent c77d9395cd
commit c36b35adc2
5 changed files with 230 additions and 355 deletions

View File

@ -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 \

View File

@ -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 <http://www.gnu.org/licenses/>.
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

View File

@ -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 <sortix/kernel/platform.h>
#include <stdint.h>
#include <string.h>
#include <sortix/kernel/cpu.h>
#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

View File

@ -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

View File

@ -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 <http://www.gnu.org/licenses/>.
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