⚠️ VeridianOS Kernel Documentation - This is low-level kernel code. All functions are unsafe unless explicitly marked otherwise. no_std

veridian_kernel/drivers/
ahci.rs

1//! AHCI (Advanced Host Controller Interface) / SATA Controller Driver
2//!
3//! Implements AHCI HBA (Host Bus Adapter) discovery via PCI enumeration,
4//! port detection with device signature identification, and block-level
5//! read/write operations using the AHCI command infrastructure (FIS,
6//! Command List, PRDT). Includes NCQ (Native Command Queuing) stubs.
7//!
8//! PCI identification: class 0x01 (Mass Storage), subclass 0x06 (SATA),
9//! prog-if 0x01 (AHCI 1.0).
10
11#![allow(dead_code)]
12
13#[cfg(feature = "alloc")]
14extern crate alloc;
15
16#[cfg(feature = "alloc")]
17use alloc::{string::String, vec::Vec};
18use core::sync::atomic::{AtomicBool, Ordering};
19
20use crate::error::KernelError;
21#[cfg(feature = "alloc")]
22use crate::fs::blockdev::BlockDevice;
23
24// ---------------------------------------------------------------------------
25// PCI identification constants
26// ---------------------------------------------------------------------------
27
28/// PCI class code for mass storage controllers.
29const PCI_CLASS_MASS_STORAGE: u8 = 0x01;
30
31/// PCI subclass for SATA controllers.
32const PCI_SUBCLASS_SATA: u8 = 0x06;
33
34/// PCI programming interface for AHCI 1.0.
35const PCI_PROGIF_AHCI: u8 = 0x01;
36
37// ---------------------------------------------------------------------------
38// HBA Generic Host Control registers (offsets from ABAR)
39// ---------------------------------------------------------------------------
40
41/// Host Capabilities (read-only).
42const HBA_REG_CAP: usize = 0x00;
43
44/// Global Host Control.
45const HBA_REG_GHC: usize = 0x04;
46
47/// Interrupt Status (read/write-1-to-clear).
48const HBA_REG_IS: usize = 0x08;
49
50/// Ports Implemented (read-only bitmask).
51const HBA_REG_PI: usize = 0x0C;
52
53/// AHCI Version.
54const HBA_REG_VS: usize = 0x10;
55
56/// Command Completion Coalescing Control.
57const HBA_REG_CCC_CTL: usize = 0x14;
58
59/// Command Completion Coalescing Ports.
60const HBA_REG_CCC_PORTS: usize = 0x18;
61
62/// Enclosure Management Location.
63const HBA_REG_EM_LOC: usize = 0x1C;
64
65/// Enclosure Management Control.
66const HBA_REG_EM_CTL: usize = 0x20;
67
68/// Host Capabilities Extended.
69const HBA_REG_CAP2: usize = 0x24;
70
71/// BIOS/OS Handoff Control and Status.
72const HBA_REG_BOHC: usize = 0x28;
73
74// ---------------------------------------------------------------------------
75// GHC (Global Host Control) register bits
76// ---------------------------------------------------------------------------
77
78/// HBA Reset.
79const GHC_HR: u32 = 1 << 0;
80
81/// Interrupt Enable.
82const GHC_IE: u32 = 1 << 1;
83
84/// AHCI Enable.
85const GHC_AE: u32 = 1 << 31;
86
87// ---------------------------------------------------------------------------
88// CAP (Host Capabilities) register bits
89// ---------------------------------------------------------------------------
90
91/// Number of ports (bits 4:0), zero-based.
92const CAP_NP_MASK: u32 = 0x1F;
93
94/// Supports NCQ (bit 30).
95const CAP_SNCQ: u32 = 1 << 30;
96
97/// Supports 64-bit addressing (bit 31).
98const CAP_S64A: u32 = 1 << 31;
99
100/// Number of command slots (bits 12:8), zero-based.
101const CAP_NCS_SHIFT: u32 = 8;
102const CAP_NCS_MASK: u32 = 0x1F;
103
104// ---------------------------------------------------------------------------
105// Port register block (per-port, base = 0x100 + port * 0x80)
106// ---------------------------------------------------------------------------
107
108const PORT_BASE: usize = 0x100;
109const PORT_SIZE: usize = 0x80;
110
111/// Port Command List Base Address (lower 32 bits).
112const PORT_CLB: usize = 0x00;
113
114/// Port Command List Base Address (upper 32 bits).
115const PORT_CLBU: usize = 0x04;
116
117/// Port FIS Base Address (lower 32 bits).
118const PORT_FB: usize = 0x08;
119
120/// Port FIS Base Address (upper 32 bits).
121const PORT_FBU: usize = 0x0C;
122
123/// Port Interrupt Status.
124const PORT_IS: usize = 0x10;
125
126/// Port Interrupt Enable.
127const PORT_IE: usize = 0x14;
128
129/// Port Command and Status.
130const PORT_CMD: usize = 0x18;
131
132/// Port Task File Data.
133const PORT_TFD: usize = 0x20;
134
135/// Port Signature.
136const PORT_SIG: usize = 0x24;
137
138/// Port SATA Status (SCR0: SStatus).
139const PORT_SSTS: usize = 0x28;
140
141/// Port SATA Control (SCR2: SControl).
142const PORT_SCTL: usize = 0x2C;
143
144/// Port SATA Error (SCR1: SError).
145const PORT_SERR: usize = 0x30;
146
147/// Port SATA Active (SCR3: SActive) -- for NCQ.
148const PORT_SACT: usize = 0x34;
149
150/// Port Command Issue.
151const PORT_CI: usize = 0x38;
152
153// ---------------------------------------------------------------------------
154// PORT_CMD bits
155// ---------------------------------------------------------------------------
156
157/// Start (process command list).
158const PORT_CMD_ST: u32 = 1 << 0;
159
160/// Spin-Up Device.
161const PORT_CMD_SUD: u32 = 1 << 1;
162
163/// Power On Device.
164const PORT_CMD_POD: u32 = 1 << 2;
165
166/// FIS Receive Enable.
167const PORT_CMD_FRE: u32 = 1 << 4;
168
169/// FIS Receive Running.
170const PORT_CMD_FR: u32 = 1 << 14;
171
172/// Command List Running.
173const PORT_CMD_CR: u32 = 1 << 15;
174
175// ---------------------------------------------------------------------------
176// PORT_TFD bits
177// ---------------------------------------------------------------------------
178
179/// Task file status: BSY (busy).
180const TFD_BSY: u32 = 1 << 7;
181
182/// Task file status: DRQ (data request).
183const TFD_DRQ: u32 = 1 << 3;
184
185/// Task file status: ERR (error).
186const TFD_ERR: u32 = 1 << 0;
187
188// ---------------------------------------------------------------------------
189// PORT_SSTS (SStatus) detection and interface fields
190// ---------------------------------------------------------------------------
191
192/// Device detection (bits 3:0).
193const SSTS_DET_MASK: u32 = 0x0F;
194
195/// Device present and PHY communication established.
196const SSTS_DET_PRESENT: u32 = 0x03;
197
198/// Interface Power Management (bits 11:8).
199const SSTS_IPM_MASK: u32 = 0x0F00;
200
201/// Interface active.
202const SSTS_IPM_ACTIVE: u32 = 0x0100;
203
204// ---------------------------------------------------------------------------
205// Device signatures (from PORT_SIG after device reset)
206// ---------------------------------------------------------------------------
207
208/// SATA drive (ATA).
209const SIG_SATA: u32 = 0x00000101;
210
211/// SATAPI (ATAPI) drive.
212const SIG_SATAPI: u32 = 0xEB140101;
213
214/// Enclosure Management Bridge (SEMB).
215const SIG_SEMB: u32 = 0xC33C0101;
216
217/// Port Multiplier.
218const SIG_PM: u32 = 0x96690101;
219
220// ---------------------------------------------------------------------------
221// FIS Types
222// ---------------------------------------------------------------------------
223
224/// FIS type: Register -- Host to Device.
225const FIS_TYPE_REG_H2D: u8 = 0x27;
226
227/// FIS type: Register -- Device to Host.
228const FIS_TYPE_REG_D2H: u8 = 0x34;
229
230/// FIS type: DMA Activate -- Device to Host.
231const FIS_TYPE_DMA_ACT: u8 = 0x39;
232
233/// FIS type: DMA Setup -- Bidirectional.
234const FIS_TYPE_DMA_SETUP: u8 = 0x41;
235
236/// FIS type: Data -- Bidirectional.
237const FIS_TYPE_DATA: u8 = 0x46;
238
239/// FIS type: BIST Activate.
240const FIS_TYPE_BIST: u8 = 0x58;
241
242/// FIS type: PIO Setup -- Device to Host.
243const FIS_TYPE_PIO_SETUP: u8 = 0x5F;
244
245/// FIS type: Set Device Bits -- Device to Host.
246const FIS_TYPE_DEV_BITS: u8 = 0xA1;
247
248// ---------------------------------------------------------------------------
249// ATA commands
250// ---------------------------------------------------------------------------
251
252/// ATA READ DMA EXT (48-bit LBA).
253const ATA_CMD_READ_DMA_EXT: u8 = 0x25;
254
255/// ATA WRITE DMA EXT (48-bit LBA).
256const ATA_CMD_WRITE_DMA_EXT: u8 = 0x35;
257
258/// ATA IDENTIFY DEVICE.
259const ATA_CMD_IDENTIFY: u8 = 0xEC;
260
261/// ATA READ FPDMA QUEUED (NCQ read).
262const ATA_CMD_READ_FPDMA_QUEUED: u8 = 0x60;
263
264/// ATA WRITE FPDMA QUEUED (NCQ write).
265const ATA_CMD_WRITE_FPDMA_QUEUED: u8 = 0x61;
266
267/// ATA FLUSH CACHE EXT.
268const ATA_CMD_FLUSH_CACHE_EXT: u8 = 0xEA;
269
270// ---------------------------------------------------------------------------
271// Sector size
272// ---------------------------------------------------------------------------
273
274/// Standard sector size in bytes.
275const SECTOR_SIZE: usize = 512;
276
277/// Maximum PRDT entries per command table.
278const MAX_PRDT_ENTRIES: usize = 65535;
279
280/// Maximum sectors per single DMA transfer (limited by PRDT entry: 4MB / 512).
281const MAX_SECTORS_PER_PRDT: u32 = 8192;
282
283/// Polling timeout iterations.
284const POLL_TIMEOUT: u32 = 1_000_000;
285
286// ---------------------------------------------------------------------------
287// FIS structures
288// ---------------------------------------------------------------------------
289
290/// FIS Register -- Host to Device (20 bytes).
291#[repr(C, packed)]
292#[derive(Debug, Clone, Copy)]
293pub struct FisRegH2D {
294    /// FIS type (0x27).
295    pub fis_type: u8,
296    /// Port multiplier | Command/Control bit (bit 7).
297    pub pm_and_c: u8,
298    /// ATA command register.
299    pub command: u8,
300    /// Feature register (7:0).
301    pub feature_lo: u8,
302
303    /// LBA (7:0).
304    pub lba0: u8,
305    /// LBA (15:8).
306    pub lba1: u8,
307    /// LBA (23:16).
308    pub lba2: u8,
309    /// Device register.
310    pub device: u8,
311
312    /// LBA (31:24).
313    pub lba3: u8,
314    /// LBA (39:32).
315    pub lba4: u8,
316    /// LBA (47:40).
317    pub lba5: u8,
318    /// Feature register (15:8).
319    pub feature_hi: u8,
320
321    /// Sector count (7:0).
322    pub count_lo: u8,
323    /// Sector count (15:8).
324    pub count_hi: u8,
325    /// Isochronous command completion.
326    pub icc: u8,
327    /// Control register.
328    pub control: u8,
329
330    /// Reserved.
331    pub _reserved: [u8; 4],
332}
333
334impl Default for FisRegH2D {
335    fn default() -> Self {
336        Self::new()
337    }
338}
339
340impl FisRegH2D {
341    /// Create a new H2D Register FIS with the command bit set.
342    pub fn new() -> Self {
343        Self {
344            fis_type: FIS_TYPE_REG_H2D,
345            pm_and_c: 0x80, // Command bit set
346            command: 0,
347            feature_lo: 0,
348            lba0: 0,
349            lba1: 0,
350            lba2: 0,
351            device: 0,
352            lba3: 0,
353            lba4: 0,
354            lba5: 0,
355            feature_hi: 0,
356            count_lo: 0,
357            count_hi: 0,
358            icc: 0,
359            control: 0,
360            _reserved: [0; 4],
361        }
362    }
363
364    /// Set 48-bit LBA address.
365    pub fn set_lba(&mut self, lba: u64) {
366        self.lba0 = (lba & 0xFF) as u8;
367        self.lba1 = ((lba >> 8) & 0xFF) as u8;
368        self.lba2 = ((lba >> 16) & 0xFF) as u8;
369        self.lba3 = ((lba >> 24) & 0xFF) as u8;
370        self.lba4 = ((lba >> 32) & 0xFF) as u8;
371        self.lba5 = ((lba >> 40) & 0xFF) as u8;
372        self.device = 1 << 6; // LBA mode
373    }
374
375    /// Set sector count (16-bit).
376    pub fn set_count(&mut self, count: u16) {
377        self.count_lo = (count & 0xFF) as u8;
378        self.count_hi = ((count >> 8) & 0xFF) as u8;
379    }
380}
381
382/// FIS Register -- Device to Host (20 bytes).
383#[repr(C, packed)]
384#[derive(Debug, Clone, Copy)]
385pub struct FisRegD2H {
386    /// FIS type (0x34).
387    pub fis_type: u8,
388    /// Port multiplier | Interrupt bit (bit 6).
389    pub pm_and_i: u8,
390    /// Status register.
391    pub status: u8,
392    /// Error register.
393    pub error: u8,
394
395    /// LBA (7:0).
396    pub lba0: u8,
397    /// LBA (15:8).
398    pub lba1: u8,
399    /// LBA (23:16).
400    pub lba2: u8,
401    /// Device register.
402    pub device: u8,
403
404    /// LBA (31:24).
405    pub lba3: u8,
406    /// LBA (39:32).
407    pub lba4: u8,
408    /// LBA (47:40).
409    pub lba5: u8,
410    /// Reserved.
411    pub _reserved0: u8,
412
413    /// Sector count (7:0).
414    pub count_lo: u8,
415    /// Sector count (15:8).
416    pub count_hi: u8,
417    /// Reserved.
418    pub _reserved1: [u8; 6],
419}
420
421/// FIS DMA Setup -- Bidirectional (28 bytes).
422#[repr(C, packed)]
423#[derive(Debug, Clone, Copy)]
424pub struct FisDmaSetup {
425    /// FIS type (0x41).
426    pub fis_type: u8,
427    /// Port multiplier | Direction | Interrupt | Auto-activate.
428    pub flags: u8,
429    /// Reserved.
430    pub _reserved0: [u8; 2],
431
432    /// DMA Buffer Identifier (low).
433    pub dma_buf_id_lo: u32,
434    /// DMA Buffer Identifier (high).
435    pub dma_buf_id_hi: u32,
436
437    /// Reserved.
438    pub _reserved1: u32,
439    /// DMA buffer offset.
440    pub dma_buf_offset: u32,
441    /// Transfer count.
442    pub transfer_count: u32,
443    /// Reserved.
444    pub _reserved2: u32,
445}
446
447/// FIS PIO Setup -- Device to Host (20 bytes).
448#[repr(C, packed)]
449#[derive(Debug, Clone, Copy)]
450pub struct FisPioSetup {
451    /// FIS type (0x5F).
452    pub fis_type: u8,
453    /// Port multiplier | Direction | Interrupt.
454    pub flags: u8,
455    /// Status register.
456    pub status: u8,
457    /// Error register.
458    pub error: u8,
459
460    /// LBA (7:0).
461    pub lba0: u8,
462    /// LBA (15:8).
463    pub lba1: u8,
464    /// LBA (23:16).
465    pub lba2: u8,
466    /// Device register.
467    pub device: u8,
468
469    /// LBA (31:24).
470    pub lba3: u8,
471    /// LBA (39:32).
472    pub lba4: u8,
473    /// LBA (47:40).
474    pub lba5: u8,
475    /// Reserved.
476    pub _reserved0: u8,
477
478    /// Sector count (7:0).
479    pub count_lo: u8,
480    /// Sector count (15:8).
481    pub count_hi: u8,
482    /// Reserved.
483    pub _reserved1: u8,
484    /// New value of status register (E_Status).
485    pub e_status: u8,
486
487    /// Transfer count.
488    pub transfer_count: u16,
489    /// Reserved.
490    pub _reserved2: [u8; 2],
491}
492
493/// FIS Data -- Bidirectional (variable length, header is 4 bytes).
494#[repr(C, packed)]
495#[derive(Debug, Clone, Copy)]
496pub struct FisData {
497    /// FIS type (0x46).
498    pub fis_type: u8,
499    /// Port multiplier.
500    pub pm: u8,
501    /// Reserved.
502    pub _reserved: [u8; 2],
503    // Followed by DWORD-aligned payload data.
504}
505
506// ---------------------------------------------------------------------------
507// Received FIS structure (256 bytes, per port)
508// ---------------------------------------------------------------------------
509
510/// Received FIS area for a single port (256 bytes, 256-byte aligned).
511#[repr(C, align(256))]
512#[derive(Debug, Clone, Copy)]
513pub struct ReceivedFis {
514    /// DMA Setup FIS (offset 0x00).
515    pub dma_setup: FisDmaSetup,
516    /// Padding.
517    pub _pad0: [u8; 4],
518
519    /// PIO Setup FIS (offset 0x20).
520    pub pio_setup: FisPioSetup,
521    /// Padding.
522    pub _pad1: [u8; 12],
523
524    /// D2H Register FIS (offset 0x40).
525    pub d2h_reg: FisRegD2H,
526    /// Padding.
527    pub _pad2: [u8; 4],
528
529    /// Set Device Bits FIS (offset 0x58).
530    pub set_device_bits: [u8; 8],
531
532    /// Unknown FIS (offset 0x60).
533    pub unknown: [u8; 64],
534
535    /// Reserved (offset 0xA0).
536    pub _reserved: [u8; 96],
537}
538
539// ---------------------------------------------------------------------------
540// Command structures
541// ---------------------------------------------------------------------------
542
543/// Physical Region Descriptor Table entry (16 bytes).
544#[repr(C)]
545#[derive(Debug, Clone, Copy)]
546pub struct PrdtEntry {
547    /// Data base address (lower 32 bits, must be word-aligned).
548    pub data_base_lo: u32,
549    /// Data base address (upper 32 bits).
550    pub data_base_hi: u32,
551    /// Reserved.
552    pub _reserved: u32,
553    /// Byte count (bit 0 must be 1 for odd byte count; bits 21:0 = count - 1).
554    /// Bit 31: Interrupt on Completion.
555    pub dbc_and_flags: u32,
556}
557
558impl PrdtEntry {
559    /// Create a new PRDT entry.
560    ///
561    /// `phys_addr`: Physical address of the data buffer (must be word-aligned).
562    /// `byte_count`: Number of bytes to transfer (max 4MB per entry, must be
563    /// even). `interrupt`: If true, set the Interrupt on Completion bit.
564    pub fn new(phys_addr: u64, byte_count: u32, interrupt: bool) -> Self {
565        let mut dbc = (byte_count - 1) & 0x003F_FFFF; // bits 21:0
566        if interrupt {
567            dbc |= 1 << 31;
568        }
569        Self {
570            data_base_lo: phys_addr as u32,
571            data_base_hi: (phys_addr >> 32) as u32,
572            _reserved: 0,
573            dbc_and_flags: dbc,
574        }
575    }
576}
577
578/// Command Header (32 bytes, one per command slot).
579///
580/// The command list is an array of up to 32 command headers.
581#[repr(C)]
582#[derive(Debug, Clone, Copy)]
583pub struct CommandHeader {
584    /// DW0: Command FIS Length (bits 4:0, in DWORDs), ATAPI (bit 5),
585    /// Write direction (bit 6), Prefetchable (bit 7), Reset (bit 8),
586    /// BIST (bit 9), Clear Busy upon R_OK (bit 10), Reserved (bit 11),
587    /// Port Multiplier Port (bits 15:12), PRDT Length (bits 31:16).
588    pub flags_and_prdtl: u32,
589    /// DW1: Physical Region Descriptor Byte Count (transferred so far).
590    pub prdbc: u32,
591    /// DW2: Command Table Descriptor Base Address (lower 32 bits, 128-byte
592    /// aligned).
593    pub ctba_lo: u32,
594    /// DW3: Command Table Descriptor Base Address (upper 32 bits).
595    pub ctba_hi: u32,
596    /// DW4-7: Reserved.
597    pub _reserved: [u32; 4],
598}
599
600impl CommandHeader {
601    /// Create a new command header.
602    ///
603    /// `fis_len_dwords`: Length of the command FIS in DWORDs (typically 5 for
604    /// H2D FIS). `prdt_count`: Number of PRDT entries.
605    /// `write`: True if this is a write (host-to-device data) command.
606    /// `ctba_phys`: Physical address of the command table (128-byte aligned).
607    pub fn new(fis_len_dwords: u8, prdt_count: u16, write: bool, ctba_phys: u64) -> Self {
608        let mut flags = (fis_len_dwords as u32) & 0x1F;
609        if write {
610            flags |= 1 << 6; // W bit
611        }
612        flags |= (prdt_count as u32) << 16;
613
614        Self {
615            flags_and_prdtl: flags,
616            prdbc: 0,
617            ctba_lo: ctba_phys as u32,
618            ctba_hi: (ctba_phys >> 32) as u32,
619            _reserved: [0; 4],
620        }
621    }
622}
623
624/// Command Table (variable size: 128-byte header + PRDT entries).
625///
626/// The header contains the Command FIS (64 bytes), ATAPI Command (16 bytes),
627/// and reserved space (48 bytes). Followed by PRDT entries.
628#[repr(C)]
629#[derive(Debug, Clone, Copy)]
630pub struct CommandTableHeader {
631    /// Command FIS (up to 64 bytes).
632    pub cfis: [u8; 64],
633    /// ATAPI Command (12 or 16 bytes, zero-padded to 16).
634    pub acmd: [u8; 16],
635    /// Reserved.
636    pub _reserved: [u8; 48],
637    // Followed by PRDT entries (PrdtEntry array).
638}
639
640impl Default for CommandTableHeader {
641    fn default() -> Self {
642        Self::new()
643    }
644}
645
646impl CommandTableHeader {
647    /// Create a zeroed command table header.
648    pub fn new() -> Self {
649        Self {
650            cfis: [0; 64],
651            acmd: [0; 16],
652            _reserved: [0; 48],
653        }
654    }
655
656    /// Write a Register H2D FIS into the command FIS area.
657    pub fn set_h2d_fis(&mut self, fis: &FisRegH2D) {
658        let fis_bytes =
659            unsafe { core::slice::from_raw_parts(fis as *const FisRegH2D as *const u8, 20) };
660        self.cfis[..20].copy_from_slice(fis_bytes);
661    }
662}
663
664// ---------------------------------------------------------------------------
665// Device type identification
666// ---------------------------------------------------------------------------
667
668/// Type of device attached to an AHCI port.
669#[derive(Debug, Clone, Copy, PartialEq, Eq)]
670pub enum AhciDeviceType {
671    /// No device detected.
672    None,
673    /// SATA drive (hard disk or SSD).
674    Sata,
675    /// SATAPI device (optical drive, etc.).
676    Satapi,
677    /// Enclosure Management Bridge.
678    Semb,
679    /// Port Multiplier.
680    PortMultiplier,
681    /// Unknown signature.
682    Unknown(u32),
683}
684
685impl AhciDeviceType {
686    /// Determine device type from port signature register value.
687    pub fn from_signature(sig: u32) -> Self {
688        match sig {
689            SIG_SATA => Self::Sata,
690            SIG_SATAPI => Self::Satapi,
691            SIG_SEMB => Self::Semb,
692            SIG_PM => Self::PortMultiplier,
693            0xFFFFFFFF | 0x00000000 => Self::None,
694            other => Self::Unknown(other),
695        }
696    }
697}
698
699// ---------------------------------------------------------------------------
700// Port state
701// ---------------------------------------------------------------------------
702
703/// State of an AHCI port.
704#[derive(Debug, Clone, Copy, PartialEq, Eq)]
705pub enum PortState {
706    /// Port is not implemented or no device.
707    Inactive,
708    /// Device detected, not yet initialized.
709    Detected,
710    /// Port fully initialized and ready for commands.
711    Ready,
712    /// Port encountered an error.
713    Error,
714}
715
716// ---------------------------------------------------------------------------
717// AHCI Port
718// ---------------------------------------------------------------------------
719
720/// Represents a single AHCI port with its state and device info.
721#[derive(Debug, Clone, Copy)]
722pub struct AhciPort {
723    /// Port number (0-31).
724    pub port_num: u8,
725    /// Base address for this port's MMIO registers.
726    pub port_mmio_base: usize,
727    /// Device type detected on this port.
728    pub device_type: AhciDeviceType,
729    /// Current port state.
730    pub state: PortState,
731    /// Number of command slots supported by the HBA.
732    pub num_cmd_slots: u8,
733    /// Whether NCQ is supported by the HBA.
734    pub ncq_supported: bool,
735    /// Total sectors reported by IDENTIFY DEVICE (0 until identified).
736    pub total_sectors: u64,
737    /// Sector size in bytes (default 512).
738    pub sector_size: usize,
739}
740
741impl AhciPort {
742    /// Create a new port descriptor.
743    pub fn new(port_num: u8, hba_mmio_base: usize, num_cmd_slots: u8, ncq_supported: bool) -> Self {
744        Self {
745            port_num,
746            port_mmio_base: hba_mmio_base + PORT_BASE + (port_num as usize) * PORT_SIZE,
747            device_type: AhciDeviceType::None,
748            state: PortState::Inactive,
749            num_cmd_slots,
750            ncq_supported,
751            total_sectors: 0,
752            sector_size: SECTOR_SIZE,
753        }
754    }
755
756    /// Read a 32-bit port register.
757    fn read_port_reg(&self, offset: usize) -> u32 {
758        // SAFETY: Reading an AHCI port MMIO register. The port_mmio_base was
759        // computed from a validated PCI BAR5 address. read_volatile ensures
760        // the compiler does not elide or reorder this hardware register access.
761        unsafe { core::ptr::read_volatile((self.port_mmio_base + offset) as *const u32) }
762    }
763
764    /// Write a 32-bit port register.
765    fn write_port_reg(&self, offset: usize, value: u32) {
766        // SAFETY: Writing an AHCI port MMIO register. Same invariants as
767        // read_port_reg.
768        unsafe { core::ptr::write_volatile((self.port_mmio_base + offset) as *mut u32, value) }
769    }
770
771    /// Detect whether a device is present on this port.
772    pub fn detect_device(&mut self) {
773        let ssts = self.read_port_reg(PORT_SSTS);
774        let det = ssts & SSTS_DET_MASK;
775        let ipm = ssts & SSTS_IPM_MASK;
776
777        if det != SSTS_DET_PRESENT || ipm != SSTS_IPM_ACTIVE {
778            self.device_type = AhciDeviceType::None;
779            self.state = PortState::Inactive;
780            return;
781        }
782
783        let sig = self.read_port_reg(PORT_SIG);
784        self.device_type = AhciDeviceType::from_signature(sig);
785        self.state = PortState::Detected;
786    }
787
788    /// Stop the port command engine (clear ST and FRE, wait for CR and FR to
789    /// clear).
790    pub fn stop_cmd(&self) -> Result<(), KernelError> {
791        let mut cmd = self.read_port_reg(PORT_CMD);
792
793        // Clear ST (Start)
794        cmd &= !PORT_CMD_ST;
795        self.write_port_reg(PORT_CMD, cmd);
796
797        // Wait for CR (Command List Running) to clear
798        let mut timeout = POLL_TIMEOUT;
799        while self.read_port_reg(PORT_CMD) & PORT_CMD_CR != 0 && timeout > 0 {
800            timeout -= 1;
801            core::hint::spin_loop();
802        }
803        if timeout == 0 {
804            return Err(KernelError::Timeout {
805                operation: "ahci_stop_cmd_cr",
806                duration_ms: 0,
807            });
808        }
809
810        // Clear FRE (FIS Receive Enable)
811        cmd = self.read_port_reg(PORT_CMD);
812        cmd &= !PORT_CMD_FRE;
813        self.write_port_reg(PORT_CMD, cmd);
814
815        // Wait for FR (FIS Receive Running) to clear
816        timeout = POLL_TIMEOUT;
817        while self.read_port_reg(PORT_CMD) & PORT_CMD_FR != 0 && timeout > 0 {
818            timeout -= 1;
819            core::hint::spin_loop();
820        }
821        if timeout == 0 {
822            return Err(KernelError::Timeout {
823                operation: "ahci_stop_cmd_fr",
824                duration_ms: 0,
825            });
826        }
827
828        Ok(())
829    }
830
831    /// Start the port command engine (set FRE then ST).
832    pub fn start_cmd(&self) {
833        // Wait until CR is clear before starting
834        while self.read_port_reg(PORT_CMD) & PORT_CMD_CR != 0 {
835            core::hint::spin_loop();
836        }
837
838        let mut cmd = self.read_port_reg(PORT_CMD);
839        cmd |= PORT_CMD_FRE;
840        self.write_port_reg(PORT_CMD, cmd);
841
842        cmd = self.read_port_reg(PORT_CMD);
843        cmd |= PORT_CMD_ST;
844        self.write_port_reg(PORT_CMD, cmd);
845    }
846
847    /// Clear the port SERR register (write-1-to-clear all bits).
848    pub fn clear_serr(&self) {
849        self.write_port_reg(PORT_SERR, 0xFFFF_FFFF);
850    }
851
852    /// Clear the port interrupt status register.
853    pub fn clear_interrupt_status(&self) {
854        self.write_port_reg(PORT_IS, 0xFFFF_FFFF);
855    }
856
857    /// Wait for the port to become not busy (TFD BSY and DRQ clear).
858    pub fn wait_ready(&self) -> Result<(), KernelError> {
859        let mut timeout = POLL_TIMEOUT;
860        while timeout > 0 {
861            let tfd = self.read_port_reg(PORT_TFD);
862            if tfd & (TFD_BSY | TFD_DRQ) == 0 {
863                return Ok(());
864            }
865            timeout -= 1;
866            core::hint::spin_loop();
867        }
868        Err(KernelError::Timeout {
869            operation: "ahci_port_ready",
870            duration_ms: 0,
871        })
872    }
873
874    /// Find a free command slot.
875    ///
876    /// Returns the slot index (0-based), or an error if all slots are occupied.
877    pub fn find_free_slot(&self) -> Result<u8, KernelError> {
878        let ci = self.read_port_reg(PORT_CI);
879        let sact = self.read_port_reg(PORT_SACT);
880        let occupied = ci | sact;
881
882        for slot in 0..self.num_cmd_slots {
883            if occupied & (1 << slot) == 0 {
884                return Ok(slot);
885            }
886        }
887
888        Err(KernelError::ResourceExhausted {
889            resource: "ahci_command_slots",
890        })
891    }
892
893    /// Issue a command in the given slot and wait for completion.
894    ///
895    /// Sets the corresponding bit in PORT_CI and polls until it clears or
896    /// an error is detected in PORT_TFD.
897    pub fn issue_command_and_wait(&self, slot: u8) -> Result<(), KernelError> {
898        // Clear any pending errors
899        self.clear_serr();
900        self.clear_interrupt_status();
901
902        // Issue command
903        self.write_port_reg(PORT_CI, 1 << slot);
904
905        // Poll for completion
906        let mut timeout = POLL_TIMEOUT;
907        loop {
908            let ci = self.read_port_reg(PORT_CI);
909            if ci & (1 << slot) == 0 {
910                // Command completed
911                break;
912            }
913
914            let tfd = self.read_port_reg(PORT_TFD);
915            if tfd & TFD_ERR != 0 {
916                return Err(KernelError::HardwareError {
917                    device: "ahci",
918                    code: tfd,
919                });
920            }
921
922            timeout -= 1;
923            if timeout == 0 {
924                return Err(KernelError::Timeout {
925                    operation: "ahci_command_completion",
926                    duration_ms: 0,
927                });
928            }
929            core::hint::spin_loop();
930        }
931
932        // Check final status
933        let tfd = self.read_port_reg(PORT_TFD);
934        if tfd & TFD_ERR != 0 {
935            return Err(KernelError::HardwareError {
936                device: "ahci",
937                code: tfd,
938            });
939        }
940
941        Ok(())
942    }
943
944    /// Build and issue a READ DMA EXT command.
945    ///
946    /// `lba`: Starting logical block address.
947    /// `sector_count`: Number of sectors to read (1-65535, 0 means 65536).
948    /// `buffer_phys`: Physical address of the destination buffer.
949    ///
950    /// This prepares the FIS, command header, PRDT, and issues the command.
951    /// In a production system, the command list and command tables would be
952    /// allocated from DMA-capable memory; here we describe the operation
953    /// structurally.
954    pub fn build_read_dma_ext(
955        &self,
956        lba: u64,
957        sector_count: u16,
958        buffer_phys: u64,
959    ) -> (CommandHeader, CommandTableHeader, PrdtEntry) {
960        let mut fis = FisRegH2D::new();
961        fis.command = ATA_CMD_READ_DMA_EXT;
962        fis.set_lba(lba);
963        fis.set_count(sector_count);
964
965        let mut ct = CommandTableHeader::new();
966        ct.set_h2d_fis(&fis);
967
968        let byte_count = (sector_count as u32) * (self.sector_size as u32);
969        let prdt = PrdtEntry::new(buffer_phys, byte_count, true);
970
971        // Command header: FIS length = 5 DWORDs (20 bytes / 4), 1 PRDT entry,
972        // not a write, command table physical address = 0 (placeholder).
973        let cmd_hdr = CommandHeader::new(5, 1, false, 0);
974
975        (cmd_hdr, ct, prdt)
976    }
977
978    /// Build and issue a WRITE DMA EXT command.
979    ///
980    /// `lba`: Starting logical block address.
981    /// `sector_count`: Number of sectors to write.
982    /// `buffer_phys`: Physical address of the source buffer.
983    pub fn build_write_dma_ext(
984        &self,
985        lba: u64,
986        sector_count: u16,
987        buffer_phys: u64,
988    ) -> (CommandHeader, CommandTableHeader, PrdtEntry) {
989        let mut fis = FisRegH2D::new();
990        fis.command = ATA_CMD_WRITE_DMA_EXT;
991        fis.set_lba(lba);
992        fis.set_count(sector_count);
993
994        let mut ct = CommandTableHeader::new();
995        ct.set_h2d_fis(&fis);
996
997        let byte_count = (sector_count as u32) * (self.sector_size as u32);
998        let prdt = PrdtEntry::new(buffer_phys, byte_count, true);
999
1000        // Write bit set in command header
1001        let cmd_hdr = CommandHeader::new(5, 1, true, 0);
1002
1003        (cmd_hdr, ct, prdt)
1004    }
1005}
1006
1007// ---------------------------------------------------------------------------
1008// NCQ (Native Command Queuing) stubs
1009// ---------------------------------------------------------------------------
1010
1011/// NCQ command tag (0-31).
1012pub type NcqTag = u8;
1013
1014/// Build a READ FPDMA QUEUED (NCQ) FIS.
1015///
1016/// NCQ uses the SATA Active (SActive) register rather than Command Issue.
1017/// The tag is encoded in the sector count register (bits 7:3).
1018pub fn build_ncq_read_fis(lba: u64, sector_count: u16, tag: NcqTag) -> FisRegH2D {
1019    let mut fis = FisRegH2D::new();
1020    fis.command = ATA_CMD_READ_FPDMA_QUEUED;
1021    fis.set_lba(lba);
1022    // NCQ: sector count goes in feature register
1023    fis.feature_lo = (sector_count & 0xFF) as u8;
1024    fis.feature_hi = ((sector_count >> 8) & 0xFF) as u8;
1025    // Tag in count register bits 7:3
1026    fis.count_lo = (tag & 0x1F) << 3;
1027    fis.count_hi = 0;
1028    fis.device = 1 << 6; // LBA mode
1029    fis
1030}
1031
1032/// Build a WRITE FPDMA QUEUED (NCQ) FIS.
1033pub fn build_ncq_write_fis(lba: u64, sector_count: u16, tag: NcqTag) -> FisRegH2D {
1034    let mut fis = FisRegH2D::new();
1035    fis.command = ATA_CMD_WRITE_FPDMA_QUEUED;
1036    fis.set_lba(lba);
1037    fis.feature_lo = (sector_count & 0xFF) as u8;
1038    fis.feature_hi = ((sector_count >> 8) & 0xFF) as u8;
1039    fis.count_lo = (tag & 0x1F) << 3;
1040    fis.count_hi = 0;
1041    fis.device = 1 << 6; // LBA mode
1042    fis
1043}
1044
1045/// Issue an NCQ command on a port (stub).
1046///
1047/// In a full implementation, this would:
1048/// 1. Find a free NCQ tag (0-31).
1049/// 2. Build the FPDMA FIS and set up the command table.
1050/// 3. Set the corresponding bit in PORT_SACT (SActive).
1051/// 4. Set the corresponding bit in PORT_CI (Command Issue).
1052/// 5. Return the tag for later completion tracking.
1053pub fn issue_ncq_command(_port: &AhciPort, _fis: &FisRegH2D) -> Result<NcqTag, KernelError> {
1054    Err(KernelError::NotImplemented {
1055        feature: "ahci_ncq_command_issue",
1056    })
1057}
1058
1059/// Poll for NCQ command completion on a port (stub).
1060///
1061/// Checks PORT_SACT to determine which tags have completed.
1062pub fn poll_ncq_completion(_port: &AhciPort) -> u32 {
1063    // In a real implementation: return ~PORT_SACT & issued_tags
1064    0
1065}
1066
1067// ---------------------------------------------------------------------------
1068// AHCI Controller
1069// ---------------------------------------------------------------------------
1070
1071/// AHCI Host Bus Adapter controller.
1072#[cfg(feature = "alloc")]
1073pub struct AhciController {
1074    /// MMIO base address (BAR5 / ABAR mapped to virtual address).
1075    mmio_base: usize,
1076    /// HBA capabilities register value.
1077    capabilities: u32,
1078    /// Number of ports supported by this HBA.
1079    num_ports: u8,
1080    /// Number of command slots per port.
1081    num_cmd_slots: u8,
1082    /// Whether NCQ is supported.
1083    ncq_supported: bool,
1084    /// Whether 64-bit addressing is supported.
1085    supports_64bit: bool,
1086    /// AHCI version (major.minor packed as u32).
1087    version: u32,
1088    /// Detected ports.
1089    ports: Vec<AhciPort>,
1090}
1091
1092#[cfg(feature = "alloc")]
1093impl AhciController {
1094    /// Create a new AHCI controller from a mapped MMIO base address.
1095    pub fn new(mmio_base: usize) -> Result<Self, KernelError> {
1096        let mut ctrl = Self {
1097            mmio_base,
1098            capabilities: 0,
1099            num_ports: 0,
1100            num_cmd_slots: 0,
1101            ncq_supported: false,
1102            supports_64bit: false,
1103            version: 0,
1104            ports: Vec::new(),
1105        };
1106
1107        ctrl.initialize()?;
1108        Ok(ctrl)
1109    }
1110
1111    /// Read a 32-bit HBA register.
1112    fn read_hba_reg(&self, offset: usize) -> u32 {
1113        // SAFETY: Reading an AHCI HBA MMIO register at mmio_base + offset.
1114        // The mmio_base was derived from a PCI BAR5 address. read_volatile
1115        // ensures the compiler does not elide or reorder this hardware access.
1116        unsafe { core::ptr::read_volatile((self.mmio_base + offset) as *const u32) }
1117    }
1118
1119    /// Write a 32-bit HBA register.
1120    fn write_hba_reg(&self, offset: usize, value: u32) {
1121        // SAFETY: Writing an AHCI HBA MMIO register. Same invariants as
1122        // read_hba_reg.
1123        unsafe { core::ptr::write_volatile((self.mmio_base + offset) as *mut u32, value) }
1124    }
1125
1126    /// Initialize the HBA: read capabilities, enable AHCI mode, detect ports.
1127    fn initialize(&mut self) -> Result<(), KernelError> {
1128        crate::println!("[AHCI] Initializing controller at 0x{:x}", self.mmio_base);
1129
1130        // Read version
1131        self.version = self.read_hba_reg(HBA_REG_VS);
1132        let major = (self.version >> 16) & 0xFFFF;
1133        let minor = self.version & 0xFFFF;
1134        crate::println!("[AHCI] Version: {}.{}", major, minor);
1135
1136        // Read capabilities
1137        self.capabilities = self.read_hba_reg(HBA_REG_CAP);
1138        self.num_ports = ((self.capabilities & CAP_NP_MASK) + 1) as u8;
1139        self.num_cmd_slots = (((self.capabilities >> CAP_NCS_SHIFT) & CAP_NCS_MASK) + 1) as u8;
1140        self.ncq_supported = self.capabilities & CAP_SNCQ != 0;
1141        self.supports_64bit = self.capabilities & CAP_S64A != 0;
1142
1143        crate::println!(
1144            "[AHCI] Ports: {}, Command Slots: {}, NCQ: {}, 64-bit: {}",
1145            self.num_ports,
1146            self.num_cmd_slots,
1147            self.ncq_supported,
1148            self.supports_64bit,
1149        );
1150
1151        // Enable AHCI mode (set GHC.AE)
1152        let ghc = self.read_hba_reg(HBA_REG_GHC);
1153        if ghc & GHC_AE == 0 {
1154            self.write_hba_reg(HBA_REG_GHC, ghc | GHC_AE);
1155            crate::println!("[AHCI] Enabled AHCI mode");
1156        }
1157
1158        // Perform HBA reset
1159        self.reset()?;
1160
1161        // Re-enable AHCI mode after reset
1162        let ghc = self.read_hba_reg(HBA_REG_GHC);
1163        self.write_hba_reg(HBA_REG_GHC, ghc | GHC_AE);
1164
1165        // Enable global interrupts
1166        let ghc = self.read_hba_reg(HBA_REG_GHC);
1167        self.write_hba_reg(HBA_REG_GHC, ghc | GHC_IE);
1168
1169        // Clear global interrupt status
1170        self.write_hba_reg(HBA_REG_IS, 0xFFFF_FFFF);
1171
1172        // Detect ports
1173        self.detect_ports();
1174
1175        crate::println!(
1176            "[AHCI] Controller initialized ({} active port(s))",
1177            self.ports
1178                .iter()
1179                .filter(|p| p.state != PortState::Inactive)
1180                .count()
1181        );
1182
1183        Ok(())
1184    }
1185
1186    /// Perform an HBA reset (GHC.HR = 1, wait for it to clear).
1187    fn reset(&self) -> Result<(), KernelError> {
1188        let ghc = self.read_hba_reg(HBA_REG_GHC);
1189        self.write_hba_reg(HBA_REG_GHC, ghc | GHC_HR);
1190
1191        // Wait for HR bit to clear (HBA reset complete)
1192        let mut timeout = POLL_TIMEOUT;
1193        while self.read_hba_reg(HBA_REG_GHC) & GHC_HR != 0 && timeout > 0 {
1194            timeout -= 1;
1195            core::hint::spin_loop();
1196        }
1197
1198        if timeout == 0 {
1199            return Err(KernelError::Timeout {
1200                operation: "ahci_hba_reset",
1201                duration_ms: 0,
1202            });
1203        }
1204
1205        crate::println!("[AHCI] HBA reset complete");
1206        Ok(())
1207    }
1208
1209    /// Detect devices on all implemented ports.
1210    fn detect_ports(&mut self) {
1211        let pi = self.read_hba_reg(HBA_REG_PI);
1212        crate::println!("[AHCI] Ports Implemented bitmask: 0x{:08x}", pi);
1213
1214        for port_num in 0..32u8 {
1215            if pi & (1 << port_num) == 0 {
1216                continue;
1217            }
1218
1219            let mut port = AhciPort::new(
1220                port_num,
1221                self.mmio_base,
1222                self.num_cmd_slots,
1223                self.ncq_supported,
1224            );
1225            port.detect_device();
1226
1227            match port.device_type {
1228                AhciDeviceType::Sata => {
1229                    crate::println!("[AHCI] Port {}: SATA drive detected", port_num);
1230                }
1231                AhciDeviceType::Satapi => {
1232                    crate::println!("[AHCI] Port {}: SATAPI device detected", port_num);
1233                }
1234                AhciDeviceType::Semb => {
1235                    crate::println!("[AHCI] Port {}: SEMB detected", port_num);
1236                }
1237                AhciDeviceType::PortMultiplier => {
1238                    crate::println!("[AHCI] Port {}: Port Multiplier detected", port_num);
1239                }
1240                AhciDeviceType::Unknown(sig) => {
1241                    crate::println!(
1242                        "[AHCI] Port {}: Unknown device (sig=0x{:08x})",
1243                        port_num,
1244                        sig
1245                    );
1246                }
1247                AhciDeviceType::None => {}
1248            }
1249
1250            self.ports.push(port);
1251        }
1252    }
1253
1254    /// Get all detected SATA ports.
1255    pub fn sata_ports(&self) -> Vec<&AhciPort> {
1256        self.ports
1257            .iter()
1258            .filter(|p| p.device_type == AhciDeviceType::Sata)
1259            .collect()
1260    }
1261
1262    /// Get a specific port by number.
1263    pub fn port(&self, port_num: u8) -> Option<&AhciPort> {
1264        self.ports.iter().find(|p| p.port_num == port_num)
1265    }
1266
1267    /// Get a mutable reference to a specific port by number.
1268    pub fn port_mut(&mut self, port_num: u8) -> Option<&mut AhciPort> {
1269        self.ports.iter_mut().find(|p| p.port_num == port_num)
1270    }
1271
1272    /// Get the number of detected ports.
1273    pub fn port_count(&self) -> usize {
1274        self.ports.len()
1275    }
1276
1277    /// Get the AHCI version as (major, minor).
1278    pub fn version(&self) -> (u16, u16) {
1279        ((self.version >> 16) as u16, self.version as u16)
1280    }
1281}
1282
1283// ---------------------------------------------------------------------------
1284// BlockDevice trait implementation for AHCI ports
1285// ---------------------------------------------------------------------------
1286
1287/// Wrapper around an AhciPort for the BlockDevice trait.
1288///
1289/// In a production system, this would hold references to DMA-allocated
1290/// command list and received FIS areas. For now, it provides the structural
1291/// interface.
1292#[cfg(feature = "alloc")]
1293pub struct AhciBlockDevice {
1294    /// Port descriptor (contains MMIO base, device info).
1295    port: AhciPort,
1296    /// Device name (e.g., "sda").
1297    name: String,
1298}
1299
1300#[cfg(feature = "alloc")]
1301impl AhciBlockDevice {
1302    /// Create a new AHCI block device from a port.
1303    pub fn new(port: AhciPort, name: String) -> Self {
1304        Self { port, name }
1305    }
1306
1307    /// Get a reference to the underlying port.
1308    pub fn port(&self) -> &AhciPort {
1309        &self.port
1310    }
1311}
1312
1313#[cfg(feature = "alloc")]
1314impl BlockDevice for AhciBlockDevice {
1315    fn name(&self) -> &str {
1316        &self.name
1317    }
1318
1319    fn block_size(&self) -> usize {
1320        self.port.sector_size
1321    }
1322
1323    fn block_count(&self) -> u64 {
1324        self.port.total_sectors
1325    }
1326
1327    fn read_blocks(&self, start_block: u64, buffer: &mut [u8]) -> Result<(), KernelError> {
1328        if buffer.is_empty() {
1329            return Ok(());
1330        }
1331
1332        if !buffer.len().is_multiple_of(self.port.sector_size) {
1333            return Err(KernelError::InvalidArgument {
1334                name: "buffer_length",
1335                value: "not_multiple_of_sector_size",
1336            });
1337        }
1338
1339        if self.port.total_sectors > 0 {
1340            let sector_count = (buffer.len() / self.port.sector_size) as u64;
1341            if start_block + sector_count > self.port.total_sectors {
1342                return Err(KernelError::InvalidArgument {
1343                    name: "block_range",
1344                    value: "out_of_bounds",
1345                });
1346            }
1347        }
1348
1349        // In a full implementation:
1350        // 1. Find a free command slot
1351        // 2. Build the READ DMA EXT command (FIS + PRDT pointing to buffer's phys addr)
1352        // 3. Write the command header into the command list
1353        // 4. Issue the command and wait for completion
1354        // 5. Data arrives in the buffer via DMA
1355        //
1356        // For now, report not implemented since DMA buffer allocation is required.
1357        Err(KernelError::NotImplemented {
1358            feature: "ahci_read_dma",
1359        })
1360    }
1361
1362    fn write_blocks(&mut self, start_block: u64, buffer: &[u8]) -> Result<(), KernelError> {
1363        if buffer.is_empty() {
1364            return Ok(());
1365        }
1366
1367        if !buffer.len().is_multiple_of(self.port.sector_size) {
1368            return Err(KernelError::InvalidArgument {
1369                name: "buffer_length",
1370                value: "not_multiple_of_sector_size",
1371            });
1372        }
1373
1374        if self.port.total_sectors > 0 {
1375            let sector_count = (buffer.len() / self.port.sector_size) as u64;
1376            if start_block + sector_count > self.port.total_sectors {
1377                return Err(KernelError::InvalidArgument {
1378                    name: "block_range",
1379                    value: "out_of_bounds",
1380                });
1381            }
1382        }
1383
1384        // Same as read_blocks -- requires DMA buffer allocation.
1385        Err(KernelError::NotImplemented {
1386            feature: "ahci_write_dma",
1387        })
1388    }
1389
1390    fn flush(&mut self) -> Result<(), KernelError> {
1391        // Would issue ATA FLUSH CACHE EXT command.
1392        Ok(())
1393    }
1394}
1395
1396// ---------------------------------------------------------------------------
1397// Global initialization
1398// ---------------------------------------------------------------------------
1399
1400/// Whether the AHCI subsystem has been initialized.
1401static AHCI_INITIALIZED: AtomicBool = AtomicBool::new(false);
1402
1403/// Discover and initialize AHCI controllers via PCI bus enumeration.
1404///
1405/// Scans the PCI bus for Mass Storage controllers with SATA subclass and
1406/// AHCI programming interface (class 0x01, subclass 0x06, prog-if 0x01).
1407pub fn init() -> Result<(), KernelError> {
1408    if AHCI_INITIALIZED.swap(true, Ordering::SeqCst) {
1409        return Ok(());
1410    }
1411
1412    crate::println!("[AHCI] Scanning PCI bus for AHCI controllers...");
1413
1414    #[cfg(target_arch = "x86_64")]
1415    {
1416        let pci_bus = crate::drivers::pci::get_pci_bus().lock();
1417        let storage_devices =
1418            pci_bus.find_devices_by_class(crate::drivers::pci::class_codes::MASS_STORAGE);
1419
1420        let mut ahci_count = 0u32;
1421        for dev in &storage_devices {
1422            if dev.subclass == PCI_SUBCLASS_SATA && dev.prog_if == PCI_PROGIF_AHCI {
1423                ahci_count += 1;
1424                crate::println!(
1425                    "[AHCI] Found AHCI controller: {:04x}:{:04x} at {}:{}.{}",
1426                    dev.vendor_id,
1427                    dev.device_id,
1428                    dev.location.bus,
1429                    dev.location.device,
1430                    dev.location.function,
1431                );
1432
1433                // AHCI uses BAR5 (ABAR) for MMIO registers
1434                if let Some(bar5) = dev.bars.get(5) {
1435                    match bar5 {
1436                        crate::drivers::pci::PciBar::Memory { address, size, .. } => {
1437                            crate::println!(
1438                                "[AHCI]   BAR5 (ABAR): MMIO at {:#x}, size {:#x}",
1439                                address,
1440                                size
1441                            );
1442
1443                            #[cfg(target_os = "none")]
1444                            {
1445                                let virt_base = crate::mm::phys_to_virt_addr(*address) as usize;
1446                                match AhciController::new(virt_base) {
1447                                    Ok(ctrl) => {
1448                                        let (maj, min) = ctrl.version();
1449                                        crate::println!(
1450                                            "[AHCI] Controller v{}.{} with {} port(s)",
1451                                            maj,
1452                                            min,
1453                                            ctrl.port_count()
1454                                        );
1455                                    }
1456                                    Err(e) => {
1457                                        crate::println!("[AHCI] Controller init failed: {:?}", e);
1458                                    }
1459                                }
1460                            }
1461                        }
1462                        crate::drivers::pci::PciBar::Io { address, size } => {
1463                            crate::println!(
1464                                "[AHCI]   BAR5: I/O at {:#x}, size {:#x} (unexpected)",
1465                                address,
1466                                size
1467                            );
1468                        }
1469                        crate::drivers::pci::PciBar::None => {
1470                            crate::println!("[AHCI]   BAR5: not configured");
1471                        }
1472                    }
1473                } else {
1474                    crate::println!("[AHCI]   No BAR5 found (need at least 6 BARs)");
1475                }
1476            }
1477        }
1478
1479        if ahci_count == 0 {
1480            crate::println!("[AHCI] No AHCI controllers found on PCI bus");
1481        } else {
1482            crate::println!("[AHCI] Found {} AHCI controller(s)", ahci_count);
1483        }
1484    }
1485
1486    #[cfg(not(target_arch = "x86_64"))]
1487    {
1488        crate::println!("[AHCI] AHCI PCI scanning not available on this architecture");
1489    }
1490
1491    Ok(())
1492}
1493
1494/// Check whether the AHCI subsystem has been initialized.
1495pub fn is_initialized() -> bool {
1496    AHCI_INITIALIZED.load(Ordering::Relaxed)
1497}
1498
1499// ---------------------------------------------------------------------------
1500// Tests
1501// ---------------------------------------------------------------------------
1502
1503#[cfg(test)]
1504mod tests {
1505    use super::*;
1506
1507    #[test]
1508    fn test_fis_reg_h2d_size() {
1509        assert_eq!(core::mem::size_of::<FisRegH2D>(), 20);
1510    }
1511
1512    #[test]
1513    fn test_fis_reg_d2h_size() {
1514        assert_eq!(core::mem::size_of::<FisRegD2H>(), 20);
1515    }
1516
1517    #[test]
1518    fn test_fis_dma_setup_size() {
1519        assert_eq!(core::mem::size_of::<FisDmaSetup>(), 28);
1520    }
1521
1522    #[test]
1523    fn test_fis_pio_setup_size() {
1524        assert_eq!(core::mem::size_of::<FisPioSetup>(), 20);
1525    }
1526
1527    #[test]
1528    fn test_prdt_entry_size() {
1529        assert_eq!(core::mem::size_of::<PrdtEntry>(), 16);
1530    }
1531
1532    #[test]
1533    fn test_command_header_size() {
1534        assert_eq!(core::mem::size_of::<CommandHeader>(), 32);
1535    }
1536
1537    #[test]
1538    fn test_command_table_header_size() {
1539        assert_eq!(core::mem::size_of::<CommandTableHeader>(), 128);
1540    }
1541
1542    #[test]
1543    fn test_received_fis_size() {
1544        assert_eq!(core::mem::size_of::<ReceivedFis>(), 256);
1545    }
1546
1547    #[test]
1548    fn test_device_type_from_signature() {
1549        assert_eq!(
1550            AhciDeviceType::from_signature(SIG_SATA),
1551            AhciDeviceType::Sata
1552        );
1553        assert_eq!(
1554            AhciDeviceType::from_signature(SIG_SATAPI),
1555            AhciDeviceType::Satapi
1556        );
1557        assert_eq!(
1558            AhciDeviceType::from_signature(SIG_SEMB),
1559            AhciDeviceType::Semb
1560        );
1561        assert_eq!(
1562            AhciDeviceType::from_signature(SIG_PM),
1563            AhciDeviceType::PortMultiplier
1564        );
1565        assert_eq!(
1566            AhciDeviceType::from_signature(0xFFFFFFFF),
1567            AhciDeviceType::None
1568        );
1569        assert_eq!(
1570            AhciDeviceType::from_signature(0x00000000),
1571            AhciDeviceType::None
1572        );
1573        assert_eq!(
1574            AhciDeviceType::from_signature(0xDEADBEEF),
1575            AhciDeviceType::Unknown(0xDEADBEEF)
1576        );
1577    }
1578
1579    #[test]
1580    fn test_fis_h2d_lba_encoding() {
1581        let mut fis = FisRegH2D::new();
1582        fis.set_lba(0x0000_1234_5678_9ABC);
1583
1584        assert_eq!(fis.lba0, 0xBC);
1585        assert_eq!(fis.lba1, 0x9A);
1586        assert_eq!(fis.lba2, 0x78);
1587        assert_eq!(fis.lba3, 0x56);
1588        assert_eq!(fis.lba4, 0x34);
1589        assert_eq!(fis.lba5, 0x12);
1590        assert_eq!(fis.device & (1 << 6), 1 << 6); // LBA mode bit
1591    }
1592
1593    #[test]
1594    fn test_fis_h2d_count_encoding() {
1595        let mut fis = FisRegH2D::new();
1596        fis.set_count(0xABCD);
1597
1598        assert_eq!(fis.count_lo, 0xCD);
1599        assert_eq!(fis.count_hi, 0xAB);
1600    }
1601
1602    #[test]
1603    fn test_prdt_entry_construction() {
1604        let prdt = PrdtEntry::new(0x1000_0000_DEAD_0000, 4096, true);
1605
1606        assert_eq!(prdt.data_base_lo, 0xDEAD_0000);
1607        assert_eq!(prdt.data_base_hi, 0x1000_0000);
1608        // byte_count - 1 = 4095 = 0xFFF, with IOC bit 31 set
1609        assert_eq!(prdt.dbc_and_flags, 0x8000_0FFF);
1610    }
1611
1612    #[test]
1613    fn test_prdt_entry_no_interrupt() {
1614        let prdt = PrdtEntry::new(0x0000_0000_0001_0000, 512, false);
1615
1616        assert_eq!(prdt.data_base_lo, 0x0001_0000);
1617        assert_eq!(prdt.data_base_hi, 0);
1618        // 512 - 1 = 511 = 0x1FF, no IOC
1619        assert_eq!(prdt.dbc_and_flags, 0x0000_01FF);
1620    }
1621
1622    #[test]
1623    fn test_command_header_construction() {
1624        let hdr = CommandHeader::new(5, 3, true, 0x0000_0001_0000_0080);
1625
1626        // flags: FIS len = 5, Write bit (6), PRDT count = 3 in bits 31:16
1627        let expected_flags = 5 | (1 << 6) | (3 << 16);
1628        assert_eq!(hdr.flags_and_prdtl, expected_flags);
1629        assert_eq!(hdr.ctba_lo, 0x0000_0080);
1630        assert_eq!(hdr.ctba_hi, 0x0000_0001);
1631        assert_eq!(hdr.prdbc, 0);
1632    }
1633
1634    #[test]
1635    fn test_ncq_read_fis_tag_encoding() {
1636        let fis = build_ncq_read_fis(0, 8, 5);
1637
1638        assert_eq!(fis.command, ATA_CMD_READ_FPDMA_QUEUED);
1639        // Tag 5 in bits 7:3 of count_lo
1640        assert_eq!(fis.count_lo, 5 << 3);
1641        // Sector count in feature register
1642        assert_eq!(fis.feature_lo, 8);
1643        assert_eq!(fis.feature_hi, 0);
1644    }
1645
1646    #[test]
1647    fn test_ncq_write_fis_tag_encoding() {
1648        let fis = build_ncq_write_fis(100, 256, 31);
1649
1650        assert_eq!(fis.command, ATA_CMD_WRITE_FPDMA_QUEUED);
1651        assert_eq!(fis.count_lo, 31 << 3);
1652        assert_eq!(fis.feature_lo, 0); // 256 & 0xFF = 0
1653        assert_eq!(fis.feature_hi, 1); // 256 >> 8 = 1
1654    }
1655
1656    #[test]
1657    fn test_port_state_default() {
1658        let port = AhciPort::new(0, 0x1000_0000, 32, true);
1659
1660        assert_eq!(port.port_num, 0);
1661        assert_eq!(port.device_type, AhciDeviceType::None);
1662        assert_eq!(port.state, PortState::Inactive);
1663        assert_eq!(port.num_cmd_slots, 32);
1664        assert!(port.ncq_supported);
1665        assert_eq!(port.total_sectors, 0);
1666        assert_eq!(port.sector_size, SECTOR_SIZE);
1667        // Port 0 MMIO base = hba_base + 0x100 + 0 * 0x80
1668        assert_eq!(port.port_mmio_base, 0x1000_0000 + PORT_BASE);
1669    }
1670
1671    #[test]
1672    fn test_port_mmio_base_calculation() {
1673        let port3 = AhciPort::new(3, 0x2000_0000, 16, false);
1674        // Port 3: base + 0x100 + 3 * 0x80 = base + 0x100 + 0x180 = base + 0x280
1675        assert_eq!(port3.port_mmio_base, 0x2000_0000 + 0x100 + 3 * 0x80);
1676    }
1677
1678    #[test]
1679    fn test_initialization_flag() {
1680        // Reset for test isolation
1681        AHCI_INITIALIZED.store(false, Ordering::SeqCst);
1682        assert!(!is_initialized());
1683    }
1684
1685    #[cfg(feature = "alloc")]
1686    #[test]
1687    fn test_ahci_block_device_name() {
1688        let port = AhciPort::new(0, 0, 32, false);
1689        let dev = AhciBlockDevice::new(port, String::from("sda"));
1690        assert_eq!(dev.name(), "sda");
1691        assert_eq!(dev.block_size(), SECTOR_SIZE);
1692        assert_eq!(dev.block_count(), 0);
1693    }
1694}