1 /*
2  * Copyright (c) 2006-2023, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2023-10-18     Shell        Add backtrace support
9  */
10 
11 #define DBG_TAG "hw.backtrace"
12 #define DBG_LVL DBG_INFO
13 #include <rtdbg.h>
14 
15 #include <rtthread.h>
16 #include <mm_aspace.h>
17 #include "riscv_mmu.h"
18 #include "stack.h"
19 
20 #define WORD                            sizeof(rt_base_t)
21 #define ARCH_CONTEXT_FETCH(pctx, id)    (*(((unsigned long *)pctx) + (id)))
22 
_bt_kaddr(rt_ubase_t * fp,struct rt_hw_backtrace_frame * frame)23 rt_inline rt_err_t _bt_kaddr(rt_ubase_t *fp, struct rt_hw_backtrace_frame *frame)
24 {
25     rt_err_t rc;
26     frame->fp = *(fp - 2);
27     frame->pc = *(fp - 1);
28 
29     if ((rt_ubase_t)fp == frame->fp)
30     {
31         rc = -RT_ERROR;
32     }
33     else
34     {
35         rc = RT_EOK;
36     }
37     return rc;
38 }
39 
40 #ifdef RT_USING_SMART
41 #include <lwp_arch.h>
42 #include <lwp_user_mm.h>
43 
_bt_uaddr(rt_lwp_t lwp,rt_ubase_t * fp,struct rt_hw_backtrace_frame * frame)44 rt_inline rt_err_t _bt_uaddr(rt_lwp_t lwp, rt_ubase_t *fp, struct rt_hw_backtrace_frame *frame)
45 {
46     rt_err_t rc;
47     if (lwp_data_get(lwp, &frame->fp, fp - 2, WORD) != WORD)
48     {
49         rc = -RT_EFAULT;
50     }
51     else if (lwp_data_get(lwp, &frame->pc, fp - 1, WORD) != WORD)
52     {
53         rc = -RT_EFAULT;
54     }
55     else if ((rt_ubase_t)fp == frame->fp)
56     {
57         rc = -RT_ERROR;
58     }
59     else
60     {
61         frame->pc -= 0;
62         rc = RT_EOK;
63     }
64     return rc;
65 }
66 #endif /* RT_USING_SMART */
67 
rt_hw_backtrace_frame_unwind(rt_thread_t thread,struct rt_hw_backtrace_frame * frame)68 rt_err_t rt_hw_backtrace_frame_unwind(rt_thread_t thread, struct rt_hw_backtrace_frame *frame)
69 {
70     rt_err_t rc = -RT_ERROR;
71     rt_uintptr_t *fp = (rt_uintptr_t *)frame->fp;
72 
73     if (fp && !((long)fp & 0x7))
74     {
75 #ifdef RT_USING_SMART
76         if (thread->lwp)
77         {
78             void *lwp = thread->lwp;
79             void *this_lwp = lwp_self();
80             if (this_lwp == lwp && rt_hw_mmu_v2p(((rt_lwp_t)lwp)->aspace, fp) != ARCH_MAP_FAILED)
81             {
82                 rc = _bt_kaddr(fp, frame);
83             }
84             else if (lwp_user_accessible_ext(lwp, (void *)fp, WORD))
85             {
86                 rc = _bt_uaddr(lwp, fp, frame);
87             }
88             else
89             {
90                 rc = -RT_EFAULT;
91             }
92         }
93         else
94 #endif
95         if ((rt_kmem_v2p(fp) != ARCH_MAP_FAILED))
96         {
97             rc = _bt_kaddr(fp, frame);
98         }
99         else
100         {
101             rc = -RT_EINVAL;
102         }
103     }
104     else
105     {
106         rc = -RT_EFAULT;
107     }
108     return rc;
109 }
110 
rt_hw_backtrace_frame_get(rt_thread_t thread,struct rt_hw_backtrace_frame * frame)111 rt_err_t rt_hw_backtrace_frame_get(rt_thread_t thread, struct rt_hw_backtrace_frame *frame)
112 {
113     rt_err_t rc;
114 
115     if (!thread || !frame)
116     {
117         rc = -RT_EINVAL;
118     }
119     else
120     {
121         rt_hw_switch_frame_t sframe = thread->sp;
122         frame->pc = sframe->regs[RT_HW_SWITCH_CONTEXT_RA];
123         frame->fp = sframe->regs[RT_HW_SWITCH_CONTEXT_S0];;
124         rc = RT_EOK;
125     }
126     return rc;
127 }
128