xref: /haiku/src/system/kernel/arch/arm/arch_int.cpp (revision 1e60bdeab63fa7a57bc9a55b032052e95a18bd2c)
1 /*
2  * Copyright 2003-2012, 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  *              Ithamar R. Adema <ithamar@upgrade-android.com>
10  *
11  * Copyright 2001, Travis Geiselbrecht. All rights reserved.
12  * Distributed under the terms of the NewOS License.
13  */
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/vm.h>
29 #include <vm/vm_priv.h>
30 #include <vm/VMAddressSpace.h>
31 #include <string.h>
32 
33 #include <drivers/bus/FDT.h>
34 #include "soc.h"
35 
36 #include "soc_pxa.h"
37 #include "soc_omap3.h"
38 
39 #define TRACE_ARCH_INT
40 #ifdef TRACE_ARCH_INT
41 #	define TRACE(x) dprintf x
42 #else
43 #	define TRACE(x) ;
44 #endif
45 
46 #define VECTORPAGE_SIZE		64
47 #define USER_VECTOR_ADDR_LOW	0x00000000
48 #define USER_VECTOR_ADDR_HIGH	0xffff0000
49 
50 extern int _vectors_start;
51 extern int _vectors_end;
52 
53 static area_id sVectorPageArea;
54 static void *sVectorPageAddress;
55 static area_id sUserVectorPageArea;
56 static void *sUserVectorPageAddress;
57 static fdt_module_info *sFdtModule;
58 
59 // An iframe stack used in the early boot process when we don't have
60 // threads yet.
61 struct iframe_stack gBootFrameStack;
62 
63 
64 void
65 arch_int_enable_io_interrupt(int irq)
66 {
67 	TRACE(("arch_int_enable_io_interrupt(%d)\n", irq));
68 	InterruptController *ic = InterruptController::Get();
69 	if (ic != NULL)
70 		ic->EnableInterrupt(irq);
71 }
72 
73 
74 void
75 arch_int_disable_io_interrupt(int irq)
76 {
77 	TRACE(("arch_int_disable_io_interrupt(%d)\n", irq));
78 	InterruptController *ic = InterruptController::Get();
79 	if (ic != NULL)
80 		ic->DisableInterrupt(irq);
81 }
82 
83 
84 /* arch_int_*_interrupts() and friends are in arch_asm.S */
85 
86 void
87 arch_int_assign_to_cpu(int32 irq, int32 cpu)
88 {
89 	// intentionally left blank; no SMP support (yet)
90 }
91 
92 static void
93 print_iframe(const char *event, struct iframe *frame)
94 {
95 	if (event)
96 		dprintf("Exception: %s\n", event);
97 
98 	dprintf("R00=%08lx R01=%08lx R02=%08lx R03=%08lx\n"
99 		"R04=%08lx R05=%08lx R06=%08lx R07=%08lx\n",
100 		frame->r0, frame->r1, frame->r2, frame->r3,
101 		frame->r4, frame->r5, frame->r6, frame->r7);
102 	dprintf("R08=%08lx R09=%08lx R10=%08lx R11=%08lx\n"
103 		"R12=%08lx SP=%08lx LR=%08lx  PC=%08lx CPSR=%08lx\n",
104 		frame->r8, frame->r9, frame->r10, frame->r11,
105 		frame->r12, frame->svc_sp, frame->svc_lr, frame->pc, frame->spsr);
106 }
107 
108 
109 status_t
110 arch_int_init(kernel_args *args)
111 {
112 	return B_OK;
113 }
114 
115 
116 extern "C" void arm_vector_init(void);
117 
118 
119 static struct fdt_device_info intc_table[] = {
120 	{
121 		.compatible = "marvell,pxa-intc",
122 		.init = PXAInterruptController::Init,
123 	}, {
124 		.compatible = "ti,omap3-intc",
125 		.init = OMAP3InterruptController::Init,
126 	}
127 };
128 static int intc_count = sizeof(intc_table) / sizeof(struct fdt_device_info);
129 
130 
131 status_t
132 arch_int_init_post_vm(kernel_args *args)
133 {
134 	// create a read/write kernel area
135 	sVectorPageArea = create_area("vectorpage", (void **)&sVectorPageAddress,
136 		B_ANY_ADDRESS, VECTORPAGE_SIZE, B_FULL_LOCK,
137 		B_KERNEL_WRITE_AREA | B_KERNEL_READ_AREA);
138 	if (sVectorPageArea < 0)
139 		panic("vector page could not be created!");
140 
141 	// clone it at a fixed address with user read/only permissions
142 	sUserVectorPageAddress = (addr_t*)USER_VECTOR_ADDR_HIGH;
143 	sUserVectorPageArea = clone_area("user_vectorpage",
144 		(void **)&sUserVectorPageAddress, B_EXACT_ADDRESS,
145 		B_READ_AREA | B_EXECUTE_AREA, sVectorPageArea);
146 
147 	if (sUserVectorPageArea < 0)
148 		panic("user vector page @ %p could not be created (%lx)!",
149 			sVectorPageAddress, sUserVectorPageArea);
150 
151 	// copy vectors into the newly created area
152 	memcpy(sVectorPageAddress, &_vectors_start, VECTORPAGE_SIZE);
153 
154 	arm_vector_init();
155 
156 	// see if high vectors are enabled
157 	if ((mmu_read_c1() & (1 << 13)) != 0)
158 		dprintf("High vectors already enabled\n");
159 	else {
160 		mmu_write_c1(mmu_read_c1() | (1 << 13));
161 
162 		if ((mmu_read_c1() & (1 << 13)) == 0)
163 			dprintf("Unable to enable high vectors!\n");
164 		else
165 			dprintf("Enabled high vectors\n");
166 	}
167 
168 	status_t rc = get_module(B_FDT_MODULE_NAME, (module_info**)&sFdtModule);
169 	if (rc != B_OK)
170 		panic("Unable to get FDT module: %08lx!\n", rc);
171 
172 	rc = sFdtModule->setup_devices(intc_table, intc_count, NULL);
173 	if (rc != B_OK)
174 		panic("No interrupt controllers found!\n");
175 
176 	return B_OK;
177 }
178 
179 
180 status_t
181 arch_int_init_io(kernel_args* args)
182 {
183 	return B_OK;
184 }
185 
186 
187 status_t
188 arch_int_init_post_device_manager(struct kernel_args *args)
189 {
190 	return B_ENTRY_NOT_FOUND;
191 }
192 
193 
194 // Little helper class for handling the
195 // iframe stack as used by KDL.
196 class IFrameScope {
197 public:
198 	IFrameScope(struct iframe *iframe) {
199 		fThread = thread_get_current_thread();
200 		if (fThread)
201 			arm_push_iframe(&fThread->arch_info.iframes, iframe);
202 		else
203 			arm_push_iframe(&gBootFrameStack, iframe);
204 	}
205 
206 	virtual ~IFrameScope() {
207 		// pop iframe
208 		if (fThread)
209 			arm_pop_iframe(&fThread->arch_info.iframes);
210 		else
211 			arm_pop_iframe(&gBootFrameStack);
212 	}
213 private:
214 	Thread* fThread;
215 };
216 
217 
218 extern "C" void
219 arch_arm_undefined(struct iframe *iframe)
220 {
221 	print_iframe("Undefined Instruction", iframe);
222 	IFrameScope scope(iframe); // push/pop iframe
223 
224 	panic("not handled!");
225 }
226 
227 
228 extern "C" void
229 arch_arm_syscall(struct iframe *iframe)
230 {
231 	print_iframe("Software interrupt", iframe);
232 	IFrameScope scope(iframe); // push/pop iframe
233 }
234 
235 
236 extern "C" void
237 arch_arm_data_abort(struct iframe *frame)
238 {
239 	Thread *thread = thread_get_current_thread();
240 	bool isUser = (frame->spsr & 0x1f) == 0x10;
241 	addr_t far = arm_get_far();
242 	bool isWrite = true;
243 	addr_t newip = 0;
244 
245 #ifdef TRACE_ARCH_INT
246 	print_iframe("Data Abort", frame);
247 	dprintf("FAR: %08lx, thread: %s\n", far, thread->name);
248 #endif
249 
250 	IFrameScope scope(frame);
251 
252 	if (debug_debugger_running()) {
253 		// If this CPU or this thread has a fault handler, we're allowed to be
254 		// here.
255 		if (thread != NULL) {
256 			cpu_ent* cpu = &gCPU[smp_get_current_cpu()];
257 
258 			if (cpu->fault_handler != 0) {
259 				debug_set_page_fault_info(far, frame->pc,
260 					isWrite ? DEBUG_PAGE_FAULT_WRITE : 0);
261 				frame->svc_sp = cpu->fault_handler_stack_pointer;
262 				frame->pc = cpu->fault_handler;
263 				return;
264 			}
265 
266 			if (thread->fault_handler != 0) {
267 				kprintf("ERROR: thread::fault_handler used in kernel "
268 					"debugger!\n");
269 				debug_set_page_fault_info(far, frame->pc,
270 						isWrite ? DEBUG_PAGE_FAULT_WRITE : 0);
271 				frame->pc = reinterpret_cast<uintptr_t>(thread->fault_handler);
272 				return;
273 			}
274 		}
275 
276 		// otherwise, not really
277 		panic("page fault in debugger without fault handler! Touching "
278 			"address %p from pc %p\n", (void *)far, (void *)frame->pc);
279 		return;
280 	} else if ((frame->spsr & (1 << 7)) != 0) {
281 		// interrupts disabled
282 
283 		// If a page fault handler is installed, we're allowed to be here.
284 		// TODO: Now we are generally allowing user_memcpy() with interrupts
285 		// disabled, which in most cases is a bug. We should add some thread
286 		// flag allowing to explicitly indicate that this handling is desired.
287 		uintptr_t handler = reinterpret_cast<uintptr_t>(thread->fault_handler);
288 		if (thread && thread->fault_handler != 0) {
289 			if (frame->pc != handler) {
290 				frame->pc = handler;
291 				return;
292 			}
293 
294 			// The fault happened at the fault handler address. This is a
295 			// certain infinite loop.
296 			panic("page fault, interrupts disabled, fault handler loop. "
297 				"Touching address %p from pc %p\n", (void*)far,
298 				(void*)frame->pc);
299 		}
300 
301 		// If we are not running the kernel startup the page fault was not
302 		// allowed to happen and we must panic.
303 		panic("page fault, but interrupts were disabled. Touching address "
304 			"%p from pc %p\n", (void *)far, (void *)frame->pc);
305 		return;
306 	} else if (thread != NULL && thread->page_faults_allowed < 1) {
307 		panic("page fault not allowed at this place. Touching address "
308 			"%p from pc %p\n", (void *)far, (void *)frame->pc);
309 		return;
310 	}
311 
312 	enable_interrupts();
313 
314 	vm_page_fault(far, frame->pc, isWrite, false, isUser, &newip);
315 
316 	if (newip != 0) {
317 		// the page fault handler wants us to modify the iframe to set the
318 		// IP the cpu will return to to be this ip
319 		frame->pc = newip;
320 	}
321 }
322 
323 
324 extern "C" void
325 arch_arm_prefetch_abort(struct iframe *iframe)
326 {
327 	print_iframe("Prefetch Abort", iframe);
328 	IFrameScope scope(iframe);
329 
330 	panic("not handled!");
331 }
332 
333 
334 extern "C" void
335 arch_arm_irq(struct iframe *iframe)
336 {
337 	IFrameScope scope(iframe);
338 
339 	InterruptController *ic = InterruptController::Get();
340 	if (ic != NULL)
341 		ic->HandleInterrupt();
342 }
343 
344 
345 extern "C" void
346 arch_arm_fiq(struct iframe *iframe)
347 {
348 	IFrameScope scope(iframe);
349 
350 	panic("FIQ not implemented yet!");
351 }
352