1 /*
2  * Copyright (c) 2014 Wind River Systems, Inc.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /**
8  * @file
9  * @brief Kernel fatal error handler for ARM Cortex-M and Cortex-R
10  *
11  * This module provides the z_arm_fatal_error() routine for ARM Cortex-M
12  * and Cortex-R CPUs.
13  */
14 
15 #include <zephyr/kernel.h>
16 #include <kernel_arch_data.h>
17 #include <zephyr/logging/log.h>
18 LOG_MODULE_DECLARE(os, CONFIG_KERNEL_LOG_LEVEL);
19 
20 #ifdef CONFIG_EXCEPTION_DEBUG
esf_dump(const struct arch_esf * esf)21 static void esf_dump(const struct arch_esf *esf)
22 {
23 	LOG_ERR("r0/a1:  0x%08x  r1/a2:  0x%08x  r2/a3:  0x%08x",
24 		esf->basic.a1, esf->basic.a2, esf->basic.a3);
25 	LOG_ERR("r3/a4:  0x%08x r12/ip:  0x%08x r14/lr:  0x%08x",
26 		esf->basic.a4, esf->basic.ip, esf->basic.lr);
27 	LOG_ERR(" xpsr:  0x%08x", esf->basic.xpsr);
28 #if defined(CONFIG_FPU) && defined(CONFIG_FPU_SHARING)
29 	for (int i = 0; i < ARRAY_SIZE(esf->fpu.s); i += 4) {
30 		LOG_ERR("s[%2d]:  0x%08x  s[%2d]:  0x%08x"
31 			"  s[%2d]:  0x%08x  s[%2d]:  0x%08x",
32 			i, (uint32_t)esf->fpu.s[i],
33 			i + 1, (uint32_t)esf->fpu.s[i + 1],
34 			i + 2, (uint32_t)esf->fpu.s[i + 2],
35 			i + 3, (uint32_t)esf->fpu.s[i + 3]);
36 	}
37 #ifdef CONFIG_VFP_FEATURE_REGS_S64_D32
38 	for (int i = 0; i < ARRAY_SIZE(esf->fpu.d); i += 4) {
39 		LOG_ERR("d[%2d]:  0x%16llx  d[%2d]:  0x%16llx"
40 			"  d[%2d]:  0x%16llx  d[%2d]:  0x%16llx",
41 			i, (uint64_t)esf->fpu.d[i],
42 			i + 1, (uint64_t)esf->fpu.d[i + 1],
43 			i + 2, (uint64_t)esf->fpu.d[i + 2],
44 			i + 3, (uint64_t)esf->fpu.d[i + 3]);
45 	}
46 #endif
47 	LOG_ERR("fpscr:  0x%08x", esf->fpu.fpscr);
48 #endif
49 #if defined(CONFIG_EXTRA_EXCEPTION_INFO)
50 	const struct _callee_saved *callee = esf->extra_info.callee;
51 
52 	if (callee != NULL) {
53 		LOG_ERR("r4/v1:  0x%08x  r5/v2:  0x%08x  r6/v3:  0x%08x",
54 			callee->v1, callee->v2, callee->v3);
55 		LOG_ERR("r7/v4:  0x%08x  r8/v5:  0x%08x  r9/v6:  0x%08x",
56 			callee->v4, callee->v5, callee->v6);
57 		LOG_ERR("r10/v7: 0x%08x  r11/v8: 0x%08x    psp:  0x%08x",
58 			callee->v7, callee->v8, callee->psp);
59 	}
60 
61 	LOG_ERR("EXC_RETURN: 0x%0x", esf->extra_info.exc_return);
62 
63 #endif /* CONFIG_EXTRA_EXCEPTION_INFO */
64 	LOG_ERR("Faulting instruction address (r15/pc): 0x%08x",
65 		esf->basic.pc);
66 }
67 #endif /* CONFIG_EXCEPTION_DEBUG */
68 
z_arm_fatal_error(unsigned int reason,const struct arch_esf * esf)69 void z_arm_fatal_error(unsigned int reason, const struct arch_esf *esf)
70 {
71 #ifdef CONFIG_EXCEPTION_DEBUG
72 	if (esf != NULL) {
73 		esf_dump(esf);
74 	}
75 #endif /* CONFIG_EXCEPTION_DEBUG */
76 
77 	/* LOG the IRQn that was unhandled */
78 #if defined(CONFIG_CPU_CORTEX_M)
79 	if (reason == K_ERR_SPURIOUS_IRQ) {
80 		uint32_t irqn = __get_IPSR() - 16;
81 
82 		LOG_ERR("Unhandled IRQn: %d", irqn);
83 	}
84 #endif
85 
86 	z_fatal_error(reason, esf);
87 }
88 
89 /**
90  * @brief Handle a software-generated fatal exception
91  * (e.g. kernel oops, panic, etc.).
92  *
93  * Notes:
94  * - the function is invoked in SVC Handler
95  * - if triggered from nPRIV mode, only oops and stack fail error reasons
96  *   may be propagated to the fault handling process.
97  * - We expect the supplied exception stack frame to always be a valid
98  *   frame. That is because, if the ESF cannot be stacked during an SVC,
99  *   a processor fault (e.g. stacking error) will be generated, and the
100  *   fault handler will executed instead of the SVC.
101  *
102  * @param esf exception frame
103  * @param callee_regs Callee-saved registers (R4-R11)
104  * @param exc_return EXC_RETURN value present in LR after exception entry.
105  */
z_do_kernel_oops(const struct arch_esf * esf,_callee_saved_t * callee_regs,uint32_t exc_return)106 void z_do_kernel_oops(const struct arch_esf *esf, _callee_saved_t *callee_regs, uint32_t exc_return)
107 {
108 #if !(defined(CONFIG_EXTRA_EXCEPTION_INFO) && defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE))
109 	ARG_UNUSED(callee_regs);
110 #endif
111 	/* Stacked R0 holds the exception reason. */
112 	unsigned int reason = esf->basic.r0;
113 
114 	z_arm_set_fault_sp(esf, exc_return);
115 
116 #if defined(CONFIG_USERSPACE)
117 	if (z_arm_preempted_thread_in_user_mode(esf)) {
118 		/*
119 		 * Exception triggered from user mode.
120 		 *
121 		 * User mode is only allowed to induce oopses and stack check
122 		 * failures via software-triggered system fatal exceptions.
123 		 */
124 		if (!((esf->basic.r0 == K_ERR_KERNEL_OOPS) ||
125 			(esf->basic.r0 == K_ERR_STACK_CHK_FAIL))) {
126 
127 			reason = K_ERR_KERNEL_OOPS;
128 		}
129 	}
130 
131 #endif /* CONFIG_USERSPACE */
132 
133 #if !defined(CONFIG_EXTRA_EXCEPTION_INFO)
134 	z_arm_fatal_error(reason, esf);
135 #else
136 	struct arch_esf esf_copy;
137 
138 	memcpy(&esf_copy, esf, offsetof(struct arch_esf, extra_info));
139 #if defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE)
140 	/* extra exception info is collected in callee_reg param
141 	 * on CONFIG_ARMV7_M_ARMV8_M_MAINLINE
142 	 */
143 
144 	esf_copy.extra_info = (struct __extra_esf_info) {
145 		.callee = callee_regs,
146 	};
147 #else
148 	/* extra exception info is not collected for kernel oops
149 	 * path today so we make a copy of the ESF and zero out
150 	 * that information
151 	 */
152 	esf_copy.extra_info = (struct __extra_esf_info) { 0 };
153 #endif /* CONFIG_ARMV7_M_ARMV8_M_MAINLINE */
154 
155 	z_arm_fatal_error(reason, &esf_copy);
156 #endif /* CONFIG_EXTRA_EXCEPTION_INFO */
157 }
158 
arch_syscall_oops(void * ssf_ptr)159 FUNC_NORETURN void arch_syscall_oops(void *ssf_ptr)
160 {
161 	uint32_t *ssf_contents = ssf_ptr;
162 	struct arch_esf oops_esf = { 0 };
163 
164 	/* TODO: Copy the rest of the register set out of ssf_ptr */
165 	oops_esf.basic.pc = ssf_contents[3];
166 
167 	z_arm_fatal_error(K_ERR_KERNEL_OOPS, &oops_esf);
168 	CODE_UNREACHABLE;
169 }
170