x86[_64]: Separate bootloader and kernel GDT and IDT logic

From now on bootloader sets up its own minimal valid GDT and IDT. Then
the kernel replaces them with its own tables.
This commit is contained in:
Pawel Dziepak 2014-01-27 23:33:48 +01:00
parent e646703a6e
commit 527da4ca8a
13 changed files with 162 additions and 270 deletions

View File

@ -78,11 +78,6 @@ struct interrupt_descriptor {
uint32 reserved : 32;
} _PACKED;
struct gdt_idt_descr {
uint16 limit;
addr_t base;
} _PACKED;
struct tss {
uint32 _reserved1;
uint64 sp0;

View File

@ -17,9 +17,6 @@
#define _PACKED __attribute__((packed))
#define IDT_LIMIT 0x800
#define GDT_LIMIT 0x800
// kernel args
typedef struct {
// architecture specific
@ -30,10 +27,6 @@ typedef struct {
uint32 num_pgtables;
uint32 pgtables[MAX_BOOT_PTABLES];
uint64 virtual_end;
uint32 phys_idt;
uint64 vir_idt;
uint32 phys_gdt;
uint64 vir_gdt;
uint64 page_hole;
// smp stuff
uint32 apic_time_cv_factor; // apic ticks per second

View File

@ -49,8 +49,6 @@ enum gate_types {
void x86_descriptors_preboot_init_percpu(kernel_args* args, int cpu);
void x86_descriptors_init(kernel_args* args);
void x86_descriptors_init_percpu(kernel_args* args, int cpu);
status_t x86_descriptors_init_post_vm(kernel_args* args);
#endif // !_ASSEMBLER

View File

@ -229,17 +229,3 @@ set_debug_idt()
set_idt(sDebugIDTDescriptor);
}
void
interrupts_init_kernel_idt(void* idt, size_t idtSize)
{
// clear it but copy the descriptors we've set up for the exceptions
memset(idt, 0, idtSize);
memcpy(idt, sDebugIDT, sizeof(sDebugIDT));
// load the idt
gdt_idt_descr idtDescriptor;
idtDescriptor.limit = idtSize - 1;
idtDescriptor.base = idt;
set_idt(idtDescriptor);
}

View File

@ -26,7 +26,6 @@ __BEGIN_DECLS
void interrupts_init();
void set_debug_idt();
void restore_bios_idt();
void interrupts_init_kernel_idt(void* idt, size_t idtSize);
__END_DECLS

View File

@ -23,8 +23,8 @@
#include <kernel.h>
#include "debug.h"
#include "smp.h"
#include "mmu.h"
#include "smp.h"
static const uint64 kTableMappingFlags = 0x7;
@ -34,8 +34,7 @@ static const uint64 kPageMappingFlags = 0x103;
extern "C" void long_enter_kernel(int currentCPU, uint64 stackTop);
extern uint32 gLongPhysicalGDT;
extern uint64 gLongVirtualGDT;
extern uint64 gLongGDT;
extern uint32 gLongPhysicalPML4;
extern uint64 gLongKernelEntry;
@ -63,47 +62,21 @@ fix_address(FixedWidthPointer<Type>& p)
static void
long_gdt_init()
{
// Allocate memory for the GDT.
segment_descriptor* gdt = (segment_descriptor*)
mmu_allocate_page(&gKernelArgs.arch_args.phys_gdt);
gKernelArgs.arch_args.vir_gdt = fix_address((addr_t)gdt);
dprintf("GDT at phys 0x%lx, virt 0x%llx\n", gKernelArgs.arch_args.phys_gdt,
gKernelArgs.arch_args.vir_gdt);
clear_segment_descriptor(&gdt[0]);
clear_segment_descriptor(&gBootGDT[0]);
// Set up code/data segments (TSS segments set up later in the kernel).
set_segment_descriptor(&gdt[KERNEL_CODE_SEGMENT], DT_CODE_EXECUTE_ONLY,
set_segment_descriptor(&gBootGDT[KERNEL_CODE_SEGMENT], DT_CODE_EXECUTE_ONLY,
DPL_KERNEL);
set_segment_descriptor(&gdt[KERNEL_DATA_SEGMENT], DT_DATA_WRITEABLE,
set_segment_descriptor(&gBootGDT[KERNEL_DATA_SEGMENT], DT_DATA_WRITEABLE,
DPL_KERNEL);
set_segment_descriptor(&gdt[USER_CODE_SEGMENT], DT_CODE_EXECUTE_ONLY,
set_segment_descriptor(&gBootGDT[USER_CODE_SEGMENT], DT_CODE_EXECUTE_ONLY,
DPL_USER);
set_segment_descriptor(&gdt[USER_DATA_SEGMENT], DT_DATA_WRITEABLE,
set_segment_descriptor(&gBootGDT[USER_DATA_SEGMENT], DT_DATA_WRITEABLE,
DPL_USER);
// Used by long_enter_kernel().
gLongPhysicalGDT = gKernelArgs.arch_args.phys_gdt;
gLongVirtualGDT = gKernelArgs.arch_args.vir_gdt;
}
static void
long_idt_init()
{
interrupt_descriptor* idt = (interrupt_descriptor*)
mmu_allocate_page(&gKernelArgs.arch_args.phys_idt);
gKernelArgs.arch_args.vir_idt = fix_address((addr_t)idt);
dprintf("IDT at phys %#lx, virt %#llx\n", gKernelArgs.arch_args.phys_idt,
gKernelArgs.arch_args.vir_idt);
// The 32-bit kernel gets an IDT with the loader's exception handlers until
// it can set up its own. Can't do that here because they won't work after
// switching to long mode. Therefore, just clear the IDT and leave the
// kernel to set it up.
memset(idt, 0, B_PAGE_SIZE);
gLongGDT = fix_address((addr_t)gBootGDT);
dprintf("GDT at 0x%llx\n", gLongGDT);
}
@ -340,7 +313,6 @@ long_start_kernel()
smp_init_other_cpus();
long_gdt_init();
long_idt_init();
long_mmu_init();
debug_cleanup();
convert_kernel_args();

View File

@ -8,6 +8,8 @@
#define __x86_64__
#include <arch/x86/descriptors.h>
#include "mmu.h"
#undef __x86_64__
@ -25,10 +27,6 @@ FUNCTION(long_enter_kernel):
movl 8(%esp), %edi
movl 12(%esp), %esi
// We're about to disable paging, so we need to load the the physical
// address of our GDT.
lgdtl long_phys_gdtr
// Currently running with 32-bit paging tables at an identity mapped
// address. To switch to 64-bit paging we must first disable 32-bit paging,
// otherwise loading the new CR3 will fault.
@ -57,6 +55,9 @@ FUNCTION(long_enter_kernel):
orl $(1 << 31), %ecx
movl %ecx, %cr0
// Load 64-bit enabled GDT
lgdtl long_gdtr
// Jump into the 64-bit code segment.
ljmp $KERNEL_CODE_SELECTOR, $.Llmode
.align 8
@ -71,9 +72,6 @@ FUNCTION(long_enter_kernel):
mov %ax, %fs
mov %ax, %gs
// Load the virtual address of the GDT.
lgdtq long_virt_gdtr(%rip)
// Set the stack pointer.
movl %edi, %esp
shl $32, %rsi
@ -94,16 +92,11 @@ FUNCTION(long_enter_kernel):
.data
long_phys_gdtr:
.word GDT_LIMIT - 1
SYMBOL(gLongPhysicalGDT):
long_gdtr:
.word BOOT_GDT_SEGMENT_COUNT * 8 - 1
SYMBOL(gLongGDT):
.long 0
long_virt_gdtr:
.word GDT_LIMIT - 1
SYMBOL(gLongVirtualGDT):
.quad 0
SYMBOL(gLongPhysicalPML4):
.long 0

View File

@ -69,6 +69,8 @@ struct extended_memory {
};
segment_descriptor gBootGDT[BOOT_GDT_SEGMENT_COUNT];
static const uint32 kDefaultPageTableFlags = 0x07; // present, user, R/W
static const size_t kMaxKernelSize = 0x1000000; // 16 MB for the kernel
@ -535,76 +537,36 @@ extern "C" void
mmu_init_for_kernel(void)
{
TRACE("mmu_init_for_kernel\n");
// set up a new idt
{
uint32 *idt;
// find a new idt
idt = (uint32 *)get_next_physical_page();
gKernelArgs.arch_args.phys_idt = (uint32)idt;
TRACE("idt at %p\n", idt);
// map the idt into virtual space
gKernelArgs.arch_args.vir_idt = (uint32)get_next_virtual_page();
map_page(gKernelArgs.arch_args.vir_idt, (uint32)idt, kDefaultPageFlags);
// initialize it
interrupts_init_kernel_idt((void*)(addr_t)gKernelArgs.arch_args.vir_idt,
IDT_LIMIT);
TRACE("idt at virtual address 0x%llx\n", gKernelArgs.arch_args.vir_idt);
}
// set up a new gdt
{
struct gdt_idt_descr gdtDescriptor;
segment_descriptor *gdt;
// find a new gdt
gdt = (segment_descriptor *)get_next_physical_page();
gKernelArgs.arch_args.phys_gdt = (uint32)gdt;
// put standard segment descriptors in GDT
clear_segment_descriptor(&gBootGDT[0]);
TRACE("gdt at %p\n", gdt);
// seg 0x08 - kernel 4GB code
set_segment_descriptor(&gBootGDT[KERNEL_CODE_SEGMENT], 0, 0xffffffff,
DT_CODE_READABLE, DPL_KERNEL);
// map the gdt into virtual space
gKernelArgs.arch_args.vir_gdt = (uint32)get_next_virtual_page();
map_page(gKernelArgs.arch_args.vir_gdt, (uint32)gdt, kDefaultPageFlags);
// seg 0x10 - kernel 4GB data
set_segment_descriptor(&gBootGDT[KERNEL_DATA_SEGMENT], 0, 0xffffffff,
DT_DATA_WRITEABLE, DPL_KERNEL);
// put standard segment descriptors in it
segment_descriptor* virtualGDT
= (segment_descriptor*)(addr_t)gKernelArgs.arch_args.vir_gdt;
clear_segment_descriptor(&virtualGDT[0]);
// seg 0x1b - ring 3 user 4GB code
set_segment_descriptor(&gBootGDT[USER_CODE_SEGMENT], 0, 0xffffffff,
DT_CODE_READABLE, DPL_USER);
// seg 0x08 - kernel 4GB code
set_segment_descriptor(&virtualGDT[KERNEL_CODE_SEGMENT], 0, 0xffffffff,
DT_CODE_READABLE, DPL_KERNEL);
// seg 0x23 - ring 3 user 4GB data
set_segment_descriptor(&gBootGDT[USER_DATA_SEGMENT], 0, 0xffffffff,
DT_DATA_WRITEABLE, DPL_USER);
// seg 0x10 - kernel 4GB data
set_segment_descriptor(&virtualGDT[KERNEL_DATA_SEGMENT], 0, 0xffffffff,
DT_DATA_WRITEABLE, DPL_KERNEL);
// load the GDT
struct gdt_idt_descr gdtDescriptor;
gdtDescriptor.limit = sizeof(gBootGDT);
gdtDescriptor.base = gBootGDT;
// seg 0x1b - ring 3 user 4GB code
set_segment_descriptor(&virtualGDT[USER_CODE_SEGMENT], 0, 0xffffffff,
DT_CODE_READABLE, DPL_USER);
asm("lgdt %0" : : "m" (gdtDescriptor));
// seg 0x23 - ring 3 user 4GB data
set_segment_descriptor(&virtualGDT[USER_DATA_SEGMENT], 0, 0xffffffff,
DT_DATA_WRITEABLE, DPL_USER);
// virtualGDT[5] and above will be filled later by the kernel
// to contain the TSS descriptors, and for TLS (one for every CPU)
// load the GDT
gdtDescriptor.limit = GDT_LIMIT - 1;
gdtDescriptor.base = (void*)(addr_t)gKernelArgs.arch_args.vir_gdt;
asm("lgdt %0;"
: : "m" (gdtDescriptor));
TRACE("gdt at virtual address %p\n",
(void*)gKernelArgs.arch_args.vir_gdt);
}
TRACE("gdt at virtual address %p\n", gBootGDT);
// Save the memory we've virtually allocated (for the kernel and other
// stuff)

View File

@ -6,9 +6,21 @@
#define MMU_H
#include <arch/x86/descriptors.h>
#define BOOT_GDT_SEGMENT_COUNT (USER_DATA_SEGMENT + 1)
#ifndef _ASSEMBLER
#include <SupportDefs.h>
extern segment_descriptor gBootGDT[BOOT_GDT_SEGMENT_COUNT];
// For use with mmu_map_physical_memory()
static const uint32 kDefaultPageFlags = 0x3; // present, R/W
@ -33,4 +45,6 @@ extern bool mmu_get_virtual_mapping(addr_t virtualAddress,
}
#endif
#endif // !_ASSEMBLER
#endif /* MMU_H */

View File

@ -97,16 +97,12 @@ smp_start_kernel(void)
asm("cld");
asm("fninit");
// Set up the final idt
idt_descr.limit = IDT_LIMIT - 1;
idt_descr.base = (uint32 *)(addr_t)gKernelArgs.arch_args.vir_idt;
asm("lidt %0;"
: : "m" (idt_descr));
// Set up idt
set_debug_idt();
// Set up the final gdt
gdt_descr.limit = GDT_LIMIT - 1;
gdt_descr.base = (uint32 *)gKernelArgs.arch_args.vir_gdt;
gdt_descr.limit = sizeof(gBootGDT) - 1;
gdt_descr.base = gBootGDT;
asm("lgdt %0;"
: : "m" (gdt_descr));

View File

@ -99,11 +99,41 @@ set_task_gate(int32 cpu, int32 n, int32 segment)
}
static void
static inline void
load_tss()
{
short seg = (TSS_SEGMENT << 3) | DPL_KERNEL;
asm("ltr %%ax" : : "a" (seg));
uint16 segment = (TSS_SEGMENT << 3) | DPL_KERNEL;
asm("ltr %w0" : : "r" (segment));
}
static inline void
load_gdt(int cpu)
{
struct {
uint16 limit;
void* address;
} _PACKED gdtDescriptor = {
GDT_SEGMENT_COUNT * sizeof(segment_descriptor) - 1,
gGDTs[cpu]
};
asm volatile("lgdt %0" : : "m" (gdtDescriptor));
}
static inline void
load_idt(int cpu)
{
struct {
uint16 limit;
void* address;
} _PACKED idtDescriptor = {
IDT_GATES_COUNT * sizeof(interrupt_descriptor) - 1,
&sIDTs[cpu]
};
asm volatile("lidt %0" : : "m" (idtDescriptor));
}
@ -181,8 +211,8 @@ init_double_fault(int cpuNum)
struct tss* tss = &gCPU[cpuNum].arch.double_fault_tss;
memset(tss, 0, sizeof(struct tss));
size_t stackSize;
tss->sp0 = (addr_t)x86_get_double_fault_stack(cpuNum, &stackSize);
size_t stackSize = 0;
//tss->sp0 = (addr_t)x86_get_double_fault_stack(cpuNum, &stackSize);
tss->sp0 += stackSize;
tss->ss0 = KERNEL_DATA_SELECTOR;
tss->cr3 = x86_read_cr3();
@ -206,21 +236,6 @@ init_double_fault(int cpuNum)
}
static void
load_gdt(int cpu)
{
struct {
uint16 limit;
void* address;
} _PACKED gdt_descriptor = {
GDT_SEGMENT_COUNT * sizeof(segment_descriptor) - 1,
gGDTs[cpu]
};
asm volatile("lgdt %0" : : "m" (gdt_descriptor));
}
static void
init_gdt_percpu(kernel_args* args, int cpu)
{
@ -264,22 +279,7 @@ init_gdt_percpu(kernel_args* args, int cpu)
load_tss();
// set kernel TLS segment
asm volatile("movw %0, %%gs" : : "r" (KERNEL_TLS_SEGMENT << 3));
}
static void
load_idt(int cpu)
{
struct {
uint16 limit;
void* address;
} _PACKED idt_descriptor = {
IDT_GATES_COUNT * sizeof(interrupt_descriptor) - 1,
&sIDTs[cpu]
};
asm volatile("lidt %0" : : "m" (idt_descriptor));
asm volatile("movw %w0, %%gs" : : "r" (KERNEL_TLS_SEGMENT << 3));
}
@ -593,16 +593,3 @@ x86_descriptors_init(kernel_args* args)
table[19] = x86_unexpected_exception; // SIMD Floating-Point Exception (#XF)
}
void
x86_descriptors_init_percpu(kernel_args* /* args */, int /* cpu */)
{
}
status_t
x86_descriptors_init_post_vm(kernel_args* /* args */)
{
return B_OK;
}

View File

@ -15,23 +15,56 @@
#include <arch/user_debugger.h>
#define IDT_GATES_COUNT 256
typedef void interrupt_handler_function(iframe* frame);
static segment_descriptor* sGDT;
static interrupt_descriptor* sIDT;
static segment_descriptor sGDT[GDT_SEGMENT_COUNT];
static interrupt_descriptor sIDT[IDT_GATES_COUNT];
static const uint32 kInterruptHandlerTableSize = 256;
static const uint32 kInterruptHandlerTableSize = IDT_GATES_COUNT;
interrupt_handler_function* gInterruptHandlerTable[kInterruptHandlerTableSize];
extern uint8 isr_array[kInterruptHandlerTableSize][16];
static void
static inline void
load_tss(int cpu)
{
uint16 seg = (TSS_SEGMENT(cpu) << 3) | DPL_KERNEL;
asm volatile("ltr %%ax" :: "a" (seg));
uint16 segment = (TSS_SEGMENT(cpu) << 3) | DPL_KERNEL;
asm volatile("ltr %w0" : : "a" (segment));
}
static inline void
load_gdt()
{
struct {
uint16 limit;
void* address;
} _PACKED gdtDescriptor = {
GDT_SEGMENT_COUNT * sizeof(segment_descriptor) - 1,
sGDT
};
asm volatile("lgdt %0" : : "m" (gdtDescriptor));
}
static inline void
load_idt()
{
struct {
uint16 limit;
void* address;
} _PACKED idtDescriptor = {
IDT_GATES_COUNT * sizeof(interrupt_descriptor) - 1,
sIDT
};
asm volatile("lidt %0" : : "m" (idtDescriptor));
}
@ -61,18 +94,43 @@ x86_64_general_protection_fault(iframe* frame)
void
x86_descriptors_preboot_init_percpu(kernel_args* /* args */, int /* cpu */)
x86_descriptors_preboot_init_percpu(kernel_args* args, int cpu)
{
if (cpu == 0) {
STATIC_ASSERT(GDT_SEGMENT_COUNT <= 8192);
set_segment_descriptor(&sGDT[KERNEL_CODE_SEGMENT], DT_CODE_EXECUTE_ONLY,
DPL_KERNEL);
set_segment_descriptor(&sGDT[KERNEL_DATA_SEGMENT], DT_DATA_WRITEABLE,
DPL_KERNEL);
set_segment_descriptor(&sGDT[USER_CODE_SEGMENT], DT_CODE_EXECUTE_ONLY,
DPL_USER);
set_segment_descriptor(&sGDT[USER_DATA_SEGMENT], DT_DATA_WRITEABLE,
DPL_USER);
}
memset(&gCPU[cpu].arch.tss, 0, sizeof(struct tss));
gCPU[cpu].arch.tss.io_map_base = sizeof(struct tss);
// Set up the descriptor for this TSS.
set_tss_descriptor(&sGDT[TSS_SEGMENT(cpu)], (addr_t)&gCPU[cpu].arch.tss,
sizeof(struct tss));
// Set up the double fault IST entry (see x86_descriptors_init()).
struct tss* tss = &gCPU[cpu].arch.tss;
size_t stackSize;
tss->ist1 = (addr_t)x86_get_double_fault_stack(cpu, &stackSize);
tss->ist1 += stackSize;
load_idt();
load_gdt();
load_tss(cpu);
}
void
x86_descriptors_init(kernel_args* args)
{
// The boot loader sets up a GDT and allocates an empty IDT for us.
sGDT = (segment_descriptor*)args->arch_args.vir_gdt;
sIDT = (interrupt_descriptor*)args->arch_args.vir_idt;
// Fill out the IDT, pointing each entry to the corresponding entry in the
// ISR array created in arch_interrupts.S (see there to see how this works).
for(uint32 i = 0; i < kInterruptHandlerTableSize; i++) {
@ -120,59 +178,3 @@ x86_descriptors_init(kernel_args* args)
table[19] = x86_unexpected_exception; // SIMD Floating-Point Exception (#XF)
}
void
x86_descriptors_init_percpu(kernel_args* args, int cpu)
{
// Load the IDT.
gdt_idt_descr idtr = {
256 * sizeof(interrupt_descriptor) - 1,
(addr_t)sIDT
};
asm volatile("lidt %0" :: "m" (idtr));
// Load the TSS for non-boot CPUs (boot CPU gets done below).
if (cpu != 0) {
load_tss(cpu);
}
}
status_t
x86_descriptors_init_post_vm(kernel_args* args)
{
area_id area;
// Create an area for the GDT.
area = create_area("gdt", (void**)&sGDT, B_EXACT_ADDRESS, B_PAGE_SIZE,
B_ALREADY_WIRED, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
if (area < B_OK)
return area;
// Same for the IDT.
area = create_area("idt", (void**)&sIDT, B_EXACT_ADDRESS, B_PAGE_SIZE,
B_ALREADY_WIRED, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
if (area < B_OK)
return area;
for (uint32 i = 0; i < args->num_cpus; i++) {
// Set up the task state segment.
memset(&gCPU[i].arch.tss, 0, sizeof(struct tss));
gCPU[i].arch.tss.io_map_base = sizeof(struct tss);
// Set up the descriptor for this TSS.
set_tss_descriptor(&sGDT[TSS_SEGMENT(i)], (addr_t)&gCPU[i].arch.tss,
sizeof(struct tss));
// Set up the double fault IST entry (see x86_descriptors_init()).
struct tss* tss = &gCPU[i].arch.tss;
size_t stackSize;
tss->ist1 = (addr_t)x86_get_double_fault_stack(i, &stackSize);
tss->ist1 += stackSize;
}
// Load the TSS for the boot CPU.
load_tss(0);
return B_OK;
}

View File

@ -1053,8 +1053,6 @@ detect_amdc1e_noarat()
status_t
arch_cpu_init_percpu(kernel_args* args, int cpu)
{
x86_descriptors_init_percpu(args, cpu);
detect_cpu(cpu);
if (!gCpuIdleFunc) {
@ -1115,9 +1113,6 @@ arch_cpu_init_post_vm(kernel_args* args)
&virtualRestrictions, &physicalRestrictions,
(void**)&sDoubleFaultStacks);
// More descriptor table setup.
x86_descriptors_init_post_vm(args);
X86PagingStructures* kernelPagingStructures
= static_cast<X86VMTranslationMap*>(
VMAddressSpace::Kernel()->TranslationMap())->PagingStructures();