aboutsummaryrefslogtreecommitdiff
path: root/arch/x86_64/src/cpu/interrupts.cpp
blob: 466389d6ca3ee4d054d775a09489abfa0197c08e (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
#include "arch/cpu/interrupts.hpp"

#include "kapi/cpu.hpp"

#include "arch/cpu/segment_selector.hpp"

#include <kstd/print>

#include <cstdint>

namespace arch::cpu
{

  namespace
  {
    constexpr auto isr_number_page_fault = 0x0e;
    constexpr auto isr_number_legacy_start = 0x20;
    constexpr auto isr_number_legacy_end = 0x29;
    constexpr auto isr_number_cascade_start = 0x2c;
    constexpr auto isr_number_cascade_end = 0x2f;

    auto handle_page_fault(interrupt_frame * frame) -> void
    {
      auto fault_address = std::uintptr_t{};
      asm volatile("mov %%cr2, %0" : "=r"(fault_address));

      auto const present = (frame->interrupt.error_code & 0x1) != 0;
      auto const write = (frame->interrupt.error_code & 0x2) != 0;
      auto const user = (frame->interrupt.error_code & 0x4) != 0;

      kstd::println(kstd::print_sink::stderr, "[x86_64:MMU] PAGE FAULT!");
      kstd::println(kstd::print_sink::stderr, "\tFault address: {:#018x}", fault_address);
      kstd::println(kstd::print_sink::stderr, "\tPresent: {}", present);
      kstd::println(kstd::print_sink::stderr, "\tWrite: {}", write);
      kstd::println(kstd::print_sink::stderr, "\tUser: {}", user);
      kstd::println(kstd::print_sink::stderr, "\tRIP: {:#018x}", frame->cpu_saved.rip);
      kstd::println(kstd::print_sink::stderr, "\tHalting the system now!");

      kapi::cpu::halt();
    }

    auto handle_legacy_interrupt(interrupt_frame * frame) -> void
    {
      kstd::println("[x86_64:SYS] Ignoring 8259 legacy interrupt {:#04x}", frame->interrupt.number);
    }
  }  // namespace

  extern "C"
  {
    extern std::uintptr_t const isr_stub_table[256];

    auto interrupt_dispatch(interrupt_frame * frame) -> void
    {
      if ((frame->interrupt.number >= isr_number_legacy_start && frame->interrupt.number <= isr_number_legacy_end) ||
          (frame->interrupt.number >= isr_number_cascade_start && frame->interrupt.number <= isr_number_cascade_end))
      {
        handle_legacy_interrupt(frame);
        return;
      }

      switch (frame->interrupt.number)
      {
        case isr_number_page_fault:
          handle_page_fault(frame);
          break;
        default:
          kstd::println(kstd::print_sink::stderr, "[x86_64:SYS] Unhandled interrupt {:#04x} received with code {:#04x}",
                        frame->interrupt.number, frame->interrupt.error_code);
          kapi::cpu::halt();
      }
    }
  }

  interrupt_descriptor_table::interrupt_descriptor_table() noexcept
  {
    for (auto i = 0uz; i < 256; ++i)
    {
      m_descriptors[i] = gate_descriptor{
        .offset_low = static_cast<std::uint16_t>(isr_stub_table[i] & 0xffff), // NOLINT(readability-magic-numbers)
        .m_code_segment = segment_selector{0, false, 1},
        .interrupt_stack_table_selector = 0,
        .gate_type = gate_type::interrupt_gate,
        .descriptor_privilege_level = 0,
        .present = true,
        .offset_middle =
            static_cast<std::uint16_t>((isr_stub_table[i] >> 16) & 0xffff), // NOLINT(readability-magic-numbers)
        .offset_high =
            static_cast<std::uint32_t>((isr_stub_table[i] >> 32) & 0xffff'ffff), // NOLINT(readability-magic-numbers)
      };
    }
  }

  auto interrupt_descriptor_table::load() const -> void
  {
    interrupt_descriptor_table_register{.limit = sizeof(m_descriptors) - 1, .base = m_descriptors.data()}.load();
  }

  auto interrupt_descriptor_table_register::load() const -> void
  {
    asm volatile("lidt %0" : : "m"(*this));
  }

  auto interrupt_descriptor_table_register::read() -> interrupt_descriptor_table_register
  {
    interrupt_descriptor_table_register idtr{};
    asm volatile("sidt %0" : : "m"(idtr));
    return idtr;
  }

  auto enable_interrupts() -> void
  {
    asm volatile("sti");
  }

  auto disable_interrupts() -> void
  {
    asm volatile("cli");
  }

}  // namespace arch::cpu