hurd_pci.c revision 2029f493
1/* 2 * Copyright (c) 2018, Damien Zammit 3 * Copyright (c) 2017, Joan Lledó 4 * Copyright (c) 2009, 2012 Samuel Thibault 5 * Heavily inspired from the freebsd, netbsd, and openbsd backends 6 * (C) Copyright Eric Anholt 2006 7 * (C) Copyright IBM Corporation 2006 8 * Copyright (c) 2008 Juan Romero Pardines 9 * Copyright (c) 2008 Mark Kettenis 10 * 11 * Permission to use, copy, modify, and distribute this software for any 12 * purpose with or without fee is hereby granted, provided that the above 13 * copyright notice and this permission notice appear in all copies. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 16 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 17 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 18 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 20 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 21 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 22 */ 23 24#define _GNU_SOURCE 25#include <unistd.h> 26#include <stdio.h> 27#include <stdlib.h> 28#include <errno.h> 29#include <sys/types.h> 30#include <sys/stat.h> 31#include <fcntl.h> 32#include <dirent.h> 33#include <sys/mman.h> 34#include <string.h> 35#include <strings.h> 36#include <hurd.h> 37#include <hurd/pci.h> 38#include <hurd/paths.h> 39 40#include "x86_pci.h" 41#include "pciaccess.h" 42#include "pciaccess_private.h" 43 44/* 45 * Hurd PCI access using RPCs. 46 * 47 * Some functions are shared with the x86 module to avoid repeating code. 48 */ 49 50/* Server path */ 51#define _SERVERS_BUS_PCI _SERVERS_BUS "/pci" 52 53/* File names */ 54#define FILE_CONFIG_NAME "config" 55#define FILE_ROM_NAME "rom" 56 57/* Level in the fs tree */ 58typedef enum { 59 LEVEL_NONE, 60 LEVEL_DOMAIN, 61 LEVEL_BUS, 62 LEVEL_DEV, 63 LEVEL_FUNC 64} tree_level; 65 66struct pci_system_hurd { 67 struct pci_system system; 68}; 69 70static int 71pci_device_hurd_probe(struct pci_device *dev) 72{ 73 uint8_t irq; 74 int err, i; 75 struct pci_bar regions[6]; 76 struct pci_xrom_bar rom; 77 struct pci_device_private *d; 78 size_t size; 79 char *buf; 80 81 /* Many of the fields were filled in during initial device enumeration. 82 * At this point, we need to fill in regions, rom_size, and irq. 83 */ 84 85 err = pci_device_cfg_read_u8(dev, &irq, PCI_IRQ); 86 if (err) 87 return err; 88 dev->irq = irq; 89 90 /* Get regions */ 91 buf = (char *)®ions; 92 size = sizeof(regions); 93 d = (struct pci_device_private *)dev; 94 err = pci_get_dev_regions(d->device_port, &buf, &size); 95 if(err) 96 return err; 97 98 if((char*)®ions != buf) 99 { 100 /* Sanity check for bogus server. */ 101 if(size > sizeof(regions)) 102 { 103 vm_deallocate(mach_task_self(), (vm_address_t) buf, size); 104 return EGRATUITOUS; 105 } 106 107 memcpy(®ions, buf, size); 108 vm_deallocate(mach_task_self(), (vm_address_t) buf, size); 109 } 110 111 for(i=0; i<6; i++) 112 { 113 if(regions[i].size == 0) 114 continue; 115 116 dev->regions[i].base_addr = regions[i].base_addr; 117 dev->regions[i].size = regions[i].size; 118 dev->regions[i].is_IO = regions[i].is_IO; 119 dev->regions[i].is_prefetchable = regions[i].is_prefetchable; 120 dev->regions[i].is_64 = regions[i].is_64; 121 } 122 123 /* Get rom info */ 124 buf = (char *)&rom; 125 size = sizeof(rom); 126 err = pci_get_dev_rom(d->device_port, &buf, &size); 127 if(err) 128 return err; 129 130 if((char*)&rom != buf) 131 { 132 /* Sanity check for bogus server. */ 133 if(size > sizeof(rom)) 134 { 135 vm_deallocate(mach_task_self(), (vm_address_t) buf, size); 136 return EGRATUITOUS; 137 } 138 139 memcpy(&rom, buf, size); 140 vm_deallocate(mach_task_self(), (vm_address_t) buf, size); 141 } 142 143 d->rom_base = rom.base_addr; 144 dev->rom_size = rom.size; 145 146 return 0; 147} 148 149/* 150 * Read `nbytes' bytes from `reg' in device's configuretion space 151 * and store them in `buf'. 152 * 153 * It's assumed that `nbytes' bytes are allocated in `buf' 154 */ 155static int 156pciclient_cfg_read(mach_port_t device_port, int reg, char *buf, 157 size_t * nbytes) 158{ 159 int err; 160 size_t nread; 161 char *data; 162 163 data = buf; 164 nread = *nbytes; 165 err = pci_conf_read(device_port, reg, &data, &nread, *nbytes); 166 if (err) 167 return err; 168 169 if (data != buf) { 170 if (nread > *nbytes) /* Sanity check for bogus server. */ { 171 vm_deallocate(mach_task_self(), (vm_address_t) data, nread); 172 return EGRATUITOUS; 173 } 174 175 memcpy(buf, data, nread); 176 vm_deallocate(mach_task_self(), (vm_address_t)data, nread); 177 } 178 179 *nbytes = nread; 180 181 return 0; 182} 183 184/* Write `nbytes' bytes from `buf' to `reg' in device's configuration space */ 185static int 186pciclient_cfg_write(mach_port_t device_port, int reg, char *buf, 187 size_t * nbytes) 188{ 189 int err; 190 size_t nwrote; 191 192 err = pci_conf_write(device_port, reg, buf, *nbytes, &nwrote); 193 194 if (!err) 195 *nbytes = nwrote; 196 197 return err; 198} 199 200/* 201 * Read up to `size' bytes from `dev' configuration space to `data' starting 202 * at `offset'. Write the amount on read bytes in `bytes_read'. 203 */ 204static int 205pci_device_hurd_read(struct pci_device *dev, void *data, 206 pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_read) 207{ 208 int err; 209 struct pci_device_private *d; 210 211 *bytes_read = 0; 212 d = (struct pci_device_private *)dev; 213 while (size > 0) { 214 size_t toread = 1 << (ffs(0x4 + (offset & 0x03)) - 1); 215 if (toread > size) 216 toread = size; 217 218 err = pciclient_cfg_read(d->device_port, offset, (char*)data, 219 &toread); 220 if (err) 221 return err; 222 223 offset += toread; 224 data = (char*)data + toread; 225 size -= toread; 226 *bytes_read += toread; 227 } 228 return 0; 229} 230 231/* 232 * Write up to `size' bytes from `data' to `dev' configuration space starting 233 * at `offset'. Write the amount on written bytes in `bytes_written'. 234 */ 235static int 236pci_device_hurd_write(struct pci_device *dev, const void *data, 237 pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_written) 238{ 239 int err; 240 struct pci_device_private *d; 241 242 *bytes_written = 0; 243 d = (struct pci_device_private *)dev; 244 while (size > 0) { 245 size_t towrite = 4; 246 if (towrite > size) 247 towrite = size; 248 if (towrite > 4 - (offset & 0x3)) 249 towrite = 4 - (offset & 0x3); 250 251 err = pciclient_cfg_write(d->device_port, offset, (char*)data, 252 &towrite); 253 if (err) 254 return err; 255 256 offset += towrite; 257 data = (const char*)data + towrite; 258 size -= towrite; 259 *bytes_written += towrite; 260 } 261 return 0; 262} 263 264/* 265 * Copy the device's firmware in `buffer' 266 */ 267static int 268pci_device_hurd_read_rom(struct pci_device * dev, void * buffer) 269{ 270 ssize_t rd; 271 int romfd; 272 char server[NAME_MAX]; 273 274 snprintf(server, NAME_MAX, "%s/%04x/%02x/%02x/%01u/%s", _SERVERS_BUS_PCI, 275 dev->domain, dev->bus, dev->dev, dev->func, FILE_ROM_NAME); 276 277 romfd = open(server, O_RDONLY | O_CLOEXEC); 278 if (romfd == -1) 279 return errno; 280 281 rd = read(romfd, buffer, dev->rom_size); 282 if (rd != dev->rom_size) { 283 close(romfd); 284 return errno; 285 } 286 287 close(romfd); 288 289 return 0; 290} 291 292/* 293 * Each device has its own server where send RPC's to. 294 * 295 * Deallocate the port before destroying the device. 296 */ 297static void 298pci_device_hurd_destroy(struct pci_device *dev) 299{ 300 struct pci_device_private *d = (struct pci_device_private*) dev; 301 302 mach_port_deallocate (mach_task_self (), d->device_port); 303} 304 305/* Walk through the FS tree to see what is allowed for us */ 306static int 307enum_devices(const char *parent, struct pci_device_private **device, 308 int domain, int bus, int dev, int func, tree_level lev) 309{ 310 int err, ret; 311 DIR *dir; 312 struct dirent *entry; 313 char path[NAME_MAX]; 314 char server[NAME_MAX]; 315 uint32_t reg; 316 size_t toread; 317 mach_port_t device_port; 318 319 dir = opendir(parent); 320 if (!dir) 321 return errno; 322 323 while ((entry = readdir(dir)) != 0) { 324 snprintf(path, NAME_MAX, "%s/%s", parent, entry->d_name); 325 if (entry->d_type == DT_DIR) { 326 if (!strncmp(entry->d_name, ".", NAME_MAX) 327 || !strncmp(entry->d_name, "..", NAME_MAX)) 328 continue; 329 330 errno = 0; 331 ret = strtol(entry->d_name, 0, 16); 332 if (errno) 333 return errno; 334 335 /* 336 * We found a valid directory. 337 * Update the address and switch to the next level. 338 */ 339 switch (lev) { 340 case LEVEL_DOMAIN: 341 domain = ret; 342 break; 343 case LEVEL_BUS: 344 bus = ret; 345 break; 346 case LEVEL_DEV: 347 dev = ret; 348 break; 349 case LEVEL_FUNC: 350 func = ret; 351 break; 352 default: 353 return -1; 354 } 355 356 err = enum_devices(path, device, domain, bus, dev, func, lev+1); 357 if (err == EPERM) 358 continue; 359 } 360 else { 361 if (strncmp(entry->d_name, FILE_CONFIG_NAME, NAME_MAX)) 362 /* We are looking for the config file */ 363 continue; 364 365 /* We found an available virtual device, add it to our list */ 366 snprintf(server, NAME_MAX, "%s/%04x/%02x/%02x/%01u/%s", 367 _SERVERS_BUS_PCI, domain, bus, dev, func, 368 entry->d_name); 369 device_port = file_name_lookup(server, 0, 0); 370 if (device_port == MACH_PORT_NULL) 371 return errno; 372 373 toread = sizeof(reg); 374 err = pciclient_cfg_read(device_port, PCI_VENDOR_ID, (char*)®, 375 &toread); 376 if (err) 377 return err; 378 if (toread != sizeof(reg)) 379 return -1; 380 381 (*device)->base.domain = domain; 382 (*device)->base.bus = bus; 383 (*device)->base.dev = dev; 384 (*device)->base.func = func; 385 (*device)->base.vendor_id = PCI_VENDOR(reg); 386 (*device)->base.device_id = PCI_DEVICE(reg); 387 388 toread = sizeof(reg); 389 err = pciclient_cfg_read(device_port, PCI_CLASS, (char*)®, 390 &toread); 391 if (err) 392 return err; 393 if (toread != sizeof(reg)) 394 return -1; 395 396 (*device)->base.device_class = reg >> 8; 397 (*device)->base.revision = reg & 0xFF; 398 399 toread = sizeof(reg); 400 err = pciclient_cfg_read(device_port, PCI_SUB_VENDOR_ID, 401 (char*)®, &toread); 402 if (err) 403 return err; 404 if (toread != sizeof(reg)) 405 return -1; 406 407 (*device)->base.subvendor_id = PCI_VENDOR(reg); 408 (*device)->base.subdevice_id = PCI_DEVICE(reg); 409 410 (*device)->device_port = device_port; 411 412 (*device)++; 413 } 414 } 415 416 return 0; 417} 418 419static const struct pci_system_methods hurd_pci_methods = { 420 .destroy = pci_system_x86_destroy, 421 .destroy_device = pci_device_hurd_destroy, 422 .read_rom = pci_device_hurd_read_rom, 423 .probe = pci_device_hurd_probe, 424 .map_range = pci_device_x86_map_range, 425 .unmap_range = pci_device_x86_unmap_range, 426 .read = pci_device_hurd_read, 427 .write = pci_device_hurd_write, 428 .fill_capabilities = pci_fill_capabilities_generic, 429 .open_legacy_io = pci_device_x86_open_legacy_io, 430 .close_io = pci_device_x86_close_io, 431 .read32 = pci_device_x86_read32, 432 .read16 = pci_device_x86_read16, 433 .read8 = pci_device_x86_read8, 434 .write32 = pci_device_x86_write32, 435 .write16 = pci_device_x86_write16, 436 .write8 = pci_device_x86_write8, 437 .map_legacy = pci_device_x86_map_legacy, 438 .unmap_legacy = pci_device_x86_unmap_legacy, 439}; 440 441_pci_hidden int 442pci_system_hurd_create(void) 443{ 444 struct pci_device_private *device; 445 int err; 446 struct pci_system_hurd *pci_sys_hurd; 447 size_t ndevs; 448 mach_port_t pci_server_port; 449 450 /* If we can open pci cfg io ports on hurd, 451 * we are the arbiter, therefore try x86 method first */ 452 err = pci_system_x86_create(); 453 if (!err) 454 return 0; 455 456 pci_sys_hurd = calloc(1, sizeof(struct pci_system_hurd)); 457 if (pci_sys_hurd == NULL) { 458 x86_disable_io(); 459 return ENOMEM; 460 } 461 pci_sys = &pci_sys_hurd->system; 462 463 pci_sys->methods = &hurd_pci_methods; 464 465 pci_server_port = file_name_lookup(_SERVERS_BUS_PCI, 0, 0); 466 if (!pci_server_port) { 467 /* Fall back to x86 access method */ 468 return pci_system_x86_create(); 469 } 470 471 /* The server gives us the number of available devices for us */ 472 err = pci_get_ndevs (pci_server_port, &ndevs); 473 if (err) { 474 mach_port_deallocate (mach_task_self (), pci_server_port); 475 /* Fall back to x86 access method */ 476 return pci_system_x86_create(); 477 } 478 mach_port_deallocate (mach_task_self (), pci_server_port); 479 480 pci_sys->num_devices = ndevs; 481 pci_sys->devices = calloc(ndevs, sizeof(struct pci_device_private)); 482 if (pci_sys->devices == NULL) { 483 x86_disable_io(); 484 free(pci_sys_hurd); 485 pci_sys = NULL; 486 return ENOMEM; 487 } 488 489 device = pci_sys->devices; 490 err = enum_devices(_SERVERS_BUS_PCI, &device, -1, -1, -1, -1, 491 LEVEL_DOMAIN); 492 if (err) 493 return pci_system_x86_create(); 494 495 return 0; 496} 497