From 3be5c6c8ee43a4c84e3a071831cbf87cc343f585 Mon Sep 17 00:00:00 2001 From: salmonik Date: Sat, 1 Aug 2026 01:42:25 +0200 Subject: ahci: skip ATAPI ports and fast-fail on device abort MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A CD-ROM (or any ATAPI device) on a scanned port previously made the driver spin the full 100M-iteration timeout on IDENTIFY, then log a scary 'timed out' (QEMU leaves ATAPI aborts pending in PxCI instead of completing the slot; tfd=0x441 = ABRT). - skip a port up-front when firmware set PxCMD.ATAPI (bit 24) - bail out of the completion poll as soon as PxTFD.ERR is set, clear the error bits and any wedged slot, and report the abort instead of timing out — the port scan stays fast with a CD attached --- rsnix/src/ahci.rs | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) (limited to 'rsnix') diff --git a/rsnix/src/ahci.rs b/rsnix/src/ahci.rs index ea1a886..2efe3b4 100644 --- a/rsnix/src/ahci.rs +++ b/rsnix/src/ahci.rs @@ -165,6 +165,13 @@ fn start_port(abar: usize, port_idx: u32) -> Option { wreg(port + P_CMD, CMD_FRE | CMD_SUD | CMD_DMAE); wreg(port + P_CMD, CMD_ST | CMD_FRE | CMD_SUD | CMD_DMAE); + // ATAPI (optical) devices reject ATA IDENTIFY — skip them so the + // scan doesn't waste a timeout on a CD-ROM. + if reg(port + P_CMD) & (1 << 24) != 0 { + crate::serial_println!("ahci: port {}: ATAPI (optical) device, skipping", port_idx); + return None; + } + let mut d = Disk { port, clb, @@ -175,7 +182,7 @@ fn start_port(abar: usize, port_idx: u32) -> Option { }; // IDENTIFY DEVICE as a sanity check + to learn the capacity. if issue(&d, ATA_IDENTIFY, 0, 1, false).is_err() { - crate::serial_println!("ahci: IDENTIFY failed on port {}", port_idx); + crate::serial_println!("ahci: IDENTIFY failed on port {} (not an ATA disk?)", port_idx); return None; } let id = unsafe { core::slice::from_raw_parts(d.buf as *const u16, 256) }; @@ -333,13 +340,20 @@ fn issue(d: &Disk, cmd: u8, lba: u64, count: u32, write: bool) -> Result<(), ()> *hdr.add(3) = 0; } - // Issue and poll. + // Issue and poll. Bail out as soon as the device reports an error + // (QEMU leaves ATAPI aborts pending in PxCI rather than completing + // the slot, so we must not wait for the timeout on those). wreg(d.port + P_CI, 1); let mut i = 0; while reg(d.port + P_CI) & 1 != 0 && i < 100_000_000 { + if reg(d.port + P_TFD) & 1 != 0 { + break; + } i += 1; } - if reg(d.port + P_CI) & 1 != 0 { + let tfd = reg(d.port + P_TFD); + let tfd_err = tfd & 1 != 0; + if reg(d.port + P_CI) & 1 != 0 && !tfd_err { crate::serial_println!( "ahci: command {:#x} lba={} cnt={} timed out (tfd={:#x} ci={:#x} cmd={:#x} prdbc={:#x})", cmd, @@ -352,9 +366,12 @@ fn issue(d: &Disk, cmd: u8, lba: u64, count: u32, write: bool) -> Result<(), ()> ); return Err(()); } - // Error? Clear any port error bits either way. - if reg(d.port + P_TFD) & 1 != 0 { + if tfd_err { + // Clear the error bits and any wedged slot so the port stays usable. wreg(d.port + 0x30, 0xFFFF_FFFF); // PxSERR: clear + if reg(d.port + P_CI) & 1 != 0 { + wreg(d.port + P_CI, 1); + } crate::serial_println!( "ahci: command {:#x} error (tfd={:#x})", cmd, -- cgit v1.2.3