bootstd: Add a test for bootmeth_cros

The ChromiumOS bootmeth has no tests at present. Before adding more
features. add a basic test.

This creates a disk which can be scanned by the bootmeth, so make sure
things work. It is quite rudimentary, since the kernel is faked, the root
disk is missing and there is no cmdline stored.

Enable the bootmeth for snow so it can build the unit test.

Signed-off-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
Simon Glass 2023-08-24 13:55:41 -06:00 committed by Tom Rini
parent 2b9adcaca2
commit d08db02d2d
4 changed files with 183 additions and 2 deletions

View File

@ -39,6 +39,8 @@
mmc1 = "/mmc1";
mmc2 = "/mmc2";
mmc3 = "/mmc3";
mmc4 = "/mmc4";
mmc5 = "/mmc5";
pci0 = &pci0;
pci1 = &pci1;
pci2 = &pci2;
@ -1055,6 +1057,13 @@
filename = "mmc4.img";
};
/* This is used for ChromiumOS tests */
mmc5 {
status = "disabled";
compatible = "sandbox,mmc";
filename = "mmc5.img";
};
pch {
compatible = "sandbox,pch";
};

View File

@ -29,6 +29,7 @@ CONFIG_DEBUG_UART=y
CONFIG_FIT=y
CONFIG_FIT_BEST_MATCH=y
CONFIG_BOOTSTD_FULL=y
CONFIG_BOOTMETH_CROS=y
CONFIG_DISTRO_DEFAULTS=y
CONFIG_SILENT_CONSOLE=y
CONFIG_BLOBLIST=y

View File

@ -27,6 +27,7 @@
DECLARE_GLOBAL_DATA_PTR;
extern U_BOOT_DRIVER(bootmeth_cros);
extern U_BOOT_DRIVER(bootmeth_script);
static int inject_response(struct unit_test_state *uts)
@ -514,7 +515,8 @@ BOOTSTD_TEST(bootflow_cmd_boot, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
* @mmc_dev: MMC device to use, e.g. "mmc4"
* Returns 0 on success, -ve on failure
*/
static int prep_mmc_bootdev(struct unit_test_state *uts, const char *mmc_dev)
static int prep_mmc_bootdev(struct unit_test_state *uts, const char *mmc_dev,
bool bind_cros)
{
const char *order[] = {"mmc2", "mmc1", mmc_dev, NULL};
struct udevice *dev, *bootstd;
@ -533,6 +535,13 @@ static int prep_mmc_bootdev(struct unit_test_state *uts, const char *mmc_dev)
ut_assertok(device_bind(bootstd, DM_DRIVER_REF(bootmeth_script),
"bootmeth_script", 0, ofnode_null(), &dev));
/* Enable the cros bootmeth if needed */
if (bind_cros) {
ut_assertok(uclass_first_device_err(UCLASS_BOOTSTD, &bootstd));
ut_assertok(device_bind(bootstd, DM_DRIVER_REF(bootmeth_cros),
"cros", 0, ofnode_null(), &dev));
}
/* Change the order to include the device */
std = dev_get_priv(bootstd);
old_order = std->bootdev_order;
@ -556,7 +565,7 @@ static int prep_mmc_bootdev(struct unit_test_state *uts, const char *mmc_dev)
*/
static int prep_mmc4_bootdev(struct unit_test_state *uts)
{
ut_assertok(prep_mmc_bootdev(uts, "mmc4"));
ut_assertok(prep_mmc_bootdev(uts, "mmc4", false));
return 0;
}
@ -963,3 +972,23 @@ static int bootflow_cmdline(struct unit_test_state *uts)
return 0;
}
BOOTSTD_TEST(bootflow_cmdline, 0);
/* Test ChromiumOS bootmeth */
static int bootflow_cros(struct unit_test_state *uts)
{
ut_assertok(prep_mmc_bootdev(uts, "mmc5", true));
ut_assertok(run_command("bootflow list", 0));
ut_assert_nextlinen("Showing all");
ut_assert_nextlinen("Seq");
ut_assert_nextlinen("---");
ut_assert_nextlinen(" 0 extlinux");
ut_assert_nextlinen(" 1 cros ready mmc 2 mmc5.bootdev.whole ");
ut_assert_nextlinen("---");
ut_assert_skip_to_line("(2 bootflows, 2 valid)");
ut_assert_console_end();
return 0;
}
BOOTSTD_TEST(bootflow_cros, 0);

View File

@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
import collections
import getpass
import gzip
import os
@ -282,6 +283,146 @@ label Fedora-Workstation-armhfp-31-1.9 (5.3.7-301.fc31.armv7hl)
copy_prepared_image(cons, mmc_dev, fname)
def setup_cros_image(cons):
"""Create a 20MB disk image with ChromiumOS partitions"""
Partition = collections.namedtuple('part', 'start,size,name')
parts = {}
disk_data = None
def pack_kernel(cons, arch, kern, dummy):
"""Pack a kernel containing some fake data
Args:
cons (ConsoleBase): Console to use
arch (str): Architecture to use ('x86' or 'arm')
kern (str): Filename containing kernel
dummy (str): Dummy filename to use for config and bootloader
Return:
bytes: Packed-kernel data
"""
kern_part = os.path.join(cons.config.result_dir, 'kern-part-{arch}.bin')
u_boot_utils.run_and_log(
cons,
f'futility vbutil_kernel --pack {kern_part} '
'--keyblock doc/chromium/files/devkeys/kernel.keyblock '
'--signprivate doc/chromium/files/devkeys/kernel_data_key.vbprivk '
f'--version 1 --config {dummy} --bootloader {dummy} '
f'--vmlinuz {kern}')
with open(kern_part, 'rb') as inf:
kern_part_data = inf.read()
return kern_part_data
def set_part_data(partnum, data):
"""Set the contents of a disk partition
This updates disk_data by putting data in the right place
Args:
partnum (int): Partition number to set
data (bytes): Data for that partition
"""
nonlocal disk_data
start = parts[partnum].start * sect_size
disk_data = disk_data[:start] + data + disk_data[start + len(data):]
mmc_dev = 5
fname = os.path.join(cons.config.source_dir, f'mmc{mmc_dev}.img')
u_boot_utils.run_and_log(cons, 'qemu-img create %s 20M' % fname)
#mnt = os.path.join(cons.config.persistent_data_dir, 'mnt')
#mkdir_cond(mnt)
u_boot_utils.run_and_log(cons, f'cgpt create {fname}')
uuid_state = 'ebd0a0a2-b9e5-4433-87c0-68b6b72699c7'
uuid_kern = 'fe3a2a5d-4f32-41a7-b725-accc3285a309'
uuid_root = '3cb8e202-3b7e-47dd-8a3c-7ff2a13cfcec'
uuid_rwfw = 'cab6e88e-abf3-4102-a07a-d4bb9be3c1d3'
uuid_reserved = '2e0a753d-9e48-43b0-8337-b15192cb1b5e'
uuid_efi = 'c12a7328-f81f-11d2-ba4b-00a0c93ec93b'
ptr = 40
# Number of sectors in 1MB
sect_size = 512
sect_1mb = (1 << 20) // sect_size
required_parts = [
{'num': 0xb, 'label':'RWFW', 'type': uuid_rwfw, 'size': '1'},
{'num': 6, 'label':'KERN_C', 'type': uuid_kern, 'size': '1'},
{'num': 7, 'label':'ROOT_C', 'type': uuid_root, 'size': '1'},
{'num': 9, 'label':'reserved', 'type': uuid_reserved, 'size': '1'},
{'num': 0xa, 'label':'reserved', 'type': uuid_reserved, 'size': '1'},
{'num': 2, 'label':'KERN_A', 'type': uuid_kern, 'size': '1M'},
{'num': 4, 'label':'KERN_B', 'type': uuid_kern, 'size': '1M'},
{'num': 8, 'label':'OEM', 'type': uuid_state, 'size': '1M'},
{'num': 0xc, 'label':'EFI-SYSTEM', 'type': uuid_efi, 'size': '1M'},
{'num': 5, 'label':'ROOT_B', 'type': uuid_root, 'size': '1'},
{'num': 3, 'label':'ROOT_A', 'type': uuid_root, 'size': '1'},
{'num': 1, 'label':'STATE', 'type': uuid_state, 'size': '1M'},
]
for part in required_parts:
size_str = part['size']
if 'M' in size_str:
size = int(size_str[:-1]) * sect_1mb
else:
size = int(size_str)
u_boot_utils.run_and_log(
cons,
f"cgpt add -i {part['num']} -b {ptr} -s {size} -t {part['type']} {fname}")
ptr += size
u_boot_utils.run_and_log(cons, f'cgpt boot -p {fname}')
out = u_boot_utils.run_and_log(cons, f'cgpt show -q {fname}')
'''We expect something like this:
8239 2048 1 Basic data
45 2048 2 ChromeOS kernel
8238 1 3 ChromeOS rootfs
2093 2048 4 ChromeOS kernel
8237 1 5 ChromeOS rootfs
41 1 6 ChromeOS kernel
42 1 7 ChromeOS rootfs
4141 2048 8 Basic data
43 1 9 ChromeOS reserved
44 1 10 ChromeOS reserved
40 1 11 ChromeOS firmware
6189 2048 12 EFI System Partition
'''
# Create a dict (indexed by partition number) containing the above info
for line in out.splitlines():
start, size, num, name = line.split(maxsplit=3)
parts[int(num)] = Partition(int(start), int(size), name)
dummy = os.path.join(cons.config.result_dir, 'dummy.txt')
with open(dummy, 'wb') as outf:
outf.write(b'dummy\n')
# For now we just use dummy kernels. This limits testing to just detecting
# a signed kernel. We could add support for the x86 data structures so that
# testing could cover getting the cmdline, setup.bin and other pieces.
kern = os.path.join(cons.config.result_dir, 'kern.bin')
with open(kern, 'wb') as outf:
outf.write(b'kernel\n')
with open(fname, 'rb') as inf:
disk_data = inf.read()
# put x86 kernel in partition 2 and arm one in partition 4
set_part_data(2, pack_kernel(cons, 'x86', kern, dummy))
set_part_data(4, pack_kernel(cons, 'arm', kern, dummy))
with open(fname, 'wb') as outf:
outf.write(disk_data)
return fname
def setup_cedit_file(cons):
infname = os.path.join(cons.config.source_dir,
'test/boot/files/expo_layout.dts')
@ -329,6 +470,7 @@ def test_ut_dm_init_bootstd(u_boot_console):
setup_bootflow_image(u_boot_console)
setup_bootmenu_image(u_boot_console)
setup_cedit_file(u_boot_console)
setup_cros_image(u_boot_console)
# Restart so that the new mmc1.img is picked up
u_boot_console.restart_uboot()