diff options
Diffstat (limited to 'src/idt.cpp')
-rw-r--r-- | src/idt.cpp | 126 |
1 files changed, 126 insertions, 0 deletions
diff --git a/src/idt.cpp b/src/idt.cpp new file mode 100644 index 0000000..5380c9e --- /dev/null +++ b/src/idt.cpp @@ -0,0 +1,126 @@ +#include "idt.hpp" +#include "portio.hpp" +#include "textoutput.hpp" + +#include <array> +#include <cstdint> +#include <utility> + +extern TextOutput& term; + +static constexpr unsigned InterruptCount = 49; + +static constexpr std::uint8_t TaskGate = 0x5; +static constexpr std::uint8_t IntrGate16 = 0x6; +static constexpr std::uint8_t TrapGate16 = 0x7; +static constexpr std::uint8_t IntrGate32 = 0xE; +static constexpr std::uint8_t TrapGate32 = 0xF; + +struct idt_entry_bits { + std::uint32_t offset_low : 16; + std::uint32_t segment_selector : 16; + std::uint32_t rsvd : 8 = 0; + std::uint32_t gate_type : 4; + std::uint32_t rsvd2 : 1 = 0; + std::uint32_t dpl : 2; + std::uint32_t present : 1; + std::uint32_t offset_high : 16; +} __attribute__((packed)); + +static std::array<Callback, InterruptCount> callbacks; + +extern "C" +void interruptGeneralHandler(Registers regs) +{ + const auto& inum = regs.inum; + + if (inum >= 32) { + if (inum >= 40) + outb(0xA0, 0x20); + + outb(0x20, 0x20); + } + + if (inum < callbacks.size()) { + if (auto cb = callbacks[inum]; cb) { + asm volatile("cli"); + cb(regs); + asm volatile("sti"); + } + } +} + +template<std::size_t N> +struct StubEntry +{ + static constexpr bool HasError = N == 8 || (N >= 10 && N <= 14) || N == 17 || N == 30; + + __attribute__((naked)) + static void stub() { + if constexpr (!HasError) + asm volatile("push $0x0"); + + asm volatile(R"( + pusha + mov %%ds, %%eax + push %%eax + mov $0x10, %%ax + mov %%ax, %%ds + mov %%ax, %%es + mov %%ax, %%fs + mov %%ax, %%gs + push %0 + cld + call interruptGeneralHandler + pop %%eax + pop %%eax + mov %%ax, %%ds + mov %%ax, %%es + mov %%ax, %%fs + mov %%ax, %%gs + popa + add $0x4, %%esp + iret + )" :: "i"(N)); + } + + static constexpr std::uint32_t segment(std::uint16_t gdt_idx, bool useLdt, std::uint16_t rpl) { + return gdt_idx | (useLdt ? 0x4 : 0x0) | (rpl & 0x3); + } + + idt_entry_bits entry = { + .offset_low = (uint32_t)stub & 0xFFFF, + .segment_selector = segment(0x8, false, 0), + .gate_type = IntrGate32, + .dpl = 0, + .present = 1, + .offset_high = (uint32_t)stub >> 16 + }; + + operator idt_entry_bits() const noexcept { + return entry; + } +}; + +static auto idt = + []<std::size_t... ints>(std::index_sequence<ints...>) { + return std::array<idt_entry_bits, 256> { StubEntry<ints>()... }; + }(std::make_index_sequence<InterruptCount>{}); + +void idt_initialize() +{ + idt[0x28].dpl = 3; + + auto idtr = reinterpret_cast<std::uint64_t>(idt.data()); + idtr <<= 16; + idtr |= idt.size() * sizeof(idt[0]); + + asm volatile("lidt %0" :: "m"(idtr)); +} + +void idt_register_callback(std::size_t num, Callback cb) +{ + if (num < callbacks.size()) + callbacks[num] = cb; +} + |