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(val_) \
107 ({ \
108 absl_status_t code_; \
109 memcpy(&code_, &val_, sizeof(code_)); \
110 status_create(code_, MODULE_ID, __FILE__, code_ == kOk ? 0 : __LINE__); \
111 })
112
113#define _ROM_ERROR_INTO_STATUS(val_) \
114 ({ \
115 uint32_t val32_; \
116 memcpy(&val32_, &val_, sizeof(val32_)); \
117 status_from_rom_error(val32_); \
118 })
119
120/**
121 * Converts a value into a status_t.
122 *
123 * This macro uses the C11 `_Generic` feature to detect the type of the input
124 * expression and apply the appropriate conversion. Once a more thorough
125 * refactoring of the DIFs is done, this can be eliminated.
126 *
127 * @param expr_ Either a `status_t`, `dif_result_t` or `rom_error_t`.
128 * @return The `status_t` representation of the input.
129 */
130#define INTO_STATUS(expr_) \
131 ({ \
132 __auto_type val_ = (expr_); \
133 _Generic(val_, \
134 status_t: val_, \
135 rom_error_t: _ROM_ERROR_INTO_STATUS(val_), \
136 dif_result_t: _DIF_RESULT_INTO_STATUS(val_)); \
137 })
138
139/**
140 * Report an error status.
141 *
142 * This header does not specify how the error is
143 * reported, or if it reported at all. Since status_t encodes the location
144 * of an error, this provides the infrastructure for a lightweight
145 * "stack trace".
146 */
147void status_report(status_t value);
148
149/**
150 * Report an error status at the calling site.
151 *
152 * Given a status_t representing an error, report a status that records
153 * this error and the location of the caller of this macro. Note that
154 * this overwrites the module ID and argument of the status passed.
155 */
156#define STATUS_REPORT_HERE(status) \
157 ({ \
158 absl_status_t err = status_err(status); \
159 status_t report = status_create(err, MODULE_ID, __FILE__, __LINE__); \
160 status_report(report); \
161 })
162
163/**
164 * Evaluates a status_t for Ok or Error status, returning the Ok value.
165 *
166 * This macro is like the `try!` macro (or now `?` operator) in Rust:
167 * It evaluates to the contained OK value or it immediately returns from
168 * the enclosing function with the error value.
169 * In case of error, it will add an entry to the stack trace.
170 *
171 * @param expr_ An expression that can be converted to a `status_t`.
172 * @return The enclosed OK value.
173 */
174#define TRY(expr_) \
175 ({ \
176 status_t status_ = INTO_STATUS(expr_); \
177 if (status_.value < 0) { \
178 STATUS_REPORT_HERE(status_); \
179 return status_; \
180 } \
181 status_.value; \
182 })
183
184// This global constant is available to all modules and is the constant zero.
185// This name intentionally violates the constant naming convention of
186// `kModuleId` because users are expected to provide an override in the form
187// of a preprocessor defintion: `#define MODULE_ID MAKE_MODULE_ID(...)`.
188extern const uint32_t MODULE_ID;
189
190// Operations on status codes:
191/**
192 * Creates a packed status_t.
193 *
194 * @param code An absl_status code.
195 * @param mod_id The module creating the status code.
196 * @param file The filename of the module creating the code.
197 * @param arg The argument associated with the status.
198 * @return `status_t`.
199 */
201status_t status_create(absl_status_t code, uint32_t mod_id, const char *file,
202 int32_t arg);
203
204/**
205 * Convert a rom_error_t into a status_t.
206 *
207 * @param val A rom_error_t, casted as uint32_t.
208 * @return `status_t`
209 */
211status_t status_from_rom_error(uint32_t val);
212
213/**
214 * Extracts the packed values from a status code.
215 *
216 * @param s The status code to extract values from.
217 * @param code Pointer to the english name of the status code.
218 * @param arg Pointer to an integer argument.
219 * @param mod_id Pointer to a char[3] buffer for the module id.
220 * @return True if the status represents and error, False if the status
221 * represents Ok.
222 */
224bool status_extract(status_t s, const char **code, int32_t *arg, char *mod_id);
225
226/**
227 * Returns whether the status value represents Ok.
228 *
229 * @param s The status code.
230 * @return True if the status represents Ok.
231 */
233OT_ALWAYS_INLINE bool status_ok(status_t s) { return s.value >= 0; }
234
235/**
236 * Returns the absl status code in the status.
237 *
238 * @param s The status code.
239 * @return `absl_status_t` contained within the status_t.
240 */
242OT_ALWAYS_INLINE absl_status_t status_err(status_t s) {
243 return s.value < 0 ? (absl_status_t)bitfield_field32_read(
244 OT_UNSIGNED(s.value), STATUS_FIELD_CODE)
245 : kOk;
246}
247
248// Create a status with an optional argument.
249// TODO(cfrantz, alphan): Figure out how we want to create statuses in
250// silicon_creator code.
251#define STATUS_CREATE(s_, ...) \
252 ({ \
253 static_assert(OT_VA_ARGS_COUNT(_, __VA_ARGS__) <= 2, \
254 "status macros take 0 or 1 arguments"); \
255 RECORD_STATUS_CREATE(s_, MODULE_ID, __FILE__); \
256 status_create(s_, MODULE_ID, __FILE__, OT_GET_LAST_ARG(__VA_ARGS__)); \
257 })
258
259// Helpers for creating statuses of various kinds.
260// clang-format off
261#define OK_STATUS(...) STATUS_CREATE(kOk, 0, ##__VA_ARGS__)
262#define CANCELLED(...) STATUS_CREATE(kCancelled, __LINE__, ##__VA_ARGS__)
263#define UNKNOWN(...) STATUS_CREATE(kUnknown, __LINE__, ##__VA_ARGS__)
264#define INVALID_ARGUMENT(...) STATUS_CREATE(kInvalidArgument, __LINE__, ##__VA_ARGS__)
265#define DEADLINE_EXCEEDED(...) STATUS_CREATE(kDeadlineExceeded, __LINE__, ##__VA_ARGS__)
266#define NOT_FOUND(...) STATUS_CREATE(kNotFound, __LINE__, ##__VA_ARGS__)
267#define ALREADY_EXISTS(...) STATUS_CREATE(kAlreadyExists, __LINE__, ##__VA_ARGS__)
268#define PERMISSION_DENIED(...) STATUS_CREATE(kPermissionDenied, __LINE__, ##__VA_ARGS__)
269#define RESOURCE_EXHAUSTED(...) STATUS_CREATE(kResourceExhausted, __LINE__, ##__VA_ARGS__)
270#define FAILED_PRECONDITION(...) STATUS_CREATE(kFailedPrecondition, __LINE__, ##__VA_ARGS__)
271#define ABORTED(...) STATUS_CREATE(kAborted, __LINE__, ##__VA_ARGS__)
272#define OUT_OF_RANGE(...) STATUS_CREATE(kOutOfRange, __LINE__, ##__VA_ARGS__)
273#define UNIMPLEMENTED(...) STATUS_CREATE(kUnimplemented, __LINE__, ##__VA_ARGS__)
274#define INTERNAL(...) STATUS_CREATE(kInternal, __LINE__, ##__VA_ARGS__)
275#define UNAVAILABLE(...) STATUS_CREATE(kUnavailable, __LINE__, ##__VA_ARGS__)
276#define DATA_LOSS(...) STATUS_CREATE(kDataLoss, __LINE__, ##__VA_ARGS__)
277#define UNAUTHENTICATED(...) STATUS_CREATE(kUnauthenticated, __LINE__, ##__VA_ARGS__)
278// clang-format on
279
280#ifdef __cplusplus
281}
282#endif
283#endif // OPENTITAN_SW_DEVICE_LIB_BASE_STATUS_H_