| // Copyright lowRISC contributors. |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| // SPDX-License-Identifier: Apache-2.0 |
| |
| #ifndef OPENTITAN_SW_DEVICE_LIB_BASE_STATUS_H_ |
| #define OPENTITAN_SW_DEVICE_LIB_BASE_STATUS_H_ |
| #include <assert.h> |
| #include <limits.h> |
| #include <stdbool.h> |
| #include <stdint.h> |
| #include <string.h> |
| |
| #include "sw/device/lib/base/bitfield.h" |
| #include "sw/device/lib/base/macros.h" |
| #include "sw/device/lib/dif/dif_base.h" |
| |
| #define USING_INTERNAL_STATUS |
| #include "sw/device/lib/base/internal/status.h" |
| #undef USING_INTERNAL_STATUS |
| |
| #ifdef __cplusplus |
| extern "C" { |
| #endif |
| |
| /** |
| * We use the error category codes from absl_status.h. We build a packed |
| * status value that identifies the source of the error (in the form of the |
| * module identifier and line number). |
| * |
| * By default, the module identifier is the first three letters of the |
| * source filename. The identifier can be overridden (per-module) with the |
| * DECLARE_MODULE_ID macro. |
| * |
| * Our status codes are arranged as a packed bitfield, with the sign |
| * bit signifying whether the value represents a result or an error. |
| * |
| * All Ok (good) values: |
| * 32 31 0 |
| * +---+---------------------------------------------+ |
| * | | 31 bit | |
| * | 0 | Result | |
| * +---+---------------------------------------------+ |
| * |
| * All Error values: |
| * 32 31 26 21 16 5 0 |
| * +---+-------+-------+-------+-------------+-------+ |
| * | | 15 bit | 11 bit | 5 bit | |
| * | 1 | Module Identifier | Line Number | code | |
| * +---+-------+-------+-------+-------------+-------+ |
| * |
| * The module identifier value is interpreted as three 5-bit fields |
| * representing the characters [0x40..0x5F] (e.g. [@ABC ... _]). |
| */ |
| typedef struct status { |
| int32_t value; |
| } status_t; |
| |
| /** |
| * Converts a value into a status_t. |
| * |
| * This macro uses the GCC/clang `__builtin_types_compatible_p` extension to |
| * detect the type of the input expression and apply the appropriate |
| * conversion. Once a more thorough refactoring of the DIFs is done, this |
| * can be eliminated. |
| * |
| * @param expr_ Either a `status_t` or `dif_result_t`. |
| * @return The `status_t` representation of the input. |
| */ |
| #define INTO_STATUS(expr_) \ |
| ({ \ |
| typeof(expr_) ex_ = (expr_); \ |
| static_assert(__builtin_types_compatible_p(typeof(ex_), status_t) || \ |
| __builtin_types_compatible_p(typeof(ex_), dif_result_t), \ |
| "Expressions passed to INTO_STATUS() must be of type " \ |
| "`status_t`, or `dif_result_t`"); \ |
| status_t status_; \ |
| if (__builtin_types_compatible_p(typeof(ex_), status_t)) { \ |
| memcpy(&status_, &ex_, sizeof(status_)); \ |
| } else if (__builtin_types_compatible_p(typeof(ex_), dif_result_t)) { \ |
| absl_status_t code; \ |
| memcpy(&code, &ex_, sizeof(code)); \ |
| status_ = status_create(code, MODULE_ID, __FILE__, \ |
| code == kOk ? 0 : __LINE__); \ |
| } \ |
| status_; \ |
| }) |
| |
| /** |
| * Evaluates a status_t for Ok or Error status, returning the Ok value. |
| * |
| * This macro is like the `try!` macro (or now `?` operator) in Rust: |
| * It evaluates to the contained OK value or it immediately returns from |
| * the enclosing function with the error value. |
| * |
| * @param expr_ An expression that can be converted to a `status_t`. |
| * @return The enclosed OK value. |
| */ |
| #define TRY(expr_) \ |
| ({ \ |
| status_t status_ = INTO_STATUS(expr_); \ |
| if (status_.value < 0) { \ |
| return status_; \ |
| } \ |
| status_.value; \ |
| }) |
| |
| // This global constant is available to all modules and is the constant zero. |
| // This name intentionally violates the constant naming convention of |
| // `kModuleId` because users are expected to provide an override in the form |
| // of a preprocessor defintion: `#define MODULE_ID MAKE_MODULE_ID(...)`. |
| extern const uint32_t MODULE_ID; |
| |
| // Operations on status codes: |
| /** |
| * Creates a packed status_t. |
| * |
| * @param code An absl_status code. |
| * @param mod_id The module creating the status code. |
| * @param file The filename of the module creating the code. |
| * @param arg The argument associated with the status. |
| * @return `status_t`. |
| */ |
| status_t status_create(absl_status_t code, uint32_t mod_id, const char *file, |
| int32_t arg); |
| |
| /** |
| * Extracts the packed values from a status code. |
| * |
| * @param s The status code to extract values from. |
| * @param code Pointer to the english name of the status code. |
| * @param arg Pointer to an integer argument. |
| * @param mod_id Pointer to a char[3] buffer for the module id. |
| * @return True if the status represents and error, False if the status |
| * represents Ok. |
| */ |
| bool status_extract(status_t s, const char **code, int32_t *arg, char *mod_id); |
| |
| /** |
| * Returns whether the status value represents Ok. |
| * |
| * @param s The status code. |
| * @return True if the status represents Ok. |
| */ |
| OT_ALWAYS_INLINE bool status_ok(status_t s) { return s.value >= 0; } |
| |
| /** |
| * Returns the absl status code in the status. |
| * |
| * @param s The status code. |
| * @return `absl_status_t` contained within the status_t. |
| */ |
| OT_ALWAYS_INLINE absl_status_t status_err(status_t s) { |
| return s.value < 0 |
| ? (absl_status_t)bitfield_field32_read(s.value, STATUS_FIELD_CODE) |
| : kOk; |
| } |
| |
| // Create a status with an optional argument. |
| // TODO(cfrantz, alphan): Figure out how we want to create statuses in |
| // silicon_creator code. |
| #define STATUS_CREATE(s_, ...) \ |
| ({ \ |
| static_assert(OT_VA_ARGS_COUNT(_, __VA_ARGS__) <= 2, \ |
| "status macros take 0 or 1 arguments"); \ |
| status_create(s_, MODULE_ID, __FILE__, OT_GET_LAST_ARG(__VA_ARGS__)); \ |
| }) |
| |
| // Helpers for creating statuses of various kinds. |
| // clang-format off |
| #define OK_STATUS(...) STATUS_CREATE(kOk, 0, ##__VA_ARGS__) |
| #define CANCELLED(...) STATUS_CREATE(kCancelled, __LINE__, ##__VA_ARGS__) |
| #define UNKNOWN(...) STATUS_CREATE(kUnknown, __LINE__, ##__VA_ARGS__) |
| #define INVALID_ARGUMENT(...) STATUS_CREATE(kInvalidArgument, __LINE__, ##__VA_ARGS__) |
| #define DEADLINE_EXCEEDED(...) STATUS_CREATE(kDeadlineExceeded, __LINE__, ##__VA_ARGS__) |
| #define NOT_FOUND(...) STATUS_CREATE(kNotFound, __LINE__, ##__VA_ARGS__) |
| #define ALREADY_EXISTS(...) STATUS_CREATE(kAlreadyExists, __LINE__, ##__VA_ARGS__) |
| #define PERMISSION_DENIED(...) STATUS_CREATE(kPermissionDenied, __LINE__, ##__VA_ARGS__) |
| #define RESOURCE_EXHAUSTED(...) STATUS_CREATE(kResourceExhausted, __LINE__, ##__VA_ARGS__) |
| #define FAILED_PRECONDITION(...) STATUS_CREATE(kFailedPrecondition, __LINE__, ##__VA_ARGS__) |
| #define ABORTED(...) STATUS_CREATE(kAborted, __LINE__, ##__VA_ARGS__) |
| #define OUT_OF_RANGE(...) STATUS_CREATE(kOutOfRange, __LINE__, ##__VA_ARGS__) |
| #define UNIMPLEMENTED(...) STATUS_CREATE(kUnimplemented, __LINE__, ##__VA_ARGS__) |
| #define INTERNAL(...) STATUS_CREATE(kInternal, __LINE__, ##__VA_ARGS__) |
| #define UNAVAILABLE(...) STATUS_CREATE(kUnavailable, __LINE__, ##__VA_ARGS__) |
| #define DATA_LOSS(...) STATUS_CREATE(kDataLoss, __LINE__, ##__VA_ARGS__) |
| #define UNAUTHENTICATED(...) STATUS_CREATE(kUnauthenticated, __LINE__, ##__VA_ARGS__) |
| // clang-format on |
| |
| #ifdef __cplusplus |
| } |
| #endif |
| #endif // OPENTITAN_SW_DEVICE_LIB_BASE_STATUS_H_ |