xref: /freebsd/sys/dev/bhnd/nvram/bhnd_nvram_data.c (revision 77cb4d3e)
1 /*-
2  * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer,
10  *    without modification.
11  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13  *    redistribution must be conditioned upon including a substantially
14  *    similar Disclaimer requirement for further binary redistribution.
15  *
16  * NO WARRANTY
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27  * THE POSSIBILITY OF SUCH DAMAGES.
28  */
29 
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32 
33 
34 #ifdef _KERNEL
35 
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 
39 #include <machine/_inttypes.h>
40 
41 #else /* !_KERNEL */
42 
43 #include <errno.h>
44 #include <stdint.h>
45 #include <stdlib.h>
46 #include <string.h>
47 
48 #endif /* _KERNEL */
49 
50 #include "bhnd_nvram_private.h"
51 #include "bhnd_nvram_io.h"
52 
53 #include "bhnd_nvram_datavar.h"
54 #include "bhnd_nvram_data.h"
55 
56 /**
57  * Return a human-readable description for the given NVRAM data class.
58  *
59  * @param cls The NVRAM class.
60  */
61 const char *
62 bhnd_nvram_data_class_desc(bhnd_nvram_data_class_t *cls)
63 {
64 	return (cls->desc);
65 }
66 
67 /**
68  * Probe to see if this NVRAM data class class supports the data mapped by the
69  * given I/O context, returning a BHND_NVRAM_DATA_PROBE probe result.
70  *
71  * @param cls The NVRAM class.
72  * @param io An I/O context mapping the NVRAM data.
73  *
74  * @retval 0 if this is the only possible NVRAM data class for @p io.
75  * @retval negative if the probe succeeds, a negative value should be returned;
76  * the class returning the highest negative value should be selected to handle
77  * NVRAM parsing.
78  * @retval ENXIO If the NVRAM format is not handled by @p cls.
79  * @retval positive if an error occurs during probing, a regular unix error
80  * code should be returned.
81  */
82 int
83 bhnd_nvram_data_probe(bhnd_nvram_data_class_t *cls, struct bhnd_nvram_io *io)
84 {
85 	return (cls->op_probe(io));
86 }
87 
88 /**
89  * Probe to see if an NVRAM data class in @p classes supports parsing
90  * of the data mapped by @p io, returning the parsed data in @p data.
91  *
92  * The caller is responsible for deallocating the returned instance via
93  * bhnd_nvram_data_release().
94  *
95  * @param[out] data On success, the parsed NVRAM data instance.
96  * @param io An I/O context mapping the NVRAM data to be copied and parsed.
97  * @param classes An array of NVRAM data classes to be probed, or NULL to
98  * probe the default supported set.
99  * @param num_classes The number of NVRAM data classes in @p classes.
100  *
101  * @retval 0 success
102  * @retval ENXIO if no class is found capable of parsing @p io.
103  * @retval non-zero if an error otherwise occurs during allocation,
104  * initialization, or parsing of the NVRAM data, a regular unix error code
105  * will be returned.
106  */
107 int
108 bhnd_nvram_data_probe_classes(struct bhnd_nvram_data **data,
109     struct bhnd_nvram_io *io, bhnd_nvram_data_class_t *classes[],
110     size_t num_classes)
111 {
112 	bhnd_nvram_data_class_t *cls;
113 	int			 error, prio, result;
114 
115 	cls = NULL;
116 	prio = 0;
117 	*data = NULL;
118 
119 	/* If class array is NULL, default to our linker set */
120 	if (classes == NULL) {
121 		classes = SET_BEGIN(bhnd_nvram_data_class_set);
122 		num_classes = SET_COUNT(bhnd_nvram_data_class_set);
123 	}
124 
125 	/* Try to find the best data class capable of parsing io */
126 	for (size_t i = 0; i < num_classes; i++) {
127 		bhnd_nvram_data_class_t *next_cls;
128 
129 		next_cls = classes[i];
130 
131 		/* Try to probe */
132 		result = bhnd_nvram_data_probe(next_cls, io);
133 
134 		/* The parser did not match if an error was returned */
135 		if (result > 0)
136 			continue;
137 
138 		/* Lower priority than previous match; keep
139 		 * searching */
140 		if (cls != NULL && result <= prio)
141 			continue;
142 
143 		/* Drop any previously parsed data */
144 		if (*data != NULL) {
145 			bhnd_nvram_data_release(*data);
146 			*data = NULL;
147 		}
148 
149 		/* If this is a 'maybe' match, attempt actual parsing to
150 		 * verify that this does in fact match */
151 		if (result <= BHND_NVRAM_DATA_PROBE_MAYBE) {
152 			/* If parsing fails, keep searching */
153 			error = bhnd_nvram_data_new(next_cls, data, io);
154 			if (error)
155 				continue;
156 		}
157 
158 		/* Record best new match */
159 		prio = result;
160 		cls = next_cls;
161 
162 
163 		/* Terminate search immediately on
164 		 * BHND_NVRAM_DATA_PROBE_SPECIFIC */
165 		if (result == BHND_NVRAM_DATA_PROBE_SPECIFIC)
166 			break;
167 	}
168 
169 	/* If no match, return error */
170 	if (cls == NULL)
171 		return (ENXIO);
172 
173 	/* If the NVRAM data was not parsed above, do so now */
174 	if (*data == NULL) {
175 		if ((error = bhnd_nvram_data_new(cls, data, io)))
176 			return (error);
177 	}
178 
179 	return (0);
180 }
181 
182 /**
183  * Allocate and initialize a new instance of data class @p cls, copying and
184  * parsing NVRAM data from @p io.
185  *
186  * The caller is responsible for releasing the returned parser instance
187  * reference via bhnd_nvram_data_release().
188  *
189  * @param cls If non-NULL, the data class to be allocated. If NULL,
190  * bhnd_nvram_data_probe_classes() will be used to determine the data format.
191  * @param[out] nv On success, a pointer to the newly allocated NVRAM data instance.
192  * @param io An I/O context mapping the NVRAM data to be copied and parsed.
193  *
194  * @retval 0 success
195  * @retval non-zero if an error occurs during allocation or initialization, a
196  * regular unix error code will be returned.
197  */
198 int
199 bhnd_nvram_data_new(bhnd_nvram_data_class_t *cls,
200     struct bhnd_nvram_data **nv, struct bhnd_nvram_io *io)
201 {
202 	struct bhnd_nvram_data	*data;
203 	int			 error;
204 
205 	/* If NULL, try to identify the appropriate class */
206 	if (cls == NULL)
207 		return (bhnd_nvram_data_probe_classes(nv, io, NULL, 0));
208 
209 	/* Allocate new instance */
210 	BHND_NV_ASSERT(sizeof(struct bhnd_nvram_data) <= cls->size,
211 	    ("instance size %zu less than minimum %zu", cls->size,
212 	     sizeof(struct bhnd_nvram_data)));
213 
214 	data = bhnd_nv_calloc(1, cls->size);
215 	data->cls = cls;
216 	refcount_init(&data->refs, 1);
217 
218 	/* Let the class handle initialization */
219 	if ((error = cls->op_new(data, io))) {
220 		bhnd_nv_free(data);
221 		return (error);
222 	}
223 
224 	*nv = data;
225 	return (0);
226 }
227 
228 /**
229  * Retain and return a reference to the given data instance.
230  *
231  * @param nv The reference to be retained.
232  */
233 struct bhnd_nvram_data *
234 bhnd_nvram_data_retain(struct bhnd_nvram_data *nv)
235 {
236 	refcount_acquire(&nv->refs);
237 	return (nv);
238 }
239 
240 /**
241  * Release a reference to the given data instance.
242  *
243  * If this is the last reference, the data instance and its associated
244  * resources will be freed.
245  *
246  * @param nv The reference to be released.
247  */
248 void
249 bhnd_nvram_data_release(struct bhnd_nvram_data *nv)
250 {
251 	if (!refcount_release(&nv->refs))
252 		return;
253 
254 	/* Free any internal resources */
255 	nv->cls->op_free(nv);
256 
257 	/* Free the instance allocation */
258 	bhnd_nv_free(nv);
259 }
260 
261 /**
262  * Return a pointer to @p nv's data class.
263  *
264  * @param nv The NVRAM data instance to be queried.
265  */
266 bhnd_nvram_data_class_t *
267 bhnd_nvram_data_class(struct bhnd_nvram_data *nv)
268 {
269 	return (nv->cls);
270 }
271 
272 /**
273  * Return the number of variables in @p nv.
274  *
275  * @param nv The NVRAM data to be queried.
276  */
277 size_t
278 bhnd_nvram_data_count(struct bhnd_nvram_data *nv)
279 {
280 	return (nv->cls->op_count(nv));
281 }
282 
283 /**
284  * Compute the size of the serialized form of @p nv.
285  *
286  * Serialization may be performed via bhnd_nvram_data_serialize().
287  *
288  * @param	nv	The NVRAM data to be queried.
289  * @param[out]	len	On success, will be set to the computed size.
290  *
291  * @retval 0		success
292  * @retval non-zero	if computing the serialized size otherwise fails, a
293  *			regular unix error code will be returned.
294  */
295 int
296 bhnd_nvram_data_size(struct bhnd_nvram_data *nv, size_t *len)
297 {
298 	return (nv->cls->op_size(nv, len));
299 }
300 
301 /**
302  * Serialize the NVRAM data to @p buf, using the NVRAM data class' native
303  * format.
304  *
305  * The resulting serialization may be reparsed with @p nv's BHND NVRAM data
306  * class.
307  *
308  * @param		nv	The NVRAM data to be serialized.
309  * @param[out]		buf	On success, the serialed NVRAM data will be
310  *				written to this buffer. This argment may be
311  *				NULL if the value is not desired.
312  * @param[in,out]	len	The capacity of @p buf. On success, will be set
313  *				to the actual length of the serialized data.
314  *
315  * @retval 0		success
316  * @retval ENOMEM	If @p buf is non-NULL and a buffer of @p len is too
317  *			small to hold the serialized data.
318  * @retval non-zero	If serialization otherwise fails, a regular unix error
319  *			code will be returned.
320  */
321 int
322 bhnd_nvram_data_serialize(struct bhnd_nvram_data *nv,
323     void *buf, size_t *len)
324 {
325 	return (nv->cls->op_serialize(nv, buf, len));
326 }
327 
328 /**
329  * Return the capability flags (@see BHND_NVRAM_DATA_CAP_*) for @p nv.
330  *
331  * @param	nv	The NVRAM data to be queried.
332  */
333 uint32_t
334 bhnd_nvram_data_caps(struct bhnd_nvram_data *nv)
335 {
336 	return (nv->cls->op_caps(nv));
337 }
338 
339 /**
340  * Iterate over @p nv, returning the names of subsequent variables.
341  *
342  * @param		nv	The NVRAM data to be iterated.
343  * @param[in,out]	cookiep	A pointer to a cookiep value previously returned
344  *				by bhnd_nvram_data_next(), or a NULL value to
345  *				begin iteration.
346  *
347  * @return Returns the next variable name, or NULL if there are no more
348  * variables defined in @p nv.
349  */
350 const char *
351 bhnd_nvram_data_next(struct bhnd_nvram_data *nv, void **cookiep)
352 {
353 	return (nv->cls->op_next(nv, cookiep));
354 }
355 
356 /**
357  * Search @p nv for a named variable, returning the variable's opaque reference
358  * if found, or NULL if unavailable.
359  *
360  * The BHND_NVRAM_DATA_CAP_INDEXED capability flag will be returned by
361  * bhnd_nvram_data_caps() if @p nv supports effecient name-based
362  * lookups.
363  *
364  * @param	nv	The NVRAM data to search.
365  * @param	name	The name to search for.
366  *
367  * @retval non-NULL	If @p name is found, the opaque cookie value will be
368  *			returned.
369  * @retval NULL		If @p name is not found.
370  */
371 void *
372 bhnd_nvram_data_find(struct bhnd_nvram_data *nv, const char *name)
373 {
374 	return (nv->cls->op_find(nv, name));
375 }
376 
377 /**
378  * A generic implementation of bhnd_nvram_data_find().
379  *
380  * This implementation will use bhnd_nvram_data_next() to perform a
381  * simple O(n) case-insensitve search for @p name.
382  */
383 void *
384 bhnd_nvram_data_generic_find(struct bhnd_nvram_data *nv, const char *name)
385 {
386 	const char	*next;
387 	void		*cookiep;
388 
389 	cookiep = NULL;
390 	while ((next = bhnd_nvram_data_next(nv, &cookiep))) {
391 		if (strcasecmp(name, next) == 0)
392 			return (cookiep);
393 	}
394 
395 	/* Not found */
396 	return (NULL);
397 }
398 
399 /**
400  * Read a variable and decode as @p type.
401  *
402  * @param		nv	The NVRAM data.
403  * @param		cookiep	An NVRAM variable cookie previously returned
404  *				via bhnd_nvram_data_next() or
405  *				bhnd_nvram_data_find().
406  * @param[out]		buf	On success, the requested value will be written
407  *				to this buffer. This argment may be NULL if
408  *				the value is not desired.
409  * @param[in,out]	len	The capacity of @p buf. On success, will be set
410  *				to the actual size of the requested value.
411  * @param		type	The data type to be written to @p buf.
412  *
413  * @retval 0		success
414  * @retval ENOMEM	If @p buf is non-NULL and a buffer of @p len is too
415  *			small to hold the requested value.
416  * @retval EFTYPE	If the variable data cannot be coerced to @p type.
417  * @retval ERANGE	If value coercion would overflow @p type.
418  */
419 int
420 bhnd_nvram_data_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
421     size_t *len, bhnd_nvram_type type)
422 {
423 	return (nv->cls->op_getvar(nv, cookiep, buf, len, type));
424 }
425 
426 /**
427  * A generic implementation of bhnd_nvram_data_getvar().
428  *
429  * This implementation will call bhnd_nvram_data_getvar_ptr() to fetch
430  * a pointer to the variable data and perform data coercion on behalf
431  * of the caller.
432  *
433  * If a variable definition for the requested variable is available via
434  * bhnd_nvram_find_vardefn(), the definition will be used to provide
435  * formatting hints to bhnd_nvram_coerce_value().
436  */
437 int
438 bhnd_nvram_data_generic_rp_getvar(struct bhnd_nvram_data *nv, void *cookiep,
439     void *outp, size_t *olen, bhnd_nvram_type otype)
440 {
441 	bhnd_nvram_val_t		 val;
442 	const struct bhnd_nvram_vardefn	*vdefn;
443 	const bhnd_nvram_val_fmt_t	*fmt;
444 	const char			*name;
445 	const void			*vptr;
446 	bhnd_nvram_type			 vtype;
447 	size_t				 vlen;
448 	int				 error;
449 
450 	BHND_NV_ASSERT(bhnd_nvram_data_caps(nv) & BHND_NVRAM_DATA_CAP_READ_PTR,
451 	    ("instance does not advertise READ_PTR support"));
452 
453 	/* Fetch pointer to our variable data */
454 	vptr = bhnd_nvram_data_getvar_ptr(nv, cookiep, &vlen, &vtype);
455 	if (vptr == NULL)
456 		return (EINVAL);
457 
458 	/* Use the NVRAM string support */
459 	switch (vtype) {
460 	case BHND_NVRAM_TYPE_STRING:
461 	case BHND_NVRAM_TYPE_STRING_ARRAY:
462 		fmt = &bhnd_nvram_val_bcm_string_fmt;
463 		break;
464 	default:
465 		fmt = NULL;
466 	}
467 
468 	/* Check the variable definition table for a matching entry; if
469 	 * it exists, use it to populate the value format. */
470 	name = bhnd_nvram_data_getvar_name(nv, cookiep);
471 	vdefn = bhnd_nvram_find_vardefn(name);
472 	if (vdefn != NULL)
473 		fmt = vdefn->fmt;
474 
475 	/* Attempt value coercion */
476 	error = bhnd_nvram_val_init(&val, fmt, vptr, vlen, vtype,
477 	    BHND_NVRAM_VAL_BORROW_DATA);
478 	if (error)
479 		return (error);
480 
481 	error = bhnd_nvram_val_encode(&val, outp, olen, otype);
482 
483 	/* Clean up */
484 	bhnd_nvram_val_release(&val);
485 	return (error);
486 }
487 
488 /**
489  * If available and supported by the NVRAM data instance, return a reference
490  * to the internal buffer containing an entry's variable data,
491  *
492  * Note that string values may not be NUL terminated.
493  *
494  * @param		nv	The NVRAM data.
495  * @param		cookiep	An NVRAM variable cookie previously returned
496  *				via bhnd_nvram_data_next() or
497  *				bhnd_nvram_data_find().
498  * @param[out]		len	On success, will be set to the actual size of
499  *				the requested value.
500  * @param[out]		type	The data type of the entry data.
501  *
502  * @retval non-NULL	success
503  * @retval NULL		if direct data access is unsupported by @p nv, or
504  *			unavailable for @p cookiep.
505  */
506 const void *
507 bhnd_nvram_data_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep,
508     size_t *len, bhnd_nvram_type *type)
509 {
510 	return (nv->cls->op_getvar_ptr(nv, cookiep, len, type));
511 }
512 
513 
514 /**
515  * Return the variable name associated with a given @p cookiep.
516  * @param		nv	The NVRAM data to be iterated.
517  * @param[in,out]	cookiep	A pointer to a cookiep value previously returned
518  *				via bhnd_nvram_data_next() or
519  *				bhnd_nvram_data_find().
520  *
521  * @return Returns the variable's name.
522  */
523 const char *
524 bhnd_nvram_data_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)
525 {
526 	return (nv->cls->op_getvar_name(nv, cookiep));
527 }
528