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 <[email protected]>
+ * \author Christian Helmuth <[email protected]>
+ * \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