9f4255428398143e9662d98e58d684c2296b58d5
[barrelfish] / kernel / arch / arm / exn.c
1 /*
2  * Copyright (c) 2009-2013 ETH Zurich.
3  * All rights reserved.
4  *
5  * This file is distributed under the terms in the attached LICENSE file.
6  * If you do not find this file, copies can be found by writing to:
7  * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
8  */
9
10 #include <kernel.h>
11 #include <dispatch.h>
12 #include <arm.h>
13 #include <arm_hal.h>
14 /* XXX - not AArch64-compatible. */
15 #include <cp15.h>
16 #include <exceptions.h>
17 #include <exec.h>
18 #include <misc.h>
19 #include <stdio.h>
20 #include <wakeup.h>
21 #include <irq.h>
22
23 void handle_user_page_fault(lvaddr_t                fault_address,
24                             arch_registers_state_t* save_area)
25 {
26     lvaddr_t handler;
27     struct dispatcher_shared_arm *disp = get_dispatcher_shared_arm(dcb_current->disp);
28     uintptr_t saved_pc = save_area->named.pc;
29
30     disp->d.disabled = dispatcher_is_disabled_ip(dcb_current->disp, saved_pc);
31     bool disabled = (disp->d.disabled != 0);
32
33     assert(dcb_current->disp_cte.cap.type == ObjType_Frame);
34
35     printk(LOG_WARN, "user page fault%s in '%.*s': addr %"PRIxLVADDR
36                       " IP %"PRIxPTR"\n",
37            disabled ? " WHILE DISABLED" : "", DISP_NAME_LEN,
38            disp->d.name, fault_address, saved_pc);
39
40     if (disabled) {
41         assert(save_area == &disp->trap_save_area);
42         handler = disp->d.dispatcher_pagefault_disabled;
43         dcb_current->faults_taken++;
44     }
45     else {
46         assert(save_area == &disp->enabled_save_area);
47         handler = disp->d.dispatcher_pagefault;
48     }
49
50     if (dcb_current->faults_taken > 2) {
51         printk(LOG_WARN, "handle_user_page_fault: too many faults, "
52                "making domain unrunnable\n");
53         dcb_current->faults_taken = 0; // just in case it gets restarted
54         scheduler_remove(dcb_current);
55         dispatch(schedule());
56     }
57     else {
58         //
59         // Upcall to dispatcher
60         //
61         // NB System might be cleaner with a prototype
62         // dispatch context that has R0-R3 to be overwritten
63         // plus initial stack, thread, and gic registers. Could do
64         // a faster resume_for_upcall().
65         //
66
67         struct dispatcher_shared_generic *disp_gen =
68             get_dispatcher_shared_generic(dcb_current->disp);
69
70         union registers_arm resume_area;
71
72         resume_area.named.cpsr = CPSR_F_MASK | ARM_MODE_USR;
73         resume_area.named.pc   = handler;
74         resume_area.named.r0   = disp_gen->udisp;
75         resume_area.named.r1   = fault_address;
76         resume_area.named.r2   = 0;
77         resume_area.named.r3   = saved_pc;
78         resume_area.named.rtls = disp_gen->udisp;
79         resume_area.named.r10  = disp->got_base;
80
81         // SP is set by handler routine.
82
83         // Upcall user to save area
84         disp->d.disabled = true;
85         resume(&resume_area);
86     }
87 }
88
89 void handle_user_undef(lvaddr_t fault_address,
90                        arch_registers_state_t* save_area)
91 {
92     union registers_arm resume_area;
93
94     struct dispatcher_shared_arm *disp = get_dispatcher_shared_arm(dcb_current->disp);
95
96     bool disabled = dispatcher_is_disabled_ip(dcb_current->disp, save_area->named.pc);
97     disp->d.disabled = disabled;
98
99     assert(dcb_current->disp_cte.cap.type == ObjType_Frame);
100     if (disabled) {
101         //        assert(save_area == &disp->trap_save_area);
102     }
103     else {
104         assert(save_area == &disp->enabled_save_area);
105     }
106
107     printk(LOG_WARN, "user undef fault%s in '%.*s': IP %" PRIuPTR "\n",
108            disabled ? " WHILE DISABLED" : "", DISP_NAME_LEN,
109            disp->d.name, fault_address);
110
111     struct dispatcher_shared_generic *disp_gen =
112         get_dispatcher_shared_generic(dcb_current->disp);
113
114     resume_area.named.cpsr = CPSR_F_MASK | ARM_MODE_USR;
115     resume_area.named.pc   = disp->d.dispatcher_trap;
116     resume_area.named.r0   = disp_gen->udisp;
117     resume_area.named.r1   = ARM_EVECTOR_UNDEF;
118     resume_area.named.r2   = 0;
119     resume_area.named.r3   = fault_address;
120     resume_area.named.rtls = disp_gen->udisp;
121     resume_area.named.r10  = disp->got_base;
122
123     // Upcall user to save area
124     disp->d.disabled = true;
125     resume(&resume_area);
126 }
127
128 /* XXX - not 64-bit clean, not AArch64-compatible. */
129 static int32_t bkpt_decode(lvaddr_t fault_address)
130 {
131     int32_t bkpt_id = -1;
132     if ((fault_address & 3) == 0 && fault_address >= KERNEL_OFFSET) {
133         const uint32_t bkpt_mask = 0xfff000f0;
134         const uint32_t bkpt_isn  = 0xe1200070;
135
136         uintptr_t isn = *((uintptr_t*)fault_address);
137         if ((isn & bkpt_mask) == bkpt_isn) {
138             bkpt_id = (int32_t)((isn & 0xf) | ((isn & 0xfff00) >> 4));
139         }
140     }
141     return bkpt_id;
142 }
143
144 /* XXX - not 64-bit clean, not AArch64-compatible. */
145 void fatal_kernel_fault(uint32_t evector, lvaddr_t address, arch_registers_state_t* save_area
146     )
147 {
148     int i;
149     printk(LOG_PANIC, "Kernel fault at %08"PRIxLVADDR
150                       " vector %08"PRIx32"\n\n", address, evector);
151     printk(LOG_PANIC, "Processor save_area at: %p\n", save_area);
152
153     for (i = 0; i < 16; i++) {
154         const char *extrainfo = "";
155
156         switch(i) {
157         case 13:
158             extrainfo = "\t(sp)";
159             break;
160
161         case 14:
162             extrainfo = "\t(lr)";
163             break;
164
165         case 15:
166             {
167                 char str[128];
168                 snprintf(str, 128, "\t(pc)\t%08lx",
169                          save_area->regs[R0_REG + i] -
170                          (uint32_t)&kernel_first_byte +
171                          0x100000);
172                 extrainfo = str;
173             }
174             break;
175         }
176
177         printk(LOG_PANIC, "r%d\t%08"PRIx32"%s\n", i, save_area->regs[R0_REG + i], extrainfo);
178     }
179     printk(LOG_PANIC, "cpsr\t%08"PRIx32"\n", save_area->regs[CPSR_REG]);
180     printk(LOG_PANIC, "called from: %#lx\n",
181             (lvaddr_t)__builtin_return_address(0) -
182             (lvaddr_t)&kernel_first_byte + 0x100000);
183
184     switch (evector) {
185         case ARM_EVECTOR_UNDEF:
186             panic("Undefined instruction.\n");
187             break;
188
189       case ARM_EVECTOR_PABT: {
190             int ifsr = cp15_read_ifsr();
191             if (ifsr == 0) {
192                 int bkpt = bkpt_decode(address);
193                 if (bkpt >= 0) {
194                     panic("Breakpoint: %4x\n", bkpt);
195                 }
196             }
197             panic("Prefetch abort: ifsr %08x\n", ifsr);
198       }
199       break;
200
201       case ARM_EVECTOR_DABT:
202           {
203               uint32_t dfsr = cp15_read_dfsr();
204
205               printf("\n");
206
207               if((dfsr >> 11) & 1) {
208                   printf("On write access\n");
209               } else {
210                   printf("On read access\n");
211               }
212
213               switch((dfsr & 0xf) | (dfsr & 0x400)) {
214               case 1:
215                   printf("Alignment fault\n");
216                   break;
217
218               case 4:
219                   printf("Instruction cache-maintenance fault\n");
220                   break;
221
222               case 5:
223                   printf("Translation fault on section\n");
224                   break;
225
226               case 6:
227                   printf("Translation fault on page\n");
228                   break;
229
230               case 8:
231                   printf("Synchronous external abort\n");
232                   break;
233
234               default:
235                   printf("Unknown fault\n");
236                   break;
237               }
238
239               panic("Data abort: dfsr %08"PRIx32"\n", dfsr);
240           }
241
242       default:
243         panic("Caused by evector: %02"PRIx32, evector);
244         break;
245     }
246 }
247
248 void handle_irq(arch_registers_state_t* save_area, uintptr_t fault_pc)
249 {
250     uint32_t irq = 0;
251 /* XXX - not revision-independent. */
252 #if defined(__ARM_ARCH_7A__)
253     irq = gic_get_active_irq();
254 #else
255     // this is for ARMv5, -SG
256     irq = pic_get_active_irq();
257 #endif
258
259 /* XXX - not 64-bit clean */
260     debug(SUBSYS_DISPATCH, "IRQ %"PRIu32" while %s\n", irq,
261           dcb_current ? (dcb_current->disabled ? "disabled": "enabled") : "in kernel");
262
263     if (dcb_current != NULL) {
264         dispatcher_handle_t handle = dcb_current->disp;
265         if (save_area == dispatcher_get_disabled_save_area(handle)) {
266             assert(dispatcher_is_disabled_ip(handle, fault_pc));
267             dcb_current->disabled = true;
268         } else {
269 /*            debug(SUBSYS_DISPATCH,
270                   "save_area=%p, dispatcher_get_enabled_save_are(handle)=%p\n",
271                    save_area, dispatcher_get_enabled_save_area(handle));
272 */
273
274             assert(save_area == dispatcher_get_enabled_save_area(handle));
275             assert(!dispatcher_is_disabled_ip(handle, fault_pc));
276             dcb_current->disabled = false;
277         }
278     }
279
280     if (pit_handle_irq(irq)) {
281         // Timer interrupt, pit_handle_irq acks it at the timer.
282         assert(kernel_ticks_enabled);
283         kernel_now += kernel_timeslice;
284         wakeup_check(kernel_now);
285         dispatch(schedule());
286     }
287     // this is the (still) unacknowledged startup interrupt sent by the BSP
288     // we just acknowledge it here
289     else if(irq == 1)
290     {
291 /* XXX - not revision-independent. */
292 #if defined(__ARM_ARCH_7A__)
293         gic_ack_irq(irq);
294 #else
295         // this is for ARMv5, -SG
296         pic_ack_irq(irq);
297 #endif
298         dispatch(schedule());
299     }
300     else {
301 /* XXX - not revision-independent. */
302 #if defined(__ARM_ARCH_7A__)
303         gic_ack_irq(irq);
304         send_user_interrupt(irq);
305         panic("Unhandled IRQ %"PRIu32"\n", irq);
306 #else
307         // SK: No support for user-level interrupts on ARMv5 and XScale
308         panic("Unhandled IRQ %"PRIu32". User-level IRQs only supported on ARMv7!\n", irq);
309 #endif
310     }
311
312 }