--- /dev/null
+/** @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