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
arch_int_enable_io_interrupt(int32 irq)57 arch_int_enable_io_interrupt(int32 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
arch_int_disable_io_interrupt(int32 irq)68 arch_int_disable_io_interrupt(int32 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
print_iframe(struct iframe * frame)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
ppc_exception_entry(int vector,struct iframe * iframe)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->post_interrupt_callback != NULL) {
250 void (*callback)(void*) = thread->post_interrupt_callback;
251 void* data = thread->post_interrupt_data;
252
253 thread->post_interrupt_callback = NULL;
254 thread->post_interrupt_data = NULL;
255
256 restore_interrupts(state);
257
258 callback(data);
259 } else if (thread->cpu->invoke_scheduler) {
260 SpinLocker schedulerLocker(thread->scheduler_lock);
261 scheduler_reschedule(B_THREAD_READY);
262 schedulerLocker.Unlock();
263 restore_interrupts(state);
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
arch_int_init(kernel_args * args)275 arch_int_init(kernel_args *args)
276 {
277 return B_OK;
278 }
279
280
281 status_t
arch_int_init_post_vm(kernel_args * args)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
arch_int_init_io(kernel_args * args)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> > {
ModuleModule339 Module(ModuleInfo *module)
340 : module(module)
341 {
342 }
343
~ModuleModule344 ~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> {
~PICModuleListPICModuleList356 ~PICModuleList()
357 {
358 while (PICModule *module = First()) {
359 Remove(module);
360 delete module;
361 }
362 }
363 };
364
365
366 class DeviceTreeIterator {
367 public:
DeviceTreeIterator(device_manager_info * deviceManager)368 DeviceTreeIterator(device_manager_info *deviceManager)
369 : fDeviceManager(deviceManager),
370 fNode(NULL),
371 fParent(NULL)
372 {
373 Rewind();
374 }
375
~DeviceTreeIterator()376 ~DeviceTreeIterator()
377 {
378 if (fParent != NULL)
379 fDeviceManager->put_node(fParent);
380 if (fNode != NULL)
381 fDeviceManager->put_node(fNode);
382 }
383
Rewind()384 void Rewind()
385 {
386 fNode = fDeviceManager->get_root_node();
387 }
388
HasNext() const389 bool HasNext() const
390 {
391 return (fNode != NULL);
392 }
393
Next()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
get_interrupt_controller_modules(PICModuleList & list)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
probe_pic_device(device_node * node,PICModuleList & picModules)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
arch_int_init_post_device_manager(struct kernel_args * args)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 *
ppc_get_cpu_exception_context(int cpu)550 ppc_get_cpu_exception_context(int cpu)
551 {
552 return sCPUExceptionContexts + cpu;
553 }
554
555
556 void
ppc_set_current_cpu_exception_context(struct ppc_cpu_exception_context * context)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
arch_int_assign_to_cpu(int32 irq,int32 cpu)575 arch_int_assign_to_cpu(int32 irq, int32 cpu)
576 {
577 // Not yet supported.
578 return 0;
579 }
580