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 *)&regions;
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*)&regions != 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(&regions, 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*)&reg,
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*)&reg,
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*)&reg, &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