This is an automated email from Gerrit. "Erhan Kurubas <erhan.kuru...@espressif.com>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/7444
-- gerrit commit 6cee433ffc44f595290e22f4d2e3b0b81c9f9046 Author: Erhan Kurubas <erhan.kuru...@espressif.com> Date: Sun Jan 22 23:43:03 2023 +0100 rtos/nuttx: add Espressif target support and refactoring Almost written from the beginning in a modern OpenOCD way. - Endiannes support - Proper variable types - Align with the other rtos implementations Signed-off-by: Erhan Kurubas <erhan.kuru...@espressif.com> Change-Id: I0868a22da2ed2ab664c82b17c171dc59ede78d10 diff --git a/src/rtos/nuttx.c b/src/rtos/nuttx.c index 993ff84bde..93781257ee 100644 --- a/src/rtos/nuttx.c +++ b/src/rtos/nuttx.c @@ -18,53 +18,55 @@ #include "rtos.h" #include "helper/log.h" #include "helper/types.h" -#include "server/gdb_server.h" - -#include "nuttx_header.h" +#include "target/register.h" #include "rtos_nuttx_stackings.h" -int rtos_thread_packet(struct connection *connection, const char *packet, int packet_size); - -#ifdef CONFIG_DISABLE_SIGNALS -#define SIG_QUEUE_NUM 0 -#else -#define SIG_QUEUE_NUM 1 -#endif /* CONFIG_DISABLE_SIGNALS */ - -#ifdef CONFIG_DISABLE_MQUEUE -#define M_QUEUE_NUM 0 -#else -#define M_QUEUE_NUM 2 -#endif /* CONFIG_DISABLE_MQUEUE */ - -#ifdef CONFIG_PAGING -#define PAGING_QUEUE_NUM 1 -#else -#define PAGING_QUEUE_NUM 0 -#endif /* CONFIG_PAGING */ +#define NAME_SIZE 32 +#define EXTRAINFO_SIZE 256 +/* Only 32-bit CPUs are supported by the current implementation. Supporting + * other CPUs will require reading this information from the target and + * adapting the code accordignly. + */ +#define PTR_WIDTH 4 -#define TASK_QUEUE_NUM (6 + SIG_QUEUE_NUM + M_QUEUE_NUM + PAGING_QUEUE_NUM) - +struct nuttx_params { + const char *target_name; + const struct rtos_register_stacking *stacking; + const struct rtos_register_stacking *(*select_stackinfo)(struct target *target); +}; -/* see nuttx/sched/os_start.c */ -static char *nuttx_symbol_list[] = { - "g_readytorun", /* 0: must be top of this array */ - "g_tasklisttable", - NULL +struct tcbinfo { + uint8_t pid_off[2]; + uint8_t state_off[2]; + uint8_t pri_off[2]; + uint8_t name_off[2]; + uint8_t regs_off[2]; + uint8_t basic_num[2]; + uint8_t total_num[2]; +} __attribute__ ((packed)); + +struct symbols { + const char *name; + bool optional; }; -/* see nuttx/include/nuttx/sched.h */ -struct tcb { - uint32_t flink; - uint32_t blink; - uint8_t dat[512]; +/* Used to index the list of retrieved symbols. See nuttx_symbol_list for the order. */ +enum nuttx_symbol_vals { + NX_SYM_READYTORUN = 0, + NX_SYM_PIDHASH, + NX_SYM_NPIDHASH, + NX_SYM_TCB_INFO, }; -static struct { - uint32_t addr; - uint32_t prio; -} g_tasklist[TASK_QUEUE_NUM]; +/* See nuttx/sched/nx_start.c */ +static const struct symbols nuttx_symbol_list[] = { + { "g_readytorun", false }, + { "g_pidhash", false }, + { "g_npidhash", false }, + { "g_tcbinfo", false }, + { NULL, false } +}; static char *task_state_str[] = { "INVALID", @@ -73,261 +75,341 @@ static char *task_state_str[] = { "RUNNING", "INACTIVE", "WAIT_SEM", -#ifndef CONFIG_DISABLE_SIGNALS "WAIT_SIG", -#endif /* CONFIG_DISABLE_SIGNALS */ -#ifndef CONFIG_DISABLE_MQUEUE "WAIT_MQNOTEMPTY", "WAIT_MQNOTFULL", -#endif /* CONFIG_DISABLE_MQUEUE */ -#ifdef CONFIG_PAGING "WAIT_PAGEFILL", -#endif /* CONFIG_PAGING */ + "STOPPED", }; -static int pid_offset = PID; -static int state_offset = STATE; -static int name_offset = NAME; -static int xcpreg_offset = XCPREG; -static int name_size = NAME_SIZE; +static const struct rtos_register_stacking *cortexm_select_stackinfo(struct target *target); + +static const struct nuttx_params nuttx_params_list[] = { + { + .target_name = "cortex_m", + .stacking = NULL, + .select_stackinfo = cortexm_select_stackinfo, + }, + { + .target_name = "hla_target", + .stacking = NULL, + .select_stackinfo = cortexm_select_stackinfo, + }, + { + .target_name = "esp32", + .stacking = &nuttx_esp32_stacking, + }, + { + .target_name = "esp32s2", + .stacking = &nuttx_esp32s2_stacking, + }, + { + .target_name = "esp32s3", + .stacking = &nuttx_esp32s3_stacking, + }, + { + .target_name = "esp32c3", + .stacking = &nuttx_riscv_stacking, + }, +}; -static int rcmd_offset(const char *cmd, const char *name) +static bool cortexm_hasfpu(struct target *target) { - if (strncmp(cmd, name, strlen(name))) - return -1; + uint32_t cpacr; + struct armv7m_common *armv7m_target = target_to_armv7m(target); - if (strlen(cmd) <= strlen(name) + 1) - return -1; + if (!is_armv7m(armv7m_target) || armv7m_target->fp_feature != FPV4_SP) + return false; - return atoi(cmd + strlen(name)); + int retval = target_read_u32(target, FPU_CPACR, &cpacr); + if (retval != ERROR_OK) { + LOG_ERROR("Could not read CPACR register to check FPU state"); + return false; + } + + return cpacr & 0x00F00000; } -static int nuttx_thread_packet(struct connection *connection, - char const *packet, int packet_size) +static const struct rtos_register_stacking *cortexm_select_stackinfo(struct target *target) { - char cmd[GDB_BUFFER_SIZE / 2 + 1] = ""; /* Extra byte for null-termination */ - - if (!strncmp(packet, "qRcmd", 5)) { - size_t len = unhexify((uint8_t *)cmd, packet + 6, sizeof(cmd)); - int offset; - - if (len <= 0) - goto pass; - - offset = rcmd_offset(cmd, "nuttx.pid_offset"); - - if (offset >= 0) { - LOG_INFO("pid_offset: %d", offset); - pid_offset = offset; - goto retok; - } - - offset = rcmd_offset(cmd, "nuttx.state_offset"); - - if (offset >= 0) { - LOG_INFO("state_offset: %d", offset); - state_offset = offset; - goto retok; - } - - offset = rcmd_offset(cmd, "nuttx.name_offset"); - - if (offset >= 0) { - LOG_INFO("name_offset: %d", offset); - name_offset = offset; - goto retok; - } - - offset = rcmd_offset(cmd, "nuttx.xcpreg_offset"); - - if (offset >= 0) { - LOG_INFO("xcpreg_offset: %d", offset); - xcpreg_offset = offset; - goto retok; - } - - offset = rcmd_offset(cmd, "nuttx.name_size"); - - if (offset >= 0) { - LOG_INFO("name_size: %d", offset); - name_size = offset; - goto retok; - } - } -pass: - return rtos_thread_packet(connection, packet, packet_size); -retok: - gdb_put_packet(connection, "OK", 2); - return ERROR_OK; + return cortexm_hasfpu(target) ? &nuttx_stacking_cortex_m_fpu : &nuttx_stacking_cortex_m; } - static bool nuttx_detect_rtos(struct target *target) { if ((target->rtos->symbols) && - (target->rtos->symbols[0].address != 0) && - (target->rtos->symbols[1].address != 0)) { + (target->rtos->symbols[NX_SYM_READYTORUN].address != 0) && + (target->rtos->symbols[NX_SYM_PIDHASH].address != 0)) return true; - } return false; } static int nuttx_create(struct target *target) { + const struct nuttx_params *param; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(nuttx_params_list); i++) { + param = &nuttx_params_list[i]; + if (strcmp(target_type_name(target), param->target_name) == 0) { + LOG_INFO("Detected target \"%s\"", param->target_name); + break; + } + } + + if (i >= ARRAY_SIZE(nuttx_params_list)) { + LOG_ERROR("Could not find \"%s\" target in NuttX compatibility list", target_type_name(target)); + return JIM_ERR; + } - target->rtos->gdb_thread_packet = nuttx_thread_packet; - LOG_INFO("target type name = %s", target->type->name); - return 0; + /* We found a target in our list, copy its reference. */ + target->rtos->rtos_specific_params = (void *)param; + + return JIM_OK; +} + +static int nuttx_smp_init(struct target *target) +{ + /* Return OK for now so that the initialisation sequence doesn't stop. + * SMP case will be implemented later. */ + return ERROR_OK; } static int nuttx_update_threads(struct rtos *rtos) { - uint32_t thread_count; - struct tcb tcb; - int ret; - uint32_t head; - uint32_t tcb_addr; - uint32_t i; + struct tcbinfo tcbinfo; + uint32_t thread_count, pidhashaddr, npidhash, tcbaddr; + uint16_t pid; uint8_t state; if (!rtos->symbols) { LOG_ERROR("No symbols for NuttX"); - return -3; + return ERROR_FAIL; } - /* free previous thread details */ + /* Free previous thread details */ rtos_free_threadlist(rtos); - ret = target_read_buffer(rtos->target, rtos->symbols[1].address, - sizeof(g_tasklist), (uint8_t *)&g_tasklist); - if (ret) { - LOG_ERROR("target_read_buffer : ret = %d\n", ret); + /* NuttX provides a hash table that keeps track of all the TCBs. + * We first read its size from g_npidhash and its address from g_pidhash. + * Its content is then read from these values. + */ + int ret = target_read_u32(rtos->target, rtos->symbols[NX_SYM_NPIDHASH].address, &npidhash); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to read g_npidhash: ret = %d", ret); return ERROR_FAIL; } - thread_count = 0; + LOG_DEBUG("Hash table size (g_npidhash) = %" PRId32, npidhash); - for (i = 0; i < TASK_QUEUE_NUM; i++) { + ret = target_read_u32(rtos->target, rtos->symbols[NX_SYM_PIDHASH].address, &pidhashaddr); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to read g_pidhash address: ret = %d", ret); + return ERROR_FAIL; + } - if (g_tasklist[i].addr == 0) - continue; + LOG_DEBUG("Hash table address (g_pidhash) = %" PRIx32, pidhashaddr); - ret = target_read_u32(rtos->target, g_tasklist[i].addr, - &head); + uint8_t *pidhash = malloc(npidhash * PTR_WIDTH); + if (!pidhash) { + LOG_ERROR("Failed to allocate pidhash"); + return ERROR_FAIL; + } - if (ret) { - LOG_ERROR("target_read_u32 : ret = %d\n", ret); - return ERROR_FAIL; - } + ret = target_read_buffer(rtos->target, pidhashaddr, PTR_WIDTH * npidhash, pidhash); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to read tcbhash: ret = %d", ret); + goto errout; + } + + /* NuttX provides a struct that contains TCB offsets for required members. + * Read its content from g_tcbinfo. + */ + ret = target_read_buffer(rtos->target, rtos->symbols[NX_SYM_TCB_INFO].address, + sizeof(tcbinfo), (uint8_t *)&tcbinfo); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to read tcbinfo: ret = %d", ret); + goto errout; + } - /* readytorun head is current thread */ - if (g_tasklist[i].addr == rtos->symbols[0].address) - rtos->current_thread = head; + /* The head of the g_readytorun list is the currently running task. + * Reading in a temporary variable first to avoid endianness issues, + * rtos->current_thread is int64_t. */ + uint32_t current_thread; + ret = target_read_u32(rtos->target, rtos->symbols[NX_SYM_READYTORUN].address, ¤t_thread); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to read g_readytorun: ret = %d", ret); + goto errout; + } + rtos->current_thread = current_thread; + + thread_count = 0; + for (unsigned int i = 0; i < npidhash; i++) { + tcbaddr = target_buffer_get_u32(rtos->target, &pidhash[i * sizeof(uint32_t)]); - tcb_addr = head; - while (tcb_addr) { + if (tcbaddr) { struct thread_detail *thread; - ret = target_read_buffer(rtos->target, tcb_addr, - sizeof(tcb), (uint8_t *)&tcb); - if (ret) { - LOG_ERROR("target_read_buffer : ret = %d\n", - ret); - return ERROR_FAIL; + + ret = target_read_u16(rtos->target, tcbaddr + le_to_h_u16(tcbinfo.pid_off), &pid); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to read PID of TCB@0x%x from pidhash[%d]: ret = %d", + tcbaddr, i, ret); + goto errout; + } + + ret = target_read_u8(rtos->target, tcbaddr + le_to_h_u16(tcbinfo.state_off), &state); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to read state of TCB@0x%x from pidhash[%d]: ret = %d", + tcbaddr, i, ret); + goto errout; } + thread_count++; rtos->thread_details = realloc(rtos->thread_details, sizeof(struct thread_detail) * thread_count); + if (!rtos->thread_details) { + ret = ERROR_FAIL; + goto errout; + } + thread = &rtos->thread_details[thread_count - 1]; - thread->threadid = tcb_addr; + thread->threadid = tcbaddr; thread->exists = true; - state = tcb.dat[state_offset - 8]; thread->extra_info_str = NULL; if (state < ARRAY_SIZE(task_state_str)) { - thread->extra_info_str = malloc(256); - snprintf(thread->extra_info_str, 256, "pid:%d, %s", - tcb.dat[pid_offset - 8] | - tcb.dat[pid_offset - 8 + 1] << 8, - task_state_str[state]); + thread->extra_info_str = malloc(EXTRAINFO_SIZE); + if (!thread->extra_info_str) { + ret = ERROR_FAIL; + goto errout; + } + snprintf(thread->extra_info_str, EXTRAINFO_SIZE, "pid:%d, %s", + pid, + task_state_str[state]); } - if (name_offset) { - thread->thread_name_str = malloc(name_size + 1); - snprintf(thread->thread_name_str, name_size, - "%s", (char *)&tcb.dat[name_offset - 8]); + if (le_to_h_u16(tcbinfo.name_off)) { + thread->thread_name_str = calloc(NAME_SIZE + 1, sizeof(char)); + if (!thread->thread_name_str) { + ret = ERROR_FAIL; + goto errout; + } + ret = target_read_buffer(rtos->target, tcbaddr + le_to_h_u16(tcbinfo.name_off), + sizeof(char) * NAME_SIZE, (uint8_t *)thread->thread_name_str); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to read thread's name: ret = %d", ret); + goto errout; + } } else { - thread->thread_name_str = malloc(sizeof("None")); - strcpy(thread->thread_name_str, "None"); + thread->thread_name_str = strdup("None"); } - - tcb_addr = tcb.flink; } } - rtos->thread_count = thread_count; - return 0; + ret = ERROR_OK; + rtos->thread_count = thread_count; +errout: + free(pidhash); + return ret; } - -/* - * thread_id = tcb address; - */ -static int nuttx_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, +static int nuttx_getreg_current_thread(struct rtos *rtos, struct rtos_reg **reg_list, int *num_regs) { - int retval; - - /* Check for armv7m with *enabled* FPU, i.e. a Cortex-M4F */ - bool cm4_fpu_enabled = false; - struct armv7m_common *armv7m_target = target_to_armv7m(rtos->target); - if (is_armv7m(armv7m_target)) { - if (armv7m_target->fp_feature == FPV4_SP) { - /* Found ARM v7m target which includes a FPU */ - uint32_t cpacr; - - retval = target_read_u32(rtos->target, FPU_CPACR, &cpacr); - if (retval != ERROR_OK) { - LOG_ERROR("Could not read CPACR register to check FPU state"); - return -1; - } + struct reg **gdb_reg_list; + + /* Registers for currently running thread are not on task's stack and + * should be retrieved from reg caches via target_get_gdb_reg_list */ + int ret = target_get_gdb_reg_list(rtos->target, &gdb_reg_list, num_regs, + REG_CLASS_GENERAL); + if (ret != ERROR_OK) { + LOG_ERROR("target_get_gdb_reg_list failed %d", ret); + return ret; + } - /* Check if CP10 and CP11 are set to full access. */ - if (cpacr & 0x00F00000) { - /* Found target with enabled FPU */ - cm4_fpu_enabled = 1; - } + *reg_list = calloc(*num_regs, sizeof(struct rtos_reg)); + if (!(*reg_list)) { + LOG_ERROR("Failed to alloc memory for %d", *num_regs); + free(gdb_reg_list); + return ERROR_FAIL; + } + + for (int i = 0; i < *num_regs; i++) { + (*reg_list)[i].number = gdb_reg_list[i]->number; + (*reg_list)[i].size = gdb_reg_list[i]->size; + memcpy((*reg_list)[i].value, gdb_reg_list[i]->value, ((*reg_list)[i].size + 7) / 8); + } + + free(gdb_reg_list); + + return ERROR_OK; +} + +static int nuttx_getregs_fromstack(struct rtos *rtos, int64_t thread_id, + struct rtos_reg **reg_list, int *num_regs) +{ + uint16_t xcpreg_off; + uint32_t regsaddr; + const struct nuttx_params *priv = (const struct nuttx_params *)rtos->rtos_specific_params; + const struct rtos_register_stacking *stacking = priv->stacking; + + if (!stacking) { + if (priv->select_stackinfo) { + stacking = priv->select_stackinfo(rtos->target); + } else { + LOG_ERROR("Can't find a way to get stacking info"); + return ERROR_FAIL; } } - const struct rtos_register_stacking *stacking; - if (cm4_fpu_enabled) - stacking = &nuttx_stacking_cortex_m_fpu; - else - stacking = &nuttx_stacking_cortex_m; + int ret = target_read_u16(rtos->target, + rtos->symbols[NX_SYM_TCB_INFO].address + offsetof(struct tcbinfo, regs_off), + &xcpreg_off); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to read registers' offset: ret = %d", ret); + return ERROR_FAIL; + } - return rtos_generic_stack_read(rtos->target, stacking, - (uint32_t)thread_id + xcpreg_offset, reg_list, num_regs); + ret = target_read_u32(rtos->target, thread_id + xcpreg_off, ®saddr); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to read registers' address: ret = %d", ret); + return ERROR_FAIL; + } + + return rtos_generic_stack_read(rtos->target, stacking, regsaddr, reg_list, num_regs); } -static int nuttx_get_symbol_list_to_lookup(struct symbol_table_elem *symbol_list[]) +static int nuttx_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, + struct rtos_reg **reg_list, int *num_regs) { - unsigned int i; + if (!rtos) + return ERROR_FAIL; - *symbol_list = (struct symbol_table_elem *) calloc(1, + if (thread_id == rtos->current_thread) + return nuttx_getreg_current_thread(rtos, reg_list, num_regs); + return nuttx_getregs_fromstack(rtos, thread_id, reg_list, num_regs); +} + +static int nuttx_get_symbol_list_to_lookup(struct symbol_table_elem *symbol_list[]) +{ + *symbol_list = (struct symbol_table_elem *)calloc(1, sizeof(struct symbol_table_elem) * ARRAY_SIZE(nuttx_symbol_list)); - for (i = 0; i < ARRAY_SIZE(nuttx_symbol_list); i++) - (*symbol_list)[i].symbol_name = nuttx_symbol_list[i]; + for (unsigned int i = 0; i < ARRAY_SIZE(nuttx_symbol_list); i++) { + (*symbol_list)[i].symbol_name = nuttx_symbol_list[i].name; + (*symbol_list)[i].optional = nuttx_symbol_list[i].optional; + } - return 0; + return ERROR_OK; } struct rtos_type nuttx_rtos = { - .name = "nuttx", + .name = "NuttX", .detect_rtos = nuttx_detect_rtos, .create = nuttx_create, + .smp_init = nuttx_smp_init, .update_threads = nuttx_update_threads, .get_thread_reg_list = nuttx_get_thread_reg_list, .get_symbol_list_to_lookup = nuttx_get_symbol_list_to_lookup, diff --git a/src/rtos/rtos_nuttx_stackings.h b/src/rtos/rtos_nuttx_stackings.h index 2e5f092121..213a060336 100644 --- a/src/rtos/rtos_nuttx_stackings.h +++ b/src/rtos/rtos_nuttx_stackings.h @@ -8,5 +8,8 @@ extern const struct rtos_register_stacking nuttx_stacking_cortex_m; extern const struct rtos_register_stacking nuttx_stacking_cortex_m_fpu; extern const struct rtos_register_stacking nuttx_riscv_stacking; +extern const struct rtos_register_stacking nuttx_esp32_stacking; +extern const struct rtos_register_stacking nuttx_esp32s2_stacking; +extern const struct rtos_register_stacking nuttx_esp32s3_stacking; #endif /* INCLUDED_RTOS_NUTTX_STACKINGS_H */ --