aboutsummaryrefslogtreecommitdiff
path: root/tftf/framework/nvm_results_helpers.c
blob: 78df642fb8d905d78d698df687f1941abe9a0868 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
/*
 * Copyright (c) 2018, Arm Limited. All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

/*
 * The include of stdarg.h is not in alphabetical order because it needs to be
 * included before stdio.h. Fixing this would require further changes.
 */
#include <arch_helpers.h>
#include <stdarg.h>
#include <assert.h>
#include <debug.h>
#include <nvm.h>
#include <platform.h>
#include <spinlock.h>
#include <stdio.h>

/*
 * Temporary buffer to store 1 test output.
 * This will eventually be saved into NVM at the end of the execution
 * of this test.
 */
static char testcase_output[TESTCASE_OUTPUT_MAX_SIZE];
/*
 * A test output can be written in several pieces by calling
 * tftf_testcase_printf() multiple times. testcase_output_idx keeps the position
 * of the last character written in testcase_output buffer and allows to easily
 * append a new string at next call to tftf_testcase_printf().
 */
static unsigned int testcase_output_idx;

/* Lock to avoid concurrent accesses to the testcase output buffer */
static spinlock_t testcase_output_lock;

static tftf_state_t tftf_init_state = {
	.build_message		= "",
	.test_to_run		= {
		.testsuite_idx	= 0,
		.testcase_idx	= 0,
	},
	.test_progress		= TEST_READY,
	.testcase_buffer	= { 0 },
	.testcase_results	= {
		{
			.result		= TEST_RESULT_NA,
			.duration	= 0,
			.output_offset	= 0,
			.output_size	= 0,
		}
	},
	.result_buffer_size	= 0,
	.result_buffer		= NULL,
};

unsigned int new_test_session(void)
{
/* NEW_TEST_SESSION == 1 => we always want to start a new session */
#if NEW_TEST_SESSION
	INFO("Always starting a new test session (NEW_TEST_SESSION == 1)\n");
	return 1;
#endif
	char saved_build_msg[BUILD_MESSAGE_SIZE];

	/*
	 * Check the validity of the build message stored in NVM.
	 * It is invalid when it doesn't match with the TFTF binary currently
	 * executing.
	 */
	tftf_nvm_read(TFTF_STATE_OFFSET(build_message), saved_build_msg,
		BUILD_MESSAGE_SIZE);
	return !!strncmp(build_message, saved_build_msg, BUILD_MESSAGE_SIZE);
}

STATUS tftf_init_nvm(void)
{
	INFO("Initialising NVM\n");

	/* Copy the build message to identify the TFTF */
	strncpy(tftf_init_state.build_message, build_message, BUILD_MESSAGE_SIZE);
	return tftf_nvm_write(0, &tftf_init_state, sizeof(tftf_init_state));
}

STATUS tftf_clean_nvm(void)
{
	unsigned char corrupt_build_message = '\0';

	/*
	 * This will cause TFTF to re-initialise its data structures next time
	 * it runs.
	 */
	return tftf_nvm_write(TFTF_STATE_OFFSET(build_message),
			&corrupt_build_message,
			sizeof(corrupt_build_message));
}

STATUS tftf_set_test_to_run(const test_ref_t test_to_run)
{
	return tftf_nvm_write(TFTF_STATE_OFFSET(test_to_run), &test_to_run,
			sizeof(test_to_run));
}

STATUS tftf_get_test_to_run(test_ref_t *test_to_run)
{
	assert(test_to_run != NULL);
	return tftf_nvm_read(TFTF_STATE_OFFSET(test_to_run), test_to_run,
			sizeof(*test_to_run));
}

STATUS tftf_set_test_progress(test_progress_t test_progress)
{
	return tftf_nvm_write(TFTF_STATE_OFFSET(test_progress), &test_progress,
			sizeof(test_progress));
}

STATUS tftf_get_test_progress(test_progress_t *test_progress)
{
	assert(test_progress != NULL);
	return tftf_nvm_read(TFTF_STATE_OFFSET(test_progress), test_progress,
			sizeof(*test_progress));
}

STATUS tftf_testcase_set_result(const test_case_t *testcase,
				test_result_t result,
				unsigned long long duration)
{
	STATUS status;
	unsigned result_buffer_size = 0;
	TESTCASE_RESULT test_result;

	assert(testcase != NULL);

	/* Initialize Test case result */
	test_result.result = result;
	test_result.duration = duration;
	test_result.output_offset = 0;
	test_result.output_size = strlen(testcase_output);

	/* Does the test have an output? */
	if (test_result.output_size != 0) {
		/* Get the size of the buffer containing all tests outputs */
		status = tftf_nvm_read(TFTF_STATE_OFFSET(result_buffer_size),
				&result_buffer_size, sizeof(unsigned));
		if (status != STATUS_SUCCESS)
			goto reset_test_output;

		/*
		 * Write the output buffer at the end of the string buffer in
		 * NVM
		 */
		test_result.output_offset = result_buffer_size;
		status = tftf_nvm_write(
			TFTF_STATE_OFFSET(result_buffer) + result_buffer_size,
			testcase_output, test_result.output_size + 1);
		if (status != STATUS_SUCCESS)
			goto reset_test_output;

		/* And update the buffer size into NVM */
		result_buffer_size += test_result.output_size + 1;
		status = tftf_nvm_write(TFTF_STATE_OFFSET(result_buffer_size),
					&result_buffer_size, sizeof(unsigned));
		if (status != STATUS_SUCCESS)
			goto reset_test_output;
	}

	/* Write the test result into NVM */
	status = tftf_nvm_write(TFTF_STATE_OFFSET(testcase_results) +
				(testcase->index * sizeof(TESTCASE_RESULT)),
				&test_result, sizeof(TESTCASE_RESULT));

reset_test_output:
	/* Reset test output buffer for the next test */
	testcase_output_idx = 0;
	testcase_output[0] = 0;

	return status;
}

STATUS tftf_testcase_get_result(const test_case_t *testcase,
				TESTCASE_RESULT *result,
				char *test_output)
{
	STATUS status;
	unsigned output_size;

	assert(testcase != NULL);
	assert(result != NULL);
	assert(test_output != NULL);

	status = tftf_nvm_read(TFTF_STATE_OFFSET(testcase_results)
			+ (testcase->index * sizeof(TESTCASE_RESULT)),
			result, sizeof(TESTCASE_RESULT));
	if (status != STATUS_SUCCESS) {
		return status;
	}

	output_size = result->output_size;

	if (output_size != 0) {
		status = tftf_nvm_read(TFTF_STATE_OFFSET(result_buffer)
				+ result->output_offset,
				test_output, output_size);
		if (status != STATUS_SUCCESS)
			return status;
	}

	test_output[output_size] = 0;

	return STATUS_SUCCESS;
}

int tftf_testcase_printf(const char *format, ...)
{
	va_list ap;
	int available;
	int written = -1;

	spin_lock(&testcase_output_lock);

	assert(sizeof(testcase_output) >= testcase_output_idx);
	available = sizeof(testcase_output) - testcase_output_idx;
	if (available == 0) {
		ERROR("%s: Output buffer is full ; the string won't be printed.\n",
			__func__);
		ERROR("%s: Consider increasing TESTCASE_OUTPUT_MAX_SIZE value.\n",
			__func__);
		goto release_lock;
	}

	va_start(ap, format);
	written = vsnprintf(&testcase_output[testcase_output_idx], available,
			format, ap);
	va_end(ap);

	if (written < 0) {
		ERROR("%s: Output error (%d)", __func__, written);
		goto release_lock;
	}
	/*
	 * If vsnprintf() truncated the string due to the size limit passed as
	 * an argument then its return value is the number of characters (not
	 * including the trailing '\0') which would have been written to the
	 * final string if enough space had been available. Thus, a return value
	 * of size or more means that the output was truncated.
	 *
	 * Adjust the value of 'written' to reflect what has been actually
	 * written.
	 */
	if (written >= available) {
		ERROR("%s: String has been truncated (%u/%u bytes written).\n",
			__func__, available - 1, written);
		ERROR("%s: Consider increasing TESTCASE_OUTPUT_MAX_SIZE value.\n",
			__func__);
		written = available - 1;
	}

	/*
	 * Update testcase_output_idx to point to the '\0' of the buffer.
	 * The next call of tftf_testcase_printf() will overwrite '\0' to
	 * append its new string to the buffer.
	 */
	testcase_output_idx += written;

release_lock:
	spin_unlock(&testcase_output_lock);
	return written;
}

void tftf_notify_reboot(void)
{
#if DEBUG
	/* This function must be called by tests, not by the framework */
	test_progress_t test_progress;
	tftf_get_test_progress(&test_progress);
	assert(test_progress == TEST_IN_PROGRESS);
#endif /* DEBUG */

	VERBOSE("Test intends to reset\n");
	tftf_set_test_progress(TEST_REBOOTING);
}