vboot/ui: Force to reacquire log on initialization

Force to reacquire a newer firmware log snapshot every time when the
user enters the firmware log screen.

Re-entering (e.g. back from language selection or debug info tab) will
still show the cached firmware log string.

BUG=b:146399181, b:168442372
TEST=make clean && make runtests
TEST=Build locally, navigate to the firmware log screen,
     go back, and enter the firmware log screen again.
BRANCH=none

Cq-Depend: chromium:2409742
Change-Id: I8c3eea23446c58603ce698a86f1aca4b264ebb0e
Signed-off-by: Hsuan Ting Chen <roccochen@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/vboot_reference/+/2411761
Commit-Queue: Yu-Ping Wu <yupingso@chromium.org>
diff --git a/firmware/2lib/2stub.c b/firmware/2lib/2stub.c
index 697b0fc..dba852e 100644
--- a/firmware/2lib/2stub.c
+++ b/firmware/2lib/2stub.c
@@ -81,7 +81,7 @@
 }
 
 __attribute__((weak))
-const char *vb2ex_get_firmware_log(void)
+const char *vb2ex_get_firmware_log(int reset)
 {
 	return NULL;
 }
diff --git a/firmware/2lib/2ui_screens.c b/firmware/2lib/2ui_screens.c
index 6cdae95..3e7ead2 100644
--- a/firmware/2lib/2ui_screens.c
+++ b/firmware/2lib/2ui_screens.c
@@ -314,7 +314,7 @@
 
 static vb2_error_t firmware_log_init(struct vb2_ui_context *ui)
 {
-	const char *log_string = vb2ex_get_firmware_log();
+	const char *log_string = vb2ex_get_firmware_log(1);
 	if (!log_string) {
 		VB2_DEBUG("ERROR: Failed to retrieve firmware log\n");
 		ui->error_code = VB2_UI_ERROR_FIRMWARE_LOG;
@@ -334,7 +334,7 @@
 
 static vb2_error_t firmware_log_reinit(struct vb2_ui_context *ui)
 {
-	const char *log_string = vb2ex_get_firmware_log();
+	const char *log_string = vb2ex_get_firmware_log(0);
 	if (!log_string) {
 		VB2_DEBUG("ERROR: Failed to retrieve firmware log\n");
 		ui->error_code = VB2_UI_ERROR_FIRMWARE_LOG;
diff --git a/firmware/2lib/include/2api.h b/firmware/2lib/include/2api.h
index dc66167..9f4dbcb 100644
--- a/firmware/2lib/include/2api.h
+++ b/firmware/2lib/include/2api.h
@@ -1440,12 +1440,14 @@
  *
  * Return a pointer to the full firmware log string which is guaranteed to be
  * null-terminated.  The function implementation should snapshot the full
- * firmware log when it is called the first time.  Subsequent calls should
- * return the same pointer.
+ * firmware log when it is called.  If `reset` is not zero, it will reset the
+ * firmware log snapshot.
  *
+ * @param reset		Discard the current firmware log snapshot and
+ *			reacquire a new one.
  * @return The pointer to the full firmware log string.  NULL on error.
  */
-const char *vb2ex_get_firmware_log(void);
+const char *vb2ex_get_firmware_log(int reset);
 
 /**
  * Specify the string to be used for an upcoming log screen display.
diff --git a/tests/vb2_ui_tests.c b/tests/vb2_ui_tests.c
index 6691660..5dc1a2e 100644
--- a/tests/vb2_ui_tests.c
+++ b/tests/vb2_ui_tests.c
@@ -91,6 +91,11 @@
 
 static int mock_enable_dev_mode;
 
+#define MOCK_PREPARE_LOG_SIZE 32
+
+static int mock_snapshot_count;
+static char mock_prepare_log[64][MOCK_PREPARE_LOG_SIZE];
+static int mock_prepare_log_count;
 static uint32_t mock_log_page_count;
 
 static void add_mock_key(uint32_t press, int trusted)
@@ -321,6 +326,8 @@
 	mock_enable_dev_mode = 0;
 
 	/* For vb2ex_prepare_log_screen */
+	mock_snapshot_count = 0;
+	mock_prepare_log_count = 0;
 	mock_log_page_count = 1;
 
 	/* Avoid Iteration #0 */
@@ -526,17 +533,26 @@
 	return "mocked debug info";
 }
 
-const char *vb2ex_get_firmware_log(void)
+const char *vb2ex_get_firmware_log(int reset)
 {
-	return "mocked firmware log";
+	static char mock_firmware_log_buf[MOCK_PREPARE_LOG_SIZE];
+	if (reset)
+		mock_snapshot_count++;
+	snprintf(mock_firmware_log_buf, MOCK_PREPARE_LOG_SIZE,
+		 "%d", mock_snapshot_count);
+	return mock_firmware_log_buf;
 }
 
 uint32_t vb2ex_prepare_log_screen(const char *str)
 {
+	if (mock_prepare_log_count < ARRAY_SIZE(mock_prepare_log))
+		strncpy(mock_prepare_log[mock_prepare_log_count],
+			str, MOCK_PREPARE_LOG_SIZE);
+	mock_prepare_log_count++;
+
 	return mock_log_page_count;
 }
 
-
 /* Tests */
 static void developer_tests(void)
 {
@@ -1168,6 +1184,73 @@
 	VB2_DEBUG("...done.\n");
 }
 
+static void firmware_log_tests(void)
+{
+	VB2_DEBUG("Testing firmware log screens...\n");
+
+	/* Get firmware log */
+	reset_common_data(FOR_MANUAL_RECOVERY);
+	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_DOWN);
+	add_mock_keypress(VB_KEY_DOWN);
+	add_mock_keypress(VB_KEY_ENTER);
+	TEST_EQ(vb2_manual_recovery_menu(ctx), VB2_REQUEST_SHUTDOWN,
+		"get firmware log");
+	TEST_EQ(mock_prepare_log_count, 1,
+		"  prepared firmware log once");
+	TEST_EQ(strcmp(mock_prepare_log[0], "1"), 0,
+		"  got correct firmware log");
+
+	/* Enter firmware log screen again will reacquire a newer one */
+	reset_common_data(FOR_MANUAL_RECOVERY);
+	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_DOWN);
+	add_mock_keypress(VB_KEY_DOWN);
+	add_mock_keypress(VB_KEY_ENTER);
+	add_mock_keypress(VB_KEY_ESC);
+	add_mock_keypress(VB_KEY_ENTER);
+	TEST_EQ(vb2_manual_recovery_menu(ctx), VB2_REQUEST_SHUTDOWN,
+		"enter the screen again and reacquire a new log");
+	TEST_EQ(mock_prepare_log_count, 2,
+		"  prepared firmware log twice");
+	TEST_EQ(strcmp(mock_prepare_log[0], "1"), 0,
+		"  got correct firmware log");
+	TEST_EQ(strcmp(mock_prepare_log[1], "2"), 0,
+		"  got a new firmware log");
+
+	/* Back to firmware log screen again will not reacquire a newer one */
+	reset_common_data(FOR_MANUAL_RECOVERY);
+	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_DOWN);
+	add_mock_keypress(VB_KEY_DOWN);
+	add_mock_keypress(VB_KEY_ENTER);
+	add_mock_keypress('\t');  /* enter debug info screen */
+	add_mock_keypress(VB_KEY_ESC);
+	TEST_EQ(vb2_manual_recovery_menu(ctx), VB2_REQUEST_SHUTDOWN,
+		"back to the screen and do not reacquire a new log");
+	TEST_EQ(mock_prepare_log_count, 3,
+		"  prepared firmware log three times");
+	TEST_EQ(strcmp(mock_prepare_log[0], "1"), 0,
+		"  got correct firmware log");
+	/* Skip entry #1 which is for preparing debug info */
+	TEST_EQ(strcmp(mock_prepare_log[2], "1"), 0,
+		"  got the same firmware log");
+
+	VB2_DEBUG("...done.\n");
+}
+
 static void developer_screen_tests(void)
 {
 	VB2_DEBUG("Testing developer mode screens...\n");
@@ -1669,6 +1752,7 @@
 	manual_recovery_tests();
 	language_selection_tests();
 	debug_info_tests();
+	firmware_log_tests();
 
 	/* Screen displayed */
 	developer_screen_tests();