bug-hurd
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH gnumach 2/2] 70_new_dde.patch


From: Justus Winter
Subject: [PATCH gnumach 2/2] 70_new_dde.patch
Date: Sun, 28 Feb 2016 00:33:22 +0100

---
 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 <device/dev_hdr.h>
 #include <device/conf.h>
 #include <device/io_req.h>
+#include <device/interrupt.h>
 #include <device/ds_routines.h>
 #include <device/net_status.h>
 #include <device/device_port.h>
@@ -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 <http://www.gnu.org/licenses/>.
+ */
+
+#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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <device/intr.h>
+#include <device/ds_routines.h>
+#include <ipc/ipc_space.h>
+#include <kern/debug.h>
+#include <kern/queue.h>
+#include <kern/printf.h>
+#include <mach/notify.h>
+#include <machine/cpu.h>
+
+#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 <device/device_types.h>
+
+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 <mach/std_types.defs>
+#include <mach/mach_types.defs>
+
+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 <mach/experimental.defs>
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 <vm/vm_object.h>
 #include <vm/memory_object_proxy.h>
 #include <device/ds_routines.h>
+#include <device/interrupt.h>
 
 #include <kern/mach.server.h>
 #include <ipc/mach_port.server.h>
@@ -56,6 +57,7 @@
 #include <device/device_pager.server.h>
 #include <kern/mach4.server.h>
 #include <kern/gnumach.server.h>
+#include <kern/experimental.server.h>
 
 #if MACH_DEBUG
 #include <kern/mach_debug.server.h>
@@ -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 <machine/model_dep.h>
 #include <mach/version.h>
 #include <device/device_init.h>
+#include <device/interrupt.h>
 
 #if MACH_KDB
 #include <device/cons.h>
@@ -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 <linux/dev/glue/glue.h>
 #include <machine/machspl.h>
+#include <device/interrupt.h>
 
 #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, &regs);
+      // 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, &regs);
       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




reply via email to

[Prev in Thread] Current Thread [Next in Thread]