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 }