2ec_sync: Reactivate VB2_CONTEXT_EC_SYNC_SLOW

With EFS2, the boot flow for EC sync is usually:

Boot 0: Initial boot with EC in RW. AP requests EC reset for EC sync.
Boot 1: EC reboots to RO. AP wants to perform EC sync, but display isn't
        initialized. Therefore, AP sets VB2_NV_DISPLAY_REQUEST and
	reboot.
Boot 2: EC is still in RO. AP sees VB2_NV_DISPLAY_REQUEST and
        initializes display. Then, AP shows EC sync screen and performs
	EC sync.

The reboot for VB2_NV_DISPLAY_REQUEST can actually be avoided, because
in boot 0 above it's already known that we're going to do EC sync in the
next boot.

To save us one reboot, reactivate the VB2_CONTEXT_EC_SYNC_SLOW flag.
When the flag is set, VB2_NV_DISPLAY_REQUEST will be set at the end of
boot 0, right before EC reset.

BUG=b:350885214
TEST=make run2tests
TEST=emerge-geralt libpayload depthcharge
BRANCH=none

Change-Id: I1eaae4a7219f4e755e83e3478684b74f894cbfc2
Signed-off-by: Yu-Ping Wu <[email protected]>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/vboot_reference/+/5676957
Reviewed-by: Julius Werner <[email protected]>
diff --git a/firmware/2lib/2ec_sync.c b/firmware/2lib/2ec_sync.c
index d04bc9c..6475dc4 100644
--- a/firmware/2lib/2ec_sync.c
+++ b/firmware/2lib/2ec_sync.c
@@ -287,6 +287,10 @@
 	 */
 	if ((sd->flags & SYNC_FLAG(VB_SELECT_FIRMWARE_EC_ACTIVE)) &&
 	    (sd->flags & VB2_SD_FLAG_ECSYNC_EC_IN_RW) && !EC_EFS) {
+		if (ctx->flags & VB2_CONTEXT_EC_SYNC_SLOW) {
+			/* Ignore the return value. */
+			vb2api_need_reboot_for_display(ctx);
+		}
 		return VB2_REQUEST_REBOOT_EC_TO_RO;
 	}
 
diff --git a/firmware/2lib/2misc.c b/firmware/2lib/2misc.c
index 807ffbc..77ff399 100644
--- a/firmware/2lib/2misc.c
+++ b/firmware/2lib/2misc.c
@@ -492,6 +492,7 @@
 	}
 }
 
+test_mockable
 int vb2api_need_reboot_for_display(struct vb2_context *ctx)
 {
 	if (!(vb2_get_sd(ctx)->flags & VB2_SD_FLAG_DISPLAY_AVAILABLE)) {
diff --git a/firmware/2lib/include/2context.h b/firmware/2lib/include/2context.h
index eee45b8..4d36585 100644
--- a/firmware/2lib/include/2context.h
+++ b/firmware/2lib/include/2context.h
@@ -107,9 +107,9 @@
 	/*
 	 * EC software sync is slow to update; warning screen should be
 	 * displayed.  Caller may set this flag at any time before calling
-	 * vb2api_kernel_phase2().  Deprecated as part of chromium:1038259.
+	 * vb2api_kernel_phase2().
 	 */
-	VB2_CONTEXT_DEPRECATED_EC_SYNC_SLOW = (1 << 16),
+	VB2_CONTEXT_EC_SYNC_SLOW = (1 << 16),
 
 	/*
 	 * EC firmware supports early firmware selection; two EC images exist,
diff --git a/tests/vb2_ec_sync_tests.c b/tests/vb2_ec_sync_tests.c
index c3a9d2c..a60f93a 100644
--- a/tests/vb2_ec_sync_tests.c
+++ b/tests/vb2_ec_sync_tests.c
@@ -30,6 +30,7 @@
 static int ec_vboot_done_calls;
 
 static int mock_display_available;
+static int need_display_called;
 static uint8_t mock_ec_ro_hash[32];
 static uint8_t mock_ec_rw_hash[32];
 static uint8_t hmir[32];
@@ -58,6 +59,7 @@
 	memset(&gbb, 0, sizeof(gbb));
 
 	mock_display_available = 1;
+	need_display_called = 0;
 
 	ec_ro_updated = 0;
 	ec_rw_updated = 0;
@@ -108,6 +110,12 @@
 	return &gbb;
 }
 
+int vb2api_need_reboot_for_display(struct vb2_context *c)
+{
+	need_display_called = 1;
+	return !mock_display_available;
+}
+
 vb2_error_t vb2ex_ec_running_rw(int *in_rw)
 {
 	*in_rw = ec_run_image;
@@ -161,8 +169,9 @@
 	if (update_retval)
 		return update_retval;
 
-	if (!mock_display_available)
-		return VB2_REQUEST_REBOOT;
+	if (ctx->flags & VB2_CONTEXT_EC_SYNC_SLOW)
+		if (vb2api_need_reboot_for_display(ctx))
+			return VB2_REQUEST_REBOOT;
 
 	if (select == VB_SELECT_FIRMWARE_READONLY) {
 		ec_ro_updated = 1;
@@ -335,6 +344,22 @@
 	TEST_EQ(ec_rw_protected, 0, "  ec rw protected");
 	TEST_EQ(ec_run_image, 1, "  ec run image");
 
+	/* Hexp != Hmir == Heff (display not available) */
+	ResetMocks();
+	hmir[0] = 43;
+	vb2_secdata_kernel_set_ec_hash(ctx, hmir);
+	ec_run_image = 1;
+	mock_ec_rw_hash[0] = 43;
+	mock_display_available = 0;
+	ctx->flags |= VB2_CONTEXT_EC_SYNC_SLOW;
+	test_ssync(VB2_REQUEST_REBOOT_EC_TO_RO,
+		   0, "Reboot after synching Hmir (display not available)");
+	TEST_EQ(ec_ro_updated, 0, "  ec ro updated");
+	TEST_EQ(ec_rw_updated, 0, "  ec rw updated");
+	TEST_EQ(ec_rw_protected, 0, "  ec rw protected");
+	TEST_EQ(ec_run_image, 1, "  ec run image");
+	TEST_TRUE(need_display_called, "  need display");
+
 	/* Hexp == Hmir != Heff */
 	ResetMocks();
 	ec_run_image = 0;
@@ -448,24 +473,28 @@
 	ResetMocks();
 	mock_ec_rw_hash[0]++;
 	mock_display_available = 0;
+	ctx->flags |= VB2_CONTEXT_EC_SYNC_SLOW;
 	test_ssync(VB2_REQUEST_REBOOT, 0,
 		   "Reboot for display - ec rw");
 	TEST_EQ(ec_ro_updated, 0, "  ec ro updated");
 	TEST_EQ(ec_rw_updated, 0, "  ec rw updated");
 	TEST_EQ(ec_rw_protected, 0, "  ec rw protected");
 	TEST_EQ(ec_run_image, 0, "  ec run image");
+	TEST_TRUE(need_display_called, "  need display");
 
 	/* Display not available - RO */
 	ResetMocks();
 	vb2_nv_set(ctx, VB2_NV_TRY_RO_SYNC, 1);
 	mock_ec_ro_hash[0]++;
 	mock_display_available = 0;
+	ctx->flags |= VB2_CONTEXT_EC_SYNC_SLOW;
 	test_ssync(VB2_REQUEST_REBOOT, 0,
 		   "Reboot for display - ec ro");
 	TEST_EQ(ec_ro_updated, 0, "  ec ro updated");
 	TEST_EQ(ec_rw_updated, 0, "  ec rw updated");
 	TEST_EQ(ec_rw_protected, 0, "  ec rw protected");
 	TEST_EQ(ec_run_image, 1, "  ec run image");
+	TEST_TRUE(need_display_called, "  need display");
 
 	/* RW cases, no update */
 	ResetMocks();