xref: /haiku/src/system/kernel/arch/ppc/arch_int.cpp (revision 4241437c836d96957da142f0263cb7f6a4056b01)
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