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