| // 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_ |