1/*
2 * Copyright (c) 2008-2015 Travis Geiselbrecht
3 *
4 * Use of this source code is governed by a MIT-style
5 * license that can be found in the LICENSE file or at
6 * https://opensource.org/licenses/MIT
7 */
8#include <lk/asm.h>
9#include <arch/asm.h>
10#include <arch/arm/cores.h>
11
12/* exception handling glue.
13 * NOTE: only usable on armv6+ cores
14 */
15
16#define TIMESTAMP_IRQ 0
17
18/* macros to align and unalign the stack on 8 byte boundary for ABI compliance */
19.macro stack_align, tempreg
20    /* make sure the stack is aligned */
21    mov     \tempreg, sp
22    tst     sp, #4
23    subeq   sp, #4
24    push    { \tempreg }
25
26    /* tempreg holds the original stack */
27.endm
28
29.macro stack_restore, tempreg
30    /* restore the potentially unaligned stack */
31    pop     { \tempreg }
32    mov     sp, \tempreg
33.endm
34
35/* save and disable the vfp unit */
36.macro vfp_save, temp1
37    /* save old fpexc */
38    vmrs    \temp1, fpexc
39
40    push    { \temp1 }
41
42    /* hard disable the vfp unit */
43    bic     \temp1, #(1<<30)
44    vmsr    fpexc, \temp1
45.endm
46
47/* restore the vfp enable/disable state */
48.macro vfp_restore, temp1
49    /* restore fpexc */
50    pop     { \temp1 }
51
52    vmsr    fpexc, \temp1
53.endm
54
55/* Save callee trashed registers.
56 * At exit r0 contains a pointer to the register frame.
57 */
58.macro save
59    /* save spsr and r14 onto the svc stack */
60    srsdb   #0x13!
61
62    /* switch to svc mode, interrupts disabled */
63    cpsid   i,#0x13
64
65    /* save callee trashed regs and lr */
66    push    { r0-r3, r12, lr }
67
68    /* save user space sp/lr */
69    sub     sp, #8
70    stmia   sp, { r13, r14 }^
71
72#if ARM_WITH_VFP
73    /* save and disable the vfp unit */
74    vfp_save    r0
75#endif
76
77    /* make sure the stack is 8 byte aligned */
78    stack_align r0
79
80    /* r0 now holds the pointer to the original iframe (before alignment) */
81.endm
82
83.macro save_offset, offset
84    sub     lr, \offset
85    save
86.endm
87
88.macro restore
89    /* undo the stack alignment we did before */
90    stack_restore r0
91
92#if ARM_WITH_VFP
93    /* restore the old state of the vfp unit */
94    vfp_restore r0
95#endif
96
97    /* restore user space sp/lr */
98    ldmia   sp, { r13, r14 }^
99    add     sp, #8
100
101    pop     { r0-r3, r12, lr }
102
103    /* return to whence we came from */
104    rfeia   sp!
105.endm
106
107/* Save all registers.
108 * At exit r0 contains a pointer to the register frame.
109 */
110.macro saveall
111    /* save spsr and r14 onto the svc stack */
112    srsdb   #0x13!
113
114    /* switch to svc mode, interrupts disabled */
115    cpsid   i,#0x13
116
117    /* save all regs */
118    push    { r0-r12, lr }
119
120    /* save user space sp/lr */
121    sub     sp, #8
122    stmia   sp, { r13, r14 }^
123
124#if ARM_WITH_VFP
125    /* save and disable the vfp unit */
126    vfp_save    r0
127#endif
128
129    /* make sure the stack is 8 byte aligned */
130    stack_align r0
131
132    /* r0 now holds the pointer to the original iframe (before alignment) */
133.endm
134
135.macro saveall_offset, offset
136    sub     lr, \offset
137    saveall
138.endm
139
140.macro restoreall
141    /* undo the stack alignment we did before */
142    stack_restore r0
143
144#if ARM_WITH_VFP
145    /* restore the old state of the vfp unit */
146    vfp_restore r0
147#endif
148
149    /* restore user space sp/lr */
150    ldmia   sp, { r13, r14 }^
151    add     sp, #8
152
153    pop     { r0-r12, r14 }
154
155    /* return to whence we came from */
156    rfeia   sp!
157.endm
158
159FUNCTION(arm_undefined)
160    save
161    /* r0 now holds pointer to iframe */
162
163    bl      arm_undefined_handler
164
165    restore
166
167#ifndef WITH_LIB_SYSCALL
168FUNCTION(arm_syscall)
169    saveall
170    /* r0 now holds pointer to iframe */
171
172    bl      arm_syscall_handler
173
174    restoreall
175#endif
176
177FUNCTION(arm_prefetch_abort)
178    saveall_offset #4
179    /* r0 now holds pointer to iframe */
180
181    bl      arm_prefetch_abort_handler
182
183    restoreall
184
185FUNCTION(arm_data_abort)
186    saveall_offset #8
187    /* r0 now holds pointer to iframe */
188
189    bl      arm_data_abort_handler
190
191    restoreall
192
193FUNCTION(arm_reserved)
194    b   .
195
196FUNCTION(arm_irq)
197#if TIMESTAMP_IRQ
198    /* read the cycle count */
199    mrc     p15, 0, sp, c9, c13, 0
200    str     sp, [pc, #__irq_cycle_count - . - 8]
201#endif
202
203    save_offset    #4
204
205    /* r0 now holds pointer to iframe */
206
207    /* track that we're inside an irq handler */
208    LOADCONST(r2, __arm_in_handler)
209    mov     r1, #1
210    str     r1, [r2]
211
212    /* call into higher level code */
213    bl  platform_irq
214
215    /* clear the irq handler status */
216    LOADCONST(r1, __arm_in_handler)
217    mov     r2, #0
218    str     r2, [r1]
219
220    /* reschedule if the handler returns nonzero */
221    cmp     r0, #0
222    blne    thread_preempt
223
224    restore
225
226FUNCTION(arm_fiq)
227    save_offset #4
228    /* r0 now holds pointer to iframe */
229
230    bl  platform_fiq
231
232    restore
233
234.ltorg
235
236#if TIMESTAMP_IRQ
237DATA(__irq_cycle_count)
238    .word   0
239#endif
240
241.data
242DATA(__arm_in_handler)
243    .word   0
244