vboot/ui: Implement debug info screen

Implement debug info screen with multiple pages.

The debug info screen can be accessed using TAB as keyboard shortcut.
The menu navigation will be added in a separate CL.

The screen has one textbox for displaying one page of debug info,
and three buttons: page up, page down, and back.

BRANCH=puff
BUG=b:146399181, b:144969088
TEST=CC=x86_64-pc-linux-gnu-clang;
     make clean && make runtests
TEST=USE="menu_ui" emerge-nami depthcharge
     press tab, observe the debug info on the screen

Cq-Depend: chromium:2251685
Signed-off-by: Hsuan Ting Chen <roccochen@chromium.org>
Change-Id: I6bc0a49c4d69817825f35435b643b006823b5c03
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/vboot_reference/+/2251868
Reviewed-by: Yu-Ping Wu <yupingso@chromium.org>
Reviewed-by: Joel Kitching <kitching@chromium.org>
diff --git a/firmware/2lib/2misc.c b/firmware/2lib/2misc.c
index 86b866d..8e5444a 100644
--- a/firmware/2lib/2misc.c
+++ b/firmware/2lib/2misc.c
@@ -574,3 +574,136 @@
 	struct vb2_gbb_header *gbb = vb2_get_gbb(ctx);
 	return gbb->flags & VB2_GBB_FLAG_DEV_SCREEN_SHORT_DELAY;
 }
+
+static void snprint_sha1_sum(struct vb2_packed_key *key,
+			     char *dest, size_t dest_size)
+{
+	uint8_t *buf = ((uint8_t *)key) + key->key_offset;
+	uint64_t buflen = key->key_size;
+	uint8_t digest[VB2_SHA1_DIGEST_SIZE];
+	int32_t used = 0;
+	int i;
+
+	vb2_digest_buffer(buf, buflen, VB2_HASH_SHA1, digest, sizeof(digest));
+	for (i = 0; i < sizeof(digest); i++)
+		if (used < dest_size)
+			used += snprintf(dest + used, dest_size - used,
+					 "%02x", digest[i]);
+	dest[dest_size - 1] = '\0';
+}
+
+#define DEBUG_INFO_MAX_LENGTH 1024
+
+#define DEBUG_INFO_APPEND(format, args...) do { \
+	if (used < DEBUG_INFO_MAX_LENGTH) \
+		used += snprintf(buf + used, DEBUG_INFO_MAX_LENGTH - used, \
+				 format, ## args); \
+} while (0)
+
+char *vb2api_get_debug_info(struct vb2_context *ctx)
+{
+	char *buf;
+	uint32_t used = 0;
+
+	struct vb2_shared_data *sd = vb2_get_sd(ctx);
+	struct vb2_gbb_header *gbb = vb2_get_gbb(ctx);
+	struct vb2_workbuf wb;
+	char sha1sum[VB2_SHA1_DIGEST_SIZE * 2 + 1];
+
+	vb2_error_t rv;
+	uint32_t i;
+
+	buf = malloc(DEBUG_INFO_MAX_LENGTH + 1);
+	if (buf == NULL)
+		return NULL;
+
+	vb2_workbuf_from_ctx(ctx, &wb);
+
+	/* Add hardware ID */
+	{
+		char hwid[VB2_GBB_HWID_MAX_SIZE];
+		uint32_t size = sizeof(hwid);
+		rv = vb2api_gbb_read_hwid(ctx, hwid, &size);
+		if (rv)
+			strcpy(hwid, "{INVALID}");
+		DEBUG_INFO_APPEND("HWID: %s", hwid);
+	}
+
+	/* Add recovery reason and subcode */
+	i = vb2_nv_get(ctx, VB2_NV_RECOVERY_SUBCODE);
+	DEBUG_INFO_APPEND("\nrecovery_reason: %#.2x / %#.2x  %s",
+			  sd->recovery_reason, i,
+			  vb2_get_recovery_reason_string(sd->recovery_reason));
+
+	/* Add vb2_context and vb2_shared_data flags */
+	DEBUG_INFO_APPEND("\ncontext.flags: %#.16" PRIx64, ctx->flags);
+	DEBUG_INFO_APPEND("\nshared_data.flags: %#.8x", sd->flags);
+	DEBUG_INFO_APPEND("\nshared_data.status: %#.8x", sd->status);
+
+	/* Add raw contents of nvdata */
+	DEBUG_INFO_APPEND("\nnvdata:");
+	if (vb2_nv_get_size(ctx) > 16)  /* Multi-line starts on next line */
+		DEBUG_INFO_APPEND("\n  ");
+	for (i = 0; i < vb2_nv_get_size(ctx); i++) {
+		/* Split into 16-byte blocks */
+		if (i > 0 && i % 16 == 0)
+			DEBUG_INFO_APPEND("\n  ");
+		DEBUG_INFO_APPEND(" %02x", ctx->nvdata[i]);
+	}
+
+	/* Add dev_boot_usb flag */
+	i = vb2_nv_get(ctx, VB2_NV_DEV_BOOT_EXTERNAL);
+	DEBUG_INFO_APPEND("\ndev_boot_usb: %d", i);
+
+	/* Add dev_boot_legacy flag */
+	i = vb2_nv_get(ctx, VB2_NV_DEV_BOOT_LEGACY);
+	DEBUG_INFO_APPEND("\ndev_boot_legacy: %d", i);
+
+	/* Add dev_default_boot flag */
+	i = vb2_nv_get(ctx, VB2_NV_DEV_DEFAULT_BOOT);
+	DEBUG_INFO_APPEND("\ndev_default_boot: %d", i);
+
+	/* Add dev_boot_signed_only flag */
+	i = vb2_nv_get(ctx, VB2_NV_DEV_BOOT_SIGNED_ONLY);
+	DEBUG_INFO_APPEND("\ndev_boot_signed_only: %d", i);
+
+	/* Add TPM versions */
+	DEBUG_INFO_APPEND("\nTPM: fwver=%#.8x kernver=%#.8x",
+			  sd->fw_version_secdata, sd->kernel_version_secdata);
+
+	/* Add GBB flags */
+	DEBUG_INFO_APPEND("\ngbb.flags: %#.8x", gbb->flags);
+
+	/* Add sha1sum for Root & Recovery keys */
+	{
+		struct vb2_packed_key *key;
+		struct vb2_workbuf wblocal = wb;
+		rv = vb2_gbb_read_root_key(ctx, &key, NULL, &wblocal);
+		if (rv == VB2_SUCCESS) {
+			snprint_sha1_sum(key, sha1sum, sizeof(sha1sum));
+			DEBUG_INFO_APPEND("\ngbb.rootkey: %s", sha1sum);
+		}
+	}
+
+	{
+		struct vb2_packed_key *key;
+		struct vb2_workbuf wblocal = wb;
+		rv = vb2_gbb_read_recovery_key(ctx, &key, NULL, &wblocal);
+		if (rv == VB2_SUCCESS) {
+			snprint_sha1_sum(key, sha1sum, sizeof(sha1sum));
+			DEBUG_INFO_APPEND("\ngbb.recovery_key: %s", sha1sum);
+		}
+	}
+
+	/* If we're in dev-mode, show the kernel subkey that we expect, too. */
+	if (!(ctx->flags & VB2_CONTEXT_RECOVERY_MODE) &&
+	    sd->kernel_key_offset) {
+		struct vb2_packed_key *key =
+			vb2_member_of(sd, sd->kernel_key_offset);
+		snprint_sha1_sum(key, sha1sum, sizeof(sha1sum));
+		DEBUG_INFO_APPEND("\nkernel_subkey: %s", sha1sum);
+	}
+
+	buf[DEBUG_INFO_MAX_LENGTH] = '\0';
+	return buf;
+}
diff --git a/firmware/2lib/2stub.c b/firmware/2lib/2stub.c
index 70cbeff..c4b9bdf 100644
--- a/firmware/2lib/2stub.c
+++ b/firmware/2lib/2stub.c
@@ -92,3 +92,15 @@
 	/* Stub simply exits. */
 	exit(1);
 }
+
+__attribute__((weak))
+const char *vb2ex_get_debug_info(struct vb2_context *ctx)
+{
+	return NULL;
+}
+
+__attribute__((weak))
+uint32_t vb2ex_prepare_log_screen(const char *str)
+{
+	return 1;
+}
diff --git a/firmware/2lib/2ui.c b/firmware/2lib/2ui.c
index fea3b4a..8adfab8 100644
--- a/firmware/2lib/2ui.c
+++ b/firmware/2lib/2ui.c
@@ -15,7 +15,6 @@
 #include "2ui_private.h"
 #include "vboot_api.h"  /* For VB_SHUTDOWN_REQUEST_POWER_BUTTON */
 #include "vboot_kernel.h"
-#include "vboot_ui_legacy.h"  /* TODO(b/144969088): VbDisplayDebugInfo */
 
 #define KEY_DELAY_MS 20  /* Delay between key scans in UI loops */
 
@@ -326,6 +325,7 @@
 					 ui.state->selected_item,
 					 ui.state->disabled_item_mask,
 					 ui.disable_timer,
+					 ui.state->current_page,
 					 ui.error_code);
 			if (ui.error_beep) {
 				vb2ex_beep(250, 400);
@@ -399,10 +399,8 @@
 	if (ui->key == VB_KEY_CTRL('D') ||
 	    (DETACHABLE && ui->key == VB_BUTTON_VOL_DOWN_LONG_PRESS))
 		return vb2_ui_developer_mode_boot_internal_action(ui);
-
-	/* TODO(b/144969088): Re-implement as debug info screen. */
 	if (ui->key == '\t')
-		VbDisplayDebugInfo(ui->ctx);
+		return vb2_ui_screen_change(ui, VB2_SCREEN_DEBUG_INFO);
 
 	return VB2_REQUEST_UI_CONTINUE;
 }
@@ -417,9 +415,9 @@
 
 vb2_error_t broken_recovery_action(struct vb2_ui_context *ui)
 {
-	/* TODO(b/144969088): Re-implement as debug info screen. */
+	/* Broken recovery keyboard shortcuts */
 	if (ui->key == '\t')
-		VbDisplayDebugInfo(ui->ctx);
+		return vb2_ui_screen_change(ui, VB2_SCREEN_DEBUG_INFO);
 
 	return VB2_REQUEST_UI_CONTINUE;
 }
@@ -455,9 +453,8 @@
 	    (DETACHABLE && ui->key == VB_BUTTON_VOL_UP_DOWN_COMBO_PRESS))
 		return vb2_ui_screen_change(ui, VB2_SCREEN_RECOVERY_TO_DEV);
 
-	/* TODO(b/144969088): Re-implement as debug info screen. */
 	if (ui->key == '\t')
-		VbDisplayDebugInfo(ui->ctx);
+		return vb2_ui_screen_change(ui, VB2_SCREEN_DEBUG_INFO);
 
 	return VB2_REQUEST_UI_CONTINUE;
 }
diff --git a/firmware/2lib/2ui_screens.c b/firmware/2lib/2ui_screens.c
index ab15a2b..741f4b9 100644
--- a/firmware/2lib/2ui_screens.c
+++ b/firmware/2lib/2ui_screens.c
@@ -52,6 +52,66 @@
 }
 
 /******************************************************************************/
+/* Functions used for log screens */
+
+static vb2_error_t log_page_init(struct vb2_ui_context *ui,
+				 uint32_t page_up_item,
+				 uint32_t page_down_item,
+				 uint32_t alternate_item)
+{
+	ui->state->current_page = 0;
+
+	if (ui->state->page_count == 1) {
+		ui->state->disabled_item_mask |= 1 << page_up_item;
+		ui->state->disabled_item_mask |= 1 << page_down_item;
+		ui->state->selected_item = alternate_item;
+	} else {
+		ui->state->disabled_item_mask |= 1 << page_up_item;
+		ui->state->selected_item = page_down_item;
+	}
+
+	return VB2_REQUEST_UI_CONTINUE;
+}
+
+static vb2_error_t log_page_prev(struct vb2_ui_context *ui,
+				 uint32_t page_up_item,
+				 uint32_t page_down_item)
+{
+	if (ui->state->current_page == 0)
+		return VB2_REQUEST_UI_CONTINUE;
+	ui->state->current_page--;
+
+	/* Clear bits of page down. */
+	ui->state->disabled_item_mask &= ~(1 << page_down_item);
+
+	if (ui->state->current_page == 0) {
+		ui->state->disabled_item_mask |= 1 << page_up_item;
+		ui->state->selected_item = page_down_item;
+	}
+
+	return VB2_REQUEST_UI_CONTINUE;
+}
+
+static vb2_error_t log_page_next(struct vb2_ui_context *ui,
+				 uint32_t page_up_item,
+				 uint32_t page_down_item)
+{
+	if (ui->state->current_page == ui->state->page_count - 1)
+		return VB2_REQUEST_UI_CONTINUE;
+	ui->state->current_page++;
+
+	/* Clear bits of page up. */
+	ui->state->disabled_item_mask &= ~(1 << page_up_item);
+
+	if (ui->state->current_page == ui->state->page_count - 1) {
+		ui->state->disabled_item_mask |= 1 << page_down_item;
+		ui->state->selected_item = page_up_item;
+	}
+
+	return VB2_REQUEST_UI_CONTINUE;
+}
+
+/******************************************************************************/
 /* VB2_SCREEN_BLANK */
 
 static const struct vb2_screen_info blank_screen = {
@@ -176,6 +236,7 @@
 		.text = "Enable developer mode",
 		.target = VB2_SCREEN_RECOVERY_TO_DEV,
 	},
+	/* TODO(b:144969088): Add debug info item */
 	[ADVANCED_OPTIONS_ITEM_BACK] = BACK_ITEM,
 	POWER_OFF_ITEM,
 };
@@ -188,6 +249,62 @@
 };
 
 /******************************************************************************/
+/* VB2_SCREEN_DEBUG_INFO */
+
+#define DEBUG_INFO_ITEM_PAGE_UP 1
+#define DEBUG_INFO_ITEM_PAGE_DOWN 2
+#define DEBUG_INFO_ITEM_BACK 3
+
+static vb2_error_t debug_info_init(struct vb2_ui_context *ui)
+{
+	const char *log_string = vb2ex_get_debug_info(ui->ctx);
+	ui->state->page_count = vb2ex_prepare_log_screen(log_string);
+	if (ui->state->page_count == 0) {
+		ui->error_code = VB2_UI_ERROR_DEBUG_LOG;
+		return vb2_ui_screen_back(ui);
+	}
+	return log_page_init(ui,
+			     DEBUG_INFO_ITEM_PAGE_UP,
+			     DEBUG_INFO_ITEM_PAGE_DOWN,
+			     DEBUG_INFO_ITEM_BACK);
+}
+
+static vb2_error_t debug_info_page_prev_action(struct vb2_ui_context *ui)
+{
+	return log_page_prev(ui,
+			     DEBUG_INFO_ITEM_PAGE_UP,
+			     DEBUG_INFO_ITEM_PAGE_DOWN);
+}
+
+static vb2_error_t debug_info_page_next_action(struct vb2_ui_context *ui)
+{
+	return log_page_next(ui,
+			     DEBUG_INFO_ITEM_PAGE_UP,
+			     DEBUG_INFO_ITEM_PAGE_DOWN);
+}
+
+static const struct vb2_menu_item debug_info_items[] = {
+	LANGUAGE_SELECT_ITEM,
+	[DEBUG_INFO_ITEM_PAGE_UP] = {
+		.text = "Page up",
+		.action = debug_info_page_prev_action,
+	},
+	[DEBUG_INFO_ITEM_PAGE_DOWN] = {
+		.text = "Page down",
+		.action = debug_info_page_next_action,
+	},
+	[DEBUG_INFO_ITEM_BACK] = BACK_ITEM,
+	POWER_OFF_ITEM,
+};
+
+static const struct vb2_screen_info debug_info_screen = {
+	.id = VB2_SCREEN_DEBUG_INFO,
+	.name = "Debug info",
+	.init = debug_info_init,
+	.menu = MENU_ITEMS(debug_info_items),
+};
+
+/******************************************************************************/
 /* VB2_SCREEN_RECOVERY_SELECT */
 
 #define RECOVERY_SELECT_ITEM_PHONE 1
@@ -662,6 +779,7 @@
 	&language_select_screen,
 	&recovery_broken_screen,
 	&advanced_options_screen,
+	&debug_info_screen,
 	&recovery_select_screen,
 	&recovery_invalid_screen,
 	&recovery_to_dev_screen,
diff --git a/firmware/2lib/include/2api.h b/firmware/2lib/include/2api.h
index b6c9bea..649ee09 100644
--- a/firmware/2lib/include/2api.h
+++ b/firmware/2lib/include/2api.h
@@ -1269,6 +1269,8 @@
 	VB2_SCREEN_ADVANCED_OPTIONS		= 0x120,
 	/* Language selection screen */
 	VB2_SCREEN_LANGUAGE_SELECT		= 0x130,
+	/* Debug info */
+	VB2_SCREEN_DEBUG_INFO			= 0x140,
 	/* First recovery screen to select recovering from disk or phone */
 	VB2_SCREEN_RECOVERY_SELECT		= 0x200,
 	/* Invalid recovery media inserted */
@@ -1297,6 +1299,8 @@
 	VB2_UI_ERROR_NONE = 0,
 	/* Dev mode already enabled */
 	VB2_UI_ERROR_DEV_MODE_ALREADY_ENABLED,
+	/* Debug info screen initialization failed */
+	VB2_UI_ERROR_DEBUG_LOG,
 };
 
 /**
@@ -1310,6 +1314,9 @@
  *				indicates whether item 'idx' is disabled.
  * @param timer_disabled	Whether timer is disabled or not. Some screen
  *				descriptions will depend on this value.
+ * @param current_page		Current page number for a log screen. If the
+ *				screen doesn't show logs, this value will be
+ *				ignored.
  * @param error_code		Error code if an error occurred.
  * @return VB2_SUCCESS, or error code on error.
  */
@@ -1318,6 +1325,7 @@
 			     uint32_t selected_item,
 			     uint32_t disabled_item_mask,
 			     int timer_disabled,
+			     uint32_t current_page,
 			     enum vb2_ui_error error_code);
 
 /**
@@ -1354,6 +1362,44 @@
  */
 void vb2ex_beep(uint32_t msec, uint32_t frequency);
 
+/**
+ * Get the full debug info string.
+ *
+ * Return a pointer to the full debug info string which is guaranteed to be
+ * null-terminated.  The function implementation should manage string memory
+ * internally.  Subsequent calls may update the string and return the same
+ * pointer, or return a new pointer if necessary.
+ *
+ * @param ctx		Vboot context
+ * @return The pointer to the full debug info string.  NULL on error.
+ */
+const char *vb2ex_get_debug_info(struct vb2_context *ctx);
+
+/**
+ * Get the vboot debug info.
+ *
+ * Return a pointer to the vboot debug info string which is guaranteed to be
+ * null-terminated.  The caller owns the string and should call free() when
+ * finished with it.
+ *
+ * @param ctx		Vboot context
+ * @return The pointer to the vboot debug info string.  NULL on error.
+ */
+char *vb2api_get_debug_info(struct vb2_context *ctx);
+
+/**
+ * Specify the string to be used for an upcoming log screen display.
+ *
+ * Before a vb2ex_display_ui() call is made for a screen which displays logs,
+ * the log string should be provided via this function.  The total number of
+ * pages in the log string is returned.  If the log string ever changes, this
+ * function should be called again before the next vb2ex_display_ui() call.
+ *
+ * @param str		The log string to display.
+ * @return The number of pages after pagination.  0 if none or error.
+ */
+uint32_t vb2ex_prepare_log_screen(const char *str);
+
 /*****************************************************************************/
 /* Timer. */
 
diff --git a/firmware/2lib/include/2return_codes.h b/firmware/2lib/include/2return_codes.h
index 8d69847..336c11e 100644
--- a/firmware/2lib/include/2return_codes.h
+++ b/firmware/2lib/include/2return_codes.h
@@ -774,6 +774,9 @@
 	/* Memory allocation failure */
 	VB2_ERROR_UI_MEMORY_ALLOC = 0x100c0007,
 
+	/* Log screen initialization failed */
+	VB2_ERROR_UI_LOG_INIT = 0x100c0008,
+
 	/**********************************************************************
 	 * Errors generated by host library (non-firmware) start here.
 	 */
diff --git a/firmware/2lib/include/2ui.h b/firmware/2lib/include/2ui.h
index fa8c66a..5f2434c 100644
--- a/firmware/2lib/include/2ui.h
+++ b/firmware/2lib/include/2ui.h
@@ -56,6 +56,11 @@
 	const struct vb2_screen_info *screen;
 	uint32_t selected_item;
 	uint32_t disabled_item_mask;
+
+	/* For log screen. */
+	uint32_t page_count;
+	uint32_t current_page;
+
 	struct vb2_screen_state *prev;
 };
 
diff --git a/tests/vb2_ui_action_tests.c b/tests/vb2_ui_action_tests.c
index fb110b4..493162f 100644
--- a/tests/vb2_ui_action_tests.c
+++ b/tests/vb2_ui_action_tests.c
@@ -387,6 +387,7 @@
 			     uint32_t selected_item,
 			     uint32_t disabled_item_mask,
 			     int timer_disabled,
+			     uint32_t current_page,
 			     enum vb2_ui_error error_code)
 {
 	struct display_call displayed = (struct display_call){
diff --git a/tests/vb2_ui_tests.c b/tests/vb2_ui_tests.c
index 516ec6f..8683364 100644
--- a/tests/vb2_ui_tests.c
+++ b/tests/vb2_ui_tests.c
@@ -27,6 +27,8 @@
 	uint32_t locale_id;
 	uint32_t selected_item;
 	uint32_t disabled_item_mask;
+	/* TODO(b/156448738): Add more params and their tests */
+	uint32_t current_page;
 };
 
 struct beep_call {
@@ -88,6 +90,8 @@
 
 static int mock_enable_dev_mode;
 
+static uint32_t mock_log_page_count;
+
 static void add_mock_key(uint32_t press, int trusted)
 {
 	if (mock_key_total >= ARRAY_SIZE(mock_key) ||
@@ -145,6 +149,7 @@
 			 uint32_t locale_id,
 			 uint32_t selected_item,
 			 uint32_t disabled_item_mask,
+			 uint32_t current_page,
 			 int line)
 {
 	char text_info[32], text_buf[128];
@@ -180,6 +185,12 @@
 		TEST_EQ(mock_displayed[mock_displayed_i].disabled_item_mask,
 			disabled_item_mask, text_buf);
 	}
+	if (current_page != MOCK_IGNORE) {
+		sprintf(text_buf, "  %s current_page of %s",
+			text_info, text);
+		TEST_EQ(mock_displayed[mock_displayed_i].current_page,
+			current_page, text_buf);
+	}
 	mock_displayed_i++;
 }
 
@@ -200,7 +211,7 @@
 
 #define DISPLAYED_PASS() \
 	displayed_eq("", MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, \
-		     __LINE__)
+		     MOCK_IGNORE, __LINE__)
 
 #define DISPLAYED_NO_EXTRA() displayed_no_extra(__LINE__)
 
@@ -306,6 +317,9 @@
 	/* For vb2_enable_developer_mode */
 	mock_enable_dev_mode = 0;
 
+	/* For vb2ex_prepare_log_screen */
+	mock_log_page_count = 1;
+
 	/* Avoid Iteration #0 */
 	add_mock_keypress(0);
 	if (t == FOR_MANUAL_RECOVERY)
@@ -327,6 +341,7 @@
 			     uint32_t selected_item,
 			     uint32_t disabled_item_mask,
 			     int timer_disabled,
+			     uint32_t current_page,
 			     enum vb2_ui_error error_code)
 {
 	struct display_call displayed = (struct display_call){
@@ -334,6 +349,7 @@
 		.locale_id = locale_id,
 		.selected_item = selected_item,
 		.disabled_item_mask = disabled_item_mask,
+		.current_page = current_page,
 	};
 
 	/* Ignore repeated calls with same arguments */
@@ -343,9 +359,10 @@
 		return VB2_SUCCESS;
 
 	VB2_DEBUG("displayed %d: screen = %#x, locale_id = %u, "
-		  "selected_item = %u, disabled_item_mask = %#x\n",
+		  "selected_item = %u, disabled_item_mask = %#x, "
+		  "current_page = %u\n",
 		  mock_displayed_count, screen, locale_id, selected_item,
-		  disabled_item_mask);
+		  disabled_item_mask, current_page);
 
 	if (mock_displayed_count >= ARRAY_SIZE(mock_displayed)) {
 		TEST_TRUE(0, "  mock vb2ex_display_ui ran out of entries!");
@@ -496,6 +513,17 @@
 	mock_enable_dev_mode = 1;
 }
 
+const char *vb2ex_get_debug_info(struct vb2_context *c)
+{
+	return NULL;
+}
+
+uint32_t vb2ex_prepare_log_screen(const char *str)
+{
+	return mock_log_page_count;
+}
+
+
 /* Tests */
 static void developer_tests(void)
 {
@@ -653,10 +681,10 @@
 	TEST_EQ(vb2_developer_menu(ctx), VB2_REQUEST_SHUTDOWN,
 		"if dev mode is disabled, goes to to_norm screen repeatedly");
 	DISPLAYED_EQ("to_norm", VB2_SCREEN_DEVELOPER_TO_NORM,
-		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
+		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
 	DISPLAYED_PASS();
 	DISPLAYED_EQ("to_norm", VB2_SCREEN_DEVELOPER_TO_NORM,
-		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
+		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
 	DISPLAYED_NO_EXTRA();
 
 	/* Select to_norm in dev menu and confirm */
@@ -788,11 +816,11 @@
 	TEST_EQ(vb2_manual_recovery_menu(ctx), VB2_SUCCESS,
 		"boots after valid image appears");
 	DISPLAYED_EQ("recovery select", VB2_SCREEN_RECOVERY_SELECT,
-		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
+		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
 	DISPLAYED_EQ("recovery invalid", VB2_SCREEN_RECOVERY_INVALID,
-		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
+		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
 	DISPLAYED_EQ("recovery select", VB2_SCREEN_RECOVERY_SELECT,
-		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
+		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
 	DISPLAYED_NO_EXTRA();
 
 	/* Ctrl+D = to_dev; space = cancel */
@@ -803,11 +831,11 @@
 		"ctrl+d = to_dev; space = cancel");
 	TEST_EQ(mock_enable_dev_mode, 0, "  dev mode not enabled");
 	DISPLAYED_EQ("recovery select", VB2_SCREEN_RECOVERY_SELECT,
-		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
+		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
 	DISPLAYED_EQ("to_dev", VB2_SCREEN_RECOVERY_TO_DEV,
-		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
+		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
 	DISPLAYED_EQ("recovery select", VB2_SCREEN_RECOVERY_SELECT,
-		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
+		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
 	DISPLAYED_NO_EXTRA();
 
 	/* Cancel to_dev transition */
@@ -875,7 +903,8 @@
 			"physical presence button stuck?");
 		TEST_EQ(mock_enable_dev_mode, 0, "  dev mode not enabled");
 		DISPLAYED_EQ("recovery select", VB2_SCREEN_RECOVERY_SELECT,
-			     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
+			     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE,
+			     MOCK_IGNORE);
 		DISPLAYED_NO_EXTRA();
 
 		/* Button stuck, enter to_dev again */
@@ -893,9 +922,11 @@
 			  "  used up mock_pp_pressed");
 		TEST_EQ(mock_enable_dev_mode, 1, "  dev mode enabled");
 		DISPLAYED_EQ("recovery select", VB2_SCREEN_RECOVERY_SELECT,
-			     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
+			     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE,
+			     MOCK_IGNORE);
 		DISPLAYED_EQ("to_dev", VB2_SCREEN_RECOVERY_TO_DEV,
-			     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
+			     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE,
+			     MOCK_IGNORE);
 		DISPLAYED_NO_EXTRA();
 
 		/* Cancel with holding pp button, enter again */
@@ -926,13 +957,17 @@
 			  "  used up mock_pp_pressed");
 		TEST_EQ(mock_enable_dev_mode, 1, "  dev mode enabled");
 		DISPLAYED_EQ("recovery select", VB2_SCREEN_RECOVERY_SELECT,
-			     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
+			     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE,
+			     MOCK_IGNORE);
 		DISPLAYED_EQ("to_dev", VB2_SCREEN_RECOVERY_TO_DEV,
-			     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
+			     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE,
+			     MOCK_IGNORE);
 		DISPLAYED_EQ("recovery select", VB2_SCREEN_RECOVERY_SELECT,
-			     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
+			     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE,
+			     MOCK_IGNORE);
 		DISPLAYED_EQ("to_dev", VB2_SCREEN_RECOVERY_TO_DEV,
-			     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
+			     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE,
+			     MOCK_IGNORE);
 		DISPLAYED_NO_EXTRA();
 	}
 
@@ -955,15 +990,15 @@
 	TEST_EQ(vb2_manual_recovery_menu(ctx), VB2_REQUEST_SHUTDOWN,
 		"change language");
 	DISPLAYED_EQ("RECOVERY_SELECT default", VB2_SCREEN_RECOVERY_SELECT,
-		     23, MOCK_IGNORE, MOCK_IGNORE);
+		     23, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
 	DISPLAYED_EQ("RECOVERY_SELECT lang", VB2_SCREEN_RECOVERY_SELECT,
-		     23, 0, MOCK_IGNORE);
+		     23, 0, MOCK_IGNORE, MOCK_IGNORE);
 	DISPLAYED_EQ("LANGUAGE_SELECT 23", VB2_SCREEN_LANGUAGE_SELECT,
-		     23, 23, MOCK_IGNORE);
+		     23, 23, MOCK_IGNORE, MOCK_IGNORE);
 	DISPLAYED_EQ("LANGUAGE_SELECT 24", VB2_SCREEN_LANGUAGE_SELECT,
-		     23, 24, MOCK_IGNORE);
+		     23, 24, MOCK_IGNORE, MOCK_IGNORE);
 	DISPLAYED_EQ("RECOVERY_SELECT new locale", VB2_SCREEN_RECOVERY_SELECT,
-		     24, MOCK_IGNORE, MOCK_IGNORE);
+		     24, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
 	DISPLAYED_NO_EXTRA();
 	TEST_EQ(vb2_nv_get(ctx, VB2_NV_LOCALIZATION_INDEX), 24,
 		"  locale 24 saved to nvdata");
@@ -979,13 +1014,102 @@
 	TEST_EQ(vb2_manual_recovery_menu(ctx), VB2_REQUEST_SHUTDOWN,
 		"enter language menu");
 	DISPLAYED_EQ("RECOVERY_SELECT default", VB2_SCREEN_RECOVERY_SELECT,
-		     23, MOCK_IGNORE, MOCK_IGNORE);
+		     23, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
 	DISPLAYED_EQ("RECOVERY_SELECT lang", VB2_SCREEN_RECOVERY_SELECT,
-		     23, 0, MOCK_IGNORE);
+		     23, 0, MOCK_IGNORE, MOCK_IGNORE);
 	DISPLAYED_EQ("LANGUAGE_SELECT index 0", VB2_SCREEN_LANGUAGE_SELECT,
-		     23, 0, MOCK_IGNORE);
+		     23, 0, MOCK_IGNORE, MOCK_IGNORE);
 	DISPLAYED_EQ("RECOVERY_SELECT locale 0", VB2_SCREEN_RECOVERY_SELECT,
-		     0, MOCK_IGNORE, MOCK_IGNORE);
+		     0, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
+	DISPLAYED_NO_EXTRA();
+
+	VB2_DEBUG("...done.\n");
+}
+
+static void debug_info_tests(void)
+{
+	VB2_DEBUG("Testing debug info screen...\n");
+
+	/* Tab = debug info for all menus */
+	reset_common_data(FOR_DEVELOPER);
+	add_mock_keypress('\t');
+	TEST_EQ(vb2_developer_menu(ctx), VB2_REQUEST_SHUTDOWN,
+		"tab = debug info in dev mode");
+	DISPLAYED_PASS();
+	DISPLAYED_EQ("debug info", VB2_SCREEN_DEBUG_INFO,
+		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
+	DISPLAYED_NO_EXTRA();
+
+	reset_common_data(FOR_BROKEN_RECOVERY);
+	add_mock_keypress('\t');
+	TEST_EQ(vb2_broken_recovery_menu(ctx), VB2_REQUEST_SHUTDOWN,
+		"tab = debug info in broken recovery mode");
+	DISPLAYED_PASS();
+	DISPLAYED_EQ("debug info", VB2_SCREEN_DEBUG_INFO,
+		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
+	DISPLAYED_NO_EXTRA();
+
+	reset_common_data(FOR_MANUAL_RECOVERY);
+	add_mock_keypress('\t');
+	TEST_EQ(vb2_manual_recovery_menu(ctx), VB2_REQUEST_SHUTDOWN,
+		"tab = debug info in manual recovery mode");
+	DISPLAYED_PASS();
+	DISPLAYED_EQ("debug info", VB2_SCREEN_DEBUG_INFO,
+		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
+	DISPLAYED_NO_EXTRA();
+
+	/* Failed to enter debug info */
+	reset_common_data(FOR_MANUAL_RECOVERY);
+	mock_log_page_count = 0;
+	add_mock_keypress('\t');
+	TEST_EQ(vb2_manual_recovery_menu(ctx), VB2_REQUEST_SHUTDOWN,
+		"failed to enter debug info");
+	DISPLAYED_PASS();
+	DISPLAYED_NO_EXTRA();
+
+	/* Get a one-page debug info */
+	reset_common_data(FOR_MANUAL_RECOVERY);
+	add_mock_keypress('\t');
+	add_mock_keypress(VB_KEY_ENTER);  /* back */
+	TEST_EQ(vb2_manual_recovery_menu(ctx), VB2_REQUEST_SHUTDOWN,
+		"get a one-page debug info");
+	DISPLAYED_PASS();
+	DISPLAYED_EQ("debug info", VB2_SCREEN_DEBUG_INFO,
+		     MOCK_IGNORE, 3, 0x6, 0);
+	DISPLAYED_EQ("back to root screen", VB2_SCREEN_RECOVERY_SELECT,
+		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
+	DISPLAYED_NO_EXTRA();
+
+	/* Get a three-page debug info and navigate */
+	reset_common_data(FOR_MANUAL_RECOVERY);
+	mock_log_page_count = 3;
+	add_mock_keypress('\t');
+	add_mock_keypress(VB_KEY_ENTER);  /* page 0, select on page down */
+	add_mock_keypress(VB_KEY_ENTER);  /* page 1, select on page down */
+	add_mock_keypress(VB_KEY_ENTER);  /* page 2, select on page up */
+	add_mock_keypress(VB_KEY_ENTER);  /* page 1, select on page up */
+	add_mock_keypress(VB_KEY_ENTER);  /* page 0, select on page down */
+	add_mock_keypress(VB_KEY_DOWN);
+	add_mock_keypress(VB_KEY_ENTER);  /* page 1, select on back */
+	TEST_EQ(vb2_manual_recovery_menu(ctx), VB2_REQUEST_SHUTDOWN,
+		"get a three-page debug info and navigate");
+	DISPLAYED_PASS();
+	DISPLAYED_EQ("debug info page #0", VB2_SCREEN_DEBUG_INFO,
+		     MOCK_IGNORE, 2, 0x2, 0);
+	DISPLAYED_EQ("debug info page #1", VB2_SCREEN_DEBUG_INFO,
+		     MOCK_IGNORE, 2, 0x0, 1);
+	DISPLAYED_EQ("debug info page #2", VB2_SCREEN_DEBUG_INFO,
+		     MOCK_IGNORE, 1, 0x4, 2);
+	DISPLAYED_EQ("debug info page #1", VB2_SCREEN_DEBUG_INFO,
+		     MOCK_IGNORE, 1, 0x0, 1);
+	DISPLAYED_EQ("debug info page #0", VB2_SCREEN_DEBUG_INFO,
+		     MOCK_IGNORE, 2, 0x2, 0);
+	DISPLAYED_EQ("debug info page #1", VB2_SCREEN_DEBUG_INFO,
+		     MOCK_IGNORE, 2, 0x0, 1);
+	DISPLAYED_EQ("debug info page #1", VB2_SCREEN_DEBUG_INFO,
+		     MOCK_IGNORE, 3, 0x0, 1);
+	DISPLAYED_EQ("back to root screen", VB2_SCREEN_RECOVERY_SELECT,
+		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
 	DISPLAYED_NO_EXTRA();
 
 	VB2_DEBUG("...done.\n");
@@ -1001,7 +1125,7 @@
 	TEST_EQ(vb2_developer_menu(ctx), VB2_SUCCESS,
 		"dev mode screen: set default selection to boot internal");
 	DISPLAYED_EQ("dev mode screen", VB2_SCREEN_DEVELOPER_MODE,
-		     MOCK_IGNORE, 2, MOCK_IGNORE);
+		     MOCK_IGNORE, 2, MOCK_IGNORE, MOCK_IGNORE);
 
 	reset_common_data(FOR_DEVELOPER);
 	add_mock_vbtlk(VB2_SUCCESS, VB_DISK_FLAG_REMOVABLE);
@@ -1009,7 +1133,7 @@
 	TEST_EQ(vb2_developer_menu(ctx), VB2_SUCCESS,
 		"dev mode screen: set default selection to boot external");
 	DISPLAYED_EQ("dev mode screen", VB2_SCREEN_DEVELOPER_MODE,
-		     MOCK_IGNORE, 3, MOCK_IGNORE);
+		     MOCK_IGNORE, 3, MOCK_IGNORE, MOCK_IGNORE);
 
 	/* Dev mode: disabled item mask */
 	reset_common_data(FOR_DEVELOPER);
@@ -1017,7 +1141,7 @@
 	TEST_EQ(vb2_developer_menu(ctx), VB2_SUCCESS,
 		"dev mode screen: no disabled item mask");
 	DISPLAYED_EQ("dev mode screen", VB2_SCREEN_DEVELOPER_MODE,
-		     MOCK_IGNORE, MOCK_IGNORE, 0x0);
+		     MOCK_IGNORE, MOCK_IGNORE, 0x0, MOCK_IGNORE);
 
 	reset_common_data(FOR_DEVELOPER);
 	gbb.flags |= VB2_GBB_FLAG_FORCE_DEV_SWITCH_ON;
@@ -1025,7 +1149,7 @@
 	TEST_EQ(vb2_developer_menu(ctx), VB2_SUCCESS,
 		"dev mode screen: disable to_norm item");
 	DISPLAYED_EQ("dev mode screen", VB2_SCREEN_DEVELOPER_MODE,
-		     MOCK_IGNORE, MOCK_IGNORE, 0x2);
+		     MOCK_IGNORE, MOCK_IGNORE, 0x2, MOCK_IGNORE);
 
 	reset_common_data(FOR_DEVELOPER);
 	add_mock_vbtlk(VB2_SUCCESS, VB_DISK_FLAG_FIXED);
@@ -1033,7 +1157,7 @@
 	TEST_EQ(vb2_developer_menu(ctx), VB2_SUCCESS,
 		"dev mode screen: disable boot external");
 	DISPLAYED_EQ("dev mode screen", VB2_SCREEN_DEVELOPER_MODE,
-		     MOCK_IGNORE, MOCK_IGNORE, 0x8);
+		     MOCK_IGNORE, MOCK_IGNORE, 0x8, MOCK_IGNORE);
 
 	/* Dev mode screen */
 	reset_common_data(FOR_DEVELOPER);  /* Select #2 by default */
@@ -1056,19 +1180,19 @@
 	DISPLAYED_PASS();
 	DISPLAYED_PASS();
 	DISPLAYED_EQ("dev mode", VB2_SCREEN_DEVELOPER_MODE,
-		     MOCK_IGNORE, 0, MOCK_IGNORE);
+		     MOCK_IGNORE, 0, MOCK_IGNORE, MOCK_IGNORE);
 	DISPLAYED_EQ("#0: language menu", VB2_SCREEN_LANGUAGE_SELECT,
-		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
+		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
 	/* #1: Return to secure mode */
 	DISPLAYED_PASS();
 	DISPLAYED_EQ("dev mode", VB2_SCREEN_DEVELOPER_MODE,
-		     MOCK_IGNORE, 1, MOCK_IGNORE);
+		     MOCK_IGNORE, 1, MOCK_IGNORE, MOCK_IGNORE);
 	DISPLAYED_EQ("#1: return to secure mode", VB2_SCREEN_DEVELOPER_TO_NORM,
-		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
+		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
 	/* #2: Boot internal */
 	DISPLAYED_PASS();
 	DISPLAYED_EQ("dev mode", VB2_SCREEN_DEVELOPER_MODE,
-		     MOCK_IGNORE, 2, MOCK_IGNORE);
+		     MOCK_IGNORE, 2, MOCK_IGNORE, MOCK_IGNORE);
 	VB2_DEBUG("#2: boot internal (no extra screen)\n");
 	DISPLAYED_NO_EXTRA();
 
@@ -1081,7 +1205,7 @@
 		"dev mode screen");
 	/* #3: Boot external */
 	DISPLAYED_EQ("dev mode", VB2_SCREEN_DEVELOPER_MODE,
-		     MOCK_IGNORE, 3, MOCK_IGNORE);
+		     MOCK_IGNORE, 3, MOCK_IGNORE, MOCK_IGNORE);
 	VB2_DEBUG("#3: boot external (no extra screen)\n");
 	DISPLAYED_NO_EXTRA();
 
@@ -1101,13 +1225,13 @@
 	DISPLAYED_PASS();
 	DISPLAYED_PASS();
 	DISPLAYED_EQ("dev mode", VB2_SCREEN_DEVELOPER_MODE,
-		     MOCK_IGNORE, 4, MOCK_IGNORE);
+		     MOCK_IGNORE, 4, MOCK_IGNORE, MOCK_IGNORE);
 	DISPLAYED_EQ("#4: advanced options", VB2_SCREEN_ADVANCED_OPTIONS,
-		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
+		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
 	/* End of menu */
 	DISPLAYED_PASS();
 	DISPLAYED_EQ("end of menu", VB2_SCREEN_DEVELOPER_MODE,
-		     MOCK_IGNORE, 5, MOCK_IGNORE);
+		     MOCK_IGNORE, 5, MOCK_IGNORE, MOCK_IGNORE);
 	DISPLAYED_NO_EXTRA();
 
 	/* Advanced options screen */
@@ -1134,19 +1258,19 @@
 	DISPLAYED_PASS();
 	/* #0: Language menu */
 	DISPLAYED_EQ("advanced options", VB2_SCREEN_ADVANCED_OPTIONS,
-		     MOCK_IGNORE, 0, 0x2);
+		     MOCK_IGNORE, 0, 0x2, MOCK_IGNORE);
 	DISPLAYED_EQ("#0: language menu", VB2_SCREEN_LANGUAGE_SELECT,
-		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
+		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
 	/* #1: (Disabled) */
 	/* #2: Back */
 	DISPLAYED_PASS();
 	DISPLAYED_EQ("advanced options", VB2_SCREEN_ADVANCED_OPTIONS,
-		     MOCK_IGNORE, 2, 0x2);
+		     MOCK_IGNORE, 2, 0x2, MOCK_IGNORE);
 	DISPLAYED_EQ("#2: back", VB2_SCREEN_DEVELOPER_MODE,
-		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
+		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
 	/* End of menu */
 	DISPLAYED_EQ("end of menu", VB2_SCREEN_ADVANCED_OPTIONS,
-		     MOCK_IGNORE, 2, MOCK_IGNORE);
+		     MOCK_IGNORE, 2, MOCK_IGNORE, MOCK_IGNORE);
 	DISPLAYED_NO_EXTRA();
 
 	VB2_DEBUG("...done.\n");
@@ -1171,18 +1295,18 @@
 	/* #0: Language menu */
 	DISPLAYED_PASS();
 	DISPLAYED_EQ("broken screen", VB2_SCREEN_RECOVERY_BROKEN,
-		     MOCK_IGNORE, 0, 0x0);
+		     MOCK_IGNORE, 0, 0x0, MOCK_IGNORE);
 	DISPLAYED_EQ("#0: language menu", VB2_SCREEN_LANGUAGE_SELECT,
-		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
+		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
 	/* #1: Advanced options */
 	DISPLAYED_PASS();
 	DISPLAYED_EQ("broken screen", VB2_SCREEN_RECOVERY_BROKEN,
-		     MOCK_IGNORE, 1, 0x0);
+		     MOCK_IGNORE, 1, 0x0, MOCK_IGNORE);
 	DISPLAYED_EQ("#1: advanced options", VB2_SCREEN_ADVANCED_OPTIONS,
-		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
+		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
 	/* End of menu */
 	DISPLAYED_EQ("end of menu", VB2_SCREEN_RECOVERY_BROKEN,
-		     MOCK_IGNORE, 1, MOCK_IGNORE);
+		     MOCK_IGNORE, 1, MOCK_IGNORE, MOCK_IGNORE);
 	DISPLAYED_NO_EXTRA();
 
 	/* Advanced options screen */
@@ -1205,19 +1329,19 @@
 	/* #0: Language menu */
 	DISPLAYED_PASS();
 	DISPLAYED_EQ("advanced options", VB2_SCREEN_ADVANCED_OPTIONS,
-		     MOCK_IGNORE, 0, 0x2);
+		     MOCK_IGNORE, 0, 0x2, MOCK_IGNORE);
 	DISPLAYED_EQ("#0: language menu", VB2_SCREEN_LANGUAGE_SELECT,
-		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
+		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
 	/* #1: (Disabled) */
 	/* #2: Back */
 	DISPLAYED_PASS();
 	DISPLAYED_EQ("advanced options", VB2_SCREEN_ADVANCED_OPTIONS,
-		     MOCK_IGNORE, 2, 0x2);
+		     MOCK_IGNORE, 2, 0x2, MOCK_IGNORE);
 	DISPLAYED_EQ("#2: back", VB2_SCREEN_RECOVERY_BROKEN,
-		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
+		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
 	/* End of menu */
 	DISPLAYED_EQ("end of menu", VB2_SCREEN_ADVANCED_OPTIONS,
-		     MOCK_IGNORE, 2, MOCK_IGNORE);
+		     MOCK_IGNORE, 2, MOCK_IGNORE, MOCK_IGNORE);
 	DISPLAYED_NO_EXTRA();
 
 	VB2_DEBUG("...done.\n");
@@ -1252,31 +1376,31 @@
 	/* #0: Language menu */
 	DISPLAYED_PASS();
 	DISPLAYED_EQ("recovery select", VB2_SCREEN_RECOVERY_SELECT,
-		     MOCK_IGNORE, 0, 0x0);
+		     MOCK_IGNORE, 0, 0x0, MOCK_IGNORE);
 	DISPLAYED_EQ("#0: language menu", VB2_SCREEN_LANGUAGE_SELECT,
-		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
+		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
 	/* #1: Phone recovery */
 	DISPLAYED_PASS();
 	DISPLAYED_EQ("recovery select", VB2_SCREEN_RECOVERY_SELECT,
-		     MOCK_IGNORE, 1, 0x0);
+		     MOCK_IGNORE, 1, 0x0, MOCK_IGNORE);
 	DISPLAYED_EQ("#1: phone recovery", VB2_SCREEN_RECOVERY_PHONE_STEP1,
-		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
+		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
 	/* #2: External disk recovery */
 	DISPLAYED_PASS();
 	DISPLAYED_EQ("recovery select", VB2_SCREEN_RECOVERY_SELECT,
-		     MOCK_IGNORE, 2, 0x0);
+		     MOCK_IGNORE, 2, 0x0, MOCK_IGNORE);
 	DISPLAYED_EQ("#2: disk recovery", VB2_SCREEN_RECOVERY_DISK_STEP1,
-		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
+		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
 	/* #3: Advanced options */
 	DISPLAYED_PASS();
 	DISPLAYED_EQ("recovery select", VB2_SCREEN_RECOVERY_SELECT,
-		     MOCK_IGNORE, 3, 0x0);
+		     MOCK_IGNORE, 3, 0x0, MOCK_IGNORE);
 	DISPLAYED_EQ("#3: advanced options", VB2_SCREEN_ADVANCED_OPTIONS,
-		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
+		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
 	/* End of menu */
 	DISPLAYED_PASS();
 	DISPLAYED_EQ("end of menu", VB2_SCREEN_RECOVERY_SELECT,
-		     MOCK_IGNORE, 4, MOCK_IGNORE);
+		     MOCK_IGNORE, 4, MOCK_IGNORE, MOCK_IGNORE);
 	DISPLAYED_NO_EXTRA();
 
 	/* Advanced options screen */
@@ -1307,25 +1431,25 @@
 	/* #0: Language menu */
 	DISPLAYED_PASS();
 	DISPLAYED_EQ("advanced options", VB2_SCREEN_ADVANCED_OPTIONS,
-		     MOCK_IGNORE, 0, 0x0);
+		     MOCK_IGNORE, 0, 0x0, MOCK_IGNORE);
 	DISPLAYED_EQ("#0: language menu", VB2_SCREEN_LANGUAGE_SELECT,
-		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
+		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
 	/* #1: Enable dev mode */
 	DISPLAYED_PASS();
 	DISPLAYED_EQ("advanced options", VB2_SCREEN_ADVANCED_OPTIONS,
-		     MOCK_IGNORE, 1, 0x0);
+		     MOCK_IGNORE, 1, 0x0, MOCK_IGNORE);
 	DISPLAYED_EQ("#1: enable dev mode", VB2_SCREEN_RECOVERY_TO_DEV,
-		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
+		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
 	/* #2: Back */
 	DISPLAYED_PASS();
 	DISPLAYED_EQ("advanced options", VB2_SCREEN_ADVANCED_OPTIONS,
-		     MOCK_IGNORE, 2, 0x0);
+		     MOCK_IGNORE, 2, 0x0, MOCK_IGNORE);
 	DISPLAYED_EQ("#2: back", VB2_SCREEN_RECOVERY_SELECT,
-		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
+		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
 	/* End of menu */
 	DISPLAYED_PASS();
 	DISPLAYED_EQ("end of menu", VB2_SCREEN_ADVANCED_OPTIONS,
-		     MOCK_IGNORE, 2, 0x0);
+		     MOCK_IGNORE, 2, 0x0, MOCK_IGNORE);
 	DISPLAYED_NO_EXTRA();
 
 	VB2_DEBUG("...done.\n");
@@ -1337,6 +1461,7 @@
 	broken_recovery_tests();
 	manual_recovery_tests();
 	language_selection_tests();
+	debug_info_tests();
 
 	/* Screen displayed */
 	developer_screen_tests();