1use crate::{
26 dma::DmaBuffer,
27 hardware::pci_client::{self as pci, ProbeCriteria},
28 memory,
29 memory::{phys_to_virt, UserSliceRead, UserSliceWrite},
30 sync::{SpinLock, WaitQueue},
31};
32use alloc::{boxed::Box, vec::Vec};
33use core::{
34 ptr,
35 sync::atomic::{AtomicBool, AtomicPtr, AtomicU32, AtomicU64, AtomicU8, Ordering},
36};
37
38pub use super::virtio_block::{BlockDevice, BlockError, SECTOR_SIZE};
39
40const HBA_GHC: u64 = 0x04;
42const HBA_IS: u64 = 0x08;
43const HBA_PI: u64 = 0x0C;
44
45const GHC_AE: u32 = 1 << 31; const GHC_IE: u32 = 1 << 1; const GHC_HR: u32 = 1 << 0; const PORT_CLB: u64 = 0x00;
51const PORT_CLBU: u64 = 0x04;
52const PORT_FB: u64 = 0x08;
53const PORT_FBU: u64 = 0x0C;
54const PORT_IS: u64 = 0x10;
55const PORT_IE: u64 = 0x14; const PORT_CMD: u64 = 0x18;
57const PORT_TFD: u64 = 0x20;
58const PORT_SIG: u64 = 0x24;
59const PORT_SSTS: u64 = 0x28;
60const PORT_SERR: u64 = 0x30;
61const PORT_CI: u64 = 0x38;
62
63const CMD_ST: u32 = 1 << 0; const CMD_FRE: u32 = 1 << 4; const CMD_FR: u32 = 1 << 14; const CMD_CR: u32 = 1 << 15; const TFD_BSY: u32 = 1 << 7;
69const TFD_DRQ: u32 = 1 << 3;
70
71const SSTS_DET_COMM: u32 = 3;
72const SSTS_DET_MASK: u32 = 0xF;
73
74const SIG_SATA: u32 = 0x0000_0101;
75
76const PXIE_DHRE: u32 = 1 << 0; const PXIE_TFEE: u32 = 1 << 30; const CLB_OFF: u64 = 0x000; const FB_OFF: u64 = 0x400; const CTAB_OFF: u64 = 0x500; const CMDH_FLAGS: usize = 0; const CMDH_PRDTL: usize = 2; const CMDH_CTBA: usize = 8; const CMDH_CTBAU: usize = 12; const CTAB_CFIS: usize = 0x00; const CTAB_PRDT: usize = 0x80; const FIS_TYPE: usize = 0;
97const FIS_FLAGS: usize = 1; const FIS_CMD: usize = 2;
99const FIS_LBA0: usize = 4;
100const FIS_LBA1: usize = 5;
101const FIS_LBA2: usize = 6;
102const FIS_DEVICE: usize = 7;
103const FIS_LBA3: usize = 8;
104const FIS_LBA4: usize = 9;
105const FIS_LBA5: usize = 10;
106const FIS_CNT_LO: usize = 12;
107const FIS_CNT_HI: usize = 13;
108
109const FIS_TYPE_H2D: u8 = 0x27;
110const FIS_C_BIT: u8 = 0x80; const FIS_LBA_MODE: u8 = 1 << 6;
112
113const ATA_IDENTIFY: u8 = 0xEC;
115const ATA_READ_DMA_EXT: u8 = 0x25;
116const ATA_WRITE_DMA_EXT: u8 = 0x35;
117
118const EFAULT: i32 = -14;
119
120const PXIS_TFES: u32 = 1 << 30;
122
123#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
126pub enum AhciError {
127 #[error("no AHCI controller on PCI bus")]
128 NoController,
129 #[error("invalid BAR5 (ABAR)")]
130 BadAbar,
131 #[error("physical memory allocation failed")]
132 Alloc,
133 #[error("port BSY/DRQ set")]
134 Busy,
135 #[error("command timed out")]
136 Timeout,
137 #[error("device reported task-file error")]
138 DeviceError,
139 #[error("invalid sector number")]
140 InvalidSector,
141 #[error("buffer too small (need ≥ SECTOR_SIZE bytes)")]
142 BufferTooSmall,
143 #[error("invalid userspace buffer")]
144 InvalidUserBuffer,
145 #[error("no usable SATA port found")]
146 NoPort,
147}
148
149struct AhciPort {
152 port_num: u8,
153 port_virt: u64, mem_phys: u64, mem_virt: u64, sector_count: u64,
157}
158
159pub struct AhciController {
162 #[allow(dead_code)]
163 abar_virt: u64,
164 ports: Vec<AhciPort>,
165}
166
167unsafe impl Send for AhciController {}
169unsafe impl Sync for AhciController {}
170
171static AHCI_ABAR_VIRT: AtomicU64 = AtomicU64::new(0);
177
178pub static AHCI_IRQ_LINE: AtomicU8 = AtomicU8::new(0xFF);
180
181static PORT_VIRT: [AtomicU64; 32] = {
183 const INIT: AtomicU64 = AtomicU64::new(0);
184 [INIT; 32]
185};
186
187static PORT_SLOT0_DONE: [AtomicBool; 32] = {
189 const INIT: AtomicBool = AtomicBool::new(false);
190 [INIT; 32]
191};
192
193static PORT_SLOT0_ERROR: [AtomicBool; 32] = {
195 const INIT: AtomicBool = AtomicBool::new(false);
196 [INIT; 32]
197};
198
199static PORT_WQ: [WaitQueue; 32] = {
201 const INIT: WaitQueue = WaitQueue::new();
202 [INIT; 32]
203};
204
205static PORT_ASYNC_RING_ID: [AtomicU64; 32] = {
217 const INIT: AtomicU64 = AtomicU64::new(0);
218 [INIT; 32]
219};
220
221static PORT_ASYNC_USER_DATA: [AtomicU64; 32] = {
223 const INIT: AtomicU64 = AtomicU64::new(0);
224 [INIT; 32]
225};
226
227static PORT_ASYNC_BUF_VADDR: [AtomicU64; 32] = {
229 const INIT: AtomicU64 = AtomicU64::new(0);
230 [INIT; 32]
231};
232
233static PORT_ASYNC_LEN: [AtomicU32; 32] = {
235 const INIT: AtomicU32 = AtomicU32::new(0);
236 [INIT; 32]
237};
238
239static PORT_ASYNC_IS_WRITE: [AtomicBool; 32] = {
241 const INIT: AtomicBool = AtomicBool::new(false);
242 [INIT; 32]
243};
244
245static PORT_ASYNC_DMA_PTR: [AtomicU64; 32] = {
249 const INIT: AtomicU64 = AtomicU64::new(0);
250 [INIT; 32]
251};
252
253static PORT_ASYNC_ACTIVE: [AtomicBool; 32] = {
255 const INIT: AtomicBool = AtomicBool::new(false);
256 [INIT; 32]
257};
258
259struct PendingAsyncReadCompletion {
260 ring_id: u64,
261 user_data: u64,
262 user_buf_vaddr: u64,
263 dma_buf: DmaBuffer,
264 len: usize,
265 result: i32,
266}
267
268static PENDING_ASYNC_READ_COMPLETIONS: SpinLock<Vec<PendingAsyncReadCompletion>> =
269 SpinLock::new(Vec::new());
270
271#[inline]
275unsafe fn rd32(base: u64, off: u64) -> u32 {
276 ptr::read_volatile((base + off) as *const u32)
277}
278
279#[inline]
281unsafe fn wr32(base: u64, off: u64, val: u32) {
282 ptr::write_volatile((base + off) as *mut u32, val);
283}
284
285fn port_stop(pvirt: u64) {
289 unsafe {
291 let mut cmd = rd32(pvirt, PORT_CMD);
292 cmd &= !(CMD_ST | CMD_FRE);
293 wr32(pvirt, PORT_CMD, cmd);
294 for _ in 0..500_000u32 {
296 if rd32(pvirt, PORT_CMD) & (CMD_FR | CMD_CR) == 0 {
297 return;
298 }
299 core::hint::spin_loop();
300 }
301 log::warn!("AHCI: port stop timed out (port registers @ {:#x})", pvirt);
302 }
303}
304
305fn port_start(pvirt: u64) {
307 unsafe {
309 let mut timeout = 500_000u32;
311 while rd32(pvirt, PORT_CMD) & CMD_CR != 0 {
312 timeout = timeout.saturating_sub(1);
313 if timeout == 0 {
314 log::warn!("AHCI: port start wait for CR clear timed out");
315 break;
316 }
317 core::hint::spin_loop();
318 }
319 let mut cmd = rd32(pvirt, PORT_CMD);
320 cmd |= CMD_FRE | CMD_ST;
321 wr32(pvirt, PORT_CMD, cmd);
322 }
323}
324
325fn port_rebase(pvirt: u64, phys: u64) {
327 port_stop(pvirt);
328 unsafe {
330 let clb = phys + CLB_OFF;
331 let fb = phys + FB_OFF;
332 wr32(pvirt, PORT_CLB, (clb & 0xFFFF_FFFF) as u32);
333 wr32(pvirt, PORT_CLBU, (clb >> 32) as u32);
334 wr32(pvirt, PORT_FB, (fb & 0xFFFF_FFFF) as u32);
335 wr32(pvirt, PORT_FBU, (fb >> 32) as u32);
336 wr32(pvirt, PORT_IS, 0xFFFF_FFFF);
338 wr32(pvirt, PORT_SERR, 0xFFFF_FFFF);
339 }
340 port_start(pvirt);
341}
342
343fn port_enable_irq(pvirt: u64) {
345 unsafe {
347 wr32(pvirt, PORT_IE, PXIE_DHRE | PXIE_TFEE);
348 }
349}
350
351fn submit_cmd(
367 port: &AhciPort,
368 lba: u64,
369 count: u16,
370 buf: &mut [u8],
371 write: bool,
372 ata_cmd: u8,
373) -> Result<(), AhciError> {
374 let nbytes = (count as usize) * SECTOR_SIZE;
375 if buf.len() < nbytes {
376 return Err(AhciError::BufferTooSmall);
377 }
378
379 let tfd = unsafe { rd32(port.port_virt, PORT_TFD) };
381 if tfd & (TFD_BSY | TFD_DRQ) != 0 {
382 return Err(AhciError::Busy);
383 }
384
385 let dma_buf = DmaBuffer::alloc(nbytes).map_err(|_| AhciError::Alloc)?;
386
387 if write {
388 unsafe {
390 ptr::copy_nonoverlapping(buf.as_ptr(), dma_buf.virt_addr() as *mut u8, nbytes);
391 }
392 }
393
394 let ctab_phys = port.mem_phys + CTAB_OFF;
395 let cmdh_virt = port.mem_virt + CLB_OFF; let ctab_virt = port.mem_virt + CTAB_OFF;
397
398 unsafe {
400 let h = cmdh_virt as *mut u8;
402 ptr::write_bytes(h, 0, 32);
403
404 let flags: u16 = 5u16 | (if write { 1 << 6 } else { 0 });
406 ptr::write_unaligned(h.add(CMDH_FLAGS) as *mut u16, flags.to_le());
407 ptr::write_unaligned(h.add(CMDH_PRDTL) as *mut u16, 1u16.to_le()); ptr::write_unaligned(
409 h.add(CMDH_CTBA) as *mut u32,
410 (ctab_phys & 0xFFFF_FFFF) as u32,
411 );
412 ptr::write_unaligned(h.add(CMDH_CTBAU) as *mut u32, (ctab_phys >> 32) as u32);
413
414 let t = ctab_virt as *mut u8;
416 ptr::write_bytes(t, 0, CTAB_PRDT + 16);
417
418 let f = t.add(CTAB_CFIS);
420 *f.add(FIS_TYPE) = FIS_TYPE_H2D;
421 *f.add(FIS_FLAGS) = FIS_C_BIT;
422 *f.add(FIS_CMD) = ata_cmd;
423 *f.add(FIS_LBA0) = (lba & 0xFF) as u8;
424 *f.add(FIS_LBA1) = ((lba >> 8) & 0xFF) as u8;
425 *f.add(FIS_LBA2) = ((lba >> 16) & 0xFF) as u8;
426 *f.add(FIS_DEVICE) = FIS_LBA_MODE; *f.add(FIS_LBA3) = ((lba >> 24) & 0xFF) as u8;
428 *f.add(FIS_LBA4) = ((lba >> 32) & 0xFF) as u8;
429 *f.add(FIS_LBA5) = ((lba >> 40) & 0xFF) as u8;
430 *f.add(FIS_CNT_LO) = (count & 0xFF) as u8;
431 *f.add(FIS_CNT_HI) = (count >> 8) as u8;
432
433 let p = t.add(CTAB_PRDT);
435 let dma_phys = dma_buf.dma_addr().as_u64();
437 ptr::write_unaligned(p.add(0) as *mut u32, (dma_phys & 0xFFFF_FFFF) as u32);
438 ptr::write_unaligned(p.add(4) as *mut u32, (dma_phys >> 32) as u32);
439 ptr::write_unaligned(p.add(8) as *mut u32, 0u32);
440 let dbc = ((nbytes as u32).saturating_sub(1)) | (1 << 31);
442 ptr::write_unaligned(p.add(12) as *mut u32, dbc);
443 }
444
445 let idx = port.port_num as usize;
446
447 PORT_SLOT0_DONE[idx].store(false, Ordering::Release);
449 PORT_SLOT0_ERROR[idx].store(false, Ordering::Release);
450
451 unsafe { wr32(port.port_virt, PORT_CI, 1) };
454
455 if crate::process::current_task_id().is_some() {
457 PORT_WQ[idx].wait_until(|| {
462 if PORT_SLOT0_DONE[idx].load(Ordering::Acquire) {
463 PORT_SLOT0_DONE[idx].store(false, Ordering::Release);
465 Some(())
466 } else {
467 None
468 }
469 });
470
471 if PORT_SLOT0_ERROR[idx].load(Ordering::Acquire) {
473 return Err(AhciError::DeviceError);
474 }
475 } else {
476 let mut tries = 5_000_000u32;
478 loop {
479 let ci = unsafe { rd32(port.port_virt, PORT_CI) };
481 let is = unsafe { rd32(port.port_virt, PORT_IS) };
482
483 if is & PXIS_TFES != 0 {
484 unsafe {
486 wr32(port.port_virt, PORT_IS, 0xFFFF_FFFF);
487 wr32(port.port_virt, PORT_SERR, 0xFFFF_FFFF);
488 }
489 return Err(AhciError::DeviceError);
490 }
491
492 if ci & 1 == 0 {
493 break; }
495
496 tries = tries.saturating_sub(1);
497 if tries == 0 {
498 return Err(AhciError::Timeout);
499 }
500 core::hint::spin_loop();
501 }
502
503 unsafe { wr32(port.port_virt, PORT_IS, 0xFFFF_FFFF) };
505 }
506
507 if !write {
508 unsafe {
510 ptr::copy_nonoverlapping(dma_buf.virt_addr() as *const u8, buf.as_mut_ptr(), nbytes);
511 }
512 }
513 Ok(())
514}
515
516fn submit_async_cmd(
531 port: &AhciPort,
532 lba: u64,
533 count: u16,
534 write: bool,
535 ata_cmd: u8,
536 dma_buf: DmaBuffer,
537) -> Result<(), AhciError> {
538 let nbytes = (count as usize) * SECTOR_SIZE;
539 let idx = port.port_num as usize;
540
541 let tfd = unsafe { rd32(port.port_virt, PORT_TFD) };
543 if tfd & (TFD_BSY | TFD_DRQ) != 0 {
544 return Err(AhciError::Busy);
545 }
546
547 if write {
549 let user_vaddr = PORT_ASYNC_BUF_VADDR[idx].load(Ordering::Acquire);
550 if user_vaddr != 0 {
551 unsafe {
554 ptr::copy_nonoverlapping(
555 user_vaddr as *const u8,
556 dma_buf.virt_addr() as *mut u8,
557 nbytes,
558 );
559 }
560 }
561 }
562
563 let ctab_phys = port.mem_phys + CTAB_OFF;
564 let cmdh_virt = port.mem_virt + CLB_OFF;
565 let ctab_virt = port.mem_virt + CTAB_OFF;
566
567 unsafe {
569 let h = cmdh_virt as *mut u8;
571 ptr::write_bytes(h, 0, 32);
572
573 let flags: u16 = 5u16 | (if write { 1 << 6 } else { 0 });
574 ptr::write_unaligned(h.add(CMDH_FLAGS) as *mut u16, flags.to_le());
575 ptr::write_unaligned(h.add(CMDH_PRDTL) as *mut u16, 1u16.to_le());
576 ptr::write_unaligned(
577 h.add(CMDH_CTBA) as *mut u32,
578 (ctab_phys & 0xFFFF_FFFF) as u32,
579 );
580 ptr::write_unaligned(h.add(CMDH_CTBAU) as *mut u32, (ctab_phys >> 32) as u32);
581
582 let t = ctab_virt as *mut u8;
584 ptr::write_bytes(t, 0, CTAB_PRDT + 16);
585
586 let f = t.add(CTAB_CFIS);
588 *f.add(FIS_TYPE) = FIS_TYPE_H2D;
589 *f.add(FIS_FLAGS) = FIS_C_BIT;
590 *f.add(FIS_CMD) = ata_cmd;
591 *f.add(FIS_LBA0) = (lba & 0xFF) as u8;
592 *f.add(FIS_LBA1) = ((lba >> 8) & 0xFF) as u8;
593 *f.add(FIS_LBA2) = ((lba >> 16) & 0xFF) as u8;
594 *f.add(FIS_DEVICE) = FIS_LBA_MODE;
595 *f.add(FIS_LBA3) = ((lba >> 24) & 0xFF) as u8;
596 *f.add(FIS_LBA4) = ((lba >> 32) & 0xFF) as u8;
597 *f.add(FIS_LBA5) = ((lba >> 40) & 0xFF) as u8;
598 *f.add(FIS_CNT_LO) = (count & 0xFF) as u8;
599 *f.add(FIS_CNT_HI) = (count >> 8) as u8;
600
601 let p = t.add(CTAB_PRDT);
603 let dma_phys = dma_buf.dma_addr().as_u64();
604 ptr::write_unaligned(p.add(0) as *mut u32, (dma_phys & 0xFFFF_FFFF) as u32);
605 ptr::write_unaligned(p.add(4) as *mut u32, (dma_phys >> 32) as u32);
606 ptr::write_unaligned(p.add(8) as *mut u32, 0u32);
607 let dbc = ((nbytes as u32).saturating_sub(1)) | (1 << 31);
608 ptr::write_unaligned(p.add(12) as *mut u32, dbc);
609 }
610
611 let dma_ptr = Box::into_raw(Box::new(dma_buf));
613 PORT_ASYNC_DMA_PTR[idx].store(dma_ptr as u64, Ordering::Release);
614
615 PORT_SLOT0_DONE[idx].store(false, Ordering::Release);
617 PORT_SLOT0_ERROR[idx].store(false, Ordering::Release);
618
619 unsafe { wr32(port.port_virt, PORT_CI, 1) };
621
622 Ok(())
623}
624
625#[allow(dead_code)]
633pub(crate) fn submit_async_storage_op(
634 port_idx: u8,
635 lba: u64,
636 byte_count: u32,
637 user_buf_vaddr: u64,
638 write: bool,
639 ring_id: u64,
640 user_data: u64,
641) -> Result<(), AhciError> {
642 let controller = get_device().ok_or(AhciError::NoController)?;
643 let port = controller.port_by_num(port_idx).ok_or(AhciError::NoPort)?;
644
645 if lba >= port.sector_count {
646 return Err(AhciError::InvalidSector);
647 }
648
649 let count = ((byte_count as usize + SECTOR_SIZE - 1) / SECTOR_SIZE) as u16;
650 if count == 0 {
651 return Err(AhciError::BufferTooSmall);
652 }
653 let nbytes = (count as usize) * SECTOR_SIZE;
654
655 if write {
656 UserSliceRead::new(user_buf_vaddr, nbytes).map_err(|_| AhciError::InvalidUserBuffer)?;
657 } else {
658 UserSliceWrite::new(user_buf_vaddr, nbytes).map_err(|_| AhciError::InvalidUserBuffer)?;
659 }
660
661 let dma_buf = DmaBuffer::alloc(nbytes).map_err(|_| AhciError::Alloc)?;
662
663 let idx = port.port_num as usize;
664 let ata_cmd = if write {
665 ATA_WRITE_DMA_EXT
666 } else {
667 ATA_READ_DMA_EXT
668 };
669
670 PORT_ASYNC_RING_ID[idx].store(ring_id, Ordering::Release);
672 PORT_ASYNC_USER_DATA[idx].store(user_data, Ordering::Release);
673 PORT_ASYNC_BUF_VADDR[idx].store(user_buf_vaddr, Ordering::Release);
674 PORT_ASYNC_LEN[idx].store(nbytes as u32, Ordering::Release);
675 PORT_ASYNC_IS_WRITE[idx].store(write, Ordering::Release);
676 PORT_ASYNC_ACTIVE[idx].store(true, Ordering::Release);
677
678 match submit_async_cmd(port, lba, count, write, ata_cmd, dma_buf) {
679 Ok(()) => Ok(()),
680 Err(e) => {
681 PORT_ASYNC_ACTIVE[idx].store(false, Ordering::Release);
683 PORT_ASYNC_RING_ID[idx].store(0, Ordering::Release);
684 PORT_ASYNC_USER_DATA[idx].store(0, Ordering::Release);
685 PORT_ASYNC_BUF_VADDR[idx].store(0, Ordering::Release);
686 PORT_ASYNC_LEN[idx].store(0, Ordering::Release);
687 PORT_ASYNC_IS_WRITE[idx].store(false, Ordering::Release);
688 PORT_ASYNC_DMA_PTR[idx].store(0, Ordering::Release);
689 Err(e)
690 }
691 }
692}
693
694pub(crate) fn flush_deferred_async_read_completions(ring_id: u64) -> u32 {
695 let Some(task) = crate::process::current_task_clone() else {
696 return 0;
697 };
698 let Some(ring) = crate::async_io::ring::find_ring(ring_id) else {
699 return discard_deferred_async_read_completions(ring_id);
700 };
701 if ring.owner_pid != task.pid {
702 return 0;
703 }
704 if ring.destroyed.load(core::sync::atomic::Ordering::Acquire) != 0 {
707 return discard_deferred_async_read_completions(ring_id);
708 }
709
710 let pending = {
711 let mut guard = PENDING_ASYNC_READ_COMPLETIONS.lock();
712 let mut pending = Vec::new();
715 let mut i = 0;
716 while i < guard.len() {
717 if guard[i].ring_id == ring_id {
718 pending.push(guard.swap_remove(i));
721 } else {
722 i += 1;
723 }
724 }
725 pending
726 };
727
728 let mut flushed = 0;
729
730 for mut completion in pending {
731 if completion.result >= 0 {
734 completion.result = match UserSliceWrite::new(completion.user_buf_vaddr, completion.len)
735 {
736 Ok(user_buf) => {
737 let src = completion.dma_buf.virt_addr() as *const u8;
750 let dst = user_buf.as_ptr() as *mut u8;
751 let n = completion.len;
752
753 if n >= 128 {
754 #[cfg(target_arch = "x86_64")]
757 unsafe {
758 core::arch::asm!(
759 "rep movsb",
760 inout("rcx") n => _,
761 inout("rsi") src => _,
762 inout("rdi") dst => _,
763 options(nostack),
764 );
765 }
766 #[cfg(not(target_arch = "x86_64"))]
767 user_buf
768 .copy_from(unsafe { core::slice::from_raw_parts(src, completion.len) });
769 } else {
770 user_buf
771 .copy_from(unsafe { core::slice::from_raw_parts(src, completion.len) });
772 }
773 completion.len as i32
774 }
775 Err(_) => EFAULT,
776 };
777 }
778
779 if crate::async_io::complete::push_completion_for_ring(
780 &ring,
781 completion.user_data,
782 completion.result,
783 0,
784 ) {
785 flushed += 1;
786 } else {
787 }
789 }
790
791 flushed
792}
793
794pub(crate) fn discard_deferred_async_read_completions(ring_id: u64) -> u32 {
795 let mut guard = PENDING_ASYNC_READ_COMPLETIONS.lock();
796 let before = guard.len();
797 guard.retain(|completion| completion.ring_id != ring_id);
798 (before - guard.len()) as u32
799}
800
801pub fn handle_interrupt() {
815 let abar = AHCI_ABAR_VIRT.load(Ordering::Relaxed);
816 if abar == 0 {
817 return; }
819
820 let global_is = unsafe { rd32(abar, HBA_IS) };
822 if global_is == 0 {
823 return; }
825
826 for port_num in 0..32u8 {
827 if global_is & (1 << port_num) == 0 {
828 continue;
829 }
830
831 let pvirt = PORT_VIRT[port_num as usize].load(Ordering::Relaxed);
832 if pvirt == 0 {
833 continue; }
835
836 let pxis = unsafe { rd32(pvirt, PORT_IS) };
838
839 if pxis & PXIS_TFES != 0 {
841 PORT_SLOT0_ERROR[port_num as usize].store(true, Ordering::Release);
842 unsafe {
844 wr32(pvirt, PORT_IS, pxis);
845 wr32(pvirt, PORT_SERR, 0xFFFF_FFFF);
846 }
847 } else {
848 PORT_SLOT0_ERROR[port_num as usize].store(false, Ordering::Release);
849 unsafe { wr32(pvirt, PORT_IS, pxis) };
851 }
852
853 unsafe { wr32(abar, HBA_IS, 1 << port_num) };
856
857 PORT_SLOT0_DONE[port_num as usize].store(true, Ordering::Release);
859
860 let idx = port_num as usize;
863 if PORT_ASYNC_ACTIVE[idx].load(Ordering::Acquire) {
864 let ring_id = PORT_ASYNC_RING_ID[idx].load(Ordering::Acquire);
865 let user_data = PORT_ASYNC_USER_DATA[idx].load(Ordering::Acquire);
866 let buf_vaddr = PORT_ASYNC_BUF_VADDR[idx].load(Ordering::Acquire);
867 let nbytes = PORT_ASYNC_LEN[idx].load(Ordering::Acquire) as usize;
868 let is_write = PORT_ASYNC_IS_WRITE[idx].load(Ordering::Acquire);
869 let dma_ptr_val = PORT_ASYNC_DMA_PTR[idx].load(Ordering::Acquire);
870
871 PORT_ASYNC_ACTIVE[idx].store(false, Ordering::Release);
873 PORT_ASYNC_RING_ID[idx].store(0, Ordering::Release);
874 PORT_ASYNC_USER_DATA[idx].store(0, Ordering::Release);
875 PORT_ASYNC_BUF_VADDR[idx].store(0, Ordering::Release);
876 PORT_ASYNC_LEN[idx].store(0, Ordering::Release);
877 PORT_ASYNC_IS_WRITE[idx].store(false, Ordering::Release);
878 PORT_ASYNC_DMA_PTR[idx].store(0, Ordering::Release);
879
880 if dma_ptr_val != 0 {
881 let dma_buf = unsafe { *Box::from_raw(dma_ptr_val as *mut DmaBuffer) };
883
884 let result = if PORT_SLOT0_ERROR[idx].load(Ordering::Acquire) {
885 -5i32 } else {
887 nbytes as i32
888 };
889
890 if is_write || result < 0 {
891 crate::async_io::complete::push_completion(ring_id, user_data, result, 0);
892 } else {
893 PENDING_ASYNC_READ_COMPLETIONS
894 .lock()
895 .push(PendingAsyncReadCompletion {
896 ring_id,
897 user_data,
898 user_buf_vaddr: buf_vaddr,
899 dma_buf,
900 len: nbytes,
901 result,
902 });
903
904 if let Some(ring) = crate::async_io::ring::find_ring(ring_id) {
905 ring.wq.wake_all();
906 }
907 }
908 }
909 } else {
910 PORT_WQ[idx].wake_one();
911 }
912 }
913}
914
915impl AhciController {
918 fn port_by_num(&self, port_num: u8) -> Option<&AhciPort> {
919 self.ports.iter().find(|port| port.port_num == port_num)
920 }
921
922 pub unsafe fn init() -> Result<Self, AhciError> {
927 let pci_dev = pci::probe_first(ProbeCriteria {
929 class_code: Some(pci::class::MASS_STORAGE),
930 subclass: Some(pci::storage_subclass::SATA),
931 prog_if: Some(pci::sata_progif::AHCI),
932 ..ProbeCriteria::any()
933 })
934 .ok_or(AhciError::NoController)?;
935
936 log::info!("AHCI: found controller at {:?}", pci_dev.address);
937
938 pci_dev.enable_bus_master();
940 pci_dev.enable_memory_space();
941
942 let (_irq_line, irq_vector) = crate::arch::x86_64::msi::probe_and_enable(&pci_dev, true);
944
945 let abar_phys = pci_dev.read_bar_raw(5).ok_or(AhciError::BadAbar)?;
947 if abar_phys == 0 {
948 return Err(AhciError::BadAbar);
949 }
950
951 crate::memory::paging::ensure_identity_map_range(abar_phys, 0x1200);
953 let abar_virt = phys_to_virt(abar_phys);
954
955 let ghc = rd32(abar_virt, HBA_GHC);
958 if ghc & GHC_AE == 0 {
959 wr32(abar_virt, HBA_GHC, ghc | GHC_AE);
960 }
961
962 let mut ghc_after = rd32(abar_virt, HBA_GHC) | GHC_AE;
964 wr32(abar_virt, HBA_GHC, ghc_after | GHC_HR);
965 let mut reset_timeout = 1_000_000u32;
966 while rd32(abar_virt, HBA_GHC) & GHC_HR != 0 {
967 reset_timeout = reset_timeout.saturating_sub(1);
968 if reset_timeout == 0 {
969 log::warn!("AHCI: HBA reset timed out, continuing with current state");
970 break;
971 }
972 core::hint::spin_loop();
973 }
974 ghc_after = rd32(abar_virt, HBA_GHC) | GHC_AE;
975 wr32(abar_virt, HBA_GHC, ghc_after);
976 wr32(abar_virt, HBA_IS, 0xFFFF_FFFF);
978
979 log::debug!(
980 "AHCI: ABAR phys={:#x} virt={:#x} GHC={:#010x}",
981 abar_phys,
982 abar_virt,
983 rd32(abar_virt, HBA_GHC)
984 );
985
986 let pi = rd32(abar_virt, HBA_PI); log::debug!("AHCI: ports implemented mask = {:#010x}", pi);
988
989 let mut ports: Vec<AhciPort> = Vec::new();
990
991 for port_num in 0..32u8 {
992 if pi & (1 << port_num) == 0 {
993 continue;
994 }
995
996 let pvirt = abar_virt + 0x100 + (port_num as u64) * 0x80;
997
998 let ssts = rd32(pvirt, PORT_SSTS);
1000 let det = ssts & SSTS_DET_MASK;
1001 if det != SSTS_DET_COMM {
1002 log::debug!("AHCI: port {} DET={} : no device, skipping", port_num, det);
1003 continue;
1004 }
1005
1006 let sig = rd32(pvirt, PORT_SIG);
1008 if sig != SIG_SATA {
1009 log::debug!(
1010 "AHCI: port {} sig={:#010x} : not plain SATA, skipping",
1011 port_num,
1012 sig
1013 );
1014 continue;
1015 }
1016
1017 let frame = crate::sync::with_irqs_disabled(|token| memory::allocate_frame(token))
1019 .map_err(|_| AhciError::Alloc)?;
1020
1021 let mem_phys = frame.start_address.as_u64();
1022 let mem_virt = phys_to_virt(mem_phys);
1023
1024 ptr::write_bytes(mem_virt as *mut u8, 0, 4096);
1027
1028 port_rebase(pvirt, mem_phys);
1029
1030 port_enable_irq(pvirt);
1032
1033 PORT_VIRT[port_num as usize].store(pvirt, Ordering::Relaxed);
1036
1037 let mut port = AhciPort {
1039 port_num,
1040 port_virt: pvirt,
1041 mem_phys,
1042 mem_virt,
1043 sector_count: 0,
1044 };
1045
1046 let mut id_buf = [0u8; SECTOR_SIZE];
1047 match submit_cmd(&port, 0, 1, &mut id_buf, false, ATA_IDENTIFY) {
1048 Ok(()) => {
1049 let w0 = u16::from_le_bytes([id_buf[200], id_buf[201]]) as u64;
1051 let w1 = u16::from_le_bytes([id_buf[202], id_buf[203]]) as u64;
1052 let w2 = u16::from_le_bytes([id_buf[204], id_buf[205]]) as u64;
1053 let w3 = u16::from_le_bytes([id_buf[206], id_buf[207]]) as u64;
1054 port.sector_count = w0 | (w1 << 16) | (w2 << 32) | (w3 << 48);
1055 log::info!(
1056 "AHCI: port {} SATA : {} sectors ({} MiB)",
1057 port_num,
1058 port.sector_count,
1059 (port.sector_count * SECTOR_SIZE as u64) / (1024 * 1024)
1060 );
1061 }
1062 Err(e) => {
1063 log::warn!("AHCI: port {} IDENTIFY failed: {}", port_num, e);
1064 }
1065 }
1066
1067 ports.push(port);
1068 }
1069
1070 if ports.is_empty() {
1071 return Err(AhciError::NoPort);
1072 }
1073
1074 AHCI_ABAR_VIRT.store(abar_virt, Ordering::Relaxed);
1077 AHCI_IRQ_LINE.store(irq_vector, Ordering::Relaxed);
1078
1079 let ghc = rd32(abar_virt, HBA_GHC);
1082 wr32(abar_virt, HBA_GHC, ghc | GHC_IE);
1083
1084 log::info!("AHCI: global interrupts enabled (vector {:#x})", irq_vector);
1085
1086 Ok(AhciController { abar_virt, ports })
1087 }
1088
1089 pub fn sector_count(&self) -> u64 {
1091 self.ports.first().map(|p| p.sector_count).unwrap_or(0)
1092 }
1093
1094 pub fn first_port_num(&self) -> Option<u8> {
1096 self.ports.first().map(|port| port.port_num)
1097 }
1098
1099 fn first_port(&self) -> Option<&AhciPort> {
1101 self.ports.first()
1102 }
1103}
1104
1105#[cfg(test)]
1106mod tests {
1107 use super::{AhciController, AhciPort};
1108 use alloc::vec;
1109
1110 fn fake_port(port_num: u8) -> AhciPort {
1111 AhciPort {
1112 port_num,
1113 port_virt: 0,
1114 mem_phys: 0,
1115 mem_virt: 0,
1116 sector_count: 1024,
1117 }
1118 }
1119
1120 #[test]
1121 fn selects_requested_port_number() {
1122 let controller = AhciController {
1123 abar_virt: 0,
1124 ports: vec![fake_port(2), fake_port(5), fake_port(7)],
1125 dma_pool: None,
1126 };
1127
1128 assert_eq!(controller.port_by_num(5).map(|port| port.port_num), Some(5));
1129 assert_eq!(controller.port_by_num(1).map(|port| port.port_num), None);
1130 assert_eq!(controller.first_port_num(), Some(2));
1131 }
1132}
1133
1134impl BlockDevice for AhciController {
1135 fn read_sector(&self, sector: u64, buf: &mut [u8]) -> Result<(), BlockError> {
1137 let port = self.first_port().ok_or(BlockError::NotReady)?;
1138 if sector >= port.sector_count {
1139 return Err(BlockError::InvalidSector);
1140 }
1141 if buf.len() < SECTOR_SIZE {
1142 return Err(BlockError::BufferTooSmall);
1143 }
1144 submit_cmd(port, sector, 1, buf, false, ATA_READ_DMA_EXT).map_err(|_| BlockError::IoError)
1145 }
1146
1147 fn write_sector(&self, sector: u64, buf: &[u8]) -> Result<(), BlockError> {
1149 let port = self.first_port().ok_or(BlockError::NotReady)?;
1150 if sector >= port.sector_count {
1151 return Err(BlockError::InvalidSector);
1152 }
1153 if buf.len() < SECTOR_SIZE {
1154 return Err(BlockError::BufferTooSmall);
1155 }
1156 let buf_mut = buf.as_ptr() as *mut u8;
1160 let buf_slice = unsafe { core::slice::from_raw_parts_mut(buf_mut, buf.len()) };
1161 submit_cmd(port, sector, 1, buf_slice, true, ATA_WRITE_DMA_EXT)
1162 .map_err(|_| BlockError::IoError)
1163 }
1164
1165 fn read_sectors(&self, sector: u64, count: u16, buf: &mut [u8]) -> Result<(), BlockError> {
1170 let port = self.first_port().ok_or(BlockError::NotReady)?;
1171 let nbytes = (count as usize) * SECTOR_SIZE;
1172 if sector.saturating_add(count as u64) > port.sector_count {
1173 return Err(BlockError::InvalidSector);
1174 }
1175 if buf.len() < nbytes {
1176 return Err(BlockError::BufferTooSmall);
1177 }
1178 submit_cmd(port, sector, count, buf, false, ATA_READ_DMA_EXT)
1179 .map_err(|_| BlockError::IoError)
1180 }
1181
1182 fn write_sectors(&self, sector: u64, count: u16, buf: &[u8]) -> Result<(), BlockError> {
1184 let port = self.first_port().ok_or(BlockError::NotReady)?;
1185 let nbytes = (count as usize) * SECTOR_SIZE;
1186 if sector.saturating_add(count as u64) > port.sector_count {
1187 return Err(BlockError::InvalidSector);
1188 }
1189 if buf.len() < nbytes {
1190 return Err(BlockError::BufferTooSmall);
1191 }
1192 let buf_mut = buf.as_ptr() as *mut u8;
1195 let buf_slice = unsafe { core::slice::from_raw_parts_mut(buf_mut, buf.len()) };
1196 submit_cmd(port, sector, count, buf_slice, true, ATA_WRITE_DMA_EXT)
1197 .map_err(|_| BlockError::IoError)
1198 }
1199
1200 fn sector_count(&self) -> u64 {
1202 self.sector_count()
1203 }
1204}
1205
1206static AHCI_PTR: core::sync::atomic::AtomicPtr<AhciController> =
1210 core::sync::atomic::AtomicPtr::new(core::ptr::null_mut());
1211
1212pub fn init() {
1216 log::info!("AHCI: scanning PCI bus...");
1217
1218 match unsafe { AhciController::init() } {
1219 Ok(ctrl) => {
1220 let leaked: &'static mut AhciController = Box::leak(Box::new(ctrl));
1221 AHCI_PTR.store(leaked as *mut AhciController, Ordering::Release);
1222 log::info!("AHCI: controller ready");
1223
1224 let vector = AHCI_IRQ_LINE.load(Ordering::Relaxed);
1226 crate::arch::x86_64::idt::register_ahci_irq(vector);
1227 }
1228 Err(AhciError::NoController) => {
1229 log::info!("AHCI: no controller found (not a SATA system?)");
1230 }
1231 Err(e) => {
1232 log::error!("AHCI: init failed: {}", e);
1233 }
1234 }
1235}
1236
1237pub fn get_device() -> Option<&'static AhciController> {
1239 let ptr = AHCI_PTR.load(Ordering::Acquire);
1240 if ptr.is_null() {
1241 None
1242 } else {
1243 Some(unsafe { &*ptr })
1245 }
1246}