xref: /illumos-gate/usr/src/lib/libfru/libnvfru/nvfru.c (revision d946b6ce)
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 #include "nvfru.h"
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 
52 static pthread_mutex_t gLock = PTHREAD_MUTEX_INITIALIZER;
53 
54 
55 
56 static void
57 convert_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 
140 static void
141 convert_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 
227 static fru_regdef_t *
228 alloc_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 
255 static int
256 convert_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 
312 static int
313 convert_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 
349 static int
350 convert_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 
381 int
382 rawfru_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