soc/amd/common/psp_verstage: Map/unmap boot device on need basis

Currently the SPI ROM is mapped completely when the boot device is
initialized. That mapping remains active throughout the execution time
of PSP verstage. Every 1 MiB of mapped SPI ROM region consumes 1 TLB
Slot in PSP for use during memory mapped or DMA access. With 16 MiB of
mapped SPI ROM + FCH devices + 4 reserved TLB slots, 31 out of 32 total
TLB slots is consumed. This leaves almost no scope for future expansion.
With upcoming programs possibly using 32 MiB SPI ROM, PSP will run out
of TLB slots to support 32 MiB.

Hence instead of mapping the entire SPI ROM upfront, get the SPI ROM SMN
address during the boot device initialization. Update the boot device
region operations to map and unmap the SPI flash with the desired offset
and size using the SVC call. Then anytime a memory mapped SPI ROM access
is performed: map the required area, read the data and immediately unmap
the area. There is no update required when using CCP DMA, since the
concerned SVC call performs mapping and unmapping of the required SPI
flash area implicitly.

With these changes, maximum of 8 slots(size of RO section) might get
used at any point in time during the PSP verstage execution.

BUG=b:240664755
TEST=Build and boot to OS in Skyrim. Perform cold, warm reboots and
suspend/resume cycles for 50 iterations each. Ensured that there is no
impact to boot time.

Change-Id: Icd44ea7b2a366e9269debcab4186d1fc71651db2
Signed-off-by: Karthikeyan Ramasubramanian <kramasub@google.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/74606
Reviewed-by: Raul Rangel <rrangel@chromium.org>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
diff --git a/src/soc/amd/common/psp_verstage/boot_dev.c b/src/soc/amd/common/psp_verstage/boot_dev.c
index 4ee78b8..c129479 100644
--- a/src/soc/amd/common/psp_verstage/boot_dev.c
+++ b/src/soc/amd/common/psp_verstage/boot_dev.c
@@ -11,22 +11,51 @@
 
 #define DEST_BUF_ALIGNMENT 16
 
-static void *boot_dev_mmap(const struct region_device *rd, size_t offset,
-			   size_t size __always_unused)
+static uint32_t active_map_count;
+
+static void *boot_dev_mmap(const struct region_device *rd, size_t offset, size_t size)
 {
 	const struct mem_region_device *mdev;
+	void *mapping = NULL;
+	uint32_t ret;
 
 	mdev = container_of(rd, __typeof__(*mdev), rdev);
 
-	return &(mdev->base[offset]);
+	if (mdev->base) {
+		if ((ret = svc_map_spi_rom(&mdev->base[offset], size, (void **)&mapping))
+										!= BL_OK)
+			printk(BIOS_ERR, "Failed(%d) to map SPI ROM offset: %zu, size: %zu\n",
+									ret, offset, size);
+		else
+			active_map_count++;
+	}
+
+	return mapping;
 }
 
-static int boot_dev_munmap(const struct region_device *rd __always_unused,
-			   void *mapping __always_unused)
+static int boot_dev_munmap(const struct region_device *rd, void *mapping)
 {
+	uint32_t ret;
+
+	active_map_count--;
+	if ((ret = svc_unmap_spi_rom(mapping)) != BL_OK)
+		printk(BIOS_ERR, "Failed(%d) to unmap SPI ROM mapping %p\n", ret, mapping);
 	return 0;
 }
 
+static ssize_t boot_dev_mmap_readat(const struct region_device *rd, void *dest,
+							size_t offset, size_t size)
+{
+	void *mapping = boot_dev_mmap(rd, offset, size);
+
+	if (!mapping)
+		return -1;
+
+	memcpy(dest, mapping, size);
+	boot_dev_munmap(rd, mapping);
+	return size;
+}
+
 static ssize_t boot_dev_dma_readat(const struct region_device *rd, void *dest,
 							size_t offset, size_t size)
 {
@@ -38,8 +67,8 @@
 		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);
+	if (memcpy_size && boot_dev_mmap_readat(rd, dest, offset, memcpy_size) != memcpy_size)
+		return -1;
 
 	dest = ((char *)dest + memcpy_size);
 	offset += memcpy_size;
@@ -65,8 +94,7 @@
 	if (CONFIG(PSP_VERSTAGE_CCP_DMA))
 		return boot_dev_dma_readat(rd, dest, offset, size);
 
-	memcpy(dest, &(mdev->base[offset]), size);
-	return size;
+	return boot_dev_mmap_readat(rd, dest, offset, size);
 }
 
 const struct region_device_ops boot_dev_rdev_ro_ops = {
@@ -85,3 +113,8 @@
 		boot_dev.base = (void *)map_spi_rom();
 	return &boot_dev.rdev;
 }
+
+uint32_t boot_dev_get_active_map_count(void)
+{
+	return active_map_count;
+}
diff --git a/src/soc/amd/common/psp_verstage/fch.c b/src/soc/amd/common/psp_verstage/fch.c
index 7f850fd..0517545 100644
--- a/src/soc/amd/common/psp_verstage/fch.c
+++ b/src/soc/amd/common/psp_verstage/fch.c
@@ -90,19 +90,14 @@
 	{"AOAC", {FCH_IO_DEVICE_AOAC}, aoac_set_bar},
 };
 
-uintptr_t *map_spi_rom(void)
+void *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;
+	return spi.SpiBiosSmnBase;
 }
 
 static uint32_t map_fch_devices(void)
diff --git a/src/soc/amd/common/psp_verstage/include/psp_verstage.h b/src/soc/amd/common/psp_verstage/include/psp_verstage.h
index c3240fa..6c5d4c0 100644
--- a/src/soc/amd/common/psp_verstage/include/psp_verstage.h
+++ b/src/soc/amd/common/psp_verstage/include/psp_verstage.h
@@ -38,7 +38,7 @@
 #define POSTCODE_CMOS_RECOVERY			0xCA
 #define POSTCODE_EARLY_INIT_ERROR		0xCB
 #define POSTCODE_INIT_TPM_FAILED		0xCC
-
+#define POSTCODE_MAP_SPI_ROM_FAILED		0xCD
 
 #define POSTCODE_UNMAP_SPI_ROM			0xF0
 #define POSTCODE_UNMAP_FCH_DEVICES		0xF1
@@ -57,7 +57,7 @@
 void verstage_soc_espi_init(void);
 void verstage_soc_i2c_init(void);
 void verstage_soc_spi_init(void);
-uintptr_t *map_spi_rom(void);
+void *map_spi_rom(void);
 
 uint32_t update_psp_bios_dir(uint32_t *psp_dir_offset, uint32_t *bios_dir_offset);
 uint32_t save_uapp_data(void *address, uint32_t size);
@@ -72,4 +72,6 @@
 
 void report_hsp_secure_state(void);
 
+uint32_t boot_dev_get_active_map_count(void);
+
 #endif /* PSP_VERSTAGE_H */
diff --git a/src/soc/amd/common/psp_verstage/psp_verstage.c b/src/soc/amd/common/psp_verstage/psp_verstage.c
index e5b0277..dfea43d 100644
--- a/src/soc/amd/common/psp_verstage/psp_verstage.c
+++ b/src/soc/amd/common/psp_verstage/psp_verstage.c
@@ -10,6 +10,7 @@
 #include <commonlib/region.h>
 #include <console/console.h>
 #include <fmap.h>
+#include <fmap_config.h>
 #include <pc80/mc146818rtc.h>
 #include <soc/iomap.h>
 #include <soc/psp_transfer.h>
@@ -74,7 +75,8 @@
 	const char *fname;
 	const char *hash_fname;
 	void *amdfw_location;
-	void *boot_dev_base = rdev_mmap_full(boot_device_ro());
+	void *map_base = NULL;
+	size_t map_offset;
 
 	/* Continue booting from RO */
 	if (ctx->flags & VB2_CONTEXT_RECOVERY_MODE) {
@@ -85,36 +87,53 @@
 	if (vboot_is_firmware_slot_a(ctx)) {
 		fname = "apu/amdfw_a";
 		hash_fname = "apu/amdfw_a_hash";
+		map_offset = FMAP_SECTION_FW_MAIN_A_START - FMAP_SECTION_FLASH_START;
+		map_base = rdev_mmap(boot_device_ro(), map_offset, FMAP_SECTION_FW_MAIN_A_SIZE);
 	} else {
 		fname = "apu/amdfw_b";
 		hash_fname = "apu/amdfw_b_hash";
+		map_offset = FMAP_SECTION_FW_MAIN_B_START - FMAP_SECTION_FLASH_START;
+		map_base = rdev_mmap(boot_device_ro(), map_offset, FMAP_SECTION_FW_MAIN_B_SIZE);
+	}
+
+	if (!map_base) {
+		printk(BIOS_ERR, "Failed to map RW FW_MAIN section.\n");
+		return POSTCODE_MAP_SPI_ROM_FAILED;
 	}
 
 	amdfw_location = cbfs_map(fname, NULL);
 	if (!amdfw_location) {
 		printk(BIOS_ERR, "AMD Firmware table not found.\n");
+		rdev_munmap(boot_device_ro(), map_base);
 		return POSTCODE_AMD_FW_MISSING;
 	}
+
 	ef_table = (struct embedded_firmware *)amdfw_location;
 	if (ef_table->signature != EMBEDDED_FW_SIGNATURE) {
 		printk(BIOS_ERR, "ROMSIG address is not correct.\n");
+		cbfs_unmap(amdfw_location);
+		rdev_munmap(boot_device_ro(), map_base);
 		return POSTCODE_ROMSIG_MISMATCH_ERROR;
 	}
 
 	psp_dir_addr = ef_table->new_psp_directory;
 	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)map_base - map_offset);
 	if (*psp_dir_in_spi != PSP_COOKIE) {
 		printk(BIOS_ERR, "PSP Directory address is not correct.\n");
+		cbfs_unmap(amdfw_location);
+		rdev_munmap(boot_device_ro(), map_base);
 		return POSTCODE_PSP_COOKIE_MISMATCH_ERROR;
 	}
 
 	if (bios_dir_addr) {
 		bios_dir_in_spi = (uint32_t *)((bios_dir_addr & SPI_ADDR_MASK) +
-				(uint32_t)boot_dev_base);
+				(uint32_t)map_base - map_offset);
 		if (*bios_dir_in_spi != BHD_COOKIE) {
 			printk(BIOS_ERR, "BIOS Directory address is not correct.\n");
+			cbfs_unmap(amdfw_location);
+			rdev_munmap(boot_device_ro(), map_base);
 			return POSTCODE_BHD_COOKIE_MISMATCH_ERROR;
 		}
 	}
@@ -128,12 +147,16 @@
 
 	if (update_psp_bios_dir(&psp_dir_addr, &bios_dir_addr)) {
 		printk(BIOS_ERR, "Updated BIOS Directory could not be set.\n");
+		cbfs_unmap(amdfw_location);
+		rdev_munmap(boot_device_ro(), map_base);
 		return POSTCODE_UPDATE_PSP_BIOS_DIR_ERROR;
 	}
 
 	if (CONFIG(SEPARATE_SIGNED_PSPFW))
 		update_psp_fw_hash_table(hash_fname);
 
+	cbfs_unmap(amdfw_location);
+	rdev_munmap(boot_device_ro(), map_base);
 	return 0;
 }
 
@@ -208,7 +231,6 @@
 {
 	uint32_t retval;
 	struct vb2_context *ctx = NULL;
-	void *boot_dev_base;
 	uint32_t bootmode;
 
 	/*
@@ -324,14 +346,7 @@
 	if (retval)
 		reboot_into_recovery(ctx, retval);
 
-
-	post_code(POSTCODE_UNMAP_SPI_ROM);
-	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");
-	}
-
+	assert(!boot_dev_get_active_map_count());
 	post_code(POSTCODE_UNMAP_FCH_DEVICES);
 	unmap_fch_devices();