#include "ioapic.h" #include "../debug/assert.h" #include "apic.h" #include "core.h" namespace IOAPIC { /*! \brief IOAPIC registers memory mapped into the CPU's address space. * * Access to the actual IOAPIC registers can be obtained by performing the * following steps: * 1. Write the number of the IOAPIC register to the address stored in * `IOREGSEL_REG` * 2. Read the value from / write the value to the address referred to by * `IOWIN_REG`. * * \see [IO-APIC manual](intel_ioapic.pdf#page=8) */ volatile Index *IOREGSEL_REG = reinterpret_cast(0xfec00000); /// \copydoc IOREGSEL_REG volatile Register *IOWIN_REG = reinterpret_cast(0xfec00010); // IOAPIC manual, p. 8 const Index IOAPICID_IDX = 0x00; const Index IOREDTBL_IDX = 0x10; const uint8_t slot_max = 24; // Helper function that reads a single register static Register read(Index reg) { *IOREGSEL_REG = reg; Register value = *IOWIN_REG; return value; } // Helper function that writes a single register static void write(Index reg, Register value) { *IOREGSEL_REG = reg; *IOWIN_REG = value; } // Helper function that reads an entry (= reads from TWO registers) from the // redirection table static RedirectionTableEntry getRedirectionTableEntry(uint8_t slot) { assert(slot < slot_max); return RedirectionTableEntry(read(IOREDTBL_IDX + 2 * slot + 0), read(IOREDTBL_IDX + 2 * slot + 1)); } // Helper function that writes an entry (= writes to TWO registers) to the // redirection table static void setRedirectionTableEntry(uint8_t slot, RedirectionTableEntry rt) { assert(slot < slot_max); write(IOREDTBL_IDX + 2 * slot + 0, rt.value_low); write(IOREDTBL_IDX + 2 * slot + 1, rt.value_high); } static void setID(uint8_t id) { Identification reg(read(IOAPICID_IDX)); reg.id = id; write(IOAPICID_IDX, reg.value); } void init() { // optional: IOREGSEL_REG = reinterpret_cast(APIC::getIOAPICAddress()); IOWIN_REG = reinterpret_cast(APIC::getIOAPICAddress() + 0x10); // set 4-bit ID to correct IO-APIC value, determined at startup setID(APIC::getIOAPICID()); for (uint8_t i = 0; i < slot_max; ++i) { RedirectionTableEntry rt = getRedirectionTableEntry(i); /* The old solution used 0xff as `destination` here, but the Intel * manual (3A, 10.6.2.2 - Logical Destination Mode) states the * following: "For both configurations of logical destination mode, when * combined with lowest priority delivery mode, software is responsible * for ensuring that all of the local APICs included in or addressed by * the IPI or I/O subsystem interrupt are present and enabled to receive * the interrupt." More recent processors assume the correctness of the * value written to `destination`, and, therefore, will try to redirect * the interrupt to non-existing LAPIC if misconfigured and wait for * response. */ rt.destination = 1; rt.vector = static_cast(Core::Interrupt::Vector::PANIC); rt.delivery_mode = DeliveryMode::LOWEST_PRIORITY; rt.destination_mode = DestinationMode::LOGICAL; rt.polarity = Polarity::HIGH; rt.trigger_mode = TriggerMode::EDGE; rt.interrupt_mask = InterruptMask::MASKED; setRedirectionTableEntry(i, rt); } } void config(uint8_t slot, Core::Interrupt::Vector vector, TriggerMode trigger_mode, Polarity polarity) { RedirectionTableEntry rt = getRedirectionTableEntry(slot); rt.vector = static_cast(vector); rt.polarity = polarity; rt.trigger_mode = trigger_mode; setRedirectionTableEntry(slot, rt); } void allow(uint8_t slot) { RedirectionTableEntry rt = getRedirectionTableEntry(slot); rt.interrupt_mask = InterruptMask::UNMASKED; setRedirectionTableEntry(slot, rt); } void forbid(uint8_t slot) { RedirectionTableEntry rt = getRedirectionTableEntry(slot); rt.interrupt_mask = InterruptMask::MASKED; setRedirectionTableEntry(slot, rt); } bool status(uint8_t slot) { RedirectionTableEntry rt = getRedirectionTableEntry(slot); return rt.interrupt_mask == InterruptMask::UNMASKED; } } // namespace IOAPIC