xref: /illumos-gate/usr/src/lib/libfru/libnvfru/nvfru.c (revision ac88567a)
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
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
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 *
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
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
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
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
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