12029f493Smrg/* 22029f493Smrg * Copyright (c) 2018, Damien Zammit 32029f493Smrg * Copyright (c) 2017, Joan Lledó 42029f493Smrg * Copyright (c) 2009, 2012 Samuel Thibault 52029f493Smrg * Heavily inspired from the freebsd, netbsd, and openbsd backends 62029f493Smrg * (C) Copyright Eric Anholt 2006 72029f493Smrg * (C) Copyright IBM Corporation 2006 82029f493Smrg * Copyright (c) 2008 Juan Romero Pardines 92029f493Smrg * Copyright (c) 2008 Mark Kettenis 102029f493Smrg * 112029f493Smrg * Permission to use, copy, modify, and distribute this software for any 122029f493Smrg * purpose with or without fee is hereby granted, provided that the above 132029f493Smrg * copyright notice and this permission notice appear in all copies. 142029f493Smrg * 152029f493Smrg * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 162029f493Smrg * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 172029f493Smrg * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 182029f493Smrg * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 192029f493Smrg * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 202029f493Smrg * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 212029f493Smrg * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 222029f493Smrg */ 232029f493Smrg 242029f493Smrg#define _GNU_SOURCE 252029f493Smrg#include <unistd.h> 262029f493Smrg#include <stdio.h> 272029f493Smrg#include <stdlib.h> 282029f493Smrg#include <errno.h> 292029f493Smrg#include <sys/types.h> 302029f493Smrg#include <sys/stat.h> 312029f493Smrg#include <fcntl.h> 322029f493Smrg#include <dirent.h> 332029f493Smrg#include <sys/mman.h> 342029f493Smrg#include <string.h> 352029f493Smrg#include <strings.h> 3648becaf0Smrg#include <mach.h> 372029f493Smrg#include <hurd.h> 382029f493Smrg#include <hurd/pci.h> 392029f493Smrg#include <hurd/paths.h> 4048becaf0Smrg#include <hurd/fs.h> 4148becaf0Smrg#include <device/device.h> 422029f493Smrg 432029f493Smrg#include "x86_pci.h" 442029f493Smrg#include "pciaccess.h" 452029f493Smrg#include "pciaccess_private.h" 462029f493Smrg 472029f493Smrg/* 482029f493Smrg * Hurd PCI access using RPCs. 492029f493Smrg * 502029f493Smrg * Some functions are shared with the x86 module to avoid repeating code. 512029f493Smrg */ 522029f493Smrg 532029f493Smrg/* Server path */ 542029f493Smrg#define _SERVERS_BUS_PCI _SERVERS_BUS "/pci" 552029f493Smrg 562029f493Smrg/* File names */ 572029f493Smrg#define FILE_CONFIG_NAME "config" 5848becaf0Smrg#define FILE_REGION_NAME "region" 592029f493Smrg#define FILE_ROM_NAME "rom" 602029f493Smrg 6148becaf0Smrg#define LEGACY_REGION -1 6248becaf0Smrg 632029f493Smrg/* Level in the fs tree */ 642029f493Smrgtypedef enum { 652029f493Smrg LEVEL_NONE, 662029f493Smrg LEVEL_DOMAIN, 672029f493Smrg LEVEL_BUS, 682029f493Smrg LEVEL_DEV, 692029f493Smrg LEVEL_FUNC 702029f493Smrg} tree_level; 712029f493Smrg 722029f493Smrgstruct pci_system_hurd { 732029f493Smrg struct pci_system system; 7448becaf0Smrg mach_port_t root; 752029f493Smrg}; 762029f493Smrg 772029f493Smrgstatic int 782029f493Smrgpci_device_hurd_probe(struct pci_device *dev) 792029f493Smrg{ 802029f493Smrg uint8_t irq; 812029f493Smrg int err, i; 822029f493Smrg struct pci_bar regions[6]; 832029f493Smrg struct pci_xrom_bar rom; 842029f493Smrg struct pci_device_private *d; 855ad99bdfSmrg mach_msg_type_number_t size; 862029f493Smrg char *buf; 872029f493Smrg 882029f493Smrg /* Many of the fields were filled in during initial device enumeration. 892029f493Smrg * At this point, we need to fill in regions, rom_size, and irq. 902029f493Smrg */ 912029f493Smrg 922029f493Smrg err = pci_device_cfg_read_u8(dev, &irq, PCI_IRQ); 932029f493Smrg if (err) 942029f493Smrg return err; 952029f493Smrg dev->irq = irq; 962029f493Smrg 972029f493Smrg /* Get regions */ 982029f493Smrg buf = (char *)®ions; 992029f493Smrg size = sizeof(regions); 1002029f493Smrg d = (struct pci_device_private *)dev; 1012029f493Smrg err = pci_get_dev_regions(d->device_port, &buf, &size); 1022029f493Smrg if(err) 1032029f493Smrg return err; 1042029f493Smrg 1052029f493Smrg if((char*)®ions != buf) 1062029f493Smrg { 1072029f493Smrg /* Sanity check for bogus server. */ 1082029f493Smrg if(size > sizeof(regions)) 1092029f493Smrg { 1102029f493Smrg vm_deallocate(mach_task_self(), (vm_address_t) buf, size); 1112029f493Smrg return EGRATUITOUS; 1122029f493Smrg } 1132029f493Smrg 1142029f493Smrg memcpy(®ions, buf, size); 1152029f493Smrg vm_deallocate(mach_task_self(), (vm_address_t) buf, size); 1162029f493Smrg } 1172029f493Smrg 1182029f493Smrg for(i=0; i<6; i++) 1192029f493Smrg { 1202029f493Smrg if(regions[i].size == 0) 1212029f493Smrg continue; 1222029f493Smrg 1232029f493Smrg dev->regions[i].base_addr = regions[i].base_addr; 1242029f493Smrg dev->regions[i].size = regions[i].size; 1252029f493Smrg dev->regions[i].is_IO = regions[i].is_IO; 1262029f493Smrg dev->regions[i].is_prefetchable = regions[i].is_prefetchable; 1272029f493Smrg dev->regions[i].is_64 = regions[i].is_64; 1282029f493Smrg } 1292029f493Smrg 1302029f493Smrg /* Get rom info */ 1312029f493Smrg buf = (char *)&rom; 1322029f493Smrg size = sizeof(rom); 1332029f493Smrg err = pci_get_dev_rom(d->device_port, &buf, &size); 1342029f493Smrg if(err) 1352029f493Smrg return err; 1362029f493Smrg 1372029f493Smrg if((char*)&rom != buf) 1382029f493Smrg { 1392029f493Smrg /* Sanity check for bogus server. */ 1402029f493Smrg if(size > sizeof(rom)) 1412029f493Smrg { 1422029f493Smrg vm_deallocate(mach_task_self(), (vm_address_t) buf, size); 1432029f493Smrg return EGRATUITOUS; 1442029f493Smrg } 1452029f493Smrg 1462029f493Smrg memcpy(&rom, buf, size); 1472029f493Smrg vm_deallocate(mach_task_self(), (vm_address_t) buf, size); 1482029f493Smrg } 1492029f493Smrg 1502029f493Smrg d->rom_base = rom.base_addr; 1512029f493Smrg dev->rom_size = rom.size; 1522029f493Smrg 1532029f493Smrg return 0; 1542029f493Smrg} 1552029f493Smrg 15648becaf0Smrgstatic void 15748becaf0Smrgpci_system_hurd_destroy(void) 15848becaf0Smrg{ 15948becaf0Smrg struct pci_system_hurd *pci_sys_hurd = (struct pci_system_hurd *)pci_sys; 16048becaf0Smrg 16148becaf0Smrg x86_disable_io(); 16248becaf0Smrg mach_port_deallocate(mach_task_self(), pci_sys_hurd->root); 16348becaf0Smrg} 16448becaf0Smrg 16548becaf0Smrgstatic int 16648becaf0Smrgpci_device_hurd_map_range(struct pci_device *dev, 16748becaf0Smrg struct pci_device_mapping *map) 16848becaf0Smrg{ 16948becaf0Smrg#if 0 17048becaf0Smrg struct pci_device_private *d = (struct pci_device_private *)dev; 17148becaf0Smrg#endif 17248becaf0Smrg struct pci_system_hurd *pci_sys_hurd = (struct pci_system_hurd *)pci_sys; 17348becaf0Smrg int err = 0; 17448becaf0Smrg file_t file = MACH_PORT_NULL; 17548becaf0Smrg memory_object_t robj, wobj, pager; 17648becaf0Smrg vm_offset_t offset; 17748becaf0Smrg vm_prot_t prot = VM_PROT_READ; 17848becaf0Smrg const struct pci_mem_region *region; 17948becaf0Smrg const struct pci_mem_region rom_region = { 18048becaf0Smrg#if 0 18148becaf0Smrg /* FIXME: We should be doing this: */ 18248becaf0Smrg .base_addr = d->rom_base, 18348becaf0Smrg /* But currently pci-arbiter cannot get rom_base from libpciaccess, so we 18448becaf0Smrg are here assuming the caller is mapping from the rom base */ 18548becaf0Smrg#else 18648becaf0Smrg .base_addr = map->base, 18748becaf0Smrg#endif 18848becaf0Smrg .size = dev->rom_size, 18948becaf0Smrg }; 19048becaf0Smrg int flags = O_RDONLY; 19148becaf0Smrg char server[NAME_MAX]; 19248becaf0Smrg 19348becaf0Smrg if (map->flags & PCI_DEV_MAP_FLAG_WRITABLE) { 19448becaf0Smrg prot |= VM_PROT_WRITE; 19548becaf0Smrg flags = O_RDWR; 19648becaf0Smrg } 19748becaf0Smrg 19848becaf0Smrg if (map->region != LEGACY_REGION) { 19948becaf0Smrg region = &dev->regions[map->region]; 20048becaf0Smrg snprintf(server, NAME_MAX, "%04x/%02x/%02x/%01u/%s%01u", 20148becaf0Smrg dev->domain, dev->bus, dev->dev, dev->func, 20248becaf0Smrg FILE_REGION_NAME, map->region); 20348becaf0Smrg } else { 20448becaf0Smrg region = &rom_region; 20548becaf0Smrg snprintf(server, NAME_MAX, "%04x/%02x/%02x/%01u/%s", 20648becaf0Smrg dev->domain, dev->bus, dev->dev, dev->func, 20748becaf0Smrg FILE_ROM_NAME); 20848becaf0Smrg if (map->base < region->base_addr || 20948becaf0Smrg map->base + map->size > region->base_addr + region->size) 21048becaf0Smrg return EINVAL; 21148becaf0Smrg } 21248becaf0Smrg 21348becaf0Smrg file = file_name_lookup_under (pci_sys_hurd->root, server, flags, 0); 21448becaf0Smrg if (! MACH_PORT_VALID (file)) { 21548becaf0Smrg return errno; 21648becaf0Smrg } 21748becaf0Smrg 21848becaf0Smrg err = io_map (file, &robj, &wobj); 21948becaf0Smrg mach_port_deallocate (mach_task_self(), file); 22048becaf0Smrg if (err) 22148becaf0Smrg return err; 22248becaf0Smrg 22348becaf0Smrg switch (prot & (VM_PROT_READ|VM_PROT_WRITE)) { 22448becaf0Smrg case VM_PROT_READ: 22548becaf0Smrg pager = robj; 22648becaf0Smrg if (wobj != MACH_PORT_NULL) 22748becaf0Smrg mach_port_deallocate (mach_task_self(), wobj); 22848becaf0Smrg break; 22948becaf0Smrg case VM_PROT_READ|VM_PROT_WRITE: 23048becaf0Smrg if (robj == wobj) { 23148becaf0Smrg if (robj == MACH_PORT_NULL) 23248becaf0Smrg return EPERM; 23348becaf0Smrg 23448becaf0Smrg pager = wobj; 23548becaf0Smrg /* Remove extra reference. */ 23648becaf0Smrg mach_port_deallocate (mach_task_self (), pager); 23748becaf0Smrg } 23848becaf0Smrg else { 23948becaf0Smrg if (robj != MACH_PORT_NULL) 24048becaf0Smrg mach_port_deallocate (mach_task_self (), robj); 24148becaf0Smrg if (wobj != MACH_PORT_NULL) 24248becaf0Smrg mach_port_deallocate (mach_task_self (), wobj); 24348becaf0Smrg 24448becaf0Smrg return EPERM; 24548becaf0Smrg } 24648becaf0Smrg break; 24748becaf0Smrg default: 24848becaf0Smrg return EINVAL; 24948becaf0Smrg } 25048becaf0Smrg 25148becaf0Smrg offset = map->base - region->base_addr; 25248becaf0Smrg err = vm_map (mach_task_self (), (vm_address_t *)&map->memory, map->size, 25348becaf0Smrg 0, 1, 25448becaf0Smrg pager, /* a memory object proxy containing only the region */ 25548becaf0Smrg offset, /* offset from region start */ 25648becaf0Smrg 0, prot, VM_PROT_ALL, VM_INHERIT_SHARE); 25748becaf0Smrg mach_port_deallocate (mach_task_self(), pager); 25848becaf0Smrg 25948becaf0Smrg return err; 26048becaf0Smrg} 26148becaf0Smrg 26248becaf0Smrgstatic int 26348becaf0Smrgpci_device_hurd_unmap_range(struct pci_device *dev, 26448becaf0Smrg struct pci_device_mapping *map) 26548becaf0Smrg{ 26648becaf0Smrg int err; 26748becaf0Smrg err = pci_device_generic_unmap_range(dev, map); 26848becaf0Smrg map->memory = NULL; 26948becaf0Smrg 27048becaf0Smrg return err; 27148becaf0Smrg} 27248becaf0Smrg 27348becaf0Smrgstatic int 27448becaf0Smrgpci_device_hurd_map_legacy(struct pci_device *dev, pciaddr_t base, 27548becaf0Smrg pciaddr_t size, unsigned map_flags, void **addr) 27648becaf0Smrg{ 27748becaf0Smrg struct pci_device_mapping map; 27848becaf0Smrg int err; 27948becaf0Smrg 28048becaf0Smrg if (base >= 0xC0000 && base < 0x100000) { 28148becaf0Smrg /* FIXME: We would rather know for sure from d->rom_base whether this is 28248becaf0Smrg the ROM or not but currently pci-arbiter cannot get rom_base from 28348becaf0Smrg libpciaccess, so we are here assuming the caller is mapping from the 28448becaf0Smrg rom base */ 28548becaf0Smrg map.base = base; 28648becaf0Smrg map.size = size; 28748becaf0Smrg map.flags = map_flags; 28848becaf0Smrg map.region = LEGACY_REGION; 28948becaf0Smrg err = pci_device_hurd_map_range(dev, &map); 29048becaf0Smrg *addr = map.memory; 29148becaf0Smrg 29248becaf0Smrg if (!err) 29348becaf0Smrg return 0; 29448becaf0Smrg } 29548becaf0Smrg 29648becaf0Smrg /* This is probably not the ROM, this is probably something like VRam or 29748becaf0Smrg the interrupt table, just map it by hand. */ 29848becaf0Smrg return pci_system_x86_map_dev_mem(addr, base, size, !!(map_flags & PCI_DEV_MAP_FLAG_WRITABLE)); 29948becaf0Smrg} 30048becaf0Smrg 30148becaf0Smrgstatic int 30248becaf0Smrgpci_device_hurd_unmap_legacy(struct pci_device *dev, void *addr, 30348becaf0Smrg pciaddr_t size) 30448becaf0Smrg{ 30548becaf0Smrg struct pci_device_mapping map; 30648becaf0Smrg 30748becaf0Smrg map.size = size; 30848becaf0Smrg map.flags = 0; 30948becaf0Smrg map.region = LEGACY_REGION; 31048becaf0Smrg map.memory = addr; 31148becaf0Smrg 31248becaf0Smrg return pci_device_hurd_unmap_range(dev, &map); 31348becaf0Smrg} 31448becaf0Smrg 3152029f493Smrg/* 31648becaf0Smrg * Read `nbytes' bytes from `reg' in device's configuration space 3172029f493Smrg * and store them in `buf'. 3182029f493Smrg * 3192029f493Smrg * It's assumed that `nbytes' bytes are allocated in `buf' 3202029f493Smrg */ 3212029f493Smrgstatic int 3222029f493Smrgpciclient_cfg_read(mach_port_t device_port, int reg, char *buf, 3232029f493Smrg size_t * nbytes) 3242029f493Smrg{ 3252029f493Smrg int err; 3265ad99bdfSmrg mach_msg_type_number_t nread; 3272029f493Smrg char *data; 3282029f493Smrg 3292029f493Smrg data = buf; 3302029f493Smrg nread = *nbytes; 33148becaf0Smrg err = __pci_conf_read(device_port, reg, &data, &nread, *nbytes); 3322029f493Smrg if (err) 3332029f493Smrg return err; 3342029f493Smrg 3352029f493Smrg if (data != buf) { 3362029f493Smrg if (nread > *nbytes) /* Sanity check for bogus server. */ { 3372029f493Smrg vm_deallocate(mach_task_self(), (vm_address_t) data, nread); 3382029f493Smrg return EGRATUITOUS; 3392029f493Smrg } 3402029f493Smrg 3412029f493Smrg memcpy(buf, data, nread); 3422029f493Smrg vm_deallocate(mach_task_self(), (vm_address_t)data, nread); 3432029f493Smrg } 3442029f493Smrg 3452029f493Smrg *nbytes = nread; 3462029f493Smrg 3472029f493Smrg return 0; 3482029f493Smrg} 3492029f493Smrg 3502029f493Smrg/* Write `nbytes' bytes from `buf' to `reg' in device's configuration space */ 3512029f493Smrgstatic int 3522029f493Smrgpciclient_cfg_write(mach_port_t device_port, int reg, char *buf, 3532029f493Smrg size_t * nbytes) 3542029f493Smrg{ 3552029f493Smrg int err; 3562029f493Smrg size_t nwrote; 3572029f493Smrg 35848becaf0Smrg err = __pci_conf_write(device_port, reg, buf, *nbytes, &nwrote); 3592029f493Smrg 3602029f493Smrg if (!err) 3612029f493Smrg *nbytes = nwrote; 3622029f493Smrg 3632029f493Smrg return err; 3642029f493Smrg} 3652029f493Smrg 3662029f493Smrg/* 3672029f493Smrg * Read up to `size' bytes from `dev' configuration space to `data' starting 3682029f493Smrg * at `offset'. Write the amount on read bytes in `bytes_read'. 3692029f493Smrg */ 3702029f493Smrgstatic int 3712029f493Smrgpci_device_hurd_read(struct pci_device *dev, void *data, 3722029f493Smrg pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_read) 3732029f493Smrg{ 3742029f493Smrg int err; 3752029f493Smrg struct pci_device_private *d; 3762029f493Smrg 3772029f493Smrg *bytes_read = 0; 3782029f493Smrg d = (struct pci_device_private *)dev; 3792029f493Smrg while (size > 0) { 3802029f493Smrg size_t toread = 1 << (ffs(0x4 + (offset & 0x03)) - 1); 3812029f493Smrg if (toread > size) 3822029f493Smrg toread = size; 3832029f493Smrg 3842029f493Smrg err = pciclient_cfg_read(d->device_port, offset, (char*)data, 3852029f493Smrg &toread); 3862029f493Smrg if (err) 3872029f493Smrg return err; 3882029f493Smrg 3892029f493Smrg offset += toread; 3902029f493Smrg data = (char*)data + toread; 3912029f493Smrg size -= toread; 3922029f493Smrg *bytes_read += toread; 3932029f493Smrg } 3942029f493Smrg return 0; 3952029f493Smrg} 3962029f493Smrg 3972029f493Smrg/* 3982029f493Smrg * Write up to `size' bytes from `data' to `dev' configuration space starting 3992029f493Smrg * at `offset'. Write the amount on written bytes in `bytes_written'. 4002029f493Smrg */ 4012029f493Smrgstatic int 4022029f493Smrgpci_device_hurd_write(struct pci_device *dev, const void *data, 4032029f493Smrg pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_written) 4042029f493Smrg{ 4052029f493Smrg int err; 4062029f493Smrg struct pci_device_private *d; 4072029f493Smrg 4082029f493Smrg *bytes_written = 0; 4092029f493Smrg d = (struct pci_device_private *)dev; 4102029f493Smrg while (size > 0) { 4112029f493Smrg size_t towrite = 4; 4122029f493Smrg if (towrite > size) 4132029f493Smrg towrite = size; 4142029f493Smrg if (towrite > 4 - (offset & 0x3)) 4152029f493Smrg towrite = 4 - (offset & 0x3); 4162029f493Smrg 4172029f493Smrg err = pciclient_cfg_write(d->device_port, offset, (char*)data, 4182029f493Smrg &towrite); 4192029f493Smrg if (err) 4202029f493Smrg return err; 4212029f493Smrg 4222029f493Smrg offset += towrite; 4232029f493Smrg data = (const char*)data + towrite; 4242029f493Smrg size -= towrite; 4252029f493Smrg *bytes_written += towrite; 4262029f493Smrg } 4272029f493Smrg return 0; 4282029f493Smrg} 4292029f493Smrg 4302029f493Smrg/* 4312029f493Smrg * Copy the device's firmware in `buffer' 4322029f493Smrg */ 4332029f493Smrgstatic int 4342029f493Smrgpci_device_hurd_read_rom(struct pci_device * dev, void * buffer) 4352029f493Smrg{ 4362029f493Smrg ssize_t rd; 4372029f493Smrg int romfd; 4382029f493Smrg char server[NAME_MAX]; 4392029f493Smrg 4402029f493Smrg snprintf(server, NAME_MAX, "%s/%04x/%02x/%02x/%01u/%s", _SERVERS_BUS_PCI, 4412029f493Smrg dev->domain, dev->bus, dev->dev, dev->func, FILE_ROM_NAME); 4422029f493Smrg 4432029f493Smrg romfd = open(server, O_RDONLY | O_CLOEXEC); 4442029f493Smrg if (romfd == -1) 4452029f493Smrg return errno; 4462029f493Smrg 4472029f493Smrg rd = read(romfd, buffer, dev->rom_size); 4482029f493Smrg if (rd != dev->rom_size) { 4492029f493Smrg close(romfd); 4502029f493Smrg return errno; 4512029f493Smrg } 4522029f493Smrg 4532029f493Smrg close(romfd); 4542029f493Smrg 4552029f493Smrg return 0; 4562029f493Smrg} 4572029f493Smrg 4582029f493Smrg/* 4592029f493Smrg * Each device has its own server where send RPC's to. 4602029f493Smrg * 4612029f493Smrg * Deallocate the port before destroying the device. 4622029f493Smrg */ 4632029f493Smrgstatic void 46448becaf0Smrgpci_device_hurd_destroy_device(struct pci_device *dev) 4652029f493Smrg{ 4662029f493Smrg struct pci_device_private *d = (struct pci_device_private*) dev; 4672029f493Smrg 4682029f493Smrg mach_port_deallocate (mach_task_self (), d->device_port); 4692029f493Smrg} 4702029f493Smrg 47148becaf0Smrgstatic struct dirent64 * 47248becaf0Smrgsimple_readdir(mach_port_t port, uint32_t *first_entry) 47348becaf0Smrg{ 47448becaf0Smrg char *data; 47548becaf0Smrg int nentries = 0; 4765ad99bdfSmrg mach_msg_type_number_t size; 47748becaf0Smrg 47848becaf0Smrg dir_readdir (port, &data, &size, *first_entry, 1, 0, &nentries); 47948becaf0Smrg 48048becaf0Smrg if (nentries == 0) { 48148becaf0Smrg return NULL; 48248becaf0Smrg } 48348becaf0Smrg 48448becaf0Smrg *first_entry = *first_entry + 1; 48548becaf0Smrg return (struct dirent64 *)data; 48648becaf0Smrg} 48748becaf0Smrg 4882029f493Smrg/* Walk through the FS tree to see what is allowed for us */ 4892029f493Smrgstatic int 49048becaf0Smrgenum_devices(mach_port_t pci_port, const char *parent, int domain, 49148becaf0Smrg int bus, int dev, int func, tree_level lev) 4922029f493Smrg{ 4932029f493Smrg int err, ret; 49448becaf0Smrg struct dirent64 *entry = NULL; 4952029f493Smrg char path[NAME_MAX]; 4962029f493Smrg char server[NAME_MAX]; 49748becaf0Smrg uint32_t reg, count = 0; 4982029f493Smrg size_t toread; 49948becaf0Smrg mach_port_t cwd_port, device_port; 50048becaf0Smrg struct pci_device_private *d, *devices; 5012029f493Smrg 50248becaf0Smrg if (lev > LEVEL_FUNC + 1) { 50348becaf0Smrg return 0; 50448becaf0Smrg } 50548becaf0Smrg cwd_port = file_name_lookup_under (pci_port, parent, 50648becaf0Smrg O_DIRECTORY | O_RDONLY | O_EXEC, 0); 50748becaf0Smrg if (cwd_port == MACH_PORT_NULL) { 50848becaf0Smrg return 0; 50948becaf0Smrg } 5102029f493Smrg 51148becaf0Smrg while ((entry = simple_readdir(cwd_port, &count)) != NULL) { 5122029f493Smrg snprintf(path, NAME_MAX, "%s/%s", parent, entry->d_name); 5132029f493Smrg if (entry->d_type == DT_DIR) { 5142029f493Smrg if (!strncmp(entry->d_name, ".", NAME_MAX) 5152029f493Smrg || !strncmp(entry->d_name, "..", NAME_MAX)) 5162029f493Smrg continue; 5172029f493Smrg 5182029f493Smrg errno = 0; 5192029f493Smrg ret = strtol(entry->d_name, 0, 16); 52048becaf0Smrg if (errno) { 5212029f493Smrg return errno; 52248becaf0Smrg } 5232029f493Smrg 5242029f493Smrg /* 5252029f493Smrg * We found a valid directory. 5262029f493Smrg * Update the address and switch to the next level. 5272029f493Smrg */ 5282029f493Smrg switch (lev) { 5292029f493Smrg case LEVEL_DOMAIN: 5302029f493Smrg domain = ret; 5312029f493Smrg break; 5322029f493Smrg case LEVEL_BUS: 5332029f493Smrg bus = ret; 5342029f493Smrg break; 5352029f493Smrg case LEVEL_DEV: 5362029f493Smrg dev = ret; 5372029f493Smrg break; 5382029f493Smrg case LEVEL_FUNC: 5392029f493Smrg func = ret; 5402029f493Smrg break; 5412029f493Smrg default: 54248becaf0Smrg return 0; 5432029f493Smrg } 5442029f493Smrg 54548becaf0Smrg err = enum_devices(pci_port, path, domain, bus, dev, func, lev+1); 54648becaf0Smrg if (err && err != EPERM && err != EACCES) { 54748becaf0Smrg return 0; 54848becaf0Smrg } 54948becaf0Smrg } else { 5502029f493Smrg if (strncmp(entry->d_name, FILE_CONFIG_NAME, NAME_MAX)) 5512029f493Smrg /* We are looking for the config file */ 5522029f493Smrg continue; 5532029f493Smrg 5542029f493Smrg /* We found an available virtual device, add it to our list */ 55548becaf0Smrg snprintf(server, NAME_MAX, "./%04x/%02x/%02x/%01u/%s", 55648becaf0Smrg domain, bus, dev, func, 5572029f493Smrg entry->d_name); 55848becaf0Smrg device_port = file_name_lookup_under(pci_port, server, O_RDONLY, 0); 55948becaf0Smrg if (device_port == MACH_PORT_NULL) { 56048becaf0Smrg return 0; 56148becaf0Smrg } 5622029f493Smrg 5632029f493Smrg toread = sizeof(reg); 5642029f493Smrg err = pciclient_cfg_read(device_port, PCI_VENDOR_ID, (char*)®, 5652029f493Smrg &toread); 56648becaf0Smrg if (err) { 56748becaf0Smrg mach_port_deallocate (mach_task_self (), device_port); 5682029f493Smrg return err; 56948becaf0Smrg } 57048becaf0Smrg if (toread != sizeof(reg)) { 57148becaf0Smrg mach_port_deallocate (mach_task_self (), device_port); 5722029f493Smrg return -1; 57348becaf0Smrg } 5742029f493Smrg 57548becaf0Smrg devices = realloc(pci_sys->devices, (pci_sys->num_devices + 1) 57648becaf0Smrg * sizeof(struct pci_device_private)); 57748becaf0Smrg if (!devices) { 57848becaf0Smrg mach_port_deallocate (mach_task_self (), device_port); 57948becaf0Smrg return ENOMEM; 58048becaf0Smrg } 58148becaf0Smrg 58248becaf0Smrg d = devices + pci_sys->num_devices; 58348becaf0Smrg memset(d, 0, sizeof(struct pci_device_private)); 58448becaf0Smrg 58548becaf0Smrg d->base.domain = domain; 58648becaf0Smrg d->base.bus = bus; 58748becaf0Smrg d->base.dev = dev; 58848becaf0Smrg d->base.func = func; 58948becaf0Smrg d->base.vendor_id = PCI_VENDOR(reg); 59048becaf0Smrg d->base.device_id = PCI_DEVICE(reg); 5912029f493Smrg 5922029f493Smrg toread = sizeof(reg); 5932029f493Smrg err = pciclient_cfg_read(device_port, PCI_CLASS, (char*)®, 5942029f493Smrg &toread); 59548becaf0Smrg if (err) { 59648becaf0Smrg mach_port_deallocate (mach_task_self (), device_port); 5972029f493Smrg return err; 59848becaf0Smrg } 59948becaf0Smrg if (toread != sizeof(reg)) { 60048becaf0Smrg mach_port_deallocate (mach_task_self (), device_port); 6012029f493Smrg return -1; 60248becaf0Smrg } 6032029f493Smrg 60448becaf0Smrg d->base.device_class = reg >> 8; 60548becaf0Smrg d->base.revision = reg & 0xFF; 6062029f493Smrg 6072029f493Smrg toread = sizeof(reg); 6082029f493Smrg err = pciclient_cfg_read(device_port, PCI_SUB_VENDOR_ID, 6092029f493Smrg (char*)®, &toread); 61048becaf0Smrg if (err) { 61148becaf0Smrg mach_port_deallocate (mach_task_self (), device_port); 6122029f493Smrg return err; 61348becaf0Smrg } 61448becaf0Smrg if (toread != sizeof(reg)) { 61548becaf0Smrg mach_port_deallocate (mach_task_self (), device_port); 6162029f493Smrg return -1; 61748becaf0Smrg } 6182029f493Smrg 61948becaf0Smrg d->base.subvendor_id = PCI_VENDOR(reg); 62048becaf0Smrg d->base.subdevice_id = PCI_DEVICE(reg); 6212029f493Smrg 62248becaf0Smrg d->device_port = device_port; 6232029f493Smrg 62448becaf0Smrg pci_sys->devices = devices; 62548becaf0Smrg pci_sys->num_devices++; 6262029f493Smrg } 6272029f493Smrg } 62848becaf0Smrg mach_port_deallocate (mach_task_self (), cwd_port); 6292029f493Smrg 6302029f493Smrg return 0; 6312029f493Smrg} 6322029f493Smrg 6332029f493Smrgstatic const struct pci_system_methods hurd_pci_methods = { 63448becaf0Smrg .destroy = pci_system_hurd_destroy, 63548becaf0Smrg .destroy_device = pci_device_hurd_destroy_device, 6362029f493Smrg .read_rom = pci_device_hurd_read_rom, 6372029f493Smrg .probe = pci_device_hurd_probe, 63848becaf0Smrg .map_range = pci_device_hurd_map_range, 63948becaf0Smrg .unmap_range = pci_device_hurd_unmap_range, 6402029f493Smrg .read = pci_device_hurd_read, 6412029f493Smrg .write = pci_device_hurd_write, 6422029f493Smrg .fill_capabilities = pci_fill_capabilities_generic, 6432029f493Smrg .open_legacy_io = pci_device_x86_open_legacy_io, 6442029f493Smrg .close_io = pci_device_x86_close_io, 6452029f493Smrg .read32 = pci_device_x86_read32, 6462029f493Smrg .read16 = pci_device_x86_read16, 6472029f493Smrg .read8 = pci_device_x86_read8, 6482029f493Smrg .write32 = pci_device_x86_write32, 6492029f493Smrg .write16 = pci_device_x86_write16, 6502029f493Smrg .write8 = pci_device_x86_write8, 65148becaf0Smrg .map_legacy = pci_device_hurd_map_legacy, 65248becaf0Smrg .unmap_legacy = pci_device_hurd_unmap_legacy, 6532029f493Smrg}; 6542029f493Smrg 65548becaf0Smrg/* Get the name of the server using libpciaccess if any */ 65648becaf0Smrgextern char *netfs_server_name; 65748becaf0Smrg#pragma weak netfs_server_name 65848becaf0Smrg 6592029f493Smrg_pci_hidden int 6602029f493Smrgpci_system_hurd_create(void) 6612029f493Smrg{ 6622029f493Smrg int err; 6632029f493Smrg struct pci_system_hurd *pci_sys_hurd; 66448becaf0Smrg mach_port_t device_master; 66548becaf0Smrg mach_port_t pci_port = MACH_PORT_NULL; 66648becaf0Smrg mach_port_t root = MACH_PORT_NULL; 66748becaf0Smrg 66848becaf0Smrg if (&netfs_server_name && netfs_server_name 66948becaf0Smrg && !strcmp(netfs_server_name, "pci-arbiter")) { 67048becaf0Smrg /* We are a PCI arbiter, try the x86 way */ 67148becaf0Smrg err = pci_system_x86_create(); 67248becaf0Smrg if (!err) 67348becaf0Smrg return 0; 67448becaf0Smrg } 6752029f493Smrg 67648becaf0Smrg /* 67748becaf0Smrg * From this point on, we are either a client or a nested arbiter. 67848becaf0Smrg * Both will connect to a master arbiter. 67948becaf0Smrg */ 6802029f493Smrg 6812029f493Smrg pci_sys_hurd = calloc(1, sizeof(struct pci_system_hurd)); 6822029f493Smrg if (pci_sys_hurd == NULL) { 6832029f493Smrg x86_disable_io(); 6842029f493Smrg return ENOMEM; 6852029f493Smrg } 6862029f493Smrg pci_sys = &pci_sys_hurd->system; 6872029f493Smrg 6882029f493Smrg pci_sys->methods = &hurd_pci_methods; 6892029f493Smrg 69048becaf0Smrg pci_sys->num_devices = 0; 69148becaf0Smrg 69248becaf0Smrg err = get_privileged_ports (NULL, &device_master); 69348becaf0Smrg 69448becaf0Smrg if(!err && device_master != MACH_PORT_NULL) { 69548becaf0Smrg err = device_open (device_master, D_READ, "pci", &pci_port); 69648becaf0Smrg mach_port_deallocate (mach_task_self (), device_master); 6972029f493Smrg } 6982029f493Smrg 69948becaf0Smrg if (!err && pci_port != MACH_PORT_NULL) { 70048becaf0Smrg root = file_name_lookup_under (pci_port, ".", O_DIRECTORY | O_RDONLY | O_EXEC, 0); 70148becaf0Smrg device_close (pci_port); 70248becaf0Smrg mach_port_deallocate (mach_task_self (), pci_port); 7032029f493Smrg } 7042029f493Smrg 70548becaf0Smrg if (root == MACH_PORT_NULL) { 70648becaf0Smrg root = file_name_lookup (_SERVERS_BUS_PCI, O_RDONLY, 0); 7072029f493Smrg } 7082029f493Smrg 70948becaf0Smrg if (root == MACH_PORT_NULL) { 71048becaf0Smrg pci_system_cleanup(); 71148becaf0Smrg return errno; 71248becaf0Smrg } 71348becaf0Smrg 71448becaf0Smrg pci_sys_hurd->root = root; 71548becaf0Smrg err = enum_devices (root, ".", -1, -1, -1, -1, LEVEL_DOMAIN); 71648becaf0Smrg if (err) { 71748becaf0Smrg pci_system_cleanup(); 71848becaf0Smrg return err; 71948becaf0Smrg } 7202029f493Smrg 7212029f493Smrg return 0; 7222029f493Smrg} 723