]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Bus/Ata/AhciPei/AhciPeiBlockIo.c
MdeModulePkg/AhciPei: Add PEI BlockIO support
[mirror_edk2.git] / MdeModulePkg / Bus / Ata / AhciPei / AhciPeiBlockIo.c
diff --git a/MdeModulePkg/Bus/Ata/AhciPei/AhciPeiBlockIo.c b/MdeModulePkg/Bus/Ata/AhciPei/AhciPeiBlockIo.c
new file mode 100644 (file)
index 0000000..e7c7a39
--- /dev/null
@@ -0,0 +1,516 @@
+/** @file\r
+  The AhciPei driver is used to manage ATA hard disk device working under AHCI\r
+  mode at PEI phase.\r
+\r
+  Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>\r
+\r
+  SPDX-License-Identifier: BSD-2-Clause-Patent\r
+\r
+**/\r
+\r
+#include "AhciPei.h"\r
+\r
+/**\r
+  Traverse the attached ATA devices list to find out the device with given index.\r
+\r
+  @param[in] Private        A pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA\r
+                            instance.\r
+  @param[in] DeviceIndex    The device index.\r
+\r
+  @retval    The pointer to the PEI_AHCI_ATA_DEVICE_DATA structure of the device\r
+             info to access.\r
+\r
+**/\r
+PEI_AHCI_ATA_DEVICE_DATA *\r
+SearchDeviceByIndex (\r
+  IN PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private,\r
+  IN UINTN                               DeviceIndex\r
+  )\r
+{\r
+  PEI_AHCI_ATA_DEVICE_DATA    *DeviceData;\r
+  LIST_ENTRY                  *Node;\r
+\r
+  if ((DeviceIndex == 0) || (DeviceIndex > Private->ActiveDevices)) {\r
+    return NULL;\r
+  }\r
+\r
+  Node = GetFirstNode (&Private->DeviceList);\r
+  while (!IsNull (&Private->DeviceList, Node)) {\r
+    DeviceData = AHCI_PEI_ATA_DEVICE_INFO_FROM_THIS (Node);\r
+\r
+    if (DeviceData->DeviceIndex == DeviceIndex) {\r
+      return DeviceData;\r
+    }\r
+\r
+    Node = GetNextNode (&Private->DeviceList, Node);\r
+  }\r
+\r
+  return NULL;\r
+}\r
+\r
+/**\r
+  Read a number of blocks from ATA device.\r
+\r
+  This function performs ATA pass through transactions to read data from ATA device.\r
+  It may separate the read request into several ATA pass through transactions.\r
+\r
+  @param[in]     DeviceData        The pointer to the PEI_AHCI_ATA_DEVICE_DATA\r
+                                   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]     NumberOfBlocks    The block number or sector count of the transfer.\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
+AccessAtaDevice (\r
+  IN     PEI_AHCI_ATA_DEVICE_DATA    *DeviceData,\r
+  IN OUT UINT8                       *Buffer,\r
+  IN     EFI_LBA                     StartLba,\r
+  IN     UINTN                       NumberOfBlocks\r
+  )\r
+{\r
+  EFI_STATUS    Status;\r
+  UINTN         MaxTransferBlockNumber;\r
+  UINTN         TransferBlockNumber;\r
+  UINTN         BlockSize;\r
+\r
+  //\r
+  // Ensure Lba48Bit is a valid boolean value\r
+  //\r
+  ASSERT ((UINTN) DeviceData->Lba48Bit < 2);\r
+  if ((UINTN) DeviceData->Lba48Bit >= 2) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Status = EFI_SUCCESS;\r
+  MaxTransferBlockNumber = mMaxTransferBlockNumber[DeviceData->Lba48Bit];\r
+  BlockSize              = DeviceData->Media.BlockSize;\r
+\r
+  do {\r
+    if (NumberOfBlocks > MaxTransferBlockNumber) {\r
+      TransferBlockNumber = MaxTransferBlockNumber;\r
+      NumberOfBlocks     -= MaxTransferBlockNumber;\r
+    } else  {\r
+      TransferBlockNumber = NumberOfBlocks;\r
+      NumberOfBlocks      = 0;\r
+    }\r
+    DEBUG ((\r
+      DEBUG_BLKIO, "%a: Blocking AccessAtaDevice, TransferBlockNumber = %x; StartLba = %x\n",\r
+      __FUNCTION__, TransferBlockNumber, StartLba\r
+      ));\r
+\r
+    Status = TransferAtaDevice (\r
+               DeviceData,\r
+               Buffer,\r
+               StartLba,\r
+               (UINT32) TransferBlockNumber,\r
+               FALSE  // Read\r
+               );\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+\r
+    StartLba += TransferBlockNumber;\r
+    Buffer   += TransferBlockNumber * BlockSize;\r
+  } while (NumberOfBlocks > 0);\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Read specified bytes from Lba from the device.\r
+\r
+  @param[in]  DeviceData    The pointer to the PEI_AHCI_ATA_DEVICE_DATA data structure.\r
+  @param[out] Buffer        The Buffer used to store the Data read from the device.\r
+  @param[in]  StartLba      The start block number.\r
+  @param[in]  BufferSize    Total bytes to be read.\r
+\r
+  @retval EFI_SUCCESS    Data are read from the device.\r
+  @retval Others         Fail to read all the data.\r
+\r
+**/\r
+EFI_STATUS\r
+AhciRead (\r
+  IN  PEI_AHCI_ATA_DEVICE_DATA    *DeviceData,\r
+  OUT VOID                        *Buffer,\r
+  IN  EFI_LBA                     StartLba,\r
+  IN  UINTN                       BufferSize\r
+  )\r
+{\r
+  EFI_STATUS    Status;\r
+  UINTN         BlockSize;\r
+  UINTN         NumberOfBlocks;\r
+\r
+  //\r
+  // Check parameters.\r
+  //\r
+  if (Buffer == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (BufferSize == 0) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  BlockSize = DeviceData->Media.BlockSize;\r
+  if ((BufferSize % BlockSize) != 0) {\r
+    return EFI_BAD_BUFFER_SIZE;\r
+  }\r
+\r
+  if (StartLba > DeviceData->Media.LastBlock) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  NumberOfBlocks  = BufferSize / BlockSize;\r
+  if (NumberOfBlocks - 1 > DeviceData->Media.LastBlock - StartLba) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Invoke low level AtaDevice Access Routine.\r
+  //\r
+  Status = AccessAtaDevice (DeviceData, Buffer, StartLba, NumberOfBlocks);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Gets the count of block I/O devices that one specific block driver detects.\r
+\r
+  This function is used for getting the count of block I/O devices that one\r
+  specific block driver detects. If no device is detected, then the function\r
+  will return zero.\r
+\r
+  @param[in]  PeiServices          General-purpose services that are available\r
+                                   to every PEIM.\r
+  @param[in]  This                 Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI\r
+                                   instance.\r
+  @param[out] NumberBlockDevices   The number of block I/O devices discovered.\r
+\r
+  @retval     EFI_SUCCESS          The operation performed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AhciBlockIoGetDeviceNo (\r
+  IN  EFI_PEI_SERVICES               **PeiServices,\r
+  IN  EFI_PEI_RECOVERY_BLOCK_IO_PPI  *This,\r
+  OUT UINTN                          *NumberBlockDevices\r
+  )\r
+{\r
+  PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private;\r
+\r
+  if (This == NULL || NumberBlockDevices == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Private = GET_AHCI_PEIM_HC_PRIVATE_DATA_FROM_THIS_BLKIO (This);\r
+  *NumberBlockDevices = Private->ActiveDevices;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Gets a block device's media information.\r
+\r
+  This function will provide the caller with the specified block device's media\r
+  information. If the media changes, calling this function will update the media\r
+  information accordingly.\r
+\r
+  @param[in]  PeiServices   General-purpose services that are available to every\r
+                            PEIM\r
+  @param[in]  This          Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance.\r
+  @param[in]  DeviceIndex   Specifies the block device to which the function wants\r
+                            to talk. Because the driver that implements Block I/O\r
+                            PPIs will manage multiple block devices, the PPIs that\r
+                            want to talk to a single device must specify the\r
+                            device index that was assigned during the enumeration\r
+                            process. This index is a number from one to\r
+                            NumberBlockDevices.\r
+  @param[out] MediaInfo     The media information of the specified block media.\r
+                            The caller is responsible for the ownership of this\r
+                            data structure.\r
+\r
+  @par Note:\r
+      The MediaInfo structure describes an enumeration of possible block device\r
+      types.  This enumeration exists because no device paths are actually passed\r
+      across interfaces that describe the type or class of hardware that is publishing\r
+      the block I/O interface. This enumeration will allow for policy decisions\r
+      in the Recovery PEIM, such as "Try to recover from legacy floppy first,\r
+      LS-120 second, CD-ROM third." If there are multiple partitions abstracted\r
+      by a given device type, they should be reported in ascending order; this\r
+      order also applies to nested partitions, such as legacy MBR, where the\r
+      outermost partitions would have precedence in the reporting order. The\r
+      same logic applies to systems such as IDE that have precedence relationships\r
+      like "Master/Slave" or "Primary/Secondary". The master device should be\r
+      reported first, the slave second.\r
+\r
+  @retval EFI_SUCCESS        Media information about the specified block device\r
+                             was obtained successfully.\r
+  @retval EFI_DEVICE_ERROR   Cannot get the media information due to a hardware\r
+                             error.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AhciBlockIoGetMediaInfo (\r
+  IN  EFI_PEI_SERVICES               **PeiServices,\r
+  IN  EFI_PEI_RECOVERY_BLOCK_IO_PPI  *This,\r
+  IN  UINTN                          DeviceIndex,\r
+  OUT EFI_PEI_BLOCK_IO_MEDIA         *MediaInfo\r
+  )\r
+{\r
+  PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private;\r
+  PEI_AHCI_ATA_DEVICE_DATA            *DeviceData;\r
+\r
+  if (This == NULL || MediaInfo == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Private    = GET_AHCI_PEIM_HC_PRIVATE_DATA_FROM_THIS_BLKIO (This);\r
+  DeviceData = SearchDeviceByIndex (Private, DeviceIndex);\r
+  if (DeviceData == NULL) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  MediaInfo->DeviceType   = (EFI_PEI_BLOCK_DEVICE_TYPE) EDKII_PEI_BLOCK_DEVICE_TYPE_ATA_HARD_DISK;\r
+  MediaInfo->MediaPresent = TRUE;\r
+  MediaInfo->LastBlock    = (UINTN) DeviceData->Media.LastBlock;\r
+  MediaInfo->BlockSize    = DeviceData->Media.BlockSize;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Reads the requested number of blocks from the specified block device.\r
+\r
+  The function reads the requested number of blocks from the device. All the\r
+  blocks are read, or an error is returned. If there is no media in the device,\r
+  the function returns EFI_NO_MEDIA.\r
+\r
+  @param[in]  PeiServices   General-purpose services that are available to\r
+                            every PEIM.\r
+  @param[in]  This          Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance.\r
+  @param[in]  DeviceIndex   Specifies the block device to which the function wants\r
+                            to talk. Because the driver that implements Block I/O\r
+                            PPIs will manage multiple block devices, PPIs that\r
+                            want to talk to a single device must specify the device\r
+                            index that was assigned during the enumeration process.\r
+                            This index is a number from one to NumberBlockDevices.\r
+  @param[in]  StartLBA      The starting logical block address (LBA) to read from\r
+                            on the device\r
+  @param[in]  BufferSize    The size of the Buffer in bytes. This number must be\r
+                            a multiple of the intrinsic block size of the device.\r
+  @param[out] Buffer        A pointer to the destination buffer for the data.\r
+                            The caller is responsible for the ownership of the\r
+                            buffer.\r
+\r
+  @retval EFI_SUCCESS             The data was read correctly from the device.\r
+  @retval EFI_DEVICE_ERROR        The device reported an error while attempting\r
+                                  to perform the read operation.\r
+  @retval EFI_INVALID_PARAMETER   The read request contains LBAs that are not\r
+                                  valid, or the buffer is not properly aligned.\r
+  @retval EFI_NO_MEDIA            There is no media in the device.\r
+  @retval EFI_BAD_BUFFER_SIZE     The BufferSize parameter is not a multiple of\r
+                                  the intrinsic block size of the device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AhciBlockIoReadBlocks (\r
+  IN  EFI_PEI_SERVICES               **PeiServices,\r
+  IN  EFI_PEI_RECOVERY_BLOCK_IO_PPI  *This,\r
+  IN  UINTN                          DeviceIndex,\r
+  IN  EFI_PEI_LBA                    StartLBA,\r
+  IN  UINTN                          BufferSize,\r
+  OUT VOID                           *Buffer\r
+  )\r
+{\r
+  PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private;\r
+  PEI_AHCI_ATA_DEVICE_DATA            *DeviceData;\r
+\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Private    = GET_AHCI_PEIM_HC_PRIVATE_DATA_FROM_THIS_BLKIO (This);\r
+  DeviceData = SearchDeviceByIndex (Private, DeviceIndex);\r
+  if (DeviceData == NULL) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  return AhciRead (DeviceData, Buffer, StartLBA, BufferSize);\r
+}\r
+\r
+/**\r
+  Gets the count of block I/O devices that one specific block driver detects.\r
+\r
+  This function is used for getting the count of block I/O devices that one\r
+  specific block driver detects. If no device is detected, then the function\r
+  will return zero.\r
+\r
+  @param[in]  PeiServices          General-purpose services that are available\r
+                                   to every PEIM.\r
+  @param[in]  This                 Indicates the EFI_PEI_RECOVERY_BLOCK_IO2_PPI\r
+                                   instance.\r
+  @param[out] NumberBlockDevices   The number of block I/O devices discovered.\r
+\r
+  @retval     EFI_SUCCESS          The operation performed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AhciBlockIoGetDeviceNo2 (\r
+  IN  EFI_PEI_SERVICES                **PeiServices,\r
+  IN  EFI_PEI_RECOVERY_BLOCK_IO2_PPI  *This,\r
+  OUT UINTN                           *NumberBlockDevices\r
+  )\r
+{\r
+  PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private;\r
+\r
+  if (This == NULL || NumberBlockDevices == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Private = GET_AHCI_PEIM_HC_PRIVATE_DATA_FROM_THIS_BLKIO2 (This);\r
+  *NumberBlockDevices = Private->ActiveDevices;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Gets a block device's media information.\r
+\r
+  This function will provide the caller with the specified block device's media\r
+  information. If the media changes, calling this function will update the media\r
+  information accordingly.\r
+\r
+  @param[in]  PeiServices   General-purpose services that are available to every\r
+                            PEIM\r
+  @param[in]  This          Indicates the EFI_PEI_RECOVERY_BLOCK_IO2_PPI instance.\r
+  @param[in]  DeviceIndex   Specifies the block device to which the function wants\r
+                            to talk. Because the driver that implements Block I/O\r
+                            PPIs will manage multiple block devices, the PPIs that\r
+                            want to talk to a single device must specify the\r
+                            device index that was assigned during the enumeration\r
+                            process. This index is a number from one to\r
+                            NumberBlockDevices.\r
+  @param[out] MediaInfo     The media information of the specified block media.\r
+                            The caller is responsible for the ownership of this\r
+                            data structure.\r
+\r
+  @par Note:\r
+      The MediaInfo structure describes an enumeration of possible block device\r
+      types.  This enumeration exists because no device paths are actually passed\r
+      across interfaces that describe the type or class of hardware that is publishing\r
+      the block I/O interface. This enumeration will allow for policy decisions\r
+      in the Recovery PEIM, such as "Try to recover from legacy floppy first,\r
+      LS-120 second, CD-ROM third." If there are multiple partitions abstracted\r
+      by a given device type, they should be reported in ascending order; this\r
+      order also applies to nested partitions, such as legacy MBR, where the\r
+      outermost partitions would have precedence in the reporting order. The\r
+      same logic applies to systems such as IDE that have precedence relationships\r
+      like "Master/Slave" or "Primary/Secondary". The master device should be\r
+      reported first, the slave second.\r
+\r
+  @retval EFI_SUCCESS        Media information about the specified block device\r
+                             was obtained successfully.\r
+  @retval EFI_DEVICE_ERROR   Cannot get the media information due to a hardware\r
+                             error.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AhciBlockIoGetMediaInfo2 (\r
+  IN  EFI_PEI_SERVICES                **PeiServices,\r
+  IN  EFI_PEI_RECOVERY_BLOCK_IO2_PPI  *This,\r
+  IN  UINTN                           DeviceIndex,\r
+  OUT EFI_PEI_BLOCK_IO2_MEDIA         *MediaInfo\r
+  )\r
+{\r
+  PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private;\r
+  PEI_AHCI_ATA_DEVICE_DATA            *DeviceData;\r
+\r
+  if (This == NULL || MediaInfo == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Private    = GET_AHCI_PEIM_HC_PRIVATE_DATA_FROM_THIS_BLKIO2 (This);\r
+  DeviceData = SearchDeviceByIndex (Private, DeviceIndex);\r
+  if (DeviceData == NULL) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  CopyMem (\r
+    MediaInfo,\r
+    &DeviceData->Media,\r
+    sizeof (EFI_PEI_BLOCK_IO2_MEDIA)\r
+    );\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Reads the requested number of blocks from the specified block device.\r
+\r
+  The function reads the requested number of blocks from the device. All the\r
+  blocks are read, or an error is returned. If there is no media in the device,\r
+  the function returns EFI_NO_MEDIA.\r
+\r
+  @param[in]  PeiServices   General-purpose services that are available to\r
+                            every PEIM.\r
+  @param[in]  This          Indicates the EFI_PEI_RECOVERY_BLOCK_IO2_PPI instance.\r
+  @param[in]  DeviceIndex   Specifies the block device to which the function wants\r
+                            to talk. Because the driver that implements Block I/O\r
+                            PPIs will manage multiple block devices, PPIs that\r
+                            want to talk to a single device must specify the device\r
+                            index that was assigned during the enumeration process.\r
+                            This index is a number from one to NumberBlockDevices.\r
+  @param[in]  StartLBA      The starting logical block address (LBA) to read from\r
+                            on the device\r
+  @param[in]  BufferSize    The size of the Buffer in bytes. This number must be\r
+                            a multiple of the intrinsic block size of the device.\r
+  @param[out] Buffer        A pointer to the destination buffer for the data.\r
+                            The caller is responsible for the ownership of the\r
+                            buffer.\r
+\r
+  @retval EFI_SUCCESS             The data was read correctly from the device.\r
+  @retval EFI_DEVICE_ERROR        The device reported an error while attempting\r
+                                  to perform the read operation.\r
+  @retval EFI_INVALID_PARAMETER   The read request contains LBAs that are not\r
+                                  valid, or the buffer is not properly aligned.\r
+  @retval EFI_NO_MEDIA            There is no media in the device.\r
+  @retval EFI_BAD_BUFFER_SIZE     The BufferSize parameter is not a multiple of\r
+                                  the intrinsic block size of the device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AhciBlockIoReadBlocks2 (\r
+  IN  EFI_PEI_SERVICES                **PeiServices,\r
+  IN  EFI_PEI_RECOVERY_BLOCK_IO2_PPI  *This,\r
+  IN  UINTN                           DeviceIndex,\r
+  IN  EFI_PEI_LBA                     StartLBA,\r
+  IN  UINTN                           BufferSize,\r
+  OUT VOID                            *Buffer\r
+  )\r
+{\r
+  PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private;\r
+\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Private = GET_AHCI_PEIM_HC_PRIVATE_DATA_FROM_THIS_BLKIO2 (This);\r
+  return AhciBlockIoReadBlocks (\r
+           PeiServices,\r
+           &Private->BlkIoPpi,\r
+           DeviceIndex,\r
+           StartLBA,\r
+           BufferSize,\r
+           Buffer\r
+           );\r
+}\r