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, 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 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 } else {
767 user_buf
768 .copy_from(unsafe { core::slice::from_raw_parts(src, completion.len) });
769 }
770 completion.len as i32
771 }
772 Err(_) => EFAULT,
773 };
774 }
775
776 if crate::async_io::complete::push_completion_for_ring(
777 &ring,
778 completion.user_data,
779 completion.result,
780 0,
781 ) {
782 flushed += 1;
783 } else {
784 }
786 }
787
788 flushed
789}
790
791pub(crate) fn discard_deferred_async_read_completions(ring_id: u64) -> u32 {
792 let mut guard = PENDING_ASYNC_READ_COMPLETIONS.lock();
793 let before = guard.len();
794 guard.retain(|completion| completion.ring_id != ring_id);
795 (before - guard.len()) as u32
796}
797
798pub fn handle_interrupt() {
812 let abar = AHCI_ABAR_VIRT.load(Ordering::Relaxed);
813 if abar == 0 {
814 return; }
816
817 let global_is = unsafe { rd32(abar, HBA_IS) };
819 if global_is == 0 {
820 return; }
822
823 for port_num in 0..32u8 {
824 if global_is & (1 << port_num) == 0 {
825 continue;
826 }
827
828 let pvirt = PORT_VIRT[port_num as usize].load(Ordering::Relaxed);
829 if pvirt == 0 {
830 continue; }
832
833 let pxis = unsafe { rd32(pvirt, PORT_IS) };
835
836 if pxis & PXIS_TFES != 0 {
838 PORT_SLOT0_ERROR[port_num as usize].store(true, Ordering::Release);
839 unsafe {
841 wr32(pvirt, PORT_IS, pxis);
842 wr32(pvirt, PORT_SERR, 0xFFFF_FFFF);
843 }
844 } else {
845 PORT_SLOT0_ERROR[port_num as usize].store(false, Ordering::Release);
846 unsafe { wr32(pvirt, PORT_IS, pxis) };
848 }
849
850 unsafe { wr32(abar, HBA_IS, 1 << port_num) };
853
854 PORT_SLOT0_DONE[port_num as usize].store(true, Ordering::Release);
856
857 let idx = port_num as usize;
860 if PORT_ASYNC_ACTIVE[idx].load(Ordering::Acquire) {
861 let ring_id = PORT_ASYNC_RING_ID[idx].load(Ordering::Acquire);
862 let user_data = PORT_ASYNC_USER_DATA[idx].load(Ordering::Acquire);
863 let buf_vaddr = PORT_ASYNC_BUF_VADDR[idx].load(Ordering::Acquire);
864 let nbytes = PORT_ASYNC_LEN[idx].load(Ordering::Acquire) as usize;
865 let is_write = PORT_ASYNC_IS_WRITE[idx].load(Ordering::Acquire);
866 let dma_ptr_val = PORT_ASYNC_DMA_PTR[idx].load(Ordering::Acquire);
867
868 PORT_ASYNC_ACTIVE[idx].store(false, Ordering::Release);
870 PORT_ASYNC_RING_ID[idx].store(0, Ordering::Release);
871 PORT_ASYNC_USER_DATA[idx].store(0, Ordering::Release);
872 PORT_ASYNC_BUF_VADDR[idx].store(0, Ordering::Release);
873 PORT_ASYNC_LEN[idx].store(0, Ordering::Release);
874 PORT_ASYNC_IS_WRITE[idx].store(false, Ordering::Release);
875 PORT_ASYNC_DMA_PTR[idx].store(0, Ordering::Release);
876
877 if dma_ptr_val != 0 {
878 let dma_buf = unsafe { *Box::from_raw(dma_ptr_val as *mut DmaBuffer) };
880
881 let result = if PORT_SLOT0_ERROR[idx].load(Ordering::Acquire) {
882 -5i32 } else {
884 nbytes as i32
885 };
886
887 if is_write || result < 0 {
888 crate::async_io::complete::push_completion(ring_id, user_data, result, 0);
889 } else {
890 PENDING_ASYNC_READ_COMPLETIONS
891 .lock()
892 .push(PendingAsyncReadCompletion {
893 ring_id,
894 user_data,
895 user_buf_vaddr: buf_vaddr,
896 dma_buf,
897 len: nbytes,
898 result,
899 });
900
901 if let Some(ring) = crate::async_io::ring::find_ring(ring_id) {
902 ring.wq.wake_all();
903 }
904 }
905 }
906 } else {
907 PORT_WQ[idx].wake_one();
908 }
909 }
910}
911
912impl AhciController {
915 fn port_by_num(&self, port_num: u8) -> Option<&AhciPort> {
916 self.ports.iter().find(|port| port.port_num == port_num)
917 }
918
919 pub unsafe fn init() -> Result<Self, AhciError> {
924 let pci_dev = pci::probe_first(ProbeCriteria {
926 class_code: Some(pci::class::MASS_STORAGE),
927 subclass: Some(pci::storage_subclass::SATA),
928 prog_if: Some(pci::sata_progif::AHCI),
929 ..ProbeCriteria::any()
930 })
931 .ok_or(AhciError::NoController)?;
932
933 log::info!("AHCI: found controller at {:?}", pci_dev.address);
934
935 pci_dev.enable_bus_master();
937 pci_dev.enable_memory_space();
938
939 let irq_line = pci_dev.read_config_u8(pci::config::INTERRUPT_LINE);
941
942 let abar_phys = pci_dev.read_bar_raw(5).ok_or(AhciError::BadAbar)?;
944 if abar_phys == 0 {
945 return Err(AhciError::BadAbar);
946 }
947
948 crate::memory::paging::ensure_identity_map_range(abar_phys, 0x1200);
950 let abar_virt = phys_to_virt(abar_phys);
951
952 let ghc = rd32(abar_virt, HBA_GHC);
955 if ghc & GHC_AE == 0 {
956 wr32(abar_virt, HBA_GHC, ghc | GHC_AE);
957 }
958
959 let mut ghc_after = rd32(abar_virt, HBA_GHC) | GHC_AE;
961 wr32(abar_virt, HBA_GHC, ghc_after | GHC_HR);
962 let mut reset_timeout = 1_000_000u32;
963 while rd32(abar_virt, HBA_GHC) & GHC_HR != 0 {
964 reset_timeout = reset_timeout.saturating_sub(1);
965 if reset_timeout == 0 {
966 log::warn!("AHCI: HBA reset timed out, continuing with current state");
967 break;
968 }
969 core::hint::spin_loop();
970 }
971 ghc_after = rd32(abar_virt, HBA_GHC) | GHC_AE;
972 wr32(abar_virt, HBA_GHC, ghc_after);
973 wr32(abar_virt, HBA_IS, 0xFFFF_FFFF);
975
976 log::debug!(
977 "AHCI: ABAR phys={:#x} virt={:#x} GHC={:#010x}",
978 abar_phys,
979 abar_virt,
980 rd32(abar_virt, HBA_GHC)
981 );
982
983 let pi = rd32(abar_virt, HBA_PI); log::debug!("AHCI: ports implemented mask = {:#010x}", pi);
985
986 let mut ports: Vec<AhciPort> = Vec::new();
987
988 for port_num in 0..32u8 {
989 if pi & (1 << port_num) == 0 {
990 continue;
991 }
992
993 let pvirt = abar_virt + 0x100 + (port_num as u64) * 0x80;
994
995 let ssts = rd32(pvirt, PORT_SSTS);
997 let det = ssts & SSTS_DET_MASK;
998 if det != SSTS_DET_COMM {
999 log::debug!("AHCI: port {} DET={} : no device, skipping", port_num, det);
1000 continue;
1001 }
1002
1003 let sig = rd32(pvirt, PORT_SIG);
1005 if sig != SIG_SATA {
1006 log::debug!(
1007 "AHCI: port {} sig={:#010x} : not plain SATA, skipping",
1008 port_num,
1009 sig
1010 );
1011 continue;
1012 }
1013
1014 let frame = crate::sync::with_irqs_disabled(|token| memory::allocate_frame(token))
1016 .map_err(|_| AhciError::Alloc)?;
1017
1018 let mem_phys = frame.start_address.as_u64();
1019 let mem_virt = phys_to_virt(mem_phys);
1020
1021 ptr::write_bytes(mem_virt as *mut u8, 0, 4096);
1024
1025 port_rebase(pvirt, mem_phys);
1026
1027 port_enable_irq(pvirt);
1029
1030 PORT_VIRT[port_num as usize].store(pvirt, Ordering::Relaxed);
1033
1034 let mut port = AhciPort {
1036 port_num,
1037 port_virt: pvirt,
1038 mem_phys,
1039 mem_virt,
1040 sector_count: 0,
1041 };
1042
1043 let mut id_buf = [0u8; SECTOR_SIZE];
1044 match submit_cmd(&port, 0, 1, &mut id_buf, false, ATA_IDENTIFY) {
1045 Ok(()) => {
1046 let w0 = u16::from_le_bytes([id_buf[200], id_buf[201]]) as u64;
1048 let w1 = u16::from_le_bytes([id_buf[202], id_buf[203]]) as u64;
1049 let w2 = u16::from_le_bytes([id_buf[204], id_buf[205]]) as u64;
1050 let w3 = u16::from_le_bytes([id_buf[206], id_buf[207]]) as u64;
1051 port.sector_count = w0 | (w1 << 16) | (w2 << 32) | (w3 << 48);
1052 log::info!(
1053 "AHCI: port {} SATA : {} sectors ({} MiB)",
1054 port_num,
1055 port.sector_count,
1056 (port.sector_count * SECTOR_SIZE as u64) / (1024 * 1024)
1057 );
1058 }
1059 Err(e) => {
1060 log::warn!("AHCI: port {} IDENTIFY failed: {}", port_num, e);
1061 }
1062 }
1063
1064 ports.push(port);
1065 }
1066
1067 if ports.is_empty() {
1068 return Err(AhciError::NoPort);
1069 }
1070
1071 AHCI_ABAR_VIRT.store(abar_virt, Ordering::Relaxed);
1074 AHCI_IRQ_LINE.store(irq_line, Ordering::Relaxed);
1075
1076 let ghc = rd32(abar_virt, HBA_GHC);
1079 wr32(abar_virt, HBA_GHC, ghc | GHC_IE);
1080
1081 log::info!("AHCI: global interrupts enabled (IRQ line {})", irq_line);
1082
1083 Ok(AhciController { abar_virt, ports })
1084 }
1085
1086 pub fn sector_count(&self) -> u64 {
1088 self.ports.first().map(|p| p.sector_count).unwrap_or(0)
1089 }
1090
1091 pub fn first_port_num(&self) -> Option<u8> {
1093 self.ports.first().map(|port| port.port_num)
1094 }
1095
1096 fn first_port(&self) -> Option<&AhciPort> {
1098 self.ports.first()
1099 }
1100}
1101
1102#[cfg(test)]
1103mod tests {
1104 use super::{AhciController, AhciPort};
1105 use alloc::vec;
1106
1107 fn fake_port(port_num: u8) -> AhciPort {
1108 AhciPort {
1109 port_num,
1110 port_virt: 0,
1111 mem_phys: 0,
1112 mem_virt: 0,
1113 sector_count: 1024,
1114 }
1115 }
1116
1117 #[test]
1118 fn selects_requested_port_number() {
1119 let controller = AhciController {
1120 abar_virt: 0,
1121 ports: vec![fake_port(2), fake_port(5), fake_port(7)],
1122 };
1123
1124 assert_eq!(controller.port_by_num(5).map(|port| port.port_num), Some(5));
1125 assert_eq!(controller.port_by_num(1).map(|port| port.port_num), None);
1126 assert_eq!(controller.first_port_num(), Some(2));
1127 }
1128}
1129
1130impl BlockDevice for AhciController {
1131 fn read_sector(&self, sector: u64, buf: &mut [u8]) -> Result<(), BlockError> {
1133 let port = self.first_port().ok_or(BlockError::NotReady)?;
1134 if sector >= port.sector_count {
1135 return Err(BlockError::InvalidSector);
1136 }
1137 if buf.len() < SECTOR_SIZE {
1138 return Err(BlockError::BufferTooSmall);
1139 }
1140 submit_cmd(port, sector, 1, buf, false, ATA_READ_DMA_EXT).map_err(|_| BlockError::IoError)
1141 }
1142
1143 fn write_sector(&self, sector: u64, buf: &[u8]) -> Result<(), BlockError> {
1145 let port = self.first_port().ok_or(BlockError::NotReady)?;
1146 if sector >= port.sector_count {
1147 return Err(BlockError::InvalidSector);
1148 }
1149 if buf.len() < SECTOR_SIZE {
1150 return Err(BlockError::BufferTooSmall);
1151 }
1152 let mut tmp = [0u8; SECTOR_SIZE];
1154 tmp.copy_from_slice(&buf[..SECTOR_SIZE]);
1155 submit_cmd(port, sector, 1, &mut tmp, true, ATA_WRITE_DMA_EXT)
1156 .map_err(|_| BlockError::IoError)
1157 }
1158
1159 fn sector_count(&self) -> u64 {
1161 self.sector_count()
1162 }
1163}
1164
1165static AHCI: SpinLock<Option<Box<AhciController>>> = SpinLock::new(None);
1168
1169pub fn init() {
1173 log::info!("AHCI: scanning PCI bus...");
1174
1175 match unsafe { AhciController::init() } {
1176 Ok(ctrl) => {
1177 *AHCI.lock() = Some(Box::new(ctrl));
1178 log::info!("AHCI: controller ready");
1179
1180 let irq = AHCI_IRQ_LINE.load(Ordering::Relaxed);
1182 crate::arch::x86_64::idt::register_ahci_irq(irq);
1183 }
1184 Err(AhciError::NoController) => {
1185 log::info!("AHCI: no controller found (not a SATA system?)");
1186 }
1187 Err(e) => {
1188 log::error!("AHCI: init failed: {}", e);
1189 }
1190 }
1191}
1192
1193pub fn get_device() -> Option<&'static AhciController> {
1195 unsafe {
1197 let lock = AHCI.lock();
1198 lock.as_ref().map(|b| {
1199 let ptr = b.as_ref() as *const AhciController;
1200 &*ptr
1201 })
1202 }
1203}