Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
3 : : */
4 : :
5 : : /*
6 : : * Architecture Overview
7 : : * =====================
8 : : * CDX is a Hardware Architecture designed for AMD FPGA devices. It
9 : : * consists of sophisticated mechanism for interaction between FPGA,
10 : : * Firmware and the APUs (Application CPUs).
11 : : *
12 : : * Firmware resides on RPU (Realtime CPUs) which interacts with
13 : : * the FPGA program manager and the APUs. The RPU provides memory-mapped
14 : : * interface (RPU if) which is used to communicate with APUs.
15 : : *
16 : : * The diagram below shows an overview of the AMD CDX architecture:
17 : : *
18 : : * +--------------------------------------+
19 : : * | DPDK |
20 : : * | DPDK CDX drivers |
21 : : * | | |
22 : : * | DPDK AMD CDX bus |
23 : : * | | |
24 : : * +-----------------------------|--------+
25 : : * |
26 : : * +-----------------------------|--------+
27 : : * | Application CPUs (APU) | |
28 : : * | | |
29 : : * | VFIO CDX driver |
30 : : * | Linux OS | |
31 : : * | Linux AMD CDX bus |
32 : : * | | |
33 : : * +-----------------------------|--------+
34 : : * |
35 : : * |
36 : : * +------------------------| RPU if |----+
37 : : * | | |
38 : : * | V |
39 : : * | Realtime CPUs (RPU) |
40 : : * | |
41 : : * +--------------------------------------+
42 : : * |
43 : : * +---------------------|----------------+
44 : : * | FPGA | |
45 : : * | +-----------------------+ |
46 : : * | | | | |
47 : : * | +-------+ +-------+ +-------+ |
48 : : * | | dev 1 | | dev 2 | | dev 3 | |
49 : : * | +-------+ +-------+ +-------+ |
50 : : * +--------------------------------------+
51 : : *
52 : : * The RPU firmware extracts the device information from the loaded FPGA
53 : : * image and implements a mechanism that allows the APU drivers to
54 : : * enumerate such devices (device personality and resource details) via
55 : : * a dedicated communication channel.
56 : : *
57 : : * VFIO CDX driver provides the CDX device resources like MMIO and interrupts
58 : : * to map to user-space. DPDK CDX bus uses sysfs interface and the vfio-cdx
59 : : * driver to discover and initialize the CDX devices for user-space
60 : : * applications.
61 : : */
62 : :
63 : : /**
64 : : * @file
65 : : * CDX probing using Linux sysfs.
66 : : */
67 : :
68 : : #include <string.h>
69 : : #include <dirent.h>
70 : :
71 : : #include <rte_eal_paging.h>
72 : : #include <rte_errno.h>
73 : : #include <rte_devargs.h>
74 : : #include <rte_kvargs.h>
75 : : #include <rte_malloc.h>
76 : : #include <rte_vfio.h>
77 : :
78 : : #include <eal_export.h>
79 : : #include <eal_filesystem.h>
80 : :
81 : : #include "bus_cdx_driver.h"
82 : : #include "cdx_logs.h"
83 : : #include "private.h"
84 : :
85 : : #define CDX_DEV_PREFIX "cdx-"
86 : :
87 : : static struct rte_bus rte_cdx_bus;
88 : :
89 : : static int
90 : 0 : cdx_get_kernel_driver_by_path(const char *filename, char *driver_name,
91 : : size_t len)
92 : : {
93 : : int count;
94 : : char path[PATH_MAX];
95 : : char *name;
96 : :
97 [ # # ]: 0 : if (!filename || !driver_name)
98 : : return -1;
99 : :
100 : 0 : count = readlink(filename, path, PATH_MAX);
101 [ # # ]: 0 : if (count >= PATH_MAX)
102 : : return -1;
103 : :
104 : : /* For device does not have a driver */
105 [ # # ]: 0 : if (count < 0)
106 : : return 1;
107 : :
108 : 0 : path[count] = '\0';
109 : :
110 : 0 : name = strrchr(path, '/');
111 [ # # ]: 0 : if (name) {
112 : 0 : strlcpy(driver_name, name + 1, len);
113 : 0 : return 0;
114 : : }
115 : :
116 : : return -1;
117 : : }
118 : :
119 : : RTE_EXPORT_INTERNAL_SYMBOL(rte_cdx_map_device)
120 : 0 : int rte_cdx_map_device(struct rte_cdx_device *dev)
121 : : {
122 : 0 : return cdx_vfio_map_resource(dev);
123 : : }
124 : :
125 : : RTE_EXPORT_INTERNAL_SYMBOL(rte_cdx_unmap_device)
126 : 0 : void rte_cdx_unmap_device(struct rte_cdx_device *dev)
127 : : {
128 : 0 : cdx_vfio_unmap_resource(dev);
129 : 0 : }
130 : :
131 : : /*
132 : : * Scan one cdx sysfs entry, and fill the devices list from it.
133 : : * It checks if the CDX device is bound to vfio-cdx driver. In case
134 : : * the device is vfio bound, it reads the vendor and device id and
135 : : * stores it for device-driver matching.
136 : : */
137 : : static int
138 : 0 : cdx_scan_one(const char *dirname, const char *dev_name)
139 : : {
140 : : char filename[PATH_MAX];
141 : : struct rte_cdx_device *dev = NULL;
142 : : char driver[PATH_MAX];
143 : : unsigned long tmp;
144 : : int ret;
145 : :
146 : 0 : dev = calloc(1, sizeof(*dev));
147 [ # # ]: 0 : if (!dev)
148 : : return -ENOMEM;
149 : :
150 : 0 : memcpy(dev->name, dev_name, RTE_DEV_NAME_MAX_LEN);
151 : 0 : dev->device.name = dev->name;
152 : :
153 : : /* parse driver */
154 : : snprintf(filename, sizeof(filename), "%s/driver", dirname);
155 : 0 : ret = cdx_get_kernel_driver_by_path(filename, driver, sizeof(driver));
156 [ # # ]: 0 : if (ret < 0) {
157 : 0 : CDX_BUS_ERR("Fail to get kernel driver");
158 : 0 : free(dev);
159 : 0 : return -1;
160 : : }
161 : :
162 : : /* Allocate interrupt instance for cdx device */
163 : 0 : dev->intr_handle =
164 : 0 : rte_intr_instance_alloc(RTE_INTR_INSTANCE_F_PRIVATE);
165 [ # # ]: 0 : if (dev->intr_handle == NULL) {
166 : 0 : CDX_BUS_ERR("Failed to create interrupt instance for %s",
167 : : dev->device.name);
168 : 0 : free(dev);
169 : 0 : return -ENOMEM;
170 : : }
171 : :
172 : : /*
173 : : * Check if device is bound to 'vfio-cdx' driver, so that user-space
174 : : * can gracefully access the device.
175 : : */
176 [ # # # # ]: 0 : if (ret || strcmp(driver, "vfio-cdx")) {
177 : : ret = 0;
178 : 0 : goto err;
179 : : }
180 : :
181 : : /* get vendor id */
182 : : snprintf(filename, sizeof(filename), "%s/vendor", dirname);
183 [ # # ]: 0 : if (eal_parse_sysfs_value(filename, &tmp) < 0) {
184 : : ret = -1;
185 : 0 : goto err;
186 : : }
187 : 0 : dev->id.vendor_id = (uint16_t)tmp;
188 : :
189 : : /* get device id */
190 : : snprintf(filename, sizeof(filename), "%s/device", dirname);
191 [ # # ]: 0 : if (eal_parse_sysfs_value(filename, &tmp) < 0) {
192 : : ret = -1;
193 : 0 : goto err;
194 : : }
195 : 0 : dev->id.device_id = (uint16_t)tmp;
196 : :
197 : 0 : rte_bus_add_device(&rte_cdx_bus, &dev->device);
198 : :
199 : 0 : return 0;
200 : :
201 : 0 : err:
202 : 0 : rte_intr_instance_free(dev->intr_handle);
203 : 0 : free(dev);
204 : 0 : return ret;
205 : : }
206 : :
207 : : /*
208 : : * Scan the content of the CDX bus, and the devices in the devices
209 : : * list.
210 : : */
211 : : static int
212 : 225 : cdx_scan(void)
213 : : {
214 : : struct dirent *e;
215 : : DIR *dir;
216 : : char dirname[PATH_MAX];
217 : :
218 : 225 : dir = opendir(RTE_CDX_BUS_DEVICES_PATH);
219 [ + - ]: 225 : if (dir == NULL) {
220 : 225 : CDX_BUS_INFO("%s(): opendir failed: %s", __func__,
221 : : strerror(errno));
222 : 225 : return 0;
223 : : }
224 : :
225 [ # # ]: 0 : while ((e = readdir(dir)) != NULL) {
226 [ # # ]: 0 : if (e->d_name[0] == '.')
227 : 0 : continue;
228 : :
229 [ # # ]: 0 : if (rte_bus_device_is_ignored(&rte_cdx_bus, e->d_name))
230 : 0 : continue;
231 : :
232 : : snprintf(dirname, sizeof(dirname), "%s/%s",
233 : : RTE_CDX_BUS_DEVICES_PATH, e->d_name);
234 : :
235 [ # # ]: 0 : if (cdx_scan_one(dirname, e->d_name) < 0)
236 : 0 : goto error;
237 : : }
238 : 0 : closedir(dir);
239 : 0 : return 0;
240 : :
241 : : error:
242 : 0 : closedir(dir);
243 : 0 : return -1;
244 : : }
245 : :
246 : : /* map a particular resource from a file */
247 : : void *
248 : 0 : cdx_map_resource(void *requested_addr, int fd, uint64_t offset, size_t size,
249 : : int additional_flags)
250 : : {
251 : : void *mapaddr;
252 : :
253 : : /* Map the cdx MMIO memory resource of device */
254 : 0 : mapaddr = rte_mem_map(requested_addr, size,
255 : : RTE_PROT_READ | RTE_PROT_WRITE,
256 : : RTE_MAP_SHARED | additional_flags, fd, offset);
257 [ # # ]: 0 : if (mapaddr == NULL) {
258 : 0 : CDX_BUS_ERR("%s(): cannot map resource(%d, %p, 0x%zx, 0x%"PRIx64"): %s (%p)",
259 : : __func__, fd, requested_addr, size, offset,
260 : : rte_strerror(rte_errno), mapaddr);
261 : : }
262 : 0 : CDX_BUS_DEBUG("CDX MMIO memory mapped at %p", mapaddr);
263 : :
264 : 0 : return mapaddr;
265 : : }
266 : :
267 : : /* unmap a particular resource */
268 : : void
269 : 0 : cdx_unmap_resource(void *requested_addr, size_t size)
270 : : {
271 [ # # ]: 0 : if (requested_addr == NULL)
272 : : return;
273 : :
274 : 0 : CDX_BUS_DEBUG("Unmapping CDX memory at %p", requested_addr);
275 : :
276 : : /* Unmap the CDX memory resource of device */
277 [ # # ]: 0 : if (rte_mem_unmap(requested_addr, size)) {
278 : 0 : CDX_BUS_ERR("%s(): cannot mem unmap(%p, %#zx): %s", __func__,
279 : : requested_addr, size, rte_strerror(rte_errno));
280 : : }
281 : : }
282 : :
283 : : static bool
284 : 0 : cdx_bus_match(const struct rte_driver *drv, const struct rte_device *dev)
285 : : {
286 : : const struct rte_cdx_driver *cdx_drv = RTE_BUS_DRIVER(drv, *cdx_drv);
287 : : const struct rte_cdx_device *cdx_dev = RTE_BUS_DEVICE(dev, *cdx_dev);
288 : : const struct rte_cdx_id *id_table;
289 : :
290 [ # # ]: 0 : for (id_table = cdx_drv->id_table; id_table->vendor_id != 0;
291 : 0 : id_table++) {
292 : : /* check if device's identifiers match the driver's ones */
293 [ # # # # ]: 0 : if (id_table->vendor_id != cdx_dev->id.vendor_id &&
294 : : id_table->vendor_id != RTE_CDX_ANY_ID)
295 : 0 : continue;
296 [ # # # # ]: 0 : if (id_table->device_id != cdx_dev->id.device_id &&
297 : : id_table->device_id != RTE_CDX_ANY_ID)
298 : 0 : continue;
299 : :
300 : : return true;
301 : : }
302 : :
303 : : return false;
304 : : }
305 : :
306 : : /*
307 : : * If vendor id and device id match, call the probe() function of the
308 : : * driver.
309 : : */
310 : : static int
311 : 0 : cdx_probe_device(struct rte_driver *drv, struct rte_device *dev)
312 : : {
313 : : struct rte_cdx_device *cdx_dev = RTE_BUS_DEVICE(dev, *cdx_dev);
314 : : struct rte_cdx_driver *cdx_drv = RTE_BUS_DRIVER(drv, *cdx_drv);
315 : 0 : const char *dev_name = cdx_dev->name;
316 : : int ret;
317 : :
318 : 0 : CDX_BUS_DEBUG(" probe device %s using driver: %s", dev_name,
319 : : cdx_drv->driver.name);
320 : :
321 [ # # ]: 0 : if (cdx_drv->drv_flags & RTE_CDX_DRV_NEED_MAPPING) {
322 : 0 : ret = cdx_vfio_map_resource(cdx_dev);
323 [ # # ]: 0 : if (ret != 0) {
324 : 0 : CDX_BUS_ERR("CDX map device failed: %d", ret);
325 : 0 : goto error_map_device;
326 : : }
327 : : }
328 : :
329 : : /* call the driver probe() function */
330 : 0 : ret = cdx_drv->probe(cdx_drv, cdx_dev);
331 [ # # ]: 0 : if (ret) {
332 : 0 : CDX_BUS_ERR("Probe CDX driver: %s device: %s failed: %d",
333 : : cdx_drv->driver.name, dev_name, ret);
334 : 0 : goto error_probe;
335 : : }
336 : :
337 : : return ret;
338 : :
339 : : error_probe:
340 : 0 : cdx_vfio_unmap_resource(cdx_dev);
341 : 0 : rte_intr_instance_free(cdx_dev->intr_handle);
342 : 0 : cdx_dev->intr_handle = NULL;
343 : : error_map_device:
344 : : return ret;
345 : : }
346 : :
347 : : static int
348 : 42 : cdx_parse(const char *name, void *addr)
349 : : {
350 : : const char **out = addr;
351 : : int ret;
352 : :
353 : 42 : ret = strncmp(name, CDX_DEV_PREFIX, strlen(CDX_DEV_PREFIX));
354 : :
355 [ - + ]: 42 : if (ret == 0 && addr)
356 : 0 : *out = name;
357 : :
358 : 42 : return ret;
359 : : }
360 : :
361 : : /* register a driver */
362 : : RTE_EXPORT_INTERNAL_SYMBOL(rte_cdx_register)
363 : : void
364 : 0 : rte_cdx_register(struct rte_cdx_driver *driver)
365 : : {
366 : 0 : rte_bus_add_driver(&rte_cdx_bus, &driver->driver);
367 : 0 : }
368 : :
369 : : /* unregister a driver */
370 : : RTE_EXPORT_INTERNAL_SYMBOL(rte_cdx_unregister)
371 : : void
372 : 0 : rte_cdx_unregister(struct rte_cdx_driver *driver)
373 : : {
374 : 0 : rte_bus_remove_driver(&rte_cdx_bus, &driver->driver);
375 : 0 : }
376 : :
377 : : static int
378 : 0 : cdx_unplug_device(struct rte_device *rte_dev)
379 : : {
380 : 0 : const struct rte_cdx_driver *dr = RTE_BUS_DRIVER(rte_dev->driver, *dr);
381 : : struct rte_cdx_device *dev = RTE_BUS_DEVICE(rte_dev, *dev);
382 : : int ret = 0;
383 : :
384 : 0 : CDX_BUS_DEBUG("detach device %s using driver: %s",
385 : : dev->device.name, dr->driver.name);
386 : :
387 [ # # ]: 0 : if (dr->remove != NULL) {
388 : 0 : ret = dr->remove(dev);
389 [ # # ]: 0 : if (ret < 0)
390 : : return ret;
391 : : }
392 : :
393 : 0 : rte_cdx_unmap_device(dev);
394 : :
395 : 0 : rte_intr_instance_free(dev->intr_handle);
396 : 0 : dev->intr_handle = NULL;
397 : :
398 : 0 : return 0;
399 : : }
400 : :
401 : : static int
402 : 0 : cdx_dma_map(struct rte_device *dev, void *addr, uint64_t iova, size_t len)
403 : : {
404 : : RTE_SET_USED(dev);
405 : :
406 : 0 : return rte_vfio_container_dma_map(RTE_VFIO_DEFAULT_CONTAINER_FD,
407 : : (uintptr_t)addr, iova, len);
408 : : }
409 : :
410 : : static int
411 : 0 : cdx_dma_unmap(struct rte_device *dev, void *addr, uint64_t iova, size_t len)
412 : : {
413 : : RTE_SET_USED(dev);
414 : :
415 : 0 : return rte_vfio_container_dma_unmap(RTE_VFIO_DEFAULT_CONTAINER_FD,
416 : : (uintptr_t)addr, iova, len);
417 : : }
418 : :
419 : : static enum rte_iova_mode
420 : 225 : cdx_get_iommu_class(void)
421 : : {
422 [ + - ]: 225 : if (TAILQ_EMPTY(&rte_cdx_bus.device_list))
423 : 225 : return RTE_IOVA_DC;
424 : :
425 : : return RTE_IOVA_VA;
426 : : }
427 : :
428 : : static struct rte_bus rte_cdx_bus = {
429 : : .scan = cdx_scan,
430 : : .probe = rte_bus_generic_probe,
431 : : .find_device = rte_bus_generic_find_device,
432 : : .match = cdx_bus_match,
433 : : .probe_device = cdx_probe_device,
434 : : .unplug_device = cdx_unplug_device,
435 : : .parse = cdx_parse,
436 : : .dma_map = cdx_dma_map,
437 : : .dma_unmap = cdx_dma_unmap,
438 : : .get_iommu_class = cdx_get_iommu_class,
439 : : .dev_iterate = rte_bus_generic_dev_iterate,
440 : : };
441 : :
442 : 301 : RTE_REGISTER_BUS(cdx, rte_cdx_bus);
443 [ - + ]: 301 : RTE_LOG_REGISTER_DEFAULT(cdx_logtype_bus, NOTICE);
|