soc/amd/picasso: Reboot for recovery if no psp workbuf is found

Instead of halting if the vboot workbuf is not passed to coreboot by the
PSP, reset and reboot into recovery mode.

This process is made more difficult because if the workbuf isn't
available, we can't reboot directly into recovery - the workbuf is
needed for that process to be done through the regular calls, and we
don't want to go around the vboot API and just write into VBNV directly.
To overcome this, we set a CMOS flag, and reset the system.
PSP_verstage checks for this flag so it will update VBNV and reset the
system after generating the workbuf.

BUG=b:152638343
TEST=Simulate the workbuf not being present and verify the reboot
process.

Signed-off-by: Martin Roth <martinroth@chromium.org>
Change-Id: I049db956a5209904b274747be28ff226ce542316
Reviewed-on: https://review.coreboot.org/c/coreboot/+/44538
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Aaron Durbin <adurbin@chromium.org>
diff --git a/src/soc/amd/picasso/Kconfig b/src/soc/amd/picasso/Kconfig
index 1b83000..642935f 100644
--- a/src/soc/amd/picasso/Kconfig
+++ b/src/soc/amd/picasso/Kconfig
@@ -505,6 +505,18 @@
 	  Runs verstage on the PSP.  Only available on
 	  certain Chrome OS branded parts from AMD.
 
+config CMOS_RECOVERY_BYTE
+	hex
+	default 0x51
+	depends on VBOOT_STARTS_BEFORE_BOOTBLOCK
+	help
+	  If the workbuf is not passed from the PSP to coreboot, set the
+	  recovery flag and reboot.  The PSP will read this byte, mark the
+	  recovery request in VBNV, and reset the system into recovery mode.
+
+	  This is the byte before the default first byte used by VBNV
+	  (0x26 + 0x0E - 1)
+
 if VBOOT_SLOTS_RW_AB && VBOOT_STARTS_BEFORE_BOOTBLOCK
 
 config RWA_REGION_ONLY
diff --git a/src/soc/amd/picasso/Makefile.inc b/src/soc/amd/picasso/Makefile.inc
index f11d895..c12b1af 100644
--- a/src/soc/amd/picasso/Makefile.inc
+++ b/src/soc/amd/picasso/Makefile.inc
@@ -22,6 +22,7 @@
 bootblock-y += gpio.c
 bootblock-y += smi_util.c
 bootblock-y += config.c
+bootblock-y += reset.c
 
 romstage-y += i2c.c
 romstage-y += romstage.c
diff --git a/src/soc/amd/picasso/bootblock/bootblock.c b/src/soc/amd/picasso/bootblock/bootblock.c
index 556fbad..4700027 100644
--- a/src/soc/amd/picasso/bootblock/bootblock.c
+++ b/src/soc/amd/picasso/bootblock/bootblock.c
@@ -2,6 +2,7 @@
 
 #include <stdint.h>
 #include <symbols.h>
+#include <amdblocks/reset.h>
 #include <bootblock_common.h>
 #include <console/console.h>
 #include <cpu/x86/cache.h>
@@ -9,6 +10,8 @@
 #include <cpu/amd/msr.h>
 #include <cpu/x86/mtrr.h>
 #include <cpu/amd/mtrr.h>
+#include <pc80/mc146818rtc.h>
+#include <soc/psp_transfer.h>
 #include <soc/southbridge.h>
 #include <soc/i2c.h>
 #include <amdblocks/amd_pci_mmconf.h>
@@ -135,7 +138,11 @@
 
 		printk(BIOS_DEBUG, "Signature: %#08x\n", *(uint32_t *)_vboot2_work);
 
-		die("Halting.\n");
+		cmos_init(0);
+		cmos_write(CMOS_RECOVERY_MAGIC_VAL, CMOS_RECOVERY_BYTE);
+		warm_reset();
+	} else {
+		cmos_write(0x00, CMOS_RECOVERY_BYTE);
 	}
 #endif
 
diff --git a/src/soc/amd/picasso/include/soc/psp_transfer.h b/src/soc/amd/picasso/include/soc/psp_transfer.h
index 6a43b55..0996f64 100644
--- a/src/soc/amd/picasso/include/soc/psp_transfer.h
+++ b/src/soc/amd/picasso/include/soc/psp_transfer.h
@@ -3,6 +3,14 @@
 #ifndef PSP_VERSTAGE_PSP_TRANSFER_H
 #define PSP_VERSTAGE_PSP_TRANSFER_H
 
+# if (CONFIG_CMOS_RECOVERY_BYTE != 0)
+#  define CMOS_RECOVERY_BYTE CONFIG_CMOS_RECOVERY_BYTE
+# elif CONFIG(VBOOT_STARTS_BEFORE_BOOTBLOCK)
+#  error "Must set CONFIG_CMOS_RECOVERY_BYTE"
+# endif
+
+#define CMOS_RECOVERY_MAGIC_VAL	0x96
+
 #define TRANSFER_INFO_SIZE		64
 #define TIMESTAMP_BUFFER_SIZE		0x200
 
diff --git a/src/soc/amd/picasso/psp_verstage/psp_verstage.c b/src/soc/amd/picasso/psp_verstage/psp_verstage.c
index 5568797..005c8b0 100644
--- a/src/soc/amd/picasso/psp_verstage/psp_verstage.c
+++ b/src/soc/amd/picasso/psp_verstage/psp_verstage.c
@@ -5,10 +5,11 @@
 #include <bl_uapp/bl_syscall_public.h>
 #include <boot_device.h>
 #include <cbfs.h>
-#include <commonlib/region.h>
 #include <console/console.h>
 #include <fmap.h>
+#include <pc80/mc146818rtc.h>
 #include <soc/psp_transfer.h>
+#include <security/vboot/vbnv.h>
 #include <security/vboot/misc.h>
 #include <security/vboot/symbols.h>
 #include <security/vboot/vboot_common.h>
@@ -35,6 +36,22 @@
 	vboot_reboot();
 }
 
+static uint32_t check_cmos_recovery(void)
+{
+	/* Only reset if cmos is valid */
+	if (vbnv_cmos_failed())
+		return 0;
+
+	/* If the byte is set, clear it, then return error to reboot */
+	if (cmos_read(CMOS_RECOVERY_BYTE) == CMOS_RECOVERY_MAGIC_VAL) {
+		cmos_write(0x00, CMOS_RECOVERY_BYTE);
+		printk(BIOS_DEBUG, "Reboot into recovery requested by coreboot\n");
+		return POSTCODE_CMOS_RECOVERY;
+	}
+
+	return 0;
+}
+
 static uintptr_t locate_amdfw(const char *name, struct region_device *rdev)
 {
 	struct cbfsf fh;
@@ -216,6 +233,9 @@
 
 	vb2api_relocate(_vboot2_work, _vboot2_work, VB2_FIRMWARE_WORKBUF_RECOMMENDED_SIZE,
 			&ctx);
+	retval = check_cmos_recovery();
+	if (retval)
+		goto err;
 
 	post_code(POSTCODE_SAVE_BUFFERS);
 	retval = save_buffers(&ctx);
diff --git a/src/soc/amd/picasso/psp_verstage/psp_verstage.h b/src/soc/amd/picasso/psp_verstage/psp_verstage.h
index 3c7574d..ef5c452 100644
--- a/src/soc/amd/picasso/psp_verstage/psp_verstage.h
+++ b/src/soc/amd/picasso/psp_verstage/psp_verstage.h
@@ -31,6 +31,7 @@
 #define POSTCODE_UPDATE_PSP_BIOS_DIR_ERROR	0xC7
 #define POSTCODE_FMAP_REGION_MISSING		0xC8
 #define POSTCODE_AMD_FW_MISSING			0xC9
+#define POSTCODE_CMOS_RECOVERY			0xCA
 
 #define POSTCODE_UNMAP_SPI_ROM			0xF0
 #define POSTCODE_UNMAP_FCH_DEVICES		0xF1