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