Discussion:
[PATCH] PCI Arbiter
Joan Lledó
2017-10-20 10:46:14 UTC
Permalink
---
Makefile | 2 +
hurd/hurd_types.defs | 19 ++-
hurd/hurd_types.h | 1 +
hurd/paths.h | 1 +
hurd/pci.defs | 50 +++++++
hurd/subsystems | 1 +
libpciclient/Makefile | 29 ++++
libpciclient/pciclient.c | 103 +++++++++++++++
libpciclient/pciclient.h | 42 ++++++
pci_arbiter/Makefile | 40 ++++++
pci_arbiter/config.h | 5 +
pci_arbiter/main.c | 116 ++++++++++++++++
pci_arbiter/mig-mutate.h | 28 ++++
pci_arbiter/pci-ops.c | 89 +++++++++++++
pci_arbiter/pci_access.c | 51 +++++++
pci_arbiter/pci_access.h | 78 +++++++++++
pci_arbiter/pci_arbiter.h | 46 +++++++
pci_arbiter/x86_pci.c | 331 ++++++++++++++++++++++++++++++++++++++++++++++
pci_arbiter/x86_pci.h | 42 ++++++
19 files changed, 1073 insertions(+), 1 deletion(-)
create mode 100644 hurd/pci.defs
create mode 100644 libpciclient/Makefile
create mode 100644 libpciclient/pciclient.c
create mode 100644 libpciclient/pciclient.h
create mode 100644 pci_arbiter/Makefile
create mode 100644 pci_arbiter/config.h
create mode 100644 pci_arbiter/main.c
create mode 100644 pci_arbiter/mig-mutate.h
create mode 100644 pci_arbiter/pci-ops.c
create mode 100644 pci_arbiter/pci_access.c
create mode 100644 pci_arbiter/pci_access.h
create mode 100644 pci_arbiter/pci_arbiter.h
create mode 100644 pci_arbiter/x86_pci.c
create mode 100644 pci_arbiter/x86_pci.h

diff --git a/Makefile b/Makefile
index 119f130b..d3d93b87 100644
--- a/Makefile
+++ b/Makefile
@@ -31,6 +31,7 @@ lib-subdirs = libshouldbeinlibc libihash libiohelp libports libthreads \
libnetfs libpipe libstore libhurdbugaddr libftpconn libcons \
libhurd-slab \
libbpf \
+ libpciclient

# Hurd programs
prog-subdirs = auth proc exec term \
@@ -45,6 +46,7 @@ prog-subdirs = auth proc exec term \
init \
devnode \
eth-multiplexer \
+ pci_arbiter

ifeq ($(HAVE_SUN_RPC),yes)
prog-subdirs += nfs nfsd
diff --git a/hurd/hurd_types.defs b/hurd/hurd_types.defs
index 4d7013c8..0e9b990e 100644
--- a/hurd/hurd_types.defs
+++ b/hurd/hurd_types.defs
@@ -1,5 +1,5 @@
/* MiG type declarations for Hurd interfaces -*- C -*-
- Copyright (C) 1993,94,95,96,98,2001,02 Free Software Foundation, Inc.
+ Copyright (C) 1993,94,95,96,98,2001,02,17 Free Software Foundation, Inc.

This file is part of the GNU Hurd.

@@ -296,6 +296,23 @@ destructor: INTERRUPT_DESTRUCTOR
#endif
;

+/* PCI arbiter */
+type pci_t = mach_port_copy_send_t
+#ifdef PCI_INTRAN
+intran: PCI_INTRAN
+intranpayload: PCI_INTRAN_PAYLOAD
+#else
+#ifdef HURD_DEFAULT_PAYLOAD_TO_PORT
+intranpayload: pci_t HURD_DEFAULT_PAYLOAD_TO_PORT
+#endif
+#endif
+#ifdef PCI_OUTTRAN
+outtran: PCI_OUTTRAN
+#endif
+#ifdef PCI_DESTRUCTOR
+destructor: PCI_DESTRUCTOR
+#endif
+;

type proccoll_t = mach_port_copy_send_t;

diff --git a/hurd/hurd_types.h b/hurd/hurd_types.h
index 2960a294..8b1092a5 100644
--- a/hurd/hurd_types.h
+++ b/hurd/hurd_types.h
@@ -50,6 +50,7 @@ typedef mach_port_t exec_startup_t;
typedef mach_port_t interrupt_t;
typedef mach_port_t proccoll_t;
typedef mach_port_t ctty_t;
+typedef mach_port_t pci_t;

#include <errno.h> /* Defines `error_t'. */

diff --git a/hurd/paths.h b/hurd/paths.h
index e1b00e90..1484b43e 100644
--- a/hurd/paths.h
+++ b/hurd/paths.h
@@ -30,6 +30,7 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#define _SERVERS_PROC _SERVERS "proc"
#define _SERVERS_PASSWORD _SERVERS "password"
#define _SERVERS_DEFPAGER _SERVERS "default-pager"
+#define _SERVERS_PCI _SERVERS "pci"

/* Directory containing naming points for socket servers.
Entries are named by the string representing the domain number
diff --git a/hurd/pci.defs b/hurd/pci.defs
new file mode 100644
index 00000000..d051bd69
--- /dev/null
+++ b/hurd/pci.defs
@@ -0,0 +1,50 @@
+/* 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
+#endif
+
+INTR_INTERFACE
+
+/* Read 'amount' bytes from B/D/F + 'reg' and store it in 'data' */
+routine pci_read (
+ master: pci_t;
+ bus: int;
+ dev: int;
+ func: int;
+ reg: int;
+ out data: data_t, dealloc;
+ amount: vm_size_t
+);
+
+/* Write 'amount' bytes from 'data' to B/D/F + 'reg' */
+routine pci_write(
+ master: pci_t;
+ bus: int;
+ dev: int;
+ func: int;
+ reg: int;
+ data: data_t;
+ out amount: vm_size_t
+);
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/libpciclient/Makefile b/libpciclient/Makefile
new file mode 100644
index 00000000..d550115d
--- /dev/null
+++ b/libpciclient/Makefile
@@ -0,0 +1,29 @@
+# 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 := libpciclient
+makemode := library
+
+libname = libpciclient
+installhdrs = pciclient.h
+
+SRCS = pciclient.c
+
+MIGSTUBS = pciUser.o
+OBJS = $(sort $(SRCS:.c=.o)) $(MIGSTUBS)
+
+include ../Makeconf
diff --git a/libpciclient/pciclient.c b/libpciclient/pciclient.c
new file mode 100644
index 00000000..9f03dc30
--- /dev/null
+++ b/libpciclient/pciclient.c
@@ -0,0 +1,103 @@
+/*
+ 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 Client implementation */
+
+#include <pciclient.h>
+
+#include <hurd.h>
+#include <hurd/paths.h>
+#include <error.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <string.h>
+
+#include <pci_U.h>
+
+/* Get the server port */
+static void
+init_client ()
+{
+ pci_server_port = file_name_lookup (_SERVERS_PCI, 0, 0);
+
+ if (pci_server_port == MACH_PORT_NULL)
+ error (-1, errno, "Could not open file `%s'", _SERVERS_PCI);
+
+ return;
+}
+
+/*
+ * Read 'size' bytes from B/D/F + reg and store them in 'data'.
+ *
+ * It's assumed that 'size' bytes are allocated in 'data'
+ */
+int
+pciclient_cfg_read (int bus, int dev, int func, int reg, char *buf,
+ size_t * nbytes)
+{
+ error_t err;
+ size_t nread;
+ char *data;
+
+ if (pci_server_port == MACH_PORT_NULL)
+ init_client ();
+
+ data = buf;
+ nread = *nbytes;
+ err =
+ pci_read (pci_server_port, bus, dev, func, reg, &data, &nread, *nbytes);
+ if (err)
+ return err;
+
+ if (data != buf)
+ {
+ if (nread > *nbytes) /* Sanity check for bogus server. */
+ {
+ vm_deallocate (mach_task_self (), (vm_address_t) data, nread);
+ return EGRATUITOUS;
+ }
+
+ memcpy (buf, data, nread);
+ vm_deallocate (mach_task_self (), (vm_address_t) data, nread);
+ }
+
+ *nbytes = nread;
+
+ return 0;
+}
+
+/* Write 'size' bytes from 'data' to B/D/F + reg */
+int
+pciclient_cfg_write (int bus, int dev, int func, int reg, char *buf,
+ size_t * nbytes)
+{
+ error_t err;
+ size_t nwrote;
+
+ if (pci_server_port == MACH_PORT_NULL)
+ init_client ();
+
+ err =
+ pci_write (pci_server_port, bus, dev, func, reg, buf, *nbytes, &nwrote);
+
+ if (!err)
+ *nbytes = nwrote;
+
+ return err;
+}
diff --git a/libpciclient/pciclient.h b/libpciclient/pciclient.h
new file mode 100644
index 00000000..5148ba25
--- /dev/null
+++ b/libpciclient/pciclient.h
@@ -0,0 +1,42 @@
+/*
+ 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/>.
+*/
+
+/* PCI client header */
+
+#ifndef PCI_CLIENT_H
+#define PCI_CLIENT_H
+
+#include <mach/port.h>
+#include <sys/types.h>
+
+/* Server port */
+mach_port_t pci_server_port = MACH_PORT_NULL;
+
+/* Public function declarations */
+int
+pciclient_cfg_read (int bus, int dev, int func, int reg, char *buf,
+ size_t * nbytes);
+
+int
+pciclient_cfg_write (int bus, int dev, int func, int reg, char *buf,
+ size_t * nbytes);
+
+#endif /* PCI_CLIENT_H */
diff --git a/pci_arbiter/Makefile b/pci_arbiter/Makefile
new file mode 100644
index 00000000..e3a3b732
--- /dev/null
+++ b/pci_arbiter/Makefile
@@ -0,0 +1,40 @@
+# 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
+MIGSRCS = pciServer.c
+OBJS = $(patsubst %.S,%.o,$(patsubst %.c,%.o, $(SRCS) $(MIGSRCS)))
+
+HURDLIBS= trivfs fshelp ports shouldbeinlibc
+
+target = pci_arbiter
+
+include ../Makeconf
+
+CFLAGS += -I$(PORTDIR)/include
+
+CPPFLAGS += -imacros $(srcdir)/config.h
+pci-MIGSFLAGS = -imacros $(srcdir)/mig-mutate.h
+
+# cpp doesn't automatically make dependencies for -imacros dependencies. argh.
+pci_S.h pciServer.c: mig-mutate.h
+$(OBJS): config.h
diff --git a/pci_arbiter/config.h b/pci_arbiter/config.h
new file mode 100644
index 00000000..b0d5196d
--- /dev/null
+++ b/pci_arbiter/config.h
@@ -0,0 +1,5 @@
+#define __KERNEL__ 1
+#undef __SMP__
+
+#define _HURD_ 1
+
diff --git a/pci_arbiter/main.c b/pci_arbiter/main.c
new file mode 100644
index 00000000..5475382a
--- /dev/null
+++ b/pci_arbiter/main.c
@@ -0,0 +1,116 @@
+/*
+ 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 <pci_arbiter.h>
+
+#include <error.h>
+#include <fcntl.h>
+#include <hurd/trivfs.h>
+
+#include <pci_access.h>
+#include <pci_S.h>
+
+int trivfs_fstype = FSTYPE_MISC;
+int trivfs_fsid = 0;
+int trivfs_support_read = 0;
+int trivfs_support_write = 0;
+int trivfs_support_exec = 0;
+int trivfs_allow_open = O_READ | O_WRITE;
+
+void
+trivfs_modify_stat (struct trivfs_protid *cred, io_statbuf_t * st)
+{
+}
+
+error_t
+trivfs_goaway (struct trivfs_control *fsys, int flags)
+{
+ exit (0);
+}
+
+int
+pci_demuxer (mach_msg_header_t * inp, mach_msg_header_t * outp)
+{
+ mig_routine_t routine;
+ if ((routine = pci_server_routine (inp)) ||
+ (routine = NULL, trivfs_demuxer (inp, outp)))
+ {
+ if (routine)
+ (*routine) (inp, outp);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ struct stat st;
+ mach_port_t bootstrap;
+
+ pci_bucket = ports_create_bucket ();
+
+ mach_port_allocate (mach_task_self (),
+ MACH_PORT_RIGHT_RECEIVE, &fsys_identity);
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap == MACH_PORT_NULL)
+ error (-1, 0, "Must be started as a translator");
+
+ err = trivfs_add_protid_port_class (&pci_protid_portclass);
+ if (err)
+ error (1, err, "Error creating protid port class");
+
+ err = trivfs_add_control_port_class (&pci_cntl_portclass);
+ if (err)
+ error (1, err, "Error creating control port class");
+
+ /* Reply to our parent */
+ err = trivfs_startup (bootstrap, 0,
+ pci_cntl_portclass,
+ pci_bucket,
+ pci_protid_portclass, pci_bucket, &pcicntl);
+ mach_port_deallocate (mach_task_self (), bootstrap);
+ if (err)
+ {
+ return (-1);
+ }
+
+ /* Initialize status from underlying node. */
+ pci_owner = pci_group = 0;
+ err = io_stat (pcicntl->underlying, &st);
+ if (!err)
+ {
+ pci_owner = st.st_uid;
+ pci_group = st.st_gid;
+ }
+
+ /* Start the PCI system */
+ err = pci_system_init ();
+ if (err)
+ error (1, err, "Error starting the PCI system");
+
+ ports_manage_port_operations_one_thread (pci_bucket, pci_demuxer, 0);
+
+ return 0;
+}
diff --git a/pci_arbiter/mig-mutate.h b/pci_arbiter/mig-mutate.h
new file mode 100644
index 00000000..df68fb05
--- /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 "../libtrivfs/mig-decls.h"; \
+
+#define PCI_INTRAN trivfs_protid_t trivfs_begin_using_protid (pci_t)
+#define PCI_INTRAN_PAYLOAD trivfs_protid_t trivfs_begin_using_protid_payload
+#define PCI_DESTRUCTOR trivfs_end_using_protid (trivfs_protid_t)
diff --git a/pci_arbiter/pci-ops.c b/pci_arbiter/pci-ops.c
new file mode 100644
index 00000000..f29b58d0
--- /dev/null
+++ b/pci_arbiter/pci-ops.c
@@ -0,0 +1,89 @@
+/*
+ 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 <hurd/fshelp.h>
+
+#include <pci_arbiter.h>
+#include <pci_access.h>
+
+error_t
+S_pci_read (struct trivfs_protid *master, int bus, int dev, int func,
+ int reg, char **data, size_t * datalen,
+ mach_msg_type_number_t amount)
+{
+ error_t err;
+
+ if (!master)
+ return EOPNOTSUPP;
+
+ if (!master->isroot)
+ {
+ struct stat st;
+
+ st.st_uid = pci_owner;
+ st.st_gid = pci_group;
+
+ err = fshelp_isowner (&st, master->user);
+ if (err)
+ return EPERM;
+ }
+
+ if (amount > *datalen)
+ amount = *datalen;
+
+ err = pci_ifc->read (bus, dev, func, reg, *data, amount);
+
+ if(!err)
+ *datalen = amount;
+
+ return err;
+}
+
+error_t
+S_pci_write (struct trivfs_protid * master, int bus, int dev, int func,
+ int reg, char *data, size_t datalen,
+ mach_msg_type_number_t * amount)
+{
+ error_t err;
+
+ if (!master)
+ return EOPNOTSUPP;
+
+ if (!master->isroot)
+ {
+ struct stat st;
+
+ st.st_uid = pci_owner;
+ st.st_gid = pci_group;
+
+ err = fshelp_isowner (&st, master->user);
+ if (err)
+ return EPERM;
+ }
+
+ err = pci_ifc->write (bus, dev, func, reg, data, datalen);
+
+ *amount = datalen;
+
+ return err;
+}
diff --git a/pci_arbiter/pci_access.c b/pci_arbiter/pci_access.c
new file mode 100644
index 00000000..eded1bff
--- /dev/null
+++ b/pci_arbiter/pci_access.c
@@ -0,0 +1,51 @@
+/*
+ * (C) Copyright IBM Corporation 2006
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * on the rights to use, copy, modify, merge, publish, distribute, sub
+ * license, and/or sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * PCI access general header.
+ *
+ * Following code is borrowed from libpciaccess:
+ * https://cgit.freedesktop.org/xorg/lib/libpciaccess/
+ */
+
+#include <pci_access.h>
+
+#include <errno.h>
+
+#include <x86_pci.h>
+
+/* Configure PCI parameters */
+int
+pci_system_init (void)
+{
+ int err = ENOSYS;
+
+#ifdef __GNU__
+ err = pci_system_x86_create ();
+#else
+#error "Unsupported OS"
+#endif
+
+ return err;
+}
diff --git a/pci_arbiter/pci_access.h b/pci_arbiter/pci_access.h
new file mode 100644
index 00000000..8735ea77
--- /dev/null
+++ b/pci_arbiter/pci_access.h
@@ -0,0 +1,78 @@
+/*
+ * (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 <stdint.h>
+
+typedef uint64_t pciaddr_t;
+
+ /* Common interface */
+struct pci_iface
+{
+ int (*read) (unsigned bus, unsigned dev, unsigned func, pciaddr_t reg,
+ void *data, unsigned size);
+ int (*write) (unsigned bus, unsigned dev, unsigned func, pciaddr_t reg,
+ const void *data, unsigned size);
+};
+
+struct pci_iface *pci_ifc;
+
+int pci_system_init (void);
+
+#endif /* PCI_ACCESS_H */
diff --git a/pci_arbiter/pci_arbiter.h b/pci_arbiter/pci_arbiter.h
new file mode 100644
index 00000000..73540e8e
--- /dev/null
+++ b/pci_arbiter/pci_arbiter.h
@@ -0,0 +1,46 @@
+/*
+ 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/>.
+*/
+
+/* Translator global declarations */
+
+#ifndef PCI_ARBITER_H
+#define PCI_ARBITER_H
+
+#include <hurd/ports.h>
+
+/* Libports stuff */
+struct port_bucket *pci_bucket;
+
+struct port_class *pci_protid_portclass;
+struct port_class *pci_cntl_portclass;
+
+mach_port_t fsys_identity;
+
+/* Trivfs control structure for lwip. */
+struct trivfs_control *pcicntl;
+
+/* Owner of the underlying node. */
+uid_t pci_owner;
+
+/* Group of the underlying node. */
+uid_t pci_group;
+
+#endif /* PCI_ARBITER_H */
diff --git a/pci_arbiter/x86_pci.c b/pci_arbiter/x86_pci.c
new file mode 100644
index 00000000..abae4938
--- /dev/null
+++ b/pci_arbiter/x86_pci.c
@@ -0,0 +1,331 @@
+/*
+ * 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 access backend.
+ *
+ * Following code is borrowed from libpciaccess:
+ * https://cgit.freedesktop.org/xorg/lib/libpciaccess/
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <x86_pci.h>
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/io.h>
+
+#include <pci_access.h>
+
+static int
+x86_enable_io (void)
+{
+ if (!ioperm (0, 0xffff, 1))
+ return 0;
+ return errno;
+}
+
+static int
+x86_disable_io (void)
+{
+ if (!ioperm (0, 0xffff, 0))
+ return 0;
+ return errno;
+}
+
+static int
+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 int
+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;
+ int 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 int
+pci_system_x86_conf1_write (unsigned bus, unsigned dev, unsigned func,
+ pciaddr_t reg, const void *data, unsigned size)
+{
+ unsigned addr = 0xCFC + (reg & 3);
+ unsigned long sav;
+ int 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 int
+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 int
+pci_system_x86_conf2_read (unsigned bus, unsigned dev, unsigned func,
+ pciaddr_t reg, void *data, unsigned size)
+{
+ unsigned addr = 0xC000 | dev << 8 | reg;
+ int 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 int
+pci_system_x86_conf2_write (unsigned bus, unsigned dev, unsigned func,
+ pciaddr_t reg, const void *data, unsigned size)
+{
+ unsigned addr = 0xC000 | dev << 8 | reg;
+ int 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;
+}
+
+/* Check that this really looks like a PCI configuration. */
+static int
+pci_system_x86_check (struct pci_iface *pci_ifc)
+{
+ 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_ifc->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_ifc->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;
+}
+
+static int
+pci_probe (struct pci_iface *pci_ifc)
+{
+ if (pci_system_x86_conf1_probe () == 0)
+ {
+ pci_ifc->read = pci_system_x86_conf1_read;
+ pci_ifc->write = pci_system_x86_conf1_write;
+ if (pci_system_x86_check (pci_ifc) == 0)
+ return 0;
+ }
+
+ if (pci_system_x86_conf2_probe () == 0)
+ {
+ pci_ifc->read = pci_system_x86_conf2_read;
+ pci_ifc->write = pci_system_x86_conf2_write;
+ if (pci_system_x86_check (pci_ifc) == 0)
+ return 0;
+ }
+
+ return ENODEV;
+}
+
+int
+pci_system_x86_create (void)
+{
+ int ret;
+
+ ret = x86_enable_io ();
+ if (ret)
+ return ret;
+
+ pci_ifc = calloc (1, sizeof (struct pci_iface));
+ if (pci_ifc == NULL)
+ {
+ x86_disable_io ();
+ return ENOMEM;
+ }
+
+ ret = pci_probe (pci_ifc);
+ if (ret)
+ {
+ x86_disable_io ();
+ free (pci_ifc);
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/pci_arbiter/x86_pci.h b/pci_arbiter/x86_pci.h
new file mode 100644
index 00000000..fe507d67
--- /dev/null
+++ b/pci_arbiter/x86_pci.h
@@ -0,0 +1,42 @@
+/*
+ * 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 access backend header.
+ *
+ * Following code is borrowed from libpciaccess:
+ * https://cgit.freedesktop.org/xorg/lib/libpciaccess/
+ */
+
+#ifndef X86_PCI_H
+#define X86_PCI_H
+
+#define PCI_VENDOR_ID 0x00
+#define PCI_VENDOR_ID_COMPAQ 0x0e11
+#define PCI_VENDOR_ID_INTEL 0x8086
+
+#define PCI_CLASS_DEVICE 0x0a
+#define PCI_CLASS_DISPLAY_VGA 0x0300
+#define PCI_CLASS_BRIDGE_HOST 0x0600
+
+int pci_system_x86_create (void);
+
+#endif /* X86_PCI_H */
--
2.14.0
Justus Winter
2017-10-20 13:18:37 UTC
Permalink
Hi Joan :)

let me be the first to say: Awesome :)
Attached are two patches. The first one is to be applied to the Hurd
source tree and contains a new mig pci interface, the pci server and a
new library which includes the mig client stubs.
So I'd say the libpciclient library is a bit superfluous, that could be
done in the Hurd-specific libpcibackend.
The second one is to be applied to the debian libpciaccess repository[1] and adds a new module for the Hurd. There's no patch for pciutils for now.
I didn't get that one.
I've tested them and they seem to be working fine with netdde in my box.
Sweet.
I'd like to have some comments, no need for a deep review, but I had to make some design decisions and wouldn't like to keep working on a wrong approach.
Ok, I don't have access to my box right now, but I can do a quick review
of the Hurdish bits.
------------------
[1] https://anonscm.debian.org/cgit/pkg-xorg/lib/libpciaccess.git/
---
Makefile | 2 +
hurd/hurd_types.defs | 19 ++-
hurd/hurd_types.h | 1 +
hurd/paths.h | 1 +
hurd/pci.defs | 50 +++++++
hurd/subsystems | 1 +
libpciclient/Makefile | 29 ++++
libpciclient/pciclient.c | 103 +++++++++++++++
libpciclient/pciclient.h | 42 ++++++
pci_arbiter/Makefile | 40 ++++++
I'd suggest pci-arbiter.
pci_arbiter/config.h | 5 +
pci_arbiter/main.c | 116 ++++++++++++++++
pci_arbiter/mig-mutate.h | 28 ++++
pci_arbiter/pci-ops.c | 89 +++++++++++++
pci_arbiter/pci_access.c | 51 +++++++
pci_arbiter/pci_access.h | 78 +++++++++++
pci_arbiter/pci_arbiter.h | 46 +++++++
pci_arbiter/x86_pci.c | 331 ++++++++++++++++++++++++++++++++++++++++++++++
pci_arbiter/x86_pci.h | 42 ++++++
19 files changed, 1073 insertions(+), 1 deletion(-)
create mode 100644 hurd/pci.defs
create mode 100644 libpciclient/Makefile
create mode 100644 libpciclient/pciclient.c
create mode 100644 libpciclient/pciclient.h
create mode 100644 pci_arbiter/Makefile
create mode 100644 pci_arbiter/config.h
create mode 100644 pci_arbiter/main.c
create mode 100644 pci_arbiter/mig-mutate.h
create mode 100644 pci_arbiter/pci-ops.c
create mode 100644 pci_arbiter/pci_access.c
create mode 100644 pci_arbiter/pci_access.h
create mode 100644 pci_arbiter/pci_arbiter.h
create mode 100644 pci_arbiter/x86_pci.c
create mode 100644 pci_arbiter/x86_pci.h
diff --git a/Makefile b/Makefile
index 119f130b..d3d93b87 100644
--- a/Makefile
+++ b/Makefile
@@ -31,6 +31,7 @@ lib-subdirs = libshouldbeinlibc libihash libiohelp libports libthreads \
libnetfs libpipe libstore libhurdbugaddr libftpconn libcons \
libhurd-slab \
libbpf \
+ libpciclient
# Hurd programs
prog-subdirs = auth proc exec term \
@@ -45,6 +46,7 @@ prog-subdirs = auth proc exec term \
init \
devnode \
eth-multiplexer \
+ pci_arbiter
ifeq ($(HAVE_SUN_RPC),yes)
prog-subdirs += nfs nfsd
diff --git a/hurd/hurd_types.defs b/hurd/hurd_types.defs
index 4d7013c8..0e9b990e 100644
--- a/hurd/hurd_types.defs
+++ b/hurd/hurd_types.defs
@@ -1,5 +1,5 @@
/* MiG type declarations for Hurd interfaces -*- C -*-
- Copyright (C) 1993,94,95,96,98,2001,02 Free Software Foundation, Inc.
+ Copyright (C) 1993,94,95,96,98,2001,02,17 Free Software Foundation, Inc.
This file is part of the GNU Hurd.
@@ -296,6 +296,23 @@ destructor: INTERRUPT_DESTRUCTOR
#endif
;
+/* PCI arbiter */
+type pci_t = mach_port_copy_send_t
+#ifdef PCI_INTRAN
+intran: PCI_INTRAN
+intranpayload: PCI_INTRAN_PAYLOAD
+#else
+#ifdef HURD_DEFAULT_PAYLOAD_TO_PORT
+intranpayload: pci_t HURD_DEFAULT_PAYLOAD_TO_PORT
+#endif
+#endif
+#ifdef PCI_OUTTRAN
+outtran: PCI_OUTTRAN
+#endif
+#ifdef PCI_DESTRUCTOR
+destructor: PCI_DESTRUCTOR
+#endif
+;
type proccoll_t = mach_port_copy_send_t;
diff --git a/hurd/hurd_types.h b/hurd/hurd_types.h
index 2960a294..8b1092a5 100644
--- a/hurd/hurd_types.h
+++ b/hurd/hurd_types.h
@@ -50,6 +50,7 @@ typedef mach_port_t exec_startup_t;
typedef mach_port_t interrupt_t;
typedef mach_port_t proccoll_t;
typedef mach_port_t ctty_t;
+typedef mach_port_t pci_t;
#include <errno.h> /* Defines `error_t'. */
diff --git a/hurd/paths.h b/hurd/paths.h
index e1b00e90..1484b43e 100644
--- a/hurd/paths.h
+++ b/hurd/paths.h
@@ -30,6 +30,7 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#define _SERVERS_PROC _SERVERS "proc"
#define _SERVERS_PASSWORD _SERVERS "password"
#define _SERVERS_DEFPAGER _SERVERS "default-pager"
+#define _SERVERS_PCI _SERVERS "pci"
/* Directory containing naming points for socket servers.
Entries are named by the string representing the domain number
diff --git a/hurd/pci.defs b/hurd/pci.defs
new file mode 100644
index 00000000..d051bd69
--- /dev/null
+++ b/hurd/pci.defs
@@ -0,0 +1,50 @@
+/* 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
+#endif
+
+INTR_INTERFACE
Leftover here.
+
+/* Read 'amount' bytes from B/D/F + 'reg' and store it in 'data' */
+routine pci_read (
+ master: pci_t;
+ bus: int;
+ dev: int;
+ func: int;
+ reg: int;
+ out data: data_t, dealloc;
+ amount: vm_size_t
+);
+
+/* Write 'amount' bytes from 'data' to B/D/F + 'reg' */
+routine pci_write(
+ master: pci_t;
+ bus: int;
+ dev: int;
+ func: int;
+ reg: int;
+ data: data_t;
+ out amount: vm_size_t
+);
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)
Looks good otherwise. I can't judge the interface, but that is easy
enough to extend later.
diff --git a/libpciclient/Makefile b/libpciclient/Makefile
new file mode 100644
index 00000000..d550115d
--- /dev/null
+++ b/libpciclient/Makefile
@@ -0,0 +1,29 @@
+# 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 := libpciclient
+makemode := library
+
+libname = libpciclient
+installhdrs = pciclient.h
+
+SRCS = pciclient.c
+
+MIGSTUBS = pciUser.o
+OBJS = $(sort $(SRCS:.c=.o)) $(MIGSTUBS)
+
+include ../Makeconf
diff --git a/libpciclient/pciclient.c b/libpciclient/pciclient.c
new file mode 100644
index 00000000..9f03dc30
--- /dev/null
+++ b/libpciclient/pciclient.c
@@ -0,0 +1,103 @@
+/*
+ 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 Client implementation */
+
+#include <pciclient.h>
+
+#include <hurd.h>
+#include <hurd/paths.h>
+#include <error.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <string.h>
+
+#include <pci_U.h>
+
+/* Get the server port */
+static void
+init_client ()
+{
+ pci_server_port = file_name_lookup (_SERVERS_PCI, 0, 0);
+
+ if (pci_server_port == MACH_PORT_NULL)
+ error (-1, errno, "Could not open file `%s'", _SERVERS_PCI);
+
+ return;
+}
+
+/*
+ * Read 'size' bytes from B/D/F + reg and store them in 'data'.
+ *
+ * It's assumed that 'size' bytes are allocated in 'data'
+ */
+int
+pciclient_cfg_read (int bus, int dev, int func, int reg, char *buf,
+ size_t * nbytes)
+{
+ error_t err;
+ size_t nread;
+ char *data;
+
+ if (pci_server_port == MACH_PORT_NULL)
+ init_client ();
+
+ data = buf;
+ nread = *nbytes;
+ err =
+ pci_read (pci_server_port, bus, dev, func, reg, &data, &nread, *nbytes);
+ if (err)
+ return err;
+
+ if (data != buf)
+ {
+ if (nread > *nbytes) /* Sanity check for bogus server. */
+ {
+ vm_deallocate (mach_task_self (), (vm_address_t) data, nread);
+ return EGRATUITOUS;
+ }
+
+ memcpy (buf, data, nread);
+ vm_deallocate (mach_task_self (), (vm_address_t) data, nread);
+ }
+
+ *nbytes = nread;
+
+ return 0;
+}
+
+/* Write 'size' bytes from 'data' to B/D/F + reg */
+int
+pciclient_cfg_write (int bus, int dev, int func, int reg, char *buf,
+ size_t * nbytes)
+{
+ error_t err;
+ size_t nwrote;
+
+ if (pci_server_port == MACH_PORT_NULL)
+ init_client ();
+
+ err =
+ pci_write (pci_server_port, bus, dev, func, reg, buf, *nbytes, &nwrote);
+
+ if (!err)
+ *nbytes = nwrote;
+
+ return err;
+}
I don't see these functions adding anything over just doing that by
foot in libpciaccess.
diff --git a/libpciclient/pciclient.h b/libpciclient/pciclient.h
new file mode 100644
index 00000000..5148ba25
--- /dev/null
+++ b/libpciclient/pciclient.h
@@ -0,0 +1,42 @@
+/*
+ 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/>.
+*/
+
+/* PCI client header */
+
+#ifndef PCI_CLIENT_H
+#define PCI_CLIENT_H
+
+#include <mach/port.h>
+#include <sys/types.h>
+
+/* Server port */
+mach_port_t pci_server_port = MACH_PORT_NULL;
+
+/* Public function declarations */
+int
+pciclient_cfg_read (int bus, int dev, int func, int reg, char *buf,
+ size_t * nbytes);
+
+int
+pciclient_cfg_write (int bus, int dev, int func, int reg, char *buf,
+ size_t * nbytes);
+
+#endif /* PCI_CLIENT_H */
diff --git a/pci_arbiter/Makefile b/pci_arbiter/Makefile
new file mode 100644
index 00000000..e3a3b732
--- /dev/null
+++ b/pci_arbiter/Makefile
@@ -0,0 +1,40 @@
+# 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
+MIGSRCS = pciServer.c
+OBJS = $(patsubst %.S,%.o,$(patsubst %.c,%.o, $(SRCS) $(MIGSRCS)))
+
+HURDLIBS= trivfs fshelp ports shouldbeinlibc
+
+target = pci_arbiter
+
+include ../Makeconf
+
+CFLAGS += -I$(PORTDIR)/include
+
+CPPFLAGS += -imacros $(srcdir)/config.h
+pci-MIGSFLAGS = -imacros $(srcdir)/mig-mutate.h
+
+# cpp doesn't automatically make dependencies for -imacros dependencies. argh.
+pci_S.h pciServer.c: mig-mutate.h
+$(OBJS): config.h
diff --git a/pci_arbiter/config.h b/pci_arbiter/config.h
new file mode 100644
index 00000000..b0d5196d
--- /dev/null
+++ b/pci_arbiter/config.h
@@ -0,0 +1,5 @@
+#define __KERNEL__ 1
+#undef __SMP__
+
+#define _HURD_ 1
+
diff --git a/pci_arbiter/main.c b/pci_arbiter/main.c
new file mode 100644
index 00000000..5475382a
--- /dev/null
+++ b/pci_arbiter/main.c
@@ -0,0 +1,116 @@
+/*
+ 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 <pci_arbiter.h>
+
+#include <error.h>
+#include <fcntl.h>
+#include <hurd/trivfs.h>
+
+#include <pci_access.h>
+#include <pci_S.h>
+
+int trivfs_fstype = FSTYPE_MISC;
+int trivfs_fsid = 0;
+int trivfs_support_read = 0;
+int trivfs_support_write = 0;
+int trivfs_support_exec = 0;
+int trivfs_allow_open = O_READ | O_WRITE;
+
+void
+trivfs_modify_stat (struct trivfs_protid *cred, io_statbuf_t * st)
+{
+}
+
+error_t
+trivfs_goaway (struct trivfs_control *fsys, int flags)
+{
+ exit (0);
+}
+
+int
+pci_demuxer (mach_msg_header_t * inp, mach_msg_header_t * outp)
+{
+ mig_routine_t routine;
+ if ((routine = pci_server_routine (inp)) ||
+ (routine = NULL, trivfs_demuxer (inp, outp)))
+ {
+ if (routine)
+ (*routine) (inp, outp);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ struct stat st;
+ mach_port_t bootstrap;
+
+ pci_bucket = ports_create_bucket ();
+
+ mach_port_allocate (mach_task_self (),
+ MACH_PORT_RIGHT_RECEIVE, &fsys_identity);
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap == MACH_PORT_NULL)
+ error (-1, 0, "Must be started as a translator");
+
+ err = trivfs_add_protid_port_class (&pci_protid_portclass);
+ if (err)
+ error (1, err, "Error creating protid port class");
+
+ err = trivfs_add_control_port_class (&pci_cntl_portclass);
+ if (err)
+ error (1, err, "Error creating control port class");
+
+ /* Reply to our parent */
+ err = trivfs_startup (bootstrap, 0,
+ pci_cntl_portclass,
+ pci_bucket,
+ pci_protid_portclass, pci_bucket, &pcicntl);
+ mach_port_deallocate (mach_task_self (), bootstrap);
+ if (err)
+ {
+ return (-1);
+ }
+
+ /* Initialize status from underlying node. */
+ pci_owner = pci_group = 0;
+ err = io_stat (pcicntl->underlying, &st);
+ if (!err)
+ {
+ pci_owner = st.st_uid;
+ pci_group = st.st_gid;
+ }
+
+ /* Start the PCI system */
+ err = pci_system_init ();
+ if (err)
+ error (1, err, "Error starting the PCI system");
+
+ ports_manage_port_operations_one_thread (pci_bucket, pci_demuxer, 0);
Maybe add a comment here that we rely on the one threaded nature of the
server for synchronization.
+
+ return 0;
+}
diff --git a/pci_arbiter/mig-mutate.h b/pci_arbiter/mig-mutate.h
new file mode 100644
index 00000000..df68fb05
--- /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 "../libtrivfs/mig-decls.h"; \
+
+#define PCI_INTRAN trivfs_protid_t trivfs_begin_using_protid (pci_t)
+#define PCI_INTRAN_PAYLOAD trivfs_protid_t trivfs_begin_using_protid_payload
+#define PCI_DESTRUCTOR trivfs_end_using_protid (trivfs_protid_t)
diff --git a/pci_arbiter/pci-ops.c b/pci_arbiter/pci-ops.c
new file mode 100644
index 00000000..f29b58d0
--- /dev/null
+++ b/pci_arbiter/pci-ops.c
@@ -0,0 +1,89 @@
+/*
+ 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 <hurd/fshelp.h>
+
+#include <pci_arbiter.h>
+#include <pci_access.h>
+
+error_t
+S_pci_read (struct trivfs_protid *master, int bus, int dev, int func,
+ int reg, char **data, size_t * datalen,
+ mach_msg_type_number_t amount)
+{
+ error_t err;
+
+ if (!master)
+ return EOPNOTSUPP;
+
+ if (!master->isroot)
+ {
+ struct stat st;
+
+ st.st_uid = pci_owner;
+ st.st_gid = pci_group;
+
+ err = fshelp_isowner (&st, master->user);
+ if (err)
+ return EPERM;
+ }
+
+ if (amount > *datalen)
+ amount = *datalen;
+
+ err = pci_ifc->read (bus, dev, func, reg, *data, amount);
+
+ if(!err)
+ *datalen = amount;
+
+ return err;
+}
+
+error_t
+S_pci_write (struct trivfs_protid * master, int bus, int dev, int func,
+ int reg, char *data, size_t datalen,
+ mach_msg_type_number_t * amount)
+{
+ error_t err;
+
+ if (!master)
+ return EOPNOTSUPP;
+
+ if (!master->isroot)
+ {
+ struct stat st;
+
+ st.st_uid = pci_owner;
+ st.st_gid = pci_group;
+
+ err = fshelp_isowner (&st, master->user);
+ if (err)
+ return EPERM;
+ }
+
+ err = pci_ifc->write (bus, dev, func, reg, data, datalen);
+
+ *amount = datalen;
+
+ return err;
+}
The server code looks good.


Cheers :)
Justus
Joan Lledó
2017-10-21 16:54:53 UTC
Permalink
Hi!
Post by Justus Winter
Hi Joan :)
let me be the first to say: Awesome :)
Attached are two patches. The first one is to be applied to the Hurd
source tree and contains a new mig pci interface, the pci server and a
new library which includes the mig client stubs.
So I'd say the libpciclient library is a bit superfluous, that could be
done in the Hurd-specific libpcibackend.
What do you mean with libpcibackend? Adding that code to libpciaccess
and pciutils? If so, I was talking to Samuel about that (irc channel
at 2017-09-28) and he suggested me to create this library to avoid
adding mig stuff to pciutils and libpciaccess.
Post by Justus Winter
The second one is to be applied to the debian libpciaccess repository[1] and adds a new module for the Hurd. There's no patch for pciutils for now.
I didn't get that one.
Yes, don't know what happened. I've uploaded it to darnassus[1].
Post by Justus Winter
I've tested them and they seem to be working fine with netdde in my box.
Sweet.
I'd like to have some comments, no need for a deep review, but I had to make some design decisions and wouldn't like to keep working on a wrong approach.
Ok, I don't have access to my box right now, but I can do a quick review
of the Hurdish bits.
------------------
[1] https://anonscm.debian.org/cgit/pkg-xorg/lib/libpciaccess.git/
---
Makefile | 2 +
hurd/hurd_types.defs | 19 ++-
hurd/hurd_types.h | 1 +
hurd/paths.h | 1 +
hurd/pci.defs | 50 +++++++
hurd/subsystems | 1 +
libpciclient/Makefile | 29 ++++
libpciclient/pciclient.c | 103 +++++++++++++++
libpciclient/pciclient.h | 42 ++++++
pci_arbiter/Makefile | 40 ++++++
I'd suggest pci-arbiter.
pci_arbiter/config.h | 5 +
pci_arbiter/main.c | 116 ++++++++++++++++
pci_arbiter/mig-mutate.h | 28 ++++
pci_arbiter/pci-ops.c | 89 +++++++++++++
pci_arbiter/pci_access.c | 51 +++++++
pci_arbiter/pci_access.h | 78 +++++++++++
pci_arbiter/pci_arbiter.h | 46 +++++++
pci_arbiter/x86_pci.c | 331 ++++++++++++++++++++++++++++++++++++++++++++++
pci_arbiter/x86_pci.h | 42 ++++++
19 files changed, 1073 insertions(+), 1 deletion(-)
create mode 100644 hurd/pci.defs
create mode 100644 libpciclient/Makefile
create mode 100644 libpciclient/pciclient.c
create mode 100644 libpciclient/pciclient.h
create mode 100644 pci_arbiter/Makefile
create mode 100644 pci_arbiter/config.h
create mode 100644 pci_arbiter/main.c
create mode 100644 pci_arbiter/mig-mutate.h
create mode 100644 pci_arbiter/pci-ops.c
create mode 100644 pci_arbiter/pci_access.c
create mode 100644 pci_arbiter/pci_access.h
create mode 100644 pci_arbiter/pci_arbiter.h
create mode 100644 pci_arbiter/x86_pci.c
create mode 100644 pci_arbiter/x86_pci.h
diff --git a/Makefile b/Makefile
index 119f130b..d3d93b87 100644
--- a/Makefile
+++ b/Makefile
@@ -31,6 +31,7 @@ lib-subdirs = libshouldbeinlibc libihash libiohelp libports libthreads \
libnetfs libpipe libstore libhurdbugaddr libftpconn libcons \
libhurd-slab \
libbpf \
+ libpciclient
# Hurd programs
prog-subdirs = auth proc exec term \
@@ -45,6 +46,7 @@ prog-subdirs = auth proc exec term \
init \
devnode \
eth-multiplexer \
+ pci_arbiter
ifeq ($(HAVE_SUN_RPC),yes)
prog-subdirs += nfs nfsd
diff --git a/hurd/hurd_types.defs b/hurd/hurd_types.defs
index 4d7013c8..0e9b990e 100644
--- a/hurd/hurd_types.defs
+++ b/hurd/hurd_types.defs
@@ -1,5 +1,5 @@
/* MiG type declarations for Hurd interfaces -*- C -*-
- Copyright (C) 1993,94,95,96,98,2001,02 Free Software Foundation, Inc.
+ Copyright (C) 1993,94,95,96,98,2001,02,17 Free Software Foundation, Inc.
This file is part of the GNU Hurd.
@@ -296,6 +296,23 @@ destructor: INTERRUPT_DESTRUCTOR
#endif
;
+/* PCI arbiter */
+type pci_t = mach_port_copy_send_t
+#ifdef PCI_INTRAN
+intran: PCI_INTRAN
+intranpayload: PCI_INTRAN_PAYLOAD
+#else
+#ifdef HURD_DEFAULT_PAYLOAD_TO_PORT
+intranpayload: pci_t HURD_DEFAULT_PAYLOAD_TO_PORT
+#endif
+#endif
+#ifdef PCI_OUTTRAN
+outtran: PCI_OUTTRAN
+#endif
+#ifdef PCI_DESTRUCTOR
+destructor: PCI_DESTRUCTOR
+#endif
+;
type proccoll_t = mach_port_copy_send_t;
diff --git a/hurd/hurd_types.h b/hurd/hurd_types.h
index 2960a294..8b1092a5 100644
--- a/hurd/hurd_types.h
+++ b/hurd/hurd_types.h
@@ -50,6 +50,7 @@ typedef mach_port_t exec_startup_t;
typedef mach_port_t interrupt_t;
typedef mach_port_t proccoll_t;
typedef mach_port_t ctty_t;
+typedef mach_port_t pci_t;
#include <errno.h> /* Defines `error_t'. */
diff --git a/hurd/paths.h b/hurd/paths.h
index e1b00e90..1484b43e 100644
--- a/hurd/paths.h
+++ b/hurd/paths.h
@@ -30,6 +30,7 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#define _SERVERS_PROC _SERVERS "proc"
#define _SERVERS_PASSWORD _SERVERS "password"
#define _SERVERS_DEFPAGER _SERVERS "default-pager"
+#define _SERVERS_PCI _SERVERS "pci"
/* Directory containing naming points for socket servers.
Entries are named by the string representing the domain number
diff --git a/hurd/pci.defs b/hurd/pci.defs
new file mode 100644
index 00000000..d051bd69
--- /dev/null
+++ b/hurd/pci.defs
@@ -0,0 +1,50 @@
+/* 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
+#endif
+
+INTR_INTERFACE
Leftover here.
+
+/* Read 'amount' bytes from B/D/F + 'reg' and store it in 'data' */
+routine pci_read (
+ master: pci_t;
+ bus: int;
+ dev: int;
+ func: int;
+ reg: int;
+ out data: data_t, dealloc;
+ amount: vm_size_t
+);
+
+/* Write 'amount' bytes from 'data' to B/D/F + 'reg' */
+routine pci_write(
+ master: pci_t;
+ bus: int;
+ dev: int;
+ func: int;
+ reg: int;
+ data: data_t;
+ out amount: vm_size_t
+);
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)
Looks good otherwise. I can't judge the interface, but that is easy
enough to extend later.
diff --git a/libpciclient/Makefile b/libpciclient/Makefile
new file mode 100644
index 00000000..d550115d
--- /dev/null
+++ b/libpciclient/Makefile
@@ -0,0 +1,29 @@
+# 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 := libpciclient
+makemode := library
+
+libname = libpciclient
+installhdrs = pciclient.h
+
+SRCS = pciclient.c
+
+MIGSTUBS = pciUser.o
+OBJS = $(sort $(SRCS:.c=.o)) $(MIGSTUBS)
+
+include ../Makeconf
diff --git a/libpciclient/pciclient.c b/libpciclient/pciclient.c
new file mode 100644
index 00000000..9f03dc30
--- /dev/null
+++ b/libpciclient/pciclient.c
@@ -0,0 +1,103 @@
+/*
+ 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 Client implementation */
+
+#include <pciclient.h>
+
+#include <hurd.h>
+#include <hurd/paths.h>
+#include <error.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <string.h>
+
+#include <pci_U.h>
+
+/* Get the server port */
+static void
+init_client ()
+{
+ pci_server_port = file_name_lookup (_SERVERS_PCI, 0, 0);
+
+ if (pci_server_port == MACH_PORT_NULL)
+ error (-1, errno, "Could not open file `%s'", _SERVERS_PCI);
+
+ return;
+}
+
+/*
+ * Read 'size' bytes from B/D/F + reg and store them in 'data'.
+ *
+ * It's assumed that 'size' bytes are allocated in 'data'
+ */
+int
+pciclient_cfg_read (int bus, int dev, int func, int reg, char *buf,
+ size_t * nbytes)
+{
+ error_t err;
+ size_t nread;
+ char *data;
+
+ if (pci_server_port == MACH_PORT_NULL)
+ init_client ();
+
+ data = buf;
+ nread = *nbytes;
+ err =
+ pci_read (pci_server_port, bus, dev, func, reg, &data, &nread, *nbytes);
+ if (err)
+ return err;
+
+ if (data != buf)
+ {
+ if (nread > *nbytes) /* Sanity check for bogus server. */
+ {
+ vm_deallocate (mach_task_self (), (vm_address_t) data, nread);
+ return EGRATUITOUS;
+ }
+
+ memcpy (buf, data, nread);
+ vm_deallocate (mach_task_self (), (vm_address_t) data, nread);
+ }
+
+ *nbytes = nread;
+
+ return 0;
+}
+
+/* Write 'size' bytes from 'data' to B/D/F + reg */
+int
+pciclient_cfg_write (int bus, int dev, int func, int reg, char *buf,
+ size_t * nbytes)
+{
+ error_t err;
+ size_t nwrote;
+
+ if (pci_server_port == MACH_PORT_NULL)
+ init_client ();
+
+ err =
+ pci_write (pci_server_port, bus, dev, func, reg, buf, *nbytes, &nwrote);
+
+ if (!err)
+ *nbytes = nwrote;
+
+ return err;
+}
I don't see these functions adding anything over just doing that by
foot in libpciaccess.
I'm not sure to understand what you mean, it's the same as before,
right? you're suggesting to move this code to libpciaccess. If so, as
I said above, the only purpose of this library is to avoid including
the mig stuff in libpciaccess and pciutils (or that's what I
understood :P).
Post by Justus Winter
diff --git a/libpciclient/pciclient.h b/libpciclient/pciclient.h
new file mode 100644
index 00000000..5148ba25
--- /dev/null
+++ b/libpciclient/pciclient.h
@@ -0,0 +1,42 @@
+/*
+ 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/>.
+*/
+
+/* PCI client header */
+
+#ifndef PCI_CLIENT_H
+#define PCI_CLIENT_H
+
+#include <mach/port.h>
+#include <sys/types.h>
+
+/* Server port */
+mach_port_t pci_server_port = MACH_PORT_NULL;
+
+/* Public function declarations */
+int
+pciclient_cfg_read (int bus, int dev, int func, int reg, char *buf,
+ size_t * nbytes);
+
+int
+pciclient_cfg_write (int bus, int dev, int func, int reg, char *buf,
+ size_t * nbytes);
+
+#endif /* PCI_CLIENT_H */
diff --git a/pci_arbiter/Makefile b/pci_arbiter/Makefile
new file mode 100644
index 00000000..e3a3b732
--- /dev/null
+++ b/pci_arbiter/Makefile
@@ -0,0 +1,40 @@
+# 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
+MIGSRCS = pciServer.c
+OBJS = $(patsubst %.S,%.o,$(patsubst %.c,%.o, $(SRCS) $(MIGSRCS)))
+
+HURDLIBS= trivfs fshelp ports shouldbeinlibc
+
+target = pci_arbiter
+
+include ../Makeconf
+
+CFLAGS += -I$(PORTDIR)/include
+
+CPPFLAGS += -imacros $(srcdir)/config.h
+pci-MIGSFLAGS = -imacros $(srcdir)/mig-mutate.h
+
+# cpp doesn't automatically make dependencies for -imacros dependencies. argh.
+pci_S.h pciServer.c: mig-mutate.h
+$(OBJS): config.h
diff --git a/pci_arbiter/config.h b/pci_arbiter/config.h
new file mode 100644
index 00000000..b0d5196d
--- /dev/null
+++ b/pci_arbiter/config.h
@@ -0,0 +1,5 @@
+#define __KERNEL__ 1
+#undef __SMP__
+
+#define _HURD_ 1
+
diff --git a/pci_arbiter/main.c b/pci_arbiter/main.c
new file mode 100644
index 00000000..5475382a
--- /dev/null
+++ b/pci_arbiter/main.c
@@ -0,0 +1,116 @@
+/*
+ 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 <pci_arbiter.h>
+
+#include <error.h>
+#include <fcntl.h>
+#include <hurd/trivfs.h>
+
+#include <pci_access.h>
+#include <pci_S.h>
+
+int trivfs_fstype = FSTYPE_MISC;
+int trivfs_fsid = 0;
+int trivfs_support_read = 0;
+int trivfs_support_write = 0;
+int trivfs_support_exec = 0;
+int trivfs_allow_open = O_READ | O_WRITE;
+
+void
+trivfs_modify_stat (struct trivfs_protid *cred, io_statbuf_t * st)
+{
+}
+
+error_t
+trivfs_goaway (struct trivfs_control *fsys, int flags)
+{
+ exit (0);
+}
+
+int
+pci_demuxer (mach_msg_header_t * inp, mach_msg_header_t * outp)
+{
+ mig_routine_t routine;
+ if ((routine = pci_server_routine (inp)) ||
+ (routine = NULL, trivfs_demuxer (inp, outp)))
+ {
+ if (routine)
+ (*routine) (inp, outp);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ struct stat st;
+ mach_port_t bootstrap;
+
+ pci_bucket = ports_create_bucket ();
+
+ mach_port_allocate (mach_task_self (),
+ MACH_PORT_RIGHT_RECEIVE, &fsys_identity);
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap == MACH_PORT_NULL)
+ error (-1, 0, "Must be started as a translator");
+
+ err = trivfs_add_protid_port_class (&pci_protid_portclass);
+ if (err)
+ error (1, err, "Error creating protid port class");
+
+ err = trivfs_add_control_port_class (&pci_cntl_portclass);
+ if (err)
+ error (1, err, "Error creating control port class");
+
+ /* Reply to our parent */
+ err = trivfs_startup (bootstrap, 0,
+ pci_cntl_portclass,
+ pci_bucket,
+ pci_protid_portclass, pci_bucket, &pcicntl);
+ mach_port_deallocate (mach_task_self (), bootstrap);
+ if (err)
+ {
+ return (-1);
+ }
+
+ /* Initialize status from underlying node. */
+ pci_owner = pci_group = 0;
+ err = io_stat (pcicntl->underlying, &st);
+ if (!err)
+ {
+ pci_owner = st.st_uid;
+ pci_group = st.st_gid;
+ }
+
+ /* Start the PCI system */
+ err = pci_system_init ();
+ if (err)
+ error (1, err, "Error starting the PCI system");
+
+ ports_manage_port_operations_one_thread (pci_bucket, pci_demuxer, 0);
Maybe add a comment here that we rely on the one threaded nature of the
server for synchronization.
+
+ return 0;
+}
diff --git a/pci_arbiter/mig-mutate.h b/pci_arbiter/mig-mutate.h
new file mode 100644
index 00000000..df68fb05
--- /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 "../libtrivfs/mig-decls.h"; \
+
+#define PCI_INTRAN trivfs_protid_t trivfs_begin_using_protid (pci_t)
+#define PCI_INTRAN_PAYLOAD trivfs_protid_t trivfs_begin_using_protid_payload
+#define PCI_DESTRUCTOR trivfs_end_using_protid (trivfs_protid_t)
diff --git a/pci_arbiter/pci-ops.c b/pci_arbiter/pci-ops.c
new file mode 100644
index 00000000..f29b58d0
--- /dev/null
+++ b/pci_arbiter/pci-ops.c
@@ -0,0 +1,89 @@
+/*
+ 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 <hurd/fshelp.h>
+
+#include <pci_arbiter.h>
+#include <pci_access.h>
+
+error_t
+S_pci_read (struct trivfs_protid *master, int bus, int dev, int func,
+ int reg, char **data, size_t * datalen,
+ mach_msg_type_number_t amount)
+{
+ error_t err;
+
+ if (!master)
+ return EOPNOTSUPP;
+
+ if (!master->isroot)
+ {
+ struct stat st;
+
+ st.st_uid = pci_owner;
+ st.st_gid = pci_group;
+
+ err = fshelp_isowner (&st, master->user);
+ if (err)
+ return EPERM;
+ }
+
+ if (amount > *datalen)
+ amount = *datalen;
+
+ err = pci_ifc->read (bus, dev, func, reg, *data, amount);
+
+ if(!err)
+ *datalen = amount;
+
+ return err;
+}
+
+error_t
+S_pci_write (struct trivfs_protid * master, int bus, int dev, int func,
+ int reg, char *data, size_t datalen,
+ mach_msg_type_number_t * amount)
+{
+ error_t err;
+
+ if (!master)
+ return EOPNOTSUPP;
+
+ if (!master->isroot)
+ {
+ struct stat st;
+
+ st.st_uid = pci_owner;
+ st.st_gid = pci_group;
+
+ err = fshelp_isowner (&st, master->user);
+ if (err)
+ return EPERM;
+ }
+
+ err = pci_ifc->write (bus, dev, func, reg, data, datalen);
+
+ *amount = datalen;
+
+ return err;
+}
The server code looks good.
Cheers :)
Justus
OK, I'll add your comments to my todo. Thanks!

-------------------------------------------------
[1] http://darnassus.sceen.net/~jlledom/patches/0001-New-module-for-the-Hurd.patch
Justus Winter
2017-10-22 08:55:13 UTC
Permalink
Post by Joan Lledó
Hi!
Post by Justus Winter
Hi Joan :)
let me be the first to say: Awesome :)
Attached are two patches. The first one is to be applied to the Hurd
source tree and contains a new mig pci interface, the pci server and a
new library which includes the mig client stubs.
So I'd say the libpciclient library is a bit superfluous, that could be
done in the Hurd-specific libpcibackend.
What do you mean with libpcibackend? Adding that code to libpciaccess
and pciutils?
Yes, sorry, that is exactly what I meant.
Post by Joan Lledó
If so, I was talking to Samuel about that (irc channel at 2017-09-28)
and he suggested me to create this library to avoid adding mig stuff
to pciutils and libpciaccess.
I see, but I'm confused now. Are we not going to add the client stubs
to libhurduser and be done with that?
Post by Joan Lledó
Post by Justus Winter
I don't see these functions adding anything over just doing that by
foot in libpciaccess.
I'm not sure to understand what you mean, it's the same as before,
right? you're suggesting to move this code to libpciaccess.
Yes.

Cheers :)
Justus
Samuel Thibault
2017-10-22 16:37:27 UTC
Permalink
Hello,
Post by Justus Winter
Post by Joan Lledó
Post by Justus Winter
let me be the first to say: Awesome :)
Same :)
Post by Justus Winter
Post by Joan Lledó
If so, I was talking to Samuel about that (irc channel at 2017-09-28)
and he suggested me to create this library to avoid adding mig stuff
to pciutils and libpciaccess.
I see, but I'm confused now. Are we not going to add the client stubs
to libhurduser and be done with that?
I guess there was a misunderstanding on IRC :)

We indeed don't want to introduce mig build stuff in
pciutils/pciaccess. But we are fine with using RPCs as they are provided
by libhurduser. So that basically you can just put your libpciclient
code into pciaccess and pciutils.

Samuel
Joan Lledó
2017-10-24 11:21:34 UTC
Permalink
Hi,

yes, there was a misunderstanding as I didn't know what libhurduser was.

Here're two new patches, now there's no libpciclient and libpciaccess uses
libhurduser.
Joan Lledó
2017-10-24 11:21:35 UTC
Permalink
---
Makefile | 1 +
hurd/hurd_types.defs | 19 ++-
hurd/hurd_types.h | 1 +
hurd/paths.h | 1 +
hurd/pci_conf.defs | 50 +++++++
hurd/subsystems | 1 +
pci_arbiter/Makefile | 40 ++++++
pci_arbiter/config.h | 5 +
pci_arbiter/main.c | 116 ++++++++++++++++
pci_arbiter/mig-mutate.h | 28 ++++
pci_arbiter/pci_access.c | 51 +++++++
pci_arbiter/pci_access.h | 78 +++++++++++
pci_arbiter/pci_arbiter.h | 46 +++++++
pci_arbiter/pci_conf-ops.c | 90 ++++++++++++
pci_arbiter/x86_pci.c | 331 +++++++++++++++++++++++++++++++++++++++++++++
pci_arbiter/x86_pci.h | 42 ++++++
16 files changed, 899 insertions(+), 1 deletion(-)
create mode 100644 hurd/pci_conf.defs
create mode 100644 pci_arbiter/Makefile
create mode 100644 pci_arbiter/config.h
create mode 100644 pci_arbiter/main.c
create mode 100644 pci_arbiter/mig-mutate.h
create mode 100644 pci_arbiter/pci_access.c
create mode 100644 pci_arbiter/pci_access.h
create mode 100644 pci_arbiter/pci_arbiter.h
create mode 100644 pci_arbiter/pci_conf-ops.c
create mode 100644 pci_arbiter/x86_pci.c
create mode 100644 pci_arbiter/x86_pci.h

diff --git a/Makefile b/Makefile
index 119f130b..a46f0140 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..53fad15c 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_conf_t = mach_port_copy_send_t
+#ifdef PCI_CONF_INTRAN
+intran: PCI_CONF_INTRAN
+intranpayload: PCI_CONF_INTRAN_PAYLOAD
+#else
+#ifdef HURD_DEFAULT_PAYLOAD_TO_PORT
+intranpayload: pci_conf_t HURD_DEFAULT_PAYLOAD_TO_PORT
+#endif
+#endif
+#ifdef PCI_CONF_OUTTRAN
+outtran: PCI_CONF_OUTTRAN
+#endif
+#ifdef PCI_CONF_DESTRUCTOR
+destructor: PCI_CONF_DESTRUCTOR
+#endif
+;

type proccoll_t = mach_port_copy_send_t;

diff --git a/hurd/hurd_types.h b/hurd/hurd_types.h
index 2960a294..db03ec31 100644
--- a/hurd/hurd_types.h
+++ b/hurd/hurd_types.h
@@ -50,6 +50,7 @@ typedef mach_port_t exec_startup_t;
typedef mach_port_t interrupt_t;
typedef mach_port_t proccoll_t;
typedef mach_port_t ctty_t;
+typedef mach_port_t pci_conf_t;

#include <errno.h> /* Defines `error_t'. */

diff --git a/hurd/paths.h b/hurd/paths.h
index e1b00e90..e4da819a 100644
--- a/hurd/paths.h
+++ b/hurd/paths.h
@@ -30,6 +30,7 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#define _SERVERS_PROC _SERVERS "proc"
#define _SERVERS_PASSWORD _SERVERS "password"
#define _SERVERS_DEFPAGER _SERVERS "default-pager"
+#define _SERVERS_PCI_CONF _SERVERS "pci"

/* Directory containing naming points for socket servers.
Entries are named by the string representing the domain number
diff --git a/hurd/pci_conf.defs b/hurd/pci_conf.defs
new file mode 100644
index 00000000..f3eccbb0
--- /dev/null
+++ b/hurd/pci_conf.defs
@@ -0,0 +1,50 @@
+/* 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_conf 39000;
+
+#include <hurd/hurd_types.defs>
+
+#ifdef PCI_CONF_IMPORTS
+PCI_CONF_IMPORTS
+#endif
+
+INTR_INTERFACE
+
+/* Read 'amount' bytes from B/D/F + 'reg' and store it in 'data' */
+routine pci_conf_read (
+ master: pci_conf_t;
+ bus: int;
+ dev: int;
+ func: int;
+ reg: int;
+ out data: data_t, dealloc;
+ amount: vm_size_t
+);
+
+/* Write 'amount' bytes from 'data' to B/D/F + 'reg' */
+routine pci_conf_write(
+ master: pci_conf_t;
+ bus: int;
+ dev: int;
+ func: int;
+ reg: int;
+ data: data_t;
+ out amount: vm_size_t
+);
diff --git a/hurd/subsystems b/hurd/subsystems
index c05895c2..9518172c 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_conf 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..b152de13
--- /dev/null
+++ b/pci_arbiter/Makefile
@@ -0,0 +1,40 @@
+# 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_conf-ops.c pci_access.c x86_pci.c
+MIGSRCS = pci_confServer.c
+OBJS = $(patsubst %.S,%.o,$(patsubst %.c,%.o, $(SRCS) $(MIGSRCS)))
+
+HURDLIBS= trivfs fshelp ports shouldbeinlibc
+
+target = pci_arbiter
+
+include ../Makeconf
+
+CFLAGS += -I$(PORTDIR)/include
+
+CPPFLAGS += -imacros $(srcdir)/config.h
+pci_conf-MIGSFLAGS = -imacros $(srcdir)/mig-mutate.h
+
+# cpp doesn't automatically make dependencies for -imacros dependencies. argh.
+pci_conf_S.h pci_confServer.c: mig-mutate.h
+$(OBJS): config.h
diff --git a/pci_arbiter/config.h b/pci_arbiter/config.h
new file mode 100644
index 00000000..b0d5196d
--- /dev/null
+++ b/pci_arbiter/config.h
@@ -0,0 +1,5 @@
+#define __KERNEL__ 1
+#undef __SMP__
+
+#define _HURD_ 1
+
diff --git a/pci_arbiter/main.c b/pci_arbiter/main.c
new file mode 100644
index 00000000..5c7b868a
--- /dev/null
+++ b/pci_arbiter/main.c
@@ -0,0 +1,116 @@
+/*
+ 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 <pci_arbiter.h>
+
+#include <error.h>
+#include <fcntl.h>
+#include <hurd/trivfs.h>
+
+#include <pci_access.h>
+#include <pci_conf_S.h>
+
+int trivfs_fstype = FSTYPE_MISC;
+int trivfs_fsid = 0;
+int trivfs_support_read = 0;
+int trivfs_support_write = 0;
+int trivfs_support_exec = 0;
+int trivfs_allow_open = O_READ | O_WRITE;
+
+void
+trivfs_modify_stat (struct trivfs_protid *cred, io_statbuf_t * st)
+{
+}
+
+error_t
+trivfs_goaway (struct trivfs_control *fsys, int flags)
+{
+ exit (0);
+}
+
+int
+pci_demuxer (mach_msg_header_t * inp, mach_msg_header_t * outp)
+{
+ mig_routine_t routine;
+ if ((routine = pci_conf_server_routine (inp)) ||
+ (routine = NULL, trivfs_demuxer (inp, outp)))
+ {
+ if (routine)
+ (*routine) (inp, outp);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ struct stat st;
+ mach_port_t bootstrap;
+
+ pci_bucket = ports_create_bucket ();
+
+ mach_port_allocate (mach_task_self (),
+ MACH_PORT_RIGHT_RECEIVE, &fsys_identity);
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap == MACH_PORT_NULL)
+ error (-1, 0, "Must be started as a translator");
+
+ err = trivfs_add_protid_port_class (&pci_protid_portclass);
+ if (err)
+ error (1, err, "Error creating protid port class");
+
+ err = trivfs_add_control_port_class (&pci_cntl_portclass);
+ if (err)
+ error (1, err, "Error creating control port class");
+
+ /* Reply to our parent */
+ err = trivfs_startup (bootstrap, 0,
+ pci_cntl_portclass,
+ pci_bucket,
+ pci_protid_portclass, pci_bucket, &pcicntl);
+ mach_port_deallocate (mach_task_self (), bootstrap);
+ if (err)
+ {
+ return (-1);
+ }
+
+ /* Initialize status from underlying node. */
+ pci_owner = pci_group = 0;
+ err = io_stat (pcicntl->underlying, &st);
+ if (!err)
+ {
+ pci_owner = st.st_uid;
+ pci_group = st.st_gid;
+ }
+
+ /* Start the PCI system */
+ err = pci_system_init ();
+ if (err)
+ error (1, err, "Error starting the PCI system");
+
+ ports_manage_port_operations_one_thread (pci_bucket, pci_demuxer, 0);
+
+ return 0;
+}
diff --git a/pci_arbiter/mig-mutate.h b/pci_arbiter/mig-mutate.h
new file mode 100644
index 00000000..ca62aafe
--- /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_CONF_IMPORTS \
+ import "../libtrivfs/mig-decls.h"; \
+
+#define PCI_CONF_INTRAN trivfs_protid_t trivfs_begin_using_protid (pci_conf_t)
+#define PCI_CONF_INTRAN_PAYLOAD trivfs_protid_t trivfs_begin_using_protid_payload
+#define PCI_CONF_DESTRUCTOR trivfs_end_using_protid (trivfs_protid_t)
diff --git a/pci_arbiter/pci_access.c b/pci_arbiter/pci_access.c
new file mode 100644
index 00000000..eded1bff
--- /dev/null
+++ b/pci_arbiter/pci_access.c
@@ -0,0 +1,51 @@
+/*
+ * (C) Copyright IBM Corporation 2006
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * on the rights to use, copy, modify, merge, publish, distribute, sub
+ * license, and/or sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * PCI access general header.
+ *
+ * Following code is borrowed from libpciaccess:
+ * https://cgit.freedesktop.org/xorg/lib/libpciaccess/
+ */
+
+#include <pci_access.h>
+
+#include <errno.h>
+
+#include <x86_pci.h>
+
+/* Configure PCI parameters */
+int
+pci_system_init (void)
+{
+ int err = ENOSYS;
+
+#ifdef __GNU__
+ err = pci_system_x86_create ();
+#else
+#error "Unsupported OS"
+#endif
+
+ return err;
+}
diff --git a/pci_arbiter/pci_access.h b/pci_arbiter/pci_access.h
new file mode 100644
index 00000000..8735ea77
--- /dev/null
+++ b/pci_arbiter/pci_access.h
@@ -0,0 +1,78 @@
+/*
+ * (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 <stdint.h>
+
+typedef uint64_t pciaddr_t;
+
+ /* Common interface */
+struct pci_iface
+{
+ int (*read) (unsigned bus, unsigned dev, unsigned func, pciaddr_t reg,
+ void *data, unsigned size);
+ int (*write) (unsigned bus, unsigned dev, unsigned func, pciaddr_t reg,
+ const void *data, unsigned size);
+};
+
+struct pci_iface *pci_ifc;
+
+int pci_system_init (void);
+
+#endif /* PCI_ACCESS_H */
diff --git a/pci_arbiter/pci_arbiter.h b/pci_arbiter/pci_arbiter.h
new file mode 100644
index 00000000..73540e8e
--- /dev/null
+++ b/pci_arbiter/pci_arbiter.h
@@ -0,0 +1,46 @@
+/*
+ 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/>.
+*/
+
+/* Translator global declarations */
+
+#ifndef PCI_ARBITER_H
+#define PCI_ARBITER_H
+
+#include <hurd/ports.h>
+
+/* Libports stuff */
+struct port_bucket *pci_bucket;
+
+struct port_class *pci_protid_portclass;
+struct port_class *pci_cntl_portclass;
+
+mach_port_t fsys_identity;
+
+/* Trivfs control structure for lwip. */
+struct trivfs_control *pcicntl;
+
+/* Owner of the underlying node. */
+uid_t pci_owner;
+
+/* Group of the underlying node. */
+uid_t pci_group;
+
+#endif /* PCI_ARBITER_H */
diff --git a/pci_arbiter/pci_conf-ops.c b/pci_arbiter/pci_conf-ops.c
new file mode 100644
index 00000000..b532158a
--- /dev/null
+++ b/pci_arbiter/pci_conf-ops.c
@@ -0,0 +1,90 @@
+/*
+ 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_conf_S.h>
+
+#include <hurd/fshelp.h>
+#include <hurd/trivfs.h>
+
+#include <pci_arbiter.h>
+#include <pci_access.h>
+
+error_t
+S_pci_conf_read (struct trivfs_protid * master, int bus, int dev, int func,
+ int reg, char **data, size_t * datalen,
+ mach_msg_type_number_t amount)
+{
+ error_t err;
+
+ if (!master)
+ return EOPNOTSUPP;
+
+ if (!master->isroot)
+ {
+ struct stat st;
+
+ st.st_uid = pci_owner;
+ st.st_gid = pci_group;
+
+ err = fshelp_isowner (&st, master->user);
+ if (err)
+ return EPERM;
+ }
+
+ if (amount > *datalen)
+ amount = *datalen;
+
+ err = pci_ifc->read (bus, dev, func, reg, *data, amount);
+
+ if (!err)
+ *datalen = amount;
+
+ return err;
+}
+
+error_t
+S_pci_conf_write (struct trivfs_protid * master, int bus, int dev, int func,
+ int reg, char *data, size_t datalen,
+ mach_msg_type_number_t * amount)
+{
+ error_t err;
+
+ if (!master)
+ return EOPNOTSUPP;
+
+ if (!master->isroot)
+ {
+ struct stat st;
+
+ st.st_uid = pci_owner;
+ st.st_gid = pci_group;
+
+ err = fshelp_isowner (&st, master->user);
+ if (err)
+ return EPERM;
+ }
+
+ err = pci_ifc->write (bus, dev, func, reg, data, datalen);
+
+ *amount = datalen;
+
+ return err;
+}
diff --git a/pci_arbiter/x86_pci.c b/pci_arbiter/x86_pci.c
new file mode 100644
index 00000000..abae4938
--- /dev/null
+++ b/pci_arbiter/x86_pci.c
@@ -0,0 +1,331 @@
+/*
+ * 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 access backend.
+ *
+ * Following code is borrowed from libpciaccess:
+ * https://cgit.freedesktop.org/xorg/lib/libpciaccess/
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <x86_pci.h>
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/io.h>
+
+#include <pci_access.h>
+
+static int
+x86_enable_io (void)
+{
+ if (!ioperm (0, 0xffff, 1))
+ return 0;
+ return errno;
+}
+
+static int
+x86_disable_io (void)
+{
+ if (!ioperm (0, 0xffff, 0))
+ return 0;
+ return errno;
+}
+
+static int
+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 int
+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;
+ int 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 int
+pci_system_x86_conf1_write (unsigned bus, unsigned dev, unsigned func,
+ pciaddr_t reg, const void *data, unsigned size)
+{
+ unsigned addr = 0xCFC + (reg & 3);
+ unsigned long sav;
+ int 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 int
+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 int
+pci_system_x86_conf2_read (unsigned bus, unsigned dev, unsigned func,
+ pciaddr_t reg, void *data, unsigned size)
+{
+ unsigned addr = 0xC000 | dev << 8 | reg;
+ int 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 int
+pci_system_x86_conf2_write (unsigned bus, unsigned dev, unsigned func,
+ pciaddr_t reg, const void *data, unsigned size)
+{
+ unsigned addr = 0xC000 | dev << 8 | reg;
+ int 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;
+}
+
+/* Check that this really looks like a PCI configuration. */
+static int
+pci_system_x86_check (struct pci_iface *pci_ifc)
+{
+ 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_ifc->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_ifc->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;
+}
+
+static int
+pci_probe (struct pci_iface *pci_ifc)
+{
+ if (pci_system_x86_conf1_probe () == 0)
+ {
+ pci_ifc->read = pci_system_x86_conf1_read;
+ pci_ifc->write = pci_system_x86_conf1_write;
+ if (pci_system_x86_check (pci_ifc) == 0)
+ return 0;
+ }
+
+ if (pci_system_x86_conf2_probe () == 0)
+ {
+ pci_ifc->read = pci_system_x86_conf2_read;
+ pci_ifc->write = pci_system_x86_conf2_write;
+ if (pci_system_x86_check (pci_ifc) == 0)
+ return 0;
+ }
+
+ return ENODEV;
+}
+
+int
+pci_system_x86_create (void)
+{
+ int ret;
+
+ ret = x86_enable_io ();
+ if (ret)
+ return ret;
+
+ pci_ifc = calloc (1, sizeof (struct pci_iface));
+ if (pci_ifc == NULL)
+ {
+ x86_disable_io ();
+ return ENOMEM;
+ }
+
+ ret = pci_probe (pci_ifc);
+ if (ret)
+ {
+ x86_disable_io ();
+ free (pci_ifc);
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/pci_arbiter/x86_pci.h b/pci_arbiter/x86_pci.h
new file mode 100644
index 00000000..fe507d67
--- /dev/null
+++ b/pci_arbiter/x86_pci.h
@@ -0,0 +1,42 @@
+/*
+ * 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 access backend header.
+ *
+ * Following code is borrowed from libpciaccess:
+ * https://cgit.freedesktop.org/xorg/lib/libpciaccess/
+ */
+
+#ifndef X86_PCI_H
+#define X86_PCI_H
+
+#define PCI_VENDOR_ID 0x00
+#define PCI_VENDOR_ID_COMPAQ 0x0e11
+#define PCI_VENDOR_ID_INTEL 0x8086
+
+#define PCI_CLASS_DEVICE 0x0a
+#define PCI_CLASS_DISPLAY_VGA 0x0300
+#define PCI_CLASS_BRIDGE_HOST 0x0600
+
+int pci_system_x86_create (void);
+
+#endif /* X86_PCI_H */
--
2.14.0
Joan Lledó
2017-10-24 11:21:36 UTC
Permalink
This new module uses Hurd's RPCs for accessing the PCI configuration space.
Direct access as in {read_write}_{8,16,32} functions is done by the old x86
module.

Some x86 function prototypes are now declared in a new header for the Hurd
module to use them, in order to duplicate as little code as possible.
---
src/Makefile.am | 4 +-
src/common_init.c | 4 +-
src/hurd_pci.c | 299 ++++++++++++++++++++++++++++++++++++++++++++++++
src/pciaccess_private.h | 6 +
src/x86_pci.c | 58 +++-------
src/x86_pci.h | 77 +++++++++++++
6 files changed, 406 insertions(+), 42 deletions(-)
create mode 100644 src/hurd_pci.c
create mode 100644 src/x86_pci.h

diff --git a/src/Makefile.am b/src/Makefile.am
index 3a46a85..f222aa5 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -52,12 +52,12 @@ VGA_ARBITER = common_vgaarb_stub.c
endif

if GNU
-OS_SUPPORT = x86_pci.c
+OS_SUPPORT = hurd_pci.c x86_pci.c x86_pci.h
VGA_ARBITER = common_vgaarb_stub.c
endif

if CYGWIN
-OS_SUPPORT = x86_pci.c
+OS_SUPPORT = x86_pci.c x86_pci.h
VGA_ARBITER = common_vgaarb_stub.c
endif

diff --git a/src/common_init.c b/src/common_init.c
index b1c0c3e..21572ee 100644
--- a/src/common_init.c
+++ b/src/common_init.c
@@ -62,7 +62,9 @@ pci_system_init( void )
err = pci_system_openbsd_create();
#elif defined(__sun)
err = pci_system_solx_devfs_create();
-#elif defined(__GNU__) || defined(__CYGWIN__)
+#elif defined(__GNU__)
+ err = pci_system_hurd_create();
+#elif defined(__CYGWIN__)
err = pci_system_x86_create();
#else
# error "Unsupported OS"
diff --git a/src/hurd_pci.c b/src/hurd_pci.c
new file mode 100644
index 0000000..cf0b1d6
--- /dev/null
+++ b/src/hurd_pci.c
@@ -0,0 +1,299 @@
+/*
+ * Copyright (c) 2017, Joan Lledó
+ * Copyright (c) 2009, 2012 Samuel Thibault
+ * Heavily inspired from the freebsd, netbsd, and openbsd backends
+ * (C) Copyright Eric Anholt 2006
+ * (C) Copyright IBM Corporation 2006
+ * Copyright (c) 2008 Juan Romero Pardines
+ * Copyright (c) 2008 Mark Kettenis
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define _GNU_SOURCE
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <string.h>
+#include <strings.h>
+#include <hurd.h>
+#include <hurd/pci_conf.h>
+#include <hurd/paths.h>
+
+#include "x86_pci.h"
+#include "pciaccess.h"
+#include "pciaccess_private.h"
+
+struct pci_system_hurd {
+ struct pci_system system;
+};
+
+/* Server port */
+mach_port_t pci_server_port = MACH_PORT_NULL;
+
+/* Get the server port */
+static int
+init_client(void)
+{
+ int ret = 0;
+ pci_server_port = file_name_lookup(_SERVERS_PCI_CONF, 0, 0);
+
+ if (pci_server_port == MACH_PORT_NULL)
+ ret = errno;
+
+ return ret;
+}
+
+/*
+ * Read 'size' bytes from B/D/F + reg and store them in 'data'.
+ *
+ * It's assumed that 'size' bytes are allocated in 'data'
+ */
+static int
+pciclient_cfg_read(int bus, int dev, int func, int reg, char *buf,
+ size_t * nbytes)
+{
+ error_t err;
+ size_t nread;
+ char *data;
+
+ data = buf;
+ nread = *nbytes;
+ err = pci_conf_read(pci_server_port, bus, dev, func, reg, &data, &nread,
+ *nbytes);
+ if (err)
+ return err;
+
+ if (data != buf) {
+ if (nread > *nbytes) /* Sanity check for bogus server. */ {
+ vm_deallocate(mach_task_self(), (vm_address_t) data, nread);
+ return EGRATUITOUS;
+ }
+
+ memcpy(buf, data, nread);
+ vm_deallocate(mach_task_self(), (vm_address_t)data, nread);
+ }
+
+ *nbytes = nread;
+
+ return 0;
+}
+
+/* Write 'size' bytes from 'data' to B/D/F + reg */
+static int
+pciclient_cfg_write(int bus, int dev, int func, int reg, char *buf,
+ size_t * nbytes)
+{
+ error_t err;
+ size_t nwrote;
+
+ err = pci_conf_write(pci_server_port, bus, dev, func, reg, buf, *nbytes,
+ &nwrote);
+
+ if (!err)
+ *nbytes = nwrote;
+
+ return err;
+}
+
+static int
+pci_nfuncs(struct pci_system_hurd *pci_sys_hurd, int bus, int dev)
+{
+ uint8_t hdr;
+ size_t size;
+ int err;
+
+ size = sizeof(hdr);
+ err = pciclient_cfg_read(bus, dev, 0, PCI_HDRTYPE, (char*)&hdr, &size);
+
+ if (err)
+ return err;
+
+ if(size != sizeof(hdr))
+ return EIO;
+
+ return hdr & 0x80 ? 8 : 1;
+}
+
+static int
+pci_device_hurd_read(struct pci_device *dev, void *data,
+ pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_read)
+{
+ int err;
+
+ *bytes_read = 0;
+ while (size > 0) {
+ size_t toread = 1 << (ffs(0x4 + (offset & 0x03)) - 1);
+ if (toread > size)
+ toread = size;
+
+ err = pciclient_cfg_read(dev->bus, dev->dev, dev->func, offset,
+ (char*)data, &toread);
+ if (err)
+ return err;
+
+ offset += toread;
+ data = (char*)data + toread;
+ size -= toread;
+ *bytes_read += toread;
+ }
+ return 0;
+}
+
+static int
+pci_device_hurd_write(struct pci_device *dev, const void *data,
+ pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_written)
+{
+ int err;
+
+ *bytes_written = 0;
+ while (size > 0) {
+ size_t towrite = 4;
+ if (towrite > size)
+ towrite = size;
+ if (towrite > 4 - (offset & 0x3))
+ towrite = 4 - (offset & 0x3);
+
+ err = pciclient_cfg_write(dev->bus, dev->dev, dev->func, offset,
+ (char*)data, &towrite);
+ if (err)
+ return err;
+
+ offset += towrite;
+ data = (const char*)data + towrite;
+ size -= towrite;
+ *bytes_written += towrite;
+ }
+ return 0;
+}
+
+static const struct pci_system_methods hurd_pci_methods = {
+ .destroy = pci_system_x86_destroy,
+ .read_rom = pci_device_x86_read_rom,
+ .probe = pci_device_x86_probe,
+ .map_range = pci_device_x86_map_range,
+ .unmap_range = pci_device_x86_unmap_range,
+ .read = pci_device_hurd_read,
+ .write = pci_device_hurd_write,
+ .fill_capabilities = pci_fill_capabilities_generic,
+ .open_legacy_io = pci_device_x86_open_legacy_io,
+ .close_io = pci_device_x86_close_io,
+ .read32 = pci_device_x86_read32,
+ .read16 = pci_device_x86_read16,
+ .read8 = pci_device_x86_read8,
+ .write32 = pci_device_x86_write32,
+ .write16 = pci_device_x86_write16,
+ .write8 = pci_device_x86_write8,
+ .map_legacy = pci_device_x86_map_legacy,
+ .unmap_legacy = pci_device_x86_unmap_legacy,
+};
+
+_pci_hidden int
+pci_system_hurd_create(void)
+{
+ struct pci_device_private *device;
+ int ret, bus, dev, ndevs, func, nfuncs;
+ struct pci_system_hurd *pci_sys_hurd;
+ uint32_t reg;
+ size_t toread;
+
+ ret = x86_enable_io();
+ if (ret)
+ return ret;
+
+ ret = init_client();
+ if (ret)
+ return ret;
+
+ pci_sys_hurd = calloc(1, sizeof(struct pci_system_hurd));
+ if (pci_sys_hurd == NULL) {
+ x86_disable_io();
+ return ENOMEM;
+ }
+ pci_sys = &pci_sys_hurd->system;
+
+ pci_sys->methods = &hurd_pci_methods;
+
+ ndevs = 0;
+ for (bus = 0; bus < 256; bus++) {
+ for (dev = 0; dev < 32; dev++) {
+ nfuncs = pci_nfuncs(pci_sys_hurd, bus, dev);
+ for (func = 0; func < nfuncs; func++) {
+ toread = sizeof(reg);
+ if (pciclient_cfg_read(bus, dev, func, PCI_VENDOR_ID,
+ (char*)&reg, &toread) != 0)
+ continue;
+ if (PCI_VENDOR(reg) == PCI_VENDOR_INVALID ||
+ PCI_VENDOR(reg) == 0 ||
+ toread != sizeof(reg))
+ continue;
+ ndevs++;
+ }
+ }
+ }
+
+ pci_sys->num_devices = ndevs;
+ pci_sys->devices = calloc(ndevs, sizeof(struct pci_device_private));
+ if (pci_sys->devices == NULL) {
+ x86_disable_io();
+ free(pci_sys_hurd);
+ pci_sys = NULL;
+ return ENOMEM;
+ }
+
+ device = pci_sys->devices;
+ for (bus = 0; bus < 256; bus++) {
+ for (dev = 0; dev < 32; dev++) {
+ nfuncs = pci_nfuncs(pci_sys_hurd, bus, dev);
+ for (func = 0; func < nfuncs; func++) {
+ toread = sizeof(reg);
+ if (pciclient_cfg_read(bus, dev, func, PCI_VENDOR_ID,
+ (char*)&reg, &toread) != 0)
+ continue;
+ if (PCI_VENDOR(reg) == PCI_VENDOR_INVALID ||
+ PCI_VENDOR(reg) == 0 ||
+ toread != sizeof(reg))
+ continue;
+ device->base.domain = 0;
+ device->base.bus = bus;
+ device->base.dev = dev;
+ device->base.func = func;
+ device->base.vendor_id = PCI_VENDOR(reg);
+ device->base.device_id = PCI_DEVICE(reg);
+
+ toread = sizeof(reg);
+ if (pciclient_cfg_read(bus, dev, func, PCI_CLASS,
+ (char*)&reg,&toread) != 0)
+ continue;
+ device->base.device_class = reg >> 8;
+ device->base.revision = reg & 0xFF;
+
+ toread = sizeof(reg);
+ if (pciclient_cfg_read(bus, dev, func, PCI_SUB_VENDOR_ID,
+ (char*)&reg, &toread) != 0)
+ continue;
+ if (toread != sizeof(reg))
+ continue;
+ device->base.subvendor_id = PCI_VENDOR(reg);
+ device->base.subdevice_id = PCI_DEVICE(reg);
+
+ device++;
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/src/pciaccess_private.h b/src/pciaccess_private.h
index 2f05b29..329e2fd 100644
--- a/src/pciaccess_private.h
+++ b/src/pciaccess_private.h
@@ -29,6 +29,9 @@
* \author Ian Romanick <***@us.ibm.com>
*/

+#ifndef PCIACCESS_PRIVATE_H
+#define PCIACCESS_PRIVATE_H
+
#if defined(__GNUC__) && (__GNUC__ >= 4)
# define _pci_hidden __attribute__((visibility("hidden")))
#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550)
@@ -189,5 +192,8 @@ extern int pci_system_netbsd_create( void );
extern int pci_system_openbsd_create( void );
extern void pci_system_openbsd_init_dev_mem( int );
extern int pci_system_solx_devfs_create( void );
+extern int pci_system_hurd_create( void );
extern int pci_system_x86_create( void );
extern void pci_io_cleanup( void );
+
+#endif /* PCIACCESS_PRIVATE_H */
diff --git a/src/x86_pci.c b/src/x86_pci.c
index 49c1cab..9fccc5d 100644
--- a/src/x86_pci.c
+++ b/src/x86_pci.c
@@ -20,6 +20,8 @@
*/

#define _GNU_SOURCE
+#include "x86_pci.h"
+
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
@@ -36,7 +38,7 @@

#include <sys/io.h>

-static int
+int
x86_enable_io(void)
{
if (!ioperm(0, 0xffff, 1))
@@ -44,7 +46,7 @@ x86_enable_io(void)
return errno;
}

-static int
+int
x86_disable_io(void)
{
if (!ioperm(0, 0xffff, 0))
@@ -205,28 +207,6 @@ outl(uint32_t value, uint16_t port)

#endif

-#define PCI_VENDOR(reg) ((reg) & 0xFFFF)
-#define PCI_VENDOR_INVALID 0xFFFF
-
-#define PCI_VENDOR_ID 0x00
-#define PCI_SUB_VENDOR_ID 0x2c
-#define PCI_VENDOR_ID_COMPAQ 0x0e11
-#define PCI_VENDOR_ID_INTEL 0x8086
-
-#define PCI_DEVICE(reg) (((reg) >> 16) & 0xFFFF)
-#define PCI_DEVICE_INVALID 0xFFFF
-
-#define PCI_CLASS 0x08
-#define PCI_CLASS_DEVICE 0x0a
-#define PCI_CLASS_DISPLAY_VGA 0x0300
-#define PCI_CLASS_BRIDGE_HOST 0x0600
-
-#define PCIC_DISPLAY 0x03
-#define PCIS_DISPLAY_VGA 0x00
-
-#define PCI_HDRTYPE 0x0E
-#define PCI_IRQ 0x3C
-
struct pci_system_x86 {
struct pci_system system;
int (*read)(unsigned bus, unsigned dev, unsigned func, pciaddr_t reg, void *data, unsigned size);
@@ -446,7 +426,7 @@ pci_nfuncs(struct pci_system_x86 *pci_sys_x86, int bus, int dev)
/**
* Read a VGA rom using the 0xc0000 mapping.
*/
-static int
+int
pci_device_x86_read_rom(struct pci_device *dev, void *buffer)
{
void *bios;
@@ -524,7 +504,7 @@ get_test_val_size( uint32_t testval )
return size;
}

-static int
+int
pci_device_x86_probe(struct pci_device *dev)
{
uint8_t irq, hdrtype;
@@ -633,7 +613,7 @@ pci_device_x86_unmap_range(struct pci_device *dev,

#else

-static int
+int
pci_device_x86_map_range(struct pci_device *dev,
struct pci_device_mapping *map)
{
@@ -654,7 +634,7 @@ pci_device_x86_map_range(struct pci_device *dev,
return 0;
}

-static int
+int
pci_device_x86_unmap_range(struct pci_device *dev,
struct pci_device_mapping *map)
{
@@ -715,13 +695,13 @@ pci_device_x86_write(struct pci_device *dev, const void *data,
return 0;
}

-static void
+void
pci_system_x86_destroy(void)
{
x86_disable_io();
}

-static struct pci_io_handle *
+struct pci_io_handle *
pci_device_x86_open_legacy_io(struct pci_io_handle *ret,
struct pci_device *dev, pciaddr_t base, pciaddr_t size)
{
@@ -734,7 +714,7 @@ pci_device_x86_open_legacy_io(struct pci_io_handle *ret,
return ret;
}

-static void
+void
pci_device_x86_close_io(struct pci_device *dev, struct pci_io_handle *handle)
{
/* Like in the Linux case, do not disable I/O, as it may be opened several
@@ -742,46 +722,46 @@ pci_device_x86_close_io(struct pci_device *dev, struct pci_io_handle *handle)
/* x86_disable_io(); */
}

-static uint32_t
+uint32_t
pci_device_x86_read32(struct pci_io_handle *handle, uint32_t reg)
{
return inl(reg + handle->base);
}

-static uint16_t
+uint16_t
pci_device_x86_read16(struct pci_io_handle *handle, uint32_t reg)
{
return inw(reg + handle->base);
}

-static uint8_t
+uint8_t
pci_device_x86_read8(struct pci_io_handle *handle, uint32_t reg)
{
return inb(reg + handle->base);
}

-static void
+void
pci_device_x86_write32(struct pci_io_handle *handle, uint32_t reg,
uint32_t data)
{
outl(data, reg + handle->base);
}

-static void
+void
pci_device_x86_write16(struct pci_io_handle *handle, uint32_t reg,
uint16_t data)
{
outw(data, reg + handle->base);
}

-static void
+void
pci_device_x86_write8(struct pci_io_handle *handle, uint32_t reg,
uint8_t data)
{
outb(data, reg + handle->base);
}

-static int
+int
pci_device_x86_map_legacy(struct pci_device *dev, pciaddr_t base,
pciaddr_t size, unsigned map_flags, void **addr)
{
@@ -797,7 +777,7 @@ pci_device_x86_map_legacy(struct pci_device *dev, pciaddr_t base,
return err;
}

-static int
+int
pci_device_x86_unmap_legacy(struct pci_device *dev, void *addr,
pciaddr_t size)
{
diff --git a/src/x86_pci.h b/src/x86_pci.h
new file mode 100644
index 0000000..2f796f4
--- /dev/null
+++ b/src/x86_pci.h
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+
+#ifndef X86_PCI_H
+#define X86_PCI_H
+
+#include "pciaccess.h"
+#include "pciaccess_private.h"
+
+#define PCI_VENDOR(reg) ((reg) & 0xFFFF)
+#define PCI_VENDOR_INVALID 0xFFFF
+
+#define PCI_VENDOR_ID 0x00
+#define PCI_SUB_VENDOR_ID 0x2c
+#define PCI_VENDOR_ID_COMPAQ 0x0e11
+#define PCI_VENDOR_ID_INTEL 0x8086
+
+#define PCI_DEVICE(reg) (((reg) >> 16) & 0xFFFF)
+#define PCI_DEVICE_INVALID 0xFFFF
+
+#define PCI_CLASS 0x08
+#define PCI_CLASS_DEVICE 0x0a
+#define PCI_CLASS_DISPLAY_VGA 0x0300
+#define PCI_CLASS_BRIDGE_HOST 0x0600
+
+#define PCIC_DISPLAY 0x03
+#define PCIS_DISPLAY_VGA 0x00
+
+#define PCI_HDRTYPE 0x0E
+#define PCI_IRQ 0x3C
+
+int x86_enable_io(void);
+int x86_disable_io(void);
+void pci_system_x86_destroy(void);
+int pci_device_x86_read_rom(struct pci_device *dev, void *buffer);
+int pci_device_x86_probe(struct pci_device *dev);
+int pci_device_x86_map_range(struct pci_device *dev,
+ struct pci_device_mapping *map);
+int pci_device_x86_unmap_range(struct pci_device *dev,
+ struct pci_device_mapping *map);
+struct pci_io_handle *pci_device_x86_open_legacy_io(struct pci_io_handle *ret,
+ struct pci_device *dev, pciaddr_t base, pciaddr_t size);
+void pci_device_x86_close_io(struct pci_device *dev,
+ struct pci_io_handle *handle);
+uint32_t pci_device_x86_read32(struct pci_io_handle *handle, uint32_t reg);
+uint16_t pci_device_x86_read16(struct pci_io_handle *handle, uint32_t reg);
+uint8_t pci_device_x86_read8(struct pci_io_handle *handle, uint32_t reg);
+void pci_device_x86_write32(struct pci_io_handle *handle, uint32_t reg,
+ uint32_t data);
+void pci_device_x86_write16(struct pci_io_handle *handle, uint32_t reg,
+ uint16_t data);
+void pci_device_x86_write8(struct pci_io_handle *handle, uint32_t reg,
+ uint8_t data);
+int pci_device_x86_map_legacy(struct pci_device *dev, pciaddr_t base,
+ pciaddr_t size, unsigned map_flags, void **addr);
+int pci_device_x86_unmap_legacy(struct pci_device *dev, void *addr,
+ pciaddr_t size);
+
+#endif /* X86_PCI_H */
--
2.14.0
Joan Lledó
2017-10-26 06:43:45 UTC
Permalink
Hi!
Post by Justus Winter
I'd suggest pci-arbiter.
Done.
Post by Justus Winter
Post by Joan Lledó
diff --git a/hurd/pci.defs b/hurd/pci.defs
new file mode 100644
index 00000000..d051bd69
--- /dev/null
+++ b/hurd/pci.defs
@@ -0,0 +1,50 @@
+/* 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
+#endif
+
+INTR_INTERFACE
Leftover here.
I cannot compile the server if I remove the `INTR_INTERFACE', The
mig-generated header doesn't mach my prototype. Is that what's left
over?
Post by Justus Winter
Post by Joan Lledó
+ /* Start the PCI system */
+ err = pci_system_init ();
+ if (err)
+ error (1, err, "Error starting the PCI system");
+
+ ports_manage_port_operations_one_thread (pci_bucket, pci_demuxer, 0);
Maybe add a comment here that we rely on the one threaded nature of the
server for synchronization.
OK
Justus Winter
2017-10-26 08:11:46 UTC
Permalink
Post by Joan Lledó
Post by Justus Winter
Post by Joan Lledó
+INTR_INTERFACE
Leftover here.
I cannot compile the server if I remove the `INTR_INTERFACE', The
mig-generated header doesn't mach my prototype. Is that what's left
over?
Oh, I was wrong here. INTR_INTERFACE indeed only says that the RPCs are
interruptible (libports manages that for you afaik).


Cheers,
Justus
Joan Lledó
2017-12-03 10:21:53 UTC
Permalink
Hello Hurd,

This is a new version of the PCI arbiter. Following are some features I've
added the project over the last weeks.

- A tiny libnetfs filesystem to expose a tree of devices.
- For each virtual device, provide files to read/write its configuration space,
regions an expansion rom.
- Ability to set access permissions over devices by command line parameter and
fsysopts.

I tried to run netdde as non-root but gave up because it seemed non-trivial for
me and the time for my thesis is running out. Next are some issues I found:

- Some network drivers poke IO ports by themselves (See #hurd, 2017-11-23 [1]).
- check_kernel() in check_kernel.c[2] is calling get_privileged_ports().

Besides, the pci server lacks some features required to run netdde as non-root:

- Provide some way for the client to map device's regions and rom into its
space. read() + mmap() could be enough for read-only spaces, but probably
making the server act as a pager is the only good solution.
(See #hurd, 2017-10-27 [3]).
- Provide libpciaccess and pciutils with a way to poke IO ports as non-root.
Adding new routines to the PCI interface is an option, or even creating a
new interface, since poking IO ports is not necessarily related to PCI.

Modules for libpciaccess and pciutils are also attached.

------------------------
[1] http://richtlijn.be/~larstiq/hurd/hurd-2017-11-23
[2] https://anonscm.debian.org/cgit/pkg-hurd/netdde.git/tree/check_kernel.c
[3] http://richtlijn.be/~larstiq/hurd/hurd-2017-10-27
Joan Lledó
2017-12-03 10:21:56 UTC
Permalink
This new module uses Hurd's RPCs for accessing the PCI configuration space.
Direct access as in {read_write}_{8,16,32} functions is done by the old x86
module.

Some x86 function prototypes are now declared in a new header for the Hurd
module to use them, in order to duplicate as little code as possible.
---
src/Makefile.am | 4 +-
src/common_init.c | 4 +-
src/hurd_pci.c | 492 ++++++++++++++++++++++++++++++++++++++++++++++++
src/pciaccess_private.h | 9 +
src/x86_pci.c | 54 ++----
src/x86_pci.h | 77 ++++++++
6 files changed, 600 insertions(+), 40 deletions(-)
create mode 100644 src/hurd_pci.c
create mode 100644 src/x86_pci.h

diff --git a/src/Makefile.am b/src/Makefile.am
index 3a46a85..f222aa5 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -52,12 +52,12 @@ VGA_ARBITER = common_vgaarb_stub.c
endif

if GNU
-OS_SUPPORT = x86_pci.c
+OS_SUPPORT = hurd_pci.c x86_pci.c x86_pci.h
VGA_ARBITER = common_vgaarb_stub.c
endif

if CYGWIN
-OS_SUPPORT = x86_pci.c
+OS_SUPPORT = x86_pci.c x86_pci.h
VGA_ARBITER = common_vgaarb_stub.c
endif

diff --git a/src/common_init.c b/src/common_init.c
index b1c0c3e..21572ee 100644
--- a/src/common_init.c
+++ b/src/common_init.c
@@ -62,7 +62,9 @@ pci_system_init( void )
err = pci_system_openbsd_create();
#elif defined(__sun)
err = pci_system_solx_devfs_create();
-#elif defined(__GNU__) || defined(__CYGWIN__)
+#elif defined(__GNU__)
+ err = pci_system_hurd_create();
+#elif defined(__CYGWIN__)
err = pci_system_x86_create();
#else
# error "Unsupported OS"
diff --git a/src/hurd_pci.c b/src/hurd_pci.c
new file mode 100644
index 0000000..10758c0
--- /dev/null
+++ b/src/hurd_pci.c
@@ -0,0 +1,492 @@
+/*
+ * Copyright (c) 2017, Joan Lledó
+ * Copyright (c) 2009, 2012 Samuel Thibault
+ * Heavily inspired from the freebsd, netbsd, and openbsd backends
+ * (C) Copyright Eric Anholt 2006
+ * (C) Copyright IBM Corporation 2006
+ * Copyright (c) 2008 Juan Romero Pardines
+ * Copyright (c) 2008 Mark Kettenis
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define _GNU_SOURCE
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <sys/mman.h>
+#include <string.h>
+#include <strings.h>
+#include <hurd.h>
+#include <hurd/pci.h>
+#include <hurd/paths.h>
+
+#include "x86_pci.h"
+#include "pciaccess.h"
+#include "pciaccess_private.h"
+
+/*
+ * Hurd PCI access using RPCs.
+ *
+ * Some functions are shared with the x86 module to avoid repeating code.
+ */
+
+/* Server path */
+#define _SERVERS_PCI_CONF _SERVERS_BUS "/pci"
+
+/* File names */
+#define FILE_CONFIG_NAME "config"
+#define FILE_ROM_NAME "rom"
+
+/* Level in the fs tree */
+typedef enum {
+ LEVEL_NONE,
+ LEVEL_DOMAIN,
+ LEVEL_BUS,
+ LEVEL_DEV,
+ LEVEL_FUNC
+} tree_level;
+
+struct pci_system_hurd {
+ struct pci_system system;
+};
+
+static int
+pci_device_hurd_probe(struct pci_device *dev)
+{
+ uint8_t irq;
+ int err, i;
+ struct pci_bar regions[6];
+ struct pci_xrom_bar rom;
+ struct pci_device_private *d;
+ size_t size;
+ char *buf;
+
+ /* Many of the fields were filled in during initial device enumeration.
+ * At this point, we need to fill in regions, rom_size, and irq.
+ */
+
+ err = pci_device_cfg_read_u8(dev, &irq, PCI_IRQ);
+ if (err)
+ return err;
+ dev->irq = irq;
+
+ /* Get regions */
+ buf = (char *)&regions;
+ size = sizeof(regions);
+ d = (struct pci_device_private *)dev;
+ err = pci_get_dev_regions(d->device_port, &buf, &size);
+ if(err)
+ return err;
+
+ if((char*)&regions != buf)
+ {
+ /* Sanity check for bogus server. */
+ if(size > sizeof(regions))
+ {
+ vm_deallocate(mach_task_self(), (vm_address_t) buf, size);
+ return EGRATUITOUS;
+ }
+
+ memcpy(&regions, buf, size);
+ vm_deallocate(mach_task_self(), (vm_address_t) buf, size);
+ }
+
+ for(i=0; i<6; i++)
+ {
+ if(regions[i].size == 0)
+ continue;
+
+ dev->regions[i].base_addr = regions[i].base_addr;
+ dev->regions[i].size = regions[i].size;
+ dev->regions[i].is_IO = regions[i].is_IO;
+ dev->regions[i].is_prefetchable = regions[i].base_addr;
+ dev->regions[i].is_64 = regions[i].is_64;
+ }
+
+ /* Get rom info */
+ buf = (char *)&rom;
+ size = sizeof(rom);
+ err = pci_get_dev_rom(d->device_port, &buf, &size);
+ if(err)
+ return err;
+
+ if((char*)&rom != buf)
+ {
+ /* Sanity check for bogus server. */
+ if(size > sizeof(rom))
+ {
+ vm_deallocate(mach_task_self(), (vm_address_t) buf, size);
+ return EGRATUITOUS;
+ }
+
+ memcpy(&rom, buf, size);
+ vm_deallocate(mach_task_self(), (vm_address_t) buf, size);
+ }
+
+ d->rom_base = rom.base_addr;
+ dev->rom_size = rom.size;
+
+ return 0;
+}
+
+/*
+ * Read `nbytes' bytes from `reg' in device's configuretion space
+ * and store them in `buf'.
+ *
+ * It's assumed that `nbytes' bytes are allocated in `buf'
+ */
+static int
+pciclient_cfg_read(mach_port_t device_port, int reg, char *buf,
+ size_t * nbytes)
+{
+ int err;
+ size_t nread;
+ char *data;
+
+ data = buf;
+ nread = *nbytes;
+ err = pci_conf_read(device_port, reg, &data, &nread, *nbytes);
+ if (err)
+ return err;
+
+ if (data != buf) {
+ if (nread > *nbytes) /* Sanity check for bogus server. */ {
+ vm_deallocate(mach_task_self(), (vm_address_t) data, nread);
+ return EGRATUITOUS;
+ }
+
+ memcpy(buf, data, nread);
+ vm_deallocate(mach_task_self(), (vm_address_t)data, nread);
+ }
+
+ *nbytes = nread;
+
+ return 0;
+}
+
+/* Write `nbytes' bytes from `buf' to `reg' in device's configuration space */
+static int
+pciclient_cfg_write(mach_port_t device_port, int reg, char *buf,
+ size_t * nbytes)
+{
+ int err;
+ size_t nwrote;
+
+ err = pci_conf_write(device_port, reg, buf, *nbytes, &nwrote);
+
+ if (!err)
+ *nbytes = nwrote;
+
+ return err;
+}
+
+/*
+ * Read up to `size' bytes from `dev' configuration space to `data' starting
+ * at `offset'. Write the amount on read bytes in `bytes_read'.
+ */
+static int
+pci_device_hurd_read(struct pci_device *dev, void *data,
+ pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_read)
+{
+ int err;
+ struct pci_device_private *d;
+
+ *bytes_read = 0;
+ d = (struct pci_device_private *)dev;
+ while (size > 0) {
+ size_t toread = 1 << (ffs(0x4 + (offset & 0x03)) - 1);
+ if (toread > size)
+ toread = size;
+
+ err = pciclient_cfg_read(d->device_port, offset, (char*)data,
+ &toread);
+ if (err)
+ return err;
+
+ offset += toread;
+ data = (char*)data + toread;
+ size -= toread;
+ *bytes_read += toread;
+ }
+ return 0;
+}
+
+/*
+ * Write up to `size' bytes from `data' to `dev' configuration space starting
+ * at `offset'. Write the amount on written bytes in `bytes_written'.
+ */
+static int
+pci_device_hurd_write(struct pci_device *dev, const void *data,
+ pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_written)
+{
+ int err;
+ struct pci_device_private *d;
+
+ *bytes_written = 0;
+ d = (struct pci_device_private *)dev;
+ while (size > 0) {
+ size_t towrite = 4;
+ if (towrite > size)
+ towrite = size;
+ if (towrite > 4 - (offset & 0x3))
+ towrite = 4 - (offset & 0x3);
+
+ err = pciclient_cfg_write(d->device_port, offset, (char*)data,
+ &towrite);
+ if (err)
+ return err;
+
+ offset += towrite;
+ data = (const char*)data + towrite;
+ size -= towrite;
+ *bytes_written += towrite;
+ }
+ return 0;
+}
+
+/*
+ * Copy the device's firmware in `buffer'
+ */
+static int
+pci_device_hurd_read_rom(struct pci_device * dev, void * buffer)
+{
+ void *rom;
+ int romfd;
+ char server[NAME_MAX];
+
+ snprintf(server, NAME_MAX, "%s/%04x/%02x/%02x/%01u/%s", _SERVERS_PCI_CONF,
+ dev->domain, dev->bus, dev->dev, dev->func, FILE_ROM_NAME);
+ romfd = open(server, O_RDONLY | O_CLOEXEC);
+ if (romfd == -1)
+ return errno;
+
+ rom = mmap(NULL, dev->rom_size, PROT_READ, 0, romfd, 0);
+ if (rom == MAP_FAILED) {
+ close(romfd);
+ return errno;
+ }
+
+ memcpy(buffer, rom, dev->rom_size);
+
+ munmap(rom, dev->rom_size);
+ close(romfd);
+
+ return 0;
+}
+
+/*
+ * Each device has its own server where send RPC's to.
+ *
+ * Deallocate the port before destroying the device.
+ */
+static void
+pci_device_hurd_destroy(struct pci_device *dev)
+{
+ struct pci_device_private *d = (struct pci_device_private*) dev;
+
+ mach_port_deallocate (mach_task_self (), d->device_port);
+}
+
+/* Walk through the FS tree to see what is allowed for us */
+static int
+enum_devices(const char *parent, struct pci_device_private **device,
+ int domain, int bus, int dev, int func, tree_level lev)
+{
+ int err, ret;
+ DIR *dir;
+ struct dirent *entry;
+ char path[NAME_MAX];
+ char server[NAME_MAX];
+ uint32_t reg;
+ size_t toread;
+ mach_port_t device_port;
+
+ dir = opendir(parent);
+ if (!dir)
+ return errno;
+
+ while ((entry = readdir(dir)) != 0) {
+ snprintf(path, NAME_MAX, "%s/%s", parent, entry->d_name);
+ if (entry->d_type == DT_DIR) {
+ if (!strncmp(entry->d_name, ".", NAME_MAX)
+ || !strncmp(entry->d_name, "..", NAME_MAX))
+ continue;
+
+ errno = 0;
+ ret = strtol(entry->d_name, 0, 16);
+ if (errno)
+ return errno;
+
+ /*
+ * We found a valid directory.
+ * Update the address and switch to the next level.
+ */
+ switch (lev) {
+ case LEVEL_DOMAIN:
+ domain = ret;
+ break;
+ case LEVEL_BUS:
+ bus = ret;
+ break;
+ case LEVEL_DEV:
+ dev = ret;
+ break;
+ case LEVEL_FUNC:
+ func = ret;
+ break;
+ default:
+ return -1;
+ }
+
+ err = enum_devices(path, device, domain, bus, dev, func, lev+1);
+ if (err == EPERM)
+ continue;
+ }
+ else {
+ if (strncmp(entry->d_name, FILE_CONFIG_NAME, NAME_MAX))
+ /* We are looking for the config file */
+ continue;
+
+ /* We found an available virtual device, add it to our list */
+ snprintf(server, NAME_MAX, "%s/%04x/%02x/%02x/%01u/%s",
+ _SERVERS_PCI_CONF, domain, bus, dev, func,
+ entry->d_name);
+ device_port = file_name_lookup(server, 0, 0);
+ if (device_port == MACH_PORT_NULL)
+ return errno;
+
+ toread = sizeof(reg);
+ err = pciclient_cfg_read(device_port, PCI_VENDOR_ID, (char*)&reg,
+ &toread);
+ if (err)
+ return err;
+ if (toread != sizeof(reg))
+ return -1;
+
+ (*device)->base.domain = domain;
+ (*device)->base.bus = bus;
+ (*device)->base.dev = dev;
+ (*device)->base.func = func;
+ (*device)->base.vendor_id = PCI_VENDOR(reg);
+ (*device)->base.device_id = PCI_DEVICE(reg);
+
+ toread = sizeof(reg);
+ err = pciclient_cfg_read(device_port, PCI_CLASS, (char*)&reg,
+ &toread);
+ if (err)
+ return err;
+ if (toread != sizeof(reg))
+ return -1;
+
+ (*device)->base.device_class = reg >> 8;
+ (*device)->base.revision = reg & 0xFF;
+
+ toread = sizeof(reg);
+ err = pciclient_cfg_read(device_port, PCI_SUB_VENDOR_ID,
+ (char*)&reg, &toread);
+ if (err)
+ return err;
+ if (toread != sizeof(reg))
+ return -1;
+
+ (*device)->base.subvendor_id = PCI_VENDOR(reg);
+ (*device)->base.subdevice_id = PCI_DEVICE(reg);
+
+ (*device)->device_port = device_port;
+
+ (*device)++;
+ }
+ }
+
+ return 0;
+}
+
+static const struct pci_system_methods hurd_pci_methods = {
+ .destroy = pci_system_x86_destroy,
+ .destroy_device = pci_device_hurd_destroy,
+ .read_rom = pci_device_hurd_read_rom,
+ .probe = pci_device_hurd_probe,
+ .map_range = pci_device_x86_map_range,
+ .unmap_range = pci_device_x86_unmap_range,
+ .read = pci_device_hurd_read,
+ .write = pci_device_hurd_write,
+ .fill_capabilities = pci_fill_capabilities_generic,
+ .open_legacy_io = pci_device_x86_open_legacy_io,
+ .close_io = pci_device_x86_close_io,
+ .read32 = pci_device_x86_read32,
+ .read16 = pci_device_x86_read16,
+ .read8 = pci_device_x86_read8,
+ .write32 = pci_device_x86_write32,
+ .write16 = pci_device_x86_write16,
+ .write8 = pci_device_x86_write8,
+ .map_legacy = pci_device_x86_map_legacy,
+ .unmap_legacy = pci_device_x86_unmap_legacy,
+};
+
+_pci_hidden int
+pci_system_hurd_create(void)
+{
+ struct pci_device_private *device;
+ int err;
+ struct pci_system_hurd *pci_sys_hurd;
+ size_t ndevs;
+ mach_port_t pci_server_port;
+
+ err = x86_enable_io();
+ if (err)
+ return err;
+
+ pci_sys_hurd = calloc(1, sizeof(struct pci_system_hurd));
+ if (pci_sys_hurd == NULL) {
+ x86_disable_io();
+ return ENOMEM;
+ }
+ pci_sys = &pci_sys_hurd->system;
+
+ pci_sys->methods = &hurd_pci_methods;
+
+ pci_server_port = file_name_lookup(_SERVERS_PCI_CONF, 0, 0);
+ if (pci_server_port == MACH_PORT_NULL)
+ return errno;
+
+ /* The server gives us the number of available devices for us */
+ err = pci_get_ndevs (pci_server_port, &ndevs);
+ if (err) {
+ mach_port_deallocate (mach_task_self (), pci_server_port);
+ return err;
+ }
+ mach_port_deallocate (mach_task_self (), pci_server_port);
+
+ pci_sys->num_devices = ndevs;
+ pci_sys->devices = calloc(ndevs, sizeof(struct pci_device_private));
+ if (pci_sys->devices == NULL) {
+ x86_disable_io();
+ free(pci_sys_hurd);
+ pci_sys = NULL;
+ return ENOMEM;
+ }
+
+ device = pci_sys->devices;
+ err = enum_devices(_SERVERS_PCI_CONF, &device, -1, -1, -1, -1,
+ LEVEL_DOMAIN);
+ if (err)
+ return err;
+
+ return 0;
+}
diff --git a/src/pciaccess_private.h b/src/pciaccess_private.h
index 2f05b29..10c9698 100644
--- a/src/pciaccess_private.h
+++ b/src/pciaccess_private.h
@@ -29,6 +29,9 @@
* \author Ian Romanick <***@us.ibm.com>
*/

+#ifndef PCIACCESS_PRIVATE_H
+#define PCIACCESS_PRIVATE_H
+
#if defined(__GNUC__) && (__GNUC__ >= 4)
# define _pci_hidden __attribute__((visibility("hidden")))
#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550)
@@ -150,6 +153,9 @@ struct pci_device_private {
#ifdef __sun
int is_primary;
#endif
+#ifdef __GNU__
+ unsigned long device_port;
+#endif
};


@@ -189,5 +195,8 @@ extern int pci_system_netbsd_create( void );
extern int pci_system_openbsd_create( void );
extern void pci_system_openbsd_init_dev_mem( int );
extern int pci_system_solx_devfs_create( void );
+extern int pci_system_hurd_create( void );
extern int pci_system_x86_create( void );
extern void pci_io_cleanup( void );
+
+#endif /* PCIACCESS_PRIVATE_H */
diff --git a/src/x86_pci.c b/src/x86_pci.c
index 49c1cab..9adb5e7 100644
--- a/src/x86_pci.c
+++ b/src/x86_pci.c
@@ -20,6 +20,8 @@
*/

#define _GNU_SOURCE
+#include "x86_pci.h"
+
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
@@ -36,7 +38,7 @@

#include <sys/io.h>

-static int
+int
x86_enable_io(void)
{
if (!ioperm(0, 0xffff, 1))
@@ -44,7 +46,7 @@ x86_enable_io(void)
return errno;
}

-static int
+int
x86_disable_io(void)
{
if (!ioperm(0, 0xffff, 0))
@@ -205,28 +207,6 @@ outl(uint32_t value, uint16_t port)

#endif

-#define PCI_VENDOR(reg) ((reg) & 0xFFFF)
-#define PCI_VENDOR_INVALID 0xFFFF
-
-#define PCI_VENDOR_ID 0x00
-#define PCI_SUB_VENDOR_ID 0x2c
-#define PCI_VENDOR_ID_COMPAQ 0x0e11
-#define PCI_VENDOR_ID_INTEL 0x8086
-
-#define PCI_DEVICE(reg) (((reg) >> 16) & 0xFFFF)
-#define PCI_DEVICE_INVALID 0xFFFF
-
-#define PCI_CLASS 0x08
-#define PCI_CLASS_DEVICE 0x0a
-#define PCI_CLASS_DISPLAY_VGA 0x0300
-#define PCI_CLASS_BRIDGE_HOST 0x0600
-
-#define PCIC_DISPLAY 0x03
-#define PCIS_DISPLAY_VGA 0x00
-
-#define PCI_HDRTYPE 0x0E
-#define PCI_IRQ 0x3C
-
struct pci_system_x86 {
struct pci_system system;
int (*read)(unsigned bus, unsigned dev, unsigned func, pciaddr_t reg, void *data, unsigned size);
@@ -633,7 +613,7 @@ pci_device_x86_unmap_range(struct pci_device *dev,

#else

-static int
+int
pci_device_x86_map_range(struct pci_device *dev,
struct pci_device_mapping *map)
{
@@ -654,7 +634,7 @@ pci_device_x86_map_range(struct pci_device *dev,
return 0;
}

-static int
+int
pci_device_x86_unmap_range(struct pci_device *dev,
struct pci_device_mapping *map)
{
@@ -715,13 +695,13 @@ pci_device_x86_write(struct pci_device *dev, const void *data,
return 0;
}

-static void
+void
pci_system_x86_destroy(void)
{
x86_disable_io();
}

-static struct pci_io_handle *
+struct pci_io_handle *
pci_device_x86_open_legacy_io(struct pci_io_handle *ret,
struct pci_device *dev, pciaddr_t base, pciaddr_t size)
{
@@ -734,7 +714,7 @@ pci_device_x86_open_legacy_io(struct pci_io_handle *ret,
return ret;
}

-static void
+void
pci_device_x86_close_io(struct pci_device *dev, struct pci_io_handle *handle)
{
/* Like in the Linux case, do not disable I/O, as it may be opened several
@@ -742,46 +722,46 @@ pci_device_x86_close_io(struct pci_device *dev, struct pci_io_handle *handle)
/* x86_disable_io(); */
}

-static uint32_t
+uint32_t
pci_device_x86_read32(struct pci_io_handle *handle, uint32_t reg)
{
return inl(reg + handle->base);
}

-static uint16_t
+uint16_t
pci_device_x86_read16(struct pci_io_handle *handle, uint32_t reg)
{
return inw(reg + handle->base);
}

-static uint8_t
+uint8_t
pci_device_x86_read8(struct pci_io_handle *handle, uint32_t reg)
{
return inb(reg + handle->base);
}

-static void
+void
pci_device_x86_write32(struct pci_io_handle *handle, uint32_t reg,
uint32_t data)
{
outl(data, reg + handle->base);
}

-static void
+void
pci_device_x86_write16(struct pci_io_handle *handle, uint32_t reg,
uint16_t data)
{
outw(data, reg + handle->base);
}

-static void
+void
pci_device_x86_write8(struct pci_io_handle *handle, uint32_t reg,
uint8_t data)
{
outb(data, reg + handle->base);
}

-static int
+int
pci_device_x86_map_legacy(struct pci_device *dev, pciaddr_t base,
pciaddr_t size, unsigned map_flags, void **addr)
{
@@ -797,7 +777,7 @@ pci_device_x86_map_legacy(struct pci_device *dev, pciaddr_t base,
return err;
}

-static int
+int
pci_device_x86_unmap_legacy(struct pci_device *dev, void *addr,
pciaddr_t size)
{
diff --git a/src/x86_pci.h b/src/x86_pci.h
new file mode 100644
index 0000000..2e00ba6
--- /dev/null
+++ b/src/x86_pci.h
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+
+/* Macros and declarations used by both x86 and Hurd modules. */
+
+#ifndef X86_PCI_H
+#define X86_PCI_H
+
+#include "pciaccess.h"
+#include "pciaccess_private.h"
+
+#define PCI_VENDOR(reg) ((reg) & 0xFFFF)
+#define PCI_VENDOR_INVALID 0xFFFF
+
+#define PCI_VENDOR_ID 0x00
+#define PCI_SUB_VENDOR_ID 0x2c
+#define PCI_VENDOR_ID_COMPAQ 0x0e11
+#define PCI_VENDOR_ID_INTEL 0x8086
+
+#define PCI_DEVICE(reg) (((reg) >> 16) & 0xFFFF)
+#define PCI_DEVICE_INVALID 0xFFFF
+
+#define PCI_CLASS 0x08
+#define PCI_CLASS_DEVICE 0x0a
+#define PCI_CLASS_DISPLAY_VGA 0x0300
+#define PCI_CLASS_BRIDGE_HOST 0x0600
+
+#define PCIC_DISPLAY 0x03
+#define PCIS_DISPLAY_VGA 0x00
+
+#define PCI_HDRTYPE 0x0E
+#define PCI_IRQ 0x3C
+
+int x86_enable_io(void);
+int x86_disable_io(void);
+void pci_system_x86_destroy(void);
+int pci_device_x86_map_range(struct pci_device *dev,
+ struct pci_device_mapping *map);
+int pci_device_x86_unmap_range(struct pci_device *dev,
+ struct pci_device_mapping *map);
+struct pci_io_handle *pci_device_x86_open_legacy_io(struct pci_io_handle *ret,
+ struct pci_device *dev, pciaddr_t base, pciaddr_t size);
+void pci_device_x86_close_io(struct pci_device *dev,
+ struct pci_io_handle *handle);
+uint32_t pci_device_x86_read32(struct pci_io_handle *handle, uint32_t reg);
+uint16_t pci_device_x86_read16(struct pci_io_handle *handle, uint32_t reg);
+uint8_t pci_device_x86_read8(struct pci_io_handle *handle, uint32_t reg);
+void pci_device_x86_write32(struct pci_io_handle *handle, uint32_t reg,
+ uint32_t data);
+void pci_device_x86_write16(struct pci_io_handle *handle, uint32_t reg,
+ uint16_t data);
+void pci_device_x86_write8(struct pci_io_handle *handle, uint32_t reg,
+ uint8_t data);
+int pci_device_x86_map_legacy(struct pci_device *dev, pciaddr_t base,
+ pciaddr_t size, unsigned map_flags, void **addr);
+int pci_device_x86_unmap_legacy(struct pci_device *dev, void *addr,
+ pciaddr_t size);
+
+#endif /* X86_PCI_H */
--
2.14.0
Joan Lledó
2017-12-03 10:21:55 UTC
Permalink
A new module for the Hurd that accesses PCI bus using available RPCs.

All references to the Hurd in the i386 access method have been removed.
---
lib/Makefile | 7 +-
lib/configure | 6 +-
lib/hurd.c | 366 +++++++++++++++++++++++++++++++++++++++++++++++++++++
lib/i386-io-hurd.h | 27 ----
lib/i386-ports.c | 4 -
lib/init.c | 5 +
lib/internal.h | 2 +-
lib/pci.h | 1 +
8 files changed, 382 insertions(+), 36 deletions(-)
create mode 100644 lib/hurd.c
delete mode 100644 lib/i386-io-hurd.h

diff --git a/lib/Makefile b/lib/Makefile
index f119b72..3e824dd 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -46,6 +46,10 @@ ifdef PCI_HAVE_PM_DARWIN_DEVICE
OBJS += darwin
endif

+ifdef PCI_HAVE_PM_HURD_CONF
+OBJS += hurd
+endif
+
all: $(PCILIB) $(PCILIBPC)

ifeq ($(SHARED),no)
@@ -74,7 +78,7 @@ $(PCILIBPC): libpci.pc.in
init.o: init.c $(INCL)
access.o: access.c $(INCL)
params.o: params.c $(INCL)
-i386-ports.o: i386-ports.c $(INCL) i386-io-hurd.h i386-io-linux.h i386-io-sunos.h i386-io-windows.h i386-io-cygwin.h
+i386-ports.o: i386-ports.c $(INCL) i386-io-linux.h i386-io-sunos.h i386-io-windows.h i386-io-cygwin.h
proc.o: proc.c $(INCL) pread.h
sysfs.o: sysfs.c $(INCL) pread.h
generic.o: generic.c $(INCL)
@@ -91,3 +95,4 @@ names-parse.o: names-parse.c $(INCL) names.h
names-hwdb.o: names-hwdb.c $(INCL) names.h
filter.o: filter.c $(INCL)
nbsd-libpci.o: nbsd-libpci.c $(INCL)
+hurd.o: hurd.c $(INCL)
diff --git a/lib/configure b/lib/configure
index 7d4cec8..1a6b171 100755
--- a/lib/configure
+++ b/lib/configure
@@ -125,9 +125,9 @@ case $sys in
echo >>$m 'WITH_LIBS+=-lpci'
LIBRESOLV=
;;
- gnu)
- echo_n " i386-ports"
- echo >>$c '#define PCI_HAVE_PM_INTEL_CONF'
+ gnu)
+ echo_n " hurd"
+ echo >>$c '#define PCI_HAVE_PM_HURD_CONF'
;;
cygwin)
echo_n " i386-ports"
diff --git a/lib/hurd.c b/lib/hurd.c
new file mode 100644
index 0000000..40aa067
--- /dev/null
+++ b/lib/hurd.c
@@ -0,0 +1,366 @@
+/*
+ * The PCI Library -- Hurd access via RPCs
+ *
+ * Copyright (c) 2017 Joan Lledó <***@member.fsf.org>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#define _GNU_SOURCE
+
+#include "internal.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <string.h>
+#include <assert.h>
+#include <hurd.h>
+#include <hurd/pci.h>
+#include <hurd/paths.h>
+
+/* Server path */
+#define _SERVERS_PCI_CONF _SERVERS_BUS "/pci"
+
+/* File names */
+#define FILE_CONFIG_NAME "config"
+#define FILE_ROM_NAME "rom"
+
+/* Level in the fs tree */
+typedef enum
+{
+ LEVEL_NONE,
+ LEVEL_DOMAIN,
+ LEVEL_BUS,
+ LEVEL_DEV,
+ LEVEL_FUNC
+} tree_level;
+
+/* Check whether there's a pci server */
+static int
+hurd_detect (struct pci_access *a)
+{
+ int err;
+ struct stat st;
+
+ err = lstat (_SERVERS_PCI_CONF, &st);
+ if (err)
+ {
+ a->error ("Could not open file `%s'", _SERVERS_PCI_CONF);
+ return 0;
+ }
+
+ /* The node must be a directory and a translator */
+ return S_ISDIR (st.st_mode) && ((st.st_mode & S_ITRANS) == S_IROOT);
+}
+
+/* Empty callbacks, we don't need any special init or cleanup */
+static void
+hurd_init (struct pci_access *a UNUSED)
+{
+}
+
+static void
+hurd_cleanup (struct pci_access *a UNUSED)
+{
+}
+
+/* Each device has its own server path. Allocate space for the port. */
+static void
+hurd_init_dev (struct pci_dev *d)
+{
+ d->aux = calloc (1, sizeof (mach_port_t));
+ assert (d->aux);
+}
+
+/* Deallocate the port and free its space */
+static void
+hurd_cleanup_dev (struct pci_dev *d)
+{
+ mach_port_t device_port;
+
+ device_port = *((mach_port_t *) d->aux);
+ mach_port_deallocate (mach_task_self (), device_port);
+
+ free (d->aux);
+}
+
+/* Walk through the FS tree to see what is allowed for us */
+static int
+enum_devices (const char *parent, struct pci_access *a, int domain, int bus,
+ int dev, int func, tree_level lev)
+{
+ int err, ret;
+ DIR *dir;
+ struct dirent *entry;
+ char path[NAME_MAX];
+ char server[NAME_MAX];
+ uint32_t vd;
+ uint8_t ht;
+ mach_port_t device_port;
+ struct pci_dev *d;
+
+ dir = opendir (parent);
+ if (!dir)
+ return errno;
+
+ while ((entry = readdir (dir)) != 0)
+ {
+ snprintf (path, NAME_MAX, "%s/%s", parent, entry->d_name);
+ if (entry->d_type == DT_DIR)
+ {
+ if (!strncmp (entry->d_name, ".", NAME_MAX)
+ || !strncmp (entry->d_name, "..", NAME_MAX))
+ continue;
+
+ errno = 0;
+ ret = strtol (entry->d_name, 0, 16);
+ if (errno)
+ return errno;
+
+ /*
+ * We found a valid directory.
+ * Update the address and switch to the next level.
+ */
+ switch (lev)
+ {
+ case LEVEL_DOMAIN:
+ domain = ret;
+ break;
+ case LEVEL_BUS:
+ bus = ret;
+ break;
+ case LEVEL_DEV:
+ dev = ret;
+ break;
+ case LEVEL_FUNC:
+ func = ret;
+ break;
+ default:
+ return -1;
+ }
+
+ err = enum_devices (path, a, domain, bus, dev, func, lev + 1);
+ if (err == EPERM)
+ continue;
+ }
+ else
+ {
+ if (strncmp (entry->d_name, FILE_CONFIG_NAME, NAME_MAX))
+ /* We are looking for the config file */
+ continue;
+
+ /* We found an available virtual device, add it to our list */
+ snprintf (server, NAME_MAX, "%s/%04x/%02x/%02x/%01u/%s",
+ _SERVERS_PCI_CONF, domain, bus, dev, func, entry->d_name);
+ device_port = file_name_lookup (server, 0, 0);
+ if (device_port == MACH_PORT_NULL)
+ return errno;
+
+ d = pci_alloc_dev (a);
+ *((mach_port_t *) d->aux) = device_port;
+ d->bus = bus;
+ d->dev = dev;
+ d->func = func;
+ pci_link_dev (a, d);
+
+ vd = pci_read_long (d, PCI_VENDOR_ID);
+ ht = pci_read_byte (d, PCI_HEADER_TYPE);
+
+ d->vendor_id = vd & 0xffff;
+ d->device_id = vd >> 16U;
+ d->known_fields = PCI_FILL_IDENT;
+ d->hdrtype = ht;
+ }
+ }
+
+ return 0;
+}
+
+/* Enumerate devices */
+static void
+hurd_scan (struct pci_access *a)
+{
+ int err;
+
+ err = enum_devices (_SERVERS_PCI_CONF, a, -1, -1, -1, -1, LEVEL_DOMAIN);
+ assert (err == 0);
+}
+
+/*
+ * Read `len' bytes to `buf'.
+ *
+ * Returns error when the number of read bytes does not match `len'.
+ */
+static int
+hurd_read (struct pci_dev *d, int pos, byte * buf, int len)
+{
+ int err;
+ size_t nread;
+ char *data;
+ mach_port_t device_port;
+
+ nread = len;
+ device_port = *((mach_port_t *) d->aux);
+ if (len > 4)
+ err = !pci_generic_block_read (d, pos, buf, nread);
+ else
+ {
+ data = (char *) buf;
+ err = pci_conf_read (device_port, pos, &data, &nread, len);
+
+ if (data != (char *) buf)
+ {
+ if (nread > (size_t) len) /* Sanity check for bogus server. */
+ {
+ vm_deallocate (mach_task_self (), (vm_address_t) data, nread);
+ return 0;
+ }
+
+ memcpy (buf, data, nread);
+ vm_deallocate (mach_task_self (), (vm_address_t) data, nread);
+ }
+ }
+ if (err)
+ return 0;
+
+ return nread == (size_t) len;
+}
+
+/*
+ * Write `len' bytes from `buf'.
+ *
+ * Returns error when the number of written bytes does not match `len'.
+ */
+static int
+hurd_write (struct pci_dev *d, int pos, byte * buf, int len)
+{
+ int err;
+ size_t nwrote;
+ mach_port_t device_port;
+
+ nwrote = len;
+ device_port = *((mach_port_t *) d->aux);
+ if (len > 4)
+ err = !pci_generic_block_write (d, pos, buf, len);
+ else
+ err = pci_conf_write (device_port, pos, (char *) buf, len, &nwrote);
+ if (err)
+ return 0;
+
+ return nwrote == (size_t) len;
+}
+
+/* Get requested info from the server */
+static int
+hurd_fill_info (struct pci_dev *d, int flags)
+{
+ int err, i;
+ struct pci_bar regions[6];
+ struct pci_xrom_bar rom;
+ size_t size;
+ char *buf;
+ mach_port_t device_port;
+
+ device_port = *((mach_port_t *) d->aux);
+
+ if (flags & PCI_FILL_IDENT)
+ {
+ d->vendor_id = pci_read_word (d, PCI_VENDOR_ID);
+ d->device_id = pci_read_word (d, PCI_DEVICE_ID);
+ }
+
+ if (flags & PCI_FILL_CLASS)
+ d->device_class = pci_read_word (d, PCI_CLASS_DEVICE);
+
+ if (flags & PCI_FILL_IRQ)
+ d->irq = pci_read_byte (d, PCI_INTERRUPT_LINE);
+
+ if (flags & PCI_FILL_BASES)
+ {
+ buf = (char *) &regions;
+ size = sizeof (regions);
+
+ err = pci_get_dev_regions (device_port, &buf, &size);
+ if (err)
+ return err;
+
+ if ((char *) &regions != buf)
+ {
+ /* Sanity check for bogus server. */
+ if (size > sizeof (regions))
+ {
+ vm_deallocate (mach_task_self (), (vm_address_t) buf, size);
+ return EGRATUITOUS;
+ }
+
+ memcpy (&regions, buf, size);
+ vm_deallocate (mach_task_self (), (vm_address_t) buf, size);
+ }
+
+ for (i = 0; i < 6; i++)
+ {
+ if (regions[i].size == 0)
+ continue;
+
+ d->base_addr[i] = regions[i].base_addr;
+ d->base_addr[i] |= regions[i].is_IO;
+ d->base_addr[i] |= regions[i].is_64 << 2;
+ d->base_addr[i] |= regions[i].is_prefetchable << 3;
+
+ if (flags & PCI_FILL_SIZES)
+ d->size[i] = regions[i].size;
+ }
+ }
+
+ if (flags & PCI_FILL_ROM_BASE)
+ {
+ /* Get rom info */
+ buf = (char *) &rom;
+ size = sizeof (rom);
+ err = pci_get_dev_rom (device_port, &buf, &size);
+ if (err)
+ return err;
+
+ if ((char *) &rom != buf)
+ {
+ /* Sanity check for bogus server. */
+ if (size > sizeof (rom))
+ {
+ vm_deallocate (mach_task_self (), (vm_address_t) buf, size);
+ return EGRATUITOUS;
+ }
+
+ memcpy (&rom, buf, size);
+ vm_deallocate (mach_task_self (), (vm_address_t) buf, size);
+ }
+
+ d->rom_base_addr = rom.base_addr;
+ d->rom_size = rom.size;
+ }
+
+ if (flags & (PCI_FILL_CAPS | PCI_FILL_EXT_CAPS))
+ flags |= pci_scan_caps (d, flags);
+
+ return flags;
+}
+
+struct pci_methods pm_hurd = {
+ "hurd",
+ "Hurd access using RPCs",
+ NULL, /* config */
+ hurd_detect,
+ hurd_init,
+ hurd_cleanup,
+ hurd_scan,
+ hurd_fill_info,
+ hurd_read,
+ hurd_write,
+ NULL, /* read_vpd */
+ hurd_init_dev,
+ hurd_cleanup_dev
+};
diff --git a/lib/i386-io-hurd.h b/lib/i386-io-hurd.h
deleted file mode 100644
index b61d656..0000000
--- a/lib/i386-io-hurd.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * The PCI Library -- Access to i386 I/O ports on GNU Hurd
- *
- * Copyright (c) 2003 Marco Gerards <***@student.han.nl>
- * Copyright (c) 2003 Martin Mares <***@ucw.cz>
- * Copyright (c) 2006 Samuel Thibault <***@ens-lyon.org> and
- * Thomas Schwinge <***@gnu.org>
- * Copyright (c) 2007 Thomas Schwinge <***@gnu.org>
- *
- * Can be freely distributed and used under the terms of the GNU GPL.
- */
-
-#include <sys/io.h>
-
-static inline int
-intel_setup_io(struct pci_access *a UNUSED)
-{
- return (ioperm (0, 65535, 1) == -1) ? 0 : 1;
-}
-
-static inline int
-intel_cleanup_io(struct pci_access *a UNUSED)
-{
- ioperm (0, 65535, 0);
-
- return -1;
-}
diff --git a/lib/i386-ports.c b/lib/i386-ports.c
index 421bbfd..e191c2d 100644
--- a/lib/i386-ports.c
+++ b/lib/i386-ports.c
@@ -6,16 +6,12 @@
* Can be freely distributed and used under the terms of the GNU GPL.
*/

-#define _GNU_SOURCE
-
#include "internal.h"

#include <unistd.h>

#if defined(PCI_OS_LINUX)
#include "i386-io-linux.h"
-#elif defined(PCI_OS_GNU)
-#include "i386-io-hurd.h"
#elif defined(PCI_OS_SUNOS)
#include "i386-io-sunos.h"
#elif defined(PCI_OS_WINDOWS)
diff --git a/lib/init.c b/lib/init.c
index c7800e0..008ce40 100644
--- a/lib/init.c
+++ b/lib/init.c
@@ -62,6 +62,11 @@ static struct pci_methods *pci_methods[PCI_ACCESS_MAX] = {
#else
NULL,
#endif
+#ifdef PCI_HAVE_PM_HURD_CONF
+ &pm_hurd,
+#else
+ NULL,
+#endif
};

void *
diff --git a/lib/internal.h b/lib/internal.h
index cbac2a7..6c8b19b 100644
--- a/lib/internal.h
+++ b/lib/internal.h
@@ -86,4 +86,4 @@ void pci_free_caps(struct pci_dev *);

extern struct pci_methods pm_intel_conf1, pm_intel_conf2, pm_linux_proc,
pm_fbsd_device, pm_aix_device, pm_nbsd_libpci, pm_obsd_device,
- pm_dump, pm_linux_sysfs, pm_darwin;
+ pm_dump, pm_linux_sysfs, pm_darwin, pm_hurd;
diff --git a/lib/pci.h b/lib/pci.h
index 794b487..99511ca 100644
--- a/lib/pci.h
+++ b/lib/pci.h
@@ -41,6 +41,7 @@ enum pci_access_type {
PCI_ACCESS_OBSD_DEVICE, /* OpenBSD /dev/pci */
PCI_ACCESS_DUMP, /* Dump file */
PCI_ACCESS_DARWIN, /* Darwin */
+ PCI_ACCESS_GNU, /* GNU/Hurd */
PCI_ACCESS_MAX
};
--
2.14.0
Joan Lledó
2017-12-03 10:21:54 UTC
Permalink
---
Makefile | 1 +
hurd/hurd_types.defs | 19 +-
hurd/hurd_types.h | 24 +-
hurd/paths.h | 3 +
hurd/pci.defs | 73 ++++
hurd/subsystems | 1 +
pci-arbiter/Makefile | 44 +++
pci-arbiter/config.h | 5 +
pci-arbiter/func_files.c | 210 +++++++++++
pci-arbiter/func_files.h | 45 +++
pci-arbiter/main.c | 117 +++++++
pci-arbiter/mig-mutate.h | 28 ++
pci-arbiter/ncache.c | 90 +++++
pci-arbiter/ncache.h | 32 ++
pci-arbiter/netfs_impl.c | 539 ++++++++++++++++++++++++++++
pci-arbiter/netfs_impl.h | 43 +++
pci-arbiter/options.c | 291 ++++++++++++++++
pci-arbiter/options.h | 74 ++++
pci-arbiter/pci-ops.c | 273 +++++++++++++++
pci-arbiter/pci_access.c | 51 +++
pci-arbiter/pci_access.h | 182 ++++++++++
pci-arbiter/pcifs.c | 450 ++++++++++++++++++++++++
pci-arbiter/pcifs.h | 210 +++++++++++
pci-arbiter/startup-ops.c | 41 +++
pci-arbiter/startup.c | 58 ++++
pci-arbiter/startup.h | 31 ++
pci-arbiter/x86_pci.c | 869 ++++++++++++++++++++++++++++++++++++++++++++++
pci-arbiter/x86_pci.h | 34 ++
28 files changed, 3836 insertions(+), 2 deletions(-)
create mode 100644 hurd/pci.defs
create mode 100644 pci-arbiter/Makefile
create mode 100644 pci-arbiter/config.h
create mode 100644 pci-arbiter/func_files.c
create mode 100644 pci-arbiter/func_files.h
create mode 100644 pci-arbiter/main.c
create mode 100644 pci-arbiter/mig-mutate.h
create mode 100644 pci-arbiter/ncache.c
create mode 100644 pci-arbiter/ncache.h
create mode 100644 pci-arbiter/netfs_impl.c
create mode 100644 pci-arbiter/netfs_impl.h
create mode 100644 pci-arbiter/options.c
create mode 100644 pci-arbiter/options.h
create mode 100644 pci-arbiter/pci-ops.c
create mode 100644 pci-arbiter/pci_access.c
create mode 100644 pci-arbiter/pci_access.h
create mode 100644 pci-arbiter/pcifs.c
create mode 100644 pci-arbiter/pcifs.h
create mode 100644 pci-arbiter/startup-ops.c
create mode 100644 pci-arbiter/startup.c
create mode 100644 pci-arbiter/startup.h
create mode 100644 pci-arbiter/x86_pci.c
create mode 100644 pci-arbiter/x86_pci.h

diff --git a/Makefile b/Makefile
index 119f130b..d5387fee 100644
--- a/Makefile
+++ b/Makefile
@@ -45,6 +45,7 @@ prog-subdirs = auth proc exec term \
init \
devnode \
eth-multiplexer \
+ pci-arbiter

ifeq ($(HAVE_SUN_RPC),yes)
prog-subdirs += nfs nfsd
diff --git a/hurd/hurd_types.defs b/hurd/hurd_types.defs
index 4d7013c8..0e9b990e 100644
--- a/hurd/hurd_types.defs
+++ b/hurd/hurd_types.defs
@@ -1,5 +1,5 @@
/* MiG type declarations for Hurd interfaces -*- C -*-
- Copyright (C) 1993,94,95,96,98,2001,02 Free Software Foundation, Inc.
+ Copyright (C) 1993,94,95,96,98,2001,02,17 Free Software Foundation, Inc.

This file is part of the GNU Hurd.

@@ -296,6 +296,23 @@ destructor: INTERRUPT_DESTRUCTOR
#endif
;

+/* PCI arbiter */
+type pci_t = mach_port_copy_send_t
+#ifdef PCI_INTRAN
+intran: PCI_INTRAN
+intranpayload: PCI_INTRAN_PAYLOAD
+#else
+#ifdef HURD_DEFAULT_PAYLOAD_TO_PORT
+intranpayload: pci_t HURD_DEFAULT_PAYLOAD_TO_PORT
+#endif
+#endif
+#ifdef PCI_OUTTRAN
+outtran: PCI_OUTTRAN
+#endif
+#ifdef PCI_DESTRUCTOR
+destructor: PCI_DESTRUCTOR
+#endif
+;

type proccoll_t = mach_port_copy_send_t;

diff --git a/hurd/hurd_types.h b/hurd/hurd_types.h
index 2960a294..420d6ec3 100644
--- a/hurd/hurd_types.h
+++ b/hurd/hurd_types.h
@@ -1,5 +1,5 @@
/* C declarations for Hurd server interfaces
- Copyright (C) 1993,94,95,96,98,99,2001,02 Free Software Foundation, Inc.
+ Copyright (C) 1993,94,95,96,98,99,2001,02,17 Free Software Foundation, Inc.

This file is part of the GNU Hurd.

@@ -50,6 +50,7 @@ typedef mach_port_t exec_startup_t;
typedef mach_port_t interrupt_t;
typedef mach_port_t proccoll_t;
typedef mach_port_t ctty_t;
+typedef mach_port_t pci_t;

#include <errno.h> /* Defines `error_t'. */

@@ -349,6 +350,7 @@ typedef int *procinfo_t;
#define FSTYPE_HTTP 0x00000018 /* Transparent HTTP */
#define FSTYPE_MEMFS 0x00000019 /* In-core filesystem */
#define FSTYPE_ISO9660 0x0000001a /* ISO9660 */
+#define FSTYPE_PCI 0x0000001b /* PCI filesystem */

/* Standard port assignments for file_exec and exec_* */
enum
@@ -375,4 +377,24 @@ enum
INIT_INT_MAX,
};

+/* PCI arbiter types*/
+#include <stdint.h>
+
+/* Memory region */
+struct pci_bar
+ {
+ uint64_t base_addr;
+ uint64_t size;
+ unsigned is_IO:1;
+ unsigned is_prefetchable:1;
+ unsigned is_64:1;
+ };
+
+/* Expansion ROM region */
+struct pci_xrom_bar
+ {
+ uint64_t base_addr;
+ uint64_t size;
+ };
+
#endif
diff --git a/hurd/paths.h b/hurd/paths.h
index e1b00e90..10ae3a6f 100644
--- a/hurd/paths.h
+++ b/hurd/paths.h
@@ -36,6 +36,9 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
in simple decimal (e.g. "/servers/socket/23"). */
#define _SERVERS_SOCKET _SERVERS "socket"

+/* Directory containing virtual filesystems for buses */
+#define _SERVERS_BUS _SERVERS "bus"
+
/* Hurd servers are specified by symbols _HURD_FOO,
the canonical pathname being /hurd/foo. */

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

Samuel
Joan Lledó
2018-01-16 11:54:27 UTC
Permalink
Hello,

I've finished my thesis and this is the final code, there are no new
features but I fixed some bugs I've found.
Mach actually provides a way to give a task the right for a given
range of I/O ports (i386_io_perm_cerate), so one could use that.
Nice!
I don't think we want to spend time on doing it for legacy ports,
and we should just work on support in the PCI arbiter: an RPC
operation which just returns a port created with i386_io_perm_create.
Ok, I'll do it that way. Thanks for your comments.
Joan Lledó
2018-01-16 11:54:29 UTC
Permalink
A new module for the Hurd that accesses PCI bus using available RPCs.

All references to the Hurd in the i386 access method have been removed.
---
lib/Makefile | 7 +-
lib/configure | 6 +-
lib/hurd.c | 366 +++++++++++++++++++++++++++++++++++++++++++++++++++++
lib/i386-io-hurd.h | 27 ----
lib/i386-ports.c | 4 -
lib/init.c | 5 +
lib/internal.h | 2 +-
lib/pci.h | 1 +
8 files changed, 382 insertions(+), 36 deletions(-)
create mode 100644 lib/hurd.c
delete mode 100644 lib/i386-io-hurd.h

diff --git a/lib/Makefile b/lib/Makefile
index f119b72..3e824dd 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -46,6 +46,10 @@ ifdef PCI_HAVE_PM_DARWIN_DEVICE
OBJS += darwin
endif

+ifdef PCI_HAVE_PM_HURD_CONF
+OBJS += hurd
+endif
+
all: $(PCILIB) $(PCILIBPC)

ifeq ($(SHARED),no)
@@ -74,7 +78,7 @@ $(PCILIBPC): libpci.pc.in
init.o: init.c $(INCL)
access.o: access.c $(INCL)
params.o: params.c $(INCL)
-i386-ports.o: i386-ports.c $(INCL) i386-io-hurd.h i386-io-linux.h i386-io-sunos.h i386-io-windows.h i386-io-cygwin.h
+i386-ports.o: i386-ports.c $(INCL) i386-io-linux.h i386-io-sunos.h i386-io-windows.h i386-io-cygwin.h
proc.o: proc.c $(INCL) pread.h
sysfs.o: sysfs.c $(INCL) pread.h
generic.o: generic.c $(INCL)
@@ -91,3 +95,4 @@ names-parse.o: names-parse.c $(INCL) names.h
names-hwdb.o: names-hwdb.c $(INCL) names.h
filter.o: filter.c $(INCL)
nbsd-libpci.o: nbsd-libpci.c $(INCL)
+hurd.o: hurd.c $(INCL)
diff --git a/lib/configure b/lib/configure
index 7d4cec8..1a6b171 100755
--- a/lib/configure
+++ b/lib/configure
@@ -125,9 +125,9 @@ case $sys in
echo >>$m 'WITH_LIBS+=-lpci'
LIBRESOLV=
;;
- gnu)
- echo_n " i386-ports"
- echo >>$c '#define PCI_HAVE_PM_INTEL_CONF'
+ gnu)
+ echo_n " hurd"
+ echo >>$c '#define PCI_HAVE_PM_HURD_CONF'
;;
cygwin)
echo_n " i386-ports"
diff --git a/lib/hurd.c b/lib/hurd.c
new file mode 100644
index 0000000..c9581df
--- /dev/null
+++ b/lib/hurd.c
@@ -0,0 +1,366 @@
+/*
+ * The PCI Library -- Hurd access via RPCs
+ *
+ * Copyright (c) 2017 Joan Lledó <***@member.fsf.org>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#define _GNU_SOURCE
+
+#include "internal.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <string.h>
+#include <assert.h>
+#include <hurd.h>
+#include <hurd/pci.h>
+#include <hurd/paths.h>
+
+/* Server path */
+#define _SERVERS_BUS_PCI _SERVERS_BUS "/pci"
+
+/* File names */
+#define FILE_CONFIG_NAME "config"
+#define FILE_ROM_NAME "rom"
+
+/* Level in the fs tree */
+typedef enum
+{
+ LEVEL_NONE,
+ LEVEL_DOMAIN,
+ LEVEL_BUS,
+ LEVEL_DEV,
+ LEVEL_FUNC
+} tree_level;
+
+/* Check whether there's a pci server */
+static int
+hurd_detect (struct pci_access *a)
+{
+ int err;
+ struct stat st;
+
+ err = lstat (_SERVERS_BUS_PCI, &st);
+ if (err)
+ {
+ a->error ("Could not open file `%s'", _SERVERS_BUS_PCI);
+ return 0;
+ }
+
+ /* The node must be a directory and a translator */
+ return S_ISDIR (st.st_mode) && ((st.st_mode & S_ITRANS) == S_IROOT);
+}
+
+/* Empty callbacks, we don't need any special init or cleanup */
+static void
+hurd_init (struct pci_access *a UNUSED)
+{
+}
+
+static void
+hurd_cleanup (struct pci_access *a UNUSED)
+{
+}
+
+/* Each device has its own server path. Allocate space for the port. */
+static void
+hurd_init_dev (struct pci_dev *d)
+{
+ d->aux = calloc (1, sizeof (mach_port_t));
+ assert (d->aux);
+}
+
+/* Deallocate the port and free its space */
+static void
+hurd_cleanup_dev (struct pci_dev *d)
+{
+ mach_port_t device_port;
+
+ device_port = *((mach_port_t *) d->aux);
+ mach_port_deallocate (mach_task_self (), device_port);
+
+ free (d->aux);
+}
+
+/* Walk through the FS tree to see what is allowed for us */
+static int
+enum_devices (const char *parent, struct pci_access *a, int domain, int bus,
+ int dev, int func, tree_level lev)
+{
+ int err, ret;
+ DIR *dir;
+ struct dirent *entry;
+ char path[NAME_MAX];
+ char server[NAME_MAX];
+ uint32_t vd;
+ uint8_t ht;
+ mach_port_t device_port;
+ struct pci_dev *d;
+
+ dir = opendir (parent);
+ if (!dir)
+ return errno;
+
+ while ((entry = readdir (dir)) != 0)
+ {
+ snprintf (path, NAME_MAX, "%s/%s", parent, entry->d_name);
+ if (entry->d_type == DT_DIR)
+ {
+ if (!strncmp (entry->d_name, ".", NAME_MAX)
+ || !strncmp (entry->d_name, "..", NAME_MAX))
+ continue;
+
+ errno = 0;
+ ret = strtol (entry->d_name, 0, 16);
+ if (errno)
+ return errno;
+
+ /*
+ * We found a valid directory.
+ * Update the address and switch to the next level.
+ */
+ switch (lev)
+ {
+ case LEVEL_DOMAIN:
+ domain = ret;
+ break;
+ case LEVEL_BUS:
+ bus = ret;
+ break;
+ case LEVEL_DEV:
+ dev = ret;
+ break;
+ case LEVEL_FUNC:
+ func = ret;
+ break;
+ default:
+ return -1;
+ }
+
+ err = enum_devices (path, a, domain, bus, dev, func, lev + 1);
+ if (err == EPERM)
+ continue;
+ }
+ else
+ {
+ if (strncmp (entry->d_name, FILE_CONFIG_NAME, NAME_MAX))
+ /* We are looking for the config file */
+ continue;
+
+ /* We found an available virtual device, add it to our list */
+ snprintf (server, NAME_MAX, "%s/%04x/%02x/%02x/%01u/%s",
+ _SERVERS_BUS_PCI, domain, bus, dev, func, entry->d_name);
+ device_port = file_name_lookup (server, 0, 0);
+ if (device_port == MACH_PORT_NULL)
+ return errno;
+
+ d = pci_alloc_dev (a);
+ *((mach_port_t *) d->aux) = device_port;
+ d->bus = bus;
+ d->dev = dev;
+ d->func = func;
+ pci_link_dev (a, d);
+
+ vd = pci_read_long (d, PCI_VENDOR_ID);
+ ht = pci_read_byte (d, PCI_HEADER_TYPE);
+
+ d->vendor_id = vd & 0xffff;
+ d->device_id = vd >> 16U;
+ d->known_fields = PCI_FILL_IDENT;
+ d->hdrtype = ht;
+ }
+ }
+
+ return 0;
+}
+
+/* Enumerate devices */
+static void
+hurd_scan (struct pci_access *a)
+{
+ int err;
+
+ err = enum_devices (_SERVERS_BUS_PCI, a, -1, -1, -1, -1, LEVEL_DOMAIN);
+ assert (err == 0);
+}
+
+/*
+ * Read `len' bytes to `buf'.
+ *
+ * Returns error when the number of read bytes does not match `len'.
+ */
+static int
+hurd_read (struct pci_dev *d, int pos, byte * buf, int len)
+{
+ int err;
+ size_t nread;
+ char *data;
+ mach_port_t device_port;
+
+ nread = len;
+ device_port = *((mach_port_t *) d->aux);
+ if (len > 4)
+ err = !pci_generic_block_read (d, pos, buf, nread);
+ else
+ {
+ data = (char *) buf;
+ err = pci_conf_read (device_port, pos, &data, &nread, len);
+
+ if (data != (char *) buf)
+ {
+ if (nread > (size_t) len) /* Sanity check for bogus server. */
+ {
+ vm_deallocate (mach_task_self (), (vm_address_t) data, nread);
+ return 0;
+ }
+
+ memcpy (buf, data, nread);
+ vm_deallocate (mach_task_self (), (vm_address_t) data, nread);
+ }
+ }
+ if (err)
+ return 0;
+
+ return nread == (size_t) len;
+}
+
+/*
+ * Write `len' bytes from `buf'.
+ *
+ * Returns error when the number of written bytes does not match `len'.
+ */
+static int
+hurd_write (struct pci_dev *d, int pos, byte * buf, int len)
+{
+ int err;
+ size_t nwrote;
+ mach_port_t device_port;
+
+ nwrote = len;
+ device_port = *((mach_port_t *) d->aux);
+ if (len > 4)
+ err = !pci_generic_block_write (d, pos, buf, len);
+ else
+ err = pci_conf_write (device_port, pos, (char *) buf, len, &nwrote);
+ if (err)
+ return 0;
+
+ return nwrote == (size_t) len;
+}
+
+/* Get requested info from the server */
+static int
+hurd_fill_info (struct pci_dev *d, int flags)
+{
+ int err, i;
+ struct pci_bar regions[6];
+ struct pci_xrom_bar rom;
+ size_t size;
+ char *buf;
+ mach_port_t device_port;
+
+ device_port = *((mach_port_t *) d->aux);
+
+ if (flags & PCI_FILL_IDENT)
+ {
+ d->vendor_id = pci_read_word (d, PCI_VENDOR_ID);
+ d->device_id = pci_read_word (d, PCI_DEVICE_ID);
+ }
+
+ if (flags & PCI_FILL_CLASS)
+ d->device_class = pci_read_word (d, PCI_CLASS_DEVICE);
+
+ if (flags & PCI_FILL_IRQ)
+ d->irq = pci_read_byte (d, PCI_INTERRUPT_LINE);
+
+ if (flags & PCI_FILL_BASES)
+ {
+ buf = (char *) &regions;
+ size = sizeof (regions);
+
+ err = pci_get_dev_regions (device_port, &buf, &size);
+ if (err)
+ return err;
+
+ if ((char *) &regions != buf)
+ {
+ /* Sanity check for bogus server. */
+ if (size > sizeof (regions))
+ {
+ vm_deallocate (mach_task_self (), (vm_address_t) buf, size);
+ return EGRATUITOUS;
+ }
+
+ memcpy (&regions, buf, size);
+ vm_deallocate (mach_task_self (), (vm_address_t) buf, size);
+ }
+
+ for (i = 0; i < 6; i++)
+ {
+ if (regions[i].size == 0)
+ continue;
+
+ d->base_addr[i] = regions[i].base_addr;
+ d->base_addr[i] |= regions[i].is_IO;
+ d->base_addr[i] |= regions[i].is_64 << 2;
+ d->base_addr[i] |= regions[i].is_prefetchable << 3;
+
+ if (flags & PCI_FILL_SIZES)
+ d->size[i] = regions[i].size;
+ }
+ }
+
+ if (flags & PCI_FILL_ROM_BASE)
+ {
+ /* Get rom info */
+ buf = (char *) &rom;
+ size = sizeof (rom);
+ err = pci_get_dev_rom (device_port, &buf, &size);
+ if (err)
+ return err;
+
+ if ((char *) &rom != buf)
+ {
+ /* Sanity check for bogus server. */
+ if (size > sizeof (rom))
+ {
+ vm_deallocate (mach_task_self (), (vm_address_t) buf, size);
+ return EGRATUITOUS;
+ }
+
+ memcpy (&rom, buf, size);
+ vm_deallocate (mach_task_self (), (vm_address_t) buf, size);
+ }
+
+ d->rom_base_addr = rom.base_addr;
+ d->rom_size = rom.size;
+ }
+
+ if (flags & (PCI_FILL_CAPS | PCI_FILL_EXT_CAPS))
+ flags |= pci_scan_caps (d, flags);
+
+ return flags;
+}
+
+struct pci_methods pm_hurd = {
+ "hurd",
+ "Hurd access using RPCs",
+ NULL, /* config */
+ hurd_detect,
+ hurd_init,
+ hurd_cleanup,
+ hurd_scan,
+ hurd_fill_info,
+ hurd_read,
+ hurd_write,
+ NULL, /* read_vpd */
+ hurd_init_dev,
+ hurd_cleanup_dev
+};
diff --git a/lib/i386-io-hurd.h b/lib/i386-io-hurd.h
deleted file mode 100644
index b61d656..0000000
--- a/lib/i386-io-hurd.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * The PCI Library -- Access to i386 I/O ports on GNU Hurd
- *
- * Copyright (c) 2003 Marco Gerards <***@student.han.nl>
- * Copyright (c) 2003 Martin Mares <***@ucw.cz>
- * Copyright (c) 2006 Samuel Thibault <***@ens-lyon.org> and
- * Thomas Schwinge <***@gnu.org>
- * Copyright (c) 2007 Thomas Schwinge <***@gnu.org>
- *
- * Can be freely distributed and used under the terms of the GNU GPL.
- */
-
-#include <sys/io.h>
-
-static inline int
-intel_setup_io(struct pci_access *a UNUSED)
-{
- return (ioperm (0, 65535, 1) == -1) ? 0 : 1;
-}
-
-static inline int
-intel_cleanup_io(struct pci_access *a UNUSED)
-{
- ioperm (0, 65535, 0);
-
- return -1;
-}
diff --git a/lib/i386-ports.c b/lib/i386-ports.c
index 421bbfd..e191c2d 100644
--- a/lib/i386-ports.c
+++ b/lib/i386-ports.c
@@ -6,16 +6,12 @@
* Can be freely distributed and used under the terms of the GNU GPL.
*/

-#define _GNU_SOURCE
-
#include "internal.h"

#include <unistd.h>

#if defined(PCI_OS_LINUX)
#include "i386-io-linux.h"
-#elif defined(PCI_OS_GNU)
-#include "i386-io-hurd.h"
#elif defined(PCI_OS_SUNOS)
#include "i386-io-sunos.h"
#elif defined(PCI_OS_WINDOWS)
diff --git a/lib/init.c b/lib/init.c
index c7800e0..008ce40 100644
--- a/lib/init.c
+++ b/lib/init.c
@@ -62,6 +62,11 @@ static struct pci_methods *pci_methods[PCI_ACCESS_MAX] = {
#else
NULL,
#endif
+#ifdef PCI_HAVE_PM_HURD_CONF
+ &pm_hurd,
+#else
+ NULL,
+#endif
};

void *
diff --git a/lib/internal.h b/lib/internal.h
index cbac2a7..6c8b19b 100644
--- a/lib/internal.h
+++ b/lib/internal.h
@@ -86,4 +86,4 @@ void pci_free_caps(struct pci_dev *);

extern struct pci_methods pm_intel_conf1, pm_intel_conf2, pm_linux_proc,
pm_fbsd_device, pm_aix_device, pm_nbsd_libpci, pm_obsd_device,
- pm_dump, pm_linux_sysfs, pm_darwin;
+ pm_dump, pm_linux_sysfs, pm_darwin, pm_hurd;
diff --git a/lib/pci.h b/lib/pci.h
index 794b487..99511ca 100644
--- a/lib/pci.h
+++ b/lib/pci.h
@@ -41,6 +41,7 @@ enum pci_access_type {
PCI_ACCESS_OBSD_DEVICE, /* OpenBSD /dev/pci */
PCI_ACCESS_DUMP, /* Dump file */
PCI_ACCESS_DARWIN, /* Darwin */
+ PCI_ACCESS_GNU, /* GNU/Hurd */
PCI_ACCESS_MAX
};
--
2.14.0
Samuel Thibault
2018-10-30 00:05:07 UTC
Permalink
Hello,
Post by Joan Lledó
All references to the Hurd in the i386 access method have been removed.
Well, we don't want to remove it entirely, otherwise we'd have a "flag
day" for switching between x86 access and pci arbiter access. The
implementation should rather try to access the arbiter and if that
fails, failback to the x86 implementation.

Samuel
Joan Lledó
2018-01-16 11:54:28 UTC
Permalink
---
Makefile | 1 +
hurd/hurd_types.defs | 19 +-
hurd/hurd_types.h | 24 +-
hurd/paths.h | 3 +
hurd/pci.defs | 73 ++++
hurd/subsystems | 1 +
pci-arbiter/Makefile | 44 +++
pci-arbiter/config.h | 5 +
pci-arbiter/func_files.c | 210 ++++++++++++
pci-arbiter/func_files.h | 44 +++
pci-arbiter/main.c | 117 +++++++
pci-arbiter/mig-mutate.h | 28 ++
pci-arbiter/ncache.c | 90 +++++
pci-arbiter/ncache.h | 32 ++
pci-arbiter/netfs_impl.c | 568 +++++++++++++++++++++++++++++++
pci-arbiter/netfs_impl.h | 43 +++
pci-arbiter/options.c | 282 +++++++++++++++
pci-arbiter/options.h | 74 ++++
pci-arbiter/pci-ops.c | 273 +++++++++++++++
pci-arbiter/pci_access.c | 51 +++
pci-arbiter/pci_access.h | 187 ++++++++++
pci-arbiter/pcifs.c | 409 ++++++++++++++++++++++
pci-arbiter/pcifs.h | 208 ++++++++++++
pci-arbiter/startup-ops.c | 40 +++
pci-arbiter/startup.c | 58 ++++
pci-arbiter/startup.h | 31 ++
pci-arbiter/x86_pci.c | 847 ++++++++++++++++++++++++++++++++++++++++++++++
pci-arbiter/x86_pci.h | 34 ++
28 files changed, 3794 insertions(+), 2 deletions(-)
create mode 100644 hurd/pci.defs
create mode 100644 pci-arbiter/Makefile
create mode 100644 pci-arbiter/config.h
create mode 100644 pci-arbiter/func_files.c
create mode 100644 pci-arbiter/func_files.h
create mode 100644 pci-arbiter/main.c
create mode 100644 pci-arbiter/mig-mutate.h
create mode 100644 pci-arbiter/ncache.c
create mode 100644 pci-arbiter/ncache.h
create mode 100644 pci-arbiter/netfs_impl.c
create mode 100644 pci-arbiter/netfs_impl.h
create mode 100644 pci-arbiter/options.c
create mode 100644 pci-arbiter/options.h
create mode 100644 pci-arbiter/pci-ops.c
create mode 100644 pci-arbiter/pci_access.c
create mode 100644 pci-arbiter/pci_access.h
create mode 100644 pci-arbiter/pcifs.c
create mode 100644 pci-arbiter/pcifs.h
create mode 100644 pci-arbiter/startup-ops.c
create mode 100644 pci-arbiter/startup.c
create mode 100644 pci-arbiter/startup.h
create mode 100644 pci-arbiter/x86_pci.c
create mode 100644 pci-arbiter/x86_pci.h

diff --git a/Makefile b/Makefile
index 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 9fa9ae29..731f50ed 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'. */

@@ -350,6 +351,7 @@ typedef int *procinfo_t;
#define FSTYPE_HTTP 0x00000018 /* Transparent HTTP */
#define FSTYPE_MEMFS 0x00000019 /* In-core filesystem */
#define FSTYPE_ISO9660 0x0000001a /* ISO9660 */
+#define FSTYPE_PCI 0x0000001b /* PCI filesystem */

/* Standard port assignments for file_exec_paths and exec_* */
enum
@@ -376,4 +378,24 @@ enum
INIT_INT_MAX,
};

+/* PCI arbiter types*/
+#include <stdint.h>
+
+/* Memory region */
+struct pci_bar
+ {
+ uint64_t base_addr;
+ uint64_t size;
+ unsigned is_IO:1;
+ unsigned is_prefetchable:1;
+ unsigned is_64:1;
+ };
+
+/* Expansion ROM region */
+struct pci_xrom_bar
+ {
+ uint64_t base_addr;
+ uint64_t size;
+ };
+
#endif
diff --git a/hurd/paths.h b/hurd/paths.h
index e1b00e90..10ae3a6f 100644
--- a/hurd/paths.h
+++ b/hurd/paths.h
@@ -36,6 +36,9 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
in simple decimal (e.g. "/servers/socket/23"). */
#define _SERVERS_SOCKET _SERVERS "socket"

+/* Directory containing virtual filesystems for buses */
+#define _SERVERS_BUS _SERVERS "bus"
+
/* Hurd servers are specified by symbols _HURD_FOO,
the canonical pathname being /hurd/foo. */

diff --git a/hurd/pci.defs b/hurd/pci.defs
new file mode 100644
index 00000000..23c90334
--- /dev/null
+++ b/hurd/pci.defs
@@ -0,0 +1,73 @@
+/* Definitions for pci-specific calls
+ Copyright (C) 2017 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+subsystem pci 39000;
+
+#include <hurd/hurd_types.defs>
+
+#ifdef PCI_IMPORTS
+PCI_IMPORTS
+INTR_INTERFACE
+#endif
+
+/*
+ * Read 'amount' bytes from offset 'reg' in the
+ * configuration space and store it in 'data'
+ */
+routine pci_conf_read (
+ master: pci_t;
+ reg: int;
+ out data: data_t, dealloc;
+ amount: vm_size_t
+);
+
+/* Write 'amount' bytes from 'data' to offset 'reg' in the config space */
+routine pci_conf_write(
+ master: pci_t;
+ reg: int;
+ data: data_t;
+ out amount: vm_size_t
+);
+
+/*
+ * Calculate the number of devices that are allowed
+ * for the user and return it in 'numdevs'.
+ */
+routine pci_get_ndevs(
+ master: pci_t;
+ out ndevs: vm_size_t
+);
+
+/*
+ * Return the memory regions for a specified device.
+ * `data' is an array of 6 struct pci_bar
+ */
+routine pci_get_dev_regions(
+ master: pci_t;
+ out data: data_t, dealloc
+);
+
+/*
+ * Return the expansion ROM bar for a given device.
+ * `data' is a struct pci_xrom_bar
+ */
+routine pci_get_dev_rom(
+ master: pci_t;
+ out data: data_t, dealloc
+);
diff --git a/hurd/subsystems b/hurd/subsystems
index c05895c2..0677bb1e 100644
--- a/hurd/subsystems
+++ b/hurd/subsystems
@@ -36,6 +36,7 @@ tape 35000 Special control operations for magtapes
login 36000 Database of logged-in users
pfinet 37000 Internet configuration calls
password 38000 Password checker
+pci 39000 PCI arbiter
<ioctl space> 100000- First subsystem of ioctl class 'f' (lowest class)
tioctl 156000 Ioctl class 't' (terminals)
tioctl 156200 (continued)
diff --git a/pci-arbiter/Makefile b/pci-arbiter/Makefile
new file mode 100644
index 00000000..c3a6db6f
--- /dev/null
+++ b/pci-arbiter/Makefile
@@ -0,0 +1,44 @@
+# Copyright (C) 2017 Free Software Foundation, Inc.
+#
+# This file is part of the GNU Hurd.
+#
+# The GNU Hurd is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# The GNU Hurd is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>.
+
+dir = pci-arbiter
+makemode = server
+
+PORTDIR = $(srcdir)/port
+
+SRCS = main.c pci-ops.c pci_access.c x86_pci.c netfs_impl.c \
+ pcifs.c ncache.c options.c func_files.c startup.c \
+ startup-ops.c
+MIGSRCS = pciServer.c startup_notifyServer.c
+OBJS = $(patsubst %.S,%.o,$(patsubst %.c,%.o, $(SRCS) $(MIGSRCS)))
+
+HURDLIBS= fshelp ports shouldbeinlibc netfs
+LDLIBS = -lpthread
+
+target = pci-arbiter
+
+include ../Makeconf
+
+CFLAGS += -I$(PORTDIR)/include
+
+CPPFLAGS += -imacros $(srcdir)/config.h
+pci-MIGSFLAGS = -imacros $(srcdir)/mig-mutate.h
+
+# cpp doesn't automatically make dependencies for -imacros dependencies. argh.
+pci_S.h pciServer.c: mig-mutate.h
+
+$(OBJS): config.h
diff --git a/pci-arbiter/config.h b/pci-arbiter/config.h
new file mode 100644
index 00000000..b0d5196d
--- /dev/null
+++ b/pci-arbiter/config.h
@@ -0,0 +1,5 @@
+#define __KERNEL__ 1
+#undef __SMP__
+
+#define _HURD_ 1
+
diff --git a/pci-arbiter/func_files.c b/pci-arbiter/func_files.c
new file mode 100644
index 00000000..a9901484
--- /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..deba5525
--- /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..6ade4e30
--- /dev/null
+++ b/pci-arbiter/main.c
@@ -0,0 +1,117 @@
+/*
+ Copyright (C) 2017 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Translator initialization and demuxing */
+
+#include <stdio.h>
+#include <error.h>
+#include <fcntl.h>
+#include <version.h>
+#include <argp.h>
+#include <hurd/netfs.h>
+
+#include <pci_S.h>
+#include <startup_notify_S.h>
+#include "libnetfs/io_S.h"
+#include "libnetfs/fs_S.h"
+#include "libports/notify_S.h"
+#include "libnetfs/fsys_S.h"
+#include "libports/interrupt_S.h"
+#include "libnetfs/ifsock_S.h"
+#include <pci_access.h>
+#include <pcifs.h>
+#include <startup.h>
+
+/* Libnetfs stuff */
+int netfs_maxsymlinks = 0;
+char *netfs_server_name = "pci-arbiter";
+char *netfs_server_version = HURD_VERSION;
+
+int
+netfs_demuxer (mach_msg_header_t * inp, mach_msg_header_t * outp)
+{
+ mig_routine_t routine;
+
+ if ((routine = netfs_io_server_routine (inp)) ||
+ (routine = netfs_fs_server_routine (inp)) ||
+ (routine = ports_notify_server_routine (inp)) ||
+ (routine = netfs_fsys_server_routine (inp)) ||
+ (routine = ports_interrupt_server_routine (inp)) ||
+ (routine = netfs_ifsock_server_routine (inp)) ||
+ (routine = pci_server_routine (inp)) ||
+ (routine = startup_notify_server_routine (inp)))
+ {
+ (*routine) (inp, outp);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ mach_port_t bootstrap;
+
+ /* Parse options */
+ alloc_file_system (&fs);
+ argp_parse (netfs_runtime_argp, argc, argv, 0, 0, 0);
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap == MACH_PORT_NULL)
+ error (1, 0, "must be started as a translator");
+
+ /* Initialize netfs and start the translator. */
+ netfs_init ();
+
+ err = maptime_map (0, 0, &pcifs_maptime);
+ if (err)
+ error (1, err, "mapping time");
+
+ /* Start the PCI system */
+ err = pci_system_init ();
+ if (err)
+ error (1, err, "Starting the PCI system");
+
+ /* Create the PCI filesystem */
+ err = init_file_system (netfs_startup (bootstrap, O_READ), fs);
+ if (err)
+ error (1, err, "Creating the PCI filesystem");
+
+ /* Create the filesystem tree */
+ err = create_fs_tree (fs, pci_sys);
+ if (err)
+ error (1, err, "Creating the PCI filesystem tree");
+
+ /* Set permissions */
+ err = fs_set_permissions (fs);
+ if (err)
+ error (1, err, "Setting permissions");
+
+ /*
+ * Ask init to tell us when the system is going down,
+ * so we can try to be friendly to our correspondents on the network.
+ */
+ arrange_shutdown_notification ();
+
+ netfs_server_loop (); /* Never returns. */
+
+ return 0;
+}
diff --git a/pci-arbiter/mig-mutate.h b/pci-arbiter/mig-mutate.h
new file mode 100644
index 00000000..f5098c97
--- /dev/null
+++ b/pci-arbiter/mig-mutate.h
@@ -0,0 +1,28 @@
+/*
+ Copyright (C) 2017 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Only CPP macro definitions should go in this file. */
+
+#define PCI_IMPORTS \
+ import "../libnetfs/priv.h"; \
+
+#define PCI_INTRAN protid_t begin_using_protid_port (pci_t)
+#define PCI_INTRAN_PAYLOAD protid_t begin_using_protid_payload
+#define PCI_DESTRUCTOR end_using_protid_port (protid_t)
diff --git a/pci-arbiter/ncache.c b/pci-arbiter/ncache.c
new file mode 100644
index 00000000..67c70b57
--- /dev/null
+++ b/pci-arbiter/ncache.c
@@ -0,0 +1,90 @@
+/* Node caching
+
+ Copyright (C) 1997 Free Software Foundation, Inc.
+ Written by Miles Bader <***@gnu.org>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <ncache.h>
+
+#include <unistd.h>
+#include <string.h>
+#include <hurd/netfs.h>
+
+#include <pcifs.h>
+#include <netfs_impl.h>
+
+/* Implementation of node caching functions */
+
+/* Remove NN's node from its position in FS's node cache. */
+void
+node_unlink (struct node *node, struct pcifs *fs)
+{
+ struct netnode *nn = node->nn;
+ if (nn->ncache_next)
+ nn->ncache_next->nn->ncache_prev = nn->ncache_prev;
+ if (nn->ncache_prev)
+ nn->ncache_prev->nn->ncache_next = nn->ncache_next;
+ if (fs->node_cache_mru == node)
+ fs->node_cache_mru = nn->ncache_next;
+ if (fs->node_cache_lru == node)
+ fs->node_cache_lru = nn->ncache_prev;
+ nn->ncache_next = 0;
+ nn->ncache_prev = 0;
+ fs->node_cache_len--;
+}
+
+/* Add NODE to the recently-used-node cache, which adds a reference to
+ prevent it from going away. NODE should be locked. */
+void
+node_cache (struct node *node)
+{
+ struct netnode *nn = node->nn;
+
+ pthread_mutex_lock (&fs->node_cache_lock);
+
+ if (fs->params.node_cache_max > 0 || fs->node_cache_len > 0)
+ {
+ if (fs->node_cache_mru != node)
+ {
+ if (nn->ncache_next || nn->ncache_prev)
+ /* Node is already in the cache. */
+ node_unlink (node, fs);
+ else
+ /* Add a reference from the cache. */
+ netfs_nref (node);
+
+ nn->ncache_next = fs->node_cache_mru;
+ nn->ncache_prev = 0;
+ if (fs->node_cache_mru)
+ fs->node_cache_mru->nn->ncache_prev = node;
+ if (!fs->node_cache_lru)
+ fs->node_cache_lru = node;
+ fs->node_cache_mru = node;
+ fs->node_cache_len++;
+ }
+
+ /* Forget the least used nodes. */
+ while (fs->node_cache_len > fs->params.node_cache_max)
+ {
+ struct node *lru = fs->node_cache_lru;
+ node_unlink (lru, fs);
+ netfs_nrele (lru);
+ }
+ }
+
+ pthread_mutex_unlock (&fs->node_cache_lock);
+}
diff --git a/pci-arbiter/ncache.h b/pci-arbiter/ncache.h
new file mode 100644
index 00000000..f39f798a
--- /dev/null
+++ b/pci-arbiter/ncache.h
@@ -0,0 +1,32 @@
+/*
+ Copyright (C) 2017 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Header for node caching functions */
+
+#ifndef NCACHE_H
+#define NCACHE_H
+
+#include <hurd/netfs.h>
+
+#include <pcifs.h>
+
+void node_cache (struct node *node);
+void node_unlink (struct node *node, struct pcifs *fs);
+
+#endif /* NCACHE_H */
diff --git a/pci-arbiter/netfs_impl.c b/pci-arbiter/netfs_impl.c
new file mode 100644
index 00000000..0541be34
--- /dev/null
+++ b/pci-arbiter/netfs_impl.c
@@ -0,0 +1,568 @@
+/*
+ Copyright (C) 2017 Free Software Foundation, Inc.
+ Written by Miles Bader <***@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..b5d06e3c
--- /dev/null
+++ b/pci-arbiter/netfs_impl.h
@@ -0,0 +1,43 @@
+/*
+ Copyright (C) 2017 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Data types required to create a directory tree using libnetfs */
+
+#ifndef NETFS_IMPL_H
+#define NETFS_IMPL_H
+
+#include <hurd/netfs.h>
+
+#include <pcifs.h>
+
+/*
+ * A netnode has a 1:1 relation with a generic libnetfs node. Hence, it's only
+ * purpose is to extend a generic node to add the new attributes our problem
+ * requires.
+ */
+struct netnode
+{
+ /* Light node */
+ struct pcifs_dirent *ln;
+
+ /* Position in the node cache. */
+ struct node *ncache_next, *ncache_prev;
+};
+
+#endif /* NETFS_IMPL_H */
diff --git a/pci-arbiter/options.c b/pci-arbiter/options.c
new file mode 100644
index 00000000..39626460
--- /dev/null
+++ b/pci-arbiter/options.c
@@ -0,0 +1,282 @@
+/*
+ Copyright (C) 2017 Free Software Foundation, Inc.
+
+ Written by Miles Bader <***@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..ddab0023
--- /dev/null
+++ b/pci-arbiter/options.h
@@ -0,0 +1,74 @@
+/*
+ Copyright (C) 2017 Free Software Foundation, Inc.
+
+ Written by Miles Bader <***@gnu.org>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Fsysopts and command line option parsing */
+
+#ifndef OPTIONS_H
+#define OPTIONS_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <argp.h>
+
+#include <pcifs.h>
+
+#define STR2(x) #x
+#define STR(x) STR2(x)
+
+/* Used to hold data during argument parsing. */
+struct parse_hook
+{
+ /* A list of specified permission sets and their corresponding options. */
+ struct pcifs_perm *permsets;
+ size_t num_permsets;
+
+ /* Permission set to which options apply. */
+ struct pcifs_perm *curset;
+
+ /* Node cache length */
+ size_t ncache_len;
+};
+
+/* Lwip translator options. Used for both startup and runtime. */
+static const struct argp_option options[] = {
+ {0, 0, 0, 0, "Permission scope:", 1},
+ {"class", 'C', "CLASS", 0, "Device class in hexadecimal"},
+ {"subclass", 's', "SUBCLASS", 0,
+ "Device subclass in hexadecimal, only valid with -c"},
+ {"domain", 'D', "DOMAIN", 0, "Device domain in hexadecimal"},
+ {"bus", 'b', "BUS", 0, "Device bus in hexadecimal, only valid with -D"},
+ {"dev", 'd', "DEV", 0, "Device dev in hexadecimal, only valid with -b"},
+ {"func", 'f', "FUNC", 0, "Device func in hexadecimal, only valid with -d"},
+ {0, 0, 0, 0, "These apply to a given permission scope:", 2},
+ {"uid", 'U', "UID", 0, "User ID to give permissions to"},
+ {"gid", 'G', "GID", 0, "Group ID to give permissions to"},
+ {0, 0, 0, 0, "Global configuration options:", 3},
+ {"ncache", 'n', "LENGTH", 0,
+ "Node cache length. " STR (NODE_CACHE_MAX) " by default"},
+ {0}
+};
+
+static const char doc[] = "More than one permission scope may be specified. \
+Uppercase options create a new permission scope if the current one already \
+has a value for that option. If one node is covered by more than one \
+permission scope, only the first permission is applied to that node.";
+
+#endif // OPTIONS_H
diff --git a/pci-arbiter/pci-ops.c b/pci-arbiter/pci-ops.c
new file mode 100644
index 00000000..9a2080d3
--- /dev/null
+++ b/pci-arbiter/pci-ops.c
@@ -0,0 +1,273 @@
+/*
+ Copyright (C) 2017 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Implementation of PCI operations */
+
+#include <pci_S.h>
+
+#include <fcntl.h>
+#include <hurd/netfs.h>
+#include <sys/mman.h>
+
+#include <pci_access.h>
+#include <pcifs.h>
+#include <func_files.h>
+
+static error_t
+check_permissions (struct protid *master, int flags)
+{
+ error_t err = 0;
+ struct node *node;
+ struct pcifs_dirent *e;
+
+ node = master->po->np;
+ e = node->nn->ln;
+
+ /* Check wheter the user has permissions to access this node */
+ err = entry_check_perms (master->user, e, flags);
+ if (err)
+ return err;
+
+ /* Check wheter the request has been sent to the proper node */
+ if (e->domain != 0 /* Only domain 0 can be accessed by I/O ports */
+ || e->bus < 0 || e->dev < 0 || e->func < 0)
+ err = EINVAL;
+
+ return err;
+}
+
+static size_t
+calculate_ndevs (struct iouser *user)
+{
+ size_t ndevs = 0;
+ int i;
+ struct pcifs_dirent *e;
+
+ for (i = 0, e = fs->entries; i < fs->num_entries; i++, e++)
+ {
+ if (e->func < 0 /* Skip entries without a full address */
+ || !S_ISDIR (e->stat.st_mode)) /* and entries that are not folders */
+ continue;
+
+ if (!entry_check_perms (user, e, O_READ))
+ /* If no error, user may access this device */
+ ndevs++;
+ }
+
+ return ndevs;
+}
+
+/*
+ * Read min(amount,*datalen) bytes and store them on `*data'.
+ *
+ * `*datalen' is updated.
+ */
+error_t
+S_pci_conf_read (struct protid * master, int reg, char **data,
+ size_t * datalen, mach_msg_type_number_t amount)
+{
+ error_t err;
+ pthread_mutex_t *lock;
+ struct pcifs_dirent *e;
+
+ if (!master)
+ return EOPNOTSUPP;
+
+ e = master->po->np->nn->ln;
+ if (strncmp (e->name, FILE_CONFIG_NAME, NAME_SIZE))
+ /* This operation may only be addressed to the config file */
+ return EINVAL;
+
+ lock = &fs->pci_conf_lock;
+
+ err = check_permissions (master, O_READ);
+ if (err)
+ return err;
+
+ /*
+ * We don't allocate new memory since we expect no more than 4 bytes-long
+ * buffers. Instead, we just take the lower value as length.
+ */
+ if (amount > *datalen)
+ amount = *datalen;
+
+ /*
+ * The server is not single-threaded anymore. Incoming rpcs are handled by
+ * libnetfs which is multi-threaded. A lock is needed for arbitration.
+ */
+ pthread_mutex_lock (lock);
+ err = pci_sys->read (e->bus, e->dev, e->func, reg, *data, amount);
+ pthread_mutex_unlock (lock);
+
+ if (!err)
+ {
+ *datalen = amount;
+ /* Update atime */
+ UPDATE_TIMES (e, TOUCH_ATIME);
+ }
+
+ return err;
+}
+
+/* Write `datalen' bytes from `data'. `amount' is updated. */
+error_t
+S_pci_conf_write (struct protid * master, int reg, char *data, size_t datalen,
+ mach_msg_type_number_t * amount)
+{
+ error_t err;
+ pthread_mutex_t *lock;
+ struct pcifs_dirent *e;
+
+ if (!master)
+ return EOPNOTSUPP;
+
+ e = master->po->np->nn->ln;
+ if (strncmp (e->name, FILE_CONFIG_NAME, NAME_SIZE))
+ /* This operation may only be addressed to the config file */
+ return EINVAL;
+
+ lock = &fs->pci_conf_lock;
+
+ err = check_permissions (master, O_WRITE);
+ if (err)
+ return err;
+
+ pthread_mutex_lock (lock);
+ err = pci_sys->write (e->bus, e->dev, e->func, reg, data, datalen);
+ pthread_mutex_unlock (lock);
+
+ if (!err)
+ {
+ *amount = datalen;
+ /* Update mtime and ctime */
+ UPDATE_TIMES (e, TOUCH_MTIME | TOUCH_CTIME);
+ }
+
+ return err;
+}
+
+/* Write in `amount' the number of devices allowed for the user. */
+error_t
+S_pci_get_ndevs (struct protid * master, mach_msg_type_number_t * amount)
+{
+ /* This RPC may only be addressed to the root node */
+ if (master->po->np != fs->root)
+ return EINVAL;
+
+ *amount = calculate_ndevs (master->user);
+
+ return 0;
+}
+
+/*
+ * Return in `data' the information about the available memory
+ * regions in the given device.
+ */
+error_t
+S_pci_get_dev_regions (struct protid * master, char **data, size_t * datalen)
+{
+ error_t err;
+ struct pcifs_dirent *e;
+ struct pci_bar regions[6], *r;
+ size_t size;
+ int i;
+
+ if (!master)
+ return EOPNOTSUPP;
+
+ e = master->po->np->nn->ln;
+ if (strncmp (e->name, FILE_CONFIG_NAME, NAME_SIZE))
+ /* This operation may only be addressed to the config file */
+ return EINVAL;
+
+ err = check_permissions (master, O_READ);
+ if (err)
+ return err;
+
+ /* Allocate memory if needed */
+ size = sizeof (regions);
+ if (size > *datalen)
+ {
+ *data = mmap (0, size, PROT_READ | PROT_WRITE, MAP_ANON, 0, 0);
+ if (*data == MAP_FAILED)
+ return ENOMEM;
+ }
+
+ /* Copy the regions info */
+ for (i = 0, r = (struct pci_bar *) *data; i < 6; i++, r++)
+ {
+ r->base_addr = e->device->regions[i].base_addr;
+ r->size = e->device->regions[i].size;
+ r->is_IO = e->device->regions[i].is_IO;
+ r->is_prefetchable = e->device->regions[i].is_prefetchable;
+ r->is_64 = e->device->regions[i].is_64;
+ }
+
+ /* Update atime */
+ UPDATE_TIMES (e, TOUCH_ATIME);
+
+ *datalen = size;
+
+ return 0;
+}
+
+/*
+ * Return in `data' the information about the expansion rom of the given device
+ */
+error_t
+S_pci_get_dev_rom (struct protid * master, char **data, size_t * datalen)
+{
+ error_t err;
+ struct pcifs_dirent *e;
+ struct pci_xrom_bar rom;
+ size_t size;
+
+ if (!master)
+ return EOPNOTSUPP;
+
+ e = master->po->np->nn->ln;
+ if (strncmp (e->name, FILE_CONFIG_NAME, NAME_SIZE))
+ /* This operation may only be addressed to the config file */
+ return EINVAL;
+
+ err = check_permissions (master, O_READ);
+ if (err)
+ return err;
+
+ /* Allocate memory if needed */
+ size = sizeof (rom);
+ if (size > *datalen)
+ {
+ *data = mmap (0, size, PROT_READ | PROT_WRITE, MAP_ANON, 0, 0);
+ if (*data == MAP_FAILED)
+ return ENOMEM;
+ }
+
+ /* Copy the regions info */
+ rom.base_addr = e->device->rom_base;
+ rom.size = e->device->rom_size;
+ memcpy (*data, &rom, size);
+
+ /* Update atime */
+ UPDATE_TIMES (e, TOUCH_ATIME);
+
+ *datalen = size;
+
+ return 0;
+}
diff --git a/pci-arbiter/pci_access.c b/pci-arbiter/pci_access.c
new file mode 100644
index 00000000..eded1bff
--- /dev/null
+++ b/pci-arbiter/pci_access.c
@@ -0,0 +1,51 @@
+/*
+ * (C) Copyright IBM Corporation 2006
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * on the rights to use, copy, modify, merge, publish, distribute, sub
+ * license, and/or sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * PCI access general header.
+ *
+ * Following code is borrowed from libpciaccess:
+ * https://cgit.freedesktop.org/xorg/lib/libpciaccess/
+ */
+
+#include <pci_access.h>
+
+#include <errno.h>
+
+#include <x86_pci.h>
+
+/* Configure PCI parameters */
+int
+pci_system_init (void)
+{
+ int err = ENOSYS;
+
+#ifdef __GNU__
+ err = pci_system_x86_create ();
+#else
+#error "Unsupported OS"
+#endif
+
+ return err;
+}
diff --git a/pci-arbiter/pci_access.h b/pci-arbiter/pci_access.h
new file mode 100644
index 00000000..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..a15bcc32
--- /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..55ad365f
--- /dev/null
+++ b/pci-arbiter/pcifs.h
@@ -0,0 +1,208 @@
+/*
+ Copyright (C) 2017 Free Software Foundation, Inc.
+ Written by Miles Bader <***@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..15bf2e06
--- /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..56209f3d
--- /dev/null
+++ b/pci-arbiter/startup.c
@@ -0,0 +1,58 @@
+/*
+ Copyright (C) 2017 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Startup and shutdown notifications management */
+
+#include <startup.h>
+
+#include <unistd.h>
+#include <hurd/paths.h>
+#include <hurd/startup.h>
+#include <hurd/netfs.h>
+
+void
+arrange_shutdown_notification ()
+{
+ error_t err;
+ mach_port_t initport, notify;
+ struct port_info *pi;
+
+ pci_shutdown_notify_class = ports_create_class (0, 0);
+
+ /* Arrange to get notified when the system goes down,
+ but if we fail for some reason, just silently give up. No big deal. */
+
+ err = ports_create_port (pci_shutdown_notify_class, netfs_port_bucket,
+ sizeof (struct port_info), &pi);
+ if (err)
+ return;
+
+ initport = file_name_lookup (_SERVERS_STARTUP, 0, 0);
+ if (initport == MACH_PORT_NULL)
+ return;
+
+ notify = ports_get_send_right (pi);
+ ports_port_deref (pi);
+ startup_request_notification (initport, notify,
+ MACH_MSG_TYPE_MAKE_SEND,
+ program_invocation_short_name);
+ mach_port_deallocate (mach_task_self (), notify);
+ mach_port_deallocate (mach_task_self (), initport);
+}
diff --git a/pci-arbiter/startup.h b/pci-arbiter/startup.h
new file mode 100644
index 00000000..12746f3b
--- /dev/null
+++ b/pci-arbiter/startup.h
@@ -0,0 +1,31 @@
+/*
+ Copyright (C) 2017 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef STARTUP_H
+#define STARTUP_H
+
+/* Startup and shutdown notifications management */
+
+/* Port class for startup requests */
+struct port_class *pci_shutdown_notify_class;
+
+void arrange_shutdown_notification ();
+
+#endif /* STARTUP_H */
diff --git a/pci-arbiter/x86_pci.c b/pci-arbiter/x86_pci.c
new file mode 100644
index 00000000..aedc119c
--- /dev/null
+++ b/pci-arbiter/x86_pci.c
@@ -0,0 +1,847 @@
+/*
+ * Copyright (c) 2017 Joan Lledó
+ * Copyright (c) 2009, 2012 Samuel Thibault
+ * Heavily inspired from the freebsd, netbsd, and openbsd backends
+ * (C) Copyright Eric Anholt 2006
+ * (C) Copyright IBM Corporation 2006
+ * Copyright (c) 2008 Juan Romero Pardines
+ * Copyright (c) 2008 Mark Kettenis
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * PCI backend for x86 (32 and 64 bit) architectures.
+ *
+ * Following code is borrowed from libpciaccess:
+ * https://cgit.freedesktop.org/xorg/lib/libpciaccess/
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <x86_pci.h>
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/io.h>
+#include <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 */
--
2.14.0
Samuel Thibault
2018-10-30 00:02:20 UTC
Permalink
Hello,

I have commited this. While skimming over the changes, I noted some
TODOs which we should work on, but don't prevent from using it.

Samuel
Joan Lledó
2018-01-16 11:54:30 UTC
Permalink
This new module uses Hurd's RPCs for accessing the PCI configuration space.
Direct access as in {read_write}_{8,16,32} functions is done by the old x86
module.

Some x86 function prototypes are now declared in a new header for the Hurd
module to use them, in order to duplicate as little code as possible.
---
src/Makefile.am | 4 +-
src/common_init.c | 4 +-
src/hurd_pci.c | 490 ++++++++++++++++++++++++++++++++++++++++++++++++
src/pciaccess_private.h | 9 +
src/x86_pci.c | 54 ++----
src/x86_pci.h | 77 ++++++++
6 files changed, 598 insertions(+), 40 deletions(-)
create mode 100644 src/hurd_pci.c
create mode 100644 src/x86_pci.h

diff --git a/src/Makefile.am b/src/Makefile.am
index 3a46a85..f222aa5 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -52,12 +52,12 @@ VGA_ARBITER = common_vgaarb_stub.c
endif

if GNU
-OS_SUPPORT = x86_pci.c
+OS_SUPPORT = hurd_pci.c x86_pci.c x86_pci.h
VGA_ARBITER = common_vgaarb_stub.c
endif

if CYGWIN
-OS_SUPPORT = x86_pci.c
+OS_SUPPORT = x86_pci.c x86_pci.h
VGA_ARBITER = common_vgaarb_stub.c
endif

diff --git a/src/common_init.c b/src/common_init.c
index b1c0c3e..21572ee 100644
--- a/src/common_init.c
+++ b/src/common_init.c
@@ -62,7 +62,9 @@ pci_system_init( void )
err = pci_system_openbsd_create();
#elif defined(__sun)
err = pci_system_solx_devfs_create();
-#elif defined(__GNU__) || defined(__CYGWIN__)
+#elif defined(__GNU__)
+ err = pci_system_hurd_create();
+#elif defined(__CYGWIN__)
err = pci_system_x86_create();
#else
# error "Unsupported OS"
diff --git a/src/hurd_pci.c b/src/hurd_pci.c
new file mode 100644
index 0000000..abdcb0e
--- /dev/null
+++ b/src/hurd_pci.c
@@ -0,0 +1,490 @@
+/*
+ * Copyright (c) 2017, Joan Lledó
+ * Copyright (c) 2009, 2012 Samuel Thibault
+ * Heavily inspired from the freebsd, netbsd, and openbsd backends
+ * (C) Copyright Eric Anholt 2006
+ * (C) Copyright IBM Corporation 2006
+ * Copyright (c) 2008 Juan Romero Pardines
+ * Copyright (c) 2008 Mark Kettenis
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define _GNU_SOURCE
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <sys/mman.h>
+#include <string.h>
+#include <strings.h>
+#include <hurd.h>
+#include <hurd/pci.h>
+#include <hurd/paths.h>
+
+#include "x86_pci.h"
+#include "pciaccess.h"
+#include "pciaccess_private.h"
+
+/*
+ * Hurd PCI access using RPCs.
+ *
+ * Some functions are shared with the x86 module to avoid repeating code.
+ */
+
+/* Server path */
+#define _SERVERS_BUS_PCI _SERVERS_BUS "/pci"
+
+/* File names */
+#define FILE_CONFIG_NAME "config"
+#define FILE_ROM_NAME "rom"
+
+/* Level in the fs tree */
+typedef enum {
+ LEVEL_NONE,
+ LEVEL_DOMAIN,
+ LEVEL_BUS,
+ LEVEL_DEV,
+ LEVEL_FUNC
+} tree_level;
+
+struct pci_system_hurd {
+ struct pci_system system;
+};
+
+static int
+pci_device_hurd_probe(struct pci_device *dev)
+{
+ uint8_t irq;
+ int err, i;
+ struct pci_bar regions[6];
+ struct pci_xrom_bar rom;
+ struct pci_device_private *d;
+ size_t size;
+ char *buf;
+
+ /* Many of the fields were filled in during initial device enumeration.
+ * At this point, we need to fill in regions, rom_size, and irq.
+ */
+
+ err = pci_device_cfg_read_u8(dev, &irq, PCI_IRQ);
+ if (err)
+ return err;
+ dev->irq = irq;
+
+ /* Get regions */
+ buf = (char *)&regions;
+ size = sizeof(regions);
+ d = (struct pci_device_private *)dev;
+ err = pci_get_dev_regions(d->device_port, &buf, &size);
+ if(err)
+ return err;
+
+ if((char*)&regions != buf)
+ {
+ /* Sanity check for bogus server. */
+ if(size > sizeof(regions))
+ {
+ vm_deallocate(mach_task_self(), (vm_address_t) buf, size);
+ return EGRATUITOUS;
+ }
+
+ memcpy(&regions, buf, size);
+ vm_deallocate(mach_task_self(), (vm_address_t) buf, size);
+ }
+
+ for(i=0; i<6; i++)
+ {
+ if(regions[i].size == 0)
+ continue;
+
+ dev->regions[i].base_addr = regions[i].base_addr;
+ dev->regions[i].size = regions[i].size;
+ dev->regions[i].is_IO = regions[i].is_IO;
+ dev->regions[i].is_prefetchable = regions[i].is_prefetchable;
+ dev->regions[i].is_64 = regions[i].is_64;
+ }
+
+ /* Get rom info */
+ buf = (char *)&rom;
+ size = sizeof(rom);
+ err = pci_get_dev_rom(d->device_port, &buf, &size);
+ if(err)
+ return err;
+
+ if((char*)&rom != buf)
+ {
+ /* Sanity check for bogus server. */
+ if(size > sizeof(rom))
+ {
+ vm_deallocate(mach_task_self(), (vm_address_t) buf, size);
+ return EGRATUITOUS;
+ }
+
+ memcpy(&rom, buf, size);
+ vm_deallocate(mach_task_self(), (vm_address_t) buf, size);
+ }
+
+ d->rom_base = rom.base_addr;
+ dev->rom_size = rom.size;
+
+ return 0;
+}
+
+/*
+ * Read `nbytes' bytes from `reg' in device's configuretion space
+ * and store them in `buf'.
+ *
+ * It's assumed that `nbytes' bytes are allocated in `buf'
+ */
+static int
+pciclient_cfg_read(mach_port_t device_port, int reg, char *buf,
+ size_t * nbytes)
+{
+ int err;
+ size_t nread;
+ char *data;
+
+ data = buf;
+ nread = *nbytes;
+ err = pci_conf_read(device_port, reg, &data, &nread, *nbytes);
+ if (err)
+ return err;
+
+ if (data != buf) {
+ if (nread > *nbytes) /* Sanity check for bogus server. */ {
+ vm_deallocate(mach_task_self(), (vm_address_t) data, nread);
+ return EGRATUITOUS;
+ }
+
+ memcpy(buf, data, nread);
+ vm_deallocate(mach_task_self(), (vm_address_t)data, nread);
+ }
+
+ *nbytes = nread;
+
+ return 0;
+}
+
+/* Write `nbytes' bytes from `buf' to `reg' in device's configuration space */
+static int
+pciclient_cfg_write(mach_port_t device_port, int reg, char *buf,
+ size_t * nbytes)
+{
+ int err;
+ size_t nwrote;
+
+ err = pci_conf_write(device_port, reg, buf, *nbytes, &nwrote);
+
+ if (!err)
+ *nbytes = nwrote;
+
+ return err;
+}
+
+/*
+ * Read up to `size' bytes from `dev' configuration space to `data' starting
+ * at `offset'. Write the amount on read bytes in `bytes_read'.
+ */
+static int
+pci_device_hurd_read(struct pci_device *dev, void *data,
+ pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_read)
+{
+ int err;
+ struct pci_device_private *d;
+
+ *bytes_read = 0;
+ d = (struct pci_device_private *)dev;
+ while (size > 0) {
+ size_t toread = 1 << (ffs(0x4 + (offset & 0x03)) - 1);
+ if (toread > size)
+ toread = size;
+
+ err = pciclient_cfg_read(d->device_port, offset, (char*)data,
+ &toread);
+ if (err)
+ return err;
+
+ offset += toread;
+ data = (char*)data + toread;
+ size -= toread;
+ *bytes_read += toread;
+ }
+ return 0;
+}
+
+/*
+ * Write up to `size' bytes from `data' to `dev' configuration space starting
+ * at `offset'. Write the amount on written bytes in `bytes_written'.
+ */
+static int
+pci_device_hurd_write(struct pci_device *dev, const void *data,
+ pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_written)
+{
+ int err;
+ struct pci_device_private *d;
+
+ *bytes_written = 0;
+ d = (struct pci_device_private *)dev;
+ while (size > 0) {
+ size_t towrite = 4;
+ if (towrite > size)
+ towrite = size;
+ if (towrite > 4 - (offset & 0x3))
+ towrite = 4 - (offset & 0x3);
+
+ err = pciclient_cfg_write(d->device_port, offset, (char*)data,
+ &towrite);
+ if (err)
+ return err;
+
+ offset += towrite;
+ data = (const char*)data + towrite;
+ size -= towrite;
+ *bytes_written += towrite;
+ }
+ return 0;
+}
+
+/*
+ * Copy the device's firmware in `buffer'
+ */
+static int
+pci_device_hurd_read_rom(struct pci_device * dev, void * buffer)
+{
+ ssize_t rd;
+ int romfd;
+ char server[NAME_MAX];
+
+ snprintf(server, NAME_MAX, "%s/%04x/%02x/%02x/%01u/%s", _SERVERS_BUS_PCI,
+ dev->domain, dev->bus, dev->dev, dev->func, FILE_ROM_NAME);
+
+ romfd = open(server, O_RDONLY | O_CLOEXEC);
+ if (romfd == -1)
+ return errno;
+
+ rd = read(romfd, buffer, dev->rom_size);
+ if (rd != dev->rom_size) {
+ close(romfd);
+ return errno;
+ }
+
+ close(romfd);
+
+ return 0;
+}
+
+/*
+ * Each device has its own server where send RPC's to.
+ *
+ * Deallocate the port before destroying the device.
+ */
+static void
+pci_device_hurd_destroy(struct pci_device *dev)
+{
+ struct pci_device_private *d = (struct pci_device_private*) dev;
+
+ mach_port_deallocate (mach_task_self (), d->device_port);
+}
+
+/* Walk through the FS tree to see what is allowed for us */
+static int
+enum_devices(const char *parent, struct pci_device_private **device,
+ int domain, int bus, int dev, int func, tree_level lev)
+{
+ int err, ret;
+ DIR *dir;
+ struct dirent *entry;
+ char path[NAME_MAX];
+ char server[NAME_MAX];
+ uint32_t reg;
+ size_t toread;
+ mach_port_t device_port;
+
+ dir = opendir(parent);
+ if (!dir)
+ return errno;
+
+ while ((entry = readdir(dir)) != 0) {
+ snprintf(path, NAME_MAX, "%s/%s", parent, entry->d_name);
+ if (entry->d_type == DT_DIR) {
+ if (!strncmp(entry->d_name, ".", NAME_MAX)
+ || !strncmp(entry->d_name, "..", NAME_MAX))
+ continue;
+
+ errno = 0;
+ ret = strtol(entry->d_name, 0, 16);
+ if (errno)
+ return errno;
+
+ /*
+ * We found a valid directory.
+ * Update the address and switch to the next level.
+ */
+ switch (lev) {
+ case LEVEL_DOMAIN:
+ domain = ret;
+ break;
+ case LEVEL_BUS:
+ bus = ret;
+ break;
+ case LEVEL_DEV:
+ dev = ret;
+ break;
+ case LEVEL_FUNC:
+ func = ret;
+ break;
+ default:
+ return -1;
+ }
+
+ err = enum_devices(path, device, domain, bus, dev, func, lev+1);
+ if (err == EPERM)
+ continue;
+ }
+ else {
+ if (strncmp(entry->d_name, FILE_CONFIG_NAME, NAME_MAX))
+ /* We are looking for the config file */
+ continue;
+
+ /* We found an available virtual device, add it to our list */
+ snprintf(server, NAME_MAX, "%s/%04x/%02x/%02x/%01u/%s",
+ _SERVERS_BUS_PCI, domain, bus, dev, func,
+ entry->d_name);
+ device_port = file_name_lookup(server, 0, 0);
+ if (device_port == MACH_PORT_NULL)
+ return errno;
+
+ toread = sizeof(reg);
+ err = pciclient_cfg_read(device_port, PCI_VENDOR_ID, (char*)&reg,
+ &toread);
+ if (err)
+ return err;
+ if (toread != sizeof(reg))
+ return -1;
+
+ (*device)->base.domain = domain;
+ (*device)->base.bus = bus;
+ (*device)->base.dev = dev;
+ (*device)->base.func = func;
+ (*device)->base.vendor_id = PCI_VENDOR(reg);
+ (*device)->base.device_id = PCI_DEVICE(reg);
+
+ toread = sizeof(reg);
+ err = pciclient_cfg_read(device_port, PCI_CLASS, (char*)&reg,
+ &toread);
+ if (err)
+ return err;
+ if (toread != sizeof(reg))
+ return -1;
+
+ (*device)->base.device_class = reg >> 8;
+ (*device)->base.revision = reg & 0xFF;
+
+ toread = sizeof(reg);
+ err = pciclient_cfg_read(device_port, PCI_SUB_VENDOR_ID,
+ (char*)&reg, &toread);
+ if (err)
+ return err;
+ if (toread != sizeof(reg))
+ return -1;
+
+ (*device)->base.subvendor_id = PCI_VENDOR(reg);
+ (*device)->base.subdevice_id = PCI_DEVICE(reg);
+
+ (*device)->device_port = device_port;
+
+ (*device)++;
+ }
+ }
+
+ return 0;
+}
+
+static const struct pci_system_methods hurd_pci_methods = {
+ .destroy = pci_system_x86_destroy,
+ .destroy_device = pci_device_hurd_destroy,
+ .read_rom = pci_device_hurd_read_rom,
+ .probe = pci_device_hurd_probe,
+ .map_range = pci_device_x86_map_range,
+ .unmap_range = pci_device_x86_unmap_range,
+ .read = pci_device_hurd_read,
+ .write = pci_device_hurd_write,
+ .fill_capabilities = pci_fill_capabilities_generic,
+ .open_legacy_io = pci_device_x86_open_legacy_io,
+ .close_io = pci_device_x86_close_io,
+ .read32 = pci_device_x86_read32,
+ .read16 = pci_device_x86_read16,
+ .read8 = pci_device_x86_read8,
+ .write32 = pci_device_x86_write32,
+ .write16 = pci_device_x86_write16,
+ .write8 = pci_device_x86_write8,
+ .map_legacy = pci_device_x86_map_legacy,
+ .unmap_legacy = pci_device_x86_unmap_legacy,
+};
+
+_pci_hidden int
+pci_system_hurd_create(void)
+{
+ struct pci_device_private *device;
+ int err;
+ struct pci_system_hurd *pci_sys_hurd;
+ size_t ndevs;
+ mach_port_t pci_server_port;
+
+ err = x86_enable_io();
+ if (err)
+ return err;
+
+ pci_sys_hurd = calloc(1, sizeof(struct pci_system_hurd));
+ if (pci_sys_hurd == NULL) {
+ x86_disable_io();
+ return ENOMEM;
+ }
+ pci_sys = &pci_sys_hurd->system;
+
+ pci_sys->methods = &hurd_pci_methods;
+
+ pci_server_port = file_name_lookup(_SERVERS_BUS_PCI, 0, 0);
+ if (pci_server_port == MACH_PORT_NULL)
+ return errno;
+
+ /* The server gives us the number of available devices for us */
+ err = pci_get_ndevs (pci_server_port, &ndevs);
+ if (err) {
+ mach_port_deallocate (mach_task_self (), pci_server_port);
+ return err;
+ }
+ mach_port_deallocate (mach_task_self (), pci_server_port);
+
+ pci_sys->num_devices = ndevs;
+ pci_sys->devices = calloc(ndevs, sizeof(struct pci_device_private));
+ if (pci_sys->devices == NULL) {
+ x86_disable_io();
+ free(pci_sys_hurd);
+ pci_sys = NULL;
+ return ENOMEM;
+ }
+
+ device = pci_sys->devices;
+ err = enum_devices(_SERVERS_BUS_PCI, &device, -1, -1, -1, -1,
+ LEVEL_DOMAIN);
+ if (err)
+ return err;
+
+ return 0;
+}
diff --git a/src/pciaccess_private.h b/src/pciaccess_private.h
index 2f05b29..10c9698 100644
--- a/src/pciaccess_private.h
+++ b/src/pciaccess_private.h
@@ -29,6 +29,9 @@
* \author Ian Romanick <***@us.ibm.com>
*/

+#ifndef PCIACCESS_PRIVATE_H
+#define PCIACCESS_PRIVATE_H
+
#if defined(__GNUC__) && (__GNUC__ >= 4)
# define _pci_hidden __attribute__((visibility("hidden")))
#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550)
@@ -150,6 +153,9 @@ struct pci_device_private {
#ifdef __sun
int is_primary;
#endif
+#ifdef __GNU__
+ unsigned long device_port;
+#endif
};


@@ -189,5 +195,8 @@ extern int pci_system_netbsd_create( void );
extern int pci_system_openbsd_create( void );
extern void pci_system_openbsd_init_dev_mem( int );
extern int pci_system_solx_devfs_create( void );
+extern int pci_system_hurd_create( void );
extern int pci_system_x86_create( void );
extern void pci_io_cleanup( void );
+
+#endif /* PCIACCESS_PRIVATE_H */
diff --git a/src/x86_pci.c b/src/x86_pci.c
index 49c1cab..9adb5e7 100644
--- a/src/x86_pci.c
+++ b/src/x86_pci.c
@@ -20,6 +20,8 @@
*/

#define _GNU_SOURCE
+#include "x86_pci.h"
+
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
@@ -36,7 +38,7 @@

#include <sys/io.h>

-static int
+int
x86_enable_io(void)
{
if (!ioperm(0, 0xffff, 1))
@@ -44,7 +46,7 @@ x86_enable_io(void)
return errno;
}

-static int
+int
x86_disable_io(void)
{
if (!ioperm(0, 0xffff, 0))
@@ -205,28 +207,6 @@ outl(uint32_t value, uint16_t port)

#endif

-#define PCI_VENDOR(reg) ((reg) & 0xFFFF)
-#define PCI_VENDOR_INVALID 0xFFFF
-
-#define PCI_VENDOR_ID 0x00
-#define PCI_SUB_VENDOR_ID 0x2c
-#define PCI_VENDOR_ID_COMPAQ 0x0e11
-#define PCI_VENDOR_ID_INTEL 0x8086
-
-#define PCI_DEVICE(reg) (((reg) >> 16) & 0xFFFF)
-#define PCI_DEVICE_INVALID 0xFFFF
-
-#define PCI_CLASS 0x08
-#define PCI_CLASS_DEVICE 0x0a
-#define PCI_CLASS_DISPLAY_VGA 0x0300
-#define PCI_CLASS_BRIDGE_HOST 0x0600
-
-#define PCIC_DISPLAY 0x03
-#define PCIS_DISPLAY_VGA 0x00
-
-#define PCI_HDRTYPE 0x0E
-#define PCI_IRQ 0x3C
-
struct pci_system_x86 {
struct pci_system system;
int (*read)(unsigned bus, unsigned dev, unsigned func, pciaddr_t reg, void *data, unsigned size);
@@ -633,7 +613,7 @@ pci_device_x86_unmap_range(struct pci_device *dev,

#else

-static int
+int
pci_device_x86_map_range(struct pci_device *dev,
struct pci_device_mapping *map)
{
@@ -654,7 +634,7 @@ pci_device_x86_map_range(struct pci_device *dev,
return 0;
}

-static int
+int
pci_device_x86_unmap_range(struct pci_device *dev,
struct pci_device_mapping *map)
{
@@ -715,13 +695,13 @@ pci_device_x86_write(struct pci_device *dev, const void *data,
return 0;
}

-static void
+void
pci_system_x86_destroy(void)
{
x86_disable_io();
}

-static struct pci_io_handle *
+struct pci_io_handle *
pci_device_x86_open_legacy_io(struct pci_io_handle *ret,
struct pci_device *dev, pciaddr_t base, pciaddr_t size)
{
@@ -734,7 +714,7 @@ pci_device_x86_open_legacy_io(struct pci_io_handle *ret,
return ret;
}

-static void
+void
pci_device_x86_close_io(struct pci_device *dev, struct pci_io_handle *handle)
{
/* Like in the Linux case, do not disable I/O, as it may be opened several
@@ -742,46 +722,46 @@ pci_device_x86_close_io(struct pci_device *dev, struct pci_io_handle *handle)
/* x86_disable_io(); */
}

-static uint32_t
+uint32_t
pci_device_x86_read32(struct pci_io_handle *handle, uint32_t reg)
{
return inl(reg + handle->base);
}

-static uint16_t
+uint16_t
pci_device_x86_read16(struct pci_io_handle *handle, uint32_t reg)
{
return inw(reg + handle->base);
}

-static uint8_t
+uint8_t
pci_device_x86_read8(struct pci_io_handle *handle, uint32_t reg)
{
return inb(reg + handle->base);
}

-static void
+void
pci_device_x86_write32(struct pci_io_handle *handle, uint32_t reg,
uint32_t data)
{
outl(data, reg + handle->base);
}

-static void
+void
pci_device_x86_write16(struct pci_io_handle *handle, uint32_t reg,
uint16_t data)
{
outw(data, reg + handle->base);
}

-static void
+void
pci_device_x86_write8(struct pci_io_handle *handle, uint32_t reg,
uint8_t data)
{
outb(data, reg + handle->base);
}

-static int
+int
pci_device_x86_map_legacy(struct pci_device *dev, pciaddr_t base,
pciaddr_t size, unsigned map_flags, void **addr)
{
@@ -797,7 +777,7 @@ pci_device_x86_map_legacy(struct pci_device *dev, pciaddr_t base,
return err;
}

-static int
+int
pci_device_x86_unmap_legacy(struct pci_device *dev, void *addr,
pciaddr_t size)
{
diff --git a/src/x86_pci.h b/src/x86_pci.h
new file mode 100644
index 0000000..2e00ba6
--- /dev/null
+++ b/src/x86_pci.h
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+
+/* Macros and declarations used by both x86 and Hurd modules. */
+
+#ifndef X86_PCI_H
+#define X86_PCI_H
+
+#include "pciaccess.h"
+#include "pciaccess_private.h"
+
+#define PCI_VENDOR(reg) ((reg) & 0xFFFF)
+#define PCI_VENDOR_INVALID 0xFFFF
+
+#define PCI_VENDOR_ID 0x00
+#define PCI_SUB_VENDOR_ID 0x2c
+#define PCI_VENDOR_ID_COMPAQ 0x0e11
+#define PCI_VENDOR_ID_INTEL 0x8086
+
+#define PCI_DEVICE(reg) (((reg) >> 16) & 0xFFFF)
+#define PCI_DEVICE_INVALID 0xFFFF
+
+#define PCI_CLASS 0x08
+#define PCI_CLASS_DEVICE 0x0a
+#define PCI_CLASS_DISPLAY_VGA 0x0300
+#define PCI_CLASS_BRIDGE_HOST 0x0600
+
+#define PCIC_DISPLAY 0x03
+#define PCIS_DISPLAY_VGA 0x00
+
+#define PCI_HDRTYPE 0x0E
+#define PCI_IRQ 0x3C
+
+int x86_enable_io(void);
+int x86_disable_io(void);
+void pci_system_x86_destroy(void);
+int pci_device_x86_map_range(struct pci_device *dev,
+ struct pci_device_mapping *map);
+int pci_device_x86_unmap_range(struct pci_device *dev,
+ struct pci_device_mapping *map);
+struct pci_io_handle *pci_device_x86_open_legacy_io(struct pci_io_handle *ret,
+ struct pci_device *dev, pciaddr_t base, pciaddr_t size);
+void pci_device_x86_close_io(struct pci_device *dev,
+ struct pci_io_handle *handle);
+uint32_t pci_device_x86_read32(struct pci_io_handle *handle, uint32_t reg);
+uint16_t pci_device_x86_read16(struct pci_io_handle *handle, uint32_t reg);
+uint8_t pci_device_x86_read8(struct pci_io_handle *handle, uint32_t reg);
+void pci_device_x86_write32(struct pci_io_handle *handle, uint32_t reg,
+ uint32_t data);
+void pci_device_x86_write16(struct pci_io_handle *handle, uint32_t reg,
+ uint16_t data);
+void pci_device_x86_write8(struct pci_io_handle *handle, uint32_t reg,
+ uint8_t data);
+int pci_device_x86_map_legacy(struct pci_device *dev, pciaddr_t base,
+ pciaddr_t size, unsigned map_flags, void **addr);
+int pci_device_x86_unmap_legacy(struct pci_device *dev, void *addr,
+ pciaddr_t size);
+
+#endif /* X86_PCI_H */
--
2.14.0
Samuel Thibault
2018-10-30 00:06:25 UTC
Permalink
Hello,
Post by Joan Lledó
This new module uses Hurd's RPCs for accessing the PCI configuration space.
Direct access as in {read_write}_{8,16,32} functions is done by the old x86
module.
Similarly, we don't want to avoid x86 completely.
Post by Joan Lledó
+_pci_hidden int
+pci_system_hurd_create(void)
+{
This could start by opening _SERVERS_BUS_PCI and if that fails, call
pci_system_x86_create instead and thus immediately get x86 operations.

Samuel

Loading...