61e799e4b1f481d4ee43d17719ab899bdcd57054
[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 AHCI_PORT_CMD cmd;
27 ULONG mappedLength, portNumber;
28 PAHCI_MEMORY_REGISTERS abar;
29 PAHCI_ADAPTER_EXTENSION adapterExtension;
30 STOR_PHYSICAL_ADDRESS commandListPhysical, receivedFISPhysical;
31
32 DebugPrint("AhciPortInitialize()\n");
33
34 adapterExtension = PortExtension->AdapterExtension;
35 abar = adapterExtension->ABAR_Address;
36 portNumber = PortExtension->PortNumber;
37
38 NT_ASSERT(abar != NULL);
39 NT_ASSERT(portNumber < adapterExtension->PortCount);
40
41 PortExtension->Port = &abar->PortList[portNumber];
42
43 commandListPhysical = StorPortGetPhysicalAddress(adapterExtension,
44 NULL,
45 PortExtension->CommandList,
46 &mappedLength);
47
48 if ((mappedLength == 0) || ((commandListPhysical.LowPart % 1024) != 0))
49 {
50 DebugPrint("\tcommandListPhysical mappedLength:%d\n", mappedLength);
51 return FALSE;
52 }
53
54 receivedFISPhysical = StorPortGetPhysicalAddress(adapterExtension,
55 NULL,
56 PortExtension->ReceivedFIS,
57 &mappedLength);
58
59 if ((mappedLength == 0) || ((receivedFISPhysical.LowPart % 256) != 0))
60 {
61 DebugPrint("\treceivedFISPhysical mappedLength:%d\n", mappedLength);
62 return FALSE;
63 }
64
65 // Ensure that the controller is not in the running state by reading and examining each
66 // implemented port’s PxCMD register. If PxCMD.ST, PxCMD.CR, PxCMD.FRE and
67 // PxCMD.FR are all cleared, the port is in an idle state. Otherwise, the port is not idle and
68 // should be placed in the idle state prior to manipulating HBA and port specific registers.
69 // System software places a port into the idle state by clearing PxCMD.ST and waiting for
70 // PxCMD.CR to return ‘0’ when read. Software should wait at least 500 milliseconds for
71 // this to occur. If PxCMD.FRE is set to ‘1’, software should clear it to ‘0’ and wait at least
72 // 500 milliseconds for PxCMD.FR to return ‘0’ when read. If PxCMD.CR or PxCMD.FR do
73 // not clear to ‘0’ correctly, then software may attempt a port reset or a full HBA reset to recove
74
75 // TODO: Check if port is in idle state or not, if not then restart port
76 cmd.Status = StorPortReadRegisterUlong(adapterExtension, &PortExtension->Port->CMD);
77 if ((cmd.FR != 0) || (cmd.CR != 0) || (cmd.FRE != 0) || (cmd.ST != 0))
78 {
79 DebugPrint("\tPort is not idle: %x\n", cmd);
80 }
81
82 // 10.1.2 For each implemented port, system software shall allocate memory for and program:
83 //  PxCLB and PxCLBU (if CAP.S64A is set to ‘1’)
84 //  PxFB and PxFBU (if CAP.S64A is set to ‘1’)
85 // Note: Assuming 32bit support only
86 StorPortWriteRegisterUlong(adapterExtension, &PortExtension->Port->CLB, commandListPhysical.LowPart);
87 if (IsAdapterCAPS64(adapterExtension->CAP))
88 {
89 StorPortWriteRegisterUlong(adapterExtension, &PortExtension->Port->CLBU, commandListPhysical.HighPart);
90 }
91
92 StorPortWriteRegisterUlong(adapterExtension, &PortExtension->Port->FB, receivedFISPhysical.LowPart);
93 if (IsAdapterCAPS64(adapterExtension->CAP))
94 {
95 StorPortWriteRegisterUlong(adapterExtension, &PortExtension->Port->FBU, receivedFISPhysical.HighPart);
96 }
97
98 PortExtension->IdentifyDeviceDataPhysicalAddress = StorPortGetPhysicalAddress(adapterExtension,
99 NULL,
100 PortExtension->IdentifyDeviceData,
101 &mappedLength);
102
103 // set device power state flag to D0
104 PortExtension->DevicePowerState = StorPowerDeviceD0;
105
106 // clear pending interrupts
107 StorPortWriteRegisterUlong(adapterExtension, &PortExtension->Port->SERR, (ULONG)~0);
108 StorPortWriteRegisterUlong(adapterExtension, &PortExtension->Port->IS, (ULONG)~0);
109 StorPortWriteRegisterUlong(adapterExtension, adapterExtension->IS, (1 << PortExtension->PortNumber));
110
111 return TRUE;
112 }// -- AhciPortInitialize();
113
114 /**
115 * @name AhciAllocateResourceForAdapter
116 * @implemented
117 *
118 * Allocate memory from poll for required pointers
119 *
120 * @param AdapterExtension
121 * @param ConfigInfo
122 *
123 * @return
124 * return TRUE if allocation was successful
125 */
126 BOOLEAN
127 AhciAllocateResourceForAdapter (
128 __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
129 __in PPORT_CONFIGURATION_INFORMATION ConfigInfo
130 )
131 {
132 PCHAR nonCachedExtension, tmp;
133 ULONG status, index, NCS, AlignedNCS;
134 ULONG portCount, portImplemented, nonCachedExtensionSize;
135
136 DebugPrint("AhciAllocateResourceForAdapter()\n");
137
138 NCS = AHCI_Global_Port_CAP_NCS(AdapterExtension->CAP);
139 AlignedNCS = ROUND_UP(NCS, 8);
140
141 // get port count -- Number of set bits in `AdapterExtension->PortImplemented`
142 portCount = 0;
143 portImplemented = AdapterExtension->PortImplemented;
144
145 NT_ASSERT(portImplemented != 0);
146 for (index = MAXIMUM_AHCI_PORT_COUNT - 1; index > 0; index--)
147 if ((portImplemented & (1 << index)) != 0)
148 break;
149
150 portCount = index + 1;
151 DebugPrint("\tPort Count: %d\n", portCount);
152
153 AdapterExtension->PortCount = portCount;
154 nonCachedExtensionSize = sizeof(AHCI_COMMAND_HEADER) * AlignedNCS + //should be 1K aligned
155 sizeof(AHCI_RECEIVED_FIS) +
156 sizeof(IDENTIFY_DEVICE_DATA);
157
158 // align nonCachedExtensionSize to 1024
159 nonCachedExtensionSize = ROUND_UP(nonCachedExtensionSize, 1024);
160
161 AdapterExtension->NonCachedExtension = StorPortGetUncachedExtension(AdapterExtension,
162 ConfigInfo,
163 nonCachedExtensionSize * portCount);
164
165 if (AdapterExtension->NonCachedExtension == NULL)
166 {
167 DebugPrint("\tadapterExtension->NonCachedExtension == NULL\n");
168 return FALSE;
169 }
170
171 nonCachedExtension = AdapterExtension->NonCachedExtension;
172 AhciZeroMemory(nonCachedExtension, nonCachedExtensionSize * portCount);
173
174 for (index = 0; index < portCount; index++)
175 {
176 AdapterExtension->PortExtension[index].IsActive = FALSE;
177 if ((AdapterExtension->PortImplemented & (1 << index)) != 0)
178 {
179 AdapterExtension->PortExtension[index].PortNumber = index;
180 AdapterExtension->PortExtension[index].IsActive = TRUE;
181 AdapterExtension->PortExtension[index].AdapterExtension = AdapterExtension;
182 AdapterExtension->PortExtension[index].CommandList = nonCachedExtension;
183
184 tmp = (PCHAR)(nonCachedExtension + sizeof(AHCI_COMMAND_HEADER) * AlignedNCS);
185
186 AdapterExtension->PortExtension[index].ReceivedFIS = (PAHCI_RECEIVED_FIS)tmp;
187 AdapterExtension->PortExtension[index].IdentifyDeviceData = (PIDENTIFY_DEVICE_DATA)(tmp + sizeof(AHCI_RECEIVED_FIS));
188 nonCachedExtension += nonCachedExtensionSize;
189 }
190 }
191
192 return TRUE;
193 }// -- AhciAllocateResourceForAdapter();
194
195 /**
196 * @name AhciStartPort
197 * @implemented
198 *
199 * Try to start the port device
200 *
201 * @param AdapterExtension
202 * @param PortExtension
203 *
204 */
205 BOOLEAN
206 AhciStartPort (
207 __in PAHCI_PORT_EXTENSION PortExtension
208 )
209 {
210 ULONG index;
211 AHCI_PORT_CMD cmd;
212 AHCI_SERIAL_ATA_STATUS ssts;
213 AHCI_SERIAL_ATA_CONTROL sctl;
214 PAHCI_ADAPTER_EXTENSION AdapterExtension;
215
216 DebugPrint("AhciStartPort()\n");
217
218 AdapterExtension = PortExtension->AdapterExtension;
219 cmd.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->CMD);
220
221 if ((cmd.FR == 1) && (cmd.CR == 1) && (cmd.FRE == 1) && (cmd.ST == 1))
222 {
223 // Already Running
224 return TRUE;
225 }
226
227 cmd.SUD = 1;
228 StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->CMD, cmd.Status);
229
230 if (((cmd.FR == 1) && (cmd.FRE == 0)) ||
231 ((cmd.CR == 1) && (cmd.ST == 0)))
232 {
233 DebugPrint("\tCOMRESET\n");
234 // perform COMRESET
235 // section 10.4.2
236
237 // Software causes a port reset (COMRESET) by writing 1h to the PxSCTL.DET field to invoke a
238 // COMRESET on the interface and start a re-establishment of Phy layer communications. Software shall
239 // wait at least 1 millisecond before clearing PxSCTL.DET to 0h; this ensures that at least one COMRESET
240 // signal is sent over the interface. After clearing PxSCTL.DET to 0h, software should wait for
241 // communication to be re-established as indicated by PxSSTS.DET being set to 3h. Then software should
242 // write all 1s to the PxSERR register to clear any bits that were set as part of the port reset.
243
244 sctl.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->SCTL);
245 sctl.DET = 1;
246 StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->SCTL, sctl.Status);
247
248 StorPortStallExecution(1000);
249
250 sctl.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->SCTL);
251 sctl.DET = 0;
252 StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->SCTL, sctl.Status);
253
254 // Poll DET to verify if a device is attached to the port
255 index = 0;
256 do
257 {
258 StorPortStallExecution(1000);
259 ssts.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->SSTS);
260
261 index++;
262 if (ssts.DET != 0)
263 {
264 break;
265 }
266 }
267 while(index < 30);
268 }
269
270 ssts.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->SSTS);
271 if (ssts.DET == 0x4)
272 {
273 // no device found
274 return FALSE;
275 }
276
277 DebugPrint("\tDET: %d %x %x\n", ssts.DET, PortExtension->Port->CMD, PortExtension->Port->SSTS);
278 return FALSE;
279 }// -- AhciStartPort();
280
281 /**
282 * @name AhciHwInitialize
283 * @implemented
284 *
285 * initializes the HBA and finds all devices that are of interest to the miniport driver.
286 *
287 * @param adapterExtension
288 *
289 * @return
290 * return TRUE if intialization was successful
291 */
292 BOOLEAN
293 AhciHwInitialize (
294 __in PVOID AdapterExtension
295 )
296 {
297 ULONG ghc, messageCount, status, cmd, index;
298 PAHCI_PORT_EXTENSION PortExtension;
299 PAHCI_ADAPTER_EXTENSION adapterExtension;
300 AHCI_SERIAL_ATA_STATUS ssts;
301
302 DebugPrint("AhciHwInitialize()\n");
303
304 adapterExtension = AdapterExtension;
305 adapterExtension->StateFlags.MessagePerPort = FALSE;
306
307 // First check what type of interrupt/synchronization device is using
308 ghc = StorPortReadRegisterUlong(adapterExtension, &adapterExtension->ABAR_Address->GHC);
309
310 // When set to ‘1’ by hardware, indicates that the HBA requested more than one MSI vector
311 // but has reverted to using the first vector only. When this bit is cleared to ‘0’,
312 // the HBA has not reverted to single MSI mode (i.e. hardware is already in single MSI mode,
313 // software has allocated the number of messages requested
314 if ((ghc & AHCI_Global_HBA_CONTROL_MRSM) == 0)
315 {
316 adapterExtension->StateFlags.MessagePerPort = TRUE;
317 DebugPrint("\tMultiple MSI based message not supported\n");
318 }
319
320 for (index = 0; index < adapterExtension->PortCount; index++)
321 {
322 if ((adapterExtension->PortImplemented & (0x1 << index)) != 0)
323 {
324 PortExtension = &adapterExtension->PortExtension[index];
325 PortExtension->IsActive = AhciStartPort(PortExtension);
326 if (PortExtension->IsActive == FALSE)
327 {
328 DebugPrint("\tPort Disabled: %d\n", index);
329 }
330 }
331 }
332
333 return TRUE;
334 }// -- AhciHwInitialize();
335
336 /**
337 * @name AhciCompleteIssuedSrb
338 * @implemented
339 *
340 * Complete issued Srbs
341 *
342 * @param PortExtension
343 *
344 */
345 VOID
346 AhciCompleteIssuedSrb (
347 __in PAHCI_PORT_EXTENSION PortExtension,
348 __in ULONG CommandsToComplete
349 )
350 {
351 ULONG NCS, i;
352 PSCSI_REQUEST_BLOCK Srb;
353 PAHCI_SRB_EXTENSION SrbExtension;
354 PAHCI_ADAPTER_EXTENSION AdapterExtension;
355 PAHCI_COMPLETION_ROUTINE CompletionRoutine;
356
357 DebugPrint("AhciCompleteIssuedSrb()\n");
358
359 NT_ASSERT(CommandsToComplete != 0);
360
361 DebugPrint("\tCompleted Commands: %d\n", CommandsToComplete);
362
363 AdapterExtension = PortExtension->AdapterExtension;
364 NCS = AHCI_Global_Port_CAP_NCS(AdapterExtension->CAP);
365
366 for (i = 0; i < NCS; i++)
367 {
368 if (((1 << i) & CommandsToComplete) != 0)
369 {
370 Srb = PortExtension->Slot[i];
371 NT_ASSERT(Srb != NULL);
372
373 if (Srb->SrbStatus == SRB_STATUS_PENDING)
374 {
375 Srb->SrbStatus = SRB_STATUS_SUCCESS;
376 }
377
378 SrbExtension = GetSrbExtension(Srb);
379 CompletionRoutine = SrbExtension->CompletionRoutine;
380
381 if (CompletionRoutine != NULL)
382 {
383 // now it's completion routine responsibility to set SrbStatus
384 CompletionRoutine(AdapterExtension, PortExtension, Srb);
385 }
386 else
387 {
388 Srb->SrbStatus = SRB_STATUS_SUCCESS;
389 StorPortNotification(RequestComplete, AdapterExtension, Srb);
390 }
391 }
392 }
393
394 return;
395 }// -- AhciCompleteIssuedSrb();
396
397 /**
398 * @name AhciInterruptHandler
399 * @not_implemented
400 *
401 * Interrupt Handler for PortExtension
402 *
403 * @param PortExtension
404 *
405 */
406 VOID
407 AhciInterruptHandler (
408 __in PAHCI_PORT_EXTENSION PortExtension
409 )
410 {
411 ULONG is, ci, sact, outstanding;
412 AHCI_INTERRUPT_STATUS PxIS;
413 AHCI_INTERRUPT_STATUS PxISMasked;
414 PAHCI_ADAPTER_EXTENSION AdapterExtension;
415
416 DebugPrint("AhciInterruptHandler()\n");
417 DebugPrint("\tPort Number: %d\n", PortExtension->PortNumber);
418
419 AdapterExtension = PortExtension->AdapterExtension;
420 NT_ASSERT(IsPortValid(AdapterExtension, PortExtension->PortNumber));
421
422 // 5.5.3
423 // 1. Software determines the cause of the interrupt by reading the PxIS register.
424 // It is possible for multiple bits to be set
425 // 2. Software clears appropriate bits in the PxIS register corresponding to the cause of the interrupt.
426 // 3. Software clears the interrupt bit in IS.IPS corresponding to the port.
427 // 4. If executing non-queued commands, software reads the PxCI register, and compares the current value to
428 // the list of commands previously issued by software that are still outstanding.
429 // If executing native queued commands, software reads the PxSACT register and compares the current
430 // value to the list of commands previously issued by software.
431 // Software completes with success any outstanding command whose corresponding bit has been cleared in
432 // the respective register. PxCI and PxSACT are volatile registers; software should only use their values
433 // to determine commands that have completed, not to determine which commands have previously been issued.
434 // 5. If there were errors, noted in the PxIS register, software performs error recovery actions (see section 6.2.2).
435 PxISMasked.Status = 0;
436 PxIS.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->IS);
437
438 // 6.2.2
439 // Fatal Error
440 // signified by the setting of PxIS.HBFS, PxIS.HBDS, PxIS.IFS, or PxIS.TFES
441 if (PxIS.HBFS || PxIS.HBDS || PxIS.IFS || PxIS.TFES)
442 {
443 // In this state, the HBA shall not issue any new commands nor acknowledge DMA Setup FISes to process
444 // any native command queuing commands. To recover, the port must be restarted
445 // To detect an error that requires software recovery actions to be performed,
446 // software should check whether any of the following status bits are set on an interrupt:
447 // PxIS.HBFS, PxIS.HBDS, PxIS.IFS, and PxIS.TFES. If any of these bits are set,
448 // software should perform the appropriate error recovery actions based on whether
449 // non-queued commands were being issued or native command queuing commands were being issued.
450
451 DebugPrint("\tFatal Error: %x\n", PxIS.Status);
452 }
453
454 // Normal Command Completion
455 // 3.3.5
456 // A D2H Register FIS has been received with the ‘I’ bit set, and has been copied into system memory.
457 PxISMasked.DHRS = PxIS.DHRS;
458 // A PIO Setup FIS has been received with the ‘I’ bit set, it has been copied into system memory.
459 PxISMasked.PSS = PxIS.PSS;
460 // A DMA Setup FIS has been received with the ‘I’ bit set and has been copied into system memory.
461 PxISMasked.DSS = PxIS.DSS;
462 // A Set Device Bits FIS has been received with the ‘I’ bit set and has been copied into system memory/
463 PxISMasked.SDBS = PxIS.SDBS;
464 // A PRD with the ‘I’ bit set has transferred all of its data.
465 PxISMasked.DPS = PxIS.DPS;
466
467 if (PxISMasked.Status != 0)
468 {
469 StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->IS, PxISMasked.Status);
470 }
471
472 // 10.7.1.1
473 // Clear port interrupt
474 // It is set by the level of the virtual interrupt line being a set, and cleared by a write of ‘1’ from the software.
475 is = (1 << PortExtension->PortNumber);
476 StorPortWriteRegisterUlong(AdapterExtension, AdapterExtension->IS, is);
477
478 ci = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->CI);
479 sact = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->SACT);
480
481 outstanding = ci | sact; // NOTE: Including both non-NCQ and NCQ based commands
482 if ((PortExtension->CommandIssuedSlots & (~outstanding)) != 0)
483 {
484 AhciCompleteIssuedSrb(PortExtension, (PortExtension->CommandIssuedSlots & (~outstanding)));
485 PortExtension->CommandIssuedSlots &= outstanding;
486 }
487
488 return;
489 }// -- AhciInterruptHandler();
490
491 /**
492 * @name AhciHwInterrupt
493 * @implemented
494 *
495 * The Storport driver calls the HwStorInterrupt routine after the HBA generates an interrupt request.
496 *
497 * @param AdapterExtension
498 *
499 * @return
500 * return TRUE Indicates that an interrupt was pending on adapter.
501 * return FALSE Indicates the interrupt was not ours.
502 */
503 BOOLEAN
504 AhciHwInterrupt(
505 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
506 )
507 {
508 ULONG portPending, nextPort, i, portCount;
509
510 DebugPrint("AhciHwInterrupt()\n");
511
512 if (AdapterExtension->StateFlags.Removed)
513 {
514 return FALSE;
515 }
516
517 portPending = StorPortReadRegisterUlong(AdapterExtension, AdapterExtension->IS);
518 // we process interrupt for implemented ports only
519 portCount = AdapterExtension->PortCount;
520 portPending = portPending & AdapterExtension->PortImplemented;
521
522 if (portPending == 0)
523 {
524 return FALSE;
525 }
526
527 for (i = 1; i <= portCount; i++)
528 {
529 nextPort = (AdapterExtension->LastInterruptPort + i) % portCount;
530
531 if ((portPending & (0x1 << nextPort)) == 0)
532 continue;
533
534 NT_ASSERT(IsPortValid(AdapterExtension, nextPort));
535
536 if (nextPort == AdapterExtension->LastInterruptPort)
537 {
538 return FALSE;
539 }
540
541 if (AdapterExtension->PortExtension[nextPort].IsActive == FALSE)
542 {
543 continue;
544 }
545
546 // we can assign this interrupt to this port
547 AdapterExtension->LastInterruptPort = nextPort;
548 AhciInterruptHandler(&AdapterExtension->PortExtension[nextPort]);
549
550 // interrupt belongs to this device
551 // should always return TRUE
552 return TRUE;
553 }
554
555 DebugPrint("\tSomething went wrong");
556 return FALSE;
557 }// -- AhciHwInterrupt();
558
559 /**
560 * @name AhciHwStartIo
561 * @not_implemented
562 *
563 * The Storport driver calls the HwStorStartIo routine one time for each incoming I/O request.
564 *
565 * @param adapterExtension
566 * @param Srb
567 *
568 * @return
569 * return TRUE if the request was accepted
570 * return FALSE if the request must be submitted later
571 */
572 BOOLEAN
573 AhciHwStartIo (
574 __in PVOID AdapterExtension,
575 __in PSCSI_REQUEST_BLOCK Srb
576 )
577 {
578 UCHAR function, pathId;
579 PAHCI_ADAPTER_EXTENSION adapterExtension;
580
581 DebugPrint("AhciHwStartIo()\n");
582
583 pathId = Srb->PathId;
584 function = Srb->Function;
585 adapterExtension = AdapterExtension;
586
587 if (!IsPortValid(adapterExtension, pathId))
588 {
589 Srb->SrbStatus = SRB_STATUS_NO_DEVICE;
590 StorPortNotification(RequestComplete, adapterExtension, Srb);
591 return TRUE;
592 }
593
594 // https://msdn.microsoft.com/windows/hardware/drivers/storage/handling-srb-function-pnp
595 // If the function member of an SRB is set to SRB_FUNCTION_PNP,
596 // the SRB is a structure of type SCSI_PNP_REQUEST_BLOCK.
597 if (function == SRB_FUNCTION_PNP)
598 {
599 PSCSI_PNP_REQUEST_BLOCK pnpRequest;
600
601 pnpRequest = (PSCSI_PNP_REQUEST_BLOCK)Srb;
602 if ((pnpRequest->SrbPnPFlags & SRB_PNP_FLAGS_ADAPTER_REQUEST) != 0)
603 {
604 if ((pnpRequest->PnPAction == StorRemoveDevice) ||
605 (pnpRequest->PnPAction == StorSurpriseRemoval))
606 {
607 Srb->SrbStatus = SRB_STATUS_SUCCESS;
608 adapterExtension->StateFlags.Removed = 1;
609 DebugPrint("\tAdapter removed\n");
610 }
611 else if (pnpRequest->PnPAction == StorStopDevice)
612 {
613 Srb->SrbStatus = SRB_STATUS_SUCCESS;
614 DebugPrint("\tRequested to Stop the adapter\n");
615 }
616 else
617 {
618 Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
619 }
620
621 StorPortNotification(RequestComplete, adapterExtension, Srb);
622 return TRUE;
623 }
624 }
625
626 if (function == SRB_FUNCTION_EXECUTE_SCSI)
627 {
628 // https://msdn.microsoft.com/en-us/windows/hardware/drivers/storage/handling-srb-function-execute-scsi
629 // On receipt of an SRB_FUNCTION_EXECUTE_SCSI request, a miniport driver's HwScsiStartIo
630 // routine does the following:
631 //
632 // - Gets and/or sets up whatever context the miniport driver maintains in its device,
633 // logical unit, and/or SRB extensions
634 // For example, a miniport driver might set up a logical unit extension with pointers
635 // to the SRB itself and the SRB DataBuffer pointer, the SRB DataTransferLength value,
636 // and a driver-defined value (or CDB SCSIOP_XXX value) indicating the operation to be
637 // carried out on the HBA.
638 //
639 // - Calls an internal routine to program the HBA, as partially directed by the SrbFlags,
640 // for the requested operation
641 // For a device I/O operation, such an internal routine generally selects the target device
642 // and sends the CDB over the bus to the target logical unit.
643 if (Srb->CdbLength > 0)
644 {
645 PCDB cdb = (PCDB)&Srb->Cdb;
646 if (cdb->CDB10.OperationCode == SCSIOP_INQUIRY)
647 {
648 Srb->SrbStatus = DeviceInquiryRequest(adapterExtension, Srb, cdb);
649 }
650 else
651 {
652 Srb->SrbStatus = SRB_STATUS_NO_DEVICE;
653 }
654 }
655 else
656 {
657 Srb->SrbStatus = SRB_STATUS_BAD_FUNCTION;
658 }
659
660 StorPortNotification(RequestComplete, adapterExtension, Srb);
661 return TRUE;
662 }
663
664 DebugPrint("\tUnknown function code recieved: %x\n", function);
665 Srb->SrbStatus = SRB_STATUS_BAD_FUNCTION;
666 StorPortNotification(RequestComplete, adapterExtension, Srb);
667 return TRUE;
668 }// -- AhciHwStartIo();
669
670 /**
671 * @name AhciHwResetBus
672 * @not_implemented
673 *
674 * The HwStorResetBus routine is called by the port driver to clear error conditions.
675 *
676 * @param adapterExtension
677 * @param PathId
678 *
679 * @return
680 * return TRUE if bus was successfully reset
681 */
682 BOOLEAN
683 AhciHwResetBus (
684 __in PVOID AdapterExtension,
685 __in ULONG PathId
686 )
687 {
688 STOR_LOCK_HANDLE lockhandle;
689 PAHCI_ADAPTER_EXTENSION adapterExtension;
690
691 DebugPrint("AhciHwResetBus()\n");
692
693 adapterExtension = AdapterExtension;
694
695 if (IsPortValid(AdapterExtension, PathId))
696 {
697 AhciZeroMemory(&lockhandle, sizeof(lockhandle));
698
699 // Acquire Lock
700 StorPortAcquireSpinLock(AdapterExtension, InterruptLock, NULL, &lockhandle);
701
702 // TODO: Perform port reset
703
704 // Release lock
705 StorPortReleaseSpinLock(AdapterExtension, &lockhandle);
706 }
707
708 return FALSE;
709 }// -- AhciHwResetBus();
710
711 /**
712 * @name AhciHwFindAdapter
713 * @implemented
714 *
715 * The HwStorFindAdapter routine uses the supplied configuration to determine whether a specific
716 * HBA is supported and, if it is, to return configuration information about that adapter.
717 *
718 * 10.1 Platform Communication
719 * http://www.intel.in/content/dam/www/public/us/en/documents/technical-specifications/serial-ata-ahci-spec-rev1_2.pdf
720
721 * @param DeviceExtension
722 * @param HwContext
723 * @param BusInformation
724 * @param ArgumentString
725 * @param ConfigInfo
726 * @param Reserved3
727 *
728 * @return
729 * SP_RETURN_FOUND
730 * 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.
731 *
732 * SP_RETURN_ERROR
733 * 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.
734 *
735 * SP_RETURN_BAD_CONFIG
736 * Indicates that the supplied configuration information was invalid for the adapter.
737 *
738 * SP_RETURN_NOT_FOUND
739 * Indicates that no supported HBA was found for the supplied configuration information.
740 *
741 * @remarks Called by Storport.
742 */
743 ULONG
744 AhciHwFindAdapter (
745 __in PVOID AdapterExtension,
746 __in PVOID HwContext,
747 __in PVOID BusInformation,
748 __in PVOID ArgumentString,
749 __inout PPORT_CONFIGURATION_INFORMATION ConfigInfo,
750 __in PBOOLEAN Reserved3
751 )
752 {
753 ULONG ghc;
754 ULONG index;
755 ULONG portCount, portImplemented;
756 ULONG pci_cfg_len;
757 UCHAR pci_cfg_buf[sizeof(PCI_COMMON_CONFIG)];
758 PACCESS_RANGE accessRange;
759
760 PAHCI_MEMORY_REGISTERS abar;
761 PPCI_COMMON_CONFIG pciConfigData;
762 PAHCI_ADAPTER_EXTENSION adapterExtension;
763
764 DebugPrint("AhciHwFindAdapter()\n");
765
766 adapterExtension = AdapterExtension;
767 adapterExtension->SlotNumber = ConfigInfo->SlotNumber;
768 adapterExtension->SystemIoBusNumber = ConfigInfo->SystemIoBusNumber;
769
770 // get PCI configuration header
771 pci_cfg_len = StorPortGetBusData(
772 adapterExtension,
773 PCIConfiguration,
774 adapterExtension->SystemIoBusNumber,
775 adapterExtension->SlotNumber,
776 pci_cfg_buf,
777 sizeof(PCI_COMMON_CONFIG));
778
779 if (pci_cfg_len != sizeof(PCI_COMMON_CONFIG))
780 {
781 DebugPrint("\tpci_cfg_len != %d :: %d", sizeof(PCI_COMMON_CONFIG), pci_cfg_len);
782 return SP_RETURN_ERROR;//Not a valid device at the given bus number
783 }
784
785 pciConfigData = pci_cfg_buf;
786 adapterExtension->VendorID = pciConfigData->VendorID;
787 adapterExtension->DeviceID = pciConfigData->DeviceID;
788 adapterExtension->RevisionID = pciConfigData->RevisionID;
789 // 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).
790 adapterExtension->AhciBaseAddress = pciConfigData->u.type0.BaseAddresses[5] & (0xFFFFFFF0);
791
792 DebugPrint("\tVendorID:%d DeviceID:%d RevisionID:%d\n", adapterExtension->VendorID,
793 adapterExtension->DeviceID,
794 adapterExtension->RevisionID);
795
796 // 2.1.11
797 abar = NULL;
798 if (ConfigInfo->NumberOfAccessRanges > 0)
799 {
800 for (index = 0; index < ConfigInfo->NumberOfAccessRanges; index++)
801 {
802 accessRange = *ConfigInfo->AccessRanges;
803 if (accessRange[index].RangeStart.QuadPart == adapterExtension->AhciBaseAddress)
804 {
805 abar = StorPortGetDeviceBase(adapterExtension,
806 ConfigInfo->AdapterInterfaceType,
807 ConfigInfo->SystemIoBusNumber,
808 accessRange[index].RangeStart,
809 accessRange[index].RangeLength,
810 !accessRange[index].RangeInMemory);
811 break;
812 }
813 }
814 }
815
816 if (abar == NULL)
817 {
818 DebugPrint("\tabar == NULL\n");
819 return SP_RETURN_ERROR; // corrupted information supplied
820 }
821
822 adapterExtension->ABAR_Address = abar;
823 adapterExtension->CAP = StorPortReadRegisterUlong(adapterExtension, &abar->CAP);
824 adapterExtension->CAP2 = StorPortReadRegisterUlong(adapterExtension, &abar->CAP2);
825 adapterExtension->Version = StorPortReadRegisterUlong(adapterExtension, &abar->VS);
826 adapterExtension->LastInterruptPort = -1;
827
828 // 10.1.2
829 // 1. Indicate that system software is AHCI aware by setting GHC.AE to ‘1’.
830 // 3.1.2 -- AE bit is read-write only if CAP.SAM is '0'
831 ghc = StorPortReadRegisterUlong(adapterExtension, &abar->GHC);
832 // AE := Highest Significant bit of GHC
833 if ((ghc & AHCI_Global_HBA_CONTROL_AE) != 0)// Hmm, controller was already in power state
834 {
835 // reset controller to have it in known state
836 DebugPrint("\tAE Already set, Reset()\n");
837 if (!AhciAdapterReset(adapterExtension))
838 {
839 DebugPrint("\tReset Failed!\n");
840 return SP_RETURN_ERROR;// reset failed
841 }
842 }
843
844 ghc = AHCI_Global_HBA_CONTROL_AE;// only AE=1
845 // tell the controller that we know about AHCI
846 StorPortWriteRegisterUlong(adapterExtension, &abar->GHC, ghc);
847
848 adapterExtension->IS = &abar->IS;
849 adapterExtension->PortImplemented = StorPortReadRegisterUlong(adapterExtension, &abar->PI);
850
851 if (adapterExtension->PortImplemented == 0)
852 {
853 DebugPrint("\tadapterExtension->PortImplemented == 0\n");
854 return SP_RETURN_ERROR;
855 }
856
857 ConfigInfo->MaximumTransferLength = MAXIMUM_TRANSFER_LENGTH;//128 KB
858 ConfigInfo->NumberOfPhysicalBreaks = 0x21;
859 ConfigInfo->MaximumNumberOfTargets = 1;
860 ConfigInfo->MaximumNumberOfLogicalUnits = 1;
861 ConfigInfo->ResetTargetSupported = TRUE;
862 ConfigInfo->NumberOfBuses = MAXIMUM_AHCI_PORT_COUNT;
863 ConfigInfo->SynchronizationModel = StorSynchronizeFullDuplex;
864 ConfigInfo->ScatterGather = TRUE;
865
866 // allocate necessary resource for each port
867 if (!AhciAllocateResourceForAdapter(adapterExtension, ConfigInfo))
868 {
869 DebugPrint("\tAhciAllocateResourceForAdapter() == FALSE\n");
870 return SP_RETURN_ERROR;
871 }
872
873 for (index = 0; index < adapterExtension->PortCount; index++)
874 {
875 if ((adapterExtension->PortImplemented & (0x1 << index)) != 0)
876 AhciPortInitialize(&adapterExtension->PortExtension[index]);
877 }
878
879 // Turn IE -- Interrupt Enabled
880 ghc = StorPortReadRegisterUlong(adapterExtension, &abar->GHC);
881 ghc |= AHCI_Global_HBA_CONTROL_IE;
882 StorPortWriteRegisterUlong(adapterExtension, &abar->GHC, ghc);
883
884 return SP_RETURN_FOUND;
885 }// -- AhciHwFindAdapter();
886
887 /**
888 * @name DriverEntry
889 * @implemented
890 *
891 * Initial Entrypoint for storahci miniport driver
892 *
893 * @param DriverObject
894 * @param RegistryPath
895 *
896 * @return
897 * NT_STATUS in case of driver loaded successfully.
898 */
899 ULONG
900 DriverEntry (
901 __in PVOID DriverObject,
902 __in PVOID RegistryPath
903 )
904 {
905 HW_INITIALIZATION_DATA hwInitializationData;
906 ULONG i, status;
907
908 DebugPrint("Storahci Loaded\n");
909
910 // initialize the hardware data structure
911 AhciZeroMemory(&hwInitializationData, sizeof(HW_INITIALIZATION_DATA));
912
913 // set size of hardware initialization structure
914 hwInitializationData.HwInitializationDataSize = sizeof(HW_INITIALIZATION_DATA);
915
916 // identity required miniport entry point routines
917 hwInitializationData.HwStartIo = AhciHwStartIo;
918 hwInitializationData.HwResetBus = AhciHwResetBus;
919 hwInitializationData.HwInterrupt = AhciHwInterrupt;
920 hwInitializationData.HwInitialize = AhciHwInitialize;
921 hwInitializationData.HwFindAdapter = AhciHwFindAdapter;
922
923 // adapter specific information
924 hwInitializationData.NeedPhysicalAddresses = TRUE;
925 hwInitializationData.TaggedQueuing = TRUE;
926 hwInitializationData.AutoRequestSense = TRUE;
927 hwInitializationData.MultipleRequestPerLu = TRUE;
928
929 hwInitializationData.NumberOfAccessRanges = 6;
930 hwInitializationData.AdapterInterfaceType = PCIBus;
931 hwInitializationData.MapBuffers = STOR_MAP_NON_READ_WRITE_BUFFERS;
932
933 // set required extension sizes
934 hwInitializationData.SrbExtensionSize = sizeof(AHCI_SRB_EXTENSION);
935 hwInitializationData.DeviceExtensionSize = sizeof(AHCI_ADAPTER_EXTENSION);
936
937 // register our hw init data
938 status = StorPortInitialize(DriverObject,
939 RegistryPath,
940 &hwInitializationData,
941 NULL);
942
943 DebugPrint("\tstatus: %x\n", status);
944 return status;
945 }// -- DriverEntry();
946
947 /**
948 * @name AhciATA_CFIS
949 * @implemented
950 *
951 * create ATA CFIS from Srb
952 *
953 * @param PortExtension
954 * @param Srb
955 *
956 */
957 VOID
958 AhciATA_CFIS (
959 __in PAHCI_PORT_EXTENSION PortExtension,
960 __in PAHCI_SRB_EXTENSION SrbExtension
961 )
962 {
963 PAHCI_COMMAND_TABLE cmdTable;
964
965 DebugPrint("AhciATA_CFIS()\n");
966
967 cmdTable = (PAHCI_COMMAND_TABLE)SrbExtension;
968
969 NT_ASSERT(sizeof(cmdTable->CFIS) == 64);
970
971 AhciZeroMemory(&cmdTable->CFIS, sizeof(cmdTable->CFIS));
972
973 cmdTable->CFIS[AHCI_ATA_CFIS_FisType] = 0x27; // FIS Type
974 cmdTable->CFIS[AHCI_ATA_CFIS_PMPort_C] = (1 << 7); // PM Port & C
975 cmdTable->CFIS[AHCI_ATA_CFIS_CommandReg] = SrbExtension->CommandReg;
976
977 cmdTable->CFIS[AHCI_ATA_CFIS_FeaturesLow] = SrbExtension->FeaturesLow;
978 cmdTable->CFIS[AHCI_ATA_CFIS_LBA0] = SrbExtension->LBA0;
979 cmdTable->CFIS[AHCI_ATA_CFIS_LBA1] = SrbExtension->LBA1;
980 cmdTable->CFIS[AHCI_ATA_CFIS_LBA2] = SrbExtension->LBA2;
981 cmdTable->CFIS[AHCI_ATA_CFIS_Device] = SrbExtension->Device;
982 cmdTable->CFIS[AHCI_ATA_CFIS_LBA3] = SrbExtension->LBA3;
983 cmdTable->CFIS[AHCI_ATA_CFIS_LBA4] = SrbExtension->LBA4;
984 cmdTable->CFIS[AHCI_ATA_CFIS_LBA5] = SrbExtension->LBA5;
985 cmdTable->CFIS[AHCI_ATA_CFIS_FeaturesHigh] = SrbExtension->FeaturesHigh;
986 cmdTable->CFIS[AHCI_ATA_CFIS_SectorCountLow] = SrbExtension->SectorCountLow;
987 cmdTable->CFIS[AHCI_ATA_CFIS_SectorCountHigh] = SrbExtension->SectorCountHigh;
988
989 return;
990 }// -- AhciATA_CFIS();
991
992 /**
993 * @name AhciATAPI_CFIS
994 * @not_implemented
995 *
996 * create ATAPI CFIS from Srb
997 *
998 * @param PortExtension
999 * @param Srb
1000 *
1001 */
1002 VOID
1003 AhciATAPI_CFIS (
1004 __in PAHCI_PORT_EXTENSION PortExtension,
1005 __in PAHCI_SRB_EXTENSION SrbExtension
1006 )
1007 {
1008 DebugPrint("AhciATAPI_CFIS()\n");
1009
1010 }// -- AhciATAPI_CFIS();
1011
1012 /**
1013 * @name AhciBuild_PRDT
1014 * @implemented
1015 *
1016 * Build PRDT for data transfer
1017 *
1018 * @param PortExtension
1019 * @param Srb
1020 *
1021 * @return
1022 * Return number of entries in PRDT.
1023 */
1024 ULONG
1025 AhciBuild_PRDT (
1026 __in PAHCI_PORT_EXTENSION PortExtension,
1027 __in PAHCI_SRB_EXTENSION SrbExtension
1028 )
1029 {
1030 ULONG index;
1031 PAHCI_COMMAND_TABLE cmdTable;
1032 PLOCAL_SCATTER_GATHER_LIST sgl;
1033 PAHCI_ADAPTER_EXTENSION AdapterExtension;
1034
1035 DebugPrint("AhciBuild_PRDT()\n");
1036
1037 sgl = &SrbExtension->Sgl;
1038 cmdTable = (PAHCI_COMMAND_TABLE)SrbExtension;
1039 AdapterExtension = PortExtension->AdapterExtension;
1040
1041 NT_ASSERT(sgl != NULL);
1042 NT_ASSERT(sgl->NumberOfElements < MAXIMUM_AHCI_PRDT_ENTRIES);
1043
1044 for (index = 0; index < sgl->NumberOfElements; index++)
1045 {
1046 NT_ASSERT(sgl->List[index].Length <= MAXIMUM_TRANSFER_LENGTH);
1047
1048 cmdTable->PRDT[index].DBA = sgl->List[index].PhysicalAddress.LowPart;
1049 if (IsAdapterCAPS64(AdapterExtension->CAP))
1050 {
1051 cmdTable->PRDT[index].DBAU = sgl->List[index].PhysicalAddress.HighPart;
1052 }
1053 }
1054
1055 return sgl->NumberOfElements;
1056 }// -- AhciBuild_PRDT();
1057
1058 /**
1059 * @name AhciProcessSrb
1060 * @implemented
1061 *
1062 * Prepare Srb for IO processing
1063 *
1064 * @param PortExtension
1065 * @param Srb
1066 * @param SlotIndex
1067 *
1068 */
1069 VOID
1070 AhciProcessSrb (
1071 __in PAHCI_PORT_EXTENSION PortExtension,
1072 __in PSCSI_REQUEST_BLOCK Srb,
1073 __in ULONG SlotIndex
1074 )
1075 {
1076 ULONG prdtlen, sig, length;
1077 PAHCI_SRB_EXTENSION SrbExtension;
1078 PAHCI_COMMAND_HEADER CommandHeader;
1079 PAHCI_ADAPTER_EXTENSION AdapterExtension;
1080 STOR_PHYSICAL_ADDRESS CommandTablePhysicalAddress;
1081
1082 DebugPrint("AhciProcessSrb()\n");
1083
1084 NT_ASSERT(Srb->PathId == PortExtension->PortNumber);
1085
1086 SrbExtension = GetSrbExtension(Srb);
1087 AdapterExtension = PortExtension->AdapterExtension;
1088
1089 NT_ASSERT(SrbExtension != NULL);
1090 NT_ASSERT(SrbExtension->AtaFunction != 0);
1091
1092 if ((SrbExtension->AtaFunction == ATA_FUNCTION_ATA_IDENTIFY) &&
1093 (SrbExtension->CommandReg == IDE_COMMAND_NOT_VALID))
1094 {
1095 // Here we are safe to check SIG register
1096 sig = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->SIG);
1097 if (sig == 0x101)
1098 {
1099 SrbExtension->CommandReg = IDE_COMMAND_IDENTIFY;
1100 }
1101 else
1102 {
1103 SrbExtension->CommandReg = IDE_COMMAND_ATAPI_IDENTIFY;
1104 }
1105 }
1106
1107 NT_ASSERT(SlotIndex < AHCI_Global_Port_CAP_NCS(AdapterExtension->CAP));
1108 SrbExtension->SlotIndex = SlotIndex;
1109
1110 // program the CFIS in the CommandTable
1111 CommandHeader = &PortExtension->CommandList[SlotIndex];
1112
1113 if (IsAtaCommand(SrbExtension->AtaFunction))
1114 {
1115 AhciATA_CFIS(PortExtension, SrbExtension);
1116 }
1117 else if (IsAtapiCommand(SrbExtension->AtaFunction))
1118 {
1119 AhciATAPI_CFIS(PortExtension, SrbExtension);
1120 }
1121
1122 prdtlen = 0;
1123 if (IsDataTransferNeeded(SrbExtension))
1124 {
1125 prdtlen = AhciBuild_PRDT(PortExtension, SrbExtension);
1126 NT_ASSERT(prdtlen != -1);
1127 }
1128
1129 // Program the command header
1130 CommandHeader->DI.PRDTL = prdtlen; // number of entries in PRD table
1131 CommandHeader->DI.CFL = 5;
1132 CommandHeader->DI.W = (SrbExtension->Flags & ATA_FLAGS_DATA_OUT) ? 1 : 0;
1133 CommandHeader->DI.P = 0; // ATA Specifications says so
1134 CommandHeader->DI.PMP = 0; // Port Multiplier
1135
1136 // Reset -- Manual Configuation
1137 CommandHeader->DI.R = 0;
1138 CommandHeader->DI.B = 0;
1139 CommandHeader->DI.C = 0;
1140
1141 CommandHeader->PRDBC = 0;
1142
1143 CommandHeader->Reserved[0] = 0;
1144 CommandHeader->Reserved[1] = 0;
1145 CommandHeader->Reserved[2] = 0;
1146 CommandHeader->Reserved[3] = 0;
1147
1148 // set CommandHeader CTBA
1149 // I am really not sure if SrbExtension is 128 byte aligned or not
1150 // Command FIS will not work if it is not so.
1151 CommandTablePhysicalAddress = StorPortGetPhysicalAddress(AdapterExtension,
1152 NULL,
1153 SrbExtension,
1154 &length);
1155
1156 // command table alignment
1157 NT_ASSERT((CommandTablePhysicalAddress.LowPart % 128) == 0);
1158
1159 CommandHeader->CTBA0 = CommandTablePhysicalAddress.LowPart;
1160
1161 if (IsAdapterCAPS64(AdapterExtension->CAP))
1162 {
1163 CommandHeader->CTBA_U0 = CommandTablePhysicalAddress.HighPart;
1164 }
1165
1166 // mark this slot
1167 PortExtension->Slot[SlotIndex] = Srb;
1168 PortExtension->QueueSlots |= 1 << SlotIndex;
1169 return;
1170 }// -- AhciProcessSrb();
1171
1172 /**
1173 * @name AhciActivatePort
1174 * @implemented
1175 *
1176 * Program Port and populate command list
1177 *
1178 * @param PortExtension
1179 *
1180 */
1181 VOID
1182 AhciActivatePort (
1183 __in PAHCI_PORT_EXTENSION PortExtension
1184 )
1185 {
1186 AHCI_PORT_CMD cmd;
1187 ULONG QueueSlots, slotToActivate, tmp;
1188 PAHCI_ADAPTER_EXTENSION AdapterExtension;
1189
1190 DebugPrint("AhciActivatePort()\n");
1191
1192 AdapterExtension = PortExtension->AdapterExtension;
1193 QueueSlots = PortExtension->QueueSlots;
1194
1195 if (QueueSlots == 0)
1196 return;
1197
1198 // section 3.3.14
1199 // Bits in this field shall only be set to ‘1’ by software when PxCMD.ST is set to ‘1’
1200 cmd.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->CMD);
1201
1202 if (cmd.ST == 0) // PxCMD.ST == 0
1203 return;
1204
1205 // get the lowest set bit
1206 tmp = QueueSlots & (QueueSlots - 1);
1207
1208 if (tmp == 0)
1209 slotToActivate = QueueSlots;
1210 else
1211 slotToActivate = (QueueSlots & (~tmp));
1212
1213 // mark that bit off in QueueSlots
1214 // so we can know we it is really needed to activate port or not
1215 PortExtension->QueueSlots &= ~slotToActivate;
1216 // mark this CommandIssuedSlots
1217 // to validate in completeIssuedCommand
1218 PortExtension->CommandIssuedSlots |= slotToActivate;
1219
1220 // tell the HBA to issue this Command Slot to the given port
1221 StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->CI, slotToActivate);
1222
1223 return;
1224 }// -- AhciActivatePort();
1225
1226 /**
1227 * @name AhciProcessIO
1228 * @implemented
1229 *
1230 * Acquire Exclusive lock to port, populate pending commands to command List
1231 * program controller's port to process new commands in command list.
1232 *
1233 * @param AdapterExtension
1234 * @param PathId
1235 * @param Srb
1236 *
1237 */
1238 VOID
1239 AhciProcessIO (
1240 __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
1241 __in UCHAR PathId,
1242 __in PSCSI_REQUEST_BLOCK Srb
1243 )
1244 {
1245 STOR_LOCK_HANDLE lockhandle;
1246 PSCSI_REQUEST_BLOCK tmpSrb;
1247 PAHCI_PORT_EXTENSION PortExtension;
1248 ULONG commandSlotMask, occupiedSlots, slotIndex, NCS;
1249
1250 DebugPrint("AhciProcessIO()\n");
1251 DebugPrint("\tPathId: %d\n", PathId);
1252
1253 PortExtension = &AdapterExtension->PortExtension[PathId];
1254
1255 NT_ASSERT(PathId < AdapterExtension->PortCount);
1256
1257 // add Srb to queue
1258 AddQueue(&PortExtension->SrbQueue, Srb);
1259
1260 if (PortExtension->IsActive == FALSE)
1261 return; // we should wait for device to get active
1262
1263 AhciZeroMemory(&lockhandle, sizeof(lockhandle));
1264
1265 // Acquire Lock
1266 StorPortAcquireSpinLock(AdapterExtension, InterruptLock, NULL, &lockhandle);
1267
1268 occupiedSlots = (PortExtension->QueueSlots | PortExtension->CommandIssuedSlots); // Busy command slots for given port
1269 NCS = AHCI_Global_Port_CAP_NCS(AdapterExtension->CAP);
1270 commandSlotMask = (1 << NCS) - 1; // available slots mask
1271
1272 commandSlotMask = (commandSlotMask & ~occupiedSlots);
1273 if(commandSlotMask != 0)
1274 {
1275 // iterate over HBA port slots
1276 for (slotIndex = 0; slotIndex < NCS; slotIndex++)
1277 {
1278 // find first free slot
1279 if ((commandSlotMask & (1 << slotIndex)) != 0)
1280 {
1281 tmpSrb = RemoveQueue(&PortExtension->SrbQueue);
1282 if (tmpSrb != NULL)
1283 {
1284 NT_ASSERT(tmpSrb->PathId == PathId);
1285 AhciProcessSrb(PortExtension, tmpSrb, slotIndex);
1286 }
1287 else
1288 {
1289 break;
1290 }
1291 }
1292 else
1293 {
1294 break;
1295 }
1296 }
1297 }
1298
1299 // program HBA port
1300 AhciActivatePort(PortExtension);
1301
1302 // Release Lock
1303 StorPortReleaseSpinLock(AdapterExtension, &lockhandle);
1304
1305 return;
1306 }// -- AhciProcessIO();
1307
1308 /**
1309 * @name InquiryCompletion
1310 * @not_implemented
1311 *
1312 * InquiryCompletion routine should be called after device signals
1313 * for device inquiry request is completed (through interrupt)
1314 *
1315 * @param PortExtension
1316 * @param Srb
1317 *
1318 */
1319 VOID
1320 InquiryCompletion (
1321 __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
1322 __in PAHCI_PORT_EXTENSION PortExtension,
1323 __in PSCSI_REQUEST_BLOCK Srb
1324 )
1325 {
1326 ULONG SrbStatus;
1327 PAHCI_SRB_EXTENSION SrbExtension;
1328
1329 DebugPrint("InquiryCompletion()\n");
1330
1331 NT_ASSERT(PortExtension != NULL);
1332 NT_ASSERT(Srb != NULL);
1333
1334 SrbStatus = Srb->SrbStatus;
1335 SrbExtension = GetSrbExtension(Srb);
1336
1337 if (SrbStatus == SRB_STATUS_SUCCESS)
1338 {
1339 if (SrbExtension->CommandReg == IDE_COMMAND_IDENTIFY)
1340 {
1341 DebugPrint("Device: ATA\n");
1342 AdapterExtension->DeviceParams.DeviceType = AHCI_DEVICE_TYPE_ATA;
1343 }
1344 else
1345 {
1346 DebugPrint("Device: ATAPI\n");
1347 AdapterExtension->DeviceParams.DeviceType = AHCI_DEVICE_TYPE_ATAPI;
1348 }
1349 // TODO: Set Device Paramters
1350 }
1351 else if (SrbStatus == SRB_STATUS_NO_DEVICE)
1352 {
1353 DebugPrint("Device: No Device\n");
1354 AdapterExtension->DeviceParams.DeviceType = AHCI_DEVICE_TYPE_NODEVICE;
1355 }
1356 else
1357 {
1358 return;
1359 }
1360
1361 return;
1362 }// -- InquiryCompletion();
1363
1364 /**
1365 * @name DeviceInquiryRequest
1366 * @implemented
1367 *
1368 * Tells wheather given port is implemented or not
1369 *
1370 * @param AdapterExtension
1371 * @param Srb
1372 * @param Cdb
1373 *
1374 * @return
1375 * return STOR status for DeviceInquiryRequest
1376 *
1377 * @remark
1378 * http://www.seagate.com/staticfiles/support/disc/manuals/Interface%20manuals/100293068c.pdf
1379 */
1380 ULONG
1381 DeviceInquiryRequest (
1382 __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
1383 __in PSCSI_REQUEST_BLOCK Srb,
1384 __in PCDB Cdb
1385 )
1386 {
1387 PVOID DataBuffer;
1388 ULONG DataBufferLength;
1389 PAHCI_PORT_EXTENSION PortExtension;
1390 PAHCI_SRB_EXTENSION SrbExtension;
1391
1392 DebugPrint("DeviceInquiryRequest()\n");
1393
1394 NT_ASSERT(IsPortValid(AdapterExtension, Srb->PathId));
1395
1396 SrbExtension = GetSrbExtension(Srb);
1397 PortExtension = &AdapterExtension->PortExtension[Srb->PathId];
1398
1399 // 3.6.1
1400 // If the EVPD bit is set to zero, the device server shall return the standard INQUIRY data
1401 if (Cdb->CDB6INQUIRY3.EnableVitalProductData == 0)
1402 {
1403 DebugPrint("\tEVPD Inquired\n");
1404 NT_ASSERT(SrbExtension != NULL);
1405
1406 SrbExtension->AtaFunction = ATA_FUNCTION_ATA_IDENTIFY;
1407 SrbExtension->Flags |= ATA_FLAGS_DATA_IN;
1408 SrbExtension->CompletionRoutine = InquiryCompletion;
1409 SrbExtension->CommandReg = IDE_COMMAND_NOT_VALID;
1410
1411 // TODO: Should use AhciZeroMemory
1412 SrbExtension->FeaturesLow = 0;
1413 SrbExtension->LBA0 = 0;
1414 SrbExtension->LBA1 = 0;
1415 SrbExtension->LBA2 = 0;
1416 SrbExtension->Device = 0;
1417 SrbExtension->LBA3 = 0;
1418 SrbExtension->LBA4 = 0;
1419 SrbExtension->LBA5 = 0;
1420 SrbExtension->FeaturesHigh = 0;
1421 SrbExtension->SectorCountLow = 0;
1422 SrbExtension->SectorCountHigh = 0;
1423
1424 SrbExtension->Sgl.NumberOfElements = 1;
1425 SrbExtension->Sgl.List[0].PhysicalAddress.LowPart = PortExtension->IdentifyDeviceDataPhysicalAddress.LowPart;
1426 SrbExtension->Sgl.List[0].PhysicalAddress.HighPart = PortExtension->IdentifyDeviceDataPhysicalAddress.HighPart;
1427 SrbExtension->Sgl.List[0].Length = sizeof(IDENTIFY_DEVICE_DATA);
1428 }
1429 else
1430 {
1431 DebugPrint("\tVPD Inquired\n");
1432
1433 DataBuffer = Srb->DataBuffer;
1434 DataBufferLength = Srb->DataTransferLength;
1435
1436 if (DataBuffer == NULL)
1437 {
1438 return SRB_STATUS_INVALID_REQUEST;
1439 }
1440
1441 AhciZeroMemory(DataBuffer, DataBufferLength);
1442
1443 // not supported
1444 return SRB_STATUS_BAD_FUNCTION;
1445 }
1446
1447 AhciProcessIO(AdapterExtension, Srb->PathId, Srb);
1448 return SRB_STATUS_PENDING;
1449 }// -- DeviceInquiryRequest();
1450
1451 /**
1452 * @name AhciAdapterReset
1453 * @implemented
1454 *
1455 * 10.4.3 HBA Reset
1456 * If the HBA becomes unusable for multiple ports, and a software reset or port reset does not correct the
1457 * problem, software may reset the entire HBA by setting GHC.HR to ‘1’. When software sets the GHC.HR
1458 * bit to ‘1’, the HBA shall perform an internal reset action. The bit shall be cleared to ‘0’ by the HBA when
1459 * the reset is complete. A software write of ‘0’ to GHC.HR shall have no effect. To perform the HBA reset,
1460 * software sets GHC.HR to ‘1’ and may poll until this bit is read to be ‘0’, at which point software knows that
1461 * the HBA reset has completed.
1462 * If the HBA has not cleared GHC.HR to ‘0’ within 1 second of software setting GHC.HR to ‘1’, the HBA is in
1463 * a hung or locked state.
1464 *
1465 * @param AdapterExtension
1466 *
1467 * @return
1468 * TRUE in case AHCI Controller RESTARTED successfully. i.e GHC.HR == 0
1469 */
1470 BOOLEAN
1471 AhciAdapterReset (
1472 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
1473 )
1474 {
1475 ULONG ghc, ticks, ghcStatus;
1476 PAHCI_MEMORY_REGISTERS abar = NULL;
1477
1478 DebugPrint("AhciAdapterReset()\n");
1479
1480 abar = AdapterExtension->ABAR_Address;
1481 if (abar == NULL) // basic sanity
1482 {
1483 return FALSE;
1484 }
1485
1486 // HR -- Very first bit (lowest significant)
1487 ghc = AHCI_Global_HBA_CONTROL_HR;
1488 StorPortWriteRegisterUlong(AdapterExtension, &abar->GHC, ghc);
1489
1490 for (ticks = 0; ticks < 50; ++ticks)
1491 {
1492 ghcStatus = StorPortReadRegisterUlong(AdapterExtension, &abar->GHC);
1493 if ((ghcStatus & AHCI_Global_HBA_CONTROL_HR) == 0)
1494 {
1495 break;
1496 }
1497 StorPortStallExecution(20000);
1498 }
1499
1500 if (ticks == 50)// 1 second
1501 {
1502 DebugPrint("\tDevice Timeout\n");
1503 return FALSE;
1504 }
1505
1506 return TRUE;
1507 }// -- AhciAdapterReset();
1508
1509 /**
1510 * @name AhciZeroMemory
1511 * @implemented
1512 *
1513 * Clear buffer by filling zeros
1514 *
1515 * @param Buffer
1516 * @param BufferSize
1517 */
1518 __inline
1519 VOID
1520 AhciZeroMemory (
1521 __out PCHAR Buffer,
1522 __in ULONG BufferSize
1523 )
1524 {
1525 ULONG i;
1526 for (i = 0; i < BufferSize; i++)
1527 {
1528 Buffer[i] = 0;
1529 }
1530
1531 return;
1532 }// -- AhciZeroMemory();
1533
1534 /**
1535 * @name IsPortValid
1536 * @implemented
1537 *
1538 * Tells wheather given port is implemented or not
1539 *
1540 * @param AdapterExtension
1541 * @param PathId
1542 *
1543 * @return
1544 * return TRUE if provided port is valid (implemented) or not
1545 */
1546 __inline
1547 BOOLEAN
1548 IsPortValid (
1549 __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
1550 __in UCHAR pathId
1551 )
1552 {
1553 NT_ASSERT(pathId >= 0);
1554
1555 if (pathId >= AdapterExtension->PortCount)
1556 {
1557 return FALSE;
1558 }
1559
1560 return AdapterExtension->PortExtension[pathId].IsActive;
1561 }// -- IsPortValid()
1562
1563 /**
1564 * @name AddQueue
1565 * @implemented
1566 *
1567 * Add Srb to Queue
1568 *
1569 * @param Queue
1570 * @param Srb
1571 *
1572 * @return
1573 * return TRUE if Srb is successfully added to Queue
1574 *
1575 */
1576 __inline
1577 BOOLEAN
1578 AddQueue (
1579 __inout PAHCI_QUEUE Queue,
1580 __in PVOID Srb
1581 )
1582 {
1583 NT_ASSERT(Queue->Head < MAXIMUM_QUEUE_BUFFER_SIZE);
1584 NT_ASSERT(Queue->Tail < MAXIMUM_QUEUE_BUFFER_SIZE);
1585
1586 if (Queue->Tail == ((Queue->Head + 1) % MAXIMUM_QUEUE_BUFFER_SIZE))
1587 return FALSE;
1588
1589 Queue->Buffer[Queue->Head++] = Srb;
1590 Queue->Head %= MAXIMUM_QUEUE_BUFFER_SIZE;
1591
1592 return TRUE;
1593 }// -- AddQueue();
1594
1595 /**
1596 * @name RemoveQueue
1597 * @implemented
1598 *
1599 * Remove and return Srb from Queue
1600 *
1601 * @param Queue
1602 *
1603 * @return
1604 * return Srb
1605 *
1606 */
1607 __inline
1608 PVOID
1609 RemoveQueue (
1610 __inout PAHCI_QUEUE Queue
1611 )
1612 {
1613 PVOID Srb;
1614
1615 NT_ASSERT(Queue->Head < MAXIMUM_QUEUE_BUFFER_SIZE);
1616 NT_ASSERT(Queue->Tail < MAXIMUM_QUEUE_BUFFER_SIZE);
1617
1618 if (Queue->Head == Queue->Tail)
1619 return NULL;
1620
1621 Srb = Queue->Buffer[Queue->Tail++];
1622 Queue->Tail %= MAXIMUM_QUEUE_BUFFER_SIZE;
1623
1624 return Srb;
1625 }// -- RemoveQueue();
1626
1627 /**
1628 * @name GetSrbExtension
1629 * @implemented
1630 *
1631 * GetSrbExtension from Srb make sure It is properly aligned
1632 *
1633 * @param Srb
1634 *
1635 * @return
1636 * return SrbExtension
1637 *
1638 */
1639 __inline
1640 PAHCI_SRB_EXTENSION
1641 GetSrbExtension (
1642 __in PSCSI_REQUEST_BLOCK Srb
1643 )
1644 {
1645 ULONG Offset;
1646 ULONG_PTR SrbExtension;
1647
1648 SrbExtension = Srb->SrbExtension;
1649 Offset = SrbExtension % 128;
1650
1651 // CommandTable should be 128 byte aligned
1652 if (Offset != 0)
1653 Offset = 128 - Offset;
1654
1655 return (PAHCI_SRB_EXTENSION)(SrbExtension + Offset);
1656 }// -- PAHCI_SRB_EXTENSION();