#include "arch/cpu/interrupts.hpp" #include "kapi/cpu.hpp" #include "kapi/memory.hpp" #include "arch/cpu/legacy_pic.hpp" #include "arch/cpu/segment_selector.hpp" #include #include namespace arch::cpu { namespace { enum struct exception { divide_error, debug_exception, non_maskable_interrupt, breakpoint, overflow, bound_range_exceeded, invalid_opcode, device_not_available, double_fault, coprocessor_segment_overrun, invalid_tss, segment_not_present, stack_segment_fault, general_protection_fault, page_fault, x87_fpu_floating_point_error = 16, alignment_check, machine_check, simd_floating_point_error, virtualization_exception, control_protection_exception, hypervisor_injection_exception = 28, vmm_communication_exception, security_exception, }; constexpr auto number_of_exception_vectors = 32u; constexpr auto pic_master_irq_start = 0x20; constexpr auto pic_master_irq_end = pic_master_irq_start + 8; constexpr auto pic_slave_irq_start = pic_master_irq_end; constexpr auto to_exception_type(exception e) { switch (e) { case exception::divide_error: case exception::x87_fpu_floating_point_error: case exception::simd_floating_point_error: return kapi::cpu::exception::type::arithmetic_error; case exception::breakpoint: return kapi::cpu::exception::type::breakpoint; case exception::invalid_opcode: return kapi::cpu::exception::type::illegal_instruction; case exception::stack_segment_fault: return kapi::cpu::exception::type::memory_access_fault; case exception::general_protection_fault: return kapi::cpu::exception::type::privilege_violation; case exception::page_fault: return kapi::cpu::exception::type::page_fault; case exception::alignment_check: return kapi::cpu::exception::type::alignment_fault; default: return kapi::cpu::exception::type::unknown; } } constexpr auto has_error_code(exception e) { switch (e) { case exception::double_fault: case exception::invalid_tss: case exception::segment_not_present: case exception::stack_segment_fault: case exception::general_protection_fault: case exception::page_fault: case exception::alignment_check: case exception::control_protection_exception: case exception::security_exception: return true; default: return false; } } auto dispatch_exception(interrupt_frame * frame) -> bool { auto type = to_exception_type(static_cast(frame->interrupt.number)); auto fault_address = kapi::memory::linear_address{}; auto instruction_pointer = frame->cpu_saved.rip; switch (type) { case kapi::cpu::exception::type::page_fault: { asm volatile("mov %%cr2, %0" : "=r"(fault_address)); auto present = (frame->interrupt.error_code & 0x1) != 0; auto write = (frame->interrupt.error_code & 0x2) != 0; auto user = (frame->interrupt.error_code & 0x4) != 0; return kapi::cpu::dispatch({type, instruction_pointer, fault_address, present, write, user}); } default: return kapi::cpu::dispatch({type, instruction_pointer}); } } auto acknowledge_pic_interrupt(interrupt_frame * frame) -> void { if (frame->interrupt.number >= pic_slave_irq_start) { pic_slave_control_port::write(pic_end_of_interrupt); } pic_master_control_port::write(pic_end_of_interrupt); } } // namespace extern "C" { extern std::uintptr_t const isr_stub_table[256]; auto interrupt_dispatch(interrupt_frame * frame) -> void { auto [number, code] = frame->interrupt; if (number < number_of_exception_vectors && !dispatch_exception(frame)) { if (has_error_code(static_cast(number))) { kstd::println(kstd::print_sink::stderr, "[x86_64:CPU] Unhandled exception number {:#04x} received with code {:#04x}", number, code); } else { kstd::println(kstd::print_sink::stderr, "[x86_64:CPU] Unhandled exception number {:#04x} received", number); } } else if (number >= number_of_exception_vectors && kapi::cpu::dispatch_interrupt(number) == kapi::cpu::status::unhandled) { kstd::println(kstd::print_sink::stderr, "[x86_64:CPU] Unhandled interrupt {:#04x}", number); } acknowledge_pic_interrupt(frame); } } interrupt_descriptor_table::interrupt_descriptor_table() noexcept { for (auto i = 0uz; i < 256; ++i) { m_descriptors[i] = gate_descriptor{ .offset_low = static_cast(isr_stub_table[i] & 0xffff), // NOLINT(readability-magic-numbers) .m_code_segment = segment_selector{0, false, 1}, .interrupt_stack_table_selector = 0, .gate_type = (i < 32 && i != 2) ? gate_type::trap_gate : gate_type::interrupt_gate, .descriptor_privilege_level = 0, .present = true, .offset_middle = static_cast((isr_stub_table[i] >> 16) & 0xffff), // NOLINT(readability-magic-numbers) .offset_high = static_cast((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