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