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