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 } 411