boot: Add logic to enable booting from fallback option

The "fallback" extlinux config option allows us to set an alternative
default boot option for when it has been detected that the default is
failing. Implement the logic required to boot from this option when
desired.

Signed-off-by: Martyn Welch <martyn.welch@collabora.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
diff --git a/boot/bootmeth_extlinux.c b/boot/bootmeth_extlinux.c
index fbb05ef..26c61a6 100644
--- a/boot/bootmeth_extlinux.c
+++ b/boot/bootmeth_extlinux.c
@@ -149,7 +149,7 @@
 	info.dev = dev;
 	info.bflow = bflow;
 	ret = pxe_setup_ctx(&ctx, &cmdtp, extlinux_getfile, &info, true,
-			    bflow->fname, false);
+			    bflow->fname, false, false);
 	if (ret)
 		return log_msg_ret("ctx", -EINVAL);
 
diff --git a/boot/bootmeth_pxe.c b/boot/bootmeth_pxe.c
index 03d2589..05c6bec 100644
--- a/boot/bootmeth_pxe.c
+++ b/boot/bootmeth_pxe.c
@@ -150,7 +150,7 @@
 	info.bflow = bflow;
 	info.cmdtp = &cmdtp;
 	ret = pxe_setup_ctx(ctx, &cmdtp, extlinux_pxe_getfile, &info, false,
-			    bflow->subdir, false);
+			    bflow->subdir, false, false);
 	if (ret)
 		return log_msg_ret("ctx", -EINVAL);
 
diff --git a/boot/pxe_utils.c b/boot/pxe_utils.c
index a80119c..d6a4b2c 100644
--- a/boot/pxe_utils.c
+++ b/boot/pxe_utils.c
@@ -1436,6 +1436,16 @@
 
 	buf = map_sysmem(menucfg, 0);
 	r = parse_pxefile_top(ctx, buf, menucfg, cfg, 1);
+
+	if (ctx->use_fallback) {
+		if (cfg->fallback_label) {
+			printf("Setting use of fallback\n");
+			cfg->default_label = cfg->fallback_label;
+		} else {
+			printf("Selected fallback option, but not set\n");
+		}
+	}
+
 	unmap_sysmem(buf);
 	if (r < 0) {
 		destroy_pxe_menu(cfg);
@@ -1586,7 +1596,8 @@
 
 int pxe_setup_ctx(struct pxe_context *ctx, struct cmd_tbl *cmdtp,
 		  pxe_getfile_func getfile, void *userdata,
-		  bool allow_abs_path, const char *bootfile, bool use_ipv6)
+		  bool allow_abs_path, const char *bootfile, bool use_ipv6,
+		  bool use_fallback)
 {
 	const char *last_slash;
 	size_t path_len = 0;
@@ -1597,6 +1608,7 @@
 	ctx->userdata = userdata;
 	ctx->allow_abs_path = allow_abs_path;
 	ctx->use_ipv6 = use_ipv6;
+	ctx->use_fallback = use_fallback;
 
 	/* figure out the boot directory, if there is one */
 	if (bootfile && strlen(bootfile) >= MAX_TFTP_PATH_LEN)
diff --git a/cmd/pxe.c b/cmd/pxe.c
index ae02c28..982e2b1 100644
--- a/cmd/pxe.c
+++ b/cmd/pxe.c
@@ -138,7 +138,7 @@
 	int i;
 
 	if (pxe_setup_ctx(&ctx, cmdtp, do_get_tftp, NULL, false,
-			  env_get("bootfile"), use_ipv6))
+			  env_get("bootfile"), use_ipv6, false))
 		return -ENOMEM;
 
 	if (IS_ENABLED(CONFIG_BOOTP_PXE_DHCP_OPTION) &&
@@ -288,7 +288,7 @@
 	}
 
 	if (pxe_setup_ctx(&ctx, cmdtp, do_get_tftp, NULL, false,
-			  env_get("bootfile"), use_ipv6)) {
+			  env_get("bootfile"), use_ipv6, false)) {
 		printf("Out of memory\n");
 		return CMD_RET_FAILURE;
 	}
diff --git a/cmd/sysboot.c b/cmd/sysboot.c
index 0ea08fd..8a06078 100644
--- a/cmd/sysboot.c
+++ b/cmd/sysboot.c
@@ -105,7 +105,7 @@
 	}
 
 	if (pxe_setup_ctx(&ctx, cmdtp, sysboot_read_file, &info, true,
-			  filename, false)) {
+			  filename, false, false)) {
 		printf("Out of memory\n");
 		return CMD_RET_FAILURE;
 	}
diff --git a/include/pxe_utils.h b/include/pxe_utils.h
index a408fb7..68ac40b 100644
--- a/include/pxe_utils.h
+++ b/include/pxe_utils.h
@@ -96,6 +96,8 @@
  *	allocated
  * @pxe_file_size: Size of the PXE file
  * @use_ipv6: TRUE : use IPv6 addressing, FALSE : use IPv4 addressing
+ * @use_fallback: TRUE : use "fallback" option as default, FALSE : use
+ *	"default" option as default
  */
 struct pxe_context {
 	struct cmd_tbl *cmdtp;
@@ -116,6 +118,7 @@
 	char *bootdir;
 	ulong pxe_file_size;
 	bool use_ipv6;
+	bool use_fallback;
 };
 
 /**
@@ -215,12 +218,17 @@
  *	none
  * @use_ipv6: TRUE : use IPv6 addressing
  *            FALSE : use IPv4 addressing
+ * @use_fallback: TRUE : Use "fallback" option instead of "default" should no
+ *                       other choice be selected
+ *                FALSE : Use "default" option should no other choice be
+ *                        selected
  * Return: 0 if OK, -ENOMEM if out of memory, -E2BIG if bootfile is larger than
  *	MAX_TFTP_PATH_LEN bytes
  */
 int pxe_setup_ctx(struct pxe_context *ctx, struct cmd_tbl *cmdtp,
 		  pxe_getfile_func getfile, void *userdata,
-		  bool allow_abs_path, const char *bootfile, bool use_ipv6);
+		  bool allow_abs_path, const char *bootfile, bool use_ipv6,
+		  bool use_fallback);
 
 /**
  * pxe_destroy_ctx() - Destroy a PXE context