paulbergmann_mpstubs/machine/lapic.cc

192 lines
5.1 KiB
C++

#include "machine/lapic.h"
#include "machine/lapic_registers.h"
namespace LAPIC {
/*! \brief Base Address
* used with offset to access memory mapped registers
*/
volatile uintptr_t base_address = 0xfee00000;
Register read(Index idx) {
return *reinterpret_cast<volatile Register *>(base_address + idx);
}
void write(Index idx, Register value) {
*reinterpret_cast<volatile Register *>(base_address + idx) = value;
}
/*! \brief Local APIC ID (for Pentium 4 and newer)
*
* Is assigned automatically during boot and should not be changed.
*
* \see [ISDMv3, 10.4.6 Local APIC ID](intel_manual_vol3.pdf#page=371)
*/
union IdentificationRegister {
struct {
uint32_t : 24, ///< (reserved)
apic_id : 8; ///< APIC ID
};
Register value;
IdentificationRegister() : value(read(Index::IDENTIFICATION)) {}
} __attribute__((packed));
/*! \brief Local APIC Version
*
* \see [ISDMv3 10.4.8 Local APIC Version Register](intel_manual_vol3.pdf#page=373)
*/
union VersionRegister {
struct {
uint32_t version : 8, ///< 0x14 for P4 and Xeon, 0x15 for more recent hardware
: 8, ///< (reserved)
max_lvt_entry : 8, ///< Maximum number of local vector entries
suppress_eoi_broadcast : 1, ///< Support for suppressing EOI broadcasts
: 7; ///< (reserved)
};
Register value;
VersionRegister() : value(read(Index::VERSION)) {}
} __attribute__((packed));
/*! \brief Logical Destination Register
* \see [ISDMv3 10.6.2.2 Logical Destination Mode](intel_manual_vol3.pdf#page=385)
*/
union LogicalDestinationRegister {
struct {
uint32_t : 24, ///< (reserved)
lapic_id : 8; ///< Logical APIC ID
};
Register value;
LogicalDestinationRegister() : value(read(Index::LOGICAL_DESTINATION)) {}
~LogicalDestinationRegister() {
write(Index::LOGICAL_DESTINATION, value);
}
} __attribute__((packed));
enum Model {
CLUSTER = 0x0,
FLAT = 0xf
};
/*! \brief Destination Format Register
*
* \see [ISDMv3 10.6.2.2 Logical Destination Mode](intel_manual_vol3.pdf#page=385)
*/
union DestinationFormatRegister {
struct {
uint32_t : 28; ///< (reserved)
Model model : 4; ///< Model (Flat vs. Cluster)
};
Register value;
DestinationFormatRegister() : value(read(Index::DESTINATION_FORMAT)) {}
~DestinationFormatRegister() {
write(Index::DESTINATION_FORMAT, value);
}
} __attribute__((packed));
/*! \brief Task Priority Register
*
* \see [ISDMv3 10.8.3.1 Task and Processor Priorities](intel_manual_vol3.pdf#page=391)
*/
union TaskPriorityRegister {
struct {
uint32_t task_prio_sub : 4, ///< Task Priority Sub-Class
task_prio : 4, ///< Task Priority
: 24; ///< (reserved)
};
Register value;
TaskPriorityRegister() : value(read(Index::TASK_PRIORITY)) {}
~TaskPriorityRegister() {
write(Index::TASK_PRIORITY, value);
}
} __attribute__((packed));
/*! \brief APIC Software Status for Spurious Interrupt Vector */
enum APICSoftware {
APIC_DISABLED = 0,
APIC_ENABLED = 1,
};
/*! \brief Focus Processor Checking for Spurious Interrupt Vector */
enum FocusProcessorChecking {
CHECKING_ENABLED = 0,
CHECKING_DISABLED = 1,
};
/*! \brief Suppress End-Of-Interrupt-Broadcast for Spurious Interrupt Vector */
enum SuppressEOIBroadcast {
BROADCAST = 0,
SUPPRESS_BROADCAST = 1,
};
/*! \brief Spurious Interrupt Vector Register
*
* \see [ISDMv3 10.9 Spurious Interrupt](intel_manual_vol3.pdf#page=394)
*/
union SpuriousInterruptVectorRegister {
struct {
uint32_t spurious_vector : 8; ///< Spurious Vector
APICSoftware apic_software : 1; ///< APIC Software Enable/Disable
FocusProcessorChecking focus_processor_checking : 1; ///< Focus Processor Checking
uint32_t reserved_1 : 2;
SuppressEOIBroadcast eoi_broadcast_suppression : 1;
uint32_t reserved:19;
};
Register value;
SpuriousInterruptVectorRegister() : value(read(Index::SPURIOUS_INTERRUPT_VECTOR)) {}
~SpuriousInterruptVectorRegister() {
write(Index::SPURIOUS_INTERRUPT_VECTOR, value);
}
} __attribute__((packed));
static_assert(sizeof(SpuriousInterruptVectorRegister) == 4, "LAPIC Spurious Interrupt Vector has wrong size");
uint8_t getID() {
IdentificationRegister ir;
return ir.apic_id;
}
uint8_t getLogicalID() {
LogicalDestinationRegister ldr;
return ldr.lapic_id;
}
uint8_t getVersion() {
VersionRegister vr;
return vr.version;
}
void init(uint8_t logical_id) {
// reset logical destination ID
// can be set using setLogicalLAPICID()
LogicalDestinationRegister ldr;
ldr.lapic_id = logical_id;
// set task priority to 0 -> accept all interrupts
TaskPriorityRegister tpr;
tpr.task_prio = 0;
tpr.task_prio_sub = 0;
// set flat delivery mode
DestinationFormatRegister dfr;
dfr.model = Model::FLAT;
// use 255 as spurious vector, enable APIC and disable focus processor
SpuriousInterruptVectorRegister sivr;
sivr.spurious_vector = 0xff;
sivr.apic_software = APICSoftware::APIC_ENABLED;
sivr.focus_processor_checking = FocusProcessorChecking::CHECKING_DISABLED;
}
void endOfInterrupt() {
// dummy read
read(SPURIOUS_INTERRUPT_VECTOR);
// signal end of interrupt
write(EOI, 0);
}
} // namespace LAPIC