soc/amd/common/psp_verstage: Introduce boot device driver

PSP verstage can access the boot device either in Programmed I/O mode or
DMA mode. Introduce a boot device driver and use the appropriate mode
based on the SoC support.

BUG=b:194990811
TEST=Build and boot to OS in Guybrush.

Change-Id: I8ca5290156199548916852e48f4e11de7cb886fb
Signed-off-by: Karthikeyan Ramasubramanian <kramasub@google.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/57563
Reviewed-by: Raul Rangel <rrangel@chromium.org>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
diff --git a/src/soc/amd/common/Kconfig.common b/src/soc/amd/common/Kconfig.common
index d6f9d01..5a84f2f 100644
--- a/src/soc/amd/common/Kconfig.common
+++ b/src/soc/amd/common/Kconfig.common
@@ -8,5 +8,6 @@
 source "src/soc/amd/common/block/*/Kconfig"
 source "src/soc/amd/common/fsp/Kconfig"
 source "src/soc/amd/common/pi/Kconfig"
+source "src/soc/amd/common/psp_verstage/Kconfig"
 
 endif # SOC_AMD_COMMON
diff --git a/src/soc/amd/common/psp_verstage/Kconfig b/src/soc/amd/common/psp_verstage/Kconfig
new file mode 100644
index 0000000..834abaa
--- /dev/null
+++ b/src/soc/amd/common/psp_verstage/Kconfig
@@ -0,0 +1,7 @@
+config PSP_VERSTAGE_CCP_DMA
+	bool
+	default n
+	help
+	  Configure PSP Verstage to use Crypto Co-processor (CCP) DMA while
+	  accessing the boot device. Select it on platforms which supports
+	  using CCP DMA to access the boot device.
diff --git a/src/soc/amd/common/psp_verstage/Makefile.inc b/src/soc/amd/common/psp_verstage/Makefile.inc
index a913a5b..fb82ef4 100644
--- a/src/soc/amd/common/psp_verstage/Makefile.inc
+++ b/src/soc/amd/common/psp_verstage/Makefile.inc
@@ -7,6 +7,7 @@
 # This size should match the size in the linker script.
 CFLAGS_arm += -Wstack-usage=40960
 
+verstage-y += boot_dev.c
 verstage-y += delay.c
 verstage-y += fch.c
 verstage-y += pmutil.c
diff --git a/src/soc/amd/common/psp_verstage/boot_dev.c b/src/soc/amd/common/psp_verstage/boot_dev.c
new file mode 100644
index 0000000..5b25d59
--- /dev/null
+++ b/src/soc/amd/common/psp_verstage/boot_dev.c
@@ -0,0 +1,85 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include "psp_verstage.h"
+
+#include <bl_uapp/bl_errorcodes_public.h>
+#include <bl_uapp/bl_syscall_public.h>
+#include <boot_device.h>
+#include <commonlib/region.h>
+#include <console/console.h>
+#include <string.h>
+
+#define DEST_BUF_ALIGNMENT 16
+
+static void *boot_dev_mmap(const struct region_device *rd, size_t offset, size_t size __unused)
+{
+	const struct mem_region_device *mdev;
+
+	mdev = container_of(rd, __typeof__(*mdev), rdev);
+
+	return &(mdev->base[offset]);
+}
+
+static int boot_dev_munmap(const struct region_device *rd __unused, void *mapping __unused)
+{
+	return 0;
+}
+
+static ssize_t boot_dev_dma_readat(const struct region_device *rd, void *dest,
+							size_t offset, size_t size)
+{
+	size_t memcpy_size = ALIGN_UP((uintptr_t)dest, DEST_BUF_ALIGNMENT) - (uintptr_t)dest;
+	const struct mem_region_device *mdev = container_of(rd, __typeof__(*mdev), rdev);
+	int ret;
+
+	if (memcpy_size > size)
+		memcpy_size = size;
+	/* Alignment requirement is only on dest buffer for CCP DMA. So do a memcpy
+	   until the destination buffer alignment requirement is met. */
+	if (memcpy_size)
+		memcpy(dest, &(mdev->base[offset]), memcpy_size);
+
+	dest = ((char *)dest + memcpy_size);
+	offset += memcpy_size;
+	size -= memcpy_size;
+	if (!size)
+		return memcpy_size;
+
+	ret = svc_ccp_dma((uint32_t)offset, dest, (uint32_t)size);
+	if (ret != BL_OK) {
+		printk(BIOS_ERR, "%s: Failed dest:%p offset:%zu size:%zu ret:%d\n",
+						__func__, dest, offset, size, ret);
+		return -1;
+	}
+
+	return size + memcpy_size;
+}
+
+static ssize_t boot_dev_readat(const struct region_device *rd, void *dest,
+						size_t offset, size_t size)
+{
+	const struct mem_region_device *mdev = container_of(rd, __typeof__(*mdev), rdev);
+
+	if (CONFIG(PSP_VERSTAGE_CCP_DMA))
+		return boot_dev_dma_readat(rd, dest, offset, size);
+
+	memcpy(dest, &(mdev->base[offset]), size);
+	return size;
+}
+
+const struct region_device_ops boot_dev_rdev_ro_ops = {
+	.mmap = boot_dev_mmap,
+	.munmap = boot_dev_munmap,
+	.readat = boot_dev_readat,
+};
+
+static struct mem_region_device boot_dev = {
+	.rdev = REGION_DEV_INIT(&boot_dev_rdev_ro_ops, 0, CONFIG_ROM_SIZE),
+};
+
+const struct region_device *boot_device_ro(void)
+{
+	if (!boot_dev.base)
+		boot_dev.base = (void *)map_spi_rom();
+	return &boot_dev.rdev;
+}
diff --git a/src/soc/amd/common/psp_verstage/psp_verstage.c b/src/soc/amd/common/psp_verstage/psp_verstage.c
index 5c59c4f..7c37355 100644
--- a/src/soc/amd/common/psp_verstage/psp_verstage.c
+++ b/src/soc/amd/common/psp_verstage/psp_verstage.c
@@ -6,6 +6,7 @@
 #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>
@@ -20,8 +21,6 @@
 #include <timestamp.h>
 
 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) {}
@@ -82,6 +81,7 @@
 	uint32_t *psp_dir_in_spi, *bios_dir_in_spi;
 	const char *fname;
 	void *amdfw_location;
+	void *boot_dev_base = rdev_mmap_full(boot_device_ro());
 
 	/* Continue booting from RO */
 	if (ctx->flags & VB2_CONTEXT_RECOVERY_MODE) {
@@ -109,9 +109,9 @@
 	psp_dir_addr = ef_table->psp_table;
 	bios_dir_addr = get_bios_dir_addr(ef_table);
 	psp_dir_in_spi = (uint32_t *)((psp_dir_addr & SPI_ADDR_MASK) +
-			(uint32_t)boot_dev.base);
+			(uint32_t)boot_dev_base);
 	bios_dir_in_spi = (uint32_t *)((bios_dir_addr & SPI_ADDR_MASK) +
-			(uint32_t)boot_dev.base);
+			(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;
@@ -200,6 +200,7 @@
 {
 	uint32_t retval;
 	struct vb2_context *ctx = NULL;
+	void *boot_dev_base;
 
 	/*
 	 * Do not use printk() before console_init()
@@ -259,8 +260,9 @@
 
 
 	post_code(POSTCODE_UNMAP_SPI_ROM);
-	if (boot_dev.base) {
-		if (svc_unmap_spi_rom((void *)boot_dev.base))
+	boot_dev_base = rdev_mmap_full(boot_device_ro());
+	if (boot_dev_base) {
+		if (svc_unmap_spi_rom((void *)boot_dev_base))
 			printk(BIOS_ERR, "Error unmapping SPI rom\n");
 	}
 
@@ -273,16 +275,6 @@
 	svc_exit(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