]>
Commit | Line | Data |
---|---|---|
637be2e5 SG |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * UPL handoff testing | |
4 | * | |
5 | * Copyright 2024 Google LLC | |
6 | * Written by Simon Glass <[email protected]> | |
7 | */ | |
8 | ||
9 | #include <abuf.h> | |
10 | #include <mapmem.h> | |
11 | #include <upl.h> | |
12 | #include <dm/ofnode.h> | |
13 | #include <test/suites.h> | |
14 | #include <test/test.h> | |
15 | #include <test/ut.h> | |
16 | #include "bootstd_common.h" | |
17 | ||
18 | /* Declare a new upl test */ | |
19 | #define UPL_TEST(_name, _flags) UNIT_TEST(_name, _flags, upl_test) | |
20 | ||
21 | static int add_region(struct unit_test_state *uts, struct alist *lst, | |
22 | ulong base, ulong size) | |
23 | { | |
24 | struct memregion region; | |
25 | ||
26 | region.base = base; | |
27 | region.size = size; | |
28 | ut_assertnonnull(alist_add(lst, region)); | |
29 | ||
30 | return 0; | |
31 | } | |
32 | ||
33 | int upl_get_test_data(struct unit_test_state *uts, struct upl *upl) | |
34 | { | |
35 | struct upl_memmap memmap; | |
36 | struct upl_memres memres; | |
37 | struct upl_image img; | |
38 | struct upl_mem mem; | |
39 | ||
40 | upl_init(upl); | |
41 | ||
42 | upl->addr_cells = 1; | |
43 | upl->size_cells = 1; | |
44 | upl->smbios = 0x123; | |
45 | upl->acpi = 0x456; | |
46 | upl->bootmode = BIT(UPLBM_DEFAULT) | BIT(UPLBM_S3); | |
47 | upl->fit = 0x789; | |
48 | upl->conf_offset = 0x234; | |
49 | upl->addr_width = 46; | |
50 | upl->acpi_nvs_size = 0x100; | |
51 | ||
52 | /* image[0] */ | |
53 | img.load = 0x1; | |
54 | img.size = 0x2; | |
55 | img.offset = 0x3; | |
56 | img.description = "U-Boot"; | |
57 | ut_assertnonnull(alist_add(&upl->image, img)); | |
58 | ||
59 | /* image[1] */ | |
60 | img.load = 0x4; | |
61 | img.size = 0x5; | |
62 | img.offset = 0x6; | |
63 | img.description = "ATF"; | |
64 | ut_assertnonnull(alist_add(&upl->image, img)); | |
65 | ||
66 | /* mem[0] : 3 regions */ | |
67 | memset(&mem, '\0', sizeof(mem)); | |
68 | alist_init_struct(&mem.region, struct memregion); | |
69 | ut_assertok(add_region(uts, &mem.region, 0x10, 0x20)); | |
70 | ut_assertok(add_region(uts, &mem.region, 0x30, 0x40)); | |
71 | ut_assertok(add_region(uts, &mem.region, 0x40, 0x50)); | |
72 | ut_assertnonnull(alist_add(&upl->mem, mem)); | |
73 | ||
74 | /* mem[0] : 1 region */ | |
75 | alist_init_struct(&mem.region, struct memregion); | |
76 | ut_assertok(add_region(uts, &mem.region, 0x70, 0x80)); | |
77 | mem.hotpluggable = true; | |
78 | ut_assertnonnull(alist_add(&upl->mem, mem)); | |
79 | mem.hotpluggable = false; | |
80 | ||
81 | /* memmap[0] : 5 regions */ | |
82 | alist_init_struct(&memmap.region, struct memregion); | |
83 | memmap.name = "acpi"; | |
84 | memmap.usage = BIT(UPLUS_ACPI_RECLAIM); | |
85 | ut_assertok(add_region(uts, &memmap.region, 0x11, 0x12)); | |
86 | ut_assertok(add_region(uts, &memmap.region, 0x13, 0x14)); | |
87 | ut_assertok(add_region(uts, &memmap.region, 0x15, 0x16)); | |
88 | ut_assertok(add_region(uts, &memmap.region, 0x17, 0x18)); | |
89 | ut_assertok(add_region(uts, &memmap.region, 0x19, 0x1a)); | |
90 | ut_assertnonnull(alist_add(&upl->memmap, memmap)); | |
91 | ||
92 | /* memmap[1] : 1 region */ | |
93 | memmap.name = "u-boot"; | |
94 | memmap.usage = BIT(UPLUS_BOOT_DATA); | |
95 | alist_init_struct(&memmap.region, struct memregion); | |
96 | ut_assertok(add_region(uts, &memmap.region, 0x21, 0x22)); | |
97 | ut_assertnonnull(alist_add(&upl->memmap, memmap)); | |
98 | ||
99 | /* memmap[2] : 1 region */ | |
100 | alist_init_struct(&memmap.region, struct memregion); | |
101 | memmap.name = "efi"; | |
102 | memmap.usage = BIT(UPLUS_RUNTIME_CODE); | |
103 | ut_assertok(add_region(uts, &memmap.region, 0x23, 0x24)); | |
104 | ut_assertnonnull(alist_add(&upl->memmap, memmap)); | |
105 | ||
106 | /* memmap[3]: 2 regions */ | |
107 | alist_init_struct(&memmap.region, struct memregion); | |
108 | memmap.name = "empty"; | |
109 | memmap.usage = 0; | |
110 | ut_assertok(add_region(uts, &memmap.region, 0x25, 0x26)); | |
111 | ut_assertok(add_region(uts, &memmap.region, 0x27, 0x28)); | |
112 | ut_assertnonnull(alist_add(&upl->memmap, memmap)); | |
113 | ||
114 | /* memmap[4]: 1 region */ | |
115 | alist_init_struct(&memmap.region, struct memregion); | |
116 | memmap.name = "acpi-things"; | |
117 | memmap.usage = BIT(UPLUS_RUNTIME_CODE) | BIT(UPLUS_ACPI_NVS); | |
118 | ut_assertok(add_region(uts, &memmap.region, 0x29, 0x2a)); | |
119 | ut_assertnonnull(alist_add(&upl->memmap, memmap)); | |
120 | ||
121 | /* memres[0]: 1 region */ | |
122 | alist_init_struct(&memres.region, struct memregion); | |
123 | memset(&memres, '\0', sizeof(memres)); | |
124 | memres.name = "mmio"; | |
125 | ut_assertok(add_region(uts, &memres.region, 0x2b, 0x2c)); | |
126 | ut_assertnonnull(alist_add(&upl->memres, memres)); | |
127 | ||
128 | /* memres[1]: 2 regions */ | |
129 | alist_init_struct(&memres.region, struct memregion); | |
130 | memres.name = "memory"; | |
131 | ut_assertok(add_region(uts, &memres.region, 0x2d, 0x2e)); | |
132 | ut_assertok(add_region(uts, &memres.region, 0x2f, 0x30)); | |
133 | memres.no_map = true; | |
134 | ut_assertnonnull(alist_add(&upl->memres, memres)); | |
135 | ||
136 | upl->serial.compatible = "ns16550a"; | |
137 | upl->serial.clock_frequency = 1843200; | |
138 | upl->serial.current_speed = 115200; | |
139 | alist_init_struct(&upl->serial.reg, struct memregion); | |
140 | ut_assertok(add_region(uts, &upl->serial.reg, 0xf1de0000, 0x100)); | |
141 | upl->serial.reg_io_shift = 2; | |
142 | upl->serial.reg_offset = 0x40; | |
143 | upl->serial.reg_io_width = 1; | |
144 | upl->serial.virtual_reg = 0x20000000; | |
145 | upl->serial.access_type = UPLSAT_MMIO; | |
146 | ||
147 | alist_init_struct(&upl->graphics.reg, struct memregion); | |
148 | ut_assertok(add_region(uts, &upl->graphics.reg, 0xd0000000, 0x10000000)); | |
149 | upl->graphics.width = 1280; | |
150 | upl->graphics.height = 1280; | |
151 | upl->graphics.stride = upl->graphics.width * 4; | |
152 | upl->graphics.format = UPLGF_ARGB32; | |
153 | ||
154 | return 0; | |
155 | } | |
156 | ||
157 | static int compare_upl_image(struct unit_test_state *uts, | |
158 | const struct upl_image *base, | |
159 | const struct upl_image *cmp) | |
160 | { | |
161 | ut_asserteq(base->load, cmp->load); | |
162 | ut_asserteq(base->size, cmp->size); | |
163 | ut_asserteq(base->offset, cmp->offset); | |
164 | ut_asserteq_str(base->description, cmp->description); | |
165 | ||
166 | return 0; | |
167 | } | |
168 | ||
169 | static int compare_upl_memregion(struct unit_test_state *uts, | |
170 | const struct memregion *base, | |
171 | const struct memregion *cmp) | |
172 | { | |
173 | ut_asserteq(base->base, cmp->base); | |
174 | ut_asserteq(base->size, cmp->size); | |
175 | ||
176 | return 0; | |
177 | } | |
178 | ||
179 | static int compare_upl_mem(struct unit_test_state *uts, | |
180 | const struct upl_mem *base, | |
181 | const struct upl_mem *cmp) | |
182 | { | |
183 | int i; | |
184 | ||
185 | ut_asserteq(base->region.count, cmp->region.count); | |
186 | ut_asserteq(base->hotpluggable, cmp->hotpluggable); | |
187 | for (i = 0; i < base->region.count; i++) { | |
188 | ut_assertok(compare_upl_memregion(uts, | |
189 | alist_get(&base->region, i, struct memregion), | |
190 | alist_get(&cmp->region, i, struct memregion))); | |
191 | } | |
192 | ||
193 | return 0; | |
194 | } | |
195 | ||
196 | static int check_device_name(struct unit_test_state *uts, const char *base, | |
197 | const char *cmp) | |
198 | { | |
199 | const char *p; | |
200 | ||
201 | p = strchr(cmp, '@'); | |
202 | if (p) { | |
203 | ut_assertnonnull(p); | |
204 | ut_asserteq_strn(base, cmp); | |
205 | ut_asserteq(p - cmp, strlen(base)); | |
206 | } else { | |
207 | ut_asserteq_str(base, cmp); | |
208 | } | |
209 | ||
210 | return 0; | |
211 | } | |
212 | ||
213 | static int compare_upl_memmap(struct unit_test_state *uts, | |
214 | const struct upl_memmap *base, | |
215 | const struct upl_memmap *cmp) | |
216 | { | |
217 | int i; | |
218 | ||
219 | ut_assertok(check_device_name(uts, base->name, cmp->name)); | |
220 | ut_asserteq(base->region.count, cmp->region.count); | |
221 | ut_asserteq(base->usage, cmp->usage); | |
222 | for (i = 0; i < base->region.count; i++) | |
223 | ut_assertok(compare_upl_memregion(uts, | |
224 | alist_get(&base->region, i, struct memregion), | |
225 | alist_get(&cmp->region, i, struct memregion))); | |
226 | ||
227 | return 0; | |
228 | } | |
229 | ||
230 | static int compare_upl_memres(struct unit_test_state *uts, | |
231 | const struct upl_memres *base, | |
232 | const struct upl_memres *cmp) | |
233 | { | |
234 | int i; | |
235 | ||
236 | ut_assertok(check_device_name(uts, base->name, cmp->name)); | |
237 | ut_asserteq(base->region.count, cmp->region.count); | |
238 | ut_asserteq(base->no_map, cmp->no_map); | |
239 | for (i = 0; i < base->region.count; i++) | |
240 | ut_assertok(compare_upl_memregion(uts, | |
241 | alist_get(&base->region, i, struct memregion), | |
242 | alist_get(&cmp->region, i, struct memregion))); | |
243 | ||
244 | return 0; | |
245 | } | |
246 | ||
247 | static int compare_upl_serial(struct unit_test_state *uts, | |
248 | struct upl_serial *base, struct upl_serial *cmp) | |
249 | { | |
250 | int i; | |
251 | ||
252 | ut_asserteq_str(base->compatible, cmp->compatible); | |
253 | ut_asserteq(base->clock_frequency, cmp->clock_frequency); | |
254 | ut_asserteq(base->current_speed, cmp->current_speed); | |
255 | for (i = 0; i < base->reg.count; i++) | |
256 | ut_assertok(compare_upl_memregion(uts, | |
257 | alist_get(&base->reg, i, struct memregion), | |
258 | alist_get(&cmp->reg, i, struct memregion))); | |
259 | ut_asserteq(base->reg_io_shift, cmp->reg_io_shift); | |
260 | ut_asserteq(base->reg_offset, cmp->reg_offset); | |
261 | ut_asserteq(base->reg_io_width, cmp->reg_io_width); | |
262 | ut_asserteq(base->virtual_reg, cmp->virtual_reg); | |
263 | ut_asserteq(base->access_type, cmp->access_type); | |
264 | ||
265 | return 0; | |
266 | } | |
267 | ||
268 | static int compare_upl_graphics(struct unit_test_state *uts, | |
269 | struct upl_graphics *base, | |
270 | struct upl_graphics *cmp) | |
271 | { | |
272 | int i; | |
273 | ||
274 | for (i = 0; i < base->reg.count; i++) | |
275 | ut_assertok(compare_upl_memregion(uts, | |
276 | alist_get(&base->reg, i, struct memregion), | |
277 | alist_get(&cmp->reg, i, struct memregion))); | |
278 | ut_asserteq(base->width, cmp->width); | |
279 | ut_asserteq(base->height, cmp->height); | |
280 | ut_asserteq(base->stride, cmp->stride); | |
281 | ut_asserteq(base->format, cmp->format); | |
282 | ||
283 | return 0; | |
284 | } | |
285 | ||
286 | static int compare_upl(struct unit_test_state *uts, struct upl *base, | |
287 | struct upl *cmp) | |
288 | { | |
289 | int i; | |
290 | ||
291 | ut_asserteq(base->addr_cells, cmp->addr_cells); | |
292 | ut_asserteq(base->size_cells, cmp->size_cells); | |
293 | ||
294 | ut_asserteq(base->smbios, cmp->smbios); | |
295 | ut_asserteq(base->acpi, cmp->acpi); | |
296 | ut_asserteq(base->bootmode, cmp->bootmode); | |
297 | ut_asserteq(base->fit, cmp->fit); | |
298 | ut_asserteq(base->conf_offset, cmp->conf_offset); | |
299 | ut_asserteq(base->addr_width, cmp->addr_width); | |
300 | ut_asserteq(base->acpi_nvs_size, cmp->acpi_nvs_size); | |
301 | ||
302 | ut_asserteq(base->image.count, cmp->image.count); | |
303 | for (i = 0; i < base->image.count; i++) | |
304 | ut_assertok(compare_upl_image(uts, | |
305 | alist_get(&base->image, i, struct upl_image), | |
306 | alist_get(&cmp->image, i, struct upl_image))); | |
307 | ||
308 | ut_asserteq(base->mem.count, cmp->mem.count); | |
309 | for (i = 0; i < base->mem.count; i++) | |
310 | ut_assertok(compare_upl_mem(uts, | |
311 | alist_get(&base->mem, i, struct upl_mem), | |
312 | alist_get(&cmp->mem, i, struct upl_mem))); | |
313 | ||
314 | ut_asserteq(base->memmap.count, cmp->memmap.count); | |
315 | for (i = 0; i < base->memmap.count; i++) | |
316 | ut_assertok(compare_upl_memmap(uts, | |
317 | alist_get(&base->memmap, i, struct upl_memmap), | |
318 | alist_get(&cmp->memmap, i, struct upl_memmap))); | |
319 | ||
320 | ut_asserteq(base->memres.count, cmp->memres.count); | |
321 | for (i = 0; i < base->memres.count; i++) | |
322 | ut_assertok(compare_upl_memres(uts, | |
323 | alist_get(&base->memres, i, struct upl_memres), | |
324 | alist_get(&cmp->memres, i, struct upl_memres))); | |
325 | ||
326 | ut_assertok(compare_upl_serial(uts, &base->serial, &cmp->serial)); | |
327 | ut_assertok(compare_upl_graphics(uts, &base->graphics, &cmp->graphics)); | |
328 | ||
329 | return 0; | |
330 | } | |
331 | ||
332 | /* Basic test of writing and reading UPL handoff */ | |
333 | static int upl_test_base(struct unit_test_state *uts) | |
334 | { | |
335 | oftree tree, check_tree; | |
336 | struct upl upl, check; | |
337 | struct abuf buf; | |
338 | ||
339 | if (!CONFIG_IS_ENABLED(OFNODE_MULTI_TREE)) | |
340 | return -EAGAIN; /* skip test */ | |
341 | ut_assertok(upl_get_test_data(uts, &upl)); | |
342 | ||
343 | ut_assertok(upl_create_handoff_tree(&upl, &tree)); | |
344 | ut_assertok(oftree_to_fdt(tree, &buf)); | |
345 | ||
346 | /* | |
347 | * strings in check_tree and therefore check are only valid so long as | |
348 | * buf stays around. As soon as we call abuf_uninit they go away | |
349 | */ | |
350 | check_tree = oftree_from_fdt(abuf_data(&buf)); | |
351 | ut_assert(ofnode_valid(oftree_path(check_tree, "/"))); | |
352 | ||
353 | ut_assertok(upl_read_handoff(&check, check_tree)); | |
354 | ut_assertok(compare_upl(uts, &upl, &check)); | |
355 | abuf_uninit(&buf); | |
356 | ||
357 | return 0; | |
358 | } | |
359 | UPL_TEST(upl_test_base, 0); | |
360 | ||
264f4b0b SG |
361 | /* Test 'upl info' command */ |
362 | static int upl_test_info(struct unit_test_state *uts) | |
363 | { | |
364 | gd_set_upl(NULL); | |
365 | ut_assertok(run_command("upl info", 0)); | |
366 | ut_assert_nextline("UPL state: inactive"); | |
367 | ut_assert_console_end(); | |
368 | ||
369 | gd_set_upl((struct upl *)uts); /* set it to any non-zero value */ | |
370 | ut_assertok(run_command("upl info", 0)); | |
371 | ut_assert_nextline("UPL state: active"); | |
372 | ut_assert_console_end(); | |
373 | gd_set_upl(NULL); | |
374 | ||
375 | return 0; | |
376 | } | |
725c438c | 377 | UPL_TEST(upl_test_info, UTF_CONSOLE_REC); |
264f4b0b SG |
378 | |
379 | /* Test 'upl read' and 'upl_write' commands */ | |
380 | static int upl_test_read_write(struct unit_test_state *uts) | |
381 | { | |
382 | ulong addr; | |
383 | ||
384 | if (!CONFIG_IS_ENABLED(OFNODE_MULTI_TREE)) | |
385 | return -EAGAIN; /* skip test */ | |
386 | ut_assertok(run_command("upl write", 0)); | |
387 | ||
388 | addr = env_get_hex("upladdr", 0); | |
389 | ut_assert_nextline("UPL handoff written to %lx size %lx", addr, | |
390 | env_get_hex("uplsize", 0)); | |
391 | ut_assert_console_end(); | |
392 | ||
393 | ut_assertok(run_command("upl read ${upladdr}", 0)); | |
394 | ut_assert_nextline("Reading UPL at %lx", addr); | |
395 | ut_assert_console_end(); | |
396 | ||
397 | return 0; | |
398 | } | |
725c438c | 399 | UPL_TEST(upl_test_read_write, UTF_CONSOLE_REC); |
264f4b0b | 400 | |
34034227 SG |
401 | /* Test UPL passthrough */ |
402 | static int upl_test_info_norun(struct unit_test_state *uts) | |
403 | { | |
404 | const struct upl_image *img; | |
405 | struct upl *upl = gd_upl(); | |
406 | const void *fit; | |
407 | ||
408 | ut_assertok(run_command("upl info -v", 0)); | |
409 | ut_assert_nextline("UPL state: active"); | |
410 | ut_assert_nextline("fit %lx", upl->fit); | |
411 | ut_assert_nextline("conf_offset %x", upl->conf_offset); | |
412 | ut_assert_nextlinen("image 0"); | |
413 | ut_assert_nextlinen("image 1"); | |
414 | ut_assert_console_end(); | |
415 | ||
416 | /* check the offsets */ | |
417 | fit = map_sysmem(upl->fit, 0); | |
418 | ut_asserteq_str("conf-1", fdt_get_name(fit, upl->conf_offset, NULL)); | |
419 | ||
420 | ut_asserteq(2, upl->image.count); | |
421 | ||
422 | img = alist_get(&upl->image, 1, struct upl_image); | |
423 | ut_asserteq_str("firmware-1", fdt_get_name(fit, img->offset, NULL)); | |
424 | ut_asserteq(CONFIG_TEXT_BASE, img->load); | |
425 | ||
426 | return 0; | |
427 | } | |
725c438c | 428 | UPL_TEST(upl_test_info_norun, UTF_CONSOLE_REC | UTF_MANUAL); |
34034227 | 429 | |
637be2e5 SG |
430 | int do_ut_upl(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) |
431 | { | |
432 | struct unit_test *tests = UNIT_TEST_SUITE_START(upl_test); | |
433 | const int n_ents = UNIT_TEST_SUITE_COUNT(upl_test); | |
434 | ||
435 | return cmd_ut_category("cmd_upl", "cmd_upl_", tests, n_ents, argc, | |
436 | argv); | |
437 | } |