Files
bsb2/kernel/arch/ioapic.cc
Niklas Gollenstede 174fe17e89 Handout
2025-10-31 22:37:36 +01:00

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