You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
228 lines
8.1 KiB
C++
228 lines
8.1 KiB
C++
/*! \file
|
|
* \brief Helper structures for interacting with the \ref IOAPIC "I/O APIC".
|
|
*/
|
|
|
|
#pragma once
|
|
#include "../types.h"
|
|
|
|
namespace IOAPIC {
|
|
typedef uint32_t Index;
|
|
typedef uint32_t Register;
|
|
|
|
extern volatile Index *IOREGSEL_REG;
|
|
extern volatile Register *IOWIN_REG;
|
|
|
|
/*! \brief I/O APIC Identification
|
|
*
|
|
* The IOAPICID register is register number 0x0. The I/O APIC's ID will be read
|
|
* from the system configuration tables (provided by the BIOS) during boot. The
|
|
* number can be queried by calling \ref APIC::getIOAPICID(). During
|
|
* initialization, this number must be written to the IOAPICID register.
|
|
*
|
|
* \see [IO-APIC manual](intel_ioapic.pdf#page=9), page 9
|
|
*/
|
|
union Identification {
|
|
struct {
|
|
uint32_t : 24, ///< Reserved, do not modify
|
|
id : 4, ///< I/O APIC Identification
|
|
: 4; ///< Reserved, do not modify
|
|
};
|
|
Register value;
|
|
explicit Identification(Register value) : value(value) {}
|
|
} __attribute__((packed));
|
|
static_assert(sizeof(Identification) == 4,
|
|
"IOAPIC Identification has wrong size");
|
|
|
|
/*! \brief Delivery mode specifies the type of interrupt sent to the CPU. */
|
|
enum DeliveryMode {
|
|
FIXED = 0, ///< "ordinary" interrupt; send to ALL cores listed in the
|
|
///< destination bit mask
|
|
LOWEST_PRIORITY = 1, ///< "ordinary" interrupt; send to the lowest priority
|
|
///< core from destination mask
|
|
SMI = 2, ///< System Management Interrupt; vector number required to be 0
|
|
// Reserved
|
|
NMI = 4, ///< Non-Maskable Interrupt, vector number ignored, only edge
|
|
///< triggered
|
|
INIT = 5, ///< Initialization interrupt (always treated as edge triggered)
|
|
// Reserved
|
|
EXTERN_INT = 7 ///< external interrupt (only edge triggered)
|
|
};
|
|
|
|
/*! \brief Way of interpreting the value written to the destination field. */
|
|
enum DestinationMode {
|
|
PHYSICAL = 0, ///< Destination contains the physical destination APIC ID
|
|
LOGICAL = 1 ///< Destination contains a mask of logical APIC IDs
|
|
};
|
|
|
|
/*! \brief Interrupt polarity for the redirection-table entry */
|
|
enum Polarity {
|
|
HIGH = 0, ///< active high
|
|
LOW = 1 ///< active low
|
|
};
|
|
|
|
/*! \brief Trigger mode */
|
|
enum TriggerMode {
|
|
EDGE = 0, ///< edge triggered
|
|
LEVEL = 1 ///< level triggered
|
|
};
|
|
|
|
/*! \brief Interrupt state */
|
|
enum DeliveryStatus {
|
|
IDLE = 0, ///< No activity for this interrupt
|
|
SEND_PENDING =
|
|
1 ///< Interrupt will be sent as soon as the bus / LAPIC is ready
|
|
};
|
|
|
|
/*! \brief Interrupt masking */
|
|
enum InterruptMask {
|
|
UNMASKED = 0, ///< Redirection-table entry is active (non-masked)
|
|
MASKED = 1 ///< Redirection-table entry is inactive (masked)
|
|
};
|
|
|
|
/*! \brief Entry in the redirection table.
|
|
*
|
|
* The redirection table begins with I/O APIC register `0x10` and ends at
|
|
* `0x3f`.
|
|
*
|
|
* Each entry has a size of 64 bit, equaling two I/O APIC registers.
|
|
* For instance, entry 0 is stored in registers `0x10` and `0x11`, in which the
|
|
* low-order 32 bit (equals \ref value_low) and high-order 32 bit (equals \ref
|
|
* value_high) need to be stored.
|
|
*
|
|
* The union defined below provides an overlay allowing convenient modification
|
|
* of individual bits, while the 32-bit values \ref value_low and \ref
|
|
* value_high can be used for writing to the I/O APIC registers.
|
|
*
|
|
* \note [Type punning](https://en.wikipedia.org/wiki/Type_punning#Use_of_union)
|
|
* is indeed undefined behavior in C++. However, *gcc* explicitly allows
|
|
* this construct as a [language extension](https://gcc.gnu.org/bugs/#nonbugs).
|
|
* Some compilers ([other than
|
|
* gcc](https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#Type%2Dpunning)
|
|
* might allow this feature only by disabling strict aliasing
|
|
* (`-fno-strict-aliasing`). In \StuBS we use this feature extensively due to
|
|
* the improved code readability.
|
|
*
|
|
* \see [IO-APIC manual](intel_ioapic.pdf#page=11), page 11-13
|
|
*/
|
|
union RedirectionTableEntry {
|
|
// @cond ANONYMOUS_STRUCT
|
|
struct {
|
|
// @endcond
|
|
|
|
/*! \brief Interrupt vector in the \ref IDT "Interrupt Descriptor Table
|
|
* (IDT)" will be activated when the corresponding external interrupt
|
|
* triggers.
|
|
*/
|
|
uint64_t vector : 8;
|
|
|
|
/*! \brief The delivery mode denotes the way the interrupts will be
|
|
* delivered to the local CPU cores, respectively to their local APICs.
|
|
*
|
|
* For StuBS, we use \ref LOWEST_PRIORITY, as all CPU cores have the same
|
|
* priority and we want to distribute interrupts evenly among them.
|
|
* It, however, is not guaranteed that this method of load balancing will
|
|
* work on every system.
|
|
*/
|
|
DeliveryMode delivery_mode : 3;
|
|
|
|
/*! \brief The destination mode defines how the value stored in \ref
|
|
* destination will be interpreted.
|
|
*
|
|
* For StuBS, we use \ref LOGICAL
|
|
*/
|
|
DestinationMode destination_mode : 1;
|
|
|
|
/*! \brief Delivery status holds the current status of interrupt delivery.
|
|
*
|
|
* \note This field is read only; write accesses to this field will be
|
|
* ignored.
|
|
*/
|
|
DeliveryStatus delivery_status : 1;
|
|
|
|
/*! \brief The polarity denotes when an interrupt should be issued.
|
|
*
|
|
* For StuBS, we usually use \ref HIGH (i.e., when the interrupt line is,
|
|
* logically, `1`).
|
|
*/
|
|
Polarity polarity : 1;
|
|
|
|
/*! \brief The remote IRR bit indicates whether the local APIC(s) accept the
|
|
* level interrupt.
|
|
*
|
|
* Once the LAPIC sends an \ref LAPIC::endOfInterrupt "End Of Interrupt
|
|
* (EOI)", this bit is reset to `0`.
|
|
*
|
|
* \note This field is read only and is only meaningful for level-triggered
|
|
* interrupts.
|
|
*/
|
|
uint64_t remote_irr : 1;
|
|
|
|
/*! \brief The trigger mode states whether the interrupt signaling is level
|
|
* or edge triggered.
|
|
*
|
|
* StuBS uses \ref EDGE for the Timer, the Keybaord and (optional) serial
|
|
* interface need \ref LEVEL
|
|
*/
|
|
TriggerMode trigger_mode : 1;
|
|
|
|
/*! \brief Mask or unmask interrupts for a particular, external source.
|
|
*
|
|
* The interrupt mask denotes whether interrupts should be
|
|
* accepted/unmasked (value \ref UNMASKED) or ignored/masked (value \ref
|
|
* MASKED).
|
|
*/
|
|
InterruptMask interrupt_mask : 1;
|
|
|
|
/*! \brief Reserved, do not modify. */
|
|
uint64_t : 39;
|
|
|
|
/*! \brief Interrupt destination.
|
|
*
|
|
* The meaning of destination depends on the destination mode:
|
|
* For the logical destination mode, destination holds a bit mask made up
|
|
* of the cores that are candidates for receiving the interrupt. In the
|
|
* single-core case, this value is `1`, in the multi-core case, the `n`
|
|
* low-order bits needs to be set (with `n` being the number of CPU cores,
|
|
* see \ref Core::count() ). Setting the `n` low-order bits marks all
|
|
* available cores as candidates for receiving interrupts and thereby
|
|
* balancing the number of interrupts between the cores.
|
|
*
|
|
* \note This form of load balancing depends on the hardware's behavior and
|
|
* may not work on all systems in the same fashion. Most notably, in QEMU
|
|
* all interrupts are sent to the BSP (core 0).
|
|
*/
|
|
uint64_t destination : 8;
|
|
|
|
// @cond ANONYMOUS_STRUCT
|
|
} __attribute__((packed));
|
|
// @endcond
|
|
|
|
// @cond ANONYMOUS_STRUCT
|
|
struct {
|
|
// @endcond
|
|
|
|
Register value_low; ///< Low-order 32 bits (for the register with the
|
|
///< smaller index)
|
|
Register value_high; ///< High-order 32 bits (for the register with the
|
|
///< higher index)
|
|
// @cond ANONYMOUS_STRUCT
|
|
} __attribute__((packed));
|
|
// @endcond
|
|
|
|
/*! \brief Constructor for an redirection-table entry
|
|
*
|
|
* Every entry in the redirection table represents an external source of
|
|
* interrupts and has a size of 64 bits. Due to the I/O APIC registers being
|
|
* only 32 bits wide, the constructor takes two 32 bit values.
|
|
*
|
|
* \param value_low First, low-order 32 bit value
|
|
* \param value_high Second, high-order 32 bit value
|
|
*/
|
|
RedirectionTableEntry(Register value_low, Register value_high)
|
|
: value_low(value_low), value_high(value_high) {}
|
|
};
|
|
|
|
static_assert(sizeof(RedirectionTableEntry) == 8,
|
|
"IOAPIC::RedirectionTableEntry has wrong size");
|
|
} // namespace IOAPIC
|