google/chromeos: Add support for saving recovery reason across reboot

On some x86 platforms (skylake, apollolake), we observe reboots at
different steps during the FSP initialization. These additional reboots
result in loss of recovery request because vboot_reference library
clears recovery request on vbnv once verification is complete and it has
made a decision about which boot path to take(normal/dev, slot-a/slot-b,
recovery).

Provide a way to allow mainboards/chipsets to inform recovery module in
vboot2 to save recovery reason to survive unexpected reboots. The
recovery reason is set in vbnv after vboot_reference library completes
its verification and clears the reason in vbnv while jumping to
payload.

BUG=chrome-os-partner:55431

Change-Id: Ie96be9aeb42c8209d8215943409e6327d6a8bf98
Signed-off-by: Furquan Shaikh <furquan@google.com>
Reviewed-on: https://review.coreboot.org/15802
Tested-by: build bot (Jenkins)
Reviewed-by: Aaron Durbin <adurbin@chromium.org>
diff --git a/src/vendorcode/google/chromeos/vboot2/Kconfig b/src/vendorcode/google/chromeos/vboot2/Kconfig
index 7580d8d..5aaf932 100644
--- a/src/vendorcode/google/chromeos/vboot2/Kconfig
+++ b/src/vendorcode/google/chromeos/vboot2/Kconfig
@@ -78,3 +78,12 @@
 	  ram to allocate the vboot work buffer. That means vboot verification
 	  is after memory init and requires main memory to back the work
 	  buffer.
+
+config VBOOT_SAVE_RECOVERY_REASON_ON_REBOOT
+	bool
+	default n
+	depends on VBOOT_VERIFY_FIRMWARE
+	help
+	  This option ensures that the recovery request is not lost because of
+	  reboots caused after vboot verification is run. e.g. reboots caused by
+	  FSP components on Intel platforms.
diff --git a/src/vendorcode/google/chromeos/vboot2/misc.h b/src/vendorcode/google/chromeos/vboot2/misc.h
index ca64b37..9b771a2 100644
--- a/src/vendorcode/google/chromeos/vboot2/misc.h
+++ b/src/vendorcode/google/chromeos/vboot2/misc.h
@@ -35,4 +35,6 @@
 /* Store the selected region in cbmem for later use. */
 void vb2_store_selected_region(void);
 
+void vb2_save_recovery_reason_vbnv(void);
+
 #endif /* __CHROMEOS_VBOOT2_MISC_H__ */
diff --git a/src/vendorcode/google/chromeos/vboot2/recovery.c b/src/vendorcode/google/chromeos/vboot2/recovery.c
index 2cd5e80..94a8cc3 100644
--- a/src/vendorcode/google/chromeos/vboot2/recovery.c
+++ b/src/vendorcode/google/chromeos/vboot2/recovery.c
@@ -14,6 +14,7 @@
  */
 
 #include <assert.h>
+#include <bootstate.h>
 #include <rules.h>
 #include <string.h>
 #include <vb2_api.h>
@@ -32,6 +33,39 @@
 	return sd->recovery_reason;
 }
 
+void vb2_save_recovery_reason_vbnv(void)
+{
+	if (!IS_ENABLED(CONFIG_VBOOT_SAVE_RECOVERY_REASON_ON_REBOOT))
+		return;
+
+	int reason =  vb2_get_recovery_reason_shared_data();
+	if (!reason)
+		return;
+
+	set_recovery_mode_into_vbnv(reason);
+}
+
+static void vb2_clear_recovery_reason_vbnv(void *unused)
+{
+	if (!IS_ENABLED(CONFIG_VBOOT_SAVE_RECOVERY_REASON_ON_REBOOT))
+		return;
+
+	set_recovery_mode_into_vbnv(0);
+}
+
+/*
+ * Recovery reason stored in VBNV needs to be cleared before the state of VBNV
+ * is backed-up anywhere or jumping to the payload (whichever occurs
+ * first). Currently, vbnv_cmos.c backs up VBNV on POST_DEVICE. Thus, we need to
+ * make sure that the stored recovery reason is cleared off before that
+ * happens.
+ * IMPORTANT: Any reboot occurring after BS_DEV_INIT state will cause loss of
+ * recovery reason on reboot. Until now, we have seen reboots occuring on x86
+ * only in FSP stages which run before BS_DEV_INIT.
+ */
+BOOT_STATE_INIT_ENTRY(BS_DEV_INIT, BS_ON_EXIT,
+		      vb2_clear_recovery_reason_vbnv, NULL);
+
 /*
  * Returns 0 for the stages where we know that cbmem does not come online.
  * Even if this function returns 1 for romstage, depending upon the point in
diff --git a/src/vendorcode/google/chromeos/vboot2/vboot_loader.c b/src/vendorcode/google/chromeos/vboot2/vboot_loader.c
index 7541518..b76d20b 100644
--- a/src/vendorcode/google/chromeos/vboot2/vboot_loader.c
+++ b/src/vendorcode/google/chromeos/vboot2/vboot_loader.c
@@ -91,6 +91,7 @@
 	if (verification_should_run()) {
 		verstage_main();
 		car_set_var(vboot_executed, 1);
+		vb2_save_recovery_reason_vbnv();
 	} else if (verstage_should_load()) {
 		struct cbfsf file;
 		struct prog verstage =