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