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