bootctl: Initial experimentation
This provides a basic prototype for boot control. Some documentation is in boot/bootctl/README.rst
This commit is contained in:
5
test/boot/bootctl/Makefile
Normal file
5
test/boot/bootctl/Makefile
Normal file
@@ -0,0 +1,5 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
# Copyright 2025 Canonical Ltd
|
||||
|
||||
obj-y += bootctl.o
|
||||
327
test/boot/bootctl/bootctl.c
Normal file
327
test/boot/bootctl/bootctl.c
Normal file
@@ -0,0 +1,327 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Tests for bootctl
|
||||
*
|
||||
* For now this is just samples, showing how the different functions can be
|
||||
* tested
|
||||
*
|
||||
* Copyright 2025 Canonical Ltd
|
||||
* Written by Simon Glass <simon.glass@canonical.com>
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <bootctl.h>
|
||||
#include <bootmeth.h>
|
||||
#include <dm.h>
|
||||
#include <os.h>
|
||||
#include <test/ut.h>
|
||||
#include "bootctl_common.h"
|
||||
#include <bootctl/measure.h>
|
||||
#include <bootctl/oslist.h>
|
||||
#include <bootctl/state.h>
|
||||
#include "../bootstd_common.h"
|
||||
|
||||
/* test that expected devices are available and can be probed */
|
||||
static int bootctl_base(struct unit_test_state *uts)
|
||||
{
|
||||
struct udevice *dev;
|
||||
|
||||
ut_assertok(bootctl_get_dev(UCLASS_BOOTCTL_UI, &dev));
|
||||
ut_asserteq_str("ui", dev->name);
|
||||
|
||||
ut_assertok(bootctl_get_dev(UCLASS_BOOTCTL_OSLIST, &dev));
|
||||
ut_asserteq_str("oslist", dev->name);
|
||||
|
||||
ut_assertok(bootctl_get_dev(UCLASS_BOOTCTL_STATE, &dev));
|
||||
ut_asserteq_str("state", dev->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
BOOTCTL_TEST(bootctl_base, UTF_DM | UTF_SCAN_FDT);
|
||||
|
||||
/* test finding an OS */
|
||||
static int bootctl_oslist(struct unit_test_state *uts)
|
||||
{
|
||||
struct oslist_iter iter;
|
||||
struct osinfo info;
|
||||
struct bootflow *bflow = &info.bflow;
|
||||
struct udevice *dev;
|
||||
|
||||
ut_assertok(bootctl_get_dev(UCLASS_BOOTCTL_OSLIST, &dev));
|
||||
ut_asserteq_str("oslist", dev->name);
|
||||
|
||||
/* initially we should only see Fedora */
|
||||
bc_oslist_setup_iter(&iter);
|
||||
ut_assertok(bc_oslist_next(dev, &iter, &info));
|
||||
ut_asserteq_str("mmc1.bootdev.part_1", bflow->name);
|
||||
ut_asserteq_strn("Fedora-Workstation", bflow->os_name);
|
||||
|
||||
ut_asserteq(-ENODEV, bc_oslist_next(dev, &iter, &info));
|
||||
|
||||
return 0;
|
||||
}
|
||||
BOOTCTL_TEST(bootctl_oslist, UTF_DM | UTF_SCAN_FDT);
|
||||
|
||||
/* test finding OSes on mmc and usb */
|
||||
static int bootctl_oslist_usb(struct unit_test_state *uts)
|
||||
{
|
||||
struct oslist_iter iter;
|
||||
struct osinfo info;
|
||||
struct bootflow *bflow = &info.bflow;
|
||||
struct udevice *dev;
|
||||
|
||||
test_set_skip_delays(true);
|
||||
bootstd_reset_usb();
|
||||
|
||||
ut_assertok(bootctl_get_dev(UCLASS_BOOTCTL_OSLIST, &dev));
|
||||
ut_asserteq_str("oslist", dev->name);
|
||||
|
||||
/* include usb in the bootdev order */
|
||||
ut_assertok(bootdev_set_order("mmc usb"));
|
||||
|
||||
bc_oslist_setup_iter(&iter);
|
||||
ut_assertok(bc_oslist_next(dev, &iter, &info));
|
||||
ut_asserteq_str("mmc1.bootdev.part_1", bflow->name);
|
||||
|
||||
ut_assertok(bc_oslist_next(dev, &iter, &info));
|
||||
ut_asserteq_str("hub1.p4.usb_mass_storage.lun0.bootdev.part_1", bflow->name);
|
||||
|
||||
ut_asserteq(-ENODEV, bc_oslist_next(dev, &iter, &info));
|
||||
|
||||
return 0;
|
||||
}
|
||||
BOOTCTL_TEST(bootctl_oslist_usb, UTF_DM | UTF_SCAN_FDT);
|
||||
|
||||
/* test basic use of state */
|
||||
static int bootctl_simple_state_base(struct unit_test_state *uts)
|
||||
{
|
||||
struct udevice *dev;
|
||||
const char *sval;
|
||||
struct abuf buf;
|
||||
bool bval;
|
||||
long ival;
|
||||
|
||||
ut_assertok(bootctl_get_dev(UCLASS_BOOTCTL_STATE, &dev));
|
||||
ut_assertok(bc_state_write_bool(dev, "fred", false));
|
||||
ut_assertok(bc_state_write_bool(dev, "mary", true));
|
||||
ut_assertok(bc_state_write_int(dev, "alex", 123));
|
||||
ut_assertok(bc_state_write_str(dev, "john", "abc"));
|
||||
|
||||
ut_assertok(bc_state_read_bool(dev, "fred", &bval));
|
||||
ut_asserteq(false, bval);
|
||||
|
||||
ut_assertok(bc_state_read_bool(dev, "mary", &bval));
|
||||
ut_asserteq(true, bval);
|
||||
|
||||
ut_assertok(bc_state_read_int(dev, "alex", &ival));
|
||||
ut_asserteq(123, ival);
|
||||
|
||||
ut_assertok(bc_state_read_str(dev, "john", &sval));
|
||||
ut_asserteq_str("abc", sval);
|
||||
|
||||
/* check the buffer contents, including the nul terminator */
|
||||
ut_assertok(bc_state_save_to_buf(dev, &buf));
|
||||
ut_asserteq_str("fred=0\nmary=1\nalex=123\njohn=abc\n", buf.data);
|
||||
ut_asserteq(strlen("fred=0\nmary=1\nalex=123\njohn=abc\n") + 1,
|
||||
buf.size);
|
||||
ut_asserteq(0, *((char *)buf.data + buf.size - 1));
|
||||
abuf_uninit(&buf);
|
||||
|
||||
/* overwrite */
|
||||
ut_assertok(bc_state_write_str(dev, "fred", "def"));
|
||||
ut_assertok(bc_state_read_str(dev, "fred", &sval));
|
||||
ut_asserteq_str("def", sval);
|
||||
|
||||
ut_assertok(bc_state_clear(dev));
|
||||
ut_asserteq(-ENOENT, bc_state_read_bool(dev, "fred", &bval));
|
||||
ut_asserteq(-ENOENT, bc_state_read_bool(dev, "mary", &bval));
|
||||
ut_asserteq(-ENOENT, bc_state_read_bool(dev, "john", &bval));
|
||||
ut_asserteq(-ENOENT, bc_state_read_bool(dev, "alex", &bval));
|
||||
|
||||
return 0;
|
||||
}
|
||||
BOOTCTL_TEST(bootctl_simple_state_base, UTF_DM | UTF_SCAN_FDT);
|
||||
|
||||
/* test loading / saving state */
|
||||
static int bootctl_simple_state_loadsave(struct unit_test_state *uts)
|
||||
{
|
||||
struct udevice *dev;
|
||||
char *buf;
|
||||
int size;
|
||||
|
||||
ut_assertok(bootctl_get_dev(UCLASS_BOOTCTL_STATE, &dev));
|
||||
ut_assertok(bc_state_write_bool(dev, "fred", false));
|
||||
ut_assertok(bc_state_write_bool(dev, "mary", true));
|
||||
ut_assertok(bc_state_save(dev));
|
||||
|
||||
/* check the file contents, including the nul terminator */
|
||||
ut_assertok(os_read_file("bootctl.ini", (void **)&buf, &size));
|
||||
ut_asserteq_str("fred=0\nmary=1\n", buf);
|
||||
ut_asserteq(strlen("fred=0\nmary=1\n") + 1, size);
|
||||
ut_asserteq(0, buf[size - 1]);
|
||||
os_free(buf);
|
||||
|
||||
ut_assertok(bc_state_load(dev));
|
||||
|
||||
return 0;
|
||||
}
|
||||
BOOTCTL_TEST(bootctl_simple_state_loadsave, UTF_DM | UTF_SCAN_FDT);
|
||||
|
||||
/* test limits */
|
||||
static int bootctl_simple_state_limits(struct unit_test_state *uts)
|
||||
{
|
||||
struct udevice *dev;
|
||||
char long_key[32]; /* avoid using constants from impl */
|
||||
struct abuf buf;
|
||||
char *data;
|
||||
int ch;
|
||||
|
||||
ut_assertok(bootctl_get_dev(UCLASS_BOOTCTL_STATE, &dev));
|
||||
|
||||
/* cannot use NULL as a key or value */
|
||||
ut_asserteq(-EINVAL, bc_state_write_bool(dev, NULL, false));
|
||||
ut_asserteq(-EINVAL, bc_state_write_str(dev, "key", NULL));
|
||||
|
||||
/* empty key and value */
|
||||
ut_asserteq(-EINVAL, bc_state_write_str(dev, "", "val"));
|
||||
ut_assertok(bc_state_write_str(dev, "empty", ""));
|
||||
|
||||
/* no spaces allowed in a key */
|
||||
ut_asserteq(-EKEYREJECTED, bc_state_write_str(dev, "my key", "val"));
|
||||
|
||||
/* check key characters */
|
||||
for (ch = 1; ch < 256; ch++) {
|
||||
char key[4] = "key";
|
||||
bool ok;
|
||||
|
||||
ok = ch == '_' || (ch >= 'a' && ch <= 'z') ||
|
||||
(ch >= '0' && ch <= '9');
|
||||
|
||||
key[1] = ch;
|
||||
printf("checking ch %x\n", ch);
|
||||
if (ok)
|
||||
ut_assertok(bc_state_write_str(dev, key, "val"));
|
||||
else
|
||||
ut_asserteq(-EKEYREJECTED, bc_state_write_str(dev, key, "val"));
|
||||
}
|
||||
|
||||
/* key too long */
|
||||
strcpy(long_key, "1234567890123456789012345678901");
|
||||
ut_asserteq(-EKEYREJECTED, bc_state_write_str(dev, long_key, "val"));
|
||||
long_key[30] = '\0';
|
||||
ut_assertok(bc_state_write_str(dev, long_key, "val"));
|
||||
|
||||
/* value too long */
|
||||
abuf_init(&buf);
|
||||
ut_asserteq(true, abuf_realloc(&buf, 0x1002));
|
||||
data = buf.data;
|
||||
memset(data, 'x', 0x1001);
|
||||
data[0x1001] = '\0';
|
||||
ut_asserteq(-E2BIG, bc_state_write_str(dev, "try", data));
|
||||
data[0x1000] = '\0';
|
||||
ut_assertok(bc_state_write_str(dev, "try", data));
|
||||
abuf_uninit(&buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
BOOTCTL_TEST(bootctl_simple_state_limits, UTF_DM | UTF_SCAN_FDT);
|
||||
|
||||
/* test integers */
|
||||
static int bootctl_simple_state_int(struct unit_test_state *uts)
|
||||
{
|
||||
struct udevice *dev;
|
||||
long ival;
|
||||
|
||||
ut_assertok(bootctl_get_dev(UCLASS_BOOTCTL_STATE, &dev));
|
||||
|
||||
/* basic integers */
|
||||
ut_assertok(bc_state_write_int(dev, "val", 0));
|
||||
ut_assertok(bc_state_read_int(dev, "val", &ival));
|
||||
ut_asserteq(0, ival);
|
||||
|
||||
ut_assertok(bc_state_write_int(dev, "val", 1));
|
||||
ut_assertok(bc_state_read_int(dev, "val", &ival));
|
||||
ut_asserteq(1, ival);
|
||||
|
||||
ut_assertok(bc_state_write_int(dev, "val", -1));
|
||||
ut_assertok(bc_state_read_int(dev, "val", &ival));
|
||||
ut_asserteq(-1, ival);
|
||||
|
||||
/* large ints */
|
||||
ut_assertok(bc_state_write_int(dev, "val", 0xffffffffl));
|
||||
ut_assertok(bc_state_read_int(dev, "val", &ival));
|
||||
ut_asserteq(0xffffffffl, ival);
|
||||
|
||||
ut_assertok(bc_state_write_int(dev, "val", -0xffffffffl));
|
||||
ut_assertok(bc_state_read_int(dev, "val", &ival));
|
||||
ut_asserteq_64(-0xffffffffl, ival);
|
||||
|
||||
ut_assertok(bc_state_write_int(dev, "val", 0x7fffffffffffffffll));
|
||||
ut_assertok(bc_state_read_int(dev, "val", &ival));
|
||||
ut_asserteq_64(0x7fffffffffffffffll, ival);
|
||||
|
||||
ut_assertok(bc_state_write_int(dev, "val", -0x7fffffffffffffffll));
|
||||
ut_assertok(bc_state_read_int(dev, "val", &ival));
|
||||
ut_asserteq_64(-0x7fffffffffffffffll, ival);
|
||||
|
||||
return 0;
|
||||
}
|
||||
BOOTCTL_TEST(bootctl_simple_state_int, UTF_DM | UTF_SCAN_FDT);
|
||||
|
||||
/* test measurement */
|
||||
static int bootctl_simple_measure(struct unit_test_state *uts)
|
||||
{
|
||||
struct bootflow_img *img[3];
|
||||
struct osinfo osinfo;
|
||||
struct bootflow *bflow = &osinfo.bflow;
|
||||
const struct measure_info *info;
|
||||
struct udevice *dev;
|
||||
struct alist result;
|
||||
|
||||
ut_assertok(bootctl_get_dev(UCLASS_BOOTCTL_MEASURE, &dev));
|
||||
|
||||
ut_assertok(bc_measure_start(dev));
|
||||
|
||||
/* set up some data */
|
||||
memset(&osinfo, '\0', sizeof(struct osinfo));
|
||||
alist_init_struct(&bflow->images, struct bootflow_img);
|
||||
|
||||
/* add a few images */
|
||||
img[0] = bootflow_img_add(bflow, "kernel",
|
||||
(enum bootflow_img_t)IH_TYPE_KERNEL, 0,
|
||||
0x100);
|
||||
ut_assertnonnull(img);
|
||||
img[1] = bootflow_img_add(bflow, "initrd",
|
||||
(enum bootflow_img_t)IH_TYPE_RAMDISK, 0x100,
|
||||
0x200);
|
||||
ut_assertnonnull(img);
|
||||
|
||||
/* the fdt is missing so this should fail */
|
||||
ut_asserteq(-ENOENT, bc_measure_process(dev, &osinfo, &result));
|
||||
if (IS_ENABLED(CONFIG_LOGF_FUNC))
|
||||
ut_assert_nextline(" simple_process() Missing image 'flat_dt'");
|
||||
else
|
||||
ut_assert_nextline("Missing image 'flat_dt'");
|
||||
ut_assert_console_end();
|
||||
|
||||
alist_uninit(&result);
|
||||
|
||||
img[2] = bootflow_img_add(bflow, "fdt",
|
||||
(enum bootflow_img_t)IH_TYPE_FLATDT, 0x300,
|
||||
0x30);
|
||||
ut_assertok(bc_measure_process(dev, &osinfo, &result));
|
||||
|
||||
/* check the result */
|
||||
ut_asserteq(3, result.count);
|
||||
info = alist_get(&result, 0, struct measure_info);
|
||||
ut_asserteq_ptr(img[0], info[0].img);
|
||||
ut_asserteq_ptr(img[1], info[1].img);
|
||||
ut_asserteq_ptr(img[2], info[2].img);
|
||||
|
||||
/* TODO: We should also a) read out the TPM log and b) check TPM PCRs */
|
||||
|
||||
ut_assertnonnull(img);
|
||||
|
||||
return 0;
|
||||
}
|
||||
BOOTCTL_TEST(bootctl_simple_measure, UTF_DM | UTF_SCAN_FDT | UTF_CONSOLE);
|
||||
14
test/boot/bootctl/bootctl_common.h
Normal file
14
test/boot/bootctl/bootctl_common.h
Normal file
@@ -0,0 +1,14 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Common definitions for bootctl tests
|
||||
*
|
||||
* Copyright 2025 Canonical Ltd
|
||||
* Written by Simon Glass <simon.glass@canonical.com>
|
||||
*/
|
||||
|
||||
#ifndef __bootctl_common_h
|
||||
#define __bootctl_common_h
|
||||
|
||||
#define BOOTCTL_TEST(_name, _flags) UNIT_TEST(_name, _flags, bootctl)
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user