HBA registers.jpg
Driver on

AHCI – OSDev Wiki

The specification:
https://www.intel.com/content material/dam/www/public/us/en/paperwork/technical-specifications/serial-ata-ahci-spec-rev1-3-1.pdf

Introduction

AHCI (Advance Host Controller Interface) is developed by Intel to facilitate dealing with SATA units. The AHCI specification emphasizes that an AHCI controller (known as host bus adapter, or HBA) is designed to be a knowledge motion engine between system reminiscence and SATA units. It encapsulates SATA units and supplies an ordinary PCI interface to the host. System designers can simply entry SATA drives utilizing system reminiscence and reminiscence mapped registers, with out the necessity for manipulating the annoying job information as IDE do.

An AHCI controller could assist as much as 32 ports which might connect completely different SATA units similar to disk drives, port multipliers, or an enclosure administration bridge. AHCI helps all native SATA options similar to command queueing, sizzling plugging, energy administration, and so on. To a software program developer, an AHCI controller is only a PCI gadget with bus grasp functionality.

AHCI is a brand new normal in comparison with IDE, which has been round for twenty years. There exists little documentation about its programming suggestions and tips. Probably the one obtainable useful resource is the Intel AHCI specification (see Exterior Hyperlinks) and a few open supply working techniques similar to Linux. This text exhibits the minimal steps an OS (not BIOS) ought to do to place AHCI controller right into a workable state, how you can determine drives hooked up, and how you can learn bodily sectors from a SATA disk. To maintain concise, many technical particulars and deep explanations of some knowledge buildings have been omitted.

It must be famous that IDE additionally helps SATA units and there are nonetheless debates about which one, IDE or AHCI, is healthier. Some assessments even present {that a} SATA disk acts higher in IDE mode than AHCI mode. However the frequent thought is that AHCI performs higher and would be the normal PC to SATA interface, although some driver software program must be enhanced to totally domesticate AHCI functionality.

All of the diagrams on this article are copied from the Intel AHCI specification 1.3.

SATA primary

There are no less than two SATA requirements maintained respectively by T13 and SATA-IO. The SATA-IO focuses on serial ATA and T13 encompasses conventional parallel ATA specs as effectively.

Whereas the {hardware} specs for IDE and SATA (and even between completely different units implementing them) differ enormously, the API and ABI are very comparable. To a software program developer, the largest distinction between SATA and parallel ATA is that SATA makes use of FIS (Body Data Construction) packet to move knowledge between host and gadget. An FIS could be considered as a knowledge set of conventional job information, or an encapsulation of ATA instructions. SATA makes use of the identical command set as parallel ATA.

1) FIS varieties

Following code defines completely different sorts of FIS laid out in Serial ATA Revision 3.0.

typedef enum
{
	FIS_TYPE_REG_H2D	= 0x27,	// Register FIS - host to gadget
	FIS_TYPE_REG_D2H	= 0x34,	// Register FIS - gadget to host
	FIS_TYPE_DMA_ACT	= 0x39,	// DMA activate FIS - gadget to host
	FIS_TYPE_DMA_SETUP	= 0x41,	// DMA setup FIS - bidirectional
	FIS_TYPE_DATA		= 0x46,	// Knowledge FIS - bidirectional
	FIS_TYPE_BIST		= 0x58,	// BIST activate FIS - bidirectional
	FIS_TYPE_PIO_SETUP	= 0x5F,	// PIO setup FIS - gadget to host
	FIS_TYPE_DEV_BITS	= 0xA1,	// Set gadget bits FIS - gadget to host
} FIS_TYPE;

2) Register FIS – Host to Gadget

A bunch to gadget register FIS is utilized by the host to ship command or management to a tool. As illustrated within the following knowledge construction, it comprises the IDE registers similar to command, LBA, gadget, function, rely and management. An ATA command is constructed on this construction and issued to the gadget. All reserved fields in an FIS must be cleared to zero.

typedef struct tagFIS_REG_H2D
{
	// DWORD 0
	uint8_t  fis_type;	// FIS_TYPE_REG_H2D
 
	uint8_t  pmport:4;	// Port multiplier
	uint8_t  rsv0:3;		// Reserved
	uint8_t  c:1;		// 1: Command, 0: Management
 
	uint8_t  command;	// Command register
	uint8_t  featurel;	// Characteristic register, 7:0
 
	// DWORD 1
	uint8_t  lba0;		// LBA low register, 7:0
	uint8_t  lba1;		// LBA mid register, 15:8
	uint8_t  lba2;		// LBA excessive register, 23:16
	uint8_t  gadget;		// Gadget register
 
	// DWORD 2
	uint8_t  lba3;		// LBA register, 31:24
	uint8_t  lba4;		// LBA register, 39:32
	uint8_t  lba5;		// LBA register, 47:40
	uint8_t  featureh;	// Characteristic register, 15:8
 
	// DWORD 3
	uint8_t  countl;		// Depend register, 7:0
	uint8_t  counth;		// Depend register, 15:8
	uint8_t  icc;		// Isochronous command completion
	uint8_t  management;	// Management register
 
	// DWORD 4
	uint8_t  rsv1[4];	// Reserved
} FIS_REG_H2D;

3) Register FIS – Gadget to Host

A tool to host register FIS is utilized by the gadget to inform the host that some ATA register has modified. It comprises the up to date job information similar to standing, error and different registers.

typedef struct tagFIS_REG_D2H
{
	// DWORD 0
	uint8_t  fis_type;    // FIS_TYPE_REG_D2H
 
	uint8_t  pmport:4;    // Port multiplier
	uint8_t  rsv0:2;      // Reserved
	uint8_t  i:1;         // Interrupt bit
	uint8_t  rsv1:1;      // Reserved
 
	uint8_t  standing;      // Standing register
	uint8_t  error;       // Error register
 
	// DWORD 1
	uint8_t  lba0;        // LBA low register, 7:0
	uint8_t  lba1;        // LBA mid register, 15:8
	uint8_t  lba2;        // LBA excessive register, 23:16
	uint8_t  gadget;      // Gadget register
 
	// DWORD 2
	uint8_t  lba3;        // LBA register, 31:24
	uint8_t  lba4;        // LBA register, 39:32
	uint8_t  lba5;        // LBA register, 47:40
	uint8_t  rsv2;        // Reserved
 
	// DWORD 3
	uint8_t  countl;      // Depend register, 7:0
	uint8_t  counth;      // Depend register, 15:8
	uint8_t  rsv3[2];     // Reserved
 
	// DWORD 4
	uint8_t  rsv4[4];     // Reserved
} FIS_REG_D2H;

4) Knowledge FIS – Bidirectional

This FIS is utilized by the host or gadget to ship knowledge payload. The information dimension could be assorted.

typedef struct tagFIS_DATA
{
	// DWORD 0
	uint8_t  fis_type;	// FIS_TYPE_DATA
 
	uint8_t  pmport:4;	// Port multiplier
	uint8_t  rsv0:4;		// Reserved
 
	uint8_t  rsv1[2];	// Reserved
 
	// DWORD 1 ~ N
	uint32_t knowledge[1];	// Payload
} FIS_DATA;

5) PIO Setup – Gadget to Host

This FIS is utilized by the gadget to inform the host that it’s about to ship or able to obtain a PIO knowledge payload.

typedef struct tagFIS_PIO_SETUP
{
	// DWORD 0
	uint8_t  fis_type;	// FIS_TYPE_PIO_SETUP
 
	uint8_t  pmport:4;	// Port multiplier
	uint8_t  rsv0:1;		// Reserved
	uint8_t  d:1;		// Knowledge switch path, 1 - gadget to host
	uint8_t  i:1;		// Interrupt bit
	uint8_t  rsv1:1;
 
	uint8_t  standing;		// Standing register
	uint8_t  error;		// Error register
 
	// DWORD 1
	uint8_t  lba0;		// LBA low register, 7:0
	uint8_t  lba1;		// LBA mid register, 15:8
	uint8_t  lba2;		// LBA excessive register, 23:16
	uint8_t  gadget;		// Gadget register
 
	// DWORD 2
	uint8_t  lba3;		// LBA register, 31:24
	uint8_t  lba4;		// LBA register, 39:32
	uint8_t  lba5;		// LBA register, 47:40
	uint8_t  rsv2;		// Reserved
 
	// DWORD 3
	uint8_t  countl;		// Depend register, 7:0
	uint8_t  counth;		// Depend register, 15:8
	uint8_t  rsv3;		// Reserved
	uint8_t  e_status;	// New worth of standing register
 
	// DWORD 4
	uint16_t tc;		// Switch rely
	uint8_t  rsv4[2];	// Reserved
} FIS_PIO_SETUP;

6) DMA Setup – Gadget to Host

typedef struct tagFIS_DMA_SETUP
{
	// DWORD 0
	uint8_t  fis_type;	// FIS_TYPE_DMA_SETUP
 
	uint8_t  pmport:4;	// Port multiplier
	uint8_t  rsv0:1;		// Reserved
	uint8_t  d:1;		// Knowledge switch path, 1 - gadget to host
	uint8_t  i:1;		// Interrupt bit
	uint8_t  a:1;            // Auto-activate. Specifies if DMA Activate FIS is required
 
        uint8_t  rsved[2];       // Reserved
 
	//DWORD 1&2
 
        uint64_t DMAbufferID;    // DMA Buffer Identifier. Used to Determine DMA buffer in host reminiscence. SATA Spec says host particular and never in Spec. Making an attempt AHCI spec may work.
 
        //DWORD 3
        uint32_t rsvd;           //Extra reserved
 
        //DWORD 4
        uint32_t DMAbufOffset;   //Byte offset into buffer. First 2 bits should be 0
 
        //DWORD 5
        uint32_t TransferCount;  //Variety of bytes to switch. Bit Zero should be 0
 
        //DWORD 6
        uint32_t resvd;          //Reserved
 
} FIS_DMA_SETUP;

7) Instance

This instance illustrates the steps to learn the Determine knowledge from a tool. Error detection and restoration is ignored.

To subject an ATA Determine command to the gadget, the FIS is constructed at follows.

FIS_REG_H2D fis;
memset(&fis, 0, sizeof(FIS_REG_H2D));
fis.fis_type = FIS_TYPE_REG_H2D;
fis.command = ATA_CMD_IDENTIFY;	// 0xEC
fis.gadget = 0;			// Grasp gadget
fis.c = 1;				// Write command register

After the gadget receives this FIS and efficiently learn the 256 phrases knowledge into its inner buffer, it sends a PIO Setup FIS – Gadget to Host to inform the host that it’s able to switch knowledge and the info dimension (FIS_PIO_SETUP.tc).

After the PIO Setup FIS – Gadget to Host has been despatched appropriately, the gadget sends a DATA FIS to the host which comprises the acquired knowledge payload (FIS_DATA.knowledge).

This situation is described in SATA revision 3.Zero as a PIO data-in command protocol. However an AHCI controller will do the latter two steps for the host. The host software program wants solely setup and subject the command FIS, and tells the AHCI controller the reminiscence tackle and dimension to retailer the acquired knowledge. After every part is completed, the AHCI controller will subject an interrupt (if enabled) to inform the host to test the info.

Discover an AHCI controller

An AHCI controller could be discovered by enumerating the PCI bus. It has a category id 0x01 (mass storage gadget) and usually a subclass id 0x06 (serial ATA). The seller id and gadget id also needs to be checked to make sure it’s actually an AHCI controller.

Figuring out what mode the controller is in

As you could bear in mind, a SATA controller can both be in IDE emulation mode or in AHCI mode. The issue that enters right here is easy:
How you can discover what mode the controller is in. The documentation is basically obscure on this. Maybe the easiest way is to initialize a SATA controller as each IDE and AHCI. On this means, so long as you’re cautious about non-existent ports, you can’t go incorrect.

One attainable means of doing that is by checking the bit 31 of GHC register. It is labeled as AHCI Allow.

AHCI Registers and Reminiscence Constructions

As talked about above, host communicates with the AHCI controller by means of system reminiscence and reminiscence mapped registers. The final PCI base tackle register (BAR[5], header offset 0x24) factors to the AHCI base reminiscence, it’s known as ABAR (AHCI Base Reminiscence Register). All AHCI registers and recollections could be situated by means of ABAR. The opposite PCI base tackle registers act similar as a conventional IDE controller. Some AHCI controller could be configured to simulate a legacy IDE one.

1) HBA reminiscence registers

An AHCI controller can assist as much as 32 ports. HBA reminiscence registers could be divided into two elements: Generic Host Management registers and Port Management registers. Generic Host Management registers controls the habits of the entire controller, whereas every port owns its personal set of Port Management registers. The precise ports an AHCI controller supported and applied could be calculated from the Capability register (HBA_MEM.cap) and the Port Carried out register (HBA_MEM.pi).

HBA registers.jpg

typedef risky struct tagHBA_MEM
{
	// 0x00 - 0x2B, Generic Host Management
	uint32_t cap;		// 0x00, Host functionality
	uint32_t ghc;		// 0x04, World host management
	uint32_t is;		// 0x08, Interrupt standing
	uint32_t pi;		// 0x0C, Port applied
	uint32_t vs;		// 0x10, Model
	uint32_t ccc_ctl;	// 0x14, Command completion coalescing management
	uint32_t ccc_pts;	// 0x18, Command completion coalescing ports
	uint32_t em_loc;		// 0x1C, Enclosure administration location
	uint32_t em_ctl;		// 0x20, Enclosure administration management
	uint32_t cap2;		// 0x24, Host capabilities prolonged
	uint32_t bohc;		// 0x28, BIOS/OS handoff management and standing
 
	// 0x2C - 0x9F, Reserved
	uint8_t  rsv[0xA0-0x2C];
 
	// 0xA0 - 0xFF, Vendor particular registers
	uint8_t  vendor[0x100-0xA0];
 
	// 0x100 - 0x10FF, Port management registers
	HBA_PORT	ports[1];	// 1 ~ 32
} HBA_MEM;
 
typedef risky struct tagHBA_PORT
{
	uint32_t clb;		// 0x00, command listing base tackle, 1K-byte aligned
	uint32_t clbu;		// 0x04, command listing base tackle higher 32 bits
	uint32_t fb;		// 0x08, FIS base tackle, 256-byte aligned
	uint32_t fbu;		// 0x0C, FIS base tackle higher 32 bits
	uint32_t is;		// 0x10, interrupt standing
	uint32_t ie;		// 0x14, interrupt allow
	uint32_t cmd;		// 0x18, command and standing
	uint32_t rsv0;		// 0x1C, Reserved
	uint32_t tfd;		// 0x20, job file knowledge
	uint32_t sig;		// 0x24, signature
	uint32_t ssts;		// 0x28, SATA standing (SCR0:SStatus)
	uint32_t sctl;		// 0x2C, SATA management (SCR2:SControl)
	uint32_t serr;		// 0x30, SATA error (SCR1:SError)
	uint32_t sact;		// 0x34, SATA lively (SCR3:SActive)
	uint32_t ci;		// 0x38, command subject
	uint32_t sntf;		// 0x3C, SATA notification (SCR4:SNotification)
	uint32_t fbs;		// 0x40, FIS-based swap management
	uint32_t rsv1[11];	// 0x44 ~ 0x6F, Reserved
	uint32_t vendor[4];	// 0x70 ~ 0x7F, vendor particular
} HBA_PORT;

This reminiscence space must be configured as uncacheable as they’re reminiscence mapped {hardware} registers, not regular prefetchable RAM. For a similar purpose, the info buildings are declared as “risky” to forestall the compiler from over optimizing the code.

2) Port Acquired FIS and Command Checklist Reminiscence

Every port can connect a single SATA gadget. Host sends instructions to the gadget utilizing Command Checklist and gadget delivers info to the host utilizing Acquired FIS construction. They’re situated at HBA_PORT.clb/clbu, and HBA_PORT.fb/fbu. A very powerful a part of AHCI initialization is to set appropriately these two pointers and the info buildings they level to.

Port memory.jpg

3) Acquired FIS

There are 4 sorts of FIS which can be despatched to the host by the gadget as indicated within the following construction declaration. When an FIS has been copied into the host specified reminiscence, an in accordance bit might be set within the Port Interrupt Standing register (HBA_PORT.is).

Knowledge FIS – Gadget to Host is just not copied to this construction. Knowledge payload is shipped and acquired by means of PRDT (Bodily Area Descriptor Desk) in Command Checklist, as might be launched later.

typedef risky struct tagHBA_FIS
{
	// 0x00
	FIS_DMA_SETUP	dsfis;		// DMA Setup FIS
	uint8_t         pad0[4];
 
	// 0x20
	FIS_PIO_SETUP	psfis;		// PIO Setup FIS
	uint8_t         pad1[12];
 
	// 0x40
	FIS_REG_D2H	rfis;		// Register – Gadget to Host FIS
	uint8_t         pad2[4];
 
	// 0x58
	FIS_DEV_BITS	sdbfis;		// Set Gadget Bit FIS
 
	// 0x60
	uint8_t         ufis[64];
 
	// 0xA0
	uint8_t   	rsv[0x100-0xA0];
} HBA_FIS;

4) Command Checklist

Host sends instructions to the gadget by means of Command Checklist. Command Checklist consists of 1 to 32 command headers, every one known as a slot. Every command header describes an ATA or ATAPI command, together with a Command FIS, an ATAPI command buffer and a bunch of Bodily Area Descriptor Tables specifying the info payload tackle and dimension.

To ship a command, the host constructs a command header, and set the in accordance bit within the Port Command Challenge register (HBA_PORT.ci). The AHCI controller will routinely ship the command to the gadget and look ahead to response. If there are some errors, error bits within the Port Interrupt register (HBA_PORT.is) might be set and extra info could be retrieved from the Port Activity File register (HBA_PORT.tfd), SStatus register (HBA_PORT.ssts) and SError register (HBA_PORT.serr). If it succeeds, the Command Challenge register bit might be cleared and the acquired knowledge payload, if any, might be copied from the gadget to the host reminiscence by the AHCI controller.

What number of slots a Command Checklist holds could be obtained from the Host functionality register (HBA_MEM.cap). It should be inside 1 and 32. SATA helps queued instructions to extend throughput. Not like conventional parallel ATA drive; a SATA drive can course of a brand new command when an previous one remains to be working. With AHCI, a number can ship as much as 32 instructions to gadget concurrently.

Command list.jpg

typedef struct tagHBA_CMD_HEADER
{
	// DW0
	uint8_t  cfl:5;		// Command FIS size in DWORDS, 2 ~ 16
	uint8_t  a:1;		// ATAPI
	uint8_t  w:1;		// Write, 1: H2D, 0: D2H
	uint8_t  p:1;		// Prefetchable
 
	uint8_t  r:1;		// Reset
	uint8_t  b:1;		// BIST
	uint8_t  c:1;		// Clear busy upon R_OK
	uint8_t  rsv0:1;		// Reserved
	uint8_t  pmp:4;		// Port multiplier port
 
	uint16_t prdtl;		// Bodily area descriptor desk size in entries
 
	// DW1
	risky
	uint32_t prdbc;		// Bodily area descriptor byte rely transferred
 
	// DW2, 3
	uint32_t ctba;		// Command desk descriptor base tackle
	uint32_t ctbau;		// Command desk descriptor base tackle higher 32 bits
 
	// DW4 - 7
	uint32_t rsv1[4];	// Reserved
} HBA_CMD_HEADER;

5) Command Desk and Bodily Area Descriptor Desk

As described above, a command desk comprises an ATA command FIS, an ATAPI command buffer and a bunch of PRDT (Bodily Area Descriptor Desk) specifying the info payload tackle and dimension.

A command desk could have Zero to 65535 PRDT entries. The precise PRDT entries rely is about within the command header (HBA_CMD_HEADER.prdtl). For example, if a number needs to learn 100Okay bytes repeatedly from a disk, the primary half to reminiscence tackle A1, and the second half to deal with A2. It should set two PRDT entries, the primary PRDT.DBA = A1, and the second PRDT.DBA = A2.

An AHCI controller acts as a PCI bus grasp to switch knowledge payload straight between gadget and system reminiscence.

Command table.jpg

typedef struct tagHBA_CMD_TBL
{
	// 0x00
	uint8_t  cfis[64];	// Command FIS
 
	// 0x40
	uint8_t  acmd[16];	// ATAPI command, 12 or 16 bytes
 
	// 0x50
	uint8_t  rsv[48];	// Reserved
 
	// 0x80
	HBA_PRDT_ENTRY	prdt_entry[1];	// Bodily area descriptor desk entries, 0 ~ 65535
} HBA_CMD_TBL;
 
typedef struct tagHBA_PRDT_ENTRY
{
	uint32_t dba;		// Knowledge base tackle
	uint32_t dbau;		// Knowledge base tackle higher 32 bits
	uint32_t rsv0;		// Reserved
 
	// DW3
	uint32_t dbc:22;		// Byte rely, 4M max
	uint32_t rsv1:9;		// Reserved
	uint32_t i:1;		// Interrupt on completion
} HBA_PRDT_ENTRY;

Detect hooked up SATA units

1) Which port is gadget hooked up

As specified within the AHCI specification, firmware (BIOS) ought to initialize the AHCI controller right into a minimal workable state. OS normally needn’t reinitialize it from the underside. A lot info is already there when the OS boots.

The Port Carried out register (HBA_MEM.pi) is a 32 bit worth and every bit represents a port. If the bit is about, the in accordance port has a tool hooked up, in any other case the port is free.

2) What sort of gadget is hooked up

There are 4 sorts of SATA units, and their signatures are outlined as beneath. The Port Signature register (HBA_PORT.sig) comprises the gadget signature, simply learn this register to search out which type of gadget is hooked up on the port. Some buggy AHCI controllers could not set the Signature register appropriately. Essentially the most dependable means is to guage from the Determine knowledge learn again from the gadget.

#outline	SATA_SIG_ATA	0x00000101	// SATA drive
#outline	SATA_SIG_ATAPI	0xEB140101	// SATAPI drive
#outline	SATA_SIG_SEMB	0xC33C0101	// Enclosure administration bridge
#outline	SATA_SIG_PM	0x96690101	// Port multiplier
 
#outline AHCI_DEV_NULL 0
#outline AHCI_DEV_SATA 1
#outline AHCI_DEV_SEMB 2
#outline AHCI_DEV_PM 3
#outline AHCI_DEV_SATAPI 4
 
#outline HBA_PORT_IPM_ACTIVE 1
#outline HBA_PORT_DET_PRESENT 3
 
void probe_port(HBA_MEM *abar)
{
	// Search disk in applied ports
	uint32_t pi = abar->pi;
	int i = 0;
	whereas (i<32)
	{
		if (pi & 1)
		{
			int dt = check_type(&abar->ports[i]);
			if (dt == AHCI_DEV_SATA)
			{
				trace_ahci("SATA drive discovered at port %dn", i);
			}
			else if (dt == AHCI_DEV_SATAPI)
			{
				trace_ahci("SATAPI drive discovered at port %dn", i);
			}
			else if (dt == AHCI_DEV_SEMB)
			{
				trace_ahci("SEMB drive discovered at port %dn", i);
			}
			else if (dt == AHCI_DEV_PM)
			{
				trace_ahci("PM drive discovered at port %dn", i);
			}
			else
			{
				trace_ahci("No drive discovered at port %dn", i);
			}
		}
 
		pi >>= 1;
		i ++;
	}
}
 
// Test gadget sort
static int check_type(HBA_PORT *port)
{
	uint32_t ssts = port->ssts;
 
	uint8_t ipm = (ssts >> 8) & 0x0F;
	uint8_t det = ssts & 0x0F;
 
	if (det != HBA_PORT_DET_PRESENT)	// Test drive standing
		return AHCI_DEV_NULL;
	if (ipm != HBA_PORT_IPM_ACTIVE)
		return AHCI_DEV_NULL;
 
	swap (port->sig)
	{
	case SATA_SIG_ATAPI:
		return AHCI_DEV_SATAPI;
	case SATA_SIG_SEMB:
		return AHCI_DEV_SEMB;
	case SATA_SIG_PM:
		return AHCI_DEV_PM;
	default:
		return AHCI_DEV_SATA;
	}
}

AHCI port reminiscence house initialization

BIOS could have already configured all the required AHCI reminiscence areas. However the OS normally must reconfigure them to make them match its necessities. It must be famous that Command Checklist should be situated at 1K aligned reminiscence tackle and Acquired FIS be 256 bytes aligned.

Earlier than rebasing Port reminiscence house, OS should look ahead to present pending instructions to complete and inform HBA to cease receiving FIS from the port. In any other case an accidently incoming FIS could also be written into {a partially} configured reminiscence space. That is finished by checking and setting corresponding bits on the Port Command And Standing register (HBA_PORT.cmd). The instance subroutines stop_cmd() and start_cmd() do the job.

The next instance assumes that the HBA has 32 ports applied and every port comprises 32 command slots, and can allocate Eight PRDTs for every command slot. (Notice: in contrast to within the above struct definitions, that is utilizing Eight as a substitute of 1)

#outline	AHCI_BASE	0x400000	// 4M
 
#outline HBA_PxCMD_ST    0x0001
#outline HBA_PxCMD_FRE   0x0010
#outline HBA_PxCMD_FR    0x4000
#outline HBA_PxCMD_CR    0x8000
 
void port_rebase(HBA_PORT *port, int portno)
{
	stop_cmd(port);	// Cease command engine
 
	// Command listing offset: 1K*portno
	// Command listing entry dimension = 32
	// Command listing entry maxim rely = 32
	// Command listing maxim dimension = 32*32 = 1K per port
	port->clb = AHCI_BASE + (portno<<10);
	port->clbu = 0;
	memset((void*)(port->clb), 0, 1024);
 
	// FIS offset: 32Okay+256*portno
	// FIS entry dimension = 256 bytes per port
	port->fb = AHCI_BASE + (32<<10) + (portno<<8);
	port->fbu = 0;
	memset((void*)(port->fb), 0, 256);
 
	// Command desk offset: 40Okay + 8K*portno
	// Command desk dimension = 256*32 = 8K per port
	HBA_CMD_HEADER *cmdheader = (HBA_CMD_HEADER*)(port->clb);
	for (int i=0; i<32; i++)
	{
		cmdheader[i].prdtl = 8;	// Eight prdt entries per command desk
					// 256 bytes per command desk, 64+16+48+16*8
		// Command desk offset: 40Okay + 8K*portno + cmdheader_index*256
		cmdheader[i].ctba = AHCI_BASE + (40<<10) + (portno<<13) + (i<<8);
		cmdheader[i].ctbau = 0;
		memset((void*)cmdheader[i].ctba, 0, 256);
	}
 
	start_cmd(port);	// Begin command engine
}
 
// Begin command engine
void start_cmd(HBA_PORT *port)
= HBA_PxCMD_ST; 

 
// Cease command engine
void stop_cmd(HBA_PORT *port)
{
	// Clear ST (bit0)
	port->cmd &= ~HBA_PxCMD_ST;
 
	// Clear FRE (bit4)
	port->cmd &= ~HBA_PxCMD_FRE;
 
	// Wait till FR (bit14), CR (bit15) are cleared
	whereas(1)
	{
		if (port->cmd & HBA_PxCMD_FR)
			proceed;
		if (port->cmd & HBA_PxCMD_CR)
			proceed;
		break;
	}
 
}

AHCI & ATAPI

The documentation relating to utilizing the AHCI interface to entry an ATAPI gadget (most certainly an optical drive) is fairly poorly defined within the specification. Nevertheless, when you perceive that the HBA does many of the be just right for you it’s fairly easy. The AHCI/ATAPI methodology works by issuing the ATA PACKET command (0xA0) as a substitute of the READ (READ is proven within the instance beneath) and populating the ACMD subject of the HBA_CMD_TBL with the 12/16 byte ATAPI command and setting the ‘a’ subject to 1 within the HBA_CMD_HEADER which tells the HBA to carry out the multi-step course of (all finished routinely) of transmitting the PACKET command, then sending the ATAPI gadget the ACMD.

Instance – Learn laborious disk sectors

The code instance exhibits how you can learn “rely” sectors from sector offset “starth:startl” to “buf” with LBA48 mode from HBA “port”. Each PRDT entry comprises 8K bytes knowledge payload at most.

#outline ATA_DEV_BUSY 0x80
#outline ATA_DEV_DRQ 0x08
 
bool learn(HBA_PORT *port, uint32_t startl, uint32_t starth, uint32_t rely, uint16_t *buf)
{
	port->is = (uint32_t) -1;		// Clear pending interrupt bits
	int spin = 0; // Spin lock timeout counter
	int slot = find_cmdslot(port);
	if (slot == -1)
		return false;
 
	HBA_CMD_HEADER *cmdheader = (HBA_CMD_HEADER*)port->clb;
	cmdheader += slot;
	cmdheader->cfl = sizeof(FIS_REG_H2D)/sizeof(uint32_t);	// Command FIS dimension
	cmdheader->w = 0;		// Learn from gadget
	cmdheader->prdtl = (uint16_t)((rely-1)>>4) + 1;	// PRDT entries rely
 
	HBA_CMD_TBL *cmdtbl = (HBA_CMD_TBL*)(cmdheader->ctba);
	memset(cmdtbl, 0, sizeof(HBA_CMD_TBL) +
 		(cmdheader->prdtl-1)*sizeof(HBA_PRDT_ENTRY));
 
	// 8K bytes (16 sectors) per PRDT
	for (int i=0; i<cmdheader->prdtl-1; i++)
	{
		cmdtbl->prdt_entry[i].dba = (uint32_t) buf;
		cmdtbl->prdt_entry[i].dbc = 8*1024-1;	// 8K bytes (this worth ought to at all times be set to 1 lower than the precise worth)
		cmdtbl->prdt_entry[i].i = 1;
		buf += 4*1024;	// 4K phrases
		rely -= 16;	// 16 sectors
	}
	// Final entry
	cmdtbl->prdt_entry[i].dba = (uint32_t) buf;
	cmdtbl->prdt_entry[i].dbc = (rely<<9)-1;	// 512 bytes per sector
	cmdtbl->prdt_entry[i].i = 1;
 
	// Setup command
	FIS_REG_H2D *cmdfis = (FIS_REG_H2D*)(&cmdtbl->cfis);
 
	cmdfis->fis_type = FIS_TYPE_REG_H2D;
	cmdfis->c = 1;	// Command
	cmdfis->command = ATA_CMD_READ_DMA_EX;
 
	cmdfis->lba0 = (uint8_t)startl;
	cmdfis->lba1 = (uint8_t)(startl>>8);
	cmdfis->lba2 = (uint8_t)(startl>>16);
	cmdfis->gadget = 1<<6;	// LBA mode
 
	cmdfis->lba3 = (uint8_t)(startl>>24);
	cmdfis->lba4 = (uint8_t)starth;
	cmdfis->lba5 = (uint8_t)(starth>>8);
 
	cmdfis->countl = rely & 0xFF;
	cmdfis->counth = (rely >> 8) & 0xFF;
 
	// The beneath loop waits till the port is now not busy earlier than issuing a brand new command
	whereas ((port->tfd & (ATA_DEV_BUSY | ATA_DEV_DRQ)) && spin < 1000000)
	{
		spin++;
	}
	if (spin == 1000000)
	{
		trace_ahci("Port is hungn");
		return FALSE;
	}
 
	port->ci = 1<<slot;	// Challenge command
 
	// Look ahead to completion
	whereas (1)
	{
		// In some longer period reads, it could be useful to spin on the DPS bit 
		// within the PxIS port subject as effectively (1 << 5)
		if ((port->ci & (1<<slot)) == 0) 
			break;
		if (port->is & HBA_PxIS_TFES)	// Activity file error
		{
			trace_ahci("Learn disk errorn");
			return FALSE;
		}
	}
 
	// Test once more
	if (port->is & HBA_PxIS_TFES)
	{
		trace_ahci("Learn disk errorn");
		return FALSE;
	}
 
	return true;
}
 
// Discover a free command listing slot
int find_cmdslot(HBA_PORT *port)
{
	// If not set in SACT and CI, the slot is free
	uint32_t slots = (port->sact | port->ci);
	for (int i=0; i<cmdslots; i++)
	{
		if ((slots&1) == 0)
			return i;
		slots >>= 1;
	}
	trace_ahci("Can't discover free command listing entryn");
	return -1;
}

Guidelines

Initialisation

  • Allow interrupts, DMA, and reminiscence house entry within the PCI command register
  • Reminiscence map BAR 5 register as uncacheable.
  • Carry out BIOS/OS handoff (if the bit within the prolonged capabilities is about)
  • Reset controller
  • Register IRQ handler, utilizing interrupt line given within the PCI register. This interrupt line could also be shared with different units, so the standard implications of this apply.
  • Allow AHCI mode and interrupts in world host management register.
  • Learn capabilities registers. Test 64-bit DMA is supported in case you want it.
  • For all of the applied ports:
    • Allocate bodily reminiscence for its command listing, the acquired FIS, and its command tables. Be sure the command tables are 128 byte aligned.
    • Reminiscence map these as uncacheable.
    • Set command listing and acquired FIS tackle registers (and higher registers, if supported).
    • Setup command listing entries to level to the corresponding command desk.
    • Reset the port.
    • Begin command listing processing with the port’s command register.
    • Allow interrupts for the port. The D2H bit will sign accomplished instructions.
    • Learn signature/standing of the port to see if it related to a drive.
    • Ship IDENTIFY ATA command to related drives. Get their sector dimension and rely.

Begin learn/write command

  • Choose an obtainable command slot to make use of.
  • Setup command FIS.
  • Setup PRDT.
  • Setup command listing entry.
  • Challenge the command, and document individually that you’ve got issued it.

IRQ handler

  • Test world interrupt standing. Write again its worth. For all of the ports which have a corresponding set bit…
  • Test the port interrupt standing. Write again its worth. If zero, proceed to the subsequent port.
  • If error bit set, reset port/retry instructions as mandatory.
  • Evaluate issued instructions register to the instructions you’ve got recorded as issuing. For any bits the place a command was issued however is now not working, because of this the command has accomplished.
  • As soon as finished, proceed checking if another units sharing the IRQ additionally want servicing.

Exterior Hyperlinks

Leave a Reply

Your email address will not be published. Required fields are marked *