1631c8726SJeuk Kim /* 2631c8726SJeuk Kim * QTest testcase for UFS 3631c8726SJeuk Kim * 4631c8726SJeuk Kim * Copyright (c) 2023 Samsung Electronics Co., Ltd. All rights reserved. 5631c8726SJeuk Kim * 6631c8726SJeuk Kim * SPDX-License-Identifier: GPL-2.0-or-later 7631c8726SJeuk Kim */ 8631c8726SJeuk Kim 9631c8726SJeuk Kim #include "qemu/osdep.h" 10631c8726SJeuk Kim #include "qemu/module.h" 11631c8726SJeuk Kim #include "qemu/units.h" 12631c8726SJeuk Kim #include "libqtest.h" 13631c8726SJeuk Kim #include "libqos/qgraph.h" 14631c8726SJeuk Kim #include "libqos/pci.h" 15631c8726SJeuk Kim #include "scsi/constants.h" 168b4d80bbSPhilippe Mathieu-Daudé #include "block/ufs.h" 17631c8726SJeuk Kim 18631c8726SJeuk Kim /* Test images sizes in Bytes */ 19631c8726SJeuk Kim #define TEST_IMAGE_SIZE (64 * 1024 * 1024) 20631c8726SJeuk Kim /* Timeout for various operations, in seconds. */ 21631c8726SJeuk Kim #define TIMEOUT_SECONDS 10 22631c8726SJeuk Kim /* Maximum PRD entry count */ 23631c8726SJeuk Kim #define MAX_PRD_ENTRY_COUNT 10 24631c8726SJeuk Kim #define PRD_ENTRY_DATA_SIZE 4096 25631c8726SJeuk Kim /* Constants to build upiu */ 26631c8726SJeuk Kim #define UTP_COMMAND_DESCRIPTOR_SIZE 4096 27631c8726SJeuk Kim #define UTP_RESPONSE_UPIU_OFFSET 1024 28631c8726SJeuk Kim #define UTP_PRDT_UPIU_OFFSET 2048 29631c8726SJeuk Kim 30631c8726SJeuk Kim typedef struct QUfs QUfs; 31631c8726SJeuk Kim 32631c8726SJeuk Kim struct QUfs { 33631c8726SJeuk Kim QOSGraphObject obj; 34631c8726SJeuk Kim QPCIDevice dev; 35631c8726SJeuk Kim QPCIBar bar; 36631c8726SJeuk Kim 37631c8726SJeuk Kim uint64_t utrlba; 38631c8726SJeuk Kim uint64_t utmrlba; 39631c8726SJeuk Kim uint64_t cmd_desc_addr; 40631c8726SJeuk Kim uint64_t data_buffer_addr; 41631c8726SJeuk Kim 42631c8726SJeuk Kim bool enabled; 43631c8726SJeuk Kim }; 44631c8726SJeuk Kim 45631c8726SJeuk Kim static inline uint32_t ufs_rreg(QUfs *ufs, size_t offset) 46631c8726SJeuk Kim { 47631c8726SJeuk Kim return qpci_io_readl(&ufs->dev, ufs->bar, offset); 48631c8726SJeuk Kim } 49631c8726SJeuk Kim 50631c8726SJeuk Kim static inline void ufs_wreg(QUfs *ufs, size_t offset, uint32_t value) 51631c8726SJeuk Kim { 52631c8726SJeuk Kim qpci_io_writel(&ufs->dev, ufs->bar, offset, value); 53631c8726SJeuk Kim } 54631c8726SJeuk Kim 55631c8726SJeuk Kim static void ufs_wait_for_irq(QUfs *ufs) 56631c8726SJeuk Kim { 57631c8726SJeuk Kim uint64_t end_time; 58631c8726SJeuk Kim uint32_t is; 59631c8726SJeuk Kim /* Wait for device to reset as the linux driver does. */ 60631c8726SJeuk Kim end_time = g_get_monotonic_time() + TIMEOUT_SECONDS * G_TIME_SPAN_SECOND; 61631c8726SJeuk Kim do { 62631c8726SJeuk Kim qtest_clock_step(ufs->dev.bus->qts, 100); 63631c8726SJeuk Kim is = ufs_rreg(ufs, A_IS); 64631c8726SJeuk Kim } while (is == 0 && g_get_monotonic_time() < end_time); 65631c8726SJeuk Kim } 66631c8726SJeuk Kim 67631c8726SJeuk Kim static UtpTransferReqDesc ufs_build_req_utrd(uint64_t cmd_desc_addr, 68631c8726SJeuk Kim uint8_t slot, 69631c8726SJeuk Kim uint32_t data_direction, 70631c8726SJeuk Kim uint16_t prd_table_length) 71631c8726SJeuk Kim { 72631c8726SJeuk Kim UtpTransferReqDesc req = { 0 }; 73631c8726SJeuk Kim uint64_t command_desc_base_addr = 74631c8726SJeuk Kim cmd_desc_addr + slot * UTP_COMMAND_DESCRIPTOR_SIZE; 75631c8726SJeuk Kim 76631c8726SJeuk Kim req.header.dword_0 = 77631c8726SJeuk Kim cpu_to_le32(1 << 28 | data_direction | UFS_UTP_REQ_DESC_INT_CMD); 78631c8726SJeuk Kim req.header.dword_2 = cpu_to_le32(UFS_OCS_INVALID_COMMAND_STATUS); 79631c8726SJeuk Kim 80631c8726SJeuk Kim req.command_desc_base_addr_hi = cpu_to_le32(command_desc_base_addr >> 32); 81631c8726SJeuk Kim req.command_desc_base_addr_lo = 82631c8726SJeuk Kim cpu_to_le32(command_desc_base_addr & 0xffffffff); 83631c8726SJeuk Kim req.response_upiu_offset = 84631c8726SJeuk Kim cpu_to_le16(UTP_RESPONSE_UPIU_OFFSET / sizeof(uint32_t)); 85631c8726SJeuk Kim req.response_upiu_length = cpu_to_le16(sizeof(UtpUpiuRsp)); 86631c8726SJeuk Kim req.prd_table_offset = cpu_to_le16(UTP_PRDT_UPIU_OFFSET / sizeof(uint32_t)); 87631c8726SJeuk Kim req.prd_table_length = cpu_to_le16(prd_table_length); 88631c8726SJeuk Kim return req; 89631c8726SJeuk Kim } 90631c8726SJeuk Kim 91631c8726SJeuk Kim static void ufs_send_nop_out(QUfs *ufs, uint8_t slot, 92631c8726SJeuk Kim UtpTransferReqDesc *utrd_out, UtpUpiuRsp *rsp_out) 93631c8726SJeuk Kim { 94631c8726SJeuk Kim /* Build up utp transfer request descriptor */ 95631c8726SJeuk Kim UtpTransferReqDesc utrd = ufs_build_req_utrd(ufs->cmd_desc_addr, slot, 96631c8726SJeuk Kim UFS_UTP_NO_DATA_TRANSFER, 0); 97631c8726SJeuk Kim uint64_t utrd_addr = ufs->utrlba + slot * sizeof(UtpTransferReqDesc); 98631c8726SJeuk Kim uint64_t req_upiu_addr = 99631c8726SJeuk Kim ufs->cmd_desc_addr + slot * UTP_COMMAND_DESCRIPTOR_SIZE; 100631c8726SJeuk Kim uint64_t rsp_upiu_addr = req_upiu_addr + UTP_RESPONSE_UPIU_OFFSET; 101631c8726SJeuk Kim qtest_memwrite(ufs->dev.bus->qts, utrd_addr, &utrd, sizeof(utrd)); 102631c8726SJeuk Kim 103631c8726SJeuk Kim /* Build up request upiu */ 104631c8726SJeuk Kim UtpUpiuReq req_upiu = { 0 }; 105631c8726SJeuk Kim req_upiu.header.trans_type = UFS_UPIU_TRANSACTION_NOP_OUT; 106631c8726SJeuk Kim req_upiu.header.task_tag = slot; 107631c8726SJeuk Kim qtest_memwrite(ufs->dev.bus->qts, req_upiu_addr, &req_upiu, 108631c8726SJeuk Kim sizeof(req_upiu)); 109631c8726SJeuk Kim 110631c8726SJeuk Kim /* Ring Doorbell */ 111631c8726SJeuk Kim ufs_wreg(ufs, A_UTRLDBR, 1); 112631c8726SJeuk Kim ufs_wait_for_irq(ufs); 113631c8726SJeuk Kim g_assert_true(FIELD_EX32(ufs_rreg(ufs, A_IS), IS, UTRCS)); 114631c8726SJeuk Kim ufs_wreg(ufs, A_IS, FIELD_DP32(0, IS, UTRCS, 1)); 115631c8726SJeuk Kim 116631c8726SJeuk Kim qtest_memread(ufs->dev.bus->qts, utrd_addr, utrd_out, sizeof(*utrd_out)); 117631c8726SJeuk Kim qtest_memread(ufs->dev.bus->qts, rsp_upiu_addr, rsp_out, sizeof(*rsp_out)); 118631c8726SJeuk Kim } 119631c8726SJeuk Kim 120631c8726SJeuk Kim static void ufs_send_query(QUfs *ufs, uint8_t slot, uint8_t query_function, 121631c8726SJeuk Kim uint8_t query_opcode, uint8_t idn, uint8_t index, 1227c85332aSYoochan Jeong uint8_t selector, uint32_t attr_value, 123631c8726SJeuk Kim UtpTransferReqDesc *utrd_out, UtpUpiuRsp *rsp_out) 124631c8726SJeuk Kim { 125631c8726SJeuk Kim /* Build up utp transfer request descriptor */ 126631c8726SJeuk Kim UtpTransferReqDesc utrd = ufs_build_req_utrd(ufs->cmd_desc_addr, slot, 127631c8726SJeuk Kim UFS_UTP_NO_DATA_TRANSFER, 0); 128631c8726SJeuk Kim uint64_t utrd_addr = ufs->utrlba + slot * sizeof(UtpTransferReqDesc); 129631c8726SJeuk Kim uint64_t req_upiu_addr = 130631c8726SJeuk Kim ufs->cmd_desc_addr + slot * UTP_COMMAND_DESCRIPTOR_SIZE; 131631c8726SJeuk Kim uint64_t rsp_upiu_addr = req_upiu_addr + UTP_RESPONSE_UPIU_OFFSET; 132631c8726SJeuk Kim qtest_memwrite(ufs->dev.bus->qts, utrd_addr, &utrd, sizeof(utrd)); 133631c8726SJeuk Kim 134631c8726SJeuk Kim /* Build up request upiu */ 135631c8726SJeuk Kim UtpUpiuReq req_upiu = { 0 }; 136631c8726SJeuk Kim req_upiu.header.trans_type = UFS_UPIU_TRANSACTION_QUERY_REQ; 137631c8726SJeuk Kim req_upiu.header.query_func = query_function; 138631c8726SJeuk Kim req_upiu.header.task_tag = slot; 139631c8726SJeuk Kim /* 1407c85332aSYoochan Jeong * QEMU UFS does not currently support Write descriptor, 141631c8726SJeuk Kim * so the value of data_segment_length is always 0. 142631c8726SJeuk Kim */ 143631c8726SJeuk Kim req_upiu.header.data_segment_length = 0; 144631c8726SJeuk Kim req_upiu.qr.opcode = query_opcode; 145631c8726SJeuk Kim req_upiu.qr.idn = idn; 146631c8726SJeuk Kim req_upiu.qr.index = index; 1477c85332aSYoochan Jeong req_upiu.qr.selector = selector; 1487c85332aSYoochan Jeong req_upiu.qr.value = attr_value; 1497c85332aSYoochan Jeong req_upiu.qr.length = UFS_QUERY_DESC_MAX_SIZE; 150631c8726SJeuk Kim qtest_memwrite(ufs->dev.bus->qts, req_upiu_addr, &req_upiu, 151631c8726SJeuk Kim sizeof(req_upiu)); 152631c8726SJeuk Kim 153631c8726SJeuk Kim /* Ring Doorbell */ 154631c8726SJeuk Kim ufs_wreg(ufs, A_UTRLDBR, 1); 155631c8726SJeuk Kim ufs_wait_for_irq(ufs); 156631c8726SJeuk Kim g_assert_true(FIELD_EX32(ufs_rreg(ufs, A_IS), IS, UTRCS)); 157631c8726SJeuk Kim ufs_wreg(ufs, A_IS, FIELD_DP32(0, IS, UTRCS, 1)); 158631c8726SJeuk Kim 159631c8726SJeuk Kim qtest_memread(ufs->dev.bus->qts, utrd_addr, utrd_out, sizeof(*utrd_out)); 160631c8726SJeuk Kim qtest_memread(ufs->dev.bus->qts, rsp_upiu_addr, rsp_out, sizeof(*rsp_out)); 161631c8726SJeuk Kim } 162631c8726SJeuk Kim 163631c8726SJeuk Kim static void ufs_send_scsi_command(QUfs *ufs, uint8_t slot, uint8_t lun, 164631c8726SJeuk Kim const uint8_t *cdb, const uint8_t *data_in, 165631c8726SJeuk Kim size_t data_in_len, uint8_t *data_out, 166631c8726SJeuk Kim size_t data_out_len, 167631c8726SJeuk Kim UtpTransferReqDesc *utrd_out, 168631c8726SJeuk Kim UtpUpiuRsp *rsp_out) 169631c8726SJeuk Kim 170631c8726SJeuk Kim { 171631c8726SJeuk Kim /* Build up PRDT */ 172631c8726SJeuk Kim UfshcdSgEntry entries[MAX_PRD_ENTRY_COUNT] = { 173631c8726SJeuk Kim 0, 174631c8726SJeuk Kim }; 175631c8726SJeuk Kim uint8_t flags; 176631c8726SJeuk Kim uint16_t prd_table_length, i; 177631c8726SJeuk Kim uint32_t data_direction, data_len; 178631c8726SJeuk Kim uint64_t req_upiu_addr = 179631c8726SJeuk Kim ufs->cmd_desc_addr + slot * UTP_COMMAND_DESCRIPTOR_SIZE; 180631c8726SJeuk Kim uint64_t prdt_addr = req_upiu_addr + UTP_PRDT_UPIU_OFFSET; 181631c8726SJeuk Kim 182631c8726SJeuk Kim g_assert_true(data_in_len < MAX_PRD_ENTRY_COUNT * PRD_ENTRY_DATA_SIZE); 183631c8726SJeuk Kim g_assert_true(data_out_len < MAX_PRD_ENTRY_COUNT * PRD_ENTRY_DATA_SIZE); 184631c8726SJeuk Kim if (data_in_len > 0) { 185631c8726SJeuk Kim g_assert_nonnull(data_in); 186631c8726SJeuk Kim data_direction = UFS_UTP_HOST_TO_DEVICE; 187631c8726SJeuk Kim data_len = data_in_len; 188631c8726SJeuk Kim flags = UFS_UPIU_CMD_FLAGS_WRITE; 189631c8726SJeuk Kim } else if (data_out_len > 0) { 190631c8726SJeuk Kim g_assert_nonnull(data_out); 191631c8726SJeuk Kim data_direction = UFS_UTP_DEVICE_TO_HOST; 192631c8726SJeuk Kim data_len = data_out_len; 193631c8726SJeuk Kim flags = UFS_UPIU_CMD_FLAGS_READ; 194631c8726SJeuk Kim } else { 195631c8726SJeuk Kim data_direction = UFS_UTP_NO_DATA_TRANSFER; 196631c8726SJeuk Kim data_len = 0; 197631c8726SJeuk Kim flags = UFS_UPIU_CMD_FLAGS_NONE; 198631c8726SJeuk Kim } 199631c8726SJeuk Kim prd_table_length = DIV_ROUND_UP(data_len, PRD_ENTRY_DATA_SIZE); 200631c8726SJeuk Kim 201631c8726SJeuk Kim qtest_memset(ufs->dev.bus->qts, ufs->data_buffer_addr, 0, 202631c8726SJeuk Kim MAX_PRD_ENTRY_COUNT * PRD_ENTRY_DATA_SIZE); 203631c8726SJeuk Kim if (data_in_len) { 204631c8726SJeuk Kim qtest_memwrite(ufs->dev.bus->qts, ufs->data_buffer_addr, data_in, 205631c8726SJeuk Kim data_in_len); 206631c8726SJeuk Kim } 207631c8726SJeuk Kim 208631c8726SJeuk Kim for (i = 0; i < prd_table_length; i++) { 209631c8726SJeuk Kim entries[i].addr = 210631c8726SJeuk Kim cpu_to_le64(ufs->data_buffer_addr + i * sizeof(UfshcdSgEntry)); 211631c8726SJeuk Kim if (i + 1 != prd_table_length) { 212631c8726SJeuk Kim entries[i].size = cpu_to_le32(PRD_ENTRY_DATA_SIZE - 1); 213631c8726SJeuk Kim } else { 214631c8726SJeuk Kim entries[i].size = cpu_to_le32( 215631c8726SJeuk Kim data_len - (PRD_ENTRY_DATA_SIZE * (prd_table_length - 1)) - 1); 216631c8726SJeuk Kim } 217631c8726SJeuk Kim } 218631c8726SJeuk Kim qtest_memwrite(ufs->dev.bus->qts, prdt_addr, entries, 219631c8726SJeuk Kim prd_table_length * sizeof(UfshcdSgEntry)); 220631c8726SJeuk Kim 221631c8726SJeuk Kim /* Build up utp transfer request descriptor */ 222631c8726SJeuk Kim UtpTransferReqDesc utrd = ufs_build_req_utrd( 223631c8726SJeuk Kim ufs->cmd_desc_addr, slot, data_direction, prd_table_length); 224631c8726SJeuk Kim uint64_t utrd_addr = ufs->utrlba + slot * sizeof(UtpTransferReqDesc); 225631c8726SJeuk Kim uint64_t rsp_upiu_addr = req_upiu_addr + UTP_RESPONSE_UPIU_OFFSET; 226631c8726SJeuk Kim qtest_memwrite(ufs->dev.bus->qts, utrd_addr, &utrd, sizeof(utrd)); 227631c8726SJeuk Kim 228631c8726SJeuk Kim /* Build up request upiu */ 229631c8726SJeuk Kim UtpUpiuReq req_upiu = { 0 }; 230631c8726SJeuk Kim req_upiu.header.trans_type = UFS_UPIU_TRANSACTION_COMMAND; 231631c8726SJeuk Kim req_upiu.header.flags = flags; 232631c8726SJeuk Kim req_upiu.header.lun = lun; 233631c8726SJeuk Kim req_upiu.header.task_tag = slot; 234631c8726SJeuk Kim req_upiu.sc.exp_data_transfer_len = cpu_to_be32(data_len); 235631c8726SJeuk Kim memcpy(req_upiu.sc.cdb, cdb, UFS_CDB_SIZE); 236631c8726SJeuk Kim qtest_memwrite(ufs->dev.bus->qts, req_upiu_addr, &req_upiu, 237631c8726SJeuk Kim sizeof(req_upiu)); 238631c8726SJeuk Kim 239631c8726SJeuk Kim /* Ring Doorbell */ 240631c8726SJeuk Kim ufs_wreg(ufs, A_UTRLDBR, 1); 241631c8726SJeuk Kim ufs_wait_for_irq(ufs); 242631c8726SJeuk Kim g_assert_true(FIELD_EX32(ufs_rreg(ufs, A_IS), IS, UTRCS)); 243631c8726SJeuk Kim ufs_wreg(ufs, A_IS, FIELD_DP32(0, IS, UTRCS, 1)); 244631c8726SJeuk Kim 245631c8726SJeuk Kim qtest_memread(ufs->dev.bus->qts, utrd_addr, utrd_out, sizeof(*utrd_out)); 246631c8726SJeuk Kim qtest_memread(ufs->dev.bus->qts, rsp_upiu_addr, rsp_out, sizeof(*rsp_out)); 247631c8726SJeuk Kim if (data_out_len) { 248631c8726SJeuk Kim qtest_memread(ufs->dev.bus->qts, ufs->data_buffer_addr, data_out, 249631c8726SJeuk Kim data_out_len); 250631c8726SJeuk Kim } 251631c8726SJeuk Kim } 252631c8726SJeuk Kim 253631c8726SJeuk Kim /** 254631c8726SJeuk Kim * Initialize Ufs host controller and logical unit. 255631c8726SJeuk Kim * After running this function, you can make a transfer request to the UFS. 256631c8726SJeuk Kim */ 257631c8726SJeuk Kim static void ufs_init(QUfs *ufs, QGuestAllocator *alloc) 258631c8726SJeuk Kim { 259631c8726SJeuk Kim uint64_t end_time; 260631c8726SJeuk Kim uint32_t nutrs, nutmrs; 261631c8726SJeuk Kim uint32_t hcs, is, ucmdarg2, cap; 262631c8726SJeuk Kim uint32_t hce = 0, ie = 0; 263631c8726SJeuk Kim UtpTransferReqDesc utrd; 264631c8726SJeuk Kim UtpUpiuRsp rsp_upiu; 265631c8726SJeuk Kim 266631c8726SJeuk Kim ufs->bar = qpci_iomap(&ufs->dev, 0, NULL); 267631c8726SJeuk Kim qpci_device_enable(&ufs->dev); 268631c8726SJeuk Kim 269631c8726SJeuk Kim /* Start host controller initialization */ 270631c8726SJeuk Kim hce = FIELD_DP32(hce, HCE, HCE, 1); 271631c8726SJeuk Kim ufs_wreg(ufs, A_HCE, hce); 272631c8726SJeuk Kim 273631c8726SJeuk Kim /* Wait for device to reset */ 274631c8726SJeuk Kim end_time = g_get_monotonic_time() + TIMEOUT_SECONDS * G_TIME_SPAN_SECOND; 275631c8726SJeuk Kim do { 276631c8726SJeuk Kim qtest_clock_step(ufs->dev.bus->qts, 100); 277631c8726SJeuk Kim hce = FIELD_EX32(ufs_rreg(ufs, A_HCE), HCE, HCE); 278631c8726SJeuk Kim } while (hce == 0 && g_get_monotonic_time() < end_time); 279631c8726SJeuk Kim g_assert_cmpuint(hce, ==, 1); 280631c8726SJeuk Kim 281631c8726SJeuk Kim /* Enable interrupt */ 282631c8726SJeuk Kim ie = FIELD_DP32(ie, IE, UCCE, 1); 283631c8726SJeuk Kim ie = FIELD_DP32(ie, IE, UHESE, 1); 284631c8726SJeuk Kim ie = FIELD_DP32(ie, IE, UHXSE, 1); 285631c8726SJeuk Kim ie = FIELD_DP32(ie, IE, UPMSE, 1); 286631c8726SJeuk Kim ufs_wreg(ufs, A_IE, ie); 287631c8726SJeuk Kim 288631c8726SJeuk Kim /* Send DME_LINK_STARTUP uic command */ 289631c8726SJeuk Kim hcs = ufs_rreg(ufs, A_HCS); 290631c8726SJeuk Kim g_assert_true(FIELD_EX32(hcs, HCS, UCRDY)); 291631c8726SJeuk Kim 292631c8726SJeuk Kim ufs_wreg(ufs, A_UCMDARG1, 0); 293631c8726SJeuk Kim ufs_wreg(ufs, A_UCMDARG2, 0); 294631c8726SJeuk Kim ufs_wreg(ufs, A_UCMDARG3, 0); 295631c8726SJeuk Kim ufs_wreg(ufs, A_UICCMD, UFS_UIC_CMD_DME_LINK_STARTUP); 296631c8726SJeuk Kim 297631c8726SJeuk Kim is = ufs_rreg(ufs, A_IS); 298631c8726SJeuk Kim g_assert_true(FIELD_EX32(is, IS, UCCS)); 299631c8726SJeuk Kim ufs_wreg(ufs, A_IS, FIELD_DP32(0, IS, UCCS, 1)); 300631c8726SJeuk Kim 301631c8726SJeuk Kim ucmdarg2 = ufs_rreg(ufs, A_UCMDARG2); 302631c8726SJeuk Kim g_assert_cmpuint(ucmdarg2, ==, 0); 303631c8726SJeuk Kim is = ufs_rreg(ufs, A_IS); 304631c8726SJeuk Kim g_assert_cmpuint(is, ==, 0); 305631c8726SJeuk Kim hcs = ufs_rreg(ufs, A_HCS); 306631c8726SJeuk Kim g_assert_true(FIELD_EX32(hcs, HCS, DP)); 307631c8726SJeuk Kim g_assert_true(FIELD_EX32(hcs, HCS, UTRLRDY)); 308631c8726SJeuk Kim g_assert_true(FIELD_EX32(hcs, HCS, UTMRLRDY)); 309631c8726SJeuk Kim g_assert_true(FIELD_EX32(hcs, HCS, UCRDY)); 310631c8726SJeuk Kim 311631c8726SJeuk Kim /* Enable all interrupt functions */ 312631c8726SJeuk Kim ie = FIELD_DP32(ie, IE, UTRCE, 1); 313631c8726SJeuk Kim ie = FIELD_DP32(ie, IE, UEE, 1); 314631c8726SJeuk Kim ie = FIELD_DP32(ie, IE, UPMSE, 1); 315631c8726SJeuk Kim ie = FIELD_DP32(ie, IE, UHXSE, 1); 316631c8726SJeuk Kim ie = FIELD_DP32(ie, IE, UHESE, 1); 317631c8726SJeuk Kim ie = FIELD_DP32(ie, IE, UTMRCE, 1); 318631c8726SJeuk Kim ie = FIELD_DP32(ie, IE, UCCE, 1); 319631c8726SJeuk Kim ie = FIELD_DP32(ie, IE, DFEE, 1); 320631c8726SJeuk Kim ie = FIELD_DP32(ie, IE, HCFEE, 1); 321631c8726SJeuk Kim ie = FIELD_DP32(ie, IE, SBFEE, 1); 322631c8726SJeuk Kim ie = FIELD_DP32(ie, IE, CEFEE, 1); 323631c8726SJeuk Kim ufs_wreg(ufs, A_IE, ie); 324631c8726SJeuk Kim ufs_wreg(ufs, A_UTRIACR, 0); 325631c8726SJeuk Kim 3264b3e4d2bSMichael Tokarev /* Enable transfer request and task management request */ 327631c8726SJeuk Kim cap = ufs_rreg(ufs, A_CAP); 328631c8726SJeuk Kim nutrs = FIELD_EX32(cap, CAP, NUTRS) + 1; 329631c8726SJeuk Kim nutmrs = FIELD_EX32(cap, CAP, NUTMRS) + 1; 330631c8726SJeuk Kim ufs->cmd_desc_addr = 331631c8726SJeuk Kim guest_alloc(alloc, nutrs * UTP_COMMAND_DESCRIPTOR_SIZE); 332631c8726SJeuk Kim ufs->data_buffer_addr = 333631c8726SJeuk Kim guest_alloc(alloc, MAX_PRD_ENTRY_COUNT * PRD_ENTRY_DATA_SIZE); 334631c8726SJeuk Kim ufs->utrlba = guest_alloc(alloc, nutrs * sizeof(UtpTransferReqDesc)); 335631c8726SJeuk Kim ufs->utmrlba = guest_alloc(alloc, nutmrs * sizeof(UtpTaskReqDesc)); 336631c8726SJeuk Kim 337631c8726SJeuk Kim ufs_wreg(ufs, A_UTRLBA, ufs->utrlba & 0xffffffff); 338631c8726SJeuk Kim ufs_wreg(ufs, A_UTRLBAU, ufs->utrlba >> 32); 339631c8726SJeuk Kim ufs_wreg(ufs, A_UTMRLBA, ufs->utmrlba & 0xffffffff); 340631c8726SJeuk Kim ufs_wreg(ufs, A_UTMRLBAU, ufs->utmrlba >> 32); 341631c8726SJeuk Kim ufs_wreg(ufs, A_UTRLRSR, 1); 342631c8726SJeuk Kim ufs_wreg(ufs, A_UTMRLRSR, 1); 343631c8726SJeuk Kim 344631c8726SJeuk Kim /* Send nop out to test transfer request */ 345631c8726SJeuk Kim ufs_send_nop_out(ufs, 0, &utrd, &rsp_upiu); 346631c8726SJeuk Kim g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); 347631c8726SJeuk Kim 348631c8726SJeuk Kim /* Set fDeviceInit flag via query request */ 349631c8726SJeuk Kim ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, 350631c8726SJeuk Kim UFS_UPIU_QUERY_OPCODE_SET_FLAG, 3517c85332aSYoochan Jeong UFS_QUERY_FLAG_IDN_FDEVICEINIT, 0, 0, 0, &utrd, &rsp_upiu); 352631c8726SJeuk Kim g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); 353631c8726SJeuk Kim 354631c8726SJeuk Kim /* Wait for device to reset */ 355631c8726SJeuk Kim end_time = g_get_monotonic_time() + TIMEOUT_SECONDS * G_TIME_SPAN_SECOND; 356631c8726SJeuk Kim do { 357631c8726SJeuk Kim qtest_clock_step(ufs->dev.bus->qts, 100); 358631c8726SJeuk Kim ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, 359631c8726SJeuk Kim UFS_UPIU_QUERY_OPCODE_READ_FLAG, 3607c85332aSYoochan Jeong UFS_QUERY_FLAG_IDN_FDEVICEINIT, 0, 0, 0, &utrd, 3617c85332aSYoochan Jeong &rsp_upiu); 362631c8726SJeuk Kim } while (be32_to_cpu(rsp_upiu.qr.value) != 0 && 363631c8726SJeuk Kim g_get_monotonic_time() < end_time); 364631c8726SJeuk Kim g_assert_cmpuint(be32_to_cpu(rsp_upiu.qr.value), ==, 0); 365631c8726SJeuk Kim 366631c8726SJeuk Kim ufs->enabled = true; 367631c8726SJeuk Kim } 368631c8726SJeuk Kim 369631c8726SJeuk Kim static void ufs_exit(QUfs *ufs, QGuestAllocator *alloc) 370631c8726SJeuk Kim { 371631c8726SJeuk Kim if (ufs->enabled) { 372631c8726SJeuk Kim guest_free(alloc, ufs->utrlba); 373631c8726SJeuk Kim guest_free(alloc, ufs->utmrlba); 374631c8726SJeuk Kim guest_free(alloc, ufs->cmd_desc_addr); 375631c8726SJeuk Kim guest_free(alloc, ufs->data_buffer_addr); 376631c8726SJeuk Kim } 377631c8726SJeuk Kim 378631c8726SJeuk Kim qpci_iounmap(&ufs->dev, ufs->bar); 379631c8726SJeuk Kim } 380631c8726SJeuk Kim 381631c8726SJeuk Kim static void *ufs_get_driver(void *obj, const char *interface) 382631c8726SJeuk Kim { 383631c8726SJeuk Kim QUfs *ufs = obj; 384631c8726SJeuk Kim 385631c8726SJeuk Kim if (!g_strcmp0(interface, "pci-device")) { 386631c8726SJeuk Kim return &ufs->dev; 387631c8726SJeuk Kim } 388631c8726SJeuk Kim 389631c8726SJeuk Kim fprintf(stderr, "%s not present in ufs\n", interface); 390631c8726SJeuk Kim g_assert_not_reached(); 391631c8726SJeuk Kim } 392631c8726SJeuk Kim 393631c8726SJeuk Kim static void *ufs_create(void *pci_bus, QGuestAllocator *alloc, void *addr) 394631c8726SJeuk Kim { 395631c8726SJeuk Kim QUfs *ufs = g_new0(QUfs, 1); 396631c8726SJeuk Kim QPCIBus *bus = pci_bus; 397631c8726SJeuk Kim 398631c8726SJeuk Kim qpci_device_init(&ufs->dev, bus, addr); 399631c8726SJeuk Kim ufs->obj.get_driver = ufs_get_driver; 400631c8726SJeuk Kim 401631c8726SJeuk Kim return &ufs->obj; 402631c8726SJeuk Kim } 403631c8726SJeuk Kim 404631c8726SJeuk Kim static void ufstest_reg_read(void *obj, void *data, QGuestAllocator *alloc) 405631c8726SJeuk Kim { 406631c8726SJeuk Kim QUfs *ufs = obj; 407631c8726SJeuk Kim uint32_t cap; 408631c8726SJeuk Kim 409631c8726SJeuk Kim ufs->bar = qpci_iomap(&ufs->dev, 0, NULL); 410631c8726SJeuk Kim qpci_device_enable(&ufs->dev); 411631c8726SJeuk Kim 412631c8726SJeuk Kim cap = ufs_rreg(ufs, A_CAP); 413631c8726SJeuk Kim g_assert_cmpuint(FIELD_EX32(cap, CAP, NUTRS), ==, 31); 414631c8726SJeuk Kim g_assert_cmpuint(FIELD_EX32(cap, CAP, NUTMRS), ==, 7); 415631c8726SJeuk Kim g_assert_cmpuint(FIELD_EX32(cap, CAP, 64AS), ==, 1); 416631c8726SJeuk Kim 417631c8726SJeuk Kim qpci_iounmap(&ufs->dev, ufs->bar); 418631c8726SJeuk Kim } 419631c8726SJeuk Kim 420631c8726SJeuk Kim static void ufstest_init(void *obj, void *data, QGuestAllocator *alloc) 421631c8726SJeuk Kim { 422631c8726SJeuk Kim QUfs *ufs = obj; 423631c8726SJeuk Kim 424631c8726SJeuk Kim uint8_t buf[4096] = { 0 }; 425631c8726SJeuk Kim const uint8_t report_luns_cdb[UFS_CDB_SIZE] = { 426631c8726SJeuk Kim /* allocation length 4096 */ 427631c8726SJeuk Kim REPORT_LUNS, 0x00, 0x00, 0x00, 0x00, 0x00, 428631c8726SJeuk Kim 0x00, 0x00, 0x10, 0x00, 0x00, 0x00 429631c8726SJeuk Kim }; 430631c8726SJeuk Kim const uint8_t test_unit_ready_cdb[UFS_CDB_SIZE] = { 431631c8726SJeuk Kim TEST_UNIT_READY, 432631c8726SJeuk Kim }; 433096434feSJeuk Kim const uint8_t request_sense_cdb[UFS_CDB_SIZE] = { 434096434feSJeuk Kim REQUEST_SENSE, 435096434feSJeuk Kim }; 436631c8726SJeuk Kim UtpTransferReqDesc utrd; 437631c8726SJeuk Kim UtpUpiuRsp rsp_upiu; 438631c8726SJeuk Kim 439631c8726SJeuk Kim ufs_init(ufs, alloc); 440631c8726SJeuk Kim 441631c8726SJeuk Kim /* Check REPORT_LUNS */ 442631c8726SJeuk Kim ufs_send_scsi_command(ufs, 0, 0, report_luns_cdb, NULL, 0, buf, sizeof(buf), 443631c8726SJeuk Kim &utrd, &rsp_upiu); 444631c8726SJeuk Kim g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); 445631c8726SJeuk Kim g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, GOOD); 446631c8726SJeuk Kim /* LUN LIST LENGTH should be 8, in big endian */ 447631c8726SJeuk Kim g_assert_cmpuint(buf[3], ==, 8); 448631c8726SJeuk Kim /* There is one logical unit whose lun is 0 */ 449631c8726SJeuk Kim g_assert_cmpuint(buf[9], ==, 0); 450631c8726SJeuk Kim 451096434feSJeuk Kim /* Clear Unit Attention */ 452096434feSJeuk Kim ufs_send_scsi_command(ufs, 0, 0, request_sense_cdb, NULL, 0, buf, 453096434feSJeuk Kim sizeof(buf), &utrd, &rsp_upiu); 454096434feSJeuk Kim g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); 455096434feSJeuk Kim g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, CHECK_CONDITION); 456096434feSJeuk Kim 457631c8726SJeuk Kim /* Check TEST_UNIT_READY */ 458631c8726SJeuk Kim ufs_send_scsi_command(ufs, 0, 0, test_unit_ready_cdb, NULL, 0, NULL, 0, 459631c8726SJeuk Kim &utrd, &rsp_upiu); 460631c8726SJeuk Kim g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); 461631c8726SJeuk Kim g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, GOOD); 462631c8726SJeuk Kim 463631c8726SJeuk Kim ufs_exit(ufs, alloc); 464631c8726SJeuk Kim } 465631c8726SJeuk Kim 466631c8726SJeuk Kim static void ufstest_read_write(void *obj, void *data, QGuestAllocator *alloc) 467631c8726SJeuk Kim { 468631c8726SJeuk Kim QUfs *ufs = obj; 469631c8726SJeuk Kim uint8_t read_buf[4096] = { 0 }; 470631c8726SJeuk Kim uint8_t write_buf[4096] = { 0 }; 471631c8726SJeuk Kim const uint8_t read_capacity_cdb[UFS_CDB_SIZE] = { 472631c8726SJeuk Kim /* allocation length 4096 */ 473631c8726SJeuk Kim SERVICE_ACTION_IN_16, 474631c8726SJeuk Kim SAI_READ_CAPACITY_16, 475631c8726SJeuk Kim 0x00, 476631c8726SJeuk Kim 0x00, 477631c8726SJeuk Kim 0x00, 478631c8726SJeuk Kim 0x00, 479631c8726SJeuk Kim 0x00, 480631c8726SJeuk Kim 0x00, 481631c8726SJeuk Kim 0x00, 482631c8726SJeuk Kim 0x00, 483631c8726SJeuk Kim 0x00, 484631c8726SJeuk Kim 0x00, 485631c8726SJeuk Kim 0x10, 486631c8726SJeuk Kim 0x00, 487631c8726SJeuk Kim 0x00, 488631c8726SJeuk Kim 0x00 489631c8726SJeuk Kim }; 490096434feSJeuk Kim const uint8_t request_sense_cdb[UFS_CDB_SIZE] = { 491096434feSJeuk Kim REQUEST_SENSE, 492096434feSJeuk Kim }; 493631c8726SJeuk Kim const uint8_t read_cdb[UFS_CDB_SIZE] = { 494631c8726SJeuk Kim /* READ(10) to LBA 0, transfer length 1 */ 495631c8726SJeuk Kim READ_10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00 496631c8726SJeuk Kim }; 497631c8726SJeuk Kim const uint8_t write_cdb[UFS_CDB_SIZE] = { 498631c8726SJeuk Kim /* WRITE(10) to LBA 0, transfer length 1 */ 499631c8726SJeuk Kim WRITE_10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00 500631c8726SJeuk Kim }; 501631c8726SJeuk Kim uint32_t block_size; 502631c8726SJeuk Kim UtpTransferReqDesc utrd; 503631c8726SJeuk Kim UtpUpiuRsp rsp_upiu; 504096434feSJeuk Kim const int test_lun = 1; 505631c8726SJeuk Kim 506631c8726SJeuk Kim ufs_init(ufs, alloc); 507631c8726SJeuk Kim 508096434feSJeuk Kim /* Clear Unit Attention */ 509096434feSJeuk Kim ufs_send_scsi_command(ufs, 0, test_lun, request_sense_cdb, NULL, 0, 510096434feSJeuk Kim read_buf, sizeof(read_buf), &utrd, &rsp_upiu); 511096434feSJeuk Kim g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); 512096434feSJeuk Kim g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, CHECK_CONDITION); 513096434feSJeuk Kim 514631c8726SJeuk Kim /* Read capacity */ 515096434feSJeuk Kim ufs_send_scsi_command(ufs, 0, test_lun, read_capacity_cdb, NULL, 0, 516096434feSJeuk Kim read_buf, sizeof(read_buf), &utrd, &rsp_upiu); 517631c8726SJeuk Kim g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); 518631c8726SJeuk Kim g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, 519096434feSJeuk Kim UFS_COMMAND_RESULT_SUCCESS); 520631c8726SJeuk Kim block_size = ldl_be_p(&read_buf[8]); 521631c8726SJeuk Kim g_assert_cmpuint(block_size, ==, 4096); 522631c8726SJeuk Kim 523631c8726SJeuk Kim /* Write data */ 52497970daeSJeuk Kim memset(write_buf, 0xab, block_size); 525096434feSJeuk Kim ufs_send_scsi_command(ufs, 0, test_lun, write_cdb, write_buf, block_size, 526096434feSJeuk Kim NULL, 0, &utrd, &rsp_upiu); 527631c8726SJeuk Kim g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); 528631c8726SJeuk Kim g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, 529096434feSJeuk Kim UFS_COMMAND_RESULT_SUCCESS); 530631c8726SJeuk Kim 531631c8726SJeuk Kim /* Read data and verify */ 532096434feSJeuk Kim ufs_send_scsi_command(ufs, 0, test_lun, read_cdb, NULL, 0, read_buf, 533096434feSJeuk Kim block_size, &utrd, &rsp_upiu); 534631c8726SJeuk Kim g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); 535631c8726SJeuk Kim g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, 536096434feSJeuk Kim UFS_COMMAND_RESULT_SUCCESS); 537631c8726SJeuk Kim g_assert_cmpint(memcmp(read_buf, write_buf, block_size), ==, 0); 538631c8726SJeuk Kim 539631c8726SJeuk Kim ufs_exit(ufs, alloc); 540631c8726SJeuk Kim } 541631c8726SJeuk Kim 542*4aac3029SYoochan Jeong static void ufstest_query_flag_request(void *obj, void *data, 543*4aac3029SYoochan Jeong QGuestAllocator *alloc) 544*4aac3029SYoochan Jeong { 545*4aac3029SYoochan Jeong QUfs *ufs = obj; 546*4aac3029SYoochan Jeong 547*4aac3029SYoochan Jeong UtpTransferReqDesc utrd; 548*4aac3029SYoochan Jeong UtpUpiuRsp rsp_upiu; 549*4aac3029SYoochan Jeong ufs_init(ufs, alloc); 550*4aac3029SYoochan Jeong 551*4aac3029SYoochan Jeong /* Read read-only flag */ 552*4aac3029SYoochan Jeong ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, 553*4aac3029SYoochan Jeong UFS_UPIU_QUERY_OPCODE_READ_FLAG, 554*4aac3029SYoochan Jeong UFS_QUERY_FLAG_IDN_FDEVICEINIT, 0, 0, 0, &utrd, &rsp_upiu); 555*4aac3029SYoochan Jeong g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); 556*4aac3029SYoochan Jeong g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); 557*4aac3029SYoochan Jeong g_assert_cmpuint(rsp_upiu.qr.opcode, ==, UFS_UPIU_QUERY_OPCODE_READ_FLAG); 558*4aac3029SYoochan Jeong g_assert_cmpuint(rsp_upiu.qr.idn, ==, UFS_QUERY_FLAG_IDN_FDEVICEINIT); 559*4aac3029SYoochan Jeong g_assert_cmpuint(rsp_upiu.qr.value, ==, be32_to_cpu(0)); 560*4aac3029SYoochan Jeong 561*4aac3029SYoochan Jeong /* Flag Set, Clear, Toggle Test with fDeviceLifeSpanModeEn */ 562*4aac3029SYoochan Jeong ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, 563*4aac3029SYoochan Jeong UFS_UPIU_QUERY_OPCODE_READ_FLAG, 564*4aac3029SYoochan Jeong UFS_QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE, 0, 0, 0, &utrd, 565*4aac3029SYoochan Jeong &rsp_upiu); 566*4aac3029SYoochan Jeong g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); 567*4aac3029SYoochan Jeong g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); 568*4aac3029SYoochan Jeong g_assert_cmpuint(rsp_upiu.qr.value, ==, be32_to_cpu(0)); 569*4aac3029SYoochan Jeong 570*4aac3029SYoochan Jeong ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, 571*4aac3029SYoochan Jeong UFS_UPIU_QUERY_OPCODE_SET_FLAG, 572*4aac3029SYoochan Jeong UFS_QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE, 0, 0, 0, &utrd, 573*4aac3029SYoochan Jeong &rsp_upiu); 574*4aac3029SYoochan Jeong g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); 575*4aac3029SYoochan Jeong g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); 576*4aac3029SYoochan Jeong g_assert_cmpuint(rsp_upiu.qr.value, ==, be32_to_cpu(1)); 577*4aac3029SYoochan Jeong 578*4aac3029SYoochan Jeong ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, 579*4aac3029SYoochan Jeong UFS_UPIU_QUERY_OPCODE_CLEAR_FLAG, 580*4aac3029SYoochan Jeong UFS_QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE, 0, 0, 0, &utrd, 581*4aac3029SYoochan Jeong &rsp_upiu); 582*4aac3029SYoochan Jeong g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); 583*4aac3029SYoochan Jeong g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); 584*4aac3029SYoochan Jeong g_assert_cmpuint(rsp_upiu.qr.value, ==, be32_to_cpu(0)); 585*4aac3029SYoochan Jeong 586*4aac3029SYoochan Jeong ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, 587*4aac3029SYoochan Jeong UFS_UPIU_QUERY_OPCODE_TOGGLE_FLAG, 588*4aac3029SYoochan Jeong UFS_QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE, 0, 0, 0, &utrd, 589*4aac3029SYoochan Jeong &rsp_upiu); 590*4aac3029SYoochan Jeong g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); 591*4aac3029SYoochan Jeong g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); 592*4aac3029SYoochan Jeong g_assert_cmpuint(rsp_upiu.qr.value, ==, be32_to_cpu(1)); 593*4aac3029SYoochan Jeong 594*4aac3029SYoochan Jeong ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, 595*4aac3029SYoochan Jeong UFS_UPIU_QUERY_OPCODE_TOGGLE_FLAG, 596*4aac3029SYoochan Jeong UFS_QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE, 0, 0, 0, &utrd, 597*4aac3029SYoochan Jeong &rsp_upiu); 598*4aac3029SYoochan Jeong g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); 599*4aac3029SYoochan Jeong g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); 600*4aac3029SYoochan Jeong g_assert_cmpuint(rsp_upiu.qr.value, ==, be32_to_cpu(0)); 601*4aac3029SYoochan Jeong 602*4aac3029SYoochan Jeong /* Read Write-only Flag (Intended Failure) */ 603*4aac3029SYoochan Jeong ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, 604*4aac3029SYoochan Jeong UFS_UPIU_QUERY_OPCODE_READ_FLAG, 605*4aac3029SYoochan Jeong UFS_QUERY_FLAG_IDN_PURGE_ENABLE, 0, 0, 0, &utrd, &rsp_upiu); 606*4aac3029SYoochan Jeong g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, 607*4aac3029SYoochan Jeong UFS_OCS_INVALID_CMD_TABLE_ATTR); 608*4aac3029SYoochan Jeong g_assert_cmpuint(rsp_upiu.header.response, ==, 609*4aac3029SYoochan Jeong UFS_QUERY_RESULT_NOT_READABLE); 610*4aac3029SYoochan Jeong 611*4aac3029SYoochan Jeong /* Write Read-Only Flag (Intended Failure) */ 612*4aac3029SYoochan Jeong ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, 613*4aac3029SYoochan Jeong UFS_UPIU_QUERY_OPCODE_SET_FLAG, UFS_QUERY_FLAG_IDN_BUSY_RTC, 614*4aac3029SYoochan Jeong 0, 0, 0, &utrd, &rsp_upiu); 615*4aac3029SYoochan Jeong g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, 616*4aac3029SYoochan Jeong UFS_OCS_INVALID_CMD_TABLE_ATTR); 617*4aac3029SYoochan Jeong g_assert_cmpuint(rsp_upiu.header.response, ==, 618*4aac3029SYoochan Jeong UFS_QUERY_RESULT_NOT_WRITEABLE); 619*4aac3029SYoochan Jeong 620*4aac3029SYoochan Jeong ufs_exit(ufs, alloc); 621*4aac3029SYoochan Jeong } 622*4aac3029SYoochan Jeong 623631c8726SJeuk Kim static void drive_destroy(void *path) 624631c8726SJeuk Kim { 625631c8726SJeuk Kim unlink(path); 626631c8726SJeuk Kim g_free(path); 627631c8726SJeuk Kim qos_invalidate_command_line(); 628631c8726SJeuk Kim } 629631c8726SJeuk Kim 630631c8726SJeuk Kim static char *drive_create(void) 631631c8726SJeuk Kim { 632631c8726SJeuk Kim int fd, ret; 633631c8726SJeuk Kim char *t_path; 634631c8726SJeuk Kim 635631c8726SJeuk Kim /* Create a temporary raw image */ 636631c8726SJeuk Kim fd = g_file_open_tmp("qtest-ufs.XXXXXX", &t_path, NULL); 637631c8726SJeuk Kim g_assert_cmpint(fd, >=, 0); 638631c8726SJeuk Kim ret = ftruncate(fd, TEST_IMAGE_SIZE); 639631c8726SJeuk Kim g_assert_cmpint(ret, ==, 0); 640631c8726SJeuk Kim close(fd); 641631c8726SJeuk Kim 642631c8726SJeuk Kim g_test_queue_destroy(drive_destroy, t_path); 643631c8726SJeuk Kim return t_path; 644631c8726SJeuk Kim } 645631c8726SJeuk Kim 646631c8726SJeuk Kim static void *ufs_blk_test_setup(GString *cmd_line, void *arg) 647631c8726SJeuk Kim { 648631c8726SJeuk Kim char *tmp_path = drive_create(); 649631c8726SJeuk Kim 650631c8726SJeuk Kim g_string_append_printf(cmd_line, 651631c8726SJeuk Kim " -blockdev file,filename=%s,node-name=drv1 " 652631c8726SJeuk Kim "-device ufs-lu,bus=ufs0,drive=drv1,lun=1 ", 653631c8726SJeuk Kim tmp_path); 654631c8726SJeuk Kim 655631c8726SJeuk Kim return arg; 656631c8726SJeuk Kim } 657631c8726SJeuk Kim 658631c8726SJeuk Kim static void ufs_register_nodes(void) 659631c8726SJeuk Kim { 660631c8726SJeuk Kim const char *arch; 661631c8726SJeuk Kim QOSGraphEdgeOptions edge_opts = { 662631c8726SJeuk Kim .before_cmd_line = "-blockdev null-co,node-name=drv0,read-zeroes=on", 663631c8726SJeuk Kim .after_cmd_line = "-device ufs-lu,bus=ufs0,drive=drv0,lun=0", 664631c8726SJeuk Kim .extra_device_opts = "addr=04.0,id=ufs0,nutrs=32,nutmrs=8" 665631c8726SJeuk Kim }; 666631c8726SJeuk Kim 667631c8726SJeuk Kim QOSGraphTestOptions io_test_opts = { 668631c8726SJeuk Kim .before = ufs_blk_test_setup, 669631c8726SJeuk Kim }; 670631c8726SJeuk Kim 671631c8726SJeuk Kim add_qpci_address(&edge_opts, &(QPCIAddress){ .devfn = QPCI_DEVFN(4, 0) }); 672631c8726SJeuk Kim 673631c8726SJeuk Kim qos_node_create_driver("ufs", ufs_create); 674631c8726SJeuk Kim qos_node_consumes("ufs", "pci-bus", &edge_opts); 675631c8726SJeuk Kim qos_node_produces("ufs", "pci-device"); 676631c8726SJeuk Kim 677631c8726SJeuk Kim qos_add_test("reg-read", "ufs", ufstest_reg_read, NULL); 678631c8726SJeuk Kim 679631c8726SJeuk Kim /* 680631c8726SJeuk Kim * Check architecture 681631c8726SJeuk Kim * TODO: Enable ufs io tests for ppc64 682631c8726SJeuk Kim */ 683631c8726SJeuk Kim arch = qtest_get_arch(); 684631c8726SJeuk Kim if (!strcmp(arch, "ppc64")) { 685631c8726SJeuk Kim g_test_message("Skipping ufs io tests for ppc64"); 686631c8726SJeuk Kim return; 687631c8726SJeuk Kim } 688631c8726SJeuk Kim qos_add_test("init", "ufs", ufstest_init, NULL); 689631c8726SJeuk Kim qos_add_test("read-write", "ufs", ufstest_read_write, &io_test_opts); 690*4aac3029SYoochan Jeong qos_add_test("flag read-write", "ufs", 691*4aac3029SYoochan Jeong ufstest_query_flag_request, &io_test_opts); 692631c8726SJeuk Kim } 693631c8726SJeuk Kim 694631c8726SJeuk Kim libqos_init(ufs_register_nodes); 695