summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoan Lledó <jlledom@member.fsf.org>2019-11-30 13:13:25 +0100
committerMartin Mares <mj@ucw.cz>2020-01-25 20:21:57 +0100
commit0a9133703e4db04fe52bf7fa774392b82d51ac04 (patch)
tree83095b793d6ad7032467f91c3c6445ee584ddf40
parentdacdaa94320df20f36a19279ae49f0cb61d51932 (diff)
downloadpciutils-0a9133703e4db04fe52bf7fa774392b82d51ac04.tar.gz
New access method: Hurd via RPCs
A new module for the Hurd that accesses PCI bus using available RPCs.
-rw-r--r--lib/Makefile5
-rwxr-xr-xlib/configure3
-rw-r--r--lib/hurd.c366
-rw-r--r--lib/init.c6
-rw-r--r--lib/internal.h2
-rw-r--r--lib/pci.h3
6 files changed, 382 insertions, 3 deletions
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ó <jlledom@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 <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 = 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 *) &regions;
+ size = sizeof(regions);
+
+ err = pci_get_dev_regions(device_port, &buf, &size);
+ if (err)
+ return err;
+
+ if ((char *) &regions != buf)
+ {
+ /* Sanity check for bogus server. */
+ if (size > sizeof(regions))
+ {
+ vm_deallocate(mach_task_self(), (vm_address_t) buf, size);
+ return EGRATUITOUS;
+ }
+
+ memcpy(&regions, 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
};