From 6eb1b09c1cb61bbc51188b14cd98936ad0a880a3 Mon Sep 17 00:00:00 2001 From: Koen De Vleeschauwer Date: Sun, 17 May 2020 20:39:00 +0200 Subject: [PATCH 1/7] pc-hosted semihosting --- src/target/cortexm.c | 268 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 257 insertions(+), 11 deletions(-) diff --git a/src/target/cortexm.c b/src/target/cortexm.c index 4e47a23b..6f273799 100644 --- a/src/target/cortexm.c +++ b/src/target/cortexm.c @@ -26,6 +26,7 @@ * * Also supports Cortex-M0 / ARMv6-M */ + #include "general.h" #include "exception.h" #include "adiv5.h" @@ -37,6 +38,23 @@ #include +#ifdef PC_HOSTED + +/* + * pc-hosted semihosting does keyboard, file and screen i/o on the system + * where blackmagic_hosted runs, using linux system calls. + * semihosting in the probe does keyboard, file and screen i/o on the system + * where gdb runs, using gdb file i/o calls. + */ + +#define TARGET_NULL ((target_addr)0) +#include +#include +#include +#include +#include +#endif + static const char cortexm_driver_str[] = "ARM Cortex-M"; static bool cortexm_vector_catch(target *t, int argc, char *argv[]); @@ -71,9 +89,7 @@ static target_addr cortexm_check_watch(target *t); static int cortexm_hostio_request(target *t); -#if !defined(PC_HOSTED) static uint32_t time0_sec = UINT32_MAX; /* sys_clock time origin */ -#endif struct cortexm_priv { ADIv5_AP_t *ap; @@ -1013,6 +1029,17 @@ static bool cortexm_vector_catch(target *t, int argc, char *argv[]) #endif /* Semihosting support */ + +/* + * If the target wants to read the special filename ":semihosting-features" + * to know what semihosting features are supported, it's easiest to create + * that file on the host in the directory where gdb runs, + * or, if using pc-hosted, where blackmagic_hosted runs. + * + * $ echo -e 'SHFB\x03' > ":semihosting-features" + * $ chmod 0444 ":semihosting-features" + */ + /* ARM Semihosting syscall numbers, from "Semihosting for AArch32 and AArch64 Version 3.0" */ #define SYS_CLOCK 0x10 @@ -1062,6 +1089,7 @@ static void probe_mem_write(target *t __attribute__((unused)), target_addr targe return; } #endif + static int cortexm_hostio_request(target *t) { uint32_t arm_regs[t->regs_size]; @@ -1076,6 +1104,227 @@ static int cortexm_hostio_request(target *t) DEBUG("syscall 0"PRIx32"%"PRIx32" (%"PRIx32" %"PRIx32" %"PRIx32" %"PRIx32")\n", syscall, params[0], params[1], params[2], params[3]); switch (syscall) { +#if defined(PC_HOSTED) + + /* code that runs in pc-hosted process. use linux system calls. */ + + case SYS_OPEN:{ /* open */ + target_addr fnam_taddr = params[0]; + uint32_t fnam_len = params[2]; + ret = -1; + if ((fnam_taddr == TARGET_NULL) || (fnam_len == 0)) break; + + /* Translate stupid fopen modes to open flags. + * See DUI0471C, Table 8-3 */ + const uint32_t flags[] = { + O_RDONLY, /* r, rb */ + O_RDWR, /* r+, r+b */ + O_WRONLY | O_CREAT | O_TRUNC,/*w*/ + O_RDWR | O_CREAT | O_TRUNC,/*w+*/ + O_WRONLY | O_CREAT | O_APPEND,/*a*/ + O_RDWR | O_CREAT | O_APPEND,/*a+*/ + }; + uint32_t pflag = flags[params[1] >> 1]; + char filename[4]; + + target_mem_read(t, filename, fnam_taddr, sizeof(filename)); + /* handle requests for console i/o */ + if (!strcmp(filename, ":tt")) { + if (pflag == TARGET_O_RDONLY) + ret = STDIN_FILENO; + else if (pflag & TARGET_O_TRUNC) + ret = STDOUT_FILENO; + else + ret = STDERR_FILENO; + ret++; + break; + } + + char *fnam = malloc(fnam_len + 1); + if (fnam == NULL) break; + target_mem_read(t, fnam, fnam_taddr, fnam_len + 1); + if (target_check_error(t)) {free(fnam); break;} + fnam[fnam_len]='\0'; + ret = open(fnam, pflag, 0644); + free(fnam); + if (ret != -1) + ret++; + break; + } + + case SYS_CLOSE: /* close */ + ret = close(params[0] - 1); + break; + + case SYS_READ: { /* read */ + ret = -1; + target_addr buf_taddr = params[1]; + uint32_t buf_len = params[2]; + if (buf_taddr == TARGET_NULL) break; + if (buf_len == 0) {ret = 0; break;} + uint8_t *buf = malloc(buf_len); + if (buf == NULL) break; + ssize_t rc = read(params[0] - 1, buf, buf_len); + if (rc >= 0) + rc = buf_len - rc; + target_mem_write(t, buf_taddr, buf, buf_len); + free(buf); + if (target_check_error(t)) break; + ret = rc; + break; + } + + case SYS_WRITE: { /* write */ + ret = -1; + target_addr buf_taddr = params[1]; + uint32_t buf_len = params[2]; + if (buf_taddr == TARGET_NULL) break; + if (buf_len == 0) {ret = 0; break;} + uint8_t *buf = malloc(buf_len); + if (buf == NULL) break; + target_mem_read(t, buf, buf_taddr, buf_len); + if (target_check_error(t)) {free(buf); break;} + ret = write(params[0] - 1, buf, buf_len); + free(buf); + if (ret >= 0) + ret = buf_len - ret; + break; + } + + case SYS_WRITEC: { /* writec */ + ret = -1; + uint8_t ch; + target_addr ch_taddr = arm_regs[1]; + if (ch_taddr == TARGET_NULL) break; + ch = target_mem_read8(t, ch_taddr); + if (target_check_error(t)) break; + fputc(ch, stderr); + ret = 0; + break; + } + + case SYS_WRITE0:{ /* write0 */ + ret = -1; + uint8_t ch; + target_addr str = arm_regs[1]; + if (str == TARGET_NULL) break; + while ((ch = target_mem_read8(t, str++)) != '\0') { + if (target_check_error(t)) break; + fputc(ch, stderr); + } + ret = 0; + break; + } + + case SYS_ISTTY: /* isatty */ + ret = isatty(params[0] - 1); + break; + + case SYS_SEEK:{ /* lseek */ + off_t pos = params[1]; + if (lseek(params[0] - 1, pos, SEEK_SET) == (off_t)pos) ret = 0; + else ret = -1; + break; + } + + case SYS_RENAME: { /* rename */ + ret = -1; + target_addr fnam1_taddr = params[0]; + uint32_t fnam1_len = params[1]; + if (fnam1_taddr == TARGET_NULL) break; + if (fnam1_len == 0) break; + target_addr fnam2_taddr = params[2]; + uint32_t fnam2_len = params[3]; + if (fnam2_taddr == TARGET_NULL) break; + if (fnam2_len == 0) break; + char *fnam1 = malloc(fnam1_len + 1); + if (fnam1 == NULL) break; + target_mem_read(t, fnam1, fnam1_taddr, fnam1_len + 1); + if (target_check_error(t)) {free(fnam1); break;} + fnam1[fnam1_len]='\0'; + char *fnam2 = malloc(fnam2_len + 1); + if (fnam2 == NULL) {free(fnam1); break;} + target_mem_read(t, fnam2, fnam2_taddr, fnam2_len + 1); + if (target_check_error(t)) {free(fnam1); free(fnam2); break;} + fnam2[fnam2_len]='\0'; + ret = rename(fnam1, fnam2); + free(fnam1); + free(fnam2); + break; + } + + case SYS_REMOVE: { /* unlink */ + ret = -1; + target_addr fnam_taddr = params[0]; + if (fnam_taddr == TARGET_NULL) break; + uint32_t fnam_len = params[1]; + if (fnam_len == 0) break; + char *fnam = malloc(fnam_len + 1); + if (fnam == NULL) break; + target_mem_read(t, fnam, fnam_taddr, fnam_len + 1); + if (target_check_error(t)) {free(fnam); break;} + fnam[fnam_len]='\0'; + ret = remove(fnam); + free(fnam); + break; + } + + case SYS_SYSTEM: { /* system */ + ret = -1; + target_addr cmd_taddr = params[0]; + if (cmd_taddr == TARGET_NULL) break; + uint32_t cmd_len = params[1]; + if (cmd_len == 0) break; + char *cmd = malloc(cmd_len + 1); + if (cmd == NULL) break; + target_mem_read(t, cmd, cmd_taddr, cmd_len + 1); + if (target_check_error(t)) {free(cmd); break;} + cmd[cmd_len]='\0'; + ret = system(cmd); + free(cmd); + break; + } + + case SYS_FLEN: { /* file length */ + ret = -1; + struct stat stat_buf; + if (fstat(params[0]-1, &stat_buf) != 0) break; + if (stat_buf.st_size > INT32_MAX) break; + ret = stat_buf.st_size; + break; + } + + case SYS_CLOCK: { /* clock */ + /* can't use clock() because that would give cpu time of pc-hosted process */ + ret = -1; + struct timeval timeval_buf; + if(gettimeofday(&timeval_buf, NULL) != 0) break; + uint32_t sec = timeval_buf.tv_sec; + uint64_t usec = timeval_buf.tv_usec; + if (time0_sec > sec) time0_sec = sec; + sec -= time0_sec; + uint64_t csec64 = (sec * 1000000ull + usec)/10000ull; + uint32_t csec = csec64 & 0x7fffffff; + ret = csec; + break; + } + + case SYS_TIME: /* time */ + ret = time(NULL); + break; + + case SYS_READC: /* readc */ + ret = getchar(); + break; + + case SYS_ERRNO: /* errno */ + ret = errno; + break; + +#else + + /* code that runs in probe. use gdb fileio calls. */ + case SYS_OPEN:{ /* open */ /* Translate stupid fopen modes to open flags. * See DUI0471C, Table 8-3 */ @@ -1102,13 +1351,13 @@ static int cortexm_hostio_request(target *t) ret++; break; } - /* FIXME handle requests for special filename ':semihosting-features' */ ret = tc_open(t, params[0], params[2] + 1, pflag, 0644); if (ret != -1) ret++; break; } + case SYS_CLOSE: /* close */ ret = tc_close(t, params[0] - 1); break; @@ -1161,10 +1410,6 @@ static int cortexm_hostio_request(target *t) break; case SYS_FLEN: -#if defined(PC_HOSTED) - t->tc->errno_ = 0; - break; -#else { /* file length */ ret = -1; uint32_t fio_stat[16]; /* same size as fio_stat in gdb/include/gdb/fileio.h */ @@ -1181,8 +1426,9 @@ static int cortexm_hostio_request(target *t) if (rc) break; /* tc_fstat() failed */ uint32_t fst_size_msw = fio_stat[7]; /* most significant 32 bits of fst_size in fio_stat */ uint32_t fst_size_lsw = fio_stat[8]; /* least significant 32 bits of fst_size in fio_stat */ - if (fst_size_msw != 0) break; /* file size too large for uint32_t return type */ + if (fst_size_msw != 0) break; /* file size too large for int32_t return type */ ret = __builtin_bswap32(fst_size_lsw); /* convert from bigendian to target order */ + if (ret < 0) ret = -1; /* file size too large for int32_t return type */ break; } @@ -1235,10 +1481,11 @@ static int cortexm_hostio_request(target *t) else ret = -1; break; } -#endif + case SYS_ERRNO: /* Return last errno from GDB */ ret = t->tc->errno_; break; +#endif case SYS_EXIT: /* _exit() */ tc_printf(t, "_exit(0x%x)\n", params[0]); @@ -1296,8 +1543,7 @@ static int cortexm_hostio_request(target *t) break; case SYS_TMPNAM: { /* tmpnam */ - /* Given a target identifier between 0 and 255, returns a temporary name. - * FIXME: add directory prefix */ + /* Given a target identifier between 0 and 255, returns a temporary name */ target_addr buf_ptr = params[0]; int target_id = params[1]; int buf_size = params[2]; From 34c0d5a1c9093918a996b8d2e91205cd41a438d9 Mon Sep 17 00:00:00 2001 From: Mark Rages Date: Fri, 29 May 2020 17:11:15 -0500 Subject: [PATCH 2/7] When timeout is 0, `timeout_is_expired()` should return immediately. --- src/platforms/common/timing.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/platforms/common/timing.c b/src/platforms/common/timing.c index 9087ba74..1717bf5a 100644 --- a/src/platforms/common/timing.c +++ b/src/platforms/common/timing.c @@ -26,6 +26,5 @@ void platform_timeout_set(platform_timeout *t, uint32_t ms) bool platform_timeout_is_expired(platform_timeout *t) { - return platform_time_ms() > t->time; + return platform_time_ms() >= t->time; } - From f9f928e9d6ed9c1633480698cd1471bbab7fbd7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20B=C3=A9n=C3=A9teau?= Date: Tue, 2 Jun 2020 14:44:03 +1000 Subject: [PATCH 3/7] Add support for LPC8N04 --- src/target/lpc11xx.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/target/lpc11xx.c b/src/target/lpc11xx.c index f93130ef..02c631db 100644 --- a/src/target/lpc11xx.c +++ b/src/target/lpc11xx.c @@ -129,6 +129,12 @@ lpc11xx_probe(target *t) target_add_ram(t, 0x10000000, 0x2000); lpc11xx_add_flash(t, 0x00000000, 0x8000, 0x1000); return true; + case 0x00008A04: /* LPC8N04 (see UM11074 Rev.1.3 section 4.5.19) */ + t->driver = "LPC8N04"; + target_add_ram(t, 0x10000000, 0x2000); + lpc11xx_add_flash(t, 0x00000000, 0x8000, 0x400); + target_add_commands(t, lpc11xx_cmd_list, "LPC8N04"); + return true; } if (idcode) { DEBUG("LPC11xx: Unknown IDCODE 0x%08" PRIx32 "\n", idcode); From 69e330849dc17c5dea996780ad1b773d53f46423 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=B6m=C3=B6t=C3=B6r=20Guly=C3=A1s?= Date: Wed, 3 Jun 2020 20:36:32 -0400 Subject: [PATCH 4/7] fix flash map for STM32G431, as it is a special case different from the STM32G47x and STM32G48x chips --- src/target/stm32l4.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/target/stm32l4.c b/src/target/stm32l4.c index 63e1e8d8..bee108c5 100644 --- a/src/target/stm32l4.c +++ b/src/target/stm32l4.c @@ -326,13 +326,22 @@ static bool stm32l4_attach(target *t) } else stm32l4_add_flash(t, 0x08000000, 0x00200000, 0x2000, -1); } else if (chip->family == FAM_STM32G4xx) { - if (options & OR_DBANK) { - uint32_t banksize = size << 9; - stm32l4_add_flash(t, 0x08000000 , banksize, 0x0800, 0x08000000 + banksize); - stm32l4_add_flash(t, 0x08000000 + banksize, banksize, 0x0800, 0x08000000 + banksize); - } else { + // RM0440 describes G43x as Category 2, G47x/G48x as Category 3 devices + // Cat 2 is always 128k with 2k pages, single bank + // Cat 3 is dual bank with an option bit to choose single 512k bank with 4k pages or dual bank as 2x256k with 2k pages + if (chip->idcode == ID_STM32G43) { uint32_t banksize = size << 10; - stm32l4_add_flash(t, 0x08000000 , banksize, 0x1000, -1); + stm32l4_add_flash(t, 0x08000000, banksize, 0x0800, -1); + } + else { + if (options & OR_DBANK) { + uint32_t banksize = size << 9; + stm32l4_add_flash(t, 0x08000000 , banksize, 0x0800, 0x08000000 + banksize); + stm32l4_add_flash(t, 0x08000000 + banksize, banksize, 0x0800, 0x08000000 + banksize); + } else { + uint32_t banksize = size << 10; + stm32l4_add_flash(t, 0x08000000 , banksize, 0x1000, -1); + } } } else if (chip->flags & DUAL_BANK) { if (options & OR_DUALBANK) { From 34a13723d86cdf4f75182d751149445f24bfa07f Mon Sep 17 00:00:00 2001 From: Stoyan Shopov Date: Tue, 2 Jun 2020 21:27:13 +0300 Subject: [PATCH 5/7] Decrease the control USB pipe size on f103 blackmagic probes to 8 bytes Rationale: In USB device mode, the f103 chip provides 512 bytes of memory for USB traffic. In the 'master' branch of the blackmagic probe, the control endpoint pipe size is set to 64 bytes, effectively consuming a quarter (1 'in' endpoint, 1 'out' endpoint == 2 endpoints, 2 /* endpoints */ x 64 /* bytes per endpoint */ == 128 bytes out of 512 /* bytes of precious usb packet memory */). The USB standard, for full speed devices, does allow a control endpoint size of 8 bytes. I am not too aware of all the details of the USB standard, but the USB standard seems to allow fragmented transactions on the control USB pipe (endpoint 0), which libopencm3 apparently supports: libopencm3/lib/usb/usb_control.c:usb_control_send_chunk() I am using this change (from 64, to 8 bytes) on a windows 10 machine, on an stlink hardware ('PROBE_HOST=stlink'). It works on my machine. This change can potentially provide other USB endpoints in the blackmagic firmware with more memory --- src/platforms/common/cdcacm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platforms/common/cdcacm.c b/src/platforms/common/cdcacm.c index a063d31a..f3df8fc9 100644 --- a/src/platforms/common/cdcacm.c +++ b/src/platforms/common/cdcacm.c @@ -58,7 +58,7 @@ static const struct usb_device_descriptor dev = { .bDeviceClass = 0xEF, /* Miscellaneous Device */ .bDeviceSubClass = 2, /* Common Class */ .bDeviceProtocol = 1, /* Interface Association */ - .bMaxPacketSize0 = 64, + .bMaxPacketSize0 = 8, .idVendor = 0x1D50, .idProduct = 0x6018, .bcdDevice = 0x0100, From b8b34e7b1dc6bf6125e9ccda4cad304dbb86ec68 Mon Sep 17 00:00:00 2001 From: Uwe Bonnes Date: Wed, 29 Apr 2020 16:07:16 +0200 Subject: [PATCH 6/7] adiv5: remove cfg for AP structure, cfg is only used local. --- src/target/adiv5.c | 6 ++++-- src/target/adiv5.h | 1 - 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/target/adiv5.c b/src/target/adiv5.c index 7cccffa6..034b48a8 100644 --- a/src/target/adiv5.c +++ b/src/target/adiv5.c @@ -422,7 +422,6 @@ ADIv5_AP_t *adiv5_new_ap(ADIv5_DP_t *dp, uint8_t apsel) memcpy(ap, &tmpap, sizeof(*ap)); adiv5_dp_ref(dp); - ap->cfg = adiv5_ap_read(ap, ADIV5_AP_CFG); ap->base = adiv5_ap_read(ap, ADIV5_AP_BASE); ap->csw = adiv5_ap_read(ap, ADIV5_AP_CSW) & ~(ADIV5_AP_CSW_SIZE_MASK | ADIV5_AP_CSW_ADDRINC_MASK); @@ -432,8 +431,11 @@ ADIv5_AP_t *adiv5_new_ap(ADIv5_DP_t *dp, uint8_t apsel) ap->csw &= ~ADIV5_AP_CSW_TRINPROG; } +#if defined(ENABLE_DEBUG) + uint32_t cfg = adiv5_ap_read(ap, ADIV5_AP_CFG); DEBUG("AP %3d: IDR=%08"PRIx32" CFG=%08"PRIx32" BASE=%08"PRIx32" CSW=%08"PRIx32"\n", - apsel, ap->idr, ap->cfg, ap->base, ap->csw); + apsel, ap->idr, cfg, ap->base, ap->csw); +#endif return ap; } diff --git a/src/target/adiv5.h b/src/target/adiv5.h index 60d89333..79b084eb 100644 --- a/src/target/adiv5.h +++ b/src/target/adiv5.h @@ -177,7 +177,6 @@ typedef struct ADIv5_AP_s { uint8_t apsel; uint32_t idr; - uint32_t cfg; uint32_t base; uint32_t csw; } ADIv5_AP_t; From 16967b43288028dcdc2759bb2a25a53472571162 Mon Sep 17 00:00:00 2001 From: Uwe Bonnes Date: Wed, 29 Apr 2020 16:13:26 +0200 Subject: [PATCH 7/7] adiv5: Remove only local dp_idcode used from ADIv5_DP_t struct. --- src/target/adiv5.c | 4 ++-- src/target/adiv5.h | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/target/adiv5.c b/src/target/adiv5.c index 034b48a8..778cd0c1 100644 --- a/src/target/adiv5.c +++ b/src/target/adiv5.c @@ -492,8 +492,8 @@ void adiv5_dp_init(ADIv5_DP_t *dp) ); DEBUG("RESET_SEQ %s\n", (platform_timeout_is_expired(&timeout)) ? "failed": "succeeded"); - dp->dp_idcode = adiv5_dp_read(dp, ADIV5_DP_IDCODE); - if ((dp->dp_idcode & ADIV5_DP_VERSION_MASK) == ADIV5_DPv2) { + uint32_t dp_idcode = adiv5_dp_read(dp, ADIV5_DP_IDCODE); + if ((dp_idcode & ADIV5_DP_VERSION_MASK) == ADIV5_DPv2) { /* Read TargetID. Can be done with device in WFI, sleep or reset!*/ adiv5_dp_write(dp, ADIV5_DP_SELECT, ADIV5_DP_BANK2); dp->targetid = adiv5_dp_read(dp, ADIV5_DP_CTRLSTAT); diff --git a/src/target/adiv5.h b/src/target/adiv5.h index 79b084eb..c72a7a42 100644 --- a/src/target/adiv5.h +++ b/src/target/adiv5.h @@ -134,7 +134,6 @@ typedef struct ADIv5_DP_s { int refcnt; uint32_t idcode; - uint32_t dp_idcode; /* Contains DPvX revision*/ uint32_t targetid; /* Contains IDCODE for DPv2 devices.*/ uint32_t (*dp_read)(struct ADIv5_DP_s *dp, uint16_t addr);