kernel/x86_64: x86_64 gdt handling code overhaul
Virtually no functional change, just rewriting the code from "C in *.cpp files" to C++. Use of constexpr may be advantageous but that code is not performance critical anyway.
This commit is contained in:
parent
c1dc104960
commit
cd59bf4349
@ -13,13 +13,6 @@
|
|||||||
#define USER_DATA_SEGMENT 3
|
#define USER_DATA_SEGMENT 3
|
||||||
#define USER_CODE_SEGMENT 4
|
#define USER_CODE_SEGMENT 4
|
||||||
|
|
||||||
#define TSS_BASE_SEGMENT 5
|
|
||||||
|
|
||||||
#define TSS_SEGMENT(cpu) (TSS_BASE_SEGMENT + cpu * 2)
|
|
||||||
|
|
||||||
#define GDT_SEGMENT_COUNT (TSS_BASE_SEGMENT + SMP_MAX_CPUS * 2)
|
|
||||||
|
|
||||||
|
|
||||||
#define KERNEL_CODE_SELECTOR ((KERNEL_CODE_SEGMENT << 3) | DPL_KERNEL)
|
#define KERNEL_CODE_SELECTOR ((KERNEL_CODE_SEGMENT << 3) | DPL_KERNEL)
|
||||||
#define KERNEL_DATA_SELECTOR ((KERNEL_DATA_SEGMENT << 3) | DPL_KERNEL)
|
#define KERNEL_DATA_SELECTOR ((KERNEL_DATA_SEGMENT << 3) | DPL_KERNEL)
|
||||||
|
|
||||||
@ -46,23 +39,6 @@ struct segment_descriptor {
|
|||||||
uint32 base1 : 8;
|
uint32 base1 : 8;
|
||||||
} _PACKED;
|
} _PACKED;
|
||||||
|
|
||||||
// Structure of a TSS segment descriptor.
|
|
||||||
struct tss_descriptor {
|
|
||||||
uint32 limit0 : 16;
|
|
||||||
uint32 base0 : 24;
|
|
||||||
uint32 type : 4;
|
|
||||||
uint32 desc_type : 1;
|
|
||||||
uint32 dpl : 2;
|
|
||||||
uint32 present : 1;
|
|
||||||
uint32 limit1 : 4;
|
|
||||||
uint32 available : 1;
|
|
||||||
uint32 unused1 : 2;
|
|
||||||
uint32 granularity : 1;
|
|
||||||
uint32 base1 : 8;
|
|
||||||
uint32 base2 : 32;
|
|
||||||
uint32 unused2 : 32;
|
|
||||||
} _PACKED;
|
|
||||||
|
|
||||||
// Structure of an interrupt descriptor.
|
// Structure of an interrupt descriptor.
|
||||||
struct interrupt_descriptor {
|
struct interrupt_descriptor {
|
||||||
uint32 base0 : 16;
|
uint32 base0 : 16;
|
||||||
@ -127,29 +103,6 @@ set_segment_descriptor(segment_descriptor* desc, uint8 type, uint8 dpl)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
set_tss_descriptor(segment_descriptor* _desc, uint64 base, uint32 limit)
|
|
||||||
{
|
|
||||||
clear_segment_descriptor(_desc);
|
|
||||||
clear_segment_descriptor(&_desc[1]);
|
|
||||||
|
|
||||||
// The TSS descriptor is a special format in 64-bit mode, it is 16 bytes
|
|
||||||
// instead of 8.
|
|
||||||
tss_descriptor* desc = (tss_descriptor*)_desc;
|
|
||||||
|
|
||||||
desc->base0 = base & 0xffffff;
|
|
||||||
desc->base1 = (base >> 24) & 0xff;
|
|
||||||
desc->base2 = (base >> 32);
|
|
||||||
desc->limit0 = limit & 0xffff;
|
|
||||||
desc->limit1 = (limit >> 16) & 0xf;
|
|
||||||
|
|
||||||
desc->present = 1;
|
|
||||||
desc->type = DT_TSS;
|
|
||||||
desc->desc_type = DT_SYSTEM_SEGMENT;
|
|
||||||
desc->dpl = DPL_KERNEL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
set_interrupt_descriptor(interrupt_descriptor* desc, uint64 addr, uint32 type,
|
set_interrupt_descriptor(interrupt_descriptor* desc, uint64 addr, uint32 type,
|
||||||
uint16 seg, uint32 dpl, uint32 ist)
|
uint16 seg, uint32 dpl, uint32 ist)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
|
* Copyright 2014, Paweł Dziepak, pdziepak@quarnos.org.
|
||||||
* Copyright 2012, Alex Smith, alex@alex-smith.me.uk.
|
* Copyright 2012, Alex Smith, alex@alex-smith.me.uk.
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
@ -18,38 +19,182 @@
|
|||||||
#define IDT_GATES_COUNT 256
|
#define IDT_GATES_COUNT 256
|
||||||
|
|
||||||
|
|
||||||
typedef void interrupt_handler_function(iframe* frame);
|
enum class DescriptorType : unsigned {
|
||||||
|
DataWritable = 0x2,
|
||||||
|
CodeExecuteOnly = 0x8,
|
||||||
|
TSS = 0x9,
|
||||||
|
};
|
||||||
|
|
||||||
|
class Descriptor {
|
||||||
|
public:
|
||||||
|
constexpr Descriptor();
|
||||||
|
inline Descriptor(uint32_t first, uint32_t second);
|
||||||
|
constexpr Descriptor(DescriptorType type, bool kernelOnly);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
union {
|
||||||
|
struct [[gnu::packed]] {
|
||||||
|
uint16_t fLimit0;
|
||||||
|
unsigned fBase0 :24;
|
||||||
|
unsigned fType :4;
|
||||||
|
unsigned fSystem :1;
|
||||||
|
unsigned fDPL :2;
|
||||||
|
unsigned fPresent :1;
|
||||||
|
unsigned fLimit1 :4;
|
||||||
|
unsigned fUnused :1;
|
||||||
|
unsigned fLong :1;
|
||||||
|
unsigned fDB :1;
|
||||||
|
unsigned fGranularity :1;
|
||||||
|
uint8_t fBase1;
|
||||||
|
};
|
||||||
|
|
||||||
|
uint32_t fDescriptor[2];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
class TSSDescriptor : public Descriptor {
|
||||||
|
public:
|
||||||
|
inline TSSDescriptor(uintptr_t base, size_t limit);
|
||||||
|
|
||||||
|
const Descriptor& GetLower() const { return *this; }
|
||||||
|
const Descriptor& GetUpper() const { return fSecond; }
|
||||||
|
|
||||||
|
static void LoadTSS(unsigned index);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Descriptor fSecond;
|
||||||
|
};
|
||||||
|
|
||||||
|
class GlobalDescriptorTable {
|
||||||
|
public:
|
||||||
|
constexpr GlobalDescriptorTable();
|
||||||
|
|
||||||
|
inline void Load() const;
|
||||||
|
|
||||||
|
unsigned SetTSS(unsigned cpu,
|
||||||
|
const TSSDescriptor& tss);
|
||||||
|
private:
|
||||||
|
static constexpr unsigned kFirstTSS = 5;
|
||||||
|
static constexpr unsigned kDescriptorCount
|
||||||
|
= kFirstTSS + SMP_MAX_CPUS * 2;
|
||||||
|
|
||||||
|
alignas(sizeof(Descriptor)) Descriptor fTable[kDescriptorCount];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
static segment_descriptor sGDT[GDT_SEGMENT_COUNT];
|
static GlobalDescriptorTable sGDT;
|
||||||
|
|
||||||
static interrupt_descriptor sIDT[IDT_GATES_COUNT];
|
static interrupt_descriptor sIDT[IDT_GATES_COUNT];
|
||||||
|
|
||||||
|
typedef void interrupt_handler_function(iframe* frame);
|
||||||
static const uint32 kInterruptHandlerTableSize = IDT_GATES_COUNT;
|
static const uint32 kInterruptHandlerTableSize = IDT_GATES_COUNT;
|
||||||
interrupt_handler_function* gInterruptHandlerTable[kInterruptHandlerTableSize];
|
interrupt_handler_function* gInterruptHandlerTable[kInterruptHandlerTableSize];
|
||||||
|
|
||||||
extern uint8 isr_array[kInterruptHandlerTableSize][16];
|
extern uint8 isr_array[kInterruptHandlerTableSize][16];
|
||||||
|
|
||||||
|
|
||||||
static inline void
|
constexpr bool
|
||||||
load_tss(int cpu)
|
is_code_segment(DescriptorType type)
|
||||||
{
|
{
|
||||||
uint16 segment = (TSS_SEGMENT(cpu) << 3) | DPL_KERNEL;
|
return type == DescriptorType::CodeExecuteOnly;
|
||||||
asm volatile("ltr %w0" : : "r" (segment));
|
};
|
||||||
|
|
||||||
|
|
||||||
|
constexpr
|
||||||
|
Descriptor::Descriptor()
|
||||||
|
:
|
||||||
|
fDescriptor { 0, 0 }
|
||||||
|
{
|
||||||
|
static_assert(sizeof(Descriptor) == sizeof(uint64_t),
|
||||||
|
"Invalid Descriptor size.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static inline void
|
Descriptor::Descriptor(uint32_t first, uint32_t second)
|
||||||
load_gdt()
|
:
|
||||||
|
fDescriptor { first, second }
|
||||||
{
|
{
|
||||||
struct {
|
}
|
||||||
uint16 limit;
|
|
||||||
void* address;
|
|
||||||
} _PACKED gdtDescriptor = {
|
constexpr
|
||||||
GDT_SEGMENT_COUNT * sizeof(segment_descriptor) - 1,
|
Descriptor::Descriptor(DescriptorType type, bool kernelOnly)
|
||||||
sGDT
|
:
|
||||||
|
fLimit0(-1),
|
||||||
|
fBase0(0),
|
||||||
|
fType(static_cast<unsigned>(type)),
|
||||||
|
fSystem(1),
|
||||||
|
fDPL(kernelOnly ? 0 : 3),
|
||||||
|
fPresent(1),
|
||||||
|
fLimit1(0xf),
|
||||||
|
fUnused(0),
|
||||||
|
fLong(is_code_segment(type) ? 1 : 0),
|
||||||
|
fDB(is_code_segment(type) ? 0 : 1),
|
||||||
|
fGranularity(1),
|
||||||
|
fBase1(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TSSDescriptor::TSSDescriptor(uintptr_t base, size_t limit)
|
||||||
|
:
|
||||||
|
fSecond(base >> 32, 0)
|
||||||
|
{
|
||||||
|
fLimit0 = static_cast<uint16_t>(limit);
|
||||||
|
fBase0 = base & 0xffffff;
|
||||||
|
fType = static_cast<unsigned>(DescriptorType::TSS);
|
||||||
|
fPresent = 1;
|
||||||
|
fLimit1 = (limit >> 16) & 0xf;
|
||||||
|
fBase1 = static_cast<uint8_t>(base >> 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
TSSDescriptor::LoadTSS(unsigned index)
|
||||||
|
{
|
||||||
|
asm volatile("ltr %w0" : : "r" (index << 3));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
constexpr
|
||||||
|
GlobalDescriptorTable::GlobalDescriptorTable()
|
||||||
|
:
|
||||||
|
fTable {
|
||||||
|
Descriptor(),
|
||||||
|
Descriptor(DescriptorType::CodeExecuteOnly, true),
|
||||||
|
Descriptor(DescriptorType::DataWritable, true),
|
||||||
|
Descriptor(DescriptorType::DataWritable, false),
|
||||||
|
Descriptor(DescriptorType::CodeExecuteOnly, false),
|
||||||
|
}
|
||||||
|
{
|
||||||
|
static_assert(kDescriptorCount <= 8192,
|
||||||
|
"GDT cannot contain more than 8192 descriptors");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
GlobalDescriptorTable::Load() const
|
||||||
|
{
|
||||||
|
struct [[gnu::packed]] {
|
||||||
|
uint16_t fLimit;
|
||||||
|
const void* fAddress;
|
||||||
|
} gdtDescriptor = {
|
||||||
|
sizeof(fTable) - 1,
|
||||||
|
static_cast<const void*>(fTable),
|
||||||
};
|
};
|
||||||
|
|
||||||
asm volatile("lgdt %0" : : "m" (gdtDescriptor));
|
asm volatile("lgdt %0" : : "m" (gdtDescriptor));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unsigned
|
||||||
|
GlobalDescriptorTable::SetTSS(unsigned cpu, const TSSDescriptor& tss)
|
||||||
|
{
|
||||||
|
auto index = kFirstTSS + cpu * 2;
|
||||||
|
ASSERT(index + 1 < kDescriptorCount);
|
||||||
|
fTable[index] = tss.GetLower();
|
||||||
|
fTable[index + 1] = tss.GetUpper();
|
||||||
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -96,35 +241,24 @@ x86_64_general_protection_fault(iframe* frame)
|
|||||||
void
|
void
|
||||||
x86_descriptors_preboot_init_percpu(kernel_args* args, int cpu)
|
x86_descriptors_preboot_init_percpu(kernel_args* args, int cpu)
|
||||||
{
|
{
|
||||||
if (cpu == 0) {
|
new(&sGDT) GlobalDescriptorTable;
|
||||||
STATIC_ASSERT(GDT_SEGMENT_COUNT <= 8192);
|
sGDT.Load();
|
||||||
|
|
||||||
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));
|
memset(&gCPU[cpu].arch.tss, 0, sizeof(struct tss));
|
||||||
gCPU[cpu].arch.tss.io_map_base = 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()).
|
// Set up the double fault IST entry (see x86_descriptors_init()).
|
||||||
struct tss* tss = &gCPU[cpu].arch.tss;
|
struct tss* tss = &gCPU[cpu].arch.tss;
|
||||||
size_t stackSize;
|
size_t stackSize;
|
||||||
tss->ist1 = (addr_t)x86_get_double_fault_stack(cpu, &stackSize);
|
tss->ist1 = (addr_t)x86_get_double_fault_stack(cpu, &stackSize);
|
||||||
tss->ist1 += stackSize;
|
tss->ist1 += stackSize;
|
||||||
|
|
||||||
|
// Set up the descriptor for this TSS.
|
||||||
|
auto tssIndex = sGDT.SetTSS(cpu,
|
||||||
|
TSSDescriptor(uintptr_t(&gCPU[cpu].arch.tss), sizeof(struct tss)));
|
||||||
|
TSSDescriptor::LoadTSS(tssIndex);
|
||||||
|
|
||||||
load_idt();
|
load_idt();
|
||||||
load_gdt();
|
|
||||||
load_tss(cpu);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user