07b0517718ec12376dc889078eb78a606c033198
[platform/kernel/u-boot.git] / test / boot / bootflow.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Test for bootdev functions. All start with 'bootdev'
4  *
5  * Copyright 2021 Google LLC
6  * Written by Simon Glass <sjg@chromium.org>
7  */
8
9 #include <common.h>
10 #include <bootdev.h>
11 #include <bootflow.h>
12 #include <bootstd.h>
13 #include <dm.h>
14 #include <asm/test.h>
15 #include <dm/device-internal.h>
16 #include <dm/lists.h>
17 #include <test/suites.h>
18 #include <test/ut.h>
19 #include "bootstd_common.h"
20
21 static int inject_response(struct unit_test_state *uts)
22 {
23         /*
24          * The image being booted presents a menu of options:
25          *
26          * Fedora-Workstation-armhfp-31-1.9 Boot Options.
27          * 1:   Fedora-Workstation-armhfp-31-1.9 (5.3.7-301.fc31.armv7hl)
28          * Enter choice:
29          *
30          * Provide input for this, to avoid waiting two seconds for a timeout.
31          */
32         ut_asserteq(2, console_in_puts("1\n"));
33
34         return 0;
35 }
36
37 /* Check 'bootflow scan/list' commands */
38 static int bootflow_cmd(struct unit_test_state *uts)
39 {
40         console_record_reset_enable();
41         ut_assertok(run_command("bootdev select 1", 0));
42         ut_assert_console_end();
43         ut_assertok(run_command("bootflow scan -l", 0));
44         ut_assert_nextline("Scanning for bootflows in bootdev 'mmc1.bootdev'");
45         ut_assert_nextline("Seq  Method       State   Uclass    Part  Name                      Filename");
46         ut_assert_nextlinen("---");
47         ut_assert_nextline("  0  syslinux     ready   mmc          1  mmc1.bootdev.part_1       /extlinux/extlinux.conf");
48         ut_assert_nextlinen("---");
49         ut_assert_nextline("(1 bootflow, 1 valid)");
50         ut_assert_console_end();
51
52         ut_assertok(run_command("bootflow list", 0));
53         ut_assert_nextline("Showing bootflows for bootdev 'mmc1.bootdev'");
54         ut_assert_nextline("Seq  Method       State   Uclass    Part  Name                      Filename");
55         ut_assert_nextlinen("---");
56         ut_assert_nextline("  0  syslinux     ready   mmc          1  mmc1.bootdev.part_1       /extlinux/extlinux.conf");
57         ut_assert_nextlinen("---");
58         ut_assert_nextline("(1 bootflow, 1 valid)");
59         ut_assert_console_end();
60
61         return 0;
62 }
63 BOOTSTD_TEST(bootflow_cmd, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
64
65 /* Check 'bootflow scan' with a name / label / seq */
66 static int bootflow_cmd_label(struct unit_test_state *uts)
67 {
68         console_record_reset_enable();
69         ut_assertok(run_command("bootflow scan -l mmc1", 0));
70         ut_assert_nextline("Scanning for bootflows in bootdev 'mmc1.bootdev'");
71         ut_assert_skip_to_line("(1 bootflow, 1 valid)");
72         ut_assert_console_end();
73
74         ut_assertok(run_command("bootflow scan -l mmc0.bootdev", 0));
75         ut_assert_nextline("Scanning for bootflows in bootdev 'mmc0.bootdev'");
76         ut_assert_skip_to_line("(0 bootflows, 0 valid)");
77         ut_assert_console_end();
78
79         ut_assertok(run_command("bootflow scan -l 0", 0));
80         ut_assert_nextline("Scanning for bootflows in bootdev 'mmc2.bootdev'");
81         ut_assert_skip_to_line("(0 bootflows, 0 valid)");
82         ut_assert_console_end();
83
84         return 0;
85 }
86 BOOTSTD_TEST(bootflow_cmd_label, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
87
88 /* Check 'bootflow scan/list' commands using all bootdevs */
89 static int bootflow_cmd_glob(struct unit_test_state *uts)
90 {
91         ut_assertok(bootstd_test_drop_bootdev_order(uts));
92
93         console_record_reset_enable();
94         ut_assertok(run_command("bootflow scan -l", 0));
95         ut_assert_nextline("Scanning for bootflows in all bootdevs");
96         ut_assert_nextline("Seq  Method       State   Uclass    Part  Name                      Filename");
97         ut_assert_nextlinen("---");
98         ut_assert_nextline("Scanning bootdev 'mmc2.bootdev':");
99         ut_assert_nextline("Scanning bootdev 'mmc1.bootdev':");
100         ut_assert_nextline("  0  syslinux     ready   mmc          1  mmc1.bootdev.part_1       /extlinux/extlinux.conf");
101         ut_assert_nextline("Scanning bootdev 'mmc0.bootdev':");
102         ut_assert_nextline("No more bootdevs");
103         ut_assert_nextlinen("---");
104         ut_assert_nextline("(1 bootflow, 1 valid)");
105         ut_assert_console_end();
106
107         ut_assertok(run_command("bootflow list", 0));
108         ut_assert_nextline("Showing all bootflows");
109         ut_assert_nextline("Seq  Method       State   Uclass    Part  Name                      Filename");
110         ut_assert_nextlinen("---");
111         ut_assert_nextline("  0  syslinux     ready   mmc          1  mmc1.bootdev.part_1       /extlinux/extlinux.conf");
112         ut_assert_nextlinen("---");
113         ut_assert_nextline("(1 bootflow, 1 valid)");
114         ut_assert_console_end();
115
116         return 0;
117 }
118 BOOTSTD_TEST(bootflow_cmd_glob, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
119
120 /* Check 'bootflow scan -e' */
121 static int bootflow_cmd_scan_e(struct unit_test_state *uts)
122 {
123         ut_assertok(bootstd_test_drop_bootdev_order(uts));
124
125         console_record_reset_enable();
126         ut_assertok(run_command("bootflow scan -ale", 0));
127         ut_assert_nextline("Scanning for bootflows in all bootdevs");
128         ut_assert_nextline("Seq  Method       State   Uclass    Part  Name                      Filename");
129         ut_assert_nextlinen("---");
130         ut_assert_nextline("Scanning bootdev 'mmc2.bootdev':");
131         ut_assert_nextline("  0  syslinux     media   mmc          0  mmc2.bootdev.whole        <NULL>");
132         ut_assert_nextline("     ** No partition found, err=-93");
133         ut_assert_nextline("  1  efi          media   mmc          0  mmc2.bootdev.whole        <NULL>");
134         ut_assert_nextline("     ** No partition found, err=-93");
135
136         ut_assert_nextline("Scanning bootdev 'mmc1.bootdev':");
137         ut_assert_nextline("  2  syslinux     media   mmc          0  mmc1.bootdev.whole        <NULL>");
138         ut_assert_nextline("     ** No partition found, err=-2");
139         ut_assert_nextline("  3  efi          media   mmc          0  mmc1.bootdev.whole        <NULL>");
140         ut_assert_nextline("     ** No partition found, err=-2");
141         ut_assert_nextline("  4  syslinux     ready   mmc          1  mmc1.bootdev.part_1       /extlinux/extlinux.conf");
142         ut_assert_nextline("  5  efi          fs      mmc          1  mmc1.bootdev.part_1       efi/boot/bootsbox.efi");
143
144         ut_assert_skip_to_line("Scanning bootdev 'mmc0.bootdev':");
145         ut_assert_skip_to_line(" 3f  efi          media   mmc          0  mmc0.bootdev.whole        <NULL>");
146         ut_assert_nextline("     ** No partition found, err=-93");
147         ut_assert_nextline("No more bootdevs");
148         ut_assert_nextlinen("---");
149         ut_assert_nextline("(64 bootflows, 1 valid)");
150         ut_assert_console_end();
151
152         ut_assertok(run_command("bootflow list", 0));
153         ut_assert_nextline("Showing all bootflows");
154         ut_assert_nextline("Seq  Method       State   Uclass    Part  Name                      Filename");
155         ut_assert_nextlinen("---");
156         ut_assert_nextline("  0  syslinux     media   mmc          0  mmc2.bootdev.whole        <NULL>");
157         ut_assert_nextline("  1  efi          media   mmc          0  mmc2.bootdev.whole        <NULL>");
158         ut_assert_skip_to_line("  4  syslinux     ready   mmc          1  mmc1.bootdev.part_1       /extlinux/extlinux.conf");
159         ut_assert_skip_to_line(" 3f  efi          media   mmc          0  mmc0.bootdev.whole        <NULL>");
160         ut_assert_nextlinen("---");
161         ut_assert_nextline("(64 bootflows, 1 valid)");
162         ut_assert_console_end();
163
164         return 0;
165 }
166 BOOTSTD_TEST(bootflow_cmd_scan_e, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
167
168 /* Check 'bootflow info' */
169 static int bootflow_cmd_info(struct unit_test_state *uts)
170 {
171         console_record_reset_enable();
172         ut_assertok(run_command("bootdev select 1", 0));
173         ut_assert_console_end();
174         ut_assertok(run_command("bootflow scan", 0));
175         ut_assert_console_end();
176         ut_assertok(run_command("bootflow select 0", 0));
177         ut_assert_console_end();
178         ut_assertok(run_command("bootflow info", 0));
179         ut_assert_nextline("Name:      mmc1.bootdev.part_1");
180         ut_assert_nextline("Device:    mmc1.bootdev");
181         ut_assert_nextline("Block dev: mmc1.blk");
182         ut_assert_nextline("Method:    syslinux");
183         ut_assert_nextline("State:     ready");
184         ut_assert_nextline("Partition: 1");
185         ut_assert_nextline("Subdir:    (none)");
186         ut_assert_nextline("Filename:  /extlinux/extlinux.conf");
187         ut_assert_nextlinen("Buffer:    ");
188         ut_assert_nextline("Size:      253 (595 bytes)");
189         ut_assert_nextline("Error:     0");
190         ut_assert_console_end();
191
192         ut_assertok(run_command("bootflow info -d", 0));
193         ut_assert_nextline("Name:      mmc1.bootdev.part_1");
194         ut_assert_skip_to_line("Error:     0");
195         ut_assert_nextline("Contents:");
196         ut_assert_nextline("%s", "");
197         ut_assert_nextline("# extlinux.conf generated by appliance-creator");
198         ut_assert_skip_to_line("        initrd /initramfs-5.3.7-301.fc31.armv7hl.img");
199         ut_assert_console_end();
200
201         return 0;
202 }
203 BOOTSTD_TEST(bootflow_cmd_info, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
204
205 /* Check 'bootflow scan -b' to boot the first available bootdev */
206 static int bootflow_scan_boot(struct unit_test_state *uts)
207 {
208         console_record_reset_enable();
209         ut_assertok(inject_response(uts));
210         ut_assertok(run_command("bootflow scan -b", 0));
211         ut_assert_nextline(
212                 "** Booting bootflow 'mmc1.bootdev.part_1' with syslinux");
213         ut_assert_nextline("Ignoring unknown command: ui");
214
215         /*
216          * We expect it to get through to boot although sandbox always returns
217          * -EFAULT as it cannot actually boot the kernel
218          */
219         ut_assert_skip_to_line("sandbox: continuing, as we cannot run Linux");
220         ut_assert_nextline("Boot failed (err=-14)");
221         ut_assert_console_end();
222
223         return 0;
224 }
225 BOOTSTD_TEST(bootflow_scan_boot, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
226
227 /* Check iterating through available bootflows */
228 static int bootflow_iter(struct unit_test_state *uts)
229 {
230         struct bootflow_iter iter;
231         struct bootflow bflow;
232
233         bootstd_clear_glob();
234
235         /* The first device is mmc2.bootdev which has no media */
236         ut_asserteq(-EPROTONOSUPPORT,
237                     bootflow_scan_first(&iter, BOOTFLOWF_ALL, &bflow));
238         ut_asserteq(2, iter.num_methods);
239         ut_asserteq(0, iter.cur_method);
240         ut_asserteq(0, iter.part);
241         ut_asserteq(0, iter.max_part);
242         ut_asserteq_str("syslinux", iter.method->name);
243         ut_asserteq(0, bflow.err);
244
245         /*
246          * This shows MEDIA even though there is none, since int
247          * bootdev_find_in_blk() we call part_get_info() which returns
248          * -EPROTONOSUPPORT. Ideally it would return -EEOPNOTSUPP and we would
249          * know.
250          */
251         ut_asserteq(BOOTFLOWST_MEDIA, bflow.state);
252
253         ut_asserteq(-EPROTONOSUPPORT, bootflow_scan_next(&iter, &bflow));
254         ut_asserteq(2, iter.num_methods);
255         ut_asserteq(1, iter.cur_method);
256         ut_asserteq(0, iter.part);
257         ut_asserteq(0, iter.max_part);
258         ut_asserteq_str("efi", iter.method->name);
259         ut_asserteq(0, bflow.err);
260         ut_asserteq(BOOTFLOWST_MEDIA, bflow.state);
261         bootflow_free(&bflow);
262
263         /* The next device is mmc1.bootdev - at first we use the whole device */
264         ut_asserteq(-ENOENT, bootflow_scan_next(&iter, &bflow));
265         ut_asserteq(2, iter.num_methods);
266         ut_asserteq(0, iter.cur_method);
267         ut_asserteq(0, iter.part);
268         ut_asserteq(0x1e, iter.max_part);
269         ut_asserteq_str("syslinux", iter.method->name);
270         ut_asserteq(0, bflow.err);
271         ut_asserteq(BOOTFLOWST_MEDIA, bflow.state);
272         bootflow_free(&bflow);
273
274         ut_asserteq(-ENOENT, bootflow_scan_next(&iter, &bflow));
275         ut_asserteq(2, iter.num_methods);
276         ut_asserteq(1, iter.cur_method);
277         ut_asserteq(0, iter.part);
278         ut_asserteq(0x1e, iter.max_part);
279         ut_asserteq_str("efi", iter.method->name);
280         ut_asserteq(0, bflow.err);
281         ut_asserteq(BOOTFLOWST_MEDIA, bflow.state);
282         bootflow_free(&bflow);
283
284         /* Then more to partition 1 where we find something */
285         ut_assertok(bootflow_scan_next(&iter, &bflow));
286         ut_asserteq(2, iter.num_methods);
287         ut_asserteq(0, iter.cur_method);
288         ut_asserteq(1, iter.part);
289         ut_asserteq(0x1e, iter.max_part);
290         ut_asserteq_str("syslinux", iter.method->name);
291         ut_asserteq(0, bflow.err);
292         ut_asserteq(BOOTFLOWST_READY, bflow.state);
293         bootflow_free(&bflow);
294
295         ut_asserteq(-ENOENT, bootflow_scan_next(&iter, &bflow));
296         ut_asserteq(2, iter.num_methods);
297         ut_asserteq(1, iter.cur_method);
298         ut_asserteq(1, iter.part);
299         ut_asserteq(0x1e, iter.max_part);
300         ut_asserteq_str("efi", iter.method->name);
301         ut_asserteq(0, bflow.err);
302         ut_asserteq(BOOTFLOWST_FS, bflow.state);
303         bootflow_free(&bflow);
304
305         /* Then more to partition 2 which doesn't exist */
306         ut_asserteq(-ENOENT, bootflow_scan_next(&iter, &bflow));
307         ut_asserteq(2, iter.num_methods);
308         ut_asserteq(0, iter.cur_method);
309         ut_asserteq(2, iter.part);
310         ut_asserteq(0x1e, iter.max_part);
311         ut_asserteq_str("syslinux", iter.method->name);
312         ut_asserteq(0, bflow.err);
313         ut_asserteq(BOOTFLOWST_MEDIA, bflow.state);
314         bootflow_free(&bflow);
315
316         bootflow_iter_uninit(&iter);
317
318         ut_assert_console_end();
319
320         return 0;
321 }
322 BOOTSTD_TEST(bootflow_iter, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
323
324 /* Check using the system bootdev */
325 static int bootflow_system(struct unit_test_state *uts)
326 {
327         struct udevice *bootstd, *dev;
328
329         /* Add the EFI bootmgr driver */
330         ut_assertok(uclass_first_device_err(UCLASS_BOOTSTD, &bootstd));
331         ut_assertok(device_bind_driver(bootstd, "bootmeth_efi_mgr", "bootmgr",
332                                        &dev));
333         ut_assertok(device_probe(dev));
334         sandbox_set_fake_efi_mgr_dev(dev, true);
335
336         /* Add the system bootdev that it uses */
337         ut_assertok(device_bind_driver(bootstd, "system_bootdev",
338                                        "system-bootdev", &dev));
339
340         ut_assertok(bootstd_test_drop_bootdev_order(uts));
341
342         /* We should get a single 'bootmgr' method right at the end */
343         bootstd_clear_glob();
344         console_record_reset_enable();
345         ut_assertok(run_command("bootflow scan -l", 0));
346         ut_assert_skip_to_line(
347                 "  0  bootmgr      ready   (none)       0  <NULL>                    <NULL>");
348         ut_assert_skip_to_line("No more bootdevs");
349         ut_assert_skip_to_line("(2 bootflows, 2 valid)");
350         ut_assert_console_end();
351
352         return 0;
353 }
354 BOOTSTD_TEST(bootflow_system, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
355
356 /* Check disabling a bootmethod if it requests it */
357 static int bootflow_iter_disable(struct unit_test_state *uts)
358 {
359         struct udevice *bootstd, *dev;
360         struct bootflow_iter iter;
361         struct bootflow bflow;
362         int i;
363
364         /* Add the EFI bootmgr driver */
365         ut_assertok(uclass_first_device_err(UCLASS_BOOTSTD, &bootstd));
366         ut_assertok(device_bind_driver(bootstd, "bootmeth_sandbox", "sandbox",
367                                        &dev));
368
369         /* Add the system bootdev that it uses */
370         ut_assertok(device_bind_driver(bootstd, "system_bootdev",
371                                        "system-bootdev", &dev));
372
373         ut_assertok(bootstd_test_drop_bootdev_order(uts));
374
375         bootstd_clear_glob();
376         console_record_reset_enable();
377         ut_assertok(inject_response(uts));
378         ut_assertok(run_command("bootflow scan -lb", 0));
379
380         /* Try to boot the bootmgr flow, which will fail */
381         console_record_reset_enable();
382         ut_assertok(bootflow_scan_first(&iter, 0, &bflow));
383         ut_asserteq(3, iter.num_methods);
384         ut_asserteq_str("sandbox", iter.method->name);
385         ut_assertok(inject_response(uts));
386         ut_asserteq(-ENOTSUPP, bootflow_run_boot(&iter, &bflow));
387
388         ut_assert_skip_to_line("Boot method 'sandbox' failed and will not be retried");
389         ut_assert_console_end();
390
391         /* Check that the sandbox bootmeth has been removed */
392         ut_asserteq(2, iter.num_methods);
393         for (i = 0; i < iter.num_methods; i++)
394                 ut_assert(strcmp("sandbox", iter.method_order[i]->name));
395
396         return 0;
397 }
398 BOOTSTD_TEST(bootflow_iter_disable, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
399
400 /* Check 'bootflow boot' to boot a selected bootflow */
401 static int bootflow_cmd_boot(struct unit_test_state *uts)
402 {
403         console_record_reset_enable();
404         ut_assertok(run_command("bootdev select 1", 0));
405         ut_assert_console_end();
406         ut_assertok(run_command("bootflow scan", 0));
407         ut_assert_console_end();
408         ut_assertok(run_command("bootflow select 0", 0));
409         ut_assert_console_end();
410
411         ut_assertok(inject_response(uts));
412         ut_asserteq(1, run_command("bootflow boot", 0));
413         ut_assert_nextline(
414                 "** Booting bootflow 'mmc1.bootdev.part_1' with syslinux");
415         ut_assert_nextline("Ignoring unknown command: ui");
416
417         /*
418          * We expect it to get through to boot although sandbox always returns
419          * -EFAULT as it cannot actually boot the kernel
420          */
421         ut_assert_skip_to_line("sandbox: continuing, as we cannot run Linux");
422         ut_assert_nextline("Boot failed (err=-14)");
423         ut_assert_console_end();
424
425         return 0;
426 }
427 BOOTSTD_TEST(bootflow_cmd_boot, UT_TESTF_DM | UT_TESTF_SCAN_FDT);