NVIDIA DOCA SDK Data Center on a Chip Framework Documentation
devemu_pci_host_common.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2024 NVIDIA CORPORATION AND AFFILIATES. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without modification, are permitted
5  * provided that the following conditions are met:
6  * * Redistributions of source code must retain the above copyright notice, this list of
7  * conditions and the following disclaimer.
8  * * Redistributions in binary form must reproduce the above copyright notice, this list of
9  * conditions and the following disclaimer in the documentation and/or other materials
10  * provided with the distribution.
11  * * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
12  * to endorse or promote products derived from this software without specific prior written
13  * permission.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
17  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
20  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
21  * STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
22  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  *
24  */
25 
26 #include "devemu_pci_host_common.h"
27 
28 #include <linux/vfio.h>
29 #include <errno.h>
30 #include <unistd.h>
31 #include <sys/mman.h>
32 #include <sys/ioctl.h>
33 #include <fcntl.h>
34 
35 #include <doca_log.h>
36 #include <doca_dev.h>
37 
38 #define VFIO_GROUP_MAX_PATH 128
39 #define VFIO_CONTAINER_PATH "/dev/vfio/vfio"
40 #define VFIO_GROUP_PATH_FORMAT "/dev/vfio/%d"
41 
42 DOCA_LOG_REGISTER(DEVEMU_PCI_HOST_COMMON);
43 
44 /*
45  * Validates the available VFIO group and container support
46  *
47  * @group_fd [in]: The VFIO group fd
48  * @container_fd [in]: The VFIO container fd
49  * @return: true if valid, false otherwise
50  */
51 static bool validate_vfio_group_and_container(int group_fd, int container_fd)
52 {
53  int vfio_api_version = ioctl(container_fd, VFIO_GET_API_VERSION);
54  if (vfio_api_version != VFIO_API_VERSION) {
55  DOCA_LOG_ERR("VFIO API version mismatch. compiled with %d, but runtime is %d",
56  VFIO_API_VERSION,
57  vfio_api_version);
58  return false;
59  }
60 
61  if (ioctl(container_fd, VFIO_CHECK_EXTENSION, VFIO_TYPE1v2_IOMMU) == 0) {
62  DOCA_LOG_ERR("VFIO Type 1 IOMMU extension not supported");
63  return false;
64  }
65 
66  struct vfio_group_status group_status = {.argsz = sizeof(group_status)};
67 
68  int status = ioctl(group_fd, VFIO_GROUP_GET_STATUS, &group_status);
69  if (status != 0) {
70  DOCA_LOG_ERR("Failed to get status of VFIO group. Status=%d, errno=%d", status, errno);
71  return false;
72  }
73 
74  if ((group_status.flags & VFIO_GROUP_FLAGS_VIABLE) == 0) {
75  DOCA_LOG_ERR("VFIO group not viable. Not all devices in IOMMU group are bound to vfio driver");
76  return false;
77  }
78 
79  return true;
80 }
81 
82 /*
83  * Add the VFIO group to the container
84  *
85  * @group_fd [in]: The VFIO group fd
86  * @container_fd [in]: The VFIO container fd
87  * @return: true on success, false otherwise
88  */
89 static bool add_vfio_group_to_container(int group_fd, int container_fd)
90 {
91  int status = ioctl(group_fd, VFIO_GROUP_SET_CONTAINER, &container_fd);
92  if (status != 0) {
93  DOCA_LOG_ERR("Failed to set group for container. Status=%d, errno=%d", status, errno);
94  return false;
95  }
96 
97  status = ioctl(container_fd, VFIO_SET_IOMMU, VFIO_TYPE1v2_IOMMU);
98  if (status != 0) {
99  DOCA_LOG_ERR("Failed to set IOMMU type 1 extension for container. Status=%d, errno=%d", status, errno);
100  return false;
101  }
102 
103  return true;
104 }
105 
106 /*
107  * Enable the command bit in the PCI configuration space
108  *
109  * @resources [in]: The sample resources
110  * @return: true on success, false otherwise
111  */
113 {
114  struct vfio_region_info reg = {.argsz = sizeof(reg)};
115 
116  reg.index = VFIO_PCI_CONFIG_REGION_INDEX;
117 
118  int status = ioctl(resources->device_fd, VFIO_DEVICE_GET_REGION_INFO, &reg);
119  if (status != 0) {
120  DOCA_LOG_ERR("Failed to get Config Region info. Status=%d, errno=%d", status, errno);
121  return false;
122  }
123 
124  uint16_t cmd = 0x6;
125  if (pwrite(resources->device_fd, &cmd, 2, reg.offset + 0x4) != 2) {
126  DOCA_LOG_ERR("Failed to enable PCI cmd. Failed to write to Config Region Space. Status=%d, errno=%d",
127  status,
128  errno);
129  return false;
130  }
131 
132  return true;
133 }
134 
135 doca_error_t init_vfio_device(struct devemu_host_resources *resources, int vfio_group, const char *pci_address)
136 {
137  char vfio_group_path[VFIO_GROUP_MAX_PATH];
138 
139  resources->container_fd = open(VFIO_CONTAINER_PATH, O_RDWR);
140  if (resources->container_fd == -1) {
141  DOCA_LOG_ERR("Failed to open VFIO container. errno=%d", errno);
142  return DOCA_ERROR_DRIVER;
143  }
144 
145  snprintf(vfio_group_path, sizeof(vfio_group_path), VFIO_GROUP_PATH_FORMAT, vfio_group);
146 
147  resources->group_fd = open(vfio_group_path, O_RDWR);
148  if (resources->group_fd == -1) {
149  DOCA_LOG_ERR("Failed to open VFIO group. errno=%d", errno);
150  return DOCA_ERROR_DRIVER;
151  }
152 
153  if (!validate_vfio_group_and_container(resources->group_fd, resources->container_fd))
155 
156  if (!add_vfio_group_to_container(resources->group_fd, resources->container_fd))
157  return DOCA_ERROR_DRIVER;
158 
159  resources->device_fd = ioctl(resources->group_fd, VFIO_GROUP_GET_DEVICE_FD, pci_address);
160  if (resources->device_fd < 0) {
161  DOCA_LOG_ERR("Failed to get device fd. errno=%d", errno);
162  return DOCA_ERROR_DRIVER;
163  }
164 
166  return DOCA_ERROR_DRIVER;
167 
168  return DOCA_SUCCESS;
169 }
170 
172  const struct bar_region_config *bar_region_config,
173  struct bar_mapped_region *mapped_mem)
174 {
175  struct vfio_region_info reg = {.argsz = sizeof(reg)};
176 
177  reg.index = bar_region_config->bar_id;
178 
179  int status = ioctl(resources->device_fd, VFIO_DEVICE_GET_REGION_INFO, &reg);
180  if (status != 0) {
181  DOCA_LOG_ERR("Failed to get Bar Region info. Status=%d, errno=%d", status, errno);
182  return DOCA_ERROR_DRIVER;
183  }
184 
185  if (bar_region_config->start_address > reg.size ||
187  DOCA_LOG_ERR(
188  "The provided stateful region exceeds the boundaries of the emulated device's bar. At bar %d, the size is %lluB, but requested to map region with start address %zu, and size of %zuB",
190  reg.size,
194  }
195 
196  uint8_t *mem = mmap(0,
198  PROT_READ | PROT_WRITE,
199  MAP_SHARED,
200  resources->device_fd,
201  reg.offset + bar_region_config->start_address);
202  if (mem == MAP_FAILED) {
203  DOCA_LOG_ERR("Failed to memory map bar region: bar %d start address %zu size %zuB",
207  return DOCA_ERROR_DRIVER;
208  }
209 
210  mapped_mem->size = bar_region_config->size;
211  mapped_mem->mem = mem;
212 
213  return DOCA_SUCCESS;
214 }
215 
217 {
218  if (resources->dma_mem.mem != NULL)
219  munmap(resources->dma_mem.mem, resources->dma_mem.size);
220  if (resources->device_fd != -1)
221  close(resources->device_fd);
222  if (resources->group_fd != -1)
223  close(resources->group_fd);
224  if (resources->container_fd != -1)
225  close(resources->container_fd);
226 
227  if (resources->stateful_region.mem != NULL)
228  munmap(resources->stateful_region.mem, resources->stateful_region.size);
229  if (resources->db_region.mem != NULL)
230  munmap(resources->db_region.mem, resources->db_region.size);
231  if (resources->msix_vector_to_fd != NULL)
232  free(resources->msix_vector_to_fd);
233 }
234 
235 doca_error_t parse_emulated_pci_address(const char *addr, char *parsed_addr)
236 {
237  int addr_len = strnlen(addr, DOCA_DEVINFO_PCI_ADDR_SIZE) + 1;
238 
239  /* Check using > to make static code analysis satisfied */
240  if (addr_len > DOCA_DEVINFO_PCI_ADDR_SIZE) {
241  DOCA_LOG_ERR("Entered device PCI address exceeding the maximum size of %d",
244  }
245 
246  if (addr_len != DOCA_DEVINFO_PCI_ADDR_SIZE) {
247  DOCA_LOG_ERR("Entered device PCI address does not match supported format: XXXX:XX:XX.X");
249  }
250 
251  strncpy(parsed_addr, addr, addr_len - 1);
252  parsed_addr[addr_len - 1] = 0;
253 
254  return DOCA_SUCCESS;
255 }
256 
258 {
259  struct doca_argp_param *param;
261 
262  result = doca_argp_param_create(&param);
263  if (result != DOCA_SUCCESS) {
264  DOCA_LOG_ERR("Failed to create ARGP param: %s", doca_error_get_descr(result));
265  return result;
266  }
267  doca_argp_param_set_short_name(param, "p");
268  doca_argp_param_set_long_name(param, "pci-addr");
269  doca_argp_param_set_description(param, "PCI address of the emulated device. Format: XXXX:XX:XX.X");
274  if (result != DOCA_SUCCESS) {
275  DOCA_LOG_ERR("Failed to register program param: %s", doca_error_get_descr(result));
276  return result;
277  }
278 
279  return DOCA_SUCCESS;
280 }
281 
283 {
284  struct doca_argp_param *vfio_group_param;
286 
287  result = doca_argp_param_create(&vfio_group_param);
288  if (result != DOCA_SUCCESS) {
289  DOCA_LOG_ERR("Failed to create ARGP param: %s", doca_error_get_descr(result));
290  return result;
291  }
292  doca_argp_param_set_short_name(vfio_group_param, "g");
293  doca_argp_param_set_long_name(vfio_group_param, "vfio-group");
294  doca_argp_param_set_description(vfio_group_param, "VFIO group ID of the device. Integer");
296  doca_argp_param_set_type(vfio_group_param, DOCA_ARGP_TYPE_INT);
297  doca_argp_param_set_mandatory(vfio_group_param);
298  result = doca_argp_register_param(vfio_group_param);
299  if (result != DOCA_SUCCESS) {
300  DOCA_LOG_ERR("Failed to register program param: %s", doca_error_get_descr(result));
301  return result;
302  }
303 
304  return DOCA_SUCCESS;
305 }
306 
308 {
309  struct doca_argp_param *region_index_param;
311 
312  result = doca_argp_param_create(&region_index_param);
313  if (result != DOCA_SUCCESS) {
314  DOCA_LOG_ERR("Failed to create ARGP param: %s", doca_error_get_descr(result));
315  return result;
316  }
317  doca_argp_param_set_short_name(region_index_param, "r");
318  doca_argp_param_set_long_name(region_index_param, "region-index");
319  doca_argp_param_set_description(region_index_param, description);
320  doca_argp_param_set_callback(region_index_param, region_callback);
321  doca_argp_param_set_type(region_index_param, DOCA_ARGP_TYPE_INT);
322  result = doca_argp_register_param(region_index_param);
323  if (result != DOCA_SUCCESS) {
324  DOCA_LOG_ERR("Failed to register program param: %s", doca_error_get_descr(result));
325  return result;
326  }
327 
328  return DOCA_SUCCESS;
329 }
#define NULL
Definition: __stddef_null.h:26
int32_t result
static doca_error_t pci_callback(void *param, void *config)
static doca_error_t vfio_group_callback(void *param, void *config)
void devemu_host_resources_cleanup(struct devemu_host_resources *resources)
#define VFIO_CONTAINER_PATH
doca_error_t register_vfio_group_param(doca_argp_param_cb_t vfio_group_callback)
doca_error_t register_region_index_param(const char *description, doca_argp_param_cb_t region_callback)
#define VFIO_GROUP_PATH_FORMAT
static bool enable_pci_cmd(struct devemu_host_resources *resources)
doca_error_t register_emulated_pci_address_param(doca_argp_param_cb_t pci_callback)
static bool validate_vfio_group_and_container(int group_fd, int container_fd)
doca_error_t parse_emulated_pci_address(const char *addr, char *parsed_addr)
static bool add_vfio_group_to_container(int group_fd, int container_fd)
doca_error_t map_bar_region_memory(struct devemu_host_resources *resources, const struct bar_region_config *bar_region_config, struct bar_mapped_region *mapped_mem)
DOCA_LOG_REGISTER(DEVEMU_PCI_HOST_COMMON)
#define VFIO_GROUP_MAX_PATH
doca_error_t init_vfio_device(struct devemu_host_resources *resources, int vfio_group, const char *pci_address)
uintptr_t addr
doca_dpa_dev_mmap_t mmap
struct rdma_resources resources
DOCA_EXPERIMENTAL void doca_argp_param_set_description(struct doca_argp_param *param, const char *description)
Set the description of the program param, used during program usage.
DOCA_EXPERIMENTAL void doca_argp_param_set_long_name(struct doca_argp_param *param, const char *name)
Set the long name of the program param.
doca_error_t(* doca_argp_param_cb_t)(void *, void *)
Flag callback function type.
Definition: doca_argp.h:37
DOCA_EXPERIMENTAL void doca_argp_param_set_callback(struct doca_argp_param *param, doca_argp_param_cb_t callback)
Set the callback function of the program param.
DOCA_EXPERIMENTAL void doca_argp_param_set_mandatory(struct doca_argp_param *param)
Mark the program param as mandatory.
DOCA_EXPERIMENTAL doca_error_t doca_argp_param_create(struct doca_argp_param **param)
Create new program param.
DOCA_EXPERIMENTAL void doca_argp_param_set_type(struct doca_argp_param *param, enum doca_argp_type type)
Set the type of the param arguments.
DOCA_EXPERIMENTAL void doca_argp_param_set_short_name(struct doca_argp_param *param, const char *name)
Set the short name of the program param.
DOCA_EXPERIMENTAL doca_error_t doca_argp_register_param(struct doca_argp_param *input_param)
Register a program flag.
@ DOCA_ARGP_TYPE_STRING
Definition: doca_argp.h:56
@ DOCA_ARGP_TYPE_INT
Definition: doca_argp.h:57
#define DOCA_DEVINFO_PCI_ADDR_SIZE
Buffer size to hold PCI BDF format: "XXXX:XX:XX.X". Including a null terminator.
Definition: doca_dev.h:313
enum doca_error doca_error_t
DOCA API return codes.
DOCA_STABLE const char * doca_error_get_descr(doca_error_t error)
Returns the description string of an error code.
@ DOCA_ERROR_INVALID_VALUE
Definition: doca_error.h:44
@ DOCA_ERROR_NOT_SUPPORTED
Definition: doca_error.h:42
@ DOCA_SUCCESS
Definition: doca_error.h:38
@ DOCA_ERROR_DRIVER
Definition: doca_error.h:59
#define DOCA_LOG_ERR(format,...)
Generates an ERROR application log message.
Definition: doca_log.h:466
type description