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