AHCI(Advance Host Controller Interface) is developed by Intel, designed to be a data movement engine between system memory and SATA devices.
AHCI controller is a PCI device, which class code is 0x010601, and contain AHCI memory bar in BAR5(offset 0x24).Here’s a big picture of AHCI related registers, which you can find in AHCI spec:
here’s a document on web, which show a good view of AHCI.
AHCI Identify Device
Send identify device command in AHCI mode, is a good and simple way to learn how it works, basically below procedure should be done:
- scan PCI devices for AHCI controller, by class code 0x010601;
- get AHCI base address, check HBA memory for Ports Implemented;
- calculate Port registers offset, check Port signature for ATA/ATAPI device,
Port offset = 100h + (PI Asserted Bit Position * 80h)
- config command list for ATA command or ATAPI command, cmd list.DW0.ATAPI(bit5);
- config D2H FIS, identify device command(0xEC) for ATA device, identify packet device command for ATAPI device;
- issue command by set port register PxCI(offset 0x38), each bit present a cmd slot;
- get return identify device data from PRDT.DBA(Data Base Address)
now let’s try to issue identify device command by utility ru.efi.
please notice that after boot into uefi shell, UEFI firmware already initial AHCI controller to a workable status, so not going to go deep for all AHCI registers in this example.
1. Find AHCI controller by List PCI devices
AHCI controller class code is 0x010601, check pci configuration space, found AHCI Bar = 0xF661C000.
2. AHCI Bar Memory Registers
Check HBA memory regs base on AHCI bar, found HBA.GHC.PI = 0x00000011, which means two ports have devices. Global Host Control = 0x80000000, which means AHCI enable. If there’s any problems during issue command, set bit0 of Global Host Control to reset AHCI controller, then set bit31 to re-enable AHCI.
3. Port Registers
By the formula, Port0 address = Abar+0x100+0x80*0=0xF661C1000. check port signature, it’s a ATAPI device, generally an ODD. Make sure PxCMD bit0=1, which enable to process command list. PxCI is bit significant, each bit corresponds to a command slot, set bit to 1 will issue the correspond command slot, need to setup command table before set it. Please check AHCI spec for port status regs if any problems after issue command.
4. Command List
Command slot0 DW0 = 0x00010005, which means command FIS length is 5, ATAPI bit = 0 will issue ATA command. PRD table = 1. as AHCI spec descript, command FIS length ‘0’ or ‘1’is illegal, max length can be 16DW. Why we set length as 5 here?
5. Host to Device FIS
Identify device command need construct H2D FIS, which layout as below shows that need 5 DW.
6. SATA Shadow Register
A register FIS sent to a SATA device always contains the contents of the HBA’s shadow register. Two specific events trigger the HBA to send a register FIS:
- A write to the Shadow Command Register
- A write to the Shadow Control Register.
7. Command Table
To get identify device data, host issue command to device, then device return identify data, so need to construct a H2D FIS, FIS type = 0x27.
command list DW0.C = 1 means register transfer is due to update of the command register, when set the Port PxCI register will issue command.
Due to port0 is ATAPI device, FIS command will set to 0xA1 as identify device packet. Specifically, the host shall not write the Features(7:0), Count(7:0), LBA(7:0), LBA(15:8),LBA(23:16), you can let as default value.
Data Base Address: identify device data will be return after command success
Data byte count: bit0 must be always be ‘1’, set to 0x1FF means 512 words.
8. Issue Command
When everything is setup, go back to port0 registers, set bit0 of PxCI to issue command slot0, after command success, go to Data byte address to check identify device data, check SATA spec for detail, we will get HDD/ODD information from this data.
Here’s my simple uefi app show how to identify device data in AHCI mode, it also contain a sample how to issue ATAPI cmd to eject ODD.