netbsd_pci.c revision 22df1cdf
1/* 2 * Copyright (c) 2008 Juan Romero Pardines 3 * Copyright (c) 2008 Mark Kettenis 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18#include <sys/param.h> 19#include <sys/ioctl.h> 20#include <sys/mman.h> 21#include <sys/types.h> 22 23#ifdef HAVE_MTRR 24#include <machine/sysarch.h> 25#include <machine/mtrr.h> 26#define netbsd_set_mtrr(mr, num) _X86_SYSARCH_L(set_mtrr)(mr, num) 27#endif 28 29#include <dev/pci/pcidevs.h> 30#include <dev/pci/pciio.h> 31#include <dev/pci/pcireg.h> 32 33#include <errno.h> 34#include <fcntl.h> 35#include <stdio.h> 36#include <stdlib.h> 37#include <string.h> 38#include <unistd.h> 39 40 41#include <pci.h> 42 43#include "pciaccess.h" 44#include "pciaccess_private.h" 45 46static int pcifd; 47 48static int 49pci_read(int bus, int dev, int func, uint32_t reg, uint32_t *val) 50{ 51 uint32_t rval; 52 53 if (pcibus_conf_read(pcifd, bus, dev, func, reg, &rval) == -1) 54 return (-1); 55 56 *val = rval; 57 58 return 0; 59} 60 61static int 62pci_write(int bus, int dev, int func, uint32_t reg, uint32_t val) 63{ 64 return pcibus_conf_write(pcifd, bus, dev, func, reg, val); 65} 66 67static int 68pci_nfuncs(int bus, int dev) 69{ 70 uint32_t hdr; 71 72 if (pci_read(bus, dev, 0, PCI_BHLC_REG, &hdr) != 0) 73 return -1; 74 75 return (PCI_HDRTYPE_MULTIFN(hdr) ? 8 : 1); 76} 77 78static int 79pci_device_netbsd_map_range(struct pci_device *dev, 80 struct pci_device_mapping *map) 81{ 82#ifdef HAVE_MTRR 83 struct mtrr m; 84 int n = 1; 85#endif 86 int prot, fd, ret = 0; 87 88 prot = PROT_READ; 89 90 if (map->flags & PCI_DEV_MAP_FLAG_WRITABLE) 91 prot |= PROT_WRITE; 92 93 fd = open("/dev/mem", O_RDWR); 94 if (fd == -1) 95 return errno; 96 map->memory = mmap(NULL, map->size, prot, MAP_SHARED, fd, map->base); 97 if (map->memory == MAP_FAILED) 98 return errno; 99 100#ifdef HAVE_MTRR 101 memset(&m, 0, sizeof(m)); 102 103 /* No need to set an MTRR if it's the default mode. */ 104 if ((map->flags & PCI_DEV_MAP_FLAG_CACHABLE) || 105 (map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE)) { 106 m.base = base; 107 m.flags = MTRR_VALID | MTRR_PRIVATE; 108 m.len = size; 109 m.owner = getpid(); 110 if (map->flags & PCI_DEV_MAP_FLAG_CACHEABLE) 111 m.type = MTRR_TYPE_WB; 112 if (map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE) 113 m.type = MTRR_TYPE_WC; 114 115 if ((netbsd_set_mtrr(&m, &n)) == -1) 116 ret = errno; 117 } 118#endif 119 120 close(fd); 121 122 return ret; 123} 124 125static int 126pci_device_netbsd_unmap_range(struct pci_device *dev, 127 struct pci_device_mapping *map) 128{ 129#ifdef HAVE_MTRR 130 struct mtrr m; 131 int n = 1; 132 133 memset(&m, 0, sizeof(m)); 134 135 if ((map->flags & PCI_DEV_MAP_FLAG_CACHABLE) || 136 (map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE)) { 137 m.base = map->base; 138 m.flags = 0; 139 m.len = size; 140 m.type = MTRR_TYPE_UC; 141 (void)netbsd_set_mtrr(&m, &n); 142 } 143#endif 144 145 return pci_device_generic_unmap_range(dev, map); 146} 147 148static int 149pci_device_netbsd_read(struct pci_device *dev, void *data, 150 pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_read) 151{ 152 u_int reg, rval; 153 154 *bytes_read = 0; 155 while (size > 0) { 156 int toread = MIN(size, 4 - (offset & 0x3)); 157 158 reg = (offset & ~0x3); 159 160 if ((pcibus_conf_read(pcifd, dev->bus, dev->dev, dev->func, 161 reg, &rval)) == -1) 162 return errno; 163 164 rval = htole32(rval); 165 rval >>= ((offset & 0x3) * 8); 166 167 memcpy(data, &rval, toread); 168 169 offset += toread; 170 data = (char *)data + toread; 171 size -= toread; 172 *bytes_read += toread; 173 } 174 175 return 0; 176} 177 178static int 179pci_device_netbsd_write(struct pci_device *dev, const void *data, 180 pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_written) 181{ 182 u_int reg, val; 183 184 if ((offset % 4) != 0 || (size % 4) != 0) 185 return EINVAL; 186 187 *bytes_written = 0; 188 while (size > 0) { 189 reg = offset; 190 memcpy(&val, data, 4); 191 192 if ((pcibus_conf_write(pcifd, dev->bus, dev->dev, dev->func, 193 reg, val)) == -1) 194 return errno; 195 196 offset += 4; 197 data = (char *)data + 4; 198 size -= 4; 199 *bytes_written += 4; 200 } 201 202 return 0; 203} 204 205static void 206pci_system_netbsd_destroy(void) 207{ 208 close(pcifd); 209 free(pci_sys); 210 pci_sys = NULL; 211} 212 213static int 214pci_device_netbsd_probe(struct pci_device *device) 215{ 216 struct pci_device_private *priv = (struct pci_device_private *)device; 217 struct pci_mem_region *region; 218 uint64_t reg64, size64; 219 uint32_t bar, reg, size; 220 int bus, dev, func, err; 221 222 bus = device->bus; 223 dev = device->dev; 224 func = device->func; 225 226 err = pci_read(bus, dev, func, PCI_BHLC_REG, ®); 227 if (err) 228 return err; 229 230 priv->header_type = PCI_HDRTYPE_TYPE(reg); 231 if (priv->header_type != 0) 232 return 0; 233 234 region = device->regions; 235 for (bar = PCI_MAPREG_START; bar < PCI_MAPREG_END; 236 bar += sizeof(uint32_t), region++) { 237 err = pci_read(bus, dev, func, bar, ®); 238 if (err) 239 return err; 240 241 /* Probe the size of the region. */ 242 err = pci_write(bus, dev, func, bar, ~0); 243 if (err) 244 return err; 245 pci_read(bus, dev, func, bar, &size); 246 pci_write(bus, dev, func, bar, reg); 247 248 if (PCI_MAPREG_TYPE(reg) == PCI_MAPREG_TYPE_IO) { 249 region->is_IO = 1; 250 region->base_addr = PCI_MAPREG_IO_ADDR(reg); 251 region->size = PCI_MAPREG_IO_SIZE(size); 252 } else { 253 if (PCI_MAPREG_MEM_PREFETCHABLE(reg)) 254 region->is_prefetchable = 1; 255 switch(PCI_MAPREG_MEM_TYPE(reg)) { 256 case PCI_MAPREG_MEM_TYPE_32BIT: 257 case PCI_MAPREG_MEM_TYPE_32BIT_1M: 258 region->base_addr = PCI_MAPREG_MEM_ADDR(reg); 259 region->size = PCI_MAPREG_MEM_SIZE(size); 260 break; 261 case PCI_MAPREG_MEM_TYPE_64BIT: 262 region->is_64 = 1; 263 264 reg64 = reg; 265 size64 = size; 266 267 bar += sizeof(uint32_t); 268 269 err = pci_read(bus, dev, func, bar, ®); 270 if (err) 271 return err; 272 reg64 |= (uint64_t)reg << 32; 273 274 err = pci_write(bus, dev, func, bar, ~0); 275 if (err) 276 return err; 277 pci_read(bus, dev, func, bar, &size); 278 pci_write(bus, dev, func, bar, reg64 >> 32); 279 size64 |= (uint64_t)size << 32; 280 281 region->base_addr = PCI_MAPREG_MEM64_ADDR(reg64); 282 region->size = PCI_MAPREG_MEM64_SIZE(size64); 283 region++; 284 break; 285 } 286 } 287 } 288 289 return 0; 290} 291 292static const struct pci_system_methods netbsd_pci_methods = { 293 pci_system_netbsd_destroy, 294 NULL, 295 NULL, 296 pci_device_netbsd_probe, 297 pci_device_netbsd_map_range, 298 pci_device_netbsd_unmap_range, 299 pci_device_netbsd_read, 300 pci_device_netbsd_write, 301 pci_fill_capabilities_generic 302}; 303 304int 305pci_system_netbsd_create(void) 306{ 307 struct pci_device_private *device; 308 int bus, dev, func, ndevs, nfuncs; 309 uint32_t reg; 310 311 pcifd = open("/dev/pci0", O_RDWR); 312 if (pcifd == -1) 313 return ENXIO; 314 315 pci_sys = calloc(1, sizeof(struct pci_system)); 316 if (pci_sys == NULL) { 317 close(pcifd); 318 return ENOMEM; 319 } 320 321 pci_sys->methods = &netbsd_pci_methods; 322 323 ndevs = 0; 324 for (bus = 0; bus < 256; bus++) { 325 for (dev = 0; dev < 32; dev++) { 326 nfuncs = pci_nfuncs(bus, dev); 327 for (func = 0; func < nfuncs; func++) { 328 if (pci_read(bus, dev, func, PCI_ID_REG, 329 ®) != 0) 330 continue; 331 if (PCI_VENDOR(reg) == PCI_VENDOR_INVALID || 332 PCI_VENDOR(reg) == 0) 333 continue; 334 335 ndevs++; 336 } 337 } 338 } 339 340 pci_sys->num_devices = ndevs; 341 pci_sys->devices = calloc(ndevs, sizeof(struct pci_device_private)); 342 if (pci_sys->devices == NULL) { 343 free(pci_sys); 344 close(pcifd); 345 return ENOMEM; 346 } 347 348 device = pci_sys->devices; 349 for (bus = 0; bus < 256; bus++) { 350 for (dev = 0; dev < 32; dev++) { 351 nfuncs = pci_nfuncs(bus, dev); 352 for (func = 0; func < nfuncs; func++) { 353 if (pci_read(bus, dev, func, PCI_ID_REG, 354 ®) != 0) 355 continue; 356 if (PCI_VENDOR(reg) == PCI_VENDOR_INVALID || 357 PCI_VENDOR(reg) == 0) 358 continue; 359 360 device->base.domain = 0; 361 device->base.bus = bus; 362 device->base.dev = dev; 363 device->base.func = func; 364 device->base.vendor_id = PCI_VENDOR(reg); 365 device->base.device_id = PCI_PRODUCT(reg); 366 367 if (pci_read(bus, dev, func, PCI_CLASS_REG, 368 ®) != 0) 369 continue; 370 371 device->base.device_class = 372 PCI_INTERFACE(reg) | PCI_CLASS(reg) << 16 | 373 PCI_SUBCLASS(reg) << 8; 374 device->base.revision = PCI_REVISION(reg); 375 376 if (pci_read(bus, dev, func, PCI_SUBSYS_ID_REG, 377 ®) != 0) 378 continue; 379 380 device->base.subvendor_id = PCI_VENDOR(reg); 381 device->base.subdevice_id = PCI_PRODUCT(reg); 382 383 device++; 384 } 385 } 386 } 387 388 return 0; 389} 390