fixed portCount boundary problem :D
[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 ((adapterExtension->CAP & AHCI_Global_HBA_CAP_S64A) != 0)
70 {
71 StorPortWriteRegisterUlong(adapterExtension, &PortExtension->Port->CLBU, commandListPhysical.HighPart);
72 }
73
74 StorPortWriteRegisterUlong(adapterExtension, &PortExtension->Port->FB, receivedFISPhysical.LowPart);
75 if ((adapterExtension->CAP & AHCI_Global_HBA_CAP_S64A) != 0)
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 * @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 AhciProcessSrb
744 * @not_implemented
745 *
746 * Prepare Srb for IO processing
747 *
748 * @param PortExtension
749 * @param Srb
750 *
751 */
752 VOID
753 AhciProcessSrb (
754 __in PAHCI_PORT_EXTENSION PortExtension,
755 __in PSCSI_REQUEST_BLOCK Srb
756 )
757 {
758 DebugPrint("AhciProcessSrb()\n");
759
760 NT_ASSERT(Srb->PathId == PortExtension->PortNumber);
761
762 return;
763 }// -- AhciProcessSrb();
764
765 /**
766 * @name AhciActivatePort
767 * @not_implemented
768 *
769 * Program Port and populate command list
770 *
771 * @param PortExtension
772 *
773 */
774 VOID
775 AhciActivatePort (
776 __in PAHCI_PORT_EXTENSION PortExtension
777 )
778 {
779 DebugPrint("AhciActivatePort()\n");
780
781 return;
782 }// -- AhciActivatePort();
783
784 /**
785 * @name AhciProcessIO
786 * @implemented
787 *
788 * Acquire Exclusive lock to port, populate pending commands to command List
789 * program controller's port to process new commands in command list.
790 *
791 * @param AdapterExtension
792 * @param PathId
793 * @param Srb
794 *
795 */
796 VOID
797 AhciProcessIO (
798 __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
799 __in UCHAR PathId,
800 __in PSCSI_REQUEST_BLOCK Srb
801 )
802 {
803 STOR_LOCK_HANDLE lockhandle;
804 PSCSI_REQUEST_BLOCK tmpSrb;
805 PAHCI_PORT_EXTENSION PortExtension;
806 ULONG commandSlotMask, occupiedSlots, slotIndex;
807
808 DebugPrint("AhciProcessIO()\n");
809 DebugPrint("\tPathId: %d\n", PathId);
810
811 PortExtension = &AdapterExtension->PortExtension[PathId];
812
813 NT_ASSERT(PathId < AdapterExtension->PortCount);
814
815 // add Srb to queue
816 AddQueue(&PortExtension->SrbQueue, Srb);
817
818 if (PortExtension->IsActive == FALSE)
819 return; // we should wait for device to get active
820
821 AhciZeroMemory(&lockhandle, sizeof(lockhandle));
822
823 // Acquire Lock
824 StorPortAcquireSpinLock(AdapterExtension, InterruptLock, NULL, &lockhandle);
825
826 occupiedSlots = PortExtension->OccupiedSlots; // Busy command slots for given port
827 commandSlotMask = (1 << AHCI_Global_Port_CAP_NCS(AdapterExtension->CAP)) - 1; // available slots mask
828
829 commandSlotMask = (commandSlotMask & ~occupiedSlots);
830 if(commandSlotMask != 0)
831 {
832 // iterate over HBA port slots
833 for (slotIndex = 0; slotIndex <= AHCI_Global_Port_CAP_NCS(AdapterExtension->CAP); slotIndex++)
834 {
835 // find first free slot
836 if ((commandSlotMask & (1 << slotIndex)) != 0)
837 {
838 tmpSrb = RemoveQueue(&PortExtension->SrbQueue);
839 if (tmpSrb != NULL)
840 {
841 NT_ASSERT(Srb->PathId == PathId);
842 AhciProcessSrb(PortExtension, tmpSrb);
843 }
844 else
845 {
846 break;
847 }
848 }
849 else
850 {
851 break;
852 }
853 }
854 }
855
856 // program HBA port
857 AhciActivatePort(PortExtension);
858
859 // Release Lock
860 StorPortReleaseSpinLock(AdapterExtension, &lockhandle);
861
862 return;
863 }// -- AhciProcessIO();
864
865 /**
866 * @name DeviceInquiryRequest
867 * @implemented
868 *
869 * Tells wheather given port is implemented or not
870 *
871 * @param AdapterExtension
872 * @param Srb
873 * @param Cdb
874 *
875 * @return
876 * return STOR status for DeviceInquiryRequest
877 *
878 * @remark
879 * http://www.seagate.com/staticfiles/support/disc/manuals/Interface%20manuals/100293068c.pdf
880 */
881 ULONG
882 DeviceInquiryRequest (
883 __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
884 __in PSCSI_REQUEST_BLOCK Srb,
885 __in PCDB Cdb
886 )
887 {
888 PVOID DataBuffer;
889 ULONG DataBufferLength;
890
891 DebugPrint("DeviceInquiryRequest()\n");
892
893 // 3.6.1
894 // If the EVPD bit is set to zero, the device server shall return the standard INQUIRY data
895 if (Cdb->CDB6INQUIRY3.EnableVitalProductData == 0)
896 {
897 DebugPrint("\tEVPD Inquired\n");
898 }
899 else
900 {
901 DebugPrint("\tVPD Inquired\n");
902
903 DataBuffer = Srb->DataBuffer;
904 DataBufferLength = Srb->DataTransferLength;
905
906 if (DataBuffer == NULL)
907 {
908 return SRB_STATUS_INVALID_REQUEST;
909 }
910
911 AhciZeroMemory(DataBuffer, DataBufferLength);
912 }
913
914 AhciProcessIO(AdapterExtension, Srb->PathId, Srb);
915 return SRB_STATUS_SUCCESS;
916 }// -- DeviceInquiryRequest();
917
918 /**
919 * @name AhciAdapterReset
920 * @implemented
921 *
922 * 10.4.3 HBA Reset
923 * If the HBA becomes unusable for multiple ports, and a software reset or port reset does not correct the
924 * problem, software may reset the entire HBA by setting GHC.HR to ‘1’. When software sets the GHC.HR
925 * bit to ‘1’, the HBA shall perform an internal reset action. The bit shall be cleared to ‘0’ by the HBA when
926 * the reset is complete. A software write of ‘0’ to GHC.HR shall have no effect. To perform the HBA reset,
927 * software sets GHC.HR to ‘1’ and may poll until this bit is read to be ‘0’, at which point software knows that
928 * the HBA reset has completed.
929 * If the HBA has not cleared GHC.HR to ‘0’ within 1 second of software setting GHC.HR to ‘1’, the HBA is in
930 * a hung or locked state.
931 *
932 * @param AdapterExtension
933 *
934 * @return
935 * TRUE in case AHCI Controller RESTARTED successfully. i.e GHC.HR == 0
936 */
937 BOOLEAN
938 AhciAdapterReset (
939 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
940 )
941 {
942 ULONG ghc, ticks, ghcStatus;
943 PAHCI_MEMORY_REGISTERS abar = NULL;
944
945 DebugPrint("AhciAdapterReset()\n");
946
947 abar = AdapterExtension->ABAR_Address;
948 if (abar == NULL) // basic sanity
949 {
950 return FALSE;
951 }
952
953 // HR -- Very first bit (lowest significant)
954 ghc = AHCI_Global_HBA_CONTROL_HR;
955 StorPortWriteRegisterUlong(AdapterExtension, &abar->GHC, ghc);
956
957 for (ticks = 0; ticks < 50; ++ticks)
958 {
959 ghcStatus = StorPortReadRegisterUlong(AdapterExtension, &abar->GHC);
960 if ((ghcStatus & AHCI_Global_HBA_CONTROL_HR) == 0)
961 {
962 break;
963 }
964 StorPortStallExecution(20000);
965 }
966
967 if (ticks == 50)// 1 second
968 {
969 DebugPrint("\tDevice Timeout\n");
970 return FALSE;
971 }
972
973 return TRUE;
974 }// -- AhciAdapterReset();
975
976 /**
977 * @name AhciZeroMemory
978 * @implemented
979 *
980 * Clear buffer by filling zeros
981 *
982 * @param Buffer
983 * @param BufferSize
984 */
985 __inline
986 VOID
987 AhciZeroMemory (
988 __out PCHAR Buffer,
989 __in ULONG BufferSize
990 )
991 {
992 ULONG i;
993 for (i = 0; i < BufferSize; i++)
994 {
995 Buffer[i] = 0;
996 }
997
998 return;
999 }// -- AhciZeroMemory();
1000
1001 /**
1002 * @name IsPortValid
1003 * @implemented
1004 *
1005 * Tells wheather given port is implemented or not
1006 *
1007 * @param AdapterExtension
1008 * @param PathId
1009 *
1010 * @return
1011 * return TRUE if provided port is valid (implemented) or not
1012 */
1013 __inline
1014 BOOLEAN
1015 IsPortValid (
1016 __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
1017 __in UCHAR pathId
1018 )
1019 {
1020 NT_ASSERT(pathId >= 0);
1021
1022 if (pathId >= AdapterExtension->PortCount)
1023 {
1024 return FALSE;
1025 }
1026
1027 return AdapterExtension->PortExtension[pathId].IsActive;
1028 }// -- IsPortValid()
1029
1030 /**
1031 * @name AddQueue
1032 * @implemented
1033 *
1034 * Add Srb to Queue
1035 *
1036 * @param Queue
1037 * @param Srb
1038 *
1039 * @return
1040 * return TRUE if Srb is successfully added to Queue
1041 *
1042 */
1043 __inline
1044 BOOLEAN
1045 AddQueue (
1046 __inout PAHCI_QUEUE Queue,
1047 __in PVOID Srb
1048 )
1049 {
1050 NT_ASSERT(Queue->Head < MAXIMUM_QUEUE_BUFFER_SIZE);
1051 NT_ASSERT(Queue->Tail < MAXIMUM_QUEUE_BUFFER_SIZE);
1052
1053 if (Queue->Head == ((Queue->Tail + 1) % MAXIMUM_QUEUE_BUFFER_SIZE))
1054 return FALSE;
1055
1056 Queue->Buffer[Queue->Head++] = Srb;
1057 Queue->Head %= MAXIMUM_QUEUE_BUFFER_SIZE;
1058
1059 return TRUE;
1060 }// -- AddQueue();
1061
1062 /**
1063 * @name RemoveQueue
1064 * @implemented
1065 *
1066 * Remove and return Srb from Queue
1067 *
1068 * @param Queue
1069 *
1070 * @return
1071 * return Srb
1072 *
1073 */
1074 __inline
1075 PVOID
1076 RemoveQueue (
1077 __inout PAHCI_QUEUE Queue
1078 )
1079 {
1080 PVOID Srb;
1081
1082 NT_ASSERT(Queue->Head < MAXIMUM_QUEUE_BUFFER_SIZE);
1083 NT_ASSERT(Queue->Tail < MAXIMUM_QUEUE_BUFFER_SIZE);
1084
1085 if (Queue->Head == Queue->Tail)
1086 return NULL;
1087
1088 Srb = Queue->Buffer[Queue->Tail++];
1089 Queue->Tail %= MAXIMUM_QUEUE_BUFFER_SIZE;
1090
1091 return Srb;
1092 }// -- RemoveQueue();