aboutsummaryrefslogtreecommitdiff
path: root/arch/x86_64/src
diff options
context:
space:
mode:
authorFelix Morgner <felix.morgner@ost.ch>2026-03-20 21:48:30 +0100
committerFelix Morgner <felix.morgner@ost.ch>2026-03-20 21:48:30 +0100
commitdd2dc3ef9a5318a0f7c7c35be59759ab08adc3dc (patch)
tree8b52da9f3d00165055262c852d3aabd545e4d70a /arch/x86_64/src
parent96f1511dbe2e80223732bcbef8068c3d5a330cee (diff)
downloadteachos-dd2dc3ef9a5318a0f7c7c35be59759ab08adc3dc.tar.xz
teachos-dd2dc3ef9a5318a0f7c7c35be59759ab08adc3dc.zip
x86_64/cpu: implement basic interrupt handling
Diffstat (limited to 'arch/x86_64/src')
-rw-r--r--arch/x86_64/src/cpu/initialization.cpp51
-rw-r--r--arch/x86_64/src/cpu/interrupt_stubs.S112
-rw-r--r--arch/x86_64/src/cpu/interrupts.cpp94
3 files changed, 237 insertions, 20 deletions
diff --git a/arch/x86_64/src/cpu/initialization.cpp b/arch/x86_64/src/cpu/initialization.cpp
index aae4f1f..214687c 100644
--- a/arch/x86_64/src/cpu/initialization.cpp
+++ b/arch/x86_64/src/cpu/initialization.cpp
@@ -1,6 +1,7 @@
#include "arch/cpu/initialization.hpp"
#include "arch/cpu/global_descriptor_table.hpp"
+#include "arch/cpu/interrupts.hpp"
#include "arch/cpu/segment_descriptor.hpp"
#include "arch/cpu/task_state_segment.hpp"
@@ -83,30 +84,38 @@ namespace arch::cpu
.granularity = segment_granularity::page,
.base_high = 0,
};
+
+ constexpr auto make_tss_descriptor(task_state_segment const * tss_ptr) -> system_segment_descriptor
+ {
+ auto const address = std::bit_cast<std::uintptr_t>(tss_ptr);
+ auto const limit = sizeof(task_state_segment) - 1;
+
+ return system_segment_descriptor{
+ {
+ .limit_low = limit & 0xffff, // NOLINT(readability-magic-numbers)
+ .base_low = address & 0xffffff, // NOLINT(readability-magic-numbers)
+ .accessed = false,
+ .read_write = false,
+ .direction_or_conforming = false,
+ .executable = false,
+ .type = segment_type::system,
+ .privilege_level = 0,
+ .present = true,
+ .limit_high = (limit >> 16) & 0xf, // NOLINT(readability-magic-numbers)
+ .long_mode = false,
+ .protected_mode = false,
+ .granularity = segment_granularity::byte,
+ .base_high = (address >> 24) & 0xff, // NOLINT(readability-magic-numbers)
+ },
+ (address >> 32) & 0xffff'ffff, // NOLINT(readability-magic-numbers)
+ };
+ }
} // namespace
auto initialize_descriptors() -> void
{
auto static tss = task_state_segment{};
- auto static tss_descriptor = system_segment_descriptor{
- {
- .limit_low = (sizeof(tss) - 1) & 0xffff, // NOLINT(readability-magic-numbers)
- .base_low = std::bit_cast<std::uintptr_t>(&tss) & 0xffffff, // NOLINT(readability-magic-numbers)
- .accessed = false,
- .read_write = false,
- .direction_or_conforming = false,
- .executable = false,
- .type = segment_type::system,
- .privilege_level = 0,
- .present = true,
- .limit_high = ((sizeof(tss) - 1) >> 16) & 0xf, // NOLINT(readability-magic-numbers)
- .long_mode = false,
- .protected_mode = false,
- .granularity = segment_granularity::byte,
- .base_high = (std::bit_cast<std::uintptr_t>(&tss) >> 24) & 0xff, // NOLINT(readability-magic-numbers)
- },
- (std::bit_cast<std::uintptr_t>(&tss) >> 32) & 0xffff'ffff, // NOLINT(readability-magic-numbers)
- };
+ auto static tss_descriptor = make_tss_descriptor(&tss);
auto static gdt = global_descriptor_table{
gdt_null_descriptor, gdt_kernel_code_descriptor, gdt_kernel_data_descriptor,
@@ -116,7 +125,9 @@ namespace arch::cpu
kstd::println("[x86_64:SYS] Reloading Global Descriptor Table.");
gdt.load(1, 2);
- kstd::println("[x86_64:SYS] TODO: initialize Interrupt Descriptor Table.");
+ kstd::println("[x86_64:SYS] Initializing Interrupt Descriptor Table.");
+ auto static idt = interrupt_descriptor_table{};
+ idt.load();
}
} // namespace arch::cpu
diff --git a/arch/x86_64/src/cpu/interrupt_stubs.S b/arch/x86_64/src/cpu/interrupt_stubs.S
new file mode 100644
index 0000000..e59bdd2
--- /dev/null
+++ b/arch/x86_64/src/cpu/interrupt_stubs.S
@@ -0,0 +1,112 @@
+.altmacro
+
+.macro ISR_WITHOUT_ERROR_CODE vector
+ .global isr\vector
+ isr\vector:
+ pushq $0
+ pushq $\vector
+ jmp common_interrupt_handler
+.endm
+
+.macro ISR_WITH_ERROR_CODE vector
+ .global isr\vector
+ isr\vector:
+ pushq $\vector
+ jmp common_interrupt_handler
+.endm
+
+.macro ISR_TABLE_ENTRY vector
+ .quad isr\vector
+.endm
+
+.section .rodata
+.global isr_stub_table
+.align 16
+
+isr_stub_table:
+.set i, 0
+.rept 256
+ ISR_TABLE_ENTRY %i
+ .set i, i + 1
+.endr
+
+
+.section .text
+
+common_interrupt_handler:
+ push %rax
+ push %rbx
+ push %rcx
+ push %rdx
+ push %rbp
+ push %rsi
+ push %rdi
+ push %r8
+ push %r9
+ push %r10
+ push %r11
+ push %r12
+ push %r13
+ push %r14
+ push %r15
+
+ mov %rsp, %rdi
+ call interrupt_dispatch
+
+ pop %r15
+ pop %r14
+ pop %r13
+ pop %r12
+ pop %r11
+ pop %r10
+ pop %r9
+ pop %r8
+ pop %rdi
+ pop %rsi
+ pop %rbp
+ pop %rdx
+ pop %rcx
+ pop %rbx
+ pop %rax
+
+ add $16, %rsp
+ iretq
+
+ISR_WITHOUT_ERROR_CODE 0
+ISR_WITHOUT_ERROR_CODE 1
+ISR_WITHOUT_ERROR_CODE 2
+ISR_WITHOUT_ERROR_CODE 3
+ISR_WITHOUT_ERROR_CODE 4
+ISR_WITHOUT_ERROR_CODE 5
+ISR_WITHOUT_ERROR_CODE 6
+ISR_WITHOUT_ERROR_CODE 7
+ISR_WITH_ERROR_CODE 8
+ISR_WITHOUT_ERROR_CODE 9
+ISR_WITH_ERROR_CODE 10
+ISR_WITH_ERROR_CODE 11
+ISR_WITH_ERROR_CODE 12
+ISR_WITH_ERROR_CODE 13
+ISR_WITH_ERROR_CODE 14
+ISR_WITHOUT_ERROR_CODE 15
+ISR_WITHOUT_ERROR_CODE 16
+ISR_WITH_ERROR_CODE 17
+ISR_WITHOUT_ERROR_CODE 18
+ISR_WITHOUT_ERROR_CODE 19
+ISR_WITHOUT_ERROR_CODE 20
+ISR_WITH_ERROR_CODE 21
+ISR_WITHOUT_ERROR_CODE 22
+ISR_WITHOUT_ERROR_CODE 23
+ISR_WITHOUT_ERROR_CODE 24
+ISR_WITHOUT_ERROR_CODE 25
+ISR_WITHOUT_ERROR_CODE 26
+ISR_WITHOUT_ERROR_CODE 27
+ISR_WITHOUT_ERROR_CODE 28
+ISR_WITH_ERROR_CODE 29
+ISR_WITH_ERROR_CODE 30
+ISR_WITHOUT_ERROR_CODE 31
+
+.set i, 32
+.rept 256 - 32
+ ISR_WITHOUT_ERROR_CODE %i
+ .set i, i + 1
+.endr
diff --git a/arch/x86_64/src/cpu/interrupts.cpp b/arch/x86_64/src/cpu/interrupts.cpp
new file mode 100644
index 0000000..08469c0
--- /dev/null
+++ b/arch/x86_64/src/cpu/interrupts.cpp
@@ -0,0 +1,94 @@
+#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_code_PF = 14;
+
+ 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: 0x{:x}", 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();
+ }
+ } // namespace
+
+ extern "C"
+ {
+ extern std::uintptr_t const isr_stub_table[256];
+
+ auto interrupt_dispatch(interrupt_frame * frame) -> void
+ {
+ switch (frame->interrupt.number)
+ {
+ case isr_code_PF:
+ handle_page_fault(frame);
+ break;
+ default:
+ kstd::println(kstd::print_sink::stderr, "[x86_64:SYS] Unhandled interrupt {} received with code {}",
+ 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;
+ }
+
+} // namespace arch::cpu \ No newline at end of file