Handout
This commit is contained in:
123
kernel/arch/ioapic.cc
Normal file
123
kernel/arch/ioapic.cc
Normal file
@@ -0,0 +1,123 @@
|
||||
#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
|
||||
Reference in New Issue
Block a user