mirror of
https://github.com/clearlinux/clr-boot-manager.git
synced 2026-09-07 14:11:36 +00:00
gummiboot hasn't been supported upstream or by Clear Linux for an age, and due to format bumps no compatibility is required with it, thus it can be completely removed from source. goofiboot, the fork of gummiboot, was initially created to address concerns in systemd-boot in their tooling. However, the concerns were mitigated by opting to entirely bypass their tooling, and clr-boot-manager absorbed all safety and distribution support that was part of libnica and goofiboot. At this point, both of the legacy bootloaders are safe to be nuked from orbit and are no longer used in CBM-using distributions. Signed-off-by: Ikey Doherty <ikey@solus-project.com>
470 lines
15 KiB
C
470 lines
15 KiB
C
/*
|
|
* This file is part of clr-boot-manager.
|
|
*
|
|
* This test suite is designed to resolve issue #54:
|
|
* https://github.com/ikeydoherty/clr-boot-manager/issues/54
|
|
*
|
|
* The basic logic is, create a suitable test environment, and then have
|
|
* CBM initialise a BootManager for the root, which will fall through:
|
|
*
|
|
* - cbm_inspect_root
|
|
* - boot_manager_select_bootloader
|
|
*
|
|
* We know the update logic works based on the other tests. Here, we are
|
|
* trying to ascertain that the correct bootloader is selected in every case
|
|
* for the given system configuration.
|
|
*
|
|
* Copyright © 2016-2017 Intel Corporation
|
|
*
|
|
* clr-boot-manager is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public License as
|
|
* published by the Free Software Foundation; either version 2.1
|
|
* of the License, or (at your option) any later version.
|
|
*/
|
|
|
|
#define _GNU_SOURCE
|
|
#include <check.h>
|
|
#include <errno.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <sys/sysmacros.h>
|
|
|
|
#include "bootloader.h"
|
|
#include "bootman.h"
|
|
#define _BOOTMAN_INTERNAL_
|
|
#include "bootman_private.h"
|
|
#undef _BOOTMAN_INTERNAL_
|
|
#include "config.h"
|
|
#include "files.h"
|
|
#include "log.h"
|
|
#include "nica/array.h"
|
|
#include "nica/files.h"
|
|
#include "util.h"
|
|
#include "writer.h"
|
|
|
|
#include "blkid-harness.h"
|
|
#include "harness.h"
|
|
#include "system-harness.h"
|
|
|
|
/**
|
|
* Define a single name for the UEFI bootloader to reduce complexity further down
|
|
*/
|
|
#if defined(HAVE_SHIM_SYSTEMD_BOOT)
|
|
#define UEFI_BOOTLOADER_NAME "shim-systemd"
|
|
#elif defined(HAVE_SYSTEMD_BOOT)
|
|
#define UEFI_BOOTLOADER_NAME "systemd"
|
|
#endif
|
|
|
|
#define PLAYGROUND_ROOT TOP_BUILD_DIR "/tests/update_playground"
|
|
|
|
/**
|
|
* Restore the default testing vtables
|
|
*/
|
|
static void bootman_select_set_default_vtables(void)
|
|
{
|
|
cbm_blkid_set_vtable(&BlkidTestOps);
|
|
cbm_system_set_vtable(&SystemTestOps);
|
|
}
|
|
|
|
/**
|
|
* Simple assertion that the bootloader is the one we've asked for
|
|
*/
|
|
static void ensure_bootloader_is(BootManager *manager, const char *expected)
|
|
{
|
|
fail_if(manager == NULL, "No BootManager");
|
|
fail_if(!manager->bootloader, "No bootloader is selected. Expected %s", expected);
|
|
const char *name = manager->bootloader->name;
|
|
fail_if(!streq(name, expected), "Expected bootloader '%s', got '%s'", expected, name);
|
|
}
|
|
|
|
/**
|
|
* Harness helps us out by creating the required "bits" within the prepared root.
|
|
* This method will forcibly break that logic by making the root device go away
|
|
*
|
|
* The blkid tests themselves will still work fine as blkid has been encapsulated
|
|
* already.
|
|
*/
|
|
static void nuke_boot_device(struct PlaygroundConfig *config)
|
|
{
|
|
if (config->uefi) {
|
|
fail_if(!nc_rm_rf(PLAYGROUND_ROOT "/sys/firmware/efi/efivars"),
|
|
"Failed to remove efivars");
|
|
}
|
|
fail_if(!nc_rm_rf(PLAYGROUND_ROOT "/dev"), "Failed to remove /dev");
|
|
}
|
|
|
|
/**
|
|
* ############ BEGIN UEFI TESTS #################
|
|
*/
|
|
|
|
/**
|
|
* We're operating in native mode (!image) with UEFI, and a boot device
|
|
*/
|
|
START_TEST(bootman_select_uefi_native_with_boot)
|
|
{
|
|
static PlaygroundConfig config = { "4.2.1-121.kvm", NULL, 0, .uefi = true };
|
|
autofree(BootManager) *m = NULL;
|
|
bootman_select_set_default_vtables();
|
|
|
|
m = prepare_playground(&config);
|
|
ensure_bootloader_is(m, UEFI_BOOTLOADER_NAME);
|
|
}
|
|
END_TEST
|
|
|
|
/**
|
|
* We're operating in native mode (!image) with UEFI, and NO boot device
|
|
*/
|
|
START_TEST(bootman_select_uefi_native_without_boot)
|
|
{
|
|
static PlaygroundConfig config = { "4.2.1-121.kvm", NULL, 0, .uefi = true };
|
|
autofree(BootManager) *m = NULL;
|
|
bootman_select_set_default_vtables();
|
|
|
|
/* Reinit the boot manager with no supporting boot device */
|
|
m = prepare_playground(&config);
|
|
nuke_boot_device(&config);
|
|
boot_manager_set_prefix(m, PLAYGROUND_ROOT);
|
|
|
|
ensure_bootloader_is(m, UEFI_BOOTLOADER_NAME);
|
|
}
|
|
END_TEST
|
|
|
|
/**
|
|
* We're operating in image mode with UEFI, and a boot device
|
|
*/
|
|
START_TEST(bootman_select_uefi_image_with_boot)
|
|
{
|
|
static PlaygroundConfig config = { "4.2.1-121.kvm", NULL, 0, .uefi = true };
|
|
autofree(BootManager) *m = NULL;
|
|
bootman_select_set_default_vtables();
|
|
|
|
m = prepare_playground(&config);
|
|
boot_manager_set_image_mode(m, true);
|
|
boot_manager_set_prefix(m, PLAYGROUND_ROOT);
|
|
|
|
ensure_bootloader_is(m, UEFI_BOOTLOADER_NAME);
|
|
}
|
|
END_TEST
|
|
|
|
/**
|
|
* We're operating in image mode with UEFI, and NO boot device
|
|
*/
|
|
START_TEST(bootman_select_uefi_image_without_boot)
|
|
{
|
|
static PlaygroundConfig config = { "4.2.1-121.kvm", NULL, 0, .uefi = true };
|
|
autofree(BootManager) *m = NULL;
|
|
bootman_select_set_default_vtables();
|
|
|
|
/* Reinit the boot manager with no supporting boot device */
|
|
m = prepare_playground(&config);
|
|
nuke_boot_device(&config);
|
|
boot_manager_set_image_mode(m, true);
|
|
boot_manager_set_prefix(m, PLAYGROUND_ROOT);
|
|
|
|
ensure_bootloader_is(m, UEFI_BOOTLOADER_NAME);
|
|
}
|
|
END_TEST
|
|
|
|
/**
|
|
* Coerce legacy lookup
|
|
*/
|
|
static inline int legacy_devno_to_wholedisk(__cbm_unused__ dev_t dev, __cbm_unused__ char *diskname,
|
|
__cbm_unused__ size_t len,
|
|
__cbm_unused__ dev_t *diskdevno)
|
|
{
|
|
*diskdevno = makedev(8, 8);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Forces detection of GPT legacy boot partition
|
|
*/
|
|
static inline unsigned long long legacy_partition_get_flags(__cbm_unused__ blkid_partition par)
|
|
{
|
|
return (1ULL << 2);
|
|
}
|
|
|
|
/**
|
|
* Force the detection of a boot partition GPT UUID
|
|
*/
|
|
static inline const char *legacy_partition_get_uuid(__cbm_unused__ blkid_partition par)
|
|
{
|
|
return DEFAULT_PART_UUID;
|
|
}
|
|
|
|
/**
|
|
* This will force the tests to use the legacy detection codepaths
|
|
*/
|
|
static CbmBlkidOps legacy_blkid_ops = {
|
|
.probe_new_from_filename = test_blkid_new_probe_from_filename,
|
|
.probe_enable_superblocks = test_blkid_probe_enable_superblocks,
|
|
.probe_set_superblocks_flags = test_blkid_probe_set_superblocks_flags,
|
|
.probe_enable_partitions = test_blkid_probe_enable_partitions,
|
|
.probe_set_partitions_flags = test_blkid_probe_set_partitions_flags,
|
|
.probe_lookup_value = test_blkid_probe_lookup_value,
|
|
.do_safeprobe = test_blkid_do_safeprobe,
|
|
.free_probe = test_blkid_free_probe,
|
|
.probe_get_partitions = test_blkid_probe_get_partitions,
|
|
.partlist_numof_partitions = test_blkid_partlist_numof_partitions,
|
|
.partlist_get_partition = test_blkid_partlist_get_partition,
|
|
.partition_get_flags = legacy_partition_get_flags,
|
|
.partition_get_uuid = legacy_partition_get_uuid,
|
|
.partlist_get_table = test_blkid_partlist_get_table,
|
|
.parttable_get_type = test_blkid_parttable_get_type,
|
|
.devno_to_wholedisk = legacy_devno_to_wholedisk,
|
|
};
|
|
|
|
static void bootman_select_set_legacy_vtables(void)
|
|
{
|
|
/* override test ops for legacy testing */
|
|
cbm_blkid_set_vtable(&legacy_blkid_ops);
|
|
cbm_system_set_vtable(&SystemTestOps);
|
|
}
|
|
|
|
/**
|
|
* ############ BEGIN SYSLINUX TESTS #################
|
|
*/
|
|
|
|
/**
|
|
* We're operating in native mode (!image) with a legacy device available
|
|
*
|
|
* syslinux mode will only be activated when we find a legacy device through
|
|
* blkid inspection of the root device. Thus we *must* have a boot device,
|
|
* and the other tests will ensure syslinux isn't detected.
|
|
*/
|
|
START_TEST(bootman_select_syslinux_native_with_boot)
|
|
{
|
|
PlaygroundConfig config = { "4.2.1-121.kvm", NULL, 0, .uefi = false };
|
|
autofree(BootManager) *m = NULL;
|
|
bootman_select_set_legacy_vtables();
|
|
|
|
m = prepare_playground(&config);
|
|
ensure_bootloader_is(m, "syslinux");
|
|
}
|
|
END_TEST
|
|
|
|
/**
|
|
* We're operating in image mode with a legacy device available
|
|
*/
|
|
START_TEST(bootman_select_syslinux_image_with_boot)
|
|
{
|
|
PlaygroundConfig config = { "4.2.1-121.kvm", NULL, 0, .uefi = false };
|
|
autofree(BootManager) *m = NULL;
|
|
bootman_select_set_legacy_vtables();
|
|
|
|
m = prepare_playground(&config);
|
|
boot_manager_set_image_mode(m, true);
|
|
boot_manager_set_prefix(m, PLAYGROUND_ROOT);
|
|
|
|
ensure_bootloader_is(m, "syslinux");
|
|
}
|
|
END_TEST
|
|
|
|
/**
|
|
* ############ BEGIN GRUB2 TESTS #################
|
|
*/
|
|
|
|
/**
|
|
* We only permit UUID in our tests.
|
|
*/
|
|
static int grub2_blkid_probe_lookup_value(__cbm_unused__ blkid_probe pr, const char *name,
|
|
const char **data, size_t *len)
|
|
{
|
|
if (!name || !data) {
|
|
return -1;
|
|
}
|
|
if (streq(name, "UUID")) {
|
|
*data = DEFAULT_UUID;
|
|
} else {
|
|
return -1;
|
|
}
|
|
if (!*data) {
|
|
abort();
|
|
}
|
|
if (len) {
|
|
*len = strlen(*data);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Force blkid ops to work for GRUB2 (UUID/MBR system, no PartUUID)
|
|
*/
|
|
static CbmBlkidOps grub2_blkid_ops = {
|
|
.probe_new_from_filename = test_blkid_new_probe_from_filename,
|
|
.probe_enable_superblocks = test_blkid_probe_enable_superblocks,
|
|
.probe_set_superblocks_flags = test_blkid_probe_set_superblocks_flags,
|
|
.probe_enable_partitions = test_blkid_probe_enable_partitions,
|
|
.probe_set_partitions_flags = test_blkid_probe_set_partitions_flags,
|
|
.probe_lookup_value = grub2_blkid_probe_lookup_value,
|
|
.do_safeprobe = test_blkid_do_safeprobe,
|
|
.free_probe = test_blkid_free_probe,
|
|
.probe_get_partitions = test_blkid_probe_get_partitions,
|
|
.partlist_numof_partitions = test_blkid_partlist_numof_partitions,
|
|
.partlist_get_partition = test_blkid_partlist_get_partition,
|
|
.partition_get_flags = test_blkid_partition_get_flags,
|
|
.partition_get_uuid = test_blkid_partition_get_uuid,
|
|
.partlist_get_table = test_blkid_partlist_get_table,
|
|
.parttable_get_type = test_blkid_parttable_get_type,
|
|
.devno_to_wholedisk = test_blkid_devno_to_wholedisk,
|
|
};
|
|
|
|
static void bootman_select_set_grub2_vtables(void)
|
|
{
|
|
/* override test ops for legacy testing */
|
|
cbm_blkid_set_vtable(&grub2_blkid_ops);
|
|
cbm_system_set_vtable(&SystemTestOps);
|
|
}
|
|
|
|
/**
|
|
* We're operating in native mode (!image) with GRUB2, and NO boot device
|
|
* We ONLY support GRUB2 in "native" mode, i.e. via chroot or actually native.
|
|
*/
|
|
START_TEST(bootman_select_grub2_native_without_boot)
|
|
{
|
|
static PlaygroundConfig config = { "4.2.1-121.kvm", NULL, 0, .uefi = false };
|
|
autofree(BootManager) *m = NULL;
|
|
bootman_select_set_grub2_vtables();
|
|
|
|
/* Reinit the boot manager with no supporting boot device */
|
|
m = prepare_playground(&config);
|
|
nuke_boot_device(&config);
|
|
boot_manager_set_prefix(m, PLAYGROUND_ROOT);
|
|
|
|
ensure_bootloader_is(m, "grub2");
|
|
}
|
|
END_TEST
|
|
|
|
/**
|
|
* ############ BEGIN EDGE CASE TESTS #################
|
|
*/
|
|
|
|
/**
|
|
* We have a GPT disk that contains a legacy partition, but we're in native
|
|
* mode with UEFI available
|
|
* We should select UEFI bootloader when /sys/firmware/efi exists
|
|
*/
|
|
START_TEST(bootman_select_edge_uefi_with_legacy_part_native)
|
|
{
|
|
static PlaygroundConfig config = { "4.2.1-121.kvm", NULL, 0, .uefi = false };
|
|
autofree(BootManager) *m = NULL;
|
|
bootman_select_set_legacy_vtables();
|
|
|
|
/* At this point we would've selected syslinux */
|
|
m = prepare_playground(&config);
|
|
ensure_bootloader_is(m, "syslinux");
|
|
|
|
/* Construct /sys/firmware/efi. Now we need it to be UEFI bootloader */
|
|
fail_if(!nc_mkdir_p(PLAYGROUND_ROOT "/sys/firmware/efi", 00755), "Failed to emulate EFI");
|
|
boot_manager_set_prefix(m, PLAYGROUND_ROOT);
|
|
ensure_bootloader_is(m, UEFI_BOOTLOADER_NAME);
|
|
}
|
|
END_TEST
|
|
|
|
/**
|
|
* We have a GPT disk that contains a legacy partition, but we're in image
|
|
* mode with UEFI available
|
|
* We should only ever select syslinux
|
|
*/
|
|
START_TEST(bootman_select_edge_uefi_with_legacy_part_image)
|
|
{
|
|
static PlaygroundConfig config = { "4.2.1-121.kvm", NULL, 0, .uefi = false };
|
|
autofree(BootManager) *m = NULL;
|
|
bootman_select_set_legacy_vtables();
|
|
|
|
/* At this point we would've selected syslinux */
|
|
m = prepare_playground(&config);
|
|
ensure_bootloader_is(m, "syslinux");
|
|
|
|
/* Ensure it's still syslinux in image mode */
|
|
boot_manager_set_image_mode(m, true);
|
|
boot_manager_set_prefix(m, PLAYGROUND_ROOT);
|
|
ensure_bootloader_is(m, "syslinux");
|
|
|
|
/* Construct /sys/firmware/efi. */
|
|
fail_if(!nc_mkdir_p(PLAYGROUND_ROOT "/sys/firmware/efi", 00755), "Failed to emulate EFI");
|
|
boot_manager_set_prefix(m, PLAYGROUND_ROOT);
|
|
|
|
/* We're in image mode, the firmware is not relevant */
|
|
ensure_bootloader_is(m, "syslinux");
|
|
}
|
|
END_TEST
|
|
|
|
static Suite *core_suite(void)
|
|
{
|
|
Suite *s = NULL;
|
|
TCase *tc = NULL;
|
|
|
|
s = suite_create("bootman_select");
|
|
|
|
/* UEFI tests */
|
|
tc = tcase_create("bootman_select_uefi_functions");
|
|
tcase_add_test(tc, bootman_select_uefi_native_with_boot);
|
|
tcase_add_test(tc, bootman_select_uefi_native_without_boot);
|
|
tcase_add_test(tc, bootman_select_uefi_image_with_boot);
|
|
tcase_add_test(tc, bootman_select_uefi_image_without_boot);
|
|
suite_add_tcase(s, tc);
|
|
|
|
/* syslinux tests */
|
|
tc = tcase_create("bootman_select_syslinux_functions");
|
|
tcase_add_test(tc, bootman_select_syslinux_native_with_boot);
|
|
tcase_add_test(tc, bootman_select_syslinux_image_with_boot);
|
|
suite_add_tcase(s, tc);
|
|
|
|
/* grub2 tests */
|
|
tc = tcase_create("bootman_select_grub2_functions");
|
|
tcase_add_test(tc, bootman_select_grub2_native_without_boot);
|
|
suite_add_tcase(s, tc);
|
|
|
|
/* edge cases */
|
|
tc = tcase_create("Bootman_select_edge_cases");
|
|
tcase_add_test(tc, bootman_select_edge_uefi_with_legacy_part_native);
|
|
tcase_add_test(tc, bootman_select_edge_uefi_with_legacy_part_image);
|
|
suite_add_tcase(s, tc);
|
|
|
|
return s;
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
Suite *s;
|
|
SRunner *sr;
|
|
int fail;
|
|
|
|
/* syncing can be problematic during test suite runs */
|
|
cbm_set_sync_filesystems(false);
|
|
|
|
/* Ensure that logging is set up properly. */
|
|
setenv("CBM_DEBUG", "1", 1);
|
|
cbm_log_init(stderr);
|
|
|
|
/* Turn off the EFI variable manipulation. */
|
|
setenv("CBM_BOOTVAR_TEST_MODE", "yes", 1);
|
|
|
|
s = core_suite();
|
|
sr = srunner_create(s);
|
|
srunner_run_all(sr, CK_VERBOSE);
|
|
fail = srunner_ntests_failed(sr);
|
|
srunner_free(sr);
|
|
|
|
if (fail > 0) {
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* Editor modelines - https://www.wireshark.org/tools/modelines.html
|
|
*
|
|
* Local variables:
|
|
* c-basic-offset: 8
|
|
* tab-width: 8
|
|
* indent-tabs-mode: nil
|
|
* End:
|
|
*
|
|
* vi: set shiftwidth=8 tabstop=8 expandtab:
|
|
* :indentSize=8:tabSize=8:noTabs=true:
|
|
*/
|