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