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