1 // SPDX-License-Identifier: BSD-3-Clause
2 //
3 // Copyright(c) 2018 Intel Corporation. All rights reserved.
4 //
5 // Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
6
7 #include <sof/debug/backtrace.h>
8 #include <sof/debug/debug.h>
9 #include <rtos/panic.h>
10 #include <rtos/interrupt.h>
11 #include <rtos/cache.h>
12 #include <sof/lib/mailbox.h>
13 #include <sof/platform.h>
14 #include <rtos/string.h>
15 #include <sof/trace/trace.h>
16 #include <ipc/trace.h>
17
18 #include <stddef.h>
19 #include <stdint.h>
20
dump_panicinfo(void * addr,struct sof_ipc_panic_info * panic_info)21 void dump_panicinfo(void *addr, struct sof_ipc_panic_info *panic_info)
22 {
23 int ret;
24
25 if (!panic_info)
26 return;
27 ret = memcpy_s(addr, sizeof(struct sof_ipc_panic_info), panic_info,
28 sizeof(struct sof_ipc_panic_info));
29 /* TODO are asserts even safe in this context? */
30 assert(!ret);
31 dcache_writeback_region(addr, sizeof(struct sof_ipc_panic_info));
32 }
33
34 /** Dumps stack as part of panic.
35 *
36 * \return SOF_IPC_PANIC_STACK if offset is off the stack_limit,
37 * unchanged 'p' panic code input otherwise.
38 */
dump_stack(uint32_t p,void * addr,size_t offset,size_t limit,uintptr_t * stack_ptr)39 static uint32_t dump_stack(uint32_t p, void *addr, size_t offset,
40 size_t limit, uintptr_t *stack_ptr)
41 {
42 uintptr_t stack_limit = (uintptr_t)arch_get_stack_entry();
43 uintptr_t stack_bottom = stack_limit + arch_get_stack_size() -
44 sizeof(void *);
45 uintptr_t stack_top = (uintptr_t)arch_get_stack_ptr() + offset;
46 size_t size = stack_bottom - stack_top;
47 int ret;
48
49 *stack_ptr = stack_top;
50
51 /* is stack smashed ? */
52 if (stack_top - offset <= stack_limit) {
53 p = SOF_IPC_PANIC_STACK;
54 return p;
55 }
56
57 /* make sure stack size won't overflow dump area */
58 if (size > limit)
59 size = limit;
60
61 /* copy stack contents and writeback */
62 ret = memcpy_s(addr, limit, (void *)stack_top, size - sizeof(void *));
63 assert(!ret);
64 dcache_writeback_region(addr, size - sizeof(void *));
65
66 return p;
67 }
68
69 /** Copy registers, panic_info and current stack to mailbox exception
70 * window. Opaque 'data' (e.g.: optional epc1) is passed to
71 * arch_dump_regs().
72 */
panic_dump(uint32_t p,struct sof_ipc_panic_info * panic_info,uintptr_t * data)73 void panic_dump(uint32_t p, struct sof_ipc_panic_info *panic_info,
74 uintptr_t *data)
75 {
76 char *ext_offset;
77 size_t avail;
78 uintptr_t stack_ptr;
79
80 /* disable all IRQs */
81 interrupt_global_disable();
82
83 /* ARCH_OOPS_SIZE is platform-dependent */
84 ext_offset = (char *)mailbox_get_exception_base() + ARCH_OOPS_SIZE;
85
86 /* dump panic info, filename and linenum */
87 dump_panicinfo(ext_offset, panic_info);
88 ext_offset += sizeof(struct sof_ipc_panic_info);
89
90 #if CONFIG_TRACE
91 trace_flush_dma_to_mbox();
92 #endif
93
94 /* Dump stack frames and override panic code 'p' if ext_offset is
95 * off stack_limit. Find stack_ptr.
96 */
97 avail = MAILBOX_EXCEPTION_SIZE -
98 (size_t)(ext_offset - (char *)mailbox_get_exception_base());
99 p = dump_stack(p, ext_offset, 0, avail, &stack_ptr);
100
101 /* Write oops.arch_hdr and oops.plat_hdr headers and dump DSP core
102 * registers. After arch_dump_regs() use only inline funcs if
103 * needed.
104 */
105 arch_dump_regs((void *)mailbox_get_exception_base(), stack_ptr, data);
106
107 /* panic - send IPC oops message to host */
108 platform_panic(p);
109
110 /* and loop forever */
111 while (1)
112 ;
113 }
114
__panic(uint32_t panic_code,char * filename,uint32_t linenum)115 void __panic(uint32_t panic_code, char *filename, uint32_t linenum)
116 {
117 struct sof_ipc_panic_info panicinfo = { .linenum = linenum };
118 const unsigned int length_max = sizeof(panicinfo.filename);
119 int mem_len;
120 int ret;
121
122 /* including the ending '\0' */
123 mem_len = rstrlen(filename) + 1;
124
125 if (mem_len > length_max) {
126 /* copy those last bytes only */
127 ret = memcpy_s(panicinfo.filename,
128 length_max,
129 filename + mem_len - length_max,
130 length_max);
131 /* TODO are asserts safe in this context? */
132 assert(!ret);
133
134 /* prefixing with "..." */
135 ret = memcpy_s(panicinfo.filename, length_max, "...", 3);
136 assert(!ret);
137 } else {
138 ret = memcpy_s(panicinfo.filename, length_max, filename, mem_len);
139 assert(!ret);
140 }
141
142 /* To distinguish regular panic() calls from exceptions, we will
143 * set a reserved value for the exception cause (63) so the
144 * coredumper tool could distinguish between the situations.
145 */
146 #if !__clang_analyzer__
147 __asm__ __volatile__("movi a3, 63\n\t"
148 "wsr.exccause a3\n\t"
149 "esync" : : :
150 "a3", "memory");
151 #endif
152
153 panic_dump(panic_code, &panicinfo, NULL);
154 }
155