MdeModulePkg/Sd: add Erase Block support on sd/emmc device

It's done by producing EFI_ERASE_BLOCK_PROTOCOL protocol instance.

Cc: Hao Wu <hao.a.wu@intel.com>
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Feng Tian <feng.tian@intel.com>
Reviewed-by: Hao Wu <hao.a.wu@intel.com>
This commit is contained in:
Feng Tian
2016-05-03 14:07:38 +08:00
parent 140cc8000f
commit 275d51369a
10 changed files with 978 additions and 6 deletions

View File

@@ -1589,3 +1589,416 @@ EmmcSecurityProtocolOut (
return Status;
}
/**
Set the erase start address through sync or async I/O request.
@param[in] Partition A pointer to the EMMC_PARTITION instance.
@param[in] StartLba The starting logical block address to be erased.
@param[in] Token A pointer to the token associated with the transaction.
@param[in] IsEnd A boolean to show whether it's the last cmd in a series of cmds.
This parameter is only meaningful in async I/O request.
@retval EFI_SUCCESS The request is executed successfully.
@retval EFI_OUT_OF_RESOURCES The request could not be executed due to a lack of resources.
@retval Others The request could not be executed successfully.
**/
EFI_STATUS
EmmcEraseBlockStart (
IN EMMC_PARTITION *Partition,
IN EFI_LBA StartLba,
IN EFI_BLOCK_IO2_TOKEN *Token,
IN BOOLEAN IsEnd
)
{
EFI_STATUS Status;
EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru;
EMMC_DEVICE *Device;
EMMC_REQUEST *EraseBlockStart;
EFI_TPL OldTpl;
EraseBlockStart = NULL;
Device = Partition->Device;
PassThru = Device->Private->PassThru;
EraseBlockStart = AllocateZeroPool (sizeof (EMMC_REQUEST));
if (EraseBlockStart == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto Error;
}
EraseBlockStart->Signature = EMMC_REQUEST_SIGNATURE;
OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
InsertTailList (&Partition->Queue, &EraseBlockStart->Link);
gBS->RestoreTPL (OldTpl);
EraseBlockStart->Packet.SdMmcCmdBlk = &EraseBlockStart->SdMmcCmdBlk;
EraseBlockStart->Packet.SdMmcStatusBlk = &EraseBlockStart->SdMmcStatusBlk;
EraseBlockStart->Packet.Timeout = EMMC_GENERIC_TIMEOUT;
EraseBlockStart->SdMmcCmdBlk.CommandIndex = EMMC_ERASE_GROUP_START;
EraseBlockStart->SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc;
EraseBlockStart->SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1;
if (Device->SectorAddressing) {
EraseBlockStart->SdMmcCmdBlk.CommandArgument = (UINT32)StartLba;
} else {
EraseBlockStart->SdMmcCmdBlk.CommandArgument = (UINT32)MultU64x32 (StartLba, Partition->BlockMedia.BlockSize);
}
EraseBlockStart->IsEnd = IsEnd;
EraseBlockStart->Token = Token;
if ((Token != NULL) && (Token->Event != NULL)) {
Status = gBS->CreateEvent (
EVT_NOTIFY_SIGNAL,
TPL_CALLBACK,
AsyncIoCallback,
EraseBlockStart,
&EraseBlockStart->Event
);
if (EFI_ERROR (Status)) {
goto Error;
}
} else {
EraseBlockStart->Event = NULL;
}
Status = PassThru->PassThru (PassThru, Device->Slot, &EraseBlockStart->Packet, EraseBlockStart->Event);
Error:
if ((Token != NULL) && (Token->Event != NULL)) {
//
// For asynchronous operation, only free request and event in error case.
// The request and event will be freed in asynchronous callback for success case.
//
if (EFI_ERROR (Status) && (EraseBlockStart != NULL)) {
RemoveEntryList (&EraseBlockStart->Link);
if (EraseBlockStart->Event != NULL) {
gBS->CloseEvent (EraseBlockStart->Event);
}
FreePool (EraseBlockStart);
}
} else {
//
// For synchronous operation, free request whatever the execution result is.
//
if (EraseBlockStart != NULL) {
RemoveEntryList (&EraseBlockStart->Link);
FreePool (EraseBlockStart);
}
}
return Status;
}
/**
Set the erase end address through sync or async I/O request.
@param[in] Partition A pointer to the EMMC_PARTITION instance.
@param[in] EndLba The ending logical block address to be erased.
@param[in] Token A pointer to the token associated with the transaction.
@param[in] IsEnd A boolean to show whether it's the last cmd in a series of cmds.
This parameter is only meaningful in async I/O request.
@retval EFI_SUCCESS The request is executed successfully.
@retval EFI_OUT_OF_RESOURCES The request could not be executed due to a lack of resources.
@retval Others The request could not be executed successfully.
**/
EFI_STATUS
EmmcEraseBlockEnd (
IN EMMC_PARTITION *Partition,
IN EFI_LBA EndLba,
IN EFI_BLOCK_IO2_TOKEN *Token,
IN BOOLEAN IsEnd
)
{
EFI_STATUS Status;
EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru;
EMMC_DEVICE *Device;
EMMC_REQUEST *EraseBlockEnd;
EFI_TPL OldTpl;
EraseBlockEnd = NULL;
Device = Partition->Device;
PassThru = Device->Private->PassThru;
EraseBlockEnd = AllocateZeroPool (sizeof (EMMC_REQUEST));
if (EraseBlockEnd == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto Error;
}
EraseBlockEnd->Signature = EMMC_REQUEST_SIGNATURE;
OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
InsertTailList (&Partition->Queue, &EraseBlockEnd->Link);
gBS->RestoreTPL (OldTpl);
EraseBlockEnd->Packet.SdMmcCmdBlk = &EraseBlockEnd->SdMmcCmdBlk;
EraseBlockEnd->Packet.SdMmcStatusBlk = &EraseBlockEnd->SdMmcStatusBlk;
EraseBlockEnd->Packet.Timeout = EMMC_GENERIC_TIMEOUT;
EraseBlockEnd->SdMmcCmdBlk.CommandIndex = EMMC_ERASE_GROUP_END;
EraseBlockEnd->SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc;
EraseBlockEnd->SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1;
if (Device->SectorAddressing) {
EraseBlockEnd->SdMmcCmdBlk.CommandArgument = (UINT32)EndLba;
} else {
EraseBlockEnd->SdMmcCmdBlk.CommandArgument = (UINT32)MultU64x32 (EndLba, Partition->BlockMedia.BlockSize);
}
EraseBlockEnd->IsEnd = IsEnd;
EraseBlockEnd->Token = Token;
if ((Token != NULL) && (Token->Event != NULL)) {
Status = gBS->CreateEvent (
EVT_NOTIFY_SIGNAL,
TPL_CALLBACK,
AsyncIoCallback,
EraseBlockEnd,
&EraseBlockEnd->Event
);
if (EFI_ERROR (Status)) {
goto Error;
}
} else {
EraseBlockEnd->Event = NULL;
}
Status = PassThru->PassThru (PassThru, Device->Slot, &EraseBlockEnd->Packet, EraseBlockEnd->Event);
Error:
if ((Token != NULL) && (Token->Event != NULL)) {
//
// For asynchronous operation, only free request and event in error case.
// The request and event will be freed in asynchronous callback for success case.
//
if (EFI_ERROR (Status) && (EraseBlockEnd != NULL)) {
RemoveEntryList (&EraseBlockEnd->Link);
if (EraseBlockEnd->Event != NULL) {
gBS->CloseEvent (EraseBlockEnd->Event);
}
FreePool (EraseBlockEnd);
}
} else {
//
// For synchronous operation, free request whatever the execution result is.
//
if (EraseBlockEnd != NULL) {
RemoveEntryList (&EraseBlockEnd->Link);
FreePool (EraseBlockEnd);
}
}
return Status;
}
/**
Erase specified blocks through sync or async I/O request.
@param[in] Partition A pointer to the EMMC_PARTITION instance.
@param[in] Token A pointer to the token associated with the transaction.
@param[in] IsEnd A boolean to show whether it's the last cmd in a series of cmds.
This parameter is only meaningful in async I/O request.
@retval EFI_SUCCESS The request is executed successfully.
@retval EFI_OUT_OF_RESOURCES The request could not be executed due to a lack of resources.
@retval Others The request could not be executed successfully.
**/
EFI_STATUS
EmmcEraseBlock (
IN EMMC_PARTITION *Partition,
IN EFI_BLOCK_IO2_TOKEN *Token,
IN BOOLEAN IsEnd
)
{
EFI_STATUS Status;
EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru;
EMMC_DEVICE *Device;
EMMC_REQUEST *EraseBlock;
EFI_TPL OldTpl;
EraseBlock = NULL;
Device = Partition->Device;
PassThru = Device->Private->PassThru;
EraseBlock = AllocateZeroPool (sizeof (EMMC_REQUEST));
if (EraseBlock == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto Error;
}
EraseBlock->Signature = EMMC_REQUEST_SIGNATURE;
OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
InsertTailList (&Partition->Queue, &EraseBlock->Link);
gBS->RestoreTPL (OldTpl);
EraseBlock->Packet.SdMmcCmdBlk = &EraseBlock->SdMmcCmdBlk;
EraseBlock->Packet.SdMmcStatusBlk = &EraseBlock->SdMmcStatusBlk;
EraseBlock->Packet.Timeout = EMMC_GENERIC_TIMEOUT;
EraseBlock->SdMmcCmdBlk.CommandIndex = EMMC_ERASE;
EraseBlock->SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc;
EraseBlock->SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1b;
EraseBlock->IsEnd = IsEnd;
EraseBlock->Token = Token;
if ((Token != NULL) && (Token->Event != NULL)) {
Status = gBS->CreateEvent (
EVT_NOTIFY_SIGNAL,
TPL_CALLBACK,
AsyncIoCallback,
EraseBlock,
&EraseBlock->Event
);
if (EFI_ERROR (Status)) {
goto Error;
}
} else {
EraseBlock->Event = NULL;
}
Status = PassThru->PassThru (PassThru, Device->Slot, &EraseBlock->Packet, EraseBlock->Event);
Error:
if ((Token != NULL) && (Token->Event != NULL)) {
//
// For asynchronous operation, only free request and event in error case.
// The request and event will be freed in asynchronous callback for success case.
//
if (EFI_ERROR (Status) && (EraseBlock != NULL)) {
RemoveEntryList (&EraseBlock->Link);
if (EraseBlock->Event != NULL) {
gBS->CloseEvent (EraseBlock->Event);
}
FreePool (EraseBlock);
}
} else {
//
// For synchronous operation, free request whatever the execution result is.
//
if (EraseBlock != NULL) {
RemoveEntryList (&EraseBlock->Link);
FreePool (EraseBlock);
}
}
return Status;
}
/**
Erase a specified number of device blocks.
@param[in] This Indicates a pointer to the calling context.
@param[in] MediaId The media ID that the erase request is for.
@param[in] Lba The starting logical block address to be
erased. The caller is responsible for erasing
only legitimate locations.
@param[in, out] Token A pointer to the token associated with the
transaction.
@param[in] Size The size in bytes to be erased. This must be
a multiple of the physical block size of the
device.
@retval EFI_SUCCESS The erase request was queued if Event is not
NULL. The data was erased correctly to the
device if the Event is NULL.to the device.
@retval EFI_WRITE_PROTECTED The device cannot be erased due to write
protection.
@retval EFI_DEVICE_ERROR The device reported an error while attempting
to perform the erase operation.
@retval EFI_INVALID_PARAMETER The erase request contains LBAs that are not
valid.
@retval EFI_NO_MEDIA There is no media in the device.
@retval EFI_MEDIA_CHANGED The MediaId is not for the current media.
**/
EFI_STATUS
EFIAPI
EmmcEraseBlocks (
IN EFI_ERASE_BLOCK_PROTOCOL *This,
IN UINT32 MediaId,
IN EFI_LBA Lba,
IN OUT EFI_ERASE_BLOCK_TOKEN *Token,
IN UINTN Size
)
{
EFI_STATUS Status;
EFI_BLOCK_IO_MEDIA *Media;
UINTN BlockSize;
UINTN BlockNum;
EFI_LBA LastLba;
UINT8 PartitionConfig;
EMMC_PARTITION *Partition;
EMMC_DEVICE *Device;
Status = EFI_SUCCESS;
Partition = EMMC_PARTITION_DATA_FROM_ERASEBLK (This);
Device = Partition->Device;
Media = &Partition->BlockMedia;
if (MediaId != Media->MediaId) {
return EFI_MEDIA_CHANGED;
}
if (Media->ReadOnly) {
return EFI_WRITE_PROTECTED;
}
//
// Check parameters.
//
BlockSize = Media->BlockSize;
if ((Size % BlockSize) != 0) {
return EFI_INVALID_PARAMETER;
}
BlockNum = Size / BlockSize;
if ((Lba + BlockNum - 1) > Media->LastBlock) {
return EFI_INVALID_PARAMETER;
}
if ((Token != NULL) && (Token->Event != NULL)) {
Token->TransactionStatus = EFI_SUCCESS;
}
LastLba = Lba + BlockNum - 1;
//
// Check if needs to switch partition access.
//
PartitionConfig = Device->ExtCsd.PartitionConfig;
if ((PartitionConfig & 0x7) != Partition->PartitionType) {
PartitionConfig &= (UINT8)~0x7;
PartitionConfig |= Partition->PartitionType;
Status = EmmcSetExtCsd (Partition, OFFSET_OF (EMMC_EXT_CSD, PartitionConfig), PartitionConfig, (EFI_BLOCK_IO2_TOKEN*)Token, FALSE);
if (EFI_ERROR (Status)) {
return Status;
}
Device->ExtCsd.PartitionConfig = PartitionConfig;
}
Status = EmmcEraseBlockStart (Partition, Lba, (EFI_BLOCK_IO2_TOKEN*)Token, FALSE);
if (EFI_ERROR (Status)) {
return Status;
}
Status = EmmcEraseBlockEnd (Partition, LastLba, (EFI_BLOCK_IO2_TOKEN*)Token, FALSE);
if (EFI_ERROR (Status)) {
return Status;
}
Status = EmmcEraseBlock (Partition, (EFI_BLOCK_IO2_TOKEN*)Token, TRUE);
if (EFI_ERROR (Status)) {
return Status;
}
DEBUG ((EFI_D_ERROR, "EmmcEraseBlocks(): Lba 0x%x BlkNo 0x%x Event %p with %r\n", Lba, BlockNum, Token->Event, Status));
return Status;
}