xref: /haiku/src/system/kernel/arch/riscv64/arch_int.cpp (revision 02354704729d38c3b078c696adc1bbbd33cbcf72)
1 /*
2  * Copyright 2003-2011, Haiku, Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *      Adrien Destugues, pulkomandy@pulkomandy.tk
7  */
8 
9 
10 #include <int.h>
11 #include <cpu.h>
12 #include <thread.h>
13 #include <vm/vm_priv.h>
14 #include <ksyscalls.h>
15 #include <syscall_numbers.h>
16 #include <arch_cpu_defs.h>
17 #include <arch_thread_types.h>
18 #include <arch/debug.h>
19 #include <util/AutoLock.h>
20 #include <Htif.h>
21 #include <Plic.h>
22 #include <Clint.h>
23 #include <AutoDeleterDrivers.h>
24 #include "RISCV64VMTranslationMap.h"
25 
26 #include <algorithm>
27 
28 
29 static uint32 sBootHartId = 0;
30 static int32 sPlicContextOfs = 0;
31 
32 
33 extern "C" void SVec();
34 extern "C" void SVecU();
35 
36 
37 //#pragma mark debug output
38 
39 void
40 WriteMode(int mode)
41 {
42 	switch (mode) {
43 		case modeU: dprintf("u"); break;
44 		case modeS: dprintf("s"); break;
45 		case modeM: dprintf("m"); break;
46 		default: dprintf("%d", mode);
47 	}
48 }
49 
50 
51 void
52 WriteModeSet(uint32_t val)
53 {
54 	bool first = true;
55 	dprintf("{");
56 	for (int i = 0; i < 32; i++) {
57 		if (((1LL << i) & val) != 0) {
58 			if (first) first = false; else dprintf(", ");
59 			WriteMode(i);
60 		}
61 	}
62 	dprintf("}");
63 }
64 
65 
66 void
67 WriteMstatus(uint64_t val)
68 {
69 	MstatusReg status(val);
70 	dprintf("(");
71 	dprintf("ie: "); WriteModeSet(status.ie);
72 	dprintf(", pie: "); WriteModeSet(status.pie);
73 	dprintf(", spp: "); WriteMode(status.spp);
74 	dprintf(", mpp: "); WriteMode(status.mpp);
75 	dprintf(", sum: %d", (int)status.sum);
76 	dprintf(")");
77 }
78 
79 
80 void
81 WriteSstatus(uint64_t val)
82 {
83 	SstatusReg status(val);
84 	dprintf("(");
85 	dprintf("ie: "); WriteModeSet(status.ie);
86 	dprintf(", pie: "); WriteModeSet(status.pie);
87 	dprintf(", spp: "); WriteMode(status.spp);
88 	dprintf(", sum: %d", (int)status.sum);
89 	dprintf(")");
90 }
91 
92 
93 void
94 WriteInterrupt(uint64_t val)
95 {
96 	switch (val) {
97 		case 0 + modeU: dprintf("uSoft"); break;
98 		case 0 + modeS: dprintf("sSoft"); break;
99 		case 0 + modeM: dprintf("mSoft"); break;
100 		case 4 + modeU: dprintf("uTimer"); break;
101 		case 4 + modeS: dprintf("sTimer"); break;
102 		case 4 + modeM: dprintf("mTimer"); break;
103 		case 8 + modeU: dprintf("uExtern"); break;
104 		case 8 + modeS: dprintf("sExtern"); break;
105 		case 8 + modeM: dprintf("mExtern"); break;
106 		default: dprintf("%" B_PRId64, val);
107 	}
108 }
109 
110 
111 void
112 WriteInterruptSet(uint64_t val)
113 {
114 	bool first = true;
115 	dprintf("{");
116 	for (int i = 0; i < 64; i++) {
117 		if (((1LL << i) & val) != 0) {
118 			if (first) first = false; else dprintf(", ");
119 			WriteInterrupt(i);
120 		}
121 	}
122 	dprintf("}");
123 }
124 
125 
126 void
127 WriteCause(uint64_t cause)
128 {
129 	if ((cause & causeInterrupt) == 0) {
130 		dprintf("exception ");
131 		switch (cause) {
132 			case causeExecMisalign: dprintf("execMisalign"); break;
133 			case causeExecAccessFault: dprintf("execAccessFault"); break;
134 			case causeIllegalInst: dprintf("illegalInst"); break;
135 			case causeBreakpoint: dprintf("breakpoint"); break;
136 			case causeLoadMisalign: dprintf("loadMisalign"); break;
137 			case causeLoadAccessFault: dprintf("loadAccessFault"); break;
138 			case causeStoreMisalign: dprintf("storeMisalign"); break;
139 			case causeStoreAccessFault: dprintf("storeAccessFault"); break;
140 			case causeUEcall: dprintf("uEcall"); break;
141 			case causeSEcall: dprintf("sEcall"); break;
142 			case causeMEcall: dprintf("mEcall"); break;
143 			case causeExecPageFault: dprintf("execPageFault"); break;
144 			case causeLoadPageFault: dprintf("loadPageFault"); break;
145 			case causeStorePageFault: dprintf("storePageFault"); break;
146 			default: dprintf("%" B_PRId64, cause);
147 			}
148 	} else {
149 		dprintf("interrupt "); WriteInterrupt(cause & ~causeInterrupt);
150 	}
151 }
152 
153 
154 void
155 WriteTrapInfo()
156 {
157 	InterruptsLocker locker;
158 	dprintf("STrap("); WriteCause(Scause()); dprintf(")\n");
159 	dprintf("  sstatus: "); WriteSstatus(Sstatus()); dprintf("\n");
160 	dprintf("  sie: "); WriteInterruptSet(Sie()); dprintf("\n");
161 	dprintf("  sip: "); WriteInterruptSet(Sip()); dprintf("\n");
162 	//dprintf("  stval: "); WritePC(Stval()); dprintf("\n");
163 	dprintf("  stval: 0x%" B_PRIx64 "\n", Stval());
164 	dprintf("  tp: 0x%" B_PRIxADDR "(%s)\n", Tp(),
165 		thread_get_current_thread()->name);
166 }
167 
168 
169 //#pragma mark -
170 
171 static void
172 SendSignal(debug_exception_type type, uint32 signalNumber, int32 signalCode,
173 	addr_t signalAddress = 0, int32 signalError = B_ERROR)
174 {
175 	if (SstatusReg(Sstatus()).spp == modeU) {
176 		struct sigaction action;
177 		Thread* thread = thread_get_current_thread();
178 
179 		WriteTrapInfo();
180 		DoStackTrace(Fp(), 0);
181 
182 		enable_interrupts();
183 
184 		// If the thread has a signal handler for the signal, we simply send it
185 		// the signal. Otherwise we notify the user debugger first.
186 		if ((sigaction(signalNumber, NULL, &action) == 0
187 				&& action.sa_handler != SIG_DFL
188 				&& action.sa_handler != SIG_IGN)
189 			|| user_debug_exception_occurred(type, signalNumber)) {
190 			Signal signal(signalNumber, signalCode, signalError,
191 				thread->team->id);
192 			signal.SetAddress((void*)signalAddress);
193 			send_signal_to_thread(thread, signal, 0);
194 		}
195 	} else {
196 		WriteTrapInfo();
197 		panic("Unexpected exception occurred in kernel mode!");
198 	}
199 }
200 
201 
202 static void
203 AfterInterrupt()
204 {
205 	if (debug_debugger_running())
206 		return;
207 
208 	Thread* thread = thread_get_current_thread();
209 	cpu_status state = disable_interrupts();
210 	if (thread->cpu->invoke_scheduler) {
211 		SpinLocker schedulerLocker(thread->scheduler_lock);
212 		scheduler_reschedule(B_THREAD_READY);
213 		schedulerLocker.Unlock();
214 		restore_interrupts(state);
215 	} else if (thread->post_interrupt_callback != NULL) {
216 		void (*callback)(void*) = thread->post_interrupt_callback;
217 		void* data = thread->post_interrupt_data;
218 
219 		thread->post_interrupt_callback = NULL;
220 		thread->post_interrupt_data = NULL;
221 
222 		restore_interrupts(state);
223 
224 		callback(data);
225 	}
226 }
227 
228 
229 static bool
230 SetAccessedFlags(addr_t addr, bool isWrite)
231 {
232 	VMAddressSpacePutter addressSpace;
233 	if (IS_KERNEL_ADDRESS(addr))
234 		addressSpace.SetTo(VMAddressSpace::GetKernel());
235 	else if (IS_USER_ADDRESS(addr))
236 		addressSpace.SetTo(VMAddressSpace::GetCurrent());
237 
238 	if(!addressSpace.IsSet())
239 		return false;
240 
241 	RISCV64VMTranslationMap* map
242 		= (RISCV64VMTranslationMap*)addressSpace->TranslationMap();
243 
244 	phys_addr_t physAdr;
245 	uint32 pageFlags;
246 	map->QueryInterrupt(addr, &physAdr, &pageFlags);
247 	if (isWrite) {
248 		if (
249 			((B_WRITE_AREA | B_KERNEL_WRITE_AREA) & pageFlags) != 0
250 			&& ((PAGE_ACCESSED | PAGE_MODIFIED) & pageFlags)
251 				!= (PAGE_ACCESSED | PAGE_MODIFIED)
252 		) {
253 			map->SetFlags(addr, PAGE_ACCESSED | PAGE_MODIFIED);
254 /*
255 			dprintf("SetAccessedFlags(%#" B_PRIxADDR ", %d)\n", addr, isWrite);
256 */
257 			return true;
258 		}
259 	} else {
260 		if (
261 			((B_READ_AREA | B_KERNEL_READ_AREA) & pageFlags) != 0
262 			&& (PAGE_ACCESSED & pageFlags) == 0
263 		) {
264 			map->SetFlags(addr, PAGE_ACCESSED);
265 /*
266 			dprintf("SetAccessedFlags(%#" B_PRIxADDR ", %d)\n", addr, isWrite);
267 */
268 			return true;
269 		}
270 	}
271 	return false;
272 }
273 
274 
275 static void
276 WriteProtection(uint32 flags)
277 {
278 	dprintf("kernel: {");
279 	if (B_KERNEL_READ_AREA & flags) dprintf("R");
280 	if (B_KERNEL_WRITE_AREA & flags) dprintf("W");
281 	if (B_KERNEL_EXECUTE_AREA & flags) dprintf("X");
282 	if (B_KERNEL_STACK_AREA & flags) dprintf("S");
283 	dprintf("}, user: {");
284 	if (B_READ_AREA & flags) dprintf("R");
285 	if (B_WRITE_AREA & flags) dprintf("W");
286 	if (B_EXECUTE_AREA & flags) dprintf("X");
287 	if (B_STACK_AREA & flags) dprintf("S");
288 	dprintf("}");
289 }
290 
291 
292 // TODO: needs moved into an arch-agnostic location?
293 
294 template<typename F>
295 class ScopeExit
296 {
297 public:
298 	explicit ScopeExit(F&& fn) : fFn(fn)
299 	{
300 	}
301 
302 	~ScopeExit()
303 	{
304 		fFn();
305 	}
306 
307 	ScopeExit(ScopeExit&& other) : fFn(std::move(other.fFn))
308 	{
309 	}
310 
311 private:
312 	ScopeExit(const ScopeExit&);
313 	ScopeExit& operator=(const ScopeExit&);
314 
315 private:
316 	F fFn;
317 };
318 
319 template<typename F>
320 ScopeExit<F> MakeScopeExit(F&& fn)
321 {
322 	return ScopeExit<F>(std::move(fn));
323 }
324 
325 
326 extern "C" void
327 STrap(iframe* frame)
328 {
329 	// dprintf("STrap("); WriteCause(Scause()); dprintf(")\n");
330 
331 	SstatusReg status(Sstatus());
332 	uint64 cause = Scause();
333 
334 	const auto& statusRestorer = MakeScopeExit([&]() {
335 		SetSstatus(status.val);
336 	});
337 
338 	switch (cause) {
339 		case causeExecPageFault:
340 		case causeLoadPageFault:
341 		case causeStorePageFault: {
342 			if (SetAccessedFlags(Stval(), cause == causeStorePageFault))
343 				return;
344 		}
345 	}
346 
347 	if (status.spp == modeU) {
348 		thread_get_current_thread()->arch_info.userFrame = frame;
349 		thread_at_kernel_entry(system_time());
350 	}
351 	const auto& kernelExit = MakeScopeExit([&]() {
352 		if (status.spp == modeU) {
353 			disable_interrupts();
354 			if ((thread_get_current_thread()->flags
355 				& (THREAD_FLAGS_SIGNALS_PENDING
356 				| THREAD_FLAGS_DEBUG_THREAD
357 				| THREAD_FLAGS_TRAP_FOR_CORE_DUMP)) != 0) {
358 				enable_interrupts();
359 				thread_at_kernel_exit();
360 			} else {
361 				thread_at_kernel_exit_no_signals();
362 			}
363 			thread_get_current_thread()->arch_info.userFrame = NULL;
364 		}
365 	});
366 
367 	switch (cause) {
368 		case causeIllegalInst: {
369 			return SendSignal(B_INVALID_OPCODE_EXCEPTION, SIGILL, ILL_ILLOPC,
370 				frame->epc);
371 		}
372 		case causeExecMisalign:
373 		case causeLoadMisalign:
374 		case causeStoreMisalign: {
375 			return SendSignal(B_ALIGNMENT_EXCEPTION, SIGBUS, BUS_ADRALN,
376 				Stval());
377 		}
378 		// case causeBreakpoint:
379 		case causeExecAccessFault:
380 		case causeLoadAccessFault:
381 		case causeStoreAccessFault: {
382 			return SendSignal(B_SEGMENT_VIOLATION, SIGBUS, BUS_ADRERR,
383 				Stval());
384 		}
385 		case causeExecPageFault:
386 		case causeLoadPageFault:
387 		case causeStorePageFault: {
388 			uint64 stval = Stval();
389 
390 			if (debug_debugger_running()) {
391 				Thread* thread = thread_get_current_thread();
392 				if (thread != NULL) {
393 					cpu_ent* cpu = &gCPU[smp_get_current_cpu()];
394 					if (cpu->fault_handler != 0) {
395 						debug_set_page_fault_info(stval, frame->epc,
396 							(cause == causeStorePageFault)
397 								? DEBUG_PAGE_FAULT_WRITE : 0);
398 						frame->epc = cpu->fault_handler;
399 						frame->sp = cpu->fault_handler_stack_pointer;
400 						return;
401 					}
402 
403 					if (thread->fault_handler != 0) {
404 						kprintf("ERROR: thread::fault_handler used in kernel "
405 							"debugger!\n");
406 						debug_set_page_fault_info(stval, frame->epc,
407 							cause == causeStorePageFault
408 								? DEBUG_PAGE_FAULT_WRITE : 0);
409 						frame->epc = (addr_t)thread->fault_handler;
410 						return;
411 					}
412 				}
413 
414 				panic("page fault in debugger without fault handler! Touching "
415 					"address %p from ip %p\n", (void*)stval, (void*)frame->epc);
416 				return;
417 			}
418 
419 			if (status.pie == 0) {
420 				WriteTrapInfo();
421 				panic("page fault with interrupts disabled@!dump_virt_page %#" B_PRIx64, stval);
422 			}
423 
424 			addr_t newIP = 0;
425 			enable_interrupts();
426 			vm_page_fault(stval, frame->epc, cause == causeStorePageFault,
427 				cause == causeExecPageFault, status.spp == modeU, &newIP);
428 			if (newIP != 0)
429 				frame->epc = newIP;
430 
431 			return;
432 		}
433 		case causeInterrupt + sTimerInt: {
434 			timer_interrupt();
435 			AfterInterrupt();
436 			return;
437 		}
438 		case causeInterrupt + sExternInt: {
439 			// TODO: get PLIC context ID mapping for HARD ID from FDT?
440 			uint64 irq = gPlicRegs->contexts[modeS + 2 * sBootHartId
441 				+ sPlicContextOfs].claimAndComplete;
442 			int_io_interrupt_handler(irq, true);
443 			gPlicRegs->contexts[modeS + 2*sBootHartId
444 				+ sPlicContextOfs].claimAndComplete = irq;
445 			AfterInterrupt();
446 			return;
447 		}
448 		case causeUEcall: {
449 			frame->epc += 4; // skip ecall
450 			uint64 syscall = frame->t0;
451 			uint64 args[20];
452 			if (syscall < (uint64)kSyscallCount) {
453 				uint32 argCnt = kExtendedSyscallInfos[syscall].parameter_count;
454 				memcpy(&args[0], &frame->a0,
455 					sizeof(uint64)*std::min<uint32>(argCnt, 8));
456 				if (argCnt > 8) {
457 					if (status_t res = user_memcpy(&args[8], (void*)frame->sp,
458 						sizeof(uint64)*(argCnt - 8)) < B_OK) {
459 						dprintf("can't read syscall arguments on user "
460 							"stack\n");
461 						frame->a0 = res;
462 						return;
463 					}
464 				}
465 			}
466 /*
467 			switch (syscall) {
468 				case SYSCALL_READ_PORT_ETC:
469 				case SYSCALL_WRITE_PORT_ETC:
470 					WriteTrapInfo();
471 					DoStackTrace(Fp(), 0);
472 					break;
473 			}
474 */
475 			// dprintf("syscall: %s\n", kExtendedSyscallInfos[syscall].name);
476 			enable_interrupts();
477 			uint64 returnValue = 0;
478 			syscall_dispatcher(syscall, (void*)args, &returnValue);
479 			frame->a0 = returnValue;
480 			return;
481 		}
482 	}
483 	WriteTrapInfo();
484 	panic("unhandled STrap");
485 }
486 
487 
488 //#pragma mark -
489 
490 status_t
491 arch_int_init(kernel_args* args)
492 {
493 	sBootHartId = args->arch_args.bootHart;
494 	sPlicContextOfs = (sBootHartId == 0) ? 0 : -1;
495 
496 	// TODO: Kernel mode FPU handling needs improved?
497 	SetStvec((uint64)SVec);
498 	SstatusReg sstatus(Sstatus());
499 	sstatus.ie = 0;
500 	sstatus.fs = extStatusInitial; // enable FPU
501 	sstatus.xs = extStatusOff;
502 	SetSstatus(sstatus.val);
503 	SetSie(Sie() | (1 << sTimerInt) | (1 << sExternInt));
504 
505 	// TODO: read from FDT
506 	reserve_io_interrupt_vectors(128, 0, INTERRUPT_TYPE_IRQ);
507 
508 	gPlicRegs->contexts[modeS + 2*sBootHartId + sPlicContextOfs].priorityThreshold = 0;
509 	return B_OK;
510 }
511 
512 
513 status_t
514 arch_int_init_post_vm(kernel_args* args)
515 {
516 	return B_OK;
517 }
518 
519 
520 status_t
521 arch_int_init_post_device_manager(struct kernel_args* args)
522 {
523 	return B_OK;
524 }
525 
526 
527 status_t
528 arch_int_init_io(kernel_args* args)
529 {
530 	return B_OK;
531 }
532 
533 
534 void
535 arch_int_enable_io_interrupt(int irq)
536 {
537 	dprintf("arch_int_enable_io_interrupt(%d)\n", irq);
538 	gPlicRegs->priority[irq] = 1;
539 	gPlicRegs->enable[modeS + 2*sBootHartId + sPlicContextOfs][irq / 32] |= 1 << (irq % 32);
540 }
541 
542 
543 void
544 arch_int_disable_io_interrupt(int irq)
545 {
546 	dprintf("arch_int_disable_io_interrupt(%d)\n", irq);
547 	gPlicRegs->priority[irq] = 0;
548 	gPlicRegs->enable[modeS + 2*sBootHartId + sPlicContextOfs][irq / 32] &= ~(1 << (irq % 32));
549 }
550 
551 
552 void
553 arch_int_assign_to_cpu(int32 irq, int32 cpu)
554 {
555 	// SMP not yet supported
556 }
557