xref: /haiku/src/system/kernel/arch/m68k/arch_int.cpp (revision 89755088d790ff4fe36f8aa77dacb2bd15507108)
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 // 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("   d0 0x%08lx    d1 0x%08lx    d2 0x%08lx    d3 0x%08lx\n",
81 				frame->d[0], frame->d[1], frame->d[2], frame->d[3]);
82 			kprintf("   d4 0x%08lx    d5 0x%08lx    d6 0x%08lx    d7 0x%08lx\n",
83 				frame->d[4], frame->d[5], frame->d[6], frame->d[7]);
84 			kprintf("   a0 0x%08lx    a1 0x%08lx    a2 0x%08lx    a3 0x%08lx\n",
85 				frame->a[0], frame->a[1], frame->a[2], frame->a[3]);
86 			kprintf("   a4 0x%08lx    a5 0x%08lx    a6 0x%08lx    "/*"a7 0x%08lx (sp)"*/"\n",
87 				frame->a[4], frame->a[5], frame->a[6]/*, frame->a[7]*/);
88 
89 			/*kprintf("   pc 0x%08lx   ccr 0x%02x\n",
90 			  frame->pc, frame->ccr);*/
91 			kprintf("   pc 0x%08lx        sr 0x%04x\n",
92 				frame->pc, frame->sr);
93 #if 0
94 	dprintf("r0-r3:   0x%08lx 0x%08lx 0x%08lx 0x%08lx\n", frame->d0, frame->d1, frame->d2, frame->d3);
95 	dprintf("r4-r7:   0x%08lx 0x%08lx 0x%08lx 0x%08lx\n", frame->d4, frame->d5, frame->d6, frame->d7);
96 	dprintf("r8-r11:  0x%08lx 0x%08lx 0x%08lx 0x%08lx\n", frame->a0, frame->a1, frame->a2, frame->a3);
97 	dprintf("r12-r15: 0x%08lx 0x%08lx 0x%08lx 0x%08lx\n", frame->a4, frame->a5, frame->a6, frame->a7);
98 	dprintf("      pc 0x%08lx         sr 0x%08lx\n", frame->pc, frame->sr);
99 #endif
100 }
101 
102 
103 extern "C" void m68k_exception_entry(int vector, struct iframe *iframe);
104 void
105 m68k_exception_entry(int vector, struct iframe *iframe)
106 {
107 	int ret = B_HANDLED_INTERRUPT;
108 
109 	if (vector != 0x900) {
110 		dprintf("m68k_exception_entry: time %lld vector 0x%x, iframe %p, "
111 			"srr0: %p\n", system_time(), vector, iframe, (void*)iframe->srr0);
112 	}
113 
114 	struct thread *thread = thread_get_current_thread();
115 
116 	// push iframe
117 	if (thread)
118 		m68k_push_iframe(&thread->arch_info.iframes, iframe);
119 	else
120 		m68k_push_iframe(&gBootFrameStack, iframe);
121 
122 	switch (vector) {
123 		case 0x100: // system reset
124 			panic("system reset exception\n");
125 			break;
126 		case 0x200: // machine check
127 			panic("machine check exception\n");
128 			break;
129 		case 0x300: // DSI
130 		case 0x400: // ISI
131 		{
132 			bool kernelDebugger = debug_debugger_running();
133 
134 			if (kernelDebugger) {
135 				// if this thread has a fault handler, we're allowed to be here
136 				struct thread *thread = thread_get_current_thread();
137 				if (thread && thread->fault_handler != NULL) {
138 					iframe->srr0 = thread->fault_handler;
139 					break;
140 				}
141 
142 				// otherwise, not really
143 				panic("page fault in debugger without fault handler! Touching "
144 					"address %p from ip %p\n", (void *)iframe->dar,
145 					(void *)iframe->srr0);
146 				break;
147 			} else if ((iframe->srr1 & MSR_EXCEPTIONS_ENABLED) == 0) {
148 				// if the interrupts were disabled, and we are not running the
149 				// kernel startup the page fault was not allowed to happen and
150 				// we must panic
151 				panic("page fault, but interrupts were disabled. Touching "
152 					"address %p from ip %p\n", (void *)iframe->dar,
153 					(void *)iframe->srr0);
154 				break;
155 			} else if (thread != NULL && thread->page_faults_allowed < 1) {
156 				panic("page fault not allowed at this place. Touching address "
157 					"%p from ip %p\n", (void *)iframe->dar,
158 					(void *)iframe->srr0);
159 			}
160 
161 			enable_interrupts();
162 
163 			addr_t newip;
164 
165 			ret = vm_page_fault(iframe->dar, iframe->srr0,
166 				iframe->dsisr & (1 << 25), // store or load
167 				iframe->srr1 & (1 << 14), // was the system in user or supervisor
168 				&newip);
169 			if (newip != 0) {
170 				// the page fault handler wants us to modify the iframe to set the
171 				// IP the cpu will return to to be this ip
172 				iframe->srr0 = newip;
173 			}
174  			break;
175 		}
176 
177 		case 0x500: // external interrupt
178 		{
179 			if (!sPIC) {
180 				panic("m68k_exception_entry(): external interrupt although we "
181 					"don't have a PIC driver!");
182 				ret = B_HANDLED_INTERRUPT;
183 				break;
184 			}
185 
186 dprintf("handling I/O interrupts...\n");
187 			int irq;
188 			while ((irq = sPIC->acknowledge_io_interrupt(sPICCookie)) >= 0) {
189 // TODO: correctly pass level-triggered vs. edge-triggered to the handler!
190 				ret = int_io_interrupt_handler(irq, true);
191 			}
192 dprintf("handling I/O interrupts done\n");
193 			break;
194 		}
195 
196 		case 0x600: // alignment exception
197 			panic("alignment exception: unimplemented\n");
198 			break;
199 		case 0x700: // program exception
200 			panic("program exception: unimplemented\n");
201 			break;
202 		case 0x800: // FP unavailable exception
203 			panic("FP unavailable exception: unimplemented\n");
204 			break;
205 		case 0x900: // decrementer exception
206 			ret = timer_interrupt();
207 			break;
208 		case 0xc00: // system call
209 			panic("system call exception: unimplemented\n");
210 			break;
211 		case 0xd00: // trace exception
212 			panic("trace exception: unimplemented\n");
213 			break;
214 		case 0xe00: // FP assist exception
215 			panic("FP assist exception: unimplemented\n");
216 			break;
217 		case 0xf00: // performance monitor exception
218 			panic("performance monitor exception: unimplemented\n");
219 			break;
220 		case 0xf20: // altivec unavailable exception
221 			panic("alitivec unavailable exception: unimplemented\n");
222 			break;
223 		case 0x1000:
224 		case 0x1100:
225 		case 0x1200:
226 			panic("TLB miss exception: unimplemented\n");
227 			break;
228 		case 0x1300: // instruction address exception
229 			panic("instruction address exception: unimplemented\n");
230 			break;
231 		case 0x1400: // system management exception
232 			panic("system management exception: unimplemented\n");
233 			break;
234 		case 0x1600: // altivec assist exception
235 			panic("altivec assist exception: unimplemented\n");
236 			break;
237 		case 0x1700: // thermal management exception
238 			panic("thermal management exception: unimplemented\n");
239 			break;
240 		default:
241 			dprintf("unhandled exception type 0x%x\n", vector);
242 			print_iframe(iframe);
243 			panic("unhandled exception type\n");
244 	}
245 
246 	if (ret == B_INVOKE_SCHEDULER) {
247 		int state = disable_interrupts();
248 		GRAB_THREAD_LOCK();
249 		scheduler_reschedule();
250 		RELEASE_THREAD_LOCK();
251 		restore_interrupts(state);
252 	}
253 
254 	// pop iframe
255 	if (thread)
256 		m68k_pop_iframe(&thread->arch_info.iframes);
257 	else
258 		m68k_pop_iframe(&gBootFrameStack);
259 }
260 
261 
262 status_t
263 arch_int_init(kernel_args *args)
264 {
265 	return B_OK;
266 }
267 
268 
269 status_t
270 arch_int_init_post_vm(kernel_args *args)
271 {
272 	void *handlers = (void *)args->arch_args.exception_handlers.start;
273 
274 	// We may need to remap the exception handler area into the kernel address
275 	// space.
276 	if (!IS_KERNEL_ADDRESS(handlers)) {
277 		addr_t address = (addr_t)handlers;
278 		status_t error = m68k_remap_address_range(&address,
279 			args->arch_args.exception_handlers.size, true);
280 		if (error != B_OK) {
281 			panic("arch_int_init_post_vm(): Failed to remap the exception "
282 				"handler area!");
283 			return error;
284 		}
285 		handlers = (void*)(address);
286 	}
287 
288 	// create a region to map the irq vector code into (physical address 0x0)
289 	area_id exceptionArea = create_area("exception_handlers",
290 		&handlers, B_EXACT_ADDRESS, args->arch_args.exception_handlers.size,
291 		B_ALREADY_WIRED, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
292 	if (exceptionArea < B_OK)
293 		panic("arch_int_init2: could not create exception handler region\n");
294 
295 	dprintf("exception handlers at %p\n", handlers);
296 
297 	// copy the handlers into this area
298 	memcpy(handlers, &__irqvec_start, args->arch_args.exception_handlers.size);
299 	arch_cpu_sync_icache(handlers, args->arch_args.exception_handlers.size);
300 
301 	// init the CPU exception contexts
302 	int cpuCount = smp_get_num_cpus();
303 	for (int i = 0; i < cpuCount; i++) {
304 		m68k_cpu_exception_context *context = m68k_get_cpu_exception_context(i);
305 		context->kernel_handle_exception = (void*)&m68k_exception_tail;
306 		context->exception_context = context;
307 		// kernel_stack is set when the current thread changes. At this point
308 		// we don't have threads yet.
309 	}
310 
311 	// set the exception context for this CPU
312 	m68k_set_current_cpu_exception_context(m68k_get_cpu_exception_context(0));
313 
314 	return B_OK;
315 }
316 
317 
318 template<typename ModuleInfo>
319 struct Module : DoublyLinkedListLinkImpl<Module<ModuleInfo> > {
320 	Module(ModuleInfo *module)
321 		: module(module)
322 	{
323 	}
324 
325 	~Module()
326 	{
327 		if (module)
328 			put_module(((module_info*)module)->name);
329 	}
330 
331 	ModuleInfo	*module;
332 };
333 
334 typedef Module<interrupt_controller_module_info> PICModule;
335 
336 struct PICModuleList : DoublyLinkedList<PICModule> {
337 	~PICModuleList()
338 	{
339 		while (PICModule *module = First()) {
340 			Remove(module);
341 			delete module;
342 		}
343 	}
344 };
345 
346 
347 class DeviceTreeIterator {
348 public:
349 	DeviceTreeIterator(device_manager_info *deviceManager)
350 		: fDeviceManager(deviceManager),
351 		  fNode(NULL),
352 		  fParent(NULL)
353 	{
354 		Rewind();
355 	}
356 
357 	~DeviceTreeIterator()
358 	{
359 		if (fParent != NULL)
360 			fDeviceManager->put_device_node(fParent);
361 		if (fNode != NULL)
362 			fDeviceManager->put_device_node(fNode);
363 	}
364 
365 	void Rewind()
366 	{
367 		fNode = fDeviceManager->get_root();
368 	}
369 
370 	bool HasNext() const
371 	{
372 		return (fNode != NULL);
373 	}
374 
375 	device_node_handle Next()
376 	{
377 		if (fNode == NULL)
378 			return NULL;
379 
380 		device_node_handle foundNode = fNode;
381 
382 		// get first child
383 		device_node_handle child = NULL;
384 		if (fDeviceManager->get_next_child_device(fNode, &child, NULL)
385 				== B_OK) {
386 			// move to the child node
387 			if (fParent != NULL)
388 				fDeviceManager->put_device_node(fParent);
389 			fParent = fNode;
390 			fNode = child;
391 
392 		// no more children; backtrack to find the next sibling
393 		} else {
394 			while (fParent != NULL) {
395 				if (fDeviceManager->get_next_child_device(fParent, &fNode, NULL)
396 						== B_OK) {
397 						// get_next_child_device() always puts the node
398 					break;
399 				}
400 				fNode = fParent;
401 				fParent = fDeviceManager->get_parent(fNode);
402 			}
403 
404 			// if we hit the root node again, we're done
405 			if (fParent == NULL) {
406 				fDeviceManager->put_device_node(fNode);
407 				fNode = NULL;
408 			}
409 		}
410 
411 		return foundNode;
412 	}
413 
414 private:
415 	device_manager_info *fDeviceManager;
416 	device_node_handle	fNode;
417 	device_node_handle	fParent;
418 };
419 
420 
421 static void
422 get_interrupt_controller_modules(PICModuleList &list)
423 {
424 	const char *namePrefix = "interrupt_controllers/";
425 	size_t namePrefixLen = strlen(namePrefix);
426 
427 	char name[B_PATH_NAME_LENGTH];
428 	size_t length;
429 	uint32 cookie = 0;
430 	while (get_next_loaded_module_name(&cookie, name, &(length = sizeof(name)))
431 			== B_OK) {
432 		// an interrupt controller module?
433 		if (length <= namePrefixLen
434 			|| strncmp(name, namePrefix, namePrefixLen) != 0) {
435 			continue;
436 		}
437 
438 		// get the module
439 		interrupt_controller_module_info *moduleInfo;
440 		if (get_module(name, (module_info**)&moduleInfo) != B_OK)
441 			continue;
442 
443 		// add it to the list
444 		PICModule *module = new(nothrow) PICModule(moduleInfo);
445 		if (!module) {
446 			put_module(((module_info*)moduleInfo)->name);
447 			continue;
448 		}
449 		list.Add(module);
450 	}
451 }
452 
453 
454 static bool
455 probe_pic_device(device_node_handle node, PICModuleList &picModules)
456 {
457 	for (PICModule *module = picModules.Head();
458 		 module;
459 		 module = picModules.GetNext(module)) {
460 		bool noConnection;
461 		if (module->module->info.supports_device(node, &noConnection) > 0) {
462 			if (module->module->info.register_device(node) == B_OK)
463 				return true;
464 		}
465 	}
466 
467 	return false;
468 }
469 
470 
471 status_t
472 arch_int_init_post_device_manager(struct kernel_args *args)
473 {
474 	// get the interrupt controller driver modules
475 	PICModuleList picModules;
476 	get_interrupt_controller_modules(picModules);
477 	if (picModules.IsEmpty()) {
478 		panic("arch_int_init_post_device_manager(): Found no PIC modules!");
479 		return B_ENTRY_NOT_FOUND;
480 	}
481 
482 	// get the device manager module
483 	device_manager_info *deviceManager;
484 	status_t error = get_module(B_DEVICE_MANAGER_MODULE_NAME,
485 		(module_info**)&deviceManager);
486 	if (error != B_OK) {
487 		panic("arch_int_init_post_device_manager(): Failed to get device "
488 			"manager: %s", strerror(error));
489 		return error;
490 	}
491 	Module<device_manager_info> _deviceManager(deviceManager);	// auto put
492 
493 	// iterate through the device tree and probe the interrupt controllers
494 	DeviceTreeIterator iterator(deviceManager);
495 	while (device_node_handle node = iterator.Next())
496 		probe_pic_device(node, picModules);
497 
498 	// iterate through the tree again and get an interrupt controller node
499 	iterator.Rewind();
500 	while (device_node_handle node = iterator.Next()) {
501 		char *deviceType;
502 		if (deviceManager->get_attr_string(node, B_DRIVER_DEVICE_TYPE,
503 				&deviceType, false) == B_OK) {
504 			bool isPIC
505 				= (strcmp(deviceType, B_INTERRUPT_CONTROLLER_DRIVER_TYPE) == 0);
506 			free(deviceType);
507 
508 			if (isPIC) {
509 				driver_module_info *driver;
510 				void *driverCookie;
511 				error = deviceManager->init_driver(node, NULL, &driver,
512 					&driverCookie);
513 				if (error == B_OK) {
514 					sPIC = (interrupt_controller_module_info *)driver;
515 					sPICCookie = driverCookie;
516 					return B_OK;
517 				}
518 			}
519 		}
520 	}
521 
522 	// no PIC found
523 	panic("arch_int_init_post_device_manager(): Found no supported PIC!");
524 
525 	return B_ENTRY_NOT_FOUND;
526 }
527 
528 
529 // #pragma mark -
530 
531 struct m68k_cpu_exception_context *
532 m68k_get_cpu_exception_context(int cpu)
533 {
534 	return sCPUExceptionContexts + cpu;
535 }
536 
537 
538 void
539 m68k_set_current_cpu_exception_context(struct m68k_cpu_exception_context *context)
540 {
541 	// translate to physical address
542 	addr_t physicalPage;
543 	addr_t inPageOffset = (addr_t)context & (B_PAGE_SIZE - 1);
544 	status_t error = vm_get_page_mapping(vm_kernel_address_space_id(),
545 		(addr_t)context - inPageOffset, &physicalPage);
546 	if (error != B_OK) {
547 		panic("m68k_set_current_cpu_exception_context(): Failed to get physical "
548 			"address!");
549 		return;
550 	}
551 
552 	asm volatile("mtsprg0 %0" : : "r"(physicalPage + inPageOffset));
553 }
554 
555