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
23 extern "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  */
55 typedef 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. */
69 typedef struct ot_status_create_record {
70  // Set to OT_SCR_UNKNOWN_MOD_ID if not fixed at compile time
71  uint32_t module_id;
72  char filename[124];
74 
75 enum 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  */
153 void 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(...)`.
194 extern 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  */
207 status_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  */
221 bool 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  */
230 OT_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  */
239 OT_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_