mirror of https://github.com/acidanthera/audk.git
2277 lines
66 KiB
C
2277 lines
66 KiB
C
/** @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 - 2014, 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));
|
|
ZeroMem (&Idata, sizeof (ATAPI_INQUIRY_DATA));
|
|
|
|
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;
|
|
}
|