1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright (c) 2014 Gary Mills
  24  *
  25  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
  26  *
  27  * Copyright (c) 2018, Joyent, Inc.
  28  */
  29 
  30 #include <stdio.h>
  31 #include <stdlib.h>
  32 #include <stdint.h>
  33 #include <strings.h>
  34 #include <assert.h>
  35 #include <pthread.h>
  36 #include <sys/byteorder.h>
  37 #include <sys/types.h>
  38 #include <sys/nvpair.h>
  39 
  40 #include "libfru.h"
  41 #include "libfrup.h"
  42 #include "fru_tag.h"
  43 #include "libfrureg.h"
  44 #include "nvfru.h"
  45 
  46 #define NUM_ITER_BYTES  4
  47 #define HEAD_ITER       0
  48 #define TAIL_ITER       1
  49 #define NUM_ITER        2
  50 #define MAX_ITER        3
  51 #define TIMESTRINGLEN   128
  52 
  53 #define PARSE_TIME      1
  54 
  55 static pthread_mutex_t gLock = PTHREAD_MUTEX_INITIALIZER;
  56 
  57 
  58 
  59 static void
  60 convert_field(const uint8_t *field, const fru_regdef_t *def, const char *path,
  61     nvlist_t *nv)
  62 {
  63         char timestring[TIMESTRINGLEN];
  64         int i;
  65         uint64_t value;
  66         time_t timefield;
  67 
  68         switch (def->dataType) {
  69         case FDTYPE_Binary:
  70                 assert(def->payloadLen <= sizeof (value));
  71                 switch (def->dispType) {
  72 #if PARSE_TIME == 1
  73                 case FDISP_Time:
  74                         if (def->payloadLen > sizeof (timefield)) {
  75                                 /* too big for formatting */
  76                                 return;
  77                         }
  78                         (void) memcpy(&timefield, field, sizeof (timefield));
  79                         timefield = BE_32(timefield);
  80                         if (strftime(timestring, sizeof (timestring), "%c",
  81                             localtime(&timefield)) == 0) {
  82                                 /* buffer too small */
  83                                 return;
  84                         }
  85                         (void) nvlist_add_string(nv, path, timestring);
  86                         return;
  87 #endif
  88 
  89                 case FDISP_Binary:
  90                 case FDISP_Octal:
  91                 case FDISP_Decimal:
  92                 case FDISP_Hex:
  93                 default:
  94                         value = 0;
  95                         (void) memcpy((((uint8_t *)&value) +
  96                             sizeof (value) - def->payloadLen),
  97                             field, def->payloadLen);
  98                         value = BE_64(value);
  99                         switch (def->payloadLen) {
 100                         case 1:
 101                                 (void) nvlist_add_uint8(nv, path,
 102                                     (uint8_t)value);
 103                                 break;
 104                         case 2:
 105                                 (void) nvlist_add_uint16(nv, path,
 106                                     (uint16_t)value);
 107                                 break;
 108                         case 4:
 109                                 (void) nvlist_add_uint32(nv, path,
 110                                     (uint32_t)value);
 111                                 break;
 112                         default:
 113                                 (void) nvlist_add_uint64(nv, path, value);
 114                         }
 115                         return;
 116                 }
 117 
 118         case FDTYPE_ASCII:
 119                 (void) nvlist_add_string(nv, path, (char *)field);
 120                 return;
 121 
 122         case FDTYPE_Enumeration:
 123                 value = 0;
 124                 (void) memcpy((((uint8_t *)&value) + sizeof (value) -
 125                     def->payloadLen), field, def->payloadLen);
 126                 value = BE_64(value);
 127                 for (i = 0; i < def->enumCount; i++) {
 128                         if (def->enumTable[i].value == value) {
 129                                 (void) nvlist_add_string(nv, path,
 130                                     def->enumTable[i].text);
 131                                 return;
 132                         }
 133                 }
 134         }
 135 
 136         /* nothing matched above, use byte array */
 137         (void) nvlist_add_byte_array(nv, path, (uchar_t *)field,
 138             def->payloadLen);
 139 }
 140 
 141 
 142 
 143 static void
 144 convert_element(const uint8_t *data, const fru_regdef_t *def, char *ppath,
 145     nvlist_t *nv, boolean_t from_iter)
 146 {
 147         int i;
 148         char *path;
 149 
 150         /* construct path */
 151         if ((def->iterationCount == 0) &&
 152             (def->iterationType != FRU_NOT_ITERATED)) {
 153                 path = ppath;
 154         } else {
 155                 path = (char *)def->name;
 156         }
 157 
 158         /* iteration, record and field */
 159         if (def->iterationCount) {
 160                 int iterlen, n;
 161                 uint8_t head, num;
 162                 fru_regdef_t newdef;
 163                 nvlist_t **nv_elems;
 164                 char num_str[32];
 165 
 166                 iterlen = (def->payloadLen - NUM_ITER_BYTES) /
 167                     def->iterationCount;
 168 
 169                 /*
 170                  * make a new element definition to describe the components of
 171                  * the iteration.
 172                  */
 173                 (void) memcpy(&newdef, def, sizeof (newdef));
 174                 newdef.iterationCount = 0;
 175                 newdef.payloadLen = iterlen;
 176 
 177                 /* validate the content of the iteration control bytes */
 178                 if ((data[HEAD_ITER] >= def->iterationCount) ||
 179                     (data[NUM_ITER] > def->iterationCount) ||
 180                     (data[MAX_ITER] != def->iterationCount)) {
 181                         /* invalid. show all iterations */
 182                         head = 0;
 183                         num = def->iterationCount;
 184                 } else {
 185                         head = data[HEAD_ITER];
 186                         num = data[NUM_ITER];
 187                 }
 188 
 189                 nv_elems = (nvlist_t **)malloc(num * sizeof (nvlist_t *));
 190                 if (!nv_elems)
 191                         return;
 192                 for (i = head, n = 0, data += sizeof (uint32_t); n < num;
 193                     i = ((i + 1) % def->iterationCount), n++) {
 194                         if (nvlist_alloc(&nv_elems[n], NV_UNIQUE_NAME, 0) != 0)
 195                                 return;
 196                         (void) snprintf(num_str, sizeof (num_str), "%d", n);
 197                         convert_element((data + i*iterlen), &newdef, num_str,
 198                             nv_elems[n], B_TRUE);
 199                 }
 200                 (void) nvlist_add_nvlist_array(nv, path, nv_elems, num);
 201 
 202         } else if (def->dataType == FDTYPE_Record) {
 203                 const fru_regdef_t *component;
 204                 nvlist_t *nv_record;
 205 
 206                 if (!from_iter) {
 207                         if (nvlist_alloc(&nv_record, NV_UNIQUE_NAME, 0) != 0) {
 208                                 return;
 209                         }
 210                 } else {
 211                         nv_record = nv;
 212                 }
 213 
 214                 for (i = 0; i < def->enumCount; i++,
 215                     data += component->payloadLen) {
 216                         component = fru_reg_lookup_def_by_name(
 217                             def->enumTable[i].text);
 218                         convert_element(data, component, "", nv_record,
 219                             B_FALSE);
 220                 }
 221 
 222                 (void) nvlist_add_nvlist(nv, path, nv_record);
 223 
 224         } else {
 225                 convert_field(data, def, path, nv);
 226         }
 227 }
 228 
 229 
 230 static fru_regdef_t *
 231 alloc_unknown_fru_regdef(void)
 232 {
 233         fru_regdef_t *p;
 234 
 235         p = malloc(sizeof (fru_regdef_t));
 236         if (!p) {
 237                 return (NULL);
 238         }
 239         p->version = REGDEF_VERSION;
 240         p->name = NULL;
 241         p->tagType = -1;
 242         p->tagDense = -1;
 243         p->payloadLen = -1;
 244         p->dataLength = -1;
 245         p->dataType = FDTYPE_ByteArray;
 246         p->dispType = FDISP_Hex;
 247         p->purgeable = FRU_WHICH_UNDEFINED;
 248         p->relocatable = FRU_WHICH_UNDEFINED;
 249         p->enumCount = 0;
 250         p-> enumTable = NULL;
 251         p->iterationCount = 0;
 252         p->iterationType = FRU_NOT_ITERATED;
 253         p->exampleString = NULL;
 254 
 255         return (p);
 256 }
 257 
 258 static int
 259 convert_packet(fru_tag_t *tag, uint8_t *payload, size_t length, void *args)
 260 {
 261         int tag_type;
 262         size_t payload_length;
 263         const fru_regdef_t *def;
 264         nvlist_t *nv = (nvlist_t *)args;
 265         char tagname[sizeof ("?_0123456789_0123456789")];
 266         tag_type = get_tag_type(tag);
 267         payload_length = 0;
 268 
 269         /* check for unrecognized tag */
 270         if ((tag_type == -1) ||
 271             ((payload_length = get_payload_length(tag)) != length)) {
 272                 fru_regdef_t *unknown;
 273 
 274                 unknown = alloc_unknown_fru_regdef();
 275                 unknown->payloadLen = length;
 276                 unknown->dataLength = unknown->payloadLen;
 277 
 278                 if (tag_type == -1) {
 279                         (void) snprintf(tagname, sizeof (tagname),
 280                             "INVALID");
 281                 } else {
 282                         (void) snprintf(tagname, sizeof (tagname),
 283                             "%s_%u_%u_%u", get_tagtype_str(tag_type),
 284                             get_tag_dense(tag), payload_length, length);
 285                 }
 286                 unknown->name = tagname;
 287                 convert_element(payload, unknown, "", nv, B_FALSE);
 288                 free(unknown);
 289 
 290         } else if ((def = fru_reg_lookup_def_by_tag(*tag)) == NULL) {
 291                 fru_regdef_t *unknown;
 292 
 293                 unknown = alloc_unknown_fru_regdef();
 294                 unknown->payloadLen = length;
 295                 unknown->dataLength = unknown->payloadLen;
 296 
 297                 (void) snprintf(tagname, sizeof (tagname), "%s_%u_%u",
 298                     get_tagtype_str(tag_type),
 299                     unknown->tagDense, payload_length);
 300 
 301                 unknown->name = tagname;
 302                 convert_element(payload, unknown, "", nv, B_FALSE);
 303                 free(unknown);
 304 
 305         } else {
 306 
 307                 convert_element(payload, def, "", nv, B_FALSE);
 308 
 309         }
 310 
 311         return (FRU_SUCCESS);
 312 }
 313 
 314 
 315 static int
 316 convert_packets_in_segment(fru_seghdl_t segment, void *args)
 317 {
 318         char *name;
 319         int ret;
 320         nvlist_t *nv = (nvlist_t *)args;
 321         nvlist_t *nv_segment;
 322 
 323         ret = fru_get_segment_name(segment, &name);
 324         if (ret != FRU_SUCCESS) {
 325                 return (ret);
 326         }
 327 
 328         /* create a new nvlist for each segment */
 329         ret = nvlist_alloc(&nv_segment, NV_UNIQUE_NAME, 0);
 330         if (ret) {
 331                 free(name);
 332                 return (FRU_FAILURE);
 333         }
 334 
 335         /* convert the segment to an nvlist */
 336         ret = fru_for_each_packet(segment, convert_packet, nv_segment);
 337         if (ret != FRU_SUCCESS) {
 338                 nvlist_free(nv_segment);
 339                 free(name);
 340                 return (ret);
 341         }
 342 
 343         /* add the nvlist for this segment */
 344         (void) nvlist_add_nvlist(nv, name, nv_segment);
 345 
 346         free(name);
 347 
 348         return (FRU_SUCCESS);
 349 }
 350 
 351 
 352 static int
 353 convert_fru(fru_nodehdl_t hdl, nvlist_t **nvlist)
 354 {
 355         int err;
 356         nvlist_t *nv;
 357         fru_node_t fru_type;
 358 
 359         if (fru_get_node_type(hdl, &fru_type) != FRU_SUCCESS) {
 360                 return (-1);
 361         }
 362 
 363         if (fru_type != FRU_NODE_CONTAINER) {
 364                 return (-1);
 365         }
 366 
 367         err = nvlist_alloc(&nv, NV_UNIQUE_NAME, 0);
 368         if (err) {
 369                 return (err);
 370         }
 371 
 372         if (fru_for_each_segment(hdl, convert_packets_in_segment, nv) !=
 373             FRU_SUCCESS) {
 374                 nvlist_free(nv);
 375                 return (-1);
 376         }
 377 
 378         *nvlist = nv;
 379 
 380         return (0);
 381 }
 382 
 383 
 384 int
 385 rawfru_to_nvlist(uint8_t *buffer, size_t bufsize, char *cont_type,
 386     nvlist_t **nvlist)
 387 {
 388         fru_errno_t fru_err;
 389         fru_nodehdl_t hdl;
 390         int err;
 391 
 392         (void) pthread_mutex_lock(&gLock);
 393         fru_err = fru_open_data_source("raw", buffer, bufsize, cont_type,
 394             NULL);
 395         if (fru_err != FRU_SUCCESS) {
 396                 (void) pthread_mutex_unlock(&gLock);
 397                 return (-1);
 398         }
 399         fru_err = fru_get_root(&hdl);
 400         if (fru_err != FRU_SUCCESS) {
 401                 (void) pthread_mutex_unlock(&gLock);
 402                 return (-1);
 403         }
 404 
 405         err = convert_fru(hdl, nvlist);
 406 
 407         (void) fru_close_data_source();
 408 
 409         (void) pthread_mutex_unlock(&gLock);
 410 
 411         return (err);
 412 }