Home | History | Annotate | Download | only in OpalPasswordSmm
      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   This function check if the memory region is in GCD MMIO region.
   1027 
   1028   @param Addr  The memory region start address to be checked.
   1029   @param Size  The memory region length to be checked.
   1030 
   1031   @retval TRUE  This memory region is in GCD MMIO region.
   1032   @retval FALSE This memory region is not in GCD MMIO region.
   1033 **/
   1034 BOOLEAN
   1035 EFIAPI
   1036 OpalIsValidMmioSpace (
   1037   IN  EFI_PHYSICAL_ADDRESS       Addr,
   1038   IN  UINTN                      Size
   1039   )
   1040 {
   1041   UINTN                           Index;
   1042   EFI_GCD_MEMORY_SPACE_DESCRIPTOR *Desc;
   1043 
   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))) {
   1047       return TRUE;
   1048     }
   1049   }
   1050 
   1051   return FALSE;
   1052 }
   1053 /**
   1054   Get AHCI mode base address registers' Value.
   1055 
   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.
   1059 
   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
   1063 
   1064 **/
   1065 EFI_STATUS
   1066 EFIAPI
   1067 GetAhciBaseAddress (
   1068   IN     UINTN                       Bus,
   1069   IN     UINTN                       Device,
   1070   IN     UINTN                       Function
   1071   )
   1072 {
   1073   UINT32  Size;
   1074 
   1075   //
   1076   // Get AHCI MMIO Bar
   1077   //
   1078   mAhciBar = PciRead32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x24));
   1079   //
   1080   // Get AHCI MMIO Bar Size
   1081   //
   1082   Size = GetAhciBarSize (Bus, Device, Function);
   1083   //
   1084   // Check if the AHCI Bar region is in SMRAM to avoid malicious attack by modifying MMIO Bar to point to SMRAM.
   1085   //
   1086   if (!OpalIsValidMmioSpace ((EFI_PHYSICAL_ADDRESS)mAhciBar, Size)) {
   1087     return EFI_UNSUPPORTED;
   1088   }
   1089 
   1090   return EFI_SUCCESS;
   1091 }
   1092 
   1093 /**
   1094   Allocate transfer-related Data struct which is used at AHCI mode.
   1095 
   1096   @retval  EFI_OUT_OF_RESOURCE   The allocation is failure.
   1097   @retval  EFI_SUCCESS           Successful to allocate memory.
   1098 
   1099 **/
   1100 EFI_STATUS
   1101 EFIAPI
   1102 AhciAllocateResource (
   1103   VOID
   1104   )
   1105 {
   1106   EFI_STATUS            Status;
   1107   EFI_PHYSICAL_ADDRESS  Base;
   1108 
   1109   //
   1110   // Allocate resources required by AHCI host controller.
   1111   //
   1112   Base = 0xFFFFFFFF;
   1113   Status = gBS->AllocatePages (
   1114                   AllocateMaxAddress,
   1115                   EfiACPIMemoryNVS,
   1116                   EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS)),
   1117                   &Base
   1118                   );
   1119   if (EFI_ERROR (Status)) {
   1120     return EFI_OUT_OF_RESOURCES;
   1121   }
   1122 
   1123   ZeroMem ((VOID *)(UINTN)Base, EFI_PAGE_SIZE * EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS)));
   1124   mAhciRegisters.AhciRFis = (VOID *)(UINTN)Base;
   1125 
   1126   Base = 0xFFFFFFFF;
   1127   Status = gBS->AllocatePages (
   1128                   AllocateMaxAddress,
   1129                   EfiACPIMemoryNVS,
   1130                   EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_LIST)),
   1131                   &Base
   1132                   );
   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;
   1136   }
   1137   ZeroMem ((VOID *)(UINTN)Base, EFI_PAGE_SIZE * EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_LIST)));
   1138   mAhciRegisters.AhciCmdList = (VOID *)(UINTN)Base;
   1139 
   1140   Base = 0xFFFFFFFF;
   1141   Status = gBS->AllocatePages (
   1142                   AllocateMaxAddress,
   1143                   EfiACPIMemoryNVS,
   1144                   EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_TABLE)),
   1145                   &Base
   1146                   );
   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;
   1151   }
   1152   ZeroMem ((VOID *)(UINTN)Base, EFI_PAGE_SIZE * EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_TABLE)));
   1153   mAhciRegisters.AhciCommandTable = (VOID *)(UINTN)Base;
   1154   return EFI_SUCCESS;
   1155 }
   1156 
   1157 /**
   1158   Free allocated transfer-related Data struct which is used at AHCI mode.
   1159 
   1160 **/
   1161 VOID
   1162 EFIAPI
   1163 AhciFreeResource (
   1164   VOID
   1165   )
   1166 {
   1167   if (mAhciRegisters.AhciRFis != NULL) {
   1168     gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)mAhciRegisters.AhciRFis, EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS)));
   1169   }
   1170 
   1171   if (mAhciRegisters.AhciCmdList != NULL) {
   1172     gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)mAhciRegisters.AhciCmdList, EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_LIST)));
   1173   }
   1174 
   1175   if (mAhciRegisters.AhciCommandTable != NULL) {
   1176     gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)mAhciRegisters.AhciCommandTable, EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_TABLE)));
   1177   }
   1178 }
   1179 
   1180 /**
   1181   Initialize ATA host controller at AHCI mode.
   1182 
   1183   The function is designed to initialize ATA host controller.
   1184 
   1185   @param[in]  Port          The port number to do initialization.
   1186 
   1187 **/
   1188 EFI_STATUS
   1189 EFIAPI
   1190 AhciModeInitialize (
   1191   UINT8              Port
   1192   )
   1193 {
   1194   EFI_STATUS         Status;
   1195   UINT32             Capability;
   1196   UINT32             Offset;
   1197   UINT32             Data;
   1198   UINT32             PhyDetectDelay;
   1199 
   1200   Status = AhciReset (ATA_TIMEOUT);
   1201   if (EFI_ERROR (Status)) {
   1202     return Status;
   1203   }
   1204 
   1205   //
   1206   // Collect AHCI controller information
   1207   //
   1208   Capability = AhciReadReg (EFI_AHCI_CAPABILITY_OFFSET);
   1209 
   1210   //
   1211   // Enable AE before accessing any AHCI registers if Supports AHCI Mode Only is not set
   1212   //
   1213   if ((Capability & EFI_AHCI_CAP_SAM) == 0) {
   1214     AhciOrReg (EFI_AHCI_GHC_OFFSET, EFI_AHCI_GHC_ENABLE);
   1215   }
   1216 
   1217   Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FB;
   1218   AhciWriteReg (Offset, (UINT32)(UINTN)mAhciRegisters.AhciRFis);
   1219 
   1220   //
   1221   // Single task envrionment, we only use one command table for all port
   1222   //
   1223   Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLB;
   1224   AhciWriteReg (Offset, (UINT32)(UINTN)mAhciRegisters.AhciCmdList);
   1225 
   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);
   1230   }
   1231 
   1232   if ((Capability & BIT27) != 0) {
   1233     AhciOrReg (Offset, EFI_AHCI_PORT_CMD_SUD);
   1234   }
   1235 
   1236   //
   1237   // Disable aggressive power management.
   1238   //
   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);
   1241   //
   1242   // Disable the reporting of the corresponding interrupt to system software.
   1243   //
   1244   Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_IE;
   1245   AhciAndReg (Offset, 0);
   1246 
   1247   Status = AhciEnableFisReceive (
   1248              Port,
   1249              EFI_TIMER_PERIOD_MILLISECONDS(500)
   1250              );
   1251   ASSERT_EFI_ERROR (Status);
   1252   if (EFI_ERROR (Status)) {
   1253     return Status;
   1254   }
   1255 
   1256   //
   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.
   1259   //
   1260   PhyDetectDelay = 16 * 1000;
   1261   do {
   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));
   1265     }
   1266     Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;
   1267 
   1268     Data = AhciReadReg (Offset) & EFI_AHCI_PORT_TFD_MASK;
   1269     if (Data == 0) {
   1270       break;
   1271     }
   1272 
   1273     MicroSecondDelay (1000);
   1274     PhyDetectDelay--;
   1275   } while (PhyDetectDelay > 0);
   1276 
   1277   if (PhyDetectDelay == 0) {
   1278     return EFI_NOT_FOUND;
   1279   }
   1280 
   1281   Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SIG;
   1282   Status = AhciWaitMmioSet (
   1283              Offset,
   1284              0x0000FFFF,
   1285              0x00000101,
   1286              EFI_TIMER_PERIOD_SECONDS(16)
   1287              );
   1288 
   1289   if (EFI_ERROR (Status)) {
   1290     return Status;
   1291   }
   1292 
   1293   return Status;
   1294 }
   1295 
   1296