| /* |
| * Copyright 2017, Data61, CSIRO (ABN 41 687 119 230) |
| * |
| * SPDX-License-Identifier: BSD-2-Clause |
| */ |
| |
| /* Error handling API for CAmkES. */ |
| |
| #pragma once |
| |
| #include <autoconf.h> |
| #include <sel4camkes/gen_config.h> |
| #include <sel4/sel4.h> |
| #include <stdbool.h> |
| #include <stdint.h> |
| #include <stdlib.h> |
| #include <utils/util.h> |
| |
| /* Types of errors that can occur in CAmkES glue code. */ |
| typedef enum { |
| |
| /* No error occurred. Used to indicate normal operation. */ |
| CE_NO_ERROR = 0, |
| |
| /* During marshalling, the next operation if we were to continue would |
| * exceed the size of the target buffer. Note that continuing in the event |
| * of such an error *will* cause an out-of-bounds memory write. |
| */ |
| CE_BUFFER_LENGTH_EXCEEDED, |
| |
| /* In an RPC communication, the method index indicated by the caller was |
| * out of range for the given interface. That is, the method index was |
| * larger than the number of methods in this interface, or it was negative. |
| */ |
| CE_INVALID_METHOD_INDEX, |
| |
| /* The payload of an RPC (marshalled parameters) was somehow invalid. This |
| * includes cases where an IPC recipient received a message that was either |
| * too long or too short for what it was expecting. |
| */ |
| CE_MALFORMED_RPC_PAYLOAD, |
| |
| /* A system call that is never expected to fail in normal operation, did |
| * so. Recovery actions are most likely dependent on what and where the |
| * actual failure was. |
| */ |
| CE_SYSCALL_FAILED, |
| |
| /* Some internal memory/bookkeeping allocation the glue code needed to |
| * perform did not complete successfully. It will be difficult to diagnose |
| * the cause of one of these without looking at the location at which the |
| * error was triggered. |
| */ |
| CE_ALLOCATION_FAILURE, |
| |
| /* A counter would overflow or underflow if execution were to continue from |
| * the current point. |
| */ |
| CE_OVERFLOW, |
| |
| } camkes_error_type_t; |
| |
| /* Tagged union representing data about an error itself. */ |
| typedef struct { |
| /* The type of the error. This tag determines which member of the union |
| * below is relevant. |
| */ |
| camkes_error_type_t type; |
| |
| /* The component instance in which this error occurred. This will probably |
| * never be ambiguous programmatically, but it may be useful to have this |
| * value available to print. Error handlers can assumes this pointer will |
| * never be NULL. |
| */ |
| const char *instance; |
| |
| /* The interface in which this error occurred. If this error occurred in a |
| * component-global context then this pointer will be NULL. |
| */ |
| const char *interface; |
| |
| /* The source code position at which the error occurred. Useful if you are |
| * debugging template generation. |
| */ |
| const char *filename; |
| long lineno; |
| |
| /* A human readable description of the failure. Error handlers can assume |
| * this pointer will never be NULL. |
| */ |
| const char *description; |
| |
| union { |
| /* No union member for CE_NO_ERROR. */ |
| |
| struct { /* CE_BUFFER_LENGTH_EXCEEDED */ |
| |
| /* The current byte offset into the buffer. */ |
| unsigned current_length; |
| |
| /* The byte offset we're about to reach if we perform the next |
| * write. I.e. an offset out-of-bounds of the buffer itself. |
| */ |
| unsigned target_length; |
| }; |
| |
| struct { /* CE_INVALID_METHOD_INDEX */ |
| |
| /* The range of valid method indices. */ |
| uint64_t lower_bound; |
| uint64_t upper_bound; |
| |
| /* The value that caused this error. */ |
| uint64_t invalid_index; |
| }; |
| |
| struct { /* CE_MALFORMED_RPC_PAYLOAD */ |
| |
| /* The (possibly invalid) length of the payload. */ |
| unsigned length; |
| |
| /* The current byte offset into the buffer. I.e. the point at which |
| * we realised we had an invalid payload. |
| */ |
| unsigned current_index; |
| }; |
| |
| struct { /* CE_SYSCALL_FAILED */ |
| |
| /* The syscall that we ran. This value will be a member of |
| * invocation_label or arch_invocation_label from libsel4. |
| */ |
| int syscall; |
| |
| /* The error value returned by seL4. */ |
| int error; |
| }; |
| |
| struct { /* CE_ALLOCATION_FAILURE */ |
| |
| /* The number of bytes that was being allocated when the failure |
| * occurred. This field may be zero if the amount was not relevant. |
| */ |
| size_t alloc_bytes; |
| }; |
| }; |
| } camkes_error_t; |
| |
| /* The action to take to recover from an error. An error handler, as described |
| * below, should return one of these values to indicate what action the glue |
| * code should take. |
| */ |
| typedef enum { |
| |
| /* Used as a sentinel for a context where an action is invalid. Don't ever |
| * return this from an error handler. |
| */ |
| CEA_INVALID = -1, |
| |
| /* Terminate the currently running piece of glue code by returning. If the |
| * offending code was called from a glue code event loop, this generally |
| * means return to the event loop. If the offending code was called from |
| * user code, this means return to user code where the calling function is |
| * expected to be aware (by some out-of-band communication) that an error |
| * has occurred. |
| */ |
| CEA_DISCARD, |
| |
| /* Ignore the error and continue. This is generally dangerous and not what |
| * you want. |
| */ |
| CEA_IGNORE, |
| |
| /* Exit with failure in the current thread. Note that what 'exit' means |
| * here depends on a number of things including whether the thread has a |
| * cap to itself. |
| */ |
| CEA_ABORT, |
| |
| /* Exit with failure and also try to halt the entire system if possible. |
| * This action implies CEA_ABORT and only differs on a debug kernel where |
| * it is possible to stop the system. If you have no error handlers |
| * installed, this is the default action. |
| */ |
| CEA_HALT, |
| |
| } camkes_error_action_t; |
| |
| /* An error handling function. If components define their own error handlers, |
| * they should conform to this prototype. The argument is the error that |
| * occurred and the return value is the recovery action the glue code should |
| * take. |
| */ |
| typedef camkes_error_action_t (*camkes_error_handler_t)(camkes_error_t *); |
| |
| /* Register a component-global error handler. More fine-grained registration |
| * functions (one per-interface) are generated and prototyped in the generated |
| * header. Errors that occur in interface glue code will only invoke this error |
| * handler if no error handler is registered for the interface itself. This |
| * registration function returns a pointer to the old handler. |
| */ |
| camkes_error_handler_t camkes_register_error_handler( |
| camkes_error_handler_t handler); |
| |
| /* Throw an error from glue code. This function is not expected to be called by |
| * user code. |
| */ |
| camkes_error_action_t camkes_error(camkes_error_t *e) COLD; |
| |
| /* Convenience for using halt() inside macro definitions. This is not expected |
| * to be called from user code. Note that it does not halt on a non-debug |
| * kernel. |
| */ |
| #ifdef CONFIG_DEBUG_BUILD |
| #define halt() seL4_DebugHalt() |
| #else |
| #define halt() do { } while (0) |
| #endif |
| |
| #ifdef CONFIG_CAMKES_ERROR_HANDLER_CONFIGURABLE |
| |
| /* Convenience macro for throwing an error from glue code. |
| * handler - A camkes_error_handler_t to invoke. |
| * edata - Error structure to throw to the error handler. |
| * action - Action to take on return of CEA_DISCARD from the error |
| * handler. This should be a GNU statement expression (aka compound |
| * statement). |
| */ |
| #define ERR(handler, edata, action) ({ \ |
| COLD_PATH(); \ |
| camkes_error_t _e = edata; \ |
| _e.filename = __FILE__; \ |
| _e.lineno = __LINE__; \ |
| switch (handler(&_e)) { \ |
| case CEA_DISCARD: \ |
| action; \ |
| UNREACHABLE(); \ |
| case CEA_IGNORE: \ |
| break; \ |
| case CEA_HALT: \ |
| halt(); \ |
| /* If we return we'll just fall through. */ \ |
| case CEA_ABORT: \ |
| abort(); \ |
| /* In case we return */ \ |
| while(1); \ |
| default: \ |
| UNREACHABLE(); \ |
| } \ |
| }) |
| |
| #elif defined(CONFIG_CAMKES_ERROR_HANDLER_GUARD) |
| |
| #define ERR(handler, edata, action) GUARD(false) |
| |
| #elif defined(CONFIG_CAMKES_ERROR_HANDLER_ABORT) |
| |
| #define ERR(handler, edata, action) abort() |
| |
| #elif defined(CONFIG_CAMKES_ERROR_HANDLER_DISCARD) |
| |
| #define ERR(handler, edata, action) action |
| |
| #else |
| |
| #error no error handling mode defined |
| |
| #endif |
| |
| #ifdef CONFIG_CAMKES_ERROR_HANDLER_GUARD |
| |
| #define ERR_IF(cond, handler, edata, action) GUARD(!(cond)) |
| |
| #else |
| |
| /* Conditionally throw an error. */ |
| #define ERR_IF(cond, handler, edata, action) ({ \ |
| if (unlikely(cond)) { \ |
| ERR(handler, edata, action); \ |
| } \ |
| }) |
| |
| #endif |