dpdk/kernel/freebsd/nic_uio/nic_uio.c
<<
>>
Prefs
   1/* SPDX-License-Identifier: BSD-3-Clause
   2 * Copyright(c) 2010-2014 Intel Corporation
   3 */
   4#include <sys/cdefs.h>
   5__FBSDID("$FreeBSD$");
   6
   7#include <sys/param.h> /* defines used in kernel.h */
   8#include <sys/module.h>
   9#include <sys/kernel.h> /* types used in module initialization */
  10#include <sys/conf.h> /* cdevsw struct */
  11#include <sys/bus.h> /* structs, prototypes for pci bus stuff and DEVMETHOD */
  12#include <sys/rman.h>
  13#include <sys/systm.h>
  14#include <sys/lock.h>
  15#include <sys/rwlock.h>
  16#include <sys/proc.h>
  17
  18#include <machine/bus.h>
  19#include <dev/pci/pcivar.h> /* For pci_get macros! */
  20#include <dev/pci/pcireg.h> /* The softc holds our per-instance data. */
  21#include <vm/vm.h>
  22#include <vm/uma.h>
  23#include <vm/vm_object.h>
  24#include <vm/vm_page.h>
  25#include <vm/vm_pager.h>
  26
  27
  28#define MAX_BARS (PCIR_MAX_BAR_0 + 1)
  29
  30#define MAX_DETACHED_DEVICES    128
  31static device_t detached_devices[MAX_DETACHED_DEVICES] = {};
  32static int num_detached = 0;
  33
  34struct nic_uio_softc {
  35        device_t        dev_t;
  36        struct cdev     *my_cdev;
  37        int              bar_id[MAX_BARS];
  38        struct resource *bar_res[MAX_BARS];
  39        u_long           bar_start[MAX_BARS];
  40        u_long           bar_size[MAX_BARS];
  41};
  42
  43/* Function prototypes */
  44static d_open_t         nic_uio_open;
  45static d_close_t        nic_uio_close;
  46static d_mmap_t         nic_uio_mmap;
  47static d_mmap_single_t  nic_uio_mmap_single;
  48static int              nic_uio_probe(device_t dev);
  49static int              nic_uio_attach(device_t dev);
  50static int              nic_uio_detach(device_t dev);
  51static int              nic_uio_shutdown(void);
  52static int              nic_uio_modevent(module_t mod, int type, void *arg);
  53
  54static struct cdevsw uio_cdevsw = {
  55                .d_name        = "nic_uio",
  56                .d_version     = D_VERSION,
  57                .d_open        = nic_uio_open,
  58                .d_close       = nic_uio_close,
  59                .d_mmap        = nic_uio_mmap,
  60                .d_mmap_single = nic_uio_mmap_single,
  61};
  62
  63static device_method_t nic_uio_methods[] = {
  64        DEVMETHOD(device_probe,    nic_uio_probe),
  65        DEVMETHOD(device_attach,   nic_uio_attach),
  66        DEVMETHOD(device_detach,   nic_uio_detach),
  67        DEVMETHOD_END
  68};
  69
  70struct device {
  71    int vend;
  72    int dev;
  73};
  74
  75struct pci_bdf {
  76        uint32_t bus;
  77        uint32_t devid;
  78        uint32_t function;
  79};
  80
  81static devclass_t nic_uio_devclass;
  82
  83DEFINE_CLASS_0(nic_uio, nic_uio_driver, nic_uio_methods, sizeof(struct nic_uio_softc));
  84DRIVER_MODULE(nic_uio, pci, nic_uio_driver, nic_uio_devclass, nic_uio_modevent, 0);
  85
  86static int
  87nic_uio_mmap(struct cdev *cdev, vm_ooffset_t offset, vm_paddr_t *paddr,
  88                int prot, vm_memattr_t *memattr)
  89{
  90        *paddr = offset;
  91        return 0;
  92}
  93
  94static int
  95nic_uio_mmap_single(struct cdev *cdev, vm_ooffset_t *offset, vm_size_t size,
  96                struct vm_object **obj, int nprot)
  97{
  98        /*
  99         * The BAR index is encoded in the offset.  Divide the offset by
 100         *  PAGE_SIZE to get the index of the bar requested by the user
 101         *  app.
 102         */
 103        unsigned bar = *offset/PAGE_SIZE;
 104        struct nic_uio_softc *sc = cdev->si_drv1;
 105
 106        if (bar >= MAX_BARS)
 107                return EINVAL;
 108
 109        if (sc->bar_res[bar] == NULL) {
 110                sc->bar_id[bar] = PCIR_BAR(bar);
 111
 112                if (PCI_BAR_IO(pci_read_config(sc->dev_t, sc->bar_id[bar], 4)))
 113                        sc->bar_res[bar] = bus_alloc_resource_any(sc->dev_t, SYS_RES_IOPORT,
 114                                        &sc->bar_id[bar], RF_ACTIVE);
 115                else
 116                        sc->bar_res[bar] = bus_alloc_resource_any(sc->dev_t, SYS_RES_MEMORY,
 117                                        &sc->bar_id[bar], RF_ACTIVE);
 118        }
 119        if (sc->bar_res[bar] == NULL)
 120                return ENXIO;
 121
 122        sc->bar_start[bar] = rman_get_start(sc->bar_res[bar]);
 123        sc->bar_size[bar] = rman_get_size(sc->bar_res[bar]);
 124
 125        device_printf(sc->dev_t, "Bar %u @ %lx, size %lx\n", bar,
 126                        sc->bar_start[bar], sc->bar_size[bar]);
 127
 128        *offset = sc->bar_start[bar];
 129        *obj = vm_pager_allocate(OBJT_DEVICE, cdev, size, nprot, *offset,
 130                                curthread->td_ucred);
 131        return 0;
 132}
 133
 134
 135int
 136nic_uio_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
 137{
 138        return 0;
 139}
 140
 141int
 142nic_uio_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
 143{
 144        return 0;
 145}
 146
 147static int
 148nic_uio_probe (device_t dev)
 149{
 150        int i;
 151        unsigned int bus = pci_get_bus(dev);
 152        unsigned int device = pci_get_slot(dev);
 153        unsigned int function = pci_get_function(dev);
 154
 155        char bdf_str[256];
 156        char *token, *remaining;
 157
 158        /* First check if we found this on load */
 159        for (i = 0; i < num_detached; i++)
 160                if (bus == pci_get_bus(detached_devices[i]) &&
 161                    device == pci_get_slot(detached_devices[i]) &&
 162                    function == pci_get_function(detached_devices[i])) {
 163                        device_set_desc(dev, "DPDK PCI Device");
 164                        return BUS_PROBE_SPECIFIC;
 165                }
 166
 167        /* otherwise check if it's a new device and if it matches the BDF */
 168        memset(bdf_str, 0, sizeof(bdf_str));
 169        TUNABLE_STR_FETCH("hw.nic_uio.bdfs", bdf_str, sizeof(bdf_str));
 170        remaining = bdf_str;
 171        while (1) {
 172                if (remaining == NULL || remaining[0] == '\0')
 173                        break;
 174                token = strsep(&remaining, ",:");
 175                if (token == NULL)
 176                        break;
 177                bus = strtol(token, NULL, 10);
 178                token = strsep(&remaining, ",:");
 179                if (token == NULL)
 180                        break;
 181                device = strtol(token, NULL, 10);
 182                token = strsep(&remaining, ",:");
 183                if (token == NULL)
 184                        break;
 185                function = strtol(token, NULL, 10);
 186
 187                if (bus == pci_get_bus(dev) &&
 188                                device == pci_get_slot(dev) &&
 189                                function == pci_get_function(dev)) {
 190
 191                        if (num_detached < MAX_DETACHED_DEVICES) {
 192                                printf("%s: probed dev=%p\n",
 193                                               __func__, dev);
 194                                detached_devices[num_detached++] = dev;
 195                                device_set_desc(dev, "DPDK PCI Device");
 196                                return BUS_PROBE_SPECIFIC;
 197                        } else {
 198                                printf("%s: reached MAX_DETACHED_DEVICES=%d. dev=%p won't be reattached\n",
 199                                                __func__, MAX_DETACHED_DEVICES,
 200                                                dev);
 201                                break;
 202                        }
 203                }
 204        }
 205
 206        return ENXIO;
 207}
 208
 209static int
 210nic_uio_attach(device_t dev)
 211{
 212        int i;
 213        struct nic_uio_softc *sc;
 214
 215        sc = device_get_softc(dev);
 216        sc->dev_t = dev;
 217        sc->my_cdev = make_dev(&uio_cdevsw, device_get_unit(dev),
 218                        UID_ROOT, GID_WHEEL, 0600, "uio@pci:%u:%u:%u",
 219                        pci_get_bus(dev), pci_get_slot(dev), pci_get_function(dev));
 220        if (sc->my_cdev == NULL)
 221                return ENXIO;
 222        sc->my_cdev->si_drv1 = sc;
 223
 224        for (i = 0; i < MAX_BARS; i++)
 225                sc->bar_res[i] = NULL;
 226
 227        pci_enable_busmaster(dev);
 228
 229        return 0;
 230}
 231
 232static int
 233nic_uio_detach(device_t dev)
 234{
 235        int i;
 236        struct nic_uio_softc *sc;
 237        sc = device_get_softc(dev);
 238
 239        for (i = 0; i < MAX_BARS; i++)
 240                if (sc->bar_res[i] != NULL) {
 241
 242                        if (PCI_BAR_IO(pci_read_config(dev, sc->bar_id[i], 4)))
 243                                bus_release_resource(dev, SYS_RES_IOPORT, sc->bar_id[i],
 244                                                sc->bar_res[i]);
 245                        else
 246                                bus_release_resource(dev, SYS_RES_MEMORY, sc->bar_id[i],
 247                                                sc->bar_res[i]);
 248                }
 249
 250        if (sc->my_cdev != NULL)
 251                destroy_dev(sc->my_cdev);
 252        return 0;
 253}
 254
 255static void
 256nic_uio_load(void)
 257{
 258        uint32_t bus, device, function;
 259        device_t dev;
 260        char bdf_str[256];
 261        char *token, *remaining;
 262
 263        memset(bdf_str, 0, sizeof(bdf_str));
 264        TUNABLE_STR_FETCH("hw.nic_uio.bdfs", bdf_str, sizeof(bdf_str));
 265        remaining = bdf_str;
 266        printf("nic_uio: hw.nic_uio.bdfs = '%s'\n", bdf_str);
 267        /*
 268         * Users should specify PCI BDFs in the format "b:d:f,b:d:f,b:d:f".
 269         *  But the code below does not try differentiate between : and ,
 270         *  and just blindly uses 3 tokens at a time to construct a
 271         *  bus/device/function tuple.
 272         *
 273         * There is no checking on strtol() return values, but this should
 274         *  be OK.  Worst case is it cannot convert and returns 0.  This
 275         *  could give us a different BDF than intended, but as long as the
 276         *  PCI device/vendor ID does not match it will not matter.
 277         */
 278        while (1) {
 279                if (remaining == NULL || remaining[0] == '\0')
 280                        break;
 281                token = strsep(&remaining, ",:");
 282                if (token == NULL)
 283                        break;
 284                bus = strtol(token, NULL, 10);
 285                token = strsep(&remaining, ",:");
 286                if (token == NULL)
 287                        break;
 288                device = strtol(token, NULL, 10);
 289                token = strsep(&remaining, ",:");
 290                if (token == NULL)
 291                        break;
 292                function = strtol(token, NULL, 10);
 293
 294                dev = pci_find_bsf(bus, device, function);
 295                if (dev == NULL)
 296                        continue;
 297
 298                if (num_detached < MAX_DETACHED_DEVICES) {
 299                        printf("nic_uio_load: detaching and storing dev=%p\n",
 300                               dev);
 301                        detached_devices[num_detached++] = dev;
 302                } else {
 303                        printf("nic_uio_load: reached MAX_DETACHED_DEVICES=%d. dev=%p won't be reattached\n",
 304                               MAX_DETACHED_DEVICES, dev);
 305                }
 306                device_detach(dev);
 307        }
 308}
 309
 310static void
 311nic_uio_unload(void)
 312{
 313        int i;
 314        printf("nic_uio_unload: entered...\n");
 315
 316        for (i = 0; i < num_detached; i++) {
 317                printf("nic_uio_unload: calling to device_probe_and_attach for dev=%p...\n",
 318                        detached_devices[i]);
 319                device_probe_and_attach(detached_devices[i]);
 320                printf("nic_uio_unload: done.\n");
 321        }
 322
 323        printf("nic_uio_unload: leaving...\n");
 324}
 325
 326static int
 327nic_uio_shutdown(void)
 328{
 329        return 0;
 330}
 331
 332static int
 333nic_uio_modevent(module_t mod, int type, void *arg)
 334{
 335
 336        switch (type) {
 337        case MOD_LOAD:
 338                nic_uio_load();
 339                break;
 340        case MOD_UNLOAD:
 341                nic_uio_unload();
 342                break;
 343        case MOD_SHUTDOWN:
 344                nic_uio_shutdown();
 345                break;
 346        default:
 347                break;
 348        }
 349
 350        return 0;
 351}
 352