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_