hrev47207 adds 7 changesets to branch 'master' old head: 5df1188e862b3653d810842ab52c00dc1ffc60e7 new head: 9db5b975f9ea2b942568eb57bbcce90a1c7420dc overview: http://cgit.haiku-os.org/haiku/log/?qt=range&q=9db5b97+%5E5df1188 ---------------------------------------------------------------------------- ea7e57c: libroot: enable c++11 in os/arch/x86_64 3446437: libroot/x86_64: minor improvements in TLS code * less inline asm * std::atomic<> instead of obsolete atomic_*() f31c19b: libroot/x86_64: relax asm constraints in find_thread() c1dc104: kernel/x86_64: enable c++11 in x86_64 specific code cd59bf4: 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. 2b6d4bc: kernel/x86: add space between literal and identifier Due to introduction of user-defined suffixes C++11 requires that there is a space between literal and identifier to avoid ambiguity. 9db5b97: kernel/x86_64: rework of IDT handling code Similarly to previous patch regarding GDT this is mostly a rewrite of IDT handling code from C to C++. Thanks to constexpr IDT is now entirely generated at compile-time. [ Pawel Dziepak <pdziepak@xxxxxxxxxxx> ] ---------------------------------------------------------------------------- 7 files changed, 313 insertions(+), 177 deletions(-) headers/private/kernel/arch/x86/64/descriptors.h | 80 ----- src/system/kernel/arch/x86/64/descriptors.cpp | 346 +++++++++++++++---- src/system/kernel/arch/x86/Jamfile | 2 + src/system/kernel/arch/x86/irq_routing_table.cpp | 2 +- src/system/libroot/os/arch/x86_64/Jamfile | 2 + src/system/libroot/os/arch/x86_64/thread.cpp | 4 +- src/system/libroot/os/arch/x86_64/tls.cpp | 54 ++- ############################################################################ Commit: ea7e57c9666997c11e3020d0fa71753e2097840b URL: http://cgit.haiku-os.org/haiku/commit/?id=ea7e57c Author: Pawel Dziepak <pdziepak@xxxxxxxxxxx> Date: Mon May 5 15:07:25 2014 UTC libroot: enable c++11 in os/arch/x86_64 ---------------------------------------------------------------------------- diff --git a/src/system/libroot/os/arch/x86_64/Jamfile b/src/system/libroot/os/arch/x86_64/Jamfile index 2cda1c6..26b8c27 100644 --- a/src/system/libroot/os/arch/x86_64/Jamfile +++ b/src/system/libroot/os/arch/x86_64/Jamfile @@ -1,5 +1,7 @@ SubDir HAIKU_TOP src system libroot os arch x86_64 ; +SubDirC++Flags -std=gnu++11 ; + local architectureObject ; for architectureObject in [ MultiArchSubDirSetup x86_64 ] { on $(architectureObject) { ############################################################################ Commit: 344643740a95c2e3bcdc2286c3310527bcbb5844 URL: http://cgit.haiku-os.org/haiku/commit/?id=3446437 Author: Pawel Dziepak <pdziepak@xxxxxxxxxxx> Date: Mon May 5 15:07:55 2014 UTC libroot/x86_64: minor improvements in TLS code * less inline asm * std::atomic<> instead of obsolete atomic_*() ---------------------------------------------------------------------------- diff --git a/src/system/libroot/os/arch/x86_64/tls.cpp b/src/system/libroot/os/arch/x86_64/tls.cpp index 15925c2..48be907 100644 --- a/src/system/libroot/os/arch/x86_64/tls.cpp +++ b/src/system/libroot/os/arch/x86_64/tls.cpp @@ -1,4 +1,5 @@ /* + * Copyright 2014, Paweł Dziepak, pdziepak@xxxxxxxxxxx. * Copyright 2012, Alex Smith, alex@xxxxxxxxxxxxxxxx. * Distributed under the terms of the MIT License. */ @@ -8,6 +9,8 @@ # define _NO_INLINE_ASM 1 #endif +#include <atomic> + #include <runtime_loader/runtime_loader.h> #include <support/TLS.h> @@ -22,54 +25,49 @@ struct tls_index { }; -static int32 gNextSlot = TLS_FIRST_FREE_SLOT; +static std::atomic<int> gNextSlot(TLS_FIRST_FREE_SLOT); + + +static inline void** +get_tls() +{ + void** tls; + __asm__ __volatile__ ("movq %%fs:0, %0" : "=r" (tls)); + return tls; +} int32 -tls_allocate(void) +tls_allocate() { - int32 next = atomic_add(&gNextSlot, 1); - if (next >= TLS_MAX_KEYS) - return B_NO_MEMORY; + if (gNextSlot < TLS_MAX_KEYS) { + auto next = gNextSlot++; + if (next < TLS_MAX_KEYS) + return next; + } - return next; + return B_NO_MEMORY; } void* -tls_get(int32 _index) +tls_get(int32 index) { - int64 index = _index; - void* ret; - - __asm__ __volatile__ ( - "movq %%fs:(, %1, 8), %0" - : "=r" (ret) : "r" (index)); - return ret; + return get_tls()[index]; } void** -tls_address(int32 _index) +tls_address(int32 index) { - int64 index = _index; - void** ret; - - __asm__ __volatile__ ( - "movq %%fs:0, %0\n\t" - "leaq (%0, %1, 8), %0\n\t" - : "=&r" (ret) : "r" (index)); - return ret; + return get_tls() + index; } void -tls_set(int32 _index, void* value) +tls_set(int32 index, void* value) { - int64 index = _index; - __asm__ __volatile__ ( - "movq %1, %%fs:(, %0, 8)" - : : "r" (index), "r" (value)); + get_tls()[index] = value; } ############################################################################ Commit: f31c19bb9e6abdd85acde7a900894c7c31e14a01 URL: http://cgit.haiku-os.org/haiku/commit/?id=f31c19b Author: Pawel Dziepak <pdziepak@xxxxxxxxxxx> Date: Mon May 5 15:09:34 2014 UTC libroot/x86_64: relax asm constraints in find_thread() ---------------------------------------------------------------------------- diff --git a/src/system/libroot/os/arch/x86_64/thread.cpp b/src/system/libroot/os/arch/x86_64/thread.cpp index 10614ba..e4da9a8 100644 --- a/src/system/libroot/os/arch/x86_64/thread.cpp +++ b/src/system/libroot/os/arch/x86_64/thread.cpp @@ -13,7 +13,9 @@ find_thread(const char* name) { if (!name) { thread_id thread; - __asm__ __volatile__ ("movq %%fs:8, %%rax" : "=a" (thread)); + static_assert(sizeof(thread_id) <= sizeof(uint32_t), + "thread_id is larger than uint32_t"); + __asm__ __volatile__ ("movl %%fs:8, %0" : "=r" (thread)); return thread; } ############################################################################ Commit: c1dc10496054f49cf6af475cce53bf076bbf4d19 URL: http://cgit.haiku-os.org/haiku/commit/?id=c1dc104 Author: Pawel Dziepak <pdziepak@xxxxxxxxxxx> Date: Mon May 5 19:16:07 2014 UTC kernel/x86_64: enable c++11 in x86_64 specific code ---------------------------------------------------------------------------- diff --git a/src/system/kernel/arch/x86/Jamfile b/src/system/kernel/arch/x86/Jamfile index 3941e18..ee6d93b 100644 --- a/src/system/kernel/arch/x86/Jamfile +++ b/src/system/kernel/arch/x86/Jamfile @@ -17,6 +17,8 @@ SEARCH_SOURCE += [ FDirName $(SUBDIR) timers ] ; local archSpecificSources ; if $(TARGET_ARCH) = x86_64 { + SubDirC++Flags -std=gnu++11 ; + SEARCH_SOURCE += [ FDirName $(SUBDIR) 64 ] ; SEARCH_SOURCE += [ FDirName $(SUBDIR) paging 64bit ] ; ############################################################################ Commit: cd59bf434970a082f1bfbb79fc83e4c93b00bc31 URL: http://cgit.haiku-os.org/haiku/commit/?id=cd59bf4 Author: Pawel Dziepak <pdziepak@xxxxxxxxxxx> Date: Mon May 5 19:16:59 2014 UTC 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. ---------------------------------------------------------------------------- diff --git a/headers/private/kernel/arch/x86/64/descriptors.h b/headers/private/kernel/arch/x86/64/descriptors.h index 78d02c7..66512b3 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; @@ -128,29 +104,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 b328eb2..37e0ef2 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@xxxxxxxxxxx. * Copyright 2012, Alex Smith, alex@xxxxxxxxxxxxxxxx. * 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 GlobalDescriptorTable sGDT; -static segment_descriptor sGDT[GDT_SEGMENT_COUNT]; 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<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 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); } ############################################################################ Commit: 2b6d4bc6578b419189a5051ed54f81dd9e70def1 URL: http://cgit.haiku-os.org/haiku/commit/?id=2b6d4bc Author: Pawel Dziepak <pdziepak@xxxxxxxxxxx> Date: Tue May 6 02:04:01 2014 UTC kernel/x86: add space between literal and identifier Due to introduction of user-defined suffixes C++11 requires that there is a space between literal and identifier to avoid ambiguity. ---------------------------------------------------------------------------- diff --git a/src/system/kernel/arch/x86/irq_routing_table.cpp b/src/system/kernel/arch/x86/irq_routing_table.cpp index 68cfd1b..9045bba 100644 --- a/src/system/kernel/arch/x86/irq_routing_table.cpp +++ b/src/system/kernel/arch/x86/irq_routing_table.cpp @@ -18,7 +18,7 @@ //#define TRACE_PRT #ifdef TRACE_PRT -# define TRACE(x...) dprintf("IRQRoutingTable: "x) +# define TRACE(x...) dprintf("IRQRoutingTable: " x) #else # define TRACE(x...) #endif ############################################################################ Revision: hrev47207 Commit: 9db5b975f9ea2b942568eb57bbcce90a1c7420dc URL: http://cgit.haiku-os.org/haiku/commit/?id=9db5b97 Author: Pawel Dziepak <pdziepak@xxxxxxxxxxx> Date: Tue May 6 12:39:34 2014 UTC kernel/x86_64: rework of IDT handling code Similarly to previous patch regarding GDT this is mostly a rewrite of IDT handling code from C to C++. Thanks to constexpr IDT is now entirely generated at compile-time. ---------------------------------------------------------------------------- diff --git a/headers/private/kernel/arch/x86/64/descriptors.h b/headers/private/kernel/arch/x86/64/descriptors.h index 66512b3..c5a783a 100644 --- a/headers/private/kernel/arch/x86/64/descriptors.h +++ b/headers/private/kernel/arch/x86/64/descriptors.h @@ -39,21 +39,6 @@ struct segment_descriptor { uint32 base1 : 8; } _PACKED; -// Structure of an interrupt descriptor. -struct interrupt_descriptor { - uint32 base0 : 16; - uint32 sel : 16; - uint32 ist : 3; - uint32 unused1 : 5; - uint32 type : 4; - uint32 unused2 : 1; - uint32 dpl : 2; - uint32 present : 1; - uint32 base1 : 16; - uint32 base2 : 32; - uint32 reserved : 32; -} _PACKED; - struct tss { uint32 _reserved1; uint64 sp0; @@ -103,24 +88,6 @@ set_segment_descriptor(segment_descriptor* desc, uint8 type, uint8 dpl) } -static inline void -set_interrupt_descriptor(interrupt_descriptor* desc, uint64 addr, uint32 type, - uint16 seg, uint32 dpl, uint32 ist) -{ - desc->base0 = addr & 0xffff; - desc->base1 = (addr >> 16) & 0xffff; - desc->base2 = (addr >> 32) & 0xffffffff; - desc->sel = seg; - desc->ist = ist; - desc->type = type; - desc->dpl = dpl; - desc->present = 1; - desc->unused1 = 0; - desc->unused2 = 0; - desc->reserved = 0; -} - - #endif /* _ASSEMBLER */ #endif /* _KERNEL_ARCH_X86_64_DESCRIPTORS_H */ diff --git a/src/system/kernel/arch/x86/64/descriptors.cpp b/src/system/kernel/arch/x86/64/descriptors.cpp index 37e0ef2..993dfdf 100644 --- a/src/system/kernel/arch/x86/64/descriptors.cpp +++ b/src/system/kernel/arch/x86/64/descriptors.cpp @@ -16,8 +16,20 @@ #include <arch/user_debugger.h> -#define IDT_GATES_COUNT 256 +template<typename T, T (*Function)(unsigned), unsigned N, unsigned ...Index> +struct GenerateTable : GenerateTable<T, Function, N - 1, N - 1, Index...> { +}; + +template<typename T, T (*Function)(unsigned), unsigned ...Index> +struct GenerateTable<T, Function, 0, Index...> { + GenerateTable() + : + fTable { Function(Index)... } + { + } + T fTable[sizeof...(Index)]; +}; enum class DescriptorType : unsigned { DataWritable = 0x2, @@ -78,19 +90,61 @@ private: static constexpr unsigned kDescriptorCount = kFirstTSS + SMP_MAX_CPUS * 2; - alignas(sizeof(Descriptor)) Descriptor fTable[kDescriptorCount]; + alignas(uint64_t) Descriptor fTable[kDescriptorCount]; }; +enum class InterruptDescriptorType : unsigned { + Interrupt = 14, + Trap, +}; -static GlobalDescriptorTable sGDT; +class [[gnu::packed]] InterruptDescriptor { +public: + constexpr InterruptDescriptor(uintptr_t isr, + unsigned ist, bool kernelOnly); + constexpr InterruptDescriptor(uintptr_t isr); -static interrupt_descriptor sIDT[IDT_GATES_COUNT]; + static constexpr InterruptDescriptor Generate(unsigned index); -typedef void interrupt_handler_function(iframe* frame); -static const uint32 kInterruptHandlerTableSize = IDT_GATES_COUNT; -interrupt_handler_function* gInterruptHandlerTable[kInterruptHandlerTableSize]; +private: + uint16_t fBase0; + uint16_t fSelector; + unsigned fIST :3; + unsigned fReserved0 :5; + unsigned fType :4; + unsigned fReserved1 :1; + unsigned fDPL :2; + unsigned fPresent :1; + uint16_t fBase1; + uint32_t fBase2; + uint32_t fReserved2; +}; -extern uint8 isr_array[kInterruptHandlerTableSize][16]; +class InterruptDescriptorTable { +public: + inline void Load() const; + + static constexpr unsigned kDescriptorCount = 256; + +private: + typedef GenerateTable<InterruptDescriptor, InterruptDescriptor::Generate, + kDescriptorCount> TableType; + alignas(uint64_t) TableType fTable; +}; + +class InterruptServiceRoutine { + alignas(16) uint8_t fDummy[16]; +}; + +extern const InterruptServiceRoutine + isr_array[InterruptDescriptorTable::kDescriptorCount]; + +static GlobalDescriptorTable sGDT; +static InterruptDescriptorTable sIDT; + +typedef void interrupt_handler_function(iframe* frame); +interrupt_handler_function* + gInterruptHandlerTable[InterruptDescriptorTable::kDescriptorCount]; constexpr bool @@ -198,18 +252,58 @@ GlobalDescriptorTable::SetTSS(unsigned cpu, const TSSDescriptor& tss) } -static inline void -load_idt() +constexpr +InterruptDescriptor::InterruptDescriptor(uintptr_t isr, unsigned ist, + bool kernelOnly) + : + fBase0(isr), + fSelector(KERNEL_CODE_SELECTOR), + fIST(ist), + fReserved0(0), + fType(static_cast<unsigned>(InterruptDescriptorType::Interrupt)), + fReserved1(0), + fDPL(kernelOnly ? 0 : 3), + fPresent(1), + fBase1(isr >> 16), + fBase2(isr >> 32), + fReserved2(0) +{ + static_assert(sizeof(InterruptDescriptor) == sizeof(uint64_t) * 2, + "Invalid InterruptDescriptor size."); +} + + +constexpr +InterruptDescriptor::InterruptDescriptor(uintptr_t isr) + : + InterruptDescriptor(isr, 0, true) +{ +} + + +void +InterruptDescriptorTable::Load() const { - struct { - uint16 limit; - void* address; - } _PACKED idtDescriptor = { - IDT_GATES_COUNT * sizeof(interrupt_descriptor) - 1, - sIDT + struct [[gnu::packed]] { + uint16_t fLimit; + const void* fAddress; + } gdtDescriptor = { + sizeof(fTable) - 1, + static_cast<const void*>(fTable.fTable), }; - asm volatile("lidt %0" : : "m" (idtDescriptor)); + asm volatile("lidt %0" : : "m" (gdtDescriptor)); +} + + +constexpr InterruptDescriptor +InterruptDescriptor::Generate(unsigned index) +{ + return index == 3 + ? InterruptDescriptor(uintptr_t(isr_array + index), 0, false) + : (index == 8 + ? InterruptDescriptor(uintptr_t(isr_array + index), 1, true) + : InterruptDescriptor(uintptr_t(isr_array + index))); } @@ -258,38 +352,22 @@ x86_descriptors_preboot_init_percpu(kernel_args* args, int cpu) TSSDescriptor(uintptr_t(&gCPU[cpu].arch.tss), sizeof(struct tss))); TSSDescriptor::LoadTSS(tssIndex); - load_idt(); + new(&sIDT) InterruptDescriptorTable; + sIDT.Load(); } void x86_descriptors_init(kernel_args* args) { - // 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++) { - // x86_64 removes task gates, therefore we cannot use a separate TSS - // for the double fault exception. However, instead it adds a new stack - // switching mechanism, the IST. The IST is a table of stack addresses - // in the TSS. If the IST field of an interrupt descriptor is non-zero, - // the CPU will switch to the stack specified by that IST entry when - // handling that interrupt. So, we use IST entry 1 to store the double - // fault stack address (set up in x86_descriptors_init_post_vm()). - uint32 ist = (i == 8) ? 1 : 0; - - // Breakpoint exception can be raised from userland. - uint32 dpl = (i == 3) ? DPL_USER : DPL_KERNEL; - - set_interrupt_descriptor(&sIDT[i], (addr_t)&isr_array[i], - GATE_INTERRUPT, KERNEL_CODE_SELECTOR, dpl, ist); - } - // Initialize the interrupt handler table. interrupt_handler_function** table = gInterruptHandlerTable; for (uint32 i = 0; i < ARCH_INTERRUPT_BASE; i++) table[i] = x86_invalid_exception; - for (uint32 i = ARCH_INTERRUPT_BASE; i < kInterruptHandlerTableSize; i++) + for (uint32 i = ARCH_INTERRUPT_BASE; + i < InterruptDescriptorTable::kDescriptorCount; i++) { table[i] = x86_hardware_interrupt; + } table[0] = x86_unexpected_exception; // Divide Error Exception (#DE) table[1] = x86_handle_debug_exception; // Debug Exception (#DB)