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