[edk2-devel] [PATCH] MdeModulePkg/UfsPassThruDxe: Fix unaligned data transfer handling

Albecki, Mateusz posted 1 patch 4 years, 9 months ago
Failed in applying to current master (apply log)
There is a newer version of this series
MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThru.h  |   2 +
.../Bus/Ufs/UfsPassThruDxe/UfsPassThruHci.c        | 194 +++++++++++++++------
2 files changed, 142 insertions(+), 54 deletions(-)
[edk2-devel] [PATCH] MdeModulePkg/UfsPassThruDxe: Fix unaligned data transfer handling
Posted by Albecki, Mateusz 4 years, 9 months ago
REF: https://bugzilla.tianocore.org/show_bug.cgi?id=1341

Since UFS specification requirs 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.

Cc: Hao A Wu <hao.a.wu@intel.com>
Signed-off-by: Mateusz Albecki <mateusz.albecki@intel.com>
---
 MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThru.h  |   2 +
 .../Bus/Ufs/UfsPassThruDxe/UfsPassThruHci.c        | 194 +++++++++++++++------
 2 files changed, 142 insertions(+), 54 deletions(-)

diff --git a/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThru.h b/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThru.h
index 6c98b3a76a..9b68db5ffe 100644
--- a/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThru.h
+++ b/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThru.h
@@ -92,6 +92,8 @@ typedef struct {
   UINT32                                        CmdDescSize;
   VOID                                          *CmdDescHost;
   VOID                                          *CmdDescMapping;
+  VOID                                          *AlignedDataBuf;
+  UINTN                                         AlignedDataBufSize;
   VOID                                          *DataBufMapping;
 
   EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET    *Packet;
diff --git a/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruHci.c b/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruHci.c
index 4b93821f38..6eb50f09ac 100644
--- a/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruHci.c
+++ b/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruHci.c
@@ -394,17 +394,13 @@ UfsInitUtpPrdt (
   UINT8      *Remaining;
   UINTN      PrdtNumber;
 
-  if ((BufferSize & (BIT0 | BIT1)) != 0) {
-    BufferSize &= ~(BIT0 | BIT1);
-    DEBUG ((DEBUG_WARN, "UfsInitUtpPrdt: The BufferSize [%d] is not dword-aligned!\n", BufferSize));
-  }
+  ASSERT (((UINTN)Buffer & (BIT0 | BIT1)) == 0);
+  ASSERT ((BufferSize & (BIT1 | BIT0)) == 0);
 
   if (BufferSize == 0) {
     return EFI_SUCCESS;
   }
 
-  ASSERT (((UINTN)Buffer & (BIT0 | BIT1)) == 0);
-
   RemainingLen = BufferSize;
   Remaining    = Buffer;
   PrdtNumber   = (UINTN)DivU64x32 ((UINT64)BufferSize + UFS_MAX_DATA_LEN_PER_PRD - 1, UFS_MAX_DATA_LEN_PER_PRD);
@@ -1317,6 +1313,135 @@ Exit:
   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);
+    }
+    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 = (EFI_PHYSICAL_ADDRESS) NULL;
+  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) {
+        FreePages (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.
 
@@ -1353,24 +1478,19 @@ UfsExecScsiCmds (
   UTP_RESPONSE_UPIU                    *Response;
   UINT16                               SenseDataLen;
   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;
   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) {
     return EFI_OUT_OF_RESOURCES;
   }
 
   TransReq->Signature     = UFS_PASS_THRU_TRANS_REQ_SIG;
   TransReq->TimeoutRemain = Packet->Timeout;
-  DataBufPhyAddr = 0;
+  TransReq->Packet        = Packet;
+
   UfsHc          = Private->UfsHostController;
   //
   // Find out which slot of transfer request list is available.
@@ -1399,44 +1519,16 @@ UfsExecScsiCmds (
 
   TransReq->CmdDescSize = TransReq->Trd->PrdtO * sizeof (UINT32) + TransReq->Trd->PrdtL * sizeof (UTP_TR_PRD);
 
-  if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) {
-    DataBuf       = Packet->InDataBuffer;
-    DataLen       = Packet->InTransferLength;
-    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;
-    }
+  Status = UfsPrepareDataTransferBuffer (Private, TransReq);
+  if (EFI_ERROR (Status)) {
+    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
   //
   if (Event != NULL) {
     OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
-    TransReq->Packet      = Packet;
     TransReq->CallerEvent = Event;
     InsertTailList (&Private->Queue, &TransReq->TransferList);
     gBS->RestoreTPL (OldTpl);
@@ -1515,9 +1607,7 @@ Exit:
 
   UfsStopExecCmd (Private, TransReq->Slot);
 
-  if (TransReq->DataBufMapping != NULL) {
-    UfsHc->Unmap (UfsHc, TransReq->DataBufMapping);
-  }
+  UfsReconcileDataTransferBuffer (Private, TransReq);
 
 Exit1:
   if (TransReq->CmdDescMapping != NULL) {
@@ -1532,7 +1622,6 @@ Exit1:
   return Status;
 }
 
-
 /**
   Sent UIC DME_LINKSTARTUP command to start the link startup procedure.
 
@@ -2100,7 +2189,6 @@ UfsControllerStop (
   return EFI_SUCCESS;
 }
 
-
 /**
   Internal helper function which will signal the caller event and clean up
   resources.
@@ -2132,9 +2220,7 @@ SignalCallerEvent (
 
   UfsStopExecCmd (Private, TransReq->Slot);
 
-  if (TransReq->DataBufMapping != NULL) {
-    UfsHc->Unmap (UfsHc, TransReq->DataBufMapping);
-  }
+  UfsReconcileDataTransferBuffer (Private, TransReq);
 
   if (TransReq->CmdDescMapping != NULL) {
     UfsHc->Unmap (UfsHc, TransReq->CmdDescMapping);
-- 
2.14.1.windows.1

--------------------------------------------------------------------

Intel Technology Poland sp. z o.o.
ul. Slowackiego 173 | 80-298 Gdansk | Sad Rejonowy Gdansk Polnoc | VII Wydzial Gospodarczy Krajowego Rejestru Sadowego - KRS 101882 | NIP 957-07-52-316 | Kapital zakladowy 200.000 PLN.

Ta wiadomosc wraz z zalacznikami jest przeznaczona dla okreslonego adresata i moze zawierac informacje poufne. W razie przypadkowego otrzymania tej wiadomosci, prosimy o powiadomienie nadawcy oraz trwale jej usuniecie; jakiekolwiek
przegladanie lub rozpowszechnianie jest zabronione.
This e-mail and any attachments may contain confidential material for the sole use of the intended recipient(s). If you are not the intended recipient, please contact the sender and delete all copies; any review or distribution by
others is strictly prohibited.


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.

View/Reply Online (#42705): https://edk2.groups.io/g/devel/message/42705
Mute This Topic: https://groups.io/mt/32159485/1787277
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub  [importer@patchew.org]
-=-=-=-=-=-=-=-=-=-=-=-