diff --git a/headers/private/kernel/arch/x86/64/descriptors.h b/headers/private/kernel/arch/x86/64/descriptors.h index 78d02c716f..66512b314d 100644 --- a/headers/private/kernel/arch/x86/64/descriptors.h +++ b/headers/private/kernel/arch/x86/64/descriptors.h @@ -13,13 +13,6 @@ #define USER_DATA_SEGMENT 3 #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_DATA_SELECTOR ((KERNEL_DATA_SEGMENT << 3) | DPL_KERNEL) @@ -46,23 +39,6 @@ struct segment_descriptor { uint32 base1 : 8; } _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. struct interrupt_descriptor { 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 set_interrupt_descriptor(interrupt_descriptor* desc, uint64 addr, uint32 type, uint16 seg, uint32 dpl, uint32 ist) diff --git a/src/system/kernel/arch/x86/64/descriptors.cpp b/src/system/kernel/arch/x86/64/descriptors.cpp index b328eb28f9..37e0ef29a1 100644 --- a/src/system/kernel/arch/x86/64/descriptors.cpp +++ b/src/system/kernel/arch/x86/64/descriptors.cpp @@ -1,4 +1,5 @@ /* + * Copyright 2014, Paweł Dziepak, pdziepak@quarnos.org. * Copyright 2012, Alex Smith, alex@alex-smith.me.uk. * Distributed under the terms of the MIT License. */ @@ -18,38 +19,182 @@ #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]; +typedef void interrupt_handler_function(iframe* frame); static const uint32 kInterruptHandlerTableSize = IDT_GATES_COUNT; interrupt_handler_function* gInterruptHandlerTable[kInterruptHandlerTableSize]; extern uint8 isr_array[kInterruptHandlerTableSize][16]; -static inline void -load_tss(int cpu) +constexpr bool +is_code_segment(DescriptorType type) { - uint16 segment = (TSS_SEGMENT(cpu) << 3) | DPL_KERNEL; - asm volatile("ltr %w0" : : "r" (segment)); + return type == DescriptorType::CodeExecuteOnly; +}; + + +constexpr +Descriptor::Descriptor() + : + fDescriptor { 0, 0 } +{ + static_assert(sizeof(Descriptor) == sizeof(uint64_t), + "Invalid Descriptor size."); } -static inline void -load_gdt() +Descriptor::Descriptor(uint32_t first, uint32_t second) + : + fDescriptor { first, second } { - struct { - uint16 limit; - void* address; - } _PACKED gdtDescriptor = { - GDT_SEGMENT_COUNT * sizeof(segment_descriptor) - 1, - sGDT +} + + +constexpr +Descriptor::Descriptor(DescriptorType type, bool kernelOnly) + : + fLimit0(-1), + fBase0(0), + fType(static_cast(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(limit); + fBase0 = base & 0xffffff; + fType = static_cast(DescriptorType::TSS); + fPresent = 1; + fLimit1 = (limit >> 16) & 0xf; + fBase1 = static_cast(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(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 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); - } + new(&sGDT) GlobalDescriptorTable; + sGDT.Load(); 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; + // 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_gdt(); - load_tss(cpu); }