xref: /qemu/tests/qtest/ufs-test.c (revision 8b4d80bb)
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 
ufs_rreg(QUfs * ufs,size_t offset)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 
ufs_wreg(QUfs * ufs,size_t offset,uint32_t value)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 
ufs_wait_for_irq(QUfs * ufs)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 
ufs_build_req_utrd(uint64_t cmd_desc_addr,uint8_t slot,uint32_t data_direction,uint16_t prd_table_length)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 
ufs_send_nop_out(QUfs * ufs,uint8_t slot,UtpTransferReqDesc * utrd_out,UtpUpiuRsp * rsp_out)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 
ufs_send_query(QUfs * ufs,uint8_t slot,uint8_t query_function,uint8_t query_opcode,uint8_t idn,uint8_t index,UtpTransferReqDesc * utrd_out,UtpUpiuRsp * rsp_out)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,
122631c8726SJeuk Kim                            UtpTransferReqDesc *utrd_out, UtpUpiuRsp *rsp_out)
123631c8726SJeuk Kim {
124631c8726SJeuk Kim     /* Build up utp transfer request descriptor */
125631c8726SJeuk Kim     UtpTransferReqDesc utrd = ufs_build_req_utrd(ufs->cmd_desc_addr, slot,
126631c8726SJeuk Kim                                                  UFS_UTP_NO_DATA_TRANSFER, 0);
127631c8726SJeuk Kim     uint64_t utrd_addr = ufs->utrlba + slot * sizeof(UtpTransferReqDesc);
128631c8726SJeuk Kim     uint64_t req_upiu_addr =
129631c8726SJeuk Kim         ufs->cmd_desc_addr + slot * UTP_COMMAND_DESCRIPTOR_SIZE;
130631c8726SJeuk Kim     uint64_t rsp_upiu_addr = req_upiu_addr + UTP_RESPONSE_UPIU_OFFSET;
131631c8726SJeuk Kim     qtest_memwrite(ufs->dev.bus->qts, utrd_addr, &utrd, sizeof(utrd));
132631c8726SJeuk Kim 
133631c8726SJeuk Kim     /* Build up request upiu */
134631c8726SJeuk Kim     UtpUpiuReq req_upiu = { 0 };
135631c8726SJeuk Kim     req_upiu.header.trans_type = UFS_UPIU_TRANSACTION_QUERY_REQ;
136631c8726SJeuk Kim     req_upiu.header.query_func = query_function;
137631c8726SJeuk Kim     req_upiu.header.task_tag = slot;
138631c8726SJeuk Kim     /*
139631c8726SJeuk Kim      * QEMU UFS does not currently support Write descriptor and Write attribute,
140631c8726SJeuk Kim      * so the value of data_segment_length is always 0.
141631c8726SJeuk Kim      */
142631c8726SJeuk Kim     req_upiu.header.data_segment_length = 0;
143631c8726SJeuk Kim     req_upiu.qr.opcode = query_opcode;
144631c8726SJeuk Kim     req_upiu.qr.idn = idn;
145631c8726SJeuk Kim     req_upiu.qr.index = index;
146631c8726SJeuk Kim     qtest_memwrite(ufs->dev.bus->qts, req_upiu_addr, &req_upiu,
147631c8726SJeuk Kim                    sizeof(req_upiu));
148631c8726SJeuk Kim 
149631c8726SJeuk Kim     /* Ring Doorbell */
150631c8726SJeuk Kim     ufs_wreg(ufs, A_UTRLDBR, 1);
151631c8726SJeuk Kim     ufs_wait_for_irq(ufs);
152631c8726SJeuk Kim     g_assert_true(FIELD_EX32(ufs_rreg(ufs, A_IS), IS, UTRCS));
153631c8726SJeuk Kim     ufs_wreg(ufs, A_IS, FIELD_DP32(0, IS, UTRCS, 1));
154631c8726SJeuk Kim 
155631c8726SJeuk Kim     qtest_memread(ufs->dev.bus->qts, utrd_addr, utrd_out, sizeof(*utrd_out));
156631c8726SJeuk Kim     qtest_memread(ufs->dev.bus->qts, rsp_upiu_addr, rsp_out, sizeof(*rsp_out));
157631c8726SJeuk Kim }
158631c8726SJeuk Kim 
ufs_send_scsi_command(QUfs * ufs,uint8_t slot,uint8_t lun,const uint8_t * cdb,const uint8_t * data_in,size_t data_in_len,uint8_t * data_out,size_t data_out_len,UtpTransferReqDesc * utrd_out,UtpUpiuRsp * rsp_out)159631c8726SJeuk Kim static void ufs_send_scsi_command(QUfs *ufs, uint8_t slot, uint8_t lun,
160631c8726SJeuk Kim                                   const uint8_t *cdb, const uint8_t *data_in,
161631c8726SJeuk Kim                                   size_t data_in_len, uint8_t *data_out,
162631c8726SJeuk Kim                                   size_t data_out_len,
163631c8726SJeuk Kim                                   UtpTransferReqDesc *utrd_out,
164631c8726SJeuk Kim                                   UtpUpiuRsp *rsp_out)
165631c8726SJeuk Kim 
166631c8726SJeuk Kim {
167631c8726SJeuk Kim     /* Build up PRDT */
168631c8726SJeuk Kim     UfshcdSgEntry entries[MAX_PRD_ENTRY_COUNT] = {
169631c8726SJeuk Kim         0,
170631c8726SJeuk Kim     };
171631c8726SJeuk Kim     uint8_t flags;
172631c8726SJeuk Kim     uint16_t prd_table_length, i;
173631c8726SJeuk Kim     uint32_t data_direction, data_len;
174631c8726SJeuk Kim     uint64_t req_upiu_addr =
175631c8726SJeuk Kim         ufs->cmd_desc_addr + slot * UTP_COMMAND_DESCRIPTOR_SIZE;
176631c8726SJeuk Kim     uint64_t prdt_addr = req_upiu_addr + UTP_PRDT_UPIU_OFFSET;
177631c8726SJeuk Kim 
178631c8726SJeuk Kim     g_assert_true(data_in_len < MAX_PRD_ENTRY_COUNT * PRD_ENTRY_DATA_SIZE);
179631c8726SJeuk Kim     g_assert_true(data_out_len < MAX_PRD_ENTRY_COUNT * PRD_ENTRY_DATA_SIZE);
180631c8726SJeuk Kim     if (data_in_len > 0) {
181631c8726SJeuk Kim         g_assert_nonnull(data_in);
182631c8726SJeuk Kim         data_direction = UFS_UTP_HOST_TO_DEVICE;
183631c8726SJeuk Kim         data_len = data_in_len;
184631c8726SJeuk Kim         flags = UFS_UPIU_CMD_FLAGS_WRITE;
185631c8726SJeuk Kim     } else if (data_out_len > 0) {
186631c8726SJeuk Kim         g_assert_nonnull(data_out);
187631c8726SJeuk Kim         data_direction = UFS_UTP_DEVICE_TO_HOST;
188631c8726SJeuk Kim         data_len = data_out_len;
189631c8726SJeuk Kim         flags = UFS_UPIU_CMD_FLAGS_READ;
190631c8726SJeuk Kim     } else {
191631c8726SJeuk Kim         data_direction = UFS_UTP_NO_DATA_TRANSFER;
192631c8726SJeuk Kim         data_len = 0;
193631c8726SJeuk Kim         flags = UFS_UPIU_CMD_FLAGS_NONE;
194631c8726SJeuk Kim     }
195631c8726SJeuk Kim     prd_table_length = DIV_ROUND_UP(data_len, PRD_ENTRY_DATA_SIZE);
196631c8726SJeuk Kim 
197631c8726SJeuk Kim     qtest_memset(ufs->dev.bus->qts, ufs->data_buffer_addr, 0,
198631c8726SJeuk Kim                  MAX_PRD_ENTRY_COUNT * PRD_ENTRY_DATA_SIZE);
199631c8726SJeuk Kim     if (data_in_len) {
200631c8726SJeuk Kim         qtest_memwrite(ufs->dev.bus->qts, ufs->data_buffer_addr, data_in,
201631c8726SJeuk Kim                        data_in_len);
202631c8726SJeuk Kim     }
203631c8726SJeuk Kim 
204631c8726SJeuk Kim     for (i = 0; i < prd_table_length; i++) {
205631c8726SJeuk Kim         entries[i].addr =
206631c8726SJeuk Kim             cpu_to_le64(ufs->data_buffer_addr + i * sizeof(UfshcdSgEntry));
207631c8726SJeuk Kim         if (i + 1 != prd_table_length) {
208631c8726SJeuk Kim             entries[i].size = cpu_to_le32(PRD_ENTRY_DATA_SIZE - 1);
209631c8726SJeuk Kim         } else {
210631c8726SJeuk Kim             entries[i].size = cpu_to_le32(
211631c8726SJeuk Kim                 data_len - (PRD_ENTRY_DATA_SIZE * (prd_table_length - 1)) - 1);
212631c8726SJeuk Kim         }
213631c8726SJeuk Kim     }
214631c8726SJeuk Kim     qtest_memwrite(ufs->dev.bus->qts, prdt_addr, entries,
215631c8726SJeuk Kim                    prd_table_length * sizeof(UfshcdSgEntry));
216631c8726SJeuk Kim 
217631c8726SJeuk Kim     /* Build up utp transfer request descriptor */
218631c8726SJeuk Kim     UtpTransferReqDesc utrd = ufs_build_req_utrd(
219631c8726SJeuk Kim         ufs->cmd_desc_addr, slot, data_direction, prd_table_length);
220631c8726SJeuk Kim     uint64_t utrd_addr = ufs->utrlba + slot * sizeof(UtpTransferReqDesc);
221631c8726SJeuk Kim     uint64_t rsp_upiu_addr = req_upiu_addr + UTP_RESPONSE_UPIU_OFFSET;
222631c8726SJeuk Kim     qtest_memwrite(ufs->dev.bus->qts, utrd_addr, &utrd, sizeof(utrd));
223631c8726SJeuk Kim 
224631c8726SJeuk Kim     /* Build up request upiu */
225631c8726SJeuk Kim     UtpUpiuReq req_upiu = { 0 };
226631c8726SJeuk Kim     req_upiu.header.trans_type = UFS_UPIU_TRANSACTION_COMMAND;
227631c8726SJeuk Kim     req_upiu.header.flags = flags;
228631c8726SJeuk Kim     req_upiu.header.lun = lun;
229631c8726SJeuk Kim     req_upiu.header.task_tag = slot;
230631c8726SJeuk Kim     req_upiu.sc.exp_data_transfer_len = cpu_to_be32(data_len);
231631c8726SJeuk Kim     memcpy(req_upiu.sc.cdb, cdb, UFS_CDB_SIZE);
232631c8726SJeuk Kim     qtest_memwrite(ufs->dev.bus->qts, req_upiu_addr, &req_upiu,
233631c8726SJeuk Kim                    sizeof(req_upiu));
234631c8726SJeuk Kim 
235631c8726SJeuk Kim     /* Ring Doorbell */
236631c8726SJeuk Kim     ufs_wreg(ufs, A_UTRLDBR, 1);
237631c8726SJeuk Kim     ufs_wait_for_irq(ufs);
238631c8726SJeuk Kim     g_assert_true(FIELD_EX32(ufs_rreg(ufs, A_IS), IS, UTRCS));
239631c8726SJeuk Kim     ufs_wreg(ufs, A_IS, FIELD_DP32(0, IS, UTRCS, 1));
240631c8726SJeuk Kim 
241631c8726SJeuk Kim     qtest_memread(ufs->dev.bus->qts, utrd_addr, utrd_out, sizeof(*utrd_out));
242631c8726SJeuk Kim     qtest_memread(ufs->dev.bus->qts, rsp_upiu_addr, rsp_out, sizeof(*rsp_out));
243631c8726SJeuk Kim     if (data_out_len) {
244631c8726SJeuk Kim         qtest_memread(ufs->dev.bus->qts, ufs->data_buffer_addr, data_out,
245631c8726SJeuk Kim                       data_out_len);
246631c8726SJeuk Kim     }
247631c8726SJeuk Kim }
248631c8726SJeuk Kim 
249631c8726SJeuk Kim /**
250631c8726SJeuk Kim  * Initialize Ufs host controller and logical unit.
251631c8726SJeuk Kim  * After running this function, you can make a transfer request to the UFS.
252631c8726SJeuk Kim  */
ufs_init(QUfs * ufs,QGuestAllocator * alloc)253631c8726SJeuk Kim static void ufs_init(QUfs *ufs, QGuestAllocator *alloc)
254631c8726SJeuk Kim {
255631c8726SJeuk Kim     uint64_t end_time;
256631c8726SJeuk Kim     uint32_t nutrs, nutmrs;
257631c8726SJeuk Kim     uint32_t hcs, is, ucmdarg2, cap;
258631c8726SJeuk Kim     uint32_t hce = 0, ie = 0;
259631c8726SJeuk Kim     UtpTransferReqDesc utrd;
260631c8726SJeuk Kim     UtpUpiuRsp rsp_upiu;
261631c8726SJeuk Kim 
262631c8726SJeuk Kim     ufs->bar = qpci_iomap(&ufs->dev, 0, NULL);
263631c8726SJeuk Kim     qpci_device_enable(&ufs->dev);
264631c8726SJeuk Kim 
265631c8726SJeuk Kim     /* Start host controller initialization */
266631c8726SJeuk Kim     hce = FIELD_DP32(hce, HCE, HCE, 1);
267631c8726SJeuk Kim     ufs_wreg(ufs, A_HCE, hce);
268631c8726SJeuk Kim 
269631c8726SJeuk Kim     /* Wait for device to reset */
270631c8726SJeuk Kim     end_time = g_get_monotonic_time() + TIMEOUT_SECONDS * G_TIME_SPAN_SECOND;
271631c8726SJeuk Kim     do {
272631c8726SJeuk Kim         qtest_clock_step(ufs->dev.bus->qts, 100);
273631c8726SJeuk Kim         hce = FIELD_EX32(ufs_rreg(ufs, A_HCE), HCE, HCE);
274631c8726SJeuk Kim     } while (hce == 0 && g_get_monotonic_time() < end_time);
275631c8726SJeuk Kim     g_assert_cmpuint(hce, ==, 1);
276631c8726SJeuk Kim 
277631c8726SJeuk Kim     /* Enable interrupt */
278631c8726SJeuk Kim     ie = FIELD_DP32(ie, IE, UCCE, 1);
279631c8726SJeuk Kim     ie = FIELD_DP32(ie, IE, UHESE, 1);
280631c8726SJeuk Kim     ie = FIELD_DP32(ie, IE, UHXSE, 1);
281631c8726SJeuk Kim     ie = FIELD_DP32(ie, IE, UPMSE, 1);
282631c8726SJeuk Kim     ufs_wreg(ufs, A_IE, ie);
283631c8726SJeuk Kim 
284631c8726SJeuk Kim     /* Send DME_LINK_STARTUP uic command */
285631c8726SJeuk Kim     hcs = ufs_rreg(ufs, A_HCS);
286631c8726SJeuk Kim     g_assert_true(FIELD_EX32(hcs, HCS, UCRDY));
287631c8726SJeuk Kim 
288631c8726SJeuk Kim     ufs_wreg(ufs, A_UCMDARG1, 0);
289631c8726SJeuk Kim     ufs_wreg(ufs, A_UCMDARG2, 0);
290631c8726SJeuk Kim     ufs_wreg(ufs, A_UCMDARG3, 0);
291631c8726SJeuk Kim     ufs_wreg(ufs, A_UICCMD, UFS_UIC_CMD_DME_LINK_STARTUP);
292631c8726SJeuk Kim 
293631c8726SJeuk Kim     is = ufs_rreg(ufs, A_IS);
294631c8726SJeuk Kim     g_assert_true(FIELD_EX32(is, IS, UCCS));
295631c8726SJeuk Kim     ufs_wreg(ufs, A_IS, FIELD_DP32(0, IS, UCCS, 1));
296631c8726SJeuk Kim 
297631c8726SJeuk Kim     ucmdarg2 = ufs_rreg(ufs, A_UCMDARG2);
298631c8726SJeuk Kim     g_assert_cmpuint(ucmdarg2, ==, 0);
299631c8726SJeuk Kim     is = ufs_rreg(ufs, A_IS);
300631c8726SJeuk Kim     g_assert_cmpuint(is, ==, 0);
301631c8726SJeuk Kim     hcs = ufs_rreg(ufs, A_HCS);
302631c8726SJeuk Kim     g_assert_true(FIELD_EX32(hcs, HCS, DP));
303631c8726SJeuk Kim     g_assert_true(FIELD_EX32(hcs, HCS, UTRLRDY));
304631c8726SJeuk Kim     g_assert_true(FIELD_EX32(hcs, HCS, UTMRLRDY));
305631c8726SJeuk Kim     g_assert_true(FIELD_EX32(hcs, HCS, UCRDY));
306631c8726SJeuk Kim 
307631c8726SJeuk Kim     /* Enable all interrupt functions */
308631c8726SJeuk Kim     ie = FIELD_DP32(ie, IE, UTRCE, 1);
309631c8726SJeuk Kim     ie = FIELD_DP32(ie, IE, UEE, 1);
310631c8726SJeuk Kim     ie = FIELD_DP32(ie, IE, UPMSE, 1);
311631c8726SJeuk Kim     ie = FIELD_DP32(ie, IE, UHXSE, 1);
312631c8726SJeuk Kim     ie = FIELD_DP32(ie, IE, UHESE, 1);
313631c8726SJeuk Kim     ie = FIELD_DP32(ie, IE, UTMRCE, 1);
314631c8726SJeuk Kim     ie = FIELD_DP32(ie, IE, UCCE, 1);
315631c8726SJeuk Kim     ie = FIELD_DP32(ie, IE, DFEE, 1);
316631c8726SJeuk Kim     ie = FIELD_DP32(ie, IE, HCFEE, 1);
317631c8726SJeuk Kim     ie = FIELD_DP32(ie, IE, SBFEE, 1);
318631c8726SJeuk Kim     ie = FIELD_DP32(ie, IE, CEFEE, 1);
319631c8726SJeuk Kim     ufs_wreg(ufs, A_IE, ie);
320631c8726SJeuk Kim     ufs_wreg(ufs, A_UTRIACR, 0);
321631c8726SJeuk Kim 
3224b3e4d2bSMichael Tokarev     /* Enable transfer request and task management request */
323631c8726SJeuk Kim     cap = ufs_rreg(ufs, A_CAP);
324631c8726SJeuk Kim     nutrs = FIELD_EX32(cap, CAP, NUTRS) + 1;
325631c8726SJeuk Kim     nutmrs = FIELD_EX32(cap, CAP, NUTMRS) + 1;
326631c8726SJeuk Kim     ufs->cmd_desc_addr =
327631c8726SJeuk Kim         guest_alloc(alloc, nutrs * UTP_COMMAND_DESCRIPTOR_SIZE);
328631c8726SJeuk Kim     ufs->data_buffer_addr =
329631c8726SJeuk Kim         guest_alloc(alloc, MAX_PRD_ENTRY_COUNT * PRD_ENTRY_DATA_SIZE);
330631c8726SJeuk Kim     ufs->utrlba = guest_alloc(alloc, nutrs * sizeof(UtpTransferReqDesc));
331631c8726SJeuk Kim     ufs->utmrlba = guest_alloc(alloc, nutmrs * sizeof(UtpTaskReqDesc));
332631c8726SJeuk Kim 
333631c8726SJeuk Kim     ufs_wreg(ufs, A_UTRLBA, ufs->utrlba & 0xffffffff);
334631c8726SJeuk Kim     ufs_wreg(ufs, A_UTRLBAU, ufs->utrlba >> 32);
335631c8726SJeuk Kim     ufs_wreg(ufs, A_UTMRLBA, ufs->utmrlba & 0xffffffff);
336631c8726SJeuk Kim     ufs_wreg(ufs, A_UTMRLBAU, ufs->utmrlba >> 32);
337631c8726SJeuk Kim     ufs_wreg(ufs, A_UTRLRSR, 1);
338631c8726SJeuk Kim     ufs_wreg(ufs, A_UTMRLRSR, 1);
339631c8726SJeuk Kim 
340631c8726SJeuk Kim     /* Send nop out to test transfer request */
341631c8726SJeuk Kim     ufs_send_nop_out(ufs, 0, &utrd, &rsp_upiu);
342631c8726SJeuk Kim     g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS);
343631c8726SJeuk Kim 
344631c8726SJeuk Kim     /* Set fDeviceInit flag via query request */
345631c8726SJeuk Kim     ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST,
346631c8726SJeuk Kim                    UFS_UPIU_QUERY_OPCODE_SET_FLAG,
347631c8726SJeuk Kim                    UFS_QUERY_FLAG_IDN_FDEVICEINIT, 0, &utrd, &rsp_upiu);
348631c8726SJeuk Kim     g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS);
349631c8726SJeuk Kim 
350631c8726SJeuk Kim     /* Wait for device to reset */
351631c8726SJeuk Kim     end_time = g_get_monotonic_time() + TIMEOUT_SECONDS * G_TIME_SPAN_SECOND;
352631c8726SJeuk Kim     do {
353631c8726SJeuk Kim         qtest_clock_step(ufs->dev.bus->qts, 100);
354631c8726SJeuk Kim         ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST,
355631c8726SJeuk Kim                        UFS_UPIU_QUERY_OPCODE_READ_FLAG,
356631c8726SJeuk Kim                        UFS_QUERY_FLAG_IDN_FDEVICEINIT, 0, &utrd, &rsp_upiu);
357631c8726SJeuk Kim     } while (be32_to_cpu(rsp_upiu.qr.value) != 0 &&
358631c8726SJeuk Kim              g_get_monotonic_time() < end_time);
359631c8726SJeuk Kim     g_assert_cmpuint(be32_to_cpu(rsp_upiu.qr.value), ==, 0);
360631c8726SJeuk Kim 
361631c8726SJeuk Kim     ufs->enabled = true;
362631c8726SJeuk Kim }
363631c8726SJeuk Kim 
ufs_exit(QUfs * ufs,QGuestAllocator * alloc)364631c8726SJeuk Kim static void ufs_exit(QUfs *ufs, QGuestAllocator *alloc)
365631c8726SJeuk Kim {
366631c8726SJeuk Kim     if (ufs->enabled) {
367631c8726SJeuk Kim         guest_free(alloc, ufs->utrlba);
368631c8726SJeuk Kim         guest_free(alloc, ufs->utmrlba);
369631c8726SJeuk Kim         guest_free(alloc, ufs->cmd_desc_addr);
370631c8726SJeuk Kim         guest_free(alloc, ufs->data_buffer_addr);
371631c8726SJeuk Kim     }
372631c8726SJeuk Kim 
373631c8726SJeuk Kim     qpci_iounmap(&ufs->dev, ufs->bar);
374631c8726SJeuk Kim }
375631c8726SJeuk Kim 
ufs_get_driver(void * obj,const char * interface)376631c8726SJeuk Kim static void *ufs_get_driver(void *obj, const char *interface)
377631c8726SJeuk Kim {
378631c8726SJeuk Kim     QUfs *ufs = obj;
379631c8726SJeuk Kim 
380631c8726SJeuk Kim     if (!g_strcmp0(interface, "pci-device")) {
381631c8726SJeuk Kim         return &ufs->dev;
382631c8726SJeuk Kim     }
383631c8726SJeuk Kim 
384631c8726SJeuk Kim     fprintf(stderr, "%s not present in ufs\n", interface);
385631c8726SJeuk Kim     g_assert_not_reached();
386631c8726SJeuk Kim }
387631c8726SJeuk Kim 
ufs_create(void * pci_bus,QGuestAllocator * alloc,void * addr)388631c8726SJeuk Kim static void *ufs_create(void *pci_bus, QGuestAllocator *alloc, void *addr)
389631c8726SJeuk Kim {
390631c8726SJeuk Kim     QUfs *ufs = g_new0(QUfs, 1);
391631c8726SJeuk Kim     QPCIBus *bus = pci_bus;
392631c8726SJeuk Kim 
393631c8726SJeuk Kim     qpci_device_init(&ufs->dev, bus, addr);
394631c8726SJeuk Kim     ufs->obj.get_driver = ufs_get_driver;
395631c8726SJeuk Kim 
396631c8726SJeuk Kim     return &ufs->obj;
397631c8726SJeuk Kim }
398631c8726SJeuk Kim 
ufstest_reg_read(void * obj,void * data,QGuestAllocator * alloc)399631c8726SJeuk Kim static void ufstest_reg_read(void *obj, void *data, QGuestAllocator *alloc)
400631c8726SJeuk Kim {
401631c8726SJeuk Kim     QUfs *ufs = obj;
402631c8726SJeuk Kim     uint32_t cap;
403631c8726SJeuk Kim 
404631c8726SJeuk Kim     ufs->bar = qpci_iomap(&ufs->dev, 0, NULL);
405631c8726SJeuk Kim     qpci_device_enable(&ufs->dev);
406631c8726SJeuk Kim 
407631c8726SJeuk Kim     cap = ufs_rreg(ufs, A_CAP);
408631c8726SJeuk Kim     g_assert_cmpuint(FIELD_EX32(cap, CAP, NUTRS), ==, 31);
409631c8726SJeuk Kim     g_assert_cmpuint(FIELD_EX32(cap, CAP, NUTMRS), ==, 7);
410631c8726SJeuk Kim     g_assert_cmpuint(FIELD_EX32(cap, CAP, 64AS), ==, 1);
411631c8726SJeuk Kim 
412631c8726SJeuk Kim     qpci_iounmap(&ufs->dev, ufs->bar);
413631c8726SJeuk Kim }
414631c8726SJeuk Kim 
ufstest_init(void * obj,void * data,QGuestAllocator * alloc)415631c8726SJeuk Kim static void ufstest_init(void *obj, void *data, QGuestAllocator *alloc)
416631c8726SJeuk Kim {
417631c8726SJeuk Kim     QUfs *ufs = obj;
418631c8726SJeuk Kim 
419631c8726SJeuk Kim     uint8_t buf[4096] = { 0 };
420631c8726SJeuk Kim     const uint8_t report_luns_cdb[UFS_CDB_SIZE] = {
421631c8726SJeuk Kim         /* allocation length 4096 */
422631c8726SJeuk Kim         REPORT_LUNS, 0x00, 0x00, 0x00, 0x00, 0x00,
423631c8726SJeuk Kim         0x00,        0x00, 0x10, 0x00, 0x00, 0x00
424631c8726SJeuk Kim     };
425631c8726SJeuk Kim     const uint8_t test_unit_ready_cdb[UFS_CDB_SIZE] = {
426631c8726SJeuk Kim         TEST_UNIT_READY,
427631c8726SJeuk Kim     };
428096434feSJeuk Kim     const uint8_t request_sense_cdb[UFS_CDB_SIZE] = {
429096434feSJeuk Kim         REQUEST_SENSE,
430096434feSJeuk Kim     };
431631c8726SJeuk Kim     UtpTransferReqDesc utrd;
432631c8726SJeuk Kim     UtpUpiuRsp rsp_upiu;
433631c8726SJeuk Kim 
434631c8726SJeuk Kim     ufs_init(ufs, alloc);
435631c8726SJeuk Kim 
436631c8726SJeuk Kim     /* Check REPORT_LUNS */
437631c8726SJeuk Kim     ufs_send_scsi_command(ufs, 0, 0, report_luns_cdb, NULL, 0, buf, sizeof(buf),
438631c8726SJeuk Kim                           &utrd, &rsp_upiu);
439631c8726SJeuk Kim     g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS);
440631c8726SJeuk Kim     g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, GOOD);
441631c8726SJeuk Kim     /* LUN LIST LENGTH should be 8, in big endian */
442631c8726SJeuk Kim     g_assert_cmpuint(buf[3], ==, 8);
443631c8726SJeuk Kim     /* There is one logical unit whose lun is 0 */
444631c8726SJeuk Kim     g_assert_cmpuint(buf[9], ==, 0);
445631c8726SJeuk Kim 
446096434feSJeuk Kim     /* Clear Unit Attention */
447096434feSJeuk Kim     ufs_send_scsi_command(ufs, 0, 0, request_sense_cdb, NULL, 0, buf,
448096434feSJeuk Kim                           sizeof(buf), &utrd, &rsp_upiu);
449096434feSJeuk Kim     g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS);
450096434feSJeuk Kim     g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, CHECK_CONDITION);
451096434feSJeuk Kim 
452631c8726SJeuk Kim     /* Check TEST_UNIT_READY */
453631c8726SJeuk Kim     ufs_send_scsi_command(ufs, 0, 0, test_unit_ready_cdb, NULL, 0, NULL, 0,
454631c8726SJeuk Kim                           &utrd, &rsp_upiu);
455631c8726SJeuk Kim     g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS);
456631c8726SJeuk Kim     g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, GOOD);
457631c8726SJeuk Kim 
458631c8726SJeuk Kim     ufs_exit(ufs, alloc);
459631c8726SJeuk Kim }
460631c8726SJeuk Kim 
ufstest_read_write(void * obj,void * data,QGuestAllocator * alloc)461631c8726SJeuk Kim static void ufstest_read_write(void *obj, void *data, QGuestAllocator *alloc)
462631c8726SJeuk Kim {
463631c8726SJeuk Kim     QUfs *ufs = obj;
464631c8726SJeuk Kim     uint8_t read_buf[4096] = { 0 };
465631c8726SJeuk Kim     uint8_t write_buf[4096] = { 0 };
466631c8726SJeuk Kim     const uint8_t read_capacity_cdb[UFS_CDB_SIZE] = {
467631c8726SJeuk Kim         /* allocation length 4096 */
468631c8726SJeuk Kim         SERVICE_ACTION_IN_16,
469631c8726SJeuk Kim         SAI_READ_CAPACITY_16,
470631c8726SJeuk Kim         0x00,
471631c8726SJeuk Kim         0x00,
472631c8726SJeuk Kim         0x00,
473631c8726SJeuk Kim         0x00,
474631c8726SJeuk Kim         0x00,
475631c8726SJeuk Kim         0x00,
476631c8726SJeuk Kim         0x00,
477631c8726SJeuk Kim         0x00,
478631c8726SJeuk Kim         0x00,
479631c8726SJeuk Kim         0x00,
480631c8726SJeuk Kim         0x10,
481631c8726SJeuk Kim         0x00,
482631c8726SJeuk Kim         0x00,
483631c8726SJeuk Kim         0x00
484631c8726SJeuk Kim     };
485096434feSJeuk Kim     const uint8_t request_sense_cdb[UFS_CDB_SIZE] = {
486096434feSJeuk Kim         REQUEST_SENSE,
487096434feSJeuk Kim     };
488631c8726SJeuk Kim     const uint8_t read_cdb[UFS_CDB_SIZE] = {
489631c8726SJeuk Kim         /* READ(10) to LBA 0, transfer length 1 */
490631c8726SJeuk Kim         READ_10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00
491631c8726SJeuk Kim     };
492631c8726SJeuk Kim     const uint8_t write_cdb[UFS_CDB_SIZE] = {
493631c8726SJeuk Kim         /* WRITE(10) to LBA 0, transfer length 1 */
494631c8726SJeuk Kim         WRITE_10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00
495631c8726SJeuk Kim     };
496631c8726SJeuk Kim     uint32_t block_size;
497631c8726SJeuk Kim     UtpTransferReqDesc utrd;
498631c8726SJeuk Kim     UtpUpiuRsp rsp_upiu;
499096434feSJeuk Kim     const int test_lun = 1;
500631c8726SJeuk Kim 
501631c8726SJeuk Kim     ufs_init(ufs, alloc);
502631c8726SJeuk Kim 
503096434feSJeuk Kim     /* Clear Unit Attention */
504096434feSJeuk Kim     ufs_send_scsi_command(ufs, 0, test_lun, request_sense_cdb, NULL, 0,
505096434feSJeuk Kim                           read_buf, sizeof(read_buf), &utrd, &rsp_upiu);
506096434feSJeuk Kim     g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS);
507096434feSJeuk Kim     g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, CHECK_CONDITION);
508096434feSJeuk Kim 
509631c8726SJeuk Kim     /* Read capacity */
510096434feSJeuk Kim     ufs_send_scsi_command(ufs, 0, test_lun, read_capacity_cdb, NULL, 0,
511096434feSJeuk Kim                           read_buf, sizeof(read_buf), &utrd, &rsp_upiu);
512631c8726SJeuk Kim     g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS);
513631c8726SJeuk Kim     g_assert_cmpuint(rsp_upiu.header.scsi_status, ==,
514096434feSJeuk Kim                      UFS_COMMAND_RESULT_SUCCESS);
515631c8726SJeuk Kim     block_size = ldl_be_p(&read_buf[8]);
516631c8726SJeuk Kim     g_assert_cmpuint(block_size, ==, 4096);
517631c8726SJeuk Kim 
518631c8726SJeuk Kim     /* Write data */
51997970daeSJeuk Kim     memset(write_buf, 0xab, block_size);
520096434feSJeuk Kim     ufs_send_scsi_command(ufs, 0, test_lun, write_cdb, write_buf, block_size,
521096434feSJeuk Kim                           NULL, 0, &utrd, &rsp_upiu);
522631c8726SJeuk Kim     g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS);
523631c8726SJeuk Kim     g_assert_cmpuint(rsp_upiu.header.scsi_status, ==,
524096434feSJeuk Kim                      UFS_COMMAND_RESULT_SUCCESS);
525631c8726SJeuk Kim 
526631c8726SJeuk Kim     /* Read data and verify */
527096434feSJeuk Kim     ufs_send_scsi_command(ufs, 0, test_lun, read_cdb, NULL, 0, read_buf,
528096434feSJeuk Kim                           block_size, &utrd, &rsp_upiu);
529631c8726SJeuk Kim     g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS);
530631c8726SJeuk Kim     g_assert_cmpuint(rsp_upiu.header.scsi_status, ==,
531096434feSJeuk Kim                      UFS_COMMAND_RESULT_SUCCESS);
532631c8726SJeuk Kim     g_assert_cmpint(memcmp(read_buf, write_buf, block_size), ==, 0);
533631c8726SJeuk Kim 
534631c8726SJeuk Kim     ufs_exit(ufs, alloc);
535631c8726SJeuk Kim }
536631c8726SJeuk Kim 
drive_destroy(void * path)537631c8726SJeuk Kim static void drive_destroy(void *path)
538631c8726SJeuk Kim {
539631c8726SJeuk Kim     unlink(path);
540631c8726SJeuk Kim     g_free(path);
541631c8726SJeuk Kim     qos_invalidate_command_line();
542631c8726SJeuk Kim }
543631c8726SJeuk Kim 
drive_create(void)544631c8726SJeuk Kim static char *drive_create(void)
545631c8726SJeuk Kim {
546631c8726SJeuk Kim     int fd, ret;
547631c8726SJeuk Kim     char *t_path;
548631c8726SJeuk Kim 
549631c8726SJeuk Kim     /* Create a temporary raw image */
550631c8726SJeuk Kim     fd = g_file_open_tmp("qtest-ufs.XXXXXX", &t_path, NULL);
551631c8726SJeuk Kim     g_assert_cmpint(fd, >=, 0);
552631c8726SJeuk Kim     ret = ftruncate(fd, TEST_IMAGE_SIZE);
553631c8726SJeuk Kim     g_assert_cmpint(ret, ==, 0);
554631c8726SJeuk Kim     close(fd);
555631c8726SJeuk Kim 
556631c8726SJeuk Kim     g_test_queue_destroy(drive_destroy, t_path);
557631c8726SJeuk Kim     return t_path;
558631c8726SJeuk Kim }
559631c8726SJeuk Kim 
ufs_blk_test_setup(GString * cmd_line,void * arg)560631c8726SJeuk Kim static void *ufs_blk_test_setup(GString *cmd_line, void *arg)
561631c8726SJeuk Kim {
562631c8726SJeuk Kim     char *tmp_path = drive_create();
563631c8726SJeuk Kim 
564631c8726SJeuk Kim     g_string_append_printf(cmd_line,
565631c8726SJeuk Kim                            " -blockdev file,filename=%s,node-name=drv1 "
566631c8726SJeuk Kim                            "-device ufs-lu,bus=ufs0,drive=drv1,lun=1 ",
567631c8726SJeuk Kim                            tmp_path);
568631c8726SJeuk Kim 
569631c8726SJeuk Kim     return arg;
570631c8726SJeuk Kim }
571631c8726SJeuk Kim 
ufs_register_nodes(void)572631c8726SJeuk Kim static void ufs_register_nodes(void)
573631c8726SJeuk Kim {
574631c8726SJeuk Kim     const char *arch;
575631c8726SJeuk Kim     QOSGraphEdgeOptions edge_opts = {
576631c8726SJeuk Kim         .before_cmd_line = "-blockdev null-co,node-name=drv0,read-zeroes=on",
577631c8726SJeuk Kim         .after_cmd_line = "-device ufs-lu,bus=ufs0,drive=drv0,lun=0",
578631c8726SJeuk Kim         .extra_device_opts = "addr=04.0,id=ufs0,nutrs=32,nutmrs=8"
579631c8726SJeuk Kim     };
580631c8726SJeuk Kim 
581631c8726SJeuk Kim     QOSGraphTestOptions io_test_opts = {
582631c8726SJeuk Kim         .before = ufs_blk_test_setup,
583631c8726SJeuk Kim     };
584631c8726SJeuk Kim 
585631c8726SJeuk Kim     add_qpci_address(&edge_opts, &(QPCIAddress){ .devfn = QPCI_DEVFN(4, 0) });
586631c8726SJeuk Kim 
587631c8726SJeuk Kim     qos_node_create_driver("ufs", ufs_create);
588631c8726SJeuk Kim     qos_node_consumes("ufs", "pci-bus", &edge_opts);
589631c8726SJeuk Kim     qos_node_produces("ufs", "pci-device");
590631c8726SJeuk Kim 
591631c8726SJeuk Kim     qos_add_test("reg-read", "ufs", ufstest_reg_read, NULL);
592631c8726SJeuk Kim 
593631c8726SJeuk Kim     /*
594631c8726SJeuk Kim      * Check architecture
595631c8726SJeuk Kim      * TODO: Enable ufs io tests for ppc64
596631c8726SJeuk Kim      */
597631c8726SJeuk Kim     arch = qtest_get_arch();
598631c8726SJeuk Kim     if (!strcmp(arch, "ppc64")) {
599631c8726SJeuk Kim         g_test_message("Skipping ufs io tests for ppc64");
600631c8726SJeuk Kim         return;
601631c8726SJeuk Kim     }
602631c8726SJeuk Kim     qos_add_test("init", "ufs", ufstest_init, NULL);
603631c8726SJeuk Kim     qos_add_test("read-write", "ufs", ufstest_read_write, &io_test_opts);
604631c8726SJeuk Kim }
605631c8726SJeuk Kim 
606631c8726SJeuk Kim libqos_init(ufs_register_nodes);
607