]> git.proxmox.com Git - mirror_edk2.git/blob - SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalAhciMode.c
SecurityPkg OpalPassword: Add solution without SMM device code
[mirror_edk2.git] / SecurityPkg / Tcg / Opal / OpalPasswordSmm / OpalAhciMode.c
1 /** @file
2 This driver is used for Opal Password Feature support at AHCI mode.
3
4 Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13 **/
14
15
16 #include "OpalPasswordSmm.h"
17
18 /**
19 Start command for give slot on specific port.
20
21 @param Port The number of port.
22 @param CommandSlot The number of CommandSlot.
23 @param Timeout The timeout Value of start.
24
25 @retval EFI_DEVICE_ERROR The command start unsuccessfully.
26 @retval EFI_TIMEOUT The operation is time out.
27 @retval EFI_SUCCESS The command start successfully.
28
29 **/
30 EFI_STATUS
31 EFIAPI
32 AhciStartCommand (
33 IN UINT8 Port,
34 IN UINT8 CommandSlot,
35 IN UINT64 Timeout
36 );
37
38 /**
39 Stop command running for giving port
40
41 @param Port The number of port.
42 @param Timeout The timeout Value of stop.
43
44 @retval EFI_DEVICE_ERROR The command stop unsuccessfully.
45 @retval EFI_TIMEOUT The operation is time out.
46 @retval EFI_SUCCESS The command stop successfully.
47
48 **/
49 EFI_STATUS
50 EFIAPI
51 AhciStopCommand (
52 IN UINT8 Port,
53 IN UINT64 Timeout
54 );
55
56 /**
57 Read AHCI Operation register.
58
59 @param Offset The operation register offset.
60
61 @return The register content read.
62
63 **/
64 UINT32
65 EFIAPI
66 AhciReadReg (
67 IN UINT32 Offset
68 )
69 {
70 UINT32 Data;
71
72 Data = 0;
73
74 Data = MmioRead32 (mAhciBar + Offset);
75
76 return Data;
77 }
78
79 /**
80 Write AHCI Operation register.
81
82 @param Offset The operation register offset.
83 @param Data The Data used to write down.
84
85 **/
86 VOID
87 EFIAPI
88 AhciWriteReg (
89 IN UINT32 Offset,
90 IN UINT32 Data
91 )
92 {
93 MmioWrite32 (mAhciBar + Offset, Data);
94
95 return ;
96 }
97
98 /**
99 Do AND operation with the Value of AHCI Operation register.
100
101 @param Offset The operation register offset.
102 @param AndData The Data used to do AND operation.
103
104 **/
105 VOID
106 EFIAPI
107 AhciAndReg (
108 IN UINT32 Offset,
109 IN UINT32 AndData
110 )
111 {
112 UINT32 Data;
113
114 Data = AhciReadReg (Offset);
115
116 Data &= AndData;
117
118 AhciWriteReg (Offset, Data);
119 }
120
121 /**
122 Do OR operation with the Value of AHCI Operation register.
123
124 @param Offset The operation register offset.
125 @param OrData The Data used to do OR operation.
126
127 **/
128 VOID
129 EFIAPI
130 AhciOrReg (
131 IN UINT32 Offset,
132 IN UINT32 OrData
133 )
134 {
135 UINT32 Data;
136
137 Data = AhciReadReg (Offset);
138
139 Data |= OrData;
140
141 AhciWriteReg (Offset, Data);
142 }
143
144 /**
145 Wait for memory set to the test Value.
146
147 @param Offset The memory address to test.
148 @param MaskValue The mask Value of memory.
149 @param TestValue The test Value of memory.
150 @param Timeout The time out Value for wait memory set.
151
152 @retval EFI_DEVICE_ERROR The memory is not set.
153 @retval EFI_TIMEOUT The memory setting is time out.
154 @retval EFI_SUCCESS The memory is correct set.
155
156 **/
157 EFI_STATUS
158 EFIAPI
159 AhciWaitMmioSet (
160 IN UINT32 Offset,
161 IN UINT32 MaskValue,
162 IN UINT32 TestValue,
163 IN UINT64 Timeout
164 )
165 {
166 UINT32 Value;
167 UINT32 Delay;
168
169 Delay = (UINT32) (DivU64x32(Timeout, 1000) + 1);
170
171 do {
172 Value = AhciReadReg (Offset) & MaskValue;
173
174 if (Value == TestValue) {
175 return EFI_SUCCESS;
176 }
177
178 //
179 // Stall for 100 microseconds.
180 //
181 MicroSecondDelay (100);
182
183 Delay--;
184
185 } while (Delay > 0);
186
187 return EFI_TIMEOUT;
188 }
189 /**
190 Wait for the Value of the specified system memory set to the test Value.
191
192 @param Address The system memory address to test.
193 @param MaskValue The mask Value of memory.
194 @param TestValue The test Value of memory.
195 @param Timeout The time out Value for wait memory set, uses 100ns as a unit.
196
197 @retval EFI_TIMEOUT The system memory setting is time out.
198 @retval EFI_SUCCESS The system memory is correct set.
199
200 **/
201 EFI_STATUS
202 EFIAPI
203 AhciWaitMemSet (
204 IN EFI_PHYSICAL_ADDRESS Address,
205 IN UINT32 MaskValue,
206 IN UINT32 TestValue,
207 IN UINT64 Timeout
208 )
209 {
210 UINT32 Value;
211 UINT32 Delay;
212
213 Delay = (UINT32) (DivU64x32 (Timeout, 1000) + 1);
214
215 do {
216 //
217 // Access sytem memory to see if the Value is the tested one.
218 //
219 // The system memory pointed by Address will be updated by the
220 // SATA Host Controller, "volatile" is introduced to prevent
221 // compiler from optimizing the access to the memory address
222 // to only read once.
223 //
224 Value = *(volatile UINT32 *) (UINTN) Address;
225 Value &= MaskValue;
226
227 if (Value == TestValue) {
228 return EFI_SUCCESS;
229 }
230
231 //
232 // Stall for 100 microseconds.
233 //
234 MicroSecondDelay (100);
235
236 Delay--;
237
238 } while (Delay > 0);
239
240 return EFI_TIMEOUT;
241 }
242
243 /**
244 Check the memory status to the test Value.
245
246 @param[in] Address The memory address to test.
247 @param[in] MaskValue The mask Value of memory.
248 @param[in] TestValue The test Value of memory.
249 @param[in, out] RetryTimes The retry times Value for waitting memory set. If 0, then just try once.
250
251 @retval EFI_NOTREADY The memory is not set.
252 @retval EFI_TIMEOUT The memory setting retry times out.
253 @retval EFI_SUCCESS The memory is correct set.
254
255 **/
256 EFI_STATUS
257 EFIAPI
258 AhciCheckMemSet (
259 IN UINTN Address,
260 IN UINT32 MaskValue,
261 IN UINT32 TestValue,
262 IN OUT UINTN *RetryTimes OPTIONAL
263 )
264 {
265 UINT32 Value;
266
267 if (RetryTimes != NULL) {
268 (*RetryTimes)--;
269 }
270
271 Value = *(volatile UINT32 *) Address;
272 Value &= MaskValue;
273
274 if (Value == TestValue) {
275 return EFI_SUCCESS;
276 }
277
278 if ((RetryTimes != NULL) && (*RetryTimes == 0)) {
279 return EFI_TIMEOUT;
280 } else {
281 return EFI_NOT_READY;
282 }
283 }
284
285 /**
286 Clear the port interrupt and error status. It will also clear
287 HBA interrupt status.
288
289 @param Port The number of port.
290
291 **/
292 VOID
293 EFIAPI
294 AhciClearPortStatus (
295 IN UINT8 Port
296 )
297 {
298 UINT32 Offset;
299
300 //
301 // Clear any error status
302 //
303 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SERR;
304 AhciWriteReg (Offset, AhciReadReg (Offset));
305
306 //
307 // Clear any port interrupt status
308 //
309 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_IS;
310 AhciWriteReg (Offset, AhciReadReg (Offset));
311
312 //
313 // Clear any HBA interrupt status
314 //
315 AhciWriteReg (EFI_AHCI_IS_OFFSET, AhciReadReg (EFI_AHCI_IS_OFFSET));
316 }
317
318 /**
319 Enable the FIS running for giving port.
320
321 @param Port The number of port.
322 @param Timeout The timeout Value of enabling FIS.
323
324 @retval EFI_DEVICE_ERROR The FIS enable setting fails.
325 @retval EFI_TIMEOUT The FIS enable setting is time out.
326 @retval EFI_SUCCESS The FIS enable successfully.
327
328 **/
329 EFI_STATUS
330 EFIAPI
331 AhciEnableFisReceive (
332 IN UINT8 Port,
333 IN UINT64 Timeout
334 )
335 {
336 UINT32 Offset;
337
338 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
339 AhciOrReg (Offset, EFI_AHCI_PORT_CMD_FRE);
340
341 return AhciWaitMmioSet (
342 Offset,
343 EFI_AHCI_PORT_CMD_FR,
344 EFI_AHCI_PORT_CMD_FR,
345 Timeout
346 );
347 }
348
349 /**
350 Disable the FIS running for giving port.
351
352 @param Port The number of port.
353 @param Timeout The timeout Value of disabling FIS.
354
355 @retval EFI_DEVICE_ERROR The FIS disable setting fails.
356 @retval EFI_TIMEOUT The FIS disable setting is time out.
357 @retval EFI_UNSUPPORTED The port is in running state.
358 @retval EFI_SUCCESS The FIS disable successfully.
359
360 **/
361 EFI_STATUS
362 EFIAPI
363 AhciDisableFisReceive (
364 IN UINT8 Port,
365 IN UINT64 Timeout
366 )
367 {
368 UINT32 Offset;
369 UINT32 Data;
370
371 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
372 Data = AhciReadReg (Offset);
373
374 //
375 // Before disabling Fis receive, the DMA engine of the port should NOT be in running status.
376 //
377 if ((Data & (EFI_AHCI_PORT_CMD_ST | EFI_AHCI_PORT_CMD_CR)) != 0) {
378 return EFI_UNSUPPORTED;
379 }
380
381 //
382 // Check if the Fis receive DMA engine for the port is running.
383 //
384 if ((Data & EFI_AHCI_PORT_CMD_FR) != EFI_AHCI_PORT_CMD_FR) {
385 return EFI_SUCCESS;
386 }
387
388 AhciAndReg (Offset, (UINT32)~(EFI_AHCI_PORT_CMD_FRE));
389
390 return AhciWaitMmioSet (
391 Offset,
392 EFI_AHCI_PORT_CMD_FR,
393 0,
394 Timeout
395 );
396 }
397
398 /**
399 Build the command list, command table and prepare the fis receiver.
400
401 @param AhciRegisters The pointer to the EFI_AHCI_REGISTERS.
402 @param Port The number of port.
403 @param PortMultiplier The timeout Value of stop.
404 @param CommandFis The control fis will be used for the transfer.
405 @param CommandList The command list will be used for the transfer.
406 @param AtapiCommand The atapi command will be used for the transfer.
407 @param AtapiCommandLength The Length of the atapi command.
408 @param CommandSlotNumber The command slot will be used for the transfer.
409 @param DataPhysicalAddr The pointer to the Data Buffer pci bus master address.
410 @param DataLength The Data count to be transferred.
411
412 **/
413 VOID
414 EFIAPI
415 AhciBuildCommand (
416 IN EFI_AHCI_REGISTERS *AhciRegisters,
417 IN UINT8 Port,
418 IN UINT8 PortMultiplier,
419 IN EFI_AHCI_COMMAND_FIS *CommandFis,
420 IN EFI_AHCI_COMMAND_LIST *CommandList,
421 IN EFI_AHCI_ATAPI_COMMAND *AtapiCommand OPTIONAL,
422 IN UINT8 AtapiCommandLength,
423 IN UINT8 CommandSlotNumber,
424 IN OUT VOID *DataPhysicalAddr,
425 IN UINT64 DataLength
426 )
427 {
428 UINT64 BaseAddr;
429 UINT64 PrdtNumber;
430 UINTN RemainedData;
431 UINTN MemAddr;
432 DATA_64 Data64;
433 UINT32 Offset;
434
435 //
436 // Filling the PRDT
437 //
438 PrdtNumber = DivU64x32 (DataLength + EFI_AHCI_MAX_DATA_PER_PRDT - 1, EFI_AHCI_MAX_DATA_PER_PRDT);
439
440 //
441 // According to AHCI 1.3 spec, a PRDT entry can point to a maximum 4MB Data block.
442 // It also limits that the maximum amount of the PRDT entry in the command table
443 // is 65535.
444 //
445 ASSERT (PrdtNumber <= 1);
446
447 Data64.Uint64 = (UINTN) (AhciRegisters->AhciRFis);
448
449 BaseAddr = Data64.Uint64;
450
451 ZeroMem ((VOID *)((UINTN) BaseAddr), sizeof (EFI_AHCI_RECEIVED_FIS));
452
453 ZeroMem (AhciRegisters->AhciCommandTable, sizeof (EFI_AHCI_COMMAND_TABLE));
454
455 CommandFis->AhciCFisPmNum = PortMultiplier;
456
457 CopyMem (&AhciRegisters->AhciCommandTable->CommandFis, CommandFis, sizeof (EFI_AHCI_COMMAND_FIS));
458
459 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
460 if (AtapiCommand != NULL) {
461 CopyMem (
462 &AhciRegisters->AhciCommandTable->AtapiCmd,
463 AtapiCommand,
464 AtapiCommandLength
465 );
466
467 CommandList->AhciCmdA = 1;
468 CommandList->AhciCmdP = 1;
469
470 AhciOrReg (Offset, (EFI_AHCI_PORT_CMD_DLAE | EFI_AHCI_PORT_CMD_ATAPI));
471 } else {
472 AhciAndReg (Offset, (UINT32)~(EFI_AHCI_PORT_CMD_DLAE | EFI_AHCI_PORT_CMD_ATAPI));
473 }
474
475 RemainedData = (UINTN) DataLength;
476 MemAddr = (UINTN) DataPhysicalAddr;
477 CommandList->AhciCmdPrdtl = (UINT32)PrdtNumber;
478
479 AhciRegisters->AhciCommandTable->PrdtTable.AhciPrdtDbc = (UINT32)RemainedData - 1;
480
481 Data64.Uint64 = (UINT64)MemAddr;
482 AhciRegisters->AhciCommandTable->PrdtTable.AhciPrdtDba = Data64.Uint32.Lower32;
483 AhciRegisters->AhciCommandTable->PrdtTable.AhciPrdtDbau = Data64.Uint32.Upper32;
484
485 //
486 // Set the last PRDT to Interrupt On Complete
487 //
488 AhciRegisters->AhciCommandTable->PrdtTable.AhciPrdtIoc = 1;
489
490 CopyMem (
491 (VOID *) ((UINTN) AhciRegisters->AhciCmdList + (UINTN) CommandSlotNumber * sizeof (EFI_AHCI_COMMAND_LIST)),
492 CommandList,
493 sizeof (EFI_AHCI_COMMAND_LIST)
494 );
495
496 Data64.Uint64 = (UINT64)(UINTN) AhciRegisters->AhciCommandTable;
497 AhciRegisters->AhciCmdList[CommandSlotNumber].AhciCmdCtba = Data64.Uint32.Lower32;
498 AhciRegisters->AhciCmdList[CommandSlotNumber].AhciCmdCtbau = Data64.Uint32.Upper32;
499 AhciRegisters->AhciCmdList[CommandSlotNumber].AhciCmdPmp = PortMultiplier;
500
501 }
502
503 /**
504 Buid a command FIS.
505
506 @param CmdFis A pointer to the EFI_AHCI_COMMAND_FIS Data structure.
507 @param AtaCommandBlock A pointer to the AhciBuildCommandFis Data structure.
508
509 **/
510 VOID
511 EFIAPI
512 AhciBuildCommandFis (
513 IN OUT EFI_AHCI_COMMAND_FIS *CmdFis,
514 IN EFI_ATA_COMMAND_BLOCK *AtaCommandBlock
515 )
516 {
517 ZeroMem (CmdFis, sizeof (EFI_AHCI_COMMAND_FIS));
518
519 CmdFis->AhciCFisType = EFI_AHCI_FIS_REGISTER_H2D;
520 //
521 // Indicator it's a command
522 //
523 CmdFis->AhciCFisCmdInd = 0x1;
524 CmdFis->AhciCFisCmd = AtaCommandBlock->AtaCommand;
525
526 CmdFis->AhciCFisFeature = AtaCommandBlock->AtaFeatures;
527 CmdFis->AhciCFisFeatureExp = AtaCommandBlock->AtaFeaturesExp;
528
529 CmdFis->AhciCFisSecNum = AtaCommandBlock->AtaSectorNumber;
530 CmdFis->AhciCFisSecNumExp = AtaCommandBlock->AtaSectorNumberExp;
531
532 CmdFis->AhciCFisClyLow = AtaCommandBlock->AtaCylinderLow;
533 CmdFis->AhciCFisClyLowExp = AtaCommandBlock->AtaCylinderLowExp;
534
535 CmdFis->AhciCFisClyHigh = AtaCommandBlock->AtaCylinderHigh;
536 CmdFis->AhciCFisClyHighExp = AtaCommandBlock->AtaCylinderHighExp;
537
538 CmdFis->AhciCFisSecCount = AtaCommandBlock->AtaSectorCount;
539 CmdFis->AhciCFisSecCountExp = AtaCommandBlock->AtaSectorCountExp;
540
541 CmdFis->AhciCFisDevHead = (UINT8) (AtaCommandBlock->AtaDeviceHead | 0xE0);
542 }
543
544 /**
545 Start a PIO Data transfer on specific port.
546
547 @param AhciRegisters The pointer to the EFI_AHCI_REGISTERS.
548 @param Port The number of port.
549 @param PortMultiplier The timeout Value of stop.
550 @param AtapiCommand The atapi command will be used for the transfer.
551 @param AtapiCommandLength The Length of the atapi command.
552 @param Read The transfer direction.
553 @param AtaCommandBlock The EFI_ATA_COMMAND_BLOCK Data.
554 @param AtaStatusBlock The EFI_ATA_STATUS_BLOCK Data.
555 @param MemoryAddr The pointer to the Data Buffer.
556 @param DataCount The Data count to be transferred.
557 @param Timeout The timeout Value of non Data transfer.
558
559 @retval EFI_DEVICE_ERROR The PIO Data transfer abort with error occurs.
560 @retval EFI_TIMEOUT The operation is time out.
561 @retval EFI_UNSUPPORTED The device is not ready for transfer.
562 @retval EFI_SUCCESS The PIO Data transfer executes successfully.
563
564 **/
565 EFI_STATUS
566 EFIAPI
567 AhciPioTransfer (
568 IN EFI_AHCI_REGISTERS *AhciRegisters,
569 IN UINT8 Port,
570 IN UINT8 PortMultiplier,
571 IN EFI_AHCI_ATAPI_COMMAND *AtapiCommand OPTIONAL,
572 IN UINT8 AtapiCommandLength,
573 IN BOOLEAN Read,
574 IN EFI_ATA_COMMAND_BLOCK *AtaCommandBlock,
575 IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock,
576 IN OUT VOID *MemoryAddr,
577 IN UINT32 DataCount,
578 IN UINT64 Timeout
579 )
580 {
581 EFI_STATUS Status;
582 UINT32 FisBaseAddr;
583 UINT32 Offset;
584 UINT32 Delay;
585 EFI_AHCI_COMMAND_FIS CFis;
586 EFI_AHCI_COMMAND_LIST CmdList;
587 UINT32 PortTfd;
588 UINT32 PrdCount;
589 UINT32 OldRfisLo;
590 UINT32 OldRfisHi;
591 UINT32 OldCmdListLo;
592 UINT32 OldCmdListHi;
593
594 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FB;
595 OldRfisLo = AhciReadReg (Offset);
596 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FBU;
597 OldRfisHi = AhciReadReg (Offset);
598 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FB;
599 AhciWriteReg (Offset, (UINT32)(UINTN)AhciRegisters->AhciRFis);
600 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FBU;
601 AhciWriteReg (Offset, 0);
602
603 //
604 // Single task envrionment, we only use one command table for all port
605 //
606 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLB;
607 OldCmdListLo = AhciReadReg (Offset);
608 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLBU;
609 OldCmdListHi = AhciReadReg (Offset);
610 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLB;
611 AhciWriteReg (Offset, (UINT32)(UINTN)AhciRegisters->AhciCmdList);
612 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLBU;
613 AhciWriteReg (Offset, 0);
614
615 //
616 // Package read needed
617 //
618 AhciBuildCommandFis (&CFis, AtaCommandBlock);
619
620 ZeroMem (&CmdList, sizeof (EFI_AHCI_COMMAND_LIST));
621
622 CmdList.AhciCmdCfl = EFI_AHCI_FIS_REGISTER_H2D_LENGTH / 4;
623 CmdList.AhciCmdW = Read ? 0 : 1;
624
625 AhciBuildCommand (
626 AhciRegisters,
627 Port,
628 PortMultiplier,
629 &CFis,
630 &CmdList,
631 AtapiCommand,
632 AtapiCommandLength,
633 0,
634 (VOID *)(UINTN)MemoryAddr,
635 DataCount
636 );
637
638 Status = AhciStartCommand (
639 Port,
640 0,
641 Timeout
642 );
643 if (EFI_ERROR (Status)) {
644 goto Exit;
645 }
646
647 //
648 // Checking the status and wait the driver sending Data
649 //
650 FisBaseAddr = (UINT32)(UINTN)AhciRegisters->AhciRFis;
651 if (Read && (AtapiCommand == 0)) {
652 //
653 // Wait device sends the PIO setup fis before Data transfer
654 //
655 Status = EFI_TIMEOUT;
656 Delay = (UINT32) (DivU64x32 (Timeout, 1000) + 1);
657 do {
658 Offset = FisBaseAddr + EFI_AHCI_PIO_FIS_OFFSET;
659
660 Status = AhciCheckMemSet (Offset, EFI_AHCI_FIS_TYPE_MASK, EFI_AHCI_FIS_PIO_SETUP, 0);
661 if (!EFI_ERROR (Status)) {
662 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;
663 PortTfd = AhciReadReg ((UINT32) Offset);
664 //
665 // PxTFD will be updated if there is a D2H or SetupFIS received.
666 // For PIO IN transfer, D2H means a device error. Therefore we only need to check the TFD after receiving a SetupFIS.
667 //
668 if ((PortTfd & EFI_AHCI_PORT_TFD_ERR) != 0) {
669 Status = EFI_DEVICE_ERROR;
670 break;
671 }
672
673 PrdCount = *(volatile UINT32 *) (&(AhciRegisters->AhciCmdList[0].AhciCmdPrdbc));
674 if (PrdCount == DataCount) {
675 break;
676 }
677 }
678
679 Offset = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET;
680 Status = AhciCheckMemSet (Offset, EFI_AHCI_FIS_TYPE_MASK, EFI_AHCI_FIS_REGISTER_D2H, 0);
681 if (!EFI_ERROR (Status)) {
682 Status = EFI_DEVICE_ERROR;
683 break;
684 }
685
686 //
687 // Stall for 100 microseconds.
688 //
689 MicroSecondDelay(100);
690
691 Delay--;
692 } while (Delay > 0);
693 } else {
694 //
695 // Wait for D2H Fis is received
696 //
697 Offset = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET;
698 Status = AhciWaitMemSet (
699 Offset,
700 EFI_AHCI_FIS_TYPE_MASK,
701 EFI_AHCI_FIS_REGISTER_D2H,
702 Timeout
703 );
704
705 if (EFI_ERROR (Status)) {
706 goto Exit;
707 }
708
709 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;
710 PortTfd = AhciReadReg ((UINT32) Offset);
711 if ((PortTfd & EFI_AHCI_PORT_TFD_ERR) != 0) {
712 Status = EFI_DEVICE_ERROR;
713 }
714 }
715
716 Exit:
717 AhciStopCommand (
718 Port,
719 Timeout
720 );
721
722 AhciDisableFisReceive (
723 Port,
724 Timeout
725 );
726
727 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FB;
728 AhciWriteReg (Offset, OldRfisLo);
729 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FBU;
730 AhciWriteReg (Offset, OldRfisHi);
731
732 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLB;
733 AhciWriteReg (Offset, OldCmdListLo);
734 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLBU;
735 AhciWriteReg (Offset, OldCmdListHi);
736
737 return Status;
738 }
739
740 /**
741 Stop command running for giving port
742
743 @param Port The number of port.
744 @param Timeout The timeout Value of stop.
745
746 @retval EFI_DEVICE_ERROR The command stop unsuccessfully.
747 @retval EFI_TIMEOUT The operation is time out.
748 @retval EFI_SUCCESS The command stop successfully.
749
750 **/
751 EFI_STATUS
752 EFIAPI
753 AhciStopCommand (
754 IN UINT8 Port,
755 IN UINT64 Timeout
756 )
757 {
758 UINT32 Offset;
759 UINT32 Data;
760
761 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
762 Data = AhciReadReg (Offset);
763
764 if ((Data & (EFI_AHCI_PORT_CMD_ST | EFI_AHCI_PORT_CMD_CR)) == 0) {
765 return EFI_SUCCESS;
766 }
767
768 if ((Data & EFI_AHCI_PORT_CMD_ST) != 0) {
769 AhciAndReg (Offset, (UINT32)~(EFI_AHCI_PORT_CMD_ST));
770 }
771
772 return AhciWaitMmioSet (
773 Offset,
774 EFI_AHCI_PORT_CMD_CR,
775 0,
776 Timeout
777 );
778 }
779
780 /**
781 Start command for give slot on specific port.
782
783 @param Port The number of port.
784 @param CommandSlot The number of CommandSlot.
785 @param Timeout The timeout Value of start.
786
787 @retval EFI_DEVICE_ERROR The command start unsuccessfully.
788 @retval EFI_TIMEOUT The operation is time out.
789 @retval EFI_SUCCESS The command start successfully.
790
791 **/
792 EFI_STATUS
793 EFIAPI
794 AhciStartCommand (
795 IN UINT8 Port,
796 IN UINT8 CommandSlot,
797 IN UINT64 Timeout
798 )
799 {
800 UINT32 CmdSlotBit;
801 EFI_STATUS Status;
802 UINT32 PortStatus;
803 UINT32 StartCmd;
804 UINT32 PortTfd;
805 UINT32 Offset;
806 UINT32 Capability;
807
808 //
809 // Collect AHCI controller information
810 //
811 Capability = AhciReadReg(EFI_AHCI_CAPABILITY_OFFSET);
812
813 CmdSlotBit = (UINT32) (1 << CommandSlot);
814
815 AhciClearPortStatus (
816 Port
817 );
818
819 Status = AhciEnableFisReceive (
820 Port,
821 Timeout
822 );
823
824 if (EFI_ERROR (Status)) {
825 return Status;
826 }
827
828 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
829 PortStatus = AhciReadReg (Offset);
830
831 StartCmd = 0;
832 if ((PortStatus & EFI_AHCI_PORT_CMD_ALPE) != 0) {
833 StartCmd = AhciReadReg (Offset);
834 StartCmd &= ~EFI_AHCI_PORT_CMD_ICC_MASK;
835 StartCmd |= EFI_AHCI_PORT_CMD_ACTIVE;
836 }
837
838 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;
839 PortTfd = AhciReadReg (Offset);
840
841 if ((PortTfd & (EFI_AHCI_PORT_TFD_BSY | EFI_AHCI_PORT_TFD_DRQ)) != 0) {
842 if ((Capability & BIT24) != 0) {
843 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
844 AhciOrReg (Offset, EFI_AHCI_PORT_CMD_COL);
845
846 AhciWaitMmioSet (
847 Offset,
848 EFI_AHCI_PORT_CMD_COL,
849 0,
850 Timeout
851 );
852 }
853 }
854
855 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
856 AhciOrReg (Offset, EFI_AHCI_PORT_CMD_ST | StartCmd);
857
858 //
859 // Setting the command
860 //
861 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SACT;
862 AhciAndReg (Offset, 0);
863 AhciOrReg (Offset, CmdSlotBit);
864
865 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CI;
866 AhciAndReg (Offset, 0);
867 AhciOrReg (Offset, CmdSlotBit);
868 return EFI_SUCCESS;
869 }
870
871
872 /**
873 Do AHCI HBA reset.
874
875 @param[in] Timeout The timeout Value of reset.
876
877 @retval EFI_DEVICE_ERROR AHCI controller is failed to complete hardware reset.
878 @retval EFI_TIMEOUT The reset operation is time out.
879 @retval EFI_SUCCESS AHCI controller is reset successfully.
880
881 **/
882 EFI_STATUS
883 EFIAPI
884 AhciReset (
885 IN UINT64 Timeout
886 )
887 {
888 UINT32 Delay;
889 UINT32 Value;
890 UINT32 Capability;
891
892 //
893 // Collect AHCI controller information
894 //
895 Capability = AhciReadReg (EFI_AHCI_CAPABILITY_OFFSET);
896
897 //
898 // Enable AE before accessing any AHCI registers if Supports AHCI Mode Only is not set
899 //
900 if ((Capability & EFI_AHCI_CAP_SAM) == 0) {
901 AhciOrReg (EFI_AHCI_GHC_OFFSET, EFI_AHCI_GHC_ENABLE);
902 }
903
904 AhciOrReg (EFI_AHCI_GHC_OFFSET, EFI_AHCI_GHC_RESET);
905
906 Delay = (UINT32) (DivU64x32(Timeout, 1000) + 1);
907
908 do {
909 Value = AhciReadReg(EFI_AHCI_GHC_OFFSET);
910 if ((Value & EFI_AHCI_GHC_RESET) == 0) {
911 return EFI_SUCCESS;
912 }
913
914 //
915 // Stall for 100 microseconds.
916 //
917 MicroSecondDelay(100);
918
919 Delay--;
920 } while (Delay > 0);
921
922 return EFI_TIMEOUT;
923
924
925 }
926
927 /**
928 Send Buffer cmd to specific device.
929
930 @param[in] AhciRegisters The pointer to the EFI_AHCI_REGISTERS.
931 @param[in] Port The port number of attached ATA device.
932 @param[in] PortMultiplier The port number of port multiplier of attached ATA device.
933 @param[in, out] Buffer The Data Buffer to store IDENTIFY PACKET Data.
934
935 @retval EFI_DEVICE_ERROR The cmd abort with error occurs.
936 @retval EFI_TIMEOUT The operation is time out.
937 @retval EFI_UNSUPPORTED The device is not ready for executing.
938 @retval EFI_SUCCESS The cmd executes successfully.
939
940 **/
941 EFI_STATUS
942 EFIAPI
943 AhciIdentify (
944 IN EFI_AHCI_REGISTERS *AhciRegisters,
945 IN UINT8 Port,
946 IN UINT8 PortMultiplier,
947 IN OUT ATA_IDENTIFY_DATA *Buffer
948 )
949 {
950 EFI_STATUS Status;
951 EFI_ATA_COMMAND_BLOCK AtaCommandBlock;
952
953 if (AhciRegisters == NULL || Buffer == NULL) {
954 return EFI_INVALID_PARAMETER;
955 }
956
957 ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK));
958
959 AtaCommandBlock.AtaCommand = ATA_CMD_IDENTIFY_DRIVE;
960 AtaCommandBlock.AtaSectorCount = 1;
961
962 Status = AhciPioTransfer (
963 AhciRegisters,
964 Port,
965 PortMultiplier,
966 NULL,
967 0,
968 TRUE,
969 &AtaCommandBlock,
970 NULL,
971 Buffer,
972 sizeof (ATA_IDENTIFY_DATA),
973 ATA_TIMEOUT
974 );
975
976 return Status;
977 }
978
979 /**
980 Get AHCI mode MMIO Bar Size.
981
982 @param[in] Bus The bus number of ata host controller.
983 @param[in] Device The device number of ata host controller.
984 @param[in] Function The function number of ata host controller.
985
986 @retval The Size of AHCI MMIO BAR.
987
988 **/
989 UINT32
990 EFIAPI
991 GetAhciBarSize (
992 IN UINTN Bus,
993 IN UINTN Device,
994 IN UINTN Function
995 )
996 {
997 UINT32 Size;
998 UINT32 OldBar;
999
1000 OldBar = PciRead32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x24));
1001 //
1002 // Disable PCI CMD.MSE bit before calculating MMIO Bar Size as it needs write all 1 to BAR register.
1003 //
1004 PciAnd32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x04), (UINT32)~BIT1);
1005
1006 //
1007 // Get AHCI MMIO Bar Size.
1008 //
1009 PciWrite32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x24), 0xFFFFFFFF);
1010 Size = PciRead32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x24));
1011 Size = (~(Size & 0xFFFFFFF0)) + 1;
1012
1013 //
1014 // Restore old MMIO Bar.
1015 //
1016 PciWrite32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x24), OldBar);
1017 //
1018 // Enable PCI CMD.MSE bit after restoring MMIO Bar.
1019 //
1020 PciOr32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x04), BIT1);
1021
1022 return Size;
1023 }
1024
1025 /**
1026 Get AHCI mode base address registers' Value.
1027
1028 @param[in] Bus The bus number of ata host controller.
1029 @param[in] Device The device number of ata host controller.
1030 @param[in] Function The function number of ata host controller.
1031
1032 @retval EFI_UNSUPPORTED Return this Value when the BARs is not IO type
1033 @retval EFI_SUCCESS Get the Base address successfully
1034 @retval Other Read the pci configureation Data error
1035
1036 **/
1037 EFI_STATUS
1038 EFIAPI
1039 GetAhciBaseAddress (
1040 IN UINTN Bus,
1041 IN UINTN Device,
1042 IN UINTN Function
1043 )
1044 {
1045 UINT32 Size;
1046
1047 //
1048 // Get AHCI MMIO Bar
1049 //
1050 mAhciBar = PciRead32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x24));
1051 //
1052 // Get AHCI MMIO Bar Size
1053 //
1054 Size = GetAhciBarSize (Bus, Device, Function);
1055 //
1056 // Check if the AHCI Bar region is in SMRAM to avoid malicious attack by modifying MMIO Bar to point to SMRAM.
1057 //
1058 if (!SmmIsMmioValid ((EFI_PHYSICAL_ADDRESS)mAhciBar, Size, NULL)) {
1059 return EFI_UNSUPPORTED;
1060 }
1061
1062 return EFI_SUCCESS;
1063 }
1064
1065 /**
1066 Allocate transfer-related Data struct which is used at AHCI mode.
1067
1068 @retval EFI_OUT_OF_RESOURCE The allocation is failure.
1069 @retval EFI_SUCCESS Successful to allocate memory.
1070
1071 **/
1072 EFI_STATUS
1073 EFIAPI
1074 AhciAllocateResource (
1075 VOID
1076 )
1077 {
1078 EFI_STATUS Status;
1079 EFI_PHYSICAL_ADDRESS Base;
1080
1081 //
1082 // Allocate resources required by AHCI host controller.
1083 //
1084 Base = 0xFFFFFFFF;
1085 Status = gBS->AllocatePages (
1086 AllocateMaxAddress,
1087 EfiACPIMemoryNVS,
1088 EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS)),
1089 &Base
1090 );
1091 if (EFI_ERROR (Status)) {
1092 return EFI_OUT_OF_RESOURCES;
1093 }
1094
1095 ZeroMem ((VOID *)(UINTN)Base, EFI_PAGE_SIZE * EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS)));
1096 mAhciRegisters.AhciRFis = (VOID *)(UINTN)Base;
1097
1098 Base = 0xFFFFFFFF;
1099 Status = gBS->AllocatePages (
1100 AllocateMaxAddress,
1101 EfiACPIMemoryNVS,
1102 EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_LIST)),
1103 &Base
1104 );
1105 if (EFI_ERROR (Status)) {
1106 gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)mAhciRegisters.AhciRFis, EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS)));
1107 return EFI_OUT_OF_RESOURCES;
1108 }
1109 ZeroMem ((VOID *)(UINTN)Base, EFI_PAGE_SIZE * EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_LIST)));
1110 mAhciRegisters.AhciCmdList = (VOID *)(UINTN)Base;
1111
1112 Base = 0xFFFFFFFF;
1113 Status = gBS->AllocatePages (
1114 AllocateMaxAddress,
1115 EfiACPIMemoryNVS,
1116 EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_TABLE)),
1117 &Base
1118 );
1119 if (EFI_ERROR (Status)) {
1120 gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)mAhciRegisters.AhciRFis, EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS)));
1121 gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)mAhciRegisters.AhciCmdList, EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_LIST)));
1122 return EFI_OUT_OF_RESOURCES;
1123 }
1124 ZeroMem ((VOID *)(UINTN)Base, EFI_PAGE_SIZE * EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_TABLE)));
1125 mAhciRegisters.AhciCommandTable = (VOID *)(UINTN)Base;
1126 return EFI_SUCCESS;
1127 }
1128
1129 /**
1130 Free allocated transfer-related Data struct which is used at AHCI mode.
1131
1132 **/
1133 VOID
1134 EFIAPI
1135 AhciFreeResource (
1136 VOID
1137 )
1138 {
1139 if (mAhciRegisters.AhciRFis != NULL) {
1140 gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)mAhciRegisters.AhciRFis, EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS)));
1141 }
1142
1143 if (mAhciRegisters.AhciCmdList != NULL) {
1144 gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)mAhciRegisters.AhciCmdList, EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_LIST)));
1145 }
1146
1147 if (mAhciRegisters.AhciCommandTable != NULL) {
1148 gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)mAhciRegisters.AhciCommandTable, EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_TABLE)));
1149 }
1150 }
1151
1152 /**
1153 Initialize ATA host controller at AHCI mode.
1154
1155 The function is designed to initialize ATA host controller.
1156
1157 @param[in] Port The port number to do initialization.
1158
1159 **/
1160 EFI_STATUS
1161 EFIAPI
1162 AhciModeInitialize (
1163 UINT8 Port
1164 )
1165 {
1166 EFI_STATUS Status;
1167 UINT32 Capability;
1168 UINT32 Offset;
1169 UINT32 Data;
1170 UINT32 PhyDetectDelay;
1171
1172 Status = AhciReset (ATA_TIMEOUT);
1173 if (EFI_ERROR (Status)) {
1174 return Status;
1175 }
1176
1177 //
1178 // Collect AHCI controller information
1179 //
1180 Capability = AhciReadReg (EFI_AHCI_CAPABILITY_OFFSET);
1181
1182 //
1183 // Enable AE before accessing any AHCI registers if Supports AHCI Mode Only is not set
1184 //
1185 if ((Capability & EFI_AHCI_CAP_SAM) == 0) {
1186 AhciOrReg (EFI_AHCI_GHC_OFFSET, EFI_AHCI_GHC_ENABLE);
1187 }
1188
1189 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FB;
1190 AhciWriteReg (Offset, (UINT32)(UINTN)mAhciRegisters.AhciRFis);
1191
1192 //
1193 // Single task envrionment, we only use one command table for all port
1194 //
1195 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLB;
1196 AhciWriteReg (Offset, (UINT32)(UINTN)mAhciRegisters.AhciCmdList);
1197
1198 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
1199 Data = AhciReadReg (Offset);
1200 if ((Data & EFI_AHCI_PORT_CMD_CPD) != 0) {
1201 AhciOrReg (Offset, EFI_AHCI_PORT_CMD_POD);
1202 }
1203
1204 if ((Capability & BIT27) != 0) {
1205 AhciOrReg (Offset, EFI_AHCI_PORT_CMD_SUD);
1206 }
1207
1208 //
1209 // Disable aggressive power management.
1210 //
1211 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SCTL;
1212 AhciOrReg (Offset, EFI_AHCI_PORT_SCTL_IPM_INIT);
1213 //
1214 // Disable the reporting of the corresponding interrupt to system software.
1215 //
1216 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_IE;
1217 AhciAndReg (Offset, 0);
1218
1219 Status = AhciEnableFisReceive (
1220 Port,
1221 EFI_TIMER_PERIOD_MILLISECONDS(500)
1222 );
1223 ASSERT_EFI_ERROR (Status);
1224 if (EFI_ERROR (Status)) {
1225 return Status;
1226 }
1227
1228 //
1229 // According to SATA1.0a spec section 5.2, we need to wait for PxTFD.BSY and PxTFD.DRQ
1230 // and PxTFD.ERR to be zero. The maximum wait time is 16s which is defined at ATA spec.
1231 //
1232 PhyDetectDelay = 16 * 1000;
1233 do {
1234 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SERR;
1235 if (AhciReadReg(Offset) != 0) {
1236 AhciWriteReg (Offset, AhciReadReg(Offset));
1237 }
1238 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;
1239
1240 Data = AhciReadReg (Offset) & EFI_AHCI_PORT_TFD_MASK;
1241 if (Data == 0) {
1242 break;
1243 }
1244
1245 MicroSecondDelay (1000);
1246 PhyDetectDelay--;
1247 } while (PhyDetectDelay > 0);
1248
1249 if (PhyDetectDelay == 0) {
1250 return EFI_NOT_FOUND;
1251 }
1252
1253 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SIG;
1254 Status = AhciWaitMmioSet (
1255 Offset,
1256 0x0000FFFF,
1257 0x00000101,
1258 EFI_TIMER_PERIOD_SECONDS(16)
1259 );
1260
1261 if (EFI_ERROR (Status)) {
1262 return Status;
1263 }
1264
1265 return Status;
1266 }
1267