aarch64: GIC SPI wiring for virtio-input (item 4.3.5d)
Punch list §25 item 4.3.5d complete. New apic_spi_enable(intid) generalizes item 0.6's PPI-only sequence to one explicit SPI (IPRIORITYR/ISENABLER/ITARGETSR, ICFGR read-checked not written). Verified via a software-pended SPI (GICD_ISPENDR, no device needed) through the existing generic IRQ dispatch, which needed no changes. Found and fixed a real bug during verification: PSTATE.I is still set at apic_init()'s point in boot, so the first self-test run correctly latched but never took the interrupt. Self-test code reverted after capturing evidence -- interrupts.c has zero net diff, only apic_spi_enable() remains, unused until 4.3.5e. Three-arch acceptance boot clean. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
8251aebcf8
commit
2a2d7c5e5e
@@ -5,12 +5,14 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* apic.c (aarch64) - Minimal GICv2 driver + ARM Generic Timer (items 0.6/0.7).
|
||||
* apic.c (aarch64) - Minimal GICv2 driver + ARM Generic Timer (items 0.6/0.7/
|
||||
* 4.3.5d).
|
||||
*
|
||||
* "Minimal" is deliberate and stays deliberate: exactly one interrupt (the
|
||||
* non-secure EL1 physical timer PPI, or its EL2 hypervisor-timer counterpart
|
||||
* if ever run at EL2) is enabled. This is not a general GIC driver and must
|
||||
* not grow into one -- no SPI support, no SGI support, no GICv3/ITS.
|
||||
* "Minimal" is deliberate and stays deliberate: item 0.6 enabled exactly one
|
||||
* PPI (the timer); item 4.3.5d adds the ability to enable exactly one SPI
|
||||
* (for virtio-keyboard-pci, item 4.3.5e), still by explicit per-INTID call,
|
||||
* not a general GIC driver -- no SGI support, no GICv3/ITS, no SPI beyond
|
||||
* the one 4.3.5e will actually enable.
|
||||
*
|
||||
* Base addresses and the PPI INTID are QEMU-virt-machine constants, not
|
||||
* device-tree-discovered, and that is a deliberate, recorded exception
|
||||
@@ -56,7 +58,9 @@ extern int aarch64_current_el(void);
|
||||
#define GICD_CTLR 0x000
|
||||
#define GICD_ISENABLER0 0x100
|
||||
#define GICD_IPRIORITYR 0x400
|
||||
#define GICD_ITARGETSR 0x800 /* item 4.3.5d -- SPI target-CPU byte array */
|
||||
#define GICD_ICFGR 0xC00
|
||||
#define GICD_ISPENDR 0x200 /* item 4.3.5d -- software set-pending, SPIs only */
|
||||
|
||||
/* GICv2 CPU Interface register byte offsets. */
|
||||
#define GICC_CTLR 0x00
|
||||
@@ -148,6 +152,70 @@ int apic_init(BootInfo *boot_info)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable and route one SPI (item 4.3.5d).
|
||||
*
|
||||
* Extends item 0.6's PPI-only init just far enough to bring up one Shared
|
||||
* Peripheral Interrupt -- still not a general GIC driver, still one
|
||||
* explicit INTID per call, same bounded-scope posture 0.6 used.
|
||||
*
|
||||
* Programs, for @p intid (expected range: SPIs, INTID >= 32):
|
||||
* 1. @c GICD_IPRIORITYR (byte-indexed, same pattern @c apic_init() already
|
||||
* uses for the timer PPI) -- @c TIMER_PRIORITY, below @c PMR_ALLOW_ALL.
|
||||
* 2. @c GICD_ISENABLER at @c 0x100 + 4*(intid/32) -- PPIs/SGIs (0-31) live
|
||||
* in word 0 (@c GICD_ISENABLER0); SPIs beyond use word @c intid/32, same
|
||||
* architecture layout, not board-specific.
|
||||
* 3. @c GICD_ITARGETSR (byte-indexed like IPRIORITYR) -- routes to CPU 0
|
||||
* only; this target has no @c -smp, so no other bit is ever valid.
|
||||
* 4. @c GICD_ICFGR is read back, not written, unless the readback disagrees
|
||||
* with the expected level-triggered configuration -- FABRIC.md §27.5.1's
|
||||
* decoded QEMU `interrupt-map` says PCI legacy INTx on this board already
|
||||
* is level-triggered by default, so this keeps item 0.6's "don't touch
|
||||
* ICFGR unless forced to" posture rather than writing it unconditionally.
|
||||
*
|
||||
* Does **not** touch @c GICD_CTLR or @c GICC_CTLR/@c PMR -- @c apic_init()
|
||||
* already enabled the distributor and CPU interface for the timer PPI, and
|
||||
* those are global, not per-INTID; nothing here needs to repeat that.
|
||||
*
|
||||
* @param intid Target SPI's INTID (32 + GIC SPI number).
|
||||
*/
|
||||
void apic_spi_enable(uint32_t intid)
|
||||
{
|
||||
{
|
||||
uint32_t reg_off = GICD_IPRIORITYR + (intid & ~3u);
|
||||
uint32_t shift = (intid & 3u) * 8u;
|
||||
uint32_t val = mmio_read32(GICD_BASE_PA, reg_off);
|
||||
val = (val & ~(0xFFu << shift)) | (TIMER_PRIORITY << shift);
|
||||
mmio_write32(GICD_BASE_PA, reg_off, val);
|
||||
}
|
||||
|
||||
{
|
||||
uint32_t reg_off = GICD_ISENABLER0 + 4u * (intid / 32u);
|
||||
uint32_t bit = intid % 32u;
|
||||
mmio_write32(GICD_BASE_PA, reg_off, 1u << bit);
|
||||
}
|
||||
|
||||
{
|
||||
uint32_t reg_off = GICD_ITARGETSR + (intid & ~3u);
|
||||
uint32_t shift = (intid & 3u) * 8u;
|
||||
uint32_t val = mmio_read32(GICD_BASE_PA, reg_off);
|
||||
val = (val & ~(0xFFu << shift)) | (0x01u << shift); /* CPU 0 only */
|
||||
mmio_write32(GICD_BASE_PA, reg_off, val);
|
||||
}
|
||||
|
||||
{
|
||||
uint32_t reg_off = GICD_ICFGR + 4u * (intid / 16u);
|
||||
uint32_t bitpos = (intid % 16u) * 2u;
|
||||
uint32_t val = mmio_read32(GICD_BASE_PA, reg_off);
|
||||
uint32_t level_expected = 0u; /* level-triggered = the "trigger mode"
|
||||
* bit (bit 1 of the 2-bit field) clear */
|
||||
if (((val >> bitpos) & 0x2u) != level_expected) {
|
||||
val = (val & ~(0x3u << bitpos)) | (level_expected << bitpos);
|
||||
mmio_write32(GICD_BASE_PA, reg_off, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read the GIC CPU interface's Interrupt Acknowledge Register.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user