tests/qtest: Add UFS HID qtest
Cover the HID attribute permission table, the disable / analysis / defrag state transitions, partial defrag bounded by dHIDSize, and the terminal auto-reset behaviors. Signed-off-by: Keoseong Park <keosung.park@samsung.com> Reviewed-by: Jeuk Kim <jeuk20.kim@samsung.com> Signed-off-by: Jeuk Kim <jeuk20.kim@samsung.com>
Keoseong Park committed
Jun 9, 2026 at 13:12 UTC
6b1215d82f7cd8a0b562a02a1473aa6714759eed
1 file changed
+275
tests/qtest/ufs-test.c
+275
@@ -1386,6 +1386,280 @@ static void *ufs_blk_test_setup(GString *cmd_line, void *arg)
1386
return arg;
1387
}
1388
1389
+/*
1390
+ * Helper to read a single HID attribute value.
1391
+ * Returns the host-endian attribute value; asserts OCS success.
1392
+ */
1393
+static uint32_t ufs_hid_read_attr(QUfs *ufs, uint8_t idn)
1394
+{
1395
+ enum UtpOcsCodes ocs;
1396
+ UtpUpiuRsp rsp;
1397
+
1398
+ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST,
1399
+ UFS_UPIU_QUERY_OPCODE_READ_ATTR, idn, 0, 0, 0, &rsp);
1400
+ g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
1401
+ g_assert_cmpuint(rsp.header.response, ==, UFS_COMMAND_RESULT_SUCCESS);
1402
+ return be32_to_cpu(rsp.qr.value);
1403
+}
1404
+
1405
+/*
1406
+ * Helper to write a single HID attribute value.
1407
+ * Asserts OCS success.
1408
+ */
1409
+static void ufs_hid_write_attr(QUfs *ufs, uint8_t idn, uint32_t value)
1410
+{
1411
+ enum UtpOcsCodes ocs;
1412
+ UtpUpiuRsp rsp;
1413
+
1414
+ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST,
1415
+ UFS_UPIU_QUERY_OPCODE_WRITE_ATTR, idn, 0, 0, value,
1416
+ &rsp);
1417
+ g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
1418
+ g_assert_cmpuint(rsp.header.response, ==, UFS_COMMAND_RESULT_SUCCESS);
1419
+}
1420
+
1421
+static uint32_t ufs_hid_wait_leave_state(QUfs *ufs, uint32_t from)
1422
+{
1423
+ uint64_t end_time =
1424
+ g_get_monotonic_time() + TIMEOUT_SECONDS * G_TIME_SPAN_SECOND;
1425
+ uint32_t state;
1426
+
1427
+ do {
1428
+ qtest_clock_step(ufs->dev.bus->qts, 100);
1429
+ state = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_STATE);
1430
+ } while (state == from && g_get_monotonic_time() < end_time);
1431
+
1432
+ return state;
1433
+}
1434
+
1435
+static uint32_t ufs_hid_wait_state(QUfs *ufs, uint32_t expected)
1436
+{
1437
+ uint64_t end_time =
1438
+ g_get_monotonic_time() + TIMEOUT_SECONDS * G_TIME_SPAN_SECOND;
1439
+ uint32_t state;
1440
+
1441
+ do {
1442
+ qtest_clock_step(ufs->dev.bus->qts, 100);
1443
+ state = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_STATE);
1444
+ } while (state != expected && g_get_monotonic_time() < end_time);
1445
+
1446
+ g_assert_cmpuint(state, ==, expected);
1447
+ return state;
1448
+}
1449
+
1450
+static void ufs_hid_wait_progress_complete(QUfs *ufs)
1451
+{
1452
+ uint64_t end_time =
1453
+ g_get_monotonic_time() + TIMEOUT_SECONDS * G_TIME_SPAN_SECOND;
1454
+ uint32_t val;
1455
+
1456
+ do {
1457
+ qtest_clock_step(ufs->dev.bus->qts, 100);
1458
+ val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_PROG_RATIO);
1459
+ } while (val != 100 && g_get_monotonic_time() < end_time);
1460
+
1461
+ g_assert_cmpuint(val, ==, 100);
1462
+}
1463
+
1464
+static void ufstest_hid(void *obj, void *data, QGuestAllocator *alloc)
1465
+{
1466
+ QUfs *ufs = obj;
1467
+ uint32_t val;
1468
+ enum UtpOcsCodes ocs;
1469
+ UtpUpiuRsp rsp;
1470
+ const int test_lun = 1;
1471
+ const uint8_t request_sense_cdb[UFS_CDB_SIZE] = {
1472
+ REQUEST_SENSE,
1473
+ };
1474
+ const uint8_t write_cdb[UFS_CDB_SIZE] = {
1475
+ /* WRITE(10) to LBA 0, transfer length 8 sectors = 1 fragment */
1476
+ WRITE_10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00
1477
+ };
1478
+ uint8_t write_buf[4096];
1479
+ uint8_t read_buf[4096];
1480
+ int i;
1481
+
1482
+ ufs_init(ufs, alloc);
1483
+
1484
+ /* Clear Unit Attention */
1485
+ ufs_send_scsi_command(ufs, test_lun, request_sense_cdb, NULL, 0, read_buf,
1486
+ sizeof(read_buf), &rsp);
1487
+
1488
+ /*
1489
+ * 1. Verify HID default attribute values
1490
+ */
1491
+ val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_DEFRAG_OP);
1492
+ g_assert_cmpuint(val, ==, UFS_HID_OP_DISABLE);
1493
+
1494
+ val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_AVAIL_SIZE);
1495
+ g_assert_cmpuint(val, ==, 0xFFFFFFFF);
1496
+
1497
+ val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_SIZE);
1498
+ g_assert_cmpuint(val, ==, 0xFFFFFFFF);
1499
+
1500
+ val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_PROG_RATIO);
1501
+ g_assert_cmpuint(val, ==, 0);
1502
+
1503
+ val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_STATE);
1504
+ g_assert_cmpuint(val, ==, UFS_HID_STATE_IDLE);
1505
+
1506
+ /*
1507
+ * 2. Verify read-only attributes reject writes
1508
+ */
1509
+ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST,
1510
+ UFS_UPIU_QUERY_OPCODE_WRITE_ATTR,
1511
+ UFS_QUERY_ATTR_IDN_HID_AVAIL_SIZE, 0, 0, 0x100, &rsp);
1512
+ g_assert_cmpuint(ocs, ==, UFS_OCS_INVALID_CMD_TABLE_ATTR);
1513
+ g_assert_cmpuint(rsp.header.response, ==, UFS_QUERY_RESULT_NOT_WRITEABLE);
1514
+
1515
+ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST,
1516
+ UFS_UPIU_QUERY_OPCODE_WRITE_ATTR,
1517
+ UFS_QUERY_ATTR_IDN_HID_PROG_RATIO, 0, 0, 50, &rsp);
1518
+ g_assert_cmpuint(ocs, ==, UFS_OCS_INVALID_CMD_TABLE_ATTR);
1519
+ g_assert_cmpuint(rsp.header.response, ==, UFS_QUERY_RESULT_NOT_WRITEABLE);
1520
+
1521
+ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST,
1522
+ UFS_UPIU_QUERY_OPCODE_WRITE_ATTR,
1523
+ UFS_QUERY_ATTR_IDN_HID_STATE, 0, 0, 0x01, &rsp);
1524
+ g_assert_cmpuint(ocs, ==, UFS_OCS_INVALID_CMD_TABLE_ATTR);
1525
+ g_assert_cmpuint(rsp.header.response, ==, UFS_QUERY_RESULT_NOT_WRITEABLE);
1526
+
1527
+ /*
1528
+ * 3. Verify invalid bDefragOperation value is rejected
1529
+ */
1530
+ ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST,
1531
+ UFS_UPIU_QUERY_OPCODE_WRITE_ATTR,
1532
+ UFS_QUERY_ATTR_IDN_DEFRAG_OP, 0, 0, 0x03, &rsp);
1533
+ g_assert_cmpuint(ocs, ==, UFS_OCS_INVALID_CMD_TABLE_ATTR);
1534
+ g_assert_cmpuint(rsp.header.response, ==, UFS_QUERY_RESULT_INVALID_VALUE);
1535
+
1536
+ /*
1537
+ * 4. dHIDSize is writable and readable
1538
+ */
1539
+ ufs_hid_write_attr(ufs, UFS_QUERY_ATTR_IDN_HID_SIZE, 0x100);
1540
+ val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_SIZE);
1541
+ g_assert_cmpuint(val, ==, 0x100);
1542
+
1543
+ /* Restore default */
1544
+ ufs_hid_write_attr(ufs, UFS_QUERY_ATTR_IDN_HID_SIZE, 0xFFFFFFFF);
1545
+
1546
+ /*
1547
+ * 5. Analysis with no fragments -> Defrag Not Required, then reading
1548
+ * bHIDState in a terminal state auto-resets HID to Idle.
1549
+ */
1550
+ ufs_hid_write_attr(ufs, UFS_QUERY_ATTR_IDN_DEFRAG_OP, UFS_HID_OP_ANALYSIS);
1551
+
1552
+ val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_STATE);
1553
+ g_assert_cmpuint(val, ==, UFS_HID_STATE_ANALYSIS_IN_PROGRESS);
1554
+
1555
+ /* Idle handler advances analysis; first non-analysis read is decisive */
1556
+ val = ufs_hid_wait_leave_state(ufs, UFS_HID_STATE_ANALYSIS_IN_PROGRESS);
1557
+ g_assert_cmpuint(val, ==, UFS_HID_STATE_DEFRAG_NOT_REQUIRED);
1558
+
1559
+ /* Reading Not Required auto-reset to Idle */
1560
+ val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_STATE);
1561
+ g_assert_cmpuint(val, ==, UFS_HID_STATE_IDLE);
1562
+
1563
+ val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_AVAIL_SIZE);
1564
+ g_assert_cmpuint(val, ==, 0xFFFFFFFF);
1565
+
1566
+ /*
1567
+ * 6. Generate fragmentation via SCSI WRITE, then analyze (analysis
1568
+ * only): the machine settles at Defrag Required.
1569
+ */
1570
+ memset(write_buf, 0xab, sizeof(write_buf));
1571
+ for (i = 0; i < 4; i++) {
1572
+ ocs = ufs_send_scsi_command(ufs, test_lun, write_cdb, write_buf,
1573
+ sizeof(write_buf), NULL, 0, &rsp);
1574
+ g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
1575
+ }
1576
+
1577
+ ufs_hid_write_attr(ufs, UFS_QUERY_ATTR_IDN_DEFRAG_OP, UFS_HID_OP_ANALYSIS);
1578
+
1579
+ val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_STATE);
1580
+ g_assert_cmpuint(val, ==, UFS_HID_STATE_ANALYSIS_IN_PROGRESS);
1581
+
1582
+ val = ufs_hid_wait_leave_state(ufs, UFS_HID_STATE_ANALYSIS_IN_PROGRESS);
1583
+ g_assert_cmpuint(val, ==, UFS_HID_STATE_DEFRAG_REQUIRED);
1584
+
1585
+ val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_DEFRAG_OP);
1586
+ g_assert_cmpuint(val, ==, UFS_HID_OP_DISABLE);
1587
+
1588
+ val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_AVAIL_SIZE);
1589
+ g_assert_cmpuint(val, >, 0);
1590
+
1591
+ /*
1592
+ * 7. Disable resets to Idle from any state
1593
+ */
1594
+ ufs_hid_write_attr(ufs, UFS_QUERY_ATTR_IDN_DEFRAG_OP, UFS_HID_OP_DISABLE);
1595
+
1596
+ val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_STATE);
1597
+ g_assert_cmpuint(val, ==, UFS_HID_STATE_IDLE);
1598
+
1599
+ /*
1600
+ * 8. Partial defrag cycle: dHIDSize limits the requested defrag size,
1601
+ * and reading bHIDProgressRatio at 100% resets the HID attributes.
1602
+ */
1603
+ for (i = 0; i < 4; i++) {
1604
+ ocs = ufs_send_scsi_command(ufs, test_lun, write_cdb, write_buf,
1605
+ sizeof(write_buf), NULL, 0, &rsp);
1606
+ g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
1607
+ }
1608
+
1609
+ ufs_hid_write_attr(ufs, UFS_QUERY_ATTR_IDN_HID_SIZE, 2);
1610
+ ufs_hid_write_attr(ufs, UFS_QUERY_ATTR_IDN_DEFRAG_OP, UFS_HID_OP_DEFRAG);
1611
+
1612
+ /* Should start in Analysis In Progress */
1613
+ val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_STATE);
1614
+ g_assert_cmpuint(val, ==, UFS_HID_STATE_ANALYSIS_IN_PROGRESS);
1615
+
1616
+ ufs_hid_wait_progress_complete(ufs);
1617
+
1618
+ val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_AVAIL_SIZE);
1619
+ g_assert_cmpuint(val, ==, 0xFFFFFFFF);
1620
+
1621
+ val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_DEFRAG_OP);
1622
+ g_assert_cmpuint(val, ==, UFS_HID_OP_DISABLE);
1623
+
1624
+ val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_PROG_RATIO);
1625
+ g_assert_cmpuint(val, ==, 0);
1626
+
1627
+ /*
1628
+ * Re-analyze after the partial defrag. There were 8 fragments before
1629
+ * defrag, and dHIDSize limited the completed operation to 2.
1630
+ */
1631
+ ufs_hid_write_attr(ufs, UFS_QUERY_ATTR_IDN_DEFRAG_OP, UFS_HID_OP_ANALYSIS);
1632
+
1633
+ val = ufs_hid_wait_leave_state(ufs, UFS_HID_STATE_ANALYSIS_IN_PROGRESS);
1634
+ g_assert_cmpuint(val, ==, UFS_HID_STATE_DEFRAG_REQUIRED);
1635
+
1636
+ val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_AVAIL_SIZE);
1637
+ g_assert_cmpuint(val, ==, 6);
1638
+
1639
+ ufs_hid_write_attr(ufs, UFS_QUERY_ATTR_IDN_DEFRAG_OP, UFS_HID_OP_DISABLE);
1640
+ ufs_hid_write_attr(ufs, UFS_QUERY_ATTR_IDN_HID_SIZE, 0xFFFFFFFF);
1641
+
1642
+ /*
1643
+ * 9. Full defrag cycle: reading bHIDState in Completed state returns
1644
+ * Completed once and resets HID attributes to Idle/default values.
1645
+ */
1646
+ ufs_hid_write_attr(ufs, UFS_QUERY_ATTR_IDN_DEFRAG_OP, UFS_HID_OP_DEFRAG);
1647
+
1648
+ val = ufs_hid_wait_state(ufs, UFS_HID_STATE_DEFRAG_COMPLETED);
1649
+ g_assert_cmpuint(val, ==, UFS_HID_STATE_DEFRAG_COMPLETED);
1650
+
1651
+ val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_STATE);
1652
+ g_assert_cmpuint(val, ==, UFS_HID_STATE_IDLE);
1653
+
1654
+ val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_DEFRAG_OP);
1655
+ g_assert_cmpuint(val, ==, UFS_HID_OP_DISABLE);
1656
+
1657
+ val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_AVAIL_SIZE);
1658
+ g_assert_cmpuint(val, ==, 0xFFFFFFFF);
1659
+
1660
+ ufs_exit(ufs, alloc);
1661
+}
1662
+
1663
static void ufs_register_nodes(void)
1664
{
1665
const char *arch;
@@ -1439,6 +1713,7 @@ static void ufs_register_nodes(void)
1713
&io_test_opts);
1714
qos_add_test("wb-init", "ufs", ufstest_wb_init, &wb_test_opts);
1715
qos_add_test("wb-read-write", "ufs", ufstest_wb_read_write, &wb_test_opts);
1716
+ qos_add_test("hid", "ufs", ufstest_hid, &io_test_opts);
1717
}
1718
1719
libqos_init(ufs_register_nodes);