300 lines
8.4 KiB
C
300 lines
8.4 KiB
C
/*
|
|
* File : stack.c
|
|
* This file is part of RT-Thread RTOS
|
|
* COPYRIGHT (C) 2013, RT-Thread Develop Team
|
|
*
|
|
* The license and distribution terms for this file may be
|
|
* found in the file LICENSE in this distribution or at
|
|
* http://www.rt-thread.org/license/LICENSE
|
|
*
|
|
* Change Logs:
|
|
* Date Author Notes
|
|
*/
|
|
#include <rtthread.h>
|
|
#include <string.h>
|
|
#include "arm_mcu_pub.h"
|
|
#include "rthw.h"
|
|
|
|
/*****************************/
|
|
/* CPU Mode */
|
|
/*****************************/
|
|
#define USERMODE 0x10
|
|
#define FIQMODE 0x11
|
|
#define IRQMODE 0x12
|
|
#define SVCMODE 0x13
|
|
#define ABORTMODE 0x17
|
|
#define UNDEFMODE 0x1b
|
|
#define MODEMASK 0x1f
|
|
#define NOINT 0xc0
|
|
|
|
#define RT_HW_DUMP_STACK_MEM 0
|
|
|
|
extern uint32_t und_stack_start;
|
|
extern uint32_t abt_stack_start;
|
|
extern uint32_t fiq_stack_start;
|
|
extern uint32_t irq_stack_start;
|
|
extern uint32_t sys_stack_start;
|
|
extern uint32_t svc_stack_start;
|
|
|
|
/**
|
|
* This function will initialize thread stack
|
|
*
|
|
* @param tentry the entry of thread
|
|
* @param parameter the parameter of entry
|
|
* @param stack_addr the beginning stack address
|
|
* @param texit the function will be called when thread exit
|
|
*
|
|
* @return stack address
|
|
*/
|
|
rt_uint8_t *rt_hw_stack_init(void *tentry, void *parameter,
|
|
rt_uint8_t *stack_addr, void *texit)
|
|
{
|
|
rt_uint32_t *stk;
|
|
|
|
//stk = (rt_uint32_t*)stack_addr;
|
|
stack_addr += sizeof(rt_uint32_t);
|
|
stack_addr = (rt_uint8_t *)RT_ALIGN_DOWN((rt_uint32_t)stack_addr, 8);
|
|
stk = (rt_uint32_t *)stack_addr;
|
|
|
|
*(--stk) = (rt_uint32_t)tentry; /* entry point */
|
|
*(--stk) = (rt_uint32_t)texit; /* lr */
|
|
*(--stk) = 0xdeadbeef; /* r12 */
|
|
*(--stk) = 0xdeadbeef; /* r11 */
|
|
*(--stk) = 0xdeadbeef; /* r10 */
|
|
*(--stk) = 0xdeadbeef; /* r9 */
|
|
*(--stk) = 0xdeadbeef; /* r8 */
|
|
*(--stk) = 0xdeadbeef; /* r7 */
|
|
*(--stk) = 0xdeadbeef; /* r6 */
|
|
*(--stk) = 0xdeadbeef; /* r5 */
|
|
*(--stk) = 0xdeadbeef; /* r4 */
|
|
*(--stk) = 0xdeadbeef; /* r3 */
|
|
*(--stk) = 0xdeadbeef; /* r2 */
|
|
*(--stk) = 0xdeadbeef; /* r1 */
|
|
*(--stk) = (rt_uint32_t)parameter; /* r0 : argument */
|
|
/* cpsr */
|
|
if ((rt_uint32_t)tentry & 0x01)
|
|
*(--stk) = SVCMODE | 0x20; /* thumb mode */
|
|
else
|
|
*(--stk) = SVCMODE; /* arm mode */
|
|
|
|
/* return task's current stack address */
|
|
return (rt_uint8_t *)stk;
|
|
}
|
|
|
|
#if RT_HW_DUMP_STACK_MEM
|
|
static void rt_hw_stack_mem_dump(uint32_t sp, uint32_t stack_end_addr)
|
|
{
|
|
uint32_t data = *((uint32_t *) sp);
|
|
uint32_t cnt = 0;
|
|
|
|
rt_kprintf("cur sp=%08x, stack end=%08x\n", sp, stack_end_addr);
|
|
for (; sp < stack_end_addr; sp += sizeof(size_t)) {
|
|
data = *((uint32_t*) sp);
|
|
if ((cnt++ % 4) == 0) {
|
|
rt_kprintf("\n%08x: ", sp);
|
|
}
|
|
rt_kprintf("%08x ", data);
|
|
}
|
|
rt_kprintf("\n");
|
|
}
|
|
#endif
|
|
|
|
static void rt_hw_stack_parse_backtrace(const char *str_type,
|
|
uint32_t stack_start_addr, uint32_t stack_end_addr,
|
|
uint32_t sp, rt_bool_t thumb_mode)
|
|
{
|
|
extern void fiq_handler();
|
|
extern char __rt_init_end;
|
|
|
|
uint32_t call_stack_buf[32] = {0};
|
|
uint32_t code_start_addr;
|
|
uint32_t code_end_addr;
|
|
uint32_t pc;
|
|
int call_stack_index = 0;
|
|
uint32_t init_sp = sp;
|
|
|
|
/* TODO: it should be more portable to calculate code space, like _stext _etext */
|
|
code_start_addr = (uint32_t)fiq_handler;
|
|
code_end_addr = (uint32_t)(&__rt_init_end);
|
|
|
|
#if RT_HW_DUMP_STACK_MEM
|
|
rt_hw_stack_mem_dump(sp, stack_end_addr);
|
|
#endif
|
|
for (; sp < stack_end_addr; sp += sizeof(size_t)) {
|
|
pc = *((uint32_t *) sp);
|
|
|
|
/* ARM9 using thumb instruction, so the pc must be an odd number */
|
|
if (thumb_mode && (pc & 1) == 0) {
|
|
continue;
|
|
}
|
|
|
|
if ((code_start_addr < pc) && (pc < code_end_addr)) {
|
|
if (pc & 1) {
|
|
pc = pc -1;
|
|
}
|
|
|
|
call_stack_buf[call_stack_index] = pc;
|
|
call_stack_index++;
|
|
}
|
|
}
|
|
|
|
if (call_stack_index > 0) {
|
|
int index;
|
|
|
|
rt_kprintf("%s, stack=[0x%x,0x%x], sp=0x%x, stackoverflow=%d\n",
|
|
str_type, stack_start_addr, stack_end_addr, init_sp, init_sp < stack_start_addr);
|
|
rt_kprintf("addr2line.exe -e rtthread.elf -piaf");
|
|
for (index = 0; index < call_stack_index; index++) {
|
|
rt_kprintf(" %lx", call_stack_buf[index]);
|
|
}
|
|
rt_kprintf("\n");
|
|
} else if (init_sp < stack_start_addr) {
|
|
rt_kprintf("%s, stack=[0x%x,0x%x], sp=0x%x, stackoverflow=%d\n",
|
|
str_type, stack_start_addr, stack_end_addr, init_sp, init_sp < stack_start_addr);
|
|
}
|
|
}
|
|
|
|
static void rt_hw_thread_stack_print(rt_thread_t thread, rt_bool_t in_exception)
|
|
{
|
|
uint32_t stack_start_addr;
|
|
uint32_t stack_end_addr;
|
|
uint32_t sp;
|
|
|
|
if (NULL == thread) {
|
|
thread = rt_thread_self();
|
|
}
|
|
|
|
stack_start_addr = (uint32_t)thread ->stack_addr;
|
|
stack_end_addr = thread->stack_size + stack_start_addr;
|
|
|
|
if (in_exception) {
|
|
sp = *(uint32_t*)(MCU_REG_BACKUP_SP_SVC);
|
|
} else {
|
|
sp = (uint32_t)thread->sp;
|
|
}
|
|
|
|
rt_hw_stack_parse_backtrace(thread->name, stack_start_addr, stack_end_addr, sp, RT_TRUE);
|
|
}
|
|
|
|
void rt_hw_exception_stack_print(void)
|
|
{
|
|
uint32_t stack_start;
|
|
uint32_t sp;
|
|
|
|
sp = *(uint32_t*)(MCU_REG_BACKUP_SP_FIQ);
|
|
stack_start = (uint32_t)&fiq_stack_start;
|
|
rt_hw_stack_parse_backtrace("fiq", (stack_start - FIQ_STACK_SIZE), stack_start, sp, RT_FALSE);
|
|
|
|
sp = *(uint32_t*)(MCU_REG_BACKUP_SP_IRQ);
|
|
stack_start = (uint32_t)&irq_stack_start;
|
|
rt_hw_stack_parse_backtrace("irq", (stack_start - IRQ_STACK_SIZE), stack_start, sp, RT_FALSE);
|
|
|
|
sp = *(uint32_t*)(MCU_REG_BACKUP_SP_UND);
|
|
stack_start = (uint32_t)&und_stack_start;
|
|
rt_hw_stack_parse_backtrace("und", stack_start - UND_STACK_SIZE, stack_start, sp, RT_FALSE);
|
|
|
|
sp = *(uint32_t*)(MCU_REG_BACKUP_SP_ABT);
|
|
stack_start = (uint32_t)&abt_stack_start;
|
|
rt_hw_stack_parse_backtrace("abt", stack_start - ABT_STACK_SIZE, stack_start, sp, RT_FALSE);
|
|
|
|
sp = *(uint32_t*)(MCU_REG_BACKUP_SP_SVC);
|
|
stack_start = (uint32_t)&svc_stack_start;
|
|
rt_hw_stack_parse_backtrace("svc", stack_start - SVC_STACK_SIZE, stack_start, sp, RT_FALSE);
|
|
|
|
sp = *(uint32_t*)(MCU_REG_BACKUP_SP_SYS);
|
|
stack_start = (uint32_t)&sys_stack_start;
|
|
rt_hw_stack_parse_backtrace("sys", stack_start - SYS_STACK_SIZE, stack_start, sp, RT_FALSE);
|
|
}
|
|
|
|
void rt_hw_stack_print_after_exception(void)
|
|
{
|
|
rt_hw_thread_stack_print(NULL, RT_TRUE);
|
|
rt_hw_exception_stack_print();
|
|
}
|
|
|
|
void rt_hw_stack_print(rt_thread_t thread)
|
|
{
|
|
rt_hw_thread_stack_print(thread, RT_FALSE);
|
|
}
|
|
|
|
void rt_dump_all_thread_stack(void)
|
|
{
|
|
struct rt_object_information *info;
|
|
struct rt_list_node *node;
|
|
struct rt_thread *thread;
|
|
rt_base_t int_level = 0;
|
|
|
|
int_level = rt_hw_interrupt_disable();
|
|
|
|
info = rt_object_get_information(RT_Object_Class_Thread);
|
|
if (NULL == info)
|
|
{
|
|
rt_kprintf("can't find thread list\n");
|
|
rt_hw_interrupt_enable(int_level);
|
|
return;
|
|
}
|
|
|
|
for (node = info->object_list.next; node != &info->object_list; node = node->next)
|
|
{
|
|
thread = rt_list_entry(node, struct rt_thread, list);
|
|
rt_hw_stack_print(thread);
|
|
}
|
|
|
|
rt_hw_interrupt_enable(int_level);
|
|
}
|
|
|
|
void rt_stack_print(int argc, char **argv)
|
|
{
|
|
struct rt_object_information *info;
|
|
struct rt_thread *thread;
|
|
struct rt_list_node *node;
|
|
size_t length;
|
|
|
|
if (argc < 2)
|
|
{
|
|
rt_kprintf("usage: stack task_name|all_tasks\n");
|
|
rt_hw_stack_print(NULL);
|
|
return;
|
|
}
|
|
|
|
info = rt_object_get_information(RT_Object_Class_Thread);
|
|
if (NULL == info)
|
|
{
|
|
rt_kprintf("can't find thread list\n");
|
|
return;
|
|
}
|
|
|
|
length = strlen((const char *)argv[1]);
|
|
if (length <= 0)
|
|
{
|
|
rt_kprintf("bad thread name\n");
|
|
return;
|
|
}
|
|
if ((length == strlen("all_tasks")) && (0 == strncmp(argv[1], "all_tasks", length)))
|
|
{
|
|
for (node = info->object_list.next; node != &info->object_list; node = node->next)
|
|
{
|
|
thread = rt_list_entry(node, struct rt_thread, list);
|
|
rt_hw_stack_print(thread);
|
|
}
|
|
return;
|
|
}
|
|
if (length > RT_NAME_MAX)
|
|
{
|
|
length = RT_NAME_MAX;
|
|
}
|
|
|
|
for (node = info->object_list.next; node != &info->object_list; node = node->next)
|
|
{
|
|
thread = rt_list_entry(node, struct rt_thread, list);
|
|
if (0 != strncmp(argv[1], thread->name, length))
|
|
{
|
|
continue;
|
|
}
|
|
rt_hw_stack_print(thread);
|
|
break;
|
|
}
|
|
}
|
|
|
|
MSH_CMD_EXPORT_ALIAS(rt_stack_print, stack, rt_hw_stack_print);
|