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