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 45 if (irq != irqInvalid) { 46 handleInterrupt(irq); 47 Arch_finaliseInterrupt(); 48 } else { 49#ifdef CONFIG_IRQ_REPORTING 50 userError("Spurious interrupt!"); 51#endif 52 handleSpuriousIRQ(); 53 } 54 55 schedule(); 56 activateThread(); 57 58 return EXCEPTION_NONE; 59} 60 61exception_t 62handleUnknownSyscall(word_t w) 63{ 64#ifdef CONFIG_PRINTING 65 if (w == SysDebugPutChar) { 66 kernel_putchar(getRegister(NODE_STATE(ksCurThread), capRegister)); 67 return EXCEPTION_NONE; 68 } 69 if (w == SysDebugDumpScheduler) { 70#ifdef CONFIG_DEBUG_BUILD 71 debug_dumpScheduler(); 72#endif 73 return EXCEPTION_NONE; 74 } 75#endif 76#ifdef CONFIG_DEBUG_BUILD 77 if (w == SysDebugHalt) { 78 tcb_t * UNUSED tptr = NODE_STATE(ksCurThread); 79 printf("Debug halt syscall from user thread %p \"%s\"\n", tptr, tptr->tcbName); 80 halt(); 81 } 82 if (w == SysDebugSnapshot) { 83 tcb_t * UNUSED tptr = NODE_STATE(ksCurThread); 84 printf("Debug snapshot syscall from user thread %p \"%s\"\n", tptr, tptr->tcbName); 85 capDL(); 86 return EXCEPTION_NONE; 87 } 88 if (w == SysDebugCapIdentify) { 89 word_t cptr = getRegister(NODE_STATE(ksCurThread), capRegister); 90 lookupCapAndSlot_ret_t lu_ret = lookupCapAndSlot(NODE_STATE(ksCurThread), cptr); 91 word_t cap_type = cap_get_capType(lu_ret.cap); 92 setRegister(NODE_STATE(ksCurThread), capRegister, cap_type); 93 return EXCEPTION_NONE; 94 } 95 96 if (w == SysDebugNameThread) { 97 /* This is a syscall meant to aid debugging, so if anything goes wrong 98 * then assume the system is completely misconfigured and halt */ 99 const char *name; 100 word_t len; 101 word_t cptr = getRegister(NODE_STATE(ksCurThread), capRegister); 102 lookupCapAndSlot_ret_t lu_ret = lookupCapAndSlot(NODE_STATE(ksCurThread), cptr); 103 /* ensure we got a TCB cap */ 104 word_t cap_type = cap_get_capType(lu_ret.cap); 105 if (cap_type != cap_thread_cap) { 106 userError("SysDebugNameThread: cap is not a TCB, halting"); 107 halt(); 108 } 109 /* Add 1 to the IPC buffer to skip the message info word */ 110 name = (const char*)(lookupIPCBuffer(true, NODE_STATE(ksCurThread)) + 1); 111 if (!name) { 112 userError("SysDebugNameThread: Failed to lookup IPC buffer, halting"); 113 halt(); 114 } 115 /* ensure the name isn't too long */ 116 len = strnlen(name, seL4_MsgMaxLength * sizeof(word_t)); 117 if (len == seL4_MsgMaxLength * sizeof(word_t)) { 118 userError("SysDebugNameThread: Name too long, halting"); 119 halt(); 120 } 121 setThreadName(TCB_PTR(cap_thread_cap_get_capTCBPtr(lu_ret.cap)), name); 122 return EXCEPTION_NONE; 123 } 124#endif /* CONFIG_DEBUG_BUILD */ 125 126#ifdef CONFIG_DANGEROUS_CODE_INJECTION 127 if (w == SysDebugRun) { 128 ((void (*) (void *))getRegister(NODE_STATE(ksCurThread), capRegister))((void*)getRegister(NODE_STATE(ksCurThread), msgInfoRegister)); 129 return EXCEPTION_NONE; 130 } 131#endif 132 133#ifdef CONFIG_KERNEL_X86_DANGEROUS_MSR 134 if (w == SysX86DangerousWRMSR) { 135 uint64_t val; 136 uint32_t reg = getRegister(NODE_STATE(ksCurThread), capRegister); 137 if (CONFIG_WORD_SIZE == 32) { 138 val = (uint64_t)getSyscallArg(0, NULL) | ((uint64_t)getSyscallArg(1, NULL) << 32); 139 } else { 140 val = getSyscallArg(0, NULL); 141 } 142 x86_wrmsr(reg, val); 143 return EXCEPTION_NONE; 144 } else if (w == SysX86DangerousRDMSR) { 145 uint64_t val; 146 uint32_t reg = getRegister(NODE_STATE(ksCurThread), capRegister); 147 val = x86_rdmsr(reg); 148 int num = 1; 149 if (CONFIG_WORD_SIZE == 32) { 150 setMR(NODE_STATE(ksCurThread), NULL, 0, val & 0xffffffff); 151 setMR(NODE_STATE(ksCurThread), NULL, 1, val >> 32); 152 num++; 153 } else { 154 setMR(NODE_STATE(ksCurThread), NULL, 0, val); 155 } 156 setRegister(NODE_STATE(ksCurThread), msgInfoRegister, wordFromMessageInfo(seL4_MessageInfo_new(0, 0, 0, num))); 157 return EXCEPTION_NONE; 158 } 159#endif 160 161#ifdef CONFIG_ENABLE_BENCHMARKS 162 if (w == SysBenchmarkFlushCaches) { 163 arch_clean_invalidate_caches(); 164 return EXCEPTION_NONE; 165 } else if (w == SysBenchmarkResetLog) { 166#ifdef CONFIG_BENCHMARK_USE_KERNEL_LOG_BUFFER 167 if (ksUserLogBuffer == 0) { 168 userError("A user-level buffer has to be set before resetting benchmark.\ 169 Use seL4_BenchmarkSetLogBuffer\n"); 170 setRegister(NODE_STATE(ksCurThread), capRegister, seL4_IllegalOperation); 171 return EXCEPTION_SYSCALL_ERROR; 172 } 173 174 ksLogIndex = 0; 175#endif /* CONFIG_BENCHMARK_USE_KERNEL_LOG_BUFFER */ 176#ifdef CONFIG_BENCHMARK_TRACK_UTILISATION 177 benchmark_log_utilisation_enabled = true; 178 NODE_STATE(ksIdleThread)->benchmark.utilisation = 0; 179 NODE_STATE(ksCurThread)->benchmark.schedule_start_time = ksEnter; 180 benchmark_start_time = ksEnter; 181 benchmark_arch_utilisation_reset(); 182#endif /* CONFIG_BENCHMARK_TRACK_UTILISATION */ 183 setRegister(NODE_STATE(ksCurThread), capRegister, seL4_NoError); 184 return EXCEPTION_NONE; 185 } else if (w == SysBenchmarkFinalizeLog) { 186#ifdef CONFIG_BENCHMARK_USE_KERNEL_LOG_BUFFER 187 ksLogIndexFinalized = ksLogIndex; 188 setRegister(NODE_STATE(ksCurThread), capRegister, ksLogIndexFinalized); 189#endif /* CONFIG_BENCHMARK_USE_KERNEL_LOG_BUFFER */ 190#ifdef CONFIG_BENCHMARK_TRACK_UTILISATION 191 benchmark_utilisation_finalise(); 192#endif /* CONFIG_BENCHMARK_TRACK_UTILISATION */ 193 return EXCEPTION_NONE; 194 } else if (w == SysBenchmarkSetLogBuffer) { 195#ifdef CONFIG_BENCHMARK_USE_KERNEL_LOG_BUFFER 196 word_t cptr_userFrame = getRegister(NODE_STATE(ksCurThread), capRegister); 197 198 if (benchmark_arch_map_logBuffer(cptr_userFrame) != EXCEPTION_NONE) { 199 setRegister(NODE_STATE(ksCurThread), capRegister, seL4_IllegalOperation); 200 return EXCEPTION_SYSCALL_ERROR; 201 } 202 203 setRegister(NODE_STATE(ksCurThread), capRegister, seL4_NoError); 204 return EXCEPTION_NONE; 205#endif /* CONFIG_BENCHMARK_USE_KERNEL_LOG_BUFFER */ 206 } 207 208#ifdef CONFIG_BENCHMARK_TRACK_UTILISATION 209 else if (w == SysBenchmarkGetThreadUtilisation) { 210 benchmark_track_utilisation_dump(); 211 return EXCEPTION_NONE; 212 } else if (w == SysBenchmarkResetThreadUtilisation) { 213 benchmark_track_reset_utilisation(); 214 return EXCEPTION_NONE; 215 } 216#endif /* CONFIG_BENCHMARK_TRACK_UTILISATION */ 217 218 else if (w == SysBenchmarkNullSyscall) { 219 return EXCEPTION_NONE; 220 } 221#endif /* CONFIG_ENABLE_BENCHMARKS */ 222 223 current_fault = seL4_Fault_UnknownSyscall_new(w); 224 handleFault(NODE_STATE(ksCurThread)); 225 226 schedule(); 227 activateThread(); 228 229 return EXCEPTION_NONE; 230} 231 232exception_t 233handleUserLevelFault(word_t w_a, word_t w_b) 234{ 235 current_fault = seL4_Fault_UserException_new(w_a, w_b); 236 handleFault(NODE_STATE(ksCurThread)); 237 238 schedule(); 239 activateThread(); 240 241 return EXCEPTION_NONE; 242} 243 244exception_t 245handleVMFaultEvent(vm_fault_type_t vm_faultType) 246{ 247 exception_t status; 248 249 status = handleVMFault(NODE_STATE(ksCurThread), vm_faultType); 250 if (status != EXCEPTION_NONE) { 251 handleFault(NODE_STATE(ksCurThread)); 252 } 253 254 schedule(); 255 activateThread(); 256 257 return EXCEPTION_NONE; 258} 259 260 261static exception_t 262handleInvocation(bool_t isCall, bool_t isBlocking) 263{ 264 seL4_MessageInfo_t info; 265 cptr_t cptr; 266 lookupCapAndSlot_ret_t lu_ret; 267 word_t *buffer; 268 exception_t status; 269 word_t length; 270 tcb_t *thread; 271 272 thread = NODE_STATE(ksCurThread); 273 274 info = messageInfoFromWord(getRegister(thread, msgInfoRegister)); 275 cptr = getRegister(thread, capRegister); 276 277 /* faulting section */ 278 lu_ret = lookupCapAndSlot(thread, cptr); 279 280 if (unlikely(lu_ret.status != EXCEPTION_NONE)) { 281 userError("Invocation of invalid cap #%lu.", cptr); 282 current_fault = seL4_Fault_CapFault_new(cptr, false); 283 284 if (isBlocking) { 285 handleFault(thread); 286 } 287 288 return EXCEPTION_NONE; 289 } 290 291 buffer = lookupIPCBuffer(false, thread); 292 293 status = lookupExtraCaps(thread, buffer, info); 294 295 if (unlikely(status != EXCEPTION_NONE)) { 296 userError("Lookup of extra caps failed."); 297 if (isBlocking) { 298 handleFault(thread); 299 } 300 return EXCEPTION_NONE; 301 } 302 303 /* Syscall error/Preemptible section */ 304 length = seL4_MessageInfo_get_length(info); 305 if (unlikely(length > n_msgRegisters && !buffer)) { 306 length = n_msgRegisters; 307 } 308 status = decodeInvocation(seL4_MessageInfo_get_label(info), length, 309 cptr, lu_ret.slot, lu_ret.cap, 310 current_extra_caps, isBlocking, isCall, 311 buffer); 312 313 if (unlikely(status == EXCEPTION_PREEMPTED)) { 314 return status; 315 } 316 317 if (unlikely(status == EXCEPTION_SYSCALL_ERROR)) { 318 if (isCall) { 319 replyFromKernel_error(thread); 320 } 321 return EXCEPTION_NONE; 322 } 323 324 if (unlikely( 325 thread_state_get_tsType(thread->tcbState) == ThreadState_Restart)) { 326 if (isCall) { 327 replyFromKernel_success_empty(thread); 328 } 329 setThreadState(thread, ThreadState_Running); 330 } 331 332 return EXCEPTION_NONE; 333} 334 335static void 336handleReply(void) 337{ 338 cte_t *callerSlot; 339 cap_t callerCap; 340 341 callerSlot = TCB_PTR_CTE_PTR(NODE_STATE(ksCurThread), tcbCaller); 342 callerCap = callerSlot->cap; 343 344 switch (cap_get_capType(callerCap)) { 345 case cap_reply_cap: { 346 tcb_t *caller; 347 348 if (cap_reply_cap_get_capReplyMaster(callerCap)) { 349 break; 350 } 351 caller = TCB_PTR(cap_reply_cap_get_capTCBPtr(callerCap)); 352 /* Haskell error: 353 * "handleReply: caller must not be the current thread" */ 354 assert(caller != NODE_STATE(ksCurThread)); 355 doReplyTransfer(NODE_STATE(ksCurThread), caller, callerSlot); 356 return; 357 } 358 359 case cap_null_cap: 360 userError("Attempted reply operation when no reply cap present."); 361 return; 362 363 default: 364 break; 365 } 366 367 fail("handleReply: invalid caller cap"); 368} 369 370static void 371handleRecv(bool_t isBlocking) 372{ 373 word_t epCPtr; 374 lookupCap_ret_t lu_ret; 375 376 epCPtr = getRegister(NODE_STATE(ksCurThread), capRegister); 377 378 lu_ret = lookupCap(NODE_STATE(ksCurThread), epCPtr); 379 380 if (unlikely(lu_ret.status != EXCEPTION_NONE)) { 381 /* current_lookup_fault has been set by lookupCap */ 382 current_fault = seL4_Fault_CapFault_new(epCPtr, true); 383 handleFault(NODE_STATE(ksCurThread)); 384 return; 385 } 386 387 switch (cap_get_capType(lu_ret.cap)) { 388 case cap_endpoint_cap: 389 if (unlikely(!cap_endpoint_cap_get_capCanReceive(lu_ret.cap))) { 390 current_lookup_fault = lookup_fault_missing_capability_new(0); 391 current_fault = seL4_Fault_CapFault_new(epCPtr, true); 392 handleFault(NODE_STATE(ksCurThread)); 393 break; 394 } 395 396 deleteCallerCap(NODE_STATE(ksCurThread)); 397 receiveIPC(NODE_STATE(ksCurThread), lu_ret.cap, isBlocking); 398 break; 399 400 case cap_notification_cap: { 401 notification_t *ntfnPtr; 402 tcb_t *boundTCB; 403 ntfnPtr = NTFN_PTR(cap_notification_cap_get_capNtfnPtr(lu_ret.cap)); 404 boundTCB = (tcb_t*)notification_ptr_get_ntfnBoundTCB(ntfnPtr); 405 if (unlikely(!cap_notification_cap_get_capNtfnCanReceive(lu_ret.cap) 406 || (boundTCB && boundTCB != NODE_STATE(ksCurThread)))) { 407 current_lookup_fault = lookup_fault_missing_capability_new(0); 408 current_fault = seL4_Fault_CapFault_new(epCPtr, true); 409 handleFault(NODE_STATE(ksCurThread)); 410 break; 411 } 412 413 receiveSignal(NODE_STATE(ksCurThread), lu_ret.cap, isBlocking); 414 break; 415 } 416 default: 417 current_lookup_fault = lookup_fault_missing_capability_new(0); 418 current_fault = seL4_Fault_CapFault_new(epCPtr, true); 419 handleFault(NODE_STATE(ksCurThread)); 420 break; 421 } 422} 423 424static void 425handleYield(void) 426{ 427 tcbSchedDequeue(NODE_STATE(ksCurThread)); 428 SCHED_APPEND_CURRENT_TCB; 429 rescheduleRequired(); 430} 431 432exception_t 433handleSyscall(syscall_t syscall) 434{ 435 exception_t ret; 436 irq_t irq; 437 438 switch (syscall) { 439 case SysSend: 440 ret = handleInvocation(false, true); 441 if (unlikely(ret != EXCEPTION_NONE)) { 442 irq = getActiveIRQ(); 443 if (irq != irqInvalid) { 444 handleInterrupt(irq); 445 Arch_finaliseInterrupt(); 446 } 447 } 448 break; 449 450 case SysNBSend: 451 ret = handleInvocation(false, false); 452 if (unlikely(ret != EXCEPTION_NONE)) { 453 irq = getActiveIRQ(); 454 if (irq != irqInvalid) { 455 handleInterrupt(irq); 456 Arch_finaliseInterrupt(); 457 } 458 } 459 break; 460 461 case SysCall: 462 ret = handleInvocation(true, true); 463 if (unlikely(ret != EXCEPTION_NONE)) { 464 irq = getActiveIRQ(); 465 if (irq != irqInvalid) { 466 handleInterrupt(irq); 467 Arch_finaliseInterrupt(); 468 } 469 } 470 break; 471 472 case SysRecv: 473 handleRecv(true); 474 break; 475 476 case SysReply: 477 handleReply(); 478 break; 479 480 case SysReplyRecv: 481 handleReply(); 482 handleRecv(true); 483 break; 484 485 case SysNBRecv: 486 handleRecv(false); 487 break; 488 489 case SysYield: 490 handleYield(); 491 break; 492 493 default: 494 fail("Invalid syscall"); 495 } 496 497 schedule(); 498 activateThread(); 499 500 return EXCEPTION_NONE; 501} 502