This patch extends the current KGDB logic to handle 'Z' and 'z'
GDB packets for setting or removing breakpoints.

Two weak functions have been added to the kgdb_stub.c:
arch_kgdb_set_sw_break() and arch_kgdb_remove_sw_break() could be
overrode by the arch implementations.

Please note, after applying this patch, those architectures, which
already enabled KGDB support, have to create a new asm/kgdb.h and
define the length of the break instruction (BREAK_INSTR_SIZE) in that
file.

Signed-off-by: Tonny Tzeng <tonny.tz...@gmail.com>
---
 common/kgdb.c       |   85 +++++++++++++++++++++++++++++++++++++++++++++++++++
 common/kgdb_stubs.c |   12 +++++++
 include/kgdb.h      |   31 ++++++++++++++++++
 3 files changed, 128 insertions(+), 0 deletions(-)

diff --git a/common/kgdb.c b/common/kgdb.c
index 0531452..66378e5 100644
--- a/common/kgdb.c
+++ b/common/kgdb.c
@@ -220,6 +220,85 @@ hexToInt(char **ptr, int *intValue)
        return (numChars);
 }
 
+/*
+ * Holds information about breakpoints in a kernel. These breakpoints are
+ * added and removed by gdb.
+ */
+static struct kgdb_bkpt        kgdb_break[KGDB_MAX_BREAKPOINTS];
+
+static int kgdb_set_sw_break(int addr)
+{
+       int i, breakno = -1;
+       struct kgdb_bkpt *bkpt;
+
+       for (i = 0; i < KGDB_MAX_BREAKPOINTS; i++) {
+               if ((kgdb_break[i].state == BP_SET) &&
+                   (kgdb_break[i].bpt_addr == addr))
+                       return -KGDBERR_BPEXIST;
+               if ((kgdb_break[i].state == BP_REMOVED) &&
+                   (kgdb_break[i].bpt_addr == addr)) {
+                       breakno = i;
+                       break;
+               }
+       }
+       if (breakno == -1)
+               for (i = 0; i < KGDB_MAX_BREAKPOINTS; i++) {
+                       if (kgdb_break[i].state == BP_UNDEFINED) {
+                               breakno = i;
+                               break;
+                       }
+               }
+       if (breakno == -1)
+               return -KGDBERR_BPNOENT;
+
+       bkpt = kgdb_break + breakno;
+       bkpt->state = BP_SET;
+       bkpt->type = BP_BREAKPOINT;
+       bkpt->bpt_addr = addr;
+       arch_kgdb_set_sw_break(bkpt);
+
+       return 0;
+}
+
+static int kgdb_remove_sw_break(int addr)
+{
+       int i;
+       struct kgdb_bkpt *bkpt;
+
+       for (i = 0; i < KGDB_MAX_BREAKPOINTS; i++) {
+               bkpt = kgdb_break + i;
+               if ((bkpt->state == BP_SET) && (bkpt->bpt_addr == addr)) {
+                       bkpt->state = BP_REMOVED;
+                       arch_kgdb_remove_sw_break(bkpt);
+                       return 0;
+               }
+       }
+       return -KGDBERR_BPNOENT;
+}
+
+/* Handle the 'z' or 'Z' breakpoint remove or set packets */
+static void gdb_cmd_break(kgdb_data *kdp)
+{
+       /*
+        * Since GDB-5.3, it's been drafted that '0' is a software
+        * breakpoint, '1' is a hardware breakpoint, so let's do that.
+        */
+       char *bpt_type = &remcomInBuffer[1];
+       char *ptr = &remcomInBuffer[2];
+       int addr, length;
+
+       if (*bpt_type != '0')
+               return;         /* Unsupported. */
+       if (*ptr++ != ',' || !hexToInt(&ptr, &addr) ||
+           *ptr++ != ',' || !hexToInt(&ptr, &length)) {
+               kgdb_error(KGDBERR_BADPARAMS);
+       }
+
+       if ((remcomInBuffer[0] == 'Z' && kgdb_set_sw_break(addr) == 0) ||
+           (remcomInBuffer[0] == 'z' && kgdb_remove_sw_break(addr) == 0))
+               strcpy(remcomOutBuffer, "OK");
+}
+
 /* scan for the sequence $<data>#<checksum>     */
 static void
 getpacket(char *buffer)
@@ -341,7 +420,9 @@ handle_exception (struct pt_regs *regs)
 
        kgdb_interruptible(0);
 
+#ifdef KGDB_DEBUG
        printf("kgdb: handle_exception; trap [0x%x]\n", kgdb_trap(regs));
+#endif
 
        if (kgdb_setjmp(error_jmp_buf) != 0)
                panic("kgdb: error or fault in entry init!\n");
@@ -516,6 +597,10 @@ handle_exception (struct pt_regs *regs)
                                kgdb_error(KGDBERR_BADPARAMS);
                        }
                        break;
+               case 'Z': /* [Z|z]N,AA..AA,LLLL Set/Remove breakpoint type N */
+               case 'z': /*      LLLL bytes at address AA.AA return OK      */
+                       gdb_cmd_break(&kd);
+                       break;
                }                       /* switch */
 
                if (errnum != 0)
diff --git a/common/kgdb_stubs.c b/common/kgdb_stubs.c
index 19b0c18..2b3f424 100644
--- a/common/kgdb_stubs.c
+++ b/common/kgdb_stubs.c
@@ -45,6 +45,18 @@ void kgdb_interruptible(int yes)
 }
 
 __attribute__((weak))
+void arch_kgdb_set_sw_break(struct kgdb_bkpt *bkpt)
+{
+       return;
+}
+
+__attribute__((weak))
+void arch_kgdb_remove_sw_break(struct kgdb_bkpt *bkpt)
+{
+       return;
+}
+
+__attribute__((weak))
 void kgdb_flush_cache_range(void *from, void *to)
 {
        flush_cache((unsigned long)from, (unsigned long)(to - from));
diff --git a/include/kgdb.h b/include/kgdb.h
index f543cd6..82ae8ab 100644
--- a/include/kgdb.h
+++ b/include/kgdb.h
@@ -2,12 +2,41 @@
 #define __KGDB_H__
 
 #include <asm/ptrace.h>
+#include <asm/kgdb.h>
 
 #define KGDBERR_BADPARAMS      1
 #define KGDBERR_NOTHEXDIG      2
 #define KGDBERR_MEMFAULT       3
 #define KGDBERR_NOSPACE                4
 #define KGDBERR_ALIGNFAULT     5
+#define KGDBERR_BPEXIST                6
+#define KGDBERR_BPNOENT                7
+
+#ifndef KGDB_MAX_BREAKPOINTS
+#define KGDB_MAX_BREAKPOINTS   1000
+#endif
+
+enum kgdb_bptype {
+       BP_BREAKPOINT = 0,
+       BP_HARDWARE_BREAKPOINT,
+       BP_WRITE_WATCHPOINT,
+       BP_READ_WATCHPOINT,
+       BP_ACCESS_WATCHPOINT
+};
+
+enum kgdb_bpstate {
+       BP_UNDEFINED = 0,
+       BP_REMOVED,
+       BP_SET,
+       BP_ACTIVE
+};
+
+struct kgdb_bkpt {
+       unsigned long           bpt_addr;
+       unsigned char           saved_instr[BREAK_INSTR_SIZE];
+       enum kgdb_bptype        type;
+       enum kgdb_bpstate       state;
+};
 
 #define KGDBDATA_MAXREGS       8
 #define KGDBDATA_MAXPRIV       8
@@ -56,6 +85,8 @@ extern void kgdb_putreg(struct pt_regs *, int, char *, int);
 extern void kgdb_putregs(struct pt_regs *, char *, int);
 extern int kgdb_trap(struct pt_regs *);
 extern void kgdb_breakpoint(int argc, char *argv[]);
+extern void arch_kgdb_set_sw_break(struct kgdb_bkpt *);
+extern void arch_kgdb_remove_sw_break(struct kgdb_bkpt *);
 
 /* these functions are provided by the platform serial driver */
 extern void kgdb_serial_init(void);
-- 
1.6.0.6


_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot

Reply via email to