GDB stub for ARM


git-svn-id: svn://svn.rockbox.org/rockbox/trunk@8447 a1c6a512-1295-4272-9138-f99709370657
diff --git a/firmware/SOURCES b/firmware/SOURCES
index 3a5f551..dbfa102 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -170,3 +170,7 @@
 pcm_record.c
 #endif
 sound.c
+#if defined(IRIVER_IFP7XX_SERIES) && defined(STUB)
+common/sscanf.c
+usb_serial.c
+#endif
diff --git a/firmware/crt0.S b/firmware/crt0.S
index ba478f8..5946276 100644
--- a/firmware/crt0.S
+++ b/firmware/crt0.S
@@ -93,6 +93,7 @@
     strhi  r5, [r2], #4
     bhi    1b
     
+#ifndef STUB
     /* Zero out IBSS */
     ldr    r2, =_iedata
     ldr    r3, =_iend
@@ -111,6 +112,7 @@
     ldrhi  r5, [r2], #4
     strhi  r5, [r3], #4
     bhi    1b
+#endif /* !STUB */
 #endif /* !BOOTLOADER */
 
     /* Initialise bss section to zero */
@@ -283,8 +285,10 @@
 
     .text
 
+#ifndef STUB
     .global irq
     .global UIE
+#endif
 
 /* All illegal exceptions call into UIE with exception address as first
    parameter. This is calculated differently depending on which exception
@@ -317,11 +321,18 @@
     b      UIE
 
 irq_handler:
+#ifndef STUB
     stmfd  sp!, {r0-r3, r12, lr}
     bl     irq
     ldmfd  sp!, {r0-r3, r12, lr}
+#endif
     subs   pc, lr, #4
-    
+
+#ifdef STUB
+UIE:
+    b UIE
+#endif
+
 /* 256 words of IRQ stack */
     .space 256*4
 irq_stack:
diff --git a/gdb/Makefile b/gdb/Makefile
index 61f4cb8..fa94eac 100644
--- a/gdb/Makefile
+++ b/gdb/Makefile
@@ -7,6 +7,67 @@
 # $Id$
 #
 
+ifeq ($(ARCHOS),ifp7xx)
+
+INCLUDES= -I$(FIRMDIR)/include -I$(FIRMDIR)/export -I. -I$(OBJDIR)	\
+ -I$(BUILDDIR)
+
+DEPFILE = $(OBJDIR)/dep-stub
+LDS := linker.cfg
+
+SRC := $(shell cat SOURCES | $(CC) -DMEMORYSIZE=$(MEMORYSIZE) $(INCLUDES) $(TARGET) $(DEFINES) -E -P -include "config.h" - )
+DIRS = .
+
+ifdef APPEXTRA
+   DIRS += $(subst :, ,$(APPEXTRA))
+   INCLUDES += $(patsubst %,-I%,$(subst :, ,$(APPEXTRA)))
+endif
+
+ifndef VERSION
+VERSION=$(shell date +%y%m%d-%H%M)
+endif
+
+CFLAGS = $(GCCOPTS) $(INCLUDES) $(TARGET) $(DEFINES)			\
+ -DAPPSVERSION=\"$(VERSION)\" $(EXTRA_DEFINES) -DMEM=${MEMORYSIZE}
+
+OBJS := $(SRC:%.c=$(OBJDIR)/%.o)
+SOURCES = $(SRC)
+LINKFILE = $(OBJDIR)/linkage.lds
+
+LIBROCKBOX = $(BUILDDIR)/librockbox.a
+
+all: $(BUILDDIR)/$(BINARY) $(FLASHFILE)
+
+dep: $(DEPFILE)
+
+$(LINKFILE): $(LDS)
+	@echo "Build LDS file"
+	@cat $< | $(CC) -DMEMORYSIZE=$(MEMORYSIZE) $(INCLUDES) $(TARGET) $(DEFINES) -E -P $(ROMBUILD) - >$@
+
+$(OBJDIR)/stub.elf : $(OBJS) $(LINKFILE) $(DEPFILE) $(LIBROCKBOX)
+	@echo "LD stub.elf"
+	@$(CC) $(GCCOPTS) -Os -nostdlib -o $@ $(OBJS) -L$(BUILDDIR) -L$(BUILDDIR)/firmware -lrockbox -lgcc -T$(LINKFILE) -Wl,-Map,$(OBJDIR)/stub.map
+
+$(OBJDIR)/stub.bin : $(OBJDIR)/stub.elf
+	@echo "OBJCOPY $<"
+	@$(OC) -O binary $< $@
+
+$(BUILDDIR)/$(BINARY) : $(OBJDIR)/stub.bin
+	@echo "Build stub file"
+	$(MKFIRMWARE) $< $@
+
+include $(TOOLSDIR)/make.inc
+
+clean:
+	@echo "cleaning stub"
+	@-rm -f $(OBJS) $(BUILDDIR)/$(BINARY) \
+	$(OBJDIR)/stub.bin $(OBJDIR)/stub.elf $(OBJDIR)/*.map \
+	$(LINKFILE) $(DEPFILE)
+
+-include $(DEPFILE)
+
+else # not ifp7xx
+
 ifdef RECORDER
 EXTRA = -DRECORDER
 EXT = ajz
@@ -38,6 +99,8 @@
 clean:
 	rm $(OBJS) $(TARGET).map $(TARGET).elf $(TARGET).out $(TARGET).mod $(TARGET).ajz
 
-start.o: start.S
+start.o: start.s
 sh-stub.o: sh-stub.c
 setjmp.o: setjmp.S
+
+endif
diff --git a/gdb/SOURCES b/gdb/SOURCES
new file mode 100644
index 0000000..9668d12
--- /dev/null
+++ b/gdb/SOURCES
@@ -0,0 +1,3 @@
+#ifdef CPU_ARM
+arm-stub.c
+#endif
diff --git a/gdb/arm-stub.c b/gdb/arm-stub.c
new file mode 100644
index 0000000..68b24a7
--- /dev/null
+++ b/gdb/arm-stub.c
@@ -0,0 +1,574 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2006 by Tomasz Malesinski
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include <stdbool.h>
+#include <string.h>
+#include "usb_serial.h"
+#include "sscanf.h"
+
+#define BUFMAX 1024
+
+#define VEC_UND 1
+#define VEC_SWI 2
+#define VEC_PABT 3
+#define VEC_DABT 4
+
+static char packet_buf[BUFMAX];
+static char reply_buf[BUFMAX];
+
+static const char hexchars[] = "0123456789abcdef";
+static int gdb_exception_no, gdb_mem_access;
+static unsigned long registers[17];
+
+static inline bool isxdigit(char c)
+{
+    return ((c >= '0') && (c <= '9'))
+        || ((c >= 'a') && (c <= 'f')) || ((c >= 'A') && (c <= 'F'));
+}
+
+static int hex(char ch) {
+    if ((ch >= 'a') && (ch <= 'f'))
+        return ch - 'a' + 10;
+    if ((ch >= '0') && (ch <= '9'))
+        return ch - '0';
+    if ((ch >= 'A') && (ch <= 'F'))
+        return ch - 'A' + 10;
+    return -1;
+}
+
+static void hex_byte(char *s, int byte) {
+    s[0] = hexchars[(byte >> 4) & 0xf];
+    s[1] = hexchars[byte & 0xf];
+}
+
+static void hex_word(char *s, unsigned long val) {
+    int i;
+    for (i = 0; i < 4; i++)
+        hex_byte(s + i * 2, (val >> (i * 8)) & 0xff);
+}
+
+static void hex_string(char *d, char *s) {
+    while (*s) {
+        hex_byte(d, *s++);
+        d += 2;
+    }
+    *d = 0;
+}
+
+static int get_hex_byte(char *s) {
+    return (hex(s[0]) << 4) + hex(s[1]);
+}
+
+static unsigned long get_hex_word(char *s) {
+    int i;
+    unsigned long r = 0;
+    for (i = 3; i >= 0; i--)
+        r = (r << 8) + get_hex_byte(s + i * 2);
+    return r;
+}
+
+static void reply_error(int n, char *reply) {
+    reply[0] = 'E';
+    hex_byte(reply + 1, n);
+    reply[3] = 0;
+}
+
+static void reply_signal(int n, char *reply) {
+    int signal;
+    reply[0] = 'S';
+    switch (n)
+    {
+        case VEC_UND:
+            signal = 4;
+            break;
+        case VEC_PABT:
+        case VEC_DABT:
+            signal = 7;
+            break;
+        default:
+            signal = 5;
+            break;
+    }
+    hex_byte(reply + 1, signal);
+    reply[3] = 0;
+}
+
+static void reply_ok(char *reply) {
+    strcpy(reply, "OK");
+}
+
+static void serial_write(unsigned char *buf, int len) {
+    int i;
+    for (i = 0; i < len; i++)
+        usb_serial_put_byte(buf[i]);
+}
+
+static void  get_packet(char *buf, int len) {
+    int count, checksum;
+    int ch;
+
+    while (1) {
+        do {
+            ch = usb_serial_get_byte();
+        } while (ch != '$');
+
+        checksum = 0;
+        count = 0;
+        while (count < len) {
+            ch = usb_serial_get_byte();
+            if (ch == '$') {
+                checksum = 0;
+                count = 0;
+            } else if (ch == '#')
+                break;
+            else {
+                checksum += ch;
+                buf[count] = ch;
+                count++;
+            }
+        }
+        buf[count] = 0;
+
+        if (ch == '#') {
+            int rchksum;
+
+            ch = usb_serial_get_byte();
+            rchksum = hex(ch) << 4;
+            ch = usb_serial_get_byte();
+            rchksum += hex(ch);
+
+            if ((checksum & 0xff) != rchksum)
+                usb_serial_put_byte('-');
+            else {
+                usb_serial_put_byte('+');
+                return;
+            }
+        }
+    }
+}
+
+static void put_packet(char *buf) {
+    int i, checksum;
+    int ch;
+    char tmp[3];
+
+    do {
+        usb_serial_put_byte('$');
+
+        checksum = 0;
+        for (i = 0; buf[i]; i++)
+            checksum += buf[i];
+
+        serial_write(buf, i);
+
+        tmp[0] = '#';
+        hex_byte(tmp + 1, checksum & 0xff);
+        serial_write(tmp, 3);
+
+        ch = usb_serial_get_byte();
+
+    } while (ch != '+');
+}
+
+static inline unsigned long get_general_reg(int n)
+{
+    return registers[n + 1];
+}
+
+static inline void set_general_reg(int n, unsigned long v)
+{
+    registers[n + 1] = v;
+}
+
+static inline unsigned long get_cpsr(void)
+{
+    return registers[0];
+}
+
+static inline void set_cpsr(unsigned long v)
+{
+    registers[0] = v;
+}
+
+static void g_reply(char *buf) {
+    int i;
+    char *p;
+
+    p = buf;
+    for (i = 0; i < 16; i++) {
+        hex_word(p, get_general_reg(i));
+        p += 8;
+    }
+
+    for (i = 0; i < 8; i++) {
+        memset(p, '0', 16);
+        p += 16;
+    }
+
+    hex_word(p, 0);
+    p += 8;
+    hex_word(p, get_cpsr());
+    p[8] = 0;
+}
+
+static void cmd_get_register(char *args, char *reply) {
+    int r;
+
+    if (sscanf(args, "%x", &r) != 1) {
+        reply_error(0, reply);
+        return;
+    }
+
+    if (r >= 0 && r < 16) {
+        hex_word(reply, get_general_reg(r));
+        reply[8] = 0;
+    } else if (r == 25) {
+        hex_word(reply, get_cpsr());
+        reply[8] = 0;
+    } else {
+        hex_word(reply, 0);
+        reply[8] = 0;
+    }
+}
+
+static void cmd_set_register(char *args, char *reply) {
+  int r, p;
+  unsigned long v;
+
+  p = -1;
+  sscanf(args, "%x=%n", &r, &p);
+  if (p == -1) {
+    reply_error(0, reply);
+    return;
+  }
+
+  v = get_hex_word(args + p);
+  if (r >= 0 && r < 16)
+    set_general_reg(r, v);
+  else if (r == 25)
+    set_cpsr(v);
+  reply_ok(reply);
+}
+
+static void cmd_set_registers(char *args, char *reply) {
+    char *p;
+    int i, len;
+
+    len = strlen(args);
+
+    p = args;
+    for (i = 0; i < 16 && len >= (i + 1) * 8; i++) {
+        set_general_reg(i, get_hex_word(p));
+        p += 8;
+    }
+
+    if (len >= 16 * 8 + 8 * 16 + 2 * 8)
+    {
+        p += 8 * 16 + 8;
+        set_cpsr(get_hex_word(p));
+    }
+
+    reply_ok(reply);
+}
+
+static void cmd_get_memory(char *args, char *reply) {
+    unsigned long addr, len, i;
+
+    if (sscanf(args, "%lx,%lx", &addr, &len) != 2) {
+        reply_error(0, reply);
+        return;
+    }
+
+    if (len > (BUFMAX - 16) / 2) {
+        reply_error(1, reply);
+        return;
+    }
+  
+    gdb_mem_access = 1;
+    for (i = 0; i < len; i++)
+        hex_byte(reply + i * 2, *((unsigned char *)(addr + i)));
+    gdb_mem_access = 0;
+
+    reply[len * 2] = 0;
+}
+
+static void cmd_put_memory(char *args, char *reply) {
+    unsigned long addr, len, i;
+    int pos;
+
+    pos = -1;
+    sscanf(args, "%lx,%lx:%n", &addr, &len, &pos);
+    if (pos == -1) {
+        reply_error(0, reply);
+        return;
+    }
+
+    gdb_mem_access = 1;
+    for (i = 0; i < len; i++)
+        *((unsigned char *)(addr + i)) = get_hex_byte(args + pos + i * 2);
+    gdb_mem_access = 0;
+
+    reply_ok(reply);
+}
+
+static void parse_continue_args(char *args) {
+    int sig;
+    unsigned long addr;
+
+    if (sscanf(args, "%x;%lx", &sig, &addr) == 2) {
+        set_general_reg(15, addr);
+    } else if (sscanf(args, "%lx", &addr) == 1) {
+        set_general_reg(15, addr);
+    }
+}
+
+static void cmd_go(char *args) {
+    parse_continue_args(args);
+
+    asm volatile(
+        "    mov    r1, %0\n"
+        "    ldr    r12, [r1], #4\n"
+        "    mov    r0, r12\n"
+        "    and    r0, r0, #0x1f\n"
+        "    cmp    r0, #0x10\n"
+        "    bne    1f\n"
+        "    ldr    r14, [r1, #60]\n"
+        "    msr    spsr_fsxc, r12\n"
+        "    ldmia  r1, {r0-r14}^\n"
+        "    movs   r15, r14\n"
+        "1:\n"
+        "    msr    cpsr_fsxc, r12\n"
+        "    ldmia  r1, {r0-r15}\n"
+        : : "r" (registers));
+}
+
+static void remote_cmd(char *cmd, char *reply) {
+    (void)cmd;
+    hex_string(reply, "Unrecognized command\n");
+}
+
+static void cmd_query(char *args, char *reply) {
+    if (!strncmp(args, "Rcmd,", 5)) {
+        unsigned i = 0;
+        char *s = args + 5;
+        char cmd[200];
+        while (isxdigit(s[0]) && isxdigit(s[1]) && i < sizeof(cmd) - 1) {
+            cmd[i++] = get_hex_byte(s);
+            s += 2;
+        }
+        cmd[i] = 0;
+        remote_cmd(cmd, reply);
+    } else
+        reply[0] = 0;
+}
+
+void gdb_loop(void) {
+    int no_reply;
+
+    gdb_mem_access = 0;
+
+    while (1) {
+        get_packet(packet_buf, sizeof(packet_buf) - 1);
+            
+        no_reply = 0;
+        switch (packet_buf[0]) {
+            case '?':
+                reply_signal(gdb_exception_no, reply_buf);
+                break;
+
+            case 'p':
+                cmd_get_register(packet_buf + 1, reply_buf);
+                break;
+
+            case 'P':
+                cmd_set_register(packet_buf + 1, reply_buf);
+                break;
+
+            case 'g':
+                g_reply(reply_buf);
+                break;
+
+            case 'G':
+                cmd_set_registers(packet_buf + 1, reply_buf);
+                break;
+
+            case 'm':
+                cmd_get_memory(packet_buf + 1, reply_buf);
+                break;
+
+            case 'M':
+                cmd_put_memory(packet_buf + 1, reply_buf);
+                break;
+
+            case 'q':
+                cmd_query(packet_buf + 1, reply_buf);
+                break;
+
+            case 'c':
+                cmd_go(packet_buf + 1);
+                reply_error(1, reply_buf);
+                break;
+
+/*             case 's': */
+/*                 cmd_go(packet_buf + 1); */
+/*                 break; */
+
+            default:
+                reply_buf[0] = 0;
+        }
+
+        if (!no_reply)
+            put_packet(reply_buf);
+    }
+}
+
+extern void *vectors[];
+
+static void gdb_set_vector(int n, void *p)
+{
+    vectors[n] = p;
+}
+
+void gdb_und_exc(void);
+void gdb_swi_exc(void);
+void gdb_pabt_exc(void);
+void gdb_dabt_exc(void);
+
+static void gdb_set_vectors(void)
+{
+    gdb_set_vector(VEC_UND, gdb_und_exc);
+    gdb_set_vector(VEC_SWI, gdb_swi_exc);
+    gdb_set_vector(VEC_PABT, gdb_pabt_exc);
+    gdb_set_vector(VEC_DABT, gdb_dabt_exc);
+}
+
+void gdb_loop_from_exc(void)
+{
+    if (gdb_mem_access)
+        reply_error(1, reply_buf);
+    else        
+        reply_signal(gdb_exception_no, reply_buf);
+    put_packet(reply_buf);
+    gdb_loop();
+}
+
+#define GPIO3_CLR  (*(volatile unsigned long *)0x800030d8)
+
+#define IRQ_REG(reg) (*(volatile unsigned long *)(0x80300000 + (reg)))
+
+static inline unsigned long irq_read(int reg)
+{
+    unsigned long v, v2;
+    do
+    {
+        v = IRQ_REG(reg);
+        v2 = IRQ_REG(reg);
+    } while (v != v2);
+    return v;
+}
+
+#define IRQ_WRITE_WAIT(reg, val, cond) \
+    do { unsigned long v, v2; \
+        do { \
+            IRQ_REG(reg) = (val); \
+            v = IRQ_REG(reg); \
+            v2 = IRQ_REG(reg); \
+        } while ((v != v2) || !(cond)); \
+    } while (0);
+
+static void system_init(void)
+{
+    int i;
+
+    /* turn off watchdog */
+    (*(volatile unsigned long *)0x80002804) = 0;
+
+    for (i = 0; i < 0x1c; i++)
+    {
+        IRQ_WRITE_WAIT(0x404 + i * 4, 0x1e000001, (v & 0x3010f) == 1);
+        IRQ_WRITE_WAIT(0x404 + i * 4, 0x4000000, (v & 0x10000) == 0);
+        IRQ_WRITE_WAIT(0x404 + i * 4, 0x10000001, (v & 0xf) == 1);
+    }
+    
+    GPIO3_CLR = 1;
+}
+
+void main(void)
+{
+    system_init();
+    usb_serial_init();
+    gdb_exception_no = VEC_SWI;
+    gdb_set_vectors();
+    gdb_loop();
+}
+
+#define str(s) #s
+#define xstr(s) str(s)
+
+asm (".text\n"
+     "gdb_und_exc:\n"
+     "    ldr    sp, =_stub_stack\n"
+     "    sub    r14, r14, #4\n"
+     "    stmfd  sp!, {r0-r3, r12, r14}\n"
+     "    mov    r0, #" xstr(VEC_UND) "\n"
+     "    b      gdb_handle_exception\n"
+     "gdb_swi_exc:\n"
+     "    ldr    sp, =_stub_stack\n"
+     "    stmfd  sp!, {r0-r3, r12, r14}\n"
+     "    mov    r0, #" xstr(VEC_SWI) "\n"
+     "    b      gdb_handle_exception\n"
+     "gdb_pabt_exc:\n"
+     "    ldr    sp, =_stub_stack\n"
+     "    stmfd  sp!, {r0-r3, r12, r14}\n"
+     "    mov    r0, #" xstr(VEC_PABT) "\n"
+     "    b      gdb_handle_exception\n"
+     "gdb_dabt_exc:\n"
+     "    ldr    sp, =_stub_stack\n"
+     "    sub    r14, r14, #4\n"
+     "    stmfd  sp!, {r0-r3, r12, r14}\n"
+     "    ldr    r0, =gdb_mem_access\n"
+     "    ldr    r0, [r0]\n"
+     "    tst    r0, r0\n"
+     "    bne    gdb_data_abort\n"
+     "    mov    r0, #" xstr(VEC_DABT) "\n"
+     "    b      gdb_handle_exception\n"
+     "gdb_handle_exception:\n"
+     "    ldr    r1, =gdb_exception_no\n"
+     "    str    r0, [r1]\n"
+     "    ldr    r0, =registers\n"
+     "    mrs    r12, spsr\n"
+     "    str    r12, [r0], #4\n"
+     "    ldmfd  sp!, {r2, r3}\n"
+     "    stmia  r0!, {r2, r3}\n"
+     "    ldmfd  sp!, {r2, r3, r12, r14}\n"
+     "    str    r14, [r0, #52]\n"
+     "    stmia  r0!, {r2-r12}\n"
+     "    mrs    r1, spsr\n"
+     "    and    r2, r1, #0x1f\n"
+     "    cmp    r2, #0x10\n"
+     "    bne    1f\n"
+     "    stmia  r0, {r13, r14}^\n"
+     "    b      gdb_data_abort\n"
+     "1:\n"
+     "    msr    cpsr_c, r1\n"
+     "    stmia  r0, {r13, r14}\n"
+     "gdb_data_abort:\n"
+     "    msr    cpsr_c, #0xd3\n"
+     "    ldr    sp, =_stub_stack\n"
+     "    b      gdb_loop_from_exc\n");
diff --git a/gdb/linker.cfg b/gdb/linker.cfg
index 5d5334b..2cc6a0e 100644
--- a/gdb/linker.cfg
+++ b/gdb/linker.cfg
@@ -1,5 +1,65 @@
+#include "config.h"
+
+#ifdef CPU_ARM
+ENTRY(start)
+STARTUP(crt0.o)
+OUTPUT_FORMAT(elf32-littlearm)
+#else
 ENTRY(_start)
 OUTPUT_FORMAT(elf32-sh)
+#endif
+
+#ifdef IRIVER_IFP7XX_SERIES
+MEMORY
+{
+   IRAM : ORIGIN = 0, LENGTH = 0x10000
+   DRAM : ORIGIN = 0x24000000, LENGTH = 0x8000
+}
+
+SECTIONS
+{
+    .text :
+    {
+	*(.init*)
+        *(.text)
+        *(.text*)
+	*(.rodata)
+	*(.rodata*)
+        *(.glue_7)
+        *(.glue_7t)
+    } >DRAM
+
+    .data :
+    {
+        *(.data)
+    } >DRAM
+
+    .vectors :
+    {
+	_vectorsstart = .;
+        *(.vectors)
+        _vectorsend = .;
+    } >IRAM AT>DRAM
+    _vectorscopy = LOADADDR(.vectors);
+
+    .stack (NOLOAD) :
+    {
+	stackbegin = .;
+        . += 0x400;
+	_stub_stack = .;
+	. += 0x200;
+        stackend = .;
+    } >DRAM
+
+    .bss (NOLOAD) :
+    {
+        _edata = .;
+	*(.bss)
+        _end = .;
+    } >DRAM
+}
+#else
+
 SECTIONS
 {
     .vectors 0x09000000 :
@@ -25,4 +85,6 @@
     {
         LONG(0);
     }
-  }
+}
+
+#endif