MdeModulePkg: Add UFS (Universal Flash Storage) Stack

It includes 4 drivers:
1. UfsPassThruDxe, which is a UEFI driver and consumes EFI_UFS_HOST_CONTROLLER_PROTOCOL and produces EFI_EXT_SCSI_PASS_THRU_PROTOCOL
2. UfsPciHcDxe, which is specific for pci-based UFS HC implementation and is a UEFI driver to produce EFI_UFS_HOST_CONTROLLER_PROTOCOL.
3. UfsBlockIoPei, which is a PEI driver and consumes EFI_UFS_HOST_CONTROLLER_PPI and produces EFI_PEI_VIRTUAL_BLOCK_IO_PPI.
4. UfsPciHcPei, which is specific for pci-based UFS HC implementation and is a PEI driver to produce EFI_UFS_HOST_CONTROLLER_PPI.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Feng Tian <feng.tian@intel.com>
Reviewed-by: Star Zeng <star.zeng@intel.com>

git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@17246 6f19259b-4bc3-4df7-8a09-765794883524
This commit is contained in:
Feng Tian
2015-04-29 02:42:58 +00:00
committed by erictian
parent 5230616612
commit 0591696eff
33 changed files with 12521 additions and 0 deletions

View File

@@ -0,0 +1,995 @@
/** @file
Copyright (c) 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 "UfsBlockIoPei.h"
//
// Template for UFS HC Peim Private Data.
//
UFS_PEIM_HC_PRIVATE_DATA gUfsHcPeimTemplate = {
UFS_PEIM_HC_SIG, // Signature
NULL, // Controller
NULL, // Pool
{ // BlkIoPpi
UfsBlockIoPeimGetDeviceNo,
UfsBlockIoPeimGetMediaInfo,
UfsBlockIoPeimReadBlocks
},
{ // BlkIoPpiList
EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST,
&gEfiPeiVirtualBlockIoPpiGuid,
NULL
},
{ // Media
{
UfsDevice,
TRUE,
0,
0x1000
},
{
UfsDevice,
TRUE,
0,
0x1000
},
{
UfsDevice,
TRUE,
0,
0x1000
},
{
UfsDevice,
TRUE,
0,
0x1000
},
{
UfsDevice,
TRUE,
0,
0x1000
},
{
UfsDevice,
TRUE,
0,
0x1000
},
{
UfsDevice,
TRUE,
0,
0x1000
},
{
UfsDevice,
TRUE,
0,
0x1000
}
},
0, // UfsHcBase
0, // Capabilities
0, // TaskTag
0, // UtpTrlBase
0, // Nutrs
0, // UtpTmrlBase
0, // Nutmrs
{ // Luns
{
UFS_LUN_0, // Ufs Common Lun 0
UFS_LUN_1, // Ufs Common Lun 1
UFS_LUN_2, // Ufs Common Lun 2
UFS_LUN_3, // Ufs Common Lun 3
UFS_LUN_4, // Ufs Common Lun 4
UFS_LUN_5, // Ufs Common Lun 5
UFS_LUN_6, // Ufs Common Lun 6
UFS_LUN_7, // Ufs Common Lun 7
},
0x0000, // By default exposing all Luns.
0x0
}
};
/**
Execute Request Sense SCSI command on a specific UFS device.
@param[in] Private A pointer to UFS_PEIM_HC_PRIVATE_DATA data structure.
@param[in] Lun The lun on which the SCSI cmd executed.
@param[out] DataBuffer A pointer to output sense data.
@param[out] DataBufferLength The length of output sense data.
@retval EFI_SUCCESS The command executed successfully.
@retval EFI_DEVICE_ERROR A device error occurred while attempting to send SCSI Request Packet.
@retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute.
**/
EFI_STATUS
UfsPeimRequestSense (
IN UFS_PEIM_HC_PRIVATE_DATA *Private,
IN UINTN Lun,
OUT VOID *DataBuffer,
OUT UINT32 *DataBufferLength
)
{
UFS_SCSI_REQUEST_PACKET Packet;
UINT8 Cdb[UFS_SCSI_OP_LENGTH_SIX];
EFI_STATUS Status;
ZeroMem (&Packet, sizeof (UFS_SCSI_REQUEST_PACKET));
ZeroMem (Cdb, sizeof (Cdb));
Cdb[0] = EFI_SCSI_OP_REQUEST_SENSE;
Packet.Timeout = UFS_TIMEOUT;
Packet.Cdb = Cdb;
Packet.CdbLength = sizeof (Cdb);
Packet.DataDirection = UfsDataIn;
Packet.InDataBuffer = DataBuffer;
Packet.InTransferLength = *DataBufferLength;
Packet.SenseData = NULL;
Packet.SenseDataLength = 0;
Status = UfsExecScsiCmds (Private,(UINT8)Lun, &Packet);
if (!EFI_ERROR (Status)) {
*DataBufferLength = Packet.InTransferLength;
}
return Status;
}
/**
Execute TEST UNITY READY SCSI command on a specific UFS device.
@param[in] Private A pointer to UFS_PEIM_HC_PRIVATE_DATA data structure.
@param[in] Lun The lun on which the SCSI cmd executed.
@param[out] SenseData A pointer to output sense data.
@param[out] SenseDataLength The length of output sense data.
@retval EFI_SUCCESS The command executed successfully.
@retval EFI_DEVICE_ERROR A device error occurred while attempting to send SCSI Request Packet.
@retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute.
**/
EFI_STATUS
UfsPeimTestUnitReady (
IN UFS_PEIM_HC_PRIVATE_DATA *Private,
IN UINTN Lun,
OUT VOID *SenseData, OPTIONAL
OUT UINT8 *SenseDataLength
)
{
UFS_SCSI_REQUEST_PACKET Packet;
UINT8 Cdb[UFS_SCSI_OP_LENGTH_SIX];
EFI_STATUS Status;
ZeroMem (&Packet, sizeof (UFS_SCSI_REQUEST_PACKET));
ZeroMem (Cdb, sizeof (Cdb));
Cdb[0] = EFI_SCSI_OP_TEST_UNIT_READY;
Packet.Timeout = UFS_TIMEOUT;
Packet.Cdb = Cdb;
Packet.CdbLength = sizeof (Cdb);
Packet.DataDirection = UfsNoData;
Packet.SenseData = SenseData;
Packet.SenseDataLength = *SenseDataLength;
Status = UfsExecScsiCmds (Private,(UINT8)Lun, &Packet);
if (*SenseDataLength != 0) {
*SenseDataLength = Packet.SenseDataLength;
}
return Status;
}
/**
Execute INQUIRY SCSI command on a specific UFS device.
@param[in] Private A pointer to UFS_PEIM_HC_PRIVATE_DATA data structure.
@param[in] Lun The lun on which the SCSI cmd executed.
@param[out] Inquiry A pointer to Inquiry data buffer.
@param[out] InquiryLengths The length of output Inquiry data.
@param[out] SenseData A pointer to output sense data.
@param[out] SenseDataLength The length of output sense data.
@retval EFI_SUCCESS The command executed successfully.
@retval EFI_DEVICE_ERROR A device error occurred while attempting to send SCSI Request Packet.
@retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute.
**/
EFI_STATUS
UfsPeimInquiry (
IN UFS_PEIM_HC_PRIVATE_DATA *Private,
IN UINTN Lun,
OUT VOID *Inquiry,
OUT UINT32 *InquiryLength,
OUT VOID *SenseData, OPTIONAL
OUT UINT8 *SenseDataLength
)
{
UFS_SCSI_REQUEST_PACKET Packet;
UINT8 Cdb[UFS_SCSI_OP_LENGTH_SIX];
EFI_STATUS Status;
ZeroMem (&Packet, sizeof (UFS_SCSI_REQUEST_PACKET));
ZeroMem (Cdb, sizeof (Cdb));
Cdb[0] = EFI_SCSI_OP_INQUIRY;
Cdb[4] = sizeof (EFI_SCSI_INQUIRY_DATA);
Packet.Timeout = UFS_TIMEOUT;
Packet.Cdb = Cdb;
Packet.CdbLength = sizeof (Cdb);
Packet.InDataBuffer = Inquiry;
Packet.InTransferLength = *InquiryLength;
Packet.DataDirection = UfsDataIn;
Packet.SenseData = SenseData;
Packet.SenseDataLength = *SenseDataLength;
Status = UfsExecScsiCmds (Private, (UINT8)Lun, &Packet);
if (*SenseDataLength != 0) {
*SenseDataLength = Packet.SenseDataLength;
}
if (!EFI_ERROR (Status)) {
*InquiryLength = Packet.InTransferLength;
}
return Status;
}
/**
Execute READ CAPACITY(10) SCSI command on a specific UFS device.
@param[in] Private A pointer to UFS_PEIM_HC_PRIVATE_DATA data structure.
@param[in] Lun The lun on which the SCSI cmd executed.
@param[out] DataBuffer A pointer to READ_CAPACITY data buffer.
@param[out] DataLength The length of output READ_CAPACITY data.
@param[out] SenseData A pointer to output sense data.
@param[out] SenseDataLength The length of output sense data.
@retval EFI_SUCCESS The command executed successfully.
@retval EFI_DEVICE_ERROR A device error occurred while attempting to send SCSI Request Packet.
@retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute.
**/
EFI_STATUS
UfsPeimReadCapacity (
IN UFS_PEIM_HC_PRIVATE_DATA *Private,
IN UINTN Lun,
OUT VOID *DataBuffer,
OUT UINT32 *DataLength,
OUT VOID *SenseData, OPTIONAL
OUT UINT8 *SenseDataLength
)
{
UFS_SCSI_REQUEST_PACKET Packet;
UINT8 Cdb[UFS_SCSI_OP_LENGTH_TEN];
EFI_STATUS Status;
ZeroMem (&Packet, sizeof (UFS_SCSI_REQUEST_PACKET));
ZeroMem (Cdb, sizeof (Cdb));
Cdb[0] = EFI_SCSI_OP_READ_CAPACITY;
Packet.Timeout = UFS_TIMEOUT;
Packet.Cdb = Cdb;
Packet.CdbLength = sizeof (Cdb);
Packet.InDataBuffer = DataBuffer;
Packet.InTransferLength = *DataLength;
Packet.DataDirection = UfsDataIn;
Packet.SenseData = SenseData;
Packet.SenseDataLength = *SenseDataLength;
Status = UfsExecScsiCmds (Private, (UINT8)Lun, &Packet);
if (*SenseDataLength != 0) {
*SenseDataLength = Packet.SenseDataLength;
}
if (!EFI_ERROR (Status)) {
*DataLength = Packet.InTransferLength;
}
return Status;
}
/**
Execute READ CAPACITY(16) SCSI command on a specific UFS device.
@param[in] Private A pointer to UFS_PEIM_HC_PRIVATE_DATA data structure.
@param[in] Lun The lun on which the SCSI cmd executed.
@param[out] DataBuffer A pointer to READ_CAPACITY data buffer.
@param[out] DataLength The length of output READ_CAPACITY data.
@param[out] SenseData A pointer to output sense data.
@param[out] SenseDataLength The length of output sense data.
@retval EFI_SUCCESS The command executed successfully.
@retval EFI_DEVICE_ERROR A device error occurred while attempting to send SCSI Request Packet.
@retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute.
**/
EFI_STATUS
UfsPeimReadCapacity16 (
IN UFS_PEIM_HC_PRIVATE_DATA *Private,
IN UINTN Lun,
OUT VOID *DataBuffer,
OUT UINT32 *DataLength,
OUT VOID *SenseData, OPTIONAL
OUT UINT8 *SenseDataLength
)
{
UFS_SCSI_REQUEST_PACKET Packet;
UINT8 Cdb[UFS_SCSI_OP_LENGTH_SIXTEEN];
EFI_STATUS Status;
ZeroMem (&Packet, sizeof (UFS_SCSI_REQUEST_PACKET));
ZeroMem (Cdb, sizeof (Cdb));
Cdb[0] = EFI_SCSI_OP_READ_CAPACITY16;
Cdb[1] = 0x10; // Service Action should be 0x10 for UFS device.
Cdb[13] = 0x20; // The maximum number of bytes for returned data.
Packet.Timeout = UFS_TIMEOUT;
Packet.Cdb = Cdb;
Packet.CdbLength = sizeof (Cdb);
Packet.InDataBuffer = DataBuffer;
Packet.InTransferLength = *DataLength;
Packet.DataDirection = UfsDataIn;
Packet.SenseData = SenseData;
Packet.SenseDataLength = *SenseDataLength;
Status = UfsExecScsiCmds (Private, (UINT8)Lun, &Packet);
if (*SenseDataLength != 0) {
*SenseDataLength = Packet.SenseDataLength;
}
if (!EFI_ERROR (Status)) {
*DataLength = Packet.InTransferLength;
}
return Status;
}
/**
Execute READ (10) SCSI command on a specific UFS device.
@param[in] Private A pointer to UFS_PEIM_HC_PRIVATE_DATA data structure.
@param[in] Lun The lun on which the SCSI cmd executed.
@param[in] StartLba The start LBA.
@param[in] SectorNum The sector number to be read.
@param[out] DataBuffer A pointer to data buffer.
@param[out] DataLength The length of output data.
@param[out] SenseData A pointer to output sense data.
@param[out] SenseDataLength The length of output sense data.
@retval EFI_SUCCESS The command executed successfully.
@retval EFI_DEVICE_ERROR A device error occurred while attempting to send SCSI Request Packet.
@retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute.
**/
EFI_STATUS
UfsPeimRead10 (
IN UFS_PEIM_HC_PRIVATE_DATA *Private,
IN UINTN Lun,
IN UINTN StartLba,
IN UINT32 SectorNum,
OUT VOID *DataBuffer,
OUT UINT32 *DataLength,
OUT VOID *SenseData, OPTIONAL
OUT UINT8 *SenseDataLength
)
{
UFS_SCSI_REQUEST_PACKET Packet;
UINT8 Cdb[UFS_SCSI_OP_LENGTH_TEN];
EFI_STATUS Status;
ZeroMem (&Packet, sizeof (UFS_SCSI_REQUEST_PACKET));
ZeroMem (Cdb, sizeof (Cdb));
Cdb[0] = EFI_SCSI_OP_READ10;
WriteUnaligned32 ((UINT32 *)&Cdb[2], SwapBytes32 ((UINT32) StartLba));
WriteUnaligned16 ((UINT16 *)&Cdb[7], SwapBytes16 ((UINT16) SectorNum));
Packet.Timeout = UFS_TIMEOUT;
Packet.Cdb = Cdb;
Packet.CdbLength = sizeof (Cdb);
Packet.InDataBuffer = DataBuffer;
Packet.InTransferLength = *DataLength;
Packet.DataDirection = UfsDataIn;
Packet.SenseData = SenseData;
Packet.SenseDataLength = *SenseDataLength;
Status = UfsExecScsiCmds (Private, (UINT8)Lun, &Packet);
if (*SenseDataLength != 0) {
*SenseDataLength = Packet.SenseDataLength;
}
if (!EFI_ERROR (Status)) {
*DataLength = Packet.InTransferLength;
}
return Status;
}
/**
Execute READ (16) SCSI command on a specific UFS device.
@param[in] Private A pointer to UFS_PEIM_HC_PRIVATE_DATA data structure.
@param[in] Lun The lun on which the SCSI cmd executed.
@param[in] StartLba The start LBA.
@param[in] SectorNum The sector number to be read.
@param[out] DataBuffer A pointer to data buffer.
@param[out] DataLength The length of output data.
@param[out] SenseData A pointer to output sense data.
@param[out] SenseDataLength The length of output sense data.
@retval EFI_SUCCESS The command executed successfully.
@retval EFI_DEVICE_ERROR A device error occurred while attempting to send SCSI Request Packet.
@retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute.
**/
EFI_STATUS
UfsPeimRead16 (
IN UFS_PEIM_HC_PRIVATE_DATA *Private,
IN UINTN Lun,
IN UINTN StartLba,
IN UINT32 SectorNum,
OUT VOID *DataBuffer,
OUT UINT32 *DataLength,
OUT VOID *SenseData, OPTIONAL
OUT UINT8 *SenseDataLength
)
{
UFS_SCSI_REQUEST_PACKET Packet;
UINT8 Cdb[UFS_SCSI_OP_LENGTH_SIXTEEN];
EFI_STATUS Status;
ZeroMem (&Packet, sizeof (UFS_SCSI_REQUEST_PACKET));
ZeroMem (Cdb, sizeof (Cdb));
Cdb[0] = EFI_SCSI_OP_READ16;
WriteUnaligned64 ((UINT64 *)&Cdb[2], SwapBytes64 (StartLba));
WriteUnaligned32 ((UINT32 *)&Cdb[10], SwapBytes32 (SectorNum));
Packet.Timeout = UFS_TIMEOUT;
Packet.Cdb = Cdb;
Packet.CdbLength = sizeof (Cdb);
Packet.InDataBuffer = DataBuffer;
Packet.InTransferLength = *DataLength;
Packet.DataDirection = UfsDataIn;
Packet.SenseData = SenseData;
Packet.SenseDataLength = *SenseDataLength;
Status = UfsExecScsiCmds (Private, (UINT8)Lun, &Packet);
if (*SenseDataLength != 0) {
*SenseDataLength = Packet.SenseDataLength;
}
if (!EFI_ERROR (Status)) {
*DataLength = Packet.InTransferLength;
}
return Status;
}
/**
Parsing Sense Keys from sense data.
@param Media The pointer of EFI_PEI_BLOCK_IO_MEDIA
@param SenseData The pointer of EFI_SCSI_SENSE_DATA
@param NeedRetry The pointer of action which indicates what is need to retry
@retval EFI_DEVICE_ERROR Indicates that error occurs
@retval EFI_SUCCESS Successfully to complete the parsing
**/
EFI_STATUS
UfsPeimParsingSenseKeys (
IN EFI_PEI_BLOCK_IO_MEDIA *Media,
IN EFI_SCSI_SENSE_DATA *SenseData,
OUT BOOLEAN *NeedRetry
)
{
if ((SenseData->Sense_Key == EFI_SCSI_SK_NOT_READY) &&
(SenseData->Addnl_Sense_Code == EFI_SCSI_ASC_NO_MEDIA)) {
Media->MediaPresent = FALSE;
*NeedRetry = FALSE;
DEBUG ((EFI_D_VERBOSE, "UfsBlockIoPei: Is No Media\n"));
return EFI_DEVICE_ERROR;
}
if ((SenseData->Sense_Key == EFI_SCSI_SK_UNIT_ATTENTION) &&
(SenseData->Addnl_Sense_Code == EFI_SCSI_ASC_MEDIA_CHANGE)) {
*NeedRetry = TRUE;
DEBUG ((EFI_D_VERBOSE, "UfsBlockIoPei: Is Media Change\n"));
return EFI_SUCCESS;
}
if ((SenseData->Sense_Key == EFI_SCSI_SK_UNIT_ATTENTION) &&
(SenseData->Addnl_Sense_Code == EFI_SCSI_ASC_RESET)) {
*NeedRetry = TRUE;
DEBUG ((EFI_D_VERBOSE, "UfsBlockIoPei: Was Reset Before\n"));
return EFI_SUCCESS;
}
if ((SenseData->Sense_Key == EFI_SCSI_SK_MEDIUM_ERROR) ||
((SenseData->Sense_Key == EFI_SCSI_SK_NOT_READY) &&
(SenseData->Addnl_Sense_Code == EFI_SCSI_ASC_MEDIA_UPSIDE_DOWN))) {
*NeedRetry = FALSE;
DEBUG ((EFI_D_VERBOSE, "UfsBlockIoPei: Media Error\n"));
return EFI_DEVICE_ERROR;
}
if (SenseData->Sense_Key == EFI_SCSI_SK_HARDWARE_ERROR) {
*NeedRetry = FALSE;
DEBUG ((EFI_D_VERBOSE, "UfsBlockIoPei: Hardware Error\n"));
return EFI_DEVICE_ERROR;
}
if ((SenseData->Sense_Key == EFI_SCSI_SK_NOT_READY) &&
(SenseData->Addnl_Sense_Code == EFI_SCSI_ASC_NOT_READY) &&
(SenseData->Addnl_Sense_Code_Qualifier == EFI_SCSI_ASCQ_IN_PROGRESS)) {
*NeedRetry = TRUE;
DEBUG ((EFI_D_VERBOSE, "UfsBlockIoPei: Was Reset Before\n"));
return EFI_SUCCESS;
}
*NeedRetry = FALSE;
DEBUG ((EFI_D_VERBOSE, "UfsBlockIoPei: Sense Key = 0x%x ASC = 0x%x!\n", SenseData->Sense_Key, SenseData->Addnl_Sense_Code));
return EFI_DEVICE_ERROR;
}
/**
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 The operation performed successfully.
**/
EFI_STATUS
EFIAPI
UfsBlockIoPeimGetDeviceNo (
IN EFI_PEI_SERVICES **PeiServices,
IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This,
OUT UINTN *NumberBlockDevices
)
{
//
// For Ufs device, it has up to 8 normal Luns plus some well-known Luns.
// At PEI phase, we will only expose normal Luns to user.
// For those disabled Lun, when user try to access it, the operation would fail.
//
*NumberBlockDevices = UFS_PEIM_MAX_LUNS;
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.
@par Note:
The MediaInfo structure describes an enumeration of possible block device
types. This enumeration exists because no device paths are actually passed
across interfaces that describe the type or class of hardware that is publishing
the block I/O interface. This enumeration will allow for policy decisions
in the Recovery PEIM, such as "Try to recover from legacy floppy first,
LS-120 second, CD-ROM third." If there are multiple partitions abstracted
by a given device type, they should be reported in ascending order; this
order also applies to nested partitions, such as legacy MBR, where the
outermost partitions would have precedence in the reporting order. The
same logic applies to systems such as IDE that have precedence relationships
like "Master/Slave" or "Primary/Secondary". The master device should be
reported first, the slave second.
@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.
**/
EFI_STATUS
EFIAPI
UfsBlockIoPeimGetMediaInfo (
IN EFI_PEI_SERVICES **PeiServices,
IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This,
IN UINTN DeviceIndex,
OUT EFI_PEI_BLOCK_IO_MEDIA *MediaInfo
)
{
EFI_STATUS Status;
UFS_PEIM_HC_PRIVATE_DATA *Private;
EFI_SCSI_SENSE_DATA SenseData;
UINT8 SenseDataLength;
EFI_SCSI_DISK_CAPACITY_DATA Capacity;
EFI_SCSI_DISK_CAPACITY_DATA16 Capacity16;
UINTN DataLength;
BOOLEAN NeedRetry;
Private = GET_UFS_PEIM_HC_PRIVATE_DATA_FROM_THIS (This);
NeedRetry = TRUE;
if (DeviceIndex >= UFS_PEIM_MAX_LUNS) {
return EFI_INVALID_PARAMETER;
}
if ((Private->Luns.BitMask & (BIT0 << DeviceIndex)) == 0) {
return EFI_ACCESS_DENIED;
}
ZeroMem (&SenseData, sizeof (SenseData));
ZeroMem (&Capacity, sizeof (Capacity));
ZeroMem (&Capacity16, sizeof (Capacity16));
SenseDataLength = sizeof (SenseData);
//
// First test unit ready
//
do {
Status = UfsPeimTestUnitReady (
Private,
DeviceIndex,
&SenseData,
&SenseDataLength
);
if (!EFI_ERROR (Status)) {
break;
}
if (SenseDataLength == 0) {
continue;
}
Status = UfsPeimParsingSenseKeys (&(Private->Media[DeviceIndex]), &SenseData, &NeedRetry);
if (EFI_ERROR (Status)) {
return EFI_DEVICE_ERROR;
}
} while (NeedRetry);
DataLength = sizeof (EFI_SCSI_DISK_CAPACITY_DATA);
SenseDataLength = 0;
Status = UfsPeimReadCapacity (Private, DeviceIndex, &Capacity, (UINT32 *)&DataLength, NULL, &SenseDataLength);
if (EFI_ERROR (Status)) {
return EFI_DEVICE_ERROR;
}
if ((Capacity.LastLba3 == 0xff) && (Capacity.LastLba2 == 0xff) &&
(Capacity.LastLba1 == 0xff) && (Capacity.LastLba0 == 0xff)) {
DataLength = sizeof (EFI_SCSI_DISK_CAPACITY_DATA16);
SenseDataLength = 0;
Status = UfsPeimReadCapacity16 (Private, DeviceIndex, &Capacity16, (UINT32 *)&DataLength, NULL, &SenseDataLength);
if (EFI_ERROR (Status)) {
return EFI_DEVICE_ERROR;
}
MediaInfo->LastBlock = (Capacity16.LastLba3 << 24) | (Capacity16.LastLba2 << 16) | (Capacity16.LastLba1 << 8) | Capacity16.LastLba0;
MediaInfo->LastBlock |= ((UINT64)Capacity16.LastLba7 << 56) | ((UINT64)Capacity16.LastLba6 << 48) | ((UINT64)Capacity16.LastLba5 << 40) | ((UINT64)Capacity16.LastLba4 << 32);
MediaInfo->BlockSize = (Capacity16.BlockSize3 << 24) | (Capacity16.BlockSize2 << 16) | (Capacity16.BlockSize1 << 8) | Capacity16.BlockSize0;
} else {
MediaInfo->LastBlock = (Capacity.LastLba3 << 24) | (Capacity.LastLba2 << 16) | (Capacity.LastLba1 << 8) | Capacity.LastLba0;
MediaInfo->BlockSize = (Capacity.BlockSize3 << 24) | (Capacity.BlockSize2 << 16) | (Capacity.BlockSize1 << 8) | Capacity.BlockSize0;
}
MediaInfo->DeviceType = UfsDevice;
MediaInfo->MediaPresent = TRUE;
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, 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
UfsBlockIoPeimReadBlocks (
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_STATUS Status;
UINTN BlockSize;
UINTN NumberOfBlocks;
UFS_PEIM_HC_PRIVATE_DATA *Private;
EFI_SCSI_SENSE_DATA SenseData;
UINT8 SenseDataLength;
BOOLEAN NeedRetry;
Status = EFI_SUCCESS;
NeedRetry = TRUE;
Private = GET_UFS_PEIM_HC_PRIVATE_DATA_FROM_THIS (This);
ZeroMem (&SenseData, sizeof (SenseData));
SenseDataLength = sizeof (SenseData);
//
// Check parameters
//
if (Buffer == NULL) {
return EFI_INVALID_PARAMETER;
}
if (BufferSize == 0) {
return EFI_SUCCESS;
}
if (DeviceIndex >= UFS_PEIM_MAX_LUNS) {
return EFI_INVALID_PARAMETER;
}
if ((Private->Luns.BitMask & (BIT0 << DeviceIndex)) == 0) {
return EFI_ACCESS_DENIED;
}
BlockSize = Private->Media[DeviceIndex].BlockSize;
if (BufferSize % BlockSize != 0) {
Status = EFI_BAD_BUFFER_SIZE;
}
if (StartLBA > Private->Media[DeviceIndex].LastBlock) {
Status = EFI_INVALID_PARAMETER;
}
NumberOfBlocks = BufferSize / BlockSize;
do {
Status = UfsPeimTestUnitReady (
Private,
DeviceIndex,
&SenseData,
&SenseDataLength
);
if (!EFI_ERROR (Status)) {
break;
}
if (SenseDataLength == 0) {
continue;
}
Status = UfsPeimParsingSenseKeys (&(Private->Media[DeviceIndex]), &SenseData, &NeedRetry);
if (EFI_ERROR (Status)) {
return EFI_DEVICE_ERROR;
}
} while (NeedRetry);
SenseDataLength = 0;
if (Private->Media[DeviceIndex].LastBlock != ~((UINTN)0)) {
Status = UfsPeimRead10 (
Private,
DeviceIndex,
(UINT32)StartLBA,
(UINT32)NumberOfBlocks,
Buffer,
(UINT32 *)&BufferSize,
NULL,
&SenseDataLength
);
} else {
Status = UfsPeimRead16 (
Private,
DeviceIndex,
(UINT32)StartLBA,
(UINT32)NumberOfBlocks,
Buffer,
(UINT32 *)&BufferSize,
NULL,
&SenseDataLength
);
}
return Status;
}
/**
The user code starts with this function.
@param FileHandle Handle of the file being invoked.
@param PeiServices Describes the list of possible PEI Services.
@retval EFI_SUCCESS The driver is successfully initialized.
@retval Others Can't initialize the driver.
**/
EFI_STATUS
EFIAPI
InitializeUfsBlockIoPeim (
IN EFI_PEI_FILE_HANDLE FileHandle,
IN CONST EFI_PEI_SERVICES **PeiServices
)
{
EFI_STATUS Status;
UFS_PEIM_HC_PRIVATE_DATA *Private;
EDKII_UFS_HOST_CONTROLLER_PPI *UfsHcPpi;
UINT32 Index;
UFS_CONFIG_DESC Config;
UINTN MmioBase;
UINT8 Controller;
//
// Shadow this PEIM to run from memory
//
if (!EFI_ERROR (PeiServicesRegisterForShadow (FileHandle))) {
return EFI_SUCCESS;
}
//
// locate ufs host controller PPI
//
Status = PeiServicesLocatePpi (
&gEdkiiPeiUfsHostControllerPpiGuid,
0,
NULL,
(VOID **) &UfsHcPpi
);
if (EFI_ERROR (Status)) {
return EFI_DEVICE_ERROR;
}
Controller = 0;
MmioBase = 0;
while (TRUE) {
Status = UfsHcPpi->GetUfsHcMmioBar (UfsHcPpi, Controller, &MmioBase);
//
// When status is error, meant no controller is found
//
if (EFI_ERROR (Status)) {
break;
}
Private = AllocateCopyPool (sizeof (UFS_PEIM_HC_PRIVATE_DATA), &gUfsHcPeimTemplate);
if (Private == NULL) {
Status = EFI_OUT_OF_RESOURCES;
break;
}
Private->BlkIoPpiList.Ppi = &Private->BlkIoPpi;
Private->UfsHcBase = MmioBase;
//
// Initialize the memory pool which will be used in all transactions.
//
Status = UfsPeimInitMemPool (Private);
if (EFI_ERROR (Status)) {
Status = EFI_OUT_OF_RESOURCES;
break;
}
//
// Initialize UFS Host Controller H/W.
//
Status = UfsControllerInit (Private);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "UfsDevicePei: Host Controller Initialization Error, Status = %r\n", Status));
Controller++;
continue;
}
//
// UFS 2.0 spec Section 13.1.3.3:
// At the end of the UFS Interconnect Layer initialization on both host and device side,
// the host shall send a NOP OUT UPIU to verify that the device UTP Layer is ready.
//
Status = UfsExecNopCmds (Private);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Ufs Sending NOP IN command Error, Status = %r\n", Status));
Controller++;
continue;
}
//
// The host enables the device initialization completion by setting fDeviceInit flag.
//
Status = UfsSetFlag (Private, UfsFlagDevInit);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Ufs Set fDeviceInit Flag Error, Status = %r\n", Status));
Controller++;
continue;
}
//
// Get Ufs Device's Lun Info by reading Configuration Descriptor.
//
Status = UfsRwDeviceDesc (Private, TRUE, UfsConfigDesc, 0, 0, &Config, sizeof (UFS_CONFIG_DESC));
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Ufs Get Configuration Descriptor Error, Status = %r\n", Status));
Controller++;
continue;
}
for (Index = 0; Index < UFS_PEIM_MAX_LUNS; Index++) {
if (Config.UnitDescConfParams[Index].LunEn != 0) {
Private->Luns.BitMask |= (BIT0 << Index);
DEBUG ((EFI_D_INFO, "Ufs %d Lun %d is enabled\n", Controller, Index));
}
}
Status = PeiServicesInstallPpi (&Private->BlkIoPpiList);
Controller++;
}
return EFI_SUCCESS;
}

View File

@@ -0,0 +1,434 @@
/** @file
Copyright (c) 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.
**/
#ifndef _UFS_BLOCK_IO_PEI_H_
#define _UFS_BLOCK_IO_PEI_H_
#include <PiPei.h>
#include <Ppi/UfsHostController.h>
#include <Ppi/BlockIo.h>
#include <Library/DebugLib.h>
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/IoLib.h>
#include <Library/TimerLib.h>
#include <Library/PeiServicesLib.h>
#include <IndustryStandard/Scsi.h>
#include "UfsHci.h"
#include "UfsHcMem.h"
#define UFS_PEIM_HC_SIG SIGNATURE_32 ('U', 'F', 'S', 'H')
#define UFS_PEIM_MAX_LUNS 8
typedef struct {
UINT8 Lun[UFS_PEIM_MAX_LUNS];
UINT16 BitMask:12; // Bit 0~7 is for common luns. Bit 8~11 is reserved for those well known luns
UINT16 Rsvd:4;
} UFS_PEIM_EXPOSED_LUNS;
typedef struct {
///
/// The timeout, in 100 ns units, to use for the execution of this SCSI
/// Request Packet. A Timeout value of 0 means that this function
/// will wait indefinitely for the SCSI Request Packet to execute. If
/// Timeout is greater than zero, then this function will return
/// EFI_TIMEOUT if the time required to execute the SCSI
/// Request Packet is greater than Timeout.
///
UINT64 Timeout;
///
/// A pointer to the data buffer to transfer between the SCSI
/// controller and the SCSI device for read and bidirectional commands.
///
VOID *InDataBuffer;
///
/// A pointer to the data buffer to transfer between the SCSI
/// controller and the SCSI device for write or bidirectional commands.
///
VOID *OutDataBuffer;
///
/// A pointer to the sense data that was generated by the execution of
/// the SCSI Request Packet.
///
VOID *SenseData;
///
/// A pointer to buffer that contains the Command Data Block to
/// send to the SCSI device specified by Target and Lun.
///
VOID *Cdb;
///
/// On Input, the size, in bytes, of InDataBuffer. On output, the
/// number of bytes transferred between the SCSI controller and the SCSI device.
///
UINT32 InTransferLength;
///
/// On Input, the size, in bytes of OutDataBuffer. On Output, the
/// Number of bytes transferred between SCSI Controller and the SCSI device.
///
UINT32 OutTransferLength;
///
/// The length, in bytes, of the buffer Cdb. The standard values are 6,
/// 10, 12, and 16, but other values are possible if a variable length CDB is used.
///
UINT8 CdbLength;
///
/// The direction of the data transfer. 0 for reads, 1 for writes. A
/// value of 2 is Reserved for Bi-Directional SCSI commands.
///
UINT8 DataDirection;
///
/// On input, the length in bytes of the SenseData buffer. On
/// output, the number of bytes written to the SenseData buffer.
///
UINT8 SenseDataLength;
} UFS_SCSI_REQUEST_PACKET;
typedef struct _UFS_PEIM_HC_PRIVATE_DATA {
UINT32 Signature;
EFI_HANDLE Controller;
UFS_PEIM_MEM_POOL *Pool;
EFI_PEI_RECOVERY_BLOCK_IO_PPI BlkIoPpi;
EFI_PEI_PPI_DESCRIPTOR BlkIoPpiList;
EFI_PEI_BLOCK_IO_MEDIA Media[UFS_PEIM_MAX_LUNS];
UINTN UfsHcBase;
UINT32 Capabilities;
UINT8 TaskTag;
VOID *UtpTrlBase;
UINT8 Nutrs;
VOID *UtpTmrlBase;
UINT8 Nutmrs;
UFS_PEIM_EXPOSED_LUNS Luns;
} UFS_PEIM_HC_PRIVATE_DATA;
#define UFS_TIMEOUT MultU64x32((UINT64)(3), 10000000)
#define ROUNDUP8(x) (((x) % 8 == 0) ? (x) : ((x) / 8 + 1) * 8)
#define IS_ALIGNED(addr, size) (((UINTN) (addr) & (size - 1)) == 0)
#define GET_UFS_PEIM_HC_PRIVATE_DATA_FROM_THIS(a) CR (a, UFS_PEIM_HC_PRIVATE_DATA, BlkIoPpi, UFS_PEIM_HC_SIG)
#define UFS_SCSI_OP_LENGTH_SIX 0x6
#define UFS_SCSI_OP_LENGTH_TEN 0xa
#define UFS_SCSI_OP_LENGTH_SIXTEEN 0x10
typedef struct _UFS_DEVICE_MANAGEMENT_REQUEST_PACKET {
UINT64 Timeout;
VOID *InDataBuffer;
VOID *OutDataBuffer;
UINT8 Opcode;
UINT8 DescId;
UINT8 Index;
UINT8 Selector;
UINT32 InTransferLength;
UINT32 OutTransferLength;
UINT8 DataDirection;
UINT8 Ocs;
} UFS_DEVICE_MANAGEMENT_REQUEST_PACKET;
/**
Sends a UFS-supported SCSI Request Packet to a UFS device that is attached to the UFS host controller.
@param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure.
@param[in] Lun The LUN of the UFS device to send the SCSI Request Packet.
@param[in, out] Packet A pointer to the SCSI Request Packet to send to a specified Lun of the
UFS device.
@retval EFI_SUCCESS The SCSI Request Packet was sent by the host. For bi-directional
commands, InTransferLength bytes were transferred from
InDataBuffer. For write and bi-directional commands,
OutTransferLength bytes were transferred by
OutDataBuffer.
@retval EFI_DEVICE_ERROR A device error occurred while attempting to send the SCSI Request
Packet.
@retval EFI_OUT_OF_RESOURCES The resource for transfer is not available.
@retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute.
**/
EFI_STATUS
UfsExecScsiCmds (
IN UFS_PEIM_HC_PRIVATE_DATA *Private,
IN UINT8 Lun,
IN OUT UFS_SCSI_REQUEST_PACKET *Packet
);
/**
Initialize the UFS host controller.
@param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure.
@retval EFI_SUCCESS The Ufs Host Controller is initialized successfully.
@retval Others A device error occurred while initializing the controller.
**/
EFI_STATUS
UfsControllerInit (
IN UFS_PEIM_HC_PRIVATE_DATA *Private
);
/**
Stop the UFS host controller.
@param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure.
@retval EFI_SUCCESS The Ufs Host Controller is stopped successfully.
@retval Others A device error occurred while stopping the controller.
**/
EFI_STATUS
UfsControllerStop (
IN UFS_PEIM_HC_PRIVATE_DATA *Private
);
/**
Set specified flag to 1 on a UFS device.
@param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure.
@param[in] FlagId The ID of flag to be set.
@retval EFI_SUCCESS The flag was set successfully.
@retval EFI_DEVICE_ERROR A device error occurred while attempting to set the flag.
@retval EFI_TIMEOUT A timeout occurred while waiting for the completion of setting the flag.
**/
EFI_STATUS
UfsSetFlag (
IN UFS_PEIM_HC_PRIVATE_DATA *Private,
IN UINT8 FlagId
);
/**
Read or write specified device descriptor of a UFS device.
@param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure.
@param[in] Read The boolean variable to show r/w direction.
@param[in] DescId The ID of device descriptor.
@param[in] Index The Index of device descriptor.
@param[in] Selector The Selector of device descriptor.
@param[in, out] Descriptor The buffer of device descriptor to be read or written.
@param[in] DescSize The size of device descriptor buffer.
@retval EFI_SUCCESS The device descriptor was read/written successfully.
@retval EFI_DEVICE_ERROR A device error occurred while attempting to r/w the device descriptor.
@retval EFI_TIMEOUT A timeout occurred while waiting for the completion of r/w the device descriptor.
**/
EFI_STATUS
UfsRwDeviceDesc (
IN UFS_PEIM_HC_PRIVATE_DATA *Private,
IN BOOLEAN Read,
IN UINT8 DescId,
IN UINT8 Index,
IN UINT8 Selector,
IN OUT VOID *Descriptor,
IN UINT32 DescSize
);
/**
Sends NOP IN cmd to a UFS device for initialization process request.
For more details, please refer to UFS 2.0 spec Figure 13.3.
@param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure.
@retval EFI_SUCCESS The NOP IN command was sent by the host. The NOP OUT response was
received successfully.
@retval EFI_DEVICE_ERROR A device error occurred while attempting to execute NOP IN command.
@retval EFI_OUT_OF_RESOURCES The resource for transfer is not available.
@retval EFI_TIMEOUT A timeout occurred while waiting for the NOP IN command to execute.
**/
EFI_STATUS
UfsExecNopCmds (
IN UFS_PEIM_HC_PRIVATE_DATA *Private
);
/**
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 The operation performed successfully.
**/
EFI_STATUS
EFIAPI
UfsBlockIoPeimGetDeviceNo (
IN EFI_PEI_SERVICES **PeiServices,
IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This,
OUT UINTN *NumberBlockDevices
);
/**
Gets a block device's media information.
This function will provide the caller with the specified block device's media
information. If the media changes, calling this function will update the media
information accordingly.
@param[in] PeiServices General-purpose services that are available to every
PEIM
@param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance.
@param[in] DeviceIndex Specifies the block device to which the function wants
to talk. Because the driver that implements Block I/O
PPIs will manage multiple block devices, the PPIs that
want to talk to a single device must specify the
device index that was assigned during the enumeration
process. This index is a number from one to
NumberBlockDevices.
@param[out] MediaInfo The media information of the specified block media.
The caller is responsible for the ownership of this
data structure.
@par Note:
The MediaInfo structure describes an enumeration of possible block device
types. This enumeration exists because no device paths are actually passed
across interfaces that describe the type or class of hardware that is publishing
the block I/O interface. This enumeration will allow for policy decisions
in the Recovery PEIM, such as "Try to recover from legacy floppy first,
LS-120 second, CD-ROM third." If there are multiple partitions abstracted
by a given device type, they should be reported in ascending order; this
order also applies to nested partitions, such as legacy MBR, where the
outermost partitions would have precedence in the reporting order. The
same logic applies to systems such as IDE that have precedence relationships
like "Master/Slave" or "Primary/Secondary". The master device should be
reported first, the slave second.
@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.
**/
EFI_STATUS
EFIAPI
UfsBlockIoPeimGetMediaInfo (
IN EFI_PEI_SERVICES **PeiServices,
IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This,
IN UINTN DeviceIndex,
OUT EFI_PEI_BLOCK_IO_MEDIA *MediaInfo
);
/**
Reads the requested number of blocks from the specified block device.
The function reads the requested number of blocks from the device. All the
blocks are read, or an error is returned. If there is no media in the device,
the function returns EFI_NO_MEDIA.
@param[in] PeiServices General-purpose services that are available to
every PEIM.
@param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance.
@param[in] DeviceIndex Specifies the block device to which the function wants
to talk. Because the driver that implements Block I/O
PPIs will manage multiple block devices, 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
UfsBlockIoPeimReadBlocks (
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
);
/**
Initialize the memory management pool for the host controller.
@param Private The Ufs Peim driver private data.
@retval EFI_SUCCESS The memory pool is initialized.
@retval Others Fail to init the memory pool.
**/
EFI_STATUS
UfsPeimInitMemPool (
IN UFS_PEIM_HC_PRIVATE_DATA *Private
);
/**
Allocate some memory from the host controller's memory pool
which can be used to communicate with host controller.
@param Pool The host controller's memory pool.
@param Size Size of the memory to allocate.
@return The allocated memory or NULL.
**/
VOID *
UfsPeimAllocateMem (
IN UFS_PEIM_MEM_POOL *Pool,
IN UINTN Size
);
/**
Free the allocated memory back to the memory pool.
@param Pool The memory pool of the host controller.
@param Mem The memory to free.
@param Size The size of the memory to free.
**/
VOID
UfsPeimFreeMem (
IN UFS_PEIM_MEM_POOL *Pool,
IN VOID *Mem,
IN UINTN Size
);
#endif

View File

@@ -0,0 +1,61 @@
## @file
# Description file for the Universal Flash Storage (UFS) Peim driver.
#
# Copyright (c) 2014 - 2015, Intel Corporation. All rights reserved.<BR>
#
# This program and the accompanying materials
# are licensed and made available under the terms and conditions of the BSD License
# which accompanies this distribution. The full text of the license may be found at
# http://opensource.org/licenses/bsd-license.php
# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
#
#
##
[Defines]
INF_VERSION = 0x00010005
BASE_NAME = UfsBlockIoPei
MODULE_UNI_FILE = UfsBlockIoPei.uni
FILE_GUID = BE189D38-C963-41CF-B695-D90E9E545A13
MODULE_TYPE = PEIM
VERSION_STRING = 0.9
ENTRY_POINT = InitializeUfsBlockIoPeim
#
# The following information is for reference only and not required by the build tools.
#
# VALID_ARCHITECTURES = IA32 X64 IPF EBC
#
[Sources]
UfsBlockIoPei.c
UfsBlockIoPei.h
UfsHci.c
UfsHci.h
UfsHcMem.c
UfsHcMem.h
[Packages]
MdePkg/MdePkg.dec
MdeModulePkg/MdeModulePkg.dec
[LibraryClasses]
IoLib
TimerLib
BaseMemoryLib
PeimEntryPoint
PeiServicesLib
DebugLib
[Ppis]
gEfiPeiVirtualBlockIoPpiGuid ## PRODUCES
gEdkiiPeiUfsHostControllerPpiGuid ## CONSUMES
[Depex]
gEfiPeiMemoryDiscoveredPpiGuid AND gEdkiiPeiUfsHostControllerPpiGuid
[UserExtensions.TianoCore."ExtraFiles"]
UfsBlockIoPeiExtra.uni

Binary file not shown.

View File

@@ -0,0 +1,455 @@
/** @file
Copyright (c) 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 "UfsBlockIoPei.h"
/**
Allocate a block of memory to be used by the buffer pool.
@param Pages How many pages to allocate.
@return The allocated memory block or NULL if failed.
**/
UFS_PEIM_MEM_BLOCK *
UfsPeimAllocMemBlock (
IN UINTN Pages
)
{
UFS_PEIM_MEM_BLOCK *Block;
EFI_STATUS Status;
VOID *TempPtr;
EFI_PHYSICAL_ADDRESS Address;
TempPtr = NULL;
Block = NULL;
Status = PeiServicesAllocatePool (sizeof(UFS_PEIM_MEM_BLOCK), &TempPtr);
if (EFI_ERROR (Status)) {
return NULL;
}
ZeroMem ((VOID*)(UINTN)TempPtr, sizeof(UFS_PEIM_MEM_BLOCK));
//
// each bit in the bit array represents UFS_PEIM_MEM_UNIT
// bytes of memory in the memory block.
//
ASSERT (UFS_PEIM_MEM_UNIT * 8 <= EFI_PAGE_SIZE);
Block = (UFS_PEIM_MEM_BLOCK*)(UINTN)TempPtr;
Block->BufLen = EFI_PAGES_TO_SIZE (Pages);
Block->BitsLen = Block->BufLen / (UFS_PEIM_MEM_UNIT * 8);
Status = PeiServicesAllocatePool (Block->BitsLen, &TempPtr);
if (EFI_ERROR (Status)) {
return NULL;
}
ZeroMem ((VOID*)(UINTN)TempPtr, Block->BitsLen);
Block->Bits = (UINT8*)(UINTN)TempPtr;
Status = PeiServicesAllocatePages (
EfiBootServicesCode,
Pages,
&Address
);
if (EFI_ERROR (Status)) {
return NULL;
}
ZeroMem ((VOID*)(UINTN)Address, EFI_PAGES_TO_SIZE (Pages));
Block->Buf = (UINT8*)((UINTN)Address);
Block->Next = NULL;
return Block;
}
/**
Free the memory block from the memory pool.
@param Pool The memory pool to free the block from.
@param Block The memory block to free.
**/
VOID
UfsPeimFreeMemBlock (
IN UFS_PEIM_MEM_POOL *Pool,
IN UFS_PEIM_MEM_BLOCK *Block
)
{
ASSERT ((Pool != NULL) && (Block != NULL));
}
/**
Alloc some memory from the block.
@param Block The memory block to allocate memory from.
@param Units Number of memory units to allocate.
@return The pointer to the allocated memory. If couldn't allocate the needed memory,
the return value is NULL.
**/
VOID *
UfsPeimAllocMemFromBlock (
IN UFS_PEIM_MEM_BLOCK *Block,
IN UINTN Units
)
{
UINTN Byte;
UINT8 Bit;
UINTN StartByte;
UINT8 StartBit;
UINTN Available;
UINTN Count;
ASSERT ((Block != 0) && (Units != 0));
StartByte = 0;
StartBit = 0;
Available = 0;
for (Byte = 0, Bit = 0; Byte < Block->BitsLen;) {
//
// If current bit is zero, the corresponding memory unit is
// available, otherwise we need to restart our searching.
// Available counts the consective number of zero bit.
//
if (!UFS_PEIM_MEM_BIT_IS_SET (Block->Bits[Byte], Bit)) {
Available++;
if (Available >= Units) {
break;
}
UFS_PEIM_NEXT_BIT (Byte, Bit);
} else {
UFS_PEIM_NEXT_BIT (Byte, Bit);
Available = 0;
StartByte = Byte;
StartBit = Bit;
}
}
if (Available < Units) {
return NULL;
}
//
// Mark the memory as allocated
//
Byte = StartByte;
Bit = StartBit;
for (Count = 0; Count < Units; Count++) {
ASSERT (!UFS_PEIM_MEM_BIT_IS_SET (Block->Bits[Byte], Bit));
Block->Bits[Byte] = (UINT8) (Block->Bits[Byte] | (UINT8) UFS_PEIM_MEM_BIT (Bit));
UFS_PEIM_NEXT_BIT (Byte, Bit);
}
return Block->Buf + (StartByte * 8 + StartBit) * UFS_PEIM_MEM_UNIT;
}
/**
Insert the memory block to the pool's list of the blocks.
@param Head The head of the memory pool's block list.
@param Block The memory block to insert.
**/
VOID
UfsPeimInsertMemBlockToPool (
IN UFS_PEIM_MEM_BLOCK *Head,
IN UFS_PEIM_MEM_BLOCK *Block
)
{
ASSERT ((Head != NULL) && (Block != NULL));
Block->Next = Head->Next;
Head->Next = Block;
}
/**
Is the memory block empty?
@param Block The memory block to check.
@retval TRUE The memory block is empty.
@retval FALSE The memory block isn't empty.
**/
BOOLEAN
UfsPeimIsMemBlockEmpty (
IN UFS_PEIM_MEM_BLOCK *Block
)
{
UINTN Index;
for (Index = 0; Index < Block->BitsLen; Index++) {
if (Block->Bits[Index] != 0) {
return FALSE;
}
}
return TRUE;
}
/**
Unlink the memory block from the pool's list.
@param Head The block list head of the memory's pool.
@param BlockToUnlink The memory block to unlink.
**/
VOID
UfsPeimUnlinkMemBlock (
IN UFS_PEIM_MEM_BLOCK *Head,
IN UFS_PEIM_MEM_BLOCK *BlockToUnlink
)
{
UFS_PEIM_MEM_BLOCK *Block;
ASSERT ((Head != NULL) && (BlockToUnlink != NULL));
for (Block = Head; Block != NULL; Block = Block->Next) {
if (Block->Next == BlockToUnlink) {
Block->Next = BlockToUnlink->Next;
BlockToUnlink->Next = NULL;
break;
}
}
}
/**
Initialize the memory management pool for the host controller.
@param Private The Ufs Peim driver private data.
@retval EFI_SUCCESS The memory pool is initialized.
@retval Others Fail to init the memory pool.
**/
EFI_STATUS
UfsPeimInitMemPool (
IN UFS_PEIM_HC_PRIVATE_DATA *Private
)
{
UFS_PEIM_MEM_POOL *Pool;
EFI_STATUS Status;
VOID *TempPtr;
TempPtr = NULL;
Pool = NULL;
Status = PeiServicesAllocatePool (sizeof (UFS_PEIM_MEM_POOL), &TempPtr);
if (EFI_ERROR (Status)) {
return EFI_OUT_OF_RESOURCES;
}
ZeroMem ((VOID*)(UINTN)TempPtr, sizeof (UFS_PEIM_MEM_POOL));
Pool = (UFS_PEIM_MEM_POOL *)((UINTN)TempPtr);
Pool->Head = UfsPeimAllocMemBlock (UFS_PEIM_MEM_DEFAULT_PAGES);
if (Pool->Head == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Private->Pool = Pool;
return EFI_SUCCESS;
}
/**
Release the memory management pool.
@param Pool The memory pool to free.
@retval EFI_DEVICE_ERROR Fail to free the memory pool.
@retval EFI_SUCCESS The memory pool is freed.
**/
EFI_STATUS
UfsPeimFreeMemPool (
IN UFS_PEIM_MEM_POOL *Pool
)
{
UFS_PEIM_MEM_BLOCK *Block;
ASSERT (Pool->Head != NULL);
//
// Unlink all the memory blocks from the pool, then free them.
// UfsPeimUnlinkMemBlock can't be used to unlink and free the
// first block.
//
for (Block = Pool->Head->Next; Block != NULL; Block = Pool->Head->Next) {
UfsPeimFreeMemBlock (Pool, Block);
}
UfsPeimFreeMemBlock (Pool, Pool->Head);
return EFI_SUCCESS;
}
/**
Allocate some memory from the host controller's memory pool
which can be used to communicate with host controller.
@param Pool The host controller's memory pool.
@param Size Size of the memory to allocate.
@return The allocated memory or NULL.
**/
VOID *
UfsPeimAllocateMem (
IN UFS_PEIM_MEM_POOL *Pool,
IN UINTN Size
)
{
UFS_PEIM_MEM_BLOCK *Head;
UFS_PEIM_MEM_BLOCK *Block;
UFS_PEIM_MEM_BLOCK *NewBlock;
VOID *Mem;
UINTN AllocSize;
UINTN Pages;
Mem = NULL;
AllocSize = UFS_PEIM_MEM_ROUND (Size);
Head = Pool->Head;
ASSERT (Head != NULL);
//
// First check whether current memory blocks can satisfy the allocation.
//
for (Block = Head; Block != NULL; Block = Block->Next) {
Mem = UfsPeimAllocMemFromBlock (Block, AllocSize / UFS_PEIM_MEM_UNIT);
if (Mem != NULL) {
ZeroMem (Mem, Size);
break;
}
}
if (Mem != NULL) {
return Mem;
}
//
// Create a new memory block if there is not enough memory
// in the pool. If the allocation size is larger than the
// default page number, just allocate a large enough memory
// block. Otherwise allocate default pages.
//
if (AllocSize > EFI_PAGES_TO_SIZE (UFS_PEIM_MEM_DEFAULT_PAGES)) {
Pages = EFI_SIZE_TO_PAGES (AllocSize) + 1;
} else {
Pages = UFS_PEIM_MEM_DEFAULT_PAGES;
}
NewBlock = UfsPeimAllocMemBlock (Pages);
if (NewBlock == NULL) {
return NULL;
}
//
// Add the new memory block to the pool, then allocate memory from it
//
UfsPeimInsertMemBlockToPool (Head, NewBlock);
Mem = UfsPeimAllocMemFromBlock (NewBlock, AllocSize / UFS_PEIM_MEM_UNIT);
if (Mem != NULL) {
ZeroMem (Mem, Size);
}
return Mem;
}
/**
Free the allocated memory back to the memory pool.
@param Pool The memory pool of the host controller.
@param Mem The memory to free.
@param Size The size of the memory to free.
**/
VOID
UfsPeimFreeMem (
IN UFS_PEIM_MEM_POOL *Pool,
IN VOID *Mem,
IN UINTN Size
)
{
UFS_PEIM_MEM_BLOCK *Head;
UFS_PEIM_MEM_BLOCK *Block;
UINT8 *ToFree;
UINTN AllocSize;
UINTN Byte;
UINTN Bit;
UINTN Count;
Head = Pool->Head;
AllocSize = UFS_PEIM_MEM_ROUND (Size);
ToFree = (UINT8 *) Mem;
for (Block = Head; Block != NULL; Block = Block->Next) {
//
// scan the memory block list for the memory block that
// completely contains the memory to free.
//
if ((Block->Buf <= ToFree) && ((ToFree + AllocSize) <= (Block->Buf + Block->BufLen))) {
//
// compute the start byte and bit in the bit array
//
Byte = ((ToFree - Block->Buf) / UFS_PEIM_MEM_UNIT) / 8;
Bit = ((ToFree - Block->Buf) / UFS_PEIM_MEM_UNIT) % 8;
//
// reset associated bits in bit arry
//
for (Count = 0; Count < (AllocSize / UFS_PEIM_MEM_UNIT); Count++) {
ASSERT (UFS_PEIM_MEM_BIT_IS_SET (Block->Bits[Byte], Bit));
Block->Bits[Byte] = (UINT8) (Block->Bits[Byte] ^ UFS_PEIM_MEM_BIT (Bit));
UFS_PEIM_NEXT_BIT (Byte, Bit);
}
break;
}
}
//
// If Block == NULL, it means that the current memory isn't
// in the host controller's pool. This is critical because
// the caller has passed in a wrong memory point
//
ASSERT (Block != NULL);
//
// Release the current memory block if it is empty and not the head
//
if ((Block != Head) && UfsPeimIsMemBlockEmpty (Block)) {
UfsPeimFreeMemBlock (Pool, Block);
}
return ;
}

View File

@@ -0,0 +1,61 @@
/** @file
Copyright (c) 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.
**/
#ifndef _UFS_PEIM_MEM_H_
#define _UFS_PEIM_MEM_H_
#define UFS_PEIM_MEM_BIT(a) ((UINTN)(1 << (a)))
#define UFS_PEIM_MEM_BIT_IS_SET(Data, Bit) \
((BOOLEAN)(((Data) & UFS_PEIM_MEM_BIT(Bit)) == UFS_PEIM_MEM_BIT(Bit)))
typedef struct _UFS_PEIM_MEM_BLOCK UFS_PEIM_MEM_BLOCK;
struct _UFS_PEIM_MEM_BLOCK {
UINT8 *Bits; // Bit array to record which unit is allocated
UINTN BitsLen;
UINT8 *Buf;
UINTN BufLen; // Memory size in bytes
UFS_PEIM_MEM_BLOCK *Next;
};
typedef struct _UFS_PEIM_MEM_POOL {
UFS_PEIM_MEM_BLOCK *Head;
} UFS_PEIM_MEM_POOL;
//
// Memory allocation unit, note that the value must meet UFS spec alignment requirement.
//
#define UFS_PEIM_MEM_UNIT 128
#define UFS_PEIM_MEM_UNIT_MASK (UFS_PEIM_MEM_UNIT - 1)
#define UFS_PEIM_MEM_DEFAULT_PAGES 16
#define UFS_PEIM_MEM_ROUND(Len) (((Len) + UFS_PEIM_MEM_UNIT_MASK) & (~UFS_PEIM_MEM_UNIT_MASK))
//
// Advance the byte and bit to the next bit, adjust byte accordingly.
//
#define UFS_PEIM_NEXT_BIT(Byte, Bit) \
do { \
(Bit)++; \
if ((Bit) > 7) { \
(Byte)++; \
(Bit) = 0; \
} \
} while (0)
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff