]> mj.ucw.cz Git - pciutils.git/commitdiff
New access method: Hurd via RPCs
authorJoan Lledó <jlledom@member.fsf.org>
Sat, 30 Nov 2019 12:13:25 +0000 (13:13 +0100)
committerMartin Mares <mj@ucw.cz>
Sat, 25 Jan 2020 19:21:57 +0000 (20:21 +0100)
A new module for the Hurd that accesses PCI bus using available RPCs.

lib/Makefile
lib/configure
lib/hurd.c [new file with mode: 0644]
lib/init.c
lib/internal.h
lib/pci.h

index 12bbe343cbcb6c789af1630d63e6437ff412a86e..d89cd6c83d92dc2e6ef803ff9fb32ab2788fe67b 100644 (file)
@@ -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)
index 4c328a9f1a99eccb1806153f434f1ec0cb212044..08b94624b9425a2f52c252de0c368f997b7dd982 100755 (executable)
@@ -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 (file)
index 0000000..8cc948b
--- /dev/null
@@ -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
+};
index adae8424a890088a8e48fafa5ffc4135792023a8..e6295fcd302b920264941ea2756ea07d0c2d288d 100644 (file)
@@ -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,
index aaa121a36316dacf77beeb4442fe276d7801f889..714a1dbbf2bb9bc920ce90dad54072dd39cf58bc 100644 (file)
@@ -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;
index 813c333a6724bd8bb1a31ff42b82b5eb241173f1..0adc5c6d4c472db500fb73673f9b21a4def4ebe9 100644 (file)
--- 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
 };