1 /* 2 * Copyright 2003-2009, Haiku Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Axel Dörfler <axeld@pinc-software.de> 7 * Ingo Weinhold <bonefish@cs.tu-berlin.de> 8 * 9 * Copyright 2001, Travis Geiselbrecht. All rights reserved. 10 * Distributed under the terms of the NewOS License. 11 */ 12 13 #include <int.h> 14 15 #include <arch/smp.h> 16 #include <boot/kernel_args.h> 17 #include <device_manager.h> 18 #include <kscheduler.h> 19 #include <interrupt_controller.h> 20 #include <smp.h> 21 #include <thread.h> 22 #include <timer.h> 23 #include <util/DoublyLinkedList.h> 24 #include <util/kernel_cpp.h> 25 #include <vm/vm.h> 26 #include <vm/vm_priv.h> 27 #include <vm/VMAddressSpace.h> 28 29 #include <string.h> 30 31 // defined in arch_exceptions.S 32 extern int __irqvec_start; 33 extern int __irqvec_end; 34 35 extern"C" void ppc_exception_tail(void); 36 37 38 // the exception contexts for all CPUs 39 static ppc_cpu_exception_context sCPUExceptionContexts[SMP_MAX_CPUS]; 40 41 42 // An iframe stack used in the early boot process when we don't have 43 // threads yet. 44 struct iframe_stack gBootFrameStack; 45 46 // interrupt controller interface (initialized 47 // in arch_int_init_post_device_manager()) 48 static struct interrupt_controller_module_info *sPIC; 49 static void *sPICCookie; 50 51 52 void 53 arch_int_enable_io_interrupt(int irq) 54 { 55 if (!sPIC) 56 return; 57 58 // TODO: I have no idea, what IRQ type is appropriate. 59 sPIC->enable_io_interrupt(sPICCookie, irq, IRQ_TYPE_LEVEL); 60 } 61 62 63 void 64 arch_int_disable_io_interrupt(int irq) 65 { 66 if (!sPIC) 67 return; 68 69 sPIC->disable_io_interrupt(sPICCookie, irq); 70 } 71 72 73 /* arch_int_*_interrupts() and friends are in arch_asm.S */ 74 75 76 static void 77 print_iframe(struct iframe *frame) 78 { 79 dprintf("iframe at %p:\n", frame); 80 dprintf("r0-r3: 0x%08lx 0x%08lx 0x%08lx 0x%08lx\n", frame->r0, frame->r1, frame->r2, frame->r3); 81 dprintf("r4-r7: 0x%08lx 0x%08lx 0x%08lx 0x%08lx\n", frame->r4, frame->r5, frame->r6, frame->r7); 82 dprintf("r8-r11: 0x%08lx 0x%08lx 0x%08lx 0x%08lx\n", frame->r8, frame->r9, frame->r10, frame->r11); 83 dprintf("r12-r15: 0x%08lx 0x%08lx 0x%08lx 0x%08lx\n", frame->r12, frame->r13, frame->r14, frame->r15); 84 dprintf("r16-r19: 0x%08lx 0x%08lx 0x%08lx 0x%08lx\n", frame->r16, frame->r17, frame->r18, frame->r19); 85 dprintf("r20-r23: 0x%08lx 0x%08lx 0x%08lx 0x%08lx\n", frame->r20, frame->r21, frame->r22, frame->r23); 86 dprintf("r24-r27: 0x%08lx 0x%08lx 0x%08lx 0x%08lx\n", frame->r24, frame->r25, frame->r26, frame->r27); 87 dprintf("r28-r31: 0x%08lx 0x%08lx 0x%08lx 0x%08lx\n", frame->r28, frame->r29, frame->r30, frame->r31); 88 dprintf(" ctr 0x%08lx xer 0x%08lx\n", frame->ctr, frame->xer); 89 dprintf(" cr 0x%08lx lr 0x%08lx\n", frame->cr, frame->lr); 90 dprintf(" dsisr 0x%08lx dar 0x%08lx\n", frame->dsisr, frame->dar); 91 dprintf(" srr1 0x%08lx srr0 0x%08lx\n", frame->srr1, frame->srr0); 92 } 93 94 95 extern "C" void ppc_exception_entry(int vector, struct iframe *iframe); 96 void 97 ppc_exception_entry(int vector, struct iframe *iframe) 98 { 99 if (vector != 0x900) { 100 dprintf("ppc_exception_entry: time %lld vector 0x%x, iframe %p, " 101 "srr0: %p\n", system_time(), vector, iframe, (void*)iframe->srr0); 102 } 103 104 struct thread *thread = thread_get_current_thread(); 105 106 // push iframe 107 if (thread) 108 ppc_push_iframe(&thread->arch_info.iframes, iframe); 109 else 110 ppc_push_iframe(&gBootFrameStack, iframe); 111 112 switch (vector) { 113 case 0x100: // system reset 114 panic("system reset exception\n"); 115 break; 116 case 0x200: // machine check 117 panic("machine check exception\n"); 118 break; 119 case 0x300: // DSI 120 case 0x400: // ISI 121 { 122 bool kernelDebugger = debug_debugger_running(); 123 124 if (kernelDebugger) { 125 // if this thread has a fault handler, we're allowed to be here 126 struct thread *thread = thread_get_current_thread(); 127 if (thread && thread->fault_handler != 0) { 128 iframe->srr0 = thread->fault_handler; 129 break; 130 } 131 132 // otherwise, not really 133 panic("page fault in debugger without fault handler! Touching " 134 "address %p from ip %p\n", (void *)iframe->dar, 135 (void *)iframe->srr0); 136 break; 137 } else if ((iframe->srr1 & MSR_EXCEPTIONS_ENABLED) == 0) { 138 // if the interrupts were disabled, and we are not running the 139 // kernel startup the page fault was not allowed to happen and 140 // we must panic 141 panic("page fault, but interrupts were disabled. Touching " 142 "address %p from ip %p\n", (void *)iframe->dar, 143 (void *)iframe->srr0); 144 break; 145 } else if (thread != NULL && thread->page_faults_allowed < 1) { 146 panic("page fault not allowed at this place. Touching address " 147 "%p from ip %p\n", (void *)iframe->dar, 148 (void *)iframe->srr0); 149 } 150 151 enable_interrupts(); 152 153 addr_t newip; 154 155 vm_page_fault(iframe->dar, iframe->srr0, 156 iframe->dsisr & (1 << 25), // store or load 157 iframe->srr1 & (1 << 14), // was the system in user or supervisor 158 &newip); 159 if (newip != 0) { 160 // the page fault handler wants us to modify the iframe to set the 161 // IP the cpu will return to to be this ip 162 iframe->srr0 = newip; 163 } 164 break; 165 } 166 167 case 0x500: // external interrupt 168 { 169 if (!sPIC) { 170 panic("ppc_exception_entry(): external interrupt although we " 171 "don't have a PIC driver!"); 172 break; 173 } 174 175 dprintf("handling I/O interrupts...\n"); 176 int irq; 177 while ((irq = sPIC->acknowledge_io_interrupt(sPICCookie)) >= 0) { 178 // TODO: correctly pass level-triggered vs. edge-triggered to the handler! 179 int_io_interrupt_handler(irq, true); 180 } 181 dprintf("handling I/O interrupts done\n"); 182 break; 183 } 184 185 case 0x600: // alignment exception 186 panic("alignment exception: unimplemented\n"); 187 break; 188 case 0x700: // program exception 189 panic("program exception: unimplemented\n"); 190 break; 191 case 0x800: // FP unavailable exception 192 panic("FP unavailable exception: unimplemented\n"); 193 break; 194 case 0x900: // decrementer exception 195 timer_interrupt(); 196 break; 197 case 0xc00: // system call 198 panic("system call exception: unimplemented\n"); 199 break; 200 case 0xd00: // trace exception 201 panic("trace exception: unimplemented\n"); 202 break; 203 case 0xe00: // FP assist exception 204 panic("FP assist exception: unimplemented\n"); 205 break; 206 case 0xf00: // performance monitor exception 207 panic("performance monitor exception: unimplemented\n"); 208 break; 209 case 0xf20: // altivec unavailable exception 210 panic("alitivec unavailable exception: unimplemented\n"); 211 break; 212 case 0x1000: 213 case 0x1100: 214 case 0x1200: 215 panic("TLB miss exception: unimplemented\n"); 216 break; 217 case 0x1300: // instruction address exception 218 panic("instruction address exception: unimplemented\n"); 219 break; 220 case 0x1400: // system management exception 221 panic("system management exception: unimplemented\n"); 222 break; 223 case 0x1600: // altivec assist exception 224 panic("altivec assist exception: unimplemented\n"); 225 break; 226 case 0x1700: // thermal management exception 227 panic("thermal management exception: unimplemented\n"); 228 break; 229 default: 230 dprintf("unhandled exception type 0x%x\n", vector); 231 print_iframe(iframe); 232 panic("unhandled exception type\n"); 233 } 234 235 cpu_status state = disable_interrupts(); 236 if (thread->cpu->invoke_scheduler) { 237 GRAB_THREAD_LOCK(); 238 scheduler_reschedule(); 239 RELEASE_THREAD_LOCK(); 240 restore_interrupts(state); 241 } else if (thread->post_interrupt_callback != NULL) { 242 restore_interrupts(state); 243 void (*callback)(void*) = thread->post_interrupt_callback; 244 void* data = thread->post_interrupt_data; 245 246 thread->post_interrupt_callback = NULL; 247 thread->post_interrupt_data = NULL; 248 249 callback(data); 250 } 251 252 // pop iframe 253 if (thread) 254 ppc_pop_iframe(&thread->arch_info.iframes); 255 else 256 ppc_pop_iframe(&gBootFrameStack); 257 } 258 259 260 status_t 261 arch_int_init(kernel_args *args) 262 { 263 return B_OK; 264 } 265 266 267 status_t 268 arch_int_init_post_vm(kernel_args *args) 269 { 270 void *handlers = (void *)args->arch_args.exception_handlers.start; 271 272 // We may need to remap the exception handler area into the kernel address 273 // space. 274 if (!IS_KERNEL_ADDRESS(handlers)) { 275 addr_t address = (addr_t)handlers; 276 status_t error = ppc_remap_address_range(&address, 277 args->arch_args.exception_handlers.size, true); 278 if (error != B_OK) { 279 panic("arch_int_init_post_vm(): Failed to remap the exception " 280 "handler area!"); 281 return error; 282 } 283 handlers = (void*)(address); 284 } 285 286 // create a region to map the irq vector code into (physical address 0x0) 287 area_id exceptionArea = create_area("exception_handlers", 288 &handlers, B_EXACT_ADDRESS, args->arch_args.exception_handlers.size, 289 B_ALREADY_WIRED, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA); 290 if (exceptionArea < B_OK) 291 panic("arch_int_init2: could not create exception handler region\n"); 292 293 dprintf("exception handlers at %p\n", handlers); 294 295 // copy the handlers into this area 296 memcpy(handlers, &__irqvec_start, args->arch_args.exception_handlers.size); 297 arch_cpu_sync_icache(handlers, args->arch_args.exception_handlers.size); 298 299 // init the CPU exception contexts 300 int cpuCount = smp_get_num_cpus(); 301 for (int i = 0; i < cpuCount; i++) { 302 ppc_cpu_exception_context *context = ppc_get_cpu_exception_context(i); 303 context->kernel_handle_exception = (void*)&ppc_exception_tail; 304 context->exception_context = context; 305 // kernel_stack is set when the current thread changes. At this point 306 // we don't have threads yet. 307 } 308 309 // set the exception context for this CPU 310 ppc_set_current_cpu_exception_context(ppc_get_cpu_exception_context(0)); 311 312 return B_OK; 313 } 314 315 316 template<typename ModuleInfo> 317 struct Module : DoublyLinkedListLinkImpl<Module<ModuleInfo> > { 318 Module(ModuleInfo *module) 319 : module(module) 320 { 321 } 322 323 ~Module() 324 { 325 if (module) 326 put_module(((module_info*)module)->name); 327 } 328 329 ModuleInfo *module; 330 }; 331 332 typedef Module<interrupt_controller_module_info> PICModule; 333 334 struct PICModuleList : DoublyLinkedList<PICModule> { 335 ~PICModuleList() 336 { 337 while (PICModule *module = First()) { 338 Remove(module); 339 delete module; 340 } 341 } 342 }; 343 344 345 class DeviceTreeIterator { 346 public: 347 DeviceTreeIterator(device_manager_info *deviceManager) 348 : fDeviceManager(deviceManager), 349 fNode(NULL), 350 fParent(NULL) 351 { 352 Rewind(); 353 } 354 355 ~DeviceTreeIterator() 356 { 357 if (fParent != NULL) 358 fDeviceManager->put_node(fParent); 359 if (fNode != NULL) 360 fDeviceManager->put_node(fNode); 361 } 362 363 void Rewind() 364 { 365 fNode = fDeviceManager->get_root_node(); 366 } 367 368 bool HasNext() const 369 { 370 return (fNode != NULL); 371 } 372 373 device_node *Next() 374 { 375 if (fNode == NULL) 376 return NULL; 377 378 device_node *foundNode = fNode; 379 380 // get first child 381 device_node *child = NULL; 382 if (fDeviceManager->get_next_child_node(fNode, NULL, &child) 383 == B_OK) { 384 // move to the child node 385 if (fParent != NULL) 386 fDeviceManager->put_node(fParent); 387 fParent = fNode; 388 fNode = child; 389 390 // no more children; backtrack to find the next sibling 391 } else { 392 while (fParent != NULL) { 393 if (fDeviceManager->get_next_child_node(fParent, NULL, &fNode) 394 == B_OK) { 395 // get_next_child_node() always puts the node 396 break; 397 } 398 fNode = fParent; 399 fParent = fDeviceManager->get_parent_node(fNode); 400 } 401 402 // if we hit the root node again, we're done 403 if (fParent == NULL) { 404 fDeviceManager->put_node(fNode); 405 fNode = NULL; 406 } 407 } 408 409 return foundNode; 410 } 411 412 private: 413 device_manager_info *fDeviceManager; 414 device_node *fNode; 415 device_node *fParent; 416 }; 417 418 419 static void 420 get_interrupt_controller_modules(PICModuleList &list) 421 { 422 const char *namePrefix = "interrupt_controllers/"; 423 size_t namePrefixLen = strlen(namePrefix); 424 425 char name[B_PATH_NAME_LENGTH]; 426 size_t length; 427 uint32 cookie = 0; 428 while (get_next_loaded_module_name(&cookie, name, &(length = sizeof(name))) 429 == B_OK) { 430 // an interrupt controller module? 431 if (length <= namePrefixLen 432 || strncmp(name, namePrefix, namePrefixLen) != 0) { 433 continue; 434 } 435 436 // get the module 437 interrupt_controller_module_info *moduleInfo; 438 if (get_module(name, (module_info**)&moduleInfo) != B_OK) 439 continue; 440 441 // add it to the list 442 PICModule *module = new(nothrow) PICModule(moduleInfo); 443 if (!module) { 444 put_module(((module_info*)moduleInfo)->name); 445 continue; 446 } 447 list.Add(module); 448 } 449 } 450 451 452 static bool 453 probe_pic_device(device_node *node, PICModuleList &picModules) 454 { 455 for (PICModule *module = picModules.Head(); 456 module; 457 module = picModules.GetNext(module)) { 458 //bool noConnection; 459 if (module->module->info.supports_device(node) > 0) { 460 if (module->module->info.register_device(node) == B_OK) 461 return true; 462 } 463 } 464 465 return false; 466 } 467 468 469 status_t 470 arch_int_init_post_device_manager(struct kernel_args *args) 471 { 472 // get the interrupt controller driver modules 473 PICModuleList picModules; 474 get_interrupt_controller_modules(picModules); 475 if (picModules.IsEmpty()) { 476 panic("arch_int_init_post_device_manager(): Found no PIC modules!"); 477 return B_ENTRY_NOT_FOUND; 478 } 479 480 // get the device manager module 481 device_manager_info *deviceManager; 482 status_t error = get_module(B_DEVICE_MANAGER_MODULE_NAME, 483 (module_info**)&deviceManager); 484 if (error != B_OK) { 485 panic("arch_int_init_post_device_manager(): Failed to get device " 486 "manager: %s", strerror(error)); 487 return error; 488 } 489 Module<device_manager_info> _deviceManager(deviceManager); // auto put 490 491 // iterate through the device tree and probe the interrupt controllers 492 DeviceTreeIterator iterator(deviceManager); 493 while (device_node *node = iterator.Next()) 494 probe_pic_device(node, picModules); 495 496 // iterate through the tree again and get an interrupt controller node 497 iterator.Rewind(); 498 while (device_node *node = iterator.Next()) { 499 const char *deviceType; 500 if (deviceManager->get_attr_string(node, B_DEVICE_TYPE, 501 &deviceType, false) == B_OK) { 502 bool isPIC = false; 503 504 /* 505 bool isPIC 506 = (strcmp(deviceType, B_INTERRUPT_CONTROLLER_DRIVER_TYPE) == 0); 507 free(deviceType); 508 */ 509 510 if (isPIC) { 511 driver_module_info *driver; 512 void *driverCookie; 513 514 deviceManager->get_driver(node, (driver_module_info **)&driver, (void **)&driverCookie); 515 516 sPIC = (interrupt_controller_module_info *)driver; 517 sPICCookie = driverCookie; 518 return B_OK; 519 } 520 } 521 } 522 523 // no PIC found 524 panic("arch_int_init_post_device_manager(): Found no supported PIC!"); 525 526 return B_ENTRY_NOT_FOUND; 527 } 528 529 530 // #pragma mark - 531 532 struct ppc_cpu_exception_context * 533 ppc_get_cpu_exception_context(int cpu) 534 { 535 return sCPUExceptionContexts + cpu; 536 } 537 538 539 void 540 ppc_set_current_cpu_exception_context(struct ppc_cpu_exception_context *context) 541 { 542 // translate to physical address 543 addr_t physicalPage; 544 addr_t inPageOffset = (addr_t)context & (B_PAGE_SIZE - 1); 545 status_t error = vm_get_page_mapping(VMAddressSpace::KernelID(), 546 (addr_t)context - inPageOffset, &physicalPage); 547 if (error != B_OK) { 548 panic("ppc_set_current_cpu_exception_context(): Failed to get physical " 549 "address!"); 550 return; 551 } 552 553 asm volatile("mtsprg0 %0" : : "r"(physicalPage + inPageOffset)); 554 } 555 556