Handled SCSIOP_TEST_UNIT_READY
[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 AhciDebugPrint("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 AhciDebugPrint("\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 AhciDebugPrint("\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 AhciDebugPrint("\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 PAHCI_PORT_EXTENSION PortExtension;
136
137 AhciDebugPrint("AhciAllocateResourceForAdapter()\n");
138
139 NCS = AHCI_Global_Port_CAP_NCS(AdapterExtension->CAP);
140 AlignedNCS = ROUND_UP(NCS, 8);
141
142 // get port count -- Number of set bits in `AdapterExtension->PortImplemented`
143 portCount = 0;
144 portImplemented = AdapterExtension->PortImplemented;
145
146 NT_ASSERT(portImplemented != 0);
147 for (index = MAXIMUM_AHCI_PORT_COUNT - 1; index > 0; index--)
148 if ((portImplemented & (1 << index)) != 0)
149 break;
150
151 portCount = index + 1;
152 AhciDebugPrint("\tPort Count: %d\n", portCount);
153
154 AdapterExtension->PortCount = portCount;
155 nonCachedExtensionSize = sizeof(AHCI_COMMAND_HEADER) * AlignedNCS + //should be 1K aligned
156 sizeof(AHCI_RECEIVED_FIS) +
157 sizeof(IDENTIFY_DEVICE_DATA);
158
159 // align nonCachedExtensionSize to 1024
160 nonCachedExtensionSize = ROUND_UP(nonCachedExtensionSize, 1024);
161
162 AdapterExtension->NonCachedExtension = StorPortGetUncachedExtension(AdapterExtension,
163 ConfigInfo,
164 nonCachedExtensionSize * portCount);
165
166 if (AdapterExtension->NonCachedExtension == NULL)
167 {
168 AhciDebugPrint("\tadapterExtension->NonCachedExtension == NULL\n");
169 return FALSE;
170 }
171
172 nonCachedExtension = AdapterExtension->NonCachedExtension;
173 AhciZeroMemory(nonCachedExtension, nonCachedExtensionSize * portCount);
174
175 for (index = 0; index < portCount; index++)
176 {
177 PortExtension = &AdapterExtension->PortExtension[index];
178
179 PortExtension->DeviceParams.IsActive = FALSE;
180 if ((AdapterExtension->PortImplemented & (1 << index)) != 0)
181 {
182 PortExtension->PortNumber = index;
183 PortExtension->DeviceParams.IsActive = TRUE;
184 PortExtension->AdapterExtension = AdapterExtension;
185 PortExtension->CommandList = (PAHCI_COMMAND_HEADER)nonCachedExtension;
186
187 tmp = (PCHAR)(nonCachedExtension + sizeof(AHCI_COMMAND_HEADER) * AlignedNCS);
188
189 PortExtension->ReceivedFIS = (PAHCI_RECEIVED_FIS)tmp;
190 PortExtension->IdentifyDeviceData = (PIDENTIFY_DEVICE_DATA)(tmp + sizeof(AHCI_RECEIVED_FIS));
191 PortExtension->MaxPortQueueDepth = NCS;
192 nonCachedExtension += nonCachedExtensionSize;
193 }
194 }
195
196 return TRUE;
197 }// -- AhciAllocateResourceForAdapter();
198
199 /**
200 * @name AhciStartPort
201 * @implemented
202 *
203 * Try to start the port device
204 *
205 * @param AdapterExtension
206 * @param PortExtension
207 *
208 */
209 BOOLEAN
210 AhciStartPort (
211 __in PAHCI_PORT_EXTENSION PortExtension
212 )
213 {
214 ULONG index;
215 AHCI_PORT_CMD cmd;
216 AHCI_TASK_FILE_DATA tfd;
217 AHCI_INTERRUPT_ENABLE ie;
218 AHCI_SERIAL_ATA_STATUS ssts;
219 AHCI_SERIAL_ATA_CONTROL sctl;
220 PAHCI_ADAPTER_EXTENSION AdapterExtension;
221
222 AhciDebugPrint("AhciStartPort()\n");
223
224 AdapterExtension = PortExtension->AdapterExtension;
225 cmd.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->CMD);
226
227 if ((cmd.FR == 1) && (cmd.CR == 1) && (cmd.FRE == 1) && (cmd.ST == 1))
228 {
229 // Already Running
230 return TRUE;
231 }
232
233 cmd.SUD = 1;
234 StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->CMD, cmd.Status);
235
236 if (((cmd.FR == 1) && (cmd.FRE == 0)) ||
237 ((cmd.CR == 1) && (cmd.ST == 0)))
238 {
239 AhciDebugPrint("\tCOMRESET\n");
240 // perform COMRESET
241 // section 10.4.2
242
243 // Software causes a port reset (COMRESET) by writing 1h to the PxSCTL.DET field to invoke a
244 // COMRESET on the interface and start a re-establishment of Phy layer communications. Software shall
245 // wait at least 1 millisecond before clearing PxSCTL.DET to 0h; this ensures that at least one COMRESET
246 // signal is sent over the interface. After clearing PxSCTL.DET to 0h, software should wait for
247 // communication to be re-established as indicated by PxSSTS.DET being set to 3h. Then software should
248 // write all 1s to the PxSERR register to clear any bits that were set as part of the port reset.
249
250 sctl.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->SCTL);
251 sctl.DET = 1;
252 StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->SCTL, sctl.Status);
253
254 StorPortStallExecution(1000);
255
256 sctl.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->SCTL);
257 sctl.DET = 0;
258 StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->SCTL, sctl.Status);
259
260 // Poll DET to verify if a device is attached to the port
261 index = 0;
262 do
263 {
264 StorPortStallExecution(1000);
265 ssts.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->SSTS);
266
267 index++;
268 if (ssts.DET != 0)
269 {
270 break;
271 }
272 }
273 while(index < 30);
274 }
275
276 ssts.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->SSTS);
277 switch (ssts.DET)
278 {
279 case 0x3:
280 {
281 NT_ASSERT(cmd.ST == 0);
282
283 // make sure FIS Recieve is enabled (cmd.FRE)
284 index = 0;
285 do
286 {
287 StorPortStallExecution(10000);
288 cmd.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->CMD);
289 cmd.FRE = 1;
290 StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->CMD, cmd.Status);
291 index++;
292 }
293 while((cmd.FR != 1) && (index < 3));
294
295 if (cmd.FR != 1)
296 {
297 // failed to start FIS DMA engine
298 // it can crash the driver later
299 // so better to turn this port off
300 return FALSE;
301 }
302
303 // start port channel
304 // set cmd.ST
305
306 NT_ASSERT(cmd.FRE == 1);
307 NT_ASSERT(cmd.CR == 0);
308
309 // why assert? well If we face such condition on DET = 0x3
310 // then we don't have port in idle state and hence before executing this part of code
311 // we must have restarted it.
312 tfd.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->TFD);
313
314 if ((tfd.STS.BSY) || (tfd.STS.DRQ))
315 {
316 AhciDebugPrint("\tUnhandled Case BSY-DRQ\n");
317 }
318
319 // clear pending interrupts
320 StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->SERR, (ULONG)~0);
321 StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->IS, (ULONG)~0);
322 StorPortWriteRegisterUlong(AdapterExtension, AdapterExtension->IS, (1 << PortExtension->PortNumber));
323
324 // set IE
325 ie.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->IE);
326 /* Device to Host Register FIS Interrupt Enable */
327 ie.DHRE = 1;
328 /* PIO Setup FIS Interrupt Enable */
329 ie.PSE = 1;
330 /* DMA Setup FIS Interrupt Enable */
331 ie.DSE = 1;
332 /* Set Device Bits FIS Interrupt Enable */
333 ie.SDBE = 1;
334 /* Unknown FIS Interrupt Enable */
335 ie.UFE = 0;
336 /* Descriptor Processed Interrupt Enable */
337 ie.DPE = 0;
338 /* Port Change Interrupt Enable */
339 ie.PCE = 1;
340 /* Device Mechanical Presence Enable */
341 ie.DMPE = 0;
342 /* PhyRdy Change Interrupt Enable */
343 ie.PRCE = 1;
344 /* Incorrect Port Multiplier Enable */
345 ie.IPME = 0;
346 /* Overflow Enable */
347 ie.OFE = 1;
348 /* Interface Non-fatal Error Enable */
349 ie.INFE = 1;
350 /* Interface Fatal Error Enable */
351 ie.IFE = 1;
352 /* Host Bus Data Error Enable */
353 ie.HBDE = 1;
354 /* Host Bus Fatal Error Enable */
355 ie.HBFE = 1;
356 /* Task File Error Enable */
357 ie.TFEE = 1;
358
359 cmd.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->CMD);
360 /* Cold Presence Detect Enable */
361 if (cmd.CPD) // does it support CPD?
362 {
363 // disable it for now
364 ie.CPDE = 0;
365 }
366
367 // should I replace this to single line?
368 // by directly setting ie.Status?
369
370 StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->IE, ie.Status);
371
372 cmd.ST = 1;
373 StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->CMD, cmd.Status);
374 cmd.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->CMD);
375
376 if (cmd.ST != 1)
377 {
378 AhciDebugPrint("\tFailed to start Port\n");
379 return FALSE;
380 }
381
382 return TRUE;
383 }
384 default:
385 // unhandled case
386 AhciDebugPrint("\tDET == %x Unsupported\n", ssts.DET);
387 return FALSE;
388 }
389
390 }// -- AhciStartPort();
391
392 /**
393 * @name AhciCommandCompletionDpcRoutine
394 * @implemented
395 *
396 * Handles Completed Commands
397 *
398 * @param Dpc
399 * @param AdapterExtension
400 * @param SystemArgument1
401 * @param SystemArgument2
402 */
403 VOID
404 AhciCommandCompletionDpcRoutine (
405 __in PSTOR_DPC Dpc,
406 __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
407 __in PAHCI_PORT_EXTENSION PortExtension,
408 __in PVOID SystemArgument2
409 )
410 {
411 PSCSI_REQUEST_BLOCK Srb;
412 PAHCI_SRB_EXTENSION SrbExtension;
413 STOR_LOCK_HANDLE lockhandle = {0};
414 PAHCI_COMPLETION_ROUTINE CompletionRoutine;
415
416 UNREFERENCED_PARAMETER(Dpc);
417 UNREFERENCED_PARAMETER(SystemArgument2);
418
419 AhciDebugPrint("AhciCommandCompletionDpcRoutine()\n");
420
421 StorPortAcquireSpinLock(AdapterExtension, InterruptLock, NULL, &lockhandle);
422 Srb = RemoveQueue(&PortExtension->CompletionQueue);
423 StorPortReleaseSpinLock(AdapterExtension, &lockhandle);
424
425 NT_ASSERT(Srb != NULL);
426
427 if (Srb->SrbStatus == SRB_STATUS_PENDING)
428 {
429 Srb->SrbStatus = SRB_STATUS_SUCCESS;
430 }
431 else
432 {
433 return;
434 }
435
436 SrbExtension = GetSrbExtension(Srb);
437
438 CompletionRoutine = SrbExtension->CompletionRoutine;
439 NT_ASSERT(CompletionRoutine != NULL);
440
441 // now it's completion routine responsibility to set SrbStatus
442 CompletionRoutine(PortExtension, Srb);
443
444 StorPortNotification(RequestComplete, AdapterExtension, Srb);
445
446 return;
447 }// -- AhciCommandCompletionDpcRoutine();
448
449 /**
450 * @name AhciHwPassiveInitialize
451 * @implemented
452 *
453 * initializes the HBA and finds all devices that are of interest to the miniport driver. (at PASSIVE LEVEL)
454 *
455 * @param adapterExtension
456 *
457 * @return
458 * return TRUE if intialization was successful
459 */
460 BOOLEAN
461 AhciHwPassiveInitialize (
462 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
463 )
464 {
465 ULONG index;
466 PAHCI_PORT_EXTENSION PortExtension;
467
468 AhciDebugPrint("AhciHwPassiveInitialize()\n");
469
470 for (index = 0; index < AdapterExtension->PortCount; index++)
471 {
472 if ((AdapterExtension->PortImplemented & (0x1 << index)) != 0)
473 {
474 PortExtension = &AdapterExtension->PortExtension[index];
475 PortExtension->DeviceParams.IsActive = AhciStartPort(PortExtension);
476 StorPortInitializeDpc(AdapterExtension, &PortExtension->CommandCompletion, AhciCommandCompletionDpcRoutine);
477 }
478 }
479
480 return TRUE;
481 }// -- AhciHwPassiveInitialize();
482
483 /**
484 * @name AhciHwInitialize
485 * @implemented
486 *
487 * initializes the HBA and finds all devices that are of interest to the miniport driver.
488 *
489 * @param adapterExtension
490 *
491 * @return
492 * return TRUE if intialization was successful
493 */
494 BOOLEAN
495 AhciHwInitialize (
496 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
497 )
498 {
499 AHCI_GHC ghc;
500
501 AhciDebugPrint("AhciHwInitialize()\n");
502
503 AdapterExtension->StateFlags.MessagePerPort = FALSE;
504
505 // First check what type of interrupt/synchronization device is using
506 ghc.Status = StorPortReadRegisterUlong(AdapterExtension, &AdapterExtension->ABAR_Address->GHC);
507
508 // When set to ‘1’ by hardware, indicates that the HBA requested more than one MSI vector
509 // but has reverted to using the first vector only. When this bit is cleared to ‘0’,
510 // the HBA has not reverted to single MSI mode (i.e. hardware is already in single MSI mode,
511 // software has allocated the number of messages requested
512 if (ghc.MRSM == 0)
513 {
514 AdapterExtension->StateFlags.MessagePerPort = TRUE;
515 AhciDebugPrint("\tMultiple MSI based message not supported\n");
516 }
517
518 StorPortEnablePassiveInitialization(AdapterExtension, AhciHwPassiveInitialize);
519
520 return TRUE;
521 }// -- AhciHwInitialize();
522
523 /**
524 * @name AhciCompleteIssuedSrb
525 * @implemented
526 *
527 * Complete issued Srbs
528 *
529 * @param PortExtension
530 *
531 */
532 VOID
533 AhciCompleteIssuedSrb (
534 __in PAHCI_PORT_EXTENSION PortExtension,
535 __in ULONG CommandsToComplete
536 )
537 {
538 ULONG NCS, i;
539 PSCSI_REQUEST_BLOCK Srb;
540 PAHCI_SRB_EXTENSION SrbExtension;
541 PAHCI_ADAPTER_EXTENSION AdapterExtension;
542
543 AhciDebugPrint("AhciCompleteIssuedSrb()\n");
544
545 NT_ASSERT(CommandsToComplete != 0);
546
547 AhciDebugPrint("\tCompleted Commands: %d\n", CommandsToComplete);
548
549 AdapterExtension = PortExtension->AdapterExtension;
550 NCS = AHCI_Global_Port_CAP_NCS(AdapterExtension->CAP);
551
552 for (i = 0; i < NCS; i++)
553 {
554 if (((1 << i) & CommandsToComplete) != 0)
555 {
556 Srb = PortExtension->Slot[i];
557
558 if (Srb == NULL)
559 {
560 continue;
561 }
562
563 SrbExtension = GetSrbExtension(Srb);
564 NT_ASSERT(SrbExtension != NULL);
565
566 if (SrbExtension->CompletionRoutine != NULL)
567 {
568 AddQueue(&PortExtension->CompletionQueue, Srb);
569 StorPortIssueDpc(AdapterExtension, &PortExtension->CommandCompletion, PortExtension, Srb);
570 }
571 else
572 {
573 NT_ASSERT(Srb->SrbStatus == SRB_STATUS_PENDING);
574 Srb->SrbStatus = SRB_STATUS_SUCCESS;
575 StorPortNotification(RequestComplete, AdapterExtension, Srb);
576 }
577 }
578 }
579
580 return;
581 }// -- AhciCompleteIssuedSrb();
582
583 /**
584 * @name AhciInterruptHandler
585 * @not_implemented
586 *
587 * Interrupt Handler for PortExtension
588 *
589 * @param PortExtension
590 *
591 */
592 VOID
593 AhciInterruptHandler (
594 __in PAHCI_PORT_EXTENSION PortExtension
595 )
596 {
597 ULONG is, ci, sact, outstanding;
598 AHCI_INTERRUPT_STATUS PxIS;
599 AHCI_INTERRUPT_STATUS PxISMasked;
600 PAHCI_ADAPTER_EXTENSION AdapterExtension;
601
602 AhciDebugPrint("AhciInterruptHandler()\n");
603 AhciDebugPrint("\tPort Number: %d\n", PortExtension->PortNumber);
604
605 AdapterExtension = PortExtension->AdapterExtension;
606 NT_ASSERT(IsPortValid(AdapterExtension, PortExtension->PortNumber));
607
608 // 5.5.3
609 // 1. Software determines the cause of the interrupt by reading the PxIS register.
610 // It is possible for multiple bits to be set
611 // 2. Software clears appropriate bits in the PxIS register corresponding to the cause of the interrupt.
612 // 3. Software clears the interrupt bit in IS.IPS corresponding to the port.
613 // 4. If executing non-queued commands, software reads the PxCI register, and compares the current value to
614 // the list of commands previously issued by software that are still outstanding.
615 // If executing native queued commands, software reads the PxSACT register and compares the current
616 // value to the list of commands previously issued by software.
617 // Software completes with success any outstanding command whose corresponding bit has been cleared in
618 // the respective register. PxCI and PxSACT are volatile registers; software should only use their values
619 // to determine commands that have completed, not to determine which commands have previously been issued.
620 // 5. If there were errors, noted in the PxIS register, software performs error recovery actions (see section 6.2.2).
621 PxISMasked.Status = 0;
622 PxIS.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->IS);
623
624 // 6.2.2
625 // Fatal Error
626 // signified by the setting of PxIS.HBFS, PxIS.HBDS, PxIS.IFS, or PxIS.TFES
627 if (PxIS.HBFS || PxIS.HBDS || PxIS.IFS || PxIS.TFES)
628 {
629 // In this state, the HBA shall not issue any new commands nor acknowledge DMA Setup FISes to process
630 // any native command queuing commands. To recover, the port must be restarted
631 // To detect an error that requires software recovery actions to be performed,
632 // software should check whether any of the following status bits are set on an interrupt:
633 // PxIS.HBFS, PxIS.HBDS, PxIS.IFS, and PxIS.TFES. If any of these bits are set,
634 // software should perform the appropriate error recovery actions based on whether
635 // non-queued commands were being issued or native command queuing commands were being issued.
636
637 AhciDebugPrint("\tFatal Error: %x\n", PxIS.Status);
638 }
639
640 // Normal Command Completion
641 // 3.3.5
642 // A D2H Register FIS has been received with the ‘I’ bit set, and has been copied into system memory.
643 PxISMasked.DHRS = PxIS.DHRS;
644 // A PIO Setup FIS has been received with the ‘I’ bit set, it has been copied into system memory.
645 PxISMasked.PSS = PxIS.PSS;
646 // A DMA Setup FIS has been received with the ‘I’ bit set and has been copied into system memory.
647 PxISMasked.DSS = PxIS.DSS;
648 // A Set Device Bits FIS has been received with the ‘I’ bit set and has been copied into system memory/
649 PxISMasked.SDBS = PxIS.SDBS;
650 // A PRD with the ‘I’ bit set has transferred all of its data.
651 PxISMasked.DPS = PxIS.DPS;
652
653 if (PxISMasked.Status != 0)
654 {
655 StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->IS, PxISMasked.Status);
656 }
657
658 // 10.7.1.1
659 // Clear port interrupt
660 // It is set by the level of the virtual interrupt line being a set, and cleared by a write of ‘1’ from the software.
661 is = (1 << PortExtension->PortNumber);
662 StorPortWriteRegisterUlong(AdapterExtension, AdapterExtension->IS, is);
663
664 ci = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->CI);
665 sact = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->SACT);
666
667 outstanding = ci | sact; // NOTE: Including both non-NCQ and NCQ based commands
668 if ((PortExtension->CommandIssuedSlots & (~outstanding)) != 0)
669 {
670 AhciCompleteIssuedSrb(PortExtension, (PortExtension->CommandIssuedSlots & (~outstanding)));
671 PortExtension->CommandIssuedSlots &= outstanding;
672 }
673
674 return;
675 }// -- AhciInterruptHandler();
676
677 /**
678 * @name AhciHwInterrupt
679 * @implemented
680 *
681 * The Storport driver calls the HwStorInterrupt routine after the HBA generates an interrupt request.
682 *
683 * @param AdapterExtension
684 *
685 * @return
686 * return TRUE Indicates that an interrupt was pending on adapter.
687 * return FALSE Indicates the interrupt was not ours.
688 */
689 BOOLEAN
690 AhciHwInterrupt (
691 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
692 )
693 {
694 ULONG portPending, nextPort, i, portCount;
695
696 if (AdapterExtension->StateFlags.Removed)
697 {
698 return FALSE;
699 }
700
701 portPending = StorPortReadRegisterUlong(AdapterExtension, AdapterExtension->IS);
702
703 // we process interrupt for implemented ports only
704 portCount = AdapterExtension->PortCount;
705 portPending = portPending & AdapterExtension->PortImplemented;
706
707 if (portPending == 0)
708 {
709 return FALSE;
710 }
711
712 for (i = 1; i <= portCount; i++)
713 {
714 nextPort = (AdapterExtension->LastInterruptPort + i) % portCount;
715 if ((portPending & (0x1 << nextPort)) == 0)
716 continue;
717
718 NT_ASSERT(IsPortValid(AdapterExtension, nextPort));
719
720 if (AdapterExtension->PortExtension[nextPort].DeviceParams.IsActive == FALSE)
721 {
722 continue;
723 }
724
725 // we can assign this interrupt to this port
726 AdapterExtension->LastInterruptPort = nextPort;
727 AhciInterruptHandler(&AdapterExtension->PortExtension[nextPort]);
728
729 portPending &= ~(1 << nextPort);
730
731 // interrupt belongs to this device
732 // should always return TRUE
733 return TRUE;
734 }
735
736 AhciDebugPrint("\tSomething went wrong");
737 return FALSE;
738 }// -- AhciHwInterrupt();
739
740 /**
741 * @name AhciHwStartIo
742 * @not_implemented
743 *
744 * The Storport driver calls the HwStorStartIo routine one time for each incoming I/O request.
745 *
746 * @param adapterExtension
747 * @param Srb
748 *
749 * @return
750 * return TRUE if the request was accepted
751 * return FALSE if the request must be submitted later
752 */
753 BOOLEAN
754 AhciHwStartIo (
755 __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
756 __in PSCSI_REQUEST_BLOCK Srb
757 )
758 {
759 AhciDebugPrint("AhciHwStartIo()\n");
760
761 if (!IsPortValid(AdapterExtension, Srb->PathId))
762 {
763 Srb->SrbStatus = SRB_STATUS_NO_DEVICE;
764 StorPortNotification(RequestComplete, AdapterExtension, Srb);
765 return TRUE;
766 }
767
768 switch(Srb->Function)
769 {
770 case SRB_FUNCTION_PNP:
771 {
772 // https://msdn.microsoft.com/windows/hardware/drivers/storage/handling-srb-function-pnp
773 // If the function member of an SRB is set to SRB_FUNCTION_PNP,
774 // the SRB is a structure of type SCSI_PNP_REQUEST_BLOCK.
775
776 PSCSI_PNP_REQUEST_BLOCK pnpRequest;
777 pnpRequest = (PSCSI_PNP_REQUEST_BLOCK)Srb;
778 if ((pnpRequest->SrbPnPFlags & SRB_PNP_FLAGS_ADAPTER_REQUEST) != 0)
779 {
780 switch(pnpRequest->PnPAction)
781 {
782 case StorRemoveDevice:
783 case StorSurpriseRemoval:
784 {
785 Srb->SrbStatus = SRB_STATUS_SUCCESS;
786 AdapterExtension->StateFlags.Removed = 1;
787 AhciDebugPrint("\tAdapter removed\n");
788 }
789 break;
790 case StorStopDevice:
791 {
792 Srb->SrbStatus = SRB_STATUS_SUCCESS;
793 AhciDebugPrint("\tRequested to Stop the adapter\n");
794 }
795 break;
796 default:
797 Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
798 break;
799 }
800 }
801 else
802 {
803 Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
804 }
805 }
806 break;
807 case SRB_FUNCTION_EXECUTE_SCSI:
808 {
809 // https://msdn.microsoft.com/en-us/windows/hardware/drivers/storage/handling-srb-function-execute-scsi
810 // On receipt of an SRB_FUNCTION_EXECUTE_SCSI request, a miniport driver's HwScsiStartIo
811 // routine does the following:
812 //
813 // - Gets and/or sets up whatever context the miniport driver maintains in its device,
814 // logical unit, and/or SRB extensions
815 // For example, a miniport driver might set up a logical unit extension with pointers
816 // to the SRB itself and the SRB DataBuffer pointer, the SRB DataTransferLength value,
817 // and a driver-defined value (or CDB SCSIOP_XXX value) indicating the operation to be
818 // carried out on the HBA.
819 //
820 // - Calls an internal routine to program the HBA, as partially directed by the SrbFlags,
821 // for the requested operation
822 // For a device I/O operation, such an internal routine generally selects the target device
823 // and sends the CDB over the bus to the target logical unit.
824 PCDB cdb = (PCDB)&Srb->Cdb;
825 if (Srb->CdbLength == 0)
826 {
827 AhciDebugPrint("\tOperationCode: %d\n", cdb->CDB10.OperationCode);
828 Srb->SrbStatus = SRB_STATUS_BAD_FUNCTION;
829 break;
830 }
831
832 NT_ASSERT(cdb != NULL);
833
834 switch(cdb->CDB10.OperationCode)
835 {
836 case SCSIOP_INQUIRY:
837 Srb->SrbStatus = DeviceInquiryRequest(AdapterExtension, Srb, cdb);
838 break;
839 case SCSIOP_REPORT_LUNS:
840 Srb->SrbStatus = DeviceReportLuns(AdapterExtension, Srb, cdb);
841 break;
842 case SCSIOP_READ_CAPACITY:
843 Srb->SrbStatus = DeviceRequestCapacity(AdapterExtension, Srb, cdb);
844 break;
845 case SCSIOP_TEST_UNIT_READY:
846 Srb->SrbStatus = DeviceRequestComplete(AdapterExtension, Srb, cdb);
847 break;
848 case SCSIOP_READ:
849 //case SCSIOP_WRITE:
850 Srb->SrbStatus = DeviceRequestReadWrite(AdapterExtension, Srb, cdb);
851 break;
852 default:
853 AhciDebugPrint("\tOperationCode: %d\n", cdb->CDB10.OperationCode);
854 Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
855 break;
856 }
857 }
858 break;
859 default:
860 AhciDebugPrint("\tUnknown function code recieved: %x\n", Srb->Function);
861 Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
862 break;
863 }
864
865 if (Srb->SrbStatus != SRB_STATUS_PENDING)
866 {
867 StorPortNotification(RequestComplete, AdapterExtension, Srb);
868 }
869 else
870 {
871 AhciProcessIO(AdapterExtension, Srb->PathId, Srb);
872 }
873 return TRUE;
874 }// -- AhciHwStartIo();
875
876 /**
877 * @name AhciHwResetBus
878 * @not_implemented
879 *
880 * The HwStorResetBus routine is called by the port driver to clear error conditions.
881 *
882 * @param adapterExtension
883 * @param PathId
884 *
885 * @return
886 * return TRUE if bus was successfully reset
887 */
888 BOOLEAN
889 AhciHwResetBus (
890 __in PVOID AdapterExtension,
891 __in ULONG PathId
892 )
893 {
894 STOR_LOCK_HANDLE lockhandle = {0};
895 PAHCI_ADAPTER_EXTENSION adapterExtension;
896
897 AhciDebugPrint("AhciHwResetBus()\n");
898
899 adapterExtension = AdapterExtension;
900
901 if (IsPortValid(AdapterExtension, PathId))
902 {
903 // Acquire Lock
904 StorPortAcquireSpinLock(AdapterExtension, InterruptLock, NULL, &lockhandle);
905
906 // TODO: Perform port reset
907
908 // Release lock
909 StorPortReleaseSpinLock(AdapterExtension, &lockhandle);
910 }
911
912 return FALSE;
913 }// -- AhciHwResetBus();
914
915 /**
916 * @name AhciHwFindAdapter
917 * @implemented
918 *
919 * The HwStorFindAdapter routine uses the supplied configuration to determine whether a specific
920 * HBA is supported and, if it is, to return configuration information about that adapter.
921 *
922 * 10.1 Platform Communication
923 * http://www.intel.in/content/dam/www/public/us/en/documents/technical-specifications/serial-ata-ahci-spec-rev1_2.pdf
924
925 * @param DeviceExtension
926 * @param HwContext
927 * @param BusInformation
928 * @param ArgumentString
929 * @param ConfigInfo
930 * @param Reserved3
931 *
932 * @return
933 * SP_RETURN_FOUND
934 * 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.
935 *
936 * SP_RETURN_ERROR
937 * 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.
938 *
939 * SP_RETURN_BAD_CONFIG
940 * Indicates that the supplied configuration information was invalid for the adapter.
941 *
942 * SP_RETURN_NOT_FOUND
943 * Indicates that no supported HBA was found for the supplied configuration information.
944 *
945 * @remarks Called by Storport.
946 */
947 ULONG
948 AhciHwFindAdapter (
949 __in PVOID AdapterExtension,
950 __in PVOID HwContext,
951 __in PVOID BusInformation,
952 __in PVOID ArgumentString,
953 __inout PPORT_CONFIGURATION_INFORMATION ConfigInfo,
954 __in PBOOLEAN Reserved3
955 )
956 {
957 AHCI_GHC ghc;
958 ULONG index, pci_cfg_len;
959 PACCESS_RANGE accessRange;
960 UCHAR pci_cfg_buf[sizeof(PCI_COMMON_CONFIG)];
961
962 PAHCI_MEMORY_REGISTERS abar;
963 PPCI_COMMON_CONFIG pciConfigData;
964 PAHCI_ADAPTER_EXTENSION adapterExtension;
965
966 AhciDebugPrint("AhciHwFindAdapter()\n");
967
968 UNREFERENCED_PARAMETER(HwContext);
969 UNREFERENCED_PARAMETER(BusInformation);
970 UNREFERENCED_PARAMETER(ArgumentString);
971 UNREFERENCED_PARAMETER(Reserved3);
972
973 adapterExtension = AdapterExtension;
974 adapterExtension->SlotNumber = ConfigInfo->SlotNumber;
975 adapterExtension->SystemIoBusNumber = ConfigInfo->SystemIoBusNumber;
976
977 // get PCI configuration header
978 pci_cfg_len = StorPortGetBusData(
979 adapterExtension,
980 PCIConfiguration,
981 adapterExtension->SystemIoBusNumber,
982 adapterExtension->SlotNumber,
983 pci_cfg_buf,
984 sizeof(PCI_COMMON_CONFIG));
985
986 if (pci_cfg_len != sizeof(PCI_COMMON_CONFIG))
987 {
988 AhciDebugPrint("\tpci_cfg_len != %d :: %d", sizeof(PCI_COMMON_CONFIG), pci_cfg_len);
989 return SP_RETURN_ERROR;//Not a valid device at the given bus number
990 }
991
992 pciConfigData = (PPCI_COMMON_CONFIG)pci_cfg_buf;
993 adapterExtension->VendorID = pciConfigData->VendorID;
994 adapterExtension->DeviceID = pciConfigData->DeviceID;
995 adapterExtension->RevisionID = pciConfigData->RevisionID;
996 // 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).
997 adapterExtension->AhciBaseAddress = pciConfigData->u.type0.BaseAddresses[5] & (0xFFFFFFF0);
998
999 AhciDebugPrint("\tVendorID:%d DeviceID:%d RevisionID:%d\n", adapterExtension->VendorID,
1000 adapterExtension->DeviceID,
1001 adapterExtension->RevisionID);
1002
1003 // 2.1.11
1004 abar = NULL;
1005 if (ConfigInfo->NumberOfAccessRanges > 0)
1006 {
1007 accessRange = *(ConfigInfo->AccessRanges);
1008 for (index = 0; index < ConfigInfo->NumberOfAccessRanges; index++)
1009 {
1010 if (accessRange[index].RangeStart.QuadPart == adapterExtension->AhciBaseAddress)
1011 {
1012 abar = StorPortGetDeviceBase(adapterExtension,
1013 ConfigInfo->AdapterInterfaceType,
1014 ConfigInfo->SystemIoBusNumber,
1015 accessRange[index].RangeStart,
1016 accessRange[index].RangeLength,
1017 !accessRange[index].RangeInMemory);
1018 break;
1019 }
1020 }
1021 }
1022
1023 if (abar == NULL)
1024 {
1025 AhciDebugPrint("\tabar == NULL\n");
1026 return SP_RETURN_ERROR; // corrupted information supplied
1027 }
1028
1029 adapterExtension->ABAR_Address = abar;
1030 adapterExtension->CAP = StorPortReadRegisterUlong(adapterExtension, &abar->CAP);
1031 adapterExtension->CAP2 = StorPortReadRegisterUlong(adapterExtension, &abar->CAP2);
1032 adapterExtension->Version = StorPortReadRegisterUlong(adapterExtension, &abar->VS);
1033 adapterExtension->LastInterruptPort = (ULONG)-1;
1034
1035 // 10.1.2
1036 // 1. Indicate that system software is AHCI aware by setting GHC.AE to ‘1’.
1037 // 3.1.2 -- AE bit is read-write only if CAP.SAM is '0'
1038 ghc.Status = StorPortReadRegisterUlong(adapterExtension, &abar->GHC);
1039 // AE := Highest Significant bit of GHC
1040 if (ghc.AE != 0)// Hmm, controller was already in power state
1041 {
1042 // reset controller to have it in known state
1043 AhciDebugPrint("\tAE Already set, Reset()\n");
1044 if (!AhciAdapterReset(adapterExtension))
1045 {
1046 AhciDebugPrint("\tReset Failed!\n");
1047 return SP_RETURN_ERROR;// reset failed
1048 }
1049 }
1050
1051 ghc.Status = 0;
1052 ghc.AE = 1;// only AE=1
1053 // tell the controller that we know about AHCI
1054 StorPortWriteRegisterUlong(adapterExtension, &abar->GHC, ghc.Status);
1055
1056 adapterExtension->IS = &abar->IS;
1057 adapterExtension->PortImplemented = StorPortReadRegisterUlong(adapterExtension, &abar->PI);
1058
1059 if (adapterExtension->PortImplemented == 0)
1060 {
1061 AhciDebugPrint("\tadapterExtension->PortImplemented == 0\n");
1062 return SP_RETURN_ERROR;
1063 }
1064
1065 ConfigInfo->Master = TRUE;
1066 ConfigInfo->AlignmentMask = 0x3;
1067 ConfigInfo->ScatterGather = TRUE;
1068 ConfigInfo->DmaWidth = Width32Bits;
1069 ConfigInfo->WmiDataProvider = FALSE;
1070 ConfigInfo->Dma32BitAddresses = TRUE;
1071
1072 if (IsAdapterCAPS64(adapterExtension->CAP))
1073 {
1074 ConfigInfo->Dma64BitAddresses = TRUE;
1075 }
1076
1077 ConfigInfo->MaximumNumberOfTargets = 1;
1078 ConfigInfo->ResetTargetSupported = TRUE;
1079 ConfigInfo->NumberOfPhysicalBreaks = 0x21;
1080 ConfigInfo->MaximumNumberOfLogicalUnits = 1;
1081 ConfigInfo->NumberOfBuses = MAXIMUM_AHCI_PORT_COUNT;
1082 ConfigInfo->MaximumTransferLength = MAXIMUM_TRANSFER_LENGTH;
1083 ConfigInfo->SynchronizationModel = StorSynchronizeFullDuplex;
1084
1085 // Turn IE -- Interrupt Enabled
1086 ghc.Status = StorPortReadRegisterUlong(adapterExtension, &abar->GHC);
1087 ghc.IE = 1;
1088 StorPortWriteRegisterUlong(adapterExtension, &abar->GHC, ghc.Status);
1089
1090 // allocate necessary resource for each port
1091 if (!AhciAllocateResourceForAdapter(adapterExtension, ConfigInfo))
1092 {
1093 NT_ASSERT(FALSE);
1094 return SP_RETURN_ERROR;
1095 }
1096
1097 for (index = 0; index < adapterExtension->PortCount; index++)
1098 {
1099 if ((adapterExtension->PortImplemented & (0x1 << index)) != 0)
1100 AhciPortInitialize(&adapterExtension->PortExtension[index]);
1101 }
1102
1103 return SP_RETURN_FOUND;
1104 }// -- AhciHwFindAdapter();
1105
1106 /**
1107 * @name DriverEntry
1108 * @implemented
1109 *
1110 * Initial Entrypoint for storahci miniport driver
1111 *
1112 * @param DriverObject
1113 * @param RegistryPath
1114 *
1115 * @return
1116 * NT_STATUS in case of driver loaded successfully.
1117 */
1118 ULONG
1119 DriverEntry (
1120 __in PVOID DriverObject,
1121 __in PVOID RegistryPath
1122 )
1123 {
1124 ULONG status;
1125 // initialize the hardware data structure
1126 HW_INITIALIZATION_DATA hwInitializationData = {0};
1127
1128 // set size of hardware initialization structure
1129 hwInitializationData.HwInitializationDataSize = sizeof(HW_INITIALIZATION_DATA);
1130
1131 // identity required miniport entry point routines
1132 hwInitializationData.HwStartIo = AhciHwStartIo;
1133 hwInitializationData.HwResetBus = AhciHwResetBus;
1134 hwInitializationData.HwInterrupt = AhciHwInterrupt;
1135 hwInitializationData.HwInitialize = AhciHwInitialize;
1136 hwInitializationData.HwFindAdapter = AhciHwFindAdapter;
1137
1138 // adapter specific information
1139 hwInitializationData.TaggedQueuing = TRUE;
1140 hwInitializationData.AutoRequestSense = TRUE;
1141 hwInitializationData.MultipleRequestPerLu = TRUE;
1142 hwInitializationData.NeedPhysicalAddresses = TRUE;
1143
1144 hwInitializationData.NumberOfAccessRanges = 6;
1145 hwInitializationData.AdapterInterfaceType = PCIBus;
1146 hwInitializationData.MapBuffers = STOR_MAP_NON_READ_WRITE_BUFFERS;
1147
1148 // set required extension sizes
1149 hwInitializationData.SrbExtensionSize = sizeof(AHCI_SRB_EXTENSION);
1150 hwInitializationData.DeviceExtensionSize = sizeof(AHCI_ADAPTER_EXTENSION);
1151
1152 // register our hw init data
1153 status = StorPortInitialize(DriverObject,
1154 RegistryPath,
1155 &hwInitializationData,
1156 NULL);
1157
1158 NT_ASSERT(status == STATUS_SUCCESS);
1159 return status;
1160 }// -- DriverEntry();
1161
1162 /**
1163 * @name AhciATA_CFIS
1164 * @implemented
1165 *
1166 * create ATA CFIS from Srb
1167 *
1168 * @param PortExtension
1169 * @param Srb
1170 *
1171 * @return
1172 * Number of CFIS fields used in DWORD
1173 */
1174 ULONG
1175 AhciATA_CFIS (
1176 __in PAHCI_PORT_EXTENSION PortExtension,
1177 __in PAHCI_SRB_EXTENSION SrbExtension
1178 )
1179 {
1180 PAHCI_COMMAND_TABLE cmdTable;
1181
1182 UNREFERENCED_PARAMETER(PortExtension);
1183
1184 AhciDebugPrint("AhciATA_CFIS()\n");
1185
1186 cmdTable = (PAHCI_COMMAND_TABLE)SrbExtension;
1187
1188 AhciZeroMemory((PCHAR)cmdTable->CFIS, sizeof(cmdTable->CFIS));
1189
1190 cmdTable->CFIS[AHCI_ATA_CFIS_FisType] = FIS_TYPE_REG_H2D; // FIS Type
1191 cmdTable->CFIS[AHCI_ATA_CFIS_PMPort_C] = (1 << 7); // PM Port & C
1192 cmdTable->CFIS[AHCI_ATA_CFIS_CommandReg] = SrbExtension->CommandReg;
1193
1194 cmdTable->CFIS[AHCI_ATA_CFIS_FeaturesLow] = SrbExtension->FeaturesLow;
1195 cmdTable->CFIS[AHCI_ATA_CFIS_LBA0] = SrbExtension->LBA0;
1196 cmdTable->CFIS[AHCI_ATA_CFIS_LBA1] = SrbExtension->LBA1;
1197 cmdTable->CFIS[AHCI_ATA_CFIS_LBA2] = SrbExtension->LBA2;
1198 cmdTable->CFIS[AHCI_ATA_CFIS_Device] = SrbExtension->Device;
1199 cmdTable->CFIS[AHCI_ATA_CFIS_LBA3] = SrbExtension->LBA3;
1200 cmdTable->CFIS[AHCI_ATA_CFIS_LBA4] = SrbExtension->LBA4;
1201 cmdTable->CFIS[AHCI_ATA_CFIS_LBA5] = SrbExtension->LBA5;
1202 cmdTable->CFIS[AHCI_ATA_CFIS_FeaturesHigh] = SrbExtension->FeaturesHigh;
1203 cmdTable->CFIS[AHCI_ATA_CFIS_SectorCountLow] = SrbExtension->SectorCountLow;
1204 cmdTable->CFIS[AHCI_ATA_CFIS_SectorCountHigh] = SrbExtension->SectorCountHigh;
1205
1206 return 5;
1207 }// -- AhciATA_CFIS();
1208
1209 /**
1210 * @name AhciATAPI_CFIS
1211 * @not_implemented
1212 *
1213 * create ATAPI CFIS from Srb
1214 *
1215 * @param PortExtension
1216 * @param Srb
1217 *
1218 * @return
1219 * Number of CFIS fields used in DWORD
1220 */
1221 ULONG
1222 AhciATAPI_CFIS (
1223 __in PAHCI_PORT_EXTENSION PortExtension,
1224 __in PAHCI_SRB_EXTENSION SrbExtension
1225 )
1226 {
1227 UNREFERENCED_PARAMETER(PortExtension);
1228 UNREFERENCED_PARAMETER(SrbExtension);
1229
1230 AhciDebugPrint("AhciATAPI_CFIS()\n");
1231
1232 return 2;
1233 }// -- AhciATAPI_CFIS();
1234
1235 /**
1236 * @name AhciBuild_PRDT
1237 * @implemented
1238 *
1239 * Build PRDT for data transfer
1240 *
1241 * @param PortExtension
1242 * @param Srb
1243 *
1244 * @return
1245 * Return number of entries in PRDT.
1246 */
1247 ULONG
1248 AhciBuild_PRDT (
1249 __in PAHCI_PORT_EXTENSION PortExtension,
1250 __in PAHCI_SRB_EXTENSION SrbExtension
1251 )
1252 {
1253 ULONG index;
1254 PAHCI_COMMAND_TABLE cmdTable;
1255 PLOCAL_SCATTER_GATHER_LIST sgl;
1256 PAHCI_ADAPTER_EXTENSION AdapterExtension;
1257
1258 AhciDebugPrint("AhciBuild_PRDT()\n");
1259
1260 sgl = SrbExtension->pSgl;
1261 cmdTable = (PAHCI_COMMAND_TABLE)SrbExtension;
1262 AdapterExtension = PortExtension->AdapterExtension;
1263
1264 NT_ASSERT(sgl != NULL);
1265 NT_ASSERT(sgl->NumberOfElements < MAXIMUM_AHCI_PRDT_ENTRIES);
1266
1267 for (index = 0; index < sgl->NumberOfElements; index++)
1268 {
1269 NT_ASSERT(sgl->List[index].Length <= MAXIMUM_TRANSFER_LENGTH);
1270
1271 cmdTable->PRDT[index].DBA = sgl->List[index].PhysicalAddress.LowPart;
1272 if (IsAdapterCAPS64(AdapterExtension->CAP))
1273 {
1274 cmdTable->PRDT[index].DBAU = sgl->List[index].PhysicalAddress.HighPart;
1275 }
1276
1277 // Data Byte Count (DBC): A ‘0’ based value that Indicates the length, in bytes, of the data block.
1278 // A maximum of length of 4MB may exist for any entry. Bit ‘0’ of this field must always be ‘1’ to
1279 // indicate an even byte count. A value of ‘1’ indicates 2 bytes, ‘3’ indicates 4 bytes, etc.
1280 cmdTable->PRDT[index].DBC = sgl->List[index].Length - 1;
1281 }
1282
1283 return sgl->NumberOfElements;
1284 }// -- AhciBuild_PRDT();
1285
1286 /**
1287 * @name AhciProcessSrb
1288 * @implemented
1289 *
1290 * Prepare Srb for IO processing
1291 *
1292 * @param PortExtension
1293 * @param Srb
1294 * @param SlotIndex
1295 *
1296 */
1297 VOID
1298 AhciProcessSrb (
1299 __in PAHCI_PORT_EXTENSION PortExtension,
1300 __in PSCSI_REQUEST_BLOCK Srb,
1301 __in ULONG SlotIndex
1302 )
1303 {
1304 ULONG prdtlen, sig, length, cfl;
1305 PAHCI_SRB_EXTENSION SrbExtension;
1306 PAHCI_COMMAND_HEADER CommandHeader;
1307 PAHCI_ADAPTER_EXTENSION AdapterExtension;
1308 STOR_PHYSICAL_ADDRESS CommandTablePhysicalAddress;
1309
1310 AhciDebugPrint("AhciProcessSrb()\n");
1311
1312 NT_ASSERT(Srb->PathId == PortExtension->PortNumber);
1313
1314 SrbExtension = GetSrbExtension(Srb);
1315 AdapterExtension = PortExtension->AdapterExtension;
1316
1317 NT_ASSERT(SrbExtension != NULL);
1318 NT_ASSERT(SrbExtension->AtaFunction != 0);
1319
1320 if ((SrbExtension->AtaFunction == ATA_FUNCTION_ATA_IDENTIFY) &&
1321 (SrbExtension->CommandReg == IDE_COMMAND_NOT_VALID))
1322 {
1323 // Here we are safe to check SIG register
1324 sig = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->SIG);
1325 if (sig == 0x101)
1326 {
1327 AhciDebugPrint("\tATA Device Found!\n");
1328 SrbExtension->CommandReg = IDE_COMMAND_IDENTIFY;
1329 }
1330 else
1331 {
1332 AhciDebugPrint("\tATAPI Device Found!\n");
1333 SrbExtension->CommandReg = IDE_COMMAND_ATAPI_IDENTIFY;
1334 }
1335 }
1336
1337 NT_ASSERT(SlotIndex < AHCI_Global_Port_CAP_NCS(AdapterExtension->CAP));
1338 SrbExtension->SlotIndex = SlotIndex;
1339
1340 // program the CFIS in the CommandTable
1341 CommandHeader = &PortExtension->CommandList[SlotIndex];
1342
1343 cfl = 0;
1344 if (IsAtapiCommand(SrbExtension->AtaFunction))
1345 {
1346 cfl = AhciATAPI_CFIS(PortExtension, SrbExtension);
1347 }
1348 else if (IsAtaCommand(SrbExtension->AtaFunction))
1349 {
1350 cfl = AhciATA_CFIS(PortExtension, SrbExtension);
1351 }
1352 else
1353 {
1354 NT_ASSERT(FALSE);
1355 }
1356
1357 prdtlen = 0;
1358 if (IsDataTransferNeeded(SrbExtension))
1359 {
1360 prdtlen = AhciBuild_PRDT(PortExtension, SrbExtension);
1361 NT_ASSERT(prdtlen != -1);
1362 }
1363
1364 // Program the command header
1365 CommandHeader->DI.PRDTL = prdtlen; // number of entries in PRD table
1366 CommandHeader->DI.CFL = cfl;
1367 CommandHeader->DI.A = (SrbExtension->AtaFunction & ATA_FUNCTION_ATAPI_COMMAND) ? 1 : 0;
1368 CommandHeader->DI.W = (SrbExtension->Flags & ATA_FLAGS_DATA_OUT) ? 1 : 0;
1369 CommandHeader->DI.P = 0; // ATA Specifications says so
1370 CommandHeader->DI.PMP = 0; // Port Multiplier
1371
1372 // Reset -- Manual Configuation
1373 CommandHeader->DI.R = 0;
1374 CommandHeader->DI.B = 0;
1375 CommandHeader->DI.C = 0;
1376
1377 CommandHeader->PRDBC = 0;
1378
1379 CommandHeader->Reserved[0] = 0;
1380 CommandHeader->Reserved[1] = 0;
1381 CommandHeader->Reserved[2] = 0;
1382 CommandHeader->Reserved[3] = 0;
1383
1384 // set CommandHeader CTBA
1385 CommandTablePhysicalAddress = StorPortGetPhysicalAddress(AdapterExtension,
1386 NULL,
1387 SrbExtension,
1388 &length);
1389
1390 NT_ASSERT(length != 0);
1391
1392 // command table alignment
1393 NT_ASSERT((CommandTablePhysicalAddress.LowPart % 128) == 0);
1394
1395 CommandHeader->CTBA = CommandTablePhysicalAddress.LowPart;
1396
1397 if (IsAdapterCAPS64(AdapterExtension->CAP))
1398 {
1399 CommandHeader->CTBA_U = CommandTablePhysicalAddress.HighPart;
1400 }
1401
1402 // mark this slot
1403 PortExtension->Slot[SlotIndex] = Srb;
1404 PortExtension->QueueSlots |= 1 << SlotIndex;
1405 return;
1406 }// -- AhciProcessSrb();
1407
1408 /**
1409 * @name AhciActivatePort
1410 * @implemented
1411 *
1412 * Program Port and populate command list
1413 *
1414 * @param PortExtension
1415 *
1416 */
1417 VOID
1418 AhciActivatePort (
1419 __in PAHCI_PORT_EXTENSION PortExtension
1420 )
1421 {
1422 AHCI_PORT_CMD cmd;
1423 ULONG QueueSlots, slotToActivate, tmp;
1424 PAHCI_ADAPTER_EXTENSION AdapterExtension;
1425
1426 AhciDebugPrint("AhciActivatePort()\n");
1427
1428 AdapterExtension = PortExtension->AdapterExtension;
1429 QueueSlots = PortExtension->QueueSlots;
1430
1431 if (QueueSlots == 0)
1432 {
1433 return;
1434 }
1435
1436 // section 3.3.14
1437 // Bits in this field shall only be set to ‘1’ by software when PxCMD.ST is set to ‘1’
1438 cmd.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->CMD);
1439
1440 if (cmd.ST == 0) // PxCMD.ST == 0
1441 {
1442 return;
1443 }
1444
1445 // get the lowest set bit
1446 tmp = QueueSlots & (QueueSlots - 1);
1447
1448 if (tmp == 0)
1449 slotToActivate = QueueSlots;
1450 else
1451 slotToActivate = (QueueSlots & (~tmp));
1452
1453 // mark that bit off in QueueSlots
1454 // so we can know we it is really needed to activate port or not
1455 PortExtension->QueueSlots &= ~slotToActivate;
1456 // mark this CommandIssuedSlots
1457 // to validate in completeIssuedCommand
1458 PortExtension->CommandIssuedSlots |= slotToActivate;
1459
1460 // tell the HBA to issue this Command Slot to the given port
1461 StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->CI, slotToActivate);
1462
1463 return;
1464 }// -- AhciActivatePort();
1465
1466 /**
1467 * @name AhciProcessIO
1468 * @implemented
1469 *
1470 * Acquire Exclusive lock to port, populate pending commands to command List
1471 * program controller's port to process new commands in command list.
1472 *
1473 * @param AdapterExtension
1474 * @param PathId
1475 * @param Srb
1476 *
1477 */
1478 VOID
1479 AhciProcessIO (
1480 __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
1481 __in UCHAR PathId,
1482 __in PSCSI_REQUEST_BLOCK Srb
1483 )
1484 {
1485 PSCSI_REQUEST_BLOCK tmpSrb;
1486 STOR_LOCK_HANDLE lockhandle = {0};
1487 PAHCI_PORT_EXTENSION PortExtension;
1488 ULONG commandSlotMask, occupiedSlots, slotIndex, NCS;
1489
1490 AhciDebugPrint("AhciProcessIO()\n");
1491 AhciDebugPrint("\tPathId: %d\n", PathId);
1492
1493 PortExtension = &AdapterExtension->PortExtension[PathId];
1494
1495 NT_ASSERT(PathId < AdapterExtension->PortCount);
1496
1497 // Acquire Lock
1498 StorPortAcquireSpinLock(AdapterExtension, InterruptLock, NULL, &lockhandle);
1499
1500 // add Srb to queue
1501 AddQueue(&PortExtension->SrbQueue, Srb);
1502
1503 if (PortExtension->DeviceParams.IsActive == FALSE)
1504 {
1505 // Release Lock
1506 StorPortReleaseSpinLock(AdapterExtension, &lockhandle);
1507 return; // we should wait for device to get active
1508 }
1509
1510 occupiedSlots = (PortExtension->QueueSlots | PortExtension->CommandIssuedSlots); // Busy command slots for given port
1511 NCS = AHCI_Global_Port_CAP_NCS(AdapterExtension->CAP);
1512 commandSlotMask = (1 << NCS) - 1; // available slots mask
1513
1514 commandSlotMask = (commandSlotMask & ~occupiedSlots);
1515 if(commandSlotMask != 0)
1516 {
1517 // iterate over HBA port slots
1518 for (slotIndex = 0; slotIndex < NCS; slotIndex++)
1519 {
1520 // find first free slot
1521 if ((commandSlotMask & (1 << slotIndex)) != 0)
1522 {
1523 tmpSrb = RemoveQueue(&PortExtension->SrbQueue);
1524 if (tmpSrb != NULL)
1525 {
1526 NT_ASSERT(tmpSrb->PathId == PathId);
1527 AhciProcessSrb(PortExtension, tmpSrb, slotIndex);
1528 }
1529 else
1530 {
1531 break;
1532 }
1533 }
1534 else
1535 {
1536 break;
1537 }
1538 }
1539 }
1540
1541 // program HBA port
1542 AhciActivatePort(PortExtension);
1543
1544 // Release Lock
1545 StorPortReleaseSpinLock(AdapterExtension, &lockhandle);
1546
1547 return;
1548 }// -- AhciProcessIO();
1549
1550 /**
1551 * @name InquiryCompletion
1552 * @not_implemented
1553 *
1554 * InquiryCompletion routine should be called after device signals
1555 * for device inquiry request is completed (through interrupt)
1556 *
1557 * @param PortExtension
1558 * @param Srb
1559 *
1560 */
1561 VOID
1562 InquiryCompletion (
1563 __in PAHCI_PORT_EXTENSION PortExtension,
1564 __in PSCSI_REQUEST_BLOCK Srb
1565 )
1566 {
1567 PCDB cdb;
1568 BOOLEAN status;
1569 PINQUIRYDATA InquiryData;
1570 PAHCI_SRB_EXTENSION SrbExtension;
1571 PAHCI_ADAPTER_EXTENSION AdapterExtension;
1572 PIDENTIFY_DEVICE_DATA IdentifyDeviceData;
1573
1574 AhciDebugPrint("InquiryCompletion()\n");
1575
1576 NT_ASSERT(Srb != NULL);
1577 NT_ASSERT(PortExtension != NULL);
1578
1579 cdb = (PCDB)&Srb->Cdb;
1580 InquiryData = Srb->DataBuffer;
1581 SrbExtension = GetSrbExtension(Srb);
1582 AdapterExtension = PortExtension->AdapterExtension;
1583 IdentifyDeviceData = PortExtension->IdentifyDeviceData;
1584
1585 if (Srb->SrbStatus != SRB_STATUS_SUCCESS)
1586 {
1587 if (Srb->SrbStatus == SRB_STATUS_NO_DEVICE)
1588 {
1589 PortExtension->DeviceParams.DeviceType = AHCI_DEVICE_TYPE_NODEVICE;
1590 }
1591 return;
1592 }
1593
1594 NT_ASSERT(InquiryData != NULL);
1595 NT_ASSERT(Srb->SrbStatus == SRB_STATUS_SUCCESS);
1596
1597 // Device specific data
1598 PortExtension->DeviceParams.MaxLba.QuadPart = 0;
1599
1600 if (SrbExtension->CommandReg == IDE_COMMAND_IDENTIFY)
1601 {
1602 PortExtension->DeviceParams.DeviceType = AHCI_DEVICE_TYPE_ATA;
1603 if (IdentifyDeviceData->GeneralConfiguration.RemovableMedia)
1604 {
1605 PortExtension->DeviceParams.RemovableDevice = 1;
1606 }
1607
1608 if ((IdentifyDeviceData->CommandSetSupport.BigLba) && (IdentifyDeviceData->CommandSetActive.BigLba))
1609 {
1610 PortExtension->DeviceParams.Lba48BitMode = 1;
1611 }
1612
1613 PortExtension->DeviceParams.AccessType = DIRECT_ACCESS_DEVICE;
1614
1615 /* Device max address lba */
1616 if (PortExtension->DeviceParams.Lba48BitMode)
1617 {
1618 PortExtension->DeviceParams.MaxLba.LowPart = IdentifyDeviceData->Max48BitLBA[0];
1619 PortExtension->DeviceParams.MaxLba.HighPart = IdentifyDeviceData->Max48BitLBA[1];
1620 }
1621 else
1622 {
1623 PortExtension->DeviceParams.MaxLba.LowPart = IdentifyDeviceData->UserAddressableSectors;
1624 }
1625
1626 /* Bytes Per Logical Sector */
1627 if (IdentifyDeviceData->PhysicalLogicalSectorSize.LogicalSectorLongerThan256Words)
1628 {
1629 AhciDebugPrint("\tBytesPerLogicalSector != DEVICE_ATA_BLOCK_SIZE\n");
1630 NT_ASSERT(FALSE);
1631 }
1632
1633 PortExtension->DeviceParams.BytesPerLogicalSector = DEVICE_ATA_BLOCK_SIZE;
1634
1635 /* Bytes Per Physical Sector */
1636 if (IdentifyDeviceData->PhysicalLogicalSectorSize.MultipleLogicalSectorsPerPhysicalSector)
1637 {
1638 AhciDebugPrint("\tBytesPerPhysicalSector != DEVICE_ATA_BLOCK_SIZE\n");
1639 NT_ASSERT(FALSE);
1640 }
1641
1642 PortExtension->DeviceParams.BytesPerPhysicalSector = DEVICE_ATA_BLOCK_SIZE;
1643
1644 // TODO: Add other device params
1645 AhciDebugPrint("\tATA Device\n");
1646 }
1647 else
1648 {
1649 AhciDebugPrint("\tATAPI Device\n");
1650 PortExtension->DeviceParams.DeviceType = AHCI_DEVICE_TYPE_ATAPI;
1651 PortExtension->DeviceParams.AccessType = READ_ONLY_DIRECT_ACCESS_DEVICE;
1652 }
1653
1654 // INQUIRYDATABUFFERSIZE = 36 ; Defined in storport.h
1655 if (Srb->DataTransferLength < INQUIRYDATABUFFERSIZE)
1656 {
1657 AhciDebugPrint("\tDataBufferLength < sizeof(INQUIRYDATA), Could crash the driver.\n");
1658 }
1659
1660 // update data transfer length
1661 Srb->DataTransferLength = INQUIRYDATABUFFERSIZE;
1662
1663 // prepare data to send
1664 InquiryData->Versions = 2;
1665 InquiryData->Wide32Bit = 1;
1666 InquiryData->CommandQueue = 0; // NCQ not supported
1667 InquiryData->ResponseDataFormat = 0x2;
1668 InquiryData->DeviceTypeModifier = 0;
1669 InquiryData->DeviceTypeQualifier = DEVICE_CONNECTED;
1670 InquiryData->AdditionalLength = INQUIRYDATABUFFERSIZE - 5;
1671 InquiryData->DeviceType = PortExtension->DeviceParams.AccessType;
1672 InquiryData->RemovableMedia = PortExtension->DeviceParams.RemovableDevice;
1673
1674 // TODO: Fill VendorID, Product Revision Level and other string fields
1675 InquiryData->VendorId[0] = '2';
1676 InquiryData->ProductId[0] = '3';
1677 InquiryData->ProductRevisionLevel[0] = '4';
1678
1679 // send queue depth
1680 status = StorPortSetDeviceQueueDepth(PortExtension->AdapterExtension,
1681 Srb->PathId,
1682 Srb->TargetId,
1683 Srb->Lun,
1684 AHCI_Global_Port_CAP_NCS(AdapterExtension->CAP));
1685
1686 NT_ASSERT(status == TRUE);
1687 return;
1688 }// -- InquiryCompletion();
1689
1690 /**
1691 * @name DeviceRequestReadWrite
1692 * @implemented
1693 *
1694 * Handle SCSIOP_READ SCSIOP_WRITE OperationCode
1695 *
1696 * @param AdapterExtension
1697 * @param Srb
1698 * @param Cdb
1699 *
1700 * @return
1701 * return STOR status for DeviceReportLuns
1702 */
1703 UCHAR DeviceRequestReadWrite (
1704 __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
1705 __in PSCSI_REQUEST_BLOCK Srb,
1706 __in PCDB Cdb
1707 )
1708 {
1709 BOOLEAN IsReading;
1710 ULONG64 StartOffset;
1711 PAHCI_SRB_EXTENSION SrbExtension;
1712 PAHCI_PORT_EXTENSION PortExtension;
1713 ULONG DataTransferLength, BytesPerSector, SectorCount;
1714
1715 AhciDebugPrint("DeviceRequestReadWrite()\n");
1716
1717 NT_ASSERT(IsPortValid(AdapterExtension, Srb->PathId));
1718 NT_ASSERT((Cdb->CDB10.OperationCode == SCSIOP_READ) || (Cdb->CDB10.OperationCode == SCSIOP_WRITE));
1719
1720 SrbExtension = GetSrbExtension(Srb);
1721 PortExtension = &AdapterExtension->PortExtension[Srb->PathId];
1722
1723 DataTransferLength = Srb->DataTransferLength;
1724 BytesPerSector = PortExtension->DeviceParams.BytesPerLogicalSector;
1725
1726 NT_ASSERT(BytesPerSector > 0);
1727
1728 ROUND_UP(DataTransferLength, BytesPerSector);
1729
1730 SectorCount = DataTransferLength / BytesPerSector;
1731 StartOffset = AhciGetLba(Cdb, Srb->CdbLength);
1732 IsReading = (Cdb->CDB10.OperationCode == SCSIOP_READ);
1733
1734 NT_ASSERT(SectorCount > 0);
1735
1736 SrbExtension->AtaFunction = ATA_FUNCTION_ATA_READ;
1737 SrbExtension->Flags |= ATA_FLAGS_USE_DMA;
1738 SrbExtension->CompletionRoutine = NULL;
1739
1740 if (IsReading)
1741 {
1742 SrbExtension->Flags |= ATA_FLAGS_DATA_IN;
1743 SrbExtension->CommandReg = IDE_COMMAND_READ_DMA;
1744 }
1745 else
1746 {
1747 SrbExtension->Flags |= ATA_FLAGS_DATA_OUT;
1748 NT_ASSERT(FALSE);
1749 }
1750
1751 SrbExtension->FeaturesLow = 0;
1752 SrbExtension->LBA0 = (StartOffset >> 0) & 0xFF;
1753 SrbExtension->LBA1 = (StartOffset >> 8) & 0xFF;
1754 SrbExtension->LBA2 = (StartOffset >> 16) & 0xFF;
1755
1756 SrbExtension->Device = (0xA0 | IDE_LBA_MODE);
1757
1758 if (PortExtension->DeviceParams.Lba48BitMode)
1759 {
1760 SrbExtension->Flags |= ATA_FLAGS_48BIT_COMMAND;
1761 SrbExtension->CommandReg = IDE_COMMAND_READ_DMA_EXT;
1762
1763 SrbExtension->LBA3 = (StartOffset >> 24) & 0xFF;
1764 SrbExtension->LBA4 = (StartOffset >> 32) & 0xFF;
1765 SrbExtension->LBA5 = (StartOffset >> 40) & 0xFF;
1766 }
1767 else
1768 {
1769 NT_ASSERT(FALSE);
1770 }
1771
1772 SrbExtension->FeaturesHigh = 0;
1773 SrbExtension->SectorCountLow = (SectorCount >> 0) & 0xFF;
1774 SrbExtension->SectorCountHigh = (SectorCount >> 8) & 0xFF;
1775
1776 NT_ASSERT(SectorCount < 0x100);
1777
1778 SrbExtension->pSgl = (PLOCAL_SCATTER_GATHER_LIST)StorPortGetScatterGatherList(AdapterExtension, Srb);
1779
1780 return SRB_STATUS_PENDING;
1781 }// -- DeviceRequestReadWrite();
1782
1783 /**
1784 * @name DeviceRequestCapacity
1785 * @implemented
1786 *
1787 * Handle SCSIOP_READ_CAPACITY OperationCode
1788 *
1789 * @param AdapterExtension
1790 * @param Srb
1791 * @param Cdb
1792 *
1793 * @return
1794 * return STOR status for DeviceReportLuns
1795 */
1796 UCHAR DeviceRequestCapacity (
1797 __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
1798 __in PSCSI_REQUEST_BLOCK Srb,
1799 __in PCDB Cdb
1800 )
1801 {
1802 ULONG MaxLba, BytesPerLogicalSector;
1803 PREAD_CAPACITY_DATA ReadCapacity;
1804 PAHCI_PORT_EXTENSION PortExtension;
1805
1806 AhciDebugPrint("DeviceRequestCapacity()\n");
1807
1808 UNREFERENCED_PARAMETER(AdapterExtension);
1809 UNREFERENCED_PARAMETER(Cdb);
1810
1811 NT_ASSERT(Srb->DataBuffer != NULL);
1812 NT_ASSERT(IsPortValid(AdapterExtension, Srb->PathId));
1813
1814 PortExtension = &AdapterExtension->PortExtension[Srb->PathId];
1815
1816 if (Cdb->CDB10.OperationCode == SCSIOP_READ_CAPACITY)
1817 {
1818 ReadCapacity = (PREAD_CAPACITY_DATA)Srb->DataBuffer;
1819
1820
1821 BytesPerLogicalSector = PortExtension->DeviceParams.BytesPerLogicalSector;
1822 MaxLba = (ULONG)PortExtension->DeviceParams.MaxLba.QuadPart;
1823
1824 // I trust you windows :D
1825 NT_ASSERT(Srb->DataTransferLength >= sizeof(READ_CAPACITY_DATA));
1826
1827 // I trust you user :D
1828 NT_ASSERT(PortExtension->DeviceParams.MaxLba.QuadPart < (ULONG)-1);
1829
1830 // Actually I don't trust anyone :p
1831 Srb->DataTransferLength = sizeof(READ_CAPACITY_DATA);
1832
1833 REVERSE_BYTES(&ReadCapacity->BytesPerBlock, &BytesPerLogicalSector);
1834 REVERSE_BYTES(&ReadCapacity->LogicalBlockAddress, &MaxLba);
1835 }
1836 else
1837 {
1838 AhciDebugPrint("\tSCSIOP_READ_CAPACITY16 not supported\n");
1839 NT_ASSERT(FALSE);
1840 }
1841
1842 return SRB_STATUS_SUCCESS;
1843 }// -- DeviceRequestCapacity();
1844
1845 /**
1846 * @name DeviceRequestComplete
1847 * @implemented
1848 *
1849 * Handle UnHandled Requests
1850 *
1851 * @param AdapterExtension
1852 * @param Srb
1853 * @param Cdb
1854 *
1855 * @return
1856 * return STOR status for DeviceRequestComplete
1857 */
1858 UCHAR DeviceRequestComplete (
1859 __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
1860 __in PSCSI_REQUEST_BLOCK Srb,
1861 __in PCDB Cdb
1862 )
1863 {
1864 AhciDebugPrint("DeviceRequestComplete()\n");
1865
1866 UNREFERENCED_PARAMETER(AdapterExtension);
1867 UNREFERENCED_PARAMETER(Cdb);
1868
1869 Srb->ScsiStatus = SCSISTAT_GOOD;
1870
1871 return SRB_STATUS_SUCCESS;
1872 }// -- DeviceRequestComplete();
1873
1874 /**
1875 * @name DeviceReportLuns
1876 * @implemented
1877 *
1878 * Handle SCSIOP_REPORT_LUNS OperationCode
1879 *
1880 * @param AdapterExtension
1881 * @param Srb
1882 * @param Cdb
1883 *
1884 * @return
1885 * return STOR status for DeviceReportLuns
1886 */
1887 UCHAR DeviceReportLuns (
1888 __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
1889 __in PSCSI_REQUEST_BLOCK Srb,
1890 __in PCDB Cdb
1891 )
1892 {
1893 PLUN_LIST LunList;
1894
1895 AhciDebugPrint("DeviceReportLuns()\n");
1896
1897 UNREFERENCED_PARAMETER(AdapterExtension);
1898 UNREFERENCED_PARAMETER(Cdb);
1899
1900 NT_ASSERT(Srb->DataTransferLength >= sizeof(LUN_LIST));
1901 NT_ASSERT(Cdb->CDB10.OperationCode == SCSIOP_REPORT_LUNS);
1902
1903 LunList = (PLUN_LIST)Srb->DataBuffer;
1904
1905 NT_ASSERT(LunList != NULL);
1906
1907 AhciZeroMemory((PCHAR)LunList, sizeof(LUN_LIST));
1908
1909 LunList->LunListLength[3] = 8;
1910
1911 Srb->ScsiStatus = SCSISTAT_GOOD;
1912 Srb->DataTransferLength = sizeof(LUN_LIST);
1913
1914 return SRB_STATUS_SUCCESS;
1915 }// -- DeviceReportLuns();
1916
1917 /**
1918 * @name DeviceInquiryRequest
1919 * @implemented
1920 *
1921 * Tells wheather given port is implemented or not
1922 *
1923 * @param AdapterExtension
1924 * @param Srb
1925 * @param Cdb
1926 *
1927 * @return
1928 * return STOR status for DeviceInquiryRequest
1929 *
1930 * @remark
1931 * http://www.seagate.com/staticfiles/support/disc/manuals/Interface%20manuals/100293068c.pdf
1932 */
1933 UCHAR
1934 DeviceInquiryRequest (
1935 __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
1936 __in PSCSI_REQUEST_BLOCK Srb,
1937 __in PCDB Cdb
1938 )
1939 {
1940 PVOID DataBuffer;
1941 PAHCI_SRB_EXTENSION SrbExtension;
1942 PAHCI_PORT_EXTENSION PortExtension;
1943 PVPD_SUPPORTED_PAGES_PAGE VpdOutputBuffer;
1944 ULONG DataBufferLength, RequiredDataBufferLength;
1945
1946 AhciDebugPrint("DeviceInquiryRequest()\n");
1947
1948 NT_ASSERT(Cdb->CDB10.OperationCode == SCSIOP_INQUIRY);
1949 NT_ASSERT(IsPortValid(AdapterExtension, Srb->PathId));
1950
1951 SrbExtension = GetSrbExtension(Srb);
1952 PortExtension = &AdapterExtension->PortExtension[Srb->PathId];
1953
1954 if (Srb->Lun != 0)
1955 {
1956 return SRB_STATUS_SELECTION_TIMEOUT;
1957 }
1958 else if (Cdb->CDB6INQUIRY3.EnableVitalProductData == 0)
1959 {
1960 // 3.6.1
1961 // If the EVPD bit is set to zero, the device server shall return the standard INQUIRY data
1962 AhciDebugPrint("\tEVPD Inquired\n");
1963 NT_ASSERT(SrbExtension != NULL);
1964
1965 SrbExtension->AtaFunction = ATA_FUNCTION_ATA_IDENTIFY;
1966 SrbExtension->Flags |= ATA_FLAGS_DATA_IN;
1967 SrbExtension->CompletionRoutine = InquiryCompletion;
1968 SrbExtension->CommandReg = IDE_COMMAND_NOT_VALID;
1969
1970 // TODO: Should use AhciZeroMemory
1971 SrbExtension->FeaturesLow = 0;
1972 SrbExtension->LBA0 = 0;
1973 SrbExtension->LBA1 = 0;
1974 SrbExtension->LBA2 = 0;
1975 SrbExtension->Device = 0xA0;
1976 SrbExtension->LBA3 = 0;
1977 SrbExtension->LBA4 = 0;
1978 SrbExtension->LBA5 = 0;
1979 SrbExtension->FeaturesHigh = 0;
1980 SrbExtension->SectorCountLow = 0;
1981 SrbExtension->SectorCountHigh = 0;
1982
1983 SrbExtension->Sgl.NumberOfElements = 1;
1984 SrbExtension->Sgl.List[0].PhysicalAddress.LowPart = PortExtension->IdentifyDeviceDataPhysicalAddress.LowPart;
1985 SrbExtension->Sgl.List[0].PhysicalAddress.HighPart = PortExtension->IdentifyDeviceDataPhysicalAddress.HighPart;
1986 SrbExtension->Sgl.List[0].Length = sizeof(IDENTIFY_DEVICE_DATA);
1987
1988 SrbExtension->pSgl = &SrbExtension->Sgl;
1989 return SRB_STATUS_PENDING;
1990 }
1991 else
1992 {
1993 AhciDebugPrint("\tVPD Inquired\n");
1994
1995 DataBuffer = Srb->DataBuffer;
1996 DataBufferLength = Srb->DataTransferLength;
1997 RequiredDataBufferLength = DataBufferLength; // make the compiler happy :p
1998
1999 if (DataBuffer == NULL)
2000 {
2001 return SRB_STATUS_INVALID_REQUEST;
2002 }
2003
2004 AhciZeroMemory(DataBuffer, DataBufferLength);
2005
2006 switch(Cdb->CDB6INQUIRY3.PageCode)
2007 {
2008 case VPD_SUPPORTED_PAGES:
2009 {
2010 AhciDebugPrint("\tVPD_SUPPORTED_PAGES\n");
2011 RequiredDataBufferLength = sizeof(VPD_SUPPORTED_PAGES_PAGE) + 1;
2012
2013 if (DataBufferLength < RequiredDataBufferLength)
2014 {
2015 AhciDebugPrint("\tDataBufferLength: %d Required: %d\n", DataBufferLength, RequiredDataBufferLength);
2016 return SRB_STATUS_INVALID_REQUEST;
2017 }
2018
2019 VpdOutputBuffer = (PVPD_SUPPORTED_PAGES_PAGE)DataBuffer;
2020
2021 VpdOutputBuffer->DeviceType = PortExtension->DeviceParams.AccessType;
2022 VpdOutputBuffer->DeviceTypeQualifier = 0;
2023 VpdOutputBuffer->PageCode = VPD_SUPPORTED_PAGES;
2024 VpdOutputBuffer->PageLength = 1;
2025 VpdOutputBuffer->SupportedPageList[0] = VPD_SUPPORTED_PAGES;
2026 //VpdOutputBuffer->SupportedPageList[1] = VPD_SERIAL_NUMBER;
2027 //VpdOutputBuffer->SupportedPageList[2] = VPD_DEVICE_IDENTIFIERS;
2028
2029 NT_ASSERT(VpdOutputBuffer->DeviceType == DIRECT_ACCESS_DEVICE);
2030 }
2031 break;
2032 case VPD_SERIAL_NUMBER:
2033 {
2034 AhciDebugPrint("\tVPD_SERIAL_NUMBER\n");
2035 }
2036 break;
2037 case VPD_DEVICE_IDENTIFIERS:
2038 {
2039 AhciDebugPrint("\tVPD_DEVICE_IDENTIFIERS\n");
2040 }
2041 break;
2042 default:
2043 AhciDebugPrint("\tPageCode: %x\n", Cdb->CDB6INQUIRY3.PageCode);
2044 return SRB_STATUS_INVALID_REQUEST;
2045 }
2046
2047 Srb->DataTransferLength = RequiredDataBufferLength;
2048 return SRB_STATUS_SUCCESS;
2049 }
2050 }// -- DeviceInquiryRequest();
2051
2052 /**
2053 * @name AhciAdapterReset
2054 * @implemented
2055 *
2056 * 10.4.3 HBA Reset
2057 * If the HBA becomes unusable for multiple ports, and a software reset or port reset does not correct the
2058 * problem, software may reset the entire HBA by setting GHC.HR to ‘1’. When software sets the GHC.HR
2059 * bit to ‘1’, the HBA shall perform an internal reset action. The bit shall be cleared to ‘0’ by the HBA when
2060 * the reset is complete. A software write of ‘0’ to GHC.HR shall have no effect. To perform the HBA reset,
2061 * software sets GHC.HR to ‘1’ and may poll until this bit is read to be ‘0’, at which point software knows that
2062 * the HBA reset has completed.
2063 * If the HBA has not cleared GHC.HR to ‘0’ within 1 second of software setting GHC.HR to ‘1’, the HBA is in
2064 * a hung or locked state.
2065 *
2066 * @param AdapterExtension
2067 *
2068 * @return
2069 * TRUE in case AHCI Controller RESTARTED successfully. i.e GHC.HR == 0
2070 */
2071 BOOLEAN
2072 AhciAdapterReset (
2073 __in PAHCI_ADAPTER_EXTENSION AdapterExtension
2074 )
2075 {
2076 ULONG ticks;
2077 AHCI_GHC ghc;
2078 PAHCI_MEMORY_REGISTERS abar = NULL;
2079
2080 AhciDebugPrint("AhciAdapterReset()\n");
2081
2082 abar = AdapterExtension->ABAR_Address;
2083 if (abar == NULL) // basic sanity
2084 {
2085 return FALSE;
2086 }
2087
2088 // HR -- Very first bit (lowest significant)
2089 ghc.HR = 1;
2090 StorPortWriteRegisterUlong(AdapterExtension, &abar->GHC, ghc.Status);
2091
2092 for (ticks = 0; ticks < 50; ++ticks)
2093 {
2094 ghc.Status = StorPortReadRegisterUlong(AdapterExtension, &abar->GHC);
2095 if (ghc.HR == 0)
2096 {
2097 break;
2098 }
2099 StorPortStallExecution(20000);
2100 }
2101
2102 if (ticks == 50)// 1 second
2103 {
2104 AhciDebugPrint("\tDevice Timeout\n");
2105 return FALSE;
2106 }
2107
2108 return TRUE;
2109 }// -- AhciAdapterReset();
2110
2111 /**
2112 * @name AhciZeroMemory
2113 * @implemented
2114 *
2115 * Clear buffer by filling zeros
2116 *
2117 * @param Buffer
2118 * @param BufferSize
2119 */
2120 __inline
2121 VOID
2122 AhciZeroMemory (
2123 __out PCHAR Buffer,
2124 __in ULONG BufferSize
2125 )
2126 {
2127 ULONG i;
2128 for (i = 0; i < BufferSize; i++)
2129 {
2130 Buffer[i] = 0;
2131 }
2132
2133 return;
2134 }// -- AhciZeroMemory();
2135
2136 /**
2137 * @name IsPortValid
2138 * @implemented
2139 *
2140 * Tells wheather given port is implemented or not
2141 *
2142 * @param AdapterExtension
2143 * @param PathId
2144 *
2145 * @return
2146 * return TRUE if provided port is valid (implemented) or not
2147 */
2148 __inline
2149 BOOLEAN
2150 IsPortValid (
2151 __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
2152 __in ULONG pathId
2153 )
2154 {
2155 NT_ASSERT(pathId < MAXIMUM_AHCI_PORT_COUNT);
2156
2157 if (pathId >= AdapterExtension->PortCount)
2158 {
2159 return FALSE;
2160 }
2161
2162 return AdapterExtension->PortExtension[pathId].DeviceParams.IsActive;
2163 }// -- IsPortValid()
2164
2165 /**
2166 * @name AddQueue
2167 * @implemented
2168 *
2169 * Add Srb to Queue
2170 *
2171 * @param Queue
2172 * @param Srb
2173 *
2174 * @return
2175 * return TRUE if Srb is successfully added to Queue
2176 *
2177 */
2178 __inline
2179 BOOLEAN
2180 AddQueue (
2181 __inout PAHCI_QUEUE Queue,
2182 __in PVOID Srb
2183 )
2184 {
2185 NT_ASSERT(Queue->Head < MAXIMUM_QUEUE_BUFFER_SIZE);
2186 NT_ASSERT(Queue->Tail < MAXIMUM_QUEUE_BUFFER_SIZE);
2187
2188 if (Queue->Tail == ((Queue->Head + 1) % MAXIMUM_QUEUE_BUFFER_SIZE))
2189 return FALSE;
2190
2191 Queue->Buffer[Queue->Head++] = Srb;
2192 Queue->Head %= MAXIMUM_QUEUE_BUFFER_SIZE;
2193
2194 return TRUE;
2195 }// -- AddQueue();
2196
2197 /**
2198 * @name RemoveQueue
2199 * @implemented
2200 *
2201 * Remove and return Srb from Queue
2202 *
2203 * @param Queue
2204 *
2205 * @return
2206 * return Srb
2207 *
2208 */
2209 __inline
2210 PVOID
2211 RemoveQueue (
2212 __inout PAHCI_QUEUE Queue
2213 )
2214 {
2215 PVOID Srb;
2216
2217 NT_ASSERT(Queue->Head < MAXIMUM_QUEUE_BUFFER_SIZE);
2218 NT_ASSERT(Queue->Tail < MAXIMUM_QUEUE_BUFFER_SIZE);
2219
2220 if (Queue->Head == Queue->Tail)
2221 return NULL;
2222
2223 Srb = Queue->Buffer[Queue->Tail++];
2224 Queue->Tail %= MAXIMUM_QUEUE_BUFFER_SIZE;
2225
2226 return Srb;
2227 }// -- RemoveQueue();
2228
2229 /**
2230 * @name GetSrbExtension
2231 * @implemented
2232 *
2233 * GetSrbExtension from Srb make sure It is properly aligned
2234 *
2235 * @param Srb
2236 *
2237 * @return
2238 * return SrbExtension
2239 *
2240 */
2241 __inline
2242 PAHCI_SRB_EXTENSION
2243 GetSrbExtension (
2244 __in PSCSI_REQUEST_BLOCK Srb
2245 )
2246 {
2247 ULONG Offset;
2248 ULONG_PTR SrbExtension;
2249
2250 SrbExtension = (ULONG_PTR)Srb->SrbExtension;
2251 Offset = SrbExtension % 128;
2252
2253 // CommandTable should be 128 byte aligned
2254 if (Offset != 0)
2255 Offset = 128 - Offset;
2256
2257 return (PAHCI_SRB_EXTENSION)(SrbExtension + Offset);
2258 }// -- PAHCI_SRB_EXTENSION();
2259
2260 /**
2261 * @name AhciGetLba
2262 * @implemented
2263 *
2264 * Find the logical address of demand block from Cdb
2265 *
2266 * @param Srb
2267 *
2268 * @return
2269 * return Logical Address of the block
2270 *
2271 */
2272 __inline
2273 ULONG64
2274 AhciGetLba (
2275 __in PCDB Cdb,
2276 __in ULONG CdbLength
2277 )
2278 {
2279 ULONG64 lba = 0;
2280
2281 NT_ASSERT(Cdb != NULL);
2282 NT_ASSERT(CdbLength != 0);
2283
2284 if (CdbLength == 0x10)
2285 {
2286 REVERSE_BYTES_QUAD(&lba, Cdb->CDB16.LogicalBlock);
2287 }
2288 else
2289 {
2290 lba |= Cdb->CDB10.LogicalBlockByte3 << 0;
2291 lba |= Cdb->CDB10.LogicalBlockByte2 << 8;
2292 lba |= Cdb->CDB10.LogicalBlockByte1 << 16;
2293 lba |= Cdb->CDB10.LogicalBlockByte0 << 24;
2294 }
2295
2296 return lba;
2297 }// -- AhciGetLba();