xref: /haiku/src/system/kernel/arch/m68k/arch_int.cpp (revision cbe0a0c436162d78cc3f92a305b64918c839d079)
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  * 		François Revol <revol@free.fr>
9  *
10  * Copyright 2001, Travis Geiselbrecht. All rights reserved.
11  * Distributed under the terms of the NewOS License.
12  */
13 
14 
15 #include <int.h>
16 
17 #include <arch_platform.h>
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/AutoLock.h>
27 #include <util/DoublyLinkedList.h>
28 #include <util/kernel_cpp.h>
29 #include <vm/vm.h>
30 #include <vm/vm_priv.h>
31 #include <vm/VMAddressSpace.h>
32 #include <string.h>
33 
34 #warning M68K: writeme!
35 
36 
37 //#define TRACE_ARCH_INT
38 #ifdef TRACE_ARCH_INT
39 #	define TRACE(x) dprintf x
40 #else
41 #	define TRACE(x) ;
42 #endif
43 
44 typedef void (*m68k_exception_handler)(void);
45 #define M68K_EXCEPTION_VECTOR_COUNT 256
46 #warning M68K: align on 4 ?
47 //m68k_exception_handler gExceptionVectors[M68K_EXCEPTION_VECTOR_COUNT];
48 m68k_exception_handler *gExceptionVectors;
49 
50 // defined in arch_exceptions.S
51 extern "C" void __m68k_exception_noop(void);
52 extern "C" void __m68k_exception_common(void);
53 
54 extern int __irqvec_start;
55 extern int __irqvec_end;
56 
57 extern"C" void m68k_exception_tail(void);
58 
59 // current fault handler
60 addr_t gFaultHandler;
61 
62 // An iframe stack used in the early boot process when we don't have
63 // threads yet.
64 struct iframe_stack gBootFrameStack;
65 
66 // interrupt controller interface (initialized
67 // in arch_int_init_post_device_manager())
68 //static struct interrupt_controller_module_info *sPIC;
69 //static void *sPICCookie;
70 
71 
72 void
73 arch_int_enable_io_interrupt(int irq)
74 {
75 	//if (!sPIC)
76 	//	return;
77 
78 	// TODO: I have no idea, what IRQ type is appropriate.
79 	//sPIC->enable_io_interrupt(sPICCookie, irq, IRQ_TYPE_LEVEL);
80 	M68KPlatform::Default()->EnableIOInterrupt(irq);
81 }
82 
83 
84 void
85 arch_int_disable_io_interrupt(int irq)
86 {
87 	//if (!sPIC)
88 	//	return;
89 
90 	//sPIC->disable_io_interrupt(sPICCookie, irq);
91 	M68KPlatform::Default()->DisableIOInterrupt(irq);
92 }
93 
94 
95 /* arch_int_*_interrupts() and friends are in arch_asm.S */
96 
97 
98 void
99 arch_int_assign_to_cpu(int32 irq, int32 cpu)
100 {
101 	// intentionally left blank; no SMP support (yet)
102 }
103 
104 
105 static void
106 print_iframe(struct iframe *frame)
107 {
108 	dprintf("iframe at %p:\n", frame);
109 	dprintf("   d0 0x%08lx    d1 0x%08lx    d2 0x%08lx    d3 0x%08lx\n",
110 				frame->d[0], frame->d[1], frame->d[2], frame->d[3]);
111 			kprintf("   d4 0x%08lx    d5 0x%08lx    d6 0x%08lx    d7 0x%08lx\n",
112 				frame->d[4], frame->d[5], frame->d[6], frame->d[7]);
113 			kprintf("   a0 0x%08lx    a1 0x%08lx    a2 0x%08lx    a3 0x%08lx\n",
114 				frame->a[0], frame->a[1], frame->a[2], frame->a[3]);
115 			kprintf("   a4 0x%08lx    a5 0x%08lx    a6 0x%08lx    "/*"a7 0x%08lx (sp)"*/"\n",
116 				frame->a[4], frame->a[5], frame->a[6]/*, frame->a[7]*/);
117 
118 			/*kprintf("   pc 0x%08lx   ccr 0x%02x\n",
119 			  frame->pc, frame->ccr);*/
120 			kprintf("   pc 0x%08lx        sr 0x%04x\n",
121 				frame->cpu.pc, frame->cpu.sr);
122 #if 0
123 	dprintf("r0-r3:   0x%08lx 0x%08lx 0x%08lx 0x%08lx\n", frame->d0, frame->d1, frame->d2, frame->d3);
124 	dprintf("r4-r7:   0x%08lx 0x%08lx 0x%08lx 0x%08lx\n", frame->d4, frame->d5, frame->d6, frame->d7);
125 	dprintf("r8-r11:  0x%08lx 0x%08lx 0x%08lx 0x%08lx\n", frame->a0, frame->a1, frame->a2, frame->a3);
126 	dprintf("r12-r15: 0x%08lx 0x%08lx 0x%08lx 0x%08lx\n", frame->a4, frame->a5, frame->a6, frame->a7);
127 	dprintf("      pc 0x%08lx         sr 0x%08lx\n", frame->pc, frame->sr);
128 #endif
129 }
130 
131 
132 static addr_t
133 fault_address(struct iframe *iframe)
134 {
135 	switch (iframe->cpu.type) {
136 		case 0x0:
137 		case 0x1:
138 			return 0;
139 		case 0x2:
140 			return iframe->cpu.type_2.instruction_address;
141 		case 0x3:
142 			return iframe->cpu.type_3.effective_address;
143 		case 0x7:
144 			return iframe->cpu.type_7.effective_address;
145 		case 0x9:
146 			return iframe->cpu.type_9.instruction_address;
147 		case 0xa:
148 			return iframe->cpu.type_a.fault_address;
149 		case 0xb:
150 			return iframe->cpu.type_b.fault_address;
151 		default:
152 			return 0;
153 	}
154 }
155 
156 
157 static bool
158 fault_was_write(struct iframe *iframe)
159 {
160 	switch (iframe->cpu.type) {
161 		case 0x7:
162 			return !iframe->cpu.type_7.ssw.rw;
163 		case 0xa:
164 			return !iframe->cpu.type_a.ssw.rw;
165 		case 0xb:
166 			return !iframe->cpu.type_b.ssw.rw;
167 		default:
168 			panic("can't determine r/w from iframe type %d\n",
169 				iframe->cpu.type);
170 			return false;
171 	}
172 }
173 
174 
175 extern "C" void m68k_exception_entry(struct iframe *iframe);
176 void
177 m68k_exception_entry(struct iframe *iframe)
178 {
179 	int vector = iframe->cpu.vector >> 2;
180 	bool hardwareInterrupt = false;
181 
182 	if (vector != -1) {
183 		dprintf("m68k_exception_entry: time %lld vector 0x%x, iframe %p, "
184 			"pc: %p\n", system_time(), vector, iframe, (void*)iframe->cpu.pc);
185 	}
186 
187 	Thread *thread = thread_get_current_thread();
188 
189 	// push iframe
190 	if (thread)
191 		m68k_push_iframe(&thread->arch_info.iframes, iframe);
192 	else
193 		m68k_push_iframe(&gBootFrameStack, iframe);
194 
195 	switch (vector) {
196 		case 0: // system reset
197 			panic("system reset exception\n");
198 			break;
199 		case 2: // bus error
200 		case 3: // address error
201 		{
202 			bool kernelDebugger = debug_debugger_running();
203 
204 			if (kernelDebugger) {
205 				// if this thread has a fault handler, we're allowed to be here
206 				if (thread && thread->fault_handler != 0) {
207 					iframe->cpu.pc = reinterpret_cast<addr_t>(thread->fault_handler);
208 					break;
209 				}
210 
211 
212 				// otherwise, not really
213 				panic("page fault in debugger without fault handler! Touching "
214 					"address %p from ip %p\n", (void *)fault_address(iframe),
215 					(void *)iframe->cpu.pc);
216 				break;
217 			} else if ((iframe->cpu.sr & SR_IP_MASK) != 0) {
218 				// interrupts disabled
219 
220 				// If a page fault handler is installed, we're allowed to be here.
221 				// TODO: Now we are generally allowing user_memcpy() with interrupts
222 				// disabled, which in most cases is a bug. We should add some thread
223 				// flag allowing to explicitly indicate that this handling is desired.
224 				if (thread && thread->fault_handler != 0) {
225 					iframe->cpu.pc = reinterpret_cast<addr_t>(thread->fault_handler);
226 						return;
227 				}
228 
229 				// if the interrupts were disabled, and we are not running the
230 				// kernel startup the page fault was not allowed to happen and
231 				// we must panic
232 				panic("page fault, but interrupts were disabled. Touching "
233 					"address %p from ip %p\n", (void *)fault_address(iframe),
234 					(void *)iframe->cpu.pc);
235 				break;
236 			} else if (thread != NULL && thread->page_faults_allowed < 1) {
237 				panic("page fault not allowed at this place. Touching address "
238 					"%p from ip %p\n", (void *)fault_address(iframe),
239 					(void *)iframe->cpu.pc);
240 			}
241 
242 			enable_interrupts();
243 
244 			addr_t newip;
245 
246 			vm_page_fault(fault_address(iframe), iframe->cpu.pc,
247 				fault_was_write(iframe), // store or load
248 				false,
249 				iframe->cpu.sr & SR_S, // was the system in user or supervisor
250 				&newip);
251 			if (newip != 0) {
252 				// the page fault handler wants us to modify the iframe to set the
253 				// IP the cpu will return to to be this ip
254 				iframe->cpu.pc = newip;
255 			}
256  			break;
257 		}
258 
259 		case 24: // spurious interrupt
260 			dprintf("spurious interrupt\n");
261 			break;
262 		case 25: // autovector interrupt
263 		case 26: // autovector interrupt
264 		case 27: // autovector interrupt
265 		case 28: // autovector interrupt
266 		case 29: // autovector interrupt
267 		case 30: // autovector interrupt
268 		case 31: // autovector interrupt
269 		{
270 #if 0
271 			if (!sPIC) {
272 				panic("m68k_exception_entry(): external interrupt although we "
273 					"don't have a PIC driver!");
274 				break;
275 			}
276 #endif
277 			M68KPlatform::Default()->AcknowledgeIOInterrupt(vector);
278 
279 dprintf("handling I/O interrupts...\n");
280 			int_io_interrupt_handler(vector, true);
281 #if 0
282 			while ((irq = sPIC->acknowledge_io_interrupt(sPICCookie)) >= 0) {
283 // TODO: correctly pass level-triggered vs. edge-triggered to the handler!
284 				int_io_interrupt_handler(irq, true);
285 			}
286 #endif
287 dprintf("handling I/O interrupts done\n");
288 			hardwareInterrupt = true;
289 			break;
290 		}
291 
292 		case 9: // trace
293 		default:
294 			// vectors >= 64 are user defined vectors, used for IRQ
295 			if (vector >= 64) {
296 				if (M68KPlatform::Default()->AcknowledgeIOInterrupt(vector)) {
297 					int_io_interrupt_handler(vector, true);
298 					break;
299 				}
300 			}
301 			dprintf("unhandled exception type 0x%x\n", vector);
302 			print_iframe(iframe);
303 			panic("unhandled exception type\n");
304 	}
305 
306 	int state = disable_interrupts();
307 	if (thread->cpu->invoke_scheduler) {
308 		SpinLocker schedulerLocker(thread->scheduler_lock);
309 		scheduler_reschedule(B_THREAD_READY);
310 		schedulerLocker.Unlock();
311 		restore_interrupts(state);
312 	} else if (hardwareInterrupt && thread->post_interrupt_callback != NULL) {
313 		void (*callback)(void*) = thread->post_interrupt_callback;
314 		void* data = thread->post_interrupt_data;
315 
316 		thread->post_interrupt_callback = NULL;
317 		thread->post_interrupt_data = NULL;
318 
319 		restore_interrupts(state);
320 
321 		callback(data);
322 	}
323 
324 	// pop iframe
325 	if (thread)
326 		m68k_pop_iframe(&thread->arch_info.iframes);
327 	else
328 		m68k_pop_iframe(&gBootFrameStack);
329 }
330 
331 
332 status_t
333 arch_int_init(kernel_args *args)
334 {
335 	status_t err;
336 	addr_t vbr;
337 	int i;
338 
339 	gExceptionVectors = (m68k_exception_handler *)args->arch_args.vir_vbr;
340 
341 	/* fill in the vector table */
342 	for (i = 0; i < M68K_EXCEPTION_VECTOR_COUNT; i++)
343 		gExceptionVectors[i] = &__m68k_exception_common;
344 
345 	vbr = args->arch_args.phys_vbr;
346 	/* point VBR to the new table */
347 	asm volatile  ("movec %0,%%vbr" : : "r"(vbr):);
348 
349 	return B_OK;
350 }
351 
352 
353 status_t
354 arch_int_init_post_vm(kernel_args *args)
355 {
356 	status_t err;
357 	err = M68KPlatform::Default()->InitPIC(args);
358 	return err;
359 }
360 
361 
362 status_t
363 arch_int_init_io(kernel_args* args)
364 {
365 	return B_OK;
366 }
367 
368 
369 #if 0 /* PIC modules */
370 template<typename ModuleInfo>
371 struct Module : DoublyLinkedListLinkImpl<Module<ModuleInfo> > {
372 	Module(ModuleInfo *module)
373 		: module(module)
374 	{
375 	}
376 
377 	~Module()
378 	{
379 		if (module)
380 			put_module(((module_info*)module)->name);
381 	}
382 
383 	ModuleInfo	*module;
384 };
385 
386 typedef Module<interrupt_controller_module_info> PICModule;
387 
388 struct PICModuleList : DoublyLinkedList<PICModule> {
389 	~PICModuleList()
390 	{
391 		while (PICModule *module = First()) {
392 			Remove(module);
393 			delete module;
394 		}
395 	}
396 };
397 
398 
399 class DeviceTreeIterator {
400 public:
401 	DeviceTreeIterator(device_manager_info *deviceManager)
402 		: fDeviceManager(deviceManager),
403 		  fNode(NULL),
404 		  fParent(NULL)
405 	{
406 		Rewind();
407 	}
408 
409 	~DeviceTreeIterator()
410 	{
411 		if (fParent != NULL)
412 			fDeviceManager->put_device_node(fParent);
413 		if (fNode != NULL)
414 			fDeviceManager->put_device_node(fNode);
415 	}
416 
417 	void Rewind()
418 	{
419 		fNode = fDeviceManager->get_root();
420 	}
421 
422 	bool HasNext() const
423 	{
424 		return (fNode != NULL);
425 	}
426 
427 	device_node_handle Next()
428 	{
429 		if (fNode == NULL)
430 			return NULL;
431 
432 		device_node_handle foundNode = fNode;
433 
434 		// get first child
435 		device_node_handle child = NULL;
436 		if (fDeviceManager->get_next_child_device(fNode, &child, NULL)
437 				== B_OK) {
438 			// move to the child node
439 			if (fParent != NULL)
440 				fDeviceManager->put_device_node(fParent);
441 			fParent = fNode;
442 			fNode = child;
443 
444 		// no more children; backtrack to find the next sibling
445 		} else {
446 			while (fParent != NULL) {
447 				if (fDeviceManager->get_next_child_device(fParent, &fNode, NULL)
448 						== B_OK) {
449 						// get_next_child_device() always puts the node
450 					break;
451 				}
452 				fNode = fParent;
453 				fParent = fDeviceManager->get_parent(fNode);
454 			}
455 
456 			// if we hit the root node again, we're done
457 			if (fParent == NULL) {
458 				fDeviceManager->put_device_node(fNode);
459 				fNode = NULL;
460 			}
461 		}
462 
463 		return foundNode;
464 	}
465 
466 private:
467 	device_manager_info *fDeviceManager;
468 	device_node_handle	fNode;
469 	device_node_handle	fParent;
470 };
471 
472 
473 static void
474 get_interrupt_controller_modules(PICModuleList &list)
475 {
476 	const char *namePrefix = "interrupt_controllers/";
477 	size_t namePrefixLen = strlen(namePrefix);
478 
479 	char name[B_PATH_NAME_LENGTH];
480 	size_t length;
481 	uint32 cookie = 0;
482 	while (get_next_loaded_module_name(&cookie, name, &(length = sizeof(name)))
483 			== B_OK) {
484 		// an interrupt controller module?
485 		if (length <= namePrefixLen
486 			|| strncmp(name, namePrefix, namePrefixLen) != 0) {
487 			continue;
488 		}
489 
490 		// get the module
491 		interrupt_controller_module_info *moduleInfo;
492 		if (get_module(name, (module_info**)&moduleInfo) != B_OK)
493 			continue;
494 
495 		// add it to the list
496 		PICModule *module = new(nothrow) PICModule(moduleInfo);
497 		if (!module) {
498 			put_module(((module_info*)moduleInfo)->name);
499 			continue;
500 		}
501 		list.Add(module);
502 	}
503 }
504 
505 
506 static bool
507 probe_pic_device(device_node_handle node, PICModuleList &picModules)
508 {
509 	for (PICModule *module = picModules.Head();
510 		 module;
511 		 module = picModules.GetNext(module)) {
512 		bool noConnection;
513 		if (module->module->info.supports_device(node, &noConnection) > 0) {
514 			if (module->module->info.register_device(node) == B_OK)
515 				return true;
516 		}
517 	}
518 
519 	return false;
520 }
521 #endif /* PIC modules */
522 
523 status_t
524 arch_int_init_post_device_manager(struct kernel_args *args)
525 {
526 #if 0 /* PIC modules */
527 	// get the interrupt controller driver modules
528 	PICModuleList picModules;
529 	get_interrupt_controller_modules(picModules);
530 	if (picModules.IsEmpty()) {
531 		panic("arch_int_init_post_device_manager(): Found no PIC modules!");
532 		return B_ENTRY_NOT_FOUND;
533 	}
534 
535 	// get the device manager module
536 	device_manager_info *deviceManager;
537 	status_t error = get_module(B_DEVICE_MANAGER_MODULE_NAME,
538 		(module_info**)&deviceManager);
539 	if (error != B_OK) {
540 		panic("arch_int_init_post_device_manager(): Failed to get device "
541 			"manager: %s", strerror(error));
542 		return error;
543 	}
544 	Module<device_manager_info> _deviceManager(deviceManager);	// auto put
545 
546 	// iterate through the device tree and probe the interrupt controllers
547 	DeviceTreeIterator iterator(deviceManager);
548 	while (device_node_handle node = iterator.Next())
549 		probe_pic_device(node, picModules);
550 
551 	// iterate through the tree again and get an interrupt controller node
552 	iterator.Rewind();
553 	while (device_node_handle node = iterator.Next()) {
554 		char *deviceType;
555 		if (deviceManager->get_attr_string(node, B_DRIVER_DEVICE_TYPE,
556 				&deviceType, false) == B_OK) {
557 			bool isPIC
558 				= (strcmp(deviceType, B_INTERRUPT_CONTROLLER_DRIVER_TYPE) == 0);
559 			free(deviceType);
560 
561 			if (isPIC) {
562 				driver_module_info *driver;
563 				void *driverCookie;
564 				error = deviceManager->init_driver(node, NULL, &driver,
565 					&driverCookie);
566 				if (error == B_OK) {
567 					sPIC = (interrupt_controller_module_info *)driver;
568 					sPICCookie = driverCookie;
569 					return B_OK;
570 				}
571 			}
572 		}
573 	}
574 
575 #endif /* PIC modules */
576 
577 	// no PIC found
578 	panic("arch_int_init_post_device_manager(): Found no supported PIC!");
579 
580 	return B_ENTRY_NOT_FOUND;
581 }
582 
583 
584 #if 0//PPC
585 // #pragma mark -
586 
587 struct m68k_cpu_exception_context *
588 m68k_get_cpu_exception_context(int cpu)
589 {
590 	return sCPUExceptionContexts + cpu;
591 }
592 
593 
594 void
595 m68k_set_current_cpu_exception_context(struct m68k_cpu_exception_context *context)
596 {
597 	// translate to physical address
598 	addr_t physicalPage;
599 	addr_t inPageOffset = (addr_t)context & (B_PAGE_SIZE - 1);
600 	status_t error = vm_get_page_mapping(VMAddressSpace::KernelID(),
601 		(addr_t)context - inPageOffset, &physicalPage);
602 	if (error != B_OK) {
603 		panic("m68k_set_current_cpu_exception_context(): Failed to get physical "
604 			"address!");
605 		return;
606 	}
607 
608 	asm volatile("mtsprg0 %0" : : "r"(physicalPage + inPageOffset));
609 }
610 
611 #endif
612