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