From: Joan Lledó <jlle...@member.fsf.org> A new module for the Hurd that accesses PCI bus using available RPCs.
All references to the Hurd in the i386 access method have been removed. --- lib/Makefile | 7 +- lib/configure | 4 +- lib/hurd.c | 381 +++++++++++++++++++++++++++++++++++++++++++++ lib/i386-io-hurd.h | 35 ----- lib/i386-ports.c | 4 - lib/init.c | 6 + lib/internal.h | 2 +- lib/pci.h | 3 +- 8 files changed, 398 insertions(+), 44 deletions(-) create mode 100644 lib/hurd.c delete mode 100644 lib/i386-io-hurd.h diff --git a/lib/Makefile b/lib/Makefile index 12bbe34..f6a32e5 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) @@ -78,7 +82,7 @@ $(PCILIBPC): libpci.pc.in init.o: init.c $(INCL) access.o: access.c $(INCL) params.o: params.c $(INCL) -i386-ports.o: i386-ports.c $(INCL) i386-io-hurd.h i386-io-linux.h i386-io-sunos.h i386-io-windows.h i386-io-cygwin.h +i386-ports.o: i386-ports.c $(INCL) i386-io-linux.h i386-io-sunos.h i386-io-windows.h i386-io-cygwin.h proc.o: proc.c $(INCL) pread.h sysfs.o: sysfs.c $(INCL) pread.h generic.o: generic.c $(INCL) @@ -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..c696175 100755 --- a/lib/configure +++ b/lib/configure @@ -122,8 +122,8 @@ case $sys in LIBRESOLV= ;; gnu) - echo_n " i386-ports" - echo >>$c '#define PCI_HAVE_PM_INTEL_CONF' + echo_n " hurd" + echo >>$c '#define PCI_HAVE_PM_HURD_CONF' ;; djgpp) echo_n " i386-ports" diff --git a/lib/hurd.c b/lib/hurd.c new file mode 100644 index 0000000..74d314c --- /dev/null +++ b/lib/hurd.c @@ -0,0 +1,381 @@ +/* + * The PCI Library -- Hurd access via RPCs + * + * Copyright (c) 2017 Joan Lledó <jlle...@member.fsf.org> + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#define _GNU_SOURCE + +#include "internal.h" + +#include <stdlib.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <dirent.h> +#include <fcntl.h> +#include <string.h> +#include <assert.h> +#include <hurd.h> +#include <hurd/pci.h> +#include <hurd/paths.h> + +/* 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 = calloc (1, sizeof (mach_port_t)); + assert (d->aux); +} + +/* 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); + + free (d->aux); +} + +/* Walk through the FS tree to see what is allowed for us */ +static int +enum_devices (const char *parent, struct pci_access *a, int domain, int bus, + int dev, int func, tree_level lev) +{ + int err, 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) + return 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) + { + closedir (dir); + return errno; + } + + /* + * 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) + return errno; + return -1; + } + + err = enum_devices (path, a, domain, bus, dev, func, lev + 1); + if (err && err != EPERM && err != EACCES) + { + if (closedir (dir) < 0) + return errno; + return err; + } + } + 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) + { + closedir (dir); + return errno; + } + + 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) + return errno; + + return 0; +} + +/* Enumerate devices */ +static void +hurd_scan (struct pci_access *a) +{ + int err; + + err = enum_devices (_SERVERS_BUS_PCI, a, -1, -1, -1, -1, LEVEL_DOMAIN); + assert (err == 0); +} + +/* + * 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_IDENT) + { + d->vendor_id = pci_read_word (d, PCI_VENDOR_ID); + d->device_id = pci_read_word (d, PCI_DEVICE_ID); + } + + if (flags & PCI_FILL_CLASS) + d->device_class = pci_read_word (d, PCI_CLASS_DEVICE); + + if (flags & PCI_FILL_IRQ) + d->irq = pci_read_byte (d, PCI_INTERRUPT_LINE); + + 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; + } + + if (flags & (PCI_FILL_CAPS | PCI_FILL_EXT_CAPS)) + flags |= pci_scan_caps (d, flags); + + return 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/i386-io-hurd.h b/lib/i386-io-hurd.h deleted file mode 100644 index d6df909..0000000 --- a/lib/i386-io-hurd.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * The PCI Library -- Access to i386 I/O ports on GNU Hurd - * - * Copyright (c) 2003 Marco Gerards <metgera...@student.han.nl> - * Copyright (c) 2003 Martin Mares <m...@ucw.cz> - * Copyright (c) 2006 Samuel Thibault <samuel.thiba...@ens-lyon.org> and - * Thomas Schwinge <tschwi...@gnu.org> - * Copyright (c) 2007 Thomas Schwinge <tschwi...@gnu.org> - * - * Can be freely distributed and used under the terms of the GNU GPL. - */ - -#include <sys/io.h> - -static inline int -intel_setup_io(struct pci_access *a UNUSED) -{ - return (ioperm (0, 65535, 1) == -1) ? 0 : 1; -} - -static inline int -intel_cleanup_io(struct pci_access *a UNUSED) -{ - ioperm (0, 65535, 0); - - return -1; -} - -static inline void intel_io_lock(void) -{ -} - -static inline void intel_io_unlock(void) -{ -} diff --git a/lib/i386-ports.c b/lib/i386-ports.c index 0b1677a..85515ca 100644 --- a/lib/i386-ports.c +++ b/lib/i386-ports.c @@ -6,16 +6,12 @@ * Can be freely distributed and used under the terms of the GNU GPL. */ -#define _GNU_SOURCE - #include "internal.h" #include <unistd.h> #if defined(PCI_OS_LINUX) #include "i386-io-linux.h" -#elif defined(PCI_OS_GNU) -#include "i386-io-hurd.h" #elif defined(PCI_OS_SUNOS) #include "i386-io-sunos.h" #elif defined(PCI_OS_WINDOWS) diff --git a/lib/init.c b/lib/init.c index adae842..d543301 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_GNU, // 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 abee05a..06b7bc7 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_GNU, /* GNU/Hurd */ PCI_ACCESS_MAX }; -- 2.20.1