xref: /haiku/src/system/kernel/arch/arm/arch_int.cpp (revision ed24eb5ff12640d052171c6a7feba37fab8a75d1)
1 /*
2  * Copyright 2003-2023, 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=%08x R01=%08x R02=%08x R03=%08x\n"
108 		"R04=%08x R05=%08x R06=%08x R07=%08x\n",
109 		frame->r0, frame->r1, frame->r2, frame->r3,
110 		frame->r4, frame->r5, frame->r6, frame->r7);
111 	dprintf("R08=%08x R09=%08x R10=%08x R11=%08x\n"
112 		"R12=%08x SPs=%08x LRs=%08x PC =%08x\n",
113 		frame->r8, frame->r9, frame->r10, frame->r11,
114 		frame->r12, frame->svc_sp, frame->svc_lr, frame->pc);
115 	dprintf("             SPu=%08x LRu=%08x CPSR=%08x\n",
116 		frame->usr_sp, frame->usr_lr, frame->spsr);
117 }
118 
119 
120 status_t
121 arch_int_init(kernel_args *args)
122 {
123 	return B_OK;
124 }
125 
126 
127 extern "C" void arm_vector_init(void);
128 
129 
130 status_t
131 arch_int_init_post_vm(kernel_args *args)
132 {
133 	// create a read/write kernel area
134 	sVectorPageArea = create_area("vectorpage", (void **)&sVectorPageAddress,
135 		B_ANY_ADDRESS, VECTORPAGE_SIZE, B_FULL_LOCK,
136 		B_KERNEL_WRITE_AREA | B_KERNEL_READ_AREA);
137 	if (sVectorPageArea < 0)
138 		panic("vector page could not be created!");
139 
140 	// clone it at a fixed address with user read/only permissions
141 	sUserVectorPageAddress = (addr_t*)USER_VECTOR_ADDR_HIGH;
142 	sUserVectorPageArea = clone_area("user_vectorpage",
143 		(void **)&sUserVectorPageAddress, B_EXACT_ADDRESS,
144 		B_READ_AREA | B_EXECUTE_AREA, sVectorPageArea);
145 
146 	if (sUserVectorPageArea < 0)
147 		panic("user vector page @ %p could not be created (%x)!",
148 			sVectorPageAddress, sUserVectorPageArea);
149 
150 	// copy vectors into the newly created area
151 	memcpy(sVectorPageAddress, &_vectors_start, VECTORPAGE_SIZE);
152 
153 	arm_vector_init();
154 
155 	// see if high vectors are enabled
156 	if ((mmu_read_c1() & (1 << 13)) != 0)
157 		dprintf("High vectors already enabled\n");
158 	else {
159 		mmu_write_c1(mmu_read_c1() | (1 << 13));
160 
161 		if ((mmu_read_c1() & (1 << 13)) == 0)
162 			dprintf("Unable to enable high vectors!\n");
163 		else
164 			dprintf("Enabled high vectors\n");
165 	}
166 
167 	if (strncmp(args->arch_args.interrupt_controller.kind, INTC_KIND_GICV2,
168 		sizeof(args->arch_args.interrupt_controller.kind)) == 0) {
169 		InterruptController *ic = new(std::nothrow) GICv2InterruptController(
170 			args->arch_args.interrupt_controller.regs1.start,
171 			args->arch_args.interrupt_controller.regs2.start);
172 		if (ic == NULL)
173 			return B_NO_MEMORY;
174 	} else if (strncmp(args->arch_args.interrupt_controller.kind, INTC_KIND_OMAP3,
175 		sizeof(args->arch_args.interrupt_controller.kind)) == 0) {
176 		InterruptController *ic = new(std::nothrow) OMAP3InterruptController(
177 			args->arch_args.interrupt_controller.regs1.start);
178 		if (ic == NULL)
179 			return B_NO_MEMORY;
180 	} else if (strncmp(args->arch_args.interrupt_controller.kind, INTC_KIND_PXA,
181 		sizeof(args->arch_args.interrupt_controller.kind)) == 0) {
182 		InterruptController *ic = new(std::nothrow) PXAInterruptController(
183 			args->arch_args.interrupt_controller.regs1.start);
184 		if (ic == NULL)
185 			return B_NO_MEMORY;
186 	} else if (strncmp(args->arch_args.interrupt_controller.kind, INTC_KIND_SUN4I,
187 		sizeof(args->arch_args.interrupt_controller.kind)) == 0) {
188 		InterruptController *ic = new(std::nothrow) Sun4iInterruptController(
189 			args->arch_args.interrupt_controller.regs1.start);
190 		if (ic == NULL)
191 			return B_NO_MEMORY;
192 	} else {
193 		panic("No interrupt controllers found!\n");
194 	}
195 
196 	return B_OK;
197 }
198 
199 
200 status_t
201 arch_int_init_io(kernel_args* args)
202 {
203 	return B_OK;
204 }
205 
206 
207 status_t
208 arch_int_init_post_device_manager(struct kernel_args *args)
209 {
210 	return B_ENTRY_NOT_FOUND;
211 }
212 
213 
214 // Little helper class for handling the
215 // iframe stack as used by KDL.
216 class IFrameScope {
217 public:
218 	IFrameScope(struct iframe *iframe) {
219 		fThread = thread_get_current_thread();
220 		if (fThread)
221 			arm_push_iframe(&fThread->arch_info.iframes, iframe);
222 		else
223 			arm_push_iframe(&gBootFrameStack, iframe);
224 	}
225 
226 	virtual ~IFrameScope() {
227 		// pop iframe
228 		if (fThread)
229 			arm_pop_iframe(&fThread->arch_info.iframes);
230 		else
231 			arm_pop_iframe(&gBootFrameStack);
232 	}
233 private:
234 	Thread* fThread;
235 };
236 
237 
238 extern "C" void
239 arch_arm_undefined(struct iframe *iframe)
240 {
241 	print_iframe("Undefined Instruction", iframe);
242 	IFrameScope scope(iframe); // push/pop iframe
243 
244 	panic("not handled!");
245 }
246 
247 
248 extern "C" void
249 arch_arm_syscall(struct iframe *iframe)
250 {
251 #ifdef TRACE_ARCH_INT
252 	print_iframe("Software interrupt", iframe);
253 #endif
254 
255 	IFrameScope scope(iframe);
256 
257 	uint32_t syscall = *(uint32_t *)(iframe->pc-4) & 0x00ffffff;
258 	TRACE("syscall number: %d\n", syscall);
259 
260 	uint32_t args[20];
261 	if (syscall < kSyscallCount) {
262 		TRACE("syscall(%s,%d)\n",
263 			kExtendedSyscallInfos[syscall].name,
264 			kExtendedSyscallInfos[syscall].parameter_count);
265 
266 		int argSize = kSyscallInfos[syscall].parameter_size;
267 		memcpy(args, &iframe->r0, std::min<int>(argSize, 4 * sizeof(uint32)));
268 		if (argSize > 4 * sizeof(uint32)) {
269 			status_t res = user_memcpy(&args[4], (void *)iframe->usr_sp,
270 				(argSize - 4 * sizeof(uint32)));
271 			if (res < B_OK) {
272 				dprintf("can't read syscall arguments on user stack\n");
273 				iframe->r0 = res;
274 				return;
275 			}
276 		}
277 	}
278 
279 	thread_get_current_thread()->arch_info.userFrame = iframe;
280 	thread_get_current_thread()->arch_info.oldR0 = iframe->r0;
281 	thread_at_kernel_entry(system_time());
282 
283 	enable_interrupts();
284 
285 	uint64 returnValue = 0;
286 	syscall_dispatcher(syscall, (void*)args, &returnValue);
287 
288 	TRACE("returning %" B_PRId64 "\n", returnValue);
289 	iframe->r0 = returnValue;
290 
291 	disable_interrupts();
292 	atomic_and(&thread_get_current_thread()->flags, ~THREAD_FLAGS_SYSCALL_RESTARTED);
293 	if ((thread_get_current_thread()->flags & (THREAD_FLAGS_SIGNALS_PENDING
294 			| THREAD_FLAGS_DEBUG_THREAD | THREAD_FLAGS_TRAP_FOR_CORE_DUMP)) != 0) {
295 		enable_interrupts();
296 		thread_at_kernel_exit();
297 	} else {
298 		thread_at_kernel_exit_no_signals();
299 	}
300 	if ((thread_get_current_thread()->flags & THREAD_FLAGS_RESTART_SYSCALL) != 0) {
301 		atomic_and(&thread_get_current_thread()->flags, ~THREAD_FLAGS_RESTART_SYSCALL);
302 		atomic_or(&thread_get_current_thread()->flags, THREAD_FLAGS_SYSCALL_RESTARTED);
303 		iframe->r0 = thread_get_current_thread()->arch_info.oldR0;
304 		iframe->pc -= 4;
305 	}
306 
307 	thread_get_current_thread()->arch_info.userFrame = NULL;
308 }
309 
310 
311 static void
312 arch_arm_page_fault(struct iframe *frame, addr_t far, uint32 fsr, bool isWrite, bool isExec)
313 {
314 	Thread *thread = thread_get_current_thread();
315 	bool isUser = (frame->spsr & CPSR_MODE_MASK) == CPSR_MODE_USR;
316 	addr_t newip = 0;
317 
318 #ifdef TRACE_ARCH_INT
319 	print_iframe("Page Fault", frame);
320 	dprintf("FAR: %08lx, FSR: %08x, isUser: %d, isWrite: %d, isExec: %d, thread: %s\n", far, fsr, isUser, isWrite, isExec, thread->name);
321 #endif
322 
323 	IFrameScope scope(frame);
324 
325 	if (debug_debugger_running()) {
326 		// If this CPU or this thread has a fault handler, we're allowed to be
327 		// here.
328 		if (thread != NULL) {
329 			cpu_ent* cpu = &gCPU[smp_get_current_cpu()];
330 
331 			if (cpu->fault_handler != 0) {
332 				debug_set_page_fault_info(far, frame->pc,
333 					isWrite ? DEBUG_PAGE_FAULT_WRITE : 0);
334 				frame->svc_sp = cpu->fault_handler_stack_pointer;
335 				frame->pc = cpu->fault_handler;
336 				return;
337 			}
338 
339 			if (thread->fault_handler != 0) {
340 				kprintf("ERROR: thread::fault_handler used in kernel "
341 					"debugger!\n");
342 				debug_set_page_fault_info(far, frame->pc,
343 						isWrite ? DEBUG_PAGE_FAULT_WRITE : 0);
344 				frame->pc = reinterpret_cast<uintptr_t>(thread->fault_handler);
345 				return;
346 			}
347 		}
348 
349 		// otherwise, not really
350 		panic("page fault in debugger without fault handler! Touching "
351 			"address %p from pc %p\n", (void *)far, (void *)frame->pc);
352 		return;
353 	} else if (isExec && !isUser && (far < KERNEL_BASE) &&
354 		(((fsr & 0x060f) == FSR_FS_PERMISSION_FAULT_L1) || ((fsr & 0x060f) == FSR_FS_PERMISSION_FAULT_L2))) {
355 		panic("PXN violation trying to execute user-mapped address 0x%08" B_PRIxADDR " from kernel mode\n",
356 			far);
357 	} else if (!isExec && ((fsr & 0x060f) == FSR_FS_ALIGNMENT_FAULT)) {
358 		panic("unhandled alignment exception\n");
359 	} else if ((frame->spsr & CPSR_I) != 0) {
360 		// interrupts disabled
361 
362 		// If a page fault handler is installed, we're allowed to be here.
363 		// TODO: Now we are generally allowing user_memcpy() with interrupts
364 		// disabled, which in most cases is a bug. We should add some thread
365 		// flag allowing to explicitly indicate that this handling is desired.
366 		uintptr_t handler = reinterpret_cast<uintptr_t>(thread->fault_handler);
367 		if (thread && thread->fault_handler != 0) {
368 			if (frame->pc != handler) {
369 				frame->pc = handler;
370 				return;
371 			}
372 
373 			// The fault happened at the fault handler address. This is a
374 			// certain infinite loop.
375 			panic("page fault, interrupts disabled, fault handler loop. "
376 				"Touching address %p from pc %p\n", (void*)far,
377 				(void*)frame->pc);
378 		}
379 
380 		// If we are not running the kernel startup the page fault was not
381 		// allowed to happen and we must panic.
382 		panic("page fault, but interrupts were disabled. Touching address "
383 			"%p from pc %p\n", (void *)far, (void *)frame->pc);
384 		return;
385 	} else if (thread != NULL && thread->page_faults_allowed < 1) {
386 		panic("page fault not allowed at this place. Touching address "
387 			"%p from pc %p\n", (void *)far, (void *)frame->pc);
388 		return;
389 	}
390 
391 	enable_interrupts();
392 
393 	vm_page_fault(far, frame->pc, isWrite, isExec, isUser, &newip);
394 
395 	if (newip != 0) {
396 		// the page fault handler wants us to modify the iframe to set the
397 		// IP the cpu will return to to be this ip
398 		frame->pc = newip;
399 	}
400 }
401 
402 
403 extern "C" void
404 arch_arm_data_abort(struct iframe *frame)
405 {
406 	addr_t dfar = arm_get_dfar();
407 	uint32 dfsr = arm_get_dfsr();
408 	bool isWrite = (dfsr & FSR_WNR) == FSR_WNR;
409 
410 	arch_arm_page_fault(frame, dfar, dfsr, isWrite, false);
411 }
412 
413 
414 extern "C" void
415 arch_arm_prefetch_abort(struct iframe *frame)
416 {
417 	addr_t ifar = arm_get_ifar();
418 	uint32 ifsr = arm_get_ifsr();
419 
420 	arch_arm_page_fault(frame, ifar, ifsr, false, true);
421 }
422 
423 
424 extern "C" void
425 arch_arm_irq(struct iframe *iframe)
426 {
427 	IFrameScope scope(iframe);
428 
429 	InterruptController *ic = InterruptController::Get();
430 	if (ic != NULL)
431 		ic->HandleInterrupt();
432 
433 	Thread* thread = thread_get_current_thread();
434 	cpu_status state = disable_interrupts();
435 	if (thread->cpu->invoke_scheduler) {
436 		SpinLocker schedulerLocker(thread->scheduler_lock);
437 		scheduler_reschedule(B_THREAD_READY);
438 		schedulerLocker.Unlock();
439 		restore_interrupts(state);
440 	} else if (thread->post_interrupt_callback != NULL) {
441 		void (*callback)(void*) = thread->post_interrupt_callback;
442 		void* data = thread->post_interrupt_data;
443 
444 		thread->post_interrupt_callback = NULL;
445 		thread->post_interrupt_data = NULL;
446 
447 		restore_interrupts(state);
448 
449 		callback(data);
450 	}
451 }
452 
453 
454 extern "C" void
455 arch_arm_fiq(struct iframe *iframe)
456 {
457 	IFrameScope scope(iframe);
458 
459 	panic("FIQ not implemented yet!");
460 }
461