Patrick Georgi has submitted this change. ( https://review.coreboot.org/c/coreboot/+/40306 )
Change subject: drivers/ipmi: Add IPMI read FRU chassis info area ......................................................................
drivers/ipmi: Add IPMI read FRU chassis info area
Implemented according to IPMI "Platform Management FRU Information Storage Definition" specification v1.0 for reading FRU data Chassis Info Area.
Tested on OCP Tioga Pass.
Change-Id: Ieb53c20f8eb4b7720bf1fe349e6aaebaa4c37247 Signed-off-by: Johnny Lin johnny_lin@wiwynn.com Reviewed-on: https://review.coreboot.org/c/coreboot/+/40306 Reviewed-by: Patrick Rudolph siro@das-labor.org Reviewed-by: Jonathan Zhang jonzhang@fb.com Tested-by: build bot (Jenkins) no-reply@coreboot.org --- M src/drivers/ipmi/ipmi_fru.c M src/drivers/ipmi/ipmi_ops.h 2 files changed, 110 insertions(+), 2 deletions(-)
Approvals: build bot (Jenkins): Verified Patrick Rudolph: Looks good to me, approved Jonathan Zhang: Looks good to me, but someone else must approve
diff --git a/src/drivers/ipmi/ipmi_fru.c b/src/drivers/ipmi/ipmi_fru.c index 506e207..9cfe893 100644 --- a/src/drivers/ipmi/ipmi_fru.c +++ b/src/drivers/ipmi/ipmi_fru.c @@ -11,6 +11,7 @@ #define READ_FRU_DATA_RETRY_INTERVAL_MS 30 /* From IPMI spec v2.0 rev 1.1 */ #define OFFSET_LENGTH_MULTIPLIER 8 /* offsets/lengths are multiples of 8 */ #define NUM_DATA_BYTES(t) (t & 0x3f) /* Encoded in type/length byte */ +#define FRU_END_OF_FIELDS 0xc1 /* type/length byte encoded to indicate no more info fields */
static enum cb_err ipmi_read_fru(const int port, struct ipmi_read_fru_data_req *req, uint8_t *fru_data) @@ -120,6 +121,98 @@
return length; } + +static enum cb_err read_fru_chassis_info_area(const int port, const uint8_t id, + uint8_t offset, struct fru_chassis_info *info) +{ + uint8_t length; + struct ipmi_read_fru_data_req req; + uint8_t *data_ptr, *end, *custom_data_ptr; + int ret = CB_SUCCESS; + + if (!offset) + return CB_ERR; + + offset = offset * OFFSET_LENGTH_MULTIPLIER; + req.fru_device_id = id; + /* Read Chassis Info Area length first. */ + req.fru_offset = offset + 1; + req.count = sizeof(length); + if (ipmi_read_fru(port, &req, &length) != CB_SUCCESS || !length) { + printk(BIOS_ERR, "%s failed, length: %d\n", __func__, length); + return CB_ERR; + } + length = length * OFFSET_LENGTH_MULTIPLIER; + data_ptr = (uint8_t *)malloc(length); + if (!data_ptr) { + printk(BIOS_ERR, "malloc %d bytes for chassis info failed\n", length); + return CB_ERR; + } + end = data_ptr + length; + /* Read Chassis Info Area data. */ + req.fru_offset = offset; + req.count = length; + if (ipmi_read_fru(port, &req, data_ptr) != CB_SUCCESS) { + printk(BIOS_ERR, "%s failed to read fru\n", __func__); + ret = CB_ERR; + goto out; + } + if (checksum(data_ptr, length)) { + printk(BIOS_ERR, "Bad FRU chassis info checksum.\n"); + ret = CB_ERR; + goto out; + } + /* Read chassis type. */ + info->chassis_type = data_ptr[CHASSIS_TYPE_OFFSET]; + + printk(BIOS_DEBUG, "Read chassis part number string.\n"); + length = read_data_string(data_ptr + CHASSIS_TYPE_OFFSET + 1, + &info->chassis_partnumber); + + printk(BIOS_DEBUG, "Read chassis serial number string.\n"); + data_ptr += CHASSIS_TYPE_OFFSET + 1 + length + 1; + length = read_data_string(data_ptr, &info->serial_number); + + printk(BIOS_DEBUG, "Read custom chassis info fields.\n"); + data_ptr += length + 1; + /* Check how many valid custom fields first. */ + info->custom_count = 0; + custom_data_ptr = data_ptr; + while ((data_ptr < end) && ((data_ptr[0] != FRU_END_OF_FIELDS))) { + length = NUM_DATA_BYTES(data_ptr[0]); + if (length > 0) + info->custom_count++; + data_ptr += length + 1; + } + if (!info->custom_count) + goto out; + + info->chassis_custom = malloc(info->custom_count * sizeof(char *)); + if (!info->chassis_custom) { + printk(BIOS_ERR, "%s failed to malloc %ld bytes for " + "chassis custom data array.\n", __func__, + info->custom_count * sizeof(char *)); + ret = CB_ERR; + goto out; + } + + /* Start reading custom chassis data. */ + data_ptr = custom_data_ptr; + int count = 0; + while ((data_ptr < end) && ((data_ptr[0] != FRU_END_OF_FIELDS))) { + length = NUM_DATA_BYTES(data_ptr[0]); + if (length > 0) { + length = read_data_string(data_ptr, info->chassis_custom + count); + count++; + } + data_ptr += length + 1; + } + +out: + free(data_ptr); + return ret; +} + static void read_fru_board_info_area(const int port, const uint8_t id, uint8_t offset, struct fru_board_info *info) { @@ -277,7 +370,8 @@ &fru_info_str->prod_info); read_fru_board_info_area(port, id, fru_common_hdr.board_area_offset, &fru_info_str->board_info); - /* ToDo: Add read_fru_chassis_info_area(). */ + read_fru_chassis_info_area(port, id, fru_common_hdr.chassis_area_offset, + &fru_info_str->chassis_info); }
void read_fru_one_area(const int port, const uint8_t id, uint16_t offset, @@ -318,7 +412,11 @@ read_fru_board_info_area(port, id, fru_common_hdr.board_area_offset, &fru_info_str->board_info); break; - /* ToDo: Add case for CHASSIS_INFO_AREA. */ + case CHASSIS_INFO_AREA: + memset(&fru_info_str->chassis_info, 0, sizeof(fru_info_str->chassis_info)); + read_fru_chassis_info_area(port, id, fru_common_hdr.chassis_area_offset, + &fru_info_str->chassis_info); + break; default: printk(BIOS_ERR, "Invalid fru_area: %d\n", fru_area); break; diff --git a/src/drivers/ipmi/ipmi_ops.h b/src/drivers/ipmi/ipmi_ops.h index d5d2945..82296a9 100644 --- a/src/drivers/ipmi/ipmi_ops.h +++ b/src/drivers/ipmi/ipmi_ops.h @@ -88,6 +88,7 @@ /* Platform Management FRU Information Storage Definition Spec. */ #define PRODUCT_MAN_TYPE_LEN_OFFSET 3 #define BOARD_MAN_TYPE_LEN_OFFSET 6 +#define CHASSIS_TYPE_OFFSET 2
struct ipmi_fru_common_hdr { uint8_t format_version; @@ -117,9 +118,18 @@ char *part_number; };
+struct fru_chassis_info { + uint8_t chassis_type; + char *chassis_partnumber; + char *serial_number; + char **chassis_custom; + int custom_count; /* Number of custom fields */ +}; + struct fru_info_str { struct fru_product_info prod_info; struct fru_board_info board_info; + struct fru_chassis_info chassis_info; };
enum typecode {