/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2014 Gary Mills * * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. * * Copyright (c) 2018, Joyent, Inc. */ #include #include #include #include #include #include #include #include #include #include "libfru.h" #include "libfrup.h" #include "fru_tag.h" #include "libfrureg.h" #include "nvfru.h" #define NUM_ITER_BYTES 4 #define HEAD_ITER 0 #define TAIL_ITER 1 #define NUM_ITER 2 #define MAX_ITER 3 #define TIMESTRINGLEN 128 #define PARSE_TIME 1 static pthread_mutex_t gLock = PTHREAD_MUTEX_INITIALIZER; static void convert_field(const uint8_t *field, const fru_regdef_t *def, const char *path, nvlist_t *nv) { char timestring[TIMESTRINGLEN]; int i; uint64_t value; time_t timefield; switch (def->dataType) { case FDTYPE_Binary: assert(def->payloadLen <= sizeof (value)); switch (def->dispType) { #if PARSE_TIME == 1 case FDISP_Time: if (def->payloadLen > sizeof (timefield)) { /* too big for formatting */ return; } (void) memcpy(&timefield, field, sizeof (timefield)); timefield = BE_32(timefield); if (strftime(timestring, sizeof (timestring), "%c", localtime(&timefield)) == 0) { /* buffer too small */ return; } (void) nvlist_add_string(nv, path, timestring); return; #endif case FDISP_Binary: case FDISP_Octal: case FDISP_Decimal: case FDISP_Hex: default: value = 0; (void) memcpy((((uint8_t *)&value) + sizeof (value) - def->payloadLen), field, def->payloadLen); value = BE_64(value); switch (def->payloadLen) { case 1: (void) nvlist_add_uint8(nv, path, (uint8_t)value); break; case 2: (void) nvlist_add_uint16(nv, path, (uint16_t)value); break; case 4: (void) nvlist_add_uint32(nv, path, (uint32_t)value); break; default: (void) nvlist_add_uint64(nv, path, value); } return; } case FDTYPE_ASCII: (void) nvlist_add_string(nv, path, (char *)field); return; case FDTYPE_Enumeration: value = 0; (void) memcpy((((uint8_t *)&value) + sizeof (value) - def->payloadLen), field, def->payloadLen); value = BE_64(value); for (i = 0; i < def->enumCount; i++) { if (def->enumTable[i].value == value) { (void) nvlist_add_string(nv, path, def->enumTable[i].text); return; } } } /* nothing matched above, use byte array */ (void) nvlist_add_byte_array(nv, path, (uchar_t *)field, def->payloadLen); } static void convert_element(const uint8_t *data, const fru_regdef_t *def, char *ppath, nvlist_t *nv, boolean_t from_iter) { int i; char *path; /* construct path */ if ((def->iterationCount == 0) && (def->iterationType != FRU_NOT_ITERATED)) { path = ppath; } else { path = (char *)def->name; } /* iteration, record and field */ if (def->iterationCount) { int iterlen, n; uint8_t head, num; fru_regdef_t newdef; nvlist_t **nv_elems; char num_str[32]; iterlen = (def->payloadLen - NUM_ITER_BYTES) / def->iterationCount; /* * make a new element definition to describe the components of * the iteration. */ (void) memcpy(&newdef, def, sizeof (newdef)); newdef.iterationCount = 0; newdef.payloadLen = iterlen; /* validate the content of the iteration control bytes */ if ((data[HEAD_ITER] >= def->iterationCount) || (data[NUM_ITER] > def->iterationCount) || (data[MAX_ITER] != def->iterationCount)) { /* invalid. show all iterations */ head = 0; num = def->iterationCount; } else { head = data[HEAD_ITER]; num = data[NUM_ITER]; } nv_elems = (nvlist_t **)malloc(num * sizeof (nvlist_t *)); if (!nv_elems) return; for (i = head, n = 0, data += sizeof (uint32_t); n < num; i = ((i + 1) % def->iterationCount), n++) { if (nvlist_alloc(&nv_elems[n], NV_UNIQUE_NAME, 0) != 0) return; (void) snprintf(num_str, sizeof (num_str), "%d", n); convert_element((data + i*iterlen), &newdef, num_str, nv_elems[n], B_TRUE); } (void) nvlist_add_nvlist_array(nv, path, nv_elems, num); } else if (def->dataType == FDTYPE_Record) { const fru_regdef_t *component; nvlist_t *nv_record; if (!from_iter) { if (nvlist_alloc(&nv_record, NV_UNIQUE_NAME, 0) != 0) { return; } } else { nv_record = nv; } for (i = 0; i < def->enumCount; i++, data += component->payloadLen) { component = fru_reg_lookup_def_by_name( def->enumTable[i].text); convert_element(data, component, "", nv_record, B_FALSE); } (void) nvlist_add_nvlist(nv, path, nv_record); } else { convert_field(data, def, path, nv); } } static fru_regdef_t * alloc_unknown_fru_regdef(void) { fru_regdef_t *p; p = malloc(sizeof (fru_regdef_t)); if (!p) { return (NULL); } p->version = REGDEF_VERSION; p->name = NULL; p->tagType = -1; p->tagDense = -1; p->payloadLen = -1; p->dataLength = -1; p->dataType = FDTYPE_ByteArray; p->dispType = FDISP_Hex; p->purgeable = FRU_WHICH_UNDEFINED; p->relocatable = FRU_WHICH_UNDEFINED; p->enumCount = 0; p-> enumTable = NULL; p->iterationCount = 0; p->iterationType = FRU_NOT_ITERATED; p->exampleString = NULL; return (p); } static int convert_packet(fru_tag_t *tag, uint8_t *payload, size_t length, void *args) { int tag_type; size_t payload_length; const fru_regdef_t *def; nvlist_t *nv = (nvlist_t *)args; char tagname[sizeof ("?_0123456789_0123456789")]; tag_type = get_tag_type(tag); payload_length = 0; /* check for unrecognized tag */ if ((tag_type == -1) || ((payload_length = get_payload_length(tag)) != length)) { fru_regdef_t *unknown; unknown = alloc_unknown_fru_regdef(); unknown->payloadLen = length; unknown->dataLength = unknown->payloadLen; if (tag_type == -1) { (void) snprintf(tagname, sizeof (tagname), "INVALID"); } else { (void) snprintf(tagname, sizeof (tagname), "%s_%u_%u_%u", get_tagtype_str(tag_type), get_tag_dense(tag), payload_length, length); } unknown->name = tagname; convert_element(payload, unknown, "", nv, B_FALSE); free(unknown); } else if ((def = fru_reg_lookup_def_by_tag(*tag)) == NULL) { fru_regdef_t *unknown; unknown = alloc_unknown_fru_regdef(); unknown->payloadLen = length; unknown->dataLength = unknown->payloadLen; (void) snprintf(tagname, sizeof (tagname), "%s_%u_%u", get_tagtype_str(tag_type), unknown->tagDense, payload_length); unknown->name = tagname; convert_element(payload, unknown, "", nv, B_FALSE); free(unknown); } else { convert_element(payload, def, "", nv, B_FALSE); } return (FRU_SUCCESS); } static int convert_packets_in_segment(fru_seghdl_t segment, void *args) { char *name; int ret; nvlist_t *nv = (nvlist_t *)args; nvlist_t *nv_segment; ret = fru_get_segment_name(segment, &name); if (ret != FRU_SUCCESS) { return (ret); } /* create a new nvlist for each segment */ ret = nvlist_alloc(&nv_segment, NV_UNIQUE_NAME, 0); if (ret) { free(name); return (FRU_FAILURE); } /* convert the segment to an nvlist */ ret = fru_for_each_packet(segment, convert_packet, nv_segment); if (ret != FRU_SUCCESS) { nvlist_free(nv_segment); free(name); return (ret); } /* add the nvlist for this segment */ (void) nvlist_add_nvlist(nv, name, nv_segment); free(name); return (FRU_SUCCESS); } static int convert_fru(fru_nodehdl_t hdl, nvlist_t **nvlist) { int err; nvlist_t *nv; fru_node_t fru_type; if (fru_get_node_type(hdl, &fru_type) != FRU_SUCCESS) { return (-1); } if (fru_type != FRU_NODE_CONTAINER) { return (-1); } err = nvlist_alloc(&nv, NV_UNIQUE_NAME, 0); if (err) { return (err); } if (fru_for_each_segment(hdl, convert_packets_in_segment, nv) != FRU_SUCCESS) { nvlist_free(nv); return (-1); } *nvlist = nv; return (0); } int rawfru_to_nvlist(uint8_t *buffer, size_t bufsize, char *cont_type, nvlist_t **nvlist) { fru_errno_t fru_err; fru_nodehdl_t hdl; int err; (void) pthread_mutex_lock(&gLock); fru_err = fru_open_data_source("raw", buffer, bufsize, cont_type, NULL); if (fru_err != FRU_SUCCESS) { (void) pthread_mutex_unlock(&gLock); return (-1); } fru_err = fru_get_root(&hdl); if (fru_err != FRU_SUCCESS) { (void) pthread_mutex_unlock(&gLock); return (-1); } err = convert_fru(hdl, nvlist); (void) fru_close_data_source(); (void) pthread_mutex_unlock(&gLock); return (err); }