--- /dev/null
+diff -urNp linux-2.6.0-test9/arch/i386/Kconfig linux-2.6.0-test9+kp/arch/i386/Kconfig
+--- linux-2.6.0-test9/arch/i386/Kconfig 2003-10-26 00:13:01.000000000 +0530
++++ linux-2.6.0-test9+kp/arch/i386/Kconfig 2003-11-18 07:29:47.000000000 +0530
+@@ -1133,6 +1133,37 @@ config DEBUG_KERNEL
+ Say Y here if you are developing drivers or trying to debug and
+ identify kernel problems.
+
++config KPROBES
++ bool "Kprobes"
++ depends on DEBUG_KERNEL
++ help
++ Kprobes allows you to trap at almost any kernel address, using
++ register_kprobe(), and providing a callback function. This is useful
++ for kernel debugging, non-intrusive instrumentation and testing. If
++ in doubt, say "N".
++
++config DEBUGREG
++ bool "Global Debug Registers"
++ depends on DEBUG_KERNEL
++ help
++ Global debug register settings will be honoured if this is turned on.
++ If in doubt, say "N".
++
++config KWATCH
++ bool "Kwatch points"
++ depends on DEBUG_KERNEL && DEBUGREG
++ help
++ This enables kernel-space watchpoints using processor's debug
++ registers. If in doubt, say "N".
++
++config DPROBES
++ tristate "Dynamic Probes"
++ depends on DEBUG_KERNEL && KPROBES
++ help
++ DProbes provides a higher level interface to kprobes. DProbes
++ enables you to write probe programs in a RPN language. If
++ in doubt, say "N".
++
+ config DEBUG_STACKOVERFLOW
+ bool "Check for stack overflows"
+ depends on DEBUG_KERNEL
+diff -urNp linux-2.6.0-test9/arch/i386/kernel/debugreg.c linux-2.6.0-test9+kp/arch/i386/kernel/debugreg.c
+--- linux-2.6.0-test9/arch/i386/kernel/debugreg.c 1970-01-01 05:30:00.000000000 +0530
++++ linux-2.6.0-test9+kp/arch/i386/kernel/debugreg.c 2003-11-18 07:29:47.000000000 +0530
+@@ -0,0 +1,178 @@
++/*
++ * This provides a debug register allocation mechanism, to be
++ * used by all debuggers, which need debug registers.
++ *
++ * Author: vamsi_krishna@in.ibm.com
++ * bharata@in.ibm.com
++ */
++#include <linux/kernel.h>
++#include <linux/spinlock.h>
++#include <linux/module.h>
++#include <asm/system.h>
++#include <asm/debugreg.h>
++
++struct debugreg dr_list[DR_MAX];
++unsigned long dr7_global_mask = 0;
++static spinlock_t dr_lock = SPIN_LOCK_UNLOCKED;
++
++static inline void set_dr7_global_mask(int regnum)
++{
++ switch (regnum) {
++ case 0: dr7_global_mask |= DR7_DR0_BITS; break;
++ case 1: dr7_global_mask |= DR7_DR1_BITS; break;
++ case 2: dr7_global_mask |= DR7_DR2_BITS; break;
++ case 3: dr7_global_mask |= DR7_DR3_BITS; break;
++ }
++ return;
++}
++
++static inline void clear_dr7_global_mask(int regnum)
++{
++ switch (regnum) {
++ case 0: dr7_global_mask &= ~DR7_DR0_BITS; break;
++ case 1: dr7_global_mask &= ~DR7_DR1_BITS; break;
++ case 2: dr7_global_mask &= ~DR7_DR2_BITS; break;
++ case 3: dr7_global_mask &= ~DR7_DR3_BITS; break;
++ }
++ return;
++}
++
++static int get_dr(int regnum, int flag)
++{
++ if ((flag == DR_ALLOC_GLOBAL) && (dr_list[regnum].flag == DR_UNUSED)) {
++ dr_list[regnum].flag = DR_GLOBAL;
++ set_dr7_global_mask(regnum);
++ return regnum;
++ }
++ else if ((dr_list[regnum].flag == DR_UNUSED) || (dr_list[regnum].flag == DR_LOCAL)) {
++ dr_list[regnum].use_count++;
++ dr_list[regnum].flag = DR_LOCAL;
++ return regnum;
++ }
++ return -1;
++}
++
++static int get_any_dr(int flag)
++{
++ int i;
++ if (flag == DR_ALLOC_LOCAL) {
++ for (i = 0; i < DR_MAX; i++) {
++ if (dr_list[i].flag == DR_LOCAL) {
++ dr_list[i].use_count++;
++ return i;
++ } else if (dr_list[i].flag == DR_UNUSED) {
++ dr_list[i].flag = DR_LOCAL;
++ dr_list[i].use_count = 1;
++ return i;
++ }
++ }
++ } else {
++ for (i = DR_MAX-1; i >= 0; i--) {
++ if (dr_list[i].flag == DR_UNUSED) {
++ dr_list[i].flag = DR_GLOBAL;
++ set_dr7_global_mask(i);
++ return i;
++ }
++ }
++ }
++ return -1;
++}
++
++static inline void dr_free_local(int regnum)
++{
++ if (! (--dr_list[regnum].use_count))
++ dr_list[regnum].flag = DR_UNUSED;
++ return;
++}
++
++static inline void dr_free_global(int regnum)
++{
++ dr_list[regnum].flag = DR_UNUSED;
++ dr_list[regnum].use_count = 0;
++ clear_dr7_global_mask(regnum);
++ return;
++}
++
++int dr_alloc(int regnum, int flag)
++{
++ int ret;
++
++ spin_lock(&dr_lock);
++ if (regnum == DR_ANY)
++ ret = get_any_dr(flag);
++ else if (regnum >= DR_MAX)
++ ret = -1;
++ else
++ ret = get_dr(regnum, flag);
++ spin_unlock(&dr_lock);
++ return ret;
++}
++
++int dr_free(int regnum)
++{
++ spin_lock(&dr_lock);
++ if (regnum >= DR_MAX || dr_list[regnum].flag == DR_UNUSED) {
++ spin_unlock(&dr_lock);
++ return -1;
++ }
++ if (dr_list[regnum].flag == DR_LOCAL)
++ dr_free_local(regnum);
++ else
++ dr_free_global(regnum);
++ spin_unlock(&dr_lock);
++ return 0;
++}
++
++void dr_inc_use_count(unsigned long mask)
++{
++ int i;
++
++ spin_lock(&dr_lock);
++ for (i =0; i < DR_MAX; i++) {
++ if (DR_IS_LOCAL(mask, i))
++ dr_list[i].use_count++;
++ }
++ spin_unlock(&dr_lock);
++}
++
++void dr_dec_use_count(unsigned long mask)
++{
++ int i;
++
++ spin_lock(&dr_lock);
++ for (i =0; i < DR_MAX; i++) {
++ if (DR_IS_LOCAL(mask, i))
++ dr_free_local(i);
++ }
++ spin_unlock(&dr_lock);
++}
++
++/*
++ * This routine decides if the ptrace request is for enabling or disabling
++ * a debug reg, and accordingly calls dr_alloc() or dr_free().
++ *
++ * gdb uses ptrace to write to debug registers. It assumes that writing to
++ * debug register always succeds and it doesn't check the return value of
++ * ptrace. Now with this new global debug register allocation/freeing,
++ * ptrace request for a local debug register can fail, if the required debug
++ * register is already globally allocated. Since gdb fails to notice this
++ * failure, it sometimes tries to free a debug register, which is not
++ * allocated for it.
++ */
++int enable_debugreg(unsigned long old_dr7, unsigned long new_dr7)
++{
++ int i, dr_shift = 1UL;
++ for (i = 0; i < DR_MAX; i++, dr_shift <<= 2) {
++ if ((old_dr7 ^ new_dr7) & dr_shift) {
++ if (new_dr7 & dr_shift)
++ dr_alloc(i, DR_ALLOC_LOCAL);
++ else
++ dr_free(i);
++ return 0;
++ }
++ }
++ return -1;
++}
++
++EXPORT_SYMBOL(dr_alloc);
++EXPORT_SYMBOL(dr_free);
+diff -urNp linux-2.6.0-test9/arch/i386/kernel/entry.S linux-2.6.0-test9+kp/arch/i386/kernel/entry.S
+--- linux-2.6.0-test9/arch/i386/kernel/entry.S 2003-10-26 00:13:14.000000000 +0530
++++ linux-2.6.0-test9+kp/arch/i386/kernel/entry.S 2003-11-18 07:29:47.000000000 +0530
+@@ -493,10 +493,17 @@ ENTRY(debug)
+ jne debug_stack_correct
+ FIX_STACK(12, debug_stack_correct, debug_esp_fix_insn)
+ debug_stack_correct:
+- pushl $0
+- pushl $do_debug
+- jmp error_code
+-
++ pushl $-1 # mark this as an int
++ SAVE_ALL
++ movl %esp,%edx
++ pushl $0
++ pushl %edx
++ call do_debug
++ addl $8,%esp
++ testl %eax,%eax
++ jnz restore_all
++ jmp ret_from_exception
++
+ /*
+ * NMI is doubly nasty. It can happen _while_ we're handling
+ * a debug fault, and the debug fault hasn't yet been able to
+@@ -544,9 +551,16 @@ nmi_debug_stack_fixup:
+ jmp nmi_stack_correct
+
+ ENTRY(int3)
++ pushl $-1 # mark this as an int
++ SAVE_ALL
++ movl %esp,%edx
+ pushl $0
+- pushl $do_int3
+- jmp error_code
++ pushl %edx
++ call do_int3
++ addl $8,%esp
++ testl %eax,%eax
++ jnz restore_all
++ jmp ret_from_exception
+
+ ENTRY(overflow)
+ pushl $0
+diff -urNp linux-2.6.0-test9/arch/i386/kernel/kprobes.c linux-2.6.0-test9+kp/arch/i386/kernel/kprobes.c
+--- linux-2.6.0-test9/arch/i386/kernel/kprobes.c 1970-01-01 05:30:00.000000000 +0530
++++ linux-2.6.0-test9+kp/arch/i386/kernel/kprobes.c 2003-11-18 07:30:12.000000000 +0530
+@@ -0,0 +1,249 @@
++/*
++ * Support for kernel probes.
++ * (C) 2002 Vamsi Krishna S <vamsi_krishna@in.ibm.com>.
++ */
++
++#include <linux/config.h>
++#include <linux/kprobes.h>
++#include <linux/ptrace.h>
++#include <linux/spinlock.h>
++#include <linux/preempt.h>
++
++/* kprobe_status settings */
++#define KPROBE_HIT_ACTIVE 0x00000001
++#define KPROBE_HIT_SS 0x00000002
++
++static struct kprobe *current_kprobe;
++static unsigned long kprobe_status, kprobe_old_eflags, kprobe_saved_eflags;
++
++void kprobes_asm_code_start(void){}
++/*
++ * returns non-zero if opcode modifies the interrupt flag.
++ */
++static inline int is_IF_modifier(kprobe_opcode_t opcode)
++{
++ switch(opcode) {
++ case 0xfa: /* cli */
++ case 0xfb: /* sti */
++ case 0xcf: /* iret/iretd */
++ case 0x9d: /* popf/popfd */
++ return 1;
++ }
++ return 0;
++}
++
++void arch_prepare_kprobe(struct kprobe *p)
++{
++ memcpy(p->insn, p->addr, MAX_INSN_SIZE);
++}
++
++static inline void disarm_kprobe(struct kprobe *p, struct pt_regs *regs)
++{
++ set_opcode(p, p->opcode);
++ regs->eip = (unsigned long)p->addr;
++}
++
++static void prepare_singlestep(struct kprobe *p, struct pt_regs *regs)
++{
++ regs->eflags |= TF_MASK;
++ regs->eflags &= ~IF_MASK;
++
++ if (!(p->user)) {
++ regs->eip = (unsigned long)&p->insn;
++ } else {
++ set_opcode(p, p->opcode);
++ regs->eip = (unsigned long)p->addr;
++ }
++}
++
++/*
++ * Interrupts are disabled on entry as trap3 is an interrupt gate and they
++ * remain disabled thorough out this function.
++ */
++int kprobe_handler(struct pt_regs *regs)
++{
++ struct kprobe *p;
++ int ret = 0;
++ kprobe_opcode_t *addr = (kprobe_opcode_t *)(regs->eip - sizeof(kprobe_opcode_t));
++
++ /* We're in an interrupt, but this is clear and BUG()-safe. */
++ preempt_disable();
++
++ /* Check we're not actually recursing */
++ if (kprobe_running()) {
++ /* We *are* holding lock here, so this is safe.
++ Disarm the probe we just hit, and ignore it. */
++ p = get_kprobe(addr);
++ if (p) {
++ disarm_kprobe(p, regs);
++ put_kprobe(p);
++ ret = 1;
++ }
++ /* If it's not ours, can't be delete race, (we hold lock). */
++ goto no_kprobe;
++ }
++
++ lock_kprobes();
++ p = get_kprobe(addr);
++ if (!p) {
++ unlock_kprobes();
++ /* Unregistered (on another cpu) after this hit? Ignore */
++ if (*addr != BREAKPOINT_INSTRUCTION)
++ ret = 1;
++ /* Not one of ours: let kernel handle it */
++ goto no_kprobe;
++ }
++ /* If p->opcode == BREAKPOINT_INSTRUCTION, means we haven't yet inserted
++ * breakpoint corresponding to this probe.No need to handle this probe.
++ */
++ if (p->user && p->opcode == BREAKPOINT_INSTRUCTION) {
++ unlock_kprobes();
++ ret = 1;
++ goto no_kprobe;
++ }
++
++ kprobe_status = KPROBE_HIT_ACTIVE;
++ current_kprobe = p;
++ kprobe_saved_eflags = kprobe_old_eflags
++ = (regs->eflags & (TF_MASK|IF_MASK));
++ if (is_IF_modifier(p->opcode))
++ kprobe_saved_eflags &= ~IF_MASK;
++
++ /* If the pre_handler returns 1, means probes are marked for emergency
++ * removal. Restore back the original opcode.
++ */
++ if (p->pre_handler(p, regs)) {
++ if (!p->user)
++ disarm_kprobe(p, regs);
++ else {
++ set_opcode(p, p->opcode);
++ regs->eip = (unsigned long)p->addr;
++ }
++ ret = 1;
++ unlock_kprobes();
++ goto no_kprobe;
++ }
++
++ prepare_singlestep(p, regs);
++ //disarm_kprobe(p, regs);
++ kprobe_status = KPROBE_HIT_SS;
++ return 1;
++
++ if (p->post_handler)
++ p->post_handler(p, regs, 0);
++ unlock_kprobes();
++ preempt_enable_no_resched();
++ return 1;
++
++no_kprobe:
++ preempt_enable_no_resched();
++ return ret;
++}
++
++static void resume_execution(struct kprobe *p, struct pt_regs *regs)
++{
++ unsigned long *tos = ®s->esp;
++ unsigned long next_eip = 0;
++ unsigned long copy_eip = (unsigned long)&p->insn;
++ unsigned long orig_eip = (unsigned long)p->addr;
++
++ /*
++ * We singlestepped with interrupts disabled. So, the result on
++ * the stack would be incorrect for "pushfl" instruction.
++ * Note that regs->esp is actually the top of the stack when the
++ * trap occurs in kernel space.
++ */
++ switch(p->insn[0]) {
++ case 0x9c: /* pushfl */
++ if (regs->eip < PAGE_OFFSET)
++ tos = (unsigned long *)regs->esp;
++ *tos &= ~(TF_MASK | IF_MASK);
++ *tos |= kprobe_old_eflags;
++ break;
++ case 0xe8: /* call relative */
++ *tos = orig_eip + (*tos - copy_eip);
++ break;
++ case 0xff:
++ if ((p->insn[1] & 0x30) == 0x10) { /* call absolute, indirect */
++ next_eip = regs->eip;
++ *tos = orig_eip + (*tos - copy_eip);
++ } else if (((p->insn[1] & 0x31) == 0x20) || /* jmp near, absolute indirect */
++ ((p->insn[1] & 0x31) == 0x21)) { /* jmp far, absolute indirect */
++ next_eip = regs->eip;
++ }
++ break;
++ case 0xea: /* jmp absolute */
++ next_eip = regs->eip;
++ break;
++ default:
++ break;
++ }
++
++ regs->eflags &= ~TF_MASK;
++ if (next_eip) {
++ regs->eip = next_eip;
++ } else {
++ regs->eip = orig_eip + (regs->eip - copy_eip);
++ }
++}
++
++static void resume_execution_user(struct kprobe *p, struct pt_regs *regs)
++{
++ regs->eflags &= ~TF_MASK;
++ set_opcode(p, BREAKPOINT_INSTRUCTION);
++}
++
++/*
++ * Interrupts are disabled on entry as trap1 is an interrupt gate and they
++ * remain disabled thorough out this function. And we hold kprobe lock.
++ */
++int post_kprobe_handler(struct pt_regs *regs)
++{
++ if (!kprobe_running())
++ return 0;
++
++ if (current_kprobe->post_handler)
++ current_kprobe->post_handler(current_kprobe, regs, 0);
++
++ if (!(current_kprobe->user))
++ resume_execution(current_kprobe, regs);
++ else
++ resume_execution_user(current_kprobe, regs);
++ regs->eflags |= kprobe_saved_eflags;
++
++ put_kprobe(current_kprobe);
++ unlock_kprobes();
++ preempt_enable_no_resched();
++
++ /*
++ * if somebody else is singlestepping across a probe point, eflags
++ * will have TF set, in which case, continue the remaining processing
++ * of do_debug, as if this is not a probe hit.
++ */
++ if (regs->eflags & TF_MASK)
++ return 0;
++
++ return 1;
++}
++
++/* Interrupts disabled, kprobe_lock held. */
++int kprobe_fault_handler(struct pt_regs *regs, int trapnr)
++{
++ if (current_kprobe->fault_handler
++ && current_kprobe->fault_handler(current_kprobe, regs, trapnr))
++ return 1;
++
++ if (kprobe_status & KPROBE_HIT_SS) {
++ if (!(current_kprobe->user))
++ resume_execution(current_kprobe, regs);
++ else
++ resume_execution_user(current_kprobe, regs);
++
++ regs->eflags |= kprobe_old_eflags;
++ put_kprobe(current_kprobe);
++ unlock_kprobes();
++ preempt_enable_no_resched();
++ }
++ return 0;
++}
++void kprobes_asm_code_end(void) {}
+diff -urNp linux-2.6.0-test9/arch/i386/kernel/kwatch.c linux-2.6.0-test9+kp/arch/i386/kernel/kwatch.c
+--- linux-2.6.0-test9/arch/i386/kernel/kwatch.c 1970-01-01 05:30:00.000000000 +0530
++++ linux-2.6.0-test9+kp/arch/i386/kernel/kwatch.c 2003-11-18 07:30:12.000000000 +0530
+@@ -0,0 +1,142 @@
++/*
++ * Support for kernel watchpoints.
++ * (C) 2002 Vamsi Krishna S <vamsi_krishna@in.ibm.com>.
++ */
++#include <linux/config.h>
++#include <linux/kprobes.h>
++#include <linux/ptrace.h>
++#include <linux/spinlock.h>
++#include <linux/module.h>
++#include <asm/kwatch.h>
++#include <asm/debugreg.h>
++#include <asm/bitops.h>
++
++static struct kwatch kwatch_list[DR_MAX];
++static spinlock_t kwatch_lock = SPIN_LOCK_UNLOCKED;
++static unsigned long kwatch_in_progress; /* currently being handled */
++
++struct dr_info {
++ int debugreg;
++ unsigned long addr;
++ int type;
++};
++
++void kwatch_asm_start(void) {}
++static inline void write_smp_dr(void *info)
++{
++ struct dr_info *dr = (struct dr_info *)info;
++
++ if (cpu_has_de && dr->type == DR_TYPE_IO)
++ set_in_cr4(X86_CR4_DE);
++ write_dr(dr->debugreg, dr->addr);
++}
++
++/* Update the debug register on all CPUs */
++static void sync_dr(int debugreg, unsigned long addr, int type)
++{
++ struct dr_info dr;
++ dr.debugreg = debugreg;
++ dr.addr = addr;
++ dr.type = type;
++ smp_call_function(write_smp_dr, &dr, 0, 0);
++}
++
++/*
++ * Interrupts are disabled on entry as trap1 is an interrupt gate and they
++ * remain disabled thorough out this function.
++ */
++int kwatch_handler(unsigned long condition, struct pt_regs *regs)
++{
++ int debugreg = dr_trap(condition);
++ unsigned long addr = dr_trap_addr(condition);
++ int retval = 0;
++
++ if (!(condition & (DR_TRAP0|DR_TRAP1|DR_TRAP2|DR_TRAP3))) {
++ return 0;
++ }
++
++ /* We're in an interrupt, but this is clear and BUG()-safe. */
++ preempt_disable();
++
++ /* If we are recursing, we already hold the lock. */
++ if (kwatch_in_progress) {
++ goto recursed;
++ }
++ set_bit(debugreg, &kwatch_in_progress);
++
++ spin_lock(&kwatch_lock);
++ if (kwatch_list[debugreg].addr != addr)
++ goto out;
++
++ if (kwatch_list[debugreg].handler) {
++ kwatch_list[debugreg].handler(&kwatch_list[debugreg], regs, debugreg);
++ }
++
++ if (kwatch_list[debugreg].type == DR_TYPE_EXECUTE)
++ regs->eflags |= RF_MASK;
++out:
++ clear_bit(debugreg, &kwatch_in_progress);
++ spin_unlock(&kwatch_lock);
++ preempt_enable_no_resched();
++ return retval;
++
++recursed:
++ if (kwatch_list[debugreg].type == DR_TYPE_EXECUTE)
++ regs->eflags |= RF_MASK;
++ preempt_enable_no_resched();
++ return 1;
++}
++
++int register_kwatch(unsigned long addr, u8 length, u8 type,
++ kwatch_handler_t handler)
++{
++ int debugreg;
++ unsigned long dr7, flags;
++
++ debugreg = dr_alloc(DR_ANY, DR_ALLOC_GLOBAL);
++ if (debugreg < 0) {
++ return -1;
++ }
++
++ spin_lock_irqsave(&kwatch_lock, flags);
++ kwatch_list[debugreg].addr = addr;
++ kwatch_list[debugreg].length = length;
++ kwatch_list[debugreg].type = type;
++ kwatch_list[debugreg].handler = handler;
++ spin_unlock_irqrestore(&kwatch_lock, flags);
++
++ write_dr(debugreg, (unsigned long)addr);
++ sync_dr(debugreg, (unsigned long)addr, type);
++ if (cpu_has_de && type == DR_TYPE_IO)
++ set_in_cr4(X86_CR4_DE);
++
++ dr7 = read_dr(7);
++ SET_DR7(dr7, debugreg, type, length);
++ write_dr(7, dr7);
++ sync_dr(7, dr7, 0);
++ return debugreg;
++}
++
++void unregister_kwatch(int debugreg)
++{
++ unsigned long flags;
++ unsigned long dr7 = read_dr(7);
++
++ RESET_DR7(dr7, debugreg);
++ write_dr(7, dr7);
++ sync_dr(7, dr7, 0);
++ dr_free(debugreg);
++
++ spin_lock_irqsave(&kwatch_lock, flags);
++ kwatch_list[debugreg].addr = 0;
++ kwatch_list[debugreg].handler = NULL;
++ spin_unlock_irqrestore(&kwatch_lock, flags);
++}
++
++void kwatch_asm_end(void) {}
++
++EXPORT_SYMBOL_GPL(register_kwatch);
++EXPORT_SYMBOL_GPL(unregister_kwatch);
++EXPORT_SYMBOL_GPL(kwatch_asm_start);
++EXPORT_SYMBOL_GPL(kwatch_asm_end);
++
+diff -urNp linux-2.6.0-test9/arch/i386/kernel/Makefile linux-2.6.0-test9+kp/arch/i386/kernel/Makefile
+--- linux-2.6.0-test9/arch/i386/kernel/Makefile 2003-10-26 00:13:03.000000000 +0530
++++ linux-2.6.0-test9+kp/arch/i386/kernel/Makefile 2003-11-18 07:29:47.000000000 +0530
+@@ -26,10 +26,13 @@ obj-$(CONFIG_X86_IO_APIC) += io_apic.o
+ obj-$(CONFIG_X86_NUMAQ) += numaq.o
+ obj-$(CONFIG_X86_SUMMIT) += summit.o
+ obj-$(CONFIG_EDD) += edd.o
++obj-$(CONFIG_KPROBES) += kprobes.o
+ obj-$(CONFIG_MODULES) += module.o
+ obj-y += sysenter.o vsyscall.o
+ obj-$(CONFIG_ACPI_SRAT) += srat.o
+ obj-$(CONFIG_HPET_TIMER) += time_hpet.o
++obj-$(CONFIG_DEBUGREG) += debugreg.o
++obj-$(CONFIG_KWATCH) += kwatch.o
+
+ EXTRA_AFLAGS := -traditional
+
+diff -urNp linux-2.6.0-test9/arch/i386/kernel/process.c linux-2.6.0-test9+kp/arch/i386/kernel/process.c
+--- linux-2.6.0-test9/arch/i386/kernel/process.c 2003-10-26 00:12:40.000000000 +0530
++++ linux-2.6.0-test9+kp/arch/i386/kernel/process.c 2003-11-18 07:29:47.000000000 +0530
+@@ -50,6 +50,7 @@
+ #ifdef CONFIG_MATH_EMULATION
+ #include <asm/math_emu.h>
+ #endif
++#include <asm/debugreg.h>
+
+ #include <linux/irq.h>
+ #include <linux/err.h>
+@@ -295,12 +296,16 @@ void exit_thread(void)
+ kfree(tsk->thread.io_bitmap_ptr);
+ tsk->thread.io_bitmap_ptr = NULL;
+ }
++ if (tsk->thread.debugreg[7])
++ dr_dec_use_count(tsk->thread.debugreg[7]);
+ }
+
+ void flush_thread(void)
+ {
+ struct task_struct *tsk = current;
+
++ if (tsk->thread.debugreg[7])
++ dr_dec_use_count(tsk->thread.debugreg[7]);
+ memset(tsk->thread.debugreg, 0, sizeof(unsigned long)*8);
+ memset(tsk->thread.tls_array, 0, sizeof(tsk->thread.tls_array));
+ /*
+@@ -390,6 +395,9 @@ int copy_thread(int nr, unsigned long cl
+ desc->b = LDT_entry_b(&info);
+ }
+
++ if (tsk->thread.debugreg[7])
++ dr_inc_use_count(tsk->thread.debugreg[7]);
++
+ err = 0;
+ out:
+ if (err && p->thread.io_bitmap_ptr)
+@@ -532,6 +540,24 @@ struct task_struct * __switch_to(struct
+ /*
+ * Now maybe reload the debug registers
+ */
++#ifdef CONFIG_DEBUGREG
++{
++ /*
++ * Don't reload global debug registers. Don't touch the global debug
++ * register settings in dr7.
++ */
++ unsigned long next_dr7 = next->debugreg[7];
++ if (unlikely(next_dr7)) {
++ if (DR7_L0(next_dr7)) loaddebug(next, 0);
++ if (DR7_L1(next_dr7)) loaddebug(next, 1);
++ if (DR7_L2(next_dr7)) loaddebug(next, 2);
++ if (DR7_L3(next_dr7)) loaddebug(next, 3);
++ /* no 4 and 5 */
++ loaddebug(next, 6);
++ load_process_dr7(next_dr7);
++ }
++}
++#else
+ if (unlikely(next->debugreg[7])) {
+ loaddebug(next, 0);
+ loaddebug(next, 1);
+@@ -541,7 +567,7 @@ struct task_struct * __switch_to(struct
+ loaddebug(next, 6);
+ loaddebug(next, 7);
+ }
+-
++#endif
+ if (unlikely(prev->io_bitmap_ptr || next->io_bitmap_ptr)) {
+ if (next->io_bitmap_ptr) {
+ /*
+diff -urNp linux-2.6.0-test9/arch/i386/kernel/ptrace.c linux-2.6.0-test9+kp/arch/i386/kernel/ptrace.c
+--- linux-2.6.0-test9/arch/i386/kernel/ptrace.c 2003-10-26 00:13:15.000000000 +0530
++++ linux-2.6.0-test9+kp/arch/i386/kernel/ptrace.c 2003-11-18 07:29:47.000000000 +0530
+@@ -350,6 +350,11 @@ asmlinkage int sys_ptrace(long request,
+
+ addr -= (long) &dummy->u_debugreg;
+ addr = addr >> 2;
++
++ if (addr == 7 && (enable_debugreg(child->thread.debugreg[addr], data)) < 0) {
++ ret = -EBUSY;
++ break;
++ }
+ child->thread.debugreg[addr] = data;
+ ret = 0;
+ }
+diff -urNp linux-2.6.0-test9/arch/i386/kernel/signal.c linux-2.6.0-test9+kp/arch/i386/kernel/signal.c
+--- linux-2.6.0-test9/arch/i386/kernel/signal.c 2003-10-26 00:13:33.000000000 +0530
++++ linux-2.6.0-test9+kp/arch/i386/kernel/signal.c 2003-11-18 07:29:47.000000000 +0530
+@@ -23,6 +23,7 @@
+ #include <asm/ucontext.h>
+ #include <asm/uaccess.h>
+ #include <asm/i387.h>
++#include <asm/debugreg.h>
+ #include "sigframe.h"
+
+ #define DEBUG_SIG 0
+@@ -578,7 +579,7 @@ int do_signal(struct pt_regs *regs, sigs
+ * have been cleared if the watchpoint triggered
+ * inside the kernel.
+ */
+- __asm__("movl %0,%%db7" : : "r" (current->thread.debugreg[7]));
++ load_process_dr7(current->thread.debugreg[7]);
+
+ /* Whee! Actually deliver the signal. */
+ handle_signal(signr, &info, oldset, regs);
+diff -urNp linux-2.6.0-test9/arch/i386/kernel/traps.c linux-2.6.0-test9+kp/arch/i386/kernel/traps.c
+--- linux-2.6.0-test9/arch/i386/kernel/traps.c 2003-10-26 00:13:02.000000000 +0530
++++ linux-2.6.0-test9+kp/arch/i386/kernel/traps.c 2003-11-18 07:29:47.000000000 +0530
+@@ -25,6 +25,7 @@
+ #include <linux/highmem.h>
+ #include <linux/kallsyms.h>
+ #include <linux/ptrace.h>
++#include <linux/kprobes.h>
+
+ #ifdef CONFIG_EISA
+ #include <linux/ioport.h>
+@@ -41,6 +42,7 @@
+ #include <asm/io.h>
+ #include <asm/atomic.h>
+ #include <asm/debugreg.h>
++#include <asm/kwatch.h>
+ #include <asm/desc.h>
+ #include <asm/i387.h>
+ #include <asm/nmi.h>
+@@ -362,7 +364,6 @@ asmlinkage void do_##name(struct pt_regs
+ }
+
+ DO_VM86_ERROR_INFO( 0, SIGFPE, "divide error", divide_error, FPE_INTDIV, regs->eip)
+-DO_VM86_ERROR( 3, SIGTRAP, "int3", int3)
+ DO_VM86_ERROR( 4, SIGSEGV, "overflow", overflow)
+ DO_VM86_ERROR( 5, SIGSEGV, "bounds", bounds)
+ DO_ERROR_INFO( 6, SIGILL, "invalid operand", invalid_op, ILL_ILLOPN, regs->eip)
+@@ -379,6 +380,9 @@ asmlinkage void do_general_protection(st
+
+ if (regs->eflags & VM_MASK)
+ goto gp_in_vm86;
++
++ if (kprobe_running() && kprobe_fault_handler(regs, 13))
++ return;
+
+ if (!(regs->xcs & 3))
+ goto gp_in_kernel;
+@@ -500,6 +504,17 @@ void unset_nmi_callback(void)
+ nmi_callback = dummy_nmi_callback;
+ }
+
++asmlinkage int do_int3(struct pt_regs *regs, long error_code)
++{
++ if (kprobe_handler(regs))
++ return 1;
++ /* This is an interrupt gate, because kprobes wants interrupts
++ disabled. Normal trap handlers don't. */
++ restore_interrupts(regs);
++ do_trap(3, SIGTRAP, "int3", 1, regs, error_code, NULL);
++ return 0;
++}
++
+ /*
+ * Our handling of the processor debug registers is non-trivial.
+ * We do not clear them on entry and exit from the kernel. Therefore
+@@ -522,14 +537,20 @@ void unset_nmi_callback(void)
+ * find every occurrence of the TF bit that could be saved away even
+ * by user code)
+ */
+-asmlinkage void do_debug(struct pt_regs * regs, long error_code)
++asmlinkage int do_debug(struct pt_regs * regs, long error_code)
+ {
+ unsigned int condition;
+ struct task_struct *tsk = current;
+ siginfo_t info;
+
++ if (post_kprobe_handler(regs))
++ return 1;
++
+ __asm__ __volatile__("movl %%db6,%0" : "=r" (condition));
+
++ if (kwatch_handler(condition, regs))
++ return 1;
++
+ /* It's safe to allow irq's after DR6 has been saved */
+ if (regs->eflags & X86_EFLAGS_IF)
+ local_irq_enable();
+@@ -581,20 +602,18 @@ asmlinkage void do_debug(struct pt_regs
+ * the signal is delivered.
+ */
+ clear_dr7:
+- __asm__("movl %0,%%db7"
+- : /* no output */
+- : "r" (0));
+- return;
++ load_process_dr7(0);
++ return 0;
+
+ debug_vm86:
+ handle_vm86_trap((struct kernel_vm86_regs *) regs, error_code, 1);
+- return;
++ return 0;
+
+ clear_TF_reenable:
+ set_tsk_thread_flag(tsk, TIF_SINGLESTEP);
+ clear_TF:
+ regs->eflags &= ~TF_MASK;
+- return;
++ return 0;
+ }
+
+ /*
+@@ -760,6 +779,8 @@ asmlinkage void math_state_restore(struc
+ struct thread_info *thread = current_thread_info();
+ struct task_struct *tsk = thread->task;
+
++ if (kprobe_running() && kprobe_fault_handler(®s, 7))
++ return;
+ clts(); /* Allow maths ops (or we recurse) */
+ if (!tsk->used_math)
+ init_fpu(tsk);
+@@ -854,7 +875,7 @@ void __init trap_init(void)
+ set_trap_gate(0,÷_error);
+ set_intr_gate(1,&debug);
+ set_intr_gate(2,&nmi);
+- set_system_gate(3,&int3); /* int3-5 can be called from all */
++ _set_gate(idt_table+3,14,3,&int3,__KERNEL_CS); /* int3-5 can be called from all */
+ set_system_gate(4,&overflow);
+ set_system_gate(5,&bounds);
+ set_trap_gate(6,&invalid_op);
+diff -urNp linux-2.6.0-test9/arch/i386/mm/fault.c linux-2.6.0-test9+kp/arch/i386/mm/fault.c
+--- linux-2.6.0-test9/arch/i386/mm/fault.c 2003-10-26 00:12:43.000000000 +0530
++++ linux-2.6.0-test9+kp/arch/i386/mm/fault.c 2003-11-18 07:29:47.000000000 +0530
+@@ -21,6 +21,7 @@
+ #include <linux/vt_kern.h> /* For unblank_screen() */
+ #include <linux/highmem.h>
+ #include <linux/module.h>
++#include <linux/kprobes.h>
+
+ #include <asm/system.h>
+ #include <asm/uaccess.h>
+@@ -222,6 +223,9 @@ asmlinkage void do_page_fault(struct pt_
+ /* get the address */
+ __asm__("movl %%cr2,%0":"=r" (address));
+
++ if (kprobe_running() && kprobe_fault_handler(regs, 14))
++ return;
++
+ /* It's safe to allow irq's after cr2 has been saved */
+ if (regs->eflags & (X86_EFLAGS_IF|VM_MASK))
+ local_irq_enable();
+diff -urNp linux-2.6.0-test9/drivers/dprobes/dprobes.c linux-2.6.0-test9+kp/drivers/dprobes/dprobes.c
+--- linux-2.6.0-test9/drivers/dprobes/dprobes.c 1970-01-01 05:30:00.000000000 +0530
++++ linux-2.6.0-test9+kp/drivers/dprobes/dprobes.c 2003-11-18 07:30:12.000000000 +0530
+@@ -0,0 +1,1589 @@
++/*
++ * IBM Dynamic Probes
++ * Copyright (c) International Business Machines Corp., 2000
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ */
++
++#include <linux/dprobes.h>
++#include <linux/dcache.h>
++#include <linux/pagemap.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/slab.h>
++#include <linux/vmalloc.h>
++#include <linux/swap.h>
++#include <linux/smp.h>
++#ifdef CONFIG_MAGIC_SYSRQ
++#include <linux/sysrq.h>
++#endif
++
++#include <asm/semaphore.h>
++#include <asm/uaccess.h>
++#include <asm/errno.h>
++#ifdef CONFIG_DEBUGREG
++#include <asm/debugreg.h>
++#endif
++
++#ifdef CONFIG_SMP
++struct dprobes_struct dprobes_set[NR_CPUS];
++#else
++struct dprobes_struct dprobes;
++#endif
++
++struct dp_module_struct *dp_module_list = NULL;
++DECLARE_RWSEM(dp_modlist_sem);
++
++byte_t *dp_heap = NULL;
++unsigned long dp_num_heap = 0;
++rwlock_t dp_heap_lock = RW_LOCK_UNLOCKED;
++
++unsigned long *dp_gv = NULL;
++unsigned long dp_num_gv = 0;
++rwlock_t dp_gv_lock = RW_LOCK_UNLOCKED;
++
++
++#ifdef CONFIG_MAGIC_SYSRQ
++static struct work_struct dprobes_work;
++static struct workqueue_struct *dprobes_wq;
++static void dprobes_sysrq( int , struct pt_regs *, struct tty_struct *);
++static struct sysrq_key_op key_op = {
++ .handler = &dprobes_sysrq,
++ .help_msg = "remoVe_dynamic_probes",
++ .action_msg = "Deactivating all probepoints",
++};
++unsigned long emergency_remove = 0; /*Flag to disable active dprobes*/
++#endif
++
++
++void dprobes_code_start(void) { }
++
++static void init_trace_hdr(struct dp_module_struct *m)
++{
++ unsigned long log_flags = m->pgm.flags & DP_LOG_MASK;
++ unsigned long mask = 0;
++ unsigned short len = sizeof(m->hdr);
++
++ mask |= DP_HDR_MAJOR | DP_HDR_MINOR;
++#ifdef CONFIG_SMP
++ mask |= DP_HDR_CPU;
++ len += sizeof(int);
++#endif
++ if (log_flags & DP_LOG_PID) {
++ mask |= DP_HDR_PID;
++ len += sizeof(pid_t);
++ }
++ if (log_flags & DP_LOG_UID) {
++ mask |= DP_HDR_UID;
++ len += sizeof(uid_t);
++ }
++ if (log_flags & DP_LOG_CS_EIP) {
++ mask |= DP_HDR_CS | DP_HDR_EIP;
++ len += (sizeof(unsigned short) + sizeof(unsigned long));
++ }
++ if (log_flags & DP_LOG_SS_ESP) {
++ mask |= DP_HDR_SS | DP_HDR_ESP;
++ len += (sizeof(unsigned short) + sizeof(unsigned long));
++ }
++ if (log_flags & DP_LOG_TSC) {
++ mask |= DP_HDR_TSC;
++ len += sizeof(struct timeval);
++ }
++ if (log_flags & DP_LOG_PROCNAME) {
++ mask |= DP_HDR_PROCNAME;
++ len += 16; /* size of task_struct.comm */
++ }
++ m->hdr.facility_id = DP_TRACE_HDR_ID;
++ m->hdr.mask = mask;
++ m->hdr.len = len;
++ /* Adjust the logmax to take care of log header bytes */
++ m->pgm.logmax += len;
++ return;
++}
++
++#if defined(CONFIG_TRACE) || defined(CONFIG_TRACE_MODULE)
++#include <linux/trace.h>
++static inline void get_trace_id(struct dp_module_struct *m)
++{
++ m->trace_id = trace_create_event(m->pgm.name, NULL, CUSTOM_EVENT_FORMAT_TYPE_HEX, NULL);
++}
++static inline void free_trace_id(struct dp_module_struct *m)
++{
++ trace_destroy_event(m->trace_id);
++}
++#else
++#define get_trace_id(m)
++#define free_trace_id(m)
++#endif
++
++static inline struct dp_module_struct * find_mod_by_inode(struct inode *inode)
++{
++ struct dp_module_struct *m;
++ for (m = dp_module_list; m; m = m->next) {
++ if (m->inode == inode)
++ break;
++ }
++ return m;
++}
++
++static inline struct dp_module_struct * find_mod_by_name(const char * name)
++{
++ struct dp_module_struct *m;
++ for (m = dp_module_list; m ; m = m->next) {
++ if (!strcmp(m->pgm.name, name))
++ break;
++ }
++ return m;
++}
++
++static inline void link_module(struct dp_module_struct *m)
++{
++ m->next = dp_module_list;
++ dp_module_list = m;
++ return;
++}
++
++static inline void unlink_module(struct dp_module_struct *m)
++{
++ struct dp_module_struct *prev;
++
++ if (m == dp_module_list) {
++ dp_module_list = m->next;
++ return;
++ }
++ prev = dp_module_list;
++ while (prev->next && prev->next != m)
++ prev = prev->next;
++ if (prev->next)
++ prev->next = prev->next->next;
++ return;
++}
++
++/*
++ * copied from kernel/module.c find_module().
++ */
++static struct module * find_kmodule(const char *name)
++{
++ struct module *kmod = NULL;
++#if 0
++ extern struct list_head *modules;
++ list_for_each_entry(kmod, modules, list) {
++ if (!strcmp(kmod->name, name))
++ break;
++ }
++#endif
++ return kmod;
++}
++
++/*
++ * Returns the index of the last dp_record that lies in the same page as the
++ * dp_record at start index.
++ */
++static inline unsigned short
++find_last(struct dp_module_struct *m, unsigned short start)
++{
++ int i;
++ struct dp_record_struct * rec = &m->rec[start];
++ unsigned short num_recs = m->pgm.num_points;
++ unsigned long end_offset = (rec->point.offset & PAGE_MASK) + PAGE_SIZE;
++
++ for (i = start; i < num_recs && rec->point.offset < end_offset; i++, rec++)
++ ;
++
++ return i;
++}
++
++/*
++ * Unlinks the given module from dp_module_list and frees all the memory
++ * taken by it.
++ */
++static int free_dp_module(struct dp_module_struct *m)
++{
++ /* unlink mod */
++ unlink_module(m);
++
++ if (m->flags & DP_MOD_FLAGS_LARGE_REC)
++ vfree(m->rec);
++ else
++ kfree(m->rec);
++ kfree(m->lv);
++ kfree(m->pgm.rpn_code);
++ putname(m->pgm.name);
++ kfree(m);
++ return 0;
++}
++
++/*
++ * Allocates dp_record_structs, copies dp_point_structs from user space into
++ * the dp_record_structs in the kernel space.
++ */
++static int
++copy_point_from_user (struct dp_module_struct *m, struct dp_pgm_struct *pgm)
++{
++ unsigned short count = m->pgm.num_points, i;
++ struct dp_record_struct *rec;
++ unsigned long rec_size;
++
++ if (!count)
++ return 0;
++
++ rec_size = count * sizeof(*rec);
++ if (rec_size > PAGE_SIZE) {
++ rec = vmalloc(rec_size);
++ m->flags |= DP_MOD_FLAGS_LARGE_REC;
++ } else {
++ rec = kmalloc(rec_size, GFP_KERNEL);
++ }
++ if (!rec)
++ return -ENOMEM;
++ memset(rec, 0, rec_size);
++ m->rec = rec;
++
++ for (i = 0; i < count; i++, rec++) {
++ if (copy_from_user (&rec->point, &pgm->point[i],
++ sizeof (struct dp_point_struct))) {
++ if (m->flags & DP_MOD_FLAGS_LARGE_REC)
++ vfree(m->rec);
++ else
++ kfree(m->rec);
++ m->rec = NULL;
++ return -EFAULT;
++ }
++ rec->status = DP_REC_STATUS_COMPILED;
++ rec->lock = SPIN_LOCK_UNLOCKED;
++ rec->mod = m;
++ rec->count = -rec->point.passcount;
++ if (!rec->point.maxhits) rec->point.maxhits =DEFAULT_MAXHITS;
++ }
++ return 0;
++}
++
++/*
++ * We may have to realloc global variables area.
++ */
++static int check_gv_space(unsigned short num_gv)
++{
++ unsigned long *tmp, *tmp1;
++ unsigned long eflags;
++
++ if (num_gv <= dp_num_gv)
++ return 0;
++
++ tmp = kmalloc(num_gv * sizeof(*tmp), GFP_KERNEL);
++ if (!tmp)
++ return -ENOMEM;
++ memset(tmp, 0, num_gv * sizeof(*tmp));
++
++ write_lock_irqsave(&dp_gv_lock, eflags);
++ if (dp_num_gv) {
++ memcpy((void *)tmp, (void *)dp_gv,
++ sizeof(unsigned long)*dp_num_gv);
++ }
++ tmp1 = dp_gv;
++ dp_gv = tmp;
++ dp_num_gv = num_gv;
++ write_unlock_irqrestore(&dp_gv_lock, eflags);
++
++ kfree(tmp1);
++ return 0;
++}
++
++static int check_heap_space(unsigned short num_heap)
++{
++ byte_t *tmp, *tmp1;
++ unsigned long eflags;
++
++ if (num_heap * num_online_cpus() <= dp_num_heap)
++ return 0;
++
++ tmp = kmalloc(num_heap * sizeof(*tmp) * num_online_cpus(), GFP_KERNEL);
++ if (!tmp)
++ return -ENOMEM;
++
++ write_lock_irqsave(&dp_heap_lock, eflags);
++ tmp1 = dp_heap;
++ dp_heap = tmp;
++ dp_num_heap = num_heap * num_online_cpus();
++ write_unlock_irqrestore(&dp_heap_lock, eflags);
++ kfree(tmp1);
++
++ return 0;
++}
++
++/*
++ * Allocates a dp_module_struct and copies the dp_pgm_struct from user space
++ * into it, in kernel space.
++ */
++static int
++copy_pgm_from_user(struct dp_module_struct **mod, struct dp_pgm_struct *pgm)
++{
++ int retval = 0;
++ struct dp_module_struct *m;
++
++ pgm->name = getname(pgm->name);
++ if (IS_ERR(pgm->name)) {
++ retval = PTR_ERR(pgm->name);
++ goto error;
++ }
++
++ m = find_mod_by_name(pgm->name);
++ if (m) {
++ retval = -EEXIST;
++ goto err1;
++ }
++
++ m = kmalloc(sizeof(*m), GFP_KERNEL);
++ if (!m) {
++ retval = -ENOMEM;
++ goto err1;
++ }
++ memset(m, 0, sizeof(*m));
++
++ m->pgm = *pgm;
++
++ if (m->pgm.logmax > LOG_SIZE) m->pgm.logmax = DEFAULT_LOGMAX;
++ if (m->pgm.jmpmax > JMP_MAX) m->pgm.jmpmax = DEFAULT_JMPMAX;
++ if (m->pgm.ex_logmax > EX_LOG_SIZE) m->pgm.ex_logmax = DEFAULT_EX_LOG_MAX;
++ if (m->pgm.heapmax > HEAPMAX)
++ m->pgm.heapmax = DEFAULT_HEAPMAX;
++
++ if (pgm->num_lv != 0) {
++ m->lv = kmalloc(pgm->num_lv * sizeof(*m->lv), GFP_KERNEL);
++ if (!m->lv) {
++ retval = -ENOMEM;
++ goto err2;
++ }
++ memset(m->lv, 0, pgm->num_lv * sizeof(*m->lv));
++ }
++
++ if (pgm->num_gv != 0) {
++ retval = check_gv_space(pgm->num_gv);
++ if (retval < 0)
++ goto err3;
++ }
++
++ if (pgm->heapmax != 0) {
++ retval = check_heap_space(pgm->heapmax);
++ if (retval < 0)
++ goto err3;
++ }
++
++ m->pgm.rpn_code = kmalloc(pgm->rpn_length, GFP_KERNEL);
++ if (!m->pgm.rpn_code) {
++ retval = -ENOMEM;
++ goto err3;
++ }
++ if (copy_from_user(m->pgm.rpn_code, pgm->rpn_code, pgm->rpn_length)) {
++ retval = -EFAULT;
++ goto err4;
++ }
++
++ retval = copy_point_from_user(m, pgm);
++ if (retval)
++ goto err4;
++ *mod = m;
++ return 0;
++
++err4: kfree(m->pgm.rpn_code);
++err3: kfree(m->lv);
++err2: kfree(m);
++err1: putname(pgm->name);
++error: return retval;
++
++}
++
++/*
++ * Fills up the base and end fields of the given dp_module_struct with the
++ * starting and end addresses of the given kernel module, accounting for
++ * correct alignment of the code segment.
++ */
++static inline int get_kmod_bounds(struct dp_module_struct *m, struct module *kmod)
++{
++ m->base = (unsigned long)kmod->module_core;
++ m->end = kmod->core_size;
++ return 1;
++}
++
++/*
++ * Fills up the base and end fields of the dp_module_struct with the details
++ * of where the corresponding kernel module is loaded.
++ */
++static inline int get_mod_load_addr(struct dp_module_struct *m)
++{
++ struct module * kmod;
++ kmod = find_kmodule(m->pgm.name);
++ if (!kmod)
++ return 0; // module not yet loaded.
++ return get_kmod_bounds(m, kmod);
++}
++
++/*
++ * Fills up the base and end fields of dp_module_struct if this module is
++ * for a kernel or a kernel module.
++ */
++static int get_load_addr(struct dp_module_struct *m)
++{
++ if (m->pgm.flags & DP_MODTYPE_KMOD)
++ return get_mod_load_addr(m);
++ m->base = PAGE_OFFSET;
++ m->end = init_mm.end_code - PAGE_OFFSET;
++ return 1;
++}
++
++/* last thing we do should be to remove the breakpoint setting. */
++static int insert_probe(byte_t *addr, struct dp_record_struct *rec,
++ struct dp_module_struct *m, struct page * page,
++ struct vm_area_struct *vma)
++{
++ /*
++ * remove residual status bits, applicable only to probes in kernel
++ * modules when they are being reinserted.
++ */
++ rec->status &= ~(DP_REC_STATUS_REMOVED | DP_REC_STATUS_DISABLED);
++ if(!(m->pgm.flags & DP_MODTYPE_USER))
++ return __insert_probe(addr, rec, m, page);
++ else
++ return insert_probe_userspace(addr, rec, page, vma);
++}
++
++/* first thing we do should be to remove the breakpoint setting. */
++static int remove_probe(byte_t * addr, struct dp_record_struct *rec,
++ struct dp_module_struct *m, struct page * page,
++ struct vm_area_struct *vma)
++{
++ unsigned long eflags;
++ spin_lock_irqsave(&rec->lock, eflags);
++ if(!(m->pgm.flags & DP_MODTYPE_USER))
++ __remove_probe(addr, rec);
++ else
++ remove_probe_userspace(addr, rec, page, vma);
++ rec->status &= ~(DP_REC_STATUS_ACTIVE | DP_REC_STATUS_MISMATCH);
++ rec->status |= DP_REC_STATUS_REMOVED;
++ spin_unlock_irqrestore(&rec->lock, eflags);
++ return 0;
++}
++
++/*
++ * Creates hash table links and inserts all records in kernel / kmodule.
++ */
++static int insert_all_recs_k(struct dp_module_struct *m)
++{
++ struct dp_record_struct * rec;
++ byte_t * addr;
++ int i;
++ unsigned short num_recs = m->pgm.num_points;
++
++ for (i = 0, rec = m->rec; i < num_recs; i++, rec++) {
++ if (!(rec->point.address_flag & DP_ADDRESS_ABSOLUTE)) {
++ rec->point.offset += m->base;
++ }
++ addr = (byte_t *)((unsigned long)rec->point.offset);
++ insert_probe(addr, rec, m, NULL, NULL);
++ }
++ return 0;
++}
++
++/*
++ * Removes probes in kernel / kmodules: all _will_ be in memory.
++ * Note that rec->point.offset is now the linear address itself.
++ */
++static int remove_all_recs_k(struct dp_module_struct *m)
++{
++ struct dp_record_struct * rec;
++ int i;
++ byte_t * addr;
++ unsigned short num_recs = m->pgm.num_points;
++
++ for (i = 0, rec = m->rec; i < num_recs; i++, rec++) {
++ addr = (byte_t *) ((unsigned long)rec->point.offset);
++ if (rec->status & DP_REC_STATUS_ACTIVE) {
++ remove_probe(addr, rec, m, NULL, NULL);
++ }
++ if (!(rec->point.address_flag & DP_ADDRESS_ABSOLUTE))
++ rec->point.offset -= m->base;
++
++ }
++ m->base = m->end = 0;
++ return 0;
++}
++
++/*
++ * mod->pgm.name is already in kernel space.
++ */
++static int insert_k (struct dp_module_struct * m)
++{
++ if (!get_load_addr(m))
++ return 0;
++ return insert_all_recs_k(m);
++}
++
++static int remove_k(struct dp_module_struct *m)
++{
++ return remove_all_recs_k(m);
++}
++
++/*
++ * Inserts user probes into the page. Insert probes that lie in the same page
++ * after waiting for the page to be unlocked, if it was locked.
++ */
++static unsigned short insert_recs_in_page (struct dp_module_struct *m,
++ struct page * page, unsigned short start, unsigned short end,
++ struct vm_area_struct *vma)
++{
++ unsigned long alias;
++ byte_t * addr;
++ unsigned short i;
++ struct dp_record_struct * rec = &m->rec[start];
++
++ if (!page) {
++ return 1;
++ }
++ wait_on_page_locked(page);
++ if (!PageUptodate(page)) { /* we could probably retry readpage here. */
++ return 1;
++ }
++
++ alias = (unsigned long)kmap(page);
++ for (i = start; i < end; i++, rec++) {
++ addr = (byte_t *) (alias +
++ (unsigned long)(rec->point.offset & ~PAGE_MASK));
++ insert_probe(addr, rec, m, page, vma);
++ }
++ kunmap(page);
++ return 1;
++}
++
++/*
++ * Removes user probes that lie in the same page after waiting for the page to
++ * be unlocked, if it was locked. Make sure the page does not get swapped out.
++ */
++static unsigned short remove_recs_from_page (struct dp_module_struct *m,
++ struct page * page, unsigned short start, unsigned short end,
++ struct vm_area_struct *vma)
++{
++ struct dp_record_struct * rec = &m->rec[start];
++ unsigned long alias;
++ byte_t * addr;
++ unsigned short i;
++
++ if (!page)
++ return 1;
++ wait_on_page_locked(page);
++ lock_page(page);
++ alias = (unsigned long)kmap(page);
++ for (i = start; i < end; i++, rec++) {
++ addr = (byte_t *) (alias +
++ (unsigned long)(rec->point.offset & ~PAGE_MASK));
++ remove_probe(addr, rec, m, page, vma);
++ }
++ kunmap(page);
++ unlock_page(page);
++
++ return 1;
++}
++
++typedef unsigned short
++(*process_recs_func_t)(struct dp_module_struct *m, struct page *page,
++ unsigned short start, unsigned short end, struct vm_area_struct *);
++
++static inline pte_t *get_one_pte(struct mm_struct *mm, unsigned long addr)
++{
++ pgd_t * pgd;
++ pmd_t * pmd;
++ pte_t * pte = NULL;
++
++ pgd = pgd_offset(mm, addr);
++ if (pgd_none(*pgd))
++ goto end;
++ if (pgd_bad(*pgd)) {
++ pgd_ERROR(*pgd);
++ pgd_clear(pgd);
++ goto end;
++ }
++
++ pmd = pmd_offset(pgd, addr);
++ if (pmd_none(*pmd))
++ goto end;
++ if (pmd_bad(*pmd)) {
++ pmd_ERROR(*pmd);
++ pmd_clear(pmd);
++ goto end;
++ }
++
++ pte = pte_offset_kernel(pmd, addr);
++ if (pte_none(*pte))
++ pte = NULL;
++end:
++ return pte;
++}
++
++struct dp_page_locator_struct {
++ struct list_head list;
++ struct mm_struct *mm;
++ unsigned long addr;
++};
++
++/*
++ * Get the page corresponding to a given virtual addr, marking it as needing
++ * to be brought in later and checked, if it has been swapped out.
++ * It doesn't bring in discarded pages as a_ops->readpage
++ * takes care of that.
++ */
++struct page * dp_vaddr_to_page(struct vm_area_struct *vma, unsigned long addr,
++struct list_head *swapped_list_head)
++{
++ struct mm_struct *mm = vma->vm_mm;
++ pte_t *pte = get_one_pte(mm, addr);
++ struct page *page;
++ struct dp_page_locator_struct *pageloc;
++
++ if (!pte) {
++ return NULL;
++ }
++ /*
++ * If the page has been swapped out, we want to bring it in.
++ * We can't do it right away since we are holding spin locks.
++ * So just queue it up on the swapped pages list.
++ */
++ if (!pte_present(*pte)) {
++ if (swapped_list_head == NULL) return NULL;
++ pageloc = kmalloc(sizeof(*pageloc), GFP_ATOMIC);
++ if (!pageloc) return NULL;
++ pageloc->mm = mm;
++ pageloc->addr = addr;
++ atomic_inc(&mm->mm_users); /* so that mm won't go away */
++ list_add(&pageloc->list, swapped_list_head);
++ return NULL;
++ }
++
++ page = pte_page(*pte);
++ page_cache_get(page); /* TODO: Check if we need the page_cache_lock */
++
++ /*
++ * If this is a COW page, then we want to make sure that any changes
++ * made to the page get written back when swapped out. We need to do
++ * the following explicitly because dprobes will write via an alias.
++ */
++ if (IS_COW_PAGE(page, vma->vm_file->f_dentry->d_inode)) {
++ if (PageSwapCache(page))
++ delete_from_swap_cache(page);
++ set_pte(pte, pte_mkdirty(*pte));
++ }
++
++ return page;
++}
++
++/*
++ * Tracks all COW pages for the page containing the specified probe point
++ * and inserts/removes all the probe points for that page.
++ * Avoid the inserts/removes if the page's inode matches the module's inode.
++ * since that means that it is the original page and not a copied page.
++ */
++static int process_recs_in_cow_pages (struct dp_module_struct *m,
++ unsigned short start, unsigned short end,
++ process_recs_func_t process_recs_in_page)
++{
++ unsigned long offset = m->rec[start].point.offset;
++ struct vm_area_struct *vma;
++ struct page *page;
++ unsigned long addr;
++ struct mm_struct *mm;
++ struct address_space *mapping = m->inode->i_mapping;
++ struct list_head *head = &mapping->i_mmap;
++ struct list_head swapped_pages_list, *ptr;
++ struct dp_page_locator_struct *pageloc;
++
++ INIT_LIST_HEAD(&swapped_pages_list);
++ down(&mapping->i_shared_sem);
++
++ list_for_each_entry(vma, head, shared) {
++ mm = vma->vm_mm;
++ spin_lock(&mm->page_table_lock);
++
++ /* Locate the page in this vma */
++ addr = vma->vm_start - (vma->vm_pgoff << PAGE_SHIFT) + offset;
++ /*
++ * The following routine would also bring in the page if it is
++ * swapped out
++ */
++ page = dp_vaddr_to_page (vma, addr, &swapped_pages_list);
++ spin_unlock(&mm->page_table_lock);
++ if (!page)
++ continue;
++ if (IS_COW_PAGE(page, m->inode)) {
++ (*process_recs_in_page)(m, page, start, end, vma);
++ }
++ page_cache_release(page); /* TODO: Do we need a lock ? */
++ }
++ up(&mapping->i_shared_sem);
++
++ while (!list_empty(&swapped_pages_list)) {
++ ptr = swapped_pages_list.next;
++ pageloc = list_entry(ptr, struct dp_page_locator_struct, list);
++ mm = pageloc->mm;
++ addr = pageloc->addr;
++ down_read(&mm->mmap_sem);
++ /* first make sure that the vma didn't go away */
++ vma = find_vma(mm, addr);
++ /* and that this is the same one we had */
++ if (vma && vma->vm_file &&
++ (vma->vm_file->f_dentry->d_inode == m->inode) &&
++ (vma->vm_start - (vma->vm_pgoff << PAGE_SHIFT) + offset
++ == addr)) {
++ if (handle_mm_fault(mm, vma, addr, 0) <= 0) {
++ goto next;
++ }
++ page = dp_vaddr_to_page(vma, addr, NULL);
++ if (!page) /* could this ever happen ? */ {
++ goto next;
++ }
++ if (IS_COW_PAGE(page, m->inode)) {
++ (*process_recs_in_page)(m, page, start, end, vma);
++ }
++ page_cache_release(page); /*TODO: Do we need a lock ?*/
++ }
++next:
++ up_read(&mm->mmap_sem);
++ mmput(mm);
++ list_del(ptr);
++ kfree(pageloc);
++ }
++ return 1;
++}
++/*
++ * find_get_vma walks through the list of process private mappings and
++ * returns the pointer to vma containing the offset if found.
++ */
++static struct vm_area_struct * find_get_vma(unsigned long offset,
++ struct address_space *mapping)
++{
++ struct vm_area_struct *vma = NULL;
++ struct list_head *head = &mapping->i_mmap;
++ struct mm_struct *mm;
++ unsigned long start, end;
++
++ down(&mapping->i_shared_sem);
++ list_for_each_entry(vma, head, shared) {
++ mm = vma->vm_mm;
++ spin_lock(&mm->page_table_lock);
++ start = vma->vm_start - (vma->vm_pgoff << PAGE_SHIFT);
++ end = vma->vm_end - (vma->vm_pgoff << PAGE_SHIFT);
++ spin_unlock(&mm->page_table_lock);
++ if ((start + offset) < end) {
++ up(&mapping->i_shared_sem);
++ return vma;
++ }
++ }
++ up(&mapping->i_shared_sem);
++ return NULL;
++}
++/*
++ * physical insertion/removal of probes in the actual pages of the module.
++ * Register user space probes before actually instering probes in the page for
++ * a given pair of inode and offset.
++ *
++ */
++static int process_recs(struct dp_module_struct *m,
++ process_recs_func_t process_recs_in_page)
++{
++ struct address_space * mapping = m->inode->i_mapping;
++ struct page *page;
++ unsigned short i = 0, j;
++ unsigned short num_recs = m->pgm.num_points;
++ unsigned short start, end;
++ struct vm_area_struct *vma = NULL;
++
++ while (i < num_recs) {
++ start = i;
++ i = end = find_last(m, start);
++ vma = find_get_vma(m->rec[start].point.offset, mapping);
++ page = find_get_page(mapping,
++ m->rec[start].point.offset >> PAGE_CACHE_SHIFT);
++
++ for (j = start; j < end; j++) {
++ if (process_recs_in_page == insert_recs_in_page)
++ register_userspace_probes(&m->rec[j]);
++ }
++
++ (*process_recs_in_page)(m, page, start, end, vma);
++ if (page)
++ page_cache_release(page); /* TODO: Lock needed ? */
++ process_recs_in_cow_pages(m, start, end, process_recs_in_page);
++
++ for (j = start; j < end; j++) {
++ if (process_recs_in_page == remove_recs_from_page)
++ unregister_userspace_probes(&m->rec[j]);
++ }
++ }
++ return 1;
++}
++
++static inline int insert_recs(struct dp_module_struct *m)
++{
++ return process_recs(m, insert_recs_in_page);
++}
++
++static inline int remove_recs(struct dp_module_struct *m)
++{
++ return process_recs(m, remove_recs_from_page);
++}
++
++/*
++ * Gets exclusive write access to the given inode to ensure that the file
++ * on which probes are currently applied does not change. Use the function,
++ * deny_write_access_to_inode() we added in fs/namei.c.
++ */
++extern int deny_write_access_to_inode(struct inode * inode);
++static inline int ex_write_lock(struct inode * inode)
++{
++ return deny_write_access_to_inode(inode);
++}
++
++/*
++ * Called when removing user space probes to release the write lock on the
++ * inode.
++ */
++static inline int ex_write_unlock(struct inode * inode)
++{
++ atomic_inc(&inode->i_writecount);
++ return 0;
++}
++
++/*
++ * Inserts user space probes.
++ *
++ * mod->pgm.name is already in user space. This function leaves with the
++ * dentry held and taking with the inode writelock held to ensure that the
++ * file on which probes are currently active does not change from under us.
++ */
++static int insert_u (struct dp_module_struct * m)
++{
++ struct address_space *as;
++ int error;
++
++ error = user_path_walk(m->pgm.name, &m->nd);
++ if (error)
++ return error;
++
++ m->inode = m->nd.dentry->d_inode;
++ as = m->inode->i_mapping;
++
++ error = ex_write_lock(m->inode);
++ if (error) {
++ path_release(&m->nd);
++ return error;
++ }
++
++ /* switch i_op to hook into readpage */
++ m->ori_a_ops = as->a_ops;
++ m->dp_a_ops = *as->a_ops;
++ m->dp_a_ops.readpage = dp_readpage;
++ as->a_ops = &m->dp_a_ops;
++
++ /* physically insert the probes in existing pages */
++ insert_recs(m);
++
++ return 0;
++}
++
++/*
++ * Removes user space probes.
++ */
++static int remove_u(struct dp_module_struct *m)
++{
++ m->inode->i_mapping->a_ops = m->ori_a_ops; /* unhook readpage */
++ remove_recs(m); /* remove the probes */
++ ex_write_unlock(m->inode);
++ path_release(&m->nd); /* release path */
++ return 0;
++}
++
++/*
++ * Entry routine for the insert command. It checks for existing probes in this
++ * module to avoid any duplications, creates the necessary data structures
++ * calls insert_k() or insert_u() depending on whether it is a kernel space or
++ * user space probe.
++ */
++static int do_insert(struct dp_pgm_struct *upgm)
++{
++ struct dp_module_struct *m;
++ struct dp_pgm_struct *pgm;
++ char *uname, *kname;
++ int retval;
++
++ if (!upgm)
++ return -EINVAL;
++
++ pgm = kmalloc (sizeof(*pgm), GFP_KERNEL);
++ if (!pgm)
++ return -ENOMEM;
++
++ if (copy_from_user(pgm, upgm, sizeof(*pgm))) {
++ retval = -EFAULT;
++ goto done;
++ }
++
++ if (!pgm->name || !pgm->num_points) {
++ retval = -EINVAL;
++ goto done;
++ }
++ uname = pgm->name;
++ retval = copy_pgm_from_user(&m, pgm);
++ if (retval)
++ goto done;
++
++ try_module_get(THIS_MODULE);
++ down_write(&dp_modlist_sem);
++ link_module(m);
++ if (pgm->flags & DP_MODTYPE_USER) {
++ /* kludge: needed to use user_path_walk. */
++ kname = m->pgm.name;
++ m->pgm.name = uname;
++ retval = insert_u(m);
++ m->pgm.name = kname;
++ } else {
++ retval = insert_k(m);
++ }
++
++ if (retval) {
++ free_dp_module(m);
++ module_put(THIS_MODULE);
++ } else {
++ init_trace_hdr(m);
++ get_trace_id(m);
++ }
++ up_write(&dp_modlist_sem);
++
++done: kfree(pgm);
++ return retval;
++}
++
++static int remove_module(struct dp_module_struct *m)
++{
++ /* remove probes */
++ if (m->pgm.flags & DP_MODTYPE_USER) {
++ remove_u(m);
++ } else {
++ remove_k(m);
++ }
++ free_trace_id(m);
++
++ /* free module and all its contents */
++ free_dp_module(m);
++ return 0;
++}
++
++/*
++ * Entry routine for removing probes.
++ */
++static int do_remove (unsigned long cmd, char * uname)
++{
++ struct dp_module_struct *m, *tmp;
++ char *name;
++
++ if (cmd & DP_ALL) {
++ down_write(&dp_modlist_sem);
++ m = dp_module_list;
++ while (m) {
++ tmp = m->next;
++ remove_module (m);
++ m = tmp;
++ module_put(THIS_MODULE);
++ }
++ up_write(&dp_modlist_sem);
++ } else {
++ if (!uname)
++ return -EINVAL;
++ name = getname(uname);
++ if (IS_ERR(name))
++ return (PTR_ERR(name));
++
++ down_write(&dp_modlist_sem);
++ m = find_mod_by_name (name);
++ putname(name);
++ if (!m) {
++ up_write(&dp_modlist_sem);
++ return -EINVAL;
++ }
++ remove_module (m);
++ module_put(THIS_MODULE);
++ up_write(&dp_modlist_sem);
++ }
++ return 0;
++}
++
++static int
++dp_get_globalvars(unsigned long cmd, struct dp_getvars_in_struct * req,
++ byte_t * result)
++{
++ unsigned long num_vars;
++ unsigned long *tmp_gv = NULL;
++ unsigned long eflags;
++ unsigned short orig_to = req->to;
++ int retval = 0;
++
++ read_lock_irqsave(&dp_gv_lock, eflags);
++ if (!(cmd & DP_GETVARS_INDEX)) {
++ req->from = 0;
++ req->to = dp_num_gv - 1;
++ num_vars = dp_num_gv;
++ } else if (req->from >= dp_num_gv) {
++ num_vars = 0;
++ } else if (req->to >= dp_num_gv) {
++ req->to = dp_num_gv - 1;
++ num_vars = req->to - req->from + 1;
++ } else {
++ num_vars = req->to - req->from + 1;
++ }
++
++ if (cmd & DP_GETVARS_RESET) {
++ memset(dp_gv + req->from, 0, num_vars*sizeof(unsigned long));
++ read_unlock_irqrestore(&dp_gv_lock, eflags);
++ goto done;
++ }
++
++ read_unlock_irqrestore(&dp_gv_lock, eflags);
++
++ if (num_vars) {
++ tmp_gv = kmalloc(num_vars * sizeof(*tmp_gv), GFP_KERNEL);
++ if (!tmp_gv) {
++ retval = -ENOMEM;
++ goto done;
++ }
++
++ read_lock_irqsave(&dp_gv_lock, eflags);
++
++ /*
++ * dp_num_gv can change(only increase) during kmalloc. In such
++ * a case, only copy the previously determined number
++ * of global vars.
++ */
++ memcpy((void *)tmp_gv, (void *)(dp_gv + req->from),
++ sizeof(unsigned long) * num_vars);
++ read_unlock_irqrestore(&dp_gv_lock, eflags);
++ }
++
++ req->returned += (num_vars + 1) * sizeof(unsigned long);
++ if (req->returned < req->allocated) {
++ if (copy_to_user(result, &num_vars, sizeof(unsigned long)))
++ retval = -EFAULT;
++ else if (num_vars) {
++ if (copy_to_user(result + sizeof(unsigned long),
++ tmp_gv, sizeof(unsigned long) * num_vars))
++ retval = -EFAULT;
++ kfree(tmp_gv);
++ }
++ }
++ else
++ retval = -ENOMEM;
++done:
++ req->to = orig_to;
++ return retval;
++}
++
++static int
++cp_lvars_for_mod(struct dp_module_struct *m, struct dp_getvars_in_struct * req,
++ byte_t * result, unsigned long var_bytes)
++{
++ unsigned long len = 0;
++ unsigned long offset = 0;
++ unsigned long num_vars = req->to - req->from + 1;
++
++ len = sizeof(struct dp_getvars_local_out_struct) +
++ var_bytes + strlen(m->pgm.name) + 1;
++ req->returned += len;
++
++ if (req->returned < req->allocated) {
++ if (copy_to_user(result, &len, sizeof(unsigned long)))
++ return -EFAULT;
++ else
++ offset = sizeof(unsigned long);
++
++ if (copy_to_user(result+offset, &num_vars,
++ sizeof(unsigned long)))
++ return -EFAULT;
++ else
++ offset += sizeof(unsigned long);
++
++ if (copy_to_user(result+offset, m->lv + req->from, var_bytes))
++ return -EFAULT;
++ else
++ offset += var_bytes;
++
++ if (copy_to_user(result+offset, m->pgm.name,
++ strlen(m->pgm.name)+1))
++ return -EFAULT;
++ }
++ else
++ return -ENOMEM;
++
++ return 0;
++}
++
++static int
++getvars_m(struct dp_module_struct *m, unsigned long cmd,
++ struct dp_getvars_in_struct *req, byte_t *result)
++{
++ int retval = 0;
++ unsigned long var_bytes;
++ unsigned short orig_to = req->to;
++
++ if (cmd & DP_GETVARS_INDEX) {
++ if (req->from >= m->pgm.num_lv)
++ return 0;
++ if(req->to >= m->pgm.num_lv)
++ req->to = m->pgm.num_lv - 1;
++ }
++ else {
++ req->from = 0;
++ req->to = m->pgm.num_lv - 1;
++ }
++ var_bytes = (req->to - req->from + 1) * sizeof(unsigned long);
++
++ if (cmd & DP_GETVARS_RESET)
++ memset(m->lv + req->from, 0, var_bytes);
++ else
++ retval = cp_lvars_for_mod(m, req, result, var_bytes);
++ req->to = orig_to;
++ return retval;
++}
++
++static int
++do_getvars(unsigned long cmd, struct dp_getvars_in_struct * ureq,
++ byte_t * result)
++{
++ struct dp_module_struct *m;
++ struct dp_getvars_in_struct req;
++ int retval = 0;
++
++ if (!ureq)
++ return -EINVAL;
++
++ if (copy_from_user(&req, ureq, sizeof(req))) {
++ retval = -EFAULT;
++ goto failed;
++ }
++ if (req.name) {
++ req.name = getname(req.name);
++ if (IS_ERR(req.name)) {
++ retval = PTR_ERR(req.name);
++ goto failed;
++ }
++ }
++ else
++ req.name = NULL;
++
++ req.returned = 0;
++ if (cmd & DP_GETVARS_GLOBAL) {
++ retval = dp_get_globalvars(cmd, &req, result);
++ if (retval)
++ goto done;
++ }
++
++ if (cmd & DP_GETVARS_LOCAL) {
++ down_read(&dp_modlist_sem);
++ if (cmd & DP_ALL) {
++ for (m = dp_module_list; m; m = m->next) {
++ if (!m->lv)
++ continue;
++ retval = getvars_m(m, cmd, &req, result + req.returned);
++ if (retval && (retval != -ENOMEM))
++ break;
++ }
++ } else if (req.name) {
++ m = find_mod_by_name(req.name);
++ if (m && m->lv)
++ retval = getvars_m(m, cmd, &req, result + req.returned);
++ else
++ retval = -EINVAL;
++ }
++ up_read(&dp_modlist_sem);
++ }
++done:
++ if (copy_to_user(&ureq->returned, &req.returned,
++ sizeof(unsigned long)))
++ retval = -EFAULT;
++
++ if (req.name)
++ putname(req.name);
++failed:
++ return retval;
++}
++
++#define dp_copy_to_user(destination, source, length) \
++do { \
++ if (copy_to_user(destination, source, length)) \
++ return -EFAULT; \
++ destination += length; \
++} while(0)
++
++static int query_r(struct dp_module_struct *m, byte_t **result)
++{
++ struct dp_outrec_struct outrec;
++ struct dp_record_struct * rec;
++ unsigned short i;
++ unsigned short num_recs = m->pgm.num_points;
++ byte_t * offset = *result;
++ unsigned long eflags;
++
++ for (i = 0, rec = m->rec; i < num_recs; i++, rec++) {
++ spin_lock_irqsave(&rec->lock, eflags);
++ outrec.status = rec->status;
++ outrec.count = rec->count;
++ outrec.dbregno = rec->dbregno;
++ outrec.point = rec->point;
++ spin_unlock_irqrestore(&rec->lock, eflags);
++ dp_copy_to_user(offset, &outrec, sizeof(outrec));
++ }
++ *result = offset;
++ return 0;
++}
++
++static int
++query_m(struct dp_module_struct *m, struct dp_query_in_struct *req,
++ byte_t * result)
++{
++ unsigned long len = 0;
++ byte_t * offset = result;
++ struct dp_outmod_struct outmod;
++ int retval;
++ unsigned long rec_bytes = m->pgm.num_points *
++ sizeof(struct dp_outrec_struct);
++ unsigned long var_bytes = m->pgm.num_lv * sizeof(m->lv);
++ unsigned long rpn_bytes = m->pgm.rpn_length;
++ unsigned long mod_name_len = strlen(m->pgm.name) + 1;
++
++ len = sizeof(struct dp_outmod_struct) + var_bytes + rec_bytes
++ + rpn_bytes + mod_name_len;
++
++ req->returned += len;
++ if (req->returned <= req->allocated) {
++ outmod.pgm = m->pgm;
++ outmod.flags = m->flags;
++ outmod.base = m->base;
++ outmod.end = m->end;
++
++ offset += sizeof(outmod);
++
++ outmod.rec = (struct dp_outrec_struct *) offset ;
++ retval = query_r(m, &offset);
++ if (retval)
++ return retval;
++
++ if (var_bytes) {
++ outmod.lv = (unsigned long *) offset;
++ dp_copy_to_user(offset, m->lv, var_bytes);
++ } else
++ outmod.lv = NULL;
++
++ outmod.pgm.rpn_code = offset;
++ dp_copy_to_user(offset, m->pgm.rpn_code, rpn_bytes);
++
++ outmod.pgm.name = (char *) offset;
++ dp_copy_to_user(offset, m->pgm.name, mod_name_len);
++ if (m->next && !(req->name))
++ outmod.next = (struct dp_outmod_struct *) offset;
++ else
++ outmod.next = NULL;
++ offset = result;
++ dp_copy_to_user(offset, &outmod, sizeof(outmod));
++ }
++ else
++ return -ENOMEM;
++ return 0;
++}
++
++static int
++do_query(unsigned long cmd, struct dp_query_in_struct * ureq, byte_t * result)
++{
++ struct dp_query_in_struct req;
++ struct dp_module_struct *m;
++ int retval = 0;
++
++ if (!ureq)
++ return -EINVAL;
++ if (copy_from_user(&req, ureq, sizeof(req))) {
++ retval = -EFAULT;
++ goto failed;
++ }
++ if (req.name) {
++ req.name = getname(req.name);
++ if (IS_ERR(req.name)) {
++ retval = PTR_ERR(req.name);
++ goto failed;
++ }
++ }
++ else
++ req.name = NULL;
++ req.returned = 0;
++
++ down_read(&dp_modlist_sem);
++ if (cmd & DP_ALL) {
++ for (m = dp_module_list; m; m = m->next) {
++ retval = query_m(m, &req, result + req.returned);
++ if (retval && (retval != -ENOMEM))
++ break;
++ }
++ }
++ else if (req.name) {
++ m = find_mod_by_name(req.name);
++ if (m)
++ retval = query_m(m, &req, result + req.returned);
++ else
++ retval = -EINVAL;
++ }
++ up_read(&dp_modlist_sem);
++
++ if (copy_to_user(&ureq->returned, &req.returned,
++ sizeof(unsigned long)))
++ retval = -EFAULT;
++ if (req.name)
++ putname(req.name);
++failed:
++ return retval;
++}
++
++/*
++ * Returns the index of the first probe record that lies in the page
++ * starting with given offset.
++ */
++static inline int
++find_first_in_page(struct dp_module_struct * m, unsigned long offset)
++{
++ int i;
++ unsigned short num_recs = m->pgm.num_points;
++ struct dp_record_struct * rec;
++ unsigned long end = offset + PAGE_SIZE;
++
++ for (i = 0, rec = m->rec; i < num_recs; i++, rec++) {
++ if (rec->point.offset >=offset && rec->point.offset < end)
++ return i;
++ }
++ return -1;
++}
++
++/*
++ * This routine does the post processing for all pages that are brought
++ * in from a user module which has active probes in it.
++ */
++static int fixup_page (struct dp_module_struct *m, struct page *page)
++{
++ int start;
++ unsigned short end;
++ struct vm_area_struct *vma = NULL;
++
++ /* find and insert all probes in this page */
++ start = find_first_in_page(m, (page->index << PAGE_CACHE_SHIFT));
++ if (start < 0)
++ return -1;
++ end = find_last(m, start);
++
++ return insert_recs_in_page (m, page, start, end, vma);
++}
++
++/*
++ * This function handles the readpage of all modules that have active probes
++ * in them. Here, we first call the original readpage() of this
++ * inode / address_space to actually read the page into memory. Then, we will
++ * insert all the probes that are specified in this page before returning.
++ */
++int dp_readpage(struct file *file, struct page *page)
++{
++ int retval = 0;
++ struct dp_module_struct *m;
++
++ down_read(&dp_modlist_sem);
++ m = find_mod_by_inode(file->f_dentry->d_inode);
++ if (!m) {
++ printk("dp_readpage: major problem. we don't have mod for this !!!\n");
++ up_read(&dp_modlist_sem);
++ return -EINVAL;
++ }
++
++ /* call original readpage() */
++ retval = m->ori_a_ops->readpage(file, page);
++ if (retval >= 0)
++ fixup_page (m, page);
++
++ up_read(&dp_modlist_sem);
++ return retval;
++}
++
++/*
++ * This is called from kernel/module.c,sys_init_module() after a kernel module
++ * is loaded before it is initialized to give us a chance to insert any probes
++ * in it.
++ */
++int dp_insmod(struct module *kmod)
++{
++ struct dp_module_struct *m;
++ int retval;
++
++ down_read(&dp_modlist_sem);
++ m = find_mod_by_name(kmod->name);
++ if (!m) {
++ retval = -1;
++ } else {
++ get_kmod_bounds(m, kmod);
++ retval = insert_all_recs_k(m);
++ }
++ up_read(&dp_modlist_sem);
++ return retval;
++}
++
++/*
++ * This is called from kernel/module.c, free_module() before freeing a kernel
++ * module and from sys_init_module() when a kernel module initialization fails
++ * to give us a chance to remove any probes in it.
++ */
++int dp_remmod(struct module *kmod)
++{
++ struct dp_module_struct *m;
++ int retval;
++
++ down_read(&dp_modlist_sem);
++ m = find_mod_by_name(kmod->name);
++ if (!m)
++ retval = -1;
++ else
++ retval = remove_all_recs_k(m);
++ up_read(&dp_modlist_sem);
++ return retval;
++}
++
++/*
++ * ioctl function for dprobes driver.
++ */
++static int dp_ioctl (struct inode *in, struct file *fp, unsigned int ioctl_cmd,
++ unsigned long arg)
++{
++ struct dp_ioctl_arg dp_arg;
++ int retval = 0;
++
++ if (current->euid) {
++ return -EACCES;
++ }
++
++ if (copy_from_user((void *)&dp_arg, (void *)arg, sizeof(dp_arg))) {
++ module_put(THIS_MODULE);
++ return -EFAULT;
++ }
++
++ switch (dp_arg.cmd & DP_CMD_MASK) {
++ case DP_INSERT:
++ retval = do_insert(dp_arg.input);
++ break;
++ case DP_REMOVE:
++ retval = do_remove(dp_arg.cmd, dp_arg.input);
++ break;
++ case DP_GETVARS:
++ retval = do_getvars(dp_arg.cmd, dp_arg.input, dp_arg.output);
++ break;
++ case DP_QUERY:
++ retval = do_query(dp_arg.cmd, dp_arg.input, dp_arg.output);
++ break;
++ default:
++ retval = -EINVAL;
++ break;
++ }
++ return retval;
++}
++
++static struct file_operations dprobes_ops = {
++ owner: THIS_MODULE,
++ ioctl: dp_ioctl
++};
++
++static int dprobes_major;
++
++#ifdef CONFIG_MAGIC_SYSRQ
++
++/* SysRq handler - emergency removal of all probes via Alt-SysRq-V */
++
++static void do_remove_sysrq(struct dprobes_struct *dp)
++{
++ int retval;
++ if ((retval = do_remove(DP_ALL, NULL)))
++ printk("<1>Probe removal failed rc %i\n", retval);
++ else
++ printk("<1> All probes removed\n");
++ test_and_clear_bit(0, &emergency_remove);
++ return;
++}
++
++static void dprobes_sysrq( int key, struct pt_regs *ptr, struct tty_struct *tty)
++{
++
++ int retval;
++ retval = test_and_set_bit(0,&emergency_remove);
++ if (!retval)
++ queue_work(dprobes_wq, &dprobes_work);
++ return;
++
++}
++#endif
++
++static int __init dprobes_init_module(void)
++{
++ int retval = 0;
++
++ dprobes_major = register_chrdev(0, "dprobes", &dprobes_ops);
++ if (dprobes_major < 0) {
++ printk("Failed to register dprobes device\n");
++ retval = dprobes_major;
++ goto err;
++ }
++
++ printk("IBM Dynamic Probes v%d.%d.%d loaded.\n", DP_MAJOR_VER,
++ DP_MINOR_VER, DP_PATCH_VER);
++
++#ifdef CONFIG_MAGIC_SYSRQ
++ if (!(dprobes_wq = create_workqueue("dprobes_wq"))) {
++ printk("dprobes create_workqueue failed\n");
++ retval = -ENOMEM;
++ goto err;
++ }
++ INIT_WORK(&dprobes_work, (void *)(void *)do_remove_sysrq, NULL);
++ if ((retval = register_sysrq_key('v', &key_op)))
++ printk("<1>register_sysrq_key returned %i\n", retval);
++#endif
++
++err: return retval;
++}
++
++static void __exit dprobes_cleanup_module(void)
++{
++ int retval;
++ unregister_chrdev(dprobes_major, "dprobes");
++
++ printk("IBM Dynamic Probes v%d.%d.%d unloaded.\n", DP_MAJOR_VER,
++ DP_MINOR_VER, DP_PATCH_VER);
++
++#ifdef CONFIG_MAGIC_SYSRQ
++ destroy_workqueue(dprobes_wq);
++ if ((retval = unregister_sysrq_key('v', &key_op)))
++ printk("<1>unregister_sysrq_key returned %i\n", retval);
++#endif
++ return;
++}
++
++module_init(dprobes_init_module);
++module_exit(dprobes_cleanup_module);
++
++MODULE_AUTHOR("IBM");
++MODULE_DESCRIPTION("Dynamic Probes");
++MODULE_LICENSE("GPL");
++
++void dprobes_code_end(void) { }
+diff -urNp linux-2.6.0-test9/drivers/dprobes/dprobes_in.c linux-2.6.0-test9+kp/drivers/dprobes/dprobes_in.c
+--- linux-2.6.0-test9/drivers/dprobes/dprobes_in.c 1970-01-01 05:30:00.000000000 +0530
++++ linux-2.6.0-test9+kp/drivers/dprobes/dprobes_in.c 2003-11-18 07:29:47.000000000 +0530
+@@ -0,0 +1,2014 @@
++/*
++ * IBM Dynamic Probes
++ * Copyright (c) International Business Machines Corp., 2000
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ */
++
++#include <linux/dprobes.h>
++#include <linux/dprobes_in.h>
++#include <asm/io.h>
++#include <asm/uaccess.h>
++
++static void dispatch_ex(void);
++static void gen_ex(unsigned long, unsigned long, unsigned long);
++static void log_st(void);
++
++void dprobes_interpreter_code_start(void) { }
++
++/*
++ * RPN stack manipulation
++ * Pushes a 32 bit number onto RPN stack.
++ */
++static inline void rpnpush(unsigned long arg)
++{
++ dprobes.rpn_tos--;
++ dprobes.rpn_tos &= RPN_STACK_SIZE - 1;
++ dprobes.rpn_stack[dprobes.rpn_tos] = arg;
++}
++
++/*
++ * Pops a 32 bit number from RPN stack.
++ */
++static inline unsigned long rpnpop(void)
++{
++ unsigned long ret = dprobes.rpn_stack[dprobes.rpn_tos];
++ dprobes.rpn_tos++;
++ dprobes.rpn_tos &= RPN_STACK_SIZE - 1;
++ return ret;
++}
++
++/*
++ * Returns a 32 bit number for RPN stack at a given position
++ * relative to RPN TOS, without poping.
++ */
++static unsigned long rpnmove(unsigned long pos)
++{
++ unsigned long index = dprobes.rpn_tos + pos;
++ index &= RPN_STACK_SIZE - 1;
++ return dprobes.rpn_stack[index];
++}
++
++/*
++ * Returns a byte(8 bit) operand from rpn code array
++ */
++static inline u8 get_u8_oprnd(void)
++{
++ return *dprobes.rpn_ip++;
++}
++
++
++/*
++ * Returns a word(16 bit) operand from rpn code array
++ */
++static inline u16 get_u16_oprnd(void)
++{
++ return *(((u16 *)dprobes.rpn_ip)++);
++}
++
++/*
++ * Returns an unsigned long operand from rpn code array
++ */
++static inline unsigned long get_ulong_oprnd(void)
++{
++ unsigned long dw;
++ dw = *((unsigned long *) (dprobes.rpn_ip));
++ dprobes.rpn_ip += sizeof(unsigned long);
++ return dw;
++}
++
++/*
++ * Returns a signed long(32 bit) operand from rpn code array
++ */
++static inline long get_long_oprnd(void)
++{
++ long l;
++ l = *((long *) (dprobes.rpn_ip));
++ dprobes.rpn_ip += sizeof(long);
++ return l;
++}
++
++#ifdef CONFIG_X86
++#include "i386/dprobes_in.c"
++#endif
++
++/*
++ * Jump instructions.
++ * Interpreter is terminated when total number of jumps and loops
++ * becomes equal to jmpmax.
++ */
++static void jmp(void)
++{
++ long offset = get_long_oprnd();
++ if (dprobes.jmp_count == dprobes.mod->pgm.jmpmax) {
++ gen_ex(EX_MAX_JMPS, dprobes.mod->pgm.jmpmax, 0);
++ return;
++ }
++ dprobes.jmp_count++;
++ dprobes.rpn_ip += offset;
++}
++
++#define COND_JMPS(name, condition) \
++static void name(void) \
++{ \
++ long offset = get_long_oprnd(); \
++ if ((long) dprobes.rpn_stack[dprobes.rpn_tos] condition 0) { \
++ if (dprobes.jmp_count == dprobes.mod->pgm.jmpmax) { \
++ gen_ex(EX_MAX_JMPS, dprobes.mod->pgm.jmpmax, 0); \
++ return; \
++ } \
++ dprobes.jmp_count++; \
++ dprobes.rpn_ip += offset; \
++ } \
++}
++
++COND_JMPS(jlt, <)
++COND_JMPS(jgt, >)
++COND_JMPS(jle, <=)
++COND_JMPS(jge, >=)
++COND_JMPS(jz, ==)
++COND_JMPS(jnz, !=)
++
++/*
++ * loop label: Decrement RPN TOS and jump to the label
++ * if TOS is not equal to 0.
++ * Interpreter is terminated when total number of jumps and loops
++ * becomes equal to jmpmax.
++ */
++static void loop(void)
++{
++ long offset = get_long_oprnd();
++ if (dprobes.jmp_count++ == dprobes.mod->pgm.jmpmax) {
++ gen_ex(EX_MAX_JMPS, dprobes.mod->pgm.jmpmax, 0);
++ return;
++ }
++ if ((long) (--dprobes.rpn_stack[dprobes.rpn_tos]) != 0)
++ dprobes.rpn_ip += offset;
++}
++
++/*
++ * Call and ret instructions. Number of nested calls is limited to 32.
++ */
++static void call(void)
++{
++ int i;
++ struct dprobes_struct *dp = &dprobes;
++ long offset = (long)get_long_oprnd();
++ unsigned long called_addr = (dp->rpn_ip - dp->rpn_code + offset);
++ if (dp->call_tos < CALL_FRAME_SIZE) {
++ gen_ex(EX_CALL_STACK_OVERFLOW, NR_NESTED_CALLS, 0);
++ return;
++ }
++ /*
++ * Push the return value(rpn_ip) onto the call stack.
++ */
++ dp->call_stack[--dp->call_tos] = dp->rpn_ip - dp->rpn_code;
++ dp->rpn_ip += offset;
++ /*
++ * push the called subroutine address, default exception handler
++ * address, and initialize pv, lv and gv range to zeros.
++ */
++ dp->call_stack[--dp->call_tos] = called_addr;
++ dp->call_stack[--dp->call_tos] = EX_NO_HANDLER;
++
++ for (i = 0; i < CALL_FRAME_SIZE - 4; i++)
++ dp->call_stack[--dp->call_tos] = 0;
++ /* Save the rpn stack base pointer */
++ dp->call_stack[--dp->call_tos] = dp->rpn_sbp;
++}
++
++/*
++ * Pops the return value into rpn_ip.
++ */
++static void ret(void)
++{
++ struct dprobes_struct *dp = &dprobes;
++ if (dp->call_tos >= CALL_STACK_SIZE - CALL_FRAME_SIZE) {
++ gen_ex(EX_CALL_STACK_OVERFLOW, 0, 0);
++ return;
++ }
++ dp->rpn_sbp = dp->call_stack[dp->call_tos++];
++ dp->call_tos += CALL_FRAME_SIZE - 2;
++ /* pop the return address */
++ dp->rpn_ip = dp->rpn_code + dp->call_stack[dp->call_tos++];
++
++ if (dp->ex_pending) {
++ dp->ex_hand = dp->call_stack[dp->call_tos +
++ OFFSET_EX_HANDLER];
++ dispatch_ex();
++ }
++}
++
++/*
++ * Exception handling instructions.
++ */
++static void sx(void)
++{
++ long offset = get_long_oprnd();
++ dprobes.call_stack[dprobes.call_tos + OFFSET_EX_HANDLER] =
++ (long)dprobes.rpn_ip + offset;
++}
++
++static void ux(void)
++{
++ dprobes.call_stack[dprobes.call_tos + OFFSET_EX_HANDLER] =
++ EX_NO_HANDLER;
++}
++
++static void dispatch_ex(void)
++{
++ struct dprobes_struct *dp = &dprobes;
++ long ex_handler;
++ /* We are at the outermost routine & no exception handler, terminate */
++ if ((dp->call_tos == CALL_STACK_SIZE - CALL_FRAME_SIZE) &&
++ (dp->call_stack[dp->call_tos + OFFSET_EX_HANDLER] ==
++ EX_NO_HANDLER)) {
++ dp->status &= ~DP_STATUS_INTERPRETER;
++ return;
++ }
++ ex_handler = dp->ex_hand;
++ if (ex_handler != EX_NO_HANDLER) {
++ dp->ex_pending = 0;
++ dp->call_stack[dp->call_tos + OFFSET_EX_HANDLER] =
++ EX_NO_HANDLER;
++ dp->rpn_ip = (byte_t *)ex_handler;
++ }
++ else {
++ ret();
++ }
++}
++
++static void rx(void)
++{
++ struct dprobes_struct *dp = &dprobes;
++ dp->ex_code = rpnmove(0);
++ dp->ex_parm1 = rpnmove(1);
++ dp->ex_parm2 = rpnmove(2);
++ dp->ex_pending = 1;
++ dp->ex_hand = dp->call_stack[dp->call_tos +
++ OFFSET_EX_HANDLER];
++ if (dp->rec->mod->pgm.autostacktrace)
++ log_st();
++ dispatch_ex();
++}
++
++static void push_x(void)
++{
++ rpnpush(dprobes.ex_parm2);
++ rpnpush(dprobes.ex_parm1);
++ rpnpush(dprobes.ex_code);
++}
++
++static void gen_ex(unsigned long ex_code, unsigned long parm1,
++ unsigned long parm2)
++{
++ struct dprobes_struct *dp = &dprobes;
++ if (ex_code & dp->rec->point.ex_mask) {
++ dp->ex_code = ex_code;
++ dp->ex_parm1 = parm1;
++ dp->ex_parm2 = parm2;
++ rpnpush(parm2);
++ rpnpush(parm1);
++ rpnpush(ex_code);
++ dp->ex_pending = 1;
++ dp->ex_hand = dp->call_stack[dp->call_tos +
++ OFFSET_EX_HANDLER];
++ if (dp->rec->mod->pgm.autostacktrace) {
++ log_st();
++ }
++ dispatch_ex();
++ } else if (ex_code & EX_NON_MASKABLE_EX) {
++ dp->ex_hand = dp->call_stack[dp->call_tos +
++ OFFSET_EX_HANDLER];
++ if (dp->rec->mod->pgm.autostacktrace)
++ log_st();
++ dp->status &= ~DP_STATUS_INTERPRETER;
++ }
++}
++
++static int write_st_buffer(unsigned char *ptr, int size)
++{
++ if (dprobes.ex_log_len + size >= dprobes.mod->pgm.ex_logmax)
++ return -1;
++ else {
++ memcpy(dprobes.ex_log + dprobes.ex_log_len, ptr, size);
++ dprobes.ex_log_len += size;
++ return 0;
++ }
++}
++
++static int log_gv_entries(unsigned long call_tos)
++{
++ struct dprobes_struct *dp = &dprobes;
++ unsigned char gv_prefix[PREFIX_SIZE] = {TOKEN_GV_ENTRY, 0, 0};
++ unsigned long range = dp->call_stack[call_tos + OFFSET_GV_RANGE];
++ unsigned long index = dp->call_stack[call_tos + OFFSET_GV_RANGE + 1];
++ int i;
++ dp->ex_off.gv = dp->ex_log_len + 1;
++ if (write_st_buffer(gv_prefix, PREFIX_SIZE))
++ return -1;
++ for (i = index; i < index + range; i++) {
++ if (i >= dp_num_gv) {
++ dp->status &= ~DP_STATUS_INTERPRETER;
++ return -1;
++ }
++ read_lock(&dp_gv_lock);
++ if (write_st_buffer((unsigned char *)(dp_gv + i), sizeof(unsigned long))) {
++ read_unlock(&dp_gv_lock);
++ return -1;
++ }
++ read_unlock(&dp_gv_lock);
++ (*(unsigned short *)(dp->ex_log + dp->ex_off.gv))++;
++ }
++ return 0;
++}
++
++static int log_lv_entries(unsigned long call_tos)
++{
++ struct dprobes_struct *dp = &dprobes;
++ unsigned char lv_prefix[PREFIX_SIZE] = {TOKEN_LV_ENTRY, 0, 0};
++ unsigned long range = dp->call_stack[call_tos + OFFSET_LV_RANGE];
++ unsigned long index = dp->call_stack[call_tos + OFFSET_LV_RANGE + 1];
++ int i;
++ dp->ex_off.lv = dp->ex_log_len + 1;
++ if (write_st_buffer(lv_prefix, PREFIX_SIZE))
++ return -1;
++ for (i = index; i < index + range; i++) {
++ if (i >= dp->rec->mod->pgm.num_lv) {
++ dp->status &= ~DP_STATUS_INTERPRETER;
++ return -1;
++ }
++ if (write_st_buffer((unsigned char *)(dp->mod->lv + i), sizeof(unsigned long)))
++ return -1;
++ (*(unsigned short *)(dp->ex_log + dp->ex_off.lv))++;
++ }
++ return 0;
++}
++
++static int log_rpnstack_entries(unsigned long call_tos)
++{
++ struct dprobes_struct *dp = &dprobes;
++ unsigned char rpn_prefix[PREFIX_SIZE] = {TOKEN_RPN_ENTRY, 0, 0};
++ unsigned long range = dp->call_stack[call_tos +
++ OFFSET_RPN_STACK_RANGE];
++ unsigned long index = dp->call_stack[call_tos +
++ OFFSET_RPN_STACK_RANGE + 1];
++ int i, j;
++ dp->ex_off.rpn = dp->ex_log_len + 1;
++ if (write_st_buffer(rpn_prefix, PREFIX_SIZE))
++ return -1;
++ for (i = j = index; i < index + range; i++, j++) {
++ j &= (RPN_STACK_SIZE - 1);
++ if (write_st_buffer((unsigned char *)(dp->rpn_stack + j),
++ sizeof(unsigned long)))
++ return -1;
++ (*(unsigned short *)(dp->ex_log + dp->ex_off.rpn))++;
++ }
++ return 0;
++}
++
++static int log_stackframes(void)
++{
++ struct dprobes_struct *dp = &dprobes;
++ unsigned long c_tos = dp->call_tos;
++ unsigned char sf_prefix[PREFIX_SIZE] = {TOKEN_STACK_FRAME, 0, 0};
++ dp->ex_off.sf = dp->ex_log_len + 1;
++ if (write_st_buffer(sf_prefix, PREFIX_SIZE))
++ return -1;
++
++ while (c_tos < (CALL_STACK_SIZE - CALL_FRAME_SIZE)) {
++ if (write_st_buffer((unsigned char *)(dp->call_stack +
++ c_tos + OFFSET_CALLED_ADDR), sizeof(unsigned long)))
++ return -1;
++ if (write_st_buffer((unsigned char *)(dp->call_stack +
++ c_tos + OFFSET_RETURN_ADDR), sizeof(unsigned long)))
++ return -1;
++
++ if (log_rpnstack_entries(c_tos))
++ return -1;
++ if (log_lv_entries(c_tos))
++ return -1;
++ if (log_gv_entries(c_tos))
++ return -1;
++
++ (*(short *)(dp->ex_log + dp->ex_off.sf))++;
++ c_tos += CALL_FRAME_SIZE;
++ }
++ return 0;
++}
++
++static int log_stacktrace(void)
++{
++ struct dprobes_struct *dp = &dprobes;
++ long ex_handler;
++ if (write_st_buffer((unsigned char *)&dp->ex_code, sizeof(unsigned long)))
++ return -1;
++ ex_handler = dp->ex_hand;
++ if (ex_handler != EX_NO_HANDLER)
++ ex_handler -= (long)dp->rpn_code;
++ if (write_st_buffer((unsigned char *)&ex_handler, sizeof(long)))
++ return -1;
++ if (write_st_buffer((unsigned char *)&dp->ex_parm1, sizeof(unsigned long)))
++ return -1;
++ if (write_st_buffer((unsigned char *)&dp->ex_parm2, sizeof(unsigned long)))
++ return -1;
++ if (log_stackframes())
++ return -1;
++ (*(short *)(dp->ex_log + dp->ex_off.st))++;
++ return 0;
++}
++
++static int get_exlog_hdr(void)
++{
++ unsigned short name_len;
++ unsigned char st_prefix[PREFIX_SIZE] = {TOKEN_STACK_TRACE, 0, 0};
++ struct dprobes_struct *dp = &dprobes;
++
++ strcpy(dp->ex_log + dp->ex_log_len + PREFIX_SIZE, dp->mod->pgm.name);
++ name_len = strlen(dp->mod->pgm.name);
++ dp->ex_log[dp->ex_log_len] = TOKEN_ASCII_LOG;
++ *(unsigned short *)(dp->ex_log + dp->ex_log_len + 1) = name_len;
++ dp->ex_log_len += (name_len + PREFIX_SIZE);
++
++ *(loff_t *)(dp->ex_log + dp->ex_log_len) =
++ dp->rec->point.offset;
++ dp->ex_log_len += sizeof(loff_t);
++
++ *(unsigned short *)(dp->ex_log + dp->ex_log_len) =
++ dp->mod->pgm.id;
++ dp->ex_log_len += sizeof(unsigned short);
++
++ *(unsigned short *)(dp->ex_log + dp->ex_log_len) =
++ dp->major;
++ dp->ex_log_len += sizeof(unsigned short);
++
++ *(unsigned short *)(dp->ex_log + dp->ex_log_len) =
++ dp->minor;
++ dp->ex_log_len += sizeof(unsigned short);
++
++ dp->ex_off.st = dp->ex_log_len + 1;
++ if (write_st_buffer(st_prefix, PREFIX_SIZE))
++ return -1;
++ return 0;
++}
++
++static void log_st(void)
++{
++ if (dprobes.ex_log_len == MIN_ST_SIZE)
++ if(get_exlog_hdr()) return;
++ log_stacktrace();
++}
++
++static void purge_st(void)
++{
++ dprobes.ex_log_len = MIN_ST_SIZE;
++}
++
++static void trace_lv(void)
++{
++ dprobes.call_stack[dprobes.call_tos + OFFSET_LV_RANGE] = rpnpop();
++ dprobes.call_stack[dprobes.call_tos + OFFSET_LV_RANGE + 1] = rpnpop();
++}
++
++static void trace_gv(void)
++{
++ dprobes.call_stack[dprobes.call_tos + OFFSET_GV_RANGE] = rpnpop();
++ dprobes.call_stack[dprobes.call_tos + OFFSET_GV_RANGE + 1] = rpnpop();
++}
++
++static void trace_pv(void)
++{
++ dprobes.call_stack[dprobes.call_tos + OFFSET_RPN_STACK_RANGE] =
++ rpnpop();
++ dprobes.call_stack[dprobes.call_tos + OFFSET_RPN_STACK_RANGE + 1] =
++ rpnpop();
++}
++
++static void push_tsp(void)
++{
++ long index = (long)rpnpop();
++ unsigned long tsp_index = ((signed)dprobes.rpn_tos + index)
++ & (RPN_STACK_SIZE - 1);
++ rpnpush(dprobes.rpn_stack[tsp_index]);
++}
++
++static void pop_tsp(void)
++{
++ unsigned long value = rpnpop();
++ long index = (long)rpnpop();
++ unsigned long tsp_index = ((signed)dprobes.rpn_tos + index)
++ & (RPN_STACK_SIZE - 1);
++ dprobes.rpn_stack[tsp_index] = value;
++}
++
++static void save_sbp(void)
++{
++ rpnpush(dprobes.rpn_sbp);
++}
++
++static void restore_sbp(void)
++{
++ dprobes.rpn_sbp = rpnpop() & (RPN_STACK_SIZE - 1);
++}
++
++static void save_tsp(void)
++{
++ rpnpush(dprobes.rpn_tos);
++}
++
++static void restore_tsp(void)
++{
++ dprobes.rpn_tos = rpnpop();
++}
++
++static void push_sbp(void)
++{
++ long index = (long)rpnpop();
++ unsigned long sbp_index = ((signed)dprobes.rpn_sbp + index)
++ & (RPN_STACK_SIZE - 1);
++ rpnpush(dprobes.rpn_stack[sbp_index]);
++}
++
++static void pop_sbp(void)
++{
++ unsigned long value = rpnpop();
++ long index = (long)rpnpop();
++ unsigned long sbp_index = ((signed)dprobes.rpn_sbp + index)
++ & (RPN_STACK_SIZE - 1);
++ dprobes.rpn_stack[sbp_index] = value;
++}
++
++static void push_stp(void)
++{
++ rpnpush(dprobes.ex_log_len);
++}
++
++static void pop_stp(void)
++{
++ dprobes.ex_log_len = rpnpop();
++}
++
++static void push_sbp_i(void)
++{
++ s16 index = (s16)get_u16_oprnd();
++ unsigned long sbp_index = ((signed)dprobes.rpn_sbp + index)
++ & (RPN_STACK_SIZE - 1);
++ rpnpush(dprobes.rpn_stack[sbp_index]);
++}
++
++static void pop_sbp_i(void)
++{
++ s16 index = (s16)get_u16_oprnd();
++ unsigned long sbp_index = ((signed)dprobes.rpn_sbp + index)
++ & (RPN_STACK_SIZE - 1);
++ dprobes.rpn_stack[sbp_index] = rpnpop();
++}
++
++static void push_tsp_i(void)
++{
++ s16 index = (s16)get_u16_oprnd();
++ unsigned long tsp_index = ((signed)dprobes.rpn_tos + index)
++ & (RPN_STACK_SIZE - 1);
++ rpnpush(dprobes.rpn_stack[tsp_index]);
++}
++
++static void pop_tsp_i(void)
++{
++ s16 index = (s16)get_u16_oprnd();
++ unsigned long tsp_index = ((signed)dprobes.rpn_tos + index)
++ & (RPN_STACK_SIZE - 1);
++ dprobes.rpn_stack[tsp_index] = rpnpop();
++}
++
++static void copy_sbp(void)
++{
++ long index = (long)rpnpop();
++ unsigned long sbp_index = ((signed)dprobes.rpn_sbp + index)
++ & (RPN_STACK_SIZE - 1);
++ dprobes.rpn_stack[sbp_index] = dprobes.rpn_stack[dprobes.rpn_tos];
++}
++
++static void copy_sbp_i(void)
++{
++ s16 index = (s16)get_u16_oprnd();
++ unsigned long sbp_index = ((signed)dprobes.rpn_sbp + index)
++ & (RPN_STACK_SIZE - 1);
++ dprobes.rpn_stack[sbp_index] = dprobes.rpn_stack[dprobes.rpn_tos];
++}
++
++static void copy_tsp(void)
++{
++ long index = (long)rpnpop();
++ unsigned long tsp_index = ((signed)dprobes.rpn_tos + index)
++ & (RPN_STACK_SIZE - 1);
++ dprobes.rpn_stack[tsp_index] = dprobes.rpn_stack[dprobes.rpn_tos];
++}
++
++static void copy_tsp_i(void)
++{
++ s16 index = (s16)get_u16_oprnd();
++ unsigned long tsp_index = ((signed)dprobes.rpn_tos + index)
++ & (RPN_STACK_SIZE - 1);
++ dprobes.rpn_stack[tsp_index] = dprobes.rpn_stack[dprobes.rpn_tos];
++}
++
++/*
++ * Normal exit from the interpreter.
++ */
++static inline void dp_exit(void)
++{
++ dprobes.status &= ~DP_STATUS_INTERPRETER;
++}
++
++/*
++ * Exit the interpreter without producing the log.
++ */
++static inline void dp_abort(void)
++{
++ dprobes.status |= DP_STATUS_ABORT;
++ dprobes.status &= ~DP_STATUS_INTERPRETER;
++}
++
++static inline void dp_remove(void)
++{
++ dprobes.rec->status |= DP_REC_STATUS_REMOVED;
++ dprobes.status &= ~DP_STATUS_INTERPRETER;
++}
++
++static inline void no_op(void)
++{
++}
++
++/*
++ * Stores the fault record in the log buffer, when logging is
++ * terminated due to invalid memory access.
++ * Fault record comprises of a token byte, which indicates the reason for
++ * fault; an unsigned short length indicating how many bytes are logged,
++ * which is 4; and the 4 logged bytes indicate the faulting address.
++ *
++ * Token byte -1: fault occurred while reading the memory, faulting address
++ * indicates the virtual(flat) address which caused the fault.
++ * Token byte -2: fault occurred when converting the segmented address to
++ * flat address. The fault address stored will indicate the selector of
++ * the segmented address.
++ * Note that the fault record will be stored only if sufficient space
++ * is available to store the entire fault record.
++ *
++ * The fault record will be stored when fault occurs because of the
++ * following instructions:
++ * log mrf
++ * log str
++ */
++static void store_fault_record(byte_t token, unsigned long addr,
++ unsigned long ex_code)
++{
++ struct dprobes_struct *dp = &dprobes;
++ if (dp->log_len + 7 <= dp->mod->pgm.logmax) {
++ dp->log[dp->log_len++] = token;
++ *((unsigned short *)(dp->log + dp->log_len)) = 4;
++ dp->log_len += sizeof(unsigned short);
++ *((unsigned long *)(dp->log + dp->log_len)) = addr;
++ dp->log_len += sizeof(unsigned long);
++ gen_ex(ex_code, addr, 0);
++ }
++ else
++ gen_ex(EX_LOG_OVERFLOW, dp->mod->pgm.logmax, 0);
++ return;
++}
++
++/*
++ * Logging group
++ */
++static inline void push_lp(void)
++{
++ rpnpush(dprobes.log_len);
++}
++
++static inline void push_plp(void)
++{
++ rpnpush(dprobes.prev_log_len);
++}
++
++static inline void pop_lp(void)
++{
++ dprobes.log_len = rpnpop();
++}
++
++static void
++log_ascii(unsigned long len, u8 *faddr)
++{
++ struct dprobes_struct *dp = &dprobes;
++ u8 b;
++ dp->prev_log_len = dp->log_len;
++ if (dp->log_len + len + PREFIX_SIZE > dp->mod->pgm.logmax) {
++ len = dp->mod->pgm.logmax - dp->log_len;
++ if ((signed)len < 0) {
++ gen_ex(EX_LOG_OVERFLOW, dp->mod->pgm.logmax, 0);
++ return;
++ }
++ }
++ dp->log_len += PREFIX_SIZE; /* reserve for prefix */
++ while (len--) {
++ if (!dp_intr_copy_from_user(&b, (void *)faddr, 1)) {
++ if(b) {
++ faddr++;
++ dp->log[dp->log_len++] = b;
++ } else
++ break;
++
++ } else {
++ dp->log_len = dp->prev_log_len;
++ store_fault_record(TOKEN_MEMORY_FAULT,
++ (unsigned long)faddr, EX_INVALID_ADDR);
++ return;
++ }
++ }
++ if (b && (signed)len >= 0) {
++ dp->status |= DP_STATUS_LOG_OVERFLOW;
++ dp->log_len = dp->prev_log_len;
++ gen_ex(EX_LOG_OVERFLOW, dp->mod->pgm.logmax, 0);
++ return;
++ }
++ dp->log[dp->prev_log_len] = TOKEN_ASCII_LOG;
++ *(unsigned short *) (dp->log + dp->prev_log_len + 1) =
++ (unsigned short) (dp->log_len - dp->prev_log_len - PREFIX_SIZE);
++ dp->prev_log_len = dp->log_len;
++ return;
++}
++
++static void
++log_memory(unsigned long len, u8 *faddr)
++{
++ struct dprobes_struct *dp = &dprobes;
++ unsigned long num_remain;
++ dp->prev_log_len = dp->log_len;
++ if (dp->log_len + len + PREFIX_SIZE > dp->mod->pgm.logmax) {
++ dp->status |= DP_STATUS_LOG_OVERFLOW;
++ len = dp->mod->pgm.logmax - dp->log_len - PREFIX_SIZE;
++ if ((signed)len < 0) {
++ gen_ex(EX_LOG_OVERFLOW, dp->mod->pgm.logmax, 0);
++ return;
++ }
++ }
++
++ if ((num_remain = dp_intr_copy_from_user(
++ (dp->log + dp->log_len + PREFIX_SIZE), faddr, len))) {
++ store_fault_record(TOKEN_MEMORY_FAULT,
++ (unsigned long)(faddr+len-num_remain), EX_INVALID_ADDR);
++ return;
++ }
++
++ dp->log[dp->log_len++] = TOKEN_MEMORY_LOG;
++ *((unsigned short *)(dp->log + dp->log_len)) = len;
++ dp->log_len += (len + sizeof(unsigned short));
++ dp->prev_log_len = dp->log_len;
++ return;
++}
++
++/* log str pops the flat address and length from
++ * the RPN stack. It tries to copy length number of
++ * bytes from flat address to the log buffer.
++ * Logging is discontinued if a NULL byte is encountered.
++ * NULL byte is not logged. If the log buffer becomes full
++ * LOG_OVERFLOW exception is generated.
++ * If the log is successful, the logged string is prefixed
++ * by a token byte of 1 and a word indicating the length of
++ * the string logged.
++ * INVALID_ADDR exception is generated if the flat address is invalid.
++ */
++static void log_str(void)
++{
++ u8 *faddr = (u8 *) rpnpop();
++ log_ascii(rpnpop(), faddr);
++}
++
++/*
++ * Log memory at flat range.
++ * INVALID_ADDR exception is generated if flat address is invalid.
++ * Log terminating conditions apply.
++ * token byte = 0.
++ */
++static void log_mrf(void)
++{
++ u8 *faddr = (u8 *) rpnpop();
++ log_memory(rpnpop(), faddr);
++}
++
++/*
++ * Log count number of elements from rpn stack to log buffer.
++ */
++static void log_i(void)
++{
++ struct dprobes_struct *dp = &dprobes;
++ u16 oprnd = get_u16_oprnd();
++ u16 count, i;
++ unsigned long size = oprnd * sizeof(unsigned long);
++ dp->prev_log_len = dp->log_len;
++
++ if (dp->log_len + size > dp->mod->pgm.logmax) {
++ size = dp->mod->pgm.logmax - dp->log_len;
++ }
++ if ((signed)size < 0) {
++ gen_ex(EX_LOG_OVERFLOW, dp->mod->pgm.logmax, 0);
++ return;
++ }
++ i = count = size / sizeof(unsigned long);
++ while (i--) {
++ *(unsigned long *) (dp->log + dp->log_len) = rpnpop();
++ dp->log_len += sizeof(unsigned long);
++ }
++ if (count < oprnd) {
++ gen_ex(EX_LOG_OVERFLOW, dp->mod->pgm.logmax, 0);
++ return;
++ }
++ dp->prev_log_len = dp->log_len;
++}
++
++static void log(void)
++{
++ struct dprobes_struct *dp = &dprobes;
++ u16 oprnd = (u16) rpnpop();
++ u16 count, i;
++ unsigned long size = oprnd * sizeof(unsigned long) + PREFIX_SIZE;
++ dp->prev_log_len = dp->log_len;
++
++ if (dp->log_len + size > dp->mod->pgm.logmax) {
++ size = dp->mod->pgm.logmax - dp->log_len;
++ }
++ if ((signed)size < PREFIX_SIZE) {
++ gen_ex(EX_LOG_OVERFLOW, dp->mod->pgm.logmax, 0);
++ return;
++ }
++ dp->log_len += PREFIX_SIZE;
++ size -= PREFIX_SIZE;
++ i = count = size / sizeof(unsigned long);
++
++ while (i--) {
++ *(unsigned long *) (dp->log + dp->log_len) = rpnpop();
++ dp->log_len += sizeof(unsigned long);
++ }
++
++ dp->log[dp->prev_log_len] = TOKEN_LOG;
++ *(unsigned short *) (dp->log + dp->prev_log_len + 1) =
++ (unsigned short) (count);
++
++ if (count < oprnd) {
++ gen_ex(EX_LOG_OVERFLOW, dp->mod->pgm.logmax, 0);
++ return;
++ }
++ dp->prev_log_len = dp->log_len;
++}
++
++static void log_lv(void)
++{
++ struct dprobes_struct *dp = &dprobes;
++ unsigned long range = rpnpop();
++ unsigned long index = rpnpop();
++ unsigned long offset;
++ unsigned char lv_prefix[PREFIX_SIZE] = {TOKEN_LV_ENTRY, 0, 0};
++ int i;
++
++ if (dp->log_len + PREFIX_SIZE > dp->mod->pgm.logmax) {
++ gen_ex(EX_LOG_OVERFLOW, dp->mod->pgm.logmax, 0);
++ return;
++ }
++ memcpy(dp->log + dp->log_len, lv_prefix, PREFIX_SIZE);
++ offset = dp->log_len + 1;
++ dp->log_len += PREFIX_SIZE;
++
++ for (i = index; i < index + range; i++) {
++ if (i >= dp->rec->mod->pgm.num_lv) {
++ gen_ex(EX_INVALID_OPERAND, 1, i);
++ return;
++ }
++ if (dp->log_len + sizeof (unsigned long) > dp->mod->pgm.logmax) {
++ gen_ex(EX_LOG_OVERFLOW, dp->mod->pgm.logmax, 0);
++ return;
++ }
++ *(unsigned long *) (dp->log + dp->log_len) =
++ *(dp->mod->lv + i);
++ dp->log_len += sizeof(unsigned long);
++ (*(unsigned short *)(dp->log + offset))++;
++ }
++ return;
++}
++
++static void log_gv(void)
++{
++ struct dprobes_struct *dp = &dprobes;
++ unsigned long range = rpnpop();
++ unsigned long index = rpnpop();
++ unsigned long offset;
++ unsigned char gv_prefix[PREFIX_SIZE] = {TOKEN_GV_ENTRY, 0, 0};
++ int i;
++
++ if (dp->log_len + PREFIX_SIZE > dp->mod->pgm.logmax) {
++ gen_ex(EX_LOG_OVERFLOW, dp->mod->pgm.logmax, 0);
++ return;
++ }
++ memcpy(dp->log + dp->log_len, gv_prefix, PREFIX_SIZE);
++ offset = dp->log_len + 1;
++ dp->log_len += PREFIX_SIZE;
++
++ for (i = index; i < index + range; i++) {
++ if (i >= dp_num_gv) {
++ gen_ex(EX_INVALID_OPERAND, 2, i);
++ return;
++ }
++ if (dp->log_len + sizeof (unsigned long) > dp->mod->pgm.logmax) {
++ gen_ex(EX_LOG_OVERFLOW, dp->mod->pgm.logmax, 0);
++ return;
++ }
++ read_lock(&dp_gv_lock);
++ *(unsigned long *) (dp->log + dp->log_len) =
++ *(dp_gv + i);
++ read_unlock(&dp_gv_lock);
++ dp->log_len += sizeof(unsigned long);
++ (*(unsigned short *)(dp->log + offset))++;
++ }
++ return;
++}
++
++/*
++ * Overriding probepoint major and minor.
++ */
++static void setmin_i(void)
++{
++ dprobes.minor = get_u16_oprnd();
++}
++
++static void setmin(void)
++{
++ dprobes.minor = (unsigned short) rpnpop();
++}
++
++static void setmaj_i(void)
++{
++ dprobes.major = get_u16_oprnd();
++}
++
++static void setmaj(void)
++{
++ dprobes.major = (unsigned short) rpnpop();
++}
++
++/*
++ * Local Variables.
++ */
++
++static void do_push_lv(unsigned short index)
++{
++ if (index > dprobes.rec->mod->pgm.num_lv)
++ gen_ex(EX_INVALID_OPERAND, 1, index);
++ else
++ rpnpush(*(dprobes.mod->lv + index));
++}
++
++static void push_lvi(void)
++{
++ do_push_lv(get_u16_oprnd());
++}
++
++static void push_lv(void)
++{
++ do_push_lv(rpnpop());
++}
++
++static void pop_lvi(void)
++{
++ unsigned short index = get_u16_oprnd();
++ if (index > dprobes.rec->mod->pgm.num_lv)
++ gen_ex(EX_INVALID_OPERAND, 1, index);
++ else
++ *(dprobes.mod->lv + index) = rpnpop();
++}
++
++static void pop_lv(void)
++{
++ unsigned long value = rpnpop();
++ unsigned short index = (unsigned short) rpnpop();
++ if (index > dprobes.rec->mod->pgm.num_lv)
++ gen_ex(EX_INVALID_OPERAND, 1, index);
++ else
++ *(dprobes.mod->lv + index) = value;
++}
++
++static void do_inc_lv(unsigned short index)
++{
++ if (index > dprobes.rec->mod->pgm.num_lv)
++ gen_ex(EX_INVALID_OPERAND, 1, index);
++ else
++ (*(dprobes.mod->lv + index))++;
++}
++
++static void inc_lvi(void)
++{
++ do_inc_lv(get_u16_oprnd());
++}
++
++static void inc_lv(void)
++{
++ do_inc_lv(rpnpop());
++}
++
++static void do_dec_lv(unsigned short index)
++{
++ if (index > dprobes.rec->mod->pgm.num_lv)
++ gen_ex(EX_INVALID_OPERAND, 1, index);
++ else
++ (*(dprobes.mod->lv + index))--;
++}
++
++static void dec_lvi(void)
++{
++ do_dec_lv(get_u16_oprnd());
++}
++
++static void dec_lv(void)
++{
++ do_dec_lv(rpnpop());
++}
++
++static void do_move_lv(unsigned short index)
++{
++ if (index > dprobes.rec->mod->pgm.num_lv)
++ gen_ex(EX_INVALID_OPERAND, 1, index);
++ else
++ *(dprobes.mod->lv + index) = dprobes.rpn_stack[dprobes.rpn_tos];
++}
++
++static void move_lvi(void)
++{
++ do_move_lv(get_u16_oprnd());
++}
++
++static void move_lv(void)
++{
++ do_move_lv(rpnpop());
++}
++
++/*
++ * global variables.
++ */
++static void do_push_gv(unsigned short index)
++{
++ read_lock(&dp_gv_lock);
++ if (index >= dp_num_gv) {
++ read_unlock(&dp_gv_lock);
++ gen_ex(EX_INVALID_OPERAND, 2, index);
++ return;
++ } else {
++ rpnpush(*(dp_gv + index));
++ read_unlock(&dp_gv_lock);
++ }
++}
++
++static void push_gvi(void)
++{
++ do_push_gv(get_u16_oprnd());
++}
++
++static void push_gv(void)
++{
++ do_push_gv(rpnpop());
++}
++
++static void pop_gvi(void)
++{
++ unsigned short index = get_u16_oprnd();
++ write_lock(&dp_gv_lock);
++ if (index >= dp_num_gv) {
++ write_unlock(&dp_gv_lock);
++ gen_ex(EX_INVALID_OPERAND, 2, index);
++ return;
++ } else {
++ *(dp_gv + index) = rpnpop();
++ write_unlock(&dp_gv_lock);
++ }
++}
++
++static void pop_gv(void)
++{
++ unsigned long value = rpnpop();
++ unsigned short index = (unsigned short) rpnpop();
++ write_lock(&dp_gv_lock);
++ if (index >= dp_num_gv) {
++ write_unlock(&dp_gv_lock);
++ gen_ex(EX_INVALID_OPERAND, 2, index);
++ return;
++ } else {
++ *(dp_gv + index) = value;
++ write_unlock(&dp_gv_lock);
++ }
++}
++
++
++static void do_inc_gv(unsigned short index)
++{
++ write_lock(&dp_gv_lock);
++ if (index >= dp_num_gv) {
++ write_unlock(&dp_gv_lock);
++ gen_ex(EX_INVALID_OPERAND, 2, index);
++ return;
++ } else {
++ (*(dp_gv + index))++;
++ write_unlock(&dp_gv_lock);
++ }
++}
++
++static void inc_gvi(void)
++{
++ do_inc_gv(get_u16_oprnd());
++}
++
++static void inc_gv(void)
++{
++ do_inc_gv(rpnpop());
++}
++
++static void do_dec_gv(unsigned short index)
++{
++ write_lock(&dp_gv_lock);
++ if (index >= dp_num_gv) {
++ write_unlock(&dp_gv_lock);
++ gen_ex(EX_INVALID_OPERAND, 2, index);
++ return;
++ } else {
++ (*(dp_gv + index))--;
++ write_unlock(&dp_gv_lock);
++ }
++}
++
++static void dec_gvi(void)
++{
++ do_dec_gv(get_u16_oprnd());
++}
++
++static void dec_gv(void)
++{
++ do_dec_gv(rpnpop());
++}
++
++static void do_move_gv(unsigned short index)
++{
++ write_lock(&dp_gv_lock);
++ if (index >= dp_num_gv) {
++ write_unlock(&dp_gv_lock);
++ gen_ex(EX_INVALID_OPERAND, 2, index);
++ return;
++ } else {
++ *(dp_gv + index) = dprobes.rpn_stack[dprobes.rpn_tos];
++ write_unlock(&dp_gv_lock);
++ }
++}
++
++static void move_gvi(void)
++{
++ do_move_gv(get_u16_oprnd());
++}
++
++static void move_gv(void)
++{
++ do_move_gv(rpnpop());
++}
++
++
++/*
++ * Arithmetic and logic group.
++ */
++static void add(void)
++{
++ rpnpush(rpnpop() + rpnpop());
++}
++
++static void mul(void)
++{
++ rpnpush(rpnpop() * rpnpop());
++}
++
++static void and(void)
++{
++ rpnpush(rpnpop() & rpnpop());
++}
++
++static void or(void)
++{
++ rpnpush(rpnpop() | rpnpop());
++}
++
++static void xor(void)
++{
++ rpnpush(rpnpop() ^ rpnpop());
++}
++
++static void neg(void)
++{
++ rpnpush(~rpnpop());
++}
++
++static void sub(void)
++{
++ rpnpush(rpnpop() - rpnpop());
++}
++
++/* pop divisor first and dividend next, pushes remainder first and quotient next */
++static void div(void)
++{
++ unsigned long divisor = rpnpop();
++ unsigned long dividend = rpnpop();
++ if (!divisor) {
++ gen_ex(EX_DIV_BY_ZERO, 0, 0);
++ return;
++ }
++ rpnpush(dividend % divisor);
++ rpnpush(dividend / divisor);
++}
++
++static void idiv(void)
++{
++ signed long divisor = (signed long) rpnpop();
++ signed long dividend = (signed long) rpnpop();
++ if (!divisor) {
++ gen_ex(EX_DIV_BY_ZERO, 0, 0);
++ return;
++ }
++ rpnpush(dividend % divisor);
++ rpnpush(dividend / divisor);
++}
++
++static void xchng(void)
++{
++ register unsigned long arg1, arg2;
++ arg1 = rpnpop();
++ arg2 = rpnpop();
++ rpnpush(arg1);
++ rpnpush(arg2);
++}
++
++static void shl_i(void)
++{
++ dprobes.rpn_stack[dprobes.rpn_tos] <<= get_u8_oprnd();
++}
++
++static void shr_i(void)
++{
++ dprobes.rpn_stack[dprobes.rpn_tos] >>= get_u8_oprnd();
++}
++
++static void shr(void)
++{
++ unsigned long oprnd = rpnpop();
++ unsigned long count = rpnpop();
++ oprnd >>= count;
++ rpnpush(oprnd);
++}
++
++static void shl(void)
++{
++ unsigned long oprnd = rpnpop();
++ unsigned long count = rpnpop();
++ oprnd <<= count;
++ rpnpush(oprnd);
++}
++
++static void dup(void)
++{
++ unsigned long dup = rpnpop();
++ u8 dupcount = rpnpop();
++ rpnpush(dup);
++ dupcount &= ~RPN_STACK_SIZE;
++ while (dupcount--)
++ rpnpush(dup);
++}
++
++static void dupn(void)
++{
++ unsigned long dup = rpnpop();
++ u8 dupcount = get_u8_oprnd();
++ dupcount &= ~RPN_STACK_SIZE;
++ while (dupcount--)
++ rpnpush(dup);
++}
++
++static void ros(void)
++{
++ dprobes.rpn_tos += get_u8_oprnd();
++ dprobes.rpn_tos &= RPN_STACK_SIZE - 1;
++}
++
++/*
++ * Verify the read access to a byte pointed by the flat linear address.
++ */
++static void verify_access(int write)
++{
++ u8 b;
++ void * addr = (void *)rpnpop();
++ if (dp_intr_copy_from_user(&b, addr, 1)) {
++ goto fail;
++ }
++ if (write && dp_intr_copy_to_user(addr, &b, 1)) {
++ goto fail;
++ }
++ rpnpush(0);
++ return;
++fail:
++ rpnpush(1);
++ return;
++}
++
++/*
++ * Push immediate value (operand) onto the rpn stack.
++ */
++static void push_i (void)
++{
++ rpnpush(get_ulong_oprnd());
++}
++
++/*
++ * Push byte, word, dword, qword present at the flat address
++ * onto rpn stack.
++ */
++static int push_flat(void *val, void *faddr, unsigned long size)
++{
++ if (dp_intr_copy_from_user(val, faddr, size)) {
++ gen_ex(EX_INVALID_ADDR, (unsigned long)faddr, 0);
++ return 0;
++ }
++ return 1;
++}
++
++static void push_mem_u8 (void)
++{
++ u8 b;
++ if(push_flat((void *)&b, (void *)rpnpop(), sizeof(u8)))
++ rpnpush(b);
++}
++
++static void push_mem_u16 (void)
++{
++ u16 w;
++ if(push_flat((void *)&w, (void *)rpnpop(), sizeof(u16)))
++ rpnpush(w);
++}
++
++static void push_mem_u32 (void)
++{
++ u32 dw;
++ if(push_flat((void *)&dw, (void *)rpnpop(), sizeof(u32)))
++ rpnpush(dw);
++}
++
++static void push_mem_u64 (void)
++{
++ unsigned long q[2];
++ void *faddr = (void *) rpnpop();
++ if (dp_intr_copy_from_user(q, faddr, 2*sizeof(unsigned long))) {
++ gen_ex(EX_INVALID_ADDR, (unsigned long)faddr, 0);
++ } else {
++ rpnpush(q[1]);
++ rpnpush(q[0]);
++ }
++}
++
++/*
++ * Pop byte, word, dword, qword from rpn stack and write
++ * them at the specified linear address.
++ */
++static void pop_flat(void *faddr, void * val, unsigned long size)
++{
++ if (dp_intr_copy_to_user(faddr, val, size))
++ gen_ex(EX_INVALID_ADDR, (unsigned long)faddr, 0);
++}
++
++static void pop_mem_u8 (void)
++{
++ u8 b = (unsigned char) rpnpop();
++ pop_flat((void *)rpnpop(), (void *)&b, sizeof(u8));
++}
++
++static void pop_mem_u16 (void)
++{
++ u16 w = (u16) rpnpop();
++ pop_flat((void *)rpnpop(), (void *)&w, sizeof(u16));
++}
++
++static void pop_mem_u32 (void)
++{
++ u32 d = (u32) rpnpop();
++ pop_flat((void *)rpnpop(), (void *)&d, sizeof(u32));
++}
++
++static void pop_mem_u64 (void)
++{
++ // PORT: on IA64
++ unsigned long q[2];
++ q[0] = rpnpop();
++ q[1] = rpnpop();
++ pop_flat((void *)rpnpop(), (void *)q, 2*sizeof(unsigned long));
++}
++
++/*
++ * Push pid onto rpn stack.
++ */
++static void push_pid(void)
++{
++ rpnpush((unsigned long) current->pid);
++}
++
++/*
++ * Push processor id onto rpn stack.
++ */
++static void push_procid(void)
++{
++ rpnpush((unsigned long)smp_processor_id());
++}
++
++/*
++ * Push the address of the current task structure onto rpn stack.
++ */
++static void push_task(void)
++{
++ rpnpush((unsigned long) current);
++}
++
++#ifdef DPROBES_CALLK_HOOK
++DECLARE_HOOK_HEAD(DPROBES_CALLK);
++USE_HOOK(DPROBES_CALLK);
++static void callk(void)
++{
++ struct dprobes_struct *dp = &dprobes;
++ dp->status &= ~DP_STATUS_INTERPRETER;
++ HOOK(DPROBES_CALLK, &dprobes);
++ dp->status |= DP_STATUS_INTERPRETER;
++}
++#endif
++
++/*
++ * Byte heap management routines.
++ */
++#ifdef CONFIG_SMP
++struct list_head heap_list_set[NR_CPUS];
++#define heap_list heap_list_set[smp_processor_id()]
++#else
++struct list_head heap_list;
++#define heap_list_set (&heap_list)
++#endif
++
++/* Validates the heap handle. Returns the block size in @size */
++static int heap_validate_handle(byte_t *handle, unsigned long *size)
++{
++ struct list_head *tmp;
++ struct heap_hdr *h;
++
++ list_for_each(tmp, &heap_list) {
++ h = list_entry(tmp, struct heap_hdr, list);
++ if (h->addr == handle && h->flags == HEAP_ALLOCATED) {
++ if (size) *size = h->size;
++ return 1;
++ }
++ }
++ return 0;
++}
++
++static void heap_coalesce(struct heap_hdr *heap)
++{
++ struct heap_hdr *h;
++
++ /* Combine this block with the next free block (if available) */
++ if (heap->list.next != &heap_list) {
++ h = list_entry(heap->list.next, struct heap_hdr, list);
++ if (h->flags == HEAP_FREE) {
++ heap->size += (h->size + HEAP_HDR_SIZE);
++ list_del(&h->list);
++ }
++ }
++
++ /* Combine this block with the previous free block (if available) */
++ if (heap->list.prev != &heap_list) {
++ h = list_entry(heap->list.prev, struct heap_hdr, list);
++ if (h->flags == HEAP_FREE) {
++ h->size += (heap->size + HEAP_HDR_SIZE);
++ list_del(&heap->list);
++ }
++ }
++}
++
++/* Allocation succeeds only if (size + HEAP_HDR_SIZE) is available */
++static byte_t *heap_alloc(int size)
++{
++ unsigned long sz;
++ struct list_head *tmp;
++ struct heap_hdr *next;
++
++ list_for_each(tmp, &heap_list) {
++ struct heap_hdr *h = list_entry(tmp, struct heap_hdr, list);
++ sz = h->size;
++ if ((h->flags == HEAP_FREE) && (sz > size + HEAP_HDR_SIZE)) {
++ h->size = size;
++ h->flags = HEAP_ALLOCATED;
++
++ if (sz > size) {
++ next = (struct heap_hdr *)(h->addr + size);
++ next->addr = h->addr + size + HEAP_HDR_SIZE;
++ next->flags = HEAP_FREE;
++ next->size = sz - size - HEAP_HDR_SIZE;
++ list_add(&next->list, &h->list);
++ }
++ return h->addr;
++ }
++ }
++ return NULL;
++}
++
++/* Returns the amount of memory freed */
++static unsigned long heap_free(byte_t *ptr)
++{
++ struct list_head *tmp;
++ unsigned long size = 0;
++
++ list_for_each(tmp, &heap_list) {
++ struct heap_hdr *h = list_entry(tmp, struct heap_hdr, list);
++ if (h->addr == ptr && h->flags == HEAP_ALLOCATED) {
++ h->flags = HEAP_FREE;
++ size = h->size;
++ heap_coalesce(h);
++ break;
++ }
++ }
++ return size;
++}
++
++static void heap_init(byte_t *heap)
++{
++ struct heap_hdr *h;
++
++ INIT_LIST_HEAD(&heap_list);
++ h = (struct heap_hdr *)heap;
++ h->addr = heap + HEAP_HDR_SIZE;
++ h->flags = HEAP_FREE;
++ h->size = dprobes.rec->point.heap_size - HEAP_HDR_SIZE;
++ list_add(&h->list, &heap_list);
++}
++
++/*
++ * Byte heap related instructions.
++ */
++static void pushh_u8_i(void)
++{
++ unsigned short count = get_u16_oprnd();
++ unsigned long offset = rpnpop();
++ byte_t *handle = (byte_t *)rpnpop();
++ unsigned long blk_size;
++
++ /* validate the handle */
++ if (!heap_validate_handle(handle, &blk_size)) {
++ gen_ex(EX_HEAP_INVALID_HANDLE, (unsigned long)handle, offset);
++ return;
++ }
++ while (count) {
++ /* validate the offset */
++ if (offset >= blk_size) {
++ gen_ex(EX_HEAP_INVALID_OFFSET, (unsigned long)handle,
++ offset);
++ return;
++ }
++ rpnpush((unsigned long)(*((u8 *)(handle + offset))));
++ offset++;
++ count--;
++ }
++}
++
++static void pushh_u16_i(void)
++{
++ unsigned short count = get_u16_oprnd();
++ unsigned long offset = rpnpop();
++ byte_t *handle = (byte_t *)rpnpop();
++ unsigned long blk_size;
++
++ /* validate the handle */
++ if (!heap_validate_handle(handle, &blk_size)) {
++ gen_ex(EX_HEAP_INVALID_HANDLE, (unsigned long)handle, offset);
++ return;
++ }
++ while (count) {
++ /* validate the offset */
++ if (offset + 2 > blk_size) {
++ gen_ex(EX_HEAP_INVALID_OFFSET, (unsigned long)handle,
++ offset);
++ return;
++ }
++ rpnpush((unsigned long)(*((u16 *)(handle + offset))));
++ offset += 2;
++ count--;
++ }
++}
++
++static void pushh_u32_i(void)
++{
++ unsigned short count = get_u16_oprnd();
++ unsigned long offset = rpnpop();
++ byte_t *handle = (byte_t *)rpnpop();
++ unsigned long blk_size;
++
++ /* validate the handle */
++ if (!heap_validate_handle(handle, &blk_size)) {
++ gen_ex(EX_HEAP_INVALID_HANDLE, (unsigned long)handle, offset);
++ return;
++ }
++ while (count) {
++ /* validate the offset */
++ if (offset + 4 > blk_size) {
++ gen_ex(EX_HEAP_INVALID_OFFSET, (unsigned long)handle,
++ offset);
++ return;
++ }
++ rpnpush((unsigned long)(*((u32 *)(handle + offset))));
++ offset += 4;
++ count--;
++ }
++}
++
++static void pushh_u64_i(void)
++{
++ unsigned short count = get_u16_oprnd();
++ unsigned long offset = rpnpop();
++ byte_t *handle = (byte_t *)rpnpop();
++ unsigned long blk_size;
++
++ /* validate the handle */
++ if (!heap_validate_handle(handle, &blk_size)) {
++ gen_ex(EX_HEAP_INVALID_HANDLE, (unsigned long)handle, offset);
++ return;
++ }
++ while (count) {
++ /* validate the offset */
++ if (offset + 8 > blk_size) {
++ gen_ex(EX_HEAP_INVALID_OFFSET, (unsigned long)handle,
++ offset);
++ return;
++ }
++ /* shouldn't this order depend on endianness ? */
++ rpnpush((unsigned long)(*((u64 *)(handle + offset))));
++ rpnpush((unsigned long)(*((u64 *)(handle + offset))));
++ offset += 8;
++ count--;
++ }
++}
++
++static void poph_u8_i(void)
++{
++ unsigned short count = get_u16_oprnd();
++ unsigned long offset = rpnpop();
++ byte_t *handle = (byte_t *)rpnpop();
++ unsigned long blk_size;
++
++ /* validate the handle */
++ if (!heap_validate_handle(handle, &blk_size)) {
++ gen_ex(EX_HEAP_INVALID_HANDLE, (unsigned long)handle, offset);
++ return;
++ }
++ while (count) {
++ /* validate the offset */
++ if (offset > blk_size) {
++ gen_ex(EX_HEAP_INVALID_OFFSET, (unsigned long)handle,
++ offset);
++ return;
++ }
++ *((u8 *)(handle + offset)) = (u8)rpnpop();
++ offset++;
++ count--;
++ }
++}
++
++static void poph_u16_i(void)
++{
++ unsigned short count = get_u16_oprnd();
++ unsigned long offset = rpnpop();
++ byte_t *handle = (byte_t *)rpnpop();
++ unsigned long blk_size;
++
++ /* validate the handle */
++ if (!heap_validate_handle(handle, &blk_size)) {
++ gen_ex(EX_HEAP_INVALID_HANDLE, (unsigned long)handle, offset);
++ return;
++ }
++ while (count) {
++ /* validate the offset */
++ if (offset + 2 > blk_size) {
++ gen_ex(EX_HEAP_INVALID_OFFSET, (unsigned long)handle,
++ offset);
++ return;
++ }
++ *((u16 *)(handle + offset)) = (u16)rpnpop();
++ offset += 2;
++ count--;
++ }
++}
++
++static void poph_u32_i(void)
++{
++ unsigned short count = get_u16_oprnd();
++ unsigned long offset = rpnpop();
++ byte_t *handle = (byte_t *)rpnpop();
++ unsigned long blk_size;
++
++ /* validate the handle */
++ if (!heap_validate_handle(handle, &blk_size)) {
++ gen_ex(EX_HEAP_INVALID_HANDLE, (unsigned long)handle, offset);
++ return;
++ }
++ while (count) {
++ /* validate the offset */
++ if (offset + 4 > blk_size) {
++ gen_ex(EX_HEAP_INVALID_OFFSET, (unsigned long)handle,
++ offset);
++ return;
++ }
++ *((u32 *)(handle + offset)) = (u32)rpnpop();
++ offset += 4;
++ count--;
++ }
++}
++
++static void poph_u64_i(void)
++{
++ unsigned short count = get_u16_oprnd();
++ unsigned long offset = rpnpop();
++ byte_t *handle = (byte_t *)rpnpop();
++ unsigned long blk_size;
++
++ /* validate the handle */
++ if (!heap_validate_handle(handle, &blk_size)) {
++ gen_ex(EX_HEAP_INVALID_HANDLE, (unsigned long)handle, offset);
++ return;
++ }
++ while (count) {
++ /* validate the offset */
++ if (offset + 8 > blk_size) {
++ gen_ex(EX_HEAP_INVALID_OFFSET, (unsigned long)handle,
++ offset);
++ return;
++ }
++ /* shouldn't this order depend on endianness ? */
++ *((u32 *)(handle + offset)) = (u32)rpnpop();
++ *((u32 *)(handle + offset + 4)) = (u32)rpnpop();
++ offset += 8;
++ count--;
++ }
++}
++
++static void malloc(void)
++{
++ unsigned long size = rpnpop();
++ byte_t *handle;
++
++ /*
++ * Does this allocation exceed the maximum heapsize specified for
++ * this probe ?
++ */
++ if (size + dprobes.heap_size > dprobes.rec->point.heap_size) {
++ gen_ex(EX_HEAP_NOMEM, size, 0);
++ return;
++ }
++ handle = heap_alloc(size);
++ if (!handle) {
++ gen_ex(EX_HEAP_NOMEM, size, 0);
++ return;
++ }
++ dprobes.heap_size += size;
++ rpnpush((unsigned long)handle);
++}
++
++static void free(void)
++{
++ byte_t *handle = (byte_t *)rpnpop();
++ unsigned long size;
++
++ if (!(size = heap_free(handle))) {
++ gen_ex(EX_HEAP_INVALID_HANDLE, (unsigned long)handle, 0);
++ return;
++ }
++ dprobes.heap_size -= size;
++}
++
++static void push_wid(void)
++{
++ union wid {
++ short a;
++ char b[sizeof(short)];
++ } w;
++ w.a = 0x0102;
++
++ if (sizeof(short) != 2) {
++ rpnpush(0);
++ return;
++ }
++
++ if (w.b[0] == 0x01 && w.b[1] == 0x02) {
++ rpnpush(BITS_PER_LONG);
++ } else if (w.b[0] == 0x02 && w.b[1] == 0x01) {
++ rpnpush(-BITS_PER_LONG);
++ } else {
++ rpnpush(0);
++ }
++}
++
++/*
++ * Entry point for the dprobes interpreter(Probe handler).
++ */
++void dp_interpreter(void)
++{
++ struct dprobes_struct *dp = &dprobes;
++
++ dp->call_tos = CALL_STACK_SIZE - CALL_FRAME_SIZE;
++ dp->call_stack[dp->call_tos + OFFSET_EX_HANDLER] = EX_NO_HANDLER;
++ dp->rpn_tos = dp->rpn_sbp = RPN_STACK_SIZE;
++ dp->rpn_code = dp->rec->mod->pgm.rpn_code;
++ dp->rpn_ip = dp->rec->mod->pgm.rpn_code + dp->rec->point.rpn_offset;
++ dp->log_len = dp->rec->mod->hdr.len;
++ dp->ex_log_len = MIN_ST_SIZE;
++ dp->jmp_count = 0;
++ dp->ex_parm1 = dp->ex_parm2 = 0;
++ if (dp->rec->point.heap_size > 2*HEAP_HDR_SIZE) {
++ read_lock(&dp_heap_lock);
++ heap_init(dp_heap + dp->mod->pgm.heapmax * smp_processor_id());
++ dp->heap_size = 0;
++ }
++
++ dp->status |= DP_STATUS_INTERPRETER;
++ while (dp->status & DP_STATUS_INTERPRETER) {
++ register u8 rpn_instr = *dp->rpn_ip++;
++ switch(rpn_instr) {
++ case DP_NOP: no_op(); break;
++
++ case DP_JMP: jmp(); break;
++ case DP_JLT: jlt(); break;
++ case DP_JLE: jle(); break;
++ case DP_JGT: jgt(); break;
++ case DP_JGE: jge(); break;
++ case DP_JZ: jz(); break;
++ case DP_JNZ: jnz(); break;
++ case DP_LOOP: loop(); break;
++ case DP_CALL: call(); break;
++ case DP_RET: ret(); break;
++ case DP_ABORT: dp_abort(); break;
++ case DP_REM: dp_remove(); break;
++ case DP_EXIT: dp_exit(); break;
++ case DP_EXIT_N: dp_exit_n(); break;
++
++ case DP_SETMIN_I: setmin_i(); break;
++ case DP_SETMIN: setmin(); break;
++ case DP_SETMAJ_I: setmaj_i(); break;
++ case DP_SETMAJ: setmaj(); break;
++ case DP_LOG_STR: log_str(); break;
++ case DP_LOG_MRF: log_mrf(); break;
++ case DP_LOG_I: log_i(); break;
++
++ case DP_PUSH_LVI: push_lvi(); break;
++ case DP_PUSH_LV: push_lv(); break;
++ case DP_POP_LVI: pop_lvi(); break;
++ case DP_POP_LV: pop_lv(); break;
++ case DP_MOVE_LVI: move_lvi(); break;
++ case DP_MOVE_LV: move_lv(); break;
++ case DP_INC_LVI: inc_lvi(); break;
++ case DP_INC_LV: inc_lv(); break;
++ case DP_DEC_LVI: dec_lvi(); break;
++ case DP_DEC_LV: dec_lv(); break;
++
++ case DP_PUSH_GVI: push_gvi(); break;
++ case DP_PUSH_GV: push_gv(); break;
++ case DP_POP_GVI: pop_gvi(); break;
++ case DP_POP_GV: pop_gv(); break;
++ case DP_MOVE_GVI: move_gvi(); break;
++ case DP_MOVE_GV: move_gv(); break;
++ case DP_INC_GVI: inc_gvi(); break;
++ case DP_INC_GV: inc_gv(); break;
++ case DP_DEC_GVI: dec_gvi(); break;
++ case DP_DEC_GV: dec_gv(); break;
++
++ case DP_ADD: add(); break;
++ case DP_SUB: sub(); break;
++ case DP_MUL: mul(); break;
++ case DP_DIV: div(); break;
++ case DP_IDIV: idiv(); break;
++
++ case DP_NEG: neg(); break;
++ case DP_AND: and(); break;
++ case DP_OR: or(); break;
++ case DP_XOR: xor(); break;
++ case DP_ROL_I: rol_i(); break;
++ case DP_ROL: rol(); break;
++ case DP_ROR_I: ror_i(); break;
++ case DP_ROR: ror(); break;
++ case DP_SHL_I: shl_i(); break;
++ case DP_SHL: shl(); break;
++ case DP_SHR_I: shr_i(); break;
++ case DP_SHR: shr(); break;
++ case DP_PBL: pbl(); break;
++ case DP_PBR: pbr(); break;
++ case DP_PBL_I: pbl_i(); break;
++ case DP_PBR_I: pbr_i(); break;
++ case DP_PZL: pzl(); break;
++ case DP_PZR: pzr(); break;
++ case DP_PZL_I: pzl_i(); break;
++ case DP_PZR_I: pzr_i(); break;
++#ifdef DPROBES_CALLK_HOOK
++ case DP_CALLK: callk(); break;
++#endif
++
++ case DP_XCHG: xchng(); break;
++ case DP_DUP_I: dupn(); break;
++ case DP_DUP: dup(); break;
++ case DP_ROS: ros(); break;
++
++ case DP_PUSH_R: pushr(); break;
++ case DP_POP_R: popr(); break;
++ case DP_PUSH_U: pushu(); break;
++ case DP_POP_U: popu(); break;
++
++ case DP_PUSH: push_i(); break;
++ case DP_PUSH_MEM_U8: push_mem_u8(); break;
++ case DP_PUSH_MEM_U16: push_mem_u16(); break;
++ case DP_PUSH_MEM_U32: push_mem_u32(); break;
++ case DP_PUSH_MEM_U64: push_mem_u64(); break;
++ case DP_POP_MEM_U8: pop_mem_u8(); break;
++ case DP_POP_MEM_U16: pop_mem_u16(); break;
++ case DP_POP_MEM_U32: pop_mem_u32(); break;
++ case DP_POP_MEM_U64: pop_mem_u64(); break;
++
++ case DP_PUSH_TASK: push_task(); break;
++ case DP_PUSH_PID: push_pid(); break;
++ case DP_PUSH_PROCID: push_procid(); break;
++
++ case DP_VFY_R: verify_access(0); break;
++ case DP_VFY_RW: verify_access(1); break;
++
++ case DP_SX: sx(); break;
++ case DP_UX: ux(); break;
++ case DP_RX: rx(); break;
++ case DP_PUSH_X: push_x(); break;
++ case DP_PUSH_LP: push_lp(); break;
++ case DP_PUSH_PLP: push_plp(); break;
++ case DP_POP_LP: pop_lp(); break;
++ case DP_LOG_ST: log_st(); break;
++ case DP_PURGE_ST: purge_st(); break;
++ case DP_TRACE_LV: trace_lv(); break;
++ case DP_TRACE_GV: trace_gv(); break;
++ case DP_TRACE_PV: trace_pv(); break;
++
++ case DP_PUSH_SBP: push_sbp(); break;
++ case DP_POP_SBP: pop_sbp(); break;
++ case DP_PUSH_TSP: push_tsp(); break;
++ case DP_POP_TSP: pop_tsp(); break;
++ case DP_COPY_SBP: copy_sbp(); break;
++ case DP_COPY_TSP: copy_tsp(); break;
++ case DP_PUSH_SBP_I: push_sbp_i(); break;
++ case DP_POP_SBP_I: pop_sbp_i(); break;
++ case DP_PUSH_TSP_I: push_tsp_i(); break;
++ case DP_POP_TSP_I: pop_tsp_i(); break;
++ case DP_COPY_SBP_I: copy_sbp_i(); break;
++ case DP_COPY_TSP_I: copy_tsp_i(); break;
++ case DP_PUSH_STP: push_stp(); break;
++ case DP_POP_STP: pop_stp(); break;
++ case DP_SAVE_SBP: save_sbp(); break;
++ case DP_RESTORE_SBP: restore_sbp(); break;
++ case DP_SAVE_TSP: save_tsp(); break;
++ case DP_RESTORE_TSP: restore_tsp(); break;
++
++ case DP_LOG: log(); break;
++ case DP_LOG_LV: log_lv(); break;
++ case DP_LOG_GV: log_gv(); break;
++
++ case DP_MALLOC: malloc(); break;
++ case DP_FREE: free(); break;
++ case DP_PUSH_WID: push_wid(); break;
++ case DP_PUSHH_U8_I: pushh_u8_i(); break;
++ case DP_PUSHH_U16_I: pushh_u16_i(); break;
++ case DP_PUSHH_U32_I: pushh_u32_i(); break;
++ case DP_PUSHH_U64_I: pushh_u64_i(); break;
++ case DP_POPH_U8_I: poph_u8_i(); break;
++ case DP_POPH_U16_I: poph_u16_i(); break;
++ case DP_POPH_U32_I: poph_u32_i(); break;
++ case DP_POPH_U64_I: poph_u64_i(); break;
++
++ default: dp_asm_interpreter(rpn_instr); break;
++ }
++ }
++ if (dp->rec->point.heap_size > 2*HEAP_HDR_SIZE)
++ read_unlock(&dp_heap_lock);
++ return;
++}
++
++void dprobes_interpreter_code_end(void) { }
+diff -urNp linux-2.6.0-test9/drivers/dprobes/i386/dprobes.c linux-2.6.0-test9+kp/drivers/dprobes/i386/dprobes.c
+--- linux-2.6.0-test9/drivers/dprobes/i386/dprobes.c 1970-01-01 05:30:00.000000000 +0530
++++ linux-2.6.0-test9+kp/drivers/dprobes/i386/dprobes.c 2003-11-18 08:15:13.000000000 +0530
+@@ -0,0 +1,808 @@
++/*
++ * IBM Dynamic Probes
++ * Copyright (c) International Business Machines Corp., 2000
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ */
++
++#include <linux/dprobes.h>
++#include <linux/vmalloc.h>
++#include <linux/module.h>
++#include <linux/serial_reg.h>
++#include <linux/ctype.h>
++
++#include <asm/kwatch.h>
++#include <asm/uaccess.h>
++#include <asm/io.h>
++#include <asm/dprobes_exclude.h>
++#ifdef CONFIG_DEBUGREG
++#include <asm/debugreg.h>
++#endif
++
++void dprobes_asm_code_start(void) { }
++
++#ifdef CONFIG_MAGIC_SYSRQ
++extern unsigned long emergency_remove;
++#endif
++
++/*
++ * Format strings for printing out the log.
++ */
++#define DP_LOG_HDR_FMT "dprobes(%d,%d) "
++#define DP_LOG_CPU_FMT "cpu=%d "
++#define DP_LOG_PROCNAME_FMT "name=%s "
++#define DP_LOG_PID_FMT "pid=%d "
++#define DP_LOG_UID_FMT "uid=%d "
++#define DP_LOG_CS_EIP_FMT "cs=%x eip=%08lx "
++#define DP_LOG_SS_ESP_FMT "ss=%x esp=%08lx "
++#define DP_LOG_TSC_FMT "tsc=%08lx:%08lx "
++#define DP_LOG_NEWLINE_FMT "\n"
++#define DP_LOG_DATA_FMT "%x "
++#define DP_LOG_DATA_FT "%x"
++
++static void log_stack_trace(struct dprobes_struct *dp)
++{
++ unsigned long i;
++ byte_t *ex_log;
++ unsigned char *tmp = dp->log_hdr;
++ unsigned long ex_log_len = dp->ex_log_len;
++
++ if (ex_log_len > MIN_ST_SIZE) {
++ tmp += sprintf(tmp, DP_LOG_HDR_FMT, 0, 0);
++
++#ifdef CONFIG_SMP
++ tmp += sprintf(tmp, DP_LOG_CPU_FMT, smp_processor_id());
++#endif
++ printk("%s ", dp->log_hdr);
++
++ ex_log_len -= MIN_ST_SIZE;
++ ex_log = dp->ex_log + MIN_ST_SIZE;
++ for (i = 0; i < ex_log_len; i++)
++ printk(DP_LOG_DATA_FMT, ex_log[i]);
++ printk(DP_LOG_NEWLINE_FMT);
++ }
++}
++
++static void fmt_log_hdr(struct dprobes_struct *dp)
++{
++ unsigned char *tmp = dp->log_hdr;
++ unsigned long log_flags = dp->mod->pgm.flags & DP_LOG_MASK;
++
++ tmp += sprintf(tmp, DP_LOG_HDR_FMT, dp->major, dp->minor);
++
++#ifdef CONFIG_SMP
++ tmp += sprintf(tmp, DP_LOG_CPU_FMT, smp_processor_id());
++#endif
++ if (log_flags & DP_LOG_PID)
++ tmp += sprintf(tmp, DP_LOG_PID_FMT, current->pid);
++ if (log_flags & DP_LOG_UID)
++ tmp += sprintf(tmp, DP_LOG_UID_FMT, current->uid);
++ if (log_flags & DP_LOG_CS_EIP)
++ tmp += sprintf(tmp, DP_LOG_CS_EIP_FMT, dp->cs, dp->eip);
++ if (log_flags & DP_LOG_SS_ESP)
++ tmp += sprintf(tmp, DP_LOG_SS_ESP_FMT, dp->ss, dp->esp);
++ if (log_flags & DP_LOG_TSC) {
++ struct timeval ts;
++ do_gettimeofday(&ts);
++ tmp += sprintf(tmp, DP_LOG_TSC_FMT, ts.tv_sec, ts.tv_usec);
++ }
++ if (log_flags & DP_LOG_PROCNAME)
++ tmp += sprintf(tmp, DP_LOG_PROCNAME_FMT, current->comm);
++ tmp += sprintf(tmp, DP_LOG_NEWLINE_FMT);
++
++ tmp += sprintf(tmp, DP_LOG_HDR_FMT, dp->major, dp->minor);
++ *tmp = '\0';
++
++ return;
++}
++
++
++#define ROWS 16
++/*
++ * Saves (writes) the log to the standard kernel log.
++ */
++static void log_dumpformat(unsigned long log_len, byte_t *log)
++{
++ unsigned long i, start = 0, end = ROWS;
++ unsigned int off=0x0;
++ char c;
++
++ while (start < log_len) {
++ printk("0x%8.8x : ", (unsigned int)off);
++ for( i = start; i < end; i++) {
++ printk("%2.2x", 0xff & log[i]);
++ if (((i + 1) % 4) == 0)
++ printk(" ");
++ }
++
++ if ((end - start) < ROWS)
++ for (i = 0; i < ((ROWS - (end - start)) * 2) + 4 - ((end - start) / 4); i++)
++ printk(" ");
++
++ printk(": ");
++ for( i = start ; i < end ; i++ ) /* Now print the ASCII field. */
++ {
++ c = 0x7f & log[i]; /* mask out bit 7 */
++ if (!(isprint(c))) /* If not printable */
++ printk("."); /* print a dot */
++ else
++ printk("%c",c);
++
++ }
++
++ printk(DP_LOG_NEWLINE_FMT);
++ if ( (log_len - end) > ROWS)
++ end += ROWS;
++ else
++ end = log_len;
++ start += ROWS;
++ off +=0x10;
++ }
++
++}
++
++static void log_stack_trace_dump(struct dprobes_struct *dp)
++{
++ byte_t *ex_log;
++ unsigned char *tmp = dp->log_hdr;
++ unsigned long ex_log_len = dp->ex_log_len;
++ if (ex_log_len > MIN_ST_SIZE) {
++ tmp += sprintf(tmp, DP_LOG_HDR_FMT, 0, 0);
++
++#ifdef CONFIG_SMP
++ tmp += sprintf(tmp, DP_LOG_CPU_FMT, smp_processor_id());
++#endif
++ printk("%s \n", dp->log_hdr);
++
++ ex_log_len -= MIN_ST_SIZE;
++ ex_log = dp->ex_log + MIN_ST_SIZE;
++ log_dumpformat(ex_log_len, ex_log);
++
++ }
++}
++
++/*
++ * Saves (writes) the log to the standard kernel log.
++ */
++static int log_to_klog_dump(struct dprobes_struct *dp)
++{
++ byte_t *log = dp->log + dp->mod->hdr.len;
++ unsigned long log_len = dp->log_len - dp->mod->hdr.len;
++
++
++ fmt_log_hdr(dp);
++ printk("%s \n", dp->log_hdr);
++ log_dumpformat( log_len, log);
++ return 0;
++}
++
++/*
++ * Saves (writes) the log to the standard kernel log.
++ */
++static int log_to_klog(struct dprobes_struct *dp)
++{
++ unsigned long i;
++ byte_t *log = dp->log + dp->mod->hdr.len;
++ unsigned long log_len = dp->log_len - dp->mod->hdr.len;
++
++ fmt_log_hdr(dp);
++ printk("%s ", dp->log_hdr);
++
++ for (i = 0; i < log_len; i++) {
++ if (log[i] < 0xf)
++ printk(DP_LOG_DATA_FT, 0);
++ printk(DP_LOG_DATA_FMT, log[i]);
++ }
++ printk(DP_LOG_NEWLINE_FMT);
++ return 0;
++}
++
++/*
++ * The order in which the optional header elements is written out has to be the
++ * same order in which the DP_HDR_* constants are defined in
++ * include/linux/dprobes.h. The formatter is expected to look for the optional
++ * elements in the same order.
++ */
++static void fmt_log_hdr_raw(struct dprobes_struct *dp)
++{
++ unsigned char *tmp = dp->log;
++ unsigned long mask = dp->mod->hdr.mask;
++
++ dp->mod->hdr.major = dp->major;
++ dp->mod->hdr.minor = dp->minor;
++ *(struct dp_trace_hdr_struct *)tmp = dp->mod->hdr;
++ tmp += sizeof(dp->mod->hdr);
++
++#ifdef CONFIG_SMP
++ *(unsigned int *)tmp = smp_processor_id();
++ tmp += sizeof(int);
++#endif
++ if (mask & DP_HDR_PID) {
++ *(pid_t *)tmp = current->pid;
++ tmp += sizeof(pid_t);
++ }
++ if (mask & DP_HDR_UID) {
++ *(uid_t *)tmp = current->uid;
++ tmp += sizeof(uid_t);
++ }
++ if (mask & DP_HDR_CS) {
++ *(unsigned short *)tmp = dp->cs;
++ tmp += sizeof(dp->cs);
++ }
++ if (mask & DP_HDR_EIP) {
++ *(unsigned long *)tmp = dp->eip;
++ tmp += sizeof(dp->eip);
++ }
++ if (mask & DP_HDR_SS) {
++ *(unsigned short *)tmp = dp->ss;
++ tmp += sizeof(dp->ss);
++ }
++ if (mask & DP_HDR_ESP) {
++ *(unsigned long *)tmp = dp->esp;
++ tmp += sizeof(dp->esp);
++ }
++ if (mask & DP_HDR_TSC) {
++ struct timeval ts;
++ do_gettimeofday(&ts);
++ *(struct timeval *)tmp = ts;
++ tmp += sizeof(ts);
++ }
++ if (mask & DP_HDR_PROCNAME) {
++ memcpy(tmp, current->comm, 16);
++ tmp += 16;
++ }
++ return;
++}
++
++/*
++ * dp->mod->hdr is changed here. Hence should be called only after
++ * fmt_log_hdr_raw().
++ */
++static void fmt_st_hdr_raw(struct dprobes_struct *dp)
++{
++ unsigned char *tmp = dp->ex_log;
++ dp->mod->hdr.mask = DP_HDR_MAJOR | DP_HDR_MINOR;
++ dp->mod->hdr.major = DP_ST_MAJOR;
++ dp->mod->hdr.minor = DP_ST_MINOR;
++ dp->mod->hdr.len = MIN_ST_SIZE;
++ *(struct dp_trace_hdr_struct *)tmp = dp->mod->hdr;
++ tmp += sizeof(dp->mod->hdr);
++#ifdef CONFIG_SMP
++ dp->mod->hdr.mask |= DP_HDR_CPU;
++ *(unsigned int *)tmp = smp_processor_id();
++ tmp += sizeof(int);
++ dp->mod->hdr.len += sizeof(int);
++#endif
++}
++
++#if defined(CONFIG_TRACE) || defined(CONFIG_TRACE_MODULE)
++#include <linux/trace.h>
++
++static int log_to_ltt(struct dprobes_struct *dp)
++{
++ trace_raw_event(dp->mod->trace_id, dp->log_len, dp->log);
++ return 0;
++}
++
++static int log_st_to_ltt(struct dprobes_struct *dp)
++{
++ trace_raw_event(dp->mod->trace_id, dp->ex_log_len, dp->ex_log);
++ return 0;
++}
++#else
++static int log_to_ltt(struct dprobes_struct *dp)
++{
++ printk(KERN_WARNING "dprobes: Linux Trace Toolkit not available.\n");
++ return 0;
++}
++
++static int log_st_to_ltt(struct dprobes_struct *dp)
++{
++ printk(KERN_WARNING "dprobes: Linux Trace Toolkit not available.\n");
++ return 0;
++}
++#endif
++
++#ifdef CONFIG_EVLOG
++#include <linux/evl_log.h>
++static int log_to_evl(struct dprobes_struct *dp)
++{
++ /*
++ * We will need to modify this in future to create a DPROBES facility
++ * type and use dp->id instead of (major,minor) for event type. Even the
++ * priority ought to be something other than LOG_KERN.
++ */
++ posix_log_write(LOG_KERN,
++ (dp->major << 16) | dp->minor, /* dp->id */
++ LOG_INFO,
++ (dp->log + dp->mod->hdr.len),
++ (dp->log_len - dp->mod->hdr.len),
++ POSIX_LOG_BINARY,
++ 0);
++ return 0;
++}
++#else
++static int log_to_evl(struct dprobes_struct *dp)
++{
++ printk(KERN_WARNING "dprobes: POSIX Event Logging not available.\n");
++ return 0;
++}
++#endif
++
++#define BOTH_EMPTY_TR (UART_LSR_TEMT | UART_LSR_THRE)
++#define COM1_ADDR 0x3f8
++#define COM2_ADDR 0x2f8
++
++/*
++ * Wait for the transmitter buffer to become empty.
++ */
++static inline void wait_for_xmitr_empty(int port)
++{
++ int lsr;
++ unsigned int tmout = 1000000;
++ do {
++ lsr = inb(port + UART_LSR);
++ if (--tmout == 0) break;
++ } while ((lsr & BOTH_EMPTY_TR) != BOTH_EMPTY_TR);
++}
++
++/*
++ * This routine sends the raw binary data to com port. The receiving
++ * end should be capable of handling raw bytes.
++ */
++static int log_to_com_port(struct dprobes_struct *dp, int port)
++{
++ int i, ier;
++ byte_t *log = dp->log;
++ byte_t *ex_log = dp->ex_log;
++ unsigned long log_len = dp->log_len;
++ unsigned long ex_log_len = dp->ex_log_len;
++
++ /* store UART configuration and program it in polled mode */
++ ier = inb(port + UART_IER);
++ outb(0x00, port + UART_IER);
++
++ /* send the log data to com port */
++ fmt_log_hdr_raw(dp);
++ for (i = 0; i < log_len; i++) {
++ wait_for_xmitr_empty(port);
++ outb(log[i], port + UART_TX);
++ }
++
++ /* send CR and LF characters */
++ wait_for_xmitr_empty(port);
++ outb(0x0d, port + UART_TX);
++ wait_for_xmitr_empty(port);
++ outb(0x0a, port + UART_TX);
++
++ /* send the stack trace data(if present) to com port */
++ if (ex_log_len > MIN_ST_SIZE) {
++ fmt_st_hdr_raw(dp);
++ for (i = 0; i < ex_log_len; i++) {
++ wait_for_xmitr_empty(port);
++ outb(ex_log[i], port + UART_TX);
++ }
++
++ /* send CR and LF characters */
++ wait_for_xmitr_empty(port);
++ outb(0x0d, port + UART_TX);
++ wait_for_xmitr_empty(port);
++ outb(0x0a, port + UART_TX);
++ }
++
++ /* restore the configuration of UART */
++ wait_for_xmitr_empty(port);
++ outb(ier, port + UART_IER);
++ return 0;
++}
++
++static void save_log(struct dprobes_struct *dp)
++{
++ switch(dp->mod->pgm.flags & DP_LOG_TARGET_MASK) {
++ case DP_LOG_TARGET_COM1:
++ log_to_com_port(dp, COM1_ADDR);
++ break;
++ case DP_LOG_TARGET_COM2:
++ log_to_com_port(dp, COM2_ADDR);
++ break;
++ case DP_LOG_TARGET_LTT:
++ fmt_log_hdr_raw(dp);
++ log_to_ltt(dp);
++ if (dp->ex_log_len > MIN_ST_SIZE) {
++ fmt_st_hdr_raw(dp);
++ log_st_to_ltt(dp);
++ }
++ break;
++ case DP_LOG_TARGET_EVL:
++ log_to_evl(dp);
++ break;
++ case DP_UNFORMATTED_OUTPUT:
++ log_to_klog(dp);
++ log_stack_trace(dp);
++ break;
++ default:
++ log_to_klog_dump(dp);
++ log_stack_trace_dump(dp);
++ break;
++ }
++ return;
++}
++
++#ifdef CONFIG_DEBUGREG
++static inline void set_rf(struct dp_record_struct *rec, struct pt_regs *regs)
++{
++ unsigned short wtype = rec->point.probe & DP_WATCHTYPE_MASK;
++ if (wtype == DP_WATCHTYPE_EXECUTE) {
++ regs->eflags |= EF_RF;
++ }
++ return;
++}
++
++inline void delete_watchpoint(unsigned int cond, struct pt_regs *regs, struct dp_record_struct *rec)
++{
++ int dbno = rec->dbregno;
++ unsigned long dr = read_dr(7);
++ RESET_DR7(dr, dbno);
++ dr_free(dbno);
++ write_dr(7, dr);
++}
++
++#else
++inline void delete_watchpoint(unsigned int cond, struct pt_regs *regs, struct dp_record_struct *rec)
++{
++ return;
++}
++
++static inline void set_rf(struct dp_record_struct *rec, struct pt_regs *regs)
++{
++ return;
++}
++
++inline int dr_trap_type(unsigned int cond)
++{
++ return 0;
++}
++#endif
++/*
++ * This routine save the registers during handling of kernel, userspace and
++ * watchpoint probes and also updates the status in the dp_record_struct.
++ */
++void save_regs(struct dp_record_struct *rec, unsigned long addr,
++ struct pt_regs *regs)
++{
++ struct dprobes_struct *dp = &dprobes;
++
++ dp->rec = rec;
++ dp->probe_addr = addr;
++ dp->regs = regs;
++ dp->mod = rec->mod;
++ dp->major = rec->point.major;
++ dp->minor = rec->point.minor;
++ dp->status = 0UL;
++ dp->cs = regs->xcs;
++ dp->eip = regs->eip - sizeof(opcode_t);
++ dp->ss = regs->xss;
++ dp->esp = regs->esp;
++ if (regs->xcs & 3) {
++ dp->status |= DP_USER_PROBE;
++ dp->uregs = regs;
++ } else {
++ dp->status |= DP_KERNEL_PROBE;
++ dp->uregs = (struct pt_regs * )((unsigned long)(current) + 2 * PAGE_SIZE - sizeof(struct pt_regs));
++ }
++ dp->status |= DP_STATUS_FIRSTFPU;
++
++ /* rec->count > 0 for passcount */
++ if (rec->status & DP_REC_STATUS_ACTIVE && rec->count >= 0) {
++ dp_interpreter();
++ if (rec->status & (DP_REC_STATUS_DISABLED | DP_REC_STATUS_REMOVED)) {
++ if (rec->status & DP_REC_STATUS_ACTIVE) {
++ rec->status &= ~DP_REC_STATUS_ACTIVE;
++ rec->status |= DP_REC_STATUS_DISABLED;
++ }
++ }
++ } else {
++ dp->status |= DP_STATUS_ABORT;
++ }
++}
++int dp_pre_handler(struct kprobe *kp, struct pt_regs *regs)
++{
++ struct dprobes_struct *dp = &dprobes;
++ struct dp_record_struct *rec = container_of(kp, typeof(*rec), kp);
++
++ if (rec->status & (DP_REC_STATUS_DISABLED | DP_REC_STATUS_REMOVED)) {
++ return 1;
++ }
++
++#ifdef CONFIG_MAGIC_SYSRQ
++ /* check if probes are marked for emergency removal */
++ if (test_bit(0, &emergency_remove))
++ return 1;
++#endif
++ dp->rec = rec;
++ save_regs(rec, (unsigned long)rec->kp.addr, regs);
++ dp->status |= DP_STATUS_SS;
++ return 0;
++}
++
++void dp_post_handler(struct kprobe *kp, struct pt_regs *regs, unsigned long flags)
++{
++ struct dprobes_struct *dp = &dprobes;
++
++ dp->rec->count++;
++ if (dp->rec->count >= dp->rec->point.maxhits) {
++ dp->rec->status &= ~DP_REC_STATUS_ACTIVE;
++ dp->rec->status |= DP_REC_STATUS_DISABLED;
++ }
++ if (dp->status & DP_STATUS_ABORT)
++ return;
++ save_log(dp);
++}
++
++int dp_fault_handler(struct kprobe *kp, struct pt_regs *regs, int trapnr)
++{
++ struct dprobes_struct *dp = &dprobes;
++
++ if (trapnr != 13 && trapnr != 14)
++ return 0;
++
++ if (dp->status & DP_STATUS_INTERPRETER) {
++ const struct exception_table_entry *fixup;
++ if ((fixup = search_exception_tables(regs->eip)) != 0) {
++ regs->eip = fixup->fixup;
++ return 1;
++ }
++ } else if (dp->status & DP_STATUS_SS) {
++ if (dp->rec->point.logonfault) {
++ dp->rec->count++;
++ save_log(dp);
++ }
++ }
++ return 0;
++}
++
++/*
++ * If there are multiple debug traps simultaneously, we handle the
++ * single step condition first (DR_STEP) as it is related to completing
++ * a previous debug trap. We then handle the debug register hits.
++ */
++int dp_do_debug(struct pt_regs * regs, unsigned long condition)
++{
++#ifdef CONFIG_DEBUGREG
++#if 0
++ /* assume that only one watchpoint possible on an address. */
++ if (condition & (DR_TRAP0|DR_TRAP1|DR_TRAP2|DR_TRAP3)) {
++ if (dp_trap(regs, DP_PROBE_WATCHPOINT, condition, dr_trap_addr(condition))) {
++ return 1;
++ }
++ }
++#endif
++#endif
++ return 0;
++}
++
++#include <asm/desc.h>
++#include <asm/ldt.h>
++#ifndef GDT_ENTRIES
++#define GDT_ENTRIES (__TSS(NR_CPUS))
++#endif
++static inline int is_fpu_instn(byte_t *addr)
++{
++ if (((*addr & 0xf8) == 0xd8) || (*addr == 0x9b))
++ return 1;
++ return 0;
++}
++/* Use this array to store the dp_record_struct during registration, and index
++ * using the debugreg number in the handler to get back, the dp_record_struct.
++ */
++static struct dp_record_struct *kwatch_rec[DP_MAX_WATCHPOINT];
++
++/*
++ * dp_kwatch_handler: Dprobes handler for watchpoint probes, registerd through
++ * kwatch interface. This routine is called by the watch point probe handler,
++ * when the probe is hit.
++ */
++
++void dp_kwatch_handler(struct kwatch *kw, struct pt_regs *regs, int debugreg)
++{
++ struct dprobes_struct *dp = &dprobes;
++ struct dp_record_struct *rec = kwatch_rec[debugreg];
++ if ((!rec) || (rec->status & (DP_REC_STATUS_DISABLED |
++ DP_REC_STATUS_REMOVED))) {
++ return ;
++ }
++ rec->count++;
++ save_regs(rec, kw->addr, regs);
++ if (dp->status & DP_STATUS_ABORT)
++ return;
++ save_log(dp);
++ dp->status &= ~DP_STATUS_DONE;
++ return;
++}
++
++/*
++ * Unregister watchpoint probes.
++ */
++
++static inline int remove_wp(byte_t *addr, struct dp_record_struct *rec)
++{
++ kwatch_rec[rec->dbregno] = NULL;
++ unregister_kwatch(rec->dbregno);
++ return 0;
++}
++
++/* Check the range for debug register watchpoint */
++static inline int invalid_range(unsigned long len)
++{
++ return (len > 3 ) ? 1 : 0;
++}
++
++/*
++ * Insert watchpoint probes, using the kwatch interface.
++ */
++static inline int insert_wp(byte_t *addr, struct dp_record_struct *rec,
++ struct dp_module_struct *m, struct page * page)
++{
++ int ret;
++
++ if (invalid_range(rec->point.len)) {
++ rec->status |= DP_REC_STATUS_WATCHPOINT_LEN_INVALID;
++ return -1;
++ }
++ ret = register_kwatch((unsigned long)addr, (u8)(rec->point.len + 1),
++ (u8)(rec->point.probe & DP_WATCHTYPE_MASK), dp_kwatch_handler);
++
++ if (ret < 0) {
++ rec->status |= DP_REC_STATUS_KPROBE_ERR;
++ return -1;
++ }
++
++ kwatch_rec[ret] = rec;
++ rec->status |= DP_REC_STATUS_ACTIVE;
++ rec->dbregno = ret;
++ return 0;
++}
++
++/*
++ * Writing the breakpoint instruction should be the last thing done
++ * in this function to ensure that all the structures that
++ * are needed to locate the probe are in place before a probe can
++ * be hit.
++ */
++static inline int insert_bp(byte_t *addr, struct dp_record_struct *rec,
++ struct dp_module_struct *m, struct page * page)
++{
++ if (dprobes_excluded((unsigned long)addr)) {
++ rec->status |= DP_REC_STATUS_EXCLUDED;
++ return -1;
++ }
++
++ if (!(m->pgm.flags & DP_MODTYPE_USER) &&
++ !(((unsigned long)addr) >= m->base &&
++ ((unsigned long)addr) < (m->end + m->base))) {
++ rec->status |= DP_REC_STATUS_INVALID_OFFSET;
++ return -1;
++ }
++ if (*addr != rec->point.opcode) {
++ if (m->pgm.flags & DP_DONT_VERIFY_OPCODES) {
++ rec->point.opcode = *addr;
++ } else {
++ if (!(m->pgm.flags & DP_MODTYPE_USER) ||
++ !IS_COW_PAGE(page, m->inode)) {
++ rec->point.actual_opcode = *addr;
++ rec->status |= DP_REC_STATUS_MISMATCH;
++ }
++ return -1;
++ }
++ }
++
++ /* all okay, register the probe */
++ rec->kp.addr = addr;
++ rec->kp.pre_handler = dp_pre_handler;
++ rec->kp.post_handler = dp_post_handler;
++ rec->kp.fault_handler = dp_fault_handler;
++
++ if (register_kprobe(&rec->kp)) {
++ rec->status |= DP_REC_STATUS_KPROBE_ERR;
++ return -1;
++ }
++ rec->status |= DP_REC_STATUS_ACTIVE;
++ return 0;
++}
++
++int __insert_probe(byte_t *addr, struct dp_record_struct *rec,
++ struct dp_module_struct *m, struct page * page)
++{
++ if (rec->point.probe & DP_PROBE_BREAKPOINT)
++ return insert_bp(addr, rec, m, page);
++ else
++ return insert_wp(addr, rec, m, page);
++}
++
++static inline void remove_bp(byte_t *addr, struct dp_record_struct *rec)
++{
++ unregister_kprobe(&rec->kp);
++}
++
++int __remove_probe(byte_t * addr, struct dp_record_struct *rec)
++{
++ if (rec->point.probe & DP_PROBE_BREAKPOINT)
++ remove_bp(addr, rec);
++ else
++ remove_wp(addr, rec);
++ return 0;
++}
++/*
++ * Insert breakpoints in the user space at the given address.
++ * Need to specify address, page and vma in kprobe structure.
++ */
++inline int insert_probe_userspace(byte_t *addr, struct dp_record_struct *rec,
++ struct page *page, struct vm_area_struct *vma)
++{
++ rec->kp.user->addr = addr;
++ rec->kp.user->page = page;
++ rec->kp.user->vma = vma;
++ if (insert_kprobe_user(&rec->kp)) {
++ rec->status |= DP_REC_STATUS_KPROBE_ERR;
++ return -1;
++ }
++ rec->status |= DP_REC_STATUS_ACTIVE;
++ return 0;
++}
++
++/*
++ * Remove breakpoints in the user space previously inserted at the given
++ * address. Need to specify address, page and vma in kprobe structure.
++ */
++inline int remove_probe_userspace(byte_t *addr, struct dp_record_struct *rec,
++ struct page *page, struct vm_area_struct *vma)
++{
++ rec->kp.user->addr = addr;
++ rec->kp.user->page = page;
++ rec->kp.user->vma = vma;
++ return remove_kprobe_user(&rec->kp);
++
++}
++
++
++/*
++ * register user space probes before actually inserting the probes.
++ */
++int register_userspace_probes(struct dp_record_struct *rec)
++{
++ rec->kp.pre_handler = dp_pre_handler;
++ rec->kp.post_handler = dp_post_handler;
++ rec->kp.fault_handler = dp_fault_handler;
++ rec->up.offset = rec->point.offset;
++ rec->up.inode = rec->mod->inode;
++ rec->kp.user = &(rec->up);
++ if (register_kprobe_user(&rec->kp)) {
++ rec->status |= DP_REC_STATUS_KPROBE_ERR;
++ return -1;
++ }
++ rec->status |= DP_REC_STATUS_ACTIVE;
++ return 0;
++}
++
++/*
++ * Unregister the user space probes previously registered.
++ * Make sure all the probes for a pair of inode and offset are removed
++ * before unregistering.
++ */
++void unregister_userspace_probes(struct dp_record_struct *rec)
++{
++ unregister_kprobe_user(&rec->kp);
++}
++void dprobes_asm_code_end(void) { }
+diff -urNp linux-2.6.0-test9/drivers/dprobes/i386/dprobes_in.c linux-2.6.0-test9+kp/drivers/dprobes/i386/dprobes_in.c
+--- linux-2.6.0-test9/drivers/dprobes/i386/dprobes_in.c 1970-01-01 05:30:00.000000000 +0530
++++ linux-2.6.0-test9+kp/drivers/dprobes/i386/dprobes_in.c 2003-11-18 07:29:47.000000000 +0530
+@@ -0,0 +1,903 @@
++/*
++ * IBM Dynamic Probes
++ * Copyright (c) International Business Machines Corp., 2000
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ */
++
++/*
++ * This is a special file, part of the dprobes interpreter that contains
++ * architecture-specific functions. It is not compiled as a seperate unit.
++ * It is #included into the arch-independent dprobes_in.c at a specific
++ * location.
++ */
++#include <linux/binfmts.h>
++#ifdef CONFIG_DUMP
++#include <linux/dump.h>
++#endif
++#ifdef CONFIG_KDB
++#include <linux/kdb.h>
++#endif
++#include <asm/desc.h>
++#include <asm/ldt.h>
++#ifndef GDT_ENTRIES
++#define GDT_ENTRIES (__TSS(NR_CPUS))
++#endif
++
++/*
++ * Convert segmented address (selector : offset) to flat linear address.
++ */
++static int dp_seg_to_flat(unsigned long sel, unsigned long off, void **faddr)
++{
++ struct Xgt_desc_struct g;
++ unsigned long ldt_desc, ldt_base, ldtr, seg_desc, seg_base;
++ unsigned long gdt_index = (sel & ~7);
++ mm_context_t *x = ¤t->mm->context;
++
++ __asm__ __volatile__ ("sgdt (%0)" : : "r"((unsigned long) &g));
++
++ if (sel & 4) {
++ if (!(x->ldt && ((sel >> 3) < x->size)))
++ return 1;
++
++ __asm__ __volatile__ ("sldt %0" :"=r"(ldtr));
++ ldt_desc = g.address + ((ldtr & ~7));
++ ldt_base = _get_base ((char *)ldt_desc);
++ seg_desc = ldt_base + (gdt_index);
++ seg_base = _get_base ((char *)seg_desc);
++ }
++ else {
++ if ((sel >> 3) > GDT_ENTRIES)
++ return 1;
++ seg_desc = g.address + gdt_index;
++ seg_base = _get_base ((char *)seg_desc);
++ }
++ *faddr = (void *)(seg_base + off);
++ return 0;
++}
++
++static void seg2lin(void)
++{
++ void *faddr;
++ unsigned long off = rpnpop();
++ unsigned long sel = rpnpop();
++
++ if (dp_seg_to_flat(sel, off, &faddr)) {
++ gen_ex(EX_SEG_FAULT, sel, 0);
++ return;
++ }
++ rpnpush((unsigned long) faddr);
++}
++
++#ifdef CONFIG_KDB
++/*
++ * Temporarily restore the orignal opcode befor calling the kdb
++ * so that kdb shows correct disassembly.
++ */
++static inline void restore_opcode(void)
++{
++ if (dprobes.rec->point.probe & DP_PROBE_BREAKPOINT) {
++ dprobes.reset_addr = dprobes.probe_addr;
++ *((byte_t *)dprobes.reset_addr) = dprobes.opcode;
++ flush_page_to_ram(dprobes.reset_addr);
++ }
++}
++
++/*
++ * After kdb terminates, restore the breakpoint.
++ */
++static inline void restore_int3(void)
++{
++ if (dprobes.rec->point.probe & DP_PROBE_BREAKPOINT) {
++ dprobes.reset_addr = dprobes.probe_addr;
++ *((byte_t *)dprobes.reset_addr) = DP_INSTR_BREAKPOINT;
++ flush_page_to_ram(dprobes.reset_addr);
++ }
++}
++
++void call_kdb(void)
++{
++ unsigned long eip = dprobes.regs->eip - 1;
++ if (dprobes.status & DP_KERNEL_PROBE) {
++ if (dprobes.rec->point.probe & DP_PROBE_BREAKPOINT)
++ dprobes.regs->eip--;
++ restore_opcode();
++ kdb(KDB_REASON_CALL, 0, dprobes.regs);
++ restore_int3();
++ if (eip == dprobes.regs->eip)
++ dprobes.regs->eip++;
++ }
++}
++#else
++
++void call_kdb(void)
++{
++ printk("Dprobes: Kernel Debugger is not present.\n");
++ return;
++}
++#endif
++/*
++ * Exiting the interpreter through the registered facility n.
++ */
++static void dp_exit_n(void)
++{
++ u8 facility = get_u8_oprnd();
++ switch(facility) {
++ case DP_EXIT_TO_SGI_KDB:
++ call_kdb();
++ break;
++
++ case DP_EXIT_TO_SGI_VMDUMP:
++#ifdef CONFIG_DUMP
++ if (dprobes.status & DP_KERNEL_PROBE)
++ dump("Dumping due to dprobes", dprobes.regs);
++#else
++ printk("dprobes: Crash dump facility is not present.\n");
++#endif
++ break;
++ case DP_EXIT_TO_CORE_DUMP:
++ if (dprobes.status & DP_USER_PROBE) {
++ if (do_coredump(SIGINT, SIGINT, dprobes.regs)) {
++ current->mm->dumpable = 1;
++ printk("dprobes(%d,%d) process %s dumped core\n", dprobes.major, dprobes.minor, current->comm);
++ }
++ else
++ printk("dprobes(%d,%d) exit to core dump failed\n", dprobes.major, dprobes.minor);
++ }
++ break;
++ default: gen_ex(EX_INVALID_OPERAND, 4, facility); return;
++
++ }
++ dprobes.status &= ~DP_STATUS_INTERPRETER;
++}
++
++/*
++ * Interpreter's version of copy_xxx_user functions. These need to save
++ * and restore cr2 contents to be able to correctly handle probes in
++ * page fault handler.
++ */
++static int dp_intr_copy_from_user(void *to, void *from, int len)
++{
++ unsigned long address;
++ int retval;
++ __asm__("movl %%cr2,%0":"=r" (address)); /* save cr2 contents */
++ retval = __copy_from_user(to, from, len);
++ __asm__("movl %0,%%cr2": :"r" (address)); /* restore cr2 contents */
++ return retval;
++}
++
++static int dp_intr_copy_to_user(void *to, void *from, int len)
++{
++ unsigned long address;
++ int retval;
++ __asm__("movl %%cr2,%0":"=r" (address)); /* save cr2 contents */
++ retval = __copy_to_user(to, from, len);
++ __asm__("movl %0,%%cr2": :"r" (address)); /* restore cr2 contents */
++ return retval;
++}
++
++#define BITS_IN_UL 32
++
++#define PROPAGATE_BIT(name, OPERATOR) \
++static void name(void) \
++{ \
++ unsigned long index, num, pos, val; \
++ index = rpnpop(); \
++ if (!index || index > BITS_IN_UL) { \
++ gen_ex(EX_INVALID_OPERAND, 3, index); \
++ return; \
++ } \
++ num = rpnpop(); \
++ pos = 1UL << (index - 1); \
++ val = num & pos; \
++ for (; pos; pos OPERATOR 1, val OPERATOR 1) \
++ num &= ~pos, num |= val; \
++ rpnpush(num); \
++}
++
++PROPAGATE_BIT(pbl, <<=)
++PROPAGATE_BIT(pbr, >>=)
++
++#define PROPAGATE_BIT_IMM(name, OPERATOR) \
++static void name(void) \
++{ \
++ unsigned long index, num, pos, val; \
++ index = (unsigned long)get_u8_oprnd(); \
++ if (!index || index > BITS_IN_UL) { \
++ dprobes.status &= ~DP_STATUS_INTERPRETER; \
++ return; \
++ } \
++ num = rpnpop(); \
++ pos = 1UL << (index - 1); \
++ val = num & pos; \
++ for (; pos; pos OPERATOR 1, val OPERATOR 1) \
++ num &= ~pos, num |= val; \
++ rpnpush(num); \
++}
++
++PROPAGATE_BIT_IMM(pbl_i, <<=)
++PROPAGATE_BIT_IMM(pbr_i, >>=)
++
++static void pzl(void)
++{
++ unsigned long index, num, pos;
++ index = rpnpop();
++ if (!index || index > BITS_IN_UL) {
++ gen_ex(EX_INVALID_OPERAND, 3, index); \
++ return;
++ }
++ num = rpnpop();
++ pos = 1UL << index;
++ for (; pos; pos <<= 1)
++ num &= ~pos;
++ rpnpush(num);
++}
++
++static void pzl_i(void)
++{
++ unsigned long index, num, pos;
++ index = (unsigned long)get_u8_oprnd();
++ if (!index || index > BITS_IN_UL) {
++ gen_ex(EX_INVALID_OPERAND, 3, index);
++ return;
++ }
++ num = rpnpop();
++ pos = 1UL << index;
++ for (; pos; pos <<= 1)
++ num &= ~pos;
++ rpnpush(num);
++}
++
++static void pzr(void)
++{
++ unsigned long index, num, pos;
++ index = rpnpop();
++ if (!index || index > BITS_IN_UL) {
++ gen_ex(EX_INVALID_OPERAND, 3, index);
++ return;
++ }
++ if (index == 1)
++ return;
++ num = rpnpop();
++ pos = 1UL << (index - 2);
++ for (; pos; pos >>= 1)
++ num &= ~pos;
++ rpnpush(num);
++}
++
++static void pzr_i(void)
++{
++ unsigned long index, num, pos;
++ index = (unsigned long)get_u8_oprnd();
++ if (!index || index > BITS_IN_UL) {
++ gen_ex(EX_INVALID_OPERAND, 3, index);
++ return;
++ }
++ if (index == 1)
++ return;
++ num = rpnpop();
++ pos = 1UL << (index - 2);
++ for (; pos; pos >>= 1)
++ num &= ~pos;
++ rpnpush(num);
++}
++
++static void ror_i(void)
++{
++ byte_t count = get_u8_oprnd();
++ __asm__ __volatile__("rorl %%cl, %0"
++ :"=m"(dprobes.rpn_stack[dprobes.rpn_tos])
++ :"c"((unsigned long) count));
++}
++
++static void rol_i(void)
++{
++ byte_t count = get_u8_oprnd();
++ __asm__ __volatile__("roll %%cl, %0"
++ :"=m"(dprobes.rpn_stack[dprobes.rpn_tos])
++ :"c"((unsigned long) count));
++}
++
++static void ror(void)
++{
++ unsigned long oprnd = rpnpop();
++ unsigned long count = rpnpop();
++ __asm__ __volatile__("rorl %%cl, %0" : "=m"(oprnd)
++ :"c"(count) );
++ rpnpush(oprnd);
++}
++
++static void rol(void)
++{
++ unsigned long oprnd = rpnpop();
++ unsigned long count = rpnpop();
++ __asm__ __volatile__("roll %%cl, %0" : "=m"(oprnd)
++ :"c"(count));
++ rpnpush(oprnd);
++}
++
++/*
++ * IO group
++ */
++static void push_io_u8(void)
++{
++ rpnpush(inb(rpnpop()));
++}
++static void push_io_u16(void)
++{
++ rpnpush(inw(rpnpop()));
++}
++
++static void push_io_u32(void)
++{
++ rpnpush(inl(rpnpop()));
++}
++
++static void pop_io_u8(void)
++{
++ u8 b = (u8)rpnpop();
++ outb(b, rpnpop());
++}
++
++static void pop_io_u16(void)
++{
++ u16 w = (u16)rpnpop();
++ outw(w, rpnpop());
++}
++
++static void pop_io_u32(void)
++{
++ u32 d = (u32)rpnpop();
++ outl(d, rpnpop());
++}
++
++/*
++ * Register push and pop, current and user registers.
++ */
++
++/*
++ * y -- allowed and implemented, n -- not allowed and not implemented.
++ *
++ * Index Register push u push r pop u pop r
++ *
++ * 0 cs y y n n
++ * 1 ds y y y y
++ * 2 es y y y y
++ * 3 fs y y y y
++ * 4 gs y y y y
++ * 5 ss y y n n
++ * 6 eax y y y y
++ * 7 ebx y y y y
++ * 8 ecx y y y y
++ * 9 edx y y y y
++ * a edi y y y y
++ * b esi y y y y
++ * c eflags y y n n
++ * d eip y y n n
++ * e esp y y n n
++ * f ebp y y y y
++ * 20 tr n y n n
++ * 21 ldtr n y n n
++ * 22 gdtr n y n n
++ * 23 idtr n y n n
++ * 24 cr0 n y n n
++ * 25 cr1 RESERVED
++ * 26 cr2 n y n n
++ * 27 cr3 n y n n
++ * 28 cr4 n y n n
++ * 29 cr5 RESERVED
++ * 2a cr6 RESERVED
++ * 2b cr7 RESERVED
++ * 2c dr0 n y n n
++ * 2d dr1 n y n n
++ * 2e dr2 n y n n
++ * 2f dr3 n y n n
++ * 30 dr4 RESERVED
++ * 31 dr5 RESERVED
++ * 32 dr6 n y n n
++ * 33 dr7 n y n n
++ * 34 tr0 RESERVED
++ * 35 tr1 RESERVED
++ * 36 tr2 RESERVED
++ * 37 tr3 RESERVED
++ * 38 tr4 RESERVED
++ * 39 tr5 RESERVED
++ * 3a tr6 RESERVED
++ * 3b tr7 RESERVED
++ * 3c cpuid y y n n
++ * 3d msr y y n n
++ * 3e fr0 y n n n
++ * 3f fr1 y n n n
++ * 40 fr2 y n n n
++ * 41 fr3 y n n n
++ * 42 fr4 y n n n
++ * 43 fr5 y n n n
++ * 44 fr6 y n n n
++ * 45 fr7 y n n n
++ * 46 fcw y n n n
++ * 47 fsw y n n n
++ * 48 ftw y n n n
++ * 49 fip y n n n
++ * 4a fcs y n n n
++ * 4b fdp y n n n
++ * 4c fds y n n n
++ * 4d xmm0 y n n n
++ * 4e xmm1 y n n n
++ * 4f xmm2 y n n n
++ * 50 xmm3 y n n n
++ * 51 xmm4 y n n n
++ * 52 xmm5 y n n n
++ * 53 xmm6 y n n n
++ * 54 xmm7 y n n n
++ * 55 mxcsr y n n n
++ */
++
++static void dp_save_fpu(void)
++{
++ dprobes.status &= ~DP_STATUS_FIRSTFPU;
++ if (HAVE_HWFP) {
++ if (!current->used_math) {
++ memset(&dprobes.fpu_save_area,0, sizeof(dprobes.fpu_save_area));
++ return;
++ }
++
++ if ((read_cr0()) & CR0_TS) {
++ dprobes.fpu_save_area = current->thread.i387;
++ }
++ else {
++ if (cpu_has_fxsr) {
++ __asm__ __volatile__ (
++ "fxsave %0\n"
++ "fxrstor %0" :"=m" (dprobes.fpu_save_area));
++ } else {
++ __asm__ __volatile__ (
++ "fsave %0\n"
++ "frstor %0" :"=m" (dprobes.fpu_save_area));
++
++ }
++ }
++ }
++ else
++ memset(&dprobes.fpu_save_area,0, sizeof(dprobes.fpu_save_area));
++
++}
++
++/*
++ * Define Floating Point Control Registers' push functions.
++ */
++
++#define DEFINE_PUSHFP(REG) \
++static void pushfp_##REG(void) \
++{ \
++ if (dprobes.status & DP_STATUS_FIRSTFPU) \
++ dp_save_fpu(); \
++ if (cpu_has_fxsr) \
++ rpnpush(dprobes.fpu_save_area.fxsave.REG); \
++ else \
++ rpnpush(dprobes.fpu_save_area.fsave.REG); \
++}
++
++DEFINE_PUSHFP(cwd)
++DEFINE_PUSHFP(swd)
++DEFINE_PUSHFP(twd)
++DEFINE_PUSHFP(fip)
++DEFINE_PUSHFP(fcs)
++DEFINE_PUSHFP(foo)
++DEFINE_PUSHFP(fos)
++
++static void pushfp_mxcsr(void)
++{
++ if (dprobes.status & DP_STATUS_FIRSTFPU)
++ dp_save_fpu();
++ if (cpu_has_xmm)
++ rpnpush(dprobes.fpu_save_area.fxsave.mxcsr);
++ else
++ rpnpush(0); /* could we return 0x1f80 ? */
++}
++
++/*
++ * Floating Point Data Registers' push function.
++ */
++static void pushfp_st(int offset)
++{
++ unsigned long st_0, st_4, st_8;
++ if (dprobes.status & DP_STATUS_FIRSTFPU)
++ dp_save_fpu();
++ if (cpu_has_fxsr) {
++ offset = (offset - 0x3e) * 16;
++ st_0 = *(unsigned long *) ((unsigned char *)
++ (dprobes.fpu_save_area.fxsave.st_space) + offset);
++ st_4 = *(unsigned long *) ((unsigned char *)
++ (dprobes.fpu_save_area.fxsave.st_space) + offset + 4);
++ st_8 = *(unsigned long *) ((unsigned char *)
++ (dprobes.fpu_save_area.fxsave.st_space) + offset + 8);
++ }
++ else {
++ offset = (offset - 0x3e) * 10;
++ st_0 = *(unsigned long *) ((unsigned char *)
++ (dprobes.fpu_save_area.fsave.st_space) + offset);
++ st_4 = *(unsigned long *) ((unsigned char *)
++ (dprobes.fpu_save_area.fsave.st_space) + offset + 4);
++ st_8 = *(unsigned short *) ((unsigned char *)
++ (dprobes.fpu_save_area.fsave.st_space) + offset + 8);
++ }
++ rpnpush(st_8);
++ rpnpush(st_4);
++ rpnpush(st_0);
++}
++
++static void push_xmm(int offset)
++{
++ unsigned long xmm_0 = 0, xmm_4 = 0, xmm_8 = 0, xmm_12 = 0;
++ if (dprobes.status & DP_STATUS_FIRSTFPU)
++ dp_save_fpu();
++ if (cpu_has_xmm) {
++ offset = (offset - 0x4d) * 16;
++ xmm_0 = *(unsigned long *) ((unsigned char *)
++ (dprobes.fpu_save_area.fxsave.xmm_space) + offset);
++ xmm_4 = *(unsigned long *) ((unsigned char *)
++ (dprobes.fpu_save_area.fxsave.xmm_space) + offset + 4);
++ xmm_8 = *(unsigned long *) ((unsigned char *)
++ (dprobes.fpu_save_area.fxsave.xmm_space) + offset + 8);
++ xmm_12 = *(unsigned long *) ((unsigned char *)
++ (dprobes.fpu_save_area.fxsave.xmm_space) + offset + 12);
++ }
++ rpnpush(xmm_12);
++ rpnpush(xmm_8);
++ rpnpush(xmm_4);
++ rpnpush(xmm_0);
++}
++
++/*
++ * push r, ss.
++ */
++static unsigned long getr_ss(void)
++{
++ unsigned long reg;
++ if (dprobes.status & DP_KERNEL_PROBE)
++ __asm__ __volatile__("movw %%ss, %%ax\n\t"
++ :"=a"(reg)
++ );
++ else
++ reg = dprobes.regs->xss;
++ return reg;
++}
++
++/*
++ * push r, esp
++ */
++static unsigned long getr_esp(void)
++{
++ if (dprobes.status & DP_KERNEL_PROBE)
++ return (unsigned long)&dprobes.regs->esp;
++ else
++ return dprobes.regs->esp;
++}
++
++/*
++ * Push GDTR
++ */
++static void push_gdtr(void)
++{
++ struct Xgt_desc_struct gdtr;
++ __asm__ __volatile__("sgdt (%0)" : : "r"((unsigned long) &gdtr));
++
++ rpnpush(gdtr.address);
++ rpnpush((unsigned long) gdtr.size);
++}
++
++/*
++ * Push LDTR
++ */
++static void push_ldtr(void)
++{
++ unsigned long ldtr = 0;
++ __asm__ __volatile__("sldt %0" : "=r"(ldtr));
++ rpnpush(ldtr);
++}
++
++/*
++ * Push IDTR
++ */
++static void push_idtr(void)
++{
++ struct Xgt_desc_struct idtr;
++ __asm__ __volatile__("sidt (%0)" : : "r"((unsigned long) &idtr));
++
++ rpnpush(idtr.address);
++ rpnpush((unsigned long)idtr.size);
++}
++
++/*
++ * Push TR
++ */
++static void push_tr(void)
++{
++ unsigned long tr;
++ __asm__ __volatile__("str %%ax" : "=a"(tr));
++ rpnpush(tr);
++}
++
++/*
++ * Push cpuid.
++ */
++static void push_cpuid(void)
++{
++ int op, cpuid_eax = 0, cpuid_ebx = 0, cpuid_ecx = 0, cpuid_edx = 0;
++ op = (int) rpnpop();
++ if ((boot_cpu_data.x86 >= 5) && (op <= boot_cpu_data.cpuid_level))
++ cpuid(op, &cpuid_eax, &cpuid_ebx, &cpuid_ecx, &cpuid_edx);
++ rpnpush(cpuid_edx);
++ rpnpush(cpuid_ecx);
++ rpnpush(cpuid_ebx);
++ rpnpush(cpuid_eax);
++}
++
++/*
++ * push msr.
++ */
++static void push_msr(void)
++{
++ unsigned long msr, val1 =0, val2 = 0;
++ msr = rpnpop();
++ if (boot_cpu_data.x86 >= 5)
++ __asm__ __volatile__ (
++ "0: rdmsr\n"
++ "1:\n"
++ ".section .fixup,\"ax\"\n"
++ "2: jmp 1b\n"
++ ".previous\n"
++ ".section __ex_table,\"a\"\n"
++ " .align 4\n"
++ " .long 0b,2b\n"
++ ".previous"
++ :"=a"(val1), "=d"(val2)
++ :"c"(msr));
++ rpnpush(val2);
++ rpnpush(val1);
++}
++
++/*
++ * Prototype for control registers'(CR0 TO CR4) and debug registers'(DR0 TO DR7)
++ * push functions.
++ */
++#define DEFINE_PUSHCRDB(REG) \
++static void push_##REG(void) \
++{ \
++ unsigned long reg; \
++ __asm__ __volatile__ ("movl %%" #REG ", %0" : "=r"(reg)); \
++ rpnpush(reg); \
++}
++
++/*
++ * Define control and debug registers' push functions.
++ */
++DEFINE_PUSHCRDB(cr0)
++DEFINE_PUSHCRDB(cr2)
++DEFINE_PUSHCRDB(cr3)
++DEFINE_PUSHCRDB(cr4)
++DEFINE_PUSHCRDB(dr0)
++DEFINE_PUSHCRDB(dr1)
++DEFINE_PUSHCRDB(dr2)
++DEFINE_PUSHCRDB(dr3)
++DEFINE_PUSHCRDB(dr6)
++DEFINE_PUSHCRDB(dr7)
++
++/*
++ * Since fs and gs are not in pt_regs, these
++ * are directly taken from the processor.
++ */
++static unsigned long read_fs(void)
++{
++ unsigned long val;
++ __asm__ __volatile__("movw %%fs, %%ax" :"=a"(val));
++ return val;
++}
++
++static unsigned long read_gs(void)
++{
++ unsigned long val;
++ __asm__ __volatile__("movw %%gs, %%ax" :"=a"(val));
++ return val;
++}
++
++static void write_fs(unsigned long val)
++{
++ __asm__ __volatile__("movw %%ax, %%fs" : : "a"(val));
++}
++
++static void write_gs(unsigned long val)
++{
++ __asm__ __volatile__("movw %%ax, %%gs" : :"a"(val));
++}
++
++/*
++ * Current register main push function.
++ */
++static void pushr(void)
++{
++ struct dprobes_struct *dp = &dprobes;
++ unsigned short index = get_u16_oprnd();
++ unsigned long val;
++ switch(index) {
++ case DP_CS: val = dp->regs->xcs; break;
++ case DP_DS: val = dp->regs->xds; break;
++ case DP_ES: val = dp->regs->xes; break;
++ case DP_FS: val = read_fs(); break;
++ case DP_GS: val = read_gs(); break;
++ case DP_SS: val = getr_ss(); break;
++ case DP_EAX: val = dp->regs->eax; break;
++ case DP_EBX: val = dp->regs->ebx; break;
++ case DP_ECX: val = dp->regs->ecx; break;
++ case DP_EDX: val = dp->regs->edx; break;
++ case DP_EDI: val = dp->regs->edi; break;
++ case DP_ESI: val = dp->regs->esi; break;
++ case DP_EFLAGS: val = dp->regs->eflags; break;
++ case DP_EIP: val = dp->regs->eip; break;
++ case DP_ESP: val = getr_esp(); break;
++ case DP_EBP: val = dp->regs->ebp; break;
++
++ /* special registers */
++ case DP_TR: push_tr(); return;
++ case DP_LDTR: push_ldtr(); return;
++ case DP_GDTR: push_gdtr(); return;
++ case DP_IDTR: push_idtr(); return;
++ case DP_CR0: push_cr0(); return;
++ case DP_CR2: push_cr2(); return;
++ case DP_CR3: push_cr3(); return;
++ case DP_CR4: push_cr4(); return;
++ case DP_DR0: push_dr0(); return;
++ case DP_DR1: push_dr1(); return;
++ case DP_DR2: push_dr2(); return;
++ case DP_DR3: push_dr3(); return;
++ case DP_DR6: push_dr6(); return;
++ case DP_DR7: push_dr7(); return;
++ case DP_CPUID: push_cpuid(); return;
++ case DP_MSR: push_msr(); return;
++ default: val = 0; break;
++ }
++ rpnpush(val);
++ return;
++}
++
++/*
++ * User register main push function.
++ */
++static void pushu(void)
++{
++ struct dprobes_struct *dp = &dprobes;
++ unsigned short index = get_u16_oprnd();
++ unsigned long val;
++ switch(index) {
++ case DP_CS: val = dp->uregs->xcs; break;
++ case DP_DS: val = dp->uregs->xds; break;
++ case DP_ES: val = dp->uregs->xes; break;
++ case DP_FS: val = read_fs(); break;
++ case DP_GS: val = read_gs(); break;
++ case DP_SS: val = dp->uregs->xss; break;
++ case DP_EAX: val = dp->uregs->eax; break;
++ case DP_EBX: val = dp->uregs->ebx; break;
++ case DP_ECX: val = dp->uregs->ecx; break;
++ case DP_EDX: val = dp->uregs->edx; break;
++ case DP_EDI: val = dp->uregs->edi; break;
++ case DP_ESI: val = dp->uregs->esi; break;
++ case DP_EFLAGS: val = dp->uregs->eflags; break;
++ case DP_EIP: val = dp->uregs->eip; break;
++ case DP_ESP: val = dp->uregs->esp; break;
++ case DP_EBP: val = dp->uregs->ebp; break;
++ case DP_CPUID: push_cpuid(); return;
++ case DP_MSR: push_msr(); return;
++ case DP_FR0: pushfp_st(0x3e); return;
++ case DP_FR1: pushfp_st(0x3f); return;
++ case DP_FR2: pushfp_st(0x40); return;
++ case DP_FR3: pushfp_st(0x41); return;
++ case DP_FR4: pushfp_st(0x42); return;
++ case DP_FR5: pushfp_st(0x43); return;
++ case DP_FR6: pushfp_st(0x44); return;
++ case DP_FR7: pushfp_st(0x45); return;
++ case DP_FCW: pushfp_cwd(); return;
++ case DP_FSW: pushfp_swd(); return;
++ case DP_FTW: pushfp_twd(); return;
++ case DP_FIP: pushfp_fip(); return;
++ case DP_FCS: pushfp_fcs(); return;
++ case DP_FDP: pushfp_foo(); return;
++ case DP_FDS: pushfp_fos(); return;
++
++ case DP_XMM0: push_xmm(0x4d); return;
++ case DP_XMM1: push_xmm(0x4e); return;
++ case DP_XMM2: push_xmm(0x4f); return;
++ case DP_XMM3: push_xmm(0x50); return;
++ case DP_XMM4: push_xmm(0x51); return;
++ case DP_XMM5: push_xmm(0x52); return;
++ case DP_XMM6: push_xmm(0x53); return;
++ case DP_XMM7: push_xmm(0x54); return;
++ case DP_MXCSR: pushfp_mxcsr(); return;
++
++ default: val = 0; break;
++ }
++ rpnpush(val);
++ return;
++}
++
++/*
++ * Current register main pop function.
++ */
++static void popr(void)
++{
++ struct dprobes_struct *dp = &dprobes;
++ unsigned short index = get_u16_oprnd();
++ unsigned long val = rpnpop();
++ switch(index) {
++ case DP_DS: dp->regs->xds = val; break;
++ case DP_ES: dp->regs->xes = val; break;
++ case DP_FS: write_fs(val); break;
++ case DP_GS: write_gs(val); break;
++ case DP_EAX: dp->regs->eax = val; break;
++ case DP_EBX: dp->regs->ebx = val; break;
++ case DP_ECX: dp->regs->ecx = val; break;
++ case DP_EDX: dp->regs->edx = val; break;
++ case DP_EDI: dp->regs->edi = val; break;
++ case DP_ESI: dp->regs->esi = val; break;
++ case DP_EBP: dp->regs->ebp = val; break;
++ default: break;
++ }
++}
++
++/*
++ * User register main pop function.
++ */
++static void popu(void)
++{
++ struct dprobes_struct *dp = &dprobes;
++ unsigned short index = get_u16_oprnd();
++ unsigned long val = rpnpop();
++ switch(index) {
++ case DP_DS: dp->uregs->xds = val; break;
++ case DP_ES: dp->uregs->xes = val; break;
++ case DP_FS: write_fs(val); break;
++ case DP_GS: write_gs(val); break;
++ case DP_EAX: dp->uregs->eax = val; break;
++ case DP_EBX: dp->uregs->ebx = val; break;
++ case DP_ECX: dp->uregs->ecx = val; break;
++ case DP_EDX: dp->uregs->edx = val; break;
++ case DP_EDI: dp->uregs->edi = val; break;
++ case DP_ESI: dp->uregs->esi = val; break;
++ case DP_EBP: dp->uregs->ebp = val; break;
++ default: break;
++ }
++}
++
++/*
++ * Entry point for the dprobes interpreter(Probe handler).
++ */
++static void dp_asm_interpreter(byte_t rpn_instr)
++{
++ struct dprobes_struct *dp = &dprobes;
++
++ switch(rpn_instr) {
++
++ case DP_SEG2LIN: seg2lin(); break;
++
++ case DP_PUSH_IO_U8: push_io_u8(); break;
++ case DP_PUSH_IO_U16: push_io_u16(); break;
++ case DP_PUSH_IO_U32: push_io_u32(); break;
++ case DP_POP_IO_U8: pop_io_u8(); break;
++ case DP_POP_IO_U16: pop_io_u16(); break;
++ case DP_POP_IO_U32: pop_io_u32(); break;
++
++ default: gen_ex(EX_INVALID_OPCODE, *(dp->rpn_ip - 1),
++ (unsigned long)(dp->rpn_ip -dp->rpn_code - 1)); break; }
++ return;
++}
+diff -urNp linux-2.6.0-test9/drivers/dprobes/Makefile linux-2.6.0-test9+kp/drivers/dprobes/Makefile
+--- linux-2.6.0-test9/drivers/dprobes/Makefile 1970-01-01 05:30:00.000000000 +0530
++++ linux-2.6.0-test9+kp/drivers/dprobes/Makefile 2003-11-18 07:29:47.000000000 +0530
+@@ -0,0 +1,13 @@
++#
++# Makefile for IBM Dynamic Probes
++#
++
++# Multipart objects.
++dp-objs := dprobes.o dprobes_in.o
++
++# Optional parts of multipart objects.
++ifeq ($(CONFIG_X86),y)
++ dp-objs += i386/dprobes.o
++endif
++
++obj-$(CONFIG_DPROBES) += dp.o
+diff -urNp linux-2.6.0-test9/drivers/Makefile linux-2.6.0-test9+kp/drivers/Makefile
+--- linux-2.6.0-test9/drivers/Makefile 2003-10-26 00:14:30.000000000 +0530
++++ linux-2.6.0-test9+kp/drivers/Makefile 2003-11-18 07:29:47.000000000 +0530
+@@ -49,3 +49,4 @@ obj-$(CONFIG_ISDN_BOOL) += isdn/
+ obj-$(CONFIG_MCA) += mca/
+ obj-$(CONFIG_EISA) += eisa/
+ obj-$(CONFIG_CPU_FREQ) += cpufreq/
++obj-$(CONFIG_DPROBES) += dprobes/
+diff -urNp linux-2.6.0-test9/fs/exec.c linux-2.6.0-test9+kp/fs/exec.c
+--- linux-2.6.0-test9/fs/exec.c 2003-10-26 00:13:25.000000000 +0530
++++ linux-2.6.0-test9+kp/fs/exec.c 2003-11-18 07:29:47.000000000 +0530
+@@ -1391,3 +1391,4 @@ fail:
+ unlock_kernel();
+ return retval;
+ }
++EXPORT_SYMBOL(do_coredump);
+diff -urNp linux-2.6.0-test9/fs/namei.c linux-2.6.0-test9+kp/fs/namei.c
+--- linux-2.6.0-test9/fs/namei.c 2003-10-26 00:13:22.000000000 +0530
++++ linux-2.6.0-test9+kp/fs/namei.c 2003-11-18 07:29:48.000000000 +0530
+@@ -264,6 +264,18 @@ int deny_write_access(struct file * file
+ return 0;
+ }
+
++int deny_write_access_to_inode(struct inode * inode)
++{
++ spin_lock(&arbitration_lock);
++ if (atomic_read(&inode->i_writecount) > 0) {
++ spin_unlock(&arbitration_lock);
++ return -ETXTBSY;
++ }
++ atomic_dec(&inode->i_writecount);
++ spin_unlock(&arbitration_lock);
++ return 0;
++}
++
+ void path_release(struct nameidata *nd)
+ {
+ dput(nd->dentry);
+@@ -2307,3 +2319,8 @@ EXPORT_SYMBOL(vfs_rename);
+ EXPORT_SYMBOL(vfs_rmdir);
+ EXPORT_SYMBOL(vfs_symlink);
+ EXPORT_SYMBOL(vfs_unlink);
++#ifdef CONFIG_DPROBES_MODULE
++extern int deny_write_access_to_inode(struct inode *);
++EXPORT_SYMBOL(deny_write_access_to_inode);
++#endif
++
+diff -urNp linux-2.6.0-test9/include/asm-i386/debugreg.h linux-2.6.0-test9+kp/include/asm-i386/debugreg.h
+--- linux-2.6.0-test9/include/asm-i386/debugreg.h 2003-10-26 00:14:17.000000000 +0530
++++ linux-2.6.0-test9+kp/include/asm-i386/debugreg.h 2003-11-18 07:29:47.000000000 +0530
+@@ -61,4 +61,166 @@
+ #define DR_LOCAL_SLOWDOWN (0x100) /* Local slow the pipeline */
+ #define DR_GLOBAL_SLOWDOWN (0x200) /* Global slow the pipeline */
+
++struct debugreg {
++ unsigned long flag;
++ unsigned long use_count;
++};
++
++/* debugreg flags */
++#define DR_UNUSED 0
++#define DR_LOCAL 1
++#define DR_GLOBAL 2
++
++#define DR_MAX 4
++#define DR_ANY DR_MAX + 1
++
++/* global or local allocation requests */
++#define DR_ALLOC_GLOBAL 0
++#define DR_ALLOC_LOCAL 1
++
++#define DR7_RW_SET(dr, regnum, rw) do { \
++ (dr) &= ~(0x3 << (16 + (4 * (regnum)))); \
++ (dr) |= (((rw) & 0x3) << (16 + (4 * (regnum)))); \
++ } while (0)
++
++#define DR7_RW_VAL(dr, regnum) \
++ (((dr) >> (16 + (4 * (regnum)))) & 0x3)
++
++#define DR7_LEN_SET(dr, regnum, len) do { \
++ (dr) &= ~(0x3 << (18 + (4 * (regnum)))); \
++ (dr) |= (((len-1) & 0x3) << (18 + (4 * (regnum)))); \
++ } while (0)
++
++#define DR7_LEN_VAL(dr, regnum) \
++ (((dr) >> (18 + (4 * (regnum)))) & 0x3)
++
++#define DR7_L0(dr) (((dr))&0x1)
++#define DR7_L1(dr) (((dr)>>2)&0x1)
++#define DR7_L2(dr) (((dr)>>4)&0x1)
++#define DR7_L3(dr) (((dr)>>6)&0x1)
++
++#define DR_IS_LOCAL(dr, num) ((dr) & (1UL << (num <<1)))
++
++/* Set the rw, len and global flag in dr7 for a debug register */
++#define SET_DR7(dr, regnum, access, len) do { \
++ DR7_RW_SET(dr, regnum, access); \
++ DR7_LEN_SET(dr, regnum, len); \
++ dr |= (2UL << regnum*2); \
++ } while (0)
++
++/* Disable a debug register by clearing the global/local flag in dr7 */
++#define RESET_DR7(dr, regnum) dr &= ~(3UL << regnum*2)
++
++#define DR7_DR0_BITS 0x000F0003
++#define DR7_DR1_BITS 0x00F0000C
++#define DR7_DR2_BITS 0x0F000030
++#define DR7_DR3_BITS 0xF00000C0
++
++#define DR_TRAP_MASK 0xF
++
++#define DR_TYPE_EXECUTE 0x0
++#define DR_TYPE_WRITE 0x1
++#define DR_TYPE_IO 0x2
++#define DR_TYPE_RW 0x3
++
++#define get_dr(regnum, val) \
++ __asm__("movl %%db" #regnum ", %0" \
++ :"=r" (val))
++static inline unsigned long read_dr(int regnum)
++{
++ unsigned long val = 0;
++ switch (regnum) {
++ case 0: get_dr(0, val); break;
++ case 1: get_dr(1, val); break;
++ case 2: get_dr(2, val); break;
++ case 3: get_dr(3, val); break;
++ case 6: get_dr(6, val); break;
++ case 7: get_dr(7, val); break;
++ }
++ return val;
++}
++#undef get_dr
++
++#define set_dr(regnum, val) \
++ __asm__("movl %0,%%db" #regnum \
++ : /* no output */ \
++ :"r" (val))
++static inline void write_dr(int regnum, unsigned long val)
++{
++ switch (regnum) {
++ case 0: set_dr(0, val); break;
++ case 1: set_dr(1, val); break;
++ case 2: set_dr(2, val); break;
++ case 3: set_dr(3, val); break;
++ case 7: set_dr(7, val); break;
++ }
++ return;
++}
++#undef set_dr
++
++#ifdef CONFIG_DEBUGREG
++/*
++ * Given the debug status register, returns the debug register number
++ * which caused the debug trap.
++ */
++static inline int dr_trap(unsigned int condition)
++{
++ int i, reg_shift = 1UL;
++ for (i = 0; i < DR_MAX; i++, reg_shift <<= 1)
++ if ((condition & reg_shift))
++ return i;
++ return -1;
++}
++
++/*
++ * Given the debug status register, returns the address due to which
++ * the debug trap occured.
++ */
++static inline unsigned long dr_trap_addr(unsigned int condition)
++{
++ int regnum = dr_trap(condition);
++
++ if (regnum == -1)
++ return -1;
++ return read_dr(regnum);
++}
++
++/*
++ * Given the debug status register, returns the type of debug trap:
++ * execute, read/write, write or io.
++ */
++static inline int dr_trap_type(unsigned int condition)
++{
++ int regnum = dr_trap(condition);
++
++ if (regnum == -1)
++ return -1;
++ return DR7_RW_VAL(read_dr(7), regnum);
++}
++
++/* Function declarations */
++
++extern int dr_alloc(int regnum, int flag);
++extern int dr_free(int regnum);
++extern void dr_inc_use_count(unsigned long mask);
++extern void dr_dec_use_count(unsigned long mask);
++extern struct debugreg dr_list[DR_MAX];
++extern unsigned long dr7_global_mask;
++extern int enable_debugreg(unsigned long old_dr7, unsigned long new_dr7);
++
++static inline void load_process_dr7(unsigned long curr_dr7)
++{
++ write_dr(7, (read_dr(7) & dr7_global_mask) | curr_dr7);
++}
++#else
++static inline int enable_debugreg(unsigned long old_dr7, unsigned long new_dr7) { return 0; }
++static inline void load_process_dr7(unsigned long curr_dr7)
++{
++ write_dr(7, curr_dr7);
++}
++
++static void dr_inc_use_count(unsigned long mask) { }
++static void dr_dec_use_count(unsigned long mask) { }
++
++#endif /* CONFIG_DEBUGREG */
+ #endif
+diff -urNp linux-2.6.0-test9/include/asm-i386/dprobes_exclude.h linux-2.6.0-test9+kp/include/asm-i386/dprobes_exclude.h
+--- linux-2.6.0-test9/include/asm-i386/dprobes_exclude.h 1970-01-01 05:30:00.000000000 +0530
++++ linux-2.6.0-test9+kp/include/asm-i386/dprobes_exclude.h 2003-11-18 07:30:12.000000000 +0530
+@@ -0,0 +1,84 @@
++#ifndef __ASM_I386_DPROBES_EXCLUDE_H__
++#define __ASM_I386_DPROBES_EXCLUDE_H__
++
++/*
++ * IBM Dynamic Probes
++ * Copyright (c) International Business Machines Corp., 2000
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ */
++
++/*
++ * Exclude regions markers: We do not allow probes to be placed in these
++ * code areas to _prevent_ recursion into dp_trap3. Note that this does
++ * not eliminate the possibility of recursion completely. dp_trap3 handles
++ * recursion by silently and permanently removing the recursed probe point.
++ */
++#include <linux/kprobes.h>
++#include <asm/kwatch.h>
++
++extern void kprobes_code_start(void);
++extern void kprobes_code_end(void);
++extern void kprobes_asm_code_start(void);
++extern void kprobes_asm_code_end(void);
++extern void dprobes_code_start(void);
++extern void dprobes_code_end(void);
++extern void dprobes_asm_code_start(void);
++extern void dprobes_asm_code_end(void);
++extern void dprobes_interpreter_code_start(void);
++extern void dprobes_interpreter_code_end(void);
++
++#ifdef CONFIG_DR_ALLOC
++extern void kwatch_asm_start(void);
++extern void kwatch_asm_end(void);
++#endif
++
++struct region {
++ unsigned long start;
++ unsigned long end;
++};
++
++#define NR_EXCLUDED_REGIONS 6
++static struct region exclude[] = {
++#ifdef CONFIG_DR_ALLOC
++ {(unsigned long)kwatch_asm_start,
++ (unsigned long)kwatch_asm_end},
++#else
++ {0, 0},
++#endif
++ {(unsigned long)kprobes_code_start,
++ (unsigned long)kprobes_code_end},
++ {(unsigned long)kprobes_asm_code_start,
++ (unsigned long)kprobes_asm_code_end},
++ {(unsigned long)dprobes_code_start,
++ (unsigned long)dprobes_code_end},
++ {(unsigned long)dprobes_asm_code_start,
++ (unsigned long)dprobes_asm_code_end},
++ {(unsigned long)dprobes_interpreter_code_start,
++ (unsigned long)dprobes_interpreter_code_end}
++};
++
++static inline int dprobes_excluded(unsigned long addr)
++{
++ int i;
++ for (i = 0; i < NR_EXCLUDED_REGIONS; i++) {
++ if (addr >= exclude[i].start && addr <= exclude[i].end) {
++ return 1;
++ }
++ }
++ return 0;
++}
++
++#endif /* __ASM_I386_DPROBES_EXCLUDE_H__ */
+diff -urNp linux-2.6.0-test9/include/asm-i386/dprobes.h linux-2.6.0-test9+kp/include/asm-i386/dprobes.h
+--- linux-2.6.0-test9/include/asm-i386/dprobes.h 1970-01-01 05:30:00.000000000 +0530
++++ linux-2.6.0-test9+kp/include/asm-i386/dprobes.h 2003-11-18 07:29:48.000000000 +0530
+@@ -0,0 +1,202 @@
++#ifndef __ASM_I386_DPROBES_H__
++#define __ASM_I386_DPROBES_H__
++
++/*
++ * IBM Dynamic Probes
++ * Copyright (c) International Business Machines Corp., 2000
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ */
++
++/*
++ * RPN stack width will always be equal to the machine register width.
++ */
++#define RPN_STACK_SIZE 1024
++#define CALL_FRAME_SIZE 10
++#define NR_NESTED_CALLS 32
++/* 1st is a dummy frame, not used by call instruction */
++#define CALL_STACK_SIZE (NR_NESTED_CALLS+1)*CALL_FRAME_SIZE
++#define FMT_LOG_HDR_SIZE 256
++
++/* Offsets of different items on call stack */
++#define OFFSET_SBP 0
++#define OFFSET_GV_RANGE 1
++#define OFFSET_LV_RANGE 3
++#define OFFSET_RPN_STACK_RANGE 5
++#define OFFSET_EX_HANDLER 7
++#define OFFSET_CALLED_ADDR 8
++#define OFFSET_RETURN_ADDR 9
++
++/*
++ * offsets of different repitition entries (3 byte prefix) in the
++ * exception stacktrace buffer.
++ */
++struct ex_buffer_offset {
++ unsigned long st; /* ex stack traces */
++ unsigned long sf; /* ex stack frames */
++ unsigned long rpn; /* rpn stack entries */
++ unsigned long lv; /* lv entries */
++ unsigned long gv; /* gv entries */
++};
++
++/* Token bytes of all available 3 byte prefixes */
++#define TOKEN_SEG_FAULT -2
++#define TOKEN_MEMORY_FAULT -1
++#define TOKEN_MEMORY_LOG 0
++#define TOKEN_ASCII_LOG 1
++#define TOKEN_STACK_TRACE 2
++#define TOKEN_STACK_FRAME 3
++#define TOKEN_RPN_ENTRY 4
++#define TOKEN_LV_ENTRY 5
++#define TOKEN_GV_ENTRY 6
++#define TOKEN_LOG 7
++
++#define PREFIX_SIZE 3
++
++struct dprobes_struct {
++ unsigned long status; /* status */
++ /*
++ * details about the probe that is hit, kept here for quick access at
++ * trap1 time.
++ */
++ unsigned long probe_addr;
++ struct pt_regs *regs;
++ struct pt_regs *uregs;
++ struct dp_module_struct *mod;
++ struct dp_record_struct *rec;
++
++ /* fpu registers are saved here */
++ union i387_union fpu_save_area;
++
++ /*
++ * Optional data to store with the log record. This data is collected
++ * in the interpreter and used in the trap1 handler when writing the
++ * log to the specified log target.
++ */
++ unsigned long eip;
++ unsigned short cs;
++ unsigned long esp;
++ unsigned short ss;
++ unsigned short major, minor;
++
++ /*
++ * per-processor data used by the interpreter.
++ */
++ unsigned long rpn_tos, call_tos, log_len, prev_log_len, ex_log_len;
++ byte_t * rpn_code;
++ byte_t * rpn_ip;
++ unsigned short jmp_count;
++ byte_t opcode;
++ byte_t reserved;
++ byte_t ex_pending;
++ unsigned long ex_code;
++ unsigned long ex_parm1, ex_parm2, ex_hand;
++ struct ex_buffer_offset ex_off;
++ unsigned long rpn_sbp;
++ unsigned long heap_size;
++
++ /*
++ * locks used to handle recursion.
++ */
++ spinlock_t lock;
++ long sem;
++
++ unsigned long rpn_stack[RPN_STACK_SIZE];
++ unsigned long call_stack[CALL_STACK_SIZE];
++ unsigned char log[LOG_SIZE];
++ unsigned char ex_log[EX_LOG_SIZE];
++ unsigned char log_hdr[FMT_LOG_HDR_SIZE];
++};
++
++/*
++ * status
++ *
++ * DP_KENREL_PROBE: When this is set, we will not use the pte* fields in the
++ * alias structure at trap1 time, we can write directly to the probe_addr.
++ *
++ * DP_STATUS_ERROR: Contains all the bits to check for error conditions.
++ *
++ * DP_STATUS_DONE: Contains all the per-probe hit bits that need to be turned
++ * off after a probe handler is executed.
++ *
++ * DP_STATUS_ABORT: Indicates that the designated exit facility should not
++ * be called after the original instruction is successfully single-stepped.
++ */
++#define DP_STATUS_INACTIVE 0x00000000
++#define DP_STATUS_ACTIVE 0x00000001
++
++#define DP_STATUS_ERROR 0x00ff0000
++#define DP_STATUS_GPF 0x00010000
++#define DP_STATUS_PF 0x00020000
++#define DP_STATUS_LOG_OVERFLOW 0x00040000
++
++#define DP_STATUS_INTERPRETER 0x01000000
++#define DP_STATUS_SS 0x02000000
++#define DP_KERNEL_PROBE 0x04000000
++#define DP_USER_PROBE 0x08000000
++#define DP_STATUS_ABORT 0x10000000
++#define DP_STATUS_FIRSTFPU 0x20000000
++#define DP_STATUS_DONE 0xffff0000
++
++#define DP_INSTR_BREAKPOINT 0xcc
++
++/* arch-specific rec flags */
++#define DP_REC_OPCODE_EMULATED 0x01000000
++#define DP_REC_OPCODE_IF_MODIFIER 0x02000000
++
++#ifndef EF_CF
++#define EF_CF 0x00000001
++#endif
++#ifndef EF_TF
++#define EF_TF 0x00000100
++#endif
++#ifndef EF_IE
++#define EF_IE 0x00000200
++#endif
++#ifndef EF_DF
++#define EF_DF 0x00000400
++#endif
++#ifndef EF_RF
++#define EF_RF 0x00010000
++#endif
++
++#define CR0_TS 0x00000008
++
++#ifndef HAVE_HWFP
++#ifdef CONFIG_MATH_EMULATION
++#define HAVE_HWFP (boot_cpu_data.hard_math)
++#else
++#define HAVE_HWFP 1
++#endif
++#endif
++
++/*
++ * Function declarations
++ */
++extern void dp_interpreter(void);
++extern int dp_gpf(struct pt_regs *);
++extern int dp_pf(struct pt_regs *);
++extern int dp_handle_fault(struct pt_regs *);
++extern int dp_do_debug(struct pt_regs *, unsigned long);
++extern int dp_trap1(struct pt_regs *);
++extern int dp_trap(struct pt_regs *, int, unsigned int, unsigned long);
++extern int __remove_probe(byte_t *, struct dp_record_struct *);
++extern int __insert_probe(byte_t *, struct dp_record_struct *, struct dp_module_struct *, struct page *);
++extern int register_userspace_probes(struct dp_record_struct *);
++extern void unregister_userspace_probes(struct dp_record_struct *);
++extern inline int insert_probe_userspace(byte_t *, struct dp_record_struct *, struct page *, struct vm_area_struct *);
++extern inline int remove_probe_userspace(byte_t *, struct dp_record_struct *, struct page *, struct vm_area_struct *);
++
++#endif
+diff -urNp linux-2.6.0-test9/include/asm-i386/dprobes_in.h linux-2.6.0-test9+kp/include/asm-i386/dprobes_in.h
+--- linux-2.6.0-test9/include/asm-i386/dprobes_in.h 1970-01-01 05:30:00.000000000 +0530
++++ linux-2.6.0-test9+kp/include/asm-i386/dprobes_in.h 2003-11-18 07:29:47.000000000 +0530
+@@ -0,0 +1,120 @@
++#ifndef __ASM_I386_DPROBES_IN_H__
++#define __ASM_I386_DPROBES_IN_H__
++
++/*
++ * IBM Dynamic Probes
++ * Copyright (c) International Business Machines Corp., 2000
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ */
++
++/*
++ * This header file defines the opcodes for the RPN instructions
++ *
++ * Opcodes 0x00 to 0xAF are for common, arch-independent instructions.
++ * Opcodes 0xB0 to 0xDF are for arch-dependent instructions.
++ * Opcodes 0xF1 to 0xFF are for two byte instructions.
++ */
++
++/*
++ * Misc
++ */
++#define DP_SEG2LIN 0xB0
++
++/*
++ * IO Group.
++ */
++#define DP_PUSH_IO_U8 0xB1
++#define DP_PUSH_IO_U16 0xB2
++#define DP_PUSH_IO_U32 0xB3
++#define DP_POP_IO_U8 0xB4
++#define DP_POP_IO_U16 0xB5
++#define DP_POP_IO_U32 0xB6
++
++/*
++ * Intel32 Register Assignments.
++ */
++#define DP_CS 0x0000
++#define DP_DS 0x0001
++#define DP_ES 0x0002
++#define DP_FS 0x0003
++#define DP_GS 0x0004
++#define DP_SS 0x0005
++#define DP_EAX 0x0006
++#define DP_EBX 0x0007
++#define DP_ECX 0x0008
++#define DP_EDX 0x0009
++#define DP_EDI 0x000a
++#define DP_ESI 0x000b
++#define DP_EFLAGS 0x000c
++#define DP_EIP 0x000d
++#define DP_ESP 0x000e
++#define DP_EBP 0x000f
++
++#define DP_TR 0x0020
++#define DP_LDTR 0x0021
++#define DP_GDTR 0x0022
++#define DP_IDTR 0x0023
++#define DP_CR0 0x0024
++#define DP_CR1 0x0025
++#define DP_CR2 0x0026
++#define DP_CR3 0x0027
++#define DP_CR4 0x0028
++
++#define DP_DR0 0x002c
++#define DP_DR1 0x002d
++#define DP_DR2 0x002e
++#define DP_DR3 0x002f
++#define DP_DR4 0x0030
++#define DP_DR5 0x0031
++#define DP_DR6 0x0032
++#define DP_DR7 0x0033
++
++#define DP_CPUID 0x003c
++#define DP_MSR 0x003d
++
++#define DP_FR0 0x003e
++#define DP_FR1 0x003f
++#define DP_FR2 0x0040
++#define DP_FR3 0x0041
++#define DP_FR4 0x0042
++#define DP_FR5 0x0043
++#define DP_FR6 0x0044
++#define DP_FR7 0x0045
++#define DP_FCW 0x0046
++#define DP_FSW 0x0047
++#define DP_FTW 0x0048
++#define DP_FIP 0x0049
++#define DP_FCS 0x004a
++#define DP_FDP 0x004b
++#define DP_FDS 0x004c
++
++#define DP_XMM0 0x004d
++#define DP_XMM1 0x004e
++#define DP_XMM2 0x004f
++#define DP_XMM3 0x0050
++#define DP_XMM4 0x0051
++#define DP_XMM5 0x0052
++#define DP_XMM6 0x0053
++#define DP_XMM7 0x0054
++#define DP_MXCSR 0x0055
++
++#ifndef __KERNEL__
++#ifndef PAGE_OFFSET
++#define PAGE_OFFSET 0xc0000000
++#endif
++#endif /* !__KERNEL__ */
++
++#endif
+diff -urNp linux-2.6.0-test9/include/asm-i386/kprobes.h linux-2.6.0-test9+kp/include/asm-i386/kprobes.h
+--- linux-2.6.0-test9/include/asm-i386/kprobes.h 1970-01-01 05:30:00.000000000 +0530
++++ linux-2.6.0-test9+kp/include/asm-i386/kprobes.h 2003-11-18 07:30:12.000000000 +0530
+@@ -0,0 +1,38 @@
++#ifndef _ASM_KPROBES_H
++#define _ASM_KPROBES_H
++/*
++ * Dynamic Probes (kprobes) support
++ * Vamsi Krishna S <vamsi_krishna@in.ibm.com>, July, 2002
++ * Mailing list: dprobes@www-124.ibm.com
++ */
++#include <linux/types.h>
++#include <linux/ptrace.h>
++
++struct pt_regs;
++
++typedef u8 kprobe_opcode_t;
++#define BREAKPOINT_INSTRUCTION 0xcc
++#define MAX_INSN_SIZE 16
++
++/* trap3/1 are intr gates for kprobes. So, restore the status of IF,
++ * if necessary, before executing the original int3/1 (trap) handler.
++ */
++static inline void restore_interrupts(struct pt_regs *regs)
++{
++ if (regs->eflags & IF_MASK)
++ __asm__ __volatile__ ("sti");
++}
++
++void kprobes_asm_code_start(void);
++void kprobes_asm_code_end(void);
++
++#ifdef CONFIG_KPROBES
++extern int kprobe_fault_handler(struct pt_regs *regs, int trapnr);
++extern int post_kprobe_handler(struct pt_regs *regs);
++extern int kprobe_handler(struct pt_regs *regs);
++#else /* !CONFIG_KPROBES */
++static inline int kprobe_fault_handler(struct pt_regs *regs, int trapnr) { return 0; }
++static inline int post_kprobe_handler(struct pt_regs *regs) { return 0; }
++static inline int kprobe_handler(struct pt_regs *regs) { return 0; }
++#endif
++#endif /* _ASM_KPROBES_H */
+diff -urNp linux-2.6.0-test9/include/asm-i386/kwatch.h linux-2.6.0-test9+kp/include/asm-i386/kwatch.h
+--- linux-2.6.0-test9/include/asm-i386/kwatch.h 1970-01-01 05:30:00.000000000 +0530
++++ linux-2.6.0-test9+kp/include/asm-i386/kwatch.h 2003-11-18 07:29:48.000000000 +0530
+@@ -0,0 +1,31 @@
++#ifndef _ASM_KWATCH_H
++#define _ASM_KWATCH_H
++/*
++ * Dynamic Probes (kwatch points) support
++ * Vamsi Krishna S <vamsi_krishna@in.ibm.com>, Oct, 2002
++ */
++#include <linux/types.h>
++#include <linux/ptrace.h>
++
++struct kwatch;
++typedef void (*kwatch_handler_t)(struct kwatch *, struct pt_regs *, int );
++
++struct kwatch {
++ unsigned long addr; /* location of watchpoint */
++ u8 length; /* range of address */
++ u8 type; /* type of watchpoint */
++ kwatch_handler_t handler;
++};
++
++#define RF_MASK 0x00010000
++
++#ifdef CONFIG_KWATCH
++extern int register_kwatch(unsigned long addr, u8 length, u8 type, kwatch_handler_t handler);
++extern void unregister_kwatch(int debugreg);
++extern int kwatch_handler(unsigned long condition, struct pt_regs *regs);
++#else
++static inline int register_kwatch(unsigned long addr, u8 length, u8 type, kwatch_handler_t handler) { return -ENOSYS; }
++static inline void unregister_kwatch(int debugreg) { }
++static inline int kwatch_handler(unsigned long condition, struct pt_regs *regs) { return 0; }
++#endif
++#endif /* _ASM_KWATCH_H */
+diff -urNp linux-2.6.0-test9/include/linux/dprobes.h linux-2.6.0-test9+kp/include/linux/dprobes.h
+--- linux-2.6.0-test9/include/linux/dprobes.h 1970-01-01 05:30:00.000000000 +0530
++++ linux-2.6.0-test9+kp/include/linux/dprobes.h 2003-11-18 07:30:29.000000000 +0530
+@@ -0,0 +1,450 @@
++#ifndef _LINUX_DPROBES_H
++#define _LINUX_DPROBES_H
++
++/*
++ * IBM Dynamic Probes
++ * Copyright (c) International Business Machines Corp., 2000
++ *
++ */
++
++#ifdef __KERNEL__
++#include <linux/types.h>
++#else
++#include <sys/types.h>
++#endif
++
++#define DP_MAJOR_VER 5
++#define DP_MINOR_VER 0
++#define DP_PATCH_VER 0
++
++/* main command codes */
++#define DP_CMD_MASK 0x000000ff
++#define DP_INSERT 0x00000001
++#define DP_REMOVE 0x00000002
++#define DP_GETVARS 0x00000004
++#define DP_QUERY 0x00000008
++#define DP_HELP 0x00000010
++#define DP_VERSION 0x00000020
++#define DP_BUILDPPDF 0x00000040
++#define DP_APPLYPPDF 0x00000080
++
++/* command modifiers */
++#define DP_FLAGS_MASK 0xff000000
++#define DP_ALL 0x80000000
++
++/* insert cmd modifiers */
++#define DP_MERGE 0x01000000
++#define DP_REPLACE 0x02000000
++#define DP_STOP_CPUS 0x04000000
++#define DP_DONT_VERIFY_OPCODES 0x08000000
++#define DP_AUTOSTACKTRACE 0x10000000
++
++/* getvars cmd modifiers */
++#define DP_GETVARS_INDEX 0x01000000
++#define DP_GETVARS_RESET 0x02000000
++#define DP_GETVARS_LOCAL 0x04000000
++#define DP_GETVARS_GLOBAL 0x08000000
++
++/* query cmd modifiers */
++#define DP_QUERY_EXTENDED 0x01000000
++
++/* pgm->flags: default data that will be part of each log record */
++#define DP_LOG_MASK 0x0000ff00
++#define DP_LOG_PROCNAME 0x00000100
++#define DP_LOG_PID 0x00000200
++#define DP_LOG_UID 0x00000400
++#define DP_LOG_NIP 0x00000800
++#define DP_LOG_PSW 0x00000800
++#define DP_LOG_UPSW 0x00001000
++#define DP_LOG_SS_ESP 0x00000800
++#define DP_LOG_CS_EIP 0x00001000
++#define DP_LOG_TSC 0x00002000
++#define DP_LOG_CPU 0x00004000
++#define DP_LOG_ALL 0x0000ff00
++
++/* pgm->flags: default log target */
++#define DP_LOG_TARGET_MASK 0x00ff0000
++#define DP_LOG_TARGET_KLOG 0x00010000
++#define DP_LOG_TARGET_LTT 0x00020000
++#define DP_LOG_TARGET_COM1 0x00040000
++#define DP_LOG_TARGET_COM2 0x00080000
++#define DP_LOG_TARGET_EVL 0x00100000
++#define DP_UNFORMATTED_OUTPUT 0x00200000
++
++/* exit facilities for use with "exit_to" instruction */
++#define DP_EXIT_TO_SGI_KDB 0x01
++#define DP_EXIT_TO_SGI_VMDUMP 0x02
++#define DP_EXIT_TO_CORE_DUMP 0x03
++
++/* bit flags to indicate elements present in the trace buffer header. */
++#define DP_HDR_MAJOR 0x00000001
++#define DP_HDR_MINOR 0x00000002
++#define DP_HDR_CPU 0x00000004
++#define DP_HDR_PID 0x00000008
++#define DP_HDR_UID 0x00000010
++#define DP_HDR_CS 0x00000020
++#define DP_HDR_EIP 0x00000040
++#define DP_HDR_SS 0x00000080
++#define DP_HDR_ESP 0x00000100
++#define DP_HDR_TSC 0x00000200
++#define DP_HDR_PROCNAME 0x00000400
++
++typedef unsigned char byte_t;
++/* FIXME: Well, we're working on getting this in arch headers */
++#if defined(CONFIG_X86) || defined(CONFIG_IA64)
++typedef byte_t opcode_t;
++#endif
++
++/*
++ * This captures the header information of each probe point. It also contains
++ * links to the rpn code of this dprobe handler.
++ *
++ * maxhits: This field indicates the number of times this probe will be
++ * executed before being disabled. A negative value here means that the
++ * probe will not be disabled automatically.
++ *
++ * count: This field keeps track of number of times this probe is hit,
++ * and executed successfully. There is a reason this is "long" and not
++ * "unsigned long". We would implement "pass-count" feature that specifies
++ * the number of times we pass over this probe with actually executing the
++ * probe handler, using the fact that count is a signed value.
++ */
++struct dp_point_struct {
++ loff_t offset; /* file offset or absolute address */
++ unsigned short address_flag;
++ opcode_t opcode;
++ opcode_t actual_opcode; /* in case of opcode mismatch */
++ unsigned short major;
++ unsigned short minor;
++ unsigned short group;
++ unsigned short type;
++ unsigned long rpn_offset; /* offset into rpn_code of dp_pgm_struct */
++ unsigned long rpn_end;
++ unsigned short probe; /* watchpoint info, probe and access types */
++ unsigned long len; /* range of the watchpoint probe */
++ int dbregno; /* the debug reg used for this watchpoint */
++ long maxhits;
++ long passcount;
++ unsigned char logonfault;
++ unsigned short ex_mask;
++ unsigned long heap_size; /* no of heap elements specified by this probe */
++};
++
++#define DP_ADDRESS_ABSOLUTE 0x0001
++
++/*
++ * Flags related to watchpoint probes.
++ */
++#define DP_PROBE_BREAKPOINT 0x8000
++#define DP_PROBE_WATCHPOINT 0x4000
++#define DP_PROBE_MASK 0xc000
++#define DP_MAX_WATCHPOINT 0x4 /* maximum number of watchpoint probes */
++
++#define DP_WATCHTYPE_EXECUTE 0x0000
++#define DP_WATCHTYPE_WRITE 0x0001
++#define DP_WATCHTYPE_IO 0x0002
++#define DP_WATCHTYPE_RDWR 0x0003
++#define DP_WATCHTYPE_MASK 0x0003
++
++/*
++ * This captures the information specified in the dprobe program file header.
++ *
++ * User application passes the details of the pgm header to the system call in
++ * this structure. It is also used in the kernel as part of dp_module_struct.
++ */
++struct dp_pgm_struct {
++ unsigned char *name; /* module for which the probe program is written */
++ unsigned long flags;
++ unsigned short major;
++ unsigned long id;
++ unsigned short jmpmax;
++ unsigned short logmax;
++ unsigned short ex_logmax; /* length of exception stack trace buffer */
++ unsigned short num_lv; /* num of local variables */
++ unsigned short num_gv; /* num of global variables */
++ unsigned long heapmax; /* num of heap elements required
++ by this probe program */
++
++ unsigned short rpn_length; /* rpn code */
++ unsigned char autostacktrace;
++ byte_t *rpn_code;
++
++ unsigned short align; /* kernel module alignment */
++ unsigned short num_points;
++ struct dp_point_struct *point;
++};
++
++/* pgm flags */
++#define DP_MODTYPE_MASK 0x000f
++#define DP_MODTYPE_USER 0x0001
++#define DP_MODTYPE_KERNEL 0x0002
++#define DP_MODTYPE_KMOD 0x0004
++
++#define LOG_SIZE 1024
++#define EX_LOG_SIZE 1024
++#define JMP_MAX 65535
++#define MAX_MAXHITS 0x7fffffff
++#define HEAPMAX 16*1024
++
++#define DEFAULT_LOGMAX LOG_SIZE
++#define DEFAULT_EX_LOG_MAX EX_LOG_SIZE
++#define DEFAULT_JMPMAX JMP_MAX
++#define DEFAULT_MAXHITS MAX_MAXHITS
++#define DEFAULT_HEAPMAX HEAPMAX
++
++/* compiled in limits */
++#define DP_MAX_LVARS 256
++#define DP_MAX_GVARS 256
++
++/* exception codes */
++#define EX_INVALID_ADDR 0x0001
++#define EX_SEG_FAULT 0x0002
++#define EX_MAX_JMPS 0x0004
++#define EX_CALL_STACK_OVERFLOW 0x0010
++#define EX_DIV_BY_ZERO 0x0020
++#define EX_INVALID_OPERAND 0x0040
++#define EX_INVALID_OPCODE 0x0080
++#define EX_HEAP_NOMEM 0x0100
++#define EX_HEAP_INVALID_HANDLE 0x0200
++#define EX_HEAP_INVALID_OFFSET 0x0400
++#define EX_LOG_OVERFLOW 0x1000
++#define EX_RPN_STACK_WRAP 0x2000
++#define EX_USER 0x8000
++#define DEFAULT_EX_MASK 0x0fff
++
++/* If a non-maskable ex is masked out, the interpreter will be terminated. */
++#define EX_NON_MASKABLE_EX 0x0fff
++
++#define EX_NO_HANDLER 0xffffffff
++
++/*
++ * This defines the Dynamic Probe getvars Request Packet, passed in from
++ * the user for subfunction DP_GETVARS.
++ */
++struct dp_getvars_in_struct {
++ unsigned char *name; /* loadable module name */
++ unsigned short from;
++ unsigned short to;
++ unsigned long allocated; /* Allocated size of result pkt */
++ unsigned long returned; /* Returned size of result pkt */
++};
++
++/*
++ * The output from getvars command is returned in this format. First all
++ * global variables will be there as dp_getvars_global_out_struct followed
++ * by local variables for each module in form of dp_getvars_local_out_struct.
++ */
++struct dp_getvars_global_out_struct {
++ unsigned long num_vars; /* number of the variables to follow */
++ /* variables */
++};
++
++struct dp_getvars_local_out_struct {
++ unsigned long length; /* length of this element */
++ unsigned long num_vars; /* number of the variables to follow */
++ /* variables followed by the name of loadable module*/
++};
++
++/*
++ * This defines the Dynamic Probe query Request Packet passed in
++ * from the user for subfunction DP_QUERY.
++ */
++struct dp_query_in_struct {
++ unsigned char *name; /* loadable module name */
++ unsigned long allocated; /* Allocated size of result pkt */
++ unsigned long returned; /* Returned size of result pkt */
++};
++
++struct dp_outrec_struct {
++ unsigned long status;
++ long count;
++ int dbregno; /* the debug reg used for this watchpoint */
++ struct dp_point_struct point;
++};
++
++/*
++ * flags
++ *
++ * Initially a probe record starts out with zero flags.
++ *
++ * DP_REC_STATUS_COMPILED: A probe record is compiled, after successfully
++ * validating the rpn code.
++ *
++ * DP_REC_STATUS_ACTIVE: A probe record becomes valid after it is verified
++ * that the opcode as specified in the probe program matches with the one
++ * at the specified location. We would probably do this verification the
++ * first time this probe is inserted.
++ *
++ * DP_REC_STATUS_MISMATCH: This means the opcode specified by the probe
++ * does not match with the one present when we try to insert it.
++ *
++ * DP_REC_STATUS_DISABLED: A probe will be disabled after being executed
++ * for max-hit number of times automatically.
++ *
++ * DP_REC_STATUS_REMOVED: The status of a probe will be removed if user
++ * explicitly removes it using dprobe --remove --major-minor command.
++ * Note that if a specific probe is removed by its major-minor, we
++ * don't remove the dp_record_struct from memory as it is too much
++ * work. We simply note that fact in the status flags and leave the
++ * dp_record_struct alone. But, if an entire probe program is removed,
++ * all its structures will be removed permanently.
++ *
++ * DP_REC_STATUS_INVALID_OFFSET: Set if the address specified for probe insertion
++ * is out of bounds for that executable module.
++ *
++ * DP_REC_STATUS_DEBUGREG_UNAVAIL: Set if the probe is of type watchpoint and
++ * no free debug registers are free for use.
++ *
++ * DP_REC_STATUS_WATCHPOINT_LEN_INVALID: Set if the probe is of type watchpoint
++ * and the range of address specified is not valid.
++ */
++#define DP_REC_STATUS_COMPILED 0x00000001
++#define DP_REC_STATUS_ACTIVE 0x00000002
++#define DP_REC_STATUS_MISMATCH 0x00000004
++#define DP_REC_STATUS_DISABLED 0x00000008
++#define DP_REC_STATUS_REMOVED 0x00000010
++#define DP_REC_STATUS_INVALID_OFFSET 0x00000020
++#define DP_REC_STATUS_DEBUGREG_UNAVAIL 0x00000040
++#define DP_REC_STATUS_WATCHPOINT_LEN_INVALID 0x00000080
++#define DP_REC_STATUS_DEBUGREG_PATCH_NEEDED 0x00000100
++#define DP_REC_STATUS_KPROBE_ERR 0x00000200
++#define DP_REC_STATUS_EXCLUDED 0x00000400
++#define DP_REC_ARCH_FLAGS 0xff000000
++
++struct dp_outmod_struct {
++ struct dp_outmod_struct *next; /* link */
++ unsigned long flags; /* cmdline switches */
++ struct dp_outrec_struct * rec; /* array of dp_outrec_structs. */
++ unsigned long * lv; /* local variables */
++ struct dp_pgm_struct pgm;
++ unsigned long base;
++ unsigned long end;
++};
++
++struct dp_ioctl_arg {
++ void * input;
++ void * output;
++ unsigned long cmd;
++};
++
++/*
++ * This should ideally be 2*HEAP_HDR_SIZE, but the latter is
++ * __KERNEL__ specific. Hence hardcoded here.
++ */
++#define MIN_HEAP_SIZE 40
++
++#ifdef __KERNEL__
++
++#include <linux/sched.h>
++#include <linux/dcache.h>
++#include <linux/namei.h>
++#include <linux/mm.h>
++#include <linux/kprobes.h>
++#include <asm/page.h>
++#include <asm/pgtable.h>
++#include <asm/ptrace.h>
++
++struct dp_record_struct {
++ struct kprobe kp;
++ struct uprobe up;
++ struct dp_module_struct *mod;
++ spinlock_t lock;
++ unsigned long status;
++ long count;
++ int dbregno; /* the debug reg used for this watchpoint */
++ struct dp_point_struct point;
++};
++
++/*
++ * When Dprobes stores it's log in a trace buffer, the log data is preceded
++ * by the header information as in this structure, followed by optional
++ * header elements.
++ */
++#define DP_TRACE_HDR_ID 1
++struct dp_trace_hdr_struct {
++ unsigned short facility_id;
++ unsigned short len;
++ unsigned long mask;
++ unsigned short major;
++ unsigned short minor;
++};
++
++#ifdef CONFIG_SMP
++#define MIN_ST_SIZE sizeof(struct dp_trace_hdr_struct) + sizeof(int)
++#else
++#define MIN_ST_SIZE sizeof(struct dp_trace_hdr_struct)
++#endif
++
++/* Major number 0 is reserved for stack trace log record */
++#define DP_ST_MAJOR 0x0000
++#define DP_ST_MINOR 0x0000
++
++/*
++ * This is the data structure that we maintain in kernel for each module
++ * that has any probes applied on it.
++ */
++struct dp_module_struct {
++ struct dp_module_struct *next; /* link */
++ unsigned long flags;
++ struct dp_record_struct * rec; /* array of dp_record_structs. */
++ unsigned long * lv; /* local variables */
++ struct inode * inode; /* for quicker access to inode */
++ int trace_id; /* ltt trace event id */
++ struct dp_trace_hdr_struct hdr;
++ struct dp_pgm_struct pgm;
++ unsigned long base; /* used to store kmod base address */
++ unsigned long end; /* used to store kmod base address */
++ struct nameidata nd; /* to hold path/dentry etc. */
++ struct address_space_operations * ori_a_ops;
++ struct address_space_operations dp_a_ops;
++};
++
++/* flags used to keep track of allocations */
++#define DP_MOD_FLAGS_LARGE_REC 0x01
++
++#ifdef CONFIG_SMP
++extern struct dprobes_struct dprobes_set[];
++#define dprobes dprobes_set[smp_processor_id()]
++#else
++extern struct dprobes_struct dprobes;
++#define dprobes_set (&dprobes)
++#endif /* CONFIG_SMP */
++
++/*extern char _stext, _etext; kernel start and end */
++
++/*
++ * global variables stuff
++ */
++extern unsigned long *dp_gv;
++extern unsigned long dp_num_gv;
++extern rwlock_t dp_gv_lock;
++
++extern byte_t *dp_heap;
++extern unsigned long dp_num_heap;
++extern rwlock_t dp_heap_lock;
++
++extern int dp_insmod(struct module *kmod);
++extern int dp_remmod(struct module *kmod);
++extern int dp_readpage(struct file *, struct page *);
++extern int dp_writepage(struct page *page);
++
++#define IS_COW_PAGE(page, inode) (!(page->mapping) || \
++ (page->mapping->host != (void *)inode))
++
++#include <linux/list.h>
++struct heap_hdr {
++ byte_t *addr;
++ unsigned char flags;
++ unsigned long size;
++ struct list_head list;
++};
++
++#define HEAP_HDR_SIZE sizeof(struct heap_hdr)
++#define HEAP_FREE 1
++#define HEAP_ALLOCATED 2
++
++#include <asm/dprobes.h>
++
++#endif /* __KERNEL__ */
++
++#endif
+diff -urNp linux-2.6.0-test9/include/linux/dprobes_in.h linux-2.6.0-test9+kp/include/linux/dprobes_in.h
+--- linux-2.6.0-test9/include/linux/dprobes_in.h 1970-01-01 05:30:00.000000000 +0530
++++ linux-2.6.0-test9+kp/include/linux/dprobes_in.h 2003-11-18 07:29:47.000000000 +0530
+@@ -0,0 +1,218 @@
++#ifndef _LINUX_DPROBES_IN_H
++#define _LINUX_DPROBES_IN_H
++
++/*
++ * IBM Dynamic Probes
++ * Copyright (c) International Business Machines Corp., 2000
++ */
++
++/*
++ * This header file defines the opcodes for the RPN instructions.
++ *
++ * Opcodes 0x00 to 0xAF are for common, arch-independent instructions.
++ * Opcodes 0xB0 to 0xDF are for arch-dependent instructions.
++ * Opcodes 0xF1 to 0xFF are for two byte instructions.
++ */
++
++#define DP_NOP 0x00
++
++/*
++ * RPN execution group.
++ */
++#define DP_JMP 0x01
++#define DP_JLT 0x02
++#define DP_JLE 0x03
++#define DP_JGT 0x04
++#define DP_JGE 0x05
++#define DP_JZ 0x06
++#define DP_JNZ 0x07
++#define DP_LOOP 0x08
++#define DP_CALL 0x09
++#define DP_RET 0x0a
++#define DP_ABORT 0x0b
++#define DP_REM 0x0c
++#define DP_EXIT 0x0d
++#define DP_EXIT_N 0x0e
++#define DP_SUSPEND 0x0f
++
++/*
++ * Logging Group.
++ */
++#define DP_RESUME 0x10
++#define DP_SETMIN_I 0x11
++#define DP_SETMIN 0x12
++#define DP_SETMAJ_I 0x13
++#define DP_SETMAJ 0x14
++#define DP_LOG_STR 0x15
++#define DP_LOG_MRF 0x17
++#define DP_LOG_I 0x1b
++
++/*
++ * Global Variable Group.
++ */
++#define DP_ALLOC_GV 0x1c
++#define DP_FREE_GV 0x1d
++#define DP_FREE_GVI 0x1e
++#define DP_PUSH_GVI 0x1f
++#define DP_PUSH_GV 0x20
++#define DP_POP_GVI 0x21
++#define DP_POP_GV 0x22
++#define DP_MOVE_GVI 0x23
++#define DP_MOVE_GV 0x24
++#define DP_INC_GVI 0x25
++#define DP_INC_GV 0x26
++#define DP_DEC_GVI 0x27
++#define DP_DEC_GV 0x28
++
++/*
++ * Local Variable Group.
++ */
++#define DP_PUSH_LVI 0x29
++#define DP_PUSH_LV 0x2a
++#define DP_POP_LVI 0x2b
++#define DP_POP_LV 0x2c
++#define DP_MOVE_LVI 0x2d
++#define DP_MOVE_LV 0x2e
++#define DP_INC_LVI 0x2f
++#define DP_INC_LV 0x30
++#define DP_DEC_LVI 0x31
++#define DP_DEC_LV 0x32
++
++/*
++ * Arithmetic Group.
++ */
++#define DP_ADD 0x33
++#define DP_SUB 0x34
++#define DP_MUL 0x35
++
++/*
++ * Logic Group.
++ */
++#define DP_NEG 0x36
++#define DP_AND 0x37
++#define DP_OR 0x38
++#define DP_XOR 0x39
++#define DP_ROL_I 0x3a
++#define DP_ROL 0x3b
++#define DP_ROR_I 0x3c
++#define DP_ROR 0x3d
++#define DP_SHL_I 0x3e
++#define DP_SHL 0x3f
++#define DP_SHR_I 0x40
++#define DP_SHR 0x41
++
++/*
++ * RPN Stack Group.
++ */
++#define DP_XCHG 0x42
++#define DP_DUP_I 0x43
++#define DP_DUP 0x44
++#define DP_ROS 0x45
++
++/*
++ * Register Group.
++ */
++#define DP_PUSH_R 0x46
++#define DP_POP_R 0x47
++#define DP_PUSH_U 0x48
++#define DP_POP_U 0x49
++
++/*
++ * Data Group.
++ */
++#define DP_PUSH 0x4c
++#define DP_PUSH_MEM_U8 0x4d
++#define DP_PUSH_MEM_U16 0x4e
++#define DP_PUSH_MEM_U32 0x4f
++#define DP_PUSH_MEM_U64 0x50
++#define DP_POP_MEM_U8 0x51
++#define DP_POP_MEM_U16 0x52
++#define DP_POP_MEM_U32 0x53
++#define DP_POP_MEM_U64 0x54
++
++/*
++ * System Variable Group.
++ */
++#define DP_PUSH_TASK 0x5d
++#define DP_PUSH_PID 0x5e
++#define DP_PUSH_PROCID 0x5f
++
++/*
++ * Address Verification.
++ */
++#define DP_VFY_R 0x60
++#define DP_VFY_RW 0x61
++
++/*
++ * Some more arithmetic/logic instructions.
++ */
++#define DP_DIV 0x62
++#define DP_IDIV 0x63
++#define DP_PBL 0x64
++#define DP_PBR 0x65
++#define DP_PBL_I 0x66
++#define DP_PBR_I 0x67
++#define DP_PZL 0x68
++#define DP_PZR 0x69
++#define DP_PZL_I 0x6a
++#define DP_PZR_I 0x6b
++
++/*
++ * Exception handling/Stacktrace instructions.
++ */
++#define DP_SX 0x6c
++#define DP_UX 0x6d
++#define DP_RX 0x6e
++#define DP_PUSH_X 0x6f
++#define DP_PUSH_LP 0x70
++#define DP_PUSH_PLP 0x71
++#define DP_POP_LP 0x72
++#define DP_LOG_ST 0x73
++#define DP_PURGE_ST 0x74
++#define DP_TRACE_LV 0x75
++#define DP_TRACE_GV 0x76
++#define DP_TRACE_PV 0x77
++
++#define DP_PUSH_SBP 0x78
++#define DP_POP_SBP 0x79
++#define DP_PUSH_TSP 0x7a
++#define DP_POP_TSP 0x7b
++#define DP_PUSH_SBP_I 0x7c
++#define DP_POP_SBP_I 0x7d
++#define DP_PUSH_TSP_I 0x7e
++#define DP_POP_TSP_I 0x7f
++#define DP_COPY_SBP_I 0x80
++#define DP_COPY_TSP_I 0x81
++#define DP_PUSH_STP 0x82
++#define DP_POP_STP 0x83
++
++#define DP_SAVE_SBP 0x84
++#define DP_RESTORE_SBP 0x85
++#define DP_SAVE_TSP 0x86
++#define DP_RESTORE_TSP 0x87
++#define DP_COPY_SBP 0x88
++#define DP_COPY_TSP 0x89
++
++/* stack based log instructions */
++#define DP_LOG 0x8a
++#define DP_LOG_LV 0x8b
++#define DP_LOG_GV 0x8c
++
++#define DP_CALLK 0x8d
++
++/* byte heap instructions */
++#define DP_MALLOC 0x8e
++#define DP_FREE 0x8f
++#define DP_PUSH_WID 0x90
++#define DP_PUSHH_U8_I 0x91
++#define DP_PUSHH_U16_I 0x92
++#define DP_PUSHH_U32_I 0x93
++#define DP_PUSHH_U64_I 0x94
++#define DP_POPH_U8_I 0x95
++#define DP_POPH_U16_I 0x96
++#define DP_POPH_U32_I 0x97
++#define DP_POPH_U64_I 0x98
++
++#include <asm/dprobes_in.h>
++
++#endif
+diff -urNp linux-2.6.0-test9/include/linux/kprobes.h linux-2.6.0-test9+kp/include/linux/kprobes.h
+--- linux-2.6.0-test9/include/linux/kprobes.h 1970-01-01 05:30:00.000000000 +0530
++++ linux-2.6.0-test9+kp/include/linux/kprobes.h 2003-11-18 07:30:12.000000000 +0530
+@@ -0,0 +1,88 @@
++#ifndef _LINUX_KPROBES_H
++#define _LINUX_KPROBES_H
++#include <linux/config.h>
++#include <linux/list.h>
++#include <linux/fs.h>
++#include <linux/notifier.h>
++#include <linux/smp.h>
++#include <asm/kprobes.h>
++
++struct kprobe;
++struct pt_regs;
++
++typedef int (*kprobe_pre_handler_t)(struct kprobe *, struct pt_regs *);
++typedef void (*kprobe_post_handler_t)(struct kprobe *, struct pt_regs *,
++ unsigned long flags);
++typedef int (*kprobe_fault_handler_t)(struct kprobe *, struct pt_regs *,
++ int trapnr);
++struct kprobe {
++ struct list_head list;
++
++ /* location of the probe point */
++ kprobe_opcode_t *addr;
++
++ /* user space probe info */
++ struct uprobe *user;
++
++ /* Called before addr is executed. */
++ kprobe_pre_handler_t pre_handler;
++
++ /* Called after addr is executed, unless... */
++ kprobe_post_handler_t post_handler;
++
++ /* ... called if executing addr causes a fault (eg. page fault).
++ * Return 1 if it handled fault, otherwise kernel will see it. */
++ kprobe_fault_handler_t fault_handler;
++
++ /* Saved opcode (which has been replaced with breakpoint) */
++ kprobe_opcode_t opcode;
++
++ /* copy of the original instruction */
++ kprobe_opcode_t insn[MAX_INSN_SIZE];
++};
++
++struct uprobe {
++ struct inode *inode;
++ unsigned long offset;
++ kprobe_opcode_t *addr;
++
++ /* for kprobes internal use */
++ struct vm_area_struct *vma;
++ struct page *page;
++};
++
++#ifdef CONFIG_KPROBES
++/* Locks kprobe: irq must be disabled */
++void lock_kprobes(void);
++void unlock_kprobes(void);
++
++/* kprobe running now on this CPU? */
++static inline int kprobe_running(void)
++{
++ extern unsigned int kprobe_cpu;
++ return kprobe_cpu == smp_processor_id();
++}
++
++extern void arch_prepare_kprobe(struct kprobe *p);
++
++/* Get the kprobe at this addr (if any). Must have called lock_kprobes */
++extern struct kprobe *get_kprobe(void *addr);
++extern void put_kprobe(struct kprobe *p);
++extern void set_opcode(struct kprobe *p, kprobe_opcode_t opcode);
++
++extern int register_kprobe(struct kprobe *p);
++extern void unregister_kprobe(struct kprobe *p);
++extern int register_kprobe_user(struct kprobe *p);
++extern void unregister_kprobe_user(struct kprobe *p);
++extern int insert_kprobe_user(struct kprobe *p);
++extern int remove_kprobe_user(struct kprobe *p);
++#else
++static inline int kprobe_running(void) { return 0; }
++static inline int register_kprobe(struct kprobe *p) { return -ENOSYS; }
++static inline void unregister_kprobe(struct kprobe *p) { }
++static inline int register_kprobe_user(struct kprobe *p) { return -ENOSYS; }
++static inline void unregister_kprobe_user(struct kprobe *p) { }
++static inline int insert_kprobe_user(struct kprobe *p) { return -ENOSYS; }
++static inline int remove_kprobe_user(struct kprobe *p) { }
++#endif
++#endif /* _LINUX_KPROBES_H */
+diff -urNp linux-2.6.0-test9/kernel/extable.c linux-2.6.0-test9+kp/kernel/extable.c
+--- linux-2.6.0-test9/kernel/extable.c 2003-10-26 00:13:20.000000000 +0530
++++ linux-2.6.0-test9+kp/kernel/extable.c 2003-11-18 07:29:47.000000000 +0530
+@@ -32,6 +32,7 @@ const struct exception_table_entry *sear
+ e = search_module_extables(addr);
+ return e;
+ }
++EXPORT_SYMBOL(search_exception_tables);
+
+ int kernel_text_address(unsigned long addr)
+ {
+diff -urNp linux-2.6.0-test9/kernel/fork.c linux-2.6.0-test9+kp/kernel/fork.c
+--- linux-2.6.0-test9/kernel/fork.c 2003-10-26 00:12:56.000000000 +0530
++++ linux-2.6.0-test9+kp/kernel/fork.c 2003-11-18 07:29:48.000000000 +0530
+@@ -1228,3 +1228,7 @@ void __init proc_caches_init(void)
+ if(!mm_cachep)
+ panic("vma_init: Cannot alloc mm_struct SLAB cache");
+ }
++#ifdef CONFIG_DPROBES_MODULE
++EXPORT_SYMBOL(mmput);
++#endif
++
+diff -urNp linux-2.6.0-test9/kernel/kprobes.c linux-2.6.0-test9+kp/kernel/kprobes.c
+--- linux-2.6.0-test9/kernel/kprobes.c 1970-01-01 05:30:00.000000000 +0530
++++ linux-2.6.0-test9+kp/kernel/kprobes.c 2003-11-18 07:30:12.000000000 +0530
+@@ -0,0 +1,379 @@
++/* Support for kernel probes.
++ (C) 2002 Vamsi Krishna S <vamsi_krishna@in.ibm.com>.
++ Support for user space probes.
++ (C) 2003 Prasanna S Panchamukhi <prasanna@in.ibm.com>.
++*/
++#include <linux/kprobes.h>
++#include <linux/sysrq.h>
++#include <linux/kallsyms.h>
++#include <linux/spinlock.h>
++#include <linux/hash.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/highmem.h>
++#include <linux/pagemap.h>
++#include <asm/cacheflush.h>
++#include <asm/errno.h>
++
++#define KPROBE_HASH_BITS 6
++#define KPROBE_TABLE_SIZE (1 << KPROBE_HASH_BITS)
++
++static struct list_head kprobe_table[KPROBE_TABLE_SIZE];
++
++unsigned int kprobe_cpu = NR_CPUS;
++static spinlock_t kprobe_lock = SPIN_LOCK_UNLOCKED;
++
++void kprobes_code_start(void){}
++
++/* Locks kprobe: irqs must be disabled */
++void lock_kprobes(void)
++{
++ spin_lock(&kprobe_lock);
++ kprobe_cpu = smp_processor_id();
++}
++
++void unlock_kprobes(void)
++{
++ kprobe_cpu = NR_CPUS;
++ spin_unlock(&kprobe_lock);
++}
++
++static struct kprobe *get_uprobe_at(struct inode *inode, unsigned long offset)
++{
++ struct list_head *head;
++ struct kprobe *p;
++
++ head = &kprobe_table[hash_long((unsigned long)inode*offset,
++ KPROBE_HASH_BITS)];
++ list_for_each_entry(p, head, list) {
++ if (p->user->inode == inode && p->user->offset == offset)
++ return p;
++ }
++ return NULL;
++}
++
++/*
++ * This leaves with page, and kmap held. They will be released in
++ * put_kprobe_user. Not sure if holding page_table_lock is also
++ * needed, it is a very small, probably can't happen, race where
++ * vma could be gone by the time we complete the single-step.
++ */
++static void get_kprobe_user(struct uprobe *u)
++{
++ kprobe_opcode_t *addr;
++ u->page = find_get_page(u->inode->i_mapping, u->offset >> PAGE_CACHE_SHIFT);
++ addr = (kprobe_opcode_t *)kmap_atomic(u->page, KM_USER0);
++ u->addr = (kprobe_opcode_t *) ((unsigned long) addr + (unsigned long) ( u->offset & ~PAGE_MASK));
++
++}
++
++static void put_kprobe_user(struct uprobe *u)
++{
++ kunmap_atomic(u->addr, KM_USER0);
++ page_cache_release(u->page);
++}
++
++/*
++ * We need to look up the inode and offset from the vma. We can't depend on
++ * the page->(mapping, index) as that would be incorrect if we ever swap this
++ * page out (possible for pages which are dirtied by GDB breakpoints etc)
++ *
++ * We acquire page_table_lock here to ensure that:
++ * - current page doesn't go away from under us (kswapd)
++ * - mm->mmap consistancy (vma are always added under this lock)
++ *
++ * We will never deadlock on page_table_lock, we always come here due to a
++ * probe in user space, no kernel code could have executed to take the
++ * page_table_lock.
++ */
++static struct kprobe *get_uprobe(void *addr)
++{
++ struct mm_struct *mm = current->mm;
++ struct vm_area_struct *vma;
++ struct inode *inode;
++ unsigned long offset;
++ struct kprobe *p;
++
++ spin_lock(&mm->page_table_lock);
++ vma = find_vma(mm, (unsigned long)addr);
++ offset = (unsigned long)addr - vma->vm_start + (vma->vm_pgoff << PAGE_SHIFT);
++ if (!vma->vm_file) {
++ spin_unlock(&mm->page_table_lock);
++ return NULL;
++ }
++ inode = vma->vm_file->f_dentry->d_inode;
++ spin_unlock(&mm->page_table_lock);
++
++ p = get_uprobe_at(inode, offset);
++ if (p) {
++ get_kprobe_user(p->user);
++ p->addr = addr;
++ p->user->vma = vma;
++ }
++ return p;
++}
++
++/* You have to be holding the kprobe_lock */
++struct kprobe *get_kprobe(void *addr)
++{
++ struct list_head *head, *tmp;
++
++ if ((unsigned long)addr < PAGE_OFFSET)
++ return get_uprobe(addr);
++
++ head = &kprobe_table[hash_ptr(addr, KPROBE_HASH_BITS)];
++ list_for_each(tmp, head) {
++ struct kprobe *p = list_entry(tmp, struct kprobe, list);
++ if (p->addr == addr)
++ return p;
++ }
++ return NULL;
++}
++
++void put_kprobe(struct kprobe *p)
++{
++ if (p->user)
++ put_kprobe_user(p->user);
++}
++
++static void set_opcode_user(struct kprobe *p, kprobe_opcode_t opcode)
++{
++ *p->user->addr = opcode;
++ flush_icache_user_range(p->user->vma, p->user->page, addr, sizeof(kprobe_opcode_t));
++}
++
++static void set_opcode_k(struct kprobe *p, kprobe_opcode_t opcode)
++{
++ kprobe_opcode_t *addr = p->addr;
++ *addr = opcode;
++ flush_icache_range(addr, addr + sizeof(kprobe_opcode_t));
++}
++
++void set_opcode(struct kprobe *p, kprobe_opcode_t opcode)
++{
++ if ((unsigned long)p->addr > PAGE_OFFSET)
++ set_opcode_k(p, opcode);
++ else
++ set_opcode_user(p, opcode);
++}
++
++int register_kprobe(struct kprobe *p)
++{
++ int ret = 0;
++ kprobe_opcode_t *addr = p->addr;
++
++ spin_lock_irq(&kprobe_lock);
++ if (get_kprobe(addr)) {
++ ret = -EEXIST;
++ goto out;
++ }
++ list_add(&p->list, &kprobe_table[hash_ptr(addr,
++ KPROBE_HASH_BITS)]);
++ arch_prepare_kprobe(p);
++ p->opcode = *addr;
++ set_opcode_k(p, BREAKPOINT_INSTRUCTION);
++ out:
++ spin_unlock_irq(&kprobe_lock);
++ return ret;
++}
++
++void unregister_kprobe(struct kprobe *p)
++{
++ spin_lock_irq(&kprobe_lock);
++ set_opcode_k(p, p->opcode);
++ list_del(&p->list);
++ spin_unlock_irq(&kprobe_lock);
++}
++
++/*
++ * User space probes defines four simple and lightweight interfaces :
++ *
++ * register_kprobe_user :
++ * Registration of user space probes is defined for a pair of inode and
++ * offset within the executable where the probes need to be inserted.
++ * Similar to kernel space probes registration, the user space
++ * registration provides three callback functions.
++ * User space registration requires a kprobes structure as an argument
++ * with the following fields initialized:
++ *
++ * pre_handler - pointer to the function that will be called when the
++ * probed instruction is about to execute.
++ * post_handler - pointer to the function that will be called after the
++ * successful execution of the instruction.
++ * fault_handler - pointer to the function that will be called if a
++ * software exception occurs, while executing inside the probe_handler
++ * or while single stepping.
++ * user - pointer to the struct uprobe. The following fields within this
++ * structure must be initialized:
++ *
++ * inode - pointer to the inode of the executable.
++ * offset - offset within the executable, where probes are
++ * inserted/removed. Each unique pair of inode and offset is
++ * used as a hashing function.
++ *
++ * User is required to retain this structure until the probes are unregistered.
++ * This routine should be called only once for a given pair of inode and
++ * offset. Only after the user space probes are registered, the user
++ * can insert and remove probes at the given address.
++ */
++
++int register_kprobe_user(struct kprobe *p)
++{
++ int ret = 0;
++ spin_lock_irq(&kprobe_lock);
++ if (get_uprobe_at(p->user->inode, p->user->offset)) {
++ ret = -EEXIST;
++ goto out;
++ }
++ list_add(&p->list, &kprobe_table[hash_long(
++ (unsigned long)p->user->inode * p->user->offset,
++ KPROBE_HASH_BITS)]);
++ p->opcode = BREAKPOINT_INSTRUCTION;
++ out :
++ spin_unlock_irq(&kprobe_lock);
++ return ret;
++
++}
++
++/*
++ * unregister_kprobe_user:
++ * Every registered user space probe must be unregistered.
++ * For unregistering, kprobes structure must be passed with
++ * the following fields initialized:
++ *
++ * user->inode - pointer to the inode of the executable.
++ * user->offset - offset within the executable, where the probe was registered.
++ *
++ * This routine must be called after all the probes for a given
++ * pair of inode and offset are removed.
++ */
++
++void unregister_kprobe_user(struct kprobe *p)
++{
++ spin_lock_irq(&kprobe_lock);
++ if (get_uprobe_at(p->user->inode, p->user->offset))
++ list_del(&p->list);
++ spin_unlock_irq(&kprobe_lock);
++}
++
++/*
++ * insert_kprobe_userspace:
++ * To insert a user space probe within a page at a given address,
++ * this routine must be called with the following fields initialized:
++ *
++ * user->inode - pointer to the inode of the executable.
++ * user->offset - offset within the executable, where we need to insert a probe.
++ * user->addr - address in the user address space where probes need to inserted.
++ * user->page - pointer to the page containing the address.
++ * user->vma - pointer to the vma containing the address.
++ *
++ * The user space probes can be inserted into the pages existing in the memory or
++ * pages read via readpage routine of the inode's address space operations or
++ * swapping in of the process private page.
++ * User has to ensure that the page containing the specified address
++ * (user->addr) must be present in the memory before calling.
++ * User has to first register user space probes for a given pair of
++ * of inode and offset before calling this routine to insert probes.
++ *
++ */
++
++int insert_kprobe_user(struct kprobe *p)
++{
++ int ret = 0;
++ spin_lock_irq(&kprobe_lock);
++ if (!get_uprobe_at(p->user->inode, p->user->offset)) {
++ ret = -ENODATA;
++ goto out;
++ }
++
++ if (*p->user->addr == BREAKPOINT_INSTRUCTION) {
++ ret = -EEXIST;
++ goto out;
++ }
++
++ p->opcode = *p->user->addr;
++ set_opcode_user(p, BREAKPOINT_INSTRUCTION);
++ out :
++ spin_unlock_irq(&kprobe_lock);
++ return ret;
++}
++
++/*
++ * remove_kprobe_user:
++ * For removing user space probe from a page at the given address, this routine must be
++ * called with the following fields initialized:
++ *
++ * user->inode - pointer to the inode of the executable.
++ * user->offset - offset within the executable,where we need to remove a probe.
++ * user->addr - address in user address space where probes need to removed.
++ * user->page - pointer to the page containing the address.
++ * user->vma - pointer to the vma containing the address.
++ *
++ * User has to ensure that the page containing the specified address
++ * (user->addr) must be present in the memory before calling this routine.
++ * Before unregistering, all the probes inserted at a given inode and offset
++ * must be removed.
++ */
++
++int remove_kprobe_user(struct kprobe *p)
++{
++ int ret = 0;
++ spin_lock_irq(&kprobe_lock);
++ if (!get_uprobe_at(p->user->inode, p->user->offset)) {
++ ret = -ENODATA;
++ goto out;
++ }
++ set_opcode_user(p, p->opcode);
++ out:
++ spin_unlock_irq(&kprobe_lock);
++ return ret;
++}
++
++static void show_kprobes(int key, struct pt_regs *pt_regs, struct tty_struct *tty)
++{
++ int i;
++ struct kprobe *p;
++
++ /* unsafe: kprobe_lock ought to be taken here */
++ for(i = 0; i < KPROBE_TABLE_SIZE; i++) {
++ if (!list_empty(&kprobe_table[i])) {
++ list_for_each_entry(p, &kprobe_table[i], list) {
++ printk("[<%p>] ", p->addr);
++ print_symbol("%s\t", (unsigned long)p->addr);
++ print_symbol("%s\n", (unsigned long)p->pre_handler);
++ }
++ }
++ }
++}
++
++static struct sysrq_key_op sysrq_show_kprobes = {
++ .handler = show_kprobes,
++ .help_msg = "shoWkprobes",
++ .action_msg = "Show kprobes\n"
++};
++
++static int __init init_kprobes(void)
++{
++ int i;
++
++ register_sysrq_key('w', &sysrq_show_kprobes);
++ /* FIXME allocate the probe table, currently defined statically */
++ /* initialize all list heads */
++ for (i = 0; i < KPROBE_TABLE_SIZE; i++)
++ INIT_LIST_HEAD(&kprobe_table[i]);
++ return 0;
++}
++
++void kprobes_code_end(void) {}
++__initcall(init_kprobes);
++
++EXPORT_SYMBOL_GPL(register_kprobe);
++EXPORT_SYMBOL_GPL(unregister_kprobe);
++EXPORT_SYMBOL_GPL(register_kprobe_user);
++EXPORT_SYMBOL_GPL(unregister_kprobe_user);
++EXPORT_SYMBOL_GPL(insert_kprobe_user);
++EXPORT_SYMBOL_GPL(remove_kprobe_user);
++EXPORT_SYMBOL_GPL(kprobes_code_start);
++EXPORT_SYMBOL_GPL(kprobes_code_end);
++EXPORT_SYMBOL_GPL(kprobes_asm_code_start);
++EXPORT_SYMBOL_GPL(kprobes_asm_code_end);
+diff -urNp linux-2.6.0-test9/kernel/Makefile linux-2.6.0-test9+kp/kernel/Makefile
+--- linux-2.6.0-test9/kernel/Makefile 2003-10-26 00:13:05.000000000 +0530
++++ linux-2.6.0-test9+kp/kernel/Makefile 2003-11-18 07:29:47.000000000 +0530
+@@ -19,6 +19,7 @@ obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o
+ obj-$(CONFIG_COMPAT) += compat.o
+ obj-$(CONFIG_IKCONFIG) += configs.o
+ obj-$(CONFIG_IKCONFIG_PROC) += configs.o
++obj-$(CONFIG_KPROBES) += kprobes.o
+
+ ifneq ($(CONFIG_IA64),y)
+ # According to Alan Modra <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is
+diff -urNp linux-2.6.0-test9/mm/memory.c linux-2.6.0-test9+kp/mm/memory.c
+--- linux-2.6.0-test9/mm/memory.c 2003-10-26 00:13:32.000000000 +0530
++++ linux-2.6.0-test9+kp/mm/memory.c 2003-11-18 07:29:48.000000000 +0530
+@@ -1701,3 +1701,7 @@ struct page * vmalloc_to_page(void * vma
+ }
+
+ EXPORT_SYMBOL(vmalloc_to_page);
++#ifdef CONFIG_DPROBES_MODULE
++EXPORT_SYMBOL(handle_mm_fault);
++#endif
++
+diff -urNp linux-2.6.0-test9/mm/swap.c linux-2.6.0-test9+kp/mm/swap.c
+--- linux-2.6.0-test9/mm/swap.c 2003-10-26 00:13:26.000000000 +0530
++++ linux-2.6.0-test9+kp/mm/swap.c 2003-11-18 08:14:41.000000000 +0530
+@@ -398,3 +398,8 @@ void __init swap_setup(void)
+ * _really_ don't want to cluster much more
+ */
+ }
++#ifdef CONFIG_DPROBES_MODULE
++EXPORT_SYMBOL(swapper_space);
++EXPORT_SYMBOL(delete_from_swap_cache);
++#endif
++
--- /dev/null
+diff -urNp linux.orig/arch/i386/Kconfig linux-2.6.0-test11/arch/i386/Kconfig
+--- linux.orig/arch/i386/Kconfig 2003-11-27 02:13:07.000000000 +0530
++++ linux-2.6.0-test11/arch/i386/Kconfig 2003-12-15 15:51:47.000000000 +0530
+@@ -1133,6 +1133,37 @@ config DEBUG_KERNEL
+ Say Y here if you are developing drivers or trying to debug and
+ identify kernel problems.
+
++config KPROBES
++ bool "Kprobes"
++ depends on DEBUG_KERNEL
++ help
++ Kprobes allows you to trap at almost any kernel address, using
++ register_kprobe(), and providing a callback function. This is useful
++ for kernel debugging, non-intrusive instrumentation and testing. If
++ in doubt, say "N".
++
++config DEBUGREG
++ bool "Global Debug Registers"
++ depends on DEBUG_KERNEL
++ help
++ Global debug register settings will be honoured if this is turned on.
++ If in doubt, say "N".
++
++config KWATCH
++ bool "Kwatch points"
++ depends on DEBUG_KERNEL && DEBUGREG
++ help
++ This enables kernel-space watchpoints using processor's debug
++ registers. If in doubt, say "N".
++
++config DPROBES
++ tristate "Dynamic Probes"
++ depends on DEBUG_KERNEL && KPROBES
++ help
++ DProbes provides a higher level interface to kprobes. DProbes
++ enables you to write probe programs in a RPN language. If
++ in doubt, say "N".
++
+ config DEBUG_STACKOVERFLOW
+ bool "Check for stack overflows"
+ depends on DEBUG_KERNEL
+diff -urNp linux.orig/arch/i386/kernel/debugreg.c linux-2.6.0-test11/arch/i386/kernel/debugreg.c
+--- linux.orig/arch/i386/kernel/debugreg.c 1970-01-01 05:30:00.000000000 +0530
++++ linux-2.6.0-test11/arch/i386/kernel/debugreg.c 2003-12-15 15:51:47.000000000 +0530
+@@ -0,0 +1,178 @@
++/*
++ * This provides a debug register allocation mechanism, to be
++ * used by all debuggers, which need debug registers.
++ *
++ * Author: vamsi_krishna@in.ibm.com
++ * bharata@in.ibm.com
++ */
++#include <linux/kernel.h>
++#include <linux/spinlock.h>
++#include <linux/module.h>
++#include <asm/system.h>
++#include <asm/debugreg.h>
++
++struct debugreg dr_list[DR_MAX];
++unsigned long dr7_global_mask = 0;
++static spinlock_t dr_lock = SPIN_LOCK_UNLOCKED;
++
++static inline void set_dr7_global_mask(int regnum)
++{
++ switch (regnum) {
++ case 0: dr7_global_mask |= DR7_DR0_BITS; break;
++ case 1: dr7_global_mask |= DR7_DR1_BITS; break;
++ case 2: dr7_global_mask |= DR7_DR2_BITS; break;
++ case 3: dr7_global_mask |= DR7_DR3_BITS; break;
++ }
++ return;
++}
++
++static inline void clear_dr7_global_mask(int regnum)
++{
++ switch (regnum) {
++ case 0: dr7_global_mask &= ~DR7_DR0_BITS; break;
++ case 1: dr7_global_mask &= ~DR7_DR1_BITS; break;
++ case 2: dr7_global_mask &= ~DR7_DR2_BITS; break;
++ case 3: dr7_global_mask &= ~DR7_DR3_BITS; break;
++ }
++ return;
++}
++
++static int get_dr(int regnum, int flag)
++{
++ if ((flag == DR_ALLOC_GLOBAL) && (dr_list[regnum].flag == DR_UNUSED)) {
++ dr_list[regnum].flag = DR_GLOBAL;
++ set_dr7_global_mask(regnum);
++ return regnum;
++ }
++ else if ((dr_list[regnum].flag == DR_UNUSED) || (dr_list[regnum].flag == DR_LOCAL)) {
++ dr_list[regnum].use_count++;
++ dr_list[regnum].flag = DR_LOCAL;
++ return regnum;
++ }
++ return -1;
++}
++
++static int get_any_dr(int flag)
++{
++ int i;
++ if (flag == DR_ALLOC_LOCAL) {
++ for (i = 0; i < DR_MAX; i++) {
++ if (dr_list[i].flag == DR_LOCAL) {
++ dr_list[i].use_count++;
++ return i;
++ } else if (dr_list[i].flag == DR_UNUSED) {
++ dr_list[i].flag = DR_LOCAL;
++ dr_list[i].use_count = 1;
++ return i;
++ }
++ }
++ } else {
++ for (i = DR_MAX-1; i >= 0; i--) {
++ if (dr_list[i].flag == DR_UNUSED) {
++ dr_list[i].flag = DR_GLOBAL;
++ set_dr7_global_mask(i);
++ return i;
++ }
++ }
++ }
++ return -1;
++}
++
++static inline void dr_free_local(int regnum)
++{
++ if (! (--dr_list[regnum].use_count))
++ dr_list[regnum].flag = DR_UNUSED;
++ return;
++}
++
++static inline void dr_free_global(int regnum)
++{
++ dr_list[regnum].flag = DR_UNUSED;
++ dr_list[regnum].use_count = 0;
++ clear_dr7_global_mask(regnum);
++ return;
++}
++
++int dr_alloc(int regnum, int flag)
++{
++ int ret;
++
++ spin_lock(&dr_lock);
++ if (regnum == DR_ANY)
++ ret = get_any_dr(flag);
++ else if (regnum >= DR_MAX)
++ ret = -1;
++ else
++ ret = get_dr(regnum, flag);
++ spin_unlock(&dr_lock);
++ return ret;
++}
++
++int dr_free(int regnum)
++{
++ spin_lock(&dr_lock);
++ if (regnum >= DR_MAX || dr_list[regnum].flag == DR_UNUSED) {
++ spin_unlock(&dr_lock);
++ return -1;
++ }
++ if (dr_list[regnum].flag == DR_LOCAL)
++ dr_free_local(regnum);
++ else
++ dr_free_global(regnum);
++ spin_unlock(&dr_lock);
++ return 0;
++}
++
++void dr_inc_use_count(unsigned long mask)
++{
++ int i;
++
++ spin_lock(&dr_lock);
++ for (i =0; i < DR_MAX; i++) {
++ if (DR_IS_LOCAL(mask, i))
++ dr_list[i].use_count++;
++ }
++ spin_unlock(&dr_lock);
++}
++
++void dr_dec_use_count(unsigned long mask)
++{
++ int i;
++
++ spin_lock(&dr_lock);
++ for (i =0; i < DR_MAX; i++) {
++ if (DR_IS_LOCAL(mask, i))
++ dr_free_local(i);
++ }
++ spin_unlock(&dr_lock);
++}
++
++/*
++ * This routine decides if the ptrace request is for enabling or disabling
++ * a debug reg, and accordingly calls dr_alloc() or dr_free().
++ *
++ * gdb uses ptrace to write to debug registers. It assumes that writing to
++ * debug register always succeds and it doesn't check the return value of
++ * ptrace. Now with this new global debug register allocation/freeing,
++ * ptrace request for a local debug register can fail, if the required debug
++ * register is already globally allocated. Since gdb fails to notice this
++ * failure, it sometimes tries to free a debug register, which is not
++ * allocated for it.
++ */
++int enable_debugreg(unsigned long old_dr7, unsigned long new_dr7)
++{
++ int i, dr_shift = 1UL;
++ for (i = 0; i < DR_MAX; i++, dr_shift <<= 2) {
++ if ((old_dr7 ^ new_dr7) & dr_shift) {
++ if (new_dr7 & dr_shift)
++ dr_alloc(i, DR_ALLOC_LOCAL);
++ else
++ dr_free(i);
++ return 0;
++ }
++ }
++ return -1;
++}
++
++EXPORT_SYMBOL(dr_alloc);
++EXPORT_SYMBOL(dr_free);
+diff -urNp linux.orig/arch/i386/kernel/entry.S linux-2.6.0-test11/arch/i386/kernel/entry.S
+--- linux.orig/arch/i386/kernel/entry.S 2003-11-27 02:13:26.000000000 +0530
++++ linux-2.6.0-test11/arch/i386/kernel/entry.S 2003-12-15 15:51:47.000000000 +0530
+@@ -495,10 +495,17 @@ ENTRY(debug)
+ jne debug_stack_correct
+ FIX_STACK(12, debug_stack_correct, debug_esp_fix_insn)
+ debug_stack_correct:
+- pushl $0
+- pushl $do_debug
+- jmp error_code
+-
++ pushl $-1 # mark this as an int
++ SAVE_ALL
++ movl %esp,%edx
++ pushl $0
++ pushl %edx
++ call do_debug
++ addl $8,%esp
++ testl %eax,%eax
++ jnz restore_all
++ jmp ret_from_exception
++
+ /*
+ * NMI is doubly nasty. It can happen _while_ we're handling
+ * a debug fault, and the debug fault hasn't yet been able to
+@@ -546,9 +553,16 @@ nmi_debug_stack_fixup:
+ jmp nmi_stack_correct
+
+ ENTRY(int3)
++ pushl $-1 # mark this as an int
++ SAVE_ALL
++ movl %esp,%edx
+ pushl $0
+- pushl $do_int3
+- jmp error_code
++ pushl %edx
++ call do_int3
++ addl $8,%esp
++ testl %eax,%eax
++ jnz restore_all
++ jmp ret_from_exception
+
+ ENTRY(overflow)
+ pushl $0
+diff -urNp linux.orig/arch/i386/kernel/kprobes.c linux-2.6.0-test11/arch/i386/kernel/kprobes.c
+--- linux.orig/arch/i386/kernel/kprobes.c 1970-01-01 05:30:00.000000000 +0530
++++ linux-2.6.0-test11/arch/i386/kernel/kprobes.c 2003-12-15 15:51:47.000000000 +0530
+@@ -0,0 +1,249 @@
++/*
++ * Support for kernel probes.
++ * (C) 2002 Vamsi Krishna S <vamsi_krishna@in.ibm.com>.
++ */
++
++#include <linux/config.h>
++#include <linux/kprobes.h>
++#include <linux/ptrace.h>
++#include <linux/spinlock.h>
++#include <linux/preempt.h>
++
++/* kprobe_status settings */
++#define KPROBE_HIT_ACTIVE 0x00000001
++#define KPROBE_HIT_SS 0x00000002
++
++static struct kprobe *current_kprobe;
++static unsigned long kprobe_status, kprobe_old_eflags, kprobe_saved_eflags;
++
++void kprobes_asm_code_start(void){}
++/*
++ * returns non-zero if opcode modifies the interrupt flag.
++ */
++static inline int is_IF_modifier(kprobe_opcode_t opcode)
++{
++ switch(opcode) {
++ case 0xfa: /* cli */
++ case 0xfb: /* sti */
++ case 0xcf: /* iret/iretd */
++ case 0x9d: /* popf/popfd */
++ return 1;
++ }
++ return 0;
++}
++
++void arch_prepare_kprobe(struct kprobe *p)
++{
++ memcpy(p->insn, p->addr, MAX_INSN_SIZE);
++}
++
++static inline void disarm_kprobe(struct kprobe *p, struct pt_regs *regs)
++{
++ set_opcode(p, p->opcode);
++ regs->eip = (unsigned long)p->addr;
++}
++
++static void prepare_singlestep(struct kprobe *p, struct pt_regs *regs)
++{
++ regs->eflags |= TF_MASK;
++ regs->eflags &= ~IF_MASK;
++
++ if (!(p->user)) {
++ regs->eip = (unsigned long)&p->insn;
++ } else {
++ set_opcode(p, p->opcode);
++ regs->eip = (unsigned long)p->addr;
++ }
++}
++
++/*
++ * Interrupts are disabled on entry as trap3 is an interrupt gate and they
++ * remain disabled thorough out this function.
++ */
++int kprobe_handler(struct pt_regs *regs)
++{
++ struct kprobe *p;
++ int ret = 0;
++ kprobe_opcode_t *addr = (kprobe_opcode_t *)(regs->eip - sizeof(kprobe_opcode_t));
++
++ /* We're in an interrupt, but this is clear and BUG()-safe. */
++ preempt_disable();
++
++ /* Check we're not actually recursing */
++ if (kprobe_running()) {
++ /* We *are* holding lock here, so this is safe.
++ Disarm the probe we just hit, and ignore it. */
++ p = get_kprobe(addr);
++ if (p) {
++ disarm_kprobe(p, regs);
++ put_kprobe(p);
++ ret = 1;
++ }
++ /* If it's not ours, can't be delete race, (we hold lock). */
++ goto no_kprobe;
++ }
++
++ lock_kprobes();
++ p = get_kprobe(addr);
++ if (!p) {
++ unlock_kprobes();
++ /* Unregistered (on another cpu) after this hit? Ignore */
++ if (*addr != BREAKPOINT_INSTRUCTION)
++ ret = 1;
++ /* Not one of ours: let kernel handle it */
++ goto no_kprobe;
++ }
++ /* If p->opcode == BREAKPOINT_INSTRUCTION, means we haven't yet inserted
++ * breakpoint corresponding to this probe.No need to handle this probe.
++ */
++ if (p->user && p->opcode == BREAKPOINT_INSTRUCTION) {
++ unlock_kprobes();
++ ret = 1;
++ goto no_kprobe;
++ }
++
++ kprobe_status = KPROBE_HIT_ACTIVE;
++ current_kprobe = p;
++ kprobe_saved_eflags = kprobe_old_eflags
++ = (regs->eflags & (TF_MASK|IF_MASK));
++ if (is_IF_modifier(p->opcode))
++ kprobe_saved_eflags &= ~IF_MASK;
++
++ /* If the pre_handler returns 1, means probes are marked for emergency
++ * removal. Restore back the original opcode.
++ */
++ if (p->pre_handler(p, regs)) {
++ if (!p->user)
++ disarm_kprobe(p, regs);
++ else {
++ set_opcode(p, p->opcode);
++ regs->eip = (unsigned long)p->addr;
++ }
++ ret = 1;
++ unlock_kprobes();
++ goto no_kprobe;
++ }
++
++ prepare_singlestep(p, regs);
++ //disarm_kprobe(p, regs);
++ kprobe_status = KPROBE_HIT_SS;
++ return 1;
++
++ if (p->post_handler)
++ p->post_handler(p, regs, 0);
++ unlock_kprobes();
++ preempt_enable_no_resched();
++ return 1;
++
++no_kprobe:
++ preempt_enable_no_resched();
++ return ret;
++}
++
++static void resume_execution(struct kprobe *p, struct pt_regs *regs)
++{
++ unsigned long *tos = ®s->esp;
++ unsigned long next_eip = 0;
++ unsigned long copy_eip = (unsigned long)&p->insn;
++ unsigned long orig_eip = (unsigned long)p->addr;
++
++ /*
++ * We singlestepped with interrupts disabled. So, the result on
++ * the stack would be incorrect for "pushfl" instruction.
++ * Note that regs->esp is actually the top of the stack when the
++ * trap occurs in kernel space.
++ */
++ switch(p->insn[0]) {
++ case 0x9c: /* pushfl */
++ if (regs->eip < PAGE_OFFSET)
++ tos = (unsigned long *)regs->esp;
++ *tos &= ~(TF_MASK | IF_MASK);
++ *tos |= kprobe_old_eflags;
++ break;
++ case 0xe8: /* call relative */
++ *tos = orig_eip + (*tos - copy_eip);
++ break;
++ case 0xff:
++ if ((p->insn[1] & 0x30) == 0x10) { /* call absolute, indirect */
++ next_eip = regs->eip;
++ *tos = orig_eip + (*tos - copy_eip);
++ } else if (((p->insn[1] & 0x31) == 0x20) || /* jmp near, absolute indirect */
++ ((p->insn[1] & 0x31) == 0x21)) { /* jmp far, absolute indirect */
++ next_eip = regs->eip;
++ }
++ break;
++ case 0xea: /* jmp absolute */
++ next_eip = regs->eip;
++ break;
++ default:
++ break;
++ }
++
++ regs->eflags &= ~TF_MASK;
++ if (next_eip) {
++ regs->eip = next_eip;
++ } else {
++ regs->eip = orig_eip + (regs->eip - copy_eip);
++ }
++}
++
++static void resume_execution_user(struct kprobe *p, struct pt_regs *regs)
++{
++ regs->eflags &= ~TF_MASK;
++ set_opcode(p, BREAKPOINT_INSTRUCTION);
++}
++
++/*
++ * Interrupts are disabled on entry as trap1 is an interrupt gate and they
++ * remain disabled thorough out this function. And we hold kprobe lock.
++ */
++int post_kprobe_handler(struct pt_regs *regs)
++{
++ if (!kprobe_running())
++ return 0;
++
++ if (current_kprobe->post_handler)
++ current_kprobe->post_handler(current_kprobe, regs, 0);
++
++ if (!(current_kprobe->user))
++ resume_execution(current_kprobe, regs);
++ else
++ resume_execution_user(current_kprobe, regs);
++ regs->eflags |= kprobe_saved_eflags;
++
++ put_kprobe(current_kprobe);
++ unlock_kprobes();
++ preempt_enable_no_resched();
++
++ /*
++ * if somebody else is singlestepping across a probe point, eflags
++ * will have TF set, in which case, continue the remaining processing
++ * of do_debug, as if this is not a probe hit.
++ */
++ if (regs->eflags & TF_MASK)
++ return 0;
++
++ return 1;
++}
++
++/* Interrupts disabled, kprobe_lock held. */
++int kprobe_fault_handler(struct pt_regs *regs, int trapnr)
++{
++ if (current_kprobe->fault_handler
++ && current_kprobe->fault_handler(current_kprobe, regs, trapnr))
++ return 1;
++
++ if (kprobe_status & KPROBE_HIT_SS) {
++ if (!(current_kprobe->user))
++ resume_execution(current_kprobe, regs);
++ else
++ resume_execution_user(current_kprobe, regs);
++
++ regs->eflags |= kprobe_old_eflags;
++ put_kprobe(current_kprobe);
++ unlock_kprobes();
++ preempt_enable_no_resched();
++ }
++ return 0;
++}
++void kprobes_asm_code_end(void) {}
+diff -urNp linux.orig/arch/i386/kernel/kwatch.c linux-2.6.0-test11/arch/i386/kernel/kwatch.c
+--- linux.orig/arch/i386/kernel/kwatch.c 1970-01-01 05:30:00.000000000 +0530
++++ linux-2.6.0-test11/arch/i386/kernel/kwatch.c 2003-12-15 15:51:47.000000000 +0530
+@@ -0,0 +1,142 @@
++/*
++ * Support for kernel watchpoints.
++ * (C) 2002 Vamsi Krishna S <vamsi_krishna@in.ibm.com>.
++ */
++#include <linux/config.h>
++#include <linux/kprobes.h>
++#include <linux/ptrace.h>
++#include <linux/spinlock.h>
++#include <linux/module.h>
++#include <asm/kwatch.h>
++#include <asm/debugreg.h>
++#include <asm/bitops.h>
++
++static struct kwatch kwatch_list[DR_MAX];
++static spinlock_t kwatch_lock = SPIN_LOCK_UNLOCKED;
++static unsigned long kwatch_in_progress; /* currently being handled */
++
++struct dr_info {
++ int debugreg;
++ unsigned long addr;
++ int type;
++};
++
++void kwatch_asm_start(void) {}
++static inline void write_smp_dr(void *info)
++{
++ struct dr_info *dr = (struct dr_info *)info;
++
++ if (cpu_has_de && dr->type == DR_TYPE_IO)
++ set_in_cr4(X86_CR4_DE);
++ write_dr(dr->debugreg, dr->addr);
++}
++
++/* Update the debug register on all CPUs */
++static void sync_dr(int debugreg, unsigned long addr, int type)
++{
++ struct dr_info dr;
++ dr.debugreg = debugreg;
++ dr.addr = addr;
++ dr.type = type;
++ smp_call_function(write_smp_dr, &dr, 0, 0);
++}
++
++/*
++ * Interrupts are disabled on entry as trap1 is an interrupt gate and they
++ * remain disabled thorough out this function.
++ */
++int kwatch_handler(unsigned long condition, struct pt_regs *regs)
++{
++ int debugreg = dr_trap(condition);
++ unsigned long addr = dr_trap_addr(condition);
++ int retval = 0;
++
++ if (!(condition & (DR_TRAP0|DR_TRAP1|DR_TRAP2|DR_TRAP3))) {
++ return 0;
++ }
++
++ /* We're in an interrupt, but this is clear and BUG()-safe. */
++ preempt_disable();
++
++ /* If we are recursing, we already hold the lock. */
++ if (kwatch_in_progress) {
++ goto recursed;
++ }
++ set_bit(debugreg, &kwatch_in_progress);
++
++ spin_lock(&kwatch_lock);
++ if (kwatch_list[debugreg].addr != addr)
++ goto out;
++
++ if (kwatch_list[debugreg].handler) {
++ kwatch_list[debugreg].handler(&kwatch_list[debugreg], regs, debugreg);
++ }
++
++ if (kwatch_list[debugreg].type == DR_TYPE_EXECUTE)
++ regs->eflags |= RF_MASK;
++out:
++ clear_bit(debugreg, &kwatch_in_progress);
++ spin_unlock(&kwatch_lock);
++ preempt_enable_no_resched();
++ return retval;
++
++recursed:
++ if (kwatch_list[debugreg].type == DR_TYPE_EXECUTE)
++ regs->eflags |= RF_MASK;
++ preempt_enable_no_resched();
++ return 1;
++}
++
++int register_kwatch(unsigned long addr, u8 length, u8 type,
++ kwatch_handler_t handler)
++{
++ int debugreg;
++ unsigned long dr7, flags;
++
++ debugreg = dr_alloc(DR_ANY, DR_ALLOC_GLOBAL);
++ if (debugreg < 0) {
++ return -1;
++ }
++
++ spin_lock_irqsave(&kwatch_lock, flags);
++ kwatch_list[debugreg].addr = addr;
++ kwatch_list[debugreg].length = length;
++ kwatch_list[debugreg].type = type;
++ kwatch_list[debugreg].handler = handler;
++ spin_unlock_irqrestore(&kwatch_lock, flags);
++
++ write_dr(debugreg, (unsigned long)addr);
++ sync_dr(debugreg, (unsigned long)addr, type);
++ if (cpu_has_de && type == DR_TYPE_IO)
++ set_in_cr4(X86_CR4_DE);
++
++ dr7 = read_dr(7);
++ SET_DR7(dr7, debugreg, type, length);
++ write_dr(7, dr7);
++ sync_dr(7, dr7, 0);
++ return debugreg;
++}
++
++void unregister_kwatch(int debugreg)
++{
++ unsigned long flags;
++ unsigned long dr7 = read_dr(7);
++
++ RESET_DR7(dr7, debugreg);
++ write_dr(7, dr7);
++ sync_dr(7, dr7, 0);
++ dr_free(debugreg);
++
++ spin_lock_irqsave(&kwatch_lock, flags);
++ kwatch_list[debugreg].addr = 0;
++ kwatch_list[debugreg].handler = NULL;
++ spin_unlock_irqrestore(&kwatch_lock, flags);
++}
++
++void kwatch_asm_end(void) {}
++
++EXPORT_SYMBOL_GPL(register_kwatch);
++EXPORT_SYMBOL_GPL(unregister_kwatch);
++EXPORT_SYMBOL_GPL(kwatch_asm_start);
++EXPORT_SYMBOL_GPL(kwatch_asm_end);
++
+diff -urNp linux.orig/arch/i386/kernel/Makefile linux-2.6.0-test11/arch/i386/kernel/Makefile
+--- linux.orig/arch/i386/kernel/Makefile 2003-11-27 02:13:09.000000000 +0530
++++ linux-2.6.0-test11/arch/i386/kernel/Makefile 2003-12-15 15:51:47.000000000 +0530
+@@ -26,10 +26,13 @@ obj-$(CONFIG_X86_IO_APIC) += io_apic.o
+ obj-$(CONFIG_X86_NUMAQ) += numaq.o
+ obj-$(CONFIG_X86_SUMMIT) += summit.o
+ obj-$(CONFIG_EDD) += edd.o
++obj-$(CONFIG_KPROBES) += kprobes.o
+ obj-$(CONFIG_MODULES) += module.o
+ obj-y += sysenter.o vsyscall.o
+ obj-$(CONFIG_ACPI_SRAT) += srat.o
+ obj-$(CONFIG_HPET_TIMER) += time_hpet.o
++obj-$(CONFIG_DEBUGREG) += debugreg.o
++obj-$(CONFIG_KWATCH) += kwatch.o
+
+ EXTRA_AFLAGS := -traditional
+
+diff -urNp linux.orig/arch/i386/kernel/process.c linux-2.6.0-test11/arch/i386/kernel/process.c
+--- linux.orig/arch/i386/kernel/process.c 2003-11-27 02:12:37.000000000 +0530
++++ linux-2.6.0-test11/arch/i386/kernel/process.c 2003-12-15 15:51:47.000000000 +0530
+@@ -50,6 +50,7 @@
+ #ifdef CONFIG_MATH_EMULATION
+ #include <asm/math_emu.h>
+ #endif
++#include <asm/debugreg.h>
+
+ #include <linux/irq.h>
+ #include <linux/err.h>
+@@ -295,12 +296,16 @@ void exit_thread(void)
+ kfree(tsk->thread.io_bitmap_ptr);
+ tsk->thread.io_bitmap_ptr = NULL;
+ }
++ if (tsk->thread.debugreg[7])
++ dr_dec_use_count(tsk->thread.debugreg[7]);
+ }
+
+ void flush_thread(void)
+ {
+ struct task_struct *tsk = current;
+
++ if (tsk->thread.debugreg[7])
++ dr_dec_use_count(tsk->thread.debugreg[7]);
+ memset(tsk->thread.debugreg, 0, sizeof(unsigned long)*8);
+ memset(tsk->thread.tls_array, 0, sizeof(tsk->thread.tls_array));
+ /*
+@@ -390,6 +395,9 @@ int copy_thread(int nr, unsigned long cl
+ desc->b = LDT_entry_b(&info);
+ }
+
++ if (tsk->thread.debugreg[7])
++ dr_inc_use_count(tsk->thread.debugreg[7]);
++
+ err = 0;
+ out:
+ if (err && p->thread.io_bitmap_ptr)
+@@ -532,6 +540,24 @@ struct task_struct * __switch_to(struct
+ /*
+ * Now maybe reload the debug registers
+ */
++#ifdef CONFIG_DEBUGREG
++{
++ /*
++ * Don't reload global debug registers. Don't touch the global debug
++ * register settings in dr7.
++ */
++ unsigned long next_dr7 = next->debugreg[7];
++ if (unlikely(next_dr7)) {
++ if (DR7_L0(next_dr7)) loaddebug(next, 0);
++ if (DR7_L1(next_dr7)) loaddebug(next, 1);
++ if (DR7_L2(next_dr7)) loaddebug(next, 2);
++ if (DR7_L3(next_dr7)) loaddebug(next, 3);
++ /* no 4 and 5 */
++ loaddebug(next, 6);
++ load_process_dr7(next_dr7);
++ }
++}
++#else
+ if (unlikely(next->debugreg[7])) {
+ loaddebug(next, 0);
+ loaddebug(next, 1);
+@@ -541,7 +567,7 @@ struct task_struct * __switch_to(struct
+ loaddebug(next, 6);
+ loaddebug(next, 7);
+ }
+-
++#endif
+ if (unlikely(prev->io_bitmap_ptr || next->io_bitmap_ptr)) {
+ if (next->io_bitmap_ptr) {
+ /*
+diff -urNp linux.orig/arch/i386/kernel/ptrace.c linux-2.6.0-test11/arch/i386/kernel/ptrace.c
+--- linux.orig/arch/i386/kernel/ptrace.c 2003-11-27 02:13:27.000000000 +0530
++++ linux-2.6.0-test11/arch/i386/kernel/ptrace.c 2003-12-15 15:51:48.000000000 +0530
+@@ -350,6 +350,11 @@ asmlinkage int sys_ptrace(long request,
+
+ addr -= (long) &dummy->u_debugreg;
+ addr = addr >> 2;
++
++ if (addr == 7 && (enable_debugreg(child->thread.debugreg[addr], data)) < 0) {
++ ret = -EBUSY;
++ break;
++ }
+ child->thread.debugreg[addr] = data;
+ ret = 0;
+ }
+diff -urNp linux.orig/arch/i386/kernel/signal.c linux-2.6.0-test11/arch/i386/kernel/signal.c
+--- linux.orig/arch/i386/kernel/signal.c 2003-11-27 02:13:51.000000000 +0530
++++ linux-2.6.0-test11/arch/i386/kernel/signal.c 2003-12-15 15:51:48.000000000 +0530
+@@ -23,6 +23,7 @@
+ #include <asm/ucontext.h>
+ #include <asm/uaccess.h>
+ #include <asm/i387.h>
++#include <asm/debugreg.h>
+ #include "sigframe.h"
+
+ #define DEBUG_SIG 0
+@@ -580,7 +581,7 @@ int do_signal(struct pt_regs *regs, sigs
+ * have been cleared if the watchpoint triggered
+ * inside the kernel.
+ */
+- __asm__("movl %0,%%db7" : : "r" (current->thread.debugreg[7]));
++ load_process_dr7(current->thread.debugreg[7]);
+
+ /* Whee! Actually deliver the signal. */
+ handle_signal(signr, &info, oldset, regs);
+diff -urNp linux.orig/arch/i386/kernel/traps.c linux-2.6.0-test11/arch/i386/kernel/traps.c
+--- linux.orig/arch/i386/kernel/traps.c 2003-11-27 02:13:09.000000000 +0530
++++ linux-2.6.0-test11/arch/i386/kernel/traps.c 2003-12-15 15:51:48.000000000 +0530
+@@ -25,6 +25,7 @@
+ #include <linux/highmem.h>
+ #include <linux/kallsyms.h>
+ #include <linux/ptrace.h>
++#include <linux/kprobes.h>
+
+ #ifdef CONFIG_EISA
+ #include <linux/ioport.h>
+@@ -41,6 +42,7 @@
+ #include <asm/io.h>
+ #include <asm/atomic.h>
+ #include <asm/debugreg.h>
++#include <asm/kwatch.h>
+ #include <asm/desc.h>
+ #include <asm/i387.h>
+ #include <asm/nmi.h>
+@@ -362,7 +364,6 @@ asmlinkage void do_##name(struct pt_regs
+ }
+
+ DO_VM86_ERROR_INFO( 0, SIGFPE, "divide error", divide_error, FPE_INTDIV, regs->eip)
+-DO_VM86_ERROR( 3, SIGTRAP, "int3", int3)
+ DO_VM86_ERROR( 4, SIGSEGV, "overflow", overflow)
+ DO_VM86_ERROR( 5, SIGSEGV, "bounds", bounds)
+ DO_ERROR_INFO( 6, SIGILL, "invalid operand", invalid_op, ILL_ILLOPN, regs->eip)
+@@ -379,6 +380,9 @@ asmlinkage void do_general_protection(st
+
+ if (regs->eflags & VM_MASK)
+ goto gp_in_vm86;
++
++ if (kprobe_running() && kprobe_fault_handler(regs, 13))
++ return;
+
+ if (!(regs->xcs & 3))
+ goto gp_in_kernel;
+@@ -500,6 +504,17 @@ void unset_nmi_callback(void)
+ nmi_callback = dummy_nmi_callback;
+ }
+
++asmlinkage int do_int3(struct pt_regs *regs, long error_code)
++{
++ if (kprobe_handler(regs))
++ return 1;
++ /* This is an interrupt gate, because kprobes wants interrupts
++ disabled. Normal trap handlers don't. */
++ restore_interrupts(regs);
++ do_trap(3, SIGTRAP, "int3", 1, regs, error_code, NULL);
++ return 0;
++}
++
+ /*
+ * Our handling of the processor debug registers is non-trivial.
+ * We do not clear them on entry and exit from the kernel. Therefore
+@@ -522,14 +537,20 @@ void unset_nmi_callback(void)
+ * find every occurrence of the TF bit that could be saved away even
+ * by user code)
+ */
+-asmlinkage void do_debug(struct pt_regs * regs, long error_code)
++asmlinkage int do_debug(struct pt_regs * regs, long error_code)
+ {
+ unsigned int condition;
+ struct task_struct *tsk = current;
+ siginfo_t info;
+
++ if (post_kprobe_handler(regs))
++ return 1;
++
+ __asm__ __volatile__("movl %%db6,%0" : "=r" (condition));
+
++ if (kwatch_handler(condition, regs))
++ return 1;
++
+ /* It's safe to allow irq's after DR6 has been saved */
+ if (regs->eflags & X86_EFLAGS_IF)
+ local_irq_enable();
+@@ -581,20 +602,18 @@ asmlinkage void do_debug(struct pt_regs
+ * the signal is delivered.
+ */
+ clear_dr7:
+- __asm__("movl %0,%%db7"
+- : /* no output */
+- : "r" (0));
+- return;
++ load_process_dr7(0);
++ return 0;
+
+ debug_vm86:
+ handle_vm86_trap((struct kernel_vm86_regs *) regs, error_code, 1);
+- return;
++ return 0;
+
+ clear_TF_reenable:
+ set_tsk_thread_flag(tsk, TIF_SINGLESTEP);
+ clear_TF:
+ regs->eflags &= ~TF_MASK;
+- return;
++ return 0;
+ }
+
+ /*
+@@ -760,6 +779,8 @@ asmlinkage void math_state_restore(struc
+ struct thread_info *thread = current_thread_info();
+ struct task_struct *tsk = thread->task;
+
++ if (kprobe_running() && kprobe_fault_handler(®s, 7))
++ return;
+ clts(); /* Allow maths ops (or we recurse) */
+ if (!tsk->used_math)
+ init_fpu(tsk);
+@@ -854,7 +875,7 @@ void __init trap_init(void)
+ set_trap_gate(0,÷_error);
+ set_intr_gate(1,&debug);
+ set_intr_gate(2,&nmi);
+- set_system_gate(3,&int3); /* int3-5 can be called from all */
++ _set_gate(idt_table+3,14,3,&int3,__KERNEL_CS); /* int3-5 can be called from all */
+ set_system_gate(4,&overflow);
+ set_system_gate(5,&bounds);
+ set_trap_gate(6,&invalid_op);
+diff -urNp linux.orig/arch/i386/mm/fault.c linux-2.6.0-test11/arch/i386/mm/fault.c
+--- linux.orig/arch/i386/mm/fault.c 2003-11-27 02:12:46.000000000 +0530
++++ linux-2.6.0-test11/arch/i386/mm/fault.c 2003-12-15 15:51:48.000000000 +0530
+@@ -21,6 +21,7 @@
+ #include <linux/vt_kern.h> /* For unblank_screen() */
+ #include <linux/highmem.h>
+ #include <linux/module.h>
++#include <linux/kprobes.h>
+
+ #include <asm/system.h>
+ #include <asm/uaccess.h>
+@@ -222,6 +223,9 @@ asmlinkage void do_page_fault(struct pt_
+ /* get the address */
+ __asm__("movl %%cr2,%0":"=r" (address));
+
++ if (kprobe_running() && kprobe_fault_handler(regs, 14))
++ return;
++
+ /* It's safe to allow irq's after cr2 has been saved */
+ if (regs->eflags & (X86_EFLAGS_IF|VM_MASK))
+ local_irq_enable();
+diff -urNp linux.orig/drivers/dprobes/dprobes.c linux-2.6.0-test11/drivers/dprobes/dprobes.c
+--- linux.orig/drivers/dprobes/dprobes.c 1970-01-01 05:30:00.000000000 +0530
++++ linux-2.6.0-test11/drivers/dprobes/dprobes.c 2003-12-15 15:51:48.000000000 +0530
+@@ -0,0 +1,1589 @@
++/*
++ * IBM Dynamic Probes
++ * Copyright (c) International Business Machines Corp., 2000
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ */
++
++#include <linux/dprobes.h>
++#include <linux/dcache.h>
++#include <linux/pagemap.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/slab.h>
++#include <linux/vmalloc.h>
++#include <linux/swap.h>
++#include <linux/smp.h>
++#ifdef CONFIG_MAGIC_SYSRQ
++#include <linux/sysrq.h>
++#endif
++
++#include <asm/semaphore.h>
++#include <asm/uaccess.h>
++#include <asm/errno.h>
++#ifdef CONFIG_DEBUGREG
++#include <asm/debugreg.h>
++#endif
++
++#ifdef CONFIG_SMP
++struct dprobes_struct dprobes_set[NR_CPUS];
++#else
++struct dprobes_struct dprobes;
++#endif
++
++struct dp_module_struct *dp_module_list = NULL;
++DECLARE_RWSEM(dp_modlist_sem);
++
++byte_t *dp_heap = NULL;
++unsigned long dp_num_heap = 0;
++rwlock_t dp_heap_lock = RW_LOCK_UNLOCKED;
++
++unsigned long *dp_gv = NULL;
++unsigned long dp_num_gv = 0;
++rwlock_t dp_gv_lock = RW_LOCK_UNLOCKED;
++
++
++#ifdef CONFIG_MAGIC_SYSRQ
++static struct work_struct dprobes_work;
++static struct workqueue_struct *dprobes_wq;
++static void dprobes_sysrq( int , struct pt_regs *, struct tty_struct *);
++static struct sysrq_key_op key_op = {
++ .handler = &dprobes_sysrq,
++ .help_msg = "remoVe_dynamic_probes",
++ .action_msg = "Deactivating all probepoints",
++};
++unsigned long emergency_remove = 0; /*Flag to disable active dprobes*/
++#endif
++
++
++void dprobes_code_start(void) { }
++
++static void init_trace_hdr(struct dp_module_struct *m)
++{
++ unsigned long log_flags = m->pgm.flags & DP_LOG_MASK;
++ unsigned long mask = 0;
++ unsigned short len = sizeof(m->hdr);
++
++ mask |= DP_HDR_MAJOR | DP_HDR_MINOR;
++#ifdef CONFIG_SMP
++ mask |= DP_HDR_CPU;
++ len += sizeof(int);
++#endif
++ if (log_flags & DP_LOG_PID) {
++ mask |= DP_HDR_PID;
++ len += sizeof(pid_t);
++ }
++ if (log_flags & DP_LOG_UID) {
++ mask |= DP_HDR_UID;
++ len += sizeof(uid_t);
++ }
++ if (log_flags & DP_LOG_CS_EIP) {
++ mask |= DP_HDR_CS | DP_HDR_EIP;
++ len += (sizeof(unsigned short) + sizeof(unsigned long));
++ }
++ if (log_flags & DP_LOG_SS_ESP) {
++ mask |= DP_HDR_SS | DP_HDR_ESP;
++ len += (sizeof(unsigned short) + sizeof(unsigned long));
++ }
++ if (log_flags & DP_LOG_TSC) {
++ mask |= DP_HDR_TSC;
++ len += sizeof(struct timeval);
++ }
++ if (log_flags & DP_LOG_PROCNAME) {
++ mask |= DP_HDR_PROCNAME;
++ len += 16; /* size of task_struct.comm */
++ }
++ m->hdr.facility_id = DP_TRACE_HDR_ID;
++ m->hdr.mask = mask;
++ m->hdr.len = len;
++ /* Adjust the logmax to take care of log header bytes */
++ m->pgm.logmax += len;
++ return;
++}
++
++#if defined(CONFIG_TRACE) || defined(CONFIG_TRACE_MODULE)
++#include <linux/trace.h>
++static inline void get_trace_id(struct dp_module_struct *m)
++{
++ m->trace_id = trace_create_event(m->pgm.name, NULL, CUSTOM_EVENT_FORMAT_TYPE_HEX, NULL);
++}
++static inline void free_trace_id(struct dp_module_struct *m)
++{
++ trace_destroy_event(m->trace_id);
++}
++#else
++#define get_trace_id(m)
++#define free_trace_id(m)
++#endif
++
++static inline struct dp_module_struct * find_mod_by_inode(struct inode *inode)
++{
++ struct dp_module_struct *m;
++ for (m = dp_module_list; m; m = m->next) {
++ if (m->inode == inode)
++ break;
++ }
++ return m;
++}
++
++static inline struct dp_module_struct * find_mod_by_name(const char * name)
++{
++ struct dp_module_struct *m;
++ for (m = dp_module_list; m ; m = m->next) {
++ if (!strcmp(m->pgm.name, name))
++ break;
++ }
++ return m;
++}
++
++static inline void link_module(struct dp_module_struct *m)
++{
++ m->next = dp_module_list;
++ dp_module_list = m;
++ return;
++}
++
++static inline void unlink_module(struct dp_module_struct *m)
++{
++ struct dp_module_struct *prev;
++
++ if (m == dp_module_list) {
++ dp_module_list = m->next;
++ return;
++ }
++ prev = dp_module_list;
++ while (prev->next && prev->next != m)
++ prev = prev->next;
++ if (prev->next)
++ prev->next = prev->next->next;
++ return;
++}
++
++/*
++ * copied from kernel/module.c find_module().
++ */
++static struct module * find_kmodule(const char *name)
++{
++ struct module *kmod = NULL;
++#if 0
++ extern struct list_head *modules;
++ list_for_each_entry(kmod, modules, list) {
++ if (!strcmp(kmod->name, name))
++ break;
++ }
++#endif
++ return kmod;
++}
++
++/*
++ * Returns the index of the last dp_record that lies in the same page as the
++ * dp_record at start index.
++ */
++static inline unsigned short
++find_last(struct dp_module_struct *m, unsigned short start)
++{
++ int i;
++ struct dp_record_struct * rec = &m->rec[start];
++ unsigned short num_recs = m->pgm.num_points;
++ unsigned long end_offset = (rec->point.offset & PAGE_MASK) + PAGE_SIZE;
++
++ for (i = start; i < num_recs && rec->point.offset < end_offset; i++, rec++)
++ ;
++
++ return i;
++}
++
++/*
++ * Unlinks the given module from dp_module_list and frees all the memory
++ * taken by it.
++ */
++static int free_dp_module(struct dp_module_struct *m)
++{
++ /* unlink mod */
++ unlink_module(m);
++
++ if (m->flags & DP_MOD_FLAGS_LARGE_REC)
++ vfree(m->rec);
++ else
++ kfree(m->rec);
++ kfree(m->lv);
++ kfree(m->pgm.rpn_code);
++ putname(m->pgm.name);
++ kfree(m);
++ return 0;
++}
++
++/*
++ * Allocates dp_record_structs, copies dp_point_structs from user space into
++ * the dp_record_structs in the kernel space.
++ */
++static int
++copy_point_from_user (struct dp_module_struct *m, struct dp_pgm_struct *pgm)
++{
++ unsigned short count = m->pgm.num_points, i;
++ struct dp_record_struct *rec;
++ unsigned long rec_size;
++
++ if (!count)
++ return 0;
++
++ rec_size = count * sizeof(*rec);
++ if (rec_size > PAGE_SIZE) {
++ rec = vmalloc(rec_size);
++ m->flags |= DP_MOD_FLAGS_LARGE_REC;
++ } else {
++ rec = kmalloc(rec_size, GFP_KERNEL);
++ }
++ if (!rec)
++ return -ENOMEM;
++ memset(rec, 0, rec_size);
++ m->rec = rec;
++
++ for (i = 0; i < count; i++, rec++) {
++ if (copy_from_user (&rec->point, &pgm->point[i],
++ sizeof (struct dp_point_struct))) {
++ if (m->flags & DP_MOD_FLAGS_LARGE_REC)
++ vfree(m->rec);
++ else
++ kfree(m->rec);
++ m->rec = NULL;
++ return -EFAULT;
++ }
++ rec->status = DP_REC_STATUS_COMPILED;
++ rec->lock = SPIN_LOCK_UNLOCKED;
++ rec->mod = m;
++ rec->count = -rec->point.passcount;
++ if (!rec->point.maxhits) rec->point.maxhits =DEFAULT_MAXHITS;
++ }
++ return 0;
++}
++
++/*
++ * We may have to realloc global variables area.
++ */
++static int check_gv_space(unsigned short num_gv)
++{
++ unsigned long *tmp, *tmp1;
++ unsigned long eflags;
++
++ if (num_gv <= dp_num_gv)
++ return 0;
++
++ tmp = kmalloc(num_gv * sizeof(*tmp), GFP_KERNEL);
++ if (!tmp)
++ return -ENOMEM;
++ memset(tmp, 0, num_gv * sizeof(*tmp));
++
++ write_lock_irqsave(&dp_gv_lock, eflags);
++ if (dp_num_gv) {
++ memcpy((void *)tmp, (void *)dp_gv,
++ sizeof(unsigned long)*dp_num_gv);
++ }
++ tmp1 = dp_gv;
++ dp_gv = tmp;
++ dp_num_gv = num_gv;
++ write_unlock_irqrestore(&dp_gv_lock, eflags);
++
++ kfree(tmp1);
++ return 0;
++}
++
++static int check_heap_space(unsigned short num_heap)
++{
++ byte_t *tmp, *tmp1;
++ unsigned long eflags;
++
++ if (num_heap * num_online_cpus() <= dp_num_heap)
++ return 0;
++
++ tmp = kmalloc(num_heap * sizeof(*tmp) * num_online_cpus(), GFP_KERNEL);
++ if (!tmp)
++ return -ENOMEM;
++
++ write_lock_irqsave(&dp_heap_lock, eflags);
++ tmp1 = dp_heap;
++ dp_heap = tmp;
++ dp_num_heap = num_heap * num_online_cpus();
++ write_unlock_irqrestore(&dp_heap_lock, eflags);
++ kfree(tmp1);
++
++ return 0;
++}
++
++/*
++ * Allocates a dp_module_struct and copies the dp_pgm_struct from user space
++ * into it, in kernel space.
++ */
++static int
++copy_pgm_from_user(struct dp_module_struct **mod, struct dp_pgm_struct *pgm)
++{
++ int retval = 0;
++ struct dp_module_struct *m;
++
++ pgm->name = getname(pgm->name);
++ if (IS_ERR(pgm->name)) {
++ retval = PTR_ERR(pgm->name);
++ goto error;
++ }
++
++ m = find_mod_by_name(pgm->name);
++ if (m) {
++ retval = -EEXIST;
++ goto err1;
++ }
++
++ m = kmalloc(sizeof(*m), GFP_KERNEL);
++ if (!m) {
++ retval = -ENOMEM;
++ goto err1;
++ }
++ memset(m, 0, sizeof(*m));
++
++ m->pgm = *pgm;
++
++ if (m->pgm.logmax > LOG_SIZE) m->pgm.logmax = DEFAULT_LOGMAX;
++ if (m->pgm.jmpmax > JMP_MAX) m->pgm.jmpmax = DEFAULT_JMPMAX;
++ if (m->pgm.ex_logmax > EX_LOG_SIZE) m->pgm.ex_logmax = DEFAULT_EX_LOG_MAX;
++ if (m->pgm.heapmax > HEAPMAX)
++ m->pgm.heapmax = DEFAULT_HEAPMAX;
++
++ if (pgm->num_lv != 0) {
++ m->lv = kmalloc(pgm->num_lv * sizeof(*m->lv), GFP_KERNEL);
++ if (!m->lv) {
++ retval = -ENOMEM;
++ goto err2;
++ }
++ memset(m->lv, 0, pgm->num_lv * sizeof(*m->lv));
++ }
++
++ if (pgm->num_gv != 0) {
++ retval = check_gv_space(pgm->num_gv);
++ if (retval < 0)
++ goto err3;
++ }
++
++ if (pgm->heapmax != 0) {
++ retval = check_heap_space(pgm->heapmax);
++ if (retval < 0)
++ goto err3;
++ }
++
++ m->pgm.rpn_code = kmalloc(pgm->rpn_length, GFP_KERNEL);
++ if (!m->pgm.rpn_code) {
++ retval = -ENOMEM;
++ goto err3;
++ }
++ if (copy_from_user(m->pgm.rpn_code, pgm->rpn_code, pgm->rpn_length)) {
++ retval = -EFAULT;
++ goto err4;
++ }
++
++ retval = copy_point_from_user(m, pgm);
++ if (retval)
++ goto err4;
++ *mod = m;
++ return 0;
++
++err4: kfree(m->pgm.rpn_code);
++err3: kfree(m->lv);
++err2: kfree(m);
++err1: putname(pgm->name);
++error: return retval;
++
++}
++
++/*
++ * Fills up the base and end fields of the given dp_module_struct with the
++ * starting and end addresses of the given kernel module, accounting for
++ * correct alignment of the code segment.
++ */
++static inline int get_kmod_bounds(struct dp_module_struct *m, struct module *kmod)
++{
++ m->base = (unsigned long)kmod->module_core;
++ m->end = kmod->core_size;
++ return 1;
++}
++
++/*
++ * Fills up the base and end fields of the dp_module_struct with the details
++ * of where the corresponding kernel module is loaded.
++ */
++static inline int get_mod_load_addr(struct dp_module_struct *m)
++{
++ struct module * kmod;
++ kmod = find_kmodule(m->pgm.name);
++ if (!kmod)
++ return 0; // module not yet loaded.
++ return get_kmod_bounds(m, kmod);
++}
++
++/*
++ * Fills up the base and end fields of dp_module_struct if this module is
++ * for a kernel or a kernel module.
++ */
++static int get_load_addr(struct dp_module_struct *m)
++{
++ if (m->pgm.flags & DP_MODTYPE_KMOD)
++ return get_mod_load_addr(m);
++ m->base = PAGE_OFFSET;
++ m->end = init_mm.end_code - PAGE_OFFSET;
++ return 1;
++}
++
++/* last thing we do should be to remove the breakpoint setting. */
++static int insert_probe(byte_t *addr, struct dp_record_struct *rec,
++ struct dp_module_struct *m, struct page * page,
++ struct vm_area_struct *vma)
++{
++ /*
++ * remove residual status bits, applicable only to probes in kernel
++ * modules when they are being reinserted.
++ */
++ rec->status &= ~(DP_REC_STATUS_REMOVED | DP_REC_STATUS_DISABLED);
++ if(!(m->pgm.flags & DP_MODTYPE_USER))
++ return __insert_probe(addr, rec, m, page);
++ else
++ return insert_probe_userspace(addr, rec, page, vma);
++}
++
++/* first thing we do should be to remove the breakpoint setting. */
++static int remove_probe(byte_t * addr, struct dp_record_struct *rec,
++ struct dp_module_struct *m, struct page * page,
++ struct vm_area_struct *vma)
++{
++ unsigned long eflags;
++ spin_lock_irqsave(&rec->lock, eflags);
++ if(!(m->pgm.flags & DP_MODTYPE_USER))
++ __remove_probe(addr, rec);
++ else
++ remove_probe_userspace(addr, rec, page, vma);
++ rec->status &= ~(DP_REC_STATUS_ACTIVE | DP_REC_STATUS_MISMATCH);
++ rec->status |= DP_REC_STATUS_REMOVED;
++ spin_unlock_irqrestore(&rec->lock, eflags);
++ return 0;
++}
++
++/*
++ * Creates hash table links and inserts all records in kernel / kmodule.
++ */
++static int insert_all_recs_k(struct dp_module_struct *m)
++{
++ struct dp_record_struct * rec;
++ byte_t * addr;
++ int i;
++ unsigned short num_recs = m->pgm.num_points;
++
++ for (i = 0, rec = m->rec; i < num_recs; i++, rec++) {
++ if (!(rec->point.address_flag & DP_ADDRESS_ABSOLUTE)) {
++ rec->point.offset += m->base;
++ }
++ addr = (byte_t *)((unsigned long)rec->point.offset);
++ insert_probe(addr, rec, m, NULL, NULL);
++ }
++ return 0;
++}
++
++/*
++ * Removes probes in kernel / kmodules: all _will_ be in memory.
++ * Note that rec->point.offset is now the linear address itself.
++ */
++static int remove_all_recs_k(struct dp_module_struct *m)
++{
++ struct dp_record_struct * rec;
++ int i;
++ byte_t * addr;
++ unsigned short num_recs = m->pgm.num_points;
++
++ for (i = 0, rec = m->rec; i < num_recs; i++, rec++) {
++ addr = (byte_t *) ((unsigned long)rec->point.offset);
++ if (rec->status & DP_REC_STATUS_ACTIVE) {
++ remove_probe(addr, rec, m, NULL, NULL);
++ }
++ if (!(rec->point.address_flag & DP_ADDRESS_ABSOLUTE))
++ rec->point.offset -= m->base;
++
++ }
++ m->base = m->end = 0;
++ return 0;
++}
++
++/*
++ * mod->pgm.name is already in kernel space.
++ */
++static int insert_k (struct dp_module_struct * m)
++{
++ if (!get_load_addr(m))
++ return 0;
++ return insert_all_recs_k(m);
++}
++
++static int remove_k(struct dp_module_struct *m)
++{
++ return remove_all_recs_k(m);
++}
++
++/*
++ * Inserts user probes into the page. Insert probes that lie in the same page
++ * after waiting for the page to be unlocked, if it was locked.
++ */
++static unsigned short insert_recs_in_page (struct dp_module_struct *m,
++ struct page * page, unsigned short start, unsigned short end,
++ struct vm_area_struct *vma)
++{
++ unsigned long alias;
++ byte_t * addr;
++ unsigned short i;
++ struct dp_record_struct * rec = &m->rec[start];
++
++ if (!page) {
++ return 1;
++ }
++ wait_on_page_locked(page);
++ if (!PageUptodate(page)) { /* we could probably retry readpage here. */
++ return 1;
++ }
++
++ alias = (unsigned long)kmap(page);
++ for (i = start; i < end; i++, rec++) {
++ addr = (byte_t *) (alias +
++ (unsigned long)(rec->point.offset & ~PAGE_MASK));
++ insert_probe(addr, rec, m, page, vma);
++ }
++ kunmap(page);
++ return 1;
++}
++
++/*
++ * Removes user probes that lie in the same page after waiting for the page to
++ * be unlocked, if it was locked. Make sure the page does not get swapped out.
++ */
++static unsigned short remove_recs_from_page (struct dp_module_struct *m,
++ struct page * page, unsigned short start, unsigned short end,
++ struct vm_area_struct *vma)
++{
++ struct dp_record_struct * rec = &m->rec[start];
++ unsigned long alias;
++ byte_t * addr;
++ unsigned short i;
++
++ if (!page)
++ return 1;
++ wait_on_page_locked(page);
++ lock_page(page);
++ alias = (unsigned long)kmap(page);
++ for (i = start; i < end; i++, rec++) {
++ addr = (byte_t *) (alias +
++ (unsigned long)(rec->point.offset & ~PAGE_MASK));
++ remove_probe(addr, rec, m, page, vma);
++ }
++ kunmap(page);
++ unlock_page(page);
++
++ return 1;
++}
++
++typedef unsigned short
++(*process_recs_func_t)(struct dp_module_struct *m, struct page *page,
++ unsigned short start, unsigned short end, struct vm_area_struct *);
++
++static inline pte_t *get_one_pte(struct mm_struct *mm, unsigned long addr)
++{
++ pgd_t * pgd;
++ pmd_t * pmd;
++ pte_t * pte = NULL;
++
++ pgd = pgd_offset(mm, addr);
++ if (pgd_none(*pgd))
++ goto end;
++ if (pgd_bad(*pgd)) {
++ pgd_ERROR(*pgd);
++ pgd_clear(pgd);
++ goto end;
++ }
++
++ pmd = pmd_offset(pgd, addr);
++ if (pmd_none(*pmd))
++ goto end;
++ if (pmd_bad(*pmd)) {
++ pmd_ERROR(*pmd);
++ pmd_clear(pmd);
++ goto end;
++ }
++
++ pte = pte_offset_kernel(pmd, addr);
++ if (pte_none(*pte))
++ pte = NULL;
++end:
++ return pte;
++}
++
++struct dp_page_locator_struct {
++ struct list_head list;
++ struct mm_struct *mm;
++ unsigned long addr;
++};
++
++/*
++ * Get the page corresponding to a given virtual addr, marking it as needing
++ * to be brought in later and checked, if it has been swapped out.
++ * It doesn't bring in discarded pages as a_ops->readpage
++ * takes care of that.
++ */
++struct page * dp_vaddr_to_page(struct vm_area_struct *vma, unsigned long addr,
++struct list_head *swapped_list_head)
++{
++ struct mm_struct *mm = vma->vm_mm;
++ pte_t *pte = get_one_pte(mm, addr);
++ struct page *page;
++ struct dp_page_locator_struct *pageloc;
++
++ if (!pte) {
++ return NULL;
++ }
++ /*
++ * If the page has been swapped out, we want to bring it in.
++ * We can't do it right away since we are holding spin locks.
++ * So just queue it up on the swapped pages list.
++ */
++ if (!pte_present(*pte)) {
++ if (swapped_list_head == NULL) return NULL;
++ pageloc = kmalloc(sizeof(*pageloc), GFP_ATOMIC);
++ if (!pageloc) return NULL;
++ pageloc->mm = mm;
++ pageloc->addr = addr;
++ atomic_inc(&mm->mm_users); /* so that mm won't go away */
++ list_add(&pageloc->list, swapped_list_head);
++ return NULL;
++ }
++
++ page = pte_page(*pte);
++ page_cache_get(page); /* TODO: Check if we need the page_cache_lock */
++
++ /*
++ * If this is a COW page, then we want to make sure that any changes
++ * made to the page get written back when swapped out. We need to do
++ * the following explicitly because dprobes will write via an alias.
++ */
++ if (IS_COW_PAGE(page, vma->vm_file->f_dentry->d_inode)) {
++ if (PageSwapCache(page))
++ delete_from_swap_cache(page);
++ set_pte(pte, pte_mkdirty(*pte));
++ }
++
++ return page;
++}
++
++/*
++ * Tracks all COW pages for the page containing the specified probe point
++ * and inserts/removes all the probe points for that page.
++ * Avoid the inserts/removes if the page's inode matches the module's inode.
++ * since that means that it is the original page and not a copied page.
++ */
++static int process_recs_in_cow_pages (struct dp_module_struct *m,
++ unsigned short start, unsigned short end,
++ process_recs_func_t process_recs_in_page)
++{
++ unsigned long offset = m->rec[start].point.offset;
++ struct vm_area_struct *vma;
++ struct page *page;
++ unsigned long addr;
++ struct mm_struct *mm;
++ struct address_space *mapping = m->inode->i_mapping;
++ struct list_head *head = &mapping->i_mmap;
++ struct list_head swapped_pages_list, *ptr;
++ struct dp_page_locator_struct *pageloc;
++
++ INIT_LIST_HEAD(&swapped_pages_list);
++ down(&mapping->i_shared_sem);
++
++ list_for_each_entry(vma, head, shared) {
++ mm = vma->vm_mm;
++ spin_lock(&mm->page_table_lock);
++
++ /* Locate the page in this vma */
++ addr = vma->vm_start - (vma->vm_pgoff << PAGE_SHIFT) + offset;
++ /*
++ * The following routine would also bring in the page if it is
++ * swapped out
++ */
++ page = dp_vaddr_to_page (vma, addr, &swapped_pages_list);
++ spin_unlock(&mm->page_table_lock);
++ if (!page)
++ continue;
++ if (IS_COW_PAGE(page, m->inode)) {
++ (*process_recs_in_page)(m, page, start, end, vma);
++ }
++ page_cache_release(page); /* TODO: Do we need a lock ? */
++ }
++ up(&mapping->i_shared_sem);
++
++ while (!list_empty(&swapped_pages_list)) {
++ ptr = swapped_pages_list.next;
++ pageloc = list_entry(ptr, struct dp_page_locator_struct, list);
++ mm = pageloc->mm;
++ addr = pageloc->addr;
++ down_read(&mm->mmap_sem);
++ /* first make sure that the vma didn't go away */
++ vma = find_vma(mm, addr);
++ /* and that this is the same one we had */
++ if (vma && vma->vm_file &&
++ (vma->vm_file->f_dentry->d_inode == m->inode) &&
++ (vma->vm_start - (vma->vm_pgoff << PAGE_SHIFT) + offset
++ == addr)) {
++ if (handle_mm_fault(mm, vma, addr, 0) <= 0) {
++ goto next;
++ }
++ page = dp_vaddr_to_page(vma, addr, NULL);
++ if (!page) /* could this ever happen ? */ {
++ goto next;
++ }
++ if (IS_COW_PAGE(page, m->inode)) {
++ (*process_recs_in_page)(m, page, start, end, vma);
++ }
++ page_cache_release(page); /*TODO: Do we need a lock ?*/
++ }
++next:
++ up_read(&mm->mmap_sem);
++ mmput(mm);
++ list_del(ptr);
++ kfree(pageloc);
++ }
++ return 1;
++}
++/*
++ * find_get_vma walks through the list of process private mappings and
++ * returns the pointer to vma containing the offset if found.
++ */
++static struct vm_area_struct * find_get_vma(unsigned long offset,
++ struct address_space *mapping)
++{
++ struct vm_area_struct *vma = NULL;
++ struct list_head *head = &mapping->i_mmap;
++ struct mm_struct *mm;
++ unsigned long start, end;
++
++ down(&mapping->i_shared_sem);
++ list_for_each_entry(vma, head, shared) {
++ mm = vma->vm_mm;
++ spin_lock(&mm->page_table_lock);
++ start = vma->vm_start - (vma->vm_pgoff << PAGE_SHIFT);
++ end = vma->vm_end - (vma->vm_pgoff << PAGE_SHIFT);
++ spin_unlock(&mm->page_table_lock);
++ if ((start + offset) < end) {
++ up(&mapping->i_shared_sem);
++ return vma;
++ }
++ }
++ up(&mapping->i_shared_sem);
++ return NULL;
++}
++/*
++ * physical insertion/removal of probes in the actual pages of the module.
++ * Register user space probes before actually instering probes in the page for
++ * a given pair of inode and offset.
++ *
++ */
++static int process_recs(struct dp_module_struct *m,
++ process_recs_func_t process_recs_in_page)
++{
++ struct address_space * mapping = m->inode->i_mapping;
++ struct page *page;
++ unsigned short i = 0, j;
++ unsigned short num_recs = m->pgm.num_points;
++ unsigned short start, end;
++ struct vm_area_struct *vma = NULL;
++
++ while (i < num_recs) {
++ start = i;
++ i = end = find_last(m, start);
++ vma = find_get_vma(m->rec[start].point.offset, mapping);
++ page = find_get_page(mapping,
++ m->rec[start].point.offset >> PAGE_CACHE_SHIFT);
++
++ for (j = start; j < end; j++) {
++ if (process_recs_in_page == insert_recs_in_page)
++ register_userspace_probes(&m->rec[j]);
++ }
++
++ (*process_recs_in_page)(m, page, start, end, vma);
++ if (page)
++ page_cache_release(page); /* TODO: Lock needed ? */
++ process_recs_in_cow_pages(m, start, end, process_recs_in_page);
++
++ for (j = start; j < end; j++) {
++ if (process_recs_in_page == remove_recs_from_page)
++ unregister_userspace_probes(&m->rec[j]);
++ }
++ }
++ return 1;
++}
++
++static inline int insert_recs(struct dp_module_struct *m)
++{
++ return process_recs(m, insert_recs_in_page);
++}
++
++static inline int remove_recs(struct dp_module_struct *m)
++{
++ return process_recs(m, remove_recs_from_page);
++}
++
++/*
++ * Gets exclusive write access to the given inode to ensure that the file
++ * on which probes are currently applied does not change. Use the function,
++ * deny_write_access_to_inode() we added in fs/namei.c.
++ */
++extern int deny_write_access_to_inode(struct inode * inode);
++static inline int ex_write_lock(struct inode * inode)
++{
++ return deny_write_access_to_inode(inode);
++}
++
++/*
++ * Called when removing user space probes to release the write lock on the
++ * inode.
++ */
++static inline int ex_write_unlock(struct inode * inode)
++{
++ atomic_inc(&inode->i_writecount);
++ return 0;
++}
++
++/*
++ * Inserts user space probes.
++ *
++ * mod->pgm.name is already in user space. This function leaves with the
++ * dentry held and taking with the inode writelock held to ensure that the
++ * file on which probes are currently active does not change from under us.
++ */
++static int insert_u (struct dp_module_struct * m)
++{
++ struct address_space *as;
++ int error;
++
++ error = user_path_walk(m->pgm.name, &m->nd);
++ if (error)
++ return error;
++
++ m->inode = m->nd.dentry->d_inode;
++ as = m->inode->i_mapping;
++
++ error = ex_write_lock(m->inode);
++ if (error) {
++ path_release(&m->nd);
++ return error;
++ }
++
++ /* switch i_op to hook into readpage */
++ m->ori_a_ops = as->a_ops;
++ m->dp_a_ops = *as->a_ops;
++ m->dp_a_ops.readpage = dp_readpage;
++ as->a_ops = &m->dp_a_ops;
++
++ /* physically insert the probes in existing pages */
++ insert_recs(m);
++
++ return 0;
++}
++
++/*
++ * Removes user space probes.
++ */
++static int remove_u(struct dp_module_struct *m)
++{
++ m->inode->i_mapping->a_ops = m->ori_a_ops; /* unhook readpage */
++ remove_recs(m); /* remove the probes */
++ ex_write_unlock(m->inode);
++ path_release(&m->nd); /* release path */
++ return 0;
++}
++
++/*
++ * Entry routine for the insert command. It checks for existing probes in this
++ * module to avoid any duplications, creates the necessary data structures
++ * calls insert_k() or insert_u() depending on whether it is a kernel space or
++ * user space probe.
++ */
++static int do_insert(struct dp_pgm_struct *upgm)
++{
++ struct dp_module_struct *m;
++ struct dp_pgm_struct *pgm;
++ char *uname, *kname;
++ int retval;
++
++ if (!upgm)
++ return -EINVAL;
++
++ pgm = kmalloc (sizeof(*pgm), GFP_KERNEL);
++ if (!pgm)
++ return -ENOMEM;
++
++ if (copy_from_user(pgm, upgm, sizeof(*pgm))) {
++ retval = -EFAULT;
++ goto done;
++ }
++
++ if (!pgm->name || !pgm->num_points) {
++ retval = -EINVAL;
++ goto done;
++ }
++ uname = pgm->name;
++ retval = copy_pgm_from_user(&m, pgm);
++ if (retval)
++ goto done;
++
++ try_module_get(THIS_MODULE);
++ down_write(&dp_modlist_sem);
++ link_module(m);
++ if (pgm->flags & DP_MODTYPE_USER) {
++ /* kludge: needed to use user_path_walk. */
++ kname = m->pgm.name;
++ m->pgm.name = uname;
++ retval = insert_u(m);
++ m->pgm.name = kname;
++ } else {
++ retval = insert_k(m);
++ }
++
++ if (retval) {
++ free_dp_module(m);
++ module_put(THIS_MODULE);
++ } else {
++ init_trace_hdr(m);
++ get_trace_id(m);
++ }
++ up_write(&dp_modlist_sem);
++
++done: kfree(pgm);
++ return retval;
++}
++
++static int remove_module(struct dp_module_struct *m)
++{
++ /* remove probes */
++ if (m->pgm.flags & DP_MODTYPE_USER) {
++ remove_u(m);
++ } else {
++ remove_k(m);
++ }
++ free_trace_id(m);
++
++ /* free module and all its contents */
++ free_dp_module(m);
++ return 0;
++}
++
++/*
++ * Entry routine for removing probes.
++ */
++static int do_remove (unsigned long cmd, char * uname)
++{
++ struct dp_module_struct *m, *tmp;
++ char *name;
++
++ if (cmd & DP_ALL) {
++ down_write(&dp_modlist_sem);
++ m = dp_module_list;
++ while (m) {
++ tmp = m->next;
++ remove_module (m);
++ m = tmp;
++ module_put(THIS_MODULE);
++ }
++ up_write(&dp_modlist_sem);
++ } else {
++ if (!uname)
++ return -EINVAL;
++ name = getname(uname);
++ if (IS_ERR(name))
++ return (PTR_ERR(name));
++
++ down_write(&dp_modlist_sem);
++ m = find_mod_by_name (name);
++ putname(name);
++ if (!m) {
++ up_write(&dp_modlist_sem);
++ return -EINVAL;
++ }
++ remove_module (m);
++ module_put(THIS_MODULE);
++ up_write(&dp_modlist_sem);
++ }
++ return 0;
++}
++
++static int
++dp_get_globalvars(unsigned long cmd, struct dp_getvars_in_struct * req,
++ byte_t * result)
++{
++ unsigned long num_vars;
++ unsigned long *tmp_gv = NULL;
++ unsigned long eflags;
++ unsigned short orig_to = req->to;
++ int retval = 0;
++
++ read_lock_irqsave(&dp_gv_lock, eflags);
++ if (!(cmd & DP_GETVARS_INDEX)) {
++ req->from = 0;
++ req->to = dp_num_gv - 1;
++ num_vars = dp_num_gv;
++ } else if (req->from >= dp_num_gv) {
++ num_vars = 0;
++ } else if (req->to >= dp_num_gv) {
++ req->to = dp_num_gv - 1;
++ num_vars = req->to - req->from + 1;
++ } else {
++ num_vars = req->to - req->from + 1;
++ }
++
++ if (cmd & DP_GETVARS_RESET) {
++ memset(dp_gv + req->from, 0, num_vars*sizeof(unsigned long));
++ read_unlock_irqrestore(&dp_gv_lock, eflags);
++ goto done;
++ }
++
++ read_unlock_irqrestore(&dp_gv_lock, eflags);
++
++ if (num_vars) {
++ tmp_gv = kmalloc(num_vars * sizeof(*tmp_gv), GFP_KERNEL);
++ if (!tmp_gv) {
++ retval = -ENOMEM;
++ goto done;
++ }
++
++ read_lock_irqsave(&dp_gv_lock, eflags);
++
++ /*
++ * dp_num_gv can change(only increase) during kmalloc. In such
++ * a case, only copy the previously determined number
++ * of global vars.
++ */
++ memcpy((void *)tmp_gv, (void *)(dp_gv + req->from),
++ sizeof(unsigned long) * num_vars);
++ read_unlock_irqrestore(&dp_gv_lock, eflags);
++ }
++
++ req->returned += (num_vars + 1) * sizeof(unsigned long);
++ if (req->returned < req->allocated) {
++ if (copy_to_user(result, &num_vars, sizeof(unsigned long)))
++ retval = -EFAULT;
++ else if (num_vars) {
++ if (copy_to_user(result + sizeof(unsigned long),
++ tmp_gv, sizeof(unsigned long) * num_vars))
++ retval = -EFAULT;
++ kfree(tmp_gv);
++ }
++ }
++ else
++ retval = -ENOMEM;
++done:
++ req->to = orig_to;
++ return retval;
++}
++
++static int
++cp_lvars_for_mod(struct dp_module_struct *m, struct dp_getvars_in_struct * req,
++ byte_t * result, unsigned long var_bytes)
++{
++ unsigned long len = 0;
++ unsigned long offset = 0;
++ unsigned long num_vars = req->to - req->from + 1;
++
++ len = sizeof(struct dp_getvars_local_out_struct) +
++ var_bytes + strlen(m->pgm.name) + 1;
++ req->returned += len;
++
++ if (req->returned < req->allocated) {
++ if (copy_to_user(result, &len, sizeof(unsigned long)))
++ return -EFAULT;
++ else
++ offset = sizeof(unsigned long);
++
++ if (copy_to_user(result+offset, &num_vars,
++ sizeof(unsigned long)))
++ return -EFAULT;
++ else
++ offset += sizeof(unsigned long);
++
++ if (copy_to_user(result+offset, m->lv + req->from, var_bytes))
++ return -EFAULT;
++ else
++ offset += var_bytes;
++
++ if (copy_to_user(result+offset, m->pgm.name,
++ strlen(m->pgm.name)+1))
++ return -EFAULT;
++ }
++ else
++ return -ENOMEM;
++
++ return 0;
++}
++
++static int
++getvars_m(struct dp_module_struct *m, unsigned long cmd,
++ struct dp_getvars_in_struct *req, byte_t *result)
++{
++ int retval = 0;
++ unsigned long var_bytes;
++ unsigned short orig_to = req->to;
++
++ if (cmd & DP_GETVARS_INDEX) {
++ if (req->from >= m->pgm.num_lv)
++ return 0;
++ if(req->to >= m->pgm.num_lv)
++ req->to = m->pgm.num_lv - 1;
++ }
++ else {
++ req->from = 0;
++ req->to = m->pgm.num_lv - 1;
++ }
++ var_bytes = (req->to - req->from + 1) * sizeof(unsigned long);
++
++ if (cmd & DP_GETVARS_RESET)
++ memset(m->lv + req->from, 0, var_bytes);
++ else
++ retval = cp_lvars_for_mod(m, req, result, var_bytes);
++ req->to = orig_to;
++ return retval;
++}
++
++static int
++do_getvars(unsigned long cmd, struct dp_getvars_in_struct * ureq,
++ byte_t * result)
++{
++ struct dp_module_struct *m;
++ struct dp_getvars_in_struct req;
++ int retval = 0;
++
++ if (!ureq)
++ return -EINVAL;
++
++ if (copy_from_user(&req, ureq, sizeof(req))) {
++ retval = -EFAULT;
++ goto failed;
++ }
++ if (req.name) {
++ req.name = getname(req.name);
++ if (IS_ERR(req.name)) {
++ retval = PTR_ERR(req.name);
++ goto failed;
++ }
++ }
++ else
++ req.name = NULL;
++
++ req.returned = 0;
++ if (cmd & DP_GETVARS_GLOBAL) {
++ retval = dp_get_globalvars(cmd, &req, result);
++ if (retval)
++ goto done;
++ }
++
++ if (cmd & DP_GETVARS_LOCAL) {
++ down_read(&dp_modlist_sem);
++ if (cmd & DP_ALL) {
++ for (m = dp_module_list; m; m = m->next) {
++ if (!m->lv)
++ continue;
++ retval = getvars_m(m, cmd, &req, result + req.returned);
++ if (retval && (retval != -ENOMEM))
++ break;
++ }
++ } else if (req.name) {
++ m = find_mod_by_name(req.name);
++ if (m && m->lv)
++ retval = getvars_m(m, cmd, &req, result + req.returned);
++ else
++ retval = -EINVAL;
++ }
++ up_read(&dp_modlist_sem);
++ }
++done:
++ if (copy_to_user(&ureq->returned, &req.returned,
++ sizeof(unsigned long)))
++ retval = -EFAULT;
++
++ if (req.name)
++ putname(req.name);
++failed:
++ return retval;
++}
++
++#define dp_copy_to_user(destination, source, length) \
++do { \
++ if (copy_to_user(destination, source, length)) \
++ return -EFAULT; \
++ destination += length; \
++} while(0)
++
++static int query_r(struct dp_module_struct *m, byte_t **result)
++{
++ struct dp_outrec_struct outrec;
++ struct dp_record_struct * rec;
++ unsigned short i;
++ unsigned short num_recs = m->pgm.num_points;
++ byte_t * offset = *result;
++ unsigned long eflags;
++
++ for (i = 0, rec = m->rec; i < num_recs; i++, rec++) {
++ spin_lock_irqsave(&rec->lock, eflags);
++ outrec.status = rec->status;
++ outrec.count = rec->count;
++ outrec.dbregno = rec->dbregno;
++ outrec.point = rec->point;
++ spin_unlock_irqrestore(&rec->lock, eflags);
++ dp_copy_to_user(offset, &outrec, sizeof(outrec));
++ }
++ *result = offset;
++ return 0;
++}
++
++static int
++query_m(struct dp_module_struct *m, struct dp_query_in_struct *req,
++ byte_t * result)
++{
++ unsigned long len = 0;
++ byte_t * offset = result;
++ struct dp_outmod_struct outmod;
++ int retval;
++ unsigned long rec_bytes = m->pgm.num_points *
++ sizeof(struct dp_outrec_struct);
++ unsigned long var_bytes = m->pgm.num_lv * sizeof(m->lv);
++ unsigned long rpn_bytes = m->pgm.rpn_length;
++ unsigned long mod_name_len = strlen(m->pgm.name) + 1;
++
++ len = sizeof(struct dp_outmod_struct) + var_bytes + rec_bytes
++ + rpn_bytes + mod_name_len;
++
++ req->returned += len;
++ if (req->returned <= req->allocated) {
++ outmod.pgm = m->pgm;
++ outmod.flags = m->flags;
++ outmod.base = m->base;
++ outmod.end = m->end;
++
++ offset += sizeof(outmod);
++
++ outmod.rec = (struct dp_outrec_struct *) offset ;
++ retval = query_r(m, &offset);
++ if (retval)
++ return retval;
++
++ if (var_bytes) {
++ outmod.lv = (unsigned long *) offset;
++ dp_copy_to_user(offset, m->lv, var_bytes);
++ } else
++ outmod.lv = NULL;
++
++ outmod.pgm.rpn_code = offset;
++ dp_copy_to_user(offset, m->pgm.rpn_code, rpn_bytes);
++
++ outmod.pgm.name = (char *) offset;
++ dp_copy_to_user(offset, m->pgm.name, mod_name_len);
++ if (m->next && !(req->name))
++ outmod.next = (struct dp_outmod_struct *) offset;
++ else
++ outmod.next = NULL;
++ offset = result;
++ dp_copy_to_user(offset, &outmod, sizeof(outmod));
++ }
++ else
++ return -ENOMEM;
++ return 0;
++}
++
++static int
++do_query(unsigned long cmd, struct dp_query_in_struct * ureq, byte_t * result)
++{
++ struct dp_query_in_struct req;
++ struct dp_module_struct *m;
++ int retval = 0;
++
++ if (!ureq)
++ return -EINVAL;
++ if (copy_from_user(&req, ureq, sizeof(req))) {
++ retval = -EFAULT;
++ goto failed;
++ }
++ if (req.name) {
++ req.name = getname(req.name);
++ if (IS_ERR(req.name)) {
++ retval = PTR_ERR(req.name);
++ goto failed;
++ }
++ }
++ else
++ req.name = NULL;
++ req.returned = 0;
++
++ down_read(&dp_modlist_sem);
++ if (cmd & DP_ALL) {
++ for (m = dp_module_list; m; m = m->next) {
++ retval = query_m(m, &req, result + req.returned);
++ if (retval && (retval != -ENOMEM))
++ break;
++ }
++ }
++ else if (req.name) {
++ m = find_mod_by_name(req.name);
++ if (m)
++ retval = query_m(m, &req, result + req.returned);
++ else
++ retval = -EINVAL;
++ }
++ up_read(&dp_modlist_sem);
++
++ if (copy_to_user(&ureq->returned, &req.returned,
++ sizeof(unsigned long)))
++ retval = -EFAULT;
++ if (req.name)
++ putname(req.name);
++failed:
++ return retval;
++}
++
++/*
++ * Returns the index of the first probe record that lies in the page
++ * starting with given offset.
++ */
++static inline int
++find_first_in_page(struct dp_module_struct * m, unsigned long offset)
++{
++ int i;
++ unsigned short num_recs = m->pgm.num_points;
++ struct dp_record_struct * rec;
++ unsigned long end = offset + PAGE_SIZE;
++
++ for (i = 0, rec = m->rec; i < num_recs; i++, rec++) {
++ if (rec->point.offset >=offset && rec->point.offset < end)
++ return i;
++ }
++ return -1;
++}
++
++/*
++ * This routine does the post processing for all pages that are brought
++ * in from a user module which has active probes in it.
++ */
++static int fixup_page (struct dp_module_struct *m, struct page *page)
++{
++ int start;
++ unsigned short end;
++ struct vm_area_struct *vma = NULL;
++
++ /* find and insert all probes in this page */
++ start = find_first_in_page(m, (page->index << PAGE_CACHE_SHIFT));
++ if (start < 0)
++ return -1;
++ end = find_last(m, start);
++
++ return insert_recs_in_page (m, page, start, end, vma);
++}
++
++/*
++ * This function handles the readpage of all modules that have active probes
++ * in them. Here, we first call the original readpage() of this
++ * inode / address_space to actually read the page into memory. Then, we will
++ * insert all the probes that are specified in this page before returning.
++ */
++int dp_readpage(struct file *file, struct page *page)
++{
++ int retval = 0;
++ struct dp_module_struct *m;
++
++ down_read(&dp_modlist_sem);
++ m = find_mod_by_inode(file->f_dentry->d_inode);
++ if (!m) {
++ printk("dp_readpage: major problem. we don't have mod for this !!!\n");
++ up_read(&dp_modlist_sem);
++ return -EINVAL;
++ }
++
++ /* call original readpage() */
++ retval = m->ori_a_ops->readpage(file, page);
++ if (retval >= 0)
++ fixup_page (m, page);
++
++ up_read(&dp_modlist_sem);
++ return retval;
++}
++
++/*
++ * This is called from kernel/module.c,sys_init_module() after a kernel module
++ * is loaded before it is initialized to give us a chance to insert any probes
++ * in it.
++ */
++int dp_insmod(struct module *kmod)
++{
++ struct dp_module_struct *m;
++ int retval;
++
++ down_read(&dp_modlist_sem);
++ m = find_mod_by_name(kmod->name);
++ if (!m) {
++ retval = -1;
++ } else {
++ get_kmod_bounds(m, kmod);
++ retval = insert_all_recs_k(m);
++ }
++ up_read(&dp_modlist_sem);
++ return retval;
++}
++
++/*
++ * This is called from kernel/module.c, free_module() before freeing a kernel
++ * module and from sys_init_module() when a kernel module initialization fails
++ * to give us a chance to remove any probes in it.
++ */
++int dp_remmod(struct module *kmod)
++{
++ struct dp_module_struct *m;
++ int retval;
++
++ down_read(&dp_modlist_sem);
++ m = find_mod_by_name(kmod->name);
++ if (!m)
++ retval = -1;
++ else
++ retval = remove_all_recs_k(m);
++ up_read(&dp_modlist_sem);
++ return retval;
++}
++
++/*
++ * ioctl function for dprobes driver.
++ */
++static int dp_ioctl (struct inode *in, struct file *fp, unsigned int ioctl_cmd,
++ unsigned long arg)
++{
++ struct dp_ioctl_arg dp_arg;
++ int retval = 0;
++
++ if (current->euid) {
++ return -EACCES;
++ }
++
++ if (copy_from_user((void *)&dp_arg, (void *)arg, sizeof(dp_arg))) {
++ module_put(THIS_MODULE);
++ return -EFAULT;
++ }
++
++ switch (dp_arg.cmd & DP_CMD_MASK) {
++ case DP_INSERT:
++ retval = do_insert(dp_arg.input);
++ break;
++ case DP_REMOVE:
++ retval = do_remove(dp_arg.cmd, dp_arg.input);
++ break;
++ case DP_GETVARS:
++ retval = do_getvars(dp_arg.cmd, dp_arg.input, dp_arg.output);
++ break;
++ case DP_QUERY:
++ retval = do_query(dp_arg.cmd, dp_arg.input, dp_arg.output);
++ break;
++ default:
++ retval = -EINVAL;
++ break;
++ }
++ return retval;
++}
++
++static struct file_operations dprobes_ops = {
++ owner: THIS_MODULE,
++ ioctl: dp_ioctl
++};
++
++static int dprobes_major;
++
++#ifdef CONFIG_MAGIC_SYSRQ
++
++/* SysRq handler - emergency removal of all probes via Alt-SysRq-V */
++
++static void do_remove_sysrq(struct dprobes_struct *dp)
++{
++ int retval;
++ if ((retval = do_remove(DP_ALL, NULL)))
++ printk("<1>Probe removal failed rc %i\n", retval);
++ else
++ printk("<1> All probes removed\n");
++ test_and_clear_bit(0, &emergency_remove);
++ return;
++}
++
++static void dprobes_sysrq( int key, struct pt_regs *ptr, struct tty_struct *tty)
++{
++
++ int retval;
++ retval = test_and_set_bit(0,&emergency_remove);
++ if (!retval)
++ queue_work(dprobes_wq, &dprobes_work);
++ return;
++
++}
++#endif
++
++static int __init dprobes_init_module(void)
++{
++ int retval = 0;
++
++ dprobes_major = register_chrdev(0, "dprobes", &dprobes_ops);
++ if (dprobes_major < 0) {
++ printk("Failed to register dprobes device\n");
++ retval = dprobes_major;
++ goto err;
++ }
++
++ printk("IBM Dynamic Probes v%d.%d.%d loaded.\n", DP_MAJOR_VER,
++ DP_MINOR_VER, DP_PATCH_VER);
++
++#ifdef CONFIG_MAGIC_SYSRQ
++ if (!(dprobes_wq = create_workqueue("dprobes_wq"))) {
++ printk("dprobes create_workqueue failed\n");
++ retval = -ENOMEM;
++ goto err;
++ }
++ INIT_WORK(&dprobes_work, (void *)(void *)do_remove_sysrq, NULL);
++ if ((retval = register_sysrq_key('v', &key_op)))
++ printk("<1>register_sysrq_key returned %i\n", retval);
++#endif
++
++err: return retval;
++}
++
++static void __exit dprobes_cleanup_module(void)
++{
++ int retval;
++ unregister_chrdev(dprobes_major, "dprobes");
++
++ printk("IBM Dynamic Probes v%d.%d.%d unloaded.\n", DP_MAJOR_VER,
++ DP_MINOR_VER, DP_PATCH_VER);
++
++#ifdef CONFIG_MAGIC_SYSRQ
++ destroy_workqueue(dprobes_wq);
++ if ((retval = unregister_sysrq_key('v', &key_op)))
++ printk("<1>unregister_sysrq_key returned %i\n", retval);
++#endif
++ return;
++}
++
++module_init(dprobes_init_module);
++module_exit(dprobes_cleanup_module);
++
++MODULE_AUTHOR("IBM");
++MODULE_DESCRIPTION("Dynamic Probes");
++MODULE_LICENSE("GPL");
++
++void dprobes_code_end(void) { }
+diff -urNp linux.orig/drivers/dprobes/dprobes_in.c linux-2.6.0-test11/drivers/dprobes/dprobes_in.c
+--- linux.orig/drivers/dprobes/dprobes_in.c 1970-01-01 05:30:00.000000000 +0530
++++ linux-2.6.0-test11/drivers/dprobes/dprobes_in.c 2003-12-15 15:51:48.000000000 +0530
+@@ -0,0 +1,2014 @@
++/*
++ * IBM Dynamic Probes
++ * Copyright (c) International Business Machines Corp., 2000
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ */
++
++#include <linux/dprobes.h>
++#include <linux/dprobes_in.h>
++#include <asm/io.h>
++#include <asm/uaccess.h>
++
++static void dispatch_ex(void);
++static void gen_ex(unsigned long, unsigned long, unsigned long);
++static void log_st(void);
++
++void dprobes_interpreter_code_start(void) { }
++
++/*
++ * RPN stack manipulation
++ * Pushes a 32 bit number onto RPN stack.
++ */
++static inline void rpnpush(unsigned long arg)
++{
++ dprobes.rpn_tos--;
++ dprobes.rpn_tos &= RPN_STACK_SIZE - 1;
++ dprobes.rpn_stack[dprobes.rpn_tos] = arg;
++}
++
++/*
++ * Pops a 32 bit number from RPN stack.
++ */
++static inline unsigned long rpnpop(void)
++{
++ unsigned long ret = dprobes.rpn_stack[dprobes.rpn_tos];
++ dprobes.rpn_tos++;
++ dprobes.rpn_tos &= RPN_STACK_SIZE - 1;
++ return ret;
++}
++
++/*
++ * Returns a 32 bit number for RPN stack at a given position
++ * relative to RPN TOS, without poping.
++ */
++static unsigned long rpnmove(unsigned long pos)
++{
++ unsigned long index = dprobes.rpn_tos + pos;
++ index &= RPN_STACK_SIZE - 1;
++ return dprobes.rpn_stack[index];
++}
++
++/*
++ * Returns a byte(8 bit) operand from rpn code array
++ */
++static inline u8 get_u8_oprnd(void)
++{
++ return *dprobes.rpn_ip++;
++}
++
++
++/*
++ * Returns a word(16 bit) operand from rpn code array
++ */
++static inline u16 get_u16_oprnd(void)
++{
++ return *(((u16 *)dprobes.rpn_ip)++);
++}
++
++/*
++ * Returns an unsigned long operand from rpn code array
++ */
++static inline unsigned long get_ulong_oprnd(void)
++{
++ unsigned long dw;
++ dw = *((unsigned long *) (dprobes.rpn_ip));
++ dprobes.rpn_ip += sizeof(unsigned long);
++ return dw;
++}
++
++/*
++ * Returns a signed long(32 bit) operand from rpn code array
++ */
++static inline long get_long_oprnd(void)
++{
++ long l;
++ l = *((long *) (dprobes.rpn_ip));
++ dprobes.rpn_ip += sizeof(long);
++ return l;
++}
++
++#ifdef CONFIG_X86
++#include "i386/dprobes_in.c"
++#endif
++
++/*
++ * Jump instructions.
++ * Interpreter is terminated when total number of jumps and loops
++ * becomes equal to jmpmax.
++ */
++static void jmp(void)
++{
++ long offset = get_long_oprnd();
++ if (dprobes.jmp_count == dprobes.mod->pgm.jmpmax) {
++ gen_ex(EX_MAX_JMPS, dprobes.mod->pgm.jmpmax, 0);
++ return;
++ }
++ dprobes.jmp_count++;
++ dprobes.rpn_ip += offset;
++}
++
++#define COND_JMPS(name, condition) \
++static void name(void) \
++{ \
++ long offset = get_long_oprnd(); \
++ if ((long) dprobes.rpn_stack[dprobes.rpn_tos] condition 0) { \
++ if (dprobes.jmp_count == dprobes.mod->pgm.jmpmax) { \
++ gen_ex(EX_MAX_JMPS, dprobes.mod->pgm.jmpmax, 0); \
++ return; \
++ } \
++ dprobes.jmp_count++; \
++ dprobes.rpn_ip += offset; \
++ } \
++}
++
++COND_JMPS(jlt, <)
++COND_JMPS(jgt, >)
++COND_JMPS(jle, <=)
++COND_JMPS(jge, >=)
++COND_JMPS(jz, ==)
++COND_JMPS(jnz, !=)
++
++/*
++ * loop label: Decrement RPN TOS and jump to the label
++ * if TOS is not equal to 0.
++ * Interpreter is terminated when total number of jumps and loops
++ * becomes equal to jmpmax.
++ */
++static void loop(void)
++{
++ long offset = get_long_oprnd();
++ if (dprobes.jmp_count++ == dprobes.mod->pgm.jmpmax) {
++ gen_ex(EX_MAX_JMPS, dprobes.mod->pgm.jmpmax, 0);
++ return;
++ }
++ if ((long) (--dprobes.rpn_stack[dprobes.rpn_tos]) != 0)
++ dprobes.rpn_ip += offset;
++}
++
++/*
++ * Call and ret instructions. Number of nested calls is limited to 32.
++ */
++static void call(void)
++{
++ int i;
++ struct dprobes_struct *dp = &dprobes;
++ long offset = (long)get_long_oprnd();
++ unsigned long called_addr = (dp->rpn_ip - dp->rpn_code + offset);
++ if (dp->call_tos < CALL_FRAME_SIZE) {
++ gen_ex(EX_CALL_STACK_OVERFLOW, NR_NESTED_CALLS, 0);
++ return;
++ }
++ /*
++ * Push the return value(rpn_ip) onto the call stack.
++ */
++ dp->call_stack[--dp->call_tos] = dp->rpn_ip - dp->rpn_code;
++ dp->rpn_ip += offset;
++ /*
++ * push the called subroutine address, default exception handler
++ * address, and initialize pv, lv and gv range to zeros.
++ */
++ dp->call_stack[--dp->call_tos] = called_addr;
++ dp->call_stack[--dp->call_tos] = EX_NO_HANDLER;
++
++ for (i = 0; i < CALL_FRAME_SIZE - 4; i++)
++ dp->call_stack[--dp->call_tos] = 0;
++ /* Save the rpn stack base pointer */
++ dp->call_stack[--dp->call_tos] = dp->rpn_sbp;
++}
++
++/*
++ * Pops the return value into rpn_ip.
++ */
++static void ret(void)
++{
++ struct dprobes_struct *dp = &dprobes;
++ if (dp->call_tos >= CALL_STACK_SIZE - CALL_FRAME_SIZE) {
++ gen_ex(EX_CALL_STACK_OVERFLOW, 0, 0);
++ return;
++ }
++ dp->rpn_sbp = dp->call_stack[dp->call_tos++];
++ dp->call_tos += CALL_FRAME_SIZE - 2;
++ /* pop the return address */
++ dp->rpn_ip = dp->rpn_code + dp->call_stack[dp->call_tos++];
++
++ if (dp->ex_pending) {
++ dp->ex_hand = dp->call_stack[dp->call_tos +
++ OFFSET_EX_HANDLER];
++ dispatch_ex();
++ }
++}
++
++/*
++ * Exception handling instructions.
++ */
++static void sx(void)
++{
++ long offset = get_long_oprnd();
++ dprobes.call_stack[dprobes.call_tos + OFFSET_EX_HANDLER] =
++ (long)dprobes.rpn_ip + offset;
++}
++
++static void ux(void)
++{
++ dprobes.call_stack[dprobes.call_tos + OFFSET_EX_HANDLER] =
++ EX_NO_HANDLER;
++}
++
++static void dispatch_ex(void)
++{
++ struct dprobes_struct *dp = &dprobes;
++ long ex_handler;
++ /* We are at the outermost routine & no exception handler, terminate */
++ if ((dp->call_tos == CALL_STACK_SIZE - CALL_FRAME_SIZE) &&
++ (dp->call_stack[dp->call_tos + OFFSET_EX_HANDLER] ==
++ EX_NO_HANDLER)) {
++ dp->status &= ~DP_STATUS_INTERPRETER;
++ return;
++ }
++ ex_handler = dp->ex_hand;
++ if (ex_handler != EX_NO_HANDLER) {
++ dp->ex_pending = 0;
++ dp->call_stack[dp->call_tos + OFFSET_EX_HANDLER] =
++ EX_NO_HANDLER;
++ dp->rpn_ip = (byte_t *)ex_handler;
++ }
++ else {
++ ret();
++ }
++}
++
++static void rx(void)
++{
++ struct dprobes_struct *dp = &dprobes;
++ dp->ex_code = rpnmove(0);
++ dp->ex_parm1 = rpnmove(1);
++ dp->ex_parm2 = rpnmove(2);
++ dp->ex_pending = 1;
++ dp->ex_hand = dp->call_stack[dp->call_tos +
++ OFFSET_EX_HANDLER];
++ if (dp->rec->mod->pgm.autostacktrace)
++ log_st();
++ dispatch_ex();
++}
++
++static void push_x(void)
++{
++ rpnpush(dprobes.ex_parm2);
++ rpnpush(dprobes.ex_parm1);
++ rpnpush(dprobes.ex_code);
++}
++
++static void gen_ex(unsigned long ex_code, unsigned long parm1,
++ unsigned long parm2)
++{
++ struct dprobes_struct *dp = &dprobes;
++ if (ex_code & dp->rec->point.ex_mask) {
++ dp->ex_code = ex_code;
++ dp->ex_parm1 = parm1;
++ dp->ex_parm2 = parm2;
++ rpnpush(parm2);
++ rpnpush(parm1);
++ rpnpush(ex_code);
++ dp->ex_pending = 1;
++ dp->ex_hand = dp->call_stack[dp->call_tos +
++ OFFSET_EX_HANDLER];
++ if (dp->rec->mod->pgm.autostacktrace) {
++ log_st();
++ }
++ dispatch_ex();
++ } else if (ex_code & EX_NON_MASKABLE_EX) {
++ dp->ex_hand = dp->call_stack[dp->call_tos +
++ OFFSET_EX_HANDLER];
++ if (dp->rec->mod->pgm.autostacktrace)
++ log_st();
++ dp->status &= ~DP_STATUS_INTERPRETER;
++ }
++}
++
++static int write_st_buffer(unsigned char *ptr, int size)
++{
++ if (dprobes.ex_log_len + size >= dprobes.mod->pgm.ex_logmax)
++ return -1;
++ else {
++ memcpy(dprobes.ex_log + dprobes.ex_log_len, ptr, size);
++ dprobes.ex_log_len += size;
++ return 0;
++ }
++}
++
++static int log_gv_entries(unsigned long call_tos)
++{
++ struct dprobes_struct *dp = &dprobes;
++ unsigned char gv_prefix[PREFIX_SIZE] = {TOKEN_GV_ENTRY, 0, 0};
++ unsigned long range = dp->call_stack[call_tos + OFFSET_GV_RANGE];
++ unsigned long index = dp->call_stack[call_tos + OFFSET_GV_RANGE + 1];
++ int i;
++ dp->ex_off.gv = dp->ex_log_len + 1;
++ if (write_st_buffer(gv_prefix, PREFIX_SIZE))
++ return -1;
++ for (i = index; i < index + range; i++) {
++ if (i >= dp_num_gv) {
++ dp->status &= ~DP_STATUS_INTERPRETER;
++ return -1;
++ }
++ read_lock(&dp_gv_lock);
++ if (write_st_buffer((unsigned char *)(dp_gv + i), sizeof(unsigned long))) {
++ read_unlock(&dp_gv_lock);
++ return -1;
++ }
++ read_unlock(&dp_gv_lock);
++ (*(unsigned short *)(dp->ex_log + dp->ex_off.gv))++;
++ }
++ return 0;
++}
++
++static int log_lv_entries(unsigned long call_tos)
++{
++ struct dprobes_struct *dp = &dprobes;
++ unsigned char lv_prefix[PREFIX_SIZE] = {TOKEN_LV_ENTRY, 0, 0};
++ unsigned long range = dp->call_stack[call_tos + OFFSET_LV_RANGE];
++ unsigned long index = dp->call_stack[call_tos + OFFSET_LV_RANGE + 1];
++ int i;
++ dp->ex_off.lv = dp->ex_log_len + 1;
++ if (write_st_buffer(lv_prefix, PREFIX_SIZE))
++ return -1;
++ for (i = index; i < index + range; i++) {
++ if (i >= dp->rec->mod->pgm.num_lv) {
++ dp->status &= ~DP_STATUS_INTERPRETER;
++ return -1;
++ }
++ if (write_st_buffer((unsigned char *)(dp->mod->lv + i), sizeof(unsigned long)))
++ return -1;
++ (*(unsigned short *)(dp->ex_log + dp->ex_off.lv))++;
++ }
++ return 0;
++}
++
++static int log_rpnstack_entries(unsigned long call_tos)
++{
++ struct dprobes_struct *dp = &dprobes;
++ unsigned char rpn_prefix[PREFIX_SIZE] = {TOKEN_RPN_ENTRY, 0, 0};
++ unsigned long range = dp->call_stack[call_tos +
++ OFFSET_RPN_STACK_RANGE];
++ unsigned long index = dp->call_stack[call_tos +
++ OFFSET_RPN_STACK_RANGE + 1];
++ int i, j;
++ dp->ex_off.rpn = dp->ex_log_len + 1;
++ if (write_st_buffer(rpn_prefix, PREFIX_SIZE))
++ return -1;
++ for (i = j = index; i < index + range; i++, j++) {
++ j &= (RPN_STACK_SIZE - 1);
++ if (write_st_buffer((unsigned char *)(dp->rpn_stack + j),
++ sizeof(unsigned long)))
++ return -1;
++ (*(unsigned short *)(dp->ex_log + dp->ex_off.rpn))++;
++ }
++ return 0;
++}
++
++static int log_stackframes(void)
++{
++ struct dprobes_struct *dp = &dprobes;
++ unsigned long c_tos = dp->call_tos;
++ unsigned char sf_prefix[PREFIX_SIZE] = {TOKEN_STACK_FRAME, 0, 0};
++ dp->ex_off.sf = dp->ex_log_len + 1;
++ if (write_st_buffer(sf_prefix, PREFIX_SIZE))
++ return -1;
++
++ while (c_tos < (CALL_STACK_SIZE - CALL_FRAME_SIZE)) {
++ if (write_st_buffer((unsigned char *)(dp->call_stack +
++ c_tos + OFFSET_CALLED_ADDR), sizeof(unsigned long)))
++ return -1;
++ if (write_st_buffer((unsigned char *)(dp->call_stack +
++ c_tos + OFFSET_RETURN_ADDR), sizeof(unsigned long)))
++ return -1;
++
++ if (log_rpnstack_entries(c_tos))
++ return -1;
++ if (log_lv_entries(c_tos))
++ return -1;
++ if (log_gv_entries(c_tos))
++ return -1;
++
++ (*(short *)(dp->ex_log + dp->ex_off.sf))++;
++ c_tos += CALL_FRAME_SIZE;
++ }
++ return 0;
++}
++
++static int log_stacktrace(void)
++{
++ struct dprobes_struct *dp = &dprobes;
++ long ex_handler;
++ if (write_st_buffer((unsigned char *)&dp->ex_code, sizeof(unsigned long)))
++ return -1;
++ ex_handler = dp->ex_hand;
++ if (ex_handler != EX_NO_HANDLER)
++ ex_handler -= (long)dp->rpn_code;
++ if (write_st_buffer((unsigned char *)&ex_handler, sizeof(long)))
++ return -1;
++ if (write_st_buffer((unsigned char *)&dp->ex_parm1, sizeof(unsigned long)))
++ return -1;
++ if (write_st_buffer((unsigned char *)&dp->ex_parm2, sizeof(unsigned long)))
++ return -1;
++ if (log_stackframes())
++ return -1;
++ (*(short *)(dp->ex_log + dp->ex_off.st))++;
++ return 0;
++}
++
++static int get_exlog_hdr(void)
++{
++ unsigned short name_len;
++ unsigned char st_prefix[PREFIX_SIZE] = {TOKEN_STACK_TRACE, 0, 0};
++ struct dprobes_struct *dp = &dprobes;
++
++ strcpy(dp->ex_log + dp->ex_log_len + PREFIX_SIZE, dp->mod->pgm.name);
++ name_len = strlen(dp->mod->pgm.name);
++ dp->ex_log[dp->ex_log_len] = TOKEN_ASCII_LOG;
++ *(unsigned short *)(dp->ex_log + dp->ex_log_len + 1) = name_len;
++ dp->ex_log_len += (name_len + PREFIX_SIZE);
++
++ *(loff_t *)(dp->ex_log + dp->ex_log_len) =
++ dp->rec->point.offset;
++ dp->ex_log_len += sizeof(loff_t);
++
++ *(unsigned short *)(dp->ex_log + dp->ex_log_len) =
++ dp->mod->pgm.id;
++ dp->ex_log_len += sizeof(unsigned short);
++
++ *(unsigned short *)(dp->ex_log + dp->ex_log_len) =
++ dp->major;
++ dp->ex_log_len += sizeof(unsigned short);
++
++ *(unsigned short *)(dp->ex_log + dp->ex_log_len) =
++ dp->minor;
++ dp->ex_log_len += sizeof(unsigned short);
++
++ dp->ex_off.st = dp->ex_log_len + 1;
++ if (write_st_buffer(st_prefix, PREFIX_SIZE))
++ return -1;
++ return 0;
++}
++
++static void log_st(void)
++{
++ if (dprobes.ex_log_len == MIN_ST_SIZE)
++ if(get_exlog_hdr()) return;
++ log_stacktrace();
++}
++
++static void purge_st(void)
++{
++ dprobes.ex_log_len = MIN_ST_SIZE;
++}
++
++static void trace_lv(void)
++{
++ dprobes.call_stack[dprobes.call_tos + OFFSET_LV_RANGE] = rpnpop();
++ dprobes.call_stack[dprobes.call_tos + OFFSET_LV_RANGE + 1] = rpnpop();
++}
++
++static void trace_gv(void)
++{
++ dprobes.call_stack[dprobes.call_tos + OFFSET_GV_RANGE] = rpnpop();
++ dprobes.call_stack[dprobes.call_tos + OFFSET_GV_RANGE + 1] = rpnpop();
++}
++
++static void trace_pv(void)
++{
++ dprobes.call_stack[dprobes.call_tos + OFFSET_RPN_STACK_RANGE] =
++ rpnpop();
++ dprobes.call_stack[dprobes.call_tos + OFFSET_RPN_STACK_RANGE + 1] =
++ rpnpop();
++}
++
++static void push_tsp(void)
++{
++ long index = (long)rpnpop();
++ unsigned long tsp_index = ((signed)dprobes.rpn_tos + index)
++ & (RPN_STACK_SIZE - 1);
++ rpnpush(dprobes.rpn_stack[tsp_index]);
++}
++
++static void pop_tsp(void)
++{
++ unsigned long value = rpnpop();
++ long index = (long)rpnpop();
++ unsigned long tsp_index = ((signed)dprobes.rpn_tos + index)
++ & (RPN_STACK_SIZE - 1);
++ dprobes.rpn_stack[tsp_index] = value;
++}
++
++static void save_sbp(void)
++{
++ rpnpush(dprobes.rpn_sbp);
++}
++
++static void restore_sbp(void)
++{
++ dprobes.rpn_sbp = rpnpop() & (RPN_STACK_SIZE - 1);
++}
++
++static void save_tsp(void)
++{
++ rpnpush(dprobes.rpn_tos);
++}
++
++static void restore_tsp(void)
++{
++ dprobes.rpn_tos = rpnpop();
++}
++
++static void push_sbp(void)
++{
++ long index = (long)rpnpop();
++ unsigned long sbp_index = ((signed)dprobes.rpn_sbp + index)
++ & (RPN_STACK_SIZE - 1);
++ rpnpush(dprobes.rpn_stack[sbp_index]);
++}
++
++static void pop_sbp(void)
++{
++ unsigned long value = rpnpop();
++ long index = (long)rpnpop();
++ unsigned long sbp_index = ((signed)dprobes.rpn_sbp + index)
++ & (RPN_STACK_SIZE - 1);
++ dprobes.rpn_stack[sbp_index] = value;
++}
++
++static void push_stp(void)
++{
++ rpnpush(dprobes.ex_log_len);
++}
++
++static void pop_stp(void)
++{
++ dprobes.ex_log_len = rpnpop();
++}
++
++static void push_sbp_i(void)
++{
++ s16 index = (s16)get_u16_oprnd();
++ unsigned long sbp_index = ((signed)dprobes.rpn_sbp + index)
++ & (RPN_STACK_SIZE - 1);
++ rpnpush(dprobes.rpn_stack[sbp_index]);
++}
++
++static void pop_sbp_i(void)
++{
++ s16 index = (s16)get_u16_oprnd();
++ unsigned long sbp_index = ((signed)dprobes.rpn_sbp + index)
++ & (RPN_STACK_SIZE - 1);
++ dprobes.rpn_stack[sbp_index] = rpnpop();
++}
++
++static void push_tsp_i(void)
++{
++ s16 index = (s16)get_u16_oprnd();
++ unsigned long tsp_index = ((signed)dprobes.rpn_tos + index)
++ & (RPN_STACK_SIZE - 1);
++ rpnpush(dprobes.rpn_stack[tsp_index]);
++}
++
++static void pop_tsp_i(void)
++{
++ s16 index = (s16)get_u16_oprnd();
++ unsigned long tsp_index = ((signed)dprobes.rpn_tos + index)
++ & (RPN_STACK_SIZE - 1);
++ dprobes.rpn_stack[tsp_index] = rpnpop();
++}
++
++static void copy_sbp(void)
++{
++ long index = (long)rpnpop();
++ unsigned long sbp_index = ((signed)dprobes.rpn_sbp + index)
++ & (RPN_STACK_SIZE - 1);
++ dprobes.rpn_stack[sbp_index] = dprobes.rpn_stack[dprobes.rpn_tos];
++}
++
++static void copy_sbp_i(void)
++{
++ s16 index = (s16)get_u16_oprnd();
++ unsigned long sbp_index = ((signed)dprobes.rpn_sbp + index)
++ & (RPN_STACK_SIZE - 1);
++ dprobes.rpn_stack[sbp_index] = dprobes.rpn_stack[dprobes.rpn_tos];
++}
++
++static void copy_tsp(void)
++{
++ long index = (long)rpnpop();
++ unsigned long tsp_index = ((signed)dprobes.rpn_tos + index)
++ & (RPN_STACK_SIZE - 1);
++ dprobes.rpn_stack[tsp_index] = dprobes.rpn_stack[dprobes.rpn_tos];
++}
++
++static void copy_tsp_i(void)
++{
++ s16 index = (s16)get_u16_oprnd();
++ unsigned long tsp_index = ((signed)dprobes.rpn_tos + index)
++ & (RPN_STACK_SIZE - 1);
++ dprobes.rpn_stack[tsp_index] = dprobes.rpn_stack[dprobes.rpn_tos];
++}
++
++/*
++ * Normal exit from the interpreter.
++ */
++static inline void dp_exit(void)
++{
++ dprobes.status &= ~DP_STATUS_INTERPRETER;
++}
++
++/*
++ * Exit the interpreter without producing the log.
++ */
++static inline void dp_abort(void)
++{
++ dprobes.status |= DP_STATUS_ABORT;
++ dprobes.status &= ~DP_STATUS_INTERPRETER;
++}
++
++static inline void dp_remove(void)
++{
++ dprobes.rec->status |= DP_REC_STATUS_REMOVED;
++ dprobes.status &= ~DP_STATUS_INTERPRETER;
++}
++
++static inline void no_op(void)
++{
++}
++
++/*
++ * Stores the fault record in the log buffer, when logging is
++ * terminated due to invalid memory access.
++ * Fault record comprises of a token byte, which indicates the reason for
++ * fault; an unsigned short length indicating how many bytes are logged,
++ * which is 4; and the 4 logged bytes indicate the faulting address.
++ *
++ * Token byte -1: fault occurred while reading the memory, faulting address
++ * indicates the virtual(flat) address which caused the fault.
++ * Token byte -2: fault occurred when converting the segmented address to
++ * flat address. The fault address stored will indicate the selector of
++ * the segmented address.
++ * Note that the fault record will be stored only if sufficient space
++ * is available to store the entire fault record.
++ *
++ * The fault record will be stored when fault occurs because of the
++ * following instructions:
++ * log mrf
++ * log str
++ */
++static void store_fault_record(byte_t token, unsigned long addr,
++ unsigned long ex_code)
++{
++ struct dprobes_struct *dp = &dprobes;
++ if (dp->log_len + 7 <= dp->mod->pgm.logmax) {
++ dp->log[dp->log_len++] = token;
++ *((unsigned short *)(dp->log + dp->log_len)) = 4;
++ dp->log_len += sizeof(unsigned short);
++ *((unsigned long *)(dp->log + dp->log_len)) = addr;
++ dp->log_len += sizeof(unsigned long);
++ gen_ex(ex_code, addr, 0);
++ }
++ else
++ gen_ex(EX_LOG_OVERFLOW, dp->mod->pgm.logmax, 0);
++ return;
++}
++
++/*
++ * Logging group
++ */
++static inline void push_lp(void)
++{
++ rpnpush(dprobes.log_len);
++}
++
++static inline void push_plp(void)
++{
++ rpnpush(dprobes.prev_log_len);
++}
++
++static inline void pop_lp(void)
++{
++ dprobes.log_len = rpnpop();
++}
++
++static void
++log_ascii(unsigned long len, u8 *faddr)
++{
++ struct dprobes_struct *dp = &dprobes;
++ u8 b;
++ dp->prev_log_len = dp->log_len;
++ if (dp->log_len + len + PREFIX_SIZE > dp->mod->pgm.logmax) {
++ len = dp->mod->pgm.logmax - dp->log_len;
++ if ((signed)len < 0) {
++ gen_ex(EX_LOG_OVERFLOW, dp->mod->pgm.logmax, 0);
++ return;
++ }
++ }
++ dp->log_len += PREFIX_SIZE; /* reserve for prefix */
++ while (len--) {
++ if (!dp_intr_copy_from_user(&b, (void *)faddr, 1)) {
++ if(b) {
++ faddr++;
++ dp->log[dp->log_len++] = b;
++ } else
++ break;
++
++ } else {
++ dp->log_len = dp->prev_log_len;
++ store_fault_record(TOKEN_MEMORY_FAULT,
++ (unsigned long)faddr, EX_INVALID_ADDR);
++ return;
++ }
++ }
++ if (b && (signed)len >= 0) {
++ dp->status |= DP_STATUS_LOG_OVERFLOW;
++ dp->log_len = dp->prev_log_len;
++ gen_ex(EX_LOG_OVERFLOW, dp->mod->pgm.logmax, 0);
++ return;
++ }
++ dp->log[dp->prev_log_len] = TOKEN_ASCII_LOG;
++ *(unsigned short *) (dp->log + dp->prev_log_len + 1) =
++ (unsigned short) (dp->log_len - dp->prev_log_len - PREFIX_SIZE);
++ dp->prev_log_len = dp->log_len;
++ return;
++}
++
++static void
++log_memory(unsigned long len, u8 *faddr)
++{
++ struct dprobes_struct *dp = &dprobes;
++ unsigned long num_remain;
++ dp->prev_log_len = dp->log_len;
++ if (dp->log_len + len + PREFIX_SIZE > dp->mod->pgm.logmax) {
++ dp->status |= DP_STATUS_LOG_OVERFLOW;
++ len = dp->mod->pgm.logmax - dp->log_len - PREFIX_SIZE;
++ if ((signed)len < 0) {
++ gen_ex(EX_LOG_OVERFLOW, dp->mod->pgm.logmax, 0);
++ return;
++ }
++ }
++
++ if ((num_remain = dp_intr_copy_from_user(
++ (dp->log + dp->log_len + PREFIX_SIZE), faddr, len))) {
++ store_fault_record(TOKEN_MEMORY_FAULT,
++ (unsigned long)(faddr+len-num_remain), EX_INVALID_ADDR);
++ return;
++ }
++
++ dp->log[dp->log_len++] = TOKEN_MEMORY_LOG;
++ *((unsigned short *)(dp->log + dp->log_len)) = len;
++ dp->log_len += (len + sizeof(unsigned short));
++ dp->prev_log_len = dp->log_len;
++ return;
++}
++
++/* log str pops the flat address and length from
++ * the RPN stack. It tries to copy length number of
++ * bytes from flat address to the log buffer.
++ * Logging is discontinued if a NULL byte is encountered.
++ * NULL byte is not logged. If the log buffer becomes full
++ * LOG_OVERFLOW exception is generated.
++ * If the log is successful, the logged string is prefixed
++ * by a token byte of 1 and a word indicating the length of
++ * the string logged.
++ * INVALID_ADDR exception is generated if the flat address is invalid.
++ */
++static void log_str(void)
++{
++ u8 *faddr = (u8 *) rpnpop();
++ log_ascii(rpnpop(), faddr);
++}
++
++/*
++ * Log memory at flat range.
++ * INVALID_ADDR exception is generated if flat address is invalid.
++ * Log terminating conditions apply.
++ * token byte = 0.
++ */
++static void log_mrf(void)
++{
++ u8 *faddr = (u8 *) rpnpop();
++ log_memory(rpnpop(), faddr);
++}
++
++/*
++ * Log count number of elements from rpn stack to log buffer.
++ */
++static void log_i(void)
++{
++ struct dprobes_struct *dp = &dprobes;
++ u16 oprnd = get_u16_oprnd();
++ u16 count, i;
++ unsigned long size = oprnd * sizeof(unsigned long);
++ dp->prev_log_len = dp->log_len;
++
++ if (dp->log_len + size > dp->mod->pgm.logmax) {
++ size = dp->mod->pgm.logmax - dp->log_len;
++ }
++ if ((signed)size < 0) {
++ gen_ex(EX_LOG_OVERFLOW, dp->mod->pgm.logmax, 0);
++ return;
++ }
++ i = count = size / sizeof(unsigned long);
++ while (i--) {
++ *(unsigned long *) (dp->log + dp->log_len) = rpnpop();
++ dp->log_len += sizeof(unsigned long);
++ }
++ if (count < oprnd) {
++ gen_ex(EX_LOG_OVERFLOW, dp->mod->pgm.logmax, 0);
++ return;
++ }
++ dp->prev_log_len = dp->log_len;
++}
++
++static void log(void)
++{
++ struct dprobes_struct *dp = &dprobes;
++ u16 oprnd = (u16) rpnpop();
++ u16 count, i;
++ unsigned long size = oprnd * sizeof(unsigned long) + PREFIX_SIZE;
++ dp->prev_log_len = dp->log_len;
++
++ if (dp->log_len + size > dp->mod->pgm.logmax) {
++ size = dp->mod->pgm.logmax - dp->log_len;
++ }
++ if ((signed)size < PREFIX_SIZE) {
++ gen_ex(EX_LOG_OVERFLOW, dp->mod->pgm.logmax, 0);
++ return;
++ }
++ dp->log_len += PREFIX_SIZE;
++ size -= PREFIX_SIZE;
++ i = count = size / sizeof(unsigned long);
++
++ while (i--) {
++ *(unsigned long *) (dp->log + dp->log_len) = rpnpop();
++ dp->log_len += sizeof(unsigned long);
++ }
++
++ dp->log[dp->prev_log_len] = TOKEN_LOG;
++ *(unsigned short *) (dp->log + dp->prev_log_len + 1) =
++ (unsigned short) (count);
++
++ if (count < oprnd) {
++ gen_ex(EX_LOG_OVERFLOW, dp->mod->pgm.logmax, 0);
++ return;
++ }
++ dp->prev_log_len = dp->log_len;
++}
++
++static void log_lv(void)
++{
++ struct dprobes_struct *dp = &dprobes;
++ unsigned long range = rpnpop();
++ unsigned long index = rpnpop();
++ unsigned long offset;
++ unsigned char lv_prefix[PREFIX_SIZE] = {TOKEN_LV_ENTRY, 0, 0};
++ int i;
++
++ if (dp->log_len + PREFIX_SIZE > dp->mod->pgm.logmax) {
++ gen_ex(EX_LOG_OVERFLOW, dp->mod->pgm.logmax, 0);
++ return;
++ }
++ memcpy(dp->log + dp->log_len, lv_prefix, PREFIX_SIZE);
++ offset = dp->log_len + 1;
++ dp->log_len += PREFIX_SIZE;
++
++ for (i = index; i < index + range; i++) {
++ if (i >= dp->rec->mod->pgm.num_lv) {
++ gen_ex(EX_INVALID_OPERAND, 1, i);
++ return;
++ }
++ if (dp->log_len + sizeof (unsigned long) > dp->mod->pgm.logmax) {
++ gen_ex(EX_LOG_OVERFLOW, dp->mod->pgm.logmax, 0);
++ return;
++ }
++ *(unsigned long *) (dp->log + dp->log_len) =
++ *(dp->mod->lv + i);
++ dp->log_len += sizeof(unsigned long);
++ (*(unsigned short *)(dp->log + offset))++;
++ }
++ return;
++}
++
++static void log_gv(void)
++{
++ struct dprobes_struct *dp = &dprobes;
++ unsigned long range = rpnpop();
++ unsigned long index = rpnpop();
++ unsigned long offset;
++ unsigned char gv_prefix[PREFIX_SIZE] = {TOKEN_GV_ENTRY, 0, 0};
++ int i;
++
++ if (dp->log_len + PREFIX_SIZE > dp->mod->pgm.logmax) {
++ gen_ex(EX_LOG_OVERFLOW, dp->mod->pgm.logmax, 0);
++ return;
++ }
++ memcpy(dp->log + dp->log_len, gv_prefix, PREFIX_SIZE);
++ offset = dp->log_len + 1;
++ dp->log_len += PREFIX_SIZE;
++
++ for (i = index; i < index + range; i++) {
++ if (i >= dp_num_gv) {
++ gen_ex(EX_INVALID_OPERAND, 2, i);
++ return;
++ }
++ if (dp->log_len + sizeof (unsigned long) > dp->mod->pgm.logmax) {
++ gen_ex(EX_LOG_OVERFLOW, dp->mod->pgm.logmax, 0);
++ return;
++ }
++ read_lock(&dp_gv_lock);
++ *(unsigned long *) (dp->log + dp->log_len) =
++ *(dp_gv + i);
++ read_unlock(&dp_gv_lock);
++ dp->log_len += sizeof(unsigned long);
++ (*(unsigned short *)(dp->log + offset))++;
++ }
++ return;
++}
++
++/*
++ * Overriding probepoint major and minor.
++ */
++static void setmin_i(void)
++{
++ dprobes.minor = get_u16_oprnd();
++}
++
++static void setmin(void)
++{
++ dprobes.minor = (unsigned short) rpnpop();
++}
++
++static void setmaj_i(void)
++{
++ dprobes.major = get_u16_oprnd();
++}
++
++static void setmaj(void)
++{
++ dprobes.major = (unsigned short) rpnpop();
++}
++
++/*
++ * Local Variables.
++ */
++
++static void do_push_lv(unsigned short index)
++{
++ if (index > dprobes.rec->mod->pgm.num_lv)
++ gen_ex(EX_INVALID_OPERAND, 1, index);
++ else
++ rpnpush(*(dprobes.mod->lv + index));
++}
++
++static void push_lvi(void)
++{
++ do_push_lv(get_u16_oprnd());
++}
++
++static void push_lv(void)
++{
++ do_push_lv(rpnpop());
++}
++
++static void pop_lvi(void)
++{
++ unsigned short index = get_u16_oprnd();
++ if (index > dprobes.rec->mod->pgm.num_lv)
++ gen_ex(EX_INVALID_OPERAND, 1, index);
++ else
++ *(dprobes.mod->lv + index) = rpnpop();
++}
++
++static void pop_lv(void)
++{
++ unsigned long value = rpnpop();
++ unsigned short index = (unsigned short) rpnpop();
++ if (index > dprobes.rec->mod->pgm.num_lv)
++ gen_ex(EX_INVALID_OPERAND, 1, index);
++ else
++ *(dprobes.mod->lv + index) = value;
++}
++
++static void do_inc_lv(unsigned short index)
++{
++ if (index > dprobes.rec->mod->pgm.num_lv)
++ gen_ex(EX_INVALID_OPERAND, 1, index);
++ else
++ (*(dprobes.mod->lv + index))++;
++}
++
++static void inc_lvi(void)
++{
++ do_inc_lv(get_u16_oprnd());
++}
++
++static void inc_lv(void)
++{
++ do_inc_lv(rpnpop());
++}
++
++static void do_dec_lv(unsigned short index)
++{
++ if (index > dprobes.rec->mod->pgm.num_lv)
++ gen_ex(EX_INVALID_OPERAND, 1, index);
++ else
++ (*(dprobes.mod->lv + index))--;
++}
++
++static void dec_lvi(void)
++{
++ do_dec_lv(get_u16_oprnd());
++}
++
++static void dec_lv(void)
++{
++ do_dec_lv(rpnpop());
++}
++
++static void do_move_lv(unsigned short index)
++{
++ if (index > dprobes.rec->mod->pgm.num_lv)
++ gen_ex(EX_INVALID_OPERAND, 1, index);
++ else
++ *(dprobes.mod->lv + index) = dprobes.rpn_stack[dprobes.rpn_tos];
++}
++
++static void move_lvi(void)
++{
++ do_move_lv(get_u16_oprnd());
++}
++
++static void move_lv(void)
++{
++ do_move_lv(rpnpop());
++}
++
++/*
++ * global variables.
++ */
++static void do_push_gv(unsigned short index)
++{
++ read_lock(&dp_gv_lock);
++ if (index >= dp_num_gv) {
++ read_unlock(&dp_gv_lock);
++ gen_ex(EX_INVALID_OPERAND, 2, index);
++ return;
++ } else {
++ rpnpush(*(dp_gv + index));
++ read_unlock(&dp_gv_lock);
++ }
++}
++
++static void push_gvi(void)
++{
++ do_push_gv(get_u16_oprnd());
++}
++
++static void push_gv(void)
++{
++ do_push_gv(rpnpop());
++}
++
++static void pop_gvi(void)
++{
++ unsigned short index = get_u16_oprnd();
++ write_lock(&dp_gv_lock);
++ if (index >= dp_num_gv) {
++ write_unlock(&dp_gv_lock);
++ gen_ex(EX_INVALID_OPERAND, 2, index);
++ return;
++ } else {
++ *(dp_gv + index) = rpnpop();
++ write_unlock(&dp_gv_lock);
++ }
++}
++
++static void pop_gv(void)
++{
++ unsigned long value = rpnpop();
++ unsigned short index = (unsigned short) rpnpop();
++ write_lock(&dp_gv_lock);
++ if (index >= dp_num_gv) {
++ write_unlock(&dp_gv_lock);
++ gen_ex(EX_INVALID_OPERAND, 2, index);
++ return;
++ } else {
++ *(dp_gv + index) = value;
++ write_unlock(&dp_gv_lock);
++ }
++}
++
++
++static void do_inc_gv(unsigned short index)
++{
++ write_lock(&dp_gv_lock);
++ if (index >= dp_num_gv) {
++ write_unlock(&dp_gv_lock);
++ gen_ex(EX_INVALID_OPERAND, 2, index);
++ return;
++ } else {
++ (*(dp_gv + index))++;
++ write_unlock(&dp_gv_lock);
++ }
++}
++
++static void inc_gvi(void)
++{
++ do_inc_gv(get_u16_oprnd());
++}
++
++static void inc_gv(void)
++{
++ do_inc_gv(rpnpop());
++}
++
++static void do_dec_gv(unsigned short index)
++{
++ write_lock(&dp_gv_lock);
++ if (index >= dp_num_gv) {
++ write_unlock(&dp_gv_lock);
++ gen_ex(EX_INVALID_OPERAND, 2, index);
++ return;
++ } else {
++ (*(dp_gv + index))--;
++ write_unlock(&dp_gv_lock);
++ }
++}
++
++static void dec_gvi(void)
++{
++ do_dec_gv(get_u16_oprnd());
++}
++
++static void dec_gv(void)
++{
++ do_dec_gv(rpnpop());
++}
++
++static void do_move_gv(unsigned short index)
++{
++ write_lock(&dp_gv_lock);
++ if (index >= dp_num_gv) {
++ write_unlock(&dp_gv_lock);
++ gen_ex(EX_INVALID_OPERAND, 2, index);
++ return;
++ } else {
++ *(dp_gv + index) = dprobes.rpn_stack[dprobes.rpn_tos];
++ write_unlock(&dp_gv_lock);
++ }
++}
++
++static void move_gvi(void)
++{
++ do_move_gv(get_u16_oprnd());
++}
++
++static void move_gv(void)
++{
++ do_move_gv(rpnpop());
++}
++
++
++/*
++ * Arithmetic and logic group.
++ */
++static void add(void)
++{
++ rpnpush(rpnpop() + rpnpop());
++}
++
++static void mul(void)
++{
++ rpnpush(rpnpop() * rpnpop());
++}
++
++static void and(void)
++{
++ rpnpush(rpnpop() & rpnpop());
++}
++
++static void or(void)
++{
++ rpnpush(rpnpop() | rpnpop());
++}
++
++static void xor(void)
++{
++ rpnpush(rpnpop() ^ rpnpop());
++}
++
++static void neg(void)
++{
++ rpnpush(~rpnpop());
++}
++
++static void sub(void)
++{
++ rpnpush(rpnpop() - rpnpop());
++}
++
++/* pop divisor first and dividend next, pushes remainder first and quotient next */
++static void div(void)
++{
++ unsigned long divisor = rpnpop();
++ unsigned long dividend = rpnpop();
++ if (!divisor) {
++ gen_ex(EX_DIV_BY_ZERO, 0, 0);
++ return;
++ }
++ rpnpush(dividend % divisor);
++ rpnpush(dividend / divisor);
++}
++
++static void idiv(void)
++{
++ signed long divisor = (signed long) rpnpop();
++ signed long dividend = (signed long) rpnpop();
++ if (!divisor) {
++ gen_ex(EX_DIV_BY_ZERO, 0, 0);
++ return;
++ }
++ rpnpush(dividend % divisor);
++ rpnpush(dividend / divisor);
++}
++
++static void xchng(void)
++{
++ register unsigned long arg1, arg2;
++ arg1 = rpnpop();
++ arg2 = rpnpop();
++ rpnpush(arg1);
++ rpnpush(arg2);
++}
++
++static void shl_i(void)
++{
++ dprobes.rpn_stack[dprobes.rpn_tos] <<= get_u8_oprnd();
++}
++
++static void shr_i(void)
++{
++ dprobes.rpn_stack[dprobes.rpn_tos] >>= get_u8_oprnd();
++}
++
++static void shr(void)
++{
++ unsigned long oprnd = rpnpop();
++ unsigned long count = rpnpop();
++ oprnd >>= count;
++ rpnpush(oprnd);
++}
++
++static void shl(void)
++{
++ unsigned long oprnd = rpnpop();
++ unsigned long count = rpnpop();
++ oprnd <<= count;
++ rpnpush(oprnd);
++}
++
++static void dup(void)
++{
++ unsigned long dup = rpnpop();
++ u8 dupcount = rpnpop();
++ rpnpush(dup);
++ dupcount &= ~RPN_STACK_SIZE;
++ while (dupcount--)
++ rpnpush(dup);
++}
++
++static void dupn(void)
++{
++ unsigned long dup = rpnpop();
++ u8 dupcount = get_u8_oprnd();
++ dupcount &= ~RPN_STACK_SIZE;
++ while (dupcount--)
++ rpnpush(dup);
++}
++
++static void ros(void)
++{
++ dprobes.rpn_tos += get_u8_oprnd();
++ dprobes.rpn_tos &= RPN_STACK_SIZE - 1;
++}
++
++/*
++ * Verify the read access to a byte pointed by the flat linear address.
++ */
++static void verify_access(int write)
++{
++ u8 b;
++ void * addr = (void *)rpnpop();
++ if (dp_intr_copy_from_user(&b, addr, 1)) {
++ goto fail;
++ }
++ if (write && dp_intr_copy_to_user(addr, &b, 1)) {
++ goto fail;
++ }
++ rpnpush(0);
++ return;
++fail:
++ rpnpush(1);
++ return;
++}
++
++/*
++ * Push immediate value (operand) onto the rpn stack.
++ */
++static void push_i (void)
++{
++ rpnpush(get_ulong_oprnd());
++}
++
++/*
++ * Push byte, word, dword, qword present at the flat address
++ * onto rpn stack.
++ */
++static int push_flat(void *val, void *faddr, unsigned long size)
++{
++ if (dp_intr_copy_from_user(val, faddr, size)) {
++ gen_ex(EX_INVALID_ADDR, (unsigned long)faddr, 0);
++ return 0;
++ }
++ return 1;
++}
++
++static void push_mem_u8 (void)
++{
++ u8 b;
++ if(push_flat((void *)&b, (void *)rpnpop(), sizeof(u8)))
++ rpnpush(b);
++}
++
++static void push_mem_u16 (void)
++{
++ u16 w;
++ if(push_flat((void *)&w, (void *)rpnpop(), sizeof(u16)))
++ rpnpush(w);
++}
++
++static void push_mem_u32 (void)
++{
++ u32 dw;
++ if(push_flat((void *)&dw, (void *)rpnpop(), sizeof(u32)))
++ rpnpush(dw);
++}
++
++static void push_mem_u64 (void)
++{
++ unsigned long q[2];
++ void *faddr = (void *) rpnpop();
++ if (dp_intr_copy_from_user(q, faddr, 2*sizeof(unsigned long))) {
++ gen_ex(EX_INVALID_ADDR, (unsigned long)faddr, 0);
++ } else {
++ rpnpush(q[1]);
++ rpnpush(q[0]);
++ }
++}
++
++/*
++ * Pop byte, word, dword, qword from rpn stack and write
++ * them at the specified linear address.
++ */
++static void pop_flat(void *faddr, void * val, unsigned long size)
++{
++ if (dp_intr_copy_to_user(faddr, val, size))
++ gen_ex(EX_INVALID_ADDR, (unsigned long)faddr, 0);
++}
++
++static void pop_mem_u8 (void)
++{
++ u8 b = (unsigned char) rpnpop();
++ pop_flat((void *)rpnpop(), (void *)&b, sizeof(u8));
++}
++
++static void pop_mem_u16 (void)
++{
++ u16 w = (u16) rpnpop();
++ pop_flat((void *)rpnpop(), (void *)&w, sizeof(u16));
++}
++
++static void pop_mem_u32 (void)
++{
++ u32 d = (u32) rpnpop();
++ pop_flat((void *)rpnpop(), (void *)&d, sizeof(u32));
++}
++
++static void pop_mem_u64 (void)
++{
++ // PORT: on IA64
++ unsigned long q[2];
++ q[0] = rpnpop();
++ q[1] = rpnpop();
++ pop_flat((void *)rpnpop(), (void *)q, 2*sizeof(unsigned long));
++}
++
++/*
++ * Push pid onto rpn stack.
++ */
++static void push_pid(void)
++{
++ rpnpush((unsigned long) current->pid);
++}
++
++/*
++ * Push processor id onto rpn stack.
++ */
++static void push_procid(void)
++{
++ rpnpush((unsigned long)smp_processor_id());
++}
++
++/*
++ * Push the address of the current task structure onto rpn stack.
++ */
++static void push_task(void)
++{
++ rpnpush((unsigned long) current);
++}
++
++#ifdef DPROBES_CALLK_HOOK
++DECLARE_HOOK_HEAD(DPROBES_CALLK);
++USE_HOOK(DPROBES_CALLK);
++static void callk(void)
++{
++ struct dprobes_struct *dp = &dprobes;
++ dp->status &= ~DP_STATUS_INTERPRETER;
++ HOOK(DPROBES_CALLK, &dprobes);
++ dp->status |= DP_STATUS_INTERPRETER;
++}
++#endif
++
++/*
++ * Byte heap management routines.
++ */
++#ifdef CONFIG_SMP
++struct list_head heap_list_set[NR_CPUS];
++#define heap_list heap_list_set[smp_processor_id()]
++#else
++struct list_head heap_list;
++#define heap_list_set (&heap_list)
++#endif
++
++/* Validates the heap handle. Returns the block size in @size */
++static int heap_validate_handle(byte_t *handle, unsigned long *size)
++{
++ struct list_head *tmp;
++ struct heap_hdr *h;
++
++ list_for_each(tmp, &heap_list) {
++ h = list_entry(tmp, struct heap_hdr, list);
++ if (h->addr == handle && h->flags == HEAP_ALLOCATED) {
++ if (size) *size = h->size;
++ return 1;
++ }
++ }
++ return 0;
++}
++
++static void heap_coalesce(struct heap_hdr *heap)
++{
++ struct heap_hdr *h;
++
++ /* Combine this block with the next free block (if available) */
++ if (heap->list.next != &heap_list) {
++ h = list_entry(heap->list.next, struct heap_hdr, list);
++ if (h->flags == HEAP_FREE) {
++ heap->size += (h->size + HEAP_HDR_SIZE);
++ list_del(&h->list);
++ }
++ }
++
++ /* Combine this block with the previous free block (if available) */
++ if (heap->list.prev != &heap_list) {
++ h = list_entry(heap->list.prev, struct heap_hdr, list);
++ if (h->flags == HEAP_FREE) {
++ h->size += (heap->size + HEAP_HDR_SIZE);
++ list_del(&heap->list);
++ }
++ }
++}
++
++/* Allocation succeeds only if (size + HEAP_HDR_SIZE) is available */
++static byte_t *heap_alloc(int size)
++{
++ unsigned long sz;
++ struct list_head *tmp;
++ struct heap_hdr *next;
++
++ list_for_each(tmp, &heap_list) {
++ struct heap_hdr *h = list_entry(tmp, struct heap_hdr, list);
++ sz = h->size;
++ if ((h->flags == HEAP_FREE) && (sz > size + HEAP_HDR_SIZE)) {
++ h->size = size;
++ h->flags = HEAP_ALLOCATED;
++
++ if (sz > size) {
++ next = (struct heap_hdr *)(h->addr + size);
++ next->addr = h->addr + size + HEAP_HDR_SIZE;
++ next->flags = HEAP_FREE;
++ next->size = sz - size - HEAP_HDR_SIZE;
++ list_add(&next->list, &h->list);
++ }
++ return h->addr;
++ }
++ }
++ return NULL;
++}
++
++/* Returns the amount of memory freed */
++static unsigned long heap_free(byte_t *ptr)
++{
++ struct list_head *tmp;
++ unsigned long size = 0;
++
++ list_for_each(tmp, &heap_list) {
++ struct heap_hdr *h = list_entry(tmp, struct heap_hdr, list);
++ if (h->addr == ptr && h->flags == HEAP_ALLOCATED) {
++ h->flags = HEAP_FREE;
++ size = h->size;
++ heap_coalesce(h);
++ break;
++ }
++ }
++ return size;
++}
++
++static void heap_init(byte_t *heap)
++{
++ struct heap_hdr *h;
++
++ INIT_LIST_HEAD(&heap_list);
++ h = (struct heap_hdr *)heap;
++ h->addr = heap + HEAP_HDR_SIZE;
++ h->flags = HEAP_FREE;
++ h->size = dprobes.rec->point.heap_size - HEAP_HDR_SIZE;
++ list_add(&h->list, &heap_list);
++}
++
++/*
++ * Byte heap related instructions.
++ */
++static void pushh_u8_i(void)
++{
++ unsigned short count = get_u16_oprnd();
++ unsigned long offset = rpnpop();
++ byte_t *handle = (byte_t *)rpnpop();
++ unsigned long blk_size;
++
++ /* validate the handle */
++ if (!heap_validate_handle(handle, &blk_size)) {
++ gen_ex(EX_HEAP_INVALID_HANDLE, (unsigned long)handle, offset);
++ return;
++ }
++ while (count) {
++ /* validate the offset */
++ if (offset >= blk_size) {
++ gen_ex(EX_HEAP_INVALID_OFFSET, (unsigned long)handle,
++ offset);
++ return;
++ }
++ rpnpush((unsigned long)(*((u8 *)(handle + offset))));
++ offset++;
++ count--;
++ }
++}
++
++static void pushh_u16_i(void)
++{
++ unsigned short count = get_u16_oprnd();
++ unsigned long offset = rpnpop();
++ byte_t *handle = (byte_t *)rpnpop();
++ unsigned long blk_size;
++
++ /* validate the handle */
++ if (!heap_validate_handle(handle, &blk_size)) {
++ gen_ex(EX_HEAP_INVALID_HANDLE, (unsigned long)handle, offset);
++ return;
++ }
++ while (count) {
++ /* validate the offset */
++ if (offset + 2 > blk_size) {
++ gen_ex(EX_HEAP_INVALID_OFFSET, (unsigned long)handle,
++ offset);
++ return;
++ }
++ rpnpush((unsigned long)(*((u16 *)(handle + offset))));
++ offset += 2;
++ count--;
++ }
++}
++
++static void pushh_u32_i(void)
++{
++ unsigned short count = get_u16_oprnd();
++ unsigned long offset = rpnpop();
++ byte_t *handle = (byte_t *)rpnpop();
++ unsigned long blk_size;
++
++ /* validate the handle */
++ if (!heap_validate_handle(handle, &blk_size)) {
++ gen_ex(EX_HEAP_INVALID_HANDLE, (unsigned long)handle, offset);
++ return;
++ }
++ while (count) {
++ /* validate the offset */
++ if (offset + 4 > blk_size) {
++ gen_ex(EX_HEAP_INVALID_OFFSET, (unsigned long)handle,
++ offset);
++ return;
++ }
++ rpnpush((unsigned long)(*((u32 *)(handle + offset))));
++ offset += 4;
++ count--;
++ }
++}
++
++static void pushh_u64_i(void)
++{
++ unsigned short count = get_u16_oprnd();
++ unsigned long offset = rpnpop();
++ byte_t *handle = (byte_t *)rpnpop();
++ unsigned long blk_size;
++
++ /* validate the handle */
++ if (!heap_validate_handle(handle, &blk_size)) {
++ gen_ex(EX_HEAP_INVALID_HANDLE, (unsigned long)handle, offset);
++ return;
++ }
++ while (count) {
++ /* validate the offset */
++ if (offset + 8 > blk_size) {
++ gen_ex(EX_HEAP_INVALID_OFFSET, (unsigned long)handle,
++ offset);
++ return;
++ }
++ /* shouldn't this order depend on endianness ? */
++ rpnpush((unsigned long)(*((u64 *)(handle + offset))));
++ rpnpush((unsigned long)(*((u64 *)(handle + offset))));
++ offset += 8;
++ count--;
++ }
++}
++
++static void poph_u8_i(void)
++{
++ unsigned short count = get_u16_oprnd();
++ unsigned long offset = rpnpop();
++ byte_t *handle = (byte_t *)rpnpop();
++ unsigned long blk_size;
++
++ /* validate the handle */
++ if (!heap_validate_handle(handle, &blk_size)) {
++ gen_ex(EX_HEAP_INVALID_HANDLE, (unsigned long)handle, offset);
++ return;
++ }
++ while (count) {
++ /* validate the offset */
++ if (offset > blk_size) {
++ gen_ex(EX_HEAP_INVALID_OFFSET, (unsigned long)handle,
++ offset);
++ return;
++ }
++ *((u8 *)(handle + offset)) = (u8)rpnpop();
++ offset++;
++ count--;
++ }
++}
++
++static void poph_u16_i(void)
++{
++ unsigned short count = get_u16_oprnd();
++ unsigned long offset = rpnpop();
++ byte_t *handle = (byte_t *)rpnpop();
++ unsigned long blk_size;
++
++ /* validate the handle */
++ if (!heap_validate_handle(handle, &blk_size)) {
++ gen_ex(EX_HEAP_INVALID_HANDLE, (unsigned long)handle, offset);
++ return;
++ }
++ while (count) {
++ /* validate the offset */
++ if (offset + 2 > blk_size) {
++ gen_ex(EX_HEAP_INVALID_OFFSET, (unsigned long)handle,
++ offset);
++ return;
++ }
++ *((u16 *)(handle + offset)) = (u16)rpnpop();
++ offset += 2;
++ count--;
++ }
++}
++
++static void poph_u32_i(void)
++{
++ unsigned short count = get_u16_oprnd();
++ unsigned long offset = rpnpop();
++ byte_t *handle = (byte_t *)rpnpop();
++ unsigned long blk_size;
++
++ /* validate the handle */
++ if (!heap_validate_handle(handle, &blk_size)) {
++ gen_ex(EX_HEAP_INVALID_HANDLE, (unsigned long)handle, offset);
++ return;
++ }
++ while (count) {
++ /* validate the offset */
++ if (offset + 4 > blk_size) {
++ gen_ex(EX_HEAP_INVALID_OFFSET, (unsigned long)handle,
++ offset);
++ return;
++ }
++ *((u32 *)(handle + offset)) = (u32)rpnpop();
++ offset += 4;
++ count--;
++ }
++}
++
++static void poph_u64_i(void)
++{
++ unsigned short count = get_u16_oprnd();
++ unsigned long offset = rpnpop();
++ byte_t *handle = (byte_t *)rpnpop();
++ unsigned long blk_size;
++
++ /* validate the handle */
++ if (!heap_validate_handle(handle, &blk_size)) {
++ gen_ex(EX_HEAP_INVALID_HANDLE, (unsigned long)handle, offset);
++ return;
++ }
++ while (count) {
++ /* validate the offset */
++ if (offset + 8 > blk_size) {
++ gen_ex(EX_HEAP_INVALID_OFFSET, (unsigned long)handle,
++ offset);
++ return;
++ }
++ /* shouldn't this order depend on endianness ? */
++ *((u32 *)(handle + offset)) = (u32)rpnpop();
++ *((u32 *)(handle + offset + 4)) = (u32)rpnpop();
++ offset += 8;
++ count--;
++ }
++}
++
++static void malloc(void)
++{
++ unsigned long size = rpnpop();
++ byte_t *handle;
++
++ /*
++ * Does this allocation exceed the maximum heapsize specified for
++ * this probe ?
++ */
++ if (size + dprobes.heap_size > dprobes.rec->point.heap_size) {
++ gen_ex(EX_HEAP_NOMEM, size, 0);
++ return;
++ }
++ handle = heap_alloc(size);
++ if (!handle) {
++ gen_ex(EX_HEAP_NOMEM, size, 0);
++ return;
++ }
++ dprobes.heap_size += size;
++ rpnpush((unsigned long)handle);
++}
++
++static void free(void)
++{
++ byte_t *handle = (byte_t *)rpnpop();
++ unsigned long size;
++
++ if (!(size = heap_free(handle))) {
++ gen_ex(EX_HEAP_INVALID_HANDLE, (unsigned long)handle, 0);
++ return;
++ }
++ dprobes.heap_size -= size;
++}
++
++static void push_wid(void)
++{
++ union wid {
++ short a;
++ char b[sizeof(short)];
++ } w;
++ w.a = 0x0102;
++
++ if (sizeof(short) != 2) {
++ rpnpush(0);
++ return;
++ }
++
++ if (w.b[0] == 0x01 && w.b[1] == 0x02) {
++ rpnpush(BITS_PER_LONG);
++ } else if (w.b[0] == 0x02 && w.b[1] == 0x01) {
++ rpnpush(-BITS_PER_LONG);
++ } else {
++ rpnpush(0);
++ }
++}
++
++/*
++ * Entry point for the dprobes interpreter(Probe handler).
++ */
++void dp_interpreter(void)
++{
++ struct dprobes_struct *dp = &dprobes;
++
++ dp->call_tos = CALL_STACK_SIZE - CALL_FRAME_SIZE;
++ dp->call_stack[dp->call_tos + OFFSET_EX_HANDLER] = EX_NO_HANDLER;
++ dp->rpn_tos = dp->rpn_sbp = RPN_STACK_SIZE;
++ dp->rpn_code = dp->rec->mod->pgm.rpn_code;
++ dp->rpn_ip = dp->rec->mod->pgm.rpn_code + dp->rec->point.rpn_offset;
++ dp->log_len = dp->rec->mod->hdr.len;
++ dp->ex_log_len = MIN_ST_SIZE;
++ dp->jmp_count = 0;
++ dp->ex_parm1 = dp->ex_parm2 = 0;
++ if (dp->rec->point.heap_size > 2*HEAP_HDR_SIZE) {
++ read_lock(&dp_heap_lock);
++ heap_init(dp_heap + dp->mod->pgm.heapmax * smp_processor_id());
++ dp->heap_size = 0;
++ }
++
++ dp->status |= DP_STATUS_INTERPRETER;
++ while (dp->status & DP_STATUS_INTERPRETER) {
++ register u8 rpn_instr = *dp->rpn_ip++;
++ switch(rpn_instr) {
++ case DP_NOP: no_op(); break;
++
++ case DP_JMP: jmp(); break;
++ case DP_JLT: jlt(); break;
++ case DP_JLE: jle(); break;
++ case DP_JGT: jgt(); break;
++ case DP_JGE: jge(); break;
++ case DP_JZ: jz(); break;
++ case DP_JNZ: jnz(); break;
++ case DP_LOOP: loop(); break;
++ case DP_CALL: call(); break;
++ case DP_RET: ret(); break;
++ case DP_ABORT: dp_abort(); break;
++ case DP_REM: dp_remove(); break;
++ case DP_EXIT: dp_exit(); break;
++ case DP_EXIT_N: dp_exit_n(); break;
++
++ case DP_SETMIN_I: setmin_i(); break;
++ case DP_SETMIN: setmin(); break;
++ case DP_SETMAJ_I: setmaj_i(); break;
++ case DP_SETMAJ: setmaj(); break;
++ case DP_LOG_STR: log_str(); break;
++ case DP_LOG_MRF: log_mrf(); break;
++ case DP_LOG_I: log_i(); break;
++
++ case DP_PUSH_LVI: push_lvi(); break;
++ case DP_PUSH_LV: push_lv(); break;
++ case DP_POP_LVI: pop_lvi(); break;
++ case DP_POP_LV: pop_lv(); break;
++ case DP_MOVE_LVI: move_lvi(); break;
++ case DP_MOVE_LV: move_lv(); break;
++ case DP_INC_LVI: inc_lvi(); break;
++ case DP_INC_LV: inc_lv(); break;
++ case DP_DEC_LVI: dec_lvi(); break;
++ case DP_DEC_LV: dec_lv(); break;
++
++ case DP_PUSH_GVI: push_gvi(); break;
++ case DP_PUSH_GV: push_gv(); break;
++ case DP_POP_GVI: pop_gvi(); break;
++ case DP_POP_GV: pop_gv(); break;
++ case DP_MOVE_GVI: move_gvi(); break;
++ case DP_MOVE_GV: move_gv(); break;
++ case DP_INC_GVI: inc_gvi(); break;
++ case DP_INC_GV: inc_gv(); break;
++ case DP_DEC_GVI: dec_gvi(); break;
++ case DP_DEC_GV: dec_gv(); break;
++
++ case DP_ADD: add(); break;
++ case DP_SUB: sub(); break;
++ case DP_MUL: mul(); break;
++ case DP_DIV: div(); break;
++ case DP_IDIV: idiv(); break;
++
++ case DP_NEG: neg(); break;
++ case DP_AND: and(); break;
++ case DP_OR: or(); break;
++ case DP_XOR: xor(); break;
++ case DP_ROL_I: rol_i(); break;
++ case DP_ROL: rol(); break;
++ case DP_ROR_I: ror_i(); break;
++ case DP_ROR: ror(); break;
++ case DP_SHL_I: shl_i(); break;
++ case DP_SHL: shl(); break;
++ case DP_SHR_I: shr_i(); break;
++ case DP_SHR: shr(); break;
++ case DP_PBL: pbl(); break;
++ case DP_PBR: pbr(); break;
++ case DP_PBL_I: pbl_i(); break;
++ case DP_PBR_I: pbr_i(); break;
++ case DP_PZL: pzl(); break;
++ case DP_PZR: pzr(); break;
++ case DP_PZL_I: pzl_i(); break;
++ case DP_PZR_I: pzr_i(); break;
++#ifdef DPROBES_CALLK_HOOK
++ case DP_CALLK: callk(); break;
++#endif
++
++ case DP_XCHG: xchng(); break;
++ case DP_DUP_I: dupn(); break;
++ case DP_DUP: dup(); break;
++ case DP_ROS: ros(); break;
++
++ case DP_PUSH_R: pushr(); break;
++ case DP_POP_R: popr(); break;
++ case DP_PUSH_U: pushu(); break;
++ case DP_POP_U: popu(); break;
++
++ case DP_PUSH: push_i(); break;
++ case DP_PUSH_MEM_U8: push_mem_u8(); break;
++ case DP_PUSH_MEM_U16: push_mem_u16(); break;
++ case DP_PUSH_MEM_U32: push_mem_u32(); break;
++ case DP_PUSH_MEM_U64: push_mem_u64(); break;
++ case DP_POP_MEM_U8: pop_mem_u8(); break;
++ case DP_POP_MEM_U16: pop_mem_u16(); break;
++ case DP_POP_MEM_U32: pop_mem_u32(); break;
++ case DP_POP_MEM_U64: pop_mem_u64(); break;
++
++ case DP_PUSH_TASK: push_task(); break;
++ case DP_PUSH_PID: push_pid(); break;
++ case DP_PUSH_PROCID: push_procid(); break;
++
++ case DP_VFY_R: verify_access(0); break;
++ case DP_VFY_RW: verify_access(1); break;
++
++ case DP_SX: sx(); break;
++ case DP_UX: ux(); break;
++ case DP_RX: rx(); break;
++ case DP_PUSH_X: push_x(); break;
++ case DP_PUSH_LP: push_lp(); break;
++ case DP_PUSH_PLP: push_plp(); break;
++ case DP_POP_LP: pop_lp(); break;
++ case DP_LOG_ST: log_st(); break;
++ case DP_PURGE_ST: purge_st(); break;
++ case DP_TRACE_LV: trace_lv(); break;
++ case DP_TRACE_GV: trace_gv(); break;
++ case DP_TRACE_PV: trace_pv(); break;
++
++ case DP_PUSH_SBP: push_sbp(); break;
++ case DP_POP_SBP: pop_sbp(); break;
++ case DP_PUSH_TSP: push_tsp(); break;
++ case DP_POP_TSP: pop_tsp(); break;
++ case DP_COPY_SBP: copy_sbp(); break;
++ case DP_COPY_TSP: copy_tsp(); break;
++ case DP_PUSH_SBP_I: push_sbp_i(); break;
++ case DP_POP_SBP_I: pop_sbp_i(); break;
++ case DP_PUSH_TSP_I: push_tsp_i(); break;
++ case DP_POP_TSP_I: pop_tsp_i(); break;
++ case DP_COPY_SBP_I: copy_sbp_i(); break;
++ case DP_COPY_TSP_I: copy_tsp_i(); break;
++ case DP_PUSH_STP: push_stp(); break;
++ case DP_POP_STP: pop_stp(); break;
++ case DP_SAVE_SBP: save_sbp(); break;
++ case DP_RESTORE_SBP: restore_sbp(); break;
++ case DP_SAVE_TSP: save_tsp(); break;
++ case DP_RESTORE_TSP: restore_tsp(); break;
++
++ case DP_LOG: log(); break;
++ case DP_LOG_LV: log_lv(); break;
++ case DP_LOG_GV: log_gv(); break;
++
++ case DP_MALLOC: malloc(); break;
++ case DP_FREE: free(); break;
++ case DP_PUSH_WID: push_wid(); break;
++ case DP_PUSHH_U8_I: pushh_u8_i(); break;
++ case DP_PUSHH_U16_I: pushh_u16_i(); break;
++ case DP_PUSHH_U32_I: pushh_u32_i(); break;
++ case DP_PUSHH_U64_I: pushh_u64_i(); break;
++ case DP_POPH_U8_I: poph_u8_i(); break;
++ case DP_POPH_U16_I: poph_u16_i(); break;
++ case DP_POPH_U32_I: poph_u32_i(); break;
++ case DP_POPH_U64_I: poph_u64_i(); break;
++
++ default: dp_asm_interpreter(rpn_instr); break;
++ }
++ }
++ if (dp->rec->point.heap_size > 2*HEAP_HDR_SIZE)
++ read_unlock(&dp_heap_lock);
++ return;
++}
++
++void dprobes_interpreter_code_end(void) { }
+diff -urNp linux.orig/drivers/dprobes/i386/dprobes.c linux-2.6.0-test11/drivers/dprobes/i386/dprobes.c
+--- linux.orig/drivers/dprobes/i386/dprobes.c 1970-01-01 05:30:00.000000000 +0530
++++ linux-2.6.0-test11/drivers/dprobes/i386/dprobes.c 2003-12-15 15:51:48.000000000 +0530
+@@ -0,0 +1,808 @@
++/*
++ * IBM Dynamic Probes
++ * Copyright (c) International Business Machines Corp., 2000
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ */
++
++#include <linux/dprobes.h>
++#include <linux/vmalloc.h>
++#include <linux/module.h>
++#include <linux/serial_reg.h>
++#include <linux/ctype.h>
++
++#include <asm/kwatch.h>
++#include <asm/uaccess.h>
++#include <asm/io.h>
++#include <asm/dprobes_exclude.h>
++#ifdef CONFIG_DEBUGREG
++#include <asm/debugreg.h>
++#endif
++
++void dprobes_asm_code_start(void) { }
++
++#ifdef CONFIG_MAGIC_SYSRQ
++extern unsigned long emergency_remove;
++#endif
++
++/*
++ * Format strings for printing out the log.
++ */
++#define DP_LOG_HDR_FMT "dprobes(%d,%d) "
++#define DP_LOG_CPU_FMT "cpu=%d "
++#define DP_LOG_PROCNAME_FMT "name=%s "
++#define DP_LOG_PID_FMT "pid=%d "
++#define DP_LOG_UID_FMT "uid=%d "
++#define DP_LOG_CS_EIP_FMT "cs=%x eip=%08lx "
++#define DP_LOG_SS_ESP_FMT "ss=%x esp=%08lx "
++#define DP_LOG_TSC_FMT "tsc=%08lx:%08lx "
++#define DP_LOG_NEWLINE_FMT "\n"
++#define DP_LOG_DATA_FMT "%x "
++#define DP_LOG_DATA_FT "%x"
++
++static void log_stack_trace(struct dprobes_struct *dp)
++{
++ unsigned long i;
++ byte_t *ex_log;
++ unsigned char *tmp = dp->log_hdr;
++ unsigned long ex_log_len = dp->ex_log_len;
++
++ if (ex_log_len > MIN_ST_SIZE) {
++ tmp += sprintf(tmp, DP_LOG_HDR_FMT, 0, 0);
++
++#ifdef CONFIG_SMP
++ tmp += sprintf(tmp, DP_LOG_CPU_FMT, smp_processor_id());
++#endif
++ printk("%s ", dp->log_hdr);
++
++ ex_log_len -= MIN_ST_SIZE;
++ ex_log = dp->ex_log + MIN_ST_SIZE;
++ for (i = 0; i < ex_log_len; i++)
++ printk(DP_LOG_DATA_FMT, ex_log[i]);
++ printk(DP_LOG_NEWLINE_FMT);
++ }
++}
++
++static void fmt_log_hdr(struct dprobes_struct *dp)
++{
++ unsigned char *tmp = dp->log_hdr;
++ unsigned long log_flags = dp->mod->pgm.flags & DP_LOG_MASK;
++
++ tmp += sprintf(tmp, DP_LOG_HDR_FMT, dp->major, dp->minor);
++
++#ifdef CONFIG_SMP
++ tmp += sprintf(tmp, DP_LOG_CPU_FMT, smp_processor_id());
++#endif
++ if (log_flags & DP_LOG_PID)
++ tmp += sprintf(tmp, DP_LOG_PID_FMT, current->pid);
++ if (log_flags & DP_LOG_UID)
++ tmp += sprintf(tmp, DP_LOG_UID_FMT, current->uid);
++ if (log_flags & DP_LOG_CS_EIP)
++ tmp += sprintf(tmp, DP_LOG_CS_EIP_FMT, dp->cs, dp->eip);
++ if (log_flags & DP_LOG_SS_ESP)
++ tmp += sprintf(tmp, DP_LOG_SS_ESP_FMT, dp->ss, dp->esp);
++ if (log_flags & DP_LOG_TSC) {
++ struct timeval ts;
++ do_gettimeofday(&ts);
++ tmp += sprintf(tmp, DP_LOG_TSC_FMT, ts.tv_sec, ts.tv_usec);
++ }
++ if (log_flags & DP_LOG_PROCNAME)
++ tmp += sprintf(tmp, DP_LOG_PROCNAME_FMT, current->comm);
++ tmp += sprintf(tmp, DP_LOG_NEWLINE_FMT);
++
++ tmp += sprintf(tmp, DP_LOG_HDR_FMT, dp->major, dp->minor);
++ *tmp = '\0';
++
++ return;
++}
++
++
++#define ROWS 16
++/*
++ * Saves (writes) the log to the standard kernel log.
++ */
++static void log_dumpformat(unsigned long log_len, byte_t *log)
++{
++ unsigned long i, start = 0, end = ROWS;
++ unsigned int off=0x0;
++ char c;
++
++ while (start < log_len) {
++ printk("0x%8.8x : ", (unsigned int)off);
++ for( i = start; i < end; i++) {
++ printk("%2.2x", 0xff & log[i]);
++ if (((i + 1) % 4) == 0)
++ printk(" ");
++ }
++
++ if ((end - start) < ROWS)
++ for (i = 0; i < ((ROWS - (end - start)) * 2) + 4 - ((end - start) / 4); i++)
++ printk(" ");
++
++ printk(": ");
++ for( i = start ; i < end ; i++ ) /* Now print the ASCII field. */
++ {
++ c = 0x7f & log[i]; /* mask out bit 7 */
++ if (!(isprint(c))) /* If not printable */
++ printk("."); /* print a dot */
++ else
++ printk("%c",c);
++
++ }
++
++ printk(DP_LOG_NEWLINE_FMT);
++ if ( (log_len - end) > ROWS)
++ end += ROWS;
++ else
++ end = log_len;
++ start += ROWS;
++ off +=0x10;
++ }
++
++}
++
++static void log_stack_trace_dump(struct dprobes_struct *dp)
++{
++ byte_t *ex_log;
++ unsigned char *tmp = dp->log_hdr;
++ unsigned long ex_log_len = dp->ex_log_len;
++ if (ex_log_len > MIN_ST_SIZE) {
++ tmp += sprintf(tmp, DP_LOG_HDR_FMT, 0, 0);
++
++#ifdef CONFIG_SMP
++ tmp += sprintf(tmp, DP_LOG_CPU_FMT, smp_processor_id());
++#endif
++ printk("%s \n", dp->log_hdr);
++
++ ex_log_len -= MIN_ST_SIZE;
++ ex_log = dp->ex_log + MIN_ST_SIZE;
++ log_dumpformat(ex_log_len, ex_log);
++
++ }
++}
++
++/*
++ * Saves (writes) the log to the standard kernel log.
++ */
++static int log_to_klog_dump(struct dprobes_struct *dp)
++{
++ byte_t *log = dp->log + dp->mod->hdr.len;
++ unsigned long log_len = dp->log_len - dp->mod->hdr.len;
++
++
++ fmt_log_hdr(dp);
++ printk("%s \n", dp->log_hdr);
++ log_dumpformat( log_len, log);
++ return 0;
++}
++
++/*
++ * Saves (writes) the log to the standard kernel log.
++ */
++static int log_to_klog(struct dprobes_struct *dp)
++{
++ unsigned long i;
++ byte_t *log = dp->log + dp->mod->hdr.len;
++ unsigned long log_len = dp->log_len - dp->mod->hdr.len;
++
++ fmt_log_hdr(dp);
++ printk("%s ", dp->log_hdr);
++
++ for (i = 0; i < log_len; i++) {
++ if (log[i] < 0xf)
++ printk(DP_LOG_DATA_FT, 0);
++ printk(DP_LOG_DATA_FMT, log[i]);
++ }
++ printk(DP_LOG_NEWLINE_FMT);
++ return 0;
++}
++
++/*
++ * The order in which the optional header elements is written out has to be the
++ * same order in which the DP_HDR_* constants are defined in
++ * include/linux/dprobes.h. The formatter is expected to look for the optional
++ * elements in the same order.
++ */
++static void fmt_log_hdr_raw(struct dprobes_struct *dp)
++{
++ unsigned char *tmp = dp->log;
++ unsigned long mask = dp->mod->hdr.mask;
++
++ dp->mod->hdr.major = dp->major;
++ dp->mod->hdr.minor = dp->minor;
++ *(struct dp_trace_hdr_struct *)tmp = dp->mod->hdr;
++ tmp += sizeof(dp->mod->hdr);
++
++#ifdef CONFIG_SMP
++ *(unsigned int *)tmp = smp_processor_id();
++ tmp += sizeof(int);
++#endif
++ if (mask & DP_HDR_PID) {
++ *(pid_t *)tmp = current->pid;
++ tmp += sizeof(pid_t);
++ }
++ if (mask & DP_HDR_UID) {
++ *(uid_t *)tmp = current->uid;
++ tmp += sizeof(uid_t);
++ }
++ if (mask & DP_HDR_CS) {
++ *(unsigned short *)tmp = dp->cs;
++ tmp += sizeof(dp->cs);
++ }
++ if (mask & DP_HDR_EIP) {
++ *(unsigned long *)tmp = dp->eip;
++ tmp += sizeof(dp->eip);
++ }
++ if (mask & DP_HDR_SS) {
++ *(unsigned short *)tmp = dp->ss;
++ tmp += sizeof(dp->ss);
++ }
++ if (mask & DP_HDR_ESP) {
++ *(unsigned long *)tmp = dp->esp;
++ tmp += sizeof(dp->esp);
++ }
++ if (mask & DP_HDR_TSC) {
++ struct timeval ts;
++ do_gettimeofday(&ts);
++ *(struct timeval *)tmp = ts;
++ tmp += sizeof(ts);
++ }
++ if (mask & DP_HDR_PROCNAME) {
++ memcpy(tmp, current->comm, 16);
++ tmp += 16;
++ }
++ return;
++}
++
++/*
++ * dp->mod->hdr is changed here. Hence should be called only after
++ * fmt_log_hdr_raw().
++ */
++static void fmt_st_hdr_raw(struct dprobes_struct *dp)
++{
++ unsigned char *tmp = dp->ex_log;
++ dp->mod->hdr.mask = DP_HDR_MAJOR | DP_HDR_MINOR;
++ dp->mod->hdr.major = DP_ST_MAJOR;
++ dp->mod->hdr.minor = DP_ST_MINOR;
++ dp->mod->hdr.len = MIN_ST_SIZE;
++ *(struct dp_trace_hdr_struct *)tmp = dp->mod->hdr;
++ tmp += sizeof(dp->mod->hdr);
++#ifdef CONFIG_SMP
++ dp->mod->hdr.mask |= DP_HDR_CPU;
++ *(unsigned int *)tmp = smp_processor_id();
++ tmp += sizeof(int);
++ dp->mod->hdr.len += sizeof(int);
++#endif
++}
++
++#if defined(CONFIG_TRACE) || defined(CONFIG_TRACE_MODULE)
++#include <linux/trace.h>
++
++static int log_to_ltt(struct dprobes_struct *dp)
++{
++ trace_raw_event(dp->mod->trace_id, dp->log_len, dp->log);
++ return 0;
++}
++
++static int log_st_to_ltt(struct dprobes_struct *dp)
++{
++ trace_raw_event(dp->mod->trace_id, dp->ex_log_len, dp->ex_log);
++ return 0;
++}
++#else
++static int log_to_ltt(struct dprobes_struct *dp)
++{
++ printk(KERN_WARNING "dprobes: Linux Trace Toolkit not available.\n");
++ return 0;
++}
++
++static int log_st_to_ltt(struct dprobes_struct *dp)
++{
++ printk(KERN_WARNING "dprobes: Linux Trace Toolkit not available.\n");
++ return 0;
++}
++#endif
++
++#ifdef CONFIG_EVLOG
++#include <linux/evl_log.h>
++static int log_to_evl(struct dprobes_struct *dp)
++{
++ /*
++ * We will need to modify this in future to create a DPROBES facility
++ * type and use dp->id instead of (major,minor) for event type. Even the
++ * priority ought to be something other than LOG_KERN.
++ */
++ posix_log_write(LOG_KERN,
++ (dp->major << 16) | dp->minor, /* dp->id */
++ LOG_INFO,
++ (dp->log + dp->mod->hdr.len),
++ (dp->log_len - dp->mod->hdr.len),
++ POSIX_LOG_BINARY,
++ 0);
++ return 0;
++}
++#else
++static int log_to_evl(struct dprobes_struct *dp)
++{
++ printk(KERN_WARNING "dprobes: POSIX Event Logging not available.\n");
++ return 0;
++}
++#endif
++
++#define BOTH_EMPTY_TR (UART_LSR_TEMT | UART_LSR_THRE)
++#define COM1_ADDR 0x3f8
++#define COM2_ADDR 0x2f8
++
++/*
++ * Wait for the transmitter buffer to become empty.
++ */
++static inline void wait_for_xmitr_empty(int port)
++{
++ int lsr;
++ unsigned int tmout = 1000000;
++ do {
++ lsr = inb(port + UART_LSR);
++ if (--tmout == 0) break;
++ } while ((lsr & BOTH_EMPTY_TR) != BOTH_EMPTY_TR);
++}
++
++/*
++ * This routine sends the raw binary data to com port. The receiving
++ * end should be capable of handling raw bytes.
++ */
++static int log_to_com_port(struct dprobes_struct *dp, int port)
++{
++ int i, ier;
++ byte_t *log = dp->log;
++ byte_t *ex_log = dp->ex_log;
++ unsigned long log_len = dp->log_len;
++ unsigned long ex_log_len = dp->ex_log_len;
++
++ /* store UART configuration and program it in polled mode */
++ ier = inb(port + UART_IER);
++ outb(0x00, port + UART_IER);
++
++ /* send the log data to com port */
++ fmt_log_hdr_raw(dp);
++ for (i = 0; i < log_len; i++) {
++ wait_for_xmitr_empty(port);
++ outb(log[i], port + UART_TX);
++ }
++
++ /* send CR and LF characters */
++ wait_for_xmitr_empty(port);
++ outb(0x0d, port + UART_TX);
++ wait_for_xmitr_empty(port);
++ outb(0x0a, port + UART_TX);
++
++ /* send the stack trace data(if present) to com port */
++ if (ex_log_len > MIN_ST_SIZE) {
++ fmt_st_hdr_raw(dp);
++ for (i = 0; i < ex_log_len; i++) {
++ wait_for_xmitr_empty(port);
++ outb(ex_log[i], port + UART_TX);
++ }
++
++ /* send CR and LF characters */
++ wait_for_xmitr_empty(port);
++ outb(0x0d, port + UART_TX);
++ wait_for_xmitr_empty(port);
++ outb(0x0a, port + UART_TX);
++ }
++
++ /* restore the configuration of UART */
++ wait_for_xmitr_empty(port);
++ outb(ier, port + UART_IER);
++ return 0;
++}
++
++static void save_log(struct dprobes_struct *dp)
++{
++ switch(dp->mod->pgm.flags & DP_LOG_TARGET_MASK) {
++ case DP_LOG_TARGET_COM1:
++ log_to_com_port(dp, COM1_ADDR);
++ break;
++ case DP_LOG_TARGET_COM2:
++ log_to_com_port(dp, COM2_ADDR);
++ break;
++ case DP_LOG_TARGET_LTT:
++ fmt_log_hdr_raw(dp);
++ log_to_ltt(dp);
++ if (dp->ex_log_len > MIN_ST_SIZE) {
++ fmt_st_hdr_raw(dp);
++ log_st_to_ltt(dp);
++ }
++ break;
++ case DP_LOG_TARGET_EVL:
++ log_to_evl(dp);
++ break;
++ case DP_UNFORMATTED_OUTPUT:
++ log_to_klog(dp);
++ log_stack_trace(dp);
++ break;
++ default:
++ log_to_klog_dump(dp);
++ log_stack_trace_dump(dp);
++ break;
++ }
++ return;
++}
++
++#ifdef CONFIG_DEBUGREG
++static inline void set_rf(struct dp_record_struct *rec, struct pt_regs *regs)
++{
++ unsigned short wtype = rec->point.probe & DP_WATCHTYPE_MASK;
++ if (wtype == DP_WATCHTYPE_EXECUTE) {
++ regs->eflags |= EF_RF;
++ }
++ return;
++}
++
++inline void delete_watchpoint(unsigned int cond, struct pt_regs *regs, struct dp_record_struct *rec)
++{
++ int dbno = rec->dbregno;
++ unsigned long dr = read_dr(7);
++ RESET_DR7(dr, dbno);
++ dr_free(dbno);
++ write_dr(7, dr);
++}
++
++#else
++inline void delete_watchpoint(unsigned int cond, struct pt_regs *regs, struct dp_record_struct *rec)
++{
++ return;
++}
++
++static inline void set_rf(struct dp_record_struct *rec, struct pt_regs *regs)
++{
++ return;
++}
++
++inline int dr_trap_type(unsigned int cond)
++{
++ return 0;
++}
++#endif
++/*
++ * This routine save the registers during handling of kernel, userspace and
++ * watchpoint probes and also updates the status in the dp_record_struct.
++ */
++void save_regs(struct dp_record_struct *rec, unsigned long addr,
++ struct pt_regs *regs)
++{
++ struct dprobes_struct *dp = &dprobes;
++
++ dp->rec = rec;
++ dp->probe_addr = addr;
++ dp->regs = regs;
++ dp->mod = rec->mod;
++ dp->major = rec->point.major;
++ dp->minor = rec->point.minor;
++ dp->status = 0UL;
++ dp->cs = regs->xcs;
++ dp->eip = regs->eip - sizeof(opcode_t);
++ dp->ss = regs->xss;
++ dp->esp = regs->esp;
++ if (regs->xcs & 3) {
++ dp->status |= DP_USER_PROBE;
++ dp->uregs = regs;
++ } else {
++ dp->status |= DP_KERNEL_PROBE;
++ dp->uregs = (struct pt_regs * )((unsigned long)(current) + 2 * PAGE_SIZE - sizeof(struct pt_regs));
++ }
++ dp->status |= DP_STATUS_FIRSTFPU;
++
++ /* rec->count > 0 for passcount */
++ if (rec->status & DP_REC_STATUS_ACTIVE && rec->count >= 0) {
++ dp_interpreter();
++ if (rec->status & (DP_REC_STATUS_DISABLED | DP_REC_STATUS_REMOVED)) {
++ if (rec->status & DP_REC_STATUS_ACTIVE) {
++ rec->status &= ~DP_REC_STATUS_ACTIVE;
++ rec->status |= DP_REC_STATUS_DISABLED;
++ }
++ }
++ } else {
++ dp->status |= DP_STATUS_ABORT;
++ }
++}
++int dp_pre_handler(struct kprobe *kp, struct pt_regs *regs)
++{
++ struct dprobes_struct *dp = &dprobes;
++ struct dp_record_struct *rec = container_of(kp, typeof(*rec), kp);
++
++ if (rec->status & (DP_REC_STATUS_DISABLED | DP_REC_STATUS_REMOVED)) {
++ return 1;
++ }
++
++#ifdef CONFIG_MAGIC_SYSRQ
++ /* check if probes are marked for emergency removal */
++ if (test_bit(0, &emergency_remove))
++ return 1;
++#endif
++ dp->rec = rec;
++ save_regs(rec, (unsigned long)rec->kp.addr, regs);
++ dp->status |= DP_STATUS_SS;
++ return 0;
++}
++
++void dp_post_handler(struct kprobe *kp, struct pt_regs *regs, unsigned long flags)
++{
++ struct dprobes_struct *dp = &dprobes;
++
++ dp->rec->count++;
++ if (dp->rec->count >= dp->rec->point.maxhits) {
++ dp->rec->status &= ~DP_REC_STATUS_ACTIVE;
++ dp->rec->status |= DP_REC_STATUS_DISABLED;
++ }
++ if (dp->status & DP_STATUS_ABORT)
++ return;
++ save_log(dp);
++}
++
++int dp_fault_handler(struct kprobe *kp, struct pt_regs *regs, int trapnr)
++{
++ struct dprobes_struct *dp = &dprobes;
++
++ if (trapnr != 13 && trapnr != 14)
++ return 0;
++
++ if (dp->status & DP_STATUS_INTERPRETER) {
++ const struct exception_table_entry *fixup;
++ if ((fixup = search_exception_tables(regs->eip)) != 0) {
++ regs->eip = fixup->fixup;
++ return 1;
++ }
++ } else if (dp->status & DP_STATUS_SS) {
++ if (dp->rec->point.logonfault) {
++ dp->rec->count++;
++ save_log(dp);
++ }
++ }
++ return 0;
++}
++
++/*
++ * If there are multiple debug traps simultaneously, we handle the
++ * single step condition first (DR_STEP) as it is related to completing
++ * a previous debug trap. We then handle the debug register hits.
++ */
++int dp_do_debug(struct pt_regs * regs, unsigned long condition)
++{
++#ifdef CONFIG_DEBUGREG
++#if 0
++ /* assume that only one watchpoint possible on an address. */
++ if (condition & (DR_TRAP0|DR_TRAP1|DR_TRAP2|DR_TRAP3)) {
++ if (dp_trap(regs, DP_PROBE_WATCHPOINT, condition, dr_trap_addr(condition))) {
++ return 1;
++ }
++ }
++#endif
++#endif
++ return 0;
++}
++
++#include <asm/desc.h>
++#include <asm/ldt.h>
++#ifndef GDT_ENTRIES
++#define GDT_ENTRIES (__TSS(NR_CPUS))
++#endif
++static inline int is_fpu_instn(byte_t *addr)
++{
++ if (((*addr & 0xf8) == 0xd8) || (*addr == 0x9b))
++ return 1;
++ return 0;
++}
++/* Use this array to store the dp_record_struct during registration, and index
++ * using the debugreg number in the handler to get back, the dp_record_struct.
++ */
++static struct dp_record_struct *kwatch_rec[DP_MAX_WATCHPOINT];
++
++/*
++ * dp_kwatch_handler: Dprobes handler for watchpoint probes, registerd through
++ * kwatch interface. This routine is called by the watch point probe handler,
++ * when the probe is hit.
++ */
++
++void dp_kwatch_handler(struct kwatch *kw, struct pt_regs *regs, int debugreg)
++{
++ struct dprobes_struct *dp = &dprobes;
++ struct dp_record_struct *rec = kwatch_rec[debugreg];
++ if ((!rec) || (rec->status & (DP_REC_STATUS_DISABLED |
++ DP_REC_STATUS_REMOVED))) {
++ return ;
++ }
++ rec->count++;
++ save_regs(rec, kw->addr, regs);
++ if (dp->status & DP_STATUS_ABORT)
++ return;
++ save_log(dp);
++ dp->status &= ~DP_STATUS_DONE;
++ return;
++}
++
++/*
++ * Unregister watchpoint probes.
++ */
++
++static inline int remove_wp(byte_t *addr, struct dp_record_struct *rec)
++{
++ kwatch_rec[rec->dbregno] = NULL;
++ unregister_kwatch(rec->dbregno);
++ return 0;
++}
++
++/* Check the range for debug register watchpoint */
++static inline int invalid_range(unsigned long len)
++{
++ return (len > 3 ) ? 1 : 0;
++}
++
++/*
++ * Insert watchpoint probes, using the kwatch interface.
++ */
++static inline int insert_wp(byte_t *addr, struct dp_record_struct *rec,
++ struct dp_module_struct *m, struct page * page)
++{
++ int ret;
++
++ if (invalid_range(rec->point.len)) {
++ rec->status |= DP_REC_STATUS_WATCHPOINT_LEN_INVALID;
++ return -1;
++ }
++ ret = register_kwatch((unsigned long)addr, (u8)(rec->point.len + 1),
++ (u8)(rec->point.probe & DP_WATCHTYPE_MASK), dp_kwatch_handler);
++
++ if (ret < 0) {
++ rec->status |= DP_REC_STATUS_KPROBE_ERR;
++ return -1;
++ }
++
++ kwatch_rec[ret] = rec;
++ rec->status |= DP_REC_STATUS_ACTIVE;
++ rec->dbregno = ret;
++ return 0;
++}
++
++/*
++ * Writing the breakpoint instruction should be the last thing done
++ * in this function to ensure that all the structures that
++ * are needed to locate the probe are in place before a probe can
++ * be hit.
++ */
++static inline int insert_bp(byte_t *addr, struct dp_record_struct *rec,
++ struct dp_module_struct *m, struct page * page)
++{
++ if (dprobes_excluded((unsigned long)addr)) {
++ rec->status |= DP_REC_STATUS_EXCLUDED;
++ return -1;
++ }
++
++ if (!(m->pgm.flags & DP_MODTYPE_USER) &&
++ !(((unsigned long)addr) >= m->base &&
++ ((unsigned long)addr) < (m->end + m->base))) {
++ rec->status |= DP_REC_STATUS_INVALID_OFFSET;
++ return -1;
++ }
++ if (*addr != rec->point.opcode) {
++ if (m->pgm.flags & DP_DONT_VERIFY_OPCODES) {
++ rec->point.opcode = *addr;
++ } else {
++ if (!(m->pgm.flags & DP_MODTYPE_USER) ||
++ !IS_COW_PAGE(page, m->inode)) {
++ rec->point.actual_opcode = *addr;
++ rec->status |= DP_REC_STATUS_MISMATCH;
++ }
++ return -1;
++ }
++ }
++
++ /* all okay, register the probe */
++ rec->kp.addr = addr;
++ rec->kp.pre_handler = dp_pre_handler;
++ rec->kp.post_handler = dp_post_handler;
++ rec->kp.fault_handler = dp_fault_handler;
++
++ if (register_kprobe(&rec->kp)) {
++ rec->status |= DP_REC_STATUS_KPROBE_ERR;
++ return -1;
++ }
++ rec->status |= DP_REC_STATUS_ACTIVE;
++ return 0;
++}
++
++int __insert_probe(byte_t *addr, struct dp_record_struct *rec,
++ struct dp_module_struct *m, struct page * page)
++{
++ if (rec->point.probe & DP_PROBE_BREAKPOINT)
++ return insert_bp(addr, rec, m, page);
++ else
++ return insert_wp(addr, rec, m, page);
++}
++
++static inline void remove_bp(byte_t *addr, struct dp_record_struct *rec)
++{
++ unregister_kprobe(&rec->kp);
++}
++
++int __remove_probe(byte_t * addr, struct dp_record_struct *rec)
++{
++ if (rec->point.probe & DP_PROBE_BREAKPOINT)
++ remove_bp(addr, rec);
++ else
++ remove_wp(addr, rec);
++ return 0;
++}
++/*
++ * Insert breakpoints in the user space at the given address.
++ * Need to specify address, page and vma in kprobe structure.
++ */
++inline int insert_probe_userspace(byte_t *addr, struct dp_record_struct *rec,
++ struct page *page, struct vm_area_struct *vma)
++{
++ rec->kp.user->addr = addr;
++ rec->kp.user->page = page;
++ rec->kp.user->vma = vma;
++ if (insert_kprobe_user(&rec->kp)) {
++ rec->status |= DP_REC_STATUS_KPROBE_ERR;
++ return -1;
++ }
++ rec->status |= DP_REC_STATUS_ACTIVE;
++ return 0;
++}
++
++/*
++ * Remove breakpoints in the user space previously inserted at the given
++ * address. Need to specify address, page and vma in kprobe structure.
++ */
++inline int remove_probe_userspace(byte_t *addr, struct dp_record_struct *rec,
++ struct page *page, struct vm_area_struct *vma)
++{
++ rec->kp.user->addr = addr;
++ rec->kp.user->page = page;
++ rec->kp.user->vma = vma;
++ return remove_kprobe_user(&rec->kp);
++
++}
++
++
++/*
++ * register user space probes before actually inserting the probes.
++ */
++int register_userspace_probes(struct dp_record_struct *rec)
++{
++ rec->kp.pre_handler = dp_pre_handler;
++ rec->kp.post_handler = dp_post_handler;
++ rec->kp.fault_handler = dp_fault_handler;
++ rec->up.offset = rec->point.offset;
++ rec->up.inode = rec->mod->inode;
++ rec->kp.user = &(rec->up);
++ if (register_kprobe_user(&rec->kp)) {
++ rec->status |= DP_REC_STATUS_KPROBE_ERR;
++ return -1;
++ }
++ rec->status |= DP_REC_STATUS_ACTIVE;
++ return 0;
++}
++
++/*
++ * Unregister the user space probes previously registered.
++ * Make sure all the probes for a pair of inode and offset are removed
++ * before unregistering.
++ */
++void unregister_userspace_probes(struct dp_record_struct *rec)
++{
++ unregister_kprobe_user(&rec->kp);
++}
++void dprobes_asm_code_end(void) { }
+diff -urNp linux.orig/drivers/dprobes/i386/dprobes_in.c linux-2.6.0-test11/drivers/dprobes/i386/dprobes_in.c
+--- linux.orig/drivers/dprobes/i386/dprobes_in.c 1970-01-01 05:30:00.000000000 +0530
++++ linux-2.6.0-test11/drivers/dprobes/i386/dprobes_in.c 2003-12-15 15:51:48.000000000 +0530
+@@ -0,0 +1,903 @@
++/*
++ * IBM Dynamic Probes
++ * Copyright (c) International Business Machines Corp., 2000
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ */
++
++/*
++ * This is a special file, part of the dprobes interpreter that contains
++ * architecture-specific functions. It is not compiled as a seperate unit.
++ * It is #included into the arch-independent dprobes_in.c at a specific
++ * location.
++ */
++#include <linux/binfmts.h>
++#ifdef CONFIG_DUMP
++#include <linux/dump.h>
++#endif
++#ifdef CONFIG_KDB
++#include <linux/kdb.h>
++#endif
++#include <asm/desc.h>
++#include <asm/ldt.h>
++#ifndef GDT_ENTRIES
++#define GDT_ENTRIES (__TSS(NR_CPUS))
++#endif
++
++/*
++ * Convert segmented address (selector : offset) to flat linear address.
++ */
++static int dp_seg_to_flat(unsigned long sel, unsigned long off, void **faddr)
++{
++ struct Xgt_desc_struct g;
++ unsigned long ldt_desc, ldt_base, ldtr, seg_desc, seg_base;
++ unsigned long gdt_index = (sel & ~7);
++ mm_context_t *x = ¤t->mm->context;
++
++ __asm__ __volatile__ ("sgdt (%0)" : : "r"((unsigned long) &g));
++
++ if (sel & 4) {
++ if (!(x->ldt && ((sel >> 3) < x->size)))
++ return 1;
++
++ __asm__ __volatile__ ("sldt %0" :"=r"(ldtr));
++ ldt_desc = g.address + ((ldtr & ~7));
++ ldt_base = _get_base ((char *)ldt_desc);
++ seg_desc = ldt_base + (gdt_index);
++ seg_base = _get_base ((char *)seg_desc);
++ }
++ else {
++ if ((sel >> 3) > GDT_ENTRIES)
++ return 1;
++ seg_desc = g.address + gdt_index;
++ seg_base = _get_base ((char *)seg_desc);
++ }
++ *faddr = (void *)(seg_base + off);
++ return 0;
++}
++
++static void seg2lin(void)
++{
++ void *faddr;
++ unsigned long off = rpnpop();
++ unsigned long sel = rpnpop();
++
++ if (dp_seg_to_flat(sel, off, &faddr)) {
++ gen_ex(EX_SEG_FAULT, sel, 0);
++ return;
++ }
++ rpnpush((unsigned long) faddr);
++}
++
++#ifdef CONFIG_KDB
++/*
++ * Temporarily restore the orignal opcode befor calling the kdb
++ * so that kdb shows correct disassembly.
++ */
++static inline void restore_opcode(void)
++{
++ if (dprobes.rec->point.probe & DP_PROBE_BREAKPOINT) {
++ dprobes.reset_addr = dprobes.probe_addr;
++ *((byte_t *)dprobes.reset_addr) = dprobes.opcode;
++ flush_page_to_ram(dprobes.reset_addr);
++ }
++}
++
++/*
++ * After kdb terminates, restore the breakpoint.
++ */
++static inline void restore_int3(void)
++{
++ if (dprobes.rec->point.probe & DP_PROBE_BREAKPOINT) {
++ dprobes.reset_addr = dprobes.probe_addr;
++ *((byte_t *)dprobes.reset_addr) = DP_INSTR_BREAKPOINT;
++ flush_page_to_ram(dprobes.reset_addr);
++ }
++}
++
++void call_kdb(void)
++{
++ unsigned long eip = dprobes.regs->eip - 1;
++ if (dprobes.status & DP_KERNEL_PROBE) {
++ if (dprobes.rec->point.probe & DP_PROBE_BREAKPOINT)
++ dprobes.regs->eip--;
++ restore_opcode();
++ kdb(KDB_REASON_CALL, 0, dprobes.regs);
++ restore_int3();
++ if (eip == dprobes.regs->eip)
++ dprobes.regs->eip++;
++ }
++}
++#else
++
++void call_kdb(void)
++{
++ printk("Dprobes: Kernel Debugger is not present.\n");
++ return;
++}
++#endif
++/*
++ * Exiting the interpreter through the registered facility n.
++ */
++static void dp_exit_n(void)
++{
++ u8 facility = get_u8_oprnd();
++ switch(facility) {
++ case DP_EXIT_TO_SGI_KDB:
++ call_kdb();
++ break;
++
++ case DP_EXIT_TO_SGI_VMDUMP:
++#ifdef CONFIG_DUMP
++ if (dprobes.status & DP_KERNEL_PROBE)
++ dump("Dumping due to dprobes", dprobes.regs);
++#else
++ printk("dprobes: Crash dump facility is not present.\n");
++#endif
++ break;
++ case DP_EXIT_TO_CORE_DUMP:
++ if (dprobes.status & DP_USER_PROBE) {
++ if (do_coredump(SIGINT, SIGINT, dprobes.regs)) {
++ current->mm->dumpable = 1;
++ printk("dprobes(%d,%d) process %s dumped core\n", dprobes.major, dprobes.minor, current->comm);
++ }
++ else
++ printk("dprobes(%d,%d) exit to core dump failed\n", dprobes.major, dprobes.minor);
++ }
++ break;
++ default: gen_ex(EX_INVALID_OPERAND, 4, facility); return;
++
++ }
++ dprobes.status &= ~DP_STATUS_INTERPRETER;
++}
++
++/*
++ * Interpreter's version of copy_xxx_user functions. These need to save
++ * and restore cr2 contents to be able to correctly handle probes in
++ * page fault handler.
++ */
++static int dp_intr_copy_from_user(void *to, void *from, int len)
++{
++ unsigned long address;
++ int retval;
++ __asm__("movl %%cr2,%0":"=r" (address)); /* save cr2 contents */
++ retval = __copy_from_user(to, from, len);
++ __asm__("movl %0,%%cr2": :"r" (address)); /* restore cr2 contents */
++ return retval;
++}
++
++static int dp_intr_copy_to_user(void *to, void *from, int len)
++{
++ unsigned long address;
++ int retval;
++ __asm__("movl %%cr2,%0":"=r" (address)); /* save cr2 contents */
++ retval = __copy_to_user(to, from, len);
++ __asm__("movl %0,%%cr2": :"r" (address)); /* restore cr2 contents */
++ return retval;
++}
++
++#define BITS_IN_UL 32
++
++#define PROPAGATE_BIT(name, OPERATOR) \
++static void name(void) \
++{ \
++ unsigned long index, num, pos, val; \
++ index = rpnpop(); \
++ if (!index || index > BITS_IN_UL) { \
++ gen_ex(EX_INVALID_OPERAND, 3, index); \
++ return; \
++ } \
++ num = rpnpop(); \
++ pos = 1UL << (index - 1); \
++ val = num & pos; \
++ for (; pos; pos OPERATOR 1, val OPERATOR 1) \
++ num &= ~pos, num |= val; \
++ rpnpush(num); \
++}
++
++PROPAGATE_BIT(pbl, <<=)
++PROPAGATE_BIT(pbr, >>=)
++
++#define PROPAGATE_BIT_IMM(name, OPERATOR) \
++static void name(void) \
++{ \
++ unsigned long index, num, pos, val; \
++ index = (unsigned long)get_u8_oprnd(); \
++ if (!index || index > BITS_IN_UL) { \
++ dprobes.status &= ~DP_STATUS_INTERPRETER; \
++ return; \
++ } \
++ num = rpnpop(); \
++ pos = 1UL << (index - 1); \
++ val = num & pos; \
++ for (; pos; pos OPERATOR 1, val OPERATOR 1) \
++ num &= ~pos, num |= val; \
++ rpnpush(num); \
++}
++
++PROPAGATE_BIT_IMM(pbl_i, <<=)
++PROPAGATE_BIT_IMM(pbr_i, >>=)
++
++static void pzl(void)
++{
++ unsigned long index, num, pos;
++ index = rpnpop();
++ if (!index || index > BITS_IN_UL) {
++ gen_ex(EX_INVALID_OPERAND, 3, index); \
++ return;
++ }
++ num = rpnpop();
++ pos = 1UL << index;
++ for (; pos; pos <<= 1)
++ num &= ~pos;
++ rpnpush(num);
++}
++
++static void pzl_i(void)
++{
++ unsigned long index, num, pos;
++ index = (unsigned long)get_u8_oprnd();
++ if (!index || index > BITS_IN_UL) {
++ gen_ex(EX_INVALID_OPERAND, 3, index);
++ return;
++ }
++ num = rpnpop();
++ pos = 1UL << index;
++ for (; pos; pos <<= 1)
++ num &= ~pos;
++ rpnpush(num);
++}
++
++static void pzr(void)
++{
++ unsigned long index, num, pos;
++ index = rpnpop();
++ if (!index || index > BITS_IN_UL) {
++ gen_ex(EX_INVALID_OPERAND, 3, index);
++ return;
++ }
++ if (index == 1)
++ return;
++ num = rpnpop();
++ pos = 1UL << (index - 2);
++ for (; pos; pos >>= 1)
++ num &= ~pos;
++ rpnpush(num);
++}
++
++static void pzr_i(void)
++{
++ unsigned long index, num, pos;
++ index = (unsigned long)get_u8_oprnd();
++ if (!index || index > BITS_IN_UL) {
++ gen_ex(EX_INVALID_OPERAND, 3, index);
++ return;
++ }
++ if (index == 1)
++ return;
++ num = rpnpop();
++ pos = 1UL << (index - 2);
++ for (; pos; pos >>= 1)
++ num &= ~pos;
++ rpnpush(num);
++}
++
++static void ror_i(void)
++{
++ byte_t count = get_u8_oprnd();
++ __asm__ __volatile__("rorl %%cl, %0"
++ :"=m"(dprobes.rpn_stack[dprobes.rpn_tos])
++ :"c"((unsigned long) count));
++}
++
++static void rol_i(void)
++{
++ byte_t count = get_u8_oprnd();
++ __asm__ __volatile__("roll %%cl, %0"
++ :"=m"(dprobes.rpn_stack[dprobes.rpn_tos])
++ :"c"((unsigned long) count));
++}
++
++static void ror(void)
++{
++ unsigned long oprnd = rpnpop();
++ unsigned long count = rpnpop();
++ __asm__ __volatile__("rorl %%cl, %0" : "=m"(oprnd)
++ :"c"(count) );
++ rpnpush(oprnd);
++}
++
++static void rol(void)
++{
++ unsigned long oprnd = rpnpop();
++ unsigned long count = rpnpop();
++ __asm__ __volatile__("roll %%cl, %0" : "=m"(oprnd)
++ :"c"(count));
++ rpnpush(oprnd);
++}
++
++/*
++ * IO group
++ */
++static void push_io_u8(void)
++{
++ rpnpush(inb(rpnpop()));
++}
++static void push_io_u16(void)
++{
++ rpnpush(inw(rpnpop()));
++}
++
++static void push_io_u32(void)
++{
++ rpnpush(inl(rpnpop()));
++}
++
++static void pop_io_u8(void)
++{
++ u8 b = (u8)rpnpop();
++ outb(b, rpnpop());
++}
++
++static void pop_io_u16(void)
++{
++ u16 w = (u16)rpnpop();
++ outw(w, rpnpop());
++}
++
++static void pop_io_u32(void)
++{
++ u32 d = (u32)rpnpop();
++ outl(d, rpnpop());
++}
++
++/*
++ * Register push and pop, current and user registers.
++ */
++
++/*
++ * y -- allowed and implemented, n -- not allowed and not implemented.
++ *
++ * Index Register push u push r pop u pop r
++ *
++ * 0 cs y y n n
++ * 1 ds y y y y
++ * 2 es y y y y
++ * 3 fs y y y y
++ * 4 gs y y y y
++ * 5 ss y y n n
++ * 6 eax y y y y
++ * 7 ebx y y y y
++ * 8 ecx y y y y
++ * 9 edx y y y y
++ * a edi y y y y
++ * b esi y y y y
++ * c eflags y y n n
++ * d eip y y n n
++ * e esp y y n n
++ * f ebp y y y y
++ * 20 tr n y n n
++ * 21 ldtr n y n n
++ * 22 gdtr n y n n
++ * 23 idtr n y n n
++ * 24 cr0 n y n n
++ * 25 cr1 RESERVED
++ * 26 cr2 n y n n
++ * 27 cr3 n y n n
++ * 28 cr4 n y n n
++ * 29 cr5 RESERVED
++ * 2a cr6 RESERVED
++ * 2b cr7 RESERVED
++ * 2c dr0 n y n n
++ * 2d dr1 n y n n
++ * 2e dr2 n y n n
++ * 2f dr3 n y n n
++ * 30 dr4 RESERVED
++ * 31 dr5 RESERVED
++ * 32 dr6 n y n n
++ * 33 dr7 n y n n
++ * 34 tr0 RESERVED
++ * 35 tr1 RESERVED
++ * 36 tr2 RESERVED
++ * 37 tr3 RESERVED
++ * 38 tr4 RESERVED
++ * 39 tr5 RESERVED
++ * 3a tr6 RESERVED
++ * 3b tr7 RESERVED
++ * 3c cpuid y y n n
++ * 3d msr y y n n
++ * 3e fr0 y n n n
++ * 3f fr1 y n n n
++ * 40 fr2 y n n n
++ * 41 fr3 y n n n
++ * 42 fr4 y n n n
++ * 43 fr5 y n n n
++ * 44 fr6 y n n n
++ * 45 fr7 y n n n
++ * 46 fcw y n n n
++ * 47 fsw y n n n
++ * 48 ftw y n n n
++ * 49 fip y n n n
++ * 4a fcs y n n n
++ * 4b fdp y n n n
++ * 4c fds y n n n
++ * 4d xmm0 y n n n
++ * 4e xmm1 y n n n
++ * 4f xmm2 y n n n
++ * 50 xmm3 y n n n
++ * 51 xmm4 y n n n
++ * 52 xmm5 y n n n
++ * 53 xmm6 y n n n
++ * 54 xmm7 y n n n
++ * 55 mxcsr y n n n
++ */
++
++static void dp_save_fpu(void)
++{
++ dprobes.status &= ~DP_STATUS_FIRSTFPU;
++ if (HAVE_HWFP) {
++ if (!current->used_math) {
++ memset(&dprobes.fpu_save_area,0, sizeof(dprobes.fpu_save_area));
++ return;
++ }
++
++ if ((read_cr0()) & CR0_TS) {
++ dprobes.fpu_save_area = current->thread.i387;
++ }
++ else {
++ if (cpu_has_fxsr) {
++ __asm__ __volatile__ (
++ "fxsave %0\n"
++ "fxrstor %0" :"=m" (dprobes.fpu_save_area));
++ } else {
++ __asm__ __volatile__ (
++ "fsave %0\n"
++ "frstor %0" :"=m" (dprobes.fpu_save_area));
++
++ }
++ }
++ }
++ else
++ memset(&dprobes.fpu_save_area,0, sizeof(dprobes.fpu_save_area));
++
++}
++
++/*
++ * Define Floating Point Control Registers' push functions.
++ */
++
++#define DEFINE_PUSHFP(REG) \
++static void pushfp_##REG(void) \
++{ \
++ if (dprobes.status & DP_STATUS_FIRSTFPU) \
++ dp_save_fpu(); \
++ if (cpu_has_fxsr) \
++ rpnpush(dprobes.fpu_save_area.fxsave.REG); \
++ else \
++ rpnpush(dprobes.fpu_save_area.fsave.REG); \
++}
++
++DEFINE_PUSHFP(cwd)
++DEFINE_PUSHFP(swd)
++DEFINE_PUSHFP(twd)
++DEFINE_PUSHFP(fip)
++DEFINE_PUSHFP(fcs)
++DEFINE_PUSHFP(foo)
++DEFINE_PUSHFP(fos)
++
++static void pushfp_mxcsr(void)
++{
++ if (dprobes.status & DP_STATUS_FIRSTFPU)
++ dp_save_fpu();
++ if (cpu_has_xmm)
++ rpnpush(dprobes.fpu_save_area.fxsave.mxcsr);
++ else
++ rpnpush(0); /* could we return 0x1f80 ? */
++}
++
++/*
++ * Floating Point Data Registers' push function.
++ */
++static void pushfp_st(int offset)
++{
++ unsigned long st_0, st_4, st_8;
++ if (dprobes.status & DP_STATUS_FIRSTFPU)
++ dp_save_fpu();
++ if (cpu_has_fxsr) {
++ offset = (offset - 0x3e) * 16;
++ st_0 = *(unsigned long *) ((unsigned char *)
++ (dprobes.fpu_save_area.fxsave.st_space) + offset);
++ st_4 = *(unsigned long *) ((unsigned char *)
++ (dprobes.fpu_save_area.fxsave.st_space) + offset + 4);
++ st_8 = *(unsigned long *) ((unsigned char *)
++ (dprobes.fpu_save_area.fxsave.st_space) + offset + 8);
++ }
++ else {
++ offset = (offset - 0x3e) * 10;
++ st_0 = *(unsigned long *) ((unsigned char *)
++ (dprobes.fpu_save_area.fsave.st_space) + offset);
++ st_4 = *(unsigned long *) ((unsigned char *)
++ (dprobes.fpu_save_area.fsave.st_space) + offset + 4);
++ st_8 = *(unsigned short *) ((unsigned char *)
++ (dprobes.fpu_save_area.fsave.st_space) + offset + 8);
++ }
++ rpnpush(st_8);
++ rpnpush(st_4);
++ rpnpush(st_0);
++}
++
++static void push_xmm(int offset)
++{
++ unsigned long xmm_0 = 0, xmm_4 = 0, xmm_8 = 0, xmm_12 = 0;
++ if (dprobes.status & DP_STATUS_FIRSTFPU)
++ dp_save_fpu();
++ if (cpu_has_xmm) {
++ offset = (offset - 0x4d) * 16;
++ xmm_0 = *(unsigned long *) ((unsigned char *)
++ (dprobes.fpu_save_area.fxsave.xmm_space) + offset);
++ xmm_4 = *(unsigned long *) ((unsigned char *)
++ (dprobes.fpu_save_area.fxsave.xmm_space) + offset + 4);
++ xmm_8 = *(unsigned long *) ((unsigned char *)
++ (dprobes.fpu_save_area.fxsave.xmm_space) + offset + 8);
++ xmm_12 = *(unsigned long *) ((unsigned char *)
++ (dprobes.fpu_save_area.fxsave.xmm_space) + offset + 12);
++ }
++ rpnpush(xmm_12);
++ rpnpush(xmm_8);
++ rpnpush(xmm_4);
++ rpnpush(xmm_0);
++}
++
++/*
++ * push r, ss.
++ */
++static unsigned long getr_ss(void)
++{
++ unsigned long reg;
++ if (dprobes.status & DP_KERNEL_PROBE)
++ __asm__ __volatile__("movw %%ss, %%ax\n\t"
++ :"=a"(reg)
++ );
++ else
++ reg = dprobes.regs->xss;
++ return reg;
++}
++
++/*
++ * push r, esp
++ */
++static unsigned long getr_esp(void)
++{
++ if (dprobes.status & DP_KERNEL_PROBE)
++ return (unsigned long)&dprobes.regs->esp;
++ else
++ return dprobes.regs->esp;
++}
++
++/*
++ * Push GDTR
++ */
++static void push_gdtr(void)
++{
++ struct Xgt_desc_struct gdtr;
++ __asm__ __volatile__("sgdt (%0)" : : "r"((unsigned long) &gdtr));
++
++ rpnpush(gdtr.address);
++ rpnpush((unsigned long) gdtr.size);
++}
++
++/*
++ * Push LDTR
++ */
++static void push_ldtr(void)
++{
++ unsigned long ldtr = 0;
++ __asm__ __volatile__("sldt %0" : "=r"(ldtr));
++ rpnpush(ldtr);
++}
++
++/*
++ * Push IDTR
++ */
++static void push_idtr(void)
++{
++ struct Xgt_desc_struct idtr;
++ __asm__ __volatile__("sidt (%0)" : : "r"((unsigned long) &idtr));
++
++ rpnpush(idtr.address);
++ rpnpush((unsigned long)idtr.size);
++}
++
++/*
++ * Push TR
++ */
++static void push_tr(void)
++{
++ unsigned long tr;
++ __asm__ __volatile__("str %%ax" : "=a"(tr));
++ rpnpush(tr);
++}
++
++/*
++ * Push cpuid.
++ */
++static void push_cpuid(void)
++{
++ int op, cpuid_eax = 0, cpuid_ebx = 0, cpuid_ecx = 0, cpuid_edx = 0;
++ op = (int) rpnpop();
++ if ((boot_cpu_data.x86 >= 5) && (op <= boot_cpu_data.cpuid_level))
++ cpuid(op, &cpuid_eax, &cpuid_ebx, &cpuid_ecx, &cpuid_edx);
++ rpnpush(cpuid_edx);
++ rpnpush(cpuid_ecx);
++ rpnpush(cpuid_ebx);
++ rpnpush(cpuid_eax);
++}
++
++/*
++ * push msr.
++ */
++static void push_msr(void)
++{
++ unsigned long msr, val1 =0, val2 = 0;
++ msr = rpnpop();
++ if (boot_cpu_data.x86 >= 5)
++ __asm__ __volatile__ (
++ "0: rdmsr\n"
++ "1:\n"
++ ".section .fixup,\"ax\"\n"
++ "2: jmp 1b\n"
++ ".previous\n"
++ ".section __ex_table,\"a\"\n"
++ " .align 4\n"
++ " .long 0b,2b\n"
++ ".previous"
++ :"=a"(val1), "=d"(val2)
++ :"c"(msr));
++ rpnpush(val2);
++ rpnpush(val1);
++}
++
++/*
++ * Prototype for control registers'(CR0 TO CR4) and debug registers'(DR0 TO DR7)
++ * push functions.
++ */
++#define DEFINE_PUSHCRDB(REG) \
++static void push_##REG(void) \
++{ \
++ unsigned long reg; \
++ __asm__ __volatile__ ("movl %%" #REG ", %0" : "=r"(reg)); \
++ rpnpush(reg); \
++}
++
++/*
++ * Define control and debug registers' push functions.
++ */
++DEFINE_PUSHCRDB(cr0)
++DEFINE_PUSHCRDB(cr2)
++DEFINE_PUSHCRDB(cr3)
++DEFINE_PUSHCRDB(cr4)
++DEFINE_PUSHCRDB(dr0)
++DEFINE_PUSHCRDB(dr1)
++DEFINE_PUSHCRDB(dr2)
++DEFINE_PUSHCRDB(dr3)
++DEFINE_PUSHCRDB(dr6)
++DEFINE_PUSHCRDB(dr7)
++
++/*
++ * Since fs and gs are not in pt_regs, these
++ * are directly taken from the processor.
++ */
++static unsigned long read_fs(void)
++{
++ unsigned long val;
++ __asm__ __volatile__("movw %%fs, %%ax" :"=a"(val));
++ return val;
++}
++
++static unsigned long read_gs(void)
++{
++ unsigned long val;
++ __asm__ __volatile__("movw %%gs, %%ax" :"=a"(val));
++ return val;
++}
++
++static void write_fs(unsigned long val)
++{
++ __asm__ __volatile__("movw %%ax, %%fs" : : "a"(val));
++}
++
++static void write_gs(unsigned long val)
++{
++ __asm__ __volatile__("movw %%ax, %%gs" : :"a"(val));
++}
++
++/*
++ * Current register main push function.
++ */
++static void pushr(void)
++{
++ struct dprobes_struct *dp = &dprobes;
++ unsigned short index = get_u16_oprnd();
++ unsigned long val;
++ switch(index) {
++ case DP_CS: val = dp->regs->xcs; break;
++ case DP_DS: val = dp->regs->xds; break;
++ case DP_ES: val = dp->regs->xes; break;
++ case DP_FS: val = read_fs(); break;
++ case DP_GS: val = read_gs(); break;
++ case DP_SS: val = getr_ss(); break;
++ case DP_EAX: val = dp->regs->eax; break;
++ case DP_EBX: val = dp->regs->ebx; break;
++ case DP_ECX: val = dp->regs->ecx; break;
++ case DP_EDX: val = dp->regs->edx; break;
++ case DP_EDI: val = dp->regs->edi; break;
++ case DP_ESI: val = dp->regs->esi; break;
++ case DP_EFLAGS: val = dp->regs->eflags; break;
++ case DP_EIP: val = dp->regs->eip; break;
++ case DP_ESP: val = getr_esp(); break;
++ case DP_EBP: val = dp->regs->ebp; break;
++
++ /* special registers */
++ case DP_TR: push_tr(); return;
++ case DP_LDTR: push_ldtr(); return;
++ case DP_GDTR: push_gdtr(); return;
++ case DP_IDTR: push_idtr(); return;
++ case DP_CR0: push_cr0(); return;
++ case DP_CR2: push_cr2(); return;
++ case DP_CR3: push_cr3(); return;
++ case DP_CR4: push_cr4(); return;
++ case DP_DR0: push_dr0(); return;
++ case DP_DR1: push_dr1(); return;
++ case DP_DR2: push_dr2(); return;
++ case DP_DR3: push_dr3(); return;
++ case DP_DR6: push_dr6(); return;
++ case DP_DR7: push_dr7(); return;
++ case DP_CPUID: push_cpuid(); return;
++ case DP_MSR: push_msr(); return;
++ default: val = 0; break;
++ }
++ rpnpush(val);
++ return;
++}
++
++/*
++ * User register main push function.
++ */
++static void pushu(void)
++{
++ struct dprobes_struct *dp = &dprobes;
++ unsigned short index = get_u16_oprnd();
++ unsigned long val;
++ switch(index) {
++ case DP_CS: val = dp->uregs->xcs; break;
++ case DP_DS: val = dp->uregs->xds; break;
++ case DP_ES: val = dp->uregs->xes; break;
++ case DP_FS: val = read_fs(); break;
++ case DP_GS: val = read_gs(); break;
++ case DP_SS: val = dp->uregs->xss; break;
++ case DP_EAX: val = dp->uregs->eax; break;
++ case DP_EBX: val = dp->uregs->ebx; break;
++ case DP_ECX: val = dp->uregs->ecx; break;
++ case DP_EDX: val = dp->uregs->edx; break;
++ case DP_EDI: val = dp->uregs->edi; break;
++ case DP_ESI: val = dp->uregs->esi; break;
++ case DP_EFLAGS: val = dp->uregs->eflags; break;
++ case DP_EIP: val = dp->uregs->eip; break;
++ case DP_ESP: val = dp->uregs->esp; break;
++ case DP_EBP: val = dp->uregs->ebp; break;
++ case DP_CPUID: push_cpuid(); return;
++ case DP_MSR: push_msr(); return;
++ case DP_FR0: pushfp_st(0x3e); return;
++ case DP_FR1: pushfp_st(0x3f); return;
++ case DP_FR2: pushfp_st(0x40); return;
++ case DP_FR3: pushfp_st(0x41); return;
++ case DP_FR4: pushfp_st(0x42); return;
++ case DP_FR5: pushfp_st(0x43); return;
++ case DP_FR6: pushfp_st(0x44); return;
++ case DP_FR7: pushfp_st(0x45); return;
++ case DP_FCW: pushfp_cwd(); return;
++ case DP_FSW: pushfp_swd(); return;
++ case DP_FTW: pushfp_twd(); return;
++ case DP_FIP: pushfp_fip(); return;
++ case DP_FCS: pushfp_fcs(); return;
++ case DP_FDP: pushfp_foo(); return;
++ case DP_FDS: pushfp_fos(); return;
++
++ case DP_XMM0: push_xmm(0x4d); return;
++ case DP_XMM1: push_xmm(0x4e); return;
++ case DP_XMM2: push_xmm(0x4f); return;
++ case DP_XMM3: push_xmm(0x50); return;
++ case DP_XMM4: push_xmm(0x51); return;
++ case DP_XMM5: push_xmm(0x52); return;
++ case DP_XMM6: push_xmm(0x53); return;
++ case DP_XMM7: push_xmm(0x54); return;
++ case DP_MXCSR: pushfp_mxcsr(); return;
++
++ default: val = 0; break;
++ }
++ rpnpush(val);
++ return;
++}
++
++/*
++ * Current register main pop function.
++ */
++static void popr(void)
++{
++ struct dprobes_struct *dp = &dprobes;
++ unsigned short index = get_u16_oprnd();
++ unsigned long val = rpnpop();
++ switch(index) {
++ case DP_DS: dp->regs->xds = val; break;
++ case DP_ES: dp->regs->xes = val; break;
++ case DP_FS: write_fs(val); break;
++ case DP_GS: write_gs(val); break;
++ case DP_EAX: dp->regs->eax = val; break;
++ case DP_EBX: dp->regs->ebx = val; break;
++ case DP_ECX: dp->regs->ecx = val; break;
++ case DP_EDX: dp->regs->edx = val; break;
++ case DP_EDI: dp->regs->edi = val; break;
++ case DP_ESI: dp->regs->esi = val; break;
++ case DP_EBP: dp->regs->ebp = val; break;
++ default: break;
++ }
++}
++
++/*
++ * User register main pop function.
++ */
++static void popu(void)
++{
++ struct dprobes_struct *dp = &dprobes;
++ unsigned short index = get_u16_oprnd();
++ unsigned long val = rpnpop();
++ switch(index) {
++ case DP_DS: dp->uregs->xds = val; break;
++ case DP_ES: dp->uregs->xes = val; break;
++ case DP_FS: write_fs(val); break;
++ case DP_GS: write_gs(val); break;
++ case DP_EAX: dp->uregs->eax = val; break;
++ case DP_EBX: dp->uregs->ebx = val; break;
++ case DP_ECX: dp->uregs->ecx = val; break;
++ case DP_EDX: dp->uregs->edx = val; break;
++ case DP_EDI: dp->uregs->edi = val; break;
++ case DP_ESI: dp->uregs->esi = val; break;
++ case DP_EBP: dp->uregs->ebp = val; break;
++ default: break;
++ }
++}
++
++/*
++ * Entry point for the dprobes interpreter(Probe handler).
++ */
++static void dp_asm_interpreter(byte_t rpn_instr)
++{
++ struct dprobes_struct *dp = &dprobes;
++
++ switch(rpn_instr) {
++
++ case DP_SEG2LIN: seg2lin(); break;
++
++ case DP_PUSH_IO_U8: push_io_u8(); break;
++ case DP_PUSH_IO_U16: push_io_u16(); break;
++ case DP_PUSH_IO_U32: push_io_u32(); break;
++ case DP_POP_IO_U8: pop_io_u8(); break;
++ case DP_POP_IO_U16: pop_io_u16(); break;
++ case DP_POP_IO_U32: pop_io_u32(); break;
++
++ default: gen_ex(EX_INVALID_OPCODE, *(dp->rpn_ip - 1),
++ (unsigned long)(dp->rpn_ip -dp->rpn_code - 1)); break; }
++ return;
++}
+diff -urNp linux.orig/drivers/dprobes/Makefile linux-2.6.0-test11/drivers/dprobes/Makefile
+--- linux.orig/drivers/dprobes/Makefile 1970-01-01 05:30:00.000000000 +0530
++++ linux-2.6.0-test11/drivers/dprobes/Makefile 2003-12-15 15:51:48.000000000 +0530
+@@ -0,0 +1,13 @@
++#
++# Makefile for IBM Dynamic Probes
++#
++
++# Multipart objects.
++dp-objs := dprobes.o dprobes_in.o
++
++# Optional parts of multipart objects.
++ifeq ($(CONFIG_X86),y)
++ dp-objs += i386/dprobes.o
++endif
++
++obj-$(CONFIG_DPROBES) += dp.o
+diff -urNp linux.orig/drivers/Makefile linux-2.6.0-test11/drivers/Makefile
+--- linux.orig/drivers/Makefile 2003-11-27 02:15:19.000000000 +0530
++++ linux-2.6.0-test11/drivers/Makefile 2003-12-15 15:51:48.000000000 +0530
+@@ -49,3 +49,4 @@ obj-$(CONFIG_ISDN_BOOL) += isdn/
+ obj-$(CONFIG_MCA) += mca/
+ obj-$(CONFIG_EISA) += eisa/
+ obj-$(CONFIG_CPU_FREQ) += cpufreq/
++obj-$(CONFIG_DPROBES) += dprobes/
+diff -urNp linux.orig/fs/exec.c linux-2.6.0-test11/fs/exec.c
+--- linux.orig/fs/exec.c 2003-11-27 02:13:36.000000000 +0530
++++ linux-2.6.0-test11/fs/exec.c 2003-12-15 15:51:48.000000000 +0530
+@@ -1391,3 +1391,4 @@ fail:
+ unlock_kernel();
+ return retval;
+ }
++EXPORT_SYMBOL(do_coredump);
+diff -urNp linux.orig/fs/namei.c linux-2.6.0-test11/fs/namei.c
+--- linux.orig/fs/namei.c 2003-11-27 02:13:34.000000000 +0530
++++ linux-2.6.0-test11/fs/namei.c 2003-12-15 15:51:48.000000000 +0530
+@@ -264,6 +264,18 @@ int deny_write_access(struct file * file
+ return 0;
+ }
+
++int deny_write_access_to_inode(struct inode * inode)
++{
++ spin_lock(&arbitration_lock);
++ if (atomic_read(&inode->i_writecount) > 0) {
++ spin_unlock(&arbitration_lock);
++ return -ETXTBSY;
++ }
++ atomic_dec(&inode->i_writecount);
++ spin_unlock(&arbitration_lock);
++ return 0;
++}
++
+ void path_release(struct nameidata *nd)
+ {
+ dput(nd->dentry);
+@@ -2307,3 +2319,8 @@ EXPORT_SYMBOL(vfs_rename);
+ EXPORT_SYMBOL(vfs_rmdir);
+ EXPORT_SYMBOL(vfs_symlink);
+ EXPORT_SYMBOL(vfs_unlink);
++#ifdef CONFIG_DPROBES_MODULE
++extern int deny_write_access_to_inode(struct inode *);
++EXPORT_SYMBOL(deny_write_access_to_inode);
++#endif
++
+diff -urNp linux.orig/include/asm-i386/debugreg.h linux-2.6.0-test11/include/asm-i386/debugreg.h
+--- linux.orig/include/asm-i386/debugreg.h 2003-11-27 02:15:08.000000000 +0530
++++ linux-2.6.0-test11/include/asm-i386/debugreg.h 2003-12-15 15:51:48.000000000 +0530
+@@ -61,4 +61,166 @@
+ #define DR_LOCAL_SLOWDOWN (0x100) /* Local slow the pipeline */
+ #define DR_GLOBAL_SLOWDOWN (0x200) /* Global slow the pipeline */
+
++struct debugreg {
++ unsigned long flag;
++ unsigned long use_count;
++};
++
++/* debugreg flags */
++#define DR_UNUSED 0
++#define DR_LOCAL 1
++#define DR_GLOBAL 2
++
++#define DR_MAX 4
++#define DR_ANY DR_MAX + 1
++
++/* global or local allocation requests */
++#define DR_ALLOC_GLOBAL 0
++#define DR_ALLOC_LOCAL 1
++
++#define DR7_RW_SET(dr, regnum, rw) do { \
++ (dr) &= ~(0x3 << (16 + (4 * (regnum)))); \
++ (dr) |= (((rw) & 0x3) << (16 + (4 * (regnum)))); \
++ } while (0)
++
++#define DR7_RW_VAL(dr, regnum) \
++ (((dr) >> (16 + (4 * (regnum)))) & 0x3)
++
++#define DR7_LEN_SET(dr, regnum, len) do { \
++ (dr) &= ~(0x3 << (18 + (4 * (regnum)))); \
++ (dr) |= (((len-1) & 0x3) << (18 + (4 * (regnum)))); \
++ } while (0)
++
++#define DR7_LEN_VAL(dr, regnum) \
++ (((dr) >> (18 + (4 * (regnum)))) & 0x3)
++
++#define DR7_L0(dr) (((dr))&0x1)
++#define DR7_L1(dr) (((dr)>>2)&0x1)
++#define DR7_L2(dr) (((dr)>>4)&0x1)
++#define DR7_L3(dr) (((dr)>>6)&0x1)
++
++#define DR_IS_LOCAL(dr, num) ((dr) & (1UL << (num <<1)))
++
++/* Set the rw, len and global flag in dr7 for a debug register */
++#define SET_DR7(dr, regnum, access, len) do { \
++ DR7_RW_SET(dr, regnum, access); \
++ DR7_LEN_SET(dr, regnum, len); \
++ dr |= (2UL << regnum*2); \
++ } while (0)
++
++/* Disable a debug register by clearing the global/local flag in dr7 */
++#define RESET_DR7(dr, regnum) dr &= ~(3UL << regnum*2)
++
++#define DR7_DR0_BITS 0x000F0003
++#define DR7_DR1_BITS 0x00F0000C
++#define DR7_DR2_BITS 0x0F000030
++#define DR7_DR3_BITS 0xF00000C0
++
++#define DR_TRAP_MASK 0xF
++
++#define DR_TYPE_EXECUTE 0x0
++#define DR_TYPE_WRITE 0x1
++#define DR_TYPE_IO 0x2
++#define DR_TYPE_RW 0x3
++
++#define get_dr(regnum, val) \
++ __asm__("movl %%db" #regnum ", %0" \
++ :"=r" (val))
++static inline unsigned long read_dr(int regnum)
++{
++ unsigned long val = 0;
++ switch (regnum) {
++ case 0: get_dr(0, val); break;
++ case 1: get_dr(1, val); break;
++ case 2: get_dr(2, val); break;
++ case 3: get_dr(3, val); break;
++ case 6: get_dr(6, val); break;
++ case 7: get_dr(7, val); break;
++ }
++ return val;
++}
++#undef get_dr
++
++#define set_dr(regnum, val) \
++ __asm__("movl %0,%%db" #regnum \
++ : /* no output */ \
++ :"r" (val))
++static inline void write_dr(int regnum, unsigned long val)
++{
++ switch (regnum) {
++ case 0: set_dr(0, val); break;
++ case 1: set_dr(1, val); break;
++ case 2: set_dr(2, val); break;
++ case 3: set_dr(3, val); break;
++ case 7: set_dr(7, val); break;
++ }
++ return;
++}
++#undef set_dr
++
++#ifdef CONFIG_DEBUGREG
++/*
++ * Given the debug status register, returns the debug register number
++ * which caused the debug trap.
++ */
++static inline int dr_trap(unsigned int condition)
++{
++ int i, reg_shift = 1UL;
++ for (i = 0; i < DR_MAX; i++, reg_shift <<= 1)
++ if ((condition & reg_shift))
++ return i;
++ return -1;
++}
++
++/*
++ * Given the debug status register, returns the address due to which
++ * the debug trap occured.
++ */
++static inline unsigned long dr_trap_addr(unsigned int condition)
++{
++ int regnum = dr_trap(condition);
++
++ if (regnum == -1)
++ return -1;
++ return read_dr(regnum);
++}
++
++/*
++ * Given the debug status register, returns the type of debug trap:
++ * execute, read/write, write or io.
++ */
++static inline int dr_trap_type(unsigned int condition)
++{
++ int regnum = dr_trap(condition);
++
++ if (regnum == -1)
++ return -1;
++ return DR7_RW_VAL(read_dr(7), regnum);
++}
++
++/* Function declarations */
++
++extern int dr_alloc(int regnum, int flag);
++extern int dr_free(int regnum);
++extern void dr_inc_use_count(unsigned long mask);
++extern void dr_dec_use_count(unsigned long mask);
++extern struct debugreg dr_list[DR_MAX];
++extern unsigned long dr7_global_mask;
++extern int enable_debugreg(unsigned long old_dr7, unsigned long new_dr7);
++
++static inline void load_process_dr7(unsigned long curr_dr7)
++{
++ write_dr(7, (read_dr(7) & dr7_global_mask) | curr_dr7);
++}
++#else
++static inline int enable_debugreg(unsigned long old_dr7, unsigned long new_dr7) { return 0; }
++static inline void load_process_dr7(unsigned long curr_dr7)
++{
++ write_dr(7, curr_dr7);
++}
++
++static void dr_inc_use_count(unsigned long mask) { }
++static void dr_dec_use_count(unsigned long mask) { }
++
++#endif /* CONFIG_DEBUGREG */
+ #endif
+diff -urNp linux.orig/include/asm-i386/dprobes_exclude.h linux-2.6.0-test11/include/asm-i386/dprobes_exclude.h
+--- linux.orig/include/asm-i386/dprobes_exclude.h 1970-01-01 05:30:00.000000000 +0530
++++ linux-2.6.0-test11/include/asm-i386/dprobes_exclude.h 2003-12-15 15:51:48.000000000 +0530
+@@ -0,0 +1,84 @@
++#ifndef __ASM_I386_DPROBES_EXCLUDE_H__
++#define __ASM_I386_DPROBES_EXCLUDE_H__
++
++/*
++ * IBM Dynamic Probes
++ * Copyright (c) International Business Machines Corp., 2000
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ */
++
++/*
++ * Exclude regions markers: We do not allow probes to be placed in these
++ * code areas to _prevent_ recursion into dp_trap3. Note that this does
++ * not eliminate the possibility of recursion completely. dp_trap3 handles
++ * recursion by silently and permanently removing the recursed probe point.
++ */
++#include <linux/kprobes.h>
++#include <asm/kwatch.h>
++
++extern void kprobes_code_start(void);
++extern void kprobes_code_end(void);
++extern void kprobes_asm_code_start(void);
++extern void kprobes_asm_code_end(void);
++extern void dprobes_code_start(void);
++extern void dprobes_code_end(void);
++extern void dprobes_asm_code_start(void);
++extern void dprobes_asm_code_end(void);
++extern void dprobes_interpreter_code_start(void);
++extern void dprobes_interpreter_code_end(void);
++
++#ifdef CONFIG_DR_ALLOC
++extern void kwatch_asm_start(void);
++extern void kwatch_asm_end(void);
++#endif
++
++struct region {
++ unsigned long start;
++ unsigned long end;
++};
++
++#define NR_EXCLUDED_REGIONS 6
++static struct region exclude[] = {
++#ifdef CONFIG_DR_ALLOC
++ {(unsigned long)kwatch_asm_start,
++ (unsigned long)kwatch_asm_end},
++#else
++ {0, 0},
++#endif
++ {(unsigned long)kprobes_code_start,
++ (unsigned long)kprobes_code_end},
++ {(unsigned long)kprobes_asm_code_start,
++ (unsigned long)kprobes_asm_code_end},
++ {(unsigned long)dprobes_code_start,
++ (unsigned long)dprobes_code_end},
++ {(unsigned long)dprobes_asm_code_start,
++ (unsigned long)dprobes_asm_code_end},
++ {(unsigned long)dprobes_interpreter_code_start,
++ (unsigned long)dprobes_interpreter_code_end}
++};
++
++static inline int dprobes_excluded(unsigned long addr)
++{
++ int i;
++ for (i = 0; i < NR_EXCLUDED_REGIONS; i++) {
++ if (addr >= exclude[i].start && addr <= exclude[i].end) {
++ return 1;
++ }
++ }
++ return 0;
++}
++
++#endif /* __ASM_I386_DPROBES_EXCLUDE_H__ */
+diff -urNp linux.orig/include/asm-i386/dprobes.h linux-2.6.0-test11/include/asm-i386/dprobes.h
+--- linux.orig/include/asm-i386/dprobes.h 1970-01-01 05:30:00.000000000 +0530
++++ linux-2.6.0-test11/include/asm-i386/dprobes.h 2003-12-15 15:51:48.000000000 +0530
+@@ -0,0 +1,202 @@
++#ifndef __ASM_I386_DPROBES_H__
++#define __ASM_I386_DPROBES_H__
++
++/*
++ * IBM Dynamic Probes
++ * Copyright (c) International Business Machines Corp., 2000
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ */
++
++/*
++ * RPN stack width will always be equal to the machine register width.
++ */
++#define RPN_STACK_SIZE 1024
++#define CALL_FRAME_SIZE 10
++#define NR_NESTED_CALLS 32
++/* 1st is a dummy frame, not used by call instruction */
++#define CALL_STACK_SIZE (NR_NESTED_CALLS+1)*CALL_FRAME_SIZE
++#define FMT_LOG_HDR_SIZE 256
++
++/* Offsets of different items on call stack */
++#define OFFSET_SBP 0
++#define OFFSET_GV_RANGE 1
++#define OFFSET_LV_RANGE 3
++#define OFFSET_RPN_STACK_RANGE 5
++#define OFFSET_EX_HANDLER 7
++#define OFFSET_CALLED_ADDR 8
++#define OFFSET_RETURN_ADDR 9
++
++/*
++ * offsets of different repitition entries (3 byte prefix) in the
++ * exception stacktrace buffer.
++ */
++struct ex_buffer_offset {
++ unsigned long st; /* ex stack traces */
++ unsigned long sf; /* ex stack frames */
++ unsigned long rpn; /* rpn stack entries */
++ unsigned long lv; /* lv entries */
++ unsigned long gv; /* gv entries */
++};
++
++/* Token bytes of all available 3 byte prefixes */
++#define TOKEN_SEG_FAULT -2
++#define TOKEN_MEMORY_FAULT -1
++#define TOKEN_MEMORY_LOG 0
++#define TOKEN_ASCII_LOG 1
++#define TOKEN_STACK_TRACE 2
++#define TOKEN_STACK_FRAME 3
++#define TOKEN_RPN_ENTRY 4
++#define TOKEN_LV_ENTRY 5
++#define TOKEN_GV_ENTRY 6
++#define TOKEN_LOG 7
++
++#define PREFIX_SIZE 3
++
++struct dprobes_struct {
++ unsigned long status; /* status */
++ /*
++ * details about the probe that is hit, kept here for quick access at
++ * trap1 time.
++ */
++ unsigned long probe_addr;
++ struct pt_regs *regs;
++ struct pt_regs *uregs;
++ struct dp_module_struct *mod;
++ struct dp_record_struct *rec;
++
++ /* fpu registers are saved here */
++ union i387_union fpu_save_area;
++
++ /*
++ * Optional data to store with the log record. This data is collected
++ * in the interpreter and used in the trap1 handler when writing the
++ * log to the specified log target.
++ */
++ unsigned long eip;
++ unsigned short cs;
++ unsigned long esp;
++ unsigned short ss;
++ unsigned short major, minor;
++
++ /*
++ * per-processor data used by the interpreter.
++ */
++ unsigned long rpn_tos, call_tos, log_len, prev_log_len, ex_log_len;
++ byte_t * rpn_code;
++ byte_t * rpn_ip;
++ unsigned short jmp_count;
++ byte_t opcode;
++ byte_t reserved;
++ byte_t ex_pending;
++ unsigned long ex_code;
++ unsigned long ex_parm1, ex_parm2, ex_hand;
++ struct ex_buffer_offset ex_off;
++ unsigned long rpn_sbp;
++ unsigned long heap_size;
++
++ /*
++ * locks used to handle recursion.
++ */
++ spinlock_t lock;
++ long sem;
++
++ unsigned long rpn_stack[RPN_STACK_SIZE];
++ unsigned long call_stack[CALL_STACK_SIZE];
++ unsigned char log[LOG_SIZE];
++ unsigned char ex_log[EX_LOG_SIZE];
++ unsigned char log_hdr[FMT_LOG_HDR_SIZE];
++};
++
++/*
++ * status
++ *
++ * DP_KENREL_PROBE: When this is set, we will not use the pte* fields in the
++ * alias structure at trap1 time, we can write directly to the probe_addr.
++ *
++ * DP_STATUS_ERROR: Contains all the bits to check for error conditions.
++ *
++ * DP_STATUS_DONE: Contains all the per-probe hit bits that need to be turned
++ * off after a probe handler is executed.
++ *
++ * DP_STATUS_ABORT: Indicates that the designated exit facility should not
++ * be called after the original instruction is successfully single-stepped.
++ */
++#define DP_STATUS_INACTIVE 0x00000000
++#define DP_STATUS_ACTIVE 0x00000001
++
++#define DP_STATUS_ERROR 0x00ff0000
++#define DP_STATUS_GPF 0x00010000
++#define DP_STATUS_PF 0x00020000
++#define DP_STATUS_LOG_OVERFLOW 0x00040000
++
++#define DP_STATUS_INTERPRETER 0x01000000
++#define DP_STATUS_SS 0x02000000
++#define DP_KERNEL_PROBE 0x04000000
++#define DP_USER_PROBE 0x08000000
++#define DP_STATUS_ABORT 0x10000000
++#define DP_STATUS_FIRSTFPU 0x20000000
++#define DP_STATUS_DONE 0xffff0000
++
++#define DP_INSTR_BREAKPOINT 0xcc
++
++/* arch-specific rec flags */
++#define DP_REC_OPCODE_EMULATED 0x01000000
++#define DP_REC_OPCODE_IF_MODIFIER 0x02000000
++
++#ifndef EF_CF
++#define EF_CF 0x00000001
++#endif
++#ifndef EF_TF
++#define EF_TF 0x00000100
++#endif
++#ifndef EF_IE
++#define EF_IE 0x00000200
++#endif
++#ifndef EF_DF
++#define EF_DF 0x00000400
++#endif
++#ifndef EF_RF
++#define EF_RF 0x00010000
++#endif
++
++#define CR0_TS 0x00000008
++
++#ifndef HAVE_HWFP
++#ifdef CONFIG_MATH_EMULATION
++#define HAVE_HWFP (boot_cpu_data.hard_math)
++#else
++#define HAVE_HWFP 1
++#endif
++#endif
++
++/*
++ * Function declarations
++ */
++extern void dp_interpreter(void);
++extern int dp_gpf(struct pt_regs *);
++extern int dp_pf(struct pt_regs *);
++extern int dp_handle_fault(struct pt_regs *);
++extern int dp_do_debug(struct pt_regs *, unsigned long);
++extern int dp_trap1(struct pt_regs *);
++extern int dp_trap(struct pt_regs *, int, unsigned int, unsigned long);
++extern int __remove_probe(byte_t *, struct dp_record_struct *);
++extern int __insert_probe(byte_t *, struct dp_record_struct *, struct dp_module_struct *, struct page *);
++extern int register_userspace_probes(struct dp_record_struct *);
++extern void unregister_userspace_probes(struct dp_record_struct *);
++extern inline int insert_probe_userspace(byte_t *, struct dp_record_struct *, struct page *, struct vm_area_struct *);
++extern inline int remove_probe_userspace(byte_t *, struct dp_record_struct *, struct page *, struct vm_area_struct *);
++
++#endif
+diff -urNp linux.orig/include/asm-i386/dprobes_in.h linux-2.6.0-test11/include/asm-i386/dprobes_in.h
+--- linux.orig/include/asm-i386/dprobes_in.h 1970-01-01 05:30:00.000000000 +0530
++++ linux-2.6.0-test11/include/asm-i386/dprobes_in.h 2003-12-15 15:51:48.000000000 +0530
+@@ -0,0 +1,120 @@
++#ifndef __ASM_I386_DPROBES_IN_H__
++#define __ASM_I386_DPROBES_IN_H__
++
++/*
++ * IBM Dynamic Probes
++ * Copyright (c) International Business Machines Corp., 2000
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ */
++
++/*
++ * This header file defines the opcodes for the RPN instructions
++ *
++ * Opcodes 0x00 to 0xAF are for common, arch-independent instructions.
++ * Opcodes 0xB0 to 0xDF are for arch-dependent instructions.
++ * Opcodes 0xF1 to 0xFF are for two byte instructions.
++ */
++
++/*
++ * Misc
++ */
++#define DP_SEG2LIN 0xB0
++
++/*
++ * IO Group.
++ */
++#define DP_PUSH_IO_U8 0xB1
++#define DP_PUSH_IO_U16 0xB2
++#define DP_PUSH_IO_U32 0xB3
++#define DP_POP_IO_U8 0xB4
++#define DP_POP_IO_U16 0xB5
++#define DP_POP_IO_U32 0xB6
++
++/*
++ * Intel32 Register Assignments.
++ */
++#define DP_CS 0x0000
++#define DP_DS 0x0001
++#define DP_ES 0x0002
++#define DP_FS 0x0003
++#define DP_GS 0x0004
++#define DP_SS 0x0005
++#define DP_EAX 0x0006
++#define DP_EBX 0x0007
++#define DP_ECX 0x0008
++#define DP_EDX 0x0009
++#define DP_EDI 0x000a
++#define DP_ESI 0x000b
++#define DP_EFLAGS 0x000c
++#define DP_EIP 0x000d
++#define DP_ESP 0x000e
++#define DP_EBP 0x000f
++
++#define DP_TR 0x0020
++#define DP_LDTR 0x0021
++#define DP_GDTR 0x0022
++#define DP_IDTR 0x0023
++#define DP_CR0 0x0024
++#define DP_CR1 0x0025
++#define DP_CR2 0x0026
++#define DP_CR3 0x0027
++#define DP_CR4 0x0028
++
++#define DP_DR0 0x002c
++#define DP_DR1 0x002d
++#define DP_DR2 0x002e
++#define DP_DR3 0x002f
++#define DP_DR4 0x0030
++#define DP_DR5 0x0031
++#define DP_DR6 0x0032
++#define DP_DR7 0x0033
++
++#define DP_CPUID 0x003c
++#define DP_MSR 0x003d
++
++#define DP_FR0 0x003e
++#define DP_FR1 0x003f
++#define DP_FR2 0x0040
++#define DP_FR3 0x0041
++#define DP_FR4 0x0042
++#define DP_FR5 0x0043
++#define DP_FR6 0x0044
++#define DP_FR7 0x0045
++#define DP_FCW 0x0046
++#define DP_FSW 0x0047
++#define DP_FTW 0x0048
++#define DP_FIP 0x0049
++#define DP_FCS 0x004a
++#define DP_FDP 0x004b
++#define DP_FDS 0x004c
++
++#define DP_XMM0 0x004d
++#define DP_XMM1 0x004e
++#define DP_XMM2 0x004f
++#define DP_XMM3 0x0050
++#define DP_XMM4 0x0051
++#define DP_XMM5 0x0052
++#define DP_XMM6 0x0053
++#define DP_XMM7 0x0054
++#define DP_MXCSR 0x0055
++
++#ifndef __KERNEL__
++#ifndef PAGE_OFFSET
++#define PAGE_OFFSET 0xc0000000
++#endif
++#endif /* !__KERNEL__ */
++
++#endif
+diff -urNp linux.orig/include/asm-i386/kprobes.h linux-2.6.0-test11/include/asm-i386/kprobes.h
+--- linux.orig/include/asm-i386/kprobes.h 1970-01-01 05:30:00.000000000 +0530
++++ linux-2.6.0-test11/include/asm-i386/kprobes.h 2003-12-15 15:51:48.000000000 +0530
+@@ -0,0 +1,38 @@
++#ifndef _ASM_KPROBES_H
++#define _ASM_KPROBES_H
++/*
++ * Dynamic Probes (kprobes) support
++ * Vamsi Krishna S <vamsi_krishna@in.ibm.com>, July, 2002
++ * Mailing list: dprobes@www-124.ibm.com
++ */
++#include <linux/types.h>
++#include <linux/ptrace.h>
++
++struct pt_regs;
++
++typedef u8 kprobe_opcode_t;
++#define BREAKPOINT_INSTRUCTION 0xcc
++#define MAX_INSN_SIZE 16
++
++/* trap3/1 are intr gates for kprobes. So, restore the status of IF,
++ * if necessary, before executing the original int3/1 (trap) handler.
++ */
++static inline void restore_interrupts(struct pt_regs *regs)
++{
++ if (regs->eflags & IF_MASK)
++ __asm__ __volatile__ ("sti");
++}
++
++void kprobes_asm_code_start(void);
++void kprobes_asm_code_end(void);
++
++#ifdef CONFIG_KPROBES
++extern int kprobe_fault_handler(struct pt_regs *regs, int trapnr);
++extern int post_kprobe_handler(struct pt_regs *regs);
++extern int kprobe_handler(struct pt_regs *regs);
++#else /* !CONFIG_KPROBES */
++static inline int kprobe_fault_handler(struct pt_regs *regs, int trapnr) { return 0; }
++static inline int post_kprobe_handler(struct pt_regs *regs) { return 0; }
++static inline int kprobe_handler(struct pt_regs *regs) { return 0; }
++#endif
++#endif /* _ASM_KPROBES_H */
+diff -urNp linux.orig/include/asm-i386/kwatch.h linux-2.6.0-test11/include/asm-i386/kwatch.h
+--- linux.orig/include/asm-i386/kwatch.h 1970-01-01 05:30:00.000000000 +0530
++++ linux-2.6.0-test11/include/asm-i386/kwatch.h 2003-12-15 15:51:48.000000000 +0530
+@@ -0,0 +1,31 @@
++#ifndef _ASM_KWATCH_H
++#define _ASM_KWATCH_H
++/*
++ * Dynamic Probes (kwatch points) support
++ * Vamsi Krishna S <vamsi_krishna@in.ibm.com>, Oct, 2002
++ */
++#include <linux/types.h>
++#include <linux/ptrace.h>
++
++struct kwatch;
++typedef void (*kwatch_handler_t)(struct kwatch *, struct pt_regs *, int );
++
++struct kwatch {
++ unsigned long addr; /* location of watchpoint */
++ u8 length; /* range of address */
++ u8 type; /* type of watchpoint */
++ kwatch_handler_t handler;
++};
++
++#define RF_MASK 0x00010000
++
++#ifdef CONFIG_KWATCH
++extern int register_kwatch(unsigned long addr, u8 length, u8 type, kwatch_handler_t handler);
++extern void unregister_kwatch(int debugreg);
++extern int kwatch_handler(unsigned long condition, struct pt_regs *regs);
++#else
++static inline int register_kwatch(unsigned long addr, u8 length, u8 type, kwatch_handler_t handler) { return -ENOSYS; }
++static inline void unregister_kwatch(int debugreg) { }
++static inline int kwatch_handler(unsigned long condition, struct pt_regs *regs) { return 0; }
++#endif
++#endif /* _ASM_KWATCH_H */
+diff -urNp linux.orig/include/linux/dprobes.h linux-2.6.0-test11/include/linux/dprobes.h
+--- linux.orig/include/linux/dprobes.h 1970-01-01 05:30:00.000000000 +0530
++++ linux-2.6.0-test11/include/linux/dprobes.h 2003-12-15 15:51:48.000000000 +0530
+@@ -0,0 +1,450 @@
++#ifndef _LINUX_DPROBES_H
++#define _LINUX_DPROBES_H
++
++/*
++ * IBM Dynamic Probes
++ * Copyright (c) International Business Machines Corp., 2000
++ *
++ */
++
++#ifdef __KERNEL__
++#include <linux/types.h>
++#else
++#include <sys/types.h>
++#endif
++
++#define DP_MAJOR_VER 5
++#define DP_MINOR_VER 0
++#define DP_PATCH_VER 0
++
++/* main command codes */
++#define DP_CMD_MASK 0x000000ff
++#define DP_INSERT 0x00000001
++#define DP_REMOVE 0x00000002
++#define DP_GETVARS 0x00000004
++#define DP_QUERY 0x00000008
++#define DP_HELP 0x00000010
++#define DP_VERSION 0x00000020
++#define DP_BUILDPPDF 0x00000040
++#define DP_APPLYPPDF 0x00000080
++
++/* command modifiers */
++#define DP_FLAGS_MASK 0xff000000
++#define DP_ALL 0x80000000
++
++/* insert cmd modifiers */
++#define DP_MERGE 0x01000000
++#define DP_REPLACE 0x02000000
++#define DP_STOP_CPUS 0x04000000
++#define DP_DONT_VERIFY_OPCODES 0x08000000
++#define DP_AUTOSTACKTRACE 0x10000000
++
++/* getvars cmd modifiers */
++#define DP_GETVARS_INDEX 0x01000000
++#define DP_GETVARS_RESET 0x02000000
++#define DP_GETVARS_LOCAL 0x04000000
++#define DP_GETVARS_GLOBAL 0x08000000
++
++/* query cmd modifiers */
++#define DP_QUERY_EXTENDED 0x01000000
++
++/* pgm->flags: default data that will be part of each log record */
++#define DP_LOG_MASK 0x0000ff00
++#define DP_LOG_PROCNAME 0x00000100
++#define DP_LOG_PID 0x00000200
++#define DP_LOG_UID 0x00000400
++#define DP_LOG_NIP 0x00000800
++#define DP_LOG_PSW 0x00000800
++#define DP_LOG_UPSW 0x00001000
++#define DP_LOG_SS_ESP 0x00000800
++#define DP_LOG_CS_EIP 0x00001000
++#define DP_LOG_TSC 0x00002000
++#define DP_LOG_CPU 0x00004000
++#define DP_LOG_ALL 0x0000ff00
++
++/* pgm->flags: default log target */
++#define DP_LOG_TARGET_MASK 0x00ff0000
++#define DP_LOG_TARGET_KLOG 0x00010000
++#define DP_LOG_TARGET_LTT 0x00020000
++#define DP_LOG_TARGET_COM1 0x00040000
++#define DP_LOG_TARGET_COM2 0x00080000
++#define DP_LOG_TARGET_EVL 0x00100000
++#define DP_UNFORMATTED_OUTPUT 0x00200000
++
++/* exit facilities for use with "exit_to" instruction */
++#define DP_EXIT_TO_SGI_KDB 0x01
++#define DP_EXIT_TO_SGI_VMDUMP 0x02
++#define DP_EXIT_TO_CORE_DUMP 0x03
++
++/* bit flags to indicate elements present in the trace buffer header. */
++#define DP_HDR_MAJOR 0x00000001
++#define DP_HDR_MINOR 0x00000002
++#define DP_HDR_CPU 0x00000004
++#define DP_HDR_PID 0x00000008
++#define DP_HDR_UID 0x00000010
++#define DP_HDR_CS 0x00000020
++#define DP_HDR_EIP 0x00000040
++#define DP_HDR_SS 0x00000080
++#define DP_HDR_ESP 0x00000100
++#define DP_HDR_TSC 0x00000200
++#define DP_HDR_PROCNAME 0x00000400
++
++typedef unsigned char byte_t;
++/* FIXME: Well, we're working on getting this in arch headers */
++#if defined(CONFIG_X86) || defined(CONFIG_IA64)
++typedef byte_t opcode_t;
++#endif
++
++/*
++ * This captures the header information of each probe point. It also contains
++ * links to the rpn code of this dprobe handler.
++ *
++ * maxhits: This field indicates the number of times this probe will be
++ * executed before being disabled. A negative value here means that the
++ * probe will not be disabled automatically.
++ *
++ * count: This field keeps track of number of times this probe is hit,
++ * and executed successfully. There is a reason this is "long" and not
++ * "unsigned long". We would implement "pass-count" feature that specifies
++ * the number of times we pass over this probe with actually executing the
++ * probe handler, using the fact that count is a signed value.
++ */
++struct dp_point_struct {
++ loff_t offset; /* file offset or absolute address */
++ unsigned short address_flag;
++ opcode_t opcode;
++ opcode_t actual_opcode; /* in case of opcode mismatch */
++ unsigned short major;
++ unsigned short minor;
++ unsigned short group;
++ unsigned short type;
++ unsigned long rpn_offset; /* offset into rpn_code of dp_pgm_struct */
++ unsigned long rpn_end;
++ unsigned short probe; /* watchpoint info, probe and access types */
++ unsigned long len; /* range of the watchpoint probe */
++ int dbregno; /* the debug reg used for this watchpoint */
++ long maxhits;
++ long passcount;
++ unsigned char logonfault;
++ unsigned short ex_mask;
++ unsigned long heap_size; /* no of heap elements specified by this probe */
++};
++
++#define DP_ADDRESS_ABSOLUTE 0x0001
++
++/*
++ * Flags related to watchpoint probes.
++ */
++#define DP_PROBE_BREAKPOINT 0x8000
++#define DP_PROBE_WATCHPOINT 0x4000
++#define DP_PROBE_MASK 0xc000
++#define DP_MAX_WATCHPOINT 0x4 /* maximum number of watchpoint probes */
++
++#define DP_WATCHTYPE_EXECUTE 0x0000
++#define DP_WATCHTYPE_WRITE 0x0001
++#define DP_WATCHTYPE_IO 0x0002
++#define DP_WATCHTYPE_RDWR 0x0003
++#define DP_WATCHTYPE_MASK 0x0003
++
++/*
++ * This captures the information specified in the dprobe program file header.
++ *
++ * User application passes the details of the pgm header to the system call in
++ * this structure. It is also used in the kernel as part of dp_module_struct.
++ */
++struct dp_pgm_struct {
++ unsigned char *name; /* module for which the probe program is written */
++ unsigned long flags;
++ unsigned short major;
++ unsigned long id;
++ unsigned short jmpmax;
++ unsigned short logmax;
++ unsigned short ex_logmax; /* length of exception stack trace buffer */
++ unsigned short num_lv; /* num of local variables */
++ unsigned short num_gv; /* num of global variables */
++ unsigned long heapmax; /* num of heap elements required
++ by this probe program */
++
++ unsigned short rpn_length; /* rpn code */
++ unsigned char autostacktrace;
++ byte_t *rpn_code;
++
++ unsigned short align; /* kernel module alignment */
++ unsigned short num_points;
++ struct dp_point_struct *point;
++};
++
++/* pgm flags */
++#define DP_MODTYPE_MASK 0x000f
++#define DP_MODTYPE_USER 0x0001
++#define DP_MODTYPE_KERNEL 0x0002
++#define DP_MODTYPE_KMOD 0x0004
++
++#define LOG_SIZE 1024
++#define EX_LOG_SIZE 1024
++#define JMP_MAX 65535
++#define MAX_MAXHITS 0x7fffffff
++#define HEAPMAX 16*1024
++
++#define DEFAULT_LOGMAX LOG_SIZE
++#define DEFAULT_EX_LOG_MAX EX_LOG_SIZE
++#define DEFAULT_JMPMAX JMP_MAX
++#define DEFAULT_MAXHITS MAX_MAXHITS
++#define DEFAULT_HEAPMAX HEAPMAX
++
++/* compiled in limits */
++#define DP_MAX_LVARS 256
++#define DP_MAX_GVARS 256
++
++/* exception codes */
++#define EX_INVALID_ADDR 0x0001
++#define EX_SEG_FAULT 0x0002
++#define EX_MAX_JMPS 0x0004
++#define EX_CALL_STACK_OVERFLOW 0x0010
++#define EX_DIV_BY_ZERO 0x0020
++#define EX_INVALID_OPERAND 0x0040
++#define EX_INVALID_OPCODE 0x0080
++#define EX_HEAP_NOMEM 0x0100
++#define EX_HEAP_INVALID_HANDLE 0x0200
++#define EX_HEAP_INVALID_OFFSET 0x0400
++#define EX_LOG_OVERFLOW 0x1000
++#define EX_RPN_STACK_WRAP 0x2000
++#define EX_USER 0x8000
++#define DEFAULT_EX_MASK 0x0fff
++
++/* If a non-maskable ex is masked out, the interpreter will be terminated. */
++#define EX_NON_MASKABLE_EX 0x0fff
++
++#define EX_NO_HANDLER 0xffffffff
++
++/*
++ * This defines the Dynamic Probe getvars Request Packet, passed in from
++ * the user for subfunction DP_GETVARS.
++ */
++struct dp_getvars_in_struct {
++ unsigned char *name; /* loadable module name */
++ unsigned short from;
++ unsigned short to;
++ unsigned long allocated; /* Allocated size of result pkt */
++ unsigned long returned; /* Returned size of result pkt */
++};
++
++/*
++ * The output from getvars command is returned in this format. First all
++ * global variables will be there as dp_getvars_global_out_struct followed
++ * by local variables for each module in form of dp_getvars_local_out_struct.
++ */
++struct dp_getvars_global_out_struct {
++ unsigned long num_vars; /* number of the variables to follow */
++ /* variables */
++};
++
++struct dp_getvars_local_out_struct {
++ unsigned long length; /* length of this element */
++ unsigned long num_vars; /* number of the variables to follow */
++ /* variables followed by the name of loadable module*/
++};
++
++/*
++ * This defines the Dynamic Probe query Request Packet passed in
++ * from the user for subfunction DP_QUERY.
++ */
++struct dp_query_in_struct {
++ unsigned char *name; /* loadable module name */
++ unsigned long allocated; /* Allocated size of result pkt */
++ unsigned long returned; /* Returned size of result pkt */
++};
++
++struct dp_outrec_struct {
++ unsigned long status;
++ long count;
++ int dbregno; /* the debug reg used for this watchpoint */
++ struct dp_point_struct point;
++};
++
++/*
++ * flags
++ *
++ * Initially a probe record starts out with zero flags.
++ *
++ * DP_REC_STATUS_COMPILED: A probe record is compiled, after successfully
++ * validating the rpn code.
++ *
++ * DP_REC_STATUS_ACTIVE: A probe record becomes valid after it is verified
++ * that the opcode as specified in the probe program matches with the one
++ * at the specified location. We would probably do this verification the
++ * first time this probe is inserted.
++ *
++ * DP_REC_STATUS_MISMATCH: This means the opcode specified by the probe
++ * does not match with the one present when we try to insert it.
++ *
++ * DP_REC_STATUS_DISABLED: A probe will be disabled after being executed
++ * for max-hit number of times automatically.
++ *
++ * DP_REC_STATUS_REMOVED: The status of a probe will be removed if user
++ * explicitly removes it using dprobe --remove --major-minor command.
++ * Note that if a specific probe is removed by its major-minor, we
++ * don't remove the dp_record_struct from memory as it is too much
++ * work. We simply note that fact in the status flags and leave the
++ * dp_record_struct alone. But, if an entire probe program is removed,
++ * all its structures will be removed permanently.
++ *
++ * DP_REC_STATUS_INVALID_OFFSET: Set if the address specified for probe insertion
++ * is out of bounds for that executable module.
++ *
++ * DP_REC_STATUS_DEBUGREG_UNAVAIL: Set if the probe is of type watchpoint and
++ * no free debug registers are free for use.
++ *
++ * DP_REC_STATUS_WATCHPOINT_LEN_INVALID: Set if the probe is of type watchpoint
++ * and the range of address specified is not valid.
++ */
++#define DP_REC_STATUS_COMPILED 0x00000001
++#define DP_REC_STATUS_ACTIVE 0x00000002
++#define DP_REC_STATUS_MISMATCH 0x00000004
++#define DP_REC_STATUS_DISABLED 0x00000008
++#define DP_REC_STATUS_REMOVED 0x00000010
++#define DP_REC_STATUS_INVALID_OFFSET 0x00000020
++#define DP_REC_STATUS_DEBUGREG_UNAVAIL 0x00000040
++#define DP_REC_STATUS_WATCHPOINT_LEN_INVALID 0x00000080
++#define DP_REC_STATUS_DEBUGREG_PATCH_NEEDED 0x00000100
++#define DP_REC_STATUS_KPROBE_ERR 0x00000200
++#define DP_REC_STATUS_EXCLUDED 0x00000400
++#define DP_REC_ARCH_FLAGS 0xff000000
++
++struct dp_outmod_struct {
++ struct dp_outmod_struct *next; /* link */
++ unsigned long flags; /* cmdline switches */
++ struct dp_outrec_struct * rec; /* array of dp_outrec_structs. */
++ unsigned long * lv; /* local variables */
++ struct dp_pgm_struct pgm;
++ unsigned long base;
++ unsigned long end;
++};
++
++struct dp_ioctl_arg {
++ void * input;
++ void * output;
++ unsigned long cmd;
++};
++
++/*
++ * This should ideally be 2*HEAP_HDR_SIZE, but the latter is
++ * __KERNEL__ specific. Hence hardcoded here.
++ */
++#define MIN_HEAP_SIZE 40
++
++#ifdef __KERNEL__
++
++#include <linux/sched.h>
++#include <linux/dcache.h>
++#include <linux/namei.h>
++#include <linux/mm.h>
++#include <linux/kprobes.h>
++#include <asm/page.h>
++#include <asm/pgtable.h>
++#include <asm/ptrace.h>
++
++struct dp_record_struct {
++ struct kprobe kp;
++ struct uprobe up;
++ struct dp_module_struct *mod;
++ spinlock_t lock;
++ unsigned long status;
++ long count;
++ int dbregno; /* the debug reg used for this watchpoint */
++ struct dp_point_struct point;
++};
++
++/*
++ * When Dprobes stores it's log in a trace buffer, the log data is preceded
++ * by the header information as in this structure, followed by optional
++ * header elements.
++ */
++#define DP_TRACE_HDR_ID 1
++struct dp_trace_hdr_struct {
++ unsigned short facility_id;
++ unsigned short len;
++ unsigned long mask;
++ unsigned short major;
++ unsigned short minor;
++};
++
++#ifdef CONFIG_SMP
++#define MIN_ST_SIZE sizeof(struct dp_trace_hdr_struct) + sizeof(int)
++#else
++#define MIN_ST_SIZE sizeof(struct dp_trace_hdr_struct)
++#endif
++
++/* Major number 0 is reserved for stack trace log record */
++#define DP_ST_MAJOR 0x0000
++#define DP_ST_MINOR 0x0000
++
++/*
++ * This is the data structure that we maintain in kernel for each module
++ * that has any probes applied on it.
++ */
++struct dp_module_struct {
++ struct dp_module_struct *next; /* link */
++ unsigned long flags;
++ struct dp_record_struct * rec; /* array of dp_record_structs. */
++ unsigned long * lv; /* local variables */
++ struct inode * inode; /* for quicker access to inode */
++ int trace_id; /* ltt trace event id */
++ struct dp_trace_hdr_struct hdr;
++ struct dp_pgm_struct pgm;
++ unsigned long base; /* used to store kmod base address */
++ unsigned long end; /* used to store kmod base address */
++ struct nameidata nd; /* to hold path/dentry etc. */
++ struct address_space_operations * ori_a_ops;
++ struct address_space_operations dp_a_ops;
++};
++
++/* flags used to keep track of allocations */
++#define DP_MOD_FLAGS_LARGE_REC 0x01
++
++#ifdef CONFIG_SMP
++extern struct dprobes_struct dprobes_set[];
++#define dprobes dprobes_set[smp_processor_id()]
++#else
++extern struct dprobes_struct dprobes;
++#define dprobes_set (&dprobes)
++#endif /* CONFIG_SMP */
++
++/*extern char _stext, _etext; kernel start and end */
++
++/*
++ * global variables stuff
++ */
++extern unsigned long *dp_gv;
++extern unsigned long dp_num_gv;
++extern rwlock_t dp_gv_lock;
++
++extern byte_t *dp_heap;
++extern unsigned long dp_num_heap;
++extern rwlock_t dp_heap_lock;
++
++extern int dp_insmod(struct module *kmod);
++extern int dp_remmod(struct module *kmod);
++extern int dp_readpage(struct file *, struct page *);
++extern int dp_writepage(struct page *page);
++
++#define IS_COW_PAGE(page, inode) (!(page->mapping) || \
++ (page->mapping->host != (void *)inode))
++
++#include <linux/list.h>
++struct heap_hdr {
++ byte_t *addr;
++ unsigned char flags;
++ unsigned long size;
++ struct list_head list;
++};
++
++#define HEAP_HDR_SIZE sizeof(struct heap_hdr)
++#define HEAP_FREE 1
++#define HEAP_ALLOCATED 2
++
++#include <asm/dprobes.h>
++
++#endif /* __KERNEL__ */
++
++#endif
+diff -urNp linux.orig/include/linux/dprobes_in.h linux-2.6.0-test11/include/linux/dprobes_in.h
+--- linux.orig/include/linux/dprobes_in.h 1970-01-01 05:30:00.000000000 +0530
++++ linux-2.6.0-test11/include/linux/dprobes_in.h 2003-12-15 15:51:48.000000000 +0530
+@@ -0,0 +1,218 @@
++#ifndef _LINUX_DPROBES_IN_H
++#define _LINUX_DPROBES_IN_H
++
++/*
++ * IBM Dynamic Probes
++ * Copyright (c) International Business Machines Corp., 2000
++ */
++
++/*
++ * This header file defines the opcodes for the RPN instructions.
++ *
++ * Opcodes 0x00 to 0xAF are for common, arch-independent instructions.
++ * Opcodes 0xB0 to 0xDF are for arch-dependent instructions.
++ * Opcodes 0xF1 to 0xFF are for two byte instructions.
++ */
++
++#define DP_NOP 0x00
++
++/*
++ * RPN execution group.
++ */
++#define DP_JMP 0x01
++#define DP_JLT 0x02
++#define DP_JLE 0x03
++#define DP_JGT 0x04
++#define DP_JGE 0x05
++#define DP_JZ 0x06
++#define DP_JNZ 0x07
++#define DP_LOOP 0x08
++#define DP_CALL 0x09
++#define DP_RET 0x0a
++#define DP_ABORT 0x0b
++#define DP_REM 0x0c
++#define DP_EXIT 0x0d
++#define DP_EXIT_N 0x0e
++#define DP_SUSPEND 0x0f
++
++/*
++ * Logging Group.
++ */
++#define DP_RESUME 0x10
++#define DP_SETMIN_I 0x11
++#define DP_SETMIN 0x12
++#define DP_SETMAJ_I 0x13
++#define DP_SETMAJ 0x14
++#define DP_LOG_STR 0x15
++#define DP_LOG_MRF 0x17
++#define DP_LOG_I 0x1b
++
++/*
++ * Global Variable Group.
++ */
++#define DP_ALLOC_GV 0x1c
++#define DP_FREE_GV 0x1d
++#define DP_FREE_GVI 0x1e
++#define DP_PUSH_GVI 0x1f
++#define DP_PUSH_GV 0x20
++#define DP_POP_GVI 0x21
++#define DP_POP_GV 0x22
++#define DP_MOVE_GVI 0x23
++#define DP_MOVE_GV 0x24
++#define DP_INC_GVI 0x25
++#define DP_INC_GV 0x26
++#define DP_DEC_GVI 0x27
++#define DP_DEC_GV 0x28
++
++/*
++ * Local Variable Group.
++ */
++#define DP_PUSH_LVI 0x29
++#define DP_PUSH_LV 0x2a
++#define DP_POP_LVI 0x2b
++#define DP_POP_LV 0x2c
++#define DP_MOVE_LVI 0x2d
++#define DP_MOVE_LV 0x2e
++#define DP_INC_LVI 0x2f
++#define DP_INC_LV 0x30
++#define DP_DEC_LVI 0x31
++#define DP_DEC_LV 0x32
++
++/*
++ * Arithmetic Group.
++ */
++#define DP_ADD 0x33
++#define DP_SUB 0x34
++#define DP_MUL 0x35
++
++/*
++ * Logic Group.
++ */
++#define DP_NEG 0x36
++#define DP_AND 0x37
++#define DP_OR 0x38
++#define DP_XOR 0x39
++#define DP_ROL_I 0x3a
++#define DP_ROL 0x3b
++#define DP_ROR_I 0x3c
++#define DP_ROR 0x3d
++#define DP_SHL_I 0x3e
++#define DP_SHL 0x3f
++#define DP_SHR_I 0x40
++#define DP_SHR 0x41
++
++/*
++ * RPN Stack Group.
++ */
++#define DP_XCHG 0x42
++#define DP_DUP_I 0x43
++#define DP_DUP 0x44
++#define DP_ROS 0x45
++
++/*
++ * Register Group.
++ */
++#define DP_PUSH_R 0x46
++#define DP_POP_R 0x47
++#define DP_PUSH_U 0x48
++#define DP_POP_U 0x49
++
++/*
++ * Data Group.
++ */
++#define DP_PUSH 0x4c
++#define DP_PUSH_MEM_U8 0x4d
++#define DP_PUSH_MEM_U16 0x4e
++#define DP_PUSH_MEM_U32 0x4f
++#define DP_PUSH_MEM_U64 0x50
++#define DP_POP_MEM_U8 0x51
++#define DP_POP_MEM_U16 0x52
++#define DP_POP_MEM_U32 0x53
++#define DP_POP_MEM_U64 0x54
++
++/*
++ * System Variable Group.
++ */
++#define DP_PUSH_TASK 0x5d
++#define DP_PUSH_PID 0x5e
++#define DP_PUSH_PROCID 0x5f
++
++/*
++ * Address Verification.
++ */
++#define DP_VFY_R 0x60
++#define DP_VFY_RW 0x61
++
++/*
++ * Some more arithmetic/logic instructions.
++ */
++#define DP_DIV 0x62
++#define DP_IDIV 0x63
++#define DP_PBL 0x64
++#define DP_PBR 0x65
++#define DP_PBL_I 0x66
++#define DP_PBR_I 0x67
++#define DP_PZL 0x68
++#define DP_PZR 0x69
++#define DP_PZL_I 0x6a
++#define DP_PZR_I 0x6b
++
++/*
++ * Exception handling/Stacktrace instructions.
++ */
++#define DP_SX 0x6c
++#define DP_UX 0x6d
++#define DP_RX 0x6e
++#define DP_PUSH_X 0x6f
++#define DP_PUSH_LP 0x70
++#define DP_PUSH_PLP 0x71
++#define DP_POP_LP 0x72
++#define DP_LOG_ST 0x73
++#define DP_PURGE_ST 0x74
++#define DP_TRACE_LV 0x75
++#define DP_TRACE_GV 0x76
++#define DP_TRACE_PV 0x77
++
++#define DP_PUSH_SBP 0x78
++#define DP_POP_SBP 0x79
++#define DP_PUSH_TSP 0x7a
++#define DP_POP_TSP 0x7b
++#define DP_PUSH_SBP_I 0x7c
++#define DP_POP_SBP_I 0x7d
++#define DP_PUSH_TSP_I 0x7e
++#define DP_POP_TSP_I 0x7f
++#define DP_COPY_SBP_I 0x80
++#define DP_COPY_TSP_I 0x81
++#define DP_PUSH_STP 0x82
++#define DP_POP_STP 0x83
++
++#define DP_SAVE_SBP 0x84
++#define DP_RESTORE_SBP 0x85
++#define DP_SAVE_TSP 0x86
++#define DP_RESTORE_TSP 0x87
++#define DP_COPY_SBP 0x88
++#define DP_COPY_TSP 0x89
++
++/* stack based log instructions */
++#define DP_LOG 0x8a
++#define DP_LOG_LV 0x8b
++#define DP_LOG_GV 0x8c
++
++#define DP_CALLK 0x8d
++
++/* byte heap instructions */
++#define DP_MALLOC 0x8e
++#define DP_FREE 0x8f
++#define DP_PUSH_WID 0x90
++#define DP_PUSHH_U8_I 0x91
++#define DP_PUSHH_U16_I 0x92
++#define DP_PUSHH_U32_I 0x93
++#define DP_PUSHH_U64_I 0x94
++#define DP_POPH_U8_I 0x95
++#define DP_POPH_U16_I 0x96
++#define DP_POPH_U32_I 0x97
++#define DP_POPH_U64_I 0x98
++
++#include <asm/dprobes_in.h>
++
++#endif
+diff -urNp linux.orig/include/linux/kprobes.h linux-2.6.0-test11/include/linux/kprobes.h
+--- linux.orig/include/linux/kprobes.h 1970-01-01 05:30:00.000000000 +0530
++++ linux-2.6.0-test11/include/linux/kprobes.h 2003-12-15 15:51:48.000000000 +0530
+@@ -0,0 +1,88 @@
++#ifndef _LINUX_KPROBES_H
++#define _LINUX_KPROBES_H
++#include <linux/config.h>
++#include <linux/list.h>
++#include <linux/fs.h>
++#include <linux/notifier.h>
++#include <linux/smp.h>
++#include <asm/kprobes.h>
++
++struct kprobe;
++struct pt_regs;
++
++typedef int (*kprobe_pre_handler_t)(struct kprobe *, struct pt_regs *);
++typedef void (*kprobe_post_handler_t)(struct kprobe *, struct pt_regs *,
++ unsigned long flags);
++typedef int (*kprobe_fault_handler_t)(struct kprobe *, struct pt_regs *,
++ int trapnr);
++struct kprobe {
++ struct list_head list;
++
++ /* location of the probe point */
++ kprobe_opcode_t *addr;
++
++ /* user space probe info */
++ struct uprobe *user;
++
++ /* Called before addr is executed. */
++ kprobe_pre_handler_t pre_handler;
++
++ /* Called after addr is executed, unless... */
++ kprobe_post_handler_t post_handler;
++
++ /* ... called if executing addr causes a fault (eg. page fault).
++ * Return 1 if it handled fault, otherwise kernel will see it. */
++ kprobe_fault_handler_t fault_handler;
++
++ /* Saved opcode (which has been replaced with breakpoint) */
++ kprobe_opcode_t opcode;
++
++ /* copy of the original instruction */
++ kprobe_opcode_t insn[MAX_INSN_SIZE];
++};
++
++struct uprobe {
++ struct inode *inode;
++ unsigned long offset;
++ kprobe_opcode_t *addr;
++
++ /* for kprobes internal use */
++ struct vm_area_struct *vma;
++ struct page *page;
++};
++
++#ifdef CONFIG_KPROBES
++/* Locks kprobe: irq must be disabled */
++void lock_kprobes(void);
++void unlock_kprobes(void);
++
++/* kprobe running now on this CPU? */
++static inline int kprobe_running(void)
++{
++ extern unsigned int kprobe_cpu;
++ return kprobe_cpu == smp_processor_id();
++}
++
++extern void arch_prepare_kprobe(struct kprobe *p);
++
++/* Get the kprobe at this addr (if any). Must have called lock_kprobes */
++extern struct kprobe *get_kprobe(void *addr);
++extern void put_kprobe(struct kprobe *p);
++extern void set_opcode(struct kprobe *p, kprobe_opcode_t opcode);
++
++extern int register_kprobe(struct kprobe *p);
++extern void unregister_kprobe(struct kprobe *p);
++extern int register_kprobe_user(struct kprobe *p);
++extern void unregister_kprobe_user(struct kprobe *p);
++extern int insert_kprobe_user(struct kprobe *p);
++extern int remove_kprobe_user(struct kprobe *p);
++#else
++static inline int kprobe_running(void) { return 0; }
++static inline int register_kprobe(struct kprobe *p) { return -ENOSYS; }
++static inline void unregister_kprobe(struct kprobe *p) { }
++static inline int register_kprobe_user(struct kprobe *p) { return -ENOSYS; }
++static inline void unregister_kprobe_user(struct kprobe *p) { }
++static inline int insert_kprobe_user(struct kprobe *p) { return -ENOSYS; }
++static inline int remove_kprobe_user(struct kprobe *p) { }
++#endif
++#endif /* _LINUX_KPROBES_H */
+diff -urNp linux.orig/kernel/extable.c linux-2.6.0-test11/kernel/extable.c
+--- linux.orig/kernel/extable.c 2003-11-27 02:13:32.000000000 +0530
++++ linux-2.6.0-test11/kernel/extable.c 2003-12-15 15:51:48.000000000 +0530
+@@ -32,6 +32,7 @@ const struct exception_table_entry *sear
+ e = search_module_extables(addr);
+ return e;
+ }
++EXPORT_SYMBOL(search_exception_tables);
+
+ int kernel_text_address(unsigned long addr)
+ {
+diff -urNp linux.orig/kernel/fork.c linux-2.6.0-test11/kernel/fork.c
+--- linux.orig/kernel/fork.c 2003-11-27 02:12:58.000000000 +0530
++++ linux-2.6.0-test11/kernel/fork.c 2003-12-15 15:51:48.000000000 +0530
+@@ -1229,3 +1229,7 @@ void __init proc_caches_init(void)
+ if(!mm_cachep)
+ panic("vma_init: Cannot alloc mm_struct SLAB cache");
+ }
++#ifdef CONFIG_DPROBES_MODULE
++EXPORT_SYMBOL(mmput);
++#endif
++
+diff -urNp linux.orig/kernel/kprobes.c linux-2.6.0-test11/kernel/kprobes.c
+--- linux.orig/kernel/kprobes.c 1970-01-01 05:30:00.000000000 +0530
++++ linux-2.6.0-test11/kernel/kprobes.c 2003-12-15 15:51:48.000000000 +0530
+@@ -0,0 +1,379 @@
++/* Support for kernel probes.
++ (C) 2002 Vamsi Krishna S <vamsi_krishna@in.ibm.com>.
++ Support for user space probes.
++ (C) 2003 Prasanna S Panchamukhi <prasanna@in.ibm.com>.
++*/
++#include <linux/kprobes.h>
++#include <linux/sysrq.h>
++#include <linux/kallsyms.h>
++#include <linux/spinlock.h>
++#include <linux/hash.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/highmem.h>
++#include <linux/pagemap.h>
++#include <asm/cacheflush.h>
++#include <asm/errno.h>
++
++#define KPROBE_HASH_BITS 6
++#define KPROBE_TABLE_SIZE (1 << KPROBE_HASH_BITS)
++
++static struct list_head kprobe_table[KPROBE_TABLE_SIZE];
++
++unsigned int kprobe_cpu = NR_CPUS;
++static spinlock_t kprobe_lock = SPIN_LOCK_UNLOCKED;
++
++void kprobes_code_start(void){}
++
++/* Locks kprobe: irqs must be disabled */
++void lock_kprobes(void)
++{
++ spin_lock(&kprobe_lock);
++ kprobe_cpu = smp_processor_id();
++}
++
++void unlock_kprobes(void)
++{
++ kprobe_cpu = NR_CPUS;
++ spin_unlock(&kprobe_lock);
++}
++
++static struct kprobe *get_uprobe_at(struct inode *inode, unsigned long offset)
++{
++ struct list_head *head;
++ struct kprobe *p;
++
++ head = &kprobe_table[hash_long((unsigned long)inode*offset,
++ KPROBE_HASH_BITS)];
++ list_for_each_entry(p, head, list) {
++ if (p->user->inode == inode && p->user->offset == offset)
++ return p;
++ }
++ return NULL;
++}
++
++/*
++ * This leaves with page, and kmap held. They will be released in
++ * put_kprobe_user. Not sure if holding page_table_lock is also
++ * needed, it is a very small, probably can't happen, race where
++ * vma could be gone by the time we complete the single-step.
++ */
++static void get_kprobe_user(struct uprobe *u)
++{
++ kprobe_opcode_t *addr;
++ u->page = find_get_page(u->inode->i_mapping, u->offset >> PAGE_CACHE_SHIFT);
++ addr = (kprobe_opcode_t *)kmap_atomic(u->page, KM_USER0);
++ u->addr = (kprobe_opcode_t *) ((unsigned long) addr + (unsigned long) ( u->offset & ~PAGE_MASK));
++
++}
++
++static void put_kprobe_user(struct uprobe *u)
++{
++ kunmap_atomic(u->addr, KM_USER0);
++ page_cache_release(u->page);
++}
++
++/*
++ * We need to look up the inode and offset from the vma. We can't depend on
++ * the page->(mapping, index) as that would be incorrect if we ever swap this
++ * page out (possible for pages which are dirtied by GDB breakpoints etc)
++ *
++ * We acquire page_table_lock here to ensure that:
++ * - current page doesn't go away from under us (kswapd)
++ * - mm->mmap consistancy (vma are always added under this lock)
++ *
++ * We will never deadlock on page_table_lock, we always come here due to a
++ * probe in user space, no kernel code could have executed to take the
++ * page_table_lock.
++ */
++static struct kprobe *get_uprobe(void *addr)
++{
++ struct mm_struct *mm = current->mm;
++ struct vm_area_struct *vma;
++ struct inode *inode;
++ unsigned long offset;
++ struct kprobe *p;
++
++ spin_lock(&mm->page_table_lock);
++ vma = find_vma(mm, (unsigned long)addr);
++ offset = (unsigned long)addr - vma->vm_start + (vma->vm_pgoff << PAGE_SHIFT);
++ if (!vma->vm_file) {
++ spin_unlock(&mm->page_table_lock);
++ return NULL;
++ }
++ inode = vma->vm_file->f_dentry->d_inode;
++ spin_unlock(&mm->page_table_lock);
++
++ p = get_uprobe_at(inode, offset);
++ if (p) {
++ get_kprobe_user(p->user);
++ p->addr = addr;
++ p->user->vma = vma;
++ }
++ return p;
++}
++
++/* You have to be holding the kprobe_lock */
++struct kprobe *get_kprobe(void *addr)
++{
++ struct list_head *head, *tmp;
++
++ if ((unsigned long)addr < PAGE_OFFSET)
++ return get_uprobe(addr);
++
++ head = &kprobe_table[hash_ptr(addr, KPROBE_HASH_BITS)];
++ list_for_each(tmp, head) {
++ struct kprobe *p = list_entry(tmp, struct kprobe, list);
++ if (p->addr == addr)
++ return p;
++ }
++ return NULL;
++}
++
++void put_kprobe(struct kprobe *p)
++{
++ if (p->user)
++ put_kprobe_user(p->user);
++}
++
++static void set_opcode_user(struct kprobe *p, kprobe_opcode_t opcode)
++{
++ *p->user->addr = opcode;
++ flush_icache_user_range(p->user->vma, p->user->page, addr, sizeof(kprobe_opcode_t));
++}
++
++static void set_opcode_k(struct kprobe *p, kprobe_opcode_t opcode)
++{
++ kprobe_opcode_t *addr = p->addr;
++ *addr = opcode;
++ flush_icache_range(addr, addr + sizeof(kprobe_opcode_t));
++}
++
++void set_opcode(struct kprobe *p, kprobe_opcode_t opcode)
++{
++ if ((unsigned long)p->addr > PAGE_OFFSET)
++ set_opcode_k(p, opcode);
++ else
++ set_opcode_user(p, opcode);
++}
++
++int register_kprobe(struct kprobe *p)
++{
++ int ret = 0;
++ kprobe_opcode_t *addr = p->addr;
++
++ spin_lock_irq(&kprobe_lock);
++ if (get_kprobe(addr)) {
++ ret = -EEXIST;
++ goto out;
++ }
++ list_add(&p->list, &kprobe_table[hash_ptr(addr,
++ KPROBE_HASH_BITS)]);
++ arch_prepare_kprobe(p);
++ p->opcode = *addr;
++ set_opcode_k(p, BREAKPOINT_INSTRUCTION);
++ out:
++ spin_unlock_irq(&kprobe_lock);
++ return ret;
++}
++
++void unregister_kprobe(struct kprobe *p)
++{
++ spin_lock_irq(&kprobe_lock);
++ set_opcode_k(p, p->opcode);
++ list_del(&p->list);
++ spin_unlock_irq(&kprobe_lock);
++}
++
++/*
++ * User space probes defines four simple and lightweight interfaces :
++ *
++ * register_kprobe_user :
++ * Registration of user space probes is defined for a pair of inode and
++ * offset within the executable where the probes need to be inserted.
++ * Similar to kernel space probes registration, the user space
++ * registration provides three callback functions.
++ * User space registration requires a kprobes structure as an argument
++ * with the following fields initialized:
++ *
++ * pre_handler - pointer to the function that will be called when the
++ * probed instruction is about to execute.
++ * post_handler - pointer to the function that will be called after the
++ * successful execution of the instruction.
++ * fault_handler - pointer to the function that will be called if a
++ * software exception occurs, while executing inside the probe_handler
++ * or while single stepping.
++ * user - pointer to the struct uprobe. The following fields within this
++ * structure must be initialized:
++ *
++ * inode - pointer to the inode of the executable.
++ * offset - offset within the executable, where probes are
++ * inserted/removed. Each unique pair of inode and offset is
++ * used as a hashing function.
++ *
++ * User is required to retain this structure until the probes are unregistered.
++ * This routine should be called only once for a given pair of inode and
++ * offset. Only after the user space probes are registered, the user
++ * can insert and remove probes at the given address.
++ */
++
++int register_kprobe_user(struct kprobe *p)
++{
++ int ret = 0;
++ spin_lock_irq(&kprobe_lock);
++ if (get_uprobe_at(p->user->inode, p->user->offset)) {
++ ret = -EEXIST;
++ goto out;
++ }
++ list_add(&p->list, &kprobe_table[hash_long(
++ (unsigned long)p->user->inode * p->user->offset,
++ KPROBE_HASH_BITS)]);
++ p->opcode = BREAKPOINT_INSTRUCTION;
++ out :
++ spin_unlock_irq(&kprobe_lock);
++ return ret;
++
++}
++
++/*
++ * unregister_kprobe_user:
++ * Every registered user space probe must be unregistered.
++ * For unregistering, kprobes structure must be passed with
++ * the following fields initialized:
++ *
++ * user->inode - pointer to the inode of the executable.
++ * user->offset - offset within the executable, where the probe was registered.
++ *
++ * This routine must be called after all the probes for a given
++ * pair of inode and offset are removed.
++ */
++
++void unregister_kprobe_user(struct kprobe *p)
++{
++ spin_lock_irq(&kprobe_lock);
++ if (get_uprobe_at(p->user->inode, p->user->offset))
++ list_del(&p->list);
++ spin_unlock_irq(&kprobe_lock);
++}
++
++/*
++ * insert_kprobe_userspace:
++ * To insert a user space probe within a page at a given address,
++ * this routine must be called with the following fields initialized:
++ *
++ * user->inode - pointer to the inode of the executable.
++ * user->offset - offset within the executable, where we need to insert a probe.
++ * user->addr - address in the user address space where probes need to inserted.
++ * user->page - pointer to the page containing the address.
++ * user->vma - pointer to the vma containing the address.
++ *
++ * The user space probes can be inserted into the pages existing in the memory or
++ * pages read via readpage routine of the inode's address space operations or
++ * swapping in of the process private page.
++ * User has to ensure that the page containing the specified address
++ * (user->addr) must be present in the memory before calling.
++ * User has to first register user space probes for a given pair of
++ * of inode and offset before calling this routine to insert probes.
++ *
++ */
++
++int insert_kprobe_user(struct kprobe *p)
++{
++ int ret = 0;
++ spin_lock_irq(&kprobe_lock);
++ if (!get_uprobe_at(p->user->inode, p->user->offset)) {
++ ret = -ENODATA;
++ goto out;
++ }
++
++ if (*p->user->addr == BREAKPOINT_INSTRUCTION) {
++ ret = -EEXIST;
++ goto out;
++ }
++
++ p->opcode = *p->user->addr;
++ set_opcode_user(p, BREAKPOINT_INSTRUCTION);
++ out :
++ spin_unlock_irq(&kprobe_lock);
++ return ret;
++}
++
++/*
++ * remove_kprobe_user:
++ * For removing user space probe from a page at the given address, this routine must be
++ * called with the following fields initialized:
++ *
++ * user->inode - pointer to the inode of the executable.
++ * user->offset - offset within the executable,where we need to remove a probe.
++ * user->addr - address in user address space where probes need to removed.
++ * user->page - pointer to the page containing the address.
++ * user->vma - pointer to the vma containing the address.
++ *
++ * User has to ensure that the page containing the specified address
++ * (user->addr) must be present in the memory before calling this routine.
++ * Before unregistering, all the probes inserted at a given inode and offset
++ * must be removed.
++ */
++
++int remove_kprobe_user(struct kprobe *p)
++{
++ int ret = 0;
++ spin_lock_irq(&kprobe_lock);
++ if (!get_uprobe_at(p->user->inode, p->user->offset)) {
++ ret = -ENODATA;
++ goto out;
++ }
++ set_opcode_user(p, p->opcode);
++ out:
++ spin_unlock_irq(&kprobe_lock);
++ return ret;
++}
++
++static void show_kprobes(int key, struct pt_regs *pt_regs, struct tty_struct *tty)
++{
++ int i;
++ struct kprobe *p;
++
++ /* unsafe: kprobe_lock ought to be taken here */
++ for(i = 0; i < KPROBE_TABLE_SIZE; i++) {
++ if (!list_empty(&kprobe_table[i])) {
++ list_for_each_entry(p, &kprobe_table[i], list) {
++ printk("[<%p>] ", p->addr);
++ print_symbol("%s\t", (unsigned long)p->addr);
++ print_symbol("%s\n", (unsigned long)p->pre_handler);
++ }
++ }
++ }
++}
++
++static struct sysrq_key_op sysrq_show_kprobes = {
++ .handler = show_kprobes,
++ .help_msg = "shoWkprobes",
++ .action_msg = "Show kprobes\n"
++};
++
++static int __init init_kprobes(void)
++{
++ int i;
++
++ register_sysrq_key('w', &sysrq_show_kprobes);
++ /* FIXME allocate the probe table, currently defined statically */
++ /* initialize all list heads */
++ for (i = 0; i < KPROBE_TABLE_SIZE; i++)
++ INIT_LIST_HEAD(&kprobe_table[i]);
++ return 0;
++}
++
++void kprobes_code_end(void) {}
++__initcall(init_kprobes);
++
++EXPORT_SYMBOL_GPL(register_kprobe);
++EXPORT_SYMBOL_GPL(unregister_kprobe);
++EXPORT_SYMBOL_GPL(register_kprobe_user);
++EXPORT_SYMBOL_GPL(unregister_kprobe_user);
++EXPORT_SYMBOL_GPL(insert_kprobe_user);
++EXPORT_SYMBOL_GPL(remove_kprobe_user);
++EXPORT_SYMBOL_GPL(kprobes_code_start);
++EXPORT_SYMBOL_GPL(kprobes_code_end);
++EXPORT_SYMBOL_GPL(kprobes_asm_code_start);
++EXPORT_SYMBOL_GPL(kprobes_asm_code_end);
+diff -urNp linux.orig/kernel/Makefile linux-2.6.0-test11/kernel/Makefile
+--- linux.orig/kernel/Makefile 2003-11-27 02:13:24.000000000 +0530
++++ linux-2.6.0-test11/kernel/Makefile 2003-12-15 15:51:48.000000000 +0530
+@@ -19,6 +19,7 @@ obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o
+ obj-$(CONFIG_COMPAT) += compat.o
+ obj-$(CONFIG_IKCONFIG) += configs.o
+ obj-$(CONFIG_IKCONFIG_PROC) += configs.o
++obj-$(CONFIG_KPROBES) += kprobes.o
+
+ ifneq ($(CONFIG_IA64),y)
+ # According to Alan Modra <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is
+diff -urNp linux.orig/mm/memory.c linux-2.6.0-test11/mm/memory.c
+--- linux.orig/mm/memory.c 2003-11-27 02:13:52.000000000 +0530
++++ linux-2.6.0-test11/mm/memory.c 2003-12-15 15:51:48.000000000 +0530
+@@ -1699,3 +1699,7 @@ struct page * vmalloc_to_page(void * vma
+ }
+
+ EXPORT_SYMBOL(vmalloc_to_page);
++#ifdef CONFIG_DPROBES_MODULE
++EXPORT_SYMBOL(handle_mm_fault);
++#endif
++
+diff -urNp linux.orig/mm/swap.c linux-2.6.0-test11/mm/swap.c
+--- linux.orig/mm/swap.c 2003-11-27 02:13:37.000000000 +0530
++++ linux-2.6.0-test11/mm/swap.c 2003-12-15 15:51:48.000000000 +0530
+@@ -419,3 +419,8 @@ void __init swap_setup(void)
+ * _really_ don't want to cluster much more
+ */
+ }
++#ifdef CONFIG_DPROBES_MODULE
++EXPORT_SYMBOL(swapper_space);
++EXPORT_SYMBOL(delete_from_swap_cache);
++#endif
++