Hi,

This patch adds GNU/Hurd support to pci-userspace. Some notes:

* It uses libpciaccess to query/modify the PCI config stuff. This part of the 
code is pretty
  generic, perhaps this approach can be useful to other ports?

* It relies on the patch I sent yesterday (allow setting LDFLAGS from 
pci-userspace Makefile)
  to setup libpciaccess dependency

* It uses experimental GNU Mach interfaces that were added for DDE:
  - vm_allocate_contiguous() to allocate contigously physical memory
  - device_intr_register() / device_intr_enable() to synchronize with interrupts

* It includes code from other people under GPLv2; I'm not sure if this may be 
an issue wrt licensing
  policy of Rump as this is only targetted at the pci-userspace module. In any 
case if you
  think it's an issue let me know and we'll try to find a solution:
  - intrthread() is heavily based on intloop() from hurd/libddekit/interrupt.c
  - experimentalUser.c is an automatically generated file copied from the Hurd 
build tree. I
    would welcome some help from MIG experts on how to do this in a more 
elegant way.
  - intr.h was just copied from GNU Mach source tree (I don't think it is 
copyright-significant
    though).

-- 
Robert Millan
--- /dev/null
+++ b/pci-userspace/src-gnu/Makefile
@@ -0,0 +1,21 @@
+RUMPTOP= ${TOPRUMP}
+
+RUMPCOMP_USER_SRCS.rumpdev_pci=		pci_user-gnu.c experimentalUser.c
+RUMPCOMP_USER_PATH.rumpdev_pci:=	${.PARSEDIR}
+RUMPCOMP_USER_CPPFLAGS.rumpdev_pci:=	-I${.PARSEDIR}
+RUMPCOMP_CPPFLAGS.rumpdev_pci:=		-I${.PARSEDIR}
+RUMPCOMP_LDFLAGS.rumpdev_pci:=		-lpciaccess
+
+.export RUMPCOMP_USER_SRCS.rumpdev_pci
+.export RUMPCOMP_USER_PATH.rumpdev_pci
+.export RUMPCOMP_USER_CPPFLAGS.rumpdev_pci
+.export RUMPCOMP_CPPFLAGS.rumpdev_pci
+.export RUMPCOMP_LDFLAGS.rumpdev_pci
+
+.include "${RUMPTOP}/dev/Makefile.rumpdevcomp"
+
+.for pcidev in ${RUMPPCIDEVS}
+SUBDIR+= ${RUMPTOP}/dev/lib/lib${pcidev}
+.endfor
+
+.include <bsd.subdir.mk>
--- /dev/null
+++ b/pci-userspace/src-gnu/experimentalUser.c
@@ -0,0 +1,461 @@
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE 1
+#endif
+
+#include "experimental_U.h"
+#define EXPORT_BOOLEAN
+#include <mach/boolean.h>
+#include <mach/kern_return.h>
+#include <mach/message.h>
+#include <mach/notify.h>
+#include <mach/mach_types.h>
+#include <mach/mig_errors.h>
+#include <mach/mig_support.h>
+#include <mach/msg_type.h>
+
+#ifndef	mig_internal
+#define	mig_internal	static
+#endif
+
+#ifndef	mig_external
+#define mig_external
+#endif
+
+#ifndef	mig_unlikely
+#define	mig_unlikely(X)	__builtin_expect (!! (X), 0)
+#endif
+
+#ifndef	TypeCheck
+#define	TypeCheck 1
+#endif
+
+#ifndef	UseExternRCSId
+#define	UseExternRCSId		1
+#endif
+
+#define BAD_TYPECHECK(type, check) mig_unlikely (({\
+  union { mach_msg_type_t t; unsigned32_t w; } _t, _c;\
+  _t.t = *(type); _c.t = *(check);_t.w != _c.w; }))
+#define msgh_request_port	msgh_remote_port
+#define msgh_reply_port		msgh_local_port
+
+#include <mach/std_types.h>
+#include <mach/mach_types.h>
+
+/* Routine device_intr_register */
+mig_external kern_return_t device_intr_register
+(
+	mach_port_t master_port,
+	int line,
+	int id,
+	int flags,
+	mach_port_t receive_port,
+	mach_msg_type_name_t receive_portPoly
+)
+{
+	typedef struct {
+		mach_msg_header_t Head;
+		mach_msg_type_t lineType;
+		int line;
+		mach_msg_type_t idType;
+		int id;
+		mach_msg_type_t flagsType;
+		int flags;
+		mach_msg_type_t receive_portType;
+		mach_port_t receive_port;
+	} Request;
+
+	typedef struct {
+		mach_msg_header_t Head;
+		mach_msg_type_t RetCodeType;
+		kern_return_t RetCode;
+	} Reply;
+
+	union {
+		Request In;
+		Reply Out;
+	} Mess;
+
+	Request *InP = &Mess.In;
+	Reply *OutP = &Mess.Out;
+
+	mach_msg_return_t msg_result;
+	boolean_t msgh_simple = TRUE;
+
+	const mach_msg_type_t lineType = {
+		/* msgt_name = */		2,
+		/* msgt_size = */		32,
+		/* msgt_number = */		1,
+		/* msgt_inline = */		TRUE,
+		/* msgt_longform = */		FALSE,
+		/* msgt_deallocate = */		FALSE,
+		/* msgt_unused = */		0
+	};
+
+	const mach_msg_type_t idType = {
+		/* msgt_name = */		2,
+		/* msgt_size = */		32,
+		/* msgt_number = */		1,
+		/* msgt_inline = */		TRUE,
+		/* msgt_longform = */		FALSE,
+		/* msgt_deallocate = */		FALSE,
+		/* msgt_unused = */		0
+	};
+
+	const mach_msg_type_t flagsType = {
+		/* msgt_name = */		2,
+		/* msgt_size = */		32,
+		/* msgt_number = */		1,
+		/* msgt_inline = */		TRUE,
+		/* msgt_longform = */		FALSE,
+		/* msgt_deallocate = */		FALSE,
+		/* msgt_unused = */		0
+	};
+
+	const mach_msg_type_t receive_portType = {
+		/* msgt_name = */		-1,
+		/* msgt_size = */		32,
+		/* msgt_number = */		1,
+		/* msgt_inline = */		TRUE,
+		/* msgt_longform = */		FALSE,
+		/* msgt_deallocate = */		FALSE,
+		/* msgt_unused = */		0
+	};
+
+	const mach_msg_type_t RetCodeCheck = {
+		/* msgt_name = */		MACH_MSG_TYPE_INTEGER_32,
+		/* msgt_size = */		32,
+		/* msgt_number = */		1,
+		/* msgt_inline = */		TRUE,
+		/* msgt_longform = */		FALSE,
+		/* msgt_deallocate = */		FALSE,
+		/* msgt_unused = */		0
+	};
+
+	InP->lineType = lineType;
+
+	InP->line = line;
+
+	InP->idType = idType;
+
+	InP->id = id;
+
+	InP->flagsType = flagsType;
+
+	InP->flags = flags;
+
+	InP->receive_portType = receive_portType;
+
+	InP->receive_port = receive_port;
+
+	if (MACH_MSG_TYPE_PORT_ANY(receive_portPoly))
+		msgh_simple = FALSE;
+
+	InP->receive_portType.msgt_name = receive_portPoly;
+
+	InP->Head.msgh_bits = msgh_simple ?
+		MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE) :
+		(MACH_MSGH_BITS_COMPLEX|
+		 MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE));
+	/* msgh_size passed as argument */
+	InP->Head.msgh_request_port = master_port;
+	InP->Head.msgh_reply_port = __mig_get_reply_port();
+	InP->Head.msgh_seqno = 0;
+	InP->Head.msgh_id = 424243;
+
+	msg_result = __mach_msg(&InP->Head, MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE, 56, sizeof(Reply), InP->Head.msgh_reply_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+	if (msg_result != MACH_MSG_SUCCESS) {
+		__mig_dealloc_reply_port(InP->Head.msgh_reply_port);
+		return msg_result;
+	}
+	__mig_put_reply_port(InP->Head.msgh_reply_port);
+
+	if (mig_unlikely (OutP->Head.msgh_id != 424343)) {
+		if (OutP->Head.msgh_id == MACH_NOTIFY_SEND_ONCE)
+			return MIG_SERVER_DIED;
+		else {
+			__mig_dealloc_reply_port(InP->Head.msgh_reply_port);
+			return MIG_REPLY_MISMATCH;
+		}
+	}
+
+#if	TypeCheck
+	if (mig_unlikely ((OutP->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) ||
+	    (OutP->Head.msgh_size != 32)))
+		return MIG_TYPE_ERROR;
+#endif	/* TypeCheck */
+
+#if	TypeCheck
+	if (BAD_TYPECHECK (&OutP->RetCodeType, &RetCodeCheck))
+		return MIG_TYPE_ERROR;
+#endif	/* TypeCheck */
+
+	return OutP->RetCode;
+}
+
+/* Routine device_intr_enable */
+mig_external kern_return_t device_intr_enable
+(
+	mach_port_t master_port,
+	int line,
+	char status
+)
+{
+	typedef struct {
+		mach_msg_header_t Head;
+		mach_msg_type_t lineType;
+		int line;
+		mach_msg_type_t statusType;
+		char status;
+		char statusPad[3];
+	} Request;
+
+	typedef struct {
+		mach_msg_header_t Head;
+		mach_msg_type_t RetCodeType;
+		kern_return_t RetCode;
+	} Reply;
+
+	union {
+		Request In;
+		Reply Out;
+	} Mess;
+
+	Request *InP = &Mess.In;
+	Reply *OutP = &Mess.Out;
+
+	mach_msg_return_t msg_result;
+
+	const mach_msg_type_t lineType = {
+		/* msgt_name = */		2,
+		/* msgt_size = */		32,
+		/* msgt_number = */		1,
+		/* msgt_inline = */		TRUE,
+		/* msgt_longform = */		FALSE,
+		/* msgt_deallocate = */		FALSE,
+		/* msgt_unused = */		0
+	};
+
+	const mach_msg_type_t statusType = {
+		/* msgt_name = */		8,
+		/* msgt_size = */		8,
+		/* msgt_number = */		1,
+		/* msgt_inline = */		TRUE,
+		/* msgt_longform = */		FALSE,
+		/* msgt_deallocate = */		FALSE,
+		/* msgt_unused = */		0
+	};
+
+	const mach_msg_type_t RetCodeCheck = {
+		/* msgt_name = */		MACH_MSG_TYPE_INTEGER_32,
+		/* msgt_size = */		32,
+		/* msgt_number = */		1,
+		/* msgt_inline = */		TRUE,
+		/* msgt_longform = */		FALSE,
+		/* msgt_deallocate = */		FALSE,
+		/* msgt_unused = */		0
+	};
+
+	InP->lineType = lineType;
+
+	InP->line = line;
+
+	InP->statusType = statusType;
+
+	InP->status = status;
+
+	InP->Head.msgh_bits =
+		MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
+	/* msgh_size passed as argument */
+	InP->Head.msgh_request_port = master_port;
+	InP->Head.msgh_reply_port = __mig_get_reply_port();
+	InP->Head.msgh_seqno = 0;
+	InP->Head.msgh_id = 424244;
+
+	msg_result = __mach_msg(&InP->Head, MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE, 40, sizeof(Reply), InP->Head.msgh_reply_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+	if (msg_result != MACH_MSG_SUCCESS) {
+		__mig_dealloc_reply_port(InP->Head.msgh_reply_port);
+		return msg_result;
+	}
+	__mig_put_reply_port(InP->Head.msgh_reply_port);
+
+	if (mig_unlikely (OutP->Head.msgh_id != 424344)) {
+		if (OutP->Head.msgh_id == MACH_NOTIFY_SEND_ONCE)
+			return MIG_SERVER_DIED;
+		else {
+			__mig_dealloc_reply_port(InP->Head.msgh_reply_port);
+			return MIG_REPLY_MISMATCH;
+		}
+	}
+
+#if	TypeCheck
+	if (mig_unlikely ((OutP->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) ||
+	    (OutP->Head.msgh_size != 32)))
+		return MIG_TYPE_ERROR;
+#endif	/* TypeCheck */
+
+#if	TypeCheck
+	if (BAD_TYPECHECK (&OutP->RetCodeType, &RetCodeCheck))
+		return MIG_TYPE_ERROR;
+#endif	/* TypeCheck */
+
+	return OutP->RetCode;
+}
+
+/* Routine vm_allocate_contiguous */
+mig_external kern_return_t vm_allocate_contiguous
+(
+	mach_port_t host_priv,
+	mach_port_t target_task,
+	vm_address_t *vaddr,
+	vm_address_t *paddr,
+	vm_size_t size
+)
+{
+	typedef struct {
+		mach_msg_header_t Head;
+		mach_msg_type_t target_taskType;
+		mach_port_t target_task;
+		mach_msg_type_t sizeType;
+		vm_size_t size;
+	} Request;
+
+	typedef struct {
+		mach_msg_header_t Head;
+		mach_msg_type_t RetCodeType;
+		kern_return_t RetCode;
+		mach_msg_type_t vaddrType;
+		vm_address_t vaddr;
+		mach_msg_type_t paddrType;
+		vm_address_t paddr;
+	} Reply;
+
+	union {
+		Request In;
+		Reply Out;
+	} Mess;
+
+	Request *InP = &Mess.In;
+	Reply *OutP = &Mess.Out;
+
+	mach_msg_return_t msg_result;
+#if	TypeCheck
+	unsigned int msgh_size;
+#endif	/* TypeCheck */
+
+	const mach_msg_type_t target_taskType = {
+		/* msgt_name = */		19,
+		/* msgt_size = */		32,
+		/* msgt_number = */		1,
+		/* msgt_inline = */		TRUE,
+		/* msgt_longform = */		FALSE,
+		/* msgt_deallocate = */		FALSE,
+		/* msgt_unused = */		0
+	};
+
+	const mach_msg_type_t sizeType = {
+		/* msgt_name = */		2,
+		/* msgt_size = */		32,
+		/* msgt_number = */		1,
+		/* msgt_inline = */		TRUE,
+		/* msgt_longform = */		FALSE,
+		/* msgt_deallocate = */		FALSE,
+		/* msgt_unused = */		0
+	};
+
+	const mach_msg_type_t RetCodeCheck = {
+		/* msgt_name = */		MACH_MSG_TYPE_INTEGER_32,
+		/* msgt_size = */		32,
+		/* msgt_number = */		1,
+		/* msgt_inline = */		TRUE,
+		/* msgt_longform = */		FALSE,
+		/* msgt_deallocate = */		FALSE,
+		/* msgt_unused = */		0
+	};
+
+	const mach_msg_type_t vaddrCheck = {
+		/* msgt_name = */		2,
+		/* msgt_size = */		32,
+		/* msgt_number = */		1,
+		/* msgt_inline = */		TRUE,
+		/* msgt_longform = */		FALSE,
+		/* msgt_deallocate = */		FALSE,
+		/* msgt_unused = */		0
+	};
+
+	const mach_msg_type_t paddrCheck = {
+		/* msgt_name = */		2,
+		/* msgt_size = */		32,
+		/* msgt_number = */		1,
+		/* msgt_inline = */		TRUE,
+		/* msgt_longform = */		FALSE,
+		/* msgt_deallocate = */		FALSE,
+		/* msgt_unused = */		0
+	};
+
+	InP->target_taskType = target_taskType;
+
+	InP->target_task = target_task;
+
+	InP->sizeType = sizeType;
+
+	InP->size = size;
+
+	InP->Head.msgh_bits = MACH_MSGH_BITS_COMPLEX|
+		MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
+	/* msgh_size passed as argument */
+	InP->Head.msgh_request_port = host_priv;
+	InP->Head.msgh_reply_port = __mig_get_reply_port();
+	InP->Head.msgh_seqno = 0;
+	InP->Head.msgh_id = 424245;
+
+	msg_result = __mach_msg(&InP->Head, MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE, 40, sizeof(Reply), InP->Head.msgh_reply_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+	if (msg_result != MACH_MSG_SUCCESS) {
+		__mig_dealloc_reply_port(InP->Head.msgh_reply_port);
+		return msg_result;
+	}
+	__mig_put_reply_port(InP->Head.msgh_reply_port);
+
+	if (mig_unlikely (OutP->Head.msgh_id != 424345)) {
+		if (OutP->Head.msgh_id == MACH_NOTIFY_SEND_ONCE)
+			return MIG_SERVER_DIED;
+		else {
+			__mig_dealloc_reply_port(InP->Head.msgh_reply_port);
+			return MIG_REPLY_MISMATCH;
+		}
+	}
+
+#if	TypeCheck
+	msgh_size = OutP->Head.msgh_size;
+
+	if (mig_unlikely ((OutP->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) ||
+	    ((msgh_size != 48) &&
+	     ((msgh_size != sizeof(mig_reply_header_t)) ||
+	      (OutP->RetCode == KERN_SUCCESS)))))
+		return MIG_TYPE_ERROR;
+#endif	/* TypeCheck */
+
+#if	TypeCheck
+	if (BAD_TYPECHECK (&OutP->RetCodeType, &RetCodeCheck))
+		return MIG_TYPE_ERROR;
+#endif	/* TypeCheck */
+
+	if (OutP->RetCode != KERN_SUCCESS)
+		return OutP->RetCode;
+
+#if	TypeCheck
+	if (BAD_TYPECHECK (&OutP->vaddrType, &vaddrCheck))
+		return MIG_TYPE_ERROR;
+#endif	/* TypeCheck */
+
+	*vaddr = OutP->vaddr;
+
+#if	TypeCheck
+	if (BAD_TYPECHECK (&OutP->paddrType, &paddrCheck))
+		return MIG_TYPE_ERROR;
+#endif	/* TypeCheck */
+
+	*paddr = OutP->paddr;
+
+	return KERN_SUCCESS;
+}
--- /dev/null
+++ b/pci-userspace/src-gnu/experimental_U.h
@@ -0,0 +1,57 @@
+#ifndef	_experimental_user_
+#define	_experimental_user_
+
+/* Module experimental */
+
+#include <mach/kern_return.h>
+#include <mach/port.h>
+#include <mach/message.h>
+
+#include <mach/std_types.h>
+#include <mach/mach_types.h>
+
+/* Routine device_intr_register */
+#ifdef	mig_external
+mig_external
+#else
+extern
+#endif
+kern_return_t device_intr_register
+(
+	mach_port_t master_port,
+	int line,
+	int id,
+	int flags,
+	mach_port_t receive_port,
+	mach_msg_type_name_t receive_portPoly
+);
+
+/* Routine device_intr_enable */
+#ifdef	mig_external
+mig_external
+#else
+extern
+#endif
+kern_return_t device_intr_enable
+(
+	mach_port_t master_port,
+	int line,
+	char status
+);
+
+/* Routine vm_allocate_contiguous */
+#ifdef	mig_external
+mig_external
+#else
+extern
+#endif
+kern_return_t vm_allocate_contiguous
+(
+	mach_port_t host_priv,
+	mach_port_t target_task,
+	vm_address_t *vaddr,
+	vm_address_t *paddr,
+	vm_size_t size
+);
+
+#endif	/* not defined(_experimental_user_) */
--- /dev/null
+++ b/pci-userspace/src-gnu/intr.h
@@ -0,0 +1,17 @@
+#ifndef __INTR_H__
+
+#define __INTR_H__
+
+#include <device/device_types.h>
+
+typedef struct
+{
+  mach_msg_header_t intr_header;
+  mach_msg_type_t   intr_type;
+  int		    line;
+} mach_intr_notification_t;
+
+#define INTR_NOTIFY_MSGH_SEQNO 0
+#define MACH_INTR_NOTIFY 424242
+
+#endif
--- /dev/null
+++ b/pci-userspace/src-gnu/pci_user-gnu.c
@@ -0,0 +1,384 @@
+/*-
+ * Copyright (c) 2014 Antti Kantee.  All Rights Reserved.
+ * Copyright (c) 2015 Robert Millan
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define _GNU_SOURCE 1
+
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/queue.h>
+#include <sys/io.h>
+
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <rump/rumpuser_component.h>
+
+#include <hurd.h>
+#include <device/device.h>
+
+#include <pciaccess.h>
+
+#include "pci_user.h"
+#include "experimental_U.h"	/* stolen from hurd build dir */
+#include "intr.h"		/* stolen from gnumach include dir */
+
+#define RUMP_IRQ_PRIO		2
+
+/* highest dev for which we've returned something sensible in config space */
+static pthread_mutex_t genericmtx = PTHREAD_MUTEX_INITIALIZER;
+static int highestdev = -1;
+
+static mach_port_t master_host;
+static mach_port_t master_device;
+
+int
+rumpcomp_pci_iospace_init(void)
+{
+	if (ioperm(0, 0x10000, 1))
+		return rumpuser_component_errtrans(errno);
+
+	return 0;
+}
+
+#define NUMDEVS	32
+static struct pci_device *pci_devices[NUMDEVS];
+
+static void
+pci_userspace_init(void)
+{
+	/* FIXME: add a hook to make rump call this, once and only once */
+	static int is_init = 0;
+	if (is_init)
+		return;
+	is_init = 1;
+
+	if (get_privileged_ports (&master_host, &master_device))
+		errx(1, "get_privileged_ports");
+
+	pci_system_init ();
+	struct pci_device_iterator *dev_iter;
+	struct pci_device *pci_dev;
+        dev_iter = pci_slot_match_iterator_create (NULL);
+	int i = 0;
+        while ((pci_dev = pci_device_next (dev_iter)) != NULL) {
+		pci_devices[i++] = pci_dev;
+	}
+}
+
+void *
+rumpcomp_pci_map(unsigned long addr, unsigned long len)
+{
+	errno = rumpuser_component_errtrans(ENOSYS);
+	return NULL;
+}
+
+int
+rumpcomp_pci_confread(unsigned bus, unsigned dev, unsigned fun,
+	int reg, unsigned int *rv)
+{
+	*rv = 0xffffffff;
+	if (fun != 0 || bus != 0)
+		return 1;
+
+	if (dev >= NUMDEVS)
+		return 1;
+
+	pci_userspace_init();
+
+	pci_device_cfg_read_u32(pci_devices[dev], rv, reg);
+
+	pthread_mutex_lock(&genericmtx);
+	if ((int)dev > highestdev)
+		highestdev = dev;
+	pthread_mutex_unlock(&genericmtx);
+
+	return 0;
+}
+
+int
+rumpcomp_pci_confwrite(unsigned bus, unsigned dev, unsigned fun,
+	int reg, unsigned int v)
+{
+	assert(bus == 0 && fun == 0);
+
+	if (dev >= NUMDEVS)
+		return 1;
+
+	pci_userspace_init();
+
+	pci_device_cfg_write_u32(pci_devices[dev], v, reg);
+
+	return 0;
+}
+
+/* this is a multifunction data structure! */
+struct irq {
+	unsigned magic_cookie;
+	unsigned device;
+
+	int (*handler)(void *);
+	void *data;
+	int intrline;
+
+	LIST_ENTRY(irq) entries;
+};
+static LIST_HEAD(, irq) irqs = LIST_HEAD_INITIALIZER(&irqs);
+
+static void *
+intrthread(void *arg)
+{
+/*
+ * This function is based on intloop() from hurd/libddekit/interrupt.c,
+ * whose authors are:
+ * 	\author  Thomas Friebel <t...@os.inf.tu-dresden.de>
+ * 	\author  Christian Helmuth <c...@os.inf.tu-dresden.de>
+ * 	\date    2007-01-22
+ */
+
+	struct irq *irq = arg;
+	mach_port_t delivery_port;
+        mach_port_t pset, psetcntl;
+	int ret;
+	int val;
+
+	rumpuser_component_kthread();
+
+	ret = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE,
+				&delivery_port);
+	if (ret)
+		errx(ret, "mach_port_allocate");
+
+	ret = thread_get_assignment (mach_thread_self (), &pset);
+	if (ret)
+		errx(ret, "thread_get_assignment");
+
+	ret = host_processor_set_priv (master_host, pset, &psetcntl);
+	if (ret)
+		errx(ret, "host_processor_set_priv");
+
+	thread_max_priority (mach_thread_self (), psetcntl, 0);
+	ret = thread_priority (mach_thread_self (), RUMP_IRQ_PRIO, 0);
+	if (ret)
+		errx(ret, "thread_priority");
+
+	ret = device_intr_register(master_device, irq->intrline,
+					0, 0x04000000, delivery_port,
+					MACH_MSG_TYPE_MAKE_SEND);
+	if (ret) {
+		warnx("device_intr_register");
+		return 0;
+	}
+
+	device_intr_enable (master_device, irq->intrline, TRUE);
+
+        int irq_server (mach_msg_header_t *inp, mach_msg_header_t *outp) {
+                mach_intr_notification_t *intr_header = (mach_intr_notification_t *) inp;
+
+                ((mig_reply_header_t *) outp)->RetCode = MIG_NO_REPLY;
+                if (inp->msgh_id != MACH_INTR_NOTIFY)
+                        return 0;
+
+                /* It's an interrupt not for us. It shouldn't happen. */
+                if (intr_header->line != irq->intrline) {
+                        printf ("We get interrupt %d, %d is expected",
+                                       intr_header->line, irq->intrline);
+                        return 1;
+                }
+
+		rumpcomp_pci_confread(0, irq->device, 0, 0x04, &val);
+		if (val & 0x400) {
+			printf("interrupt disabled!\n");
+			val &= ~0x400;
+			rumpcomp_pci_confwrite(0, irq->device, 0, 0x04, val);
+		}
+
+		rumpuser_component_schedule(NULL);
+		irq->handler(irq->data);
+		rumpuser_component_unschedule();
+
+                /* If the irq has been disabled by the linux device,
+                 * we don't need to reenable the real one. */
+                device_intr_enable (master_device, irq->intrline, TRUE);
+
+                return 1;
+        }
+
+        mach_msg_server (irq_server, 0, delivery_port);
+
+	return NULL;
+}
+
+int
+rumpcomp_pci_irq_map(unsigned bus, unsigned device, unsigned fun,
+	int intrline, unsigned cookie)
+{
+	struct irq *irq;
+
+	irq = malloc(sizeof(*irq));
+	if (irq == NULL)
+		return ENOENT;
+
+	irq->magic_cookie = cookie;
+	irq->device = device;
+	irq->intrline = intrline;
+
+	pthread_mutex_lock(&genericmtx);
+	LIST_INSERT_HEAD(&irqs, irq, entries);
+	pthread_mutex_unlock(&genericmtx);
+
+	return 0;
+}
+
+void *
+rumpcomp_pci_irq_establish(unsigned cookie, int (*handler)(void *), void *data)
+{
+	struct irq *irq;
+	pthread_t pt;
+
+	pthread_mutex_lock(&genericmtx);
+	LIST_FOREACH(irq, &irqs, entries) {
+		if (irq->magic_cookie == cookie)
+			break;
+	}
+	pthread_mutex_unlock(&genericmtx);
+	if (!irq)
+		return NULL;
+
+	irq->handler = handler;
+	irq->data = data;
+
+	if (pthread_create(&pt, NULL, intrthread, irq) != 0) {
+		warn("interrupt thread create");
+		free(irq);
+		return NULL;
+	}
+
+	return irq;
+}
+
+struct virt_to_mach {
+	unsigned long pa;
+	unsigned long va;
+
+        LIST_ENTRY(virt_to_mach) entries;
+};
+static LIST_HEAD(, virt_to_mach) virt_to_mach_list = LIST_HEAD_INITIALIZER(&virt_to_mach_list);
+
+/*
+ * Allocate physically contiguous memory.  We could be slightly more
+ * efficient here and implement an allocator on top of the
+ * hugepages to ensure that they get used more efficiently.  TODO4u
+ */
+int
+rumpcomp_pci_dmalloc(size_t size, size_t align,
+	unsigned long *pap, unsigned long *vap)
+{
+	struct virt_to_mach *virt_to_mach;
+	const size_t pagesize = getpagesize();
+
+	if (align > pagesize) {
+		warnx("requested alignment (%x) is larger than page size (%x)", align, pagesize);
+		return 1;
+	}
+
+	pci_userspace_init();
+
+	if (vm_allocate_contiguous (master_host, mach_task_self(), vap, pap, size)) {
+		warn("vm_allocate_contiguous");
+		return 1;
+	}
+
+	assert(*pap);
+
+	virt_to_mach = malloc(sizeof(*virt_to_mach));
+	if (virt_to_mach == NULL)
+		return errno;
+
+	virt_to_mach->pa = *pap;
+	virt_to_mach->va = *vap;
+
+        LIST_INSERT_HEAD(&virt_to_mach_list, virt_to_mach, entries);
+
+	return 0;
+}
+
+void
+rumpcomp_pci_dmafree(unsigned long vap, size_t size)
+{
+	void *v = (void *) vap;
+	struct virt_to_mach *virt_to_mach;
+
+	munmap(v, size);
+
+	LIST_FOREACH(virt_to_mach, &virt_to_mach_list, entries) {
+		if (virt_to_mach->va == (uintptr_t)vap) {
+			LIST_REMOVE(virt_to_mach, entries);
+			break;
+		}
+	}
+}
+
+/*
+ * "maps" dma memory into virtual address space.  For now, we just
+ * rely on it already being mapped.  This means that support for
+ * >1 segs is not supported.  We could call mremap() ...
+ */
+int
+rumpcomp_pci_dmamem_map(struct rumpcomp_pci_dmaseg *dss, size_t nseg,
+	size_t totlen, void **vap)
+{
+	if (nseg > 1) {
+		printf("dmamem_map for >1 seg currently not supported");
+		return ENOTSUP;
+	}
+
+	*vap = (void *)dss[0].ds_vacookie;
+	return 0;
+}
+
+/*
+ * Finds the physical address for the given virtual address.
+ */
+unsigned long
+rumpcomp_pci_virt_to_mach(void *virt)
+{
+	struct virt_to_mach *virt_to_mach;
+
+	/* FIXME: find a Mach API to do this properly, and drop the bookkeeping */
+
+	LIST_FOREACH(virt_to_mach, &virt_to_mach_list, entries) {
+		if (virt_to_mach->va == (uintptr_t)virt)
+			return virt_to_mach->pa;
+	}
+	return 0;
+}
--- /dev/null
+++ b/pci-userspace/src-gnu/rumpcomp_userfeatures_pci.h
@@ -0,0 +1,2 @@
+#define RUMPCOMP_USERFEATURE_PCI_IOSPACE
+#define RUMPCOMP_USERFEATURE_PCI_DMAFREE

Reply via email to