mirror of https://github.com/acidanthera/audk.git
MdeModulePkg/UfsPassThruDxe: Fix unaligned data transfer handling
REF: https://bugzilla.tianocore.org/show_bug.cgi?id=1341 Since UFS specification requires the data buffer specified in PRDT to be DWORD aligned in size we had a code in UfsInitUtpPrdt that aligned the data buffer by rounding down the buffer size to DWORD boundary. This meant that for SCSI commands that wanted to perform unaligned data transfer(such as SENSE command) we specified to small buffer for the data to fit and transfer was aborted. This change introduces code that allocates auxiliary DWORD aligned data buffer for unaligned transfer. Device transfers data to aligned buffer and when data transfer is over driver copies data from aligned buffer to data buffer passed by user. Signed-off-by: Mateusz Albecki <mateusz.albecki@intel.com> Reviewed-by: Hao A Wu <hao.a.wu@intel.com>
This commit is contained in:
parent
ffe048a080
commit
a37e18f6fc
|
@ -92,6 +92,8 @@ typedef struct {
|
||||||
UINT32 CmdDescSize;
|
UINT32 CmdDescSize;
|
||||||
VOID *CmdDescHost;
|
VOID *CmdDescHost;
|
||||||
VOID *CmdDescMapping;
|
VOID *CmdDescMapping;
|
||||||
|
VOID *AlignedDataBuf;
|
||||||
|
UINTN AlignedDataBufSize;
|
||||||
VOID *DataBufMapping;
|
VOID *DataBufMapping;
|
||||||
|
|
||||||
EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet;
|
EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet;
|
||||||
|
|
|
@ -394,17 +394,13 @@ UfsInitUtpPrdt (
|
||||||
UINT8 *Remaining;
|
UINT8 *Remaining;
|
||||||
UINTN PrdtNumber;
|
UINTN PrdtNumber;
|
||||||
|
|
||||||
if ((BufferSize & (BIT0 | BIT1)) != 0) {
|
ASSERT (((UINTN)Buffer & (BIT0 | BIT1)) == 0);
|
||||||
BufferSize &= ~(BIT0 | BIT1);
|
ASSERT ((BufferSize & (BIT1 | BIT0)) == 0);
|
||||||
DEBUG ((DEBUG_WARN, "UfsInitUtpPrdt: The BufferSize [%d] is not dword-aligned!\n", BufferSize));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (BufferSize == 0) {
|
if (BufferSize == 0) {
|
||||||
return EFI_SUCCESS;
|
return EFI_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT (((UINTN)Buffer & (BIT0 | BIT1)) == 0);
|
|
||||||
|
|
||||||
RemainingLen = BufferSize;
|
RemainingLen = BufferSize;
|
||||||
Remaining = Buffer;
|
Remaining = Buffer;
|
||||||
PrdtNumber = (UINTN)DivU64x32 ((UINT64)BufferSize + UFS_MAX_DATA_LEN_PER_PRD - 1, UFS_MAX_DATA_LEN_PER_PRD);
|
PrdtNumber = (UINTN)DivU64x32 ((UINT64)BufferSize + UFS_MAX_DATA_LEN_PER_PRD - 1, UFS_MAX_DATA_LEN_PER_PRD);
|
||||||
|
@ -1317,6 +1313,143 @@ Exit:
|
||||||
return Status;
|
return Status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Cleanup data buffers after data transfer. This function
|
||||||
|
also takes care to copy all data to user memory pool for
|
||||||
|
unaligned data transfers.
|
||||||
|
|
||||||
|
@param[in] Private Pointer to the UFS_PASS_THRU_PRIVATE_DATA
|
||||||
|
@param[in] TransReq Pointer to the transfer request
|
||||||
|
**/
|
||||||
|
VOID
|
||||||
|
UfsReconcileDataTransferBuffer (
|
||||||
|
IN UFS_PASS_THRU_PRIVATE_DATA *Private,
|
||||||
|
IN UFS_PASS_THRU_TRANS_REQ *TransReq
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (TransReq->DataBufMapping != NULL) {
|
||||||
|
Private->UfsHostController->Unmap (
|
||||||
|
Private->UfsHostController,
|
||||||
|
TransReq->DataBufMapping
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Check if unaligned transfer was performed. If it was and we read
|
||||||
|
// data from device copy memory to user data buffers before cleanup.
|
||||||
|
// The assumption is if auxiliary aligned data buffer is not NULL then
|
||||||
|
// unaligned transfer has been performed.
|
||||||
|
//
|
||||||
|
if (TransReq->AlignedDataBuf != NULL) {
|
||||||
|
if (TransReq->Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) {
|
||||||
|
CopyMem (TransReq->Packet->InDataBuffer, TransReq->AlignedDataBuf, TransReq->Packet->InTransferLength);
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// Wipe out the transfer buffer in case it contains sensitive data.
|
||||||
|
//
|
||||||
|
ZeroMem (TransReq->AlignedDataBuf, TransReq->AlignedDataBufSize);
|
||||||
|
FreeAlignedPages (TransReq->AlignedDataBuf, EFI_SIZE_TO_PAGES (TransReq->AlignedDataBufSize));
|
||||||
|
TransReq->AlignedDataBuf = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Prepare data buffer for transfer.
|
||||||
|
|
||||||
|
@param[in] Private Pointer to the UFS_PASS_THRU_PRIVATE_DATA
|
||||||
|
@param[in, out] TransReq Pointer to the transfer request
|
||||||
|
|
||||||
|
@retval EFI_DEVICE_ERROR Failed to prepare buffer for transfer
|
||||||
|
@retval EFI_SUCCESS Buffer ready for transfer
|
||||||
|
**/
|
||||||
|
EFI_STATUS
|
||||||
|
UfsPrepareDataTransferBuffer (
|
||||||
|
IN UFS_PASS_THRU_PRIVATE_DATA *Private,
|
||||||
|
IN OUT UFS_PASS_THRU_TRANS_REQ *TransReq
|
||||||
|
)
|
||||||
|
{
|
||||||
|
EFI_STATUS Status;
|
||||||
|
VOID *DataBuf;
|
||||||
|
UINT32 DataLen;
|
||||||
|
UINTN MapLength;
|
||||||
|
EFI_PHYSICAL_ADDRESS DataBufPhyAddr;
|
||||||
|
EDKII_UFS_HOST_CONTROLLER_OPERATION Flag;
|
||||||
|
UTP_TR_PRD *PrdtBase;
|
||||||
|
|
||||||
|
DataBufPhyAddr = 0;
|
||||||
|
DataBuf = NULL;
|
||||||
|
|
||||||
|
//
|
||||||
|
// For unaligned data transfers we allocate auxiliary DWORD aligned memory pool.
|
||||||
|
// When command is finished auxiliary memory pool is copied into actual user memory.
|
||||||
|
// This is requiered to assure data transfer safety(DWORD alignment required by UFS spec.)
|
||||||
|
//
|
||||||
|
if (TransReq->Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) {
|
||||||
|
if (((UINTN)TransReq->Packet->InDataBuffer % 4 != 0) || (TransReq->Packet->InTransferLength % 4 != 0)) {
|
||||||
|
DataLen = TransReq->Packet->InTransferLength + (4 - (TransReq->Packet->InTransferLength % 4));
|
||||||
|
DataBuf = AllocateAlignedPages (EFI_SIZE_TO_PAGES (DataLen), 4);
|
||||||
|
if (DataBuf == NULL) {
|
||||||
|
return EFI_DEVICE_ERROR;
|
||||||
|
}
|
||||||
|
ZeroMem (DataBuf, DataLen);
|
||||||
|
TransReq->AlignedDataBuf = DataBuf;
|
||||||
|
TransReq->AlignedDataBufSize = DataLen;
|
||||||
|
} else {
|
||||||
|
DataLen = TransReq->Packet->InTransferLength;
|
||||||
|
DataBuf = TransReq->Packet->InDataBuffer;
|
||||||
|
}
|
||||||
|
Flag = EdkiiUfsHcOperationBusMasterWrite;
|
||||||
|
} else {
|
||||||
|
if (((UINTN)TransReq->Packet->OutDataBuffer % 4 != 0) || (TransReq->Packet->OutTransferLength % 4 != 0)) {
|
||||||
|
DataLen = TransReq->Packet->OutTransferLength + (4 - (TransReq->Packet->OutTransferLength % 4));
|
||||||
|
DataBuf = AllocateAlignedPages (EFI_SIZE_TO_PAGES (DataLen), 4);
|
||||||
|
if (DataBuf == NULL) {
|
||||||
|
return EFI_DEVICE_ERROR;
|
||||||
|
}
|
||||||
|
CopyMem (DataBuf, TransReq->Packet->OutDataBuffer, TransReq->Packet->OutTransferLength);
|
||||||
|
TransReq->AlignedDataBuf = DataBuf;
|
||||||
|
TransReq->AlignedDataBufSize = DataLen;
|
||||||
|
} else {
|
||||||
|
DataLen = TransReq->Packet->OutTransferLength;
|
||||||
|
DataBuf = TransReq->Packet->OutDataBuffer;
|
||||||
|
}
|
||||||
|
Flag = EdkiiUfsHcOperationBusMasterRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DataLen != 0) {
|
||||||
|
MapLength = DataLen;
|
||||||
|
Status = Private->UfsHostController->Map (
|
||||||
|
Private->UfsHostController,
|
||||||
|
Flag,
|
||||||
|
DataBuf,
|
||||||
|
&MapLength,
|
||||||
|
&DataBufPhyAddr,
|
||||||
|
&TransReq->DataBufMapping
|
||||||
|
);
|
||||||
|
|
||||||
|
if (EFI_ERROR (Status) || (DataLen != MapLength)) {
|
||||||
|
if (TransReq->AlignedDataBuf != NULL) {
|
||||||
|
//
|
||||||
|
// Wipe out the transfer buffer in case it contains sensitive data.
|
||||||
|
//
|
||||||
|
ZeroMem (TransReq->AlignedDataBuf, TransReq->AlignedDataBufSize);
|
||||||
|
FreeAlignedPages (TransReq->AlignedDataBuf, EFI_SIZE_TO_PAGES (TransReq->AlignedDataBufSize));
|
||||||
|
TransReq->AlignedDataBuf = NULL;
|
||||||
|
}
|
||||||
|
return EFI_DEVICE_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Fill PRDT table of Command UPIU for executed SCSI cmd.
|
||||||
|
//
|
||||||
|
PrdtBase = (UTP_TR_PRD*)((UINT8*)TransReq->CmdDescHost + ROUNDUP8 (sizeof (UTP_COMMAND_UPIU)) + ROUNDUP8 (sizeof (UTP_RESPONSE_UPIU)));
|
||||||
|
ASSERT (PrdtBase != NULL);
|
||||||
|
UfsInitUtpPrdt (PrdtBase, (VOID*)(UINTN)DataBufPhyAddr, DataLen);
|
||||||
|
|
||||||
|
return EFI_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Sends a UFS-supported SCSI Request Packet to a UFS device that is attached to the UFS host controller.
|
Sends a UFS-supported SCSI Request Packet to a UFS device that is attached to the UFS host controller.
|
||||||
|
|
||||||
|
@ -1353,24 +1486,19 @@ UfsExecScsiCmds (
|
||||||
UTP_RESPONSE_UPIU *Response;
|
UTP_RESPONSE_UPIU *Response;
|
||||||
UINT16 SenseDataLen;
|
UINT16 SenseDataLen;
|
||||||
UINT32 ResTranCount;
|
UINT32 ResTranCount;
|
||||||
VOID *DataBuf;
|
|
||||||
EFI_PHYSICAL_ADDRESS DataBufPhyAddr;
|
|
||||||
UINT32 DataLen;
|
|
||||||
UINTN MapLength;
|
|
||||||
EDKII_UFS_HOST_CONTROLLER_PROTOCOL *UfsHc;
|
|
||||||
EDKII_UFS_HOST_CONTROLLER_OPERATION Flag;
|
|
||||||
UTP_TR_PRD *PrdtBase;
|
|
||||||
EFI_TPL OldTpl;
|
EFI_TPL OldTpl;
|
||||||
UFS_PASS_THRU_TRANS_REQ *TransReq;
|
UFS_PASS_THRU_TRANS_REQ *TransReq;
|
||||||
|
EDKII_UFS_HOST_CONTROLLER_PROTOCOL *UfsHc;
|
||||||
|
|
||||||
TransReq = AllocateZeroPool (sizeof (UFS_PASS_THRU_TRANS_REQ));
|
TransReq = AllocateZeroPool (sizeof (UFS_PASS_THRU_TRANS_REQ));
|
||||||
if (TransReq == NULL) {
|
if (TransReq == NULL) {
|
||||||
return EFI_OUT_OF_RESOURCES;
|
return EFI_OUT_OF_RESOURCES;
|
||||||
}
|
}
|
||||||
|
|
||||||
TransReq->Signature = UFS_PASS_THRU_TRANS_REQ_SIG;
|
TransReq->Signature = UFS_PASS_THRU_TRANS_REQ_SIG;
|
||||||
TransReq->TimeoutRemain = Packet->Timeout;
|
TransReq->TimeoutRemain = Packet->Timeout;
|
||||||
DataBufPhyAddr = 0;
|
TransReq->Packet = Packet;
|
||||||
|
|
||||||
UfsHc = Private->UfsHostController;
|
UfsHc = Private->UfsHostController;
|
||||||
//
|
//
|
||||||
// Find out which slot of transfer request list is available.
|
// Find out which slot of transfer request list is available.
|
||||||
|
@ -1399,44 +1527,16 @@ UfsExecScsiCmds (
|
||||||
|
|
||||||
TransReq->CmdDescSize = TransReq->Trd->PrdtO * sizeof (UINT32) + TransReq->Trd->PrdtL * sizeof (UTP_TR_PRD);
|
TransReq->CmdDescSize = TransReq->Trd->PrdtO * sizeof (UINT32) + TransReq->Trd->PrdtL * sizeof (UTP_TR_PRD);
|
||||||
|
|
||||||
if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) {
|
Status = UfsPrepareDataTransferBuffer (Private, TransReq);
|
||||||
DataBuf = Packet->InDataBuffer;
|
if (EFI_ERROR (Status)) {
|
||||||
DataLen = Packet->InTransferLength;
|
goto Exit1;
|
||||||
Flag = EdkiiUfsHcOperationBusMasterWrite;
|
|
||||||
} else {
|
|
||||||
DataBuf = Packet->OutDataBuffer;
|
|
||||||
DataLen = Packet->OutTransferLength;
|
|
||||||
Flag = EdkiiUfsHcOperationBusMasterRead;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DataLen != 0) {
|
|
||||||
MapLength = DataLen;
|
|
||||||
Status = UfsHc->Map (
|
|
||||||
UfsHc,
|
|
||||||
Flag,
|
|
||||||
DataBuf,
|
|
||||||
&MapLength,
|
|
||||||
&DataBufPhyAddr,
|
|
||||||
&TransReq->DataBufMapping
|
|
||||||
);
|
|
||||||
|
|
||||||
if (EFI_ERROR (Status) || (DataLen != MapLength)) {
|
|
||||||
goto Exit1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//
|
|
||||||
// Fill PRDT table of Command UPIU for executed SCSI cmd.
|
|
||||||
//
|
|
||||||
PrdtBase = (UTP_TR_PRD*)((UINT8*)TransReq->CmdDescHost + ROUNDUP8 (sizeof (UTP_COMMAND_UPIU)) + ROUNDUP8 (sizeof (UTP_RESPONSE_UPIU)));
|
|
||||||
ASSERT (PrdtBase != NULL);
|
|
||||||
UfsInitUtpPrdt (PrdtBase, (VOID*)(UINTN)DataBufPhyAddr, DataLen);
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Insert the async SCSI cmd to the Async I/O list
|
// Insert the async SCSI cmd to the Async I/O list
|
||||||
//
|
//
|
||||||
if (Event != NULL) {
|
if (Event != NULL) {
|
||||||
OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
|
OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
|
||||||
TransReq->Packet = Packet;
|
|
||||||
TransReq->CallerEvent = Event;
|
TransReq->CallerEvent = Event;
|
||||||
InsertTailList (&Private->Queue, &TransReq->TransferList);
|
InsertTailList (&Private->Queue, &TransReq->TransferList);
|
||||||
gBS->RestoreTPL (OldTpl);
|
gBS->RestoreTPL (OldTpl);
|
||||||
|
@ -1515,9 +1615,7 @@ Exit:
|
||||||
|
|
||||||
UfsStopExecCmd (Private, TransReq->Slot);
|
UfsStopExecCmd (Private, TransReq->Slot);
|
||||||
|
|
||||||
if (TransReq->DataBufMapping != NULL) {
|
UfsReconcileDataTransferBuffer (Private, TransReq);
|
||||||
UfsHc->Unmap (UfsHc, TransReq->DataBufMapping);
|
|
||||||
}
|
|
||||||
|
|
||||||
Exit1:
|
Exit1:
|
||||||
if (TransReq->CmdDescMapping != NULL) {
|
if (TransReq->CmdDescMapping != NULL) {
|
||||||
|
@ -1532,7 +1630,6 @@ Exit1:
|
||||||
return Status;
|
return Status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Send UIC command.
|
Send UIC command.
|
||||||
|
|
||||||
|
@ -2086,7 +2183,6 @@ UfsControllerStop (
|
||||||
return EFI_SUCCESS;
|
return EFI_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Internal helper function which will signal the caller event and clean up
|
Internal helper function which will signal the caller event and clean up
|
||||||
resources.
|
resources.
|
||||||
|
@ -2118,9 +2214,7 @@ SignalCallerEvent (
|
||||||
|
|
||||||
UfsStopExecCmd (Private, TransReq->Slot);
|
UfsStopExecCmd (Private, TransReq->Slot);
|
||||||
|
|
||||||
if (TransReq->DataBufMapping != NULL) {
|
UfsReconcileDataTransferBuffer (Private, TransReq);
|
||||||
UfsHc->Unmap (UfsHc, TransReq->DataBufMapping);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (TransReq->CmdDescMapping != NULL) {
|
if (TransReq->CmdDescMapping != NULL) {
|
||||||
UfsHc->Unmap (UfsHc, TransReq->CmdDescMapping);
|
UfsHc->Unmap (UfsHc, TransReq->CmdDescMapping);
|
||||||
|
|
Loading…
Reference in New Issue