2 * Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
35 #include <sys/param.h>
36 #include <sys/ctype.h>
37 #include <sys/malloc.h>
38 #include <sys/systm.h>
50 #include "bhnd_nvram_private.h"
52 #include "bhnd_nvram_datavar.h"
53 #include "bhnd_nvram_data_bcmvar.h"
56 * Broadcom-RAW NVRAM data class.
58 * The Broadcom NVRAM NUL-delimited ASCII format is used by most
61 * The NVRAM data is encoded as a stream of of NUL-terminated 'key=value'
62 * strings; the end of the stream is denoted by a single extra NUL character.
65 struct bhnd_nvram_bcmraw;
67 /** BCM-RAW NVRAM data class instance */
68 struct bhnd_nvram_bcmraw {
69 struct bhnd_nvram_data nv; /**< common instance state */
70 char *data; /**< backing buffer */
71 size_t size; /**< buffer size */
72 size_t count; /**< variable count */
75 BHND_NVRAM_DATA_CLASS_DEFN(bcmraw, "Broadcom (RAW)",
76 BHND_NVRAM_DATA_CAP_DEVPATHS, sizeof(struct bhnd_nvram_bcmraw))
79 bhnd_nvram_bcmraw_probe(struct bhnd_nvram_io *io)
86 io_size = bhnd_nvram_io_getsize(io);
91 envp_len = bhnd_nv_ummin(sizeof(envp), io_size);
92 if ((error = bhnd_nvram_io_read(io, 0x0, envp, envp_len)))
95 /* An empty BCM-RAW buffer should still contain a single terminating
104 return (BHND_NVRAM_DATA_PROBE_MAYBE);
107 /* Must contain only printable ASCII characters delimited
108 * by NUL record delimiters */
109 for (size_t i = 0; i < envp_len; i++) {
112 /* If we hit a newline, this is probably BCM-TXT */
116 if (c == '\0' && !bhnd_nv_isprint(c))
120 /* A valid BCM-RAW buffer should contain a terminating NUL for
121 * the last record, followed by a final empty record terminated by
124 if (io_size < envp_len)
127 if ((error = bhnd_nvram_io_read(io, io_size-envp_len, envp, envp_len)))
130 if (envp[0] != '\0' || envp[1] != '\0')
133 return (BHND_NVRAM_DATA_PROBE_MAYBE + 1);
137 bhnd_nvram_bcmraw_getvar_direct(struct bhnd_nvram_io *io, const char *name,
138 void *buf, size_t *len, bhnd_nvram_type type)
140 return (bhnd_nvram_bcm_getvar_direct_common(io, name, buf, len, type,
145 bhnd_nvram_bcmraw_serialize(bhnd_nvram_data_class *cls, bhnd_nvram_plist *props,
146 bhnd_nvram_plist *options, void *outp, size_t *olen)
148 bhnd_nvram_prop *prop;
149 size_t limit, nbytes;
152 /* Determine output byte limit */
160 /* Write all properties */
162 while ((prop = bhnd_nvram_plist_next(props, prop)) != NULL) {
166 size_t name_len, value_len;
168 if (outp == NULL || limit < nbytes) {
172 p = ((char *)outp) + nbytes;
173 prop_limit = limit - nbytes;
176 /* Fetch and write name + '=' to output */
177 name = bhnd_nvram_prop_name(prop);
178 name_len = strlen(name) + 1;
180 if (prop_limit > name_len) {
181 memcpy(p, name, name_len - 1);
182 p[name_len - 1] = '=';
184 prop_limit -= name_len;
191 /* Advance byte count */
192 if (SIZE_MAX - nbytes < name_len)
193 return (EFTYPE); /* would overflow size_t */
197 /* Attempt to write NUL-terminated value to output */
198 value_len = prop_limit;
199 error = bhnd_nvram_prop_encode(prop, p, &value_len,
200 BHND_NVRAM_TYPE_STRING);
202 /* If encoding failed for any reason other than ENOMEM (which
203 * we'll detect and report after encoding all properties),
204 * return immediately */
205 if (error && error != ENOMEM) {
206 BHND_NV_LOG("error serializing %s to required type "
208 bhnd_nvram_type_name(BHND_NVRAM_TYPE_STRING),
213 /* Advance byte count */
214 if (SIZE_MAX - nbytes < value_len)
215 return (EFTYPE); /* would overflow size_t */
220 /* Write terminating '\0' */
222 *((char *)outp + nbytes) = '\0';
224 if (nbytes == SIZE_MAX)
225 return (EFTYPE); /* would overflow size_t */
229 /* Provide required length */
242 * Initialize @p bcm with the provided NVRAM data mapped by @p src.
244 * @param bcm A newly allocated data instance.
247 bhnd_nvram_bcmraw_init(struct bhnd_nvram_bcmraw *bcm, struct bhnd_nvram_io *src)
250 size_t capacity, offset;
253 /* Fetch the input image size */
254 io_size = bhnd_nvram_io_getsize(src);
256 /* Allocate a buffer large enough to hold the NVRAM image, and
257 * an extra EOF-signaling NUL (on the chance it's missing from the
259 if (io_size == SIZE_MAX)
262 capacity = io_size + 1 /* room for extra NUL */;
264 if ((bcm->data = bhnd_nv_malloc(capacity)) == NULL)
267 /* Copy in the NVRAM image */
268 if ((error = bhnd_nvram_io_read(src, 0x0, bcm->data, io_size)))
271 /* Process the buffer */
273 for (offset = 0; offset < bcm->size; offset++) {
275 const char *name, *value;
277 size_t name_len, value_len;
279 /* Parse the key=value string */
280 envp = (char *) (bcm->data + offset);
281 envp_len = strnlen(envp, bcm->size - offset);
282 error = bhnd_nvram_parse_env(envp, envp_len, '=', &name,
283 &name_len, &value, &value_len);
285 BHND_NV_LOG("error parsing envp at offset %#zx: %d\n",
290 /* Insert a '\0' character, replacing the '=' delimiter and
291 * allowing us to vend references directly to the variable
293 *(envp + name_len) = '\0';
295 /* Add to variable count */
298 /* Seek past the value's terminating '\0' */
300 if (offset == io_size) {
301 BHND_NV_LOG("missing terminating NUL at offset %#zx\n",
306 /* If we hit EOF without finding a terminating NUL
307 * byte, we need to append it */
308 if (++offset == bcm->size) {
309 BHND_NV_ASSERT(offset < capacity,
310 ("appending past end of buffer"));
312 *(bcm->data + offset) = '\0';
315 /* Check for explicit EOF (encoded as a single empty NUL
316 * terminated string) */
317 if (*(bcm->data + offset) == '\0')
321 /* Reclaim any unused space in he backing buffer */
322 if (offset < bcm->size) {
323 bcm->data = bhnd_nv_reallocf(bcm->data, bcm->size);
324 if (bcm->data == NULL)
332 bhnd_nvram_bcmraw_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io)
334 struct bhnd_nvram_bcmraw *bcm;
337 bcm = (struct bhnd_nvram_bcmraw *)nv;
339 /* Parse the BCM input data and initialize our backing
340 * data representation */
341 if ((error = bhnd_nvram_bcmraw_init(bcm, io))) {
342 bhnd_nvram_bcmraw_free(nv);
350 bhnd_nvram_bcmraw_free(struct bhnd_nvram_data *nv)
352 struct bhnd_nvram_bcmraw *bcm = (struct bhnd_nvram_bcmraw *)nv;
354 if (bcm->data != NULL)
355 bhnd_nv_free(bcm->data);
358 static bhnd_nvram_plist *
359 bhnd_nvram_bcmraw_options(struct bhnd_nvram_data *nv)
365 bhnd_nvram_bcmraw_count(struct bhnd_nvram_data *nv)
367 struct bhnd_nvram_bcmraw *bcm = (struct bhnd_nvram_bcmraw *)nv;
373 bhnd_nvram_bcmraw_caps(struct bhnd_nvram_data *nv)
375 return (BHND_NVRAM_DATA_CAP_READ_PTR|BHND_NVRAM_DATA_CAP_DEVPATHS);
379 bhnd_nvram_bcmraw_next(struct bhnd_nvram_data *nv, void **cookiep)
381 struct bhnd_nvram_bcmraw *bcm;
384 bcm = (struct bhnd_nvram_bcmraw *)nv;
386 if (*cookiep == NULL) {
387 /* Start at the first NVRAM data record */
390 /* Seek to next record */
392 envp += strlen(envp) + 1; /* key + '\0' */
393 envp += strlen(envp) + 1; /* value + '\0' */
400 *cookiep = (void *)(uintptr_t)envp;
405 bhnd_nvram_bcmraw_find(struct bhnd_nvram_data *nv, const char *name)
407 return (bhnd_nvram_data_generic_find(nv, name));
411 bhnd_nvram_bcmraw_getvar_order(struct bhnd_nvram_data *nv, void *cookiep1,
414 if (cookiep1 < cookiep2)
417 if (cookiep1 > cookiep2)
424 bhnd_nvram_bcmraw_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
425 size_t *len, bhnd_nvram_type type)
427 return (bhnd_nvram_data_generic_rp_getvar(nv, cookiep, buf, len, type));
431 bhnd_nvram_bcmraw_copy_val(struct bhnd_nvram_data *nv, void *cookiep,
432 bhnd_nvram_val **value)
434 return (bhnd_nvram_data_generic_rp_copy_val(nv, cookiep, value));
438 bhnd_nvram_bcmraw_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep,
439 size_t *len, bhnd_nvram_type *type)
443 /* Cookie points to key\0value\0 -- get the value address */
445 envp += strlen(envp) + 1; /* key + '\0' */
446 *len = strlen(envp) + 1; /* value + '\0' */
447 *type = BHND_NVRAM_TYPE_STRING;
453 bhnd_nvram_bcmraw_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)
455 /* Cookie points to key\0value\0 */
460 bhnd_nvram_bcmraw_filter_setvar(struct bhnd_nvram_data *nv, const char *name,
461 bhnd_nvram_val *value, bhnd_nvram_val **result)
466 /* Name (trimmed of any path prefix) must be valid */
467 if (!bhnd_nvram_validate_name(bhnd_nvram_trim_path_name(name)))
470 /* Value must be bcm-formatted string */
471 error = bhnd_nvram_val_convert_new(&str, &bhnd_nvram_val_bcm_string_fmt,
472 value, BHND_NVRAM_VAL_DYNAMIC);
476 /* Success. Transfer result ownership to the caller. */
482 bhnd_nvram_bcmraw_filter_unsetvar(struct bhnd_nvram_data *nv, const char *name)
484 /* We permit deletion of any variable */