From 0a9133703e4db04fe52bf7fa774392b82d51ac04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joan=20Lled=C3=B3?= Date: Sat, 30 Nov 2019 13:13:25 +0100 Subject: New access method: Hurd via RPCs A new module for the Hurd that accesses PCI bus using available RPCs. --- lib/Makefile | 5 + lib/configure | 3 +- lib/hurd.c | 366 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/init.c | 6 + lib/internal.h | 2 +- lib/pci.h | 3 +- 6 files changed, 382 insertions(+), 3 deletions(-) create mode 100644 lib/hurd.c diff --git a/lib/Makefile b/lib/Makefile index 12bbe34..d89cd6c 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -50,6 +50,10 @@ ifdef PCI_HAVE_PM_SYLIXOS_DEVICE OBJS += sylixos-device endif +ifdef PCI_HAVE_PM_HURD_CONF +OBJS += hurd +endif + all: $(PCILIB) $(PCILIBPC) ifeq ($(SHARED),no) @@ -95,3 +99,4 @@ names-parse.o: names-parse.c $(INCL) names.h names-hwdb.o: names-hwdb.c $(INCL) names.h filter.o: filter.c $(INCL) nbsd-libpci.o: nbsd-libpci.c $(INCL) +hurd.o: hurd.c $(INCL) diff --git a/lib/configure b/lib/configure index 4c328a9..08b9462 100755 --- a/lib/configure +++ b/lib/configure @@ -122,7 +122,8 @@ case $sys in LIBRESOLV= ;; gnu) - echo_n " i386-ports" + echo_n " hurd i386-ports" + echo >>$c '#define PCI_HAVE_PM_HURD_CONF' echo >>$c '#define PCI_HAVE_PM_INTEL_CONF' ;; djgpp) diff --git a/lib/hurd.c b/lib/hurd.c new file mode 100644 index 0000000..8cc948b --- /dev/null +++ b/lib/hurd.c @@ -0,0 +1,366 @@ +/* + * The PCI Library -- Hurd access via RPCs + * + * Copyright (c) 2017 Joan Lledó + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#define _GNU_SOURCE + +#include "internal.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Server path */ +#define _SERVERS_BUS_PCI _SERVERS_BUS "/pci" + +/* File names */ +#define FILE_CONFIG_NAME "config" +#define FILE_ROM_NAME "rom" + +/* Level in the fs tree */ +typedef enum +{ + LEVEL_NONE, + LEVEL_DOMAIN, + LEVEL_BUS, + LEVEL_DEV, + LEVEL_FUNC +} tree_level; + +/* Check whether there's a pci server */ +static int +hurd_detect(struct pci_access *a) +{ + int err; + struct stat st; + + err = stat(_SERVERS_BUS_PCI, &st); + if (err) + { + a->error("Could not open file `%s'", _SERVERS_BUS_PCI); + return 0; + } + + /* The node must be a directory and a translator */ + return S_ISDIR(st.st_mode) && ((st.st_mode & S_ITRANS) == S_IROOT); +} + +/* Empty callbacks, we don't need any special init or cleanup */ +static void +hurd_init(struct pci_access *a UNUSED) +{ +} + +static void +hurd_cleanup(struct pci_access *a UNUSED) +{ +} + +/* Each device has its own server path. Allocate space for the port. */ +static void +hurd_init_dev(struct pci_dev *d) +{ + d->aux = pci_malloc(d->access, sizeof(mach_port_t)); +} + +/* Deallocate the port and free its space */ +static void +hurd_cleanup_dev(struct pci_dev *d) +{ + mach_port_t device_port; + + device_port = *((mach_port_t *) d->aux); + mach_port_deallocate(mach_task_self(), device_port); + + pci_mfree(d->aux); +} + +/* Walk through the FS tree to see what is allowed for us */ +static void +enum_devices(const char *parent, struct pci_access *a, int domain, int bus, + int dev, int func, tree_level lev) +{ + int ret; + DIR *dir; + struct dirent *entry; + char path[NAME_MAX]; + char server[NAME_MAX]; + uint32_t vd; + uint8_t ht; + mach_port_t device_port; + struct pci_dev *d; + + dir = opendir(parent); + if (!dir) + { + if (errno == EPERM || errno == EACCES) + /* The client lacks the permissions to access this function, skip */ + return; + else + a->error("Cannot open directory: %s (%s)", parent, strerror(errno)); + } + + while ((entry = readdir(dir)) != 0) + { + snprintf(path, NAME_MAX, "%s/%s", parent, entry->d_name); + if (entry->d_type == DT_DIR) + { + if (!strncmp(entry->d_name, ".", NAME_MAX) + || !strncmp(entry->d_name, "..", NAME_MAX)) + continue; + + errno = 0; + ret = strtol(entry->d_name, 0, 16); + if (errno) + { + if (closedir(dir) < 0) + a->warning("Cannot close directory: %s (%s)", parent, + strerror(errno)); + a->error("Wrong directory name: %s (number expected) probably \ + not connected to an arbiter", entry->d_name); + } + + /* + * We found a valid directory. + * Update the address and switch to the next level. + */ + switch (lev) + { + case LEVEL_DOMAIN: + domain = ret; + break; + case LEVEL_BUS: + bus = ret; + break; + case LEVEL_DEV: + dev = ret; + break; + case LEVEL_FUNC: + func = ret; + break; + default: + if (closedir(dir) < 0) + a->warning("Cannot close directory: %s (%s)", parent, + strerror(errno)); + a->error("Wrong directory tree, probably not connected \ + to an arbiter"); + } + + enum_devices(path, a, domain, bus, dev, func, lev + 1); + } + else + { + if (strncmp(entry->d_name, FILE_CONFIG_NAME, NAME_MAX)) + /* We are looking for the config file */ + continue; + + /* We found an available virtual device, add it to our list */ + snprintf(server, NAME_MAX, "%s/%04x/%02x/%02x/%01u/%s", + _SERVERS_BUS_PCI, domain, bus, dev, func, entry->d_name); + device_port = file_name_lookup(server, 0, 0); + if (device_port == MACH_PORT_NULL) + { + if (closedir(dir) < 0) + a->warning("Cannot close directory: %s (%s)", parent, + strerror(errno)); + a->error("Cannot open %s", server); + } + + d = pci_alloc_dev(a); + *((mach_port_t *) d->aux) = device_port; + d->bus = bus; + d->dev = dev; + d->func = func; + pci_link_dev(a, d); + + vd = pci_read_long(d, PCI_VENDOR_ID); + ht = pci_read_byte(d, PCI_HEADER_TYPE); + + d->vendor_id = vd & 0xffff; + d->device_id = vd >> 16U; + d->known_fields = PCI_FILL_IDENT; + d->hdrtype = ht; + } + } + + if (closedir(dir) < 0) + a->error("Cannot close directory: %s (%s)", parent, strerror(errno)); +} + +/* Enumerate devices */ +static void +hurd_scan(struct pci_access *a) +{ + enum_devices(_SERVERS_BUS_PCI, a, -1, -1, -1, -1, LEVEL_DOMAIN); +} + +/* + * Read `len' bytes to `buf'. + * + * Returns error when the number of read bytes does not match `len'. + */ +static int +hurd_read(struct pci_dev *d, int pos, byte * buf, int len) +{ + int err; + size_t nread; + char *data; + mach_port_t device_port; + + nread = len; + device_port = *((mach_port_t *) d->aux); + if (len > 4) + err = !pci_generic_block_read(d, pos, buf, nread); + else + { + data = (char *) buf; + err = pci_conf_read(device_port, pos, &data, &nread, len); + + if (data != (char *) buf) + { + if (nread > (size_t) len) /* Sanity check for bogus server. */ + { + vm_deallocate(mach_task_self(), (vm_address_t) data, nread); + return 0; + } + + memcpy(buf, data, nread); + vm_deallocate(mach_task_self(), (vm_address_t) data, nread); + } + } + if (err) + return 0; + + return nread == (size_t) len; +} + +/* + * Write `len' bytes from `buf'. + * + * Returns error when the number of written bytes does not match `len'. + */ +static int +hurd_write(struct pci_dev *d, int pos, byte * buf, int len) +{ + int err; + size_t nwrote; + mach_port_t device_port; + + nwrote = len; + device_port = *((mach_port_t *) d->aux); + if (len > 4) + err = !pci_generic_block_write(d, pos, buf, len); + else + err = pci_conf_write(device_port, pos, (char *) buf, len, &nwrote); + if (err) + return 0; + + return nwrote == (size_t) len; +} + +/* Get requested info from the server */ +static int +hurd_fill_info(struct pci_dev *d, int flags) +{ + int err, i; + struct pci_bar regions[6]; + struct pci_xrom_bar rom; + size_t size; + char *buf; + mach_port_t device_port; + + device_port = *((mach_port_t *) d->aux); + + if (flags & PCI_FILL_BASES) + { + buf = (char *) ®ions; + size = sizeof(regions); + + err = pci_get_dev_regions(device_port, &buf, &size); + if (err) + return err; + + if ((char *) ®ions != buf) + { + /* Sanity check for bogus server. */ + if (size > sizeof(regions)) + { + vm_deallocate(mach_task_self(), (vm_address_t) buf, size); + return EGRATUITOUS; + } + + memcpy(®ions, buf, size); + vm_deallocate(mach_task_self(), (vm_address_t) buf, size); + } + + for (i = 0; i < 6; i++) + { + if (regions[i].size == 0) + continue; + + d->base_addr[i] = regions[i].base_addr; + d->base_addr[i] |= regions[i].is_IO; + d->base_addr[i] |= regions[i].is_64 << 2; + d->base_addr[i] |= regions[i].is_prefetchable << 3; + + if (flags & PCI_FILL_SIZES) + d->size[i] = regions[i].size; + } + } + + if (flags & PCI_FILL_ROM_BASE) + { + /* Get rom info */ + buf = (char *) &rom; + size = sizeof(rom); + err = pci_get_dev_rom(device_port, &buf, &size); + if (err) + return err; + + if ((char *) &rom != buf) + { + /* Sanity check for bogus server. */ + if (size > sizeof(rom)) + { + vm_deallocate(mach_task_self(), (vm_address_t) buf, size); + return EGRATUITOUS; + } + + memcpy(&rom, buf, size); + vm_deallocate(mach_task_self(), (vm_address_t) buf, size); + } + + d->rom_base_addr = rom.base_addr; + d->rom_size = rom.size; + } + + return pci_generic_fill_info(d, flags); +} + +struct pci_methods pm_hurd = { + "hurd", + "Hurd access using RPCs", + NULL, /* config */ + hurd_detect, + hurd_init, + hurd_cleanup, + hurd_scan, + hurd_fill_info, + hurd_read, + hurd_write, + NULL, /* read_vpd */ + hurd_init_dev, + hurd_cleanup_dev +}; diff --git a/lib/init.c b/lib/init.c index adae842..e6295fc 100644 --- a/lib/init.c +++ b/lib/init.c @@ -67,6 +67,11 @@ static struct pci_methods *pci_methods[PCI_ACCESS_MAX] = { #else NULL, #endif +#ifdef PCI_HAVE_PM_HURD_CONF + &pm_hurd, +#else + NULL, +#endif }; // If PCI_ACCESS_AUTO is selected, we probe the access methods in this order @@ -80,6 +85,7 @@ static int probe_sequence[] = { PCI_ACCESS_OBSD_DEVICE, PCI_ACCESS_DARWIN, PCI_ACCESS_SYLIXOS_DEVICE, + PCI_ACCESS_HURD, // Low-level methods poking the hardware directly PCI_ACCESS_I386_TYPE1, PCI_ACCESS_I386_TYPE2, diff --git a/lib/internal.h b/lib/internal.h index aaa121a..714a1db 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -94,4 +94,4 @@ void pci_free_caps(struct pci_dev *); extern struct pci_methods pm_intel_conf1, pm_intel_conf2, pm_linux_proc, pm_fbsd_device, pm_aix_device, pm_nbsd_libpci, pm_obsd_device, - pm_dump, pm_linux_sysfs, pm_darwin, pm_sylixos_device; + pm_dump, pm_linux_sysfs, pm_darwin, pm_sylixos_device, pm_hurd; diff --git a/lib/pci.h b/lib/pci.h index 813c333..0adc5c6 100644 --- a/lib/pci.h +++ b/lib/pci.h @@ -41,7 +41,8 @@ enum pci_access_type { PCI_ACCESS_OBSD_DEVICE, /* OpenBSD /dev/pci */ PCI_ACCESS_DUMP, /* Dump file */ PCI_ACCESS_DARWIN, /* Darwin */ - PCI_ACCESS_SYLIXOS_DEVICE, /* SylixOS pci */ + PCI_ACCESS_SYLIXOS_DEVICE, /* SylixOS pci */ + PCI_ACCESS_HURD, /* GNU/Hurd */ PCI_ACCESS_MAX }; -- cgit v1.2.1