summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoan Lledó <joanlluislledo@gmail.com>2018-01-16 12:54:28 +0100
committerSamuel Thibault <samuel.thibault@ens-lyon.org>2018-10-30 00:47:36 +0100
commita830bef66e689f6f1defa9988f257a7ec2bb034d (patch)
tree3d49249a05195148fc8a8e6a84f9c56b46dbab7b
parent6783a1b124eff9383571dfd4b5e83a03ee63866a (diff)
PCI Arbiter
-rw-r--r--Makefile1
-rw-r--r--hurd/hurd_types.defs19
-rw-r--r--hurd/hurd_types.h24
-rw-r--r--hurd/paths.h3
-rw-r--r--hurd/pci.defs73
-rw-r--r--hurd/subsystems1
-rw-r--r--pci-arbiter/Makefile41
-rw-r--r--pci-arbiter/func_files.c210
-rw-r--r--pci-arbiter/func_files.h44
-rw-r--r--pci-arbiter/main.c117
-rw-r--r--pci-arbiter/mig-mutate.h28
-rw-r--r--pci-arbiter/ncache.c90
-rw-r--r--pci-arbiter/ncache.h32
-rw-r--r--pci-arbiter/netfs_impl.c568
-rw-r--r--pci-arbiter/netfs_impl.h43
-rw-r--r--pci-arbiter/options.c282
-rw-r--r--pci-arbiter/options.h74
-rw-r--r--pci-arbiter/pci-ops.c273
-rw-r--r--pci-arbiter/pci_access.c51
-rw-r--r--pci-arbiter/pci_access.h187
-rw-r--r--pci-arbiter/pcifs.c409
-rw-r--r--pci-arbiter/pcifs.h208
-rw-r--r--pci-arbiter/startup-ops.c40
-rw-r--r--pci-arbiter/startup.c58
-rw-r--r--pci-arbiter/startup.h31
-rw-r--r--pci-arbiter/x86_pci.c843
-rw-r--r--pci-arbiter/x86_pci.h34
27 files changed, 3782 insertions, 2 deletions
diff --git a/Makefile b/Makefile
index 2d9d3f80..c0aa59a0 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 4d68b36b..eb06029b 100644
--- a/hurd/hurd_types.h
+++ b/hurd/hurd_types.h
@@ -1,6 +1,6 @@
/* C declarations for Hurd server interfaces
Copyright (C) 1993, 1994, 1995, 1996, 1998, 1999, 2001, 2002,
- 2010 Free Software Foundation, Inc.
+ 2010, 2017 Free Software Foundation, Inc.
This file is part of the GNU Hurd.
@@ -51,6 +51,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'. */
@@ -358,6 +359,7 @@ typedef const int *const_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_paths and exec_* */
enum
@@ -384,4 +386,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..3fc3010b
--- /dev/null
+++ b/pci-arbiter/Makefile
@@ -0,0 +1,41 @@
+# 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
+
+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
diff --git a/pci-arbiter/func_files.c b/pci-arbiter/func_files.c
new file mode 100644
index 00000000..7df94d2f
--- /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 > dev->config_size)
+ return EINVAL;
+ if ((offset + *len) > dev->config_size)
+ *len = dev->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..b65d2c57
--- /dev/null
+++ b/pci-arbiter/func_files.h
@@ -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/>.
+*/
+
+/* Per-function files header */
+
+#ifndef FUNC_FILES_H
+#define FUNC_FILES_H
+
+#include "pcifs.h"
+
+/* Config */
+#define FILE_CONFIG_NAME "config"
+
+/* 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..bc085274
--- /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..6a513e37
--- /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..7ebc0c77
--- /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..b96c7d8d
--- /dev/null
+++ b/pci-arbiter/netfs_impl.c
@@ -0,0 +1,568 @@
+/*
+ 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;
+}
+
+static 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;
+}
+
+static void
+destroy_node (struct node *node)
+{
+ if (node->nn->ln)
+ node->nn->ln->node = 0;
+ free (node);
+}
+
+/* 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..ca0662fd
--- /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..76ce6460
--- /dev/null
+++ b/pci-arbiter/options.c
@@ -0,0 +1,282 @@
+/*
+ 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)
+
+ 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.
+ */
+ free (h->permsets);
+ free (h);
+ PERR (err, "Invalid options, no changes were applied");
+ }
+ else
+ /* Invalid options on a non-started server, exit() */
+ PERR (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..814f81ad
--- /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..328db5b2
--- /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..4d7fc68f
--- /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..cf42cf62
--- /dev/null
+++ b/pci-arbiter/pci_access.h
@@ -0,0 +1,187 @@
+/*
+ * (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;
+
+ /*
+ * Size of the configuration space
+ */
+ size_t config_size;
+};
+
+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..e7f495e1
--- /dev/null
+++ b/pci-arbiter/pcifs.c
@@ -0,0 +1,409 @@
+/*
+ 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 | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH |
+ S_IXOTH;
+ np->nn_translated = np->nn_stat.st_mode;
+
+ /* 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 = 1; /* Skip root entry */
+ c_domain = c_bus = c_dev = -1;
+ for (i = 0, device = pci_sys->devices; i < pci_sys->num_devices;
+ i++, device++)
+ {
+ if (device->domain != c_domain)
+ {
+ c_domain = device->domain;
+ c_bus = -1;
+ c_dev = -1;
+ nentries++;
+ }
+
+ 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;
+ 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 */
+ e_stat = list->stat;
+ e_stat.st_mode &= ~S_IROOT; /* Remove the root mode */
+ 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_mode |= S_IFREG | S_IWUSR | S_IWGRP;
+ e_stat.st_size = device->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)
+{
+ /* 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;
+}
diff --git a/pci-arbiter/pcifs.h b/pci-arbiter/pcifs.h
new file mode 100644
index 00000000..7a35c77d
--- /dev/null
+++ b/pci-arbiter/pcifs.h
@@ -0,0 +1,208 @@
+/*
+ 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);
+
+#endif /* PCIFS_H */
diff --git a/pci-arbiter/startup-ops.c b/pci-arbiter/startup-ops.c
new file mode 100644
index 00000000..f3506c42
--- /dev/null
+++ b/pci-arbiter/startup-ops.c
@@ -0,0 +1,40 @@
+/*
+ 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. Call netfs_shutdown() */
+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..6ffb8c60
--- /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..9cf1f54a
--- /dev/null
+++ b/pci-arbiter/x86_pci.c
@@ -0,0 +1,843 @@
+/*
+ * Copyright (c) 2017 Joan Lledó
+ * Copyright (c) 2009, 2012, 2018 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/
+ */
+
+#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 <string.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
+
+#define PCI_CONFIG_SIZE 256
+
+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;
+ memset (d, 0, sizeof (struct pci_device));
+
+ /* Fixed values as PCI express is still not supported */
+ d->domain = 0;
+ d->config_size = PCI_CONFIG_SIZE;
+
+ 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;
+}
+
+/* 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_scan_bus (pci_sys, 0);
+ 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 */