soc/amd/picasso: add psp_verstage

This is the main code for building coreboot's verstage as a userspace
application to run on the PSP.  It does a minimal setup of hardware,
then runs verstage_main.  It uses hardware hashing to increase the speed
and will directly reboot into recovery mode if there are any failures.

BUG=b:158124527
TEST=Build & boot trembyle

Signed-off-by: Martin Roth <martin@coreboot.org>
Change-Id: Ia58839caa5bfbae0408702ee8d02ef482f2861c4
Reviewed-on: https://review.coreboot.org/c/coreboot/+/41816
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Raul Rangel <rrangel@chromium.org>
diff --git a/src/soc/amd/picasso/Kconfig b/src/soc/amd/picasso/Kconfig
index fd2a205..5872a6f 100644
--- a/src/soc/amd/picasso/Kconfig
+++ b/src/soc/amd/picasso/Kconfig
@@ -10,7 +10,7 @@
 config CPU_SPECIFIC_OPTIONS
 	def_bool y
 	select ARCH_BOOTBLOCK_X86_32
-	select ARCH_VERSTAGE_X86_32
+	select ARCH_VERSTAGE_X86_32 if !VBOOT_STARTS_BEFORE_BOOTBLOCK
 	select ARCH_ROMSTAGE_X86_32
 	select ARCH_RAMSTAGE_X86_32
 	select RESET_VECTOR_IN_RAM
@@ -473,6 +473,16 @@
 	  Note that this option may conflict with the whitelist file if a
 	  different PSP bootloader binary is specified.
 
+config PSP_SHAREDMEM_SIZE
+	hex "Maximum size of shared memory area"
+	default 0x3000 if VBOOT
+	default 0x0
+	help
+	  Sets the maximum size for the PSP to pass the vboot workbuf and
+	  any logs or timestamps back to coreboot.  This will be copied
+	  into main memory by the PSP and will be available when the x86 is
+	  started.
+
 config PSP_UNLOCK_SECURE_DEBUG
 	bool "Unlock secure debug"
 	default n
@@ -481,4 +491,17 @@
 
 endmenu
 
+
+config VBOOT
+	select VBOOT_VBNV_CMOS
+	select VBOOT_VBNV_CMOS_BACKUP_TO_FLASH if ! VBOOT_STARTS_BEFORE_BOOTBLOCK
+
+config VBOOT_STARTS_BEFORE_BOOTBLOCK
+	def_bool n
+	depends on VBOOT
+	select ARCH_VERSTAGE_ARMV7
+	help
+	  Runs verstage on the PSP.  Only available on
+	  certain Chrome OS branded parts from AMD.
+
 endif # SOC_AMD_PICASSO
diff --git a/src/soc/amd/picasso/Makefile.inc b/src/soc/amd/picasso/Makefile.inc
index 39269e9..9ea770c 100644
--- a/src/soc/amd/picasso/Makefile.inc
+++ b/src/soc/amd/picasso/Makefile.inc
@@ -9,6 +9,7 @@
 subdirs-y += ../../../cpu/x86/mtrr
 subdirs-y += ../../../cpu/x86/pae
 subdirs-y += ../../../cpu/x86/smm
+subdirs-$(CONFIG_VBOOT_STARTS_BEFORE_BOOTBLOCK) += psp_verstage
 
 bootblock-y += bootblock/pre_c.S
 bootblock-y += bootblock/bootblock.c
@@ -21,11 +22,13 @@
 bootblock-y += gpio.c
 bootblock-y += smi_util.c
 bootblock-y += config.c
+bootblock-y += ../../../arch/x86/memmove.c
 
 romstage-y += i2c.c
 romstage-y += romstage.c
 romstage-y += gpio.c
 romstage-y += pmutil.c
+romstage-y += reset.c
 romstage-y += memmap.c
 romstage-y += uart.c
 romstage-$(CONFIG_PICASSO_CONSOLE_UART) += uart_console.c
@@ -37,14 +40,15 @@
 romstage-y += config.c
 romstage-y += mrc_cache.c
 
-verstage-y += gpio.c
 verstage-y += i2c.c
 verstage-y += pmutil.c
 verstage-y += config.c
 verstage-y += aoac.c
-verstage-y += uart.c
-verstage-$(CONFIG_PICASSO_CONSOLE_UART) += uart_console.c
-verstage-y += tsc_freq.c
+verstage_x86-y += gpio.c
+verstage_x86-y += uart.c
+verstage_x86-$(CONFIG_PICASSO_CONSOLE_UART) += uart_console.c
+verstage_x86-y += tsc_freq.c
+verstage_x86-y += reset.c
 
 ramstage-y += i2c.c
 ramstage-y += chip.c
@@ -58,6 +62,7 @@
 ramstage-y += aoac.c
 ramstage-y += southbridge.c
 ramstage-y += pmutil.c
+ramstage-y += reset.c
 ramstage-y += acp.c
 ramstage-y += sata.c
 ramstage-y += memmap.c
@@ -75,8 +80,6 @@
 ramstage-y += update_microcode.c
 ramstage-y += graphics.c
 
-all-y += reset.c
-
 smm-y += smihandler.c
 smm-y += smi_util.c
 smm-y += tsc_freq.c
diff --git a/src/soc/amd/picasso/chip.h b/src/soc/amd/picasso/chip.h
index 1dbe3d9..205733a 100644
--- a/src/soc/amd/picasso/chip.h
+++ b/src/soc/amd/picasso/chip.h
@@ -12,7 +12,7 @@
 #include <soc/iomap.h>
 #include <soc/southbridge.h>
 #include <acpi/acpi_device.h>
-#include <arch/smp/mpspec.h>
+#include <arch/x86/include/arch/smp/mpspec.h> /* point from top level */
 
 /*
   USB 2.0 PHY Parameters
diff --git a/src/soc/amd/picasso/memlayout.ld b/src/soc/amd/picasso/memlayout.ld
index 6f43ba1..6f5d4cf 100644
--- a/src/soc/amd/picasso/memlayout.ld
+++ b/src/soc/amd/picasso/memlayout.ld
@@ -1,101 +1,7 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 
-#include <memlayout.h>
-#include <arch/header.ld>
-
-#define EARLY_RESERVED_DRAM_START(addr)		SYMBOL(early_reserved_dram, addr)
-#define EARLY_RESERVED_DRAM_END(addr)		SYMBOL(eearly_reserved_dram, addr)
-
-#define PSP_SHAREDMEM_DRAM_START(addr)		SYMBOL(psp_sharedmem_dram, addr)
-#define PSP_SHAREDMEM_DRAM_END(addr)		SYMBOL(epsp_sharedmem_dram, addr)
-
-/*
- *
- *                     +--------------------------------+
- *                     |                                |
- *                     |                                |
- *                     |                                |
- *                     |                                |
- *                     |                                |
- *                     |                                |
- *                     |                                |
- *   reserved_dram_end +--------------------------------+
- *                     |                                |
- *                     |       verstage (if reqd)       |
- *                     |          (VERSTAGE_SIZE)       |
- *                     +--------------------------------+ VERSTAGE_ADDR
- *                     |                                |
- *                     |            FSP-M               |
- *                     |         (FSP_M_SIZE)           |
- *                     +--------------------------------+ FSP_M_ADDR
- *                     |                                |X86_RESET_VECTOR = ROMSTAGE_ADDR + ROMSTAGE_SIZE - 0x10
- *                     |           romstage             |
- *                     |        (ROMSTAGE_SIZE)         |
- *                     +--------------------------------+ ROMSTAGE_ADDR
- *                     |           bootblock            |
- *                     |     (C_ENV_BOOTBLOCK_SIZE)     |
- *                     +--------------------------------+ BOOTBLOCK_ADDR
- *                     |          Unused hole           |
- *                     +--------------------------------+
- *                     |     FMAP cache (FMAP_SIZE)     |
- *                     +--------------------------------+ PSP_SHAREDMEM_BASE + PSP_SHAREDMEM_SIZE + PRERAM_CBMEM_CONSOLE_SIZE + 0x200
- *                     |  Early Timestamp region (512B) |
- *                     +--------------------------------+ PSP_SHAREDMEM_BASE + PSP_SHAREDMEM_SIZE + PRERAM_CBMEM_CONSOLE_SIZE
- *                     |      Preram CBMEM console      |
- *                     |   (PRERAM_CBMEM_CONSOLE_SIZE)  |
- *                     +--------------------------------+ PSP_SHAREDMEM_BASE + PSP_SHAREDMEM_SIZE
- *                     |   PSP shared (vboot workbuf)   |
- *                     |      (PSP_SHAREDMEM_SIZE)      |
- *                     +--------------------------------+ PSP_SHAREDMEM_BASE
- *                     |          APOB (64KiB)          |
- *                     +--------------------------------+ PSP_APOB_DRAM_ADDRESS
- *                     |        Early BSP stack         |
- *                     |   (EARLYRAM_BSP_STACK_SIZE)    |
- * reserved_dram_start +--------------------------------+ EARLY_RESERVED_DRAM_BASE
- *                     |              DRAM              |
- *                     +--------------------------------+ 0x100000
- *                     |           Option ROM           |
- *                     +--------------------------------+ 0xc0000
- *                     |           Legacy VGA           |
- *                     +--------------------------------+ 0xa0000
- *                     |              DRAM              |
- *                     +--------------------------------+ 0x0
- */
-SECTIONS
-{
-	DRAM_START(0x0)
-
-	EARLY_RESERVED_DRAM_START(CONFIG_EARLY_RESERVED_DRAM_BASE)
-
-	EARLYRAM_STACK(., CONFIG_EARLYRAM_BSP_STACK_SIZE)
-	REGION(apob, CONFIG_PSP_APOB_DRAM_ADDRESS, 64K, 1)
-
-#if CONFIG(VBOOT)
-	PSP_SHAREDMEM_DRAM_START(CONFIG_PSP_SHAREDMEM_BASE)
-	VBOOT2_WORK(., VB2_FIRMWARE_WORKBUF_RECOMMENDED_SIZE)
-	PSP_SHAREDMEM_DRAM_END(CONFIG_PSP_SHAREDMEM_BASE + CONFIG_PSP_SHAREDMEM_SIZE)
-#endif
-
-	PRERAM_CBMEM_CONSOLE(., CONFIG_PRERAM_CBMEM_CONSOLE_SIZE)
-	TIMESTAMP(., 0x200)
-	FMAP_CACHE(., FMAP_SIZE)
-
-	_ = ASSERT((CONFIG_BOOTBLOCK_ADDR + CONFIG_C_ENV_BOOTBLOCK_SIZE - 0x10) == CONFIG_X86_RESET_VECTOR, "Reset vector should be -0x10 from end of bootblock");
-	_ = ASSERT(CONFIG_BOOTBLOCK_ADDR == ((CONFIG_BOOTBLOCK_ADDR + 0xFFFF) & 0xFFFF0000), "Bootblock must be 16 bit aligned");
-	BOOTBLOCK(CONFIG_BOOTBLOCK_ADDR, CONFIG_C_ENV_BOOTBLOCK_SIZE)
-	ROMSTAGE(CONFIG_ROMSTAGE_ADDR, CONFIG_ROMSTAGE_SIZE)
-	REGION(fspm, CONFIG_FSP_M_ADDR, CONFIG_FSP_M_SIZE, 1)
-#if CONFIG(VBOOT_SEPARATE_VERSTAGE)
-	VERSTAGE(CONFIG_VERSTAGE_ADDR, CONFIG_VERSTAGE_SIZE)
-#endif
-
-	EARLY_RESERVED_DRAM_END(.)
-
-	RAMSTAGE(CONFIG_RAMBASE, 8M)
-}
-
-#if ENV_BOOTBLOCK
-/* Bootblock specific scripts which provide more SECTION directives. */
-#include <cpu/x86/16bit/entry16.ld>
-#include <cpu/x86/16bit/reset16.ld>
-#endif  /* ENV_BOOTBLOCK */
+#if ENV_SEPARATE_VERSTAGE && CONFIG(VBOOT_STARTS_BEFORE_BOOTBLOCK)
+	#include "memlayout_psp_verstage.ld"
+#else
+	#include "memlayout_x86.ld"
+#endif /* ENV_SEPARATE_VERSTAGE && CONFIG(VBOOT_STARTS_BEFORE_BOOTBLOCK) */
diff --git a/src/soc/amd/picasso/memlayout_psp_verstage.ld b/src/soc/amd/picasso/memlayout_psp_verstage.ld
new file mode 100644
index 0000000..d2b409c
--- /dev/null
+++ b/src/soc/amd/picasso/memlayout_psp_verstage.ld
@@ -0,0 +1,65 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <memlayout.h>
+
+/*
+ * Start of available space is 0x15000 and this is where the
+ * header for the user app (verstage) must be mapped.
+ * Size is 0x28000 bytes
+ */
+#define PSP_SRAM_START			0x15000
+#define PSP_SRAM_SIZE			160K
+
+#define VERSTAGE_START			0x15000
+#define VERSTAGE_SIZE			60K
+#define VBOOT_WORK_START		VERSTAGE_START + VERSTAGE_SIZE
+#define VBOOT_WORK_SIZE			12K
+#define FMAP_CACHE_SIZE			2K
+
+/*
+ * The temp stack can be made much smaller if needed - even 256 bytes
+ * should be sufficient.  This is just for the function mapping the
+ * actual stack.
+ */
+#define PSP_VERSTAGE_TEMP_STACK_START	0x39000
+#define PSP_VERSTAGE_TEMP_STACK_SIZE	4K
+
+/*
+ * The top of the stack must be 4k aligned, so set the bottom as 4k aligned
+ * and make the size a multiple of 4k
+ */
+#define PSP_VERSTAGE_STACK_START	0x3B000
+#define PSP_VERSTAGE_STACK_SIZE		8K
+
+ENTRY(_psp_vs_start)
+SECTIONS
+{
+	SRAM_START(PSP_SRAM_START)
+	_verstage = .;
+
+		.text : { *(PSP_HEADER_DATA) }
+		.text : { *(.text._psp_vs_start) }
+		.text : { *(.text.Main) }
+		.text : { *(.text*) }
+		.rodata : { *(.rodata*) }
+
+		.data : { *(.data*) }
+		.data : { *(PSP_FOOTER_DATA) }
+
+		_bss_start = .;
+		.bss : { *(.bss*) }
+		_bss_end = .;
+
+	_everstage = _verstage + VERSTAGE_SIZE;
+
+	REGION(vboot2_work, VBOOT_WORK_START, VBOOT_WORK_SIZE, 64)
+
+	FMAP_CACHE(., FMAP_SIZE)
+
+	PSP_VERSTAGE_TEMP_STACK_END = (PSP_VERSTAGE_TEMP_STACK_START + PSP_VERSTAGE_TEMP_STACK_SIZE );
+
+	REGION(stack, PSP_VERSTAGE_STACK_START, PSP_VERSTAGE_STACK_SIZE, 64)
+	PSP_VERSTAGE_STACK_BASE = _stack;
+	PSP_VERSTAGE_STACK_END = _estack;
+	SRAM_END(PSP_SRAM_START + PSP_SRAM_SIZE)
+}
diff --git a/src/soc/amd/picasso/memlayout_x86.ld b/src/soc/amd/picasso/memlayout_x86.ld
new file mode 100644
index 0000000..6f43ba1
--- /dev/null
+++ b/src/soc/amd/picasso/memlayout_x86.ld
@@ -0,0 +1,101 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <memlayout.h>
+#include <arch/header.ld>
+
+#define EARLY_RESERVED_DRAM_START(addr)		SYMBOL(early_reserved_dram, addr)
+#define EARLY_RESERVED_DRAM_END(addr)		SYMBOL(eearly_reserved_dram, addr)
+
+#define PSP_SHAREDMEM_DRAM_START(addr)		SYMBOL(psp_sharedmem_dram, addr)
+#define PSP_SHAREDMEM_DRAM_END(addr)		SYMBOL(epsp_sharedmem_dram, addr)
+
+/*
+ *
+ *                     +--------------------------------+
+ *                     |                                |
+ *                     |                                |
+ *                     |                                |
+ *                     |                                |
+ *                     |                                |
+ *                     |                                |
+ *                     |                                |
+ *   reserved_dram_end +--------------------------------+
+ *                     |                                |
+ *                     |       verstage (if reqd)       |
+ *                     |          (VERSTAGE_SIZE)       |
+ *                     +--------------------------------+ VERSTAGE_ADDR
+ *                     |                                |
+ *                     |            FSP-M               |
+ *                     |         (FSP_M_SIZE)           |
+ *                     +--------------------------------+ FSP_M_ADDR
+ *                     |                                |X86_RESET_VECTOR = ROMSTAGE_ADDR + ROMSTAGE_SIZE - 0x10
+ *                     |           romstage             |
+ *                     |        (ROMSTAGE_SIZE)         |
+ *                     +--------------------------------+ ROMSTAGE_ADDR
+ *                     |           bootblock            |
+ *                     |     (C_ENV_BOOTBLOCK_SIZE)     |
+ *                     +--------------------------------+ BOOTBLOCK_ADDR
+ *                     |          Unused hole           |
+ *                     +--------------------------------+
+ *                     |     FMAP cache (FMAP_SIZE)     |
+ *                     +--------------------------------+ PSP_SHAREDMEM_BASE + PSP_SHAREDMEM_SIZE + PRERAM_CBMEM_CONSOLE_SIZE + 0x200
+ *                     |  Early Timestamp region (512B) |
+ *                     +--------------------------------+ PSP_SHAREDMEM_BASE + PSP_SHAREDMEM_SIZE + PRERAM_CBMEM_CONSOLE_SIZE
+ *                     |      Preram CBMEM console      |
+ *                     |   (PRERAM_CBMEM_CONSOLE_SIZE)  |
+ *                     +--------------------------------+ PSP_SHAREDMEM_BASE + PSP_SHAREDMEM_SIZE
+ *                     |   PSP shared (vboot workbuf)   |
+ *                     |      (PSP_SHAREDMEM_SIZE)      |
+ *                     +--------------------------------+ PSP_SHAREDMEM_BASE
+ *                     |          APOB (64KiB)          |
+ *                     +--------------------------------+ PSP_APOB_DRAM_ADDRESS
+ *                     |        Early BSP stack         |
+ *                     |   (EARLYRAM_BSP_STACK_SIZE)    |
+ * reserved_dram_start +--------------------------------+ EARLY_RESERVED_DRAM_BASE
+ *                     |              DRAM              |
+ *                     +--------------------------------+ 0x100000
+ *                     |           Option ROM           |
+ *                     +--------------------------------+ 0xc0000
+ *                     |           Legacy VGA           |
+ *                     +--------------------------------+ 0xa0000
+ *                     |              DRAM              |
+ *                     +--------------------------------+ 0x0
+ */
+SECTIONS
+{
+	DRAM_START(0x0)
+
+	EARLY_RESERVED_DRAM_START(CONFIG_EARLY_RESERVED_DRAM_BASE)
+
+	EARLYRAM_STACK(., CONFIG_EARLYRAM_BSP_STACK_SIZE)
+	REGION(apob, CONFIG_PSP_APOB_DRAM_ADDRESS, 64K, 1)
+
+#if CONFIG(VBOOT)
+	PSP_SHAREDMEM_DRAM_START(CONFIG_PSP_SHAREDMEM_BASE)
+	VBOOT2_WORK(., VB2_FIRMWARE_WORKBUF_RECOMMENDED_SIZE)
+	PSP_SHAREDMEM_DRAM_END(CONFIG_PSP_SHAREDMEM_BASE + CONFIG_PSP_SHAREDMEM_SIZE)
+#endif
+
+	PRERAM_CBMEM_CONSOLE(., CONFIG_PRERAM_CBMEM_CONSOLE_SIZE)
+	TIMESTAMP(., 0x200)
+	FMAP_CACHE(., FMAP_SIZE)
+
+	_ = ASSERT((CONFIG_BOOTBLOCK_ADDR + CONFIG_C_ENV_BOOTBLOCK_SIZE - 0x10) == CONFIG_X86_RESET_VECTOR, "Reset vector should be -0x10 from end of bootblock");
+	_ = ASSERT(CONFIG_BOOTBLOCK_ADDR == ((CONFIG_BOOTBLOCK_ADDR + 0xFFFF) & 0xFFFF0000), "Bootblock must be 16 bit aligned");
+	BOOTBLOCK(CONFIG_BOOTBLOCK_ADDR, CONFIG_C_ENV_BOOTBLOCK_SIZE)
+	ROMSTAGE(CONFIG_ROMSTAGE_ADDR, CONFIG_ROMSTAGE_SIZE)
+	REGION(fspm, CONFIG_FSP_M_ADDR, CONFIG_FSP_M_SIZE, 1)
+#if CONFIG(VBOOT_SEPARATE_VERSTAGE)
+	VERSTAGE(CONFIG_VERSTAGE_ADDR, CONFIG_VERSTAGE_SIZE)
+#endif
+
+	EARLY_RESERVED_DRAM_END(.)
+
+	RAMSTAGE(CONFIG_RAMBASE, 8M)
+}
+
+#if ENV_BOOTBLOCK
+/* Bootblock specific scripts which provide more SECTION directives. */
+#include <cpu/x86/16bit/entry16.ld>
+#include <cpu/x86/16bit/reset16.ld>
+#endif  /* ENV_BOOTBLOCK */
diff --git a/src/soc/amd/picasso/psp_verstage/Makefile.inc b/src/soc/amd/picasso/psp_verstage/Makefile.inc
new file mode 100644
index 0000000..320b704
--- /dev/null
+++ b/src/soc/amd/picasso/psp_verstage/Makefile.inc
@@ -0,0 +1,24 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+verstage-generic-ccopts += -I$(src)/soc/amd/picasso/psp_verstage/include
+verstage-generic-ccopts += -I$(src)/vendorcode/amd/fsp/picasso/include
+CPPFLAGS_common += -I$(VBOOT_SOURCE)/firmware/2lib/include/
+
+verstage-y += delay.c
+verstage-y += fch.c
+verstage-y += pmutil.c
+verstage-y += post.c
+verstage-y += printk.c
+verstage-y += psp_verstage.c
+verstage-y += psp.c
+verstage-y += reset.c
+verstage-y += svc.c
+verstage-y += timer.c
+verstage-y += timestamp.c
+verstage-y += vboot_crypto.c
+
+verstage-y += $(top)/src/vendorcode/amd/fsp/picasso/bl_uapp/bl_uapp_startup.S
+verstage-y += $(top)/src/vendorcode/amd/fsp/picasso/bl_uapp/bl_uapp_end.S
+
+$(obj)/psp_verstage.bin: $(objcbfs)/verstage.elf
+	$(OBJCOPY_verstage) -O binary $^ $@
diff --git a/src/soc/amd/picasso/psp_verstage/delay.c b/src/soc/amd/picasso/psp_verstage/delay.c
new file mode 100644
index 0000000..ff62524
--- /dev/null
+++ b/src/soc/amd/picasso/psp_verstage/delay.c
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <bl_uapp/bl_syscall_public.h>
+#include <delay.h>
+#include <stdint.h>
+
+void udelay(uint32_t usecs)
+{
+	svc_delay_in_usec(usecs);
+}
diff --git a/src/soc/amd/picasso/psp_verstage/fch.c b/src/soc/amd/picasso/psp_verstage/fch.c
new file mode 100644
index 0000000..9059c9e
--- /dev/null
+++ b/src/soc/amd/picasso/psp_verstage/fch.c
@@ -0,0 +1,159 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include "psp_verstage.h"
+
+#include <amdblocks/acpimmio.h>
+#include <amdblocks/espi.h>
+#include <arch/exception.h>
+#include <arch/hlt.h>
+#include <arch/io.h>
+#include <bl_uapp/bl_errorcodes_public.h>
+#include <bl_uapp/bl_syscall_public.h>
+#include <console/console.h>
+#include <delay.h>
+#include <soc/i2c.h>
+#include <soc/southbridge.h>
+#include <stdint.h>
+#include <timestamp.h>
+
+static void i2c3_set_bar(void *bar)
+{
+	i2c_set_bar(3, (uintptr_t)bar);
+}
+
+static void i2c2_set_bar(void *bar)
+{
+	i2c_set_bar(2, (uintptr_t)bar);
+}
+
+static void espi_set_bar(void *bar)
+{
+	espi_update_static_bar((uintptr_t)bar);
+}
+
+static void iomux_set_bar(void *bar)
+{
+	acpimmio_iomux = bar;
+}
+
+static void misc_set_bar(void *bar)
+{
+	acpimmio_misc = bar;
+}
+
+static void gpio_set_bar(void *bar)
+{
+	acpimmio_gpio0 = bar;
+}
+
+static uintptr_t io_bar;
+
+static void io_set_bar(void *bar)
+{
+	io_bar = (uintptr_t)bar;
+}
+
+u8 io_read8(u16 reg)
+{
+	return read8((void *)(io_bar + reg));
+}
+
+void io_write8(u16 reg, u8 value)
+{
+	write8((void *)(io_bar + reg), value);
+}
+
+static struct {
+	const char *name;
+	struct {
+		FCH_IO_DEVICE device;
+		uint32_t arg0;
+	} args;
+	void (*set_bar)(void *bar);
+	void *_bar;
+} bar_map[] = {
+	{"IOMUX", {FCH_IO_DEVICE_IOMUX}, iomux_set_bar},
+	{"MISC", {FCH_IO_DEVICE_MISC}, misc_set_bar},
+	{"GPIO", {FCH_IO_DEVICE_GPIO}, gpio_set_bar},
+	{"IO", {FCH_IO_DEVICE_IOPORT}, io_set_bar},
+	{"eSPI", {FCH_IO_DEVICE_ESPI}, espi_set_bar},
+	{"I2C2", {FCH_IO_DEVICE_I2C, 2}, i2c2_set_bar},
+	{"I2C3", {FCH_IO_DEVICE_I2C, 3}, i2c3_set_bar},
+};
+
+uintptr_t *map_spi_rom(void)
+{
+	uintptr_t *addr = NULL;
+	struct SPIROM_INFO spi = {0};
+
+	if (svc_get_spi_rom_info(&spi))
+		printk(BIOS_DEBUG, "Error getting SPI ROM info.\n");
+
+	if (spi.SpiBiosSmnBase != 0)
+		if (svc_map_spi_rom(spi.SpiBiosSmnBase, CONFIG_ROM_SIZE, (void **)&addr))
+			printk(BIOS_DEBUG, "Error mapping SPI ROM to address.\n");
+
+	return addr;
+}
+
+void sb_enable_legacy_io(void)
+{
+	pm_io_write32(PM_DECODE_EN, pm_io_read32(PM_DECODE_EN) | LEGACY_IO_EN);
+}
+
+static uint32_t map_fch_devices(void)
+{
+	void *bar;
+	uint32_t err;
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(bar_map); ++i) {
+		err = svc_map_fch_dev(bar_map[i].args.device, bar_map[i].args.arg0, 0, &bar);
+		if (err) {
+			printk(BIOS_ERR, "Failed to map %s: %u\n", bar_map[i].name, err);
+			return err;
+		}
+
+		bar_map[i]._bar = bar;
+		bar_map[i].set_bar(bar);
+	}
+
+	return BL_UAPP_OK;
+}
+
+uint32_t unmap_fch_devices(void)
+{
+	void *bar;
+	uint32_t err, rtn = BL_UAPP_OK;
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(bar_map); ++i) {
+		bar = bar_map[i]._bar;
+		if (!bar)
+			continue;
+
+		err = svc_unmap_fch_dev(bar_map[i].args.device, bar);
+		if (err) {
+			printk(BIOS_ERR, "Failed to unmap %s: %u\n", bar_map[i].name, err);
+			rtn = err;
+		} else {
+			bar_map[i]._bar = NULL;
+			bar_map[i].set_bar(NULL);
+		}
+	}
+
+	return rtn;
+}
+
+uint32_t verstage_soc_early_init(void)
+{
+	return map_fch_devices();
+}
+
+void verstage_soc_init(void)
+{
+	if (CONFIG(SOC_AMD_COMMON_BLOCK_USE_ESPI)) {
+		espi_setup();
+		espi_configure_decodes();
+	}
+}
diff --git a/src/soc/amd/picasso/psp_verstage/include/arch/io.h b/src/soc/amd/picasso/psp_verstage/include/arch/io.h
new file mode 100644
index 0000000..efa128b
--- /dev/null
+++ b/src/soc/amd/picasso/psp_verstage/include/arch/io.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef __ARCH_IO_H__
+#define __ARCH_IO_H__
+
+#include <stdint.h>
+
+u8 io_read8(u16 reg);
+void io_write8(u16 reg, u8 value);
+
+static inline void outb(uint8_t value, uint16_t port)
+{
+	io_write8(port, value);
+}
+
+static inline uint8_t inb(uint16_t port)
+{
+	return io_read8(port);
+}
+
+#endif
diff --git a/src/soc/amd/picasso/psp_verstage/pmutil.c b/src/soc/amd/picasso/psp_verstage/pmutil.c
new file mode 100644
index 0000000..ddaf54d
--- /dev/null
+++ b/src/soc/amd/picasso/psp_verstage/pmutil.c
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <bl_uapp/bl_syscall_public.h>
+#include <bootmode.h>
+#include <console/console.h>
+#include <stdint.h>
+
+int platform_is_resuming(void)
+{
+	uint32_t bootmode = 0;
+	if (svc_get_boot_mode(&bootmode)) {
+		printk(BIOS_ERR, "Error getting boot mode. Assuming no resume.\n");
+		return 0;
+	}
+
+	if (bootmode == PSP_BOOT_MODE_S3_RESUME || bootmode == PSP_BOOT_MODE_S0i3_RESUME)
+		return 1;
+
+	return 0;
+}
diff --git a/src/soc/amd/picasso/psp_verstage/post.c b/src/soc/amd/picasso/psp_verstage/post.c
new file mode 100644
index 0000000..5b429c9
--- /dev/null
+++ b/src/soc/amd/picasso/psp_verstage/post.c
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <bl_uapp/bl_syscall_public.h>
+#include <console/console.h>
+#include <post.h>
+
+void arch_post_code(u8 value)
+{
+	if (CONFIG(POST_IO) && CONFIG_POST_IO_PORT == 0x80)
+		svc_write_postcode(value);
+}
diff --git a/src/soc/amd/picasso/psp_verstage/printk.c b/src/soc/amd/picasso/psp_verstage/printk.c
new file mode 100644
index 0000000..2252d44
--- /dev/null
+++ b/src/soc/amd/picasso/psp_verstage/printk.c
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <bl_uapp/bl_syscall_public.h>
+#include <console/console.h>
+#include <console/streams.h>
+#include <stdarg.h>
+#include <stdint.h>
+
+void console_hw_init(void)
+{
+	// Nothing to init for svc_debug_print
+}
+
+int do_printk(int msg_level, const char *fmt, ...)
+{
+	va_list args;
+	int i;
+
+	va_start(args, fmt);
+	i = do_vprintk(msg_level, fmt, args);
+	va_end(args);
+
+	return i;
+}
+
+int do_vprintk(int msg_level, const char *fmt, va_list args)
+{
+	int i, log_this;
+	char buf[256];
+
+	log_this = console_log_level(msg_level);
+	if (log_this < CONSOLE_LOG_FAST)
+		return 0;
+
+	i = vsnprintf(buf, sizeof(buf), fmt, args);
+	svc_debug_print(buf);
+	return i;
+}
diff --git a/src/soc/amd/picasso/psp_verstage/psp.c b/src/soc/amd/picasso/psp_verstage/psp.c
new file mode 100644
index 0000000..7f24b20
--- /dev/null
+++ b/src/soc/amd/picasso/psp_verstage/psp.c
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <smp/node.h>
+
+int boot_cpu(void)
+{
+	return 1;
+}
diff --git a/src/soc/amd/picasso/psp_verstage/psp_verstage.c b/src/soc/amd/picasso/psp_verstage/psp_verstage.c
new file mode 100644
index 0000000..bac0548
--- /dev/null
+++ b/src/soc/amd/picasso/psp_verstage/psp_verstage.c
@@ -0,0 +1,216 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include "psp_verstage.h"
+
+#include <bl_uapp/bl_syscall_public.h>
+#include <boot_device.h>
+#include <console/console.h>
+#include <security/vboot/misc.h>
+#include <security/vboot/symbols.h>
+#include <security/vboot/vboot_common.h>
+#include <arch/stages.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#define RUN_PSP_SVC_TESTS 0
+
+extern char _bss_start, _bss_end;
+static struct mem_region_device boot_dev =
+		MEM_REGION_DEV_RO_INIT(NULL, CONFIG_ROM_SIZE);
+
+void __weak verstage_mainboard_early_init(void) {}
+void __weak verstage_mainboard_init(void) {}
+
+static void reboot_into_recovery(struct vb2_context *ctx, uint32_t subcode)
+{
+	subcode += PSP_VBOOT_ERROR_SUBCODE;
+	svc_write_postcode(subcode);
+
+	vb2api_fail(ctx, VB2_RECOVERY_RO_UNSPECIFIED, (int)subcode);
+	vboot_save_data(ctx);
+
+	svc_debug_print("Rebooting into recovery\n");
+	vboot_reboot();
+}
+
+/*
+ * Tell the PSP where to load the rest of the firmware from
+ */
+static uint32_t update_boot_region(struct vb2_context *ctx)
+{
+	struct psp_ef_table *ef_table;
+	uint32_t psp_dir_addr, bios_dir_addr;
+	uint32_t *psp_dir_in_spi, *bios_dir_in_spi;
+
+	/* Continue booting from RO */
+	if (ctx->flags & VB2_CONTEXT_RECOVERY_MODE) {
+		printk(BIOS_ERR, "In recovery mode. Staying in RO.\n");
+		return 0;
+	}
+
+	if (vboot_is_firmware_slot_a(ctx)) {
+		printk(BIOS_SPEW, "Using FMAP RW_A region.\n");
+		ef_table = (struct psp_ef_table *)((CONFIG_PICASSO_FW_A_POSITION &
+						SPI_ADDR_MASK) + (uint32_t)boot_dev.base);
+	} else {
+		printk(BIOS_SPEW, "Using FMAP RW_B region.\n");
+		ef_table = (struct psp_ef_table *)((CONFIG_PICASSO_FW_B_POSITION &
+						SPI_ADDR_MASK) + (uint32_t)boot_dev.base);
+	}
+
+	if (ef_table->signature != EMBEDDED_FW_SIGNATURE) {
+		printk(BIOS_ERR, "Error: ROMSIG address is not correct.\n");
+		return POSTCODE_ROMSIG_MISMATCH_ERROR;
+	}
+
+	psp_dir_addr = ef_table->psp_table;
+	bios_dir_addr = ef_table->bios1_entry;
+	psp_dir_in_spi = (uint32_t *)((psp_dir_addr & SPI_ADDR_MASK) +
+			(uint32_t)boot_dev.base);
+	bios_dir_in_spi = (uint32_t *)((bios_dir_addr & SPI_ADDR_MASK) +
+			(uint32_t)boot_dev.base);
+	if (*psp_dir_in_spi != PSP_COOKIE) {
+		printk(BIOS_ERR, "Error: PSP Directory address is not correct.\n");
+		return POSTCODE_PSP_COOKIE_MISMATCH_ERROR;
+	}
+	if (*bios_dir_in_spi != BDT1_COOKIE) {
+		printk(BIOS_ERR, "Error: BIOS Directory address is not correct.\n");
+		return POSTCODE_BDT1_COOKIE_MISMATCH_ERROR;
+	}
+
+	if (svc_update_psp_bios_dir((void *)&psp_dir_addr,
+			(void *)&bios_dir_addr, DIR_OFFSET_SET)) {
+		printk(BIOS_ERR, "Error: Updated BIOS Directory could not be set.\n");
+		return POSTCODE_UPDATE_PSP_BIOS_DIR_ERROR;
+	}
+
+	return 0;
+}
+
+/*
+ * Save workbuf (and soon memory console and timestamps) to the bootloader to pass
+ * back to coreboot.
+ */
+static uint32_t save_buffers(struct vb2_context **ctx)
+{
+	uint32_t retval;
+	uint32_t buffer_size = DEFAULT_WORKBUF_TRANSFER_SIZE;
+	uint32_t max_buffer_size;
+
+	/*
+	 * This should never fail, but if it does, we should still try to
+	 * save the buffer. If that fails, then we should go to recovery mode.
+	 */
+	if (svc_get_max_workbuf_size(&max_buffer_size)) {
+		post_code(POSTCODE_DEFAULT_BUFFER_SIZE_NOTICE);
+		printk(BIOS_NOTICE, "Notice: using default transfer buffer size.\n");
+		max_buffer_size = DEFAULT_WORKBUF_TRANSFER_SIZE;
+	}
+	printk(BIOS_DEBUG, "\nMaximum buffer size: %d bytes\n", max_buffer_size);
+
+	retval = vb2api_relocate(_vboot2_work, _vboot2_work, buffer_size, ctx);
+	if (retval != VB2_SUCCESS) {
+		printk(BIOS_ERR, "Error shrinking workbuf. Error code %#x\n", retval);
+		buffer_size = VB2_FIRMWARE_WORKBUF_RECOMMENDED_SIZE;
+		post_code(POSTCODE_WORKBUF_RESIZE_WARNING);
+	}
+
+	if (buffer_size > max_buffer_size) {
+		printk(BIOS_ERR, "Error: Workbuf is larger than max buffer size.\n");
+		post_code(POSTCODE_WORKBUF_BUFFER_SIZE_ERROR);
+		return POSTCODE_WORKBUF_BUFFER_SIZE_ERROR;
+	}
+
+	retval = svc_save_uapp_data(UAPP_COPYBUF_CHROME_WORKBUF, (void *)_vboot2_work,
+			buffer_size);
+	if (retval) {
+		printk(BIOS_ERR, "Error: Could not save workbuf. Error code 0x%08x\n",
+				retval);
+		return POSTCODE_WORKBUF_SAVE_ERROR;
+	}
+
+	return 0;
+}
+
+void Main(void)
+{
+	uint32_t retval;
+	struct vb2_context *ctx = NULL;
+
+	/*
+	 * Do not use printk() before console_init()
+	 * Do not use post_code() before verstage_mainboard_init()
+	 */
+	svc_write_postcode(POSTCODE_ENTERED_PSP_VERSTAGE);
+	svc_debug_print("Entering verstage on PSP\n");
+	memset(&_bss_start, '\0', &_bss_end - &_bss_start);
+
+	svc_write_postcode(POSTCODE_CONSOLE_INIT);
+	console_init();
+
+	svc_write_postcode(POSTCODE_EARLY_INIT);
+	retval = verstage_soc_early_init();
+	if (retval) {
+		svc_debug_print("verstage_soc_early_init failed\n");
+		reboot_into_recovery(NULL, retval);
+	}
+	svc_debug_print("calling verstage_mainboard_early_init\n");
+
+	verstage_mainboard_early_init();
+
+	svc_write_postcode(POSTCODE_LATE_INIT);
+	sb_enable_legacy_io();
+	verstage_soc_init();
+	verstage_mainboard_init();
+
+	post_code(POSTCODE_VERSTAGE_MAIN);
+
+	verstage_main();
+
+	post_code(POSTCODE_SAVE_BUFFERS);
+	retval = save_buffers(&ctx);
+	if (retval)
+		goto err;
+
+	post_code(POSTCODE_UPDATE_BOOT_REGION);
+	retval = update_boot_region(ctx);
+	if (retval)
+		goto err;
+
+	post_code(POSTCODE_UNMAP_SPI_ROM);
+	if (boot_dev.base) {
+		if (svc_unmap_spi_rom((void *)boot_dev.base))
+			printk(BIOS_ERR, "Error unmapping SPI rom\n");
+	}
+
+	post_code(POSTCODE_UNMAP_FCH_DEVICES);
+	unmap_fch_devices();
+
+	post_code(POSTCODE_LEAVING_VERSTAGE);
+
+	printk(BIOS_DEBUG, "Leaving verstage on PSP\n");
+	svc_exit(retval);
+
+err:
+	reboot_into_recovery(ctx, retval);
+}
+
+const struct region_device *boot_device_ro(void)
+{
+	uintptr_t *addr;
+
+	addr = map_spi_rom();
+	mem_region_device_ro_init(&boot_dev, (void *)addr, CONFIG_ROM_SIZE);
+
+	return &boot_dev.rdev;
+}
+
+/*
+ * The stage_entry function is not used directly, but stage_entry() is marked as an entry
+ * point in arm/arch/header.h, so if stage_entry() isn't present and calling Main(), all
+ * the verstage code gets dropped by the linker.  Slightly hacky, but mostly harmless.
+ */
+void stage_entry(uintptr_t stage_arg)
+{
+	Main();
+}
diff --git a/src/soc/amd/picasso/psp_verstage/psp_verstage.h b/src/soc/amd/picasso/psp_verstage/psp_verstage.h
new file mode 100644
index 0000000..6fe5c7a
--- /dev/null
+++ b/src/soc/amd/picasso/psp_verstage/psp_verstage.h
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef PSP_VERSTAGE_H
+#define PSP_VERSTAGE_H
+
+#include <stdint.h>
+
+#define EMBEDDED_FW_SIGNATURE			0x55aa55aa
+#define PSP_COOKIE				0x50535024	/* 'PSP$' */
+#define BDT1_COOKIE				0x44484224	/* 'DHB$ */
+
+#define PSP_VBOOT_ERROR_SUBCODE			0x0D5D0000
+
+#define POSTCODE_ENTERED_PSP_VERSTAGE		0x00
+#define POSTCODE_CONSOLE_INIT			0x01
+#define POSTCODE_EARLY_INIT			0x02
+#define POSTCODE_LATE_INIT			0x03
+#define POSTCODE_VERSTAGE_MAIN			0x04
+
+#define POSTCODE_SAVE_BUFFERS			0x0F
+#define POSTCODE_UPDATE_BOOT_REGION		0x0F
+
+#define POSTCODE_DEFAULT_BUFFER_SIZE_NOTICE	0xC0
+#define POSTCODE_WORKBUF_RESIZE_WARNING		0xC1
+#define POSTCODE_WORKBUF_SAVE_ERROR		0xC2
+#define POSTCODE_WORKBUF_BUFFER_SIZE_ERROR	0xC3
+#define POSTCODE_ROMSIG_MISMATCH_ERROR		0xC4
+#define POSTCODE_PSP_COOKIE_MISMATCH_ERROR	0xC5
+#define POSTCODE_BDT1_COOKIE_MISMATCH_ERROR	0xC6
+#define POSTCODE_UPDATE_PSP_BIOS_DIR_ERROR	0xC7
+
+#define POSTCODE_UNMAP_SPI_ROM			0xF0
+#define POSTCODE_UNMAP_FCH_DEVICES		0xF1
+#define POSTCODE_LEAVING_VERSTAGE		0xF2
+
+#define SPI_ADDR_MASK				0x00ffffff
+#define DEFAULT_WORKBUF_TRANSFER_SIZE		(8 * KiB)
+
+struct psp_ef_table {
+	uint32_t signature; /* 0x55aa55aa */
+	uint32_t reserved0[4];
+	uint32_t psp_table;
+	uint32_t bios0_entry;
+	uint32_t bios1_entry;
+	uint32_t bios2_entry;
+} __attribute__((packed, aligned(16)));
+
+void test_svc_calls(void);
+uint32_t unmap_fch_devices(void);
+uint32_t verstage_soc_early_init(void);
+void verstage_soc_init(void);
+uintptr_t *map_spi_rom(void);
+void sb_enable_legacy_io(void);
+
+#endif /* PSP_VERSTAGE_H */
diff --git a/src/soc/amd/picasso/psp_verstage/reset.c b/src/soc/amd/picasso/psp_verstage/reset.c
new file mode 100644
index 0000000..5619e45
--- /dev/null
+++ b/src/soc/amd/picasso/psp_verstage/reset.c
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <bl_uapp/bl_syscall_public.h>
+#include <console/console.h>
+#include <reset.h>
+#include <stdint.h>
+
+
+void do_board_reset(void)
+{
+	printk(BIOS_ERR, "Resetting the board now.\n");
+	svc_reset_system(RESET_TYPE_COLD);
+}
diff --git a/src/soc/amd/picasso/psp_verstage/svc.c b/src/soc/amd/picasso/psp_verstage/svc.c
new file mode 100644
index 0000000..149e3e2
--- /dev/null
+++ b/src/soc/amd/picasso/psp_verstage/svc.c
@@ -0,0 +1,152 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include "svc.h"
+
+#include <assert.h>
+#include <bl_uapp/bl_syscall_public.h>
+#include <stddef.h>
+
+void svc_exit(uint32_t status)
+{
+	uint32_t unused = 0;
+	SVC_CALL0(SVC_EXIT, unused);
+}
+
+uint32_t svc_map_user_stack(void *start_addr, void *end_addr, void *stack_va)
+{
+	uint32_t retval = 0;
+	SVC_CALL3(SVC_MAP_USER_STACK, (uint32_t)start_addr,
+			(uint32_t)end_addr, stack_va, retval);
+	return retval;
+}
+
+void svc_debug_print(const char *string)
+{
+	uint32_t unused = 0;
+	SVC_CALL1(SVC_DEBUG_PRINT, (uint32_t)string, unused);
+}
+
+void svc_debug_print_ex(uint32_t dword0,
+		uint32_t dword1, uint32_t dword2, uint32_t dword3)
+{
+	uint32_t unused = 0;
+	SVC_CALL4(SVC_DEBUG_PRINT_EX, dword0, dword1, dword2, dword3, unused);
+}
+
+uint32_t svc_wait_10ns_multiple(uint32_t multiple)
+{
+	uint32_t retval = 0;
+	SVC_CALL1(SVC_WAIT_10NS_MULTIPLE, multiple, retval);
+	return retval;
+}
+
+uint32_t svc_get_boot_mode(uint32_t *boot_mode)
+{
+	uint32_t retval = 0;
+	SVC_CALL1(SVC_GET_BOOT_MODE, boot_mode, retval);
+	return retval;
+}
+
+void svc_delay_in_usec(uint32_t delay)
+{
+	uint32_t unused = 0;
+	SVC_CALL1(SVC_DELAY_IN_MICRO_SECONDS, delay, unused);
+}
+
+uint32_t svc_get_spi_rom_info(SPIROM_INFO *spi_rom_info)
+{
+	uint32_t retval = 0;
+	SVC_CALL1(SVC_GET_SPI_INFO, (uint32_t)spi_rom_info, retval);
+	return retval;
+}
+
+uint32_t svc_map_fch_dev(FCH_IO_DEVICE io_device,
+		uint32_t arg1, uint32_t arg2, void **io_device_axi_addr)
+{
+	uint32_t retval = 0;
+	assert(io_device < FCH_IO_DEVICE_END);
+	SVC_CALL4(SVC_MAP_FCH_IO_DEVICE, io_device, arg1, arg2,
+			(uint32_t)io_device_axi_addr, retval);
+	return retval;
+}
+
+uint32_t svc_unmap_fch_dev(FCH_IO_DEVICE io_device, void *io_device_axi_addr)
+{
+	uint32_t retval = 0;
+	assert(io_device < FCH_IO_DEVICE_END);
+	SVC_CALL2(SVC_UNMAP_FCH_IO_DEVICE, (uint32_t)io_device,
+			(uint32_t)io_device_axi_addr, retval);
+	return retval;
+}
+
+uint32_t svc_map_spi_rom(void *spi_rom_addr,
+		uint32_t size, void **spi_rom_axi_addr)
+{
+	uint32_t retval = 0;
+	SVC_CALL3(SVC_MAP_SPIROM_DEVICE, spi_rom_addr, size,
+			(uint32_t)spi_rom_axi_addr, retval);
+	return retval;
+}
+
+uint32_t svc_unmap_spi_rom(void *spi_rom_addr)
+{
+	uint32_t retval = 0;
+	SVC_CALL1(SVC_UNMAP_SPIROM_DEVICE, (uint32_t)spi_rom_addr, retval);
+	return retval;
+}
+
+uint32_t svc_update_psp_bios_dir(uint32_t *psp_dir_offset,
+		uint32_t *bios_dir_offset, DIR_OFFSET_OPERATION operation)
+{
+	uint32_t retval = 0;
+	assert(operation < DIR_OFFSET_OPERATION_MAX);
+	SVC_CALL3(SVC_UPDATE_PSP_BIOS_DIR, (uint32_t)psp_dir_offset,
+			(uint32_t)bios_dir_offset, operation, retval);
+	return retval;
+}
+
+uint32_t svc_save_uapp_data(UAPP_COPYBUF type, void *address,
+		uint32_t size)
+{
+	uint32_t retval = 0;
+	assert(type < UAPP_COPYBUF_MAX);
+	SVC_CALL3(SVC_COPY_DATA_FROM_UAPP, type, (uint32_t)address, size, retval);
+	return retval;
+}
+
+uint32_t svc_read_timer_val(PSP_TIMER_TYPE type, uint64_t *counter_value)
+{
+	unsigned int retval = 0;
+	assert(type < PSP_TIMER_TYPE_MAX);
+	SVC_CALL2(SVC_READ_TIMER_VAL, type, counter_value, retval);
+	return retval;
+}
+
+uint32_t svc_reset_system(RESET_TYPE reset_type)
+{
+	unsigned int retval = 0;
+	assert(reset_type < RESET_TYPE_MAX);
+	SVC_CALL1(SVC_RESET_SYSTEM, reset_type, retval);
+	return retval;
+}
+
+uint32_t svc_write_postcode(uint32_t postcode)
+{
+	uint32_t retval = 0;
+	SVC_CALL1(SVC_WRITE_POSTCODE, postcode, retval);
+	return retval;
+}
+
+uint32_t svc_get_max_workbuf_size(uint32_t *size)
+{
+	uint32_t retval = 0;
+	SVC_CALL1(SVC_GET_MAX_WORKBUF_SIZE, size, retval);
+	return retval;
+}
+
+uint32_t svc_crypto_sha(SHA_GENERIC_DATA *sha_op, SHA_OPERATION_MODE sha_mode)
+{
+	uint32_t retval = 0;
+	SVC_CALL2(SVC_SHA, sha_op, sha_mode, retval);
+	return retval;
+}
diff --git a/src/soc/amd/picasso/psp_verstage/svc.h b/src/soc/amd/picasso/psp_verstage/svc.h
new file mode 100644
index 0000000..03bae06
--- /dev/null
+++ b/src/soc/amd/picasso/psp_verstage/svc.h
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef PSP_VERSTAGE_SVC_H
+#define PSP_VERSTAGE_SVC_H
+
+#define SVC_CALL4(SVC_ID, R0, R1, R2, R3, Ret) \
+	__asm__ __volatile__ ( \
+	"mov r0, %[reg0]\n\t" \
+	"mov r1, %[reg1]\n\t" \
+	"mov r2, %[reg2]\n\t" \
+	"mov r3, %[reg3]\n\t" \
+	"svc %[id]\n\t" \
+	"mov %[result], r0\n\t" \
+	: [result] "=r" (Ret) /* output */ \
+	: [id] "i" (SVC_ID), [reg0] "r" (R0), [reg1] "r" (R1), [reg2] "r" (R2), \
+		[reg3] "r" (R3) /* input(s) */ \
+	: "r0", "r1", "r2", "r3", "memory", "cc" /* list of clobbered registers */);
+
+#define SVC_CALL3(SVC_ID, R0,  R1, R2, Ret) \
+	__asm__ __volatile__ ( \
+	"mov r0, %[reg0]\n\t" \
+	"mov r1, %[reg1]\n\t" \
+	"mov r2, %[reg2]\n\t" \
+	"svc %[id]\n\t" \
+	"mov %[result], r0\n\t" \
+	: [result] "=r" (Ret) /* output */ \
+	: [id] "i" (SVC_ID), [reg0] "r" (R0), [reg1] "r" (R1), [reg2] "r" (R2) \
+	: "r0", "r1", "r2", "memory", "cc" /* list of clobbered registers */);
+
+#define SVC_CALL2(SVC_ID, R0, R1, Ret) \
+	__asm__ __volatile__ ( \
+	"mov r0, %[reg0]\n\t" \
+	"mov r1, %[reg1]\n\t" \
+	"svc %[id]\n\t" \
+	"mov %[result], r0\n\t" \
+	: [result] "=r" (Ret) /* output */ \
+	: [id] "i" (SVC_ID), [reg0] "r" (R0), [reg1] "r" (R1)/* input(s) */ \
+	: "r0", "r1", "memory", "cc" /* list of clobbered registers */);
+
+#define SVC_CALL1(SVC_ID, R0, Ret) \
+	__asm__ __volatile__ ( \
+	"mov r0, %[reg0]\n\t" \
+	"svc %[id]\n\t" \
+	"mov %[result], r0\n\t" \
+	: [result] "=r" (Ret) /* output */ \
+	: [id] "i" (SVC_ID), [reg0] "r" (R0) /* input(s) */ \
+	: "r0", "memory", "cc" /* list of clobbered registers */);
+
+#define SVC_CALL0(SVC_ID, Ret) \
+	__asm__ __volatile__ ( \
+	"svc %[id]\n\t" \
+	"mov %[result], r0\n\t" \
+	: [result] "=r" (Ret) /* output */ \
+	: [id] "I" (SVC_ID) /* input(s) */ \
+	: "memory", "cc" /* list of clobbered registers */);
+
+#endif /* PSP_VERSTAGE_SVC_H */
diff --git a/src/soc/amd/picasso/psp_verstage/timer.c b/src/soc/amd/picasso/psp_verstage/timer.c
new file mode 100644
index 0000000..04728ba
--- /dev/null
+++ b/src/soc/amd/picasso/psp_verstage/timer.c
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <bl_uapp/bl_syscall_public.h>
+#include <stdint.h>
+#include <timer.h>
+
+void timer_monotonic_get(struct mono_time *mt)
+{
+	/* Chrono timer is based on a 25MHz clock */
+	uint64_t clk;
+
+	svc_read_timer_val(PSP_TIMER_TYPE_CHRONO, &clk);
+
+	mt->microseconds = clk / 25;
+}
diff --git a/src/soc/amd/picasso/psp_verstage/timestamp.c b/src/soc/amd/picasso/psp_verstage/timestamp.c
new file mode 100644
index 0000000..b3b8f75
--- /dev/null
+++ b/src/soc/amd/picasso/psp_verstage/timestamp.c
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <timestamp.h>
+
+/* Stubs */
+void timestamp_add_now(enum timestamp_id id)
+{
+}
+
+void timestamp_add(enum timestamp_id id, uint64_t ts)
+{
+}
+
+uint64_t timestamp_get(void)
+{
+	return 0;
+}
diff --git a/src/soc/amd/picasso/psp_verstage/vboot_crypto.c b/src/soc/amd/picasso/psp_verstage/vboot_crypto.c
new file mode 100644
index 0000000..c010eb6
--- /dev/null
+++ b/src/soc/amd/picasso/psp_verstage/vboot_crypto.c
@@ -0,0 +1,104 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <2crypto.h>
+#include <2return_codes.h>
+#include <bl_uapp/bl_syscall_public.h>
+#include <commonlib/bsd/helpers.h>
+#include <console/console.h>
+#include "psp_verstage.h"
+#include <stddef.h>
+#include <string.h>
+#include <vb2_api.h>
+
+static struct SHA_GENERIC_DATA_T sha_op;
+static uint32_t sha_op_size_remaining;
+static uint8_t __attribute__((aligned(32))) sha_hash[64];
+
+vb2_error_t vb2ex_hwcrypto_digest_init(enum vb2_hash_algorithm hash_alg, uint32_t data_size)
+{
+	printk(BIOS_DEBUG, "Calculating hash of %d bytes\n", data_size);
+
+	sha_op_size_remaining = data_size;
+
+	if (hash_alg == VB2_HASH_SHA256) {
+		sha_op.SHAType = SHA_TYPE_256;
+		sha_op.DigestLen = 32;
+	} else if (hash_alg == VB2_HASH_SHA512) {
+		sha_op.SHAType = SHA_TYPE_512;
+		sha_op.DigestLen = 64;
+	} else {
+		printk(BIOS_INFO, "Unsupported hash_alg %d!\n", hash_alg);
+		return VB2_ERROR_EX_HWCRYPTO_UNSUPPORTED;
+	}
+
+	/* Set init flag for first operation */
+	sha_op.Init = 1;
+
+	/* Clear eom flag until last operation */
+	sha_op.Eom = 0;
+
+	/* Need documentation on this b:157610147 */
+	sha_op.DataMemType = 2;
+
+	sha_op.Digest = sha_hash;
+
+	sha_op.IntermediateDigest = NULL;
+
+	sha_op.IntermediateMsgLen = 0;
+
+	return VB2_SUCCESS;
+}
+
+vb2_error_t vb2ex_hwcrypto_digest_extend(const uint8_t *buf, uint32_t size)
+{
+	uint32_t retval;
+	sha_op.Data = (uint8_t *) buf;
+
+	if (!sha_op_size_remaining) {
+		printk(BIOS_ERR, "ERROR: got more data than expected.\n");
+		return VB2_ERROR_UNKNOWN;
+	}
+
+	while (size) {
+		sha_op.DataLen = size;
+
+		sha_op_size_remaining -= sha_op.DataLen;
+
+		/* Set eom flag for final operation */
+		if (sha_op_size_remaining == 0)
+			sha_op.Eom = 1;
+
+		retval = svc_crypto_sha(&sha_op, SHA_GENERIC);
+		if (retval) {
+			printk(BIOS_ERR, "ERROR: HW crypto failed - errorcode: %#x\n",
+					retval);
+			return VB2_ERROR_UNKNOWN;
+		}
+
+		/* Clear init flag after first operation */
+		if (sha_op.Init == 1)
+			sha_op.Init = 0;
+
+		size -= sha_op.DataLen;
+	}
+
+	return VB2_SUCCESS;
+}
+
+/* Copy the hash back to verstage */
+vb2_error_t vb2ex_hwcrypto_digest_finalize(uint8_t *digest, uint32_t digest_size)
+{
+	if (sha_op.Eom == 0) {
+		printk(BIOS_ERR, "ERROR: Got less data than expected.\n");
+		return VB2_ERROR_UNKNOWN;
+	}
+
+	if (digest_size != sha_op.DigestLen) {
+		printk(BIOS_ERR, "ERROR: Digest size does not match expected length.\n");
+		return VB2_ERROR_UNKNOWN;
+	}
+
+	memcpy(digest, sha_hash, digest_size);
+
+	return VB2_SUCCESS;
+}