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