Handout
This commit is contained in:
228
kernel/arch/ioapic_registers.h
Normal file
228
kernel/arch/ioapic_registers.h
Normal file
@@ -0,0 +1,228 @@
|
||||
/*! \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
|
||||
Reference in New Issue
Block a user