minidiag: add entry

The entry `Launch diagnostics` in recovery screen will set
VB2_NV_DIAG_REQUEST and reboot.

BRANCH=none
BUG=b:155848434
TEST=( export CC=x86_64-pc-linux-gnu-clang DEBUG=1 DIAGNOSTIC_UI=0
    MINIMAL=1 TPM2_MODE= MOCK_TPM=; make clean &&
    make -j32 test_setup && make runtests; echo $? )
TEST=( export CC=x86_64-pc-linux-gnu-clang DEBUG=1 DIAGNOSTIC_UI=1
    MINIMAL=1 TPM2_MODE= MOCK_TPM=; make clean &&
    make -j32 test_setup && make runtests; echo $? )
TEST=LOCALES="en" USE="menu_ui minidiag" emerge-nami chromeos-bmpblk
     vboot_reference depthcharge chromeos-bootimage
TEST=Unset by
       &= ~VB2_SECDATA_KERNEL_FLAG_DIAGNOSTIC_UI_DISABLED manually;
     trigger recovery mode on device;
     visually confirmed `Launch diagnostics` is shown;
     select, reboot and confirm behavior from serial output:
       "Unset diagnostic request (undo display init)".

Cq-Depend: chromium:2282643, chromium:2282664, chromium:2289814
Change-Id: I34af282edee66c5b9943e2d11fb514bac9ccddd8
Signed-off-by: Chun-Ta Lin <itspeter@google.com>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/vboot_reference/+/2282867
Tested-by: Chun-ta Lin <itspeter@chromium.org>
Reviewed-by: Joel Kitching <kitching@chromium.org>
diff --git a/firmware/2lib/2ui_screens.c b/firmware/2lib/2ui_screens.c
index 4af8095..2510392 100644
--- a/firmware/2lib/2ui_screens.c
+++ b/firmware/2lib/2ui_screens.c
@@ -312,6 +312,15 @@
 
 #define RECOVERY_SELECT_ITEM_PHONE 1
 #define RECOVERY_SELECT_ITEM_EXTERNAL_DISK 2
+#define RECOVERY_SELECT_ITEM_DIAGNOSTICS 3
+
+/* Set VB2_NV_DIAG_REQUEST and reboot. */
+static vb2_error_t launch_diagnostics_action(struct vb2_ui_context *ui)
+{
+	vb2_nv_set(ui->ctx, VB2_NV_DIAG_REQUEST, 1);
+	VB2_DEBUG("Diagnostics requested, rebooting\n");
+	return VB2_REQUEST_REBOOT;
+}
 
 vb2_error_t recovery_select_init(struct vb2_ui_context *ui)
 {
@@ -322,6 +331,11 @@
 			1 << RECOVERY_SELECT_ITEM_PHONE;
 		ui->state->selected_item = RECOVERY_SELECT_ITEM_EXTERNAL_DISK;
 	}
+
+        if (!DIAGNOSTIC_UI || !vb2api_diagnostic_ui_enabled(ui->ctx))
+                ui->state->disabled_item_mask |=
+			1 << RECOVERY_SELECT_ITEM_DIAGNOSTICS;
+
 	return VB2_REQUEST_UI_CONTINUE;
 }
 
@@ -335,6 +349,10 @@
 		.text = "Recovery using external disk",
 		.target = VB2_SCREEN_RECOVERY_DISK_STEP1,
 	},
+	[RECOVERY_SELECT_ITEM_DIAGNOSTICS] = {
+		.text = "Launch diagnostics",
+		.action = launch_diagnostics_action,
+	},
 	ADVANCED_OPTIONS_ITEM,
 	POWER_OFF_ITEM,
 };
diff --git a/tests/vb2_ui_tests.c b/tests/vb2_ui_tests.c
index d4e0ae5..2dca546 100644
--- a/tests/vb2_ui_tests.c
+++ b/tests/vb2_ui_tests.c
@@ -971,6 +971,20 @@
 		DISPLAYED_NO_EXTRA();
 	}
 
+	/* Enter diagnostics */
+	if (DIAGNOSTIC_UI) {
+		/* Launch diagnostics is inside manual recovery */
+		reset_common_data(FOR_MANUAL_RECOVERY);
+		add_mock_keypress(VB_KEY_DOWN);
+		add_mock_keypress(VB_KEY_DOWN);
+		add_mock_keypress(VB_KEY_ENTER);
+		TEST_EQ(vb2_manual_recovery_menu(ctx),
+			VB2_REQUEST_REBOOT,
+			"Reboot immediately after request diagnostics");
+		TEST_EQ(vb2_nv_get(ctx, VB2_NV_DIAG_REQUEST), 1,
+			"VB2_NV_DIAG_REQUEST is set");
+	}
+
 	VB2_DEBUG("...done.\n");
 }
 
@@ -1382,8 +1396,11 @@
 	add_mock_keypress(VB_KEY_ESC);
 	add_mock_keypress(VB_KEY_DOWN);
 	add_mock_keypress(VB_KEY_ENTER);
-	/* #3: Advanced options */
 	add_mock_keypress(VB_KEY_ESC);
+	/* #3: Launch diagnostics */
+	if (DIAGNOSTIC_UI)
+		add_mock_keypress(VB_KEY_DOWN);
+	/* #4: Advanced options */
 	add_mock_keypress(VB_KEY_DOWN);
 	add_mock_keypress(VB_KEY_ENTER);
 	/* End of menu */
@@ -1396,31 +1413,38 @@
 	/* #0: Language menu */
 	DISPLAYED_PASS();
 	DISPLAYED_EQ("recovery select", VB2_SCREEN_RECOVERY_SELECT,
-		     MOCK_IGNORE, 0, 0x0, MOCK_IGNORE);
+		     MOCK_IGNORE, 0, DIAGNOSTIC_UI ? 0x0 : (1 << 3),
+		     MOCK_IGNORE);
 	DISPLAYED_EQ("#0: language menu", VB2_SCREEN_LANGUAGE_SELECT,
 		     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);
+		     MOCK_IGNORE, 1, MOCK_IGNORE, MOCK_IGNORE);
 	DISPLAYED_EQ("#1: phone recovery", VB2_SCREEN_RECOVERY_PHONE_STEP1,
 		     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);
+		     MOCK_IGNORE, 2, MOCK_IGNORE, MOCK_IGNORE);
 	DISPLAYED_EQ("#2: disk recovery", VB2_SCREEN_RECOVERY_DISK_STEP1,
 		     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);
+		MOCK_IGNORE, 2, MOCK_IGNORE, MOCK_IGNORE);
+	/* #3: Launch diagnostics */
+	if (DIAGNOSTIC_UI)
+		DISPLAYED_EQ("recovery select", VB2_SCREEN_RECOVERY_SELECT,
+			MOCK_IGNORE, 3, MOCK_IGNORE, MOCK_IGNORE);
+	/* #4: Advanced options */
+	DISPLAYED_EQ("recovery select", VB2_SCREEN_RECOVERY_SELECT,
+		     MOCK_IGNORE, 4, MOCK_IGNORE, MOCK_IGNORE);
+
 	DISPLAYED_EQ("#3: advanced options", VB2_SCREEN_ADVANCED_OPTIONS,
 		     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);
+		     MOCK_IGNORE, 5, MOCK_IGNORE, MOCK_IGNORE);
 	DISPLAYED_NO_EXTRA();
 
 	/* Advanced options screen */
@@ -1428,6 +1452,8 @@
 	/* #0: Language menu */
 	add_mock_keypress(VB_KEY_DOWN);
 	add_mock_keypress(VB_KEY_DOWN);
+	if (DIAGNOSTIC_UI)
+		add_mock_keypress(VB_KEY_DOWN);
 	add_mock_keypress(VB_KEY_ENTER);
 	add_mock_keypress(VB_KEY_UP);
 	add_mock_keypress(VB_KEY_ENTER);
@@ -1451,6 +1477,8 @@
 		"advanced options screen");
 	DISPLAYED_PASS();
 	DISPLAYED_PASS();
+	if (DIAGNOSTIC_UI)
+		DISPLAYED_PASS();
 	DISPLAYED_PASS();
 	/* #0: Language menu */
 	DISPLAYED_PASS();