]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Bus/Ata/AhciPei/AhciMode.c
MdeModulePkg/AhciPei: Fix device cannot be found in non-S3 path
[mirror_edk2.git] / MdeModulePkg / Bus / Ata / AhciPei / AhciMode.c
index a83c213a474d4722a0cba175db421442958ba0ea..8c491bd138656e2d62463cfd84337f405a0204cc 100644 (file)
@@ -48,7 +48,13 @@ UINT8  mAtaTrustCommands[2] = {
 // Look up table (Lba48Bit) for maximum transfer block number\r
 //\r
 #define MAX_28BIT_TRANSFER_BLOCK_NUM     0x100\r
-#define MAX_48BIT_TRANSFER_BLOCK_NUM     0xFFFF\r
+//\r
+// Due to limited resource for VTd PEI DMA buffer on platforms, the driver\r
+// limits the maximum transfer block number for 48-bit addressing.\r
+// Here, setting to 0x800 means that for device with 512-byte block size, the\r
+// maximum buffer for DMA mapping will be 1M bytes in size.\r
+//\r
+#define MAX_48BIT_TRANSFER_BLOCK_NUM     0x800\r
 \r
 UINT32 mMaxTransferBlockNumber[2] = {\r
   MAX_28BIT_TRANSFER_BLOCK_NUM,\r
@@ -1707,7 +1713,7 @@ AhciModeInitialization (
   MaxPortNumber = MIN (MaxPortNumber, (UINT8)(UINTN)(HighBitSet32(PortImplementBitMap) + 1));\r
   MaxPortNumber = MIN (MaxPortNumber, AhciGetNumberOfPortsFromMap (Private->PortBitMap));\r
 \r
-  PortInitializeBitMap = Private->PortBitMap;\r
+  PortInitializeBitMap = Private->PortBitMap & PortImplementBitMap;\r
   AhciRegisters        = &Private->AhciRegisters;\r
   DeviceIndex          = 0;\r
   //\r
@@ -1715,6 +1721,13 @@ AhciModeInitialization (
   //\r
   for (PortIndex = 1; PortIndex <= MaxPortNumber; PortIndex ++) {\r
     Status = AhciGetPortFromMap (PortInitializeBitMap, PortIndex, &Port);\r
+    if (EFI_ERROR (Status)) {\r
+      //\r
+      // No more available port, just break out of the loop.\r
+      //\r
+      break;\r
+    }\r
+\r
     if ((PortImplementBitMap & (BIT0 << Port)) != 0) {\r
       //\r
       // Initialize FIS Base Address Register and Command List Base Address\r
@@ -1866,6 +1879,124 @@ AhciModeInitialization (
   return EFI_SUCCESS;\r
 }\r
 \r
+/**\r
+  Transfer data from ATA device.\r
+\r
+  This function performs one ATA pass through transaction to transfer data from/to\r
+  ATA device. It chooses the appropriate ATA command and protocol to invoke PassThru\r
+  interface of ATA pass through.\r
+\r
+  @param[in]     DeviceData        A pointer to PEI_AHCI_ATA_DEVICE_DATA structure.\r
+  @param[in,out] Buffer            The pointer to the current transaction buffer.\r
+  @param[in]     StartLba          The starting logical block address to be accessed.\r
+  @param[in]     TransferLength    The block number or sector count of the transfer.\r
+  @param[in]     IsWrite           Indicates whether it is a write operation.\r
+\r
+  @retval EFI_SUCCESS    The data transfer is complete successfully.\r
+  @return others         Some error occurs when transferring data.\r
+\r
+**/\r
+EFI_STATUS\r
+TransferAtaDevice (\r
+  IN     PEI_AHCI_ATA_DEVICE_DATA    *DeviceData,\r
+  IN OUT VOID                        *Buffer,\r
+  IN     EFI_LBA                     StartLba,\r
+  IN     UINT32                      TransferLength,\r
+  IN     BOOLEAN                     IsWrite\r
+  )\r
+{\r
+  PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private;\r
+  EDKII_PEI_ATA_PASS_THRU_PPI         *AtaPassThru;\r
+  EFI_ATA_COMMAND_BLOCK               Acb;\r
+  EFI_ATA_PASS_THRU_COMMAND_PACKET    Packet;\r
+\r
+  Private     = DeviceData->Private;\r
+  AtaPassThru = &Private->AtaPassThruPpi;\r
+\r
+  //\r
+  // Ensure Lba48Bit and IsWrite are valid boolean values\r
+  //\r
+  ASSERT ((UINTN) DeviceData->Lba48Bit < 2);\r
+  ASSERT ((UINTN) IsWrite < 2);\r
+  if (((UINTN) DeviceData->Lba48Bit >= 2) ||\r
+      ((UINTN) IsWrite >= 2)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Prepare for ATA command block.\r
+  //\r
+  ZeroMem (&Acb, sizeof (EFI_ATA_COMMAND_BLOCK));\r
+  Acb.AtaCommand = mAtaCommands[DeviceData->Lba48Bit][IsWrite];\r
+  Acb.AtaSectorNumber = (UINT8) StartLba;\r
+  Acb.AtaCylinderLow  = (UINT8) RShiftU64 (StartLba, 8);\r
+  Acb.AtaCylinderHigh = (UINT8) RShiftU64 (StartLba, 16);\r
+  Acb.AtaDeviceHead   = (UINT8) (BIT7 | BIT6 | BIT5 |\r
+                                 (DeviceData->PortMultiplier == 0xFFFF ?\r
+                                 0 : (DeviceData->PortMultiplier << 4)));\r
+  Acb.AtaSectorCount  = (UINT8) TransferLength;\r
+  if (DeviceData->Lba48Bit) {\r
+    Acb.AtaSectorNumberExp = (UINT8) RShiftU64 (StartLba, 24);\r
+    Acb.AtaCylinderLowExp  = (UINT8) RShiftU64 (StartLba, 32);\r
+    Acb.AtaCylinderHighExp = (UINT8) RShiftU64 (StartLba, 40);\r
+    Acb.AtaSectorCountExp  = (UINT8) (TransferLength >> 8);\r
+  } else {\r
+    Acb.AtaDeviceHead      = (UINT8) (Acb.AtaDeviceHead | RShiftU64 (StartLba, 24));\r
+  }\r
+\r
+  //\r
+  // Prepare for ATA pass through packet.\r
+  //\r
+  ZeroMem (&Packet, sizeof (EFI_ATA_PASS_THRU_COMMAND_PACKET));\r
+  if (IsWrite) {\r
+    Packet.OutDataBuffer     = Buffer;\r
+    Packet.OutTransferLength = TransferLength;\r
+  } else {\r
+    Packet.InDataBuffer      = Buffer;\r
+    Packet.InTransferLength  = TransferLength;\r
+  }\r
+  Packet.Asb      = NULL;\r
+  Packet.Acb      = &Acb;\r
+  Packet.Protocol = mAtaPassThruCmdProtocols[IsWrite];\r
+  Packet.Length   = EFI_ATA_PASS_THRU_LENGTH_SECTOR_COUNT;\r
+  //\r
+  // |------------------------|-----------------|\r
+  // | ATA PIO Transfer Mode  |  Transfer Rate  |\r
+  // |------------------------|-----------------|\r
+  // |       PIO Mode 0       |  3.3Mbytes/sec  |\r
+  // |------------------------|-----------------|\r
+  // |       PIO Mode 1       |  5.2Mbytes/sec  |\r
+  // |------------------------|-----------------|\r
+  // |       PIO Mode 2       |  8.3Mbytes/sec  |\r
+  // |------------------------|-----------------|\r
+  // |       PIO Mode 3       | 11.1Mbytes/sec  |\r
+  // |------------------------|-----------------|\r
+  // |       PIO Mode 4       | 16.6Mbytes/sec  |\r
+  // |------------------------|-----------------|\r
+  //\r
+  // As AtaBus is used to manage ATA devices, we have to use the lowest transfer\r
+  // rate to calculate the possible maximum timeout value for each read/write\r
+  // operation. The timout value is rounded up to nearest integar and here an\r
+  // additional 30s is added to follow ATA spec in which it mentioned that the\r
+  // device may take up to 30s to respond commands in the Standby/Idle mode.\r
+  //\r
+  // Calculate the maximum timeout value for PIO read/write operation.\r
+  //\r
+  Packet.Timeout = TIMER_PERIOD_SECONDS (\r
+                     DivU64x32 (\r
+                       MultU64x32 (TransferLength, DeviceData->Media.BlockSize),\r
+                       3300000\r
+                       ) + 31\r
+                     );\r
+\r
+  return AtaPassThru->PassThru (\r
+                        AtaPassThru,\r
+                        DeviceData->Port,\r
+                        DeviceData->PortMultiplier,\r
+                        &Packet\r
+                        );\r
+}\r
+\r
 /**\r
   Trust transfer data from/to ATA device.\r
 \r