From 82ab3e19d58d56edcc2c6a1193a7ff38e310ef76 Mon Sep 17 00:00:00 2001 From: Ratan Gupta Date: Fri, 27 Jan 2017 23:09:16 +0530 Subject: Change c->cpp. It is required as we would be using c++ containers. Change-Id: Ifefc468ac7b2e94d1b9948a6aeb7f8d42d8e6555 Signed-off-by: Ratan Gupta --- Makefile.am | 2 +- frup.c | 1126 ----------------------------------------------------------- frup.cpp | 1126 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1127 insertions(+), 1127 deletions(-) delete mode 100644 frup.c create mode 100644 frup.cpp diff --git a/Makefile.am b/Makefile.am index 49c254d..288ac61 100644 --- a/Makefile.am +++ b/Makefile.am @@ -12,7 +12,7 @@ phosphor_read_eeprom_LDADD = libwritefrudata.la libwritefrudatadir = ${libdir} libwritefrudata_LTLIBRARIES = libwritefrudata.la -libwritefrudata_la_SOURCES = frup.c writefrudata.cpp +libwritefrudata_la_SOURCES = frup.cpp writefrudata.cpp libwritefrudata_la_LDFLAGS = $(SYSTEMD_LIBS) $(libmapper_LIBS) -version-info 1:0:0 -shared libwritefrudata_la_CXXFLAGS = $(SYSTEMD_CFLAGS) $(libmapper_CFLAGS) diff --git a/frup.c b/frup.c deleted file mode 100644 index 1740f10..0000000 --- a/frup.c +++ /dev/null @@ -1,1126 +0,0 @@ -/* - * Copyright (C) 2003-2014 FreeIPMI Core Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -/*****************************************************************************\ - * Copyright (C) 2007-2014 Lawrence Livermore National Security, LLC. - * Copyright (C) 2007 The Regents of the University of California. - * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). - * Written by Albert Chu - * UCRL-CODE-232183 - * - * This file is part of Ipmi-fru, a tool used for retrieving - * motherboard field replaceable unit (FRU) information. For details, - * see http://www.llnl.gov/linux/. - * - * Ipmi-fru is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 3 of the License, or (at your - * option) any later version. - * - * Ipmi-fru is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License along - * with Ipmi-fru. If not, see . -\*****************************************************************************/ -#include -#include -#include -#include -#include -#include -#include - -#define uint8_t unsigned char -#define uint32_t unsigned int - -#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 -#define IPMI_FRU_TYPE_LENGTH_TYPE_CODE_MASK 0xC0 -#define IPMI_FRU_TYPE_LENGTH_TYPE_CODE_SHIFT 0x06 -#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 -{ - uint8_t type_length_field[IPMI_FRU_AREA_TYPE_LENGTH_FIELD_MAX]; - /* store length of data stored in buffer */ - unsigned int type_length_field_length; -}; - -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_CUSTOM1, - OPENBMC_VPD_KEY_CHASSIS_CUSTOM2, - OPENBMC_VPD_KEY_CHASSIS_CUSTOM3, - OPENBMC_VPD_KEY_CHASSIS_CUSTOM4, - OPENBMC_VPD_KEY_CHASSIS_CUSTOM5, - OPENBMC_VPD_KEY_CHASSIS_CUSTOM6, - OPENBMC_VPD_KEY_CHASSIS_CUSTOM7, - OPENBMC_VPD_KEY_CHASSIS_CUSTOM8, - OPENBMC_VPD_KEY_CHASSIS_MAX = OPENBMC_VPD_KEY_CHASSIS_CUSTOM8, - /* 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_CUSTOM1, - OPENBMC_VPD_KEY_BOARD_CUSTOM2, - OPENBMC_VPD_KEY_BOARD_CUSTOM3, - OPENBMC_VPD_KEY_BOARD_CUSTOM4, - OPENBMC_VPD_KEY_BOARD_CUSTOM5, - OPENBMC_VPD_KEY_BOARD_CUSTOM6, - OPENBMC_VPD_KEY_BOARD_CUSTOM7, - OPENBMC_VPD_KEY_BOARD_CUSTOM8, - OPENBMC_VPD_KEY_BOARD_MAX = OPENBMC_VPD_KEY_BOARD_CUSTOM8, - /* 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_CUSTOM1, - OPENBMC_VPD_KEY_PRODUCT_CUSTOM2, - OPENBMC_VPD_KEY_PRODUCT_CUSTOM3, - OPENBMC_VPD_KEY_PRODUCT_CUSTOM4, - OPENBMC_VPD_KEY_PRODUCT_CUSTOM5, - OPENBMC_VPD_KEY_PRODUCT_CUSTOM6, - OPENBMC_VPD_KEY_PRODUCT_CUSTOM7, - OPENBMC_VPD_KEY_PRODUCT_CUSTOM8, - OPENBMC_VPD_KEY_PRODUCT_MAX = OPENBMC_VPD_KEY_PRODUCT_CUSTOM8, - - OPENBMC_VPD_KEY_MAX, - OPENBMC_VPD_KEY_CUSTOM_FIELDS_MAX=8, - -}; - -const char* vpd_key_names [] = -{ - "Key Names Table Start", - "Type", /*OPENBMC_VPD_KEY_CHASSIS_TYPE*/ - "Part Number", /*OPENBMC_VPD_KEY_CHASSIS_PART_NUM,*/ - "Serial Number", /*OPENBMC_VPD_KEY_CHASSIS_SERIAL_NUM,*/ - "Custom Field 1", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM1,*/ - "Custom Field 2", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM2,*/ - "Custom Field 3", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM3,*/ - "Custom Field 4", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM4,*/ - "Custom Field 5", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM5,*/ - "Custom Field 6", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM6,*/ - "Custom Field 7", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM7,*/ - "Custom Field 8", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM8,*/ - - "Mfg Date", /* OPENBMC_VPD_KEY_BOARD_MFG_DATE, */ /* not a type/len */ - "Manufacturer", /* OPENBMC_VPD_KEY_BOARD_MFR, */ - "Name", /* OPENBMC_VPD_KEY_BOARD_NAME, */ - "Serial Number", /* OPENBMC_VPD_KEY_BOARD_SERIAL_NUM, */ - "Part Number", /* OPENBMC_VPD_KEY_BOARD_PART_NUM, */ - "FRU File ID", /* OPENBMC_VPD_KEY_BOARD_FRU_FILE_ID, */ - "Custom Field 1", /*OPENBMC_VPD_KEY_BOARD_CUSTOM1,*/ - "Custom Field 2", /*OPENBMC_VPD_KEY_BOARD_CUSTOM2,*/ - "Custom Field 3", /*OPENBMC_VPD_KEY_BOARD_CUSTOM3,*/ - "Custom Field 4", /*OPENBMC_VPD_KEY_BOARD_CUSTOM4,*/ - "Custom Field 5", /*OPENBMC_VPD_KEY_BOARD_CUSTOM5,*/ - "Custom Field 6", /*OPENBMC_VPD_KEY_BOARD_CUSTOM6,*/ - "Custom Field 7", /*OPENBMC_VPD_KEY_BOARD_CUSTOM7,*/ - "Custom Field 8", /*OPENBMC_VPD_KEY_BOARD_CUSTOM8,*/ - - "Manufacturer", /* OPENBMC_VPD_KEY_PRODUCT_MFR, */ - "Name", /* OPENBMC_VPD_KEY_PRODUCT_NAME, */ - "Model Number", /* OPENBMC_VPD_KEY_PRODUCT_PART_MODEL_NUM, */ - "Version", /* OPENBMC_VPD_KEY_PRODUCT_VER, */ - "Serial Number", /* OPENBMC_VPD_KEY_PRODUCT_SERIAL_NUM, */ - "Asset Tag", /* OPENBMC_VPD_KEY_PRODUCT_ASSET_TAG, */ - "FRU File ID", /* OPENBMC_VPD_KEY_PRODUCT_FRU_FILE_ID, */ - "Custom Field 1", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM1,*/ - "Custom Field 2", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM2,*/ - "Custom Field 3", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM3,*/ - "Custom Field 4", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM4,*/ - "Custom Field 5", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM5,*/ - "Custom Field 6", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM6,*/ - "Custom Field 7", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM7,*/ - "Custom Field 8", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM8,*/ - - "Key Names Table End" /*OPENBMC_VPD_KEY_MAX,*/ -}; - - -/* - * -------------------------------------------------------------------- - * - * -------------------------------------------------------------------- - */ - -static size_t _to_time_str (uint32_t mfg_date_time, char* timestr, uint32_t len) -{ - struct tm tm; - time_t t; - size_t s; - - ASSERT (timestr); - ASSERT (len); - - memset (&tm, '\0', sizeof (struct tm)); - - t = mfg_date_time; - gmtime_r (&t, &tm); - s = strftime (timestr, len, "%F - %H:%M:%S", &tm); - - return s; -} - -/* private method to parse type/length */ -static int -_parse_type_length (const void *areabuf, - unsigned int areabuflen, - unsigned int current_area_offset, - uint8_t *number_of_data_bytes, - ipmi_fru_field_t *field) -{ - const uint8_t *areabufptr = (const uint8_t*) areabuf; - uint8_t type_length; - uint8_t type_code; - - ASSERT (areabuf); - ASSERT (areabuflen); - ASSERT (number_of_data_bytes); - - type_length = areabufptr[current_area_offset]; - - /* ipmi workaround - * - * 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 - * motherboard, some areas list this, indicating that there is - * 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. - */ - - ASSERT (type_length != IPMI_FRU_SENTINEL_VALUE); - - 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). - */ - if (type_code == IPMI_FRU_TYPE_LENGTH_TYPE_CODE_LANGUAGE_CODE - && (*number_of_data_bytes) == 0x01) - { - return (-1); - } - - if ((current_area_offset + 1 + (*number_of_data_bytes)) > areabuflen) - { - return (-1); - } - - if (field) - { - memset (field->type_length_field, - '\0', - IPMI_FRU_AREA_TYPE_LENGTH_FIELD_MAX); - memcpy (field->type_length_field, - &areabufptr[current_area_offset], - 1 + (*number_of_data_bytes)); - field->type_length_field_length = 1 + (*number_of_data_bytes); - } - - return (0); -} - -int -ipmi_fru_chassis_info_area (const void *areabuf, - unsigned int areabuflen, - uint8_t *chassis_type, - ipmi_fru_field_t *chassis_part_number, - ipmi_fru_field_t *chassis_serial_number, - ipmi_fru_field_t *chassis_custom_fields, - unsigned int chassis_custom_fields_len) -{ - const uint8_t *areabufptr = (const uint8_t*) areabuf; - unsigned int area_offset = 0; - unsigned int custom_fields_index = 0; - uint8_t number_of_data_bytes; - int rv = -1; - - if (!areabuf || !areabuflen) - { - return (-1); - } - - if (chassis_part_number) - memset (chassis_part_number, - '\0', - sizeof (ipmi_fru_field_t)); - if (chassis_serial_number) - memset (chassis_serial_number, - '\0', - sizeof (ipmi_fru_field_t)); - if (chassis_custom_fields && chassis_custom_fields_len) - memset (chassis_custom_fields, - '\0', - sizeof (ipmi_fru_field_t) * chassis_custom_fields_len); - - if (chassis_type) - (*chassis_type) = areabufptr[area_offset]; - area_offset++; - - if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) - goto out; - - if (_parse_type_length (areabufptr, - areabuflen, - area_offset, - &number_of_data_bytes, - chassis_part_number) < 0) - goto cleanup; - area_offset += 1; /* type/length byte */ - area_offset += number_of_data_bytes; - - if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) - goto out; - - if (_parse_type_length (areabufptr, - areabuflen, - area_offset, - &number_of_data_bytes, - chassis_serial_number) < 0) - goto cleanup; - area_offset += 1; /* type/length byte */ - area_offset += number_of_data_bytes; - - while (area_offset < areabuflen - && areabufptr[area_offset] != IPMI_FRU_SENTINEL_VALUE) - { - ipmi_fru_field_t *field_ptr = NULL; - - if (chassis_custom_fields && chassis_custom_fields_len) - { - if (custom_fields_index < chassis_custom_fields_len) - field_ptr = &chassis_custom_fields[custom_fields_index]; - else - { - goto cleanup; - } - } - - if (_parse_type_length (areabufptr, - areabuflen, - area_offset, - &number_of_data_bytes, - field_ptr) < 0) - goto cleanup; - - area_offset += 1; /* type/length byte */ - area_offset += number_of_data_bytes; - custom_fields_index++; - } - - - out: - rv = 0; - cleanup: - return (rv); -} - -int -ipmi_fru_board_info_area (const void *areabuf, - unsigned int areabuflen, - uint8_t *language_code, - uint32_t *mfg_date_time, - ipmi_fru_field_t *board_manufacturer, - ipmi_fru_field_t *board_product_name, - ipmi_fru_field_t *board_serial_number, - ipmi_fru_field_t *board_part_number, - ipmi_fru_field_t *board_fru_file_id, - ipmi_fru_field_t *board_custom_fields, - unsigned int board_custom_fields_len) -{ - const uint8_t *areabufptr = (const uint8_t*) areabuf; - uint32_t mfg_date_time_tmp = 0; - unsigned int area_offset = 0; - unsigned int custom_fields_index = 0; - uint8_t number_of_data_bytes; - int rv = -1; - - if (!areabuf || !areabuflen) - { - return (-1); - } - - if (board_manufacturer) - memset (board_manufacturer, - '\0', - sizeof (ipmi_fru_field_t)); - if (board_product_name) - memset (board_product_name, - '\0', - sizeof (ipmi_fru_field_t)); - if (board_serial_number) - memset (board_serial_number, - '\0', - sizeof (ipmi_fru_field_t)); - if (board_part_number) - memset (board_part_number, - '\0', - sizeof (ipmi_fru_field_t)); - if (board_fru_file_id) - memset (board_fru_file_id, - '\0', - sizeof (ipmi_fru_field_t)); - if (board_custom_fields && board_custom_fields_len) - memset (board_custom_fields, - '\0', - sizeof (ipmi_fru_field_t) * board_custom_fields_len); - - if (language_code) - (*language_code) = areabufptr[area_offset]; - area_offset++; - - if (mfg_date_time) - { - struct tm tm; - time_t t; - - /* mfg_date_time is little endian - see spec */ - mfg_date_time_tmp |= areabufptr[area_offset]; - area_offset++; - mfg_date_time_tmp |= (areabufptr[area_offset] << 8); - area_offset++; - mfg_date_time_tmp |= (areabufptr[area_offset] << 16); - area_offset++; - - /* 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 - * 'struct tm', thus passing 'struct tm' between functions could - * have issues. so we need to memset. - */ - memset (&tm, '\0', sizeof(struct tm)); - - /* in fru, epoch is 0:00 hrs 1/1/96 - * - * so convert into ansi epoch - */ - - tm.tm_year = 96; /* years since 1900 */ - tm.tm_mon = 0; /* months since january */ - tm.tm_mday = 1; /* 1-31 */ - tm.tm_hour = 0; - tm.tm_min = 0; - tm.tm_sec = 0; - tm.tm_isdst = -1; - - if ((t = mktime (&tm)) == (time_t)-1) - { - goto cleanup; - } - - mfg_date_time_tmp += (uint32_t)t; - (*mfg_date_time) = mfg_date_time_tmp; - } - else - area_offset += 3; - - if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) - goto out; - - if (_parse_type_length (areabufptr, - areabuflen, - area_offset, - &number_of_data_bytes, - board_manufacturer) < 0) - goto cleanup; - area_offset += 1; /* type/length byte */ - area_offset += number_of_data_bytes; - - if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) - goto out; - - if (_parse_type_length (areabufptr, - areabuflen, - area_offset, - &number_of_data_bytes, - board_product_name) < 0) - goto cleanup; - area_offset += 1; /* type/length byte */ - area_offset += number_of_data_bytes; - - if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) - goto out; - - if (_parse_type_length (areabufptr, - areabuflen, - area_offset, - &number_of_data_bytes, - board_serial_number) < 0) - goto cleanup; - area_offset += 1; /* type/length byte */ - area_offset += number_of_data_bytes; - - if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) - goto out; - - if (_parse_type_length (areabufptr, - areabuflen, - area_offset, - &number_of_data_bytes, - board_part_number) < 0) - goto cleanup; - area_offset += 1; /* type/length byte */ - area_offset += number_of_data_bytes; - - if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) - goto out; - - if (_parse_type_length (areabufptr, - areabuflen, - area_offset, - &number_of_data_bytes, - board_fru_file_id) < 0) - goto cleanup; - area_offset += 1; /* type/length byte */ - area_offset += number_of_data_bytes; - - while (area_offset < areabuflen - && areabufptr[area_offset] != IPMI_FRU_SENTINEL_VALUE) - { - ipmi_fru_field_t *field_ptr = NULL; - - if (board_custom_fields && board_custom_fields_len) - { - if (custom_fields_index < board_custom_fields_len) - field_ptr = &board_custom_fields[custom_fields_index]; - else - { - goto cleanup; - } - } - - if (_parse_type_length (areabufptr, - areabuflen, - area_offset, - &number_of_data_bytes, - field_ptr) < 0) - goto cleanup; - - area_offset += 1; /* type/length byte */ - area_offset += number_of_data_bytes; - custom_fields_index++; - } - - out: - rv = 0; - cleanup: - return (rv); -} - -int -ipmi_fru_product_info_area (const void *areabuf, - unsigned int areabuflen, - uint8_t *language_code, - ipmi_fru_field_t *product_manufacturer_name, - ipmi_fru_field_t *product_name, - ipmi_fru_field_t *product_part_model_number, - ipmi_fru_field_t *product_version, - ipmi_fru_field_t *product_serial_number, - ipmi_fru_field_t *product_asset_tag, - ipmi_fru_field_t *product_fru_file_id, - ipmi_fru_field_t *product_custom_fields, - unsigned int product_custom_fields_len) -{ - const uint8_t *areabufptr = (const uint8_t*) areabuf; - unsigned int area_offset = 0; - unsigned int custom_fields_index = 0; - uint8_t number_of_data_bytes; - int rv = -1; - - if (!areabuf || !areabuflen) - { - return (-1); - } - - if (product_manufacturer_name) - memset (product_manufacturer_name, - '\0', - sizeof (ipmi_fru_field_t)); - if (product_name) - memset (product_name, - '\0', - sizeof (ipmi_fru_field_t)); - if (product_part_model_number) - memset (product_part_model_number, - '\0', - sizeof (ipmi_fru_field_t)); - if (product_version) - memset (product_version, - '\0', - sizeof (ipmi_fru_field_t)); - if (product_serial_number) - memset (product_serial_number, - '\0', - sizeof (ipmi_fru_field_t)); - if (product_asset_tag) - memset (product_asset_tag, - '\0', - sizeof (ipmi_fru_field_t)); - if (product_fru_file_id) - memset (product_fru_file_id, - '\0', - sizeof (ipmi_fru_field_t)); - if (product_custom_fields && product_custom_fields_len) - memset (product_custom_fields, - '\0', - sizeof (ipmi_fru_field_t) * product_custom_fields_len); - - if (language_code) - (*language_code) = areabufptr[area_offset]; - area_offset++; - - if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) - goto out; - - if (_parse_type_length (areabufptr, - areabuflen, - area_offset, - &number_of_data_bytes, - product_manufacturer_name) < 0) - goto cleanup; - area_offset += 1; /* type/length byte */ - area_offset += number_of_data_bytes; - - if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) - goto out; - - if (_parse_type_length (areabufptr, - areabuflen, - area_offset, - &number_of_data_bytes, - product_name) < 0) - goto cleanup; - area_offset += 1; /* type/length byte */ - area_offset += number_of_data_bytes; - - if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) - goto out; - - if (_parse_type_length (areabufptr, - areabuflen, - area_offset, - &number_of_data_bytes, - product_part_model_number) < 0) - goto cleanup; - area_offset += 1; /* type/length byte */ - area_offset += number_of_data_bytes; - - if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) - goto out; - - if (_parse_type_length (areabufptr, - areabuflen, - area_offset, - &number_of_data_bytes, - product_version) < 0) - goto cleanup; - area_offset += 1; /* type/length byte */ - area_offset += number_of_data_bytes; - - if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) - goto out; - - if (_parse_type_length (areabufptr, - areabuflen, - area_offset, - &number_of_data_bytes, - product_serial_number) < 0) - goto cleanup; - area_offset += 1; /* type/length byte */ - area_offset += number_of_data_bytes; - - if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) - goto out; - - if (_parse_type_length (areabufptr, - areabuflen, - area_offset, - &number_of_data_bytes, - product_asset_tag) < 0) - goto cleanup; - area_offset += 1; /* type/length byte */ - area_offset += number_of_data_bytes; - - if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) - goto out; - - if (_parse_type_length (areabufptr, - areabuflen, - area_offset, - &number_of_data_bytes, - product_fru_file_id) < 0) - goto cleanup; - - area_offset += 1; /* type/length byte */ - area_offset += number_of_data_bytes; - - while (area_offset < areabuflen - && areabufptr[area_offset] != IPMI_FRU_SENTINEL_VALUE) - { - ipmi_fru_field_t *field_ptr = NULL; - - if (product_custom_fields && product_custom_fields_len) - { - if (custom_fields_index < product_custom_fields_len) - field_ptr = &product_custom_fields[custom_fields_index]; - else - { - goto cleanup; - } - } - - if (_parse_type_length (areabufptr, - areabuflen, - area_offset, - &number_of_data_bytes, - field_ptr) < 0) - goto cleanup; - - area_offset += 1; /* type/length byte */ - area_offset += number_of_data_bytes; - custom_fields_index++; - } - - - out: - rv = 0; - cleanup: - return (rv); -} - -void _append_to_dict (uint8_t vpd_key_id, uint8_t* vpd_key_val, sd_bus_message* vpdtbl) -{ - int type_length = vpd_key_val[0]; - int type_code = (type_length & IPMI_FRU_TYPE_LENGTH_TYPE_CODE_MASK) >> IPMI_FRU_TYPE_LENGTH_TYPE_CODE_SHIFT; - int vpd_val_len = type_length & IPMI_FRU_TYPE_LENGTH_NUMBER_OF_DATA_BYTES_MASK; - int sdr=0; - - /* Needed to convert each uint8_t byte to a ascii */ - char bin_byte[3] = {0}; - - /* - * Max number of characters needed to represent 1 unsigned byte in string - * is number of bytes multipled by 2. Extra 3 for 0x and a ending '\0'; - */ - char bin_in_ascii_len = vpd_val_len * 2 + 3; - - /* Binary converted to ascii in array */ - char *bin_in_ascii = (char *)malloc(bin_in_ascii_len); - - /* For reading byte from the area */ - size_t val = 0; - - char *bin_copy = &((char *)bin_in_ascii)[2]; - - switch (type_code) - { - case 0: - memset(bin_in_ascii, 0x0, bin_in_ascii_len); - - /* Offset 1 is where actual data starts */ - for(val = 1; val <= vpd_val_len ; val++) - { - /* 2 bytes for data and 1 for terminating '\0' */ - snprintf(bin_byte, 3, "%02x", vpd_key_val[val]); - - /* Its a running string so strip off the '\0' */ - strncat(bin_copy, bin_byte, 2); - } - - /* We need the data represented as 0x...... */ - if(vpd_val_len > 0) - { - strncpy(bin_in_ascii, "0x", 2); - } - - printf ("_append_to_dict: VPD Key = [%s] : Type Code = [BINARY] : Len = [%d] : Val = [%s]\n", - vpd_key_names [vpd_key_id], vpd_val_len, bin_in_ascii); - sdr = sd_bus_message_append (vpdtbl, "{sv}", vpd_key_names[vpd_key_id], "s", bin_in_ascii); - break; - - case 3: - printf ("_append_to_dict: VPD Key = [%s] : Type Code = [ASCII+Latin] : Len = [%d] : Val = [%s]\n", vpd_key_names [vpd_key_id], vpd_val_len, &vpd_key_val[1]); - sdr = sd_bus_message_append (vpdtbl, "{sv}", vpd_key_names[vpd_key_id], "s", &vpd_key_val[1]); - break; - } - - if(bin_in_ascii) - { - free(bin_in_ascii); - bin_in_ascii = NULL; - } - - - if (sdr < 0) - { -#if IPMI_FRU_PARSER_DEBUG - printf ("_append_to_dict : sd_bus_message_append Failed [ %d ] for [%s]\n", sdr, vpd_key_names[vpd_key_id]); -#endif - } -} - -int -parse_fru (const void* msgbuf, sd_bus_message* vpdtbl) -{ - 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; - char timestr [ OPENBMC_VPD_VAL_LEN ]; - - - ipmi_fru_field_t vpd_info [ OPENBMC_VPD_KEY_MAX ]; - //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; iinternal; - 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], - &vpd_info [OPENBMC_VPD_KEY_CHASSIS_CUSTOM1], - OPENBMC_VPD_KEY_CUSTOM_FIELDS_MAX); - } - - 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], - &vpd_info [OPENBMC_VPD_KEY_BOARD_CUSTOM1], - OPENBMC_VPD_KEY_CUSTOM_FIELDS_MAX); - } - - 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], - &vpd_info [OPENBMC_VPD_KEY_PRODUCT_CUSTOM1], - OPENBMC_VPD_KEY_CUSTOM_FIELDS_MAX); - } - - 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. + * + */ +/*****************************************************************************\ + * Copyright (C) 2007-2014 Lawrence Livermore National Security, LLC. + * Copyright (C) 2007 The Regents of the University of California. + * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). + * Written by Albert Chu + * UCRL-CODE-232183 + * + * This file is part of Ipmi-fru, a tool used for retrieving + * motherboard field replaceable unit (FRU) information. For details, + * see http://www.llnl.gov/linux/. + * + * Ipmi-fru is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * Ipmi-fru is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with Ipmi-fru. If not, see . +\*****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include + +#define uint8_t unsigned char +#define uint32_t unsigned int + +#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 +#define IPMI_FRU_TYPE_LENGTH_TYPE_CODE_MASK 0xC0 +#define IPMI_FRU_TYPE_LENGTH_TYPE_CODE_SHIFT 0x06 +#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 +{ + uint8_t type_length_field[IPMI_FRU_AREA_TYPE_LENGTH_FIELD_MAX]; + /* store length of data stored in buffer */ + unsigned int type_length_field_length; +}; + +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_CUSTOM1, + OPENBMC_VPD_KEY_CHASSIS_CUSTOM2, + OPENBMC_VPD_KEY_CHASSIS_CUSTOM3, + OPENBMC_VPD_KEY_CHASSIS_CUSTOM4, + OPENBMC_VPD_KEY_CHASSIS_CUSTOM5, + OPENBMC_VPD_KEY_CHASSIS_CUSTOM6, + OPENBMC_VPD_KEY_CHASSIS_CUSTOM7, + OPENBMC_VPD_KEY_CHASSIS_CUSTOM8, + OPENBMC_VPD_KEY_CHASSIS_MAX = OPENBMC_VPD_KEY_CHASSIS_CUSTOM8, + /* 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_CUSTOM1, + OPENBMC_VPD_KEY_BOARD_CUSTOM2, + OPENBMC_VPD_KEY_BOARD_CUSTOM3, + OPENBMC_VPD_KEY_BOARD_CUSTOM4, + OPENBMC_VPD_KEY_BOARD_CUSTOM5, + OPENBMC_VPD_KEY_BOARD_CUSTOM6, + OPENBMC_VPD_KEY_BOARD_CUSTOM7, + OPENBMC_VPD_KEY_BOARD_CUSTOM8, + OPENBMC_VPD_KEY_BOARD_MAX = OPENBMC_VPD_KEY_BOARD_CUSTOM8, + /* 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_CUSTOM1, + OPENBMC_VPD_KEY_PRODUCT_CUSTOM2, + OPENBMC_VPD_KEY_PRODUCT_CUSTOM3, + OPENBMC_VPD_KEY_PRODUCT_CUSTOM4, + OPENBMC_VPD_KEY_PRODUCT_CUSTOM5, + OPENBMC_VPD_KEY_PRODUCT_CUSTOM6, + OPENBMC_VPD_KEY_PRODUCT_CUSTOM7, + OPENBMC_VPD_KEY_PRODUCT_CUSTOM8, + OPENBMC_VPD_KEY_PRODUCT_MAX = OPENBMC_VPD_KEY_PRODUCT_CUSTOM8, + + OPENBMC_VPD_KEY_MAX, + OPENBMC_VPD_KEY_CUSTOM_FIELDS_MAX=8, + +}; + +const char* vpd_key_names [] = +{ + "Key Names Table Start", + "Type", /*OPENBMC_VPD_KEY_CHASSIS_TYPE*/ + "Part Number", /*OPENBMC_VPD_KEY_CHASSIS_PART_NUM,*/ + "Serial Number", /*OPENBMC_VPD_KEY_CHASSIS_SERIAL_NUM,*/ + "Custom Field 1", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM1,*/ + "Custom Field 2", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM2,*/ + "Custom Field 3", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM3,*/ + "Custom Field 4", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM4,*/ + "Custom Field 5", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM5,*/ + "Custom Field 6", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM6,*/ + "Custom Field 7", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM7,*/ + "Custom Field 8", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM8,*/ + + "Mfg Date", /* OPENBMC_VPD_KEY_BOARD_MFG_DATE, */ /* not a type/len */ + "Manufacturer", /* OPENBMC_VPD_KEY_BOARD_MFR, */ + "Name", /* OPENBMC_VPD_KEY_BOARD_NAME, */ + "Serial Number", /* OPENBMC_VPD_KEY_BOARD_SERIAL_NUM, */ + "Part Number", /* OPENBMC_VPD_KEY_BOARD_PART_NUM, */ + "FRU File ID", /* OPENBMC_VPD_KEY_BOARD_FRU_FILE_ID, */ + "Custom Field 1", /*OPENBMC_VPD_KEY_BOARD_CUSTOM1,*/ + "Custom Field 2", /*OPENBMC_VPD_KEY_BOARD_CUSTOM2,*/ + "Custom Field 3", /*OPENBMC_VPD_KEY_BOARD_CUSTOM3,*/ + "Custom Field 4", /*OPENBMC_VPD_KEY_BOARD_CUSTOM4,*/ + "Custom Field 5", /*OPENBMC_VPD_KEY_BOARD_CUSTOM5,*/ + "Custom Field 6", /*OPENBMC_VPD_KEY_BOARD_CUSTOM6,*/ + "Custom Field 7", /*OPENBMC_VPD_KEY_BOARD_CUSTOM7,*/ + "Custom Field 8", /*OPENBMC_VPD_KEY_BOARD_CUSTOM8,*/ + + "Manufacturer", /* OPENBMC_VPD_KEY_PRODUCT_MFR, */ + "Name", /* OPENBMC_VPD_KEY_PRODUCT_NAME, */ + "Model Number", /* OPENBMC_VPD_KEY_PRODUCT_PART_MODEL_NUM, */ + "Version", /* OPENBMC_VPD_KEY_PRODUCT_VER, */ + "Serial Number", /* OPENBMC_VPD_KEY_PRODUCT_SERIAL_NUM, */ + "Asset Tag", /* OPENBMC_VPD_KEY_PRODUCT_ASSET_TAG, */ + "FRU File ID", /* OPENBMC_VPD_KEY_PRODUCT_FRU_FILE_ID, */ + "Custom Field 1", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM1,*/ + "Custom Field 2", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM2,*/ + "Custom Field 3", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM3,*/ + "Custom Field 4", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM4,*/ + "Custom Field 5", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM5,*/ + "Custom Field 6", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM6,*/ + "Custom Field 7", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM7,*/ + "Custom Field 8", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM8,*/ + + "Key Names Table End" /*OPENBMC_VPD_KEY_MAX,*/ +}; + + +/* + * -------------------------------------------------------------------- + * + * -------------------------------------------------------------------- + */ + +static size_t _to_time_str (uint32_t mfg_date_time, char* timestr, uint32_t len) +{ + struct tm tm; + time_t t; + size_t s; + + ASSERT (timestr); + ASSERT (len); + + memset (&tm, '\0', sizeof (struct tm)); + + t = mfg_date_time; + gmtime_r (&t, &tm); + s = strftime (timestr, len, "%F - %H:%M:%S", &tm); + + return s; +} + +/* private method to parse type/length */ +static int +_parse_type_length (const void *areabuf, + unsigned int areabuflen, + unsigned int current_area_offset, + uint8_t *number_of_data_bytes, + ipmi_fru_field_t *field) +{ + const uint8_t *areabufptr = (const uint8_t*) areabuf; + uint8_t type_length; + uint8_t type_code; + + ASSERT (areabuf); + ASSERT (areabuflen); + ASSERT (number_of_data_bytes); + + type_length = areabufptr[current_area_offset]; + + /* ipmi workaround + * + * 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 + * motherboard, some areas list this, indicating that there is + * 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. + */ + + ASSERT (type_length != IPMI_FRU_SENTINEL_VALUE); + + 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). + */ + if (type_code == IPMI_FRU_TYPE_LENGTH_TYPE_CODE_LANGUAGE_CODE + && (*number_of_data_bytes) == 0x01) + { + return (-1); + } + + if ((current_area_offset + 1 + (*number_of_data_bytes)) > areabuflen) + { + return (-1); + } + + if (field) + { + memset (field->type_length_field, + '\0', + IPMI_FRU_AREA_TYPE_LENGTH_FIELD_MAX); + memcpy (field->type_length_field, + &areabufptr[current_area_offset], + 1 + (*number_of_data_bytes)); + field->type_length_field_length = 1 + (*number_of_data_bytes); + } + + return (0); +} + +int +ipmi_fru_chassis_info_area (const void *areabuf, + unsigned int areabuflen, + uint8_t *chassis_type, + ipmi_fru_field_t *chassis_part_number, + ipmi_fru_field_t *chassis_serial_number, + ipmi_fru_field_t *chassis_custom_fields, + unsigned int chassis_custom_fields_len) +{ + const uint8_t *areabufptr = (const uint8_t*) areabuf; + unsigned int area_offset = 0; + unsigned int custom_fields_index = 0; + uint8_t number_of_data_bytes; + int rv = -1; + + if (!areabuf || !areabuflen) + { + return (-1); + } + + if (chassis_part_number) + memset (chassis_part_number, + '\0', + sizeof (ipmi_fru_field_t)); + if (chassis_serial_number) + memset (chassis_serial_number, + '\0', + sizeof (ipmi_fru_field_t)); + if (chassis_custom_fields && chassis_custom_fields_len) + memset (chassis_custom_fields, + '\0', + sizeof (ipmi_fru_field_t) * chassis_custom_fields_len); + + if (chassis_type) + (*chassis_type) = areabufptr[area_offset]; + area_offset++; + + if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) + goto out; + + if (_parse_type_length (areabufptr, + areabuflen, + area_offset, + &number_of_data_bytes, + chassis_part_number) < 0) + goto cleanup; + area_offset += 1; /* type/length byte */ + area_offset += number_of_data_bytes; + + if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) + goto out; + + if (_parse_type_length (areabufptr, + areabuflen, + area_offset, + &number_of_data_bytes, + chassis_serial_number) < 0) + goto cleanup; + area_offset += 1; /* type/length byte */ + area_offset += number_of_data_bytes; + + while (area_offset < areabuflen + && areabufptr[area_offset] != IPMI_FRU_SENTINEL_VALUE) + { + ipmi_fru_field_t *field_ptr = NULL; + + if (chassis_custom_fields && chassis_custom_fields_len) + { + if (custom_fields_index < chassis_custom_fields_len) + field_ptr = &chassis_custom_fields[custom_fields_index]; + else + { + goto cleanup; + } + } + + if (_parse_type_length (areabufptr, + areabuflen, + area_offset, + &number_of_data_bytes, + field_ptr) < 0) + goto cleanup; + + area_offset += 1; /* type/length byte */ + area_offset += number_of_data_bytes; + custom_fields_index++; + } + + + out: + rv = 0; + cleanup: + return (rv); +} + +int +ipmi_fru_board_info_area (const void *areabuf, + unsigned int areabuflen, + uint8_t *language_code, + uint32_t *mfg_date_time, + ipmi_fru_field_t *board_manufacturer, + ipmi_fru_field_t *board_product_name, + ipmi_fru_field_t *board_serial_number, + ipmi_fru_field_t *board_part_number, + ipmi_fru_field_t *board_fru_file_id, + ipmi_fru_field_t *board_custom_fields, + unsigned int board_custom_fields_len) +{ + const uint8_t *areabufptr = (const uint8_t*) areabuf; + uint32_t mfg_date_time_tmp = 0; + unsigned int area_offset = 0; + unsigned int custom_fields_index = 0; + uint8_t number_of_data_bytes; + int rv = -1; + + if (!areabuf || !areabuflen) + { + return (-1); + } + + if (board_manufacturer) + memset (board_manufacturer, + '\0', + sizeof (ipmi_fru_field_t)); + if (board_product_name) + memset (board_product_name, + '\0', + sizeof (ipmi_fru_field_t)); + if (board_serial_number) + memset (board_serial_number, + '\0', + sizeof (ipmi_fru_field_t)); + if (board_part_number) + memset (board_part_number, + '\0', + sizeof (ipmi_fru_field_t)); + if (board_fru_file_id) + memset (board_fru_file_id, + '\0', + sizeof (ipmi_fru_field_t)); + if (board_custom_fields && board_custom_fields_len) + memset (board_custom_fields, + '\0', + sizeof (ipmi_fru_field_t) * board_custom_fields_len); + + if (language_code) + (*language_code) = areabufptr[area_offset]; + area_offset++; + + if (mfg_date_time) + { + struct tm tm; + time_t t; + + /* mfg_date_time is little endian - see spec */ + mfg_date_time_tmp |= areabufptr[area_offset]; + area_offset++; + mfg_date_time_tmp |= (areabufptr[area_offset] << 8); + area_offset++; + mfg_date_time_tmp |= (areabufptr[area_offset] << 16); + area_offset++; + + /* 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 + * 'struct tm', thus passing 'struct tm' between functions could + * have issues. so we need to memset. + */ + memset (&tm, '\0', sizeof(struct tm)); + + /* in fru, epoch is 0:00 hrs 1/1/96 + * + * so convert into ansi epoch + */ + + tm.tm_year = 96; /* years since 1900 */ + tm.tm_mon = 0; /* months since january */ + tm.tm_mday = 1; /* 1-31 */ + tm.tm_hour = 0; + tm.tm_min = 0; + tm.tm_sec = 0; + tm.tm_isdst = -1; + + if ((t = mktime (&tm)) == (time_t)-1) + { + goto cleanup; + } + + mfg_date_time_tmp += (uint32_t)t; + (*mfg_date_time) = mfg_date_time_tmp; + } + else + area_offset += 3; + + if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) + goto out; + + if (_parse_type_length (areabufptr, + areabuflen, + area_offset, + &number_of_data_bytes, + board_manufacturer) < 0) + goto cleanup; + area_offset += 1; /* type/length byte */ + area_offset += number_of_data_bytes; + + if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) + goto out; + + if (_parse_type_length (areabufptr, + areabuflen, + area_offset, + &number_of_data_bytes, + board_product_name) < 0) + goto cleanup; + area_offset += 1; /* type/length byte */ + area_offset += number_of_data_bytes; + + if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) + goto out; + + if (_parse_type_length (areabufptr, + areabuflen, + area_offset, + &number_of_data_bytes, + board_serial_number) < 0) + goto cleanup; + area_offset += 1; /* type/length byte */ + area_offset += number_of_data_bytes; + + if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) + goto out; + + if (_parse_type_length (areabufptr, + areabuflen, + area_offset, + &number_of_data_bytes, + board_part_number) < 0) + goto cleanup; + area_offset += 1; /* type/length byte */ + area_offset += number_of_data_bytes; + + if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) + goto out; + + if (_parse_type_length (areabufptr, + areabuflen, + area_offset, + &number_of_data_bytes, + board_fru_file_id) < 0) + goto cleanup; + area_offset += 1; /* type/length byte */ + area_offset += number_of_data_bytes; + + while (area_offset < areabuflen + && areabufptr[area_offset] != IPMI_FRU_SENTINEL_VALUE) + { + ipmi_fru_field_t *field_ptr = NULL; + + if (board_custom_fields && board_custom_fields_len) + { + if (custom_fields_index < board_custom_fields_len) + field_ptr = &board_custom_fields[custom_fields_index]; + else + { + goto cleanup; + } + } + + if (_parse_type_length (areabufptr, + areabuflen, + area_offset, + &number_of_data_bytes, + field_ptr) < 0) + goto cleanup; + + area_offset += 1; /* type/length byte */ + area_offset += number_of_data_bytes; + custom_fields_index++; + } + + out: + rv = 0; + cleanup: + return (rv); +} + +int +ipmi_fru_product_info_area (const void *areabuf, + unsigned int areabuflen, + uint8_t *language_code, + ipmi_fru_field_t *product_manufacturer_name, + ipmi_fru_field_t *product_name, + ipmi_fru_field_t *product_part_model_number, + ipmi_fru_field_t *product_version, + ipmi_fru_field_t *product_serial_number, + ipmi_fru_field_t *product_asset_tag, + ipmi_fru_field_t *product_fru_file_id, + ipmi_fru_field_t *product_custom_fields, + unsigned int product_custom_fields_len) +{ + const uint8_t *areabufptr = (const uint8_t*) areabuf; + unsigned int area_offset = 0; + unsigned int custom_fields_index = 0; + uint8_t number_of_data_bytes; + int rv = -1; + + if (!areabuf || !areabuflen) + { + return (-1); + } + + if (product_manufacturer_name) + memset (product_manufacturer_name, + '\0', + sizeof (ipmi_fru_field_t)); + if (product_name) + memset (product_name, + '\0', + sizeof (ipmi_fru_field_t)); + if (product_part_model_number) + memset (product_part_model_number, + '\0', + sizeof (ipmi_fru_field_t)); + if (product_version) + memset (product_version, + '\0', + sizeof (ipmi_fru_field_t)); + if (product_serial_number) + memset (product_serial_number, + '\0', + sizeof (ipmi_fru_field_t)); + if (product_asset_tag) + memset (product_asset_tag, + '\0', + sizeof (ipmi_fru_field_t)); + if (product_fru_file_id) + memset (product_fru_file_id, + '\0', + sizeof (ipmi_fru_field_t)); + if (product_custom_fields && product_custom_fields_len) + memset (product_custom_fields, + '\0', + sizeof (ipmi_fru_field_t) * product_custom_fields_len); + + if (language_code) + (*language_code) = areabufptr[area_offset]; + area_offset++; + + if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) + goto out; + + if (_parse_type_length (areabufptr, + areabuflen, + area_offset, + &number_of_data_bytes, + product_manufacturer_name) < 0) + goto cleanup; + area_offset += 1; /* type/length byte */ + area_offset += number_of_data_bytes; + + if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) + goto out; + + if (_parse_type_length (areabufptr, + areabuflen, + area_offset, + &number_of_data_bytes, + product_name) < 0) + goto cleanup; + area_offset += 1; /* type/length byte */ + area_offset += number_of_data_bytes; + + if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) + goto out; + + if (_parse_type_length (areabufptr, + areabuflen, + area_offset, + &number_of_data_bytes, + product_part_model_number) < 0) + goto cleanup; + area_offset += 1; /* type/length byte */ + area_offset += number_of_data_bytes; + + if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) + goto out; + + if (_parse_type_length (areabufptr, + areabuflen, + area_offset, + &number_of_data_bytes, + product_version) < 0) + goto cleanup; + area_offset += 1; /* type/length byte */ + area_offset += number_of_data_bytes; + + if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) + goto out; + + if (_parse_type_length (areabufptr, + areabuflen, + area_offset, + &number_of_data_bytes, + product_serial_number) < 0) + goto cleanup; + area_offset += 1; /* type/length byte */ + area_offset += number_of_data_bytes; + + if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) + goto out; + + if (_parse_type_length (areabufptr, + areabuflen, + area_offset, + &number_of_data_bytes, + product_asset_tag) < 0) + goto cleanup; + area_offset += 1; /* type/length byte */ + area_offset += number_of_data_bytes; + + if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) + goto out; + + if (_parse_type_length (areabufptr, + areabuflen, + area_offset, + &number_of_data_bytes, + product_fru_file_id) < 0) + goto cleanup; + + area_offset += 1; /* type/length byte */ + area_offset += number_of_data_bytes; + + while (area_offset < areabuflen + && areabufptr[area_offset] != IPMI_FRU_SENTINEL_VALUE) + { + ipmi_fru_field_t *field_ptr = NULL; + + if (product_custom_fields && product_custom_fields_len) + { + if (custom_fields_index < product_custom_fields_len) + field_ptr = &product_custom_fields[custom_fields_index]; + else + { + goto cleanup; + } + } + + if (_parse_type_length (areabufptr, + areabuflen, + area_offset, + &number_of_data_bytes, + field_ptr) < 0) + goto cleanup; + + area_offset += 1; /* type/length byte */ + area_offset += number_of_data_bytes; + custom_fields_index++; + } + + + out: + rv = 0; + cleanup: + return (rv); +} + +void _append_to_dict (uint8_t vpd_key_id, uint8_t* vpd_key_val, sd_bus_message* vpdtbl) +{ + int type_length = vpd_key_val[0]; + int type_code = (type_length & IPMI_FRU_TYPE_LENGTH_TYPE_CODE_MASK) >> IPMI_FRU_TYPE_LENGTH_TYPE_CODE_SHIFT; + int vpd_val_len = type_length & IPMI_FRU_TYPE_LENGTH_NUMBER_OF_DATA_BYTES_MASK; + int sdr=0; + + /* Needed to convert each uint8_t byte to a ascii */ + char bin_byte[3] = {0}; + + /* + * Max number of characters needed to represent 1 unsigned byte in string + * is number of bytes multipled by 2. Extra 3 for 0x and a ending '\0'; + */ + char bin_in_ascii_len = vpd_val_len * 2 + 3; + + /* Binary converted to ascii in array */ + char *bin_in_ascii = (char *)malloc(bin_in_ascii_len); + + /* For reading byte from the area */ + size_t val = 0; + + char *bin_copy = &((char *)bin_in_ascii)[2]; + + switch (type_code) + { + case 0: + memset(bin_in_ascii, 0x0, bin_in_ascii_len); + + /* Offset 1 is where actual data starts */ + for(val = 1; val <= vpd_val_len ; val++) + { + /* 2 bytes for data and 1 for terminating '\0' */ + snprintf(bin_byte, 3, "%02x", vpd_key_val[val]); + + /* Its a running string so strip off the '\0' */ + strncat(bin_copy, bin_byte, 2); + } + + /* We need the data represented as 0x...... */ + if(vpd_val_len > 0) + { + strncpy(bin_in_ascii, "0x", 2); + } + + printf ("_append_to_dict: VPD Key = [%s] : Type Code = [BINARY] : Len = [%d] : Val = [%s]\n", + vpd_key_names [vpd_key_id], vpd_val_len, bin_in_ascii); + sdr = sd_bus_message_append (vpdtbl, "{sv}", vpd_key_names[vpd_key_id], "s", bin_in_ascii); + break; + + case 3: + printf ("_append_to_dict: VPD Key = [%s] : Type Code = [ASCII+Latin] : Len = [%d] : Val = [%s]\n", vpd_key_names [vpd_key_id], vpd_val_len, &vpd_key_val[1]); + sdr = sd_bus_message_append (vpdtbl, "{sv}", vpd_key_names[vpd_key_id], "s", &vpd_key_val[1]); + break; + } + + if(bin_in_ascii) + { + free(bin_in_ascii); + bin_in_ascii = NULL; + } + + + if (sdr < 0) + { +#if IPMI_FRU_PARSER_DEBUG + printf ("_append_to_dict : sd_bus_message_append Failed [ %d ] for [%s]\n", sdr, vpd_key_names[vpd_key_id]); +#endif + } +} + +int +parse_fru (const void* msgbuf, sd_bus_message* vpdtbl) +{ + 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; + char timestr [ OPENBMC_VPD_VAL_LEN ]; + + + ipmi_fru_field_t vpd_info [ OPENBMC_VPD_KEY_MAX ]; + //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; iinternal; + 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], + &vpd_info [OPENBMC_VPD_KEY_CHASSIS_CUSTOM1], + OPENBMC_VPD_KEY_CUSTOM_FIELDS_MAX); + } + + 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], + &vpd_info [OPENBMC_VPD_KEY_BOARD_CUSTOM1], + OPENBMC_VPD_KEY_CUSTOM_FIELDS_MAX); + } + + 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], + &vpd_info [OPENBMC_VPD_KEY_PRODUCT_CUSTOM1], + OPENBMC_VPD_KEY_CUSTOM_FIELDS_MAX); + } + + 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