[GH-ISSUE #39] drvAHCI: IRQ interrupt storm hangs the kernel after the port scan #33

Closed
opened 2026-11-03 22:02:38 -07:00 by gitea-mirror · 1 comment
Owner

Originally created by @raynorpat on GitHub (Oct 6, 2026).
Original GitHub issue: https://github.com/RhapsodiOS/RhapsodiOS/issues/39

Found while booting the phase 6 installer ISO from an AHCI CD-ROM (QEMU -device ahci + ide-cd, i440FX and q35).

Background. Until d231848fb (branch install-media-p6, "kernel: keep the page offset when mapping device memory") the AHCI driver never reached the controller: i386 pages are 8 KB and _KernBusMemoryCreateMapping dropped the offset within the page, so QEMU's ABAR at 0xfebf1000 mapped to 0xfebf0000 (the cirrus BAR) and the driver printed AHCI: HBA reset and initialisation failed. With that fixed, the driver reads the real registers (CAP 0xc0141f05, PI 0x3f) and logs AHCI: port 0 ATAPI device detected, ports 1–5 empty.

Symptom. Right after the port scan the kernel hangs (no root mount; the screen doesn't change from 60 s to 180 s).

Evidence (QEMU monitor and -trace ahci_*):

  • The driver sets GHC.IE while the global IS still has port 0's bit set; QEMU raises INTx once (ahci_irq_raise).
  • QEMU then counts about 16 million interrupts on IRQ 2 (the cascade) against 1 on IRQ 11.
  • The slave PIC shows irr=08 isr=08 imr=b7 elcr=0c: IRQ 11 is level-triggered, in service and unmasked.
  • Every EIP sample lands in _intr_handler (+0x9d, +0x10d, +0x187, +0x196) or _int_0xFF.

So the kernel keeps re-taking a level IRQ 11 that is never masked or cleared, and the driver's -interruptOccurred (which runs in the I/O thread and clears IS) never runs. NE2K on the same IRQ 11 works, which points at drvAHCI's interrupt setup rather than DriverKit's level-IRQ handling.

Likely causes to check.

  • GHC.IE enabled before the pending port status (PxIS / IS) is cleared during init.
  • A custom interrupt handler or level/edge setting in drvAHCI that leaves the line asserted or unmasked.

Impact. Machines whose disk or CD is on AHCI hang at boot. AHCI CD boot is out of phase 6's gates (IDE CD-ROM only) until this is fixed. Installed disks on IDE are unaffected.

Code: src/drivers-i386/ide/drvAHCI (AHCIATAPI.m and the controller init), src/kernel-7/driverkit interrupt dispatch.

Originally created by @raynorpat on GitHub (Oct 6, 2026). Original GitHub issue: https://github.com/RhapsodiOS/RhapsodiOS/issues/39 Found while booting the phase 6 installer ISO from an AHCI CD-ROM (QEMU `-device ahci` + `ide-cd`, i440FX and q35). **Background.** Until `d231848fb` (branch `install-media-p6`, "kernel: keep the page offset when mapping device memory") the AHCI driver never reached the controller: i386 pages are 8 KB and `_KernBusMemoryCreateMapping` dropped the offset within the page, so QEMU's ABAR at `0xfebf1000` mapped to `0xfebf0000` (the cirrus BAR) and the driver printed `AHCI: HBA reset and initialisation failed.` With that fixed, the driver reads the real registers (CAP `0xc0141f05`, PI `0x3f`) and logs `AHCI: port 0 ATAPI device detected`, ports 1–5 empty. **Symptom.** Right after the port scan the kernel hangs (no root mount; the screen doesn't change from 60 s to 180 s). **Evidence** (QEMU monitor and `-trace ahci_*`): - The driver sets `GHC.IE` while the global IS still has port 0's bit set; QEMU raises INTx once (`ahci_irq_raise`). - QEMU then counts about 16 million interrupts on IRQ 2 (the cascade) against 1 on IRQ 11. - The slave PIC shows `irr=08 isr=08 imr=b7 elcr=0c`: IRQ 11 is level-triggered, in service and unmasked. - Every EIP sample lands in `_intr_handler` (+0x9d, +0x10d, +0x187, +0x196) or `_int_0xFF`. So the kernel keeps re-taking a level IRQ 11 that is never masked or cleared, and the driver's `-interruptOccurred` (which runs in the I/O thread and clears IS) never runs. NE2K on the same IRQ 11 works, which points at drvAHCI's interrupt setup rather than DriverKit's level-IRQ handling. **Likely causes to check.** - `GHC.IE` enabled before the pending port status (PxIS / IS) is cleared during init. - A custom interrupt handler or level/edge setting in drvAHCI that leaves the line asserted or unmasked. **Impact.** Machines whose disk or CD is on AHCI hang at boot. AHCI CD boot is out of phase 6's gates (IDE CD-ROM only) until this is fixed. Installed disks on IDE are unaffected. Code: `src/drivers-i386/ide/drvAHCI` (`AHCIATAPI.m` and the controller init), `src/kernel-7/driverkit` interrupt dispatch.
Author
Owner

@raynorpat commented on GitHub (Oct 7, 2026):

Fixed on branch install-media-p6 in commit 55e8df871 (not pushed yet, so the commit isn't visible on the remote until that branch is).

Root cause: drvAHCI's tables had lost "Share IRQ Levels" = "YES" (dropped in 95af26861). Without it the kernel uses the plain dispatch in KernBusInterrupt.m, which leaves the PIC line unmasked after the handler returns. Level-triggered IRQ 10/11 with a pending AHCI status then re-fires before the I/O thread can run -interruptOccurred. With the key, the shared dispatch masks the line until -enableAllInterrupts resumes it. The fix restores the key in Default.table, Instance0.table and Instance1.table. No kernel or driver code change.

Verified on QEMU q35 with the page-offset mapping fix, root on AHCI:

  • Before: IRQ 2 (cascade) counted about 31 million interrupts against 1 on the AHCI line, and boot hung after the port scan.
  • After: root mounts from hd0a, and IRQ 10 and IRQ 2 each stay in the low thousands.
  • Two controllers on the same IRQ line both attach and serve interrupts (one reads a disk label, the other boots root). The claim in 95af26861 that sharing breaks both controllers did not hold.
  • An ATAPI CD on AHCI port 2 (the configuration in this issue) is detected and boot proceeds with no storm.

Not covered: drivers that use the C IOEnableInterrupt(identity) API with this key (VMXNet, IntelAC97, SB128) were not tested.

<!-- gh-comment-id:6032296068 --> @raynorpat commented on GitHub (Oct 7, 2026): Fixed on branch install-media-p6 in commit 55e8df871 (not pushed yet, so the commit isn't visible on the remote until that branch is). Root cause: drvAHCI's tables had lost `"Share IRQ Levels" = "YES"` (dropped in 95af26861). Without it the kernel uses the plain dispatch in KernBusInterrupt.m, which leaves the PIC line unmasked after the handler returns. Level-triggered IRQ 10/11 with a pending AHCI status then re-fires before the I/O thread can run -interruptOccurred. With the key, the shared dispatch masks the line until -enableAllInterrupts resumes it. The fix restores the key in Default.table, Instance0.table and Instance1.table. No kernel or driver code change. Verified on QEMU q35 with the page-offset mapping fix, root on AHCI: - Before: IRQ 2 (cascade) counted about 31 million interrupts against 1 on the AHCI line, and boot hung after the port scan. - After: root mounts from hd0a, and IRQ 10 and IRQ 2 each stay in the low thousands. - Two controllers on the same IRQ line both attach and serve interrupts (one reads a disk label, the other boots root). The claim in 95af26861 that sharing breaks both controllers did not hold. - An ATAPI CD on AHCI port 2 (the configuration in this issue) is detected and boot proceeds with no storm. Not covered: drivers that use the C IOEnableInterrupt(identity) API with this key (VMXNet, IntelAC97, SB128) were not tested.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
github-starred/RhapsodiOS#33
No description provided.