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