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