1/*
2 * Copyright (c) 2014 Wind River Systems, Inc.
3 * Copyright (c) 2018 Synopsys.
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8/**
9 * @file
10 * @brief Fault handlers for ARCv2
11 *
12 * Fault handlers for ARCv2 processors.
13 */
14
15#include <zephyr/toolchain.h>
16#include <zephyr/linker/sections.h>
17#include <zephyr/arch/cpu.h>
18#include <swap_macros.h>
19#include <zephyr/syscall.h>
20#include <zephyr/arch/arc/asm-compat/assembler.h>
21
22GTEXT(_Fault)
23GTEXT(__reset)
24GTEXT(__memory_error)
25GTEXT(__instruction_error)
26GTEXT(__ev_machine_check)
27GTEXT(__ev_tlb_miss_i)
28GTEXT(__ev_tlb_miss_d)
29GTEXT(__ev_prot_v)
30GTEXT(__ev_privilege_v)
31GTEXT(__ev_swi)
32GTEXT(__ev_trap)
33GTEXT(__ev_extension)
34GTEXT(__ev_div_zero)
35GTEXT(__ev_dc_error)
36GTEXT(__ev_maligned)
37
38.macro _save_exc_regs_into_stack
39#ifdef CONFIG_ARC_HAS_SECURE
40	/* ERSEC_STAT is IOW/RAZ in normal mode */
41	lr r0,[_ARC_V2_ERSEC_STAT]
42	st_s r0, [sp, ___isf_t_sec_stat_OFFSET]
43#endif
44	LRR r0, [_ARC_V2_ERET]
45	STR r0, sp, ___isf_t_pc_OFFSET
46	LRR r0, [_ARC_V2_ERSTATUS]
47	STR r0, sp, ___isf_t_status32_OFFSET
48.endm
49
50/*
51 * The exception handling will use top part of interrupt stack to
52 * get smaller memory footprint, because exception is not frequent.
53 * To reduce the impact on interrupt handling, especially nested interrupt
54 * the top part of interrupt stack cannot be too large, so add a check
55 * here
56 */
57#if CONFIG_ARC_EXCEPTION_STACK_SIZE > (CONFIG_ISR_STACK_SIZE >> 1)
58#error "interrupt stack size is too small"
59#endif
60
61/*
62 * @brief Fault handler installed in the fault and reserved vectors
63 */
64
65SECTION_SUBSEC_FUNC(TEXT,__fault,__memory_error)
66SECTION_SUBSEC_FUNC(TEXT,__fault,__instruction_error)
67SECTION_SUBSEC_FUNC(TEXT,__fault,__ev_machine_check)
68SECTION_SUBSEC_FUNC(TEXT,__fault,__ev_tlb_miss_i)
69SECTION_SUBSEC_FUNC(TEXT,__fault,__ev_tlb_miss_d)
70SECTION_SUBSEC_FUNC(TEXT,__fault,__ev_prot_v)
71SECTION_SUBSEC_FUNC(TEXT,__fault,__ev_privilege_v)
72SECTION_SUBSEC_FUNC(TEXT,__fault,__ev_swi)
73SECTION_SUBSEC_FUNC(TEXT,__fault,__ev_extension)
74SECTION_SUBSEC_FUNC(TEXT,__fault,__ev_div_zero)
75SECTION_SUBSEC_FUNC(TEXT,__fault,__ev_dc_error)
76SECTION_SUBSEC_FUNC(TEXT,__fault,__ev_maligned)
77
78_exc_entry:
79	/*
80	 * re-use the top part of interrupt stack as exception
81	 * stack. If this top part is used by interrupt handling,
82	 * and exception is raised, then here it's guaranteed that
83	 * exception handling has necessary stack to use
84	 */
85	MOVR ilink, sp
86	_get_curr_cpu_irq_stack sp
87	SUBR sp, sp, (CONFIG_ISR_STACK_SIZE - CONFIG_ARC_EXCEPTION_STACK_SIZE)
88
89	/*
90	 * save caller saved registers
91	 * this stack frame is set up in exception stack,
92	 * not in the original sp (thread stack or interrupt stack).
93	 * Because the exception may be raised by stack checking or
94	 * mpu protect violation related to stack. If this stack frame
95	 * is setup in original sp, double exception may be raised during
96	 * _create_irq_stack_frame, which is unrecoverable.
97	 */
98	_create_irq_stack_frame
99
100	_save_exc_regs_into_stack
101
102	/* sp is parameter of _Fault */
103	MOVR r0, sp
104	/* ilink is the thread's original sp */
105	MOVR r1, ilink
106	jl _Fault
107
108_exc_return:
109/* the exception cause must be fixed in exception handler when exception returns
110 * directly, or exception will be repeated.
111 *
112 * If thread switch is raised in exception handler, the context of old thread will
113 * not be saved, i.e., it cannot be recovered, because we don't know where the
114 * exception comes out,  thread context?irq_context?nest irq context?
115 */
116
117	_get_next_switch_handle
118
119	BREQR r0, 0, _exc_return_from_exc
120
121	/* Save old thread into switch handle which is required by z_sched_switch_spin which
122	 * will be called during old thread abort.
123	 */
124	STR r2, r2, ___thread_t_switch_handle_OFFSET
125
126	MOVR r2, r0
127
128#ifdef CONFIG_ARC_SECURE_FIRMWARE
129	/*
130	 * sync up the ERSEC_STAT.ERM and SEC_STAT.IRM.
131	 * use a fake interrupt return to simulate an exception turn.
132	 * ERM and IRM record which mode the cpu should return, 1: secure
133	 * 0: normal
134	 */
135	lr r3,[_ARC_V2_ERSEC_STAT]
136	btst r3, 31
137	bset.nz r3, r3, _ARC_V2_SEC_STAT_IRM_BIT
138	bclr.z r3, r3, _ARC_V2_SEC_STAT_IRM_BIT
139	sflag r3
140#endif
141	/* clear AE bit to forget this was an exception, and go to
142	 * register bank0 (if exception is raised in firq with 2 reg
143	 * banks, then we may be bank1)
144	 */
145#if defined(CONFIG_ARC_FIRQ) && CONFIG_RGF_NUM_BANKS != 1
146	/* save r2 in ilink because of the possible following reg
147	 * bank switch
148	 */
149	mov ilink, r2
150#endif
151	LRR r3, [_ARC_V2_STATUS32]
152	ANDR r3, r3, (~(_ARC_V2_STATUS32_AE | _ARC_V2_STATUS32_RB(7)))
153	kflag r3
154	/* pretend lowest priority interrupt happened to use common handler
155	 * if exception is raised in irq, i.e., _ARC_V2_AUX_IRQ_ACT !=0,
156	 * ignore irq handling, we cannot return to irq handling which may
157	 * raise exception again. The ignored interrupts will be re-triggered
158	 * if not cleared, or re-triggered by interrupt sources, or just missed
159	 */
160
161#ifdef CONFIG_ARC_SECURE_FIRMWARE
162	mov_s r3, (1 << (ARC_N_IRQ_START_LEVEL - 1))
163#else
164	MOVR r3, (1 << (CONFIG_NUM_IRQ_PRIO_LEVELS - 1))
165#endif
166
167#ifdef CONFIG_ARC_NORMAL_FIRMWARE
168	push_s r2
169	mov_s r0, _ARC_V2_AUX_IRQ_ACT
170	mov_s r1, r3
171	mov_s r6, ARC_S_CALL_AUX_WRITE
172	sjli SJLI_CALL_ARC_SECURE
173	pop_s r2
174#else
175	SRR r3, [_ARC_V2_AUX_IRQ_ACT]
176#endif
177
178#if defined(CONFIG_ARC_FIRQ) && CONFIG_RGF_NUM_BANKS != 1
179	mov r2, ilink
180#endif
181
182	/* Assumption: r2 has next thread */
183	b _rirq_newthread_switch
184
185_exc_return_from_exc:
186	/* exception handler may change return address.
187	 * reload it
188	 */
189	LDR r0, sp, ___isf_t_pc_OFFSET
190	SRR r0, [_ARC_V2_ERET]
191
192	_pop_irq_stack_frame
193	MOVR sp, ilink
194	rtie
195
196/* separated entry for trap which may be used by irq_offload, USERPSACE */
197SECTION_SUBSEC_FUNC(TEXT,__fault,__ev_trap)
198	/* get the id of trap_s */
199	LRR ilink, [_ARC_V2_ECR]
200	ANDR ilink, ilink, 0x3f
201#ifdef CONFIG_USERSPACE
202	cmp ilink, _TRAP_S_CALL_SYSTEM_CALL
203	bne _do_non_syscall_trap
204	/* do sys_call */
205	mov ilink, K_SYSCALL_LIMIT
206	cmp r6, ilink
207	blo valid_syscall_id
208
209	mov_s r0, r6
210	mov_s r6, K_SYSCALL_BAD
211
212valid_syscall_id:
213	/* create a sys call frame
214	 * caller regs (r0 - 12) are saved in _create_irq_stack_frame
215	 * ok to use them later
216	 */
217	_create_irq_stack_frame
218
219	_save_exc_regs_into_stack
220
221	/* exc return and do sys call in kernel mode,
222	 * so need to clear U bit, r0 is already loaded
223	 * with ERSTATUS in _save_exc_regs_into_stack
224	 */
225
226	bclr r0, r0, _ARC_V2_STATUS32_U_BIT
227	sr r0, [_ARC_V2_ERSTATUS]
228
229	mov_s r0, _arc_do_syscall
230	sr r0, [_ARC_V2_ERET]
231
232	rtie
233
234_do_non_syscall_trap:
235#endif /* CONFIG_USERSPACE */
236	b _exc_entry
237