diff --git a/.travis.yml b/.travis.yml index 8e58d9a..dfd85d4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,6 +31,8 @@ addons: - lcov - llvm-3.8 - valgrind + - gnu-efi + - libefivar-dev before_install: @@ -42,7 +44,6 @@ script: - ./configure --enable-coverage && make && make check && make distcheck && make check-valgrind - clang-format-3.8 -i $(find . -name '*.[ch]') && git diff --exit-code - after_success: - cd ${TRAVIS_BUILD_DIR} - lcov --compat-libtool --directory . --capture --output-file coverage.info diff --git a/Makefile.am b/Makefile.am index b8e6e9f..55794cc 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2,73 +2,78 @@ ACLOCAL_AMFLAGS = -I m4 -EXTRA_DIST = ${top_srcdir}/README.md \ - ${top_srcdir}/LICENSE.LGPL2.1 \ - ${top_srcdir}/HACKING \ - ${top_srcdir}/findstatic.pl \ - ${top_srcdir}/tests/data/blobfile \ - ${top_srcdir}/tests/data/match \ - ${top_srcdir}/tests/data/match1 \ - ${top_srcdir}/tests/data/nomatch1 \ - ${top_srcdir}/tests/data/nomatch2 \ - ${top_srcdir}/tests/data/clear.os-release \ - ${top_srcdir}/tests/data/solus.os-release \ - ${top_srcdir}/tests/data/cmdline/comments \ - ${top_srcdir}/tests/data/cmdline/mangledmess \ - ${top_srcdir}/tests/data/cmdline/multi \ - ${top_srcdir}/tests/data/cmdline/oneline \ - ${top_srcdir}/tests/data/etc/kernel/cmdline \ - ${top_srcdir}/tests/data/etc/kernel/cmdline.d/00_empty.conf \ - ${top_srcdir}/tests/data/etc/kernel/cmdline.d/00_newline.conf \ - ${top_srcdir}/tests/data/etc/kernel/cmdline.d/10_first.conf \ - ${top_srcdir}/tests/data/etc/kernel/cmdline.d/20_second.conf \ - ${top_srcdir}/tests/data/etc/kernel/cmdline.d/30_third.conf \ - ${top_srcdir}/tests/data/cmdline_vendor_only/usr/share/kernel/cmdline.d/10_first.conf \ - ${top_srcdir}/tests/data/cmdline_vendor_only/usr/share/kernel/cmdline.d/20_second.conf \ - ${top_srcdir}/tests/data/cmdline_vendor_only/usr/share/kernel/cmdline.d/30_third.conf \ - ${top_srcdir}/tests/data/cmdline_vendor_merged/usr/share/kernel/cmdline.d/00_empty.conf \ - ${top_srcdir}/tests/data/cmdline_vendor_merged/usr/share/kernel/cmdline.d/00_newline.conf \ - ${top_srcdir}/tests/data/cmdline_vendor_merged/usr/share/kernel/cmdline.d/10_first.conf \ - ${top_srcdir}/tests/data/cmdline_vendor_merged/usr/share/kernel/cmdline.d/20_second.conf \ - ${top_srcdir}/tests/data/cmdline_vendor_merged/usr/share/kernel/cmdline.d/30_third.conf \ - ${top_srcdir}/tests/data/cmdline_vendor_merged/usr/share/kernel/cmdline.d/40_masked.conf \ - ${top_srcdir}/tests/data/cmdline_vendor_merged/etc/kernel/cmdline \ - ${top_srcdir}/tests/data/cmdline_vendor_merged/etc/kernel/cmdline.d/10_local_first.conf \ - ${top_srcdir}/tests/data/cmdline_vendor_merged/etc/kernel/cmdline.d/20_local_second.conf \ - ${top_srcdir}/tests/data/cmdline_vendor_merged/etc/kernel/cmdline.d/30_local_third.conf \ - ${top_srcdir}/tests/data/cmdline_vendor_merged/etc/kernel/cmdline.d/40_masked.conf \ - ${top_srcdir}/tests/data/gptmbr.bin \ - ${top_srcdir}/tests/data/gptmbr.bin.v2 \ - ${top_srcdir}/sgcheck.suppressions \ - ${top_srcdir}/data/clr-boot-manager-booted.service \ - ${top_srcdir}/man/clr-boot-manager.1 +EXTRA_DIST = \ + ${top_srcdir}/README.md \ + ${top_srcdir}/LICENSE.LGPL2.1 \ + ${top_srcdir}/HACKING \ + ${top_srcdir}/findstatic.pl \ + ${top_srcdir}/tests/data/blobfile \ + ${top_srcdir}/tests/data/match \ + ${top_srcdir}/tests/data/match1 \ + ${top_srcdir}/tests/data/nomatch1 \ + ${top_srcdir}/tests/data/nomatch2 \ + ${top_srcdir}/tests/data/clear.os-release \ + ${top_srcdir}/tests/data/solus.os-release \ + ${top_srcdir}/tests/data/cmdline/comments \ + ${top_srcdir}/tests/data/cmdline/mangledmess \ + ${top_srcdir}/tests/data/cmdline/multi \ + ${top_srcdir}/tests/data/cmdline/oneline \ + ${top_srcdir}/tests/data/etc/kernel/cmdline \ + ${top_srcdir}/tests/data/etc/kernel/cmdline.d/00_empty.conf \ + ${top_srcdir}/tests/data/etc/kernel/cmdline.d/00_newline.conf \ + ${top_srcdir}/tests/data/etc/kernel/cmdline.d/10_first.conf \ + ${top_srcdir}/tests/data/etc/kernel/cmdline.d/20_second.conf \ + ${top_srcdir}/tests/data/etc/kernel/cmdline.d/30_third.conf \ + ${top_srcdir}/tests/data/cmdline_vendor_only/usr/share/kernel/cmdline.d/10_first.conf \ + ${top_srcdir}/tests/data/cmdline_vendor_only/usr/share/kernel/cmdline.d/20_second.conf \ + ${top_srcdir}/tests/data/cmdline_vendor_only/usr/share/kernel/cmdline.d/30_third.conf \ + ${top_srcdir}/tests/data/cmdline_vendor_merged/usr/share/kernel/cmdline.d/00_empty.conf \ + ${top_srcdir}/tests/data/cmdline_vendor_merged/usr/share/kernel/cmdline.d/00_newline.conf \ + ${top_srcdir}/tests/data/cmdline_vendor_merged/usr/share/kernel/cmdline.d/10_first.conf \ + ${top_srcdir}/tests/data/cmdline_vendor_merged/usr/share/kernel/cmdline.d/20_second.conf \ + ${top_srcdir}/tests/data/cmdline_vendor_merged/usr/share/kernel/cmdline.d/30_third.conf \ + ${top_srcdir}/tests/data/cmdline_vendor_merged/usr/share/kernel/cmdline.d/40_masked.conf \ + ${top_srcdir}/tests/data/cmdline_vendor_merged/etc/kernel/cmdline \ + ${top_srcdir}/tests/data/cmdline_vendor_merged/etc/kernel/cmdline.d/10_local_first.conf \ + ${top_srcdir}/tests/data/cmdline_vendor_merged/etc/kernel/cmdline.d/20_local_second.conf \ + ${top_srcdir}/tests/data/cmdline_vendor_merged/etc/kernel/cmdline.d/30_local_third.conf \ + ${top_srcdir}/tests/data/cmdline_vendor_merged/etc/kernel/cmdline.d/40_masked.conf \ + ${top_srcdir}/tests/data/gptmbr.bin \ + ${top_srcdir}/tests/data/gptmbr.bin.v2 \ + ${top_srcdir}/sgcheck.suppressions \ + ${top_srcdir}/data/clr-boot-manager-booted.service \ + ${top_srcdir}/man/clr-boot-manager.1 NULL = CLEANFILES = DISTCHECK_CONFIGURE_FLAGS = \ - --with-systemdsystemunitdir="${distdir}/${libdir}/systemd/system" + --with-systemdsystemunitdir="${distdir}/${libdir}/systemd/system" -AM_CFLAGS = -fstack-protector -Wall -pedantic \ - -Wstrict-prototypes -Wundef -fno-common \ - -Werror-implicit-function-declaration \ - -Wformat -Wformat-security -Werror=format-security \ - -Wconversion -Wunused-variable -Wunreachable-code \ - -Wall -W -D_FORTIFY_SOURCE=2 \ - -Wno-missing-field-initializers \ - -std=c11 \ - -DSYSCONFDIR=\"$(sysconfdir)\" +AM_CFLAGS = -fstack-protector -Wall -pedantic \ + -Wstrict-prototypes -Wundef -fno-common \ + -Werror-implicit-function-declaration \ + -Wformat -Wformat-security -Werror=format-security \ + -Wconversion -Wunused-variable -Wunreachable-code \ + -Wall -W -D_FORTIFY_SOURCE=2 \ + -Wno-missing-field-initializers \ + -std=c11 \ + -DSYSCONFDIR=\"$(sysconfdir)\" \ + -D_POSIX_C_SOURCE=201112L -AM_CPPFLAGS = \ - -I $(top_srcdir)/src \ - -I $(top_srcdir)/src/bootman \ - -I $(top_srcdir)/src/cli \ - -I $(top_srcdir)/src/bootloaders \ - -I $(top_srcdir)/src/lib \ - -I $(top_srcdir)/src/libnica/src/include \ - -DTOP_DIR=\"$(abs_top_srcdir)\" \ - -DTOP_BUILD_DIR=\"$(abs_top_builddir)\" +AM_CPPFLAGS = \ + -I $(top_srcdir)/src \ + -I $(top_srcdir)/src/bootman \ + -I $(top_srcdir)/src/cli \ + -I $(top_srcdir)/src/bootloaders \ + -I $(top_srcdir)/src/lib \ + -I $(top_srcdir)/src/libnica/src/include \ + -isystem @efivar@ \ + -isystem @gnuefi@ \ + -isystem @gnuefi@/@host_cpu@ \ + -DTOP_DIR=\"$(abs_top_srcdir)\" \ + -DTOP_BUILD_DIR=\"$(abs_top_builddir)\" AUTOMAKE_OPTIONS = color-tests parallel-tests @@ -89,68 +94,76 @@ noinst_LTLIBRARIES = \ bin_PROGRAMS = clr-boot-manager -libcbm_la_SOURCES = \ - src/bootloaders/bootloader.h \ - src/bootloaders/systemd-class.h \ - src/bootloaders/systemd-class.c \ - src/bootloaders/systemd-boot.c \ - src/bootloaders/grub2.c \ - src/bootloaders/gummiboot.c \ - src/bootloaders/goofiboot.c \ - src/bootloaders/syslinux.c \ - src/bootman/bootman.h \ - src/bootman/bootman_private.h \ - src/bootman/bootman.c \ - src/bootman/kernel.c \ - src/bootman/sysconfig.c \ - src/bootman/timeout.c \ - src/bootman/update.c \ - src/lib/blkid_stub.h \ - src/lib/blkid_stub.c \ - src/lib/cmdline.h \ - src/lib/cmdline.c \ - src/lib/files.h \ - src/lib/files.c \ - src/lib/os-release.h \ - src/lib/os-release.c \ - src/lib/log.h \ - src/lib/log.c \ - src/lib/probe.h \ - src/lib/probe.c \ - src/lib/system_stub.h \ - src/lib/system_stub.c \ - src/lib/writer.h \ - src/lib/writer.c \ - src/lib/util.h \ - src/lib/util.c +SOURCE_FILES = \ + src/bootloaders/bootloader.h \ + src/bootloaders/systemd-class.h \ + src/bootloaders/systemd-class.c \ + src/bootloaders/shim-systemd.c \ + src/bootloaders/systemd-boot.c \ + src/bootloaders/grub2.c \ + src/bootloaders/gummiboot.c \ + src/bootloaders/goofiboot.c \ + src/bootloaders/syslinux.c \ + src/bootman/bootman.h \ + src/bootman/bootman_private.h \ + src/bootman/bootman.c \ + src/bootman/kernel.c \ + src/bootman/sysconfig.c \ + src/bootman/timeout.c \ + src/bootman/update.c \ + src/lib/blkid_stub.h \ + src/lib/blkid_stub.c \ + src/lib/cmdline.h \ + src/lib/cmdline.c \ + src/lib/files.h \ + src/lib/files.c \ + src/lib/os-release.h \ + src/lib/os-release.c \ + src/lib/log.h \ + src/lib/log.c \ + src/lib/probe.h \ + src/lib/probe.c \ + src/lib/system_stub.h \ + src/lib/system_stub.c \ + src/lib/writer.h \ + src/lib/writer.c \ + src/lib/util.h \ + src/lib/util.c \ + src/lib/bootvar.h \ + src/lib/bootvar.c -libcbm_la_CFLAGS = \ - -D_BOOTMAN_INTERNAL_ \ - $(BLKID_CFLAGS) \ - $(AM_CFLAGS) +libcbm_la_SOURCES = \ + $(SOURCE_FILES) -libcbm_la_LIBADD = \ - src/libnica/libnica.la +libcbm_la_CFLAGS = \ + -D_BOOTMAN_INTERNAL_ \ + $(BLKID_CFLAGS) \ + $(AM_CFLAGS) -clr_boot_manager_SOURCES = \ - src/cli/cli.h \ - src/cli/cli.c \ - src/cli/ops/update.h \ - src/cli/ops/update.c \ - src/cli/ops/timeout.h \ - src/cli/ops/timeout.c \ - src/cli/ops/report_booted.h \ - src/cli/ops/report_booted.c \ - src/cli/main.c +libcbm_la_LIBADD = \ + src/libnica/libnica.la -clr_boot_manager_CFLAGS = \ - $(BLKID_CFLAGS) \ - $(AM_CFLAGS) +clr_boot_manager_SOURCES = \ + src/cli/cli.h \ + src/cli/cli.c \ + src/cli/ops/update.h \ + src/cli/ops/update.c \ + src/cli/ops/timeout.h \ + src/cli/ops/timeout.c \ + src/cli/ops/report_booted.h \ + src/cli/ops/report_booted.c \ + src/cli/main.c -clr_boot_manager_LDADD = \ - libcbm.la \ - src/libnica/libnica.la \ - $(BLKID_LIBS) +clr_boot_manager_CFLAGS = \ + $(BLKID_CFLAGS) \ + $(AM_CFLAGS) + +clr_boot_manager_LDADD = \ + libcbm.la \ + src/libnica/libnica.la \ + -lefiboot \ + -lefivar \ + $(BLKID_LIBS) install-exec-hook: perl ${top_srcdir}/findstatic.pl ${top_builddir}/src/*.o ${top_builddir}/src/*/*.o | grep -v Checking ||: @@ -162,180 +175,196 @@ dist_systemdsystemunit_DATA = ${top_srcdir}/data/clr-boot-manager-booted.service endif manpagesdir = $(mandir)/man1 -manpages_DATA = \ - man/clr-boot-manager.1 +manpages_DATA = \ + man/clr-boot-manager.1 distclean-local: rm -rf ${top_builddir}/tests/update_playground -TESTS = \ - check_core \ - check_cmdline \ - check_files \ - check_legacy \ - check_os_release \ - check_uefi \ - check_grub2 \ - check_select_bootloader \ - check_probe +TESTS = \ + check_core \ + check_cmdline \ + check_files \ + check_legacy \ + check_os_release \ + check_uefi \ + check_grub2 \ + check_select_bootloader \ + check_probe check_PROGRAMS = $(TESTS) -check_core_SOURCES = \ - tests/check-core.c \ - tests/blkid-harness.h \ - tests/system-harness.h \ - tests/harness.h \ - tests/harness.c +check_core_SOURCES = \ + tests/check-core.c \ + tests/blkid-harness.h \ + tests/system-harness.h \ + tests/harness.h \ + tests/harness.c -check_core_CFLAGS = \ - $(BLKID_CFLAGS) \ - $(CHECK_CFLAGS) \ - $(AM_CFLAGS) +check_core_CFLAGS = \ + $(BLKID_CFLAGS) \ + $(CHECK_CFLAGS) \ + $(AM_CFLAGS) -check_core_LDADD = \ - libcbm.la \ - src/libnica/libnica.la \ - $(BLKID_LIBS) \ - $(CHECK_LIBS) +check_core_LDADD = \ + libcbm.la \ + src/libnica/libnica.la \ + -lefiboot \ + -lefivar \ + $(BLKID_LIBS) \ + $(CHECK_LIBS) -check_cmdline_SOURCES = \ - tests/check-cmdline.c +check_cmdline_SOURCES = \ + tests/check-cmdline.c -check_cmdline_CFLAGS = \ - $(BLKID_CFLAGS) \ - $(CHECK_CFLAGS) \ - $(AM_CFLAGS) +check_cmdline_CFLAGS = \ + $(BLKID_CFLAGS) \ + $(CHECK_CFLAGS) \ + $(AM_CFLAGS) -check_cmdline_LDADD = \ - libcbm.la \ - src/libnica/libnica.la \ - $(BLKID_LIBS) \ - $(CHECK_LIBS) +check_cmdline_LDADD = \ + libcbm.la \ + src/libnica/libnica.la \ + -lefiboot \ + -lefivar \ + $(BLKID_LIBS) \ + $(CHECK_LIBS) -check_files_SOURCES = \ - tests/check-files.c \ - tests/harness.h \ - tests/harness.c +check_files_SOURCES = \ + tests/check-files.c \ + tests/harness.h \ + tests/harness.c -check_files_CFLAGS = \ - $(BLKID_CFLAGS) \ - $(CHECK_CFLAGS) \ - $(AM_CFLAGS) +check_files_CFLAGS = \ + $(BLKID_CFLAGS) \ + $(CHECK_CFLAGS) \ + $(AM_CFLAGS) -check_files_LDADD = \ - libcbm.la \ - src/libnica/libnica.la \ - $(BLKID_LIBS) \ - $(CHECK_LIBS) +check_files_LDADD = \ + libcbm.la \ + src/libnica/libnica.la \ + -lefiboot \ + -lefivar \ + $(BLKID_LIBS) \ + $(CHECK_LIBS) -check_legacy_SOURCES = \ - tests/check-legacy.c \ - tests/blkid-harness.h \ - tests/system-harness.h \ - tests/harness.h \ - tests/harness.c +check_legacy_SOURCES = \ + tests/check-legacy.c \ + tests/blkid-harness.h \ + tests/system-harness.h \ + tests/harness.h \ + tests/harness.c -check_legacy_CFLAGS = \ - $(BLKID_CFLAGS) \ - $(CHECK_CFLAGS) \ - $(AM_CFLAGS) +check_legacy_CFLAGS = \ + $(BLKID_CFLAGS) \ + $(CHECK_CFLAGS) \ + $(AM_CFLAGS) -check_legacy_LDADD = \ - libcbm.la \ - src/libnica/libnica.la \ - $(BLKID_LIBS) \ - $(CHECK_LIBS) +check_legacy_LDADD = \ + libcbm.la \ + src/libnica/libnica.la \ + -lefiboot \ + -lefivar \ + $(BLKID_LIBS) \ + $(CHECK_LIBS) -check_os_release_SOURCES = \ - tests/check-os-release.c +check_os_release_SOURCES = \ + tests/check-os-release.c -check_os_release_CFLAGS = \ - $(BLKID_CFLAGS) \ - $(CHECK_CFLAGS) \ - $(AM_CFLAGS) +check_os_release_CFLAGS = \ + $(BLKID_CFLAGS) \ + $(CHECK_CFLAGS) \ + $(AM_CFLAGS) -check_os_release_LDADD = \ - libcbm.la \ - src/libnica/libnica.la \ - $(BLKID_LIBS) \ - $(CHECK_LIBS) +check_os_release_LDADD = \ + libcbm.la \ + src/libnica/libnica.la \ + -lefiboot \ + -lefivar \ + $(BLKID_LIBS) \ + $(CHECK_LIBS) -check_uefi_SOURCES = \ - tests/check-uefi.c \ - tests/blkid-harness.h \ - tests/system-harness.h \ - tests/harness.h \ - tests/harness.c +check_uefi_SOURCES = \ + tests/check-uefi.c \ + tests/blkid-harness.h \ + tests/system-harness.h \ + tests/harness.h \ + tests/harness.c -check_uefi_CFLAGS = \ - $(BLKID_CFLAGS) \ - $(CHECK_CFLAGS) \ - $(AM_CFLAGS) +check_uefi_CFLAGS = \ + $(BLKID_CFLAGS) \ + $(CHECK_CFLAGS) \ + $(AM_CFLAGS) -check_uefi_LDADD = \ - libcbm.la \ - src/libnica/libnica.la \ - $(BLKID_LIBS) \ - $(CHECK_LIBS) +check_uefi_LDADD = \ + libcbm.la \ + src/libnica/libnica.la \ + -lefiboot \ + -lefivar \ + $(BLKID_LIBS) \ + $(CHECK_LIBS) -check_grub2_SOURCES = \ - tests/check-grub2.c \ - tests/blkid-harness.h \ - tests/system-harness.h \ - tests/harness.h \ - tests/harness.c +check_grub2_SOURCES = \ + tests/check-grub2.c \ + tests/blkid-harness.h \ + tests/system-harness.h \ + tests/harness.h \ + tests/harness.c -check_grub2_CFLAGS = \ - $(BLKID_CFLAGS) \ - $(CHECK_CFLAGS) \ - $(AM_CFLAGS) +check_grub2_CFLAGS = \ + $(BLKID_CFLAGS) \ + $(CHECK_CFLAGS) \ + $(AM_CFLAGS) -check_grub2_LDADD = \ - libcbm.la \ - src/libnica/libnica.la \ - $(BLKID_LIBS) \ - $(CHECK_LIBS) +check_grub2_LDADD = \ + libcbm.la \ + src/libnica/libnica.la \ + -lefiboot \ + -lefivar \ + $(BLKID_LIBS) \ + $(CHECK_LIBS) -check_select_bootloader_SOURCES = \ - tests/check-select-bootloader.c \ - tests/blkid-harness.h \ - tests/system-harness.h \ - tests/harness.h \ - tests/harness.c +check_select_bootloader_SOURCES = \ + tests/check-select-bootloader.c \ + tests/blkid-harness.h \ + tests/system-harness.h \ + tests/harness.h \ + tests/harness.c -check_select_bootloader_CFLAGS = \ - $(BLKID_CFLAGS) \ - $(CHECK_CFLAGS) \ - $(AM_CFLAGS) +check_select_bootloader_CFLAGS = \ + $(BLKID_CFLAGS) \ + $(CHECK_CFLAGS) \ + $(AM_CFLAGS) -check_select_bootloader_LDADD = \ - libcbm.la \ - src/libnica/libnica.la \ - $(BLKID_LIBS) \ - $(CHECK_LIBS) +check_select_bootloader_LDADD = \ + libcbm.la \ + src/libnica/libnica.la \ + -lefiboot \ + -lefivar \ + $(BLKID_LIBS) \ + $(CHECK_LIBS) -check_probe_SOURCES = \ - tests/check-probe.c \ - tests/blkid-harness.h \ - tests/system-harness.h \ - tests/harness.h \ - tests/harness.c +check_probe_SOURCES = \ + tests/check-probe.c \ + tests/blkid-harness.h \ + tests/system-harness.h \ + tests/harness.h \ + tests/harness.c -check_probe_CFLAGS = \ - $(BLKID_CFLAGS) \ - $(CHECK_CFLAGS) \ - $(AM_CFLAGS) - -check_probe_LDADD = \ - libcbm.la \ - src/libnica/libnica.la \ - $(BLKID_LIBS) \ - $(CHECK_LIBS) +check_probe_CFLAGS = \ + $(BLKID_CFLAGS) \ + $(CHECK_CFLAGS) \ + $(AM_CFLAGS) +check_probe_LDADD = \ + libcbm.la \ + src/libnica/libnica.la \ + -lefiboot \ + -lefivar \ + $(BLKID_LIBS) \ + $(CHECK_LIBS) @VALGRIND_CHECK_RULES@ - -VALGRIND_SUPPRESSIONS_FILES = \ +VALGRIND_SUPPRESSIONS_FILES = \ ${abs_top_srcdir}/sgcheck.suppressions diff --git a/configure.ac b/configure.ac index dcabea5..b428882 100644 --- a/configure.ac +++ b/configure.ac @@ -2,6 +2,7 @@ AC_INIT([clr-boot-manager], 1.5.5, [william.douglas@intel.com], [clr-boot-manage AM_INIT_AUTOMAKE([-Wno-portability no-dist-gzip dist-xz foreign subdir-objects]) AC_PROG_CC AC_PROG_CC_STDC +AC_CANONICAL_HOST AC_CONFIG_HEADERS([config.h]) AC_PREFIX_DEFAULT(/usr/local) AM_SILENT_RULES([yes]) @@ -42,7 +43,11 @@ AC_ARG_WITH([bootloader], AS_HELP_STRING([--with-bootloader=BOOTLOADER], if test x$bootloader = "xauto"; then AC_MSG_CHECKING([whether an EFI bootloader is available]) if test -e "/usr/lib/systemd/boot/efi"; then - bootloader="systemd-boot" + if test -e "/usr/lib/shim"; then + bootloader="shim-systemd-boot" + else + bootloader="systemd-boot" + fi elif test -e "/usr/lib/goofiboot"; then bootloader="goofiboot" # use newer goofiboot, may be dual installed @@ -50,13 +55,17 @@ if test x$bootloader = "xauto"; then bootloader="gummiboot" else AC_MSG_WARN([Cannot find a valid EFI bootloader, defaulting to systemd-boot]) - bootloader="systemd-boot" + bootloader="shim-systemd-boot" fi AC_MSG_RESULT([$bootloader]) fi # Now test the option -if test x$bootloader = "xsystemd-boot"; then +if test x$bootloader = "xshim-systemd-boot"; then + have_shim_systemdboot="yes" + AC_DEFINE([HAVE_SHIM_SYSTEMD_BOOT], [1], [Using shim-systemd-boot]) + AC_DEFINE([HAVE_SYSTEMD_BOOT], [1], [Adding systemd-boot]) +elif test x$bootloader = "xsystemd-boot"; then have_systemdboot="yes" AC_DEFINE([HAVE_SYSTEMD_BOOT], [1], [Using systemd-boot]) elif test x$bootloader = "xgummiboot"; then @@ -66,9 +75,10 @@ elif test x$bootloader = "xgoofiboot"; then have_goofiboot="yes" AC_DEFINE([HAVE_GOOFIBOOT], [1], [Using goofiboot]) else - AC_MSG_ERROR([Unknown bootloader, use: systemd-boot, gummiboot, or goofiboot]) + AC_MSG_ERROR([Unknown bootloader, use: shim-systemd-boot, systemd-boot, gummiboot, or goofiboot]) fi +AM_CONDITIONAL([HAVE_SHIM_SYSTEMD_BOOT], [test x$have_shim_systemdboot = "xyes"]) AM_CONDITIONAL([HAVE_SYSTEMD_BOOT], [test x$have_systemdboot = "xyes"]) AM_CONDITIONAL([HAVE_GUMMIBOOT], [test x$have_gummiboot = "xyes"]) AM_CONDITIONAL([HAVE_GOOFIBOOT], [test x$have_goofiboot = "xyes"]) @@ -89,6 +99,19 @@ AC_SUBST(kernelmodulesdir) AC_DEFINE_UNQUOTED(KERNEL_MODULES_DIRECTORY, "$kernelmodulesdir", [The location of kernel modules in this OS configuration]) +AC_ARG_WITH([gnu-efi], AS_HELP_STRING([--with-gnu-efi], + [the location of gnu-efi headers]), [gnuefi=${withval}], + [gnuefi="/usr/include/efi"]) +AC_SUBST(gnuefi) + +dnl DO NOT use PKG_CHECK_MODULES for efivar: it's headers do not compile with +dnl -pendantic --std=c11 with standard -I. Instead capture the directory and use +dnl -isystem (to avoid enforcement of the standards). +AC_ARG_WITH([efivar], AS_HELP_STRING([--with-efivar], + [the location of efivar headers]), [efivar=${withval}], + [efivar="/usr/include/efivar"]) +AC_SUBST(efivar) + AC_ARG_WITH([kernel-namespace], AS_HELP_STRING([--with-kernel-namespace], [the kernel path namespace to use]), [kernelnamespace=${withval}], [kernelnamespace="org.clearlinux"]) @@ -168,6 +191,8 @@ AC_MSG_RESULT([ bootloader: ${bootloader} kernel-dir: ${kerneldir} kernel-modules-dir: ${kernelmodulesdir} + gnu-efi: ${gnuefi} + efivar: ${efivar} kernel-namespace: ${kernelnamespace} kernel-conf-dir: ${kernelconfdir} vendor-kernel-conf-dir: ${vendorkernelconfdir} diff --git a/src/bootloaders/bootloader.h b/src/bootloaders/bootloader.h index 2f0ac49..7441b59 100644 --- a/src/bootloaders/bootloader.h +++ b/src/bootloaders/bootloader.h @@ -21,6 +21,7 @@ typedef bool (*boot_loader_init)(const BootManager *); typedef bool (*boot_loader_install_kernel)(const BootManager *, const Kernel *); +typedef char *(*boot_loader_get_kernel_destination)(const BootManager *); typedef bool (*boot_loader_remove_kernel)(const BootManager *, const Kernel *); typedef bool (*boot_loader_set_default_kernel)(const BootManager *, const Kernel *kernel); typedef bool (*boot_loader_needs_update)(const BootManager *); @@ -43,8 +44,10 @@ typedef enum { * Virtual BootLoader provider */ typedef struct BootLoader { - const char *name; /** + +#include "bootloader.h" +#include "bootman.h" +#include "bootvar.h" +#include "config.h" +#include "files.h" +#include "nica/files.h" +#include "systemd-class.h" +#include + +/* + * This file implements 2-stage bootloader configuration in which shim is used as + * the first stage bootloader and systemd-boot as the second stage bootloader. + * + * This implementation uses the following ESP layout: + * + * /EFI/ + * Boot/ + * BOOTX64.EFI <-- this implementation never modifies the + * default fallback loader + * + * /org.clearlinux/ + * bootloaderx64.efi <-- shim + * loaderx64.efi <-- systemd-boot bootloader + * mmx64.efi <-- MOK mgr + * fbx64.efi <-- MOK mgr + * + * kernel/ <-- kernels + * kernel-org.clearlinux..... + * ... + * + * /loader/ <-- systemd-boot config + * entries/ <-- boot menu entries + * Clear-linux...conf + * ... + * loader.conf <-- bootloader config + * + * Note that default bootloader at /EFI/Boot/BOOTX64.EFI is never modified. + * Instead, we create an EFI BootXXX variable and put it first in the BootOrder + * EFI variable. + */ + +static char *shim_systemd_get_kernel_dst(const BootManager *); +static bool shim_systemd_install_kernel(const BootManager *, const Kernel *); +static bool shim_systemd_remove_kernel(const BootManager *, const Kernel *); +static bool shim_systemd_set_default_kernel(const BootManager *, const Kernel *); +static bool shim_systemd_needs_install(const BootManager *); +static bool shim_systemd_needs_update(const BootManager *); +static bool shim_systemd_install(const BootManager *); +static bool shim_systemd_update(const BootManager *); +static bool shim_systemd_remove(const BootManager *); +static bool shim_systemd_init(const BootManager *); +static void shim_systemd_destroy(const BootManager *); +static int shim_systemd_get_capabilities(const BootManager *); + +__cbm_export__ const BootLoader + shim_systemd_bootloader = {.name = "systemd", + .init = shim_systemd_init, + .get_kernel_destination = shim_systemd_get_kernel_dst, + .install_kernel = shim_systemd_install_kernel, + .remove_kernel = shim_systemd_remove_kernel, + .set_default_kernel = shim_systemd_set_default_kernel, + .needs_install = shim_systemd_needs_install, + .needs_update = shim_systemd_needs_update, + .install = shim_systemd_install, + .update = shim_systemd_update, + .remove = shim_systemd_remove, + .destroy = shim_systemd_destroy, + .get_capabilities = shim_systemd_get_capabilities }; + +#if UINTPTR_MAX == 0xffffffffffffffff +#define EFI_SUFFIX "x64.efi" +#else +#define EFI_SUFFIX "ia32.efi" +#endif + +/* Layout entries, see the layout description at the top of the file. */ +#define SHIM_SRC_DIR "usr/lib/shim" +#define SHIM_SRC \ + SHIM_SRC_DIR \ + "/" \ + "shim" EFI_SUFFIX +#define MM_SRC \ + SHIM_SRC_DIR \ + "/" \ + "mm" EFI_SUFFIX +#define FB_SRC \ + SHIM_SRC_DIR \ + "/" \ + "fb" EFI_SUFFIX +#define SYSTEMD_SRC_DIR "usr/lib/systemd/boot/efi" +#define SYSTEMD_SRC \ + SYSTEMD_SRC_DIR \ + "/" \ + "systemd-boot" EFI_SUFFIX +#define DST_DIR "/" KERNEL_NAMESPACE +#define SHIM_DST \ + DST_DIR \ + "/" \ + "bootloader" EFI_SUFFIX +#define SYSTEMD_DST \ + DST_DIR \ + "/" \ + "loader" EFI_SUFFIX +#define KERNEL_DST_DIR DST_DIR "/kernel" +#define SYSTEMD_CONFIG_DIR "/loader" +#define SYSTEMD_CONFIG SYSTEMD_CONFIG_DIR "/loader.conf" +#define SYSTEMD_ENTRIES SYSTEMD_CONFIG_DIR "/entries" + +static char *shim_src; +static char *shim_dst_host; /* as accessible by the CMB for file ops. */ +static char *shim_dst_esp; /* absolute location of shim on the ESP. */ +static char *systemd_src; +static char *systemd_dst_host; + +extern void sd_class_set_get_kernel_destination_impl(char *(*)(const BootManager *)); + +static char *shim_systemd_get_kernel_dst(__cbm_unused__ const BootManager *manager) +{ + return strdup(KERNEL_DST_DIR); +} + +static bool shim_systemd_install_kernel(const BootManager *manager, const Kernel *kernel) +{ + return sd_class_install_kernel(manager, kernel); +} + +static bool shim_systemd_remove_kernel(const BootManager *manager, const Kernel *kernel) +{ + return sd_class_remove_kernel(manager, kernel); +} + +static bool shim_systemd_set_default_kernel(const BootManager *manager, const Kernel *kernel) +{ + /* this writes systemd config. systemd has the configuration paths + * hardcoded, hence whatever sd_class is doing is OK. */ + return sd_class_set_default_kernel(manager, kernel); +} + +static bool exists_identical(const char *path, const char *spath) +{ + if (!nc_file_exists(path)) + return false; + if (spath && !cbm_files_match(path, spath)) + return false; + return true; +} + +static bool shim_systemd_needs_install(const BootManager *manager) +{ + (void)manager; + if (!exists_identical(shim_dst_host, NULL)) + return true; + if (!exists_identical(systemd_dst_host, NULL)) + return true; + return false; +} + +static bool shim_systemd_needs_update(const BootManager *manager) +{ + (void)manager; + if (!exists_identical(shim_dst_host, shim_src)) + return true; + if (!exists_identical(systemd_dst_host, systemd_src)) + return true; + return false; +} + +static bool make_layout(const BootManager *manager) +{ + char *boot_root = boot_manager_get_boot_dir((BootManager *)manager); + char path[PATH_MAX]; + snprintf(path, PATH_MAX, "%s%s", boot_root, DST_DIR); + if (!nc_mkdir_p(path, 00755)) { + goto fail; + } + snprintf(path, PATH_MAX, "%s%s", boot_root, KERNEL_DST_DIR); + if (!nc_mkdir_p(path, 00755)) { + goto fail; + } + snprintf(path, PATH_MAX, "%s%s", boot_root, SYSTEMD_ENTRIES); + if (!nc_mkdir_p(path, 00755)) { + goto fail; + } + return true; +fail: + LOG_FATAL("Failed to make dir: %s", path); + return false; +} + +static bool shim_systemd_install(const BootManager *manager) +{ + char varname[9]; + + if (!make_layout(manager)) { + LOG_FATAL("Cannot create layout"); + return false; + } + + if (!copy_file_atomic(shim_src, shim_dst_host, 00644)) { + LOG_FATAL("Cannot copy %s to %s", shim_src, shim_dst_host); + return false; + } + if (!copy_file_atomic(systemd_src, systemd_dst_host, 00644)) { + LOG_FATAL("Cannot copy %s to %s", systemd_src, systemd_dst_host); + return false; + } + + if (bootvar_create(BOOT_DIRECTORY, shim_dst_esp, varname, 9)) { + LOG_FATAL("Cannot create EFI variable"); + return false; + } + + return true; +} + +static bool shim_systemd_update(const BootManager *manager) +{ + return shim_systemd_install(manager); +} + +static bool shim_systemd_remove(const BootManager *manager) +{ + (void)manager; + fprintf(stderr, "%s is not implemented\n", __func__); + return true; +} + +static bool shim_systemd_init(const BootManager *manager) +{ + size_t len; + char *prefix, *boot_root; + + if (bootvar_init()) + return false; + + /* init systemd-class since we're reusing it for kernel install. + * specific values do not matter as long as sd_class is not used to + * install the bootloaders themselves. */ + static BootLoaderConfig systemd_config = {.vendor_dir = "systemd", + .efi_dir = "/usr/lib/systemd/boot/efi", + .efi_blob = "systemd-boot" EFI_SUFFIX, + .name = "systemd-boot" }; + sd_class_init(manager, &systemd_config); + sd_class_set_get_kernel_destination_impl(shim_systemd_get_kernel_dst); + + prefix = strdup(boot_manager_get_prefix((BootManager *)manager)); + len = strlen(prefix); + if (len > 0 && prefix[len - 1] == '/') + prefix[len - 1] = '\0'; + shim_src = string_printf("%s/%s", prefix, SHIM_SRC); + systemd_src = string_printf("%s/%s", prefix, SYSTEMD_SRC); + + boot_root = strdup(boot_manager_get_boot_dir((BootManager *)manager)); + len = strlen(boot_root); + /* SHIM_DST and SYSTEMD_DST are defined with leading '/', take extra + * care to produce clean paths. */ + if (len > 0 && boot_root[len - 1] == '/') + boot_root[len - 1] = '\0'; + shim_dst_host = string_printf("%s%s", boot_root, SHIM_DST); + systemd_dst_host = string_printf("%s%s", boot_root, SYSTEMD_DST); + + shim_dst_esp = SHIM_DST; + + free(prefix); + free(boot_root); + + return true; +} + +static void shim_systemd_destroy(const BootManager *manager) +{ + (void)manager; + + free(shim_src); + free(systemd_src); + free(shim_dst_host); + free(systemd_dst_host); + bootvar_destroy(); + + return; +} + +static int shim_systemd_get_capabilities(const BootManager *manager) +{ + (void)manager; + return BOOTLOADER_CAP_GPT | BOOTLOADER_CAP_UEFI; +} + +/* + * 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: + */ diff --git a/src/bootloaders/systemd-class.c b/src/bootloaders/systemd-class.c index bc800ab..84d8b83 100644 --- a/src/bootloaders/systemd-class.c +++ b/src/bootloaders/systemd-class.c @@ -38,11 +38,14 @@ typedef struct SdClassConfig { char *efi_blob_dest; char *default_path_efi_blob; char *loader_config; + char *kernel_dir; } SdClassConfig; static SdClassConfig sd_class_config = { 0 }; static BootLoaderConfig *sd_config = NULL; +static char *(*get_kernel_destination_impl)(const BootManager *); + #define FREE_IF_SET(x) \ { \ if (x) { \ @@ -65,6 +68,8 @@ bool sd_class_init(const BootManager *manager, BootLoaderConfig *config) sd_config = config; + get_kernel_destination_impl = sd_class_get_kernel_destination; + /* Cache all of these to save useless allocs of the same paths later */ base_path = boot_manager_get_boot_dir((BootManager *)manager); OOM_CHECK_RET(base_path, false); @@ -112,9 +117,16 @@ bool sd_class_init(const BootManager *manager, BootLoaderConfig *config) OOM_CHECK_RET(loader_config, false); sd_class_config.loader_config = loader_config; + sd_class_config.kernel_dir = "/EFI/" KERNEL_NAMESPACE; + return true; } +void sd_class_set_get_kernel_destination_impl(char *(*impl)(const BootManager *)) +{ + get_kernel_destination_impl = impl; +} + void sd_class_destroy(__cbm_unused__ const BootManager *manager) { FREE_IF_SET(sd_class_config.efi_dir); @@ -151,7 +163,7 @@ static char *get_entry_path_for_kernel(BootManager *manager, const Kernel *kerne NULL); } -static bool sd_class_ensure_dirs(__cbm_unused__ const BootManager *manager) +static bool sd_class_ensure_dirs(void) { if (!nc_mkdir_p(sd_class_config.efi_dir, 00755)) { LOG_FATAL("Failed to create %s: %s", sd_class_config.efi_dir, strerror(errno)); @@ -174,6 +186,11 @@ static bool sd_class_ensure_dirs(__cbm_unused__ const BootManager *manager) return true; } +char *sd_class_get_kernel_destination(__cbm_unused__ const BootManager *manager) +{ + return strdup(sd_class_config.kernel_dir); +} + bool sd_class_install_kernel(const BootManager *manager, const Kernel *kernel) { if (!manager || !kernel) { @@ -187,12 +204,6 @@ bool sd_class_install_kernel(const BootManager *manager, const Kernel *kernel) conf_path = get_entry_path_for_kernel((BootManager *)manager, kernel); - /* Ensure all the relevant directories exist */ - if (!sd_class_ensure_dirs(manager)) { - LOG_FATAL("Failed to create required directories"); - return false; - } - if (!cbm_writer_open(writer)) { DECLARE_OOM(); abort(); @@ -210,14 +221,14 @@ bool sd_class_install_kernel(const BootManager *manager, const Kernel *kernel) /* Standard title + linux lines */ cbm_writer_append_printf(writer, "title %s\n", os_name); cbm_writer_append_printf(writer, - "linux /EFI/%s/%s\n", - KERNEL_NAMESPACE, + "linux %s/%s\n", + get_kernel_destination_impl(manager), kernel->target.path); /* Optional initrd */ if (kernel->target.initrd_path) { cbm_writer_append_printf(writer, - "initrd /EFI/%s/%s\n", - KERNEL_NAMESPACE, + "initrd %s/%s\n", + get_kernel_destination_impl(manager), kernel->target.initrd_path); } /* Add the root= section */ @@ -290,11 +301,6 @@ bool sd_class_set_default_kernel(const BootManager *manager, const Kernel *kerne return false; } - if (!sd_class_ensure_dirs(manager)) { - LOG_FATAL("Failed to create required directories for %s", sd_config->name); - return false; - } - autofree(char) *item_name = NULL; int timeout = 0; const char *prefix = NULL; @@ -404,7 +410,7 @@ bool sd_class_install(const BootManager *manager) return false; } - if (!sd_class_ensure_dirs(manager)) { + if (!sd_class_ensure_dirs()) { LOG_FATAL("Failed to create required directories for %s", sd_config->name); return false; } @@ -439,7 +445,7 @@ bool sd_class_update(const BootManager *manager) if (!manager) { return false; } - if (!sd_class_ensure_dirs(manager)) { + if (!sd_class_ensure_dirs()) { LOG_FATAL("Failed to create required directories for %s", sd_config->name); return false; } diff --git a/src/bootloaders/systemd-class.h b/src/bootloaders/systemd-class.h index 5a885cd..64e0ef1 100644 --- a/src/bootloaders/systemd-class.h +++ b/src/bootloaders/systemd-class.h @@ -29,6 +29,8 @@ typedef struct BootLoaderConfig { const char *name; } BootLoaderConfig; +char *sd_class_get_kernel_destination(const BootManager *manager); + bool sd_class_install_kernel(const BootManager *manager, const Kernel *kernel); bool sd_class_remove_kernel(const BootManager *manager, const Kernel *kernel); diff --git a/src/bootman/bootman.c b/src/bootman/bootman.c index d720047..5334363 100644 --- a/src/bootman/bootman.c +++ b/src/bootman/bootman.c @@ -31,6 +31,7 @@ * Total "usable" bootloaders */ extern const BootLoader grub2_bootloader; +extern const BootLoader shim_systemd_bootloader; extern const BootLoader systemd_bootloader; extern const BootLoader gummiboot_bootloader; extern const BootLoader goofiboot_bootloader; @@ -41,7 +42,9 @@ extern const BootLoader syslinux_bootloader; */ const BootLoader *bootman_known_loaders[] = { &grub2_bootloader, /**bootloader->get_capabilities(manager) & BOOTLOADER_CAP_UEFI); + autofree(char) *efi_boot_dir = + is_uefi ? manager->bootloader->get_kernel_destination(manager) : NULL; assert(manager != NULL); assert(kernel != NULL); + if (is_uefi && !efi_boot_dir) + return false; + /* Boot path */ base_path = boot_manager_get_boot_dir((BootManager *)manager); OOM_CHECK_RET(base_path, false); - /* Determine if UEFI is in use */ - if ((manager->bootloader->get_capabilities(manager) & BOOTLOADER_CAP_UEFI) == - BOOTLOADER_CAP_UEFI) { - is_uefi = true; - efi_boot_dir = nc_build_case_correct_path(base_path, "EFI", KERNEL_NAMESPACE, NULL); - } - - /* For UEFI kernels we namespace into /EFI/$NEEDLE, i.e. /EFI/org.clearlinux */ - if (is_uefi) { - /* Ensure namespace directory exists */ - if (!nc_mkdir_p(efi_boot_dir, 00755)) { - LOG_FATAL("Failed to create namespace directory: %s %s", - efi_boot_dir, - strerror(errno)); - return false; - } - kfile_target = string_printf("%s/%s", efi_boot_dir, kernel->target.path); - } else { - kfile_target = string_printf("%s/%s", base_path, kernel->target.legacy_path); - } + /* for UEFI, the kernel location is prefixed with efi_boot_dir which is + * guaranteed to start with '/' since it's its absolute path on ESP. */ + kfile_target = string_printf("%s%s/%s", + base_path, + (is_uefi ? efi_boot_dir : ""), + (is_uefi ? kernel->target.path : kernel->target.legacy_path)); /* Now copy the kernel file to it's new location */ if (!cbm_files_match(kernel->source.path, kfile_target)) { @@ -641,11 +631,10 @@ bool boot_manager_install_kernel_internal(const BootManager *manager, const Kern return true; } - if (is_uefi) { - initrd_target = string_printf("%s/%s", efi_boot_dir, kernel->target.initrd_path); - } else { - initrd_target = string_printf("%s/%s", base_path, kernel->target.initrd_path); - } + initrd_target = string_printf("%s%s/%s", + base_path, + (is_uefi ? efi_boot_dir : ""), + kernel->target.initrd_path); if (!cbm_files_match(initrd_source, initrd_target)) { if (!copy_file_atomic(initrd_source, initrd_target, 00644)) { @@ -676,33 +665,31 @@ bool boot_manager_remove_kernel_internal(const BootManager *manager, const Kerne autofree(char) *kfile_target = NULL; autofree(char) *base_path = NULL; autofree(char) *initrd_target = NULL; - bool is_uefi = false; - autofree(char) *efi_boot_dir = NULL; + int is_uefi = (manager->bootloader->get_capabilities(manager) & BOOTLOADER_CAP_UEFI); + autofree(char) *efi_boot_dir = + is_uefi ? manager->bootloader->get_kernel_destination(manager) : NULL; assert(manager != NULL); assert(kernel != NULL); + /* if it's UEFI, then bootloader->get_kernel_dst() must return a value. */ + if (is_uefi && !efi_boot_dir) + return false; + /* Boot path */ base_path = boot_manager_get_boot_dir((BootManager *)manager); OOM_CHECK_RET(base_path, false); - /* Determine if UEFI is in use */ - if ((manager->bootloader->get_capabilities(manager) & BOOTLOADER_CAP_UEFI) == - BOOTLOADER_CAP_UEFI) { - is_uefi = true; - efi_boot_dir = nc_build_case_correct_path(base_path, "EFI", KERNEL_NAMESPACE, NULL); - } - /* Remove old blobs */ - if (is_uefi) { - kfile_target = string_printf("%s/%s", efi_boot_dir, kernel->target.path); - } else { - kfile_target = string_printf("%s/%s", base_path, kernel->target.legacy_path); - } + kfile_target = string_printf("%s%s/%s", + base_path, + (is_uefi ? efi_boot_dir : ""), + (is_uefi ? kernel->target.path : kernel->target.legacy_path)); if (kernel->source.initrd_file) { - initrd_target = string_printf("%s/%s", - is_uefi ? efi_boot_dir : base_path, + initrd_target = string_printf("%s%s/%s", + base_path, + (is_uefi ? efi_boot_dir : ""), kernel->target.initrd_path); } diff --git a/src/lib/bootvar.c b/src/lib/bootvar.c new file mode 100644 index 0000000..d0c4ec5 --- /dev/null +++ b/src/lib/bootvar.c @@ -0,0 +1,456 @@ +/* + * This file is part of clr-boot-manager. + * + * Copyright © 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 "bootvar.h" + +#include +/* Workaround for using --std=c11 in CBM. Provide "relaxed" defines which efivar + * expects. */ +#define BYTE_ORDER __BYTE_ORDER +#define LITTLE_ENDIAN __LITTLE_ENDIAN +#define BIG_ENDIAN __BIG_ENDIAN + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 1K is the limit for boot var storage that efivar defines. it should be + * enough. actual space occupied is normally >2 times less. */ +#define BOOT_VAR_MAX 1024 + +/* env var to turn on the test mode. if set to "yes", turns off any side-effects + * functions defined in this file may have: the call will always succeed without + * side effects. */ +#define CBM_BOOTVAR_TEST_MODE_VAR "CBM_BOOTVAR_TEST_MODE" + +typedef struct boot_rec boot_rec_t; + +struct boot_rec { + char *name; + int num; + unsigned char *data; + boot_rec_t *next; +}; + +static boot_rec_t *boot_recs; +static int boot_recs_cnt; + +static int test_mode = 0; + +static void bootvar_free_boot_recs(void) +{ + boot_rec_t *p, *c; + c = boot_recs; + if (!c) + return; + boot_recs = NULL; + do { + p = c; + c = c->next; + free(p); + } while (c); +} + +static void bootvar_print_boot_recs(void) __attribute__((unused)); +static void bootvar_print_boot_recs(void) +{ + boot_rec_t *c = boot_recs; + if (!c) + return; + do { + fprintf(stderr, "Boot record #%d: %s\n", c->num, c->name); + } while ((c = c->next)); +} + +/* enumerates boot recs and initializes boot_recs and boot_recs_cnt. */ +static int bootvar_read_boot_recs(void) +{ + int res; + efi_guid_t *guid = NULL; + char *name = NULL; + boot_rec_t *p = NULL, *c; + int i = 0; + + bootvar_free_boot_recs(); + + while ((res = efi_get_next_variable_name(&guid, &name)) > 0) { + char *num_end; + if (strncmp(name, "Boot", 4)) + continue; + if (!isxdigit(name[4]) || !isxdigit(name[5]) || !isxdigit(name[6]) || + !isxdigit(name[7])) + continue; + if (memcmp(guid, &efi_guid_global, sizeof(efi_guid_t))) + continue; + + c = (boot_rec_t *)malloc(sizeof(boot_rec_t)); + memset(c, 0, sizeof(boot_rec_t)); + c->name = strdup(name); + c->num = (int)strtol(name + 4, &num_end, 16); + if (num_end - name - 4 != 4) + continue; + + if (!boot_recs) { + boot_recs = p = c; + } else { + p->next = c; + p = c; + } + i++; + } + if (res < 0) { + LOG_FATAL("efi_get_next_variable_name() failed: %s", strerror(errno)); + return -EBOOT_VAR_ERR; + } + boot_recs_cnt = i; + return 0; +} + +/* given the record, puts it first in the boot order (via BootOrder EFI + * variable). */ +static int bootvar_push_to_boot_order(boot_rec_t *rec) +{ + uint16_t number; + uint16_t *boot_order; + uint16_t *new_boot_order; + size_t boot_order_size; + size_t new_boot_order_size; + uint32_t boot_order_attrs; + unsigned int i; + int found = 0; + + if (!rec || !rec->name) + return -EBOOT_VAR_ERR; + + if (efi_get_variable(EFI_GLOBAL_GUID, + "BootOrder", + (uint8_t **)&boot_order, + &boot_order_size, + &boot_order_attrs)) { + LOG_FATAL("efi_get_variable() failed: %s", strerror(errno)); + return -EBOOT_VAR_ERR; + } + + sscanf(rec->name, "Boot%04hX", &number); + + /* read as uint16_t, hence twice less the returned size */ + boot_order_size >>= 1; + + for (i = 0; i < boot_order_size; i++) { + if (boot_order[i] == number) { + found = 1; + break; + } + } + + if (!found) { + new_boot_order_size = boot_order_size + 1; + new_boot_order = (uint16_t *)malloc(new_boot_order_size * sizeof(uint16_t)); + new_boot_order[0] = number; + memcpy(new_boot_order + 1, boot_order, boot_order_size * sizeof(uint16_t)); + } else { + uint16_t *c; + new_boot_order = (uint16_t *)malloc(boot_order_size * sizeof(uint16_t)); + new_boot_order[0] = number; + c = new_boot_order + 1; + for (i = 0; i < boot_order_size; i++) { + if (boot_order[i] != number) { + *c = boot_order[i]; + c++; + } + } + new_boot_order_size = (size_t)(c - new_boot_order); + } + + new_boot_order_size <<= 1; + if (efi_set_variable(EFI_GLOBAL_GUID, + "BootOrder", + (uint8_t *)new_boot_order, + new_boot_order_size, + boot_order_attrs, + 0644)) { + LOG_FATAL("efi_set_variable() failed: %s", strerror(errno)); + return -EBOOT_VAR_ERR; + } + + return 0; +} + +static int cmp(const void *a, const void *b) +{ + return *((int *)a) - *((int *)b); +} + +/* finds the first available free number for a boot var. */ +static int bootvar_find_free_no(void) +{ + int *nums; + int res; + int i = 0; + boot_rec_t *c = boot_recs; + size_t cnt; + + if (!boot_recs) + return -1; + if (!boot_recs_cnt) + return 0; /* no records. */ + + cnt = (size_t)boot_recs_cnt; + + nums = (int *)alloca(sizeof(int) * cnt); + memset(nums, 0, sizeof(int) * cnt); + + do { + nums[i] = c->num; + i++; + } while ((c = c->next)); + + qsort(nums, cnt, sizeof(int), cmp); + + for (i = 0, res = 0; i < boot_recs_cnt; i++, res++) { + if (res < nums[i]) + break; + } + if (res == nums[boot_recs_cnt - 1]) + res++; /* no gap. */ + return res; +} + +/* finds and returns boot rec whose value is data of size. NULL if not found. */ +static boot_rec_t *bootvar_find_boot_rec(uint8_t *data, size_t size) +{ + boot_rec_t *c = boot_recs; + boot_rec_t *res = NULL; + + uint8_t *cdata; + size_t csize; + uint32_t cattr; + + if (!boot_recs || !boot_recs_cnt) + return NULL; + + do { + if (efi_get_variable(EFI_GLOBAL_GUID, c->name, &cdata, &csize, &cattr) < 0) { + LOG_ERROR("efi_get_variable() failed: %s", strerror(errno)); + continue; + } + if (csize == size && !memcmp(cdata, data, csize)) { + res = c; + break; + } + } while ((c = c->next)); + + return res; +} + +typedef struct part_info { + char disk_path[PATH_MAX]; + int part_no; + char part_type[36 + 1]; /* GUID string, hyphen notation */ +} part_info_t; + +/* given the location of the ESP mount point, returns the partition information + * needed to create boot variable which points to that bootloader. */ +static int bootvar_get_part_info(const char *path, part_info_t *pi) +{ + blkid_probe probe; + blkid_partition part; + blkid_partlist parts; + struct stat st; + dev_t disk_dev; + char disk_path[PATH_MAX]; + const char *part_type; + + if (stat(path, &st)) { + LOG_FATAL("stat() failed on %s: %s", path, strerror(errno)); + return -EBOOT_VAR_ERR; + } + + strcpy(disk_path, "/dev/"); + if (blkid_devno_to_wholedisk(st.st_dev, disk_path + 5, PATH_MAX - 5, &disk_dev)) { + LOG_FATAL("blkid_devno_to_wholedisk() error"); + return -EBOOT_VAR_ERR; + } + + if (!(probe = blkid_new_probe_from_filename(disk_path))) { + LOG_FATAL("blkid_new_probe_from_filename() error"); + return -EBOOT_VAR_ERR; + } + + if (blkid_probe_enable_partitions(probe, 1)) { + LOG_FATAL("blkid_probe_enable_partitions() error"); + return -EBOOT_VAR_ERR; + } + + if (!(parts = blkid_probe_get_partitions(probe))) { + LOG_FATAL("blkid_probe_get_partitions() error"); + return -EBOOT_VAR_ERR; + } + + if (!(part = blkid_partlist_devno_to_partition(parts, st.st_dev))) { + LOG_FATAL("blkid_partlist_devno_to_partition() error"); + return -EBOOT_VAR_ERR; + } + + if ((pi->part_no = blkid_partition_get_partno(part)) < 0) { + LOG_FATAL("blkid_partition_get_partno() error"); + return -EBOOT_VAR_ERR; + } + + part_type = blkid_partition_get_type_string(part); + if (!part_type) { + LOG_FATAL("blkid_partition_get_type_string() returned NULL"); + return -EBOOT_VAR_ERR; + } else if (strlen(part_type) != 36) { + LOG_FATAL("partition type does not seem to be a GUID: %s", part_type); + return -EBOOT_VAR_ERR; + } + + snprintf(pi->disk_path, strlen(disk_path) + 1, "%s", disk_path); + snprintf(pi->part_type, 36 + 1, "%s", part_type); + + blkid_free_probe(probe); + + return 0; +} + +/* attempts to look up existing record, otherwise creates a new one. */ +static boot_rec_t *bootvar_add_boot_rec(uint8_t *data, size_t len) +{ + char name[9]; /* variable name, e.g. "BootXXXX". */ + int slot; + uint32_t attr = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS; + boot_rec_t *c, *res = bootvar_find_boot_rec(data, len); + + if (res) + return res; + /* no such record, create one. */ + slot = bootvar_find_free_no(); + if (slot < 0) + return NULL; + if (snprintf(name, 9, "Boot%04X", slot) > 8) + return NULL; + if (efi_set_variable(EFI_GLOBAL_GUID, name, data, len, attr, 0644) < 0) { + LOG_FATAL("efi_set_variable() failed: %s", strerror(errno)); + return NULL; + } + /* re-read the records and find the variable that was just created. */ + if (bootvar_read_boot_recs() < 0) + return NULL; + if (!boot_recs) + return NULL; /* something went terribly wrong. */ + c = boot_recs; + do { + if (!strcmp(c->name, name)) { + res = c; + break; + } + } while ((c = c->next)); + + return res; +} + +int bootvar_create(const char *esp_mount_path, const char *bootloader_esp_path, char *varname, + size_t size) +{ + part_info_t pi; + uint8_t fdev_path[PATH_MAX]; + uint8_t data[BOOT_VAR_MAX]; /* this is what efivar supports and it should be + enough. */ + long int len; + boot_rec_t *rec; + + if (test_mode) + return 0; + + if (bootvar_get_part_info(esp_mount_path, &pi)) + return -1; + + len = efi_generate_file_device_path_from_esp(fdev_path, + PATH_MAX, + pi.disk_path, + pi.part_no, + bootloader_esp_path, + EFIBOOT_ABBREV_HD); + if (len < 0) { + LOG_FATAL("efi_generate_file_device_path_from_esp() failed: %s", strerror(errno)); + return -EBOOT_VAR_ERR; + } + + len = efi_loadopt_create(data, + BOOT_VAR_MAX, + LOAD_OPTION_ACTIVE, + (void *)fdev_path, + len, + (unsigned char *)"Linux bootloader", + NULL, + 0); + if (len < 0) { + LOG_FATAL("efi_loadopt_create() failed: %s", strerror(errno)); + return -EBOOT_VAR_ERR; + } + + rec = bootvar_add_boot_rec(data, (size_t)len); + if (!rec) + return -EBOOT_VAR_ERR; + + if (bootvar_push_to_boot_order(rec)) + return -EBOOT_VAR_ERR; + + if (varname && size) { + size_t len = strlen(rec->name); + if (len < size) { + snprintf(varname, len + 1, "%s", rec->name); + } else { + LOG_ERROR("%lu bytes is not enough. Need %lu.", size, len); + } + } + + return 0; +} + +int bootvar_init(void) +{ + char *test_mode_env = getenv(CBM_BOOTVAR_TEST_MODE_VAR); + if (test_mode_env && !strncmp(test_mode_env, "yes", 4)) { + LOG_INFO("EFI variables support is disabled: " CBM_BOOTVAR_TEST_MODE_VAR " is set"); + test_mode = 1; + } + if (test_mode) + return 0; + if (efi_variables_supported() < 0) + return -EBOOT_VAR_NOSUP; + if (bootvar_read_boot_recs() < 0) + return -EBOOT_VAR_ERR; + return 0; +} + +void bootvar_destroy(void) +{ + if (test_mode) + return; + bootvar_free_boot_recs(); +} + +/* vim: set nosi noai cin ts=8 sw=8 et tw=80: */ diff --git a/src/lib/bootvar.h b/src/lib/bootvar.h new file mode 100644 index 0000000..bf48a62 --- /dev/null +++ b/src/lib/bootvar.h @@ -0,0 +1,21 @@ +/* + * This file is part of clr-boot-manager. + * + * Copyright © 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. + */ + +#include + +#define EBOOT_VAR_ERR 1 /* general error */ +#define EBOOT_VAR_NOSUP 127 /* EFI vars not supported */ + +int bootvar_init(void); +void bootvar_destroy(void); +int bootvar_create(const char *, const char *, char *, size_t); + +/* vim: set nosi noai cin ts=8 sw=8 et tw=80: */ diff --git a/src/lib/files.h b/src/lib/files.h index 1042b02..b8f6f05 100644 --- a/src/lib/files.h +++ b/src/lib/files.h @@ -59,9 +59,9 @@ char *get_parent_disk(char *path); char *get_legacy_boot_device(char *path); /** -* Determine if the files match in content by comparing -* their checksums -*/ + * Determine if the files match in content by comparing + * their checksums + */ bool cbm_files_match(const char *p1, const char *p2); /** diff --git a/tests/check-cmdline.c b/tests/check-cmdline.c index c048b92..3d1f45e 100644 --- a/tests/check-cmdline.c +++ b/tests/check-cmdline.c @@ -134,6 +134,9 @@ int main(void) 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); diff --git a/tests/check-core.c b/tests/check-core.c index 60c49b5..83f7cd6 100644 --- a/tests/check-core.c +++ b/tests/check-core.c @@ -430,6 +430,9 @@ int main(void) setenv("CBM_DEBUG", "1", 1); cbm_log_init(stderr); + /* Turn off the EFI variable manipulation. */ + setenv("CBM_BOOTVAR_TEST_MODE", "yes", 1); + cbm_blkid_set_vtable(&BlkidTestOps); cbm_system_set_vtable(&SystemTestOps); diff --git a/tests/check-files.c b/tests/check-files.c index 6480c23..f58fdd0 100644 --- a/tests/check-files.c +++ b/tests/check-files.c @@ -110,6 +110,9 @@ int main(void) 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); diff --git a/tests/check-grub2.c b/tests/check-grub2.c index 4868cf1..1303383 100644 --- a/tests/check-grub2.c +++ b/tests/check-grub2.c @@ -261,6 +261,9 @@ int main(void) setenv("CBM_DEBUG", "1", 1); cbm_log_init(stderr); + /* Turn off the EFI variable manipulation. */ + setenv("CBM_BOOTVAR_TEST_MODE", "yes", 1); + cbm_blkid_set_vtable(&blkid_ops); cbm_system_set_vtable(&SystemTestOps); diff --git a/tests/check-legacy.c b/tests/check-legacy.c index e780fad..e60d115 100644 --- a/tests/check-legacy.c +++ b/tests/check-legacy.c @@ -294,6 +294,9 @@ int main(void) setenv("CBM_DEBUG", "1", 1); cbm_log_init(stderr); + /* Turn off the EFI variable manipulation. */ + setenv("CBM_BOOTVAR_TEST_MODE", "yes", 1); + cbm_blkid_set_vtable(&blkid_ops); cbm_system_set_vtable(&SystemTestOps); diff --git a/tests/check-os-release.c b/tests/check-os-release.c index 954cd4c..583264e 100644 --- a/tests/check-os-release.c +++ b/tests/check-os-release.c @@ -73,6 +73,9 @@ int main(void) 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); diff --git a/tests/check-probe.c b/tests/check-probe.c index 2aea5a3..a538e53 100644 --- a/tests/check-probe.c +++ b/tests/check-probe.c @@ -241,6 +241,9 @@ int main(void) 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); diff --git a/tests/check-select-bootloader.c b/tests/check-select-bootloader.c index 96db3d9..946fc20 100644 --- a/tests/check-select-bootloader.c +++ b/tests/check-select-bootloader.c @@ -441,6 +441,9 @@ int main(void) 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); diff --git a/tests/check-uefi.c b/tests/check-uefi.c index b748012..8e09e83 100644 --- a/tests/check-uefi.c +++ b/tests/check-uefi.c @@ -453,6 +453,9 @@ int main(void) setenv("CBM_DEBUG", "1", 1); cbm_log_init(stderr); + /* Turn off the EFI variable manipulation. */ + setenv("CBM_BOOTVAR_TEST_MODE", "yes", 1); + cbm_blkid_set_vtable(&BlkidTestOps); cbm_system_set_vtable(&SystemTestOps); diff --git a/tests/harness.c b/tests/harness.c index 8321814..f8b95b9 100644 --- a/tests/harness.c +++ b/tests/harness.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -59,15 +60,20 @@ */ /** - * Systemd support + * Systemd support, including shim-systemd two-stage */ #if defined(HAVE_SYSTEMD_BOOT) +#if defined(HAVE_SHIM_SYSTEMD_BOOT) +#define ESP_BOOT_DIR BOOT_FULL "/" KERNEL_NAMESPACE +#define ESP_BOOT_STUB ESP_BOOT_DIR "/bootloader" EFI_STUB_SUFFIX_L +#define SHIM_BOOT_COPY_DIR PLAYGROUND_ROOT "/usr/lib/shim" +#else #define ESP_BOOT_DIR EFI_START "/systemd" #define ESP_BOOT_STUB ESP_BOOT_DIR "/systemd-boot" EFI_STUB_SUFFIX_L +#endif /* HAVE_SHIM_SYSTEMD_BOOT */ #define BOOT_COPY_TARGET PLAYGROUND_ROOT "/usr/lib/systemd/boot/efi/systemd-boot" EFI_STUB_SUFFIX_L #define BOOT_COPY_DIR PLAYGROUND_ROOT "/usr/lib/systemd/boot/efi" - /** * gummiboot support */ @@ -126,17 +132,21 @@ static void push_syslinux(void) void confirm_bootloader(void) { +#if !defined(HAVE_SHIM_SYSTEMD_BOOT) fail_if(!noisy_file_exists(EFI_STUB_MAIN), "Main EFI stub missing"); +#endif fail_if(!noisy_file_exists(ESP_BOOT_DIR), "ESP target directory missing"); fail_if(!noisy_file_exists(ESP_BOOT_STUB), "ESP target stub missing"); } bool confirm_bootloader_match(void) { +#if !defined(HAVE_SHIM_SYSTEMD_BOOT) if (!cbm_files_match(BOOT_COPY_TARGET, EFI_STUB_MAIN)) { fprintf(stderr, "EFI_STUB_MAIN doesn't match the source\n"); return false; } +#endif /* !HAVE_SHIM_SYSTEMD_BOOT */ if (!cbm_files_match(BOOT_COPY_TARGET, ESP_BOOT_STUB)) { fprintf(stderr, "ESP_BOOT_STUB(vendor) doesn't match the source\n"); return false; @@ -322,6 +332,24 @@ bool push_bootloader_update(int revision) fprintf(stderr, "Failed to update bootloader: %s\n", strerror(errno)); return false; } +#if defined(HAVE_SHIM_SYSTEMD_BOOT) + { + int i; + char path[PATH_MAX]; + char *files[3] = { "fb", "mm", "shim" }; + if (!nc_file_exists(SHIM_BOOT_COPY_DIR) && !nc_mkdir_p(SHIM_BOOT_COPY_DIR, 0755)) + return false; + for (i = 0; i < 3; i++) { + snprintf(path, + PATH_MAX, + "%s/%s" EFI_STUB_SUFFIX_L, + SHIM_BOOT_COPY_DIR, + files[i]); + if (!file_set_text(path, text)) + return false; + } + } +#endif /* HAVE_SHIM_SYSTEMD_BOOT */ return true; } @@ -449,14 +477,18 @@ int kernel_installed_files_count(BootManager *manager, PlaygroundKernel *kernel) autofree(char) *kernel_blob_legacy = NULL; autofree(char) *initrd_file = NULL; autofree(char) *initrd_file_legacy = NULL; + /* where the kernel files are expected to be found on the ESP */ + char *esp_path = manager->bootloader->get_kernel_destination + ? manager->bootloader->get_kernel_destination(manager) + : "efi/" KERNEL_NAMESPACE; const char *vendor = NULL; int file_count = 0; vendor = boot_manager_get_vendor_prefix(manager); - kernel_blob = string_printf("%s/efi/%s/kernel-%s.%s.%s-%d", + kernel_blob = string_printf("%s/%s/kernel-%s.%s.%s-%d", BOOT_FULL, - KERNEL_NAMESPACE, + esp_path, KERNEL_NAMESPACE, kernel->ktype, kernel->version, @@ -470,9 +502,9 @@ int kernel_installed_files_count(BootManager *manager, PlaygroundKernel *kernel) kernel->version, kernel->release); - initrd_file = string_printf("%s/efi/%s/initrd-%s.%s.%s-%d", + initrd_file = string_printf("%s/%s/initrd-%s.%s.%s-%d", BOOT_FULL, - KERNEL_NAMESPACE, + esp_path, KERNEL_NAMESPACE, kernel->ktype, kernel->version,