Newer
Older
AMI-Aptio-BIOS-Reversed / AmiModulePkg / AHCI / Ahci / Ahci.md
@Ajax Dong Ajax Dong 2 days ago 24 KB Restructure the repo

Ahci

Function Table

Address Name Description
AhciDriverEntryPoint
AhciDriverBindingSupported
AhciDriverBindingStart
AhciDriverBindingStop
AhciGetDriverName
AhciGetComponentName
AhciInitController
AhciStopController
AhciInitializePortRegisters
AhciAllocateMemory
AhciCreatePort
AhciEnumerateDevice
AhciIdentifySataPort
AhciIdentifyPmPort
AhciSetPortInterfacePower
AhciConfigureDevice
AhciSetTransferMode
AhciSetFeatureRwDmaSetup
AhciReadCapacity
AhciBlockIoGetMediaInfo
AhciBlockIoGetDevicePath
AhciBlockIoFlush
AhciBlockIoReadBlocks
AhciBlockIoRwDispatch
AhciBlockIoRwExDispatch
AhciSetupBlockIoMedia
AhciGetMediaInfo
AhciDetectPartitionType
AhciParsePartitionEntry
AhciInitTaskFile
AhciCopyMem
AhciSetupCmdHeader
AhciSetupCmdTable
AhciSetupPrdTable
AhciStartCommand
AhciWaitCommandComplete
AhciAtaSoftReset
AhciNonDataCommand
AhciDmaCommand
AhciPacketCommand
AhciSataPhyCommand
AhciCheckMediaType
AhciDetectMedia
AhciReadLba
AhciWriteLba
AhciReadVerify
AhciPortReset
AhciSoftReset
AhciResetPort
AhciResetPmPorts
AhciPollPortReady
AhciReadPortStatus
AhciClearPortError
AhciSetAhciMode
AhciMmioPollReady
AhciMmioPollRead
AhciPreparePortAccess
AhciAcquirePortAccess
AhciBlockIo2Read
AhciBlockIo2Write
AhciDebugPrint
AhciDebugPrint2
AhciDebugTrace
AhciAssert
AhciDebugLevelEnabled
AhciGetDebugInterface
AhciCacheDebugProtocol
AhciTestProtocol
AhciCompareDevicePath
AhciReadUint32Le
AhciIsDevicePathNode
AhciZeroMem
AhciBlockIoRead
AhciBlockIoWrite
AhciNonBlockingRead
AhciNonBlockingWrite
Global system table pointers
EFI_HANDLE gImageHandle = NULL; // +0x8DE8
Cached boot services table pointer
EFI_BOOT_SERVICES *gBS_Cached = NULL; // +0x8E08
EFI_HANDLE gControllerHandle = NULL; // +0x8CE0
HOB list pointer
VOID *gHobList = NULL; // +0x8E00
Forward declarations for EFI_BLOCK_IO_PROTOCOL interface
EFI_BLOCK_IO_PROTOCOL gBlockIoTemplate = {
Media - filled per-port
Reset not used
EFI_DRIVER_BINDING_PROTOCOL instance
EFI_DRIVER_BINDING_PROTOCOL gAhciDriverBinding = {
Version NULL, // ImageHandle
DriverBindingHandle };
EFI_COMPONENT_NAME2_PROTOCOL gAhciComponentName2 = {
EFI_ATA_PASS_THRU_PROTOCOL instance (stub - minimal implementation)
EFI_ATA_PASS_THRU_PROTOCOL gAhciAtaPassThru = {
Driver name strings
CHAR16 *gAhciDriverName[] = {
Save global system table pointers
gImageHandle = ImageHandle;
Cache protocol pointers for faster access
gBS_Cached = gBS;
Get HOB list for platform configuration
AhciGetHobList (ImageHandle);
Clear global controller state
gAhciControllerBase = 0;
Install driver binding, component name, and ATA pass thru protocols
on a new driver handle
gControllerHandle = NULL;
Check if ATA Pass Thru is already installed (another driver claimed it)
Status = gBS->OpenProtocol (
Open PCI I/O protocol
Read PCI config space to check for AHCI class code
Status = PciIo->Pci.Read (
Check for AHCI (Mass Storage Controller - SATA) class code
Class Code = 0x01 (Mass Storage), Subclass = 0x06 (SATA)
Programming Interface = 0x01 (AHCI)
if **(Pci.Hdr.ClassCode[2] != PCI_CLASS_MASS_STORAGE **
If not AHCI, check if the IDE Controller Init protocol is installed
Query the IDE Controller Init to see if any channels support AHCI
Primary &Supports
SATA channel found via IDE controller init protocol
return EFI_SUCCESS;
Open required protocols
Optionally open IDE Controller Init protocol
Allocate the AHCI_CONTROLLER private structure
Controller = (AHCI_CONTROLLER *)gBS->AllocatePool (
Initialize the controller (enable PCI bus master, MMIO, etc.)
Status = AhciInitController (
Read the Ports Implemented (PI) register to find active ports
MaxPorts = 32;
Enumerate this port - first initialize port registers, then create port
Status = AhciInitializePortRegisters (Controller, Port);
Enumerate device on this port (without port multiplier)
Status = AhciEnumerateDevice (
Get our private controller structure
Stop each port and free resources
for (Entry = Controller->PortListHead.ForwardLink;
If the port was configured, perform ATA soft reset
if (Port->State == AHCI_PORT_STATE_READY &&
Stop the controller hardware
AhciStopController (Controller);
Close protocols
Free the controller structure
if (Controller != NULL) {
CONTROLLER INITIALIZATION
Get the AHCI BAR (BAR5 typically)
Status = PciIo->GetBarAttributes (
Enable PCI bus master and memory space
Status = PciIo->Attributes (
Map the AHCI register BAR to CPU-accessible MMIO
Read AHCI capabilities
Capabilities = MmioRead32 (AhciBarAddr + AHCI_GHC_CAP);
Clear global state
gAhciControllerBase = AhciBarAddr;
Initialize the port list
Clear the global controller base so MMIO accesses fail-safe
PORT MANAGEMENT
Calculate the port register base
PortRegBase = Controller->AhciRegBase + ((UINT64)Port + 2) * 0x80;
Allocate memory for the command list (1K aligned, 1K minimum)
and the FIS receive area (256 bytes aligned to 256)
MemoryBase = AhciAllocateMemory (sizeof (AHCI_CMD_HEADER) * 32 +
Program the port registers
MmioWrite32 (PortRegBase + AHCI_PORT_REG_CLB, (UINT32)(MemoryBase & 0xFFFFFFFF));
Store the BAR info in the port block
Clear error registers
MmioWrite32 (PortRegBase + AHCI_PORT_REG_SERR, 0xFFFFFFFF);
Enable FIS receive and start the port
MmioOr32 (PortRegBase + AHCI_PORT_REG_CMD
Wait for the port to come ready
AhciMmioPollReady (
Align size to 4K boundary
AlignedSize = ALIGN_VALUE (Size, SIZE_4KB);
Zero the allocated memory
ZeroMem ((VOID *)(UINTN)PhysicalAddress, AlignedSize);
Check if this port already exists in the list
Already configured
Allocate the port structure
AhciPort = (AHCI_PORT *)gBS->AllocatePool (
Initialize port fields
Compute port register base
Store AHCI BAR info for this port
Link back to the controller
Insert into the linked list
InsertTailList (&Controller->PortListHead, &AhciPort->Link);
Read the port signature to determine device type
DEVICE ENUMERATION
Check if we already have this port and it's good
Create the port (or get existing one)
Status = AhciCreatePort (Controller, PciIo, Port, PmPort);
Find the port in the list again
If it's a port multiplier, check the PM type
if (AhciPort->PortType == AHCI_SIG_PM && AhciPort->MediaType == AHCI_MEDIA_TYPE_NONE) {
Configure the device (soft reset, read capacity, etc.)
if (AhciPort->MediaType != AHCI_MEDIA_TYPE_NONE) {
Set device features
Status = AhciSetDeviceFeature (AhciPort, Capacity);
Set port interface power
AhciSetPortInterfacePower (AhciPort);
Initialize the task file for this device
AhciInitTaskFile (AhciPort, PciIo, NULL, 0);
Set up the Block I/O media
Status = AhciSetupBlockIoMedia (AhciPort);
Install Block I/O protocol on a new child handle
Status = gBS->InstallMultipleProtocolInterfaces (
Install Block I/O 2 protocol
Install Block I/O 2 on child handle
Status = gBS->InstallProtocolInterface (
PORT IDENTIFICATION
Reset the port
Status = AhciPortReset (
Poll for port ready
Status = AhciPollPortReady (Port, NULL, 10, 0xFF);
Set AHCI mode (enable FIS, start port)
Status = AhciSetAhciMode (Port->Controller, PortNumber, Port->AhciBarBase);
Clear port error status
MmioWrite32 (Port->AhciBarBase + AHCI_PORT_REG_SERR, 0xFFFFFFFF);
Send SATA Phy command to identify device on this PM port
Status = AhciSataPhyCommand (Port, 0, 0, &PhyResult, 0);
PORT POWER MANAGEMENT
Enable Aggressive Link Power Management
CmdValue = MmioRead32 (PortRegBase + AHCI_PORT_REG_CMD);
Set Partial/Slumber timer
MmioWrite32 (PortRegBase + 0x44, 0x00010001); // PxDEVSLP
DEVICE CONFIGURATION
Prepare port for access
Status = AhciPreparePortAccess (Port);
Perform ATA soft reset
Status = AhciAtaSoftReset (Port, TaskFile);
Identify device
if (Port->MediaType == AHCI_MEDIA_TYPE_ATAPI) {
ATAPI - use IDENTIFY PACKET DEVICE
Status = AhciSataPhyCommand (Port, 0xA1, 0, (UINT16 *)TaskFile, 0);
ATA - send IDENTIFY DEVICE via SATA Phy path
Status = AhciSataPhyCommand (Port, 0xEC, 0, (UINT16 *)TaskFile, 0);
Set transfer mode
Status = AhciSetTransferMode (Port);
Enable RW DMA Setup
Status = AhciSetFeatureRwDmaSetup (Port);
Read capacity
Status = AhciReadCapacity (Port);
Determine best transfer mode from identify data
if (Port->IdentifyData[201] & 0x20) {
Ultra DMA supported - select highest UDMA mode
TransferMode **= ATA_TRANSFER_MODE_UDMA 0x07; // UDMA/133**
Multiword DMA
TransferMode **= ATA_TRANSFER_MODE_MULTI_DMA 0x02;**
PIO mode
TransferMode **= ATA_TRANSFER_MODE_PIO_DEFAULT 0x00;**
Build SET FEATURES task file
SetMem (TaskFile, sizeof (TaskFile), 0);
Execute the command
return AhciAtaSoftReset (Port, TaskFile);
Check if device supports RW DMA Setup
if ((Port->Controller->Capability & 0x4000000) == 0) {
SubCommand = ATA_RW_DMA_SETUP_ENABLE;
Build READ CAPACITY (or READ NATIVE MAX) task file
READ DMA EXT
Execute the non-data command
Status = AhciNonDataCommand (Port, (UINT8 *)TaskFile, 0);
Parse the response in the ATA registers
BLOCK I/O INTERFACE
Return a device path node for this port
For AHCI, writes go directly to device, so flush is a no-op
Verify media ID
if (Media->MediaId != MediaId) {
Validate buffer size is block-aligned
if (BufferSize % Media->BlockSize != 0) {
Check for valid LBA range
if (Lba + BlockCount > Media->LastBlock + 1) {
Validate parameters
Check media status
Status = AhciDetectMedia (Port);
Perform the DMA operation
if (IsWrite) {
Check if the device is SSD (word 35 non-zero) or HDD
if (Port->MediaType != AHCI_MEDIA_TYPE_NONE) {
Check for extended sector sizes (512e / 4Kn)
BlockSize = 512;
Logical sector size > 512 bytes
BlockSize *= 2 (Port->IdentifyData[269] (Port->IdentifyData[271] << 16));**
Determine last block from identify data
if ((Port->IdentifyData[201] & 0x400) != 0) {
LastBlock = Port->IdentifyData[235] - 1;
Allocate and fill the media structure
Media = (EFI_BLOCK_IO_MEDIA *)AhciAllocateZeroPool (sizeof (EFI_BLOCK_IO_MEDIA));
Fill the Block I/O protocol instance
EFI_BLOCK_IO_PROTOCOL *BlockIo;
Fill the Block I/O 2 protocol instance
EFI_BLOCK_IO2_PROTOCOL *BlockIo2;
Configure LBA count and block size from identify data
if ((Port->IdentifyData[247] & 0xC000) == 0x4000 &&
Logical sector size reported in word 247
Maps media type values:
ATAPI_REMOVABLE -> 16
ATA_REMOVABLE -> 8
ATAPI_FIXED -> 0
return (EFI_STATUS)(UINTN)0;
PARTITION DETECTION
Check for GPT/ protective MBR or legacy MBR
Read first sector for MBR analysis
AhciParsePartitionEntry (NULL, 0x402, &PartitionType);
EFI GPT partition
MBR entries start at offset 0x1BE (446)
Each entry is 16 bytes
Entry = Mbr + 12; // +0x1BE/4 = 446/4 = 111.5? Actually Mbr[446/4]
Scan entries (max 4 for MBR)
for (INTN i = 0; i < 4; i++) {
Protective MBR (GPT)
GPT return EFI_SUCCESS;
ATA COMMAND EXECUTION
Calculate CRC-16 over the task file for verification
if (TaskFile != NULL && TaskFileSize > 0) {
Process encoding
Zero the command header
ZeroMem (CmdHeader, sizeof (AHCI_CMD_HEADER));
Clear reserved fields and set PM port
PM port
Set command table base address
Clear CFL (will be set in AhciSetupCmdTable)
Zero the command table
ZeroMem (CmdTable, sizeof (AHCI_CMD_HEADER) + sizeof (AHCI_CMD_TABLE));
Fill in the Register H2D FIS (type 0x27)
FIS type: Register H2D
ATA Command
Features CmdTable->Cfis[4] = TaskFile[16]; // LBA Low
LBA High
LBA Mid
LBA High (ext)
count CmdTable->Cfis[10] = TaskFile[21]; // count (ext)
sector count
sector count (ext)
device CmdTable->Cfis[15] = TaskFile[24]; // control
Set command slot attributes
Clear prefetch, set ATAPI if needed
PRD starts at +128
Clear PRD flags
Set data buffer address
Max PRD size is 4MB - 1 (0x3FFFFF)
if (Remaining >= 0x400000) {
4MB - 1
Set DBC (Data Byte Count) field in bits 0..21
Clear interrupt bit (I=0)
Advance //
Each PRD is 16 bytes
Check if we have room
if (PrdIndex 16 + 128 >= ((UINT32 *)Port->AhciBarBase + 14)) {
Set command list override and start
Clear SError, enable interrupts
MmioOr32 (PortRegBase + AHCI_PORT_REG_SERR, 0x7FF0F03);
Zero the FIS receive area
ZeroMem ((VOID *)(UINTN)Port->AhciBarBase, 256);
Set start (ST) bit and command issue (CI)
MmioOr32 (PortRegBase + AHCI_PORT_REG_CMD, AHCI_PXCMD_ST);
Poll for CI to clear, with timeout
do {
Delay 500us
Check for errors
Check error bits in SError
if (CiValue & 0x7FA0F00) {
Error condition detected
goto ErrorExit;
Check if CI cleared (command done)
Check IS register for completion interrupt
CiValue = MmioRead32 (PortRegBase + AHCI_PORT_REG_IS);
Clear status bits
Read the received FIS status
Command completed - check task file
CiValue = MmioRead32 (PortRegBase + AHCI_PORT_REG_TFD);
Check status in received FIS
Timeout expired
Log the error
ATA COMMAND BUILDING AND EXECUTION
Build a RESET task file
SetMem (PortReset, sizeof (PortReset), 0);
Port reset
Acquire port access
Status = AhciAcquirePortAccess (Port, PortReset, FALSE);
Reset PM ports
AhciResetPmPorts (Port);
Setup command header and table
AhciSetupCmdHeader (Port, (AHCI_CMD_HEADER *)PortReset, (UINT64)PortReset);
Start command and wait
AhciStartCommand (Port->Controller, Port);
Acquire exclusive port access
Status = AhciAcquirePortAccess (Port, TaskFile, FALSE);
Set up command header
AhciSetupCmdHeader (Port, (AHCI_CMD_HEADER *)TaskFile, (UINT64)TaskFile);
Set up command table (no data)
AhciSetupCmdTable (Port, TaskFile, (UINT32 )TaskFile, (AHCI_CMD_TABLE )(TaskFile + 32));
No PRD for non-data commands
Start command
Wait for completion
Status = AhciWaitCommandComplete (Port, (UINT32)AHCI_COMMAND_TIMEOUT, FALSE);
Status = AhciAcquirePortAccess (Port, TaskFile, TRUE);
Setup command structures
AhciSetupCmdHeader (Port, (AHCI_CMD_HEADER *)TaskFile, (UINT64)(TaskFile + 64));
Check media type (may need to retry after reset)
Status = AhciCheckMediaType (Port);
Status = AhciWaitCommandComplete (Port, (UINT32)AHCI_COMMAND_TIMEOUT, TRUE);
Status = AhciAcquirePortAccess (Port, (UINT8 *)TaskFile, TRUE);
Setup command structures (with ATAPI flag)
AhciSetupCmdHeader (Port, (AHCI_CMD_HEADER *)TaskFile, (UINT64)(TaskFile + 8));
Status = AhciAcquirePortAccess (Port, NULL, FALSE);
Build a register H2D FIS with the SATA command
AhciSetupCmdHeader (Port, (AHCI_CMD_HEADER *)&PhyCmd, (UINT64)&PhyCmd);
Wait for completion with port reset timeout
Status = AhciWaitCommandComplete (Port, AHCI_PORT_RESET_TIMEOUT, FALSE);
Check the identify data for DMA support
Build a simple task file to check
Reset port and check for device
Status = AhciResetPort (Port);
Try DMA to see if the device responds
Status = AhciDmaCommand (Port, (UINT8 *)&Status, 0);
READ / WRITE LBA OPERATIONS
0x23 = 0x230023000000000LL encodes valid commands
Valid read commands: 0x20,0x24,0x25,0xC4,0xC8,0xD0,0xD4
Build the task file
Command TaskFile[12] = 0x23; // Sector count
Execute non-data command to read
return AhciNonDataCommand (Port, TaskFile, 0);
Build the task file for DMA write
Execute as packet/custom command
Status = AhciPacketCommand (Port, (UINT64 *)TaskFile, 0);
Execute READ VERIFY SECTOR(S) command
Check for valid command based on LBA size
if ((UINT8 )(TaskFile + 23) > 0x39) {
PORT RESET AND CONTROL
Clear ST (Start) bit to stop command processing
Wait for command list and FIS receive to stop
Perform COMINIT: set DET=1 in PxSCTL
MmioWrite32 (PortRegBase + AHCI_PORT_REG_SCTL, 0x101);
Clear DET
MmioWrite32 (PortRegBase + AHCI_PORT_REG_SCTL, 0x100);
Wait for device to come ready
AhciPollPortReady ((AHCI_PORT *)Controller, NULL, 10, 0xFF);
Build the soft reset task file
Set up command structures
Build a reset task file
Execute DMA command as reset
Status = AhciDmaCommand (Port, TaskFile, 0);
Reset each PM port
PORT STATUS AND POLLING
Poll for device detection (SSTS.DET = 3)
Read port status
StatusValue = MmioRead32 (PortRegBase + 0x24); // PxSSTS
SerrValue = MmioRead32 (PortRegBase + AHCI_PORT_REG_SERR);
PhyRdy Change - clear error
MmioWrite32 (PortRegBase + AHCI_PORT_REG_SERR, 0x100);
Read status again after clearing
StatusValue = MmioRead32 (PortRegBase + 0x24);
Check device detection
PollStatus = AhciReadPortStatus (Port, PortStatus, Port->Port, 0xFF, 0);
Device detection in progress
PollStatus = EFI_NOT_READY;
Read TFD register for BSY/DRQ status
if (ReadType == 1) {
Read TFD
Clear known error bits:
0x7FF0F03 **= AHCI_PXIS_TFES AHCI_PXIS_HBFS AHCI_PXIS_HBDS **
AHCI_PXIS_IFS ** AHCI_PXIS_INFS AHCI_PXIS_OFS **
AHCI_PXIS_IPMS ** AHCI_PXIS_PRCS AHCI_PXIS_PCS **
AHCI_PXIS_DMP ** AHCI_PXIS_UFS**
ErrorBits = 0x7FF0F03;
Direct port - write SError to clear all
if (Port->Controller != NULL) {
PxSERR via port register
PM port - use Phy command
AhciSataPhyCommand (Port, Flags, 1, (UINT16 *)&ErrorBits, 1);
Enable FIS receive and start
Wait for FIS receive to start
MMIO POLLING OPERATIONS
Read register
100us delay
Poll for command completion with retries
for (INTN retry = 500; retry > 0; retry--) {
PORT ACCESS SYNCHRONIZATION
Build a port access task file
Send appropriate command based on media type:
ATAPI uses IDENTIFY PACKET DEVICE (0xA1)
ATA uses IDENTIFY DEVICE
Execute as non-data command
Status = AhciNonDataCommand (Port, TaskFile, 0);
Check command register for errors
Ensure the port is ready
BLOCK I/O 2 (NON-BLOCKING) INTERFACE
Fall through to blocking implementation.
return EFI_UNSUPPORTED;
DEBUG AND ASSERT SUPPORT
Forward to UEFI DebugLib if debug protocol is active
if (AhciDebugLevelEnabled ()) {
Cache the debug protocol on first call
AhciCacheDebugProtocol ();
Log the trace if debug is enabled
Get debug interface and report assertion
Read CMOS RTC register 0x4B (AHCI-specific debug mask)
IoWrite8 **(0x70, (IoRead8 (0x70) & 0x80) 0x4B);**
PROTOCOL HELPERS
Locate the AMI debug protocol
Status = gBS->LocateProtocol (
Try alternate method if debug protocol is a small interface
if (gBS->Hdr.HeaderSize <= 0x10) {
Cache debug protocol on first access
if (gAhciDebugProtocol != NULL) {
HOB AND HELPER FUNCTIONS
Get HOB list from system table runtime services
HobList = (VOID *)gST->BootServices;
Check RemainingDevicePath
if (RemainingDevicePath != NULL) {
Try to open ATA Pass Thru protocol
Test for IDE Controller Init protocol
Check for PCI IO protocol
Query for SATA channels
Status = IdeInit->GetChannelInfo (IdeInit, 0, (UINT64 *)ChannelInfo);
Compare with "en-US" language string
return (DevicePath[0] == 'e' &&

Generated by HR650X BIOS Decompilation Project