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