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