summaryrefslogtreecommitdiffstats
path: root/src/drivers/ipmi/ipmi_fru.c
diff options
context:
space:
mode:
authorJohnny Lin <johnny_lin@wiwynn.com>2020-01-22 16:36:17 +0800
committerPatrick Georgi <pgeorgi@google.com>2020-06-25 11:54:17 +0000
commitd6a47729d34bedaf903e7758ad5d16a5f7ec8294 (patch)
treec2392178b4b7393e336634933ddc1c4defb6a4ee /src/drivers/ipmi/ipmi_fru.c
parent496bd1555da13e66087bf02ff2386c579e894e7d (diff)
downloadcoreboot-d6a47729d34bedaf903e7758ad5d16a5f7ec8294.tar.gz
coreboot-d6a47729d34bedaf903e7758ad5d16a5f7ec8294.tar.bz2
coreboot-d6a47729d34bedaf903e7758ad5d16a5f7ec8294.zip
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>
Diffstat (limited to 'src/drivers/ipmi/ipmi_fru.c')
-rw-r--r--src/drivers/ipmi/ipmi_fru.c102
1 files changed, 100 insertions, 2 deletions
diff --git a/src/drivers/ipmi/ipmi_fru.c b/src/drivers/ipmi/ipmi_fru.c
index 506e2079ef70..9cfe893ccf8c 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;