Message ID | 20220322112903.32726-1-christian.storm@siemens.com |
---|---|
State | Accepted |
Headers | show |
Series | [v3,1/5] bootloader: runtime-dynamic bootloader selection | expand |
Hi Christian, applied (whole series) to -master, thanks ! Best regards, Stefano On 22.03.22 12:28, Christian Storm wrote: > Make the bootloader interface implementation selectable at run-time. > The mechanism is three-staged with the latter overriding the former: > (1) compile-time selected default bootloader interface implementation > (2) run-time configuration file selected bootloader > interface implementation > (3) run-time command line selected bootloader interface > implementation (by the new -B switch) > > At compile-time, a set of enabled bootloader interface implementations > is selected. Via dynamically linking against the enabled bootloaders' > shared libraries (currently EFI Boot Guard and U-Boot), the very same > SWUpdate compile configuration can be used for different use cases > requiring different bootloaders, simply by run-time configuration > rather than having to recompile/repackage SWUpdate for each. This > helps distributions to pick up and package SWUpdate more easily. > > Signed-off-by: Christian Storm <christian.storm@siemens.com> > --- > Kconfig | 8 -- > Makefile | 4 +- > Makefile.deps | 8 -- > Makefile.flags | 17 ++++- > bootloader/Config.in | 110 ++++++++++++++++++---------- > bootloader/Makefile | 8 +- > core/Makefile | 1 + > core/bootloader.c | 71 ++++++++++++++++++ > core/swupdate.c | 34 ++++++++- > doc/source/swupdate.rst | 3 + > examples/configuration/swupdate.cfg | 5 ++ > include/bootloader.h | 64 +++++++++++++--- > include/util.h | 1 + > 13 files changed, 259 insertions(+), 75 deletions(-) > create mode 100644 core/bootloader.c > > diff --git a/Kconfig b/Kconfig > index da6af8d..85fa5fd 100644 > --- a/Kconfig > +++ b/Kconfig > @@ -53,14 +53,6 @@ config HAVE_LIBUBI > bool > option env="HAVE_LIBUBI" > > -config HAVE_LIBUBOOTENV > - bool > - option env="HAVE_LIBUBOOTENV" > - > -config HAVE_LIBEBGENV > - bool > - option env="HAVE_LIBEBGENV" > - > config HAVE_LIBEXT2FS > bool > option env="HAVE_LIBEXT2FS" > diff --git a/Makefile b/Makefile > index 932330c..8d30532 100644 > --- a/Makefile > +++ b/Makefile > @@ -362,8 +362,8 @@ include $(srctree)/Makefile.flags > # This allow a user to issue only 'make' to build a kernel including modules > # Defaults to vmlinux, but the arch makefile usually adds further targets > > -objs-y := core handlers > -libs-y := corelib mongoose parser suricatta bootloader fs > +objs-y := core handlers bootloader > +libs-y := corelib mongoose parser suricatta fs > bindings-y := bindings > tools-y := tools > > diff --git a/Makefile.deps b/Makefile.deps > index 58ed373..08df4e2 100644 > --- a/Makefile.deps > +++ b/Makefile.deps > @@ -54,14 +54,6 @@ ifeq ($(HAVE_ZSTD),) > export HAVE_ZSTD = y > endif > > -ifeq ($(HAVE_LIBUBOOTENV),) > -export HAVE_LIBUBOOTENV = y > -endif > - > -ifeq ($(HAVE_LIBEBGENV),) > -export HAVE_LIBEBGENV = y > -endif > - > ifeq ($(HAVE_LIBEXT2FS),) > export HAVE_LIBEXT2FS = y > endif > diff --git a/Makefile.flags b/Makefile.flags > index 019ef77..9d9163d 100644 > --- a/Makefile.flags > +++ b/Makefile.flags > @@ -210,7 +210,7 @@ LDLIBS += gpiod > endif > > ifeq ($(CONFIG_UBOOT),y) > -LDLIBS += z ubootenv > +LDLIBS += dl > endif > > ifeq ($(CONFIG_SYSTEMD),y) > @@ -218,7 +218,20 @@ LDLIBS += systemd > endif > > ifeq ($(CONFIG_BOOTLOADER_EBG),y) > -LDLIBS += ebgenv z > +LDLIBS += dl > +endif > + > +ifneq ($(CONFIG_BOOTLOADER_DEFAULT_NONE),) > +KBUILD_CPPFLAGS += -DBOOTLOADER_DEFAULT="none" > +endif > +ifneq ($(CONFIG_BOOTLOADER_DEFAULT_GRUB),) > +KBUILD_CPPFLAGS += -DBOOTLOADER_DEFAULT="grub" > +endif > +ifneq ($(CONFIG_BOOTLOADER_DEFAULT_UBOOT),) > +KBUILD_CPPFLAGS += -DBOOTLOADER_DEFAULT="uboot" > +endif > +ifneq ($(CONFIG_BOOTLOADER_DEFAULT_EBG),) > +KBUILD_CPPFLAGS += -DBOOTLOADER_DEFAULT="ebg" > endif > > # SWU forwarder > diff --git a/bootloader/Config.in b/bootloader/Config.in > index ad9fec1..a0ebeef 100644 > --- a/bootloader/Config.in > +++ b/bootloader/Config.in > @@ -2,56 +2,100 @@ > # > # SPDX-License-Identifier: GPL-2.0-only > > -choice > - prompt "Bootloader (U-Boot, ..)" > - default UBOOT > +menu "Bootloader Interfaces" > + > +config BOOTLOADER_NONE > + bool "Environment in RAM" > help > - Choose the bootloader > + This simulates the interface to a bootloader. > + Bootloader environment is just maintained in RAM > + and lost when SWUpdate exits. > > config BOOTLOADER_EBG > bool "EFI Boot Guard" > - depends on HAVE_LIBEBGENV > - depends on HAVE_ZLIB > help > Support for EFI Boot Guard > https://github.com/siemens/efibootguard > > -comment "EFI Boot Guard needs libebgenv and libz" > - depends on !HAVE_ZLIB || !HAVE_LIBEBGENV > - > config UBOOT > bool "U-Boot" > - depends on HAVE_LIBUBOOTENV > - depends on HAVE_ZLIB > help > Support for U-Boot > https://www.denx.de/wiki/U-Boot > > -comment "U-Boot support needs libubootenv and libz" > - depends on !HAVE_LIBUBOOTENV || !HAVE_ZLIB > +config UBOOT_FWENV > + string "U-Boot Environment Configuration file" > + depends on UBOOT > + default "/etc/fw_env.config" > + help > + This is the file described in U-Boot documentation > + in the tools directory. It tells where the U-Boot > + environment is saved. > > -config BOOTLOADER_NONE > - bool "Environment in RAM" > +config UBOOT_DEFAULTENV > + string "U-Boot Initial Environment file" > + depends on UBOOT > + default "/etc/u-boot-initial-env" > help > - This simulates the interface to a bootloader. > - Bootloader environment is just maitained in RAM > - and lost when SWUpdate exits. > + This is the file with the initial environment delivered > + with the bootloader. It is used by SWUpdate if no environment > + is found on the storage. > > config BOOTLOADER_GRUB > bool "GRUB" > help > Support for GRUB > https://www.gnu.org/software/grub/ > -endchoice > > -config UBOOT_FWENV > - string "U-Boot Environment Configuration file" > +config GRUBENV_PATH > + string "GRUB Environment block file path" > + depends on BOOTLOADER_GRUB > + default "/boot/efi/EFI/BOOT/grub/grubenv" > + help > + Provide path to GRUB environment block file > + > +endmenu > + > +choice > + prompt "Default Bootloader Interface" > + help > + Default bootloader interface to use if not explicitly > + overridden via configuration or command-line option > + at run-time. > + > +config BOOTLOADER_DEFAULT_EBG > + bool "EBG" > + depends on BOOTLOADER_EBG > + help > + Use EFI Boot Guard as default bootloader interface. > + > + Note: Make sure the environment modification shared > + library https://github.com/siemens/efibootguard (including > + dependencies) is installed on the target system. > + > +config BOOTLOADER_DEFAULT_UBOOT > + bool "U-Boot" > depends on UBOOT > - default "/etc/fw_env.config" > help > - This is the file described in U-Boot documentation > - in the tools directory. It tells where the U-Boot > - environment is saved. > + Use U-Boot as default bootloader interface. > + > + Note: Make sure the environment modification shared > + library https://github.com/sbabic/libubootenv (including > + dependencies) is installed on the target system. > + > +config BOOTLOADER_DEFAULT_GRUB > + bool "GRUB" > + depends on BOOTLOADER_GRUB > + help > + Use GRUB as default bootloader interface. > + > +config BOOTLOADER_DEFAULT_NONE > + bool "Environment in RAM" > + depends on BOOTLOADER_NONE > + help > + Use Environment in RAM as default bootloader interface. > + > +endchoice > > choice > prompt "Update Status Storage" > @@ -67,6 +111,7 @@ config UPDATE_STATE_CHOICE_NONE > > config UPDATE_STATE_CHOICE_BOOTLOADER > bool "Bootloader" > + depends on BOOTLOADER_EBG || UBOOT || BOOTLOADER_NONE || BOOTLOADER_GRUB > help > Store update status in Bootloader's environment. > Specify Bootloader environment variable name to store update status in. > @@ -80,18 +125,3 @@ config UPDATE_STATE_BOOTLOADER > help > Store update information in Bootloader's environment. > > -config UBOOT_DEFAULTENV > - string "U-Boot Initial Environment file" > - depends on UBOOT > - default "/etc/u-boot-initial-env" > - help > - This is the file with the initial environment delivered > - with the bootloader. It is used by SWUpdate if no environment > - is found on the storage. > - > -config GRUBENV_PATH > - string "GRUB Environment block file path" > - depends on BOOTLOADER_GRUB > - default "/boot/efi/EFI/BOOT/grub/grubenv" > - help > - Provide path to GRUB environment block file > diff --git a/bootloader/Makefile b/bootloader/Makefile > index ff56387..2c34291 100644 > --- a/bootloader/Makefile > +++ b/bootloader/Makefile > @@ -2,7 +2,7 @@ > # > # SPDX-License-Identifier: GPL-2.0-only > # > -lib-$(CONFIG_UBOOT) += uboot.o > -lib-$(CONFIG_BOOTLOADER_NONE) += none.o > -lib-$(CONFIG_BOOTLOADER_GRUB) += grub.o > -lib-$(CONFIG_BOOTLOADER_EBG) += ebg.o > +obj-$(CONFIG_UBOOT) += uboot.o > +obj-$(CONFIG_BOOTLOADER_NONE) += none.o > +obj-$(CONFIG_BOOTLOADER_GRUB) += grub.o > +obj-$(CONFIG_BOOTLOADER_EBG) += ebg.o > diff --git a/core/Makefile b/core/Makefile > index fa30e6e..90f1177 100644 > --- a/core/Makefile > +++ b/core/Makefile > @@ -12,6 +12,7 @@ obj-y += swupdate.o \ > cpio_utils.o \ > notifier.o \ > handler.o \ > + bootloader.o \ > install_from_file.o \ > util.o \ > parser.o \ > diff --git a/core/bootloader.c b/core/bootloader.c > new file mode 100644 > index 0000000..f34cb41 > --- /dev/null > +++ b/core/bootloader.c > @@ -0,0 +1,71 @@ > +/* > + * Author: Christian Storm > + * Copyright (C) 2022, Siemens AG > + * > + * SPDX-License-Identifier: GPL-2.0-only > + */ > +#include <stdlib.h> > +#include <errno.h> > +#include <util.h> > +#include <bootloader.h> > + > +int (*bootloader_env_set)(const char *, const char *); > +int (*bootloader_env_unset)(const char *); > +char* (*bootloader_env_get)(const char *); > +int (*bootloader_apply_list)(const char *); > + > +typedef struct { > + const char *name; > + bootloader *funcs; > +} entry; > + > +static entry *current = NULL; > +static entry *available = NULL; > +static unsigned int num_available = 0; > + > +int register_bootloader(const char *name, bootloader *bl) > +{ > + entry *tmp = reallocarray(available, num_available + 1, sizeof(entry)); > + if (!tmp) { > + return -ENOMEM; > + } > + tmp[num_available].name = (char*)name; > + tmp[num_available].funcs = bl; > + num_available++; > + available = tmp; > + return 0; > +} > + > +int set_bootloader(const char *name) > +{ > + if (!name) { > + return -ENOENT; > + } > + for (unsigned int i = 0; i < num_available; i++) { > + if (available[i].funcs && > + (strcmp(available[i].name, name) == 0)) { > + bootloader_env_set = available[i].funcs->env_set; > + bootloader_env_get = available[i].funcs->env_get; > + bootloader_env_unset = available[i].funcs->env_unset; > + bootloader_apply_list = available[i].funcs->apply_list; > + current = &available[i]; > + return 0; > + } > + } > + return -ENOENT; > +} > + > +const char* get_bootloader(void) > +{ > + return current ? current->name : NULL; > +} > + > +void print_registered_bootloaders(void) > +{ > + INFO("Registered bootloaders:"); > + for (unsigned int i = 0; i < num_available; i++) { > + INFO("\t%s\t%s", available[i].name, > + available[i].funcs == NULL ? "shared lib not found." > + : "loaded."); > + } > +} > diff --git a/core/swupdate.c b/core/swupdate.c > index b59e0eb..d2035f9 100644 > --- a/core/swupdate.c > +++ b/core/swupdate.c > @@ -114,6 +114,7 @@ static struct option long_options[] = { > #ifdef CONFIG_WEBSERVER > {"webserver", required_argument, NULL, 'w'}, > #endif > + {"bootloader", required_argument, NULL, 'B'}, > {NULL, 0, NULL, 0} > }; > > @@ -129,6 +130,7 @@ static void usage(char *programname) > #ifdef CONFIG_UBIATTACH > " -b, --blacklist <list of mtd> : MTDs that must not be scanned for UBI\n" > #endif > + " -B, --bootloader : bootloader interface (default: " PREPROCVALUE(BOOTLOADER_DEFAULT) ")\n" > " -p, --postupdate : execute post-update command\n" > " -P, --preupdate : execute pre-update command\n" > " -e, --select <software>,<mode> : Select software images set and source\n" > @@ -283,6 +285,15 @@ static int read_globals_settings(void *elem, void *data) > char tmp[SWUPDATE_GENERAL_STRING_SIZE] = ""; > struct swupdate_cfg *sw = (struct swupdate_cfg *)data; > > + GET_FIELD_STRING(LIBCFG_PARSER, elem, > + "bootloader", tmp); > + if (tmp[0] != '\0') { > + if (set_bootloader(tmp) != 0) { > + ERROR("Bootloader interface '%s' could not be initialized.", tmp); > + exit(EXIT_FAILURE); > + } > + tmp[0] = '\0'; > + } > GET_FIELD_STRING(LIBCFG_PARSER, elem, > "public-key-file", sw->publickeyfname); > GET_FIELD_STRING(LIBCFG_PARSER, elem, > @@ -435,7 +446,7 @@ int main(int argc, char **argv) > #endif > memset(main_options, 0, sizeof(main_options)); > memset(image_url, 0, sizeof(image_url)); > - strcpy(main_options, "vhni:e:gq:l:Lcf:p:P:o:N:R:Mm"); > + strcpy(main_options, "vhni:e:gq:l:Lcf:p:P:o:N:R:MmB:"); > #ifdef CONFIG_MTD > strcat(main_options, "b:"); > #endif > @@ -581,6 +592,13 @@ int main(int argc, char **argv) > case 'o': > strlcpy(swcfg.output, optarg, sizeof(swcfg.output)); > break; > + case 'B': > + if (set_bootloader(optarg) != 0) { > + ERROR("Bootloader interface '%s' could not be initialized.", optarg); > + print_registered_bootloaders(); > + exit(EXIT_FAILURE); > + } > + break; > case 'l': > loglevel = strtoul(optarg, NULL, 10); > break; > @@ -754,6 +772,20 @@ int main(int argc, char **argv) > printf("Licensed under GPLv2. See source distribution for detailed " > "copyright notices.\n\n"); > > + print_registered_bootloaders(); > + if (!get_bootloader()) { > + if (set_bootloader(PREPROCVALUE(BOOTLOADER_DEFAULT)) != 0) { > + ERROR("Default bootloader interface '" PREPROCVALUE( > + BOOTLOADER_DEFAULT) "' couldn't be loaded."); > + INFO("Check that the bootloader interface shared library is present."); > + INFO("Or chose another bootloader interface by supplying -B <loader>."); > + exit(EXIT_FAILURE); > + } > + INFO("Using default bootloader interface: " PREPROCVALUE(BOOTLOADER_DEFAULT)); > + } else { > + INFO("Using bootloader interface: %s", get_bootloader()); > + } > + > /* > * Install a child handler to check if a subprocess > * dies > diff --git a/doc/source/swupdate.rst b/doc/source/swupdate.rst > index 7bd6c92..802b16a 100644 > --- a/doc/source/swupdate.rst > +++ b/doc/source/swupdate.rst > @@ -467,6 +467,9 @@ Command line parameters > | | | Example: U-Boot and environment in MTD0-1: | > | | | ``swupdate -b "0 1"``. | > +-------------+----------+--------------------------------------------+ > +| -B <loader> | string | Override the default bootloader interface | > +| | | to use ``loader`` instead. | > ++-------------+----------+--------------------------------------------+ > | -e <sel> | string | ``sel`` is in the format <software>,<mode>.| > | | | It allows one to find a subset of rules in | > | | | the sw-description file. With it, | > diff --git a/examples/configuration/swupdate.cfg b/examples/configuration/swupdate.cfg > index 2f19ffa..76d0a02 100644 > --- a/examples/configuration/swupdate.cfg > +++ b/examples/configuration/swupdate.cfg > @@ -43,6 +43,11 @@ > # set expected common name of signer certificate > # select: : string > # select software images set and source (<software>,<mode>) > +# bootloader: : string > +# bootloader interface to use, overruling compile-time default. > +# Possible values are ebg, grub, uboot, and none for > +# EFI Boot Guard, U-Boot, GRUB, and the Environment in RAM bootloader, > +# respectively, given the respective bootloader support is compiled-in. > globals : > { > > diff --git a/include/bootloader.h b/include/bootloader.h > index 7bf5fa3..8315759 100644 > --- a/include/bootloader.h > +++ b/include/bootloader.h > @@ -5,8 +5,56 @@ > * SPDX-License-Identifier: GPL-2.0-only > */ > > -#ifndef _BOOTLOADER_INTERFACE_H > -#define _BOOTLOADER_INTERFACE_H > +#pragma once > + > +#define load_symbol(handle, container, fname) \ > + *(void**)(container) = dlsym(handle, fname); \ > + if (dlerror() != NULL) { \ > + (void)dlclose(handle); \ > + return NULL; \ > + } > + > +typedef struct { > + int (*env_set)(const char *, const char *); > + int (*env_unset)(const char *); > + char* (*env_get)(const char *); > + int (*apply_list)(const char *); > +} bootloader; > + > +/* > + * register_bootloader - register bootloader. > + * > + * @name : bootloader's name to register. > + * @bootloader : struct bootloader with bootloader details. > + * > + * Return: > + * 0 on success, -ENOMEM on error. > + */ > +int register_bootloader(const char *name, bootloader *bl); > + > +/* > + * set_bootloader - set bootloader to use. > + * > + * @name : bootloader's name to set. > + * > + * Return: > + * 0 on success, -ENOENT on error. > + */ > +int set_bootloader(const char *name); > + > +/* > + * get_bootloader - get set bootloader's name > + * > + * Return: > + * name on success, NULL on error. > + */ > +const char* get_bootloader(void); > + > +/* > + * print_registered_bootloaders - print registered bootloaders > + */ > +void print_registered_bootloaders(void); > + > > /* > * bootloader_env_set - modify a variable > @@ -17,8 +65,7 @@ > * Return: > * 0 on success > */ > - > -int bootloader_env_set(const char *name, const char *value); > +extern int (*bootloader_env_set)(const char *, const char *); > > /* > * bootloader_env_unset - drop a variable > @@ -28,8 +75,7 @@ int bootloader_env_set(const char *name, const char *value); > * Return: > * 0 on success > */ > - > -int bootloader_env_unset(const char *name); > +extern int (*bootloader_env_unset)(const char *); > > /* > * bootloader_env_get - get value of a variable > @@ -40,8 +86,7 @@ int bootloader_env_unset(const char *name); > * string if variable is found > * NULL if no variable with name is found > */ > - > -char *bootloader_env_get(const char *name); > +extern char* (*bootloader_env_get)(const char *); > > /* > * bootloader_apply_list - set multiple variables > @@ -51,6 +96,5 @@ char *bootloader_env_get(const char *name); > * Return: > * 0 on success > */ > -int bootloader_apply_list(const char *script); > +extern int (*bootloader_apply_list)(const char *); > > -#endif > diff --git a/include/util.h b/include/util.h > index 270978d..302ea04 100644 > --- a/include/util.h > +++ b/include/util.h > @@ -125,6 +125,7 @@ void notifier_set_color(int level, char *col); > #endif > > #define STRINGIFY(...) #__VA_ARGS__ > +#define PREPROCVALUE(s) STRINGIFY(s) > #define SETSTRING(p, v) do { \ > if (p) \ > free(p); \
diff --git a/Kconfig b/Kconfig index da6af8d..85fa5fd 100644 --- a/Kconfig +++ b/Kconfig @@ -53,14 +53,6 @@ config HAVE_LIBUBI bool option env="HAVE_LIBUBI" -config HAVE_LIBUBOOTENV - bool - option env="HAVE_LIBUBOOTENV" - -config HAVE_LIBEBGENV - bool - option env="HAVE_LIBEBGENV" - config HAVE_LIBEXT2FS bool option env="HAVE_LIBEXT2FS" diff --git a/Makefile b/Makefile index 932330c..8d30532 100644 --- a/Makefile +++ b/Makefile @@ -362,8 +362,8 @@ include $(srctree)/Makefile.flags # This allow a user to issue only 'make' to build a kernel including modules # Defaults to vmlinux, but the arch makefile usually adds further targets -objs-y := core handlers -libs-y := corelib mongoose parser suricatta bootloader fs +objs-y := core handlers bootloader +libs-y := corelib mongoose parser suricatta fs bindings-y := bindings tools-y := tools diff --git a/Makefile.deps b/Makefile.deps index 58ed373..08df4e2 100644 --- a/Makefile.deps +++ b/Makefile.deps @@ -54,14 +54,6 @@ ifeq ($(HAVE_ZSTD),) export HAVE_ZSTD = y endif -ifeq ($(HAVE_LIBUBOOTENV),) -export HAVE_LIBUBOOTENV = y -endif - -ifeq ($(HAVE_LIBEBGENV),) -export HAVE_LIBEBGENV = y -endif - ifeq ($(HAVE_LIBEXT2FS),) export HAVE_LIBEXT2FS = y endif diff --git a/Makefile.flags b/Makefile.flags index 019ef77..9d9163d 100644 --- a/Makefile.flags +++ b/Makefile.flags @@ -210,7 +210,7 @@ LDLIBS += gpiod endif ifeq ($(CONFIG_UBOOT),y) -LDLIBS += z ubootenv +LDLIBS += dl endif ifeq ($(CONFIG_SYSTEMD),y) @@ -218,7 +218,20 @@ LDLIBS += systemd endif ifeq ($(CONFIG_BOOTLOADER_EBG),y) -LDLIBS += ebgenv z +LDLIBS += dl +endif + +ifneq ($(CONFIG_BOOTLOADER_DEFAULT_NONE),) +KBUILD_CPPFLAGS += -DBOOTLOADER_DEFAULT="none" +endif +ifneq ($(CONFIG_BOOTLOADER_DEFAULT_GRUB),) +KBUILD_CPPFLAGS += -DBOOTLOADER_DEFAULT="grub" +endif +ifneq ($(CONFIG_BOOTLOADER_DEFAULT_UBOOT),) +KBUILD_CPPFLAGS += -DBOOTLOADER_DEFAULT="uboot" +endif +ifneq ($(CONFIG_BOOTLOADER_DEFAULT_EBG),) +KBUILD_CPPFLAGS += -DBOOTLOADER_DEFAULT="ebg" endif # SWU forwarder diff --git a/bootloader/Config.in b/bootloader/Config.in index ad9fec1..a0ebeef 100644 --- a/bootloader/Config.in +++ b/bootloader/Config.in @@ -2,56 +2,100 @@ # # SPDX-License-Identifier: GPL-2.0-only -choice - prompt "Bootloader (U-Boot, ..)" - default UBOOT +menu "Bootloader Interfaces" + +config BOOTLOADER_NONE + bool "Environment in RAM" help - Choose the bootloader + This simulates the interface to a bootloader. + Bootloader environment is just maintained in RAM + and lost when SWUpdate exits. config BOOTLOADER_EBG bool "EFI Boot Guard" - depends on HAVE_LIBEBGENV - depends on HAVE_ZLIB help Support for EFI Boot Guard https://github.com/siemens/efibootguard -comment "EFI Boot Guard needs libebgenv and libz" - depends on !HAVE_ZLIB || !HAVE_LIBEBGENV - config UBOOT bool "U-Boot" - depends on HAVE_LIBUBOOTENV - depends on HAVE_ZLIB help Support for U-Boot https://www.denx.de/wiki/U-Boot -comment "U-Boot support needs libubootenv and libz" - depends on !HAVE_LIBUBOOTENV || !HAVE_ZLIB +config UBOOT_FWENV + string "U-Boot Environment Configuration file" + depends on UBOOT + default "/etc/fw_env.config" + help + This is the file described in U-Boot documentation + in the tools directory. It tells where the U-Boot + environment is saved. -config BOOTLOADER_NONE - bool "Environment in RAM" +config UBOOT_DEFAULTENV + string "U-Boot Initial Environment file" + depends on UBOOT + default "/etc/u-boot-initial-env" help - This simulates the interface to a bootloader. - Bootloader environment is just maitained in RAM - and lost when SWUpdate exits. + This is the file with the initial environment delivered + with the bootloader. It is used by SWUpdate if no environment + is found on the storage. config BOOTLOADER_GRUB bool "GRUB" help Support for GRUB https://www.gnu.org/software/grub/ -endchoice -config UBOOT_FWENV - string "U-Boot Environment Configuration file" +config GRUBENV_PATH + string "GRUB Environment block file path" + depends on BOOTLOADER_GRUB + default "/boot/efi/EFI/BOOT/grub/grubenv" + help + Provide path to GRUB environment block file + +endmenu + +choice + prompt "Default Bootloader Interface" + help + Default bootloader interface to use if not explicitly + overridden via configuration or command-line option + at run-time. + +config BOOTLOADER_DEFAULT_EBG + bool "EBG" + depends on BOOTLOADER_EBG + help + Use EFI Boot Guard as default bootloader interface. + + Note: Make sure the environment modification shared + library https://github.com/siemens/efibootguard (including + dependencies) is installed on the target system. + +config BOOTLOADER_DEFAULT_UBOOT + bool "U-Boot" depends on UBOOT - default "/etc/fw_env.config" help - This is the file described in U-Boot documentation - in the tools directory. It tells where the U-Boot - environment is saved. + Use U-Boot as default bootloader interface. + + Note: Make sure the environment modification shared + library https://github.com/sbabic/libubootenv (including + dependencies) is installed on the target system. + +config BOOTLOADER_DEFAULT_GRUB + bool "GRUB" + depends on BOOTLOADER_GRUB + help + Use GRUB as default bootloader interface. + +config BOOTLOADER_DEFAULT_NONE + bool "Environment in RAM" + depends on BOOTLOADER_NONE + help + Use Environment in RAM as default bootloader interface. + +endchoice choice prompt "Update Status Storage" @@ -67,6 +111,7 @@ config UPDATE_STATE_CHOICE_NONE config UPDATE_STATE_CHOICE_BOOTLOADER bool "Bootloader" + depends on BOOTLOADER_EBG || UBOOT || BOOTLOADER_NONE || BOOTLOADER_GRUB help Store update status in Bootloader's environment. Specify Bootloader environment variable name to store update status in. @@ -80,18 +125,3 @@ config UPDATE_STATE_BOOTLOADER help Store update information in Bootloader's environment. -config UBOOT_DEFAULTENV - string "U-Boot Initial Environment file" - depends on UBOOT - default "/etc/u-boot-initial-env" - help - This is the file with the initial environment delivered - with the bootloader. It is used by SWUpdate if no environment - is found on the storage. - -config GRUBENV_PATH - string "GRUB Environment block file path" - depends on BOOTLOADER_GRUB - default "/boot/efi/EFI/BOOT/grub/grubenv" - help - Provide path to GRUB environment block file diff --git a/bootloader/Makefile b/bootloader/Makefile index ff56387..2c34291 100644 --- a/bootloader/Makefile +++ b/bootloader/Makefile @@ -2,7 +2,7 @@ # # SPDX-License-Identifier: GPL-2.0-only # -lib-$(CONFIG_UBOOT) += uboot.o -lib-$(CONFIG_BOOTLOADER_NONE) += none.o -lib-$(CONFIG_BOOTLOADER_GRUB) += grub.o -lib-$(CONFIG_BOOTLOADER_EBG) += ebg.o +obj-$(CONFIG_UBOOT) += uboot.o +obj-$(CONFIG_BOOTLOADER_NONE) += none.o +obj-$(CONFIG_BOOTLOADER_GRUB) += grub.o +obj-$(CONFIG_BOOTLOADER_EBG) += ebg.o diff --git a/core/Makefile b/core/Makefile index fa30e6e..90f1177 100644 --- a/core/Makefile +++ b/core/Makefile @@ -12,6 +12,7 @@ obj-y += swupdate.o \ cpio_utils.o \ notifier.o \ handler.o \ + bootloader.o \ install_from_file.o \ util.o \ parser.o \ diff --git a/core/bootloader.c b/core/bootloader.c new file mode 100644 index 0000000..f34cb41 --- /dev/null +++ b/core/bootloader.c @@ -0,0 +1,71 @@ +/* + * Author: Christian Storm + * Copyright (C) 2022, Siemens AG + * + * SPDX-License-Identifier: GPL-2.0-only + */ +#include <stdlib.h> +#include <errno.h> +#include <util.h> +#include <bootloader.h> + +int (*bootloader_env_set)(const char *, const char *); +int (*bootloader_env_unset)(const char *); +char* (*bootloader_env_get)(const char *); +int (*bootloader_apply_list)(const char *); + +typedef struct { + const char *name; + bootloader *funcs; +} entry; + +static entry *current = NULL; +static entry *available = NULL; +static unsigned int num_available = 0; + +int register_bootloader(const char *name, bootloader *bl) +{ + entry *tmp = reallocarray(available, num_available + 1, sizeof(entry)); + if (!tmp) { + return -ENOMEM; + } + tmp[num_available].name = (char*)name; + tmp[num_available].funcs = bl; + num_available++; + available = tmp; + return 0; +} + +int set_bootloader(const char *name) +{ + if (!name) { + return -ENOENT; + } + for (unsigned int i = 0; i < num_available; i++) { + if (available[i].funcs && + (strcmp(available[i].name, name) == 0)) { + bootloader_env_set = available[i].funcs->env_set; + bootloader_env_get = available[i].funcs->env_get; + bootloader_env_unset = available[i].funcs->env_unset; + bootloader_apply_list = available[i].funcs->apply_list; + current = &available[i]; + return 0; + } + } + return -ENOENT; +} + +const char* get_bootloader(void) +{ + return current ? current->name : NULL; +} + +void print_registered_bootloaders(void) +{ + INFO("Registered bootloaders:"); + for (unsigned int i = 0; i < num_available; i++) { + INFO("\t%s\t%s", available[i].name, + available[i].funcs == NULL ? "shared lib not found." + : "loaded."); + } +} diff --git a/core/swupdate.c b/core/swupdate.c index b59e0eb..d2035f9 100644 --- a/core/swupdate.c +++ b/core/swupdate.c @@ -114,6 +114,7 @@ static struct option long_options[] = { #ifdef CONFIG_WEBSERVER {"webserver", required_argument, NULL, 'w'}, #endif + {"bootloader", required_argument, NULL, 'B'}, {NULL, 0, NULL, 0} }; @@ -129,6 +130,7 @@ static void usage(char *programname) #ifdef CONFIG_UBIATTACH " -b, --blacklist <list of mtd> : MTDs that must not be scanned for UBI\n" #endif + " -B, --bootloader : bootloader interface (default: " PREPROCVALUE(BOOTLOADER_DEFAULT) ")\n" " -p, --postupdate : execute post-update command\n" " -P, --preupdate : execute pre-update command\n" " -e, --select <software>,<mode> : Select software images set and source\n" @@ -283,6 +285,15 @@ static int read_globals_settings(void *elem, void *data) char tmp[SWUPDATE_GENERAL_STRING_SIZE] = ""; struct swupdate_cfg *sw = (struct swupdate_cfg *)data; + GET_FIELD_STRING(LIBCFG_PARSER, elem, + "bootloader", tmp); + if (tmp[0] != '\0') { + if (set_bootloader(tmp) != 0) { + ERROR("Bootloader interface '%s' could not be initialized.", tmp); + exit(EXIT_FAILURE); + } + tmp[0] = '\0'; + } GET_FIELD_STRING(LIBCFG_PARSER, elem, "public-key-file", sw->publickeyfname); GET_FIELD_STRING(LIBCFG_PARSER, elem, @@ -435,7 +446,7 @@ int main(int argc, char **argv) #endif memset(main_options, 0, sizeof(main_options)); memset(image_url, 0, sizeof(image_url)); - strcpy(main_options, "vhni:e:gq:l:Lcf:p:P:o:N:R:Mm"); + strcpy(main_options, "vhni:e:gq:l:Lcf:p:P:o:N:R:MmB:"); #ifdef CONFIG_MTD strcat(main_options, "b:"); #endif @@ -581,6 +592,13 @@ int main(int argc, char **argv) case 'o': strlcpy(swcfg.output, optarg, sizeof(swcfg.output)); break; + case 'B': + if (set_bootloader(optarg) != 0) { + ERROR("Bootloader interface '%s' could not be initialized.", optarg); + print_registered_bootloaders(); + exit(EXIT_FAILURE); + } + break; case 'l': loglevel = strtoul(optarg, NULL, 10); break; @@ -754,6 +772,20 @@ int main(int argc, char **argv) printf("Licensed under GPLv2. See source distribution for detailed " "copyright notices.\n\n"); + print_registered_bootloaders(); + if (!get_bootloader()) { + if (set_bootloader(PREPROCVALUE(BOOTLOADER_DEFAULT)) != 0) { + ERROR("Default bootloader interface '" PREPROCVALUE( + BOOTLOADER_DEFAULT) "' couldn't be loaded."); + INFO("Check that the bootloader interface shared library is present."); + INFO("Or chose another bootloader interface by supplying -B <loader>."); + exit(EXIT_FAILURE); + } + INFO("Using default bootloader interface: " PREPROCVALUE(BOOTLOADER_DEFAULT)); + } else { + INFO("Using bootloader interface: %s", get_bootloader()); + } + /* * Install a child handler to check if a subprocess * dies diff --git a/doc/source/swupdate.rst b/doc/source/swupdate.rst index 7bd6c92..802b16a 100644 --- a/doc/source/swupdate.rst +++ b/doc/source/swupdate.rst @@ -467,6 +467,9 @@ Command line parameters | | | Example: U-Boot and environment in MTD0-1: | | | | ``swupdate -b "0 1"``. | +-------------+----------+--------------------------------------------+ +| -B <loader> | string | Override the default bootloader interface | +| | | to use ``loader`` instead. | ++-------------+----------+--------------------------------------------+ | -e <sel> | string | ``sel`` is in the format <software>,<mode>.| | | | It allows one to find a subset of rules in | | | | the sw-description file. With it, | diff --git a/examples/configuration/swupdate.cfg b/examples/configuration/swupdate.cfg index 2f19ffa..76d0a02 100644 --- a/examples/configuration/swupdate.cfg +++ b/examples/configuration/swupdate.cfg @@ -43,6 +43,11 @@ # set expected common name of signer certificate # select: : string # select software images set and source (<software>,<mode>) +# bootloader: : string +# bootloader interface to use, overruling compile-time default. +# Possible values are ebg, grub, uboot, and none for +# EFI Boot Guard, U-Boot, GRUB, and the Environment in RAM bootloader, +# respectively, given the respective bootloader support is compiled-in. globals : { diff --git a/include/bootloader.h b/include/bootloader.h index 7bf5fa3..8315759 100644 --- a/include/bootloader.h +++ b/include/bootloader.h @@ -5,8 +5,56 @@ * SPDX-License-Identifier: GPL-2.0-only */ -#ifndef _BOOTLOADER_INTERFACE_H -#define _BOOTLOADER_INTERFACE_H +#pragma once + +#define load_symbol(handle, container, fname) \ + *(void**)(container) = dlsym(handle, fname); \ + if (dlerror() != NULL) { \ + (void)dlclose(handle); \ + return NULL; \ + } + +typedef struct { + int (*env_set)(const char *, const char *); + int (*env_unset)(const char *); + char* (*env_get)(const char *); + int (*apply_list)(const char *); +} bootloader; + +/* + * register_bootloader - register bootloader. + * + * @name : bootloader's name to register. + * @bootloader : struct bootloader with bootloader details. + * + * Return: + * 0 on success, -ENOMEM on error. + */ +int register_bootloader(const char *name, bootloader *bl); + +/* + * set_bootloader - set bootloader to use. + * + * @name : bootloader's name to set. + * + * Return: + * 0 on success, -ENOENT on error. + */ +int set_bootloader(const char *name); + +/* + * get_bootloader - get set bootloader's name + * + * Return: + * name on success, NULL on error. + */ +const char* get_bootloader(void); + +/* + * print_registered_bootloaders - print registered bootloaders + */ +void print_registered_bootloaders(void); + /* * bootloader_env_set - modify a variable @@ -17,8 +65,7 @@ * Return: * 0 on success */ - -int bootloader_env_set(const char *name, const char *value); +extern int (*bootloader_env_set)(const char *, const char *); /* * bootloader_env_unset - drop a variable @@ -28,8 +75,7 @@ int bootloader_env_set(const char *name, const char *value); * Return: * 0 on success */ - -int bootloader_env_unset(const char *name); +extern int (*bootloader_env_unset)(const char *); /* * bootloader_env_get - get value of a variable @@ -40,8 +86,7 @@ int bootloader_env_unset(const char *name); * string if variable is found * NULL if no variable with name is found */ - -char *bootloader_env_get(const char *name); +extern char* (*bootloader_env_get)(const char *); /* * bootloader_apply_list - set multiple variables @@ -51,6 +96,5 @@ char *bootloader_env_get(const char *name); * Return: * 0 on success */ -int bootloader_apply_list(const char *script); +extern int (*bootloader_apply_list)(const char *); -#endif diff --git a/include/util.h b/include/util.h index 270978d..302ea04 100644 --- a/include/util.h +++ b/include/util.h @@ -125,6 +125,7 @@ void notifier_set_color(int level, char *col); #endif #define STRINGIFY(...) #__VA_ARGS__ +#define PREPROCVALUE(s) STRINGIFY(s) #define SETSTRING(p, v) do { \ if (p) \ free(p); \
Make the bootloader interface implementation selectable at run-time. The mechanism is three-staged with the latter overriding the former: (1) compile-time selected default bootloader interface implementation (2) run-time configuration file selected bootloader interface implementation (3) run-time command line selected bootloader interface implementation (by the new -B switch) At compile-time, a set of enabled bootloader interface implementations is selected. Via dynamically linking against the enabled bootloaders' shared libraries (currently EFI Boot Guard and U-Boot), the very same SWUpdate compile configuration can be used for different use cases requiring different bootloaders, simply by run-time configuration rather than having to recompile/repackage SWUpdate for each. This helps distributions to pick up and package SWUpdate more easily. Signed-off-by: Christian Storm <christian.storm@siemens.com> --- Kconfig | 8 -- Makefile | 4 +- Makefile.deps | 8 -- Makefile.flags | 17 ++++- bootloader/Config.in | 110 ++++++++++++++++++---------- bootloader/Makefile | 8 +- core/Makefile | 1 + core/bootloader.c | 71 ++++++++++++++++++ core/swupdate.c | 34 ++++++++- doc/source/swupdate.rst | 3 + examples/configuration/swupdate.cfg | 5 ++ include/bootloader.h | 64 +++++++++++++--- include/util.h | 1 + 13 files changed, 259 insertions(+), 75 deletions(-) create mode 100644 core/bootloader.c