2 This driver is used for Opal Password Feature support at AHCI mode.
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
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.
16 #include "OpalPasswordSmm.h"
19 Start command for give slot on specific port.
21 @param Port The number of port.
22 @param CommandSlot The number of CommandSlot.
23 @param Timeout The timeout Value of start.
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.
39 Stop command running for giving port
41 @param Port The number of port.
42 @param Timeout The timeout Value of stop.
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.
57 Read AHCI Operation register.
59 @param Offset The operation register offset.
61 @return The register content read.
74 Data
= MmioRead32 (mAhciBar
+ Offset
);
80 Write AHCI Operation register.
82 @param Offset The operation register offset.
83 @param Data The Data used to write down.
93 MmioWrite32 (mAhciBar
+ Offset
, Data
);
99 Do AND operation with the Value of AHCI Operation register.
101 @param Offset The operation register offset.
102 @param AndData The Data used to do AND operation.
114 Data
= AhciReadReg (Offset
);
118 AhciWriteReg (Offset
, Data
);
122 Do OR operation with the Value of AHCI Operation register.
124 @param Offset The operation register offset.
125 @param OrData The Data used to do OR operation.
137 Data
= AhciReadReg (Offset
);
141 AhciWriteReg (Offset
, Data
);
145 Wait for memory set to the test Value.
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.
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.
169 Delay
= (UINT32
) (DivU64x32(Timeout
, 1000) + 1);
172 Value
= AhciReadReg (Offset
) & MaskValue
;
174 if (Value
== TestValue
) {
179 // Stall for 100 microseconds.
181 MicroSecondDelay (100);
190 Wait for the Value of the specified system memory set to the test Value.
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.
197 @retval EFI_TIMEOUT The system memory setting is time out.
198 @retval EFI_SUCCESS The system memory is correct set.
204 IN EFI_PHYSICAL_ADDRESS Address
,
213 Delay
= (UINT32
) (DivU64x32 (Timeout
, 1000) + 1);
217 // Access sytem memory to see if the Value is the tested one.
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.
224 Value
= *(volatile UINT32
*) (UINTN
) Address
;
227 if (Value
== TestValue
) {
232 // Stall for 100 microseconds.
234 MicroSecondDelay (100);
244 Check the memory status to the test Value.
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.
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.
262 IN OUT UINTN
*RetryTimes OPTIONAL
267 if (RetryTimes
!= NULL
) {
271 Value
= *(volatile UINT32
*) Address
;
274 if (Value
== TestValue
) {
278 if ((RetryTimes
!= NULL
) && (*RetryTimes
== 0)) {
281 return EFI_NOT_READY
;
286 Clear the port interrupt and error status. It will also clear
287 HBA interrupt status.
289 @param Port The number of port.
294 AhciClearPortStatus (
301 // Clear any error status
303 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_SERR
;
304 AhciWriteReg (Offset
, AhciReadReg (Offset
));
307 // Clear any port interrupt status
309 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_IS
;
310 AhciWriteReg (Offset
, AhciReadReg (Offset
));
313 // Clear any HBA interrupt status
315 AhciWriteReg (EFI_AHCI_IS_OFFSET
, AhciReadReg (EFI_AHCI_IS_OFFSET
));
319 Enable the FIS running for giving port.
321 @param Port The number of port.
322 @param Timeout The timeout Value of enabling FIS.
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.
331 AhciEnableFisReceive (
338 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_CMD
;
339 AhciOrReg (Offset
, EFI_AHCI_PORT_CMD_FRE
);
341 return AhciWaitMmioSet (
343 EFI_AHCI_PORT_CMD_FR
,
344 EFI_AHCI_PORT_CMD_FR
,
350 Disable the FIS running for giving port.
352 @param Port The number of port.
353 @param Timeout The timeout Value of disabling FIS.
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.
363 AhciDisableFisReceive (
371 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_CMD
;
372 Data
= AhciReadReg (Offset
);
375 // Before disabling Fis receive, the DMA engine of the port should NOT be in running status.
377 if ((Data
& (EFI_AHCI_PORT_CMD_ST
| EFI_AHCI_PORT_CMD_CR
)) != 0) {
378 return EFI_UNSUPPORTED
;
382 // Check if the Fis receive DMA engine for the port is running.
384 if ((Data
& EFI_AHCI_PORT_CMD_FR
) != EFI_AHCI_PORT_CMD_FR
) {
388 AhciAndReg (Offset
, (UINT32
)~(EFI_AHCI_PORT_CMD_FRE
));
390 return AhciWaitMmioSet (
392 EFI_AHCI_PORT_CMD_FR
,
399 Build the command list, command table and prepare the fis receiver.
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.
416 IN EFI_AHCI_REGISTERS
*AhciRegisters
,
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
,
438 PrdtNumber
= DivU64x32 (DataLength
+ EFI_AHCI_MAX_DATA_PER_PRDT
- 1, EFI_AHCI_MAX_DATA_PER_PRDT
);
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
445 ASSERT (PrdtNumber
<= 1);
447 Data64
.Uint64
= (UINTN
) (AhciRegisters
->AhciRFis
);
449 BaseAddr
= Data64
.Uint64
;
451 ZeroMem ((VOID
*)((UINTN
) BaseAddr
), sizeof (EFI_AHCI_RECEIVED_FIS
));
453 ZeroMem (AhciRegisters
->AhciCommandTable
, sizeof (EFI_AHCI_COMMAND_TABLE
));
455 CommandFis
->AhciCFisPmNum
= PortMultiplier
;
457 CopyMem (&AhciRegisters
->AhciCommandTable
->CommandFis
, CommandFis
, sizeof (EFI_AHCI_COMMAND_FIS
));
459 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_CMD
;
460 if (AtapiCommand
!= NULL
) {
462 &AhciRegisters
->AhciCommandTable
->AtapiCmd
,
467 CommandList
->AhciCmdA
= 1;
468 CommandList
->AhciCmdP
= 1;
470 AhciOrReg (Offset
, (EFI_AHCI_PORT_CMD_DLAE
| EFI_AHCI_PORT_CMD_ATAPI
));
472 AhciAndReg (Offset
, (UINT32
)~(EFI_AHCI_PORT_CMD_DLAE
| EFI_AHCI_PORT_CMD_ATAPI
));
475 RemainedData
= (UINTN
) DataLength
;
476 MemAddr
= (UINTN
) DataPhysicalAddr
;
477 CommandList
->AhciCmdPrdtl
= (UINT32
)PrdtNumber
;
479 AhciRegisters
->AhciCommandTable
->PrdtTable
.AhciPrdtDbc
= (UINT32
)RemainedData
- 1;
481 Data64
.Uint64
= (UINT64
)MemAddr
;
482 AhciRegisters
->AhciCommandTable
->PrdtTable
.AhciPrdtDba
= Data64
.Uint32
.Lower32
;
483 AhciRegisters
->AhciCommandTable
->PrdtTable
.AhciPrdtDbau
= Data64
.Uint32
.Upper32
;
486 // Set the last PRDT to Interrupt On Complete
488 AhciRegisters
->AhciCommandTable
->PrdtTable
.AhciPrdtIoc
= 1;
491 (VOID
*) ((UINTN
) AhciRegisters
->AhciCmdList
+ (UINTN
) CommandSlotNumber
* sizeof (EFI_AHCI_COMMAND_LIST
)),
493 sizeof (EFI_AHCI_COMMAND_LIST
)
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
;
506 @param CmdFis A pointer to the EFI_AHCI_COMMAND_FIS Data structure.
507 @param AtaCommandBlock A pointer to the AhciBuildCommandFis Data structure.
512 AhciBuildCommandFis (
513 IN OUT EFI_AHCI_COMMAND_FIS
*CmdFis
,
514 IN EFI_ATA_COMMAND_BLOCK
*AtaCommandBlock
517 ZeroMem (CmdFis
, sizeof (EFI_AHCI_COMMAND_FIS
));
519 CmdFis
->AhciCFisType
= EFI_AHCI_FIS_REGISTER_H2D
;
521 // Indicator it's a command
523 CmdFis
->AhciCFisCmdInd
= 0x1;
524 CmdFis
->AhciCFisCmd
= AtaCommandBlock
->AtaCommand
;
526 CmdFis
->AhciCFisFeature
= AtaCommandBlock
->AtaFeatures
;
527 CmdFis
->AhciCFisFeatureExp
= AtaCommandBlock
->AtaFeaturesExp
;
529 CmdFis
->AhciCFisSecNum
= AtaCommandBlock
->AtaSectorNumber
;
530 CmdFis
->AhciCFisSecNumExp
= AtaCommandBlock
->AtaSectorNumberExp
;
532 CmdFis
->AhciCFisClyLow
= AtaCommandBlock
->AtaCylinderLow
;
533 CmdFis
->AhciCFisClyLowExp
= AtaCommandBlock
->AtaCylinderLowExp
;
535 CmdFis
->AhciCFisClyHigh
= AtaCommandBlock
->AtaCylinderHigh
;
536 CmdFis
->AhciCFisClyHighExp
= AtaCommandBlock
->AtaCylinderHighExp
;
538 CmdFis
->AhciCFisSecCount
= AtaCommandBlock
->AtaSectorCount
;
539 CmdFis
->AhciCFisSecCountExp
= AtaCommandBlock
->AtaSectorCountExp
;
541 CmdFis
->AhciCFisDevHead
= (UINT8
) (AtaCommandBlock
->AtaDeviceHead
| 0xE0);
545 Start a PIO Data transfer on specific port.
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.
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.
568 IN EFI_AHCI_REGISTERS
*AhciRegisters
,
570 IN UINT8 PortMultiplier
,
571 IN EFI_AHCI_ATAPI_COMMAND
*AtapiCommand OPTIONAL
,
572 IN UINT8 AtapiCommandLength
,
574 IN EFI_ATA_COMMAND_BLOCK
*AtaCommandBlock
,
575 IN OUT EFI_ATA_STATUS_BLOCK
*AtaStatusBlock
,
576 IN OUT VOID
*MemoryAddr
,
585 EFI_AHCI_COMMAND_FIS CFis
;
586 EFI_AHCI_COMMAND_LIST CmdList
;
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);
604 // Single task envrionment, we only use one command table for all port
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);
616 // Package read needed
618 AhciBuildCommandFis (&CFis
, AtaCommandBlock
);
620 ZeroMem (&CmdList
, sizeof (EFI_AHCI_COMMAND_LIST
));
622 CmdList
.AhciCmdCfl
= EFI_AHCI_FIS_REGISTER_H2D_LENGTH
/ 4;
623 CmdList
.AhciCmdW
= Read
? 0 : 1;
634 (VOID
*)(UINTN
)MemoryAddr
,
638 Status
= AhciStartCommand (
643 if (EFI_ERROR (Status
)) {
648 // Checking the status and wait the driver sending Data
650 FisBaseAddr
= (UINT32
)(UINTN
)AhciRegisters
->AhciRFis
;
651 if (Read
&& (AtapiCommand
== 0)) {
653 // Wait device sends the PIO setup fis before Data transfer
655 Status
= EFI_TIMEOUT
;
656 Delay
= (UINT32
) (DivU64x32 (Timeout
, 1000) + 1);
658 Offset
= FisBaseAddr
+ EFI_AHCI_PIO_FIS_OFFSET
;
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
);
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.
668 if ((PortTfd
& EFI_AHCI_PORT_TFD_ERR
) != 0) {
669 Status
= EFI_DEVICE_ERROR
;
673 PrdCount
= *(volatile UINT32
*) (&(AhciRegisters
->AhciCmdList
[0].AhciCmdPrdbc
));
674 if (PrdCount
== DataCount
) {
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
;
687 // Stall for 100 microseconds.
689 MicroSecondDelay(100);
695 // Wait for D2H Fis is received
697 Offset
= FisBaseAddr
+ EFI_AHCI_D2H_FIS_OFFSET
;
698 Status
= AhciWaitMemSet (
700 EFI_AHCI_FIS_TYPE_MASK
,
701 EFI_AHCI_FIS_REGISTER_D2H
,
705 if (EFI_ERROR (Status
)) {
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
;
722 AhciDisableFisReceive (
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
);
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
);
741 Stop command running for giving port
743 @param Port The number of port.
744 @param Timeout The timeout Value of stop.
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.
761 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_CMD
;
762 Data
= AhciReadReg (Offset
);
764 if ((Data
& (EFI_AHCI_PORT_CMD_ST
| EFI_AHCI_PORT_CMD_CR
)) == 0) {
768 if ((Data
& EFI_AHCI_PORT_CMD_ST
) != 0) {
769 AhciAndReg (Offset
, (UINT32
)~(EFI_AHCI_PORT_CMD_ST
));
772 return AhciWaitMmioSet (
774 EFI_AHCI_PORT_CMD_CR
,
781 Start command for give slot on specific port.
783 @param Port The number of port.
784 @param CommandSlot The number of CommandSlot.
785 @param Timeout The timeout Value of start.
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.
796 IN UINT8 CommandSlot
,
809 // Collect AHCI controller information
811 Capability
= AhciReadReg(EFI_AHCI_CAPABILITY_OFFSET
);
813 CmdSlotBit
= (UINT32
) (1 << CommandSlot
);
815 AhciClearPortStatus (
819 Status
= AhciEnableFisReceive (
824 if (EFI_ERROR (Status
)) {
828 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_CMD
;
829 PortStatus
= AhciReadReg (Offset
);
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
;
838 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_TFD
;
839 PortTfd
= AhciReadReg (Offset
);
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
);
848 EFI_AHCI_PORT_CMD_COL
,
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
);
859 // Setting the command
861 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_SACT
;
862 AhciAndReg (Offset
, 0);
863 AhciOrReg (Offset
, CmdSlotBit
);
865 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_CI
;
866 AhciAndReg (Offset
, 0);
867 AhciOrReg (Offset
, CmdSlotBit
);
875 @param[in] Timeout The timeout Value of reset.
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.
893 // Collect AHCI controller information
895 Capability
= AhciReadReg (EFI_AHCI_CAPABILITY_OFFSET
);
898 // Enable AE before accessing any AHCI registers if Supports AHCI Mode Only is not set
900 if ((Capability
& EFI_AHCI_CAP_SAM
) == 0) {
901 AhciOrReg (EFI_AHCI_GHC_OFFSET
, EFI_AHCI_GHC_ENABLE
);
904 AhciOrReg (EFI_AHCI_GHC_OFFSET
, EFI_AHCI_GHC_RESET
);
906 Delay
= (UINT32
) (DivU64x32(Timeout
, 1000) + 1);
909 Value
= AhciReadReg(EFI_AHCI_GHC_OFFSET
);
910 if ((Value
& EFI_AHCI_GHC_RESET
) == 0) {
915 // Stall for 100 microseconds.
917 MicroSecondDelay(100);
928 Send Buffer cmd to specific device.
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.
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.
944 IN EFI_AHCI_REGISTERS
*AhciRegisters
,
946 IN UINT8 PortMultiplier
,
947 IN OUT ATA_IDENTIFY_DATA
*Buffer
951 EFI_ATA_COMMAND_BLOCK AtaCommandBlock
;
953 if (AhciRegisters
== NULL
|| Buffer
== NULL
) {
954 return EFI_INVALID_PARAMETER
;
957 ZeroMem (&AtaCommandBlock
, sizeof (EFI_ATA_COMMAND_BLOCK
));
959 AtaCommandBlock
.AtaCommand
= ATA_CMD_IDENTIFY_DRIVE
;
960 AtaCommandBlock
.AtaSectorCount
= 1;
962 Status
= AhciPioTransfer (
972 sizeof (ATA_IDENTIFY_DATA
),
980 Get AHCI mode MMIO Bar Size.
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.
986 @retval The Size of AHCI MMIO BAR.
1000 OldBar
= PciRead32 (PCI_LIB_ADDRESS (Bus
, Device
, Function
, 0x24));
1002 // Disable PCI CMD.MSE bit before calculating MMIO Bar Size as it needs write all 1 to BAR register.
1004 PciAnd32 (PCI_LIB_ADDRESS (Bus
, Device
, Function
, 0x04), (UINT32
)~BIT1
);
1007 // Get AHCI MMIO Bar Size.
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;
1014 // Restore old MMIO Bar.
1016 PciWrite32 (PCI_LIB_ADDRESS (Bus
, Device
, Function
, 0x24), OldBar
);
1018 // Enable PCI CMD.MSE bit after restoring MMIO Bar.
1020 PciOr32 (PCI_LIB_ADDRESS (Bus
, Device
, Function
, 0x04), BIT1
);
1026 This function check if the memory region is in GCD MMIO region.
1028 @param Addr The memory region start address to be checked.
1029 @param Size The memory region length to be checked.
1031 @retval TRUE This memory region is in GCD MMIO region.
1032 @retval FALSE This memory region is not in GCD MMIO region.
1036 OpalIsValidMmioSpace (
1037 IN EFI_PHYSICAL_ADDRESS Addr
,
1042 EFI_GCD_MEMORY_SPACE_DESCRIPTOR
*Desc
;
1044 for (Index
= 0; Index
< mNumberOfDescriptors
; Index
++) {
1045 Desc
= &mGcdMemSpace
[Index
];
1046 if ((Desc
->GcdMemoryType
== EfiGcdMemoryTypeMemoryMappedIo
) && (Addr
>= Desc
->BaseAddress
) && ((Addr
+ Size
) <= (Desc
->BaseAddress
+ Desc
->Length
))) {
1054 Get AHCI mode base address registers' Value.
1056 @param[in] Bus The bus number of ata host controller.
1057 @param[in] Device The device number of ata host controller.
1058 @param[in] Function The function number of ata host controller.
1060 @retval EFI_UNSUPPORTED Return this Value when the BARs is not IO type
1061 @retval EFI_SUCCESS Get the Base address successfully
1062 @retval Other Read the pci configureation Data error
1067 GetAhciBaseAddress (
1076 // Get AHCI MMIO Bar
1078 mAhciBar
= PciRead32 (PCI_LIB_ADDRESS (Bus
, Device
, Function
, 0x24));
1080 // Get AHCI MMIO Bar Size
1082 Size
= GetAhciBarSize (Bus
, Device
, Function
);
1084 // Check if the AHCI Bar region is in SMRAM to avoid malicious attack by modifying MMIO Bar to point to SMRAM.
1086 if (!OpalIsValidMmioSpace ((EFI_PHYSICAL_ADDRESS
)mAhciBar
, Size
)) {
1087 return EFI_UNSUPPORTED
;
1094 Allocate transfer-related Data struct which is used at AHCI mode.
1096 @retval EFI_OUT_OF_RESOURCE The allocation is failure.
1097 @retval EFI_SUCCESS Successful to allocate memory.
1102 AhciAllocateResource (
1107 EFI_PHYSICAL_ADDRESS Base
;
1110 // Allocate resources required by AHCI host controller.
1113 Status
= gBS
->AllocatePages (
1116 EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS
)),
1119 if (EFI_ERROR (Status
)) {
1120 return EFI_OUT_OF_RESOURCES
;
1123 ZeroMem ((VOID
*)(UINTN
)Base
, EFI_PAGE_SIZE
* EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS
)));
1124 mAhciRegisters
.AhciRFis
= (VOID
*)(UINTN
)Base
;
1127 Status
= gBS
->AllocatePages (
1130 EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_LIST
)),
1133 if (EFI_ERROR (Status
)) {
1134 gBS
->FreePages ((EFI_PHYSICAL_ADDRESS
)(UINTN
)mAhciRegisters
.AhciRFis
, EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS
)));
1135 return EFI_OUT_OF_RESOURCES
;
1137 ZeroMem ((VOID
*)(UINTN
)Base
, EFI_PAGE_SIZE
* EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_LIST
)));
1138 mAhciRegisters
.AhciCmdList
= (VOID
*)(UINTN
)Base
;
1141 Status
= gBS
->AllocatePages (
1144 EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_TABLE
)),
1147 if (EFI_ERROR (Status
)) {
1148 gBS
->FreePages ((EFI_PHYSICAL_ADDRESS
)(UINTN
)mAhciRegisters
.AhciRFis
, EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS
)));
1149 gBS
->FreePages ((EFI_PHYSICAL_ADDRESS
)(UINTN
)mAhciRegisters
.AhciCmdList
, EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_LIST
)));
1150 return EFI_OUT_OF_RESOURCES
;
1152 ZeroMem ((VOID
*)(UINTN
)Base
, EFI_PAGE_SIZE
* EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_TABLE
)));
1153 mAhciRegisters
.AhciCommandTable
= (VOID
*)(UINTN
)Base
;
1158 Free allocated transfer-related Data struct which is used at AHCI mode.
1167 if (mAhciRegisters
.AhciRFis
!= NULL
) {
1168 gBS
->FreePages ((EFI_PHYSICAL_ADDRESS
)(UINTN
)mAhciRegisters
.AhciRFis
, EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS
)));
1171 if (mAhciRegisters
.AhciCmdList
!= NULL
) {
1172 gBS
->FreePages ((EFI_PHYSICAL_ADDRESS
)(UINTN
)mAhciRegisters
.AhciCmdList
, EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_LIST
)));
1175 if (mAhciRegisters
.AhciCommandTable
!= NULL
) {
1176 gBS
->FreePages ((EFI_PHYSICAL_ADDRESS
)(UINTN
)mAhciRegisters
.AhciCommandTable
, EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_TABLE
)));
1181 Initialize ATA host controller at AHCI mode.
1183 The function is designed to initialize ATA host controller.
1185 @param[in] Port The port number to do initialization.
1190 AhciModeInitialize (
1198 UINT32 PhyDetectDelay
;
1200 Status
= AhciReset (ATA_TIMEOUT
);
1201 if (EFI_ERROR (Status
)) {
1206 // Collect AHCI controller information
1208 Capability
= AhciReadReg (EFI_AHCI_CAPABILITY_OFFSET
);
1211 // Enable AE before accessing any AHCI registers if Supports AHCI Mode Only is not set
1213 if ((Capability
& EFI_AHCI_CAP_SAM
) == 0) {
1214 AhciOrReg (EFI_AHCI_GHC_OFFSET
, EFI_AHCI_GHC_ENABLE
);
1217 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_FB
;
1218 AhciWriteReg (Offset
, (UINT32
)(UINTN
)mAhciRegisters
.AhciRFis
);
1221 // Single task envrionment, we only use one command table for all port
1223 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_CLB
;
1224 AhciWriteReg (Offset
, (UINT32
)(UINTN
)mAhciRegisters
.AhciCmdList
);
1226 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_CMD
;
1227 Data
= AhciReadReg (Offset
);
1228 if ((Data
& EFI_AHCI_PORT_CMD_CPD
) != 0) {
1229 AhciOrReg (Offset
, EFI_AHCI_PORT_CMD_POD
);
1232 if ((Capability
& BIT27
) != 0) {
1233 AhciOrReg (Offset
, EFI_AHCI_PORT_CMD_SUD
);
1237 // Disable aggressive power management.
1239 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_SCTL
;
1240 AhciOrReg (Offset
, EFI_AHCI_PORT_SCTL_IPM_INIT
);
1242 // Disable the reporting of the corresponding interrupt to system software.
1244 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_IE
;
1245 AhciAndReg (Offset
, 0);
1247 Status
= AhciEnableFisReceive (
1249 EFI_TIMER_PERIOD_MILLISECONDS(500)
1251 ASSERT_EFI_ERROR (Status
);
1252 if (EFI_ERROR (Status
)) {
1257 // According to SATA1.0a spec section 5.2, we need to wait for PxTFD.BSY and PxTFD.DRQ
1258 // and PxTFD.ERR to be zero. The maximum wait time is 16s which is defined at ATA spec.
1260 PhyDetectDelay
= 16 * 1000;
1262 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_SERR
;
1263 if (AhciReadReg(Offset
) != 0) {
1264 AhciWriteReg (Offset
, AhciReadReg(Offset
));
1266 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_TFD
;
1268 Data
= AhciReadReg (Offset
) & EFI_AHCI_PORT_TFD_MASK
;
1273 MicroSecondDelay (1000);
1275 } while (PhyDetectDelay
> 0);
1277 if (PhyDetectDelay
== 0) {
1278 return EFI_NOT_FOUND
;
1281 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_SIG
;
1282 Status
= AhciWaitMmioSet (
1286 EFI_TIMER_PERIOD_SECONDS(16)
1289 if (EFI_ERROR (Status
)) {