Add a screen showing a menu for alternative firmware

At present we allow the user to press a keypad number to boot into another
bootloader but there is no indication which one is which.

Add a new screen for this. It is entered via Ctrl-L and shows the
available bootloaders, along with the number to press for each. The
contents of the screen is rendered by the bootloader, as usual.

This is supported by two new screens, one for the keyboard UI and one for
the menu UI. Also a new function, VbExGetAltFwIdxMask(), is added to find
out what bootloaders are available.

Note: This CL combines changes for both UIs. The changes may be easier to
review separately.

CQ-DEPEND=CL:1273269
BUG=chromium:837018
BRANCH=none
TEST=FEATURES=test emerge-grunt --nodeps vboot_reference

Change-Id: Ib3227545dc677c8f9587944753e32f3b49647360
Signed-off-by: Simon Glass <sjg@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/1273268
Reviewed-by: Julius Werner <jwerner@chromium.org>
diff --git a/firmware/include/vboot_api.h b/firmware/include/vboot_api.h
index 9d0ec3f..5eedcaa 100644
--- a/firmware/include/vboot_api.h
+++ b/firmware/include/vboot_api.h
@@ -667,6 +667,10 @@
 	VB_SCREEN_OPTIONS_MENU = 0x210,
 	/* Alt OS picker screen */
 	VB_SCREEN_ALT_OS = 0x211,
+	/* Alt firmware picker screen (for keyboard UI) */
+	VB_SCREEN_ALT_FW_PICK = 0x212,
+	/* Alt firmware menu screen (for detachable UI ) */
+	VB_SCREEN_ALT_FW_MENU = 0x213,
 };
 
 /**
@@ -994,6 +998,21 @@
  */
 VbError_t VbExGetLocalizationCount(uint32_t *count);
 
+enum vb_altfw {
+	VB_ALTFW_COUNT	= 9,	/* We allow 9 bootloaders, numbered 1-9 */
+};
+
+/**
+ * Get a mask of available alternative firmware options
+ *
+ * There are up to 9 bootloaders, numbered 1 to 9, using bits 1 to 9 of this
+ * mask. Bit 0 is unused.
+ *
+ * @return Bit mask indicating which bootloaders are present (bit n indicates
+ *	bootloader n is present)
+ */
+uint32_t VbExGetAltFwIdxMask(void);
+
 #ifdef __cplusplus
 }
 #endif  /* __cplusplus */
diff --git a/firmware/lib/include/vboot_ui_common.h b/firmware/lib/include/vboot_ui_common.h
index 19c3809..30ade5a 100644
--- a/firmware/lib/include/vboot_ui_common.h
+++ b/firmware/lib/include/vboot_ui_common.h
@@ -28,4 +28,21 @@
  */
 void vb2_run_altfw(int altfw_num);
 
+/** Display an error and beep to indicate that altfw is not available */
+void vb2_error_no_altfw(void);
+
+/**
+ * Jump to a bootloader if possible
+ *
+ * This checks if the operation is permitted. If it is, then it jumps to the
+ * selected bootloader and execution continues there, never returning.
+ *
+ * If the operation is not permitted, or it is permitted but the bootloader
+ * cannot be found, it beeps and returns.
+ *
+ * @allowed	1 if allowed, 0 if not allowed
+ * @altfw_num	Number of bootloader to start (0=any, 1=first, etc.)
+ */
+void vb2_try_alt_fw(int allowed, int altfw_num);
+
 #endif  /* VBOOT_REFERENCE_VBOOT_UI_COMMON_H_ */
diff --git a/firmware/lib/include/vboot_ui_menu_private.h b/firmware/lib/include/vboot_ui_menu_private.h
index e07ece0..828a4a0 100644
--- a/firmware/lib/include/vboot_ui_menu_private.h
+++ b/firmware/lib/include/vboot_ui_menu_private.h
@@ -33,6 +33,7 @@
 	VB_MENU_RECOVERY_NO_GOOD,
 	VB_MENU_RECOVERY_BROKEN,
 	VB_MENU_TO_NORM_CONFIRMED,
+	VB_MENU_ALT_FW,
 	VB_MENU_COUNT,
 } VB_MENU;
 
diff --git a/firmware/lib/vboot_display.c b/firmware/lib/vboot_display.c
index b34976d..c760772 100644
--- a/firmware/lib/vboot_display.c
+++ b/firmware/lib/vboot_display.c
@@ -30,6 +30,11 @@
 	return VBERROR_UNKNOWN;
 }
 
+__attribute__((weak))
+VbError_t VbExGetAltFwIdxMask(void) {
+	return 0;
+}
+
 VbError_t VbDisplayScreen(struct vb2_context *ctx, uint32_t screen, int force)
 {
 	uint32_t locale;
diff --git a/firmware/lib/vboot_ui.c b/firmware/lib/vboot_ui.c
index 63ae9cc..71a7ceb 100644
--- a/firmware/lib/vboot_ui.c
+++ b/firmware/lib/vboot_ui.c
@@ -73,31 +73,6 @@
 	return !!shutdown_request;
 }
 
-/**
- * Jump to a bootloader if possible
- *
- * This checks if the operation is permitted. If it is, then it jumps to the
- * selected bootloader and execution continues there, never returning.
- *
- * If the operation is not permitted, or it is permitted but the bootloader
- * cannot be found, it beeps and returns.
- *
- * @allowed	1 if allowed, 0 if not allowed
- * @altfw_num	Number of bootloader to start (0=any, 1=first, etc.)
- */
-static void vb2_try_alt_fw(int allowed, int altfw_num)
-{
-	if (allowed) {
-		vb2_run_altfw(altfw_num);	/* will not return if found */
-	} else {
-		VB2_DEBUG("VbBootDeveloper() - Legacy boot is disabled\n");
-		VbExDisplayDebugInfo("WARNING: Booting legacy BIOS has not "
-				     "been enabled. Refer to the developer"
-				     "-mode documentation for details.\n");
-		vb2_error_beep(VB_BEEP_NOT_ALLOWED);
-	}
-}
-
 uint32_t VbTryUsb(struct vb2_context *ctx)
 {
 	uint32_t retval = VbTryLoadKernel(ctx, VB_DISK_FLAG_REMOVABLE);
@@ -186,6 +161,62 @@
 /* Delay in developer ui */
 #define DEV_KEY_DELAY        20       /* Check keys every 20ms */
 
+/*
+ * User interface for selecting alternative firmware
+ *
+ * This shows the user a list of bootloaders and allows selection of one of
+ * them. We loop forever until something is chosen or Escape is pressed.
+ */
+VbError_t vb2_altfw_ui(struct vb2_context *ctx)
+{
+	int active = 1;
+
+	VbDisplayScreen(ctx, VB_SCREEN_ALT_FW_PICK, 0);
+
+	/* We'll loop until the user decides what to do */
+	do {
+		uint32_t key = VbExKeyboardRead();
+
+		if (VbWantShutdown(ctx, key)) {
+			VB2_DEBUG("VbBootDeveloper() - shutdown requested!\n");
+			return VBERROR_SHUTDOWN_REQUESTED;
+		}
+		switch (key) {
+		case 0:
+			/* nothing pressed */
+			break;
+		case VB_KEY_ESC:
+			/* Escape pressed - return to developer screen */
+			VB2_DEBUG("VbBootDeveloper() - user pressed Esc:"
+				  "exit to Developer screen\n");
+			active = 0;
+			break;
+		/* We allow selection of the default '0' bootloader here */
+		case '0'...'9':
+			VB2_DEBUG("VbBootDeveloper() - "
+				  "user pressed key '%c': Boot alternative "
+				  "firmware\n", key);
+			/*
+			 * This will not return if successful. Drop out to
+			 * developer mode on failure.
+			 */
+			vb2_run_altfw(key - '0');
+			active = 0;
+			break;
+		default:
+			VB2_DEBUG("VbBootDeveloper() - pressed key %d\n", key);
+			VbCheckDisplayKey(ctx, key);
+			break;
+		}
+		VbExSleepMs(DEV_KEY_DELAY);
+	} while (active);
+
+	/* Back to developer screen */
+	VbDisplayScreen(ctx, VB_SCREEN_DEVELOPER_WARNING, 0);
+
+	return 0;
+}
+
 static const char dev_disable_msg[] =
 	"Developer mode is disabled on this device by system policy.\n"
 	"For more information, see http://dev.chromium.org/chromium-os/fwmp\n"
@@ -349,10 +380,17 @@
 			break;
 		case 0x0c:
 			VB2_DEBUG("VbBootDeveloper() - "
-				  "user pressed Ctrl+L; Try legacy boot\n");
-			vb2_try_alt_fw(allow_legacy, 0);
-			break;
+				  "user pressed Ctrl+L; Try alt firmware\n");
+			if (allow_legacy) {
+				int ret;
 
+				ret = vb2_altfw_ui(ctx);
+				if (ret)
+					return ret;
+			} else {
+				vb2_error_no_altfw();
+			}
+			break;
 		case VB_KEY_CTRL_ENTER:
 			/*
 			 * The Ctrl-Enter is special for Lumpy test purpose;
@@ -386,7 +424,8 @@
 				}
 			}
 			break;
-		case '1'...'9':
+		/* We allow selection of the default '0' bootloader here */
+		case '0'...'9':
 			VB2_DEBUG("VbBootDeveloper() - "
 				  "user pressed key '%c': Boot alternative "
 				  "firmware\n", key);
diff --git a/firmware/lib/vboot_ui_common.c b/firmware/lib/vboot_ui_common.c
index 727ed77..df1615d 100644
--- a/firmware/lib/vboot_ui_common.c
+++ b/firmware/lib/vboot_ui_common.c
@@ -38,3 +38,20 @@
 		VbExLegacy(altfw_num);	/* will not return if found */
 	vb2_error_beep(VB_BEEP_FAILED);
 }
+
+void vb2_error_no_altfw(void)
+{
+	VB2_DEBUG("Legacy boot is disabled\n");
+	VbExDisplayDebugInfo("WARNING: Booting legacy BIOS has not been "
+			     "enabled. Refer to the developer-mode "
+			     "documentation for details.\n");
+	vb2_error_beep(VB_BEEP_NOT_ALLOWED);
+}
+
+void vb2_try_alt_fw(int allowed, int altfw_num)
+{
+	if (allowed)
+		vb2_run_altfw(altfw_num);	/* will not return if found */
+	else
+		vb2_error_no_altfw();
+}
diff --git a/firmware/lib/vboot_ui_menu.c b/firmware/lib/vboot_ui_menu.c
index 9bbc2c7..e547f38 100644
--- a/firmware/lib/vboot_ui_menu.c
+++ b/firmware/lib/vboot_ui_menu.c
@@ -36,6 +36,7 @@
 static uint32_t disable_dev_boot;
 static uint32_t altfw_allowed;
 static struct vb2_menu menus[];
+static const char no_legacy[] = "Legacy boot failed. Missing BIOS?\n";
 
 /**
  * Checks GBB flags against VbExIsShutdownRequested() shutdown request to
@@ -86,7 +87,6 @@
 {
 	prev_menu = current_menu;
 	current_menu = new_current_menu;
-	current_menu_idx = new_current_menu_idx;
 
 	/* Reconfigure disabled_idx_mask for the new menu */
 	disabled_idx_mask = 0;
@@ -97,6 +97,19 @@
 	if (current_menu == VB_MENU_TO_NORM &&
 	    disable_dev_boot == 1)
 		disabled_idx_mask |= 1 << VB_TO_NORM_CANCEL;
+
+	/* Enable menu items for the selected bootloaders */
+	if (current_menu == VB_MENU_ALT_FW) {
+		disabled_idx_mask = ~(VbExGetAltFwIdxMask() >> 1);
+
+		/* Make sure 'cancel' is shown even with an invalid mask */
+		disabled_idx_mask &= (1 << VB_ALTFW_COUNT) - 1;
+	}
+	/* We assume that there is at least one enabled item */
+	while ((1 << new_current_menu_idx) & disabled_idx_mask)
+		new_current_menu_idx++;
+	if (new_current_menu_idx < menus[current_menu].size)
+		current_menu_idx = new_current_menu_idx;
 }
 
 /************************
@@ -118,8 +131,6 @@
 /* Boot legacy BIOS if allowed and available. */
 static VbError_t boot_legacy_action(struct vb2_context *ctx)
 {
-	const char no_legacy[] = "Legacy boot failed. Missing BIOS?\n";
-
 	if (disable_dev_boot) {
 		vb2_flash_screen(ctx);
 		vb2_error_beep(VB_BEEP_NOT_ALLOWED);
@@ -258,6 +269,26 @@
 	return VBERROR_KEEP_LOOPING;
 }
 
+/* Boot alternative bootloader if allowed and available. */
+static VbError_t enter_altfw_menu(struct vb2_context *ctx)
+{
+	VB2_DEBUG("enter_altfw_menu()\n");
+	if (disable_dev_boot) {
+		vb2_flash_screen(ctx);
+		vb2_error_beep(VB_BEEP_NOT_ALLOWED);
+		return VBERROR_KEEP_LOOPING;
+	}
+	if (!altfw_allowed) {
+		vb2_flash_screen(ctx);
+		vb2_error_no_altfw();
+		return VBERROR_KEEP_LOOPING;
+	}
+	vb2_change_menu(VB_MENU_ALT_FW, 0);
+	vb2_draw_current_screen(ctx);
+
+	return VBERROR_KEEP_LOOPING;
+}
+
 static VbError_t debug_info_action(struct vb2_context *ctx)
 {
 	VbDisplayDebugInfo(ctx);
@@ -298,6 +329,17 @@
 	}
 }
 
+/* Action when selecting a bootloader in the alternative firmware menu. */
+static VbError_t altfw_action(struct vb2_context *ctx)
+{
+	vb2_run_altfw(current_menu_idx + 1);
+	vb2_flash_screen(ctx);
+	VB2_DEBUG(no_legacy);
+	VbExDisplayDebugInfo(no_legacy);
+
+	return VBERROR_KEEP_LOOPING;
+}
+
 /* Action that enables developer mode and reboots. */
 static VbError_t to_dev_action(struct vb2_context *ctx)
 {
@@ -481,7 +523,7 @@
 			},
 			[VB_DEV_LEGACY] = {
 				.text = "Boot Legacy BIOS",
-				.action = boot_legacy_action,
+				.action = enter_altfw_menu,
 			},
 			[VB_DEV_USB] = {
 				.text = "Boot From USB or SD Card",
@@ -595,6 +637,42 @@
 		.screen = VB_SCREEN_TO_NORM_CONFIRMED,
 		.items = NULL,
 	},
+	[VB_MENU_ALT_FW] = {
+		.screen = VB_SCREEN_ALT_FW_MENU,
+		.size = VB_ALTFW_COUNT + 1,
+		.items = (struct vb2_menu_item[]) {{
+				.text = "Bootloader 1",
+				.action = altfw_action,
+			}, {
+				.text = "Bootloader 2",
+				.action = altfw_action,
+			}, {
+				.text = "Bootloader 3",
+				.action = altfw_action,
+			}, {
+				.text = "Bootloader 4",
+				.action = altfw_action,
+			}, {
+				.text = "Bootloader 5",
+				.action = altfw_action,
+			}, {
+				.text = "Bootloader 6",
+				.action = altfw_action,
+			}, {
+				.text = "Bootloader 7",
+				.action = altfw_action,
+			}, {
+				.text = "Bootloader 8",
+				.action = altfw_action,
+			}, {
+				.text = "Bootloader 9",
+				.action = altfw_action,
+			}, {
+				.text = "Cancel",
+				.action = enter_developer_menu,
+			},
+		},
+	},
 };
 
 /* Initialize menu state. Must be called once before displaying any menus. */
@@ -614,6 +692,7 @@
 		return VBERROR_UNKNOWN;
 
 	for (i = 0; i < count; i++) {
+		/* The actual language is drawn by the bootloader */
 		items[i].text = "Some Language";
 		items[i].action = language_action;
 	}
@@ -679,14 +758,21 @@
 			ret = boot_disk_action(ctx);
 			break;
 		case 'L' & 0x1f:
-			/* Ctrl+L = boot legacy BIOS */
-			ret = boot_legacy_action(ctx);
+			/* Ctrl+L = boot alternative bootloader */
+			ret = enter_altfw_menu(ctx);
 			break;
 		case VB_BUTTON_VOL_UP_LONG_PRESS:
 		case 'U' & 0x1f:
 			/* Ctrl+U = boot from USB or SD card */
 			ret = boot_usb_action(ctx);
 			break;
+		/* We allow selection of the default '0' bootloader here */
+		case '0'...'9':
+			VB2_DEBUG("VbBootDeveloper() - "
+				  "user pressed key '%c': Boot alternative "
+				  "firmware\n", key);
+			vb2_try_alt_fw(altfw_allowed, key - '0');
+			break;
 		default:
 			ret = vb2_handle_menu_input(ctx, key, 0);
 			break;
@@ -703,7 +789,7 @@
 		VbExSleepMs(DEV_KEY_DELAY);
 
 		/* If dev mode was disabled, loop forever (never timeout) */
-	} while(disable_dev_boot ? 1 : vb2_audio_looping());
+	} while (disable_dev_boot ? 1 : vb2_audio_looping());
 
 	if (default_boot == VB2_DEV_DEFAULT_BOOT_LEGACY)
 		boot_legacy_action(ctx);	/* Doesn't return on success. */
diff --git a/tests/vboot_api_kernel2_tests.c b/tests/vboot_api_kernel2_tests.c
index 2f76490..2f1dee9 100644
--- a/tests/vboot_api_kernel2_tests.c
+++ b/tests/vboot_api_kernel2_tests.c
@@ -484,25 +484,49 @@
 	TEST_EQ(VbBootDeveloper(&ctx), 1002, "Ctrl+L normal");
 	TEST_EQ(vbexlegacy_called, 0, "  not legacy");
 
+	/* Enter altfw menu and time out */
+	ResetMocks();
+	shutdown_request_calls_left = 1000;
+	sd->gbb_flags |= VB2_GBB_FLAG_FORCE_DEV_BOOT_LEGACY;
+	mock_keypress[0] = 0x0c;
+	TEST_EQ(VbBootDeveloper(&ctx), VBERROR_SHUTDOWN_REQUESTED,
+		"Ctrl+L force legacy");
+	TEST_EQ(vbexlegacy_called, 0, "  try legacy");
+
+	/* Enter altfw menu and select firmware 0 */
 	ResetMocks();
 	sd->gbb_flags |= VB2_GBB_FLAG_FORCE_DEV_BOOT_LEGACY;
 	mock_keypress[0] = 0x0c;
+	mock_keypress[1] = '0';
 	TEST_EQ(VbBootDeveloper(&ctx), 1002,
 		"Ctrl+L force legacy");
 	TEST_EQ(vbexlegacy_called, 1, "  try legacy");
 	TEST_EQ(altfw_num, 0, "  check altfw_num");
 
+	/* Enter altfw menu and then exit it */
 	ResetMocks();
 	vb2_nv_set(&ctx, VB2_NV_DEV_BOOT_LEGACY, 1);
 	mock_keypress[0] = 0x0c;
+	mock_keypress[1] = VB_KEY_ESC;
+	TEST_EQ(VbBootDeveloper(&ctx), 1002,
+		"Ctrl+L nv legacy");
+	TEST_EQ(vbexlegacy_called, 0, "  try legacy");
+
+	/* Enter altfw menu and select firmware 0 */
+	ResetMocks();
+	vb2_nv_set(&ctx, VB2_NV_DEV_BOOT_LEGACY, 1);
+	mock_keypress[0] = 0x0c;
+	mock_keypress[1] = '0';
 	TEST_EQ(VbBootDeveloper(&ctx), 1002,
 		"Ctrl+L nv legacy");
 	TEST_EQ(vbexlegacy_called, 1, "  try legacy");
 	TEST_EQ(altfw_num, 0, "  check altfw_num");
 
+	/* Enter altfw menu and select firmware 0 */
 	ResetMocks();
 	VbApiKernelGetFwmp()->flags |= FWMP_DEV_ENABLE_LEGACY;
 	mock_keypress[0] = 0x0c;
+	mock_keypress[1] = '0';
 	TEST_EQ(VbBootDeveloper(&ctx), 1002,
 		"Ctrl+L fwmp legacy");
 	TEST_EQ(vbexlegacy_called, 1, "  fwmp legacy");
diff --git a/tests/vboot_detach_menu_tests.c b/tests/vboot_detach_menu_tests.c
index 6ec9f33..a9034b3 100644
--- a/tests/vboot_detach_menu_tests.c
+++ b/tests/vboot_detach_menu_tests.c
@@ -56,6 +56,8 @@
 static uint32_t screens_count = 0;
 static uint32_t beeps_played[64];
 static uint32_t beeps_count = 0;
+static uint32_t mock_altfw_mask;
+static int vbexaltfwmask_called;
 
 extern enum VbEcBootMode_t VbGetMode(void);
 extern struct RollbackSpaceFwmp *VbApiKernelGetFwmp(void);
@@ -107,6 +109,9 @@
 	memset(mock_switches, 0, sizeof(mock_switches));
 	mock_switches_count = 0;
 	mock_switches_are_stuck = 0;
+
+	mock_altfw_mask = 3 << 1;	/* This mask selects 1 and 2 */
+	vbexaltfwmask_called = 0;
 }
 
 static void ResetMocksForDeveloper(void)
@@ -127,6 +132,13 @@
 
 /* Mock functions */
 
+uint32_t VbExGetAltFwIdxMask() {
+
+	vbexaltfwmask_called++;
+
+	return mock_altfw_mask;
+}
+
 uint32_t VbExIsShutdownRequested(void)
 {
 	if (shutdown_request_calls_left == 0)
@@ -614,17 +626,18 @@
 	ResetMocksForDeveloper();
 	sd->gbb_flags |= GBB_FLAG_FORCE_DEV_BOOT_LEGACY;
 	mock_keypress[0] = 'L' & 0x1f;
+	mock_keypress[1] = VB_BUTTON_POWER_SHORT_PRESS;
 	TEST_EQ(VbBootDeveloperMenu(&ctx), vbtlk_retval_fixed,
 		"Ctrl+L force legacy");
 	TEST_EQ(vbexlegacy_called, 1, "  try legacy");
-	TEST_EQ(altfw_num, 0, "  check altfw_num");
+	TEST_EQ(altfw_num, 1, "  check altfw_num");
 	TEST_EQ(screens_displayed[0], VB_SCREEN_DEVELOPER_WARNING_MENU,
 		"  warning screen");
-	TEST_EQ(screens_displayed[1], VB_SCREEN_BLANK, "  blank (error flash)");
-	TEST_EQ(screens_displayed[2], VB_SCREEN_DEVELOPER_WARNING_MENU,
-		"  warning screen");
-	TEST_EQ(screens_displayed[3], VB_SCREEN_BLANK, "  final blank screen");
-	TEST_EQ(screens_count, 4, "  no extra screens");
+	TEST_EQ(screens_displayed[1], VB_SCREEN_ALT_FW_MENU, "  altfw");
+	TEST_EQ(screens_displayed[2], VB_SCREEN_BLANK, "  blank (error flash)");
+	TEST_EQ(screens_displayed[3], VB_SCREEN_ALT_FW_MENU, "  altfw");
+	TEST_EQ(screens_displayed[4], VB_SCREEN_BLANK, "  blank (error flash)");
+	TEST_EQ(screens_count, 5, "  no extra screens");
 	TEST_EQ(beeps_count, 1, "  error beep: legacy BIOS not found");
 	TEST_EQ(beeps_played[0], 200, "    low-frequency error beep");
 
@@ -632,17 +645,18 @@
 	ResetMocksForDeveloper();
 	vb2_nv_set(&ctx, VB2_NV_DEV_BOOT_LEGACY, 1);
 	mock_keypress[0] = 'L' & 0x1f;
+	mock_keypress[1] = VB_BUTTON_POWER_SHORT_PRESS;
 	TEST_EQ(VbBootDeveloperMenu(&ctx), vbtlk_retval_fixed,
 		"Ctrl+L nv legacy");
 	TEST_EQ(vbexlegacy_called, 1, "  try legacy");
 	TEST_EQ(vb2_nv_get(&ctx, VB2_NV_RECOVERY_REQUEST), 0, "  no recovery");
 	TEST_EQ(screens_displayed[0], VB_SCREEN_DEVELOPER_WARNING_MENU,
 		"  warning screen");
-	TEST_EQ(screens_displayed[1], VB_SCREEN_BLANK, "  blank (error flash)");
-	TEST_EQ(screens_displayed[2], VB_SCREEN_DEVELOPER_WARNING_MENU,
-		"  warning screen");
-	TEST_EQ(screens_displayed[3], VB_SCREEN_BLANK, "  final blank screen");
-	TEST_EQ(screens_count, 4, "  no extra screens");
+	TEST_EQ(screens_displayed[1], VB_SCREEN_ALT_FW_MENU, "  altfw");
+	TEST_EQ(screens_displayed[2], VB_SCREEN_BLANK, "  blank (error flash)");
+	TEST_EQ(screens_displayed[3], VB_SCREEN_ALT_FW_MENU, "  altfw");
+	TEST_EQ(screens_displayed[4], VB_SCREEN_BLANK, "  blank (error flash)");
+	TEST_EQ(screens_count, 5, "  no extra screens");
 	TEST_EQ(beeps_count, 1, "  error beep: legacy BIOS not found");
 	TEST_EQ(beeps_played[0], 200, "    low-frequency error beep");
 
@@ -650,20 +664,23 @@
 	ResetMocksForDeveloper();
 	VbApiKernelGetFwmp()->flags |= FWMP_DEV_ENABLE_LEGACY;
 	mock_keypress[0] = 'L' & 0x1f;
+	mock_keypress[1] = VB_BUTTON_POWER_SHORT_PRESS;
 	TEST_EQ(VbBootDeveloperMenu(&ctx), vbtlk_retval_fixed,
 		"Ctrl+L fwmp legacy");
 	TEST_EQ(vbexlegacy_called, 1, "  fwmp legacy");
-	TEST_EQ(altfw_num, 0, "  check altfw_num");
+	TEST_EQ(altfw_num, 1, "  check altfw_num");
 	TEST_EQ(screens_displayed[0], VB_SCREEN_DEVELOPER_WARNING_MENU,
 		"  warning screen");
-	TEST_EQ(screens_displayed[1], VB_SCREEN_BLANK, "  blank (error flash)");
-	TEST_EQ(screens_displayed[2], VB_SCREEN_DEVELOPER_WARNING_MENU,
-		"  warning screen");
-	TEST_EQ(screens_displayed[3], VB_SCREEN_BLANK, "  final blank screen");
-	TEST_EQ(screens_count, 4, "  no extra screens");
+	TEST_EQ(screens_displayed[1], VB_SCREEN_ALT_FW_MENU, "  altfw");
+	TEST_EQ(screens_displayed[2], VB_SCREEN_BLANK, "  blank (error flash)");
+	TEST_EQ(screens_displayed[3], VB_SCREEN_ALT_FW_MENU, "  altfw");
+	TEST_EQ(screens_displayed[4], VB_SCREEN_BLANK, "  final blank screen");
+	TEST_EQ(screens_count, 5, "  no extra screens");
 	TEST_EQ(beeps_count, 1, "  error beep: legacy BIOS not found");
 	TEST_EQ(beeps_played[0], 200, "    low-frequency error beep");
 
+	/* TODO(sjg@chromium.org): Add a test for there being no bootloaders */
+
 	/* Ctrl+U boots USB only if enabled */
 	ResetMocksForDeveloper();
 	mock_keypress[0] = 'U' & 0x1f;
@@ -908,8 +925,9 @@
 	mock_keypress[i++] = VB_BUTTON_VOL_UP_SHORT_PRESS; // Enable OS Verif
 	mock_keypress[i++] = VB_BUTTON_VOL_UP_SHORT_PRESS; // Show Debug Info
 	mock_keypress[i++] = VB_BUTTON_VOL_UP_SHORT_PRESS; // Developer Options
-	mock_keypress[i++] = VB_BUTTON_POWER_SHORT_PRESS;
-	mock_keypress[i++] = VB_BUTTON_POWER_SHORT_PRESS;
+	mock_keypress[i++] = VB_BUTTON_POWER_SHORT_PRESS; // Enter altfw menu
+	mock_keypress[i++] = VB_BUTTON_POWER_SHORT_PRESS; // Select first option
+	mock_keypress[i++] = VB_BUTTON_POWER_SHORT_PRESS; // and again
 	vb2_nv_set(&ctx, VB2_NV_DEV_BOOT_LEGACY, 1);
 	vb2_nv_set(&ctx, VB2_NV_DEV_DEFAULT_BOOT, VB2_DEV_DEFAULT_BOOT_LEGACY);
 	TEST_EQ(VbBootDeveloperMenu(&ctx), vbtlk_retval_fixed,
@@ -930,13 +948,12 @@
 		"  dev warning menu: developer options");
 	TEST_EQ(screens_displayed[i++], VB_SCREEN_DEVELOPER_MENU,
 		"  dev menu: legacy boot");
+	TEST_EQ(screens_displayed[i++], VB_SCREEN_ALT_FW_MENU, "  altfw");
 	TEST_EQ(screens_displayed[i++], VB_SCREEN_BLANK, "  blank (flash)");
-	TEST_EQ(screens_displayed[i++], VB_SCREEN_DEVELOPER_MENU,
-		"  dev menu: legacy boot");
+	TEST_EQ(screens_displayed[i++], VB_SCREEN_ALT_FW_MENU, "  altfw");
 	TEST_EQ(screens_displayed[i++], VB_SCREEN_BLANK, "  blank (flash)");
-	TEST_EQ(screens_displayed[i++], VB_SCREEN_DEVELOPER_MENU,
-		"  dev menu: legacy boot");
-	TEST_EQ(screens_displayed[i++], VB_SCREEN_BLANK,"  final blank screen");
+	TEST_EQ(screens_displayed[i++], VB_SCREEN_ALT_FW_MENU, "  altfw");
+	TEST_EQ(screens_displayed[i++], VB_SCREEN_BLANK, "  blank (flash)");
 	TEST_EQ(screens_count, i, "  no extra screens");
 	TEST_EQ(beeps_count, 2, "  two error beeps: legacy BIOS not found");
 	TEST_EQ(beeps_played[0], 200, "    low-frequency error beep");
@@ -1282,6 +1299,7 @@
 	TEST_EQ(screens_displayed[1], VB_SCREEN_BLANK, "  final blank screen");
 	TEST_EQ(screens_count, 2, "  no extra screens");
 	TEST_EQ(beeps_count, 0, "  no beep on shutdown");
+	TEST_EQ(vbexaltfwmask_called, 0, "  VbExGetAltFwIdxMask not called");
 
 	/* BROKEN screen with disks inserted */
 	ResetMocks();
@@ -2192,13 +2210,23 @@
 	mock_keypress[i++] = VB_BUTTON_VOL_UP_SHORT_PRESS; // Boot From USB
 	mock_keypress[i++] = VB_BUTTON_VOL_UP_SHORT_PRESS; // Boot Legacy
 	mock_keypress[i++] = VB_BUTTON_VOL_UP_SHORT_PRESS; // (end of menu)
-	mock_keypress[i++] = VB_BUTTON_POWER_SHORT_PRESS;
+	mock_keypress[i++] = VB_BUTTON_POWER_SHORT_PRESS; // Select altfw menu
+	mock_keypress[i++] = VB_BUTTON_VOL_DOWN_SHORT_PRESS; // Second altfw
+	mock_keypress[i++] = VB_BUTTON_VOL_DOWN_SHORT_PRESS; // Cancel
+	mock_keypress[i++] = VB_BUTTON_VOL_DOWN_SHORT_PRESS; // (end of menu)
+	mock_keypress[i++] = VB_BUTTON_POWER_SHORT_PRESS; // Back to dev options
+	mock_keypress[i++] = VB_BUTTON_VOL_UP_SHORT_PRESS; // Boot From Disk
+	mock_keypress[i++] = VB_BUTTON_VOL_UP_SHORT_PRESS; // Boot From USB
+	mock_keypress[i++] = VB_BUTTON_VOL_UP_SHORT_PRESS; // Boot Legacy
+	mock_keypress[i++] = VB_BUTTON_POWER_SHORT_PRESS; // Select altfw menu
+	mock_keypress[i++] = VB_BUTTON_VOL_DOWN_SHORT_PRESS; // Second altfw
+	mock_keypress[i++] = VB_BUTTON_POWER_SHORT_PRESS; // Select 2nd altfw
 	vb2_nv_set(&ctx, VB2_NV_DEV_BOOT_LEGACY, 1);
 	TEST_EQ(VbBootDeveloperMenu(&ctx), vbtlk_retval_fixed,
 		"developer mode long navigation");
 	TEST_EQ(debug_info_displayed, 1, "  showed debug info");
 	TEST_EQ(vbexlegacy_called, 1, "  tried legacy");
-	TEST_EQ(altfw_num, 0, "  check altfw_num");
+	TEST_EQ(altfw_num, 2, "  check altfw_num");
 	TEST_EQ(audio_looping_calls_left, 0, "  audio timeout");
 	TEST_EQ(vb2_nv_get(&ctx, VB2_NV_RECOVERY_REQUEST), 0, "  no recovery");
 	i = 0;
@@ -2280,9 +2308,24 @@
 		"  dev menu: boot legacy");
 	TEST_EQ(screens_displayed[i++], VB_SCREEN_DEVELOPER_MENU,
 		"  dev menu: boot legacy");
-	TEST_EQ(screens_displayed[i++], VB_SCREEN_BLANK, "  blank (flash)");
+	TEST_EQ(screens_displayed[i++], VB_SCREEN_ALT_FW_MENU, "  altfw 1");
+	TEST_EQ(screens_displayed[i++], VB_SCREEN_ALT_FW_MENU, "  altfw 2");
+	TEST_EQ(screens_displayed[i++], VB_SCREEN_ALT_FW_MENU,
+		"  altfw cancel");
+	TEST_EQ(screens_displayed[i++], VB_SCREEN_ALT_FW_MENU,
+		"  altfw end");
+	TEST_EQ(screens_displayed[i++], VB_SCREEN_DEVELOPER_MENU,
+		"  dev menu: cancel");
+	TEST_EQ(screens_displayed[i++], VB_SCREEN_DEVELOPER_MENU,
+		"  dev menu: boot from disk");
+	TEST_EQ(screens_displayed[i++], VB_SCREEN_DEVELOPER_MENU,
+		"  dev menu: boot from USB");
 	TEST_EQ(screens_displayed[i++], VB_SCREEN_DEVELOPER_MENU,
 		"  dev menu: boot legacy");
+	TEST_EQ(screens_displayed[i++], VB_SCREEN_ALT_FW_MENU, "  altfw");
+	TEST_EQ(screens_displayed[i++], VB_SCREEN_ALT_FW_MENU, "  altfw");
+	TEST_EQ(screens_displayed[i++], VB_SCREEN_BLANK, "  blank (flash)");
+	TEST_EQ(screens_displayed[i++], VB_SCREEN_ALT_FW_MENU, "  altfw");
 	TEST_EQ(screens_displayed[i++], VB_SCREEN_BLANK,"  final blank screen");
 	TEST_EQ(screens_count, i, "  no extra screens");
 	TEST_EQ(beeps_count, 1, "  error beep: legacy BIOS not found");
diff --git a/tests/vboot_display_tests.c b/tests/vboot_display_tests.c
index 71b66ae..ef9d146 100644
--- a/tests/vboot_display_tests.c
+++ b/tests/vboot_display_tests.c
@@ -33,6 +33,7 @@
 struct vb2_shared_data *sd;
 static uint8_t workbuf[VB2_KERNEL_WORKBUF_RECOMMENDED_SIZE];
 static uint32_t mock_localization_count;
+static uint32_t mock_altfw_mask;
 
 /* Reset mock data (for use before each test) */
 static void ResetMocks(void)
@@ -51,6 +52,7 @@
 	gbb_used = (gbb_used + gbb->hwid_size + 7) & ~7;
 
 	mock_localization_count = 3;
+	mock_altfw_mask = 3 << 1;	/* This mask selects 1 and 2 */
 
 	gbb->header_size = sizeof(*gbb);
 	gbb->rootkey_offset = gbb_used;
@@ -88,6 +90,10 @@
 	return VBERROR_SUCCESS;
 }
 
+uint32_t VbExGetAltFwIdxMask() {
+	return mock_altfw_mask;
+}
+
 VbError_t VbExDisplayDebugInfo(const char *info_str)
 {
 	strncpy(debug_info, info_str, sizeof(debug_info));