]> Zhao Yanbai Git Server - minix.git/commitdiff
this patch adds access to the debug breakpoints to
authorErik van der Kouwe <erik@minix3.org>
Fri, 19 Mar 2010 19:15:20 +0000 (19:15 +0000)
committerErik van der Kouwe <erik@minix3.org>
Fri, 19 Mar 2010 19:15:20 +0000 (19:15 +0000)
the kernel. They are not used atm, but having them in trunk allows them
to be easily used when needed. To set a breakpoint that triggers when
the variable foo is written to (the most common use case), one calls:

breakpoint_set(vir2phys((vir_bytes) &foo), 0,
  BREAKPOINT_FLAG_MODE_GLOBAL |
  BREAKPOINT_FLAG_RW_WRITE |
  BREAKPOINT_FLAG_LEN_4);

It can later be disabled using:

breakpoint_set(vir2phys((vir_bytes) &foo), 0,
  BREAKPOINT_FLAG_MODE_OFF);

There are some limitations:

- There are at most four breakpoints (hardware limit); the index of the
  breakpoint (0-3) is specified as the second parameter of
  breakpoint_set.

- The breakpoint exception in the kernel is not handled and causes a
  panic; it would be reasonably easy to change this by inspecing DR6,
  printing a message, disabling the breakpoint and continuing. However,
  in my experience even just a panic can be very useful.

- Breakpoints can be set only in the part of the address space that is
  in every page table. It is useful for the kernel, but to use this for
  user processes would require saving and restoring the debug registers
  as part of the context switch. Although the CPU provides support for
  local breakpoints (I implemened this as BREAKPOINT_FLAG_LOCAL) they
  only work if task switching is used.

commands/i386/gas2ack/asm86.c
commands/i386/gas2ack/gas2ack.c
docs/UPDATING
kernel/arch/i386/Makefile
kernel/arch/i386/breakpoints.c [new file with mode: 0644]
kernel/arch/i386/debugreg.S [new file with mode: 0644]
kernel/arch/i386/debugreg.h [new file with mode: 0644]
kernel/arch/i386/proto.h

index 29c5b7ba9510c2481fada358afda2f348aa5e04e..adde7c531d22fabec47bc92b54c58a5d911163db 100644 (file)
@@ -75,6 +75,7 @@ int isregister(const char *name)
                "eax", "ebx", "ecx", "edx", "esi", "edi", "ebp", "esp",
                "cs", "ds", "es", "fs", "gs", "ss",
                "cr0", "cr1", "cr2", "cr3", "cr4",
+               "dr0", "dr1", "dr2", "dr3", "dr6", "dr7",
                "st",
        };
        int reg;
index 2b6088d1644e403b36ca16295e3acf835b7a0f14..f1dfc2c5f952beae981c4765896f523ebed4f49c 100644 (file)
@@ -1,7 +1,7 @@
-/*     asmconv 1.11 - convert 80X86 assembly           Author: Kees J. Bot
+/*     asmconv 1.12 - convert 80X86 assembly           Author: Kees J. Bot
  *                                                             24 Dec 1993
  */
-static char version[] = "1.11";
+static char version[] = "1.12";
 
 #define nil 0
 #include <stdio.h>
index 872fe1525ec6949163d992cddc3245cabf81aa82..31848400f3b8fe4597bff4720633daf5547cfc6e 100644 (file)
@@ -70,4 +70,6 @@
         The hello driver (/dev/hello) added to the distribution:
         # cd /usr/src/commands/scripts && make clean install
         # cd /dev && MAKEDEV hello
+20100318:
+        Gas2ack updates: Run 'make install' in commands/i386/gas2ack
 
index 69eb7601715e3b64a64ec9c335f7ff492a997bb9..d4a10b566aa2c1f360ee7bf136f249162aaa0d4b 100644 (file)
@@ -9,7 +9,9 @@ ARCHAR=$(ARCH).a
 # by an upper level Makefile.
 
 OBJS=  arch_do_vmctl.o \
+       breakpoints.o \
        clock.o \
+       debugreg.o \
        do_int86.o \
        do_iopenable.o \
        do_readbios.o \
@@ -56,6 +58,11 @@ apic_asm.o: apic_asm.S
        gas2ack -u $@.tmp $@.s
        $(CC) $(CFLAGS) -c -o $@ $@.s
 
+debugreg.o: debugreg.S
+       $(CC) $(CFLAGS) -E -D__ASSEMBLY__ -o $@.tmp $<
+       gas2ack -u $@.tmp $@.s
+       $(CC) $(CFLAGS) -c -o $@ $@.s
+
 $(HEAD): mpx386.o
        cp $< $@
 
diff --git a/kernel/arch/i386/breakpoints.c b/kernel/arch/i386/breakpoints.c
new file mode 100644 (file)
index 0000000..0e9d407
--- /dev/null
@@ -0,0 +1,60 @@
+#include "../../kernel.h"
+#include "proto.h"
+
+#include "debugreg.h"
+
+int breakpoint_set(phys_bytes linaddr, int bp, int flags)
+{
+       u32_t dr7, dr7flags;
+       
+       if (bp >= BREAKPOINT_COUNT)
+               return EINVAL;
+       
+       /* convert flags */
+       dr7flags = 0;
+       switch (flags & BREAKPOINT_FLAG_RW_MASK) {
+               case BREAKPOINT_FLAG_RW_EXEC:  dr7flags |= DR7_RW_EXEC(bp);  break;
+               case BREAKPOINT_FLAG_RW_WRITE: dr7flags |= DR7_RW_WRITE(bp); break;
+               case BREAKPOINT_FLAG_RW_RW:    dr7flags |= DR7_RW_RW(bp);    break;
+               default: return EINVAL;                 
+       }
+       switch (flags & BREAKPOINT_FLAG_LEN_MASK) {
+               case BREAKPOINT_FLAG_LEN_1: dr7flags |= DR7_LN_1(bp); break;
+               case BREAKPOINT_FLAG_LEN_2: dr7flags |= DR7_LN_2(bp); break;
+               case BREAKPOINT_FLAG_LEN_4: dr7flags |= DR7_LN_4(bp); break;
+               default: return EINVAL; 
+       }
+       switch (flags & BREAKPOINT_FLAG_MODE_MASK) {
+               case BREAKPOINT_FLAG_MODE_OFF: break;
+               case BREAKPOINT_FLAG_MODE_LOCAL: dr7flags |= DR7_L(bp); break;
+               case BREAKPOINT_FLAG_MODE_GLOBAL: dr7flags |= DR7_G(bp); break;
+               default: return EINVAL; 
+       }
+       
+       /* disable breakpoint before setting address */
+       dr7 = st_dr7();
+       dr7 &= ~(DR7_L(bp) | DR7_G(bp) | DR7_RW_MASK(bp) | DR7_LN_MASK(bp));
+printf("ld_dr7(0x%x)\n",dr7);
+       ld_dr7(dr7);
+
+       /* need to set new breakpoint? */       
+       if ((flags & BREAKPOINT_FLAG_MODE_MASK) == BREAKPOINT_FLAG_MODE_OFF)
+               return 0;
+                       
+       /* set breakpoint address */
+       switch (bp) {
+               case 0: ld_dr0(linaddr); break;
+               case 1: ld_dr1(linaddr); break;
+               case 2: ld_dr2(linaddr); break;
+               case 3: ld_dr3(linaddr); break;
+               default: panic(__FILE__, "invalid breakpoint index", __LINE__);
+       }
+printf("ld_dr%d(0x%x, 0x%x)\n",bp,linaddr);
+       
+       /* set new flags */
+       dr7 |= dr7flags;
+       ld_dr7(dr7);
+printf("ld_dr7(0x%x)\n",dr7);
+       return 0;
+}
+
diff --git a/kernel/arch/i386/debugreg.S b/kernel/arch/i386/debugreg.S
new file mode 100644 (file)
index 0000000..9d60c5b
--- /dev/null
@@ -0,0 +1,26 @@
+.text
+
+#define LD_ST_REG(reg)                                                 ;\
+.globl ld_##reg                                                        ;\
+.globl st_##reg                                                        ;\
+                                                                       ;\
+ld_##reg:                                                              ;\
+       mov     4(%esp), %eax                                           ;\
+       mov     %eax,   %reg                                            ;\
+       ret                                                             ;\
+                                                                       ;\
+st_##reg:                                                              ;\
+       mov     %reg,   %eax                                            ;\
+       ret
+
+/*
+ * void ld_dr0(u32_t value)
+ * u32_t st_dr0(void)
+ */
+LD_ST_REG(dr0)
+LD_ST_REG(dr1)
+LD_ST_REG(dr2)
+LD_ST_REG(dr3)
+LD_ST_REG(dr6)
+LD_ST_REG(dr7)
+
diff --git a/kernel/arch/i386/debugreg.h b/kernel/arch/i386/debugreg.h
new file mode 100644 (file)
index 0000000..8590fea
--- /dev/null
@@ -0,0 +1,44 @@
+#ifndef __DEBUGREG_H__
+#define __DEBUGREG_H__
+
+/* DR6: status flags */
+#define DR6_B(bp)      (1 << (bp))     /* breakpoint was triggered */
+#define DR6_BD         (1 << 13)       /* debug register access detected */
+#define DR6_BS         (1 << 14)       /* single step */
+#define DR6_BT         (1 << 15)       /* task switch */
+
+/* DR7: control flags */
+#define        DR7_L(bp)       (1 << (2*(bp)))         /* breakpoint armed locally */
+#define        DR7_G(bp)       (1 << (1+2*(bp)))       /* breakpoint armed globally */
+#define        DR7_LE          (1 << 8)                /* exact local breakpoints */
+#define        DR7_GE          (1 << 9)                /* exact global breakpoints */
+#define        DR7_GD          (1 << 13)               /* detect debug reg movs */
+
+#define        DR7_RW_MASK(bp)         (3 << (16+4*(bp)))      
+#define        DR7_RW_EXEC(bp)         (0 << (16+4*(bp)))      /* execute */
+#define        DR7_RW_WRITE(bp)        (1 << (16+4*(bp)))      /* write */
+#define        DR7_RW_IO(bp)           (2 << (16+4*(bp)))      /* IO */
+#define        DR7_RW_RW(bp)           (3 << (16+4*(bp)))      /* read or write */
+
+#define        DR7_LN_MASK(bp) (3 << (18+4*(bp)))
+#define        DR7_LN_1(bp)    (0 << (18+4*(bp)))      /* 1 byte */
+#define        DR7_LN_2(bp)    (1 << (18+4*(bp)))      /* 2 bytes */
+#define        DR7_LN_8(bp)    (2 << (18+4*(bp)))      /* 8 bytes */
+#define        DR7_LN_4(bp)    (3 << (18+4*(bp)))      /* 4 bytes */
+
+/* debugreg.S */
+void ld_dr0(u32_t value);
+void ld_dr1(u32_t value);
+void ld_dr2(u32_t value);
+void ld_dr3(u32_t value);
+void ld_dr6(u32_t value);
+void ld_dr7(u32_t value);
+u32_t st_dr0(void); 
+u32_t st_dr1(void); 
+u32_t st_dr2(void); 
+u32_t st_dr3(void); 
+u32_t st_dr6(void); 
+u32_t st_dr7(void); 
+
+#endif /* __DEBUGREG_H__ */
+
index b6f00631c7227e1aaaa5d5f1748a8d6607b9d4ab..5e7c55f3d18847d6e61ff6f70081074be7b4ab4f 100644 (file)
@@ -156,6 +156,23 @@ _PROTOTYPE(void __copy_msg_from_user_end, (void));
 _PROTOTYPE(void __copy_msg_to_user_end, (void));
 _PROTOTYPE(void __user_copy_msg_pointer_failure, (void));
 
+/* breakpoints.c */
+#define BREAKPOINT_COUNT               4
+#define BREAKPOINT_FLAG_RW_MASK                (3 << 0)
+#define BREAKPOINT_FLAG_RW_EXEC                (0 << 0)
+#define BREAKPOINT_FLAG_RW_WRITE       (1 << 0)
+#define BREAKPOINT_FLAG_RW_RW          (2 << 0)
+#define BREAKPOINT_FLAG_LEN_MASK       (3 << 2)
+#define BREAKPOINT_FLAG_LEN_1          (0 << 2)
+#define BREAKPOINT_FLAG_LEN_2          (1 << 2)
+#define BREAKPOINT_FLAG_LEN_4          (2 << 2)
+#define BREAKPOINT_FLAG_MODE_MASK      (3 << 4)
+#define BREAKPOINT_FLAG_MODE_OFF       (0 << 4)
+#define BREAKPOINT_FLAG_MODE_LOCAL     (1 << 4)
+#define BREAKPOINT_FLAG_MODE_GLOBAL    (2 << 4)
+
+_PROTOTYPE(int breakpoint_set, (phys_bytes linaddr, int index, int flags));
+
 /* functions defined in architecture-independent kernel source. */
 #include "../../proto.h"