1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright (C) 2016 Intel Corporation.
3 * All rights reserved.
4 */
5
6 #include "spdk/stdinc.h"
7
8 #include "CUnit/Basic.h"
9 #include "spdk_internal/mock.h"
10 #include "spdk_internal/cunit.h"
11
12 #include "env_dpdk/pci.c"
13
14 static void
pci_claim_test(struct spdk_pci_device * dev)15 pci_claim_test(struct spdk_pci_device *dev)
16 {
17 int rc = 0;
18 pid_t childPid;
19 int status, ret;
20
21 rc = spdk_pci_device_claim(dev);
22 CU_ASSERT(rc >= 0);
23
24 childPid = fork();
25 CU_ASSERT(childPid >= 0);
26 if (childPid == 0) {
27 ret = spdk_pci_device_claim(dev);
28 CU_ASSERT(ret == -1);
29 exit(0);
30 } else {
31 waitpid(childPid, &status, 0);
32 }
33 }
34
35 static struct spdk_pci_driver ut_pci_driver;
36
37 struct ut_pci_dev {
38 struct spdk_pci_device pci;
39 char config[16];
40 char bar[16];
41 bool attached;
42 };
43
44 static int
ut_map_bar(struct spdk_pci_device * dev,uint32_t bar,void ** mapped_addr,uint64_t * phys_addr,uint64_t * size)45 ut_map_bar(struct spdk_pci_device *dev, uint32_t bar,
46 void **mapped_addr, uint64_t *phys_addr, uint64_t *size)
47 {
48 struct ut_pci_dev *ut_dev = (struct ut_pci_dev *)dev;
49
50 /* just one bar */
51 if (bar > 0) {
52 return -1;
53 }
54
55 *mapped_addr = ut_dev->bar;
56 *phys_addr = 0;
57 *size = sizeof(ut_dev->bar);
58 return 0;
59 }
60
61 static int
ut_unmap_bar(struct spdk_pci_device * device,uint32_t bar,void * addr)62 ut_unmap_bar(struct spdk_pci_device *device, uint32_t bar, void *addr)
63 {
64 return 0;
65 }
66
67 static int
ut_cfg_read(struct spdk_pci_device * dev,void * value,uint32_t len,uint32_t offset)68 ut_cfg_read(struct spdk_pci_device *dev, void *value, uint32_t len, uint32_t offset)
69 {
70 struct ut_pci_dev *ut_dev = (struct ut_pci_dev *)dev;
71
72 if (len + offset >= sizeof(ut_dev->config)) {
73 return -1;
74 }
75
76 memcpy(value, (void *)((uintptr_t)ut_dev->config + offset), len);
77 return 0;
78 }
79
80 static int
ut_cfg_write(struct spdk_pci_device * dev,void * value,uint32_t len,uint32_t offset)81 ut_cfg_write(struct spdk_pci_device *dev, void *value, uint32_t len, uint32_t offset)
82 {
83 struct ut_pci_dev *ut_dev = (struct ut_pci_dev *)dev;
84
85 if (len + offset >= sizeof(ut_dev->config)) {
86 return -1;
87 }
88
89 memcpy((void *)((uintptr_t)ut_dev->config + offset), value, len);
90 return 0;
91 }
92
93
94 static int
ut_enum_cb(void * ctx,struct spdk_pci_device * dev)95 ut_enum_cb(void *ctx, struct spdk_pci_device *dev)
96 {
97 struct ut_pci_dev *ut_dev = (struct ut_pci_dev *)dev;
98
99 ut_dev->attached = true;
100 return 0;
101 }
102
103 static int
ut_attach_cb(const struct spdk_pci_addr * addr)104 ut_attach_cb(const struct spdk_pci_addr *addr)
105 {
106 return -ENODEV;
107 }
108
109 static void
ut_detach_cb(struct spdk_pci_device * dev)110 ut_detach_cb(struct spdk_pci_device *dev)
111 {
112 }
113
114 static struct spdk_pci_device_provider g_ut_provider = {
115 .name = "custom",
116 .attach_cb = ut_attach_cb,
117 .detach_cb = ut_detach_cb,
118 };
119
120 SPDK_PCI_REGISTER_DEVICE_PROVIDER(ut, &g_ut_provider);
121
122 static void
pci_hook_test(void)123 pci_hook_test(void)
124 {
125 struct ut_pci_dev ut_dev = {};
126 uint32_t value_32;
127 void *bar0_vaddr;
128 uint64_t bar0_paddr, bar0_size;
129 int rc;
130
131 ut_dev.pci.type = "custom";
132 ut_dev.pci.id.vendor_id = 0x4;
133 ut_dev.pci.id.device_id = 0x8;
134
135 /* Use add parse for initialization */
136 spdk_pci_addr_parse(&ut_dev.pci.addr, "10000:00:01.0");
137 CU_ASSERT(ut_dev.pci.addr.domain == 0x10000);
138 CU_ASSERT(ut_dev.pci.addr.bus == 0x0);
139 CU_ASSERT(ut_dev.pci.addr.dev == 0x1);
140 CU_ASSERT(ut_dev.pci.addr.func == 0x0);
141
142 ut_dev.pci.map_bar = ut_map_bar;
143 ut_dev.pci.unmap_bar = ut_unmap_bar;
144 ut_dev.pci.cfg_read = ut_cfg_read;
145 ut_dev.pci.cfg_write = ut_cfg_write;
146
147 /* hook the device into the PCI layer */
148 rc = spdk_pci_hook_device(&ut_pci_driver, &ut_dev.pci);
149 CU_ASSERT_EQUAL(rc, 0);
150
151 /* try to attach a device with the matching driver and bdf */
152 rc = spdk_pci_device_attach(&ut_pci_driver, ut_enum_cb, NULL, &ut_dev.pci.addr);
153 CU_ASSERT(rc == 0);
154 CU_ASSERT(ut_dev.pci.internal.attached);
155 CU_ASSERT(ut_dev.attached);
156
157 /* check PCI config writes and reads */
158 value_32 = 0xDEADBEEF;
159 rc = spdk_pci_device_cfg_write32(&ut_dev.pci, value_32, 0);
160 CU_ASSERT(rc == 0);
161
162 value_32 = 0x0BADF00D;
163 rc = spdk_pci_device_cfg_write32(&ut_dev.pci, value_32, 4);
164 CU_ASSERT(rc == 0);
165
166 rc = spdk_pci_device_cfg_read32(&ut_dev.pci, &value_32, 0);
167 CU_ASSERT(rc == 0);
168 CU_ASSERT(value_32 == 0xDEADBEEF);
169 CU_ASSERT(memcmp(&value_32, &ut_dev.config[0], 4) == 0);
170
171 rc = spdk_pci_device_cfg_read32(&ut_dev.pci, &value_32, 4);
172 CU_ASSERT(rc == 0);
173 CU_ASSERT(value_32 == 0x0BADF00D);
174 CU_ASSERT(memcmp(&value_32, &ut_dev.config[4], 4) == 0);
175
176 /* out-of-bounds write */
177 rc = spdk_pci_device_cfg_read32(&ut_dev.pci, &value_32, sizeof(ut_dev.config));
178 CU_ASSERT(rc != 0);
179
180 /* map a bar */
181 rc = spdk_pci_device_map_bar(&ut_dev.pci, 0, &bar0_vaddr, &bar0_paddr, &bar0_size);
182 CU_ASSERT(rc == 0);
183 CU_ASSERT(bar0_vaddr == ut_dev.bar);
184 CU_ASSERT(bar0_size == sizeof(ut_dev.bar));
185 spdk_pci_device_unmap_bar(&ut_dev.pci, 0, bar0_vaddr);
186
187 /* map an inaccessible bar */
188 rc = spdk_pci_device_map_bar(&ut_dev.pci, 1, &bar0_vaddr, &bar0_paddr, &bar0_size);
189 CU_ASSERT(rc != 0);
190
191 /* test spdk_pci_device_claim() */
192 pci_claim_test(&ut_dev.pci);
193
194 spdk_pci_device_detach(&ut_dev.pci);
195 CU_ASSERT(!ut_dev.pci.internal.attached);
196
197 /* unhook the device */
198 spdk_pci_unhook_device(&ut_dev.pci);
199
200 /* try to attach the same device again */
201 rc = spdk_pci_device_attach(&ut_pci_driver, ut_enum_cb, NULL, &ut_dev.pci.addr);
202 CU_ASSERT(rc != 0);
203 }
204
205 int
main(int argc,char ** argv)206 main(int argc, char **argv)
207 {
208 CU_pSuite suite = NULL;
209 unsigned int num_failures;
210
211 if (CU_initialize_registry() != CUE_SUCCESS) {
212 return CU_get_error();
213 }
214
215 suite = CU_add_suite("pci", NULL, NULL);
216 if (suite == NULL) {
217 CU_cleanup_registry();
218 return CU_get_error();
219 }
220
221 if (
222 CU_add_test(suite, "pci_hook", pci_hook_test) == NULL
223 ) {
224 CU_cleanup_registry();
225 return CU_get_error();
226 }
227
228 num_failures = spdk_ut_run_tests(argc, argv, NULL);
229 CU_cleanup_registry();
230 return num_failures;
231 }
232