diff options
Diffstat (limited to 'MdeModulePkg/Bus/Sd/EmmcDxe/EmmcBlockIo.c')
-rw-r--r-- | MdeModulePkg/Bus/Sd/EmmcDxe/EmmcBlockIo.c | 1591 |
1 files changed, 1591 insertions, 0 deletions
diff --git a/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcBlockIo.c b/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcBlockIo.c new file mode 100644 index 0000000000..edb438b09b --- /dev/null +++ b/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcBlockIo.c @@ -0,0 +1,1591 @@ +/** @file
+ The helper functions for BlockIo and BlockIo2 protocol.
+
+ Copyright (c) 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.
+
+**/
+
+#include "EmmcDxe.h"
+
+/**
+ Nonblocking I/O callback funtion when the event is signaled.
+
+ @param[in] Event The Event this notify function registered to.
+ @param[in] Context Pointer to the context data registered to the
+ Event.
+
+**/
+VOID
+EFIAPI
+AsyncIoCallback (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ EMMC_REQUEST *Request;
+ EFI_STATUS Status;
+
+ Status = gBS->CloseEvent (Event);
+ if (EFI_ERROR (Status)) {
+ return;
+ }
+
+ Request = (EMMC_REQUEST *) Context;
+
+ DEBUG_CODE_BEGIN ();
+ DEBUG ((EFI_D_INFO, "Emmc Async Request: CmdIndex[%d] Arg[%08x] %r\n",
+ Request->SdMmcCmdBlk.CommandIndex, Request->SdMmcCmdBlk.CommandArgument,
+ Request->Packet.TransactionStatus));
+ DEBUG_CODE_END ();
+
+ if (EFI_ERROR (Request->Packet.TransactionStatus)) {
+ Request->Token->TransactionStatus = Request->Packet.TransactionStatus;
+ }
+
+ RemoveEntryList (&Request->Link);
+
+ if (Request->IsEnd) {
+ gBS->SignalEvent (Request->Token->Event);
+ }
+
+ FreePool (Request);
+}
+
+/**
+ Send command SELECT to the device to select/deselect the device.
+
+ @param[in] Device A pointer to the EMMC_DEVICE instance.
+ @param[in] Rca The relative device address to use.
+
+ @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
+EmmcSelect (
+ IN EMMC_DEVICE *Device,
+ IN UINT16 Rca
+ )
+{
+ EFI_STATUS Status;
+ EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru;
+ EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk;
+ EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk;
+ EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet;
+
+ PassThru = Device->Private->PassThru;
+
+ ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk));
+ ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk));
+ ZeroMem (&Packet, sizeof (Packet));
+ Packet.SdMmcCmdBlk = &SdMmcCmdBlk;
+ Packet.SdMmcStatusBlk = &SdMmcStatusBlk;
+ Packet.Timeout = EMMC_GENERIC_TIMEOUT;
+
+ SdMmcCmdBlk.CommandIndex = EMMC_SELECT_DESELECT_CARD;
+ SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc;
+ SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1;
+ SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16;
+
+ Status = PassThru->PassThru (PassThru, Device->Slot, &Packet, NULL);
+
+ return Status;
+}
+
+/**
+ Send command SEND_STATUS to the device to get device status.
+
+ @param[in] Device A pointer to the EMMC_DEVICE instance.
+ @param[in] Rca The relative device address to use.
+ @param[out] DevStatus The buffer to store the device status.
+
+ @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
+EmmcSendStatus (
+ IN EMMC_DEVICE *Device,
+ IN UINT16 Rca,
+ OUT UINT32 *DevStatus
+ )
+{
+ EFI_STATUS Status;
+ EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru;
+ EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk;
+ EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk;
+ EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet;
+
+ PassThru = Device->Private->PassThru;
+
+ ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk));
+ ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk));
+ ZeroMem (&Packet, sizeof (Packet));
+ Packet.SdMmcCmdBlk = &SdMmcCmdBlk;
+ Packet.SdMmcStatusBlk = &SdMmcStatusBlk;
+ Packet.Timeout = EMMC_GENERIC_TIMEOUT;
+
+ SdMmcCmdBlk.CommandIndex = EMMC_SEND_STATUS;
+ SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc;
+ SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1;
+ SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16;
+
+ Status = PassThru->PassThru (PassThru, Device->Slot, &Packet, NULL);
+ if (!EFI_ERROR (Status)) {
+ CopyMem (DevStatus, &SdMmcStatusBlk.Resp0, sizeof (UINT32));
+ }
+
+ return Status;
+}
+
+/**
+ Send command SEND_CSD to the device to get the CSD register data.
+
+ @param[in] Device A pointer to the EMMC_DEVICE instance.
+ @param[in] Rca The relative device address to use.
+ @param[out] Csd The buffer to store the EMMC_CSD register data.
+
+ @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
+EmmcGetCsd (
+ IN EMMC_DEVICE *Device,
+ IN UINT16 Rca,
+ OUT EMMC_CSD *Csd
+ )
+{
+ EFI_STATUS Status;
+ EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru;
+ EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk;
+ EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk;
+ EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet;
+
+ PassThru = Device->Private->PassThru;
+
+ ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk));
+ ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk));
+ ZeroMem (&Packet, sizeof (Packet));
+ ZeroMem (Csd, sizeof (EMMC_CSD));
+
+ Packet.SdMmcCmdBlk = &SdMmcCmdBlk;
+ Packet.SdMmcStatusBlk = &SdMmcStatusBlk;
+ Packet.Timeout = EMMC_GENERIC_TIMEOUT;
+
+ SdMmcCmdBlk.CommandIndex = EMMC_SEND_CSD;
+ SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc;
+ SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR2;
+ SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16;
+
+ Status = PassThru->PassThru (PassThru, Device->Slot, &Packet, NULL);
+ if (!EFI_ERROR (Status)) {
+ //
+ // For details, refer to SD Host Controller Simplified Spec 3.0 Table 2-12.
+ //
+ CopyMem (((UINT8*)Csd) + 1, &SdMmcStatusBlk.Resp0, sizeof (EMMC_CSD) - 1);
+ }
+
+ return Status;
+}
+
+/**
+ Send command SEND_CID to the device to get the CID register data.
+
+ @param[in] Device A pointer to the EMMC_DEVICE instance.
+ @param[in] Rca The relative device address to use.
+ @param[out] Cid The buffer to store the EMMC_CID register data.
+
+ @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
+EmmcGetCid (
+ IN EMMC_DEVICE *Device,
+ IN UINT16 Rca,
+ OUT EMMC_CID *Cid
+ )
+{
+ EFI_STATUS Status;
+ EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru;
+ EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk;
+ EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk;
+ EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet;
+
+ PassThru = Device->Private->PassThru;
+
+ ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk));
+ ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk));
+ ZeroMem (&Packet, sizeof (Packet));
+ ZeroMem (Cid, sizeof (EMMC_CID));
+
+ Packet.SdMmcCmdBlk = &SdMmcCmdBlk;
+ Packet.SdMmcStatusBlk = &SdMmcStatusBlk;
+ Packet.Timeout = EMMC_GENERIC_TIMEOUT;
+
+ SdMmcCmdBlk.CommandIndex = EMMC_SEND_CID;
+ SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc;
+ SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR2;
+ SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16;
+
+ Status = PassThru->PassThru (PassThru, Device->Slot, &Packet, NULL);
+ if (!EFI_ERROR (Status)) {
+ //
+ // For details, refer to SD Host Controller Simplified Spec 3.0 Table 2-12.
+ //
+ CopyMem (((UINT8*)Cid) + 1, &SdMmcStatusBlk.Resp0, sizeof (EMMC_CID) - 1);
+ }
+
+ return Status;
+}
+
+/**
+ Send command SEND_EXT_CSD to the device to get the EXT_CSD register data.
+
+ @param[in] Device A pointer to the EMMC_DEVICE instance.
+ @param[out] ExtCsd The buffer to store the EXT_CSD register data.
+
+ @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
+EmmcGetExtCsd (
+ IN EMMC_DEVICE *Device,
+ OUT EMMC_EXT_CSD *ExtCsd
+ )
+{
+ EFI_STATUS Status;
+ EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru;
+ EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk;
+ EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk;
+ EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet;
+
+ PassThru = Device->Private->PassThru;
+
+ ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk));
+ ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk));
+ ZeroMem (&Packet, sizeof (Packet));
+ ZeroMem (ExtCsd, sizeof (EMMC_EXT_CSD));
+ Packet.SdMmcCmdBlk = &SdMmcCmdBlk;
+ Packet.SdMmcStatusBlk = &SdMmcStatusBlk;
+ Packet.Timeout = EMMC_GENERIC_TIMEOUT;
+
+ SdMmcCmdBlk.CommandIndex = EMMC_SEND_EXT_CSD;
+ SdMmcCmdBlk.CommandType = SdMmcCommandTypeAdtc;
+ SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1;
+ SdMmcCmdBlk.CommandArgument = 0x00000000;
+ Packet.InDataBuffer = ExtCsd;
+ Packet.InTransferLength = sizeof (EMMC_EXT_CSD);
+
+ Status = PassThru->PassThru (PassThru, Device->Slot, &Packet, NULL);
+
+ return Status;
+}
+
+/**
+ Set the specified EXT_CSD register field through sync or async I/O request.
+
+ @param[in] Partition A pointer to the EMMC_PARTITION instance.
+ @param[in] Offset The offset of the specified field in EXT_CSD register.
+ @param[in] Value The byte value written to the field specified by Offset.
+ @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
+EmmcSetExtCsd (
+ IN EMMC_PARTITION *Partition,
+ IN UINT8 Offset,
+ IN UINT8 Value,
+ IN EFI_BLOCK_IO2_TOKEN *Token,
+ IN BOOLEAN IsEnd
+ )
+{
+ EFI_STATUS Status;
+ EMMC_DEVICE *Device;
+ EMMC_REQUEST *SetExtCsdReq;
+ EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru;
+ UINT32 CommandArgument;
+ EFI_TPL OldTpl;
+
+ SetExtCsdReq = NULL;
+
+ Device = Partition->Device;
+ PassThru = Device->Private->PassThru;
+
+ SetExtCsdReq = AllocateZeroPool (sizeof (EMMC_REQUEST));
+ if (SetExtCsdReq == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Error;
+ }
+
+ SetExtCsdReq->Signature = EMMC_REQUEST_SIGNATURE;
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ InsertTailList (&Partition->Queue, &SetExtCsdReq->Link);
+ gBS->RestoreTPL (OldTpl);
+ SetExtCsdReq->Packet.SdMmcCmdBlk = &SetExtCsdReq->SdMmcCmdBlk;
+ SetExtCsdReq->Packet.SdMmcStatusBlk = &SetExtCsdReq->SdMmcStatusBlk;
+ SetExtCsdReq->Packet.Timeout = EMMC_GENERIC_TIMEOUT;
+
+ SetExtCsdReq->SdMmcCmdBlk.CommandIndex = EMMC_SWITCH;
+ SetExtCsdReq->SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc;
+ SetExtCsdReq->SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1b;
+ //
+ // Write the Value to the field specified by Offset.
+ //
+ CommandArgument = (Value << 8) | (Offset << 16) | BIT24 | BIT25;
+ SetExtCsdReq->SdMmcCmdBlk.CommandArgument = CommandArgument;
+
+ SetExtCsdReq->IsEnd = IsEnd;
+ SetExtCsdReq->Token = Token;
+
+ if ((Token != NULL) && (Token->Event != NULL)) {
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK,
+ AsyncIoCallback,
+ SetExtCsdReq,
+ &SetExtCsdReq->Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto Error;
+ }
+ } else {
+ SetExtCsdReq->Event = NULL;
+ }
+
+ Status = PassThru->PassThru (PassThru, Device->Slot, &SetExtCsdReq->Packet, SetExtCsdReq->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) && (SetExtCsdReq != NULL)) {
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ RemoveEntryList (&SetExtCsdReq->Link);
+ gBS->RestoreTPL (OldTpl);
+ if (SetExtCsdReq->Event != NULL) {
+ gBS->CloseEvent (SetExtCsdReq->Event);
+ }
+ FreePool (SetExtCsdReq);
+ }
+ } else {
+ //
+ // For synchronous operation, free request whatever the execution result is.
+ //
+ if (SetExtCsdReq != NULL) {
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ RemoveEntryList (&SetExtCsdReq->Link);
+ gBS->RestoreTPL (OldTpl);
+ FreePool (SetExtCsdReq);
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Set the number of blocks for a block read/write cmd through sync or async I/O request.
+
+ @param[in] Partition A pointer to the EMMC_PARTITION instance.
+ @param[in] BlockNum The number of blocks for transfer.
+ @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
+EmmcSetBlkCount (
+ IN EMMC_PARTITION *Partition,
+ IN UINT16 BlockNum,
+ IN EFI_BLOCK_IO2_TOKEN *Token,
+ IN BOOLEAN IsEnd
+ )
+{
+ EFI_STATUS Status;
+ EMMC_DEVICE *Device;
+ EMMC_REQUEST *SetBlkCntReq;
+ EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru;
+ EFI_TPL OldTpl;
+
+ SetBlkCntReq = NULL;
+
+ Device = Partition->Device;
+ PassThru = Device->Private->PassThru;
+
+ SetBlkCntReq = AllocateZeroPool (sizeof (EMMC_REQUEST));
+ if (SetBlkCntReq == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Error;
+ }
+
+ SetBlkCntReq->Signature = EMMC_REQUEST_SIGNATURE;
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ InsertTailList (&Partition->Queue, &SetBlkCntReq->Link);
+ gBS->RestoreTPL (OldTpl);
+ SetBlkCntReq->Packet.SdMmcCmdBlk = &SetBlkCntReq->SdMmcCmdBlk;
+ SetBlkCntReq->Packet.SdMmcStatusBlk = &SetBlkCntReq->SdMmcStatusBlk;
+ SetBlkCntReq->Packet.Timeout = EMMC_GENERIC_TIMEOUT;
+
+ SetBlkCntReq->SdMmcCmdBlk.CommandIndex = EMMC_SET_BLOCK_COUNT;
+ SetBlkCntReq->SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc;
+ SetBlkCntReq->SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1;
+ SetBlkCntReq->SdMmcCmdBlk.CommandArgument = BlockNum;
+
+ SetBlkCntReq->IsEnd = IsEnd;
+ SetBlkCntReq->Token = Token;
+
+ if ((Token != NULL) && (Token->Event != NULL)) {
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK,
+ AsyncIoCallback,
+ SetBlkCntReq,
+ &SetBlkCntReq->Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto Error;
+ }
+ } else {
+ SetBlkCntReq->Event = NULL;
+ }
+
+ Status = PassThru->PassThru (PassThru, Device->Slot, &SetBlkCntReq->Packet, SetBlkCntReq->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) && (SetBlkCntReq != NULL)) {
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ RemoveEntryList (&SetBlkCntReq->Link);
+ gBS->RestoreTPL (OldTpl);
+ if (SetBlkCntReq->Event != NULL) {
+ gBS->CloseEvent (SetBlkCntReq->Event);
+ }
+ FreePool (SetBlkCntReq);
+ }
+ } else {
+ //
+ // For synchronous operation, free request whatever the execution result is.
+ //
+ if (SetBlkCntReq != NULL) {
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ RemoveEntryList (&SetBlkCntReq->Link);
+ gBS->RestoreTPL (OldTpl);
+ FreePool (SetBlkCntReq);
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Read blocks through security protocol cmds with the way of sync or async I/O request.
+
+ @param[in] Partition A pointer to the EMMC_PARTITION instance.
+ @param[in] SecurityProtocolId The value of the "Security Protocol" parameter of
+ the security protocol command to be sent.
+ @param[in] SecurityProtocolSpecificData The value of the "Security Protocol Specific" parameter
+ of the security protocol command to be sent.
+ @param[in] PayloadBufferSize Size in bytes of the payload data buffer.
+ @param[out] PayloadBuffer A pointer to a destination buffer to store the security
+ protocol command specific payload data for the security
+ protocol command. The caller is responsible for having
+ either implicit or explicit ownership of the buffer.
+ @param[in] IsRead Indicates it is a read or write operation.
+ @param[in] Timeout The timeout value, in 100ns units.
+ @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
+EmmcProtocolInOut (
+ IN EMMC_PARTITION *Partition,
+ IN UINT8 SecurityProtocol,
+ IN UINT16 SecurityProtocolSpecificData,
+ IN UINTN PayloadBufferSize,
+ OUT VOID *PayloadBuffer,
+ IN BOOLEAN IsRead,
+ IN UINT64 Timeout,
+ IN EFI_BLOCK_IO2_TOKEN *Token,
+ IN BOOLEAN IsEnd
+ )
+{
+ EFI_STATUS Status;
+ EMMC_DEVICE *Device;
+ EMMC_REQUEST *ProtocolReq;
+ EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru;
+ EFI_TPL OldTpl;
+
+ ProtocolReq = NULL;
+
+ Device = Partition->Device;
+ PassThru = Device->Private->PassThru;
+
+ ProtocolReq = AllocateZeroPool (sizeof (EMMC_REQUEST));
+ if (ProtocolReq == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Error;
+ }
+
+ ProtocolReq->Signature = EMMC_REQUEST_SIGNATURE;
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ InsertTailList (&Partition->Queue, &ProtocolReq->Link);
+ gBS->RestoreTPL (OldTpl);
+ ProtocolReq->Packet.SdMmcCmdBlk = &ProtocolReq->SdMmcCmdBlk;
+ ProtocolReq->Packet.SdMmcStatusBlk = &ProtocolReq->SdMmcStatusBlk;
+
+ if (IsRead) {
+ ProtocolReq->Packet.InDataBuffer = PayloadBuffer;
+ ProtocolReq->Packet.InTransferLength = (UINT32)PayloadBufferSize;
+
+ ProtocolReq->SdMmcCmdBlk.CommandIndex = EMMC_PROTOCOL_RD;
+ ProtocolReq->SdMmcCmdBlk.CommandType = SdMmcCommandTypeAdtc;
+ ProtocolReq->SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1;
+ } else {
+ ProtocolReq->Packet.OutDataBuffer = PayloadBuffer;
+ ProtocolReq->Packet.OutTransferLength = (UINT32)PayloadBufferSize;
+
+ ProtocolReq->SdMmcCmdBlk.CommandIndex = EMMC_PROTOCOL_WR;
+ ProtocolReq->SdMmcCmdBlk.CommandType = SdMmcCommandTypeAdtc;
+ ProtocolReq->SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1;
+ }
+
+ ProtocolReq->SdMmcCmdBlk.CommandArgument = (SecurityProtocol << 8) | (SecurityProtocolSpecificData << 16);
+ //
+ // Convert to 1 microsecond unit.
+ //
+ ProtocolReq->Packet.Timeout = DivU64x32 (Timeout, 10) + 1;
+
+ ProtocolReq->IsEnd = IsEnd;
+ ProtocolReq->Token = Token;
+
+ if ((Token != NULL) && (Token->Event != NULL)) {
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK,
+ AsyncIoCallback,
+ ProtocolReq,
+ &ProtocolReq->Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto Error;
+ }
+ } else {
+ ProtocolReq->Event = NULL;
+ }
+
+ Status = PassThru->PassThru (PassThru, Device->Slot, &ProtocolReq->Packet, ProtocolReq->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) && (ProtocolReq != NULL)) {
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ RemoveEntryList (&ProtocolReq->Link);
+ gBS->RestoreTPL (OldTpl);
+ if (ProtocolReq->Event != NULL) {
+ gBS->CloseEvent (ProtocolReq->Event);
+ }
+ FreePool (ProtocolReq);
+ }
+ } else {
+ //
+ // For synchronous operation, free request whatever the execution result is.
+ //
+ if (ProtocolReq != NULL) {
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ RemoveEntryList (&ProtocolReq->Link);
+ gBS->RestoreTPL (OldTpl);
+ FreePool (ProtocolReq);
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Read/write multiple blocks through sync or async I/O request.
+
+ @param[in] Partition A pointer to the EMMC_PARTITION instance.
+ @param[in] Lba The starting logical block address to be read/written.
+ The caller is responsible for reading/writing to only
+ legitimate locations.
+ @param[in] Buffer A pointer to the destination/source buffer for the data.
+ @param[in] BufferSize Size of Buffer, must be a multiple of device block size.
+ @param[in] IsRead Indicates it is a read or write operation.
+ @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
+EmmcRwMultiBlocks (
+ IN EMMC_PARTITION *Partition,
+ IN EFI_LBA Lba,
+ IN VOID *Buffer,
+ IN UINTN BufferSize,
+ IN BOOLEAN IsRead,
+ IN EFI_BLOCK_IO2_TOKEN *Token,
+ IN BOOLEAN IsEnd
+ )
+{
+ EFI_STATUS Status;
+ EMMC_DEVICE *Device;
+ EMMC_REQUEST *RwMultiBlkReq;
+ EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru;
+ EFI_TPL OldTpl;
+
+ RwMultiBlkReq = NULL;
+
+ Device = Partition->Device;
+ PassThru = Device->Private->PassThru;
+
+ RwMultiBlkReq = AllocateZeroPool (sizeof (EMMC_REQUEST));
+ if (RwMultiBlkReq == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Error;
+ }
+
+ RwMultiBlkReq->Signature = EMMC_REQUEST_SIGNATURE;
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ InsertTailList (&Partition->Queue, &RwMultiBlkReq->Link);
+ gBS->RestoreTPL (OldTpl);
+ RwMultiBlkReq->Packet.SdMmcCmdBlk = &RwMultiBlkReq->SdMmcCmdBlk;
+ RwMultiBlkReq->Packet.SdMmcStatusBlk = &RwMultiBlkReq->SdMmcStatusBlk;
+ //
+ // Calculate timeout value through the below formula.
+ // Timeout = (transfer size) / (2MB/s).
+ // Taking 2MB/s as divisor is because it's nearest to the eMMC lowest
+ // transfer speed (2.4MB/s).
+ // Refer to eMMC 5.0 spec section 6.9.1 for details.
+ //
+ RwMultiBlkReq->Packet.Timeout = (BufferSize / (2 * 1024 * 1024) + 1) * 1000 * 1000;
+
+ if (IsRead) {
+ RwMultiBlkReq->Packet.InDataBuffer = Buffer;
+ RwMultiBlkReq->Packet.InTransferLength = (UINT32)BufferSize;
+
+ RwMultiBlkReq->SdMmcCmdBlk.CommandIndex = EMMC_READ_MULTIPLE_BLOCK;
+ RwMultiBlkReq->SdMmcCmdBlk.CommandType = SdMmcCommandTypeAdtc;
+ RwMultiBlkReq->SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1;
+ } else {
+ RwMultiBlkReq->Packet.OutDataBuffer = Buffer;
+ RwMultiBlkReq->Packet.OutTransferLength = (UINT32)BufferSize;
+
+ RwMultiBlkReq->SdMmcCmdBlk.CommandIndex = EMMC_WRITE_MULTIPLE_BLOCK;
+ RwMultiBlkReq->SdMmcCmdBlk.CommandType = SdMmcCommandTypeAdtc;
+ RwMultiBlkReq->SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1;
+ }
+
+ if (Partition->Device->SectorAddressing) {
+ RwMultiBlkReq->SdMmcCmdBlk.CommandArgument = (UINT32)Lba;
+ } else {
+ RwMultiBlkReq->SdMmcCmdBlk.CommandArgument = (UINT32)MultU64x32 (Lba, Partition->BlockMedia.BlockSize);
+ }
+
+ RwMultiBlkReq->IsEnd = IsEnd;
+ RwMultiBlkReq->Token = Token;
+
+ if ((Token != NULL) && (Token->Event != NULL)) {
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK,
+ AsyncIoCallback,
+ RwMultiBlkReq,
+ &RwMultiBlkReq->Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto Error;
+ }
+ } else {
+ RwMultiBlkReq->Event = NULL;
+ }
+
+ Status = PassThru->PassThru (PassThru, Device->Slot, &RwMultiBlkReq->Packet, RwMultiBlkReq->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) && (RwMultiBlkReq != NULL)) {
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ RemoveEntryList (&RwMultiBlkReq->Link);
+ gBS->RestoreTPL (OldTpl);
+ if (RwMultiBlkReq->Event != NULL) {
+ gBS->CloseEvent (RwMultiBlkReq->Event);
+ }
+ FreePool (RwMultiBlkReq);
+ }
+ } else {
+ //
+ // For synchronous operation, free request whatever the execution result is.
+ //
+ if (RwMultiBlkReq != NULL) {
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ RemoveEntryList (&RwMultiBlkReq->Link);
+ gBS->RestoreTPL (OldTpl);
+ FreePool (RwMultiBlkReq);
+ }
+ }
+
+ return Status;
+}
+
+/**
+ This function transfers data from/to EMMC device.
+
+ @param[in] Partition A pointer to the EMMC_PARTITION instance.
+ @param[in] MediaId The media ID that the read/write request is for.
+ @param[in] Lba The starting logical block address to be read/written.
+ The caller is responsible for reading/writing to only
+ legitimate locations.
+ @param[in, out] Buffer A pointer to the destination/source buffer for the data.
+ @param[in] BufferSize Size of Buffer, must be a multiple of device block size.
+ @param[in] IsRead Indicates it is a read or write operation.
+ @param[in, out] Token A pointer to the token associated with the transaction.
+
+ @retval EFI_SUCCESS The data was read/written correctly to the device.
+ @retval EFI_WRITE_PROTECTED The device can not be read/written to.
+ @retval EFI_DEVICE_ERROR The device reported an error while performing the read/write.
+ @retval EFI_NO_MEDIA There is no media in the device.
+ @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device.
+ @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device.
+ @retval EFI_INVALID_PARAMETER The read/write request contains LBAs that are not valid,
+ or the buffer is not on proper alignment.
+
+**/
+EFI_STATUS
+EmmcReadWrite (
+ IN EMMC_PARTITION *Partition,
+ IN UINT32 MediaId,
+ IN EFI_LBA Lba,
+ IN OUT VOID *Buffer,
+ IN UINTN BufferSize,
+ IN BOOLEAN IsRead,
+ IN OUT EFI_BLOCK_IO2_TOKEN *Token
+ )
+{
+ EFI_STATUS Status;
+ EMMC_DEVICE *Device;
+ EFI_BLOCK_IO_MEDIA *Media;
+ UINTN BlockSize;
+ UINTN BlockNum;
+ UINTN IoAlign;
+ UINT8 PartitionConfig;
+ UINTN Remaining;
+ UINT32 MaxBlock;
+ BOOLEAN LastRw;
+
+ Status = EFI_SUCCESS;
+ Device = Partition->Device;
+ Media = &Partition->BlockMedia;
+ LastRw = FALSE;
+
+ if (MediaId != Media->MediaId) {
+ return EFI_MEDIA_CHANGED;
+ }
+
+ if (!IsRead && Media->ReadOnly) {
+ return EFI_WRITE_PROTECTED;
+ }
+
+ //
+ // Check parameters.
+ //
+ if (Buffer == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (BufferSize == 0) {
+ if ((Token != NULL) && (Token->Event != NULL)) {
+ Token->TransactionStatus = EFI_SUCCESS;
+ gBS->SignalEvent (Token->Event);
+ }
+ return EFI_SUCCESS;
+ }
+
+ BlockSize = Media->BlockSize;
+ if ((BufferSize % BlockSize) != 0) {
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ BlockNum = BufferSize / BlockSize;
+ if ((Lba + BlockNum - 1) > Media->LastBlock) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ IoAlign = Media->IoAlign;
+ if (IoAlign > 0 && (((UINTN) Buffer & (IoAlign - 1)) != 0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((Token != NULL) && (Token->Event != NULL)) {
+ Token->TransactionStatus = EFI_SUCCESS;
+ }
+ //
+ // 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, Token, FALSE);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ Device->ExtCsd.PartitionConfig = PartitionConfig;
+ }
+ //
+ // Start to execute data transfer. The max block number in single cmd is 65535 blocks.
+ //
+ Remaining = BlockNum;
+ MaxBlock = 0xFFFF;
+
+ while (Remaining > 0) {
+ if (Remaining <= MaxBlock) {
+ BlockNum = Remaining;
+ LastRw = TRUE;
+ } else {
+ BlockNum = MaxBlock;
+ }
+ Status = EmmcSetBlkCount (Partition, (UINT16)BlockNum, Token, FALSE);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ BufferSize = BlockNum * BlockSize;
+ Status = EmmcRwMultiBlocks (Partition, Lba, Buffer, BufferSize, IsRead, Token, LastRw);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ DEBUG ((EFI_D_INFO, "Emmc%a(): Lba 0x%x BlkNo 0x%x Event %p with %r\n", IsRead ? "Read" : "Write", Lba, BlockNum, Token->Event, Status));
+
+ Lba += BlockNum;
+ Buffer = (UINT8*)Buffer + BufferSize;
+ Remaining -= BlockNum;
+ }
+
+ return Status;
+}
+
+/**
+ Reset the Block Device.
+
+ @param This Indicates a pointer to the calling context.
+ @param ExtendedVerification Driver may perform diagnostics on reset.
+
+ @retval EFI_SUCCESS The device was reset.
+ @retval EFI_DEVICE_ERROR The device is not functioning properly and could
+ not be reset.
+
+**/
+EFI_STATUS
+EFIAPI
+EmmcReset (
+ IN EFI_BLOCK_IO_PROTOCOL *This,
+ IN BOOLEAN ExtendedVerification
+ )
+{
+ EFI_STATUS Status;
+ EMMC_PARTITION *Partition;
+ EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru;
+
+ Partition = EMMC_PARTITION_DATA_FROM_BLKIO (This);
+
+ PassThru = Partition->Device->Private->PassThru;
+ Status = PassThru->ResetDevice (PassThru, Partition->Device->Slot);
+ if (EFI_ERROR (Status)) {
+ Status = EFI_DEVICE_ERROR;
+ }
+
+ return Status;
+}
+
+/**
+ Read BufferSize bytes from Lba into Buffer.
+
+ @param This Indicates a pointer to the calling context.
+ @param MediaId Id of the media, changes every time the media is replaced.
+ @param Lba The starting Logical Block Address to read from
+ @param BufferSize Size of Buffer, must be a multiple of device block size.
+ @param Buffer A pointer to the destination buffer for the data. The caller is
+ responsible for either having implicit or explicit 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 performing the read.
+ @retval EFI_NO_MEDIA There is no media in the device.
+ @retval EFI_MEDIA_CHANGED The MediaId does not matched the current device.
+ @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device.
+ @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid,
+ or the buffer is not on proper alignment.
+
+**/
+EFI_STATUS
+EFIAPI
+EmmcReadBlocks (
+ IN EFI_BLOCK_IO_PROTOCOL *This,
+ IN UINT32 MediaId,
+ IN EFI_LBA Lba,
+ IN UINTN BufferSize,
+ OUT VOID *Buffer
+ )
+{
+ EFI_STATUS Status;
+ EMMC_PARTITION *Partition;
+
+ Partition = EMMC_PARTITION_DATA_FROM_BLKIO (This);
+
+ Status = EmmcReadWrite (Partition, MediaId, Lba, Buffer, BufferSize, TRUE, NULL);
+ return Status;
+}
+
+/**
+ Write BufferSize bytes from Lba into Buffer.
+
+ @param This Indicates a pointer to the calling context.
+ @param MediaId The media ID that the write request is for.
+ @param Lba The starting logical block address to be written. The caller is
+ responsible for writing to only legitimate locations.
+ @param BufferSize Size of Buffer, must be a multiple of device block size.
+ @param Buffer A pointer to the source buffer for the data.
+
+ @retval EFI_SUCCESS The data was written correctly to the device.
+ @retval EFI_WRITE_PROTECTED The device can not be written to.
+ @retval EFI_DEVICE_ERROR The device reported an error while performing the write.
+ @retval EFI_NO_MEDIA There is no media in the device.
+ @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device.
+ @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device.
+ @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid,
+ or the buffer is not on proper alignment.
+
+**/
+EFI_STATUS
+EFIAPI
+EmmcWriteBlocks (
+ IN EFI_BLOCK_IO_PROTOCOL *This,
+ IN UINT32 MediaId,
+ IN EFI_LBA Lba,
+ IN UINTN BufferSize,
+ IN VOID *Buffer
+ )
+{
+ EFI_STATUS Status;
+ EMMC_PARTITION *Partition;
+
+ Partition = EMMC_PARTITION_DATA_FROM_BLKIO (This);
+
+ Status = EmmcReadWrite (Partition, MediaId, Lba, Buffer, BufferSize, FALSE, NULL);
+ return Status;
+}
+
+/**
+ Flush the Block Device.
+
+ @param This Indicates a pointer to the calling context.
+
+ @retval EFI_SUCCESS All outstanding data was written to the device
+ @retval EFI_DEVICE_ERROR The device reported an error while writing back the data
+ @retval EFI_NO_MEDIA There is no media in the device.
+
+**/
+EFI_STATUS
+EFIAPI
+EmmcFlushBlocks (
+ IN EFI_BLOCK_IO_PROTOCOL *This
+ )
+{
+ //
+ // return directly
+ //
+ return EFI_SUCCESS;
+}
+
+/**
+ Reset the Block Device.
+
+ @param[in] This Indicates a pointer to the calling context.
+ @param[in] ExtendedVerification Driver may perform diagnostics on reset.
+
+ @retval EFI_SUCCESS The device was reset.
+ @retval EFI_DEVICE_ERROR The device is not functioning properly and could
+ not be reset.
+
+**/
+EFI_STATUS
+EFIAPI
+EmmcResetEx (
+ IN EFI_BLOCK_IO2_PROTOCOL *This,
+ IN BOOLEAN ExtendedVerification
+ )
+{
+ EMMC_PARTITION *Partition;
+ LIST_ENTRY *Link;
+ LIST_ENTRY *NextLink;
+ EMMC_REQUEST *Request;
+ EFI_TPL OldTpl;
+
+ Partition = EMMC_PARTITION_DATA_FROM_BLKIO2 (This);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ for (Link = GetFirstNode (&Partition->Queue);
+ !IsNull (&Partition->Queue, Link);
+ Link = NextLink) {
+ NextLink = GetNextNode (&Partition->Queue, Link);
+ RemoveEntryList (Link);
+
+ Request = EMMC_REQUEST_FROM_LINK (Link);
+
+ gBS->CloseEvent (Request->Event);
+ Request->Token->TransactionStatus = EFI_ABORTED;
+
+ if (Request->IsEnd) {
+ gBS->SignalEvent (Request->Token->Event);
+ }
+
+ FreePool (Request);
+ }
+ gBS->RestoreTPL (OldTpl);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Read BufferSize bytes from Lba into Buffer.
+
+ @param[in] This Indicates a pointer to the calling context.
+ @param[in] MediaId Id of the media, changes every time the media is replaced.
+ @param[in] Lba The starting Logical Block Address to read from.
+ @param[in, out] Token A pointer to the token associated with the transaction.
+ @param[in] BufferSize Size of Buffer, must be a multiple of device block size.
+ @param[out] Buffer A pointer to the destination buffer for the data. The caller is
+ responsible for either having implicit or explicit ownership of the buffer.
+
+ @retval EFI_SUCCESS The read request was queued if Event is not NULL.
+ The data was read correctly from the device if
+ the Event is NULL.
+ @retval EFI_DEVICE_ERROR The device reported an error while performing
+ the read.
+ @retval EFI_NO_MEDIA There is no media in the device.
+ @retval EFI_MEDIA_CHANGED The MediaId is not for the current media.
+ @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of the
+ intrinsic block size of the device.
+ @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid,
+ or the buffer is not on proper alignment.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack
+ of resources.
+
+**/
+EFI_STATUS
+EFIAPI
+EmmcReadBlocksEx (
+ IN EFI_BLOCK_IO2_PROTOCOL *This,
+ IN UINT32 MediaId,
+ IN EFI_LBA Lba,
+ IN OUT EFI_BLOCK_IO2_TOKEN *Token,
+ IN UINTN BufferSize,
+ OUT VOID *Buffer
+ )
+{
+ EFI_STATUS Status;
+ EMMC_PARTITION *Partition;
+
+ Partition = EMMC_PARTITION_DATA_FROM_BLKIO2 (This);
+
+ Status = EmmcReadWrite (Partition, MediaId, Lba, Buffer, BufferSize, TRUE, Token);
+ return Status;
+}
+
+/**
+ Write BufferSize bytes from Lba into Buffer.
+
+ @param[in] This Indicates a pointer to the calling context.
+ @param[in] MediaId The media ID that the write request is for.
+ @param[in] Lba The starting logical block address to be written. The
+ caller is responsible for writing to only legitimate
+ locations.
+ @param[in, out] Token A pointer to the token associated with the transaction.
+ @param[in] BufferSize Size of Buffer, must be a multiple of device block size.
+ @param[in] Buffer A pointer to the source buffer for the data.
+
+ @retval EFI_SUCCESS The data was written correctly to the device.
+ @retval EFI_WRITE_PROTECTED The device can not be written to.
+ @retval EFI_DEVICE_ERROR The device reported an error while performing the write.
+ @retval EFI_NO_MEDIA There is no media in the device.
+ @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device.
+ @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device.
+ @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid,
+ or the buffer is not on proper alignment.
+
+**/
+EFI_STATUS
+EFIAPI
+EmmcWriteBlocksEx (
+ IN EFI_BLOCK_IO2_PROTOCOL *This,
+ IN UINT32 MediaId,
+ IN EFI_LBA Lba,
+ IN OUT EFI_BLOCK_IO2_TOKEN *Token,
+ IN UINTN BufferSize,
+ IN VOID *Buffer
+ )
+{
+ EFI_STATUS Status;
+ EMMC_PARTITION *Partition;
+
+ Partition = EMMC_PARTITION_DATA_FROM_BLKIO2 (This);
+
+ Status = EmmcReadWrite (Partition, MediaId, Lba, Buffer, BufferSize, FALSE, Token);
+ return Status;
+}
+
+/**
+ Flush the Block Device.
+
+ @param[in] This Indicates a pointer to the calling context.
+ @param[in, out] Token A pointer to the token associated with the transaction.
+
+ @retval EFI_SUCCESS All outstanding data was written to the device
+ @retval EFI_DEVICE_ERROR The device reported an error while writing back the data
+ @retval EFI_NO_MEDIA There is no media in the device.
+
+**/
+EFI_STATUS
+EFIAPI
+EmmcFlushBlocksEx (
+ IN EFI_BLOCK_IO2_PROTOCOL *This,
+ IN OUT EFI_BLOCK_IO2_TOKEN *Token
+ )
+{
+ //
+ // Signal event and return directly.
+ //
+ if (Token != NULL && Token->Event != NULL) {
+ Token->TransactionStatus = EFI_SUCCESS;
+ gBS->SignalEvent (Token->Event);
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Send a security protocol command to a device that receives data and/or the result
+ of one or more commands sent by SendData.
+
+ The ReceiveData function sends a security protocol command to the given MediaId.
+ The security protocol command sent is defined by SecurityProtocolId and contains
+ the security protocol specific data SecurityProtocolSpecificData. The function
+ returns the data from the security protocol command in PayloadBuffer.
+
+ For devices supporting the SCSI command set, the security protocol command is sent
+ using the SECURITY PROTOCOL IN command defined in SPC-4.
+
+ For devices supporting the ATA command set, the security protocol command is sent
+ using one of the TRUSTED RECEIVE commands defined in ATA8-ACS if PayloadBufferSize
+ is non-zero.
+
+ If the PayloadBufferSize is zero, the security protocol command is sent using the
+ Trusted Non-Data command defined in ATA8-ACS.
+
+ If PayloadBufferSize is too small to store the available data from the security
+ protocol command, the function shall copy PayloadBufferSize bytes into the
+ PayloadBuffer and return EFI_WARN_BUFFER_TOO_SMALL.
+
+ If PayloadBuffer or PayloadTransferSize is NULL and PayloadBufferSize is non-zero,
+ the function shall return EFI_INVALID_PARAMETER.
+
+ If the given MediaId does not support security protocol commands, the function shall
+ return EFI_UNSUPPORTED. If there is no media in the device, the function returns
+ EFI_NO_MEDIA. If the MediaId is not the ID for the current media in the device,
+ the function returns EFI_MEDIA_CHANGED.
+
+ If the security protocol fails to complete within the Timeout period, the function
+ shall return EFI_TIMEOUT.
+
+ If the security protocol command completes without an error, the function shall
+ return EFI_SUCCESS. If the security protocol command completes with an error, the
+ function shall return EFI_DEVICE_ERROR.
+
+ @param[in] This Indicates a pointer to the calling context.
+ @param[in] MediaId ID of the medium to receive data from.
+ @param[in] Timeout The timeout, in 100ns units, to use for the execution
+ of the security protocol command. A Timeout value of 0
+ means that this function will wait indefinitely for the
+ security protocol command to execute. If Timeout is greater
+ than zero, then this function will return EFI_TIMEOUT
+ if the time required to execute the receive data command
+ is greater than Timeout.
+ @param[in] SecurityProtocolId The value of the "Security Protocol" parameter of
+ the security protocol command to be sent.
+ @param[in] SecurityProtocolSpecificData The value of the "Security Protocol Specific" parameter
+ of the security protocol command to be sent.
+ @param[in] PayloadBufferSize Size in bytes of the payload data buffer.
+ @param[out] PayloadBuffer A pointer to a destination buffer to store the security
+ protocol command specific payload data for the security
+ protocol command. The caller is responsible for having
+ either implicit or explicit ownership of the buffer.
+ @param[out] PayloadTransferSize A pointer to a buffer to store the size in bytes of the
+ data written to the payload data buffer.
+ @param[in] IsRead Indicates it is a read or write operation.
+
+ @retval EFI_SUCCESS The security protocol command completed successfully.
+ @retval EFI_WARN_BUFFER_TOO_SMALL The PayloadBufferSize was too small to store the available
+ data from the device. The PayloadBuffer contains the truncated data.
+ @retval EFI_UNSUPPORTED The given MediaId does not support security protocol commands.
+ @retval EFI_DEVICE_ERROR The security protocol command completed with an error.
+ @retval EFI_NO_MEDIA There is no media in the device.
+ @retval EFI_MEDIA_CHANGED The MediaId is not for the current media.
+ @retval EFI_INVALID_PARAMETER The PayloadBuffer or PayloadTransferSize is NULL and
+ PayloadBufferSize is non-zero.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the security
+ protocol command to execute.
+
+**/
+EFI_STATUS
+EFIAPI
+EmmcSecurityProtocolInOut (
+ IN EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *This,
+ IN UINT32 MediaId,
+ IN UINT64 Timeout,
+ IN UINT8 SecurityProtocolId,
+ IN UINT16 SecurityProtocolSpecificData,
+ IN UINTN PayloadBufferSize,
+ OUT VOID *PayloadBuffer,
+ OUT UINTN *PayloadTransferSize,
+ IN BOOLEAN IsRead
+ )
+{
+ EFI_STATUS Status;
+ EMMC_PARTITION *Partition;
+ EMMC_DEVICE *Device;
+ EFI_BLOCK_IO_MEDIA *Media;
+ UINTN BlockSize;
+ UINTN BlockNum;
+ UINTN IoAlign;
+ UINTN Remaining;
+ UINT32 MaxBlock;
+ UINT8 PartitionConfig;
+
+ Status = EFI_SUCCESS;
+ Partition = EMMC_PARTITION_DATA_FROM_SSP (This);
+ Device = Partition->Device;
+ Media = &Partition->BlockMedia;
+
+ if (PayloadTransferSize != NULL) {
+ *PayloadTransferSize = 0;
+ }
+
+ if ((PayloadBuffer == NULL) && (PayloadBufferSize != 0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (MediaId != Media->MediaId) {
+ return EFI_MEDIA_CHANGED;
+ }
+
+ if (PayloadBufferSize == 0) {
+ return EFI_SUCCESS;
+ }
+
+ BlockSize = Media->BlockSize;
+ if ((PayloadBufferSize % BlockSize) != 0) {
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ BlockNum = PayloadBufferSize / BlockSize;
+
+ IoAlign = Media->IoAlign;
+ if (IoAlign > 0 && (((UINTN) PayloadBuffer & (IoAlign - 1)) != 0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Security protocol interface is synchronous transfer.
+ // Waiting for async I/O list to be empty before any operation.
+ //
+ while (!IsListEmpty (&Partition->Queue));
+
+ //
+ // 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, NULL, FALSE);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ Device->ExtCsd.PartitionConfig = PartitionConfig;
+ }
+ //
+ // Start to execute data transfer. The max block number in single cmd is 65535 blocks.
+ //
+ Remaining = BlockNum;
+ MaxBlock = 0xFFFF;
+
+ while (Remaining > 0) {
+ if (Remaining <= MaxBlock) {
+ BlockNum = Remaining;
+ } else {
+ BlockNum = MaxBlock;
+ }
+
+ Status = EmmcSetBlkCount (Partition, (UINT16)BlockNum, NULL, FALSE);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ PayloadBufferSize = BlockNum * BlockSize;
+ Status = EmmcProtocolInOut (Partition, SecurityProtocolId, SecurityProtocolSpecificData, PayloadBufferSize, PayloadBuffer, IsRead, Timeout, NULL, FALSE);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ PayloadBuffer = (UINT8*)PayloadBuffer + PayloadBufferSize;
+ Remaining -= BlockNum;
+ if (PayloadTransferSize != NULL) {
+ *PayloadTransferSize += PayloadBufferSize;
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Send a security protocol command to a device that receives data and/or the result
+ of one or more commands sent by SendData.
+
+ The ReceiveData function sends a security protocol command to the given MediaId.
+ The security protocol command sent is defined by SecurityProtocolId and contains
+ the security protocol specific data SecurityProtocolSpecificData. The function
+ returns the data from the security protocol command in PayloadBuffer.
+
+ For devices supporting the SCSI command set, the security protocol command is sent
+ using the SECURITY PROTOCOL IN command defined in SPC-4.
+
+ For devices supporting the ATA command set, the security protocol command is sent
+ using one of the TRUSTED RECEIVE commands defined in ATA8-ACS if PayloadBufferSize
+ is non-zero.
+
+ If the PayloadBufferSize is zero, the security protocol command is sent using the
+ Trusted Non-Data command defined in ATA8-ACS.
+
+ If PayloadBufferSize is too small to store the available data from the security
+ protocol command, the function shall copy PayloadBufferSize bytes into the
+ PayloadBuffer and return EFI_WARN_BUFFER_TOO_SMALL.
+
+ If PayloadBuffer or PayloadTransferSize is NULL and PayloadBufferSize is non-zero,
+ the function shall return EFI_INVALID_PARAMETER.
+
+ If the given MediaId does not support security protocol commands, the function shall
+ return EFI_UNSUPPORTED. If there is no media in the device, the function returns
+ EFI_NO_MEDIA. If the MediaId is not the ID for the current media in the device,
+ the function returns EFI_MEDIA_CHANGED.
+
+ If the security protocol fails to complete within the Timeout period, the function
+ shall return EFI_TIMEOUT.
+
+ If the security protocol command completes without an error, the function shall
+ return EFI_SUCCESS. If the security protocol command completes with an error, the
+ function shall return EFI_DEVICE_ERROR.
+
+ @param This Indicates a pointer to the calling context.
+ @param MediaId ID of the medium to receive data from.
+ @param Timeout The timeout, in 100ns units, to use for the execution
+ of the security protocol command. A Timeout value of 0
+ means that this function will wait indefinitely for the
+ security protocol command to execute. If Timeout is greater
+ than zero, then this function will return EFI_TIMEOUT
+ if the time required to execute the receive data command
+ is greater than Timeout.
+ @param SecurityProtocolId The value of the "Security Protocol" parameter of
+ the security protocol command to be sent.
+ @param SecurityProtocolSpecificData The value of the "Security Protocol Specific" parameter
+ of the security protocol command to be sent.
+ @param PayloadBufferSize Size in bytes of the payload data buffer.
+ @param PayloadBuffer A pointer to a destination buffer to store the security
+ protocol command specific payload data for the security
+ protocol command. The caller is responsible for having
+ either implicit or explicit ownership of the buffer.
+ @param PayloadTransferSize A pointer to a buffer to store the size in bytes of the
+ data written to the payload data buffer.
+
+ @retval EFI_SUCCESS The security protocol command completed successfully.
+ @retval EFI_WARN_BUFFER_TOO_SMALL The PayloadBufferSize was too small to store the available
+ data from the device. The PayloadBuffer contains the truncated data.
+ @retval EFI_UNSUPPORTED The given MediaId does not support security protocol commands.
+ @retval EFI_DEVICE_ERROR The security protocol command completed with an error.
+ @retval EFI_NO_MEDIA There is no media in the device.
+ @retval EFI_MEDIA_CHANGED The MediaId is not for the current media.
+ @retval EFI_INVALID_PARAMETER The PayloadBuffer or PayloadTransferSize is NULL and
+ PayloadBufferSize is non-zero.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the security
+ protocol command to execute.
+
+**/
+EFI_STATUS
+EFIAPI
+EmmcSecurityProtocolIn (
+ IN EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *This,
+ IN UINT32 MediaId,
+ IN UINT64 Timeout,
+ IN UINT8 SecurityProtocolId,
+ IN UINT16 SecurityProtocolSpecificData,
+ IN UINTN PayloadBufferSize,
+ OUT VOID *PayloadBuffer,
+ OUT UINTN *PayloadTransferSize
+ )
+{
+ EFI_STATUS Status;
+
+ if ((PayloadTransferSize == NULL) && PayloadBufferSize != 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = EmmcSecurityProtocolInOut (
+ This,
+ MediaId,
+ Timeout,
+ SecurityProtocolId,
+ SecurityProtocolSpecificData,
+ PayloadBufferSize,
+ PayloadBuffer,
+ PayloadTransferSize,
+ TRUE
+ );
+
+ return Status;
+}
+
+/**
+ Send a security protocol command to a device.
+
+ The SendData function sends a security protocol command containing the payload
+ PayloadBuffer to the given MediaId. The security protocol command sent is
+ defined by SecurityProtocolId and contains the security protocol specific data
+ SecurityProtocolSpecificData. If the underlying protocol command requires a
+ specific padding for the command payload, the SendData function shall add padding
+ bytes to the command payload to satisfy the padding requirements.
+
+ For devices supporting the SCSI command set, the security protocol command is sent
+ using the SECURITY PROTOCOL OUT command defined in SPC-4.
+
+ For devices supporting the ATA command set, the security protocol command is sent
+ using one of the TRUSTED SEND commands defined in ATA8-ACS if PayloadBufferSize
+ is non-zero. If the PayloadBufferSize is zero, the security protocol command is
+ sent using the Trusted Non-Data command defined in ATA8-ACS.
+
+ If PayloadBuffer is NULL and PayloadBufferSize is non-zero, the function shall
+ return EFI_INVALID_PARAMETER.
+
+ If the given MediaId does not support security protocol commands, the function
+ shall return EFI_UNSUPPORTED. If there is no media in the device, the function
+ returns EFI_NO_MEDIA. If the MediaId is not the ID for the current media in the
+ device, the function returns EFI_MEDIA_CHANGED.
+
+ If the security protocol fails to complete within the Timeout period, the function
+ shall return EFI_TIMEOUT.
+
+ If the security protocol command completes without an error, the function shall return
+ EFI_SUCCESS. If the security protocol command completes with an error, the function
+ shall return EFI_DEVICE_ERROR.
+
+ @param This Indicates a pointer to the calling context.
+ @param MediaId ID of the medium to receive data from.
+ @param Timeout The timeout, in 100ns units, to use for the execution
+ of the security protocol command. A Timeout value of 0
+ means that this function will wait indefinitely for the
+ security protocol command to execute. If Timeout is greater
+ than zero, then this function will return EFI_TIMEOUT
+ if the time required to execute the receive data command
+ is greater than Timeout.
+ @param SecurityProtocolId The value of the "Security Protocol" parameter of
+ the security protocol command to be sent.
+ @param SecurityProtocolSpecificData The value of the "Security Protocol Specific" parameter
+ of the security protocol command to be sent.
+ @param PayloadBufferSize Size in bytes of the payload data buffer.
+ @param PayloadBuffer A pointer to a destination buffer to store the security
+ protocol command specific payload data for the security
+ protocol command.
+
+ @retval EFI_SUCCESS The security protocol command completed successfully.
+ @retval EFI_UNSUPPORTED The given MediaId does not support security protocol commands.
+ @retval EFI_DEVICE_ERROR The security protocol command completed with an error.
+ @retval EFI_NO_MEDIA There is no media in the device.
+ @retval EFI_MEDIA_CHANGED The MediaId is not for the current media.
+ @retval EFI_INVALID_PARAMETER The PayloadBuffer is NULL and PayloadBufferSize is non-zero.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the security
+ protocol command to execute.
+
+**/
+EFI_STATUS
+EFIAPI
+EmmcSecurityProtocolOut (
+ IN EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *This,
+ IN UINT32 MediaId,
+ IN UINT64 Timeout,
+ IN UINT8 SecurityProtocolId,
+ IN UINT16 SecurityProtocolSpecificData,
+ IN UINTN PayloadBufferSize,
+ IN VOID *PayloadBuffer
+ )
+{
+ EFI_STATUS Status;
+
+ Status = EmmcSecurityProtocolInOut (
+ This,
+ MediaId,
+ Timeout,
+ SecurityProtocolId,
+ SecurityProtocolSpecificData,
+ PayloadBufferSize,
+ PayloadBuffer,
+ NULL,
+ FALSE
+ );
+
+ return Status;
+}
+
|