124 lines
4.0 KiB
C++
124 lines
4.0 KiB
C++
#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<volatile Index *>(0xfec00000);
|
|
/// \copydoc IOREGSEL_REG
|
|
volatile Register *IOWIN_REG =
|
|
reinterpret_cast<volatile Register *>(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<volatile Index *>(APIC::getIOAPICAddress());
|
|
IOWIN_REG =
|
|
reinterpret_cast<volatile Index *>(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<uint8_t>(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<uint8_t>(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
|