1/*
2 * Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 */
6
7#include <config.h>
8#include <types.h>
9#include <api/faults.h>
10#include <api/syscall.h>
11#include <kernel/thread.h>
12#include <arch/kernel/thread.h>
13#include <machine/debug.h>
14#ifdef CONFIG_KERNEL_MCS
15#include <mode/api/ipc_buffer.h>
16#include <object/schedcontext.h>
17#endif
18
19/* consistency with libsel4 */
20compile_assert(InvalidRoot, lookup_fault_invalid_root + 1 == seL4_InvalidRoot)
21compile_assert(MissingCapability, lookup_fault_missing_capability + 1 == seL4_MissingCapability)
22compile_assert(DepthMismatch, lookup_fault_depth_mismatch + 1 == seL4_DepthMismatch)
23compile_assert(GuardMismatch, lookup_fault_guard_mismatch + 1 == seL4_GuardMismatch)
24compile_assert(seL4_UnknownSyscall_Syscall, (word_t) n_syscallMessage == seL4_UnknownSyscall_Syscall)
25compile_assert(seL4_UserException_Number, (word_t) n_exceptionMessage == seL4_UserException_Number)
26compile_assert(seL4_UserException_Code, (word_t) n_exceptionMessage + 1 == seL4_UserException_Code)
27
28static inline unsigned int
29setMRs_lookup_failure(tcb_t *receiver, word_t *receiveIPCBuffer,
30                      lookup_fault_t luf, unsigned int offset)
31{
32    word_t lufType = lookup_fault_get_lufType(luf);
33    word_t i;
34
35    i = setMR(receiver, receiveIPCBuffer, offset, lufType + 1);
36
37    /* check constants match libsel4 */
38    if (offset == seL4_CapFault_LookupFailureType) {
39        assert(offset + 1 == seL4_CapFault_BitsLeft);
40        assert(offset + 2 == seL4_CapFault_DepthMismatch_BitsFound);
41        assert(offset + 2 == seL4_CapFault_GuardMismatch_GuardFound);
42        assert(offset + 3 == seL4_CapFault_GuardMismatch_BitsFound);
43    } else {
44        assert(offset == 1);
45    }
46
47    switch (lufType) {
48    case lookup_fault_invalid_root:
49        return i;
50
51    case lookup_fault_missing_capability:
52        return setMR(receiver, receiveIPCBuffer, offset + 1,
53                     lookup_fault_missing_capability_get_bitsLeft(luf));
54
55    case lookup_fault_depth_mismatch:
56        setMR(receiver, receiveIPCBuffer, offset + 1,
57              lookup_fault_depth_mismatch_get_bitsLeft(luf));
58        return setMR(receiver, receiveIPCBuffer, offset + 2,
59                     lookup_fault_depth_mismatch_get_bitsFound(luf));
60
61    case lookup_fault_guard_mismatch:
62        setMR(receiver, receiveIPCBuffer, offset + 1,
63              lookup_fault_guard_mismatch_get_bitsLeft(luf));
64        setMR(receiver, receiveIPCBuffer, offset + 2,
65              lookup_fault_guard_mismatch_get_guardFound(luf));
66        return setMR(receiver, receiveIPCBuffer, offset + 3,
67                     lookup_fault_guard_mismatch_get_bitsFound(luf));
68
69    default:
70        fail("Invalid lookup failure");
71    }
72}
73
74static inline void copyMRsFaultReply(tcb_t *sender, tcb_t *receiver, MessageID_t id, word_t length)
75{
76    word_t i;
77    bool_t archInfo;
78
79    archInfo = Arch_getSanitiseRegisterInfo(receiver);
80
81    for (i = 0; i < MIN(length, n_msgRegisters); i++) {
82        register_t r = fault_messages[id][i];
83        word_t v = getRegister(sender, msgRegisters[i]);
84        setRegister(receiver, r, sanitiseRegister(r, v, archInfo));
85    }
86
87    if (i < length) {
88        word_t *sendBuf = lookupIPCBuffer(false, sender);
89        if (sendBuf) {
90            for (; i < length; i++) {
91                register_t r = fault_messages[id][i];
92                word_t v = sendBuf[i + 1];
93                setRegister(receiver, r, sanitiseRegister(r, v, archInfo));
94            }
95        }
96    }
97}
98
99static inline void copyMRsFault(tcb_t *sender, tcb_t *receiver, MessageID_t id,
100                                word_t length, word_t *receiveIPCBuffer)
101{
102    word_t i;
103    for (i = 0; i < MIN(length, n_msgRegisters); i++) {
104        setRegister(receiver, msgRegisters[i], getRegister(sender, fault_messages[id][i]));
105    }
106
107    if (receiveIPCBuffer) {
108        for (; i < length; i++) {
109            receiveIPCBuffer[i + 1] = getRegister(sender, fault_messages[id][i]);
110        }
111    }
112}
113
114bool_t handleFaultReply(tcb_t *receiver, tcb_t *sender)
115{
116    /* These lookups are moved inward from doReplyTransfer */
117    seL4_MessageInfo_t tag = messageInfoFromWord(getRegister(sender, msgInfoRegister));
118    word_t label = seL4_MessageInfo_get_label(tag);
119    word_t length = seL4_MessageInfo_get_length(tag);
120    seL4_Fault_t fault = receiver->tcbFault;
121
122    switch (seL4_Fault_get_seL4_FaultType(fault)) {
123    case seL4_Fault_CapFault:
124        return true;
125
126    case seL4_Fault_UnknownSyscall:
127        copyMRsFaultReply(sender, receiver, MessageID_Syscall, MIN(length, n_syscallMessage));
128        return (label == 0);
129
130    case seL4_Fault_UserException:
131        copyMRsFaultReply(sender, receiver, MessageID_Exception, MIN(length, n_exceptionMessage));
132        return (label == 0);
133
134#ifdef CONFIG_KERNEL_MCS
135    case seL4_Fault_Timeout:
136        copyMRsFaultReply(sender, receiver, MessageID_TimeoutReply, MIN(length, n_timeoutMessage));
137        return (label == 0);
138#endif
139#ifdef CONFIG_HARDWARE_DEBUG_API
140    case seL4_Fault_DebugException: {
141        word_t n_instrs;
142
143        if (seL4_Fault_DebugException_get_exceptionReason(fault) != seL4_SingleStep) {
144            /* Only single-step replies are required to set message registers.
145             */
146            return (label == 0);
147        }
148
149        if (length < DEBUG_REPLY_N_EXPECTED_REGISTERS) {
150            /* A single-step reply doesn't mean much if it isn't composed of the bp
151             * number and number of instructions to skip. But even if both aren't
152             * set, we can still allow the thread to continue because replying
153             * should uniformly resume thread execution, based on the general seL4
154             * API model.
155             *
156             * If it was single-step, but no reply registers were set, just
157             * default to skipping 1 and continuing.
158             *
159             * On x86, bp_num actually doesn't matter for single-stepping
160             * because single-stepping doesn't use a hardware register -- it
161             * uses EFLAGS.TF.
162             */
163            n_instrs = 1;
164        } else {
165            /* If the reply had all expected registers set, proceed as normal */
166            n_instrs = getRegister(sender, msgRegisters[0]);
167        }
168
169        syscall_error_t res;
170
171        res = Arch_decodeConfigureSingleStepping(receiver, 0, n_instrs, true);
172        if (res.type != seL4_NoError) {
173            return false;
174        };
175
176        configureSingleStepping(receiver, 0, n_instrs, true);
177
178        /* Replying will always resume the thread: the only variant behaviour
179         * is whether or not the thread will be resumed with stepping still
180         * enabled.
181         */
182        return (label == 0);
183    }
184#endif
185
186    default:
187        return Arch_handleFaultReply(receiver, sender, seL4_Fault_get_seL4_FaultType(fault));
188    }
189}
190
191word_t setMRs_fault(tcb_t *sender, tcb_t *receiver, word_t *receiveIPCBuffer)
192{
193    switch (seL4_Fault_get_seL4_FaultType(sender->tcbFault)) {
194    case seL4_Fault_CapFault:
195        setMR(receiver, receiveIPCBuffer, seL4_CapFault_IP, getRestartPC(sender));
196        setMR(receiver, receiveIPCBuffer, seL4_CapFault_Addr,
197              seL4_Fault_CapFault_get_address(sender->tcbFault));
198        setMR(receiver, receiveIPCBuffer, seL4_CapFault_InRecvPhase,
199              seL4_Fault_CapFault_get_inReceivePhase(sender->tcbFault));
200        return setMRs_lookup_failure(receiver, receiveIPCBuffer,
201                                     sender->tcbLookupFailure, seL4_CapFault_LookupFailureType);
202
203    case seL4_Fault_UnknownSyscall: {
204        copyMRsFault(sender, receiver, MessageID_Syscall, n_syscallMessage,
205                     receiveIPCBuffer);
206
207        return setMR(receiver, receiveIPCBuffer, n_syscallMessage,
208                     seL4_Fault_UnknownSyscall_get_syscallNumber(sender->tcbFault));
209    }
210
211    case seL4_Fault_UserException: {
212        copyMRsFault(sender, receiver, MessageID_Exception,
213                     n_exceptionMessage, receiveIPCBuffer);
214        setMR(receiver, receiveIPCBuffer, n_exceptionMessage,
215              seL4_Fault_UserException_get_number(sender->tcbFault));
216        return setMR(receiver, receiveIPCBuffer, n_exceptionMessage + 1u,
217                     seL4_Fault_UserException_get_code(sender->tcbFault));
218    }
219
220#ifdef CONFIG_KERNEL_MCS
221    case seL4_Fault_Timeout: {
222        word_t len = setMR(receiver, receiveIPCBuffer, seL4_Timeout_Data,
223                           seL4_Fault_Timeout_get_badge(sender->tcbFault));
224        if (sender->tcbSchedContext) {
225            time_t consumed = schedContext_updateConsumed(sender->tcbSchedContext);
226            return mode_setTimeArg(len, consumed,
227                                   receiveIPCBuffer, receiver);
228        } else {
229            return len;
230        }
231    }
232#endif
233#ifdef CONFIG_HARDWARE_DEBUG_API
234    case seL4_Fault_DebugException: {
235        word_t reason = seL4_Fault_DebugException_get_exceptionReason(sender->tcbFault);
236
237        setMR(receiver, receiveIPCBuffer,
238              seL4_DebugException_FaultIP, getRestartPC(sender));
239        unsigned int ret = setMR(receiver, receiveIPCBuffer,
240                                 seL4_DebugException_ExceptionReason, reason);
241
242        if (reason != seL4_SingleStep && reason != seL4_SoftwareBreakRequest) {
243            ret = setMR(receiver, receiveIPCBuffer,
244                        seL4_DebugException_TriggerAddress,
245                        seL4_Fault_DebugException_get_breakpointAddress(sender->tcbFault));
246
247            /* Breakpoint messages also set a "breakpoint number" register. */
248            ret = setMR(receiver, receiveIPCBuffer,
249                        seL4_DebugException_BreakpointNumber,
250                        seL4_Fault_DebugException_get_breakpointNumber(sender->tcbFault));
251        }
252        return ret;
253    }
254#endif /* CONFIG_HARDWARE_DEBUG_API */
255
256    default:
257        return Arch_setMRs_fault(sender, receiver, receiveIPCBuffer,
258                                 seL4_Fault_get_seL4_FaultType(sender->tcbFault));
259    }
260}
261