summaryrefslogtreecommitdiff
path: root/MdeModulePkg/Bus
diff options
context:
space:
mode:
authorjljusten <jljusten@6f19259b-4bc3-4df7-8a09-765794883524>2011-06-27 23:31:21 +0000
committerjljusten <jljusten@6f19259b-4bc3-4df7-8a09-765794883524>2011-06-27 23:31:21 +0000
commitea060cfad64d90fec60419a360e96c32186c9a65 (patch)
tree5c737e8af1ad6688410777973e7b6e50289f8c54 /MdeModulePkg/Bus
parent4b1bf81c20d5523554a83f6604df7930396f7c21 (diff)
downloadedk2-ea060cfad64d90fec60419a360e96c32186c9a65.tar.gz
MdeModulePkg: Add IdeBusPei driver
Signed-off-by: jljusten Reviewed-by: mdkinney Reviewed-by: geekboy15a git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@11902 6f19259b-4bc3-4df7-8a09-765794883524
Diffstat (limited to 'MdeModulePkg/Bus')
-rw-r--r--MdeModulePkg/Bus/Pci/IdeBusPei/AtapiPeim.c2275
-rw-r--r--MdeModulePkg/Bus/Pci/IdeBusPei/AtapiPeim.h667
-rw-r--r--MdeModulePkg/Bus/Pci/IdeBusPei/IdeBusPei.inf66
3 files changed, 3008 insertions, 0 deletions
diff --git a/MdeModulePkg/Bus/Pci/IdeBusPei/AtapiPeim.c b/MdeModulePkg/Bus/Pci/IdeBusPei/AtapiPeim.c
new file mode 100644
index 000000000..cc9b06aa9
--- /dev/null
+++ b/MdeModulePkg/Bus/Pci/IdeBusPei/AtapiPeim.c
@@ -0,0 +1,2275 @@
+/** @file
+PEIM to produce gEfiPeiVirtualBlockIoPpiGuid PPI for ATA controllers in the platform.
+This PPI canl be consumed by PEIM which produce gEfiPeiDeviceRecoveryModulePpiGuid
+for Atapi CD ROM device.
+
+Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>
+
+This program and the accompanying materials
+are licensed and made available under the terms and conditions
+of the BSD License which accompanies this distribution. The
+full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "AtapiPeim.h"
+
+/**
+ Initializes the Atapi Block Io PPI.
+
+ @param[in] FileHandle Handle of the file being invoked.
+ @param[in] PeiServices Describes the list of possible PEI Services.
+
+ @retval EFI_SUCCESS Operation performed successfully.
+ @retval EFI_OUT_OF_RESOURCES Not enough memory to allocate.
+
+**/
+EFI_STATUS
+EFIAPI
+AtapiPeimEntry (
+ IN EFI_PEI_FILE_HANDLE FileHandle,
+ IN CONST EFI_PEI_SERVICES **PeiServices
+ )
+{
+ PEI_ATA_CONTROLLER_PPI *AtaControllerPpi;
+ EFI_STATUS Status;
+ ATAPI_BLK_IO_DEV *AtapiBlkIoDev;
+
+ Status = PeiServicesRegisterForShadow (FileHandle);
+ if (!EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = PeiServicesLocatePpi (
+ &gPeiAtaControllerPpiGuid,
+ 0,
+ NULL,
+ (VOID **) &AtaControllerPpi
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ AtapiBlkIoDev = AllocatePages (EFI_SIZE_TO_PAGES (sizeof (*AtapiBlkIoDev)));
+ if (AtapiBlkIoDev == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ AtapiBlkIoDev->Signature = ATAPI_BLK_IO_DEV_SIGNATURE;
+ AtapiBlkIoDev->AtaControllerPpi = AtaControllerPpi;
+
+ //
+ // atapi device enumeration and build private data
+ //
+ AtapiEnumerateDevices (AtapiBlkIoDev);
+
+ AtapiBlkIoDev->AtapiBlkIo.GetNumberOfBlockDevices = AtapiGetNumberOfBlockDevices;
+ AtapiBlkIoDev->AtapiBlkIo.GetBlockDeviceMediaInfo = AtapiGetBlockDeviceMediaInfo;
+ AtapiBlkIoDev->AtapiBlkIo.ReadBlocks = AtapiReadBlocks;
+
+ AtapiBlkIoDev->PpiDescriptor.Flags = (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST);
+ AtapiBlkIoDev->PpiDescriptor.Guid = &gEfiPeiVirtualBlockIoPpiGuid;
+ AtapiBlkIoDev->PpiDescriptor.Ppi = &AtapiBlkIoDev->AtapiBlkIo;
+
+ DEBUG ((EFI_D_INFO, "Atatpi Device Count is %d\n", AtapiBlkIoDev->DeviceCount));
+ if (AtapiBlkIoDev->DeviceCount != 0) {
+ Status = PeiServicesInstallPpi (&AtapiBlkIoDev->PpiDescriptor);
+ if (EFI_ERROR (Status)) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Gets the count of block I/O devices that one specific block driver detects.
+
+ This function is used for getting the count of block I/O devices that one
+ specific block driver detects. To the PEI ATAPI driver, it returns the number
+ of all the detected ATAPI devices it detects during the enumeration process.
+ To the PEI legacy floppy driver, it returns the number of all the legacy
+ devices it finds during its enumeration process. If no device is detected,
+ then the function will return zero.
+
+ @param[in] PeiServices General-purpose services that are available
+ to every PEIM.
+ @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI
+ instance.
+ @param[out] NumberBlockDevices The number of block I/O devices discovered.
+
+ @retval EFI_SUCCESS Operation performed successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+AtapiGetNumberOfBlockDevices (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This,
+ OUT UINTN *NumberBlockDevices
+ )
+{
+ ATAPI_BLK_IO_DEV *AtapiBlkIoDev;
+
+ AtapiBlkIoDev = NULL;
+
+ AtapiBlkIoDev = PEI_RECOVERY_ATAPI_FROM_BLKIO_THIS (This);
+
+ *NumberBlockDevices = AtapiBlkIoDev->DeviceCount;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Gets a block device's media information.
+
+ This function will provide the caller with the specified block device's media
+ information. If the media changes, calling this function will update the media
+ information accordingly.
+
+ @param[in] PeiServices General-purpose services that are available to every
+ PEIM
+ @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance.
+ @param[in] DeviceIndex Specifies the block device to which the function wants
+ to talk. Because the driver that implements Block I/O
+ PPIs will manage multiple block devices, the PPIs that
+ want to talk to a single device must specify the
+ device index that was assigned during the enumeration
+ process. This index is a number from one to
+ NumberBlockDevices.
+ @param[out] MediaInfo The media information of the specified block media.
+ The caller is responsible for the ownership of this
+ data structure.
+
+ @retval EFI_SUCCESS Media information about the specified block device
+ was obtained successfully.
+ @retval EFI_DEVICE_ERROR Cannot get the media information due to a hardware
+ error.
+ @retval Others Other failure occurs.
+
+**/
+EFI_STATUS
+EFIAPI
+AtapiGetBlockDeviceMediaInfo (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This,
+ IN UINTN DeviceIndex,
+ OUT EFI_PEI_BLOCK_IO_MEDIA *MediaInfo
+ )
+{
+ UINTN DeviceCount;
+ ATAPI_BLK_IO_DEV *AtapiBlkIoDev;
+ EFI_STATUS Status;
+ UINTN Index;
+
+ AtapiBlkIoDev = NULL;
+
+ if (This == NULL || MediaInfo == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ AtapiBlkIoDev = PEI_RECOVERY_ATAPI_FROM_BLKIO_THIS (This);
+
+ DeviceCount = AtapiBlkIoDev->DeviceCount;
+
+ //
+ // DeviceIndex is a value from 1 to NumberBlockDevices.
+ //
+ if ((DeviceIndex < 1) || (DeviceIndex > DeviceCount) || (DeviceIndex > MAX_IDE_DEVICES)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Index = DeviceIndex - 1;
+
+ //
+ // probe media and retrieve latest media information
+ //
+ DEBUG ((EFI_D_INFO, "Atatpi GetInfo DevicePosition is %d\n", AtapiBlkIoDev->DeviceInfo[Index].DevicePosition));
+ DEBUG ((EFI_D_INFO, "Atatpi GetInfo DeviceType is %d\n", AtapiBlkIoDev->DeviceInfo[Index].MediaInfo.DeviceType));
+ DEBUG ((EFI_D_INFO, "Atatpi GetInfo MediaPresent is %d\n", AtapiBlkIoDev->DeviceInfo[Index].MediaInfo.MediaPresent));
+ DEBUG ((EFI_D_INFO, "Atatpi GetInfo BlockSize is 0x%x\n", AtapiBlkIoDev->DeviceInfo[Index].MediaInfo.BlockSize));
+ DEBUG ((EFI_D_INFO, "Atatpi GetInfo LastBlock is 0x%x\n", AtapiBlkIoDev->DeviceInfo[Index].MediaInfo.LastBlock));
+
+ Status = DetectMedia (
+ AtapiBlkIoDev,
+ AtapiBlkIoDev->DeviceInfo[Index].DevicePosition,
+ &AtapiBlkIoDev->DeviceInfo[Index].MediaInfo
+ );
+ if (Status != EFI_SUCCESS) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ DEBUG ((EFI_D_INFO, "Atatpi GetInfo DevicePosition is %d\n", AtapiBlkIoDev->DeviceInfo[Index].DevicePosition));
+ DEBUG ((EFI_D_INFO, "Atatpi GetInfo DeviceType is %d\n", AtapiBlkIoDev->DeviceInfo[Index].MediaInfo.DeviceType));
+ DEBUG ((EFI_D_INFO, "Atatpi GetInfo MediaPresent is %d\n", AtapiBlkIoDev->DeviceInfo[Index].MediaInfo.MediaPresent));
+ DEBUG ((EFI_D_INFO, "Atatpi GetInfo BlockSize is 0x%x\n", AtapiBlkIoDev->DeviceInfo[Index].MediaInfo.BlockSize));
+ DEBUG ((EFI_D_INFO, "Atatpi GetInfo LastBlock is 0x%x\n", AtapiBlkIoDev->DeviceInfo[Index].MediaInfo.LastBlock));
+
+ //
+ // Get media info from AtapiBlkIoDev
+ //
+ CopyMem (MediaInfo, &AtapiBlkIoDev->DeviceInfo[Index].MediaInfo, sizeof(EFI_PEI_BLOCK_IO_MEDIA));
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Reads the requested number of blocks from the specified block device.
+
+ The function reads the requested number of blocks from the device. All the
+ blocks are read, or an error is returned. If there is no media in the device,
+ the function returns EFI_NO_MEDIA.
+
+ @param[in] PeiServices General-purpose services that are available to
+ every PEIM.
+ @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance.
+ @param[in] DeviceIndex Specifies the block device to which the function wants
+ to talk. Because the driver that implements Block I/O
+ PPIs will manage multiple block devices, the PPIs that
+ want to talk to a single device must specify the device
+ index that was assigned during the enumeration process.
+ This index is a number from one to NumberBlockDevices.
+ @param[in] StartLBA The starting logical block address (LBA) to read from
+ on the device
+ @param[in] BufferSize The size of the Buffer in bytes. This number must be
+ a multiple of the intrinsic block size of the device.
+ @param[out] Buffer A pointer to the destination buffer for the data.
+ The caller is responsible for the ownership of the
+ buffer.
+
+ @retval EFI_SUCCESS The data was read correctly from the device.
+ @retval EFI_DEVICE_ERROR The device reported an error while attempting
+ to perform the read operation.
+ @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not
+ valid, or the buffer is not properly aligned.
+ @retval EFI_NO_MEDIA There is no media in the device.
+ @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of
+ the intrinsic block size of the device.
+
+**/
+EFI_STATUS
+EFIAPI
+AtapiReadBlocks (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This,
+ IN UINTN DeviceIndex,
+ IN EFI_PEI_LBA StartLBA,
+ IN UINTN BufferSize,
+ OUT VOID *Buffer
+ )
+{
+
+ EFI_PEI_BLOCK_IO_MEDIA MediaInfo;
+ EFI_STATUS Status;
+ UINTN NumberOfBlocks;
+ UINTN BlockSize;
+ ATAPI_BLK_IO_DEV *AtapiBlkIoDev;
+
+ AtapiBlkIoDev = NULL;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ AtapiBlkIoDev = PEI_RECOVERY_ATAPI_FROM_BLKIO_THIS (This);
+
+ if (Buffer == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (BufferSize == 0) {
+ return EFI_SUCCESS;
+ }
+
+ Status = AtapiGetBlockDeviceMediaInfo (
+ PeiServices,
+ This,
+ DeviceIndex,
+ &MediaInfo
+ );
+ if (Status != EFI_SUCCESS) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (!MediaInfo.MediaPresent) {
+ return EFI_NO_MEDIA;
+ }
+
+ BlockSize = MediaInfo.BlockSize;
+
+ if (BufferSize % BlockSize != 0) {
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ NumberOfBlocks = BufferSize / BlockSize;
+
+ if ((StartLBA + NumberOfBlocks - 1) > AtapiBlkIoDev->DeviceInfo[DeviceIndex - 1].MediaInfo.LastBlock) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = ReadSectors (
+ AtapiBlkIoDev,
+ AtapiBlkIoDev->DeviceInfo[DeviceIndex - 1].DevicePosition,
+ Buffer,
+ StartLBA,
+ NumberOfBlocks,
+ BlockSize
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Enumerate Atapi devices.
+
+ This function is used to enumerate Atatpi device in Ide channel.
+
+ @param[in] AtapiBlkIoDev A pointer to atapi block IO device
+
+**/
+VOID
+AtapiEnumerateDevices (
+ IN ATAPI_BLK_IO_DEV *AtapiBlkIoDev
+ )
+{
+ UINT8 Index1;
+ UINT8 Index2;
+ UINTN DevicePosition;
+ EFI_PEI_BLOCK_IO_MEDIA MediaInfo;
+ EFI_STATUS Status;
+ UINTN DeviceCount;
+ UINT16 CommandBlockBaseAddr;
+ UINT16 ControlBlockBaseAddr;
+ UINT32 IdeEnabledNumber;
+ IDE_REGS_BASE_ADDR IdeRegsBaseAddr[MAX_IDE_CHANNELS];
+
+ DeviceCount = 0;
+ DevicePosition = 0;
+
+ //
+ // Scan IDE bus for ATAPI devices
+ //
+
+ //
+ // Enable Sata and IDE controller.
+ //
+ AtapiBlkIoDev->AtaControllerPpi->EnableAtaChannel (
+ (EFI_PEI_SERVICES **) GetPeiServicesTablePointer(),
+ AtapiBlkIoDev->AtaControllerPpi,
+ PEI_ICH_IDE_PRIMARY | PEI_ICH_IDE_SECONDARY
+ );
+
+ //
+ // Allow SATA Devices to spin-up. This is needed if
+ // SEC and PEI phase is too short, for example Release Build.
+ //
+ DEBUG ((EFI_D_INFO, "Delay for %d seconds for SATA devices to spin-up\n", PcdGet16 (PcdSataSpinUpDelayInSecForRecoveryPath)));
+ MicroSecondDelay (PcdGet16 (PcdSataSpinUpDelayInSecForRecoveryPath) * 1000 * 1000); //
+
+ //
+ // Get four channels (primary or secondary Pata, Sata Channel) Command and Control Regs Base address.
+ //
+ IdeEnabledNumber = AtapiBlkIoDev->AtaControllerPpi->GetIdeRegsBaseAddr (
+ (EFI_PEI_SERVICES **) GetPeiServicesTablePointer(),
+ AtapiBlkIoDev->AtaControllerPpi,
+ IdeRegsBaseAddr
+ );
+
+ //
+ // Using Command and Control Regs Base Address to fill other registers.
+ //
+ for (Index1 = 0; Index1 < IdeEnabledNumber; Index1 ++) {
+ CommandBlockBaseAddr = IdeRegsBaseAddr[Index1].CommandBlockBaseAddr;
+ AtapiBlkIoDev->IdeIoPortReg[Index1].Data = CommandBlockBaseAddr;
+ AtapiBlkIoDev->IdeIoPortReg[Index1].Reg1.Feature = (UINT16) (CommandBlockBaseAddr + 0x1);
+ AtapiBlkIoDev->IdeIoPortReg[Index1].SectorCount = (UINT16) (CommandBlockBaseAddr + 0x2);
+ AtapiBlkIoDev->IdeIoPortReg[Index1].SectorNumber = (UINT16) (CommandBlockBaseAddr + 0x3);
+ AtapiBlkIoDev->IdeIoPortReg[Index1].CylinderLsb = (UINT16) (CommandBlockBaseAddr + 0x4);
+ AtapiBlkIoDev->IdeIoPortReg[Index1].CylinderMsb = (UINT16) (CommandBlockBaseAddr + 0x5);
+ AtapiBlkIoDev->IdeIoPortReg[Index1].Head = (UINT16) (CommandBlockBaseAddr + 0x6);
+ AtapiBlkIoDev->IdeIoPortReg[Index1].Reg.Command = (UINT16) (CommandBlockBaseAddr + 0x7);
+
+ ControlBlockBaseAddr = IdeRegsBaseAddr[Index1].ControlBlockBaseAddr;
+ AtapiBlkIoDev->IdeIoPortReg[Index1].Alt.DeviceControl = ControlBlockBaseAddr;
+ AtapiBlkIoDev->IdeIoPortReg[Index1].DriveAddress = (UINT16) (ControlBlockBaseAddr + 0x1);
+
+ //
+ // Scan IDE bus for ATAPI devices IDE or Sata device
+ //
+ for (Index2 = IdeMaster; Index2 < IdeMaxDevice; Index2++) {
+ //
+ // Pata & Sata, Primary & Secondary channel, Master & Slave device
+ //
+ DevicePosition = (UINTN) (Index1 * 2 + Index2);
+
+ if (DiscoverAtapiDevice (AtapiBlkIoDev, DevicePosition, &MediaInfo)) {
+ //
+ // ATAPI Device at DevicePosition is found.
+ //
+ AtapiBlkIoDev->DeviceInfo[DeviceCount].DevicePosition = DevicePosition;
+ //
+ // Retrieve Media Info
+ //
+ Status = DetectMedia (AtapiBlkIoDev, DevicePosition, &MediaInfo);
+ CopyMem (&(AtapiBlkIoDev->DeviceInfo[DeviceCount].MediaInfo), &MediaInfo, sizeof (MediaInfo));
+
+ DEBUG ((EFI_D_INFO, "Atatpi Device Position is %d\n", DevicePosition));
+ DEBUG ((EFI_D_INFO, "Atatpi DeviceType is %d\n", MediaInfo.DeviceType));
+ DEBUG ((EFI_D_INFO, "Atatpi MediaPresent is %d\n", MediaInfo.MediaPresent));
+ DEBUG ((EFI_D_INFO, "Atatpi BlockSize is 0x%x\n", MediaInfo.BlockSize));
+
+ if (EFI_ERROR (Status)) {
+ AtapiBlkIoDev->DeviceInfo[DeviceCount].MediaInfo.MediaPresent = FALSE;
+ AtapiBlkIoDev->DeviceInfo[DeviceCount].MediaInfo.LastBlock = 0;
+ }
+ DeviceCount += 1;
+ }
+ }
+ }
+
+ AtapiBlkIoDev->DeviceCount = DeviceCount;
+}
+
+/**
+ Detect Atapi devices.
+
+ @param[in] AtapiBlkIoDev A pointer to atapi block IO device.
+ @param[in] DevicePosition An integer to signify device position.
+ @param[out] MediaInfo The media information of the specified block media.
+
+ @retval TRUE Atapi device exists in specified position.
+ @retval FALSE Atapi device does not exist in specified position.
+
+**/
+BOOLEAN
+DiscoverAtapiDevice (
+ IN ATAPI_BLK_IO_DEV *AtapiBlkIoDev,
+ IN UINTN DevicePosition,
+ OUT EFI_PEI_BLOCK_IO_MEDIA *MediaInfo
+ )
+{
+ EFI_STATUS Status;
+
+ if (!DetectIDEController (AtapiBlkIoDev, DevicePosition)) {
+ return FALSE;
+ }
+ //
+ // test if it is an ATAPI device (only supported device)
+ //
+ if (ATAPIIdentify (AtapiBlkIoDev, DevicePosition) == EFI_SUCCESS) {
+
+ Status = Inquiry (AtapiBlkIoDev, DevicePosition, MediaInfo);
+ if (!EFI_ERROR (Status)) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ Check power mode of Atapi devices.
+
+ @param[in] AtapiBlkIoDev A pointer to atapi block IO device.
+ @param[in] DevicePosition An integer to signify device position.
+ @param[in] AtaCommand The Ata Command passed in.
+
+ @retval EFI_SUCCESS The Atapi device support power mode.
+ @retval EFI_NOT_FOUND The Atapi device not found.
+ @retval EFI_TIMEOUT Atapi command transaction is time out.
+ @retval EFI_ABORTED Atapi command abort.
+
+**/
+EFI_STATUS
+CheckPowerMode (
+ IN ATAPI_BLK_IO_DEV *AtapiBlkIoDev,
+ IN UINTN DevicePosition,
+ IN UINT8 AtaCommand
+ )
+{
+ UINT8 Channel;
+ UINT8 Device;
+ UINT16 StatusRegister;
+ UINT16 HeadRegister;
+ UINT16 CommandRegister;
+ UINT16 ErrorRegister;
+ UINT16 SectorCountRegister;
+ EFI_STATUS Status;
+ UINT8 StatusValue;
+ UINT8 ErrorValue;
+ UINT8 SectorCountValue;
+
+ Channel = (UINT8) (DevicePosition / 2);
+ Device = (UINT8) (DevicePosition % 2);
+
+ ASSERT (Channel < MAX_IDE_CHANNELS);
+
+ StatusRegister = AtapiBlkIoDev->IdeIoPortReg[Channel].Reg.Status;
+ HeadRegister = AtapiBlkIoDev->IdeIoPortReg[Channel].Head;
+ CommandRegister = AtapiBlkIoDev->IdeIoPortReg[Channel].Reg.Command;
+ ErrorRegister = AtapiBlkIoDev->IdeIoPortReg[Channel].Reg1.Error;
+ SectorCountRegister = AtapiBlkIoDev->IdeIoPortReg[Channel].SectorCount;
+
+ //
+ // select device
+ //
+ IoWrite8 (HeadRegister, (UINT8) ((Device << 4) | 0xe0));
+
+ //
+ // refresh the SectorCount register
+ //
+ SectorCountValue = 0x55;
+ IoWrite8 (SectorCountRegister, SectorCountValue);
+
+ //
+ // select device
+ //
+ IoWrite8 (HeadRegister, (UINT8) ((Device << 4) | 0xe0));
+
+ Status = DRDYReady (AtapiBlkIoDev, &(AtapiBlkIoDev->IdeIoPortReg[Channel]), 100);
+
+ //
+ // select device
+ //
+ IoWrite8 (HeadRegister, (UINT8) ((Device << 4) | 0xe0));
+ //
+ // send 'check power' commandd via Command Register
+ //
+ IoWrite8 (CommandRegister, AtaCommand);
+
+ Status = WaitForBSYClear (AtapiBlkIoDev, &(AtapiBlkIoDev->IdeIoPortReg[Channel]), 3000);
+ if (EFI_ERROR (Status)) {
+ return EFI_TIMEOUT;
+ }
+
+ StatusValue = IoRead8 (StatusRegister);
+
+ //
+ // command returned status is DRDY, indicating device supports the command,
+ // so device is present.
+ //
+ if ((StatusValue & ATA_STSREG_DRDY) == ATA_STSREG_DRDY) {
+ return EFI_SUCCESS;
+ }
+
+ SectorCountValue = IoRead8 (SectorCountRegister);
+
+ //
+ // command returned status is ERR & ABRT_ERR, indicating device does not support
+ // the command, so device is present.
+ //
+ if ((StatusValue & ATA_STSREG_ERR) == ATA_STSREG_ERR) {
+ ErrorValue = IoRead8 (ErrorRegister);
+ if ((ErrorValue & ATA_ERRREG_ABRT) == ATA_ERRREG_ABRT) {
+ return EFI_ABORTED;
+ } else {
+ //
+ // According to spec, no other error code is valid
+ //
+ return EFI_NOT_FOUND;
+ }
+ }
+
+ if ((SectorCountValue == 0x00) || (SectorCountValue == 0x80) || (SectorCountValue == 0xff)) {
+ //
+ // Write SectorCount 0x55 but return valid state value. Maybe no device
+ // exists or some slow kind of ATAPI device exists.
+ //
+ IoWrite8 (HeadRegister, (UINT8) ((Device << 4) | 0xe0));
+
+ //
+ // write 0x55 and 0xaa to SectorCounter register,
+ // if the data could be written into the register,
+ // indicating the device is present, otherwise the device is not present.
+ //
+ SectorCountValue = 0x55;
+ IoWrite8 (SectorCountRegister, SectorCountValue);
+ MicroSecondDelay (10000);
+
+ SectorCountValue = IoRead8 (SectorCountRegister);
+ if (SectorCountValue != 0x55) {
+ return EFI_NOT_FOUND;
+ }
+ //
+ // Send a "ATAPI TEST UNIT READY" command ... slow but accurate
+ //
+ Status = TestUnitReady (AtapiBlkIoDev, DevicePosition);
+ return Status;
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+/**
+ Detect if an IDE controller exists in specified position.
+
+ @param[in] AtapiBlkIoDev A pointer to atapi block IO device.
+ @param[in] DevicePosition An integer to signify device position.
+
+ @retval TRUE The Atapi device exists.
+ @retval FALSE The Atapi device does not present.
+
+**/
+BOOLEAN
+DetectIDEController (
+ IN ATAPI_BLK_IO_DEV *AtapiBlkIoDev,
+ IN UINTN DevicePosition
+ )
+{
+ UINT8 Channel;
+ EFI_STATUS Status;
+ UINT8 AtaCommand;
+
+ Channel = (UINT8) (DevicePosition / 2);
+
+ ASSERT (Channel < MAX_IDE_CHANNELS);
+ //
+ // Wait 31 seconds for BSY clear
+ //
+ Status = WaitForBSYClear (AtapiBlkIoDev, &(AtapiBlkIoDev->IdeIoPortReg[Channel]), 31000);
+ if (EFI_ERROR (Status)) {
+ return FALSE;
+ }
+ //
+ // Send 'check power' command for IDE device
+ //
+ AtaCommand = 0xE5;
+ Status = CheckPowerMode (AtapiBlkIoDev, DevicePosition, AtaCommand);
+ if ((Status == EFI_ABORTED) || (Status == EFI_SUCCESS)) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ Wait specified time interval to poll for BSY bit clear in the Status Register.
+
+ @param[in] AtapiBlkIoDev A pointer to atapi block IO device.
+ @param[in] IdeIoRegisters A pointer to IDE IO registers.
+ @param[in] TimeoutInMilliSeconds Time specified in milliseconds.
+
+ @retval EFI_SUCCESS BSY bit is cleared in the specified time interval.
+ @retval EFI_TIMEOUT BSY bit is not cleared in the specified time interval.
+
+**/
+EFI_STATUS
+WaitForBSYClear (
+ IN ATAPI_BLK_IO_DEV *AtapiBlkIoDev,
+ IN IDE_BASE_REGISTERS *IdeIoRegisters,
+ IN UINTN TimeoutInMilliSeconds
+ )
+{
+ UINTN Delay;
+ UINT16 StatusRegister;
+ UINT8 StatusValue;
+
+ StatusValue = 0;
+
+ StatusRegister = IdeIoRegisters->Reg.Status;
+
+ Delay = ((TimeoutInMilliSeconds * STALL_1_MILLI_SECOND) / 250) + 1;
+ do {
+ StatusValue = IoRead8 (StatusRegister);
+ if ((StatusValue & ATA_STSREG_BSY) == 0x00) {
+ break;
+ }
+ MicroSecondDelay (250);
+
+ Delay--;
+
+ } while (Delay != 0);
+
+ if (Delay == 0) {
+ return EFI_TIMEOUT;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Wait specified time interval to poll for DRDY bit set in the Status register.
+
+ @param[in] AtapiBlkIoDev A pointer to atapi block IO device.
+ @param[in] IdeIoRegisters A pointer to IDE IO registers.
+ @param[in] TimeoutInMilliSeconds Time specified in milliseconds.
+
+ @retval EFI_SUCCESS DRDY bit is set in the specified time interval.
+ @retval EFI_TIMEOUT DRDY bit is not set in the specified time interval.
+
+**/
+EFI_STATUS
+DRDYReady (
+ IN ATAPI_BLK_IO_DEV *AtapiBlkIoDev,
+ IN IDE_BASE_REGISTERS *IdeIoRegisters,
+ IN UINTN TimeoutInMilliSeconds
+ )
+{
+ UINTN Delay;
+ UINT16 StatusRegister;
+ UINT8 StatusValue;
+ UINT8 ErrValue;
+
+ StatusValue = 0;
+
+ StatusRegister = IdeIoRegisters->Reg.Status;
+
+ Delay = ((TimeoutInMilliSeconds * STALL_1_MILLI_SECOND) / 250) + 1;
+ do {
+ StatusValue = IoRead8 (StatusRegister);
+ //
+ // BSY == 0 , DRDY == 1
+ //
+ if ((StatusValue & (ATA_STSREG_DRDY | ATA_STSREG_BSY)) == ATA_STSREG_DRDY) {
+ break;
+ }
+
+ if ((StatusValue & (ATA_STSREG_ERR | ATA_STSREG_BSY)) == ATA_STSREG_ERR) {
+ ErrValue = IoRead8 (IdeIoRegisters->Reg1.Error);
+ if ((ErrValue & ATA_ERRREG_ABRT) == ATA_ERRREG_ABRT) {
+ return EFI_ABORTED;
+ }
+ }
+
+ MicroSecondDelay (250);
+
+ Delay--;
+
+ } while (Delay != 0);
+
+ if (Delay == 0) {
+ return EFI_TIMEOUT;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Wait specified time interval to poll for DRQ bit clear in the Status Register.
+
+ @param[in] AtapiBlkIoDev A pointer to atapi block IO device.
+ @param[in] IdeIoRegisters A pointer to IDE IO registers.
+ @param[in] TimeoutInMilliSeconds Time specified in milliseconds.
+
+ @retval EFI_SUCCESS DRQ bit is cleared in the specified time interval.
+ @retval EFI_TIMEOUT DRQ bit is not cleared in the specified time interval.
+
+**/
+EFI_STATUS
+DRQClear (
+ IN ATAPI_BLK_IO_DEV *AtapiBlkIoDev,
+ IN IDE_BASE_REGISTERS *IdeIoRegisters,
+ IN UINTN TimeoutInMilliSeconds
+ )
+{
+ UINTN Delay;
+ UINT16 StatusRegister;
+ UINT8 StatusValue;
+ UINT8 ErrValue;
+
+ StatusValue = 0;
+
+ StatusRegister = IdeIoRegisters->Reg.Status;
+
+ Delay = ((TimeoutInMilliSeconds * STALL_1_MILLI_SECOND) / 250) + 1;
+ do {
+
+ StatusValue = IoRead8 (StatusRegister);
+
+ //
+ // wait for BSY == 0 and DRQ == 0
+ //
+ if ((StatusValue & (ATA_STSREG_DRQ | ATA_STSREG_BSY)) == 0) {
+ break;
+ }
+
+ if ((StatusValue & (ATA_STSREG_BSY | ATA_STSREG_ERR)) == ATA_STSREG_ERR) {
+ ErrValue = IoRead8 (IdeIoRegisters->Reg1.Error);
+ if ((ErrValue & ATA_ERRREG_ABRT) == ATA_ERRREG_ABRT) {
+ return EFI_ABORTED;
+ }
+ }
+
+ MicroSecondDelay (250);
+
+ Delay--;
+ } while (Delay != 0);
+
+ if (Delay == 0) {
+ return EFI_TIMEOUT;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Wait specified time interval to poll for DRQ bit clear in the Alternate Status Register.
+
+ @param[in] AtapiBlkIoDev A pointer to atapi block IO device.
+ @param[in] IdeIoRegisters A pointer to IDE IO registers.
+ @param[in] TimeoutInMilliSeconds Time specified in milliseconds.
+
+ @retval EFI_SUCCESS DRQ bit is cleared in the specified time interval.
+ @retval EFI_TIMEOUT DRQ bit is not cleared in the specified time interval.
+
+**/
+EFI_STATUS
+DRQClear2 (
+ IN ATAPI_BLK_IO_DEV *AtapiBlkIoDev,
+ IN IDE_BASE_REGISTERS *IdeIoRegisters,
+ IN UINTN TimeoutInMilliSeconds
+ )
+{
+ UINTN Delay;
+ UINT16 AltStatusRegister;
+ UINT8 AltStatusValue;
+ UINT8 ErrValue;
+
+ AltStatusValue = 0;
+
+ AltStatusRegister = IdeIoRegisters->Alt.AltStatus;
+
+ Delay = ((TimeoutInMilliSeconds * STALL_1_MILLI_SECOND) / 250) + 1;
+ do {
+
+ AltStatusValue = IoRead8 (AltStatusRegister);
+
+ //
+ // wait for BSY == 0 and DRQ == 0
+ //
+ if ((AltStatusValue & (ATA_STSREG_DRQ | ATA_STSREG_BSY)) == 0) {
+ break;
+ }
+
+ if ((AltStatusValue & (ATA_STSREG_BSY | ATA_STSREG_ERR)) == ATA_STSREG_ERR) {
+ ErrValue = IoRead8 (IdeIoRegisters->Reg1.Error);
+ if ((ErrValue & ATA_ERRREG_ABRT) == ATA_ERRREG_ABRT) {
+ return EFI_ABORTED;
+ }
+ }
+
+ MicroSecondDelay (250);
+
+ Delay--;
+ } while (Delay != 0);
+
+ if (Delay == 0) {
+ return EFI_TIMEOUT;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Wait specified time interval to poll for DRQ bit set in the Status Register.
+
+ @param[in] AtapiBlkIoDev A pointer to atapi block IO device.
+ @param[in] IdeIoRegisters A pointer to IDE IO registers.
+ @param[in] TimeoutInMilliSeconds Time specified in milliseconds.
+
+ @retval EFI_SUCCESS DRQ bit is set in the specified time interval.
+ @retval EFI_TIMEOUT DRQ bit is not set in the specified time interval.
+ @retval EFI_ABORTED Operation Aborted.
+
+**/
+EFI_STATUS
+DRQReady (
+ IN ATAPI_BLK_IO_DEV *AtapiBlkIoDev,
+ IN IDE_BASE_REGISTERS *IdeIoRegisters,
+ IN UINTN TimeoutInMilliSeconds
+ )
+{
+ UINTN Delay;
+ UINT16 StatusRegister;
+ UINT8 StatusValue;
+ UINT8 ErrValue;
+
+ StatusValue = 0;
+ ErrValue = 0;
+
+ StatusRegister = IdeIoRegisters->Reg.Status;
+
+ Delay = ((TimeoutInMilliSeconds * STALL_1_MILLI_SECOND) / 250) + 1;
+ do {
+ //
+ // read Status Register will clear interrupt
+ //
+ StatusValue = IoRead8 (StatusRegister);
+
+ //
+ // BSY==0,DRQ==1
+ //
+ if ((StatusValue & (ATA_STSREG_BSY | ATA_STSREG_DRQ)) == ATA_STSREG_DRQ) {
+ break;
+ }
+
+ if ((StatusValue & (ATA_STSREG_BSY | ATA_STSREG_ERR)) == ATA_STSREG_ERR) {
+
+ ErrValue = IoRead8 (IdeIoRegisters->Reg1.Error);
+ if ((ErrValue & ATA_ERRREG_ABRT) == ATA_ERRREG_ABRT) {
+ return EFI_ABORTED;
+ }
+ }
+ MicroSecondDelay (250);
+
+ Delay--;
+ } while (Delay != 0);
+
+ if (Delay == 0) {
+ return EFI_TIMEOUT;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Wait specified time interval to poll for DRQ bit set in the Alternate Status Register.
+
+ @param[in] AtapiBlkIoDev A pointer to atapi block IO device.
+ @param[in] IdeIoRegisters A pointer to IDE IO registers.
+ @param[in] TimeoutInMilliSeconds Time specified in milliseconds.
+
+ @retval EFI_SUCCESS DRQ bit is set in the specified time interval.
+ @retval EFI_TIMEOUT DRQ bit is not set in the specified time interval.
+ @retval EFI_ABORTED Operation Aborted.
+
+**/
+EFI_STATUS
+DRQReady2 (
+ IN ATAPI_BLK_IO_DEV *AtapiBlkIoDev,
+ IN IDE_BASE_REGISTERS *IdeIoRegisters,
+ IN UINTN TimeoutInMilliSeconds
+ )
+{
+ UINTN Delay;
+ UINT16 AltStatusRegister;
+ UINT8 AltStatusValue;
+ UINT8 ErrValue;
+
+ AltStatusValue = 0;
+
+ AltStatusRegister = IdeIoRegisters->Alt.AltStatus;
+
+ Delay = ((TimeoutInMilliSeconds * STALL_1_MILLI_SECOND) / 250) + 1;
+ do {
+
+ AltStatusValue = IoRead8 (AltStatusRegister);
+
+ //
+ // BSY==0,DRQ==1
+ //
+ if ((AltStatusValue & (ATA_STSREG_BSY | ATA_STSREG_DRQ)) == ATA_STSREG_DRQ) {
+ break;
+ }
+
+ if ((AltStatusValue & (ATA_STSREG_BSY | ATA_STSREG_ERR)) == ATA_STSREG_ERR) {
+
+ ErrValue = IoRead8 (IdeIoRegisters->Reg1.Error);
+ if ((ErrValue & ATA_ERRREG_ABRT) == ATA_ERRREG_ABRT) {
+ return EFI_ABORTED;
+ }
+ }
+ MicroSecondDelay (250);
+
+ Delay--;
+ } while (Delay != 0);
+
+ if (Delay == 0) {
+ return EFI_TIMEOUT;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Check if there is an error in Status Register.
+
+ @param[in] AtapiBlkIoDev A pointer to atapi block IO device.
+ @param[in] StatusReg The address to IDE IO registers.
+
+ @retval EFI_SUCCESS Operation success.
+ @retval EFI_DEVICE_ERROR Device error.
+
+**/
+EFI_STATUS
+CheckErrorStatus (
+ IN ATAPI_BLK_IO_DEV *AtapiBlkIoDev,
+ IN UINT16 StatusReg
+ )
+{
+ UINT8 StatusValue;
+
+ StatusValue = IoRead8 (StatusReg);
+
+ if ((StatusValue & (ATA_STSREG_ERR | ATA_STSREG_DWF | ATA_STSREG_CORR)) == 0) {
+
+ return EFI_SUCCESS;
+ }
+
+ return EFI_DEVICE_ERROR;
+
+}
+
+/**
+ Idendify Atapi devices.
+
+ @param[in] AtapiBlkIoDev A pointer to atapi block IO device.
+ @param[in] DevicePosition An integer to signify device position.
+
+ @retval EFI_SUCCESS Identify successfully.
+ @retval EFI_DEVICE_ERROR Device cannot be identified successfully.
+
+**/
+EFI_STATUS
+ATAPIIdentify (
+ IN ATAPI_BLK_IO_DEV *AtapiBlkIoDev,
+ IN UINTN DevicePosition
+ )
+{
+ ATAPI_IDENTIFY_DATA AtapiIdentifyData;
+ UINT8 Channel;
+ UINT8 Device;
+ UINT16 StatusReg;
+ UINT16 HeadReg;
+ UINT16 CommandReg;
+ UINT16 DataReg;
+ UINT16 SectorCountReg;
+ UINT16 SectorNumberReg;
+ UINT16 CylinderLsbReg;
+ UINT16 CylinderMsbReg;
+
+ UINT32 WordCount;
+ UINT32 Increment;
+ UINT32 Index;
+ UINT32 ByteCount;
+ UINT16 *Buffer16;
+
+ EFI_STATUS Status;
+
+ ByteCount = sizeof (AtapiIdentifyData);
+ Buffer16 = (UINT16 *) &AtapiIdentifyData;
+
+ Channel = (UINT8) (DevicePosition / 2);
+ Device = (UINT8) (DevicePosition % 2);
+
+ ASSERT (Channel < MAX_IDE_CHANNELS);
+
+ StatusReg = AtapiBlkIoDev->IdeIoPortReg[Channel].Reg.Status;
+ HeadReg = AtapiBlkIoDev->IdeIoPortReg[Channel].Head;
+ CommandReg = AtapiBlkIoDev->IdeIoPortReg[Channel].Reg.Command;
+ DataReg = AtapiBlkIoDev->IdeIoPortReg[Channel].Data;
+ SectorCountReg = AtapiBlkIoDev->IdeIoPortReg[Channel].SectorCount;
+ SectorNumberReg = AtapiBlkIoDev->IdeIoPortReg[Channel].SectorNumber;
+ CylinderLsbReg = AtapiBlkIoDev->IdeIoPortReg[Channel].CylinderLsb;
+ CylinderMsbReg = AtapiBlkIoDev->IdeIoPortReg[Channel].CylinderMsb;
+
+ //
+ // Send ATAPI Identify Command to get IDENTIFY data.
+ //
+ if (WaitForBSYClear (
+ AtapiBlkIoDev,
+ &(AtapiBlkIoDev->IdeIoPortReg[Channel]),
+ ATATIMEOUT
+ ) != EFI_SUCCESS) {
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // select device via Head/Device register.
+ // Before write Head/Device register, BSY and DRQ must be 0.
+ //
+ if (DRQClear2 (AtapiBlkIoDev, &(AtapiBlkIoDev->IdeIoPortReg[Channel]), ATATIMEOUT) != EFI_SUCCESS) {
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // e0:1110,0000-- bit7 and bit5 are reserved bits.
+ // bit6 set means LBA mode
+ //
+ IoWrite8 (HeadReg, (UINT8) ((Device << 4) | 0xe0));
+
+ //
+ // set all the command parameters
+ // Before write to all the following registers, BSY and DRQ must be 0.
+ //
+ if (DRQClear2 (
+ AtapiBlkIoDev,
+ &(AtapiBlkIoDev->IdeIoPortReg[Channel]),
+ ATATIMEOUT
+ ) != EFI_SUCCESS) {
+
+ return EFI_DEVICE_ERROR;
+ }
+
+ IoWrite8 (SectorCountReg, 0);
+ IoWrite8 (SectorNumberReg, 0);
+ IoWrite8 (CylinderLsbReg, 0);
+ IoWrite8 (CylinderMsbReg, 0);
+
+ //
+ // send command via Command Register
+ //
+ IoWrite8 (CommandReg, ATA_CMD_IDENTIFY_DEVICE);
+
+ //
+ // According to PIO data in protocol, host can perform a series of reads to the
+ // data register after each time device set DRQ ready;
+ // The data size of "a series of read" is command specific.
+ // For most ATA command, data size received from device will not exceed 1 sector,
+ // hense the data size for "a series of read" can be the whole data size of one command request.
+ // For ATA command such as Read Sector command, whole data size of one ATA command request is often larger
+ // than 1 sector, according to the Read Sector command, the data size of "a series of read" is exactly
+ // 1 sector.
+ // Here for simplification reason, we specify the data size for "a series of read" to
+ // 1 sector (256 words) if whole data size of one ATA commmand request is larger than 256 words.
+ //
+ Increment = 256;
+ //
+ // 256 words
+ //
+ WordCount = 0;
+ //
+ // WordCount is used to record bytes of currently transfered data
+ //
+ while (WordCount < ByteCount / 2) {
+ //
+ // Poll DRQ bit set, data transfer can be performed only when DRQ is ready.
+ //
+ Status = DRQReady2 (AtapiBlkIoDev, &(AtapiBlkIoDev->IdeIoPortReg[Channel]), ATATIMEOUT);
+ if (Status != EFI_SUCCESS) {
+ return Status;
+ }
+
+ if (CheckErrorStatus (AtapiBlkIoDev, StatusReg) != EFI_SUCCESS) {
+
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // Get the byte count for one series of read
+ //
+ if ((WordCount + Increment) > ByteCount / 2) {
+ Increment = ByteCount / 2 - WordCount;
+ }
+ //
+ // perform a series of read without check DRQ ready
+ //
+ for (Index = 0; Index < Increment; Index++) {
+ *Buffer16++ = IoRead16 (DataReg);
+ }
+
+ WordCount += Increment;
+
+ }
+ //
+ // while
+ //
+ if (DRQClear (
+ AtapiBlkIoDev,
+ &(AtapiBlkIoDev->IdeIoPortReg[Channel]),
+ ATATIMEOUT
+ ) != EFI_SUCCESS) {
+ return CheckErrorStatus (AtapiBlkIoDev, StatusReg);
+ }
+
+ return EFI_SUCCESS;
+
+}
+
+/**
+ Sends out ATAPI Test Unit Ready Packet Command to the specified device
+ to find out whether device is accessible.
+
+ @param[in] AtapiBlkIoDev A pointer to atapi block IO device.
+ @param[in] DevicePosition An integer to signify device position.
+
+ @retval EFI_SUCCESS TestUnit command executed successfully.
+ @retval EFI_DEVICE_ERROR Device cannot be executed TestUnit command successfully.
+
+**/
+EFI_STATUS
+TestUnitReady (
+ IN ATAPI_BLK_IO_DEV *AtapiBlkIoDev,
+ IN UINTN DevicePosition
+ )
+{
+ ATAPI_PACKET_COMMAND Packet;
+ EFI_STATUS Status;
+
+ //
+ // fill command packet
+ //
+ ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND));
+ Packet.TestUnitReady.opcode = ATA_CMD_TEST_UNIT_READY;
+
+ //
+ // send command packet
+ //
+ Status = AtapiPacketCommandIn (AtapiBlkIoDev, DevicePosition, &Packet, NULL, 0, ATAPITIMEOUT);
+ return Status;
+}
+
+/**
+ Send out ATAPI commands conforms to the Packet Command with PIO Data In Protocol.
+
+ @param[in] AtapiBlkIoDev A pointer to atapi block IO device.
+ @param[in] DevicePosition An integer to signify device position.
+ @param[in] Packet A pointer to ATAPI command packet.
+ @param[in] Buffer Buffer to contain requested transfer data from device.
+ @param[in] ByteCount Requested transfer data length.
+ @param[in] TimeoutInMilliSeconds Time out value, in unit of milliseconds.
+
+ @retval EFI_SUCCESS Command executed successfully.
+ @retval EFI_DEVICE_ERROR Device cannot be executed command successfully.
+
+**/
+EFI_STATUS
+AtapiPacketCommandIn (
+ IN ATAPI_BLK_IO_DEV *AtapiBlkIoDev,
+ IN UINTN DevicePosition,
+ IN ATAPI_PACKET_COMMAND *Packet,
+ IN UINT16 *Buffer,
+ IN UINT32 ByteCount,
+ IN UINTN TimeoutInMilliSeconds
+ )
+{
+ UINT8 Channel;
+ UINT8 Device;
+ UINT16 StatusReg;
+ UINT16 HeadReg;
+ UINT16 CommandReg;
+ UINT16 FeatureReg;
+ UINT16 CylinderLsbReg;
+ UINT16 CylinderMsbReg;
+ UINT16 DeviceControlReg;
+ UINT16 DataReg;
+ EFI_STATUS Status;
+ UINT32 Count;
+ UINT16 *CommandIndex;
+ UINT16 *PtrBuffer;
+ UINT32 Index;
+ UINT8 StatusValue;
+ UINT32 WordCount;
+
+ //
+ // required transfer data in word unit.
+ //
+ UINT32 RequiredWordCount;
+
+ //
+ // actual transfer data in word unit.
+ //
+ UINT32 ActualWordCount;
+
+ Channel = (UINT8) (DevicePosition / 2);
+ Device = (UINT8) (DevicePosition % 2);
+
+ ASSERT (Channel < MAX_IDE_CHANNELS);
+
+ StatusReg = AtapiBlkIoDev->IdeIoPortReg[Channel].Reg.Status;
+ HeadReg = AtapiBlkIoDev->IdeIoPortReg[Channel].Head;
+ CommandReg = AtapiBlkIoDev->IdeIoPortReg[Channel].Reg.Command;
+ FeatureReg = AtapiBlkIoDev->IdeIoPortReg[Channel].Reg1.Feature;
+ CylinderLsbReg = AtapiBlkIoDev->IdeIoPortReg[Channel].CylinderLsb;
+ CylinderMsbReg = AtapiBlkIoDev->IdeIoPortReg[Channel].CylinderMsb;
+ DeviceControlReg = AtapiBlkIoDev->IdeIoPortReg[Channel].Alt.DeviceControl;
+ DataReg = AtapiBlkIoDev->IdeIoPortReg[Channel].Data;
+
+ //
+ // Set all the command parameters by fill related registers.
+ // Before write to all the following registers, BSY and DRQ must be 0.
+ //
+ if (DRQClear2 (
+ AtapiBlkIoDev,
+ &(AtapiBlkIoDev->IdeIoPortReg[Channel]),
+ ATATIMEOUT
+ ) != EFI_SUCCESS) {
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // Select device via Device/Head Register.
+ // DEFAULT_CMD: 0xa0 (1010,0000)
+ //
+ IoWrite8 (HeadReg, (UINT8) ((Device << 4) | ATA_DEFAULT_CMD));
+
+ //
+ // No OVL; No DMA
+ //
+ IoWrite8 (FeatureReg, 0x00);
+
+ //
+ // set the transfersize to MAX_ATAPI_BYTE_COUNT to let the device
+ // determine how many data should be transfered.
+ //
+ IoWrite8 (CylinderLsbReg, (UINT8) (ATAPI_MAX_BYTE_COUNT & 0x00ff));
+ IoWrite8 (CylinderMsbReg, (UINT8) (ATAPI_MAX_BYTE_COUNT >> 8));
+
+ //
+ // DEFAULT_CTL:0x0a (0000,1010)
+ // Disable interrupt
+ //
+ IoWrite8 (DeviceControlReg, ATA_DEFAULT_CTL);
+
+ //
+ // Send Packet command to inform device
+ // that the following data bytes are command packet.
+ //
+ IoWrite8 (CommandReg, ATA_CMD_PACKET);
+
+ Status = DRQReady (AtapiBlkIoDev, &(AtapiBlkIoDev->IdeIoPortReg[Channel]), TimeoutInMilliSeconds);
+ if (Status != EFI_SUCCESS) {
+ return Status;
+ }
+ //
+ // Send out command packet
+ //
+ CommandIndex = Packet->Data16;
+ for (Count = 0; Count < 6; Count++, CommandIndex++) {
+ IoWrite16 (DataReg, *CommandIndex);
+ MicroSecondDelay (10);
+ }
+
+ StatusValue = IoRead8 (StatusReg);
+ if ((StatusValue & ATA_STSREG_ERR) == ATA_STSREG_ERR) {
+ //
+ // Trouble! Something's wrong here... Wait some time and return. 3 second is
+ // supposed to be long enough for a device reset latency or error recovery
+ //
+ MicroSecondDelay (3000000);
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (Buffer == NULL || ByteCount == 0) {
+ return EFI_SUCCESS;
+ }
+ //
+ // call PioReadWriteData() function to get
+ // requested transfer data form device.
+ //
+ PtrBuffer = Buffer;
+ RequiredWordCount = ByteCount / 2;
+ //
+ // ActuralWordCount means the word count of data really transfered.
+ //
+ ActualWordCount = 0;
+
+ Status = EFI_SUCCESS;
+ while ((Status == EFI_SUCCESS) && (ActualWordCount < RequiredWordCount)) {
+ //
+ // before each data transfer stream, the host should poll DRQ bit ready,
+ // which informs device is ready to transfer data.
+ //
+ if (DRQReady2 (
+ AtapiBlkIoDev,
+ &(AtapiBlkIoDev->IdeIoPortReg[Channel]),
+ TimeoutInMilliSeconds
+ ) != EFI_SUCCESS) {
+ return CheckErrorStatus (AtapiBlkIoDev, StatusReg);
+ }
+ //
+ // read Status Register will clear interrupt
+ //
+ StatusValue = IoRead8 (StatusReg);
+
+ //
+ // get current data transfer size from Cylinder Registers.
+ //
+ WordCount = IoRead8 (CylinderMsbReg) << 8;
+ WordCount = WordCount | IoRead8 (CylinderLsbReg);
+ WordCount = WordCount & 0xffff;
+ WordCount /= 2;
+
+ //
+ // perform a series data In/Out.
+ //
+ for (Index = 0; (Index < WordCount) && (ActualWordCount < RequiredWordCount); Index++, ActualWordCount++) {
+
+ *PtrBuffer = IoRead16 (DataReg);
+
+ PtrBuffer++;
+
+ }
+
+ if (((ATAPI_REQUEST_SENSE_CMD *) Packet)->opcode == ATA_CMD_REQUEST_SENSE && ActualWordCount >= 4) {
+ RequiredWordCount = MIN (
+ RequiredWordCount,
+ (UINT32) (4 + (((ATAPI_REQUEST_SENSE_DATA *) Buffer)->addnl_sense_length / 2))
+ );
+ }
+
+ }
+ //
+ // After data transfer is completed, normally, DRQ bit should clear.
+ //
+ Status = DRQClear2 (AtapiBlkIoDev, &(AtapiBlkIoDev->IdeIoPortReg[Channel]), TimeoutInMilliSeconds);
+ if (Status != EFI_SUCCESS) {
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // read status register to check whether error happens.
+ //
+ Status = CheckErrorStatus (AtapiBlkIoDev, StatusReg);
+ return Status;
+}
+
+/**
+ Sends out ATAPI Inquiry Packet Command to the specified device.
+ This command will return INQUIRY data of the device.
+
+ @param[in] AtapiBlkIoDev A pointer to atapi block IO device.
+ @param[in] DevicePosition An integer to signify device position.
+ @param[out] MediaInfo The media information of the specified block media.
+
+ @retval EFI_SUCCESS Command executed successfully.
+ @retval EFI_DEVICE_ERROR Device cannot be executed command successfully.
+ @retval EFI_UNSUPPORTED Unsupported device type.
+
+**/
+EFI_STATUS
+Inquiry (
+ IN ATAPI_BLK_IO_DEV *AtapiBlkIoDev,
+ IN UINTN DevicePosition,
+ OUT EFI_PEI_BLOCK_IO_MEDIA *MediaInfo
+ )
+{
+ ATAPI_PACKET_COMMAND Packet;
+ EFI_STATUS Status;
+ ATAPI_INQUIRY_DATA Idata;
+
+ //
+ // prepare command packet for the ATAPI Inquiry Packet Command.
+ //
+ ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND));
+
+ Packet.Inquiry.opcode = ATA_CMD_INQUIRY;
+ Packet.Inquiry.page_code = 0;
+ Packet.Inquiry.allocation_length = (UINT8) sizeof (ATAPI_INQUIRY_DATA);
+
+ //
+ // Send command packet and get requested Inquiry data.
+ //
+ Status = AtapiPacketCommandIn (
+ AtapiBlkIoDev,
+ DevicePosition,
+ &Packet,
+ (UINT16 *) (&Idata),
+ sizeof (ATAPI_INQUIRY_DATA),
+ ATAPITIMEOUT
+ //50
+ );
+
+ if (Status != EFI_SUCCESS) {
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // Identify device type via INQUIRY data.
+ //
+ switch (Idata.peripheral_type & 0x1f) {
+ case 0x00:
+ //
+ // Magnetic Disk
+ //
+ MediaInfo->DeviceType = IdeLS120;
+ MediaInfo->MediaPresent = FALSE;
+ MediaInfo->LastBlock = 0;
+ MediaInfo->BlockSize = 0x200;
+ break;
+
+ case 0x05:
+ //
+ // CD-ROM
+ //
+ MediaInfo->DeviceType = IdeCDROM;
+ MediaInfo->MediaPresent = FALSE;
+ MediaInfo->LastBlock = 0;
+ MediaInfo->BlockSize = 0x800;
+ break;
+
+ default:
+ return EFI_UNSUPPORTED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Used before read/write blocks from/to ATAPI device media.
+ Since ATAPI device media is removable, it is necessary to detect
+ whether media is present and get current present media's information.
+
+ @param[in] AtapiBlkIoDev A pointer to atapi block IO device.
+ @param[in] DevicePosition An integer to signify device position.
+ @param[in, out] MediaInfo The media information of the specified block media.
+
+ @retval EFI_SUCCESS Command executed successfully.
+ @retval EFI_DEVICE_ERROR Some device errors happen.
+ @retval EFI_OUT_OF_RESOURCES Can not allocate required resources.
+
+**/
+EFI_STATUS
+DetectMedia (
+ IN ATAPI_BLK_IO_DEV *AtapiBlkIoDev,
+ IN UINTN DevicePosition,
+ IN OUT EFI_PEI_BLOCK_IO_MEDIA *MediaInfo
+ )
+{
+
+ UINTN Index;
+ UINTN RetryNum;
+ UINTN MaxRetryNum;
+ ATAPI_REQUEST_SENSE_DATA *SenseBuffers;
+ BOOLEAN NeedReadCapacity;
+ BOOLEAN NeedRetry;
+ EFI_STATUS Status;
+ UINT8 SenseCounts;
+
+ SenseBuffers = AllocatePages (EFI_SIZE_TO_PAGES (sizeof (*SenseBuffers)));
+ if (SenseBuffers == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Test Unit Ready command is used to detect whether device is accessible,
+ // the device will produce corresponding Sense data.
+ //
+ for (Index = 0; Index < 2; Index++) {
+
+ Status = TestUnitReady (AtapiBlkIoDev, DevicePosition);
+ if (Status != EFI_SUCCESS) {
+ Status = ResetDevice (AtapiBlkIoDev, DevicePosition, FALSE);
+
+ if (Status != EFI_SUCCESS) {
+ ResetDevice (AtapiBlkIoDev, DevicePosition, TRUE);
+ }
+
+ } else {
+ break;
+ }
+ }
+
+ SenseCounts = MAX_SENSE_KEY_COUNT;
+ Status = EFI_SUCCESS;
+ NeedReadCapacity = TRUE;
+
+ for (Index = 0; Index < 5; Index++) {
+ SenseCounts = MAX_SENSE_KEY_COUNT;
+ Status = RequestSense (
+ AtapiBlkIoDev,
+ DevicePosition,
+ SenseBuffers,
+ &SenseCounts
+ );
+ DEBUG ((EFI_D_INFO, "Atapi Request Sense Count is %d\n", SenseCounts));
+ if (IsDeviceStateUnclear (SenseBuffers, SenseCounts) || IsNoMedia (SenseBuffers, SenseCounts)) {
+ //
+ // We are not sure whether the media is present or not, try again
+ //
+ TestUnitReady (AtapiBlkIoDev, DevicePosition);
+ } else {
+ break;
+ }
+ }
+
+ if (Status == EFI_SUCCESS) {
+
+ if (IsNoMedia (SenseBuffers, SenseCounts)) {
+
+ NeedReadCapacity = FALSE;
+ MediaInfo->MediaPresent = FALSE;
+ MediaInfo->LastBlock = 0;
+ }
+
+ if (IsMediaError (SenseBuffers, SenseCounts)) {
+ return EFI_DEVICE_ERROR;
+ }
+ }
+
+ if (NeedReadCapacity) {
+ //
+ // at most retry 5 times
+ //
+ MaxRetryNum = 5;
+ RetryNum = 1;
+ //
+ // initial retry once
+ //
+ for (Index = 0; (Index < RetryNum) && (Index < MaxRetryNum); Index++) {
+
+ Status = ReadCapacity (AtapiBlkIoDev, DevicePosition, MediaInfo);
+ MicroSecondDelay (200000);
+ SenseCounts = MAX_SENSE_KEY_COUNT;
+
+ if (Status != EFI_SUCCESS) {
+
+ Status = RequestSense (AtapiBlkIoDev, DevicePosition, SenseBuffers, &SenseCounts);
+ //
+ // If Request Sense data failed, reset the device and retry.
+ //
+ if (Status != EFI_SUCCESS) {
+
+ Status = ResetDevice (AtapiBlkIoDev, DevicePosition, FALSE);
+ //
+ // if ATAPI soft reset fail,
+ // use stronger reset mechanism -- ATA soft reset.
+ //
+ if (Status != EFI_SUCCESS) {
+ ResetDevice (AtapiBlkIoDev, DevicePosition, TRUE);
+ }
+
+ RetryNum++;
+ //
+ // retry once more
+ //
+ continue;
+ }
+ //
+ // No Media
+ //
+ if (IsNoMedia (SenseBuffers, SenseCounts)) {
+
+ MediaInfo->MediaPresent = FALSE;
+ MediaInfo->LastBlock = 0;
+ break;
+ }
+
+ if (IsMediaError (SenseBuffers, SenseCounts)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (!IsDriveReady (SenseBuffers, SenseCounts, &NeedRetry)) {
+ //
+ // Drive not ready: if NeedRetry, then retry once more;
+ // else return error
+ //
+ if (NeedRetry) {
+ RetryNum++;
+ continue;
+ } else {
+ return EFI_DEVICE_ERROR;
+ }
+ }
+ //
+ // if read capacity fail not for above reasons, retry once more
+ //
+ RetryNum++;
+
+ }
+
+ }
+
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Reset specified Atapi device.
+
+ @param[in] AtapiBlkIoDev A pointer to atapi block IO device.
+ @param[in] DevicePosition An integer to signify device position.
+ @param[in] Extensive If TRUE, use ATA soft reset, otherwise use Atapi soft reset.
+
+ @retval EFI_SUCCESS Command executed successfully.
+ @retval EFI_DEVICE_ERROR Some device errors happen.
+
+**/
+EFI_STATUS
+ResetDevice (
+ IN ATAPI_BLK_IO_DEV *AtapiBlkIoDev,
+ IN UINTN DevicePosition,
+ IN BOOLEAN Extensive
+ )
+{
+ UINT8 DevControl;
+ UINT8 Command;
+ UINT8 DeviceSelect;
+ UINT16 DeviceControlReg;
+ UINT16 CommandReg;
+ UINT16 HeadReg;
+ UINT8 Channel;
+ UINT8 Device;
+
+ Channel = (UINT8) (DevicePosition / 2);
+ Device = (UINT8) (DevicePosition % 2);
+
+ ASSERT (Channel < MAX_IDE_CHANNELS);
+
+ DeviceControlReg = AtapiBlkIoDev->IdeIoPortReg[Channel].Alt.DeviceControl;
+ CommandReg = AtapiBlkIoDev->IdeIoPortReg[Channel].Reg.Command;
+ HeadReg = AtapiBlkIoDev->IdeIoPortReg[Channel].Head;
+
+ if (Extensive) {
+
+ DevControl = 0;
+ DevControl |= ATA_CTLREG_SRST;
+ //
+ // set SRST bit to initiate soft reset
+ //
+ DevControl |= BIT1;
+ //
+ // disable Interrupt
+ //
+ IoWrite8 (DeviceControlReg, DevControl);
+
+ //
+ // Wait 10us
+ //
+ MicroSecondDelay (10);
+
+ //
+ // Clear SRST bit
+ //
+ DevControl &= 0xfb;
+ //
+ // 0xfb:1111,1011
+ //
+ IoWrite8 (DeviceControlReg, DevControl);
+
+ //
+ // slave device needs at most 31s to clear BSY
+ //
+ if (WaitForBSYClear (AtapiBlkIoDev, &(AtapiBlkIoDev->IdeIoPortReg[Channel]), 31000) == EFI_TIMEOUT) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ } else {
+ //
+ // for ATAPI device, no need to wait DRDY ready after device selecting.
+ // bit7 and bit5 are both set to 1 for backward compatibility
+ //
+ DeviceSelect = (UINT8) (((BIT7 | BIT5) | (Device << 4)));
+ IoWrite8 (HeadReg, DeviceSelect);
+
+ Command = ATA_CMD_SOFT_RESET;
+ IoWrite8 (CommandReg, Command);
+
+ //
+ // BSY cleared is the only status return to the host by the device when reset is completed
+ // slave device needs at most 31s to clear BSY
+ //
+ if (WaitForBSYClear (AtapiBlkIoDev, &(AtapiBlkIoDev->IdeIoPortReg[Channel]), 31000) != EFI_SUCCESS) {
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // stall 5 seconds to make the device status stable
+ //
+ MicroSecondDelay (STALL_1_SECONDS * 5);
+ }
+
+ return EFI_SUCCESS;
+
+}
+
+/**
+ Sends out ATAPI Request Sense Packet Command to the specified device.
+
+ @param[in] AtapiBlkIoDev A pointer to atapi block IO device.
+ @param[in] DevicePosition An integer to signify device position.
+ @param[in] SenseBuffers Pointer to sense buffer.
+ @param[in, out] SenseCounts Length of sense buffer.
+
+ @retval EFI_SUCCESS Command executed successfully.
+ @retval EFI_DEVICE_ERROR Some device errors happen.
+
+**/
+EFI_STATUS
+RequestSense (
+ IN ATAPI_BLK_IO_DEV *AtapiBlkIoDev,
+ IN UINTN DevicePosition,
+ IN ATAPI_REQUEST_SENSE_DATA *SenseBuffers,
+ IN OUT UINT8 *SenseCounts
+ )
+{
+ EFI_STATUS Status;
+ ATAPI_REQUEST_SENSE_DATA *Sense;
+ UINT16 *Ptr;
+ BOOLEAN SenseReq;
+ ATAPI_PACKET_COMMAND Packet;
+
+ ZeroMem (SenseBuffers, sizeof (ATAPI_REQUEST_SENSE_DATA) * (*SenseCounts));
+ //
+ // fill command packet for Request Sense Packet Command
+ //
+ ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND));
+ Packet.RequestSence.opcode = ATA_CMD_REQUEST_SENSE;
+ Packet.RequestSence.allocation_length = (UINT8) sizeof (ATAPI_REQUEST_SENSE_DATA);
+
+ Ptr = (UINT16 *) SenseBuffers;
+ //
+ // initialize pointer
+ //
+ *SenseCounts = 0;
+ //
+ // request sense data from device continiously until no sense data exists in the device.
+ //
+ for (SenseReq = TRUE; SenseReq;) {
+
+ Sense = (ATAPI_REQUEST_SENSE_DATA *) Ptr;
+
+ //
+ // send out Request Sense Packet Command and get one Sense data form device
+ //
+ Status = AtapiPacketCommandIn (
+ AtapiBlkIoDev,
+ DevicePosition,
+ &Packet,
+ Ptr,
+ sizeof (ATAPI_REQUEST_SENSE_DATA),
+ ATAPITIMEOUT
+ );
+ //
+ // failed to get Sense data
+ //
+ if (Status != EFI_SUCCESS) {
+ if (*SenseCounts == 0) {
+ return EFI_DEVICE_ERROR;
+ } else {
+ return EFI_SUCCESS;
+ }
+ }
+
+ (*SenseCounts)++;
+
+ if (*SenseCounts > MAX_SENSE_KEY_COUNT) {
+ return EFI_SUCCESS;
+ }
+ //
+ // We limit MAX sense data count to 20 in order to avoid dead loop. Some
+ // incompatible ATAPI devices don't retrive NO_SENSE when there is no media.
+ // In this case, dead loop occurs if we don't have a gatekeeper. 20 is
+ // supposed to be large enough for any ATAPI device.
+ //
+ if ((Sense->sense_key != ATA_SK_NO_SENSE) && ((*SenseCounts) < 20)) {
+
+ Ptr += sizeof (ATAPI_REQUEST_SENSE_DATA) / 2;
+ //
+ // Ptr is word based pointer
+ //
+ } else {
+ //
+ // when no sense key, skip out the loop
+ //
+ SenseReq = FALSE;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Sends out ATAPI Read Capacity Packet Command to the specified device.
+ This command will return the information regarding the capacity of the
+ media in the device.
+
+ @param[in] AtapiBlkIoDev A pointer to atapi block IO device.
+ @param[in] DevicePosition An integer to signify device position.
+ @param[in, out] MediaInfo The media information of the specified block media.
+
+ @retval EFI_SUCCESS Command executed successfully.
+ @retval EFI_DEVICE_ERROR Some device errors happen.
+
+**/
+EFI_STATUS
+ReadCapacity (
+ IN ATAPI_BLK_IO_DEV *AtapiBlkIoDev,
+ IN UINTN DevicePosition,
+ IN OUT EFI_PEI_BLOCK_IO_MEDIA *MediaInfo
+ )
+{
+ EFI_STATUS Status;
+ ATAPI_PACKET_COMMAND Packet;
+
+ //
+ // used for capacity data returned from ATAPI device
+ //
+ ATAPI_READ_CAPACITY_DATA Data;
+ ATAPI_READ_FORMAT_CAPACITY_DATA FormatData;
+
+ ZeroMem (&Data, sizeof (Data));
+ ZeroMem (&FormatData, sizeof (FormatData));
+
+ if (MediaInfo->DeviceType == IdeCDROM) {
+
+ ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND));
+ Packet.Inquiry.opcode = ATA_CMD_READ_CAPACITY;
+ Status = AtapiPacketCommandIn (
+ AtapiBlkIoDev,
+ DevicePosition,
+ &Packet,
+ (UINT16 *) (&Data),
+ sizeof (ATAPI_READ_CAPACITY_DATA),
+ ATAPITIMEOUT
+ );
+
+ } else {
+ //
+ // DeviceType == IdeLS120
+ //
+ ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND));
+ Packet.ReadFormatCapacity.opcode = ATA_CMD_READ_FORMAT_CAPACITY;
+ Packet.ReadFormatCapacity.allocation_length_lo = 12;
+ Status = AtapiPacketCommandIn (
+ AtapiBlkIoDev,
+ DevicePosition,
+ &Packet,
+ (UINT16 *) (&FormatData),
+ sizeof (ATAPI_READ_FORMAT_CAPACITY_DATA),
+ ATAPITIMEOUT*10
+ );
+ }
+
+ if (Status == EFI_SUCCESS) {
+
+ if (MediaInfo->DeviceType == IdeCDROM) {
+
+ MediaInfo->LastBlock = (Data.LastLba3 << 24) | (Data.LastLba2 << 16) | (Data.LastLba1 << 8) | Data.LastLba0;
+ MediaInfo->MediaPresent = TRUE;
+ //
+ // Because the user data portion in the sector of the Data CD supported
+ // is always 800h
+ //
+ MediaInfo->BlockSize = 0x800;
+ }
+
+ if (MediaInfo->DeviceType == IdeLS120) {
+
+ if (FormatData.DesCode == 3) {
+ MediaInfo->MediaPresent = FALSE;
+ MediaInfo->LastBlock = 0;
+ } else {
+ MediaInfo->LastBlock = (FormatData.LastLba3 << 24) |
+ (FormatData.LastLba2 << 16) |
+ (FormatData.LastLba1 << 8) |
+ FormatData.LastLba0;
+ MediaInfo->LastBlock--;
+
+ MediaInfo->MediaPresent = TRUE;
+
+ MediaInfo->BlockSize = 0x200;
+
+ }
+ }
+
+ return EFI_SUCCESS;
+
+ } else {
+ return EFI_DEVICE_ERROR;
+ }
+}
+
+/**
+ Perform read from disk in block unit.
+
+ @param[in] AtapiBlkIoDev A pointer to atapi block IO device.
+ @param[in] DevicePosition An integer to signify device position.
+ @param[in] Buffer Buffer to contain read data.
+ @param[in] StartLba Starting LBA address.
+ @param[in] NumberOfBlocks Number of blocks to read.
+ @param[in] BlockSize Size of each block.
+
+ @retval EFI_SUCCESS Command executed successfully.
+ @retval EFI_DEVICE_ERROR Some device errors happen.
+
+**/
+EFI_STATUS
+ReadSectors (
+ IN ATAPI_BLK_IO_DEV *AtapiBlkIoDev,
+ IN UINTN DevicePosition,
+ IN VOID *Buffer,
+ IN EFI_PEI_LBA StartLba,
+ IN UINTN NumberOfBlocks,
+ IN UINTN BlockSize
+ )
+{
+
+ ATAPI_PACKET_COMMAND Packet;
+ ATAPI_READ10_CMD *Read10Packet;
+ EFI_STATUS Status;
+ UINTN BlocksRemaining;
+ UINT32 Lba32;
+ UINT32 ByteCount;
+ UINT16 SectorCount;
+ VOID *PtrBuffer;
+ UINT16 MaxBlock;
+
+ //
+ // fill command packet for Read(10) command
+ //
+ ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND));
+ Read10Packet = &Packet.Read10;
+ Lba32 = (UINT32) StartLba;
+ PtrBuffer = Buffer;
+
+ //
+ // limit the data bytes that can be transfered by one Read(10) Command
+ //
+ MaxBlock = (UINT16) (0x10000 / BlockSize);
+ //
+ // (64k bytes)
+ //
+ BlocksRemaining = NumberOfBlocks;
+
+ Status = EFI_SUCCESS;
+ while (BlocksRemaining > 0) {
+
+ if (BlocksRemaining <= MaxBlock) {
+ SectorCount = (UINT16) BlocksRemaining;
+ } else {
+ SectorCount = MaxBlock;
+ }
+ //
+ // fill the Packet data sturcture
+ //
+ Read10Packet->opcode = ATA_CMD_READ_10;
+
+ //
+ // Lba0 ~ Lba3 specify the start logical block address of the data transfer.
+ // Lba0 is MSB, Lba3 is LSB
+ //
+ Read10Packet->Lba3 = (UINT8) (Lba32 & 0xff);
+ Read10Packet->Lba2 = (UINT8) (Lba32 >> 8);
+ Read10Packet->Lba1 = (UINT8) (Lba32 >> 16);
+ Read10Packet->Lba0 = (UINT8) (Lba32 >> 24);
+
+ //
+ // TranLen0 ~ TranLen1 specify the transfer length in block unit.
+ // TranLen0 is MSB, TranLen is LSB
+ //
+ Read10Packet->TranLen1 = (UINT8) (SectorCount & 0xff);
+ Read10Packet->TranLen0 = (UINT8) (SectorCount >> 8);
+
+ ByteCount = (UINT32) (SectorCount * BlockSize);
+
+ Status = AtapiPacketCommandIn (
+ AtapiBlkIoDev,
+ DevicePosition,
+ &Packet,
+ (UINT16 *) PtrBuffer,
+ ByteCount,
+ ATAPILONGTIMEOUT
+ );
+ if (Status != EFI_SUCCESS) {
+ return Status;
+ }
+
+ Lba32 += SectorCount;
+ PtrBuffer = (UINT8 *) PtrBuffer + SectorCount * BlockSize;
+ BlocksRemaining -= SectorCount;
+ }
+
+ return Status;
+}
+
+/**
+ Check if there is media according to sense data.
+
+ @param[in] SenseData Pointer to sense data.
+ @param[in] SenseCounts Count of sense data.
+
+ @retval TRUE No media
+ @retval FALSE Media exists
+
+**/
+BOOLEAN
+IsNoMedia (
+ IN ATAPI_REQUEST_SENSE_DATA *SenseData,
+ IN UINTN SenseCounts
+ )
+{
+ ATAPI_REQUEST_SENSE_DATA *SensePtr;
+ UINTN Index;
+ BOOLEAN IsNoMedia;
+
+ IsNoMedia = FALSE;
+
+ SensePtr = SenseData;
+
+ for (Index = 0; Index < SenseCounts; Index++) {
+
+ if ((SensePtr->sense_key == ATA_SK_NOT_READY) && (SensePtr->addnl_sense_code == ATA_ASC_NO_MEDIA)) {
+ IsNoMedia = TRUE;
+ }
+
+ SensePtr++;
+ }
+
+ return IsNoMedia;
+}
+
+/**
+ Check if device state is unclear according to sense data.
+
+ @param[in] SenseData Pointer to sense data.
+ @param[in] SenseCounts Count of sense data.
+
+ @retval TRUE Device state is unclear
+ @retval FALSE Device state is clear
+
+**/
+BOOLEAN
+IsDeviceStateUnclear (
+ IN ATAPI_REQUEST_SENSE_DATA *SenseData,
+ IN UINTN SenseCounts
+ )
+{
+ ATAPI_REQUEST_SENSE_DATA *SensePtr;
+ UINTN Index;
+ BOOLEAN Unclear;
+
+ Unclear = FALSE;
+
+ SensePtr = SenseData;
+
+ for (Index = 0; Index < SenseCounts; Index++) {
+
+ if (SensePtr->sense_key == 0x06) {
+ //
+ // Sense key is 0x06 means the device is just be reset or media just
+ // changed. The current state of the device is unclear.
+ //
+ Unclear = TRUE;
+ break;
+ }
+
+ SensePtr++;
+ }
+
+ return Unclear;
+}
+
+/**
+ Check if there is media error according to sense data.
+
+ @param[in] SenseData Pointer to sense data.
+ @param[in] SenseCounts Count of sense data.
+
+ @retval TRUE Media error
+ @retval FALSE No media error
+
+**/
+BOOLEAN
+IsMediaError (
+ IN ATAPI_REQUEST_SENSE_DATA *SenseData,
+ IN UINTN SenseCounts
+ )
+{
+ ATAPI_REQUEST_SENSE_DATA *SensePtr;
+ UINTN Index;
+ BOOLEAN IsError;
+
+ IsError = FALSE;
+
+ SensePtr = SenseData;
+
+ for (Index = 0; Index < SenseCounts; Index++) {
+
+ switch (SensePtr->sense_key) {
+
+ case ATA_SK_MEDIUM_ERROR:
+ switch (SensePtr->addnl_sense_code) {
+ case ATA_ASC_MEDIA_ERR1:
+ //
+ // fall through
+ //
+ case ATA_ASC_MEDIA_ERR2:
+ //
+ // fall through
+ //
+ case ATA_ASC_MEDIA_ERR3:
+ //
+ // fall through
+ //
+ case ATA_ASC_MEDIA_ERR4:
+ IsError = TRUE;
+ break;
+
+ default:
+ break;
+ }
+
+ break;
+
+ case ATA_SK_NOT_READY:
+ switch (SensePtr->addnl_sense_code) {
+ case ATA_ASC_MEDIA_UPSIDE_DOWN:
+ IsError = TRUE;
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ SensePtr++;
+ }
+
+ return IsError;
+}
+
+/**
+ Check if drive is ready according to sense data.
+
+ @param[in] SenseData Pointer to sense data.
+ @param[in] SenseCounts Count of sense data.
+ @param[out] NeedRetry Indicate if retry is needed.
+
+ @retval TRUE Drive ready
+ @retval FALSE Drive not ready
+
+**/
+BOOLEAN
+IsDriveReady (
+ IN ATAPI_REQUEST_SENSE_DATA *SenseData,
+ IN UINTN SenseCounts,
+ OUT BOOLEAN *NeedRetry
+ )
+{
+ ATAPI_REQUEST_SENSE_DATA *SensePtr;
+ UINTN Index;
+ BOOLEAN IsReady;
+
+ IsReady = TRUE;
+ *NeedRetry = FALSE;
+
+ SensePtr = SenseData;
+
+ for (Index = 0; Index < SenseCounts; Index++) {
+
+ switch (SensePtr->sense_key) {
+
+ case ATA_SK_NOT_READY:
+ switch (SensePtr->addnl_sense_code) {
+ case ATA_ASC_NOT_READY:
+ switch (SensePtr->addnl_sense_code_qualifier) {
+ case ATA_ASCQ_IN_PROGRESS:
+ IsReady = FALSE;
+ *NeedRetry = TRUE;
+ break;
+
+ default:
+ IsReady = FALSE;
+ *NeedRetry = FALSE;
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ SensePtr++;
+ }
+
+ return IsReady;
+}
diff --git a/MdeModulePkg/Bus/Pci/IdeBusPei/AtapiPeim.h b/MdeModulePkg/Bus/Pci/IdeBusPei/AtapiPeim.h
new file mode 100644
index 000000000..5cbd6fd96
--- /dev/null
+++ b/MdeModulePkg/Bus/Pci/IdeBusPei/AtapiPeim.h
@@ -0,0 +1,667 @@
+/** @file
+Private Include file for IdeBus PEIM.
+
+Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>
+
+This program and the accompanying materials
+are licensed and made available under the terms and conditions
+of the BSD License which accompanies this distribution. The
+full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _RECOVERY_ATAPI_H_
+#define _RECOVERY_ATAPI_H_
+
+#include <PiPei.h>
+
+#include <Ppi/BlockIo.h>
+#include <Ppi/AtaController.h>
+
+#include <Library/DebugLib.h>
+#include <Library/TimerLib.h>
+#include <Library/PeimEntryPoint.h>
+#include <Library/PeiServicesLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/IoLib.h>
+#include <Library/PeiServicesTablePointerLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PcdLib.h>
+
+
+#include <IndustryStandard/Atapi.h>
+
+#define MAX_SENSE_KEY_COUNT 6
+#define MAX_IDE_CHANNELS 4 // Ide and Sata Primary, Secondary Channel.
+#define MAX_IDE_DEVICES 8 // Ide, Sata Primary, Secondary and Master, Slave device.
+
+typedef enum {
+ IdePrimary = 0,
+ IdeSecondary = 1,
+ IdeMaxChannel = 2
+} EFI_IDE_CHANNEL;
+
+typedef enum {
+ IdeMaster = 0,
+ IdeSlave = 1,
+ IdeMaxDevice = 2
+} EFI_IDE_DEVICE;
+
+//
+// IDE Registers
+//
+typedef union {
+ UINT16 Command; /* when write */
+ UINT16 Status; /* when read */
+} IDE_CMD_OR_STATUS;
+
+typedef union {
+ UINT16 Error; /* when read */
+ UINT16 Feature; /* when write */
+} IDE_ERROR_OR_FEATURE;
+
+typedef union {
+ UINT16 AltStatus; /* when read */
+ UINT16 DeviceControl; /* when write */
+} IDE_ALTSTATUS_OR_DEVICECONTROL;
+
+//
+// IDE registers set
+//
+typedef struct {
+ UINT16 Data;
+ IDE_ERROR_OR_FEATURE Reg1;
+ UINT16 SectorCount;
+ UINT16 SectorNumber;
+ UINT16 CylinderLsb;
+ UINT16 CylinderMsb;
+ UINT16 Head;
+ IDE_CMD_OR_STATUS Reg;
+
+ IDE_ALTSTATUS_OR_DEVICECONTROL Alt;
+ UINT16 DriveAddress;
+} IDE_BASE_REGISTERS;
+
+typedef struct {
+
+ UINTN DevicePosition;
+ EFI_PEI_BLOCK_IO_MEDIA MediaInfo;
+
+} PEI_ATAPI_DEVICE_INFO;
+
+#define ATAPI_BLK_IO_DEV_SIGNATURE SIGNATURE_32 ('a', 'b', 'i', 'o')
+typedef struct {
+ UINTN Signature;
+
+ EFI_PEI_RECOVERY_BLOCK_IO_PPI AtapiBlkIo;
+ EFI_PEI_PPI_DESCRIPTOR PpiDescriptor;
+ PEI_ATA_CONTROLLER_PPI *AtaControllerPpi;
+
+ UINTN DeviceCount;
+ PEI_ATAPI_DEVICE_INFO DeviceInfo[MAX_IDE_DEVICES]; //for max 8 device
+ IDE_BASE_REGISTERS IdeIoPortReg[MAX_IDE_CHANNELS]; //for max 4 channel.
+} ATAPI_BLK_IO_DEV;
+
+#define PEI_RECOVERY_ATAPI_FROM_BLKIO_THIS(a) CR (a, ATAPI_BLK_IO_DEV, AtapiBlkIo, ATAPI_BLK_IO_DEV_SIGNATURE)
+
+
+#define STALL_1_MILLI_SECOND 1000 // stall 1 ms
+#define STALL_1_SECONDS 1000 * STALL_1_MILLI_SECOND
+
+//
+// Time Out Value For IDE Device Polling
+//
+// ATATIMEOUT is used for waiting time out for ATA device
+//
+#define ATATIMEOUT 1000 // 1 second
+// ATAPITIMEOUT is used for waiting operation
+// except read and write time out for ATAPI device
+//
+#define ATAPITIMEOUT 1000 // 1 second
+// ATAPILONGTIMEOUT is used for waiting read and
+// write operation timeout for ATAPI device
+//
+#define CDROMLONGTIMEOUT 2000 // 2 seconds
+#define ATAPILONGTIMEOUT 5000 // 5 seconds
+
+//
+// PEI Recovery Block I/O PPI
+//
+
+/**
+ Gets the count of block I/O devices that one specific block driver detects.
+
+ This function is used for getting the count of block I/O devices that one
+ specific block driver detects. To the PEI ATAPI driver, it returns the number
+ of all the detected ATAPI devices it detects during the enumeration process.
+ To the PEI legacy floppy driver, it returns the number of all the legacy
+ devices it finds during its enumeration process. If no device is detected,
+ then the function will return zero.
+
+ @param[in] PeiServices General-purpose services that are available
+ to every PEIM.
+ @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI
+ instance.
+ @param[out] NumberBlockDevices The number of block I/O devices discovered.
+
+ @retval EFI_SUCCESS Operation performed successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+AtapiGetNumberOfBlockDevices (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This,
+ OUT UINTN *NumberBlockDevices
+ );
+
+/**
+ Gets a block device's media information.
+
+ This function will provide the caller with the specified block device's media
+ information. If the media changes, calling this function will update the media
+ information accordingly.
+
+ @param[in] PeiServices General-purpose services that are available to every
+ PEIM
+ @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance.
+ @param[in] DeviceIndex Specifies the block device to which the function wants
+ to talk. Because the driver that implements Block I/O
+ PPIs will manage multiple block devices, the PPIs that
+ want to talk to a single device must specify the
+ device index that was assigned during the enumeration
+ process. This index is a number from one to
+ NumberBlockDevices.
+ @param[out] MediaInfo The media information of the specified block media.
+ The caller is responsible for the ownership of this
+ data structure.
+
+ @retval EFI_SUCCESS Media information about the specified block device
+ was obtained successfully.
+ @retval EFI_DEVICE_ERROR Cannot get the media information due to a hardware
+ error.
+ @retval Others Other failure occurs.
+
+**/
+EFI_STATUS
+EFIAPI
+AtapiGetBlockDeviceMediaInfo (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This,
+ IN UINTN DeviceIndex,
+ OUT EFI_PEI_BLOCK_IO_MEDIA *MediaInfo
+ );
+
+/**
+ Reads the requested number of blocks from the specified block device.
+
+ The function reads the requested number of blocks from the device. All the
+ blocks are read, or an error is returned. If there is no media in the device,
+ the function returns EFI_NO_MEDIA.
+
+ @param[in] PeiServices General-purpose services that are available to
+ every PEIM.
+ @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance.
+ @param[in] DeviceIndex Specifies the block device to which the function wants
+ to talk. Because the driver that implements Block I/O
+ PPIs will manage multiple block devices, the PPIs that
+ want to talk to a single device must specify the device
+ index that was assigned during the enumeration process.
+ This index is a number from one to NumberBlockDevices.
+ @param[in] StartLBA The starting logical block address (LBA) to read from
+ on the device
+ @param[in] BufferSize The size of the Buffer in bytes. This number must be
+ a multiple of the intrinsic block size of the device.
+ @param[out] Buffer A pointer to the destination buffer for the data.
+ The caller is responsible for the ownership of the
+ buffer.
+
+ @retval EFI_SUCCESS The data was read correctly from the device.
+ @retval EFI_DEVICE_ERROR The device reported an error while attempting
+ to perform the read operation.
+ @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not
+ valid, or the buffer is not properly aligned.
+ @retval EFI_NO_MEDIA There is no media in the device.
+ @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of
+ the intrinsic block size of the device.
+
+**/
+EFI_STATUS
+EFIAPI
+AtapiReadBlocks (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This,
+ IN UINTN DeviceIndex,
+ IN EFI_PEI_LBA StartLBA,
+ IN UINTN BufferSize,
+ OUT VOID *Buffer
+ );
+
+//
+// Internal functions
+//
+
+/**
+ Enumerate Atapi devices.
+
+ This function is used to enumerate Atatpi device in Ide channel.
+
+ @param[in] AtapiBlkIoDev A pointer to atapi block IO device
+
+**/
+VOID
+AtapiEnumerateDevices (
+ IN ATAPI_BLK_IO_DEV *AtapiBlkIoDev
+ );
+
+/**
+ Detect Atapi devices.
+
+ @param[in] AtapiBlkIoDev A pointer to atapi block IO device.
+ @param[in] DevicePosition An integer to signify device position.
+ @param[out] MediaInfo The media information of the specified block media.
+
+ @retval TRUE Atapi device exists in specified position.
+ @retval FALSE Atapi device does not exist in specified position.
+
+**/
+BOOLEAN
+DiscoverAtapiDevice (
+ IN ATAPI_BLK_IO_DEV *AtapiBlkIoDev,
+ IN UINTN DevicePosition,
+ OUT EFI_PEI_BLOCK_IO_MEDIA *MediaInfo
+ );
+
+/**
+ Detect if an IDE controller exists in specified position.
+
+ @param[in] AtapiBlkIoDev A pointer to atapi block IO device.
+ @param[in] DevicePosition An integer to signify device position.
+
+ @retval TRUE The Atapi device exists.
+ @retval FALSE The Atapi device does not present.
+
+**/
+BOOLEAN
+DetectIDEController (
+ IN ATAPI_BLK_IO_DEV *AtapiBlkIoDev,
+ IN UINTN DevicePosition
+ );
+
+/**
+ Wait specified time interval to poll for BSY bit clear in the Status Register.
+
+ @param[in] AtapiBlkIoDev A pointer to atapi block IO device.
+ @param[in] IdeIoRegisters A pointer to IDE IO registers.
+ @param[in] TimeoutInMilliSeconds Time specified in milliseconds.
+
+ @retval EFI_SUCCESS BSY bit is cleared in the specified time interval.
+ @retval EFI_TIMEOUT BSY bit is not cleared in the specified time interval.
+
+**/
+EFI_STATUS
+WaitForBSYClear (
+ IN ATAPI_BLK_IO_DEV *AtapiBlkIoDev,
+ IN IDE_BASE_REGISTERS *IdeIoRegisters,
+ IN UINTN TimeoutInMilliSeconds
+ );
+
+/**
+ Wait specified time interval to poll for DRDY bit set in the Status register.
+
+ @param[in] AtapiBlkIoDev A pointer to atapi block IO device.
+ @param[in] IdeIoRegisters A pointer to IDE IO registers.
+ @param[in] TimeoutInMilliSeconds Time specified in milliseconds.
+
+ @retval EFI_SUCCESS DRDY bit is set in the specified time interval.
+ @retval EFI_TIMEOUT DRDY bit is not set in the specified time interval.
+
+**/
+EFI_STATUS
+DRDYReady (
+ IN ATAPI_BLK_IO_DEV *AtapiBlkIoDev,
+ IN IDE_BASE_REGISTERS *IdeIoRegisters,
+ IN UINTN TimeoutInMilliSeconds
+ );
+
+/**
+ Wait specified time interval to poll for DRQ bit clear in the Status Register.
+
+ @param[in] AtapiBlkIoDev A pointer to atapi block IO device.
+ @param[in] IdeIoRegisters A pointer to IDE IO registers.
+ @param[in] TimeoutInMilliSeconds Time specified in milliseconds.
+
+ @retval EFI_SUCCESS DRQ bit is cleared in the specified time interval.
+ @retval EFI_TIMEOUT DRQ bit is not cleared in the specified time interval.
+
+**/
+EFI_STATUS
+DRQClear (
+ IN ATAPI_BLK_IO_DEV *AtapiBlkIoDev,
+ IN IDE_BASE_REGISTERS *IdeIoRegisters,
+ IN UINTN TimeoutInMilliSeconds
+ );
+
+/**
+ Wait specified time interval to poll for DRQ bit clear in the Alternate Status Register.
+
+ @param[in] AtapiBlkIoDev A pointer to atapi block IO device.
+ @param[in] IdeIoRegisters A pointer to IDE IO registers.
+ @param[in] TimeoutInMilliSeconds Time specified in milliseconds.
+
+ @retval EFI_SUCCESS DRQ bit is cleared in the specified time interval.
+ @retval EFI_TIMEOUT DRQ bit is not cleared in the specified time interval.
+
+**/
+EFI_STATUS
+DRQClear2 (
+ IN ATAPI_BLK_IO_DEV *AtapiBlkIoDev,
+ IN IDE_BASE_REGISTERS *IdeIoRegisters,
+ IN UINTN TimeoutInMilliSeconds
+ );
+
+/**
+ Wait specified time interval to poll for DRQ bit set in the Status Register.
+
+ @param[in] AtapiBlkIoDev A pointer to atapi block IO device.
+ @param[in] IdeIoRegisters A pointer to IDE IO registers.
+ @param[in] TimeoutInMilliSeconds Time specified in milliseconds.
+
+ @retval EFI_SUCCESS DRQ bit is set in the specified time interval.
+ @retval EFI_TIMEOUT DRQ bit is not set in the specified time interval.
+ @retval EFI_ABORTED Operation Aborted.
+
+**/
+EFI_STATUS
+DRQReady (
+ IN ATAPI_BLK_IO_DEV *AtapiBlkIoDev,
+ IN IDE_BASE_REGISTERS *IdeIoRegisters,
+ IN UINTN TimeoutInMilliSeconds
+ );
+
+/**
+ Wait specified time interval to poll for DRQ bit set in the Alternate Status Register.
+
+ @param[in] AtapiBlkIoDev A pointer to atapi block IO device.
+ @param[in] IdeIoRegisters A pointer to IDE IO registers.
+ @param[in] TimeoutInMilliSeconds Time specified in milliseconds.
+
+ @retval EFI_SUCCESS DRQ bit is set in the specified time interval.
+ @retval EFI_TIMEOUT DRQ bit is not set in the specified time interval.
+ @retval EFI_ABORTED Operation Aborted.
+
+**/
+EFI_STATUS
+DRQReady2 (
+ IN ATAPI_BLK_IO_DEV *AtapiBlkIoDev,
+ IN IDE_BASE_REGISTERS *IdeIoRegisters,
+ IN UINTN TimeoutInMilliSeconds
+ );
+
+/**
+ Check if there is an error in Status Register.
+
+ @param[in] AtapiBlkIoDev A pointer to atapi block IO device.
+ @param[in] StatusReg The address to IDE IO registers.
+
+ @retval EFI_SUCCESS Operation success.
+ @retval EFI_DEVICE_ERROR Device error.
+
+**/
+EFI_STATUS
+CheckErrorStatus (
+ IN ATAPI_BLK_IO_DEV *AtapiBlkIoDev,
+ IN UINT16 StatusReg
+ );
+
+/**
+ Idendify Atapi devices.
+
+ @param[in] AtapiBlkIoDev A pointer to atapi block IO device.
+ @param[in] DevicePosition An integer to signify device position.
+
+ @retval EFI_SUCCESS Identify successfully.
+ @retval EFI_DEVICE_ERROR Device cannot be identified successfully.
+
+**/
+EFI_STATUS
+ATAPIIdentify (
+ IN ATAPI_BLK_IO_DEV *AtapiBlkIoDev,
+ IN UINTN DevicePosition
+ );
+
+/**
+ Sends out ATAPI Test Unit Ready Packet Command to the specified device
+ to find out whether device is accessible.
+
+ @param[in] AtapiBlkIoDev A pointer to atapi block IO device.
+ @param[in] DevicePosition An integer to signify device position.
+
+ @retval EFI_SUCCESS TestUnit command executed successfully.
+ @retval EFI_DEVICE_ERROR Device cannot be executed TestUnit command successfully.
+
+**/
+EFI_STATUS
+TestUnitReady (
+ IN ATAPI_BLK_IO_DEV *AtapiBlkIoDev,
+ IN UINTN DevicePosition
+ ) ;
+
+/**
+ Send out ATAPI commands conforms to the Packet Command with PIO Data In Protocol.
+
+ @param[in] AtapiBlkIoDev A pointer to atapi block IO device.
+ @param[in] DevicePosition An integer to signify device position.
+ @param[in] Packet A pointer to ATAPI command packet.
+ @param[in] Buffer Buffer to contain requested transfer data from device.
+ @param[in] ByteCount Requested transfer data length.
+ @param[in] TimeoutInMilliSeconds Time out value, in unit of milliseconds.
+
+ @retval EFI_SUCCESS Command executed successfully.
+ @retval EFI_DEVICE_ERROR Device cannot be executed command successfully.
+
+**/
+EFI_STATUS
+AtapiPacketCommandIn (
+ IN ATAPI_BLK_IO_DEV *AtapiBlkIoDev,
+ IN UINTN DevicePosition,
+ IN ATAPI_PACKET_COMMAND *Packet,
+ IN UINT16 *Buffer,
+ IN UINT32 ByteCount,
+ IN UINTN TimeoutInMilliSeconds
+ );
+
+/**
+ Sends out ATAPI Inquiry Packet Command to the specified device.
+ This command will return INQUIRY data of the device.
+
+ @param[in] AtapiBlkIoDev A pointer to atapi block IO device.
+ @param[in] DevicePosition An integer to signify device position.
+ @param[out] MediaInfo The media information of the specified block media.
+
+ @retval EFI_SUCCESS Command executed successfully.
+ @retval EFI_DEVICE_ERROR Device cannot be executed command successfully.
+ @retval EFI_UNSUPPORTED Unsupported device type.
+
+**/
+EFI_STATUS
+Inquiry (
+ IN ATAPI_BLK_IO_DEV *AtapiBlkIoDev,
+ IN UINTN DevicePosition,
+ OUT EFI_PEI_BLOCK_IO_MEDIA *MediaInfo
+ );
+
+/**
+ Used before read/write blocks from/to ATAPI device media.
+ Since ATAPI device media is removable, it is necessary to detect
+ whether media is present and get current present media's information.
+
+ @param[in] AtapiBlkIoDev A pointer to atapi block IO device.
+ @param[in] DevicePosition An integer to signify device position.
+ @param[in, out] MediaInfo The media information of the specified block media.
+
+ @retval EFI_SUCCESS Command executed successfully.
+ @retval EFI_DEVICE_ERROR Some device errors happen.
+ @retval EFI_OUT_OF_RESOURCES Can not allocate required resources.
+
+**/
+EFI_STATUS
+DetectMedia (
+ IN ATAPI_BLK_IO_DEV *AtapiBlkIoDev,
+ IN UINTN DevicePosition,
+ IN OUT EFI_PEI_BLOCK_IO_MEDIA *MediaInfo
+ );
+
+/**
+ Reset specified Atapi device.
+
+ @param[in] AtapiBlkIoDev A pointer to atapi block IO device.
+ @param[in] DevicePosition An integer to signify device position.
+ @param[in] Extensive If TRUE, use ATA soft reset, otherwise use Atapi soft reset.
+
+ @retval EFI_SUCCESS Command executed successfully.
+ @retval EFI_DEVICE_ERROR Some device errors happen.
+
+**/
+EFI_STATUS
+ResetDevice (
+ IN ATAPI_BLK_IO_DEV *AtapiBlkIoDev,
+ IN UINTN DevicePosition,
+ IN BOOLEAN Extensive
+ );
+
+/**
+ Sends out ATAPI Request Sense Packet Command to the specified device.
+
+ @param[in] AtapiBlkIoDev A pointer to atapi block IO device.
+ @param[in] DevicePosition An integer to signify device position.
+ @param[in] SenseBuffers Pointer to sense buffer.
+ @param[in, out] SenseCounts Length of sense buffer.
+
+ @retval EFI_SUCCESS Command executed successfully.
+ @retval EFI_DEVICE_ERROR Some device errors happen.
+
+**/
+EFI_STATUS
+RequestSense (
+ IN ATAPI_BLK_IO_DEV *AtapiBlkIoDev,
+ IN UINTN DevicePosition,
+ IN ATAPI_REQUEST_SENSE_DATA *SenseBuffers,
+ IN OUT UINT8 *SenseCounts
+ );
+
+/**
+ Sends out ATAPI Read Capacity Packet Command to the specified device.
+ This command will return the information regarding the capacity of the
+ media in the device.
+
+ @param[in] AtapiBlkIoDev A pointer to atapi block IO device.
+ @param[in] DevicePosition An integer to signify device position.
+ @param[in, out] MediaInfo The media information of the specified block media.
+
+ @retval EFI_SUCCESS Command executed successfully.
+ @retval EFI_DEVICE_ERROR Some device errors happen.
+
+**/
+EFI_STATUS
+ReadCapacity (
+ IN ATAPI_BLK_IO_DEV *AtapiBlkIoDev,
+ IN UINTN DevicePosition,
+ IN OUT EFI_PEI_BLOCK_IO_MEDIA *MediaInfo
+ );
+
+/**
+ Perform read from disk in block unit.
+
+ @param[in] AtapiBlkIoDev A pointer to atapi block IO device.
+ @param[in] DevicePosition An integer to signify device position.
+ @param[in] Buffer Buffer to contain read data.
+ @param[in] StartLba Starting LBA address.
+ @param[in] NumberOfBlocks Number of blocks to read.
+ @param[in] BlockSize Size of each block.
+
+ @retval EFI_SUCCESS Command executed successfully.
+ @retval EFI_DEVICE_ERROR Some device errors happen.
+
+**/
+EFI_STATUS
+ReadSectors (
+ IN ATAPI_BLK_IO_DEV *AtapiBlkIoDev,
+ IN UINTN DevicePosition,
+ IN VOID *Buffer,
+ IN EFI_PEI_LBA StartLba,
+ IN UINTN NumberOfBlocks,
+ IN UINTN BlockSize
+ );
+
+/**
+ Check if there is media according to sense data.
+
+ @param[in] SenseData Pointer to sense data.
+ @param[in] SenseCounts Count of sense data.
+
+ @retval TRUE No media
+ @retval FALSE Media exists
+
+**/
+BOOLEAN
+IsNoMedia (
+ IN ATAPI_REQUEST_SENSE_DATA *SenseData,
+ IN UINTN SenseCounts
+ );
+
+/**
+ Check if device state is unclear according to sense data.
+
+ @param[in] SenseData Pointer to sense data.
+ @param[in] SenseCounts Count of sense data.
+
+ @retval TRUE Device state is unclear
+ @retval FALSE Device state is clear
+
+**/
+BOOLEAN
+IsDeviceStateUnclear (
+ IN ATAPI_REQUEST_SENSE_DATA *SenseData,
+ IN UINTN SenseCounts
+ );
+
+/**
+ Check if there is media error according to sense data.
+
+ @param[in] SenseData Pointer to sense data.
+ @param[in] SenseCounts Count of sense data.
+
+ @retval TRUE Media error
+ @retval FALSE No media error
+
+**/
+BOOLEAN
+IsMediaError (
+ IN ATAPI_REQUEST_SENSE_DATA *SenseData,
+ IN UINTN SenseCounts
+ );
+
+/**
+ Check if drive is ready according to sense data.
+
+ @param[in] SenseData Pointer to sense data.
+ @param[in] SenseCounts Count of sense data.
+ @param[out] NeedRetry Indicate if retry is needed.
+
+ @retval TRUE Drive ready
+ @retval FALSE Drive not ready
+
+**/
+BOOLEAN
+IsDriveReady (
+ IN ATAPI_REQUEST_SENSE_DATA *SenseData,
+ IN UINTN SenseCounts,
+ OUT BOOLEAN *NeedRetry
+ );
+
+#endif
diff --git a/MdeModulePkg/Bus/Pci/IdeBusPei/IdeBusPei.inf b/MdeModulePkg/Bus/Pci/IdeBusPei/IdeBusPei.inf
new file mode 100644
index 000000000..f28124c6b
--- /dev/null
+++ b/MdeModulePkg/Bus/Pci/IdeBusPei/IdeBusPei.inf
@@ -0,0 +1,66 @@
+## @file
+# PEIM to produce gEfiPeiVirtualBlockIoPpiGuid PPI for ATA controllers in the platform.
+# This PPI canl be consumed by PEIM which produce gEfiPeiDeviceRecoveryModulePpiGuid
+# for Atapi CD ROM device.
+#
+# This module discovers CDROM devices in Legacy and native mode and installs block IO ppis for them.
+# Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>
+#
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions
+# of the BSD License which accompanies this distribution. The
+# full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = IdeBusPei
+ FILE_GUID = B7A5041A-78BA-49e3-B73B-54C757811FB6
+ MODULE_TYPE = PEIM
+ VERSION_STRING = 1.0
+
+ ENTRY_POINT = AtapiPeimEntry
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+
+[Sources]
+ AtapiPeim.h
+ AtapiPeim.c
+
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+
+[LibraryClasses]
+ IoLib
+ BaseMemoryLib
+ PeiServicesLib
+ PeimEntryPoint
+ DebugLib
+ TimerLib
+ PeiServicesTablePointerLib
+ MemoryAllocationLib
+
+[Ppis]
+ gPeiAtaControllerPpiGuid # PPI ALWAYS_CONSUMED
+ gEfiPeiVirtualBlockIoPpiGuid # PPI ALWAYS_PRODUCED
+
+[Pcd]
+ gEfiMdeModulePkgTokenSpaceGuid.PcdSataSpinUpDelayInSecForRecoveryPath
+
+[Depex]
+ gEfiPeiMemoryDiscoveredPpiGuid AND gEfiPeiBootInRecoveryModePpiGuid AND gPeiAtaControllerPpiGuid
+
+
+