--- /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 --- /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 +#include +#include +#include +#include +#include +#include +#include + +#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 +#include + +/* 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 +#include +#include + +#include +#include + +/* 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 + +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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#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 + * \author Christian Helmuth + * \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