diff options
-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 { |