diff options
author | Samuel Thibault <samuel.thibault@ens-lyon.org> | 2020-07-09 23:46:32 +0200 |
---|---|---|
committer | Samuel Thibault <samuel.thibault@ens-lyon.org> | 2020-07-09 23:52:46 +0200 |
commit | 297950e5dad7b18d27bb6ab53d676524a744c979 (patch) | |
tree | f479d83504ccb456d8c405fd9cedbfcd3df6df62 | |
parent | af36dbf3fd0cd065afadf0d8e7ae3aac39280bc4 (diff) | |
parent | e85ae9d7befbfe7a79235e68fb0423d40b6baf6c (diff) |
Merge branch 'master-user_level_drivers2' into master-user_level_drivers2-debian
-rw-r--r-- | device/ds_routines.c | 4 | ||||
-rw-r--r-- | device/intr.c | 11 | ||||
-rw-r--r-- | device/intr.h | 3 | ||||
-rw-r--r-- | include/device/notify.h | 2 | ||||
-rw-r--r-- | linux/dev/arch/i386/kernel/irq.c.orig | 732 |
5 files changed, 11 insertions, 741 deletions
diff --git a/device/ds_routines.c b/device/ds_routines.c index c36eeecd..c16b2098 100644 --- a/device/ds_routines.c +++ b/device/ds_routines.c @@ -340,7 +340,7 @@ ds_device_intr_register (device_t dev, int id, 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); + user_intr_t *e = insert_intr_entry (&irqtab, id, receive_port, 0); if (!e) return D_NO_MEMORY; @@ -375,7 +375,7 @@ experimental_device_intr_register (ipc_port_t master_port, int line, if (flags != 0x04000000) return D_INVALID_OPERATION; - user_intr_t *user_intr = insert_intr_entry (&irqtab, line, receive_port); + user_intr_t *user_intr = insert_intr_entry (&irqtab, line, receive_port, 1); if (!user_intr) return D_NO_MEMORY; // TODO The original port should be replaced diff --git a/device/intr.c b/device/intr.c index 56f0c32c..a757b25c 100644 --- a/device/intr.c +++ b/device/intr.c @@ -24,7 +24,7 @@ #ifndef MACH_XEN queue_head_t main_intr_queue; -static boolean_t deliver_intr (int id, ipc_port_t dst_port); +static boolean_t deliver_intr (int id, mach_msg_id_t msgh_id, ipc_port_t dst_port); static user_intr_t * search_intr (struct irqdev *dev, ipc_port_t dst_port) @@ -113,7 +113,7 @@ deliver_user_intr (struct irqdev *dev, int id, user_intr_t *e) * 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) +insert_intr_entry (struct irqdev *dev, int id, ipc_port_t dst_port, int compat) { user_intr_t *e, *new, *ret; int free = 0; @@ -137,6 +137,7 @@ insert_intr_entry (struct irqdev *dev, int id, ipc_port_t dst_port) new->id = id; new->dst_port = dst_port; new->interrupts = 0; + new->compat = compat; queue_enter (dev->intr_queue, new, user_intr_t *, chain); out: @@ -207,7 +208,7 @@ intr_thread (void) irqtab.tot_num_intr--; splx (s); - deliver_intr (id, dst_port); + deliver_intr (id, e->compat ? 424242 : DEVICE_INTR_NOTIFY, dst_port); s = splhigh (); } } @@ -236,7 +237,7 @@ intr_thread (void) } static boolean_t -deliver_intr (int id, ipc_port_t dst_port) +deliver_intr (int id, mach_msg_id_t msgh_id, ipc_port_t dst_port) { ipc_kmsg_t kmsg; device_intr_notification_t *n; @@ -260,7 +261,7 @@ deliver_intr (int id, ipc_port_t dst_port) 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; + m->msgh_id = msgh_id; t->msgt_name = MACH_MSG_TYPE_INTEGER_32; t->msgt_size = 32; diff --git a/device/intr.h b/device/intr.h index cd3e0bce..bd639910 100644 --- a/device/intr.h +++ b/device/intr.h @@ -36,6 +36,7 @@ typedef struct { 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 */ + int compat; } user_intr_t; struct irqdev { @@ -52,7 +53,7 @@ struct irqdev { 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); +extern user_intr_t *insert_intr_entry (struct irqdev *dev, int id, ipc_port_t receive_port, int compat); void intr_thread (void); kern_return_t irq_acknowledge (ipc_port_t receive_port); diff --git a/include/device/notify.h b/include/device/notify.h index bd8d3a56..addf9114 100644 --- a/include/device/notify.h +++ b/include/device/notify.h @@ -29,6 +29,6 @@ typedef struct int id; } device_intr_notification_t; -#define DEVICE_INTR_NOTIFY 424242 +#define DEVICE_INTR_NOTIFY 100 #endif /* _MACH_DEVICE_NOTIFY_H_ */ diff --git a/linux/dev/arch/i386/kernel/irq.c.orig b/linux/dev/arch/i386/kernel/irq.c.orig deleted file mode 100644 index 75f8f812..00000000 --- a/linux/dev/arch/i386/kernel/irq.c.orig +++ /dev/null @@ -1,732 +0,0 @@ -/* - * Linux IRQ management. - * 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. - */ - -/* - * linux/arch/i386/kernel/irq.c - * - * Copyright (C) 1992 Linus Torvalds - */ - -#include <sys/types.h> -#include <mach/mach_types.h> -#include <mach/vm_param.h> -#include <kern/assert.h> - -#include <i386/spl.h> -#include <i386/pic.h> -#include <i386/pit.h> - -#define MACH_INCLUDE -#include <linux/mm.h> -#include <linux/interrupt.h> -#include <linux/ptrace.h> -#include <linux/delay.h> -#include <linux/kernel_stat.h> -#include <linux/malloc.h> -#include <linux/ioport.h> - -#include <asm/system.h> -#include <asm/bitops.h> -#include <asm/irq.h> -#include <asm/io.h> -#include <asm/hardirq.h> - -#include <linux/dev/glue/glue.h> -#include <machine/machspl.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. */ -unsigned int local_bh_count[NR_CPUS]; -unsigned int local_irq_count[NR_CPUS]; -#endif - -/* - * XXX Move this into more suitable place... - * Set if the machine has an EISA bus. - */ -int EISA_bus = 0; - -/* - * Flag indicating an interrupt is being handled. - */ -unsigned int intr_count = 0; - -/* - * List of Linux interrupt handlers. - */ -struct linux_action -{ - void (*handler) (int, void *, struct pt_regs *); - void *dev_id; - struct linux_action *next; - unsigned long flags; -}; - -static struct linux_action *irq_action[16] = -{ - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL -}; - -/* - * Generic interrupt handler for Linux devices. - * Set up a fake `struct pt_regs' then call the real handler. - */ -static void -linux_intr (int irq) -{ - struct pt_regs regs; - struct linux_action *action = *(irq_action + irq); - unsigned long flags; - - kstat.interrupts[irq]++; - intr_count++; - - save_flags (flags); - if (action && (action->flags & SA_INTERRUPT)) - cli (); - - while (action) - { - action->handler (irq, action->dev_id, ®s); - 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) - { - 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); - } -} - -void -disable_irq (unsigned int irq_nr) -{ - unsigned long flags; - - assert (irq_nr < NR_IRQS); - - save_flags (flags); - cli (); - 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 (); - unmask_irq (irq_nr); - restore_flags (flags); -} - -static int -setup_x86_irq (int irq, struct linux_action *new) -{ - int shared = 0; - struct linux_action *old, **p; - unsigned long flags; - - p = irq_action + irq; - if ((old = *p) != NULL) - { - /* Can't share interrupts unless both agree to */ - if (!(old->flags & new->flags & SA_SHIRQ)) - return (-EBUSY); - - /* Can't share interrupts unless both are same type */ - if ((old->flags ^ new->flags) & SA_INTERRUPT) - return (-EBUSY); - - /* add new interrupt at end of irq queue */ - do - { - p = &old->next; - old = *p; - } - while (old); - shared = 1; - } - - save_flags (flags); - cli (); - *p = new; - - if (!shared) - { - ivect[irq] = linux_intr; - iunit[irq] = irq; - unmask_irq (irq); - } - restore_flags (flags); - return 0; -} - -/* - * Attach a handler to an IRQ. - */ -int -request_irq (unsigned int irq, void (*handler) (int, void *, struct pt_regs *), - unsigned long flags, const char *device, void *dev_id) -{ - struct linux_action *action; - int retval; - - assert (irq < 16); - - if (!handler) - return -EINVAL; - - /* - * Hmm... Should I use `kalloc()' ? - * By OKUJI Yoshinori. - */ - action = (struct linux_action *) - linux_kmalloc (sizeof (struct linux_action), GFP_KERNEL); - if (action == NULL) - return -ENOMEM; - - action->handler = handler; - action->next = NULL; - action->dev_id = dev_id; - action->flags = flags; - - retval = setup_x86_irq (irq, action); - if (retval) - linux_kfree (action); - - return retval; -} - -/* - * Deallocate an irq. - */ -void -free_irq (unsigned int irq, void *dev_id) -{ - struct linux_action *action, **p; - unsigned long flags; - - if (irq > 15) - panic ("free_irq: bad irq number"); - - for (p = irq_action + irq; (action = *p) != NULL; p = &action->next) - { - if (action->dev_id != dev_id) - continue; - - save_flags (flags); - cli (); - *p = action->next; - if (!irq_action[irq]) - { - mask_irq (irq); - ivect[irq] = intnull; - iunit[irq] = irq; - } - restore_flags (flags); - linux_kfree (action); - return; - } - - panic ("free_irq: bad irq number"); -} - -/* - * Set for an irq probe. - */ -unsigned long -probe_irq_on (void) -{ - unsigned i, irqs = 0; - unsigned long delay; - - assert (curr_ipl == 0); - - /* - * Allocate all available IRQs. - */ - for (i = 15; i > 0; i--) - { - if (!irq_action[i] && ivect[i] == intnull) - { - enable_irq (i); - irqs |= 1 << i; - } - } - - /* - * Wait for spurious interrupts to mask themselves out. - */ - for (delay = jiffies + HZ / 10; delay > jiffies;) - ; - - return (irqs & ~curr_pic_mask); -} - -/* - * Return the result of an irq probe. - */ -int -probe_irq_off (unsigned long irqs) -{ - unsigned int i; - - assert (curr_ipl == 0); - - irqs &= curr_pic_mask; - - /* - * Disable unnecessary IRQs. - */ - for (i = 15; i > 0; i--) - { - if (!irq_action[i] && ivect[i] == intnull) - { - disable_irq (i); - } - } - - /* - * Return IRQ number. - */ - if (!irqs) - return 0; - i = ffz (~irqs); - if (irqs != (irqs & (1 << i))) - i = -i; - return i; -} - -/* - * Reserve IRQs used by Mach drivers. - * Must be called before Linux IRQ detection, after Mach IRQ detection. - */ - -static void reserved_mach_handler (int line, void *cookie, struct pt_regs *regs) -{ - /* These interrupts are actually handled in Mach. */ - assert (! "reached"); -} - -static const struct linux_action reserved_mach = - { - reserved_mach_handler, NULL, NULL, 0 - }; - -static void -reserve_mach_irqs (void) -{ - unsigned int i; - - for (i = 0; i < 16; i++) - { - if (ivect[i] != intnull) - /* This dummy action does not specify SA_SHIRQ, so - setup_x86_irq will not try to add a handler to this - slot. Therefore, the cast is safe. */ - irq_action[i] = (struct linux_action *) &reserved_mach; - } -} - -#ifdef __SMP__ -unsigned char global_irq_holder = NO_PROC_ID; -unsigned volatile int global_irq_lock; -atomic_t global_irq_count; - -atomic_t global_bh_count; -atomic_t global_bh_lock; - -/* - * "global_cli()" is a special case, in that it can hold the - * interrupts disabled for a longish time, and also because - * we may be doing TLB invalidates when holding the global - * IRQ lock for historical reasons. Thus we may need to check - * SMP invalidate events specially by hand here (but not in - * any normal spinlocks) - */ -#if 0 -/* XXX: check how Mach handles this */ -static inline void check_smp_invalidate(int cpu) -{ - if (test_bit(cpu, &smp_invalidate_needed)) { - clear_bit(cpu, &smp_invalidate_needed); - local_flush_tlb(); - } -} -#endif - -static void show(char * str) -{ - int i; - unsigned long *stack; - int cpu = smp_processor_id(); - - printk("\n%s, CPU %d:\n", str, cpu); - printk("irq: %d [%d %d]\n", - atomic_read(&global_irq_count), local_irq_count[0], local_irq_count[1]); - printk("bh: %d [%d %d]\n", - atomic_read(&global_bh_count), local_bh_count[0], local_bh_count[1]); - stack = (unsigned long *) &stack; - for (i = 40; i ; i--) { - unsigned long x = *++stack; - //if (x > (unsigned long) &get_options && x < (unsigned long) &vsprintf) { - printk("<[%08lx]> ", x); - //} - } -} - -#define MAXCOUNT 100000000 - -static inline void wait_on_bh(void) -{ - int count = MAXCOUNT; - do { - if (!--count) { - show("wait_on_bh"); - count = ~0; - } - /* nothing .. wait for the other bh's to go away */ - } while (atomic_read(&global_bh_count) != 0); -} - -/* - * I had a lockup scenario where a tight loop doing - * spin_unlock()/spin_lock() on CPU#1 was racing with - * spin_lock() on CPU#0. CPU#0 should have noticed spin_unlock(), but - * apparently the spin_unlock() information did not make it - * through to CPU#0 ... nasty, is this by design, do we have to limit - * 'memory update oscillation frequency' artificially like here? - * - * Such 'high frequency update' races can be avoided by careful design, but - * some of our major constructs like spinlocks use similar techniques, - * it would be nice to clarify this issue. Set this define to 0 if you - * want to check whether your system freezes. I suspect the delay done - * by SYNC_OTHER_CORES() is in correlation with 'snooping latency', but - * i thought that such things are guaranteed by design, since we use - * the 'LOCK' prefix. - */ -#define SUSPECTED_CPU_OR_CHIPSET_BUG_WORKAROUND 1 - -#if SUSPECTED_CPU_OR_CHIPSET_BUG_WORKAROUND -# define SYNC_OTHER_CORES(x) udelay(x+1) -#else -/* - * We have to allow irqs to arrive between __sti and __cli - */ -# define SYNC_OTHER_CORES(x) __asm__ __volatile__ ("nop") -#endif - -static inline void wait_on_irq(int cpu) -{ - int count = MAXCOUNT; - - for (;;) { - - /* - * Wait until all interrupts are gone. Wait - * for bottom half handlers unless we're - * already executing in one.. - */ - if (!atomic_read(&global_irq_count)) { - if (local_bh_count[cpu] || !atomic_read(&global_bh_count)) - break; - } - - /* Duh, we have to loop. Release the lock to avoid deadlocks */ - clear_bit(0,&global_irq_lock); - - for (;;) { - if (!--count) { - show("wait_on_irq"); - count = ~0; - } - __sti(); - SYNC_OTHER_CORES(cpu); - __cli(); - //check_smp_invalidate(cpu); - if (atomic_read(&global_irq_count)) - continue; - if (global_irq_lock) - continue; - if (!local_bh_count[cpu] && atomic_read(&global_bh_count)) - continue; - if (!test_and_set_bit(0,&global_irq_lock)) - break; - } - } -} - -/* - * This is called when we want to synchronize with - * bottom half handlers. We need to wait until - * no other CPU is executing any bottom half handler. - * - * Don't wait if we're already running in an interrupt - * context or are inside a bh handler. - */ -void synchronize_bh(void) -{ - if (atomic_read(&global_bh_count) && !in_interrupt()) - wait_on_bh(); -} - -/* - * This is called when we want to synchronize with - * interrupts. We may for example tell a device to - * stop sending interrupts: but to make sure there - * are no interrupts that are executing on another - * CPU we need to call this function. - */ -void synchronize_irq(void) -{ - if (atomic_read(&global_irq_count)) { - /* Stupid approach */ - cli(); - sti(); - } -} - -static inline void get_irqlock(int cpu) -{ - if (test_and_set_bit(0,&global_irq_lock)) { - /* do we already hold the lock? */ - if ((unsigned char) cpu == global_irq_holder) - return; - /* Uhhuh.. Somebody else got it. Wait.. */ - do { - do { - //check_smp_invalidate(cpu); - } while (test_bit(0,&global_irq_lock)); - } while (test_and_set_bit(0,&global_irq_lock)); - } - /* - * We also to make sure that nobody else is running - * in an interrupt context. - */ - wait_on_irq(cpu); - - /* - * Ok, finally.. - */ - global_irq_holder = cpu; -} - -#define EFLAGS_IF_SHIFT 9 - -/* - * A global "cli()" while in an interrupt context - * turns into just a local cli(). Interrupts - * should use spinlocks for the (very unlikely) - * case that they ever want to protect against - * each other. - * - * If we already have local interrupts disabled, - * this will not turn a local disable into a - * global one (problems with spinlocks: this makes - * save_flags+cli+sti usable inside a spinlock). - */ -void __global_cli(void) -{ - unsigned int flags; - - __save_flags(flags); - if (flags & (1 << EFLAGS_IF_SHIFT)) { - int cpu = smp_processor_id(); - __cli(); - if (!local_irq_count[cpu]) - get_irqlock(cpu); - } -} - -void __global_sti(void) -{ - int cpu = smp_processor_id(); - - if (!local_irq_count[cpu]) - release_irqlock(cpu); - __sti(); -} - -/* - * SMP flags value to restore to: - * 0 - global cli - * 1 - global sti - * 2 - local cli - * 3 - local sti - */ -unsigned long __global_save_flags(void) -{ - int retval; - int local_enabled; - unsigned long flags; - - __save_flags(flags); - local_enabled = (flags >> EFLAGS_IF_SHIFT) & 1; - /* default to local */ - retval = 2 + local_enabled; - - /* check for global flags if we're not in an interrupt */ - if (!local_irq_count[smp_processor_id()]) { - if (local_enabled) - retval = 1; - if (global_irq_holder == (unsigned char) smp_processor_id()) - retval = 0; - } - return retval; -} - -void __global_restore_flags(unsigned long flags) -{ - switch (flags) { - case 0: - __global_cli(); - break; - case 1: - __global_sti(); - break; - case 2: - __cli(); - break; - case 3: - __sti(); - break; - default: - printk("global_restore_flags: %08lx (%08lx)\n", - flags, (&flags)[-1]); - } -} - -#endif - -static void (*old_clock_handler) (); - -void -init_IRQ (void) -{ - char *p; - int latch = (CLKNUM + hz / 2) / hz; - - /* - * Ensure interrupts are disabled. - */ - (void) splhigh (); - - /* - * Program counter 0 of 8253 to interrupt hz times per second. - */ - outb_p (PIT_C0 | PIT_SQUAREMODE | PIT_READMODE, PITCTL_PORT); - outb_p (latch & 0xff, PITCTR0_PORT); - outb (latch >> 8, PITCTR0_PORT); - - /* - * Install our clock interrupt handler. - */ - old_clock_handler = ivect[0]; - ivect[0] = linux_timer_intr; - - reserve_mach_irqs (); - - /* - * Enable interrupts. - */ - (void) spl0 (); - - /* - * Check if the machine has an EISA bus. - */ - p = (char *) 0x0FFFD9; - if (*p++ == 'E' && *p++ == 'I' && *p++ == 'S' && *p == 'A') - EISA_bus = 1; - - /* - * Permanently allocate standard device ports. - */ - request_region (0x00, 0x20, "dma1"); - request_region (0x20, 0x20, "pic1"); - request_region (0x40, 0x20, "timer"); - request_region (0x70, 0x10, "rtc"); - request_region (0x80, 0x20, "dma page reg"); - request_region (0xa0, 0x20, "pic2"); - request_region (0xc0, 0x20, "dma2"); - request_region (0xf0, 0x10, "npu"); -} - -void -restore_IRQ (void) -{ - /* - * Disable interrupts. - */ - (void) splhigh (); - - /* - * Restore clock interrupt handler. - */ - ivect[0] = old_clock_handler; -} - |