diff --git a/fru.c b/fru.c index 0871ed2..09a846d 100644 --- a/fru.c +++ b/fru.c @@ -39,12 +39,29 @@ static bool autodetect = true; +const char* enc_names[TOTAL_FIELD_TYPES] = { + [FIELD_TYPE_AUTO] = "auto", + [FIELD_TYPE_BINARY] = "binary", + [FIELD_TYPE_BCDPLUS] = "bcdplus", + [FIELD_TYPE_SIXBITASCII] = "sixbitascii", + [FIELD_TYPE_TEXT] = "text" +}; + void fru_set_autodetect(bool enable) { autodetect = enable; } -static time_t epoch_seconds_1996() { +/** + * Get the FRU date/time base in seconds since UNIX Epoch + * + * According to IPMI FRU Information Storage Definition v1.0, rev 1.3, + * the date/time encoded as zero designates "0:00 hrs 1/1/96", + * see Table 11-1 "BOARD INFO AREA" + * + * @returns The number of seconds from UNIX Epoch to the FRU date/time base + */ +static time_t fru_datetime_base() { struct tm tm_1996 = { .tm_year = 96, .tm_mon = 0, @@ -230,13 +247,18 @@ static fru_field_t *fru_encode_6bit(const unsigned char *s /**< [in] Input strin } /** - * Decode a 6-bit ASCII string + * @brief Decode a 6-bit ASCII string. * - * Return false if there were errors during decoding and true otherwise. + * @param[in] field Field to decode. + * @param[out] out Buffer to decode into. + * @param[in] out_len Length of output buffer. + * @retval true Success. + * @retval false Failure. */ -static bool fru_decode_6bit(const fru_field_t *field, - uint8_t *out, //< [out] buffer to decode into - size_t out_len) //< [in] length of output buffer +static +bool fru_decode_6bit(const fru_field_t *field, + uint8_t *out, + size_t out_len) { const unsigned char *s6; int len, len6bit; @@ -251,7 +273,6 @@ static bool fru_decode_6bit(const fru_field_t *field, if (out_len < (len + 1)) { return false; } - DEBUG("Allocated a destination buffer at %p\n", out); for(i = 0, i6 = 0; i6 <= len6bit && i < len && s6[i6]; i++) { int base = i / 4; @@ -290,16 +311,23 @@ static bool fru_decode_6bit(const fru_field_t *field, } /** - * Decode BCDPLUS string + * @brief Decode BCDPLUS string. * - * Return false if there were errors during decoding and true otherwise. + * @param[in] field Field to decode. + * @param[out] out Buffer to decode into. + * @param[in] out_len Length of output buffer. + * @retval true Success. + * @retval false Failure. */ -static bool fru_decode_bcdplus(const fru_field_t *field, - uint8_t *out, //< [out] buffer to decode into - size_t out_len) //< [in] length of output buffer +static +bool fru_decode_bcdplus(const fru_field_t *field, + uint8_t *out, + size_t out_len) { int i; uint8_t c; + if (out_len < 2 * FRU_FIELDDATALEN(field->typelen) + 1) + return false; /* Copy the data and pack it as BCD */ for (i = 0; i < 2 * FRU_FIELDDATALEN(field->typelen); i++) { c = (field->data[i / 2] >> ((i % 2) ? 0 : 4)) & 0x0F; @@ -313,12 +341,20 @@ static bool fru_decode_bcdplus(const fru_field_t *field, case 0xC: out[i] = '.'; break; + case 0xD: + out[i] = '?'; + break; + case 0xE: + out[i] = '?'; + break; + case 0xF: + out[i] = '?'; + break; default: // Digits out[i] = c + '0'; } } out[2 * FRU_FIELDDATALEN(field->typelen)] = 0; // Terminate the string - out_len = 2 * FRU_FIELDDATALEN(field->typelen) + 1; // Strip trailing spaces that may have emerged when a string of odd // length was BCD-encoded. cut_tail(out); @@ -327,13 +363,18 @@ static bool fru_decode_bcdplus(const fru_field_t *field, } /** - * Get binary value string representation + * @brief Get a hex string representation of the supplied binary field. * - * Return false if there were errors during decoding and true otherwise. + * @param[in] field Field to decode. + * @param[out] out Buffer to decode into. + * @param[in] out_len Length of output buffer. + * @retval true Success. + * @retval false Failure. */ -static bool fru_decode_binary(const fru_field_t *field, - uint8_t *out, //< [out] buffer to decode into - size_t out_len) //< [in] length of output buffer +static +bool fru_decode_binary(const fru_field_t *field, + uint8_t *out, + size_t out_len) { int i; uint8_t c; @@ -343,9 +384,9 @@ static bool fru_decode_binary(const fru_field_t *field, for (i = 0; i < FRU_FIELDDATALEN(field->typelen); i++) { c = (field->data[i] & 0xf0) >> 4; - out[2 * i] = c > 9? c + 97 - 10: c + 48; + out[2 * i] = c > 9? c - 10 + 'A': c + '0'; c = field->data[i] & 0xf; - out[2 * i + 1] = c > 9? c + 97 - 10: c + 48; + out[2 * i + 1] = c > 9? c - 10 + 'A': c + '0'; } out[i * 2 + 1] = '0'; @@ -353,7 +394,13 @@ static bool fru_decode_binary(const fru_field_t *field, } /** - * Allocate a buffer and encode that data as per FRU specification + * @brief Allocate a buffer and encode that data as per FRU specification + * + * @param[in] len Buffer length. + * @param[in] data Binary buffer. + * @param[in] out_len Length of output buffer. + * @retval NULL Failure. + * @return Encoded field. */ fru_field_t * fru_encode_data(int len, const uint8_t *data) { @@ -542,7 +589,7 @@ fru_info_area_t *fru_create_info_area(fru_area_type_t atype, ///< [in] Area t fru_time = FRU_DATE_UNSPECIFIED; } else { // FRU time is in minutes and we don't care about microseconds - fru_time = (tv->tv_sec - epoch_seconds_1996()) / 60; + fru_time = (tv->tv_sec - fru_datetime_base()) / 60; } header.mfgdate[0] = fru_time & 0xFF; header.mfgdate[1] = (fru_time >> 8) & 0xFF; @@ -715,8 +762,8 @@ fru_chassis_area_t * fru_encode_chassis_info(const fru_exploded_chassis_t *chass } bool fru_decode_chassis_info( - const fru_chassis_area_t *area, //< [in] encoded chassis - fru_exploded_chassis_t *chassis_out //< [out] + const fru_chassis_area_t *area, + fru_exploded_chassis_t *chassis_out ) { chassis_out->type = area->langtype; @@ -785,8 +832,8 @@ fru_board_area_t * fru_encode_board_info(const fru_exploded_board_t *board) ///< } bool fru_decode_board_info( - const fru_board_area_t *area, //< [in] encoded board - fru_exploded_board_t *board_out //< [out] + const fru_board_area_t *area, + fru_exploded_board_t *board_out ) { fru_field_t *field; @@ -794,19 +841,18 @@ bool fru_decode_board_info( board_out->lang = area->langtype; - // NOTE: host is not always small endian! // - uint32_t min_since_1996_big_endian = 0; - ((uint8_t*)&min_since_1996_big_endian)[1] = area->mfgdate[2]; - ((uint8_t*)&min_since_1996_big_endian)[2] = area->mfgdate[1]; - ((uint8_t*)&min_since_1996_big_endian)[3] = area->mfgdate[0]; - uint32_t min_since_1996 = be32toh(min_since_1996_big_endian); - struct tm tm_1996 = { - .tm_year = 96, - .tm_mon = 0, - .tm_mday = 1 - }; + // NOTE: host is not always little endian! // + union { + uint32_t val; + uint8_t arr[4]; + } min_since_1996_big_endian; + min_since_1996_big_endian.val = 0; + min_since_1996_big_endian.arr[1] = area->mfgdate[2]; + min_since_1996_big_endian.arr[2] = area->mfgdate[1]; + min_since_1996_big_endian.arr[3] = area->mfgdate[0]; + uint32_t min_since_1996 = be32toh(min_since_1996_big_endian.val); // The argument to mktime is zoneless - board_out->tv.tv_sec = epoch_seconds_1996() + 60 * min_since_1996; + board_out->tv.tv_sec = fru_datetime_base() + 60 * min_since_1996; field = (fru_field_t*)data; if (!fru_decode_data(field, &board_out->mfg, @@ -1073,8 +1119,8 @@ fru_mr_area_t *fru_mr_area(fru_mr_reclist_t *reclist, size_t *total) } bool fru_decode_product_info( - const fru_product_area_t *area, //< [in] encoded product - fru_exploded_product_t *product_out //< [out] + const fru_product_area_t *area, + fru_exploded_product_t *product_out ) { fru_field_t *field; @@ -1234,27 +1280,23 @@ fru_t *find_fru_header(uint8_t *buffer, size_t size) { return header; } -#define AREAS \ - X(chassis) \ - X(board) \ - X(product) -#define X(AREA) \ -fru_##AREA##_area_t *find_fru_##AREA##_area(uint8_t *buffer, size_t size) { \ +#define AREA(NAME) \ +fru_##NAME##_area_t *find_fru_##NAME##_area(uint8_t *buffer, size_t size) { \ fru_t *header = find_fru_header(buffer, size); \ - if ((header == NULL) || (header->AREA == 0)) { \ + if ((header == NULL) || (header->NAME == 0)) { \ return NULL; \ } \ - if ((header->AREA + 3) > size) { \ + if ((header->NAME + 3) > size) { \ errno = ENOBUFS; \ return NULL; \ } \ - fru_##AREA##_area_t *area = \ - (fru_##AREA##_area_t *)(buffer + FRU_BYTES(header->AREA)); \ + fru_##NAME##_area_t *area = \ + (fru_##NAME##_area_t *)(buffer + FRU_BYTES(header->NAME)); \ if (area->ver != 1) { \ errno = EPROTO; \ return NULL; \ } \ - if (FRU_BYTES(header->AREA) + FRU_BYTES(area->blocks) > size) { \ + if (FRU_BYTES(header->NAME) + FRU_BYTES(area->blocks) > size) { \ errno = ENOBUFS; \ return NULL; \ } \ @@ -1265,9 +1307,9 @@ fru_##AREA##_area_t *find_fru_##AREA##_area(uint8_t *buffer, size_t size) { } \ return area; \ } -AREAS -#undef X -#undef AREAS +AREA(chassis); +AREA(board); +AREA(product); #ifdef __STANDALONE__ diff --git a/fru.h b/fru.h index 1439723..fd94d06 100644 --- a/fru.h +++ b/fru.h @@ -273,14 +273,23 @@ typedef fru_mr_rec_t fru_mr_area_t; /// Intended for use as a pointer only #define FRU_BYTES(blocks) ((blocks) * FRU_BLOCK_SZ) #define FRU_BLOCKS(bytes) (((bytes) + FRU_BLOCK_SZ - 1) / FRU_BLOCK_SZ) +typedef enum { + FIELD_TYPE_AUTO, + FIELD_TYPE_BINARY = (__TYPE_BINARY + 1), + BASE_FIELD_TYPE = FIELD_TYPE_BINARY, + FIELD_TYPE_BCDPLUS = (__TYPE_BCDPLUS + 1), + FIELD_TYPE_SIXBITASCII = (__TYPE_ASCII_6BIT + 1), + FIELD_TYPE_TEXT = (__TYPE_TEXT + 1), + TOTAL_FIELD_TYPES +} field_type_t; + +extern const char* enc_names[TOTAL_FIELD_TYPES]; + +/// Extract FRU field type as field_type_t +#define FIELD_TYPE_T(t) (FRU_TYPE(t) + BASE_FIELD_TYPE) + typedef struct { - enum { - FIELD_TYPE_AUTO, - FIELD_TYPE_BINARY, - FIELD_TYPE_BCDPLUS, - FIELD_TYPE_SIXBITASCII, - FIELD_TYPE_TEXT - } type; + field_type_t type; unsigned char val[FRU_FIELDMAXARRAY]; } typed_field_t; @@ -326,10 +335,51 @@ int fru_mr_uuid2rec(fru_mr_rec_t **rec, const unsigned char *str); fru_mr_reclist_t * add_mr_reclist(fru_mr_reclist_t **reclist); fru_mr_area_t * fru_mr_area(fru_mr_reclist_t *reclist, size_t *total); +/** + * @brief Encode chassis info into binary buffer. + * + * Binary buffer needs to be freed after use. + * + * @param[in] chassis Area info. + * @return Encoded area buffer. + * @retval NULL Encoding failed. \p errno is set accordingly. + */ fru_chassis_area_t * fru_encode_chassis_info(const fru_exploded_chassis_t *chassis); + +/** + * @brief Encode board info into binary buffer. + * + * Binary buffer needs to be freed after use. + * + * @param[in] board Area info. + * @return Encoded area buffer. + * @retval NULL Encoding failed. \p errno is set accordingly. + */ fru_board_area_t * fru_encode_board_info(const fru_exploded_board_t *board); + +/** + * @brief Encode product info into binary buffer. + * + * Binary buffer needs to be freed after use. + * + * @param[in] product Area info. + * @return Encoded area buffer. + * @retval NULL Encoding failed. \p errno is set accordingly. + */ fru_product_area_t * fru_encode_product_info(const fru_exploded_product_t *product); + +/** + * @brief Encode data field. + * + * Binary buffer needs to be freed after use. + * + * @param[in] len Binary buffer length. + * @param[in] data Binary buffer. + * @return Encoded field buffer. + * @retval NULL Encoding error. \p errno is set accordingly. + */ fru_field_t * fru_encode_data(int len, const uint8_t *data); + fru_t * fru_create(fru_area_t area[FRU_MAX_AREAS], size_t *size); /** @@ -338,7 +388,7 @@ fru_t * fru_create(fru_area_t area[FRU_MAX_AREAS], size_t *size); * @param[in] buffer Byte buffer. * @param[in] size Byte buffer size. * @return Pointer to the FRU header in the buffer. - * @retval NULL FRU header not found. + * @retval NULL FRU header not found. \p errno is set accordingly. */ fru_t *find_fru_header(uint8_t *buffer, size_t size); @@ -348,7 +398,7 @@ fru_t *find_fru_header(uint8_t *buffer, size_t size); * @param[in] buffer Byte buffer. * @param[in] size Byte buffer size. * @return Pointer to the FRU chassis area in the buffer. - * @retval NULL FRU chassis area not found. + * @retval NULL FRU chassis area not found. \p errno is set accordingly. */ fru_chassis_area_t *find_fru_chassis_area(uint8_t *buffer, size_t size); @@ -358,7 +408,7 @@ fru_chassis_area_t *find_fru_chassis_area(uint8_t *buffer, size_t size); * @param[in] buffer Byte buffer. * @param[in] size Byte buffer size. * @return Pointer to the FRU board area in the buffer. - * @retval NULL FRU board area not found. + * @retval NULL FRU board area not found. \p errno is set accordingly. */ fru_board_area_t *find_fru_board_area(uint8_t *buffer, size_t size); @@ -368,7 +418,7 @@ fru_board_area_t *find_fru_board_area(uint8_t *buffer, size_t size); * @param[in] buffer Byte buffer. * @param[in] size Byte buffer size. * @return Pointer to the FRU product area in the buffer. - * @retval NULL FRU product area not found. + * @retval NULL FRU product area not found. \p errno is set accordingly. */ fru_product_area_t *find_fru_product_area(uint8_t *buffer, size_t size); diff --git a/frugen.c b/frugen.c index 407b6df..0c88789 100644 --- a/frugen.c +++ b/frugen.c @@ -36,14 +36,6 @@ exit(1); \ } while(0) -const char* type_names[] = { - "auto", - "binary", - "bcdplus", - "sixbitascii", - "text" -}; - volatile int debug_level = 0; #define debug(level, fmt, args...) do { \ int e = errno; \ @@ -58,52 +50,10 @@ volatile int debug_level = 0; static int typelen2ind(uint8_t field) { - if (FRU_ISTYPE(field, TEXT)) { - return 4; - } else if (FRU_ISTYPE(field, ASCII_6BIT)) { - return 3; - } else if (FRU_ISTYPE(field, BCDPLUS)) { - return 2; - } else if (FRU_ISTYPE(field, BINARY)) { - return 1; - } else { - return 0; - } -} - -static -int json_object_add_with_type(struct json_object* obj, - const char* key, - const char* val, - int type) { - struct json_object *string, *type_string, *entry; - if ((string = json_object_new_string(val)) == NULL) - goto STRING_ERR; - - if (type == 0) { - entry = string; - } else { - if ((type_string = json_object_new_string(type_names[type])) == NULL) - goto TYPE_STRING_ERR; - if ((entry = json_object_new_object()) == NULL) - goto ENTRY_ERR; - if (json_object_object_add(entry, "type", type_string)) - goto ADD_ERR; - if (json_object_object_add(entry, "data", string)) - goto ADD_ERR; - } - if (key == NULL) - return json_object_array_add(obj, entry); + if (FIELD_TYPE_T(field) < TOTAL_FIELD_TYPES) + return FIELD_TYPE_T(field); else - return json_object_object_add(obj, key, entry); - -ADD_ERR: -ENTRY_ERR: - json_object_put(type_string); -TYPE_STRING_ERR: - json_object_put(string); -STRING_ERR: - return -1; + return FIELD_TYPE_AUTO; } static @@ -438,6 +388,40 @@ bool json_fill_fru_mr_reclist(json_object *jso, fru_mr_reclist_t **mr_reclist) return has_multirec; } +static +int json_object_add_with_type(struct json_object* obj, + const char* key, + const char* val, + int type) { + struct json_object *string, *type_string, *entry; + if ((string = json_object_new_string(val)) == NULL) + goto STRING_ERR; + + if (type == 0) { + entry = string; + } else { + if ((type_string = json_object_new_string(enc_names[type])) == NULL) + goto TYPE_STRING_ERR; + if ((entry = json_object_new_object()) == NULL) + goto ENTRY_ERR; + if (json_object_object_add(entry, "type", type_string)) + goto ADD_ERR; + if (json_object_object_add(entry, "data", string)) + goto ADD_ERR; + } + if (key == NULL) + return json_object_array_add(obj, entry); + else + return json_object_object_add(obj, key, entry); + +ADD_ERR: +ENTRY_ERR: + json_object_put(type_string); +TYPE_STRING_ERR: + json_object_put(string); +STRING_ERR: + return -1; +} #endif /* __HAS_JSON__ */ @@ -1013,12 +997,12 @@ int main(int argc, char *argv[]) if (has_chassis) { puts("Chassis"); printf("\ttype: %u\n", chassis.type); - printf("\tpn(%s): %s\n", type_names[chassis.pn.type], chassis.pn.val); - printf("\tserial(%s): %s\n", type_names[chassis.serial.type], chassis.serial.val); + printf("\tpn(%s): %s\n", enc_names[chassis.pn.type], chassis.pn.val); + printf("\tserial(%s): %s\n", enc_names[chassis.serial.type], chassis.serial.val); fru_reclist_t *next = chassis.cust; while (next != NULL) { printf("\tcustom(%s): %s\n", - type_names[typelen2ind(next->rec->typelen)], + enc_names[typelen2ind(next->rec->typelen)], next->rec->data); next = next->next; } @@ -1027,17 +1011,17 @@ int main(int argc, char *argv[]) if (has_product) { puts("Product"); printf("\tlang: %u\n", product.lang); - printf("\tmfg(%s): %s\n", type_names[product.mfg.type], product.mfg.val); - printf("\tpname(%s): %s\n", type_names[product.pname.type], product.pname.val); - printf("\tserial(%s): %s\n", type_names[product.serial.type], product.serial.val); - printf("\tpn(%s): %s\n", type_names[product.pn.type], product.pn.val); - printf("\tver(%s): %s\n", type_names[product.ver.type], product.ver.val); - printf("\tatag(%s): %s\n", type_names[product.atag.type], product.atag.val); - printf("\tfile(%s): %s\n", type_names[product.file.type], product.file.val); - next = product.cust; + printf("\tmfg(%s): %s\n", enc_names[product.mfg.type], product.mfg.val); + printf("\tpname(%s): %s\n", enc_names[product.pname.type], product.pname.val); + printf("\tserial(%s): %s\n", enc_names[product.serial.type], product.serial.val); + printf("\tpn(%s): %s\n", enc_names[product.pn.type], product.pn.val); + printf("\tver(%s): %s\n", enc_names[product.ver.type], product.ver.val); + printf("\tatag(%s): %s\n", enc_names[product.atag.type], product.atag.val); + printf("\tfile(%s): %s\n", enc_names[product.file.type], product.file.val); + fru_reclist_t *next = product.cust; while (next != NULL) { printf("\tcustom(%s): %s\n", - type_names[typelen2ind(next->rec->typelen)], + enc_names[typelen2ind(next->rec->typelen)], next->rec->data); next = next->next; } @@ -1047,15 +1031,15 @@ int main(int argc, char *argv[]) puts("Board"); printf("\tlang: %u\n", board.lang); printf("\ttime: %s\n", timebuf); - printf("\tmfg(%s): %s\n", type_names[board.mfg.type], board.mfg.val); - printf("\tpname(%s): %s\n", type_names[board.pname.type], board.pname.val); - printf("\tserial(%s): %s\n", type_names[board.serial.type], board.serial.val); - printf("\tpn(%s): %s\n", type_names[board.pn.type], board.pn.val); - printf("\tfile(%s): %s\n", type_names[board.file.type], board.file.val); - next = board.cust; + printf("\tmfg(%s): %s\n", enc_names[board.mfg.type], board.mfg.val); + printf("\tpname(%s): %s\n", enc_names[board.pname.type], board.pname.val); + printf("\tserial(%s): %s\n", enc_names[board.serial.type], board.serial.val); + printf("\tpn(%s): %s\n", enc_names[board.pn.type], board.pn.val); + printf("\tfile(%s): %s\n", enc_names[board.file.type], board.file.val); + fru_reclist_t *next = board.cust; while (next != NULL) { printf("\tcustom(%s): %s\n", - type_names[typelen2ind(next->rec->typelen)], + enc_names[typelen2ind(next->rec->typelen)], next->rec->data); next = next->next; }