root/nxos/base/interrupts.S

Revision 495:8da3e4159453, 5.2 kB (checked in by David Anderson <dave@…>, 13 months ago)

Implement prefetch and data abort handlers.

The handlers are fairly basic, and just attempt to synchronously push a
message to the screen before locking up the system, giving the type of
abort, as well as the PC and CPSR at the time of abort.

Line 
1/* Copyright (C) 2007 the NxOS developers
2 *
3 * See AUTHORS for a full list of the developers.
4 *
5 * Redistribution of this file is permitted under
6 * the terms of the GNU Public License (GPL) version 2.
7 */
8
9.text
10.code 32
11.align  0
12
13#include "asm_decls.h"
14
15/*
16 * Definition of a few constants, for readability.
17 */
18#define AIC_IVR 0xFFFFF100   /* AIC Interrupt Vector Register */
19#define AIC_EOICR 0xFFFFF130 /* End Of Interrupt Control Register */
20
21/**********************************************************
22 * Default handlers for unknown IRQ and FIQ interrupts, and
23 * the spurious IRQ handler and unknown exception handler.
24 */
25        .global nx__default_irq
26        .global nx__default_fiq
27        .global nx__spurious_irq
28        .global nx__unhandled_exception
29nx__default_irq:
30nx__default_fiq:
31nx__spurious_irq:
32nx__unhandled_exception:
33        b nx__unhandled_exception
34
35
36/**********************************************************
37 * IRQ entry/exit routine. This gets called for all
38 * interrupts, saves state, switches to Supervisor mode,
39 * enables nested IRQ handling, and calls the service
40 * routing that was registered with the AIC for the given
41 * IRQ.
42 */
43        .global nx__irq_handler
44nx__irq_handler:
45        /* Save the return address and return processor mode registers
46         * on the IRQ stack. This is needed to allow nested interrupts,
47         * where a higher-priority interrupt halts execution of a lower
48         * priority interrupt.
49         */
50        sub lr, lr, #4
51        stmfd sp!, {lr}
52        mrs lr, spsr
53        stmfd sp!, {lr}
54
55        /* If we were interrupted out of User or System mode, there is a
56         * scheduler running, and we need to save the task state nicely.
57         *
58         * This code is beautiful. An ode of joy to everything ARM. Conditional
59         * execution, status bit updates, the next few instructions
60         * have it all.
61         *
62         * Note that each code path pushes one final value on the
63         * stack. This value is 0 if this interrupt is nested, and !0 if
64         * this interrupt is interrupting a user task. This value will be
65         * used to figure out how to return from the interrupt later.
66         */
67
68        /* Compare the SPSR mode bits to both 0000 and 1111 (usr/sys mode). */
69        ands lr, lr, #7
70        eornes lr, lr, #7
71
72        /* Switch to either sys or svc mode to save the state. */
73        msreq cpsr_c, #(MODE_SYS | IRQ_FIQ_MASK)
74        msrne cpsr_c, #(MODE_SVC | IRQ_FIQ_MASK)
75
76        /* Save the state. If we're saving a user task, also grab the task
77         * CPSR and PC from the IRQ stack.
78         */
79        stmfd sp!, {r0-r12,lr}
80        ldreq r0, =__irq_stack__
81        ldmeqdb r0, {r1,r2}
82        stmeqfd sp!, {r1,r2}
83        movne r0, #0
84
85        /* Get the IVR value. */
86        ldr r1, =AIC_IVR
87        ldr r2, [r1]
88
89        /* If we're in Protected mode (usually for JTAG debugging), we
90         * need to write back to the IVR register to tell the AIC it
91         * can dispatch other higher priority interrupts again.
92         * In normal mode, this has no effect, so we can safely do it.
93         */
94        str r1, [r1]
95
96        /* Switch to Supervisor mode if necessary, reenable IRQ and FIQ handling,
97         * and stack either 0 (nested IRQ) or the address of the IRQ stack base (for
98         * a task IRQ).
99         */
100        msr cpsr_c, #MODE_SVC
101        stmfd sp!, {r0}
102
103        /* Dispatch the IRQ to the registered handler. */
104        mov lr, pc
105        bx r2
106
107        /* Restore the interrupted state. How this is done depends on the value at
108         * the top of the stack, as explained above.
109         *
110         * Note that we inhibit IRQ and FIQ handling during task restoration only if
111         * we're restoring a user task. Getting interrupted while we're tweaking around
112         * in system mode would confuse the IRQ handler into breaking things.
113         */
114        ldmfd sp!, {r0}
115        cmp r0, #0
116        msrne cpsr_c, #(MODE_SYS | IRQ_FIQ_MASK)
117        ldrne r0, =__irq_stack__
118        ldmnefd sp!, {r1,r2}
119        stmnedb r0, {r1,r2}
120        ldmfd sp!, {r0-r12,lr}
121
122        /* Switch back to IRQ mode and tell the AIC that the interrupt has been
123         * handled.
124         */
125        msr cpsr_c, #(MODE_IRQ | IRQ_FIQ_MASK)
126        ldr lr, =AIC_EOICR
127        str lr, [lr]
128
129        /* Restore the SPSR */
130        ldmfd sp!, {lr}
131        msr spsr_all, lr
132
133        /* Restore execution to the main code. */
134        ldmfd sp!, {pc}^
135
136
137/**********************************************************
138 * Abort entry points. These get run when the CPU enters
139 * prefetch or data abort modes. These handlers just set
140 * up the necessary arguments and invoke nx__abort().
141 */
142        .extern nx__abort
143        .global nx__prefetch_abort_handler
144nx__prefetch_abort_handler:
145        sub r1, lr, #4
146        mov r0, #0
147        mrs r2, spsr
148        b nx__abort
149
150        .global nx__data_abort_handler
151nx__data_abort_handler:
152        sub r1, lr, #8
153        mov r0, #1
154        mrs r2, spsr
155        b nx__abort
156
157
158/**********************************************************
159 * Nested interrupt disable/enable routines. The
160 * interrupts_disable routine disables IRQ/FIQ handling,
161 * and increments a nesting counter. The interrupts_enable
162 * routine decrements the counter, and reenables interrupts
163 * when the counter reaches zero.
164 *
165 * When the system boots, the internal counter is set to
166 * interrupts disabled with 1 level of nesting (ie. calling
167 * interrupts_enable once will enable interrupts).
168 */
169        .global nx_interrupts_disable
170        .global nx_interrupts_enable
171nx_interrupts_disable:
172        mrs r0, cpsr
173        orr r0, r0, #IRQ_FIQ_MASK
174        msr cpsr_c, r0
175
176        ldr r1, =interrupts_count
177        ldr r0, [r1]
178        add r0, r0, #1
179        str r0, [r1]
180
181        bx lr
182
183nx_interrupts_enable:
184        ldr r0, =interrupts_count
185        ldr r1, [r0]
186        subs r1, r1, #1
187        str r1, [r0]
188
189        bxne lr
190
191        mrs r0, cpsr
192        bic r0, r0, #IRQ_FIQ_MASK
193        msr cpsr_c, r0
194        bx lr
195
196interrupts_count:       .long 1
Note: See TracBrowser for help on using the browser.