diff mbox series

[3/4] bootstd: Add command to enable setting of bootmeth specific properties

Message ID 20241002132633.246942-3-martyn.welch@collabora.com
State New
Delegated to: Tom Rini
Headers show
Series [1/4] boot: pxe_utils: Add fallback support | expand

Commit Message

Martyn Welch Oct. 2, 2024, 1:26 p.m. UTC
We have previously added logic to allow a "fallback" option to be
specified in the extlinux configuration. Provide a command that allows
us to set this as the preferred default option when booting.

Combined with the bootcount functionality, this allows the "altbootcmd"
to provide a means of falling back to a previously known good state
after a failed update. For example, if "bootcmd" is set to:

    bootflow scan -lb

We would set "altbootcmd" to:

    bootmeth set extlinux fallback 1; bootflow scan -lb

Causing the boot process to boot from the fallback option.

Signed-off-by: Martyn Welch <martyn.welch@collabora.com>
---
 boot/bootmeth-uclass.c   | 25 ++++++++++++++
 boot/bootmeth_extlinux.c | 73 +++++++++++++++++++++++++++++++++++++++-
 cmd/bootmeth.c           | 25 ++++++++++++--
 include/bootmeth.h       | 31 +++++++++++++++++
 4 files changed, 151 insertions(+), 3 deletions(-)
diff mbox series

Patch

diff --git a/boot/bootmeth-uclass.c b/boot/bootmeth-uclass.c
index c0abadef97..5b5fea39b3 100644
--- a/boot/bootmeth-uclass.c
+++ b/boot/bootmeth-uclass.c
@@ -251,6 +251,31 @@  int bootmeth_set_order(const char *order_str)
 	return 0;
 }
 
+int bootmeth_set_property(const char *name, const char *property, const char *value)
+{
+	int ret;
+	int len;
+	struct udevice *dev;
+	const struct bootmeth_ops *ops;
+
+	len = strlen(name);
+
+	ret = uclass_find_device_by_namelen(UCLASS_BOOTMETH, name, len,
+					    &dev);
+	if (ret) {
+		printf("Unknown bootmeth '%s'\n", name);
+		return ret;
+	}
+
+	ops = bootmeth_get_ops(dev);
+	if (!ops->set_property) {
+		printf("set_property not found\n");
+		return -ENODEV;
+	}
+
+	return ops->set_property(dev, property, value);
+}
+
 int bootmeth_setup_fs(struct bootflow *bflow, struct blk_desc *desc)
 {
 	int ret;
diff --git a/boot/bootmeth_extlinux.c b/boot/bootmeth_extlinux.c
index 26c61a65e2..be8fbf4df6 100644
--- a/boot/bootmeth_extlinux.c
+++ b/boot/bootmeth_extlinux.c
@@ -21,6 +21,39 @@ 
 #include <mmc.h>
 #include <pxe_utils.h>
 
+struct extlinux_plat {
+	bool use_fallback;
+};
+
+enum extlinux_option_type {
+	EO_FALLBACK,
+	EO_INVALID
+};
+
+struct extlinux_option {
+	char *name;
+	enum extlinux_option_type option;
+};
+
+static const struct extlinux_option options[] = {
+	{"fallback", EO_FALLBACK},
+	{NULL, EO_INVALID}
+};
+
+static enum extlinux_option_type get_option(const char *option)
+{
+	int i = 0;
+
+	while (options[i].name) {
+		if (!strcmp(options[i].name, option))
+			return options[i].option;
+
+		i++;
+	}
+
+	return EO_INVALID;
+};
+
 static int extlinux_get_state_desc(struct udevice *dev, char *buf, int maxsize)
 {
 	if (IS_ENABLED(CONFIG_SANDBOX)) {
@@ -142,14 +175,18 @@  static int extlinux_boot(struct udevice *dev, struct bootflow *bflow)
 	struct cmd_tbl cmdtp = {};	/* dummy */
 	struct pxe_context ctx;
 	struct extlinux_info info;
+	struct extlinux_plat *plat;
 	ulong addr;
 	int ret;
 
 	addr = map_to_sysmem(bflow->buf);
 	info.dev = dev;
 	info.bflow = bflow;
+
+	plat = dev_get_plat(dev);
+
 	ret = pxe_setup_ctx(&ctx, &cmdtp, extlinux_getfile, &info, true,
-			    bflow->fname, false, false);
+			    bflow->fname, false, plat->use_fallback);
 	if (ret)
 		return log_msg_ret("ctx", -EINVAL);
 
@@ -160,6 +197,38 @@  static int extlinux_boot(struct udevice *dev, struct bootflow *bflow)
 	return 0;
 }
 
+static int extlinux_set_property(struct udevice *dev, const char *property, const char *value)
+{
+	struct extlinux_plat *plat;
+	static enum extlinux_option_type option;
+
+	plat = dev_get_plat(dev);
+
+	option = get_option(property);
+	if (option == EO_INVALID) {
+		printf("Invalid option\n");
+		return -EINVAL;
+	}
+
+	switch (option) {
+	case EO_FALLBACK:
+		if (!strcmp(value, "1")) {
+			plat->use_fallback = true;
+		} else if (!strcmp(value, "0")) {
+			plat->use_fallback = false;
+		} else {
+			printf("Unexpected value '%s'\n", value);
+			return -EINVAL;
+		}
+		break;
+	default:
+		printf("Unrecognised property '%s'\n", property);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 static int extlinux_bootmeth_bind(struct udevice *dev)
 {
 	struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev);
@@ -176,6 +245,7 @@  static struct bootmeth_ops extlinux_bootmeth_ops = {
 	.read_bootflow	= extlinux_read_bootflow,
 	.read_file	= bootmeth_common_read_file,
 	.boot		= extlinux_boot,
+	.set_property	= extlinux_set_property,
 };
 
 static const struct udevice_id extlinux_bootmeth_ids[] = {
@@ -190,4 +260,5 @@  U_BOOT_DRIVER(bootmeth_1extlinux) = {
 	.of_match	= extlinux_bootmeth_ids,
 	.ops		= &extlinux_bootmeth_ops,
 	.bind		= extlinux_bootmeth_bind,
+	.plat_auto	= sizeof(struct extlinux_plat)
 };
diff --git a/cmd/bootmeth.c b/cmd/bootmeth.c
index ebf8b7e253..2f41fa1bec 100644
--- a/cmd/bootmeth.c
+++ b/cmd/bootmeth.c
@@ -103,10 +103,31 @@  static int do_bootmeth_order(struct cmd_tbl *cmdtp, int flag, int argc,
 	return 0;
 }
 
+static int do_bootmeth_set(struct cmd_tbl *cmdtp, int flag, int argc,
+			     char *const argv[])
+{
+	int ret;
+
+	if (argc < 4) {
+		printf("Required parameters not provided\n");
+		return CMD_RET_FAILURE;
+	}
+
+	ret = bootmeth_set_property(argv[1], argv[2], argv[3]);
+	if (ret) {
+		printf("Failed (err=%d)\n", ret);
+		return CMD_RET_FAILURE;
+	}
+
+	return 0;
+}
+
 U_BOOT_LONGHELP(bootmeth,
 	"list [-a]     - list available bootmeths (-a all)\n"
-	"bootmeth order [<bd> ...]  - select bootmeth order / subset to use");
+	"bootmeth order [<bd> ...]  - select bootmeth order / subset to use\n"
+	"bootmeth set <bootmeth> <property> <value> - set optional property");
 
 U_BOOT_CMD_WITH_SUBCMDS(bootmeth, "Boot methods", bootmeth_help_text,
 	U_BOOT_SUBCMD_MKENT(list, 2, 1, do_bootmeth_list),
-	U_BOOT_SUBCMD_MKENT(order, CONFIG_SYS_MAXARGS, 1, do_bootmeth_order));
+	U_BOOT_SUBCMD_MKENT(order, CONFIG_SYS_MAXARGS, 1, do_bootmeth_order),
+	U_BOOT_SUBCMD_MKENT(set, CONFIG_SYS_MAXARGS, 1, do_bootmeth_set));
diff --git a/include/bootmeth.h b/include/bootmeth.h
index 4d8ca48efd..a08ebf005a 100644
--- a/include/bootmeth.h
+++ b/include/bootmeth.h
@@ -146,6 +146,22 @@  struct bootmeth_ops {
 	 *	something changes, other -ve on other error
 	 */
 	int (*boot)(struct udevice *dev, struct bootflow *bflow);
+
+	/**
+	 * set_property() - set the bootmeth property
+	 *
+	 * This allows the setting of boot method specific properties to enable
+	 * automated finer grain control of the boot process
+	 *
+	 * @name: String containing the name of the relevant boot method
+	 * @property: String containing the name of the property to set
+	 * @value: String containing the value to be set for the specified
+	 *         property
+	 * Return: 0 if OK, -ENODEV if an unknown bootmeth or property is
+	 *      provided, -ENOENT if there are no bootmeth devices
+	 */
+	int (*set_property)(struct udevice *dev, const char *property,
+			    const char *value);
 };
 
 #define bootmeth_get_ops(dev)  ((struct bootmeth_ops *)(dev)->driver->ops)
@@ -290,6 +306,21 @@  int bootmeth_setup_iter_order(struct bootflow_iter *iter, bool include_global);
  */
 int bootmeth_set_order(const char *order_str);
 
+/**
+ * bootmeth_set_property() - Set the bootmeth property
+ *
+ * This allows the setting of boot method specific properties to enable
+ * automated finer grain control of the boot process
+ *
+ * @name: String containing the name of the relevant boot method
+ * @property: String containing the name of the property to set
+ * @value: String containing the value to be set for the specified property
+ * Return: 0 if OK, -ENODEV if an unknown bootmeth or property is provided,
+ * -ENOENT if there are no bootmeth devices
+ */
+int bootmeth_set_property(const char *name, const char *property,
+			  const char *value);
+
 /**
  * bootmeth_setup_fs() - Set up read to read a file
  *