1/*
2 * Copyright 2013, winocm. <winocm@icloud.com>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 *   Redistributions of source code must retain the above copyright notice, this
9 *   list of conditions and the following disclaimer.
10 *
11 *   Redistributions in binary form must reproduce the above copyright notice, this
12 *   list of conditions and the following disclaimer in the documentation and/or
13 *   other materials provided with the distribution.
14 *
15 *   If you are going to use this software in any form that does not involve
16 *   releasing the source to this project or improving it, let me know beforehand.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
22 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29/*
30 * ARM processor abort handlers.
31 */
32
33#include <arm/arch.h>
34#include <arm/asm_help.h>
35#include <assym.s>
36#include <mach/arm/asm.h>
37
38/**
39 * fleh_reset
40 *
41 * Just halt the processor right here with a forever loop.
42 */
43EnterARM(fleh_reset)
44    b       .
45
46/**
47 * fleh_undef
48 *
49 * Call the undefined handler.
50 */
51EnterARM(fleh_undef)
52    /* Get current mode */
53    mrs     sp, spsr
54    tst     sp, #0x20
55
56    /* Subtract lr based on cpsr bit */
57    subeq   lr, lr, #4
58    subne   lr, lr, #2
59
60    /* See if it's in the kernel. */
61    mrs     sp, spsr
62    tst     sp, #0xf
63    bne     undef_from_kernel
64undef_from_user:
65
66    /* Usermode undefined, maybe it's a VFP instruction, HUH?! */
67    LoadThreadRegister(sp)
68    ldr     sp, [sp, TH_PCB_USS]
69    stmea   sp, {r0-lr}^
70
71    str     lr, [sp, #0x3C]
72    mrs     r2, spsr
73    str     r2, [sp, #0x40]
74    mov     r0, sp
75
76    /* Change to supervisor */
77    cpsid   i, #0x13
78    LoadThreadRegister(r1)
79    ldr     sp, [r1, TH_PCB_ISS]
80    blx     _sleh_undef
81    b       _thread_exception_return
82
83undef_from_kernel:
84    /* Oops. */
85    cpsid   i, #0x13
86
87    sub     sp, sp, #0x50
88    stmea   sp, {r0-r12}
89
90    str     lr, [sp, #0x38]
91    mov     r0, sp
92
93    /* Undefined */
94    cpsid   i, #0x1b
95    mrs     r2, spsr
96    str     lr, [r0, #0x3C]
97    str     r2, [r0, #0x40]
98
99    /* Supervisor */
100    cpsid   i, #0x13
101
102    add     r3, sp, #0x50
103    str     r3, [r0, #0x34]
104
105    blx     _sleh_undef
106
107    /* NEVER COME BACK */
108    b       .
109
110/**
111 * fleh_swi
112 *
113 * Call the software interrupt handler.
114 */
115EnterARM(fleh_swi)
116    cmn     r12, #3
117    beq     swi_trap_tb
118
119    /* Save current registers and spsr */
120    LoadThreadRegister(sp)
121    ldr     sp, [sp, TH_PCB_USS]
122    stmea   sp, {r0-lr}^
123
124    str     lr, [sp, #0x3C]
125    mrs     r2, spsr
126    str     r2, [sp, #0x40]
127
128    mov     r8, sp
129
130    LoadThreadRegister(sp)
131    ldr     sp, [sp, TH_PCB_ISS]
132
133    mov     r11, r12
134    cpsie   i
135    /* Is it a trap? */
136    cmp     r11, #0x80000000
137    beq     swi_trap
138
139swi_trap_ret:
140    rsbs    r5, r11, #0
141    ble     swi_unix
142
143swi_mach:
144    /* Load the mach function from the mach trap table and call it. */
145    adr     lr, swi_exit
146    mov     r4, r5
147    cmp     r5, #0x80
148    bge     swi_mach_error
149    LOAD_ADDR(r1, mach_trap_table)
150
151    add     r11, r5, r5, lsl#1
152    add     r1, r1, r11, lsl#2
153
154    ldr     r1, [r1, #4]
155    LOAD_ADDR(r2, kern_invalid)
156
157    mov     r0, r8
158    teq     r1, r2
159    beq     swi_mach_error
160    bx      r1
161swi_exit64:
162    str     r1, [r8, #4]
163swi_exit:
164    /* Exit and go back. */
165    str     r0, [r8]
166    mov     r0, r8
167    blx     _mach_syscall_trace
168    bl      _thread_exception_return
169
170    mov     r0, #0x80
171    b       irqvec_panic
172
173swi_mach_error:
174    /* There was an error processing the Mach system call, panic. */
175    mov     r0, #7
176    mov     r1, r4
177    mov     r2, #1
178    blx     _doexception
179
180    mov     r0, #0x81
181    b       irqvec_panic
182
183swi_unix:
184    /* Unix syscall, call unix_syscall to dispatch */
185    mov     r0, r8
186    blx     _unix_syscall
187    mov     r0, #0x82
188    b       irqvec_panic
189
190swi_trap:
191    /* Switch outcome based on our input value */
192    cmp     r3, #3
193    addls   pc, pc, r3, lsl#2
194swi_trap_table:
195    b       swi_trap_ret
196    b       xxx_trap    /* icache clean */
197    b       xxx_trap    /* dcache clean */
198    b       thread_set_cthread_trap
199    b       thread_get_cthread_trap
200
201swi_trap_tb:
202    /* Fast return */
203    movs    pc, lr
204
205xxx_trap:
206    /* Just return. */
207    bl      _thread_exception_return
208
209thread_set_cthread_trap:
210    /* Set the current cthread value. */
211    blx     _thread_set_cthread_self
212    bl      _thread_exception_return
213    mov     r0, #0x82
214    b       irqvec_panic
215
216thread_get_cthread_trap:
217    /* Get the current cthread value and return to user. */
218    blx     _thread_get_cthread_self
219    LoadThreadRegister(r1)
220    ldr     r1, [r1, TH_PCB_USS]
221    str     r0, [r1]
222    bl      _thread_exception_return
223    mov     r0, #0x83
224    b       irqvec_panic
225
226/**
227 * fleh_prefabt
228 *
229 * Save registers and call the prefetch handler
230 */
231EnterARM(fleh_prefabt)
232    sub     lr, lr, #4
233
234    mrs     sp, cpsr
235    bic     sp, sp, #0x100
236    msr     cpsr_c, sp
237
238    mrs     sp, spsr
239    tst     sp, #0xf
240    bne     prefetch_abort_in_kernel
241
242prefetch_abort_in_user:
243    /* Oh well, not now. */
244    LoadThreadRegister(sp)
245    ldr     sp, [sp, TH_PCB_USS]
246    stmea   sp, {r0-lr}^
247
248    /* Save lr */
249    str     lr, [sp, #0x3C]
250
251    /* Save SPSR */
252    mrs     r0, spsr
253    str     r0, [sp, #0x40]
254
255    /* Save IFSR */
256    mrc     p15, 0, r0, c5, c0, 1
257    str     r0, [sp, #0x44]
258
259    /* Save IFAR */
260    mrc     p15, 0, r0, c6, c0, 2
261    str     r0, [sp, #0x48]
262
263    /* Supervisor mode */
264    cpsid   i, #0x13
265
266    mov     r0, sp
267
268    LoadThreadRegister(r1)
269    ldr     sp, [r1, TH_PCB_ISS]
270
271    mov     r1, #3      /* Prefetch Abort */
272    blx     _sleh_abort
273    b       _thread_exception_return
274
275prefetch_abort_in_kernel:
276    /* Supervisor mode */
277    msr     cpsr_c, #0x93
278
279    /* Make space on the stack for the registers. */
280    sub     sp, sp, #0x50
281    stmea   sp, {r0-r12}
282
283    /* Save the remaining registers. */
284    str     lr, [sp, #0x38]
285    mov     r12, sp
286
287    /* Abort mode */
288    msr     cpsr_c, #0x97
289
290    /* Save lr */
291    str     lr, [r12, #0x3C]
292
293    /* Save SPSR */
294    mrs     r4, spsr
295    str     r4, [r12, #0x40]
296
297    /* Save IFSR */
298    mrc     p15, 0, r5, c5, c0, 1
299    str     r5, [r12, #0x44]
300
301    /* Save IFAR */
302    mrc     p15, 0, r6, c6, c0, 2
303    str     r6, [r12, #0x48]
304
305    /* Supervisor mode */
306    msr     cpsr_c, #0x93
307
308    add     r12, r12, #0x50
309    str     r12, [sp, #0x34]
310    sub     r12, r12, #0x50
311
312    mov     r0, sp
313    mov     r1, #3      /* Prefetch Abort */
314    bl      _sleh_abort
315    b       restore_kernel_context
316
317/**
318 * fleh_dataabt
319 *
320 * Handle data aborts.
321 */
322EnterARM(fleh_dataabt)
323    /* Make sure the data abort was in the kernel. */
324    sub     lr, lr, #8
325    mrs     sp, spsr
326    tst     sp, #0xf
327    bne     data_abort_crash_in_kernel
328
329data_abort_crash_in_usermode:
330    /* Oh well, not now. */
331    LoadThreadRegister(sp)
332    ldr     sp, [sp, TH_PCB_USS]
333    stmea   sp, {r0-lr}^
334
335    /* Save lr */
336    str     lr, [sp, #0x3C]
337
338    /* Save SPSR */
339    mrs     r0, spsr
340    str     r0, [sp, #0x40]
341
342    /* Save DFSR */
343    mrc     p15, 0, r0, c5, c0, 0
344    str     r0, [sp, #0x44]
345
346    /* Save DFAR */
347    mrc     p15, 0, r0, c6, c0, 0
348    str     r0, [sp, #0x48]
349
350    mov     r0, sp
351
352    /* Supervisor mode */
353    cpsid   i, #0x13
354
355    LoadThreadRegister(r1)
356    ldr     sp, [r1, TH_PCB_ISS]
357
358    mov     r1, #4      /* Data Abort */
359    blx     _sleh_abort
360    b       _thread_exception_return
361
362data_abort_crash_in_kernel:
363    /* Supervisor mode */
364    cpsid   i, #0x13
365
366    /* Make space on the stack for the registers. */
367    sub     sp, sp, #0x50
368    stmea   sp, {r0-r12}
369
370    /* Save the remaining registers. */
371    str     lr, [sp, #0x38]
372    mov     r12, sp
373
374    /* Abort mode */
375    cpsid   i, #0x17
376
377    /* Save lr */
378    str     lr, [r12, #0x3C]
379
380    /* Save SPSR */
381    mrs     r4, spsr
382    str     r4, [r12, #0x40]
383
384    /* Supervisor mode */
385    cpsid   i, #0x13
386
387    /* Save DFSR */
388    mrc     p15, 0, r5, c5, c0, 0
389    str     r5, [sp, #0x44]
390
391    /* Save DFAR */
392    mrc     p15, 0, r6, c6, c0, 0
393    str     r6, [sp, #0x48]
394
395    /* Go to abort handler. */
396    add     r12, r12, #0x50
397    str     r12, [sp, #0x34]
398    sub     r12, r12, #0x50
399
400    mov     r0, sp
401    mov     r1, #4      /* Data Abort */
402    bl      _sleh_abort
403    b       return_to_kernel
404
405/**
406 * fleh_dataexc
407 *
408 * "Reserved" by ARM standards.
409 */
410EnterARM(fleh_dataexc)
411    b       .
412
413/**
414 * fleh_irq
415 *
416 * Dispatch timer and IRQ events.
417 */
418EnterARM(fleh_irq)
419    /* Check to see if the IRQ ocurred in user or in kernel mode */
420    sub     lr, lr, #4
421    mrs     sp, spsr
422    tst     sp, #0xF
423    bne     irqhandler_from_kernel
424
425irqhandler_from_user:
426    LoadThreadRegister(sp)
427    ldr     sp, [sp, TH_PCB_USS]
428    stmea   sp, {r0-lr}^
429    str     lr, [sp, #0x3C]
430    mrs     r0, spsr
431    str     r0, [sp, #0x40]
432    mov     r5, sp
433    LOAD_ADDR(sp, irqstack_top)
434    b       irq_join
435
436irqhandler_from_kernel:
437    /* Set up IRQ stack */
438    LOAD_ADDR(sp, irqstack_top)
439
440    /* Now save the registers */
441    sub     sp, sp, #0x50
442    stmea   sp, {r0-r12}
443
444    /* Set t-bit */
445    orr     lr, lr, #1
446
447    /* Save lr */
448    str     lr, [sp, #0x3C]
449
450    /* Save SPSR */
451    mrs     r4, spsr
452    str     r4, [sp, #0x40]
453
454    /* Change modes */
455    mov     r5, sp
456    and     r4, r4, #0x1F
457    orr     r4, r4, #0xC0
458    msr     cpsr_c, r4
459
460    /* Save sp */
461    str     sp, [r5, #0x34]
462    str     lr, [r5, #0x38]
463
464    /* Disable interrupts */
465    cpsid   i, #0x12
466
467irq_join:
468    mov     r0, r5
469    blx     _irq_handler
470    mrs     r4, spsr
471    tst     r4, #0xf
472    beq     irq_restore_user
473
474irq_restore_kernel:
475    /* Restore kernel registers and threads */
476    ldr     r4, [sp, #0x40]
477    tst     r4, #0x80
478    movne   r0, #1
479    bne     irqvec_panic
480
481    /* Threads. */
482    LoadThreadRegister(r12)
483    ldr     r0, [r12, MACHINE_THREAD_PREEMPT_COUNT]
484
485    /* Preempt thread if necessary */
486    bne     restore_kernel_context
487
488irq_preempt:
489    /* Prreempt thread */
490    ldmfd   sp, {r0-r12}
491    mrs     lr, spsr
492
493    /* Switch modes */
494    and     lr, lr, #0x1F
495    orr     lr, lr, #0xC0
496    msr     cpsr_c, lr
497
498    /* Save registers */
499    sub     sp, sp, #0x50
500    stmea   sp, {r0-r12}
501    mov     r5, sp
502
503    /* Switch modes, disable interrupts */
504    cpsid   i, #0x12
505
506    /* Set r0-r3 */
507    ldr     r0, [sp, #0x34]
508    str     r0, [r5, #0x34]
509
510    ldr     r0, [sp, #0x38]
511    str     r0, [r5, #0x38]
512
513    ldr     r0, [sp, #0x3c]
514    str     r0, [r5, #0x3c]
515
516    ldr     r0, [sp, #0x40]
517    str     r0, [r5, #0x40]
518
519    mov     sp, r5
520
521    /* Return to the kernel */
522    b       return_to_kernel
523irq_restore_user:
524    cpsid   i, #0x13
525    LoadThreadRegister(sp)
526    ldr     sp, [sp, TH_PCB_ISS]
527    b       _thread_exception_return
528
529return_to_kernel:
530    mov     r5, sp
531
532    /* Switch modes */
533    cpsid   i, #0x13
534
535    /* Check preemption */
536    ldr     r4, [sp, #0x40]
537    tst     r4, #0x80
538    bne     restart_kernel_execution
539
540    /* Check preemption count */
541    LoadThreadRegister(r12)
542    ldr     r0, [r12, MACHINE_THREAD_PREEMPT_COUNT]
543    cmp     r0, #0
544    bne     restart_kernel_execution
545
546preempt_kernel:
547    mov     r0, #7
548    mov     r1, #0
549    blx     _ast_taken
550
551restart_kernel_execution:
552    ldr     r6, [r5, #0x40]
553
554    /* Switch modes */
555    and     r6, r6, #0x1F
556    orr     r6, r6, #0xC0
557    msr     cpsr_c, r6
558    ldr     lr, [r5, #0x38]
559    ldr     sp, [r5, #0x34]
560    cpsid   i, #0x17
561    mov     sp, r5
562
563    /* The following routine should also be shared with data abort. */
564restore_kernel_context:
565    ldmfd   sp, {r0-r12}
566    add     sp, sp, #0x3C
567    rfefd   sp!
568    b       .
569
570/*
571 * vector panic helper.
572 */
573irqvec_panic:
574    LOAD_ADDR(sp, irqstack_top)
575    mov     r1, r0
576    adr     r0, vecPanicString
577    blx     _panic
578    b       .
579
580vecPanicString:
581    .asciz  "fleh_vectors: exception in exception vectors, 0x%08x"
582
583LOAD_ADDR_GEN_DEF(irqstack_top)
584LOAD_ADDR_GEN_DEF(kern_invalid)
585LOAD_ADDR_GEN_DEF(mach_trap_table)
586
587