Commit 92d01b9ee2 for qemu.org

commit 92d01b9ee2bc95771e8b9348b111a10083ff64e2
Author: Stanley Jhu <stanleyjhu@google.com>
Date:   Mon Oct 5 19:03:15 2026 +0800

    hw/ufs: Skip nullified MCQ submission queue entries with OCS_ABORTED

    Per JEDEC UFSHCI (JESD223G Section 6.1.1 and Section 4.4.6 Step 2B),
    host software in MCQ mode may stop a Submission Queue and nullify an
    unfetched SQE by setting its UTRD Command Type (DW0 bits 31:28) to
    0xF. When the Submission Queue is resumed, the host controller shall
    skip execution of the nullified transfer request and post a Completion
    Queue Entry (CQE) to the Completion Queue with OCS = ABORTED (0x06).

    In ufs_mcq_process_sq(), check for UFS_UTP_CMD_TYPE_NULL (0xF), read
    the Request UPIU header on a best-effort basis via
    ufs_dma_read_req_upiu() to populate cqe.task_tag and cqe.lun for
    UFSHCI 4.1+ completion decoding, and unconditionally post a CQE with
    UFS_OCS_ABORTED without executing the command or writing a Response
    UPIU for the nullified entry.

    Signed-off-by: Stanley Jhu <stanleyjhu@google.com>
    Reviewed-by: Jeuk Kim <jeuk20.kim@samsung.com>
    Signed-off-by: Jeuk Kim <jeuk20.kim@samsung.com>

diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c
index 4c505e0bff..f008af7467 100644
--- a/hw/ufs/ufs.c
+++ b/hw/ufs/ufs.c
@@ -36,6 +36,7 @@

 static void ufs_exec_req(UfsRequest *req);
 static void ufs_clear_req(UfsRequest *req);
+static void ufs_mcq_process_cq(void *opaque);

 static inline uint64_t ufs_mcq_reg_addr(UfsHc *u, int qid)
 {
@@ -474,6 +475,17 @@ static void ufs_mcq_process_sq(void *opaque)
         ufs_mcq_init_req(sq->u, req, sq);
         memcpy(&req->utrd, &sqe, sizeof(req->utrd));

+        if ((le32_to_cpu(sqe.header.dword_0) >> 28) == UFS_UTP_CMD_TYPE_NULL) {
+            ufs_dma_read_req_upiu(req);
+            req->utrd.header.dword_2 = cpu_to_le32(
+                (le32_to_cpu(req->utrd.header.dword_2) & ~UFS_MASK_OCS) |
+                UFS_OCS_ABORTED);
+            req->state = UFS_REQUEST_COMPLETE;
+            QTAILQ_INSERT_TAIL(&sq->cq->req_list, req, entry);
+            ufs_mcq_process_cq(sq->cq);
+            continue;
+        }
+
         req->state = UFS_REQUEST_RUNNING;
         ufs_exec_req(req);
     }
@@ -493,7 +505,10 @@ static void ufs_mcq_process_cq(void *opaque)
             break;
         }

-        ufs_dma_write_rsp_upiu(req);
+        if ((le32_to_cpu(req->utrd.header.dword_0) >> 28) !=
+            UFS_UTP_CMD_TYPE_NULL) {
+            ufs_dma_write_rsp_upiu(req);
+        }

         /* UTRD/CQE are LE; round-trip through host to keep BE correct. */
         uint64_t ucdba =
diff --git a/include/block/ufs.h b/include/block/ufs.h
index b0515325f9..4a614fbe52 100644
--- a/include/block/ufs.h
+++ b/include/block/ufs.h
@@ -665,6 +665,7 @@ enum {
     UFS_UTP_CMD_TYPE_SCSI = 0x0,
     UFS_UTP_CMD_TYPE_UFS = 0x1,
     UFS_UTP_CMD_TYPE_DEV_MANAGE = 0x2,
+    UFS_UTP_CMD_TYPE_NULL = 0xf,
 };

 /* To accommodate UFS2.0 required Command type */