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