diff options
Diffstat (limited to 'src/soc/amd/common/pi')
-rw-r--r-- | src/soc/amd/common/pi/Kconfig | 37 | ||||
-rw-r--r-- | src/soc/amd/common/pi/Makefile.inc | 19 | ||||
-rw-r--r-- | src/soc/amd/common/pi/agesawrapper.c | 422 | ||||
-rw-r--r-- | src/soc/amd/common/pi/amd_late_init.c | 177 | ||||
-rw-r--r-- | src/soc/amd/common/pi/amd_resume_final.c | 12 | ||||
-rw-r--r-- | src/soc/amd/common/pi/def_callouts.c | 245 | ||||
-rw-r--r-- | src/soc/amd/common/pi/heapmanager.c | 440 | ||||
-rw-r--r-- | src/soc/amd/common/pi/image.c | 48 | ||||
-rw-r--r-- | src/soc/amd/common/pi/refcode_loader.c | 105 | ||||
-rw-r--r-- | src/soc/amd/common/pi/s3_resume.c | 80 |
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; +} |