nvfru.c revision 11015:0a0751599d31
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
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
52static pthread_mutex_t gLock = PTHREAD_MUTEX_INITIALIZER;
53
54
55
56static void
57convert_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
140static void
141convert_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
227static fru_regdef_t *
228alloc_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
255static int
256convert_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
312static int
313convert_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
349static int
350convert_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
381int
382rawfru_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