/** @file | |
Copyright (c) 2014 - 2021, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#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 | |
}, | |
{ // BlkIo2Ppi | |
EFI_PEI_RECOVERY_BLOCK_IO2_PPI_REVISION, | |
UfsBlockIoPeimGetDeviceNo2, | |
UfsBlockIoPeimGetMediaInfo2, | |
UfsBlockIoPeimReadBlocks2 | |
}, | |
{ // BlkIoPpiList | |
EFI_PEI_PPI_DESCRIPTOR_PPI, | |
&gEfiPeiVirtualBlockIoPpiGuid, | |
NULL | |
}, | |
{ // BlkIo2PpiList | |
EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST, | |
&gEfiPeiVirtualBlockIo2PpiGuid, | |
NULL | |
}, | |
{ // Media | |
{ | |
MSG_UFS_DP, | |
FALSE, | |
TRUE, | |
FALSE, | |
0x1000, | |
0 | |
}, | |
{ | |
MSG_UFS_DP, | |
FALSE, | |
TRUE, | |
FALSE, | |
0x1000, | |
0 | |
}, | |
{ | |
MSG_UFS_DP, | |
FALSE, | |
TRUE, | |
FALSE, | |
0x1000, | |
0 | |
}, | |
{ | |
MSG_UFS_DP, | |
FALSE, | |
TRUE, | |
FALSE, | |
0x1000, | |
0 | |
}, | |
{ | |
MSG_UFS_DP, | |
FALSE, | |
TRUE, | |
FALSE, | |
0x1000, | |
0 | |
}, | |
{ | |
MSG_UFS_DP, | |
FALSE, | |
TRUE, | |
FALSE, | |
0x1000, | |
0 | |
}, | |
{ | |
MSG_UFS_DP, | |
FALSE, | |
TRUE, | |
FALSE, | |
0x1000, | |
0 | |
}, | |
{ | |
MSG_UFS_DP, | |
FALSE, | |
TRUE, | |
FALSE, | |
0x1000, | |
0 | |
} | |
}, | |
{ // EndOfPeiNotifyList | |
(EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), | |
&gEfiEndOfPeiSignalPpiGuid, | |
UfsEndOfPei | |
}, | |
0, // UfsHcBase | |
0, // Capabilities | |
0, // TaskTag | |
0, // UtpTrlBase | |
0, // Nutrs | |
NULL, // TrlMapping | |
0, // UtpTmrlBase | |
0, // Nutmrs | |
NULL, // TmrlMapping | |
{ // 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 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 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_IO2_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 ((DEBUG_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 ((DEBUG_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 ((DEBUG_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 ((DEBUG_VERBOSE, "UfsBlockIoPei: Media Error\n")); | |
return EFI_DEVICE_ERROR; | |
} | |
if (SenseData->Sense_Key == EFI_SCSI_SK_HARDWARE_ERROR) { | |
*NeedRetry = FALSE; | |
DEBUG ((DEBUG_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 ((DEBUG_VERBOSE, "UfsBlockIoPei: Was Reset Before\n")); | |
return EFI_SUCCESS; | |
} | |
*NeedRetry = FALSE; | |
DEBUG ((DEBUG_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; | |
UINTN Lun; | |
Private = GET_UFS_PEIM_HC_PRIVATE_DATA_FROM_THIS (This); | |
NeedRetry = TRUE; | |
if ((DeviceIndex == 0) || (DeviceIndex > UFS_PEIM_MAX_LUNS)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
Lun = DeviceIndex - 1; | |
if ((Private->Luns.BitMask & (BIT0 << Lun)) == 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, | |
Lun, | |
&SenseData, | |
&SenseDataLength | |
); | |
if (!EFI_ERROR (Status)) { | |
break; | |
} | |
if (SenseDataLength == 0) { | |
continue; | |
} | |
Status = UfsPeimParsingSenseKeys (&(Private->Media[Lun]), &SenseData, &NeedRetry); | |
if (EFI_ERROR (Status)) { | |
return EFI_DEVICE_ERROR; | |
} | |
} while (NeedRetry); | |
DataLength = sizeof (EFI_SCSI_DISK_CAPACITY_DATA); | |
SenseDataLength = 0; | |
Status = UfsPeimReadCapacity (Private, Lun, &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, Lun, &Capacity16, (UINT32 *)&DataLength, NULL, &SenseDataLength); | |
if (EFI_ERROR (Status)) { | |
return EFI_DEVICE_ERROR; | |
} | |
Private->Media[Lun].LastBlock = ((UINT32)Capacity16.LastLba3 << 24) | (Capacity16.LastLba2 << 16) | (Capacity16.LastLba1 << 8) | Capacity16.LastLba0; | |
Private->Media[Lun].LastBlock |= LShiftU64 ((UINT64)Capacity16.LastLba7, 56) | LShiftU64 ((UINT64)Capacity16.LastLba6, 48) | LShiftU64 ((UINT64)Capacity16.LastLba5, 40) | LShiftU64 ((UINT64)Capacity16.LastLba4, 32); | |
Private->Media[Lun].BlockSize = (Capacity16.BlockSize3 << 24) | (Capacity16.BlockSize2 << 16) | (Capacity16.BlockSize1 << 8) | Capacity16.BlockSize0; | |
} else { | |
Private->Media[Lun].LastBlock = ((UINT32)Capacity.LastLba3 << 24) | (Capacity.LastLba2 << 16) | (Capacity.LastLba1 << 8) | Capacity.LastLba0; | |
Private->Media[Lun].BlockSize = (Capacity.BlockSize3 << 24) | (Capacity.BlockSize2 << 16) | (Capacity.BlockSize1 << 8) | Capacity.BlockSize0; | |
} | |
MediaInfo->DeviceType = UfsDevice; | |
MediaInfo->MediaPresent = Private->Media[Lun].MediaPresent; | |
MediaInfo->LastBlock = (UINTN)Private->Media[Lun].LastBlock; | |
MediaInfo->BlockSize = Private->Media[Lun].BlockSize; | |
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; | |
UINTN Lun; | |
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 == 0) || (DeviceIndex > UFS_PEIM_MAX_LUNS)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
Lun = DeviceIndex - 1; | |
if ((Private->Luns.BitMask & (BIT0 << Lun)) == 0) { | |
return EFI_ACCESS_DENIED; | |
} | |
BlockSize = Private->Media[Lun].BlockSize; | |
if (BufferSize % BlockSize != 0) { | |
Status = EFI_BAD_BUFFER_SIZE; | |
} | |
if (StartLBA > Private->Media[Lun].LastBlock) { | |
Status = EFI_INVALID_PARAMETER; | |
} | |
NumberOfBlocks = BufferSize / BlockSize; | |
do { | |
Status = UfsPeimTestUnitReady ( | |
Private, | |
Lun, | |
&SenseData, | |
&SenseDataLength | |
); | |
if (!EFI_ERROR (Status)) { | |
break; | |
} | |
if (SenseDataLength == 0) { | |
continue; | |
} | |
Status = UfsPeimParsingSenseKeys (&(Private->Media[Lun]), &SenseData, &NeedRetry); | |
if (EFI_ERROR (Status)) { | |
return EFI_DEVICE_ERROR; | |
} | |
} while (NeedRetry); | |
SenseDataLength = 0; | |
if (Private->Media[Lun].LastBlock < 0xfffffffful) { | |
Status = UfsPeimRead10 ( | |
Private, | |
Lun, | |
(UINT32)StartLBA, | |
(UINT32)NumberOfBlocks, | |
Buffer, | |
(UINT32 *)&BufferSize, | |
NULL, | |
&SenseDataLength | |
); | |
} else { | |
Status = UfsPeimRead16 ( | |
Private, | |
Lun, | |
(UINT32)StartLBA, | |
(UINT32)NumberOfBlocks, | |
Buffer, | |
(UINT32 *)&BufferSize, | |
NULL, | |
&SenseDataLength | |
); | |
} | |
return Status; | |
} | |
/** | |
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_IO2_PPI | |
instance. | |
@param[out] NumberBlockDevices The number of block I/O devices discovered. | |
@retval EFI_SUCCESS The operation performed successfully. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
UfsBlockIoPeimGetDeviceNo2 ( | |
IN EFI_PEI_SERVICES **PeiServices, | |
IN EFI_PEI_RECOVERY_BLOCK_IO2_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_IO2_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 | |
UfsBlockIoPeimGetMediaInfo2 ( | |
IN EFI_PEI_SERVICES **PeiServices, | |
IN EFI_PEI_RECOVERY_BLOCK_IO2_PPI *This, | |
IN UINTN DeviceIndex, | |
OUT EFI_PEI_BLOCK_IO2_MEDIA *MediaInfo | |
) | |
{ | |
EFI_STATUS Status; | |
UFS_PEIM_HC_PRIVATE_DATA *Private; | |
EFI_PEI_BLOCK_IO_MEDIA Media; | |
UINTN Lun; | |
Private = GET_UFS_PEIM_HC_PRIVATE_DATA_FROM_THIS2 (This); | |
Status = UfsBlockIoPeimGetMediaInfo ( | |
PeiServices, | |
&Private->BlkIoPpi, | |
DeviceIndex, | |
&Media | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Lun = DeviceIndex - 1; | |
CopyMem (MediaInfo, &(Private->Media[Lun]), sizeof (EFI_PEI_BLOCK_IO2_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_IO2_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 | |
UfsBlockIoPeimReadBlocks2 ( | |
IN EFI_PEI_SERVICES **PeiServices, | |
IN EFI_PEI_RECOVERY_BLOCK_IO2_PPI *This, | |
IN UINTN DeviceIndex, | |
IN EFI_PEI_LBA StartLBA, | |
IN UINTN BufferSize, | |
OUT VOID *Buffer | |
) | |
{ | |
EFI_STATUS Status; | |
UFS_PEIM_HC_PRIVATE_DATA *Private; | |
Status = EFI_SUCCESS; | |
Private = GET_UFS_PEIM_HC_PRIVATE_DATA_FROM_THIS2 (This); | |
Status = UfsBlockIoPeimReadBlocks ( | |
PeiServices, | |
&Private->BlkIoPpi, | |
DeviceIndex, | |
StartLBA, | |
BufferSize, | |
Buffer | |
); | |
return Status; | |
} | |
/** | |
One notified function to cleanup the allocated DMA buffers at the end of PEI. | |
@param[in] PeiServices Pointer to PEI Services Table. | |
@param[in] NotifyDescriptor Pointer to the descriptor for the Notification | |
event that caused this function to execute. | |
@param[in] Ppi Pointer to the PPI data associated with this function. | |
@retval EFI_SUCCESS The function completes successfully | |
**/ | |
EFI_STATUS | |
EFIAPI | |
UfsEndOfPei ( | |
IN EFI_PEI_SERVICES **PeiServices, | |
IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor, | |
IN VOID *Ppi | |
) | |
{ | |
UFS_PEIM_HC_PRIVATE_DATA *Private; | |
Private = GET_UFS_PEIM_HC_PRIVATE_DATA_FROM_THIS_NOTIFY (NotifyDescriptor); | |
if ((Private->Pool != NULL) && (Private->Pool->Head != NULL)) { | |
UfsPeimFreeMemPool (Private->Pool); | |
} | |
if (Private->UtpTmrlBase != NULL) { | |
IoMmuFreeBuffer ( | |
EFI_SIZE_TO_PAGES (Private->Nutmrs * sizeof (UTP_TMRD)), | |
Private->UtpTmrlBase, | |
Private->TmrlMapping | |
); | |
} | |
if (Private->UtpTrlBase != NULL) { | |
IoMmuFreeBuffer ( | |
EFI_SIZE_TO_PAGES (Private->Nutrs * sizeof (UTP_TRD)), | |
Private->UtpTrlBase, | |
Private->TrlMapping | |
); | |
} | |
UfsControllerStop (Private); | |
return EFI_SUCCESS; | |
} | |
/** | |
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; | |
UINTN MmioBase; | |
UINT8 Controller; | |
UFS_UNIT_DESC UnitDescriptor; | |
// | |
// 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; | |
} | |
IoMmuInit (); | |
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->BlkIo2PpiList.Ppi = &Private->BlkIo2Ppi; | |
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 ((DEBUG_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 ((DEBUG_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 ((DEBUG_ERROR, "Ufs Set fDeviceInit Flag Error, Status = %r\n", Status)); | |
Controller++; | |
continue; | |
} | |
// | |
// Check if 8 common luns are active and set corresponding bit mask. | |
// | |
for (Index = 0; Index < UFS_PEIM_MAX_LUNS; Index++) { | |
Status = UfsRwDeviceDesc (Private, TRUE, UfsUnitDesc, (UINT8)Index, 0, &UnitDescriptor, sizeof (UFS_UNIT_DESC)); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "Fail to read UFS Unit Descriptor, Index = %X, Status = %r\n", Index, Status)); | |
continue; | |
} | |
if (UnitDescriptor.LunEn == 0x1) { | |
DEBUG ((DEBUG_INFO, "Ufs %d Lun %d is enabled\n", Controller, Index)); | |
Private->Luns.BitMask |= (BIT0 << Index); | |
} | |
} | |
PeiServicesInstallPpi (&Private->BlkIoPpiList); | |
PeiServicesNotifyPpi (&Private->EndOfPeiNotifyList); | |
Controller++; | |
} | |
return EFI_SUCCESS; | |
} |