1*631c8726SJeuk Kim /* 2*631c8726SJeuk Kim * QTest testcase for UFS 3*631c8726SJeuk Kim * 4*631c8726SJeuk Kim * Copyright (c) 2023 Samsung Electronics Co., Ltd. All rights reserved. 5*631c8726SJeuk Kim * 6*631c8726SJeuk Kim * SPDX-License-Identifier: GPL-2.0-or-later 7*631c8726SJeuk Kim */ 8*631c8726SJeuk Kim 9*631c8726SJeuk Kim #include "qemu/osdep.h" 10*631c8726SJeuk Kim #include "qemu/module.h" 11*631c8726SJeuk Kim #include "qemu/units.h" 12*631c8726SJeuk Kim #include "libqtest.h" 13*631c8726SJeuk Kim #include "libqos/qgraph.h" 14*631c8726SJeuk Kim #include "libqos/pci.h" 15*631c8726SJeuk Kim #include "scsi/constants.h" 16*631c8726SJeuk Kim #include "include/block/ufs.h" 17*631c8726SJeuk Kim 18*631c8726SJeuk Kim /* Test images sizes in Bytes */ 19*631c8726SJeuk Kim #define TEST_IMAGE_SIZE (64 * 1024 * 1024) 20*631c8726SJeuk Kim /* Timeout for various operations, in seconds. */ 21*631c8726SJeuk Kim #define TIMEOUT_SECONDS 10 22*631c8726SJeuk Kim /* Maximum PRD entry count */ 23*631c8726SJeuk Kim #define MAX_PRD_ENTRY_COUNT 10 24*631c8726SJeuk Kim #define PRD_ENTRY_DATA_SIZE 4096 25*631c8726SJeuk Kim /* Constants to build upiu */ 26*631c8726SJeuk Kim #define UTP_COMMAND_DESCRIPTOR_SIZE 4096 27*631c8726SJeuk Kim #define UTP_RESPONSE_UPIU_OFFSET 1024 28*631c8726SJeuk Kim #define UTP_PRDT_UPIU_OFFSET 2048 29*631c8726SJeuk Kim 30*631c8726SJeuk Kim typedef struct QUfs QUfs; 31*631c8726SJeuk Kim 32*631c8726SJeuk Kim struct QUfs { 33*631c8726SJeuk Kim QOSGraphObject obj; 34*631c8726SJeuk Kim QPCIDevice dev; 35*631c8726SJeuk Kim QPCIBar bar; 36*631c8726SJeuk Kim 37*631c8726SJeuk Kim uint64_t utrlba; 38*631c8726SJeuk Kim uint64_t utmrlba; 39*631c8726SJeuk Kim uint64_t cmd_desc_addr; 40*631c8726SJeuk Kim uint64_t data_buffer_addr; 41*631c8726SJeuk Kim 42*631c8726SJeuk Kim bool enabled; 43*631c8726SJeuk Kim }; 44*631c8726SJeuk Kim 45*631c8726SJeuk Kim static inline uint32_t ufs_rreg(QUfs *ufs, size_t offset) 46*631c8726SJeuk Kim { 47*631c8726SJeuk Kim return qpci_io_readl(&ufs->dev, ufs->bar, offset); 48*631c8726SJeuk Kim } 49*631c8726SJeuk Kim 50*631c8726SJeuk Kim static inline void ufs_wreg(QUfs *ufs, size_t offset, uint32_t value) 51*631c8726SJeuk Kim { 52*631c8726SJeuk Kim qpci_io_writel(&ufs->dev, ufs->bar, offset, value); 53*631c8726SJeuk Kim } 54*631c8726SJeuk Kim 55*631c8726SJeuk Kim static void ufs_wait_for_irq(QUfs *ufs) 56*631c8726SJeuk Kim { 57*631c8726SJeuk Kim uint64_t end_time; 58*631c8726SJeuk Kim uint32_t is; 59*631c8726SJeuk Kim /* Wait for device to reset as the linux driver does. */ 60*631c8726SJeuk Kim end_time = g_get_monotonic_time() + TIMEOUT_SECONDS * G_TIME_SPAN_SECOND; 61*631c8726SJeuk Kim do { 62*631c8726SJeuk Kim qtest_clock_step(ufs->dev.bus->qts, 100); 63*631c8726SJeuk Kim is = ufs_rreg(ufs, A_IS); 64*631c8726SJeuk Kim } while (is == 0 && g_get_monotonic_time() < end_time); 65*631c8726SJeuk Kim } 66*631c8726SJeuk Kim 67*631c8726SJeuk Kim static UtpTransferReqDesc ufs_build_req_utrd(uint64_t cmd_desc_addr, 68*631c8726SJeuk Kim uint8_t slot, 69*631c8726SJeuk Kim uint32_t data_direction, 70*631c8726SJeuk Kim uint16_t prd_table_length) 71*631c8726SJeuk Kim { 72*631c8726SJeuk Kim UtpTransferReqDesc req = { 0 }; 73*631c8726SJeuk Kim uint64_t command_desc_base_addr = 74*631c8726SJeuk Kim cmd_desc_addr + slot * UTP_COMMAND_DESCRIPTOR_SIZE; 75*631c8726SJeuk Kim 76*631c8726SJeuk Kim req.header.dword_0 = 77*631c8726SJeuk Kim cpu_to_le32(1 << 28 | data_direction | UFS_UTP_REQ_DESC_INT_CMD); 78*631c8726SJeuk Kim req.header.dword_2 = cpu_to_le32(UFS_OCS_INVALID_COMMAND_STATUS); 79*631c8726SJeuk Kim 80*631c8726SJeuk Kim req.command_desc_base_addr_hi = cpu_to_le32(command_desc_base_addr >> 32); 81*631c8726SJeuk Kim req.command_desc_base_addr_lo = 82*631c8726SJeuk Kim cpu_to_le32(command_desc_base_addr & 0xffffffff); 83*631c8726SJeuk Kim req.response_upiu_offset = 84*631c8726SJeuk Kim cpu_to_le16(UTP_RESPONSE_UPIU_OFFSET / sizeof(uint32_t)); 85*631c8726SJeuk Kim req.response_upiu_length = cpu_to_le16(sizeof(UtpUpiuRsp)); 86*631c8726SJeuk Kim req.prd_table_offset = cpu_to_le16(UTP_PRDT_UPIU_OFFSET / sizeof(uint32_t)); 87*631c8726SJeuk Kim req.prd_table_length = cpu_to_le16(prd_table_length); 88*631c8726SJeuk Kim return req; 89*631c8726SJeuk Kim } 90*631c8726SJeuk Kim 91*631c8726SJeuk Kim static void ufs_send_nop_out(QUfs *ufs, uint8_t slot, 92*631c8726SJeuk Kim UtpTransferReqDesc *utrd_out, UtpUpiuRsp *rsp_out) 93*631c8726SJeuk Kim { 94*631c8726SJeuk Kim /* Build up utp transfer request descriptor */ 95*631c8726SJeuk Kim UtpTransferReqDesc utrd = ufs_build_req_utrd(ufs->cmd_desc_addr, slot, 96*631c8726SJeuk Kim UFS_UTP_NO_DATA_TRANSFER, 0); 97*631c8726SJeuk Kim uint64_t utrd_addr = ufs->utrlba + slot * sizeof(UtpTransferReqDesc); 98*631c8726SJeuk Kim uint64_t req_upiu_addr = 99*631c8726SJeuk Kim ufs->cmd_desc_addr + slot * UTP_COMMAND_DESCRIPTOR_SIZE; 100*631c8726SJeuk Kim uint64_t rsp_upiu_addr = req_upiu_addr + UTP_RESPONSE_UPIU_OFFSET; 101*631c8726SJeuk Kim qtest_memwrite(ufs->dev.bus->qts, utrd_addr, &utrd, sizeof(utrd)); 102*631c8726SJeuk Kim 103*631c8726SJeuk Kim /* Build up request upiu */ 104*631c8726SJeuk Kim UtpUpiuReq req_upiu = { 0 }; 105*631c8726SJeuk Kim req_upiu.header.trans_type = UFS_UPIU_TRANSACTION_NOP_OUT; 106*631c8726SJeuk Kim req_upiu.header.task_tag = slot; 107*631c8726SJeuk Kim qtest_memwrite(ufs->dev.bus->qts, req_upiu_addr, &req_upiu, 108*631c8726SJeuk Kim sizeof(req_upiu)); 109*631c8726SJeuk Kim 110*631c8726SJeuk Kim /* Ring Doorbell */ 111*631c8726SJeuk Kim ufs_wreg(ufs, A_UTRLDBR, 1); 112*631c8726SJeuk Kim ufs_wait_for_irq(ufs); 113*631c8726SJeuk Kim g_assert_true(FIELD_EX32(ufs_rreg(ufs, A_IS), IS, UTRCS)); 114*631c8726SJeuk Kim ufs_wreg(ufs, A_IS, FIELD_DP32(0, IS, UTRCS, 1)); 115*631c8726SJeuk Kim 116*631c8726SJeuk Kim qtest_memread(ufs->dev.bus->qts, utrd_addr, utrd_out, sizeof(*utrd_out)); 117*631c8726SJeuk Kim qtest_memread(ufs->dev.bus->qts, rsp_upiu_addr, rsp_out, sizeof(*rsp_out)); 118*631c8726SJeuk Kim } 119*631c8726SJeuk Kim 120*631c8726SJeuk Kim static void ufs_send_query(QUfs *ufs, uint8_t slot, uint8_t query_function, 121*631c8726SJeuk Kim uint8_t query_opcode, uint8_t idn, uint8_t index, 122*631c8726SJeuk Kim UtpTransferReqDesc *utrd_out, UtpUpiuRsp *rsp_out) 123*631c8726SJeuk Kim { 124*631c8726SJeuk Kim /* Build up utp transfer request descriptor */ 125*631c8726SJeuk Kim UtpTransferReqDesc utrd = ufs_build_req_utrd(ufs->cmd_desc_addr, slot, 126*631c8726SJeuk Kim UFS_UTP_NO_DATA_TRANSFER, 0); 127*631c8726SJeuk Kim uint64_t utrd_addr = ufs->utrlba + slot * sizeof(UtpTransferReqDesc); 128*631c8726SJeuk Kim uint64_t req_upiu_addr = 129*631c8726SJeuk Kim ufs->cmd_desc_addr + slot * UTP_COMMAND_DESCRIPTOR_SIZE; 130*631c8726SJeuk Kim uint64_t rsp_upiu_addr = req_upiu_addr + UTP_RESPONSE_UPIU_OFFSET; 131*631c8726SJeuk Kim qtest_memwrite(ufs->dev.bus->qts, utrd_addr, &utrd, sizeof(utrd)); 132*631c8726SJeuk Kim 133*631c8726SJeuk Kim /* Build up request upiu */ 134*631c8726SJeuk Kim UtpUpiuReq req_upiu = { 0 }; 135*631c8726SJeuk Kim req_upiu.header.trans_type = UFS_UPIU_TRANSACTION_QUERY_REQ; 136*631c8726SJeuk Kim req_upiu.header.query_func = query_function; 137*631c8726SJeuk Kim req_upiu.header.task_tag = slot; 138*631c8726SJeuk Kim /* 139*631c8726SJeuk Kim * QEMU UFS does not currently support Write descriptor and Write attribute, 140*631c8726SJeuk Kim * so the value of data_segment_length is always 0. 141*631c8726SJeuk Kim */ 142*631c8726SJeuk Kim req_upiu.header.data_segment_length = 0; 143*631c8726SJeuk Kim req_upiu.qr.opcode = query_opcode; 144*631c8726SJeuk Kim req_upiu.qr.idn = idn; 145*631c8726SJeuk Kim req_upiu.qr.index = index; 146*631c8726SJeuk Kim qtest_memwrite(ufs->dev.bus->qts, req_upiu_addr, &req_upiu, 147*631c8726SJeuk Kim sizeof(req_upiu)); 148*631c8726SJeuk Kim 149*631c8726SJeuk Kim /* Ring Doorbell */ 150*631c8726SJeuk Kim ufs_wreg(ufs, A_UTRLDBR, 1); 151*631c8726SJeuk Kim ufs_wait_for_irq(ufs); 152*631c8726SJeuk Kim g_assert_true(FIELD_EX32(ufs_rreg(ufs, A_IS), IS, UTRCS)); 153*631c8726SJeuk Kim ufs_wreg(ufs, A_IS, FIELD_DP32(0, IS, UTRCS, 1)); 154*631c8726SJeuk Kim 155*631c8726SJeuk Kim qtest_memread(ufs->dev.bus->qts, utrd_addr, utrd_out, sizeof(*utrd_out)); 156*631c8726SJeuk Kim qtest_memread(ufs->dev.bus->qts, rsp_upiu_addr, rsp_out, sizeof(*rsp_out)); 157*631c8726SJeuk Kim } 158*631c8726SJeuk Kim 159*631c8726SJeuk Kim static void ufs_send_scsi_command(QUfs *ufs, uint8_t slot, uint8_t lun, 160*631c8726SJeuk Kim const uint8_t *cdb, const uint8_t *data_in, 161*631c8726SJeuk Kim size_t data_in_len, uint8_t *data_out, 162*631c8726SJeuk Kim size_t data_out_len, 163*631c8726SJeuk Kim UtpTransferReqDesc *utrd_out, 164*631c8726SJeuk Kim UtpUpiuRsp *rsp_out) 165*631c8726SJeuk Kim 166*631c8726SJeuk Kim { 167*631c8726SJeuk Kim /* Build up PRDT */ 168*631c8726SJeuk Kim UfshcdSgEntry entries[MAX_PRD_ENTRY_COUNT] = { 169*631c8726SJeuk Kim 0, 170*631c8726SJeuk Kim }; 171*631c8726SJeuk Kim uint8_t flags; 172*631c8726SJeuk Kim uint16_t prd_table_length, i; 173*631c8726SJeuk Kim uint32_t data_direction, data_len; 174*631c8726SJeuk Kim uint64_t req_upiu_addr = 175*631c8726SJeuk Kim ufs->cmd_desc_addr + slot * UTP_COMMAND_DESCRIPTOR_SIZE; 176*631c8726SJeuk Kim uint64_t prdt_addr = req_upiu_addr + UTP_PRDT_UPIU_OFFSET; 177*631c8726SJeuk Kim 178*631c8726SJeuk Kim g_assert_true(data_in_len < MAX_PRD_ENTRY_COUNT * PRD_ENTRY_DATA_SIZE); 179*631c8726SJeuk Kim g_assert_true(data_out_len < MAX_PRD_ENTRY_COUNT * PRD_ENTRY_DATA_SIZE); 180*631c8726SJeuk Kim if (data_in_len > 0) { 181*631c8726SJeuk Kim g_assert_nonnull(data_in); 182*631c8726SJeuk Kim data_direction = UFS_UTP_HOST_TO_DEVICE; 183*631c8726SJeuk Kim data_len = data_in_len; 184*631c8726SJeuk Kim flags = UFS_UPIU_CMD_FLAGS_WRITE; 185*631c8726SJeuk Kim } else if (data_out_len > 0) { 186*631c8726SJeuk Kim g_assert_nonnull(data_out); 187*631c8726SJeuk Kim data_direction = UFS_UTP_DEVICE_TO_HOST; 188*631c8726SJeuk Kim data_len = data_out_len; 189*631c8726SJeuk Kim flags = UFS_UPIU_CMD_FLAGS_READ; 190*631c8726SJeuk Kim } else { 191*631c8726SJeuk Kim data_direction = UFS_UTP_NO_DATA_TRANSFER; 192*631c8726SJeuk Kim data_len = 0; 193*631c8726SJeuk Kim flags = UFS_UPIU_CMD_FLAGS_NONE; 194*631c8726SJeuk Kim } 195*631c8726SJeuk Kim prd_table_length = DIV_ROUND_UP(data_len, PRD_ENTRY_DATA_SIZE); 196*631c8726SJeuk Kim 197*631c8726SJeuk Kim qtest_memset(ufs->dev.bus->qts, ufs->data_buffer_addr, 0, 198*631c8726SJeuk Kim MAX_PRD_ENTRY_COUNT * PRD_ENTRY_DATA_SIZE); 199*631c8726SJeuk Kim if (data_in_len) { 200*631c8726SJeuk Kim qtest_memwrite(ufs->dev.bus->qts, ufs->data_buffer_addr, data_in, 201*631c8726SJeuk Kim data_in_len); 202*631c8726SJeuk Kim } 203*631c8726SJeuk Kim 204*631c8726SJeuk Kim for (i = 0; i < prd_table_length; i++) { 205*631c8726SJeuk Kim entries[i].addr = 206*631c8726SJeuk Kim cpu_to_le64(ufs->data_buffer_addr + i * sizeof(UfshcdSgEntry)); 207*631c8726SJeuk Kim if (i + 1 != prd_table_length) { 208*631c8726SJeuk Kim entries[i].size = cpu_to_le32(PRD_ENTRY_DATA_SIZE - 1); 209*631c8726SJeuk Kim } else { 210*631c8726SJeuk Kim entries[i].size = cpu_to_le32( 211*631c8726SJeuk Kim data_len - (PRD_ENTRY_DATA_SIZE * (prd_table_length - 1)) - 1); 212*631c8726SJeuk Kim } 213*631c8726SJeuk Kim } 214*631c8726SJeuk Kim qtest_memwrite(ufs->dev.bus->qts, prdt_addr, entries, 215*631c8726SJeuk Kim prd_table_length * sizeof(UfshcdSgEntry)); 216*631c8726SJeuk Kim 217*631c8726SJeuk Kim /* Build up utp transfer request descriptor */ 218*631c8726SJeuk Kim UtpTransferReqDesc utrd = ufs_build_req_utrd( 219*631c8726SJeuk Kim ufs->cmd_desc_addr, slot, data_direction, prd_table_length); 220*631c8726SJeuk Kim uint64_t utrd_addr = ufs->utrlba + slot * sizeof(UtpTransferReqDesc); 221*631c8726SJeuk Kim uint64_t rsp_upiu_addr = req_upiu_addr + UTP_RESPONSE_UPIU_OFFSET; 222*631c8726SJeuk Kim qtest_memwrite(ufs->dev.bus->qts, utrd_addr, &utrd, sizeof(utrd)); 223*631c8726SJeuk Kim 224*631c8726SJeuk Kim /* Build up request upiu */ 225*631c8726SJeuk Kim UtpUpiuReq req_upiu = { 0 }; 226*631c8726SJeuk Kim req_upiu.header.trans_type = UFS_UPIU_TRANSACTION_COMMAND; 227*631c8726SJeuk Kim req_upiu.header.flags = flags; 228*631c8726SJeuk Kim req_upiu.header.lun = lun; 229*631c8726SJeuk Kim req_upiu.header.task_tag = slot; 230*631c8726SJeuk Kim req_upiu.sc.exp_data_transfer_len = cpu_to_be32(data_len); 231*631c8726SJeuk Kim memcpy(req_upiu.sc.cdb, cdb, UFS_CDB_SIZE); 232*631c8726SJeuk Kim qtest_memwrite(ufs->dev.bus->qts, req_upiu_addr, &req_upiu, 233*631c8726SJeuk Kim sizeof(req_upiu)); 234*631c8726SJeuk Kim 235*631c8726SJeuk Kim /* Ring Doorbell */ 236*631c8726SJeuk Kim ufs_wreg(ufs, A_UTRLDBR, 1); 237*631c8726SJeuk Kim ufs_wait_for_irq(ufs); 238*631c8726SJeuk Kim g_assert_true(FIELD_EX32(ufs_rreg(ufs, A_IS), IS, UTRCS)); 239*631c8726SJeuk Kim ufs_wreg(ufs, A_IS, FIELD_DP32(0, IS, UTRCS, 1)); 240*631c8726SJeuk Kim 241*631c8726SJeuk Kim qtest_memread(ufs->dev.bus->qts, utrd_addr, utrd_out, sizeof(*utrd_out)); 242*631c8726SJeuk Kim qtest_memread(ufs->dev.bus->qts, rsp_upiu_addr, rsp_out, sizeof(*rsp_out)); 243*631c8726SJeuk Kim if (data_out_len) { 244*631c8726SJeuk Kim qtest_memread(ufs->dev.bus->qts, ufs->data_buffer_addr, data_out, 245*631c8726SJeuk Kim data_out_len); 246*631c8726SJeuk Kim } 247*631c8726SJeuk Kim } 248*631c8726SJeuk Kim 249*631c8726SJeuk Kim /** 250*631c8726SJeuk Kim * Initialize Ufs host controller and logical unit. 251*631c8726SJeuk Kim * After running this function, you can make a transfer request to the UFS. 252*631c8726SJeuk Kim */ 253*631c8726SJeuk Kim static void ufs_init(QUfs *ufs, QGuestAllocator *alloc) 254*631c8726SJeuk Kim { 255*631c8726SJeuk Kim uint64_t end_time; 256*631c8726SJeuk Kim uint32_t nutrs, nutmrs; 257*631c8726SJeuk Kim uint32_t hcs, is, ucmdarg2, cap; 258*631c8726SJeuk Kim uint32_t hce = 0, ie = 0; 259*631c8726SJeuk Kim UtpTransferReqDesc utrd; 260*631c8726SJeuk Kim UtpUpiuRsp rsp_upiu; 261*631c8726SJeuk Kim 262*631c8726SJeuk Kim ufs->bar = qpci_iomap(&ufs->dev, 0, NULL); 263*631c8726SJeuk Kim qpci_device_enable(&ufs->dev); 264*631c8726SJeuk Kim 265*631c8726SJeuk Kim /* Start host controller initialization */ 266*631c8726SJeuk Kim hce = FIELD_DP32(hce, HCE, HCE, 1); 267*631c8726SJeuk Kim ufs_wreg(ufs, A_HCE, hce); 268*631c8726SJeuk Kim 269*631c8726SJeuk Kim /* Wait for device to reset */ 270*631c8726SJeuk Kim end_time = g_get_monotonic_time() + TIMEOUT_SECONDS * G_TIME_SPAN_SECOND; 271*631c8726SJeuk Kim do { 272*631c8726SJeuk Kim qtest_clock_step(ufs->dev.bus->qts, 100); 273*631c8726SJeuk Kim hce = FIELD_EX32(ufs_rreg(ufs, A_HCE), HCE, HCE); 274*631c8726SJeuk Kim } while (hce == 0 && g_get_monotonic_time() < end_time); 275*631c8726SJeuk Kim g_assert_cmpuint(hce, ==, 1); 276*631c8726SJeuk Kim 277*631c8726SJeuk Kim /* Enable interrupt */ 278*631c8726SJeuk Kim ie = FIELD_DP32(ie, IE, UCCE, 1); 279*631c8726SJeuk Kim ie = FIELD_DP32(ie, IE, UHESE, 1); 280*631c8726SJeuk Kim ie = FIELD_DP32(ie, IE, UHXSE, 1); 281*631c8726SJeuk Kim ie = FIELD_DP32(ie, IE, UPMSE, 1); 282*631c8726SJeuk Kim ufs_wreg(ufs, A_IE, ie); 283*631c8726SJeuk Kim 284*631c8726SJeuk Kim /* Send DME_LINK_STARTUP uic command */ 285*631c8726SJeuk Kim hcs = ufs_rreg(ufs, A_HCS); 286*631c8726SJeuk Kim g_assert_true(FIELD_EX32(hcs, HCS, UCRDY)); 287*631c8726SJeuk Kim 288*631c8726SJeuk Kim ufs_wreg(ufs, A_UCMDARG1, 0); 289*631c8726SJeuk Kim ufs_wreg(ufs, A_UCMDARG2, 0); 290*631c8726SJeuk Kim ufs_wreg(ufs, A_UCMDARG3, 0); 291*631c8726SJeuk Kim ufs_wreg(ufs, A_UICCMD, UFS_UIC_CMD_DME_LINK_STARTUP); 292*631c8726SJeuk Kim 293*631c8726SJeuk Kim is = ufs_rreg(ufs, A_IS); 294*631c8726SJeuk Kim g_assert_true(FIELD_EX32(is, IS, UCCS)); 295*631c8726SJeuk Kim ufs_wreg(ufs, A_IS, FIELD_DP32(0, IS, UCCS, 1)); 296*631c8726SJeuk Kim 297*631c8726SJeuk Kim ucmdarg2 = ufs_rreg(ufs, A_UCMDARG2); 298*631c8726SJeuk Kim g_assert_cmpuint(ucmdarg2, ==, 0); 299*631c8726SJeuk Kim is = ufs_rreg(ufs, A_IS); 300*631c8726SJeuk Kim g_assert_cmpuint(is, ==, 0); 301*631c8726SJeuk Kim hcs = ufs_rreg(ufs, A_HCS); 302*631c8726SJeuk Kim g_assert_true(FIELD_EX32(hcs, HCS, DP)); 303*631c8726SJeuk Kim g_assert_true(FIELD_EX32(hcs, HCS, UTRLRDY)); 304*631c8726SJeuk Kim g_assert_true(FIELD_EX32(hcs, HCS, UTMRLRDY)); 305*631c8726SJeuk Kim g_assert_true(FIELD_EX32(hcs, HCS, UCRDY)); 306*631c8726SJeuk Kim 307*631c8726SJeuk Kim /* Enable all interrupt functions */ 308*631c8726SJeuk Kim ie = FIELD_DP32(ie, IE, UTRCE, 1); 309*631c8726SJeuk Kim ie = FIELD_DP32(ie, IE, UEE, 1); 310*631c8726SJeuk Kim ie = FIELD_DP32(ie, IE, UPMSE, 1); 311*631c8726SJeuk Kim ie = FIELD_DP32(ie, IE, UHXSE, 1); 312*631c8726SJeuk Kim ie = FIELD_DP32(ie, IE, UHESE, 1); 313*631c8726SJeuk Kim ie = FIELD_DP32(ie, IE, UTMRCE, 1); 314*631c8726SJeuk Kim ie = FIELD_DP32(ie, IE, UCCE, 1); 315*631c8726SJeuk Kim ie = FIELD_DP32(ie, IE, DFEE, 1); 316*631c8726SJeuk Kim ie = FIELD_DP32(ie, IE, HCFEE, 1); 317*631c8726SJeuk Kim ie = FIELD_DP32(ie, IE, SBFEE, 1); 318*631c8726SJeuk Kim ie = FIELD_DP32(ie, IE, CEFEE, 1); 319*631c8726SJeuk Kim ufs_wreg(ufs, A_IE, ie); 320*631c8726SJeuk Kim ufs_wreg(ufs, A_UTRIACR, 0); 321*631c8726SJeuk Kim 322*631c8726SJeuk Kim /* Enable tranfer request and task management request */ 323*631c8726SJeuk Kim cap = ufs_rreg(ufs, A_CAP); 324*631c8726SJeuk Kim nutrs = FIELD_EX32(cap, CAP, NUTRS) + 1; 325*631c8726SJeuk Kim nutmrs = FIELD_EX32(cap, CAP, NUTMRS) + 1; 326*631c8726SJeuk Kim ufs->cmd_desc_addr = 327*631c8726SJeuk Kim guest_alloc(alloc, nutrs * UTP_COMMAND_DESCRIPTOR_SIZE); 328*631c8726SJeuk Kim ufs->data_buffer_addr = 329*631c8726SJeuk Kim guest_alloc(alloc, MAX_PRD_ENTRY_COUNT * PRD_ENTRY_DATA_SIZE); 330*631c8726SJeuk Kim ufs->utrlba = guest_alloc(alloc, nutrs * sizeof(UtpTransferReqDesc)); 331*631c8726SJeuk Kim ufs->utmrlba = guest_alloc(alloc, nutmrs * sizeof(UtpTaskReqDesc)); 332*631c8726SJeuk Kim 333*631c8726SJeuk Kim ufs_wreg(ufs, A_UTRLBA, ufs->utrlba & 0xffffffff); 334*631c8726SJeuk Kim ufs_wreg(ufs, A_UTRLBAU, ufs->utrlba >> 32); 335*631c8726SJeuk Kim ufs_wreg(ufs, A_UTMRLBA, ufs->utmrlba & 0xffffffff); 336*631c8726SJeuk Kim ufs_wreg(ufs, A_UTMRLBAU, ufs->utmrlba >> 32); 337*631c8726SJeuk Kim ufs_wreg(ufs, A_UTRLRSR, 1); 338*631c8726SJeuk Kim ufs_wreg(ufs, A_UTMRLRSR, 1); 339*631c8726SJeuk Kim 340*631c8726SJeuk Kim /* Send nop out to test transfer request */ 341*631c8726SJeuk Kim ufs_send_nop_out(ufs, 0, &utrd, &rsp_upiu); 342*631c8726SJeuk Kim g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); 343*631c8726SJeuk Kim 344*631c8726SJeuk Kim /* Set fDeviceInit flag via query request */ 345*631c8726SJeuk Kim ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, 346*631c8726SJeuk Kim UFS_UPIU_QUERY_OPCODE_SET_FLAG, 347*631c8726SJeuk Kim UFS_QUERY_FLAG_IDN_FDEVICEINIT, 0, &utrd, &rsp_upiu); 348*631c8726SJeuk Kim g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); 349*631c8726SJeuk Kim 350*631c8726SJeuk Kim /* Wait for device to reset */ 351*631c8726SJeuk Kim end_time = g_get_monotonic_time() + TIMEOUT_SECONDS * G_TIME_SPAN_SECOND; 352*631c8726SJeuk Kim do { 353*631c8726SJeuk Kim qtest_clock_step(ufs->dev.bus->qts, 100); 354*631c8726SJeuk Kim ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, 355*631c8726SJeuk Kim UFS_UPIU_QUERY_OPCODE_READ_FLAG, 356*631c8726SJeuk Kim UFS_QUERY_FLAG_IDN_FDEVICEINIT, 0, &utrd, &rsp_upiu); 357*631c8726SJeuk Kim } while (be32_to_cpu(rsp_upiu.qr.value) != 0 && 358*631c8726SJeuk Kim g_get_monotonic_time() < end_time); 359*631c8726SJeuk Kim g_assert_cmpuint(be32_to_cpu(rsp_upiu.qr.value), ==, 0); 360*631c8726SJeuk Kim 361*631c8726SJeuk Kim ufs->enabled = true; 362*631c8726SJeuk Kim } 363*631c8726SJeuk Kim 364*631c8726SJeuk Kim static void ufs_exit(QUfs *ufs, QGuestAllocator *alloc) 365*631c8726SJeuk Kim { 366*631c8726SJeuk Kim if (ufs->enabled) { 367*631c8726SJeuk Kim guest_free(alloc, ufs->utrlba); 368*631c8726SJeuk Kim guest_free(alloc, ufs->utmrlba); 369*631c8726SJeuk Kim guest_free(alloc, ufs->cmd_desc_addr); 370*631c8726SJeuk Kim guest_free(alloc, ufs->data_buffer_addr); 371*631c8726SJeuk Kim } 372*631c8726SJeuk Kim 373*631c8726SJeuk Kim qpci_iounmap(&ufs->dev, ufs->bar); 374*631c8726SJeuk Kim } 375*631c8726SJeuk Kim 376*631c8726SJeuk Kim static void *ufs_get_driver(void *obj, const char *interface) 377*631c8726SJeuk Kim { 378*631c8726SJeuk Kim QUfs *ufs = obj; 379*631c8726SJeuk Kim 380*631c8726SJeuk Kim if (!g_strcmp0(interface, "pci-device")) { 381*631c8726SJeuk Kim return &ufs->dev; 382*631c8726SJeuk Kim } 383*631c8726SJeuk Kim 384*631c8726SJeuk Kim fprintf(stderr, "%s not present in ufs\n", interface); 385*631c8726SJeuk Kim g_assert_not_reached(); 386*631c8726SJeuk Kim } 387*631c8726SJeuk Kim 388*631c8726SJeuk Kim static void *ufs_create(void *pci_bus, QGuestAllocator *alloc, void *addr) 389*631c8726SJeuk Kim { 390*631c8726SJeuk Kim QUfs *ufs = g_new0(QUfs, 1); 391*631c8726SJeuk Kim QPCIBus *bus = pci_bus; 392*631c8726SJeuk Kim 393*631c8726SJeuk Kim qpci_device_init(&ufs->dev, bus, addr); 394*631c8726SJeuk Kim ufs->obj.get_driver = ufs_get_driver; 395*631c8726SJeuk Kim 396*631c8726SJeuk Kim return &ufs->obj; 397*631c8726SJeuk Kim } 398*631c8726SJeuk Kim 399*631c8726SJeuk Kim static void ufstest_reg_read(void *obj, void *data, QGuestAllocator *alloc) 400*631c8726SJeuk Kim { 401*631c8726SJeuk Kim QUfs *ufs = obj; 402*631c8726SJeuk Kim uint32_t cap; 403*631c8726SJeuk Kim 404*631c8726SJeuk Kim ufs->bar = qpci_iomap(&ufs->dev, 0, NULL); 405*631c8726SJeuk Kim qpci_device_enable(&ufs->dev); 406*631c8726SJeuk Kim 407*631c8726SJeuk Kim cap = ufs_rreg(ufs, A_CAP); 408*631c8726SJeuk Kim g_assert_cmpuint(FIELD_EX32(cap, CAP, NUTRS), ==, 31); 409*631c8726SJeuk Kim g_assert_cmpuint(FIELD_EX32(cap, CAP, NUTMRS), ==, 7); 410*631c8726SJeuk Kim g_assert_cmpuint(FIELD_EX32(cap, CAP, 64AS), ==, 1); 411*631c8726SJeuk Kim 412*631c8726SJeuk Kim qpci_iounmap(&ufs->dev, ufs->bar); 413*631c8726SJeuk Kim } 414*631c8726SJeuk Kim 415*631c8726SJeuk Kim static void ufstest_init(void *obj, void *data, QGuestAllocator *alloc) 416*631c8726SJeuk Kim { 417*631c8726SJeuk Kim QUfs *ufs = obj; 418*631c8726SJeuk Kim 419*631c8726SJeuk Kim uint8_t buf[4096] = { 0 }; 420*631c8726SJeuk Kim const uint8_t report_luns_cdb[UFS_CDB_SIZE] = { 421*631c8726SJeuk Kim /* allocation length 4096 */ 422*631c8726SJeuk Kim REPORT_LUNS, 0x00, 0x00, 0x00, 0x00, 0x00, 423*631c8726SJeuk Kim 0x00, 0x00, 0x10, 0x00, 0x00, 0x00 424*631c8726SJeuk Kim }; 425*631c8726SJeuk Kim const uint8_t test_unit_ready_cdb[UFS_CDB_SIZE] = { 426*631c8726SJeuk Kim TEST_UNIT_READY, 427*631c8726SJeuk Kim }; 428*631c8726SJeuk Kim UtpTransferReqDesc utrd; 429*631c8726SJeuk Kim UtpUpiuRsp rsp_upiu; 430*631c8726SJeuk Kim 431*631c8726SJeuk Kim ufs_init(ufs, alloc); 432*631c8726SJeuk Kim 433*631c8726SJeuk Kim /* Check REPORT_LUNS */ 434*631c8726SJeuk Kim ufs_send_scsi_command(ufs, 0, 0, report_luns_cdb, NULL, 0, buf, sizeof(buf), 435*631c8726SJeuk Kim &utrd, &rsp_upiu); 436*631c8726SJeuk Kim g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); 437*631c8726SJeuk Kim g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, GOOD); 438*631c8726SJeuk Kim /* LUN LIST LENGTH should be 8, in big endian */ 439*631c8726SJeuk Kim g_assert_cmpuint(buf[3], ==, 8); 440*631c8726SJeuk Kim /* There is one logical unit whose lun is 0 */ 441*631c8726SJeuk Kim g_assert_cmpuint(buf[9], ==, 0); 442*631c8726SJeuk Kim 443*631c8726SJeuk Kim /* Check TEST_UNIT_READY */ 444*631c8726SJeuk Kim ufs_send_scsi_command(ufs, 0, 0, test_unit_ready_cdb, NULL, 0, NULL, 0, 445*631c8726SJeuk Kim &utrd, &rsp_upiu); 446*631c8726SJeuk Kim g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); 447*631c8726SJeuk Kim g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, GOOD); 448*631c8726SJeuk Kim 449*631c8726SJeuk Kim ufs_exit(ufs, alloc); 450*631c8726SJeuk Kim } 451*631c8726SJeuk Kim 452*631c8726SJeuk Kim static void ufstest_read_write(void *obj, void *data, QGuestAllocator *alloc) 453*631c8726SJeuk Kim { 454*631c8726SJeuk Kim QUfs *ufs = obj; 455*631c8726SJeuk Kim uint8_t read_buf[4096] = { 0 }; 456*631c8726SJeuk Kim uint8_t write_buf[4096] = { 0 }; 457*631c8726SJeuk Kim const uint8_t read_capacity_cdb[UFS_CDB_SIZE] = { 458*631c8726SJeuk Kim /* allocation length 4096 */ 459*631c8726SJeuk Kim SERVICE_ACTION_IN_16, 460*631c8726SJeuk Kim SAI_READ_CAPACITY_16, 461*631c8726SJeuk Kim 0x00, 462*631c8726SJeuk Kim 0x00, 463*631c8726SJeuk Kim 0x00, 464*631c8726SJeuk Kim 0x00, 465*631c8726SJeuk Kim 0x00, 466*631c8726SJeuk Kim 0x00, 467*631c8726SJeuk Kim 0x00, 468*631c8726SJeuk Kim 0x00, 469*631c8726SJeuk Kim 0x00, 470*631c8726SJeuk Kim 0x00, 471*631c8726SJeuk Kim 0x10, 472*631c8726SJeuk Kim 0x00, 473*631c8726SJeuk Kim 0x00, 474*631c8726SJeuk Kim 0x00 475*631c8726SJeuk Kim }; 476*631c8726SJeuk Kim const uint8_t read_cdb[UFS_CDB_SIZE] = { 477*631c8726SJeuk Kim /* READ(10) to LBA 0, transfer length 1 */ 478*631c8726SJeuk Kim READ_10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00 479*631c8726SJeuk Kim }; 480*631c8726SJeuk Kim const uint8_t write_cdb[UFS_CDB_SIZE] = { 481*631c8726SJeuk Kim /* WRITE(10) to LBA 0, transfer length 1 */ 482*631c8726SJeuk Kim WRITE_10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00 483*631c8726SJeuk Kim }; 484*631c8726SJeuk Kim uint32_t block_size; 485*631c8726SJeuk Kim UtpTransferReqDesc utrd; 486*631c8726SJeuk Kim UtpUpiuRsp rsp_upiu; 487*631c8726SJeuk Kim 488*631c8726SJeuk Kim ufs_init(ufs, alloc); 489*631c8726SJeuk Kim 490*631c8726SJeuk Kim /* Read capacity */ 491*631c8726SJeuk Kim ufs_send_scsi_command(ufs, 0, 1, read_capacity_cdb, NULL, 0, read_buf, 492*631c8726SJeuk Kim sizeof(read_buf), &utrd, &rsp_upiu); 493*631c8726SJeuk Kim g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); 494*631c8726SJeuk Kim g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, 495*631c8726SJeuk Kim UFS_COMMAND_RESULT_SUCESS); 496*631c8726SJeuk Kim block_size = ldl_be_p(&read_buf[8]); 497*631c8726SJeuk Kim g_assert_cmpuint(block_size, ==, 4096); 498*631c8726SJeuk Kim 499*631c8726SJeuk Kim /* Write data */ 500*631c8726SJeuk Kim memset(write_buf, rand() % 255 + 1, block_size); 501*631c8726SJeuk Kim ufs_send_scsi_command(ufs, 0, 1, write_cdb, write_buf, block_size, NULL, 0, 502*631c8726SJeuk Kim &utrd, &rsp_upiu); 503*631c8726SJeuk Kim g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); 504*631c8726SJeuk Kim g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, 505*631c8726SJeuk Kim UFS_COMMAND_RESULT_SUCESS); 506*631c8726SJeuk Kim 507*631c8726SJeuk Kim /* Read data and verify */ 508*631c8726SJeuk Kim ufs_send_scsi_command(ufs, 0, 1, read_cdb, NULL, 0, read_buf, block_size, 509*631c8726SJeuk Kim &utrd, &rsp_upiu); 510*631c8726SJeuk Kim g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); 511*631c8726SJeuk Kim g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, 512*631c8726SJeuk Kim UFS_COMMAND_RESULT_SUCESS); 513*631c8726SJeuk Kim g_assert_cmpint(memcmp(read_buf, write_buf, block_size), ==, 0); 514*631c8726SJeuk Kim 515*631c8726SJeuk Kim ufs_exit(ufs, alloc); 516*631c8726SJeuk Kim } 517*631c8726SJeuk Kim 518*631c8726SJeuk Kim static void drive_destroy(void *path) 519*631c8726SJeuk Kim { 520*631c8726SJeuk Kim unlink(path); 521*631c8726SJeuk Kim g_free(path); 522*631c8726SJeuk Kim qos_invalidate_command_line(); 523*631c8726SJeuk Kim } 524*631c8726SJeuk Kim 525*631c8726SJeuk Kim static char *drive_create(void) 526*631c8726SJeuk Kim { 527*631c8726SJeuk Kim int fd, ret; 528*631c8726SJeuk Kim char *t_path; 529*631c8726SJeuk Kim 530*631c8726SJeuk Kim /* Create a temporary raw image */ 531*631c8726SJeuk Kim fd = g_file_open_tmp("qtest-ufs.XXXXXX", &t_path, NULL); 532*631c8726SJeuk Kim g_assert_cmpint(fd, >=, 0); 533*631c8726SJeuk Kim ret = ftruncate(fd, TEST_IMAGE_SIZE); 534*631c8726SJeuk Kim g_assert_cmpint(ret, ==, 0); 535*631c8726SJeuk Kim close(fd); 536*631c8726SJeuk Kim 537*631c8726SJeuk Kim g_test_queue_destroy(drive_destroy, t_path); 538*631c8726SJeuk Kim return t_path; 539*631c8726SJeuk Kim } 540*631c8726SJeuk Kim 541*631c8726SJeuk Kim static void *ufs_blk_test_setup(GString *cmd_line, void *arg) 542*631c8726SJeuk Kim { 543*631c8726SJeuk Kim char *tmp_path = drive_create(); 544*631c8726SJeuk Kim 545*631c8726SJeuk Kim g_string_append_printf(cmd_line, 546*631c8726SJeuk Kim " -blockdev file,filename=%s,node-name=drv1 " 547*631c8726SJeuk Kim "-device ufs-lu,bus=ufs0,drive=drv1,lun=1 ", 548*631c8726SJeuk Kim tmp_path); 549*631c8726SJeuk Kim 550*631c8726SJeuk Kim return arg; 551*631c8726SJeuk Kim } 552*631c8726SJeuk Kim 553*631c8726SJeuk Kim static void ufs_register_nodes(void) 554*631c8726SJeuk Kim { 555*631c8726SJeuk Kim const char *arch; 556*631c8726SJeuk Kim QOSGraphEdgeOptions edge_opts = { 557*631c8726SJeuk Kim .before_cmd_line = "-blockdev null-co,node-name=drv0,read-zeroes=on", 558*631c8726SJeuk Kim .after_cmd_line = "-device ufs-lu,bus=ufs0,drive=drv0,lun=0", 559*631c8726SJeuk Kim .extra_device_opts = "addr=04.0,id=ufs0,nutrs=32,nutmrs=8" 560*631c8726SJeuk Kim }; 561*631c8726SJeuk Kim 562*631c8726SJeuk Kim QOSGraphTestOptions io_test_opts = { 563*631c8726SJeuk Kim .before = ufs_blk_test_setup, 564*631c8726SJeuk Kim }; 565*631c8726SJeuk Kim 566*631c8726SJeuk Kim add_qpci_address(&edge_opts, &(QPCIAddress){ .devfn = QPCI_DEVFN(4, 0) }); 567*631c8726SJeuk Kim 568*631c8726SJeuk Kim qos_node_create_driver("ufs", ufs_create); 569*631c8726SJeuk Kim qos_node_consumes("ufs", "pci-bus", &edge_opts); 570*631c8726SJeuk Kim qos_node_produces("ufs", "pci-device"); 571*631c8726SJeuk Kim 572*631c8726SJeuk Kim qos_add_test("reg-read", "ufs", ufstest_reg_read, NULL); 573*631c8726SJeuk Kim 574*631c8726SJeuk Kim /* 575*631c8726SJeuk Kim * Check architecture 576*631c8726SJeuk Kim * TODO: Enable ufs io tests for ppc64 577*631c8726SJeuk Kim */ 578*631c8726SJeuk Kim arch = qtest_get_arch(); 579*631c8726SJeuk Kim if (!strcmp(arch, "ppc64")) { 580*631c8726SJeuk Kim g_test_message("Skipping ufs io tests for ppc64"); 581*631c8726SJeuk Kim return; 582*631c8726SJeuk Kim } 583*631c8726SJeuk Kim qos_add_test("init", "ufs", ufstest_init, NULL); 584*631c8726SJeuk Kim qos_add_test("read-write", "ufs", ufstest_read_write, &io_test_opts); 585*631c8726SJeuk Kim } 586*631c8726SJeuk Kim 587*631c8726SJeuk Kim libqos_init(ufs_register_nodes); 588