+static symbol_address_t ecos_value(struct rtos *rtos, unsigned int idx)
+{
+ if (idx < ARRAY_SIZE(ecos_symbol_list))
+ return rtos->symbols[idx].address;
+
+ /* We do not terminate, just return 0 in this case. */
+ LOG_ERROR("eCos: Invalid symbol index %u", idx);
+ return 0;
+}
+
+#define XMLENTRY(_c, _s) { .xc = (_c), .rs = (_s), .rlen = (sizeof(_s) - 1) }
+
+static const struct {
+ char xc;
+ const char *rs;
+ size_t rlen;
+} xmlchars[] = {
+ XMLENTRY('<', "<"),
+ XMLENTRY('&', "&"),
+ XMLENTRY('>', ">"),
+ XMLENTRY('\'', "'"),
+ XMLENTRY('"', """)
+};
+
+/** Escape any XML reserved characters in a string. */
+static bool ecos_escape_string(const char *raw, char *out, size_t limit)
+{
+ static const char *tokens = "<&>\'\"";
+ bool escaped = false;
+
+ if (!out || !limit)
+ return false;
+
+ (void)memset(out, '\0', limit);
+
+ while (raw && *raw && limit) {
+ size_t lok = strcspn(raw, tokens);
+ if (lok) {
+ size_t tocopy;
+ tocopy = ((limit < lok) ? limit : lok);
+ (void)memcpy(out, raw, tocopy);
+ limit -= tocopy;
+ out += tocopy;
+ raw += lok;
+ continue;
+ }
+
+ char *fidx = strchr(tokens, *raw);
+ if (!fidx) {
+ /* Should never happen assuming xmlchars
+ * vector and tokens string match. */
+ LOG_ERROR("eCos: Unexpected XML char %c", *raw);
+ continue;
+ }
+
+ uint32_t cidx = (fidx - tokens);
+ size_t tocopy = xmlchars[cidx].rlen;
+ if (limit < tocopy)
+ break;
+
+ escaped = true;
+ (void)memcpy(out, xmlchars[cidx].rs, tocopy);
+ limit -= tocopy;
+ out += tocopy;
+ raw++;
+ }
+
+ return escaped;
+}
+
+static int ecos_check_app_info(struct rtos *rtos, struct ecos_params *param)
+{
+ if (!rtos || !param)
+ return -1;
+
+ if (param->flush_common) {
+ if (debug_level >= LOG_LVL_DEBUG) {
+ for (unsigned int idx = 0; idx < ARRAY_SIZE(ecos_symbol_list); idx++) {
+ LOG_DEBUG("eCos: %s 0x%016" PRIX64 " %s",
+ rtos->symbols[idx].optional ? "OPTIONAL" : " ",
+ rtos->symbols[idx].address, rtos->symbols[idx].symbol_name);
+ }
+ }
+
+ /* If "__ecospro_syminfo.size.cyg_thread.list_next" is non-zero then we
+ * expect all of the generic thread structure symbols to have been
+ * provided. */
+ symbol_address_t thread_next_size = ecos_value(rtos, ECOS_VAL_COMMON_THREAD_NEXT_SIZE);
+ if (thread_next_size != 0) {
+ param->pointer_width = thread_next_size;
+ param->uid_width = ecos_value(rtos, ECOS_VAL_COMMON_THREAD_ID_SIZE);
+ param->state_width = ecos_value(rtos, ECOS_VAL_COMMON_THREAD_STATE_SIZE);
+ param->thread_stack_offset = ecos_value(rtos, ECOS_VAL_COMMON_THREAD_STACK_OFF);
+ param->thread_name_offset = ecos_value(rtos, ECOS_VAL_COMMON_THREAD_NAME_OFF);
+ param->thread_state_offset = ecos_value(rtos, ECOS_VAL_COMMON_THREAD_STATE_OFF);
+ param->thread_next_offset = ecos_value(rtos, ECOS_VAL_COMMON_THREAD_NEXT_OFF);
+ param->thread_uniqueid_offset = ecos_value(rtos, ECOS_VAL_COMMON_THREAD_ID_OFF);
+ }
+
+ if (param->uid_width != sizeof(uint16_t)) {
+ /* Currently all eCos configurations use a 16-bit field to hold the
+ * unique thread ID. */
+ LOG_WARNING("eCos: Unexpected unique_id width %" PRIu8, param->uid_width);
+ param->uid_width = (unsigned char)sizeof(uint16_t);
+ }
+
+ param->stacking_info = NULL;
+ param->flush_common = false;
+ }
+
+ return ERROR_OK;
+}
+
+/* The Cortex-M eCosPro "thread" contexts have a "type" indicator, which tracks
+ * the context state of (THREAD | EXCEPTION | INTERRUPT) and whether FPU
+ * registers are saved.
+ *
+ * For thread-aware debugging from GDB we are only interested in THREAD states
+ * and so do not need to implement support for INTERRUPT or EXCEPTION thread
+ * contexts since this code does not expose those stack contexts via the
+ * constructed thread list support. */
+static int ecos_stack_layout_cortexm(struct rtos *rtos,
+ struct ecos_params *param, int64_t stack_ptr,
+ const struct rtos_register_stacking **si)
+{
+ int retval = ERROR_OK;
+
+ /* CONSIDER: We could return
+ * ecos_value(rtos, ECOS_VAL_CORTEXM_THREAD_SAVED) as the actual PC
+ * address of a context switch, with the LR being set to the context PC
+ * field to give a true representation of where the thread switch
+ * occurs. However that would require extending the common
+ * rtos_generic_stack_read() code with suitable support for applying a
+ * supplied value, or just implementing our own version of that code that
+ * can inject data into what is passed onwards to GDB. */
+
+ /* UPDATE: When we can return VFP register state then we will NOT be
+ * basing the cached state on the single param->stacking_info value,
+ * since we will need a different stacking_info structure returned for
+ * each thread type when FPU support is enabled. The use of the single
+ * param->stacking_info is a holder whilst we are limited to the fixed
+ * ARMV7M_NUM_CORE_REGS set of descriptors. */
+
+ if (!param->stacking_info &&
+ ecos_value(rtos, ECOS_VAL_CORTEXM_THREAD_SAVED) &&
+ ecos_value(rtos, ECOS_VAL_CORTEXM_VAL_THREAD)) {
+ unsigned char numoutreg = ECOS_CORTEXM_BASE_NUMREGS;
+
+ rtos_ecos_stacking.stack_registers_size = ecos_value(rtos, ECOS_VAL_CORTEXM_CTX_THREAD_SIZE);
+ rtos_ecos_stacking.calculate_process_stack = rtos_generic_stack_align8;
+ rtos_ecos_stacking.register_offsets = rtos_ecos_regoff_cortexm;
+
+ rtos_ecos_regoff_cortexm[ECOS_REGLIST_R0].offset = (ecos_value(rtos, ECOS_VAL_CORTEXM_CTX_REG_OFF) + 0x00);
+ rtos_ecos_regoff_cortexm[ECOS_REGLIST_R1].offset = (ecos_value(rtos, ECOS_VAL_CORTEXM_CTX_REG_OFF) + 0x04);
+ rtos_ecos_regoff_cortexm[ECOS_REGLIST_R2].offset = (ecos_value(rtos, ECOS_VAL_CORTEXM_CTX_REG_OFF) + 0x08);
+ rtos_ecos_regoff_cortexm[ECOS_REGLIST_R3].offset = (ecos_value(rtos, ECOS_VAL_CORTEXM_CTX_REG_OFF) + 0x0C);
+ rtos_ecos_regoff_cortexm[ECOS_REGLIST_R4].offset = (ecos_value(rtos, ECOS_VAL_CORTEXM_CTX_REG_OFF) + 0x10);
+ rtos_ecos_regoff_cortexm[ECOS_REGLIST_R5].offset = (ecos_value(rtos, ECOS_VAL_CORTEXM_CTX_REG_OFF) + 0x14);
+ rtos_ecos_regoff_cortexm[ECOS_REGLIST_R6].offset = (ecos_value(rtos, ECOS_VAL_CORTEXM_CTX_REG_OFF) + 0x18);
+ rtos_ecos_regoff_cortexm[ECOS_REGLIST_R7].offset = (ecos_value(rtos, ECOS_VAL_CORTEXM_CTX_REG_OFF) + 0x1C);
+ rtos_ecos_regoff_cortexm[ECOS_REGLIST_R8].offset = (ecos_value(rtos, ECOS_VAL_CORTEXM_CTX_REG_OFF) + 0x20);
+ rtos_ecos_regoff_cortexm[ECOS_REGLIST_R9].offset = (ecos_value(rtos, ECOS_VAL_CORTEXM_CTX_REG_OFF) + 0x24);
+ rtos_ecos_regoff_cortexm[ECOS_REGLIST_R10].offset = (ecos_value(rtos, ECOS_VAL_CORTEXM_CTX_REG_OFF) + 0x28);
+ rtos_ecos_regoff_cortexm[ECOS_REGLIST_R11].offset = (ecos_value(rtos, ECOS_VAL_CORTEXM_CTX_REG_OFF) + 0x2C);
+ rtos_ecos_regoff_cortexm[ECOS_REGLIST_R12].offset = (ecos_value(rtos, ECOS_VAL_CORTEXM_CTX_REG_OFF) + 0x30);
+ /* Rather than using the stacked ECOS_VAL_CORTEXM_CTX_SP_OFF
+ * value we force the reported sp to be after the stacked
+ * register context. */
+ rtos_ecos_regoff_cortexm[ECOS_REGLIST_R13].offset = -2;
+ rtos_ecos_regoff_cortexm[ECOS_REGLIST_R14].offset = -1;
+ rtos_ecos_regoff_cortexm[ECOS_REGLIST_PC].offset = ecos_value(rtos, ECOS_VAL_CORTEXM_CTX_PC_OFF);
+ rtos_ecos_regoff_cortexm[ECOS_REGLIST_XPSR].offset = -1;
+
+ param->stacking_info = &rtos_ecos_stacking;
+
+ /* Common Cortex-M thread register offsets for the current
+ * symbol table: */
+ if (retval == ERROR_OK && param->stacking_info) {
+ if (numoutreg > ECOS_REGLIST_BASEPRI) {
+ rtos_ecos_regoff_cortexm[ECOS_REGLIST_BASEPRI].offset =
+ ecos_value(rtos, ECOS_VAL_CORTEXM_CTX_BASEPRI_OFF);
+ }
+
+ rtos_ecos_stacking.num_output_registers = numoutreg;
+ }
+ }
+
+ if (si)
+ *si = param->stacking_info;
+
+ return retval;
+}
+
+static int ecos_stack_layout_arm(struct rtos *rtos, struct ecos_params *param,
+ int64_t stack_ptr, const struct rtos_register_stacking **si)
+{
+ int retval = ERROR_OK;
+
+ if (!param->stacking_info && ecos_value(rtos, ECOS_VAL_ARM_REGSIZE)) {
+ /* When OpenOCD is extended to allow FPU registers to be returned from a
+ * stacked thread context we can check:
+ * if (0 != ecos_value(rtos, ECOS_VAL_ARM_FPUSIZE)) { FPU }
+ * for presence of FPU registers in the context. */
+
+ rtos_ecos_stacking.stack_registers_size = ecos_value(rtos, ECOS_VAL_ARM_REGSIZE);
+ rtos_ecos_stacking.num_output_registers = ARRAY_SIZE(rtos_ecos_regoff_arm);
+ rtos_ecos_stacking.register_offsets = rtos_ecos_regoff_arm;
+
+ rtos_ecos_regoff_arm[0].offset = ecos_value(rtos, ECOS_VAL_ARM_CTX_R0_OFF);
+ rtos_ecos_regoff_arm[1].offset = ecos_value(rtos, ECOS_VAL_ARM_CTX_R1_OFF);
+ rtos_ecos_regoff_arm[2].offset = ecos_value(rtos, ECOS_VAL_ARM_CTX_R2_OFF);
+ rtos_ecos_regoff_arm[3].offset = ecos_value(rtos, ECOS_VAL_ARM_CTX_R3_OFF);
+ rtos_ecos_regoff_arm[4].offset = ecos_value(rtos, ECOS_VAL_ARM_CTX_R4_OFF);
+ rtos_ecos_regoff_arm[5].offset = ecos_value(rtos, ECOS_VAL_ARM_CTX_R5_OFF);
+ rtos_ecos_regoff_arm[6].offset = ecos_value(rtos, ECOS_VAL_ARM_CTX_R6_OFF);
+ rtos_ecos_regoff_arm[7].offset = ecos_value(rtos, ECOS_VAL_ARM_CTX_R7_OFF);
+ rtos_ecos_regoff_arm[8].offset = ecos_value(rtos, ECOS_VAL_ARM_CTX_R8_OFF);
+ rtos_ecos_regoff_arm[9].offset = ecos_value(rtos, ECOS_VAL_ARM_CTX_R9_OFF);
+ rtos_ecos_regoff_arm[10].offset = ecos_value(rtos, ECOS_VAL_ARM_CTX_R10_OFF);
+ rtos_ecos_regoff_arm[11].offset = ecos_value(rtos, ECOS_VAL_ARM_CTX_FP_OFF);
+ rtos_ecos_regoff_arm[12].offset = ecos_value(rtos, ECOS_VAL_ARM_CTX_IP_OFF);
+ rtos_ecos_regoff_arm[13].offset = ecos_value(rtos, ECOS_VAL_ARM_CTX_SP_OFF);
+ rtos_ecos_regoff_arm[14].offset = ecos_value(rtos, ECOS_VAL_ARM_CTX_LR_OFF);
+ rtos_ecos_regoff_arm[15].offset = ecos_value(rtos, ECOS_VAL_ARM_CTX_PC_OFF);
+ rtos_ecos_regoff_arm[16].offset = ecos_value(rtos, ECOS_VAL_ARM_CTX_CPSR_OFF);
+
+ param->stacking_info = &rtos_ecos_stacking;
+ }
+
+ if (si)
+ *si = param->stacking_info;
+
+ return retval;
+}
+
+/* We see this function called on a new connection, it looks like before and
+ * after the "tar rem"/"tar extended-remote". It might be the only point we can
+ * decide to cache information (to check if the symbol table has changed). */