diff mbox series

[v3,2/5] bootloader: adapt implementations to runtime-dynamic selection

Message ID 20220322112903.32726-2-christian.storm@siemens.com
State Accepted
Headers show
Series [v3,1/5] bootloader: runtime-dynamic bootloader selection | expand

Commit Message

Storm, Christian March 22, 2022, 11:29 a.m. UTC
Adopt the bootloader interface implementations to the
runtime-dynamic bootloader selection mechanism.

Signed-off-by: Christian Storm <christian.storm@siemens.com>
---
 bootloader/ebg.c   | 96 ++++++++++++++++++++++++++++++++++------------
 bootloader/grub.c  | 21 ++++++++--
 bootloader/none.c  | 21 ++++++++--
 bootloader/uboot.c | 86 +++++++++++++++++++++++++++++++----------
 4 files changed, 172 insertions(+), 52 deletions(-)
diff mbox series

Patch

diff --git a/bootloader/ebg.c b/bootloader/ebg.c
index 2aa9010..b16a657 100644
--- a/bootloader/ebg.c
+++ b/bootloader/ebg.c
@@ -16,20 +16,34 @@ 
 #include <efibootguard/ebgenv.h>
 #include <generated/autoconf.h>
 #include <state.h>
+#include "dlfcn.h"
 #include "bootloader.h"
 
+static struct {
+	void (*beverbose)(ebgenv_t *e, bool v);
+	int  (*env_create_new)(ebgenv_t *e);
+	int  (*env_open_current)(ebgenv_t *e);
+	int  (*env_get)(ebgenv_t *e, char *key, char* buffer);
+	int  (*env_set)(ebgenv_t *e, char *key, char *value);
+	int  (*env_set_ex)(ebgenv_t *e, char *key, uint64_t datatype, uint8_t *value, uint32_t datalen);
+	uint16_t (*env_getglobalstate)(ebgenv_t *e);
+	int  (*env_setglobalstate)(ebgenv_t *e, uint16_t ustate);
+	int  (*env_close)(ebgenv_t *e);
+	int  (*env_finalize_update)(ebgenv_t *e);
+} libebg;
+
 static ebgenv_t ebgenv = {0};
 
-int bootloader_env_set(const char *name, const char *value)
+static int do_env_set(const char *name, const char *value)
 {
 	int ret;
 
 	errno = 0;
-	ebg_beverbose(&ebgenv, loglevel > INFOLEVEL ? true : false);
+	libebg.beverbose(&ebgenv, loglevel > INFOLEVEL ? true : false);
 
 	DEBUG("Setting %s=%s in bootloader environment", name, value);
 
-	if ((ret = ebg_env_open_current(&ebgenv)) != 0) {
+	if ((ret = libebg.env_open_current(&ebgenv)) != 0) {
 		ERROR("Cannot open current bootloader environment: %s.", strerror(ret));
 		return ret;
 	}
@@ -40,73 +54,73 @@  int bootloader_env_set(const char *name, const char *value)
 		/* Open or create a new environment to reflect
 		 * EFI Boot Guard's representation of SWUpdate's
 		 * recovery_status=in_progress. */
-		if ((ret = ebg_env_create_new(&ebgenv)) != 0) {
+		if ((ret = libebg.env_create_new(&ebgenv)) != 0) {
 			ERROR("Cannot open/create new bootloader environment: %s.",
 			     strerror(ret));
 		}
 	} else if (strncmp(name, (char *)STATE_KEY, strlen((char *)STATE_KEY) + 1) == 0) {
 		/* Map suricatta's update_state_t to EFI Boot Guard's API. */
-		if ((ret = ebg_env_setglobalstate(&ebgenv, *value - '0')) != 0) {
+		if ((ret = libebg.env_setglobalstate(&ebgenv, *value - '0')) != 0) {
 			ERROR("Cannot set %s=%s in bootloader environment.", STATE_KEY, value);
 		}
 	} else {
 		/* A new environment is created if EFI Boot Guard's
 		 * representation of SWUpdate's recovery_status is
 		 * not in_progress. */
-		if ((ret = ebg_env_create_new(&ebgenv)) != 0) {
+		if ((ret = libebg.env_create_new(&ebgenv)) != 0) {
 			ERROR("Cannot open/create new bootloader environment: %s.",
 			     strerror(ret));
 			return ret;
 		}
-		if ((ret = ebg_env_set(&ebgenv, (char *)name, (char *)value)) != 0) {
+		if ((ret = libebg.env_set(&ebgenv, (char *)name, (char *)value)) != 0) {
 			ERROR("Cannot set %s=%s in bootloader environment: %s.",
 			    name, value, strerror(ret));
 		}
 	}
-	(void)ebg_env_close(&ebgenv);
+	(void)libebg.env_close(&ebgenv);
 
 	return ret;
 }
 
-int bootloader_env_unset(const char *name)
+static int do_env_unset(const char *name)
 {
 	int ret;
 
-	ebg_beverbose(&ebgenv, loglevel > INFOLEVEL ? true : false);
+	libebg.beverbose(&ebgenv, loglevel > INFOLEVEL ? true : false);
 
-	if ((ret = ebg_env_open_current(&ebgenv)) != 0) {
+	if ((ret = libebg.env_open_current(&ebgenv)) != 0) {
 		ERROR("Cannot open current bootloader environment: %s.", strerror(ret));
 		return ret;
 	}
 
 	if (strncmp(name, BOOTVAR_TRANSACTION, strlen(name) + 1) == 0) {
-		ret = ebg_env_finalize_update(&ebgenv);
+		ret = libebg.env_finalize_update(&ebgenv);
 		if (ret) {
 			ERROR("Cannot unset %s in bootloader environment: %s.", BOOTVAR_TRANSACTION, strerror(ret));
 		}
 	} else if (strncmp(name, (char *)STATE_KEY, strlen((char *)STATE_KEY) + 1) == 0) {
 		/* Unsetting STATE_KEY is semantically equivalent to setting it to STATE_OK. */
-		if ((ret = ebg_env_setglobalstate(&ebgenv, STATE_OK - '0')) != 0) {
+		if ((ret = libebg.env_setglobalstate(&ebgenv, STATE_OK - '0')) != 0) {
 			ERROR("Cannot unset %s in bootloader environment.", STATE_KEY);
 		}
 	} else {
-		ret = ebg_env_set_ex(&ebgenv, (char *)name, USERVAR_TYPE_DELETED, (uint8_t *)"", 1);
+		ret = libebg.env_set_ex(&ebgenv, (char *)name, USERVAR_TYPE_DELETED, (uint8_t *)"", 1);
 	}
-	(void)ebg_env_close(&ebgenv);
+	(void)libebg.env_close(&ebgenv);
 
 	return ret;
 }
 
-char *bootloader_env_get(const char *name)
+static char *do_env_get(const char *name)
 {
 	char *value = NULL;
 	size_t size;
 
 	errno = 0;
-	ebg_beverbose(&ebgenv, loglevel > INFOLEVEL ? true : false);
+	libebg.beverbose(&ebgenv, loglevel > INFOLEVEL ? true : false);
 
 	int ret;
-	if ((ret = ebg_env_open_current(&ebgenv)) != 0) {
+	if ((ret = libebg.env_open_current(&ebgenv)) != 0) {
 		ERROR("Cannot open current bootloader environment: %s.",
 		     strerror(ret));
 		return NULL;
@@ -114,19 +128,19 @@  char *bootloader_env_get(const char *name)
 
 	if (strncmp(name, (char *)STATE_KEY, strlen((char *)STATE_KEY) + 1) == 0) {
 		value = (char *)malloc(sizeof(char));
-		*value = ebg_env_getglobalstate(&ebgenv);
+		*value = libebg.env_getglobalstate(&ebgenv);
 	} else {
-		if ((size = ebg_env_get(&ebgenv, (char *)name, NULL)) != 0) {
+		if ((size = libebg.env_get(&ebgenv, (char *)name, NULL)) != 0) {
 			value = malloc(size);
 			if (value) {
-				if (ebg_env_get(&ebgenv, (char *)name, value) != 0) {
+				if (libebg.env_get(&ebgenv, (char *)name, value) != 0) {
 					value = NULL;
 				}
 			}
 		}
 	}
 
-	(void)ebg_env_close(&ebgenv);
+	(void)libebg.env_close(&ebgenv);
 
 	if (value == NULL) {
 		ERROR("Cannot get %s from bootloader environment: %s",
@@ -138,7 +152,7 @@  char *bootloader_env_get(const char *name)
 	return value;
 }
 
-int bootloader_apply_list(const char *filename)
+static int do_apply_list(const char *filename)
 {
 	FILE *fp = NULL;
 	char *line = NULL;
@@ -148,7 +162,7 @@  int bootloader_apply_list(const char *filename)
 	int ret = 0;
 
 	errno = 0;
-	ebg_beverbose(&ebgenv, loglevel > INFOLEVEL ? true : false);
+	libebg.beverbose(&ebgenv, loglevel > INFOLEVEL ? true : false);
 
 	if (!(fp = fopen(filename, "rb"))) {
 		ERROR("Failed to open bootloader environment file %s: %s",
@@ -174,3 +188,37 @@  int bootloader_apply_list(const char *filename)
 	}
 	return ret;
 }
+
+static bootloader ebg = {
+	.env_get = &do_env_get,
+	.env_set = &do_env_set,
+	.env_unset = &do_env_unset,
+	.apply_list = &do_apply_list
+};
+
+static bootloader* probe(void)
+{
+	void* handle = dlopen("libebgenv.so", RTLD_NOW | RTLD_GLOBAL);
+	if (!handle) {
+		return NULL;
+	}
+
+	(void)dlerror();
+	load_symbol(handle, &libebg.beverbose, "ebg_beverbose");
+	load_symbol(handle, &libebg.env_create_new, "ebg_env_create_new");
+	load_symbol(handle, &libebg.env_open_current, "ebg_env_open_current");
+	load_symbol(handle, &libebg.env_get, "ebg_env_get");
+	load_symbol(handle, &libebg.env_set, "ebg_env_set");
+	load_symbol(handle, &libebg.env_set_ex, "ebg_env_set_ex");
+	load_symbol(handle, &libebg.env_getglobalstate, "ebg_env_getglobalstate");
+	load_symbol(handle, &libebg.env_setglobalstate, "ebg_env_setglobalstate");
+	load_symbol(handle, &libebg.env_close, "ebg_env_close");
+	load_symbol(handle, &libebg.env_finalize_update, "ebg_env_finalize_update");
+	return &ebg;
+}
+
+__attribute__((constructor))
+static void ebg_probe(void)
+{
+	(void)register_bootloader("ebg", probe());
+}
diff --git a/bootloader/grub.c b/bootloader/grub.c
index 0dbdc55..2d901b3 100644
--- a/bootloader/grub.c
+++ b/bootloader/grub.c
@@ -258,7 +258,7 @@  static inline void grubenv_close(struct grubenv_t *grubenv)
 /* I feel that '#' and '=' characters should be forbidden. Although it's not
  * explicitly mentioned in original grub env code, they may cause unexpected
  * behavior */
-int bootloader_env_set(const char *name, const char *value)
+static int do_env_set(const char *name, const char *value)
 {
 	static struct grubenv_t grubenv;
 	int ret;
@@ -280,7 +280,7 @@  cleanup:
 	return ret;
 }
 
-int bootloader_env_unset(const char *name)
+static int do_env_unset(const char *name)
 {
 	static struct grubenv_t grubenv;
 	int ret = 0;
@@ -301,7 +301,7 @@  cleanup:
 	return ret;
 }
 
-char *bootloader_env_get(const char *name)
+static char *do_env_get(const char *name)
 {
 	static struct grubenv_t grubenv;
 	char *value = NULL, *var;
@@ -322,7 +322,7 @@  cleanup:
 
 }
 
-int bootloader_apply_list(const char *script)
+static int do_apply_list(const char *script)
 {
 	static struct grubenv_t grubenv;
 	int ret = 0;
@@ -343,3 +343,16 @@  cleanup:
 	grubenv_close(&grubenv);
 	return ret;
 }
+
+static bootloader grub = {
+	.env_get = &do_env_get,
+	.env_set = &do_env_set,
+	.env_unset = &do_env_unset,
+	.apply_list = &do_apply_list
+};
+
+__attribute__((constructor))
+static void grub_probe(void)
+{
+	(void)register_bootloader("grub", &grub);
+}
diff --git a/bootloader/none.c b/bootloader/none.c
index 67d01e0..d9d1c0b 100644
--- a/bootloader/none.c
+++ b/bootloader/none.c
@@ -12,7 +12,7 @@ 
 
 static struct dict environment;
 
-int bootloader_env_set(const char *name,
+static int do_env_set(const char *name,
 			const char  *value)
 {
 	dict_set_value(&environment, name, value);
@@ -20,14 +20,14 @@  int bootloader_env_set(const char *name,
 	return 0;
 }
 
-int bootloader_env_unset(const char *name)
+static int do_env_unset(const char *name)
 {
 	dict_remove(&environment, name);
 
 	return 0;
 }
 
-char *bootloader_env_get(const char  *name)
+static char *do_env_get(const char  *name)
 {
 	char *value = NULL, *var;
 
@@ -39,7 +39,20 @@  char *bootloader_env_get(const char  *name)
 	return value;
 }
 
-int bootloader_apply_list(const char *filename)
+static int do_apply_list(const char *filename)
 {
 	return dict_parse_script(&environment, filename);
 }
+
+static bootloader none = {
+	.env_get = &do_env_get,
+	.env_set = &do_env_set,
+	.env_unset = &do_env_unset,
+	.apply_list = &do_apply_list
+};
+
+__attribute__((constructor))
+static void none_probe(void)
+{
+	(void)register_bootloader("none", &none);
+}
diff --git a/bootloader/uboot.c b/bootloader/uboot.c
index 86d368b..729cf16 100644
--- a/bootloader/uboot.c
+++ b/bootloader/uboot.c
@@ -17,6 +17,7 @@ 
 #include <dirent.h>
 #include "generated/autoconf.h"
 #include "util.h"
+#include "dlfcn.h"
 #include "bootloader.h"
 
 #include <libuboot.h>
@@ -24,19 +25,31 @@ 
 #define CONFIG_UBOOT_DEFAULTENV	"/etc/u-boot-initial-env"
 #endif
 
+static struct {
+	int   (*open)(struct uboot_ctx*);
+	void  (*close)(struct uboot_ctx *ctx);
+	void  (*exit)(struct uboot_ctx *ctx);
+	int   (*initialize)(struct uboot_ctx **out, struct uboot_env_device *envdevs);
+	char* (*get_env)(struct uboot_ctx *ctx, const char *varname);
+	int   (*read_config)(struct uboot_ctx *ctx, const char *config);
+	int   (*load_file)(struct uboot_ctx *ctx, const char *filename);
+	int   (*set_env)(struct uboot_ctx *ctx, const char *varname, const char *value);
+	int   (*env_store)(struct uboot_ctx *ctx);
+} libuboot;
+
 static int bootloader_initialize(struct uboot_ctx **ctx)
 {
-	if (libuboot_initialize(ctx, NULL) < 0) {
+	if (libuboot.initialize(ctx, NULL) < 0) {
 		ERROR("Error: environment not initialized");
 		return -ENODEV;
 	}
-	if (libuboot_read_config(*ctx, CONFIG_UBOOT_FWENV) < 0) {
+	if (libuboot.read_config(*ctx, CONFIG_UBOOT_FWENV) < 0) {
 		ERROR("Configuration file %s wrong or corrupted", CONFIG_UBOOT_FWENV);
 		return -EINVAL;
 	}
-	if (libuboot_open(*ctx) < 0) {
+	if (libuboot.open(*ctx) < 0) {
 		WARN("Cannot read environment, using default");
-		if (libuboot_load_file(*ctx, CONFIG_UBOOT_DEFAULTENV) < 0) {
+		if (libuboot.load_file(*ctx, CONFIG_UBOOT_DEFAULTENV) < 0) {
 			ERROR("Error: Cannot read default environment from file");
 			return -ENODATA;
 		}
@@ -45,47 +58,47 @@  static int bootloader_initialize(struct uboot_ctx **ctx)
 	return 0;
 }
 
-int bootloader_env_set(const char *name, const char *value)
+static int do_env_set(const char *name, const char *value)
 {
 	int ret;
 	struct uboot_ctx *ctx = NULL;
 
 	ret = bootloader_initialize(&ctx);
 	if (!ret) {
-		libuboot_set_env(ctx, name, value);
-		ret = libuboot_env_store(ctx);
+		libuboot.set_env(ctx, name, value);
+		ret = libuboot.env_store(ctx);
 	}
 
-	libuboot_close(ctx);
-	libuboot_exit(ctx);
+	libuboot.close(ctx);
+	libuboot.exit(ctx);
 
 	return ret;
 }
 
-int bootloader_env_unset(const char *name)
+static int do_env_unset(const char *name)
 {
-	return bootloader_env_set(name, NULL);
+	return do_env_set(name, NULL);
 }
 
 
-int bootloader_apply_list(const char *filename)
+static int do_apply_list(const char *filename)
 {
 	int ret;
 	struct uboot_ctx *ctx = NULL;
 
 	ret = bootloader_initialize(&ctx);
 	if (!ret) {
-		libuboot_load_file(ctx, filename);
-		ret = libuboot_env_store(ctx);
+		libuboot.load_file(ctx, filename);
+		ret = libuboot.env_store(ctx);
 	}
 
-	libuboot_close(ctx);
-	libuboot_exit(ctx);
+	libuboot.close(ctx);
+	libuboot.exit(ctx);
 
 	return ret;
 }
 
-char *bootloader_env_get(const char *name)
+static char *do_env_get(const char *name)
 {
 	int ret;
 	struct uboot_ctx *ctx = NULL;
@@ -93,10 +106,43 @@  char *bootloader_env_get(const char *name)
 
 	ret = bootloader_initialize(&ctx);
 	if (!ret) {
-		value = libuboot_get_env(ctx, name);
+		value = libuboot.get_env(ctx, name);
 	}
-	libuboot_close(ctx);
-	libuboot_exit(ctx);
+	libuboot.close(ctx);
+	libuboot.exit(ctx);
 
 	return value;
 }
+
+static bootloader uboot = {
+	.env_get = &do_env_get,
+	.env_set = &do_env_set,
+	.env_unset = &do_env_unset,
+	.apply_list = &do_apply_list
+};
+
+static bootloader* probe(void)
+{
+	void* handle = dlopen("libubootenv.so", RTLD_NOW | RTLD_GLOBAL);
+	if (!handle) {
+		return NULL;
+	}
+
+	(void)dlerror();
+	load_symbol(handle, &libuboot.open, "libuboot_open");
+	load_symbol(handle, &libuboot.close, "libuboot_close");
+	load_symbol(handle, &libuboot.exit, "libuboot_exit");
+	load_symbol(handle, &libuboot.initialize, "libuboot_initialize");
+	load_symbol(handle, &libuboot.get_env, "libuboot_get_env");
+	load_symbol(handle, &libuboot.read_config, "libuboot_read_config");
+	load_symbol(handle, &libuboot.load_file, "libuboot_load_file");
+	load_symbol(handle, &libuboot.set_env, "libuboot_set_env");
+	load_symbol(handle, &libuboot.env_store, "libuboot_env_store");
+	return &uboot;
+}
+
+__attribute__((constructor))
+static void uboot_probe(void)
+{
+	(void)register_bootloader("uboot", probe());
+}