summaryrefslogtreecommitdiff
path: root/src/soc/amd/common/pi
diff options
context:
space:
mode:
authorFelix Held <felix.held@amd.corp-partner.google.com>2021-09-23 17:16:32 +0200
committerFelix Held <felix-coreboot@felixheld.de>2021-09-24 15:47:59 +0000
commitc9737c5ce9d5a3c52ae86ea5fe538bec0b305b99 (patch)
treeb2c0ef3904b50b29add494f5f75b105bb8e2ead5 /src/soc/amd/common/pi
parentc0982abf86a6312e2572cc0225bbfe702c7ff2bd (diff)
soc/amd/common: move block/pi out of the block folder
Since the binaryPI glue code is specific to a binary interface, but not for a hardware block, move it out of the common blocks directory. This also brings the binaryPI support in line with the FSP support which is used on the newer generations. This also drops the SOC_AMD_COMMON_BLOCK_PI Kconfig option and makes use of the already existing SOC_AMD_PI Kconfig option instead. Signed-off-by: Felix Held <felix-coreboot@felixheld.de> Change-Id: I014e538f2772938031950475e456cc40dd05d74c Reviewed-on: https://review.coreboot.org/c/coreboot/+/57884 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Raul Rangel <rrangel@chromium.org>
Diffstat (limited to 'src/soc/amd/common/pi')
-rw-r--r--src/soc/amd/common/pi/Kconfig37
-rw-r--r--src/soc/amd/common/pi/Makefile.inc19
-rw-r--r--src/soc/amd/common/pi/agesawrapper.c422
-rw-r--r--src/soc/amd/common/pi/amd_late_init.c177
-rw-r--r--src/soc/amd/common/pi/amd_resume_final.c12
-rw-r--r--src/soc/amd/common/pi/def_callouts.c245
-rw-r--r--src/soc/amd/common/pi/heapmanager.c440
-rw-r--r--src/soc/amd/common/pi/image.c48
-rw-r--r--src/soc/amd/common/pi/refcode_loader.c105
-rw-r--r--src/soc/amd/common/pi/s3_resume.c80
10 files changed, 1585 insertions, 0 deletions
diff --git a/src/soc/amd/common/pi/Kconfig b/src/soc/amd/common/pi/Kconfig
new file mode 100644
index 0000000000..7b95364961
--- /dev/null
+++ b/src/soc/amd/common/pi/Kconfig
@@ -0,0 +1,37 @@
+config SOC_AMD_PI
+ bool
+ depends on SOC_AMD_COMMON_BLOCK_ACPI
+ select CACHE_MRC_SETTINGS
+ select HAVE_DEBUG_RAM_SETUP
+ select MRC_WRITE_NV_LATE
+ help
+ This option builds functions that interface AMD's AGESA reference
+ code packaged in the binaryPI form and S3-related functionality.
+
+if SOC_AMD_PI
+
+config PI_AGESA_CAR_HEAP_BASE
+ hex
+ default 0x400000
+ help
+ The AGESA PI blob may be built to allow an optional callout for
+ AgesaHeapRebase. If AGESA calls AgesaHeapRebase, this option
+ determines the location of the heap prior to DRAM availability.
+
+config PI_AGESA_TEMP_RAM_BASE
+ hex
+ default 0x100000
+ help
+ During a boot from S5, AGESA copies its CAR-based heap to a temporary
+ location in DRAM. Once coreboot has established cbmem, the heap
+ is moved again. This symbol determines the temporary location for
+ the heap.
+
+config PI_AGESA_HEAP_SIZE
+ hex
+ default 0x20000
+ help
+ This option determines the amount of space allowed for AGESA heap
+ prior to DRAM availability.
+
+endif # SOC_AMD_PI
diff --git a/src/soc/amd/common/pi/Makefile.inc b/src/soc/amd/common/pi/Makefile.inc
new file mode 100644
index 0000000000..55fbd95cc8
--- /dev/null
+++ b/src/soc/amd/common/pi/Makefile.inc
@@ -0,0 +1,19 @@
+ifeq ($(CONFIG_SOC_AMD_PI),y)
+
+romstage-y += agesawrapper.c
+romstage-y += def_callouts.c
+romstage-y += heapmanager.c
+romstage-y += image.c
+romstage-y += refcode_loader.c
+romstage-y += s3_resume.c
+
+ramstage-y += agesawrapper.c
+ramstage-y += amd_late_init.c
+ramstage-$(CONFIG_HAVE_ACPI_RESUME) += amd_resume_final.c
+ramstage-y += def_callouts.c
+ramstage-y += heapmanager.c
+ramstage-y += image.c
+ramstage-y += refcode_loader.c
+ramstage-y += s3_resume.c
+
+endif # CONFIG_SOC_AMD_PI
diff --git a/src/soc/amd/common/pi/agesawrapper.c b/src/soc/amd/common/pi/agesawrapper.c
new file mode 100644
index 0000000000..ca576bad99
--- /dev/null
+++ b/src/soc/amd/common/pi/agesawrapper.c
@@ -0,0 +1,422 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <acpi/acpi.h>
+#include <console/console.h>
+#include <timestamp.h>
+#include <amdblocks/biosram.h>
+#include <amdblocks/s3_resume.h>
+#include <amdblocks/agesawrapper.h>
+#include <amdblocks/BiosCallOuts.h>
+#include <amdblocks/ioapic.h>
+#include <soc/pci_devs.h>
+#include <soc/northbridge.h>
+#include <soc/cpu.h>
+
+void __weak SetMemParams(AMD_POST_PARAMS *PostParams) {}
+void __weak OemPostParams(AMD_POST_PARAMS *PostParams) {}
+
+/* ACPI table pointers returned by AmdInitLate */
+static void *DmiTable;
+static void *AcpiPstate;
+static void *AcpiSrat;
+static void *AcpiSlit;
+
+static void *AcpiWheaMce;
+static void *AcpiWheaCmc;
+static void *AcpiAlib;
+static void *AcpiIvrs;
+static void *AcpiCrat;
+
+static AGESA_STATUS module_dispatch(AGESA_STRUCT_NAME func,
+ AMD_CONFIG_PARAMS *StdHeader)
+{
+ MODULE_ENTRY dispatcher = agesa_get_dispatcher();
+
+ if (!dispatcher)
+ return AGESA_UNSUPPORTED;
+
+ StdHeader->Func = func;
+ return dispatcher(StdHeader);
+}
+
+static AGESA_STATUS amd_dispatch(void *Params)
+{
+ AMD_CONFIG_PARAMS *StdHeader = Params;
+ return module_dispatch(StdHeader->Func, StdHeader);
+}
+
+AGESA_STATUS amd_late_run_ap_task(AP_EXE_PARAMS *ApExeParams)
+{
+ AMD_CONFIG_PARAMS *StdHeader = (void *)ApExeParams;
+ return module_dispatch(AMD_LATE_RUN_AP_TASK, StdHeader);
+}
+
+static AGESA_STATUS amd_create_struct(AMD_INTERFACE_PARAMS *aip,
+ AGESA_STRUCT_NAME func, void *buf, size_t len)
+{
+ AGESA_STATUS status;
+
+ /* Should clone entire StdHeader here. */
+ memset(aip, 0, sizeof(*aip));
+ aip->StdHeader.CalloutPtr = &GetBiosCallout;
+
+ /* If we provide the buffer, API expects it to have
+ StdHeader already filled. */
+ if (buf != NULL && len >= sizeof(aip->StdHeader)) {
+ memcpy(buf, &aip->StdHeader, sizeof(aip->StdHeader));
+ aip->AllocationMethod = ByHost;
+ aip->NewStructPtr = buf;
+ aip->NewStructSize = len;
+ } else {
+ if (ENV_ROMSTAGE)
+ aip->AllocationMethod = PreMemHeap;
+ if (ENV_RAMSTAGE)
+ aip->AllocationMethod = PostMemDram;
+ }
+
+ aip->AgesaFunctionName = func;
+ status = module_dispatch(AMD_CREATE_STRUCT, &aip->StdHeader);
+
+ if (status != AGESA_SUCCESS) {
+ printk(BIOS_ERR, "Error: AmdCreateStruct() for 0x%x returned 0x%x. "
+ "Proper system initialization may not be possible.\n",
+ aip->AgesaFunctionName, status);
+ }
+
+ if (!aip->NewStructPtr)
+ die("No AGESA structure created");
+
+ return status;
+}
+
+static AGESA_STATUS amd_release_struct(AMD_INTERFACE_PARAMS *aip)
+{
+ return module_dispatch(AMD_RELEASE_STRUCT, &aip->StdHeader);
+}
+
+static AGESA_STATUS amd_init_reset(AMD_RESET_PARAMS *ResetParams)
+{
+ AGESA_STATUS status;
+
+ SetFchResetParams(&ResetParams->FchInterface);
+
+ timestamp_add_now(TS_AGESA_INIT_RESET_START);
+ status = amd_dispatch(ResetParams);
+ timestamp_add_now(TS_AGESA_INIT_RESET_DONE);
+
+ return status;
+}
+
+static AGESA_STATUS amd_init_early(AMD_EARLY_PARAMS *EarlyParams)
+{
+ AGESA_STATUS status;
+
+ soc_customize_init_early(EarlyParams);
+ OemCustomizeInitEarly(EarlyParams);
+
+ timestamp_add_now(TS_AGESA_INIT_EARLY_START);
+ status = amd_dispatch(EarlyParams);
+ timestamp_add_now(TS_AGESA_INIT_EARLY_DONE);
+
+ return status;
+}
+
+static void print_init_post_settings(AMD_POST_PARAMS *parms)
+{
+ u64 syslimit, bottomio, uma_size, uma_start;
+ const char *mode;
+
+ switch (parms->MemConfig.UmaMode) {
+ case UMA_AUTO:
+ mode = "UMA_AUTO";
+ break;
+ case UMA_SPECIFIED:
+ mode = "UMA_SPECIFIED";
+ break;
+ case UMA_NONE:
+ mode = "UMA_NONE";
+ break;
+ default:
+ mode = "unknown!";
+ break;
+ }
+
+ syslimit = (u64)(parms->MemConfig.SysLimit + 1) * 64 * KiB - 1;
+ bottomio = (u64)parms->MemConfig.BottomIo * 64 * KiB;
+
+ uma_size = (u64)parms->MemConfig.UmaSize * 64 * KiB;
+ uma_start = (u64)parms->MemConfig.UmaBase * 64 * KiB;
+
+ printk(BIOS_SPEW, "AGESA set: umamode %s\n", mode);
+ printk(BIOS_SPEW, " : syslimit 0x%llx, bottomio 0x%08llx\n",
+ syslimit, bottomio);
+ printk(BIOS_SPEW, " : uma size %lluMB, uma start 0x%08llx\n",
+ uma_size / MiB, uma_start);
+}
+
+static AGESA_STATUS amd_init_post(AMD_POST_PARAMS *PostParams)
+{
+ AGESA_STATUS status;
+
+ PostParams->MemConfig.UmaMode = CONFIG(GFXUMA) ? UMA_AUTO : UMA_NONE;
+ PostParams->MemConfig.UmaSize = 0;
+ PostParams->MemConfig.BottomIo = (uint16_t)
+ (CONFIG_BOTTOMIO_POSITION >> 24);
+
+ SetMemParams(PostParams);
+ OemPostParams(PostParams);
+ printk(BIOS_SPEW, "DRAM clear on reset: %s\n",
+ (PostParams->MemConfig.EnableMemClr == FALSE) ? "Keep" :
+ (PostParams->MemConfig.EnableMemClr == TRUE) ? "Clear" :
+ "unknown"
+ );
+
+ timestamp_add_now(TS_AGESA_INIT_POST_START);
+ status = amd_dispatch(PostParams);
+ timestamp_add_now(TS_AGESA_INIT_POST_DONE);
+
+ /*
+ * AGESA passes back the base and size of UMA. This is the only
+ * opportunity to get and save these settings to be used in resource
+ * allocation. We also need to allocate the top of low memory.
+ * If UMA is below 4GiB, UMA base is the top of low memory, otherwise
+ * Sub4GCachetop is the top of low memory.
+ * With UMA_NONE we see UmaBase==0.
+ */
+ uintptr_t top;
+ if (PostParams->MemConfig.UmaBase &&
+ (PostParams->MemConfig.UmaBase < ((4ull * GiB) >> 16)))
+ top = PostParams->MemConfig.UmaBase << 16;
+ else
+ top = PostParams->MemConfig.Sub4GCacheTop;
+ backup_top_of_low_cacheable(top);
+
+ save_uma_size(PostParams->MemConfig.UmaSize * 64 * KiB);
+ save_uma_base((u64)PostParams->MemConfig.UmaBase * 64 * KiB);
+
+ print_init_post_settings(PostParams);
+
+ return status;
+}
+
+static AGESA_STATUS amd_init_env(AMD_ENV_PARAMS *EnvParams)
+{
+ AGESA_STATUS status;
+
+ SetFchEnvParams(&EnvParams->FchInterface);
+ SetNbEnvParams(&EnvParams->GnbEnvConfiguration);
+
+ timestamp_add_now(TS_AGESA_INIT_ENV_START);
+ status = amd_dispatch(EnvParams);
+ timestamp_add_now(TS_AGESA_INIT_ENV_DONE);
+
+ return status;
+}
+
+void *agesawrapper_getlateinitptr(int pick)
+{
+ switch (pick) {
+ case PICK_DMI:
+ return DmiTable;
+ case PICK_PSTATE:
+ return AcpiPstate;
+ case PICK_SRAT:
+ return AcpiSrat;
+ case PICK_SLIT:
+ return AcpiSlit;
+ case PICK_WHEA_MCE:
+ return AcpiWheaMce;
+ case PICK_WHEA_CMC:
+ return AcpiWheaCmc;
+ case PICK_ALIB:
+ return AcpiAlib;
+ case PICK_IVRS:
+ return AcpiIvrs;
+ case PICK_CRAT:
+ return AcpiCrat;
+ default:
+ return NULL;
+ }
+}
+
+static AGESA_STATUS amd_init_mid(AMD_MID_PARAMS *MidParams)
+{
+ AGESA_STATUS status;
+
+ /* Enable MMIO on AMD CPU Address Map Controller */
+ amd_initcpuio();
+
+ SetFchMidParams(&MidParams->FchInterface);
+ SetNbMidParams(&MidParams->GnbMidConfiguration);
+
+ timestamp_add_now(TS_AGESA_INIT_MID_START);
+ status = amd_dispatch(MidParams);
+ timestamp_add_now(TS_AGESA_INIT_MID_DONE);
+
+ return status;
+}
+
+static AGESA_STATUS amd_init_late(AMD_LATE_PARAMS *LateParams)
+{
+ AGESA_STATUS Status;
+
+ const struct device *dev = pcidev_path_on_root(IOMMU_DEVFN);
+ if (dev && dev->enabled) {
+ LateParams->GnbLateConfiguration.GnbIoapicId = GNB_IOAPIC_ID;
+ LateParams->GnbLateConfiguration.FchIoapicId = FCH_IOAPIC_ID;
+ }
+
+ timestamp_add_now(TS_AGESA_INIT_LATE_START);
+ Status = amd_dispatch(LateParams);
+ timestamp_add_now(TS_AGESA_INIT_LATE_DONE);
+
+ DmiTable = LateParams->DmiTable;
+ AcpiPstate = LateParams->AcpiPState;
+
+ AcpiWheaMce = LateParams->AcpiWheaMce;
+ AcpiWheaCmc = LateParams->AcpiWheaCmc;
+ AcpiAlib = LateParams->AcpiAlib;
+ AcpiIvrs = LateParams->AcpiIvrs;
+ AcpiCrat = LateParams->AcpiCrat;
+
+ printk(BIOS_DEBUG, "DmiTable:%p, AcpiPstatein: %p, AcpiSrat:%p,"
+ "AcpiSlit:%p, Mce:%p, Cmc:%p,"
+ "Alib:%p, AcpiIvrs:%p in %s\n",
+ DmiTable, AcpiPstate, AcpiSrat,
+ AcpiSlit, AcpiWheaMce, AcpiWheaCmc,
+ AcpiAlib, AcpiIvrs, __func__);
+
+ return Status;
+}
+
+static AGESA_STATUS amd_init_rtb(AMD_RTB_PARAMS *RtbParams)
+{
+ AGESA_STATUS Status;
+
+ timestamp_add_now(TS_AGESA_INIT_RTB_START);
+ Status = amd_dispatch(RtbParams);
+ timestamp_add_now(TS_AGESA_INIT_RTB_DONE);
+
+ if (Status != AGESA_SUCCESS)
+ return Status;
+
+ if (OemS3Save(&RtbParams->S3DataBlock) != AGESA_SUCCESS)
+ printk(BIOS_ERR, "S3 data not saved, resuming impossible\n");
+
+ return Status;
+}
+
+static AGESA_STATUS amd_init_resume(AMD_RESUME_PARAMS *InitResumeParams)
+{
+ AGESA_STATUS status;
+
+ OemInitResume(&InitResumeParams->S3DataBlock);
+
+ timestamp_add_now(TS_AGESA_INIT_RESUME_START);
+ status = amd_dispatch(InitResumeParams);
+ timestamp_add_now(TS_AGESA_INIT_RESUME_DONE);
+
+ return status;
+}
+
+static AGESA_STATUS amd_s3late_restore(AMD_S3LATE_PARAMS *S3LateParams)
+{
+ AGESA_STATUS Status;
+
+ amd_initcpuio();
+
+ OemS3LateRestore(&S3LateParams->S3DataBlock);
+
+ timestamp_add_now(TS_AGESA_S3_LATE_START);
+ Status = amd_dispatch(S3LateParams);
+ timestamp_add_now(TS_AGESA_S3_LATE_DONE);
+
+ return Status;
+}
+
+static AGESA_STATUS amd_s3final_restore(AMD_S3FINAL_PARAMS *S3FinalParams)
+{
+ AGESA_STATUS Status;
+
+ OemS3LateRestore(&S3FinalParams->S3DataBlock);
+
+ timestamp_add_now(TS_AGESA_S3_FINAL_START);
+ Status = amd_dispatch(S3FinalParams);
+ timestamp_add_now(TS_AGESA_S3_FINAL_DONE);
+
+ return Status;
+}
+
+static AGESA_STATUS romstage_dispatch(AMD_CONFIG_PARAMS *StdHeader)
+{
+ void *Params = StdHeader;
+
+ switch (StdHeader->Func) {
+ case AMD_INIT_RESET:
+ return amd_init_reset(Params);
+ case AMD_INIT_EARLY:
+ return amd_init_early(Params);
+ case AMD_INIT_POST:
+ return amd_init_post(Params);
+ case AMD_INIT_RESUME:
+ return amd_init_resume(Params);
+ default:
+ return AGESA_UNSUPPORTED;
+ }
+}
+
+static AGESA_STATUS ramstage_dispatch(AMD_CONFIG_PARAMS *StdHeader)
+{
+ void *Params = StdHeader;
+
+ switch (StdHeader->Func) {
+ case AMD_INIT_ENV:
+ return amd_init_env(Params);
+ case AMD_INIT_MID:
+ return amd_init_mid(Params);
+ case AMD_INIT_LATE:
+ return amd_init_late(Params);
+ case AMD_INIT_RTB:
+ return amd_init_rtb(Params);
+ case AMD_S3LATE_RESTORE:
+ return amd_s3late_restore(Params);
+ case AMD_S3FINAL_RESTORE:
+ return amd_s3final_restore(Params);
+ default:
+ return AGESA_UNSUPPORTED;
+ }
+
+}
+
+AGESA_STATUS agesa_execute_state(AGESA_STRUCT_NAME func)
+{
+ AGESA_STATUS status = AGESA_UNSUPPORTED;
+ AMD_CONFIG_PARAMS template = {};
+ AMD_CONFIG_PARAMS *StdHeader = &template;
+ AMD_INTERFACE_PARAMS AmdParamStruct;
+ AMD_INTERFACE_PARAMS *aip = &AmdParamStruct;
+ union {
+ AMD_RESET_PARAMS ResetParams;
+ AMD_S3LATE_PARAMS S3LateParams;
+ AMD_S3FINAL_PARAMS S3FinalParams;
+ } sp;
+
+ if ((func == AMD_INIT_RESET) || (func == AMD_S3LATE_RESTORE) ||
+ (func == AMD_S3FINAL_RESTORE)) {
+ memset(&sp, 0, sizeof(sp));
+ amd_create_struct(aip, func, &sp, sizeof(sp));
+ } else {
+ amd_create_struct(aip, func, NULL, 0);
+ }
+
+ StdHeader = aip->NewStructPtr;
+ StdHeader->Func = func;
+
+ if (ENV_ROMSTAGE)
+ status = romstage_dispatch(StdHeader);
+ if (ENV_RAMSTAGE)
+ status = ramstage_dispatch(StdHeader);
+
+ amd_release_struct(aip);
+ return status;
+}
diff --git a/src/soc/amd/common/pi/amd_late_init.c b/src/soc/amd/common/pi/amd_late_init.c
new file mode 100644
index 0000000000..25aaea37bf
--- /dev/null
+++ b/src/soc/amd/common/pi/amd_late_init.c
@@ -0,0 +1,177 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <acpi/acpi.h>
+#include <bootstate.h>
+#include <cbmem.h>
+#include <console/console.h>
+#include <device/device.h>
+#include <device/pci_def.h>
+#include <dimm_info_util.h>
+#include <memory_info.h>
+#include <lib.h>
+#include <string.h>
+
+#include <amdblocks/agesawrapper.h>
+#include <amdblocks/agesawrapper_call.h>
+
+/**
+ * Populate dimm_info using AGESA TYPE17_DMI_INFO.
+ */
+static void transfer_memory_info(TYPE17_DMI_INFO *dmi17,
+ struct dimm_info *dimm)
+{
+ hexstrtobin(dmi17->SerialNumber, dimm->serial, sizeof(dimm->serial));
+
+ dimm->dimm_size =
+ smbios_memory_size_to_mib(dmi17->MemorySize, dmi17->ExtSize);
+
+ dimm->ddr_type = dmi17->MemoryType;
+
+ /*
+ * dimm_info uses ddr_frequency for setting both config speed and max
+ * speed. Lets use config speed so we don't get the false impression
+ * that the RAM is running faster than it actually is.
+ */
+ dimm->ddr_frequency = dmi17->ConfigSpeed;
+
+ dimm->rank_per_dimm = dmi17->Attributes;
+
+ dimm->mod_type = smbios_form_factor_to_spd_mod_type(dmi17->FormFactor);
+
+ dimm->bus_width =
+ smbios_bus_width_to_spd_width(dmi17->TotalWidth, dmi17->DataWidth);
+
+ dimm->mod_id = dmi17->ManufacturerIdCode;
+
+ dimm->bank_locator = 0;
+
+ strncpy((char *)dimm->module_part_number, dmi17->PartNumber,
+ sizeof(dimm->module_part_number) - 1);
+}
+
+static void print_dimm_info(const struct dimm_info *dimm)
+{
+ printk(RAM_SPEW,
+ "CBMEM_ID_MEMINFO:\n"
+ " dimm_size: %u\n"
+ " ddr_type: 0x%hx\n"
+ " ddr_frequency: %hu\n"
+ " rank_per_dimm: %hhu\n"
+ " channel_num: %hhu\n"
+ " dimm_num: %hhu\n"
+ " bank_locator: %hhu\n"
+ " mod_id: %hu\n"
+ " mod_type: 0x%hhx\n"
+ " bus_width: %hhu\n"
+ " serial: %02hhx%02hhx%02hhx%02hhx\n"
+ " module_part_number(%zu): %s\n",
+ dimm->dimm_size,
+ dimm->ddr_type,
+ dimm->ddr_frequency,
+ dimm->rank_per_dimm,
+ dimm->channel_num,
+ dimm->dimm_num,
+ dimm->bank_locator,
+ dimm->mod_id,
+ dimm->mod_type,
+ dimm->bus_width,
+ dimm->serial[0],
+ dimm->serial[1],
+ dimm->serial[2],
+ dimm->serial[3],
+ strlen((char *) dimm->module_part_number),
+ (char *) dimm->module_part_number
+ );
+}
+
+static void print_dmi_info(const TYPE17_DMI_INFO *dmi17)
+{
+ printk(RAM_SPEW,
+ "AGESA TYPE 17 DMI INFO:\n"
+ " Handle: %hu\n"
+ " TotalWidth: %hu\n"
+ " DataWidth: %hu\n"
+ " MemorySize: %hu\n"
+ " DeviceSet: %hhu\n"
+ " Speed: %hu\n"
+ " ManufacturerIdCode: %llu\n"
+ " Attributes: %hhu\n"
+ " ExtSize: %u\n"
+ " ConfigSpeed: %hu\n"
+ " MemoryType: 0x%x\n"
+ " FormFactor: 0x%x\n"
+ " DeviceLocator: %8s\n"
+ " BankLocator: %10s\n"
+ " SerialNumber(%zu): %9s\n"
+ " PartNumber(%zu): %19s\n",
+ dmi17->Handle,
+ dmi17->TotalWidth,
+ dmi17->DataWidth,
+ dmi17->MemorySize,
+ dmi17->DeviceSet,
+ dmi17->Speed,
+ dmi17->ManufacturerIdCode,
+ dmi17->Attributes,
+ dmi17->ExtSize,
+ dmi17->ConfigSpeed,
+ dmi17->MemoryType,
+ dmi17->FormFactor,
+ dmi17->DeviceLocator,
+ dmi17->BankLocator,
+ strlen((char *) dmi17->SerialNumber),
+ dmi17->SerialNumber,
+ strlen((char *) dmi17->PartNumber),
+ dmi17->PartNumber
+ );
+}
+
+static void prepare_dmi_17(void *unused)
+{
+ DMI_INFO *DmiTable;
+ TYPE17_DMI_INFO *address;
+ struct memory_info *mem_info;
+ struct dimm_info *dimm;
+ int i, j, dimm_cnt = 0;
+
+ mem_info = cbmem_add(CBMEM_ID_MEMINFO, sizeof(struct memory_info));
+ if (!mem_info) {
+ printk(BIOS_NOTICE, "Failed to add memory info to CBMEM.\n");
+ return;
+ }
+ memset(mem_info, 0, sizeof(struct memory_info));
+
+ DmiTable = agesawrapper_getlateinitptr(PICK_DMI);
+ for (i = 0; i < MAX_CHANNELS_PER_SOCKET; i++) {
+ for (j = 0; j < MAX_DIMMS_PER_CHANNEL; j++) {
+ address = &DmiTable->T17[0][i][j];
+ if (address->Handle > 0) {
+ dimm = &mem_info->dimm[dimm_cnt];
+ dimm->channel_num = i;
+ dimm->dimm_num = j;
+ transfer_memory_info(address, dimm);
+ print_dmi_info(address);
+ print_dimm_info(dimm);
+ dimm_cnt++;
+ }
+ }
+ }
+ mem_info->dimm_cnt = dimm_cnt;
+}
+
+BOOT_STATE_INIT_ENTRY(BS_WRITE_TABLES, BS_ON_ENTRY, prepare_dmi_17, NULL);
+
+static void agesawrapper_post_device(void *unused)
+{
+ if (acpi_is_wakeup_s3())
+ return;
+
+ do_agesawrapper(AMD_INIT_LATE, "amdinitlate");
+
+ if (!acpi_s3_resume_allowed())
+ return;
+
+ do_agesawrapper(AMD_INIT_RTB, "amdinitrtb");
+}
+
+BOOT_STATE_INIT_ENTRY(BS_POST_DEVICE, BS_ON_EXIT, agesawrapper_post_device,
+ NULL);
diff --git a/src/soc/amd/common/pi/amd_resume_final.c b/src/soc/amd/common/pi/amd_resume_final.c
new file mode 100644
index 0000000000..380ffc8b1a
--- /dev/null
+++ b/src/soc/amd/common/pi/amd_resume_final.c
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <bootstate.h>
+#include <amdblocks/agesawrapper_call.h>
+
+static void agesawrapper_s3finalrestore(void *unused)
+{
+ do_agesawrapper(AMD_S3FINAL_RESTORE, "amds3finalrestore");
+}
+
+BOOT_STATE_INIT_ENTRY(BS_OS_RESUME, BS_ON_ENTRY,
+ agesawrapper_s3finalrestore, NULL);
diff --git a/src/soc/amd/common/pi/def_callouts.c b/src/soc/amd/common/pi/def_callouts.c
new file mode 100644
index 0000000000..2ee7f46056
--- /dev/null
+++ b/src/soc/amd/common/pi/def_callouts.c
@@ -0,0 +1,245 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <cbfs.h>
+#include <console/console.h>
+#include <cpu/x86/lapic.h>
+#include <cpu/x86/mp.h>
+#include <timer.h>
+#include <amdblocks/BiosCallOuts.h>
+#include <amdblocks/agesawrapper.h>
+#include <amdblocks/agesawrapper_call.h>
+#include <amdblocks/reset.h>
+#include <soc/southbridge.h>
+
+#if ENV_BOOTBLOCK
+const BIOS_CALLOUT_STRUCT BiosCallouts[] = {
+ { AGESA_DO_RESET, agesa_Reset },
+ { AGESA_FCH_OEM_CALLOUT, agesa_fch_initreset },
+ { AGESA_HALT_THIS_AP, agesa_HaltThisAp },
+ { AGESA_HEAP_REBASE, agesa_HeapRebase },
+ { AGESA_GNB_PCIE_SLOT_RESET, agesa_PcieSlotResetControl }
+};
+#else
+const BIOS_CALLOUT_STRUCT BiosCallouts[] = {
+ /* Required callouts */
+#if ENV_ROMSTAGE
+ { AGESA_HALT_THIS_AP, agesa_HaltThisAp },
+#endif
+ { AGESA_ALLOCATE_BUFFER, agesa_AllocateBuffer },
+ { AGESA_DEALLOCATE_BUFFER, agesa_DeallocateBuffer },
+ { AGESA_DO_RESET, agesa_Reset },
+ { AGESA_LOCATE_BUFFER, agesa_LocateBuffer },
+ { AGESA_READ_SPD, agesa_ReadSpd },
+ { AGESA_GNB_PCIE_SLOT_RESET, agesa_PcieSlotResetControl },
+ { AGESA_GET_TEMP_HEAP_BASE, agesa_GetTempHeapBase },
+ { AGESA_HEAP_REBASE, agesa_HeapRebase },
+#if ENV_RAMSTAGE
+ { AGESA_RUNFUNC_ONAP, agesa_RunFuncOnAp },
+ { AGESA_RUNFUNC_ON_ALL_APS, agesa_RunFcnOnAllAps },
+ { AGESA_WAIT_FOR_ALL_APS, agesa_WaitForAllApsFinished },
+ { AGESA_IDLE_AN_AP, agesa_IdleAnAp },
+#endif /* ENV_RAMSTAGE */
+
+ /* Optional callouts */
+ { AGESA_GET_IDS_INIT_DATA, agesa_EmptyIdsInitData },
+ //AgesaHeapRebase - Hook ID?
+ { AGESA_HOOKBEFORE_DRAM_INIT, agesa_NoopUnsupported },
+ { AGESA_HOOKBEFORE_DQS_TRAINING, agesa_NoopUnsupported },
+ { AGESA_EXTERNAL_2D_TRAIN_VREF_CHANGE, agesa_NoopUnsupported },
+ { AGESA_HOOKBEFORE_EXIT_SELF_REF, agesa_NoopUnsupported },
+ { AGESA_GNB_GFX_GET_VBIOS_IMAGE, agesa_GfxGetVbiosImage },
+ { AGESA_FCH_OEM_CALLOUT, agesa_fch_initenv },
+ { AGESA_EXTERNAL_VOLTAGE_ADJUST, agesa_NoopUnsupported },
+ { AGESA_GNB_PCIE_CLK_REQ, agesa_NoopUnsupported },
+
+ /* Deprecated */
+ { AGESA_HOOKBEFORE_DRAM_INIT_RECOVERY, agesa_NoopUnsupported},
+ { AGESA_READ_SPD_RECOVERY, agesa_NoopUnsupported },
+
+};
+#endif
+
+const int BiosCalloutsLen = ARRAY_SIZE(BiosCallouts);
+
+AGESA_STATUS GetBiosCallout(uint32_t Func, uintptr_t Data, void *ConfigPtr)
+{
+ uintptr_t i;
+
+ for (i = 0 ; i < BiosCalloutsLen ; i++) {
+ if (BiosCallouts[i].CalloutName == Func)
+ break;
+ }
+
+ if (i >= BiosCalloutsLen) {
+ printk(BIOS_ERR, "ERROR: AGESA Callout Not Supported: 0x%x\n",
+ (u32)Func);
+ return AGESA_UNSUPPORTED;
+ }
+
+ return BiosCallouts[i].CalloutPtr(Func, Data, ConfigPtr);
+}
+
+AGESA_STATUS agesa_NoopUnsupported(uint32_t Func, uintptr_t Data,
+ void *ConfigPtr)
+{
+ return AGESA_UNSUPPORTED;
+}
+
+AGESA_STATUS agesa_NoopSuccess(uint32_t Func, uintptr_t Data, void *ConfigPtr)
+{
+ return AGESA_SUCCESS;
+}
+
+AGESA_STATUS agesa_EmptyIdsInitData(uint32_t Func, uintptr_t Data,
+ void *ConfigPtr)
+{
+ IDS_NV_ITEM *IdsPtr = ((IDS_CALLOUT_STRUCT *) ConfigPtr)->IdsNvPtr;
+ if (Data == IDS_CALLOUT_INIT)
+ IdsPtr[0].IdsNvValue = IdsPtr[0].IdsNvId = 0xffff;
+ return AGESA_SUCCESS;
+}
+
+AGESA_STATUS agesa_Reset(uint32_t Func, uintptr_t Data, void *ConfigPtr)
+{
+ AGESA_STATUS Status;
+ uintptr_t ResetType;
+
+ ResetType = Data;
+
+ /*
+ * This should perform the RESET based upon the ResetType, but coreboot
+ * doesn't have a reset manager to handle a WHENEVER case. Do all
+ * resets immediately.
+ */
+ switch (ResetType) {
+ case WARM_RESET_WHENEVER:
+ case WARM_RESET_IMMEDIATELY:
+ warm_reset();
+ break;
+
+ case COLD_RESET_WHENEVER:
+ case COLD_RESET_IMMEDIATELY:
+ cold_reset();
+ break;
+
+ default:
+ break;
+ }
+
+ Status = 0;
+ return Status;
+}
+
+AGESA_STATUS agesa_GfxGetVbiosImage(uint32_t Func, uintptr_t FchData,
+ void *ConfigPrt)
+{
+ GFX_VBIOS_IMAGE_INFO *pVbiosImageInfo;
+
+ pVbiosImageInfo = (GFX_VBIOS_IMAGE_INFO *)ConfigPrt;
+ pVbiosImageInfo->ImagePtr = cbfs_map(
+ "pci"CONFIG_VGA_BIOS_ID".rom", NULL);
+ printk(BIOS_DEBUG, "%s: IMGptr=%p\n", __func__,
+ pVbiosImageInfo->ImagePtr);
+ return pVbiosImageInfo->ImagePtr ? AGESA_SUCCESS : AGESA_WARNING;
+}
+
+AGESA_STATUS __weak platform_PcieSlotResetControl(uint32_t Func,
+ uintptr_t Data, void *ConfigPtr)
+{
+ printk(BIOS_WARNING, "Warning - AGESA callout: %s not supported\n",
+ __func__);
+ return AGESA_UNSUPPORTED;
+}
+
+AGESA_STATUS agesa_PcieSlotResetControl(uint32_t Func, uintptr_t Data,
+ void *ConfigPtr)
+{
+ return platform_PcieSlotResetControl(Func, Data, ConfigPtr);
+}
+
+/*
+ * Application Processor callouts:
+ * agesa_RunFuncOnAp() and agesa_RunFcnOnAllAps() are called after main memory
+ * has been initialized and coreboot has taken control of AP task dispatching.
+ * These functions execute callout_ap_entry() on each AP, which calls the
+ * AmdLateRunApTask() entry point if it is a targeted AP.
+ */
+
+/*
+ * Global data for APs.
+ * Passed from the AGESA_Callout for the agesawrapper_amdlaterunaptask.
+ */
+static struct agesa_data {
+ uint32_t Func;
+ uintptr_t Data;
+ AP_EXE_PARAMS *ConfigPtr;
+} agesadata;
+
+/*
+ * BSP deploys APs to callout_ap_entry(), which calls
+ * agesawrapper_amdlaterunaptask with the agesadata.
+ */
+static void callout_ap_entry(void *unused)
+{
+ AGESA_STATUS Status = AGESA_UNSUPPORTED;
+
+ printk(BIOS_DEBUG, "%s Func: 0x%x, Data: 0x%lx, Ptr: %p\n",
+ __func__, agesadata.Func, agesadata.Data, agesadata.ConfigPtr);
+
+ /* Check if this AP should run the function */
+ if (!((agesadata.Func == AGESA_RUNFUNC_ONAP) &&
+ (agesadata.Data == lapicid())))
+ return;
+
+ Status = amd_late_run_ap_task(agesadata.ConfigPtr);
+
+ if (Status)
+ printk(BIOS_DEBUG, "There was a problem with %x returned %s\n",
+ lapicid(), decodeAGESA_STATUS(Status));
+}
+
+AGESA_STATUS agesa_RunFuncOnAp(uint32_t Func, uintptr_t Data, void *ConfigPtr)
+{
+ printk(BIOS_DEBUG, "%s\n", __func__);
+
+ agesadata.Func = Func;
+ agesadata.Data = Data;
+ agesadata.ConfigPtr = ConfigPtr;
+ if (mp_run_on_aps(callout_ap_entry, NULL, MP_RUN_ON_ALL_CPUS, 100 * USECS_PER_MSEC))
+ return AGESA_ERROR;
+
+ return AGESA_SUCCESS;
+}
+
+AGESA_STATUS agesa_RunFcnOnAllAps(uint32_t Func, uintptr_t Data,
+ void *ConfigPtr)
+{
+ printk(BIOS_DEBUG, "%s\n", __func__);
+
+ agesadata.Func = Func;
+ agesadata.Data = Data;
+ agesadata.ConfigPtr = ConfigPtr;
+ if (mp_run_on_aps(callout_ap_entry, NULL, MP_RUN_ON_ALL_CPUS, 100 * USECS_PER_MSEC))
+ return AGESA_ERROR;
+
+ return AGESA_SUCCESS;
+}
+
+AGESA_STATUS agesa_WaitForAllApsFinished(uint32_t Func, uintptr_t Data,
+ void *ConfigPtr)
+{
+ printk(BIOS_WARNING, "Warning - AGESA callout: %s not supported\n",
+ __func__);
+ AGESA_STATUS Status = AGESA_UNSUPPORTED;
+
+ return Status;
+}
+
+AGESA_STATUS agesa_IdleAnAp(uint32_t Func, uintptr_t Data, void *ConfigPtr)
+{
+ printk(BIOS_WARNING, "Warning - AGESA callout: %s no supported\n",
+ __func__);
+ AGESA_STATUS Status = AGESA_UNSUPPORTED;
+
+ return Status;
+}
diff --git a/src/soc/amd/common/pi/heapmanager.c b/src/soc/amd/common/pi/heapmanager.c
new file mode 100644
index 0000000000..699bb53431
--- /dev/null
+++ b/src/soc/amd/common/pi/heapmanager.c
@@ -0,0 +1,440 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <amdblocks/agesawrapper.h>
+#include <amdblocks/BiosCallOuts.h>
+#include <cbmem.h>
+#include <string.h>
+
+static void *agesa_heap_base(void)
+{
+ return cbmem_add(CBMEM_ID_RESUME_SCRATCH, BIOS_HEAP_SIZE);
+}
+
+static void EmptyHeap(int unused)
+{
+ void *BiosManagerPtr = agesa_heap_base();
+ memset(BiosManagerPtr, 0, BIOS_HEAP_SIZE);
+}
+
+/*
+ * Name agesa_GetTempHeapBase
+ * Brief description Get the location for TempRam, the target location in
+ * memory where AmdInitPost copies the heap prior to CAR
+ * teardown. AmdInitEnv calls this function after
+ * teardown for the source address when relocation the
+ * heap to its final location.
+ * Input parameters
+ * Func Unused
+ * Data Unused
+ * ConfigPtr Pointer to type AGESA_TEMP_HEAP_BASE_PARAMS
+ * Output parameters
+ * Status Indicates whether TempHeapAddress was successfully
+ * set.
+ */
+AGESA_STATUS agesa_GetTempHeapBase(uint32_t Func, uintptr_t Data,
+ void *ConfigPtr)
+{
+ AGESA_TEMP_HEAP_BASE_PARAMS *pTempHeapBase;
+
+ pTempHeapBase = (AGESA_TEMP_HEAP_BASE_PARAMS *)ConfigPtr;
+ pTempHeapBase->TempHeapAddress = CONFIG_PI_AGESA_TEMP_RAM_BASE;
+
+ return AGESA_SUCCESS;
+}
+
+/*
+ * Name agesa_HeapRebase
+ * Brief description AGESA may use internal hardcoded locations for its
+ * heap. Modern implementations allow the base to be
+ * overridden by calling agesa_HeapRebase.
+ * Input parameters
+ * Func Unused
+ * Data Unused
+ * ConfigPtr Pointer to type AGESA_REBASE_PARAMS
+ * Output parameters
+ * Status Indicates whether HeapAddress was successfully
+ * set.
+ */
+AGESA_STATUS agesa_HeapRebase(uint32_t Func, uintptr_t Data, void *ConfigPtr)
+{
+ AGESA_REBASE_PARAMS *Rebase;
+
+ Rebase = (AGESA_REBASE_PARAMS *)ConfigPtr;
+ Rebase->HeapAddress = (uintptr_t)agesa_heap_base();
+ if (!Rebase->HeapAddress)
+ Rebase->HeapAddress = CONFIG_PI_AGESA_CAR_HEAP_BASE;
+
+ return AGESA_SUCCESS;
+}
+
+/*
+ * Name FindAllocatedNode
+ * Brief description Find an allocated node that matches the handle.
+ * Input parameter The desired handle.
+ * Output parameters
+ * pointer Here is returned either the found node or the last
+ * allocated node if the handle is not found. This is
+ * intentional, as the field NextNode of this node will
+ * have to be filled with the offset of the node being
+ * created in procedure agesa_AllocateBuffer().
+ * Status Indicates if the node was or was not found.
+ */
+static AGESA_STATUS FindAllocatedNode(uint32_t handle,
+ BIOS_BUFFER_NODE **last_allocd_or_match)
+{
+ uint32_t AllocNodeOffset;
+ uint8_t *BiosHeapBaseAddr;
+ BIOS_BUFFER_NODE *AllocNodePtr;
+ BIOS_HEAP_MANAGER *BiosHeapBasePtr;
+ AGESA_STATUS Status = AGESA_SUCCESS;
+
+ BiosHeapBaseAddr = agesa_heap_base();
+ BiosHeapBasePtr = (BIOS_HEAP_MANAGER *)BiosHeapBaseAddr;
+
+ AllocNodeOffset = BiosHeapBasePtr->StartOfAllocatedNodes;
+ AllocNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr + AllocNodeOffset);
+
+ while (handle != AllocNodePtr->BufferHandle) {
+ if (AllocNodePtr->NextNodeOffset == 0) {
+ Status = AGESA_BOUNDS_CHK;
+ break;
+ }
+ AllocNodeOffset = AllocNodePtr->NextNodeOffset;
+ AllocNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr +
+ AllocNodeOffset);
+ }
+ *last_allocd_or_match = AllocNodePtr;
+ return Status;
+}
+
+/*
+ * Name ConcatenateNodes
+ * Brief description Concatenates two adjacent nodes into a single node,
+ * this procedure is used by agesa_DeallocateBuffer().
+ * Input parameters
+ * FirstNodePtr This node is in the front, its header will be
+ * maintained.
+ * SecondNodePtr This node is in the back, its header will be cleared.
+ */
+static void ConcatenateNodes(BIOS_BUFFER_NODE *FirstNodePtr,
+ BIOS_BUFFER_NODE *SecondNodePtr)
+{
+ FirstNodePtr->BufferSize += SecondNodePtr->BufferSize +
+ sizeof(BIOS_BUFFER_NODE);
+ FirstNodePtr->NextNodeOffset = SecondNodePtr->NextNodeOffset;
+
+ /* Zero out the SecondNode header */
+ memset(SecondNodePtr, 0, sizeof(BIOS_BUFFER_NODE));
+}
+
+ROMSTAGE_CBMEM_INIT_HOOK(EmptyHeap)
+
+AGESA_STATUS agesa_AllocateBuffer(uint32_t Func, uintptr_t Data,
+ void *ConfigPtr)
+{
+ /*
+ * Size variables explanation:
+ * FreedNodeSize - the size of the buffer node being examined,
+ * will be copied to BestFitNodeSize if the node
+ * is selected as a possible best fit.
+ * BestFitNodeSize - the size qf the buffer of the node currently
+ * considered the best fit.
+ * MinimumSize - the requested size + sizeof(BIOS_BUFFER_NODE).
+ * Its the minimum size for the buffer to be broken
+ * down into 2 nodes, once a node is selected as
+ * the best fit.
+ */
+ uint32_t AvailableHeapSize;
+ uint8_t *BiosHeapBaseAddr;
+ uint32_t CurrNodeOffset;
+ uint32_t PrevNodeOffset;
+ uint32_t FreedNodeOffset;
+ uint32_t FreedNodeSize;
+ uint32_t BestFitNodeOffset;
+ uint32_t BestFitNodeSize;
+ uint32_t BestFitPrevNodeOffset;
+ uint32_t NextFreeOffset;
+ uint32_t MinimumSize;
+ BIOS_BUFFER_NODE *CurrNodePtr;
+ BIOS_BUFFER_NODE *FreedNodePtr;
+ BIOS_BUFFER_NODE *BestFitNodePtr;
+ BIOS_BUFFER_NODE *BestFitPrevNodePtr;
+ BIOS_BUFFER_NODE *NextFreePtr;
+ BIOS_HEAP_MANAGER *BiosHeapBasePtr;
+ AGESA_BUFFER_PARAMS *AllocParams;
+ AGESA_STATUS Status;
+
+ AllocParams = ((AGESA_BUFFER_PARAMS *)ConfigPtr);
+ AllocParams->BufferPointer = NULL;
+ MinimumSize = AllocParams->BufferLength + sizeof(BIOS_BUFFER_NODE);
+
+ AvailableHeapSize = BIOS_HEAP_SIZE - sizeof(BIOS_HEAP_MANAGER);
+ BestFitNodeSize = AvailableHeapSize; /* init with largest possible */
+ BiosHeapBaseAddr = agesa_heap_base();
+ BiosHeapBasePtr = (BIOS_HEAP_MANAGER *)BiosHeapBaseAddr;
+
+ if (BiosHeapBasePtr->StartOfAllocatedNodes == 0) {
+ /* First allocation */
+ CurrNodeOffset = sizeof(BIOS_HEAP_MANAGER);
+ CurrNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr
+ + CurrNodeOffset);
+ CurrNodePtr->BufferHandle = AllocParams->BufferHandle;
+ CurrNodePtr->BufferSize = AllocParams->BufferLength;
+ CurrNodePtr->NextNodeOffset = 0;
+ AllocParams->BufferPointer = (uint8_t *)CurrNodePtr
+ + sizeof(BIOS_BUFFER_NODE);
+
+ /* Update the remaining free space */
+ FreedNodeOffset = CurrNodeOffset + CurrNodePtr->BufferSize
+ + sizeof(BIOS_BUFFER_NODE);
+ FreedNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr
+ + FreedNodeOffset);
+ FreedNodePtr->BufferSize = AvailableHeapSize
+ - (FreedNodeOffset - CurrNodeOffset)
+ - sizeof(BIOS_BUFFER_NODE);
+ FreedNodePtr->NextNodeOffset = 0;
+
+ /* Update the offsets for Allocated and Freed nodes */
+ BiosHeapBasePtr->StartOfAllocatedNodes = CurrNodeOffset;
+ BiosHeapBasePtr->StartOfFreedNodes = FreedNodeOffset;
+ } else {
+ /*
+ * Find out whether BufferHandle has been allocated on the heap.
+ * If it has, return AGESA_BOUNDS_CHK.
+ */
+ Status = FindAllocatedNode(AllocParams->BufferHandle,
+ &CurrNodePtr);
+ if (Status == AGESA_SUCCESS)
+ return AGESA_BOUNDS_CHK;
+
+ /*
+ * If status ditn't returned AGESA_SUCCESS, CurrNodePtr here
+ * points to the end of the allocated nodes list.
+ */
+
+ /* Find the node that best fits the requested buffer size */
+ FreedNodeOffset = BiosHeapBasePtr->StartOfFreedNodes;
+ PrevNodeOffset = FreedNodeOffset;
+ BestFitNodeOffset = 0;
+ BestFitPrevNodeOffset = 0;
+ while (FreedNodeOffset != 0) {
+ FreedNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr
+ + FreedNodeOffset);
+ FreedNodeSize = FreedNodePtr->BufferSize;
+ if (FreedNodeSize >= MinimumSize) {
+ if (BestFitNodeOffset == 0) {
+ /*
+ * First node that fits the requested
+ * buffer size
+ */
+ BestFitNodeOffset = FreedNodeOffset;
+ BestFitPrevNodeOffset = PrevNodeOffset;
+ BestFitNodeSize = FreedNodeSize;
+ } else {
+ /*
+ * Find out whether current node is a
+ * betterfit than the previous nodes
+ */
+ if (BestFitNodeSize > FreedNodeSize) {
+
+ BestFitNodeOffset =
+ FreedNodeOffset;
+ BestFitPrevNodeOffset =
+ PrevNodeOffset;
+ BestFitNodeSize = FreedNodeSize;
+ }
+ }
+ }
+ PrevNodeOffset = FreedNodeOffset;
+ FreedNodeOffset = FreedNodePtr->NextNodeOffset;
+ } /* end of while loop */
+
+ if (BestFitNodeOffset == 0) {
+ /*
+ * If we could not find a node that fits the requested
+ * buffer size, return AGESA_BOUNDS_CHK.
+ */
+ return AGESA_BOUNDS_CHK;
+ }
+
+ BestFitNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr
+ + BestFitNodeOffset);
+ BestFitPrevNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr +
+ BestFitPrevNodeOffset);
+
+ /*
+ * If BestFitNode is larger than the requested buffer,
+ * fragment the node further
+ */
+ if (BestFitNodePtr->BufferSize > MinimumSize) {
+ NextFreeOffset = BestFitNodeOffset + MinimumSize;
+ NextFreePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr +
+ NextFreeOffset);
+ NextFreePtr->BufferSize = BestFitNodeSize - MinimumSize;
+
+ /* Remove BestFitNode from list of Freed nodes */
+ NextFreePtr->NextNodeOffset =
+ BestFitNodePtr->NextNodeOffset;
+ } else {
+ /*
+ * Otherwise, next free node is NextNodeOffset of
+ * BestFitNode. Remove it from list of Freed nodes.
+ */
+ NextFreeOffset = BestFitNodePtr->NextNodeOffset;
+ }
+
+ /*
+ * If BestFitNode is the first buffer in the list, then
+ * update StartOfFreedNodes to reflect new free node.
+ */
+ if (BestFitNodeOffset == BiosHeapBasePtr->StartOfFreedNodes)
+ BiosHeapBasePtr->StartOfFreedNodes = NextFreeOffset;
+ else
+ BestFitPrevNodePtr->NextNodeOffset = NextFreeOffset;
+
+ /* Add BestFitNode to the list of Allocated nodes */
+ CurrNodePtr->NextNodeOffset = BestFitNodeOffset;
+ BestFitNodePtr->BufferSize = AllocParams->BufferLength;
+ BestFitNodePtr->BufferHandle = AllocParams->BufferHandle;
+ BestFitNodePtr->NextNodeOffset = 0;
+
+ AllocParams->BufferPointer = (uint8_t *)BestFitNodePtr +
+ sizeof(BIOS_BUFFER_NODE);
+ }
+
+ return AGESA_SUCCESS;
+}
+
+AGESA_STATUS agesa_DeallocateBuffer(uint32_t Func, uintptr_t Data,
+ void *ConfigPtr)
+{
+
+ uint8_t *BiosHeapBaseAddr;
+ uint32_t AllocNodeOffset;
+ uint32_t PrevNodeOffset;
+ uint32_t NextNodeOffset;
+ uint32_t FreedNodeOffset;
+ uint32_t EndNodeOffset;
+ BIOS_BUFFER_NODE *AllocNodePtr;
+ BIOS_BUFFER_NODE *PrevNodePtr;
+ BIOS_BUFFER_NODE *FreedNodePtr;
+ BIOS_BUFFER_NODE *NextNodePtr;
+ BIOS_HEAP_MANAGER *BiosHeapBasePtr;
+ AGESA_BUFFER_PARAMS *AllocParams;
+
+ AllocParams = (AGESA_BUFFER_PARAMS *)ConfigPtr;
+
+ BiosHeapBaseAddr = agesa_heap_base();
+ BiosHeapBasePtr = (BIOS_HEAP_MANAGER *)BiosHeapBaseAddr;
+
+ /* Find target node to deallocate in list of allocated nodes.
+ * Return AGESA_BOUNDS_CHK if the BufferHandle is not found.
+ */
+ AllocNodeOffset = BiosHeapBasePtr->StartOfAllocatedNodes;
+ AllocNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr + AllocNodeOffset);
+ PrevNodeOffset = AllocNodeOffset;
+
+ while (AllocNodePtr->BufferHandle != AllocParams->BufferHandle) {
+ if (AllocNodePtr->NextNodeOffset == 0)
+ return AGESA_BOUNDS_CHK;
+ PrevNodeOffset = AllocNodeOffset;
+ AllocNodeOffset = AllocNodePtr->NextNodeOffset;
+ AllocNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr
+ + AllocNodeOffset);
+ }
+
+ /* Remove target node from list of allocated nodes */
+ PrevNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr + PrevNodeOffset);
+ PrevNodePtr->NextNodeOffset = AllocNodePtr->NextNodeOffset;
+
+ /* Zero out the buffer, and clear the BufferHandle */
+ memset((uint8_t *)AllocNodePtr + sizeof(BIOS_BUFFER_NODE), 0,
+ AllocNodePtr->BufferSize);
+ AllocNodePtr->BufferHandle = 0;
+
+ /* Add deallocated node in order to the list of freed nodes */
+ FreedNodeOffset = BiosHeapBasePtr->StartOfFreedNodes;
+ FreedNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr + FreedNodeOffset);
+
+ EndNodeOffset = AllocNodeOffset + AllocNodePtr->BufferSize +
+ sizeof(BIOS_BUFFER_NODE);
+
+ if (AllocNodeOffset < FreedNodeOffset) {
+ /* Add to the start of the freed list */
+ if (EndNodeOffset == FreedNodeOffset) {
+ /* If the freed node is adjacent to the first node in
+ * the list, concatenate both nodes
+ */
+ ConcatenateNodes(AllocNodePtr, FreedNodePtr);
+ } else {
+ /* Otherwise, add freed node to the start of the list
+ * Update NextNodeOffset and BufferSize to include the
+ * size of BIOS_BUFFER_NODE.
+ */
+ AllocNodePtr->NextNodeOffset = FreedNodeOffset;
+ }
+ /* Update StartOfFreedNodes to the new first node */
+ BiosHeapBasePtr->StartOfFreedNodes = AllocNodeOffset;
+ } else {
+ /* Traverse list of freed nodes to find where the deallocated
+ * node should be placed.
+ */
+ NextNodeOffset = FreedNodeOffset;
+ NextNodePtr = FreedNodePtr;
+ while (AllocNodeOffset > NextNodeOffset) {
+ PrevNodeOffset = NextNodeOffset;
+ if (NextNodePtr->NextNodeOffset == 0)
+ break;
+ NextNodeOffset = NextNodePtr->NextNodeOffset;
+ NextNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr
+ + NextNodeOffset);
+ }
+
+ /* If deallocated node is adjacent to the next node,
+ * concatenate both nodes.
+ */
+ if (NextNodeOffset == EndNodeOffset) {
+ NextNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr
+ + NextNodeOffset);
+ ConcatenateNodes(AllocNodePtr, NextNodePtr);
+ } else {
+ /*AllocNodePtr->NextNodeOffset =
+ * FreedNodePtr->NextNodeOffset; */
+ AllocNodePtr->NextNodeOffset = NextNodeOffset;
+ }
+ /*
+ * If deallocated node is adjacent to the previous node,
+ * concatenate both nodes.
+ */
+ PrevNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr
+ + PrevNodeOffset);
+ EndNodeOffset = PrevNodeOffset + PrevNodePtr->BufferSize +
+ sizeof(BIOS_BUFFER_NODE);
+
+ if (AllocNodeOffset == EndNodeOffset)
+ ConcatenateNodes(PrevNodePtr, AllocNodePtr);
+ else
+ PrevNodePtr->NextNodeOffset = AllocNodeOffset;
+ }
+ return AGESA_SUCCESS;
+}
+
+AGESA_STATUS agesa_LocateBuffer(uint32_t Func, uintptr_t Data, void *ConfigPtr)
+{
+ BIOS_BUFFER_NODE *AllocNodePtr;
+ AGESA_BUFFER_PARAMS *AllocParams;
+ AGESA_STATUS Status;
+
+ AllocParams = (AGESA_BUFFER_PARAMS *)ConfigPtr;
+
+ Status = FindAllocatedNode(AllocParams->BufferHandle, &AllocNodePtr);
+
+ if (Status == AGESA_SUCCESS) {
+ AllocParams->BufferPointer = (uint8_t *)((uint8_t *)AllocNodePtr
+ + sizeof(BIOS_BUFFER_NODE));
+ AllocParams->BufferLength = AllocNodePtr->BufferSize;
+ }
+
+ return Status;
+
+}
diff --git a/src/soc/amd/common/pi/image.c b/src/soc/amd/common/pi/image.c
new file mode 100644
index 0000000000..03a2a473a0
--- /dev/null
+++ b/src/soc/amd/common/pi/image.c
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <agesa_headers.h>
+#include <amdblocks/image.h>
+#include <types.h>
+
+/* Check if the image has the desired module. */
+static bool validate_image(void *module_chain, const char module_signature[8])
+{
+ AMD_MODULE_HEADER *mod_ptr = (AMD_MODULE_HEADER *)module_chain;
+ uint64_t signature = *(uint64_t *)module_signature;
+ char *checking_str;
+
+ while ((mod_ptr != NULL) &&
+ (MODULE_SIGNATURE == *(uint32_t *)&mod_ptr->ModuleHeaderSignature)) {
+ checking_str = (char *)&mod_ptr->ModuleIdentifier;
+ if (signature == *(uint64_t *)checking_str)
+ return true;
+ mod_ptr = (AMD_MODULE_HEADER *)mod_ptr->NextBlock;
+ }
+ return false;
+}
+
+/*
+ * Find an image that has the desired module. The image is aligned within
+ * a given range.
+ */
+void *amd_find_image(const void *start_address, const void *end_address,
+ uint32_t alignment, const char name[8])
+{
+ uint8_t *current_ptr = (uint8_t *)start_address;
+ uint8_t *start = (uint8_t *)start_address;
+ uint8_t *end = (uint8_t *)end_address;
+ AMD_IMAGE_HEADER *image_ptr;
+
+ while ((current_ptr >= start) && (current_ptr < end)) {
+ if (IMAGE_SIGNATURE == *((uint32_t *)current_ptr)) {
+ image_ptr = (AMD_IMAGE_HEADER *) current_ptr;
+
+ /* Check if the image has the desired module */
+ if (validate_image((void *)image_ptr->ModuleInfoOffset,
+ name))
+ return current_ptr;
+ }
+ current_ptr += alignment;
+ }
+ return NULL;
+}
diff --git a/src/soc/amd/common/pi/refcode_loader.c b/src/soc/amd/common/pi/refcode_loader.c
new file mode 100644
index 0000000000..d9704e0548
--- /dev/null
+++ b/src/soc/amd/common/pi/refcode_loader.c
@@ -0,0 +1,105 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <acpi/acpi.h>
+#include <cbfs.h>
+#include <cbmem.h>
+#include <console/console.h>
+#include <rmodule.h>
+#include <stage_cache.h>
+#include <amdblocks/agesawrapper.h>
+#include <amdblocks/image.h>
+
+static void *agesa_map_raw_file(const char *name, size_t *size)
+{
+ enum cbfs_type type = CBFS_TYPE_RAW;
+ return cbfs_type_map(name, size, &type);
+}
+
+static void *agesa_map_stage_file_early(const char *name, size_t *size)
+{
+ enum cbfs_type type = CBFS_TYPE_STAGE;
+ return cbfs_type_map(name, size, &type);
+}
+
+static void *agesa_map_stage_file_ramstage(const char *name, size_t *size)
+{
+ struct prog prog = PROG_INIT(PROG_REFCODE, name);
+ struct rmod_stage_load rmod_agesa = {
+ .cbmem_id = CBMEM_ID_REFCODE,
+ .prog = &prog,
+ };
+
+ if (resume_from_stage_cache()) {
+ stage_cache_load_stage(STAGE_REFCODE, &prog);
+ } else {
+ if (rmodule_stage_load(&rmod_agesa) < 0)
+ return NULL;
+
+ stage_cache_add(STAGE_REFCODE, &prog);
+ }
+
+ *size = prog_size(&prog);
+ return prog_start(&prog);
+}
+
+static void *agesa_map_stage_file(const char *name, size_t *size)
+{
+ if (!ENV_RAMSTAGE || !CONFIG(AGESA_SPLIT_MEMORY_FILES))
+ return agesa_map_stage_file_early(name, size);
+ return agesa_map_stage_file_ramstage(name, size);
+}
+
+static const char *get_agesa_cbfs_name(void)
+{
+ if (!CONFIG(AGESA_SPLIT_MEMORY_FILES))
+ return CONFIG_AGESA_CBFS_NAME;
+ if (!ENV_RAMSTAGE)
+ return CONFIG_AGESA_PRE_MEMORY_CBFS_NAME;
+ return CONFIG_AGESA_POST_MEMORY_CBFS_NAME;
+}
+
+const void *agesawrapper_locate_module(const char name[8])
+{
+ const void *agesa;
+ const AMD_IMAGE_HEADER *image;
+ size_t file_size;
+ const char *fname;
+
+ /* Assume boot device is memory mapped so the mapping can leak. */
+ assert(CONFIG(BOOT_DEVICE_MEMORY_MAPPED));
+
+ fname = get_agesa_cbfs_name();
+
+ if (CONFIG(AGESA_BINARY_PI_AS_STAGE))
+ agesa = agesa_map_stage_file(fname, &file_size);
+ else
+ agesa = agesa_map_raw_file(fname, &file_size);
+
+ if (!agesa)
+ return NULL;
+
+ image = amd_find_image(agesa, agesa + file_size, 4096, name);
+
+ if (!image)
+ return NULL;
+
+ return (AMD_MODULE_HEADER *)image->ModuleInfoOffset;
+}
+
+static MODULE_ENTRY agesa_dispatcher;
+
+MODULE_ENTRY agesa_get_dispatcher(void)
+{
+ const AMD_MODULE_HEADER *module;
+ static const char id[8] = AGESA_ID;
+
+ if (agesa_dispatcher != NULL)
+ return agesa_dispatcher;
+
+ module = agesawrapper_locate_module(id);
+ if (!module)
+ return NULL;
+
+ agesa_dispatcher = module->ModuleDispatcher;
+ return agesa_dispatcher;
+}
diff --git a/src/soc/amd/common/pi/s3_resume.c b/src/soc/amd/common/pi/s3_resume.c
new file mode 100644
index 0000000000..2094931dca
--- /dev/null
+++ b/src/soc/amd/common/pi/s3_resume.c
@@ -0,0 +1,80 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <device/mmio.h>
+#include <stage_cache.h>
+#include <mrc_cache.h>
+#include <reset.h>
+#include <console/console.h>
+#include <soc/southbridge.h>
+#include <amdblocks/s3_resume.h>
+#include <amdblocks/acpi.h>
+
+/* Training data versioning is not supported or tracked. */
+#define DEFAULT_MRC_VERSION 0
+
+static void __noreturn reboot_from_resume(const char *message)
+{
+ printk(BIOS_ERR, "%s", message);
+ set_pm1cnt_s5();
+ board_reset();
+}
+
+AGESA_STATUS OemInitResume(S3_DATA_BLOCK *dataBlock)
+{
+ void *base;
+ size_t size;
+ int i;
+ uint32_t erased = 0xffffffff;
+
+ base = mrc_cache_current_mmap_leak(MRC_TRAINING_DATA,
+ DEFAULT_MRC_VERSION,
+ &size);
+ if (!base || !size)
+ reboot_from_resume("Error: S3 NV data not found, rebooting.\n");
+
+ /* Read 16 bytes to infer if the NV has been erased from flash. */
+ for (i = 0; i < 4; i++)
+ erased &= read32((uint32_t *)base + i);
+ if (erased == 0xffffffff)
+ reboot_from_resume("Error: S3 NV data invalid, rebooting.\n");
+
+ dataBlock->NvStorage = base;
+ dataBlock->NvStorageSize = size;
+ printk(BIOS_SPEW, "S3 NV data @%p, 0x%0zx bytes\n",
+ dataBlock->NvStorage, (size_t)dataBlock->NvStorageSize);
+
+ return AGESA_SUCCESS;
+}
+
+AGESA_STATUS OemS3LateRestore(S3_DATA_BLOCK *dataBlock)
+{
+ void *base = NULL;
+ size_t size = 0;
+
+ stage_cache_get_raw(STAGE_S3_DATA, &base, &size);
+ if (!base || !size) {
+ printk(BIOS_ERR, "Error: S3 volatile data not found\n");
+ return AGESA_FATAL;
+ }
+
+ dataBlock->VolatileStorage = base;
+ dataBlock->VolatileStorageSize = size;
+ printk(BIOS_SPEW, "S3 volatile data @%p, 0x%0zx bytes\n",
+ dataBlock->VolatileStorage, (size_t)dataBlock->VolatileStorageSize);
+
+ return AGESA_SUCCESS;
+}
+
+AGESA_STATUS OemS3Save(S3_DATA_BLOCK *dataBlock)
+{
+ if (mrc_cache_stash_data(MRC_TRAINING_DATA, DEFAULT_MRC_VERSION,
+ dataBlock->NvStorage, dataBlock->NvStorageSize) < 0) {
+ printk(BIOS_ERR, "Failed to stash MRC data\n");
+ return AGESA_CRITICAL;
+ }
+
+ stage_cache_add_raw(STAGE_S3_DATA, dataBlock->VolatileStorage,
+ dataBlock->VolatileStorageSize);
+
+ return AGESA_SUCCESS;
+}