diff options
author | Johnny Lin <johnny_lin@wiwynn.com> | 2020-01-22 16:36:17 +0800 |
---|---|---|
committer | Patrick Georgi <pgeorgi@google.com> | 2020-06-25 11:54:17 +0000 |
commit | d6a47729d34bedaf903e7758ad5d16a5f7ec8294 (patch) | |
tree | c2392178b4b7393e336634933ddc1c4defb6a4ee | |
parent | 496bd1555da13e66087bf02ff2386c579e894e7d (diff) |
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>
-rw-r--r-- | src/drivers/ipmi/ipmi_fru.c | 102 | ||||
-rw-r--r-- | src/drivers/ipmi/ipmi_ops.h | 10 |
2 files changed, 110 insertions, 2 deletions
diff --git a/src/drivers/ipmi/ipmi_fru.c b/src/drivers/ipmi/ipmi_fru.c index 506e2079ef..9cfe893ccf 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 @@ static int read_data_string(const uint8_t *data_ptr, char **string) 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 @@ void read_fru_areas(const int port, const uint8_t id, uint16_t offset, &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 @@ void read_fru_one_area(const int port, const uint8_t id, uint16_t offset, 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 d5d2945b4a..82296a92f0 100644 --- a/src/drivers/ipmi/ipmi_ops.h +++ b/src/drivers/ipmi/ipmi_ops.h @@ -88,6 +88,7 @@ struct ipmi_add_sel_rsp { /* 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 @@ struct fru_board_info { 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 { |