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 *
9 * Copyright 2001, Travis Geiselbrecht. All rights reserved.
10 * Distributed under the terms of the NewOS License.
11 */
12
13
14#include <int.h>
15
16#include <arch/smp.h>
17#include <boot/kernel_args.h>
18#include <device_manager.h>
19#include <kscheduler.h>
20#include <interrupt_controller.h>
21#include <smp.h>
22#include <thread.h>
23#include <timer.h>
24#include <util/AutoLock.h>
25#include <util/DoublyLinkedList.h>
26#include <util/kernel_cpp.h>
27#include <vm/vm.h>
28#include <vm/vm_priv.h>
29#include <vm/VMAddressSpace.h>
30#include <PCI.h>
31
32#include <string.h>
33
34
35// defined in arch_exceptions.S
36extern int __irqvec_start;
37extern int __irqvec_end;
38
39extern"C" void ppc_exception_tail(void);
40
41
42// the exception contexts for all CPUs
43static ppc_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.
48struct iframe_stack gBootFrameStack;
49
50// interrupt controller interface (initialized
51// in arch_int_init_post_device_manager())
52static struct interrupt_controller_module_info *sPIC;
53static void *sPICCookie;
54
55
56void
57arch_int_enable_io_interrupt(int32 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
67void
68arch_int_disable_io_interrupt(int32 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
80static void
81print_iframe(struct iframe *frame)
82{
83	dprintf("iframe at %p:\n", frame);
84	dprintf("r0-r3:   0x%08lx 0x%08lx 0x%08lx 0x%08lx\n", frame->r0, frame->r1, frame->r2, frame->r3);
85	dprintf("r4-r7:   0x%08lx 0x%08lx 0x%08lx 0x%08lx\n", frame->r4, frame->r5, frame->r6, frame->r7);
86	dprintf("r8-r11:  0x%08lx 0x%08lx 0x%08lx 0x%08lx\n", frame->r8, frame->r9, frame->r10, frame->r11);
87	dprintf("r12-r15: 0x%08lx 0x%08lx 0x%08lx 0x%08lx\n", frame->r12, frame->r13, frame->r14, frame->r15);
88	dprintf("r16-r19: 0x%08lx 0x%08lx 0x%08lx 0x%08lx\n", frame->r16, frame->r17, frame->r18, frame->r19);
89	dprintf("r20-r23: 0x%08lx 0x%08lx 0x%08lx 0x%08lx\n", frame->r20, frame->r21, frame->r22, frame->r23);
90	dprintf("r24-r27: 0x%08lx 0x%08lx 0x%08lx 0x%08lx\n", frame->r24, frame->r25, frame->r26, frame->r27);
91	dprintf("r28-r31: 0x%08lx 0x%08lx 0x%08lx 0x%08lx\n", frame->r28, frame->r29, frame->r30, frame->r31);
92	dprintf("     ctr 0x%08lx        xer 0x%08lx\n", frame->ctr, frame->xer);
93	dprintf("      cr 0x%08lx         lr 0x%08lx\n", frame->cr, frame->lr);
94	dprintf("   dsisr 0x%08lx        dar 0x%08lx\n", frame->dsisr, frame->dar);
95	dprintf("    srr1 0x%08lx       srr0 0x%08lx\n", frame->srr1, frame->srr0);
96}
97
98
99extern "C" void ppc_exception_entry(int vector, struct iframe *iframe);
100void
101ppc_exception_entry(int vector, struct iframe *iframe)
102{
103	if (vector != 0x900) {
104		dprintf("ppc_exception_entry: time %lld vector 0x%x, iframe %p, "
105			"srr0: %p\n", system_time(), vector, iframe, (void*)iframe->srr0);
106	}
107
108	Thread *thread = thread_get_current_thread();
109
110	// push iframe
111	if (thread)
112		ppc_push_iframe(&thread->arch_info.iframes, iframe);
113	else
114		ppc_push_iframe(&gBootFrameStack, iframe);
115
116	switch (vector) {
117		case 0x100: // system reset
118			panic("system reset exception\n");
119			break;
120		case 0x200: // machine check
121			panic("machine check exception\n");
122			break;
123		case 0x300: // DSI
124		case 0x400: // ISI
125		{
126			bool kernelDebugger = debug_debugger_running();
127
128			if (kernelDebugger) {
129				// if this CPU or this thread has a fault handler,
130				// we're allowed to be here
131				cpu_ent* cpu = &gCPU[smp_get_current_cpu()];
132				if (cpu->fault_handler != 0) {
133					iframe->srr0 = cpu->fault_handler;
134					iframe->r1 = cpu->fault_handler_stack_pointer;
135					break;
136				}
137				Thread *thread = thread_get_current_thread();
138				if (thread && thread->fault_handler != 0) {
139					iframe->srr0 =
140						reinterpret_cast<uintptr_t>(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			vm_page_fault(iframe->dar, iframe->srr0,
168				iframe->dsisr & (1 << 25), // store or load
169				false,
170				iframe->srr1 & (1 << 14), // was the system in user or supervisor
171				&newip);
172			if (newip != 0) {
173				// the page fault handler wants us to modify the iframe to set the
174				// IP the cpu will return to to be this ip
175				iframe->srr0 = newip;
176			}
177 			break;
178		}
179
180		case 0x500: // external interrupt
181		{
182			if (!sPIC) {
183				panic("ppc_exception_entry(): external interrupt although we "
184					"don't have a PIC driver!");
185				break;
186			}
187
188dprintf("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				int_io_interrupt_handler(irq, true);
193			}
194dprintf("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			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	cpu_status state = disable_interrupts();
249	if (thread->cpu->invoke_scheduler) {
250		SpinLocker schedulerLocker(thread->scheduler_lock);
251		scheduler_reschedule(B_THREAD_READY);
252		schedulerLocker.Unlock();
253		restore_interrupts(state);
254	} else if (thread->post_interrupt_callback != NULL) {
255		void (*callback)(void*) = thread->post_interrupt_callback;
256		void* data = thread->post_interrupt_data;
257
258		thread->post_interrupt_callback = NULL;
259		thread->post_interrupt_data = NULL;
260
261		restore_interrupts(state);
262
263		callback(data);
264	}
265
266	// pop iframe
267	if (thread)
268		ppc_pop_iframe(&thread->arch_info.iframes);
269	else
270		ppc_pop_iframe(&gBootFrameStack);
271}
272
273
274status_t
275arch_int_init(kernel_args *args)
276{
277	return B_OK;
278}
279
280
281status_t
282arch_int_init_post_vm(kernel_args *args)
283{
284	void *handlers = (void *)args->arch_args.exception_handlers.start;
285
286	// We may need to remap the exception handler area into the kernel address
287	// space.
288	if (!IS_KERNEL_ADDRESS(handlers)) {
289		addr_t address = (addr_t)handlers;
290		status_t error = ppc_remap_address_range(&address,
291			args->arch_args.exception_handlers.size, true);
292		if (error != B_OK) {
293			panic("arch_int_init_post_vm(): Failed to remap the exception "
294				"handler area!");
295			return error;
296		}
297		handlers = (void*)(address);
298	}
299
300	// create a region to map the irq vector code into (physical address 0x0)
301	area_id exceptionArea = create_area("exception_handlers",
302		&handlers, B_EXACT_ADDRESS, args->arch_args.exception_handlers.size,
303		B_ALREADY_WIRED, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
304	if (exceptionArea < B_OK)
305		panic("arch_int_init2: could not create exception handler region\n");
306
307	dprintf("exception handlers at %p\n", handlers);
308
309	// copy the handlers into this area
310	memcpy(handlers, &__irqvec_start, args->arch_args.exception_handlers.size);
311	arch_cpu_sync_icache(handlers, args->arch_args.exception_handlers.size);
312
313	// init the CPU exception contexts
314	int cpuCount = smp_get_num_cpus();
315	for (int i = 0; i < cpuCount; i++) {
316		ppc_cpu_exception_context *context = ppc_get_cpu_exception_context(i);
317		context->kernel_handle_exception = (void*)&ppc_exception_tail;
318		context->exception_context = context;
319		// kernel_stack is set when the current thread changes. At this point
320		// we don't have threads yet.
321	}
322
323	// set the exception context for this CPU
324	ppc_set_current_cpu_exception_context(ppc_get_cpu_exception_context(0));
325
326	return B_OK;
327}
328
329
330status_t
331arch_int_init_io(kernel_args* args)
332{
333	return B_OK;
334}
335
336
337template<typename ModuleInfo>
338struct Module : DoublyLinkedListLinkImpl<Module<ModuleInfo> > {
339	Module(ModuleInfo *module)
340		: module(module)
341	{
342	}
343
344	~Module()
345	{
346		if (module)
347			put_module(((module_info*)module)->name);
348	}
349
350	ModuleInfo	*module;
351};
352
353typedef Module<interrupt_controller_module_info> PICModule;
354
355struct PICModuleList : DoublyLinkedList<PICModule> {
356	~PICModuleList()
357	{
358		while (PICModule *module = First()) {
359			Remove(module);
360			delete module;
361		}
362	}
363};
364
365
366class DeviceTreeIterator {
367public:
368	DeviceTreeIterator(device_manager_info *deviceManager)
369		: fDeviceManager(deviceManager),
370		  fNode(NULL),
371		  fParent(NULL)
372	{
373		Rewind();
374	}
375
376	~DeviceTreeIterator()
377	{
378		if (fParent != NULL)
379			fDeviceManager->put_node(fParent);
380		if (fNode != NULL)
381			fDeviceManager->put_node(fNode);
382	}
383
384	void Rewind()
385	{
386		fNode = fDeviceManager->get_root_node();
387	}
388
389	bool HasNext() const
390	{
391		return (fNode != NULL);
392	}
393
394	device_node *Next()
395	{
396		if (fNode == NULL)
397			return NULL;
398
399		device_node *foundNode = fNode;
400
401		// get first child
402		device_node *child = NULL;
403		if (fDeviceManager->get_next_child_node(fNode, NULL, &child)
404				== B_OK) {
405			// move to the child node
406			if (fParent != NULL)
407				fDeviceManager->put_node(fParent);
408			fParent = fNode;
409			fNode = child;
410
411		// no more children; backtrack to find the next sibling
412		} else {
413			while (fParent != NULL) {
414				if (fDeviceManager->get_next_child_node(fParent, NULL, &fNode)
415						== B_OK) {
416						// get_next_child_node() always puts the node
417					break;
418				}
419				fNode = fParent;
420				fParent = fDeviceManager->get_parent_node(fNode);
421			}
422
423			// if we hit the root node again, we're done
424			if (fParent == NULL) {
425				fDeviceManager->put_node(fNode);
426				fNode = NULL;
427			}
428		}
429
430		return foundNode;
431	}
432
433private:
434	device_manager_info *fDeviceManager;
435	device_node	*fNode;
436	device_node	*fParent;
437};
438
439
440static void
441get_interrupt_controller_modules(PICModuleList &list)
442{
443	const char *namePrefix = "interrupt_controllers/";
444	size_t namePrefixLen = strlen(namePrefix);
445
446	char name[B_PATH_NAME_LENGTH];
447	size_t length;
448	uint32 cookie = 0;
449	while (get_next_loaded_module_name(&cookie, name, &(length = sizeof(name)))
450			== B_OK) {
451		// an interrupt controller module?
452		if (length <= namePrefixLen
453			|| strncmp(name, namePrefix, namePrefixLen) != 0) {
454			continue;
455		}
456
457		// get the module
458		interrupt_controller_module_info *moduleInfo;
459		if (get_module(name, (module_info**)&moduleInfo) != B_OK)
460			continue;
461
462		// add it to the list
463		PICModule *module = new(nothrow) PICModule(moduleInfo);
464		if (!module) {
465			put_module(((module_info*)moduleInfo)->name);
466			continue;
467		}
468		list.Add(module);
469	}
470}
471
472
473static bool
474probe_pic_device(device_node *node, PICModuleList &picModules)
475{
476	for (PICModule *module = picModules.Head();
477		 module;
478		 module = picModules.GetNext(module)) {
479		if (module->module->info.supports_device(node) > 0) {
480			if (module->module->info.register_device(node) == B_OK)
481				return true;
482		}
483	}
484
485	return false;
486}
487
488
489status_t
490arch_int_init_post_device_manager(struct kernel_args *args)
491{
492	// get the interrupt controller driver modules
493	PICModuleList picModules;
494	get_interrupt_controller_modules(picModules);
495	if (picModules.IsEmpty()) {
496		panic("arch_int_init_post_device_manager(): Found no PIC modules!");
497		return B_ENTRY_NOT_FOUND;
498	}
499
500	// get the device manager module
501	device_manager_info *deviceManager;
502	status_t error = get_module(B_DEVICE_MANAGER_MODULE_NAME,
503		(module_info**)&deviceManager);
504	if (error != B_OK) {
505		panic("arch_int_init_post_device_manager(): Failed to get device "
506			"manager: %s", strerror(error));
507		return error;
508	}
509	Module<device_manager_info> _deviceManager(deviceManager);	// auto put
510
511	// iterate through the device tree and probe the interrupt controllers
512	DeviceTreeIterator iterator(deviceManager);
513	while (device_node *node = iterator.Next())
514		probe_pic_device(node, picModules);
515
516	// iterate through the tree again and get an interrupt controller node
517	iterator.Rewind();
518	while (device_node *node = iterator.Next()) {
519		uint16 pciBaseClass, pciSubType;
520		if (deviceManager->get_attr_uint16(node, B_DEVICE_TYPE,
521				&pciBaseClass, false) == B_OK &&
522			deviceManager->get_attr_uint16(node, B_DEVICE_SUB_TYPE,
523				&pciSubType, false) == B_OK) {
524			bool isPIC = (pciBaseClass == PCI_base_peripheral)
525				&& (pciSubType == PCI_pic);
526
527			if (isPIC) {
528				driver_module_info *driver;
529				void *driverCookie;
530
531				deviceManager->get_driver(node, (driver_module_info **)&driver, (void **)&driverCookie);
532
533				sPIC = (interrupt_controller_module_info *)driver;
534				sPICCookie = driverCookie;
535				return B_OK;
536			}
537		}
538	}
539
540	// no PIC found
541	panic("arch_int_init_post_device_manager(): Found no supported PIC!");
542
543	return B_ENTRY_NOT_FOUND;
544}
545
546
547// #pragma mark -
548
549struct ppc_cpu_exception_context *
550ppc_get_cpu_exception_context(int cpu)
551{
552	return sCPUExceptionContexts + cpu;
553}
554
555
556void
557ppc_set_current_cpu_exception_context(struct ppc_cpu_exception_context *context)
558{
559	// translate to physical address
560	phys_addr_t physicalPage;
561	addr_t inPageOffset = (addr_t)context & (B_PAGE_SIZE - 1);
562	status_t error = vm_get_page_mapping(VMAddressSpace::KernelID(),
563		(addr_t)context - inPageOffset, &physicalPage);
564	if (error != B_OK) {
565		panic("ppc_set_current_cpu_exception_context(): Failed to get physical "
566			"address!");
567		return;
568	}
569
570	asm volatile("mtsprg0 %0" : : "r"(physicalPage + inPageOffset));
571}
572
573
574int32
575arch_int_assign_to_cpu(int32 irq, int32 cpu)
576{
577	// Not yet supported.
578	return 0;
579}
580