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