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.kurubas@espressif.com> Change-Id: I0868a22da2ed2ab664c82b17c171dc59ede78d10 Reviewed-on: https://review.openocd.org/c/openocd/+/7444 Tested-by: jenkins Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
This commit is contained in:
parent
9ce6b0898e
commit
ee31f1578a
|
@ -34,5 +34,4 @@ noinst_LTLIBRARIES += %D%/librtos.la
|
||||||
%D%/rtos_mqx_stackings.h \
|
%D%/rtos_mqx_stackings.h \
|
||||||
%D%/rtos_riot_stackings.h \
|
%D%/rtos_riot_stackings.h \
|
||||||
%D%/rtos_ucos_iii_stackings.h \
|
%D%/rtos_ucos_iii_stackings.h \
|
||||||
%D%/rtos_nuttx_stackings.h \
|
%D%/rtos_nuttx_stackings.h
|
||||||
%D%/nuttx_header.h
|
|
||||||
|
|
561
src/rtos/nuttx.c
561
src/rtos/nuttx.c
|
@ -18,53 +18,60 @@
|
||||||
#include "rtos.h"
|
#include "rtos.h"
|
||||||
#include "helper/log.h"
|
#include "helper/log.h"
|
||||||
#include "helper/types.h"
|
#include "helper/types.h"
|
||||||
#include "server/gdb_server.h"
|
#include "target/register.h"
|
||||||
|
|
||||||
#include "nuttx_header.h"
|
|
||||||
#include "rtos_nuttx_stackings.h"
|
#include "rtos_nuttx_stackings.h"
|
||||||
|
|
||||||
int rtos_thread_packet(struct connection *connection, const char *packet, int packet_size);
|
#define NAME_SIZE 32
|
||||||
|
#define EXTRAINFO_SIZE 256
|
||||||
|
|
||||||
#ifdef CONFIG_DISABLE_SIGNALS
|
/* Only 32-bit CPUs are supported by the current implementation. Supporting
|
||||||
#define SIG_QUEUE_NUM 0
|
* other CPUs will require reading this information from the target and
|
||||||
#else
|
* adapting the code accordingly.
|
||||||
#define SIG_QUEUE_NUM 1
|
*/
|
||||||
#endif /* CONFIG_DISABLE_SIGNALS */
|
#define PTR_WIDTH 4
|
||||||
|
|
||||||
#ifdef CONFIG_DISABLE_MQUEUE
|
struct nuttx_params {
|
||||||
#define M_QUEUE_NUM 0
|
const char *target_name;
|
||||||
#else
|
const struct rtos_register_stacking *stacking;
|
||||||
#define M_QUEUE_NUM 2
|
const struct rtos_register_stacking *(*select_stackinfo)(struct target *target);
|
||||||
#endif /* CONFIG_DISABLE_MQUEUE */
|
|
||||||
|
|
||||||
#ifdef CONFIG_PAGING
|
|
||||||
#define PAGING_QUEUE_NUM 1
|
|
||||||
#else
|
|
||||||
#define PAGING_QUEUE_NUM 0
|
|
||||||
#endif /* CONFIG_PAGING */
|
|
||||||
|
|
||||||
|
|
||||||
#define TASK_QUEUE_NUM (6 + SIG_QUEUE_NUM + M_QUEUE_NUM + PAGING_QUEUE_NUM)
|
|
||||||
|
|
||||||
|
|
||||||
/* see nuttx/sched/os_start.c */
|
|
||||||
static char *nuttx_symbol_list[] = {
|
|
||||||
"g_readytorun", /* 0: must be top of this array */
|
|
||||||
"g_tasklisttable",
|
|
||||||
NULL
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* see nuttx/include/nuttx/sched.h */
|
/*
|
||||||
struct tcb {
|
* struct tcbinfo_s is located in the sched.h
|
||||||
uint32_t flink;
|
* https://github.com/apache/nuttx/blob/master/include/nuttx/sched.h
|
||||||
uint32_t blink;
|
*/
|
||||||
uint8_t dat[512];
|
#define TCBINFO_TARGET_SIZE 22
|
||||||
|
struct tcbinfo {
|
||||||
|
uint16_t pid_off; /* Offset of tcb.pid */
|
||||||
|
uint16_t state_off; /* Offset of tcb.task_state */
|
||||||
|
uint16_t pri_off; /* Offset of tcb.sched_priority */
|
||||||
|
uint16_t name_off; /* Offset of tcb.name */
|
||||||
|
uint16_t regs_off; /* Offset of tcb.regs */
|
||||||
|
uint16_t basic_num; /* Num of genernal regs */
|
||||||
|
uint16_t total_num; /* Num of regs in tcbinfo.reg_offs */
|
||||||
|
target_addr_t xcpreg_off; /* Offset pointer of xcp.regs */
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct {
|
struct symbols {
|
||||||
uint32_t addr;
|
const char *name;
|
||||||
uint32_t prio;
|
bool optional;
|
||||||
} g_tasklist[TASK_QUEUE_NUM];
|
};
|
||||||
|
|
||||||
|
/* 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 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[] = {
|
static char *task_state_str[] = {
|
||||||
"INVALID",
|
"INVALID",
|
||||||
|
@ -73,261 +80,363 @@ static char *task_state_str[] = {
|
||||||
"RUNNING",
|
"RUNNING",
|
||||||
"INACTIVE",
|
"INACTIVE",
|
||||||
"WAIT_SEM",
|
"WAIT_SEM",
|
||||||
#ifndef CONFIG_DISABLE_SIGNALS
|
|
||||||
"WAIT_SIG",
|
"WAIT_SIG",
|
||||||
#endif /* CONFIG_DISABLE_SIGNALS */
|
|
||||||
#ifndef CONFIG_DISABLE_MQUEUE
|
|
||||||
"WAIT_MQNOTEMPTY",
|
"WAIT_MQNOTEMPTY",
|
||||||
"WAIT_MQNOTFULL",
|
"WAIT_MQNOTFULL",
|
||||||
#endif /* CONFIG_DISABLE_MQUEUE */
|
|
||||||
#ifdef CONFIG_PAGING
|
|
||||||
"WAIT_PAGEFILL",
|
"WAIT_PAGEFILL",
|
||||||
#endif /* CONFIG_PAGING */
|
"STOPPED",
|
||||||
};
|
};
|
||||||
|
|
||||||
static int pid_offset = PID;
|
static const struct rtos_register_stacking *cortexm_select_stackinfo(struct target *target);
|
||||||
static int state_offset = STATE;
|
|
||||||
static int name_offset = NAME;
|
|
||||||
static int xcpreg_offset = XCPREG;
|
|
||||||
static int name_size = NAME_SIZE;
|
|
||||||
|
|
||||||
static int rcmd_offset(const char *cmd, const char *name)
|
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 bool cortexm_hasfpu(struct target *target)
|
||||||
{
|
{
|
||||||
if (strncmp(cmd, name, strlen(name)))
|
uint32_t cpacr;
|
||||||
return -1;
|
struct armv7m_common *armv7m_target = target_to_armv7m(target);
|
||||||
|
|
||||||
if (strlen(cmd) <= strlen(name) + 1)
|
if (!is_armv7m(armv7m_target) || armv7m_target->fp_feature == FP_NONE)
|
||||||
return -1;
|
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");
|
||||||
static int nuttx_thread_packet(struct connection *connection,
|
return false;
|
||||||
char const *packet, int packet_size)
|
|
||||||
{
|
|
||||||
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);
|
return cpacr & 0x00F00000;
|
||||||
retok:
|
|
||||||
gdb_put_packet(connection, "OK", 2);
|
|
||||||
return ERROR_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct rtos_register_stacking *cortexm_select_stackinfo(struct target *target)
|
||||||
|
{
|
||||||
|
return cortexm_hasfpu(target) ? &nuttx_stacking_cortex_m_fpu : &nuttx_stacking_cortex_m;
|
||||||
|
}
|
||||||
|
|
||||||
static bool nuttx_detect_rtos(struct target *target)
|
static bool nuttx_detect_rtos(struct target *target)
|
||||||
{
|
{
|
||||||
if ((target->rtos->symbols) &&
|
if (target->rtos->symbols &&
|
||||||
(target->rtos->symbols[0].address != 0) &&
|
target->rtos->symbols[NX_SYM_READYTORUN].address != 0 &&
|
||||||
(target->rtos->symbols[1].address != 0)) {
|
target->rtos->symbols[NX_SYM_PIDHASH].address != 0)
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nuttx_create(struct target *target)
|
static int nuttx_create(struct target *target)
|
||||||
{
|
{
|
||||||
|
const struct nuttx_params *param;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
target->rtos->gdb_thread_packet = nuttx_thread_packet;
|
for (i = 0; i < ARRAY_SIZE(nuttx_params_list); i++) {
|
||||||
LOG_INFO("target type name = %s", target->type->name);
|
param = &nuttx_params_list[i];
|
||||||
return 0;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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 target_addr_t target_buffer_get_addr(struct target *target, const uint8_t *buffer)
|
||||||
|
{
|
||||||
|
#if PTR_WIDTH == 8
|
||||||
|
return target_buffer_get_u64(target, buffer);
|
||||||
|
#else
|
||||||
|
return target_buffer_get_u32(target, buffer);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nuttx_update_threads(struct rtos *rtos)
|
static int nuttx_update_threads(struct rtos *rtos)
|
||||||
{
|
{
|
||||||
uint32_t thread_count;
|
struct tcbinfo tcbinfo;
|
||||||
struct tcb tcb;
|
uint32_t pidhashaddr, npidhash, tcbaddr;
|
||||||
int ret;
|
uint16_t pid;
|
||||||
uint32_t head;
|
|
||||||
uint32_t tcb_addr;
|
|
||||||
uint32_t i;
|
|
||||||
uint8_t state;
|
uint8_t state;
|
||||||
|
|
||||||
if (!rtos->symbols) {
|
if (!rtos->symbols) {
|
||||||
LOG_ERROR("No symbols for NuttX");
|
LOG_ERROR("No symbols for nuttx");
|
||||||
return -3;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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);
|
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
thread_count = 0;
|
/* Free previous thread details */
|
||||||
|
rtos_free_threadlist(rtos);
|
||||||
|
|
||||||
for (i = 0; i < TASK_QUEUE_NUM; i++) {
|
/* 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;
|
||||||
|
}
|
||||||
|
|
||||||
if (g_tasklist[i].addr == 0)
|
LOG_DEBUG("Hash table size (g_npidhash) = %" PRId32, npidhash);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DEBUG("Hash table address (g_pidhash) = %" PRIx32, pidhashaddr);
|
||||||
|
|
||||||
|
uint8_t *pidhash = malloc(npidhash * PTR_WIDTH);
|
||||||
|
if (!pidhash) {
|
||||||
|
LOG_ERROR("Failed to allocate pidhash");
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
uint8_t buff[TCBINFO_TARGET_SIZE];
|
||||||
|
ret = target_read_buffer(rtos->target, rtos->symbols[NX_SYM_TCB_INFO].address, sizeof(buff), buff);
|
||||||
|
if (ret != ERROR_OK) {
|
||||||
|
LOG_ERROR("Failed to read tcbinfo: ret = %d", ret);
|
||||||
|
goto errout;
|
||||||
|
}
|
||||||
|
tcbinfo.pid_off = target_buffer_get_u16(rtos->target, buff);
|
||||||
|
tcbinfo.state_off = target_buffer_get_u16(rtos->target, buff + 2);
|
||||||
|
tcbinfo.pri_off = target_buffer_get_u16(rtos->target, buff + 4);
|
||||||
|
tcbinfo.name_off = target_buffer_get_u16(rtos->target, buff + 6);
|
||||||
|
tcbinfo.regs_off = target_buffer_get_u16(rtos->target, buff + 8);
|
||||||
|
tcbinfo.basic_num = target_buffer_get_u16(rtos->target, buff + 10);
|
||||||
|
tcbinfo.total_num = target_buffer_get_u16(rtos->target, buff + 12);
|
||||||
|
tcbinfo.xcpreg_off = target_buffer_get_addr(rtos->target, buff + 14);
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
|
||||||
|
uint32_t thread_count = 0;
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < npidhash; i++) {
|
||||||
|
tcbaddr = target_buffer_get_u32(rtos->target, &pidhash[i * PTR_WIDTH]);
|
||||||
|
|
||||||
|
if (!tcbaddr)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
ret = target_read_u32(rtos->target, g_tasklist[i].addr,
|
ret = target_read_u16(rtos->target, tcbaddr + tcbinfo.pid_off, &pid);
|
||||||
&head);
|
if (ret != ERROR_OK) {
|
||||||
|
LOG_ERROR("Failed to read PID of TCB@0x%x from pidhash[%d]: ret = %d",
|
||||||
if (ret) {
|
tcbaddr, i, ret);
|
||||||
LOG_ERROR("target_read_u32 : ret = %d\n", ret);
|
goto errout;
|
||||||
return ERROR_FAIL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* readytorun head is current thread */
|
ret = target_read_u8(rtos->target, tcbaddr + tcbinfo.state_off, &state);
|
||||||
if (g_tasklist[i].addr == rtos->symbols[0].address)
|
if (ret != ERROR_OK) {
|
||||||
rtos->current_thread = head;
|
LOG_ERROR("Failed to read state of TCB@0x%x from pidhash[%d]: ret = %d",
|
||||||
|
tcbaddr, i, ret);
|
||||||
|
goto errout;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct thread_detail *new_thread_details = realloc(rtos->thread_details,
|
||||||
|
sizeof(struct thread_detail) * (thread_count + 1));
|
||||||
|
if (!new_thread_details) {
|
||||||
|
ret = ERROR_FAIL;
|
||||||
|
goto errout;
|
||||||
|
}
|
||||||
|
|
||||||
tcb_addr = head;
|
struct thread_detail *thread = &new_thread_details[thread_count];
|
||||||
while (tcb_addr) {
|
thread->threadid = tcbaddr;
|
||||||
struct thread_detail *thread;
|
thread->exists = true;
|
||||||
ret = target_read_buffer(rtos->target, tcb_addr,
|
thread->extra_info_str = NULL;
|
||||||
sizeof(tcb), (uint8_t *)&tcb);
|
|
||||||
if (ret) {
|
rtos->thread_details = new_thread_details;
|
||||||
LOG_ERROR("target_read_buffer : ret = %d\n",
|
thread_count++;
|
||||||
ret);
|
|
||||||
return ERROR_FAIL;
|
if (state < ARRAY_SIZE(task_state_str)) {
|
||||||
|
thread->extra_info_str = malloc(EXTRAINFO_SIZE);
|
||||||
|
if (!thread->extra_info_str) {
|
||||||
|
ret = ERROR_FAIL;
|
||||||
|
goto errout;
|
||||||
}
|
}
|
||||||
thread_count++;
|
snprintf(thread->extra_info_str, EXTRAINFO_SIZE, "pid:%d, %s",
|
||||||
|
pid,
|
||||||
|
task_state_str[state]);
|
||||||
|
}
|
||||||
|
|
||||||
rtos->thread_details = realloc(rtos->thread_details,
|
if (tcbinfo.name_off) {
|
||||||
sizeof(struct thread_detail) * thread_count);
|
thread->thread_name_str = calloc(NAME_SIZE + 1, sizeof(char));
|
||||||
thread = &rtos->thread_details[thread_count - 1];
|
if (!thread->thread_name_str) {
|
||||||
thread->threadid = tcb_addr;
|
ret = ERROR_FAIL;
|
||||||
thread->exists = true;
|
goto errout;
|
||||||
|
|
||||||
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]);
|
|
||||||
}
|
}
|
||||||
|
ret = target_read_buffer(rtos->target, tcbaddr + tcbinfo.name_off,
|
||||||
if (name_offset) {
|
sizeof(char) * NAME_SIZE, (uint8_t *)thread->thread_name_str);
|
||||||
thread->thread_name_str = malloc(name_size + 1);
|
if (ret != ERROR_OK) {
|
||||||
snprintf(thread->thread_name_str, name_size,
|
LOG_ERROR("Failed to read thread's name: ret = %d", ret);
|
||||||
"%s", (char *)&tcb.dat[name_offset - 8]);
|
goto errout;
|
||||||
} else {
|
|
||||||
thread->thread_name_str = malloc(sizeof("None"));
|
|
||||||
strcpy(thread->thread_name_str, "None");
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
tcb_addr = tcb.flink;
|
thread->thread_name_str = strdup("None");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rtos->thread_count = thread_count;
|
|
||||||
|
|
||||||
return 0;
|
ret = ERROR_OK;
|
||||||
|
rtos->thread_count = thread_count;
|
||||||
|
errout:
|
||||||
|
free(pidhash);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int nuttx_getreg_current_thread(struct rtos *rtos,
|
||||||
|
struct rtos_reg **reg_list, int *num_regs)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
*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 = 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* thread_id = tcb address;
|
|
||||||
*/
|
|
||||||
static int nuttx_get_thread_reg_list(struct rtos *rtos, int64_t thread_id,
|
static int nuttx_get_thread_reg_list(struct rtos *rtos, int64_t thread_id,
|
||||||
struct rtos_reg **reg_list, int *num_regs)
|
struct rtos_reg **reg_list, int *num_regs)
|
||||||
{
|
{
|
||||||
int retval;
|
if (!rtos) {
|
||||||
|
LOG_ERROR("NUTTX: out of memory");
|
||||||
/* Check for armv7m with *enabled* FPU, i.e. a Cortex-M4F */
|
return ERROR_FAIL;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if CP10 and CP11 are set to full access. */
|
|
||||||
if (cpacr & 0x00F00000) {
|
|
||||||
/* Found target with enabled FPU */
|
|
||||||
cm4_fpu_enabled = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const struct rtos_register_stacking *stacking;
|
if (thread_id == rtos->current_thread)
|
||||||
if (cm4_fpu_enabled)
|
return nuttx_getreg_current_thread(rtos, reg_list, num_regs);
|
||||||
stacking = &nuttx_stacking_cortex_m_fpu;
|
return nuttx_getregs_fromstack(rtos, thread_id, reg_list, num_regs);
|
||||||
else
|
|
||||||
stacking = &nuttx_stacking_cortex_m;
|
|
||||||
|
|
||||||
return rtos_generic_stack_read(rtos->target, stacking,
|
|
||||||
(uint32_t)thread_id + xcpreg_offset, reg_list, num_regs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nuttx_get_symbol_list_to_lookup(struct symbol_table_elem *symbol_list[])
|
static int nuttx_get_symbol_list_to_lookup(struct symbol_table_elem *symbol_list[])
|
||||||
{
|
{
|
||||||
unsigned int i;
|
*symbol_list = calloc(ARRAY_SIZE(nuttx_symbol_list), sizeof(**symbol_list));
|
||||||
|
if (!*symbol_list) {
|
||||||
|
LOG_ERROR("NUTTX: out of memory");
|
||||||
|
return ERROR_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
*symbol_list = (struct symbol_table_elem *) calloc(1,
|
for (unsigned int i = 0; i < ARRAY_SIZE(nuttx_symbol_list); i++) {
|
||||||
sizeof(struct symbol_table_elem) * ARRAY_SIZE(nuttx_symbol_list));
|
(*symbol_list)[i].symbol_name = nuttx_symbol_list[i].name;
|
||||||
|
(*symbol_list)[i].optional = nuttx_symbol_list[i].optional;
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(nuttx_symbol_list); i++)
|
return ERROR_OK;
|
||||||
(*symbol_list)[i].symbol_name = nuttx_symbol_list[i];
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const struct rtos_type nuttx_rtos = {
|
const struct rtos_type nuttx_rtos = {
|
||||||
.name = "nuttx",
|
.name = "nuttx",
|
||||||
.detect_rtos = nuttx_detect_rtos,
|
.detect_rtos = nuttx_detect_rtos,
|
||||||
.create = nuttx_create,
|
.create = nuttx_create,
|
||||||
|
.smp_init = nuttx_smp_init,
|
||||||
.update_threads = nuttx_update_threads,
|
.update_threads = nuttx_update_threads,
|
||||||
.get_thread_reg_list = nuttx_get_thread_reg_list,
|
.get_thread_reg_list = nuttx_get_thread_reg_list,
|
||||||
.get_symbol_list_to_lookup = nuttx_get_symbol_list_to_lookup,
|
.get_symbol_list_to_lookup = nuttx_get_symbol_list_to_lookup,
|
||||||
|
|
|
@ -1,60 +0,0 @@
|
||||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* Copyright 2016,2017 Sony Video & Sound Products Inc. *
|
|
||||||
* Masatoshi Tateishi - Masatoshi.Tateishi@jp.sony.com *
|
|
||||||
* Masayuki Ishikawa - Masayuki.Ishikawa@jp.sony.com *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#ifndef OPENOCD_RTOS_NUTTX_HEADER_H
|
|
||||||
#define OPENOCD_RTOS_NUTTX_HEADER_H
|
|
||||||
|
|
||||||
/* gdb script to update the header file
|
|
||||||
according to kernel version and build option
|
|
||||||
before executing function awareness
|
|
||||||
kernel symbol must be loaded : symbol nuttx
|
|
||||||
|
|
||||||
define awareness
|
|
||||||
set logging off
|
|
||||||
set logging file nuttx_header.h
|
|
||||||
set logging on
|
|
||||||
|
|
||||||
printf "#define PID %p\n",&((struct tcb_s *)(0))->pid
|
|
||||||
printf "#define XCPREG %p\n",&((struct tcb_s *)(0))->xcp.regs
|
|
||||||
printf "#define STATE %p\n",&((struct tcb_s *)(0))->task_state
|
|
||||||
printf "#define NAME %p\n",&((struct tcb_s *)(0))->name
|
|
||||||
printf "#define NAME_SIZE %d\n",sizeof(((struct tcb_s *)(0))->name)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
OR ~/.gdbinit
|
|
||||||
|
|
||||||
|
|
||||||
define hookpost-file
|
|
||||||
|
|
||||||
if &g_readytorun != 0
|
|
||||||
eval "monitor nuttx.pid_offset %d", &((struct tcb_s *)(0))->pid
|
|
||||||
eval "monitor nuttx.xcpreg_offset %d", &((struct tcb_s *)(0))->xcp.regs
|
|
||||||
eval "monitor nuttx.state_offset %d", &((struct tcb_s *)(0))->task_state
|
|
||||||
eval "monitor nuttx.name_offset %d", &((struct tcb_s *)(0))->name
|
|
||||||
eval "monitor nuttx.name_size %d", sizeof(((struct tcb_s *)(0))->name)
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* default offset */
|
|
||||||
#define PID 0xc
|
|
||||||
#define XCPREG 0x70
|
|
||||||
#define STATE 0x19
|
|
||||||
#define NAME 0xb8
|
|
||||||
#define NAME_SIZE 32
|
|
||||||
|
|
||||||
/* defconfig of nuttx */
|
|
||||||
/* #define CONFIG_DISABLE_SIGNALS */
|
|
||||||
#define CONFIG_DISABLE_MQUEUE
|
|
||||||
/* #define CONFIG_PAGING */
|
|
||||||
|
|
||||||
|
|
||||||
#endif /* OPENOCD_RTOS_NUTTX_HEADER_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;
|
||||||
extern const struct rtos_register_stacking nuttx_stacking_cortex_m_fpu;
|
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_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 */
|
#endif /* INCLUDED_RTOS_NUTTX_STACKINGS_H */
|
||||||
|
|
Loading…
Reference in New Issue