// 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 */ #include #include #include #include #include #include #include "bootctl_common.h" #include #include #include #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);