Branch: slice/6.4b. Commit only there; never to trunk. Two
check-ins, in order: (i) the guest's x2APIC and its timer, then (ii) the
8259 and the PIT, with the virtio console's interrupt. Get (i)'s specs
passing before starting (ii). Past ~400 non-spec lines in either, stop,
commit nothing, and say so (rule 3).
Building: x86_64 outside BQ2's seed. aarch64 only inside it, exactly as
docs/tasks/4.1a.md shows, only to show it's unchanged.
Read first:
- PLAN's What Linux sees: the x86 row and the note x86 guests' interrupts under choice A (decided Nov 11, 2026), which is this slice's design and its stakes; and the decision log's entry for it. No IOAPIC, no HPET, no instruction decoder.
docs/phases/06-interrupts-drivers.md: slices 6.4a and 6.4b ("keeping VMCB injection behind the same injection call as 6.4a is Loose Coupling").- What 6.4a merged (
boot/vgic.cr,Vgic.inject, thewfiwait), and 6.1b, 6.2 and 6.3b on x86 (boot/apic.cr,boot/timer.cr, the INTR intercept inboot/svm.cr), 5.2's port dispatcher and 5.5's PCI (boot/pci.cr,boot/virtio_pci.cr), and the test guest. - QEMU 11.1.2, standing in for AMD's and Intel's manuals:
target/i386/svm.h(V_IRQlines 9–10,V_INTR_PRIO,V_INTR_MASKING24–25,EVENTINJ42–53, the MSR permission map and the MSR exit),tcg/system/svm_helper.c(howEVENTINJandV_IRQare taken on entry, and line 270 for the MSR map),hw/intc/apic.c(x2APIC MSRs 0x800–0x8ff: ID, version, TPR, EOI, SVR, ICR, the LVT timer, initial and current count, divide,SELF_IPI),target/i386/cpu.c(CPUID leaf 1 ECX bit 21, x2APIC),hw/intc/i8259.c,hw/timer/i8254.c. Cite each value you use.
Claim. An x86 guest gets the interrupt hardware PLAN's x86 row now names: a local APIC in x2APIC mode that it reaches through MSRs, an 8259 pair and a PIT on I/O ports. Glint injects interrupts through the VMCB behind one injection call, the same shape as 6.4a's, and the guest takes them through its own IDT, ends them, and is interrupted again. Glint never decodes a guest instruction.
Passage. Hailperin §7.5.2, as in 6.4a. Quote it if the text is in the workspace; otherwise paraphrase and say so.
Make, (i) the x2APIC and its timer:
- CPUID: intercept CPUID and report x2APIC (leaf 1 ECX bit 21), with everything else passed through as now; the hypervisor leaf stays as PLAN's table says ("CPUID hypervisor leaf identifies Glint"), if it exists yet, or say that it doesn't.
boot/vlapic.cr: the guest's local APIC as x2APIC MSRs, intercepted through the MSR permission map: ID, version, TPR, EOI, SVR, ICR (fixed self-IPI only; other destinations refused until several CPUs exist), the in-service and request registers it needs, LVT timer, initial count, current count and divide.IA32_APIC_BASE(0x1b) reports x2APIC enabled only after the guest enables it, as hardware does. Only one CPU.- Injection:
Vlapic.inject(vector)sets it pending; before each guest entry Glint delivers the highest-priority pending vector the guest's TPR and in-service state allow, throughV_IRQwithV_INTR_VECTORwhen the guest has interrupts disabled (so it's taken when it enables them), orEVENTINJwhen it can take it now; say which you use when, and cite why. An EOI MSR write retires the in-service vector. - The guest's APIC timer: one-shot and periodic, counting from the
guest's own clock (the TSC or Glint's tick; say which and why), raising
its LVT vector through
Vlapic.inject. hlt: intercepted already; it now waits until an interrupt is pending for the guest, as 6.4a'swfidoes.- The test guest on x86_64 enables x2APIC, installs an IDT, arms its
APIC timer for 10 ms,
hlts, takes the vector, EOIs, and printstest guest: timer interrupt 0xNN after 10 ms(measured from the TSC, with the spec accepting 10–20 ms as 6.4a's does).
Make, (ii) the 8259, the PIT and the virtio interrupt:
boot/pic8259.cr: a master and slave pair on ports 0x20–0x21 and 0xa0–0xa1 and the ELCR at 0x4d0–0x4d1: initialization words, masks, specific and non-specific EOI, the cascade on IRQ 2, IRR/ISR reads. Its output reaches the guest through the local APIC's LINT0 in ExtINT mode (virtual-wire), as on a PC, soVlapicasks the 8259 for the vector.boot/pit8254.cr: the PIT on ports 0x40–0x43, channel 0 in modes 0, 2 and 3 raising IRQ 0, and channel 2's gate and output through port 0x61, which Linux's calibration reads. Counting at 1,193,182 Hz from the guest's clock.- The virtio console's interrupt:
VirtioPciraises an INTx line through the 8259 when it returns a used buffer; its configuration space's interrupt line and pin say which, and the ISR register acks it, as the legacy interface defines. - The test guest programs the 8259 (vectors 0x20/0x28), unmasks the
virtio line and IRQ 0, waits for the virtio console's buffer with
hltinstead of polling, printstest guest: virtio interrupt IRQ N received, then measures 10 ms with PIT channel 2 against the TSC and printstest guest: PIT agrees with the TSC(or both values).
Specs.
- Host: the x2APIC MSRs (reset values, enable sequence, EOI, TPR masking,
timer counting), the injection choice between
V_IRQandEVENTINJwith the VMCB fields built from QEMU's layout, the 8259's initialization and EOI sequences and cascade, and the PIT's modes and channel 2 gate. - Boots, outside the seed:
bin/glint-run x86_64 --mode hypervisor --guest build/x86_64/guest/test.efi --expect "VM: guest powered off"shows the new lines after each check-in, in order with the old ones. The aarch64 boots pass unedited in the seed.
Constraints. BQ2 tools only. No instruction decoder: every guest access here is a port or an MSR exit, reported with its operands. No start-up initialisers and no heap; the image check passes on both images. No new function over 2 KiB, and no existing one pushed over it, in the built image. About 300 non-spec lines in each check-in. Report every constraint you couldn't meet; never change one silently.
Demonstration. Each check-in: its host specs; outside the seed, the
whole x86_64 boot suite and crystal spec with ZEPHYR_SRC set; in the
seed, the whole aarch64 boot suite; crystal tool format --check.
Check-in comments. Rule 2's four parts, one per check-in.