xref: /qemu/tests/qtest/ufs-test.c (revision 631c8726)
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"
16631c8726SJeuk Kim #include "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,
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 
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  */
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 
322631c8726SJeuk Kim     /* Enable tranfer 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 
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 
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 
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 
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 
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     };
428631c8726SJeuk Kim     UtpTransferReqDesc utrd;
429631c8726SJeuk Kim     UtpUpiuRsp rsp_upiu;
430631c8726SJeuk Kim 
431631c8726SJeuk Kim     ufs_init(ufs, alloc);
432631c8726SJeuk Kim 
433631c8726SJeuk Kim     /* Check REPORT_LUNS */
434631c8726SJeuk Kim     ufs_send_scsi_command(ufs, 0, 0, report_luns_cdb, NULL, 0, buf, sizeof(buf),
435631c8726SJeuk Kim                           &utrd, &rsp_upiu);
436631c8726SJeuk Kim     g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS);
437631c8726SJeuk Kim     g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, GOOD);
438631c8726SJeuk Kim     /* LUN LIST LENGTH should be 8, in big endian */
439631c8726SJeuk Kim     g_assert_cmpuint(buf[3], ==, 8);
440631c8726SJeuk Kim     /* There is one logical unit whose lun is 0 */
441631c8726SJeuk Kim     g_assert_cmpuint(buf[9], ==, 0);
442631c8726SJeuk Kim 
443631c8726SJeuk Kim     /* Check TEST_UNIT_READY */
444631c8726SJeuk Kim     ufs_send_scsi_command(ufs, 0, 0, test_unit_ready_cdb, NULL, 0, NULL, 0,
445631c8726SJeuk Kim                           &utrd, &rsp_upiu);
446631c8726SJeuk Kim     g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS);
447631c8726SJeuk Kim     g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, GOOD);
448631c8726SJeuk Kim 
449631c8726SJeuk Kim     ufs_exit(ufs, alloc);
450631c8726SJeuk Kim }
451631c8726SJeuk Kim 
452631c8726SJeuk Kim static void ufstest_read_write(void *obj, void *data, QGuestAllocator *alloc)
453631c8726SJeuk Kim {
454631c8726SJeuk Kim     QUfs *ufs = obj;
455631c8726SJeuk Kim     uint8_t read_buf[4096] = { 0 };
456631c8726SJeuk Kim     uint8_t write_buf[4096] = { 0 };
457631c8726SJeuk Kim     const uint8_t read_capacity_cdb[UFS_CDB_SIZE] = {
458631c8726SJeuk Kim         /* allocation length 4096 */
459631c8726SJeuk Kim         SERVICE_ACTION_IN_16,
460631c8726SJeuk Kim         SAI_READ_CAPACITY_16,
461631c8726SJeuk Kim         0x00,
462631c8726SJeuk Kim         0x00,
463631c8726SJeuk Kim         0x00,
464631c8726SJeuk Kim         0x00,
465631c8726SJeuk Kim         0x00,
466631c8726SJeuk Kim         0x00,
467631c8726SJeuk Kim         0x00,
468631c8726SJeuk Kim         0x00,
469631c8726SJeuk Kim         0x00,
470631c8726SJeuk Kim         0x00,
471631c8726SJeuk Kim         0x10,
472631c8726SJeuk Kim         0x00,
473631c8726SJeuk Kim         0x00,
474631c8726SJeuk Kim         0x00
475631c8726SJeuk Kim     };
476631c8726SJeuk Kim     const uint8_t read_cdb[UFS_CDB_SIZE] = {
477631c8726SJeuk Kim         /* READ(10) to LBA 0, transfer length 1 */
478631c8726SJeuk Kim         READ_10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00
479631c8726SJeuk Kim     };
480631c8726SJeuk Kim     const uint8_t write_cdb[UFS_CDB_SIZE] = {
481631c8726SJeuk Kim         /* WRITE(10) to LBA 0, transfer length 1 */
482631c8726SJeuk Kim         WRITE_10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00
483631c8726SJeuk Kim     };
484631c8726SJeuk Kim     uint32_t block_size;
485631c8726SJeuk Kim     UtpTransferReqDesc utrd;
486631c8726SJeuk Kim     UtpUpiuRsp rsp_upiu;
487631c8726SJeuk Kim 
488631c8726SJeuk Kim     ufs_init(ufs, alloc);
489631c8726SJeuk Kim 
490631c8726SJeuk Kim     /* Read capacity */
491631c8726SJeuk Kim     ufs_send_scsi_command(ufs, 0, 1, read_capacity_cdb, NULL, 0, read_buf,
492631c8726SJeuk Kim                           sizeof(read_buf), &utrd, &rsp_upiu);
493631c8726SJeuk Kim     g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS);
494631c8726SJeuk Kim     g_assert_cmpuint(rsp_upiu.header.scsi_status, ==,
495631c8726SJeuk Kim                      UFS_COMMAND_RESULT_SUCESS);
496631c8726SJeuk Kim     block_size = ldl_be_p(&read_buf[8]);
497631c8726SJeuk Kim     g_assert_cmpuint(block_size, ==, 4096);
498631c8726SJeuk Kim 
499631c8726SJeuk Kim     /* Write data */
500631c8726SJeuk Kim     memset(write_buf, rand() % 255 + 1, block_size);
501631c8726SJeuk Kim     ufs_send_scsi_command(ufs, 0, 1, write_cdb, write_buf, block_size, NULL, 0,
502631c8726SJeuk Kim                           &utrd, &rsp_upiu);
503631c8726SJeuk Kim     g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS);
504631c8726SJeuk Kim     g_assert_cmpuint(rsp_upiu.header.scsi_status, ==,
505631c8726SJeuk Kim                      UFS_COMMAND_RESULT_SUCESS);
506631c8726SJeuk Kim 
507631c8726SJeuk Kim     /* Read data and verify */
508631c8726SJeuk Kim     ufs_send_scsi_command(ufs, 0, 1, read_cdb, NULL, 0, read_buf, block_size,
509631c8726SJeuk Kim                           &utrd, &rsp_upiu);
510631c8726SJeuk Kim     g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS);
511631c8726SJeuk Kim     g_assert_cmpuint(rsp_upiu.header.scsi_status, ==,
512631c8726SJeuk Kim                      UFS_COMMAND_RESULT_SUCESS);
513631c8726SJeuk Kim     g_assert_cmpint(memcmp(read_buf, write_buf, block_size), ==, 0);
514631c8726SJeuk Kim 
515631c8726SJeuk Kim     ufs_exit(ufs, alloc);
516631c8726SJeuk Kim }
517631c8726SJeuk Kim 
518631c8726SJeuk Kim static void drive_destroy(void *path)
519631c8726SJeuk Kim {
520631c8726SJeuk Kim     unlink(path);
521631c8726SJeuk Kim     g_free(path);
522631c8726SJeuk Kim     qos_invalidate_command_line();
523631c8726SJeuk Kim }
524631c8726SJeuk Kim 
525631c8726SJeuk Kim static char *drive_create(void)
526631c8726SJeuk Kim {
527631c8726SJeuk Kim     int fd, ret;
528631c8726SJeuk Kim     char *t_path;
529631c8726SJeuk Kim 
530631c8726SJeuk Kim     /* Create a temporary raw image */
531631c8726SJeuk Kim     fd = g_file_open_tmp("qtest-ufs.XXXXXX", &t_path, NULL);
532631c8726SJeuk Kim     g_assert_cmpint(fd, >=, 0);
533631c8726SJeuk Kim     ret = ftruncate(fd, TEST_IMAGE_SIZE);
534631c8726SJeuk Kim     g_assert_cmpint(ret, ==, 0);
535631c8726SJeuk Kim     close(fd);
536631c8726SJeuk Kim 
537631c8726SJeuk Kim     g_test_queue_destroy(drive_destroy, t_path);
538631c8726SJeuk Kim     return t_path;
539631c8726SJeuk Kim }
540631c8726SJeuk Kim 
541631c8726SJeuk Kim static void *ufs_blk_test_setup(GString *cmd_line, void *arg)
542631c8726SJeuk Kim {
543631c8726SJeuk Kim     char *tmp_path = drive_create();
544631c8726SJeuk Kim 
545631c8726SJeuk Kim     g_string_append_printf(cmd_line,
546631c8726SJeuk Kim                            " -blockdev file,filename=%s,node-name=drv1 "
547631c8726SJeuk Kim                            "-device ufs-lu,bus=ufs0,drive=drv1,lun=1 ",
548631c8726SJeuk Kim                            tmp_path);
549631c8726SJeuk Kim 
550631c8726SJeuk Kim     return arg;
551631c8726SJeuk Kim }
552631c8726SJeuk Kim 
553631c8726SJeuk Kim static void ufs_register_nodes(void)
554631c8726SJeuk Kim {
555631c8726SJeuk Kim     const char *arch;
556631c8726SJeuk Kim     QOSGraphEdgeOptions edge_opts = {
557631c8726SJeuk Kim         .before_cmd_line = "-blockdev null-co,node-name=drv0,read-zeroes=on",
558631c8726SJeuk Kim         .after_cmd_line = "-device ufs-lu,bus=ufs0,drive=drv0,lun=0",
559631c8726SJeuk Kim         .extra_device_opts = "addr=04.0,id=ufs0,nutrs=32,nutmrs=8"
560631c8726SJeuk Kim     };
561631c8726SJeuk Kim 
562631c8726SJeuk Kim     QOSGraphTestOptions io_test_opts = {
563631c8726SJeuk Kim         .before = ufs_blk_test_setup,
564631c8726SJeuk Kim     };
565631c8726SJeuk Kim 
566631c8726SJeuk Kim     add_qpci_address(&edge_opts, &(QPCIAddress){ .devfn = QPCI_DEVFN(4, 0) });
567631c8726SJeuk Kim 
568631c8726SJeuk Kim     qos_node_create_driver("ufs", ufs_create);
569631c8726SJeuk Kim     qos_node_consumes("ufs", "pci-bus", &edge_opts);
570631c8726SJeuk Kim     qos_node_produces("ufs", "pci-device");
571631c8726SJeuk Kim 
572631c8726SJeuk Kim     qos_add_test("reg-read", "ufs", ufstest_reg_read, NULL);
573631c8726SJeuk Kim 
574631c8726SJeuk Kim     /*
575631c8726SJeuk Kim      * Check architecture
576631c8726SJeuk Kim      * TODO: Enable ufs io tests for ppc64
577631c8726SJeuk Kim      */
578631c8726SJeuk Kim     arch = qtest_get_arch();
579631c8726SJeuk Kim     if (!strcmp(arch, "ppc64")) {
580631c8726SJeuk Kim         g_test_message("Skipping ufs io tests for ppc64");
581631c8726SJeuk Kim         return;
582631c8726SJeuk Kim     }
583631c8726SJeuk Kim     qos_add_test("init", "ufs", ufstest_init, NULL);
584631c8726SJeuk Kim     qos_add_test("read-write", "ufs", ufstest_read_write, &io_test_opts);
585631c8726SJeuk Kim }
586631c8726SJeuk Kim 
587631c8726SJeuk Kim libqos_init(ufs_register_nodes);
588