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