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.
This commit is contained in:
parent
2b6d4bc657
commit
9db5b975f9
@ -39,21 +39,6 @@ struct segment_descriptor {
|
|||||||
uint32 base1 : 8;
|
uint32 base1 : 8;
|
||||||
} _PACKED;
|
} _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 {
|
struct tss {
|
||||||
uint32 _reserved1;
|
uint32 _reserved1;
|
||||||
uint64 sp0;
|
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 /* _ASSEMBLER */
|
||||||
|
|
||||||
#endif /* _KERNEL_ARCH_X86_64_DESCRIPTORS_H */
|
#endif /* _KERNEL_ARCH_X86_64_DESCRIPTORS_H */
|
||||||
|
@ -16,8 +16,20 @@
|
|||||||
#include <arch/user_debugger.h>
|
#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 {
|
enum class DescriptorType : unsigned {
|
||||||
DataWritable = 0x2,
|
DataWritable = 0x2,
|
||||||
@ -78,19 +90,61 @@ private:
|
|||||||
static constexpr unsigned kDescriptorCount
|
static constexpr unsigned kDescriptorCount
|
||||||
= kFirstTSS + SMP_MAX_CPUS * 2;
|
= kFirstTSS + SMP_MAX_CPUS * 2;
|
||||||
|
|
||||||
alignas(sizeof(Descriptor)) Descriptor fTable[kDescriptorCount];
|
alignas(uint64_t) Descriptor fTable[kDescriptorCount];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class InterruptDescriptorType : unsigned {
|
||||||
|
Interrupt = 14,
|
||||||
|
Trap,
|
||||||
|
};
|
||||||
|
|
||||||
|
class [[gnu::packed]] InterruptDescriptor {
|
||||||
|
public:
|
||||||
|
constexpr InterruptDescriptor(uintptr_t isr,
|
||||||
|
unsigned ist, bool kernelOnly);
|
||||||
|
constexpr InterruptDescriptor(uintptr_t isr);
|
||||||
|
|
||||||
|
static constexpr InterruptDescriptor Generate(unsigned index);
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
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 GlobalDescriptorTable sGDT;
|
||||||
|
static InterruptDescriptorTable sIDT;
|
||||||
static interrupt_descriptor sIDT[IDT_GATES_COUNT];
|
|
||||||
|
|
||||||
typedef void interrupt_handler_function(iframe* frame);
|
typedef void interrupt_handler_function(iframe* frame);
|
||||||
static const uint32 kInterruptHandlerTableSize = IDT_GATES_COUNT;
|
interrupt_handler_function*
|
||||||
interrupt_handler_function* gInterruptHandlerTable[kInterruptHandlerTableSize];
|
gInterruptHandlerTable[InterruptDescriptorTable::kDescriptorCount];
|
||||||
|
|
||||||
extern uint8 isr_array[kInterruptHandlerTableSize][16];
|
|
||||||
|
|
||||||
|
|
||||||
constexpr bool
|
constexpr bool
|
||||||
@ -198,18 +252,58 @@ GlobalDescriptorTable::SetTSS(unsigned cpu, const TSSDescriptor& tss)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static inline void
|
constexpr
|
||||||
load_idt()
|
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)
|
||||||
{
|
{
|
||||||
struct {
|
static_assert(sizeof(InterruptDescriptor) == sizeof(uint64_t) * 2,
|
||||||
uint16 limit;
|
"Invalid InterruptDescriptor size.");
|
||||||
void* address;
|
}
|
||||||
} _PACKED idtDescriptor = {
|
|
||||||
IDT_GATES_COUNT * sizeof(interrupt_descriptor) - 1,
|
|
||||||
sIDT
|
constexpr
|
||||||
|
InterruptDescriptor::InterruptDescriptor(uintptr_t isr)
|
||||||
|
:
|
||||||
|
InterruptDescriptor(isr, 0, true)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
InterruptDescriptorTable::Load() const
|
||||||
|
{
|
||||||
|
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(uintptr_t(&gCPU[cpu].arch.tss), sizeof(struct tss)));
|
||||||
TSSDescriptor::LoadTSS(tssIndex);
|
TSSDescriptor::LoadTSS(tssIndex);
|
||||||
|
|
||||||
load_idt();
|
new(&sIDT) InterruptDescriptorTable;
|
||||||
|
sIDT.Load();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
x86_descriptors_init(kernel_args* args)
|
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.
|
// Initialize the interrupt handler table.
|
||||||
interrupt_handler_function** table = gInterruptHandlerTable;
|
interrupt_handler_function** table = gInterruptHandlerTable;
|
||||||
for (uint32 i = 0; i < ARCH_INTERRUPT_BASE; i++)
|
for (uint32 i = 0; i < ARCH_INTERRUPT_BASE; i++)
|
||||||
table[i] = x86_invalid_exception;
|
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[i] = x86_hardware_interrupt;
|
||||||
|
}
|
||||||
|
|
||||||
table[0] = x86_unexpected_exception; // Divide Error Exception (#DE)
|
table[0] = x86_unexpected_exception; // Divide Error Exception (#DE)
|
||||||
table[1] = x86_handle_debug_exception; // Debug Exception (#DB)
|
table[1] = x86_handle_debug_exception; // Debug Exception (#DB)
|
||||||
|
Loading…
Reference in New Issue
Block a user