xref: /haiku/src/system/kernel/arch/arm/arch_int.cpp (revision 52f7c9389475e19fc21487b38064b4390eeb6fea)
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 
45 #define TRACE_ARCH_INT
46 #ifdef TRACE_ARCH_INT
47 #	define TRACE(x) dprintf x
48 #else
49 #	define TRACE(x) ;
50 #endif
51 
52 #define VECTORPAGE_SIZE		64
53 #define USER_VECTOR_ADDR_LOW	0x00000000
54 #define USER_VECTOR_ADDR_HIGH	0xffff0000
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 //static fdt_module_info *sFdtModule;
64 
65 // An iframe stack used in the early boot process when we don't have
66 // threads yet.
67 struct iframe_stack gBootFrameStack;
68 
69 
70 void
71 arch_int_enable_io_interrupt(int irq)
72 {
73 	TRACE(("arch_int_enable_io_interrupt(%d)\n", irq));
74 	InterruptController *ic = InterruptController::Get();
75 	if (ic != NULL)
76 		ic->EnableInterrupt(irq);
77 }
78 
79 
80 void
81 arch_int_disable_io_interrupt(int irq)
82 {
83 	TRACE(("arch_int_disable_io_interrupt(%d)\n", irq));
84 	InterruptController *ic = InterruptController::Get();
85 	if (ic != NULL)
86 		ic->DisableInterrupt(irq);
87 }
88 
89 
90 /* arch_int_*_interrupts() and friends are in arch_asm.S */
91 
92 int32
93 arch_int_assign_to_cpu(int32 irq, int32 cpu)
94 {
95 	// Not yet supported.
96 	return 0;
97 }
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 SP=%08lx LR=%08lx  PC=%08lx CPSR=%08lx\n",
112 		frame->r8, frame->r9, frame->r10, frame->r11,
113 		frame->r12, frame->svc_sp, frame->svc_lr, frame->pc, 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 	if (sVectorPageArea < 0)
135 		panic("vector page could not be created!");
136 
137 	// clone it at a fixed address with user read/only permissions
138 	sUserVectorPageAddress = (addr_t*)USER_VECTOR_ADDR_HIGH;
139 	sUserVectorPageArea = clone_area("user_vectorpage",
140 		(void **)&sUserVectorPageAddress, B_EXACT_ADDRESS,
141 		B_READ_AREA | B_EXECUTE_AREA, sVectorPageArea);
142 
143 	if (sUserVectorPageArea < 0)
144 		panic("user vector page @ %p could not be created (%lx)!",
145 			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 	if (strncmp(args->arch_args.interrupt_controller.kind, INTC_KIND_GICV2,
165 		sizeof(args->arch_args.interrupt_controller.kind)) == 0) {
166 		InterruptController *ic = new(std::nothrow) GICv2InterruptController(
167 			args->arch_args.interrupt_controller.regs1.start,
168 			args->arch_args.interrupt_controller.regs2.start);
169 		if (ic == NULL)
170 			return B_NO_MEMORY;
171 	} else if (strncmp(args->arch_args.interrupt_controller.kind, INTC_KIND_OMAP3,
172 		sizeof(args->arch_args.interrupt_controller.kind)) == 0) {
173 		InterruptController *ic = new(std::nothrow) OMAP3InterruptController(
174 			args->arch_args.interrupt_controller.regs1.start);
175 		if (ic == NULL)
176 			return B_NO_MEMORY;
177 	} else if (strncmp(args->arch_args.interrupt_controller.kind, INTC_KIND_PXA,
178 		sizeof(args->arch_args.interrupt_controller.kind)) == 0) {
179 		InterruptController *ic = new(std::nothrow) PXAInterruptController(
180 			args->arch_args.interrupt_controller.regs1.start);
181 		if (ic == NULL)
182 			return B_NO_MEMORY;
183 	} else {
184 		panic("No interrupt controllers found!\n");
185 	}
186 
187 	return B_OK;
188 }
189 
190 
191 status_t
192 arch_int_init_io(kernel_args* args)
193 {
194 	return B_OK;
195 }
196 
197 
198 status_t
199 arch_int_init_post_device_manager(struct kernel_args *args)
200 {
201 	return B_ENTRY_NOT_FOUND;
202 }
203 
204 
205 // Little helper class for handling the
206 // iframe stack as used by KDL.
207 class IFrameScope {
208 public:
209 	IFrameScope(struct iframe *iframe) {
210 		fThread = thread_get_current_thread();
211 		if (fThread)
212 			arm_push_iframe(&fThread->arch_info.iframes, iframe);
213 		else
214 			arm_push_iframe(&gBootFrameStack, iframe);
215 	}
216 
217 	virtual ~IFrameScope() {
218 		// pop iframe
219 		if (fThread)
220 			arm_pop_iframe(&fThread->arch_info.iframes);
221 		else
222 			arm_pop_iframe(&gBootFrameStack);
223 	}
224 private:
225 	Thread* fThread;
226 };
227 
228 
229 extern "C" void
230 arch_arm_undefined(struct iframe *iframe)
231 {
232 	print_iframe("Undefined Instruction", iframe);
233 	IFrameScope scope(iframe); // push/pop iframe
234 
235 	panic("not handled!");
236 }
237 
238 
239 extern "C" void
240 arch_arm_syscall(struct iframe *iframe)
241 {
242 #ifdef TRACE_ARCH_INT
243 	print_iframe("Software interrupt", iframe);
244 #endif
245 
246 	uint32_t syscall = *(uint32_t *)(iframe->pc-4) & 0x00ffffff;
247 	TRACE(("syscall number: %d\n", syscall));
248 
249 	uint32_t args[20];
250 	if (syscall < kSyscallCount) {
251 		TRACE(("syscall(%s,%d)\n",
252 			kExtendedSyscallInfos[syscall].name,
253 			kExtendedSyscallInfos[syscall].parameter_count));
254 
255 		int argSize = kSyscallInfos[syscall].parameter_size;
256 		memcpy(args, &iframe->r0, std::min<int>(argSize, 4 * sizeof(uint32)));
257 		if (argSize > 4 * sizeof(uint32)) {
258 			status_t res = user_memcpy(&args[4], (void *)iframe->usr_sp,
259 				(argSize - 4 * sizeof(uint32)));
260 			if (res < B_OK) {
261 				dprintf("can't read syscall arguments on user stack\n");
262 				iframe->r0 = res;
263 				return;
264 			}
265 		}
266 	}
267 
268 	enable_interrupts();
269 
270 	uint64 returnValue = 0;
271 	syscall_dispatcher(syscall, (void*)args, &returnValue);
272 
273 	TRACE(("returning %" B_PRId64 "\n", returnValue));
274 	iframe->r0 = returnValue;
275 }
276 
277 
278 extern "C" void
279 arch_arm_data_abort(struct iframe *frame)
280 {
281 	Thread *thread = thread_get_current_thread();
282 	bool isUser = (frame->spsr & CPSR_MODE_MASK) == CPSR_MODE_USR;
283 	int32 fsr = arm_get_fsr();
284 	addr_t far = arm_get_far();
285 	bool isWrite = (fsr & FSR_WNR) == FSR_WNR;
286 	addr_t newip = 0;
287 
288 #ifdef TRACE_ARCH_INT
289 	print_iframe("Data Abort", frame);
290 	dprintf("FAR: %08lx, isWrite: %d, thread: %s\n", far, isWrite, thread->name);
291 #endif
292 
293 	IFrameScope scope(frame);
294 
295 	if (debug_debugger_running()) {
296 		// If this CPU or this thread has a fault handler, we're allowed to be
297 		// here.
298 		if (thread != NULL) {
299 			cpu_ent* cpu = &gCPU[smp_get_current_cpu()];
300 
301 			if (cpu->fault_handler != 0) {
302 				debug_set_page_fault_info(far, frame->pc,
303 					isWrite ? DEBUG_PAGE_FAULT_WRITE : 0);
304 				frame->svc_sp = cpu->fault_handler_stack_pointer;
305 				frame->pc = cpu->fault_handler;
306 				return;
307 			}
308 
309 			if (thread->fault_handler != 0) {
310 				kprintf("ERROR: thread::fault_handler used in kernel "
311 					"debugger!\n");
312 				debug_set_page_fault_info(far, frame->pc,
313 						isWrite ? DEBUG_PAGE_FAULT_WRITE : 0);
314 				frame->pc = reinterpret_cast<uintptr_t>(thread->fault_handler);
315 				return;
316 			}
317 		}
318 
319 		// otherwise, not really
320 		panic("page fault in debugger without fault handler! Touching "
321 			"address %p from pc %p\n", (void *)far, (void *)frame->pc);
322 		return;
323 	} else if ((frame->spsr & (1 << 7)) != 0) {
324 		// interrupts disabled
325 
326 		// If a page fault handler is installed, we're allowed to be here.
327 		// TODO: Now we are generally allowing user_memcpy() with interrupts
328 		// disabled, which in most cases is a bug. We should add some thread
329 		// flag allowing to explicitly indicate that this handling is desired.
330 		uintptr_t handler = reinterpret_cast<uintptr_t>(thread->fault_handler);
331 		if (thread && thread->fault_handler != 0) {
332 			if (frame->pc != handler) {
333 				frame->pc = handler;
334 				return;
335 			}
336 
337 			// The fault happened at the fault handler address. This is a
338 			// certain infinite loop.
339 			panic("page fault, interrupts disabled, fault handler loop. "
340 				"Touching address %p from pc %p\n", (void*)far,
341 				(void*)frame->pc);
342 		}
343 
344 		// If we are not running the kernel startup the page fault was not
345 		// allowed to happen and we must panic.
346 		panic("page fault, but interrupts were disabled. Touching address "
347 			"%p from pc %p\n", (void *)far, (void *)frame->pc);
348 		return;
349 	} else if (thread != NULL && thread->page_faults_allowed < 1) {
350 		panic("page fault not allowed at this place. Touching address "
351 			"%p from pc %p\n", (void *)far, (void *)frame->pc);
352 		return;
353 	}
354 
355 	enable_interrupts();
356 
357 	vm_page_fault(far, frame->pc, isWrite, false, isUser, &newip);
358 
359 	if (newip != 0) {
360 		// the page fault handler wants us to modify the iframe to set the
361 		// IP the cpu will return to to be this ip
362 		frame->pc = newip;
363 	}
364 }
365 
366 
367 extern "C" void
368 arch_arm_prefetch_abort(struct iframe *frame)
369 {
370 	Thread *thread = thread_get_current_thread();
371 	bool isUser = (frame->spsr & CPSR_MODE_MASK) == CPSR_MODE_USR;
372 	addr_t newip = 0;
373 
374 #ifdef TRACE_ARCH_INT
375 	print_iframe("Prefetch Abort", frame);
376 	dprintf("thread: %s\n", thread->name);
377 #endif
378 
379 	IFrameScope scope(frame);
380 
381 	if (debug_debugger_running()) {
382 		// If this CPU or this thread has a fault handler, we're allowed to be
383 		// here.
384 		if (thread != NULL) {
385 			cpu_ent* cpu = &gCPU[smp_get_current_cpu()];
386 
387 			if (cpu->fault_handler != 0) {
388 				debug_set_page_fault_info(frame->pc, frame->pc, 0);
389 				frame->svc_sp = cpu->fault_handler_stack_pointer;
390 				frame->pc = cpu->fault_handler;
391 				return;
392 			}
393 
394 			if (thread->fault_handler != 0) {
395 				kprintf("ERROR: thread::fault_handler used in kernel "
396 					"debugger!\n");
397 				debug_set_page_fault_info(frame->pc, frame->pc, 0);
398 				frame->pc = reinterpret_cast<uintptr_t>(thread->fault_handler);
399 				return;
400 			}
401 		}
402 
403 		// otherwise, not really
404 		panic("page fault in debugger without fault handler! Prefetch abort at %p\n",
405 			(void *)frame->pc);
406 		return;
407 	} else if ((frame->spsr & (1 << 7)) != 0) {
408 		// interrupts disabled
409 
410 		// If a page fault handler is installed, we're allowed to be here.
411 		uintptr_t handler = reinterpret_cast<uintptr_t>(thread->fault_handler);
412 		if (thread && thread->fault_handler != 0) {
413 			if (frame->pc != handler) {
414 				frame->pc = handler;
415 				return;
416 			}
417 
418 			// The fault happened at the fault handler address. This is a
419 			// certain infinite loop.
420 			panic("page fault, interrupts disabled, fault handler loop. "
421 				"Prefetch abort at %p\n", (void*)frame->pc);
422 		}
423 
424 		// If we are not running the kernel startup the page fault was not
425 		// allowed to happen and we must panic.
426 		panic("page fault, but interrupts were disabled. Prefetch abort at %p\n",
427 			(void *)frame->pc);
428 		return;
429 	} else if (thread != NULL && thread->page_faults_allowed < 1) {
430 		panic("page fault not allowed at this place. Prefetch abort at %p\n",
431 			(void *)frame->pc);
432 		return;
433 	}
434 
435 	enable_interrupts();
436 
437 	vm_page_fault(frame->pc, frame->pc, false, true, isUser, &newip);
438 
439 	if (newip != 0) {
440 		frame->pc = newip;
441 	}
442 }
443 
444 
445 extern "C" void
446 arch_arm_irq(struct iframe *iframe)
447 {
448 	IFrameScope scope(iframe);
449 
450 	InterruptController *ic = InterruptController::Get();
451 	if (ic != NULL)
452 		ic->HandleInterrupt();
453 
454 	Thread* thread = thread_get_current_thread();
455 	cpu_status state = disable_interrupts();
456 	if (thread->cpu->invoke_scheduler) {
457 		SpinLocker schedulerLocker(thread->scheduler_lock);
458 		scheduler_reschedule(B_THREAD_READY);
459 		schedulerLocker.Unlock();
460 		restore_interrupts(state);
461 	} else if (thread->post_interrupt_callback != NULL) {
462 		void (*callback)(void*) = thread->post_interrupt_callback;
463 		void* data = thread->post_interrupt_data;
464 
465 		thread->post_interrupt_callback = NULL;
466 		thread->post_interrupt_data = NULL;
467 
468 		restore_interrupts(state);
469 
470 		callback(data);
471 	}
472 }
473 
474 
475 extern "C" void
476 arch_arm_fiq(struct iframe *iframe)
477 {
478 	IFrameScope scope(iframe);
479 
480 	panic("FIQ not implemented yet!");
481 }
482