From 5020d1f05db920cd0633982016c0b1978a310d20 Mon Sep 17 00:00:00 2001 From: Gareth McMullin Date: Sun, 14 Apr 2013 19:47:11 -0700 Subject: [PATCH] Move semihosting support to cortexm.c. Try to implement more syscalls. --- src/cortexm.c | 159 +++++++++++++++++++++++++++++++++++++++++++ src/gdb_main.c | 57 +++------------- src/include/target.h | 7 ++ 3 files changed, 175 insertions(+), 48 deletions(-) diff --git a/src/cortexm.c b/src/cortexm.c index 36319eef..179fb363 100644 --- a/src/cortexm.c +++ b/src/cortexm.c @@ -204,6 +204,9 @@ static int cortexm_check_hw_wp(struct target_s *target, uint32_t *addr); #define CORTEXM_MAX_WATCHPOINTS 4 /* architecture says up to 15, no implementation has > 4 */ #define CORTEXM_MAX_BREAKPOINTS 6 /* architecture says up to 127, no implementation has > 6 */ +static int cortexm_hostio_request(target *t); +static void cortexm_hostio_reply(target *t, int32_t retcode, uint32_t errcode); + struct cortexm_priv { bool stepping; bool on_bkpt; @@ -219,6 +222,10 @@ struct cortexm_priv { unsigned hw_breakpoint_max; /* Copy of DEMCR for vector-catch */ uint32_t demcr; + /* Semihosting state */ + uint32_t syscall; + uint32_t errno; + uint32_t byte_count; }; /* Register number tables */ @@ -335,6 +342,8 @@ cortexm_probe(struct target_s *target) target->halt_resume = cortexm_halt_resume; target->regs_size = sizeof(regnum_cortex_m); + target->hostio_reply = cortexm_hostio_reply; + target_add_commands(target, cortexm_cmd_list, cortexm_driver_str); /* Probe for FP extension */ @@ -595,6 +604,23 @@ cortexm_halt_wait(struct target_s *target) /* Remember if we stopped on a breakpoint */ priv->on_bkpt = dfsr & (CORTEXM_DFSR_BKPT); + if (priv->on_bkpt) { + /* If we've hit a programmed breakpoint, check for semihosting + * call. */ + uint32_t pc = cortexm_pc_read(target); + uint16_t bkpt_instr; + target_mem_read_bytes(target, (uint8_t *)&bkpt_instr, pc, 2); + if (bkpt_instr == 0xBEAB) { + int n = cortexm_hostio_request(target); + if (n > 0) { + target_halt_resume(target, priv->stepping); + return 0; + } else if (n < 0) { + return -1; + } + } + } + if (dfsr & (CORTEXM_DFSR_BKPT | CORTEXM_DFSR_DWTTRAP)) return SIGTRAP; @@ -861,3 +887,136 @@ static bool cortexm_vector_catch(target *t, int argc, char *argv[]) return true; } + +/* Semihosting support */ +/* ARM Semihosting syscall numbers, from ARM doc DUI0471C, Chapter 8 */ +#define SYS_CLOSE 0x02 +#define SYS_CLOCK 0x10 +#define SYS_ELAPSED 0x30 +#define SYS_ERRNO 0x13 +#define SYS_FLEN 0x0C +#define SYS_GET_CMDLINE 0x15 +#define SYS_HEAPINFO 0x16 +#define SYS_ISERROR 0x08 +#define SYS_ISTTY 0x09 +#define SYS_OPEN 0x01 +#define SYS_READ 0x06 +#define SYS_READC 0x07 +#define SYS_REMOVE 0x0E +#define SYS_RENAME 0x0F +#define SYS_SEEK 0x0A +#define SYS_SYSTEM 0x12 +#define SYS_TICKFREQ 0x31 +#define SYS_TIME 0x11 +#define SYS_TMPNAM 0x0D +#define SYS_WRITE 0x05 +#define SYS_WRITEC 0x03 +#define SYS_WRITE0 0x04 + +#define FILEIO_O_RDONLY 0 +#define FILEIO_O_WRONLY 1 +#define FILEIO_O_RDWR 2 +#define FILEIO_O_APPEND 0x008 +#define FILEIO_O_CREAT 0x200 +#define FILEIO_O_TRUNC 0x400 + +#define FILEIO_SEEK_SET 0 +#define FILEIO_SEEK_CUR 1 +#define FILEIO_SEEK_END 2 + +static int cortexm_hostio_request(target *t) +{ + ADIv5_AP_t *ap = adiv5_target_ap(t); + struct cortexm_priv *priv = ap->priv; + uint32_t arm_regs[t->regs_size]; + uint32_t params[4]; + + target_regs_read(t, arm_regs); + target_mem_read_words(t, params, arm_regs[1], sizeof(params)); + priv->syscall = arm_regs[0]; + + DEBUG("syscall 0x%x (%x %x %x %x)\n", priv->syscall, + params[0], params[1], params[2], params[3]); + switch (priv->syscall) { + case SYS_OPEN:{ /* open */ + /* Translate stupid fopen modes to open flags. + * See DUI0471C, Table 8-3 */ + const uint32_t flags[] = { + FILEIO_O_RDONLY, /* r, rb */ + FILEIO_O_RDWR, /* r+, r+b */ + FILEIO_O_WRONLY | FILEIO_O_CREAT | FILEIO_O_TRUNC,/*w*/ + FILEIO_O_RDWR | FILEIO_O_CREAT | FILEIO_O_TRUNC,/*w+*/ + FILEIO_O_WRONLY | FILEIO_O_CREAT | FILEIO_O_APPEND,/*a*/ + FILEIO_O_RDWR | FILEIO_O_CREAT | FILEIO_O_APPEND,/*a+*/ + }; + gdb_putpacket_f("Fopen,%08X/%X,%08X,%08X", + params[0], params[2] + 1, + flags[params[1] >> 1], 0644); + break; + } + case SYS_CLOSE: /* close */ + gdb_putpacket_f("Fclose,%08X", params[0]); + break; + case SYS_READ: /* read */ + priv->byte_count = params[2]; + gdb_putpacket_f("Fread,%08X,%08X,%08X", + params[0], params[1], params[2]); + break; + case SYS_WRITE: /* write */ + priv->byte_count = params[2]; + gdb_putpacket_f("Fwrite,%08X,%08X,%08X", + params[0], params[1], params[2]); + break; + case SYS_ISTTY: /* isatty */ + gdb_putpacket_f("Fisatty,%08X", params[0]); + break; + case SYS_SEEK: /* lseek */ + gdb_putpacket_f("Flseek,%08X,%08X,%08X", + params[0], params[1], FILEIO_SEEK_SET); + break; + case SYS_RENAME:/* rename */ + gdb_putpacket_f("Frename,%08X/%X,%08X/%X", + params[0], params[1] + 1, + params[2], params[3] + 1); + break; + case SYS_REMOVE:/* unlink */ + gdb_putpacket_f("Funlink,%08X/%X", params[0], params[1] + 1); + break; + case SYS_SYSTEM:/* system */ + gdb_putpacket_f("Fsystem,%08X/%X", params[0], params[1] + 1); + break; + + case SYS_FLEN: /* Not supported, fake success */ + priv->errno = 0; + return 1; + + case SYS_ERRNO: /* Return last errno from GDB */ + arm_regs[0] = priv->errno; + target_regs_write(t, arm_regs); + return 1; + + case SYS_TIME: /* gettimeofday */ + /* FIXME How do we use gdb's gettimeofday? */ + default: + return 0; + } + + return -1; +} + +static void cortexm_hostio_reply(target *t, int32_t retcode, uint32_t errcode) +{ + ADIv5_AP_t *ap = adiv5_target_ap(t); + struct cortexm_priv *priv = ap->priv; + uint32_t arm_regs[t->regs_size]; + + DEBUG("syscall return ret=%d errno=%d\n", retcode, errcode); + target_regs_read(t, arm_regs); + if (((priv->syscall == SYS_READ) || (priv->syscall == SYS_WRITE)) && + (retcode > 0)) + retcode = priv->byte_count - retcode; + arm_regs[0] = retcode; + target_regs_write(t, arm_regs); + priv->errno = errcode; +} + diff --git a/src/gdb_main.c b/src/gdb_main.c index 887b9acb..69d5ed13 100644 --- a/src/gdb_main.c +++ b/src/gdb_main.c @@ -76,7 +76,6 @@ gdb_main(void) int size; bool single_step = false; char last_activity = 0; - uint32_t semihost_read_bytes = 0; DEBUG("Entring GDB protocol main loop\n"); /* GDB protocol main loop */ @@ -161,6 +160,7 @@ gdb_main(void) break; } + last_activity = pbuf[0]; /* Wait for target halt */ while(!(sig = target_halt_wait(cur_target))) { unsigned char c = gdb_if_getchar_to(0); @@ -171,42 +171,9 @@ gdb_main(void) } SET_RUN_STATE(0); - uint32_t arm_regs[cur_target->regs_size]; - uint32_t semihost_buf[4]; - target_regs_read(cur_target, arm_regs); - target_mem_read_bytes(cur_target, (uint8_t *)semihost_buf, arm_regs[15], 2); - /* Is this a semihosting breakpoint? */ - if ((semihost_buf[0] & 0xFFFF) == 0xBEAB) { - last_activity = pbuf[0]; - semihost_read_bytes = 0; - target_mem_read_words(cur_target, semihost_buf, arm_regs[1], sizeof(semihost_buf)); - - switch (arm_regs[0]) { - case 0x09: /* SYS_ISTTY */ - arm_regs[0] = 1; /* it's a tty */ - target_regs_write(cur_target, arm_regs); - /* fall-through */ - case 0x01: /* SYS_OPEN */ - case 0x0C: /* SYS_FLEN */ - /* pretend it's successful, r0 is non-zero already */ - goto continue_activity; - case 0x13: /* SYS_ERRNO */ - arm_regs[0] = 4; /* EINTR */ - target_regs_write(cur_target, arm_regs); - goto continue_activity; - case 0x05: /* SYS_WRITE */ - gdb_putpacket_f("Fwrite,1,%08X,%08X", - semihost_buf[1], semihost_buf[2]); - arm_regs[0] = 0; /* pretend it's always successful */ - target_regs_write(cur_target, arm_regs); - continue; - case 0x06: /* SYS_READ */ - gdb_putpacket_f("Fread,0,%08X,%08X", - semihost_buf[1], semihost_buf[2]); - semihost_read_bytes = semihost_buf[2]; - continue; - } - } + /* Negative signal indicates we're in a syscall */ + if (sig < 0) + break; /* Report reason for halt */ if(target_check_hw_wp(cur_target, &watch_addr)) { @@ -218,23 +185,17 @@ gdb_main(void) break; } case 'F': { /* Semihosting call finished */ - int bytes, errcode, items; + int retcode, errcode, items; char c, *p; if (pbuf[1] == '-') p = &pbuf[2]; else p = &pbuf[1]; - items = sscanf(p, "%x,%x,%c", &bytes, &errcode, &c); + items = sscanf(p, "%x,%x,%c", &retcode, &errcode, &c); + if (pbuf[1] == '-') + retcode = -retcode; - if (semihost_read_bytes) { - uint32_t arm_regs[cur_target->regs_size]; - target_regs_read(cur_target, arm_regs); - if (items == 3 && c == 'C') - arm_regs[0] = -1; - else - arm_regs[0] = semihost_read_bytes - bytes; - target_regs_write(cur_target, arm_regs); - } + target_hostio_reply(cur_target, retcode, errcode); /* if break is requested */ if (items == 3 && c == 'C') { diff --git a/src/include/target.h b/src/include/target.h index cd3079c2..b83a2e9f 100644 --- a/src/include/target.h +++ b/src/include/target.h @@ -116,6 +116,10 @@ target *target_attach(target *t, target_destroy_callback destroy_cb); #define target_flash_write(target, dest, src, len) \ (target)->flash_write((target), (dest), (src), (len)) +/* Host I/O */ +#define target_hostio_reply(target, recode, errcode) \ + (target)->hostio_reply((target), (retcode), (errcode)) + struct target_s { /* Notify controlling debugger if target is lost */ @@ -171,6 +175,9 @@ struct target_s { int (*flash_write)(struct target_s *target, uint32_t dest, const uint8_t *src, int len); + /* Host I/O support */ + void (*hostio_reply)(target *t, int32_t retcode, uint32_t errcode); + const char *driver; struct target_command_s *commands;