@samitouri / QOSamiQemu / commits / 6b1215d82f

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);