diff --git a/arch/ppc/config.in b/arch/ppc/config.in
index 08c6c40..133e8d8 100644
--- a/arch/ppc/config.in
+++ b/arch/ppc/config.in
@@ -892,6 +892,11 @@ else
   fi
 fi
 
+bool 'Interrupt pipeline' CONFIG_IPIPE
+if [ "$CONFIG_IPIPE" = "y" ]; then
+  bool 'Check for illicit cross-domain calls' CONFIG_IPIPE_DEBUG_CONTEXT
+fi
+
 bool 'Networking support' CONFIG_NET
 bool 'Sysctl support' CONFIG_SYSCTL
 bool 'System V IPC' CONFIG_SYSVIPC
diff --git a/arch/ppc/kernel/Makefile b/arch/ppc/kernel/Makefile
index 96b1cde..4843b45 100644
--- a/arch/ppc/kernel/Makefile
+++ b/arch/ppc/kernel/Makefile
@@ -46,12 +46,14 @@ export-objs			:= ppc_ksyms.o time.o ocp.o ppc4xx_setup.o \
 					ppc4xx_dma.o ppc4xx_sgdma.o \
 					ppc4xx_stbdma.o ppc4xx_pm.o \
 					gt64260_common.o mpc5xxx_common.o \
-					ppc85xx_common.o ppc83xx_common.o
+					ppc85xx_common.o ppc83xx_common.o \
+					ipipe.o
 
 obj-y				:= entry.o traps.o irq.o idle.o time.o misc.o \
 					process.o signal.o ptrace.o align.o \
 					semaphore.o syscalls.o setup.o \
 					cputable.o ppc_htab.o
+obj-$(CONFIG_IPIPE)		+= ipipe.o
 ifneq ($(CONFIG_6xx),y)
 obj-y				+= idle_gen.o
 endif
diff --git a/arch/ppc/kernel/entry.S b/arch/ppc/kernel/entry.S
index d19d49d..cdb86ff 100644
--- a/arch/ppc/kernel/entry.S
+++ b/arch/ppc/kernel/entry.S
@@ -53,6 +53,21 @@ _GLOBAL(DoSyscall)
 	lis	r10,0x1000
 	andc	r11,r11,r10
 	stw	r11,_CCR(r1)
+#ifdef CONFIG_IPIPE
+	addi	r3,r1,GPR0
+	bl	__ipipe_syscall_root
+	cmpwi	r3,0
+	lwz	r3,GPR3(r1)
+	lwz	r0,GPR0(r1)
+	lwz	r4,GPR4(r1)
+	lwz	r5,GPR5(r1)
+	lwz	r6,GPR6(r1)
+	lwz	r7,GPR7(r1)
+	lwz	r8,GPR8(r1)
+	lwz	r9,GPR9(r1)
+	bgt	restore
+	blt	ret_from_syscall_1
+#endif /* CONFIG_IPIPE */
 #ifdef SHOW_SYSCALLS
 #ifdef SHOW_SYSCALLS_TASK
 	lis	r31,show_syscalls_task@ha
@@ -280,7 +295,11 @@ ret_from_fork:
 	lwz	r0,TASK_PTRACE(r2)
 	andi.	r0,r0,PT_TRACESYS
 	bnel-	syscall_trace
+#ifdef CONFIG_IPIPE
+	b	ret_from_except_1
+#else	
 	b	ret_from_except
+#endif
 
 	.globl	ret_from_intercept
 ret_from_intercept:
@@ -288,10 +307,21 @@ ret_from_intercept:
 	 * We may be returning from RTL and cannot do the normal checks
 	 * -- Cort
 	 */
+	/*
+	 * We may also be returning from Adeos and cannot do the normal checks either
+	 * -- rpm
+	 */
 	cmpi	0,r3,0
 	beq	restore
+	b	ret_from_except_1
 	.globl	ret_from_except
 ret_from_except:
+#ifdef CONFIG_IPIPE
+        bl	__ipipe_check_root
+        cmpwi	r3, 0
+        beq-	restore
+#endif /* CONFIG_IPIPE */
+ret_from_except_1:
 	lwz	r3,_MSR(r1)	/* Returning to user mode? */
 	andi.	r3,r3,MSR_PR
 	beq+	do_signal_ret	/* if so, check need_resched and signals */
diff --git a/arch/ppc/kernel/head.S b/arch/ppc/kernel/head.S
index 7b9e0c2..2605305 100644
--- a/arch/ppc/kernel/head.S
+++ b/arch/ppc/kernel/head.S
@@ -435,7 +435,11 @@ HardwareInterrupt:
 	bl	transfer_to_handler
 	.globl do_IRQ_intercept
 do_IRQ_intercept:
-	.long	do_IRQ;
+#ifdef CONFIG_IPIPE
+	.long	__ipipe_grab_irq
+#else /* !CONFIG_IPIPE */
+	.long	do_IRQ
+#endif /* CONFIG_IPIPE */
 	.long	ret_from_intercept
 
 /* Alignment exception */
@@ -485,7 +489,11 @@ Decrementer:
 	bl	transfer_to_handler
 	.globl timer_interrupt_intercept
 timer_interrupt_intercept:
+#ifdef CONFIG_IPIPE
+	.long	__ipipe_grab_timer
+#else /* !CONFIG_IPIPE */
 	.long	timer_interrupt
+#endif /* CONFIG_IPIPE */
 	.long	ret_from_intercept
 
 	STD_EXCEPTION(0xa00, Trap_0a, UnknownException)
diff --git a/arch/ppc/kernel/head_440.S b/arch/ppc/kernel/head_440.S
index a52bf16..d8cbb41 100644
--- a/arch/ppc/kernel/head_440.S
+++ b/arch/ppc/kernel/head_440.S
@@ -539,7 +539,11 @@ interrupt_base:
 	li      r4,0
 	bl      transfer_to_handler
 _GLOBAL(do_IRQ_intercept)
-	.long   do_IRQ
+#ifdef CONFIG_IPIPE
+	.long	__ipipe_grab_irq
+#else /* !CONFIG_IPIPE */
+	.long	do_IRQ
+#endif /* CONFIG_IPIPE */
 	.long   ret_from_intercept
 	.long	0x500
 
@@ -590,7 +594,11 @@ _GLOBAL(do_IRQ_intercept)
 	li      r20,MSR_KERNEL
 	bl      transfer_to_handler
 _GLOBAL(timer_interrupt_intercept)
-	.long   timer_interrupt
+#ifdef CONFIG_IPIPE
+	.long	__ipipe_grab_timer
+#else /* !CONFIG_IPIPE */
+	.long	timer_interrupt
+#endif /* CONFIG_IPIPE */
 	.long   ret_from_intercept
 	.long	0x1000
 
diff --git a/arch/ppc/kernel/head_44x.S b/arch/ppc/kernel/head_44x.S
index bc7f216..7f53079 100644
--- a/arch/ppc/kernel/head_44x.S
+++ b/arch/ppc/kernel/head_44x.S
@@ -552,7 +552,11 @@ interrupt_base:
 	li      r4,0
 	bl      transfer_to_handler
 _GLOBAL(do_IRQ_intercept)
-	.long   do_IRQ
+#ifdef CONFIG_IPIPE
+	.long	__ipipe_grab_irq
+#else /* !CONFIG_IPIPE */
+	.long	do_IRQ
+#endif /* CONFIG_IPIPE */
 	.long   ret_from_intercept
 	.long	0x500
 
@@ -603,7 +607,11 @@ _GLOBAL(do_IRQ_intercept)
 	li      r20,MSR_KERNEL
 	bl      transfer_to_handler
 _GLOBAL(timer_interrupt_intercept)
-	.long   timer_interrupt
+#ifdef CONFIG_IPIPE
+	.long	__ipipe_grab_timer
+#else /* !CONFIG_IPIPE */
+	.long	timer_interrupt
+#endif /* CONFIG_IPIPE */
 	.long   ret_from_intercept
 	.long	0x1000
 
diff --git a/arch/ppc/kernel/head_4xx.S b/arch/ppc/kernel/head_4xx.S
index 2f5d73f..fda2742 100644
--- a/arch/ppc/kernel/head_4xx.S
+++ b/arch/ppc/kernel/head_4xx.S
@@ -384,7 +384,11 @@ label:
 	li	r4,0
 	bl	transfer_to_handler
 _GLOBAL(do_IRQ_intercept)
+#ifdef CONFIG_IPIPE
+	.long	__ipipe_grab_irq
+#else /* !CONFIG_IPIPE */
 	.long	do_IRQ
+#endif /* CONFIG_IPIPE */
 	.long	ret_from_intercept
 
 /* 0x0600 - Alignment Exception
@@ -456,7 +460,11 @@ _GLOBAL(do_IRQ_intercept)
 	li	r20,MSR_KERNEL
 	bl	transfer_to_handler
 _GLOBAL(timer_interrupt_intercept)
+#ifdef CONFIG_IPIPE
+	.long	__ipipe_grab_timer
+#else /* !CONFIG_IPIPE */
 	.long	timer_interrupt
+#endif /* CONFIG_IPIPE */
 	.long	ret_from_intercept
 
 #if 0
diff --git a/arch/ppc/kernel/head_8xx.S b/arch/ppc/kernel/head_8xx.S
index d610508..b9de7ff 100644
--- a/arch/ppc/kernel/head_8xx.S
+++ b/arch/ppc/kernel/head_8xx.S
@@ -231,7 +231,11 @@ HardwareInterrupt:
 	bl	transfer_to_handler
 	.globl	do_IRQ_intercept
 do_IRQ_intercept:
-	.long	do_IRQ;
+#ifdef CONFIG_IPIPE
+	.long   __ipipe_grab_irq
+#else /* !CONFIG_IPIPE */
+	.long	do_IRQ
+#endif /* CONFIG_IPIPE */
 	.long	ret_from_intercept
 
 /* Alignment exception */
@@ -268,7 +272,11 @@ Decrementer:
 	bl	transfer_to_handler
 	.globl	timer_interrupt_intercept
 timer_interrupt_intercept:
-	.long	timer_interrupt
+#ifdef CONFIG_IPIPE
+	.long   __ipipe_grab_timer
+#else /* !CONFIG_IPIPE */
+	.long   timer_interrupt
+#endif /* CONFIG_IPIPE */
 	.long	ret_from_intercept
 
 	STD_EXCEPTION(0xa00, Trap_0a, UnknownException)
diff --git a/arch/ppc/kernel/head_e500.S b/arch/ppc/kernel/head_e500.S
index 9d9fc68..f3149c4 100644
--- a/arch/ppc/kernel/head_e500.S
+++ b/arch/ppc/kernel/head_e500.S
@@ -618,7 +618,11 @@ interrupt_base:
 	li      r4,0
 	bl      transfer_to_handler
 _GLOBAL(do_IRQ_intercept)
-	.long   do_IRQ
+#ifdef CONFIG_IPIPE
+	.long	__ipipe_grab_irq
+#else /* !CONFIG_IPIPE */
+	.long	do_IRQ
+#endif /* CONFIG_IPIPE */
 	.long   ret_from_intercept
 	.long	0x500
 
@@ -669,7 +673,11 @@ _GLOBAL(do_IRQ_intercept)
 	li      r20,MSR_KERNEL
 	bl      transfer_to_handler
 _GLOBAL(timer_interrupt_intercept)
-	.long   timer_interrupt
+#ifdef CONFIG_IPIPE
+	.long	__ipipe_grab_timer
+#else /* !CONFIG_IPIPE */
+	.long	timer_interrupt
+#endif /* CONFIG_IPIPE */
 	.long   ret_from_intercept
 	.long	0x900
 
diff --git a/arch/ppc/kernel/idle.c b/arch/ppc/kernel/idle.c
index 29a95bf..ea58c00 100644
--- a/arch/ppc/kernel/idle.c
+++ b/arch/ppc/kernel/idle.c
@@ -50,6 +50,7 @@ int idled(void)
 	current->counter = -100;
 	init_idle();
 	for (;;) {
+		ipipe_suspend_domain();
 #ifdef CONFIG_SMP
 		if (!do_power_save) {
 			/*
diff --git a/arch/ppc/kernel/ipic.c b/arch/ppc/kernel/ipic.c
index 2ee19ac..5d85ee6 100644
--- a/arch/ppc/kernel/ipic.c
+++ b/arch/ppc/kernel/ipic.c
@@ -377,7 +377,7 @@ static inline struct ipic * ipic_from_irq(unsigned int irq)
 	return primary_ipic;
 }
 
-static void ipic_enable_irq(unsigned int irq)
+static void __ipic_enable_irq(unsigned int irq)
 {
 	struct ipic *ipic = ipic_from_irq(irq);
 	unsigned int src = irq - ipic->irq_offset;
@@ -388,7 +388,17 @@ static void ipic_enable_irq(unsigned int irq)
 	ipic_write(ipic->regs, ipic_info[src].mask, temp);
 }
 
-static void ipic_disable_irq(unsigned int irq)
+static void ipic_enable_irq(unsigned int irq)
+{
+	unsigned long flags;
+
+	local_irq_save_hw_cond(flags);
+	__ipic_enable_irq(irq);
+	ipipe_irq_unlock(irq);
+	local_irq_restore_hw_cond(flags);
+}
+
+static void __ipic_disable_irq(unsigned int irq)
 {
 	struct ipic *ipic = ipic_from_irq(irq);
 	unsigned int src = irq - ipic->irq_offset;
@@ -399,23 +409,39 @@ static void ipic_disable_irq(unsigned int irq)
 	ipic_write(ipic->regs, ipic_info[src].mask, temp);
 }
 
+static void ipic_disable_irq(unsigned int irq)
+{
+	unsigned long flags;
+
+	local_irq_save_hw_cond(flags);
+	__ipic_disable_irq(irq);
+	ipipe_irq_lock(irq);
+	local_irq_restore_hw_cond(flags);
+}
+
 static void ipic_disable_irq_and_ack(unsigned int irq)
 {
 	struct ipic *ipic = ipic_from_irq(irq);
 	unsigned int src = irq - ipic->irq_offset;
+	unsigned long flags;
 	u32 temp;
 
-	ipic_disable_irq(irq);
+	local_irq_save_hw_cond(flags);
+
+	__ipic_disable_irq(irq);
 
 	temp = ipic_read(ipic->regs, ipic_info[src].pend);
 	temp |= (1 << (31 - ipic_info[src].bit));
 	ipic_write(ipic->regs, ipic_info[src].pend, temp);
+
+	local_irq_restore_hw_cond(flags);
 }
 
 static void ipic_end_irq(unsigned int irq)
 {
-	if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
-		ipic_enable_irq(irq);
+	if (!ipipe_root_domain_p ||
+	    !(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
+		__ipic_enable_irq(irq);
 }
 
 struct hw_interrupt_type ipic = {
diff --git a/arch/ppc/kernel/ipipe.c b/arch/ppc/kernel/ipipe.c
new file mode 100644
index 0000000..9319405
--- /dev/null
+++ b/arch/ppc/kernel/ipipe.c
@@ -0,0 +1,406 @@
+/* -*- linux-c -*-
+ * linux/arch/ppc/kernel/ipipe.c
+ *
+ * Copyright (C) 2009 Philippe Gerum (I-pipe backport from 2.6 series)
+ * Copyright (C) 2005 Heikki Lindholm (PPC64 port).
+ * Copyright (C) 2004 Wolfgang Grandegger (Adeos/ppc port over 2.4).
+ * Copyright (C) 2002-2007 Philippe Gerum.
+ *
+ * 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, Inc., 675 Mass Ave, Cambridge MA 02139,
+ * USA; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Architecture-dependent I-PIPE core support for PowerPC 32/64bit.
+ */
+
+#include <linux/kernel.h>
+#include <linux/smp.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/bitops.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/kernel_stat.h>
+#include <asm/system.h>
+#include <asm/mmu_context.h>
+#include <asm/unistd.h>
+#include <asm/machdep.h>
+#include <asm/atomic.h>
+#include <asm/hardirq.h>
+#include <asm/io.h>
+#include <asm/time.h>
+
+static void __ipipe_do_IRQ(unsigned irq, void *cookie);
+
+static void __ipipe_do_timer(unsigned irq, void *cookie);
+
+DEFINE_PER_CPU(struct pt_regs, __ipipe_tick_regs);
+
+#define DECREMENTER_MAX	0x7fffffff
+
+/*
+ * ipipe_critical_enter() -- Grab the superlock excluding all CPUs
+ * but the current one from a critical section. This lock is used when
+ * we must enforce a global critical section for a single CPU in a
+ * possibly SMP system whichever context the CPUs are running.
+ */
+unsigned long ipipe_critical_enter(void (*syncfn) (void))
+{
+	unsigned long flags;
+
+	local_irq_save_hw(flags);
+
+	return flags;
+}
+
+/* ipipe_critical_exit() -- Release the superlock. */
+
+void ipipe_critical_exit(unsigned long flags)
+{
+	local_irq_restore_hw(flags);
+}
+
+void __ipipe_init_platform(void)
+{
+	unsigned int virq;
+
+	/*
+	 * Allocate a virtual IRQ for the decrementer trap early to
+	 * get it mapped to IPIPE_VIRQ_BASE
+	 */
+
+	virq = ipipe_alloc_virq();
+
+	if (virq != IPIPE_TIMER_VIRQ)
+		panic("I-pipe: cannot reserve timer virq #%d (got #%d)",
+		      IPIPE_TIMER_VIRQ, virq);
+}
+
+void __ipipe_end_irq(unsigned irq)
+{
+	irq_desc_t *desc = get_irq_desc(irq);
+	if (desc->handler) {
+		if (desc->handler->end)
+			desc->handler->end(irq);
+		else if (desc->handler->enable)
+			desc->handler->enable(irq);
+	}
+}
+
+void __ipipe_enable_irqdesc(struct ipipe_domain *ipd, unsigned irq)
+{
+	get_irq_desc(irq)->status &= ~IRQ_DISABLED;
+}
+
+static void __ipipe_ack_irq(unsigned irq, struct irq_desc *desc)
+{
+	ack_irq(irq);
+}
+
+/*
+ * __ipipe_enable_pipeline() -- We are running on the boot CPU, hw
+ * interrupts are off, and secondary CPUs are still lost in space.
+ */
+void __ipipe_enable_pipeline(void)
+{
+	unsigned long flags;
+	unsigned irq;
+
+	flags = ipipe_critical_enter(NULL);
+
+	/* First, virtualize all interrupts from the root domain. */
+
+	for (irq = 0; irq < NR_IRQS; irq++)
+		ipipe_virtualize_irq(ipipe_root_domain,
+				     irq,
+				     &__ipipe_do_IRQ, NULL,
+				     &__ipipe_ack_irq,
+				     IPIPE_HANDLE_MASK | IPIPE_PASS_MASK);
+	/*
+	 * We use a virtual IRQ to handle the timer irq (decrementer trap)
+	 * which has been allocated early in __ipipe_init_platform().
+	 */
+
+	ipipe_virtualize_irq(ipipe_root_domain,
+			     IPIPE_TIMER_VIRQ,
+			     &__ipipe_do_timer, NULL,
+			     NULL, IPIPE_HANDLE_MASK | IPIPE_PASS_MASK);
+
+	ipipe_critical_exit(flags);
+}
+
+int ipipe_get_sysinfo(struct ipipe_sysinfo *info)
+{
+	info->ncpus = num_online_cpus();
+	info->cpufreq = ipipe_cpu_freq();
+	info->archdep.tmirq = IPIPE_TIMER_VIRQ;
+	info->archdep.tmfreq = info->cpufreq;
+
+	return 0;
+}
+
+/*
+ * ipipe_trigger_irq() -- Push the interrupt at front of the pipeline
+ * just like if it has been actually received from a hw source. Also
+ * works for virtual interrupts.
+ */
+int ipipe_trigger_irq(unsigned irq)
+{
+	unsigned long flags;
+
+#ifdef CONFIG_IPIPE_DEBUG
+	if (irq >= IPIPE_NR_IRQS ||
+	    (ipipe_virtual_irq_p(irq)
+	     && !test_bit(irq - IPIPE_VIRQ_BASE, &__ipipe_virtual_irq_map)))
+		return -EINVAL;
+#endif
+	local_irq_save_hw(flags);
+	__ipipe_handle_irq(irq, NULL);
+	local_irq_restore_hw(flags);
+
+	return 1;
+}
+
+/*
+ * __ipipe_handle_irq() -- IPIPE's generic IRQ handler. An optimistic
+ * interrupt protection log is maintained here for each domain. Hw
+ * interrupts are off on entry.
+ */
+void __ipipe_handle_irq(int irq, struct pt_regs *regs)
+{
+	struct ipipe_domain *this_domain, *next_domain;
+	struct list_head *head, *pos;
+	int m_ack;
+
+	/* Software-triggered IRQs do not need any ack. */
+	m_ack = (regs == NULL);
+
+#ifdef CONFIG_IPIPE_DEBUG
+	if (unlikely(irq >= IPIPE_NR_IRQS)) {
+		printk(KERN_ERR "I-pipe: spurious interrupt %d\n", irq);
+		return;
+	}
+#endif
+	this_domain = __ipipe_current_domain;
+
+	if (unlikely(test_bit(IPIPE_STICKY_FLAG, &this_domain->irqs[irq].control)))
+		head = &this_domain->p_link;
+	else {
+		head = __ipipe_pipeline.next;
+		next_domain = list_entry(head, struct ipipe_domain, p_link);
+		if (likely(test_bit(IPIPE_WIRED_FLAG, &next_domain->irqs[irq].control))) {
+			if (!m_ack && next_domain->irqs[irq].acknowledge)
+				next_domain->irqs[irq].acknowledge(irq, irq_desc + irq);
+			__ipipe_dispatch_wired(next_domain, irq);
+			return;
+		}
+	}
+
+	/* Ack the interrupt. */
+
+	pos = head;
+
+	while (pos != &__ipipe_pipeline) {
+		next_domain = list_entry(pos, struct ipipe_domain, p_link);
+		prefetch(next_domain);
+		/*
+		 * For each domain handling the incoming IRQ, mark it as
+		 * pending in its log.
+		 */
+		if (test_bit(IPIPE_HANDLE_FLAG, &next_domain->irqs[irq].control)) {
+			/*
+			 * Domains that handle this IRQ are polled for
+			 * acknowledging it by decreasing priority order. The
+			 * interrupt must be made pending _first_ in the
+			 * domain's status flags before the PIC is unlocked.
+			 */
+			__ipipe_set_irq_pending(next_domain, irq);
+
+			if (!m_ack && next_domain->irqs[irq].acknowledge) {
+				next_domain->irqs[irq].acknowledge(irq, irq_desc + irq);
+				m_ack = 1;
+			}
+		}
+
+		/*
+		 * If the domain does not want the IRQ to be passed down the
+		 * interrupt pipe, exit the loop now.
+		 */
+		if (!test_bit(IPIPE_PASS_FLAG, &next_domain->irqs[irq].control))
+			break;
+
+		pos = next_domain->p_link.next;
+	}
+
+	/*
+	 * If the interrupt preempted the head domain, then do not
+	 * even try to walk the pipeline, unless an interrupt is
+	 * pending for it.
+	 */
+	if (test_bit(IPIPE_AHEAD_FLAG, &this_domain->flags) &&
+	    ipipe_head_cpudom_var(irqpend_himask) == 0)
+		return;
+
+	/*
+	 * Now walk the pipeline, yielding control to the highest
+	 * priority domain that has pending interrupt(s) or
+	 * immediately to the current domain if the interrupt has been
+	 * marked as 'sticky'. This search does not go beyond the
+	 * current domain in the pipeline.
+	 */
+
+	__ipipe_walk_pipeline(head);
+}
+
+int __ipipe_grab_irq(struct pt_regs *regs)
+{
+	extern int ppc_spurious_interrupts;
+	int irq;
+
+	irq = ppc_md.get_irq(regs);
+	if (unlikely(irq < 0)) {
+		if (irq == -1)
+			ppc_spurious_interrupts++;
+		goto root_checks;
+	}
+
+	__ipipe_handle_irq(irq, regs);
+
+root_checks:
+
+	if (__ipipe_root_domain_p) {
+		if (!test_bit(IPIPE_STALL_FLAG, &ipipe_root_cpudom_var(status)))
+			return 1;
+	}
+
+	return 0;
+}
+
+static void __ipipe_do_IRQ(unsigned irq, void *cookie)
+{
+	struct pt_regs *regs = &__raw_get_cpu_var(__ipipe_tick_regs);
+
+        hardirq_enter(0);
+	ppc_irq_dispatch_handler(regs, irq);
+        hardirq_exit(0);
+
+	if (softirq_pending(0))
+		do_softirq();
+}
+
+static void __ipipe_do_timer(unsigned irq, void *cookie)
+{
+	timer_interrupt(&__raw_get_cpu_var(__ipipe_tick_regs));
+}
+
+int __ipipe_grab_timer(struct pt_regs *regs)
+{
+	struct ipipe_domain *ipd, *head;
+
+	ipd = __ipipe_current_domain;
+	head = __ipipe_pipeline_head();
+
+	set_dec(DECREMENTER_MAX);
+
+	__raw_get_cpu_var(__ipipe_tick_regs).msr = regs->msr; /* for timer_interrupt() */
+	__raw_get_cpu_var(__ipipe_tick_regs).nip = regs->nip;
+
+	if (ipd != &ipipe_root)
+		__raw_get_cpu_var(__ipipe_tick_regs).msr &= ~MSR_EE;
+
+	if (test_bit(IPIPE_WIRED_FLAG, &head->irqs[IPIPE_TIMER_VIRQ].control))
+		/*
+		 * Finding a wired IRQ means that we do have a
+		 * registered head domain as well. The decrementer
+		 * interrupt requires no acknowledge, so we may branch
+		 * to the wired IRQ dispatcher directly. Additionally,
+		 * we may bypass checks for locked interrupts or
+		 * stalled stage (the decrementer cannot be locked and
+		 * the head domain is obviously not stalled since we
+		 * got there).
+		 */
+		__ipipe_dispatch_wired_nocheck(head, IPIPE_TIMER_VIRQ);
+	else
+		__ipipe_handle_irq(IPIPE_TIMER_VIRQ, NULL);
+
+	if (ipd == &ipipe_root) {
+		if (!test_bit(IPIPE_STALL_FLAG, &ipipe_root_cpudom_var(status)))
+			return 1;
+	}
+
+	return 0;
+}
+
+notrace int __ipipe_check_root(void) /* hw IRQs off */
+{
+	return __ipipe_root_domain_p;
+}
+
+int __ipipe_syscall_root(struct pt_regs *regs)
+{
+	struct ipipe_percpu_domain_data *p;
+	unsigned long flags;
+        int ret;
+
+        /*
+         * This routine either returns:
+         * 0 -- if the syscall is to be passed to Linux;
+         * >0 -- if the syscall should not be passed to Linux, and no
+         * tail work should be performed;
+         * <0 -- if the syscall should not be passed to Linux but the
+         * tail work has to be performed (for handling signals etc).
+         */
+
+        if (!__ipipe_syscall_watched_p(current, regs->gpr[0]) ||
+            !__ipipe_event_monitored_p(IPIPE_EVENT_SYSCALL))
+                return 0;
+
+        ret = __ipipe_dispatch_event(IPIPE_EVENT_SYSCALL, regs);
+
+	local_irq_save_hw(flags);
+
+        if (!__ipipe_root_domain_p) {
+		local_irq_restore_hw(flags);
+		return 1;
+	}
+
+	p = ipipe_root_cpudom_ptr();
+	if ((p->irqpend_himask & IPIPE_IRQMASK_VIRT) != 0)
+		__ipipe_sync_pipeline(IPIPE_IRQMASK_VIRT);
+
+	local_irq_restore_hw(flags);
+
+	return -ret;
+}
+
+void __ipipe_pin_range_globally(unsigned long start, unsigned long end)
+{
+	/* We don't support this. */
+}
+
+EXPORT_SYMBOL_GPL(__switch_to);
+EXPORT_SYMBOL_GPL(show_stack);
+EXPORT_SYMBOL_GPL(_switch);
+EXPORT_SYMBOL_GPL(tasklist_lock);
+void atomic_set_mask(unsigned long mask, unsigned long *ptr);
+void atomic_clear_mask(unsigned long mask, unsigned long *ptr);
+#ifdef FEW_CONTEXTS
+EXPORT_SYMBOL_GPL(nr_free_contexts);
+EXPORT_SYMBOL_GPL(context_mm);
+EXPORT_SYMBOL_GPL(steal_context);
+#endif	/* !FEW_CONTEXTS */
+EXPORT_SYMBOL_GPL(atomic_set_mask);
+EXPORT_SYMBOL_GPL(atomic_clear_mask);
+EXPORT_SYMBOL_GPL(last_task_used_math);
diff --git a/arch/ppc/kernel/irq.c b/arch/ppc/kernel/irq.c
index 8c9f63d..2558665 100644
--- a/arch/ppc/kernel/irq.c
+++ b/arch/ppc/kernel/irq.c
@@ -433,7 +433,9 @@ void ppc_irq_dispatch_handler(struct pt_regs *regs, int irq)
 
 	kstat.irqs[cpu][irq]++;
 	spin_lock(&desc->lock);
+#ifndef CONFIG_IPIPE
 	ack_irq(irq);
+#endif /* CONFIG_IPIPE */
 	/*
 	   REPLAY is when Linux resends an IRQ that was dropped earlier
 	   WAITING is used by probe to mark irqs that are being tested
diff --git a/arch/ppc/kernel/mpc5xxx_pic.c b/arch/ppc/kernel/mpc5xxx_pic.c
index fd1f458..b303c88 100644
--- a/arch/ppc/kernel/mpc5xxx_pic.c
+++ b/arch/ppc/kernel/mpc5xxx_pic.c
@@ -26,7 +26,7 @@
 #include <asm/mpc5xxx.h>
 
 static void
-mpc5xxx_ic_disable(unsigned int irq)
+__mpc5xxx_ic_disable(unsigned int irq)
 {
 	struct mpc5xxx_intr *intr = (struct mpc5xxx_intr *)MPC5xxx_INTR;
 	struct mpc5xxx_sdma *sdma = (struct mpc5xxx_sdma *)MPC5xxx_SDMA;
@@ -63,7 +63,18 @@ mpc5xxx_ic_disable(unsigned int irq)
 }
 
 static void
-mpc5xxx_ic_enable(unsigned int irq)
+mpc5xxx_ic_disable(unsigned int irq)
+{
+	unsigned long flags;
+
+	local_irq_save_hw_cond(flags);
+	__mpc5xxx_ic_disable(irq);
+	ipipe_irq_lock(irq);
+	local_irq_restore_hw_cond(flags);
+}
+
+static void
+__mpc5xxx_ic_enable(unsigned int irq)
 {
 	struct mpc5xxx_intr *intr = (struct mpc5xxx_intr *)MPC5xxx_INTR;
 	struct mpc5xxx_sdma *sdma = (struct mpc5xxx_sdma *)MPC5xxx_SDMA;
@@ -100,6 +111,17 @@ mpc5xxx_ic_enable(unsigned int irq)
 }
 
 static void
+mpc5xxx_ic_enable(unsigned int irq)
+{
+	unsigned long flags;
+
+	local_irq_save_hw_cond(flags);
+	__mpc5xxx_ic_enable(irq);
+	ipipe_irq_unlock(irq);
+	local_irq_restore_hw_cond(flags);
+}
+
+static void
 mpc5xxx_ic_ack(unsigned int irq)
 {
 	struct mpc5xxx_intr *intr = (struct mpc5xxx_intr *)MPC5xxx_INTR;
@@ -149,15 +171,21 @@ mpc5xxx_ic_ack(unsigned int irq)
 static void
 mpc5xxx_ic_disable_and_ack(unsigned int irq)
 {
-	mpc5xxx_ic_disable(irq);
+	__mpc5xxx_ic_disable(irq);
 	mpc5xxx_ic_ack(irq);
 }
 
 static void
 mpc5xxx_ic_end(unsigned int irq)
 {
-	if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
-		mpc5xxx_ic_enable(irq);
+	unsigned long flags;
+
+	if (!ipipe_root_domain_p ||
+	    !(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))) {
+		local_irq_save_hw_cond(flags);
+		__mpc5xxx_ic_enable(irq);
+		local_irq_restore_hw_cond(flags);
+	}
 }
 
 static struct hw_interrupt_type mpc5xxx_ic = {
diff --git a/arch/ppc/kernel/ppc4xx_pic.c b/arch/ppc/kernel/ppc4xx_pic.c
index 7fb2311..b110c13 100644
--- a/arch/ppc/kernel/ppc4xx_pic.c
+++ b/arch/ppc/kernel/ppc4xx_pic.c
@@ -103,13 +103,17 @@ ppc403_pic_get_irq(struct pt_regs *regs)
 static void
 ppc403_aic_enable(unsigned int irq)
 {
+	unsigned long flags;
 	int bit, word;
 
 	bit = irq & 0x1f;
 	word = irq >> 5;
 
+	local_irq_save_hw_cond(flags);
 	ppc_cached_irq_mask[word] |= (1 << (31 - bit));
 	mtdcr(DCRN_EXIER, ppc_cached_irq_mask[word]);
+	ipipe_irq_unlock(irq);
+	local_irq_restore_hw_cond(flags);
 }
 
 static void
@@ -120,8 +124,11 @@ ppc403_aic_disable(unsigned int irq)
 	bit = irq & 0x1f;
 	word = irq >> 5;
 
+	local_irq_save_hw_cond(flags);
 	ppc_cached_irq_mask[word] &= ~(1 << (31 - bit));
 	mtdcr(DCRN_EXIER, ppc_cached_irq_mask[word]);
+	ipipe_irq_lock(irq);
+	local_irq_restore_hw_cond(flags);
 }
 
 static void
@@ -148,6 +155,7 @@ ppc405_uic_enable(unsigned int irq)
 {
 	int bit, word;
 	irq_desc_t *desc = irq_desc + irq;
+	unsigned long flags;
 
 	bit = irq & 0x1f;
 	word = irq >> 5;
@@ -155,6 +163,7 @@ ppc405_uic_enable(unsigned int irq)
 #ifdef UIC_DEBUG
 	printk("ppc405_uic_enable - irq %d word %d bit 0x%x\n", irq, word, bit);
 #endif
+	local_irq_save_hw_cond(flags);
 	ppc_cached_irq_mask[word] |= 1 << (31 - bit);
 	switch (word) {
 	case 0:
@@ -182,12 +191,14 @@ ppc405_uic_enable(unsigned int irq)
 		desc->status = desc->status & ~IRQ_LEVEL;
 	break;
 	}
-
+	ipipe_irq_unlock(irq);
+	local_irq_restore_hw_cond(flags);
 }
 
 static void
 ppc405_uic_disable(unsigned int irq)
 {
+	unsigned long flags;
 	int bit, word;
 
 	bit = irq & 0x1f;
@@ -196,6 +207,7 @@ ppc405_uic_disable(unsigned int irq)
 	printk("ppc405_uic_disable - irq %d word %d bit 0x%x\n", irq, word,
 	       bit);
 #endif
+	local_irq_save_hw_cond(flags);
 	ppc_cached_irq_mask[word] &= ~(1 << (31 - bit));
 	switch (word) {
 	case 0:
@@ -205,6 +217,8 @@ ppc405_uic_disable(unsigned int irq)
 		mtdcr(DCRN_UIC_ER(UIC1), ppc_cached_irq_mask[word]);
 		break;
 	}
+	ipipe_irq_lock(irq);
+	local_irq_restore_hw_cond(flags);
 }
 
 static void
@@ -241,6 +255,7 @@ ppc405_uic_end(unsigned int irq)
 {
 	int bit, word;
 	unsigned int tr_bits;
+	unsigned long flags;
 
 	bit = irq & 0x1f;
 	word = irq >> 5;
@@ -249,6 +264,8 @@ ppc405_uic_end(unsigned int irq)
 	printk("ppc405_uic_end - irq %d word %d bit 0x%x\n", irq, word, bit);
 #endif
 
+	local_irq_save_hw_cond(flags);
+
 	switch (word) {
 	case 0:
 		tr_bits = mfdcr(DCRN_UIC_TR(UIC0));
@@ -274,7 +291,8 @@ ppc405_uic_end(unsigned int irq)
 		}
 	}
 
-	if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))) {
+	if (!__ipipe_root_domain_p ||
+	    !(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))) {
 		ppc_cached_irq_mask[word] |= 1 << (31 - bit);
 		switch (word) {
 		case 0:
@@ -285,6 +303,8 @@ ppc405_uic_end(unsigned int irq)
 			break;
 		}
 	}
+
+	local_irq_restore_hw_cond(flags);
 }
 
 static struct hw_interrupt_type ppc405_uic = {
diff --git a/arch/ppc/kernel/ppc_ksyms.c b/arch/ppc/kernel/ppc_ksyms.c
index d90d323..513c65d 100644
--- a/arch/ppc/kernel/ppc_ksyms.c
+++ b/arch/ppc/kernel/ppc_ksyms.c
@@ -314,6 +314,7 @@ EXPORT_SYMBOL(screen_info);
 #endif
 
 EXPORT_SYMBOL(__delay);
+#ifndef CONFIG_IPIPE
 EXPORT_SYMBOL(__sti);
 EXPORT_SYMBOL(__sti_end);
 EXPORT_SYMBOL(__cli);
@@ -322,6 +323,7 @@ EXPORT_SYMBOL(__save_flags_ptr);
 EXPORT_SYMBOL(__save_flags_ptr_end);
 EXPORT_SYMBOL(__restore_flags);
 EXPORT_SYMBOL(__restore_flags_end);
+#endif /* CONFIG_IPIPE */
 EXPORT_SYMBOL(timer_interrupt_intercept);
 EXPORT_SYMBOL(timer_interrupt);
 EXPORT_SYMBOL(do_IRQ_intercept);
diff --git a/arch/ppc/kernel/traps.c b/arch/ppc/kernel/traps.c
index aa4eaff..6f37807 100644
--- a/arch/ppc/kernel/traps.c
+++ b/arch/ppc/kernel/traps.c
@@ -211,6 +211,9 @@ MachineCheckException(struct pt_regs *regs)
 {
 	unsigned long reason = get_reason(regs);
 
+	if (ipipe_trap_notify(IPIPE_TRAP_MCE, regs))
+	    	return;
+
 	if (user_mode(regs)) {
 		_exception(SIGBUS, regs, BUS_ADRERR, regs->nip);
 		return;
@@ -314,6 +317,8 @@ SMIException(struct pt_regs *regs)
 void
 UnknownException(struct pt_regs *regs)
 {
+	if (ipipe_trap_notify(IPIPE_TRAP_UNKNOWN, regs))
+	    	return;
 	printk("Bad trap at PC: %lx, SR: %lx, vector=%lx    %s\n",
 	       regs->nip, regs->msr, regs->trap, print_tainted());
 	_exception(SIGTRAP, regs, 0, 0);
@@ -322,6 +327,8 @@ UnknownException(struct pt_regs *regs)
 void
 InstructionBreakpoint(struct pt_regs *regs)
 {
+	if (ipipe_trap_notify(IPIPE_TRAP_IABR, regs))
+	    	return;
 	if (debugger_iabr_match(regs))
 		return;
 	_exception(SIGTRAP, regs, TRAP_BRKPT, 0);
@@ -330,6 +337,8 @@ InstructionBreakpoint(struct pt_regs *regs)
 void
 RunModeException(struct pt_regs *regs)
 {
+	if (ipipe_trap_notify(IPIPE_TRAP_RM, regs))
+	    	return;
 	_exception(SIGTRAP, regs, 0, 0);
 }
 
@@ -382,6 +391,8 @@ static void emulate_single_step(struct pt_regs *regs)
 {
 	if (single_stepping(regs)) {
 		clear_single_step(regs);
+		if (ipipe_trap_notify(IPIPE_TRAP_SSTEP, regs))
+		    	return;
 		if (debugger_sstep(regs))
 			return;
 		_exception(SIGTRAP, regs, TRAP_TRACE, 0);
@@ -394,6 +405,9 @@ ProgramCheckException(struct pt_regs *regs)
 	unsigned int reason = get_reason(regs);
 	extern int do_mathemu(struct pt_regs *regs);
 
+	if (ipipe_trap_notify(IPIPE_TRAP_PCE, regs))
+	    	return;
+
 #ifdef CONFIG_MATH_EMULATION
 	/* (reason & REASON_ILLEGAL) would be the obvious thing here,
 	 * but there seems to be a hardware bug on the 405GP (RevD)
@@ -457,6 +471,8 @@ void
 SingleStepException(struct pt_regs *regs)
 {
 	regs->msr &= ~MSR_SE;  /* Turn off 'trace' bit */
+	if (ipipe_trap_notify(IPIPE_TRAP_SSTEP, regs))
+	    	return;
 	if (debugger_sstep(regs))
 		return;
 	_exception(SIGTRAP, regs, TRAP_TRACE, 0);
@@ -473,6 +489,8 @@ AlignmentException(struct pt_regs *regs)
 		emulate_single_step(regs);
 		return;
 	}
+	if (ipipe_trap_notify(IPIPE_TRAP_ALIGNMENT, regs))
+	    	return;
 	if (fixed == -EFAULT) {
 		/* fixed == -EFAULT means the operand address was bad */
 		if (user_mode(regs))
@@ -510,6 +528,9 @@ SoftwareEmulation(struct pt_regs *regs)
 	extern int Soft_emulate_8xx(struct pt_regs *);
 	int errcode;
 
+	if (ipipe_trap_notify(IPIPE_TRAP_SOFTEMU, regs))
+	    	return;
+
 	if (!user_mode(regs)) {
 		debugger(regs);
 		die("Kernel Mode Software FPU Emulation", regs, SIGFPE);
@@ -538,6 +559,9 @@ void DebugException(struct pt_regs *regs)
 {
 	unsigned long debug_status;
 
+	if (ipipe_trap_notify(IPIPE_TRAP_DEBUG, regs))
+	    	return;
+
 	debug_status = mfspr(SPRN_DBSR);
 
 	regs->msr &= ~MSR_DE;  /* Turn off 'debug' bit */
@@ -574,6 +598,8 @@ TAUException(struct pt_regs *regs)
 void
 AltivecAssistException(struct pt_regs *regs)
 {
+	if (ipipe_trap_notify(IPIPE_TRAP_ALTASSIST, regs))
+	    	return;
 	if (regs->msr & MSR_VEC)
 		giveup_altivec(current);
 	/* XXX quick hack for now: set the non-Java bit in the VSCR */
@@ -603,6 +629,9 @@ SPEFloatingPointException(struct pt_regs *regs)
 	int fpexc_mode;
 	int code = 0;
 
+	if (ipipe_trap_notify(IPIPE_TRAP_SPE, regs))
+	    	return;
+
 	spefscr = current->thread.spefscr;
 	fpexc_mode = current->thread.fpexc_mode;
 
diff --git a/arch/ppc/mm/fault.c b/arch/ppc/mm/fault.c
index 19fe78e..a3b36f8 100644
--- a/arch/ppc/mm/fault.c
+++ b/arch/ppc/mm/fault.c
@@ -99,7 +99,7 @@ void do_page_fault(struct pt_regs *regs, unsigned long address,
 		   unsigned long error_code)
 {
 	struct vm_area_struct * vma;
-	struct mm_struct *mm = current->mm;
+	struct mm_struct *mm;
 	siginfo_t info;
 	int code = SEGV_MAPERR;
 #if defined(CONFIG_4xx) || defined (CONFIG_BOOKE)
@@ -119,6 +119,11 @@ void do_page_fault(struct pt_regs *regs, unsigned long address,
 		is_write = error_code & 0x02000000;
 #endif /* CONFIG_4xx || CONFIG_BOOKE */
 
+	if (ipipe_trap_notify(IPIPE_TRAP_ACCESS, regs))
+	    	return;
+
+	mm = current->mm;
+
 #if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
 	if (debugger_fault_handler && regs->trap == 0x300) {
 		debugger_fault_handler(regs);
diff --git a/arch/ppc/platforms/icecube.c b/arch/ppc/platforms/icecube.c
index 40b6040..c35c2fa 100644
--- a/arch/ppc/platforms/icecube.c
+++ b/arch/ppc/platforms/icecube.c
@@ -46,6 +46,9 @@
 unsigned char __res[sizeof(bd_t)];
 #endif
 
+void
+mpc5xxx_progress(char *s, unsigned short hex);
+
 extern int mpc5xxx_setkeycode(unsigned int scancode, unsigned int keycode);
 extern int mpc5xxx_getkeycode(unsigned int scancode);
 extern int mpc5xxx_translate(unsigned char scancode, unsigned char *keycode,
diff --git a/include/asm-ppc/hw_irq.h b/include/asm-ppc/hw_irq.h
index 8f0e170..ba649dc 100644
--- a/include/asm-ppc/hw_irq.h
+++ b/include/asm-ppc/hw_irq.h
@@ -10,14 +10,95 @@ extern unsigned long do_IRQ_intercept;
 extern int timer_interrupt(struct pt_regs *);
 extern void ppc_irq_dispatch_handler(struct pt_regs *regs, int irq);
 
+#define irqs_disabled_hw()	((mfmsr() & MSR_EE) == 0)
+
+#ifdef CONFIG_IPIPE
+
+#include <asm/ipipe_base.h>
+
+void __ipipe_unstall_root(void);
+
+void __ipipe_restore_root(unsigned long x);
+
+#define __cli()		do { __ipipe_stall_root(); barrier(); } while(0)
+#define __sti()		do { barrier(); __ipipe_unstall_root(); } while(0)
+
+#define __save_flags_ptr(pflags)  \
+	do {							\
+		*(pflags) = (!__ipipe_test_root()) << 15;	\
+		barrier();					\
+	} while(0)
+
+#define __save_and_cli(flags) \
+	do {								\
+		(flags) = (!__ipipe_test_and_stall_root()) << 15;	\
+		barrier();						\
+	} while(0)
+#define __restore_flags(flags) 	__ipipe_restore_root(!((flags) & MSR_EE))
+
+static inline void local_irq_disable_hw_notrace(void)
+{
+	unsigned long msr = mfmsr();
+	mtmsr(msr & ~MSR_EE);
+	__asm__ __volatile__("": : :"memory");
+}
+
+static inline void local_irq_enable_hw_notrace(void)
+{
+	unsigned long msr;
+	__asm__ __volatile__("": : :"memory");
+	msr = mfmsr();
+	mtmsr(msr | MSR_EE);
+}
+
+static inline void local_irq_save_ptr_hw_notrace(unsigned long *flags)
+{
+	unsigned long msr;
+	msr = mfmsr();
+	*flags = msr;
+	mtmsr(msr & ~MSR_EE);
+	__asm__ __volatile__("": : :"memory");
+}
+
+#define local_save_flags_hw(x)			((x) = mfmsr())
+#define local_test_iflag_hw(x)			((x) & MSR_EE)
+#define raw_irqs_disabled_flags(x)		(!local_test_iflag_hw(x))
+
+#define local_irq_save_hw_notrace(x)		local_irq_save_ptr_hw_notrace(&(x))
+#define local_irq_restore_hw_notrace(x)		mtmsr(x)
+
+#define local_irq_save_hw(x)	local_irq_save_hw_notrace(x)
+#define local_irq_restore_hw(x)	local_irq_restore_hw_notrace(x)
+#define local_irq_enable_hw()	local_irq_enable_hw_notrace()
+#define local_irq_disable_hw()	local_irq_disable_hw_notrace()
+
+static inline unsigned long raw_mangle_irq_bits(int virt, unsigned long real)
+{
+	/* Merge virtual and real interrupt mask bits into a single
+	   32bit word. */
+	return (real & ~(1 << 31)) | ((virt != 0) << 31);
+}
+
+static inline int raw_demangle_irq_bits(unsigned long *x)
+{
+	int virt = (*x & (1 << 31)) != 0;
+	*x &= ~(1L << 31);
+	return virt;
+}
+
+#else /* !CONFIG_IPIPE */
+
 extern void __sti(void);
 extern void __cli(void);
 extern void __restore_flags(unsigned long);
 extern void __save_flags_ptr(unsigned long *);
 extern unsigned long __sti_end, __cli_end, __restore_flags_end, __save_flags_ptr_end;
 
-#define __save_flags(flags) __save_flags_ptr((unsigned long *)&flags)
 #define __save_and_cli(flags) ({__save_flags(flags);__cli();})
+
+#endif /* CONFIG_IPIPE */
+
+#define __save_flags(flags) __save_flags_ptr((unsigned long *)&flags)
 #define __save_and_sti(flags) ({__save_flags(flags);__sti();})
 
 extern void do_lost_interrupts(unsigned long);
diff --git a/include/asm-ppc/ipipe.h b/include/asm-ppc/ipipe.h
new file mode 100644
index 0000000..a49c72a
--- /dev/null
+++ b/include/asm-ppc/ipipe.h
@@ -0,0 +1,183 @@
+/*
+ *   include/asm-ppc/ipipe.h
+ *
+ *   I-pipe backport from 2.6 series - Copyright (C) 2009 Philippe Gerum.
+ *   I-pipe 32/64bit merge - Copyright (C) 2007 Philippe Gerum.
+ *   I-pipe PA6T support - Copyright (C) 2007 Philippe Gerum.
+ *   I-pipe 64-bit PowerPC port - Copyright (C) 2005 Heikki Lindholm.
+ *   I-pipe PowerPC support - Copyright (C) 2002-2005 Philippe Gerum.
+ *
+ *   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, Inc., 675 Mass Ave, Cambridge MA 02139,
+ *   USA; either version 2 of the License, or (at your option) any later
+ *   version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ASM_PPC_IPIPE_H
+#define __ASM_PPC_IPIPE_H
+
+#include <linux/config.h>
+
+#ifdef CONFIG_IPIPE
+
+#include <linux/list.h>
+#include <linux/cache.h>
+#include <linux/threads.h>
+#include <asm/ptrace.h>
+#include <asm/hw_irq.h>
+#include <asm/irq.h>
+#include <asm/bitops.h>
+
+#define IPIPE_ARCH_STRING	"2.0-00"
+#define IPIPE_MAJOR_NUMBER	2
+#define IPIPE_MINOR_NUMBER	0
+#define IPIPE_PATCH_NUMBER	0
+
+#define prepare_arch_switch(next)			\
+	do {						\
+		ipipe_schedule_notify(current ,next);	\
+		local_irq_disable_hw();			\
+	} while(0)
+
+#define task_hijacked(p)						\
+	({								\
+		int __x__ = __ipipe_root_domain_p;			\
+		__clear_bit(IPIPE_SYNC_FLAG, &ipipe_root_cpudom_var(status)); \
+		if (__x__) local_irq_enable_hw(); !__x__;		\
+	})
+
+struct ipipe_domain;
+
+struct ipipe_sysinfo {
+
+	int ncpus;			/* Number of CPUs on board */
+	u64 cpufreq;			/* CPU frequency (in Hz) */
+
+	/* Arch-dependent block */
+
+	struct {
+		unsigned tmirq;		/* Decrementer virtual IRQ */
+		u64 tmfreq;		/* Timebase frequency */
+	} archdep;
+};
+
+extern unsigned tb_ticks_per_jiffy;
+#define ipipe_cpu_freq()	(HZ * tb_ticks_per_jiffy)
+#define ipipe_read_tsc(t)					\
+	({							\
+		unsigned long __tbu;				\
+		__asm__ __volatile__ ("1: mftbu %0\n"		\
+				      "mftb %1\n"		\
+				      "mftbu %2\n"		\
+				      "cmpw %2,%0\n"		\
+				      "bne- 1b\n"		\
+				      :"=r" (((unsigned long *)&t)[0]),	\
+				       "=r" (((unsigned long *)&t)[1]),	\
+				       "=r" (__tbu));			\
+		t;							\
+	})
+#define ipipe_tsc2ns(t)		((((unsigned long)(t)) * 1000) / (ipipe_cpu_freq() / 1000000))
+#define ipipe_tsc2us(t)						\
+	({							\
+		unsigned long long delta = (t);			\
+		do_div(delta, ipipe_cpu_freq()/1000000+1);	\
+		(unsigned long)delta;				\
+	})
+#define __ipipe_read_timebase()					\
+	({							\
+ 	unsigned long long t;					\
+ 	ipipe_read_tsc(t);					\
+ 	t;							\
+ 	})
+
+/* Private interface -- Internal use only */
+
+#define __ipipe_check_platform()		do { } while(0)
+#define __ipipe_enable_irq(irq)			enable_irq(irq)
+#define __ipipe_disable_irq(irq)		disable_irq(irq)
+#define __ipipe_disable_irqdesc(ipd, irq)	do { } while(0)
+
+void __ipipe_enable_irqdesc(struct ipipe_domain *ipd, unsigned irq);
+
+void __ipipe_init_platform(void);
+
+void __ipipe_enable_pipeline(void);
+
+void __ipipe_end_irq(unsigned irq);
+
+#define __ipipe_hook_critical_ipi(ipd)	do { } while(0)
+
+DECLARE_PER_CPU(struct pt_regs, __ipipe_tick_regs);
+
+void __ipipe_handle_irq(int irq, struct pt_regs *regs);
+
+static inline void ipipe_handle_chained_irq(unsigned int irq)
+{
+	struct pt_regs regs;	/* dummy */
+
+	__ipipe_handle_irq(irq, &regs);
+}
+
+struct irq_desc;
+void __ipipe_ack_level_irq(unsigned irq, struct irq_desc *desc);
+void __ipipe_end_level_irq(unsigned irq, struct irq_desc *desc);
+void __ipipe_ack_edge_irq(unsigned irq, struct irq_desc *desc);
+void __ipipe_end_edge_irq(unsigned irq, struct irq_desc *desc);
+
+void __ipipe_serial_debug(const char *fmt, ...);
+
+#define __ipipe_tick_irq	IPIPE_TIMER_VIRQ
+
+static inline unsigned long __ipipe_ffnz(unsigned long ul)
+{
+	__asm__ __volatile__("cntlzw %0, %1":"=r"(ul):"r"(ul & (-ul)));
+	return 31 - ul;
+}
+
+/*
+ * When running handlers, enable hw interrupts for all domains but the
+ * one heading the pipeline, so that IRQs can never be significantly
+ * deferred for the latter.
+ */
+#define __ipipe_run_isr(ipd, irq)					\
+do {									\
+	if (!__ipipe_pipeline_head_p(ipd))				\
+		local_irq_enable_hw();					\
+	if (ipd == ipipe_root_domain)					\
+		if (likely(!ipipe_virtual_irq_p(irq)))			\
+			ipd->irqs[irq].handler(irq, NULL);		\
+		else 							\
+			ipd->irqs[irq].handler(irq, ipd->irqs[irq].cookie);\
+									\
+	else {								\
+		__clear_bit(IPIPE_SYNC_FLAG, &ipipe_cpudom_var(ipd, status)); \
+		ipd->irqs[irq].handler(irq, ipd->irqs[irq].cookie);	\
+		__set_bit(IPIPE_SYNC_FLAG, &ipipe_cpudom_var(ipd, status)); \
+	}								\
+	local_irq_disable_hw();						\
+} while(0)
+
+#define __ipipe_syscall_watched_p(p, sc)	\
+	(((p)->flags & PF_EVNOTIFY) || (unsigned long)sc >= NR_syscalls)
+
+#define __ipipe_root_tick_p(regs)	((regs)->msr & MSR_EE)
+
+#else /* !CONFIG_IPIPE */
+
+#define prepare_arch_switch(next)	do { } while(0)	     
+#define task_hijacked(p)	0
+#define ipipe_handle_chained_irq(irq)	generic_handle_irq(irq)
+
+#endif /* CONFIG_IPIPE */
+
+#endif /* !__ASM_PPC_IPIPE_H */
diff --git a/include/asm-ppc/ipipe_base.h b/include/asm-ppc/ipipe_base.h
new file mode 100644
index 0000000..f77f39a
--- /dev/null
+++ b/include/asm-ppc/ipipe_base.h
@@ -0,0 +1,108 @@
+/* -*- linux-c -*-
+ * include/asm-powerpc/ipipe_base.h
+ *
+ * Copyright (C) 2007 Philippe Gerum.
+ *
+ * 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, Inc., 675 Mass Ave, Cambridge MA 02139,
+ * USA; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ASM_POWERPC_IPIPE_BASE_H
+#define __ASM_POWERPC_IPIPE_BASE_H
+
+#include <linux/config.h>
+
+#ifdef CONFIG_IPIPE
+
+#define IPIPE_NR_XIRQS		NR_IRQS
+#ifdef CONFIG_PPC64
+#define IPIPE_IRQ_ISHIFT	6		/* 64-bit arch. */
+#else
+#define IPIPE_IRQ_ISHIFT	5		/* 32-bit arch. */
+#endif
+
+/* 
+ * The first virtual interrupt is reserved for the timer (see
+ * __ipipe_init_platform). 
+ */
+#define IPIPE_TIMER_VIRQ	IPIPE_VIRQ_BASE
+
+#define ipipe_processor_id()	0
+
+/* traps */
+#define IPIPE_TRAP_ACCESS	 0	/* Data or instruction access exception */
+#define IPIPE_TRAP_ALIGNMENT	 1	/* Alignment exception */
+#define IPIPE_TRAP_ALTUNAVAIL	 2	/* Altivec unavailable */
+#define IPIPE_TRAP_PCE		 3	/* Program check exception */
+#define IPIPE_TRAP_MCE		 4	/* Machine check exception */
+#define IPIPE_TRAP_UNKNOWN	 5	/* Unknown exception */
+#define IPIPE_TRAP_IABR		 6	/* Instruction breakpoint */
+#define IPIPE_TRAP_RM		 7	/* Run mode exception */
+#define IPIPE_TRAP_SSTEP	 8	/* Single-step exception */
+#define IPIPE_TRAP_NREC		 9	/* Non-recoverable exception */
+#define IPIPE_TRAP_SOFTEMU	10	/* Software emulation */
+#define IPIPE_TRAP_DEBUG	11	/* Debug exception */
+#define IPIPE_TRAP_SPE		12	/* SPE exception */
+#define IPIPE_TRAP_ALTASSIST	13	/* Altivec assist exception */
+#define IPIPE_TRAP_CACHE	14	/* Cache-locking exception (FSL) */
+#define IPIPE_TRAP_KFPUNAVAIL	15	/* FP unavailable exception */
+#define IPIPE_NR_FAULTS		16
+/* Pseudo-vectors used for kernel events */
+#define IPIPE_FIRST_EVENT		IPIPE_NR_FAULTS
+#define IPIPE_EVENT_SYSCALL		(IPIPE_FIRST_EVENT)
+#define IPIPE_EVENT_SCHEDULE		(IPIPE_FIRST_EVENT + 1)
+#define IPIPE_EVENT_SIGWAKE		(IPIPE_FIRST_EVENT + 2)
+#define IPIPE_EVENT_SETSCHED		(IPIPE_FIRST_EVENT + 3)
+#define IPIPE_EVENT_INIT		(IPIPE_FIRST_EVENT + 4)
+#define IPIPE_EVENT_EXIT		(IPIPE_FIRST_EVENT + 5)
+#define IPIPE_EVENT_CLEANUP		(IPIPE_FIRST_EVENT + 6)
+#define IPIPE_LAST_EVENT		IPIPE_EVENT_CLEANUP
+#define IPIPE_NR_EVENTS			(IPIPE_LAST_EVENT + 1)
+
+#ifndef __ASSEMBLY__
+
+#include <asm/bitops.h>
+
+#if __GNUC__ >= 4
+/* Alias to ipipe_root_cpudom_var(status) */
+extern unsigned long __ipipe_root_status;
+#else
+extern unsigned long *const __ipipe_root_status_addr;
+#define __ipipe_root_status	(*__ipipe_root_status_addr)
+#endif
+
+static __inline__ void __ipipe_stall_root(void)
+{
+	volatile unsigned long *p = &__ipipe_root_status;
+	set_bit(0, p);
+}
+
+static __inline__ unsigned long __ipipe_test_and_stall_root(void)
+{
+	volatile unsigned long *p = &__ipipe_root_status;
+	return test_and_set_bit(0, p);
+}
+
+static __inline__ unsigned long __ipipe_test_root(void)
+{
+	volatile unsigned long *p = &__ipipe_root_status;
+	return test_bit(0, p);
+}
+
+#endif /* !__ASSEMBLY__ */
+
+#endif /* CONFIG_IPIPE */
+
+#endif	/* !__ASM_POWERPC_IPIPE_BASE_H */
diff --git a/include/asm-ppc/mmu_context.h b/include/asm-ppc/mmu_context.h
index 6c6c141..38c33de 100644
--- a/include/asm-ppc/mmu_context.h
+++ b/include/asm-ppc/mmu_context.h
@@ -148,12 +148,15 @@ static inline void get_mmu_context(struct mm_struct *mm)
  */
 static inline void destroy_context(struct mm_struct *mm)
 {
+	unsigned long flags;
 	if (mm->context != NO_CONTEXT) {
+		local_irq_save_hw_cond(flags);
 		clear_bit(mm->context, context_map);
 		mm->context = NO_CONTEXT;
 #ifdef FEW_CONTEXTS
 		atomic_inc(&nr_free_contexts);
 #endif
+		local_irq_restore_hw_cond(flags);
 	}
 }
 
@@ -171,9 +174,12 @@ static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next,
  */
 static inline void activate_mm(struct mm_struct *active_mm, struct mm_struct *mm)
 {
+	unsigned long flags;
+	local_irq_save_hw_cond(flags);
 	current->thread.pgdir = mm->pgd;
 	get_mmu_context(mm);
 	set_context(mm->context, mm->pgd);
+	local_irq_restore_hw_cond(flags);
 }
 
 extern void mmu_context_init(void);
diff --git a/include/linux/ipipe.h b/include/linux/ipipe.h
new file mode 100644
index 0000000..967204c
--- /dev/null
+++ b/include/linux/ipipe.h
@@ -0,0 +1,673 @@
+/* -*- linux-c -*-
+ * include/linux/ipipe.h
+ *
+ * Copyright (C) 2002-2007 Philippe Gerum.
+ *
+ * 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, Inc., 675 Mass Ave, Cambridge MA 02139,
+ * USA; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __LINUX_IPIPE_H
+#define __LINUX_IPIPE_H
+
+#include <linux/config.h>
+#include <linux/spinlock.h>
+#include <linux/cache.h>
+#include <linux/sys.h>
+#include <linux/linkage.h>
+#include <linux/ipipe_base.h>
+#include <linux/ipipe_compat.h>
+#include <linux/ipipe_percpu.h>
+#include <asm/ipipe.h>
+
+#ifdef CONFIG_IPIPE_DEBUG_CONTEXT
+
+#include <asm/system.h>
+
+static inline int ipipe_disable_context_check(int cpu)
+{
+	return xchg(&per_cpu(ipipe_percpu_context_check, cpu), 0);
+}
+
+static inline void ipipe_restore_context_check(int cpu, int old_state)
+{
+	per_cpu(ipipe_percpu_context_check, cpu) = old_state;
+}
+
+static inline void ipipe_context_check_off(void)
+{
+	int cpu;
+	for_each_online_cpu(cpu)
+		per_cpu(ipipe_percpu_context_check, cpu) = 0;
+}
+
+#else	/* !CONFIG_IPIPE_DEBUG_CONTEXT */
+
+static inline int ipipe_disable_context_check(int cpu)
+{
+	return 0;
+}
+
+static inline void ipipe_restore_context_check(int cpu, int old_state) { }
+
+static inline void ipipe_context_check_off(void) { }
+
+#endif	/* !CONFIG_IPIPE_DEBUG_CONTEXT */
+
+#ifdef CONFIG_IPIPE
+
+/*
+ * Sanity check: IPIPE_VIRQ_BASE depends on CONFIG_NR_CPUS, and if the
+ * latter gets too large, we fail to map the virtual interrupts.
+ */
+#if IPIPE_VIRQ_BASE / BITS_PER_LONG > BITS_PER_LONG
+#error "CONFIG_NR_CPUS is too large, please lower it."
+#endif
+
+#define IPIPE_VERSION_STRING	IPIPE_ARCH_STRING
+#define IPIPE_RELEASE_NUMBER	((IPIPE_MAJOR_NUMBER << 16) | \
+				 (IPIPE_MINOR_NUMBER <<  8) | \
+				 (IPIPE_PATCH_NUMBER))
+
+#ifndef BROKEN_BUILTIN_RETURN_ADDRESS
+#define __BUILTIN_RETURN_ADDRESS0 ((unsigned long)__builtin_return_address(0))
+#define __BUILTIN_RETURN_ADDRESS1 ((unsigned long)__builtin_return_address(1))
+#endif /* !BUILTIN_RETURN_ADDRESS */
+
+#define IPIPE_ROOT_PRIO		100
+#define IPIPE_ROOT_ID		0
+#define IPIPE_ROOT_NPTDKEYS	4	/* Must be <= BITS_PER_LONG */
+
+#define IPIPE_RESET_TIMER	0x1
+#define IPIPE_GRAB_TIMER	0x2
+
+/* Global domain flags */
+#define IPIPE_SPRINTK_FLAG	0	/* Synchronous printk() allowed */
+#define IPIPE_AHEAD_FLAG	1	/* Domain always heads the pipeline */
+
+/* Interrupt control bits */
+#define IPIPE_HANDLE_FLAG	0
+#define IPIPE_PASS_FLAG		1
+#define IPIPE_ENABLE_FLAG	2
+#define IPIPE_DYNAMIC_FLAG	IPIPE_HANDLE_FLAG
+#define IPIPE_STICKY_FLAG	3
+#define IPIPE_SYSTEM_FLAG	4
+#define IPIPE_LOCK_FLAG		5
+#define IPIPE_WIRED_FLAG	6
+#define IPIPE_EXCLUSIVE_FLAG	7
+
+#define IPIPE_HANDLE_MASK	(1 << IPIPE_HANDLE_FLAG)
+#define IPIPE_PASS_MASK		(1 << IPIPE_PASS_FLAG)
+#define IPIPE_ENABLE_MASK	(1 << IPIPE_ENABLE_FLAG)
+#define IPIPE_DYNAMIC_MASK	IPIPE_HANDLE_MASK
+#define IPIPE_STICKY_MASK	(1 << IPIPE_STICKY_FLAG)
+#define IPIPE_SYSTEM_MASK	(1 << IPIPE_SYSTEM_FLAG)
+#define IPIPE_LOCK_MASK		(1 << IPIPE_LOCK_FLAG)
+#define IPIPE_WIRED_MASK	(1 << IPIPE_WIRED_FLAG)
+#define IPIPE_EXCLUSIVE_MASK	(1 << IPIPE_EXCLUSIVE_FLAG)
+
+#define IPIPE_DEFAULT_MASK	(IPIPE_HANDLE_MASK|IPIPE_PASS_MASK)
+#define IPIPE_STDROOT_MASK	(IPIPE_HANDLE_MASK|IPIPE_PASS_MASK|IPIPE_SYSTEM_MASK)
+
+#define IPIPE_EVENT_SELF        0x80000000
+
+#define IPIPE_NR_CPUS		NR_CPUS
+
+/* This accessor assumes hw IRQs are off on SMP; allows assignment. */
+#define __ipipe_current_domain	__ipipe_get_cpu_var(ipipe_percpu_domain)
+/* This read-only accessor makes sure that hw IRQs are off on SMP. */
+#define ipipe_current_domain				\
+	({						\
+		struct ipipe_domain *__ipd__;		\
+		unsigned long __flags__;		\
+		local_irq_save_hw_smp(__flags__);	\
+		__ipd__ = __ipipe_current_domain;	\
+		local_irq_restore_hw_smp(__flags__);	\
+		__ipd__;				\
+	})
+
+#define ipipe_virtual_irq_p(irq)	((irq) >= IPIPE_VIRQ_BASE && \
+					 (irq) < IPIPE_NR_IRQS)
+
+#define IPIPE_SAME_HANDLER	((ipipe_irq_handler_t)(-1))
+
+struct irq_desc;
+
+typedef void (*ipipe_irq_ackfn_t)(unsigned irq, struct irq_desc *desc);
+
+typedef int (*ipipe_event_handler_t)(unsigned event,
+				     struct ipipe_domain *from,
+				     void *data);
+struct ipipe_domain {
+
+	int slot;			/* Slot number in percpu domain data array. */
+	struct list_head p_link;	/* Link in pipeline */
+	ipipe_event_handler_t evhand[IPIPE_NR_EVENTS]; /* Event handlers. */
+	unsigned long long evself;	/* Self-monitored event bits. */
+
+	struct {
+		unsigned long control;
+		ipipe_irq_ackfn_t acknowledge;
+		ipipe_irq_handler_t handler;
+		void *cookie;
+	} ____cacheline_aligned irqs[IPIPE_NR_IRQS];
+
+	int priority;
+	void *pdd;
+	unsigned long flags;
+	unsigned domid;
+	const char *name;
+	struct semaphore mutex;
+};
+
+#define IPIPE_HEAD_PRIORITY	(-1) /* For domains always heading the pipeline */
+
+struct ipipe_domain_attr {
+
+	unsigned domid;		/* Domain identifier -- Magic value set by caller */
+	const char *name;	/* Domain name -- Warning: won't be dup'ed! */
+	int priority;		/* Priority in interrupt pipeline */
+	void (*entry) (void);	/* Domain entry point */
+	void *pdd;		/* Per-domain (opaque) data pointer */
+};
+
+#define __ipipe_irq_cookie(ipd, irq)		(ipd)->irqs[irq].cookie
+#define __ipipe_irq_handler(ipd, irq)		(ipd)->irqs[irq].handler
+#define __ipipe_cpudata_irq_hits(ipd, cpu, irq)	ipipe_percpudom(ipd, irqall, cpu)[irq]
+
+#define get_irq_desc(irq)	(irq_desc + irq)
+
+extern unsigned __ipipe_printk_virq;
+
+extern unsigned long __ipipe_virtual_irq_map;
+
+extern struct list_head __ipipe_pipeline;
+
+extern int __ipipe_event_monitors[];
+
+/* Private interface */
+
+void ipipe_init(void);
+
+#ifdef CONFIG_PROC_FS
+void ipipe_init_proc(void);
+
+#ifdef CONFIG_IPIPE_TRACE
+void __ipipe_init_tracer(void);
+#else /* !CONFIG_IPIPE_TRACE */
+#define __ipipe_init_tracer()       do { } while(0)
+#endif /* CONFIG_IPIPE_TRACE */
+
+#else	/* !CONFIG_PROC_FS */
+#define ipipe_init_proc()	do { } while(0)
+#endif	/* CONFIG_PROC_FS */
+
+void __ipipe_init_stage(struct ipipe_domain *ipd);
+
+void __ipipe_cleanup_domain(struct ipipe_domain *ipd);
+
+void __ipipe_add_domain_proc(struct ipipe_domain *ipd);
+
+void __ipipe_remove_domain_proc(struct ipipe_domain *ipd);
+
+void __ipipe_flush_printk(unsigned irq, void *cookie);
+
+void __ipipe_walk_pipeline(struct list_head *pos);
+
+void __ipipe_pend_irq(unsigned irq, struct list_head *head);
+
+int __ipipe_dispatch_event(unsigned event, void *data);
+
+void __ipipe_dispatch_wired_nocheck(struct ipipe_domain *head, unsigned irq);
+
+void __ipipe_dispatch_wired(struct ipipe_domain *head, unsigned irq);
+
+void __ipipe_sync_stage(unsigned long syncmask);
+
+void __ipipe_set_irq_pending(struct ipipe_domain *ipd, unsigned irq);
+
+void __ipipe_lock_irq(struct ipipe_domain *ipd, int cpu, unsigned irq);
+
+void __ipipe_unlock_irq(struct ipipe_domain *ipd, unsigned irq);
+
+void __ipipe_pin_range_globally(unsigned long start, unsigned long end);
+
+/* Must be called hw IRQs off. */
+static inline void ipipe_irq_lock(unsigned irq)
+{
+	__ipipe_lock_irq(__ipipe_current_domain, ipipe_processor_id(), irq);
+}
+
+/* Must be called hw IRQs off. */
+static inline void ipipe_irq_unlock(unsigned irq)
+{
+	__ipipe_unlock_irq(__ipipe_current_domain, irq);
+}
+
+#ifndef __ipipe_sync_pipeline
+#define __ipipe_sync_pipeline(syncmask) __ipipe_sync_stage(syncmask)
+#endif
+
+#ifndef __ipipe_run_irqtail
+#define __ipipe_run_irqtail() do { } while(0)
+#endif
+
+#define __ipipe_pipeline_head_p(ipd) (&(ipd)->p_link == __ipipe_pipeline.next)
+
+/*
+ * Keep the following as a macro, so that client code could check for
+ * the support of the invariant pipeline head optimization.
+ */
+#define __ipipe_pipeline_head() \
+	list_entry(__ipipe_pipeline.next, struct ipipe_domain, p_link)
+
+#define local_irq_enable_hw_cond()		local_irq_enable_hw()
+#define local_irq_disable_hw_cond()		local_irq_disable_hw()
+#define local_irq_save_hw_cond(flags)		local_irq_save_hw(flags)
+#define local_irq_restore_hw_cond(flags)	local_irq_restore_hw(flags)
+
+#define local_irq_save_hw_smp(flags)		do { (void)(flags); } while(0)
+#define local_irq_restore_hw_smp(flags)		do { } while(0)
+
+#define local_irq_save_full(vflags, rflags)		\
+	do {						\
+		local_irq_save(vflags);			\
+		local_irq_save_hw(rflags);		\
+	} while(0)
+
+#define local_irq_restore_full(vflags, rflags)		\
+	do {						\
+		local_irq_restore_hw(rflags);		\
+		local_irq_restore(vflags);		\
+	} while(0)
+
+static inline void __local_irq_restore_nosync(unsigned long x)
+{
+	struct ipipe_percpu_domain_data *p = ipipe_root_cpudom_ptr();
+
+	if (raw_irqs_disabled_flags(x))
+		set_bit(IPIPE_STALL_FLAG, &p->status);
+	else
+		clear_bit(IPIPE_STALL_FLAG, &p->status);
+}
+
+static inline void local_irq_restore_nosync(unsigned long x)
+{
+	unsigned long flags;
+	local_irq_save_hw_smp(flags);
+	__local_irq_restore_nosync(x);
+	local_irq_restore_hw_smp(flags);
+}
+
+#define __ipipe_root_domain_p	(__ipipe_current_domain == ipipe_root_domain)
+#define ipipe_root_domain_p	(ipipe_current_domain == ipipe_root_domain)
+
+static inline int __ipipe_event_monitored_p(int ev)
+{
+	if (__ipipe_event_monitors[ev] > 0)
+		return 1;
+
+	return (ipipe_current_domain->evself & (1LL << ev)) != 0;
+}
+
+#define ipipe_sigwake_notify(p)	\
+do {					\
+	if (((p)->flags & PF_EVNOTIFY) && __ipipe_event_monitored_p(IPIPE_EVENT_SIGWAKE)) \
+		__ipipe_dispatch_event(IPIPE_EVENT_SIGWAKE, p);		\
+} while(0)
+
+#define ipipe_exit_notify(p)	\
+do {				\
+	if (((p)->flags & PF_EVNOTIFY) && __ipipe_event_monitored_p(IPIPE_EVENT_EXIT)) \
+		__ipipe_dispatch_event(IPIPE_EVENT_EXIT, p);		\
+} while(0)
+
+#define ipipe_setsched_notify(p)	\
+do {					\
+	if (((p)->flags & PF_EVNOTIFY) && __ipipe_event_monitored_p(IPIPE_EVENT_SETSCHED)) \
+		__ipipe_dispatch_event(IPIPE_EVENT_SETSCHED, p);	\
+} while(0)
+
+#define ipipe_schedule_notify(prev, next)				\
+do {									\
+	if ((((prev)->flags|(next)->flags) & PF_EVNOTIFY) &&		\
+	    __ipipe_event_monitored_p(IPIPE_EVENT_SCHEDULE))		\
+		__ipipe_dispatch_event(IPIPE_EVENT_SCHEDULE,next);	\
+} while(0)
+
+#define ipipe_trap_notify(ex, regs)					\
+({									\
+	unsigned long __flags__;					\
+	int __ret__ = 0;						\
+	local_irq_save_hw_smp(__flags__);				\
+	if ((test_bit(IPIPE_NOSTACK_FLAG, &ipipe_this_cpudom_var(status)) || \
+	     ((current)->flags & PF_EVNOTIFY)) &&			\
+	    __ipipe_event_monitored_p(ex)) {				\
+		local_irq_restore_hw_smp(__flags__);			\
+		__ret__ = __ipipe_dispatch_event(ex, regs);		\
+	} else								\
+		local_irq_restore_hw_smp(__flags__);			\
+	__ret__;							\
+})
+
+static inline void ipipe_init_notify(struct task_struct *p)
+{
+	if (__ipipe_event_monitored_p(IPIPE_EVENT_INIT))
+		__ipipe_dispatch_event(IPIPE_EVENT_INIT, p);
+}
+
+struct mm_struct;
+
+static inline void ipipe_cleanup_notify(struct mm_struct *mm)
+{
+	if (__ipipe_event_monitored_p(IPIPE_EVENT_CLEANUP))
+		__ipipe_dispatch_event(IPIPE_EVENT_CLEANUP, mm);
+}
+
+/* Public interface */
+
+int ipipe_register_domain(struct ipipe_domain *ipd,
+			  struct ipipe_domain_attr *attr);
+
+int ipipe_unregister_domain(struct ipipe_domain *ipd);
+
+void ipipe_suspend_domain(void);
+
+int ipipe_virtualize_irq(struct ipipe_domain *ipd,
+			 unsigned irq,
+			 ipipe_irq_handler_t handler,
+			 void *cookie,
+			 ipipe_irq_ackfn_t acknowledge,
+			 unsigned modemask);
+
+int ipipe_control_irq(unsigned irq,
+		      unsigned clrmask,
+		      unsigned setmask);
+
+unsigned ipipe_alloc_virq(void);
+
+int ipipe_free_virq(unsigned virq);
+
+int ipipe_trigger_irq(unsigned irq);
+
+static inline void __ipipe_propagate_irq(unsigned irq)
+{
+	struct list_head *next = __ipipe_current_domain->p_link.next;
+	if (next == &ipipe_root.p_link) {
+		/* Fast path: root must handle all interrupts. */
+		__ipipe_set_irq_pending(&ipipe_root, irq);
+		return;
+	}
+	__ipipe_pend_irq(irq, next);
+}
+
+static inline void __ipipe_schedule_irq(unsigned irq)
+{
+	__ipipe_pend_irq(irq, &__ipipe_current_domain->p_link);
+}
+
+static inline void __ipipe_schedule_irq_head(unsigned irq)
+{
+	__ipipe_set_irq_pending(__ipipe_pipeline_head(), irq);
+}
+
+static inline void __ipipe_schedule_irq_root(unsigned irq)
+{
+	__ipipe_set_irq_pending(&ipipe_root, irq);
+}
+
+static inline void ipipe_propagate_irq(unsigned irq)
+{
+	unsigned long flags;
+
+	local_irq_save_hw(flags);
+	__ipipe_propagate_irq(irq);
+	local_irq_restore_hw(flags);
+}
+
+static inline void ipipe_schedule_irq(unsigned irq)
+{
+	unsigned long flags;
+
+	local_irq_save_hw(flags);
+	__ipipe_schedule_irq(irq);
+	local_irq_restore_hw(flags);
+}
+
+static inline void ipipe_schedule_irq_head(unsigned irq)
+{
+	unsigned long flags;
+
+	local_irq_save_hw(flags);
+	__ipipe_schedule_irq_head(irq);
+	local_irq_restore_hw(flags);
+}
+
+static inline void ipipe_schedule_irq_root(unsigned irq)
+{
+	unsigned long flags;
+
+	local_irq_save_hw(flags);
+	__ipipe_schedule_irq_root(irq);
+	local_irq_restore_hw(flags);
+}
+
+void ipipe_stall_pipeline_from(struct ipipe_domain *ipd);
+
+unsigned long ipipe_test_and_stall_pipeline_from(struct ipipe_domain *ipd);
+
+unsigned long ipipe_test_and_unstall_pipeline_from(struct ipipe_domain *ipd);
+
+static inline void ipipe_unstall_pipeline_from(struct ipipe_domain *ipd)
+{
+	ipipe_test_and_unstall_pipeline_from(ipd);
+}
+
+void ipipe_restore_pipeline_from(struct ipipe_domain *ipd,
+					  unsigned long x);
+
+static inline unsigned long ipipe_test_pipeline_from(struct ipipe_domain *ipd)
+{
+	return test_bit(IPIPE_STALL_FLAG, &ipipe_cpudom_var(ipd, status));
+}
+
+static inline void ipipe_stall_pipeline_head(void)
+{
+	local_irq_disable_hw();
+	__set_bit(IPIPE_STALL_FLAG, &ipipe_head_cpudom_var(status));
+}
+
+static inline unsigned long ipipe_test_and_stall_pipeline_head(void)
+{
+	local_irq_disable_hw();
+	return __test_and_set_bit(IPIPE_STALL_FLAG, &ipipe_head_cpudom_var(status));
+}
+
+void ipipe_unstall_pipeline_head(void);
+
+void __ipipe_restore_pipeline_head(unsigned long x);
+
+static inline void ipipe_restore_pipeline_head(unsigned long x)
+{
+	/* On some archs, __test_and_set_bit() might return different
+	 * truth value than test_bit(), so we test the exclusive OR of
+	 * both statuses, assuming that the lowest bit is always set in
+	 * the truth value (if this is wrong, the failed optimization will
+	 * be caught in __ipipe_restore_pipeline_head() if
+	 * CONFIG_DEBUG_KERNEL is set). */
+	if ((x ^ test_bit(IPIPE_STALL_FLAG, &ipipe_head_cpudom_var(status))) & 1)
+		__ipipe_restore_pipeline_head(x);
+}
+
+#define ipipe_unstall_pipeline() \
+	ipipe_unstall_pipeline_from(ipipe_current_domain)
+
+#define ipipe_test_and_unstall_pipeline() \
+	ipipe_test_and_unstall_pipeline_from(ipipe_current_domain)
+
+#define ipipe_test_pipeline() \
+	ipipe_test_pipeline_from(ipipe_current_domain)
+
+#define ipipe_test_and_stall_pipeline() \
+	ipipe_test_and_stall_pipeline_from(ipipe_current_domain)
+
+#define ipipe_stall_pipeline() \
+	ipipe_stall_pipeline_from(ipipe_current_domain)
+
+#define ipipe_restore_pipeline(x) \
+	ipipe_restore_pipeline_from(ipipe_current_domain, (x))
+
+void ipipe_init_attr(struct ipipe_domain_attr *attr);
+
+int ipipe_get_sysinfo(struct ipipe_sysinfo *sysinfo);
+
+unsigned long ipipe_critical_enter(void (*syncfn) (void));
+
+void ipipe_critical_exit(unsigned long flags);
+
+static inline void ipipe_set_printk_sync(struct ipipe_domain *ipd)
+{
+	set_bit(IPIPE_SPRINTK_FLAG, &ipd->flags);
+}
+
+static inline void ipipe_set_printk_async(struct ipipe_domain *ipd)
+{
+	clear_bit(IPIPE_SPRINTK_FLAG, &ipd->flags);
+}
+
+static inline void ipipe_set_foreign_stack(struct ipipe_domain *ipd)
+{
+	/* Must be called hw interrupts off. */
+	__set_bit(IPIPE_NOSTACK_FLAG, &ipipe_cpudom_var(ipd, status));
+}
+
+static inline void ipipe_clear_foreign_stack(struct ipipe_domain *ipd)
+{
+	/* Must be called hw interrupts off. */
+	__clear_bit(IPIPE_NOSTACK_FLAG, &ipipe_cpudom_var(ipd, status));
+}
+
+#ifndef ipipe_safe_current
+#define ipipe_safe_current()					\
+({								\
+	struct task_struct *p;					\
+	unsigned long flags;					\
+	local_irq_save_hw_smp(flags);				\
+	p = test_bit(IPIPE_NOSTACK_FLAG,			\
+		     &ipipe_this_cpudom_var(status)) ? &init_task : current; \
+	local_irq_restore_hw_smp(flags);			\
+	p; \
+})
+#endif
+
+ipipe_event_handler_t ipipe_catch_event(struct ipipe_domain *ipd,
+					unsigned event,
+					ipipe_event_handler_t handler);
+
+cpumask_t ipipe_set_irq_affinity(unsigned irq,
+				 cpumask_t cpumask);
+
+int ipipe_send_ipi(unsigned ipi,
+		   cpumask_t cpumask);
+
+int ipipe_setscheduler_root(struct task_struct *p,
+			    int policy,
+			    int prio);
+
+int ipipe_reenter_root(struct task_struct *prev,
+		       int policy,
+		       int prio);
+
+int ipipe_alloc_ptdkey(void);
+
+int ipipe_free_ptdkey(int key);
+
+int ipipe_set_ptd(int key,
+		  void *value);
+
+void *ipipe_get_ptd(int key);
+
+int ipipe_disable_ondemand_mappings(struct task_struct *tsk);
+
+static inline void ipipe_nmi_enter(void)
+{
+	int cpu = ipipe_processor_id();
+
+	per_cpu(ipipe_nmi_saved_root, cpu) = ipipe_root_cpudom_var(status);
+	__set_bit(IPIPE_STALL_FLAG, &ipipe_root_cpudom_var(status));
+
+#ifdef CONFIG_IPIPE_DEBUG_CONTEXT
+	per_cpu(ipipe_saved_context_check_state, cpu) =
+		ipipe_disable_context_check(cpu);
+#endif /* CONFIG_IPIPE_DEBUG_CONTEXT */
+}
+
+static inline void ipipe_nmi_exit(void)
+{
+	int cpu = ipipe_processor_id();
+
+#ifdef CONFIG_IPIPE_DEBUG_CONTEXT
+	ipipe_restore_context_check
+		(cpu, per_cpu(ipipe_saved_context_check_state, cpu));
+#endif /* CONFIG_IPIPE_DEBUG_CONTEXT */
+
+	if (!test_bit(IPIPE_STALL_FLAG, &per_cpu(ipipe_nmi_saved_root, cpu)))
+		__clear_bit(IPIPE_STALL_FLAG, &ipipe_root_cpudom_var(status));
+}
+
+#else	/* !CONFIG_IPIPE */
+
+#define ipipe_init()			do { } while(0)
+#define ipipe_suspend_domain()		do { } while(0)
+#define ipipe_sigwake_notify(p)		do { } while(0)
+#define ipipe_setsched_notify(p)	do { } while(0)
+#define ipipe_init_notify(p)		do { } while(0)
+#define ipipe_exit_notify(p)		do { } while(0)
+#define ipipe_cleanup_notify(mm)	do { } while(0)
+#define ipipe_trap_notify(t,r)		0
+#define ipipe_init_proc()		do { } while(0)
+
+static inline void __ipipe_pin_range_globally(unsigned long start,
+					      unsigned long end)
+{
+}
+
+#define local_irq_enable_hw_cond()		do { } while(0)
+#define local_irq_disable_hw_cond()		do { } while(0)
+#define local_irq_save_hw_cond(flags)		do { (void)(flags); } while(0)
+#define local_irq_restore_hw_cond(flags)	do { } while(0)
+#define local_irq_save_hw_smp(flags)		do { (void)(flags); } while(0)
+#define local_irq_restore_hw_smp(flags)		do { } while(0)
+
+#define ipipe_irq_lock(irq)		do { } while(0)
+#define ipipe_irq_unlock(irq)		do { } while(0)
+
+#define __ipipe_root_domain_p		1
+#define ipipe_root_domain_p		1
+#define ipipe_safe_current		current
+#define ipipe_processor_id()		smp_processor_id()
+
+#define ipipe_nmi_enter()		do { } while (0)
+#define ipipe_nmi_exit()		do { } while (0)
+
+#define local_irq_disable_head()	local_irq_disable()
+
+#define local_irq_save_full(vflags, rflags)	do { (void)(vflags); local_irq_save(rflags); } while(0)
+#define local_irq_restore_full(vflags, rflags)	do { (void)(vflags); local_irq_restore(rflags); } while(0)
+#define local_irq_restore_nosync(vflags)	local_irq_restore(vflags)
+
+#endif	/* CONFIG_IPIPE */
+
+#endif	/* !__LINUX_IPIPE_H */
diff --git a/include/linux/ipipe_base.h b/include/linux/ipipe_base.h
new file mode 100644
index 0000000..f30a77a
--- /dev/null
+++ b/include/linux/ipipe_base.h
@@ -0,0 +1,79 @@
+/* -*- linux-c -*-
+ * include/linux/ipipe_base.h
+ *
+ * Copyright (C) 2002-2007 Philippe Gerum.
+ *               2007 Jan Kiszka.
+ *
+ * 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, Inc., 675 Mass Ave, Cambridge MA 02139,
+ * USA; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __LINUX_IPIPE_BASE_H
+#define __LINUX_IPIPE_BASE_H
+
+#include <linux/config.h>
+
+#ifdef CONFIG_IPIPE
+
+#include <asm/ipipe_base.h>
+
+/* Number of virtual IRQs */
+#define IPIPE_NR_VIRQS		BITS_PER_LONG
+/* First virtual IRQ # */
+#define IPIPE_VIRQ_BASE		(((IPIPE_NR_XIRQS + BITS_PER_LONG - 1) / BITS_PER_LONG) * BITS_PER_LONG)
+/* Total number of IRQ slots */
+#define IPIPE_NR_IRQS		(IPIPE_VIRQ_BASE + IPIPE_NR_VIRQS)
+/* Number of indirect words needed to map the whole IRQ space. */
+#define IPIPE_IRQ_IWORDS	((IPIPE_NR_IRQS + BITS_PER_LONG - 1) / BITS_PER_LONG)
+#define IPIPE_IRQ_IMASK		(BITS_PER_LONG - 1)
+#define IPIPE_IRQMASK_ANY	(~0L)
+#define IPIPE_IRQMASK_VIRT	(IPIPE_IRQMASK_ANY << (IPIPE_VIRQ_BASE / BITS_PER_LONG))
+
+/* Per-cpu pipeline status */
+#define IPIPE_STALL_FLAG	0	/* Stalls a pipeline stage -- guaranteed at bit #0 */
+#define IPIPE_SYNC_FLAG		1	/* The interrupt syncer is running for the domain */
+#define IPIPE_NOSTACK_FLAG	2	/* Domain currently runs on a foreign stack */
+
+#define IPIPE_STALL_MASK	(1L << IPIPE_STALL_FLAG)
+#define IPIPE_SYNC_MASK		(1L << IPIPE_SYNC_FLAG)
+#define IPIPE_NOSTACK_MASK	(1L << IPIPE_NOSTACK_FLAG)
+
+typedef void (*ipipe_irq_handler_t)(unsigned irq,
+				    void *cookie);
+
+extern struct ipipe_domain ipipe_root;
+
+#define ipipe_root_domain (&ipipe_root)
+
+void __ipipe_unstall_root(void);
+
+void __ipipe_restore_root(unsigned long x);
+
+#ifdef CONFIG_IPIPE_DEBUG_CONTEXT
+void ipipe_check_context(struct ipipe_domain *border_ipd);
+#else /* !CONFIG_IPIPE_DEBUG_CONTEXT */
+static inline void ipipe_check_context(struct ipipe_domain *border_ipd) { }
+#endif /* !CONFIG_IPIPE_DEBUG_CONTEXT */
+
+/* Generic features */
+
+#define __IPIPE_FEATURE_FASTPEND_IRQ       1
+#define __IPIPE_FEATURE_TRACE_EVENT	   1
+
+#else /* !CONFIG_IPIPE */
+#define ipipe_check_context(ipd)	do { } while(0)
+#endif	/* CONFIG_IPIPE */
+
+#endif	/* !__LINUX_IPIPE_BASE_H */
diff --git a/include/linux/ipipe_compat.h b/include/linux/ipipe_compat.h
new file mode 100644
index 0000000..01eb437
--- /dev/null
+++ b/include/linux/ipipe_compat.h
@@ -0,0 +1,56 @@
+/* -*- linux-c -*-
+ * include/linux/ipipe_compat.h
+ *
+ * Copyright (C) 2007 Philippe Gerum.
+ *
+ * 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, Inc., 675 Mass Ave, Cambridge MA 02139,
+ * USA; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __LINUX_IPIPE_COMPAT_H
+#define __LINUX_IPIPE_COMPAT_H
+
+#include <linux/config.h>
+
+#ifdef CONFIG_IPIPE_COMPAT
+/*
+ * OBSOLETE: defined only for backward compatibility. Will be removed
+ * in future releases, please update client code accordingly.
+ */
+
+#ifdef CONFIG_SMP
+#define ipipe_declare_cpuid	int cpuid
+#define ipipe_load_cpuid()	do { \
+					cpuid = ipipe_processor_id();	\
+				} while(0)
+#define ipipe_lock_cpu(flags)	do { \
+					local_irq_save_hw(flags); \
+					cpuid = ipipe_processor_id(); \
+				} while(0)
+#define ipipe_unlock_cpu(flags)	local_irq_restore_hw(flags)
+#define ipipe_get_cpu(flags)	ipipe_lock_cpu(flags)
+#define ipipe_put_cpu(flags)	ipipe_unlock_cpu(flags)
+#else /* !CONFIG_SMP */
+#define ipipe_declare_cpuid	const int cpuid = 0
+#define ipipe_load_cpuid()	do { } while(0)
+#define ipipe_lock_cpu(flags)	local_irq_save_hw(flags)
+#define ipipe_unlock_cpu(flags)	local_irq_restore_hw(flags)
+#define ipipe_get_cpu(flags)	do { (void)(flags); } while(0)
+#define ipipe_put_cpu(flags)	do { } while(0)
+#endif /* CONFIG_SMP */
+
+#endif /* CONFIG_IPIPE_COMPAT */
+
+#endif	/* !__LINUX_IPIPE_COMPAT_H */
diff --git a/include/linux/ipipe_percpu.h b/include/linux/ipipe_percpu.h
new file mode 100644
index 0000000..eace4da
--- /dev/null
+++ b/include/linux/ipipe_percpu.h
@@ -0,0 +1,138 @@
+/*   -*- linux-c -*-
+ *   include/linux/ipipe_percpu.h
+ *
+ *   Copyright (C) 2007 Philippe Gerum.
+ *
+ *   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, Inc., 675 Mass Ave, Cambridge MA 02139,
+ *   USA; either version 2 of the License, or (at your option) any later
+ *   version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __LINUX_IPIPE_PERCPU_H
+#define __LINUX_IPIPE_PERCPU_H
+
+#include <linux/config.h>
+
+#ifdef CONFIG_IPIPE
+
+#include <asm/ptrace.h>
+#include <asm/irq.h>
+
+#ifdef CONFIG_SMP
+#error "SMP not supported - upgrade to I-pipe/2.6 series"
+#endif
+
+/*
+ * Emulate the needed bits from Linux 2.6 percpu & cpumask supports in
+ * the !CONFIG_SMP case, so that the rest of the I-pipe code can be
+ * backported with no change related to SMP support.
+ */
+
+/* Lifted from include/asm-generic/percpu.h */
+#define per_cpu_var(var)		per_cpu__##var
+#define per_cpu(var, cpu)		(*((void)(cpu), &per_cpu_var(var)))
+#define __get_cpu_var(var)		per_cpu_var(var)
+#define __raw_get_cpu_var(var)		per_cpu_var(var)
+#define __ipipe_get_cpu_var(var)	__raw_get_cpu_var(var)
+
+/* Lifted from include/linux/percpu.h */
+#define DECLARE_PER_CPU(type, name)	extern __typeof__(type) per_cpu_var(name)
+#define DEFINE_PER_CPU(type, name)	__typeof__(type) per_cpu__##name
+#define EXPORT_PER_CPU_SYMBOL(var)	EXPORT_SYMBOL(per_cpu__##var)
+
+#define for_each_possible_cpu(cpu)	for ((cpu) = 0; (cpu) < 1; (cpu)++)
+#define for_each_online_cpu(cpu)	for_each_possible_cpu(cpu)
+#define num_online_cpus()		1
+
+typedef unsigned long cpumask_t;
+
+#define CPU_MASK_NONE			0
+#define CPU_MASK_ALL			(~0UL)
+#define cpus_andnot(dst,src1,src2)	((dst) = (src1) & ~(src2))
+#define cpus_and(dst,src1,src2)		((dst) = (src1) & (src2))
+#define cpus_equal(src1,src2)		((src1) == (src2))
+#define cpus_empty(src)			((src) == 0)
+#define cpus_clear(dst)			((dst) = 0)
+#define cpus_weight(src)		hweight32(src)
+#define cpu_clear(cpu,dst)		((dst) &= ~(1UL << (cpu)))
+#define cpu_isset(cpu,src)		((src) & (1UL << (cpu)))
+#define cpu_set(cpu,dst)		((dst) |= (1UL << (cpu)))
+#define cpumask_of_cpu(cpu)		(1UL << (cpu))
+#define cpu_test_and_set(cpu,dst)	test_and_set_bit(cpu,&dst)
+#define cpu_test_and_clear(cpu,dst)	test_and_clear_bit(cpu,&dst)
+#define first_cpu(src)			(ffs(src) - 1)
+
+#define NR_IPIPE_DOMAINS  4
+
+struct ipipe_domain;
+
+struct ipipe_percpu_domain_data {
+	unsigned long status;	/* <= Must be first in struct. */
+	unsigned long irqpend_himask;
+	unsigned long irqpend_lomask[IPIPE_IRQ_IWORDS];
+	unsigned long irqheld_mask[IPIPE_IRQ_IWORDS];
+	unsigned long irqall[IPIPE_NR_IRQS];
+	u64 evsync;
+};
+
+/*
+ * CAREFUL: all accessors based on __raw_get_cpu_var() you may find in
+ * this file should be used only while hw interrupts are off, to
+ * prevent from CPU migration regardless of the running domain.
+ */
+#ifdef CONFIG_SMP
+#define ipipe_percpudom_ptr(ipd, cpu)	\
+	(&per_cpu(ipipe_percpu_darray, cpu)[(ipd)->slot])
+#define ipipe_cpudom_ptr(ipd)	\
+	(&__ipipe_get_cpu_var(ipipe_percpu_darray)[(ipd)->slot])
+#else
+DECLARE_PER_CPU(struct ipipe_percpu_domain_data *, ipipe_percpu_daddr[NR_IPIPE_DOMAINS]);
+#define ipipe_percpudom_ptr(ipd, cpu)	\
+	(per_cpu(ipipe_percpu_daddr, cpu)[(ipd)->slot])
+#define ipipe_cpudom_ptr(ipd)	\
+	(__ipipe_get_cpu_var(ipipe_percpu_daddr)[(ipd)->slot])
+#endif
+#define ipipe_percpudom(ipd, var, cpu)	(ipipe_percpudom_ptr(ipd, cpu)->var)
+#define ipipe_cpudom_var(ipd, var)	(ipipe_cpudom_ptr(ipd)->var)
+
+#define IPIPE_ROOT_SLOT			0
+#define IPIPE_HEAD_SLOT			(NR_IPIPE_DOMAINS - 1)
+
+DECLARE_PER_CPU(struct ipipe_percpu_domain_data, ipipe_percpu_darray[NR_IPIPE_DOMAINS]);
+
+DECLARE_PER_CPU(struct ipipe_domain *, ipipe_percpu_domain);
+
+DECLARE_PER_CPU(unsigned long, ipipe_nmi_saved_root);
+
+#ifdef CONFIG_IPIPE_DEBUG_CONTEXT
+DECLARE_PER_CPU(int, ipipe_percpu_context_check);
+DECLARE_PER_CPU(int, ipipe_saved_context_check_state);
+#endif
+
+#define ipipe_root_cpudom_ptr(var)	\
+	(&__ipipe_get_cpu_var(ipipe_percpu_darray)[IPIPE_ROOT_SLOT])
+
+#define ipipe_root_cpudom_var(var)	ipipe_root_cpudom_ptr()->var
+
+#define ipipe_this_cpudom_var(var)	\
+	ipipe_cpudom_var(__ipipe_current_domain, var)
+
+#define ipipe_head_cpudom_ptr()		\
+	(&__ipipe_get_cpu_var(ipipe_percpu_darray)[IPIPE_HEAD_SLOT])
+
+#define ipipe_head_cpudom_var(var)	ipipe_head_cpudom_ptr()->var
+
+#endif /* CONFIG_IPIPE */
+
+#endif	/* !__LINUX_IPIPE_PERCPU_H */
diff --git a/include/linux/ipipe_trace.h b/include/linux/ipipe_trace.h
new file mode 100644
index 0000000..e69de29
diff --git a/include/linux/irq.h b/include/linux/irq.h
index 2791f0e..9347920 100644
--- a/include/linux/irq.h
+++ b/include/linux/irq.h
@@ -56,7 +56,7 @@ typedef struct hw_interrupt_type  hw_irq_controller;
  *
  * Pad this out to 32 bytes for cache and indexing reasons.
  */
-typedef struct {
+typedef struct irq_desc {
 	unsigned int status;		/* IRQ status */
 	hw_irq_controller *handler;
 	struct irqaction *action;	/* IRQ action list */
diff --git a/include/linux/linkage.h b/include/linux/linkage.h
index 23b9ae4..4dfb3fc 100644
--- a/include/linux/linkage.h
+++ b/include/linux/linkage.h
@@ -60,4 +60,8 @@
 
 #endif
 
+#ifndef notrace
+#define notrace		__attribute__((no_instrument_function))
+#endif
+
 #endif
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 90bd418..405121d 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -13,6 +13,7 @@ extern unsigned long event;
 #include <linux/times.h>
 #include <linux/timex.h>
 #include <linux/rbtree.h>
+#include <linux/ipipe.h>
 
 #include <asm/system.h>
 #include <asm/semaphore.h>
@@ -91,6 +92,11 @@ extern int last_pid;
 #define TASK_UNINTERRUPTIBLE	2
 #define TASK_ZOMBIE		4
 #define TASK_STOPPED		8
+#ifdef CONFIG_IPIPE
+#define TASK_NOWAKEUP		512
+#else  /* !CONFIG_IPIPE */
+#define TASK_NOWAKEUP		0
+#endif /* CONFIG_IPIPE */
 
 #define __set_task_state(tsk, state_value)		\
 	do { (tsk)->state = (state_value); } while (0)
@@ -144,6 +150,15 @@ extern void update_process_times(int user);
 extern void update_one_process(struct task_struct *p, unsigned long user,
 			       unsigned long system, int cpu);
 
+#ifdef CONFIG_IPIPE
+extern void update_root_process_times(struct pt_regs *regs);
+#else  /* !CONFIG_IPIPE */
+static inline void update_root_process_times(struct pt_regs *regs)
+{
+	update_process_times(user_mode(regs));
+}
+#endif /* CONFIG_IPIPE */
+
 #define	MAX_SCHEDULE_TIMEOUT	LONG_MAX
 extern signed long FASTCALL(schedule_timeout(signed long timeout));
 asmlinkage void schedule(void);
@@ -418,6 +433,9 @@ struct task_struct {
 #ifdef CONFIG_SHOW_PNAME_ON_DISPLAY
 	char PName[8]; /* 8 characters from the ProcessName for the Display */
 #endif
+#ifdef CONFIG_IPIPE
+	void *ptd[IPIPE_ROOT_NPTDKEYS];
+#endif
 };
 
 /*
@@ -427,6 +445,11 @@ struct task_struct {
 					/* Not implemented yet, only for 486*/
 #define PF_STARTING	0x00000002	/* being created */
 #define PF_EXITING	0x00000004	/* getting shut down */
+#ifdef CONFIG_IPIPE
+#define PF_EVNOTIFY	0x00000020	/* Notify other domains about internal events */
+#else
+#define PF_EVNOTIFY	0
+#endif /* CONFIG_IPIPE */
 #define PF_FORKNOEXEC	0x00000040	/* forked but didn't exec */
 #define PF_SUPERPRIV	0x00000100	/* used super-user privileges */
 #define PF_DUMPCORE	0x00000200	/* dumped core */
diff --git a/include/linux/spinlock.h b/include/linux/spinlock.h
index bae52d3..1462732 100644
--- a/include/linux/spinlock.h
+++ b/include/linux/spinlock.h
@@ -5,12 +5,56 @@
 
 #include <asm/system.h>
 
+#undef TYPE_EQUAL
+#define TYPE_EQUAL(lock, type) \
+	__builtin_types_compatible_p(typeof(lock), type *)
+
+#define PICK_SPINLOCK_IRQSAVE(lock, flags)				\
+do {									\
+	if (TYPE_EQUAL((lock), __ipipe_spinlock_t))			\
+		(flags) = __ipipe_spin_lock_irqsave(&((__ipipe_spinlock_t *)(lock))->__lock); \
+	else if (TYPE_EQUAL((lock), spinlock_t)) {			\
+		local_irq_save(flags);					\
+		spin_lock((spinlock_t *)(lock));			\
+	}								\
+} while (0)
+
+#define PICK_SPINUNLOCK_IRQRESTORE(lock, flags)				\
+do {									\
+	if (TYPE_EQUAL((lock), __ipipe_spinlock_t))			\
+		__ipipe_spin_unlock_irqrestore(&((__ipipe_spinlock_t *)(lock))->__lock, flags); \
+	else if (TYPE_EQUAL((lock), spinlock_t)) {			\
+		spin_unlock((spinlock_t *)(lock));			\
+		local_irq_restore(flags);				\
+	}								\
+} while (0)
+
+#define PICK_SPINLOCK_IRQ(lock)						\
+do {									\
+	if (TYPE_EQUAL((lock), __ipipe_spinlock_t))			\
+		__ipipe_spin_lock_irq(&((__ipipe_spinlock_t *)(lock))->__lock); \
+	else if (TYPE_EQUAL((lock), spinlock_t)) {			\
+		local_irq_disable();					\
+		spin_lock((spinlock_t *)(lock));			\
+	}								\
+} while (0)
+
+#define PICK_SPINUNLOCK_IRQ(lock)					\
+do {									\
+	if (TYPE_EQUAL((lock), __ipipe_spinlock_t))			\
+		__ipipe_spin_unlock_irq(&((__ipipe_spinlock_t *)(lock))->__lock); \
+	else if (TYPE_EQUAL((lock), spinlock_t)) {			\
+		spin_unlock((spinlock_t *)(lock));			\
+		local_irq_enable();					\
+	}								\
+} while (0)
+
 /*
  * These are the generic versions of the spinlocks and read-write
  * locks..
  */
-#define spin_lock_irqsave(lock, flags)		do { local_irq_save(flags);       spin_lock(lock); } while (0)
-#define spin_lock_irq(lock)			do { local_irq_disable();         spin_lock(lock); } while (0)
+#define spin_lock_irqsave(lock, flags)		PICK_SPINLOCK_IRQSAVE(lock, flags)
+#define spin_lock_irq(lock)			PICK_SPINLOCK_IRQ(lock)
 #define spin_lock_bh(lock)			do { local_bh_disable();          spin_lock(lock); } while (0)
 
 #define read_lock_irqsave(lock, flags)		do { local_irq_save(flags);       read_lock(lock); } while (0)
@@ -21,8 +65,8 @@
 #define write_lock_irq(lock)			do { local_irq_disable();        write_lock(lock); } while (0)
 #define write_lock_bh(lock)			do { local_bh_disable();         write_lock(lock); } while (0)
 
-#define spin_unlock_irqrestore(lock, flags)	do { spin_unlock(lock);  local_irq_restore(flags); } while (0)
-#define spin_unlock_irq(lock)			do { spin_unlock(lock);  local_irq_enable();       } while (0)
+#define spin_unlock_irqrestore(lock, flags)	PICK_SPINUNLOCK_IRQRESTORE(lock, flags)
+#define spin_unlock_irq(lock)			PICK_SPINUNLOCK_IRQ(lock)
 #define spin_unlock_bh(lock)			do { spin_unlock(lock);  local_bh_enable();        } while (0)
 
 #define read_unlock_irqrestore(lock, flags)	do { read_unlock(lock);  local_irq_restore(flags); } while (0)
@@ -153,6 +197,44 @@ typedef struct {
 
 #endif /* !SMP */
 
+typedef struct {
+	spinlock_t __lock;
+} __ipipe_spinlock_t;
+
+#ifdef CONFIG_IPIPE
+#define IPIPE_SPIN_LOCK_UNLOCKED	{ .__lock = SPIN_LOCK_UNLOCKED }
+#define ipipe_spinlock_t		__ipipe_spinlock_t
+#define IPIPE_DEFINE_SPINLOCK(x)	ipipe_spinlock_t x = IPIPE_SPIN_LOCK_UNLOCKED
+#define IPIPE_DECLARE_SPINLOCK(x)	extern ipipe_spinlock_t x
+
+void __ipipe_spin_lock_irq(spinlock_t *lock);
+void __ipipe_spin_unlock_irq(spinlock_t *lock);
+unsigned long __ipipe_spin_lock_irqsave(spinlock_t *lock);
+void __ipipe_spin_unlock_irqrestore(spinlock_t *lock, unsigned long x);
+void __ipipe_spin_unlock_irqbegin(ipipe_spinlock_t *lock);
+void __ipipe_spin_unlock_irqcomplete(unsigned long x);
+#define spin_lock_irqsave_cond(lock, flags) \
+	spin_lock_irqsave(lock, flags)
+#define spin_unlock_irqrestore_cond(lock, flags) \
+	spin_unlock_irqrestore(lock, flags)
+#else /* !CONFIG_IPIPE */
+#define ipipe_spinlock_t		spinlock_t
+#define IPIPE_SPIN_LOCK_UNLOCKED	SPIN_LOCK_UNLOCKED
+#define IPIPE_DEFINE_SPINLOCK(x)	spinlock_t x
+#define IPIPE_DECLARE_SPINLOCK(x)	extern spinlock_t x
+
+#define spin_lock_irqsave_cond(lock, flags) \
+	do { (void)(flags); spin_lock(lock); } while(0)
+#define spin_unlock_irqrestore_cond(lock, flags) \
+	spin_unlock(lock)
+#define __ipipe_spin_lock_irq(lock)		do { } while(0)
+#define __ipipe_spin_unlock_irq(lock)		do { } while(0)
+#define __ipipe_spin_lock_irqsave(lock)		0
+#define __ipipe_spin_unlock_irqrestore(lock, x)	do { (void)(x); } while(0)
+#define __ipipe_spin_unlock_irqbegin(lock)	do { } while(0)
+#define __ipipe_spin_unlock_irqcomplete(x)	do { (void)(x); } while(0)
+#endif /* CONFIG_IPIPE */
+
 /* "lock on reference count zero" */
 #ifndef ATOMIC_DEC_AND_LOCK
 #include <asm/atomic.h>
diff --git a/init/main.c b/init/main.c
index 510efe9..b0c63c7 100644
--- a/init/main.c
+++ b/init/main.c
@@ -412,6 +412,11 @@ asmlinkage void __init start_kernel(void)
 	softirq_init();
 	time_init();
 	WATCHDOG_RESET;
+ 	/*
+ 	 * We need to wait for the interrupt and time subsystems to be
+ 	 * initialized before enabling the pipeline.
+ 	 */
+  	ipipe_init();
 
 	/*
 	 * HACK ALERT! This is early. We're enabling the console before
@@ -548,6 +553,7 @@ static void __init do_basic_setup(void)
 #ifdef CONFIG_SYSCTL
 	sysctl_init();
 #endif
+ 	ipipe_init_proc();
 
 	/*
 	 * Ok, at this point all CPU's should be initialized, so
diff --git a/kernel/Makefile b/kernel/Makefile
index 02c4d3a..3985e7a 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -16,6 +16,7 @@ obj-y     = sched.o dma.o fork.o exec_domain.o panic.o printk.o \
 	    sysctl.o acct.o capability.o ptrace.o timer.o user.o \
 	    signal.o sys.o kmod.o context.o
 
+obj-$(CONFIG_IPIPE) += ipipe/ipipe.o
 obj-$(CONFIG_UID16) += uid16.o
 obj-$(CONFIG_MODULES) += ksyms.o
 obj-$(CONFIG_PM) += pm.o
@@ -29,4 +30,6 @@ ifneq ($(CONFIG_IA64),y)
 CFLAGS_sched.o := $(PROFILING) -fno-omit-frame-pointer
 endif
 
+subdir-$(CONFIG_IPIPE) += ipipe
+
 include $(TOPDIR)/Rules.make
diff --git a/kernel/exit.c b/kernel/exit.c
index 35283a3..33cbfc8 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -440,6 +440,7 @@ fake_volatile:
 	acct_process(code);
 #endif
 	__exit_mm(tsk);
+ 	ipipe_exit_notify(tsk);
 
 	lock_kernel();
 	sem_exit();
diff --git a/kernel/fork.c b/kernel/fork.c
index c146ad4..9b14c0d 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -271,6 +271,7 @@ struct mm_struct * mm_alloc(void)
 void __mmdrop(struct mm_struct *mm)
 {
 	BUG_ON(mm == &init_mm);
+	BUG_ON(irqs_disabled_hw());
 	pgd_free(mm->pgd);
 	check_pgt_cache();
 	destroy_context(mm);
@@ -284,6 +285,7 @@ void mmput(struct mm_struct *mm)
 {
 	if (atomic_dec_and_lock(&mm->mm_users, &mmlist_lock)) {
 		extern struct mm_struct *swap_mm;
+		ipipe_cleanup_notify(mm);
 		if (swap_mm == mm)
 			swap_mm = list_entry(mm->mmlist.next, struct mm_struct, mmlist);
 		list_del(&mm->mmlist);
@@ -598,7 +600,7 @@ static inline void copy_flags(unsigned long clone_flags, struct task_struct *p)
 {
 	unsigned long new_flags = p->flags;
 
-	new_flags &= ~(PF_SUPERPRIV | PF_USEDFPU);
+	new_flags &= ~(PF_SUPERPRIV | PF_USEDFPU | PF_EVNOTIFY);
 	new_flags |= PF_FORKNOEXEC;
 	if (!(clone_flags & CLONE_PTRACE))
 		p->ptrace = 0;
@@ -815,6 +817,10 @@ int do_fork(unsigned long clone_flags, unsigned long stack_start,
 	nr_threads++;
 	write_unlock_irq(&tasklist_lock);
 
+#ifdef CONFIG_IPIPE
+	memset(p->ptd, 0, sizeof(p->ptd));
+#endif /* CONFIG_IPIPE */
+
 	if (p->ptrace & PT_PTRACED)
 		send_sig(SIGSTOP, p, 1);
 #ifdef CONFIG_SHOW_PNAME_ON_DISPLAY
diff --git a/kernel/ipipe/Makefile b/kernel/ipipe/Makefile
new file mode 100644
index 0000000..6669e37
--- /dev/null
+++ b/kernel/ipipe/Makefile
@@ -0,0 +1,7 @@
+O_TARGET := ipipe.o
+
+obj-y := core.o
+
+export-objs := $(obj-y)
+
+include $(TOPDIR)/Rules.make
diff --git a/kernel/ipipe/core.c b/kernel/ipipe/core.c
new file mode 100644
index 0000000..394b7df
--- /dev/null
+++ b/kernel/ipipe/core.c
@@ -0,0 +1,1513 @@
+/* -*- linux-c -*-
+ * linux/kernel/ipipe/core.c
+ *
+ * Copyright (C) 2002-2005 Philippe Gerum.
+ *
+ * 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, Inc., 675 Mass Ave, Cambridge MA 02139,
+ * USA; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Architecture-independent I-PIPE core support.
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/bitops.h>
+#include <linux/prefetch.h>
+#ifdef CONFIG_PROC_FS
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#endif	/* CONFIG_PROC_FS */
+#include <linux/irq.h>
+
+static int __ipipe_ptd_key_count;
+
+static unsigned long __ipipe_ptd_key_map;
+
+static unsigned long __ipipe_domain_slot_map;
+
+struct ipipe_domain ipipe_root;
+
+/*
+ * Create an alias to the unique root status, so that arch-dep code
+ * may get simple and easy access to this percpu variable.  We also
+ * create an array of pointers to the percpu domain data; this tends
+ * to produce a better code when reaching non-root domains. We make
+ * sure that the early boot code would be able to dereference the
+ * pointer to the root domain data safely by statically initializing
+ * its value (local_irq*() routines depend on this).
+ */
+#if __GNUC__ >= 4
+extern unsigned long __ipipe_root_status
+__attribute__((alias(__stringify(__raw_get_cpu_var(ipipe_percpu_darray)))));
+EXPORT_SYMBOL(__ipipe_root_status);
+#else /* __GNUC__ < 4 */
+/*
+ * Work around a GCC 3.x issue making alias symbols unusable as
+ * constant initializers.
+ */
+unsigned long *const __ipipe_root_status_addr =
+	&__raw_get_cpu_var(ipipe_percpu_darray)[IPIPE_ROOT_SLOT].status;
+EXPORT_SYMBOL(__ipipe_root_status_addr);
+#endif /* __GNUC__ < 4 */
+
+DEFINE_PER_CPU(struct ipipe_percpu_domain_data *, ipipe_percpu_daddr[NR_IPIPE_DOMAINS]) =
+{ [IPIPE_ROOT_SLOT] = (struct ipipe_percpu_domain_data *)&__raw_get_cpu_var(ipipe_percpu_darray) };
+EXPORT_PER_CPU_SYMBOL(ipipe_percpu_daddr);
+
+DEFINE_PER_CPU(struct ipipe_percpu_domain_data, ipipe_percpu_darray[NR_IPIPE_DOMAINS]) =
+{ [IPIPE_ROOT_SLOT] = { .status = IPIPE_STALL_MASK } }; /* Root domain stalled on each CPU at startup. */
+
+DEFINE_PER_CPU(struct ipipe_domain *, ipipe_percpu_domain) = { &ipipe_root };
+
+DEFINE_PER_CPU(unsigned long, ipipe_nmi_saved_root); /* Copy of root status during NMI */
+
+static IPIPE_DEFINE_SPINLOCK(__ipipe_pipelock);
+
+LIST_HEAD(__ipipe_pipeline);
+
+unsigned long __ipipe_virtual_irq_map;
+
+unsigned __ipipe_printk_virq;
+
+int __ipipe_event_monitors[IPIPE_NR_EVENTS];
+
+/*
+ * ipipe_init() -- Initialization routine of the IPIPE layer. Called
+ * by the host kernel early during the boot procedure.
+ */
+void __init ipipe_init(void)
+{
+	struct ipipe_domain *ipd = &ipipe_root;
+
+	__ipipe_check_platform();	/* Do platform dependent checks first. */
+
+	/*
+	 * A lightweight registration code for the root domain. We are
+	 * running on the boot CPU, hw interrupts are off, and
+	 * secondary CPUs are still lost in space.
+	 */
+
+	/* Reserve percpu data slot #0 for the root domain. */
+	ipd->slot = 0;
+	set_bit(0, &__ipipe_domain_slot_map);
+
+	ipd->name = "Linux";
+	ipd->domid = IPIPE_ROOT_ID;
+	ipd->priority = IPIPE_ROOT_PRIO;
+
+	__ipipe_init_stage(ipd);
+
+	INIT_LIST_HEAD(&ipd->p_link);
+	list_add_tail(&ipd->p_link, &__ipipe_pipeline);
+
+	__ipipe_init_platform();
+
+	__ipipe_printk_virq = ipipe_alloc_virq();	/* Cannot fail here. */
+	ipd->irqs[__ipipe_printk_virq].handler = &__ipipe_flush_printk;
+	ipd->irqs[__ipipe_printk_virq].cookie = NULL;
+	ipd->irqs[__ipipe_printk_virq].acknowledge = NULL;
+	ipd->irqs[__ipipe_printk_virq].control = IPIPE_HANDLE_MASK;
+
+	__ipipe_enable_pipeline();
+
+	printk(KERN_INFO "I-pipe %s: pipeline enabled.\n",
+	       IPIPE_VERSION_STRING);
+}
+
+void __ipipe_init_stage(struct ipipe_domain *ipd)
+{
+	int cpu, n;
+
+	for_each_online_cpu(cpu) {
+
+		ipipe_percpudom(ipd, irqpend_himask, cpu) = 0;
+
+		for (n = 0; n < IPIPE_IRQ_IWORDS; n++) {
+			ipipe_percpudom(ipd, irqpend_lomask, cpu)[n] = 0;
+			ipipe_percpudom(ipd, irqheld_mask, cpu)[n] = 0;
+		}
+
+		for (n = 0; n < IPIPE_NR_IRQS; n++)
+			ipipe_percpudom(ipd, irqall, cpu)[n] = 0;
+
+		ipipe_percpudom(ipd, evsync, cpu) = 0;
+	}
+
+	for (n = 0; n < IPIPE_NR_IRQS; n++) {
+		ipd->irqs[n].acknowledge = NULL;
+		ipd->irqs[n].handler = NULL;
+		ipd->irqs[n].control = IPIPE_PASS_MASK;	/* Pass but don't handle */
+	}
+
+	for (n = 0; n < IPIPE_NR_EVENTS; n++)
+		ipd->evhand[n] = NULL;
+
+	ipd->evself = 0LL;
+	init_MUTEX(&ipd->mutex);
+
+	__ipipe_hook_critical_ipi(ipd);
+}
+
+void __ipipe_cleanup_domain(struct ipipe_domain *ipd)
+{
+	ipipe_unstall_pipeline_from(ipd);
+
+	__raw_get_cpu_var(ipipe_percpu_daddr)[ipd->slot] = NULL;
+
+	clear_bit(ipd->slot, &__ipipe_domain_slot_map);
+}
+
+void __ipipe_unstall_root(void)
+{
+	struct ipipe_percpu_domain_data *p;
+
+        local_irq_disable_hw();
+
+#ifdef CONFIG_IPIPE_DEBUG
+	/* This helps catching bad usage from assembly call sites. */
+	BUG_ON(!__ipipe_root_domain_p);
+#endif
+
+	p = ipipe_root_cpudom_ptr();
+
+        __clear_bit(IPIPE_STALL_FLAG, &p->status);
+
+        if (unlikely(p->irqpend_himask != 0))
+                __ipipe_sync_pipeline(IPIPE_IRQMASK_ANY);
+
+        local_irq_enable_hw();
+}
+
+void __ipipe_restore_root(unsigned long x)
+{
+#ifdef CONFIG_IPIPE_DEBUG
+	BUG_ON(!ipipe_root_domain_p);
+#endif
+	if (x)
+		__ipipe_stall_root();
+	else
+		__ipipe_unstall_root();
+}
+
+void ipipe_stall_pipeline_from(struct ipipe_domain *ipd)
+{
+	unsigned long flags;
+	/*
+	 * We have to prevent against race on updating the status
+	 * variable _and_ CPU migration at the same time, so disable
+	 * hw IRQs here.
+	 */
+	local_irq_save_hw(flags);
+
+	__set_bit(IPIPE_STALL_FLAG, &ipipe_cpudom_var(ipd, status));
+
+	if (!__ipipe_pipeline_head_p(ipd))
+		local_irq_restore_hw(flags);
+}
+
+unsigned long ipipe_test_and_stall_pipeline_from(struct ipipe_domain *ipd)
+{
+	unsigned long flags, x;
+
+	/* See ipipe_stall_pipeline_from() */
+	local_irq_save_hw(flags);
+
+	x = __test_and_set_bit(IPIPE_STALL_FLAG, &ipipe_cpudom_var(ipd, status));
+
+	if (!__ipipe_pipeline_head_p(ipd))
+		local_irq_restore_hw(flags);
+
+	return x;
+}
+
+unsigned long ipipe_test_and_unstall_pipeline_from(struct ipipe_domain *ipd)
+{
+	unsigned long flags, x;
+	struct list_head *pos;
+
+	local_irq_save_hw(flags);
+
+	x = __test_and_clear_bit(IPIPE_STALL_FLAG, &ipipe_cpudom_var(ipd, status));
+
+	if (ipd == __ipipe_current_domain)
+		pos = &ipd->p_link;
+	else
+		pos = __ipipe_pipeline.next;
+
+	__ipipe_walk_pipeline(pos);
+
+	if (likely(__ipipe_pipeline_head_p(ipd)))
+		local_irq_enable_hw();
+	else
+		local_irq_restore_hw(flags);
+
+	return x;
+}
+
+void ipipe_restore_pipeline_from(struct ipipe_domain *ipd,
+					  unsigned long x)
+{
+	if (x)
+		ipipe_stall_pipeline_from(ipd);
+	else
+		ipipe_unstall_pipeline_from(ipd);
+}
+
+void ipipe_unstall_pipeline_head(void)
+{
+	struct ipipe_percpu_domain_data *p = ipipe_head_cpudom_ptr();
+
+	local_irq_disable_hw();
+
+	__clear_bit(IPIPE_STALL_FLAG, &p->status);
+
+	if (unlikely(p->irqpend_himask != 0)) {
+		struct ipipe_domain *head_domain = __ipipe_pipeline_head();
+		if (likely(head_domain == __ipipe_current_domain))
+			__ipipe_sync_pipeline(IPIPE_IRQMASK_ANY);
+		else
+			__ipipe_walk_pipeline(&head_domain->p_link);
+        }
+
+	local_irq_enable_hw();
+}
+
+void __ipipe_restore_pipeline_head(unsigned long x)
+{
+	struct ipipe_percpu_domain_data *p = ipipe_head_cpudom_ptr();
+
+	local_irq_disable_hw();
+
+	if (x)
+		set_bit(IPIPE_STALL_FLAG, &p->status);
+	else {
+		__clear_bit(IPIPE_STALL_FLAG, &p->status);
+		if (unlikely(p->irqpend_himask != 0)) {
+			struct ipipe_domain *head_domain = __ipipe_pipeline_head();
+			if (likely(head_domain == __ipipe_current_domain))
+				__ipipe_sync_pipeline(IPIPE_IRQMASK_ANY);
+			else
+				__ipipe_walk_pipeline(&head_domain->p_link);
+		}
+		local_irq_enable_hw();
+	}
+}
+
+void __ipipe_spin_lock_irq(spinlock_t *lock)
+{
+	local_irq_disable_hw();
+	spin_lock(lock);
+	__set_bit(IPIPE_STALL_FLAG, &ipipe_this_cpudom_var(status));
+}
+
+void __ipipe_spin_unlock_irq(spinlock_t *lock)
+{
+	spin_unlock(lock);
+	__clear_bit(IPIPE_STALL_FLAG, &ipipe_this_cpudom_var(status));
+	local_irq_enable_hw();
+}
+
+unsigned long __ipipe_spin_lock_irqsave(spinlock_t *lock)
+{
+	unsigned long flags;
+	int s;
+
+	local_irq_save_hw(flags);
+	spin_lock(lock);
+	s = __test_and_set_bit(IPIPE_STALL_FLAG, &ipipe_this_cpudom_var(status));
+
+	return raw_mangle_irq_bits(s, flags);
+}
+
+void __ipipe_spin_unlock_irqrestore(spinlock_t *lock, unsigned long x)
+{
+	spin_unlock(lock);
+	if (!raw_demangle_irq_bits(&x))
+		__clear_bit(IPIPE_STALL_FLAG, &ipipe_this_cpudom_var(status));
+	local_irq_restore_hw(x);
+}
+
+void __ipipe_spin_unlock_irqbegin(ipipe_spinlock_t *lock)
+{
+	spin_unlock(&lock->__lock);
+}
+
+void __ipipe_spin_unlock_irqcomplete(unsigned long x)
+{
+	if (!raw_demangle_irq_bits(&x))
+		__clear_bit(IPIPE_STALL_FLAG, &ipipe_this_cpudom_var(status));
+	local_irq_restore_hw(x);
+}
+
+/* Must be called hw IRQs off. */
+void __ipipe_set_irq_pending(struct ipipe_domain *ipd, unsigned irq)
+{
+	int level = irq >> IPIPE_IRQ_ISHIFT, rank = irq & IPIPE_IRQ_IMASK;
+	struct ipipe_percpu_domain_data *p = ipipe_cpudom_ptr(ipd);
+
+	prefetchw(p);
+	
+	if (likely(!test_bit(IPIPE_LOCK_FLAG, &ipd->irqs[irq].control))) {
+		__set_bit(rank, &p->irqpend_lomask[level]);
+		__set_bit(level, &p->irqpend_himask);
+	} else
+		__set_bit(rank, &p->irqheld_mask[level]);
+
+	p->irqall[irq]++;
+}
+
+/* Must be called hw IRQs off. */
+void __ipipe_lock_irq(struct ipipe_domain *ipd, int cpu, unsigned irq)
+{
+	struct ipipe_percpu_domain_data *p;
+	int level, rank;
+
+	if (unlikely(test_and_set_bit(IPIPE_LOCK_FLAG, &ipd->irqs[irq].control)))
+		return;
+
+	level = irq >> IPIPE_IRQ_ISHIFT;
+	rank = irq & IPIPE_IRQ_IMASK;
+	p = ipipe_percpudom_ptr(ipd, cpu);
+
+	if (__test_and_clear_bit(rank, &p->irqpend_lomask[level]))
+		__set_bit(rank, &p->irqheld_mask[level]);
+	if (p->irqpend_lomask[level] == 0)
+		__clear_bit(level, &p->irqpend_himask);
+}
+
+/* Must be called hw IRQs off. */
+void __ipipe_unlock_irq(struct ipipe_domain *ipd, unsigned irq)
+{
+	struct ipipe_percpu_domain_data *p;
+	int cpu, level, rank;
+
+	if (unlikely(!test_and_clear_bit(IPIPE_LOCK_FLAG, &ipd->irqs[irq].control)))
+		return;
+
+	level = irq >> IPIPE_IRQ_ISHIFT, rank = irq & IPIPE_IRQ_IMASK;
+	for_each_online_cpu(cpu) {
+		p = ipipe_percpudom_ptr(ipd, cpu);
+		if (test_and_clear_bit(rank, &p->irqheld_mask[level])) {
+			/* We need atomic ops here: */
+			set_bit(rank, &p->irqpend_lomask[level]);
+			set_bit(level, &p->irqpend_himask);
+		}
+	}
+}
+
+/*
+ * __ipipe_walk_pipeline(): Plays interrupts pending in the log. Must
+ * be called with local hw interrupts disabled.
+ */
+void __ipipe_walk_pipeline(struct list_head *pos)
+{
+	struct ipipe_domain *this_domain = __ipipe_current_domain, *next_domain;
+	struct ipipe_percpu_domain_data *p, *np;
+
+	p = ipipe_cpudom_ptr(this_domain);
+
+	while (pos != &__ipipe_pipeline) {
+
+		next_domain = list_entry(pos, struct ipipe_domain, p_link);
+		np = ipipe_cpudom_ptr(next_domain);
+
+		if (test_bit(IPIPE_STALL_FLAG, &np->status))
+			break;	/* Stalled stage -- do not go further. */
+
+		if (np->irqpend_himask) {
+			if (next_domain == this_domain)
+				__ipipe_sync_pipeline(IPIPE_IRQMASK_ANY);
+			else {
+
+				p->evsync = 0;
+				__ipipe_current_domain = next_domain;
+				ipipe_suspend_domain();	/* Sync stage and propagate interrupts. */
+
+				if (__ipipe_current_domain == next_domain)
+					__ipipe_current_domain = this_domain;
+				/*
+				 * Otherwise, something changed the current domain under our
+				 * feet recycling the register set; do not override the new
+				 * domain.
+				 */
+
+				if (p->irqpend_himask &&
+				    !test_bit(IPIPE_STALL_FLAG, &p->status))
+					__ipipe_sync_pipeline(IPIPE_IRQMASK_ANY);
+			}
+			break;
+		} else if (next_domain == this_domain)
+			break;
+
+		pos = next_domain->p_link.next;
+	}
+}
+
+/*
+ * ipipe_suspend_domain() -- Suspend the current domain, switching to
+ * the next one which has pending work down the pipeline.
+ */
+void ipipe_suspend_domain(void)
+{
+	struct ipipe_domain *this_domain, *next_domain;
+	struct ipipe_percpu_domain_data *p;
+	struct list_head *ln;
+	unsigned long flags;
+
+	local_irq_save_hw(flags);
+
+	this_domain = next_domain = __ipipe_current_domain;
+	p = ipipe_cpudom_ptr(this_domain);
+	p->status &= ~(IPIPE_STALL_MASK|IPIPE_SYNC_MASK);
+
+	if (p->irqpend_himask != 0)
+		goto sync_stage;
+
+	for (;;) {
+		ln = next_domain->p_link.next;
+
+		if (ln == &__ipipe_pipeline)
+			break;
+
+		next_domain = list_entry(ln, struct ipipe_domain, p_link);
+		p = ipipe_cpudom_ptr(next_domain);
+
+		if (p->status & IPIPE_STALL_MASK)
+			break;
+
+		if (p->irqpend_himask == 0)
+			continue;
+
+		__ipipe_current_domain = next_domain;
+sync_stage:
+		__ipipe_sync_pipeline(IPIPE_IRQMASK_ANY);
+
+		if (__ipipe_current_domain != next_domain)
+			/*
+			 * Something has changed the current domain under our
+			 * feet, recycling the register set; take note.
+			 */
+			this_domain = __ipipe_current_domain;
+	}
+
+	__ipipe_current_domain = this_domain;
+
+	local_irq_restore_hw(flags);
+}
+
+
+/* ipipe_alloc_virq() -- Allocate a pipelined virtual/soft interrupt.
+ * Virtual interrupts are handled in exactly the same way than their
+ * hw-generated counterparts wrt pipelining.
+ */
+unsigned ipipe_alloc_virq(void)
+{
+	unsigned long flags, irq = 0;
+	int ipos;
+
+	spin_lock_irqsave(&__ipipe_pipelock, flags);
+
+	if (__ipipe_virtual_irq_map != ~0) {
+		ipos = ffz(__ipipe_virtual_irq_map);
+		set_bit(ipos, &__ipipe_virtual_irq_map);
+		irq = ipos + IPIPE_VIRQ_BASE;
+	}
+
+	spin_unlock_irqrestore(&__ipipe_pipelock, flags);
+
+	return irq;
+}
+
+/* ipipe_virtualize_irq() -- Attach a handler (and optionally a hw
+   acknowledge routine) to an interrupt for a given domain. */
+
+int ipipe_virtualize_irq(struct ipipe_domain *ipd,
+			 unsigned irq,
+			 ipipe_irq_handler_t handler,
+			 void *cookie,
+			 ipipe_irq_ackfn_t acknowledge,
+			 unsigned modemask)
+{
+	ipipe_irq_handler_t old_handler;
+	unsigned long flags;
+	int err;
+
+	if (irq >= IPIPE_NR_IRQS)
+		return -EINVAL;
+
+	if (ipd->irqs[irq].control & IPIPE_SYSTEM_MASK)
+		return -EPERM;
+
+	if (!test_bit(IPIPE_AHEAD_FLAG, &ipd->flags))
+		/* Silently unwire interrupts for non-heading domains. */
+		modemask &= ~IPIPE_WIRED_MASK;
+
+	spin_lock_irqsave(&__ipipe_pipelock, flags);
+
+	old_handler = ipd->irqs[irq].handler;
+
+	if (handler != NULL) {
+		if (handler == IPIPE_SAME_HANDLER) {
+			handler = old_handler;
+			cookie = ipd->irqs[irq].cookie;
+
+			if (handler == NULL) {
+				err = -EINVAL;
+				goto unlock_and_exit;
+			}
+		} else if ((modemask & IPIPE_EXCLUSIVE_MASK) != 0 &&
+			   old_handler != NULL) {
+			err = -EBUSY;
+			goto unlock_and_exit;
+		}
+
+		/* Wired interrupts can only be delivered to domains
+		 * always heading the pipeline, and using dynamic
+		 * propagation. */
+
+		if ((modemask & IPIPE_WIRED_MASK) != 0) {
+			if ((modemask & (IPIPE_PASS_MASK | IPIPE_STICKY_MASK)) != 0) {
+				err = -EINVAL;
+				goto unlock_and_exit;
+			}
+			modemask |= (IPIPE_HANDLE_MASK);
+		}
+
+		if ((modemask & IPIPE_STICKY_MASK) != 0)
+			modemask |= IPIPE_HANDLE_MASK;
+	} else
+		modemask &=
+		    ~(IPIPE_HANDLE_MASK | IPIPE_STICKY_MASK |
+		      IPIPE_EXCLUSIVE_MASK | IPIPE_WIRED_MASK);
+
+	if (acknowledge == NULL && !ipipe_virtual_irq_p(irq))
+		/*
+		 * Acknowledge handler unspecified for a hw interrupt:
+		 * use the Linux-defined handler instead.
+		 */
+		acknowledge = ipipe_root_domain->irqs[irq].acknowledge;
+
+	ipd->irqs[irq].handler = handler;
+	ipd->irqs[irq].cookie = cookie;
+	ipd->irqs[irq].acknowledge = acknowledge;
+	ipd->irqs[irq].control = modemask;
+
+	if (irq < NR_IRQS && !ipipe_virtual_irq_p(irq)) {
+		if (handler != NULL) {
+			__ipipe_enable_irqdesc(ipd, irq);
+
+			if ((modemask & IPIPE_ENABLE_MASK) != 0) {
+				if (ipd != __ipipe_current_domain) {
+		/*
+		 * IRQ enable/disable state is domain-sensitive, so we
+		 * may not change it for another domain. What is
+		 * allowed however is forcing some domain to handle an
+		 * interrupt source, by passing the proper 'ipd'
+		 * descriptor which thus may be different from
+		 * __ipipe_current_domain.
+		 */
+					err = -EPERM;
+					goto unlock_and_exit;
+				}
+				__ipipe_enable_irq(irq);
+			}
+		} else if (old_handler != NULL)
+				__ipipe_disable_irqdesc(ipd, irq);
+	}
+
+	err = 0;
+
+      unlock_and_exit:
+
+	spin_unlock_irqrestore(&__ipipe_pipelock, flags);
+
+	return err;
+}
+
+/* ipipe_control_irq() -- Change modes of a pipelined interrupt for
+ * the current domain. */
+
+int ipipe_control_irq(unsigned irq, unsigned clrmask, unsigned setmask)
+{
+	struct ipipe_domain *ipd;
+	unsigned long flags;
+
+	if (irq >= IPIPE_NR_IRQS)
+		return -EINVAL;
+
+	spin_lock_irqsave(&__ipipe_pipelock, flags);
+
+	ipd = __ipipe_current_domain;
+
+	if (ipd->irqs[irq].control & IPIPE_SYSTEM_MASK) {
+		spin_unlock_irqrestore(&__ipipe_pipelock, flags);
+		return -EPERM;
+	}
+
+	if (ipd->irqs[irq].handler == NULL)
+		setmask &= ~(IPIPE_HANDLE_MASK | IPIPE_STICKY_MASK);
+
+	if ((setmask & IPIPE_STICKY_MASK) != 0)
+		setmask |= IPIPE_HANDLE_MASK;
+
+	if ((clrmask & (IPIPE_HANDLE_MASK | IPIPE_STICKY_MASK)) != 0)	/* If one goes, both go. */
+		clrmask |= (IPIPE_HANDLE_MASK | IPIPE_STICKY_MASK);
+
+	ipd->irqs[irq].control &= ~clrmask;
+	ipd->irqs[irq].control |= setmask;
+
+	if ((setmask & IPIPE_ENABLE_MASK) != 0)
+		__ipipe_enable_irq(irq);
+	else if ((clrmask & IPIPE_ENABLE_MASK) != 0)
+		__ipipe_disable_irq(irq);
+
+	spin_unlock_irqrestore(&__ipipe_pipelock, flags);
+
+	return 0;
+}
+
+/* __ipipe_dispatch_event() -- Low-level event dispatcher. */
+
+int __ipipe_dispatch_event (unsigned event, void *data)
+{
+	struct ipipe_domain *start_domain, *this_domain, *next_domain;
+	ipipe_event_handler_t evhand;
+	struct list_head *pos, *npos;
+	unsigned long flags;
+	int propagate = 1;
+
+	local_irq_save_hw(flags);
+
+	start_domain = this_domain = __ipipe_current_domain;
+
+	list_for_each_safe(pos, npos, &__ipipe_pipeline) {
+		/*
+		 * Note: Domain migration may occur while running
+		 * event or interrupt handlers, in which case the
+		 * current register set is going to be recycled for a
+		 * different domain than the initiating one. We do
+		 * care for that, always tracking the current domain
+		 * descriptor upon return from those handlers.
+		 */
+		next_domain = list_entry(pos, struct ipipe_domain, p_link);
+
+		/*
+		 * Keep a cached copy of the handler's address since
+		 * ipipe_catch_event() may clear it under our feet.
+		 */
+		evhand = next_domain->evhand[event];
+
+		if (evhand != NULL) {
+			__ipipe_current_domain = next_domain;
+			ipipe_cpudom_var(next_domain, evsync) |= (1LL << event);
+			local_irq_restore_hw(flags);
+			propagate = !evhand(event, start_domain, data);
+			local_irq_save_hw(flags);
+			/*
+			 * We may have a migration issue here, if the
+			 * current task is migrated to another CPU on
+			 * behalf of the invoked handler, usually when
+			 * a syscall event is processed. However,
+			 * ipipe_catch_event() will make sure that a
+			 * CPU that clears a handler for any given
+			 * event will not attempt to wait for itself
+			 * to clear the evsync bit for that event,
+			 * which practically plugs the hole, without
+			 * resorting to a much more complex strategy.
+			 */
+			ipipe_cpudom_var(next_domain, evsync) &= ~(1LL << event);
+			if (__ipipe_current_domain != next_domain)
+				this_domain = __ipipe_current_domain;
+		}
+
+		if (next_domain != ipipe_root_domain &&	/* NEVER sync the root stage here. */
+		    ipipe_cpudom_var(next_domain, irqpend_himask) != 0 &&
+		    !test_bit(IPIPE_STALL_FLAG, &ipipe_cpudom_var(next_domain, status))) {
+			__ipipe_current_domain = next_domain;
+			__ipipe_sync_pipeline(IPIPE_IRQMASK_ANY);
+			if (__ipipe_current_domain != next_domain)
+				this_domain = __ipipe_current_domain;
+		}
+
+		__ipipe_current_domain = this_domain;
+
+		if (next_domain == this_domain || !propagate)
+			break;
+	}
+
+	local_irq_restore_hw(flags);
+
+	return !propagate;
+}
+
+/*
+ * __ipipe_dispatch_wired -- Wired interrupt dispatcher. Wired
+ * interrupts are immediately and unconditionally delivered to the
+ * domain heading the pipeline upon receipt, and such domain must have
+ * been registered as an invariant head for the system (priority ==
+ * IPIPE_HEAD_PRIORITY). The motivation for using wired interrupts is
+ * to get an extra-fast dispatching path for those IRQs, by relying on
+ * a straightforward logic based on assumptions that must always be
+ * true for invariant head domains.  The following assumptions are
+ * made when dealing with such interrupts:
+ *
+ * 1- Wired interrupts are purely dynamic, i.e. the decision to
+ * propagate them down the pipeline must be done from the head domain
+ * ISR.
+ * 2- Wired interrupts cannot be shared or sticky.
+ * 3- The root domain cannot be an invariant pipeline head, in
+ * consequence of what the root domain cannot handle wired
+ * interrupts.
+ * 4- Wired interrupts must have a valid acknowledge handler for the
+ * head domain (if needed, see __ipipe_handle_irq).
+ *
+ * Called with hw interrupts off.
+ */
+
+void __ipipe_dispatch_wired(struct ipipe_domain *head, unsigned irq)
+{
+	struct ipipe_percpu_domain_data *p = ipipe_cpudom_ptr(head);
+
+	prefetchw(p);
+
+	if (unlikely(test_bit(IPIPE_LOCK_FLAG, &head->irqs[irq].control))) {
+		/*
+		 * If we can't process this IRQ right now, we must
+		 * mark it as held, so that it will get played during
+		 * normal log sync when the corresponding interrupt
+		 * source is eventually unlocked.
+		 */
+		p->irqall[irq]++;
+		__set_bit(irq & IPIPE_IRQ_IMASK, &p->irqheld_mask[irq >> IPIPE_IRQ_ISHIFT]);
+		return;
+	}
+
+	if (test_bit(IPIPE_STALL_FLAG, &p->status)) {
+		__ipipe_set_irq_pending(head, irq);
+		return;
+	}
+
+	__ipipe_dispatch_wired_nocheck(head, irq);
+}
+
+void __ipipe_dispatch_wired_nocheck(struct ipipe_domain *head, unsigned irq) /* hw interrupts off */
+{
+	struct ipipe_percpu_domain_data *p = ipipe_cpudom_ptr(head);
+	struct ipipe_domain *old;
+
+	prefetchw(p);
+
+	old = __ipipe_current_domain;
+	__ipipe_current_domain = head; /* Switch to the head domain. */
+
+	p->irqall[irq]++;
+	__set_bit(IPIPE_STALL_FLAG, &p->status);
+	head->irqs[irq].handler(irq, head->irqs[irq].cookie); /* Call the ISR. */
+	__ipipe_run_irqtail();
+	__clear_bit(IPIPE_STALL_FLAG, &p->status);
+
+	if (__ipipe_current_domain == head) {
+		__ipipe_current_domain = old;
+		if (old == head) {
+			if (p->irqpend_himask)
+				__ipipe_sync_pipeline(IPIPE_IRQMASK_ANY);
+			return;
+		}
+	}
+
+	__ipipe_walk_pipeline(&head->p_link);
+}
+
+/*
+ * __ipipe_sync_stage() -- Flush the pending IRQs for the current
+ * domain (and processor). This routine flushes the interrupt log
+ * (see "Optimistic interrupt protection" from D. Stodolsky et al. for
+ * more on the deferred interrupt scheme). Every interrupt that
+ * occurred while the pipeline was stalled gets played. WARNING:
+ * callers on SMP boxen should always check for CPU migration on
+ * return of this routine. One can control the kind of interrupts
+ * which are going to be sync'ed using the syncmask
+ * parameter. IPIPE_IRQMASK_ANY plays them all, IPIPE_IRQMASK_VIRT
+ * plays virtual interrupts only.
+ *
+ * This routine must be called with hw interrupts off.
+ */
+void __ipipe_sync_stage(unsigned long syncmask)
+{
+	struct ipipe_percpu_domain_data *p;
+	unsigned long mask, submask;
+	struct ipipe_domain *ipd;
+	int level, rank, cpu;
+	unsigned irq;
+
+	ipd = __ipipe_current_domain;
+	p = ipipe_cpudom_ptr(ipd);
+
+	if (__test_and_set_bit(IPIPE_SYNC_FLAG, &p->status))
+		return;
+
+	cpu = ipipe_processor_id();
+
+	/*
+	 * The policy here is to keep the dispatching code interrupt-free
+	 * by stalling the current stage. If the upper domain handler
+	 * (which we call) wants to re-enable interrupts while in a safe
+	 * portion of the code (e.g. SA_INTERRUPT flag unset for Linux's
+	 * sigaction()), it will have to unstall (then stall again before
+	 * returning to us!) the stage when it sees fit.
+	 */
+	while ((mask = (p->irqpend_himask & syncmask)) != 0) {
+		level = __ipipe_ffnz(mask);
+
+		while ((submask = p->irqpend_lomask[level]) != 0) {
+			rank = __ipipe_ffnz(submask);
+			irq = (level << IPIPE_IRQ_ISHIFT) + rank;
+
+			__clear_bit(rank, &p->irqpend_lomask[level]);
+
+			if (p->irqpend_lomask[level] == 0)
+				__clear_bit(level, &p->irqpend_himask);
+			/*
+			 * Make sure the compiler will not postpone
+			 * the pending bitmask updates before calling
+			 * the interrupt handling routine. Otherwise,
+			 * those late updates could overwrite any
+			 * change to irqpend_hi/lomask due to a nested
+			 * interrupt, leaving the latter unprocessed
+			 * (seen on mpc836x).
+			 */
+			barrier();
+
+			if (test_bit(IPIPE_LOCK_FLAG, &ipd->irqs[irq].control))
+				continue;
+
+			__set_bit(IPIPE_STALL_FLAG, &p->status);
+			barrier();
+
+			__ipipe_run_isr(ipd, irq);
+			barrier();
+			p = ipipe_cpudom_ptr(__ipipe_current_domain);
+			__clear_bit(IPIPE_STALL_FLAG, &p->status);
+		}
+	}
+
+	__clear_bit(IPIPE_SYNC_FLAG, &p->status);
+}
+
+/* ipipe_register_domain() -- Link a new domain to the pipeline. */
+
+int ipipe_register_domain(struct ipipe_domain *ipd,
+			  struct ipipe_domain_attr *attr)
+{
+	struct ipipe_domain *_ipd;
+	struct list_head *pos = NULL;
+	unsigned long flags;
+
+	if (!ipipe_root_domain_p) {
+		printk(KERN_WARNING
+		       "I-pipe: Only the root domain may register a new domain.\n");
+		return -EPERM;
+	}
+
+	flags = ipipe_critical_enter(NULL);
+
+	if (attr->priority == IPIPE_HEAD_PRIORITY) {
+		if (test_bit(IPIPE_HEAD_SLOT, &__ipipe_domain_slot_map)) {
+			ipipe_critical_exit(flags);
+			return -EAGAIN;	/* Cannot override current head. */
+		}
+		ipd->slot = IPIPE_HEAD_SLOT;
+	} else
+		ipd->slot = ffz(__ipipe_domain_slot_map);
+
+	if (ipd->slot < NR_IPIPE_DOMAINS) {
+		set_bit(ipd->slot, &__ipipe_domain_slot_map);
+		list_for_each(pos, &__ipipe_pipeline) {
+			_ipd = list_entry(pos, struct ipipe_domain, p_link);
+			if (_ipd->domid == attr->domid)
+				break;
+		}
+	}
+
+	ipipe_critical_exit(flags);
+
+	if (pos != &__ipipe_pipeline) {
+		if (ipd->slot < NR_IPIPE_DOMAINS)
+			clear_bit(ipd->slot, &__ipipe_domain_slot_map);
+		return -EBUSY;
+	}
+
+	/*
+	 * Set up the perdomain pointers for direct access to the
+	 * percpu domain data. This saves a costly multiply each time
+	 * we need to refer to the contents of the percpu domain data
+	 * array.
+	 */
+	__raw_get_cpu_var(ipipe_percpu_daddr)[ipd->slot] = &__raw_get_cpu_var(ipipe_percpu_darray)[ipd->slot];
+
+	ipd->name = attr->name;
+	ipd->domid = attr->domid;
+	ipd->pdd = attr->pdd;
+	ipd->flags = 0;
+
+	if (attr->priority == IPIPE_HEAD_PRIORITY) {
+		ipd->priority = INT_MAX;
+		__set_bit(IPIPE_AHEAD_FLAG,&ipd->flags);
+	}
+	else
+		ipd->priority = attr->priority;
+
+	__ipipe_init_stage(ipd);
+
+	INIT_LIST_HEAD(&ipd->p_link);
+
+#ifdef CONFIG_PROC_FS
+	__ipipe_add_domain_proc(ipd);
+#endif /* CONFIG_PROC_FS */
+
+	flags = ipipe_critical_enter(NULL);
+
+	list_for_each(pos, &__ipipe_pipeline) {
+		_ipd = list_entry(pos, struct ipipe_domain, p_link);
+		if (ipd->priority > _ipd->priority)
+			break;
+	}
+
+	list_add_tail(&ipd->p_link, pos);
+
+	ipipe_critical_exit(flags);
+
+	printk(KERN_INFO "I-pipe: Domain %s registered.\n", ipd->name);
+
+	/*
+	 * Finally, allow the new domain to perform its initialization
+	 * chores.
+	 */
+
+	if (attr->entry != NULL) {
+		local_irq_save_hw_smp(flags);
+		__ipipe_current_domain = ipd;
+		local_irq_restore_hw_smp(flags);
+		attr->entry();
+		local_irq_save_hw(flags);
+		__ipipe_current_domain = ipipe_root_domain;
+
+		if (ipipe_root_cpudom_var(irqpend_himask) != 0 &&
+		    !test_bit(IPIPE_STALL_FLAG, &ipipe_root_cpudom_var(status)))
+			__ipipe_sync_pipeline(IPIPE_IRQMASK_ANY);
+
+		local_irq_restore_hw(flags);
+	}
+
+	return 0;
+}
+
+/* ipipe_unregister_domain() -- Remove a domain from the pipeline. */
+
+int ipipe_unregister_domain(struct ipipe_domain *ipd)
+{
+	unsigned long flags;
+
+	if (!ipipe_root_domain_p) {
+		printk(KERN_WARNING
+		       "I-pipe: Only the root domain may unregister a domain.\n");
+		return -EPERM;
+	}
+
+	if (ipd == ipipe_root_domain) {
+		printk(KERN_WARNING
+		       "I-pipe: Cannot unregister the root domain.\n");
+		return -EPERM;
+	}
+
+	down(&ipd->mutex);
+
+#ifdef CONFIG_PROC_FS
+	__ipipe_remove_domain_proc(ipd);
+#endif /* CONFIG_PROC_FS */
+
+	/*
+	 * Simply remove the domain from the pipeline and we are almost done.
+	 */
+
+	flags = ipipe_critical_enter(NULL);
+	list_del_init(&ipd->p_link);
+	ipipe_critical_exit(flags);
+
+	__ipipe_cleanup_domain(ipd);
+
+	up(&ipd->mutex);
+
+	printk(KERN_INFO "I-pipe: Domain %s unregistered.\n", ipd->name);
+
+	return 0;
+}
+
+/*
+ * ipipe_propagate_irq() -- Force a given IRQ propagation on behalf of
+ * a running interrupt handler to the next domain down the pipeline.
+ * ipipe_schedule_irq() -- Does almost the same as above, but attempts
+ * to pend the interrupt for the current domain first.
+ * Must be called hw IRQs off.
+ */
+void __ipipe_pend_irq(unsigned irq, struct list_head *head)
+{
+	struct ipipe_domain *ipd;
+	struct list_head *ln;
+
+#ifdef CONFIG_IPIPE_DEBUG
+	BUG_ON(irq >= IPIPE_NR_IRQS ||
+	       (ipipe_virtual_irq_p(irq)
+		&& !test_bit(irq - IPIPE_VIRQ_BASE, &__ipipe_virtual_irq_map)));
+#endif
+	for (ln = head; ln != &__ipipe_pipeline; ln = ipd->p_link.next) {
+		ipd = list_entry(ln, struct ipipe_domain, p_link);
+		if (test_bit(IPIPE_HANDLE_FLAG, &ipd->irqs[irq].control)) {
+			__ipipe_set_irq_pending(ipd, irq);
+			return;
+		}
+	}
+}
+
+/* ipipe_free_virq() -- Release a virtual/soft interrupt. */
+
+int ipipe_free_virq(unsigned virq)
+{
+	if (!ipipe_virtual_irq_p(virq))
+		return -EINVAL;
+
+	clear_bit(virq - IPIPE_VIRQ_BASE, &__ipipe_virtual_irq_map);
+
+	return 0;
+}
+
+void ipipe_init_attr(struct ipipe_domain_attr *attr)
+{
+	attr->name = "anon";
+	attr->domid = 1;
+	attr->entry = NULL;
+	attr->priority = IPIPE_ROOT_PRIO;
+	attr->pdd = NULL;
+}
+
+/*
+ * ipipe_catch_event() -- Interpose or remove an event handler for a
+ * given domain.
+ */
+ipipe_event_handler_t ipipe_catch_event(struct ipipe_domain *ipd,
+					unsigned event,
+					ipipe_event_handler_t handler)
+{
+	ipipe_event_handler_t old_handler;
+	unsigned long flags;
+	int self = 0, cpu;
+
+	if (event & IPIPE_EVENT_SELF) {
+		event &= ~IPIPE_EVENT_SELF;
+		self = 1;
+	}
+
+	if (event >= IPIPE_NR_EVENTS)
+		return NULL;
+
+	flags = ipipe_critical_enter(NULL);
+
+	if (!(old_handler = xchg(&ipd->evhand[event],handler)))	{
+		if (handler) {
+			if (self)
+				ipd->evself |= (1LL << event);
+			else
+				__ipipe_event_monitors[event]++;
+		}
+	}
+	else if (!handler) {
+		if (ipd->evself & (1LL << event))
+			ipd->evself &= ~(1LL << event);
+		else
+			__ipipe_event_monitors[event]--;
+	} else if ((ipd->evself & (1LL << event)) && !self) {
+			__ipipe_event_monitors[event]++;
+			ipd->evself &= ~(1LL << event);
+	} else if (!(ipd->evself & (1LL << event)) && self) {
+			__ipipe_event_monitors[event]--;
+			ipd->evself |= (1LL << event);
+	}
+
+	ipipe_critical_exit(flags);
+
+	if (!handler && ipipe_root_domain_p) {
+		/*
+		 * If we cleared a handler on behalf of the root
+		 * domain, we have to wait for any current invocation
+		 * to drain, since our caller might subsequently unmap
+		 * the target domain. To this aim, this code
+		 * synchronizes with __ipipe_dispatch_event(),
+		 * guaranteeing that either the dispatcher sees a null
+		 * handler in which case it discards the invocation
+		 * (which also prevents from entering a livelock), or
+		 * finds a valid handler and calls it. Symmetrically,
+		 * ipipe_catch_event() ensures that the called code
+		 * won't be unmapped under our feet until the event
+		 * synchronization flag is cleared for the given event
+		 * on all CPUs.
+		 */
+		cpu = smp_processor_id();
+		/*
+		 * Hack: this solves the potential migration issue
+		 * raised in __ipipe_dispatch_event(). This is a
+		 * work-around which makes the assumption that other
+		 * CPUs will subsequently, either process at least one
+		 * interrupt for the target domain, or call
+		 * __ipipe_dispatch_event() without going through a
+		 * migration while running the handler at least once;
+		 * practically, this is safe on any normally running
+		 * system.
+		 */
+		ipipe_percpudom(ipd, evsync, cpu) &= ~(1LL << event);
+
+		for_each_online_cpu(cpu) {
+			while (ipipe_percpudom(ipd, evsync, cpu) & (1LL << event))
+				schedule_timeout(HZ / 50);
+		}
+	}
+
+	return old_handler;
+}
+
+cpumask_t ipipe_set_irq_affinity (unsigned irq, cpumask_t cpumask)
+{
+	return 0;
+}
+
+int ipipe_send_ipi (unsigned ipi, cpumask_t cpumask)
+
+{
+	return -EINVAL;
+}
+
+int ipipe_alloc_ptdkey (void)
+{
+	unsigned long flags;
+	int key = -1;
+
+	spin_lock_irqsave(&__ipipe_pipelock,flags);
+
+	if (__ipipe_ptd_key_count < IPIPE_ROOT_NPTDKEYS) {
+		key = ffz(__ipipe_ptd_key_map);
+		set_bit(key,&__ipipe_ptd_key_map);
+		__ipipe_ptd_key_count++;
+	}
+
+	spin_unlock_irqrestore(&__ipipe_pipelock,flags);
+
+	return key;
+}
+
+int ipipe_free_ptdkey (int key)
+{
+	unsigned long flags;
+
+	if (key < 0 || key >= IPIPE_ROOT_NPTDKEYS)
+		return -EINVAL;
+
+	spin_lock_irqsave(&__ipipe_pipelock,flags);
+
+	if (test_and_clear_bit(key,&__ipipe_ptd_key_map))
+		__ipipe_ptd_key_count--;
+
+	spin_unlock_irqrestore(&__ipipe_pipelock,flags);
+
+	return 0;
+}
+
+int ipipe_set_ptd (int key, void *value)
+
+{
+	if (key < 0 || key >= IPIPE_ROOT_NPTDKEYS)
+		return -EINVAL;
+
+	current->ptd[key] = value;
+
+	return 0;
+}
+
+void *ipipe_get_ptd (int key)
+
+{
+	if (key < 0 || key >= IPIPE_ROOT_NPTDKEYS)
+		return NULL;
+
+	return current->ptd[key];
+}
+
+#ifdef CONFIG_PROC_FS
+
+struct proc_dir_entry *ipipe_proc_root;
+
+static int __ipipe_version_info_proc(char *page,
+				     char **start,
+				     off_t off, int count, int *eof, void *data)
+{
+	int len = sprintf(page, "%s\n", IPIPE_VERSION_STRING);
+
+	len -= off;
+
+	if (len <= off + count)
+		*eof = 1;
+
+	*start = page + off;
+
+	if(len > count)
+		len = count;
+
+	if(len < 0)
+		len = 0;
+
+	return len;
+}
+
+static int __ipipe_common_info_show(struct seq_file *p, void *data)
+{
+	char handling, stickiness, lockbit, exclusive, virtuality;
+	struct ipipe_domain *ipd = p->private;
+	unsigned long ctlbits;
+	unsigned irq;
+
+	seq_printf(p, "       +----- Handling ([A]ccepted, [G]rabbed, [W]ired, [D]iscarded)\n");
+	seq_printf(p, "       |+---- Sticky\n");
+	seq_printf(p, "       ||+--- Locked\n");
+	seq_printf(p, "       |||+-- Exclusive\n");
+	seq_printf(p, "       ||||+- Virtual\n");
+	seq_printf(p, "[IRQ]  |||||\n");
+
+	down(&ipd->mutex);
+
+	for (irq = 0; irq < IPIPE_NR_IRQS; irq++) {
+		/* Remember to protect against
+		 * ipipe_virtual_irq/ipipe_control_irq if more fields
+		 * get involved. */
+		ctlbits = ipd->irqs[irq].control;
+
+		if (irq >= IPIPE_NR_XIRQS && !ipipe_virtual_irq_p(irq))
+			/*
+			 * There might be a hole between the last external
+			 * IRQ and the first virtual one; skip it.
+			 */
+			continue;
+
+		if (ipipe_virtual_irq_p(irq)
+		    && !test_bit(irq - IPIPE_VIRQ_BASE, &__ipipe_virtual_irq_map))
+			/* Non-allocated virtual IRQ; skip it. */
+			continue;
+
+		/*
+		 * Statuses are as follows:
+		 * o "accepted" means handled _and_ passed down the pipeline.
+		 * o "grabbed" means handled, but the interrupt might be
+		 * terminated _or_ passed down the pipeline depending on
+		 * what the domain handler asks for to the I-pipe.
+		 * o "wired" is basically the same as "grabbed", except that
+		 * the interrupt is unconditionally delivered to an invariant
+		 * pipeline head domain.
+		 * o "passed" means unhandled by the domain but passed
+		 * down the pipeline.
+		 * o "discarded" means unhandled and _not_ passed down the
+		 * pipeline. The interrupt merely disappears from the
+		 * current domain down to the end of the pipeline.
+		 */
+		if (ctlbits & IPIPE_HANDLE_MASK) {
+			if (ctlbits & IPIPE_PASS_MASK)
+				handling = 'A';
+			else if (ctlbits & IPIPE_WIRED_MASK)
+				handling = 'W';
+			else
+				handling = 'G';
+		} else if (ctlbits & IPIPE_PASS_MASK)
+			/* Do not output if no major action is taken. */
+			continue;
+		else
+			handling = 'D';
+
+		if (ctlbits & IPIPE_STICKY_MASK)
+			stickiness = 'S';
+		else
+			stickiness = '.';
+
+		if (ctlbits & IPIPE_LOCK_MASK)
+			lockbit = 'L';
+		else
+			lockbit = '.';
+
+		if (ctlbits & IPIPE_EXCLUSIVE_MASK)
+			exclusive = 'X';
+		else
+			exclusive = '.';
+
+		if (ipipe_virtual_irq_p(irq))
+			virtuality = 'V';
+		else
+			virtuality = '.';
+
+		seq_printf(p, " %3u:  %c%c%c%c%c\n",
+			     irq, handling, stickiness, lockbit, exclusive, virtuality);
+	}
+
+	seq_printf(p, "[Domain info]\n");
+
+	seq_printf(p, "id=0x%.8x\n", ipd->domid);
+
+	if (test_bit(IPIPE_AHEAD_FLAG,&ipd->flags))
+		seq_printf(p, "priority=topmost\n");
+	else
+		seq_printf(p, "priority=%d\n", ipd->priority);
+
+	up(&ipd->mutex);
+
+	return 0;
+}
+
+static int __ipipe_common_info_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, __ipipe_common_info_show, PDE(inode)->data);
+}
+
+static struct file_operations __ipipe_info_proc_ops = {
+	.owner		= THIS_MODULE,
+	.open		= __ipipe_common_info_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+void __ipipe_add_domain_proc(struct ipipe_domain *ipd)
+{
+	struct proc_dir_entry *e = create_proc_entry(ipd->name, 0444, ipipe_proc_root);
+	if (e) {
+		e->proc_fops = &__ipipe_info_proc_ops;
+		e->data = ipd;
+	}
+}
+
+void __ipipe_remove_domain_proc(struct ipipe_domain *ipd)
+{
+	remove_proc_entry(ipd->name,ipipe_proc_root);
+}
+
+void __init ipipe_init_proc(void)
+{
+	ipipe_proc_root = create_proc_entry("ipipe",S_IFDIR, 0);
+	create_proc_read_entry("version",0444,ipipe_proc_root,&__ipipe_version_info_proc,NULL);
+	__ipipe_add_domain_proc(ipipe_root_domain);
+
+	__ipipe_init_tracer();
+}
+
+#endif	/* CONFIG_PROC_FS */
+
+#ifdef CONFIG_IPIPE_DEBUG_CONTEXT
+
+DEFINE_PER_CPU(int, ipipe_percpu_context_check) = { 1 };
+DEFINE_PER_CPU(int, ipipe_saved_context_check_state);
+
+void ipipe_check_context(struct ipipe_domain *border_domain)
+{
+        struct ipipe_percpu_domain_data *p; 
+        struct ipipe_domain *this_domain; 
+        unsigned long flags;
+	int cpu;
+ 
+        local_irq_save_hw_smp(flags); 
+
+        this_domain = __ipipe_current_domain; 
+        p = ipipe_head_cpudom_ptr(); 
+        if (likely(this_domain->priority <= border_domain->priority && 
+		   !test_bit(IPIPE_STALL_FLAG, &p->status))) { 
+                local_irq_restore_hw_smp(flags); 
+                return; 
+        } 
+ 
+	cpu = ipipe_processor_id();
+        if (!per_cpu(ipipe_percpu_context_check, cpu)) { 
+                local_irq_restore_hw_smp(flags); 
+                return; 
+        } 
+ 
+        local_irq_restore_hw_smp(flags); 
+
+	ipipe_context_check_off();
+	ipipe_set_printk_sync(__ipipe_current_domain);
+
+	if (this_domain->priority > border_domain->priority)
+		printk(KERN_ERR "I-pipe: Detected illicit call from domain "
+				"'%s'\n"
+		       KERN_ERR "        into a service reserved for domain "
+				"'%s' and below.\n",
+		       this_domain->name, border_domain->name);
+	else
+		printk(KERN_ERR "I-pipe: Detected stalled topmost domain, "
+				"probably caused by a bug.\n"
+				"        A critical section may have been "
+				"left unterminated.\n");
+	dump_stack();
+}
+
+EXPORT_SYMBOL(ipipe_check_context);
+
+#endif /* CONFIG_IPIPE_DEBUG_CONTEXT */
+
+EXPORT_SYMBOL(ipipe_virtualize_irq);
+EXPORT_SYMBOL(ipipe_control_irq);
+EXPORT_SYMBOL(ipipe_suspend_domain);
+EXPORT_SYMBOL(ipipe_alloc_virq);
+EXPORT_PER_CPU_SYMBOL(ipipe_percpu_domain);
+EXPORT_PER_CPU_SYMBOL(ipipe_percpu_darray);
+EXPORT_SYMBOL(ipipe_root);
+EXPORT_SYMBOL(ipipe_stall_pipeline_from);
+EXPORT_SYMBOL(ipipe_test_and_stall_pipeline_from);
+EXPORT_SYMBOL(ipipe_test_and_unstall_pipeline_from);
+EXPORT_SYMBOL(ipipe_restore_pipeline_from);
+EXPORT_SYMBOL(ipipe_unstall_pipeline_head);
+EXPORT_SYMBOL(__ipipe_restore_pipeline_head);
+EXPORT_SYMBOL(__ipipe_unstall_root);
+EXPORT_SYMBOL(__ipipe_restore_root);
+EXPORT_SYMBOL(__ipipe_spin_lock_irq);
+EXPORT_SYMBOL(__ipipe_spin_unlock_irq);
+EXPORT_SYMBOL(__ipipe_spin_lock_irqsave);
+EXPORT_SYMBOL(__ipipe_spin_unlock_irqrestore);
+EXPORT_SYMBOL(__ipipe_pipeline);
+EXPORT_SYMBOL(__ipipe_lock_irq);
+EXPORT_SYMBOL(__ipipe_unlock_irq);
+EXPORT_SYMBOL(ipipe_register_domain);
+EXPORT_SYMBOL(ipipe_unregister_domain);
+EXPORT_SYMBOL(ipipe_free_virq);
+EXPORT_SYMBOL(ipipe_init_attr);
+EXPORT_SYMBOL(ipipe_catch_event);
+EXPORT_SYMBOL(ipipe_alloc_ptdkey);
+EXPORT_SYMBOL(ipipe_free_ptdkey);
+EXPORT_SYMBOL(ipipe_set_ptd);
+EXPORT_SYMBOL(ipipe_get_ptd);
+EXPORT_SYMBOL(ipipe_set_irq_affinity);
+EXPORT_SYMBOL(ipipe_send_ipi);
+EXPORT_SYMBOL(__ipipe_pend_irq);
+EXPORT_SYMBOL(__ipipe_set_irq_pending);
+
+EXPORT_SYMBOL(ipipe_critical_enter);
+EXPORT_SYMBOL(ipipe_critical_exit);
+EXPORT_SYMBOL(ipipe_trigger_irq);
+EXPORT_SYMBOL(ipipe_get_sysinfo);
diff --git a/kernel/printk.c b/kernel/printk.c
index c2d6893..adbc6e9 100644
--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -413,8 +413,88 @@ static void emit_log_char(char c)
  * then changes console_loglevel may break. This is because console_loglevel
  * is inspected when the actual printing occurs.
  */
+#ifdef CONFIG_IPIPE
+
+static IPIPE_DEFINE_SPINLOCK(__ipipe_printk_lock);
+
+static int __ipipe_printk_fill;
+
+static char __ipipe_printk_buf[LOG_BUF_LEN];
+
+static int do_printk(const char *fmt, ...);
+
+void __ipipe_flush_printk (unsigned virq, void *cookie)
+{
+	char *p = __ipipe_printk_buf;
+	int len, lmax, out = 0;
+	unsigned long flags;
+
+	goto start;
+
+	do {
+		spin_unlock_irqrestore(&__ipipe_printk_lock, flags);
+ start:
+		lmax = __ipipe_printk_fill;
+		while (out < lmax) {
+			len = strlen(p) + 1;
+			do_printk("%s", p);
+			p += len;
+			out += len;
+		}
+		spin_lock_irqsave(&__ipipe_printk_lock, flags);
+	}
+	while (__ipipe_printk_fill != lmax);
+
+	__ipipe_printk_fill = 0;
+
+	spin_unlock_irqrestore(&__ipipe_printk_lock, flags);
+}
+
+int vscnprintf(char *buf, size_t size, const char *fmt, va_list args)
+{
+	int i = vsnprintf(buf,size,fmt,args);
+	return (i >= size) ? (size - 1) : i;
+}
+
 asmlinkage int printk(const char *fmt, ...)
 {
+    	unsigned long flags, oldcount;
+	int r, fbytes;
+	va_list args;
+
+	va_start(args, fmt);
+
+	spin_lock_irqsave(&__ipipe_printk_lock, flags);
+
+	oldcount = __ipipe_printk_fill;
+	fbytes = LOG_BUF_LEN - __ipipe_printk_fill;
+
+	if (fbytes > 1)	{
+		r = vscnprintf(__ipipe_printk_buf + __ipipe_printk_fill,
+			       fbytes, fmt, args) + 1; /* account for the null byte */
+		__ipipe_printk_fill += r;
+	} else
+		r = 0;
+
+	spin_unlock_irqrestore(&__ipipe_printk_lock, flags);
+
+	if (ipipe_current_domain == ipipe_root_domain ||
+	    test_bit(IPIPE_SPRINTK_FLAG,&ipipe_current_domain->flags) ||
+	    oops_in_progress) {
+		__ipipe_flush_printk(__ipipe_printk_virq, NULL);
+	} else if (oldcount == 0)
+	    	ipipe_trigger_irq(__ipipe_printk_virq);
+
+	va_end(args);
+
+	return r;
+}
+
+static int do_printk(const char *fmt, ...)
+#else /* !CONFIG_IPIPE */
+asmlinkage int printk(const char *fmt, ...)
+#endif /* CONFIG_IPIPE */
+{
 	va_list args;
 	unsigned long flags;
 	int printed_len;
@@ -486,6 +566,7 @@ asmlinkage int printk(const char *fmt, ...)
 out:
 	return printed_len;
 }
+
 EXPORT_SYMBOL(printk);
 
 /**
diff --git a/kernel/sched.c b/kernel/sched.c
index afd6d35..115f07b 100644
--- a/kernel/sched.c
+++ b/kernel/sched.c
@@ -29,6 +29,7 @@
 #include <linux/completion.h>
 #include <linux/prefetch.h>
 #include <linux/compiler.h>
+#include <linux/module.h>
 
 #include <asm/uaccess.h>
 #include <asm/mmu_context.h>
@@ -359,6 +360,8 @@ static inline int try_to_wake_up(struct task_struct * p, int synchronous)
 	 * We want the common case fall through straight, thus the goto.
 	 */
 	spin_lock_irqsave(&runqueue_lock, flags);
+	if (p->state & TASK_NOWAKEUP)
+		goto out;
 	p->state = TASK_RUNNING;
 	if (task_on_runqueue(p))
 		goto out;
@@ -535,7 +538,61 @@ needs_resched:
 
 asmlinkage void schedule_tail(struct task_struct *prev)
 {
+#ifdef CONFIG_IPIPE
+	local_irq_disable();
+	local_irq_enable_hw();
+#endif /* CONFIG_IPIPE */
 	__schedule_tail(prev);
+	ipipe_init_notify(current);
+#ifdef CONFIG_IPIPE
+	local_irq_enable();
+#endif /* CONFIG_IPIPE */
+}
+
+static struct delayed_mmreq {
+	int in;
+	int out;
+	int count;
+#define MAX_DELAYED_MM  128  /* need ^2 here. */
+	struct mm_struct *mm[MAX_DELAYED_MM];
+} delayed_mmtab[NR_CPUS];
+
+static void __ipipe_delay_mmdrop (struct task_struct *prev)
+{
+	struct delayed_mmreq *p;
+	struct mm_struct *oldmm;
+
+    	if (prev->mm)
+		return;
+
+	p = delayed_mmtab + prev->processor;
+	oldmm = prev->active_mm;
+
+	BUG_ON(p->count >= MAX_DELAYED_MM);
+	/*
+	 * Prevent the mm from being dropped in schedule() since this
+	 * could cause 1) large latencies to high priority domains
+	 * hijacking Linux tasks, 2) subtle mm recycling error at task
+	 * exit due to co-scheduling issues, then pend a request to
+	 * drop it later in __ipipe_mmdrop_sync() when Linux is back
+	 * in control.
+	 */
+	atomic_inc(&oldmm->mm_count);
+	p->mm[p->in] = oldmm;
+	p->in = (p->in + 1) & (MAX_DELAYED_MM - 1);
+	p->count++;
+}
+
+static void __ipipe_sync_mmdrop (void)
+{
+    	struct delayed_mmreq *p = delayed_mmtab + smp_processor_id();
+
+	while (p->out != p->in) {
+		struct mm_struct *oldmm = p->mm[p->out];
+		mmdrop(oldmm);
+		p->out = (p->out + 1) & (MAX_DELAYED_MM - 1);
+		p->count--;
+	}
 }
 
 /*
@@ -673,6 +730,8 @@ repeat_schedule:
 	 * This might sound slightly confusing but makes tons of sense.
 	 */
 	prepare_to_switch();
+	prepare_arch_switch(next);
+	__ipipe_delay_mmdrop(prev);
 	{
 		struct mm_struct *mm = next->mm;
 		struct mm_struct *oldmm = prev->active_mm;
@@ -706,6 +765,9 @@ repeat_schedule:
 	}
 #endif
 	switch_to(prev, next, prev);
+	if (task_hijacked(prev))
+	    return;
+	__ipipe_sync_mmdrop();
 	__schedule_tail(prev);
 
 same_process:
@@ -1004,6 +1066,7 @@ static int setscheduler(pid_t pid, int policy,
 	retval = 0;
 	p->policy = policy;
 	p->rt_priority = lp.sched_priority;
+	ipipe_setsched_notify(p);
 
 	current->need_resched = 1;
 
@@ -1408,3 +1471,34 @@ void __init sched_init(void)
 	atomic_inc(&init_mm.mm_count);
 	enter_lazy_tlb(&init_mm, current, cpu);
 }
+
+#ifdef CONFIG_IPIPE
+
+int ipipe_setscheduler_root (struct task_struct *p, int policy, int prio)
+{
+	read_lock_irq(&tasklist_lock);
+	spin_lock(&runqueue_lock);
+
+	p->policy = policy;
+	p->rt_priority = prio;
+	current->need_resched = 1;
+
+	spin_unlock(&runqueue_lock);
+	read_unlock_irq(&tasklist_lock);
+
+	return 0;
+}
+
+int ipipe_reenter_root (struct task_struct *prev, int policy, int prio)
+{
+    	__schedule_tail(prev);
+	reacquire_kernel_lock(current);
+	local_irq_enable();
+
+	if (current->policy != policy || current->rt_priority != prio)
+		return ipipe_setscheduler_root(current,policy,prio);
+
+	return 0;
+}
+
+#endif /* CONFIG_IPIPE */
diff --git a/kernel/signal.c b/kernel/signal.c
index 77371a0..7fb130d 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -495,6 +495,7 @@ static int send_signal(int sig, struct siginfo *info, struct sigpending *signals
 static inline void signal_wake_up(struct task_struct *t)
 {
 	t->sigpending = 1;
+	ipipe_sigwake_notify(t); /* t->sigpending must be set first. */
 
 #ifdef CONFIG_SMP
 	/*
