Software APIs
status.h
1// Copyright lowRISC contributors (OpenTitan project).
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
5#ifndef OPENTITAN_SW_DEVICE_LIB_BASE_STATUS_H_
6#define OPENTITAN_SW_DEVICE_LIB_BASE_STATUS_H_
7#include <assert.h>
8#include <limits.h>
9#include <stdbool.h>
10#include <stdint.h>
11#include <string.h>
12
16#include "sw/device/silicon_creator/lib/error.h"
17
18#define USING_INTERNAL_STATUS
19#include "sw/device/lib/base/internal/status.h"
20#undef USING_INTERNAL_STATUS
21
22#ifdef __cplusplus
23extern "C" {
24#endif
25
26/**
27 * We use the error category codes from absl_status.h. We build a packed
28 * status value that identifies the source of the error (in the form of the
29 * module identifier and line number).
30 *
31 * By default, the module identifier is the first three letters of the
32 * source filename. The identifier can be overridden (per-module) with the
33 * DECLARE_MODULE_ID macro.
34 *
35 * Our status codes are arranged as a packed bitfield, with the sign
36 * bit signifying whether the value represents a result or an error.
37 *
38 * All Ok (good) values:
39 * 32 31 0
40 * +---+---------------------------------------------+
41 * | | 31 bit |
42 * | 0 | Result |
43 * +---+---------------------------------------------+
44 *
45 * All Error values:
46 * 32 31 26 21 16 5 0
47 * +---+-------+-------+-------+-------------+-------+
48 * | | 15 bit | 11 bit | 5 bit |
49 * | 1 | Module Identifier | Line Number | code |
50 * +---+-------+-------+-------+-------------+-------+
51 *
52 * The module identifier value is interpreted as three 5-bit fields
53 * representing the characters [0x40..0x5F] (e.g. [@ABC ... _]).
54 */
55typedef struct status {
56 int32_t value;
57} status_t;
58
59/**
60 * Record the creation of a status_t in a special section of the executable.
61 * This can be used by tools to more easily decode status_t values.
62 */
63#define RECORD_STATUS_CREATE(code, mod_id, file) \
64 _RECORD_STATUS_CREATE(code, mod_id, file)
65
66/* Multiple instances of this structure will go to the .ot.status_create_record
67 * section and will be read by tools. Therefore it is important that the layout
68 * of this structure remains stable. */
70 // Set to OT_SCR_UNKNOWN_MOD_ID if not fixed at compile time
71 uint32_t module_id;
72 char filename[124];
73} ot_status_create_record_t;
74
75enum ot_status_create_record_magic {
76 OT_SCR_UNKNOWN_MOD_ID = 0xffffffff,
77};
78
79#ifdef __cplusplus
80/* Recording statuses in C++ is not really useful since that means the code
81 * will not run on the target. It also creates problems with g++ because
82 * some statuses are created in classes and putting symbols in sections from
83 * both global functions and classes creates in conflict in section types. */
84#define _RECORD_STATUS_CREATE(...)
85#else /* __cplusplus */
86#define _RECORD_STATUS_CREATE(code_val, mod_id, file) \
87 /* We are only interested in non-Ok statuses so we try to avoid them. */ \
88 /* Assume that the code is a constant value so that the compiler will */ \
89 /* remove the code when the condition is false. */ \
90 __builtin_choose_expr(code_val != kOk, \
91 __RECORD_STATUS_CREATE(code_val, mod_id, file), ({}))
92
93#define __RECORD_STATUS_CREATE(code_val, mod_id, file) \
94 ({ \
95 OT_SECTION(".ot.status_create_record") \
96 OT_USED \
97 static const ot_status_create_record_t kOtStatusRecord = { \
98 /* mod_id is either an external pointer, or a value produced by MAKE_MODULE_ID */ \
99 /* in which case it is already shifted by 16 bits so it can be OR'ed. */ \
100 .module_id = __builtin_constant_p(mod_id) ? mod_id : OT_SCR_UNKNOWN_MOD_ID, \
101 .filename = file, \
102 }; \
103 })
104#endif /* __cplusplus */
105
106#define DIF_RESULT_INTO_STATUS(expr_) \
107 ({ \
108 typeof(expr_) _val = (expr_); \
109 absl_status_t code; \
110 memcpy(&code, &_val, sizeof(code)); \
111 status_create(code, MODULE_ID, __FILE__, code == kOk ? 0 : __LINE__); \
112 })
113
114#define ROM_ERROR_INTO_STATUS(expr_) \
115 ({ \
116 typeof(expr_) ex_ = (expr_); \
117 uint32_t val; \
118 memcpy(&val, &ex_, sizeof(val)); \
119 absl_status_t code = \
120 val == kErrorOk ? 0 \
121 : bitfield_field32_read(val, ROM_ERROR_FIELD_STATUS); \
122 int32_t arg = (int32_t)bitfield_field32_read(val, ROM_ERROR_FIELD_ERROR); \
123 uint32_t mod = bitfield_field32_read(val, ROM_ERROR_FIELD_MODULE); \
124 uint32_t module = (mod & 0x1F) << 16 | (mod & 0x1F00) << (21 - 8); \
125 status_create(code, module, __FILE__, code == kOk ? kErrorOk : arg); \
126 })
127
128/**
129 * Converts a value into a status_t.
130 *
131 * This macro uses the C11 `_Generic` feature to detect the type of the input
132 * expression and apply the appropriate conversion. Once a more thorough
133 * refactoring of the DIFs is done, this can be eliminated.
134 *
135 * @param expr_ Either a `status_t`, `dif_result_t` or `rom_error_t`.
136 * @return The `status_t` representation of the input.
137 */
138// clang-format off
139 #define INTO_STATUS(expr_) _Generic((expr_), \
140 status_t: (expr_), \
141 rom_error_t: ROM_ERROR_INTO_STATUS(expr_), \
142 dif_result_t: DIF_RESULT_INTO_STATUS(expr_))
143// clang-format on
144
145/**
146 * Report an error status.
147 *
148 * This header does not specify how the error is
149 * reported, or if it reported at all. Since status_t encodes the location
150 * of an error, this provides the infrastructure for a lightweight
151 * "stack trace".
152 */
153void status_report(status_t value);
154
155/**
156 * Report an error status at the calling site.
157 *
158 * Given a status_t representing an error, report a status that records
159 * this error and the location of the caller of this macro. Note that
160 * this overwrites the module ID and argument of the status passed.
161 */
162#define STATUS_REPORT_HERE(status) \
163 ({ \
164 absl_status_t err = status_err(status); \
165 status_t report = status_create(err, MODULE_ID, __FILE__, __LINE__); \
166 status_report(report); \
167 })
168
169/**
170 * Evaluates a status_t for Ok or Error status, returning the Ok value.
171 *
172 * This macro is like the `try!` macro (or now `?` operator) in Rust:
173 * It evaluates to the contained OK value or it immediately returns from
174 * the enclosing function with the error value.
175 * In case of error, it will add an entry to the stack trace.
176 *
177 * @param expr_ An expression that can be converted to a `status_t`.
178 * @return The enclosed OK value.
179 */
180#define TRY(expr_) \
181 ({ \
182 status_t status_ = INTO_STATUS(expr_); \
183 if (status_.value < 0) { \
184 STATUS_REPORT_HERE(status_); \
185 return status_; \
186 } \
187 status_.value; \
188 })
189
190// This global constant is available to all modules and is the constant zero.
191// This name intentionally violates the constant naming convention of
192// `kModuleId` because users are expected to provide an override in the form
193// of a preprocessor defintion: `#define MODULE_ID MAKE_MODULE_ID(...)`.
194extern const uint32_t MODULE_ID;
195
196// Operations on status codes:
197/**
198 * Creates a packed status_t.
199 *
200 * @param code An absl_status code.
201 * @param mod_id The module creating the status code.
202 * @param file The filename of the module creating the code.
203 * @param arg The argument associated with the status.
204 * @return `status_t`.
205 */
207status_t status_create(absl_status_t code, uint32_t mod_id, const char *file,
208 int32_t arg);
209
210/**
211 * Extracts the packed values from a status code.
212 *
213 * @param s The status code to extract values from.
214 * @param code Pointer to the english name of the status code.
215 * @param arg Pointer to an integer argument.
216 * @param mod_id Pointer to a char[3] buffer for the module id.
217 * @return True if the status represents and error, False if the status
218 * represents Ok.
219 */
221bool status_extract(status_t s, const char **code, int32_t *arg, char *mod_id);
222
223/**
224 * Returns whether the status value represents Ok.
225 *
226 * @param s The status code.
227 * @return True if the status represents Ok.
228 */
230OT_ALWAYS_INLINE bool status_ok(status_t s) { return s.value >= 0; }
231
232/**
233 * Returns the absl status code in the status.
234 *
235 * @param s The status code.
236 * @return `absl_status_t` contained within the status_t.
237 */
239OT_ALWAYS_INLINE absl_status_t status_err(status_t s) {
240 return s.value < 0 ? (absl_status_t)bitfield_field32_read(
241 OT_UNSIGNED(s.value), STATUS_FIELD_CODE)
242 : kOk;
243}
244
245// Create a status with an optional argument.
246// TODO(cfrantz, alphan): Figure out how we want to create statuses in
247// silicon_creator code.
248#define STATUS_CREATE(s_, ...) \
249 ({ \
250 static_assert(OT_VA_ARGS_COUNT(_, __VA_ARGS__) <= 2, \
251 "status macros take 0 or 1 arguments"); \
252 RECORD_STATUS_CREATE(s_, MODULE_ID, __FILE__); \
253 status_create(s_, MODULE_ID, __FILE__, OT_GET_LAST_ARG(__VA_ARGS__)); \
254 })
255
256// Helpers for creating statuses of various kinds.
257// clang-format off
258#define OK_STATUS(...) STATUS_CREATE(kOk, 0, ##__VA_ARGS__)
259#define CANCELLED(...) STATUS_CREATE(kCancelled, __LINE__, ##__VA_ARGS__)
260#define UNKNOWN(...) STATUS_CREATE(kUnknown, __LINE__, ##__VA_ARGS__)
261#define INVALID_ARGUMENT(...) STATUS_CREATE(kInvalidArgument, __LINE__, ##__VA_ARGS__)
262#define DEADLINE_EXCEEDED(...) STATUS_CREATE(kDeadlineExceeded, __LINE__, ##__VA_ARGS__)
263#define NOT_FOUND(...) STATUS_CREATE(kNotFound, __LINE__, ##__VA_ARGS__)
264#define ALREADY_EXISTS(...) STATUS_CREATE(kAlreadyExists, __LINE__, ##__VA_ARGS__)
265#define PERMISSION_DENIED(...) STATUS_CREATE(kPermissionDenied, __LINE__, ##__VA_ARGS__)
266#define RESOURCE_EXHAUSTED(...) STATUS_CREATE(kResourceExhausted, __LINE__, ##__VA_ARGS__)
267#define FAILED_PRECONDITION(...) STATUS_CREATE(kFailedPrecondition, __LINE__, ##__VA_ARGS__)
268#define ABORTED(...) STATUS_CREATE(kAborted, __LINE__, ##__VA_ARGS__)
269#define OUT_OF_RANGE(...) STATUS_CREATE(kOutOfRange, __LINE__, ##__VA_ARGS__)
270#define UNIMPLEMENTED(...) STATUS_CREATE(kUnimplemented, __LINE__, ##__VA_ARGS__)
271#define INTERNAL(...) STATUS_CREATE(kInternal, __LINE__, ##__VA_ARGS__)
272#define UNAVAILABLE(...) STATUS_CREATE(kUnavailable, __LINE__, ##__VA_ARGS__)
273#define DATA_LOSS(...) STATUS_CREATE(kDataLoss, __LINE__, ##__VA_ARGS__)
274#define UNAUTHENTICATED(...) STATUS_CREATE(kUnauthenticated, __LINE__, ##__VA_ARGS__)
275// clang-format on
276
277#ifdef __cplusplus
278}
279#endif
280#endif // OPENTITAN_SW_DEVICE_LIB_BASE_STATUS_H_