#include "arch/devices/local_apic.hpp" #include "kapi/devices.hpp" #include "kapi/memory.hpp" #include #include #include #include namespace arch::devices { namespace { constexpr auto lapic_enable_bit = 0x100u; constexpr auto spurious_interrupt_vector = 0xFFu; constexpr auto offset_of_version = 0u; constexpr auto offset_of_max_lvt_entry = 16u; constexpr auto offset_of_eoi_suppression = 24u; } // namespace enum struct local_apic::registers : std::ptrdiff_t { id = 0x020, version = 0x030, task_priority = 0x080, arbitration_priority = 0x090, processor_priority = 0x0a0, eoi = 0x0b0, remote_read = 0x0c0, logical_destination = 0x0d0, destination_format = 0x0e0, spurious_interrupt_vector = 0x0f0, in_service_0 = 0x100, in_service_1 = 0x110, in_service_2 = 0x120, in_service_3 = 0x130, in_service_4 = 0x140, in_service_5 = 0x150, in_service_6 = 0x160, in_service_7 = 0x170, trigger_mode_0 = 0x180, trigger_mode_1 = 0x190, trigger_mode_2 = 0x1a0, trigger_mode_3 = 0x1b0, trigger_mode_4 = 0x1c0, trigger_mode_5 = 0x1d0, trigger_mode_6 = 0x1e0, trigger_mode_7 = 0x1f0, interrupt_request_0 = 0x200, interrupt_request_1 = 0x210, interrupt_request_2 = 0x220, interrupt_request_3 = 0x230, interrupt_request_4 = 0x240, interrupt_request_5 = 0x250, interrupt_request_6 = 0x260, interrupt_request_7 = 0x270, error_status = 0x280, lvt_corrected_machine_check_interrupt = 0x2f0, interrupt_command_0 = 0x300, interrupt_command_1 = 0x310, lvt_timer = 0x320, lvt_thermal_sensors = 0x330, lvt_performance_monitoring_counters = 0x340, lvt_local_interrupt_0 = 0x350, lvt_local_interrupt_1 = 0x360, lvt_error = 0x370, initial_count = 0x380, current_count = 0x390, divide_configuration = 0x3e0, }; local_apic::local_apic(std::size_t major, std::size_t minor, std::uint64_t hardware_id, kapi::memory::physical_address base, bool is_bsp) : kapi::devices::device{major, minor, "lapic"} , m_hardware_id{hardware_id} , m_base{base} , m_is_bsp{is_bsp} {} auto local_apic::init() -> bool { auto static shared_virtual_base = kapi::memory::allocate_mmio_region(1); auto static is_mapped = false; if (!is_mapped) { if (!kapi::memory::map_mmio_region(shared_virtual_base, m_base, kapi::memory::page_mapper::flags::writable)) { kstd::println("[x86_64:DEV] LAPIC {} MMIO mapping failed!", m_hardware_id); return false; } is_mapped = true; } m_mapped_region = shared_virtual_base; if (m_is_bsp) { auto raw_version = read_register(registers::version); m_version = (raw_version >> offset_of_version) & 0xff; m_highest_lvt_entry_index = (raw_version >> offset_of_max_lvt_entry) & 0xff; m_supports_eoi_broadcast_suppression = (raw_version >> offset_of_eoi_suppression) & 0x1; write_register(registers::spurious_interrupt_vector, lapic_enable_bit | spurious_interrupt_vector); kstd::println("[x86_64:DEV] LAPIC initialized. version: {#x} | max_lvt_entry: {} | eoi_suppression: {:s}", m_version, m_highest_lvt_entry_index, m_supports_eoi_broadcast_suppression); } else { kstd::println("[x86_64:DEV] LAPIC {} is not on the BSP, deferring initialization.", m_hardware_id); } return true; } auto local_apic::read_register(registers id) const -> std::uint32_t { auto reg = static_cast(m_mapped_region.first + std::to_underlying(id)); return *reg; } auto local_apic::write_register(registers id, std::uint32_t value) -> void { auto reg = static_cast(m_mapped_region.first + std::to_underlying(id)); *reg = value; } } // namespace arch::devices