]> Git Repo - J-u-boot.git/blob - test/boot/bootflow.c
bootstd: Add a test for the bootstd menu
[J-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 <[email protected]>
7  */
8
9 #include <common.h>
10 #include <bootdev.h>
11 #include <bootflow.h>
12 #include <bootmeth.h>
13 #include <bootstd.h>
14 #include <cli.h>
15 #include <dm.h>
16 #ifdef CONFIG_SANDBOX
17 #include <asm/test.h>
18 #endif
19 #include <dm/device-internal.h>
20 #include <dm/lists.h>
21 #include <test/suites.h>
22 #include <test/ut.h>
23 #include "bootstd_common.h"
24
25 DECLARE_GLOBAL_DATA_PTR;
26
27 extern U_BOOT_DRIVER(bootmeth_script);
28
29 static int inject_response(struct unit_test_state *uts)
30 {
31         /*
32          * The image being booted presents a menu of options:
33          *
34          * Fedora-Workstation-armhfp-31-1.9 Boot Options.
35          * 1:   Fedora-Workstation-armhfp-31-1.9 (5.3.7-301.fc31.armv7hl)
36          * Enter choice:
37          *
38          * Provide input for this, to avoid waiting two seconds for a timeout.
39          */
40         ut_asserteq(2, console_in_puts("1\n"));
41
42         return 0;
43 }
44
45 /* Check 'bootflow scan/list' commands */
46 static int bootflow_cmd(struct unit_test_state *uts)
47 {
48         console_record_reset_enable();
49         ut_assertok(run_command("bootdev select 1", 0));
50         ut_assert_console_end();
51         ut_assertok(run_command("bootflow scan -l", 0));
52         ut_assert_nextline("Scanning for bootflows in bootdev 'mmc1.bootdev'");
53         ut_assert_nextline("Seq  Method       State   Uclass    Part  Name                      Filename");
54         ut_assert_nextlinen("---");
55         ut_assert_nextline("  0  syslinux     ready   mmc          1  mmc1.bootdev.part_1       /extlinux/extlinux.conf");
56         ut_assert_nextlinen("---");
57         ut_assert_nextline("(1 bootflow, 1 valid)");
58         ut_assert_console_end();
59
60         ut_assertok(run_command("bootflow list", 0));
61         ut_assert_nextline("Showing bootflows for bootdev 'mmc1.bootdev'");
62         ut_assert_nextline("Seq  Method       State   Uclass    Part  Name                      Filename");
63         ut_assert_nextlinen("---");
64         ut_assert_nextline("  0  syslinux     ready   mmc          1  mmc1.bootdev.part_1       /extlinux/extlinux.conf");
65         ut_assert_nextlinen("---");
66         ut_assert_nextline("(1 bootflow, 1 valid)");
67         ut_assert_console_end();
68
69         return 0;
70 }
71 BOOTSTD_TEST(bootflow_cmd, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
72
73 /* Check 'bootflow scan' with a name / label / seq */
74 static int bootflow_cmd_label(struct unit_test_state *uts)
75 {
76         console_record_reset_enable();
77         ut_assertok(run_command("bootflow scan -l mmc1", 0));
78         ut_assert_nextline("Scanning for bootflows in bootdev 'mmc1.bootdev'");
79         ut_assert_skip_to_line("(1 bootflow, 1 valid)");
80         ut_assert_console_end();
81
82         ut_assertok(run_command("bootflow scan -l mmc0.bootdev", 0));
83         ut_assert_nextline("Scanning for bootflows in bootdev 'mmc0.bootdev'");
84         ut_assert_skip_to_line("(0 bootflows, 0 valid)");
85         ut_assert_console_end();
86
87         ut_assertok(run_command("bootflow scan -l 0", 0));
88         ut_assert_nextline("Scanning for bootflows in bootdev 'mmc2.bootdev'");
89         ut_assert_skip_to_line("(0 bootflows, 0 valid)");
90         ut_assert_console_end();
91
92         return 0;
93 }
94 BOOTSTD_TEST(bootflow_cmd_label, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
95
96 /* Check 'bootflow scan/list' commands using all bootdevs */
97 static int bootflow_cmd_glob(struct unit_test_state *uts)
98 {
99         ut_assertok(bootstd_test_drop_bootdev_order(uts));
100
101         console_record_reset_enable();
102         ut_assertok(run_command("bootflow scan -lG", 0));
103         ut_assert_nextline("Scanning for bootflows in all bootdevs");
104         ut_assert_nextline("Seq  Method       State   Uclass    Part  Name                      Filename");
105         ut_assert_nextlinen("---");
106         ut_assert_nextline("Scanning bootdev 'mmc2.bootdev':");
107         ut_assert_nextline("Scanning bootdev 'mmc1.bootdev':");
108         ut_assert_nextline("  0  syslinux     ready   mmc          1  mmc1.bootdev.part_1       /extlinux/extlinux.conf");
109         ut_assert_nextline("Scanning bootdev 'mmc0.bootdev':");
110         ut_assert_nextline("No more bootdevs");
111         ut_assert_nextlinen("---");
112         ut_assert_nextline("(1 bootflow, 1 valid)");
113         ut_assert_console_end();
114
115         ut_assertok(run_command("bootflow list", 0));
116         ut_assert_nextline("Showing all bootflows");
117         ut_assert_nextline("Seq  Method       State   Uclass    Part  Name                      Filename");
118         ut_assert_nextlinen("---");
119         ut_assert_nextline("  0  syslinux     ready   mmc          1  mmc1.bootdev.part_1       /extlinux/extlinux.conf");
120         ut_assert_nextlinen("---");
121         ut_assert_nextline("(1 bootflow, 1 valid)");
122         ut_assert_console_end();
123
124         return 0;
125 }
126 BOOTSTD_TEST(bootflow_cmd_glob, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
127
128 /* Check 'bootflow scan -e' */
129 static int bootflow_cmd_scan_e(struct unit_test_state *uts)
130 {
131         ut_assertok(bootstd_test_drop_bootdev_order(uts));
132
133         console_record_reset_enable();
134         ut_assertok(run_command("bootflow scan -aleG", 0));
135         ut_assert_nextline("Scanning for bootflows in all bootdevs");
136         ut_assert_nextline("Seq  Method       State   Uclass    Part  Name                      Filename");
137         ut_assert_nextlinen("---");
138         ut_assert_nextline("Scanning bootdev 'mmc2.bootdev':");
139         ut_assert_nextline("  0  syslinux     media   mmc          0  mmc2.bootdev.whole        <NULL>");
140         ut_assert_nextline("     ** No partition found, err=-93");
141         ut_assert_nextline("  1  efi          media   mmc          0  mmc2.bootdev.whole        <NULL>");
142         ut_assert_nextline("     ** No partition found, err=-93");
143
144         ut_assert_nextline("Scanning bootdev 'mmc1.bootdev':");
145         ut_assert_nextline("  2  syslinux     media   mmc          0  mmc1.bootdev.whole        <NULL>");
146         ut_assert_nextline("     ** No partition found, err=-2");
147         ut_assert_nextline("  3  efi          media   mmc          0  mmc1.bootdev.whole        <NULL>");
148         ut_assert_nextline("     ** No partition found, err=-2");
149         ut_assert_nextline("  4  syslinux     ready   mmc          1  mmc1.bootdev.part_1       /extlinux/extlinux.conf");
150         ut_assert_nextline("  5  efi          fs      mmc          1  mmc1.bootdev.part_1       efi/boot/bootsbox.efi");
151
152         ut_assert_skip_to_line("Scanning bootdev 'mmc0.bootdev':");
153         ut_assert_skip_to_line(" 3f  efi          media   mmc          0  mmc0.bootdev.whole        <NULL>");
154         ut_assert_nextline("     ** No partition found, err=-93");
155         ut_assert_nextline("No more bootdevs");
156         ut_assert_nextlinen("---");
157         ut_assert_nextline("(64 bootflows, 1 valid)");
158         ut_assert_console_end();
159
160         ut_assertok(run_command("bootflow list", 0));
161         ut_assert_nextline("Showing all bootflows");
162         ut_assert_nextline("Seq  Method       State   Uclass    Part  Name                      Filename");
163         ut_assert_nextlinen("---");
164         ut_assert_nextline("  0  syslinux     media   mmc          0  mmc2.bootdev.whole        <NULL>");
165         ut_assert_nextline("  1  efi          media   mmc          0  mmc2.bootdev.whole        <NULL>");
166         ut_assert_skip_to_line("  4  syslinux     ready   mmc          1  mmc1.bootdev.part_1       /extlinux/extlinux.conf");
167         ut_assert_skip_to_line(" 3f  efi          media   mmc          0  mmc0.bootdev.whole        <NULL>");
168         ut_assert_nextlinen("---");
169         ut_assert_nextline("(64 bootflows, 1 valid)");
170         ut_assert_console_end();
171
172         return 0;
173 }
174 BOOTSTD_TEST(bootflow_cmd_scan_e, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
175
176 /* Check 'bootflow info' */
177 static int bootflow_cmd_info(struct unit_test_state *uts)
178 {
179         console_record_reset_enable();
180         ut_assertok(run_command("bootdev select 1", 0));
181         ut_assert_console_end();
182         ut_assertok(run_command("bootflow scan", 0));
183         ut_assert_console_end();
184         ut_assertok(run_command("bootflow select 0", 0));
185         ut_assert_console_end();
186         ut_assertok(run_command("bootflow info", 0));
187         ut_assert_nextline("Name:      mmc1.bootdev.part_1");
188         ut_assert_nextline("Device:    mmc1.bootdev");
189         ut_assert_nextline("Block dev: mmc1.blk");
190         ut_assert_nextline("Method:    syslinux");
191         ut_assert_nextline("State:     ready");
192         ut_assert_nextline("Partition: 1");
193         ut_assert_nextline("Subdir:    (none)");
194         ut_assert_nextline("Filename:  /extlinux/extlinux.conf");
195         ut_assert_nextlinen("Buffer:    ");
196         ut_assert_nextline("Size:      253 (595 bytes)");
197         ut_assert_nextline("OS:        Fedora-Workstation-armhfp-31-1.9 (5.3.7-301.fc31.armv7hl)");
198         ut_assert_nextline("Logo:      (none)");
199         ut_assert_nextline("Error:     0");
200         ut_assert_console_end();
201
202         ut_assertok(run_command("bootflow info -d", 0));
203         ut_assert_nextline("Name:      mmc1.bootdev.part_1");
204         ut_assert_skip_to_line("Error:     0");
205         ut_assert_nextline("Contents:");
206         ut_assert_nextline("%s", "");
207         ut_assert_nextline("# extlinux.conf generated by appliance-creator");
208         ut_assert_skip_to_line("        initrd /initramfs-5.3.7-301.fc31.armv7hl.img");
209         ut_assert_console_end();
210
211         return 0;
212 }
213 BOOTSTD_TEST(bootflow_cmd_info, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
214
215 /* Check 'bootflow scan -b' to boot the first available bootdev */
216 static int bootflow_scan_boot(struct unit_test_state *uts)
217 {
218         console_record_reset_enable();
219         ut_assertok(inject_response(uts));
220         ut_assertok(run_command("bootflow scan -b", 0));
221         ut_assert_nextline(
222                 "** Booting bootflow 'mmc1.bootdev.part_1' with syslinux");
223         ut_assert_nextline("Ignoring unknown command: ui");
224
225         /*
226          * We expect it to get through to boot although sandbox always returns
227          * -EFAULT as it cannot actually boot the kernel
228          */
229         ut_assert_skip_to_line("sandbox: continuing, as we cannot run Linux");
230         ut_assert_nextline("Boot failed (err=-14)");
231         ut_assert_console_end();
232
233         return 0;
234 }
235 BOOTSTD_TEST(bootflow_scan_boot, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
236
237 /* Check iterating through available bootflows */
238 static int bootflow_iter(struct unit_test_state *uts)
239 {
240         struct bootflow_iter iter;
241         struct bootflow bflow;
242
243         bootstd_clear_glob();
244
245         /* The first device is mmc2.bootdev which has no media */
246         ut_asserteq(-EPROTONOSUPPORT,
247                     bootflow_scan_first(&iter, BOOTFLOWF_ALL | BOOTFLOWF_SKIP_GLOBAL, &bflow));
248         ut_asserteq(2, iter.num_methods);
249         ut_asserteq(0, iter.cur_method);
250         ut_asserteq(0, iter.part);
251         ut_asserteq(0, iter.max_part);
252         ut_asserteq_str("syslinux", iter.method->name);
253         ut_asserteq(0, bflow.err);
254
255         /*
256          * This shows MEDIA even though there is none, since in
257          * bootdev_find_in_blk() we call part_get_info() which returns
258          * -EPROTONOSUPPORT. Ideally it would return -EEOPNOTSUPP and we would
259          * know.
260          */
261         ut_asserteq(BOOTFLOWST_MEDIA, bflow.state);
262
263         ut_asserteq(-EPROTONOSUPPORT, bootflow_scan_next(&iter, &bflow));
264         ut_asserteq(2, iter.num_methods);
265         ut_asserteq(1, iter.cur_method);
266         ut_asserteq(0, iter.part);
267         ut_asserteq(0, iter.max_part);
268         ut_asserteq_str("efi", iter.method->name);
269         ut_asserteq(0, bflow.err);
270         ut_asserteq(BOOTFLOWST_MEDIA, bflow.state);
271         bootflow_free(&bflow);
272
273         /* The next device is mmc1.bootdev - at first we use the whole device */
274         ut_asserteq(-ENOENT, bootflow_scan_next(&iter, &bflow));
275         ut_asserteq(2, iter.num_methods);
276         ut_asserteq(0, iter.cur_method);
277         ut_asserteq(0, iter.part);
278         ut_asserteq(0x1e, iter.max_part);
279         ut_asserteq_str("syslinux", iter.method->name);
280         ut_asserteq(0, bflow.err);
281         ut_asserteq(BOOTFLOWST_MEDIA, bflow.state);
282         bootflow_free(&bflow);
283
284         ut_asserteq(-ENOENT, bootflow_scan_next(&iter, &bflow));
285         ut_asserteq(2, iter.num_methods);
286         ut_asserteq(1, iter.cur_method);
287         ut_asserteq(0, iter.part);
288         ut_asserteq(0x1e, iter.max_part);
289         ut_asserteq_str("efi", iter.method->name);
290         ut_asserteq(0, bflow.err);
291         ut_asserteq(BOOTFLOWST_MEDIA, bflow.state);
292         bootflow_free(&bflow);
293
294         /* Then more to partition 1 where we find something */
295         ut_assertok(bootflow_scan_next(&iter, &bflow));
296         ut_asserteq(2, iter.num_methods);
297         ut_asserteq(0, iter.cur_method);
298         ut_asserteq(1, iter.part);
299         ut_asserteq(0x1e, iter.max_part);
300         ut_asserteq_str("syslinux", iter.method->name);
301         ut_asserteq(0, bflow.err);
302         ut_asserteq(BOOTFLOWST_READY, bflow.state);
303         bootflow_free(&bflow);
304
305         ut_asserteq(-ENOENT, bootflow_scan_next(&iter, &bflow));
306         ut_asserteq(2, iter.num_methods);
307         ut_asserteq(1, iter.cur_method);
308         ut_asserteq(1, iter.part);
309         ut_asserteq(0x1e, iter.max_part);
310         ut_asserteq_str("efi", iter.method->name);
311         ut_asserteq(0, bflow.err);
312         ut_asserteq(BOOTFLOWST_FS, bflow.state);
313         bootflow_free(&bflow);
314
315         /* Then more to partition 2 which doesn't exist */
316         ut_asserteq(-ENOENT, bootflow_scan_next(&iter, &bflow));
317         ut_asserteq(2, iter.num_methods);
318         ut_asserteq(0, iter.cur_method);
319         ut_asserteq(2, iter.part);
320         ut_asserteq(0x1e, iter.max_part);
321         ut_asserteq_str("syslinux", iter.method->name);
322         ut_asserteq(0, bflow.err);
323         ut_asserteq(BOOTFLOWST_MEDIA, bflow.state);
324         bootflow_free(&bflow);
325
326         bootflow_iter_uninit(&iter);
327
328         ut_assert_console_end();
329
330         return 0;
331 }
332 BOOTSTD_TEST(bootflow_iter, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
333
334 #if defined(CONFIG_SANDBOX) && defined(CONFIG_BOOTMETH_GLOBAL)
335 /* Check using the system bootdev */
336 static int bootflow_system(struct unit_test_state *uts)
337 {
338         struct udevice *dev;
339
340         if (!IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR))
341                 return -EAGAIN;
342         ut_assertok(uclass_get_device_by_name(UCLASS_BOOTMETH, "efi_mgr",
343                                               &dev));
344         sandbox_set_fake_efi_mgr_dev(dev, true);
345
346         /* We should get a single 'bootmgr' method right at the end */
347         bootstd_clear_glob();
348         console_record_reset_enable();
349         ut_assertok(run_command("bootflow scan -l", 0));
350         ut_assert_skip_to_line(
351                 "  0  efi_mgr      ready   (none)       0  <NULL>                    <NULL>");
352         ut_assert_skip_to_line("No more bootdevs");
353         ut_assert_skip_to_line("(5 bootflows, 5 valid)");
354         ut_assert_console_end();
355
356         return 0;
357 }
358 BOOTSTD_TEST(bootflow_system, UT_TESTF_DM | UT_TESTF_SCAN_PDATA |
359              UT_TESTF_SCAN_FDT);
360 #endif
361
362 /* Check disabling a bootmethod if it requests it */
363 static int bootflow_iter_disable(struct unit_test_state *uts)
364 {
365         struct udevice *bootstd, *dev;
366         struct bootflow_iter iter;
367         struct bootflow bflow;
368         int i;
369
370         /* Add the EFI bootmgr driver */
371         ut_assertok(uclass_first_device_err(UCLASS_BOOTSTD, &bootstd));
372         ut_assertok(device_bind_driver(bootstd, "bootmeth_sandbox", "sandbox",
373                                        &dev));
374
375         ut_assertok(bootstd_test_drop_bootdev_order(uts));
376
377         bootstd_clear_glob();
378         console_record_reset_enable();
379         ut_assertok(inject_response(uts));
380         ut_assertok(run_command("bootflow scan -lb", 0));
381
382         /* Try to boot the bootmgr flow, which will fail */
383         console_record_reset_enable();
384         ut_assertok(bootflow_scan_first(&iter, 0, &bflow));
385         ut_asserteq(3, iter.num_methods);
386         ut_asserteq_str("sandbox", iter.method->name);
387         ut_assertok(inject_response(uts));
388         ut_asserteq(-ENOTSUPP, bootflow_run_boot(&iter, &bflow));
389
390         ut_assert_skip_to_line("Boot method 'sandbox' failed and will not be retried");
391         ut_assert_console_end();
392
393         /* Check that the sandbox bootmeth has been removed */
394         ut_asserteq(2, iter.num_methods);
395         for (i = 0; i < iter.num_methods; i++)
396                 ut_assert(strcmp("sandbox", iter.method_order[i]->name));
397
398         return 0;
399 }
400 BOOTSTD_TEST(bootflow_iter_disable, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
401
402 /* Check 'bootflow scan' with a bootmeth ordering including a global bootmeth */
403 static int bootflow_scan_glob_bootmeth(struct unit_test_state *uts)
404 {
405         if (!IS_ENABLED(CONFIG_BOOTMETH_GLOBAL))
406                 return -EAGAIN;
407
408         ut_assertok(bootstd_test_drop_bootdev_order(uts));
409
410         /*
411          * Make sure that the -G flag makes the scan fail, since this is not
412          * supported when an ordering is provided
413          */
414         console_record_reset_enable();
415         ut_assertok(bootmeth_set_order("efi firmware0"));
416         ut_assertok(run_command("bootflow scan -lG", 0));
417         ut_assert_nextline("Scanning for bootflows in all bootdevs");
418         ut_assert_nextline(
419                 "Seq  Method       State   Uclass    Part  Name                      Filename");
420         ut_assert_nextlinen("---");
421         ut_assert_nextlinen("---");
422         ut_assert_nextline("(0 bootflows, 0 valid)");
423         ut_assert_console_end();
424
425         ut_assertok(run_command("bootflow scan -l", 0));
426         ut_assert_nextline("Scanning for bootflows in all bootdevs");
427         ut_assert_nextline(
428                 "Seq  Method       State   Uclass    Part  Name                      Filename");
429         ut_assert_nextlinen("---");
430         ut_assert_nextline("Scanning global bootmeth 'firmware0':");
431         ut_assert_nextline("Scanning bootdev 'mmc2.bootdev':");
432         ut_assert_nextline("Scanning bootdev 'mmc1.bootdev':");
433         ut_assert_nextline("Scanning bootdev 'mmc0.bootdev':");
434         ut_assert_nextline("No more bootdevs");
435         ut_assert_nextlinen("---");
436         ut_assert_nextline("(0 bootflows, 0 valid)");
437         ut_assert_console_end();
438
439         return 0;
440 }
441 BOOTSTD_TEST(bootflow_scan_glob_bootmeth, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
442
443 /* Check 'bootflow boot' to boot a selected bootflow */
444 static int bootflow_cmd_boot(struct unit_test_state *uts)
445 {
446         console_record_reset_enable();
447         ut_assertok(run_command("bootdev select 1", 0));
448         ut_assert_console_end();
449         ut_assertok(run_command("bootflow scan", 0));
450         ut_assert_console_end();
451         ut_assertok(run_command("bootflow select 0", 0));
452         ut_assert_console_end();
453
454         ut_assertok(inject_response(uts));
455         ut_asserteq(1, run_command("bootflow boot", 0));
456         ut_assert_nextline(
457                 "** Booting bootflow 'mmc1.bootdev.part_1' with syslinux");
458         ut_assert_nextline("Ignoring unknown command: ui");
459
460         /*
461          * We expect it to get through to boot although sandbox always returns
462          * -EFAULT as it cannot actually boot the kernel
463          */
464         ut_assert_skip_to_line("sandbox: continuing, as we cannot run Linux");
465         ut_assert_nextline("Boot failed (err=-14)");
466         ut_assert_console_end();
467
468         return 0;
469 }
470 BOOTSTD_TEST(bootflow_cmd_boot, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
471
472 /* Check 'bootflow menu' to select a bootflow */
473 static int bootflow_cmd_menu(struct unit_test_state *uts)
474 {
475         static const char *order[] = {"mmc2", "mmc1", "mmc4", NULL};
476         struct udevice *dev, *bootstd;
477         struct bootstd_priv *std;
478         const char **old_order;
479         char prev[3];
480         ofnode node;
481
482         /* Enable the mmc4 node since we need a second bootflow */
483         node = ofnode_path("/mmc4");
484         ut_assertok(lists_bind_fdt(gd->dm_root, node, &dev, NULL, false));
485
486         /* Enable the script bootmeth too */
487         ut_assertok(uclass_first_device_err(UCLASS_BOOTSTD, &bootstd));
488         ut_assertok(device_bind(bootstd, DM_DRIVER_REF(bootmeth_script),
489                                 "bootmeth_script", 0, ofnode_null(), &dev));
490
491         /* Change the order to include mmc4 */
492         std = dev_get_priv(bootstd);
493         old_order = std->bootdev_order;
494         std->bootdev_order = order;
495
496         console_record_reset_enable();
497         ut_assertok(run_command("bootflow scan", 0));
498         ut_assert_console_end();
499
500         /* Restore the order used by the device tree */
501         std->bootdev_order = old_order;
502
503         /* Add keypresses to move to and select the second one in the list */
504         prev[0] = CTL_CH('n');
505         prev[1] = '\r';
506         prev[2] = '\0';
507         ut_asserteq(2, console_in_puts(prev));
508
509         ut_assertok(run_command("bootflow menu", 0));
510         ut_assert_nextline("Selected: Armbian");
511         ut_assert_console_end();
512
513         return 0;
514 }
515 BOOTSTD_TEST(bootflow_cmd_menu, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
This page took 0.054506 seconds and 4 git commands to generate.