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
convert_field(const uint8_t * field,const fru_regdef_t * def,const char * path,nvlist_t * nv)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
convert_element(const uint8_t * data,const fru_regdef_t * def,char * ppath,nvlist_t * nv,boolean_t from_iter)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 *
alloc_unknown_fru_regdef(void)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
convert_packet(fru_tag_t * tag,uint8_t * payload,size_t length,void * args)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
convert_packets_in_segment(fru_seghdl_t segment,void * args)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
convert_fru(fru_nodehdl_t hdl,nvlist_t ** nvlist)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
rawfru_to_nvlist(uint8_t * buffer,size_t bufsize,char * cont_type,nvlist_t ** nvlist)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 }
409