#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(base_address + idx); } void write(Index idx, Register value) { *reinterpret_cast(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