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 }