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