xref: /haiku/src/system/kernel/arch/m68k/arch_int.cpp (revision 1214ef1b2100f2b3299fc9d8d6142e46f70a4c3f)
1 /*
2  * Copyright 2003-2006, 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  * 		François Revol <revol@free.fr>
9  * Distributed under the terms of the MIT License.
10  *
11  *
12  * Copyright 2001, Travis Geiselbrecht. All rights reserved.
13  * Distributed under the terms of the NewOS License.
14  */
15 
16 #include <int.h>
17 
18 #include <arch/smp.h>
19 #include <boot/kernel_args.h>
20 #include <device_manager.h>
21 #include <kscheduler.h>
22 #include <interrupt_controller.h>
23 #include <smp.h>
24 #include <thread.h>
25 #include <timer.h>
26 #include <util/DoublyLinkedList.h>
27 #include <util/kernel_cpp.h>
28 #include <vm.h>
29 #include <vm_address_space.h>
30 #include <vm_priv.h>
31 
32 #include <string.h>
33 #warning M68K: writeme!
34 
35 // defined in arch_exceptions.S
36 extern int __irqvec_start;
37 extern int __irqvec_end;
38 
39 extern"C" void m68k_exception_tail(void);
40 
41 
42 // the exception contexts for all CPUs
43 static m68k_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("   d0 0x%08lx    d1 0x%08lx    d2 0x%08lx    d3 0x%08lx\n",
85 				frame->d0, frame->d1, frame->d2, frame->d3);
86 			kprintf("   d4 0x%08lx    d5 0x%08lx    d6 0x%08lx    d7 0x%08lx\n",
87 				frame->d4, frame->d5, frame->d6, frame->d7);
88 			kprintf("   a0 0x%08lx    a1 0x%08lx    a2 0x%08lx    a3 0x%08lx\n",
89 				frame->a0, frame->a1, frame->a2, frame->a3);
90 			kprintf("   a4 0x%08lx    a5 0x%08lx    a6 0x%08lx    a7 0x%08lx (sp)\n",
91 				frame->d4, frame->d5, frame->d6, frame->d7);
92 
93 			/*kprintf("   pc 0x%08lx   ccr 0x%02x\n",
94 			  frame->pc, frame->ccr);*/
95 			kprintf("   pc 0x%08lx        sr 0x%04x\n",
96 				frame->pc, frame->sr);
97 	dprintf("r0-r3:   0x%08lx 0x%08lx 0x%08lx 0x%08lx\n", frame->d0, frame->d1, frame->d2, frame->d3);
98 	dprintf("r4-r7:   0x%08lx 0x%08lx 0x%08lx 0x%08lx\n", frame->d4, frame->d5, frame->d6, frame->d7);
99 	dprintf("r8-r11:  0x%08lx 0x%08lx 0x%08lx 0x%08lx\n", frame->a0, frame->a1, frame->a2, frame->a3);
100 	dprintf("r12-r15: 0x%08lx 0x%08lx 0x%08lx 0x%08lx\n", frame->a4, frame->a5, frame->a6, frame->a7);
101 	dprintf("      pc 0x%08lx         sr 0x%08lx\n", frame->pc, frame->sr);
102 }
103 
104 
105 extern "C" void m68k_exception_entry(int vector, struct iframe *iframe);
106 void
107 m68k_exception_entry(int vector, struct iframe *iframe)
108 {
109 	int ret = B_HANDLED_INTERRUPT;
110 
111 	if (vector != 0x900) {
112 		dprintf("m68k_exception_entry: time %lld vector 0x%x, iframe %p, "
113 			"srr0: %p\n", system_time(), vector, iframe, (void*)iframe->srr0);
114 	}
115 
116 	struct thread *thread = thread_get_current_thread();
117 
118 	// push iframe
119 	if (thread)
120 		m68k_push_iframe(&thread->arch_info.iframes, iframe);
121 	else
122 		m68k_push_iframe(&gBootFrameStack, iframe);
123 
124 	switch (vector) {
125 		case 0x100: // system reset
126 			panic("system reset exception\n");
127 			break;
128 		case 0x200: // machine check
129 			panic("machine check exception\n");
130 			break;
131 		case 0x300: // DSI
132 		case 0x400: // ISI
133 		{
134 			bool kernelDebugger = debug_debugger_running();
135 
136 			if (kernelDebugger) {
137 				// if this thread has a fault handler, we're allowed to be here
138 				struct thread *thread = thread_get_current_thread();
139 				if (thread && thread->fault_handler != NULL) {
140 					iframe->srr0 = 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 			ret = vm_page_fault(iframe->dar, iframe->srr0,
168 				iframe->dsisr & (1 << 25), // store or load
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("m68k_exception_entry(): external interrupt although we "
183 					"don't have a PIC driver!");
184 				ret = B_HANDLED_INTERRUPT;
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 				ret = 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 			ret = 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 	if (ret == B_INVOKE_SCHEDULER) {
249 		int state = disable_interrupts();
250 		GRAB_THREAD_LOCK();
251 		scheduler_reschedule();
252 		RELEASE_THREAD_LOCK();
253 		restore_interrupts(state);
254 	}
255 
256 	// pop iframe
257 	if (thread)
258 		m68k_pop_iframe(&thread->arch_info.iframes);
259 	else
260 		m68k_pop_iframe(&gBootFrameStack);
261 }
262 
263 
264 status_t
265 arch_int_init(kernel_args *args)
266 {
267 	return B_OK;
268 }
269 
270 
271 status_t
272 arch_int_init_post_vm(kernel_args *args)
273 {
274 	void *handlers = (void *)args->arch_args.exception_handlers.start;
275 
276 	// We may need to remap the exception handler area into the kernel address
277 	// space.
278 	if (!IS_KERNEL_ADDRESS(handlers)) {
279 		addr_t address = (addr_t)handlers;
280 		status_t error = m68k_remap_address_range(&address,
281 			args->arch_args.exception_handlers.size, true);
282 		if (error != B_OK) {
283 			panic("arch_int_init_post_vm(): Failed to remap the exception "
284 				"handler area!");
285 			return error;
286 		}
287 		handlers = (void*)(address);
288 	}
289 
290 	// create a region to map the irq vector code into (physical address 0x0)
291 	area_id exceptionArea = create_area("exception_handlers",
292 		&handlers, B_EXACT_ADDRESS, args->arch_args.exception_handlers.size,
293 		B_ALREADY_WIRED, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
294 	if (exceptionArea < B_OK)
295 		panic("arch_int_init2: could not create exception handler region\n");
296 
297 	dprintf("exception handlers at %p\n", handlers);
298 
299 	// copy the handlers into this area
300 	memcpy(handlers, &__irqvec_start, args->arch_args.exception_handlers.size);
301 	arch_cpu_sync_icache(handlers, args->arch_args.exception_handlers.size);
302 
303 	// init the CPU exception contexts
304 	int cpuCount = smp_get_num_cpus();
305 	for (int i = 0; i < cpuCount; i++) {
306 		m68k_cpu_exception_context *context = m68k_get_cpu_exception_context(i);
307 		context->kernel_handle_exception = (void*)&m68k_exception_tail;
308 		context->exception_context = context;
309 		// kernel_stack is set when the current thread changes. At this point
310 		// we don't have threads yet.
311 	}
312 
313 	// set the exception context for this CPU
314 	m68k_set_current_cpu_exception_context(m68k_get_cpu_exception_context(0));
315 
316 	return B_OK;
317 }
318 
319 
320 template<typename ModuleInfo>
321 struct Module : DoublyLinkedListLinkImpl<Module<ModuleInfo> > {
322 	Module(ModuleInfo *module)
323 		: module(module)
324 	{
325 	}
326 
327 	~Module()
328 	{
329 		if (module)
330 			put_module(((module_info*)module)->name);
331 	}
332 
333 	ModuleInfo	*module;
334 };
335 
336 typedef Module<interrupt_controller_module_info> PICModule;
337 
338 struct PICModuleList : DoublyLinkedList<PICModule> {
339 	~PICModuleList()
340 	{
341 		while (PICModule *module = First()) {
342 			Remove(module);
343 			delete module;
344 		}
345 	}
346 };
347 
348 
349 class DeviceTreeIterator {
350 public:
351 	DeviceTreeIterator(device_manager_info *deviceManager)
352 		: fDeviceManager(deviceManager),
353 		  fNode(NULL),
354 		  fParent(NULL)
355 	{
356 		Rewind();
357 	}
358 
359 	~DeviceTreeIterator()
360 	{
361 		if (fParent != NULL)
362 			fDeviceManager->put_device_node(fParent);
363 		if (fNode != NULL)
364 			fDeviceManager->put_device_node(fNode);
365 	}
366 
367 	void Rewind()
368 	{
369 		fNode = fDeviceManager->get_root();
370 	}
371 
372 	bool HasNext() const
373 	{
374 		return (fNode != NULL);
375 	}
376 
377 	device_node_handle Next()
378 	{
379 		if (fNode == NULL)
380 			return NULL;
381 
382 		device_node_handle foundNode = fNode;
383 
384 		// get first child
385 		device_node_handle child = NULL;
386 		if (fDeviceManager->get_next_child_device(fNode, &child, NULL)
387 				== B_OK) {
388 			// move to the child node
389 			if (fParent != NULL)
390 				fDeviceManager->put_device_node(fParent);
391 			fParent = fNode;
392 			fNode = child;
393 
394 		// no more children; backtrack to find the next sibling
395 		} else {
396 			while (fParent != NULL) {
397 				if (fDeviceManager->get_next_child_device(fParent, &fNode, NULL)
398 						== B_OK) {
399 						// get_next_child_device() always puts the node
400 					break;
401 				}
402 				fNode = fParent;
403 				fParent = fDeviceManager->get_parent(fNode);
404 			}
405 
406 			// if we hit the root node again, we're done
407 			if (fParent == NULL) {
408 				fDeviceManager->put_device_node(fNode);
409 				fNode = NULL;
410 			}
411 		}
412 
413 		return foundNode;
414 	}
415 
416 private:
417 	device_manager_info *fDeviceManager;
418 	device_node_handle	fNode;
419 	device_node_handle	fParent;
420 };
421 
422 
423 static void
424 get_interrupt_controller_modules(PICModuleList &list)
425 {
426 	const char *namePrefix = "interrupt_controllers/";
427 	size_t namePrefixLen = strlen(namePrefix);
428 
429 	char name[B_PATH_NAME_LENGTH];
430 	size_t length;
431 	uint32 cookie = 0;
432 	while (get_next_loaded_module_name(&cookie, name, &(length = sizeof(name)))
433 			== B_OK) {
434 		// an interrupt controller module?
435 		if (length <= namePrefixLen
436 			|| strncmp(name, namePrefix, namePrefixLen) != 0) {
437 			continue;
438 		}
439 
440 		// get the module
441 		interrupt_controller_module_info *moduleInfo;
442 		if (get_module(name, (module_info**)&moduleInfo) != B_OK)
443 			continue;
444 
445 		// add it to the list
446 		PICModule *module = new(nothrow) PICModule(moduleInfo);
447 		if (!module) {
448 			put_module(((module_info*)moduleInfo)->name);
449 			continue;
450 		}
451 		list.Add(module);
452 	}
453 }
454 
455 
456 static bool
457 probe_pic_device(device_node_handle node, PICModuleList &picModules)
458 {
459 	for (PICModule *module = picModules.Head();
460 		 module;
461 		 module = picModules.GetNext(module)) {
462 		bool noConnection;
463 		if (module->module->info.supports_device(node, &noConnection) > 0) {
464 			if (module->module->info.register_device(node) == B_OK)
465 				return true;
466 		}
467 	}
468 
469 	return false;
470 }
471 
472 
473 status_t
474 arch_int_init_post_device_manager(struct kernel_args *args)
475 {
476 	// get the interrupt controller driver modules
477 	PICModuleList picModules;
478 	get_interrupt_controller_modules(picModules);
479 	if (picModules.IsEmpty()) {
480 		panic("arch_int_init_post_device_manager(): Found no PIC modules!");
481 		return B_ENTRY_NOT_FOUND;
482 	}
483 
484 	// get the device manager module
485 	device_manager_info *deviceManager;
486 	status_t error = get_module(B_DEVICE_MANAGER_MODULE_NAME,
487 		(module_info**)&deviceManager);
488 	if (error != B_OK) {
489 		panic("arch_int_init_post_device_manager(): Failed to get device "
490 			"manager: %s", strerror(error));
491 		return error;
492 	}
493 	Module<device_manager_info> _deviceManager(deviceManager);	// auto put
494 
495 	// iterate through the device tree and probe the interrupt controllers
496 	DeviceTreeIterator iterator(deviceManager);
497 	while (device_node_handle node = iterator.Next())
498 		probe_pic_device(node, picModules);
499 
500 	// iterate through the tree again and get an interrupt controller node
501 	iterator.Rewind();
502 	while (device_node_handle node = iterator.Next()) {
503 		char *deviceType;
504 		if (deviceManager->get_attr_string(node, B_DRIVER_DEVICE_TYPE,
505 				&deviceType, false) == B_OK) {
506 			bool isPIC
507 				= (strcmp(deviceType, B_INTERRUPT_CONTROLLER_DRIVER_TYPE) == 0);
508 			free(deviceType);
509 
510 			if (isPIC) {
511 				driver_module_info *driver;
512 				void *driverCookie;
513 				error = deviceManager->init_driver(node, NULL, &driver,
514 					&driverCookie);
515 				if (error == B_OK) {
516 					sPIC = (interrupt_controller_module_info *)driver;
517 					sPICCookie = driverCookie;
518 					return B_OK;
519 				}
520 			}
521 		}
522 	}
523 
524 	// no PIC found
525 	panic("arch_int_init_post_device_manager(): Found no supported PIC!");
526 
527 	return B_ENTRY_NOT_FOUND;
528 }
529 
530 
531 // #pragma mark -
532 
533 struct m68k_cpu_exception_context *
534 m68k_get_cpu_exception_context(int cpu)
535 {
536 	return sCPUExceptionContexts + cpu;
537 }
538 
539 
540 void
541 m68k_set_current_cpu_exception_context(struct m68k_cpu_exception_context *context)
542 {
543 	// translate to physical address
544 	addr_t physicalPage;
545 	addr_t inPageOffset = (addr_t)context & (B_PAGE_SIZE - 1);
546 	status_t error = vm_get_page_mapping(vm_kernel_address_space_id(),
547 		(addr_t)context - inPageOffset, &physicalPage);
548 	if (error != B_OK) {
549 		panic("m68k_set_current_cpu_exception_context(): Failed to get physical "
550 			"address!");
551 		return;
552 	}
553 
554 	asm volatile("mtsprg0 %0" : : "r"(physicalPage + inPageOffset));
555 }
556 
557