xref: /illumos-gate/usr/src/lib/libfru/libnvfru/nvfru.c (revision 29c3ebdb)
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  * Copyright (c) 2018, Joyent, Inc.
28  */
29 
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <stdint.h>
33 #include <strings.h>
34 #include <assert.h>
35 #include <pthread.h>
36 #include <sys/byteorder.h>
37 #include <sys/types.h>
38 #include <sys/nvpair.h>
39 
40 #include "libfru.h"
41 #include "libfrup.h"
42 #include "fru_tag.h"
43 #include "libfrureg.h"
44 #include "nvfru.h"
45 
46 #define	NUM_ITER_BYTES	4
47 #define	HEAD_ITER	0
48 #define	TAIL_ITER	1
49 #define	NUM_ITER	2
50 #define	MAX_ITER	3
51 #define	TIMESTRINGLEN	128
52 
53 #define	PARSE_TIME	1
54 
55 static pthread_mutex_t gLock = PTHREAD_MUTEX_INITIALIZER;
56 
57 
58 
59 static void
convert_field(const uint8_t * field,const fru_regdef_t * def,const char * path,nvlist_t * nv)60 convert_field(const uint8_t *field, const fru_regdef_t *def, const char *path,
61     nvlist_t *nv)
62 {
63 	char timestring[TIMESTRINGLEN];
64 	int i;
65 	uint64_t value;
66 	time_t timefield;
67 
68 	switch (def->dataType) {
69 	case FDTYPE_Binary:
70 		assert(def->payloadLen <= sizeof (value));
71 		switch (def->dispType) {
72 #if PARSE_TIME == 1
73 		case FDISP_Time:
74 			if (def->payloadLen > sizeof (timefield)) {
75 				/* too big for formatting */
76 				return;
77 			}
78 			(void) memcpy(&timefield, field, sizeof (timefield));
79 			timefield = BE_32(timefield);
80 			if (strftime(timestring, sizeof (timestring), "%c",
81 			    localtime(&timefield)) == 0) {
82 				/* buffer too small */
83 				return;
84 			}
85 			(void) nvlist_add_string(nv, path, timestring);
86 			return;
87 #endif
88 
89 		case FDISP_Binary:
90 		case FDISP_Octal:
91 		case FDISP_Decimal:
92 		case FDISP_Hex:
93 		default:
94 			value = 0;
95 			(void) memcpy((((uint8_t *)&value) +
96 			    sizeof (value) - def->payloadLen),
97 			    field, def->payloadLen);
98 			value = BE_64(value);
99 			switch (def->payloadLen) {
100 			case 1:
101 				(void) nvlist_add_uint8(nv, path,
102 				    (uint8_t)value);
103 				break;
104 			case 2:
105 				(void) nvlist_add_uint16(nv, path,
106 				    (uint16_t)value);
107 				break;
108 			case 4:
109 				(void) nvlist_add_uint32(nv, path,
110 				    (uint32_t)value);
111 				break;
112 			default:
113 				(void) nvlist_add_uint64(nv, path, value);
114 			}
115 			return;
116 		}
117 
118 	case FDTYPE_ASCII:
119 		(void) nvlist_add_string(nv, path, (char *)field);
120 		return;
121 
122 	case FDTYPE_Enumeration:
123 		value = 0;
124 		(void) memcpy((((uint8_t *)&value) + sizeof (value) -
125 		    def->payloadLen), field, def->payloadLen);
126 		value = BE_64(value);
127 		for (i = 0; i < def->enumCount; i++) {
128 			if (def->enumTable[i].value == value) {
129 				(void) nvlist_add_string(nv, path,
130 				    def->enumTable[i].text);
131 				return;
132 			}
133 		}
134 	}
135 
136 	/* nothing matched above, use byte array */
137 	(void) nvlist_add_byte_array(nv, path, (uchar_t *)field,
138 	    def->payloadLen);
139 }
140 
141 
142 
143 static void
convert_element(const uint8_t * data,const fru_regdef_t * def,char * ppath,nvlist_t * nv,boolean_t from_iter)144 convert_element(const uint8_t *data, const fru_regdef_t *def, char *ppath,
145     nvlist_t *nv, boolean_t from_iter)
146 {
147 	int i;
148 	char *path;
149 
150 	/* construct path */
151 	if ((def->iterationCount == 0) &&
152 	    (def->iterationType != FRU_NOT_ITERATED)) {
153 		path = ppath;
154 	} else {
155 		path = (char *)def->name;
156 	}
157 
158 	/* iteration, record and field */
159 	if (def->iterationCount) {
160 		int iterlen, n;
161 		uint8_t head, num;
162 		fru_regdef_t newdef;
163 		nvlist_t **nv_elems;
164 		char num_str[32];
165 
166 		iterlen = (def->payloadLen - NUM_ITER_BYTES) /
167 		    def->iterationCount;
168 
169 		/*
170 		 * make a new element definition to describe the components of
171 		 * the iteration.
172 		 */
173 		(void) memcpy(&newdef, def, sizeof (newdef));
174 		newdef.iterationCount = 0;
175 		newdef.payloadLen = iterlen;
176 
177 		/* validate the content of the iteration control bytes */
178 		if ((data[HEAD_ITER] >= def->iterationCount) ||
179 		    (data[NUM_ITER] > def->iterationCount) ||
180 		    (data[MAX_ITER] != def->iterationCount)) {
181 			/* invalid. show all iterations */
182 			head = 0;
183 			num = def->iterationCount;
184 		} else {
185 			head = data[HEAD_ITER];
186 			num = data[NUM_ITER];
187 		}
188 
189 		nv_elems = (nvlist_t **)malloc(num * sizeof (nvlist_t *));
190 		if (!nv_elems)
191 			return;
192 		for (i = head, n = 0, data += sizeof (uint32_t); n < num;
193 		    i = ((i + 1) % def->iterationCount), n++) {
194 			if (nvlist_alloc(&nv_elems[n], NV_UNIQUE_NAME, 0) != 0)
195 				return;
196 			(void) snprintf(num_str, sizeof (num_str), "%d", n);
197 			convert_element((data + i*iterlen), &newdef, num_str,
198 			    nv_elems[n], B_TRUE);
199 		}
200 		(void) nvlist_add_nvlist_array(nv, path, nv_elems, num);
201 
202 	} else if (def->dataType == FDTYPE_Record) {
203 		const fru_regdef_t *component;
204 		nvlist_t *nv_record;
205 
206 		if (!from_iter) {
207 			if (nvlist_alloc(&nv_record, NV_UNIQUE_NAME, 0) != 0) {
208 				return;
209 			}
210 		} else {
211 			nv_record = nv;
212 		}
213 
214 		for (i = 0; i < def->enumCount; i++,
215 		    data += component->payloadLen) {
216 			component = fru_reg_lookup_def_by_name(
217 			    def->enumTable[i].text);
218 			convert_element(data, component, "", nv_record,
219 			    B_FALSE);
220 		}
221 
222 		(void) nvlist_add_nvlist(nv, path, nv_record);
223 
224 	} else {
225 		convert_field(data, def, path, nv);
226 	}
227 }
228 
229 
230 static fru_regdef_t *
alloc_unknown_fru_regdef(void)231 alloc_unknown_fru_regdef(void)
232 {
233 	fru_regdef_t *p;
234 
235 	p = malloc(sizeof (fru_regdef_t));
236 	if (!p) {
237 		return (NULL);
238 	}
239 	p->version = REGDEF_VERSION;
240 	p->name = NULL;
241 	p->tagType = -1;
242 	p->tagDense = -1;
243 	p->payloadLen = -1;
244 	p->dataLength = -1;
245 	p->dataType = FDTYPE_ByteArray;
246 	p->dispType = FDISP_Hex;
247 	p->purgeable = FRU_WHICH_UNDEFINED;
248 	p->relocatable = FRU_WHICH_UNDEFINED;
249 	p->enumCount = 0;
250 	p-> enumTable = NULL;
251 	p->iterationCount = 0;
252 	p->iterationType = FRU_NOT_ITERATED;
253 	p->exampleString = NULL;
254 
255 	return (p);
256 }
257 
258 static int
convert_packet(fru_tag_t * tag,uint8_t * payload,size_t length,void * args)259 convert_packet(fru_tag_t *tag, uint8_t *payload, size_t length, void *args)
260 {
261 	int tag_type;
262 	size_t payload_length;
263 	const fru_regdef_t *def;
264 	nvlist_t *nv = (nvlist_t *)args;
265 	char tagname[sizeof ("?_0123456789_0123456789")];
266 	tag_type = get_tag_type(tag);
267 	payload_length = 0;
268 
269 	/* check for unrecognized tag */
270 	if ((tag_type == -1) ||
271 	    ((payload_length = get_payload_length(tag)) != length)) {
272 		fru_regdef_t *unknown;
273 
274 		unknown = alloc_unknown_fru_regdef();
275 		unknown->payloadLen = length;
276 		unknown->dataLength = unknown->payloadLen;
277 
278 		if (tag_type == -1) {
279 			(void) snprintf(tagname, sizeof (tagname),
280 			    "INVALID");
281 		} else {
282 			(void) snprintf(tagname, sizeof (tagname),
283 			    "%s_%u_%u_%u", get_tagtype_str(tag_type),
284 			    get_tag_dense(tag), payload_length, length);
285 		}
286 		unknown->name = tagname;
287 		convert_element(payload, unknown, "", nv, B_FALSE);
288 		free(unknown);
289 
290 	} else if ((def = fru_reg_lookup_def_by_tag(*tag)) == NULL) {
291 		fru_regdef_t *unknown;
292 
293 		unknown = alloc_unknown_fru_regdef();
294 		unknown->payloadLen = length;
295 		unknown->dataLength = unknown->payloadLen;
296 
297 		(void) snprintf(tagname, sizeof (tagname), "%s_%u_%u",
298 		    get_tagtype_str(tag_type),
299 		    unknown->tagDense, payload_length);
300 
301 		unknown->name = tagname;
302 		convert_element(payload, unknown, "", nv, B_FALSE);
303 		free(unknown);
304 
305 	} else {
306 
307 		convert_element(payload, def, "", nv, B_FALSE);
308 
309 	}
310 
311 	return (FRU_SUCCESS);
312 }
313 
314 
315 static int
convert_packets_in_segment(fru_seghdl_t segment,void * args)316 convert_packets_in_segment(fru_seghdl_t segment, void *args)
317 {
318 	char *name;
319 	int ret;
320 	nvlist_t *nv = (nvlist_t *)args;
321 	nvlist_t *nv_segment;
322 
323 	ret = fru_get_segment_name(segment, &name);
324 	if (ret != FRU_SUCCESS) {
325 		return (ret);
326 	}
327 
328 	/* create a new nvlist for each segment */
329 	ret = nvlist_alloc(&nv_segment, NV_UNIQUE_NAME, 0);
330 	if (ret) {
331 		free(name);
332 		return (FRU_FAILURE);
333 	}
334 
335 	/* convert the segment to an nvlist */
336 	ret = fru_for_each_packet(segment, convert_packet, nv_segment);
337 	if (ret != FRU_SUCCESS) {
338 		nvlist_free(nv_segment);
339 		free(name);
340 		return (ret);
341 	}
342 
343 	/* add the nvlist for this segment */
344 	(void) nvlist_add_nvlist(nv, name, nv_segment);
345 
346 	free(name);
347 
348 	return (FRU_SUCCESS);
349 }
350 
351 
352 static int
convert_fru(fru_nodehdl_t hdl,nvlist_t ** nvlist)353 convert_fru(fru_nodehdl_t hdl, nvlist_t **nvlist)
354 {
355 	int err;
356 	nvlist_t *nv;
357 	fru_node_t fru_type;
358 
359 	if (fru_get_node_type(hdl, &fru_type) != FRU_SUCCESS) {
360 		return (-1);
361 	}
362 
363 	if (fru_type != FRU_NODE_CONTAINER) {
364 		return (-1);
365 	}
366 
367 	err = nvlist_alloc(&nv, NV_UNIQUE_NAME, 0);
368 	if (err) {
369 		return (err);
370 	}
371 
372 	if (fru_for_each_segment(hdl, convert_packets_in_segment, nv) !=
373 	    FRU_SUCCESS) {
374 		nvlist_free(nv);
375 		return (-1);
376 	}
377 
378 	*nvlist = nv;
379 
380 	return (0);
381 }
382 
383 
384 int
rawfru_to_nvlist(uint8_t * buffer,size_t bufsize,char * cont_type,nvlist_t ** nvlist)385 rawfru_to_nvlist(uint8_t *buffer, size_t bufsize, char *cont_type,
386     nvlist_t **nvlist)
387 {
388 	fru_errno_t fru_err;
389 	fru_nodehdl_t hdl;
390 	int err;
391 
392 	(void) pthread_mutex_lock(&gLock);
393 	fru_err = fru_open_data_source("raw", buffer, bufsize, cont_type,
394 	    NULL);
395 	if (fru_err != FRU_SUCCESS) {
396 		(void) pthread_mutex_unlock(&gLock);
397 		return (-1);
398 	}
399 	fru_err = fru_get_root(&hdl);
400 	if (fru_err != FRU_SUCCESS) {
401 		(void) pthread_mutex_unlock(&gLock);
402 		return (-1);
403 	}
404 
405 	err = convert_fru(hdl, nvlist);
406 
407 	(void) fru_close_data_source();
408 
409 	(void) pthread_mutex_unlock(&gLock);
410 
411 	return (err);
412 }
413