xref: /haiku/src/system/kernel/arch/arm/arch_int.cpp (revision 6f80a9801fedbe7355c4360bd204ba746ec3ec2d)
1 /*
2  * Copyright 2003-2022, 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_cpu_defs.h>
19 #include <arch/smp.h>
20 #include <boot/kernel_args.h>
21 #include <device_manager.h>
22 #include <kscheduler.h>
23 #include <interrupt_controller.h>
24 #include <ksyscalls.h>
25 #include <smp.h>
26 #include <syscall_numbers.h>
27 #include <thread.h>
28 #include <timer.h>
29 #include <util/AutoLock.h>
30 #include <util/DoublyLinkedList.h>
31 #include <util/kernel_cpp.h>
32 #include <vm/vm.h>
33 #include <vm/vm_priv.h>
34 #include <vm/VMAddressSpace.h>
35 #include <algorithm>
36 #include <string.h>
37 
38 #include <drivers/bus/FDT.h>
39 #include "arch_int_gicv2.h"
40 #include "soc.h"
41 
42 #include "soc_pxa.h"
43 #include "soc_omap3.h"
44 #include "soc_sun4i.h"
45 
46 #define TRACE_ARCH_INT
47 #ifdef TRACE_ARCH_INT
48 #	define TRACE(x) dprintf x
49 #else
50 #	define TRACE(x) ;
51 #endif
52 
53 #define VECTORPAGE_SIZE		64
54 #define USER_VECTOR_ADDR_LOW	0x00000000
55 #define USER_VECTOR_ADDR_HIGH	0xffff0000
56 
57 extern int _vectors_start;
58 extern int _vectors_end;
59 
60 static area_id sVectorPageArea;
61 static void *sVectorPageAddress;
62 static area_id sUserVectorPageArea;
63 static void *sUserVectorPageAddress;
64 //static fdt_module_info *sFdtModule;
65 
66 // An iframe stack used in the early boot process when we don't have
67 // threads yet.
68 struct iframe_stack gBootFrameStack;
69 
70 
71 void
72 arch_int_enable_io_interrupt(int irq)
73 {
74 	TRACE(("arch_int_enable_io_interrupt(%d)\n", irq));
75 	InterruptController *ic = InterruptController::Get();
76 	if (ic != NULL)
77 		ic->EnableInterrupt(irq);
78 }
79 
80 
81 void
82 arch_int_disable_io_interrupt(int irq)
83 {
84 	TRACE(("arch_int_disable_io_interrupt(%d)\n", irq));
85 	InterruptController *ic = InterruptController::Get();
86 	if (ic != NULL)
87 		ic->DisableInterrupt(irq);
88 }
89 
90 
91 /* arch_int_*_interrupts() and friends are in arch_asm.S */
92 
93 int32
94 arch_int_assign_to_cpu(int32 irq, int32 cpu)
95 {
96 	// Not yet supported.
97 	return 0;
98 }
99 
100 
101 static void
102 print_iframe(const char *event, struct iframe *frame)
103 {
104 	if (event)
105 		dprintf("Exception: %s\n", event);
106 
107 	dprintf("R00=%08lx R01=%08lx R02=%08lx R03=%08lx\n"
108 		"R04=%08lx R05=%08lx R06=%08lx R07=%08lx\n",
109 		frame->r0, frame->r1, frame->r2, frame->r3,
110 		frame->r4, frame->r5, frame->r6, frame->r7);
111 	dprintf("R08=%08lx R09=%08lx R10=%08lx R11=%08lx\n"
112 		"R12=%08lx SP=%08lx LR=%08lx  PC=%08lx CPSR=%08lx\n",
113 		frame->r8, frame->r9, frame->r10, frame->r11,
114 		frame->r12, frame->svc_sp, frame->svc_lr, frame->pc, frame->spsr);
115 }
116 
117 
118 status_t
119 arch_int_init(kernel_args *args)
120 {
121 	return B_OK;
122 }
123 
124 
125 extern "C" void arm_vector_init(void);
126 
127 
128 status_t
129 arch_int_init_post_vm(kernel_args *args)
130 {
131 	// create a read/write kernel area
132 	sVectorPageArea = create_area("vectorpage", (void **)&sVectorPageAddress,
133 		B_ANY_ADDRESS, VECTORPAGE_SIZE, B_FULL_LOCK,
134 		B_KERNEL_WRITE_AREA | B_KERNEL_READ_AREA);
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)!",
146 			sVectorPageAddress, sUserVectorPageArea);
147 
148 	// copy vectors into the newly created area
149 	memcpy(sVectorPageAddress, &_vectors_start, VECTORPAGE_SIZE);
150 
151 	arm_vector_init();
152 
153 	// see if high vectors are enabled
154 	if ((mmu_read_c1() & (1 << 13)) != 0)
155 		dprintf("High vectors already enabled\n");
156 	else {
157 		mmu_write_c1(mmu_read_c1() | (1 << 13));
158 
159 		if ((mmu_read_c1() & (1 << 13)) == 0)
160 			dprintf("Unable to enable high vectors!\n");
161 		else
162 			dprintf("Enabled high vectors\n");
163 	}
164 
165 	if (strncmp(args->arch_args.interrupt_controller.kind, INTC_KIND_GICV2,
166 		sizeof(args->arch_args.interrupt_controller.kind)) == 0) {
167 		InterruptController *ic = new(std::nothrow) GICv2InterruptController(
168 			args->arch_args.interrupt_controller.regs1.start,
169 			args->arch_args.interrupt_controller.regs2.start);
170 		if (ic == NULL)
171 			return B_NO_MEMORY;
172 	} else if (strncmp(args->arch_args.interrupt_controller.kind, INTC_KIND_OMAP3,
173 		sizeof(args->arch_args.interrupt_controller.kind)) == 0) {
174 		InterruptController *ic = new(std::nothrow) OMAP3InterruptController(
175 			args->arch_args.interrupt_controller.regs1.start);
176 		if (ic == NULL)
177 			return B_NO_MEMORY;
178 	} else if (strncmp(args->arch_args.interrupt_controller.kind, INTC_KIND_PXA,
179 		sizeof(args->arch_args.interrupt_controller.kind)) == 0) {
180 		InterruptController *ic = new(std::nothrow) PXAInterruptController(
181 			args->arch_args.interrupt_controller.regs1.start);
182 		if (ic == NULL)
183 			return B_NO_MEMORY;
184 	} else if (strncmp(args->arch_args.interrupt_controller.kind, INTC_KIND_SUN4I,
185 		sizeof(args->arch_args.interrupt_controller.kind)) == 0) {
186 		InterruptController *ic = new(std::nothrow) Sun4iInterruptController(
187 			args->arch_args.interrupt_controller.regs1.start);
188 		if (ic == NULL)
189 			return B_NO_MEMORY;
190 	} else {
191 		panic("No interrupt controllers found!\n");
192 	}
193 
194 	return B_OK;
195 }
196 
197 
198 status_t
199 arch_int_init_io(kernel_args* args)
200 {
201 	return B_OK;
202 }
203 
204 
205 status_t
206 arch_int_init_post_device_manager(struct kernel_args *args)
207 {
208 	return B_ENTRY_NOT_FOUND;
209 }
210 
211 
212 // Little helper class for handling the
213 // iframe stack as used by KDL.
214 class IFrameScope {
215 public:
216 	IFrameScope(struct iframe *iframe) {
217 		fThread = thread_get_current_thread();
218 		if (fThread)
219 			arm_push_iframe(&fThread->arch_info.iframes, iframe);
220 		else
221 			arm_push_iframe(&gBootFrameStack, iframe);
222 	}
223 
224 	virtual ~IFrameScope() {
225 		// pop iframe
226 		if (fThread)
227 			arm_pop_iframe(&fThread->arch_info.iframes);
228 		else
229 			arm_pop_iframe(&gBootFrameStack);
230 	}
231 private:
232 	Thread* fThread;
233 };
234 
235 
236 extern "C" void
237 arch_arm_undefined(struct iframe *iframe)
238 {
239 	print_iframe("Undefined Instruction", iframe);
240 	IFrameScope scope(iframe); // push/pop iframe
241 
242 	panic("not handled!");
243 }
244 
245 
246 extern "C" void
247 arch_arm_syscall(struct iframe *iframe)
248 {
249 #ifdef TRACE_ARCH_INT
250 	print_iframe("Software interrupt", iframe);
251 #endif
252 
253 	uint32_t syscall = *(uint32_t *)(iframe->pc-4) & 0x00ffffff;
254 	TRACE(("syscall number: %d\n", syscall));
255 
256 	uint32_t args[20];
257 	if (syscall < kSyscallCount) {
258 		TRACE(("syscall(%s,%d)\n",
259 			kExtendedSyscallInfos[syscall].name,
260 			kExtendedSyscallInfos[syscall].parameter_count));
261 
262 		int argSize = kSyscallInfos[syscall].parameter_size;
263 		memcpy(args, &iframe->r0, std::min<int>(argSize, 4 * sizeof(uint32)));
264 		if (argSize > 4 * sizeof(uint32)) {
265 			status_t res = user_memcpy(&args[4], (void *)iframe->usr_sp,
266 				(argSize - 4 * sizeof(uint32)));
267 			if (res < B_OK) {
268 				dprintf("can't read syscall arguments on user stack\n");
269 				iframe->r0 = res;
270 				return;
271 			}
272 		}
273 	}
274 
275 	enable_interrupts();
276 
277 	uint64 returnValue = 0;
278 	syscall_dispatcher(syscall, (void*)args, &returnValue);
279 
280 	TRACE(("returning %" B_PRId64 "\n", returnValue));
281 	iframe->r0 = returnValue;
282 }
283 
284 
285 extern "C" void
286 arch_arm_data_abort(struct iframe *frame)
287 {
288 	Thread *thread = thread_get_current_thread();
289 	bool isUser = (frame->spsr & CPSR_MODE_MASK) == CPSR_MODE_USR;
290 	int32 fsr = arm_get_fsr();
291 	addr_t far = arm_get_far();
292 	bool isWrite = (fsr & FSR_WNR) == FSR_WNR;
293 	addr_t newip = 0;
294 
295 #ifdef TRACE_ARCH_INT
296 	print_iframe("Data Abort", frame);
297 	dprintf("FAR: %08lx, isWrite: %d, thread: %s\n", far, isWrite, thread->name);
298 #endif
299 
300 	IFrameScope scope(frame);
301 
302 	if (debug_debugger_running()) {
303 		// If this CPU or this thread has a fault handler, we're allowed to be
304 		// here.
305 		if (thread != NULL) {
306 			cpu_ent* cpu = &gCPU[smp_get_current_cpu()];
307 
308 			if (cpu->fault_handler != 0) {
309 				debug_set_page_fault_info(far, frame->pc,
310 					isWrite ? DEBUG_PAGE_FAULT_WRITE : 0);
311 				frame->svc_sp = cpu->fault_handler_stack_pointer;
312 				frame->pc = cpu->fault_handler;
313 				return;
314 			}
315 
316 			if (thread->fault_handler != 0) {
317 				kprintf("ERROR: thread::fault_handler used in kernel "
318 					"debugger!\n");
319 				debug_set_page_fault_info(far, frame->pc,
320 						isWrite ? DEBUG_PAGE_FAULT_WRITE : 0);
321 				frame->pc = reinterpret_cast<uintptr_t>(thread->fault_handler);
322 				return;
323 			}
324 		}
325 
326 		// otherwise, not really
327 		panic("page fault in debugger without fault handler! Touching "
328 			"address %p from pc %p\n", (void *)far, (void *)frame->pc);
329 		return;
330 	} else if ((frame->spsr & (1 << 7)) != 0) {
331 		// interrupts disabled
332 
333 		// If a page fault handler is installed, we're allowed to be here.
334 		// TODO: Now we are generally allowing user_memcpy() with interrupts
335 		// disabled, which in most cases is a bug. We should add some thread
336 		// flag allowing to explicitly indicate that this handling is desired.
337 		uintptr_t handler = reinterpret_cast<uintptr_t>(thread->fault_handler);
338 		if (thread && thread->fault_handler != 0) {
339 			if (frame->pc != handler) {
340 				frame->pc = handler;
341 				return;
342 			}
343 
344 			// The fault happened at the fault handler address. This is a
345 			// certain infinite loop.
346 			panic("page fault, interrupts disabled, fault handler loop. "
347 				"Touching address %p from pc %p\n", (void*)far,
348 				(void*)frame->pc);
349 		}
350 
351 		// If we are not running the kernel startup the page fault was not
352 		// allowed to happen and we must panic.
353 		panic("page fault, but interrupts were disabled. Touching address "
354 			"%p from pc %p\n", (void *)far, (void *)frame->pc);
355 		return;
356 	} else if (thread != NULL && thread->page_faults_allowed < 1) {
357 		panic("page fault not allowed at this place. Touching address "
358 			"%p from pc %p\n", (void *)far, (void *)frame->pc);
359 		return;
360 	}
361 
362 	enable_interrupts();
363 
364 	vm_page_fault(far, frame->pc, isWrite, false, isUser, &newip);
365 
366 	if (newip != 0) {
367 		// the page fault handler wants us to modify the iframe to set the
368 		// IP the cpu will return to to be this ip
369 		frame->pc = newip;
370 	}
371 }
372 
373 
374 extern "C" void
375 arch_arm_prefetch_abort(struct iframe *frame)
376 {
377 	Thread *thread = thread_get_current_thread();
378 	bool isUser = (frame->spsr & CPSR_MODE_MASK) == CPSR_MODE_USR;
379 	addr_t newip = 0;
380 
381 #ifdef TRACE_ARCH_INT
382 	print_iframe("Prefetch Abort", frame);
383 	dprintf("thread: %s\n", thread->name);
384 #endif
385 
386 	IFrameScope scope(frame);
387 
388 	if (debug_debugger_running()) {
389 		// If this CPU or this thread has a fault handler, we're allowed to be
390 		// here.
391 		if (thread != NULL) {
392 			cpu_ent* cpu = &gCPU[smp_get_current_cpu()];
393 
394 			if (cpu->fault_handler != 0) {
395 				debug_set_page_fault_info(frame->pc, frame->pc, 0);
396 				frame->svc_sp = cpu->fault_handler_stack_pointer;
397 				frame->pc = cpu->fault_handler;
398 				return;
399 			}
400 
401 			if (thread->fault_handler != 0) {
402 				kprintf("ERROR: thread::fault_handler used in kernel "
403 					"debugger!\n");
404 				debug_set_page_fault_info(frame->pc, frame->pc, 0);
405 				frame->pc = reinterpret_cast<uintptr_t>(thread->fault_handler);
406 				return;
407 			}
408 		}
409 
410 		// otherwise, not really
411 		panic("page fault in debugger without fault handler! Prefetch abort at %p\n",
412 			(void *)frame->pc);
413 		return;
414 	} else if ((frame->spsr & (1 << 7)) != 0) {
415 		// interrupts disabled
416 
417 		// If a page fault handler is installed, we're allowed to be here.
418 		uintptr_t handler = reinterpret_cast<uintptr_t>(thread->fault_handler);
419 		if (thread && thread->fault_handler != 0) {
420 			if (frame->pc != handler) {
421 				frame->pc = handler;
422 				return;
423 			}
424 
425 			// The fault happened at the fault handler address. This is a
426 			// certain infinite loop.
427 			panic("page fault, interrupts disabled, fault handler loop. "
428 				"Prefetch abort at %p\n", (void*)frame->pc);
429 		}
430 
431 		// If we are not running the kernel startup the page fault was not
432 		// allowed to happen and we must panic.
433 		panic("page fault, but interrupts were disabled. Prefetch abort at %p\n",
434 			(void *)frame->pc);
435 		return;
436 	} else if (thread != NULL && thread->page_faults_allowed < 1) {
437 		panic("page fault not allowed at this place. Prefetch abort at %p\n",
438 			(void *)frame->pc);
439 		return;
440 	}
441 
442 	enable_interrupts();
443 
444 	vm_page_fault(frame->pc, frame->pc, false, true, isUser, &newip);
445 
446 	if (newip != 0) {
447 		frame->pc = newip;
448 	}
449 }
450 
451 
452 extern "C" void
453 arch_arm_irq(struct iframe *iframe)
454 {
455 	IFrameScope scope(iframe);
456 
457 	InterruptController *ic = InterruptController::Get();
458 	if (ic != NULL)
459 		ic->HandleInterrupt();
460 
461 	Thread* thread = thread_get_current_thread();
462 	cpu_status state = disable_interrupts();
463 	if (thread->cpu->invoke_scheduler) {
464 		SpinLocker schedulerLocker(thread->scheduler_lock);
465 		scheduler_reschedule(B_THREAD_READY);
466 		schedulerLocker.Unlock();
467 		restore_interrupts(state);
468 	} else if (thread->post_interrupt_callback != NULL) {
469 		void (*callback)(void*) = thread->post_interrupt_callback;
470 		void* data = thread->post_interrupt_data;
471 
472 		thread->post_interrupt_callback = NULL;
473 		thread->post_interrupt_data = NULL;
474 
475 		restore_interrupts(state);
476 
477 		callback(data);
478 	}
479 }
480 
481 
482 extern "C" void
483 arch_arm_fiq(struct iframe *iframe)
484 {
485 	IFrameScope scope(iframe);
486 
487 	panic("FIQ not implemented yet!");
488 }
489