AHCI Storage Driver
BoredOS implements an Advanced Host Controller Interface (AHCI) driver to interface with Serial ATA (SATA) mass storage devices. The driver is located in src/dev/ahci.c and allows the OS to read and write sectors directly to physical hard drives or solid-state drives using DMA (Direct Memory Access).
1. Discovery and Initialization
The AHCI initialization process (ahci_init) starts by querying the PCI subsystem:
- It searches for a PCI device with Class
0x01(Mass Storage) and Subclass0x06(SATA). - It calls
pci_enable_bus_masteringandpci_enable_mmioto ensure the controller can perform DMA and its registers are accessible. - It retrieves the ABAR (AHCI Base Address Register) from PCI BAR5.
- The ABAR points to the
HBA_MEMstructure (Host Bus Adapter Memory Registers). The kernel iterates through thepi(Ports Implemented) bitmask to find active SATA ports.
2. Port Configuration
For every active SATA port found, the driver must allocate memory structures that the hardware will use to process commands:
- Command List Base (
clb): A 1KB memory region holding 32 Command Headers. - FIS Base (
fb): A 256-byte memory region where the HBA writes incoming Frame Information Structures (FIS) from the drive. - Command Tables (
ctba): A larger memory region allocated for each Command Header, containing the actual SATA command bytes and the scatter/gather lists (PRDT).
Note: All AHCI data structures must be allocated in physically contiguous memory and properly aligned (e.g., 1KB or 256-byte boundaries) because the HBA reads them directly from physical RAM via DMA.
3. Physical Region Descriptor Tables (PRDT)
When reading or writing data, BoredOS must tell the AHCI controller where in RAM the data should be stored or fetched. This is done using PRDT entries.
Each HBA_PRDT_ENTRY specifies:
- A physical Data Base Address (
dba). - A Byte Count (
dbc), limited to a maximum of 4MB per entry.
If a read/write request spans multiple fragmented pages or exceeds 4MB, the driver constructs multiple PRDT entries within the Command Table to form a scatter/gather list. The AHCI hardware seamlessly processes these entries as a single contiguous disk operation.
4. Reading and Writing Sectors
To execute a command (e.g., ahci_read_sectors or ahci_write_sectors):
- The driver finds a free slot in the Command List.
- It populates the Command Header, setting the
cfl(Command FIS Length) andw(Write) bit. - It builds a Host-to-Device Register FIS (
FIS_REG_H2D) in the Command Table, issuing theATA_CMD_READ_DMA_EXorATA_CMD_WRITE_DMA_EXcommand and specifying the starting LBA (Logical Block Address) and sector count. - It sets up the PRDT entries pointing to the physical memory of the provided buffer.
- It sets the corresponding bit in the Port's Command Issue register (
ci). - The driver then polls the
ciregister (or waits for an interrupt) until the bit clears, indicating the hardware has completed the DMA transfer.