blob: 78df642fb8d905d78d698df687f1941abe9a0868 [file] [log] [blame]
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +02001/*
2 * Copyright (c) 2018, Arm Limited. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
Ambroise Vincent602b7f52019-02-11 14:13:43 +00007/*
8 * The include of stdarg.h is not in alphabetical order because it needs to be
9 * included before stdio.h. Fixing this would require further changes.
10 */
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +020011#include <arch_helpers.h>
Ambroise Vincent602b7f52019-02-11 14:13:43 +000012#include <stdarg.h>
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +020013#include <assert.h>
14#include <debug.h>
15#include <nvm.h>
16#include <platform.h>
17#include <spinlock.h>
Ambroise Vincent602b7f52019-02-11 14:13:43 +000018#include <stdio.h>
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +020019
20/*
21 * Temporary buffer to store 1 test output.
22 * This will eventually be saved into NVM at the end of the execution
23 * of this test.
24 */
25static char testcase_output[TESTCASE_OUTPUT_MAX_SIZE];
26/*
27 * A test output can be written in several pieces by calling
28 * tftf_testcase_printf() multiple times. testcase_output_idx keeps the position
29 * of the last character written in testcase_output buffer and allows to easily
30 * append a new string at next call to tftf_testcase_printf().
31 */
32static unsigned int testcase_output_idx;
33
34/* Lock to avoid concurrent accesses to the testcase output buffer */
35static spinlock_t testcase_output_lock;
36
37static tftf_state_t tftf_init_state = {
38 .build_message = "",
39 .test_to_run = {
40 .testsuite_idx = 0,
41 .testcase_idx = 0,
42 },
43 .test_progress = TEST_READY,
44 .testcase_buffer = { 0 },
45 .testcase_results = {
46 {
47 .result = TEST_RESULT_NA,
48 .duration = 0,
49 .output_offset = 0,
50 .output_size = 0,
51 }
52 },
53 .result_buffer_size = 0,
54 .result_buffer = NULL,
55};
56
57unsigned int new_test_session(void)
58{
59/* NEW_TEST_SESSION == 1 => we always want to start a new session */
60#if NEW_TEST_SESSION
61 INFO("Always starting a new test session (NEW_TEST_SESSION == 1)\n");
62 return 1;
63#endif
64 char saved_build_msg[BUILD_MESSAGE_SIZE];
65
66 /*
67 * Check the validity of the build message stored in NVM.
68 * It is invalid when it doesn't match with the TFTF binary currently
69 * executing.
70 */
71 tftf_nvm_read(TFTF_STATE_OFFSET(build_message), saved_build_msg,
72 BUILD_MESSAGE_SIZE);
73 return !!strncmp(build_message, saved_build_msg, BUILD_MESSAGE_SIZE);
74}
75
76STATUS tftf_init_nvm(void)
77{
78 INFO("Initialising NVM\n");
79
80 /* Copy the build message to identify the TFTF */
81 strncpy(tftf_init_state.build_message, build_message, BUILD_MESSAGE_SIZE);
82 return tftf_nvm_write(0, &tftf_init_state, sizeof(tftf_init_state));
83}
84
85STATUS tftf_clean_nvm(void)
86{
87 unsigned char corrupt_build_message = '\0';
88
89 /*
90 * This will cause TFTF to re-initialise its data structures next time
91 * it runs.
92 */
93 return tftf_nvm_write(TFTF_STATE_OFFSET(build_message),
94 &corrupt_build_message,
95 sizeof(corrupt_build_message));
96}
97
98STATUS tftf_set_test_to_run(const test_ref_t test_to_run)
99{
100 return tftf_nvm_write(TFTF_STATE_OFFSET(test_to_run), &test_to_run,
101 sizeof(test_to_run));
102}
103
104STATUS tftf_get_test_to_run(test_ref_t *test_to_run)
105{
106 assert(test_to_run != NULL);
107 return tftf_nvm_read(TFTF_STATE_OFFSET(test_to_run), test_to_run,
108 sizeof(*test_to_run));
109}
110
111STATUS tftf_set_test_progress(test_progress_t test_progress)
112{
113 return tftf_nvm_write(TFTF_STATE_OFFSET(test_progress), &test_progress,
114 sizeof(test_progress));
115}
116
117STATUS tftf_get_test_progress(test_progress_t *test_progress)
118{
119 assert(test_progress != NULL);
120 return tftf_nvm_read(TFTF_STATE_OFFSET(test_progress), test_progress,
121 sizeof(*test_progress));
122}
123
124STATUS tftf_testcase_set_result(const test_case_t *testcase,
125 test_result_t result,
126 unsigned long long duration)
127{
128 STATUS status;
129 unsigned result_buffer_size = 0;
130 TESTCASE_RESULT test_result;
131
132 assert(testcase != NULL);
133
134 /* Initialize Test case result */
135 test_result.result = result;
136 test_result.duration = duration;
137 test_result.output_offset = 0;
138 test_result.output_size = strlen(testcase_output);
139
140 /* Does the test have an output? */
141 if (test_result.output_size != 0) {
142 /* Get the size of the buffer containing all tests outputs */
143 status = tftf_nvm_read(TFTF_STATE_OFFSET(result_buffer_size),
144 &result_buffer_size, sizeof(unsigned));
145 if (status != STATUS_SUCCESS)
146 goto reset_test_output;
147
148 /*
149 * Write the output buffer at the end of the string buffer in
150 * NVM
151 */
152 test_result.output_offset = result_buffer_size;
153 status = tftf_nvm_write(
154 TFTF_STATE_OFFSET(result_buffer) + result_buffer_size,
155 testcase_output, test_result.output_size + 1);
156 if (status != STATUS_SUCCESS)
157 goto reset_test_output;
158
159 /* And update the buffer size into NVM */
160 result_buffer_size += test_result.output_size + 1;
161 status = tftf_nvm_write(TFTF_STATE_OFFSET(result_buffer_size),
162 &result_buffer_size, sizeof(unsigned));
163 if (status != STATUS_SUCCESS)
164 goto reset_test_output;
165 }
166
167 /* Write the test result into NVM */
168 status = tftf_nvm_write(TFTF_STATE_OFFSET(testcase_results) +
169 (testcase->index * sizeof(TESTCASE_RESULT)),
170 &test_result, sizeof(TESTCASE_RESULT));
171
172reset_test_output:
173 /* Reset test output buffer for the next test */
174 testcase_output_idx = 0;
175 testcase_output[0] = 0;
176
177 return status;
178}
179
180STATUS tftf_testcase_get_result(const test_case_t *testcase,
181 TESTCASE_RESULT *result,
182 char *test_output)
183{
184 STATUS status;
185 unsigned output_size;
186
187 assert(testcase != NULL);
188 assert(result != NULL);
189 assert(test_output != NULL);
190
191 status = tftf_nvm_read(TFTF_STATE_OFFSET(testcase_results)
192 + (testcase->index * sizeof(TESTCASE_RESULT)),
193 result, sizeof(TESTCASE_RESULT));
194 if (status != STATUS_SUCCESS) {
195 return status;
196 }
197
198 output_size = result->output_size;
199
200 if (output_size != 0) {
201 status = tftf_nvm_read(TFTF_STATE_OFFSET(result_buffer)
202 + result->output_offset,
203 test_output, output_size);
204 if (status != STATUS_SUCCESS)
205 return status;
206 }
207
208 test_output[output_size] = 0;
209
210 return STATUS_SUCCESS;
211}
212
213int tftf_testcase_printf(const char *format, ...)
214{
215 va_list ap;
216 int available;
217 int written = -1;
218
219 spin_lock(&testcase_output_lock);
220
221 assert(sizeof(testcase_output) >= testcase_output_idx);
222 available = sizeof(testcase_output) - testcase_output_idx;
223 if (available == 0) {
224 ERROR("%s: Output buffer is full ; the string won't be printed.\n",
225 __func__);
226 ERROR("%s: Consider increasing TESTCASE_OUTPUT_MAX_SIZE value.\n",
227 __func__);
228 goto release_lock;
229 }
230
231 va_start(ap, format);
232 written = vsnprintf(&testcase_output[testcase_output_idx], available,
233 format, ap);
234 va_end(ap);
235
236 if (written < 0) {
237 ERROR("%s: Output error (%d)", __func__, written);
238 goto release_lock;
239 }
240 /*
241 * If vsnprintf() truncated the string due to the size limit passed as
242 * an argument then its return value is the number of characters (not
243 * including the trailing '\0') which would have been written to the
244 * final string if enough space had been available. Thus, a return value
245 * of size or more means that the output was truncated.
246 *
247 * Adjust the value of 'written' to reflect what has been actually
248 * written.
249 */
250 if (written >= available) {
251 ERROR("%s: String has been truncated (%u/%u bytes written).\n",
252 __func__, available - 1, written);
253 ERROR("%s: Consider increasing TESTCASE_OUTPUT_MAX_SIZE value.\n",
254 __func__);
255 written = available - 1;
256 }
257
258 /*
259 * Update testcase_output_idx to point to the '\0' of the buffer.
260 * The next call of tftf_testcase_printf() will overwrite '\0' to
261 * append its new string to the buffer.
262 */
263 testcase_output_idx += written;
264
265release_lock:
266 spin_unlock(&testcase_output_lock);
267 return written;
268}
269
270void tftf_notify_reboot(void)
271{
272#if DEBUG
273 /* This function must be called by tests, not by the framework */
274 test_progress_t test_progress;
275 tftf_get_test_progress(&test_progress);
276 assert(test_progress == TEST_IN_PROGRESS);
277#endif /* DEBUG */
278
279 VERBOSE("Test intends to reset\n");
280 tftf_set_test_progress(TEST_REBOOTING);
281}