1/*
2 * Copyright 2014, General Dynamics C4 Systems
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 */
6
7#include <api/failures.h>
8#include <kernel/cspace.h>
9#include <kernel/faulthandler.h>
10#include <kernel/thread.h>
11#include <machine/io.h>
12#include <arch/machine.h>
13
14#ifdef CONFIG_KERNEL_MCS
15void handleFault(tcb_t *tptr)
16{
17    bool_t hasFaultHandler = sendFaultIPC(tptr, TCB_PTR_CTE_PTR(tptr, tcbFaultHandler)->cap,
18                                          tptr->tcbSchedContext != NULL);
19    if (!hasFaultHandler) {
20        handleNoFaultHandler(tptr);
21    }
22}
23
24void handleTimeout(tcb_t *tptr)
25{
26    assert(validTimeoutHandler(tptr));
27    sendFaultIPC(tptr, TCB_PTR_CTE_PTR(tptr, tcbTimeoutHandler)->cap, false);
28}
29
30bool_t sendFaultIPC(tcb_t *tptr, cap_t handlerCap, bool_t can_donate)
31{
32    if (cap_get_capType(handlerCap) == cap_endpoint_cap) {
33        assert(cap_endpoint_cap_get_capCanSend(handlerCap));
34        assert(cap_endpoint_cap_get_capCanGrant(handlerCap) ||
35               cap_endpoint_cap_get_capCanGrantReply(handlerCap));
36
37        tptr->tcbFault = current_fault;
38        sendIPC(true, false,
39                cap_endpoint_cap_get_capEPBadge(handlerCap),
40                cap_endpoint_cap_get_capCanGrant(handlerCap),
41                cap_endpoint_cap_get_capCanGrantReply(handlerCap),
42                can_donate, tptr,
43                EP_PTR(cap_endpoint_cap_get_capEPPtr(handlerCap)));
44
45        return true;
46    } else {
47        assert(cap_get_capType(handlerCap) == cap_null_cap);
48        return false;
49    }
50}
51#else
52
53void handleFault(tcb_t *tptr)
54{
55    exception_t status;
56    seL4_Fault_t fault = current_fault;
57
58    status = sendFaultIPC(tptr);
59    if (status != EXCEPTION_NONE) {
60        handleDoubleFault(tptr, fault);
61    }
62}
63
64exception_t sendFaultIPC(tcb_t *tptr)
65{
66    cptr_t handlerCPtr;
67    cap_t  handlerCap;
68    lookupCap_ret_t lu_ret;
69    lookup_fault_t original_lookup_fault;
70
71    original_lookup_fault = current_lookup_fault;
72
73    handlerCPtr = tptr->tcbFaultHandler;
74    lu_ret = lookupCap(tptr, handlerCPtr);
75    if (lu_ret.status != EXCEPTION_NONE) {
76        current_fault = seL4_Fault_CapFault_new(handlerCPtr, false);
77        return EXCEPTION_FAULT;
78    }
79    handlerCap = lu_ret.cap;
80
81    if (cap_get_capType(handlerCap) == cap_endpoint_cap &&
82        cap_endpoint_cap_get_capCanSend(handlerCap) &&
83        (cap_endpoint_cap_get_capCanGrant(handlerCap) ||
84         cap_endpoint_cap_get_capCanGrantReply(handlerCap))) {
85        tptr->tcbFault = current_fault;
86        if (seL4_Fault_get_seL4_FaultType(current_fault) == seL4_Fault_CapFault) {
87            tptr->tcbLookupFailure = original_lookup_fault;
88        }
89        sendIPC(true, true,
90                cap_endpoint_cap_get_capEPBadge(handlerCap),
91                cap_endpoint_cap_get_capCanGrant(handlerCap), true, tptr,
92                EP_PTR(cap_endpoint_cap_get_capEPPtr(handlerCap)));
93
94        return EXCEPTION_NONE;
95    } else {
96        current_fault = seL4_Fault_CapFault_new(handlerCPtr, false);
97        current_lookup_fault = lookup_fault_missing_capability_new(0);
98
99        return EXCEPTION_FAULT;
100    }
101}
102#endif
103
104#ifdef CONFIG_PRINTING
105static void print_fault(seL4_Fault_t f)
106{
107    switch (seL4_Fault_get_seL4_FaultType(f)) {
108    case seL4_Fault_NullFault:
109        printf("null fault");
110        break;
111    case seL4_Fault_CapFault:
112        printf("cap fault in %s phase at address %p",
113               seL4_Fault_CapFault_get_inReceivePhase(f) ? "receive" : "send",
114               (void *)seL4_Fault_CapFault_get_address(f));
115        break;
116    case seL4_Fault_VMFault:
117        printf("vm fault on %s at address %p with status %p",
118               seL4_Fault_VMFault_get_instructionFault(f) ? "code" : "data",
119               (void *)seL4_Fault_VMFault_get_address(f),
120               (void *)seL4_Fault_VMFault_get_FSR(f));
121        break;
122    case seL4_Fault_UnknownSyscall:
123        printf("unknown syscall %p",
124               (void *)seL4_Fault_UnknownSyscall_get_syscallNumber(f));
125        break;
126    case seL4_Fault_UserException:
127        printf("user exception %p code %p",
128               (void *)seL4_Fault_UserException_get_number(f),
129               (void *)seL4_Fault_UserException_get_code(f));
130        break;
131#ifdef CONFIG_KERNEL_MCS
132    case seL4_Fault_Timeout:
133        printf("Timeout fault for 0x%x\n", (unsigned int) seL4_Fault_Timeout_get_badge(f));
134        break;
135#endif
136    default:
137        printf("unknown fault");
138        break;
139    }
140}
141#endif
142
143#ifdef CONFIG_KERNEL_MCS
144void handleNoFaultHandler(tcb_t *tptr)
145#else
146/* The second fault, ex2, is stored in the global current_fault */
147void handleDoubleFault(tcb_t *tptr, seL4_Fault_t ex1)
148#endif
149{
150#ifdef CONFIG_PRINTING
151#ifdef CONFIG_KERNEL_MCS
152    printf("Found thread has no fault handler while trying to handle:\n");
153    print_fault(current_fault);
154#else
155    seL4_Fault_t ex2 = current_fault;
156    printf("Caught ");
157    print_fault(ex2);
158    printf("\nwhile trying to handle:\n");
159    print_fault(ex1);
160#endif
161#ifdef CONFIG_DEBUG_BUILD
162    printf("\nin thread %p \"%s\" ", tptr, TCB_PTR_DEBUG_PTR(tptr)->tcbName);
163#endif /* CONFIG_DEBUG_BUILD */
164
165    printf("at address %p\n", (void *)getRestartPC(tptr));
166    printf("With stack:\n");
167    Arch_userStackTrace(tptr);
168#endif
169
170    setThreadState(tptr, ThreadState_Inactive);
171}
172