bug-hurd
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH] PCI Arbiter


From: Joan Lledó
Subject: [PATCH] PCI Arbiter
Date: Sun, 3 Dec 2017 11:21:54 +0100

---
 Makefile                  |   1 +
 hurd/hurd_types.defs      |  19 +-
 hurd/hurd_types.h         |  24 +-
 hurd/paths.h              |   3 +
 hurd/pci.defs             |  73 ++++
 hurd/subsystems           |   1 +
 pci-arbiter/Makefile      |  44 +++
 pci-arbiter/config.h      |   5 +
 pci-arbiter/func_files.c  | 210 +++++++++++
 pci-arbiter/func_files.h  |  45 +++
 pci-arbiter/main.c        | 117 +++++++
 pci-arbiter/mig-mutate.h  |  28 ++
 pci-arbiter/ncache.c      |  90 +++++
 pci-arbiter/ncache.h      |  32 ++
 pci-arbiter/netfs_impl.c  | 539 ++++++++++++++++++++++++++++
 pci-arbiter/netfs_impl.h  |  43 +++
 pci-arbiter/options.c     | 291 ++++++++++++++++
 pci-arbiter/options.h     |  74 ++++
 pci-arbiter/pci-ops.c     | 273 +++++++++++++++
 pci-arbiter/pci_access.c  |  51 +++
 pci-arbiter/pci_access.h  | 182 ++++++++++
 pci-arbiter/pcifs.c       | 450 ++++++++++++++++++++++++
 pci-arbiter/pcifs.h       | 210 +++++++++++
 pci-arbiter/startup-ops.c |  41 +++
 pci-arbiter/startup.c     |  58 ++++
 pci-arbiter/startup.h     |  31 ++
 pci-arbiter/x86_pci.c     | 869 ++++++++++++++++++++++++++++++++++++++++++++++
 pci-arbiter/x86_pci.h     |  34 ++
 28 files changed, 3836 insertions(+), 2 deletions(-)
 create mode 100644 hurd/pci.defs
 create mode 100644 pci-arbiter/Makefile
 create mode 100644 pci-arbiter/config.h
 create mode 100644 pci-arbiter/func_files.c
 create mode 100644 pci-arbiter/func_files.h
 create mode 100644 pci-arbiter/main.c
 create mode 100644 pci-arbiter/mig-mutate.h
 create mode 100644 pci-arbiter/ncache.c
 create mode 100644 pci-arbiter/ncache.h
 create mode 100644 pci-arbiter/netfs_impl.c
 create mode 100644 pci-arbiter/netfs_impl.h
 create mode 100644 pci-arbiter/options.c
 create mode 100644 pci-arbiter/options.h
 create mode 100644 pci-arbiter/pci-ops.c
 create mode 100644 pci-arbiter/pci_access.c
 create mode 100644 pci-arbiter/pci_access.h
 create mode 100644 pci-arbiter/pcifs.c
 create mode 100644 pci-arbiter/pcifs.h
 create mode 100644 pci-arbiter/startup-ops.c
 create mode 100644 pci-arbiter/startup.c
 create mode 100644 pci-arbiter/startup.h
 create mode 100644 pci-arbiter/x86_pci.c
 create mode 100644 pci-arbiter/x86_pci.h

diff --git a/Makefile b/Makefile
index 119f130b..d5387fee 100644
--- a/Makefile
+++ b/Makefile
@@ -45,6 +45,7 @@ prog-subdirs = auth proc exec term \
               init \
               devnode \
               eth-multiplexer \
+              pci-arbiter
 
 ifeq ($(HAVE_SUN_RPC),yes)
 prog-subdirs += nfs nfsd
diff --git a/hurd/hurd_types.defs b/hurd/hurd_types.defs
index 4d7013c8..0e9b990e 100644
--- a/hurd/hurd_types.defs
+++ b/hurd/hurd_types.defs
@@ -1,5 +1,5 @@
 /* MiG type declarations for Hurd interfaces           -*- C -*-
-   Copyright (C) 1993,94,95,96,98,2001,02 Free Software Foundation, Inc.
+   Copyright (C) 1993,94,95,96,98,2001,02,17 Free Software Foundation, Inc.
 
 This file is part of the GNU Hurd.
 
@@ -296,6 +296,23 @@ destructor: INTERRUPT_DESTRUCTOR
 #endif
 ;
 
+/* PCI arbiter */
+type pci_t = mach_port_copy_send_t
+#ifdef PCI_INTRAN
+intran: PCI_INTRAN
+intranpayload: PCI_INTRAN_PAYLOAD
+#else
+#ifdef HURD_DEFAULT_PAYLOAD_TO_PORT
+intranpayload: pci_t HURD_DEFAULT_PAYLOAD_TO_PORT
+#endif
+#endif
+#ifdef PCI_OUTTRAN
+outtran: PCI_OUTTRAN
+#endif
+#ifdef PCI_DESTRUCTOR
+destructor: PCI_DESTRUCTOR
+#endif
+;
 
 type proccoll_t = mach_port_copy_send_t;
 
diff --git a/hurd/hurd_types.h b/hurd/hurd_types.h
index 2960a294..420d6ec3 100644
--- a/hurd/hurd_types.h
+++ b/hurd/hurd_types.h
@@ -1,5 +1,5 @@
 /* C declarations for Hurd server interfaces
-   Copyright (C) 1993,94,95,96,98,99,2001,02 Free Software Foundation, Inc.
+   Copyright (C) 1993,94,95,96,98,99,2001,02,17 Free Software Foundation, Inc.
 
 This file is part of the GNU Hurd.
 
@@ -50,6 +50,7 @@ typedef mach_port_t exec_startup_t;
 typedef mach_port_t interrupt_t;
 typedef mach_port_t proccoll_t;
 typedef mach_port_t ctty_t;
+typedef mach_port_t pci_t;
 
 #include <errno.h>             /* Defines `error_t'.  */
 
@@ -349,6 +350,7 @@ typedef int *procinfo_t;
 #define FSTYPE_HTTP    0x00000018 /* Transparent HTTP */
 #define FSTYPE_MEMFS   0x00000019 /* In-core filesystem */
 #define FSTYPE_ISO9660 0x0000001a /* ISO9660 */
+#define FSTYPE_PCI     0x0000001b /* PCI filesystem */
 
 /* Standard port assignments for file_exec and exec_* */
 enum
@@ -375,4 +377,24 @@ enum
     INIT_INT_MAX,
   };
 
+/* PCI arbiter types*/
+#include <stdint.h>
+
+/* Memory region */
+struct pci_bar
+  {
+    uint64_t base_addr;
+    uint64_t size;
+    unsigned is_IO:1;
+    unsigned is_prefetchable:1;
+    unsigned is_64:1;
+  };
+
+/* Expansion ROM region */
+struct pci_xrom_bar
+  {
+    uint64_t base_addr;
+    uint64_t size;
+  };
+
 #endif
diff --git a/hurd/paths.h b/hurd/paths.h
index e1b00e90..10ae3a6f 100644
--- a/hurd/paths.h
+++ b/hurd/paths.h
@@ -36,6 +36,9 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 
02139, USA.  */
    in simple decimal (e.g. "/servers/socket/23").  */
 #define        _SERVERS_SOCKET         _SERVERS "socket"
 
+/* Directory containing virtual filesystems for buses */
+#define        _SERVERS_BUS            _SERVERS "bus"
+
 /* Hurd servers are specified by symbols _HURD_FOO,
    the canonical pathname being /hurd/foo.  */
 
diff --git a/hurd/pci.defs b/hurd/pci.defs
new file mode 100644
index 00000000..23c90334
--- /dev/null
+++ b/hurd/pci.defs
@@ -0,0 +1,73 @@
+/* Definitions for pci-specific calls
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING.  If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+subsystem pci 39000;
+
+#include <hurd/hurd_types.defs>
+
+#ifdef PCI_IMPORTS
+PCI_IMPORTS
+INTR_INTERFACE
+#endif
+
+/*
+ * Read 'amount' bytes from offset 'reg' in the
+ *  configuration space and store it in 'data'
+ */
+routine pci_conf_read (
+       master: pci_t;
+       reg: int;
+       out data: data_t, dealloc;
+       amount: vm_size_t
+);
+
+/* Write 'amount' bytes from 'data' to offset 'reg' in the config space */
+routine pci_conf_write(
+       master: pci_t;
+       reg: int;
+       data: data_t;
+       out amount: vm_size_t
+);
+
+/*
+ * Calculate the number of devices that are allowed
+ * for the user and return it in 'numdevs'.
+ */
+routine pci_get_ndevs(
+       master: pci_t;
+       out ndevs: vm_size_t
+);
+
+/*
+ * Return the memory regions for a specified device.
+ * `data' is an array of 6 struct pci_bar
+ */
+routine pci_get_dev_regions(
+       master: pci_t;
+       out data: data_t, dealloc
+);
+
+/*
+ * Return the expansion ROM bar for a given device.
+ * `data' is a struct pci_xrom_bar
+ */
+routine pci_get_dev_rom(
+       master: pci_t;
+       out data: data_t, dealloc
+);
diff --git a/hurd/subsystems b/hurd/subsystems
index c05895c2..0677bb1e 100644
--- a/hurd/subsystems
+++ b/hurd/subsystems
@@ -36,6 +36,7 @@ tape          35000   Special control operations for magtapes
 login          36000   Database of logged-in users
 pfinet         37000   Internet configuration calls
 password       38000   Password checker
+pci            39000   PCI arbiter
 <ioctl space>  100000- First subsystem of ioctl class 'f' (lowest class)
 tioctl        156000   Ioctl class 't' (terminals)
 tioctl        156200     (continued)
diff --git a/pci-arbiter/Makefile b/pci-arbiter/Makefile
new file mode 100644
index 00000000..c3a6db6f
--- /dev/null
+++ b/pci-arbiter/Makefile
@@ -0,0 +1,44 @@
+#   Copyright (C) 2017 Free Software Foundation, Inc.
+#
+#   This file is part of the GNU Hurd.
+#
+#   The GNU Hurd is free software; you can redistribute it and/or
+#   modify it under the terms of the GNU General Public License as
+#   published by the Free Software Foundation; either version 2, or (at
+#   your option) any later version.
+#
+#   The GNU Hurd is distributed in the hope that it will be useful, but
+#   WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#   General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with the GNU Hurd.  If not, see <http://www.gnu.org/licenses/>.
+
+dir            = pci-arbiter
+makemode       = server
+
+PORTDIR = $(srcdir)/port
+
+SRCS           = main.c pci-ops.c pci_access.c x86_pci.c netfs_impl.c \
+                 pcifs.c ncache.c options.c func_files.c startup.c \
+                 startup-ops.c
+MIGSRCS                = pciServer.c startup_notifyServer.c
+OBJS           = $(patsubst %.S,%.o,$(patsubst %.c,%.o, $(SRCS) $(MIGSRCS)))
+
+HURDLIBS= fshelp ports shouldbeinlibc netfs
+LDLIBS = -lpthread
+
+target = pci-arbiter
+
+include ../Makeconf
+
+CFLAGS += -I$(PORTDIR)/include
+
+CPPFLAGS += -imacros $(srcdir)/config.h
+pci-MIGSFLAGS = -imacros $(srcdir)/mig-mutate.h
+
+# cpp doesn't automatically make dependencies for -imacros dependencies. argh.
+pci_S.h pciServer.c: mig-mutate.h
+
+$(OBJS): config.h
diff --git a/pci-arbiter/config.h b/pci-arbiter/config.h
new file mode 100644
index 00000000..b0d5196d
--- /dev/null
+++ b/pci-arbiter/config.h
@@ -0,0 +1,5 @@
+#define __KERNEL__     1
+#undef __SMP__
+
+#define _HURD_         1
+
diff --git a/pci-arbiter/func_files.c b/pci-arbiter/func_files.c
new file mode 100644
index 00000000..135e3fbf
--- /dev/null
+++ b/pci-arbiter/func_files.c
@@ -0,0 +1,210 @@
+/*
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+   This file is part of the GNU Hurd.
+
+   The GNU Hurd is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2, or (at
+   your option) any later version.
+
+   The GNU Hurd is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the GNU Hurd.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Per-function files implementation.
+ *
+ * Implementation of all files repeated for each function.
+ */
+
+#include <func_files.h>
+
+#include <assert.h>
+#include <sys/io.h>
+
+/* Read or write a block of data from/to the configuration space */
+static error_t
+config_block_op (struct pci_device *dev, off_t offset, size_t * len,
+                void *data, pci_io_op_t op)
+{
+  error_t err;
+  size_t pendent = *len;
+
+  while (pendent >= 4)
+    {
+      err = op (dev->bus, dev->dev, dev->func, offset, data, 4);
+      if (err)
+       return err;
+
+      offset += 4;
+      data += 4;
+      pendent -= 4;
+    }
+
+  if (pendent >= 2)
+    {
+      err = op (dev->bus, dev->dev, dev->func, offset, data, 2);
+      if (err)
+       return err;
+
+      offset += 2;
+      data += 2;
+      pendent -= 2;
+    }
+
+  if (pendent)
+    {
+      err = op (dev->bus, dev->dev, dev->func, offset, data, 1);
+      if (err)
+       return err;
+
+      offset++;
+      data++;
+      pendent--;
+    }
+
+  *len -= pendent;
+
+  return 0;
+}
+
+/* Read or write from/to the config file */
+error_t
+io_config_file (struct pci_device * dev, off_t offset, size_t * len,
+               void *data, pci_io_op_t op)
+{
+  error_t err;
+
+  /* This should never happen */
+  assert_backtrace (dev != 0);
+
+  /* Don't exceed the config space size */
+  if (offset > FILE_CONFIG_SIZE)
+    return EINVAL;
+  if ((offset + *len) > FILE_CONFIG_SIZE)
+    *len = FILE_CONFIG_SIZE - offset;
+
+  pthread_mutex_lock (&fs->pci_conf_lock);
+  err = config_block_op (dev, offset, len, data, op);
+  pthread_mutex_unlock (&fs->pci_conf_lock);
+
+  return err;
+}
+
+/* Read the mapped ROM */
+error_t
+read_rom_file (struct pci_device * dev, off_t offset, size_t * len,
+              void *data)
+{
+  error_t err;
+
+  /* This should never happen */
+  assert_backtrace (dev != 0);
+
+  /* Refresh the ROM */
+  err = pci_sys->device_refresh (dev, -1, 1);
+  if (err)
+    return err;
+
+  /* Don't exceed the ROM size */
+  if (offset > dev->rom_size)
+    return EINVAL;
+  if ((offset + *len) > dev->rom_size)
+    *len = dev->rom_size - offset;
+
+  memcpy (data, dev->rom_memory + offset, *len);
+
+  return 0;
+}
+
+/* Read or write from/to a memory region by using I/O ports */
+static error_t
+region_block_ioport_op (uint16_t port, off_t offset, size_t * len,
+                       void *data, int read)
+{
+  size_t pending = *len;
+
+  while (pending >= 4)
+    {
+      /* read == true: read; else: write */
+      if (read)
+       *((unsigned int *) data) = inl (port + offset);
+      else
+       outl (*((unsigned int *) data), port + offset);
+
+      offset += 4;
+      data += 4;
+      pending -= 4;
+    }
+
+  if (pending >= 2)
+    {
+      if (read)
+       *((unsigned short *) data) = inw (port + offset);
+      else
+       outw (*((unsigned short *) data), port + offset);
+
+      offset += 2;
+      data += 2;
+      pending -= 2;
+    }
+
+  if (pending)
+    {
+      if (read)
+       *((unsigned char *) data) = inb (port + offset);
+      else
+       outb (*((unsigned char *) data), port + offset);
+
+      offset++;
+      data++;
+      pending--;
+    }
+
+  *len -= pending;
+
+  return 0;
+}
+
+/* Read or write from/to a region file */
+error_t
+io_region_file (struct pcifs_dirent * e, off_t offset, size_t * len,
+               void *data, int read)
+{
+  error_t err;
+  size_t reg_num;
+  struct pci_mem_region *region;
+
+  /* This should never happen */
+  assert_backtrace (e->device != 0);
+
+  /* Get the region */
+  reg_num = strtol (&e->name[strlen (e->name) - 1], 0, 16);
+  region = &e->device->regions[reg_num];
+
+  /* Refresh the region */
+  err = pci_sys->device_refresh (e->device, reg_num, -1);
+  if (err)
+    return err;
+
+  /* Don't exceed the region size */
+  if (offset > region->size)
+    return EINVAL;
+  if ((offset + *len) > region->size)
+    *len = region->size - offset;
+
+  if (region->is_IO)
+    region_block_ioport_op (region->base_addr, offset, len, data, read);
+  else if (read)
+    memcpy (data, region->memory + offset, *len);
+  else
+    memcpy (region->memory + offset, data, *len);
+
+  return 0;
+}
diff --git a/pci-arbiter/func_files.h b/pci-arbiter/func_files.h
new file mode 100644
index 00000000..e4c64d65
--- /dev/null
+++ b/pci-arbiter/func_files.h
@@ -0,0 +1,45 @@
+/*
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+   This file is part of the GNU Hurd.
+
+   The GNU Hurd is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2, or (at
+   your option) any later version.
+
+   The GNU Hurd is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the GNU Hurd.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Per-function files header */
+
+#ifndef FUNC_FILES_H
+#define FUNC_FILES_H
+
+#include <pcifs.h>
+
+/* Config */
+#define FILE_CONFIG_NAME  "config"
+#define FILE_CONFIG_SIZE  256
+
+/* Rom */
+#define FILE_ROM_NAME     "rom"
+
+/* Region */
+#define FILE_REGION_NAME     "region"
+
+error_t io_config_file (struct pci_device * dev, off_t offset, size_t * len,
+                       void *data, pci_io_op_t op);
+
+error_t read_rom_file (struct pci_device *dev, off_t offset, size_t * len,
+                      void *data);
+
+error_t io_region_file (struct pcifs_dirent *e, off_t offset, size_t * len,
+                       void *data, int read);
+#endif /* FUNC_FILES_H */
diff --git a/pci-arbiter/main.c b/pci-arbiter/main.c
new file mode 100644
index 00000000..6ade4e30
--- /dev/null
+++ b/pci-arbiter/main.c
@@ -0,0 +1,117 @@
+/*
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+   This file is part of the GNU Hurd.
+
+   The GNU Hurd is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2, or (at
+   your option) any later version.
+
+   The GNU Hurd is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the GNU Hurd.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Translator initialization and demuxing */
+
+#include <stdio.h>
+#include <error.h>
+#include <fcntl.h>
+#include <version.h>
+#include <argp.h>
+#include <hurd/netfs.h>
+
+#include <pci_S.h>
+#include <startup_notify_S.h>
+#include "libnetfs/io_S.h"
+#include "libnetfs/fs_S.h"
+#include "libports/notify_S.h"
+#include "libnetfs/fsys_S.h"
+#include "libports/interrupt_S.h"
+#include "libnetfs/ifsock_S.h"
+#include <pci_access.h>
+#include <pcifs.h>
+#include <startup.h>
+
+/* Libnetfs stuff */
+int netfs_maxsymlinks = 0;
+char *netfs_server_name = "pci-arbiter";
+char *netfs_server_version = HURD_VERSION;
+
+int
+netfs_demuxer (mach_msg_header_t * inp, mach_msg_header_t * outp)
+{
+  mig_routine_t routine;
+
+  if ((routine = netfs_io_server_routine (inp)) ||
+      (routine = netfs_fs_server_routine (inp)) ||
+      (routine = ports_notify_server_routine (inp)) ||
+      (routine = netfs_fsys_server_routine (inp)) ||
+      (routine = ports_interrupt_server_routine (inp)) ||
+      (routine = netfs_ifsock_server_routine (inp)) ||
+      (routine = pci_server_routine (inp)) ||
+      (routine = startup_notify_server_routine (inp)))
+    {
+      (*routine) (inp, outp);
+      return TRUE;
+    }
+  else
+    return FALSE;
+}
+
+int
+main (int argc, char **argv)
+{
+  error_t err;
+  mach_port_t bootstrap;
+
+  /* Parse options */
+  alloc_file_system (&fs);
+  argp_parse (netfs_runtime_argp, argc, argv, 0, 0, 0);
+
+  task_get_bootstrap_port (mach_task_self (), &bootstrap);
+  if (bootstrap == MACH_PORT_NULL)
+    error (1, 0, "must be started as a translator");
+
+  /* Initialize netfs and start the translator. */
+  netfs_init ();
+
+  err = maptime_map (0, 0, &pcifs_maptime);
+  if (err)
+    error (1, err, "mapping time");
+
+  /* Start the PCI system */
+  err = pci_system_init ();
+  if (err)
+    error (1, err, "Starting the PCI system");
+
+  /* Create the PCI filesystem */
+  err = init_file_system (netfs_startup (bootstrap, O_READ), fs);
+  if (err)
+    error (1, err, "Creating the PCI filesystem");
+
+  /* Create the filesystem tree */
+  err = create_fs_tree (fs, pci_sys);
+  if (err)
+    error (1, err, "Creating the PCI filesystem tree");
+
+  /* Set permissions */
+  err = fs_set_permissions (fs);
+  if (err)
+    error (1, err, "Setting permissions");
+
+  /*
+   * Ask init to tell us when the system is going down,
+   * so we can try to be friendly to our correspondents on the network.
+   */
+  arrange_shutdown_notification ();
+
+  netfs_server_loop ();                /* Never returns.  */
+
+  return 0;
+}
diff --git a/pci-arbiter/mig-mutate.h b/pci-arbiter/mig-mutate.h
new file mode 100644
index 00000000..f5098c97
--- /dev/null
+++ b/pci-arbiter/mig-mutate.h
@@ -0,0 +1,28 @@
+/*
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   Written by Michael I. Bushnell, p/BSG.
+
+   This file is part of the GNU Hurd.
+
+   The GNU Hurd is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2, or (at
+   your option) any later version.
+
+   The GNU Hurd is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the GNU Hurd.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Only CPP macro definitions should go in this file. */
+
+#define PCI_IMPORTS                            \
+  import "../libnetfs/priv.h";         \
+
+#define PCI_INTRAN protid_t begin_using_protid_port (pci_t)
+#define PCI_INTRAN_PAYLOAD protid_t begin_using_protid_payload
+#define PCI_DESTRUCTOR end_using_protid_port (protid_t)
diff --git a/pci-arbiter/ncache.c b/pci-arbiter/ncache.c
new file mode 100644
index 00000000..67c70b57
--- /dev/null
+++ b/pci-arbiter/ncache.c
@@ -0,0 +1,90 @@
+/* Node caching
+
+   Copyright (C) 1997 Free Software Foundation, Inc.
+   Written by Miles Bader <miles@gnu.org>
+   This file is part of the GNU Hurd.
+
+   The GNU Hurd is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2, or (at
+   your option) any later version.
+
+   The GNU Hurd is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA. */
+
+#include <ncache.h>
+
+#include <unistd.h>
+#include <string.h>
+#include <hurd/netfs.h>
+
+#include <pcifs.h>
+#include <netfs_impl.h>
+
+/* Implementation of node caching functions */
+
+/* Remove NN's node from its position in FS's node cache.  */
+void
+node_unlink (struct node *node, struct pcifs *fs)
+{
+  struct netnode *nn = node->nn;
+  if (nn->ncache_next)
+    nn->ncache_next->nn->ncache_prev = nn->ncache_prev;
+  if (nn->ncache_prev)
+    nn->ncache_prev->nn->ncache_next = nn->ncache_next;
+  if (fs->node_cache_mru == node)
+    fs->node_cache_mru = nn->ncache_next;
+  if (fs->node_cache_lru == node)
+    fs->node_cache_lru = nn->ncache_prev;
+  nn->ncache_next = 0;
+  nn->ncache_prev = 0;
+  fs->node_cache_len--;
+}
+
+/* Add NODE to the recently-used-node cache, which adds a reference to
+   prevent it from going away.  NODE should be locked.  */
+void
+node_cache (struct node *node)
+{
+  struct netnode *nn = node->nn;
+
+  pthread_mutex_lock (&fs->node_cache_lock);
+
+  if (fs->params.node_cache_max > 0 || fs->node_cache_len > 0)
+    {
+      if (fs->node_cache_mru != node)
+       {
+         if (nn->ncache_next || nn->ncache_prev)
+           /* Node is already in the cache.  */
+           node_unlink (node, fs);
+         else
+           /* Add a reference from the cache.  */
+           netfs_nref (node);
+
+         nn->ncache_next = fs->node_cache_mru;
+         nn->ncache_prev = 0;
+         if (fs->node_cache_mru)
+           fs->node_cache_mru->nn->ncache_prev = node;
+         if (!fs->node_cache_lru)
+           fs->node_cache_lru = node;
+         fs->node_cache_mru = node;
+         fs->node_cache_len++;
+       }
+
+      /* Forget the least used nodes.  */
+      while (fs->node_cache_len > fs->params.node_cache_max)
+       {
+         struct node *lru = fs->node_cache_lru;
+         node_unlink (lru, fs);
+         netfs_nrele (lru);
+       }
+    }
+
+  pthread_mutex_unlock (&fs->node_cache_lock);
+}
diff --git a/pci-arbiter/ncache.h b/pci-arbiter/ncache.h
new file mode 100644
index 00000000..f39f798a
--- /dev/null
+++ b/pci-arbiter/ncache.h
@@ -0,0 +1,32 @@
+/*
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+   This file is part of the GNU Hurd.
+
+   The GNU Hurd is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2, or (at
+   your option) any later version.
+
+   The GNU Hurd is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the GNU Hurd.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Header for node caching functions */
+
+#ifndef NCACHE_H
+#define NCACHE_H
+
+#include <hurd/netfs.h>
+
+#include <pcifs.h>
+
+void node_cache (struct node *node);
+void node_unlink (struct node *node, struct pcifs *fs);
+
+#endif /* NCACHE_H */
diff --git a/pci-arbiter/netfs_impl.c b/pci-arbiter/netfs_impl.c
new file mode 100644
index 00000000..e6e0917a
--- /dev/null
+++ b/pci-arbiter/netfs_impl.c
@@ -0,0 +1,539 @@
+/*
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   Written by Miles Bader <miles@gnu.org>
+   This file is part of the GNU Hurd.
+
+   The GNU Hurd is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2, or (at
+   your option) any later version.
+
+   The GNU Hurd is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the GNU Hurd.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Libnetfs callbacks */
+
+#include <netfs_impl.h>
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <hurd/netfs.h>
+
+#include <pcifs.h>
+#include <ncache.h>
+#include <pci_access.h>
+#include <func_files.h>
+
+#define DIRENTS_CHUNK_SIZE      (8*1024)
+/* Returned directory entries are aligned to blocks this many bytes long.
+ * Must be a power of two.  */
+#define DIRENT_ALIGN 4
+#define DIRENT_NAME_OFFS offsetof (struct dirent, d_name)
+
+/* Length is structure before the name + the name + '\0', all
+ *    padded to a four-byte alignment.  */
+#define DIRENT_LEN(name_len)                                                  \
+  ((DIRENT_NAME_OFFS + (name_len) + 1 + (DIRENT_ALIGN - 1))                   \
+   & ~(DIRENT_ALIGN - 1))
+
+/* Fetch a directory, as for netfs_get_dirents.  */
+static error_t
+get_dirents (struct pcifs_dirent *dir,
+            int first_entry, int max_entries, char **data,
+            mach_msg_type_number_t * data_len,
+            vm_size_t max_data_len, int *data_entries)
+{
+  struct pcifs_dirent *e;
+  error_t err = 0;
+  int i, count;
+  size_t size;
+  char *p;
+
+  if (first_entry >= dir->dir->num_entries)
+    {
+      *data_len = 0;
+      *data_entries = 0;
+      return 0;
+    }
+
+  if (max_entries < 0)
+    count = dir->dir->num_entries;
+  else
+    {
+      count = ((first_entry + max_entries) >= dir->dir->num_entries ?
+              dir->dir->num_entries : max_entries) - first_entry;
+    }
+
+  size =
+    (count * DIRENTS_CHUNK_SIZE) >
+    max_data_len ? max_data_len : count * DIRENTS_CHUNK_SIZE;
+
+  *data = mmap (0, size, PROT_READ | PROT_WRITE, MAP_ANON, 0, 0);
+  err = ((void *) *data == (void *) -1) ? errno : 0;
+  if (err)
+    return err;
+
+  p = *data;
+  for (i = 0; i < count; i++)
+    {
+      struct dirent hdr;
+      size_t name_len;
+      size_t sz;
+      int entry_type;
+
+      e = dir->dir->entries[i + first_entry];
+      name_len = strlen (e->name) + 1;
+      sz = DIRENT_LEN (name_len);
+      entry_type = IFTODT (e->stat.st_mode);
+
+      hdr.d_namlen = name_len;
+      hdr.d_fileno = e->stat.st_ino;
+      hdr.d_reclen = sz;
+      hdr.d_type = entry_type;
+
+      memcpy (p, &hdr, DIRENT_NAME_OFFS);
+      strncpy (p + DIRENT_NAME_OFFS, e->name, name_len);
+      p += sz;
+    }
+
+  vm_address_t alloc_end = (vm_address_t) (*data + size);
+  vm_address_t real_end = round_page (p);
+  if (alloc_end > real_end)
+    munmap ((caddr_t) real_end, alloc_end - real_end);
+  *data_len = p - *data;
+  *data_entries = count;
+
+  return err;
+}
+
+static struct pcifs_dirent *
+lookup (struct node *np, char *name)
+{
+  int i;
+  struct pcifs_dirent *ret = 0, *e;
+
+  for (i = 0; i < np->nn->ln->dir->num_entries; i++)
+    {
+      e = np->nn->ln->dir->entries[i];
+
+      if (!strncmp (e->name, name, NAME_SIZE))
+       {
+         ret = e;
+         break;
+       }
+    }
+
+  return ret;
+}
+
+/* Attempt to create a file named NAME in DIR for USER with MODE.  Set *NODE
+   to the new node upon return.  On any error, clear *NODE.  *NODE should be
+   locked on success; no matter what, unlock DIR before returning.  */
+error_t
+netfs_attempt_create_file (struct iouser * user, struct node * dir,
+                          char *name, mode_t mode, struct node ** node)
+{
+  *node = 0;
+  pthread_mutex_unlock (&dir->lock);
+  return EOPNOTSUPP;
+}
+
+/* Node NODE is being opened by USER, with FLAGS.  NEWNODE is nonzero if we
+   just created this node.  Return an error if we should not permit the open
+   to complete because of a permission restriction. */
+error_t
+netfs_check_open_permissions (struct iouser * user, struct node * node,
+                             int flags, int newnode)
+{
+  return entry_check_perms (user, node->nn->ln, flags);
+}
+
+/* This should attempt a utimes call for the user specified by CRED on node
+   NODE, to change the atime to ATIME and the mtime to MTIME. */
+error_t
+netfs_attempt_utimes (struct iouser * cred, struct node * node,
+                     struct timespec * atime, struct timespec * mtime)
+{
+  return EOPNOTSUPP;
+}
+
+/* Return the valid access types (bitwise OR of O_READ, O_WRITE, and O_EXEC)
+   in *TYPES for file NODE and user CRED.  */
+error_t
+netfs_report_access (struct iouser * cred, struct node * node, int *types)
+{
+  return EOPNOTSUPP;
+}
+
+/* Trivial definitions.  */
+
+/* Make sure that NP->nn_stat is filled with current information.  CRED
+   identifies the user responsible for the operation.  */
+error_t
+netfs_validate_stat (struct node * node, struct iouser * cred)
+{
+  /* Nothing to do here */
+  return 0;
+}
+
+/* This should sync the file NODE completely to disk, for the user CRED.  If
+   WAIT is set, return only after sync is completely finished.  */
+error_t
+netfs_attempt_sync (struct iouser * cred, struct node * node, int wait)
+{
+  return EOPNOTSUPP;
+}
+
+error_t
+netfs_get_dirents (struct iouser * cred, struct node * dir,
+                  int first_entry, int max_entries, char **data,
+                  mach_msg_type_number_t * data_len,
+                  vm_size_t max_data_len, int *data_entries)
+{
+  error_t err = 0;
+
+  if (dir->nn->ln->dir)
+    {
+      err = get_dirents (dir->nn->ln, first_entry, max_entries,
+                        data, data_len, max_entries, data_entries);
+    }
+  else
+    err = ENOTDIR;
+
+  if (!err)
+    /* Update atime */
+    UPDATE_TIMES (dir->nn->ln, TOUCH_ATIME);
+
+  return err;
+}
+
+/* Lookup NAME in DIR for USER; set *NODE to the found name upon return.  If
+   the name was not found, then return ENOENT.  On any error, clear *NODE.
+   (*NODE, if found, should be locked, this call should unlock DIR no matter
+   what.) */
+error_t
+netfs_attempt_lookup (struct iouser * user, struct node * dir,
+                     char *name, struct node ** node)
+{
+  error_t err = 0;
+  struct pcifs_dirent *entry;
+
+  if (*name == '\0' || strcmp (name, ".") == 0)
+    /* Current directory -- just add an additional reference to DIR's node
+       and return it.  */
+    {
+      netfs_nref (dir);
+      *node = dir;
+      return 0;
+    }
+  else if (strcmp (name, "..") == 0)
+    /* Parent directory.  */
+    {
+      if (dir->nn->ln->parent)
+       {
+         *node = dir->nn->ln->parent->node;
+         pthread_mutex_lock (&(*node)->lock);
+         netfs_nref (*node);
+       }
+      else
+       {
+         err = ENOENT;         /* No .. */
+         *node = 0;
+       }
+
+      pthread_mutex_unlock (&dir->lock);
+
+      return err;
+    }
+
+  /* `name' is not . nor .. */
+  if (dir->nn->ln->dir)
+    {
+      /* `dir' is a directory */
+
+      /* Check dir permissions */
+      err = entry_check_perms (user, dir->nn->ln, O_READ | O_EXEC);
+      if (!err)
+       {
+         entry = lookup (dir, name);
+         if (!entry)
+           {
+             err = ENOENT;
+           }
+         else
+           {
+             if (entry->node)
+               {
+                 netfs_nref (entry->node);
+               }
+             else
+               {
+                 /*
+                  * No active node, create one.
+                  * The new node is created with a reference.
+                  */
+                 err = create_node (entry, node);
+               }
+
+             if (!err)
+               {
+                 *node = entry->node;
+                 /* We have to unlock DIR's node before locking the child node
+                    because the locking order is always child-parent.  We know
+                    the child node won't go away because we already hold the
+                    additional reference to it.  */
+                 pthread_mutex_unlock (&dir->lock);
+                 pthread_mutex_lock (&(*node)->lock);
+               }
+           }
+       }
+    }
+  else
+    {
+      err = ENOTDIR;
+    }
+
+  if (err)
+    {
+      *node = 0;
+      pthread_mutex_unlock (&dir->lock);
+    }
+  else
+    {
+      /* Update the node cache */
+      node_cache (*node);
+    }
+
+  return err;
+}
+
+/* Delete NAME in DIR for USER. */
+error_t
+netfs_attempt_unlink (struct iouser * user, struct node * dir, char *name)
+{
+  return EOPNOTSUPP;
+}
+
+/* Note that in this one call, neither of the specific nodes are locked. */
+error_t
+netfs_attempt_rename (struct iouser * user, struct node * fromdir,
+                     char *fromname, struct node * todir,
+                     char *toname, int excl)
+{
+  return EOPNOTSUPP;
+}
+
+/* Attempt to create a new directory named NAME in DIR for USER with mode
+   MODE.  */
+error_t
+netfs_attempt_mkdir (struct iouser * user, struct node * dir,
+                    char *name, mode_t mode)
+{
+  return EOPNOTSUPP;
+}
+
+/* Attempt to remove directory named NAME in DIR for USER. */
+error_t
+netfs_attempt_rmdir (struct iouser * user, struct node * dir, char *name)
+{
+  return EOPNOTSUPP;
+}
+
+/* This should attempt a chmod call for the user specified by CRED on node
+   NODE, to change the owner to UID and the group to GID. */
+error_t
+netfs_attempt_chown (struct iouser * cred, struct node * node,
+                    uid_t uid, uid_t gid)
+{
+  return EOPNOTSUPP;
+}
+
+/* This should attempt a chauthor call for the user specified by CRED on node
+   NODE, to change the author to AUTHOR. */
+error_t
+netfs_attempt_chauthor (struct iouser * cred, struct node * node,
+                       uid_t author)
+{
+  return EOPNOTSUPP;
+}
+
+/* This should attempt a chmod call for the user specified by CRED on node
+   NODE, to change the mode to MODE.  Unlike the normal Unix and Hurd meaning
+   of chmod, this function is also used to attempt to change files into other
+   types.  If such a transition is attempted which is impossible, then return
+   EOPNOTSUPP.  */
+error_t
+netfs_attempt_chmod (struct iouser * cred, struct node * node, mode_t mode)
+{
+  return EOPNOTSUPP;
+}
+
+/* Attempt to turn NODE (user CRED) into a symlink with target NAME. */
+error_t
+netfs_attempt_mksymlink (struct iouser * cred, struct node * node, char *name)
+{
+  return EOPNOTSUPP;
+}
+
+/* Attempt to turn NODE (user CRED) into a device.  TYPE is either S_IFBLK or
+   S_IFCHR. */
+error_t
+netfs_attempt_mkdev (struct iouser * cred, struct node * node,
+                    mode_t type, dev_t indexes)
+{
+  return EOPNOTSUPP;
+}
+
+/* This should attempt a chflags call for the user specified by CRED on node
+   NODE, to change the flags to FLAGS. */
+error_t
+netfs_attempt_chflags (struct iouser * cred, struct node * node, int flags)
+{
+  return EOPNOTSUPP;
+}
+
+/* This should attempt to set the size of the file NODE (for user CRED) to
+   SIZE bytes long. */
+error_t
+netfs_attempt_set_size (struct iouser * cred, struct node * node, off_t size)
+{
+  /* Do nothing */
+  return 0;
+}
+
+/* This should attempt to fetch filesystem status information for the remote
+   filesystem, for the user CRED. */
+error_t
+netfs_attempt_statfs (struct iouser * cred, struct node * node,
+                     struct statfs * st)
+{
+  memset (st, 0, sizeof *st);
+  st->f_type = FSTYPE_PCI;
+  st->f_fsid = getpid ();
+  return 0;
+}
+
+/* This should sync the entire remote filesystem.  If WAIT is set, return
+   only after sync is completely finished.  */
+error_t
+netfs_attempt_syncfs (struct iouser * cred, int wait)
+{
+  return 0;
+}
+
+/* Create a link in DIR with name NAME to FILE for USER.  Note that neither
+   DIR nor FILE are locked.  If EXCL is set, do not delete the target, but
+   return EEXIST if NAME is already found in DIR.  */
+error_t
+netfs_attempt_link (struct iouser * user, struct node * dir,
+                   struct node * file, char *name, int excl)
+{
+  return EOPNOTSUPP;
+}
+
+/* Attempt to create an anonymous file related to DIR for USER with MODE.
+   Set *NODE to the returned file upon success.  No matter what, unlock DIR. */
+error_t
+netfs_attempt_mkfile (struct iouser * user, struct node * dir,
+                     mode_t mode, struct node ** node)
+{
+  return EOPNOTSUPP;
+}
+
+/* Read the contents of NODE (a symlink), for USER, into BUF. */
+error_t
+netfs_attempt_readlink (struct iouser * user, struct node * node, char *buf)
+{
+  return EOPNOTSUPP;
+}
+
+/* Read from the file NODE for user CRED starting at OFFSET and continuing for
+   up to *LEN bytes.  Put the data at DATA.  Set *LEN to the amount
+   successfully read upon return.  */
+error_t
+netfs_attempt_read (struct iouser * cred, struct node * node,
+                   off_t offset, size_t * len, void *data)
+{
+  error_t err;
+
+  if (!strncmp (node->nn->ln->name, FILE_CONFIG_NAME, NAME_SIZE))
+    {
+      err =
+       io_config_file (node->nn->ln->device, offset, len, data,
+                       pci_sys->read);
+      if (!err)
+       /* Update atime */
+       UPDATE_TIMES (node->nn->ln, TOUCH_ATIME);
+    }
+  else if (!strncmp (node->nn->ln->name, FILE_ROM_NAME, NAME_SIZE))
+    {
+      err = read_rom_file (node->nn->ln->device, offset, len, data);
+      if (!err)
+       /* Update atime */
+       UPDATE_TIMES (node->nn->ln, TOUCH_ATIME);
+    }
+  else if (!strncmp
+          (node->nn->ln->name, FILE_REGION_NAME, strlen (FILE_REGION_NAME)))
+    {
+      err = io_region_file (node->nn->ln, offset, len, data, 1);
+      if (!err)
+       /* Update atime */
+       UPDATE_TIMES (node->nn->ln, TOUCH_ATIME);
+    }
+  else
+    return EOPNOTSUPP;
+
+  return err;
+}
+
+/* Write to the file NODE for user CRED starting at OFSET and continuing for up
+   to *LEN bytes from DATA.  Set *LEN to the amount seccessfully written upon
+   return. */
+error_t
+netfs_attempt_write (struct iouser * cred, struct node * node,
+                    off_t offset, size_t * len, void *data)
+{
+  error_t err;
+
+  if (!strncmp (node->nn->ln->name, FILE_CONFIG_NAME, NAME_SIZE))
+    {
+      err =
+       io_config_file (node->nn->ln->device, offset, len, data,
+                       pci_sys->write);
+      if (!err)
+       {
+         /* Update mtime and ctime */
+         UPDATE_TIMES (node->nn->ln, TOUCH_MTIME | TOUCH_CTIME);
+       }
+    }
+  else if (!strncmp
+          (node->nn->ln->name, FILE_REGION_NAME, strlen (FILE_REGION_NAME)))
+    {
+      err = io_region_file (node->nn->ln, offset, len, data, 0);
+      if (!err)
+       /* Update atime */
+       UPDATE_TIMES (node->nn->ln, TOUCH_MTIME | TOUCH_CTIME);
+    }
+  else
+    return EOPNOTSUPP;
+
+  return err;
+}
+
+/* Node NP is all done; free all its associated storage. */
+void
+netfs_node_norefs (struct node *node)
+{
+  destroy_node (node);
+}
diff --git a/pci-arbiter/netfs_impl.h b/pci-arbiter/netfs_impl.h
new file mode 100644
index 00000000..b5d06e3c
--- /dev/null
+++ b/pci-arbiter/netfs_impl.h
@@ -0,0 +1,43 @@
+/*
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+   This file is part of the GNU Hurd.
+
+   The GNU Hurd is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2, or (at
+   your option) any later version.
+
+   The GNU Hurd is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the GNU Hurd.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Data types required to create a directory tree using libnetfs */
+
+#ifndef NETFS_IMPL_H
+#define NETFS_IMPL_H
+
+#include <hurd/netfs.h>
+
+#include <pcifs.h>
+
+/*
+ * A netnode has a 1:1 relation with a generic libnetfs node. Hence, it's only
+ * purpose is to extend a generic node to add the new attributes our problem
+ * requires.
+ */
+struct netnode
+{
+  /* Light node */
+  struct pcifs_dirent *ln;
+
+  /* Position in the node cache.  */
+  struct node *ncache_next, *ncache_prev;
+};
+
+#endif /* NETFS_IMPL_H */
diff --git a/pci-arbiter/options.c b/pci-arbiter/options.c
new file mode 100644
index 00000000..3c51b8b5
--- /dev/null
+++ b/pci-arbiter/options.c
@@ -0,0 +1,291 @@
+/*
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+   Written by Miles Bader <miles@gnu.org>
+
+   This file is part of the GNU Hurd.
+
+   The GNU Hurd is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2, or (at
+   your option) any later version.
+
+   The GNU Hurd is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the GNU Hurd.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Fsysopts and command line option parsing */
+
+#include <options.h>
+
+#include <stdlib.h>
+#include <argp.h>
+#include <argz.h>
+#include <error.h>
+
+#include <pcifs.h>
+
+/* Fsysopts and command line option parsing */
+
+/* Adds an empty interface slot to H, and sets H's current interface to it, or
+   returns an error. */
+static error_t
+parse_hook_add_set (struct parse_hook *h)
+{
+  struct pcifs_perm *new = realloc (h->permsets,
+                                   (h->num_permsets +
+                                    1) * sizeof (struct pcifs_perm));
+  if (!new)
+    return ENOMEM;
+
+  h->permsets = new;
+  h->num_permsets++;
+  h->curset = new + h->num_permsets - 1;
+  h->curset->domain = -1;
+  h->curset->bus = -1;
+  h->curset->dev = -1;
+  h->curset->func = -1;
+  h->curset->d_class = -1;
+  h->curset->d_subclass = -1;
+  h->curset->uid = -1;
+  h->curset->gid = -1;
+
+  return 0;
+}
+
+/*
+ * Some options depend on other options to be valid. Check whether all
+ * dependences are met.
+ */
+static error_t
+check_options_validity (struct parse_hook *h)
+{
+  int i;
+  struct pcifs_perm *p;
+
+  for (p = h->permsets, i = 0; i < h->num_permsets; i++, p++)
+    {
+      if ((p->func >= 0 && p->dev < 0)
+         || (p->dev >= 0 && p->bus < 0)
+         || (p->bus >= 0 && p->domain < 0)
+         || (p->d_subclass >= 0 && p->d_class < 0)
+         || ((p->uid >= 0 || p->gid >= 0)
+             && (p->d_class < 0 && p->domain < 0)) || ((p->d_class >= 0
+                                                        || p->domain >= 0)
+                                                       && !(p->uid >= 0
+                                                            || p->gid >= 0)))
+       return EINVAL;
+    }
+
+  return 0;
+}
+
+/* Option parser */
+static error_t
+parse_opt (int opt, char *arg, struct argp_state *state)
+{
+  error_t err = 0;
+  struct parse_hook *h = state->hook;
+
+  /* Return _ERR from this routine */
+#define RETURN(_err)                          \
+  do { return _err; } while (0)
+
+  /* Print a parsing error message and (if exiting is turned off) return the
+     error code ERR.  */
+#define PERR(err, fmt, args...)               \
+  do { argp_error (state, fmt , ##args); RETURN (err); } while (0)
+
+  /* Like PERR but for non-parsing errors.  */
+#define FAIL(rerr, status, perr, fmt, args...)  \
+  do{ argp_failure (state, status, perr, fmt , ##args); RETURN (rerr); } 
while(0)
+
+  /* Parse STR and return the corresponding  internet address.  If STR is not
+     a valid internet address, signal an error mentioned TYPE.  */
+#undef ADDR
+#define ADDR(str, type)                         \
+  ({ unsigned long addr = inet_addr (str);      \
+     if (addr == INADDR_NONE) PERR (EINVAL, "Malformed %s", type);  \
+     addr; })
+
+  if (!arg && state->next < state->argc && (*state->argv[state->next] != '-'))
+    {
+      arg = state->argv[state->next];
+      state->next++;
+    }
+
+  switch (opt)
+    {
+    case 'C':
+      /* Init a new set if the current one already has a value for this option 
*/
+      if (h->curset->d_class >= 0)
+       parse_hook_add_set (h);
+
+      h->curset->d_class = strtol (arg, 0, 16);
+      break;
+    case 's':
+      h->curset->d_subclass = strtol (arg, 0, 16);
+      break;
+    case 'D':
+      if (h->curset->domain >= 0)
+       parse_hook_add_set (h);
+
+      h->curset->domain = strtol (arg, 0, 16);
+      break;
+    case 'b':
+      h->curset->bus = strtol (arg, 0, 16);
+      break;
+    case 'd':
+      h->curset->dev = strtol (arg, 0, 16);
+      break;
+    case 'f':
+      h->curset->func = strtol (arg, 0, 16);
+      break;
+    case 'U':
+      if (h->curset->uid >= 0)
+       parse_hook_add_set (h);
+
+      h->curset->uid = atoi (arg);
+      break;
+    case 'G':
+      if (h->curset->gid >= 0)
+       parse_hook_add_set (h);
+
+      h->curset->gid = atoi (arg);
+      break;
+    case 'n':
+      h->ncache_len = atoi (arg);
+      break;
+    case ARGP_KEY_INIT:
+      /* Initialize our parsing state.  */
+      h = malloc (sizeof (struct parse_hook));
+      if (!h)
+       FAIL (ENOMEM, 1, ENOMEM, "option parsing");
+
+      h->permsets = 0;
+      h->num_permsets = 0;
+      h->ncache_len = NODE_CACHE_MAX;
+      err = parse_hook_add_set (h);
+      if (err)
+       FAIL (err, 1, err, "option parsing");
+
+      state->hook = h;
+      break;
+
+    case ARGP_KEY_SUCCESS:
+      /* Check option dependencies */
+      err = check_options_validity (h);
+      if (err)
+       {
+         if (fs->root)
+           {
+             /*
+              * If there's an option dependence error but the server is yet
+              * running, print the error and do nothing
+              */
+             error (0, err, "Invalid options, no changes were applied");
+             free (h->permsets);
+             free (h);
+             break;
+           }
+         else
+           /* Invalid options on a non-started server, exit() */
+           error (1, err, "Option dependence error");
+       }
+
+      /* Set permissions to FS */
+      if (fs->params.perms)
+       free (fs->params.perms);
+      fs->params.perms = h->permsets;
+      fs->params.num_perms = h->num_permsets;
+
+      /* Set cache len */
+      fs->params.node_cache_max = h->ncache_len;
+
+      if (fs->root)
+       {
+         /*
+          * FS is already initialized, that means we've been called by 
fsysopts.
+          * Update permissions.
+          */
+
+         /* Don't accept new RPCs during this process */
+         err = ports_inhibit_all_rpcs ();
+         if (err)
+           return err;
+
+         err = fs_set_permissions (fs);
+
+         /* Accept RPCs again */
+         ports_resume_all_rpcs ();
+       }
+
+      /* Free the hook */
+      free (h);
+
+      break;
+
+    case ARGP_KEY_ERROR:
+      /* Parsing error occurred, free the permissions. */
+      free (h->permsets);
+      free (h);
+      break;
+
+    default:
+      return ARGP_ERR_UNKNOWN;
+    }
+
+  return err;
+}
+
+/*
+ * Print current permissions. Called by fsysopts.
+ */
+error_t
+netfs_append_args (char **argz, size_t * argz_len)
+{
+  error_t err = 0;
+  struct pcifs_perm *p;
+  int i;
+
+#define ADD_OPT(fmt, args...)           \
+  do { char buf[100];                   \
+       if (! err) {                     \
+         snprintf (buf, sizeof buf, fmt , ##args);      \
+         err = argz_add (argz, argz_len, buf); } } while (0)
+
+  for (i = 0, p = fs->params.perms; i < fs->params.num_perms; i++, p++)
+    {
+      if (p->d_class >= 0)
+       ADD_OPT ("--class=0x%02x", p->d_class);
+      if (p->d_subclass >= 0)
+       ADD_OPT ("--subclass=0x%02x", p->d_subclass);
+      if (p->domain >= 0)
+       ADD_OPT ("--domain=0x%04x", p->domain);
+      if (p->bus >= 0)
+       ADD_OPT ("--bus=0x%02x", p->bus);
+      if (p->dev >= 0)
+       ADD_OPT ("--dev=0x%02x", p->dev);
+      if (p->func >= 0)
+       ADD_OPT ("--func=%01u", p->func);
+      if (p->uid >= 0)
+       ADD_OPT ("--uid=%u", p->uid);
+      if (p->gid >= 0)
+       ADD_OPT ("--gid=%u", p->gid);
+    }
+
+  if (fs->params.node_cache_max != NODE_CACHE_MAX)
+    ADD_OPT ("--ncache=%u", fs->params.node_cache_max);
+
+#undef ADD_OPT
+  return err;
+}
+
+struct argp pci_argp = { options, parse_opt, 0, doc };
+
+struct argp *netfs_runtime_argp = &pci_argp;
diff --git a/pci-arbiter/options.h b/pci-arbiter/options.h
new file mode 100644
index 00000000..ddab0023
--- /dev/null
+++ b/pci-arbiter/options.h
@@ -0,0 +1,74 @@
+/*
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+   Written by Miles Bader <miles@gnu.org>
+
+   This file is part of the GNU Hurd.
+
+   The GNU Hurd is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2, or (at
+   your option) any later version.
+
+   The GNU Hurd is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the GNU Hurd.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Fsysopts and command line option parsing */
+
+#ifndef OPTIONS_H
+#define OPTIONS_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <argp.h>
+
+#include <pcifs.h>
+
+#define STR2(x)  #x
+#define STR(x)  STR2(x)
+
+/* Used to hold data during argument parsing.  */
+struct parse_hook
+{
+  /* A list of specified permission sets and their corresponding options.  */
+  struct pcifs_perm *permsets;
+  size_t num_permsets;
+
+  /* Permission set to which options apply.  */
+  struct pcifs_perm *curset;
+
+  /* Node cache length */
+  size_t ncache_len;
+};
+
+/* Lwip translator options.  Used for both startup and runtime.  */
+static const struct argp_option options[] = {
+  {0, 0, 0, 0, "Permission scope:", 1},
+  {"class", 'C', "CLASS", 0, "Device class in hexadecimal"},
+  {"subclass", 's', "SUBCLASS", 0,
+   "Device subclass in hexadecimal, only valid with -c"},
+  {"domain", 'D', "DOMAIN", 0, "Device domain in hexadecimal"},
+  {"bus", 'b', "BUS", 0, "Device bus in hexadecimal, only valid with -D"},
+  {"dev", 'd', "DEV", 0, "Device dev in hexadecimal, only valid with -b"},
+  {"func", 'f', "FUNC", 0, "Device func in hexadecimal, only valid with -d"},
+  {0, 0, 0, 0, "These apply to a given permission scope:", 2},
+  {"uid", 'U', "UID", 0, "User ID to give permissions to"},
+  {"gid", 'G', "GID", 0, "Group ID to give permissions to"},
+  {0, 0, 0, 0, "Global configuration options:", 3},
+  {"ncache", 'n', "LENGTH", 0,
+   "Node cache length. " STR (NODE_CACHE_MAX) " by default"},
+  {0}
+};
+
+static const char doc[] = "More than one permission scope may be specified. \
+Uppercase options create a new permission scope if the current one already \
+has a value for that option. If one node is covered by more than one \
+permission scope, only the first permission is applied to that node.";
+
+#endif // OPTIONS_H
diff --git a/pci-arbiter/pci-ops.c b/pci-arbiter/pci-ops.c
new file mode 100644
index 00000000..9a2080d3
--- /dev/null
+++ b/pci-arbiter/pci-ops.c
@@ -0,0 +1,273 @@
+/*
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+   This file is part of the GNU Hurd.
+
+   The GNU Hurd is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2, or (at
+   your option) any later version.
+
+   The GNU Hurd is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the GNU Hurd.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Implementation of PCI operations */
+
+#include <pci_S.h>
+
+#include <fcntl.h>
+#include <hurd/netfs.h>
+#include <sys/mman.h>
+
+#include <pci_access.h>
+#include <pcifs.h>
+#include <func_files.h>
+
+static error_t
+check_permissions (struct protid *master, int flags)
+{
+  error_t err = 0;
+  struct node *node;
+  struct pcifs_dirent *e;
+
+  node = master->po->np;
+  e = node->nn->ln;
+
+  /* Check wheter the user has permissions to access this node */
+  err = entry_check_perms (master->user, e, flags);
+  if (err)
+    return err;
+
+  /* Check wheter the request has been sent to the proper node */
+  if (e->domain != 0           /* Only domain 0 can be accessed by I/O ports */
+      || e->bus < 0 || e->dev < 0 || e->func < 0)
+    err = EINVAL;
+
+  return err;
+}
+
+static size_t
+calculate_ndevs (struct iouser *user)
+{
+  size_t ndevs = 0;
+  int i;
+  struct pcifs_dirent *e;
+
+  for (i = 0, e = fs->entries; i < fs->num_entries; i++, e++)
+    {
+      if (e->func < 0          /* Skip entries without a full address  */
+         || !S_ISDIR (e->stat.st_mode))        /* and entries that are not 
folders     */
+       continue;
+
+      if (!entry_check_perms (user, e, O_READ))
+       /* If no error, user may access this device */
+       ndevs++;
+    }
+
+  return ndevs;
+}
+
+/*
+ * Read min(amount,*datalen) bytes and store them on `*data'.
+ *
+ * `*datalen' is updated.
+ */
+error_t
+S_pci_conf_read (struct protid * master, int reg, char **data,
+                size_t * datalen, mach_msg_type_number_t amount)
+{
+  error_t err;
+  pthread_mutex_t *lock;
+  struct pcifs_dirent *e;
+
+  if (!master)
+    return EOPNOTSUPP;
+
+  e = master->po->np->nn->ln;
+  if (strncmp (e->name, FILE_CONFIG_NAME, NAME_SIZE))
+    /* This operation may only be addressed to the config file */
+    return EINVAL;
+
+  lock = &fs->pci_conf_lock;
+
+  err = check_permissions (master, O_READ);
+  if (err)
+    return err;
+
+  /*
+   * We don't allocate new memory since we expect no more than 4 bytes-long
+   * buffers. Instead, we just take the lower value as length.
+   */
+  if (amount > *datalen)
+    amount = *datalen;
+
+  /*
+   * The server is not single-threaded anymore. Incoming rpcs are handled by
+   * libnetfs which is multi-threaded. A lock is needed for arbitration.
+   */
+  pthread_mutex_lock (lock);
+  err = pci_sys->read (e->bus, e->dev, e->func, reg, *data, amount);
+  pthread_mutex_unlock (lock);
+
+  if (!err)
+    {
+      *datalen = amount;
+      /* Update atime */
+      UPDATE_TIMES (e, TOUCH_ATIME);
+    }
+
+  return err;
+}
+
+/* Write `datalen' bytes from `data'. `amount' is updated. */
+error_t
+S_pci_conf_write (struct protid * master, int reg, char *data, size_t datalen,
+                 mach_msg_type_number_t * amount)
+{
+  error_t err;
+  pthread_mutex_t *lock;
+  struct pcifs_dirent *e;
+
+  if (!master)
+    return EOPNOTSUPP;
+
+  e = master->po->np->nn->ln;
+  if (strncmp (e->name, FILE_CONFIG_NAME, NAME_SIZE))
+    /* This operation may only be addressed to the config file */
+    return EINVAL;
+
+  lock = &fs->pci_conf_lock;
+
+  err = check_permissions (master, O_WRITE);
+  if (err)
+    return err;
+
+  pthread_mutex_lock (lock);
+  err = pci_sys->write (e->bus, e->dev, e->func, reg, data, datalen);
+  pthread_mutex_unlock (lock);
+
+  if (!err)
+    {
+      *amount = datalen;
+      /* Update mtime and ctime */
+      UPDATE_TIMES (e, TOUCH_MTIME | TOUCH_CTIME);
+    }
+
+  return err;
+}
+
+/* Write in `amount' the number of devices allowed for the user. */
+error_t
+S_pci_get_ndevs (struct protid * master, mach_msg_type_number_t * amount)
+{
+  /* This RPC may only be addressed to the root node */
+  if (master->po->np != fs->root)
+    return EINVAL;
+
+  *amount = calculate_ndevs (master->user);
+
+  return 0;
+}
+
+/*
+ * Return in `data' the information about the available memory
+ * regions in the given device.
+ */
+error_t
+S_pci_get_dev_regions (struct protid * master, char **data, size_t * datalen)
+{
+  error_t err;
+  struct pcifs_dirent *e;
+  struct pci_bar regions[6], *r;
+  size_t size;
+  int i;
+
+  if (!master)
+    return EOPNOTSUPP;
+
+  e = master->po->np->nn->ln;
+  if (strncmp (e->name, FILE_CONFIG_NAME, NAME_SIZE))
+    /* This operation may only be addressed to the config file */
+    return EINVAL;
+
+  err = check_permissions (master, O_READ);
+  if (err)
+    return err;
+
+  /* Allocate memory if needed */
+  size = sizeof (regions);
+  if (size > *datalen)
+    {
+      *data = mmap (0, size, PROT_READ | PROT_WRITE, MAP_ANON, 0, 0);
+      if (*data == MAP_FAILED)
+       return ENOMEM;
+    }
+
+  /* Copy the regions info */
+  for (i = 0, r = (struct pci_bar *) *data; i < 6; i++, r++)
+    {
+      r->base_addr = e->device->regions[i].base_addr;
+      r->size = e->device->regions[i].size;
+      r->is_IO = e->device->regions[i].is_IO;
+      r->is_prefetchable = e->device->regions[i].is_prefetchable;
+      r->is_64 = e->device->regions[i].is_64;
+    }
+
+  /* Update atime */
+  UPDATE_TIMES (e, TOUCH_ATIME);
+
+  *datalen = size;
+
+  return 0;
+}
+
+/*
+ * Return in `data' the information about the expansion rom of the given device
+ */
+error_t
+S_pci_get_dev_rom (struct protid * master, char **data, size_t * datalen)
+{
+  error_t err;
+  struct pcifs_dirent *e;
+  struct pci_xrom_bar rom;
+  size_t size;
+
+  if (!master)
+    return EOPNOTSUPP;
+
+  e = master->po->np->nn->ln;
+  if (strncmp (e->name, FILE_CONFIG_NAME, NAME_SIZE))
+    /* This operation may only be addressed to the config file */
+    return EINVAL;
+
+  err = check_permissions (master, O_READ);
+  if (err)
+    return err;
+
+  /* Allocate memory if needed */
+  size = sizeof (rom);
+  if (size > *datalen)
+    {
+      *data = mmap (0, size, PROT_READ | PROT_WRITE, MAP_ANON, 0, 0);
+      if (*data == MAP_FAILED)
+       return ENOMEM;
+    }
+
+  /* Copy the regions info */
+  rom.base_addr = e->device->rom_base;
+  rom.size = e->device->rom_size;
+  memcpy (*data, &rom, size);
+
+  /* Update atime */
+  UPDATE_TIMES (e, TOUCH_ATIME);
+
+  *datalen = size;
+
+  return 0;
+}
diff --git a/pci-arbiter/pci_access.c b/pci-arbiter/pci_access.c
new file mode 100644
index 00000000..eded1bff
--- /dev/null
+++ b/pci-arbiter/pci_access.c
@@ -0,0 +1,51 @@
+/*
+ * (C) Copyright IBM Corporation 2006
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * on the rights to use, copy, modify, merge, publish, distribute, sub
+ * license, and/or sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
+ * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * PCI access general header.
+ *
+ * Following code is borrowed from libpciaccess:
+ * https://cgit.freedesktop.org/xorg/lib/libpciaccess/
+ */
+
+#include <pci_access.h>
+
+#include <errno.h>
+
+#include <x86_pci.h>
+
+/* Configure PCI parameters */
+int
+pci_system_init (void)
+{
+  int err = ENOSYS;
+
+#ifdef __GNU__
+  err = pci_system_x86_create ();
+#else
+#error "Unsupported OS"
+#endif
+
+  return err;
+}
diff --git a/pci-arbiter/pci_access.h b/pci-arbiter/pci_access.h
new file mode 100644
index 00000000..1a0b22ac
--- /dev/null
+++ b/pci-arbiter/pci_access.h
@@ -0,0 +1,182 @@
+/*
+ * (C) Copyright IBM Corporation 2006
+ * Copyright 2009 Red Hat, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * on the rights to use, copy, modify, merge, publish, distribute, sub
+ * license, and/or sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
+ * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+/*
+ * Copyright (c) 2007 Paulo R. Zanoni, Tiago Vignatti
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+/*
+ * PCI access general header.
+ *
+ * Following code is borrowed from libpciaccess:
+ * https://cgit.freedesktop.org/xorg/lib/libpciaccess/
+ */
+
+#ifndef PCI_ACCESS_H
+#define PCI_ACCESS_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <errno.h>
+
+typedef uint64_t pciaddr_t;
+
+/*
+ * BAR descriptor for a PCI device.
+ */
+struct pci_mem_region
+{
+  /*
+   * When the region is mapped, this is the pointer to the memory.
+   */
+  void *memory;
+
+  /*
+   * Base physical address of the region from the CPU's point of view.
+   *
+   * This address is typically passed to \c pci_device_map_range to create
+   * a mapping of the region to the CPU's virtual address space.
+   */
+  pciaddr_t base_addr;
+
+
+  /*
+   * Size, in bytes, of the region.
+   */
+  pciaddr_t size;
+
+
+  /*
+   * Is the region I/O ports or memory?
+   */
+  unsigned is_IO:1;
+
+  /*
+   * Is the memory region prefetchable?
+   *
+   * \note
+   * This can only be set if \c is_IO is not set.
+   */
+  unsigned is_prefetchable:1;
+
+
+  /*
+   * Is the memory at a 64-bit address?
+   *
+   * \note
+   * This can only be set if \c is_IO is not set.
+   */
+  unsigned is_64:1;
+};
+
+/*
+ * PCI device.
+ *
+ * Contains all of the information about a particular PCI device.
+ */
+struct pci_device
+{
+  /*
+   * Complete bus identification, including domain, of the device.  On
+   * platforms that do not support PCI domains (e.g., 32-bit x86 hardware),
+   * the domain will always be zero.
+   */
+  uint16_t domain;
+  uint8_t bus;
+  uint8_t dev;
+  uint8_t func;
+
+  /*
+   * Device's class, subclass, and programming interface packed into a
+   * single 32-bit value.  The class is at bits [23:16], subclass is at
+   * bits [15:8], and programming interface is at [7:0].
+   */
+  uint32_t device_class;
+
+  /*
+   * BAR descriptors for the device.
+   */
+  struct pci_mem_region regions[6];
+
+  /*
+   * Size, in bytes, of the device's expansion ROM.
+   */
+  pciaddr_t rom_size;
+
+  /*
+   * Physical address of the ROM
+   */
+  pciaddr_t rom_base;
+
+  /*
+   * Mapped ROM
+   */
+  void *rom_memory;
+};
+
+typedef error_t (*pci_io_op_t) (unsigned bus, unsigned dev, unsigned func,
+                               pciaddr_t reg, void *data, unsigned size);
+
+typedef error_t (*pci_refresh_dev_op_t) (struct pci_device * dev,
+                                        int num_region, int rom);
+
+/* Global PCI data */
+struct pci_system
+{
+  size_t num_devices;
+  struct pci_device *devices;
+
+  /* Callbacks */
+  pci_io_op_t read;
+  pci_io_op_t write;
+  pci_refresh_dev_op_t device_refresh;
+};
+
+struct pci_system *pci_sys;
+
+int pci_system_init (void);
+
+#endif /* PCI_ACCESS_H */
diff --git a/pci-arbiter/pcifs.c b/pci-arbiter/pcifs.c
new file mode 100644
index 00000000..f005e5a5
--- /dev/null
+++ b/pci-arbiter/pcifs.c
@@ -0,0 +1,450 @@
+/*
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+   This file is part of the GNU Hurd.
+
+   The GNU Hurd is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2, or (at
+   your option) any later version.
+
+   The GNU Hurd is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the GNU Hurd.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* PCI Filesystem implementation */
+
+#include <pcifs.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <hurd/netfs.h>
+
+#include <ncache.h>
+#include <func_files.h>
+
+static error_t
+create_dir_entry (int32_t domain, int16_t bus, int16_t dev,
+                 int16_t func, int32_t device_class, char *name,
+                 struct pcifs_dirent *parent, io_statbuf_t stat,
+                 struct node *node, struct pci_device *device,
+                 struct pcifs_dirent *entry)
+{
+  uint16_t parent_num_entries;
+
+  entry->domain = domain;
+  entry->bus = bus;
+  entry->dev = dev;
+  entry->func = func;
+  entry->device_class = device_class;
+  strncpy (entry->name, name, NAME_SIZE);
+  entry->parent = parent;
+  entry->stat = stat;
+  entry->dir = 0;
+  entry->node = node;
+  entry->device = device;
+
+  /* Update parent's child list */
+  if (entry->parent)
+    {
+      if (!entry->parent->dir)
+       {
+         /* First child */
+         entry->parent->dir = calloc (1, sizeof (struct pcifs_dir));
+         if (!entry->parent->dir)
+           return ENOMEM;
+       }
+
+      parent_num_entries = entry->parent->dir->num_entries++;
+      entry->parent->dir->entries = realloc (entry->parent->dir->entries,
+                                            entry->parent->dir->num_entries *
+                                            sizeof (struct pcifs_dirent *));
+      if (!entry->parent->dir->entries)
+       return ENOMEM;
+      entry->parent->dir->entries[parent_num_entries] = entry;
+    }
+
+  return 0;
+}
+
+error_t
+alloc_file_system (struct pcifs ** fs)
+{
+  *fs = calloc (1, sizeof (struct pcifs));
+  if (!*fs)
+    return ENOMEM;
+
+  return 0;
+}
+
+error_t
+init_file_system (file_t underlying_node, struct pcifs * fs)
+{
+  error_t err;
+  struct node *np;
+  io_statbuf_t underlying_node_stat;
+
+  /* Initialize status from underlying node.  */
+  err = io_stat (underlying_node, &underlying_node_stat);
+  if (err)
+    return err;
+
+  np = netfs_make_node_alloc (sizeof (struct netnode));
+  if (!np)
+    return ENOMEM;
+  np->nn_stat = underlying_node_stat;
+  np->nn_stat.st_fsid = getpid ();
+  np->nn_stat.st_mode = S_IFDIR | S_IROOT
+    | (underlying_node_stat.st_mode & ~S_IFMT & ~S_ITRANS);
+  np->nn_translated = np->nn_stat.st_mode;
+
+  /* If the underlying node isn't a directory, enhance the stat
+     information.  */
+  if (!S_ISDIR (underlying_node_stat.st_mode))
+    {
+      if (underlying_node_stat.st_mode & S_IRUSR)
+       np->nn_stat.st_mode |= S_IXUSR;
+      if (underlying_node_stat.st_mode & S_IRGRP)
+       np->nn_stat.st_mode |= S_IXGRP;
+      if (underlying_node_stat.st_mode & S_IROTH)
+       np->nn_stat.st_mode |= S_IXOTH;
+    }
+
+  /* Remove write permissions to others */
+  np->nn_stat.st_mode &= ~S_IWOTH;
+
+  /* Set times to now */
+  fshelp_touch (&np->nn_stat, TOUCH_ATIME | TOUCH_MTIME | TOUCH_CTIME,
+               pcifs_maptime);
+
+  fs->entries = calloc (1, sizeof (struct pcifs_dirent));
+  if (!fs->entries)
+    {
+      free (fs->entries);
+      return ENOMEM;
+    }
+
+  /* Create the root entry */
+  err =
+    create_dir_entry (-1, -1, -1, -1, -1, "", 0, np->nn_stat, np, 0,
+                     fs->entries);
+
+  fs->num_entries = 1;
+  fs->root = netfs_root_node = np;
+  fs->root->nn->ln = fs->entries;
+  pthread_mutex_init (&fs->node_cache_lock, 0);
+  pthread_mutex_init (&fs->pci_conf_lock, 0);
+
+  return 0;
+}
+
+error_t
+create_fs_tree (struct pcifs * fs, struct pci_system * pci_sys)
+{
+  error_t err = 0;
+  int c_domain, c_bus, c_dev, i, j;
+  size_t nentries;
+  struct pci_device *device;
+  struct pcifs_dirent *e, *domain_parent, *bus_parent, *dev_parent,
+    *func_parent, *list;
+  struct stat e_stat;
+  char entry_name[NAME_SIZE];
+
+  nentries = 2;                        /* Skip root and domain entries */
+  c_bus = c_dev = -1;
+  for (i = 0, device = pci_sys->devices; i < pci_sys->num_devices;
+       i++, device++)
+    {
+      if (device->bus != c_bus)
+       {
+         c_bus = device->bus;
+         c_dev = -1;
+         nentries++;
+       }
+
+      if (device->dev != c_dev)
+       {
+         c_dev = device->dev;
+         nentries++;
+       }
+
+      nentries += 2;           /* func dir + config */
+
+      for (j = 0; j < 6; j++)
+       if (device->regions[j].size > 0)
+         nentries++;           /* + memory region */
+
+      if (device->rom_size)
+       nentries++;             /* + rom */
+    }
+
+  list = realloc (fs->entries, nentries * sizeof (struct pcifs_dirent));
+  if (!list)
+    return ENOMEM;
+
+  e = list + 1;
+  e_stat = list->stat;
+  e_stat.st_mode &= ~S_IROOT;  /* Remove the root mode */
+  c_domain = c_bus = c_dev = -1;
+  domain_parent = bus_parent = dev_parent = func_parent = 0;
+  for (i = 0, device = pci_sys->devices; i < pci_sys->num_devices;
+       i++, device++)
+    {
+      if (device->domain != c_domain)
+       {
+         /* We've found a new domain. Add an entry for it */
+         memset (entry_name, 0, NAME_SIZE);
+         snprintf (entry_name, NAME_SIZE, "%04x", device->domain);
+         err =
+           create_dir_entry (device->domain, -1, -1, -1, -1, entry_name,
+                             list, e_stat, 0, 0, e);
+         if (err)
+           return err;
+
+         /* Switch to bus level */
+         domain_parent = e++;
+         c_domain = device->domain;
+         c_bus = -1;
+         c_dev = -1;
+       }
+
+      if (device->bus != c_bus)
+       {
+         /* We've found a new bus. Add an entry for it */
+         memset (entry_name, 0, NAME_SIZE);
+         snprintf (entry_name, NAME_SIZE, "%02x", device->bus);
+         err =
+           create_dir_entry (device->domain, device->bus, -1, -1, -1,
+                             entry_name, domain_parent, domain_parent->stat,
+                             0, 0, e);
+         if (err)
+           return err;
+
+         /* Switch to dev level */
+         bus_parent = e++;
+         c_bus = device->bus;
+         c_dev = -1;
+       }
+
+      if (device->dev != c_dev)
+       {
+         /* We've found a new dev. Add an entry for it */
+         memset (entry_name, 0, NAME_SIZE);
+         snprintf (entry_name, NAME_SIZE, "%02x", device->dev);
+         err =
+           create_dir_entry (device->domain, device->bus, device->dev, -1,
+                             -1, entry_name, bus_parent, bus_parent->stat, 0,
+                             0, e);
+         if (err)
+           return err;
+
+         /* Switch to func level */
+         dev_parent = e++;
+         c_dev = device->dev;
+       }
+
+      /* Remove all permissions to others */
+      e_stat = dev_parent->stat;
+      e_stat.st_mode &= ~(S_IROTH | S_IWOTH | S_IXOTH);
+
+      /* Add func entry */
+      memset (entry_name, 0, NAME_SIZE);
+      snprintf (entry_name, NAME_SIZE, "%01u", device->func);
+      err =
+       create_dir_entry (device->domain, device->bus, device->dev,
+                         device->func, device->device_class, entry_name,
+                         dev_parent, e_stat, 0, device, e);
+      if (err)
+       return err;
+
+      /* Switch to the lowest level */
+      func_parent = e++;
+
+      /* Change mode to a regular file */
+      e_stat = func_parent->stat;
+      e_stat.st_mode &= ~(S_IFDIR | S_IXUSR | S_IXGRP);
+      e_stat.st_size = FILE_CONFIG_SIZE;
+
+      /* Create config entry */
+      strncpy (entry_name, FILE_CONFIG_NAME, NAME_SIZE);
+      err =
+       create_dir_entry (device->domain, device->bus, device->dev,
+                         device->func, device->device_class, entry_name,
+                         func_parent, e_stat, 0, device, e++);
+      if (err)
+       return err;
+
+      /* Create regions entries */
+      for (j = 0; j < 6; j++)
+       {
+         if (device->regions[j].size > 0)
+           {
+             e_stat.st_size = device->regions[j].size;
+             snprintf (entry_name, NAME_SIZE, "%s%01u", FILE_REGION_NAME, j);
+             err =
+               create_dir_entry (device->domain, device->bus, device->dev,
+                                 device->func, device->device_class,
+                                 entry_name, func_parent, e_stat, 0, device,
+                                 e++);
+             if (err)
+               return err;
+           }
+       }
+
+      /* Create rom entry */
+      if (device->rom_size)
+       {
+         /* Make rom is read only */
+         e_stat.st_mode &= ~(S_IWUSR | S_IWGRP);
+         e_stat.st_size = device->rom_size;
+         strncpy (entry_name, FILE_ROM_NAME, NAME_SIZE);
+         err =
+           create_dir_entry (device->domain, device->bus, device->dev,
+                             device->func, device->device_class, entry_name,
+                             func_parent, e_stat, 0, device, e++);
+         if (err)
+           return err;
+       }
+    }
+
+  /* The root node points to the first element of the entry list */
+  fs->entries = list;
+  fs->num_entries = nentries;
+  fs->root->nn->ln = fs->entries;
+
+  return err;
+}
+
+error_t
+entry_check_perms (struct iouser * user, struct pcifs_dirent * e, int flags)
+{
+  error_t err = 0;
+
+  if (!err && (flags & O_READ))
+    err = fshelp_access (&e->stat, S_IREAD, user);
+  if (!err && (flags & O_WRITE))
+    err = fshelp_access (&e->stat, S_IWRITE, user);
+  if (!err && (flags & O_EXEC))
+    err = fshelp_access (&e->stat, S_IEXEC, user);
+
+  return err;
+}
+
+/* Set default permissions to the given entry */
+static void
+entry_default_perms (struct pcifs *fs, struct pcifs_dirent *e)
+{
+  int i;
+  struct pcifs_perm *perms = fs->params.perms, *p;
+  size_t num_perms = fs->params.num_perms;
+
+  for (i = 0, p = perms; i < num_perms; i++, p++)
+    {
+      /* Set default owner and group */
+      UPDATE_OWNER (e, fs->root->nn->ln->stat.st_uid);
+      UPDATE_GROUP (e, fs->root->nn->ln->stat.st_gid);
+
+      /* Update ctime */
+      UPDATE_TIMES (e, TOUCH_CTIME);
+    }
+
+  return;
+}
+
+static void
+entry_set_perms (struct pcifs *fs, struct pcifs_dirent *e)
+{
+  int i;
+  struct pcifs_perm *perms = fs->params.perms, *p;
+  size_t num_perms = fs->params.num_perms;
+
+  for (i = 0, p = perms; i < num_perms; i++, p++)
+    {
+      uint8_t e_class = e->device_class >> 16;
+      uint8_t e_subclass = ((e->device_class >> 8) & 0xFF);
+
+      /* Check whether the entry is convered by this permission scope */
+      if (p->d_class >= 0 && e_class != p->d_class)
+       continue;
+      if (p->d_subclass >= 0 && e_subclass != p->d_subclass)
+       continue;
+      if (p->domain >= 0 && p->domain != e->domain)
+       continue;
+      if (p->bus >= 0 && e->bus != p->bus)
+       continue;
+      if (p->dev >= 0 && e->dev != p->dev)
+       continue;
+      if (p->func >= 0 && e->func != p->func)
+       continue;
+
+      /* This permission set covers this entry */
+      if (p->uid >= 0)
+       UPDATE_OWNER (e, p->uid);
+      if (p->gid >= 0)
+       UPDATE_GROUP (e, p->gid);
+
+      /* Update ctime */
+      UPDATE_TIMES (e, TOUCH_CTIME);
+
+      /* Break, as only one permission set can cover each node */
+      break;
+    }
+
+  return;
+}
+
+/* Update all entries' permissions */
+error_t
+fs_set_permissions (struct pcifs * fs)
+{
+  int i;
+  struct pcifs_dirent *e;
+
+  for (i = 0, e = fs->entries; i < fs->num_entries; i++, e++)
+    {
+      /* Restore default perms, as this may be called from fsysopts */
+      entry_default_perms (fs, e);
+
+      /* Set new permissions, if any */
+      entry_set_perms (fs, e);
+    }
+
+  return 0;
+}
+
+error_t
+create_node (struct pcifs_dirent * e, struct node ** node)
+{
+  struct node *np;
+  struct netnode *nn;
+
+  np = netfs_make_node_alloc (sizeof (struct netnode));
+  if (!np)
+    return ENOMEM;
+  np->nn_stat = e->stat;
+  np->nn_translated = np->nn_stat.st_mode;
+
+  nn = netfs_node_netnode (np);
+  memset (nn, 0, sizeof (struct netnode));
+  nn->ln = e;
+
+  *node = e->node = np;
+
+  return 0;
+}
+
+void
+destroy_node (struct node *node)
+{
+  if (node->nn->ln)
+    node->nn->ln->node = 0;
+  free (node);
+}
diff --git a/pci-arbiter/pcifs.h b/pci-arbiter/pcifs.h
new file mode 100644
index 00000000..e83f4ee7
--- /dev/null
+++ b/pci-arbiter/pcifs.h
@@ -0,0 +1,210 @@
+/*
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   Written by Miles Bader <miles@gnu.org>
+
+   This file is part of the GNU Hurd.
+
+   The GNU Hurd is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2, or (at
+   your option) any later version.
+
+   The GNU Hurd is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the GNU Hurd.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* PCI Filesystem header */
+
+#ifndef PCIFS_H
+#define PCIFS_H
+
+#include <hurd/netfs.h>
+#include <pthread.h>
+#include <maptime.h>
+
+#include <pci_access.h>
+#include <netfs_impl.h>
+
+/* Size of a directory entry name */
+#ifndef NAME_SIZE
+#define NAME_SIZE 16
+#endif
+
+/* Node cache defaults size */
+#define NODE_CACHE_MAX 16
+
+/*
+ * Directory entry. Contains all per-node data our problem requires.
+ *
+ * All directory entries are created on startup and used to generate the
+ * fs tree and create or retrieve libnetfs node objects.
+ *
+ * From libnetfs' point of view, these are the light nodes.
+ */
+struct pcifs_dirent
+{
+  /*
+   * Complete bus identification, including domain, of the device.  On
+   * platforms that do not support PCI domains (e.g., 32-bit x86 hardware),
+   * the domain will always be zero.
+   *
+   * Negative value means no value.
+   */
+  int32_t domain;
+  int16_t bus;
+  int16_t dev;
+  int8_t func;
+
+  /*
+   * Device's class, subclass, and programming interface packed into a
+   * single 32-bit value.  The class is at bits [23:16], subclass is at
+   * bits [15:8], and programming interface is at [7:0].
+   *
+   * Negative value means no value.
+   */
+  int32_t device_class;
+
+  char name[NAME_SIZE];
+  struct pcifs_dirent *parent;
+  io_statbuf_t stat;
+
+  /*
+   * We only need two kind of nodes: files and directories.
+   * When `dir' is null, this is a file; when not null, it's a directory.
+   */
+  struct pcifs_dir *dir;
+
+  /* Active node on this entry */
+  struct node *node;
+
+  /*
+   * Underlying virtual device if any.
+   *
+   * Only for entries having a full B/D/F address.
+   */
+  struct pci_device *device;
+};
+
+/*
+ * A directory, it only contains a list of directory entries
+ */
+struct pcifs_dir
+{
+  /* Number of directory entries */
+  uint16_t num_entries;
+
+  /* Array of directory entries */
+  struct pcifs_dirent **entries;
+};
+
+/*
+ * Set of permissions.
+ *
+ * For each Class[,subclass] and/or Domain[,bus[,dev[,func]]], one UID and/or 
GID.
+ */
+struct pcifs_perm
+{
+  /*
+   * D/b/d/f scope of permissions.
+   *
+   * Negative value means no value.
+   */
+  int32_t domain;
+  int16_t bus;
+  int16_t dev;
+  int8_t func;
+
+  /*
+   * Class and subclass scope of permissions
+   *
+   * Negative value means no value.
+   */
+  int16_t d_class;
+  int16_t d_subclass;
+
+  /* User and group ids */
+  int32_t uid;
+  int32_t gid;
+};
+
+/* Various parameters that can be used to change the behavior of an ftpfs.  */
+struct pcifs_params
+{
+  /* The size of the node cache.  */
+  size_t node_cache_max;
+
+  /* FS permissions.  */
+  struct pcifs_perm *perms;
+  size_t num_perms;
+};
+
+/* A particular filesystem.  */
+struct pcifs
+{
+  /* Root of filesystem.  */
+  struct node *root;
+
+  /* FS configuration */
+  struct pcifs_params params;
+
+  /* A cache that holds a reference to recently used nodes.  */
+  struct node *node_cache_mru, *node_cache_lru;
+  size_t node_cache_len;       /* Number of entries in it.  */
+  pthread_mutex_t node_cache_lock;
+
+  /* Lock for pci_conf operations */
+  pthread_mutex_t pci_conf_lock;
+
+  struct pcifs_dirent *entries;
+  size_t num_entries;
+};
+
+/* Main FS pointer */
+struct pcifs *fs;
+
+/* Global mapped time */
+volatile struct mapped_time_value *pcifs_maptime;
+
+/* Update entry and node times */
+#define UPDATE_TIMES(e, what) (\
+  {\
+    fshelp_touch (&e->stat, what, pcifs_maptime);\
+    if(e->node)\
+      fshelp_touch (&e->node->nn_stat, what, pcifs_maptime);\
+  }\
+)
+
+/* Update entry and node owner */
+#define UPDATE_OWNER(e, uid) (\
+  {\
+    e->stat.st_uid = uid;\
+    if(e->node)\
+      e->node->nn_stat.st_uid = uid;\
+  }\
+)
+
+/* Update entry and node group */
+#define UPDATE_GROUP(e, gid) (\
+  {\
+    e->stat.st_gid = gid;\
+    if(e->node)\
+      e->node->nn_stat.st_gid = gid;\
+  }\
+)
+
+/* FS manipulation functions */
+error_t alloc_file_system (struct pcifs **fs);
+error_t init_file_system (file_t underlying_node, struct pcifs *fs);
+error_t create_fs_tree (struct pcifs *fs, struct pci_system *pci_sys);
+error_t fs_set_permissions (struct pcifs *fs);
+error_t entry_check_perms (struct iouser *user, struct pcifs_dirent *e,
+                          int flags);
+error_t create_node (struct pcifs_dirent *e, struct node **node);
+void destroy_node (struct node *node);
+
+#endif /* PCIFS_H */
diff --git a/pci-arbiter/startup-ops.c b/pci-arbiter/startup-ops.c
new file mode 100644
index 00000000..ff92fd8d
--- /dev/null
+++ b/pci-arbiter/startup-ops.c
@@ -0,0 +1,41 @@
+/*
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   Written by Michael I. Bushnell, p/BSG.
+
+   This file is part of the GNU Hurd.
+
+   The GNU Hurd is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2, or (at
+   your option) any later version.
+
+   The GNU Hurd is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the GNU Hurd.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <startup_notify_S.h>
+
+#include <hurd/netfs.h>
+
+#include <startup.h>
+
+/* The system is going down; destroy all the extant port rights.  That
+   will cause net channels and such to close promptly.  */
+error_t
+S_startup_dosync (mach_port_t handle)
+{
+  struct port_info *inpi = ports_lookup_port (netfs_port_bucket, handle,
+                                             pci_shutdown_notify_class);
+
+  if (!inpi)
+    return EOPNOTSUPP;
+
+  ports_port_deref (inpi);
+
+  return netfs_shutdown (FSYS_GOAWAY_FORCE);
+}
diff --git a/pci-arbiter/startup.c b/pci-arbiter/startup.c
new file mode 100644
index 00000000..56209f3d
--- /dev/null
+++ b/pci-arbiter/startup.c
@@ -0,0 +1,58 @@
+/*
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   Written by Michael I. Bushnell, p/BSG.
+
+   This file is part of the GNU Hurd.
+
+   The GNU Hurd is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2, or (at
+   your option) any later version.
+
+   The GNU Hurd is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the GNU Hurd.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Startup and shutdown notifications management */
+
+#include <startup.h>
+
+#include <unistd.h>
+#include <hurd/paths.h>
+#include <hurd/startup.h>
+#include <hurd/netfs.h>
+
+void
+arrange_shutdown_notification ()
+{
+  error_t err;
+  mach_port_t initport, notify;
+  struct port_info *pi;
+
+  pci_shutdown_notify_class = ports_create_class (0, 0);
+
+  /* Arrange to get notified when the system goes down,
+     but if we fail for some reason, just silently give up.  No big deal. */
+
+  err = ports_create_port (pci_shutdown_notify_class, netfs_port_bucket,
+                          sizeof (struct port_info), &pi);
+  if (err)
+    return;
+
+  initport = file_name_lookup (_SERVERS_STARTUP, 0, 0);
+  if (initport == MACH_PORT_NULL)
+    return;
+
+  notify = ports_get_send_right (pi);
+  ports_port_deref (pi);
+  startup_request_notification (initport, notify,
+                               MACH_MSG_TYPE_MAKE_SEND,
+                               program_invocation_short_name);
+  mach_port_deallocate (mach_task_self (), notify);
+  mach_port_deallocate (mach_task_self (), initport);
+}
diff --git a/pci-arbiter/startup.h b/pci-arbiter/startup.h
new file mode 100644
index 00000000..12746f3b
--- /dev/null
+++ b/pci-arbiter/startup.h
@@ -0,0 +1,31 @@
+/*
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   Written by Michael I. Bushnell, p/BSG.
+
+   This file is part of the GNU Hurd.
+
+   The GNU Hurd is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2, or (at
+   your option) any later version.
+
+   The GNU Hurd is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the GNU Hurd.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef STARTUP_H
+#define STARTUP_H
+
+/* Startup and shutdown notifications management */
+
+/* Port class for startup requests */
+struct port_class *pci_shutdown_notify_class;
+
+void arrange_shutdown_notification ();
+
+#endif /* STARTUP_H */
diff --git a/pci-arbiter/x86_pci.c b/pci-arbiter/x86_pci.c
new file mode 100644
index 00000000..09e202c1
--- /dev/null
+++ b/pci-arbiter/x86_pci.c
@@ -0,0 +1,869 @@
+/*
+ * Copyright (c) 2017 Joan Lledó
+ * Copyright (c) 2009, 2012 Samuel Thibault
+ * Heavily inspired from the freebsd, netbsd, and openbsd backends
+ * (C) Copyright Eric Anholt 2006
+ * (C) Copyright IBM Corporation 2006
+ * Copyright (c) 2008 Juan Romero Pardines
+ * Copyright (c) 2008 Mark Kettenis
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * PCI backend for x86 (32 and 64 bit) architectures.
+ *
+ * Following code is borrowed from libpciaccess:
+ * https://cgit.freedesktop.org/xorg/lib/libpciaccess/
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <x86_pci.h>
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/io.h>
+
+#include <pci_access.h>
+
+#define PCI_VENDOR(reg)                ((reg) & 0xFFFF)
+#define PCI_VENDOR_INVALID     0xFFFF
+
+#define PCI_VENDOR_ID          0x00
+#define PCI_VENDOR_ID_COMPAQ   0x0e11
+#define PCI_VENDOR_ID_INTEL    0x8086
+
+#define PCI_CLASS              0x08
+#define PCI_CLASS_DEVICE       0x0a
+#define PCI_CLASS_DISPLAY_VGA  0x0300
+#define PCI_CLASS_BRIDGE_HOST  0x0600
+
+#define PCI_BAR_ADDR_0         0x10
+#define PCI_XROMBAR_ADDR_00    0x30
+#define PCI_XROMBAR_ADDR_01    0x38
+
+#define PCI_HDRTYPE            0x0E
+#define PCI_HDRTYPE_DEVICE     0x00
+#define PCI_HDRTYPE_BRIDGE     0x01
+#define PCI_HDRTYPE_CARDBUS    0x02
+
+#define PCI_COMMAND            0x04
+#define PCI_SECONDARY_BUS      0x19
+
+static error_t
+x86_enable_io (void)
+{
+  if (!ioperm (0, 0xffff, 1))
+    return 0;
+  return errno;
+}
+
+static error_t
+x86_disable_io (void)
+{
+  if (!ioperm (0, 0xffff, 0))
+    return 0;
+  return errno;
+}
+
+static error_t
+pci_system_x86_conf1_probe (void)
+{
+  unsigned long sav;
+  int res = ENODEV;
+
+  outb (0x01, 0xCFB);
+  sav = inl (0xCF8);
+  outl (0x80000000, 0xCF8);
+  if (inl (0xCF8) == 0x80000000)
+    res = 0;
+  outl (sav, 0xCF8);
+
+  return res;
+}
+
+static error_t
+pci_system_x86_conf1_read (unsigned bus, unsigned dev, unsigned func,
+                          pciaddr_t reg, void *data, unsigned size)
+{
+  unsigned addr = 0xCFC + (reg & 3);
+  unsigned long sav;
+  error_t ret = 0;
+
+  if (bus >= 0x100 || dev >= 32 || func >= 8 || reg >= 0x100 || size > 4
+      || size == 3)
+    return EIO;
+
+  sav = inl (0xCF8);
+  outl (0x80000000 | (bus << 16) | (dev << 11) | (func << 8) | (reg & ~3),
+       0xCF8);
+  /* NOTE: x86 is already LE */
+  switch (size)
+    {
+    case 1:
+      {
+       uint8_t *val = data;
+       *val = inb (addr);
+       break;
+      }
+    case 2:
+      {
+       uint16_t *val = data;
+       *val = inw (addr);
+       break;
+      }
+    case 4:
+      {
+       uint32_t *val = data;
+       *val = inl (addr);
+       break;
+      }
+    }
+  outl (sav, 0xCF8);
+
+  return ret;
+}
+
+static error_t
+pci_system_x86_conf1_write (unsigned bus, unsigned dev, unsigned func,
+                           pciaddr_t reg, void *data, unsigned size)
+{
+  unsigned addr = 0xCFC + (reg & 3);
+  unsigned long sav;
+  error_t ret = 0;
+
+  if (bus >= 0x100 || dev >= 32 || func >= 8 || reg >= 0x100 || size > 4
+      || size == 3)
+    return EIO;
+
+  sav = inl (0xCF8);
+  outl (0x80000000 | (bus << 16) | (dev << 11) | (func << 8) | (reg & ~3),
+       0xCF8);
+  /* NOTE: x86 is already LE */
+  switch (size)
+    {
+    case 1:
+      {
+       const uint8_t *val = data;
+       outb (*val, addr);
+       break;
+      }
+    case 2:
+      {
+       const uint16_t *val = data;
+       outw (*val, addr);
+       break;
+      }
+    case 4:
+      {
+       const uint32_t *val = data;
+       outl (*val, addr);
+       break;
+      }
+    }
+  outl (sav, 0xCF8);
+
+  return ret;
+}
+
+static error_t
+pci_system_x86_conf2_probe (void)
+{
+  outb (0, 0xCFB);
+  outb (0, 0xCF8);
+  outb (0, 0xCFA);
+  if (inb (0xCF8) == 0 && inb (0xCFA) == 0)
+    return 0;
+
+  return ENODEV;
+}
+
+static error_t
+pci_system_x86_conf2_read (unsigned bus, unsigned dev, unsigned func,
+                          pciaddr_t reg, void *data, unsigned size)
+{
+  unsigned addr = 0xC000 | dev << 8 | reg;
+  error_t ret = 0;
+
+  if (bus >= 0x100 || dev >= 16 || func >= 8 || reg >= 0x100)
+    return EIO;
+
+  outb ((func << 1) | 0xF0, 0xCF8);
+  outb (bus, 0xCFA);
+  /* NOTE: x86 is already LE */
+  switch (size)
+    {
+    case 1:
+      {
+       uint8_t *val = data;
+       *val = inb (addr);
+       break;
+      }
+    case 2:
+      {
+       uint16_t *val = data;
+       *val = inw (addr);
+       break;
+      }
+    case 4:
+      {
+       uint32_t *val = data;
+       *val = inl (addr);
+       break;
+      }
+    default:
+      ret = EIO;
+      break;
+    }
+  outb (0, 0xCF8);
+
+  return ret;
+}
+
+static error_t
+pci_system_x86_conf2_write (unsigned bus, unsigned dev, unsigned func,
+                           pciaddr_t reg, void *data, unsigned size)
+{
+  unsigned addr = 0xC000 | dev << 8 | reg;
+  error_t ret = 0;
+
+  if (bus >= 0x100 || dev >= 16 || func >= 8 || reg >= 0x100)
+    return EIO;
+
+  outb ((func << 1) | 0xF0, 0xCF8);
+  outb (bus, 0xCFA);
+  /* NOTE: x86 is already LE */
+  switch (size)
+    {
+    case 1:
+      {
+       const uint8_t *val = data;
+       outb (*val, addr);
+       break;
+      }
+    case 2:
+      {
+       const uint16_t *val = data;
+       outw (*val, addr);
+       break;
+      }
+    case 4:
+      {
+       const uint32_t *val = data;
+       outl (*val, addr);
+       break;
+      }
+    default:
+      ret = EIO;
+      break;
+    }
+  outb (0, 0xCF8);
+
+  return ret;
+}
+
+/* Returns the number of regions (base address registers) the device has */
+static int
+pci_device_x86_get_num_regions (uint8_t header_type)
+{
+  switch (header_type & 0x7f)
+    {
+    case 0:
+      return 6;
+    case 1:
+      return 2;
+    case 2:
+      return 1;
+    default:
+      return 0;
+    }
+}
+
+/* Masks out the flag bigs of the base address register value */
+static uint32_t
+get_map_base (uint32_t val)
+{
+  if (val & 0x01)
+    return val & ~0x03;
+  else
+    return val & ~0x0f;
+}
+
+/* Returns the size of a region based on the all-ones test value */
+static unsigned
+get_test_val_size (uint32_t testval)
+{
+  unsigned size = 1;
+
+  if (testval == 0)
+    return 0;
+
+  /* Mask out the flag bits */
+  testval = get_map_base (testval);
+  if (!testval)
+    return 0;
+
+  while ((testval & 1) == 0)
+    {
+      size <<= 1;
+      testval >>= 1;
+    }
+
+  return size;
+}
+
+/* Read BAR `reg_num' in `dev' and map the data if any */
+static error_t
+pci_device_x86_region_probe (struct pci_device *dev, int reg_num)
+{
+  error_t err;
+  uint8_t offset;
+  uint32_t reg, addr, testval;
+  int memfd;
+
+  offset = PCI_BAR_ADDR_0 + 0x4 * reg_num;
+
+  /* Get the base address */
+  err =
+    pci_sys->read (dev->bus, dev->dev, dev->func, offset, &addr,
+                  sizeof (addr));
+  if (err)
+    return err;
+
+  /* Test write all ones to the register, then restore it. */
+  reg = 0xffffffff;
+  err = pci_sys->write (dev->bus, dev->dev, dev->func, offset, &reg,
+                       sizeof (reg));
+  if (err)
+    return err;
+  err = pci_sys->read (dev->bus, dev->dev, dev->func, offset, &testval,
+                      sizeof (testval));
+  if (err)
+    return err;
+  err = pci_sys->write (dev->bus, dev->dev, dev->func, offset, &addr,
+                       sizeof (addr));
+  if (err)
+    return err;
+
+  if (addr & 0x01)
+    dev->regions[reg_num].is_IO = 1;
+  if (addr & 0x04)
+    dev->regions[reg_num].is_64 = 1;
+  if (addr & 0x08)
+    dev->regions[reg_num].is_prefetchable = 1;
+
+  /* Set the size */
+  dev->regions[reg_num].size = get_test_val_size (testval);
+
+  /* Set the base address value */
+  dev->regions[reg_num].base_addr = get_map_base (addr);
+
+  if (dev->regions[reg_num].is_64)
+    {
+      err =
+       pci_sys->read (dev->bus, dev->dev, dev->func, offset + 4, &addr,
+                      sizeof (addr));
+      if (err)
+       return err;
+
+      dev->regions[reg_num].base_addr |= ((uint64_t) addr << 32);
+    }
+
+  if (dev->regions[reg_num].is_IO)
+    {
+      /* Enable the I/O Space bit */
+      err =
+       pci_sys->read (dev->bus, dev->dev, dev->func, PCI_COMMAND, &reg,
+                      sizeof (reg));
+      if (err)
+       return err;
+
+      if (!(reg & 0x1))
+       {
+         reg |= 0x1;
+
+         err =
+           pci_sys->write (dev->bus, dev->dev, dev->func, PCI_COMMAND,
+                           &reg, sizeof (reg));
+         if (err)
+           return err;
+       }
+
+      /* Clear the map pointer */
+      dev->regions[reg_num].memory = 0;
+    }
+  else if (dev->regions[reg_num].size > 0)
+    {
+      /* Enable the Memory Space bit */
+      err =
+       pci_sys->read (dev->bus, dev->dev, dev->func, PCI_COMMAND, &reg,
+                      sizeof (reg));
+      if (err)
+       return err;
+
+      if (!(reg & 0x2))
+       {
+         reg |= 0x2;
+
+         err =
+           pci_sys->write (dev->bus, dev->dev, dev->func, PCI_COMMAND,
+                           &reg, sizeof (reg));
+         if (err)
+           return err;
+       }
+
+      /* Map the region in our space */
+      memfd = open ("/dev/mem", O_RDONLY | O_CLOEXEC);
+      if (memfd == -1)
+       return errno;
+
+      dev->regions[reg_num].memory =
+       mmap (NULL, dev->regions[reg_num].size, PROT_READ | PROT_WRITE, 0,
+             memfd, dev->regions[reg_num].base_addr);
+      if (dev->regions[reg_num].memory == MAP_FAILED)
+       {
+         dev->regions[reg_num].memory = 0;
+         close (memfd);
+         return errno;
+       }
+
+      close (memfd);
+    }
+
+  return 0;
+}
+
+/* Read the XROMBAR in `dev' and map the data if any */
+static error_t
+pci_device_x86_rom_probe (struct pci_device *dev)
+{
+  error_t err;
+  uint8_t reg_8, xrombar_addr;
+  uint32_t reg, reg_back;
+  pciaddr_t rom_size;
+  pciaddr_t rom_base;
+  void *rom_mapped;
+  int memfd;
+
+  /* First we need to know which type of header is this */
+  err = pci_sys->read (dev->bus, dev->dev, dev->func, PCI_HDRTYPE, &reg_8,
+                      sizeof (reg_8));
+  if (err)
+    return err;
+
+  /* Get the XROMBAR register address */
+  switch (reg_8 & 0x3)
+    {
+    case PCI_HDRTYPE_DEVICE:
+      xrombar_addr = PCI_XROMBAR_ADDR_00;
+      break;
+    case PCI_HDRTYPE_BRIDGE:
+      xrombar_addr = PCI_XROMBAR_ADDR_01;
+      break;
+    default:
+      return -1;
+    }
+
+  /* Get size and physical address */
+  err = pci_sys->read (dev->bus, dev->dev, dev->func, xrombar_addr, &reg,
+                      sizeof (reg));
+  if (err)
+    return err;
+
+  reg_back = reg;
+  reg = 0xFFFFF800;            /* Base address: first 21 bytes */
+  err = pci_sys->write (dev->bus, dev->dev, dev->func, xrombar_addr, &reg,
+                       sizeof (reg));
+  if (err)
+    return err;
+  err = pci_sys->read (dev->bus, dev->dev, dev->func, xrombar_addr, &reg,
+                      sizeof (reg));
+  if (err)
+    return err;
+
+  rom_size = (~reg + 1);
+  rom_base = reg_back & reg;
+
+  if (rom_size == 0)
+    return 0;
+
+  /* Enable the address decoder and write the physical address back */
+  reg_back |= 0x1;
+  err = pci_sys->write
+    (dev->bus, dev->dev, dev->func, xrombar_addr, &reg_back,
+     sizeof (reg_back));
+  if (err)
+    return err;
+
+  /* Enable the Memory Space bit */
+  err = pci_sys->read (dev->bus, dev->dev, dev->func, PCI_COMMAND, &reg,
+                      sizeof (reg));
+  if (err)
+    return err;
+
+  if (!(reg & 0x2))
+    {
+      reg |= 0x2;
+
+      err =
+       pci_sys->write (dev->bus, dev->dev, dev->func, PCI_COMMAND, &reg,
+                       sizeof (reg));
+      if (err)
+       return err;
+    }
+
+  /* Map the ROM in our space */
+  memfd = open ("/dev/mem", O_RDONLY | O_CLOEXEC);
+  if (memfd == -1)
+    return errno;
+
+  rom_mapped = mmap (NULL, rom_size, PROT_READ, 0, memfd, rom_base);
+  if (rom_mapped == MAP_FAILED)
+    {
+      close (memfd);
+      return errno;
+    }
+
+  close (memfd);
+
+  dev->rom_size = rom_size;
+  dev->rom_base = rom_base;
+  dev->rom_memory = rom_mapped;
+
+  return 0;
+}
+
+/* Configure BARs and ROM */
+static error_t
+pci_device_x86_probe (struct pci_device *dev)
+{
+  error_t err;
+  uint8_t hdrtype;
+  int i;
+
+  /* Probe BARs */
+  err = pci_sys->read (dev->bus, dev->dev, dev->func, PCI_HDRTYPE, &hdrtype,
+                      sizeof (hdrtype));
+  if (err)
+    return err;
+
+  for (i = 0; i < pci_device_x86_get_num_regions (hdrtype); i++)
+    {
+      err = pci_device_x86_region_probe (dev, i);
+      if (err)
+       return err;
+
+      if (dev->regions[i].is_64)
+       /* Move the pointer one BAR ahead */
+       i++;
+    }
+
+  /* Probe ROM */
+  err = pci_device_x86_rom_probe (dev);
+  if (err)
+    return err;
+
+  return 0;
+}
+
+/*
+ * Refresh the device. Check for updates in region `reg_num'
+ * or in ROM if `rom' = true. `reg_num' < 0 means no region check.
+ */
+static error_t
+pci_device_x86_refresh (struct pci_device *dev, int reg_num, int rom)
+{
+  error_t err;
+  uint8_t offset, hdrtype;
+  uint32_t addr;
+
+  if (reg_num >= 0 && dev->regions[reg_num].size > 0)
+    {
+      /* Read the BAR */
+      offset = PCI_BAR_ADDR_0 + 0x4 * reg_num;
+      err =
+       pci_sys->read (dev->bus, dev->dev, dev->func, offset, &addr,
+                      sizeof (addr));
+      if (err)
+       return err;
+
+      /* Check whether the region is outdated, if so, the refresh it */
+      if (dev->regions[reg_num].base_addr != get_map_base (addr))
+       {
+         err = pci_device_x86_region_probe (dev, reg_num);
+         if (err)
+           return err;
+       }
+    }
+
+  if (rom && dev->rom_size > 0)
+    {
+      /* Read the BAR */
+      err =
+       pci_sys->read (dev->bus, dev->dev, dev->func, PCI_HDRTYPE, &hdrtype,
+                      sizeof (hdrtype));
+      if (err)
+       return err;
+
+      switch (hdrtype & 0x3)
+       {
+       case PCI_HDRTYPE_DEVICE:
+         offset = PCI_XROMBAR_ADDR_00;
+         break;
+       case PCI_HDRTYPE_BRIDGE:
+         offset = PCI_XROMBAR_ADDR_01;
+         break;
+       default:
+         return -1;
+       }
+
+      err = pci_sys->read (dev->bus, dev->dev, dev->func, offset, &addr,
+                          sizeof (addr));
+      if (err)
+       return err;
+
+      /* Check whether the ROM is outdated, if so, the refresh it */
+      if (dev->rom_base != (addr & 0xFFFFF800))
+       {
+         err = pci_device_x86_rom_probe (dev);
+         if (err)
+           return err;
+       }
+    }
+
+  return 0;
+}
+
+/* Check that this really looks like a PCI configuration. */
+static error_t
+pci_system_x86_check (struct pci_system *pci_sys)
+{
+  int dev;
+  uint16_t class, vendor;
+
+  /* Look on bus 0 for a device that is a host bridge, a VGA card,
+   * or an intel or compaq device.  */
+
+  for (dev = 0; dev < 32; dev++)
+    {
+      if (pci_sys->read (0, dev, 0, PCI_CLASS_DEVICE, &class, sizeof (class)))
+       continue;
+      if (class == PCI_CLASS_BRIDGE_HOST || class == PCI_CLASS_DISPLAY_VGA)
+       return 0;
+      if (pci_sys->read (0, dev, 0, PCI_VENDOR_ID, &vendor, sizeof (vendor)))
+       continue;
+      if (vendor == PCI_VENDOR_ID_INTEL || class == PCI_VENDOR_ID_COMPAQ)
+       return 0;
+    }
+
+  return ENODEV;
+}
+
+/* Find out which conf access method use */
+static error_t
+pci_probe (struct pci_system *pci_sys)
+{
+  if (pci_system_x86_conf1_probe () == 0)
+    {
+      pci_sys->read = pci_system_x86_conf1_read;
+      pci_sys->write = pci_system_x86_conf1_write;
+      if (pci_system_x86_check (pci_sys) == 0)
+       return 0;
+    }
+
+  if (pci_system_x86_conf2_probe () == 0)
+    {
+      pci_sys->read = pci_system_x86_conf2_read;
+      pci_sys->write = pci_system_x86_conf2_write;
+      if (pci_system_x86_check (pci_sys) == 0)
+       return 0;
+    }
+
+  return ENODEV;
+}
+
+static error_t
+pci_nfuncs (struct pci_system *pci_sys, int bus, int dev, uint8_t * nfuncs)
+{
+  uint8_t hdrtype;
+  error_t err;
+
+  err = pci_sys->read (bus, dev, 0, PCI_HDRTYPE, &hdrtype, sizeof (hdrtype));
+  if (err)
+    return err;
+
+  *nfuncs = hdrtype & 0x80 ? 8 : 1;
+
+  return 0;
+}
+
+/* Recursively scan bus number `bus' */
+static error_t
+pci_system_x86_scan_bus (struct pci_system *pci_sys, uint8_t bus)
+{
+  error_t err;
+  uint8_t dev, func, nfuncs, hdrtype, secbus;
+  uint32_t reg;
+  struct pci_device *d, *devices;
+
+  for (dev = 0; dev < 32; dev++)
+    {
+      err = pci_nfuncs (pci_sys, bus, dev, &nfuncs);
+      if (err)
+       return err;
+
+      for (func = 0; func < nfuncs; func++)
+       {
+         err =
+           pci_sys->read (bus, dev, func, PCI_VENDOR_ID, &reg, sizeof (reg));
+         if (err)
+           return err;
+
+         if (PCI_VENDOR (reg) == PCI_VENDOR_INVALID || PCI_VENDOR (reg) == 0)
+           continue;
+
+         err = pci_sys->read (bus, dev, func, PCI_CLASS, &reg, sizeof (reg));
+         if (err)
+           return err;
+
+         err =
+           pci_sys->read (bus, dev, func, PCI_HDRTYPE, &hdrtype,
+                          sizeof (hdrtype));
+         if (err)
+           return err;
+
+         devices =
+           realloc (pci_sys->devices,
+                    (pci_sys->num_devices + 1) * sizeof (struct pci_device));
+         if (!devices)
+           return ENOMEM;
+
+         d = devices + pci_sys->num_devices;
+
+         d->domain = 0;        /* PCI express still not supported */
+         d->bus = bus;
+         d->dev = dev;
+         d->func = func;
+
+         d->device_class = reg >> 8;
+
+         err = pci_device_x86_probe (d);
+         if (err)
+           return err;
+
+         pci_sys->devices = devices;
+         pci_sys->num_devices++;
+
+         switch (hdrtype & 0x3)
+           {
+           case PCI_HDRTYPE_DEVICE:
+             break;
+           case PCI_HDRTYPE_BRIDGE:
+           case PCI_HDRTYPE_CARDBUS:
+             {
+               err =
+                 pci_sys->read (bus, dev, func, PCI_SECONDARY_BUS, &secbus,
+                                sizeof (secbus));
+               if (err)
+                 return err;
+
+               err = pci_system_x86_scan_bus (pci_sys, secbus);
+               if (err)
+                 return err;
+
+               break;
+             }
+           default:
+             /* Unknown header, do nothing */
+             break;
+           }
+       }
+    }
+
+  return 0;
+}
+
+/* Look for host bridges in bus 0 dev 0 and enumerate devices */
+static error_t
+pci_system_x86_find_host_bridges (struct pci_system *pci_sys)
+{
+  error_t err;
+  uint8_t nfuncs;
+  uint32_t reg;
+  int i;
+
+  err = pci_nfuncs (pci_sys, 0, 0, &nfuncs);
+  if (err)
+    return err;
+
+  for (i = 0; i < nfuncs; i++)
+    {
+      err = pci_sys->read (0, 0, i, PCI_VENDOR_ID, &reg, sizeof (reg));
+      if (err)
+       return err;
+
+      if (PCI_VENDOR (reg) == PCI_VENDOR_INVALID || PCI_VENDOR (reg) == 0)
+       continue;
+
+      /* Host bridge found, scan its bus */
+      pci_system_x86_scan_bus (pci_sys, i);
+    }
+
+  return 0;
+}
+
+/* Initialize the x86 module */
+error_t
+pci_system_x86_create (void)
+{
+  error_t err;
+
+  err = x86_enable_io ();
+  if (err)
+    return err;
+
+  pci_sys = calloc (1, sizeof (struct pci_system));
+  if (pci_sys == NULL)
+    {
+      x86_disable_io ();
+      return ENOMEM;
+    }
+
+  err = pci_probe (pci_sys);
+  if (err)
+    {
+      x86_disable_io ();
+      free (pci_sys);
+      return err;
+    }
+  pci_sys->device_refresh = pci_device_x86_refresh;
+
+  /* Recursive scan */
+  pci_sys->num_devices = 0;
+  err = pci_system_x86_find_host_bridges (pci_sys);
+  if (err)
+    {
+      x86_disable_io ();
+      free (pci_sys);
+      pci_sys = NULL;
+      return err;
+    }
+
+  return 0;
+}
diff --git a/pci-arbiter/x86_pci.h b/pci-arbiter/x86_pci.h
new file mode 100644
index 00000000..fb2374a6
--- /dev/null
+++ b/pci-arbiter/x86_pci.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2009, 2012 Samuel Thibault
+ * Heavily inspired from the freebsd, netbsd, and openbsd backends
+ * (C) Copyright Eric Anholt 2006
+ * (C) Copyright IBM Corporation 2006
+ * Copyright (c) 2008 Juan Romero Pardines
+ * Copyright (c) 2008 Mark Kettenis
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * PCI backend header for x86 (32 and 64 bit) architectures.
+ *
+ * Following code is borrowed from libpciaccess:
+ * https://cgit.freedesktop.org/xorg/lib/libpciaccess/
+ */
+
+#ifndef X86_PCI_H
+#define X86_PCI_H
+
+int pci_system_x86_create (void);
+
+#endif /* X86_PCI_H */
-- 
2.14.0




reply via email to

[Prev in Thread] Current Thread [Next in Thread]