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