Allow fast (1+us) user notification of device interrupts.  This allows
more powerful user I/O applications to be written.  The process of porting
to other architectures is straight forward and fully documented.  More
information can be found at http://oss.sgi.com/projects/uli/.

Signed-off-by: Michael A Raymond <[EMAIL PROTECTED]>

-- 
Michael A. Raymond              Office: (651) 683-3434
Core OS Group                   Real-Time System Software
diff -urN linux-2.6.11/arch/ia64/kernel/asm-offsets.c 
linux-2.6.11-uli/arch/ia64/kernel/asm-offsets.c
--- linux-2.6.11/arch/ia64/kernel/asm-offsets.c 2005-03-10 06:44:14.693503599 
-0600
+++ linux-2.6.11-uli/arch/ia64/kernel/asm-offsets.c     2005-03-10 
06:45:49.777264508 -0600
@@ -7,6 +7,7 @@
 #include <linux/config.h>
 
 #include <linux/sched.h>
+#include <linux/uli.h>
 
 #include <asm-ia64/processor.h>
 #include <asm-ia64/ptrace.h>
@@ -236,4 +237,19 @@
        DEFINE(IA64_TIME_SOURCE_MMIO64, TIME_SOURCE_MMIO64);
        DEFINE(IA64_TIME_SOURCE_MMIO32, TIME_SOURCE_MMIO32);
        DEFINE(IA64_TIMESPEC_TV_NSEC_OFFSET, offsetof (struct timespec, 
tv_nsec));
+
+#ifdef CONFIG_ULI
+       BLANK();
+       DEFINE(ULI_GP_OFFSET, offsetof(struct uli, uli_gp));
+       DEFINE(ULI_SP_OFFSET, offsetof(struct uli, uli_sp));
+       DEFINE(ULI_BSPSTORE_OFFSET, offsetof(struct uli, uli_arch0));
+       DEFINE(ULI_TP_OFFSET, offsetof(struct uli, uli_arch1));
+       DEFINE(ULI_PC_OFFSET, offsetof(struct uli, uli_pc));
+       DEFINE(ULI_ARG_OFFSET, offsetof(struct uli, uli_funcarg));
+       DEFINE(ULI_TSTAMP_OFFSET, offsetof(struct uli, uli_tstamp));
+       DEFINE(ULI_DOUBLE_OFFSET, offsetof(struct uli, uli_double));
+       DEFINE(ULI_INTR_SP_OFFSET, offsetof(struct uli, uli_intr_sp));
+       DEFINE(ULI_INTR_BSPSTORE_OFFSET, offsetof(struct uli, uli_intr_arch0));
+       DEFINE(ULI_SAVED_EPC_OFFSET, offsetof(struct uli, uli_saved_epc));
+#endif /* CONFIG_ULI */
 }
diff -urN linux-2.6.11/arch/ia64/kernel/entry.S 
linux-2.6.11-uli/arch/ia64/kernel/entry.S
--- linux-2.6.11/arch/ia64/kernel/entry.S       2005-03-02 01:37:50.000000000 
-0600
+++ linux-2.6.11-uli/arch/ia64/kernel/entry.S   2005-03-22 08:41:17.597377762 
-0600
@@ -698,6 +698,16 @@
 (pUStk)        cmp.eq.unc p6,p0=r0,r0          // p6 <- pUStk
 #endif
 .work_processed_syscall:
+#ifdef CONFIG_ULI
+       addl r22 = THIS_CPU(uli_cur),r0 // uli_cur
+       ;;
+       ld8 r22 = [r22] // load current ULI
+       ;;
+(p6)   cmp.eq p6,p0=r0,r22 // If would do work && no ULI, then can still check
+(pUStk) cmp.eq.unc p7,p0=r0,r22 // Don't store to on_ustack if there's a ULI
+#else /* CONFIG_ULI */
+(pUStk) cmp.eq.unc p7,p0=r0,r0
+#endif /* CONFIG_ULI */
        adds r2=PT(LOADRS)+16,r12
        adds r3=PT(AR_BSPSTORE)+16,r12
        adds r18=TI_FLAGS+IA64_TASK_SIZE,r13
@@ -760,7 +770,7 @@
        addl r3=THIS_CPU(ia64_phys_stacked_size_p8),r0
        ;;
 (pUStk)        ld4 r3=[r3]             // r3 = cpu_data->phys_stacked_size_p8
-(pUStk) st1 [r14]=r17
+(p7)   st1 [r14]=r17
        mov b6=r18              // I0  restore b6
        ;;
        mov r14=r0              // clear r14
@@ -814,6 +824,14 @@
 (pUStk)        cmp.eq.unc p6,p0=r0,r0          // p6 <- pUStk
 #endif
 .work_processed_kernel:
+#ifdef CONFIG_ULI
+       addl r22 = THIS_CPU(uli_cur),r0 // uli_cur
+       ;;
+       ld8 r22 = [r22] // load current ULI
+       ;;
+(p6)   cmp.eq p6,p0=r0,r22 // If would do work && no ULI, then can still check
+       cmp.eq p9,p7=r0,r22 // Set p7 if there's a current ULI
+#endif /* CONFIG_ULI */
        adds r17=TI_FLAGS+IA64_TASK_SIZE,r13
        ;;
 (p6)   ld4 r31=[r17]                           // load 
current_thread_info()->flags
@@ -842,13 +860,44 @@
        ;;
        ld8 r31=[r2],16         // load ar.ssd
        ld8.fill r8=[r3],16
+#ifdef CONFIG_ULI
+(p7)   add r19 = ULI_DOUBLE_OFFSET,r22 /* &cur_uli->uli_double */
+#endif /* CONFIG_ULI */
        ;;
        ld8.fill r9=[r2],16
        ld8.fill r10=[r3],PT(R17)-PT(R10)
+#ifdef CONFIG_ULI
+(p7)    add r18 = ULI_TSTAMP_OFFSET,r22
+#endif /* CONFIG_ULI */
        ;;
        ld8.fill r11=[r2],PT(R18)-PT(R11)
        ld8.fill r17=[r3],16
+(pUStk) cmp.eq.unc p6,p0=r0,r0 /* p6 was false, set to pUStk */
+       ;;
+#ifdef CONFIG_ULI
+/* We need to check if the current ULI has been running too long */
+(p9)   br.cond.sptk.many 1f /* No current ULI, skip the check */
+       ;;
+       ld4 r22=[r19] /* *cur_uli->uli_double, # of double intrs  */
+       ld8 r18=[r18] /* *tstamp */
+       cmp.eq p0,p6=r0,r0 /* If there's a current ULI, p6 becomes false */
+       ;;
+       mov r23 = ar.itc /* Get the current time */
+       sub r22=r22,r0,1 /* One less nested level above ULI handler */
+       ;;
+       cmp.eq p8,p9=r22,r0 /* Returning into a handler? */
+       st4 [r19] = r22 /* store lower # of doubles */
+       ;;
+(p8)   cmp.ge p0,p9=r23,r18 /* Returning into a handler && overrun? */
+       ;;
+(p9)   br.cond.sptk.many 1f /* No overrun, continue exiting */
+       alloc r0=ar.pfs,0,0,2,0 /* There's been an overrun, abort the ULI */
        ;;
+       mov out0 = 24 /* SIGXCPU */
+       mov out1 = r0 /* &pt_regs */
+       br.call.spnt.few b6=uli_return /* Go to the start pt, this won't ret */
+1:
+#endif /* CONFIG_ULI */
        ld8.fill r18=[r2],16
        ld8.fill r19=[r3],16
        ;;
@@ -930,7 +979,7 @@
 (pUStk)        mov r17=1
        ;;
        ld8.fill r3=[r16]
-(pUStk)        st1 [r18]=r17           // restore current->thread.on_ustack
+(p6)   st1 [r18]=r17           // restore current->thread.on_ustack
        shr.u r18=r19,16        // get byte size of existing "dirty" partition
        ;;
        mov r16=ar.bsp          // get existing backing store pointer
diff -urN linux-2.6.11/arch/ia64/kernel/fsys.S 
linux-2.6.11-uli/arch/ia64/kernel/fsys.S
--- linux-2.6.11/arch/ia64/kernel/fsys.S        2005-03-02 01:38:34.000000000 
-0600
+++ linux-2.6.11-uli/arch/ia64/kernel/fsys.S    2005-03-22 08:45:29.832958277 
-0600
@@ -579,6 +579,9 @@
        mov psr.l=r9                    // slam the door (17 cyc to srlz.i)
        or r29=r8,r29                   // construct cr.ipsr value to save
        addl r22=IA64_RBS_OFFSET,r2     // compute base of RBS
+#ifdef CONFIG_ULI
+       addl r17 = THIS_CPU(uli_cur),r0 // &uli_cur[this cpu]
+#endif /* CONFIG_ULI */
        ;;
        // GAS reports a spurious RAW hazard on the read of ar.rnat because it 
thinks
        // we may be reading ar.itc after writing to psr.l.  Avoid that message 
with
@@ -587,6 +590,9 @@
        mov.m r24=ar.rnat               // read ar.rnat (5 cyc lat)
        lfetch.fault.excl.nt1 [r22]
        adds r16=IA64_TASK_THREAD_ON_USTACK_OFFSET,r2
+#ifdef CONFIG_ULI
+       ld8 r3 = [r17] // Get the address of the current ULI
+#endif /* CONFIG_ULI */
 
        // ensure previous insn group is issued before we stall for srlz.i:
        ;;
@@ -595,6 +601,14 @@
        ////////// from this point on, execution is not interruptible anymore
        
/////////////////////////////////////////////////////////////////////////////
        addl r1=IA64_STK_OFFSET-IA64_PT_REGS_SIZE,r2    // compute base of 
memory stack
+#ifdef CONFIG_ULI
+       cmp.eq p7,p20=r0,r3      // ULI?
+       add r17 = ULI_INTR_SP_OFFSET, r3 // Former stack pointer
+       add r30 = ULI_INTR_BSPSTORE_OFFSET, r3   // Former BSP
+       ;;
+(p20)  ld8 r1 = [r17] // sp
+(p20)  ld8 r22 = [r30] // bspstore
+#endif /* CONFIG_ULI */
        cmp.ne pKStk,pUStk=r0,r0        // set pKStk <- 0, pUStk <- 1
        ;;
        st1 [r16]=r0                    // clear current->thread.on_ustack flag
@@ -613,6 +627,9 @@
        mov rp=r2                               // set the real return addr
        tbit.z p8,p0=r3,TIF_SYSCALL_TRACE
        ;;
+#ifdef CONFIG_ULI
+(p20)  br.call.spnt.few b6=uli_syscall
+#endif /* CONFIG_ULI */
 (p10)  br.cond.spnt.many ia64_ret_from_syscall // p10==true means out 
registers are more than 8
 (p8)   br.call.sptk.many b6=b6         // ignore this return addr
        br.cond.sptk ia64_trace_syscall
diff -urN linux-2.6.11/arch/ia64/kernel/ivt.S 
linux-2.6.11-uli/arch/ia64/kernel/ivt.S
--- linux-2.6.11/arch/ia64/kernel/ivt.S 2005-03-02 01:37:49.000000000 -0600
+++ linux-2.6.11-uli/arch/ia64/kernel/ivt.S     2005-03-21 08:53:52.611672980 
-0600
@@ -704,7 +704,21 @@
        cmp.eq p0,p7=r18,r17                    // is this a system call? (p7 
<- false, if so)
 (p7)   br.cond.spnt non_syscall
        ;;
+#ifdef CONFIG_ULI
+       /*
+        * If we're in a ULI doing a system call, we need to start on the stack
+        * where we switched out to handle the ULI.
+        */
+       addl r17 = THIS_CPU(uli_cur),r0  // &uli_cur[this cpu]
+       ;;
+       ld8 r22 = [r17]                  // r22 = current ULI, MINSTATE expects 
r22
+       ;;
+       cmp.eq pUStk,pLvSys=r0,r22          // ULI?  MINSTATE expects a valid 
pLvSys
+       ;;
+(pUStk) ld1 r17=[r16]           // load current->thread.on_ustack flag
+#else /* CONFIG_ULI */
        ld1 r17=[r16]                           // load 
current->thread.on_ustack flag
+#endif /* CONFIG_ULI */
        st1 [r16]=r0                            // clear 
current->thread.on_ustack flag
        add r1=-IA64_TASK_THREAD_ON_USTACK_OFFSET,r16   // set r1 for 
MINSTATE_START_SAVE_MIN_VIRT
        ;;
@@ -721,6 +735,7 @@
 (p6)   adds r28=16,r28                         // switch cr.iip to next bundle 
cr.ipsr.ei wrapped
 (p7)   adds r8=1,r8                            // increment ei to next slot
        ;;
+       /* If we're in a ULI then r17 will be non-zero and we'll get pUStk */
        cmp.eq pKStk,pUStk=r0,r17               // are we in kernel mode 
already?
        dep r29=r8,r29,41,2                     // insert new ei into cr.ipsr
        ;;
@@ -760,6 +775,9 @@
        cmp.eq p8,p0=r2,r0
        mov b6=r20
        ;;
+#ifdef CONFIG_ULI
+(pLvSys) br.call.spnt.few b6=uli_syscall
+#endif /* CONFIG_ULI */
 (p8)   br.call.sptk.many b6=b6                 // ignore this return addr
        br.cond.sptk ia64_trace_syscall
        // NOT REACHED
diff -urN linux-2.6.11/arch/ia64/kernel/Makefile 
linux-2.6.11-uli/arch/ia64/kernel/Makefile
--- linux-2.6.11/arch/ia64/kernel/Makefile      2005-03-10 06:44:22.122120706 
-0600
+++ linux-2.6.11-uli/arch/ia64/kernel/Makefile  2005-03-10 06:47:33.250570322 
-0600
@@ -21,6 +21,7 @@
 obj-$(CONFIG_IA64_CYCLONE)     += cyclone.o
 obj-$(CONFIG_IA64_MCA_RECOVERY)        += mca_recovery.o
 mca_recovery-y                 += mca_drv.o mca_drv_asm.o
+obj-$(CONFIG_ULI)              += uli_asm.o
 
 # The gate DSO image is built using a special linker script.
 targets += gate.so gate-syms.o
diff -urN linux-2.6.11/arch/ia64/kernel/minstate.h 
linux-2.6.11-uli/arch/ia64/kernel/minstate.h
--- linux-2.6.11/arch/ia64/kernel/minstate.h    2005-03-02 01:38:25.000000000 
-0600
+++ linux-2.6.11-uli/arch/ia64/kernel/minstate.h        2005-03-18 
15:31:09.001118433 -0600
@@ -4,15 +4,41 @@
 
 #include "entry.h"
 
+#ifdef CONFIG_ULI
+#include <asm/percpu.h>
+
+#define MINSTATE_LOAD_OFFSETS \
+(pLvSys)       add r17 = ULI_INTR_SP_OFFSET, r22;      /* prev sp - 
size(pt_regs) */ \
+(pLvSys)       add r18 = ULI_INTR_BSPSTORE_OFFSET, r22;        /* previous 
bspstore */
+
+#define MINSTATE_LOAD_STACK \
+(pLvSys)       ld8 r1 = [r17]; /* sp is prev mod'd sp */ \
+(pLvSys)       ld8 r22 = [r18];        /* bspstore is prev bspstore */
+
+# else /* CONFIG_ULI */
+
+#define MINSTATE_LOAD_OFFSETS
+#define MINSTATE_LOAD_STACK
+
+#endif /* CONFIG_ULI */
+
 /*
  * For ivt.s we want to access the stack virtually so we don't have to disable 
translation
  * on interrupts.
  *
  *  On entry:
  *     r1:     pointer to current task (ar.k6)
+ *
+ * For ULI code:
+ * If we came from user space, pUStk is true.
+ * If there is no current ULI and we took this trap while in the kernel,
+ *   OR if we're well above a ULI, pKStk is true.
+ * If we're right on top of a ULI, pLvSys is true and we use it to override
+ *   some of pUStk's values to get the appropriate kernel stacks.
  */
 #define MINSTATE_START_SAVE_MIN_VIRT                                           
                \
 (pUStk)        mov ar.rsc=0;           /* set enforced lazy mode, pl 0, 
little-endian, loadrs=0 */     \
+MINSTATE_LOAD_OFFSETS \
        ;;                                                                      
                \
 (pUStk)        mov.m r24=ar.rnat;                                              
                        \
 (pUStk)        addl r22=IA64_RBS_OFFSET,r1;                    /* compute base 
of RBS */               \
@@ -25,6 +51,8 @@
 (pUStk)        mov ar.bspstore=r22;                            /* switch to 
kernel RBS */              \
 (pKStk) addl r1=-IA64_PT_REGS_SIZE,r1;                 /* if in kernel mode, 
use sp (r12) */   \
        ;;                                                                      
                \
+MINSTATE_LOAD_STACK \
+       ;;      \
 (pUStk)        mov r18=ar.bsp;                                                 
                        \
 (pUStk)        mov ar.rsc=0x3;         /* set eager mode, pl 0, little-endian, 
loadrs=0 */             \
 
@@ -61,7 +89,34 @@
        ;;
 
 #ifdef MINSTATE_VIRT
+#ifdef CONFIG_ULI
+
+/*
+ * We leave with pUStk true if we're not on a ULI OR we're well above one.
+ * We leave with pKStk true if there's a current ULI
+ * We leave with PLvSys true if we're the first above a ULI.
+ */
+#define MINSTATE_GET_CURRENT(reg) \
+       addl r20 = THIS_CPU(uli_cur),r0;        \
+       ;;      \
+       ld8 r22 = [r20]; /* r22 = cur_uli, needed by START_SAVE_MIN_VIRT */     
\
+       ;;      \
+       cmp.eq pUStk,pKStk=r0,r22;      /* cur == NULL? */      \
+       add r20 = ULI_DOUBLE_OFFSET,r22;        /* r20 = &cur_uli.uli_double */ 
\
+       ;;      \
+(pKStk)        ld4 r21 = [r20]; /* load # of doubles */        \
+(pUStk)        cmp.eq pUStk,pLvSys=r0,r0; /* init pLvSys to false */   \
+       ;;      \
+(pKStk)        cmp.eq pLvSys,pUStk=r0,r21; /* First nested intr? */    \
+       ;; \
+       mov reg=IA64_KR(CURRENT); /* If normal, use actual current */   \
+(pKStk)        add r21 = 1,r21; /* Increment the nested count */       \
+       ;; \
+(pKStk)        st4 [r20] = r21; /* Mark that we're above a ULI */
+
+#else /* CONFIG_ULI */
 # define MINSTATE_GET_CURRENT(reg)     mov reg=IA64_KR(CURRENT)
+#endif /* CONFIG_ULI */
 # define MINSTATE_START_SAVE_MIN       MINSTATE_START_SAVE_MIN_VIRT
 # define MINSTATE_END_SAVE_MIN         MINSTATE_END_SAVE_MIN_VIRT
 #endif
@@ -72,6 +127,21 @@
 # define MINSTATE_END_SAVE_MIN         MINSTATE_END_SAVE_MIN_PHYS
 #endif
 
+#if defined(CONFIG_ULI) && !defined(MINSTATE_PHYS)
+/*
+ * pLvSys is true if we're right on top of a ULI
+ * pUStk is true if (we're not in a ULI) OR (well above a ULI)
+ * pLvSys and pUStk are opposites
+ */
+#define CHECK_USR \
+(pLvSys) mov r17 = 0x1; /* Pretend we're on the user stack */ \
+(pUStk) ld1 r17=[r16];                         /* load 
current->thread.on_ustack flag */
+
+#else /* CONFIG_ULI */
+#define CHECK_USR \
+       ld1 r17=[r16];                          /* load 
current->thread.on_ustack flag */
+#endif /* CONFIG_ULI */
+
 /*
  * DO_SAVE_MIN switches to the kernel stacks (if necessary) and saves
  * the minimum state necessary that allows us to turn psr.ic back
@@ -110,7 +180,7 @@
        ;;                                                                      
                \
        adds r16=IA64_TASK_THREAD_ON_USTACK_OFFSET,r16;                         
                \
        ;;                                                                      
                \
-       ld1 r17=[r16];                          /* load 
current->thread.on_ustack flag */       \
+       CHECK_USR;      \
        st1 [r16]=r0;                           /* clear 
current->thread.on_ustack flag */      \
        adds r1=-IA64_TASK_THREAD_ON_USTACK_OFFSET,r16                          
                \
        /* switch from user to kernel RBS: */                                   
                \
diff -urN linux-2.6.11/arch/ia64/kernel/traps.c 
linux-2.6.11-uli/arch/ia64/kernel/traps.c
--- linux-2.6.11/arch/ia64/kernel/traps.c       2005-03-02 01:38:26.000000000 
-0600
+++ linux-2.6.11-uli/arch/ia64/kernel/traps.c   2005-03-10 06:45:50.655183125 
-0600
@@ -15,6 +15,7 @@
 #include <linux/vt_kern.h>             /* For unblank_screen() */
 #include <linux/module.h>       /* for EXPORT_SYMBOL */
 #include <linux/hardirq.h>
+#include <linux/uli.h>
 
 #include <asm/fpswa.h>
 #include <asm/ia32.h>
@@ -181,6 +182,10 @@
                        sig = SIGTRAP; code = TRAP_BRKPT;
                }
        }
+
+       /* If a ULI caused this, abort it */
+       uli_trap(sig, regs);
+
        siginfo.si_signo = sig;
        siginfo.si_errno = 0;
        siginfo.si_code = code;
@@ -375,6 +380,8 @@
                        return rv;
        }
 #endif
+       /* If a ULI caused this abort it */
+       uli_trap(SIGILL, &regs);
 
        sprintf(buf, "IA-64 Illegal operation fault");
        die_if_kernel(buf, &regs, 0);
@@ -408,6 +415,9 @@
                "Unknown fault 13", "Unknown fault 14", "Unknown fault 15"
        };
 
+       /* If a ULI caused this abort it */
+       uli_trap(SIGILL, &regs);
+
        if ((isr & IA64_ISR_NA) && ((isr & IA64_ISR_CODE_MASK) == 
IA64_ISR_CODE_LFETCH)) {
                /*
                 * This fault was due to lfetch.fault, set "ed" bit in the psr 
to cancel
diff -urN linux-2.6.11/arch/ia64/kernel/uli_asm.S 
linux-2.6.11-uli/arch/ia64/kernel/uli_asm.S
--- linux-2.6.11/arch/ia64/kernel/uli_asm.S     1969-12-31 18:00:00.000000000 
-0600
+++ linux-2.6.11-uli/arch/ia64/kernel/uli_asm.S 2005-03-21 08:51:01.522668631 
-0600
@@ -0,0 +1,244 @@
+/*
+ * Copyright (c) 2005 Silicon Graphics, Inc.
+ * All rights reserved.
+ *
+ *   Michael A. Raymond <[EMAIL PROTECTED]>
+ */
+
+/*
+ *  This file is part of the User Level Interrupt (ULI) feature.
+ *
+ *  ULI 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.
+ *
+ *  ULI 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 ULI; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+#include <asm/asmmacro.h>
+#include <asm/offsets.h>
+
+/*
+ * We save the current state of the CPU and jump into
+ * uli_setup_eret.
+ */
+GLOBAL_ENTRY(uli_goto_user)
+       alloc r14=ar.pfs,1,0,1,0
+       flushrs
+       ;;
+       mov r2=in0 // Index into ULI context save area
+       add r3=8,in0 // Ditto
+       mov r24=pr // Save the predicate register
+       mov r8=ar.unat
+       mov r15=ar.fpsr // Save the FP status reg since the user might modify it
+       mov r9=in0 // Use ULI storage spot for UNAT calc
+       mov r23=ar.bsp // Get the BSP return it
+       mov r16=rp      // b0
+       mov r17=b1
+       ;;
+.mem.offset 0,0;       st8.spill.nta [r2]=gp,16        // r1 (gp)
+.mem.offset 8,0;       st8.spill.nta [r3]=r4,16        // r4
+       mov r18=b2
+       ;;
+.mem.offset 0,0;       st8.spill.nta [r2]=r5,16        // r5
+.mem.offset 8,0;       st8.spill.nta [r3]=r6,16        // r6
+       extr.u r9=r9,3,6 // Start calculating post spill ar.unat
+       ;;
+.mem.offset 0,0;       st8.spill.nta [r2]=r7,16        // r7
+.mem.offset 8,0;       st8.spill.nta [r3]=sp,16        // r12 (sp)
+       sub r10=64,r9
+       ;;
+       mov ar.rsc = 0 // Put in lazy mode so we can read the folowing ar's
+       mov r25=ar.unat // Get UNAT after spill
+       mov out0=in0 // Pass on the ULI to use
+       ;;
+       mov r22=ar.rnat
+       mov ar.rsc = 3 // Return to regular kernel mode
+       shr.u r11=r25,r9
+       ;;
+       mov ar.unat=r8  // Restore the original UNAT after gr spill
+       ;;
+       st8.nta [r2]=r8,16              // save caller's unat
+       st8.nta [r3]=r15,16             // save fpsr
+       mov r19=b3
+       ;; 
+       st8.nta [r2]=r16,16             // b0
+       st8.nta [r3]=r17,16             // b1
+       mov r20=b4
+       ;;
+       st8.nta [r2]=r18,16             // b2
+       st8.nta [r3]=r19,16             // b3
+       mov r21=b5
+       ;; 
+       st8.nta [r2]=r20,16             // b4
+       st8.nta [r3]=r21,16             // b5
+       shl r25=r25,r10
+       ;; 
+       st8.nta [r2]=r14,16             // ar.pfs
+       st8.nta [r3]=r22,16             // ar.rnat
+       or r25=r25,r11 // Arive at post spill ar.unat
+       ;;
+       st8.nta [r2]=r24,16             // pr
+       st8.nta [r3]=r23,16             // ar.bsp
+       mov r26=ar.lc
+       ;;
+       st8.nta [r2]=r25                // ar.unat - post save
+       st8.nta [r3]=r26                // ar.lc
+       ;;
+       br.call.sptk.many b6=uli_setup_eret
+       /*NOTREACHED*/
+END(uli_goto_user)
+
+/*
+ * Longjmp back to the state saved in uli_goto_user.  From there
+ * return to uli_goto_user's calling function.
+ */
+GLOBAL_ENTRY(uli_return_from_user)
+       alloc r8=ar.pfs,1,0,0,0
+       invala                  // virt. -> phys. regnum mapping may change
+       add r4=0x90,in0 // &post unat
+       ;;
+       ld8 r11=[r4]    // load post unat
+       mov r27=ar.rsc   // Save this for restoring later
+       extr.u r9=in0, 3, 6      // Start converting the ar.unat to use
+       ;;
+       sub r8 = 64, r9
+       shl r10 = r11, r9
+       mov r2=in0
+       ;;
+       shr.u r11=r11, r8
+       add r3=8,in0            // r3 <- &jmpbuf.r4
+       ;;
+       or r11 = r11, r10        // Complete converting the ar.unat
+       ;;
+       mov ar.unat = r11
+       add r8 = ULI_SAVED_EPC_OFFSET, in0
+       ;;
+       ld8.fill.nta gp=[r2],16 // r1 (gp)
+       ld8.fill.nta r4=[r3],16         // r4
+       ;;
+       ld8.fill.nta r5=[r2],16 // r5
+       ld8.fill.nta r6=[r3],16         // r6
+       ;;
+       ld8.fill.nta r7=[r2],16 // r7
+       ld8.fill.nta sp=[r3],16         // r12 (sp)
+       ;;
+       ld8.nta r28=[r2],16             // caller's unat
+       ld8.nta r29=[r3],16             // fpsr
+       ;;
+       ld8.nta r16=[r2],16             // b0
+       ld8.nta r17=[r3],16             // b1
+       ;;
+       ld8.nta r18=[r2],16             // b2
+       ld8.nta r19=[r3],16             // b3
+       mov b0=r16
+       ;;
+       ld8.nta r20=[r2],16             // b4
+       ld8.nta r21=[r3],16             // b5
+       mov b1=r17
+       ;;
+       ld8.nta r10=[r2],16             // ar.pfs
+       ld8.nta r22=[r3],16             // ar.rnat
+       mov b2=r18
+       ;;
+       ld8.nta r24=[r2],16             // pr
+       ld8.nta r23=[r3],16             // ar.bsp
+       mov b3=r19
+       ;;
+       ld8.nta r26=[r3]                // ar.lc
+       ld8 r8=[r8]                 // cr.iip
+       mov b4=r20
+       ;;
+       rsm psr.ic | psr.i      // Allow cr.iip to be set later
+       mov ar.pfs=r10
+       mov b5=r21
+       ;;
+       srlz.d   // Make sure the disabling of interrupts is seen
+       mov ar.fpsr=r29                 // restore fpsr
+       ;;
+       mov ar.rsc=0            // Put RSE into lazy mode
+       loadrs
+       ;;
+       mov cr.iip = r8         // restored originally preempted PC
+       mov ar.lc = r26
+       ;;
+       mov.m ar.bspstore=r23   // restore ar.bspstore
+       mov.m ar.unat=r28                       // restore caller's unat
+       ;;
+       mov.m ar.rnat=r22               // restore ar.rnat
+       mov.m ar.rsc=r27                // restore ar.rsc
+       ;;
+       ssm psr.ic | psr.i       // Reenable interrupts
+       ;;
+       srlz.d   // Make sure that the above enabling of interrupts is seen
+       mov pr=r24,-1
+       br.ret.sptk.many rp
+END(uli_return_from_user)
+
+/*
+ * This is called from uli_setup_eret to do an rfi into the owning process's
+ * ULI handler.  We have to:
+ * - Save the actual current's PC when this interrupt occured
+ * - Save the actual current's sp & bspstore when this interrupt occured
+ * - Set the pc, sp, bspstore, gp, etc
+ * - Reenable interrupts on our way out
+ */
+GLOBAL_ENTRY(uli_eret)
+       mov r20 = ar.bsp // Save for bspstore loading on doubly nested intrs
+       alloc r21=ar.pfs,2,0,0,0 // We have two inputs
+       mov r22 = IA64_PT_REGS_SIZE // later subtraction wants general regs
+       ;;
+       flushrs // Done after the alloc, allow switching of the bspstore
+       movl r21 = 0x8000000000000000 // valid & empty frame marker
+       ;;
+       sub r22 = sp, r22 // sp - PT_REGS Make's handling nested intrs easier
+       rsm psr.ic // Turn off so that we can change cr.ifs
+       add r17 = ULI_INTR_SP_OFFSET, in0 // &uli_intr_sp
+       ;;
+       srlz.d // make sure everyone has seen the status change
+       st8 [r17] = r22 // save mod'd sp for use in doubly nested interrupts
+       add r18 = ULI_INTR_BSPSTORE_OFFSET, in0 // &uli_intr_bspstore
+       ;;
+       mov cr.ifs = r21 // Initialize their current frame marker
+       st8 [r18] = r20 // save parent's bspstore
+       add r17 = ULI_PC_OFFSET, in0 // &uli_pc
+       ;;
+       mov r22 = cr.iip // Get PC of when we took this interrupt
+       ld8 r21 = [r17] // Load ULI handler's PC
+       add r18 = ULI_SAVED_EPC_OFFSET, in0 // &uli_epc
+       ;;
+       mov cr.iip = r21 // rfi will load this as handler's PC
+       mov cr.ipsr = in1 // User space status register values to use
+       add r19 = ULI_SP_OFFSET, in0 // &uli_sp
+       ;;
+       st8 [r18] = r22 // Save PC of when we took this interrupt
+       ld8 r12 = [r19] // Set the user's sp
+       add r20 = ULI_GP_OFFSET, in0 // &uli_gp
+       ;;
+       ld8 r1 = [r20] // Set the user's gp
+       add r17 = ULI_ARG_OFFSET, in0 // &uli_funcarg
+       add r18 = ULI_BSPSTORE_OFFSET,in0 // &uli_bspstore
+       ;; 
+       ld8 r20 = [r17] // Load the argument for the ULI handler
+       ld8 r21 = [r18] // Load user's bspstore to use
+       add r19 = ULI_TP_OFFSET,in0 // &uli_threadp
+       ;;
+       mov ar.rsc = 0 // lazy mode for bspstore switch
+       invala // virtual->physical mapping changed, invalidate the ALAT
+       ;;
+       mov ar.bspstore = r21 // Set the user's bspstore
+       mov ar.rsc = 0xf // standard user mode
+       mov r32 = r20 // handler's argument
+       ;;
+       ssm psr.i | psr.ic // reenable interrupts
+       ld8 r13 = [r19] // Set the user's thread pointer
+       ;;
+       rfi
+END(uli_eret)
diff -urN linux-2.6.11/arch/ia64/mm/fault.c 
linux-2.6.11-uli/arch/ia64/mm/fault.c
--- linux-2.6.11/arch/ia64/mm/fault.c   2005-03-02 01:38:32.000000000 -0600
+++ linux-2.6.11-uli/arch/ia64/mm/fault.c       2005-03-10 06:45:51.190332627 
-0600
@@ -9,6 +9,7 @@
 #include <linux/mm.h>
 #include <linux/smp_lock.h>
 #include <linux/interrupt.h>
+#include <linux/uli.h>
 
 #include <asm/pgtable.h>
 #include <asm/processor.h>
@@ -84,6 +85,9 @@
        struct siginfo si;
        unsigned long mask;
 
+       /* If a ULI caused this abort it */
+       uli_trap(SIGSEGV, regs);
+
        /*
         * If we're in an interrupt or have no user context, we must not take 
the fault..
         */
diff -urN linux-2.6.11/drivers/base/Kconfig 
linux-2.6.11-uli/drivers/base/Kconfig
--- linux-2.6.11/drivers/base/Kconfig   2005-03-02 01:37:49.000000000 -0600
+++ linux-2.6.11-uli/drivers/base/Kconfig       2005-03-10 06:45:51.386619214 
-0600
@@ -37,4 +37,14 @@
 
          If you are unsure about this, say N here.
 
+config ULI
+       bool "User Level Interrupt Support"
+       depends on IA64 && EXPERIMENTAL
+       default n
+       help
+         This enables hardware interrupts to be handled by software in
+         user space.  This gives user processes more control over hardware
+         and can enable driver development with reduced risk to system
+         stability.
+
 endmenu
diff -urN linux-2.6.11/drivers/base/Makefile 
linux-2.6.11-uli/drivers/base/Makefile
--- linux-2.6.11/drivers/base/Makefile  2005-03-02 01:38:20.000000000 -0600
+++ linux-2.6.11-uli/drivers/base/Makefile      2005-03-10 06:45:51.631733311 
-0600
@@ -7,6 +7,7 @@
 obj-y                  += power/
 obj-$(CONFIG_FW_LOADER)        += firmware_class.o
 obj-$(CONFIG_NUMA)     += node.o
+obj-$(CONFIG_ULI)      += uli.o
 
 ifeq ($(CONFIG_DEBUG_DRIVER),y)
 EXTRA_CFLAGS += -DDEBUG
diff -urN linux-2.6.11/drivers/base/uli.c linux-2.6.11-uli/drivers/base/uli.c
--- linux-2.6.11/drivers/base/uli.c     1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.11-uli/drivers/base/uli.c 2005-03-23 09:13:31.137340683 -0600
@@ -0,0 +1,687 @@
+/*
+ * Copyright (c) 2005 Silicon Graphics, Inc.
+ * All rights reserved.
+ *
+ *   Michael A. Raymond <[EMAIL PROTECTED]>
+ */
+
+/*
+ *  This file is part of the User Level Interrupt (ULI) feature.
+ *
+ *  ULI 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.
+ *
+ *  ULI 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 ULI; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+/*
+ *   User Level Interrupts (ULIs) are a method of handling interrupts in
+ * user space that would normally be entirely handled within the kernel.
+ * They are useful for fault tolerant interrupt handling code and for
+ * programs that do a lot of memory mapped I/O.
+ *
+ *   ULIs work by doing a return-from-interrupt (RFI) from the kernel
+ * interrupt handler to a user space function specified by the user.  The
+ * code is run entirely in user space and so faults can be contained and
+ * it can access all of the user's address space.  The only limitation is
+ * that system calls cannot be made from the handler function.  The
+ * supporting library, libuli, does use system calls to return to the
+ * kernel, but this is the only allowed or possible usage.
+ *
+ *                                                  -------
+ *      --------         ------------------------  | User  |
+ *     |Shared  |       | User        -------- <-|-|Process|
+ *     |Memory /|       |Process <-->|  ULI   |  |  -------
+ *     |Mapped  |<------|----------->|Function|  |      -------
+ *     |Memory  |       |             -------- <-|-----| User  |
+ *      --------        |Memory Access   A       |     |Process|
+ *                       ------------------------       -------
+ * User Level                            |
+ * --------------------------------------------------------------------
+ * Interrupt Level                       |
+ *                                     ------
+ *                                    |Kernel|
+ *                                    |ISR   | <--- Interrupt
+ *                                     ------
+ *
+ *   The specific details of the ULI implementation are as follows.  When
+ * a kernel interrupt handler that has been registered with by the user
+ * to use a ULI is run, it calls into uli_handler.  This function switches
+ * the virtual memory settings around so that the address space of the
+ * registering program is used.  A function called by uli_handler does an rfi
+ * into the user's address space after first saving the system state.  When
+ * the user's handler is completed, libuli does a write() call on the file
+ * descriptor representing its /dev/uli instance, which results in uli_write
+ * executing.  uli_write restores the state saved previously along with the
+ * proper address space.  uli_handler can then return to the previously
+ * executing kernel code.
+ *
+ *   Special care must be taken when an interrupt occurs while a ULI handler
+ * is already running.  Even though the system was running in user space,
+ * it must not start from the top of the kernel stack; it needs to start
+ * from where the kernel stack was before it did the rfi.  This is kept track
+ * of through uli_cur[] and each ULI's uli_double field.  If a ULI is
+ * currently running on a CPU then uli_cur[cpu] will point to the top level
+ * one.  When an interrupt occurs during a ULI, the ULI's uli_double field is
+ * incremented.  If the field is already >= 1, then no special steps need to
+ * be taken.  If a ULI happens to run nested above another ULI, then for each
+ * new nested interrupt checks and updates will only be made against the top
+ * most ULI.
+ *
+ *   libuli is a very simple wrapper library to make ULI usage easier.  It
+ * is by no means required though.
+ *
+ *                         -------
+ *                     -> |handler|->write()---->----------
+ *                    |    -------                         | User Space
+ *--------------------------------------------------------------------
+ *                    |       ------                       | Kernel Space
+ *                    |    <-|      | uli_return_from_user |
+ *                   rfi  |  |      |                      |
+ *                    |   l   ------                       |
+ *                    |   o  |      | uli_return           |
+ *                ------  n  |      |                      |
+ *      uli_eret |      | g   ------                       |
+ *               |      | j  |      | uli_write            |
+ *                ------  u  |      |                      |
+ * uli_setup_eret|      | m   ------                       V
+ *               |      | p  |      | uli_syscall          |
+ *                ------  |  |      |                      |
+ * uli_goto_user |      |<    ------                       |
+ *               |      |    |ptregs|                      |
+ *                  A            A                         |
+ *                  \-> ------    -----------<-------------
+ *         uli_handler |      |
+ *                     |      |
+ *                      ------
+ *      Kernel Handler |      |
+ *                     |      |
+ *
+ * Locks
+ *   There is one lock in the ULI which is called through ULILOCK and
+ * ULIUNLOCK.  This lock protects ULI creation / destruction.  Each handler
+ * can only run on one CPU.  To destroy a ULI, the function should run on
+ * its CPU, raise the interrupt level to keep the ULI from running, and then
+ * remove the ULI from the list of ULIs that can be run from that CPU.
+ *
+ *   Because ULIs are referenced through the file system we rely on the file
+ * system's standard methods for reference counting and the like.  Only
+ * processes which have a copy of a ULI's file open can block on it.  The ULI
+ * keeps a reference to its registering process's address space.
+ *
+ * Porting
+ *   To port ULI to a new architecture the following steps should be taken:
+ * - Add any architecture specific changes for the handler to libuli
+ * - Create a new <asm/uli_plat.h>.  This file must define:
+ *   - uli_context     - System state
+ *   - uli_sr          - CPU status register
+ *   - ULI_IRQS        - # of lines to support
+ *   - uli_delay       - Calculate cut off time for handler
+ *   - uli_setup_args  - One time arch specific set up routine
+ *   - uli_enable_fpu  - Reenable the kernel's FPU usage (optional)
+ *   - uli_disable_fpu - Disable the handler's FPU usage (optional)
+ * - Add uli_goto_user, uli_return_from_user, and uli_eret functions.
+ *   uli_goto_user saves system state and uli_return_from user restores it.
+ *   uli_eret prepares the CPU and does the rfi into the user's handler.
+ * - Modify the syscall entry and interrupt state saving / restoring code
+ * - Modify the architecture's IRQ processing code to check if it should
+ *   call into the ULI code.
+ */
+
+#include <linux/elfcore.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/miscdevice.h>
+#include <linux/ptrace.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp.h>
+#include <linux/uli.h>
+
+#include <asm/mmu_context.h>
+#include <asm/semaphore.h>
+#include <asm/spinlock.h>
+#include <asm/uaccess.h>
+#include <asm/uli_plat.h>
+
+#define ULI_NAME "uli"
+
+static struct uli * uli_table[MAX_ULIS]; /* Points to all allocated ULIs */
+
+static spinlock_t uli_lock; /* Protects uli_table[] */
+
+#define ULILOCK(_flags) spin_lock_irqsave(&uli_lock, _flags)
+#define ULIUNLOCK(_flags) spin_unlock_irqrestore(&uli_lock, _flags)
+
+/* The ULI currently executing on each CPU */
+DEFINE_PER_CPU(struct uli *, uli_cur);
+
+static void uli_free(struct uli *);
+static int uli_ioctl(struct inode *, struct file *, unsigned int,
+                       unsigned long);
+static int uli_release(struct inode *, struct file *);
+static ssize_t uli_write(struct file *, const char *, size_t, loff_t *);
+
+/* The file operations for /dev/uli */
+static struct file_operations uli_fops = {
+       ioctl: uli_ioctl,
+       release: uli_release,
+       write: uli_write,
+};
+
+/* The registration structure for /dev/uli */
+static struct miscdevice uli_miscdev = {
+       MISC_DYNAMIC_MINOR,
+       ULI_NAME,
+       &uli_fops,
+};
+
+/*
+ * Stop the passed ULI from running any more.
+ */
+static void
+uli_irq_teardown(struct uli * uli)
+{
+       /* Give up the IRQ */
+       free_irq((int)uli->teardownarg0, uli);
+}
+
+/*
+ * Help get the CPU ready to RFI into the ULI handler.  uli_eret
+ * will do the remainder of the work.
+ */
+void
+uli_setup_eret(struct uli * uli)
+{
+       struct uli ** cur_uli = &per_cpu(uli_cur, smp_processor_id());
+       union uli_sr sr;
+
+       /* Turn off all interrupts and get the CPU status */
+       local_irq_save(sr.psr_long);
+       uli->uli_saved_sr.psr_long = sr.psr_long;
+
+       /* Save previous ULI */
+       uli->uli_prev = *cur_uli;
+
+       /* Tell interrupt handling code that we're in a ULI */
+       uli->uli_double = 0;
+
+       /*
+        * Set the current ULI.  We do this after setting uli_prev and
+        * uli_double so that if another ULI comes in it will see a
+        * consistent view.
+        */
+       *cur_uli = uli;
+
+       /* Set up ULI thread's address space */
+       if (likely(current->active_mm != uli->uli_mm)) {
+               activate_mm(current->active_mm, uli->uli_mm);
+       }
+
+       /*
+        * Set a time stamp so that we can tell if the ULI has
+        * been running for too long and abort it.
+        */
+       uli->uli_tstamp = uli_delay();
+
+       /* Reeanble interrupts and RFI into the ULI handler */
+       uli_eret(uli, uli->uli_sr.psr_long);
+
+       /*NOTREACHED*/
+       return;
+}
+
+/*
+ * This is called by a low level interrupt handler to call into
+ * the user's ULI handler.  During de-registration the
+ * specific device type code guarantees proper locking and that
+ * this uli won't be free'd out from under us.
+ */
+void
+uli_handler(struct uli * uli)
+{
+       struct uli ** cur_uli = &per_cpu(uli_cur, smp_processor_id());
+
+       /* Make sure this ULI is running where it's supposed to */
+       if (unlikely(uli->uli_cpu != smp_processor_id())) {
+               panic("ULI %p running on %d instead of %d\n",
+                     uli,
+                     smp_processor_id(),
+                     uli->uli_cpu);
+               return;
+       }
+
+       /* Save the current context and call uli_setup_eret to do the rfi */
+       uli_goto_user(uli);
+
+       /* The ULI is finished, restore VM info */
+       if (unlikely(!current->mm)) {
+               enter_lazy_tlb(current->active_mm, current);
+       } else if (likely(current->active_mm != uli->uli_mm)) {
+               activate_mm(uli->uli_mm, current->active_mm);
+       }
+
+       /* Reenable the FPU and interrupts */
+       uli_enable_fpu();
+       local_irq_restore(uli->uli_saved_sr.psr_long);
+
+       /* Restore any preempted ULI */
+       *cur_uli = uli->uli_prev;
+
+       /*
+        * Check if we're expected to signal the ULI's creating process.
+        * If we can't find it anymore then we destroy the ULI in order to
+        * free up the affected address space and avoid weird situations.
+        */
+       if (uli->uli_sig &&
+           kill_proc_info(uli->uli_sig, SEND_SIG_FORCED,
+                            uli->uli_pid)) {
+               uli_free(uli);
+       }
+
+       return;
+}
+
+/*
+ * Run all the ULIs registered with the specified device.  This is
+ * assumed to be run at interrupt level so no locking of the ULI
+ * chain is needed.
+ */
+/*ARGSUSED*/
+static irqreturn_t
+uli_IRQ_handler(int dev, void * dev_id, struct pt_regs * regs)
+{
+       struct uli * uli = (struct uli *) dev_id;
+
+       /* Call the handler */
+       if (uli->uli_cpu == smp_processor_id()) {
+               uli_handler(uli);
+       }
+
+       return 0;
+}
+
+/*
+ * This is called at ULI completion time to return to the initiating
+ * code.  A ULI is considered to have completed when it successfully
+ * returns to the kernel or is aborted due to some fault.
+ */
+void
+uli_return(int sig, struct pt_regs * regs)
+{
+       struct uli * uli = per_cpu(uli_cur, local_cpu_data->cpu);
+
+       /* Save sig to be sent to ULI thread, if any */
+       uli->uli_sig = sig;
+
+       /*
+        * If we were passed a particular context to store, copy them
+        * into the ULI structure.  This currently only happens during
+        * ULI abort because it's "too hard" to store the context data
+        * into uli_eframe during the initial context save.
+        */
+       if (regs) {
+               memcpy(&uli->uli_eframe, regs, sizeof(struct pt_regs));
+       }
+       
+       /* Return to where we fired off the ULI in uli_callup. */
+       uli_return_from_user(uli);
+
+       /*NOTREACHED*/
+}
+
+/*
+ * Generic set up code.
+ */
+int
+uli_new(struct uli ** uli_spot, struct uliargs * uargs)
+{
+       struct uli * uli;
+       int i;
+       unsigned long flags;
+
+       /* Validate the number of semaphores requested */
+       if (uargs->nsemas > ULI_MAX_SEMAS) {
+               return -EINVAL;
+       }
+
+       /* Allocate per-ULI data */
+       uli = kmalloc(ULI_SIZE(uargs->nsemas), GFP_KERNEL);
+       memset(uli, 0, ULI_SIZE(uargs->nsemas));
+       *uli_spot = uli;
+
+       /* Get a reference to the current address space */
+       if (!get_task_mm(current)) {
+               kfree(uli);
+               return -EBUSY;
+       }
+
+       /* Protect the global ULI table */
+       ULILOCK(flags);
+
+       /* Find a free spot from which to look up the ULI */
+       for (i = 0; i < MAX_ULIS; i++) {
+               if (uli_table[i] == NULL)
+                       break;
+       }
+
+       /* No space left */
+       if (i == MAX_ULIS) {
+               ULIUNLOCK(flags);
+               kfree(uli);
+               return -EBUSY;
+       }
+
+       /* Record in the master table */
+       uli_table[i] = uli;
+       uli->uli_index = i;
+
+       /* Get the current context to use. */
+       uli->uli_pid = current->group_leader->pid;
+       uli->uli_mm = current->mm;
+
+       /* Set up the handler code */
+       uli->uli_sp = uargs->sp;
+       uli->uli_pc = uargs->pc;
+       uli->uli_gp = uargs->gp;
+       uli->uli_funcarg = uargs->funcarg;
+
+       /* Set up platform specific handler values */
+       uli_setup_args(uli, uargs);
+
+       /*
+        * Disable the handler from using the FPU.  We don't save FPU
+        * state for speed reasons, so this is necessary.  Anyone
+        * nested above a ULI handler will either be another ULI or
+        * other kernel code.
+        */
+       uli_disable_fpu(uli->uli_sr);
+
+       /* Set up the semaphores */
+       uli->uli_nsemas = uargs->nsemas;
+       for (i = 0; i < uli->uli_nsemas; i++) {
+               sema_init(&uli->uli_sema[i], 0);
+       }
+
+       ULIUNLOCK(flags);
+
+       return 0;
+}
+
+/*
+ * Sleep on the passed semaphore of the passed ULI.
+ */
+static int
+uli_down(struct uli * uli, int sema)
+{
+       /* Check for a valid semaphore */
+       if (sema < 0 || sema >= uli->uli_nsemas) {
+               return -EINVAL;
+       }
+
+       /* Block allowing signals to wake us */
+       return down_interruptible(&uli->uli_sema[sema]);
+}
+
+/*
+ * Wake up the next thread on the passed semaphore of the passed ULI.
+ */
+static int
+uli_up(struct uli * uli, int sema)
+{
+       /* Check for a valid semaphore */
+       if (sema < 0 || sema >= uli->uli_nsemas) {
+               return -EINVAL;
+       }
+
+       /* Wake the next thread */
+       up(&uli->uli_sema[sema]);
+
+       return 0;
+}
+
+/*
+ * Destroy the passed ULI structure.  It first disconnects the ULI
+ * from its interrupt source then frees it.
+ */
+void
+uli_free(struct uli * uli)
+{
+       unsigned long flags;
+       cpumask_t oldmask;
+
+       /* Reschedule onto the ULI's CPU */
+       oldmask = current->cpus_allowed;
+       set_cpus_allowed(current, cpumask_of_cpu(uli->uli_cpu));
+
+       /* Block all interrupts so we know that the ULI isn't running */
+       ULILOCK(flags);
+
+       /* Disable the ULI */
+       uli_table[uli->uli_index] = NULL;
+       if (uli->uli_teardown) {
+               uli->uli_teardown(uli);
+       }
+
+       /* Reenable interrupts */
+       ULIUNLOCK(flags);
+
+       /* Reschedule */
+       set_cpus_allowed(current, oldmask);
+
+       /* Release the reference to its address space */
+       mmput(uli->uli_mm);
+
+       /* No longer need the per-ULI data */
+       kfree(uli);
+}
+
+/*
+ * When the per-interrupt opened file of /dev/uli is closed,
+ * this gets called so that we can free up any associated state.
+ */
+/*ARGSUSED*/
+static int
+uli_release(struct inode * inode, struct file * filp)
+{
+       struct uli * uli = filp->private_data;
+
+       /* If /dev/uli was opened but a ULI was never registered */
+       if (!uli) {
+               return 0;
+       }
+
+       /* For debugging purposes, clear the pointer to the ULI */
+       filp->private_data = NULL;
+
+       /* Call the generic free'ing function */
+       uli_free(uli);
+
+       return 0;
+}
+
+/*
+ * Handle ULI control operations
+ */
+static int
+uli_ioctl(struct inode * inode, struct file * filp,
+          unsigned int cmd, unsigned long arg)
+{
+       struct uliargs uargs;
+       struct uli * uli;
+       int err;
+       unsigned long flags;
+
+       switch (cmd) {
+       case IOC_ULI_REG_IRQ:
+               /* We need to use the private_data field ourselves */
+               if (filp->private_data) {
+                       return -EINVAL;
+               }
+
+               if (copy_from_user(&uargs, (void*)arg, sizeof(uargs))) {
+                       return -EFAULT;
+               }
+
+               /* Validate the line requested */
+               if (uargs.id < 0 || uargs.id > ULI_IRQS) {
+                       return -EINVAL;
+               }
+
+               /* Validate the CPU */
+               if (uargs.intarg > num_online_cpus()) {
+                       return -EINVAL;
+               }
+
+               /* Create the ULI */
+               if ((err = uli_new(&uli, &uargs))) {
+                       return err;
+               }
+
+               /* Store which line we registered with */
+               uli->teardownarg0 = uargs.id;
+
+               /* Mark which CPU the ULI can occur on */
+               uli->uli_cpu = uargs.intarg;
+
+               /* Request the IRQ */
+               err = request_irq(uargs.id, uli_IRQ_handler, SA_SHIRQ,
+                                   "ULI", uli);
+               if (err) {
+                       uli_free(uli);
+                       return err;
+               }
+
+               /* Store the ULI for later teardown */
+               filp->private_data = uli;
+
+               /* Link the ULI into the interrupt line structure */
+               ULILOCK(flags);
+               uli->uli_teardown = uli_irq_teardown;
+               ULIUNLOCK(flags);
+
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/*
+ * Handle fast ULI operations.  We don't use ioctl because of
+ * the BKL.
+ */
+/*ARGSUSED*/
+static ssize_t
+uli_write(struct file * file, const char * buf, size_t count,
+          loff_t * f_pos)
+{
+       struct uli * uli = (struct uli *)file->private_data;
+
+       /* The particular command was passed as the buffer address */
+       switch ((long)buf) {
+       case ULI_SLEEP_ADDR:
+               if (uli) {
+                       return uli_down(uli, count);
+               }
+               return -EINVAL;
+               break;
+       case ULI_WAKEUP_ADDR:
+               if (uli) {
+                       return uli_up(uli, count);
+               }
+               return -EINVAL;
+               break;
+       case ULI_RESOLV_ADDR: /* No action needed */
+               break;
+       }
+
+       return 0;
+}
+
+/*
+ * Handle fast ULI operations.  We make this look like sys_write()
+ * because libuli uses write() to return to the kernel from the handler.
+ * We can't always use uli_write() because the "current" running when
+ * the ULI handler does may not have the same file descriptor table.
+ */
+/*ARGSUSED*/
+asmlinkage ssize_t
+uli_syscall(int fd, const char * buf, size_t count,
+            loff_t * f_pos)
+{
+       struct uli * uli = per_cpu(uli_cur, local_cpu_data->cpu);
+
+       /* The particular command was passed as the buffer address */
+       switch ((long)buf) {
+       case ULI_RETURN_ADDR: /* First for speed */
+               uli_return(0, NULL);
+               /*NOTREACHED*/
+               break;
+       case ULI_WAKEUP_ADDR:
+               return uli_up(uli, count);
+       default:
+               /*
+                * No actual syscalls or any other ULI commands should be
+                * called from a ULI handler.
+                */
+               uli_return(SIGILL, (struct pt_regs *)uli->uli_intr_sp);
+               /*NOTREACHED*/
+       }
+
+       return -EINVAL;
+}
+
+/*
+ * This is called at startup time.  It creates the /dev
+ * ULI entry and handles setting up any other initial state.
+ */
+static int __init
+uli_init(void)
+{
+       int res, i;
+
+       /* They should already be NULL, but it can't hurt to be thorough */
+       for (i = 0; i < NR_CPUS; i++) {
+               per_cpu(uli_cur, i) = NULL;
+       }
+
+       /* Initialize the storage table */
+       for (i = 0; i < MAX_ULIS; i++) {
+               uli_table[i] = NULL;
+       }
+
+       /* Initialize the create / destroy lock */
+       spin_lock_init(&uli_lock);
+
+       /* Create the /dev ULI entry */
+       if ((res = misc_register(&uli_miscdev)) < 0) {
+               printk(KERN_ERR "%s: failed to register device, %d\n",
+                       ULI_NAME, res);
+               return res;
+       }
+
+       return 0;
+}
+
+__initcall(uli_init);
+
+/*
+ * Should another kernel module wish to use ULIs, these are the minimum
+ * needed routines.
+ */
+EXPORT_SYMBOL(uli_free);
+EXPORT_SYMBOL(uli_handler);
+EXPORT_SYMBOL(uli_new);
+EXPORT_SYMBOL(uli_return);
diff -urN linux-2.6.11/include/asm-ia64/uli_plat.h 
linux-2.6.11-uli/include/asm-ia64/uli_plat.h
--- linux-2.6.11/include/asm-ia64/uli_plat.h    1969-12-31 18:00:00.000000000 
-0600
+++ linux-2.6.11-uli/include/asm-ia64/uli_plat.h        2005-03-10 
06:45:52.621955200 -0600
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2005 Silicon Graphics, Inc.
+ * All rights reserved.
+ *
+ *   Michael A. Raymond <[EMAIL PROTECTED]>
+ */
+
+/*
+ *  This file is part of the User Level Interrupt (ULI) feature.
+ *
+ *  ULI 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.
+ *
+ *  ULI 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 ULI; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#ifndef __ARCH_ULI_H
+#define __ARCH_ULI_H
+
+#include <linux/ptrace.h>
+#include <asm/delay.h>
+#include <asm/irq.h>
+#include <asm/processor.h>
+
+/* Buffer to save minimum CPU context in */
+#define ULI_CTXTLEN 20
+typedef struct {
+       long _data[ULI_CTXTLEN];
+} uli_context __attribute__((__aligned__(16)));
+
+/* Simple union to make dealing with the processor state easier */
+union uli_sr {
+       struct ia64_psr psr_struct;
+       unsigned long psr_long;
+};
+
+#define ULI_IRQS NR_IRQS
+
+/* Some arbitrary point in the future */
+#define uli_delay() (ia64_get_itc() + 10000)
+
+/*
+ * Set up IA64 specific ULI handler values.  We must align the
+ * stack, get r13, and set the IP to the first instruction in the
+ * passed bundle.
+ */
+#define uli_setup_args(uli, uargs) \
+{      \
+       struct pt_regs * regs = ia64_task_regs(current);        \
+       unsigned long addr = (unsigned long)uli->uli_sp -       \
+               uargs->stacksize;       \
+\
+       /* The BSP must be 8-byte aligned */    \
+       addr = (addr + 0x7) & ~0x7;     \
+       uli->uli_arch0 = (caddr_t)addr; \
+\
+       /* The sp must be 16-byte aligned */    \
+       addr = (unsigned long)uli->uli_sp & ~0xF;       \
+       uli->uli_sp = (caddr_t)addr;    \
+\
+       /* Set its user thread pointer */       \
+       uli->uli_arch1 = (void*)regs->r13;      \
+\
+       /* Get the status register value to use */      \
+       uli->uli_sr.psr_long = regs->cr_ipsr;   \
+\
+       /* Start with the first instruction in the bundle */    \
+       uli->uli_sr.psr_struct.ri = 0;  \
+}
+
+/*
+ * Reenable the FPU when the ULI handler is completed
+ */
+#define uli_enable_fpu()
+
+/*
+ * Platform specific disabling of FPU
+ */
+#define uli_disable_fpu(sr) \
+       sr.psr_struct.dfl = 1; \
+       sr.psr_struct.dfh = 1;
+
+#endif /* __ARCH_ULI_H */
diff -urN linux-2.6.11/include/linux/uli.h linux-2.6.11-uli/include/linux/uli.h
--- linux-2.6.11/include/linux/uli.h    1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.11-uli/include/linux/uli.h        2005-03-23 09:15:07.875463206 
-0600
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2005 Silicon Graphics, Inc.
+ * All rights reserved.
+ *
+ *   Michael A. Raymond <[EMAIL PROTECTED]>
+ */
+
+/*
+ *  This file is part of the User Level Interrupt (ULI) feature.
+ *
+ *  ULI 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.
+ *
+ *  ULI 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 ULI; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#ifndef __SYS_ULI_H
+#define __SYS_ULI_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/* This is the ULI /dev entry */
+#define ULI_DEV "/dev/uli"
+
+/*
+ * This structure contains the arguments that must be passed to
+ * register ULIs.
+ */
+struct uliargs {
+       /* device independent fields */
+       caddr_t pc;             /* PC of handler function */
+       caddr_t gp;             /* GP for handler function */
+       caddr_t sp;             /* Base SP of stack to use */
+       size_t stacksize;       /* Size of stack (needed for IA64) */
+       void * funcarg;         /* argument to handler function */
+       unsigned int nsemas;            /* ULI's semaphores to sleep on */
+
+       int id;                 /* Interrupt device specifier */
+       unsigned long intarg;   /* Special device parameter */
+};
+
+/* /dev/uli ioctl arguments */
+#define IOC_ULI_REG_IRQ 0x0010 /* Register a ULI for an IRQ line */
+
+/* /dev/uli I/O addresses */
+#define ULI_SLEEP_ADDR 0x100   /* ULI_sleep */
+#define ULI_WAKEUP_ADDR        0x200   /* ULI_wakeup */
+#define ULI_RETURN_ADDR        0x300   /* return from intr */
+#define ULI_RESOLV_ADDR        0x500   /* For pre-resolving symbols */
+
+#define ULI_MAX_SEMAS  32      /* Maximum number supported */
+
+/********************************************************************/
+#if defined(__KERNEL__)
+#if defined(CONFIG_ULI)
+
+#include <linux/config.h>
+#include <linux/elf.h>
+#include <linux/ptrace.h>
+#include <linux/types.h>
+#include <asm/mmu.h>
+#include <asm/percpu.h>
+#include <asm/semaphore.h>
+#include <asm/uli_plat.h>
+
+#define MAX_ULIS 64 /* Max number of ULIs supported */
+
+/*
+ * This kernel per-ULI structure keeps track off all its state.
+ */
+struct uli {
+       /* This must be first */
+       uli_context uli_context;                /* State before user handler */
+
+       caddr_t uli_gp;                 /* Handler's GP */
+       caddr_t uli_sp;                 /* Handler's SP */
+       union uli_sr uli_sr;            /* Handler's SR */
+       caddr_t uli_pc;                 /* Handler's PC */
+       void * uli_funcarg;             /* Argument to handler */
+       caddr_t uli_arch0;              /* Arch specific user register */
+       caddr_t uli_arch1;              /* Arch specific user register */
+
+       pid_t uli_pid;          /* The registering process */
+       struct mm_struct * uli_mm;      /* MM of owning process */
+       unsigned long uli_tstamp;               /* Time ULI started */
+
+       unsigned int uli_double;        /* In doubly nested interrupt? */
+       unsigned long uli_intr_sp;      /* SP at time of goto user */
+       union uli_sr uli_saved_sr;      /* SR at time of goto user */
+       caddr_t uli_saved_epc;          /* PC interrupted by ULI handled intr */
+       unsigned long uli_intr_arch0;   /* Arch reg at goto user */
+
+       short uli_cpu;                  /* Only this calls the handler */
+       struct uli * uli_next;          /* per-dev list of registered ULIs */
+       struct uli * uli_prev;          /* ULI preempted by this one */
+       int uli_sig;                    /* Error status of ULI handler */
+       int uli_index;                  /* ULI's spot in uli_table[] */
+
+       struct pt_regs uli_eframe;      /* Context of failed ULI */
+
+       /* Device dependent teardown func to disconnect interrupt */
+       void (*uli_teardown)(struct uli*);      /* Teardown from reg'd dev */
+       unsigned long teardownarg0;             /* Per-device teardown data */
+       unsigned long teardownarg1;             /* to be referenced from the */
+       unsigned long teardownarg2;             /* teardown handler */
+
+       int uli_nsemas;                 /* # of semaphores in uli_sema[] */
+
+       /* Must be last element */
+       struct semaphore uli_sema[1];   /* So threads can queue off this ULI */
+};
+
+/* Size of the total ULI struct given number of semaphores */
+#define ULI_SIZE(nsemas) ((size_t)&(((struct uli*)0)->uli_sema[nsemas]))
+
+extern void uli_callup(unsigned int);
+extern void uli_eret(struct uli *, unsigned long);
+extern void uli_goto_user(struct uli *);
+extern void uli_handler(struct uli *);
+extern void uli_return(int, struct pt_regs *);
+extern void uli_return_from_user(struct uli *);
+extern void uli_setup_eret(struct uli *);
+
+/*
+ * If we're in a ULI and we commit any kind of illegal
+ * instruction,  we abort the ULI and send a signal to the
+ * owning process.
+ */
+static inline void
+uli_trap(int sig, struct pt_regs * regs)
+{
+       extern struct uli * per_cpu__uli_cur;
+       struct uli * uli = per_cpu(uli_cur, local_cpu_data->cpu);
+
+       /*
+        * If uli_double > 1 then the fault wasn't the fault of the
+        * ULI handler.
+        */
+       if (uli &&
+           uli->uli_double == 1) {
+               uli_return(sig, regs);
+       }
+}
+
+#else /* CONFIG_ULI */
+
+#define uli_trap(sig, regs) do {} while (0)
+
+#endif /* CONFIG_ULI */
+#endif /* __KERNEL__ */
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* __SYS_ULI_H */

Reply via email to