2 * Acess2 Kernel - AHCI Driver
3 * - By John Hodge (thePowersGang)
9 #define VERSION VER2(1,0)
17 #define MAX_CONTROLLERS 4
20 int AHCI_Install(char **Arguments);
21 int AHCI_Cleanup(void);
23 int AHCI_InitSys(tAHCI_Ctrlr *Ctrlr);
24 void AHCI_QueryDevice(tAHCI_Ctrlr *Ctrlr, int PortNum);
25 void AHCI_IRQHandler(int UNUSED(IRQ), void *Data);
26 void AHCI_int_IRQHandlerPort(tAHCI_Port *Port);
28 int AHCI_ReadSectors(void *Ptr, Uint64 Address, size_t Count, void *Buffer);
29 int AHCI_WriteSectors(void *Ptr, Uint64 Address, size_t Count, const void *Buffer);
31 int AHCI_SendLBA28Cmd(tAHCI_Port *Port, int bWrite,
32 Uint8 Dev, Uint8 Sectors, Uint64 LBA, Uint8 Cmd, size_t Size, void *Data
34 int AHCI_SendLBA48Cmd(tAHCI_Port *Port, int bWrite,
35 Uint8 Dev, Uint8 Sectors, Uint64 LBA, Uint8 Cmd, size_t Size, void *Data
37 int AHCI_DoFIS(tAHCI_Port *Port, int bWrite,
38 size_t CmdSize, const void *CmdData, size_t PktSize, const void *PktData,
39 size_t OutSize, void *OutData
41 int AHCI_WaitForInterrupt(tAHCI_Port *Port, unsigned int Timeout);
44 MODULE_DEFINE(0, VERSION, AHCI, AHCI_Install, AHCI_Cleanup, "LVM", NULL);
45 tAHCI_Ctrlr gaAHCI_Controllers[MAX_CONTROLLERS];
46 tLVM_VolType gAHCI_VolumeType = {
48 .Read = AHCI_ReadSectors,
49 .Write = AHCI_WriteSectors
53 int AHCI_Install(char **Arguments)
56 LOG("offsetof(struct sAHCI_MemSpace, Ports) = %x", offsetof(struct sAHCI_MemSpace, Ports));
57 ASSERT( offsetof(struct sAHCI_MemSpace, Ports) == 0x100 );
59 // 0106XX = Standard, 010400 = RAID
62 while( (id = PCI_GetDeviceByClass(0x010601, 0xFFFFFF, id)) >= 0 && i < MAX_CONTROLLERS )
64 tAHCI_Ctrlr *ctrlr = &gaAHCI_Controllers[i];
66 ctrlr->PMemBase = PCI_GetBAR(id, 5);
68 if( !ctrlr->PMemBase )
70 // ctrlr->PMemBase = PCI_AllocateBAR(id, 5);
71 // TODO: Allocate space
75 if( (ctrlr->PMemBase & 0x1FFF) ) {
76 Log_Warning("AHCI", "Controller %i [PCI %i] is invalid (BAR5=%P)",
77 i, id, ctrlr->PMemBase);
81 ctrlr->IRQ = PCI_GetIRQ(id);
82 IRQ_AddHandler(ctrlr->IRQ, AHCI_IRQHandler, ctrlr);
84 // Prepare MMIO (two pages according to the spec)
85 ctrlr->MMIO = (void*)MM_MapHWPages(ctrlr->PMemBase, 2);
87 LOG("%i [%i]: %P/IRQ%i mapped to %p",
88 i, id, ctrlr->PMemBase, ctrlr->IRQ, ctrlr->MMIO);
90 LOG(" CAP = %x, PI = %x, VS = %x",
91 ctrlr->MMIO->CAP, ctrlr->MMIO->PI, ctrlr->MMIO->VS);
93 if( (rv = AHCI_InitSys(ctrlr)) ) {
94 // Clean up controller's allocations
95 // TODO: Should an init error cause module unload?
102 Log_Notice("AHCI", "Only up to %i controllers are supported", MAX_CONTROLLERS);
108 int AHCI_Cleanup(void)
113 static inline void *AHCI_AllocPage(tAHCI_Ctrlr *Ctrlr, const char *AllocName)
117 if( Ctrlr->Supports64Bit )
118 ret = (void*)MM_AllocDMA(1, 64, NULL);
121 ret = (void*)MM_AllocDMA(1, 32, NULL);
123 Log_Error("AHCI", "Unable to allocate page for '%s'", AllocName);
129 static inline void AHCI_int_SetAddr(tAHCI_Ctrlr *Ctrlr, volatile Uint32 *Addr, tPAddr PAddr)
133 if(Ctrlr->Supports64Bit)
134 Addr[1] = PAddr >> 32;
135 else if( PAddr >> 32 )
136 Log_Notice("AHCI", "Bug: 64-bit address used with 32-bit only controller");
142 int AHCI_InitSys(tAHCI_Ctrlr *Ctrlr)
145 Ctrlr->MMIO->GHC |= AHCI_GHC_AE;
146 // 2. Enumerate ports using PI
147 tTime basetime = now();
148 for( int i = 0; i < 32; i ++ )
150 if( !(Ctrlr->MMIO->PI & (1 << i)) )
155 volatile struct s_port *port = &Ctrlr->MMIO->Ports[i];
156 // 3. (for each port) Ensure that port is not running
157 // - if PxCMD.(ST|CR|FRE|FR) all are clear, port is idle
158 if( (port->PxCMD & (AHCI_PxCMD_ST|AHCI_PxCMD_CR|AHCI_PxCMD_FRE|AHCI_PxCMD_FR)) == 0 )
160 // - Idle is set by clearing PxCMD.ST and waiting for .CR to clear (timeout 500ms)
161 // - AND .FRE = 0, checking .FR (timeout 500ms again)
164 // > On timeout, port/HBA reset
166 Ctrlr->Ports = malloc( Ctrlr->PortCount * sizeof(*Ctrlr->Ports) );
167 if( !Ctrlr->Ports ) {
168 return MODULE_ERR_MALLOC;
170 // - Process timeouts after all ports have been poked, saves time
171 for( int i = 0, idx = 0; i < 32; i ++ )
173 if( !(Ctrlr->MMIO->PI & (1 << i)) )
175 volatile struct s_port *port = &Ctrlr->MMIO->Ports[i];
176 Ctrlr->Ports[idx].Ctrlr = Ctrlr;
177 Ctrlr->Ports[idx].Idx = i;
178 Ctrlr->Ports[idx].MMIO = port;
181 while( (port->PxCMD & (AHCI_PxCMD_CR|AHCI_PxCMD_FR)) && now()-basetime < 500 )
184 if( (port->PxCMD & (AHCI_PxCMD_CR|AHCI_PxCMD_FR)) )
186 Log_Error("AHCI", "Port %i did not return to idle", i);
189 // 4. Read CAP.NCS to get number of command slots
190 Ctrlr->NCS = (Ctrlr->MMIO->CAP & AHCI_CAP_NCS) >> AHCI_CAP_NCS_ofs;
191 // 5. Allocate PxCLB and PxFB for each port (setting PxCMD.FRE once done)
192 struct sAHCI_RcvdFIS *fis_vpage = NULL;
193 struct sAHCI_CmdHdr *cl_vpage = NULL;
194 struct sAHCI_CmdTable *ct_vpage = NULL;
195 for( int i = 0; i < Ctrlr->PortCount; i ++ )
197 tAHCI_Port *port = &Ctrlr->Ports[i];
198 // CLB First (1 KB alignemnt)
199 if( ((tVAddr)cl_vpage & 0xFFF) == 0 ) {
200 cl_vpage = AHCI_AllocPage(Ctrlr, "CLB");
202 return MODULE_ERR_MALLOC;
204 port->CmdList = cl_vpage;
205 cl_vpage += 1024/sizeof(*cl_vpage);
207 tPAddr cl_paddr = MM_GetPhysAddr(port->CmdList);
208 AHCI_int_SetAddr(Ctrlr, &port->MMIO->PxCLB, cl_paddr);
211 // - If there is space for the FIS in the end of the 1K block, use it
212 if( Ctrlr->NCS <= (1024-256)/32 )
214 Ctrlr->Ports[i].RcvdFIS = (void*)(cl_vpage - 256/32);
218 if( ((tVAddr)fis_vpage & 0xFFF) == 0 ) {
219 fis_vpage = AHCI_AllocPage(Ctrlr, "FIS");
221 return MODULE_ERR_MALLOC;
223 Ctrlr->Ports[i].RcvdFIS = fis_vpage;
226 tPAddr fis_paddr = MM_GetPhysAddr(Ctrlr->Ports[i].RcvdFIS);
227 AHCI_int_SetAddr(Ctrlr, &port->MMIO->PxFB, fis_paddr);
230 for( int j = 0; j < Ctrlr->NCS; j ++ )
232 if( ((tVAddr)ct_vpage & 0xFFF) == 0 ) {
233 ct_vpage = AHCI_AllocPage(Ctrlr, "CmdTab");
235 return MODULE_ERR_MALLOC;
237 port->CommandTables[j] = ct_vpage;
238 port->CmdList[j].Flags = 0;
239 port->CmdList[j].PRDTL = 0;
240 port->CmdList[j].PRDBC = 0;
241 AHCI_int_SetAddr(Ctrlr, &port->CmdList[j].CTBA, MM_GetPhysAddr(ct_vpage));
245 LOG("Port #%i: CLB=%p/%P, FB=%p/%P", i,
246 Ctrlr->Ports[i].CmdList, cl_paddr,
247 Ctrlr->Ports[i].RcvdFIS, fis_paddr);
250 // 6. Clear PxSERR with 1 to each implimented bit
251 // > Clear PxIS then IS.IPS before setting PxIE/GHC.IE
252 // 7. Set PxIE with desired interrupts and set GHC.IE
253 for( int i = 0; i < Ctrlr->PortCount; i ++ )
255 tAHCI_Port *port = &Ctrlr->Ports[i];
257 port->MMIO->PxSERR = 0x3FF783;
258 port->MMIO->PxIS = -1;
259 port->MMIO->PxIE = AHCI_PxIS_CPDS|AHCI_PxIS_DSS|AHCI_PxIS_PSS|AHCI_PxIS_DHRS;
261 Ctrlr->MMIO->IS = -1;
262 Ctrlr->MMIO->GHC |= AHCI_GHC_IE;
264 // Start command engine on all implimented ports
265 for( int i = 0; i < Ctrlr->PortCount; i ++ )
267 tAHCI_Port *port = &Ctrlr->Ports[i];
268 port->MMIO->PxCMD |= AHCI_PxCMD_ST|AHCI_PxCMD_FRE;
271 // Detect present ports using:
272 // > PxTFD.STS.BSY = 0, PxTFD.STS.DRQ = 0, and PxSSTS.DET = 3
273 for( int i = 0; i < Ctrlr->PortCount; i ++ )
275 if( Ctrlr->Ports[i].MMIO->PxTFD & (AHCI_PxTFD_STS_BSY|AHCI_PxTFD_STS_DRQ) )
277 if( (Ctrlr->Ports[i].MMIO->PxSSTS & AHCI_PxSSTS_DET) != 3 << AHCI_PxSSTS_DET_ofs )
280 LOG("Port #%i: Connection detected", i);
281 Ctrlr->Ports[i].bPresent = 1;
282 AHCI_QueryDevice(Ctrlr, i);
287 static inline void _flipChars(char *buffer, size_t pairs)
289 for( int i = 0; i < pairs; i ++ )
291 char tmp = buffer[i*2];
292 buffer[i*2] = buffer[i*2+1];
297 void AHCI_QueryDevice(tAHCI_Ctrlr *Ctrlr, int PortNum)
299 tAHCI_Port *const Port = &Ctrlr->Ports[PortNum];
302 AHCI_SendLBA28Cmd(Port, false, 0, 0, 0, ATA_CMD_IDENTIFY_DEVICE, sizeof(data), &data);
303 if( AHCI_WaitForInterrupt(Port, 1000) ) {
304 Log_Error("AHCI", "Port %i:%i ATA IDENTIFY_DEVICE timed out", Ctrlr->ID, PortNum);
307 // TODO: Check status from command
308 // TODO: on error, mark device as bad and return
310 _flipChars(data.SerialNum, 20/2);
311 _flipChars(data.FirmwareVer, 8/2);
312 _flipChars(data.ModelNumber, 40/2);
313 LOG("data.SerialNum = '%.20s'", data.SerialNum);
314 LOG("data.FirmwareVer = '%.8s'", data.FirmwareVer);
315 LOG("data.ModelNumber = '%.40s'", data.ModelNumber);
317 if( data.Sectors48 != 0 ) {
319 LOG("Size[48] = 0x%X", (Uint64)data.Sectors48);
320 Port->SectorCount = data.Sectors48;
324 LOG("Size[28] = 0x%x", data.Sectors28);
325 Port->SectorCount = data.Sectors28;
329 #if AHCI_VOLNAME_SERIAL
330 char lvmname[4+1+20+1];
331 strcpy(lvmname, "ahci:");
332 memcpy(lvmname+5, data.SerialNum, 20);
333 for(int i = 20+5; i-- && lvmname[i] == ' '; )
336 char lvmname[5+3+4+1];
337 snprintf(lvmname, sizeof(lvmname), "ahci:%i-%i", Ctrlr->ID, PortNum);
341 Port->LVMHandle = LVM_AddVolume(&gAHCI_VolumeType, lvmname, Port, 512, Port->SectorCount);
344 void AHCI_IRQHandler(int UNUSED(IRQ), void *Data)
346 tAHCI_Ctrlr *Ctrlr = Data;
347 tAHCI_Port *port = &Ctrlr->Ports[0];
349 Uint32 IS = Ctrlr->MMIO->IS;
350 Uint32 PI = Ctrlr->MMIO->PI;
352 for( int i = 0; i < 32; i ++ )
354 if( !(PI & (1 << i)) )
358 AHCI_int_IRQHandlerPort(port);
363 Ctrlr->MMIO->IS = IS;
366 void AHCI_int_IRQHandlerPort(tAHCI_Port *Port)
368 Uint32 PxIS = Port->MMIO->PxIS;
369 LOG("port[%i]->MMIO->PxIS = %x", Port->Idx, PxIS);
370 Port->LastIS |= PxIS;
371 if( PxIS & AHCI_PxIS_CPDS ) {
372 // Cold port detect change detected
373 // TODO: Handle removal of a device by poking LVM.
376 if( PxIS & AHCI_PxIS_DHRS ) {
377 // LOG("Port->RcvdFIS->RFIS = {");
378 // LOG(".Status = 0x%02x", Port->RcvdFIS->RFIS.Status);
379 // LOG(".Error = 0x%02x", Port->RcvdFIS->RFIS.Error);
383 // Get bitfield of completed commands (Issued but no activity)
384 Uint32 done_commands = Port->IssuedCommands ^ Port->MMIO->PxSACT;
385 //LOG("done_commands = %x", done_commands);
386 for( int i = 0; i < 32; i ++ )
388 if( !(done_commands & (1 << i)) )
390 ASSERT( Port->IssuedCommands & (1 << i) );
391 // If complete, post a SHORTWAIT
392 if( Port->CommandThreads[i] ) {
393 LOG("%i - Poking thread %p", i, Port->CommandThreads[i]);
394 Threads_PostEvent(Port->CommandThreads[i], THREAD_EVENT_SHORTWAIT);
395 Port->CommandThreads[i] = NULL;
397 Port->IssuedCommands &= ~(1 << i);
399 Port->MMIO->PxIS = PxIS;
403 int AHCI_ReadSectors(void *Ptr, Uint64 Address, size_t Count, void *Buffer)
405 tAHCI_Port *Port = Ptr;
407 ENTER("pPtr XAddress xCount pBuffer",
408 Ptr, Address, Count, Buffer);
410 memset(Buffer, 0xFF, Count*512);
412 ASSERT(Count <= 8096/512);
413 if( (Address+Count) < (1 << 24) )
414 AHCI_SendLBA28Cmd(Port, 0, 0, Count, Address, ATA_CMD_READDMA28, Count*512, Buffer);
416 AHCI_SendLBA48Cmd(Port, 0, 0, Count, Address, ATA_CMD_READDMA48, Count*512, Buffer);
417 if( AHCI_WaitForInterrupt(Port, 1000) ) {
418 Log_Notice("AHCI", "Timeout reading from disk");
422 if( Port->RcvdFIS->RFIS.Status & ATA_STATUS_ERR )
424 LOG("Error detected = 0x%02x", Port->RcvdFIS->RFIS.Error);
428 //Debug_HexDump("AHCI_ReadSectors", Buffer, Count*512);
429 // TODO: Check status from command
434 int AHCI_WriteSectors(void *Ptr, Uint64 Address, size_t Count, const void *Buffer)
440 int AHCI_int_GetCommandSlot(tAHCI_Port *Port)
442 Mutex_Acquire(&Port->lCommandSlots);
443 Uint32 PxCI = Port->MMIO->PxCI;
444 Uint32 PxSACT = Port->MMIO->PxSACT;
445 for( int i = 0; i < Port->Ctrlr->NCS; i ++ )
447 if( PxCI & (1 << i) )
449 if( PxSACT & (1 << i) )
451 LOG("Allocated slot %i", i);
452 Port->IssuedCommands |= (1 << i);
457 void AHCI_int_StartCommand(tAHCI_Port *Port, int Slot)
459 Port->MMIO->PxCI = 1 << Slot;
460 Mutex_Release(&Port->lCommandSlots);
462 void AHCI_int_CancelCommand(tAHCI_Port *Port, int Slot)
465 Port->IssuedCommands &= ~(1 << Slot);
466 Mutex_Release(&Port->lCommandSlots);
470 int AHCI_SendLBA48Cmd(tAHCI_Port *Port, int bWrite,
471 Uint8 Dev, Uint8 Sectors, Uint64 LBA, Uint8 Cmd, size_t Size, void *Data)
473 struct sSATA_FIS_H2DRegister regs;
475 regs.Type = SATA_FIS_H2DRegister;
476 regs.Flags = 0x80; // [7]: Update to command register
480 regs.SectorNum = LBA;
481 regs.CylLow = LBA >> 8;
482 regs.CylHigh = LBA >> 16;
483 regs.Dev_Head = 0x40|Dev; // TODO: Need others?
485 regs.SectorNumExp = LBA >> 24;
486 regs.CylLowExp = LBA >> 32;
487 regs.CylHighExp = LBA >> 40;
488 regs.FeaturesExp = 0;
490 regs.SectorCount = Sectors;
491 regs.SectorCountExp = 0;
494 LOG("Sending command %02x : 0x%llx with %p+0x%x", Cmd, LBA, Data, Size);
495 AHCI_DoFIS(Port, bWrite, sizeof(regs), ®s, 0, NULL, Size, Data);
500 int AHCI_SendLBA28Cmd(tAHCI_Port *Port, int bWrite,
501 Uint8 Dev, Uint8 Sectors, Uint64 LBA, Uint8 Cmd, size_t Size, void *Data)
503 struct sSATA_FIS_H2DRegister regs;
505 ASSERT(LBA < (1 << 24));
507 regs.Type = SATA_FIS_H2DRegister;
508 regs.Flags = 0x80; // [7]: Update to command register
512 regs.SectorNum = LBA;
513 regs.CylLow = LBA >> 8;
514 regs.CylHigh = LBA >> 16;
515 regs.Dev_Head = 0x40|Dev|(LBA >> 24);
517 regs.SectorNumExp = 0;
520 regs.FeaturesExp = 0;
522 regs.SectorCount = Sectors;
523 regs.SectorCountExp = 0;
526 LOG("Sending command %02x : 0x%llx with %p+0x%x", Cmd, LBA, Data, Size);
527 AHCI_DoFIS(Port, bWrite, sizeof(regs), ®s, 0, NULL, Size, Data);
532 int AHCI_ReadFIS(tAHCI_Port *Port,
533 size_t CmdSize, const void *CmdData, size_t PktSize, const void *PktData,
534 size_t InSize, void *InData)
536 return AHCI_DoFIS(Port, 0, CmdSize, CmdData, PktSize, PktData, InSize, InData);
538 int AHCI_WriteFIS(tAHCI_Port *Port,
539 size_t CmdSize, const void *CmdData, size_t PktSize, const void *PktData,
540 size_t OutSize, const void *OutData)
542 return AHCI_DoFIS(Port, 1, CmdSize, CmdData, PktSize, PktData, OutSize, (void*)OutData);
545 int AHCI_DoFIS(tAHCI_Port *Port, int bWrite,
546 size_t CmdSize, const void *CmdData, size_t PktSize, const void *PktData,
547 size_t OutSize, void *OutData)
549 // 1. Obtain a command slot
550 int slot = AHCI_int_GetCommandSlot(Port);
555 struct sAHCI_CmdTable *cmdt = Port->CommandTables[slot];
559 Log_Error("AHCI", "_DoFIS: Command FIS size %i > 64", CmdSize);
562 memcpy(cmdt->CFIS, CmdData, CmdSize);
564 Log_Error("AHCI", "_DoFIS: ATAPI packet size %i > 64", PktSize);
567 memcpy(cmdt->ACMD, PktData, PktSize);
572 while( ofs < OutSize )
574 tPAddr phys = MM_GetPhysAddr( (char*)OutData + ofs );
575 ASSERT( !(phys & 3) );
576 // TODO: must be 4 byte aligned, and handle 64-bit addressing
577 size_t len = MIN(OutSize - ofs, PAGE_SIZE - (phys % PAGE_SIZE));
578 ASSERT( !(len & 1) );
579 ASSERT( len < 4*1024*1024 );
580 LOG("PRDTL[%i] = %P+%i", prdtl, phys, len);
581 // TODO: count must be even.
582 AHCI_int_SetAddr(Port->Ctrlr, &cmdt->PRDT[prdtl].DBA, phys);
583 cmdt->PRDT[prdtl].DBC = len-1;
587 ASSERT(ofs == OutSize);
588 cmdt->PRDT[prdtl-1].DBC |= (1<<31); // Set IOC
590 // TODO: Port multipliers
591 Port->CmdList[slot].PRDTL = prdtl;
592 Port->CmdList[slot].Flags = (bWrite << 6) | (CmdSize / 4);
595 Port->CommandThreads[slot] = Proc_GetCurThread();
596 Threads_ClearEvent(THREAD_EVENT_SHORTWAIT);
599 AHCI_int_StartCommand(Port, slot);
603 AHCI_int_CancelCommand(Port, slot);
607 int AHCI_WaitForInterrupt(tAHCI_Port *Port, unsigned int Timeout)
609 // Set up a timeout callback
610 Threads_ClearEvent(THREAD_EVENT_TIMER);
611 tTimer *timeout = Time_AllocateTimer(NULL, NULL);
612 Time_ScheduleTimer(timeout, Timeout);
614 // Wait until an interrupt arrives or the timeout fires
615 Uint32 ev = Threads_WaitEvents(THREAD_EVENT_SHORTWAIT|THREAD_EVENT_TIMER);
616 Time_FreeTimer(timeout);
618 if( ev & THREAD_EVENT_TIMER ) {
619 Log_Notice("AHCI", "Timeout of %i ms exceeded", Timeout);