nvfru.c revision 12126:60364f3f65c7
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
51static pthread_mutex_t gLock = PTHREAD_MUTEX_INITIALIZER;
52
53
54
55static void
56convert_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
139static void
140convert_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
226static fru_regdef_t *
227alloc_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
254static int
255convert_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
311static int
312convert_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
348static int
349convert_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
380int
381rawfru_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