1/* 2 * Copyright 2014, General Dynamics C4 Systems 3 * 4 * This software may be distributed and modified according to the terms of 5 * the GNU General Public License version 2. Note that NO WARRANTY is provided. 6 * See "LICENSE_GPLv2.txt" for details. 7 * 8 * @TAG(GD_GPL) 9 */ 10 11#include <types.h> 12#include <benchmark/benchmark.h> 13#include <arch/benchmark.h> 14#include <benchmark/benchmark_track.h> 15#include <benchmark/benchmark_utilisation.h> 16#include <api/syscall.h> 17#include <api/failures.h> 18#include <api/faults.h> 19#include <kernel/cspace.h> 20#include <kernel/faulthandler.h> 21#include <kernel/thread.h> 22#include <kernel/vspace.h> 23#include <machine/io.h> 24#include <plat/machine/hardware.h> 25#include <object/interrupt.h> 26#include <model/statedata.h> 27#include <string.h> 28#include <kernel/traps.h> 29#include <arch/machine.h> 30 31#ifdef CONFIG_DEBUG_BUILD 32#include <arch/machine/capdl.h> 33#endif 34 35/* The haskell function 'handleEvent' is split into 'handleXXX' variants 36 * for each event causing a kernel entry */ 37 38exception_t 39handleInterruptEntry(void) 40{ 41 irq_t irq; 42 43 irq = getActiveIRQ(); 44 if (SMP_TERNARY(clh_is_self_in_queue(), 1)) { 45 updateTimestamp(false); 46 checkBudget(); 47 } 48 49 if (irq != irqInvalid) { 50 handleInterrupt(irq); 51 Arch_finaliseInterrupt(); 52 } else { 53#ifdef CONFIG_IRQ_REPORTING 54 userError("Spurious interrupt!"); 55#endif 56 handleSpuriousIRQ(); 57 } 58 59 if (SMP_TERNARY(clh_is_self_in_queue(), 1)) { 60 schedule(); 61 activateThread(); 62 } 63 64 return EXCEPTION_NONE; 65} 66 67exception_t 68handleUnknownSyscall(word_t w) 69{ 70#ifdef CONFIG_PRINTING 71 if (w == SysDebugPutChar) { 72 kernel_putchar(getRegister(NODE_STATE(ksCurThread), capRegister)); 73 return EXCEPTION_NONE; 74 } 75 if (w == SysDebugDumpScheduler) { 76#ifdef CONFIG_DEBUG_BUILD 77 debug_dumpScheduler(); 78#endif 79 return EXCEPTION_NONE; 80 } 81#endif 82#ifdef CONFIG_DEBUG_BUILD 83 if (w == SysDebugHalt) { 84 tcb_t * UNUSED tptr = NODE_STATE(ksCurThread); 85 printf("Debug halt syscall from user thread %p \"%s\"\n", tptr, tptr->tcbName); 86 halt(); 87 } 88 if (w == SysDebugSnapshot) { 89 tcb_t * UNUSED tptr = NODE_STATE(ksCurThread); 90 printf("Debug snapshot syscall from user thread %p \"%s\"\n", tptr, tptr->tcbName); 91 capDL(); 92 return EXCEPTION_NONE; 93 } 94 if (w == SysDebugCapIdentify) { 95 word_t cptr = getRegister(NODE_STATE(ksCurThread), capRegister); 96 lookupCapAndSlot_ret_t lu_ret = lookupCapAndSlot(NODE_STATE(ksCurThread), cptr); 97 word_t cap_type = cap_get_capType(lu_ret.cap); 98 setRegister(NODE_STATE(ksCurThread), capRegister, cap_type); 99 return EXCEPTION_NONE; 100 } 101 102 if (w == SysDebugNameThread) { 103 /* This is a syscall meant to aid debugging, so if anything goes wrong 104 * then assume the system is completely misconfigured and halt */ 105 const char *name; 106 word_t len; 107 word_t cptr = getRegister(NODE_STATE(ksCurThread), capRegister); 108 lookupCapAndSlot_ret_t lu_ret = lookupCapAndSlot(NODE_STATE(ksCurThread), cptr); 109 /* ensure we got a TCB cap */ 110 word_t cap_type = cap_get_capType(lu_ret.cap); 111 if (cap_type != cap_thread_cap) { 112 userError("SysDebugNameThread: cap is not a TCB, halting"); 113 halt(); 114 } 115 /* Add 1 to the IPC buffer to skip the message info word */ 116 name = (const char*)(lookupIPCBuffer(true, NODE_STATE(ksCurThread)) + 1); 117 if (!name) { 118 userError("SysDebugNameThread: Failed to lookup IPC buffer, halting"); 119 halt(); 120 } 121 /* ensure the name isn't too long */ 122 len = strnlen(name, seL4_MsgMaxLength * sizeof(word_t)); 123 if (len == seL4_MsgMaxLength * sizeof(word_t)) { 124 userError("SysDebugNameThread: Name too long, halting"); 125 halt(); 126 } 127 setThreadName(TCB_PTR(cap_thread_cap_get_capTCBPtr(lu_ret.cap)), name); 128 return EXCEPTION_NONE; 129 } 130#endif /* CONFIG_DEBUG_BUILD */ 131 132#ifdef CONFIG_DANGEROUS_CODE_INJECTION 133 if (w == SysDebugRun) { 134 ((void (*) (void *))getRegister(NODE_STATE(ksCurThread), capRegister))((void*)getRegister(NODE_STATE(ksCurThread), msgInfoRegister)); 135 return EXCEPTION_NONE; 136 } 137#endif 138 139#ifdef CONFIG_KERNEL_X86_DANGEROUS_MSR 140 if (w == SysX86DangerousWRMSR) { 141 uint64_t val; 142 uint32_t reg = getRegister(NODE_STATE(ksCurThread), capRegister); 143 if (CONFIG_WORD_SIZE == 32) { 144 val = (uint64_t)getSyscallArg(0, NULL) | ((uint64_t)getSyscallArg(1, NULL) << 32); 145 } else { 146 val = getSyscallArg(0, NULL); 147 } 148 x86_wrmsr(reg, val); 149 return EXCEPTION_NONE; 150 } else if (w == SysX86DangerousRDMSR) { 151 uint64_t val; 152 uint32_t reg = getRegister(NODE_STATE(ksCurThread), capRegister); 153 val = x86_rdmsr(reg); 154 int num = 1; 155 if (CONFIG_WORD_SIZE == 32) { 156 setMR(NODE_STATE(ksCurThread), NULL, 0, val & 0xffffffff); 157 setMR(NODE_STATE(ksCurThread), NULL, 1, val >> 32); 158 num++; 159 } else { 160 setMR(NODE_STATE(ksCurThread), NULL, 0, val); 161 } 162 setRegister(NODE_STATE(ksCurThread), msgInfoRegister, wordFromMessageInfo(seL4_MessageInfo_new(0, 0, 0, num))); 163 return EXCEPTION_NONE; 164 } 165#endif 166 167#ifdef CONFIG_ENABLE_BENCHMARKS 168 if (w == SysBenchmarkFlushCaches) { 169 arch_clean_invalidate_caches(); 170 return EXCEPTION_NONE; 171 } else if (w == SysBenchmarkResetLog) { 172#ifdef CONFIG_BENCHMARK_USE_KERNEL_LOG_BUFFER 173 if (ksUserLogBuffer == 0) { 174 userError("A user-level buffer has to be set before resetting benchmark.\ 175 Use seL4_BenchmarkSetLogBuffer\n"); 176 setRegister(NODE_STATE(ksCurThread), capRegister, seL4_IllegalOperation); 177 return EXCEPTION_SYSCALL_ERROR; 178 } 179 180 ksLogIndex = 0; 181#endif /* CONFIG_BENCHMARK_USE_KERNEL_LOG_BUFFER */ 182#ifdef CONFIG_BENCHMARK_TRACK_UTILISATION 183 benchmark_log_utilisation_enabled = true; 184 NODE_STATE(ksIdleThread)->benchmark.utilisation = 0; 185 NODE_STATE(ksCurThread)->benchmark.schedule_start_time = ksEnter; 186 benchmark_start_time = ksEnter; 187 benchmark_arch_utilisation_reset(); 188#endif /* CONFIG_BENCHMARK_TRACK_UTILISATION */ 189 setRegister(NODE_STATE(ksCurThread), capRegister, seL4_NoError); 190 return EXCEPTION_NONE; 191 } else if (w == SysBenchmarkFinalizeLog) { 192#ifdef CONFIG_BENCHMARK_USE_KERNEL_LOG_BUFFER 193 ksLogIndexFinalized = ksLogIndex; 194 setRegister(NODE_STATE(ksCurThread), capRegister, ksLogIndexFinalized); 195#endif /* CONFIG_BENCHMARK_USE_KERNEL_LOG_BUFFER */ 196#ifdef CONFIG_BENCHMARK_TRACK_UTILISATION 197 benchmark_utilisation_finalise(); 198#endif /* CONFIG_BENCHMARK_TRACK_UTILISATION */ 199 return EXCEPTION_NONE; 200 } else if (w == SysBenchmarkSetLogBuffer) { 201#ifdef CONFIG_BENCHMARK_USE_KERNEL_LOG_BUFFER 202 word_t cptr_userFrame = getRegister(NODE_STATE(ksCurThread), capRegister); 203 204 if (benchmark_arch_map_logBuffer(cptr_userFrame) != EXCEPTION_NONE) { 205 setRegister(NODE_STATE(ksCurThread), capRegister, seL4_IllegalOperation); 206 return EXCEPTION_SYSCALL_ERROR; 207 } 208 209 setRegister(NODE_STATE(ksCurThread), capRegister, seL4_NoError); 210 return EXCEPTION_NONE; 211#endif /* CONFIG_BENCHMARK_USE_KERNEL_LOG_BUFFER */ 212 } 213 214#ifdef CONFIG_BENCHMARK_TRACK_UTILISATION 215 else if (w == SysBenchmarkGetThreadUtilisation) { 216 benchmark_track_utilisation_dump(); 217 return EXCEPTION_NONE; 218 } else if (w == SysBenchmarkResetThreadUtilisation) { 219 benchmark_track_reset_utilisation(); 220 return EXCEPTION_NONE; 221 } 222#endif /* CONFIG_BENCHMARK_TRACK_UTILISATION */ 223 224 else if (w == SysBenchmarkNullSyscall) { 225 return EXCEPTION_NONE; 226 } 227#endif /* CONFIG_ENABLE_BENCHMARKS */ 228 229 updateTimestamp(false); 230 if (likely(checkBudgetRestart())) { 231 current_fault = seL4_Fault_UnknownSyscall_new(w); 232 handleFault(NODE_STATE(ksCurThread)); 233 } 234 235 schedule(); 236 activateThread(); 237 238 return EXCEPTION_NONE; 239} 240 241exception_t 242handleUserLevelFault(word_t w_a, word_t w_b) 243{ 244 updateTimestamp(false); 245 if (likely(checkBudgetRestart())) { 246 current_fault = seL4_Fault_UserException_new(w_a, w_b); 247 handleFault(NODE_STATE(ksCurThread)); 248 } 249 schedule(); 250 activateThread(); 251 252 return EXCEPTION_NONE; 253} 254 255exception_t 256handleVMFaultEvent(vm_fault_type_t vm_faultType) 257{ 258 updateTimestamp(false); 259 if (likely(checkBudgetRestart())) { 260 exception_t status = handleVMFault(NODE_STATE(ksCurThread), vm_faultType); 261 if (status != EXCEPTION_NONE) { 262 handleFault(NODE_STATE(ksCurThread)); 263 } 264 } 265 266 schedule(); 267 activateThread(); 268 269 return EXCEPTION_NONE; 270} 271 272 273static exception_t 274handleInvocation(bool_t isCall, bool_t isBlocking, bool_t canDonate, cptr_t cptr) 275{ 276 seL4_MessageInfo_t info; 277 lookupCapAndSlot_ret_t lu_ret; 278 word_t *buffer; 279 exception_t status; 280 word_t length; 281 tcb_t *thread; 282 283 thread = NODE_STATE(ksCurThread); 284 285 info = messageInfoFromWord(getRegister(thread, msgInfoRegister)); 286 287 /* faulting section */ 288 lu_ret = lookupCapAndSlot(thread, cptr); 289 290 if (unlikely(lu_ret.status != EXCEPTION_NONE)) { 291 userError("Invocation of invalid cap #%lu.", cptr); 292 current_fault = seL4_Fault_CapFault_new(cptr, false); 293 294 if (isBlocking) { 295 handleFault(thread); 296 } 297 298 return EXCEPTION_NONE; 299 } 300 301 buffer = lookupIPCBuffer(false, thread); 302 303 status = lookupExtraCaps(thread, buffer, info); 304 305 if (unlikely(status != EXCEPTION_NONE)) { 306 userError("Lookup of extra caps failed."); 307 if (isBlocking) { 308 handleFault(thread); 309 } 310 return EXCEPTION_NONE; 311 } 312 313 /* Syscall error/Preemptible section */ 314 length = seL4_MessageInfo_get_length(info); 315 if (unlikely(length > n_msgRegisters && !buffer)) { 316 length = n_msgRegisters; 317 } 318 status = decodeInvocation(seL4_MessageInfo_get_label(info), length, 319 cptr, lu_ret.slot, lu_ret.cap, 320 current_extra_caps, isBlocking, isCall, 321 canDonate, buffer); 322 323 if (unlikely(status == EXCEPTION_PREEMPTED)) { 324 return status; 325 } 326 327 if (unlikely(status == EXCEPTION_SYSCALL_ERROR)) { 328 if (isCall) { 329 replyFromKernel_error(thread); 330 } 331 return EXCEPTION_NONE; 332 } 333 334 if (unlikely( 335 thread_state_get_tsType(thread->tcbState) == ThreadState_Restart)) { 336 if (isCall) { 337 replyFromKernel_success_empty(thread); 338 } 339 setThreadState(thread, ThreadState_Running); 340 } 341 342 return EXCEPTION_NONE; 343} 344 345static inline lookupCap_ret_t 346lookupReply(void) 347{ 348 word_t replyCPtr = getRegister(NODE_STATE(ksCurThread), replyRegister); 349 lookupCap_ret_t lu_ret = lookupCap(NODE_STATE(ksCurThread), replyCPtr); 350 if (unlikely(lu_ret.status != EXCEPTION_NONE)) { 351 userError("Reply cap lookup failed"); 352 current_fault = seL4_Fault_CapFault_new(replyCPtr, true); 353 handleFault(NODE_STATE(ksCurThread)); 354 return lu_ret; 355 } 356 357 if (unlikely(cap_get_capType(lu_ret.cap) != cap_reply_cap)) { 358 userError("Cap in reply slot is not a reply"); 359 current_fault = seL4_Fault_CapFault_new(replyCPtr, true); 360 handleFault(NODE_STATE(ksCurThread)); 361 lu_ret.status = EXCEPTION_FAULT; 362 return lu_ret; 363 } 364 365 return lu_ret; 366} 367 368static void 369handleRecv(bool_t isBlocking, bool_t canReply) 370{ 371 word_t epCPtr; 372 lookupCap_ret_t lu_ret; 373 374 epCPtr = getRegister(NODE_STATE(ksCurThread), capRegister); 375 376 lu_ret = lookupCap(NODE_STATE(ksCurThread), epCPtr); 377 378 if (unlikely(lu_ret.status != EXCEPTION_NONE)) { 379 /* current_lookup_fault has been set by lookupCap */ 380 current_fault = seL4_Fault_CapFault_new(epCPtr, true); 381 handleFault(NODE_STATE(ksCurThread)); 382 return; 383 } 384 385 switch (cap_get_capType(lu_ret.cap)) { 386 case cap_endpoint_cap: 387 if (unlikely(!cap_endpoint_cap_get_capCanReceive(lu_ret.cap))) { 388 current_lookup_fault = lookup_fault_missing_capability_new(0); 389 current_fault = seL4_Fault_CapFault_new(epCPtr, true); 390 handleFault(NODE_STATE(ksCurThread)); 391 break; 392 } 393 394 cap_t ep_cap = lu_ret.cap; 395 cap_t reply_cap = cap_null_cap_new(); 396 if (canReply) { 397 lu_ret = lookupReply(); 398 if (lu_ret.status != EXCEPTION_NONE) { 399 return; 400 } else { 401 reply_cap = lu_ret.cap; 402 } 403 } 404 receiveIPC(NODE_STATE(ksCurThread), ep_cap, isBlocking, reply_cap); 405 break; 406 407 case cap_notification_cap: { 408 notification_t *ntfnPtr; 409 tcb_t *boundTCB; 410 ntfnPtr = NTFN_PTR(cap_notification_cap_get_capNtfnPtr(lu_ret.cap)); 411 boundTCB = (tcb_t*)notification_ptr_get_ntfnBoundTCB(ntfnPtr); 412 if (unlikely(!cap_notification_cap_get_capNtfnCanReceive(lu_ret.cap) 413 || (boundTCB && boundTCB != NODE_STATE(ksCurThread)))) { 414 current_lookup_fault = lookup_fault_missing_capability_new(0); 415 current_fault = seL4_Fault_CapFault_new(epCPtr, true); 416 handleFault(NODE_STATE(ksCurThread)); 417 break; 418 } 419 420 receiveSignal(NODE_STATE(ksCurThread), lu_ret.cap, isBlocking); 421 break; 422 } 423 default: 424 current_lookup_fault = lookup_fault_missing_capability_new(0); 425 current_fault = seL4_Fault_CapFault_new(epCPtr, true); 426 handleFault(NODE_STATE(ksCurThread)); 427 break; 428 } 429} 430 431static void 432handleYield(void) 433{ 434 /* Yield the current remaining budget */ 435 ticks_t consumed = NODE_STATE(ksCurSC)->scConsumed; 436 chargeBudget(0, REFILL_HEAD(NODE_STATE(ksCurSC)).rAmount, false, CURRENT_CPU_INDEX(), true); 437 NODE_STATE(ksCurSC)->scConsumed = consumed; 438} 439 440exception_t 441handleSyscall(syscall_t syscall) 442{ 443 exception_t ret = EXCEPTION_NONE; 444 updateTimestamp(false); 445 446 if (likely(checkBudgetRestart())) { 447 switch (syscall) { 448 case SysSend: 449 ret = handleInvocation(false, true, false, getRegister(NODE_STATE(ksCurThread), capRegister)); 450 break; 451 452 case SysNBSend: 453 ret = handleInvocation(false, false, false, getRegister(NODE_STATE(ksCurThread), capRegister)); 454 break; 455 456 case SysCall: 457 ret = handleInvocation(true, true, true, getRegister(NODE_STATE(ksCurThread), capRegister)); 458 break; 459 460 case SysWait: 461 handleRecv(true, false); 462 break; 463 464 case SysNBWait: 465 handleRecv(false, false); 466 break; 467 468 case SysRecv: 469 handleRecv(true, true); 470 break; 471 472 case SysReplyRecv: { 473 cptr_t reply = getRegister(NODE_STATE(ksCurThread), replyRegister); 474 ret = handleInvocation(false, false, true, reply); 475 /* reply cannot error and is not preemptible */ 476 assert(ret == EXCEPTION_NONE); 477 handleRecv(true, true); 478 break; 479 } 480 case SysNBRecv: 481 handleRecv(false, true); 482 break; 483 484 case SysNBSendRecv: { 485 cptr_t dest = getNBSendRecvDest(); 486 ret = handleInvocation(false, false, true, dest); 487 if (ret != EXCEPTION_NONE) { 488 break; 489 } 490 handleRecv(true, true); 491 break; 492 } 493 494 case SysNBSendWait: 495 ret = handleInvocation(false, false, true, getRegister(NODE_STATE(ksCurThread), replyRegister)); 496 if (ret != EXCEPTION_NONE) { 497 break; 498 } 499 handleRecv(true, false); 500 break; 501 502 case SysYield: 503 handleYield(); 504 break; 505 506 default: 507 fail("Invalid syscall"); 508 } 509 510 if (unlikely(ret != EXCEPTION_NONE)) { 511 irq_t irq = getActiveIRQ(); 512 if (irq != irqInvalid) { 513 checkBudget(); 514 handleInterrupt(irq); 515 Arch_finaliseInterrupt(); 516 } 517 } 518 } 519 520 schedule(); 521 activateThread(); 522 523 return EXCEPTION_NONE; 524} 525