/** * APIC (Advanced Programmable Interrupt Controller) functionality. */ module hulk.apic; import hulk.klog; import hulk.hurl; import hulk.acpi; import hulk.idt; import hulk.rtc; struct Apic { private enum uint PERIODIC_MODE = 0x2_0000u; private enum ulong IRQ_RTC = 8u; static struct ApicRegister { public uint value; alias value this; private ubyte[12] _padding; } static struct ApicRegisters { ApicRegister[2] _reserved0; ApicRegister lapic_id; ApicRegister lapic_version; ApicRegister[4] _reserved1; ApicRegister task_priority; ApicRegister arbitration_priority; ApicRegister processor_priority; ApicRegister eoi; ApicRegister remote_read; ApicRegister logical_destination; ApicRegister destination_format; ApicRegister spurious_interrupt_vector; ApicRegister[8] in_service; ApicRegister[8] trigger_mode; ApicRegister[8] interrupt_request; ApicRegister error_status; ApicRegister[6] _reserved2; ApicRegister lvt_cmci; ApicRegister[2] interrupt_command; ApicRegister lvt_timer; ApicRegister lvt_thermal_sensor; ApicRegister lvt_performance_monitoring_counters; ApicRegister[2] lvt_lint; ApicRegister lvt_error; ApicRegister initial_count; ApicRegister current_count; ApicRegister[4] _reserved3; ApicRegister divide_configuration; } static struct IoApicRegisters { ApicRegister address; ApicRegister data; } private static __gshared ApicRegisters * apic_registers; private static __gshared IoApicRegisters * io_apic_registers; public static void initialize() { apic_registers = cast(ApicRegisters *)Acpi.apic_address; io_apic_registers = cast(IoApicRegisters *)0xFEC0_0000u; Hurl.map(cast(ulong)apic_registers, cast(ulong)apic_registers, PT_WRITABLE | PT_WRITE_THROUGH | PT_DISABLE_CACHE | PT_NO_EXECUTE); Hurl.map(cast(ulong)io_apic_registers, cast(ulong)io_apic_registers, PT_WRITABLE | PT_WRITE_THROUGH | PT_DISABLE_CACHE | PT_NO_EXECUTE); Klog.writefln("LAPIC ID: 0x%08x", apic_registers.lapic_id.value); Klog.writefln("LAPIC version: 0x%08x", apic_registers.lapic_version.value); /* Enable local APIC to receive interrupts and set spurious interrupt * vector to 0xFF. */ apic_registers.spurious_interrupt_vector.value = 0x100u | Idt.INT_APIC_SPURIOUS; apic_registers.lvt_timer.value = Idt.INT_APIC_TIMER; apic_registers.lvt_lint[0].value = Idt.INT_APIC_LINT0; apic_registers.lvt_lint[1].value = Idt.INT_APIC_LINT1; /* Enable RTC interrupt. */ configure_io_apic_irq(IRQ_RTC, Idt.INT_APIC_BASE + IRQ_RTC); } private static void configure_io_apic_irq(size_t io_apic_irq, size_t interrupt_id) { ulong entry = interrupt_id; io_apic_registers.address.value = cast(uint)(0x10u + io_apic_irq * 2u); io_apic_registers.data.value = entry & 0xFFFF_FFFFu; io_apic_registers.address.value = cast(uint)(0x10u + io_apic_irq * 2u + 1u); io_apic_registers.data.value = entry >> 32u; } public static void eoi() { apic_registers.eoi.value = 0u; } public static void isr(ulong vector) { switch (vector) { case Idt.INT_APIC_BASE + IRQ_RTC: Rtc.isr(); break; default: break; } eoi(); } }