openocd/src/rtos/nuttx.c

391 lines
9.6 KiB
C

/* 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 *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <jtag/jtag.h>
#include "target/target.h"
#include "target/target_type.h"
#include "target/armv7m.h"
#include "target/cortex_m.h"
#include "rtos.h"
#include "helper/log.h"
#include "helper/types.h"
#include "server/gdb_server.h"
#include "nuttx_header.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 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 {
uint32_t flink;
uint32_t blink;
uint8_t dat[512];
};
static struct {
uint32_t addr;
uint32_t prio;
} g_tasklist[TASK_QUEUE_NUM];
static char *task_state_str[] = {
"INVALID",
"PENDING",
"READYTORUN",
"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 */
};
/* see arch/arm/include/armv7-m/irq_cmnvector.h */
static const struct stack_register_offset nuttx_stack_offsets_cortex_m[] = {
{ ARMV7M_R0, 0x28, 32 }, /* r0 */
{ ARMV7M_R1, 0x2c, 32 }, /* r1 */
{ ARMV7M_R2, 0x30, 32 }, /* r2 */
{ ARMV7M_R3, 0x34, 32 }, /* r3 */
{ ARMV7M_R4, 0x08, 32 }, /* r4 */
{ ARMV7M_R5, 0x0c, 32 }, /* r5 */
{ ARMV7M_R6, 0x10, 32 }, /* r6 */
{ ARMV7M_R7, 0x14, 32 }, /* r7 */
{ ARMV7M_R8, 0x18, 32 }, /* r8 */
{ ARMV7M_R9, 0x1c, 32 }, /* r9 */
{ ARMV7M_R10, 0x20, 32 }, /* r10 */
{ ARMV7M_R11, 0x24, 32 }, /* r11 */
{ ARMV7M_R12, 0x38, 32 }, /* r12 */
{ ARMV7M_R13, 0, 32 }, /* sp */
{ ARMV7M_R14, 0x3c, 32 }, /* lr */
{ ARMV7M_PC, 0x40, 32 }, /* pc */
{ ARMV7M_xPSR, 0x44, 32 }, /* xPSR */
};
static const struct rtos_register_stacking nuttx_stacking_cortex_m = {
.stack_registers_size = 0x48,
.stack_growth_direction = -1,
.num_output_registers = 17,
.register_offsets = nuttx_stack_offsets_cortex_m
};
static const struct stack_register_offset nuttx_stack_offsets_cortex_m_fpu[] = {
{ ARMV7M_R0, 0x6c, 32 }, /* r0 */
{ ARMV7M_R1, 0x70, 32 }, /* r1 */
{ ARMV7M_R2, 0x74, 32 }, /* r2 */
{ ARMV7M_R3, 0x78, 32 }, /* r3 */
{ ARMV7M_R4, 0x08, 32 }, /* r4 */
{ ARMV7M_R5, 0x0c, 32 }, /* r5 */
{ ARMV7M_R6, 0x10, 32 }, /* r6 */
{ ARMV7M_R7, 0x14, 32 }, /* r7 */
{ ARMV7M_R8, 0x18, 32 }, /* r8 */
{ ARMV7M_R9, 0x1c, 32 }, /* r9 */
{ ARMV7M_R10, 0x20, 32 }, /* r10 */
{ ARMV7M_R11, 0x24, 32 }, /* r11 */
{ ARMV7M_R12, 0x7c, 32 }, /* r12 */
{ ARMV7M_R13, 0, 32 }, /* sp */
{ ARMV7M_R14, 0x80, 32 }, /* lr */
{ ARMV7M_PC, 0x84, 32 }, /* pc */
{ ARMV7M_xPSR, 0x88, 32 }, /* xPSR */
};
static const struct rtos_register_stacking nuttx_stacking_cortex_m_fpu = {
.stack_registers_size = 0x8c,
.stack_growth_direction = -1,
.num_output_registers = 17,
.register_offsets = nuttx_stack_offsets_cortex_m_fpu
};
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 int rcmd_offset(const char *cmd, const char *name)
{
if (strncmp(cmd, name, strlen(name)))
return -1;
if (strlen(cmd) <= strlen(name) + 1)
return -1;
return atoi(cmd + strlen(name));
}
static int nuttx_thread_packet(struct connection *connection,
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);
retok:
gdb_put_packet(connection, "OK", 2);
return ERROR_OK;
}
static bool nuttx_detect_rtos(struct target *target)
{
if ((target->rtos->symbols) &&
(target->rtos->symbols[0].address != 0) &&
(target->rtos->symbols[1].address != 0)) {
return true;
}
return false;
}
static int nuttx_create(struct target *target)
{
target->rtos->gdb_thread_packet = nuttx_thread_packet;
LOG_INFO("target type name = %s", target->type->name);
return 0;
}
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;
uint8_t state;
if (!rtos->symbols) {
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;
}
thread_count = 0;
for (i = 0; i < TASK_QUEUE_NUM; i++) {
if (g_tasklist[i].addr == 0)
continue;
ret = target_read_u32(rtos->target, g_tasklist[i].addr,
&head);
if (ret) {
LOG_ERROR("target_read_u32 : ret = %d\n", ret);
return ERROR_FAIL;
}
/* readytorun head is current thread */
if (g_tasklist[i].addr == rtos->symbols[0].address)
rtos->current_thread = head;
tcb_addr = head;
while (tcb_addr) {
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;
}
thread_count++;
rtos->thread_details = realloc(rtos->thread_details,
sizeof(struct thread_detail) * thread_count);
thread = &rtos->thread_details[thread_count - 1];
thread->threadid = tcb_addr;
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]);
}
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]);
} else {
thread->thread_name_str = malloc(sizeof("None"));
strcpy(thread->thread_name_str, "None");
}
tcb_addr = tcb.flink;
}
}
rtos->thread_count = thread_count;
return 0;
}
/*
* thread_id = tcb address;
*/
static int nuttx_get_thread_reg_list(struct rtos *rtos, int64_t thread_id,
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;
}
/* 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 (cm4_fpu_enabled)
stacking = &nuttx_stacking_cortex_m_fpu;
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[])
{
unsigned int i;
*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];
return 0;
}
struct rtos_type nuttx_rtos = {
.name = "nuttx",
.detect_rtos = nuttx_detect_rtos,
.create = nuttx_create,
.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,
};