From 274a5023c283edcde321b95462a9ad06045d0df4 Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Fri, 10 Jul 2020 00:21:59 +0200 Subject: typo --- device/ds_routines.c | 2 +- device/intr.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/device/ds_routines.c b/device/ds_routines.c index af9c7617..78ff51fe 100644 --- a/device/ds_routines.c +++ b/device/ds_routines.c @@ -345,7 +345,7 @@ ds_device_intr_register (device_t dev, int id, return D_NO_MEMORY; // TODO The original port should be replaced - // when the same device driver calls it again, + // when the same device driver calls it again, // in order to handle the case that the device driver crashes and restarts. err = install_user_intr_handler (&irqtab, id, flags, e); if (err == D_SUCCESS) diff --git a/device/intr.c b/device/intr.c index 6ef9d6ec..fbb9f495 100644 --- a/device/intr.c +++ b/device/intr.c @@ -154,7 +154,7 @@ intr_thread (void) int id; ipc_port_t dst_port; queue_init (&main_intr_queue); - + for (;;) { assert_wait ((event_t) &intr_thread, FALSE); @@ -247,7 +247,7 @@ deliver_intr (int id, ipc_port_t dst_port) return FALSE; kmsg = ikm_alloc(sizeof *n); - if (kmsg == IKM_NULL) + if (kmsg == IKM_NULL) return FALSE; ikm_init(kmsg, sizeof *n); -- cgit v1.2.3 From 6054cda4de2341b9a77ec4421411725f3684006b Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Fri, 10 Jul 2020 00:23:32 +0200 Subject: Add hardware interrupt notification mechanism This allows privileged userland drivers to get notifications of hardware interrupts. Initial work by Zheng Da, reworked by Damien Zammit and myself. * Makefrag.am (libkernel_a_SOURCES): Add device/intr.c and device/intr.h. (include_device_HEADERS): Add include/device/notify.defs and include/device/notify.h. * device/dev_hdr.h (name_equal): Add declaration. * device/ds_routines.c: Include (ds_device_intr_register, ds_device_intr_ack): New functions. * device/intr.c, device/intr.h: New files. * doc/mach.texi (Device Interrupt): New section. * i386/Makefrag.am (libkernel_a_SOURCES): Add i386/i386/irq.c and i386/i386/irq.h. * i386/i386/irq.c, i386/i386/irq.h: New files. * i386/i386at/conf.c: Include . (irqname): New macro. (dev_name_list): Add irq device. * include/device/device.defs (device_intr_register, device_intr_ack): New RPCs. * include/device/notify.defs, include/device/notify.h: New files. * kern/startup.c: Include (start_kernel_threads): Start intr_thread thread. * linux/dev/arch/i386/kernel/irq.c: Include (linux_action): Add user_intr field. (linux_intr): Call user_intr action if any. (mask_irq, unmask_irq): Move functions to i386/i386/pic.c (__disable_irq, __enable_irq): Move functions to i386/i386/irq.c. (install_user_intr_handler): New function. (request_irq): Initialize user_intr field. * linux/src/include/asm-i386/irq.h (__disable_irq, __enable_irq): Remove prototypes. * i386/i386/pic.c (mask_irq, unmask_irq): New functions. * i386/i386/pic.h (mask_irq, unmask_irq): New prototypes. --- Makefrag.am | 4 + device/dev_hdr.h | 9 ++ device/ds_routines.c | 54 ++++++++ device/intr.c | 283 +++++++++++++++++++++++++++++++++++++++ device/intr.h | 62 +++++++++ doc/mach.texi | 22 +++ i386/Makefrag.am | 2 + i386/i386/irq.c | 66 +++++++++ i386/i386/irq.h | 27 ++++ i386/i386/pic.c | 72 ++++++++++ i386/i386/pic.h | 2 + i386/i386at/conf.c | 8 ++ include/device/device.defs | 18 +++ include/device/notify.defs | 36 +++++ include/device/notify.h | 34 +++++ kern/startup.c | 4 + linux/dev/arch/i386/kernel/irq.c | 152 ++++++++++----------- linux/src/include/asm-i386/irq.h | 2 - 18 files changed, 776 insertions(+), 81 deletions(-) create mode 100644 device/intr.c create mode 100644 device/intr.h create mode 100644 i386/i386/irq.c create mode 100644 i386/i386/irq.h create mode 100644 include/device/notify.defs create mode 100644 include/device/notify.h diff --git a/Makefrag.am b/Makefrag.am index 9cf976a5..ea612275 100644 --- a/Makefrag.am +++ b/Makefrag.am @@ -314,6 +314,8 @@ libkernel_a_SOURCES += \ device/ds_routines.h \ device/if_ether.h \ device/if_hdr.h \ + device/intr.c \ + device/intr.h \ device/io_req.h \ device/net_io.c \ device/net_io.h \ @@ -361,6 +363,8 @@ include_device_HEADERS = \ include/device/device_types.h \ include/device/disk_status.h \ include/device/net_status.h \ + include/device/notify.defs \ + include/device/notify.h \ include/device/tape_status.h \ include/device/tty_status.h diff --git a/device/dev_hdr.h b/device/dev_hdr.h index ad98e0bb..4bd12c1c 100644 --- a/device/dev_hdr.h +++ b/device/dev_hdr.h @@ -146,4 +146,13 @@ extern void dev_set_indirection( dev_ops_t ops, int unit); +/* + * compare device name + */ +extern boolean_t __attribute__ ((pure)) +name_equal( + const char *src, + int len, + const char *target); + #endif /* _DEVICE_DEV_HDR_H_ */ diff --git a/device/ds_routines.c b/device/ds_routines.c index fc051e8f..78ff51fe 100644 --- a/device/ds_routines.c +++ b/device/ds_routines.c @@ -92,6 +92,7 @@ #include #include #include +#include #include @@ -319,6 +320,59 @@ ds_device_map (device_t dev, vm_prot_t prot, vm_offset_t offset, offset, size, pager, unmap); } +/* TODO: missing deregister support */ +io_return_t +ds_device_intr_register (device_t dev, int id, + int flags, ipc_port_t receive_port) +{ + kern_return_t err; + mach_device_t mdev = dev->emul_data; + + /* Refuse if device is dead or not completely open. */ + if (dev == DEVICE_NULL) + return D_NO_SUCH_DEVICE; + + /* No flag is defined for now */ + if (flags != 0) + return D_INVALID_OPERATION; + + /* Must be called on the irq device only */ + if (! name_equal(mdev->dev_ops->d_name, 3, "irq")) + return D_INVALID_OPERATION; + + user_intr_t *e = insert_intr_entry (&irqtab, id, receive_port); + if (!e) + return D_NO_MEMORY; + + // TODO The original port should be replaced + // when the same device driver calls it again, + // in order to handle the case that the device driver crashes and restarts. + err = install_user_intr_handler (&irqtab, id, flags, e); + if (err == D_SUCCESS) + { + /* If the port is installed successfully, increase its reference by 1. + * Thus, the port won't be destroyed after its task is terminated. */ + ip_reference (receive_port); + } + return err; +} + +kern_return_t +ds_device_intr_ack (device_t dev, ipc_port_t receive_port) +{ + mach_device_t mdev = dev->emul_data; + + /* Refuse if device is dead or not completely open. */ + if (dev == DEVICE_NULL) + return D_NO_SUCH_DEVICE; + + /* Must be called on the irq device only */ + if (! name_equal(mdev->dev_ops->d_name, 3, "irq")) + return D_INVALID_OPERATION; + + return irq_acknowledge(receive_port); +} + boolean_t ds_notify (mach_msg_header_t *msg) { diff --git a/device/intr.c b/device/intr.c new file mode 100644 index 00000000..fbb9f495 --- /dev/null +++ b/device/intr.c @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2010, 2011, 2016, 2019 Free Software Foundation, Inc. + * + * 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. + * + * THE FREE SOFTWARE FOUNDATIONALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. THE FREE SOFTWARE FOUNDATION DISCLAIMS ANY LIABILITY OF ANY KIND + * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef MACH_XEN + +queue_head_t main_intr_queue; +static boolean_t deliver_intr (int id, ipc_port_t dst_port); + +static user_intr_t * +search_intr (struct irqdev *dev, ipc_port_t dst_port) +{ + user_intr_t *e; + queue_iterate (dev->intr_queue, e, user_intr_t *, chain) + { + if (e->dst_port == dst_port) + return e; + } + return NULL; +} + +kern_return_t +irq_acknowledge (ipc_port_t receive_port) +{ + user_intr_t *e; + kern_return_t ret = 0; + + spl_t s = splhigh (); + e = search_intr (&irqtab, receive_port); + + if (!e) + printf("didn't find user intr for interrupt !?\n"); + else + { + if (!e->n_unacked) + ret = D_INVALID_OPERATION; + else + e->n_unacked--; + } + splx (s); + + if (ret) + return ret; + + if (irqtab.irqdev_ack) + (*(irqtab.irqdev_ack)) (&irqtab, e->id); + + __enable_irq (irqtab.irq[e->id]); + + return D_SUCCESS; +} + +/* This function can only be used in the interrupt handler. */ +static void +queue_intr (struct irqdev *dev, int id, user_intr_t *e) +{ + /* Until userland has handled the IRQ in the driver, we have to keep it + * disabled. Level-triggered interrupts would keep raising otherwise. */ + __disable_irq (dev->irq[id]); + + spl_t s = splhigh (); + e->n_unacked++; + e->interrupts++; + dev->tot_num_intr++; + splx (s); + + thread_wakeup ((event_t) &intr_thread); +} + +int +deliver_user_intr (struct irqdev *dev, int id, user_intr_t *e) +{ + /* The reference of the port was increased + * when the port was installed. + * If the reference is 1, it means the port should + * have been destroyed and I destroy it now. */ + if (e->dst_port + && e->dst_port->ip_references == 1) + { + printf ("irq handler [%d]: release a dead delivery port %p entry %p\n", id, e->dst_port, e); + ipc_port_release (e->dst_port); + e->dst_port = MACH_PORT_NULL; + thread_wakeup ((event_t) &intr_thread); + return 0; + } + else + { + queue_intr (dev, id, e); + return 1; + } +} + +/* insert an interrupt entry in the queue. + * This entry exists in the queue until + * the corresponding interrupt port is removed.*/ +user_intr_t * +insert_intr_entry (struct irqdev *dev, int id, ipc_port_t dst_port) +{ + user_intr_t *e, *new, *ret; + int free = 0; + + new = (user_intr_t *) kalloc (sizeof (*new)); + if (new == NULL) + return NULL; + + /* check whether the intr entry has been in the queue. */ + spl_t s = splhigh (); + e = search_intr (dev, dst_port); + if (e) + { + printf ("the interrupt entry for irq[%d] and port %p has already been inserted\n", id, dst_port); + free = 1; + ret = NULL; + goto out; + } + printf("irq handler [%d]: new delivery port %p entry %p\n", id, dst_port, new); + ret = new; + new->id = id; + new->dst_port = dst_port; + new->interrupts = 0; + new->n_unacked = 0; + + queue_enter (dev->intr_queue, new, user_intr_t *, chain); +out: + splx (s); + if (free) + kfree ((vm_offset_t) new, sizeof (*new)); + return ret; +} + +void +intr_thread (void) +{ + user_intr_t *e; + int id; + ipc_port_t dst_port; + queue_init (&main_intr_queue); + + for (;;) + { + assert_wait ((event_t) &intr_thread, FALSE); + /* Make sure we wake up from times to times to check for aborted processes */ + thread_set_timeout (hz); + spl_t s = splhigh (); + + /* Check for aborted processes */ + queue_iterate (&main_intr_queue, e, user_intr_t *, chain) + { + if ((!e->dst_port || e->dst_port->ip_references == 1) && e->n_unacked) + { + printf ("irq handler [%d]: release dead delivery %d unacked irqs port %p entry %p\n", e->id, e->n_unacked, e->dst_port, e); + /* The reference of the port was increased + * when the port was installed. + * If the reference is 1, it means the port should + * have been destroyed and I clear unacked irqs now, so the Linux + * handling can trigger, and we will cleanup later after the Linux + * handler is cleared. */ + /* TODO: rather immediately remove from Linux handler */ + while (e->n_unacked) + { + __enable_irq (irqtab.irq[e->id]); + e->n_unacked--; + } + } + } + + /* Now check for interrupts */ + while (irqtab.tot_num_intr) + { + int del = 0; + + queue_iterate (&main_intr_queue, e, user_intr_t *, chain) + { + /* if an entry doesn't have dest port, + * we should remove it. */ + if (e->dst_port == MACH_PORT_NULL) + { + clear_wait (current_thread (), 0, 0); + del = 1; + break; + } + + if (e->interrupts) + { + clear_wait (current_thread (), 0, 0); + id = e->id; + dst_port = e->dst_port; + e->interrupts--; + irqtab.tot_num_intr--; + + splx (s); + deliver_intr (id, dst_port); + s = splhigh (); + } + } + + /* remove the entry without dest port from the queue and free it. */ + if (del) + { + assert (!queue_empty (&main_intr_queue)); + queue_remove (&main_intr_queue, e, user_intr_t *, chain); + if (e->n_unacked) + printf("irq handler [%d]: still %d unacked irqs in entry %p\n", e->id, e->n_unacked, e); + while (e->n_unacked) + { + __enable_irq (irqtab.irq[e->id]); + e->n_unacked--; + } + printf("irq handler [%d]: removed entry %p\n", e->id, e); + splx (s); + kfree ((vm_offset_t) e, sizeof (*e)); + s = splhigh (); + } + } + splx (s); + thread_block (NULL); + } +} + +static boolean_t +deliver_intr (int id, ipc_port_t dst_port) +{ + ipc_kmsg_t kmsg; + device_intr_notification_t *n; + mach_port_t dest = (mach_port_t) dst_port; + + if (dest == MACH_PORT_NULL) + return FALSE; + + kmsg = ikm_alloc(sizeof *n); + if (kmsg == IKM_NULL) + return FALSE; + + ikm_init(kmsg, sizeof *n); + n = (device_intr_notification_t *) &kmsg->ikm_header; + + mach_msg_header_t *m = &n->intr_header; + mach_msg_type_t *t = &n->intr_type; + + m->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND, 0); + m->msgh_size = sizeof *n; + m->msgh_seqno = DEVICE_NOTIFY_MSGH_SEQNO; + m->msgh_local_port = MACH_PORT_NULL; + m->msgh_remote_port = MACH_PORT_NULL; + m->msgh_id = DEVICE_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; + + n->intr_header.msgh_remote_port = dest; + n->id = id; + + ipc_port_copy_send (dst_port); + ipc_mqueue_send_always(kmsg); + + return TRUE; +} + +#endif /* MACH_XEN */ diff --git a/device/intr.h b/device/intr.h new file mode 100644 index 00000000..cd3e0bce --- /dev/null +++ b/device/intr.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2010, 2011, 2019 Free Software Foundation, Inc. + * + * 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. + * + * THE FREE SOFTWARE FOUNDATIONALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. THE FREE SOFTWARE FOUNDATION DISCLAIMS ANY LIABILITY OF ANY KIND + * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + */ + +#ifndef __INTR_H__ +#define __INTR_H__ + +#ifndef MACH_XEN + +#include +#include +#include +#include +#include + +#define DEVICE_NOTIFY_MSGH_SEQNO 0 + +#include + +struct irqdev; +#include + +typedef struct { + queue_chain_t chain; + int interrupts; /* Number of interrupts occurred since last run of intr_thread */ + int n_unacked; /* Number of times irqs were disabled for this */ + ipc_port_t dst_port; /* Notification port */ + int id; /* Mapping to machine dependent irq_t array elem */ +} user_intr_t; + +struct irqdev { + char *name; + void (*irqdev_ack)(struct irqdev *dev, int id); + + queue_head_t *intr_queue; + int tot_num_intr; /* Total number of unprocessed interrupts */ + + /* Machine dependent */ + irq_t irq[NINTR]; +}; + +extern queue_head_t main_intr_queue; +extern int install_user_intr_handler (struct irqdev *dev, int id, unsigned long flags, user_intr_t *e); +extern int deliver_user_intr (struct irqdev *dev, int id, user_intr_t *e); +extern user_intr_t *insert_intr_entry (struct irqdev *dev, int id, ipc_port_t receive_port); + +void intr_thread (void); +kern_return_t irq_acknowledge (ipc_port_t receive_port); + +#endif /* MACH_XEN */ + +#endif diff --git a/doc/mach.texi b/doc/mach.texi index 91ec96ee..11b0a066 100644 --- a/doc/mach.texi +++ b/doc/mach.texi @@ -286,6 +286,7 @@ Device Interface * Device Map:: Mapping devices into virtual memory. * Device Status:: Querying and manipulating a device. * Device Filter:: Filtering packets arriving on a device. +* Device Interrupt:: Getting hardware interrupt notifications. Kernel Debugger @@ -6305,6 +6306,7 @@ All constants and functions in this chapter are defined in * Device Map:: Mapping devices into virtual memory. * Device Status:: Querying and manipulating a device. * Device Filter:: Filtering packets arriving on a device. +* Device Interrupt:: Getting hardware interrupt notifications. @end menu @@ -6699,6 +6701,26 @@ a device port or the device is dead or not completely open. @end deftypefun +@node Device Interrupt +@section Device Interrupt + +@deftypefun kern_return_t device_intr_register (@w{device_t @var{device}}, @w{int @var{id}}, @w{int @var{flags}}, @w{mach_port_t @var{receive_port}}) +The function @code{device_intr_register} registers for receiving hardware +interrupt events through @var{device_intr_notify} notifications. The hardware +interrupt identifier is specified by @var{id}. @var{flags} must be set to 0. The +notifications will be sent on the @var{receive_port} send right. +@code{device_intr_register} is only available on the dedicated @code{irq} device. +@end deftypefun + +@deftypefun kern_return_t device_intr_ack (@w{device_t @var{device}}, @w{mach_port_t @var{receive_port}}) +On a hardware interrupt, the kernel disables the interrupt line before sending +notifications. To prevent from interrupt losses, the interrupt is kept disabled +until @code{device_intr_ack} is called to acknowledge the interrupt. +@var{receive_port} is the send right on which the interrupt notification was +received. +@end deftypefun + + @node Kernel Debugger @chapter Kernel Debugger diff --git a/i386/Makefrag.am b/i386/Makefrag.am index f38c0785..59571416 100644 --- a/i386/Makefrag.am +++ b/i386/Makefrag.am @@ -102,6 +102,8 @@ libkernel_a_SOURCES += \ i386/i386/io_perm.c \ i386/i386/io_perm.h \ i386/i386/ipl.h \ + i386/i386/irq.c \ + i386/i386/irq.h \ i386/i386/ktss.c \ i386/i386/ktss.h \ i386/i386/kttd_interface.c \ diff --git a/i386/i386/irq.c b/i386/i386/irq.c new file mode 100644 index 00000000..c65d2ea2 --- /dev/null +++ b/i386/i386/irq.c @@ -0,0 +1,66 @@ +/* + * Copyright (C) 1995 Shantanu Goel + * Copyright (C) 2020 Free Software Foundation, Inc + * + * 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, 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, write to the Free Software + * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include + +extern queue_head_t main_intr_queue; + +static void +irq_eoi (struct irqdev *dev, int id) +{ + /* TODO EOI(dev->irq[id]) */ +} + +static unsigned int ndisabled_irq[NINTR]; + +void +__disable_irq (irq_t irq_nr) +{ + assert (irq_nr < NINTR); + + spl_t s = splhigh(); + ndisabled_irq[irq_nr]++; + assert (ndisabled_irq[irq_nr] > 0); + if (ndisabled_irq[irq_nr] == 1) + mask_irq (irq_nr); + splx(s); +} + +void +__enable_irq (irq_t irq_nr) +{ + assert (irq_nr < NINTR); + + spl_t s = splhigh(); + assert (ndisabled_irq[irq_nr] > 0); + ndisabled_irq[irq_nr]--; + if (ndisabled_irq[irq_nr] == 0) + unmask_irq (irq_nr); + splx(s); +} + +struct irqdev irqtab = { + "irq", irq_eoi, &main_intr_queue, 0, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, +}; + diff --git a/i386/i386/irq.h b/i386/i386/irq.h new file mode 100644 index 00000000..d48a8e92 --- /dev/null +++ b/i386/i386/irq.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2020 Free Software Foundation, Inc. + * + * 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. + * + * THE FREE SOFTWARE FOUNDATION ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. THE FREE SOFTWARE FOUNDATION DISCLAIMS ANY LIABILITY OF ANY KIND + * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + */ + +#ifndef _I386_IRQ_H +#define _I386_IRQ_H + +#include + +typedef unsigned int irq_t; + +void __enable_irq (irq_t irq); +void __disable_irq (irq_t irq); + +extern struct irqdev irqtab; + +#endif diff --git a/i386/i386/pic.c b/i386/i386/pic.c index 0feebc6f..62ed9ed1 100644 --- a/i386/i386/pic.c +++ b/i386/i386/pic.c @@ -49,6 +49,24 @@ OTHER TORTIOUS ACTION, ARISING OUR OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +/* + * Copyright (C) 1995 Shantanu Goel. + * + * 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, 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, write to the Free Software + * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + #include #include #include @@ -185,3 +203,57 @@ intnull(int unit_dev) } } + +/* + * Mask a PIC IRQ. + */ +inline void +mask_irq (unsigned int irq_nr) +{ + int new_pic_mask = curr_pic_mask | 1 << irq_nr; + + if (curr_pic_mask != new_pic_mask) + { + curr_pic_mask = new_pic_mask; + if (irq_nr < 8) + { + outb (PIC_MASTER_OCW, curr_pic_mask & 0xff); + } + else + { + outb (PIC_SLAVE_OCW, curr_pic_mask >> 8); + } + } +} + +/* + * Unmask a PIC IRQ. + */ +inline void +unmask_irq (unsigned int irq_nr) +{ + int mask; + int new_pic_mask; + + mask = 1 << irq_nr; + if (irq_nr >= 8) + { + mask |= 1 << 2; + } + + new_pic_mask = curr_pic_mask & ~mask; + + if (curr_pic_mask != new_pic_mask) + { + curr_pic_mask = new_pic_mask; + if (irq_nr < 8) + { + outb (PIC_MASTER_OCW, curr_pic_mask & 0xff); + } + else + { + outb (PIC_SLAVE_OCW, curr_pic_mask >> 8); + } + } +} + diff --git a/i386/i386/pic.h b/i386/i386/pic.h index f492de5e..6434bf08 100644 --- a/i386/i386/pic.h +++ b/i386/i386/pic.h @@ -180,6 +180,8 @@ WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. extern void picinit (void); extern int curr_pic_mask; extern void intnull(int unit); +extern inline void mask_irq (unsigned int irq_nr); +extern inline void unmask_irq (unsigned int irq_nr); #endif /* __ASSEMBLER__ */ #endif /* _I386_PIC_H_ */ diff --git a/i386/i386at/conf.c b/i386/i386at/conf.c index fe7c7c09..ca5d0dfb 100644 --- a/i386/i386at/conf.c +++ b/i386/i386at/conf.c @@ -68,6 +68,9 @@ #define hypcnname "hyp" #endif /* MACH_HYP */ +#include +#define irqname "irq" + /* * List of devices - console must be at slot 0 */ @@ -149,6 +152,11 @@ struct dev_ops dev_name_list[] = nodev }, #endif /* MACH_HYP */ + { irqname, nulldev_open, nulldev_close, nulldev_read, + nulldev_write,nulldev_getstat,nulldev_setstat, nomap, + nodev, nulldev, nulldev_portdeath,0, + nodev }, + }; int dev_name_count = sizeof(dev_name_list)/sizeof(dev_name_list[0]); diff --git a/include/device/device.defs b/include/device/device.defs index 409146f5..ec4b5bf8 100644 --- a/include/device/device.defs +++ b/include/device/device.defs @@ -142,3 +142,21 @@ routine device_set_filter( in filter : filter_array_t ); +routine device_intr_register( + device : device_t; + in id : int; + in flags : int; + in receive_port : mach_port_send_t + ); + +/* + * Acknowledge the specified interrupt notification. + */ +/* + * When an IRQ happens and an intr notification is thus sent, the IRQ line + * is kept disabled until the notification is acknowledged with this RPC + */ +routine device_intr_ack( + device : device_t; + in receive_port : mach_port_send_t); + diff --git a/include/device/notify.defs b/include/device/notify.defs new file mode 100644 index 00000000..7919b339 --- /dev/null +++ b/include/device/notify.defs @@ -0,0 +1,36 @@ +/* + * 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 notify 100; + +#include + +serverprefix do_; +serverdemux device_intr_notify_server; + +simpleroutine device_intr_notify( + notify : notify_port_t; + id : int); diff --git a/include/device/notify.h b/include/device/notify.h new file mode 100644 index 00000000..addf9114 --- /dev/null +++ b/include/device/notify.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2010 Free Software Foundation, Inc. + * + * 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. + * + * THE FREE SOFTWARE FOUNDATIONALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. THE FREE SOFTWARE FOUNDATION DISCLAIMS ANY LIABILITY OF ANY KIND + * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + */ + +/* + * Device notification definitions. + */ + +#ifndef _MACH_DEVICE_NOTIFY_H_ +#define _MACH_DEVICE_NOTIFY_H_ + +#include +#include + +typedef struct +{ + mach_msg_header_t intr_header; + mach_msg_type_t intr_type; + int id; +} device_intr_notification_t; + +#define DEVICE_INTR_NOTIFY 100 + +#endif /* _MACH_DEVICE_NOTIFY_H_ */ diff --git a/kern/startup.c b/kern/startup.c index 56e05b3f..1f873192 100644 --- a/kern/startup.c +++ b/kern/startup.c @@ -63,6 +63,7 @@ #include #include #include +#include #if MACH_KDB #include @@ -228,6 +229,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 18448638..1e911f33 100644 --- a/linux/dev/arch/i386/kernel/irq.c +++ b/linux/dev/arch/i386/kernel/irq.c @@ -50,6 +50,8 @@ #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. */ unsigned int local_bh_count[NR_CPUS]; @@ -76,6 +78,7 @@ struct linux_action void *dev_id; struct linux_action *next; unsigned long flags; + user_intr_t *user_intr; }; static struct linux_action *irq_action[16] = @@ -95,6 +98,7 @@ linux_intr (int irq) { struct pt_regs regs; struct linux_action *action = *(irq_action + irq); + struct linux_action **prev = &irq_action[irq]; unsigned long flags; kstat.interrupts[irq]++; @@ -106,93 +110,35 @@ 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->user_intr) + { + if (!deliver_user_intr(&irqtab, irq, action->user_intr)) + { + *prev = action->next; + linux_kfree(action); + action = *prev; + continue; + } + } + else if (action->handler) + action->handler (irq, action->dev_id, ®s); + prev = &action->next; action = action->next; } - restore_flags (flags); - - intr_count--; -} - -/* - * Mask an IRQ. - */ -static inline void -mask_irq (unsigned int irq_nr) -{ - int new_pic_mask = curr_pic_mask | 1 << irq_nr; - - if (curr_pic_mask != new_pic_mask) - { - curr_pic_mask = new_pic_mask; - if (irq_nr < 8) - outb (curr_pic_mask & 0xff, PIC_MASTER_OCW); - else - outb (curr_pic_mask >> 8, PIC_SLAVE_OCW); - } -} - -/* - * Unmask an IRQ. - */ -static inline void -unmask_irq (unsigned int irq_nr) -{ - int mask; - int new_pic_mask; - - mask = 1 << irq_nr; - if (irq_nr >= 8) - mask |= 1 << 2; - - new_pic_mask = curr_pic_mask & ~mask; - - if (curr_pic_mask != new_pic_mask) + if (!irq_action[irq]) { - curr_pic_mask = new_pic_mask; - if (irq_nr < 8) - outb (curr_pic_mask & 0xff, PIC_MASTER_OCW); - else - outb (curr_pic_mask >> 8, PIC_SLAVE_OCW); + /* No handler any more, disable interrupt */ + mask_irq (irq); + ivect[irq] = intnull; + iunit[irq] = irq; } -} - -/* Count how many subsystems requested to disable each IRQ */ -static unsigned ndisabled_irq[NR_IRQS]; - -/* These disable/enable IRQs for real after counting how many subsystems - * requested that */ -void -__disable_irq (unsigned int irq_nr) -{ - unsigned long flags; - - assert (irq_nr < NR_IRQS); - save_flags (flags); - cli (); - ndisabled_irq[irq_nr]++; - assert (ndisabled_irq[irq_nr] > 0); - if (ndisabled_irq[irq_nr] == 1) - mask_irq (irq_nr); restore_flags (flags); -} -void -__enable_irq (unsigned int irq_nr) -{ - unsigned long flags; - - assert (irq_nr < NR_IRQS); - - save_flags (flags); - cli (); - assert (ndisabled_irq[irq_nr] > 0); - ndisabled_irq[irq_nr]--; - if (ndisabled_irq[irq_nr] == 0) - unmask_irq (irq_nr); - restore_flags (flags); + intr_count--; } /* IRQ mask according to Linux drivers */ @@ -273,6 +219,53 @@ setup_x86_irq (int irq, struct linux_action *new) return 0; } +int +install_user_intr_handler (struct irqdev *dev, int id, unsigned long flags, + user_intr_t *user_intr) +{ + struct linux_action *action; + struct linux_action *old; + int retval; + + unsigned int irq = dev->irq[id]; + + assert (irq < 16); + + /* Test whether the irq handler has been set */ + // TODO I need to protect the array when iterating it. + old = irq_action[irq]; + while (old) + { + if (old->user_intr && old->user_intr->dst_port == user_intr->dst_port) + { + printk ("The interrupt handler has already been installed on line %d", irq); + return linux_to_mach_error (-EAGAIN); + } + old = old->next; + } + + /* + * 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 = SA_SHIRQ; + action->user_intr = user_intr; + + retval = setup_x86_irq (irq, action); + if (retval) + linux_kfree (action); + + return linux_to_mach_error (retval); +} + /* * Attach a handler to an IRQ. */ @@ -301,6 +294,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->user_intr = NULL; retval = setup_x86_irq (irq, action); if (retval) diff --git a/linux/src/include/asm-i386/irq.h b/linux/src/include/asm-i386/irq.h index d7d1e3c5..c75744a5 100644 --- a/linux/src/include/asm-i386/irq.h +++ b/linux/src/include/asm-i386/irq.h @@ -16,8 +16,6 @@ #define TIMER_IRQ 0 -extern void __disable_irq(unsigned int); -extern void __enable_irq(unsigned int); extern void disable_irq(unsigned int); extern void enable_irq(unsigned int); -- cgit v1.2.3