blob: a4e9eb2dda6cd816cc031bb8b0c94027aa55ef79 [file] [log] [blame]
/*
* SPDX-License-Identifier: Apache-2.0
* Copyright (c) 2024 Tom Jones <thj@freebsd.org>
*
* This software was developed by Tom Jones <thj@freebsd.org> under sponsorship
* from the FreeBSD Foundation.
*
*/
#include <vlib/vlib.h>
#include <vlib/pci/pci.h>
#include <vlib/unix/unix.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/eventfd.h>
#include <sys/pciio.h>
#include <fcntl.h>
#include <dirent.h>
#include <net/if.h>
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"),
};