aboutsummaryrefslogtreecommitdiffstats
path: root/idt.cpp
blob: 5380c9e62adb29dfc546179d2331186caf0d4a6d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
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;
}