mirror of
https://github.com/qemu/qemu.git
synced 2026-07-08 17:46:17 +00:00
Merge tag 'pull-ufs-20260612' of https://gitlab.com/jeuk20.kim/qemu into staging
ufs queue # -----BEGIN PGP SIGNATURE----- # # iQIzBAABCgAdFiEEUBfYMVl8eKPZB+73EuIgTA5dtgIFAmorUfAACgkQEuIgTA5d # tgIQWA/+MkEs+rKdSTQbFOgR4LoK8YEDO0M1cxF3x8NVcWYxwyNrotOk+4dRiSWH # zy+PaiCmibHgqgtluPkFc1Zzo6iFD/5h6/wb3yJiNfiOTiCbYIzLrIEn/LUqwXtp # YKNBEKaQSnWFjYF0oRGOeblMQ+KOTmna7vR/zsoLx7S3N9IC0tJB/AUIXBcpW2ur # d7Qg+YTl/mGlOYQcZ3njBAjf0k2LVuG1TUuMAyBfEcz2fjC6fgR2plKURRjuEL3R # aGENupphIomHTxIJUZIw+PyOCJ0sdV4/rwUbQGcM5hQni+6dny8ywAZj7obXmAGJ # esRP2WVV3GVRyNNKUqlEuiD+ReUKJHkmCpzGYcaI41YKC097GEMsJzdk/KpiU5Gl # e4L6KMvDuwk8HXM5cfK+hvBz279vY30l9+s3Oa3ConZdQC4wXN9OJBNJkboP23HL # +yLsiSxYaBIbA8/RNNOZmI8VauAMuMmlTG0ukZsVvpk5FUOVeo9c4sDf+2bLrOyG # YTCTL9fqrWZZCWLXava69+rGQTrPmAA8UvKO5unGz4Wf1yZyYhDD4SWnbFye0U+V # FCqNXvtg7IAV0spxIhGx0D3Ypdb1AbROEXI4kU7kdiNgsie53HsENLCY/fCv6PY+ # HNxGCx9HpadKVG+pm8lEJgFH/R7vf4qWB9K/TZ08KFr2txXjUwg= # =NmLP # -----END PGP SIGNATURE----- # gpg: Signature made Thu 11 Jun 2026 20:25:20 EDT # gpg: using RSA key 5017D831597C78A3D907EEF712E2204C0E5DB602 # gpg: Good signature from "Jeuk Kim <jeuk20.kim@samsung.com>" [unknown] # gpg: aka "Jeuk Kim <jeuk20.kim@gmail.com>" [unknown] # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: 5017 D831 597C 78A3 D907 EEF7 12E2 204C 0E5D B602 * tag 'pull-ufs-20260612' of https://gitlab.com/jeuk20.kim/qemu: tests/qtest: Add UFS HID qtest hw/ufs: Add Host Initiated Defragmentation (HID) support Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
16
hw/ufs/lu.c
16
hw/ufs/lu.c
@@ -154,15 +154,29 @@ static void ufs_wb_process_write_req(UfsRequest *req, uint32_t transfered_len)
|
||||
ufs_wb_update_avail_buffer(u);
|
||||
}
|
||||
|
||||
#define UFS_HID_MAX_FRAGMENTS 1024
|
||||
static void ufs_hid_count_fragments(UfsRequest *req)
|
||||
{
|
||||
UfsHc *u = req->hc;
|
||||
|
||||
if (!ufs_is_write_req(req)) {
|
||||
return;
|
||||
}
|
||||
|
||||
u->hid_fragment_count =
|
||||
MIN(u->hid_fragment_count + 1, UFS_HID_MAX_FRAGMENTS);
|
||||
}
|
||||
|
||||
static void ufs_scsi_command_complete(SCSIRequest *scsi_req, size_t resid)
|
||||
{
|
||||
UfsRequest *req = scsi_req->hba_private;
|
||||
int16_t status = scsi_req->status;
|
||||
uint32_t transfered_len = scsi_req->cmd.xfer - resid;
|
||||
|
||||
/* WB accounting should only happen for successful commands */
|
||||
/* WB / HID accounting should only happen for successful commands */
|
||||
if (status == GOOD) {
|
||||
ufs_wb_process_write_req(req, transfered_len);
|
||||
ufs_hid_count_fragments(req);
|
||||
}
|
||||
|
||||
ufs_build_scsi_response_upiu(req, scsi_req->sense, scsi_req->sense_len,
|
||||
|
||||
@@ -51,3 +51,7 @@ ufs_err_mcq_create_cq_already_exists(uint8_t qid) "mcq cqid %"PRIu8 "already exi
|
||||
ufs_err_mcq_delete_cq_invalid_cqid(uint8_t qid) "invalid mcq cqid %"PRIu8""
|
||||
ufs_err_mcq_delete_cq_not_exists(uint8_t qid) "mcq cqid %"PRIu8 "not exists"
|
||||
ufs_err_mcq_delete_cq_sq_not_deleted(uint8_t sqid, uint8_t cqid) "mcq sq %"PRIu8" still has cq %"PRIu8""
|
||||
|
||||
# HID (Host Initiated Defragmentation)
|
||||
ufs_hid_defrag_operation(uint8_t op, uint8_t state) "HID defrag operation 0x%"PRIx8", new state 0x%"PRIx8""
|
||||
ufs_hid_defrag_progress(uint32_t remaining, uint8_t progress) "HID defrag remaining %"PRIu32", progress %"PRIu8"%%"
|
||||
|
||||
154
hw/ufs/ufs.c
154
hw/ufs/ufs.c
@@ -40,6 +40,9 @@
|
||||
#define UFS_TOO_HIGH_TEMP_BOUNDARY 160
|
||||
#define UFS_TOO_LOW_TEMP_BOUNDARY 60
|
||||
|
||||
#define UFS_HID_DEFRAG_BATCH_DIV 10 /* ~10% of remaining per tick */
|
||||
#define UFS_HID_PROGRESS_COMPLETE 100
|
||||
|
||||
static void ufs_exec_req(UfsRequest *req);
|
||||
static void ufs_clear_req(UfsRequest *req);
|
||||
|
||||
@@ -1288,10 +1291,9 @@ static const int attr_permission[UFS_QUERY_ATTR_IDN_COUNT] = {
|
||||
[UFS_QUERY_ATTR_IDN_REFRESH_UNIT] = UFS_QUERY_ATTR_READ,
|
||||
[UFS_QUERY_ATTR_IDN_TIMESTAMP] = UFS_QUERY_ATTR_WRITE,
|
||||
[UFS_QUERY_ATTR_IDN_DEVICE_LEVEL_EXCEPTION_ID] = UFS_QUERY_ATTR_READ,
|
||||
/* host initiated defragmentation is not supported */
|
||||
[UFS_QUERY_ATTR_IDN_DEFRAG_OP] = UFS_QUERY_ATTR_READ,
|
||||
[UFS_QUERY_ATTR_IDN_DEFRAG_OP] = UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE,
|
||||
[UFS_QUERY_ATTR_IDN_HID_AVAIL_SIZE] = UFS_QUERY_ATTR_READ,
|
||||
[UFS_QUERY_ATTR_IDN_HID_SIZE] = UFS_QUERY_ATTR_READ,
|
||||
[UFS_QUERY_ATTR_IDN_HID_SIZE] = UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE,
|
||||
[UFS_QUERY_ATTR_IDN_HID_PROG_RATIO] = UFS_QUERY_ATTR_READ,
|
||||
[UFS_QUERY_ATTR_IDN_HID_STATE] = UFS_QUERY_ATTR_READ,
|
||||
[UFS_QUERY_ATTR_IDN_WB_BUFF_RESIZE_HINT] = UFS_QUERY_ATTR_READ,
|
||||
@@ -1371,8 +1373,31 @@ static inline uint32_t ufs_wb_read_resize_status(UfsHc *u)
|
||||
return value;
|
||||
}
|
||||
|
||||
static void ufs_hid_reset(UfsHc *u)
|
||||
{
|
||||
u->attributes.defrag_op = UFS_HID_OP_DISABLE;
|
||||
u->attributes.hid_state = UFS_HID_STATE_IDLE;
|
||||
u->attributes.hid_prog_ratio = 0;
|
||||
u->attributes.hid_avail_size = cpu_to_be32(0xFFFFFFFF);
|
||||
u->hid_defrag_total = 0;
|
||||
u->hid_defrag_remaining = 0;
|
||||
}
|
||||
|
||||
static uint32_t ufs_hid_read_progress_ratio(UfsHc *u)
|
||||
{
|
||||
uint32_t value = u->attributes.hid_prog_ratio;
|
||||
|
||||
if (value == UFS_HID_PROGRESS_COMPLETE) {
|
||||
ufs_hid_reset(u);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static uint32_t ufs_read_attr_value(UfsHc *u, uint8_t idn)
|
||||
{
|
||||
uint8_t state;
|
||||
|
||||
switch (idn) {
|
||||
case UFS_QUERY_ATTR_IDN_BOOT_LU_EN:
|
||||
return u->attributes.boot_lun_en;
|
||||
@@ -1451,9 +1476,16 @@ static uint32_t ufs_read_attr_value(UfsHc *u, uint8_t idn)
|
||||
case UFS_QUERY_ATTR_IDN_HID_SIZE:
|
||||
return be32_to_cpu(u->attributes.hid_size);
|
||||
case UFS_QUERY_ATTR_IDN_HID_PROG_RATIO:
|
||||
return u->attributes.hid_prog_ratio;
|
||||
return ufs_hid_read_progress_ratio(u);
|
||||
case UFS_QUERY_ATTR_IDN_HID_STATE:
|
||||
return u->attributes.hid_state;
|
||||
state = u->attributes.hid_state;
|
||||
|
||||
if (state == UFS_HID_STATE_DEFRAG_COMPLETED ||
|
||||
state == UFS_HID_STATE_DEFRAG_NOT_REQUIRED) {
|
||||
ufs_hid_reset(u);
|
||||
}
|
||||
|
||||
return state;
|
||||
case UFS_QUERY_ATTR_IDN_WB_BUFF_RESIZE_HINT:
|
||||
return u->attributes.wb_buffer_resize_hint;
|
||||
case UFS_QUERY_ATTR_IDN_WB_BUFF_RESIZE_STATUS:
|
||||
@@ -1604,6 +1636,26 @@ static bool ufs_wb_pinned_min_size(UfsHc *u, uint32_t value)
|
||||
return true;
|
||||
}
|
||||
|
||||
static QueryRespCode ufs_hid_write_defrag_operation(UfsHc *u, uint32_t value)
|
||||
{
|
||||
switch (value) {
|
||||
case UFS_HID_OP_DISABLE:
|
||||
ufs_hid_reset(u);
|
||||
break;
|
||||
case UFS_HID_OP_ANALYSIS:
|
||||
case UFS_HID_OP_DEFRAG:
|
||||
u->attributes.defrag_op = value;
|
||||
u->attributes.hid_state = UFS_HID_STATE_ANALYSIS_IN_PROGRESS;
|
||||
u->attributes.hid_prog_ratio = 0;
|
||||
break;
|
||||
default:
|
||||
return UFS_QUERY_RESULT_INVALID_VALUE;
|
||||
}
|
||||
|
||||
trace_ufs_hid_defrag_operation(value, u->attributes.hid_state);
|
||||
return UFS_QUERY_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static QueryRespCode ufs_write_attr_value(UfsHc *u, uint8_t idn, uint32_t value)
|
||||
{
|
||||
switch (idn) {
|
||||
@@ -1668,6 +1720,11 @@ static QueryRespCode ufs_write_attr_value(UfsHc *u, uint8_t idn, uint32_t value)
|
||||
return UFS_QUERY_RESULT_INVALID_VALUE;
|
||||
}
|
||||
break;
|
||||
case UFS_QUERY_ATTR_IDN_DEFRAG_OP:
|
||||
return ufs_hid_write_defrag_operation(u, value);
|
||||
case UFS_QUERY_ATTR_IDN_HID_SIZE:
|
||||
u->attributes.hid_size = cpu_to_be32(value);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
return 0;
|
||||
@@ -2211,11 +2268,92 @@ static void ufs_wb_process_resize(UfsHc *u)
|
||||
u->attributes.wb_buffer_resize_status = UFS_WB_RESIZE_COMPLETED;
|
||||
}
|
||||
|
||||
static void ufs_hid_process(UfsHc *u)
|
||||
{
|
||||
uint32_t requested, batch, done;
|
||||
|
||||
switch (u->attributes.hid_state) {
|
||||
case UFS_HID_STATE_ANALYSIS_IN_PROGRESS:
|
||||
u->attributes.hid_avail_size = cpu_to_be32(u->hid_fragment_count);
|
||||
if (u->hid_fragment_count > 0) {
|
||||
u->attributes.hid_state = UFS_HID_STATE_DEFRAG_REQUIRED;
|
||||
} else {
|
||||
u->attributes.hid_state = UFS_HID_STATE_DEFRAG_NOT_REQUIRED;
|
||||
}
|
||||
|
||||
if (u->attributes.defrag_op == UFS_HID_OP_ANALYSIS ||
|
||||
u->attributes.hid_state == UFS_HID_STATE_DEFRAG_NOT_REQUIRED) {
|
||||
u->attributes.defrag_op = UFS_HID_OP_DISABLE;
|
||||
}
|
||||
|
||||
trace_ufs_hid_defrag_operation(u->attributes.defrag_op,
|
||||
u->attributes.hid_state);
|
||||
break;
|
||||
|
||||
case UFS_HID_STATE_DEFRAG_REQUIRED:
|
||||
if (u->attributes.defrag_op != UFS_HID_OP_DEFRAG) {
|
||||
break;
|
||||
}
|
||||
|
||||
requested = MIN(be32_to_cpu(u->attributes.hid_size),
|
||||
be32_to_cpu(u->attributes.hid_avail_size));
|
||||
if (!requested) {
|
||||
u->attributes.hid_state = UFS_HID_STATE_DEFRAG_COMPLETED;
|
||||
u->attributes.defrag_op = UFS_HID_OP_DISABLE;
|
||||
u->attributes.hid_prog_ratio = UFS_HID_PROGRESS_COMPLETE;
|
||||
|
||||
trace_ufs_hid_defrag_operation(u->attributes.defrag_op,
|
||||
u->attributes.hid_state);
|
||||
break;
|
||||
}
|
||||
|
||||
u->attributes.hid_state = UFS_HID_STATE_DEFRAG_IN_PROGRESS;
|
||||
u->hid_defrag_total = requested;
|
||||
u->hid_defrag_remaining = requested;
|
||||
u->attributes.hid_prog_ratio = 0;
|
||||
break;
|
||||
|
||||
case UFS_HID_STATE_DEFRAG_IN_PROGRESS:
|
||||
if (u->hid_defrag_remaining > 0) {
|
||||
batch = u->hid_defrag_remaining / UFS_HID_DEFRAG_BATCH_DIV;
|
||||
if (batch == 0) {
|
||||
batch = 1;
|
||||
}
|
||||
|
||||
u->hid_defrag_remaining -= batch;
|
||||
u->hid_fragment_count -= batch;
|
||||
|
||||
done = u->hid_defrag_total - u->hid_defrag_remaining;
|
||||
u->attributes.hid_prog_ratio =
|
||||
((uint64_t)done * UFS_HID_PROGRESS_COMPLETE) /
|
||||
u->hid_defrag_total;
|
||||
|
||||
trace_ufs_hid_defrag_progress(u->hid_defrag_remaining,
|
||||
u->attributes.hid_prog_ratio);
|
||||
}
|
||||
|
||||
if (!u->hid_defrag_remaining) {
|
||||
u->attributes.hid_state = UFS_HID_STATE_DEFRAG_COMPLETED;
|
||||
u->attributes.defrag_op = UFS_HID_OP_DISABLE;
|
||||
u->attributes.hid_prog_ratio = UFS_HID_PROGRESS_COMPLETE;
|
||||
|
||||
trace_ufs_hid_defrag_operation(u->attributes.defrag_op,
|
||||
u->attributes.hid_state);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void ufs_process_idle(UfsHc *u)
|
||||
{
|
||||
ufs_wb_process_flush(u);
|
||||
ufs_wb_process_resize(u);
|
||||
ufs_wb_sync_buffer_size(u);
|
||||
ufs_hid_process(u);
|
||||
}
|
||||
|
||||
static inline bool ufs_check_idle(UfsHc *u)
|
||||
@@ -2384,8 +2522,8 @@ static void ufs_init_hc(UfsHc *u)
|
||||
uint32_t mcqconfig = 0;
|
||||
uint32_t mcqcap = 0;
|
||||
uint32_t ext_wb_sup = WB_RESIZE | WB_FIFO | WB_PINNED;
|
||||
uint32_t ext_ufs_feat_sup =
|
||||
UFS_DEV_WB_SUPPORT | UFS_DEV_HIGH_TEMP_NOTIF | UFS_DEV_LOW_TEMP_NOTIF;
|
||||
uint32_t ext_ufs_feat_sup = UFS_DEV_WB_SUPPORT | UFS_DEV_HIGH_TEMP_NOTIF |
|
||||
UFS_DEV_LOW_TEMP_NOTIF | UFS_DEV_HID_SUPPORT;
|
||||
int64_t now = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL_RT);
|
||||
|
||||
u->reg_size = pow2ceil(ufs_reg_size(u));
|
||||
@@ -2487,6 +2625,8 @@ static void ufs_init_hc(UfsHc *u)
|
||||
u->attributes.max_num_of_rtt = 0x02;
|
||||
u->attributes.device_too_high_temp_boundary = UFS_TOO_HIGH_TEMP_BOUNDARY;
|
||||
u->attributes.device_too_low_temp_boundary = UFS_TOO_LOW_TEMP_BOUNDARY;
|
||||
u->attributes.hid_avail_size = cpu_to_be32(0xFFFFFFFF);
|
||||
u->attributes.hid_size = cpu_to_be32(0xFFFFFFFF);
|
||||
|
||||
memset(&u->flags, 0, sizeof(u->flags));
|
||||
u->flags.permanently_disable_fw_update = 1;
|
||||
|
||||
@@ -175,6 +175,10 @@ typedef struct UfsHc {
|
||||
uint8_t temperature;
|
||||
|
||||
QEMUTimer idle_timer;
|
||||
|
||||
uint32_t hid_fragment_count; /* Remaining fragmented 4KB units */
|
||||
uint32_t hid_defrag_total; /* Requested units at defrag start */
|
||||
uint32_t hid_defrag_remaining; /* Requested units left to move */
|
||||
} UfsHc;
|
||||
|
||||
static inline uint32_t ufs_mcq_sq_tail(UfsHc *u, uint32_t qid)
|
||||
|
||||
@@ -951,6 +951,23 @@ enum attr_idn {
|
||||
UFS_QUERY_ATTR_IDN_COUNT,
|
||||
};
|
||||
|
||||
/* HID (Host Initiated Defragmentation) operation values for bDefragOperation */
|
||||
enum ufs_hid_op {
|
||||
UFS_HID_OP_DISABLE = 0x00,
|
||||
UFS_HID_OP_ANALYSIS = 0x01,
|
||||
UFS_HID_OP_DEFRAG = 0x02,
|
||||
};
|
||||
|
||||
/* HID state values for bHIDState */
|
||||
enum ufs_hid_state {
|
||||
UFS_HID_STATE_IDLE = 0x00,
|
||||
UFS_HID_STATE_ANALYSIS_IN_PROGRESS = 0x01,
|
||||
UFS_HID_STATE_DEFRAG_REQUIRED = 0x02,
|
||||
UFS_HID_STATE_DEFRAG_IN_PROGRESS = 0x03,
|
||||
UFS_HID_STATE_DEFRAG_COMPLETED = 0x04,
|
||||
UFS_HID_STATE_DEFRAG_NOT_REQUIRED = 0x05,
|
||||
};
|
||||
|
||||
/* Descriptor idn for Query requests */
|
||||
enum desc_idn {
|
||||
UFS_QUERY_DESC_IDN_DEVICE = 0x0,
|
||||
@@ -1142,6 +1159,7 @@ enum {
|
||||
/* Possible values for dExtendedUFSFeaturesSupport */
|
||||
enum {
|
||||
UFS_DEV_WB_SUPPORT = BIT(8),
|
||||
UFS_DEV_HID_SUPPORT = BIT(13),
|
||||
};
|
||||
|
||||
/* WriteBooster buffer mode */
|
||||
|
||||
@@ -1386,6 +1386,280 @@ 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 +1713,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);
|
||||
|
||||
Reference in New Issue
Block a user