Commit 0c6b9e77fb for qemu.org

commit 0c6b9e77fbd1ecd3888c6a694349a3cec7ad6256
Author: Stanley Jhu <stanleyjhu@google.com>
Date:   Fri Oct 23 19:38:25 2026 +0800

    hw/ufs: Add ssu-timeout mode for power management recovery testing

    Extend the experimental fault injection property x-fault-inject-mode
    with a new "ssu-timeout" mode to facilitate automated testing of host
    driver power management and Start Stop Unit (SSU) recovery handling.

    When x-fault-inject-mode="ssu-timeout" is set and x-fault-inject-tag
    is armed (e.g., 0xfffffffe for any tag, or a matching tag), the
    controller only intercepts SCSI START_STOP (0x1B) commands sent to
    the UFS Device Well-Known LUN (WLUN) and holds them without queuing
    them to the SCSI layer, while letting non-SSU commands complete
    normally. This triggers the host driver's 10-second SSU command
    timeout and subsequent power management recovery routines (e.g.,
    during suspend-to-idle or freeze transitions), verifying host error
    recovery and link restoration paths.

    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 83d5fc2441..f8da7c9554 100644
--- a/hw/ufs/ufs.c
+++ b/hw/ufs/ufs.c
@@ -862,6 +862,7 @@ static void ufs_fault_inject_sq_cleanup(UfsHc *u, UfsRequest *req)
 static bool ufs_fault_inject_exec_req(UfsRequest *req)
 {
     UfsHc *u = req->hc;
+    uint8_t op;

     if (likely(u->active_fault_inject_tag == UFS_FAULT_INJECT_TAG_NONE) ||
         u->held_req != NULL ||
@@ -869,8 +870,21 @@ static bool ufs_fault_inject_exec_req(UfsRequest *req)
         return false;
     }

+    op = req->req_upiu.sc.cdb[0];
+    if (u->params.fault_inject_mode == UFS_FAULT_INJECT_SSU_TIMEOUT) {
+        if (op == START_STOP &&
+            req->req_upiu.header.lun == UFS_UPIU_UFS_DEVICE_WLUN &&
+            (u->active_fault_inject_tag == UFS_FAULT_INJECT_TAG_ANY ||
+             req->req_upiu.header.task_tag == u->active_fault_inject_tag)) {
+            u->active_fault_inject_tag = req->req_upiu.header.task_tag;
+            u->held_req = req;
+            trace_ufs_fault_inject_hold_req(req->req_upiu.header.task_tag, 0);
+            return true;
+        }
+        return false;
+    }
+
     if (u->active_fault_inject_tag == UFS_FAULT_INJECT_TAG_ANY) {
-        uint8_t op = req->req_upiu.sc.cdb[0];
         uint64_t lba = 0;
         bool is_match = false;

@@ -3226,6 +3240,7 @@ const QEnumLookup UfsFaultInjectMode_lookup = {
         [UFS_FAULT_INJECT_ABORT_FAILED] = "abort-failed",
         [UFS_FAULT_INJECT_IN_TRANSITION] = "in-transition",
         [UFS_FAULT_INJECT_TIMEOUT] = "timeout",
+        [UFS_FAULT_INJECT_SSU_TIMEOUT] = "ssu-timeout",
     },
     .size = UFS_FAULT_INJECT_MODE_MAX,
 };
@@ -3295,7 +3310,7 @@ const PropertyInfo ufs_prop_fault_inject_tag = {
 const PropertyInfo ufs_prop_fault_inject_mode = {
     .type = "UfsFaultInjectMode",
     .description = "Fault injection mode: abort-success, abort-failed, "
-                   "in-transition, timeout",
+                   "in-transition, timeout, ssu-timeout",
     .enum_table = &UfsFaultInjectMode_lookup,
     .get = qdev_propinfo_get_enum,
     .set = qdev_propinfo_set_enum,
diff --git a/hw/ufs/ufs.h b/hw/ufs/ufs.h
index 80bdaa1a2a..7ddc540546 100644
--- a/hw/ufs/ufs.h
+++ b/hw/ufs/ufs.h
@@ -110,12 +110,16 @@ typedef struct UfsLu {
  * UFS_FAULT_INJECT_TIMEOUT:       TMR ABORT_TASK does not complete or clear
  *                                 doorbell, triggering hardware-level TMR
  *                                 timeout.
+ * UFS_FAULT_INJECT_SSU_TIMEOUT:   Intercepts SCSI START_STOP (0x1B) commands
+ *                                 targeting the UFS Device WLUN when
+ *                                 x-fault-inject-tag is armed.
  */
 typedef enum UfsFaultInjectMode {
     UFS_FAULT_INJECT_ABORT_SUCCESS = 0,
     UFS_FAULT_INJECT_ABORT_FAILED,
     UFS_FAULT_INJECT_IN_TRANSITION,
     UFS_FAULT_INJECT_TIMEOUT,
+    UFS_FAULT_INJECT_SSU_TIMEOUT,
     UFS_FAULT_INJECT_MODE_MAX,
 } UfsFaultInjectMode;