diff options
| author | Felix Morgner <felix.morgner@ost.ch> | 2026-03-20 21:48:30 +0100 |
|---|---|---|
| committer | Felix Morgner <felix.morgner@ost.ch> | 2026-03-20 21:48:30 +0100 |
| commit | dd2dc3ef9a5318a0f7c7c35be59759ab08adc3dc (patch) | |
| tree | 8b52da9f3d00165055262c852d3aabd545e4d70a /arch/x86_64/src | |
| parent | 96f1511dbe2e80223732bcbef8068c3d5a330cee (diff) | |
| download | teachos-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.cpp | 51 | ||||
| -rw-r--r-- | arch/x86_64/src/cpu/interrupt_stubs.S | 112 | ||||
| -rw-r--r-- | arch/x86_64/src/cpu/interrupts.cpp | 94 |
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 |
