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.
bsb1/arch/ioapic_registers.h

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