From 168b41b26ae8b2730719a43cf71650b13d7b0c2c Mon Sep 17 00:00:00 2001 From: Justus Winter Date: Thu, 25 Feb 2016 18:46:32 +0100 Subject: [PATCH gnumach 2/2] 70_new_dde.patch --- Makefrag.am | 9 ++ device/ds_routines.c | 27 ++++ device/interrupt.h | 34 ++++ device/intr.c | 341 +++++++++++++++++++++++++++++++++++++++ include/device/intr.h | 18 +++ include/mach/experimental.defs | 98 +++++++++++ kern/experimental.srv | 3 + kern/ipc_kobject.c | 11 ++ kern/ipc_kobject.h | 5 +- kern/startup.c | 4 + linux/dev/arch/i386/kernel/irq.c | 66 +++++++- linux/dev/drivers/block/genhd.c | 4 +- vm/vm_user.c | 103 ++++++++++++ 13 files changed, 719 insertions(+), 4 deletions(-) create mode 100644 device/interrupt.h create mode 100644 device/intr.c create mode 100644 include/device/intr.h create mode 100644 include/mach/experimental.defs create mode 100644 kern/experimental.srv diff --git a/Makefrag.am b/Makefrag.am index 9a68af8..de11a71 100644 --- a/Makefrag.am +++ b/Makefrag.am @@ -220,6 +220,7 @@ EXTRA_DIST += \ kern/mach.srv \ kern/mach4.srv \ kern/gnumach.srv \ + kern/experimental.srv \ kern/mach_debug.srv \ kern/mach_host.srv \ kern/task_notify.cli @@ -304,6 +305,7 @@ libkernel_a_SOURCES += \ device/device_types_kernel.h \ device/ds_routines.c \ device/ds_routines.h \ + device/intr.c \ device/if_ether.h \ device/if_hdr.h \ device/io_req.h \ @@ -352,6 +354,7 @@ include_device_HEADERS = \ include/device/device_types.defs \ include/device/device_types.h \ include/device/disk_status.h \ + include/device/intr.h \ include/device/net_status.h \ include/device/tape_status.h \ include/device/tty_status.h @@ -374,6 +377,7 @@ include_mach_HEADERS = \ include/mach/memory_object_default.defs \ include/mach/notify.defs \ include/mach/std_types.defs \ + include/mach/experimental.defs \ include/mach/alert.h \ include/mach/boolean.h \ include/mach/boot.h \ @@ -522,6 +526,7 @@ nodist_lib_dep_tr_for_defs_a_SOURCES += \ kern/mach.server.defs.c \ kern/mach4.server.defs.c \ kern/gnumach.server.defs.c \ + kern/experimental.server.defs.c \ kern/mach_debug.server.defs.c \ kern/mach_host.server.defs.c nodist_libkernel_a_SOURCES += \ @@ -534,6 +539,9 @@ nodist_libkernel_a_SOURCES += \ kern/gnumach.server.h \ kern/gnumach.server.c \ kern/gnumach.server.msgids \ + kern/experimental.server.h \ + kern/experimental.server.c \ + kern/experimental.server.msgids \ kern/mach_debug.server.h \ kern/mach_debug.server.c \ kern/mach_debug.server.msgids \ @@ -543,6 +551,7 @@ nodist_libkernel_a_SOURCES += \ # kern/mach.server.defs # kern/mach4.server.defs # kern/gnumach.server.defs +# kern/experimental.server.defs # kern/mach_debug.server.defs # kern/mach_host.server.defs diff --git a/device/ds_routines.c b/device/ds_routines.c index dbff7f8..ec26044 100644 --- a/device/ds_routines.c +++ b/device/ds_routines.c @@ -87,6 +87,7 @@ #include #include #include +#include #include #include #include @@ -318,6 +319,26 @@ ds_device_map (device_t dev, vm_prot_t prot, vm_offset_t offset, offset, size, pager, unmap); } +io_return_t +experimental_device_intr_register (ipc_port_t master_port, int line, + int id, int flags, ipc_port_t receive_port) +{ +#ifdef MACH_XEN + return D_INVALID_OPERATION; +#else /* MACH_XEN */ + io_return_t ret; + + /* Open must be called on the master device port. */ + if (master_port != master_device_port) + return D_INVALID_OPERATION; + + if (receive_port == IP_NULL) + return D_INVALID_OPERATION; + + return insert_intr_entry (line, receive_port); +#endif /* MACH_XEN */ +} + boolean_t ds_notify (mach_msg_header_t *msg) { @@ -1798,6 +1819,12 @@ device_writev_trap (mach_device_t device, dev_mode_t mode, return (result); } +kern_return_t +experimental_device_intr_enable(ipc_port_t master_port, int line, char status) +{ + return D_INVALID_OPERATION; +} + struct device_emulation_ops mach_device_emulation_ops = { (void*) mach_device_reference, diff --git a/device/interrupt.h b/device/interrupt.h new file mode 100644 index 0000000..855f163 --- /dev/null +++ b/device/interrupt.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2016 Free Software Foundation. + * + * This program 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 of the License, or + * (at your option) any later version. + * + * This program 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, see . + */ + +#ifndef DEVICE_INTERRUPT_H +#define DEVICE_INTERRUPT_H + +struct intr_entry; +boolean_t queue_intr (struct intr_entry *e); +kern_return_t insert_intr_entry (int line, ipc_port_t notification_port); + +boolean_t intr_entry_notify (mach_msg_header_t *msg); +void intr_thread (void); + +/* linux/dev/arch/i386/kernel/irq.c */ +int install_user_intr_handler (unsigned int line, + unsigned long flags, + struct intr_entry *entry); +int remove_user_intr_handler (unsigned int irq, struct intr_entry *entry); + +#endif /* DEVICE_INTERRUPT_H */ diff --git a/device/intr.c b/device/intr.c new file mode 100644 index 0000000..85915cd --- /dev/null +++ b/device/intr.c @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2010-2016 Free Software Foundation. + * + * This program 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 of the License, or + * (at your option) any later version. + * + * This program 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, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "interrupt.h" + +#ifndef MACH_XEN + +/* The cache which holds our proxy memory objects. */ +static struct kmem_cache intr_entry_cache; + +struct intr_entry +{ + ipc_port_t port; /* We receive notifications on this port. */ + queue_chain_t chain; + ipc_port_t notification_port; + int line; + /* The number of interrupts occur since last run of intr_thread. */ + int interrupts; +}; +typedef struct intr_entry *intr_entry_t; + +static queue_head_t intr_queue; + +/* This function can only be used in the interrupt handler. */ +boolean_t +queue_intr (struct intr_entry *e) +{ + unsigned long flags; + + cpu_intr_save (&flags); + e->interrupts++; + cpu_intr_restore (flags); + + thread_wakeup ((event_t) &intr_thread); + return TRUE; +} + +/* insert an interrupt entry in the queue. + * This entry exists in the queue until + * the corresponding interrupt port is removed.*/ +kern_return_t +insert_intr_entry (int line, ipc_port_t notification_port) +{ + kern_return_t err = 0; + unsigned long flags; + struct intr_entry *e, *new; + int free = 0; + ipc_port_t dnnotify; + ipc_port_request_index_t dnindex; + + /* XXX: move to arch-specific */ + if (line < 0 || line >= 16) + return D_INVALID_OPERATION; + + new = (struct intr_entry *) kmem_cache_alloc (&intr_entry_cache); + if (new == NULL) + return D_NO_MEMORY; + + /* Allocate port, keeping a reference for it. */ + new->port = ipc_port_alloc_kernel (); + if (new->port == IP_NULL) + { + kmem_cache_free (&intr_entry_cache, (vm_offset_t) new); + return KERN_RESOURCE_SHORTAGE; + } + + /* Associate the port with the object. */ + ipc_kobject_set (new->port, (ipc_kobject_t) new, IKOT_INTR_ENTRY); + + new->line = line; + new->notification_port = notification_port; + new->interrupts = 0; + + /* Register a dead-name notification so that we are notified if the + userspace handler dies. */ + dnnotify = ipc_port_make_sonce (new->port); + ip_lock (notification_port); + /* We use a bogus port name. We don't need it to distinguish the + notifications because we register just one per object. */ + retry: + err = ipc_port_dnrequest (notification_port, (mach_port_t) 1, dnnotify, &dnindex); + if (err) + { + err = ipc_port_dngrow (notification_port); + /* notification_port is unlocked */ + if (err != KERN_SUCCESS) + { + ipc_port_release_sonce (dnnotify); + kmem_cache_free (&intr_entry_cache, (vm_offset_t) new); + return err; + } + ip_lock (notification_port); + goto retry; + } + /* notification_port is locked. dnindex is only valid until we unlock it and we + might decide to cancel. */ + + /* check whether the intr entry has been in the queue. */ + cpu_intr_save (&flags); + queue_iterate (&intr_queue, e, struct intr_entry *, chain) + if (e->notification_port == notification_port && e->line == line) + { + printf ("the interrupt entry for line %d and port %p " + "has already been inserted before.\n", + line, notification_port); + free = 1; + err = D_ALREADY_OPEN; + goto out; + } + queue_enter (&intr_queue, new, struct intr_entry *, chain); + out: + cpu_intr_restore (flags); + if (free) + { + ipc_port_dncancel (new->notification_port, (mach_port_t) 1, dnindex); + ip_unlock (new->notification_port); + ipc_port_release_sonce (dnnotify); + ipc_port_dealloc_kernel (new->port); + ipc_port_release_send (new->notification_port); + kmem_cache_free (&intr_entry_cache, (vm_offset_t) new); + } + else + ip_unlock (new->notification_port); + + if (! err) + err = install_user_intr_handler (line, flags, new); + + return err; +} + +/* Lookup a intr_entry object by its port. */ +static intr_entry_t +intr_entry_port_lookup (ipc_port_t port) +{ + struct intr_entry *entry; + + if (!IP_VALID(port)) + return 0; + + ip_lock (port); + if (ip_active (port) && (ip_kotype (port) == IKOT_INTR_ENTRY)) + entry = (struct intr_entry *) port->ip_kobject; + else + entry = 0; + ip_unlock (port); + return entry; +} + +/* Process a dead-name notification for a userspace interrupt handler + notification port. */ +boolean_t +intr_entry_notify (mach_msg_header_t *msg) +{ + struct intr_entry *entry; + + if (msg->msgh_id == MACH_NOTIFY_NO_SENDERS) + { + /* XXX uses internal Linux IRQ handling function. */ + extern void enable_irq (unsigned int irq_nr); + mach_no_senders_notification_t *ns; + + ns = (mach_no_senders_notification_t *) msg; + entry = intr_entry_port_lookup + ((ipc_port_t) ns->not_header.msgh_remote_port); + assert (entry); + + enable_irq (entry->line); + return TRUE; + } + else if (msg->msgh_id == MACH_NOTIFY_DEAD_NAME) + { + int line; + mach_dead_name_notification_t *dn; + unsigned long flags; + + dn = (mach_dead_name_notification_t *) msg; + entry = intr_entry_port_lookup + ((ipc_port_t) dn->not_header.msgh_remote_port); + assert (entry); + + cpu_intr_save (&flags); + line = entry->line; + assert (!queue_empty (&intr_queue)); + queue_remove (&intr_queue, entry, struct intr_entry *, chain); + remove_user_intr_handler (entry->line, entry); + cpu_intr_restore (flags); + + ipc_port_dealloc_kernel (entry->port); + ipc_port_release_send (entry->notification_port); + kmem_cache_free (&intr_entry_cache, (vm_offset_t) entry); + + printf ("irq handler %d: userspace handler died\n", line); + return TRUE; + } + + printf ("intr_entry_notify: strange notification %d\n", + msg->msgh_id); + return FALSE; +} + + +mach_intr_notification_t mach_intr_notification_template; + +static void +init_mach_intr_notification (mach_intr_notification_t *n) +{ + mach_msg_header_t *m = &n->intr_header; + mach_msg_type_t *t = &n->line_type; + + m->msgh_bits = MACH_MSGH_BITS (MACH_MSG_TYPE_PORT_SEND, + MACH_MSG_TYPE_PORT_SEND); + m->msgh_size = sizeof *n; + m->msgh_seqno = INTR_NOTIFY_MSGH_SEQNO; + m->msgh_local_port = MACH_PORT_NULL; + m->msgh_remote_port = MACH_PORT_NULL; + m->msgh_id = MACH_INTR_NOTIFY; + + t->msgt_name = MACH_MSG_TYPE_INTEGER_32; + t->msgt_size = 32; + t->msgt_number = 1; + t->msgt_inline = TRUE; + t->msgt_longform = FALSE; + t->msgt_deallocate = FALSE; + t->msgt_unused = 0; +} + +static boolean_t +deliver_intr (int line, ipc_port_t notification_port, ipc_port_t interrupt_port) +{ + ipc_kmsg_t kmsg; + mach_intr_notification_t *n; + ipc_port_t sright, sonce, old; + + if (notification_port == IP_NULL) + return FALSE; + + if (interrupt_port == IP_NULL) + return FALSE; + + kmsg = ikm_alloc(sizeof *n); + if (kmsg == IKM_NULL) + return FALSE; + + ikm_init(kmsg, sizeof *n); + n = (mach_intr_notification_t *) &kmsg->ikm_header; + *n = mach_intr_notification_template; + + /* Arrange no-senders notification. */ + sright = ipc_port_make_send (interrupt_port); + sonce = ipc_port_make_sonce (interrupt_port); + ip_lock (interrupt_port); + ipc_port_nsrequest (interrupt_port, interrupt_port->ip_mscount, + sonce, &old); + if (old != IP_NULL) + ipc_port_release_sonce (old); + + n->intr_header.msgh_remote_port = (mach_port_t) notification_port; + n->intr_header.msgh_local_port = (mach_port_t) sright; + n->line = line; + + ipc_port_copy_send (notification_port); + ipc_mqueue_send_always (kmsg); + + return TRUE; +} + +void +intr_thread () +{ + queue_init (&intr_queue); + init_mach_intr_notification (&mach_intr_notification_template); + + kmem_cache_init (&intr_entry_cache, "intr_entry", + sizeof (struct intr_entry), 0, NULL, 0); + + for (;;) + { + struct intr_entry *e; + unsigned long flags; + assert_wait ((event_t) &intr_thread, FALSE); + cpu_intr_save (&flags); + + restart: + queue_iterate (&intr_queue, e, struct intr_entry *, chain) + if (e->interrupts) + { + int line = e->line; + ipc_port_t notification_port = e->notification_port; + ipc_port_t interrupt_port = e->port; + + assert (e->interrupts == 1); + e->interrupts--; + + cpu_intr_restore (flags); + deliver_intr (line, notification_port, interrupt_port); + cpu_intr_save (&flags); + + + /* We cannot assume that e still exists at this point + because we released the lock. Hence we restart the + iteration. */ + goto restart; + } + + cpu_intr_restore (flags); + thread_block (thread_no_continuation); + } +} + +#else /* MACH_XEN */ + +boolean_t +intr_entry_notify (mach_msg_header_t *msg) +{ + panic ("not reached"); +} + +#endif /* MACH_XEN */ diff --git a/include/device/intr.h b/include/device/intr.h new file mode 100644 index 0000000..34798b4 --- /dev/null +++ b/include/device/intr.h @@ -0,0 +1,18 @@ +#ifndef __INTR_H__ + +#define __INTR_H__ + +#include + +typedef struct +{ + mach_msg_header_t intr_header; + mach_msg_type_t line_type; + int line; +} mach_intr_notification_t; + + +#define INTR_NOTIFY_MSGH_SEQNO 0 +#define MACH_INTR_NOTIFY 424242 + +#endif diff --git a/include/mach/experimental.defs b/include/mach/experimental.defs new file mode 100644 index 0000000..16850df --- /dev/null +++ b/include/mach/experimental.defs @@ -0,0 +1,98 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +subsystem +#if KERNEL_USER + KernelUser +#endif /* KERNEL_USER */ +#if KERNEL_SERVER + KernelServer +#endif /* KERNEL_SERVER */ + experimental 424242; + +#include +#include + +serverprefix experimental_; + +type notify_port_t = MACH_MSG_TYPE_MOVE_SEND_ONCE + ctype: mach_port_t; + +skip; /*simpleroutine mach_intr_notify( + notify : notify_port_t; + name : int);*/ + +routine device_intr_register( + master_port : mach_port_t; + in line : int; + in id : int; + in flags : int; + in receive_port : mach_port_send_t + ); +/* JW: It doesn't look safe to pass flags through. dde sets + * SA_SHIRQ. + * + * ID seems unused. dde hands in 0. + */ + +/* This no longer does anything. */ +routine device_intr_enable( + master_port : mach_port_t; + line : int; + status : char); + +/* + * This routine is created for allocating DMA buffers. + * We are going to get a contiguous physical memory + * and its physical address in addition to the virtual address. + */ + + /* XXX + This RPC lacks a few additional constraints like boundaries, alignment +and maybe phase. We may not use them now, but they're important for +portability (e.g. if GNU Mach supports PAE, drivers that can't use +physical memory beyond the 4 GiB limit must be able to express it). + +> What do you mean by "phase"? + +Offset from the alignment. But I don't think it's useful at all in this +case. Minimum and maximum addresses and alignment should do. Maybe +boundary crossing but usually, specifying the right alignment and size +is enough. + +For upstream +inclusion, we need to do it properly: the RPC should return a special +memory object (similar to device_map() ), which can then be mapped into +the process address space with vm_map() like any other memory object. + +phys_address_t? + */ +routine vm_allocate_contiguous( + host_priv : host_priv_t; + target_task : vm_task_t; + out vaddr : vm_address_t; + out paddr : vm_address_t; + size : vm_size_t); diff --git a/kern/experimental.srv b/kern/experimental.srv new file mode 100644 index 0000000..2ccfd78 --- /dev/null +++ b/kern/experimental.srv @@ -0,0 +1,3 @@ +#define KERNEL_SERVER 1 + +#include diff --git a/kern/ipc_kobject.c b/kern/ipc_kobject.c index 709ec9e..97c632a 100644 --- a/kern/ipc_kobject.c +++ b/kern/ipc_kobject.c @@ -48,6 +48,7 @@ #include #include #include +#include #include #include @@ -56,6 +57,7 @@ #include #include #include +#include #if MACH_DEBUG #include @@ -159,6 +161,7 @@ ipc_kobject_server(request) * to perform the kernel function */ { + extern mig_routine_t experimental_server_routine(); check_simple_locks(); if ((routine = mach_server_routine(&request->ikm_header)) != 0 || (routine = mach_port_server_routine(&request->ikm_header)) != 0 @@ -170,6 +173,7 @@ ipc_kobject_server(request) #endif /* MACH_DEBUG */ || (routine = mach4_server_routine(&request->ikm_header)) != 0 || (routine = gnumach_server_routine(&request->ikm_header)) != 0 + || (routine = experimental_server_routine(&request->ikm_header)) != 0 #if MACH_MACHINE_ROUTINES || (routine = MACHINE_SERVER_ROUTINE(&request->ikm_header)) != 0 #endif /* MACH_MACHINE_ROUTINES */ @@ -323,6 +327,10 @@ ipc_kobject_destroy( vm_object_pager_wakeup(port); break; + case IKOT_INTR_ENTRY: + /* Do nothing. */ + break; + default: #if MACH_ASSERT printf("ipc_kobject_destroy: port 0x%p, kobj 0x%lx, type %d\n", @@ -365,6 +373,9 @@ ipc_kobject_notify(request_header, reply_header) case IKOT_PAGER_PROXY: return memory_object_proxy_notify(request_header); + case IKOT_INTR_ENTRY: + return intr_entry_notify(request_header); + default: return FALSE; } diff --git a/kern/ipc_kobject.h b/kern/ipc_kobject.h index 606a66a..6aefea7 100644 --- a/kern/ipc_kobject.h +++ b/kern/ipc_kobject.h @@ -77,9 +77,10 @@ typedef unsigned int ipc_kobject_type_t; #define IKOT_CLOCK 25 #define IKOT_CLOCK_CTRL 26 #define IKOT_PAGER_PROXY 27 +#define IKOT_INTR_ENTRY 28 /* << new entries here */ -#define IKOT_UNKNOWN 28 /* magic catchall */ -#define IKOT_MAX_TYPE 29 /* # of IKOT_ types */ +#define IKOT_UNKNOWN 29 /* magic catchall */ +#define IKOT_MAX_TYPE 30 /* # of IKOT_ types */ /* Please keep ipc/ipc_object.c:ikot_print_array up to date */ #define is_ipc_kobject(ikot) (ikot != IKOT_NONE) diff --git a/kern/startup.c b/kern/startup.c index bd29694..0a571ff 100644 --- a/kern/startup.c +++ b/kern/startup.c @@ -62,6 +62,7 @@ #include #include #include +#include #if MACH_KDB #include @@ -221,6 +222,9 @@ void start_kernel_threads(void) (void) kernel_thread(kernel_task, reaper_thread, (char *) 0); (void) kernel_thread(kernel_task, swapin_thread, (char *) 0); (void) kernel_thread(kernel_task, sched_thread, (char *) 0); +#ifndef MACH_XEN + (void) kernel_thread(kernel_task, intr_thread, (char *)0); +#endif /* MACH_XEN */ #if NCPUS > 1 /* diff --git a/linux/dev/arch/i386/kernel/irq.c b/linux/dev/arch/i386/kernel/irq.c index 7753814..e2c18c7 100644 --- a/linux/dev/arch/i386/kernel/irq.c +++ b/linux/dev/arch/i386/kernel/irq.c @@ -49,6 +49,7 @@ #include #include +#include #if 0 /* XXX: This is the way it's done in linux 2.2. GNU Mach currently uses intr_count. It should be made using local_{bh/irq}_count instead (through hardirq_enter/exit) for SMP support. */ @@ -83,6 +84,7 @@ struct linux_action void *dev_id; struct linux_action *next; unsigned long flags; + struct intr_entry *userspace_handler; }; static struct linux_action *irq_action[16] = @@ -113,7 +115,17 @@ linux_intr (int irq) while (action) { - action->handler (irq, action->dev_id, ®s); + // TODO I might need to check whether the interrupt belongs to + // the current device. But I don't do it for now. + if (action->userspace_handler) + { + /* We disable the irq here and it will be enabled + * after the interrupt is handled by the user space driver. */ + disable_irq (irq); + queue_intr (action->userspace_handler); + } + else if (action->handler) + action->handler (irq, action->dev_id, ®s); action = action->next; } @@ -233,6 +245,7 @@ setup_x86_irq (int irq, struct linux_action *new) } while (old); shared = 1; + printk("store a new irq %d\n", irq); } save_flags (flags); @@ -250,6 +263,56 @@ setup_x86_irq (int irq, struct linux_action *new) return 0; } +int +install_user_intr_handler (unsigned int irq, unsigned long flags, + struct intr_entry *entry) +{ + struct linux_action *action; + int retval; + + assert (irq < 16); + + /* + * Hmm... Should I use `kalloc()' ? + * By OKUJI Yoshinori. + */ + action = (struct linux_action *) + linux_kmalloc (sizeof (struct linux_action), GFP_KERNEL); + if (action == NULL) + return linux_to_mach_error (-ENOMEM); + + action->handler = NULL; + action->next = NULL; + action->dev_id = NULL; + action->flags = flags; + action->userspace_handler = entry; + + retval = setup_x86_irq (irq, action); + if (retval) + linux_kfree (action); + + return linux_to_mach_error (retval); +} + +int +remove_user_intr_handler (unsigned int irq, struct intr_entry *entry) +{ + struct linux_action *action, **prev; + + for (prev = &irq_action[irq], action = irq_action[irq]; + action; + prev = &action->next, action = action->next) + if (action->userspace_handler == entry) + { + *prev = action->next; + linux_kfree(action); + enable_irq (irq); + return 1; + } + + return 0; +} + /* * Attach a handler to an IRQ. */ @@ -278,6 +341,7 @@ request_irq (unsigned int irq, void (*handler) (int, void *, struct pt_regs *), action->next = NULL; action->dev_id = dev_id; action->flags = flags; + action->userspace_handler = NULL; retval = setup_x86_irq (irq, action); if (retval) diff --git a/linux/dev/drivers/block/genhd.c b/linux/dev/drivers/block/genhd.c index 3a86138..4a36f7f 100644 --- a/linux/dev/drivers/block/genhd.c +++ b/linux/dev/drivers/block/genhd.c @@ -812,7 +812,9 @@ void device_setup(void) #ifdef MACH linux_intr_pri = SPL6; #endif - net_dev_init(); + extern char *kernel_cmdline; + if (!strstr(kernel_cmdline, " nonetdev")) + net_dev_init(); #endif #ifndef MACH console_map_init(); diff --git a/vm/vm_user.c b/vm/vm_user.c index e65f6d5..788f43a 100644 --- a/vm/vm_user.c +++ b/vm/vm_user.c @@ -449,3 +449,106 @@ kern_return_t vm_wire(port, map, start, size, access) round_page(start+size), access); } + +kern_return_t experimental_vm_allocate_contiguous(host_priv, map, result_vaddr, result_paddr, size) + host_t host_priv; + vm_map_t map; + vm_address_t *result_vaddr; + vm_address_t *result_paddr; + vm_size_t size; +{ + unsigned int npages; + unsigned int i; + unsigned int order; + vm_page_t pages; + vm_object_t object; + vm_map_entry_t entry; + kern_return_t kr; + vm_address_t vaddr; + vm_offset_t offset = 0; + + if (host_priv == HOST_NULL) + return KERN_INVALID_HOST; + + if (map == VM_MAP_NULL) + return KERN_INVALID_TASK; + + /* + * XXX The page allocator returns blocks with a power-of-two size. + * The requested size may not be a power-of-two, causing the pages + * at the end of a block to be unused. In order to keep track of + * those pages, they must all be inserted in the VM object created + * by this function. + */ + order = vm_page_order(size); + size = (1 << (order + PAGE_SHIFT)); + + /* We allocate the contiguous physical pages for the buffer. */ + + npages = size / PAGE_SIZE; + pages = vm_page_grab_contig(size, VM_PAGE_SEL_DIRECTMAP); + if (pages == NULL) + { + return KERN_RESOURCE_SHORTAGE; + } + +#if 0 + kr = vm_page_grab_contig(npages, pages, NULL, TRUE); + if (kr) + { + kfree (pages, npages * sizeof (vm_page_t)); + return kr; + } +#endif + + /* Allocate the object + * and find the virtual address for the DMA buffer */ + + object = vm_object_allocate(size); + vm_map_lock(map); + /* TODO user_wired_count might need to be set as 1 */ + kr = vm_map_find_entry(map, &vaddr, size, (vm_offset_t) 0, + VM_OBJECT_NULL, &entry); + if (kr != KERN_SUCCESS) + { + vm_map_unlock(map); + vm_object_deallocate(object); + vm_page_free_contig(pages, size); + return kr; + } + + entry->object.vm_object = object; + entry->offset = 0; + + /* We can unlock map now. */ + vm_map_unlock(map); + + /* We have physical pages we need and now we need to do the mapping. */ + + pmap_pageable (map->pmap, vaddr, vaddr + size, FALSE); + + *result_vaddr = vaddr; + *result_paddr = pages->phys_addr; + + for (i = 0; i < npages; i++) + { + vm_object_lock(object); + vm_page_lock_queues(); + vm_page_insert(&pages[i], object, offset); + vm_page_wire(&pages[i]); + vm_page_unlock_queues(); + vm_object_unlock(object); + + /* Enter it in the kernel pmap */ + PMAP_ENTER(map->pmap, vaddr, &pages[i], VM_PROT_DEFAULT, TRUE); + + vm_object_lock(object); + PAGE_WAKEUP_DONE(&pages[i]); + vm_object_unlock(object); + + vaddr += PAGE_SIZE; + offset += PAGE_SIZE; + } + + return KERN_SUCCESS; +} -- 2.1.4