summaryrefslogtreecommitdiff
path: root/src/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'src/drivers')
-rw-r--r--src/drivers/ipmi/ipmi_fru.c102
-rw-r--r--src/drivers/ipmi/ipmi_ops.h10
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 {