xref: /haiku/src/system/kernel/arch/arm/arch_int.cpp (revision 17889a8c70dbb3d59c1412f6431968753c767bab)
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=%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 	enable_interrupts();
280 
281 	uint64 returnValue = 0;
282 	syscall_dispatcher(syscall, (void*)args, &returnValue);
283 
284 	TRACE("returning %" B_PRId64 "\n", returnValue);
285 	iframe->r0 = returnValue;
286 }
287 
288 
289 static void
290 arch_arm_page_fault(struct iframe *frame, addr_t far, uint32 fsr, bool isWrite, bool isExec)
291 {
292 	Thread *thread = thread_get_current_thread();
293 	bool isUser = (frame->spsr & CPSR_MODE_MASK) == CPSR_MODE_USR;
294 	addr_t newip = 0;
295 
296 #ifdef TRACE_ARCH_INT
297 	print_iframe("Page Fault", frame);
298 	dprintf("FAR: %08lx, FSR: %08x, isUser: %d, isWrite: %d, isExec: %d, thread: %s\n", far, fsr, isUser, isWrite, isExec, thread->name);
299 #endif
300 
301 	IFrameScope scope(frame);
302 
303 	if (debug_debugger_running()) {
304 		// If this CPU or this thread has a fault handler, we're allowed to be
305 		// here.
306 		if (thread != NULL) {
307 			cpu_ent* cpu = &gCPU[smp_get_current_cpu()];
308 
309 			if (cpu->fault_handler != 0) {
310 				debug_set_page_fault_info(far, frame->pc,
311 					isWrite ? DEBUG_PAGE_FAULT_WRITE : 0);
312 				frame->svc_sp = cpu->fault_handler_stack_pointer;
313 				frame->pc = cpu->fault_handler;
314 				return;
315 			}
316 
317 			if (thread->fault_handler != 0) {
318 				kprintf("ERROR: thread::fault_handler used in kernel "
319 					"debugger!\n");
320 				debug_set_page_fault_info(far, frame->pc,
321 						isWrite ? DEBUG_PAGE_FAULT_WRITE : 0);
322 				frame->pc = reinterpret_cast<uintptr_t>(thread->fault_handler);
323 				return;
324 			}
325 		}
326 
327 		// otherwise, not really
328 		panic("page fault in debugger without fault handler! Touching "
329 			"address %p from pc %p\n", (void *)far, (void *)frame->pc);
330 		return;
331 	} else if (isExec && !isUser && (far < KERNEL_BASE) &&
332 		(((fsr & 0x060f) == FSR_FS_PERMISSION_FAULT_L1) || ((fsr & 0x060f) == FSR_FS_PERMISSION_FAULT_L2))) {
333 		panic("PXN violation trying to execute user-mapped address 0x%08" B_PRIxADDR " from kernel mode\n",
334 			far);
335 	} else if (!isExec && ((fsr & 0x060f) == FSR_FS_ALIGNMENT_FAULT)) {
336 		panic("unhandled alignment exception\n");
337 	} else if ((frame->spsr & CPSR_I) != 0) {
338 		// interrupts disabled
339 
340 		// If a page fault handler is installed, we're allowed to be here.
341 		// TODO: Now we are generally allowing user_memcpy() with interrupts
342 		// disabled, which in most cases is a bug. We should add some thread
343 		// flag allowing to explicitly indicate that this handling is desired.
344 		uintptr_t handler = reinterpret_cast<uintptr_t>(thread->fault_handler);
345 		if (thread && thread->fault_handler != 0) {
346 			if (frame->pc != handler) {
347 				frame->pc = handler;
348 				return;
349 			}
350 
351 			// The fault happened at the fault handler address. This is a
352 			// certain infinite loop.
353 			panic("page fault, interrupts disabled, fault handler loop. "
354 				"Touching address %p from pc %p\n", (void*)far,
355 				(void*)frame->pc);
356 		}
357 
358 		// If we are not running the kernel startup the page fault was not
359 		// allowed to happen and we must panic.
360 		panic("page fault, but interrupts were disabled. Touching address "
361 			"%p from pc %p\n", (void *)far, (void *)frame->pc);
362 		return;
363 	} else if (thread != NULL && thread->page_faults_allowed < 1) {
364 		panic("page fault not allowed at this place. Touching address "
365 			"%p from pc %p\n", (void *)far, (void *)frame->pc);
366 		return;
367 	}
368 
369 	enable_interrupts();
370 
371 	vm_page_fault(far, frame->pc, isWrite, isExec, isUser, &newip);
372 
373 	if (newip != 0) {
374 		// the page fault handler wants us to modify the iframe to set the
375 		// IP the cpu will return to to be this ip
376 		frame->pc = newip;
377 	}
378 }
379 
380 
381 extern "C" void
382 arch_arm_data_abort(struct iframe *frame)
383 {
384 	addr_t dfar = arm_get_dfar();
385 	uint32 dfsr = arm_get_dfsr();
386 	bool isWrite = (dfsr & FSR_WNR) == FSR_WNR;
387 
388 	arch_arm_page_fault(frame, dfar, dfsr, isWrite, false);
389 }
390 
391 
392 extern "C" void
393 arch_arm_prefetch_abort(struct iframe *frame)
394 {
395 	addr_t ifar = arm_get_ifar();
396 	uint32 ifsr = arm_get_ifsr();
397 
398 	arch_arm_page_fault(frame, ifar, ifsr, false, true);
399 }
400 
401 
402 extern "C" void
403 arch_arm_irq(struct iframe *iframe)
404 {
405 	IFrameScope scope(iframe);
406 
407 	InterruptController *ic = InterruptController::Get();
408 	if (ic != NULL)
409 		ic->HandleInterrupt();
410 
411 	Thread* thread = thread_get_current_thread();
412 	cpu_status state = disable_interrupts();
413 	if (thread->cpu->invoke_scheduler) {
414 		SpinLocker schedulerLocker(thread->scheduler_lock);
415 		scheduler_reschedule(B_THREAD_READY);
416 		schedulerLocker.Unlock();
417 		restore_interrupts(state);
418 	} else if (thread->post_interrupt_callback != NULL) {
419 		void (*callback)(void*) = thread->post_interrupt_callback;
420 		void* data = thread->post_interrupt_data;
421 
422 		thread->post_interrupt_callback = NULL;
423 		thread->post_interrupt_data = NULL;
424 
425 		restore_interrupts(state);
426 
427 		callback(data);
428 	}
429 }
430 
431 
432 extern "C" void
433 arch_arm_fiq(struct iframe *iframe)
434 {
435 	IFrameScope scope(iframe);
436 
437 	panic("FIQ not implemented yet!");
438 }
439