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 <platform.h> 14/* XXX - not AArch64-compatible. */ 15#include <cp15.h> 16#include <exceptions.h> 17#include <exec.h> 18#include <kputchar.h> 19#include <misc.h> 20#include <stdio.h> 21#include <wakeup.h> 22#include <irq.h> 23#include <gic.h> 24#include <systime.h> 25 26void handle_user_page_fault(lvaddr_t fault_address, 27 arch_registers_state_t* save_area, 28 struct dispatcher_shared_arm *disp) 29{ 30 // XXX 31 // Set dcb_current->disabled correctly. This should really be 32 // done in exceptions.S 33 // XXX 34 assert(dcb_current != NULL); 35 assert((struct dispatcher_shared_arm *)(dcb_current->disp) == disp); 36 if (dispatcher_is_disabled_ip((dispatcher_handle_t)disp, save_area->named.pc)) { 37 assert(save_area == dispatcher_get_trap_save_area((dispatcher_handle_t)disp)); 38 dcb_current->disabled = true; 39 } else { 40 assert(save_area == dispatcher_get_enabled_save_area((dispatcher_handle_t)disp)); 41 dcb_current->disabled = false; 42 } 43 44 lvaddr_t handler; 45 uintptr_t saved_pc = save_area->named.pc; 46 47 assert(dcb_current->disp_cte.cap.type == ObjType_Frame); 48 49 printk(LOG_WARN, "user page fault%s in '%.*s': addr %"PRIxLVADDR 50 " IP %"PRIxPTR"\n", 51 dcb_current->disabled ? " WHILE DISABLED" : "", DISP_NAME_LEN, 52 disp->d.name, fault_address, saved_pc); 53 54 if (dcb_current->disabled) { 55 handler = disp->d.dispatcher_pagefault_disabled; 56 dcb_current->faults_taken++; 57 } else { 58 handler = disp->d.dispatcher_pagefault; 59 } 60 61 if (dcb_current->faults_taken > 2) { 62 printk(LOG_WARN, "handle_user_page_fault: too many faults, " 63 "making domain unrunnable\n"); 64 dcb_current->faults_taken = 0; // just in case it gets restarted 65 scheduler_remove(dcb_current); 66 dispatch(schedule()); 67 } else { 68 // 69 // Upcall to dispatcher 70 // 71 // NB System might be cleaner with a prototype 72 // dispatch context that has R0-R3 to be overwritten 73 // plus initial stack, thread, and gic registers. Could do 74 // a faster resume_for_upcall(). 75 // 76 union registers_arm resume_area; 77 78 resume_area.named.cpsr = CPSR_F_MASK | ARM_MODE_USR; 79 resume_area.named.pc = handler; 80 resume_area.named.r0 = disp->d.udisp; 81 resume_area.named.r1 = fault_address; 82 resume_area.named.r2 = 0; 83 resume_area.named.r3 = saved_pc; 84 resume_area.named.r9 = disp->got_base; 85 86 // SP is set by handler routine. 87 88 // Upcall user to save area 89 disp->d.disabled = true; 90 resume(&resume_area); 91 } 92} 93 94void handle_user_undef(lvaddr_t fault_address, 95 arch_registers_state_t* save_area, 96 struct dispatcher_shared_arm *disp) 97{ 98 // XXX 99 // Set dcb_current->disabled correctly. This should really be 100 // done in exceptions.S 101 // XXX 102 assert(dcb_current != NULL); 103 assert((struct dispatcher_shared_arm *)(dcb_current->disp) == disp); 104 if (dispatcher_is_disabled_ip((dispatcher_handle_t)disp, save_area->named.pc)) { 105 assert(save_area == dispatcher_get_trap_save_area((dispatcher_handle_t)disp)); 106 dcb_current->disabled = true; 107 } else { 108 assert(save_area == dispatcher_get_enabled_save_area((dispatcher_handle_t)disp)); 109 dcb_current->disabled = false; 110 } 111 112 union registers_arm resume_area; 113 114 assert(dcb_current->disp_cte.cap.type == ObjType_Frame); 115 printk(LOG_WARN, "user undef fault%s in '%.*s': IP %p\n", 116 dcb_current->disabled ? " WHILE DISABLED" : "", DISP_NAME_LEN, 117 disp->d.name, fault_address); 118 119 resume_area.named.cpsr = CPSR_F_MASK | ARM_MODE_USR; 120 resume_area.named.pc = disp->d.dispatcher_trap; 121 resume_area.named.r0 = disp->d.udisp; 122 resume_area.named.r1 = ARM_EVECTOR_UNDEF; 123 resume_area.named.r2 = 0; 124 resume_area.named.r3 = fault_address; 125 resume_area.named.r9 = disp->got_base; 126 127 // Upcall user to save area 128 disp->d.disabled = true; 129 resume(&resume_area); 130} 131 132/* XXX - not 64-bit clean, not AArch64-compatible. */ 133static int32_t bkpt_decode(lvaddr_t fault_address) 134{ 135 int32_t bkpt_id = -1; 136 if ((fault_address & 3) == 0 && fault_address >= KERNEL_OFFSET) { 137 const uint32_t bkpt_mask = 0xfff000f0; 138 const uint32_t bkpt_isn = 0xe1200070; 139 140 uintptr_t isn = *((uintptr_t*)fault_address); 141 if ((isn & bkpt_mask) == bkpt_isn) { 142 bkpt_id = (int32_t)((isn & 0xf) | ((isn & 0xfff00) >> 4)); 143 } 144 } 145 return bkpt_id; 146} 147 148/* XXX - not 64-bit clean, not AArch64-compatible. */ 149void fatal_kernel_fault(uint32_t evector, lvaddr_t address, 150 arch_registers_state_t* save_area) 151{ 152 int i; 153 154 /* Force the print spinlock release. We're panicking now anyway, and if 155 * the kernel fault was *inside* kprintf, then we'll spin forever here, 156 * and never actually report the panic. */ 157 /* XXX - implement lock_do_i_hold(), so we only do this if we're the 158 * holder. */ 159 kprintf_end(); 160 161 printk(LOG_PANIC, "\n"); 162 printk(LOG_PANIC, "Kernel fault at %08"PRIxLVADDR 163 " vector %08"PRIx32"\n\n", address, evector); 164 printk(LOG_PANIC, "Processor save_area at: %p\n", save_area); 165 166 for (i = 0; i < 16; i++) { 167 const char *extrainfo = ""; 168 169 switch(i) { 170 case 13: 171 extrainfo = "\t(sp)"; 172 break; 173 174 case 14: 175 extrainfo = "\t(lr)"; 176 break; 177 178 case 15: 179 { 180 char str[128]; 181 snprintf(str, 128, "\t(pc)\t%08"PRIx32, 182 save_area->regs[R0_REG + i] - 183 (uint32_t)&kernel_first_byte + 184 0x100000); 185 extrainfo = str; 186 } 187 break; 188 } 189 190 printk(LOG_PANIC, "r%d\t%08"PRIx32"%s\n", i, save_area->regs[R0_REG + i], extrainfo); 191 } 192 printk(LOG_PANIC, "cpsr\t%08"PRIx32"\n", save_area->regs[CPSR_REG]); 193 printk(LOG_PANIC, "called from: #%08"PRIx32"\n", 194 (lvaddr_t)__builtin_return_address(0) - 195 (lvaddr_t)&kernel_first_byte + 0x100000); 196 197 switch (evector) { 198 case ARM_EVECTOR_UNDEF: 199 panic("Undefined instruction.\n"); 200 break; 201 202 case ARM_EVECTOR_PABT: { 203 int ifsr = cp15_read_ifsr(); 204 if (ifsr == 0) { 205 int bkpt = bkpt_decode(address); 206 if (bkpt >= 0) { 207 panic("Breakpoint: %4x\n", bkpt); 208 } 209 } 210 panic("Prefetch abort: ifsr %08x\n", ifsr); 211 } 212 213 case ARM_EVECTOR_DABT: { 214 uint32_t dfsr = cp15_read_dfsr(); 215 216 printf("\n"); 217 218 printf("Data abort "); 219 if((dfsr >> 11) & 1) { 220 printf("on write\n"); 221 } else { 222 printf("on read\n"); 223 } 224 225 switch((dfsr & 0xf) | (dfsr & 0x400)) { 226 case 1: 227 printf("Alignment fault\n"); 228 break; 229 230 case 4: 231 printf("Instruction cache-maintenance fault\n"); 232 break; 233 234 case 5: 235 printf("Translation fault on section\n"); 236 break; 237 238 case 6: 239 printf("Translation fault on page\n"); 240 break; 241 242 case 8: 243 printf("Synchronous external abort\n"); 244 break; 245 246 default: 247 printf("Unknown fault\n"); 248 break; 249 } 250 251 panic("Data abort: dfsr %08"PRIx32"\n", dfsr); 252 } 253 254 case ARM_EVECTOR_IRQ: { 255 uint32_t irq = gic_get_active_irq(); 256 panic("IRQ %"PRIu32" in the kernel", irq); 257 } 258 259 default: 260 panic("Caused by evector: %02"PRIx32, evector); 261 break; 262 } 263} 264 265void handle_fiq_kernel(arch_registers_state_t* save_area, uintptr_t fault_pc) 266{ 267 panic("FIQ Interrupt in the kernel"); 268} 269 270void handle_fiq(arch_registers_state_t* save_area, 271 uintptr_t fault_pc, 272 struct dispatcher_shared_generic *disp) 273{ 274 panic("FIQ interrupt from user mode"); 275} 276 277void handle_irq_kernel(arch_registers_state_t* save_area, 278 uintptr_t fault_pc) 279{ 280 /* In-kernel interrupts are bugs, except if we'd gone to sleep in 281 * wait_for_interrupt(), in which case there is no current dispatcher. */ 282 if(waiting_for_interrupt) { 283 waiting_for_interrupt= 0; 284 } 285 else { 286 fatal_kernel_fault(ARM_EVECTOR_IRQ, fault_pc, save_area); 287 } 288 289 handle_irq(save_area, fault_pc, NULL); 290} 291 292void handle_irq(arch_registers_state_t* save_area, 293 uintptr_t fault_pc, 294 struct dispatcher_shared_arm *disp) 295{ 296 // XXX 297 // Set dcb_current->disabled correctly. This should really be 298 // done in exceptions.S 299 // XXX 300 if(dcb_current != NULL) { 301 assert((struct dispatcher_shared_arm *)(dcb_current->disp) == disp); 302 if (dispatcher_is_disabled_ip((dispatcher_handle_t)disp, fault_pc)) { 303 assert(save_area == 304 dispatcher_get_disabled_save_area( 305 (dispatcher_handle_t)disp)); 306 dcb_current->disabled = true; 307 } else { 308 assert(save_area == 309 dispatcher_get_enabled_save_area( 310 (dispatcher_handle_t)disp)); 311 dcb_current->disabled = false; 312 } 313 } 314 315 // Retrieve the current IRQ number 316 uint32_t irq = 0; 317 irq = gic_get_active_irq(); 318 debug(SUBSYS_DISPATCH, "IRQ %"PRIu32" while %s\n", irq, 319 dcb_current->disabled ? "disabled": "enabled" ); 320 321 // Offer it to the timer 322 if (timer_interrupt(irq)) { 323 // Timer interrupt, timer_interrupt() acks it at the timer. 324 wakeup_check(systime_now()); 325 dispatch(schedule()); 326 } 327 // this is the (still) unacknowledged startup interrupt sent by the BSP 328 // we just acknowledge it here 329 else if(irq == 1) 330 { 331 gic_ack_irq(irq); 332 dispatch(schedule()); 333 } 334 else { 335 gic_ack_irq(irq); 336 send_user_interrupt(irq); 337 panic("Unhandled IRQ %"PRIu32"\n", irq); 338 } 339} 340