1 /*
2 * Copyright 2018 The Hafnium Authors.
3 *
4 * Use of this source code is governed by a BSD-style
5 * license that can be found in the LICENSE file or at
6 * https://opensource.org/licenses/BSD-3-Clause.
7 */
8
9 #include "hf/arch/vm/power_mgmt.h"
10
11 #include "hf/boot_params.h"
12 #include "hf/fdt_handler.h"
13 #include "hf/memiter.h"
14 #include "hf/std.h"
15
16 #include "hftest_common.h"
17 #include "test/hftest.h"
18
19 #define HFTEST_CTRL_JSON_START "[hftest_ctrl:json_start]"
20 #define HFTEST_CTRL_JSON_END "[hftest_ctrl:json_end]"
21
22 static struct hftest_test hftest_constructed[HFTEST_MAX_TESTS];
23 static size_t hftest_count;
24 static struct hftest_test *hftest_list;
25
26 static struct hftest_context global_context;
27
alignas(PAGE_SIZE)28 static alignas(PAGE_SIZE) uint8_t secondary_ec_stack[MAX_CPUS][PAGE_SIZE];
29
30 struct hftest_context *hftest_get_context(void)
31 {
32 return &global_context;
33 }
34
35 /**
36 * Adds the given test information to the global list, to be used by
37 * `hftest_use_registered_list`.
38 */
hftest_register(struct hftest_test test)39 void hftest_register(struct hftest_test test)
40 {
41 if (hftest_count < HFTEST_MAX_TESTS) {
42 hftest_constructed[hftest_count++] = test;
43 } else {
44 HFTEST_FAIL(true, "Too many tests");
45 }
46 }
47
48 /**
49 * Uses the list of tests registered by `hftest_register(...)` as the ones to
50 * run.
51 */
hftest_use_registered_list(void)52 void hftest_use_registered_list(void)
53 {
54 hftest_list = hftest_constructed;
55 }
56
57 /**
58 * Uses the given list of tests as the ones to run.
59 */
hftest_use_list(struct hftest_test list[],size_t count)60 void hftest_use_list(struct hftest_test list[], size_t count)
61 {
62 hftest_list = list;
63 hftest_count = count;
64 }
65
66 /**
67 * Writes out a JSON structure describing the available tests.
68 */
hftest_json(void)69 void hftest_json(void)
70 {
71 const char *suite = NULL;
72 size_t i;
73 size_t tests_in_suite = 0;
74
75 /* Wrap the JSON in tags for the hftest script to use. */
76 HFTEST_LOG(HFTEST_CTRL_JSON_START);
77
78 HFTEST_LOG("{");
79 HFTEST_LOG(" \"suites\": [");
80 for (i = 0; i < hftest_count; ++i) {
81 struct hftest_test *test = &hftest_list[i];
82 if (test->suite != suite) {
83 /* Close out previously open suite. */
84 if (tests_in_suite) {
85 HFTEST_LOG(" ]");
86 HFTEST_LOG(" },");
87 }
88 /* Move onto new suite. */
89 suite = test->suite;
90 tests_in_suite = 0;
91 HFTEST_LOG(" {");
92 HFTEST_LOG(" \"name\": \"%s\",", test->suite);
93 }
94 if (test->kind == HFTEST_KIND_SET_UP) {
95 HFTEST_LOG(" \"setup\": true,");
96 }
97 if (test->kind == HFTEST_KIND_TEAR_DOWN) {
98 HFTEST_LOG(" \"teardown\": true,");
99 }
100 if (test->kind == HFTEST_KIND_TEST) {
101 /*
102 * If test has a precondition, run respective function.
103 * If it returns false, then the current setup is not
104 * meant to run the test. Hence, we must skip it.
105 */
106 bool skip_test = test->precondition != NULL &&
107 !test->precondition();
108
109 if (!tests_in_suite) {
110 HFTEST_LOG(" \"tests\": [");
111 }
112 /*
113 * It's easier to put the comma at the start of the line
114 * than the end even though the JSON looks a bit funky.
115 */
116 HFTEST_LOG(" %c{", tests_in_suite ? ',' : ' ');
117 HFTEST_LOG(" \"name\": \"%s\",", test->name);
118 HFTEST_LOG(" \"is_long_running\": %s,",
119 test->is_long_running ? "true" : "false");
120 HFTEST_LOG(" \"skip_test\": %s",
121 skip_test ? "true" : "false");
122 HFTEST_LOG(" }");
123 ++tests_in_suite;
124 }
125 }
126 if (tests_in_suite) {
127 HFTEST_LOG(" ]");
128 HFTEST_LOG(" }");
129 }
130 HFTEST_LOG(" ]");
131 HFTEST_LOG("}");
132
133 /* Wrap the JSON in tags for the hftest script to use. */
134 HFTEST_LOG(HFTEST_CTRL_JSON_END);
135 }
136
137 /**
138 * Logs a failure message and shut down.
139 */
abort(void)140 noreturn void abort(void)
141 {
142 HFTEST_LOG("FAIL");
143 arch_power_off();
144 }
145
run_test(hftest_test_fn set_up,hftest_test_fn test,hftest_test_fn tear_down,const struct fdt * fdt)146 static void run_test(hftest_test_fn set_up, hftest_test_fn test,
147 hftest_test_fn tear_down, const struct fdt *fdt)
148 {
149 /* Prepare the context. */
150 struct hftest_context *ctx = hftest_get_context();
151 memset_s(ctx, sizeof(*ctx), 0, sizeof(*ctx));
152 ctx->abort = abort;
153 ctx->fdt = fdt;
154
155 /* Run any set up functions. */
156 if (set_up) {
157 set_up();
158 if (ctx->failures) {
159 abort();
160 }
161 }
162
163 /* Run the test. */
164 test();
165 if (ctx->failures) {
166 abort();
167 }
168
169 /* Run any tear down functions. */
170 if (tear_down) {
171 tear_down();
172 if (ctx->failures) {
173 abort();
174 }
175 }
176
177 HFTEST_LOG("FINISHED");
178 }
179
180 /**
181 * Runs the given test case.
182 */
hftest_run(struct memiter suite_name,struct memiter test_name,const struct fdt * fdt)183 void hftest_run(struct memiter suite_name, struct memiter test_name,
184 const struct fdt *fdt)
185 {
186 size_t i;
187 hftest_test_fn suite_set_up = NULL;
188 hftest_test_fn suite_tear_down = NULL;
189
190 for (i = 0; i < hftest_count; ++i) {
191 struct hftest_test *test = &hftest_list[i];
192
193 /* Check if this test is part of the suite we want. */
194 if (memiter_iseq(&suite_name, test->suite)) {
195 switch (test->kind) {
196 /*
197 * The first entries in the suite are the set up and
198 * tear down functions.
199 */
200 case HFTEST_KIND_SET_UP:
201 suite_set_up = test->fn;
202 break;
203 case HFTEST_KIND_TEAR_DOWN:
204 suite_tear_down = test->fn;
205 break;
206 /* Find the test. */
207 case HFTEST_KIND_TEST:
208 if (memiter_iseq(&test_name, test->name)) {
209 run_test(suite_set_up, test->fn,
210 suite_tear_down, fdt);
211 return;
212 }
213 break;
214 default:
215 /* Ignore other kinds. */
216 break;
217 }
218 }
219 }
220
221 HFTEST_LOG("Unable to find requested tests.");
222 }
223
224 /**
225 * Writes out usage information.
226 */
hftest_help(void)227 void hftest_help(void)
228 {
229 HFTEST_LOG("usage:");
230 HFTEST_LOG("");
231 HFTEST_LOG(" help");
232 HFTEST_LOG("");
233 HFTEST_LOG(" Show this help.");
234 HFTEST_LOG("");
235 HFTEST_LOG(" json");
236 HFTEST_LOG("");
237 HFTEST_LOG(
238 " Print a directory of test suites and tests in "
239 "JSON "
240 "format.");
241 HFTEST_LOG("");
242 HFTEST_LOG(" run <suite> <test>");
243 HFTEST_LOG("");
244 HFTEST_LOG(" Run the named test from the named test suite.");
245 }
246
hftest_command(struct fdt * fdt)247 void hftest_command(struct fdt *fdt)
248 {
249 struct memiter command_line;
250 struct memiter command;
251
252 if (!hftest_ctrl_start(fdt, &command_line)) {
253 HFTEST_LOG("Unable to read the command line.");
254 return;
255 }
256
257 if (!memiter_parse_str(&command_line, &command)) {
258 HFTEST_LOG("Unable to parse command.");
259 return;
260 }
261
262 if (memiter_iseq(&command, "exit")) {
263 hftest_device_exit_test_environment();
264 return;
265 }
266
267 if (memiter_iseq(&command, "json")) {
268 hftest_json();
269 return;
270 }
271
272 if (memiter_iseq(&command, "run")) {
273 struct memiter suite_name;
274 struct memiter test_name;
275
276 if (!memiter_parse_str(&command_line, &suite_name)) {
277 HFTEST_LOG("Unable to parse test suite.");
278 return;
279 }
280
281 if (!memiter_parse_str(&command_line, &test_name)) {
282 HFTEST_LOG("Unable to parse test.");
283 return;
284 }
285 hftest_run(suite_name, test_name, fdt);
286 return;
287 }
288
289 hftest_help();
290 }
291
vcpu_index_to_id(size_t index)292 static uintptr_t vcpu_index_to_id(size_t index)
293 {
294 /* For now we use indices as IDs for vCPUs. */
295 return index;
296 }
297
hftest_get_secondary_ec_stack(size_t id)298 uint8_t *hftest_get_secondary_ec_stack(size_t id)
299 {
300 assert(id < MAX_CPUS);
301 return secondary_ec_stack[id];
302 }
303
304 /**
305 * Get the ID of the CPU with the given index.
306 */
hftest_get_cpu_id(size_t index)307 cpu_id_t hftest_get_cpu_id(size_t index)
308 {
309 struct boot_params params;
310 const struct fdt *fdt = hftest_get_context()->fdt;
311
312 if (fdt == NULL) {
313 /*
314 * We must be in a service VM, so apply the mapping that Hafnium
315 * uses for vCPU IDs.
316 */
317 return vcpu_index_to_id(index);
318 }
319
320 /*
321 * VM is primary VM. Convert vCPU ids to the linear cpu id as passed to
322 * the primary VM in the FDT structure.
323 */
324 index = MAX_CPUS - index;
325
326 /* Find physical CPU ID from FDT. */
327 fdt_find_cpus(fdt, params.cpu_ids, ¶ms.cpu_count);
328
329 return params.cpu_ids[index];
330 }
331