/* * SPDX-License-Identifier: Apache-2.0 * Copyright (c) 2024 Tom Jones * * This software was developed by Tom Jones under sponsorship * from the FreeBSD Foundation. * */ #include #include #include #include #include #include #include #include #include #include #include extern vlib_pci_main_t freebsd_pci_main; uword vlib_pci_get_private_data (vlib_main_t *vm, vlib_pci_dev_handle_t h) { return 0; } void vlib_pci_set_private_data (vlib_main_t *vm, vlib_pci_dev_handle_t h, uword private_data) { } vlib_pci_addr_t * vlib_pci_get_addr (vlib_main_t *vm, vlib_pci_dev_handle_t h) { return NULL; } u32 vlib_pci_get_numa_node (vlib_main_t *vm, vlib_pci_dev_handle_t h) { return 0; } u32 vlib_pci_get_num_msix_interrupts (vlib_main_t *vm, vlib_pci_dev_handle_t h) { return 0; } /* Call to allocate/initialize the pci subsystem. This is not an init function so that users can explicitly enable pci only when it's needed. */ clib_error_t *pci_bus_init (vlib_main_t *vm); vlib_pci_device_info_t * vlib_pci_get_device_info (vlib_main_t *vm, vlib_pci_addr_t *addr, clib_error_t **error) { /* Populate a vlib_pci_device_info_t from the given address */ clib_error_t *err = NULL; vlib_pci_device_info_t *di = NULL; int fd = -1; struct pci_conf_io pci; struct pci_conf match; struct pci_match_conf pattern; bzero (&match, sizeof (match)); bzero (&pattern, sizeof (pattern)); pattern.pc_sel.pc_domain = addr->domain; pattern.pc_sel.pc_bus = addr->bus; pattern.pc_sel.pc_dev = addr->slot; pattern.pc_sel.pc_func = addr->function; pattern.flags = PCI_GETCONF_MATCH_DOMAIN | PCI_GETCONF_MATCH_BUS | PCI_GETCONF_MATCH_DEV | PCI_GETCONF_MATCH_FUNC; pci.pat_buf_len = sizeof (pattern); pci.num_patterns = 1; pci.patterns = &pattern; pci.match_buf_len = sizeof (match); pci.num_matches = 1; pci.matches = &match; pci.offset = 0; pci.generation = 0; pci.status = 0; fd = open ("/dev/pci", 0); if (fd == -1) { err = clib_error_return_unix (0, "open '/dev/pci'"); goto error; } if (ioctl (fd, PCIOCGETCONF, &pci) == -1) { err = clib_error_return_unix (0, "reading PCIOCGETCONF"); goto error; } di = clib_mem_alloc (sizeof (vlib_pci_device_info_t)); clib_memset (di, 0, sizeof (vlib_pci_device_info_t)); di->addr.as_u32 = addr->as_u32; di->numa_node = 0; /* TODO: Place holder until we have NUMA on FreeBSD */ di->device_class = match.pc_class; di->vendor_id = match.pc_vendor; di->device_id = match.pc_device; di->revision = match.pc_revid; di->product_name = NULL; di->vpd_r = 0; di->vpd_w = 0; di->driver_name = format (0, "%s", &match.pd_name); di->iommu_group = -1; goto done; error: vlib_pci_free_device_info (di); di = NULL; done: if (error) *error = err; close (fd); return di; } clib_error_t *__attribute__ ((weak)) vlib_pci_get_device_root_bus (vlib_pci_addr_t *addr, vlib_pci_addr_t *root_bus) { return NULL; } clib_error_t * vlib_pci_bind_to_uio (vlib_main_t *vm, vlib_pci_addr_t *addr, char *uio_drv_name, int force) { clib_error_t *error = 0; if (error) { return error; } if (strncmp ("auto", uio_drv_name, 5) == 0) { /* TODO: We should confirm that nic_uio is loaded and return an error. */ uio_drv_name = "nic_uio"; } return error; } clib_error_t * vlib_pci_register_intx_handler (vlib_main_t *vm, vlib_pci_dev_handle_t h, pci_intx_handler_function_t *intx_handler) { return NULL; } clib_error_t * vlib_pci_unregister_intx_handler (vlib_main_t *vm, vlib_pci_dev_handle_t h) { return NULL; } clib_error_t * vlib_pci_register_msix_handler (vlib_main_t *vm, vlib_pci_dev_handle_t h, u32 start, u32 count, pci_msix_handler_function_t *msix_handler) { return NULL; } clib_error_t * vlib_pci_unregister_msix_handler (vlib_main_t *vm, vlib_pci_dev_handle_t h, u32 start, u32 count) { return NULL; } clib_error_t * vlib_pci_enable_msix_irq (vlib_main_t *vm, vlib_pci_dev_handle_t h, u16 start, u16 count) { return NULL; } uword vlib_pci_get_msix_file_index (vlib_main_t *vm, vlib_pci_dev_handle_t h, u16 index) { return 0; } clib_error_t * vlib_pci_disable_msix_irq (vlib_main_t *vm, vlib_pci_dev_handle_t h, u16 start, u16 count) { return NULL; } /* Configuration space read/write. */ clib_error_t * vlib_pci_read_write_config (vlib_main_t *vm, vlib_pci_dev_handle_t h, vlib_read_or_write_t read_or_write, uword address, void *data, u32 n_bytes) { return NULL; } clib_error_t * vlib_pci_map_region (vlib_main_t *vm, vlib_pci_dev_handle_t h, u32 resource, void **result) { return NULL; } clib_error_t * vlib_pci_map_region_fixed (vlib_main_t *vm, vlib_pci_dev_handle_t h, u32 resource, u8 *addr, void **result) { return NULL; } clib_error_t * vlib_pci_io_region (vlib_main_t *vm, vlib_pci_dev_handle_t h, u32 resource) { return NULL; } clib_error_t * vlib_pci_read_write_io (vlib_main_t *vm, vlib_pci_dev_handle_t h, vlib_read_or_write_t read_or_write, uword offset, void *data, u32 length) { return NULL; } clib_error_t * vlib_pci_map_dma (vlib_main_t *vm, vlib_pci_dev_handle_t h, void *ptr) { return NULL; } int vlib_pci_supports_virtual_addr_dma (vlib_main_t *vm, vlib_pci_dev_handle_t h) { return 0; } clib_error_t * vlib_pci_device_open (vlib_main_t *vm, vlib_pci_addr_t *addr, pci_device_id_t ids[], vlib_pci_dev_handle_t *handle) { return NULL; } void vlib_pci_device_close (vlib_main_t *vm, vlib_pci_dev_handle_t h) { } void init_device_from_registered (vlib_main_t *vm, vlib_pci_device_info_t *di) { } static int pci_addr_cmp (void *v1, void *v2) { vlib_pci_addr_t *a1 = v1; vlib_pci_addr_t *a2 = v2; if (a1->domain > a2->domain) return 1; if (a1->domain < a2->domain) return -1; if (a1->bus > a2->bus) return 1; if (a1->bus < a2->bus) return -1; if (a1->slot > a2->slot) return 1; if (a1->slot < a2->slot) return -1; if (a1->function > a2->function) return 1; if (a1->function < a2->function) return -1; return 0; } vlib_pci_addr_t * vlib_pci_get_all_dev_addrs () { vlib_pci_addr_t *addrs = 0; int fd = -1; struct pci_conf_io pci; struct pci_conf matches[32]; bzero (matches, sizeof (matches)); pci.pat_buf_len = 0; pci.num_patterns = 0; pci.patterns = NULL; pci.match_buf_len = sizeof (matches); pci.num_matches = 32; pci.matches = (struct pci_conf *) &matches; pci.offset = 0; pci.generation = 0; pci.status = 0; fd = open ("/dev/pci", 0); if (fd == -1) { clib_error_return_unix (0, "opening /dev/pci"); return (NULL); } if (ioctl (fd, PCIOCGETCONF, &pci) == -1) { clib_error_return_unix (0, "reading pci config"); close (fd); return (NULL); } for (int i = 0; i < pci.num_matches; i++) { struct pci_conf *m = &pci.matches[i]; vlib_pci_addr_t addr; addr.domain = m->pc_sel.pc_domain; addr.bus = m->pc_sel.pc_bus; addr.slot = m->pc_sel.pc_dev; addr.function = m->pc_sel.pc_func; vec_add1 (addrs, addr); } vec_sort_with_function (addrs, pci_addr_cmp); close (fd); return addrs; } clib_error_t * freebsd_pci_init (vlib_main_t *vm) { vlib_pci_main_t *pm = &pci_main; vlib_pci_addr_t *addr = 0, *addrs; pm->vlib_main = vm; ASSERT (sizeof (vlib_pci_addr_t) == sizeof (u32)); addrs = vlib_pci_get_all_dev_addrs (); vec_foreach (addr, addrs) { vlib_pci_device_info_t *d; if ((d = vlib_pci_get_device_info (vm, addr, 0))) { init_device_from_registered (vm, d); vlib_pci_free_device_info (d); } } return 0; } VLIB_INIT_FUNCTION (freebsd_pci_init) = { .runs_after = VLIB_INITS ("unix_input_init"), };