xref: /freebsd/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmraw.c (revision 93a065e7496dfbfbd0a5b0208ef763f37ea975c7)
1 /*-
2  * Copyright (c) 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 #ifdef _KERNEL
34 
35 #include <sys/param.h>
36 #include <sys/ctype.h>
37 #include <sys/malloc.h>
38 #include <sys/systm.h>
39 
40 #else /* !_KERNEL */
41 
42 #include <ctype.h>
43 #include <stdint.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 
48 #endif /* _KERNEL */
49 
50 #include "bhnd_nvram_private.h"
51 
52 #include "bhnd_nvram_datavar.h"
53 
54 /*
55  * Broadcom-RAW NVRAM data class.
56  *
57  * The Broadcom NVRAM NUL-delimited ASCII format is used by most
58  * Broadcom SoCs.
59  *
60  * The NVRAM data is encoded as a stream of of NUL-terminated 'key=value'
61  * strings; the end of the stream is denoted by a single extra NUL character.
62  */
63 
64 struct bhnd_nvram_bcmraw;
65 
66 /** BCM-RAW NVRAM data class instance */
67 struct bhnd_nvram_bcmraw {
68 	struct bhnd_nvram_data		 nv;	/**< common instance state */
69 	char				*data;	/**< backing buffer */
70 	size_t				 size;	/**< buffer size */
71 	size_t				 count;	/**< variable count */
72 };
73 
74 BHND_NVRAM_DATA_CLASS_DEFN(bcmraw, "Broadcom (RAW)",
75     BHND_NVRAM_DATA_CAP_DEVPATHS, sizeof(struct bhnd_nvram_bcmraw))
76 
77 static int
78 bhnd_nvram_bcmraw_probe(struct bhnd_nvram_io *io)
79 {
80 	char	 envp[16];
81 	size_t	 envp_len;
82 	size_t	 io_size;
83 	int	 error;
84 
85 	io_size = bhnd_nvram_io_getsize(io);
86 
87 	/*
88 	 * Fetch initial bytes
89 	 */
90 	envp_len = bhnd_nv_ummin(sizeof(envp), io_size);
91 	if ((error = bhnd_nvram_io_read(io, 0x0, envp, envp_len)))
92 		return (error);
93 
94 	/* An empty BCM-RAW buffer should still contain a single terminating
95 	 * NUL */
96 	if (envp_len == 0)
97 		return (ENXIO);
98 
99 	if (envp_len == 1) {
100 		if (envp[0] != '\0')
101 			return (ENXIO);
102 
103 		return (BHND_NVRAM_DATA_PROBE_MAYBE);
104 	}
105 
106 	/* Must contain only printable ASCII characters delimited
107 	 * by NUL record delimiters */
108 	for (size_t i = 0; i < envp_len; i++) {
109 		char c = envp[i];
110 
111 		/* If we hit a newline, this is probably BCM-TXT */
112 		if (c == '\n')
113 			return (ENXIO);
114 
115 		if (c == '\0' && !bhnd_nv_isprint(c))
116 			continue;
117 	}
118 
119 	/* A valid BCM-RAW buffer should contain a terminating NUL for
120 	 * the last record, followed by a final empty record terminated by
121 	 * NUL */
122 	envp_len = 2;
123 	if (io_size < envp_len)
124 		return (ENXIO);
125 
126 	if ((error = bhnd_nvram_io_read(io, io_size-envp_len, envp, envp_len)))
127 		return (error);
128 
129 	if (envp[0] != '\0' || envp[1] != '\0')
130 		return (ENXIO);
131 
132 	return (BHND_NVRAM_DATA_PROBE_MAYBE + 1);
133 }
134 
135 static int
136 bhnd_nvram_bcmraw_serialize(bhnd_nvram_data_class *cls, bhnd_nvram_plist *props,
137     bhnd_nvram_plist *options, void *outp, size_t *olen)
138 {
139 	bhnd_nvram_prop	*prop;
140 	size_t		 limit, nbytes;
141 	int		 error;
142 
143 	/* Determine output byte limit */
144 	if (outp != NULL)
145 		limit = *olen;
146 	else
147 		limit = 0;
148 
149 	nbytes = 0;
150 
151 	/* Write all properties */
152 	prop = NULL;
153 	while ((prop = bhnd_nvram_plist_next(props, prop)) != NULL) {
154 		const char	*name;
155 		char		*p;
156 		size_t		 prop_limit;
157 		size_t		 name_len, value_len;
158 
159 		if (outp == NULL || limit < nbytes) {
160 			p = NULL;
161 			prop_limit = 0;
162 		} else {
163 			p = ((char *)outp) + nbytes;
164 			prop_limit = limit - nbytes;
165 		}
166 
167 		/* Fetch and write name + '=' to output */
168 		name = bhnd_nvram_prop_name(prop);
169 		name_len = strlen(name) + 1;
170 
171 		if (prop_limit > name_len) {
172 			memcpy(p, name, name_len - 1);
173 			p[name_len - 1] = '=';
174 
175 			prop_limit -= name_len;
176 			p += name_len;
177 		} else {
178 			prop_limit = 0;
179 			p = NULL;
180 		}
181 
182 		/* Advance byte count */
183 		if (SIZE_MAX - nbytes < name_len)
184 			return (EFTYPE); /* would overflow size_t */
185 
186 		nbytes += name_len;
187 
188 		/* Attempt to write NUL-terminated value to output */
189 		value_len = prop_limit;
190 		error = bhnd_nvram_prop_encode(prop, p, &value_len,
191 		    BHND_NVRAM_TYPE_STRING);
192 
193 		/* If encoding failed for any reason other than ENOMEM (which
194 		 * we'll detect and report after encoding all properties),
195 		 * return immediately */
196 		if (error && error != ENOMEM) {
197 			BHND_NV_LOG("error serializing %s to required type "
198 			    "%s: %d\n", name,
199 			    bhnd_nvram_type_name(BHND_NVRAM_TYPE_STRING),
200 			    error);
201 			return (error);
202 		}
203 
204 		/* Advance byte count */
205 		if (SIZE_MAX - nbytes < value_len)
206 			return (EFTYPE); /* would overflow size_t */
207 
208 		nbytes += value_len;
209 	}
210 
211 	/* Write terminating '\0' */
212 	if (limit > nbytes)
213 		*((char *)outp + nbytes) = '\0';
214 
215 	if (nbytes == SIZE_MAX)
216 		return (EFTYPE); /* would overflow size_t */
217 	else
218 		nbytes++;
219 
220 	/* Provide required length */
221 	*olen = nbytes;
222 	if (limit < *olen) {
223 		if (outp == NULL)
224 			return (0);
225 
226 		return (ENOMEM);
227 	}
228 
229 	return (0);
230 }
231 
232 /**
233  * Initialize @p bcm with the provided NVRAM data mapped by @p src.
234  *
235  * @param bcm A newly allocated data instance.
236  */
237 static int
238 bhnd_nvram_bcmraw_init(struct bhnd_nvram_bcmraw *bcm, struct bhnd_nvram_io *src)
239 {
240 	size_t	 io_size;
241 	size_t	 capacity, offset;
242 	int	 error;
243 
244 	/* Fetch the input image size */
245 	io_size = bhnd_nvram_io_getsize(src);
246 
247 	/* Allocate a buffer large enough to hold the NVRAM image, and
248 	 * an extra EOF-signaling NUL (on the chance it's missing from the
249 	 * source data) */
250 	if (io_size == SIZE_MAX)
251 		return (ENOMEM);
252 
253 	capacity = io_size + 1 /* room for extra NUL */;
254 	bcm->size = io_size;
255 	if ((bcm->data = bhnd_nv_malloc(capacity)) == NULL)
256 		return (ENOMEM);
257 
258 	/* Copy in the NVRAM image */
259 	if ((error = bhnd_nvram_io_read(src, 0x0, bcm->data, io_size)))
260 		return (error);
261 
262 	/* Process the buffer */
263 	bcm->count = 0;
264 	for (offset = 0; offset < bcm->size; offset++) {
265 		char		*envp;
266 		const char	*name, *value;
267 		size_t		 envp_len;
268 		size_t		 name_len, value_len;
269 
270 		/* Parse the key=value string */
271 		envp = (char *) (bcm->data + offset);
272 		envp_len = strnlen(envp, bcm->size - offset);
273 		error = bhnd_nvram_parse_env(envp, envp_len, '=', &name,
274 					     &name_len, &value, &value_len);
275 		if (error) {
276 			BHND_NV_LOG("error parsing envp at offset %#zx: %d\n",
277 			    offset, error);
278 			return (error);
279 		}
280 
281 		/* Insert a '\0' character, replacing the '=' delimiter and
282 		 * allowing us to vend references directly to the variable
283 		 * name */
284 		*(envp + name_len) = '\0';
285 
286 		/* Add to variable count */
287 		bcm->count++;
288 
289 		/* Seek past the value's terminating '\0' */
290 		offset += envp_len;
291 		if (offset == io_size) {
292 			BHND_NV_LOG("missing terminating NUL at offset %#zx\n",
293 			    offset);
294 			return (EINVAL);
295 		}
296 
297 		/* If we hit EOF without finding a terminating NUL
298 		 * byte, we need to append it */
299 		if (++offset == bcm->size) {
300 			BHND_NV_ASSERT(offset < capacity,
301 			    ("appending past end of buffer"));
302 			bcm->size++;
303 			*(bcm->data + offset) = '\0';
304 		}
305 
306 		/* Check for explicit EOF (encoded as a single empty NUL
307 		 * terminated string) */
308 		if (*(bcm->data + offset) == '\0')
309 			break;
310 	}
311 
312 	/* Reclaim any unused space in he backing buffer */
313 	if (offset < bcm->size) {
314 		bcm->data = bhnd_nv_reallocf(bcm->data, bcm->size);
315 		if (bcm->data == NULL)
316 			return (ENOMEM);
317 	}
318 
319 	return (0);
320 }
321 
322 static int
323 bhnd_nvram_bcmraw_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io)
324 {
325 	struct bhnd_nvram_bcmraw	*bcm;
326 	int				 error;
327 
328 	bcm = (struct bhnd_nvram_bcmraw *)nv;
329 
330 	/* Parse the BCM input data and initialize our backing
331 	 * data representation */
332 	if ((error = bhnd_nvram_bcmraw_init(bcm, io))) {
333 		bhnd_nvram_bcmraw_free(nv);
334 		return (error);
335 	}
336 
337 	return (0);
338 }
339 
340 static void
341 bhnd_nvram_bcmraw_free(struct bhnd_nvram_data *nv)
342 {
343 	struct bhnd_nvram_bcmraw *bcm = (struct bhnd_nvram_bcmraw *)nv;
344 
345 	if (bcm->data != NULL)
346 		bhnd_nv_free(bcm->data);
347 }
348 
349 static bhnd_nvram_plist *
350 bhnd_nvram_bcmraw_options(struct bhnd_nvram_data *nv)
351 {
352 	return (NULL);
353 }
354 
355 static size_t
356 bhnd_nvram_bcmraw_count(struct bhnd_nvram_data *nv)
357 {
358 	struct bhnd_nvram_bcmraw *bcm = (struct bhnd_nvram_bcmraw *)nv;
359 
360 	return (bcm->count);
361 }
362 
363 static uint32_t
364 bhnd_nvram_bcmraw_caps(struct bhnd_nvram_data *nv)
365 {
366 	return (BHND_NVRAM_DATA_CAP_READ_PTR|BHND_NVRAM_DATA_CAP_DEVPATHS);
367 }
368 
369 static const char *
370 bhnd_nvram_bcmraw_next(struct bhnd_nvram_data *nv, void **cookiep)
371 {
372 	struct bhnd_nvram_bcmraw	*bcm;
373 	const char			*envp;
374 
375 	bcm = (struct bhnd_nvram_bcmraw *)nv;
376 
377 	if (*cookiep == NULL) {
378 		/* Start at the first NVRAM data record */
379 		envp = bcm->data;
380 	} else {
381 		/* Seek to next record */
382 		envp = *cookiep;
383 		envp += strlen(envp) + 1;	/* key + '\0' */
384 		envp += strlen(envp) + 1;	/* value + '\0' */
385 	}
386 
387 	/* EOF? */
388 	if (*envp == '\0')
389 		return (NULL);
390 
391 	*cookiep = (void *)(uintptr_t)envp;
392 	return (envp);
393 }
394 
395 static void *
396 bhnd_nvram_bcmraw_find(struct bhnd_nvram_data *nv, const char *name)
397 {
398 	return (bhnd_nvram_data_generic_find(nv, name));
399 }
400 
401 static int
402 bhnd_nvram_bcmraw_getvar_order(struct bhnd_nvram_data *nv, void *cookiep1,
403     void *cookiep2)
404 {
405 	if (cookiep1 < cookiep2)
406 		return (-1);
407 
408 	if (cookiep1 > cookiep2)
409 		return (1);
410 
411 	return (0);
412 }
413 
414 static int
415 bhnd_nvram_bcmraw_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
416     size_t *len, bhnd_nvram_type type)
417 {
418 	return (bhnd_nvram_data_generic_rp_getvar(nv, cookiep, buf, len, type));
419 }
420 
421 static int
422 bhnd_nvram_bcmraw_copy_val(struct bhnd_nvram_data *nv, void *cookiep,
423     bhnd_nvram_val **value)
424 {
425 	return (bhnd_nvram_data_generic_rp_copy_val(nv, cookiep, value));
426 }
427 
428 static const void *
429 bhnd_nvram_bcmraw_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep,
430     size_t *len, bhnd_nvram_type *type)
431 {
432 	const char *envp;
433 
434 	/* Cookie points to key\0value\0 -- get the value address */
435 	envp = cookiep;
436 	envp += strlen(envp) + 1;	/* key + '\0' */
437 	*len = strlen(envp) + 1;	/* value + '\0' */
438 	*type = BHND_NVRAM_TYPE_STRING;
439 
440 	return (envp);
441 }
442 
443 static const char *
444 bhnd_nvram_bcmraw_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)
445 {
446 	/* Cookie points to key\0value\0 */
447 	return (cookiep);
448 }
449 
450 static int
451 bhnd_nvram_bcmraw_filter_setvar(struct bhnd_nvram_data *nv, const char *name,
452     bhnd_nvram_val *value, bhnd_nvram_val **result)
453 {
454 	bhnd_nvram_val	*str;
455 	int		 error;
456 
457 	/* Name (trimmed of any path prefix) must be valid */
458 	if (!bhnd_nvram_validate_name(bhnd_nvram_trim_path_name(name)))
459 		return (EINVAL);
460 
461 	/* Value must be bcm-formatted string */
462 	error = bhnd_nvram_val_convert_new(&str, &bhnd_nvram_val_bcm_string_fmt,
463 	    value, BHND_NVRAM_VAL_DYNAMIC);
464 	if (error)
465 		return (error);
466 
467 	/* Success. Transfer result ownership to the caller. */
468 	*result = str;
469 	return (0);
470 }
471 
472 static int
473 bhnd_nvram_bcmraw_filter_unsetvar(struct bhnd_nvram_data *nv, const char *name)
474 {
475 	/* We permit deletion of any variable */
476 	return (0);
477 }
478