vboot: use vboot persistent context

vb2_context object is now stored on the workbuf as part of
vb2_shared_data.  Use vboot's new API functions vb2api_init
and vb2api_relocate to create and move the workbuf.

BUG=b:124141368, chromium:994060
TEST=Build locally
BRANCH=none

Change-Id: I051be1e47bf79b15a1689d49a5d4c031e9363dfa
Signed-off-by: Joel Kitching <kitching@google.com>
Also-Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/coreboot/+/1902339
Reviewed-on: https://review.coreboot.org/c/coreboot/+/36300
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Patrick Georgi <pgeorgi@google.com>
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
Reviewed-by: Maulik V Vaghela <maulik.v.vaghela@intel.com>
diff --git a/src/mainboard/google/drallion/chromeos.c b/src/mainboard/google/drallion/chromeos.c
index 0eb311b..00571ed 100644
--- a/src/mainboard/google/drallion/chromeos.c
+++ b/src/mainboard/google/drallion/chromeos.c
@@ -95,8 +95,8 @@
 	 * and the value from the TPM would be wrong anyway since the verstage
 	 * read would have cleared the value on the TPM.
 	 *
-	 * The TPM recovery request is passed between stages through the
-	 * vboot_get_shared_data or cbmem depending on stage.
+	 * The TPM recovery request is passed between stages through vboot data
+	 * or cbmem depending on stage.
 	 */
 	if (ENV_VERSTAGE &&
 	    tlcl_cr50_get_recovery_button(&cr50_state) == TPM_SUCCESS &&
diff --git a/src/mainboard/google/sarien/chromeos.c b/src/mainboard/google/sarien/chromeos.c
index 6643d9b..bdd414c 100644
--- a/src/mainboard/google/sarien/chromeos.c
+++ b/src/mainboard/google/sarien/chromeos.c
@@ -93,8 +93,8 @@
 	 * and the value from the TPM would be wrong anyway since the verstage
 	 * read would have cleared the value on the TPM.
 	 *
-	 * The TPM recovery request is passed between stages through the
-	 * vboot_get_shared_data or cbmem depending on stage.
+	 * The TPM recovery request is passed between stages through vboot data
+	 * or cbmem depending on stage.
 	 */
 	if (ENV_VERSTAGE &&
 	    tlcl_cr50_get_recovery_button(&cr50_state) == TPM_SUCCESS &&
diff --git a/src/security/vboot/bootmode.c b/src/security/vboot/bootmode.c
index 4625bcd..0ab0431 100644
--- a/src/security/vboot/bootmode.c
+++ b/src/security/vboot/bootmode.c
@@ -26,7 +26,7 @@
 
 static int vboot_get_recovery_reason_shared_data(void)
 {
-	struct vb2_shared_data *sd = vboot_get_shared_data();
+	struct vb2_shared_data *sd = vb2_get_sd(vboot_get_context());
 	assert(sd);
 	return sd->recovery_reason;
 }
diff --git a/src/security/vboot/common.c b/src/security/vboot/common.c
index 626fbc5..043748c 100644
--- a/src/security/vboot/common.c
+++ b/src/security/vboot/common.c
@@ -24,6 +24,8 @@
 #include <security/vboot/symbols.h>
 #include <security/vboot/vboot_common.h>
 
+static struct vb2_context *vboot_ctx CAR_GLOBAL;
+
 struct vboot_working_data *vboot_get_working_data(void)
 {
 	struct vboot_working_data *wd = NULL;
@@ -40,43 +42,45 @@
 	return wd;
 }
 
-void vboot_init_work_context(struct vb2_context *ctx)
+static inline void *vboot_get_workbuf(struct vboot_working_data *wd)
 {
+	return (void *)((uintptr_t)wd + wd->buffer_offset);
+}
+
+struct vb2_context *vboot_get_context(void)
+{
+	struct vb2_context **vboot_ctx_ptr = car_get_var_ptr(&vboot_ctx);
 	struct vboot_working_data *wd;
 
-	/* First initialize the working data region. */
+	/* Return if context has already been initialized/restored. */
+	if (*vboot_ctx_ptr)
+		return *vboot_ctx_ptr;
+
 	wd = vboot_get_working_data();
-	memset(wd, 0, VB2_FIRMWARE_WORKBUF_RECOMMENDED_SIZE);
+
+	/* Restore context from a previous stage. */
+	if (vboot_logic_executed()) {
+		assert(vb2api_reinit(vboot_get_workbuf(wd),
+				     vboot_ctx_ptr) == VB2_SUCCESS);
+		return *vboot_ctx_ptr;
+	}
+
+	assert(verification_should_run());
 
 	/*
 	 * vboot prefers 16-byte alignment. This takes away 16 bytes
 	 * from the VBOOT2_WORK region, but the vboot devs said that's okay.
 	 */
+	memset(wd, 0, sizeof(*wd));
 	wd->buffer_offset = ALIGN_UP(sizeof(*wd), 16);
 	wd->buffer_size = VB2_FIRMWARE_WORKBUF_RECOMMENDED_SIZE
 			  - wd->buffer_offset;
 
-	/* Initialize the vb2_context. */
-	memset(ctx, 0, sizeof(*ctx));
-	ctx->workbuf = (void *)vboot_get_shared_data();
-	ctx->workbuf_size = wd->buffer_size;
-}
+	/* Initialize vb2_shared_data and friends. */
+	assert(vb2api_init(vboot_get_workbuf(wd), wd->buffer_size,
+			   vboot_ctx_ptr) == VB2_SUCCESS);
 
-void vboot_finalize_work_context(struct vb2_context *ctx)
-{
-	/*
-	 * Shrink buffer_size so that vboot_migrate_cbmem knows how
-	 * much of vboot_working_data needs to be copied into CBMEM
-	 * (if applicable), and so that downstream users know how much
-	 * of the workbuf is currently used.
-	 */
-	vboot_get_working_data()->buffer_size = ctx->workbuf_used;
-}
-
-struct vb2_shared_data *vboot_get_shared_data(void)
-{
-	struct vboot_working_data *wd = vboot_get_working_data();
-	return (void *)((uintptr_t)wd + wd->buffer_offset);
+	return *vboot_ctx_ptr;
 }
 
 int vboot_get_selected_region(struct region *region)
@@ -126,17 +130,25 @@
  */
 static void vboot_migrate_cbmem(int unused)
 {
-	const struct vboot_working_data *wd_preram =
+	const size_t cbmem_size = VB2_KERNEL_WORKBUF_RECOMMENDED_SIZE;
+	struct vboot_working_data *wd_preram =
 		(struct vboot_working_data *)_vboot2_work;
-	size_t cbmem_size = wd_preram->buffer_offset + wd_preram->buffer_size;
 	struct vboot_working_data *wd_cbmem =
 		cbmem_add(CBMEM_ID_VBOOT_WORKBUF, cbmem_size);
 	assert(wd_cbmem != NULL);
-
-	printk(BIOS_DEBUG,
-	       "VBOOT: copying vboot_working_data (%zu bytes) to CBMEM...\n",
-	       cbmem_size);
-	memcpy(wd_cbmem, wd_preram, cbmem_size);
+	memcpy(wd_cbmem, wd_preram, sizeof(struct vboot_working_data));
+	/*
+	 * TODO(chromium:1021452): buffer_size is uint16_t and not large enough
+	 * to hold the kernel verification workbuf size.  The only code which
+	 * reads this value is in lb_vboot_workbuf() for lb_range->range_size.
+	 * This value being zero doesn't cause any problems, since it is never
+	 * read downstream.  Fix or deprecate vboot_working_data.
+	 */
+	wd_cbmem->buffer_size = 0;
+	vb2api_relocate(vboot_get_workbuf(wd_cbmem),
+			vboot_get_workbuf(wd_preram),
+			cbmem_size - wd_cbmem->buffer_offset,
+			car_get_var_ptr(&vboot_ctx));
 }
 ROMSTAGE_CBMEM_INIT_HOOK(vboot_migrate_cbmem)
 #else
@@ -144,7 +156,7 @@
 {
 	struct vboot_working_data *wd_cbmem =
 		cbmem_add(CBMEM_ID_VBOOT_WORKBUF,
-			  VB2_FIRMWARE_WORKBUF_RECOMMENDED_SIZE);
+			  VB2_KERNEL_WORKBUF_RECOMMENDED_SIZE);
 	assert(wd_cbmem != NULL);
 }
 ROMSTAGE_CBMEM_INIT_HOOK(vboot_setup_cbmem)
diff --git a/src/security/vboot/misc.h b/src/security/vboot/misc.h
index 1458354..b45fc9c 100644
--- a/src/security/vboot/misc.h
+++ b/src/security/vboot/misc.h
@@ -29,10 +29,11 @@
 };
 
 /*
- * this is placed at the start of the vboot work buffer. selected_region is used
- * for the verstage to return the location of the selected slot. buffer is used
- * by the vboot2 core. Keep the struct CPU architecture agnostic as it crosses
- * stage boundaries.
+ * Stores vboot-related information.  selected_region is used by verstage to
+ * store the location of the selected slot.  buffer is used by vboot to store
+ * its work buffer.  vb2_context is contained within this work buffer, and is
+ * accessible via vboot_init_context() and vboot_get_context() (see below).
+ * Keep the struct CPU architecture agnostic as it crosses stage boundaries.
  */
 struct vboot_working_data {
 	struct selected_region selected_region;
@@ -54,9 +55,7 @@
  * Source: security/vboot/common.c
  */
 struct vboot_working_data *vboot_get_working_data(void);
-void vboot_init_work_context(struct vb2_context *ctx);
-void vboot_finalize_work_context(struct vb2_context *ctx);
-struct vb2_shared_data *vboot_get_shared_data(void);
+struct vb2_context *vboot_get_context(void);
 
 /* Returns 0 on success. < 0 on failure. */
 int vboot_get_selected_region(struct region *region);
diff --git a/src/security/vboot/vboot_logic.c b/src/security/vboot/vboot_logic.c
index da6231a..5110147 100644
--- a/src/security/vboot/vboot_logic.c
+++ b/src/security/vboot/vboot_logic.c
@@ -319,17 +319,17 @@
  */
 void verstage_main(void)
 {
-	struct vb2_context ctx;
+	struct vb2_context *ctx;
 	struct region_device fw_main;
 	vb2_error_t rv;
 
 	timestamp_add_now(TS_START_VBOOT);
 
 	/* Set up context and work buffer */
-	vboot_init_work_context(&ctx);
+	ctx = vboot_get_context();
 
 	/* Initialize and read nvdata from non-volatile storage. */
-	vbnv_init(ctx.nvdata);
+	vbnv_init(ctx->nvdata);
 
 	/* Set S3 resume flag if vboot should behave differently when selecting
 	 * which slot to boot.  This is only relevant to vboot if the platform
@@ -337,51 +337,51 @@
 	 * the same slot that it booted from. */
 	if (CONFIG(RESUME_PATH_SAME_AS_BOOT) &&
 		vboot_platform_is_resuming())
-		ctx.flags |= VB2_CONTEXT_S3_RESUME;
+		ctx->flags |= VB2_CONTEXT_S3_RESUME;
 
 	/* Read secdata from TPM. Initialize TPM if secdata not found. We don't
 	 * check the return value here because vb2api_fw_phase1 will catch
 	 * invalid secdata and tell us what to do (=reboot). */
 	timestamp_add_now(TS_START_TPMINIT);
-	if (vboot_setup_tpm(&ctx) == TPM_SUCCESS)
-		antirollback_read_space_firmware(&ctx);
+	if (vboot_setup_tpm(ctx) == TPM_SUCCESS)
+		antirollback_read_space_firmware(ctx);
 	timestamp_add_now(TS_END_TPMINIT);
 
 	/* Enable measured boot mode */
 	if (CONFIG(VBOOT_MEASURED_BOOT) &&
-		!(ctx.flags & VB2_CONTEXT_S3_RESUME)) {
+		!(ctx->flags & VB2_CONTEXT_S3_RESUME)) {
 		if (vboot_init_crtm() != VB2_SUCCESS)
 			die_with_post_code(POST_INVALID_ROM,
 				"Initializing measured boot mode failed!");
 	}
 
 	if (get_recovery_mode_switch()) {
-		ctx.flags |= VB2_CONTEXT_FORCE_RECOVERY_MODE;
+		ctx->flags |= VB2_CONTEXT_FORCE_RECOVERY_MODE;
 		if (CONFIG(VBOOT_DISABLE_DEV_ON_RECOVERY))
-			ctx.flags |= VB2_CONTEXT_DISABLE_DEVELOPER_MODE;
+			ctx->flags |= VB2_CONTEXT_DISABLE_DEVELOPER_MODE;
 	}
 
 	if (CONFIG(VBOOT_WIPEOUT_SUPPORTED) &&
 		get_wipeout_mode_switch())
-		ctx.flags |= VB2_CONTEXT_FORCE_WIPEOUT_MODE;
+		ctx->flags |= VB2_CONTEXT_FORCE_WIPEOUT_MODE;
 
 	if (CONFIG(VBOOT_LID_SWITCH) && !get_lid_switch())
-		ctx.flags |= VB2_CONTEXT_NOFAIL_BOOT;
+		ctx->flags |= VB2_CONTEXT_NOFAIL_BOOT;
 
 	/* Mainboard/SoC always initializes display. */
 	if (!CONFIG(VBOOT_MUST_REQUEST_DISPLAY))
-		ctx.flags |= VB2_CONTEXT_DISPLAY_INIT;
+		ctx->flags |= VB2_CONTEXT_DISPLAY_INIT;
 
 	/* Do early init (set up secdata and NVRAM, load GBB) */
 	printk(BIOS_INFO, "Phase 1\n");
-	rv = vb2api_fw_phase1(&ctx);
+	rv = vb2api_fw_phase1(ctx);
 
 	/* Jot down some information from vboot which may be required later on
 	   in coreboot boot flow. */
-	if (ctx.flags & VB2_CONTEXT_DISPLAY_INIT)
+	if (ctx->flags & VB2_CONTEXT_DISPLAY_INIT)
 		/* Mainboard/SoC should initialize display. */
 		vboot_get_working_data()->flags |= VBOOT_WD_FLAG_DISPLAY_INIT;
-	if (ctx.flags & VB2_CONTEXT_DEVELOPER_MODE)
+	if (ctx->flags & VB2_CONTEXT_DEVELOPER_MODE)
 		vboot_get_working_data()->flags |= VBOOT_WD_FLAG_DEVELOPER_MODE;
 
 	if (rv) {
@@ -393,58 +393,58 @@
 		 */
 		if (rv == VB2_ERROR_API_PHASE1_RECOVERY) {
 			printk(BIOS_INFO, "Recovery requested (%x)\n", rv);
-			save_if_needed(&ctx);
-			extend_pcrs(&ctx); /* ignore failures */
+			save_if_needed(ctx);
+			extend_pcrs(ctx); /* ignore failures */
 			goto verstage_main_exit;
 		}
 
 		printk(BIOS_INFO, "Reboot requested (%x)\n", rv);
-		save_if_needed(&ctx);
+		save_if_needed(ctx);
 		vboot_reboot();
 	}
 
 	/* Determine which firmware slot to boot (based on NVRAM) */
 	printk(BIOS_INFO, "Phase 2\n");
-	rv = vb2api_fw_phase2(&ctx);
+	rv = vb2api_fw_phase2(ctx);
 	if (rv) {
 		printk(BIOS_INFO, "Reboot requested (%x)\n", rv);
-		save_if_needed(&ctx);
+		save_if_needed(ctx);
 		vboot_reboot();
 	}
 
 	/* Try that slot (verify its keyblock and preamble) */
 	printk(BIOS_INFO, "Phase 3\n");
 	timestamp_add_now(TS_START_VERIFY_SLOT);
-	rv = vb2api_fw_phase3(&ctx);
+	rv = vb2api_fw_phase3(ctx);
 	timestamp_add_now(TS_END_VERIFY_SLOT);
 	if (rv) {
 		printk(BIOS_INFO, "Reboot requested (%x)\n", rv);
-		save_if_needed(&ctx);
+		save_if_needed(ctx);
 		vboot_reboot();
 	}
 
 	printk(BIOS_INFO, "Phase 4\n");
-	rv = locate_firmware(&ctx, &fw_main);
+	rv = locate_firmware(ctx, &fw_main);
 	if (rv)
 		die_with_post_code(POST_INVALID_ROM,
 			"Failed to read FMAP to locate firmware");
 
-	rv = hash_body(&ctx, &fw_main);
-	save_if_needed(&ctx);
+	rv = hash_body(ctx, &fw_main);
+	save_if_needed(ctx);
 	if (rv) {
 		printk(BIOS_INFO, "Reboot requested (%x)\n", rv);
 		vboot_reboot();
 	}
 
 	/* Only extend PCRs once on boot. */
-	if (!(ctx.flags & VB2_CONTEXT_S3_RESUME)) {
+	if (!(ctx->flags & VB2_CONTEXT_S3_RESUME)) {
 		timestamp_add_now(TS_START_TPMPCR);
-		rv = extend_pcrs(&ctx);
+		rv = extend_pcrs(ctx);
 		if (rv) {
 			printk(BIOS_WARNING,
 			       "Failed to extend TPM PCRs (%#x)\n", rv);
-			vb2api_fail(&ctx, VB2_RECOVERY_RO_TPM_U_ERROR, rv);
-			save_if_needed(&ctx);
+			vb2api_fail(ctx, VB2_RECOVERY_RO_TPM_U_ERROR, rv);
+			save_if_needed(ctx);
 			vboot_reboot();
 		}
 		timestamp_add_now(TS_END_TPMPCR);
@@ -456,8 +456,8 @@
 	rv = antirollback_lock_space_firmware();
 	if (rv) {
 		printk(BIOS_INFO, "Failed to lock TPM (%x)\n", rv);
-		vb2api_fail(&ctx, VB2_RECOVERY_RO_TPM_L_ERROR, 0);
-		save_if_needed(&ctx);
+		vb2api_fail(ctx, VB2_RECOVERY_RO_TPM_L_ERROR, 0);
+		save_if_needed(ctx);
 		vboot_reboot();
 	}
 	timestamp_add_now(TS_END_TPMLOCK);
@@ -468,14 +468,14 @@
 		if (rv) {
 			printk(BIOS_INFO, "Failed to lock rec hash space(%x)\n",
 			       rv);
-			vb2api_fail(&ctx, VB2_RECOVERY_RO_TPM_REC_HASH_L_ERROR,
+			vb2api_fail(ctx, VB2_RECOVERY_RO_TPM_REC_HASH_L_ERROR,
 				    0);
-			save_if_needed(&ctx);
+			save_if_needed(ctx);
 			vboot_reboot();
 		}
 	}
 
-	printk(BIOS_INFO, "Slot %c is selected\n", is_slot_a(&ctx) ? 'A' : 'B');
+	printk(BIOS_INFO, "Slot %c is selected\n", is_slot_a(ctx) ? 'A' : 'B');
 	vboot_set_selected_region(region_device_region(&fw_main));
 
  verstage_main_exit:
@@ -487,6 +487,5 @@
 	/* Save recovery reason in case of unexpected reboots on x86. */
 	vboot_save_recovery_reason_vbnv();
 
-	vboot_finalize_work_context(&ctx);
 	timestamp_add_now(TS_END_VBOOT);
 }