Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright(C) 2023 Marvell.
3 : : */
4 : :
5 : : #include <uapi/linux/vfio.h>
6 : :
7 : : #include <dirent.h>
8 : : #include <inttypes.h>
9 : : #include <stdlib.h>
10 : : #include <string.h>
11 : : #include <sys/ioctl.h>
12 : : #include <sys/mman.h>
13 : : #include <sys/queue.h>
14 : : #include <unistd.h>
15 : :
16 : : #include <bus_driver.h>
17 : : #include <bus_platform_driver.h>
18 : : #include <eal_export.h>
19 : : #include <eal_filesystem.h>
20 : : #include <rte_bus.h>
21 : : #include <rte_devargs.h>
22 : : #include <rte_errno.h>
23 : : #include <rte_log.h>
24 : : #include <rte_memory.h>
25 : : #include <rte_string_fns.h>
26 : : #include <rte_vfio.h>
27 : :
28 : : #include "private.h"
29 : :
30 : : #define PLATFORM_BUS_DEVICES_PATH "/sys/bus/platform/devices"
31 : :
32 : : static struct rte_bus platform_bus;
33 : :
34 : : RTE_EXPORT_INTERNAL_SYMBOL(rte_platform_register)
35 : : void
36 : 0 : rte_platform_register(struct rte_platform_driver *pdrv)
37 : : {
38 : 0 : rte_bus_add_driver(&platform_bus, &pdrv->driver);
39 : 0 : }
40 : :
41 : : RTE_EXPORT_INTERNAL_SYMBOL(rte_platform_unregister)
42 : : void
43 : 0 : rte_platform_unregister(struct rte_platform_driver *pdrv)
44 : : {
45 : 0 : rte_bus_remove_driver(&platform_bus, &pdrv->driver);
46 : 0 : }
47 : :
48 : : static int
49 : 0 : dev_add(const char *dev_name)
50 : : {
51 : : struct rte_platform_device *pdev, *tmp;
52 : : char path[PATH_MAX];
53 : : unsigned long val;
54 : :
55 : 0 : pdev = calloc(1, sizeof(*pdev));
56 [ # # ]: 0 : if (pdev == NULL)
57 : : return -ENOMEM;
58 : :
59 : 0 : rte_strscpy(pdev->name, dev_name, sizeof(pdev->name));
60 : 0 : pdev->device.name = pdev->name;
61 : 0 : pdev->device.devargs = rte_bus_find_devargs(&platform_bus, dev_name);
62 : : snprintf(path, sizeof(path), PLATFORM_BUS_DEVICES_PATH "/%s/numa_node", dev_name);
63 [ # # ]: 0 : pdev->device.numa_node = eal_parse_sysfs_value(path, &val) ? rte_socket_id() : val;
64 : :
65 [ # # ]: 0 : RTE_BUS_FOREACH_DEV(tmp, &platform_bus) {
66 [ # # ]: 0 : if (!strcmp(tmp->name, pdev->name)) {
67 : 0 : PLATFORM_LOG_LINE(INFO, "device %s already added", pdev->name);
68 : :
69 [ # # ]: 0 : if (tmp->device.devargs != pdev->device.devargs)
70 : 0 : rte_devargs_remove(pdev->device.devargs);
71 : :
72 : 0 : free(pdev);
73 : 0 : return -EEXIST;
74 : : }
75 : : }
76 : :
77 : 0 : rte_bus_add_device(&platform_bus, &pdev->device);
78 : :
79 : 0 : PLATFORM_LOG_LINE(INFO, "adding device %s to the list", dev_name);
80 : :
81 : 0 : return 0;
82 : : }
83 : :
84 : : static char *
85 : 2442 : dev_kernel_driver_name(const char *dev_name)
86 : : {
87 : 2442 : char path[PATH_MAX], buf[BUFSIZ] = { };
88 : : char *kdrv;
89 : : int ret;
90 : :
91 : : snprintf(path, sizeof(path), PLATFORM_BUS_DEVICES_PATH "/%s/driver", dev_name);
92 : : /* save space for NUL */
93 : 2442 : ret = readlink(path, buf, sizeof(buf) - 1);
94 [ + + ]: 2442 : if (ret <= 0)
95 : : return NULL;
96 : :
97 : : /* last token is kernel driver name */
98 : 1332 : kdrv = strrchr(buf, '/');
99 [ + - ]: 1332 : if (kdrv != NULL)
100 : 1332 : return strdup(kdrv + 1);
101 : :
102 : : return NULL;
103 : : }
104 : :
105 : : static bool
106 : 2442 : dev_is_bound_vfio_platform(const char *dev_name)
107 : : {
108 : : char *kdrv;
109 : : int ret;
110 : :
111 : 2442 : kdrv = dev_kernel_driver_name(dev_name);
112 [ + + ]: 2442 : if (!kdrv)
113 : : return false;
114 : :
115 : 1332 : ret = strcmp(kdrv, "vfio-platform");
116 : 1332 : free(kdrv);
117 : :
118 : 1332 : return ret == 0;
119 : : }
120 : :
121 : : static int
122 : 225 : platform_bus_scan(void)
123 : : {
124 : : const struct dirent *ent;
125 : : const char *dev_name;
126 : : int ret = 0;
127 : : DIR *dp;
128 : :
129 : 225 : dp = opendir(PLATFORM_BUS_DEVICES_PATH);
130 [ - + ]: 225 : if (dp == NULL) {
131 : 0 : PLATFORM_LOG_LINE(INFO, "failed to open %s", PLATFORM_BUS_DEVICES_PATH);
132 : 0 : return -errno;
133 : : }
134 : :
135 [ + + ]: 3150 : while ((ent = readdir(dp))) {
136 : 2925 : dev_name = ent->d_name;
137 [ + + ]: 2925 : if (dev_name[0] == '.')
138 : 450 : continue;
139 : :
140 [ + + ]: 2475 : if (rte_bus_device_is_ignored(&platform_bus, dev_name))
141 : 33 : continue;
142 : :
143 [ + - ]: 2442 : if (!dev_is_bound_vfio_platform(dev_name))
144 : 2442 : continue;
145 : :
146 : 0 : ret = dev_add(dev_name);
147 [ # # ]: 0 : if (ret)
148 : : break;
149 : : }
150 : :
151 : 225 : closedir(dp);
152 : :
153 : 225 : return ret;
154 : : }
155 : :
156 : : static int
157 : 0 : device_map_resource_offset(struct rte_platform_device *pdev, struct rte_platform_resource *res,
158 : : size_t offset)
159 : : {
160 : 0 : res->mem.addr = mmap(NULL, res->mem.len, PROT_READ | PROT_WRITE, MAP_SHARED, pdev->dev_fd,
161 : : offset);
162 [ # # ]: 0 : if (res->mem.addr == MAP_FAILED)
163 : 0 : return -errno;
164 : :
165 : 0 : PLATFORM_LOG_LINE(DEBUG, "adding resource va = %p len = %"PRIu64" name = %s", res->mem.addr,
166 : : res->mem.len, res->name);
167 : :
168 : 0 : return 0;
169 : : }
170 : :
171 : : static void
172 : 0 : device_unmap_resources(struct rte_platform_device *pdev)
173 : : {
174 : : struct rte_platform_resource *res;
175 : : unsigned int i;
176 : :
177 [ # # ]: 0 : for (i = 0; i < pdev->num_resource; i++) {
178 : 0 : res = &pdev->resource[i];
179 : 0 : munmap(res->mem.addr, res->mem.len);
180 : 0 : free(res->name);
181 : : }
182 : :
183 : 0 : free(pdev->resource);
184 : 0 : pdev->resource = NULL;
185 : 0 : pdev->num_resource = 0;
186 : 0 : }
187 : :
188 : : static int
189 : 0 : read_sysfs_string(const char *path, char *buf, size_t size)
190 : : {
191 : : FILE *f;
192 : : char *p;
193 : :
194 : 0 : f = fopen(path, "r");
195 [ # # ]: 0 : if (f == NULL)
196 : 0 : return -errno;
197 : :
198 [ # # # # ]: 0 : if (fgets(buf, size, f) == NULL) {
199 : 0 : fclose(f);
200 : 0 : return -ENODATA;
201 : : }
202 : :
203 : 0 : fclose(f);
204 : :
205 : 0 : p = strrchr(buf, '\n');
206 [ # # ]: 0 : if (p != NULL)
207 : 0 : *p = '\0';
208 : :
209 : : return 0;
210 : : }
211 : :
212 : : static char *
213 : 0 : of_resource_name(const char *dev_name, int index)
214 : : {
215 : 0 : char path[PATH_MAX], buf[BUFSIZ] = { };
216 : : int num = 0, ret;
217 : : char *name;
218 : :
219 : : snprintf(path, sizeof(path), PLATFORM_BUS_DEVICES_PATH "/%s/of_node/reg-names", dev_name);
220 : 0 : ret = read_sysfs_string(path, buf, sizeof(buf) - 1);
221 [ # # ]: 0 : if (ret)
222 : : return NULL;
223 : :
224 [ # # ]: 0 : for (name = buf; *name != 0; name += strlen(name) + 1) {
225 [ # # ]: 0 : if (num++ != index)
226 : : continue;
227 : 0 : return strdup(name);
228 : : }
229 : :
230 : : return NULL;
231 : : }
232 : :
233 : : static int
234 : 0 : device_map_resources(struct rte_platform_device *pdev, unsigned int num)
235 : : {
236 : : struct rte_platform_resource *res;
237 : : unsigned int i;
238 : : int ret;
239 : :
240 [ # # ]: 0 : if (num == 0) {
241 : 0 : PLATFORM_LOG_LINE(WARNING, "device %s has no resources", pdev->name);
242 : 0 : return 0;
243 : : }
244 : :
245 : 0 : pdev->resource = calloc(num, sizeof(*pdev->resource));
246 [ # # ]: 0 : if (pdev->resource == NULL)
247 : : return -ENOMEM;
248 : :
249 [ # # ]: 0 : for (i = 0; i < num; i++) {
250 : 0 : struct vfio_region_info reg_info = {
251 : : .argsz = sizeof(reg_info),
252 : : .index = i,
253 : : };
254 : :
255 : 0 : ret = ioctl(pdev->dev_fd, VFIO_DEVICE_GET_REGION_INFO, ®_info);
256 [ # # ]: 0 : if (ret) {
257 : 0 : PLATFORM_LOG_LINE(ERR, "failed to get region info at %d", i);
258 : 0 : ret = -errno;
259 : 0 : goto out;
260 : : }
261 : :
262 : 0 : res = &pdev->resource[i];
263 : 0 : res->name = of_resource_name(pdev->name, reg_info.index);
264 : 0 : res->mem.len = reg_info.size;
265 : 0 : ret = device_map_resource_offset(pdev, res, reg_info.offset);
266 [ # # ]: 0 : if (ret) {
267 : 0 : PLATFORM_LOG_LINE(ERR, "failed to ioremap resource at %d", i);
268 : 0 : goto out;
269 : : }
270 : :
271 : 0 : pdev->num_resource++;
272 : : }
273 : :
274 : : return 0;
275 : : out:
276 : 0 : device_unmap_resources(pdev);
277 : :
278 : 0 : return ret;
279 : : }
280 : :
281 : : static void
282 : 0 : device_cleanup(struct rte_platform_device *pdev)
283 : : {
284 : 0 : device_unmap_resources(pdev);
285 : 0 : rte_vfio_release_device(PLATFORM_BUS_DEVICES_PATH, pdev->name, pdev->dev_fd);
286 : 0 : }
287 : :
288 : : static int
289 : 0 : device_setup(struct rte_platform_device *pdev)
290 : : {
291 : 0 : struct vfio_device_info dev_info = { .argsz = sizeof(dev_info), };
292 : 0 : const char *name = pdev->name;
293 : : int ret;
294 : :
295 : 0 : ret = rte_vfio_setup_device(PLATFORM_BUS_DEVICES_PATH, name, &pdev->dev_fd, &dev_info);
296 [ # # ]: 0 : if (ret) {
297 : 0 : PLATFORM_LOG_LINE(ERR, "failed to setup %s", name);
298 : 0 : return -ENODEV;
299 : : }
300 : :
301 : : /* This is an extra check to confirm that platform device was initialized
302 : : * by a kernel vfio-platform driver. On kernels that predate vfio-platform
303 : : * driver this flag obviously does not exist. In such scenarios this
304 : : * check needs to be removed otherwise compilation fails.
305 : : *
306 : : * Now, on such old kernels code will never reach here because
307 : : * there is another check much earlier which verifies whether
308 : : * device has been bound to vfio-platform driver.
309 : : */
310 : : #ifdef VFIO_DEVICE_FLAGS_PLATFORM
311 [ # # ]: 0 : if (!(dev_info.flags & VFIO_DEVICE_FLAGS_PLATFORM)) {
312 : 0 : PLATFORM_LOG_LINE(ERR, "device not backed by vfio-platform");
313 : : ret = -ENOTSUP;
314 : 0 : goto out;
315 : : }
316 : : #endif
317 : :
318 : 0 : ret = device_map_resources(pdev, dev_info.num_regions);
319 [ # # ]: 0 : if (ret) {
320 : 0 : PLATFORM_LOG_LINE(ERR, "failed to setup platform resources");
321 : 0 : goto out;
322 : : }
323 : :
324 : : return 0;
325 : 0 : out:
326 : 0 : device_cleanup(pdev);
327 : :
328 : 0 : return ret;
329 : : }
330 : :
331 : : static int
332 : : driver_call_probe(struct rte_platform_driver *pdrv, struct rte_platform_device *pdev)
333 : : {
334 [ # # ]: 0 : if (pdrv->probe != NULL)
335 : 0 : return pdrv->probe(pdev);
336 : :
337 : : return 0;
338 : : }
339 : :
340 : : static int
341 : 0 : driver_probe_device(struct rte_platform_driver *pdrv, struct rte_platform_device *pdev)
342 : : {
343 : : enum rte_iova_mode iova_mode;
344 : : int ret;
345 : :
346 : 0 : iova_mode = rte_eal_iova_mode();
347 [ # # # # ]: 0 : if (pdrv->drv_flags & RTE_PLATFORM_DRV_NEED_IOVA_AS_VA && iova_mode != RTE_IOVA_VA) {
348 : 0 : PLATFORM_LOG_LINE(ERR, "driver %s expects VA IOVA mode but current mode is PA",
349 : : pdrv->driver.name);
350 : 0 : return -EINVAL;
351 : : }
352 : :
353 : 0 : ret = device_setup(pdev);
354 [ # # ]: 0 : if (ret)
355 : : return ret;
356 : :
357 : : ret = driver_call_probe(pdrv, pdev);
358 [ # # ]: 0 : if (ret)
359 : 0 : device_cleanup(pdev);
360 : :
361 : : return ret;
362 : : }
363 : :
364 : : static bool
365 : 0 : platform_bus_match(const struct rte_driver *drv, const struct rte_device *dev)
366 : : {
367 : : const struct rte_platform_driver *pdrv = RTE_BUS_DRIVER(drv, *pdrv);
368 : : const struct rte_platform_device *pdev = RTE_BUS_DEVICE(dev, *pdev);
369 : : bool match = false;
370 : : char *kdrv;
371 : :
372 : 0 : kdrv = dev_kernel_driver_name(pdev->name);
373 [ # # ]: 0 : if (!kdrv)
374 : : return false;
375 : :
376 : : /* match by driver name */
377 [ # # ]: 0 : if (!strcmp(kdrv, pdrv->driver.name)) {
378 : : match = true;
379 : 0 : goto out;
380 : : }
381 : :
382 : : /* match by driver alias */
383 [ # # # # ]: 0 : if (pdrv->driver.alias != NULL && !strcmp(kdrv, pdrv->driver.alias)) {
384 : : match = true;
385 : 0 : goto out;
386 : : }
387 : :
388 : : /* match by device name */
389 [ # # ]: 0 : if (!strcmp(pdev->name, pdrv->driver.name))
390 : : match = true;
391 : :
392 : 0 : out:
393 : 0 : free(kdrv);
394 : :
395 : 0 : return match;
396 : : }
397 : :
398 : : static int
399 : 0 : platform_bus_probe_device(struct rte_driver *drv, struct rte_device *dev)
400 : : {
401 [ # # ]: 0 : if (!dev_is_bound_vfio_platform(dev->name))
402 : : return -EPERM;
403 : :
404 : 0 : return driver_probe_device(RTE_BUS_DRIVER(drv, struct rte_platform_driver),
405 : : RTE_BUS_DEVICE(dev, struct rte_platform_device));
406 : : }
407 : :
408 : : static void
409 : 0 : device_release_driver(struct rte_platform_device *pdev)
410 : : {
411 : 0 : const struct rte_platform_driver *pdrv = RTE_BUS_DRIVER(pdev->device.driver, *pdrv);
412 : : int ret;
413 : :
414 [ # # ]: 0 : if (pdrv->remove != NULL) {
415 : 0 : ret = pdrv->remove(pdev);
416 [ # # ]: 0 : if (ret)
417 : 0 : PLATFORM_LOG_LINE(WARNING, "failed to remove %s", pdev->name);
418 : : }
419 : 0 : }
420 : :
421 : : static int
422 : 0 : platform_bus_unplug_device(struct rte_device *dev)
423 : : {
424 : : struct rte_platform_device *pdev = RTE_BUS_DEVICE(dev, *pdev);
425 : :
426 : 0 : device_release_driver(pdev);
427 : 0 : device_cleanup(pdev);
428 : :
429 : 0 : return 0;
430 : : }
431 : :
432 : : static int
433 : 30 : platform_bus_parse(const char *name, void *addr)
434 : : {
435 : 30 : struct rte_platform_device pdev = { };
436 : : struct rte_platform_driver *pdrv;
437 : : const char **out = addr;
438 : :
439 : 30 : rte_strscpy(pdev.name, name, sizeof(pdev.name));
440 : :
441 [ - + ]: 30 : RTE_BUS_FOREACH_DRV(pdrv, &platform_bus) {
442 [ # # ]: 0 : if (platform_bus_match(&pdrv->driver, &pdev.device))
443 : : break;
444 : : }
445 : :
446 [ - + ]: 30 : if (pdrv != NULL && addr != NULL)
447 : 0 : *out = name;
448 : :
449 [ + - ]: 30 : return pdrv != NULL ? 0 : -ENODEV;
450 : : }
451 : :
452 : : static int
453 : 0 : platform_bus_dma_map(struct rte_device *dev, void *addr, uint64_t iova, size_t len)
454 : : {
455 : : struct rte_platform_device *pdev = RTE_BUS_DEVICE(dev, *pdev);
456 : 0 : const struct rte_platform_driver *pdrv = RTE_BUS_DRIVER(dev->driver, *pdrv);
457 : :
458 [ # # ]: 0 : if (pdrv->dma_map != NULL)
459 : 0 : return pdrv->dma_map(pdev, addr, iova, len);
460 : :
461 : 0 : return rte_vfio_container_dma_map(RTE_VFIO_DEFAULT_CONTAINER_FD, (uint64_t)addr, iova, len);
462 : : }
463 : :
464 : : static int
465 : 0 : platform_bus_dma_unmap(struct rte_device *dev, void *addr, uint64_t iova, size_t len)
466 : : {
467 : : struct rte_platform_device *pdev = RTE_BUS_DEVICE(dev, *pdev);
468 : 0 : const struct rte_platform_driver *pdrv = RTE_BUS_DRIVER(dev->driver, *pdrv);
469 : :
470 [ # # ]: 0 : if (pdrv->dma_unmap != NULL)
471 : 0 : return pdrv->dma_unmap(pdev, addr, iova, len);
472 : :
473 : 0 : return rte_vfio_container_dma_unmap(RTE_VFIO_DEFAULT_CONTAINER_FD, (uint64_t)addr, iova,
474 : : len);
475 : : }
476 : :
477 : : static enum rte_iova_mode
478 : 225 : platform_bus_get_iommu_class(void)
479 : : {
480 : : const struct rte_platform_driver *pdrv;
481 : : struct rte_platform_device *pdev;
482 : :
483 [ - + ]: 225 : RTE_BUS_FOREACH_DEV(pdev, &platform_bus) {
484 [ # # ]: 0 : if (!rte_dev_is_probed(&pdev->device))
485 : 0 : continue;
486 : 0 : pdrv = RTE_BUS_DRIVER(pdev->device.driver, *pdrv);
487 [ # # ]: 0 : if (pdrv->drv_flags & RTE_PLATFORM_DRV_NEED_IOVA_AS_VA)
488 : : return RTE_IOVA_VA;
489 : : }
490 : :
491 : : return RTE_IOVA_DC;
492 : : }
493 : :
494 : : static void
495 : 0 : platform_free_device(struct rte_device *dev)
496 : : {
497 : 0 : free(RTE_BUS_DEVICE(dev, struct rte_platform_device));
498 : 0 : }
499 : :
500 : : static struct rte_bus platform_bus = {
501 : : .scan = platform_bus_scan,
502 : : .probe = rte_bus_generic_probe,
503 : : .free_device = platform_free_device,
504 : : .cleanup = rte_bus_generic_cleanup,
505 : : .find_device = rte_bus_generic_find_device,
506 : : .match = platform_bus_match,
507 : : .probe_device = platform_bus_probe_device,
508 : : .unplug_device = platform_bus_unplug_device,
509 : : .parse = platform_bus_parse,
510 : : .dma_map = platform_bus_dma_map,
511 : : .dma_unmap = platform_bus_dma_unmap,
512 : : .get_iommu_class = platform_bus_get_iommu_class,
513 : : .dev_iterate = rte_bus_generic_dev_iterate,
514 : : };
515 : :
516 : 301 : RTE_REGISTER_BUS(platform, platform_bus);
517 [ - + ]: 301 : RTE_LOG_REGISTER_DEFAULT(platform_bus_logtype, NOTICE);
|