Ported the GDT and TSS to x64.
This commit is contained in:
parent
a1cf7e9dbc
commit
4579384312
|
@ -33,26 +33,45 @@ namespace Sortix
|
||||||
{
|
{
|
||||||
namespace GDT
|
namespace GDT
|
||||||
{
|
{
|
||||||
// Lets us access our ASM functions from our C++ code.
|
|
||||||
extern "C" void gdt_flush(addr_t);
|
extern "C" void gdt_flush(addr_t);
|
||||||
extern "C" void tss_flush();
|
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;
|
gdt_ptr_t gdt_ptr;
|
||||||
tss_entry_t tss_entry;
|
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()
|
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;
|
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(0, 0, 0, 0, 0); // Null segment
|
||||||
SetGate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF); // Code segment
|
SetGate(1, 0, 0xFFFFFFFF, 0x9A, gran); // Code segment
|
||||||
SetGate(2, 0, 0xFFFFFFFF, 0x92, 0xCF); // Data segment
|
SetGate(2, 0, 0xFFFFFFFF, 0x92, gran); // Data segment
|
||||||
SetGate(3, 0, 0xFFFFFFFF, 0xFA, 0xCF); // User mode code segment
|
SetGate(3, 0, 0xFFFFFFFF, 0xFA, gran); // User mode code segment
|
||||||
SetGate(4, 0, 0xFFFFFFFF, 0xF2, 0xCF); // User mode data segment
|
SetGate(4, 0, 0xFFFFFFFF, 0xF2, gran); // User mode data segment
|
||||||
|
|
||||||
WriteTSS(5, 0x10, 0x0);
|
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);
|
gdt_flush((addr_t) &gdt_ptr);
|
||||||
tss_flush();
|
tss_flush();
|
||||||
}
|
}
|
||||||
|
@ -60,47 +79,76 @@ namespace Sortix
|
||||||
// Set the value of a GDT entry.
|
// Set the value of a GDT entry.
|
||||||
void SetGate(int32_t num, uint32_t base, uint32_t limit, uint8_t access, uint8_t gran)
|
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_entry_t* entry = (gdt_entry_t*) (&gdt_entries[num]);
|
||||||
gdt_entries[num].base_middle = (base >> 16) & 0xFF;
|
|
||||||
gdt_entries[num].base_high = (base >> 24) & 0xFF;
|
|
||||||
|
|
||||||
gdt_entries[num].limit_low = (limit & 0xFFFF);
|
entry->base_low = (base & 0xFFFF);
|
||||||
gdt_entries[num].granularity = (limit >> 16) & 0x0F;
|
entry->base_middle = (base >> 16) & 0xFF;
|
||||||
|
entry->base_high = (base >> 24) & 0xFF;
|
||||||
gdt_entries[num].granularity |= gran & 0xF0;
|
|
||||||
gdt_entries[num].access = access;
|
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.
|
// 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.
|
// First, let's compute the base and limit of our entry in the GDT.
|
||||||
addr_t base = (addr_t) &tss_entry;
|
addr_t base = (addr_t) &tss_entry;
|
||||||
uint32_t limit = base + sizeof(tss_entry);
|
uint32_t limit = base + sizeof(tss_entry);
|
||||||
|
|
||||||
// Now, add our TSS descriptor's address to the GDT.
|
// Now, add our TSS descriptor's address to the GDT.
|
||||||
SetGate(num, base, limit, 0xE9, 0x00);
|
#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.
|
// Ensure the descriptor is initially zero.
|
||||||
Memory::Set(&tss_entry, 0, sizeof(tss_entry));
|
Memory::Set(&tss_entry, 0, sizeof(tss_entry));
|
||||||
|
|
||||||
tss_entry.ss0 = ss0; // Set the kernel stack segment.
|
#ifdef PLATFORM_X86
|
||||||
tss_entry.esp0 = esp0; // Set the kernel stack pointer.
|
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
|
// Here we set the cs, ss, ds, es, fs and gs entries in the TSS.
|
||||||
// segments should be loaded when the processor switches to kernel mode. Therefore
|
// These specify what segments should be loaded when the processor
|
||||||
// they are just our normal kernel code/data segments - 0x08 and 0x10 respectively,
|
// switches to kernel mode. Therefore they are just our normal
|
||||||
// but with the last two bits set, making 0x0b and 0x13. The setting of these bits
|
// kernel code/data segments - 0x08 and 0x10 respectively, but with
|
||||||
// sets the RPL (requested privilege level) to 3, meaning that this TSS can be used
|
// the last two bits set, making 0x0b and 0x13. The setting of these
|
||||||
// to switch to kernel mode from ring 3.
|
// bits sets the RPL (requested privilege level) to 3, meaning that
|
||||||
tss_entry.cs = 0x08 | 0x3;
|
// this TSS can be used to switch to kernel mode from ring 3.
|
||||||
tss_entry.ss = tss_entry.ds = tss_entry.es = tss_entry.fs = tss_entry.gs = 0x10 | 0x3;
|
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)
|
void SetKernelStack(size_t* stack)
|
||||||
{
|
{
|
||||||
#ifdef PLATFORM_X86
|
#ifdef PLATFORM_X86
|
||||||
tss_entry.esp0 = (uint32_t) stack;
|
tss_entry.esp0 = (uint32_t) stack;
|
||||||
|
#elif defined(PLATFORM_X64)
|
||||||
|
tss_entry.stack0 = (uint64_t) stack;
|
||||||
#else
|
#else
|
||||||
#warning "TSS is not yet supported on this arch!"
|
#warning "TSS is not yet supported on this arch!"
|
||||||
while(true);
|
while(true);
|
||||||
|
|
|
@ -42,7 +42,20 @@ namespace Sortix
|
||||||
uint8_t base_high; // The last 8 bits of the base.
|
uint8_t base_high; // The last 8 bits of the base.
|
||||||
} __attribute__((packed));
|
} __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_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
|
// 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
|
// 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;
|
typedef struct gdt_ptr_struct gdt_ptr_t;
|
||||||
|
|
||||||
// A struct describing a Task State Segment.
|
// A struct describing a Task State Segment.
|
||||||
|
#ifdef PLATFORM_X86
|
||||||
struct tss_entry_struct
|
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 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 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 ss0; // The stack segment to load when we change to kernel mode.
|
||||||
uint32_t esp1; // Unused...
|
uint32_t esp1; // Unused...
|
||||||
uint32_t ss1;
|
uint32_t ss1;
|
||||||
uint32_t esp2;
|
uint32_t esp2;
|
||||||
uint32_t ss2;
|
uint32_t ss2;
|
||||||
uint32_t cr3;
|
uint32_t cr3;
|
||||||
uint32_t eip;
|
uint32_t eip;
|
||||||
uint32_t eflags;
|
uint32_t eflags;
|
||||||
uint32_t eax;
|
uint32_t eax;
|
||||||
uint32_t ecx;
|
uint32_t ecx;
|
||||||
uint32_t edx;
|
uint32_t edx;
|
||||||
uint32_t ebx;
|
uint32_t ebx;
|
||||||
uint32_t esp;
|
uint32_t esp;
|
||||||
uint32_t ebp;
|
uint32_t ebp;
|
||||||
uint32_t esi;
|
uint32_t esi;
|
||||||
uint32_t edi;
|
uint32_t edi;
|
||||||
uint32_t es; // The value to load into ES when we change to kernel mode.
|
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 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 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 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 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 gs; // The value to load into GS when we change to kernel mode.
|
||||||
uint32_t ldt; // Unused...
|
uint32_t ldt; // Unused...
|
||||||
uint16_t trap;
|
uint16_t trap;
|
||||||
uint16_t iomap_base;
|
uint16_t iomap_base;
|
||||||
} __attribute__((packed));
|
} __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;
|
typedef struct tss_entry_struct tss_entry_t;
|
||||||
|
|
||||||
void Init();
|
void Init();
|
||||||
void SetGate(int32_t num, uint32_t base, uint32_t limit, uint8_t access, uint8_t gran);
|
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);
|
void SetKernelStack(size_t* stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
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
|
||||||
|
|
Loading…
Reference in New Issue