Please look at notes.txt for implementation and progress status.
[reactos.git] / drivers / storage / storahci / storahci.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GNU GPLv2 only as published by the Free Software Foundation
4 * PURPOSE: To Implement AHCI Miniport driver targeting storport NT 5.2
5 * PROGRAMMERS: Aman Priyadarshi (aman.eureka@gmail.com)
6 */
7
8 #include "storahci.h"
9
10 /**
11 * @name AhciPortInitialize
12 * @implemented
13 *
14 * Initialize port by setting up PxCLB & PxFB Registers
15 *
16 * @param PortExtension
17 *
18 * @return
19 * Return true if intialization was successful
20 */
21 BOOLEAN
22 AhciPortInitialize (
23 __in PAHCI_PORT_EXTENSION PortExtension
24 )
25 {
26 ULONG mappedLength, portNumber;
27 PAHCI_MEMORY_REGISTERS abar;
28 PAHCI_ADAPTER_EXTENSION adapterExtension;
29 STOR_PHYSICAL_ADDRESS commandListPhysical, receivedFISPhysical;
30
31 DebugPrint("AhciPortInitialize()\n");
32
33 adapterExtension = PortExtension->AdapterExtension;
34 abar = adapterExtension->ABAR_Address;
35 portNumber = PortExtension->PortNumber;
36
37 NT_ASSERT(abar != NULL);
38 NT_ASSERT(portNumber < adapterExtension->PortCount);
39
40 PortExtension->Port = &abar->PortList[portNumber];
41
42 commandListPhysical = StorPortGetPhysicalAddress(adapterExtension,
43 NULL,
44 PortExtension->CommandList,
45 &mappedLength);
46
47 if ((mappedLength == 0) || ((commandListPhysical.LowPart % 1024) != 0))
48 {
49 DebugPrint("\tcommandListPhysical mappedLength:%d\n", mappedLength);
50 return FALSE;
51 }
52
53 receivedFISPhysical = StorPortGetPhysicalAddress(adapterExtension,
54 NULL,
55 PortExtension->ReceivedFIS,
56 &mappedLength);
57
58 if ((mappedLength == 0) || ((receivedFISPhysical.LowPart % 256) != 0))
59 {
60 DebugPrint("\treceivedFISPhysical mappedLength:%d\n", mappedLength);
61 return FALSE;
62 }
63
64 // 10.1.2 For each implemented port, system software shall allocate memory for and program:
65 //  PxCLB and PxCLBU (if CAP.S64A is set to ‘1’)
66 //  PxFB and PxFBU (if CAP.S64A is set to ‘1’)
67 // Note: Assuming 32bit support only
68 StorPortWriteRegisterUlong(adapterExtension, &PortExtension->Port->CLB, commandListPhysical.LowPart);
69 if (IsAdapterCAPS64(adapterExtension->CAP))
70 {
71 StorPortWriteRegisterUlong(adapterExtension, &PortExtension->Port->CLBU, commandListPhysical.HighPart);
72 }
73
74 StorPortWriteRegisterUlong(adapterExtension, &PortExtension->Port->FB, receivedFISPhysical.LowPart);
75 if (IsAdapterCAPS64(adapterExtension->CAP))
76 {
77 StorPortWriteRegisterUlong(adapterExtension, &PortExtension->Port->FBU, receivedFISPhysical.HighPart);
78 }
79
80 // set device power state flag to D0
81 PortExtension->DevicePowerState = StorPowerDeviceD0;
82
83 // clear pending interrupts
84 StorPortWriteRegisterUlong(adapterExtension, &PortExtension->Port->SERR, (ULONG)-1);
85 StorPortWriteRegisterUlong(adapterExtension, &PortExtension->Port->IS, (ULONG)-1);
86 StorPortWriteRegisterUlong(adapterExtension, PortExtension->AdapterExtension->IS, (1 << PortExtension->PortNumber));
87
88 return TRUE;
89 }// -- AhciPortInitialize();
90
91 /**
92 * @name AhciAllocateResourceForAdapter
93 * @implemented
94 *
95 * Allocate memory from poll for required pointers
96 *
97 * @param AdapterExtension
98 * @param ConfigInfo
99 *
100 * @return
101 * return TRUE if allocation was successful
102 */
103 BOOLEAN
104 AhciAllocateResourceForAdapter (
105 __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
106 __in PPORT_CONFIGURATION_INFORMATION ConfigInfo
107 )
108 {
109 PVOID portsExtension = NULL;
110 PCHAR nonCachedExtension;
111 ULONG status, index, NCS, AlignedNCS;
112 ULONG portCount, portImplemented, nonCachedExtensionSize;
113
114 DebugPrint("AhciAllocateResourceForAdapter()\n");
115
116 NCS = AHCI_Global_Port_CAP_NCS(AdapterExtension->CAP);
117 AlignedNCS = ROUND_UP(NCS, 8);
118
119 // get port count -- Number of set bits in `AdapterExtension->PortImplemented`
120 portCount = 0;
121 portImplemented = AdapterExtension->PortImplemented;
122
123 NT_ASSERT(portImplemented != 0);
124 for (index = MAXIMUM_AHCI_PORT_COUNT - 1; index > 0; index--)
125 if ((portImplemented & (1 << index)) != 0)
126 break;
127
128 portCount = index + 1;
129 DebugPrint("\tPort Count: %d\n", portCount);
130
131 AdapterExtension->PortCount = portCount;
132 nonCachedExtensionSize = sizeof(AHCI_COMMAND_HEADER) * AlignedNCS + //should be 1K aligned
133 sizeof(AHCI_RECEIVED_FIS);
134
135 // align nonCachedExtensionSize to 1024
136 nonCachedExtensionSize = ROUND_UP(nonCachedExtensionSize, 1024);
137
138 AdapterExtension->NonCachedExtension = StorPortGetUncachedExtension(AdapterExtension,
139 ConfigInfo,
140 nonCachedExtensionSize * portCount);
141
142 if (AdapterExtension->NonCachedExtension == NULL)
143 {
144 DebugPrint("\tadapterExtension->NonCachedExtension == NULL\n");
145 return FALSE;
146 }
147
148 nonCachedExtension = AdapterExtension->NonCachedExtension;
149 AhciZeroMemory(nonCachedExtension, nonCachedExtensionSize * portCount);
150
151 for (index = 0; index < portCount; index++)
152 {
153 AdapterExtension->PortExtension[index].IsActive = FALSE;
154 if ((AdapterExtension->PortImplemented & (1 << index)) != 0)
155 {
156 AdapterExtension->PortExtension[index].PortNumber = index;
157 AdapterExtension->PortExtension[index].IsActive = TRUE;
158 AdapterExtension->PortExtension[index].AdapterExtension = AdapterExtension;
159 AdapterExtension->PortExtension[index].CommandList = nonCachedExtension;
160 AdapterExtension->PortExtension[index].ReceivedFIS = (PAHCI_RECEIVED_FIS)(nonCachedExtension + sizeof(AHCI_COMMAND_HEADER) * AlignedNCS);
161 nonCachedExtension += nonCachedExtensionSize;
162 }
163 }
164
165 return TRUE;
166 }// -- AhciAllocateResourceForAdapter();
167
168 /**
169 * @name AhciHwInitialize
170 * @implemented
171 *
172 * initializes the HBA and finds all devices that are of interest to the miniport driver.
173 *
174 * @param adapterExtension
175 *
176 * @return
177 * return TRUE if intialization was successful
178 */
179 BOOLEAN
180 AhciHwInitialize (
181 __in PVOID AdapterExtension
182 )
183 {
184 ULONG ghc, messageCount, status;
185 PAHCI_ADAPTER_EXTENSION adapterExtension;
186
187 DebugPrint("AhciHwInitialize()\n");
188
189 adapterExtension = AdapterExtension;
190 adapterExtension->StateFlags.MessagePerPort = FALSE;
191
192 // First check what type of interrupt/synchronization device is using
193 ghc = StorPortReadRegisterUlong(adapterExtension, &adapterExtension->ABAR_Address->GHC);
194
195 // When set to ‘1’ by hardware, indicates that the HBA requested more than one MSI vector
196 // but has reverted to using the first vector only. When this bit is cleared to ‘0’,
197 // the HBA has not reverted to single MSI mode (i.e. hardware is already in single MSI mode,
198 // software has allocated the number of messages requested
199 if ((ghc & AHCI_Global_HBA_CONTROL_MRSM) == 0)
200 {
201 adapterExtension->StateFlags.MessagePerPort = TRUE;
202 DebugPrint("\tMultiple MSI based message not supported\n");
203 }
204
205 return TRUE;
206 }// -- AhciHwInitialize();
207
208 /**
209 * @name AhciInterruptHandler
210 * @not_implemented
211 *
212 * Interrupt Handler for PortExtension
213 *
214 * @param PortExtension
215 *
216 */
217 VOID
218 AhciInterruptHandler (
219 __in PAHCI_PORT_EXTENSION PortExtension
220 )
221 {
222 ULONG IS;
223 AHCI_INTERRUPT_STATUS PxIS;
224 AHCI_INTERRUPT_STATUS PxISMasked;
225 PAHCI_ADAPTER_EXTENSION AdapterExtension;
226
227 DebugPrint("AhciInterruptHandler()\n");
228 DebugPrint("\tPort Number: %d\n", PortExtension->PortNumber);
229
230 AdapterExtension = PortExtension->AdapterExtension;
231 NT_ASSERT(IsPortValid(AdapterExtension, PortExtension->PortNumber));
232
233 // 5.5.3
234 // 1. Software determines the cause of the interrupt by reading the PxIS register.
235 // It is possible for multiple bits to be set
236 // 2. Software clears appropriate bits in the PxIS register corresponding to the cause of the interrupt.
237 // 3. Software clears the interrupt bit in IS.IPS corresponding to the port.
238 // 4. If executing non-queued commands, software reads the PxCI register, and compares the current value to
239 // the list of commands previously issued by software that are still outstanding.
240 // If executing native queued commands, software reads the PxSACT register and compares the current
241 // value to the list of commands previously issued by software.
242 // Software completes with success any outstanding command whose corresponding bit has been cleared in
243 // the respective register. PxCI and PxSACT are volatile registers; software should only use their values
244 // to determine commands that have completed, not to determine which commands have previously been issued.
245 // 5. If there were errors, noted in the PxIS register, software performs error recovery actions (see section 6.2.2).
246 PxISMasked.Status = 0;
247 PxIS.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->IS);
248
249 // 6.2.2
250 // Fatal Error
251 // signified by the setting of PxIS.HBFS, PxIS.HBDS, PxIS.IFS, or PxIS.TFES
252 if (PxIS.HBFS || PxIS.HBDS || PxIS.IFS || PxIS.TFES)
253 {
254 // In this state, the HBA shall not issue any new commands nor acknowledge DMA Setup FISes to process
255 // any native command queuing commands. To recover, the port must be restarted
256 // To detect an error that requires software recovery actions to be performed,
257 // software should check whether any of the following status bits are set on an interrupt:
258 // PxIS.HBFS, PxIS.HBDS, PxIS.IFS, and PxIS.TFES. If any of these bits are set,
259 // software should perform the appropriate error recovery actions based on whether
260 // non-queued commands were being issued or native command queuing commands were being issued.
261
262 DebugPrint("\tFata Error: %x\n", PxIS.Status);
263 }
264
265 // Normal Command Completion
266 // 3.3.5
267 // A D2H Register FIS has been received with the ‘I’ bit set, and has been copied into system memory.
268 PxISMasked.DHRS = PxIS.DHRS;
269 // A PIO Setup FIS has been received with the ‘I’ bit set, it has been copied into system memory.
270 PxISMasked.PSS = PxIS.PSS;
271 // A DMA Setup FIS has been received with the ‘I’ bit set and has been copied into system memory.
272 PxISMasked.DSS = PxIS.DSS;
273 // A Set Device Bits FIS has been received with the ‘I’ bit set and has been copied into system memory/
274 PxISMasked.SDBS = PxIS.SDBS;
275 // A PRD with the ‘I’ bit set has transferred all of its data.
276 PxISMasked.DPS = PxIS.DPS;
277
278 if (PxISMasked.Status != 0)
279 {
280 StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->IS, PxISMasked.Status);
281 }
282
283 // 10.7.1.1
284 // Clear port interrupt
285 // It is set by the level of the virtual interrupt line being a set, and cleared by a write of ‘1’ from the software.
286 IS = (1 << PortExtension->PortNumber);
287 StorPortWriteRegisterUlong(AdapterExtension, AdapterExtension->IS, IS);
288
289 return;
290 }// -- AhciInterruptHandler();
291
292 /**
293 * @name AhciHwInterrupt
294 * @implemented
295 *
296 * The Storport driver calls the HwStorInterrupt routine after the HBA generates an interrupt request.
297 *
298 * @param AdapterExtension
299 *
300 * @return
301 * return TRUE Indicates that an interrupt was pending on adapter.
302 * return FALSE Indicates the interrupt was not ours.
303 */
304 BOOLEAN
305 AhciHwInterrupt(
306 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
307 )
308 {
309 ULONG portPending, nextPort, i, portCount;
310
311 DebugPrint("AhciHwInterrupt()\n");
312
313 if (AdapterExtension->StateFlags.Removed)
314 {
315 return FALSE;
316 }
317
318 portPending = StorPortReadRegisterUlong(AdapterExtension, AdapterExtension->IS);
319 // we process interrupt for implemented ports only
320 portCount = AdapterExtension->PortCount;
321 portPending = portPending & AdapterExtension->PortImplemented;
322
323 if (portPending == 0)
324 {
325 return FALSE;
326 }
327
328 for (i = 1; i <= portCount; i++)
329 {
330 nextPort = (AdapterExtension->LastInterruptPort + i) % portCount;
331
332 if ((portPending & (0x1 << nextPort)) == 0)
333 continue;
334
335 NT_ASSERT(IsPortValid(AdapterExtension, nextPort));
336
337 if ((nextPort == AdapterExtension->LastInterruptPort) ||
338 (AdapterExtension->PortExtension[nextPort].IsActive == FALSE))
339 {
340 return FALSE;
341 }
342
343 // we can assign this interrupt to this port
344 AdapterExtension->LastInterruptPort = nextPort;
345 AhciInterruptHandler(&AdapterExtension->PortExtension[nextPort]);
346
347 // interrupt belongs to this device
348 // should always return TRUE
349 return TRUE;
350 }
351
352 DebugPrint("\tSomething went wrong");
353 return FALSE;
354 }// -- AhciHwInterrupt();
355
356 /**
357 * @name AhciHwStartIo
358 * @not_implemented
359 *
360 * The Storport driver calls the HwStorStartIo routine one time for each incoming I/O request.
361 *
362 * @param adapterExtension
363 * @param Srb
364 *
365 * @return
366 * return TRUE if the request was accepted
367 * return FALSE if the request must be submitted later
368 */
369 BOOLEAN
370 AhciHwStartIo (
371 __in PVOID AdapterExtension,
372 __in PSCSI_REQUEST_BLOCK Srb
373 )
374 {
375 UCHAR function, pathId;
376 PAHCI_ADAPTER_EXTENSION adapterExtension;
377
378 DebugPrint("AhciHwStartIo()\n");
379
380 pathId = Srb->PathId;
381 function = Srb->Function;
382 adapterExtension = AdapterExtension;
383
384 if (!IsPortValid(adapterExtension, pathId))
385 {
386 Srb->SrbStatus = SRB_STATUS_NO_DEVICE;
387 StorPortNotification(RequestComplete, adapterExtension, Srb);
388 return TRUE;
389 }
390
391 // https://msdn.microsoft.com/windows/hardware/drivers/storage/handling-srb-function-pnp
392 // If the function member of an SRB is set to SRB_FUNCTION_PNP,
393 // the SRB is a structure of type SCSI_PNP_REQUEST_BLOCK.
394 if (function == SRB_FUNCTION_PNP)
395 {
396 PSCSI_PNP_REQUEST_BLOCK pnpRequest;
397
398 pnpRequest = (PSCSI_PNP_REQUEST_BLOCK)Srb;
399 if ((pnpRequest->SrbPnPFlags & SRB_PNP_FLAGS_ADAPTER_REQUEST) != 0)
400 {
401 if ((pnpRequest->PnPAction == StorRemoveDevice) ||
402 (pnpRequest->PnPAction == StorSurpriseRemoval))
403 {
404 Srb->SrbStatus = SRB_STATUS_SUCCESS;
405 adapterExtension->StateFlags.Removed = 1;
406 DebugPrint("\tAdapter removed\n");
407 }
408 else if (pnpRequest->PnPAction == StorStopDevice)
409 {
410 Srb->SrbStatus = SRB_STATUS_SUCCESS;
411 DebugPrint("\tRequested to Stop the adapter\n");
412 }
413 else
414 {
415 Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
416 }
417
418 StorPortNotification(RequestComplete, adapterExtension, Srb);
419 return TRUE;
420 }
421 }
422
423 if (function == SRB_FUNCTION_EXECUTE_SCSI)
424 {
425 // https://msdn.microsoft.com/en-us/windows/hardware/drivers/storage/handling-srb-function-execute-scsi
426 // On receipt of an SRB_FUNCTION_EXECUTE_SCSI request, a miniport driver's HwScsiStartIo
427 // routine does the following:
428 //
429 // - Gets and/or sets up whatever context the miniport driver maintains in its device,
430 // logical unit, and/or SRB extensions
431 // For example, a miniport driver might set up a logical unit extension with pointers
432 // to the SRB itself and the SRB DataBuffer pointer, the SRB DataTransferLength value,
433 // and a driver-defined value (or CDB SCSIOP_XXX value) indicating the operation to be
434 // carried out on the HBA.
435 //
436 // - Calls an internal routine to program the HBA, as partially directed by the SrbFlags,
437 // for the requested operation
438 // For a device I/O operation, such an internal routine generally selects the target device
439 // and sends the CDB over the bus to the target logical unit.
440 if (Srb->CdbLength > 0)
441 {
442 PCDB cdb = (PCDB)&Srb->Cdb;
443 if (cdb->CDB10.OperationCode == SCSIOP_INQUIRY)
444 {
445 Srb->SrbStatus = DeviceInquiryRequest(adapterExtension, Srb, cdb);
446 }
447 else
448 {
449 Srb->SrbStatus = SRB_STATUS_NO_DEVICE;
450 }
451 }
452 else
453 {
454 Srb->SrbStatus = SRB_STATUS_BAD_FUNCTION;
455 }
456
457 StorPortNotification(RequestComplete, adapterExtension, Srb);
458 return TRUE;
459 }
460
461 DebugPrint("\tUnknown function code recieved: %x\n", function);
462 Srb->SrbStatus = SRB_STATUS_BAD_FUNCTION;
463 StorPortNotification(RequestComplete, adapterExtension, Srb);
464 return TRUE;
465 }// -- AhciHwStartIo();
466
467 /**
468 * @name AhciHwResetBus
469 * @not_implemented
470 *
471 * The HwStorResetBus routine is called by the port driver to clear error conditions.
472 *
473 * @param adapterExtension
474 * @param PathId
475 *
476 * @return
477 * return TRUE if bus was successfully reset
478 */
479 BOOLEAN
480 AhciHwResetBus (
481 __in PVOID AdapterExtension,
482 __in ULONG PathId
483 )
484 {
485 STOR_LOCK_HANDLE lockhandle;
486 PAHCI_ADAPTER_EXTENSION adapterExtension;
487
488 DebugPrint("AhciHwResetBus()\n");
489
490 adapterExtension = AdapterExtension;
491
492 if (IsPortValid(AdapterExtension, PathId))
493 {
494 AhciZeroMemory(&lockhandle, sizeof(lockhandle));
495
496 // Acquire Lock
497 StorPortAcquireSpinLock(AdapterExtension, InterruptLock, NULL, &lockhandle);
498
499 // TODO: Perform port reset
500
501 // Release lock
502 StorPortReleaseSpinLock(AdapterExtension, &lockhandle);
503 }
504
505 return FALSE;
506 }// -- AhciHwResetBus();
507
508 /**
509 * @name AhciHwFindAdapter
510 * @implemented
511 *
512 * The HwStorFindAdapter routine uses the supplied configuration to determine whether a specific
513 * HBA is supported and, if it is, to return configuration information about that adapter.
514 *
515 * 10.1 Platform Communication
516 * http://www.intel.in/content/dam/www/public/us/en/documents/technical-specifications/serial-ata-ahci-spec-rev1_2.pdf
517
518 * @param DeviceExtension
519 * @param HwContext
520 * @param BusInformation
521 * @param ArgumentString
522 * @param ConfigInfo
523 * @param Reserved3
524 *
525 * @return
526 * SP_RETURN_FOUND
527 * Indicates that a supported HBA was found and that the HBA-relevant configuration information was successfully determined and set in the PORT_CONFIGURATION_INFORMATION structure.
528 *
529 * SP_RETURN_ERROR
530 * Indicates that an HBA was found but there was an error obtaining the configuration information. If possible, such an error should be logged with StorPortLogError.
531 *
532 * SP_RETURN_BAD_CONFIG
533 * Indicates that the supplied configuration information was invalid for the adapter.
534 *
535 * SP_RETURN_NOT_FOUND
536 * Indicates that no supported HBA was found for the supplied configuration information.
537 *
538 * @remarks Called by Storport.
539 */
540 ULONG
541 AhciHwFindAdapter (
542 __in PVOID AdapterExtension,
543 __in PVOID HwContext,
544 __in PVOID BusInformation,
545 __in PVOID ArgumentString,
546 __inout PPORT_CONFIGURATION_INFORMATION ConfigInfo,
547 __in PBOOLEAN Reserved3
548 )
549 {
550 ULONG ghc;
551 ULONG index;
552 ULONG portCount, portImplemented;
553 ULONG pci_cfg_len;
554 UCHAR pci_cfg_buf[sizeof(PCI_COMMON_CONFIG)];
555 PACCESS_RANGE accessRange;
556
557 PAHCI_MEMORY_REGISTERS abar;
558 PPCI_COMMON_CONFIG pciConfigData;
559 PAHCI_ADAPTER_EXTENSION adapterExtension;
560
561 DebugPrint("AhciHwFindAdapter()\n");
562
563 adapterExtension = AdapterExtension;
564 adapterExtension->SlotNumber = ConfigInfo->SlotNumber;
565 adapterExtension->SystemIoBusNumber = ConfigInfo->SystemIoBusNumber;
566
567 // get PCI configuration header
568 pci_cfg_len = StorPortGetBusData(
569 adapterExtension,
570 PCIConfiguration,
571 adapterExtension->SystemIoBusNumber,
572 adapterExtension->SlotNumber,
573 pci_cfg_buf,
574 sizeof(PCI_COMMON_CONFIG));
575
576 if (pci_cfg_len != sizeof(PCI_COMMON_CONFIG))
577 {
578 DebugPrint("\tpci_cfg_len != %d :: %d", sizeof(PCI_COMMON_CONFIG), pci_cfg_len);
579 return SP_RETURN_ERROR;//Not a valid device at the given bus number
580 }
581
582 pciConfigData = pci_cfg_buf;
583 adapterExtension->VendorID = pciConfigData->VendorID;
584 adapterExtension->DeviceID = pciConfigData->DeviceID;
585 adapterExtension->RevisionID = pciConfigData->RevisionID;
586 // The last PCI base address register (BAR[5], header offset 0x24) points to the AHCI base memory, it’s called ABAR (AHCI Base Memory Register).
587 adapterExtension->AhciBaseAddress = pciConfigData->u.type0.BaseAddresses[5] & (0xFFFFFFF0);
588
589 DebugPrint("\tVendorID:%d DeviceID:%d RevisionID:%d\n", adapterExtension->VendorID,
590 adapterExtension->DeviceID,
591 adapterExtension->RevisionID);
592
593 // 2.1.11
594 abar = NULL;
595 if (ConfigInfo->NumberOfAccessRanges > 0)
596 {
597 for (index = 0; index < ConfigInfo->NumberOfAccessRanges; index++)
598 {
599 accessRange = *ConfigInfo->AccessRanges;
600 if (accessRange[index].RangeStart.QuadPart == adapterExtension->AhciBaseAddress)
601 {
602 abar = StorPortGetDeviceBase(adapterExtension,
603 ConfigInfo->AdapterInterfaceType,
604 ConfigInfo->SystemIoBusNumber,
605 accessRange[index].RangeStart,
606 accessRange[index].RangeLength,
607 !accessRange[index].RangeInMemory);
608 break;
609 }
610 }
611 }
612
613 if (abar == NULL)
614 {
615 DebugPrint("\tabar == NULL\n");
616 return SP_RETURN_ERROR; // corrupted information supplied
617 }
618
619 adapterExtension->ABAR_Address = abar;
620 adapterExtension->CAP = StorPortReadRegisterUlong(adapterExtension, &abar->CAP);
621 adapterExtension->CAP2 = StorPortReadRegisterUlong(adapterExtension, &abar->CAP2);
622 adapterExtension->Version = StorPortReadRegisterUlong(adapterExtension, &abar->VS);
623 adapterExtension->LastInterruptPort = -1;
624
625 // 10.1.2
626 // 1. Indicate that system software is AHCI aware by setting GHC.AE to ‘1’.
627 // 3.1.2 -- AE bit is read-write only if CAP.SAM is '0'
628 ghc = StorPortReadRegisterUlong(adapterExtension, &abar->GHC);
629 // AE := Highest Significant bit of GHC
630 if ((ghc & AHCI_Global_HBA_CONTROL_AE) != 0)// Hmm, controller was already in power state
631 {
632 // reset controller to have it in known state
633 DebugPrint("\tAE Already set, Reset()\n");
634 if (!AhciAdapterReset(adapterExtension))
635 {
636 DebugPrint("\tReset Failed!\n");
637 return SP_RETURN_ERROR;// reset failed
638 }
639 }
640
641 ghc = AHCI_Global_HBA_CONTROL_AE;// only AE=1
642 StorPortWriteRegisterUlong(adapterExtension, &abar->GHC, ghc);
643
644 adapterExtension->IS = &abar->IS;
645 adapterExtension->PortImplemented = StorPortReadRegisterUlong(adapterExtension, &abar->PI);
646
647 if (adapterExtension->PortImplemented == 0)
648 {
649 DebugPrint("\tadapterExtension->PortImplemented == 0\n");
650 return SP_RETURN_ERROR;
651 }
652
653 ConfigInfo->MaximumTransferLength = MAXIMUM_TRANSFER_LENGTH;//128 KB
654 ConfigInfo->NumberOfPhysicalBreaks = 0x21;
655 ConfigInfo->MaximumNumberOfTargets = 1;
656 ConfigInfo->MaximumNumberOfLogicalUnits = 1;
657 ConfigInfo->ResetTargetSupported = TRUE;
658 ConfigInfo->NumberOfBuses = MAXIMUM_AHCI_PORT_COUNT;
659 ConfigInfo->SynchronizationModel = StorSynchronizeFullDuplex;
660 ConfigInfo->ScatterGather = TRUE;
661
662 // Turn IE -- Interrupt Enabled
663 ghc |= AHCI_Global_HBA_CONTROL_IE;
664 StorPortWriteRegisterUlong(adapterExtension, &abar->GHC, ghc);
665
666 // allocate necessary resource for each port
667 if (!AhciAllocateResourceForAdapter(adapterExtension, ConfigInfo))
668 {
669 DebugPrint("\tAhciAllocateResourceForAdapter() == FALSE\n");
670 return SP_RETURN_ERROR;
671 }
672
673 for (index = 0; index < adapterExtension->PortCount; index++)
674 {
675 if ((adapterExtension->PortImplemented & (0x1 << index)) != 0)
676 AhciPortInitialize(&adapterExtension->PortExtension[index]);
677 }
678
679 return SP_RETURN_FOUND;
680 }// -- AhciHwFindAdapter();
681
682 /**
683 * @name DriverEntry
684 * @implemented
685 *
686 * Initial Entrypoint for storahci miniport driver
687 *
688 * @param DriverObject
689 * @param RegistryPath
690 *
691 * @return
692 * NT_STATUS in case of driver loaded successfully.
693 */
694 ULONG
695 DriverEntry (
696 __in PVOID DriverObject,
697 __in PVOID RegistryPath
698 )
699 {
700 HW_INITIALIZATION_DATA hwInitializationData;
701 ULONG i, status;
702
703 DebugPrint("Storahci Loaded\n");
704
705 // initialize the hardware data structure
706 AhciZeroMemory(&hwInitializationData, sizeof(HW_INITIALIZATION_DATA));
707
708 // set size of hardware initialization structure
709 hwInitializationData.HwInitializationDataSize = sizeof(HW_INITIALIZATION_DATA);
710
711 // identity required miniport entry point routines
712 hwInitializationData.HwStartIo = AhciHwStartIo;
713 hwInitializationData.HwResetBus = AhciHwResetBus;
714 hwInitializationData.HwInterrupt = AhciHwInterrupt;
715 hwInitializationData.HwInitialize = AhciHwInitialize;
716 hwInitializationData.HwFindAdapter = AhciHwFindAdapter;
717
718 // adapter specific information
719 hwInitializationData.NeedPhysicalAddresses = TRUE;
720 hwInitializationData.TaggedQueuing = TRUE;
721 hwInitializationData.AutoRequestSense = TRUE;
722 hwInitializationData.MultipleRequestPerLu = TRUE;
723
724 hwInitializationData.NumberOfAccessRanges = 6;
725 hwInitializationData.AdapterInterfaceType = PCIBus;
726 hwInitializationData.MapBuffers = STOR_MAP_NON_READ_WRITE_BUFFERS;
727
728 // set required extension sizes
729 hwInitializationData.SrbExtensionSize = sizeof(AHCI_SRB_EXTENSION);
730 hwInitializationData.DeviceExtensionSize = sizeof(AHCI_ADAPTER_EXTENSION);
731
732 // register our hw init data
733 status = StorPortInitialize(DriverObject,
734 RegistryPath,
735 &hwInitializationData,
736 NULL);
737
738 DebugPrint("\tstatus:%x\n", status);
739 return status;
740 }// -- DriverEntry();
741
742 /**
743 * @name AhciATA_CFIS
744 * @not_implemented
745 *
746 * create ATA CFIS from Srb
747 *
748 * @param PortExtension
749 * @param Srb
750 *
751 */
752 VOID
753 AhciATA_CFIS (
754 __in PAHCI_PORT_EXTENSION PortExtension,
755 __in PAHCI_SRB_EXTENSION SrbExtension
756 )
757 {
758 DebugPrint("AhciATA_CFIS()\n");
759
760 }// -- AhciATA_CFIS();
761
762 /**
763 * @name AhciATAPI_CFIS
764 * @not_implemented
765 *
766 * create ATAPI CFIS from Srb
767 *
768 * @param PortExtension
769 * @param Srb
770 *
771 */
772 VOID
773 AhciATAPI_CFIS (
774 __in PAHCI_PORT_EXTENSION PortExtension,
775 __in PAHCI_SRB_EXTENSION SrbExtension
776 )
777 {
778 DebugPrint("AhciATAPI_CFIS()\n");
779
780 }// -- AhciATAPI_CFIS();
781
782 /**
783 * @name AhciBuild_PRDT
784 * @not_implemented
785 *
786 * Build PRDT for data transfer
787 *
788 * @param PortExtension
789 * @param Srb
790 *
791 * @return
792 * Return number of entries in PRDT.
793 */
794 ULONG
795 AhciBuild_PRDT (
796 __in PAHCI_PORT_EXTENSION PortExtension,
797 __in PAHCI_SRB_EXTENSION SrbExtension
798 )
799 {
800 DebugPrint("AhciBuild_PRDT()\n");
801
802 return -1;
803 }// -- AhciBuild_PRDT();
804
805 /**
806 * @name AhciProcessSrb
807 * @implemented
808 *
809 * Prepare Srb for IO processing
810 *
811 * @param PortExtension
812 * @param Srb
813 * @param SlotIndex
814 *
815 */
816 VOID
817 AhciProcessSrb (
818 __in PAHCI_PORT_EXTENSION PortExtension,
819 __in PSCSI_REQUEST_BLOCK Srb,
820 __in ULONG SlotIndex
821 )
822 {
823 ULONG prdtlen, sig, length;
824 PAHCI_SRB_EXTENSION SrbExtension;
825 PAHCI_COMMAND_HEADER CommandHeader;
826 PAHCI_ADAPTER_EXTENSION AdapterExtension;
827 STOR_PHYSICAL_ADDRESS CommandTablePhysicalAddress;
828
829 DebugPrint("AhciProcessSrb()\n");
830
831 NT_ASSERT(Srb->PathId == PortExtension->PortNumber);
832
833 SrbExtension = Srb->SrbExtension;
834 AdapterExtension = PortExtension->AdapterExtension;
835
836 NT_ASSERT(SrbExtension != NULL);
837 NT_ASSERT(SrbExtension->AtaFunction != 0);
838
839 if ((SrbExtension->AtaFunction == ATA_FUNCTION_ATA_IDENTIFY) &&
840 (SrbExtension->Task.CommandReg == IDE_COMMAND_NOT_VALID))
841 {
842 // Here we are safe to check SIG register
843 sig = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->SIG);
844 if (sig == 0x101)
845 {
846 SrbExtension->Task.CommandReg = IDE_COMMAND_IDENTIFY;
847 }
848 else
849 {
850 SrbExtension->Task.CommandReg = IDE_COMMAND_ATAPI_IDENTIFY;
851 }
852 }
853
854 NT_ASSERT(SlotIndex < AHCI_Global_Port_CAP_NCS(AdapterExtension->CAP));
855 SrbExtension->SlotIndex = SlotIndex;
856
857 // program the CFIS in the CommandTable
858 CommandHeader = &PortExtension->CommandList[SlotIndex];
859
860 if (IsAtaCommand(SrbExtension->AtaFunction))
861 {
862 AhciATA_CFIS(PortExtension, SrbExtension);
863 }
864 else if (IsAtapiCommand(SrbExtension->AtaFunction))
865 {
866 AhciATAPI_CFIS(PortExtension, SrbExtension);
867 }
868
869 prdtlen = 0;
870 if (IsDataTransferNeeded(SrbExtension))
871 {
872 prdtlen = AhciBuild_PRDT(PortExtension, SrbExtension);
873 NT_ASSERT(prdtlen != -1);
874 }
875
876 // Program the command header
877 CommandHeader->DI.PRDTL = prdtlen; // number of entries in PRD table
878 CommandHeader->DI.CFL = 5;
879 CommandHeader->DI.W = (SrbExtension->Flags & ATA_FLAGS_DATA_OUT) ? 1 : 0;
880 CommandHeader->DI.P = 0; // ATA Specifications says so
881 CommandHeader->DI.PMP = 0; // Port Multiplier
882
883 // Reset -- Manual Configuation
884 CommandHeader->DI.R = 0;
885 CommandHeader->DI.B = 0;
886 CommandHeader->DI.C = 0;
887
888 CommandHeader->PRDBC = 0;
889
890 CommandHeader->Reserved[0] = 0;
891 CommandHeader->Reserved[1] = 0;
892 CommandHeader->Reserved[2] = 0;
893 CommandHeader->Reserved[3] = 0;
894
895 // set CommandHeader CTBA
896 // I am really not sure if SrbExtension is 128 byte aligned or not
897 // Command FIS will not work if it is not so.
898 CommandTablePhysicalAddress = StorPortGetPhysicalAddress(AdapterExtension,
899 NULL,
900 SrbExtension,
901 &length);
902
903 // command table alignment
904 NT_ASSERT((CommandTablePhysicalAddress.LowPart % 128) == 0);
905
906 CommandHeader->CTBA0 = CommandTablePhysicalAddress.LowPart;
907
908 if (IsAdapterCAPS64(AdapterExtension->CAP))
909 {
910 CommandHeader->CTBA_U0 = CommandTablePhysicalAddress.HighPart;
911 }
912
913 // mark this slot
914 PortExtension->OccupiedSlots |= SlotIndex;
915 return;
916 }// -- AhciProcessSrb();
917
918 /**
919 * @name AhciActivatePort
920 * @not_implemented
921 *
922 * Program Port and populate command list
923 *
924 * @param PortExtension
925 *
926 */
927 VOID
928 AhciActivatePort (
929 __in PAHCI_PORT_EXTENSION PortExtension
930 )
931 {
932 DebugPrint("AhciActivatePort()\n");
933
934 return;
935 }// -- AhciActivatePort();
936
937 /**
938 * @name AhciProcessIO
939 * @implemented
940 *
941 * Acquire Exclusive lock to port, populate pending commands to command List
942 * program controller's port to process new commands in command list.
943 *
944 * @param AdapterExtension
945 * @param PathId
946 * @param Srb
947 *
948 */
949 VOID
950 AhciProcessIO (
951 __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
952 __in UCHAR PathId,
953 __in PSCSI_REQUEST_BLOCK Srb
954 )
955 {
956 STOR_LOCK_HANDLE lockhandle;
957 PSCSI_REQUEST_BLOCK tmpSrb;
958 PAHCI_PORT_EXTENSION PortExtension;
959 ULONG commandSlotMask, occupiedSlots, slotIndex, NCS;
960
961 DebugPrint("AhciProcessIO()\n");
962 DebugPrint("\tPathId: %d\n", PathId);
963
964 PortExtension = &AdapterExtension->PortExtension[PathId];
965
966 NT_ASSERT(PathId < AdapterExtension->PortCount);
967
968 // add Srb to queue
969 AddQueue(&PortExtension->SrbQueue, Srb);
970
971 if (PortExtension->IsActive == FALSE)
972 return; // we should wait for device to get active
973
974 AhciZeroMemory(&lockhandle, sizeof(lockhandle));
975
976 // Acquire Lock
977 StorPortAcquireSpinLock(AdapterExtension, InterruptLock, NULL, &lockhandle);
978
979 occupiedSlots = PortExtension->OccupiedSlots; // Busy command slots for given port
980 NCS = AHCI_Global_Port_CAP_NCS(AdapterExtension->CAP);
981 commandSlotMask = (1 << NCS) - 1; // available slots mask
982
983 commandSlotMask = (commandSlotMask & ~occupiedSlots);
984 if(commandSlotMask != 0)
985 {
986 // iterate over HBA port slots
987 for (slotIndex = 0; slotIndex < NCS; slotIndex++)
988 {
989 // find first free slot
990 if ((commandSlotMask & (1 << slotIndex)) != 0)
991 {
992 tmpSrb = RemoveQueue(&PortExtension->SrbQueue);
993 if (tmpSrb != NULL)
994 {
995 NT_ASSERT(Srb->PathId == PathId);
996 AhciProcessSrb(PortExtension, tmpSrb, slotIndex);
997 }
998 else
999 {
1000 break;
1001 }
1002 }
1003 else
1004 {
1005 break;
1006 }
1007 }
1008 }
1009
1010 // program HBA port
1011 AhciActivatePort(PortExtension);
1012
1013 // Release Lock
1014 StorPortReleaseSpinLock(AdapterExtension, &lockhandle);
1015
1016 return;
1017 }// -- AhciProcessIO();
1018
1019 /**
1020 * @name DeviceInquiryRequest
1021 * @implemented
1022 *
1023 * Tells wheather given port is implemented or not
1024 *
1025 * @param AdapterExtension
1026 * @param Srb
1027 * @param Cdb
1028 *
1029 * @return
1030 * return STOR status for DeviceInquiryRequest
1031 *
1032 * @remark
1033 * http://www.seagate.com/staticfiles/support/disc/manuals/Interface%20manuals/100293068c.pdf
1034 */
1035 ULONG
1036 DeviceInquiryRequest (
1037 __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
1038 __in PSCSI_REQUEST_BLOCK Srb,
1039 __in PCDB Cdb
1040 )
1041 {
1042 PVOID DataBuffer;
1043 ULONG DataBufferLength;
1044 PAHCI_SRB_EXTENSION SrbExtension;
1045
1046 DebugPrint("DeviceInquiryRequest()\n");
1047
1048 SrbExtension = Srb->SrbExtension;
1049
1050 // 3.6.1
1051 // If the EVPD bit is set to zero, the device server shall return the standard INQUIRY data
1052 if (Cdb->CDB6INQUIRY3.EnableVitalProductData == 0)
1053 {
1054 DebugPrint("\tEVPD Inquired\n");
1055 NT_ASSERT(SrbExtension != NULL);
1056
1057 SrbExtension->AtaFunction = ATA_FUNCTION_ATA_IDENTIFY;
1058 SrbExtension->Task.CommandReg = IDE_COMMAND_NOT_VALID;
1059 }
1060 else
1061 {
1062 DebugPrint("\tVPD Inquired\n");
1063
1064 DataBuffer = Srb->DataBuffer;
1065 DataBufferLength = Srb->DataTransferLength;
1066
1067 if (DataBuffer == NULL)
1068 {
1069 return SRB_STATUS_INVALID_REQUEST;
1070 }
1071
1072 AhciZeroMemory(DataBuffer, DataBufferLength);
1073 }
1074
1075 AhciProcessIO(AdapterExtension, Srb->PathId, Srb);
1076 return SRB_STATUS_SUCCESS;
1077 }// -- DeviceInquiryRequest();
1078
1079 /**
1080 * @name AhciAdapterReset
1081 * @implemented
1082 *
1083 * 10.4.3 HBA Reset
1084 * If the HBA becomes unusable for multiple ports, and a software reset or port reset does not correct the
1085 * problem, software may reset the entire HBA by setting GHC.HR to ‘1’. When software sets the GHC.HR
1086 * bit to ‘1’, the HBA shall perform an internal reset action. The bit shall be cleared to ‘0’ by the HBA when
1087 * the reset is complete. A software write of ‘0’ to GHC.HR shall have no effect. To perform the HBA reset,
1088 * software sets GHC.HR to ‘1’ and may poll until this bit is read to be ‘0’, at which point software knows that
1089 * the HBA reset has completed.
1090 * If the HBA has not cleared GHC.HR to ‘0’ within 1 second of software setting GHC.HR to ‘1’, the HBA is in
1091 * a hung or locked state.
1092 *
1093 * @param AdapterExtension
1094 *
1095 * @return
1096 * TRUE in case AHCI Controller RESTARTED successfully. i.e GHC.HR == 0
1097 */
1098 BOOLEAN
1099 AhciAdapterReset (
1100 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
1101 )
1102 {
1103 ULONG ghc, ticks, ghcStatus;
1104 PAHCI_MEMORY_REGISTERS abar = NULL;
1105
1106 DebugPrint("AhciAdapterReset()\n");
1107
1108 abar = AdapterExtension->ABAR_Address;
1109 if (abar == NULL) // basic sanity
1110 {
1111 return FALSE;
1112 }
1113
1114 // HR -- Very first bit (lowest significant)
1115 ghc = AHCI_Global_HBA_CONTROL_HR;
1116 StorPortWriteRegisterUlong(AdapterExtension, &abar->GHC, ghc);
1117
1118 for (ticks = 0; ticks < 50; ++ticks)
1119 {
1120 ghcStatus = StorPortReadRegisterUlong(AdapterExtension, &abar->GHC);
1121 if ((ghcStatus & AHCI_Global_HBA_CONTROL_HR) == 0)
1122 {
1123 break;
1124 }
1125 StorPortStallExecution(20000);
1126 }
1127
1128 if (ticks == 50)// 1 second
1129 {
1130 DebugPrint("\tDevice Timeout\n");
1131 return FALSE;
1132 }
1133
1134 return TRUE;
1135 }// -- AhciAdapterReset();
1136
1137 /**
1138 * @name AhciZeroMemory
1139 * @implemented
1140 *
1141 * Clear buffer by filling zeros
1142 *
1143 * @param Buffer
1144 * @param BufferSize
1145 */
1146 __inline
1147 VOID
1148 AhciZeroMemory (
1149 __out PCHAR Buffer,
1150 __in ULONG BufferSize
1151 )
1152 {
1153 ULONG i;
1154 for (i = 0; i < BufferSize; i++)
1155 {
1156 Buffer[i] = 0;
1157 }
1158
1159 return;
1160 }// -- AhciZeroMemory();
1161
1162 /**
1163 * @name IsPortValid
1164 * @implemented
1165 *
1166 * Tells wheather given port is implemented or not
1167 *
1168 * @param AdapterExtension
1169 * @param PathId
1170 *
1171 * @return
1172 * return TRUE if provided port is valid (implemented) or not
1173 */
1174 __inline
1175 BOOLEAN
1176 IsPortValid (
1177 __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
1178 __in UCHAR pathId
1179 )
1180 {
1181 NT_ASSERT(pathId >= 0);
1182
1183 if (pathId >= AdapterExtension->PortCount)
1184 {
1185 return FALSE;
1186 }
1187
1188 return AdapterExtension->PortExtension[pathId].IsActive;
1189 }// -- IsPortValid()
1190
1191 /**
1192 * @name AddQueue
1193 * @implemented
1194 *
1195 * Add Srb to Queue
1196 *
1197 * @param Queue
1198 * @param Srb
1199 *
1200 * @return
1201 * return TRUE if Srb is successfully added to Queue
1202 *
1203 */
1204 __inline
1205 BOOLEAN
1206 AddQueue (
1207 __inout PAHCI_QUEUE Queue,
1208 __in PVOID Srb
1209 )
1210 {
1211 NT_ASSERT(Queue->Head < MAXIMUM_QUEUE_BUFFER_SIZE);
1212 NT_ASSERT(Queue->Tail < MAXIMUM_QUEUE_BUFFER_SIZE);
1213
1214 if (Queue->Head == ((Queue->Tail + 1) % MAXIMUM_QUEUE_BUFFER_SIZE))
1215 return FALSE;
1216
1217 Queue->Buffer[Queue->Head++] = Srb;
1218 Queue->Head %= MAXIMUM_QUEUE_BUFFER_SIZE;
1219
1220 return TRUE;
1221 }// -- AddQueue();
1222
1223 /**
1224 * @name RemoveQueue
1225 * @implemented
1226 *
1227 * Remove and return Srb from Queue
1228 *
1229 * @param Queue
1230 *
1231 * @return
1232 * return Srb
1233 *
1234 */
1235 __inline
1236 PVOID
1237 RemoveQueue (
1238 __inout PAHCI_QUEUE Queue
1239 )
1240 {
1241 PVOID Srb;
1242
1243 NT_ASSERT(Queue->Head < MAXIMUM_QUEUE_BUFFER_SIZE);
1244 NT_ASSERT(Queue->Tail < MAXIMUM_QUEUE_BUFFER_SIZE);
1245
1246 if (Queue->Head == Queue->Tail)
1247 return NULL;
1248
1249 Srb = Queue->Buffer[Queue->Tail++];
1250 Queue->Tail %= MAXIMUM_QUEUE_BUFFER_SIZE;
1251
1252 return Srb;
1253 }// -- RemoveQueue();