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 <johnny_lin@wiwynn.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/40308 Reviewed-by: Philipp Deppenwiese <zaolin.daisuki@gmail.com> Reviewed-by: Jonathan Zhang <jonzhang@fb.com> Reviewed-by: Christian Walter <christian.walter@9elements.com> Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
This commit is contained in:
parent
29f61a2110
commit
ad0ccb336d
|
@ -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
|
|
@ -0,0 +1 @@
|
||||||
|
ramstage-$(CONFIG_OCP_DMI) += smbios.c
|
|
@ -0,0 +1,20 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||||
|
|
||||||
|
#ifndef __OCP_DMI_H
|
||||||
|
#define __OCP_DMI_H
|
||||||
|
|
||||||
|
#include <cpu/x86/msr.h>
|
||||||
|
#include <device/device.h>
|
||||||
|
#include <smbios.h>
|
||||||
|
|
||||||
|
#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
|
|
@ -0,0 +1,260 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||||
|
|
||||||
|
#include <console/console.h>
|
||||||
|
#include <device/device.h>
|
||||||
|
#include <drivers/ipmi/ipmi_ops.h>
|
||||||
|
#include <delay.h>
|
||||||
|
#include <cpu/x86/mp.h>
|
||||||
|
#include <timer.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <soc/soc_util.h>
|
||||||
|
#include <soc/cpu.h>
|
||||||
|
#include <smbios.h>
|
||||||
|
|
||||||
|
#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");
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue