1/*
2 * Copyright 2017, Data61
3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
4 * ABN 41 687 119 230.
5 *
6 * This software may be distributed and modified according to the terms of
7 * the BSD 2-Clause license. Note that NO WARRANTY is provided.
8 * See "LICENSE_BSD2.txt" for details.
9 *
10 * @TAG(DATA61_BSD)
11 */
12
13/* Error handling API for CAmkES. */
14
15#pragma once
16
17#include <autoconf.h>
18#include <sel4camkes/gen_config.h>
19#include <sel4/sel4.h>
20#include <stdbool.h>
21#include <stdint.h>
22#include <stdlib.h>
23#include <utils/util.h>
24
25/* Types of errors that can occur in CAmkES glue code. */
26typedef enum {
27
28    /* No error occurred. Used to indicate normal operation. */
29    CE_NO_ERROR = 0,
30
31    /* During marshalling, the next operation if we were to continue would
32     * exceed the size of the target buffer. Note that continuing in the event
33     * of such an error *will* cause an out-of-bounds memory write.
34     */
35    CE_BUFFER_LENGTH_EXCEEDED,
36
37    /* In an RPC communication, the method index indicated by the caller was
38     * out of range for the given interface. That is, the method index was
39     * larger than the number of methods in this interface, or it was negative.
40     */
41    CE_INVALID_METHOD_INDEX,
42
43    /* The payload of an RPC (marshalled parameters) was somehow invalid. This
44     * includes cases where an IPC recipient received a message that was either
45     * too long or too short for what it was expecting.
46     */
47    CE_MALFORMED_RPC_PAYLOAD,
48
49    /* A system call that is never expected to fail in normal operation, did
50     * so. Recovery actions are most likely dependent on what and where the
51     * actual failure was.
52     */
53    CE_SYSCALL_FAILED,
54
55    /* Some internal memory/bookkeeping allocation the glue code needed to
56     * perform did not complete successfully. It will be difficult to diagnose
57     * the cause of one of these without looking at the location at which the
58     * error was triggered.
59     */
60    CE_ALLOCATION_FAILURE,
61
62    /* A counter would overflow or underflow if execution were to continue from
63     * the current point.
64     */
65    CE_OVERFLOW,
66
67} camkes_error_type_t;
68
69/* Tagged union representing data about an error itself. */
70typedef struct {
71    /* The type of the error. This tag determines which member of the union
72     * below is relevant.
73     */
74    camkes_error_type_t type;
75
76    /* The component instance in which this error occurred. This will probably
77     * never be ambiguous programmatically, but it may be useful to have this
78     * value available to print. Error handlers can assumes this pointer will
79     * never be NULL.
80     */
81    const char *instance;
82
83    /* The interface in which this error occurred. If this error occurred in a
84     * component-global context then this pointer will be NULL.
85     */
86    const char *interface;
87
88    /* The source code position at which the error occurred. Useful if you are
89     * debugging template generation.
90     */
91    const char *filename;
92    long lineno;
93
94    /* A human readable description of the failure. Error handlers can assume
95     * this pointer will never be NULL.
96     */
97    const char *description;
98
99    union {
100        /* No union member for CE_NO_ERROR. */
101
102        struct { /* CE_BUFFER_LENGTH_EXCEEDED */
103
104            /* The current byte offset into the buffer. */
105            unsigned current_length;
106
107            /* The byte offset we're about to reach if we perform the next
108             * write. I.e. an offset out-of-bounds of the buffer itself.
109             */
110            unsigned target_length;
111        };
112
113        struct { /* CE_INVALID_METHOD_INDEX */
114
115            /* The range of valid method indices. */
116            uint64_t lower_bound;
117            uint64_t upper_bound;
118
119            /* The value that caused this error. */
120            uint64_t invalid_index;
121        };
122
123        struct { /* CE_MALFORMED_RPC_PAYLOAD */
124
125            /* The (possibly invalid) length of the payload. */
126            unsigned length;
127
128            /* The current byte offset into the buffer. I.e. the point at which
129             * we realised we had an invalid payload.
130             */
131            unsigned current_index;
132        };
133
134        struct { /* CE_SYSCALL_FAILED */
135
136            /* The syscall that we ran. This value will be a member of
137             * invocation_label or arch_invocation_label from libsel4.
138             */
139            int syscall;
140
141            /* The error value returned by seL4. */
142            int error;
143        };
144
145        struct { /* CE_ALLOCATION_FAILURE */
146
147            /* The number of bytes that was being allocated when the failure
148             * occurred. This field may be zero if the amount was not relevant.
149             */
150            size_t alloc_bytes;
151        };
152    };
153} camkes_error_t;
154
155/* The action to take to recover from an error. An error handler, as described
156 * below, should return one of these values to indicate what action the glue
157 * code should take.
158 */
159typedef enum {
160
161    /* Used as a sentinel for a context where an action is invalid. Don't ever
162     * return this from an error handler.
163     */
164    CEA_INVALID = -1,
165
166    /* Terminate the currently running piece of glue code by returning. If the
167     * offending code was called from a glue code event loop, this generally
168     * means return to the event loop. If the offending code was called from
169     * user code, this means return to user code where the calling function is
170     * expected to be aware (by some out-of-band communication) that an error
171     * has occurred.
172     */
173    CEA_DISCARD,
174
175    /* Ignore the error and continue. This is generally dangerous and not what
176     * you want.
177     */
178    CEA_IGNORE,
179
180    /* Exit with failure in the current thread. Note that what 'exit' means
181     * here depends on a number of things including whether the thread has a
182     * cap to itself.
183     */
184    CEA_ABORT,
185
186    /* Exit with failure and also try to halt the entire system if possible.
187     * This action implies CEA_ABORT and only differs on a debug kernel where
188     * it is possible to stop the system. If you have no error handlers
189     * installed, this is the default action.
190     */
191    CEA_HALT,
192
193} camkes_error_action_t;
194
195/* An error handling function. If components define their own error handlers,
196 * they should conform to this prototype. The argument is the error that
197 * occurred and the return value is the recovery action the glue code should
198 * take.
199 */
200typedef camkes_error_action_t (*camkes_error_handler_t)(camkes_error_t *);
201
202/* Register a component-global error handler. More fine-grained registration
203 * functions (one per-interface) are generated and prototyped in the generated
204 * header. Errors that occur in interface glue code will only invoke this error
205 * handler if no error handler is registered for the interface itself. This
206 * registration function returns a pointer to the old handler.
207 */
208camkes_error_handler_t camkes_register_error_handler(
209    camkes_error_handler_t handler);
210
211/* Throw an error from glue code. This function is not expected to be called by
212 * user code.
213 */
214camkes_error_action_t camkes_error(camkes_error_t *e) COLD;
215
216/* Convenience for using halt() inside macro definitions. This is not expected
217 * to be called from user code. Note that it does not halt on a non-debug
218 * kernel.
219 */
220#ifdef CONFIG_DEBUG_BUILD
221#define halt() seL4_DebugHalt()
222#else
223#define halt() do { } while (0)
224#endif
225
226#ifdef CONFIG_CAMKES_ERROR_HANDLER_CONFIGURABLE
227
228/* Convenience macro for throwing an error from glue code.
229 *  handler - A camkes_error_handler_t to invoke.
230 *  edata - Error structure to throw to the error handler.
231 *  action - Action to take on return of CEA_DISCARD from the error
232 *    handler. This should be a GNU statement expression (aka compound
233 *    statement).
234 */
235#define ERR(handler, edata, action) ({ \
236            COLD_PATH(); \
237            camkes_error_t _e = edata; \
238            _e.filename = __FILE__; \
239            _e.lineno = __LINE__; \
240            switch (handler(&_e)) { \
241                case CEA_DISCARD: \
242                    action; \
243                    UNREACHABLE(); \
244                case CEA_IGNORE: \
245                    break; \
246                case CEA_HALT: \
247                    halt(); \
248                    /* If we return we'll just fall through. */ \
249                case CEA_ABORT: \
250                    abort(); \
251                    /* In case we return */ \
252                    while(1); \
253                default: \
254                    UNREACHABLE(); \
255            } \
256        })
257
258#elif defined(CONFIG_CAMKES_ERROR_HANDLER_GUARD)
259
260#define ERR(handler, edata, action) GUARD(false)
261
262#elif defined(CONFIG_CAMKES_ERROR_HANDLER_ABORT)
263
264#define ERR(handler, edata, action) abort()
265
266#elif defined(CONFIG_CAMKES_ERROR_HANDLER_DISCARD)
267
268#define ERR(handler, edata, action) action
269
270#else
271
272#error no error handling mode defined
273
274#endif
275
276#ifdef CONFIG_CAMKES_ERROR_HANDLER_GUARD
277
278#define ERR_IF(cond, handler, edata, action) GUARD(!(cond))
279
280#else
281
282/* Conditionally throw an error. */
283#define ERR_IF(cond, handler, edata, action) ({ \
284            if (unlikely(cond)) { \
285                ERR(handler, edata, action); \
286            } \
287        })
288
289#endif
290