Repository navigation
feat(cpu): send a PCI device's pin through its I/O APIC line when it has no MSI - #799
Merged
MasterLaplace merged 9 commits intoNov 8, 2026
Conversation
…has no MSI The boot tells firmware the APIC model when the MADT declares an I/O APIC. A function's pin then goes to the line its published _PRT route gives, with the route's trigger and polarity, on a device vector, to the core that asks. The audio capture takes MSI first, then its I/O APIC line, and its 8259 line only in the PIC model.
…pts-through-the-io-apic
…pts-through-the-io-apic
…the APIC model Step 5's \_PIC (1) adds 3,066 instructions to W2 (76,303). W5 drops from 537,507 to 373,080: in the APIC model q35's _PRT names its GSIx links, whose _CRS and _PRS are data, where its LNKx links run methods that read the PIRQ registers.
…reaches a core The boot told firmware the APIC model whenever the MADT declared an I/O APIC. In x2APIC mode on bare metal no device interrupt reaches a core without interrupt remapping, so the I/O APIC line and MSI were both refused, and the 8259 too, since firmware may have moved PCI interrupts off it: capture lost its interrupt. The model is now APIC only when device interrupts are delivered in the mode the local APICs will run, which apic_runs_x2apic_mode tells before the local APIC is initialised. Step 5 of the specification writes the rule.
…rves An 8-bit register index reaches entry 0x77; entries 0x78 to 0x7F wrapped onto the identifier, version and arbitration registers. A PCI route to a Global System Interrupt that an ISA IRQ with an 8259 handler also drives would have programmed that pin level-triggered under the ISA device. The capture test now checks the line's slot, that it fired more than once, that another vector and an ISA handler are refused, that the vector holding it can aim it at another core, and the 8259 line where no device interrupt reaches a core. The new macros spell out their acronym, and the W2 header counts \_PIC (1) against the PIC model's 73,251 instructions.
…pts-through-the-io-apic # Conflicts: # tools/budgets/acpi-interrupt-routing.budget
After a \_PIC (1) that fails, the routes answer for an unknown model: firmware may have moved PCI interrupts off the 8259 or not, so audio capture takes neither a line nor the 8259 there. The rule is the routing module's, advanced_configuration_and_power_interface_interrupt_routing_ leaves_the_8259, and its test runs on the host and in ring 0.
…entified once 373,080 to 354,430 instructions, measured on the release server kernel.
MasterLaplace
added a commit
that referenced
this pull request
Nov 8, 2026
…d over x2APIC (#797) Closes #795 ## What changed On bare metal (CPUID.1:ECX[31] clear) the local APIC stays in xAPIC mode unless the firmware already set IA32_APIC_BASE.EXTD; under a hypervisor x2APIC is turned on as before. The bootstrap processor chooses (`apic_choose_mode`), every application processor applies that mode, and each says why: `CPU APIC mode=xapic-active, reason=bare-metal-without-interrupt-remapping, apic_id=1`, and `apic_reason=` in `[LPLTLM] machine`. An AP the mode cannot address (0xFF and up in xAPIC) is refused by name and counted as `ap_unaddressable` in `[LPLTLM] smp`. ## Why Without interrupt remapping no device interrupt reaches x2APIC mode on bare metal (Intel SDM vol. 3, 325384-093US, §13.12.6-7), and `device_interrupt_platform_delivers` refuses to aim one there. ## With main Since #799 the boot chooses the interrupt model it tells firmware before the local APIC is initialised, from `apic_runs_x2apic_mode`. That prediction now follows this rule: before `apic_initialize_on_cpu`, the mode `apic_choose_mode` would choose; after it, the chosen one. `the_interrupt_model_follows_the_mode_the_local_apics_run` checks the model against the mode the local APICs run, computed apart; the old prediction fails it under `-cpu max,-hypervisor`. Merged with main, every boot ends with 0 fail: - server: q35 with NVMe 867, and 870 under `-cpu max,-hypervisor`; pc 854; - satellite: q35 713 to 716 and pc 707 to 709, with MSI and on the I/O APIC line (GSI 23 and 11), under a hypervisor and without. ## How to verify `./build.sh --server`, an image from `python3 -I tools/nvme-test-image.py nvme.img`, then ``` qemu-system-i386 -machine q35 -cpu max,-hypervisor -m 1G -smp 2 -kernel kernel/lpl.kernel -display none -serial stdio \ -drive file=nvme.img,if=none,id=lplnvme,format=raw -device nvme,serial=lplnvme0,drive=lplnvme \ -append 'lpl.test.require=non_volatile_memory_express.*,message_signaled_interrupts.*' ``` KTAP `# Totals`, the same command with the row's `-machine`/`-cpu`: | bench | main | branch | |---|---|---| | q35 `-cpu max,-hypervisor` | 852 pass, 1 fail (`each_queue_interrupts_the_core_its_entry_names`) | 857 pass, 0 fail | | q35 `-cpu max` | 853 / 0 | 856 / 0 | | q35, default CPU | 850 / 1, the intermittent failure below | 854 / 0 | | pc, no drive | 838 / 0 | 841 / 0 | | APIC IDs 0x0, 0xFF, 0x100 (the bench of #564), with and without `-hypervisor` | 18 / 0 | 21 / 0 | At 7f0e459, which keeps today's rule, `bare_metal_keeps_xapic_unless_the_firmware_handed_over_x2apic` and `the_bootstrap_processor_runs_the_mode_this_machine_gets` fail; `every_processor_runs_the_mode_the_bootstrap_processor_chose` fails once an AP keeps the mode it woke in. Under qboot (`-bios /usr/share/qemu/qboot.rom`), which leaves xAPIC with IDs 0xFF and 0x100, both APs are refused by name. ## Not in it - Interrupt remapping (#108): where the firmware hands over x2APIC on bare metal, device messages and I/O APIC lines stay refused, the NVMe test fails as on main, and the boot keeps the PIC model. - Linux trusts only hypervisors whose guest support says so; every one is still trusted. - On main the NVMe test failed 2 of 16 boots on q35 `-cpu max` or the default CPU; not seen in 30 boots of the branch, not investigated.
MasterLaplace
added a commit
that referenced
this pull request
Nov 8, 2026
…7, and count them (#804) ## What An I/O APIC's version register declares up to 256 inputs, but its 8-bit register index reaches entry 0x77 at most (82093AA datasheet, §3.1.1 and §3.2.4). Since #799 an entry past 0x77 gets no register. The routing scaffold now also keeps only the first 120 inputs of each unit, so a Global System Interrupt past them belongs to no unit and no route reaches it. The inputs left out are counted once, when the scaffold runs, rather than per route asked: `ioapic_inputs_unreachable` in the `[LPLTLM] machine` record. ## Verify - `an_io_apic_keeps_the_inputs_its_index_reaches` was red on a stub that kept every declared input: 121 and 256 stayed whole. - Server: q35 861/0, `-cpu max` 863/0, pc 855/0. Satellite: q35 711/0. Each record says `ioapic_inputs_unreachable=0`. - Two of three mutants fail the test: the limit one entry further, and counting every declared input. Keeping every declared input in the unit survives, since QEMU's I/O APIC declares 24. - No budget's window holds the scaffold. ## Not in it - A run with more than 120 declared inputs: QEMU has none, so the ID register keeping its value is read in the code, not seen. Closes #572
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
A PCI function without MSI now interrupts through its I/O APIC line:
input_output_advanced_programmable_interrupt_controller_route_devicetakes the line the published_PRTroute gives the function's pin (§3.11). It programs the route's trigger and polarity, on a device vector, to the slot it is given. The entry stays masked while it is written. A line held by an ISA route, by an ISA IRQ with an 8259 handler, or by another vector is refused. No entry past 0x77, the last an 8-bit index reaches, is written.\_PIC (1)that fails.Specification: said, says, because
Step 5 said the APIC model waits until "no PCI interrupt goes through the 8259 (#59)". It now states the rule, because on bare metal x2APIC no device interrupt reaches a core without remapping.
Verify
Satellite,
intel-hda,msi=off, each line computed apart:acpiexec_routing.pyon QEMU's DSDTpci_irqs[3]for LNKD-cpu max,-hypervisorThe line fires 4 times on slot 0 and can be moved to slot 1 and back. Every boot ends with 0 fail:
ring0 quickandring0 qemu: 9 of 9 and 4 of 4 agree with the host.There are 13 mutants, and each one fails its test.
Budgets: W2 goes from 73,251 to 76,303, the cost of
\_PIC (1). W5 goes from 518,853 to 354,430. W1 and the tick are unchanged.Not in it
Stacked on #798. Refs #59