summaryrefslogtreecommitdiff
path: root/src/soc/amd/common/pi
diff options
context:
space:
mode:
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;
+}