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