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