From ad0ccb336d0184c7e4bd8157483bc80a5dfd5ab4 Mon Sep 17 00:00:00 2001 From: Johnny Lin Date: Fri, 10 Apr 2020 18:28:33 +0800 Subject: drivers/ocp/dmi: Add OCP_DMI driver for populating SMBIOS from IPMI FRU data It implements the SMBIOS IPMI FRU mapping table defined in https://www.opencompute.org/documents/facebook-xeon-motherboard-v31 22.3 SMBIOS FRU mapping table. Mainboard needs to configure the correct values for FRU_DEVICE_ID and BMC_KCS_BASE. For type 11 string 1 to 6 are common and implemented in this driver, the rest are project dependent and can be added in the mainboard code. Tested on OCP Tioga Pass. Change-Id: I08c958dfad83216cd12545760a19d205efc2515b Signed-off-by: Johnny Lin Reviewed-on: https://review.coreboot.org/c/coreboot/+/40308 Reviewed-by: Philipp Deppenwiese Reviewed-by: Jonathan Zhang Reviewed-by: Christian Walter Tested-by: build bot (Jenkins) --- src/drivers/ocp/dmi/Kconfig | 12 ++ src/drivers/ocp/dmi/Makefile.inc | 1 + src/drivers/ocp/dmi/ocp_dmi.h | 20 +++ src/drivers/ocp/dmi/smbios.c | 260 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 293 insertions(+) create mode 100644 src/drivers/ocp/dmi/Kconfig create mode 100644 src/drivers/ocp/dmi/Makefile.inc create mode 100644 src/drivers/ocp/dmi/ocp_dmi.h create mode 100644 src/drivers/ocp/dmi/smbios.c (limited to 'src/drivers') diff --git a/src/drivers/ocp/dmi/Kconfig b/src/drivers/ocp/dmi/Kconfig new file mode 100644 index 0000000000..b30a40d473 --- /dev/null +++ b/src/drivers/ocp/dmi/Kconfig @@ -0,0 +1,12 @@ +config OCP_DMI + bool + default n + depends on IPMI_KCS && GENERATE_SMBIOS_TABLES && XEON_SP_COMMON_BASE + help + It implements the SMBIOS IPMI FRU mapping table defined in + https://www.opencompute.org/documents/facebook-xeon-motherboard-v31 + 22.3 SMBIOS FRU mapping table + +config FRU_DEVICE_ID + int + default 0 diff --git a/src/drivers/ocp/dmi/Makefile.inc b/src/drivers/ocp/dmi/Makefile.inc new file mode 100644 index 0000000000..587ac89d00 --- /dev/null +++ b/src/drivers/ocp/dmi/Makefile.inc @@ -0,0 +1 @@ +ramstage-$(CONFIG_OCP_DMI) += smbios.c diff --git a/src/drivers/ocp/dmi/ocp_dmi.h b/src/drivers/ocp/dmi/ocp_dmi.h new file mode 100644 index 0000000000..ef0c740c98 --- /dev/null +++ b/src/drivers/ocp/dmi/ocp_dmi.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef __OCP_DMI_H +#define __OCP_DMI_H + +#include +#include +#include + +#define TBF "To Be Filled By O.E.M." + +extern msr_t xeon_sp_ppin[]; + +/* Override SMBIOS type 11 OEM string 1 to string 6 */ +void ocp_oem_smbios_strings(struct device *dev, struct smbios_type11 *t); + +/* This function allows adding the same repeated string to the table */ +int smbios_add_oem_string(u8 *start, const char *str); + +#endif diff --git a/src/drivers/ocp/dmi/smbios.c b/src/drivers/ocp/dmi/smbios.c new file mode 100644 index 0000000000..4e54af0b2a --- /dev/null +++ b/src/drivers/ocp/dmi/smbios.c @@ -0,0 +1,260 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ocp_dmi.h" + +#define PPIN_STR_LEN 17 + +struct fru_info_str fru_strings = {0}; +/* The spec defines only at most 2 sockets */ +msr_t xeon_sp_ppin[2] = {0}; +static bool remote_ppin_done = false; + +/* Override SMBIOS type 1 data. */ +const char *smbios_system_manufacturer(void) +{ + if (fru_strings.prod_info.manufacturer != NULL) + return (const char *)fru_strings.prod_info.manufacturer; + else + return CONFIG_MAINBOARD_SMBIOS_MANUFACTURER; +} + +const char *smbios_system_product_name(void) +{ + if (fru_strings.board_info.product_name != NULL) + return (const char *)fru_strings.prod_info.product_name; + else + return CONFIG_MAINBOARD_SMBIOS_PRODUCT_NAME; +} + +const char *smbios_system_serial_number(void) +{ + if (fru_strings.prod_info.serial_number != NULL) + return (const char *)fru_strings.prod_info.serial_number; + else + return CONFIG_MAINBOARD_SERIAL_NUMBER; +} + +const char *smbios_system_version(void) +{ + if (fru_strings.prod_info.product_version != NULL) + return (const char *)fru_strings.prod_info.product_version; + else + return CONFIG_MAINBOARD_VERSION; +} + +/* Override SMBIOS type 1 uuid from the value from BMC. */ +void smbios_system_set_uuid(u8 *uuid) +{ + ipmi_get_system_guid(CONFIG_BMC_KCS_BASE, uuid); +} + +/* Override SMBIOS type 2 data. */ +const char *smbios_mainboard_version(void) +{ + if (fru_strings.board_info.part_number != NULL) + return (const char *)fru_strings.board_info.part_number; + else + return CONFIG_MAINBOARD_VERSION; +} + +const char *smbios_mainboard_manufacturer(void) +{ + if (fru_strings.board_info.manufacturer != NULL) + return (const char *)fru_strings.board_info.manufacturer; + else + return CONFIG_MAINBOARD_SMBIOS_MANUFACTURER; +} + +const char *smbios_mainboard_product_name(void) +{ + if (fru_strings.board_info.product_name != NULL) + return (const char *)fru_strings.board_info.product_name; + else + return CONFIG_MAINBOARD_SMBIOS_PRODUCT_NAME; +} + +const char *smbios_mainboard_serial_number(void) +{ + if (fru_strings.board_info.serial_number != NULL) + return (const char *)fru_strings.board_info.serial_number; + else + return CONFIG_MAINBOARD_SERIAL_NUMBER; +} + +/* Override SMBIOS type 2 and 3 asset_tag data. */ +const char *smbios_mainboard_asset_tag(void) +{ + if (fru_strings.prod_info.asset_tag != NULL) + return (const char *)fru_strings.prod_info.asset_tag; + else + return ""; +} + +/* Override SMBIOS type 3 data. */ +smbios_enclosure_type smbios_mainboard_enclosure_type(void) +{ + /* SMBIOS System Enclosure or Chassis Types are values from 0 to 20h. */ + if (fru_strings.chassis_info.chassis_type <= 0x20) + return fru_strings.chassis_info.chassis_type; + else + return SMBIOS_ENCLOSURE_RACK_MOUNT_CHASSIS; +} + +const char *smbios_chassis_version(void) +{ + if (fru_strings.chassis_info.chassis_partnumber != NULL) + return fru_strings.chassis_info.chassis_partnumber; + else + return ""; +} + +const char *smbios_chassis_serial_number(void) +{ + if (fru_strings.chassis_info.serial_number != NULL) + return fru_strings.chassis_info.serial_number; + else + return ""; +} + +/* Override SMBIOS type 4 processor serial numbers from FRU Chassis custom data. */ +const char *smbios_processor_serial_number(void) +{ + /* For now type 4 only creates for one CPU, so it can only write the serial number + * of CPU0. + */ + if (*fru_strings.chassis_info.chassis_custom != NULL) + return *fru_strings.chassis_info.chassis_custom; + else + return ""; +} + +static void read_remote_ppin(void *data) +{ + *(msr_t *)data = read_msr_ppin(); + remote_ppin_done = true; +} + +static void wait_for_remote_ppin(void) +{ + struct stopwatch sw; + + stopwatch_init_msecs_expire(&sw, 500); + while (!stopwatch_expired(&sw)) { + if (remote_ppin_done) + break; + mdelay(100); + } + if (stopwatch_expired(&sw)) + printk(BIOS_ERR, "Wait for read_remote_ppin() timeout\n"); +} + +int smbios_add_oem_string(u8 *start, const char *str) +{ + int i = 1; + char *p = (char *)start; + + if (*str == '\0') + return 0; + + for (;;) { + if (!*p) { + strcpy(p, str); + p += strlen(str); + *p++ = '\0'; + *p++ = '\0'; + return i; + } + + p += strlen(p)+1; + i++; + } +} + +/* When the most significant 4 bits of PPIN hi/lo are 0, add '0' to the beginning */ +static void ppin_string_fixup(int i, char *ppin) +{ + if ((xeon_sp_ppin[i].hi & 0xf0000000) == 0) { + if ((xeon_sp_ppin[i].lo & 0xf0000000) == 0) + snprintf(ppin, PPIN_STR_LEN, "0%x0%x", xeon_sp_ppin[i].hi, + xeon_sp_ppin[i].lo); + else + snprintf(ppin, PPIN_STR_LEN, "0%x%x", xeon_sp_ppin[i].hi, + xeon_sp_ppin[i].lo); + } else if ((xeon_sp_ppin[i].lo & 0xf0000000) == 0) { + snprintf(ppin, PPIN_STR_LEN, "%x0%x", xeon_sp_ppin[i].hi, xeon_sp_ppin[i].lo); + } else { + snprintf(ppin, PPIN_STR_LEN, "%x%x", xeon_sp_ppin[i].hi, xeon_sp_ppin[i].lo); + } +} + +/* + * Override SMBIOS type 11 OEM string 1 to string 6, the rest are project dependent + * and can be added in the mainboard code. + */ +void ocp_oem_smbios_strings(struct device *dev, struct smbios_type11 *t) +{ + char ppin[PPIN_STR_LEN]; + + /* Add OEM string 1 to 4 */ + if (fru_strings.board_info.custom_count > 0 && + *fru_strings.board_info.board_custom != NULL) + t->count = smbios_add_oem_string(t->eos, *fru_strings.board_info.board_custom); + else + t->count = smbios_add_oem_string(t->eos, TBF); + + if (fru_strings.prod_info.custom_count > 0 && + *fru_strings.prod_info.product_custom != NULL) + t->count = smbios_add_oem_string(t->eos, *fru_strings.prod_info.product_custom); + else + t->count = smbios_add_oem_string(t->eos, TBF); + + if (fru_strings.prod_info.custom_count > 1 && + *(fru_strings.prod_info.product_custom + 1) != NULL) + t->count = smbios_add_oem_string(t->eos, + *(fru_strings.prod_info.product_custom + 1)); + else + t->count = smbios_add_oem_string(t->eos, TBF); + + if (fru_strings.prod_info.custom_count > 2 && + *(fru_strings.prod_info.product_custom + 2) != NULL) { + t->count = smbios_add_oem_string(t->eos, + *(fru_strings.prod_info.product_custom + 2)); + } else { + t->count = smbios_add_oem_string(t->eos, TBF); + } + + /* Add CPU0 PPIN to OEM string 5 */ + xeon_sp_ppin[0] = read_msr_ppin(); + ppin_string_fixup(0, ppin); + t->count = smbios_add_oem_string(t->eos, ppin); + + /* Add CPU1 PPIN to OEM string 6 */ + if (CONFIG_MAX_SOCKET == 2 && CONFIG(PARALLEL_MP_AP_WORK)) { + /* Read the last CPU MSR */ + if (mp_run_on_aps(read_remote_ppin, (void *)&xeon_sp_ppin[1], + get_platform_thread_count() - 1, 100 * USECS_PER_MSEC)) { + printk(BIOS_ERR, "Failed to read remote PPIN.\n"); + t->count = smbios_add_oem_string(t->eos, TBF); + } else { + /* Wait for read_remote_ppin() to finish because it's executed + in parallel */ + wait_for_remote_ppin(); + ppin_string_fixup(1, ppin); + t->count = smbios_add_oem_string(t->eos, ppin); + } + } else { + t->count = smbios_add_oem_string(t->eos, "0000"); + } +} -- cgit v1.2.3