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 <[email protected]> --- tests/qtest/ufs-test.c | 290 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 290 insertions(+) diff --git a/tests/qtest/ufs-test.c b/tests/qtest/ufs-test.c index f677896db0..5496276326 100644 --- a/tests/qtest/ufs-test.c +++ b/tests/qtest/ufs-test.c @@ -1386,6 +1386,295 @@ static void *ufs_blk_test_setup(GString *cmd_line, void *arg) return arg; } +/* + * Helper to read a single HID attribute value. + * Returns the host-endian attribute value; asserts OCS success. + */ +static uint32_t ufs_hid_read_attr(QUfs *ufs, uint8_t idn) +{ + enum UtpOcsCodes ocs; + UtpUpiuRsp rsp; + + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_ATTR, + idn, 0, 0, 0, &rsp); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); + return be32_to_cpu(rsp.qr.value); +} + +/* + * Helper to write a single HID attribute value. + * Asserts OCS success. + */ +static void ufs_hid_write_attr(QUfs *ufs, uint8_t idn, uint32_t value) +{ + enum UtpOcsCodes ocs; + UtpUpiuRsp rsp; + + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_WRITE_ATTR, + idn, 0, 0, value, &rsp); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); +} + +static uint32_t ufs_hid_wait_leave_state(QUfs *ufs, uint32_t from) +{ + uint64_t end_time = + g_get_monotonic_time() + TIMEOUT_SECONDS * G_TIME_SPAN_SECOND; + uint32_t state; + + do { + qtest_clock_step(ufs->dev.bus->qts, 100); + state = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_STATE); + } while (state == from && g_get_monotonic_time() < end_time); + + return state; +} + +static uint32_t ufs_hid_wait_state(QUfs *ufs, uint32_t expected) +{ + uint64_t end_time = + g_get_monotonic_time() + TIMEOUT_SECONDS * G_TIME_SPAN_SECOND; + uint32_t state; + + do { + qtest_clock_step(ufs->dev.bus->qts, 100); + state = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_STATE); + } while (state != expected && g_get_monotonic_time() < end_time); + + g_assert_cmpuint(state, ==, expected); + return state; +} + +static void ufs_hid_wait_progress_complete(QUfs *ufs) +{ + uint64_t end_time = + g_get_monotonic_time() + TIMEOUT_SECONDS * G_TIME_SPAN_SECOND; + uint32_t val; + + do { + qtest_clock_step(ufs->dev.bus->qts, 100); + val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_PROG_RATIO); + } while (val != 100 && g_get_monotonic_time() < end_time); + + g_assert_cmpuint(val, ==, 100); +} + +static void ufstest_hid(void *obj, void *data, QGuestAllocator *alloc) +{ + QUfs *ufs = obj; + uint32_t val; + enum UtpOcsCodes ocs; + UtpUpiuRsp rsp; + const int test_lun = 1; + const uint8_t request_sense_cdb[UFS_CDB_SIZE] = { + REQUEST_SENSE, + }; + const uint8_t write_cdb[UFS_CDB_SIZE] = { + /* WRITE(10) to LBA 0, transfer length 8 sectors = 1 fragment */ + WRITE_10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00 + }; + uint8_t write_buf[4096]; + uint8_t read_buf[4096]; + int i; + + ufs_init(ufs, alloc); + + /* Clear Unit Attention */ + ufs_send_scsi_command(ufs, test_lun, request_sense_cdb, NULL, 0, + read_buf, sizeof(read_buf), &rsp); + + /* + * 1. Verify HID default attribute values + */ + val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_DEFRAG_OP); + g_assert_cmpuint(val, ==, UFS_HID_OP_DISABLE); + + val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_AVAIL_SIZE); + g_assert_cmpuint(val, ==, 0xFFFFFFFF); + + val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_SIZE); + g_assert_cmpuint(val, ==, 0xFFFFFFFF); + + val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_PROG_RATIO); + g_assert_cmpuint(val, ==, 0); + + val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_STATE); + g_assert_cmpuint(val, ==, UFS_HID_STATE_IDLE); + + /* + * 2. Verify read-only attributes reject writes + */ + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_WRITE_ATTR, + UFS_QUERY_ATTR_IDN_HID_AVAIL_SIZE, 0, 0, 0x100, + &rsp); + g_assert_cmpuint(ocs, ==, UFS_OCS_INVALID_CMD_TABLE_ATTR); + g_assert_cmpuint(rsp.header.response, ==, + UFS_QUERY_RESULT_NOT_WRITEABLE); + + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_WRITE_ATTR, + UFS_QUERY_ATTR_IDN_HID_PROG_RATIO, 0, 0, 50, + &rsp); + g_assert_cmpuint(ocs, ==, UFS_OCS_INVALID_CMD_TABLE_ATTR); + g_assert_cmpuint(rsp.header.response, ==, + UFS_QUERY_RESULT_NOT_WRITEABLE); + + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_WRITE_ATTR, + UFS_QUERY_ATTR_IDN_HID_STATE, 0, 0, 0x01, &rsp); + g_assert_cmpuint(ocs, ==, UFS_OCS_INVALID_CMD_TABLE_ATTR); + g_assert_cmpuint(rsp.header.response, ==, + UFS_QUERY_RESULT_NOT_WRITEABLE); + + /* + * 3. Verify invalid bDefragOperation value is rejected + */ + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_WRITE_ATTR, + UFS_QUERY_ATTR_IDN_DEFRAG_OP, 0, 0, 0x03, + &rsp); + g_assert_cmpuint(ocs, ==, UFS_OCS_INVALID_CMD_TABLE_ATTR); + g_assert_cmpuint(rsp.header.response, ==, + UFS_QUERY_RESULT_INVALID_VALUE); + + /* + * 4. dHIDSize is writable and readable + */ + ufs_hid_write_attr(ufs, UFS_QUERY_ATTR_IDN_HID_SIZE, 0x100); + val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_SIZE); + g_assert_cmpuint(val, ==, 0x100); + + /* Restore default */ + ufs_hid_write_attr(ufs, UFS_QUERY_ATTR_IDN_HID_SIZE, 0xFFFFFFFF); + + /* + * 5. Analysis with no fragments -> Defrag Not Required, then reading + * bHIDState in a terminal state auto-resets HID to Idle. + */ + ufs_hid_write_attr(ufs, UFS_QUERY_ATTR_IDN_DEFRAG_OP, + UFS_HID_OP_ANALYSIS); + + val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_STATE); + g_assert_cmpuint(val, ==, UFS_HID_STATE_ANALYSIS_IN_PROGRESS); + + /* Idle handler advances analysis; first non-analysis read is decisive */ + val = ufs_hid_wait_leave_state(ufs, UFS_HID_STATE_ANALYSIS_IN_PROGRESS); + g_assert_cmpuint(val, ==, UFS_HID_STATE_DEFRAG_NOT_REQUIRED); + + /* Reading Not Required auto-reset to Idle */ + val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_STATE); + g_assert_cmpuint(val, ==, UFS_HID_STATE_IDLE); + + val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_AVAIL_SIZE); + g_assert_cmpuint(val, ==, 0xFFFFFFFF); + + /* + * 6. Generate fragmentation via SCSI WRITE, then analyze (analysis + * only): the machine settles at Defrag Required. + */ + memset(write_buf, 0xab, sizeof(write_buf)); + for (i = 0; i < 4; i++) { + ocs = ufs_send_scsi_command(ufs, test_lun, write_cdb, write_buf, + sizeof(write_buf), NULL, 0, &rsp); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); + } + + ufs_hid_write_attr(ufs, UFS_QUERY_ATTR_IDN_DEFRAG_OP, + UFS_HID_OP_ANALYSIS); + + val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_STATE); + g_assert_cmpuint(val, ==, UFS_HID_STATE_ANALYSIS_IN_PROGRESS); + + val = ufs_hid_wait_leave_state(ufs, UFS_HID_STATE_ANALYSIS_IN_PROGRESS); + g_assert_cmpuint(val, ==, UFS_HID_STATE_DEFRAG_REQUIRED); + + val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_DEFRAG_OP); + g_assert_cmpuint(val, ==, UFS_HID_OP_DISABLE); + + val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_AVAIL_SIZE); + g_assert_cmpuint(val, >, 0); + + /* + * 7. Disable resets to Idle from any state + */ + ufs_hid_write_attr(ufs, UFS_QUERY_ATTR_IDN_DEFRAG_OP, + UFS_HID_OP_DISABLE); + + val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_STATE); + g_assert_cmpuint(val, ==, UFS_HID_STATE_IDLE); + + /* + * 8. Partial defrag cycle: dHIDSize limits the requested defrag size, + * and reading bHIDProgressRatio at 100% resets the HID attributes. + */ + for (i = 0; i < 4; i++) { + ocs = ufs_send_scsi_command(ufs, test_lun, write_cdb, write_buf, + sizeof(write_buf), NULL, 0, &rsp); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); + } + + ufs_hid_write_attr(ufs, UFS_QUERY_ATTR_IDN_HID_SIZE, 2); + ufs_hid_write_attr(ufs, UFS_QUERY_ATTR_IDN_DEFRAG_OP, + UFS_HID_OP_DEFRAG); + + /* Should start in Analysis In Progress */ + val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_STATE); + g_assert_cmpuint(val, ==, UFS_HID_STATE_ANALYSIS_IN_PROGRESS); + + ufs_hid_wait_progress_complete(ufs); + + val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_AVAIL_SIZE); + g_assert_cmpuint(val, ==, 0xFFFFFFFF); + + val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_DEFRAG_OP); + g_assert_cmpuint(val, ==, UFS_HID_OP_DISABLE); + + val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_PROG_RATIO); + g_assert_cmpuint(val, ==, 0); + + /* + * Re-analyze after the partial defrag. There were 8 fragments before + * defrag, and dHIDSize limited the completed operation to 2. + */ + ufs_hid_write_attr(ufs, UFS_QUERY_ATTR_IDN_DEFRAG_OP, + UFS_HID_OP_ANALYSIS); + + val = ufs_hid_wait_leave_state(ufs, UFS_HID_STATE_ANALYSIS_IN_PROGRESS); + g_assert_cmpuint(val, ==, UFS_HID_STATE_DEFRAG_REQUIRED); + + val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_AVAIL_SIZE); + g_assert_cmpuint(val, ==, 6); + + ufs_hid_write_attr(ufs, UFS_QUERY_ATTR_IDN_DEFRAG_OP, + UFS_HID_OP_DISABLE); + ufs_hid_write_attr(ufs, UFS_QUERY_ATTR_IDN_HID_SIZE, 0xFFFFFFFF); + + /* + * 9. Full defrag cycle: reading bHIDState in Completed state returns + * Completed once and resets HID attributes to Idle/default values. + */ + ufs_hid_write_attr(ufs, UFS_QUERY_ATTR_IDN_DEFRAG_OP, + UFS_HID_OP_DEFRAG); + + val = ufs_hid_wait_state(ufs, UFS_HID_STATE_DEFRAG_COMPLETED); + g_assert_cmpuint(val, ==, UFS_HID_STATE_DEFRAG_COMPLETED); + + val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_STATE); + g_assert_cmpuint(val, ==, UFS_HID_STATE_IDLE); + + val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_DEFRAG_OP); + g_assert_cmpuint(val, ==, UFS_HID_OP_DISABLE); + + val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_AVAIL_SIZE); + g_assert_cmpuint(val, ==, 0xFFFFFFFF); + + ufs_exit(ufs, alloc); +} + static void ufs_register_nodes(void) { const char *arch; @@ -1439,6 +1728,7 @@ static void ufs_register_nodes(void) &io_test_opts); qos_add_test("wb-init", "ufs", ufstest_wb_init, &wb_test_opts); qos_add_test("wb-read-write", "ufs", ufstest_wb_read_write, &wb_test_opts); + qos_add_test("hid", "ufs", ufstest_hid, &io_test_opts); } libqos_init(ufs_register_nodes); -- 2.25.1
