From fcafc6bb716116059170182aca278a37a7e28785 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 8 Aug 2023 17:13:50 +0100 Subject: [PATCH 01/50] adiv5: Defined a proper Cortex-R architecture type and corrected the Cortex-R4 debug component entry --- src/target/adiv5.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/target/adiv5.c b/src/target/adiv5.c index d567161c7d2..71a4d91fd7e 100644 --- a/src/target/adiv5.c +++ b/src/target/adiv5.c @@ -122,6 +122,7 @@ typedef enum arm_arch { aa_nosupport, aa_cortexm, aa_cortexa, + aa_cortexr, aa_end } arm_arch_e; @@ -243,7 +244,7 @@ static const struct { {0xc08, 0x00, 0, aa_cortexa, cidc_dc, ARM_COMPONENT_STR("Cortex-A8 Debug", "(Debug Unit)")}, {0xc09, 0x00, 0, aa_cortexa, cidc_dc, ARM_COMPONENT_STR("Cortex-A9 Debug", "(Debug Unit)")}, {0xc0f, 0x00, 0, aa_nosupport, cidc_unknown, ARM_COMPONENT_STR("Cortex-A15 Debug", "(Debug Unit)")}, /* support? */ - {0xc14, 0x00, 0, aa_nosupport, cidc_unknown, ARM_COMPONENT_STR("Cortex-R4 Debug", "(Debug Unit)")}, /* support? */ + {0xc14, 0x15, 0, aa_cortexr, cidc_unknown, ARM_COMPONENT_STR("Cortex-R4", "(Debug Unit)")}, {0xcd0, 0x00, 0, aa_nosupport, cidc_unknown, ARM_COMPONENT_STR("Atmel DSU", "(Device Service Unit)")}, {0xd20, 0x00, 0x2a04, aa_cortexm, cidc_gipc, ARM_COMPONENT_STR("Cortex-M23", "(System Control Space)")}, {0xd20, 0x11, 0, aa_nosupport, cidc_dc, ARM_COMPONENT_STR("Cortex-M23", "(Trace Port Interface Unit)")}, @@ -687,6 +688,9 @@ static void adiv5_component_probe( DEBUG_INFO("%s-> cortexa_probe\n", indent + 1); cortexa_probe(ap, addr); break; + case aa_cortexr: + DEBUG_INFO("%s-> cortexr_probe\n", indent + 1); + break; default: break; } From d8cad9d1cf164c3cf6521752fd85e4b87bead2ba Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 8 Aug 2023 18:57:23 +0100 Subject: [PATCH 02/50] cortexr: Began implementing a probe routine for detecting and handling ARM Cortex-R cores --- src/Makefile | 8 +++- src/platforms/hosted/Makefile.inc | 2 + src/target/adiv5.c | 1 + src/target/cortexr.c | 77 +++++++++++++++++++++++++++++++ src/target/target_probe.c | 22 ++++----- src/target/target_probe.h | 4 +- 6 files changed, 100 insertions(+), 14 deletions(-) create mode 100644 src/target/cortexr.c diff --git a/src/Makefile b/src/Makefile index 0e096d18540..85f35d3c4bc 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,7 +1,8 @@ PROBE_HOST ?= native PLATFORM_DIR = platforms/$(PROBE_HOST) VPATH += $(PLATFORM_DIR) target -ENABLE_DEBUG ?= +ENABLE_DEBUG ?= 0 +ENABLE_CORTEXR ?= 0 SYS = $(shell $(CC) -dumpmachine) @@ -89,6 +90,11 @@ ifeq (,$(filter all_platforms,$(MAKECMDGOALS))) include $(PLATFORM_DIR)/Makefile.inc endif +ifeq ($(ENABLE_CORTEXR), 1) +CFLAGS += -DENABLE_CORTEXR +SRC += cortexr.c +endif + ifneq ($(PC_HOSTED),1) # Output memory usage information LDFLAGS += -Wl,--print-memory-usage diff --git a/src/platforms/hosted/Makefile.inc b/src/platforms/hosted/Makefile.inc index b02cb721b18..f73cc319af7 100644 --- a/src/platforms/hosted/Makefile.inc +++ b/src/platforms/hosted/Makefile.inc @@ -5,6 +5,8 @@ SYS := $(shell $(CC) -dumpmachine) CFLAGS += -DENABLE_DEBUG -DPLATFORM_HAS_DEBUG CFLAGS +=-I ./target +ENABLE_CORTEXR := 1 + # Clang requires some special handling here: -gnu means MinGW # while -msvc means Clang/CL. We don't currently support the latter # and we need the former to fit the formula of `x86_64-w64-mingw32` diff --git a/src/target/adiv5.c b/src/target/adiv5.c index 71a4d91fd7e..9180600b8ba 100644 --- a/src/target/adiv5.c +++ b/src/target/adiv5.c @@ -690,6 +690,7 @@ static void adiv5_component_probe( break; case aa_cortexr: DEBUG_INFO("%s-> cortexr_probe\n", indent + 1); + cortexr_probe(ap, addr); break; default: break; diff --git a/src/target/cortexr.c b/src/target/cortexr.c new file mode 100644 index 00000000000..0fb0294a839 --- /dev/null +++ b/src/target/cortexr.c @@ -0,0 +1,77 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2023 1BitSquared + * Written by Rachel Mant + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "general.h" +#include "adiv5.h" +#include "target.h" +#include "target_internal.h" +#include "target_probe.h" +#include "cortex_internal.h" + +typedef struct cortexr_priv { + /* Base core information */ + cortex_priv_s base; +} cortexr_priv_s; + +bool cortexr_probe(adiv5_access_port_s *const ap, const target_addr_t base_address) +{ + target_s *target = target_new(); + if (!target) + return false; + + adiv5_ap_ref(ap); + if (ap->dp->version >= 2 && ap->dp->target_designer_code != 0) { + /* Use TARGETID register to identify target */ + target->designer_code = ap->dp->target_designer_code; + target->part_id = ap->dp->target_partno; + } else { + /* Use AP DESIGNER and AP PARTNO to identify target */ + target->designer_code = ap->designer_code; + target->part_id = ap->partno; + } + + cortexr_priv_s *const priv = calloc(1, sizeof(*priv)); + if (!priv) { /* calloc failed: heap exhaustion */ + DEBUG_ERROR("calloc: failed in %s\n", __func__); + return false; + } + + target->priv = priv; + target->priv_free = cortex_priv_free; + priv->base.ap = ap; + priv->base.base_addr = base_address; + + target->driver = "ARM Cortex-R"; + + return true; +} diff --git a/src/target/target_probe.c b/src/target/target_probe.c index 5edecacd92b..b833fd9dacd 100644 --- a/src/target/target_probe.c +++ b/src/target/target_probe.c @@ -27,14 +27,14 @@ #define DO_PRAGMA(x) DO_PRAGMA_(x) // __attribute__((alias)) is not supported in AppleClang. #define weak_alias(name, aliasname) DO_PRAGMA(weak name = aliasname) -#define CORTEXA_PROBE_WEAK_NOP(name) weak_alias(name, cortexa_probe_nop) +#define CORTEXAR_PROBE_WEAK_NOP(name) weak_alias(name, cortexar_probe_nop) #define CORTEXM_PROBE_WEAK_NOP(name) weak_alias(name, cortexm_probe_nop) #define TARGET_PROBE_WEAK_NOP(name) weak_alias(name, target_probe_nop) #define LPC55_DP_PREPARE_WEAK_NOP(name) weak_alias(name, lpc55_dp_prepare_nop) #else #define APPLE_STATIC static inline -#define CORTEXA_PROBE_WEAK_NOP(name) \ - extern bool name(adiv5_access_port_s *, uint32_t) __attribute__((weak, alias("cortexa_probe_nop"))); +#define CORTEXAR_PROBE_WEAK_NOP(name) \ + extern bool name(adiv5_access_port_s *, target_addr_t) __attribute__((weak, alias("cortexar_probe_nop"))); #define CORTEXM_PROBE_WEAK_NOP(name) \ extern bool name(adiv5_access_port_s *) __attribute__((weak, alias("cortexm_probe_nop"))); #define TARGET_PROBE_WEAK_NOP(name) extern bool name(target_s *) __attribute__((weak, alias("target_probe_nop"))); @@ -42,26 +42,26 @@ extern void name(adiv5_debug_port_s *) __attribute__((weak, alias("lpc55_dp_prepare_nop"))); #endif -APPLE_STATIC bool cortexa_probe_nop(adiv5_access_port_s *apb, uint32_t debug_base) +APPLE_STATIC bool cortexar_probe_nop(adiv5_access_port_s *const access_port, const target_addr_t base_address) { - (void)apb; - (void)debug_base; + (void)access_port; + (void)base_address; return false; } -APPLE_STATIC bool cortexm_probe_nop(adiv5_access_port_s *access_port) +APPLE_STATIC bool cortexm_probe_nop(adiv5_access_port_s *const access_port) { (void)access_port; return false; } -APPLE_STATIC bool target_probe_nop(target_s *target) +APPLE_STATIC bool target_probe_nop(target_s *const target) { (void)target; return false; } -APPLE_STATIC void lpc55_dp_prepare_nop(adiv5_debug_port_s *debug_port) +APPLE_STATIC void lpc55_dp_prepare_nop(adiv5_debug_port_s *const debug_port) { (void)debug_port; } @@ -71,8 +71,8 @@ APPLE_STATIC void lpc55_dp_prepare_nop(adiv5_debug_port_s *debug_port) * to be disabled by not compiling/linking them in. */ -CORTEXA_PROBE_WEAK_NOP(cortexa_probe) - +CORTEXAR_PROBE_WEAK_NOP(cortexa_probe) +CORTEXAR_PROBE_WEAK_NOP(cortexr_probe) CORTEXM_PROBE_WEAK_NOP(cortexm_probe) CORTEXM_PROBE_WEAK_NOP(kinetis_mdm_probe) diff --git a/src/target/target_probe.h b/src/target/target_probe.h index fe186273652..ed55729d9d1 100644 --- a/src/target/target_probe.h +++ b/src/target/target_probe.h @@ -38,8 +38,8 @@ * Actual functions implemented in their respective drivers. */ -bool cortexa_probe(adiv5_access_port_s *apb, uint32_t debug_base); - +bool cortexa_probe(adiv5_access_port_s *apb, target_addr_t debug_base); +bool cortexr_probe(adiv5_access_port_s *apb, target_addr_t base_address); bool cortexm_probe(adiv5_access_port_s *ap); bool kinetis_mdm_probe(adiv5_access_port_s *ap); From 7b0df18a3e74772d95a7e4d8cc8a75367563fa64 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 8 Aug 2023 19:01:41 +0100 Subject: [PATCH 03/50] cortexr: Added Cortex-R definitions for and a call to cortex_read_cpuid() --- src/target/cortex.c | 3 +++ src/target/cortex.h | 3 +++ src/target/cortexr.c | 6 ++++++ 3 files changed, 12 insertions(+) diff --git a/src/target/cortex.c b/src/target/cortex.c index a8bff8a002f..59559b5061c 100644 --- a/src/target/cortex.c +++ b/src/target/cortex.c @@ -101,6 +101,9 @@ void cortex_read_cpuid(target_s *target) case CORTEX_A9: target->core = "A9"; break; + case CORTEX_R4: + target->core = "R4"; + break; case STAR_MC1: target->core = "STAR-MC1"; break; diff --git a/src/target/cortex.h b/src/target/cortex.h index 4fd7360a015..ff481aec0d3 100644 --- a/src/target/cortex.h +++ b/src/target/cortex.h @@ -50,6 +50,9 @@ #define CORTEX_M33 0xd210U #define STAR_MC1 0x1320U +/* Cortex-R CPU IDs */ +#define CORTEX_R4 0xc140U + /* Cortex-A CPU IDs */ #define CORTEX_A5 0xc050U #define CORTEX_A7 0xc070U diff --git a/src/target/cortexr.c b/src/target/cortexr.c index 0fb0294a839..652fb00474d 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -36,6 +36,8 @@ #include "target.h" #include "target_internal.h" #include "target_probe.h" +#include "jep106.h" +#include "cortex.h" #include "cortex_internal.h" typedef struct cortexr_priv { @@ -43,6 +45,8 @@ typedef struct cortexr_priv { cortex_priv_s base; } cortexr_priv_s; +#define CORTEXR_CPUID 0xd00U + bool cortexr_probe(adiv5_access_port_s *const ap, const target_addr_t base_address) { target_s *target = target_new(); @@ -73,5 +77,7 @@ bool cortexr_probe(adiv5_access_port_s *const ap, const target_addr_t base_addre target->driver = "ARM Cortex-R"; + cortex_read_cpuid(target); + return true; } From 94c8446b0132fa3fa3440d56d530e907253f628d Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 8 Aug 2023 22:55:38 +0100 Subject: [PATCH 04/50] cortexr: Implemented support for reading and writing memory on Cortex-R targets --- src/target/cortex.c | 3 +++ src/target/cortex.h | 1 + src/target/cortexr.c | 14 ++++++++++++++ 3 files changed, 18 insertions(+) diff --git a/src/target/cortex.c b/src/target/cortex.c index 59559b5061c..09076a10bd7 100644 --- a/src/target/cortex.c +++ b/src/target/cortex.c @@ -104,6 +104,9 @@ void cortex_read_cpuid(target_s *target) case CORTEX_R4: target->core = "R4"; break; + case CORTEX_R5: + target->core = "R5"; + break; case STAR_MC1: target->core = "STAR-MC1"; break; diff --git a/src/target/cortex.h b/src/target/cortex.h index ff481aec0d3..841d2ce3096 100644 --- a/src/target/cortex.h +++ b/src/target/cortex.h @@ -52,6 +52,7 @@ /* Cortex-R CPU IDs */ #define CORTEX_R4 0xc140U +#define CORTEX_R5 0xc150U /* Cortex-A CPU IDs */ #define CORTEX_A5 0xc050U diff --git a/src/target/cortexr.c b/src/target/cortexr.c index 652fb00474d..38ab4ae7c09 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -47,6 +47,16 @@ typedef struct cortexr_priv { #define CORTEXR_CPUID 0xd00U +static void cortexr_mem_read(target_s *const target, void *const dest, const target_addr_t src, const size_t len) +{ + adiv5_mem_read(cortex_ap(target), dest, src, len); +} + +static void cortexr_mem_write(target_s *const target, const target_addr_t dest, const void *const src, const size_t len) +{ + adiv5_mem_write(cortex_ap(target), dest, src, len); +} + bool cortexr_probe(adiv5_access_port_s *const ap, const target_addr_t base_address) { target_s *target = target_new(); @@ -75,6 +85,10 @@ bool cortexr_probe(adiv5_access_port_s *const ap, const target_addr_t base_addre priv->base.ap = ap; priv->base.base_addr = base_address; + target->check_error = cortex_check_error; + target->mem_read = cortexr_mem_read; + target->mem_write = cortexr_mem_write; + target->driver = "ARM Cortex-R"; cortex_read_cpuid(target); From 71f7d8cabc7b648e58c0f038a3a307d391a0f33e Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 9 Aug 2023 15:18:04 +0100 Subject: [PATCH 05/50] cortexr: Defined various registers found in the debug address space --- src/target/cortexr.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/target/cortexr.c b/src/target/cortexr.c index 38ab4ae7c09..706bdd6445c 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -45,7 +45,21 @@ typedef struct cortexr_priv { cortex_priv_s base; } cortexr_priv_s; +#define CORTEXR_DBG_WFAR 0x018U +#define CORTEXR_DBG_VCR 0x01cU +#define CORTEXR_DBG_DSCCR 0x028U +#define CORTEXR_DBG_DTRTX 0x080U +#define CORTEXR_DBG_ITR 0x084U +#define CORTEXR_DBG_DSCR 0x088U +#define CORTEXR_DBG_DTRRX 0x08cU +#define CORTEXR_DBG_DRCR 0x090U +#define CORTEXR_DBG_BVR 0x100U +#define CORTEXR_DBG_BCR 0x140U +#define CORTEXR_DBG_WVR 0x180U +#define CORTEXR_DBG_WCR 0x1c0U + #define CORTEXR_CPUID 0xd00U +#define CORTEXR_CTR 0xd04U static void cortexr_mem_read(target_s *const target, void *const dest, const target_addr_t src, const size_t len) { From 11b4aa7c02be6eaca8f718cc4f7f52eddf80a09d Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 9 Aug 2023 16:05:01 +0100 Subject: [PATCH 06/50] cortexr: Implemented a mechanism for reading coprocessor registers --- src/target/cortexr.c | 66 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/src/target/cortexr.c b/src/target/cortexr.c index 706bdd6445c..fb896bfbdd6 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -61,6 +61,28 @@ typedef struct cortexr_priv { #define CORTEXR_CPUID 0xd00U #define CORTEXR_CTR 0xd04U +#define CORTEXR_DBG_DSCR_INSN_COMPLETE (1U << 24U) +#define CORTEXR_DBG_DSCR_DTR_READ_READY (1U << 29U) +#define CORTEXR_DBG_DSCR_DTR_WRITE_DONE (1U << 30U) + +/* + * Instruction encodings for the coprocessor interface + * MRC -> Move to ARM core register from Coprocessor (DDI0406C §A8.8.108, pg493) + * MCR -> Move to Coprocessor from ARM core register (DDI0406C §A8.8.99, pg477) + */ +#define ARM_MRC_INSN 0xee100010U +#define ARM_MCR_INSN 0xee000010U +/* + * Encodes a core <=> coprocessor access for use with the MRC and MCR instructions. + * opc1 -> Coprocessor-specific opcode 1 + * rt -> ARM core register to use for the transfer + * crn -> Primary coprocessor register + * crm -> Additional coprocessor register + * opc2 -> Coprocessor-specific opcode 2 + */ +#define ENCODE_CP_ACCESS(coproc, opc1, rt, crn, crm, opc2) \ + (((opc1) << 21U) | ((crn) << 16U) | ((rt) << 12U) | ((coproc) << 8U) | ((opc2) << 5U) | (crm)) + static void cortexr_mem_read(target_s *const target, void *const dest, const target_addr_t src, const size_t len) { adiv5_mem_read(cortex_ap(target), dest, src, len); @@ -71,6 +93,50 @@ static void cortexr_mem_write(target_s *const target, const target_addr_t dest, adiv5_mem_write(cortex_ap(target), dest, src, len); } +static void cortexr_run_insn(target_s *const target, const uint32_t insn) +{ + /* Issue the requested instruction to the core */ + cortex_dbg_write32(target, CORTEXR_DBG_ITR, insn); + /* Poll for the instruction to complete */ + while (!(cortex_dbg_read32(target, CORTEXR_DBG_DSCR) & CORTEXR_DBG_DSCR_INSN_COMPLETE)) + continue; +} + +static uint32_t cortexr_run_read_insn(target_s *const target, const uint32_t insn) +{ + /* Issue the requested instruction to the core */ + cortex_dbg_write32(target, CORTEXR_DBG_ITR, insn); + /* Poll for the instruction to complete and the data to become ready in the DTR */ + while ((cortex_dbg_read32(target, CORTEXR_DBG_DSCR) & + (CORTEXR_DBG_DSCR_INSN_COMPLETE | CORTEXR_DBG_DSCR_DTR_READ_READY)) != + (CORTEXR_DBG_DSCR_INSN_COMPLETE | CORTEXR_DBG_DSCR_DTR_READ_READY)) + continue; + /* Read back the DTR to complete the read */ + return cortex_dbg_read32(target, CORTEXR_DBG_DTRRX); +} + +static inline uint32_t cortexr_core_reg_read(target_s *const target, const uint8_t reg) +{ + /* Build an issue a core to coprocessor transfer for the requested register and read back the result */ + return cortexr_run_read_insn(target, ARM_MCR_INSN | ENCODE_CP_ACCESS(14, 0, reg, 0, 5, 0)); +} + +uint32_t cortexr_coproc_read(target_s *const target, const uint8_t coproc, const uint16_t op) +{ + /* + * Perform a read of a coprocessor - which one (between 0 and 15) is given by the coproc parameter + * and which register of the coprocessor to read and the operands required is given by op. + * This follows the steps laid out in DDI0406C §C6.4.1 pg2109 + * + * Encode the MCR (Move to ARM core register from Coprocessor) instruction in ARM ISA format + * using core reg r0 as the read target. + */ + cortexr_run_insn(target, + ARM_MRC_INSN | + ENCODE_CP_ACCESS(coproc & 0xfU, (op >> 8U) & 0x7U, 0U, (op >> 4U) & 0xfU, op & 0xfU, (op >> 12U) & 0x7U)); + return cortexr_core_reg_read(target, 0U); +} + bool cortexr_probe(adiv5_access_port_s *const ap, const target_addr_t base_address) { target_s *target = target_new(); From bf3fde3c1220978e641a53a171312c96b4621454 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 9 Aug 2023 16:29:35 +0100 Subject: [PATCH 07/50] cortexr: Implemented a mechanism for writing coprocessor registers --- src/target/cortexr.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/target/cortexr.c b/src/target/cortexr.c index fb896bfbdd6..71108f27b10 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -115,12 +115,33 @@ static uint32_t cortexr_run_read_insn(target_s *const target, const uint32_t ins return cortex_dbg_read32(target, CORTEXR_DBG_DTRRX); } +static void cortexr_run_write_insn(target_s *const target, const uint32_t insn, const uint32_t data) +{ + /* Set up the data in the DTR for the transaction */ + cortex_dbg_write32(target, CORTEXR_DBG_DTRTX, data); + /* Poll for the data to become ready in the DTR */ + while (!(cortex_dbg_read32(target, CORTEXR_DBG_DSCR) & CORTEXR_DBG_DSCR_DTR_WRITE_DONE)) + continue; + /* Issue the requested instruction to the core */ + cortex_dbg_write32(target, CORTEXR_DBG_ITR, insn); + /* Poll for the instruction to complete and the data to be consumed from the DTR */ + while ((cortex_dbg_read32(target, CORTEXR_DBG_DSCR) & + (CORTEXR_DBG_DSCR_INSN_COMPLETE | CORTEXR_DBG_DSCR_DTR_WRITE_DONE)) != CORTEXR_DBG_DSCR_INSN_COMPLETE) + continue; +} + static inline uint32_t cortexr_core_reg_read(target_s *const target, const uint8_t reg) { /* Build an issue a core to coprocessor transfer for the requested register and read back the result */ return cortexr_run_read_insn(target, ARM_MCR_INSN | ENCODE_CP_ACCESS(14, 0, reg, 0, 5, 0)); } +static inline void cortexr_core_reg_write(target_s *const target, const uint8_t reg, const uint32_t value) +{ + /* Build and issue a coprocessor to core transfer for the requested register and send the new data */ + cortexr_run_write_insn(target, ARM_MRC_INSN | ENCODE_CP_ACCESS(14, 0, reg, 0, 5, 0), value); +} + uint32_t cortexr_coproc_read(target_s *const target, const uint8_t coproc, const uint16_t op) { /* @@ -137,6 +158,22 @@ uint32_t cortexr_coproc_read(target_s *const target, const uint8_t coproc, const return cortexr_core_reg_read(target, 0U); } +void cortexr_coproc_write(target_s *const target, const uint8_t coproc, const uint16_t op, const uint32_t value) +{ + /* + * Perform a write of a coprocessor - which one (between 0 and 15) is given by the coproc parameter + * and which register of the coprocessor to write and the operands required is given by op. + * This follows the steps laid out in DDI0406C §C6.4.1 pg2109 + * + * Encode the MRC (Move to Coprocessor from ARM core register) instruction in ARM ISA format + * using core reg r0 as the write data source. + */ + cortexr_core_reg_write(target, 0U, value); + cortexr_run_insn(target, + ARM_MCR_INSN | + ENCODE_CP_ACCESS(coproc & 0xfU, (op >> 8U) & 0x7U, 0U, (op >> 4U) & 0xfU, op & 0xfU, (op >> 12U) & 0x7U)); +} + bool cortexr_probe(adiv5_access_port_s *const ap, const target_addr_t base_address) { target_s *target = target_new(); From dec13691cbf6cfff6a7c03a3c1de258a8bff3cda Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 9 Aug 2023 16:51:41 +0100 Subject: [PATCH 08/50] cortexr: Implemented support for checking if a device implements hardware float --- src/target/cortexr.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/target/cortexr.c b/src/target/cortexr.c index 71108f27b10..83db54d6dbf 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -82,6 +82,15 @@ typedef struct cortexr_priv { */ #define ENCODE_CP_ACCESS(coproc, opc1, rt, crn, crm, opc2) \ (((opc1) << 21U) | ((crn) << 16U) | ((rt) << 12U) | ((coproc) << 8U) | ((opc2) << 5U) | (crm)) +/* Packs a CRn and CRm value for the coprocessor IO rouines below to unpack */ +#define ENCODE_CP_REG(n, m, opc1, opc2) \ + ((((n)&0xfU) << 4U) | ((m)&0xfU) | (((opc1)&0x7U) << 8U) | (((opc2)&0x7U) << 12U)) + +/* Coprocessor register definitions */ +#define CORTEXR_CPACR 15U, ENCODE_CP_REG(1U, 0U, 0U, 2U) + +#define CORTEXR_CPACR_CP10_FULL_ACCESS 0x00300000U +#define CORTEXR_CPACR_CP11_FULL_ACCESS 0x00c00000U static void cortexr_mem_read(target_s *const target, void *const dest, const target_addr_t src, const size_t len) { @@ -142,7 +151,7 @@ static inline void cortexr_core_reg_write(target_s *const target, const uint8_t cortexr_run_write_insn(target, ARM_MRC_INSN | ENCODE_CP_ACCESS(14, 0, reg, 0, 5, 0), value); } -uint32_t cortexr_coproc_read(target_s *const target, const uint8_t coproc, const uint16_t op) +static uint32_t cortexr_coproc_read(target_s *const target, const uint8_t coproc, const uint16_t op) { /* * Perform a read of a coprocessor - which one (between 0 and 15) is given by the coproc parameter @@ -158,7 +167,7 @@ uint32_t cortexr_coproc_read(target_s *const target, const uint8_t coproc, const return cortexr_core_reg_read(target, 0U); } -void cortexr_coproc_write(target_s *const target, const uint8_t coproc, const uint16_t op, const uint32_t value) +static void cortexr_coproc_write(target_s *const target, const uint8_t coproc, const uint16_t op, const uint32_t value) { /* * Perform a write of a coprocessor - which one (between 0 and 15) is given by the coproc parameter @@ -210,5 +219,17 @@ bool cortexr_probe(adiv5_access_port_s *const ap, const target_addr_t base_addre cortex_read_cpuid(target); + /* Grab r0 as the next steps clobber it */ + const uint32_t r0 = cortexr_core_reg_read(target, 0U); + /* Probe for FP extension. */ + uint32_t cpacr = cortexr_coproc_read(target, CORTEXR_CPACR); + cpacr |= CORTEXR_CPACR_CP10_FULL_ACCESS | CORTEXR_CPACR_CP11_FULL_ACCESS; + cortexr_coproc_write(target, CORTEXR_CPACR, cpacr); + const bool core_has_fpu = cortexr_coproc_read(target, CORTEXR_CPACR) == cpacr; + DEBUG_TARGET("%s: FPU present? %s\n", __func__, core_has_fpu ? "yes" : "no"); + + /* Restore r0 after all these steps */ + cortexr_core_reg_write(target, 0U, r0); + return true; } From 6c61ec03d74f668f7dac6e83363c4c17a209c33f Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 9 Aug 2023 16:52:05 +0100 Subject: [PATCH 09/50] cortexr: Added a post-probe message for reporting unknown devices --- src/target/cortexr.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/target/cortexr.c b/src/target/cortexr.c index 83db54d6dbf..103a8f92ed0 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -39,6 +39,7 @@ #include "jep106.h" #include "cortex.h" #include "cortex_internal.h" +#include "gdb_packet.h" typedef struct cortexr_priv { /* Base core information */ @@ -231,5 +232,11 @@ bool cortexr_probe(adiv5_access_port_s *const ap, const target_addr_t base_addre /* Restore r0 after all these steps */ cortexr_core_reg_write(target, 0U, r0); +#if PC_HOSTED == 0 + gdb_outf("Please report unknown device with Designer 0x%x Part ID 0x%x\n", target->designer_code, target->part_id); +#else + DEBUG_WARN( + "Please report unknown device with Designer 0x%x Part ID 0x%x\n", target->designer_code, target->part_id); +#endif return true; } From dbc723216c593f0c3b6c54bd7aaeb7ee175b1dfc Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 9 Aug 2023 16:57:10 +0100 Subject: [PATCH 10/50] cortexr: Added some documentation on where and how to find the architecture and technical reference manuals used --- src/target/cortexr.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/target/cortexr.c b/src/target/cortexr.c index 103a8f92ed0..6bb950c7730 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -31,6 +31,16 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +/* + * This file implements support for Cortex-R family processors. + * + * References: + * DDI0406C - ARM Architecture Reference Manual for ARMv7-A/R + * https://documentation-service.arm.com/static/5f8daeb7f86e16515cdb8c4e + * DDI0363G - Cortex-R4 and Cortex-R4F Technical Reference Manual + * https://documentation-service.arm.com/static/5f0358e8dbdee951c1cd6f3b + */ + #include "general.h" #include "adiv5.h" #include "target.h" From 581cc45e9f0d04f08e25a1359ea2aa99e57d1919 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 9 Aug 2023 21:43:53 +0100 Subject: [PATCH 11/50] cortexr: Handle when the core has a floating point unit --- src/target/cortexr.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/target/cortexr.c b/src/target/cortexr.c index 6bb950c7730..c132498d07b 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -103,6 +103,8 @@ typedef struct cortexr_priv { #define CORTEXR_CPACR_CP10_FULL_ACCESS 0x00300000U #define CORTEXR_CPACR_CP11_FULL_ACCESS 0x00c00000U +#define TOPT_FLAVOUR_FLOAT (1U << 1U) /* If set, core has a hardware FPU */ + static void cortexr_mem_read(target_s *const target, void *const dest, const target_addr_t src, const size_t len) { adiv5_mem_read(cortex_ap(target), dest, src, len); @@ -239,6 +241,11 @@ bool cortexr_probe(adiv5_access_port_s *const ap, const target_addr_t base_addre const bool core_has_fpu = cortexr_coproc_read(target, CORTEXR_CPACR) == cpacr; DEBUG_TARGET("%s: FPU present? %s\n", __func__, core_has_fpu ? "yes" : "no"); + if (core_has_fpu) { + target->target_options |= TOPT_FLAVOUR_FLOAT; + target->regs_size += sizeof(uint32_t) * CORTEX_FLOAT_REG_COUNT; + } + /* Restore r0 after all these steps */ cortexr_core_reg_write(target, 0U, r0); From 1ce2887d9a6820b4e911dac1ab588af5ccf672e4 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 9 Aug 2023 21:44:38 +0100 Subject: [PATCH 12/50] cortexr: Read out and decode the number of breakpoints and watchpoints a core implements --- src/target/cortexr.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/target/cortexr.c b/src/target/cortexr.c index c132498d07b..703c5d6bf2e 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -54,8 +54,13 @@ typedef struct cortexr_priv { /* Base core information */ cortex_priv_s base; + /* Watchpoint unit information */ + uint8_t hw_watchpoint_max; + /* Breakpoint unit information */ + uint8_t hw_breakpoint_max; } cortexr_priv_s; +#define CORTEXR_DBG_IDR 0x000U #define CORTEXR_DBG_WFAR 0x018U #define CORTEXR_DBG_VCR 0x01cU #define CORTEXR_DBG_DSCCR 0x028U @@ -72,6 +77,11 @@ typedef struct cortexr_priv { #define CORTEXR_CPUID 0xd00U #define CORTEXR_CTR 0xd04U +#define CORTEXR_DBG_IDR_BREAKPOINT_MASK 0xfU +#define CORTEXR_DBG_IDR_BREAKPOINT_SHIFT 24U +#define CORTEXR_DBG_IDR_WATCHPOINT_MASK 0xfU +#define CORTEXR_DBG_IDR_WATCHPOINT_SHIFT 28U + #define CORTEXR_DBG_DSCR_INSN_COMPLETE (1U << 24U) #define CORTEXR_DBG_DSCR_DTR_READ_READY (1U << 29U) #define CORTEXR_DBG_DSCR_DTR_WRITE_DONE (1U << 30U) @@ -231,6 +241,12 @@ bool cortexr_probe(adiv5_access_port_s *const ap, const target_addr_t base_addre target->driver = "ARM Cortex-R"; cortex_read_cpuid(target); + /* The format of the debug identification register is described in DDI0406C §C11.11.15 pg2217 */ + const uint32_t debug_id = cortex_dbg_read32(target, CORTEXR_DBG_IDR); + priv->hw_breakpoint_max = ((debug_id >> CORTEXR_DBG_IDR_BREAKPOINT_SHIFT) & CORTEXR_DBG_IDR_BREAKPOINT_MASK) + 1U; + priv->hw_watchpoint_max = ((debug_id >> CORTEXR_DBG_IDR_WATCHPOINT_SHIFT) & CORTEXR_DBG_IDR_WATCHPOINT_MASK) + 1U; + DEBUG_TARGET("%s %s core has %u breakpoint and %u watchpoint units available\n", target->driver, target->core, + priv->hw_breakpoint_max, priv->hw_watchpoint_max); /* Grab r0 as the next steps clobber it */ const uint32_t r0 = cortexr_core_reg_read(target, 0U); From ef77c48e872a7b0c23cb489cf67409f31b90d3b8 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 10 Aug 2023 02:59:40 +0100 Subject: [PATCH 13/50] cortexr: Implemented readout and decoding of the processor cache information --- src/target/cortexr.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/target/cortexr.c b/src/target/cortexr.c index 703c5d6bf2e..6be1f20106d 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -262,6 +262,21 @@ bool cortexr_probe(adiv5_access_port_s *const ap, const target_addr_t base_addre target->regs_size += sizeof(uint32_t) * CORTEX_FLOAT_REG_COUNT; } + /* Check cache type */ + const uint32_t cache_type = cortex_dbg_read32(target, CORTEXR_CTR); + if (cache_type >> CORTEX_CTR_FORMAT_SHIFT == CORTEX_CTR_FORMAT_ARMv7) { + /* If there is an ICache defined, decompress its length to a uint32_t count */ + if (cache_type & CORTEX_CTR_ICACHE_LINE_MASK) + priv->base.icache_line_length = CORTEX_CTR_ICACHE_LINE(cache_type); + /* If there is a DCache defined, decompress its length to a uint32_t count */ + if ((cache_type >> CORTEX_CTR_DCACHE_LINE_SHIFT) & CORTEX_CTR_DCACHE_LINE_MASK) + priv->base.dcache_line_length = CORTEX_CTR_DCACHE_LINE(cache_type); + + DEBUG_TARGET("%s: ICache line length = %u, DCache line length = %u\n", __func__, + priv->base.icache_line_length << 2U, priv->base.dcache_line_length << 2U); + } else + target_check_error(target); + /* Restore r0 after all these steps */ cortexr_core_reg_write(target, 0U, r0); From 212d54b91ce92d08562bc5f092c8f032693cb103 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 10 Aug 2023 20:22:58 +0100 Subject: [PATCH 14/50] cortexr: Implemented support for halting the target core --- src/target/cortexr.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/target/cortexr.c b/src/target/cortexr.c index 6be1f20106d..20dc72558f1 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -42,6 +42,7 @@ */ #include "general.h" +#include "exception.h" #include "adiv5.h" #include "target.h" #include "target_internal.h" @@ -86,6 +87,12 @@ typedef struct cortexr_priv { #define CORTEXR_DBG_DSCR_DTR_READ_READY (1U << 29U) #define CORTEXR_DBG_DSCR_DTR_WRITE_DONE (1U << 30U) +#define CORTEXR_DBG_DRCR_HALT_REQ (1U << 0U) +#define CORTEXR_DBG_DRCR_RESTART_REQ (1U << 1U) +#define CORTEXR_DBG_DRCR_CLR_STICKY_EXC (1U << 2U) +#define CORTEXR_DBG_DRCR_CLR_STICKY_PIPEADV (1U << 3U) +#define CORTEXR_DBG_DRCR_CANCEL_BUS_REQ (1U << 4U) + /* * Instruction encodings for the coprocessor interface * MRC -> Move to ARM core register from Coprocessor (DDI0406C §A8.8.108, pg493) @@ -115,6 +122,8 @@ typedef struct cortexr_priv { #define TOPT_FLAVOUR_FLOAT (1U << 1U) /* If set, core has a hardware FPU */ +static void cortexr_halt_request(target_s *target); + static void cortexr_mem_read(target_s *const target, void *const dest, const target_addr_t src, const size_t len) { adiv5_mem_read(cortex_ap(target), dest, src, len); @@ -240,6 +249,8 @@ bool cortexr_probe(adiv5_access_port_s *const ap, const target_addr_t base_addre target->driver = "ARM Cortex-R"; + target->halt_request = cortexr_halt_request; + cortex_read_cpuid(target); /* The format of the debug identification register is described in DDI0406C §C11.11.15 pg2217 */ const uint32_t debug_id = cortex_dbg_read32(target, CORTEXR_DBG_IDR); @@ -288,3 +299,13 @@ bool cortexr_probe(adiv5_access_port_s *const ap, const target_addr_t base_addre #endif return true; } + +static void cortexr_halt_request(target_s *const target) +{ + volatile exception_s error; + TRY_CATCH (error, EXCEPTION_TIMEOUT) { + cortex_dbg_write32(target, CORTEXR_DBG_DRCR, CORTEXR_DBG_DRCR_HALT_REQ); + } + if (error.type) + tc_printf(target, "Timeout sending interrupt, is target in WFI?\n"); +} From c2e3b4c61861cefb37a276799a706f1f233579da Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 10 Aug 2023 20:49:39 +0100 Subject: [PATCH 15/50] cortexr: Implemented handling for polling if the core is halted and why --- src/target/cortexr.c | 69 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 3 deletions(-) diff --git a/src/target/cortexr.c b/src/target/cortexr.c index 20dc72558f1..fdbcc982925 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -83,9 +83,20 @@ typedef struct cortexr_priv { #define CORTEXR_DBG_IDR_WATCHPOINT_MASK 0xfU #define CORTEXR_DBG_IDR_WATCHPOINT_SHIFT 28U -#define CORTEXR_DBG_DSCR_INSN_COMPLETE (1U << 24U) -#define CORTEXR_DBG_DSCR_DTR_READ_READY (1U << 29U) -#define CORTEXR_DBG_DSCR_DTR_WRITE_DONE (1U << 30U) +#define CORTEXR_DBG_DSCR_HALTED (1U << 0U) +#define CORTEXR_DBG_DSCR_RESTARTED (1U << 1U) +#define CORTEXR_DBG_DSCR_MOE_MASK 0x0000003cU +#define CORTEXR_DBG_DSCR_MOE_HALT_REQUEST 0x00000000U +#define CORTEXR_DBG_DSCR_MOE_BREAKPOINT 0x00000004U +#define CORTEXR_DBG_DSCR_MOE_ASYNC_WATCH 0x00000008U +#define CORTEXR_DBG_DSCR_MOE_BKPT_INSN 0x0000000cU +#define CORTEXR_DBG_DSCR_MOE_EXTERNAL_DBG 0x00000010U +#define CORTEXR_DBG_DSCR_MOE_VEC_CATCH 0x00000014U +#define CORTEXR_DBG_DSCR_MOE_SYNC_WATCH 0x00000028U +#define CORTEXR_DBG_DSCR_ITR_ENABLE (1U << 13U) +#define CORTEXR_DBG_DSCR_INSN_COMPLETE (1U << 24U) +#define CORTEXR_DBG_DSCR_DTR_READ_READY (1U << 29U) +#define CORTEXR_DBG_DSCR_DTR_WRITE_DONE (1U << 30U) #define CORTEXR_DBG_DRCR_HALT_REQ (1U << 0U) #define CORTEXR_DBG_DRCR_RESTART_REQ (1U << 1U) @@ -122,6 +133,7 @@ typedef struct cortexr_priv { #define TOPT_FLAVOUR_FLOAT (1U << 1U) /* If set, core has a hardware FPU */ +static target_halt_reason_e cortexr_halt_poll(target_s *target, target_addr_t *watch); static void cortexr_halt_request(target_s *target); static void cortexr_mem_read(target_s *const target, void *const dest, const target_addr_t src, const size_t len) @@ -250,6 +262,7 @@ bool cortexr_probe(adiv5_access_port_s *const ap, const target_addr_t base_addre target->driver = "ARM Cortex-R"; target->halt_request = cortexr_halt_request; + target->halt_poll = cortexr_halt_poll; cortex_read_cpuid(target); /* The format of the debug identification register is described in DDI0406C §C11.11.15 pg2217 */ @@ -309,3 +322,53 @@ static void cortexr_halt_request(target_s *const target) if (error.type) tc_printf(target, "Timeout sending interrupt, is target in WFI?\n"); } + +static target_halt_reason_e cortexr_halt_poll(target_s *const target, target_addr_t *const watch) +{ + volatile uint32_t dscr = 0; + volatile exception_s error; + TRY_CATCH (error, EXCEPTION_ALL) { + /* If this times out because the target is in WFI then the target is still running. */ + dscr = cortex_dbg_read32(target, CORTEXR_DBG_DSCR); + } + switch (error.type) { + case EXCEPTION_ERROR: + /* Things went seriously wrong and there is no recovery from this... */ + target_list_free(); + return TARGET_HALT_ERROR; + case EXCEPTION_TIMEOUT: + /* Timeout isn't actually a problem and probably means target is in WFI */ + return TARGET_HALT_RUNNING; + } + + /* Check that the core actually halted */ + if (!(dscr & CORTEXR_DBG_DSCR_HALTED)) + return TARGET_HALT_RUNNING; + + /* Make sure ITR is enabled */ + cortex_dbg_write32(target, CORTEXR_DBG_DSCR, dscr | CORTEXR_DBG_DSCR_ITR_ENABLE); + + /* Read the target's registers out and cache them */ + + target_halt_reason_e reason = TARGET_HALT_FAULT; + /* Determine why we halted exactly from the Method Of Entry bits */ + switch (dscr & CORTEXR_DBG_DSCR_MOE_MASK) { + case CORTEXR_DBG_DSCR_MOE_HALT_REQUEST: + reason = TARGET_HALT_REQUEST; + break; + case CORTEXR_DBG_DSCR_MOE_EXTERNAL_DBG: + case CORTEXR_DBG_DSCR_MOE_BREAKPOINT: + case CORTEXR_DBG_DSCR_MOE_BKPT_INSN: + case CORTEXR_DBG_DSCR_MOE_VEC_CATCH: + reason = TARGET_HALT_BREAKPOINT; + break; + case CORTEXR_DBG_DSCR_MOE_ASYNC_WATCH: + case CORTEXR_DBG_DSCR_MOE_SYNC_WATCH: + /* TODO: determine the watchpoint we hit */ + (void)watch; + reason = TARGET_HALT_WATCHPOINT; + break; + } + /* Check if we halted because we were actually single-stepping */ + return reason; +} From a69408dc54795f7b455849360db0f6f154f570b7 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 10 Aug 2023 21:35:14 +0100 Subject: [PATCH 16/50] cortexr: Implemented support for resuming the target core --- src/target/cortexr.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/target/cortexr.c b/src/target/cortexr.c index fdbcc982925..3b39eb65aca 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -135,6 +135,7 @@ typedef struct cortexr_priv { static target_halt_reason_e cortexr_halt_poll(target_s *target, target_addr_t *watch); static void cortexr_halt_request(target_s *target); +static void cortexr_halt_resume(target_s *target, bool step); static void cortexr_mem_read(target_s *const target, void *const dest, const target_addr_t src, const size_t len) { @@ -263,6 +264,7 @@ bool cortexr_probe(adiv5_access_port_s *const ap, const target_addr_t base_addre target->halt_request = cortexr_halt_request; target->halt_poll = cortexr_halt_poll; + target->halt_resume = cortexr_halt_resume; cortex_read_cpuid(target); /* The format of the debug identification register is described in DDI0406C §C11.11.15 pg2217 */ @@ -372,3 +374,17 @@ static target_halt_reason_e cortexr_halt_poll(target_s *const target, target_add /* Check if we halted because we were actually single-stepping */ return reason; } + +static void cortexr_halt_resume(target_s *const target, const bool step) +{ + (void)step; + /* Start by asking to resume the core */ + cortex_dbg_write32(target, CORTEXR_DBG_DRCR, CORTEXR_DBG_DRCR_CLR_STICKY_EXC | CORTEXR_DBG_DRCR_RESTART_REQ); + + /* Then poll for when the core actually resumes */ + platform_timeout_s timeout; + platform_timeout_set(&timeout, 250); + uint32_t status = CORTEXR_DBG_DSCR_HALTED; + while (!(status & CORTEXR_DBG_DSCR_RESTARTED) && !platform_timeout_is_expired(&timeout)) + status = cortex_dbg_read32(target, CORTEXR_DBG_DSCR); +} From 1ac620e2dce2165e263b1e541dbc737cf0d3ebfe Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 10 Aug 2023 21:37:15 +0100 Subject: [PATCH 17/50] cortexr: Implemented core halting prior to executing coprocessor instructions on the target --- src/target/cortexr.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/target/cortexr.c b/src/target/cortexr.c index 3b39eb65aca..c61df395cbe 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -266,6 +266,17 @@ bool cortexr_probe(adiv5_access_port_s *const ap, const target_addr_t base_addre target->halt_poll = cortexr_halt_poll; target->halt_resume = cortexr_halt_resume; + /* Try to halt the target core */ + target_halt_request(target); + platform_timeout_s timeout; + platform_timeout_set(&timeout, 250); + target_halt_reason_e reason = TARGET_HALT_RUNNING; + while (!platform_timeout_is_expired(&timeout) && reason == TARGET_HALT_RUNNING) + reason = target_halt_poll(target, NULL); + /* If we did not succeed, we must abort at this point. */ + if (reason == TARGET_HALT_FAULT || reason == TARGET_HALT_ERROR) + return false; + cortex_read_cpuid(target); /* The format of the debug identification register is described in DDI0406C §C11.11.15 pg2217 */ const uint32_t debug_id = cortex_dbg_read32(target, CORTEXR_DBG_IDR); From 9c782fb79488a6d7d1727ea38fffe2dc1dd43fdd Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 11 Aug 2023 04:54:54 +0100 Subject: [PATCH 18/50] cortexr: Implemented a save/restore mechanism for r0-r15 + CPSR --- src/target/cortexr.c | 84 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 73 insertions(+), 11 deletions(-) diff --git a/src/target/cortexr.c b/src/target/cortexr.c index c61df395cbe..56cb8be154b 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -59,6 +59,12 @@ typedef struct cortexr_priv { uint8_t hw_watchpoint_max; /* Breakpoint unit information */ uint8_t hw_breakpoint_max; + + /* Core registers cache */ + struct { + uint32_t r[16]; + uint32_t cpsr; + } core_regs; } cortexr_priv_s; #define CORTEXR_DBG_IDR 0x000U @@ -104,6 +110,18 @@ typedef struct cortexr_priv { #define CORTEXR_DBG_DRCR_CLR_STICKY_PIPEADV (1U << 3U) #define CORTEXR_DBG_DRCR_CANCEL_BUS_REQ (1U << 4U) +/* + * Instruction encodings for reading/writing the program counter to/from r0, + * and reading/writing CPSR to/from r0 + */ +#define ARM_MOV_R0_PC_INSN 0xe1a0000fU +#define ARM_MOV_PC_R0_INSN 0xe1a0f000U +#define ARM_MRS_R0_CPSR_INSN 0xe10f0000U +#define ARM_MSR_CPSR_R0_INSN 0xe12ff000U + +/* CPSR register definitions */ +#define CORTEXR_CPSR_THUMB (1U << 5U) + /* * Instruction encodings for the coprocessor interface * MRC -> Move to ARM core register from Coprocessor (DDI0406C §A8.8.108, pg493) @@ -186,14 +204,59 @@ static void cortexr_run_write_insn(target_s *const target, const uint32_t insn, static inline uint32_t cortexr_core_reg_read(target_s *const target, const uint8_t reg) { - /* Build an issue a core to coprocessor transfer for the requested register and read back the result */ - return cortexr_run_read_insn(target, ARM_MCR_INSN | ENCODE_CP_ACCESS(14, 0, reg, 0, 5, 0)); + /* If the register is a GPR and not the program counter, use a "simple" MCR to read */ + if (reg < 15U) + /* Build an issue a core to coprocessor transfer for the requested register and read back the result */ + return cortexr_run_read_insn(target, ARM_MCR_INSN | ENCODE_CP_ACCESS(14, 0, reg, 0, 5, 0)); + /* If the register is the program counter, we first have to extract it to r0 */ + else if (reg == 15U) { + cortexr_run_insn(target, ARM_MOV_R0_PC_INSN); + return cortexr_core_reg_read(target, 0U); + } + return 0U; +} + +static void cortexr_core_regs_save(target_s *const target) +{ + cortexr_priv_s *const priv = (cortexr_priv_s *)target->priv; + /* Save out r0-r15 in that order (r15, aka pc, clobbers r0) */ + for (size_t i = 0U; i < ARRAY_LENGTH(priv->core_regs.r); ++i) + priv->core_regs.r[i] = cortexr_core_reg_read(target, i); + /* Read CPSR to r0 and retrieve it */ + cortexr_run_insn(target, ARM_MRS_R0_CPSR_INSN); + priv->core_regs.cpsr = cortexr_core_reg_read(target, 0U); + /* Adjust the program counter according to the mode */ + priv->core_regs.r[CORTEX_REG_PC] -= (priv->core_regs.cpsr & CORTEXR_CPSR_THUMB) ? 4U : 8U; } static inline void cortexr_core_reg_write(target_s *const target, const uint8_t reg, const uint32_t value) { - /* Build and issue a coprocessor to core transfer for the requested register and send the new data */ - cortexr_run_write_insn(target, ARM_MRC_INSN | ENCODE_CP_ACCESS(14, 0, reg, 0, 5, 0), value); + /* If the register is a GPR and not the program counter, use a "simple" MCR to read */ + if (reg < 15U) + /* Build and issue a coprocessor to core transfer for the requested register and send the new data */ + cortexr_run_write_insn(target, ARM_MRC_INSN | ENCODE_CP_ACCESS(14, 0, reg, 0, 5, 0), value); + /* If the register is the program counter, we first have to write it to r0 */ + else if (reg == 15U) { + cortexr_core_reg_write(target, 0U, value); + cortexr_run_insn(target, ARM_MOV_PC_R0_INSN); + } +} + +static void cortexr_core_regs_restore(target_s *const target) +{ + cortexr_priv_s *const priv = (cortexr_priv_s *)target->priv; + /* Load the value for CPSR to r0 and then shove it back into place */ + cortexr_core_reg_write(target, 0U, priv->core_regs.cpsr); + cortexr_run_insn(target, ARM_MSR_CPSR_R0_INSN); + /* Fix up the program counter for the mode */ + if (priv->core_regs.cpsr & CORTEXR_CPSR_THUMB) + priv->core_regs.r[CORTEX_REG_PC] |= 1U; + /* Restore r1-15 in that order. Ignore r0 for the moment as it gets clobbered repeatedly */ + for (size_t i = 1U; i < ARRAY_LENGTH(priv->core_regs.r); ++i) + cortexr_core_reg_write(target, i, priv->core_regs.r[i]); + + /* Now we're done with the rest of the registers, restore r0 */ + cortexr_core_reg_write(target, 0U, priv->core_regs.r[0U]); } static uint32_t cortexr_coproc_read(target_s *const target, const uint8_t coproc, const uint16_t op) @@ -285,8 +348,6 @@ bool cortexr_probe(adiv5_access_port_s *const ap, const target_addr_t base_addre DEBUG_TARGET("%s %s core has %u breakpoint and %u watchpoint units available\n", target->driver, target->core, priv->hw_breakpoint_max, priv->hw_watchpoint_max); - /* Grab r0 as the next steps clobber it */ - const uint32_t r0 = cortexr_core_reg_read(target, 0U); /* Probe for FP extension. */ uint32_t cpacr = cortexr_coproc_read(target, CORTEXR_CPACR); cpacr |= CORTEXR_CPACR_CP10_FULL_ACCESS | CORTEXR_CPACR_CP11_FULL_ACCESS; @@ -314,9 +375,6 @@ bool cortexr_probe(adiv5_access_port_s *const ap, const target_addr_t base_addre } else target_check_error(target); - /* Restore r0 after all these steps */ - cortexr_core_reg_write(target, 0U, r0); - #if PC_HOSTED == 0 gdb_outf("Please report unknown device with Designer 0x%x Part ID 0x%x\n", target->designer_code, target->part_id); #else @@ -361,7 +419,8 @@ static target_halt_reason_e cortexr_halt_poll(target_s *const target, target_add /* Make sure ITR is enabled */ cortex_dbg_write32(target, CORTEXR_DBG_DSCR, dscr | CORTEXR_DBG_DSCR_ITR_ENABLE); - /* Read the target's registers out and cache them */ + /* Save the target core's registers as debugging operations clobber them */ + cortexr_core_regs_save(target); target_halt_reason_e reason = TARGET_HALT_FAULT; /* Determine why we halted exactly from the Method Of Entry bits */ @@ -389,7 +448,10 @@ static target_halt_reason_e cortexr_halt_poll(target_s *const target, target_add static void cortexr_halt_resume(target_s *const target, const bool step) { (void)step; - /* Start by asking to resume the core */ + /* Restore the core's registers so the running program doesn't know we've been in there */ + cortexr_core_regs_restore(target); + + /* Ask to resume the core */ cortex_dbg_write32(target, CORTEXR_DBG_DRCR, CORTEXR_DBG_DRCR_CLR_STICKY_EXC | CORTEXR_DBG_DRCR_RESTART_REQ); /* Then poll for when the core actually resumes */ From 5a6dec33db6f4b0dede570ef15133125e4c69ea8 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 11 Aug 2023 06:45:24 +0100 Subject: [PATCH 19/50] cortexr: Implemented a save/restore mechanism for d0-d15 + FPSCR --- src/target/cortexr.c | 53 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/src/target/cortexr.c b/src/target/cortexr.c index 56cb8be154b..b61f10d158c 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -62,8 +62,11 @@ typedef struct cortexr_priv { /* Core registers cache */ struct { - uint32_t r[16]; + uint32_t r[16U]; uint32_t cpsr; + uint32_t spsr[5U]; + uint64_t d[16U]; + uint32_t fpcsr; } core_regs; } cortexr_priv_s; @@ -122,6 +125,15 @@ typedef struct cortexr_priv { /* CPSR register definitions */ #define CORTEXR_CPSR_THUMB (1U << 5U) +/* + * Instruction encodings for reading/writing the VFPv3 float registers + * to/from r0 and r1 and reading/writing FPSCR to/from r0 + */ +#define ARM_VMRS_R0_FPCSR_INSN 0xeef10a10 +#define ARM_VMSR_FPCSR_R0_INSN 0xeee10a10 +#define ARM_VMOV_R0_R1_DN_INSN 0xec510b10 +#define ARM_VMOV_DN_R0_R1_INSN 0xec410b10 + /* * Instruction encodings for the coprocessor interface * MRC -> Move to ARM core register from Coprocessor (DDI0406C §A8.8.108, pg493) @@ -229,6 +241,24 @@ static void cortexr_core_regs_save(target_s *const target) priv->core_regs.r[CORTEX_REG_PC] -= (priv->core_regs.cpsr & CORTEXR_CPSR_THUMB) ? 4U : 8U; } +static void cortexr_float_regs_save(target_s *const target) +{ + cortexr_priv_s *const priv = (cortexr_priv_s *)target->priv; + /* Read FPCSR to r0 and retrieve it */ + cortexr_run_insn(target, ARM_VMRS_R0_FPCSR_INSN); + priv->core_regs.fpcsr = cortexr_core_reg_read(target, 0U); + /* Now step through each double-precision float register, reading it back to r0,r1 */ + for (size_t i = 0; i < ARRAY_LENGTH(priv->core_regs.d); ++i) { + /* The float register to read slots into the bottom 4 bits of the instruction */ + cortexr_run_insn(target, ARM_VMOV_R0_R1_DN_INSN | i); + /* Read back the data */ + const uint32_t d_low = cortexr_core_reg_read(target, 0U); + const uint32_t d_high = cortexr_core_reg_read(target, 1U); + /* Reassemble it as a full 64-bit value */ + priv->core_regs.d[i] = d_low | ((uint64_t)d_high << 32U); + } +} + static inline void cortexr_core_reg_write(target_s *const target, const uint8_t reg, const uint32_t value) { /* If the register is a GPR and not the program counter, use a "simple" MCR to read */ @@ -259,6 +289,22 @@ static void cortexr_core_regs_restore(target_s *const target) cortexr_core_reg_write(target, 0U, priv->core_regs.r[0U]); } +static void cortexr_float_regs_restore(target_s *const target) +{ + const cortexr_priv_s *const priv = (cortexr_priv_s *)target->priv; + /* Step through each double-precision float register, writing it back via r0,r1 */ + for (size_t i = 0; i < ARRAY_LENGTH(priv->core_regs.d); ++i) { + /* Load the low 32 bits into r0, and the high into r1 */ + cortexr_core_reg_write(target, 0U, priv->core_regs.d[i] & UINT32_MAX); + cortexr_core_reg_write(target, 1U, priv->core_regs.d[i] >> 32U); + /* The float register to write slots into the bottom 4 bits of the instruction */ + cortexr_run_insn(target, ARM_VMOV_DN_R0_R1_INSN | i); + } + /* Load the value for FPCSR to r0 and then shove it back into place */ + cortexr_core_reg_write(target, 0U, priv->core_regs.fpcsr); + cortexr_run_insn(target, ARM_VMSR_FPCSR_R0_INSN); +} + static uint32_t cortexr_coproc_read(target_s *const target, const uint8_t coproc, const uint16_t op) { /* @@ -358,6 +404,7 @@ bool cortexr_probe(adiv5_access_port_s *const ap, const target_addr_t base_addre if (core_has_fpu) { target->target_options |= TOPT_FLAVOUR_FLOAT; target->regs_size += sizeof(uint32_t) * CORTEX_FLOAT_REG_COUNT; + cortexr_float_regs_save(target); } /* Check cache type */ @@ -421,6 +468,8 @@ static target_halt_reason_e cortexr_halt_poll(target_s *const target, target_add /* Save the target core's registers as debugging operations clobber them */ cortexr_core_regs_save(target); + if (target->target_options & TOPT_FLAVOUR_FLOAT) + cortexr_float_regs_save(target); target_halt_reason_e reason = TARGET_HALT_FAULT; /* Determine why we halted exactly from the Method Of Entry bits */ @@ -449,6 +498,8 @@ static void cortexr_halt_resume(target_s *const target, const bool step) { (void)step; /* Restore the core's registers so the running program doesn't know we've been in there */ + if (target->target_options & TOPT_FLAVOUR_FLOAT) + cortexr_float_regs_restore(target); cortexr_core_regs_restore(target); /* Ask to resume the core */ From 485e86a004c997e9b5edb982f26d9d5ef92c6ffa Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 11 Aug 2023 18:13:56 +0100 Subject: [PATCH 20/50] cortexr: Implemented save/restore for the SPSRs --- src/target/cortexr.c | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/src/target/cortexr.c b/src/target/cortexr.c index b61f10d158c..f37f3958a2d 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -115,16 +115,33 @@ typedef struct cortexr_priv { /* * Instruction encodings for reading/writing the program counter to/from r0, - * and reading/writing CPSR to/from r0 + * reading/writing CPSR to/from r0, and reading/writing the SPSRs to/from r0. */ #define ARM_MOV_R0_PC_INSN 0xe1a0000fU #define ARM_MOV_PC_R0_INSN 0xe1a0f000U #define ARM_MRS_R0_CPSR_INSN 0xe10f0000U #define ARM_MSR_CPSR_R0_INSN 0xe12ff000U +#define ARM_MRS_R0_SPSR_INSN 0xe1400200U +#define ARM_MSR_SPSR_R0_INSN 0xe160f200U /* CPSR register definitions */ #define CORTEXR_CPSR_THUMB (1U << 5U) +/* + * Table of encodings for the banked SPSRs - These are encoded in the following format: + * Bits[0]: SYSm[0] + * Bits[15:12]: SYSm[4:1] + * This allows these values to simply be shifted up a little to put them in the right spot + * for use in the banked MRS/MSR instructions. + */ +static const uint16_t cortexr_spsr_encodings[5] = { + 0xc001U, /* FIQ */ + 0x1000U, /* IRQ */ + 0x5000U, /* SVC */ + 0x9000U, /* ABT */ + 0xd000U, /* UND */ +}; + /* * Instruction encodings for reading/writing the VFPv3 float registers * to/from r0 and r1 and reading/writing FPSCR to/from r0 @@ -239,6 +256,12 @@ static void cortexr_core_regs_save(target_s *const target) priv->core_regs.cpsr = cortexr_core_reg_read(target, 0U); /* Adjust the program counter according to the mode */ priv->core_regs.r[CORTEX_REG_PC] -= (priv->core_regs.cpsr & CORTEXR_CPSR_THUMB) ? 4U : 8U; + /* Read the SPSRs into r0 and retrieve them */ + for (size_t i = 0; i < ARRAY_LENGTH(priv->core_regs.spsr); ++i) { + /* Build and issue the banked MRS for the required SPSR */ + cortexr_run_insn(target, ARM_MRS_R0_SPSR_INSN | (cortexr_spsr_encodings[i] << 4U)); + priv->core_regs.spsr[i] = cortexr_core_reg_read(target, 0U); + } } static void cortexr_float_regs_save(target_s *const target) @@ -275,6 +298,12 @@ static inline void cortexr_core_reg_write(target_s *const target, const uint8_t static void cortexr_core_regs_restore(target_s *const target) { cortexr_priv_s *const priv = (cortexr_priv_s *)target->priv; + /* Load the values for each of the SPSRs in turn into r0 and shove them back into place */ + for (size_t i = 0; i < ARRAY_LENGTH(priv->core_regs.spsr); ++i) { + cortexr_core_reg_write(target, 0U, priv->core_regs.spsr[i]); + /* Build and issue the banked MSR for the required SPSR */ + cortexr_run_insn(target, ARM_MSR_SPSR_R0_INSN | (cortexr_spsr_encodings[i] << 4U)); + } /* Load the value for CPSR to r0 and then shove it back into place */ cortexr_core_reg_write(target, 0U, priv->core_regs.cpsr); cortexr_run_insn(target, ARM_MSR_CPSR_R0_INSN); @@ -284,7 +313,6 @@ static void cortexr_core_regs_restore(target_s *const target) /* Restore r1-15 in that order. Ignore r0 for the moment as it gets clobbered repeatedly */ for (size_t i = 1U; i < ARRAY_LENGTH(priv->core_regs.r); ++i) cortexr_core_reg_write(target, i, priv->core_regs.r[i]); - /* Now we're done with the rest of the registers, restore r0 */ cortexr_core_reg_write(target, 0U, priv->core_regs.r[0U]); } From d5666cd91a766115dbb999f31f88ce857eda1a07 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 11 Aug 2023 18:14:51 +0100 Subject: [PATCH 21/50] cortexr: Tidied up the register save/restore code to properly abstract it for use elsewhere --- src/target/cortexr.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/target/cortexr.c b/src/target/cortexr.c index f37f3958a2d..dae30f92446 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -282,6 +282,13 @@ static void cortexr_float_regs_save(target_s *const target) } } +static void cortexr_regs_save(target_s *const target) +{ + cortexr_core_regs_save(target); + if (target->target_options & TOPT_FLAVOUR_FLOAT) + cortexr_float_regs_save(target); +} + static inline void cortexr_core_reg_write(target_s *const target, const uint8_t reg, const uint32_t value) { /* If the register is a GPR and not the program counter, use a "simple" MCR to read */ @@ -333,6 +340,13 @@ static void cortexr_float_regs_restore(target_s *const target) cortexr_run_insn(target, ARM_VMSR_FPCSR_R0_INSN); } +static void cortexr_regs_restore(target_s *const target) +{ + if (target->target_options & TOPT_FLAVOUR_FLOAT) + cortexr_float_regs_restore(target); + cortexr_core_regs_restore(target); +} + static uint32_t cortexr_coproc_read(target_s *const target, const uint8_t coproc, const uint16_t op) { /* @@ -495,9 +509,7 @@ static target_halt_reason_e cortexr_halt_poll(target_s *const target, target_add cortex_dbg_write32(target, CORTEXR_DBG_DSCR, dscr | CORTEXR_DBG_DSCR_ITR_ENABLE); /* Save the target core's registers as debugging operations clobber them */ - cortexr_core_regs_save(target); - if (target->target_options & TOPT_FLAVOUR_FLOAT) - cortexr_float_regs_save(target); + cortexr_regs_save(target); target_halt_reason_e reason = TARGET_HALT_FAULT; /* Determine why we halted exactly from the Method Of Entry bits */ @@ -526,9 +538,7 @@ static void cortexr_halt_resume(target_s *const target, const bool step) { (void)step; /* Restore the core's registers so the running program doesn't know we've been in there */ - if (target->target_options & TOPT_FLAVOUR_FLOAT) - cortexr_float_regs_restore(target); - cortexr_core_regs_restore(target); + cortexr_regs_restore(target); /* Ask to resume the core */ cortex_dbg_write32(target, CORTEXR_DBG_DRCR, CORTEXR_DBG_DRCR_CLR_STICKY_EXC | CORTEXR_DBG_DRCR_RESTART_REQ); From 1a57878d7911199e612c130bce13c5061a6680db Mon Sep 17 00:00:00 2001 From: dragonmux Date: Mon, 14 Aug 2023 03:22:06 +0100 Subject: [PATCH 22/50] cortexr: Implemented support for building the XML target description for non-FPU cores --- src/target/cortexr.c | 116 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/src/target/cortexr.c b/src/target/cortexr.c index dae30f92446..b2fde3f5bd7 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -50,8 +50,11 @@ #include "jep106.h" #include "cortex.h" #include "cortex_internal.h" +#include "gdb_reg.h" #include "gdb_packet.h" +#include + typedef struct cortexr_priv { /* Base core information */ cortex_priv_s base; @@ -180,10 +183,42 @@ static const uint16_t cortexr_spsr_encodings[5] = { #define TOPT_FLAVOUR_FLOAT (1U << 1U) /* If set, core has a hardware FPU */ +/* + * Fields for Cortex-R special-purpose registers, used in the generation of GDB's target description XML. + * The general-purpose registers r0-r12 and the vector floating point (VFP) registers d0-d15 all follow a + * very regular format, so we only need to store fields for the special-purpose registers. + * The array for each SPR field have the same order as each other, making each of these pseudo + * 'associative array's. + */ + +/* Cortex-R special-purpose register name strings */ +static const char *cortexr_spr_names[] = { + "sp", + "lr", + "pc", + "cpsr", +}; + +/* Cortex-R special-purpose register types */ +static const gdb_reg_type_e cortexr_spr_types[] = { + GDB_TYPE_DATA_PTR, /* sp */ + GDB_TYPE_CODE_PTR, /* lr */ + GDB_TYPE_CODE_PTR, /* pc */ + GDB_TYPE_UNSPECIFIED, /* cpsr */ +}; + +/* clang-format off */ +static_assert(ARRAY_LENGTH(cortexr_spr_types) == ARRAY_LENGTH(cortexr_spr_names), + "SPR array length mistmatch! SPR type array should ahve the same length as SPR name array." +); +/* clang-format on */ + static target_halt_reason_e cortexr_halt_poll(target_s *target, target_addr_t *watch); static void cortexr_halt_request(target_s *target); static void cortexr_halt_resume(target_s *target, bool step); +static const char *cortexr_target_description(target_s *target); + static void cortexr_mem_read(target_s *const target, void *const dest, const target_addr_t src, const size_t len) { adiv5_mem_read(cortex_ap(target), dest, src, len); @@ -443,6 +478,8 @@ bool cortexr_probe(adiv5_access_port_s *const ap, const target_addr_t base_addre const bool core_has_fpu = cortexr_coproc_read(target, CORTEXR_CPACR) == cpacr; DEBUG_TARGET("%s: FPU present? %s\n", __func__, core_has_fpu ? "yes" : "no"); + target->regs_description = cortexr_target_description; + if (core_has_fpu) { target->target_options |= TOPT_FLAVOUR_FLOAT; target->regs_size += sizeof(uint32_t) * CORTEX_FLOAT_REG_COUNT; @@ -550,3 +587,82 @@ static void cortexr_halt_resume(target_s *const target, const bool step) while (!(status & CORTEXR_DBG_DSCR_RESTARTED) && !platform_timeout_is_expired(&timeout)) status = cortex_dbg_read32(target, CORTEXR_DBG_DSCR); } + +/* + * This function creates the target description XML string for a Cortex-R part. + * This is done this way to decrease string duplications and thus code size, + * making it unfortunately much less readable than the string literal it is + * equivilent to. + * + * The string it creates is aproximately the following: + * + * + * + * arm + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ +static size_t cortexr_build_target_description(char *const buffer, size_t max_length, const bool has_fpu) +{ + (void)has_fpu; + size_t print_size = max_length; + /* Start with the "preamble" chunks which are mostly common across targets save for 2 words. */ + int offset = snprintf(buffer, print_size, "%s target %sarm%s ", + gdb_xml_preamble_first, gdb_xml_preamble_second, gdb_xml_preamble_third); + + /* Then build the general purpose register descriptions for r0-r12 */ + for (uint8_t i = 0; i <= 12U; ++i) { + if (max_length != 0) + print_size = max_length - (size_t)offset; + + offset += snprintf(buffer + offset, print_size, "", i); + } + + /* Now build the special-purpose register descriptions using the arrays at the top of file */ + for (uint8_t i = 0; i < ARRAY_LENGTH(cortexr_spr_names); ++i) { + if (max_length != 0) + print_size = max_length - (size_t)offset; + + const char *const name = cortexr_spr_names[i]; + const gdb_reg_type_e type = cortexr_spr_types[i]; + + offset += snprintf(buffer + offset, print_size, "", name, + gdb_reg_type_strings[type], i == 3U ? " regnum=\"25\"" : ""); + } + + if (max_length != 0) + print_size = max_length - (size_t)offset; + + offset += snprintf(buffer + offset, print_size, ""); + /* offset is now the total length of the string created, discard the sign and return it. */ + return (size_t)offset; +} + +static const char *cortexr_target_description(target_s *const target) +{ + const size_t description_length = + cortexr_build_target_description(NULL, 0, target->target_options & TOPT_FLAVOUR_FLOAT) + 1U; + char *const description = malloc(description_length); + if (description) + (void)cortexr_build_target_description( + description, description_length, target->target_options & TOPT_FLAVOUR_FLOAT); + return description; +} From dd9d021f1f9be83176d72b20f46ea57ade1f0071 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Mon, 14 Aug 2023 03:41:20 +0100 Subject: [PATCH 23/50] cortexr: Implemented support for building the XML target description for VFPv2 FPUs --- src/target/cortexr.c | 59 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/src/target/cortexr.c b/src/target/cortexr.c index b2fde3f5bd7..19d340c8f31 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -588,6 +588,55 @@ static void cortexr_halt_resume(target_s *const target, const bool step) status = cortex_dbg_read32(target, CORTEXR_DBG_DSCR); } +/* + * This function creates the target description XML substring for the FPU (VFPv2) on + * a Cortex-R part. This has the same rationale as the function below. + * + * The string it creates is conceptually the following: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ +static size_t cortexr_build_target_fpu_description(char *const buffer, size_t max_length) +{ + size_t print_size = max_length; + /* Terminate the previous feature block and start the new */ + int offset = snprintf(buffer, print_size, ""); + + /* Build the FPU general purpose register descriptions for d0-d15 */ + for (uint8_t i = 0; i < 16U; ++i) { + if (max_length != 0) + print_size = max_length - (size_t)offset; + + offset += snprintf(buffer + offset, print_size, "", i); + } + + /* Build the FPU status/control register (fpscr) description */ + if (max_length != 0) + print_size = max_length - (size_t)offset; + + offset += snprintf(buffer + offset, print_size, ""); + + /* offset is now the total length of the string created, discard the sign and return it. */ + return (size_t)offset; +} + /* * This function creates the target description XML string for a Cortex-R part. * This is done this way to decrease string duplications and thus code size, @@ -622,7 +671,6 @@ static void cortexr_halt_resume(target_s *const target, const bool step) */ static size_t cortexr_build_target_description(char *const buffer, size_t max_length, const bool has_fpu) { - (void)has_fpu; size_t print_size = max_length; /* Start with the "preamble" chunks which are mostly common across targets save for 2 words. */ int offset = snprintf(buffer, print_size, "%s target %sarm%s ", @@ -648,6 +696,15 @@ static size_t cortexr_build_target_description(char *const buffer, size_t max_le gdb_reg_type_strings[type], i == 3U ? " regnum=\"25\"" : ""); } + /* Handle when the core has a FPU (VFP) */ + if (has_fpu) { + if (max_length != 0) + print_size = max_length - (size_t)offset; + + offset += cortexr_build_target_fpu_description(buffer + offset, print_size); + } + + /* Build the XML blob's termination */ if (max_length != 0) print_size = max_length - (size_t)offset; From b59c5b4f96cbcbb8b8d54e58fa612180185ed475 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 11 Aug 2023 18:33:56 +0100 Subject: [PATCH 24/50] cortexr: Implemented cortexr_regs_{read,write} based atop the save/restore functionality --- src/target/cortex.h | 1 + src/target/cortexr.c | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/src/target/cortex.h b/src/target/cortex.h index 841d2ce3096..ba9d89431de 100644 --- a/src/target/cortex.h +++ b/src/target/cortex.h @@ -69,6 +69,7 @@ #define CORTEX_REG_MSP 17U #define CORTEX_REG_PSP 18U #define CORTEX_REG_SPECIAL 19U +#define CORTEX_REG_FPCSR 49U #define CORTEX_CPUID_PARTNO_MASK 0xfff0U #define CORTEX_CPUID_REVISION_MASK 0x00f00000U diff --git a/src/target/cortexr.c b/src/target/cortexr.c index 19d340c8f31..d5b407f3bca 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -213,6 +213,9 @@ static_assert(ARRAY_LENGTH(cortexr_spr_types) == ARRAY_LENGTH(cortexr_spr_names) ); /* clang-format on */ +static void cortexr_regs_read(target_s *target, void *data); +static void cortexr_regs_write(target_s *target, const void *data); + static target_halt_reason_e cortexr_halt_poll(target_s *target, target_addr_t *watch); static void cortexr_halt_request(target_s *target); static void cortexr_halt_resume(target_s *target, bool step); @@ -479,6 +482,9 @@ bool cortexr_probe(adiv5_access_port_s *const ap, const target_addr_t base_addre DEBUG_TARGET("%s: FPU present? %s\n", __func__, core_has_fpu ? "yes" : "no"); target->regs_description = cortexr_target_description; + target->regs_read = cortexr_regs_read; + target->regs_write = cortexr_regs_write; + target->regs_size = sizeof(uint32_t) * CORTEXAR_GENERAL_REG_COUNT; if (core_has_fpu) { target->target_options |= TOPT_FLAVOUR_FLOAT; @@ -510,6 +516,32 @@ bool cortexr_probe(adiv5_access_port_s *const ap, const target_addr_t base_addre return true; } +static void cortexr_regs_read(target_s *const target, void *const data) +{ + const cortexr_priv_s *const priv = (cortexr_priv_s *)target->priv; + uint32_t *const regs = (uint32_t *)data; + /* Copy the register values out from our cache */ + memcpy(regs, priv->core_regs.r, sizeof(priv->core_regs.r)); + regs[CORTEX_REG_CPSR] = priv->core_regs.cpsr; + if (target->target_options & TOPT_FLAVOUR_FLOAT) { + memcpy(regs + CORTEXAR_GENERAL_REG_COUNT, priv->core_regs.d, sizeof(priv->core_regs.d)); + regs[CORTEX_REG_FPCSR] = priv->core_regs.fpcsr; + } +} + +static void cortexr_regs_write(target_s *const target, const void *const data) +{ + cortexr_priv_s *const priv = (cortexr_priv_s *)target->priv; + const uint32_t *const regs = (const uint32_t *)data; + /* Copy the new register values into our cache */ + memcpy(priv->core_regs.r, regs, sizeof(priv->core_regs.r)); + priv->core_regs.cpsr = regs[CORTEX_REG_CPSR]; + if (target->target_options & TOPT_FLAVOUR_FLOAT) { + memcpy(priv->core_regs.d, regs + CORTEXAR_GENERAL_REG_COUNT, sizeof(priv->core_regs.d)); + priv->core_regs.fpcsr = regs[CORTEX_REG_FPCSR]; + } +} + static void cortexr_halt_request(target_s *const target) { volatile exception_s error; From 6d4e0fc52aaad36181d759db29bdb2056c281402 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 12 Aug 2023 06:16:42 +0100 Subject: [PATCH 25/50] cortexr: Refactored the breakpoint and watchpoint definitions into the general Cortex private structure --- src/target/cortexr.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/target/cortexr.c b/src/target/cortexr.c index d5b407f3bca..fa00356825f 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -58,10 +58,6 @@ typedef struct cortexr_priv { /* Base core information */ cortex_priv_s base; - /* Watchpoint unit information */ - uint8_t hw_watchpoint_max; - /* Breakpoint unit information */ - uint8_t hw_breakpoint_max; /* Core registers cache */ struct { @@ -469,10 +465,12 @@ bool cortexr_probe(adiv5_access_port_s *const ap, const target_addr_t base_addre cortex_read_cpuid(target); /* The format of the debug identification register is described in DDI0406C §C11.11.15 pg2217 */ const uint32_t debug_id = cortex_dbg_read32(target, CORTEXR_DBG_IDR); - priv->hw_breakpoint_max = ((debug_id >> CORTEXR_DBG_IDR_BREAKPOINT_SHIFT) & CORTEXR_DBG_IDR_BREAKPOINT_MASK) + 1U; - priv->hw_watchpoint_max = ((debug_id >> CORTEXR_DBG_IDR_WATCHPOINT_SHIFT) & CORTEXR_DBG_IDR_WATCHPOINT_MASK) + 1U; + /* Reserve the last available breakpoint for our use to implement single-stepping */ + priv->base.breakpoints_available = (debug_id >> CORTEXR_DBG_IDR_BREAKPOINT_SHIFT) & CORTEXR_DBG_IDR_BREAKPOINT_MASK; + priv->base.watchpoints_available = + ((debug_id >> CORTEXR_DBG_IDR_WATCHPOINT_SHIFT) & CORTEXR_DBG_IDR_WATCHPOINT_MASK) + 1U; DEBUG_TARGET("%s %s core has %u breakpoint and %u watchpoint units available\n", target->driver, target->core, - priv->hw_breakpoint_max, priv->hw_watchpoint_max); + priv->base.breakpoints_available + 1U, priv->base.watchpoints_available); /* Probe for FP extension. */ uint32_t cpacr = cortexr_coproc_read(target, CORTEXR_CPACR); From 02f6dddfc72757c922f842e4a21ca4cace79a271 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 12 Aug 2023 06:22:23 +0100 Subject: [PATCH 26/50] cortexr: Implemented attach --- src/target/cortexr.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/target/cortexr.c b/src/target/cortexr.c index fa00356825f..0ebe345d253 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -216,6 +216,8 @@ static target_halt_reason_e cortexr_halt_poll(target_s *target, target_addr_t *w static void cortexr_halt_request(target_s *target); static void cortexr_halt_resume(target_s *target, bool step); +bool cortexr_attach(target_s *target); + static const char *cortexr_target_description(target_s *target); static void cortexr_mem_read(target_s *const target, void *const dest, const target_addr_t src, const size_t len) @@ -472,6 +474,8 @@ bool cortexr_probe(adiv5_access_port_s *const ap, const target_addr_t base_addre DEBUG_TARGET("%s %s core has %u breakpoint and %u watchpoint units available\n", target->driver, target->core, priv->base.breakpoints_available + 1U, priv->base.watchpoints_available); + target->attach = cortexr_attach; + /* Probe for FP extension. */ uint32_t cpacr = cortexr_coproc_read(target, CORTEXR_CPACR); cpacr |= CORTEXR_CPACR_CP10_FULL_ACCESS | CORTEXR_CPACR_CP11_FULL_ACCESS; @@ -514,6 +518,45 @@ bool cortexr_probe(adiv5_access_port_s *const ap, const target_addr_t base_addre return true; } +bool cortexr_attach(target_s *const target) +{ + adiv5_access_port_s *ap = cortex_ap(target); + /* Mark the DP as being in fault so error recovery will switch to this core when in multi-drop mode */ + ap->dp->fault = 1; + + /* Clear any pending fault condition (and switch to this core) */ + target_check_error(target); + + /* Try to halt the core */ + target_halt_request(target); + platform_timeout_s timeout; + platform_timeout_set(&timeout, 250); + target_halt_reason_e reason = TARGET_HALT_RUNNING; + while (!platform_timeout_is_expired(&timeout) && reason == TARGET_HALT_RUNNING) + reason = target_halt_poll(target, NULL); + if (reason != TARGET_HALT_REQUEST) { + DEBUG_ERROR("Failed to halt the core\n"); + return false; + } + + cortexr_priv_s *const priv = (cortexr_priv_s *)target->priv; + /* Clear any stale breakpoints */ + priv->base.breakpoints_mask = 0U; + for (size_t i = 0; i <= priv->base.breakpoints_available; ++i) { + cortex_dbg_write32(target, CORTEXR_DBG_BVR + (i << 2U), 0U); + cortex_dbg_write32(target, CORTEXR_DBG_BCR + (i << 2U), 0U); + } + + /* Clear any stale watchpoints */ + priv->base.watchpoints_mask = 0U; + for (size_t i = 0; i < priv->base.watchpoints_available; ++i) { + cortex_dbg_write32(target, CORTEXR_DBG_WVR + (i << 2U), 0U); + cortex_dbg_write32(target, CORTEXR_DBG_WCR + (i << 2U), 0U); + } + + return true; +} + static void cortexr_regs_read(target_s *const target, void *const data) { const cortexr_priv_s *const priv = (cortexr_priv_s *)target->priv; From 3379fd3f5f4577da94aa9899e056960cdc86cf56 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 13 Aug 2023 05:13:37 +0100 Subject: [PATCH 27/50] cortexr: Implemented detach --- src/target/cortexr.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/target/cortexr.c b/src/target/cortexr.c index 0ebe345d253..e1f2a047cb4 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -217,6 +217,7 @@ static void cortexr_halt_request(target_s *target); static void cortexr_halt_resume(target_s *target, bool step); bool cortexr_attach(target_s *target); +void cortexr_detach(target_s *target); static const char *cortexr_target_description(target_s *target); @@ -475,6 +476,7 @@ bool cortexr_probe(adiv5_access_port_s *const ap, const target_addr_t base_addre priv->base.breakpoints_available + 1U, priv->base.watchpoints_available); target->attach = cortexr_attach; + target->detach = cortexr_detach; /* Probe for FP extension. */ uint32_t cpacr = cortexr_coproc_read(target, CORTEXR_CPACR); @@ -557,6 +559,25 @@ bool cortexr_attach(target_s *const target) return true; } +void cortexr_detach(target_s *const target) +{ + const cortexr_priv_s *const priv = (cortexr_priv_s *)target->priv; + + /* Clear any set breakpoints */ + for (size_t i = 0; i <= priv->base.breakpoints_available; ++i) { + cortex_dbg_write32(target, CORTEXR_DBG_BVR + (i << 2U), 0U); + cortex_dbg_write32(target, CORTEXR_DBG_BCR + (i << 2U), 0U); + } + + /* Clear any set watchpoints */ + for (size_t i = 0; i < priv->base.watchpoints_available; ++i) { + cortex_dbg_write32(target, CORTEXR_DBG_WVR + (i << 2U), 0U); + cortex_dbg_write32(target, CORTEXR_DBG_WCR + (i << 2U), 0U); + } + + target_halt_resume(target, false); +} + static void cortexr_regs_read(target_s *const target, void *const data) { const cortexr_priv_s *const priv = (cortexr_priv_s *)target->priv; From 0b221900cd04f0a31b0f19c2b356e1476aa0d5b5 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Mon, 14 Aug 2023 08:03:27 +0100 Subject: [PATCH 28/50] cortexr: Implemented single register IO --- src/target/cortexr.c | 64 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/src/target/cortexr.c b/src/target/cortexr.c index e1f2a047cb4..003d6bd1fe3 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -211,6 +211,8 @@ static_assert(ARRAY_LENGTH(cortexr_spr_types) == ARRAY_LENGTH(cortexr_spr_names) static void cortexr_regs_read(target_s *target, void *data); static void cortexr_regs_write(target_s *target, const void *data); +static ssize_t cortexr_reg_read(target_s *target, uint32_t reg, void *data, size_t max); +static ssize_t cortexr_reg_write(target_s *target, uint32_t reg, const void *data, size_t max); static target_halt_reason_e cortexr_halt_poll(target_s *target, target_addr_t *watch); static void cortexr_halt_request(target_s *target); @@ -488,6 +490,8 @@ bool cortexr_probe(adiv5_access_port_s *const ap, const target_addr_t base_addre target->regs_description = cortexr_target_description; target->regs_read = cortexr_regs_read; target->regs_write = cortexr_regs_write; + target->reg_read = cortexr_reg_read; + target->reg_write = cortexr_reg_write; target->regs_size = sizeof(uint32_t) * CORTEXAR_GENERAL_REG_COUNT; if (core_has_fpu) { @@ -604,6 +608,66 @@ static void cortexr_regs_write(target_s *const target, const void *const data) } } +static void *cortexr_reg_ptr(target_s *const target, const size_t reg) +{ + cortexr_priv_s *const priv = (cortexr_priv_s *)target->priv; + /* r0-r15 */ + if (reg < 16U) + return &priv->core_regs.r[reg]; + /* cpsr */ + if (reg == 16U) + return &priv->core_regs.cpsr; + /* Check if the core has a FPU first */ + if (!(target->target_options & TOPT_FLAVOUR_FLOAT)) + return NULL; + /* d0-d15 */ + if (reg < 33U) + return &priv->core_regs.d[reg - CORTEXAR_GENERAL_REG_COUNT]; + /* fpcsr */ + if (reg == 33U) + return &priv->core_regs.fpcsr; + return NULL; +} + +static size_t cortexr_reg_width(const size_t reg) +{ + /* r0-r15, cpsr, fpcsr */ + if (reg < CORTEXAR_GENERAL_REG_COUNT || reg == 33U) + return 4U; + /* d0-d15 */ + return 8U; +} + +static ssize_t cortexr_reg_read(target_s *const target, const uint32_t reg, void *const data, const size_t max) +{ + /* Try to get a pointer to the storage for the requested register, and return -1 if that fails */ + const void *const reg_ptr = cortexr_reg_ptr(target, reg); + if (!reg_ptr) + return -1; + /* Now we have a valid register, get its width in bytes, and check that against max */ + const size_t reg_width = cortexr_reg_width(reg); + if (max < reg_width) + return -1; + /* Finally, copy the register data out and return the width */ + memcpy(data, reg_ptr, reg_width); + return reg_width; +} + +static ssize_t cortexr_reg_write(target_s *const target, const uint32_t reg, const void *const data, const size_t max) +{ + /* Try to get a poitner to the storage for the requested register, and return -1 if that fails */ + void *const reg_ptr = cortexr_reg_ptr(target, reg); + if (!reg_ptr) + return -1; + /* Now we have a valid register, get its width in bytes, and check that against max */ + const size_t reg_width = cortexr_reg_width(reg); + if (max < reg_width) + return -1; + /* Finally, copy the new register data in and return the width */ + memcpy(reg_ptr, data, reg_width); + return reg_width; +} + static void cortexr_halt_request(target_s *const target) { volatile exception_s error; From bf691ed69cd843423f32188acbf70336bf488f49 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 29 Sep 2023 01:30:33 +0100 Subject: [PATCH 29/50] cortexr: Implemented support for hardware breakpoints --- src/target/cortexr.c | 62 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/src/target/cortexr.c b/src/target/cortexr.c index 003d6bd1fe3..235f8eb7812 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -112,6 +112,11 @@ typedef struct cortexr_priv { #define CORTEXR_DBG_DRCR_CLR_STICKY_PIPEADV (1U << 3U) #define CORTEXR_DBG_DRCR_CANCEL_BUS_REQ (1U << 4U) +#define CORTEXR_DBG_BCR_ENABLE 0x00000001U +#define CORTEXR_DBG_BCR_TYPE_UNLINKED_INSN_MATCH 0x00000000U +#define CORTEXR_DBG_BCR_TYPE_UNLINKED_INSN_MISMATCH 0x00400000U +#define CORTEXR_DBG_BCR_ALL_MODES 0x00002006U + /* * Instruction encodings for reading/writing the program counter to/from r0, * reading/writing CPSR to/from r0, and reading/writing the SPSRs to/from r0. @@ -218,6 +223,9 @@ static target_halt_reason_e cortexr_halt_poll(target_s *target, target_addr_t *w static void cortexr_halt_request(target_s *target); static void cortexr_halt_resume(target_s *target, bool step); +static int cortexr_breakwatch_set(target_s *target, breakwatch_s *breakwatch); +static int cortexr_breakwatch_clear(target_s *target, breakwatch_s *breakwatch); + bool cortexr_attach(target_s *target); void cortexr_detach(target_s *target); @@ -500,6 +508,9 @@ bool cortexr_probe(adiv5_access_port_s *const ap, const target_addr_t base_addre cortexr_float_regs_save(target); } + target->breakwatch_set = cortexr_breakwatch_set; + target->breakwatch_clear = cortexr_breakwatch_clear; + /* Check cache type */ const uint32_t cache_type = cortex_dbg_read32(target, CORTEXR_CTR); if (cache_type >> CORTEX_CTR_FORMAT_SHIFT == CORTEX_CTR_FORMAT_ARMv7) { @@ -746,6 +757,57 @@ static void cortexr_halt_resume(target_s *const target, const bool step) status = cortex_dbg_read32(target, CORTEXR_DBG_DSCR); } +static int cortexr_breakwatch_set(target_s *const target, breakwatch_s *const breakwatch) +{ + cortexr_priv_s *const priv = (cortexr_priv_s *)target->priv; + + switch (breakwatch->type) { + case TARGET_BREAK_HARD: { + /* First try and find a unused breakpoint slot */ + size_t breakpoint = 0; + for (; breakpoint < priv->base.breakpoints_available; ++breakpoint) { + /* Check if the slot is presently in use, breaking if it is not */ + if (!(priv->base.breakpoints_mask & (1U << breakpoint))) + break; + } + /* If none was available, return an error */ + if (breakpoint == priv->base.breakpoints_available) + return -1; + + /* Set the breakpoint slot up and mark it used */ + cortex_dbg_write32(target, CORTEXR_DBG_BVR + (breakpoint << 2U), breakwatch->addr); + cortex_dbg_write32(target, CORTEXR_DBG_BCR + (breakpoint << 2U), + CORTEXR_DBG_BCR_ENABLE | CORTEXR_DBG_BCR_TYPE_UNLINKED_INSN_MATCH | CORTEXR_DBG_BCR_ALL_MODES); + priv->base.breakpoints_mask |= 1U << breakpoint; + breakwatch->reserved[0] = breakpoint; + /* Tell the debugger that it was successfully able to set the breakpoint */ + return 0; + } + default: + /* If the breakwatch type is not one of the above, tell the debugger we don't support it */ + return 1; + } +} + +static int cortexr_breakwatch_clear(target_s *const target, breakwatch_s *const breakwatch) +{ + cortexr_priv_s *const priv = (cortexr_priv_s *)target->priv; + + switch (breakwatch->type) { + case TARGET_BREAK_HARD: { + /* Clear the breakpoint slot this used */ + const size_t breakpoint = breakwatch->reserved[0]; + cortex_dbg_write32(target, CORTEXR_DBG_BCR + (breakpoint << 2U), 0); + priv->base.breakpoints_mask &= ~(1U << breakpoint); + /* Tell the debugger that it was successfully able to clear the breakpoint */ + return 0; + } + default: + /* If the breakwatch type is not one of the above, tell the debugger wed on't support it */ + return 1; + } +} + /* * This function creates the target description XML substring for the FPU (VFPv2) on * a Cortex-R part. This has the same rationale as the function below. From 1afefa72b28178fb9ba2cad3faccd8509c574f24 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 29 Sep 2023 01:39:42 +0100 Subject: [PATCH 30/50] cortexr: Implemented support for single-stepping the core --- src/target/cortexr.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/target/cortexr.c b/src/target/cortexr.c index 235f8eb7812..6c7fd04aaf5 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -742,10 +742,23 @@ static target_halt_reason_e cortexr_halt_poll(target_s *const target, target_add static void cortexr_halt_resume(target_s *const target, const bool step) { - (void)step; + cortexr_priv_s *const priv = (cortexr_priv_s *)target->priv; /* Restore the core's registers so the running program doesn't know we've been in there */ cortexr_regs_restore(target); + /* + * If we're setting up to single-step the core, configure the final breakpoint slot approrpriately. + * We always keep the final supported breakpoint reserved for this purpose so + * `priv->base.breakpoints_available` represents this slot index. + */ + if (step) { + const size_t breakpoint = priv->base.breakpoints_available << 2U; + cortex_dbg_write32(target, CORTEXR_DBG_BVR + breakpoint, priv->core_regs.r[CORTEX_REG_PC] & ~3U); + cortex_dbg_write32(target, CORTEXR_DBG_BCR + breakpoint, + CORTEXR_DBG_BCR_ENABLE | CORTEXR_DBG_BCR_TYPE_UNLINKED_INSN_MISMATCH | CORTEXR_DBG_BCR_ALL_MODES); + } else + cortex_dbg_write32(target, CORTEXR_DBG_BCR + (priv->base.breakpoints_available << 2U), 0U); + /* Ask to resume the core */ cortex_dbg_write32(target, CORTEXR_DBG_DRCR, CORTEXR_DBG_DRCR_CLR_STICKY_EXC | CORTEXR_DBG_DRCR_RESTART_REQ); From b4aacb14e0239bcb097b215b766c1717908d3acd Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 30 Sep 2023 00:34:09 +0100 Subject: [PATCH 31/50] cortexr: Enable halting debug mode when the processor halts --- src/target/cortexr.c | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/target/cortexr.c b/src/target/cortexr.c index 6c7fd04aaf5..bb98dd0ee49 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -91,20 +91,21 @@ typedef struct cortexr_priv { #define CORTEXR_DBG_IDR_WATCHPOINT_MASK 0xfU #define CORTEXR_DBG_IDR_WATCHPOINT_SHIFT 28U -#define CORTEXR_DBG_DSCR_HALTED (1U << 0U) -#define CORTEXR_DBG_DSCR_RESTARTED (1U << 1U) -#define CORTEXR_DBG_DSCR_MOE_MASK 0x0000003cU -#define CORTEXR_DBG_DSCR_MOE_HALT_REQUEST 0x00000000U -#define CORTEXR_DBG_DSCR_MOE_BREAKPOINT 0x00000004U -#define CORTEXR_DBG_DSCR_MOE_ASYNC_WATCH 0x00000008U -#define CORTEXR_DBG_DSCR_MOE_BKPT_INSN 0x0000000cU -#define CORTEXR_DBG_DSCR_MOE_EXTERNAL_DBG 0x00000010U -#define CORTEXR_DBG_DSCR_MOE_VEC_CATCH 0x00000014U -#define CORTEXR_DBG_DSCR_MOE_SYNC_WATCH 0x00000028U -#define CORTEXR_DBG_DSCR_ITR_ENABLE (1U << 13U) -#define CORTEXR_DBG_DSCR_INSN_COMPLETE (1U << 24U) -#define CORTEXR_DBG_DSCR_DTR_READ_READY (1U << 29U) -#define CORTEXR_DBG_DSCR_DTR_WRITE_DONE (1U << 30U) +#define CORTEXR_DBG_DSCR_HALTED (1U << 0U) +#define CORTEXR_DBG_DSCR_RESTARTED (1U << 1U) +#define CORTEXR_DBG_DSCR_MOE_MASK 0x0000003cU +#define CORTEXR_DBG_DSCR_MOE_HALT_REQUEST 0x00000000U +#define CORTEXR_DBG_DSCR_MOE_BREAKPOINT 0x00000004U +#define CORTEXR_DBG_DSCR_MOE_ASYNC_WATCH 0x00000008U +#define CORTEXR_DBG_DSCR_MOE_BKPT_INSN 0x0000000cU +#define CORTEXR_DBG_DSCR_MOE_EXTERNAL_DBG 0x00000010U +#define CORTEXR_DBG_DSCR_MOE_VEC_CATCH 0x00000014U +#define CORTEXR_DBG_DSCR_MOE_SYNC_WATCH 0x00000028U +#define CORTEXR_DBG_DSCR_ITR_ENABLE (1U << 13U) +#define CORTEXR_DBG_DSCR_HALTING_DBG_ENABLE (1U << 14U) +#define CORTEXR_DBG_DSCR_INSN_COMPLETE (1U << 24U) +#define CORTEXR_DBG_DSCR_DTR_READ_READY (1U << 29U) +#define CORTEXR_DBG_DSCR_DTR_WRITE_DONE (1U << 30U) #define CORTEXR_DBG_DRCR_HALT_REQ (1U << 0U) #define CORTEXR_DBG_DRCR_RESTART_REQ (1U << 1U) @@ -711,8 +712,9 @@ static target_halt_reason_e cortexr_halt_poll(target_s *const target, target_add if (!(dscr & CORTEXR_DBG_DSCR_HALTED)) return TARGET_HALT_RUNNING; - /* Make sure ITR is enabled */ - cortex_dbg_write32(target, CORTEXR_DBG_DSCR, dscr | CORTEXR_DBG_DSCR_ITR_ENABLE); + /* Make sure ITR is enabled and likewise halting debug (so breakpoints work) */ + cortex_dbg_write32( + target, CORTEXR_DBG_DSCR, dscr | CORTEXR_DBG_DSCR_ITR_ENABLE | CORTEXR_DBG_DSCR_HALTING_DBG_ENABLE); /* Save the target core's registers as debugging operations clobber them */ cortexr_regs_save(target); From 290a5c77e5e4f9ac42423c86fe3f2e93df1317eb Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 30 Sep 2023 00:36:43 +0100 Subject: [PATCH 32/50] cortexr: Refactored out the breakpoint setup logic into its own function --- src/target/cortexr.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/target/cortexr.c b/src/target/cortexr.c index bb98dd0ee49..f0aef713aa6 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -226,6 +226,7 @@ static void cortexr_halt_resume(target_s *target, bool step); static int cortexr_breakwatch_set(target_s *target, breakwatch_s *breakwatch); static int cortexr_breakwatch_clear(target_s *target, breakwatch_s *breakwatch); +static void cortexr_config_breakpoint(target_s *target, size_t slot, uint32_t mode, target_addr_t addr); bool cortexr_attach(target_s *target); void cortexr_detach(target_s *target); @@ -753,12 +754,10 @@ static void cortexr_halt_resume(target_s *const target, const bool step) * We always keep the final supported breakpoint reserved for this purpose so * `priv->base.breakpoints_available` represents this slot index. */ - if (step) { - const size_t breakpoint = priv->base.breakpoints_available << 2U; - cortex_dbg_write32(target, CORTEXR_DBG_BVR + breakpoint, priv->core_regs.r[CORTEX_REG_PC] & ~3U); - cortex_dbg_write32(target, CORTEXR_DBG_BCR + breakpoint, - CORTEXR_DBG_BCR_ENABLE | CORTEXR_DBG_BCR_TYPE_UNLINKED_INSN_MISMATCH | CORTEXR_DBG_BCR_ALL_MODES); - } else + if (step) + cortexr_config_breakpoint(target, priv->base.breakpoints_available, CORTEXR_DBG_BCR_TYPE_UNLINKED_INSN_MISMATCH, + priv->core_regs.r[CORTEX_REG_PC]); + else cortex_dbg_write32(target, CORTEXR_DBG_BCR + (priv->base.breakpoints_available << 2U), 0U); /* Ask to resume the core */ @@ -772,6 +771,14 @@ static void cortexr_halt_resume(target_s *const target, const bool step) status = cortex_dbg_read32(target, CORTEXR_DBG_DSCR); } +static void cortexr_config_breakpoint( + target_s *const target, const size_t slot, const uint32_t mode, const target_addr_t addr) +{ + cortex_dbg_write32(target, CORTEXR_DBG_BVR + (slot << 2U), addr & ~3U); + cortex_dbg_write32( + target, CORTEXR_DBG_BCR + (slot << 2U), CORTEXR_DBG_BCR_ENABLE | CORTEXR_DBG_BCR_ALL_MODES | mode); +} + static int cortexr_breakwatch_set(target_s *const target, breakwatch_s *const breakwatch) { cortexr_priv_s *const priv = (cortexr_priv_s *)target->priv; @@ -790,9 +797,7 @@ static int cortexr_breakwatch_set(target_s *const target, breakwatch_s *const br return -1; /* Set the breakpoint slot up and mark it used */ - cortex_dbg_write32(target, CORTEXR_DBG_BVR + (breakpoint << 2U), breakwatch->addr); - cortex_dbg_write32(target, CORTEXR_DBG_BCR + (breakpoint << 2U), - CORTEXR_DBG_BCR_ENABLE | CORTEXR_DBG_BCR_TYPE_UNLINKED_INSN_MATCH | CORTEXR_DBG_BCR_ALL_MODES); + cortexr_config_breakpoint(target, breakpoint, CORTEXR_DBG_BCR_TYPE_UNLINKED_INSN_MATCH, breakwatch->addr); priv->base.breakpoints_mask |= 1U << breakpoint; breakwatch->reserved[0] = breakpoint; /* Tell the debugger that it was successfully able to set the breakpoint */ From 74c87233526bc954d6355e800296f535c54fc49b Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 30 Sep 2023 01:45:27 +0100 Subject: [PATCH 33/50] cortexr: Implemented handling for ARM vs Thumb breakpoints --- src/target/cortexr.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/target/cortexr.c b/src/target/cortexr.c index f0aef713aa6..9bf89a49ede 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -117,6 +117,9 @@ typedef struct cortexr_priv { #define CORTEXR_DBG_BCR_TYPE_UNLINKED_INSN_MATCH 0x00000000U #define CORTEXR_DBG_BCR_TYPE_UNLINKED_INSN_MISMATCH 0x00400000U #define CORTEXR_DBG_BCR_ALL_MODES 0x00002006U +#define CORTEXR_DBG_BCR_BYTE_SELECT_ALL 0x000001e0U +#define CORTEXR_DBG_BCR_BYTE_SELECT_LOW_HALF 0x00000060U +#define CORTEXR_DBG_BCR_BYTE_SELECT_HIGH_HALF 0x00000180U /* * Instruction encodings for reading/writing the program counter to/from r0, @@ -755,7 +758,8 @@ static void cortexr_halt_resume(target_s *const target, const bool step) * `priv->base.breakpoints_available` represents this slot index. */ if (step) - cortexr_config_breakpoint(target, priv->base.breakpoints_available, CORTEXR_DBG_BCR_TYPE_UNLINKED_INSN_MISMATCH, + cortexr_config_breakpoint(target, priv->base.breakpoints_available, + CORTEXR_DBG_BCR_TYPE_UNLINKED_INSN_MISMATCH | ((priv->core_regs.cpsr & CORTEXR_CPSR_THUMB) ? 2 : 4), priv->core_regs.r[CORTEX_REG_PC]); else cortex_dbg_write32(target, CORTEXR_DBG_BCR + (priv->base.breakpoints_available << 2U), 0U); @@ -772,11 +776,22 @@ static void cortexr_halt_resume(target_s *const target, const bool step) } static void cortexr_config_breakpoint( - target_s *const target, const size_t slot, const uint32_t mode, const target_addr_t addr) + target_s *const target, const size_t slot, uint32_t mode, const target_addr_t addr) { + /* + * Figure out if the breakpoint is for an ARM or Thumb instruction and which + * part of the lowest 2 bits of the address to match + how + */ + const bool thumb_breakpoint = (mode & 7U) == 2U; + if (thumb_breakpoint) + mode |= (addr & 2U) ? CORTEXR_DBG_BCR_BYTE_SELECT_HIGH_HALF : CORTEXR_DBG_BCR_BYTE_SELECT_LOW_HALF; + else + mode |= CORTEXR_DBG_BCR_BYTE_SELECT_ALL; + + /* Configure the breakpoint slot */ cortex_dbg_write32(target, CORTEXR_DBG_BVR + (slot << 2U), addr & ~3U); cortex_dbg_write32( - target, CORTEXR_DBG_BCR + (slot << 2U), CORTEXR_DBG_BCR_ENABLE | CORTEXR_DBG_BCR_ALL_MODES | mode); + target, CORTEXR_DBG_BCR + (slot << 2U), CORTEXR_DBG_BCR_ENABLE | CORTEXR_DBG_BCR_ALL_MODES | (mode & ~7U)); } static int cortexr_breakwatch_set(target_s *const target, breakwatch_s *const breakwatch) @@ -797,7 +812,8 @@ static int cortexr_breakwatch_set(target_s *const target, breakwatch_s *const br return -1; /* Set the breakpoint slot up and mark it used */ - cortexr_config_breakpoint(target, breakpoint, CORTEXR_DBG_BCR_TYPE_UNLINKED_INSN_MATCH, breakwatch->addr); + cortexr_config_breakpoint( + target, breakpoint, CORTEXR_DBG_BCR_TYPE_UNLINKED_INSN_MATCH | (breakwatch->size & 7U), breakwatch->addr); priv->base.breakpoints_mask |= 1U << breakpoint; breakwatch->reserved[0] = breakpoint; /* Tell the debugger that it was successfully able to set the breakpoint */ From 85dbcdd11422e6847716c2c4e68fcce8c9bf83af Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 30 Sep 2023 04:51:24 +0100 Subject: [PATCH 34/50] cortexr: Implemented support for watchpoints --- src/target/cortexr.c | 72 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/src/target/cortexr.c b/src/target/cortexr.c index 9bf89a49ede..9721ec039f9 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -121,6 +121,15 @@ typedef struct cortexr_priv { #define CORTEXR_DBG_BCR_BYTE_SELECT_LOW_HALF 0x00000060U #define CORTEXR_DBG_BCR_BYTE_SELECT_HIGH_HALF 0x00000180U +#define CORTEXR_DBG_WCR_ENABLE 0x00000001U +#define CORTEXR_DBG_WCR_MATCH_ON_LOAD 0x00000008U +#define CORTEXR_DBG_WCR_MATCH_ON_STORE 0x00000010U +#define CORTEXR_DBG_WCR_MATCH_ANY_ACCESS 0x00000018U +#define CORTEXR_DBG_WCR_ALL_MODES 0x00002006U +#define CORTEXR_DBG_WCR_BYTE_SELECT_OFFSET 5U +#define CORTEXR_DBG_WCR_BYTE_SELECT_MASK 0x00001fe0U +#define CORTEXR_DBG_WCR_BYTE_SELECT(x) (((x) << CORTEXR_DBG_WCR_BYTE_SELECT_OFFSET) & CORTEXR_DBG_WCR_BYTE_SELECT_MASK) + /* * Instruction encodings for reading/writing the program counter to/from r0, * reading/writing CPSR to/from r0, and reading/writing the SPSRs to/from r0. @@ -794,6 +803,38 @@ static void cortexr_config_breakpoint( target, CORTEXR_DBG_BCR + (slot << 2U), CORTEXR_DBG_BCR_ENABLE | CORTEXR_DBG_BCR_ALL_MODES | (mode & ~7U)); } +static uint32_t cortexr_watchpoint_mode(const target_breakwatch_e type) +{ + switch (type) { + case TARGET_WATCH_READ: + return CORTEXR_DBG_WCR_MATCH_ON_LOAD; + case TARGET_WATCH_WRITE: + return CORTEXR_DBG_WCR_MATCH_ON_STORE; + case TARGET_WATCH_ACCESS: + return CORTEXR_DBG_WCR_MATCH_ANY_ACCESS; + default: + return 0U; + } +} + +static void cortexr_config_watchpoint(target_s *const target, const size_t slot, const breakwatch_s *const breakwatch) +{ + /* + * Construct the access and bytes masks - starting with the bytes mask which uses the fact + * that any `(1 << N) - 1` will result in all the bits less than the Nth being set. + * The DBG_WCR BAS field is a bit-per-byte bitfield, so to match on two bytes, one has to program + * it with 0b11 somewhere in its length, and for four bytes that's 0b1111. + * Which set of bits need to be 1's depends on the address low bits. + */ + const uint32_t byte_mask = ((1U << breakwatch->size) - 1U) << (breakwatch->addr & 3U); + const uint32_t mode = cortexr_watchpoint_mode(breakwatch->type) | CORTEXR_DBG_WCR_BYTE_SELECT(byte_mask); + + /* Configure the watchpoint slot */ + cortex_dbg_write32(target, CORTEXR_DBG_WVR + (slot << 2U), breakwatch->addr & ~3U); + cortex_dbg_write32( + target, CORTEXR_DBG_WCR + (slot << 2U), CORTEXR_DBG_WCR_ENABLE | CORTEXR_DBG_WCR_ALL_MODES | mode); +} + static int cortexr_breakwatch_set(target_s *const target, breakwatch_s *const breakwatch) { cortexr_priv_s *const priv = (cortexr_priv_s *)target->priv; @@ -819,6 +860,27 @@ static int cortexr_breakwatch_set(target_s *const target, breakwatch_s *const br /* Tell the debugger that it was successfully able to set the breakpoint */ return 0; } + case TARGET_WATCH_READ: + case TARGET_WATCH_WRITE: + case TARGET_WATCH_ACCESS: { + /* First try and find an unused watchpoint slot */ + size_t watchpoint = 0; + for (; watchpoint < priv->base.watchpoints_available; ++watchpoint) { + /* Check if the slot is presently in use, breaking if it is not */ + if (!(priv->base.watchpoints_mask & (1U << watchpoint))) + break; + } + /* If none was available, return an error */ + if (watchpoint == priv->base.watchpoints_available) + return -1; + + /* Set the watchpoint slot up and mark it used */ + cortexr_config_watchpoint(target, watchpoint, breakwatch); + priv->base.watchpoints_mask |= 1U << watchpoint; + breakwatch->reserved[0] = watchpoint; + /* Tell the debugger that it was successfully able to set the watchpoint */ + return 0; + } default: /* If the breakwatch type is not one of the above, tell the debugger we don't support it */ return 1; @@ -838,6 +900,16 @@ static int cortexr_breakwatch_clear(target_s *const target, breakwatch_s *const /* Tell the debugger that it was successfully able to clear the breakpoint */ return 0; } + case TARGET_WATCH_READ: + case TARGET_WATCH_WRITE: + case TARGET_WATCH_ACCESS: { + /* Clear the watchpoint slot this used */ + const size_t watchpoint = breakwatch->reserved[0]; + cortex_dbg_write32(target, CORTEXR_DBG_WCR + (watchpoint << 2U), 0); + priv->base.watchpoints_mask &= ~(1U << watchpoint); + /* Tell the debugger that it was successfully able to clear the watchpoint */ + return 0; + } default: /* If the breakwatch type is not one of the above, tell the debugger wed on't support it */ return 1; From 706fd859b8cc968a352e0603e73d765f991f64e7 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 7 Oct 2023 07:28:19 +0100 Subject: [PATCH 35/50] cortexr: Implemented handling for disabling interrupts during single stepping and handling the ITR enable bit properly --- src/target/cortexr.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/target/cortexr.c b/src/target/cortexr.c index 9721ec039f9..c1aef4f21f5 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -101,6 +101,7 @@ typedef struct cortexr_priv { #define CORTEXR_DBG_DSCR_MOE_EXTERNAL_DBG 0x00000010U #define CORTEXR_DBG_DSCR_MOE_VEC_CATCH 0x00000014U #define CORTEXR_DBG_DSCR_MOE_SYNC_WATCH 0x00000028U +#define CORTEXR_DBG_DSCR_INTERRUPT_DISABLE (1U << 11U) #define CORTEXR_DBG_DSCR_ITR_ENABLE (1U << 13U) #define CORTEXR_DBG_DSCR_HALTING_DBG_ENABLE (1U << 14U) #define CORTEXR_DBG_DSCR_INSN_COMPLETE (1U << 24U) @@ -761,18 +762,24 @@ static void cortexr_halt_resume(target_s *const target, const bool step) /* Restore the core's registers so the running program doesn't know we've been in there */ cortexr_regs_restore(target); + uint32_t dscr = cortex_dbg_read32(target, CORTEXR_DBG_DSCR); /* * If we're setting up to single-step the core, configure the final breakpoint slot approrpriately. * We always keep the final supported breakpoint reserved for this purpose so * `priv->base.breakpoints_available` represents this slot index. + * Additionally, adjust DSCR to disable interrupts as necessary. */ - if (step) + if (step) { cortexr_config_breakpoint(target, priv->base.breakpoints_available, CORTEXR_DBG_BCR_TYPE_UNLINKED_INSN_MISMATCH | ((priv->core_regs.cpsr & CORTEXR_CPSR_THUMB) ? 2 : 4), priv->core_regs.r[CORTEX_REG_PC]); - else + dscr |= CORTEXR_DBG_DSCR_INTERRUPT_DISABLE; + } else { cortex_dbg_write32(target, CORTEXR_DBG_BCR + (priv->base.breakpoints_available << 2U), 0U); + dscr &= ~CORTEXR_DBG_DSCR_INTERRUPT_DISABLE; + } + cortex_dbg_write32(target, CORTEXR_DBG_DSCR, dscr & ~CORTEXR_DBG_DSCR_ITR_ENABLE); /* Ask to resume the core */ cortex_dbg_write32(target, CORTEXR_DBG_DRCR, CORTEXR_DBG_DRCR_CLR_STICKY_EXC | CORTEXR_DBG_DRCR_RESTART_REQ); From c6c78fb76ef1c162d7559b2a1ac25187b774ea49 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 7 Oct 2023 07:30:20 +0100 Subject: [PATCH 36/50] cortexr: Implemented readout of the processor features to check for the security and virt extensions --- src/target/cortexr.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/target/cortexr.c b/src/target/cortexr.c index c1aef4f21f5..3919735a0fc 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -85,6 +85,7 @@ typedef struct cortexr_priv { #define CORTEXR_CPUID 0xd00U #define CORTEXR_CTR 0xd04U +#define CORTEXR_PFR1 0xd24U #define CORTEXR_DBG_IDR_BREAKPOINT_MASK 0xfU #define CORTEXR_DBG_IDR_BREAKPOINT_SHIFT 24U @@ -196,7 +197,12 @@ static const uint16_t cortexr_spsr_encodings[5] = { #define CORTEXR_CPACR_CP10_FULL_ACCESS 0x00300000U #define CORTEXR_CPACR_CP11_FULL_ACCESS 0x00c00000U -#define TOPT_FLAVOUR_FLOAT (1U << 1U) /* If set, core has a hardware FPU */ +#define CORTEXR_PFR1_SEC_EXT_MASK 0x000000f0U +#define CORTEXR_PFR1_VIRT_EXT_MASK 0x0000f000U + +#define TOPT_FLAVOUR_FLOAT (1U << 1U) /* If set, core has a hardware FPU */ +#define TOPT_FLAVOUR_SEC_EXT (1U << 2U) /* If set, core has security extensions */ +#define TOPT_FLAVOUR_VIRT_EXT (1U << 3U) /* If set, core has virtualisation extensions */ /* * Fields for Cortex-R special-purpose registers, used in the generation of GDB's target description XML. @@ -500,6 +506,17 @@ bool cortexr_probe(adiv5_access_port_s *const ap, const target_addr_t base_addre DEBUG_TARGET("%s %s core has %u breakpoint and %u watchpoint units available\n", target->driver, target->core, priv->base.breakpoints_available + 1U, priv->base.watchpoints_available); + /* Read out processor feature register 1 and check for the security and virtualisation extensions */ + const uint32_t proc_features = cortex_dbg_read32(target, CORTEXR_PFR1); + if (proc_features & CORTEXR_PFR1_SEC_EXT_MASK) { + target->target_options |= TOPT_FLAVOUR_SEC_EXT; + DEBUG_TARGET("%s: Core has security extensions\n", __func__); + } + if (proc_features & CORTEXR_PFR1_VIRT_EXT_MASK) { + target->target_options |= TOPT_FLAVOUR_VIRT_EXT; + DEBUG_TARGET("%s: Core has virtualisation extensions\n", __func__); + } + target->attach = cortexr_attach; target->detach = cortexr_detach; From 465d77e7267034d6ba2fe9260a6006c7c233dc54 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 7 Oct 2023 09:39:12 +0100 Subject: [PATCH 37/50] cortexr: Implemented readout of the memory model to configure how we talk with the target --- src/target/cortexr.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/target/cortexr.c b/src/target/cortexr.c index 3919735a0fc..68ee956173f 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -86,6 +86,7 @@ typedef struct cortexr_priv { #define CORTEXR_CPUID 0xd00U #define CORTEXR_CTR 0xd04U #define CORTEXR_PFR1 0xd24U +#define CORTEXR_MMFR0 0xd30U #define CORTEXR_DBG_IDR_BREAKPOINT_MASK 0xfU #define CORTEXR_DBG_IDR_BREAKPOINT_SHIFT 24U @@ -200,9 +201,13 @@ static const uint16_t cortexr_spsr_encodings[5] = { #define CORTEXR_PFR1_SEC_EXT_MASK 0x000000f0U #define CORTEXR_PFR1_VIRT_EXT_MASK 0x0000f000U +#define CORTEXR_MMFR0_VMSA_MASK 0x0000000fU +#define CORTEXR_MMFR0_PMSA_MASK 0x000000f0U + #define TOPT_FLAVOUR_FLOAT (1U << 1U) /* If set, core has a hardware FPU */ #define TOPT_FLAVOUR_SEC_EXT (1U << 2U) /* If set, core has security extensions */ #define TOPT_FLAVOUR_VIRT_EXT (1U << 3U) /* If set, core has virtualisation extensions */ +#define TOPT_FLAVOUR_VIRT_MEM (1U << 4U) /* If set, core uses the virtual memory model, not protected */ /* * Fields for Cortex-R special-purpose registers, used in the generation of GDB's target description XML. @@ -517,6 +522,19 @@ bool cortexr_probe(adiv5_access_port_s *const ap, const target_addr_t base_addre DEBUG_TARGET("%s: Core has virtualisation extensions\n", __func__); } + /* + * Read out memory model feature register 0 and check for VMSA vs PMSA memory models to + * configure address translation and determine which cp15 registers we can poke. + */ + const uint32_t memory_model = cortex_dbg_read32(target, CORTEXR_MMFR0); + /* The manual says this cannot happen, if it does then assume VMSA */ + if ((memory_model & CORTEXR_MMFR0_VMSA_MASK) && (memory_model & CORTEXR_MMFR0_PMSA_MASK)) + DEBUG_ERROR("%s: Core claims to support both virtual and protected memory modes!\n", __func__); + if (memory_model & CORTEXR_MMFR0_VMSA_MASK) + target->target_options |= TOPT_FLAVOUR_VIRT_MEM; + DEBUG_TARGET( + "%s: Core uses the %cMSA memory model\n", __func__, target->target_options & TOPT_FLAVOUR_VIRT_MEM ? 'V' : 'P'); + target->attach = cortexr_attach; target->detach = cortexr_detach; From f0f40bae0e05510f22288d378c92fa58591b3bba Mon Sep 17 00:00:00 2001 From: dragonmux Date: Mon, 9 Oct 2023 14:33:28 +0100 Subject: [PATCH 38/50] cortexr: Implemented extremely basic support for handling watchpoints getting triggered --- src/target/cortexr.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/target/cortexr.c b/src/target/cortexr.c index 68ee956173f..f963beb78a4 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -780,13 +780,23 @@ static target_halt_reason_e cortexr_halt_poll(target_s *const target, target_add case CORTEXR_DBG_DSCR_MOE_VEC_CATCH: reason = TARGET_HALT_BREAKPOINT; break; - case CORTEXR_DBG_DSCR_MOE_ASYNC_WATCH: case CORTEXR_DBG_DSCR_MOE_SYNC_WATCH: - /* TODO: determine the watchpoint we hit */ - (void)watch; - reason = TARGET_HALT_WATCHPOINT; + case CORTEXR_DBG_DSCR_MOE_ASYNC_WATCH: { + const cortexr_priv_s *const priv = (cortexr_priv_s *)target->priv; + if (priv->base.watchpoints_mask == 1U) { + for (const breakwatch_s *breakwatch = target->bw_list; breakwatch; breakwatch = breakwatch->next) { + if (breakwatch->type != TARGET_WATCH_READ && breakwatch->type != TARGET_WATCH_WRITE && + breakwatch->type != TARGET_WATCH_ACCESS) + continue; + *watch = breakwatch->addr; + break; + } + reason = TARGET_HALT_WATCHPOINT; + } else + reason = TARGET_HALT_BREAKPOINT; break; } + } /* Check if we halted because we were actually single-stepping */ return reason; } From bfafbc1f6978bbbb8007267f975d52c9b04c1796 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Mon, 9 Oct 2023 14:53:29 +0100 Subject: [PATCH 39/50] cortexr: Reordered some of the target structure initialisation so things are done in a more sensible order --- src/target/cortexr.c | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/src/target/cortexr.c b/src/target/cortexr.c index f963beb78a4..1e77baf7216 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -239,6 +239,9 @@ static_assert(ARRAY_LENGTH(cortexr_spr_types) == ARRAY_LENGTH(cortexr_spr_names) ); /* clang-format on */ +static void cortexr_mem_read(target_s *target, void *dest, target_addr_t src, size_t len); +static void cortexr_mem_write(target_s *target, target_addr_t dest, const void *src, size_t len); + static void cortexr_regs_read(target_s *target, void *data); static void cortexr_regs_write(target_s *target, const void *data); static ssize_t cortexr_reg_read(target_s *target, uint32_t reg, void *data, size_t max); @@ -257,16 +260,6 @@ void cortexr_detach(target_s *target); static const char *cortexr_target_description(target_s *target); -static void cortexr_mem_read(target_s *const target, void *const dest, const target_addr_t src, const size_t len) -{ - adiv5_mem_read(cortex_ap(target), dest, src, len); -} - -static void cortexr_mem_write(target_s *const target, const target_addr_t dest, const void *const src, const size_t len) -{ - adiv5_mem_write(cortex_ap(target), dest, src, len); -} - static void cortexr_run_insn(target_s *const target, const uint32_t insn) { /* Issue the requested instruction to the core */ @@ -475,17 +468,12 @@ bool cortexr_probe(adiv5_access_port_s *const ap, const target_addr_t base_addre return false; } + target->driver = "ARM Cortex-R"; target->priv = priv; target->priv_free = cortex_priv_free; priv->base.ap = ap; priv->base.base_addr = base_address; - target->check_error = cortex_check_error; - target->mem_read = cortexr_mem_read; - target->mem_write = cortexr_mem_write; - - target->driver = "ARM Cortex-R"; - target->halt_request = cortexr_halt_request; target->halt_poll = cortexr_halt_poll; target->halt_resume = cortexr_halt_resume; @@ -558,6 +546,10 @@ bool cortexr_probe(adiv5_access_port_s *const ap, const target_addr_t base_addre cortexr_float_regs_save(target); } + target->check_error = cortex_check_error; + target->mem_read = cortexr_mem_read; + target->mem_write = cortexr_mem_write; + target->breakwatch_set = cortexr_breakwatch_set; target->breakwatch_clear = cortexr_breakwatch_clear; @@ -642,6 +634,15 @@ void cortexr_detach(target_s *const target) target_halt_resume(target, false); } +static void cortexr_mem_read(target_s *const target, void *const dest, const target_addr_t src, const size_t len) +{ + adiv5_mem_read(cortex_ap(target), dest, src, len); +} + +static void cortexr_mem_write(target_s *const target, const target_addr_t dest, const void *const src, const size_t len) +{ + adiv5_mem_write(cortex_ap(target), dest, src, len); +} static void cortexr_regs_read(target_s *const target, void *const data) { From a97666b0a1728141fb94032275b8cec177d6c712 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 10 Oct 2023 12:06:07 +0100 Subject: [PATCH 40/50] cortexr: Implemented handling for when an instruction launch that reads data hits an error --- src/target/cortexr.c | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src/target/cortexr.c b/src/target/cortexr.c index 1e77baf7216..a1eca949f51 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -103,6 +103,7 @@ typedef struct cortexr_priv { #define CORTEXR_DBG_DSCR_MOE_EXTERNAL_DBG 0x00000010U #define CORTEXR_DBG_DSCR_MOE_VEC_CATCH 0x00000014U #define CORTEXR_DBG_DSCR_MOE_SYNC_WATCH 0x00000028U +#define CORTEXR_DBG_DSCR_SYNC_DATA_ABORT (1U << 6U) #define CORTEXR_DBG_DSCR_INTERRUPT_DISABLE (1U << 11U) #define CORTEXR_DBG_DSCR_ITR_ENABLE (1U << 13U) #define CORTEXR_DBG_DSCR_HALTING_DBG_ENABLE (1U << 14U) @@ -269,17 +270,24 @@ static void cortexr_run_insn(target_s *const target, const uint32_t insn) continue; } -static uint32_t cortexr_run_read_insn(target_s *const target, const uint32_t insn) +static bool cortexr_run_read_insn(target_s *const target, const uint32_t insn, uint32_t *const result) { /* Issue the requested instruction to the core */ cortex_dbg_write32(target, CORTEXR_DBG_ITR, insn); /* Poll for the instruction to complete and the data to become ready in the DTR */ - while ((cortex_dbg_read32(target, CORTEXR_DBG_DSCR) & - (CORTEXR_DBG_DSCR_INSN_COMPLETE | CORTEXR_DBG_DSCR_DTR_READ_READY)) != - (CORTEXR_DBG_DSCR_INSN_COMPLETE | CORTEXR_DBG_DSCR_DTR_READ_READY)) - continue; - /* Read back the DTR to complete the read */ - return cortex_dbg_read32(target, CORTEXR_DBG_DTRRX); + uint32_t status = 0; + while ((status & (CORTEXR_DBG_DSCR_INSN_COMPLETE | CORTEXR_DBG_DSCR_DTR_READ_READY)) != + (CORTEXR_DBG_DSCR_INSN_COMPLETE | CORTEXR_DBG_DSCR_DTR_READ_READY)) { + status = cortex_dbg_read32(target, CORTEXR_DBG_DSCR); + /* If the instruction triggered a synchronous data abort, signal failure having cleared it */ + if (status & CORTEXR_DBG_DSCR_SYNC_DATA_ABORT) { + cortex_dbg_write32(target, CORTEXR_DBG_DRCR, CORTEXR_DBG_DRCR_CLR_STICKY_EXC); + return false; + } + } + /* Read back the DTR to complete the read and signal success */ + *result = cortex_dbg_read32(target, CORTEXR_DBG_DTRRX); + return true; } static void cortexr_run_write_insn(target_s *const target, const uint32_t insn, const uint32_t data) @@ -300,9 +308,13 @@ static void cortexr_run_write_insn(target_s *const target, const uint32_t insn, static inline uint32_t cortexr_core_reg_read(target_s *const target, const uint8_t reg) { /* If the register is a GPR and not the program counter, use a "simple" MCR to read */ - if (reg < 15U) + if (reg < 15U) { + uint32_t value = 0; /* Build an issue a core to coprocessor transfer for the requested register and read back the result */ - return cortexr_run_read_insn(target, ARM_MCR_INSN | ENCODE_CP_ACCESS(14, 0, reg, 0, 5, 0)); + (void)cortexr_run_read_insn(target, ARM_MCR_INSN | ENCODE_CP_ACCESS(14, 0, reg, 0, 5, 0), &value); + /* Return whatever value was read as we don't care about DCSR.SDABORT here */ + return value; + } /* If the register is the program counter, we first have to extract it to r0 */ else if (reg == 15U) { cortexr_run_insn(target, ARM_MOV_R0_PC_INSN); From dcae3fca6a19cfd2aaeef1f3733f3eb423197c89 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 10 Oct 2023 12:10:01 +0100 Subject: [PATCH 41/50] cortexr: Began overhauling `cortexr_mem_read()` to support reading memory properly as it requires a jump from the debug to system busses on Cortex-A and -R targets --- src/target/cortexr.c | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/src/target/cortexr.c b/src/target/cortexr.c index a1eca949f51..a1ce0b05463 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -193,6 +193,19 @@ static const uint16_t cortexr_spsr_encodings[5] = { #define ENCODE_CP_REG(n, m, opc1, opc2) \ ((((n)&0xfU) << 4U) | ((m)&0xfU) | (((opc1)&0x7U) << 8U) | (((opc2)&0x7U) << 12U)) +/* + * Instruction encodings for coprocessor load/store + * LDC -> Load Coprocessor (DDI0406C §A8.8.56, pg393) + */ +#define ARM_LDC_INSN 0xec100000U +/* + * Pre-encoded LDC/STC operands for getting data in and out of the core + * The first is a LDC encoded to move [r0] to the debug DTR and then increment r0 by 4 + * (`LDC p14, c5, [r0], #+4`, Preincrement = 0, Unindexed = 1, Doublelength = 0, Writeback = 1) + * The immediate is encoded shifted right by 2, and the reads are done 32 bits at a time. + */ +#define ARM_LDC_R0_POSTINC4_DTRTX_INSN (ARM_LDC_INSN | 0x00a05e01U) + /* Coprocessor register definitions */ #define CORTEXR_CPACR 15U, ENCODE_CP_REG(1U, 0U, 0U, 2U) @@ -646,9 +659,34 @@ void cortexr_detach(target_s *const target) target_halt_resume(target, false); } + +/* Fast path for cortexr_mem_read(). Assumes the address to read data from is already loaded in r0. */ +static inline bool cortexr_mem_read_fast(target_s *const target, uint32_t *const dest, const size_t count) +{ + /* Read each of the uint32_t's checking for failure */ + for (size_t offset = 0; offset < count; ++offset) { + if (!cortexr_run_read_insn(target, ARM_LDC_R0_POSTINC4_DTRTX_INSN, dest + offset)) + return false; /* Propagate failure if it happens */ + } + return true; /* Signal success */ +} + +/* + * This reads memory by jumping from the debug unit bus to the system bus. + * NB: This requires the core to be halted! Uses instruction launches on + * the core and requires we're in debug mode to work. Trashes r0. + */ static void cortexr_mem_read(target_s *const target, void *const dest, const target_addr_t src, const size_t len) { - adiv5_mem_read(cortex_ap(target), dest, src, len); + DEBUG_TARGET("%s: Reading %zu bytes @0x%" PRIx32 "\n", __func__, len, src); + /* Move the start address into the core's r0 */ + cortexr_core_reg_write(target, 0U, src); + + /* If the address is 32-bit aligned and we're reading 32 bits at a time, use the fast path */ + if ((src & 3U) == 0U && (len & 3U) == 0U) + cortexr_mem_read_fast(target, (uint32_t *)dest, len >> 2U); + else + adiv5_mem_read(cortex_ap(target), dest, src, len); } static void cortexr_mem_write(target_s *const target, const target_addr_t dest, const void *const src, const size_t len) From 1466d6d37622017ce843d409e7ef4c710550178d Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 10 Oct 2023 14:41:58 +0100 Subject: [PATCH 42/50] cortexr: Implemented an error reporting mechanism for data and MMU faults --- src/target/cortexr.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/target/cortexr.c b/src/target/cortexr.c index a1ce0b05463..03464395169 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -67,6 +67,9 @@ typedef struct cortexr_priv { uint64_t d[16U]; uint32_t fpcsr; } core_regs; + + /* Control and status information */ + uint8_t core_status; } cortexr_priv_s; #define CORTEXR_DBG_IDR 0x000U @@ -223,6 +226,9 @@ static const uint16_t cortexr_spsr_encodings[5] = { #define TOPT_FLAVOUR_VIRT_EXT (1U << 3U) /* If set, core has virtualisation extensions */ #define TOPT_FLAVOUR_VIRT_MEM (1U << 4U) /* If set, core uses the virtual memory model, not protected */ +#define CORTEXR_STATUS_DATA_FAULT (1U << 0U) +#define CORTEXR_STATUS_MMU_FAULT (1U << 1U) + /* * Fields for Cortex-R special-purpose registers, used in the generation of GDB's target description XML. * The general-purpose registers r0-r12 and the vector floating point (VFP) registers d0-d15 all follow a @@ -253,6 +259,7 @@ static_assert(ARRAY_LENGTH(cortexr_spr_types) == ARRAY_LENGTH(cortexr_spr_names) ); /* clang-format on */ +static bool cortexr_check_error(target_s *target); static void cortexr_mem_read(target_s *target, void *dest, target_addr_t src, size_t len); static void cortexr_mem_write(target_s *target, target_addr_t dest, const void *src, size_t len); @@ -294,6 +301,8 @@ static bool cortexr_run_read_insn(target_s *const target, const uint32_t insn, u status = cortex_dbg_read32(target, CORTEXR_DBG_DSCR); /* If the instruction triggered a synchronous data abort, signal failure having cleared it */ if (status & CORTEXR_DBG_DSCR_SYNC_DATA_ABORT) { + cortexr_priv_s *const priv = (cortexr_priv_s *)target->priv; + priv->core_status |= CORTEXR_STATUS_DATA_FAULT; cortex_dbg_write32(target, CORTEXR_DBG_DRCR, CORTEXR_DBG_DRCR_CLR_STICKY_EXC); return false; } @@ -571,7 +580,7 @@ bool cortexr_probe(adiv5_access_port_s *const ap, const target_addr_t base_addre cortexr_float_regs_save(target); } - target->check_error = cortex_check_error; + target->check_error = cortexr_check_error; target->mem_read = cortexr_mem_read; target->mem_write = cortexr_mem_write; @@ -660,6 +669,14 @@ void cortexr_detach(target_s *const target) target_halt_resume(target, false); } +static bool cortexr_check_error(target_s *const target) +{ + cortexr_priv_s *const priv = (cortexr_priv_s *)target->priv; + const bool fault = priv->core_status & (CORTEXR_STATUS_DATA_FAULT | CORTEXR_STATUS_MMU_FAULT); + priv->core_status = 0; + return fault || cortex_check_error(target); +} + /* Fast path for cortexr_mem_read(). Assumes the address to read data from is already loaded in r0. */ static inline bool cortexr_mem_read_fast(target_s *const target, uint32_t *const dest, const size_t count) { From dff48254e9e908b909bbb1354094ebc55854b374 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 10 Oct 2023 15:09:49 +0100 Subject: [PATCH 43/50] cortexr: Implemented fault handling for `cortexr_mem_read()` to clean up after ourselves --- src/target/cortexr.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/target/cortexr.c b/src/target/cortexr.c index 03464395169..5a37bac6d47 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -211,6 +211,8 @@ static const uint16_t cortexr_spsr_encodings[5] = { /* Coprocessor register definitions */ #define CORTEXR_CPACR 15U, ENCODE_CP_REG(1U, 0U, 0U, 2U) +#define CORTEXR_DFSR 15U, ENCODE_CP_REG(5U, 0U, 0U, 0U) +#define CORTEXR_DFAR 15U, ENCODE_CP_REG(6U, 0U, 0U, 0U) #define CORTEXR_CPACR_CP10_FULL_ACCESS 0x00300000U #define CORTEXR_CPACR_CP11_FULL_ACCESS 0x00c00000U @@ -695,7 +697,14 @@ static inline bool cortexr_mem_read_fast(target_s *const target, uint32_t *const */ static void cortexr_mem_read(target_s *const target, void *const dest, const target_addr_t src, const size_t len) { + cortexr_priv_s *const priv = (cortexr_priv_s *)target->priv; DEBUG_TARGET("%s: Reading %zu bytes @0x%" PRIx32 "\n", __func__, len, src); + /* Cache DFSR and DFAR in case we wind up triggering a data fault */ + const uint32_t initial_fault_status = cortexr_coproc_read(target, CORTEXR_DFSR); + const uint32_t initial_fault_addr = cortexr_coproc_read(target, CORTEXR_DFAR); + /* Clear any existing fault state */ + priv->core_status &= ~(CORTEXR_STATUS_DATA_FAULT | CORTEXR_STATUS_MMU_FAULT); + /* Move the start address into the core's r0 */ cortexr_core_reg_write(target, 0U, src); @@ -704,6 +713,18 @@ static void cortexr_mem_read(target_s *const target, void *const dest, const tar cortexr_mem_read_fast(target, (uint32_t *)dest, len >> 2U); else adiv5_mem_read(cortex_ap(target), dest, src, len); + + /* If we suffered a fault of some kind, grab the reason and restore DFSR/DFAR */ + if (priv->core_status & CORTEXR_STATUS_DATA_FAULT) { +#ifdef ENABLE_DEBUG + const uint32_t fault_status = cortexr_coproc_read(target, CORTEXR_DFSR); + const uint32_t fault_addr = cortexr_coproc_read(target, CORTEXR_DFAR); +#endif + cortexr_coproc_write(target, CORTEXR_DFAR, initial_fault_addr); + cortexr_coproc_write(target, CORTEXR_DFSR, initial_fault_status); + + DEBUG_WARN("%s: Read failed at 0x%08" PRIx32 " (%08" PRIx32 ")\n", __func__, fault_addr, fault_status); + } } static void cortexr_mem_write(target_s *const target, const target_addr_t dest, const void *const src, const size_t len) From 006002a3d93cd1872e8a46fc1c319c6dfc9e287f Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 11 Oct 2023 07:44:40 +0100 Subject: [PATCH 44/50] cortexr: Implemented a slow path memory reader for `cortexr_mem_read()` --- src/target/cortexr.c | 50 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/src/target/cortexr.c b/src/target/cortexr.c index 5a37bac6d47..7fcc85620aa 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -52,6 +52,7 @@ #include "cortex_internal.h" #include "gdb_reg.h" #include "gdb_packet.h" +#include "buffer_utils.h" #include @@ -209,6 +210,19 @@ static const uint16_t cortexr_spsr_encodings[5] = { */ #define ARM_LDC_R0_POSTINC4_DTRTX_INSN (ARM_LDC_INSN | 0x00a05e01U) +/* + * Instruction encodings for indirect loads and stores of data via the CPU + * LDRB -> Load Register Byte (immediate) (DDI0406C §A8.8.69, pg419) + * LDRH -> Load Register Halfword (immediate) (DDI0406C §A8.8.81, pg443) + * + * The first is `LDRB r1, [r0], #+1` to load a uint8_t from [r0] into r1 and increment the + * address in r0 by 1, writing the new address back to r0. + * The second is `LDRH r1, [r0], #+2` to load a uint16_t from [r0] into r1 and increment + * the address in r0 by 2, writing the new address back to r0. + */ +#define ARM_LDRB_R0_R1_INSN 0xe4f01001U +#define ARM_LDRH_R0_R1_INSN 0xe0f010b2U + /* Coprocessor register definitions */ #define CORTEXR_CPACR 15U, ENCODE_CP_REG(1U, 0U, 0U, 2U) #define CORTEXR_DFSR 15U, ENCODE_CP_REG(5U, 0U, 0U, 0U) @@ -690,6 +704,40 @@ static inline bool cortexr_mem_read_fast(target_s *const target, uint32_t *const return true; /* Signal success */ } +/* Slow path for cortexr_mem_read(). Trashes r0 and r1. */ +static bool cortexr_mem_read_slow(target_s *const target, uint8_t *const data, target_addr_t addr, const size_t length) +{ + size_t offset = 0; + /* If the address is odd, read a byte to get onto an even address */ + if (addr & 1U) { + cortexr_run_insn(target, ARM_LDRB_R0_R1_INSN); + data[offset++] = (uint8_t)cortexr_core_reg_read(target, 1U); + ++addr; + } + /* If the address is now even but only 16-bit aligned, read a uint16_t to get onto 32-bit alignment */ + if ((addr & 2U) && length - offset >= 2U) { + cortexr_run_insn(target, ARM_LDRH_R0_R1_INSN); + write_le2(data, offset, (uint16_t)cortexr_core_reg_read(target, 1U)); + offset += 2U; + } + /* Use the fast path to read as much as possible before doing a slow path fixup at the end */ + if (!cortexr_mem_read_fast(target, (uint32_t *)(data + offset), (length - offset) >> 2U)) + return false; + const uint8_t remainder = (length - offset) & 3U; + /* If the remainder needs at least 2 more bytes read, do this first */ + if (remainder & 2U) { + cortexr_run_insn(target, ARM_LDRH_R0_R1_INSN); + write_le2(data, offset, (uint16_t)cortexr_core_reg_read(target, 1U)); + offset += 2U; + } + /* Finally, fix things up if a final byte is required. */ + if (remainder & 1U) { + cortexr_run_insn(target, ARM_LDRB_R0_R1_INSN); + data[offset] = (uint8_t)cortexr_core_reg_read(target, 1U); + } + return true; /* Signal success */ +} + /* * This reads memory by jumping from the debug unit bus to the system bus. * NB: This requires the core to be halted! Uses instruction launches on @@ -712,7 +760,7 @@ static void cortexr_mem_read(target_s *const target, void *const dest, const tar if ((src & 3U) == 0U && (len & 3U) == 0U) cortexr_mem_read_fast(target, (uint32_t *)dest, len >> 2U); else - adiv5_mem_read(cortex_ap(target), dest, src, len); + cortexr_mem_read_slow(target, (uint8_t *)dest, src, len); /* If we suffered a fault of some kind, grab the reason and restore DFSR/DFAR */ if (priv->core_status & CORTEXR_STATUS_DATA_FAULT) { From 79d30447ddbccce92975213d584ba03ca683103a Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 11 Oct 2023 07:47:58 +0100 Subject: [PATCH 45/50] cortexr: Added fault handling for `cortexr_run_insn()` --- src/target/cortexr.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/target/cortexr.c b/src/target/cortexr.c index 7fcc85620aa..c6f16813964 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -297,13 +297,21 @@ void cortexr_detach(target_s *target); static const char *cortexr_target_description(target_s *target); -static void cortexr_run_insn(target_s *const target, const uint32_t insn) +static bool cortexr_run_insn(target_s *const target, const uint32_t insn) { /* Issue the requested instruction to the core */ cortex_dbg_write32(target, CORTEXR_DBG_ITR, insn); /* Poll for the instruction to complete */ - while (!(cortex_dbg_read32(target, CORTEXR_DBG_DSCR) & CORTEXR_DBG_DSCR_INSN_COMPLETE)) - continue; + uint32_t status = 0; + while (!(status & CORTEXR_DBG_DSCR_INSN_COMPLETE)) + status = cortex_dbg_read32(target, CORTEXR_DBG_DSCR); + /* If the instruction triggered a synchronous data abort, signal failure having cleared it */ + if (status & CORTEXR_DBG_DSCR_SYNC_DATA_ABORT) { + cortexr_priv_s *const priv = (cortexr_priv_s *)target->priv; + priv->core_status |= CORTEXR_STATUS_DATA_FAULT; + cortex_dbg_write32(target, CORTEXR_DBG_DRCR, CORTEXR_DBG_DRCR_CLR_STICKY_EXC); + } + return !(status & CORTEXR_DBG_DSCR_SYNC_DATA_ABORT); } static bool cortexr_run_read_insn(target_s *const target, const uint32_t insn, uint32_t *const result) From a59ca06ddf90d581c10288009b428a6db357a909 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 11 Oct 2023 07:50:10 +0100 Subject: [PATCH 46/50] cortexr: Implemented fault handling for `cortexr_mem_read_slow()` to clean up after ourselves --- src/target/cortexr.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/target/cortexr.c b/src/target/cortexr.c index c6f16813964..01604b95fdc 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -718,13 +718,15 @@ static bool cortexr_mem_read_slow(target_s *const target, uint8_t *const data, t size_t offset = 0; /* If the address is odd, read a byte to get onto an even address */ if (addr & 1U) { - cortexr_run_insn(target, ARM_LDRB_R0_R1_INSN); + if (!cortexr_run_insn(target, ARM_LDRB_R0_R1_INSN)) + return false; data[offset++] = (uint8_t)cortexr_core_reg_read(target, 1U); ++addr; } /* If the address is now even but only 16-bit aligned, read a uint16_t to get onto 32-bit alignment */ if ((addr & 2U) && length - offset >= 2U) { - cortexr_run_insn(target, ARM_LDRH_R0_R1_INSN); + if (!cortexr_run_insn(target, ARM_LDRH_R0_R1_INSN)) + return false; write_le2(data, offset, (uint16_t)cortexr_core_reg_read(target, 1U)); offset += 2U; } @@ -734,13 +736,15 @@ static bool cortexr_mem_read_slow(target_s *const target, uint8_t *const data, t const uint8_t remainder = (length - offset) & 3U; /* If the remainder needs at least 2 more bytes read, do this first */ if (remainder & 2U) { - cortexr_run_insn(target, ARM_LDRH_R0_R1_INSN); + if (!cortexr_run_insn(target, ARM_LDRH_R0_R1_INSN)) + return false; write_le2(data, offset, (uint16_t)cortexr_core_reg_read(target, 1U)); offset += 2U; } /* Finally, fix things up if a final byte is required. */ if (remainder & 1U) { - cortexr_run_insn(target, ARM_LDRB_R0_R1_INSN); + if (!cortexr_run_insn(target, ARM_LDRB_R0_R1_INSN)) + return false; data[offset] = (uint8_t)cortexr_core_reg_read(target, 1U); } return true; /* Signal success */ From 0d568fe9344c8d65e6e93825d39bf3144c1ddc4d Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 11 Oct 2023 16:00:40 +0100 Subject: [PATCH 47/50] cortexr: Refactored out the data fault post-processing from `cortexr_mem_read()` --- src/target/cortexr.c | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/src/target/cortexr.c b/src/target/cortexr.c index 01604b95fdc..e51f0a6ceeb 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -750,6 +750,25 @@ static bool cortexr_mem_read_slow(target_s *const target, uint8_t *const data, t return true; /* Signal success */ } +static void cortexr_mem_handle_fault( + target_s *const target, const char *const func, const uint32_t orig_fault_status, const uint32_t orig_fault_addr) +{ + const cortexr_priv_s *const priv = (cortexr_priv_s *)target->priv; + /* If we suffered a fault of some kind, grab the reason and restore DFSR/DFAR */ + if (priv->core_status & CORTEXR_STATUS_DATA_FAULT) { +#ifdef ENABLE_DEBUG + const uint32_t fault_status = cortexr_coproc_read(target, CORTEXR_DFSR); + const uint32_t fault_addr = cortexr_coproc_read(target, CORTEXR_DFAR); +#else + (void)func; +#endif + cortexr_coproc_write(target, CORTEXR_DFAR, orig_fault_addr); + cortexr_coproc_write(target, CORTEXR_DFSR, orig_fault_status); + + DEBUG_WARN("%s: Failed at 0x%08" PRIx32 " (%08" PRIx32 ")\n", func, fault_addr, fault_status); + } +} + /* * This reads memory by jumping from the debug unit bus to the system bus. * NB: This requires the core to be halted! Uses instruction launches on @@ -758,10 +777,9 @@ static bool cortexr_mem_read_slow(target_s *const target, uint8_t *const data, t static void cortexr_mem_read(target_s *const target, void *const dest, const target_addr_t src, const size_t len) { cortexr_priv_s *const priv = (cortexr_priv_s *)target->priv; - DEBUG_TARGET("%s: Reading %zu bytes @0x%" PRIx32 "\n", __func__, len, src); /* Cache DFSR and DFAR in case we wind up triggering a data fault */ - const uint32_t initial_fault_status = cortexr_coproc_read(target, CORTEXR_DFSR); - const uint32_t initial_fault_addr = cortexr_coproc_read(target, CORTEXR_DFAR); + const uint32_t fault_status = cortexr_coproc_read(target, CORTEXR_DFSR); + const uint32_t fault_addr = cortexr_coproc_read(target, CORTEXR_DFAR); /* Clear any existing fault state */ priv->core_status &= ~(CORTEXR_STATUS_DATA_FAULT | CORTEXR_STATUS_MMU_FAULT); @@ -773,18 +791,8 @@ static void cortexr_mem_read(target_s *const target, void *const dest, const tar cortexr_mem_read_fast(target, (uint32_t *)dest, len >> 2U); else cortexr_mem_read_slow(target, (uint8_t *)dest, src, len); - - /* If we suffered a fault of some kind, grab the reason and restore DFSR/DFAR */ - if (priv->core_status & CORTEXR_STATUS_DATA_FAULT) { -#ifdef ENABLE_DEBUG - const uint32_t fault_status = cortexr_coproc_read(target, CORTEXR_DFSR); - const uint32_t fault_addr = cortexr_coproc_read(target, CORTEXR_DFAR); -#endif - cortexr_coproc_write(target, CORTEXR_DFAR, initial_fault_addr); - cortexr_coproc_write(target, CORTEXR_DFSR, initial_fault_status); - - DEBUG_WARN("%s: Read failed at 0x%08" PRIx32 " (%08" PRIx32 ")\n", __func__, fault_addr, fault_status); - } + /* Deal with any data faults that occured */ + cortexr_mem_handle_fault(target, __func__, fault_status, fault_addr); } static void cortexr_mem_write(target_s *const target, const target_addr_t dest, const void *const src, const size_t len) From 4ca538f3cc2c3dd67a6f27984924e2ed3e25bbd8 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 11 Oct 2023 16:14:25 +0100 Subject: [PATCH 48/50] cortexr: Implemented handling for when an instruction launch that writes data hits an error --- src/target/cortexr.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/target/cortexr.c b/src/target/cortexr.c index e51f0a6ceeb..1f542496534 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -336,7 +336,7 @@ static bool cortexr_run_read_insn(target_s *const target, const uint32_t insn, u return true; } -static void cortexr_run_write_insn(target_s *const target, const uint32_t insn, const uint32_t data) +static bool cortexr_run_write_insn(target_s *const target, const uint32_t insn, const uint32_t data) { /* Set up the data in the DTR for the transaction */ cortex_dbg_write32(target, CORTEXR_DBG_DTRTX, data); @@ -346,9 +346,19 @@ static void cortexr_run_write_insn(target_s *const target, const uint32_t insn, /* Issue the requested instruction to the core */ cortex_dbg_write32(target, CORTEXR_DBG_ITR, insn); /* Poll for the instruction to complete and the data to be consumed from the DTR */ - while ((cortex_dbg_read32(target, CORTEXR_DBG_DSCR) & - (CORTEXR_DBG_DSCR_INSN_COMPLETE | CORTEXR_DBG_DSCR_DTR_WRITE_DONE)) != CORTEXR_DBG_DSCR_INSN_COMPLETE) - continue; + uint32_t status = 0; + while ((status & (CORTEXR_DBG_DSCR_INSN_COMPLETE | CORTEXR_DBG_DSCR_DTR_WRITE_DONE)) != + CORTEXR_DBG_DSCR_INSN_COMPLETE) { + status = cortex_dbg_read32(target, CORTEXR_DBG_DSCR); + /* If the instruction triggered a synchronous data abort, signal failure having cleared it */ + if (status & CORTEXR_DBG_DSCR_SYNC_DATA_ABORT) { + cortexr_priv_s *const priv = (cortexr_priv_s *)target->priv; + priv->core_status |= CORTEXR_STATUS_DATA_FAULT; + cortex_dbg_write32(target, CORTEXR_DBG_DRCR, CORTEXR_DBG_DRCR_CLR_STICKY_EXC); + return false; + } + } + return true; } static inline uint32_t cortexr_core_reg_read(target_s *const target, const uint8_t reg) From 564e48d3ed775a751117c83a0fc98e9c49b7dba8 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 11 Oct 2023 16:15:59 +0100 Subject: [PATCH 49/50] cortexr: Began overhauling `cortexr_mem_write()` to support writing memory properly as it requires ajump from the debug to system busses on Cortex-A and -R targets --- src/target/cortexr.c | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/src/target/cortexr.c b/src/target/cortexr.c index 1f542496534..af6dc956af3 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -200,15 +200,21 @@ static const uint16_t cortexr_spsr_encodings[5] = { /* * Instruction encodings for coprocessor load/store * LDC -> Load Coprocessor (DDI0406C §A8.8.56, pg393) + * STC -> Store Corprocessor (DDI0406C §A8.8.119, pg663) */ #define ARM_LDC_INSN 0xec100000U +#define ARM_STC_INSN 0xec000000U /* * Pre-encoded LDC/STC operands for getting data in and out of the core * The first is a LDC encoded to move [r0] to the debug DTR and then increment r0 by 4 * (`LDC p14, c5, [r0], #+4`, Preincrement = 0, Unindexed = 1, Doublelength = 0, Writeback = 1) * The immediate is encoded shifted right by 2, and the reads are done 32 bits at a time. + * The second is a STC encoded to move from the debug DTR to [r0] and then increment r0 by 4 + * (`STC p14, c5, [r0], #+4`, Preincrement = 0, Unindexed = 1, Doublelength = 0, Writeback = 1) + * As with read, the immediate is encoded shifted right by 2 and writes are done 32 bits at a time. */ #define ARM_LDC_R0_POSTINC4_DTRTX_INSN (ARM_LDC_INSN | 0x00a05e01U) +#define ARM_STC_DTRRX_R0_POSTINC4_INSN (ARM_STC_INSN | 0x00a05e01U) /* * Instruction encodings for indirect loads and stores of data via the CPU @@ -805,9 +811,42 @@ static void cortexr_mem_read(target_s *const target, void *const dest, const tar cortexr_mem_handle_fault(target, __func__, fault_status, fault_addr); } +/* Fast path for cortexr_mem_write(). Assumes the address to read data from is already loaded in r0. */ +static inline bool cortexr_mem_write_fast(target_s *const target, const uint32_t *const src, const size_t count) +{ + /* Read each of the uint32_t's checking for failure */ + for (size_t offset = 0; offset < count; ++offset) { + if (!cortexr_run_write_insn(target, ARM_STC_DTRRX_R0_POSTINC4_INSN, src[offset])) + return false; /* Propagate failure if it happens */ + } + return true; /* Signal success */ +} + +/* + * This writes memory by jumping from the debug unit bus to the system bus. + * NB: This requires the core to be halted! Uses instruction launches on + * the core and requires we're in debug mode to work. Trashes r0. + */ static void cortexr_mem_write(target_s *const target, const target_addr_t dest, const void *const src, const size_t len) { - adiv5_mem_write(cortex_ap(target), dest, src, len); + cortexr_priv_s *const priv = (cortexr_priv_s *)target->priv; + DEBUG_TARGET("%s: Writing %zu bytes @0x%" PRIx32 "\n", __func__, len, dest); + /* Cache DFSR and DFAR in case we wind up triggering a data fault */ + const uint32_t fault_status = cortexr_coproc_read(target, CORTEXR_DFSR); + const uint32_t fault_addr = cortexr_coproc_read(target, CORTEXR_DFAR); + /* Clear any existing fault state */ + priv->core_status &= ~(CORTEXR_STATUS_DATA_FAULT | CORTEXR_STATUS_MMU_FAULT); + + /* Move the start address into the core's r0 */ + cortexr_core_reg_write(target, 0U, dest); + + /* If the address is 32-bit aligned and we're writing 32 bits at a time, use the fast path */ + if ((dest & 3U) == 0U && (len & 3U) == 0U) + cortexr_mem_write_fast(target, (const uint32_t *)src, len >> 2U); + else + adiv5_mem_write(cortex_ap(target), dest, src, len); + /* Deal with any data faults that occured */ + cortexr_mem_handle_fault(target, __func__, fault_status, fault_addr); } static void cortexr_regs_read(target_s *const target, void *const data) From a5f7eebb96324d329565c5cd7dda4a22ce93cb9a Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 11 Oct 2023 16:55:31 +0100 Subject: [PATCH 50/50] cortexr: Implemented a slow path memory writer for `cortexr_mem_write()` --- src/target/cortexr.c | 49 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/src/target/cortexr.c b/src/target/cortexr.c index af6dc956af3..4110b738900 100644 --- a/src/target/cortexr.c +++ b/src/target/cortexr.c @@ -220,14 +220,22 @@ static const uint16_t cortexr_spsr_encodings[5] = { * Instruction encodings for indirect loads and stores of data via the CPU * LDRB -> Load Register Byte (immediate) (DDI0406C §A8.8.69, pg419) * LDRH -> Load Register Halfword (immediate) (DDI0406C §A8.8.81, pg443) + * STRB -> Store Register Byte (immediate) (DDI0406C §A8.8.208, pg681) + * STRH -> Store Register Halfword (immediate) (DDI0406C §A8.8.218, pg701) * * The first is `LDRB r1, [r0], #+1` to load a uint8_t from [r0] into r1 and increment the * address in r0 by 1, writing the new address back to r0. * The second is `LDRH r1, [r0], #+2` to load a uint16_t from [r0] into r1 and increment * the address in r0 by 2, writing the new address back to r0. + * The third is `STRB r1, [r0], #+1` to store a uint8_t to [r0] from r1 and increment the + * address in r0 by 1, writing the new address back to r0. + * The fourth is `STRH r1, [r0], #+2` to store a uint16_t to [r0] from r1 and increment + * the address in r0 by 2, writing the new address back to r0. */ #define ARM_LDRB_R0_R1_INSN 0xe4f01001U #define ARM_LDRH_R0_R1_INSN 0xe0f010b2U +#define ARM_STRB_R1_R0_INSN 0xe4e01001U +#define ARM_STRH_R1_R0_INSN 0xe0e010b2U /* Coprocessor register definitions */ #define CORTEXR_CPACR 15U, ENCODE_CP_REG(1U, 0U, 0U, 2U) @@ -822,6 +830,45 @@ static inline bool cortexr_mem_write_fast(target_s *const target, const uint32_t return true; /* Signal success */ } +/* Slow path for cortexr_mem_write(). Trashes r0 and r1. */ +static bool cortexr_mem_write_slow( + target_s *const target, target_addr_t addr, const uint8_t *const data, const size_t length) +{ + size_t offset = 0; + /* If the address is odd, write a byte to get onto an even address */ + if (addr & 1U) { + cortexr_core_reg_write(target, 1U, data[offset++]); + if (!cortexr_run_insn(target, ARM_STRB_R1_R0_INSN)) + return false; + ++addr; + } + /* If the address is now even but only 16-bit aligned, write a uint16_t to get onto 32-bit alignment */ + if ((addr & 2U) && length - offset >= 2U) { + cortexr_core_reg_write(target, 1U, read_le2(data, offset)); + if (!cortexr_run_insn(target, ARM_STRH_R1_R0_INSN)) + return false; + offset += 2U; + } + /* Use the fast path to write as much as possible before doing a slow path fixup at the end */ + if (!cortexr_mem_write_fast(target, (uint32_t *)(data + offset), (length - offset) >> 2U)) + return false; + const uint8_t remainder = (length - offset) & 3U; + /* If the remainder needs at least 2 more bytes write, do this first */ + if (remainder & 2U) { + cortexr_core_reg_write(target, 1U, read_le2(data, offset)); + if (!cortexr_run_insn(target, ARM_STRH_R1_R0_INSN)) + return false; + offset += 2U; + } + /* Finally, fix things up if a final byte is required. */ + if (remainder & 1U) { + cortexr_core_reg_write(target, 1U, data[offset]); + if (!cortexr_run_insn(target, ARM_STRB_R1_R0_INSN)) + return false; + } + return true; /* Signal success */ +} + /* * This writes memory by jumping from the debug unit bus to the system bus. * NB: This requires the core to be halted! Uses instruction launches on @@ -844,7 +891,7 @@ static void cortexr_mem_write(target_s *const target, const target_addr_t dest, if ((dest & 3U) == 0U && (len & 3U) == 0U) cortexr_mem_write_fast(target, (const uint32_t *)src, len >> 2U); else - adiv5_mem_write(cortex_ap(target), dest, src, len); + cortexr_mem_write_slow(target, dest, (const uint8_t *)src, len); /* Deal with any data faults that occured */ cortexr_mem_handle_fault(target, __func__, fault_status, fault_addr); }