Software APIs
check.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_TESTING_TEST_FRAMEWORK_CHECK_H_
6#define OPENTITAN_SW_DEVICE_LIB_TESTING_TEST_FRAMEWORK_CHECK_H_
7
8#include <stdbool.h>
9
12#include "sw/device/lib/base/status.h"
16#include "sw/device/lib/testing/test_framework/status.h"
17
18/**
19 * Runtime assertion macros with log.h integration.
20 */
21
22#ifdef __cplusplus
23#error "This file is C-only; it is not a polyglot header!"
24#endif
25
26/**
27 * Checks that the given condition is true. If the condition is false, this
28 * function logs and then returns a `kInternal` error.
29 *
30 * @param condition An expression to check.
31 * @param ... Arguments to a LOG_* macro, which are evaluated if the check
32 * fails.
33 */
34#define CHECK_IMPL(condition, ...) \
35 ({ \
36 status_t sts_ = OK_STATUS(); \
37 /* NOTE: The volatile intermediate variable is added to guarantee that \
38 * this macro won't be optimized out when used with compiling time \
39 * constants.*/ \
40 volatile bool res_ = (condition); \
41 if (!res_) { \
42 /* NOTE: because the condition in this if \
43 statement can be statically determined, \
44 only one of the below string constants \
45 will be included in the final binary.*/ \
46 if (OT_VA_ARGS_COUNT(_, ##__VA_ARGS__) == 0) { \
47 LOG_ERROR("CHECK-fail: " #condition); \
48 } else { \
49 LOG_ERROR("CHECK-fail: " __VA_ARGS__); \
50 } \
51 sts_ = INTERNAL(); \
52 } \
53 sts_; \
54 })
55
56/**
57 * Checks that the given condition is true. If the condition is false, this
58 * function logs and then aborts.
59 *
60 * @param condition An expression to check.
61 * @param ... Arguments to a LOG_* macro, which are evaluated if the check
62 * fails.
63 */
64#define CHECK(condition, ...) \
65 do { \
66 if (status_err(CHECK_IMPL(condition, ##__VA_ARGS__))) { \
67 /* Currently, this macro will call into \
68 the test failure code, which logs \
69 "FAIL" and aborts. In the future, \
70 we will try to condition on whether \
71 or not this is a test.*/ \
72 test_status_set(kTestStatusFailed); \
73 } \
74 } while (false)
75
76/**
77 * Same as `CHECK` above but returns a status_t if false rather than aborting.
78 */
79#define TRY_CHECK(condition, ...) TRY(CHECK_IMPL(condition, ##__VA_ARGS__))
80
81// Note that this is *not* a polyglot header, so we can use the C11-only
82// _Generic keyword safely.
83// See: https://en.cppreference.com/w/c/language/generic
84// clang-format off
85#define SHOW_MISMATCH_FMT_STR_(a) _Generic((a), \
86 bool: "CHECK-fail: [%d] got: 0x%02x; want: 0x%02x", \
87 int8_t: "CHECK-fail: [%d] got: 0x%02x; want: 0x%02x", \
88 uint8_t: "CHECK-fail: [%d] got: 0x%02x; want: 0x%02x", \
89 int16_t: "CHECK-fail: [%d] got: 0x%04x; want: 0x%04x", \
90 uint16_t: "CHECK-fail: [%d] got: 0x%04x; want: 0x%04x", \
91 int32_t: "CHECK-fail: [%d] got: 0x%08x; want: 0x%08x", \
92 uint32_t: "CHECK-fail: [%d] got: 0x%08x; want: 0x%08x", \
93 int64_t: "CHECK-fail: [%d] got: 0x%016x; want: 0x%016x", \
94 uint64_t: "CHECK-fail: [%d] got: 0x%016x; want: 0x%016x")
95#define SHOW_MATCH_FMT_STR_(a) _Generic((a), \
96 bool: "CHECK-fail: [%d] both equal: 0x%02x", \
97 int8_t: "CHECK-fail: [%d] both equal: 0x%02x", \
98 uint8_t: "CHECK-fail: [%d] both equal: 0x%02x", \
99 int16_t: "CHECK-fail: [%d] both equal: 0x%04x", \
100 uint16_t: "CHECK-fail: [%d] both equal: 0x%04x", \
101 int32_t: "CHECK-fail: [%d] both equal: 0x%08x", \
102 uint32_t: "CHECK-fail: [%d] both equal: 0x%08x", \
103 int64_t: "CHECK-fail: [%d] both equal: 0x%016x", \
104 uint64_t: "CHECK-fail: [%d] both equal: 0x%016x")
105// clang-format on
106
107/**
108 * Compare `actual_` against `ref_` buffer.
109 *
110 * Prints matches between `actual_` and `ref_` before logging an error.
111 *
112 * @param expect_eq_ True if the arrays are expected to be equal, false
113 * otherwise.
114 * @param actual_ Buffer containing actual values.
115 * @param ref_ Buffer containing the reference values.
116 * @param size_ Number of items to compare.
117 * @param ... Arguments to a LOG_* macro, which are evaluated if the check.
118 * @return Either `kOk` or `kInternal`.
119 */
120
121#define CHECK_ARRAYS_IMPL(expect_eq_, actual_, ref_, size_, ...) \
122 ({ \
123 static_assert(sizeof(*(actual_)) == sizeof(*(ref_)), \
124 "CHECK_ARRAYS requires arguments of equal size."); \
125 status_t sts_ = OK_STATUS(); \
126 volatile bool is_eq = \
127 memcmp((actual_), (ref_), size_ * sizeof(*(actual_))) == 0; \
128 if (is_eq != expect_eq_) { \
129 if (OT_VA_ARGS_COUNT(_, ##__VA_ARGS__) == 0) { \
130 LOG_INFO("CHECK-fail: " #actual_ " %smatches " #ref_, \
131 expect_eq_ ? " un" : " "); \
132 } else { \
133 LOG_INFO("CHECK-fail: " __VA_ARGS__); \
134 } \
135 for (size_t i = 0; i < size_; ++i) { \
136 if (expect_eq_) { \
137 LOG_INFO(SHOW_MISMATCH_FMT_STR_((actual_)[i]), i, (actual_)[i], \
138 (ref_)[i]); \
139 } else { \
140 LOG_INFO(SHOW_MATCH_FMT_STR_((actual_)[i]), i, (actual_)[i]); \
141 } \
142 } \
143 sts_ = INTERNAL(); \
144 } \
145 sts_; \
146 })
147
148/**
149 * Compare `num_items_` of `actual_` against `expected_` buffer.
150 *
151 * Prints differences between `actual_` and `expected_` before logging an error.
152 * Note in case the arrays are not equal the test will be terminated.
153 * @param actual_ Buffer containing actual values.
154 * @param expected_ Buffer containing expected values.
155 * @param num_items_ Number of items to compare.
156 * @param ... Arguments to a LOG_* macro, which are evaluated if the check.
157 */
158#define CHECK_ARRAYS_EQ(actual_, expected_, num_items_, ...) \
159 do { \
160 if (status_err(CHECK_ARRAYS_IMPL(true, actual_, expected_, num_items_, \
161 ##__VA_ARGS__))) { \
162 /* Currently, this macro will call into the test failure code, \
163 which logs "FAIL" and aborts. In the future, we will try to \
164 condition on whether or not this is a test.*/ \
165 test_status_set(kTestStatusFailed); \
166 } \
167 } while (false)
168
169/**
170 * Same as `CHECK_ARRAYS_EQ` above but returns `kInternal` if the arrays are not
171 * equal rather than aborting.
172 */
173#define TRY_CHECK_ARRAYS_EQ(actual_, expected_, num_items_, ...) \
174 TRY(CHECK_ARRAYS_IMPL(true, actual_, expected_, num_items_, ##__VA_ARGS__))
175
176/**
177 * Same as `CHECK_ARRAYS_NE` above but returns `kInternal` if the arrays are not
178 * equal rather than aborting.
179 */
180#define TRY_CHECK_ARRAYS_NE(actual_, expected_, num_items_, ...) \
181 TRY(CHECK_ARRAYS_IMPL(false, actual_, expected_, num_items_, ##__VA_ARGS__))
182
183/**
184 * Compare `num_items_` of `actual_` against `not_expected_` buffer.
185 *
186 * Prints matches between `actual_` and `not_expected_` before logging an error.
187 *
188 * @param actual_ Buffer containing actual values.
189 * @param not_expected_ Buffer containing not expected values.
190 * @param num_items_ Number of items to compare.
191 * @param ... Arguments to a LOG_* macro, which are evaluated if the check.
192 */
193#define CHECK_ARRAYS_NE(actual_, not_expected_, num_items_, ...) \
194 do { \
195 if (status_err(CHECK_ARRAYS_IMPL(false, actual_, not_expected_, \
196 num_items_, ##__VA_ARGS__))) { \
197 /* Currently, this macro will call into the test failure code, which \
198 logs "FAIL" and aborts. In the future, we will try to condition on \
199 whether or not this is a test.*/ \
200 test_status_set(kTestStatusFailed); \
201 } \
202 } while (false)
203
204/**
205 * Checks the characters of two strings are the same, up to and including the
206 * first null character. The CHECK macro is called on each character pair.
207 *
208 * @param actual_ The first string in the comparison.
209 * @param expected_ The second string in the comparison.
210 */
211#define CHECK_STR_EQ(actual_, expected_) \
212 do { \
213 size_t i = 0; \
214 const char *expected = (expected_); \
215 const char *actual = (actual_); \
216 do { \
217 CHECK(actual[i] == expected[i], \
218 "Strings differ at char %d, so \"%s\" != \"%s\".", i, actual, \
219 expected); \
220 ++i; \
221 } while (actual[i] != '\0' || expected[i] != '\0'); \
222 } while (false)
223
224/**
225 * Checks that the given DIF call returns kDifOk. If the DIF call returns a
226 * different dif_result_t value (defined in sw/device/lib/dif/dif_base.h), this
227 * function logs and then aborts.
228 *
229 * @param dif_call DIF call to invoke and check its return value.
230 * @param ... Arguments to a LOG_* macro, which are evaluated if the check
231 * fails.
232 */
233#define CHECK_DIF_OK(dif_call, ...) \
234 do { \
235 dif_result_t dif_result = dif_call; \
236 if (dif_result != kDifOk) { \
237 /* NOTE: because the condition in this if \
238 statement can be statically determined, \
239 only one of the below string constants \
240 will be included in the final binary.*/ \
241 if (OT_VA_ARGS_COUNT(_, ##__VA_ARGS__) == 0) { \
242 LOG_ERROR("DIF-fail: " #dif_call " returns %d", dif_result); \
243 } else { \
244 LOG_ERROR("DIF-fail: " __VA_ARGS__); \
245 } \
246 /* Currently, this macro will call into \
247 the test failure code, which logs \
248 "FAIL" and aborts. In the future, \
249 we will try to condition on whether \
250 or not this is a test.*/ \
251 test_status_set(kTestStatusFailed); \
252 } \
253 } while (false)
254
255/**
256 * Prints `status_t` using a format compatible with DV.
257 *
258 * The %r specifier supported by the software print module is no compatible with
259 * the DV environment.
260 *
261 * If you landed here after reviewing a DV error log: Please read the status.h
262 * documentation for more details on how `status_t` encodes the module id, line
263 * number and error code.
264 *
265 * @param expr An expression which evaluates to a `status_t`.
266 *
267 */
268#define _LOG_ERROR_STATUS_DV(expr) \
269 do { \
270 status_t status_ = expr; \
271 const char *msg; \
272 char mod_id[3] = {0}; \
273 int line; \
274 if (status_extract(status_, &msg, &line, mod_id)) { \
275 LOG_ERROR("CHECK-STATUS-fail: %c%c%c:%d = %s", mod_id[0], mod_id[1], \
276 mod_id[2], line, msg); \
277 } else { \
278 LOG_ERROR("CHECK-STATUS-fail: 0x%08x", status_); \
279 } \
280 } while (false)
281
282/**
283 * Unwrap a `status_t` when it represents a non-error value, otherwise prints a
284 * human-readable error message and abort.
285 *
286 * @param expr An expression which evaluates to a `status_t`.
287 */
288#define UNWRAP(expr, ...) \
289 ({ \
290 status_t status_ = expr; \
291 if (!status_ok(status_)) { \
292 if (device_log_bypass_uart_address()) { \
293 _LOG_ERROR_STATUS_DV(status_); \
294 } else { \
295 LOG_ERROR("CHECK-STATUS-fail: %r", status_); \
296 } \
297 test_status_set(kTestStatusFailed); \
298 } \
299 status_.value; \
300 })
301
302/**
303 * Checks that the `status_t` represents a non-error value.
304 *
305 * Prints a human-readable error message if the status represents an error.
306 *
307 * @param expr An expression which evaluates to a `status_t`.
308 */
309#define CHECK_STATUS_OK(expr, ...) \
310 do { \
311 status_t status_ = expr; \
312 if (!status_ok(status_)) { \
313 if (device_log_bypass_uart_address()) { \
314 _LOG_ERROR_STATUS_DV(status_); \
315 } else { \
316 LOG_ERROR("CHECK-STATUS-fail: %r", status_); \
317 } \
318 test_status_set(kTestStatusFailed); \
319 } \
320 } while (false)
321
322/**
323 * Checks that the `status_t` represents a error value.
324 *
325 * Prints a human-readable error message if the status represents an non-error.
326 *
327 * @param expr An expression which evaluates to a `status_t`.
328 */
329#define CHECK_STATUS_NOT_OK(expr, ...) \
330 do { \
331 status_t status_ = expr; \
332 if (status_ok(status_)) { \
333 if (device_log_bypass_uart_address()) { \
334 _LOG_ERROR_STATUS_DV(status_); \
335 } else { \
336 LOG_ERROR("CHECK-STATUS-fail: %r", status_); \
337 } \
338 test_status_set(kTestStatusFailed); \
339 } \
340 } while (false)
341
342#endif // OPENTITAN_SW_DEVICE_LIB_TESTING_TEST_FRAMEWORK_CHECK_H_