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/param.h>
31 #include <sys/endian.h>
32
33 #ifdef _KERNEL
34
35 #include <sys/bus.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 #include "bhnd_nvram_data_bcmreg.h"
55 #include "bhnd_nvram_data_bcmvar.h"
56
57 /*
58 * Broadcom NVRAM data class.
59 *
60 * The Broadcom NVRAM NUL-delimited ASCII format is used by most
61 * Broadcom SoCs.
62 *
63 * The NVRAM data is encoded as a standard header, followed by series of
64 * NUL-terminated 'key=value' strings; the end of the stream is denoted
65 * by a single extra NUL character.
66 */
67
68 struct bhnd_nvram_bcm;
69
70 static struct bhnd_nvram_bcm_hvar *bhnd_nvram_bcm_gethdrvar(
71 struct bhnd_nvram_bcm *bcm,
72 const char *name);
73 static struct bhnd_nvram_bcm_hvar *bhnd_nvram_bcm_to_hdrvar(
74 struct bhnd_nvram_bcm *bcm,
75 void *cookiep);
76 static size_t bhnd_nvram_bcm_hdrvar_index(
77 struct bhnd_nvram_bcm *bcm,
78 struct bhnd_nvram_bcm_hvar *hvar);
79 /*
80 * Set of BCM NVRAM header values that are required to be mirrored in the
81 * NVRAM data itself.
82 *
83 * If they're not included in the parsed NVRAM data, we need to vend the
84 * header-parsed values with their appropriate keys, and add them in any
85 * updates to the NVRAM data.
86 *
87 * If they're modified in NVRAM, we need to sync the changes with the
88 * the NVRAM header values.
89 */
90 static const struct bhnd_nvram_bcm_hvar bhnd_nvram_bcm_hvars[] = {
91 {
92 .name = BCM_NVRAM_CFG0_SDRAM_INIT_VAR,
93 .type = BHND_NVRAM_TYPE_UINT16,
94 .len = sizeof(uint16_t),
95 .nelem = 1,
96 },
97 {
98 .name = BCM_NVRAM_CFG1_SDRAM_CFG_VAR,
99 .type = BHND_NVRAM_TYPE_UINT16,
100 .len = sizeof(uint16_t),
101 .nelem = 1,
102 },
103 {
104 .name = BCM_NVRAM_CFG1_SDRAM_REFRESH_VAR,
105 .type = BHND_NVRAM_TYPE_UINT16,
106 .len = sizeof(uint16_t),
107 .nelem = 1,
108 },
109 {
110 .name = BCM_NVRAM_SDRAM_NCDL_VAR,
111 .type = BHND_NVRAM_TYPE_UINT32,
112 .len = sizeof(uint32_t),
113 .nelem = 1,
114 },
115 };
116
117 /** BCM NVRAM data class instance */
118 struct bhnd_nvram_bcm {
119 struct bhnd_nvram_data nv; /**< common instance state */
120 struct bhnd_nvram_io *data; /**< backing buffer */
121 bhnd_nvram_plist *opts; /**< serialization options */
122
123 /** BCM header values */
124 struct bhnd_nvram_bcm_hvar hvars[nitems(bhnd_nvram_bcm_hvars)];
125
126 size_t count; /**< total variable count */
127 };
128
129 BHND_NVRAM_DATA_CLASS_DEFN(bcm, "Broadcom", BHND_NVRAM_DATA_CAP_DEVPATHS,
130 sizeof(struct bhnd_nvram_bcm))
131
132 static int
bhnd_nvram_bcm_probe(struct bhnd_nvram_io * io)133 bhnd_nvram_bcm_probe(struct bhnd_nvram_io *io)
134 {
135 struct bhnd_nvram_bcmhdr hdr;
136 int error;
137
138 if ((error = bhnd_nvram_io_read(io, 0x0, &hdr, sizeof(hdr))))
139 return (error);
140
141 if (le32toh(hdr.magic) != BCM_NVRAM_MAGIC)
142 return (ENXIO);
143
144 if (le32toh(hdr.size) > bhnd_nvram_io_getsize(io))
145 return (ENXIO);
146
147 return (BHND_NVRAM_DATA_PROBE_DEFAULT);
148 }
149
150 /**
151 * Parser states for bhnd_nvram_bcm_getvar_direct_common().
152 */
153 typedef enum {
154 BCM_PARSE_KEY_START,
155 BCM_PARSE_KEY_CONT,
156 BCM_PARSE_KEY,
157 BCM_PARSE_NEXT_KEY,
158 BCM_PARSE_VALUE_START,
159 BCM_PARSE_VALUE
160 } bcm_parse_state;
161
162 static int
bhnd_nvram_bcm_getvar_direct(struct bhnd_nvram_io * io,const char * name,void * outp,size_t * olen,bhnd_nvram_type otype)163 bhnd_nvram_bcm_getvar_direct(struct bhnd_nvram_io *io, const char *name,
164 void *outp, size_t *olen, bhnd_nvram_type otype)
165 {
166 return (bhnd_nvram_bcm_getvar_direct_common(io, name, outp, olen, otype,
167 true));
168 }
169
170 /**
171 * Common BCM/BCMRAW implementation of bhnd_nvram_getvar_direct().
172 */
173 int
bhnd_nvram_bcm_getvar_direct_common(struct bhnd_nvram_io * io,const char * name,void * outp,size_t * olen,bhnd_nvram_type otype,bool have_header)174 bhnd_nvram_bcm_getvar_direct_common(struct bhnd_nvram_io *io, const char *name,
175 void *outp, size_t *olen, bhnd_nvram_type otype, bool have_header)
176 {
177 struct bhnd_nvram_bcmhdr hdr;
178 char buf[512];
179 bcm_parse_state pstate;
180 size_t limit, offset;
181 size_t buflen, bufpos;
182 size_t namelen, namepos;
183 size_t vlen;
184 int error;
185
186 limit = bhnd_nvram_io_getsize(io);
187 offset = 0;
188
189 /* Fetch and validate the header */
190 if (have_header) {
191 if ((error = bhnd_nvram_io_read(io, offset, &hdr, sizeof(hdr))))
192 return (error);
193
194 if (le32toh(hdr.magic) != BCM_NVRAM_MAGIC)
195 return (ENXIO);
196
197 offset += sizeof(hdr);
198 limit = bhnd_nv_ummin(le32toh(hdr.size), limit);
199 }
200
201 /* Loop our parser until we find the requested variable, or hit EOF */
202 pstate = BCM_PARSE_KEY_START;
203 buflen = 0;
204 bufpos = 0;
205 namelen = strlen(name);
206 namepos = 0;
207 vlen = 0;
208
209 while ((offset - bufpos) < limit) {
210 BHND_NV_ASSERT(bufpos <= buflen,
211 ("buf position invalid (%zu > %zu)", bufpos, buflen));
212 BHND_NV_ASSERT(buflen <= sizeof(buf),
213 ("buf length invalid (%zu > %zu", buflen, sizeof(buf)));
214
215 /* Repopulate our parse buffer? */
216 if (buflen - bufpos == 0) {
217 BHND_NV_ASSERT(offset < limit, ("offset overrun"));
218
219 buflen = bhnd_nv_ummin(sizeof(buf), limit - offset);
220 bufpos = 0;
221
222 error = bhnd_nvram_io_read(io, offset, buf, buflen);
223 if (error)
224 return (error);
225
226 offset += buflen;
227 }
228
229 switch (pstate) {
230 case BCM_PARSE_KEY_START:
231 BHND_NV_ASSERT(buflen - bufpos > 0, ("empty buffer!"));
232
233 /* An extra '\0' denotes NVRAM EOF */
234 if (buf[bufpos] == '\0')
235 return (ENOENT);
236
237 /* Reset name matching position */
238 namepos = 0;
239
240 /* Start name matching */
241 pstate = BCM_PARSE_KEY_CONT;
242 break;
243
244 case BCM_PARSE_KEY_CONT: {
245 size_t navail, nleft;
246
247 nleft = namelen - namepos;
248 navail = bhnd_nv_ummin(buflen - bufpos, nleft);
249
250 if (strncmp(name+namepos, buf+bufpos, navail) == 0) {
251 /* Matched */
252 namepos += navail;
253 bufpos += navail;
254
255 /* If we've matched the full variable name,
256 * look for its trailing delimiter */
257 if (namepos == namelen)
258 pstate = BCM_PARSE_KEY;
259 } else {
260 /* No match; advance to next entry and restart
261 * name matching */
262 pstate = BCM_PARSE_NEXT_KEY;
263 }
264
265 break;
266 }
267
268 case BCM_PARSE_KEY:
269 BHND_NV_ASSERT(buflen - bufpos > 0, ("empty buffer!"));
270
271 if (buf[bufpos] == '=') {
272 /* Key fully matched; advance past '=' and
273 * parse the value */
274 bufpos++;
275 pstate = BCM_PARSE_VALUE_START;
276 } else {
277 /* No match; advance to next entry and restart
278 * name matching */
279 pstate = BCM_PARSE_NEXT_KEY;
280 }
281
282 break;
283
284 case BCM_PARSE_NEXT_KEY: {
285 const char *p;
286
287 /* Scan for a '\0' terminator */
288 p = memchr(buf+bufpos, '\0', buflen - bufpos);
289
290 if (p != NULL) {
291 /* Found entry terminator; restart name
292 * matching at next entry */
293 pstate = BCM_PARSE_KEY_START;
294 bufpos = (p - buf) + 1 /* skip '\0' */;
295 } else {
296 /* Consumed full buffer looking for '\0';
297 * force repopulation of the buffer and
298 * retry */
299 bufpos = buflen;
300 }
301
302 break;
303 }
304
305 case BCM_PARSE_VALUE_START: {
306 const char *p;
307
308 /* Scan for a '\0' terminator */
309 p = memchr(buf+bufpos, '\0', buflen - bufpos);
310
311 if (p != NULL) {
312 /* Found entry terminator; parse the value */
313 vlen = p - &buf[bufpos];
314 pstate = BCM_PARSE_VALUE;
315
316 } else if (p == NULL && offset == limit) {
317 /* Hit EOF without a terminating '\0';
318 * treat the entry as implicitly terminated */
319 vlen = buflen - bufpos;
320 pstate = BCM_PARSE_VALUE;
321
322 } else if (p == NULL && bufpos > 0) {
323 size_t nread;
324
325 /* Move existing value data to start of
326 * buffer */
327 memmove(buf, buf+bufpos, buflen - bufpos);
328 buflen = bufpos;
329 bufpos = 0;
330
331 /* Populate full buffer to allow retry of
332 * value parsing */
333 nread = bhnd_nv_ummin(sizeof(buf) - buflen,
334 limit - offset);
335
336 error = bhnd_nvram_io_read(io, offset,
337 buf+buflen, nread);
338 if (error)
339 return (error);
340
341 offset += nread;
342 buflen += nread;
343 } else {
344 /* Value exceeds our buffer capacity */
345 BHND_NV_LOG("cannot parse value for '%s' "
346 "(exceeds %zu byte limit)\n", name,
347 sizeof(buf));
348
349 return (ENXIO);
350 }
351
352 break;
353 }
354
355 case BCM_PARSE_VALUE:
356 BHND_NV_ASSERT(vlen <= buflen, ("value buf overrun"));
357
358 return (bhnd_nvram_value_coerce(buf+bufpos, vlen,
359 BHND_NVRAM_TYPE_STRING, outp, olen, otype));
360 }
361 }
362
363 /* Variable not found */
364 return (ENOENT);
365 }
366
367 static int
bhnd_nvram_bcm_serialize(bhnd_nvram_data_class * cls,bhnd_nvram_plist * props,bhnd_nvram_plist * options,void * outp,size_t * olen)368 bhnd_nvram_bcm_serialize(bhnd_nvram_data_class *cls, bhnd_nvram_plist *props,
369 bhnd_nvram_plist *options, void *outp, size_t *olen)
370 {
371 struct bhnd_nvram_bcmhdr hdr;
372 bhnd_nvram_prop *prop;
373 size_t limit, nbytes;
374 uint32_t sdram_ncdl;
375 uint16_t sdram_init, sdram_cfg, sdram_refresh;
376 uint8_t bcm_ver, crc8;
377 int error;
378
379 /* Determine output byte limit */
380 if (outp != NULL)
381 limit = *olen;
382 else
383 limit = 0;
384
385 /* Fetch required header variables */
386 #define PROPS_GET_HDRVAR(_name, _dest, _type) do { \
387 const char *name = BCM_NVRAM_ ## _name ## _VAR; \
388 if (!bhnd_nvram_plist_contains(props, name)) { \
389 BHND_NV_LOG("missing required property: %s\n", \
390 name); \
391 return (EFTYPE); \
392 } \
393 \
394 error = bhnd_nvram_plist_get_encoded(props, name, \
395 (_dest), sizeof(*(_dest)), \
396 BHND_NVRAM_TYPE_ ##_type); \
397 if (error) { \
398 BHND_NV_LOG("error reading required header " \
399 "%s property: %d\n", name, error); \
400 return (EFTYPE); \
401 } \
402 } while (0)
403
404 PROPS_GET_HDRVAR(SDRAM_NCDL, &sdram_ncdl, UINT32);
405 PROPS_GET_HDRVAR(CFG0_SDRAM_INIT, &sdram_init, UINT16);
406 PROPS_GET_HDRVAR(CFG1_SDRAM_CFG, &sdram_cfg, UINT16);
407 PROPS_GET_HDRVAR(CFG1_SDRAM_REFRESH, &sdram_refresh, UINT16);
408
409 #undef PROPS_GET_HDRVAR
410
411 /* Fetch BCM nvram version from options */
412 if (options != NULL &&
413 bhnd_nvram_plist_contains(options, BCM_NVRAM_ENCODE_OPT_VERSION))
414 {
415 error = bhnd_nvram_plist_get_uint8(options,
416 BCM_NVRAM_ENCODE_OPT_VERSION, &bcm_ver);
417 if (error) {
418 BHND_NV_LOG("error reading %s uint8 option value: %d\n",
419 BCM_NVRAM_ENCODE_OPT_VERSION, error);
420 return (EINVAL);
421 }
422 } else {
423 bcm_ver = BCM_NVRAM_CFG0_VER_DEFAULT;
424 }
425
426 /* Construct our header */
427 hdr = (struct bhnd_nvram_bcmhdr) {
428 .magic = htole32(BCM_NVRAM_MAGIC),
429 .size = 0,
430 .cfg0 = 0,
431 .cfg1 = 0,
432 .sdram_ncdl = htole32(sdram_ncdl)
433 };
434
435 hdr.cfg0 = BCM_NVRAM_SET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_CRC, 0x0);
436 hdr.cfg0 = BCM_NVRAM_SET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_VER, bcm_ver);
437 hdr.cfg0 = BCM_NVRAM_SET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_SDRAM_INIT,
438 htole16(sdram_init));
439
440 hdr.cfg1 = BCM_NVRAM_SET_BITS(hdr.cfg1, BCM_NVRAM_CFG1_SDRAM_CFG,
441 htole16(sdram_cfg));
442 hdr.cfg1 = BCM_NVRAM_SET_BITS(hdr.cfg1, BCM_NVRAM_CFG1_SDRAM_REFRESH,
443 htole16(sdram_refresh));
444
445 /* Write the header */
446 nbytes = sizeof(hdr);
447 if (limit >= nbytes)
448 memcpy(outp, &hdr, sizeof(hdr));
449
450 /* Write all properties */
451 prop = NULL;
452 while ((prop = bhnd_nvram_plist_next(props, prop)) != NULL) {
453 const char *name;
454 char *p;
455 size_t prop_limit;
456 size_t name_len, value_len;
457
458 if (outp == NULL || limit < nbytes) {
459 p = NULL;
460 prop_limit = 0;
461 } else {
462 p = ((char *)outp) + nbytes;
463 prop_limit = limit - nbytes;
464 }
465
466 /* Fetch and write name + '=' to output */
467 name = bhnd_nvram_prop_name(prop);
468 name_len = strlen(name) + 1;
469
470 if (prop_limit > name_len) {
471 memcpy(p, name, name_len - 1);
472 p[name_len - 1] = '=';
473
474 prop_limit -= name_len;
475 p += name_len;
476 } else {
477 prop_limit = 0;
478 p = NULL;
479 }
480
481 /* Advance byte count */
482 if (SIZE_MAX - nbytes < name_len)
483 return (EFTYPE); /* would overflow size_t */
484
485 nbytes += name_len;
486
487 /* Attempt to write NUL-terminated value to output */
488 value_len = prop_limit;
489 error = bhnd_nvram_prop_encode(prop, p, &value_len,
490 BHND_NVRAM_TYPE_STRING);
491
492 /* If encoding failed for any reason other than ENOMEM (which
493 * we'll detect and report after encoding all properties),
494 * return immediately */
495 if (error && error != ENOMEM) {
496 BHND_NV_LOG("error serializing %s to required type "
497 "%s: %d\n", name,
498 bhnd_nvram_type_name(BHND_NVRAM_TYPE_STRING),
499 error);
500 return (error);
501 }
502
503 /* Advance byte count */
504 if (SIZE_MAX - nbytes < value_len)
505 return (EFTYPE); /* would overflow size_t */
506
507 nbytes += value_len;
508 }
509
510 /* Write terminating '\0' */
511 if (limit > nbytes)
512 *((char *)outp + nbytes) = '\0';
513
514 if (nbytes == SIZE_MAX)
515 return (EFTYPE); /* would overflow size_t */
516 else
517 nbytes++;
518
519 /* Update header length; this must fit within the header's 32-bit size
520 * field */
521 if (nbytes <= UINT32_MAX) {
522 hdr.size = (uint32_t)nbytes;
523 } else {
524 BHND_NV_LOG("size %zu exceeds maximum supported size of %u "
525 "bytes\n", nbytes, UINT32_MAX);
526 return (EFTYPE);
527 }
528
529 /* Provide required length */
530 *olen = nbytes;
531 if (limit < *olen) {
532 if (outp == NULL)
533 return (0);
534
535 return (ENOMEM);
536 }
537
538 /* Calculate the CRC value */
539 BHND_NV_ASSERT(nbytes >= BCM_NVRAM_CRC_SKIP, ("invalid output size"));
540 crc8 = bhnd_nvram_crc8((uint8_t *)outp + BCM_NVRAM_CRC_SKIP,
541 nbytes - BCM_NVRAM_CRC_SKIP, BHND_NVRAM_CRC8_INITIAL);
542
543 /* Update CRC and write the finalized header */
544 BHND_NV_ASSERT(nbytes >= sizeof(hdr), ("invalid output size"));
545 hdr.cfg0 = BCM_NVRAM_SET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_CRC, crc8);
546 memcpy(outp, &hdr, sizeof(hdr));
547
548 return (0);
549 }
550
551 /**
552 * Initialize @p bcm with the provided NVRAM data mapped by @p src.
553 *
554 * @param bcm A newly allocated data instance.
555 */
556 static int
bhnd_nvram_bcm_init(struct bhnd_nvram_bcm * bcm,struct bhnd_nvram_io * src)557 bhnd_nvram_bcm_init(struct bhnd_nvram_bcm *bcm, struct bhnd_nvram_io *src)
558 {
559 struct bhnd_nvram_bcmhdr hdr;
560 uint8_t *p;
561 void *ptr;
562 size_t io_offset, io_size;
563 uint8_t crc, valid, bcm_ver;
564 int error;
565
566 if ((error = bhnd_nvram_io_read(src, 0x0, &hdr, sizeof(hdr))))
567 return (error);
568
569 if (le32toh(hdr.magic) != BCM_NVRAM_MAGIC)
570 return (ENXIO);
571
572 /* Fetch the actual NVRAM image size */
573 io_size = le32toh(hdr.size);
574 if (io_size < sizeof(hdr)) {
575 /* The header size must include the header itself */
576 BHND_NV_LOG("corrupt header size: %zu\n", io_size);
577 return (EINVAL);
578 }
579
580 if (io_size > bhnd_nvram_io_getsize(src)) {
581 BHND_NV_LOG("header size %zu exceeds input size %zu\n",
582 io_size, bhnd_nvram_io_getsize(src));
583 return (EINVAL);
584 }
585
586 /* Allocate a buffer large enough to hold the NVRAM image, and
587 * an extra EOF-signaling NUL (on the chance it's missing from the
588 * source data) */
589 if (io_size == SIZE_MAX)
590 return (ENOMEM);
591
592 bcm->data = bhnd_nvram_iobuf_empty(io_size, io_size + 1);
593 if (bcm->data == NULL)
594 return (ENOMEM);
595
596 /* Fetch a pointer into our backing buffer and copy in the
597 * NVRAM image. */
598 error = bhnd_nvram_io_write_ptr(bcm->data, 0x0, &ptr, io_size, NULL);
599 if (error)
600 return (error);
601
602 p = ptr;
603 if ((error = bhnd_nvram_io_read(src, 0x0, p, io_size)))
604 return (error);
605
606 /* Verify the CRC */
607 valid = BCM_NVRAM_GET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_CRC);
608 crc = bhnd_nvram_crc8(p + BCM_NVRAM_CRC_SKIP,
609 io_size - BCM_NVRAM_CRC_SKIP, BHND_NVRAM_CRC8_INITIAL);
610
611 if (crc != valid) {
612 BHND_NV_LOG("warning: NVRAM CRC error (crc=%#hhx, "
613 "expected=%hhx)\n", crc, valid);
614 }
615
616 /* Populate header variable definitions */
617 #define BCM_READ_HDR_VAR(_name, _dest, _swap) do { \
618 struct bhnd_nvram_bcm_hvar *data; \
619 data = bhnd_nvram_bcm_gethdrvar(bcm, _name ##_VAR); \
620 BHND_NV_ASSERT(data != NULL, \
621 ("no such header variable: " __STRING(_name))); \
622 \
623 \
624 data->value. _dest = _swap(BCM_NVRAM_GET_BITS( \
625 hdr. _name ## _FIELD, _name)); \
626 } while(0)
627
628 BCM_READ_HDR_VAR(BCM_NVRAM_CFG0_SDRAM_INIT, u16, le16toh);
629 BCM_READ_HDR_VAR(BCM_NVRAM_CFG1_SDRAM_CFG, u16, le16toh);
630 BCM_READ_HDR_VAR(BCM_NVRAM_CFG1_SDRAM_REFRESH, u16, le16toh);
631 BCM_READ_HDR_VAR(BCM_NVRAM_SDRAM_NCDL, u32, le32toh);
632
633 _Static_assert(nitems(bcm->hvars) == 4, "missing initialization for"
634 "NVRAM header variable(s)");
635
636 #undef BCM_READ_HDR_VAR
637
638 /* Process the buffer */
639 bcm->count = 0;
640 io_offset = sizeof(hdr);
641 while (io_offset < io_size) {
642 char *envp;
643 const char *name, *value;
644 size_t envp_len;
645 size_t name_len, value_len;
646
647 /* Parse the key=value string */
648 envp = (char *) (p + io_offset);
649 envp_len = strnlen(envp, io_size - io_offset);
650 error = bhnd_nvram_parse_env(envp, envp_len, '=', &name,
651 &name_len, &value, &value_len);
652 if (error) {
653 BHND_NV_LOG("error parsing envp at offset %#zx: %d\n",
654 io_offset, error);
655 return (error);
656 }
657
658 /* Insert a '\0' character, replacing the '=' delimiter and
659 * allowing us to vend references directly to the variable
660 * name */
661 *(envp + name_len) = '\0';
662
663 /* Record any NVRAM variables that mirror our header variables.
664 * This is a brute-force search -- for the amount of data we're
665 * operating on, it shouldn't be an issue. */
666 for (size_t i = 0; i < nitems(bcm->hvars); i++) {
667 struct bhnd_nvram_bcm_hvar *hvar;
668 union bhnd_nvram_bcm_hvar_value hval;
669 size_t hval_len;
670
671 hvar = &bcm->hvars[i];
672
673 /* Already matched? */
674 if (hvar->envp != NULL)
675 continue;
676
677 /* Name matches? */
678 if ((strcmp(name, hvar->name)) != 0)
679 continue;
680
681 /* Save pointer to mirrored envp */
682 hvar->envp = envp;
683
684 /* Check for stale value */
685 hval_len = sizeof(hval);
686 error = bhnd_nvram_value_coerce(value, value_len,
687 BHND_NVRAM_TYPE_STRING, &hval, &hval_len,
688 hvar->type);
689 if (error) {
690 /* If parsing fails, we can likely only make
691 * things worse by trying to synchronize the
692 * variables */
693 BHND_NV_LOG("error parsing header variable "
694 "'%s=%s': %d\n", name, value, error);
695 } else if (hval_len != hvar->len) {
696 hvar->stale = true;
697 } else if (memcmp(&hval, &hvar->value, hval_len) != 0) {
698 hvar->stale = true;
699 }
700 }
701
702 /* Seek past the value's terminating '\0' */
703 io_offset += envp_len;
704 if (io_offset == io_size) {
705 BHND_NV_LOG("missing terminating NUL at offset %#zx\n",
706 io_offset);
707 return (EINVAL);
708 }
709
710 if (*(p + io_offset) != '\0') {
711 BHND_NV_LOG("invalid terminator '%#hhx' at offset "
712 "%#zx\n", *(p + io_offset), io_offset);
713 return (EINVAL);
714 }
715
716 /* Update variable count */
717 bcm->count++;
718
719 /* Seek to the next record */
720 if (++io_offset == io_size) {
721 char ch;
722
723 /* Hit EOF without finding a terminating NUL
724 * byte; we need to grow our buffer and append
725 * it */
726 io_size++;
727 if ((error = bhnd_nvram_io_setsize(bcm->data, io_size)))
728 return (error);
729
730 /* Write NUL byte */
731 ch = '\0';
732 error = bhnd_nvram_io_write(bcm->data, io_size-1, &ch,
733 sizeof(ch));
734 if (error)
735 return (error);
736 }
737
738 /* Check for explicit EOF (encoded as a single empty NUL
739 * terminated string) */
740 if (*(p + io_offset) == '\0')
741 break;
742 }
743
744 /* Add non-mirrored header variables to total count variable */
745 for (size_t i = 0; i < nitems(bcm->hvars); i++) {
746 if (bcm->hvars[i].envp == NULL)
747 bcm->count++;
748 }
749
750 /* Populate serialization options from our header */
751 bcm_ver = BCM_NVRAM_GET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_VER);
752 error = bhnd_nvram_plist_append_bytes(bcm->opts,
753 BCM_NVRAM_ENCODE_OPT_VERSION, &bcm_ver, sizeof(bcm_ver),
754 BHND_NVRAM_TYPE_UINT8);
755 if (error)
756 return (error);
757
758 return (0);
759 }
760
761 static int
bhnd_nvram_bcm_new(struct bhnd_nvram_data * nv,struct bhnd_nvram_io * io)762 bhnd_nvram_bcm_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io)
763 {
764 struct bhnd_nvram_bcm *bcm;
765 int error;
766
767 bcm = (struct bhnd_nvram_bcm *)nv;
768
769 /* Populate default BCM mirrored header variable set */
770 _Static_assert(sizeof(bcm->hvars) == sizeof(bhnd_nvram_bcm_hvars),
771 "hvar declarations must match bhnd_nvram_bcm_hvars template");
772 memcpy(bcm->hvars, bhnd_nvram_bcm_hvars, sizeof(bcm->hvars));
773
774 /* Allocate (empty) option list, to be populated by
775 * bhnd_nvram_bcm_init() */
776 bcm->opts = bhnd_nvram_plist_new();
777 if (bcm->opts == NULL)
778 return (ENOMEM);
779
780 /* Parse the BCM input data and initialize our backing
781 * data representation */
782 if ((error = bhnd_nvram_bcm_init(bcm, io))) {
783 bhnd_nvram_bcm_free(nv);
784 return (error);
785 }
786
787 return (0);
788 }
789
790 static void
bhnd_nvram_bcm_free(struct bhnd_nvram_data * nv)791 bhnd_nvram_bcm_free(struct bhnd_nvram_data *nv)
792 {
793 struct bhnd_nvram_bcm *bcm = (struct bhnd_nvram_bcm *)nv;
794
795 if (bcm->data != NULL)
796 bhnd_nvram_io_free(bcm->data);
797
798 if (bcm->opts != NULL)
799 bhnd_nvram_plist_release(bcm->opts);
800 }
801
802 size_t
bhnd_nvram_bcm_count(struct bhnd_nvram_data * nv)803 bhnd_nvram_bcm_count(struct bhnd_nvram_data *nv)
804 {
805 struct bhnd_nvram_bcm *bcm = (struct bhnd_nvram_bcm *)nv;
806 return (bcm->count);
807 }
808
809 static bhnd_nvram_plist *
bhnd_nvram_bcm_options(struct bhnd_nvram_data * nv)810 bhnd_nvram_bcm_options(struct bhnd_nvram_data *nv)
811 {
812 struct bhnd_nvram_bcm *bcm = (struct bhnd_nvram_bcm *)nv;
813 return (bcm->opts);
814 }
815
816 static uint32_t
bhnd_nvram_bcm_caps(struct bhnd_nvram_data * nv)817 bhnd_nvram_bcm_caps(struct bhnd_nvram_data *nv)
818 {
819 return (BHND_NVRAM_DATA_CAP_READ_PTR|BHND_NVRAM_DATA_CAP_DEVPATHS);
820 }
821
822 static const char *
bhnd_nvram_bcm_next(struct bhnd_nvram_data * nv,void ** cookiep)823 bhnd_nvram_bcm_next(struct bhnd_nvram_data *nv, void **cookiep)
824 {
825 struct bhnd_nvram_bcm *bcm;
826 struct bhnd_nvram_bcm_hvar *hvar, *hvar_next;
827 const void *ptr;
828 const char *envp, *basep;
829 size_t io_size, io_offset;
830 int error;
831
832 bcm = (struct bhnd_nvram_bcm *)nv;
833
834 io_offset = sizeof(struct bhnd_nvram_bcmhdr);
835 io_size = bhnd_nvram_io_getsize(bcm->data) - io_offset;
836
837 /* Map backing buffer */
838 error = bhnd_nvram_io_read_ptr(bcm->data, io_offset, &ptr, io_size,
839 NULL);
840 if (error) {
841 BHND_NV_LOG("error mapping backing buffer: %d\n", error);
842 return (NULL);
843 }
844
845 basep = ptr;
846
847 /* If cookiep pointers into our header variable array, handle as header
848 * variable iteration. */
849 hvar = bhnd_nvram_bcm_to_hdrvar(bcm, *cookiep);
850 if (hvar != NULL) {
851 size_t idx;
852
853 /* Advance to next entry, if any */
854 idx = bhnd_nvram_bcm_hdrvar_index(bcm, hvar) + 1;
855
856 /* Find the next header-defined variable that isn't defined in
857 * the NVRAM data, start iteration there */
858 for (size_t i = idx; i < nitems(bcm->hvars); i++) {
859 hvar_next = &bcm->hvars[i];
860 if (hvar_next->envp != NULL && !hvar_next->stale)
861 continue;
862
863 *cookiep = hvar_next;
864 return (hvar_next->name);
865 }
866
867 /* No further header-defined variables; iteration
868 * complete */
869 return (NULL);
870 }
871
872 /* Handle standard NVRAM data iteration */
873 if (*cookiep == NULL) {
874 /* Start at the first NVRAM data record */
875 envp = basep;
876 } else {
877 /* Seek to next record */
878 envp = *cookiep;
879 envp += strlen(envp) + 1; /* key + '\0' */
880 envp += strlen(envp) + 1; /* value + '\0' */
881 }
882
883 /*
884 * Skip entries that have an existing header variable entry that takes
885 * precedence over the NVRAM data value.
886 *
887 * The header's value will be provided when performing header variable
888 * iteration
889 */
890 while ((size_t)(envp - basep) < io_size && *envp != '\0') {
891 /* Locate corresponding header variable */
892 hvar = NULL;
893 for (size_t i = 0; i < nitems(bcm->hvars); i++) {
894 if (bcm->hvars[i].envp != envp)
895 continue;
896
897 hvar = &bcm->hvars[i];
898 break;
899 }
900
901 /* If no corresponding hvar entry, or the entry does not take
902 * precedence over this NVRAM value, we can safely return this
903 * value as-is. */
904 if (hvar == NULL || !hvar->stale)
905 break;
906
907 /* Seek to next record */
908 envp += strlen(envp) + 1; /* key + '\0' */
909 envp += strlen(envp) + 1; /* value + '\0' */
910 }
911
912 /* On NVRAM data EOF, try switching to header variables */
913 if ((size_t)(envp - basep) == io_size || *envp == '\0') {
914 /* Find first valid header variable */
915 for (size_t i = 0; i < nitems(bcm->hvars); i++) {
916 if (bcm->hvars[i].envp != NULL)
917 continue;
918
919 *cookiep = &bcm->hvars[i];
920 return (bcm->hvars[i].name);
921 }
922
923 /* No header variables */
924 return (NULL);
925 }
926
927 *cookiep = __DECONST(void *, envp);
928 return (envp);
929 }
930
931 static void *
bhnd_nvram_bcm_find(struct bhnd_nvram_data * nv,const char * name)932 bhnd_nvram_bcm_find(struct bhnd_nvram_data *nv, const char *name)
933 {
934 return (bhnd_nvram_data_generic_find(nv, name));
935 }
936
937 static int
bhnd_nvram_bcm_getvar_order(struct bhnd_nvram_data * nv,void * cookiep1,void * cookiep2)938 bhnd_nvram_bcm_getvar_order(struct bhnd_nvram_data *nv, void *cookiep1,
939 void *cookiep2)
940 {
941 struct bhnd_nvram_bcm *bcm;
942 struct bhnd_nvram_bcm_hvar *hvar1, *hvar2;
943
944 bcm = (struct bhnd_nvram_bcm *)nv;
945
946 hvar1 = bhnd_nvram_bcm_to_hdrvar(bcm, cookiep1);
947 hvar2 = bhnd_nvram_bcm_to_hdrvar(bcm, cookiep2);
948
949 /* Header variables are always ordered below any variables defined
950 * in the BCM data */
951 if (hvar1 != NULL && hvar2 == NULL) {
952 return (1); /* hvar follows non-hvar */
953 } else if (hvar1 == NULL && hvar2 != NULL) {
954 return (-1); /* non-hvar precedes hvar */
955 }
956
957 /* Otherwise, both cookies are either hvars or non-hvars. We can
958 * safely fall back on pointer order, which will provide a correct
959 * ordering matching the behavior of bhnd_nvram_data_next() for
960 * both cases */
961 if (cookiep1 < cookiep2)
962 return (-1);
963
964 if (cookiep1 > cookiep2)
965 return (1);
966
967 return (0);
968 }
969
970 static int
bhnd_nvram_bcm_getvar(struct bhnd_nvram_data * nv,void * cookiep,void * buf,size_t * len,bhnd_nvram_type type)971 bhnd_nvram_bcm_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
972 size_t *len, bhnd_nvram_type type)
973 {
974 return (bhnd_nvram_data_generic_rp_getvar(nv, cookiep, buf, len, type));
975 }
976
977 static int
bhnd_nvram_bcm_copy_val(struct bhnd_nvram_data * nv,void * cookiep,bhnd_nvram_val ** value)978 bhnd_nvram_bcm_copy_val(struct bhnd_nvram_data *nv, void *cookiep,
979 bhnd_nvram_val **value)
980 {
981 return (bhnd_nvram_data_generic_rp_copy_val(nv, cookiep, value));
982 }
983
984 static const void *
bhnd_nvram_bcm_getvar_ptr(struct bhnd_nvram_data * nv,void * cookiep,size_t * len,bhnd_nvram_type * type)985 bhnd_nvram_bcm_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep,
986 size_t *len, bhnd_nvram_type *type)
987 {
988 struct bhnd_nvram_bcm *bcm;
989 struct bhnd_nvram_bcm_hvar *hvar;
990 const char *envp;
991
992 bcm = (struct bhnd_nvram_bcm *)nv;
993
994 /* Handle header variables */
995 if ((hvar = bhnd_nvram_bcm_to_hdrvar(bcm, cookiep)) != NULL) {
996 BHND_NV_ASSERT(bhnd_nvram_value_check_aligned(&hvar->value,
997 hvar->len, hvar->type) == 0, ("value misaligned"));
998
999 *type = hvar->type;
1000 *len = hvar->len;
1001 return (&hvar->value);
1002 }
1003
1004 /* Cookie points to key\0value\0 -- get the value address */
1005 BHND_NV_ASSERT(cookiep != NULL, ("NULL cookiep"));
1006
1007 envp = cookiep;
1008 envp += strlen(envp) + 1; /* key + '\0' */
1009 *len = strlen(envp) + 1; /* value + '\0' */
1010 *type = BHND_NVRAM_TYPE_STRING;
1011
1012 return (envp);
1013 }
1014
1015 static const char *
bhnd_nvram_bcm_getvar_name(struct bhnd_nvram_data * nv,void * cookiep)1016 bhnd_nvram_bcm_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)
1017 {
1018 struct bhnd_nvram_bcm *bcm;
1019 struct bhnd_nvram_bcm_hvar *hvar;
1020
1021 bcm = (struct bhnd_nvram_bcm *)nv;
1022
1023 /* Handle header variables */
1024 if ((hvar = bhnd_nvram_bcm_to_hdrvar(bcm, cookiep)) != NULL) {
1025 return (hvar->name);
1026 }
1027
1028 /* Cookie points to key\0value\0 */
1029 return (cookiep);
1030 }
1031
1032 static int
bhnd_nvram_bcm_filter_setvar(struct bhnd_nvram_data * nv,const char * name,bhnd_nvram_val * value,bhnd_nvram_val ** result)1033 bhnd_nvram_bcm_filter_setvar(struct bhnd_nvram_data *nv, const char *name,
1034 bhnd_nvram_val *value, bhnd_nvram_val **result)
1035 {
1036 bhnd_nvram_val *str;
1037 int error;
1038
1039 /* Name (trimmed of any path prefix) must be valid */
1040 if (!bhnd_nvram_validate_name(bhnd_nvram_trim_path_name(name)))
1041 return (EINVAL);
1042
1043 /* Value must be bcm-formatted string */
1044 error = bhnd_nvram_val_convert_new(&str, &bhnd_nvram_val_bcm_string_fmt,
1045 value, BHND_NVRAM_VAL_DYNAMIC);
1046 if (error)
1047 return (error);
1048
1049 /* Success. Transfer result ownership to the caller. */
1050 *result = str;
1051 return (0);
1052 }
1053
1054 static int
bhnd_nvram_bcm_filter_unsetvar(struct bhnd_nvram_data * nv,const char * name)1055 bhnd_nvram_bcm_filter_unsetvar(struct bhnd_nvram_data *nv, const char *name)
1056 {
1057 /* We permit deletion of any variable */
1058 return (0);
1059 }
1060
1061 /**
1062 * Return the internal BCM data reference for a header-defined variable
1063 * with @p name, or NULL if none exists.
1064 */
1065 static struct bhnd_nvram_bcm_hvar *
bhnd_nvram_bcm_gethdrvar(struct bhnd_nvram_bcm * bcm,const char * name)1066 bhnd_nvram_bcm_gethdrvar(struct bhnd_nvram_bcm *bcm, const char *name)
1067 {
1068 for (size_t i = 0; i < nitems(bcm->hvars); i++) {
1069 if (strcmp(bcm->hvars[i].name, name) == 0)
1070 return (&bcm->hvars[i]);
1071 }
1072
1073 /* Not found */
1074 return (NULL);
1075 }
1076
1077 /**
1078 * If @p cookiep references a header-defined variable, return the
1079 * internal BCM data reference. Otherwise, returns NULL.
1080 */
1081 static struct bhnd_nvram_bcm_hvar *
bhnd_nvram_bcm_to_hdrvar(struct bhnd_nvram_bcm * bcm,void * cookiep)1082 bhnd_nvram_bcm_to_hdrvar(struct bhnd_nvram_bcm *bcm, void *cookiep)
1083 {
1084 #ifdef BHND_NVRAM_INVARIANTS
1085 uintptr_t base, ptr;
1086 #endif
1087
1088 /* If the cookie falls within the hvar array, it's a
1089 * header variable cookie */
1090 if (nitems(bcm->hvars) == 0)
1091 return (NULL);
1092
1093 if (cookiep < (void *)&bcm->hvars[0])
1094 return (NULL);
1095
1096 if (cookiep > (void *)&bcm->hvars[nitems(bcm->hvars)-1])
1097 return (NULL);
1098
1099 #ifdef BHND_NVRAM_INVARIANTS
1100 base = (uintptr_t)bcm->hvars;
1101 ptr = (uintptr_t)cookiep;
1102
1103 BHND_NV_ASSERT((ptr - base) % sizeof(bcm->hvars[0]) == 0,
1104 ("misaligned hvar pointer %p/%p", cookiep, bcm->hvars));
1105 #endif /* INVARIANTS */
1106
1107 return ((struct bhnd_nvram_bcm_hvar *)cookiep);
1108 }
1109
1110 /**
1111 * Return the index of @p hdrvar within @p bcm's backing hvars array.
1112 */
1113 static size_t
bhnd_nvram_bcm_hdrvar_index(struct bhnd_nvram_bcm * bcm,struct bhnd_nvram_bcm_hvar * hdrvar)1114 bhnd_nvram_bcm_hdrvar_index(struct bhnd_nvram_bcm *bcm,
1115 struct bhnd_nvram_bcm_hvar *hdrvar)
1116 {
1117 BHND_NV_ASSERT(bhnd_nvram_bcm_to_hdrvar(bcm, (void *)hdrvar) != NULL,
1118 ("%p is not a valid hdrvar reference", hdrvar));
1119
1120 return (hdrvar - &bcm->hvars[0]);
1121 }
1122