xref: /haiku/src/system/kernel/arch/arm/arch_int.cpp (revision 776c58b2b56d8bcf33638a2ecb6c697f95a1cbf3)
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 
100 static void
101 print_iframe(const char *event, struct iframe *frame)
102 {
103 	if (event)
104 		dprintf("Exception: %s\n", event);
105 
106 	dprintf("R00=%08lx R01=%08lx R02=%08lx R03=%08lx\n"
107 		"R04=%08lx R05=%08lx R06=%08lx R07=%08lx\n",
108 		frame->r0, frame->r1, frame->r2, frame->r3,
109 		frame->r4, frame->r5, frame->r6, frame->r7);
110 	dprintf("R08=%08lx R09=%08lx R10=%08lx R11=%08lx\n"
111 		"R12=%08lx R13=%08lx R14=%08lx CPSR=%08lx\n",
112 		frame->r8, frame->r9, frame->r10, frame->r11,
113 		frame->r12, frame->usr_sp, frame->usr_lr, frame->spsr);
114 }
115 
116 
117 status_t
118 arch_int_init(kernel_args *args)
119 {
120 	return B_OK;
121 }
122 
123 
124 extern "C" void arm_vector_init(void);
125 
126 
127 status_t
128 arch_int_init_post_vm(kernel_args *args)
129 {
130 	// create a read/write kernel area
131 	sVectorPageArea = create_area("vectorpage", (void **)&sVectorPageAddress,
132 		B_ANY_ADDRESS, VECTORPAGE_SIZE, B_FULL_LOCK,
133 		B_KERNEL_WRITE_AREA | B_KERNEL_READ_AREA);
134 
135 	if (sVectorPageArea < 0)
136 		panic("vector page could not be created!");
137 
138 	// clone it at a fixed address with user read/only permissions
139 	sUserVectorPageAddress = (addr_t*)USER_VECTOR_ADDR_HIGH;
140 	sUserVectorPageArea = clone_area("user_vectorpage",
141 		(void **)&sUserVectorPageAddress, B_EXACT_ADDRESS,
142 		B_READ_AREA | B_EXECUTE_AREA, sVectorPageArea);
143 
144 	if (sUserVectorPageArea < 0)
145 		panic("user vector page @ %p could not be created (%lx)!", sVectorPageAddress, sUserVectorPageArea);
146 
147 	// copy vectors into the newly created area
148 	memcpy(sVectorPageAddress, &_vectors_start, VECTORPAGE_SIZE);
149 
150 	arm_vector_init();
151 
152 	// see if high vectors are enabled
153 	if ((mmu_read_c1() & (1 << 13)) != 0)
154 		dprintf("High vectors already enabled\n");
155 	else {
156 		mmu_write_c1(mmu_read_c1() | (1 << 13));
157 
158 		if ((mmu_read_c1() & (1 << 13)) == 0)
159 			dprintf("Unable to enable high vectors!\n");
160 		else
161 			dprintf("Enabled high vectors\n");
162 	}
163 
164 	sPxaInterruptArea = map_physical_memory("pxa_intc", PXA_INTERRUPT_PHYS_BASE,
165 		PXA_INTERRUPT_SIZE, 0, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, (void**)&sPxaInterruptBase);
166 
167 	if (sPxaInterruptArea < 0)
168 		return sPxaInterruptArea;
169 
170 	sPxaInterruptBase[PXA_ICMR] = 0;
171 	sPxaInterruptBase[PXA_ICMR2] = 0;
172 
173 	return B_OK;
174 }
175 
176 
177 status_t
178 arch_int_init_io(kernel_args* args)
179 {
180 	return B_OK;
181 }
182 
183 
184 status_t
185 arch_int_init_post_device_manager(struct kernel_args *args)
186 {
187 	return B_ENTRY_NOT_FOUND;
188 }
189 
190 
191 extern "C" void
192 arch_arm_undefined(struct iframe *iframe)
193 {
194 	print_iframe("Undefined Instruction", iframe);
195 	panic("not handled!");
196 }
197 
198 
199 extern "C" void
200 arch_arm_syscall(struct iframe *iframe)
201 {
202 	print_iframe("Software interrupt", iframe);
203 }
204 
205 
206 extern "C" void
207 arch_arm_data_abort(struct iframe *frame)
208 {
209 	Thread *thread = thread_get_current_thread();
210 	bool isUser = (frame->spsr & 0x1f) == 0x10;
211 	addr_t far = arm_get_far();
212 	bool isWrite = true;
213 	addr_t newip = 0;
214 
215 #ifdef TRACE_ARCH_INT
216 	print_iframe("Data Abort", frame);
217 #endif
218 
219 	if (debug_debugger_running()) {
220 		// If this CPU or this thread has a fault handler, we're allowed to be
221 		// here.
222 		if (thread != NULL) {
223 			cpu_ent* cpu = &gCPU[smp_get_current_cpu()];
224 
225 			if (cpu->fault_handler != 0) {
226 				debug_set_page_fault_info(far, frame->pc,
227 					isWrite ? DEBUG_PAGE_FAULT_WRITE : 0);
228 				frame->svc_sp = cpu->fault_handler_stack_pointer;
229 				frame->pc = cpu->fault_handler;
230 				return;
231 			}
232 
233 			if (thread->fault_handler != 0) {
234 				kprintf("ERROR: thread::fault_handler used in kernel "
235 					"debugger!\n");
236 				debug_set_page_fault_info(far, frame->pc,
237 						isWrite ? DEBUG_PAGE_FAULT_WRITE : 0);
238 				frame->pc = thread->fault_handler;
239 				return;
240 			}
241 		}
242 
243 		// otherwise, not really
244 		panic("page fault in debugger without fault handler! Touching "
245 			"address %p from pc %p\n", (void *)far, (void *)frame->pc);
246 		return;
247 	} else if ((frame->spsr & (1 << 7)) != 0) {
248 		// interrupts disabled
249 
250 		// If a page fault handler is installed, we're allowed to be here.
251 		// TODO: Now we are generally allowing user_memcpy() with interrupts
252 		// disabled, which in most cases is a bug. We should add some thread
253 		// flag allowing to explicitly indicate that this handling is desired.
254 		if (thread && thread->fault_handler != 0) {
255 			if (frame->pc != thread->fault_handler) {
256 				frame->pc = thread->fault_handler;
257 				return;
258 			}
259 
260 			// The fault happened at the fault handler address. This is a
261 			// certain infinite loop.
262 			panic("page fault, interrupts disabled, fault handler loop. "
263 				"Touching address %p from pc %p\n", (void*)far,
264 				(void*)frame->pc);
265 		}
266 
267 		// If we are not running the kernel startup the page fault was not
268 		// allowed to happen and we must panic.
269 		panic("page fault, but interrupts were disabled. Touching address "
270 			"%p from pc %p\n", (void *)far, (void *)frame->pc);
271 		return;
272 	} else if (thread != NULL && thread->page_faults_allowed < 1) {
273 		panic("page fault not allowed at this place. Touching address "
274 			"%p from pc %p\n", (void *)far, (void *)frame->pc);
275 		return;
276 	}
277 
278 	enable_interrupts();
279 
280 	vm_page_fault(far, frame->pc, isWrite, isUser, &newip);
281 
282 	if (newip != 0) {
283 		// the page fault handler wants us to modify the iframe to set the
284 		// IP the cpu will return to to be this ip
285 		frame->pc = newip;
286 	}
287 }
288 
289 
290 extern "C" void
291 arch_arm_prefetch_abort(struct iframe *iframe)
292 {
293 	print_iframe("Prefetch Abort", iframe);
294 	panic("not handled!");
295 }
296 
297 
298 extern "C" void
299 arch_arm_irq(struct iframe *iframe)
300 {
301 	for (int i=0; i < 32; i++) {
302 		if (sPxaInterruptBase[PXA_ICIP] & (1 << i))
303 			int_io_interrupt_handler(i, true);
304 	}
305 }
306 
307 
308 extern "C" void
309 arch_arm_fiq(struct iframe *iframe)
310 {
311 	for (int i=0; i < 32; i++) {
312 		if (sPxaInterruptBase[PXA_ICIP] & (1 << i)) {
313 			dprintf("arch_arm_fiq: help me, FIQ %d was triggered but no "
314 				"FIQ support!\n", i);
315 		}
316 	}
317 }
318