From 23419df34cf447d94994ae74b241ab0b40216140 Mon Sep 17 00:00:00 2001 From: Subrata Banik Date: Thu, 9 Oct 2025 16:28:08 +0530 Subject: [PATCH] drivers/intel/fsp2_0: Implement API to retrieve DIMM info This patch extracts the implementation responsible for retrieving DIMM information from the FSP HOBs and centralizes it within the common FSP driver code. This ensures that each SoC layer does not duplicate the logic for parsing memory-related data. The primary goal is to rely on the common FSP driver code for retrieving memory information. The only SoC-specific implementation that remains is limited to handling dependencies related to FSP UPD or header differences across SoC generations. TEST=Able to build and boot google/kinmen. Verify the memory related information is proper as part of the SMBIOS table. Change-Id: Ic3741a248bb1fe9420c784d51fbf459a30f8c42f Signed-off-by: Subrata Banik Reviewed-on: https://review.coreboot.org/c/coreboot/+/89494 Reviewed-by: Kapil Porwal Tested-by: build bot (Jenkins) --- src/drivers/intel/fsp2_0/Kconfig | 7 ++ src/drivers/intel/fsp2_0/Makefile.mk | 1 + src/drivers/intel/fsp2_0/fsp_dimm_info.c | 139 +++++++++++++++++++++ src/drivers/intel/fsp2_0/include/fsp/api.h | 15 +++ 4 files changed, 162 insertions(+) create mode 100644 src/drivers/intel/fsp2_0/fsp_dimm_info.c diff --git a/src/drivers/intel/fsp2_0/Kconfig b/src/drivers/intel/fsp2_0/Kconfig index 715ec2a759..ed599d5e72 100644 --- a/src/drivers/intel/fsp2_0/Kconfig +++ b/src/drivers/intel/fsp2_0/Kconfig @@ -493,4 +493,11 @@ config USE_COREBOOT_FOR_BMP_RENDERING Platforms can choose to override this Kconfig option based on their specific graphics requirements and FSP capabilities. +config FSP_DIMM_INFO + bool + default n + help + Select this option to retrieve DIMM (Memory) related information by relying on + the Intel FSP. coreboot relies on this information to prepare SMBIOS table 17. + endif diff --git a/src/drivers/intel/fsp2_0/Makefile.mk b/src/drivers/intel/fsp2_0/Makefile.mk index 16905ca373..a7dd80a1ed 100644 --- a/src/drivers/intel/fsp2_0/Makefile.mk +++ b/src/drivers/intel/fsp2_0/Makefile.mk @@ -17,6 +17,7 @@ romstage-$(CONFIG_MMA) += mma_core.c romstage-y += cbmem.c romstage-$(CONFIG_ENABLE_FSP_ERROR_INFO) += fsp_error_info_hob.c romstage-$(CONFIG_CACHE_MRC_SETTINGS) += save_mrc_data.c +romstage-$(CONFIG_FSP_DIMM_INFO) += fsp_dimm_info.c ramstage-y += debug.c ramstage-$(CONFIG_FSP_USES_CB_DEBUG_EVENT_HANDLER) += fsp_debug_event.c diff --git a/src/drivers/intel/fsp2_0/fsp_dimm_info.c b/src/drivers/intel/fsp2_0/fsp_dimm_info.c new file mode 100644 index 0000000000..82e1fa157b --- /dev/null +++ b/src/drivers/intel/fsp2_0/fsp_dimm_info.c @@ -0,0 +1,139 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * Processes a single DIMM slot, checks for presence, fills in the + * dimm_info structure, and prepares for SMBIOS table creation. + * + * @param src_dimm: Source DIMM info from FSP HOB. + * @param dest_dimm: Destination DIMM info in CBMEM. + * @param channel_info: Channel info from FSP HOB. + * @param meminfo_hob: Overall memory info HOB. + * @param node: Memory controller node index. + * @param dram_part_num_ptr: Pointer to the current DRAM part number string. + * @param dram_part_num_len_ptr: Pointer to the current DRAM part number length. + * @return 1 if a DIMM was processed (index incremented), 0 otherwise. + */ +static int fsp_process_dimm_slot(const DIMM_INFO *src_dimm, + struct dimm_info *dest_dimm, + const CHANNEL_INFO *channel_info, + const MEMORY_INFO_DATA_HOB *meminfo_hob, + int node, + const char **dram_part_num_ptr, + size_t *dram_part_num_len_ptr) +{ + const char *dram_part_num = *dram_part_num_ptr; + size_t dram_part_num_len = *dram_part_num_len_ptr; + const uint8_t *serial_num; + uint8_t mem_profile_number; + struct dimm_fill_args fill_args; + + if (src_dimm->Status != DIMM_PRESENT) + return 0; + + /* If there is no DRAM part number overridden by + * mainboard then use original one. */ + if (!dram_part_num) { + dram_part_num_len = sizeof(src_dimm->ModulePartNum); + dram_part_num = (const char *)&src_dimm->ModulePartNum[0]; + } + + mem_profile_number = meminfo_hob->MemoryProfile; + serial_num = src_dimm->SpdSave + SPD_SAVE_OFFSET_SERIAL; + + platform_fill_dimm_info_args(src_dimm, meminfo_hob, &fill_args); + + /* Populate the DIMM information */ + dimm_info_fill(dest_dimm, + src_dimm->DimmCapacity, + meminfo_hob->MemoryType, + meminfo_hob->ConfiguredMemoryClockSpeed, + src_dimm->RankInDimm, + channel_info->ChannelId, + src_dimm->DimmId, + dram_part_num, + dram_part_num_len, + serial_num, + fill_args.data_width, + meminfo_hob->VddVoltage[mem_profile_number], + meminfo_hob->EccSupport, + fill_args.mfg_id_arg, + src_dimm->SpdModuleType, + node, + meminfo_hob->MaximumMemoryClockSpeed); + + /* Update the pointers in case the DRAM part number was set here */ + *dram_part_num_ptr = dram_part_num; + *dram_part_num_len_ptr = dram_part_num_len; + + return 1; +} + +void fsp_save_dimm_info(void) +{ + int node, channel, dimm_max; + size_t hob_size; + const CONTROLLER_INFO *ctrlr_info; + const CHANNEL_INFO *channel_info; + struct memory_info *mem_info; + const MEMORY_INFO_DATA_HOB *meminfo_hob; + const uint8_t smbios_memory_info_guid[] = { + 0xd4, 0x71, 0x20, 0x9b, 0x54, 0xb0, 0x0c, 0x4e, + 0x8d, 0x09, 0x11, 0xcf, 0x8b, 0x9f, 0x03, 0x23 + }; + const char *dram_part_num = NULL; + size_t dram_part_num_len = 0; + + /* Locate the memory info HOB, presence validated by raminit */ + meminfo_hob = fsp_find_extension_hob_by_guid(smbios_memory_info_guid, &hob_size); + + if (!meminfo_hob || hob_size == 0) { + printk(BIOS_ERR, "SMBIOS MEMORY_INFO_DATA_HOB not found\n"); + return; + } + + /* Allocate CBMEM area for DIMM information... */ + mem_info = cbmem_add(CBMEM_ID_MEMINFO, sizeof(*mem_info)); + if (!mem_info) { + printk(BIOS_ERR, "CBMEM entry for DIMM info missing\n"); + return; + } + + memset(mem_info, 0, sizeof(*mem_info)); + + /* Allow mainboard to override DRAM part number. */ + dram_part_num = mainboard_get_dram_part_num(); + if (dram_part_num) + dram_part_num_len = strlen(dram_part_num); + + /* Save available DIMM information */ + size_t dimm = 0; + dimm_max = ARRAY_SIZE(mem_info->dimm); + for (node = 0; node < MAX_NODE; node++) { + ctrlr_info = &meminfo_hob->Controller[node]; + for (channel = 0; channel < MAX_CH && dimm < dimm_max; channel++) { + channel_info = &ctrlr_info->ChannelInfo[channel]; + if (channel_info->Status != CHANNEL_PRESENT) + continue; + for (size_t index = 0; index < MAX_DIMM && dimm < dimm_max; index++) + dimm += fsp_process_dimm_slot(&channel_info->DimmInfo[index], + &mem_info->dimm[dimm], + channel_info, meminfo_hob, + node, &dram_part_num, + &dram_part_num_len); + } + } + + mem_info->dimm_cnt = dimm; + if (mem_info->dimm_cnt == 0) + printk(BIOS_ERR, "No DIMMs found\n"); + else + printk(BIOS_DEBUG, "%d DIMMs found\n", mem_info->dimm_cnt); +} diff --git a/src/drivers/intel/fsp2_0/include/fsp/api.h b/src/drivers/intel/fsp2_0/include/fsp/api.h index 84edb640b2..b9a58dbaa0 100644 --- a/src/drivers/intel/fsp2_0/include/fsp/api.h +++ b/src/drivers/intel/fsp2_0/include/fsp/api.h @@ -50,6 +50,21 @@ void platform_fsp_silicon_init_params_cb(FSPS_UPD *supd); /* Callbacks for SoC/Mainboard specific overrides */ void platform_fsp_memory_multi_phase_init_cb(uint32_t phase_index); void platform_fsp_silicon_multi_phase_init_cb(uint32_t phase_index); + +#if CONFIG(FSP_DIMM_INFO) +struct dimm_fill_args { + uint16_t data_width; + uint16_t mfg_id_arg; +}; + +/* Retrieve DIMM Information by relying on the FSP */ +void fsp_save_dimm_info(void); +/* Callbacks for SoC/Mainboard specific overrides */ +void platform_fill_dimm_info_args(const DIMM_INFO *src_dimm, + const MEMORY_INFO_DATA_HOB *meminfo_hob, + struct dimm_fill_args *args); +#endif + /* * Displays an early shutdown notification to the user. *