diff options
author | Hariharasubramanian R <hramasub@in.ibm.com> | 2015-10-16 06:47:56 -0500 |
---|---|---|
committer | Patrick Williams <patrick@stwcx.xyz> | 2015-10-31 23:00:11 -0500 |
commit | c2d7946a5ba027fe3cc88de953d17383bfdfed23 (patch) | |
tree | 34165b3f35a84b7799439965272b1b9f917bb768 | |
parent | 4447309a25106fb0a9b174673f198357b5822cbb (diff) | |
download | ipmi-fru-parser-c2d7946a5ba027fe3cc88de953d17383bfdfed23.tar.gz ipmi-fru-parser-c2d7946a5ba027fe3cc88de953d17383bfdfed23.zip |
Add interface to parse fru data into a dictionary.
-rw-r--r-- | frup.c | 428 | ||||
-rw-r--r-- | frup.h | 11 |
2 files changed, 423 insertions, 16 deletions
@@ -43,12 +43,25 @@ #include <unistd.h> #include <string.h> #include <time.h> +#include <systemd/sd-bus.h> +#define IPMI_FRU_PARSER_DEBUG 1 #define uint8_t unsigned char #define uint32_t unsigned int -#define ASSERT(x) if(!(x)) return -1; - +#define TEXTSTR(a) #a +# define ASSERT(x) \ +do { \ +if (0 == (x)) { \ +fprintf(stderr, \ +"Assertion failed: %s, " \ +"%d at \'%s\'\n", \ +__FILE__, \ +__LINE__, \ +TEXTSTR(a)); \ +return -1; \ +} \ +} while (0) #define IPMI_FRU_AREA_TYPE_LENGTH_FIELD_MAX 512 #define IPMI_FRU_SENTINEL_VALUE 0xC1 @@ -57,6 +70,16 @@ #define IPMI_FRU_TYPE_LENGTH_NUMBER_OF_DATA_BYTES_MASK 0x3F #define IPMI_FRU_TYPE_LENGTH_TYPE_CODE_LANGUAGE_CODE 0x03 +/* OpenBMC defines for Parser */ +#define IPMI_FRU_AREA_INTERNAL_USE 0x00 +#define IPMI_FRU_AREA_CHASSIS_INFO 0x01 +#define IPMI_FRU_AREA_BOARD_INFO 0x02 +#define IPMI_FRU_AREA_PRODUCT_INFO 0x03 +#define IPMI_FRU_AREA_MULTI_RECORD 0x04 +#define IPMI_FRU_AREA_TYPE_MAX 0x05 + +#define OPENBMC_VPD_KEY_LEN 64 +#define OPENBMC_VPD_VAL_LEN 512 struct ipmi_fru_field { @@ -66,7 +89,93 @@ struct ipmi_fru_field }; typedef struct ipmi_fru_field ipmi_fru_field_t; +/* + * FRU Parser + */ + +typedef struct ipmi_fru_area_info +{ + uint8_t off; + uint8_t len; +} ipmi_fru_area_info_t; + +typedef struct ipmi_fru_common_hdr +{ + uint8_t fmtver; + uint8_t internal; + uint8_t chassis; + uint8_t board; + uint8_t product; + uint8_t multirec; +} __attribute__((packed)) ipmi_fru_common_hdr_t; + +enum openbmc_vpd_key_id +{ + OPENBMC_VPD_KEY_CHASSIS_TYPE = 1, /* not a type/len */ + OPENBMC_VPD_KEY_CHASSIS_PART_NUM, + OPENBMC_VPD_KEY_CHASSIS_SERIAL_NUM, + OPENBMC_VPD_KEY_CHASSIS_MAX = OPENBMC_VPD_KEY_CHASSIS_SERIAL_NUM, + /* TODO: chassis_custom_fields */ + + OPENBMC_VPD_KEY_BOARD_MFG_DATE, /* not a type/len */ + OPENBMC_VPD_KEY_BOARD_MFR, + OPENBMC_VPD_KEY_BOARD_NAME, + OPENBMC_VPD_KEY_BOARD_SERIAL_NUM, + OPENBMC_VPD_KEY_BOARD_PART_NUM, + OPENBMC_VPD_KEY_BOARD_FRU_FILE_ID, + OPENBMC_VPD_KEY_BOARD_MAX = OPENBMC_VPD_KEY_BOARD_FRU_FILE_ID, + /* TODO: board_custom_fields */ + + OPENBMC_VPD_KEY_PRODUCT_MFR, + OPENBMC_VPD_KEY_PRODUCT_NAME, + OPENBMC_VPD_KEY_PRODUCT_PART_MODEL_NUM, + OPENBMC_VPD_KEY_PRODUCT_VER, + OPENBMC_VPD_KEY_PRODUCT_SERIAL_NUM, + OPENBMC_VPD_KEY_PRODUCT_ASSET_TAG, + OPENBMC_VPD_KEY_PRODUCT_FRU_FILE_ID, + OPENBMC_VPD_KEY_PRODUCT_MAX = OPENBMC_VPD_KEY_PRODUCT_FRU_FILE_ID, + /* TODO: product_custom_fields */ + + OPENBMC_VPD_KEY_MAX, + +}; + +const char* vpd_key_names [] = +{ + "Key Names Table Start", + "Chassis Type", /*OPENBMC_VPD_KEY_CHASSIS_TYPE*/ + "Chassis Part Number", /*OPENBMC_VPD_KEY_CHASSIS_PART_NUM,*/ + "Chassis Serial Number", /*OPENBMC_VPD_KEY_CHASSIS_SERIAL_NUM,*/ + + /* TODO: chassis_custom_fields */ + + "Board Mfg Date", /* OPENBMC_VPD_KEY_BOARD_MFG_DATE, */ /* not a type/len */ + "Board Manufacturer", /* OPENBMC_VPD_KEY_BOARD_MFR, */ + "Board Name", /* OPENBMC_VPD_KEY_BOARD_NAME, */ + "Board Serial Number", /* OPENBMC_VPD_KEY_BOARD_SERIAL_NUM, */ + "Board Part Number", /* OPENBMC_VPD_KEY_BOARD_PART_NUM, */ + "Board FRU File ID", /* OPENBMC_VPD_KEY_BOARD_FRU_FILE_ID, */ + /* TODO: board_custom_fields */ + + "Product Manufacturer", /* OPENBMC_VPD_KEY_PRODUCT_MFR, */ + "Product Name", /* OPENBMC_VPD_KEY_PRODUCT_NAME, */ + "Product Model Number", /* OPENBMC_VPD_KEY_PRODUCT_PART_MODEL_NUM, */ + "Product Version", /* OPENBMC_VPD_KEY_PRODUCT_VER, */ + "Product Serial Number", /* OPENBMC_VPD_KEY_PRODUCT_SERIAL_NUM, */ + "Product Asset Tag", /* OPENBMC_VPD_KEY_PRODUCT_ASSET_TAG, */ + "Product FRU File ID", /* OPENBMC_VPD_KEY_PRODUCT_FRU_FILE_ID, */ + /* TODO: product_custom_fields */ + + "Key Names Table End" /*OPENBMC_VPD_KEY_MAX,*/ +}; + +/* + * -------------------------------------------------------------------- + * + * -------------------------------------------------------------------- + */ +/* private method to parse type/length */ static int _parse_type_length (const void *areabuf, unsigned int areabuflen, @@ -84,14 +193,14 @@ _parse_type_length (const void *areabuf, type_length = areabufptr[current_area_offset]; - /* IPMI Workaround + /* ipmi workaround * - * Dell P weredge R610 + * dell p weredge r610 * - * My reading of the FRU Spec is that all non-custom fields are - * required to be listed by the vendor. However, on this + * my reading of the fru spec is that all non-custom fields are + * required to be listed by the vendor. however, on this * motherboard, some areas list this, indicating that there is - * no more data to be parsed. So now, for "required" fields, I + * no more data to be parsed. so now, for "required" fields, i * check to see if the type-length field is a sentinel before * calling this function. */ @@ -101,8 +210,8 @@ _parse_type_length (const void *areabuf, type_code = (type_length & IPMI_FRU_TYPE_LENGTH_TYPE_CODE_MASK) >> IPMI_FRU_TYPE_LENGTH_TYPE_CODE_SHIFT; (*number_of_data_bytes) = type_length & IPMI_FRU_TYPE_LENGTH_NUMBER_OF_DATA_BYTES_MASK; - /* Special Case: This shouldn't be a length of 0x01 (see type/length - * byte format in FRU Information Storage Definition). + /* special case: this shouldn't be a length of 0x01 (see type/length + * byte format in fru information storage definition). */ if (type_code == IPMI_FRU_TYPE_LENGTH_TYPE_CODE_LANGUAGE_CODE && (*number_of_data_bytes) == 0x01) @@ -294,19 +403,19 @@ ipmi_fru_board_info_area (const void *areabuf, /* mfg_date_time is in minutes, so multiple by 60 to get seconds */ mfg_date_time_tmp *= 60; - /* Posix says individual calls need not clear/set all portions of + /* posix says individual calls need not clear/set all portions of * 'struct tm', thus passing 'struct tm' between functions could - * have issues. So we need to memset. + * have issues. so we need to memset. */ memset (&tm, '\0', sizeof(struct tm)); - /* In FRU, epoch is 0:00 hrs 1/1/96 + /* in fru, epoch is 0:00 hrs 1/1/96 * - * So convert into ansi epoch + * so convert into ansi epoch */ tm.tm_year = 96; /* years since 1900 */ - tm.tm_mon = 0; /* months since January */ + tm.tm_mon = 0; /* months since january */ tm.tm_mday = 1; /* 1-31 */ tm.tm_hour = 0; tm.tm_min = 0; @@ -363,8 +472,7 @@ ipmi_fru_board_info_area (const void *areabuf, if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) goto out; - if (_parse_type_length ( - areabufptr, + if (_parse_type_length (areabufptr, areabuflen, area_offset, &number_of_data_bytes, @@ -597,3 +705,291 @@ ipmi_fru_product_info_area (const void *areabuf, cleanup: return (rv); } + + +int +parse_fru (const void* msgbuf, sd_bus_message* vpdtbl) +{ + int ret = 0; + int rv = -1; + int i = 0; + ipmi_fru_area_info_t fru_area_info [ IPMI_FRU_AREA_TYPE_MAX ]; + ipmi_fru_common_hdr_t* chdr = NULL; + uint8_t* hdr = NULL; + + + ipmi_fru_field_t vpd_info [ OPENBMC_VPD_KEY_MAX ]; + /*char ipmi_fru_field_str [ IPMI_FRU_AREA_TYPE_LENGTH_FIELD_MAX ]; + uint32_t len=0;*/ + const uint8_t* ipmi_fru_field_str; + + /* Chassis */ + uint8_t chassis_type; + + /* Board */ + uint32_t mfg_date_time; + + /* Product */ + unsigned int product_custom_fields_len; + + ASSERT (msgbuf); + ASSERT (vpdtbl); + + for (i=0; i<OPENBMC_VPD_KEY_MAX; i++) + { + memset (vpd_info[i].type_length_field, '\0', IPMI_FRU_AREA_TYPE_LENGTH_FIELD_MAX); + vpd_info[i].type_length_field_length = 0; + } + + for (i=0; i<IPMI_FRU_AREA_TYPE_MAX; i++) + { + fru_area_info [ i ].off = 0; + fru_area_info [ i ].len = 0; + } + + chdr = (ipmi_fru_common_hdr_t*) msgbuf; + hdr = (uint8_t*) msgbuf; + + fru_area_info [ IPMI_FRU_AREA_INTERNAL_USE ].off = chdr->internal; + fru_area_info [ IPMI_FRU_AREA_CHASSIS_INFO ].off = chdr->chassis; + fru_area_info [ IPMI_FRU_AREA_BOARD_INFO ].off = chdr->board; + fru_area_info [ IPMI_FRU_AREA_PRODUCT_INFO ].off = chdr->product; + fru_area_info [ IPMI_FRU_AREA_MULTI_RECORD ].off = chdr->multirec; + + if (chdr->internal) + { + fru_area_info [ IPMI_FRU_AREA_INTERNAL_USE ].len = 8*(*(hdr+8*chdr->internal+1)); + + /* TODO: Parse internal use area */ + } + + if (chdr->chassis) + { + fru_area_info [ IPMI_FRU_AREA_CHASSIS_INFO ].len = 8*(*(hdr+8*chdr->chassis+1)); + ipmi_fru_chassis_info_area (hdr+8*chdr->chassis+2, + fru_area_info [ IPMI_FRU_AREA_CHASSIS_INFO ].len, + &chassis_type, + &vpd_info [OPENBMC_VPD_KEY_CHASSIS_PART_NUM], + &vpd_info [OPENBMC_VPD_KEY_CHASSIS_SERIAL_NUM], + NULL, 0); + } + + if (chdr->board) + { + fru_area_info [ IPMI_FRU_AREA_BOARD_INFO ].len = 8*(*(hdr+8*chdr->board+1)); + ipmi_fru_board_info_area (hdr+8*chdr->board+2, + fru_area_info [ IPMI_FRU_AREA_BOARD_INFO ].len, + NULL, + &mfg_date_time, + &vpd_info [OPENBMC_VPD_KEY_BOARD_MFR], + &vpd_info [OPENBMC_VPD_KEY_BOARD_NAME], + &vpd_info [OPENBMC_VPD_KEY_BOARD_SERIAL_NUM], + &vpd_info [OPENBMC_VPD_KEY_BOARD_PART_NUM], + &vpd_info [OPENBMC_VPD_KEY_BOARD_FRU_FILE_ID], + NULL, 0); + } + + if (chdr->product) + { + fru_area_info [ IPMI_FRU_AREA_PRODUCT_INFO ].len = 8*(*(hdr+8*chdr->product+1)); + ipmi_fru_product_info_area (hdr+8*chdr->product+2, + fru_area_info [ IPMI_FRU_AREA_PRODUCT_INFO ].len, + NULL, + &vpd_info [OPENBMC_VPD_KEY_PRODUCT_MFR], + &vpd_info [OPENBMC_VPD_KEY_PRODUCT_NAME], + &vpd_info [OPENBMC_VPD_KEY_PRODUCT_PART_MODEL_NUM], + &vpd_info [OPENBMC_VPD_KEY_PRODUCT_VER], + &vpd_info [OPENBMC_VPD_KEY_PRODUCT_SERIAL_NUM], + &vpd_info [OPENBMC_VPD_KEY_PRODUCT_ASSET_TAG], + &vpd_info [OPENBMC_VPD_KEY_PRODUCT_FRU_FILE_ID], + NULL, 0); + } + + if (chdr->multirec) + { + fru_area_info [ IPMI_FRU_AREA_MULTI_RECORD ].len = 8*(*(hdr+8*chdr->multirec+1)); + /* TODO: Parse multi record area */ + } + + for (i=0; i<IPMI_FRU_AREA_TYPE_MAX; i++) + { +#if IPMI_FRU_PARSER_DEBUG + printf ("IPMI_FRU_AREA_TYPE=[%d] : Offset=[%d] : Len=[%d]\n", i, fru_area_info [i].off, fru_area_info[i].len); +#else +; +#endif + } + + /* Populate VPD Table */ + for (i=1; i<OPENBMC_VPD_KEY_MAX; i++) + { + if (i==OPENBMC_VPD_KEY_CHASSIS_TYPE) + { + sd_bus_message_append (vpdtbl, "{sv}", vpd_key_names[i], "y", chassis_type); +#if IPMI_FRU_PARSER_DEBUG + printf ("[%s] = [%d]\n", vpd_key_names[i], chassis_type); +#else +; +#endif + continue; + } + + if (i==OPENBMC_VPD_KEY_BOARD_MFG_DATE) + { + sd_bus_message_append (vpdtbl, "sa{y}", vpd_key_names[i], mfg_date_time); +#if IPMI_FRU_PARSER_DEBUG + printf ("[%s] = [%d]\n", vpd_key_names[i], mfg_date_time); +#else +; +#endif + continue; + } + + /* FIXME: Field type encoding *ASSUMED* to be *BINARY* */ + ipmi_fru_field_str = (unsigned char*) &(vpd_info[i].type_length_field) + 1; + sd_bus_message_append (vpdtbl, "{sv}", vpd_key_names[i], "s", ipmi_fru_field_str); + if (vpd_info[i].type_length_field_length) + { +#if IPMI_FRU_PARSER_DEBUG + printf ("[%s] = [%s]\n", vpd_key_names[i], ipmi_fru_field_str); +#else +; +#endif + } + } + + out: + rv = 0; + cleanup: + return (rv); +} + +int parse_fru_area (const uint8_t area, const void* msgbuf, const uint8_t len, sd_bus_message* vpdtbl) +{ + int ret = 0; + int rv = -1; + int i = 0; + ipmi_fru_area_info_t fru_area_info [ IPMI_FRU_AREA_TYPE_MAX ]; + ipmi_fru_common_hdr_t* chdr = NULL; + uint8_t* hdr = NULL; + const uint8_t* ipmi_fru_field_str=NULL; + + + ipmi_fru_field_t vpd_info [ OPENBMC_VPD_KEY_MAX ]; + for (i=0; i<OPENBMC_VPD_KEY_MAX; i++) + { + memset (vpd_info[i].type_length_field, '\0', IPMI_FRU_AREA_TYPE_LENGTH_FIELD_MAX); + vpd_info[i].type_length_field_length = 0; + } + + /* Chassis */ + uint8_t chassis_type; + + /* Board */ + uint32_t mfg_date_time; + + /* Product */ + unsigned int product_custom_fields_len; + + ASSERT (msgbuf); + ASSERT (vpdtbl); + + switch (area) + { + case IPMI_FRU_AREA_CHASSIS_INFO: + ipmi_fru_chassis_info_area (msgbuf, + len, + &chassis_type, + &vpd_info [OPENBMC_VPD_KEY_CHASSIS_PART_NUM], + &vpd_info [OPENBMC_VPD_KEY_CHASSIS_SERIAL_NUM], + NULL, 0); + + /* Populate VPD Table */ + for (i=1; i<=OPENBMC_VPD_KEY_CHASSIS_MAX; i++) + { + if (i==OPENBMC_VPD_KEY_CHASSIS_TYPE) + { + sd_bus_message_append (vpdtbl, "{sv}", vpd_key_names[i], "y", chassis_type); +#if IPMI_FRU_PARSER_DEBUG + printf ("Chassis : [%s] = [%d]\n", vpd_key_names[i], chassis_type); +#else +; +#endif + continue; + } + ipmi_fru_field_str = (unsigned char*) &(vpd_info[i].type_length_field) + 1; + sd_bus_message_append (vpdtbl, "{sv}", vpd_key_names[i], "s", ipmi_fru_field_str); +#if IPMI_FRU_PARSER_DEBUG + printf ("Chassis : [%s] = [%s]\n", vpd_key_names[i], ipmi_fru_field_str); +#else +; +#endif + } + break; + case IPMI_FRU_AREA_BOARD_INFO: + ipmi_fru_board_info_area (msgbuf, + len, + NULL, + &mfg_date_time, + &vpd_info [OPENBMC_VPD_KEY_BOARD_MFR], + &vpd_info [OPENBMC_VPD_KEY_BOARD_NAME], + &vpd_info [OPENBMC_VPD_KEY_BOARD_SERIAL_NUM], + &vpd_info [OPENBMC_VPD_KEY_BOARD_PART_NUM], + &vpd_info [OPENBMC_VPD_KEY_BOARD_FRU_FILE_ID], + NULL, 0); + + /* Populate VPD Table */ + for (i=OPENBMC_VPD_KEY_BOARD_MFR; i<=OPENBMC_VPD_KEY_BOARD_MAX; i++) + { + if (i==OPENBMC_VPD_KEY_BOARD_MFG_DATE) + { + sd_bus_message_append (vpdtbl, "sa{y}", vpd_key_names[i], mfg_date_time); +#if IPMI_FRU_PARSER_DEBUG + printf ("Board : [%s] = [%d]\n", vpd_key_names[i], mfg_date_time); +#else +; +#endif + continue; + } + ipmi_fru_field_str = (unsigned char*) &(vpd_info[i].type_length_field) + 1; + sd_bus_message_append (vpdtbl, "{sv}", vpd_key_names[i], "s", ipmi_fru_field_str); +#if IPMI_FRU_PARSER_DEBUG + printf ("Board : [%s] = [%s]\n", vpd_key_names[i], ipmi_fru_field_str); +#else +; +#endif + } + break; + case IPMI_FRU_AREA_PRODUCT_INFO: + ipmi_fru_product_info_area (msgbuf, + len, + NULL, + &vpd_info [OPENBMC_VPD_KEY_PRODUCT_MFR], + &vpd_info [OPENBMC_VPD_KEY_PRODUCT_NAME], + &vpd_info [OPENBMC_VPD_KEY_PRODUCT_PART_MODEL_NUM], + &vpd_info [OPENBMC_VPD_KEY_PRODUCT_VER], + &vpd_info [OPENBMC_VPD_KEY_PRODUCT_SERIAL_NUM], + &vpd_info [OPENBMC_VPD_KEY_PRODUCT_ASSET_TAG], + &vpd_info [OPENBMC_VPD_KEY_PRODUCT_FRU_FILE_ID], + NULL, 0); + for (i=OPENBMC_VPD_KEY_PRODUCT_MFR; i<=OPENBMC_VPD_KEY_PRODUCT_MAX; i++) + { + ipmi_fru_field_str = (unsigned char*) &(vpd_info[i].type_length_field) + 1; + sd_bus_message_append (vpdtbl, "{sv}", vpd_key_names[i], "s", ipmi_fru_field_str); +#if IPMI_FRU_PARSER_DEBUG + printf ("Product : [%s] = [%s]\n", vpd_key_names[i], ipmi_fru_field_str); +#else +; +#endif + } + break; + defualt: + /* TODO: Parse Multi Rec / Internal use area */ + break; + } + + out: + rv = 0; + cleanup: + return (rv); +} @@ -3,5 +3,16 @@ /* Parse an IPMI write fru data message into a dictionary containing name value pair of VPD entries.*/ int parse_fru (const void* msgbuf, sd_bus_message* vpdtbl); +int parse_fru_area (const uint8_t area, const void* msgbuf, const uint8_t len, sd_bus_message* vpdtbl); + +enum openbmc_ipmi_fru_area_type +{ + IPMI_FRU_AREA_INTERNAL_USE = 0x00, + IPMI_FRU_AREA_CHASSIS_INFO, + IPMI_FRU_AREA_BOARD_INFO, + IPMI_FRU_AREA_PRODUCT_INFO, + IPMI_FRU_AREA_MULTI_RECORD, + IPMI_FRU_AREA_TYPE_MAX +} #endif |