summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-08-01 01:42:25 +0200
committersalmonik <salmonik@tuta.io>2026-08-01 01:42:25 +0200
commit3be5c6c8ee43a4c84e3a071831cbf87cc343f585 (patch)
tree884071bc751b9f8b26ffa9a10140d635dcc4cbde
parentahci: scan all ports; fat: mount first valid FAT32 disk (diff)
downloadrs-dos-3be5c6c8ee43a4c84e3a071831cbf87cc343f585.tar.gz
rs-dos-3be5c6c8ee43a4c84e3a071831cbf87cc343f585.zip
ahci: skip ATAPI ports and fast-fail on device abort
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
Diffstat (limited to '')
-rw-r--r--rsnix/src/ahci.rs27
1 files changed, 22 insertions, 5 deletions
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<Disk> {
165 wreg(port + P_CMD, CMD_FRE | CMD_SUD | CMD_DMAE); 165 wreg(port + P_CMD, CMD_FRE | CMD_SUD | CMD_DMAE);
166 wreg(port + P_CMD, CMD_ST | CMD_FRE | CMD_SUD | CMD_DMAE); 166 wreg(port + P_CMD, CMD_ST | CMD_FRE | CMD_SUD | CMD_DMAE);
167 167
168 // ATAPI (optical) devices reject ATA IDENTIFY — skip them so the
169 // scan doesn't waste a timeout on a CD-ROM.
170 if reg(port + P_CMD) & (1 << 24) != 0 {
171 crate::serial_println!("ahci: port {}: ATAPI (optical) device, skipping", port_idx);
172 return None;
173 }
174
168 let mut d = Disk { 175 let mut d = Disk {
169 port, 176 port,
170 clb, 177 clb,
@@ -175,7 +182,7 @@ fn start_port(abar: usize, port_idx: u32) -> Option<Disk> {
175 }; 182 };
176 // IDENTIFY DEVICE as a sanity check + to learn the capacity. 183 // IDENTIFY DEVICE as a sanity check + to learn the capacity.
177 if issue(&d, ATA_IDENTIFY, 0, 1, false).is_err() { 184 if issue(&d, ATA_IDENTIFY, 0, 1, false).is_err() {
178 crate::serial_println!("ahci: IDENTIFY failed on port {}", port_idx); 185 crate::serial_println!("ahci: IDENTIFY failed on port {} (not an ATA disk?)", port_idx);
179 return None; 186 return None;
180 } 187 }
181 let id = unsafe { core::slice::from_raw_parts(d.buf as *const u16, 256) }; 188 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<(), ()>
333 *hdr.add(3) = 0; 340 *hdr.add(3) = 0;
334 } 341 }
335 342
336 // Issue and poll. 343 // Issue and poll. Bail out as soon as the device reports an error
344 // (QEMU leaves ATAPI aborts pending in PxCI rather than completing
345 // the slot, so we must not wait for the timeout on those).
337 wreg(d.port + P_CI, 1); 346 wreg(d.port + P_CI, 1);
338 let mut i = 0; 347 let mut i = 0;
339 while reg(d.port + P_CI) & 1 != 0 && i < 100_000_000 { 348 while reg(d.port + P_CI) & 1 != 0 && i < 100_000_000 {
349 if reg(d.port + P_TFD) & 1 != 0 {
350 break;
351 }
340 i += 1; 352 i += 1;
341 } 353 }
342 if reg(d.port + P_CI) & 1 != 0 { 354 let tfd = reg(d.port + P_TFD);
355 let tfd_err = tfd & 1 != 0;
356 if reg(d.port + P_CI) & 1 != 0 && !tfd_err {
343 crate::serial_println!( 357 crate::serial_println!(
344 "ahci: command {:#x} lba={} cnt={} timed out (tfd={:#x} ci={:#x} cmd={:#x} prdbc={:#x})", 358 "ahci: command {:#x} lba={} cnt={} timed out (tfd={:#x} ci={:#x} cmd={:#x} prdbc={:#x})",
345 cmd, 359 cmd,
@@ -352,9 +366,12 @@ fn issue(d: &Disk, cmd: u8, lba: u64, count: u32, write: bool) -> Result<(), ()>
352 ); 366 );
353 return Err(()); 367 return Err(());
354 } 368 }
355 // Error? Clear any port error bits either way. 369 if tfd_err {
356 if reg(d.port + P_TFD) & 1 != 0 { 370 // Clear the error bits and any wedged slot so the port stays usable.
357 wreg(d.port + 0x30, 0xFFFF_FFFF); // PxSERR: clear 371 wreg(d.port + 0x30, 0xFFFF_FFFF); // PxSERR: clear
372 if reg(d.port + P_CI) & 1 != 0 {
373 wreg(d.port + P_CI, 1);
374 }
358 crate::serial_println!( 375 crate::serial_println!(
359 "ahci: command {:#x} error (tfd={:#x})", 376 "ahci: command {:#x} error (tfd={:#x})",
360 cmd, 377 cmd,