Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/dev/bhnd/nvram/bhnd_nvram_data_bcm.c
39536 views
1
/*-
2
* Copyright (c) 2016 Landon Fuller <[email protected]>
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
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
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
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
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
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
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
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
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 *
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
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 *
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 *
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
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
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
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 *
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 *
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
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
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 *
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 *
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
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
1123