vserver 1.9.5.x5
[linux-2.6.git] / arch / i386 / kernel / kprobes.c
index 4d066cc..f74b755 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/spinlock.h>
 #include <linux/preempt.h>
 #include <asm/kdebug.h>
+#include <asm/desc.h>
 
 /* kprobe_status settings */
 #define KPROBE_HIT_ACTIVE      0x00000001
@@ -42,6 +43,7 @@ static struct pt_regs jprobe_saved_regs;
 static long *jprobe_saved_esp;
 /* copy of the kernel stack at the probe fire time */
 static kprobe_opcode_t jprobes_stack[MAX_STACK_SIZE];
+void jprobe_return_end(void);
 
 /*
  * returns non-zero if opcode modifies the interrupt flag.
@@ -58,9 +60,18 @@ static inline int is_IF_modifier(kprobe_opcode_t opcode)
        return 0;
 }
 
-void arch_prepare_kprobe(struct kprobe *p)
+int arch_prepare_kprobe(struct kprobe *p)
+{
+       return 0;
+}
+
+void arch_copy_kprobe(struct kprobe *p)
+{
+       memcpy(p->ainsn.insn, p->addr, MAX_INSN_SIZE * sizeof(kprobe_opcode_t));
+}
+
+void arch_remove_kprobe(struct kprobe *p)
 {
-       memcpy(p->insn, p->addr, MAX_INSN_SIZE * sizeof(kprobe_opcode_t));
 }
 
 static inline void disarm_kprobe(struct kprobe *p, struct pt_regs *regs)
@@ -73,22 +84,33 @@ static inline void prepare_singlestep(struct kprobe *p, struct pt_regs *regs)
 {
        regs->eflags |= TF_MASK;
        regs->eflags &= ~IF_MASK;
-       regs->eip = (unsigned long)&p->insn;
+       regs->eip = (unsigned long)&p->ainsn.insn;
 }
 
 /*
  * Interrupts are disabled on entry as trap3 is an interrupt gate and they
  * remain disabled thorough out this function.
  */
-static inline int kprobe_handler(struct pt_regs *regs)
+static int kprobe_handler(struct pt_regs *regs)
 {
        struct kprobe *p;
        int ret = 0;
-       u8 *addr = (u8 *) (regs->eip - 1);
+       kprobe_opcode_t *addr = NULL;
+       unsigned long *lp;
 
        /* We're in an interrupt, but this is clear and BUG()-safe. */
        preempt_disable();
-
+       /* Check if the application is using LDT entry for its code segment and
+        * calculate the address by reading the base address from the LDT entry.
+        */
+       if ((regs->xcs & 4) && (current->mm)) {
+               lp = (unsigned long *) ((unsigned long)((regs->xcs >> 3) * 8)
+                                       + (char *) current->mm->context.ldt);
+               addr = (kprobe_opcode_t *) (get_desc_base(lp) + regs->eip -
+                                               sizeof(kprobe_opcode_t));
+       } else {
+               addr = (kprobe_opcode_t *)(regs->eip - sizeof(kprobe_opcode_t));
+       }
        /* Check we're not actually recursing */
        if (kprobe_running()) {
                /* We *are* holding lock here, so this is safe.
@@ -111,6 +133,11 @@ static inline int kprobe_handler(struct pt_regs *regs)
        p = get_kprobe(addr);
        if (!p) {
                unlock_kprobes();
+               if (regs->eflags & VM_MASK) {
+                       /* We are in virtual-8086 mode. Return 0 */
+                       goto no_kprobe;
+               }
+
                if (*addr != BREAKPOINT_INSTRUCTION) {
                        /*
                         * The breakpoint instruction was removed right
@@ -153,7 +180,7 @@ static inline int kprobe_handler(struct pt_regs *regs)
  * instruction.  To avoid the SMP problems that can occur when we
  * temporarily put back the original opcode to single-step, we
  * single-stepped a copy of the instruction.  The address of this
- * copy is p->insn.
+ * copy is p->ainsn.insn.
  *
  * This function prepares to return from the post-single-step
  * interrupt.  We have to fix up the stack as follows:
@@ -173,10 +200,10 @@ static void resume_execution(struct kprobe *p, struct pt_regs *regs)
 {
        unsigned long *tos = (unsigned long *)&regs->esp;
        unsigned long next_eip = 0;
-       unsigned long copy_eip = (unsigned long)&p->insn;
+       unsigned long copy_eip = (unsigned long)&p->ainsn.insn;
        unsigned long orig_eip = (unsigned long)p->addr;
 
-       switch (p->insn[0]) {
+       switch (p->ainsn.insn[0]) {
        case 0x9c:              /* pushfl */
                *tos &= ~(TF_MASK | IF_MASK);
                *tos |= kprobe_old_eflags;
@@ -185,13 +212,13 @@ static void resume_execution(struct kprobe *p, struct pt_regs *regs)
                *tos = orig_eip + (*tos - copy_eip);
                break;
        case 0xff:
-               if ((p->insn[1] & 0x30) == 0x10) {
+               if ((p->ainsn.insn[1] & 0x30) == 0x10) {
                        /* call absolute, indirect */
                        /* Fix return addr; eip is correct. */
                        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 */
+               } else if (((p->ainsn.insn[1] & 0x31) == 0x20) ||       /* jmp near, absolute indirect */
+                          ((p->ainsn.insn[1] & 0x31) == 0x21)) {       /* jmp far, absolute indirect */
                        /* eip is correct. */
                        next_eip = regs->eip;
                }
@@ -315,12 +342,12 @@ void jprobe_return(void)
 {
        preempt_enable_no_resched();
        asm volatile ("       xchgl   %%ebx,%%esp     \n"
-                     "       int3                      \n"::"b"
+                     "       int3                      \n"
+                     "       .globl jprobe_return_end  \n"
+                     "       jprobe_return_end:        \n"
+                     "       nop                       \n"::"b"
                      (jprobe_saved_esp):"memory");
 }
-void jprobe_return_end(void)
-{
-};
 
 int longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
 {