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_btxt.c
39536 views
1
/*-
2
* Copyright (c) 2015-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/cdefs.h>
31
#include <sys/endian.h>
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 <stdlib.h>
45
#include <string.h>
46
47
#endif /* _KERNEL */
48
49
#include "bhnd_nvram_private.h"
50
51
#include "bhnd_nvram_datavar.h"
52
53
#include "bhnd_nvram_data_bcmreg.h" /* for BCM_NVRAM_MAGIC */
54
55
/**
56
* Broadcom "Board Text" data class.
57
*
58
* This format is used to provide external NVRAM data for some
59
* fullmac WiFi devices, and as an input format when programming
60
* NVRAM/SPROM/OTP.
61
*/
62
63
struct bhnd_nvram_btxt {
64
struct bhnd_nvram_data nv; /**< common instance state */
65
struct bhnd_nvram_io *data; /**< memory-backed board text data */
66
size_t count; /**< variable count */
67
};
68
69
BHND_NVRAM_DATA_CLASS_DEFN(btxt, "Broadcom Board Text",
70
BHND_NVRAM_DATA_CAP_DEVPATHS, sizeof(struct bhnd_nvram_btxt))
71
72
/** Minimal identification header */
73
union bhnd_nvram_btxt_ident {
74
uint32_t bcm_magic;
75
char btxt[8];
76
};
77
78
static void *bhnd_nvram_btxt_offset_to_cookiep(struct bhnd_nvram_btxt *btxt,
79
size_t io_offset);
80
static size_t bhnd_nvram_btxt_cookiep_to_offset(struct bhnd_nvram_btxt *btxt,
81
void *cookiep);
82
83
static int bhnd_nvram_btxt_entry_len(struct bhnd_nvram_io *io,
84
size_t offset, size_t *line_len, size_t *env_len);
85
static int bhnd_nvram_btxt_seek_next(struct bhnd_nvram_io *io,
86
size_t *offset);
87
static int bhnd_nvram_btxt_seek_eol(struct bhnd_nvram_io *io,
88
size_t *offset);
89
90
static int
91
bhnd_nvram_btxt_probe(struct bhnd_nvram_io *io)
92
{
93
union bhnd_nvram_btxt_ident ident;
94
char c;
95
int error;
96
97
/* Look at the initial header for something that looks like
98
* an ASCII board text file */
99
if ((error = bhnd_nvram_io_read(io, 0x0, &ident, sizeof(ident))))
100
return (error);
101
102
/* The BCM NVRAM format uses a 'FLSH' little endian magic value, which
103
* shouldn't be interpreted as BTXT */
104
if (le32toh(ident.bcm_magic) == BCM_NVRAM_MAGIC)
105
return (ENXIO);
106
107
/* Don't match on non-ASCII/non-printable data */
108
for (size_t i = 0; i < nitems(ident.btxt); i++) {
109
c = ident.btxt[i];
110
if (!bhnd_nv_isprint(c))
111
return (ENXIO);
112
}
113
114
/* The first character should either be a valid key char (alpha),
115
* whitespace, or the start of a comment ('#') */
116
c = ident.btxt[0];
117
if (!bhnd_nv_isspace(c) && !bhnd_nv_isalpha(c) && c != '#')
118
return (ENXIO);
119
120
/* We assert a low priority, given that we've only scanned an
121
* initial few bytes of the file. */
122
return (BHND_NVRAM_DATA_PROBE_MAYBE);
123
}
124
125
/**
126
* Parser states for bhnd_nvram_bcm_getvar_direct_common().
127
*/
128
typedef enum {
129
BTXT_PARSE_LINE_START,
130
BTXT_PARSE_KEY,
131
BTXT_PARSE_KEY_END,
132
BTXT_PARSE_NEXT_LINE,
133
BTXT_PARSE_VALUE_START,
134
BTXT_PARSE_VALUE
135
} btxt_parse_state;
136
137
static int
138
bhnd_nvram_btxt_getvar_direct(struct bhnd_nvram_io *io, const char *name,
139
void *outp, size_t *olen, bhnd_nvram_type otype)
140
{
141
char buf[512];
142
btxt_parse_state pstate;
143
size_t limit, offset;
144
size_t buflen, bufpos;
145
size_t namelen, namepos;
146
size_t vlen;
147
int error;
148
149
limit = bhnd_nvram_io_getsize(io);
150
offset = 0;
151
152
/* Loop our parser until we find the requested variable, or hit EOF */
153
pstate = BTXT_PARSE_LINE_START;
154
buflen = 0;
155
bufpos = 0;
156
namelen = strlen(name);
157
namepos = 0;
158
vlen = 0;
159
160
while ((offset - bufpos) < limit) {
161
BHND_NV_ASSERT(bufpos <= buflen,
162
("buf position invalid (%zu > %zu)", bufpos, buflen));
163
BHND_NV_ASSERT(buflen <= sizeof(buf),
164
("buf length invalid (%zu > %zu", buflen, sizeof(buf)));
165
166
/* Repopulate our parse buffer? */
167
if (buflen - bufpos == 0) {
168
BHND_NV_ASSERT(offset < limit, ("offset overrun"));
169
170
buflen = bhnd_nv_ummin(sizeof(buf), limit - offset);
171
bufpos = 0;
172
173
error = bhnd_nvram_io_read(io, offset, buf, buflen);
174
if (error)
175
return (error);
176
177
offset += buflen;
178
}
179
180
switch (pstate) {
181
case BTXT_PARSE_LINE_START:
182
BHND_NV_ASSERT(bufpos < buflen, ("empty buffer!"));
183
184
/* Reset name matching position */
185
namepos = 0;
186
187
/* Trim any leading whitespace */
188
while (bufpos < buflen && bhnd_nv_isspace(buf[bufpos]))
189
{
190
bufpos++;
191
}
192
193
if (bufpos == buflen) {
194
/* Continue parsing the line */
195
pstate = BTXT_PARSE_LINE_START;
196
} else if (bufpos < buflen && buf[bufpos] == '#') {
197
/* Comment; skip to next line */
198
pstate = BTXT_PARSE_NEXT_LINE;
199
} else {
200
/* Start name matching */
201
pstate = BTXT_PARSE_KEY;
202
}
203
204
break;
205
206
case BTXT_PARSE_KEY: {
207
size_t navail, nleft;
208
209
nleft = namelen - namepos;
210
navail = bhnd_nv_ummin(buflen - bufpos, nleft);
211
212
if (strncmp(name+namepos, buf+bufpos, navail) == 0) {
213
/* Matched */
214
namepos += navail;
215
bufpos += navail;
216
217
if (namepos == namelen) {
218
/* Matched the full variable; look for
219
* its trailing delimiter */
220
pstate = BTXT_PARSE_KEY_END;
221
} else {
222
/* Continue matching the name */
223
pstate = BTXT_PARSE_KEY;
224
}
225
} else {
226
/* No match; advance to next entry and restart
227
* name matching */
228
pstate = BTXT_PARSE_NEXT_LINE;
229
}
230
231
break;
232
}
233
234
case BTXT_PARSE_KEY_END:
235
BHND_NV_ASSERT(bufpos < buflen, ("empty buffer!"));
236
237
if (buf[bufpos] == '=') {
238
/* Key fully matched; advance past '=' and
239
* parse the value */
240
bufpos++;
241
pstate = BTXT_PARSE_VALUE_START;
242
} else {
243
/* No match; advance to next line and restart
244
* name matching */
245
pstate = BTXT_PARSE_NEXT_LINE;
246
}
247
248
break;
249
250
case BTXT_PARSE_NEXT_LINE: {
251
const char *p;
252
253
/* Scan for a '\r', '\n', or '\r\n' terminator */
254
p = memchr(buf+bufpos, '\n', buflen - bufpos);
255
if (p == NULL)
256
p = memchr(buf+bufpos, '\r', buflen - bufpos);
257
258
if (p != NULL) {
259
/* Found entry terminator; restart name
260
* matching at next line */
261
pstate = BTXT_PARSE_LINE_START;
262
bufpos = (p - buf);
263
} else {
264
/* Consumed full buffer looking for newline;
265
* force repopulation of the buffer and
266
* retry */
267
pstate = BTXT_PARSE_NEXT_LINE;
268
bufpos = buflen;
269
}
270
271
break;
272
}
273
274
case BTXT_PARSE_VALUE_START: {
275
const char *p;
276
277
/* Scan for a terminating newline */
278
p = memchr(buf+bufpos, '\n', buflen - bufpos);
279
if (p == NULL)
280
p = memchr(buf+bufpos, '\r', buflen - bufpos);
281
282
if (p != NULL) {
283
/* Found entry terminator; parse the value */
284
vlen = p - &buf[bufpos];
285
pstate = BTXT_PARSE_VALUE;
286
287
} else if (p == NULL && offset == limit) {
288
/* Hit EOF without a terminating newline;
289
* treat the entry as implicitly terminated */
290
vlen = buflen - bufpos;
291
pstate = BTXT_PARSE_VALUE;
292
293
} else if (p == NULL && bufpos > 0) {
294
size_t nread;
295
296
/* Move existing value data to start of
297
* buffer */
298
memmove(buf, buf+bufpos, buflen - bufpos);
299
buflen = bufpos;
300
bufpos = 0;
301
302
/* Populate full buffer to allow retry of
303
* value parsing */
304
nread = bhnd_nv_ummin(sizeof(buf) - buflen,
305
limit - offset);
306
307
error = bhnd_nvram_io_read(io, offset,
308
buf+buflen, nread);
309
if (error)
310
return (error);
311
312
offset += nread;
313
buflen += nread;
314
} else {
315
/* Value exceeds our buffer capacity */
316
BHND_NV_LOG("cannot parse value for '%s' "
317
"(exceeds %zu byte limit)\n", name,
318
sizeof(buf));
319
320
return (ENXIO);
321
}
322
323
break;
324
}
325
326
case BTXT_PARSE_VALUE:
327
BHND_NV_ASSERT(vlen <= buflen, ("value buf overrun"));
328
329
/* Trim any trailing whitespace */
330
while (vlen > 0 && bhnd_nv_isspace(buf[bufpos+vlen-1]))
331
vlen--;
332
333
/* Write the value to the caller's buffer */
334
return (bhnd_nvram_value_coerce(buf+bufpos, vlen,
335
BHND_NVRAM_TYPE_STRING, outp, olen, otype));
336
}
337
}
338
339
/* Variable not found */
340
return (ENOENT);
341
}
342
343
static int
344
bhnd_nvram_btxt_serialize(bhnd_nvram_data_class *cls, bhnd_nvram_plist *props,
345
bhnd_nvram_plist *options, void *outp, size_t *olen)
346
{
347
bhnd_nvram_prop *prop;
348
size_t limit, nbytes;
349
int error;
350
351
/* Determine output byte limit */
352
if (outp != NULL)
353
limit = *olen;
354
else
355
limit = 0;
356
357
nbytes = 0;
358
359
/* Write all properties */
360
prop = NULL;
361
while ((prop = bhnd_nvram_plist_next(props, prop)) != NULL) {
362
const char *name;
363
char *p;
364
size_t prop_limit;
365
size_t name_len, value_len;
366
367
if (outp == NULL || limit < nbytes) {
368
p = NULL;
369
prop_limit = 0;
370
} else {
371
p = ((char *)outp) + nbytes;
372
prop_limit = limit - nbytes;
373
}
374
375
/* Fetch and write 'name=' to output */
376
name = bhnd_nvram_prop_name(prop);
377
name_len = strlen(name) + 1;
378
379
if (prop_limit > name_len) {
380
memcpy(p, name, name_len - 1);
381
p[name_len - 1] = '=';
382
383
prop_limit -= name_len;
384
p += name_len;
385
} else {
386
prop_limit = 0;
387
p = NULL;
388
}
389
390
/* Advance byte count */
391
if (SIZE_MAX - nbytes < name_len)
392
return (EFTYPE); /* would overflow size_t */
393
394
nbytes += name_len;
395
396
/* Write NUL-terminated value to output, rewrite NUL as
397
* '\n' record delimiter */
398
value_len = prop_limit;
399
error = bhnd_nvram_prop_encode(prop, p, &value_len,
400
BHND_NVRAM_TYPE_STRING);
401
if (p != NULL && error == 0) {
402
/* Replace trailing '\0' with newline */
403
BHND_NV_ASSERT(value_len > 0, ("string length missing "
404
"minimum required trailing NUL"));
405
406
*(p + (value_len - 1)) = '\n';
407
} else if (error && error != ENOMEM) {
408
/* If encoding failed for any reason other than ENOMEM
409
* (which we'll detect and report after encoding all
410
* properties), return immediately */
411
BHND_NV_LOG("error serializing %s to required type "
412
"%s: %d\n", name,
413
bhnd_nvram_type_name(BHND_NVRAM_TYPE_STRING),
414
error);
415
return (error);
416
}
417
418
/* Advance byte count */
419
if (SIZE_MAX - nbytes < value_len)
420
return (EFTYPE); /* would overflow size_t */
421
422
nbytes += value_len;
423
}
424
425
/* Provide required length */
426
*olen = nbytes;
427
if (limit < *olen) {
428
if (outp == NULL)
429
return (0);
430
431
return (ENOMEM);
432
}
433
434
return (0);
435
}
436
437
/**
438
* Initialize @p btxt with the provided board text data mapped by @p src.
439
*
440
* @param btxt A newly allocated data instance.
441
*/
442
static int
443
bhnd_nvram_btxt_init(struct bhnd_nvram_btxt *btxt, struct bhnd_nvram_io *src)
444
{
445
const void *ptr;
446
const char *name, *value;
447
size_t name_len, value_len;
448
size_t line_len, env_len;
449
size_t io_offset, io_size, str_size;
450
int error;
451
452
BHND_NV_ASSERT(btxt->data == NULL, ("btxt data already allocated"));
453
454
if ((btxt->data = bhnd_nvram_iobuf_copy(src)) == NULL)
455
return (ENOMEM);
456
457
io_size = bhnd_nvram_io_getsize(btxt->data);
458
io_offset = 0;
459
460
/* Fetch a pointer mapping the entirity of the board text data */
461
error = bhnd_nvram_io_read_ptr(btxt->data, 0x0, &ptr, io_size, NULL);
462
if (error)
463
return (error);
464
465
/* Determine the actual size, minus any terminating NUL. We
466
* parse NUL-terminated C strings, but do not include NUL termination
467
* in our internal or serialized representations */
468
str_size = strnlen(ptr, io_size);
469
470
/* If the terminating NUL is not found at the end of the buffer,
471
* this is BCM-RAW or other NUL-delimited NVRAM format. */
472
if (str_size < io_size && str_size + 1 < io_size)
473
return (EINVAL);
474
475
/* Adjust buffer size to account for NUL termination (if any) */
476
io_size = str_size;
477
if ((error = bhnd_nvram_io_setsize(btxt->data, io_size)))
478
return (error);
479
480
/* Process the buffer */
481
btxt->count = 0;
482
while (io_offset < io_size) {
483
const void *envp;
484
485
/* Seek to the next key=value entry */
486
if ((error = bhnd_nvram_btxt_seek_next(btxt->data, &io_offset)))
487
return (error);
488
489
/* Determine the entry and line length */
490
error = bhnd_nvram_btxt_entry_len(btxt->data, io_offset,
491
&line_len, &env_len);
492
if (error)
493
return (error);
494
495
/* EOF? */
496
if (env_len == 0) {
497
BHND_NV_ASSERT(io_offset == io_size,
498
("zero-length record returned from "
499
"bhnd_nvram_btxt_seek_next()"));
500
break;
501
}
502
503
/* Fetch a pointer to the line start */
504
error = bhnd_nvram_io_read_ptr(btxt->data, io_offset, &envp,
505
env_len, NULL);
506
if (error)
507
return (error);
508
509
/* Parse the key=value string */
510
error = bhnd_nvram_parse_env(envp, env_len, '=', &name,
511
&name_len, &value, &value_len);
512
if (error) {
513
return (error);
514
}
515
516
/* Insert a '\0' character, replacing the '=' delimiter and
517
* allowing us to vend references directly to the variable
518
* name */
519
error = bhnd_nvram_io_write(btxt->data, io_offset+name_len,
520
&(char){'\0'}, 1);
521
if (error)
522
return (error);
523
524
/* Add to variable count */
525
btxt->count++;
526
527
/* Advance past EOL */
528
io_offset += line_len;
529
}
530
531
return (0);
532
}
533
534
static int
535
bhnd_nvram_btxt_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io)
536
{
537
struct bhnd_nvram_btxt *btxt;
538
int error;
539
540
/* Allocate and initialize the BTXT data instance */
541
btxt = (struct bhnd_nvram_btxt *)nv;
542
543
/* Parse the BTXT input data and initialize our backing
544
* data representation */
545
if ((error = bhnd_nvram_btxt_init(btxt, io))) {
546
bhnd_nvram_btxt_free(nv);
547
return (error);
548
}
549
550
return (0);
551
}
552
553
static void
554
bhnd_nvram_btxt_free(struct bhnd_nvram_data *nv)
555
{
556
struct bhnd_nvram_btxt *btxt = (struct bhnd_nvram_btxt *)nv;
557
if (btxt->data != NULL)
558
bhnd_nvram_io_free(btxt->data);
559
}
560
561
size_t
562
bhnd_nvram_btxt_count(struct bhnd_nvram_data *nv)
563
{
564
struct bhnd_nvram_btxt *btxt = (struct bhnd_nvram_btxt *)nv;
565
return (btxt->count);
566
}
567
568
static bhnd_nvram_plist *
569
bhnd_nvram_btxt_options(struct bhnd_nvram_data *nv)
570
{
571
return (NULL);
572
}
573
574
static uint32_t
575
bhnd_nvram_btxt_caps(struct bhnd_nvram_data *nv)
576
{
577
return (BHND_NVRAM_DATA_CAP_READ_PTR|BHND_NVRAM_DATA_CAP_DEVPATHS);
578
}
579
580
static void *
581
bhnd_nvram_btxt_find(struct bhnd_nvram_data *nv, const char *name)
582
{
583
return (bhnd_nvram_data_generic_find(nv, name));
584
}
585
586
static const char *
587
bhnd_nvram_btxt_next(struct bhnd_nvram_data *nv, void **cookiep)
588
{
589
struct bhnd_nvram_btxt *btxt;
590
const void *nptr;
591
size_t io_offset, io_size;
592
int error;
593
594
btxt = (struct bhnd_nvram_btxt *)nv;
595
596
io_size = bhnd_nvram_io_getsize(btxt->data);
597
598
if (*cookiep == NULL) {
599
/* Start search at initial file offset */
600
io_offset = 0x0;
601
} else {
602
/* Start search after the current entry */
603
io_offset = bhnd_nvram_btxt_cookiep_to_offset(btxt, *cookiep);
604
605
/* Scan past the current entry by finding the next newline */
606
error = bhnd_nvram_btxt_seek_eol(btxt->data, &io_offset);
607
if (error) {
608
BHND_NV_LOG("unexpected error in seek_eol(): %d\n",
609
error);
610
return (NULL);
611
}
612
}
613
614
/* Already at EOF? */
615
if (io_offset == io_size)
616
return (NULL);
617
618
/* Seek to the first valid entry, or EOF */
619
if ((error = bhnd_nvram_btxt_seek_next(btxt->data, &io_offset))) {
620
BHND_NV_LOG("unexpected error in seek_next(): %d\n", error);
621
return (NULL);
622
}
623
624
/* Hit EOF? */
625
if (io_offset == io_size)
626
return (NULL);
627
628
/* Provide the new cookie for this offset */
629
*cookiep = bhnd_nvram_btxt_offset_to_cookiep(btxt, io_offset);
630
631
/* Fetch the name pointer; it must be at least 1 byte long */
632
error = bhnd_nvram_io_read_ptr(btxt->data, io_offset, &nptr, 1, NULL);
633
if (error) {
634
BHND_NV_LOG("unexpected error in read_ptr(): %d\n", error);
635
return (NULL);
636
}
637
638
/* Return the name pointer */
639
return (nptr);
640
}
641
642
static int
643
bhnd_nvram_btxt_getvar_order(struct bhnd_nvram_data *nv, void *cookiep1,
644
void *cookiep2)
645
{
646
if (cookiep1 < cookiep2)
647
return (-1);
648
649
if (cookiep1 > cookiep2)
650
return (1);
651
652
return (0);
653
}
654
655
static int
656
bhnd_nvram_btxt_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
657
size_t *len, bhnd_nvram_type type)
658
{
659
return (bhnd_nvram_data_generic_rp_getvar(nv, cookiep, buf, len, type));
660
}
661
662
static int
663
bhnd_nvram_btxt_copy_val(struct bhnd_nvram_data *nv, void *cookiep,
664
bhnd_nvram_val **value)
665
{
666
return (bhnd_nvram_data_generic_rp_copy_val(nv, cookiep, value));
667
}
668
669
const void *
670
bhnd_nvram_btxt_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep,
671
size_t *len, bhnd_nvram_type *type)
672
{
673
struct bhnd_nvram_btxt *btxt;
674
const void *eptr;
675
const char *vptr;
676
size_t io_offset, io_size;
677
size_t line_len, env_len;
678
int error;
679
680
btxt = (struct bhnd_nvram_btxt *)nv;
681
682
io_size = bhnd_nvram_io_getsize(btxt->data);
683
io_offset = bhnd_nvram_btxt_cookiep_to_offset(btxt, cookiep);
684
685
/* At EOF? */
686
if (io_offset == io_size)
687
return (NULL);
688
689
/* Determine the entry length */
690
error = bhnd_nvram_btxt_entry_len(btxt->data, io_offset, &line_len,
691
&env_len);
692
if (error) {
693
BHND_NV_LOG("unexpected error in entry_len(): %d\n", error);
694
return (NULL);
695
}
696
697
/* Fetch the entry's value pointer and length */
698
error = bhnd_nvram_io_read_ptr(btxt->data, io_offset, &eptr, env_len,
699
NULL);
700
if (error) {
701
BHND_NV_LOG("unexpected error in read_ptr(): %d\n", error);
702
return (NULL);
703
}
704
705
error = bhnd_nvram_parse_env(eptr, env_len, '\0', NULL, NULL, &vptr,
706
len);
707
if (error) {
708
BHND_NV_LOG("unexpected error in parse_env(): %d\n", error);
709
return (NULL);
710
}
711
712
/* Type is always CSTR */
713
*type = BHND_NVRAM_TYPE_STRING;
714
715
return (vptr);
716
}
717
718
static const char *
719
bhnd_nvram_btxt_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)
720
{
721
struct bhnd_nvram_btxt *btxt;
722
const void *ptr;
723
size_t io_offset, io_size;
724
int error;
725
726
btxt = (struct bhnd_nvram_btxt *)nv;
727
728
io_size = bhnd_nvram_io_getsize(btxt->data);
729
io_offset = bhnd_nvram_btxt_cookiep_to_offset(btxt, cookiep);
730
731
/* At EOF? */
732
if (io_offset == io_size)
733
BHND_NV_PANIC("invalid cookiep: %p", cookiep);
734
735
/* Variable name is found directly at the given offset; trailing
736
* NUL means we can assume that it's at least 1 byte long */
737
error = bhnd_nvram_io_read_ptr(btxt->data, io_offset, &ptr, 1, NULL);
738
if (error)
739
BHND_NV_PANIC("unexpected error in read_ptr(): %d\n", error);
740
741
return (ptr);
742
}
743
744
/**
745
* Return a cookiep for the given I/O offset.
746
*/
747
static void *
748
bhnd_nvram_btxt_offset_to_cookiep(struct bhnd_nvram_btxt *btxt,
749
size_t io_offset)
750
{
751
const void *ptr;
752
int error;
753
754
BHND_NV_ASSERT(io_offset < bhnd_nvram_io_getsize(btxt->data),
755
("io_offset %zu out-of-range", io_offset));
756
BHND_NV_ASSERT(io_offset < UINTPTR_MAX,
757
("io_offset %#zx exceeds UINTPTR_MAX", io_offset));
758
759
error = bhnd_nvram_io_read_ptr(btxt->data, 0x0, &ptr, io_offset, NULL);
760
if (error)
761
BHND_NV_PANIC("error mapping offset %zu: %d", io_offset, error);
762
763
ptr = (const uint8_t *)ptr + io_offset;
764
return (__DECONST(void *, ptr));
765
}
766
767
/* Convert a cookiep back to an I/O offset */
768
static size_t
769
bhnd_nvram_btxt_cookiep_to_offset(struct bhnd_nvram_btxt *btxt, void *cookiep)
770
{
771
const void *ptr;
772
intptr_t offset;
773
size_t io_size;
774
int error;
775
776
BHND_NV_ASSERT(cookiep != NULL, ("null cookiep"));
777
778
io_size = bhnd_nvram_io_getsize(btxt->data);
779
error = bhnd_nvram_io_read_ptr(btxt->data, 0x0, &ptr, io_size, NULL);
780
if (error)
781
BHND_NV_PANIC("error mapping offset %zu: %d", io_size, error);
782
783
offset = (const uint8_t *)cookiep - (const uint8_t *)ptr;
784
BHND_NV_ASSERT(offset >= 0, ("invalid cookiep"));
785
BHND_NV_ASSERT((uintptr_t)offset < SIZE_MAX, ("cookiep > SIZE_MAX)"));
786
BHND_NV_ASSERT((uintptr_t)offset <= io_size, ("cookiep > io_size)"));
787
788
return ((size_t)offset);
789
}
790
791
/* Determine the entry length and env 'key=value' string length of the entry
792
* at @p offset */
793
static int
794
bhnd_nvram_btxt_entry_len(struct bhnd_nvram_io *io, size_t offset,
795
size_t *line_len, size_t *env_len)
796
{
797
const uint8_t *baseptr, *p;
798
const void *rbuf;
799
size_t nbytes;
800
int error;
801
802
/* Fetch read buffer */
803
if ((error = bhnd_nvram_io_read_ptr(io, offset, &rbuf, 0, &nbytes)))
804
return (error);
805
806
/* Find record termination (EOL, or '#') */
807
p = rbuf;
808
baseptr = rbuf;
809
while ((size_t)(p - baseptr) < nbytes) {
810
if (*p == '#' || *p == '\n' || *p == '\r')
811
break;
812
813
p++;
814
}
815
816
/* Got line length, now trim any trailing whitespace to determine
817
* actual env length */
818
*line_len = p - baseptr;
819
*env_len = *line_len;
820
821
for (size_t i = 0; i < *line_len; i++) {
822
char c = baseptr[*line_len - i - 1];
823
if (!bhnd_nv_isspace(c))
824
break;
825
826
*env_len -= 1;
827
}
828
829
return (0);
830
}
831
832
/* Seek past the next line ending (\r, \r\n, or \n) */
833
static int
834
bhnd_nvram_btxt_seek_eol(struct bhnd_nvram_io *io, size_t *offset)
835
{
836
const uint8_t *baseptr, *p;
837
const void *rbuf;
838
size_t nbytes;
839
int error;
840
841
/* Fetch read buffer */
842
if ((error = bhnd_nvram_io_read_ptr(io, *offset, &rbuf, 0, &nbytes)))
843
return (error);
844
845
baseptr = rbuf;
846
p = rbuf;
847
while ((size_t)(p - baseptr) < nbytes) {
848
char c = *p;
849
850
/* Advance to next char. The next position may be EOF, in which
851
* case a read will be invalid */
852
p++;
853
854
if (c == '\r') {
855
/* CR, check for optional LF */
856
if ((size_t)(p - baseptr) < nbytes) {
857
if (*p == '\n')
858
p++;
859
}
860
861
break;
862
} else if (c == '\n') {
863
break;
864
}
865
}
866
867
/* Hit newline or EOF */
868
*offset += (p - baseptr);
869
return (0);
870
}
871
872
/* Seek to the next valid non-comment line (or EOF) */
873
static int
874
bhnd_nvram_btxt_seek_next(struct bhnd_nvram_io *io, size_t *offset)
875
{
876
const uint8_t *baseptr, *p;
877
const void *rbuf;
878
size_t nbytes;
879
int error;
880
881
/* Fetch read buffer */
882
if ((error = bhnd_nvram_io_read_ptr(io, *offset, &rbuf, 0, &nbytes)))
883
return (error);
884
885
/* Skip leading whitespace and comments */
886
baseptr = rbuf;
887
p = rbuf;
888
while ((size_t)(p - baseptr) < nbytes) {
889
char c = *p;
890
891
/* Skip whitespace */
892
if (bhnd_nv_isspace(c)) {
893
p++;
894
continue;
895
}
896
897
/* Skip entire comment line */
898
if (c == '#') {
899
size_t line_off = *offset + (p - baseptr);
900
901
if ((error = bhnd_nvram_btxt_seek_eol(io, &line_off)))
902
return (error);
903
904
p = baseptr + (line_off - *offset);
905
continue;
906
}
907
908
/* Non-whitespace, non-comment */
909
break;
910
}
911
912
*offset += (p - baseptr);
913
return (0);
914
}
915
916
static int
917
bhnd_nvram_btxt_filter_setvar(struct bhnd_nvram_data *nv, const char *name,
918
bhnd_nvram_val *value, bhnd_nvram_val **result)
919
{
920
bhnd_nvram_val *str;
921
const char *inp;
922
bhnd_nvram_type itype;
923
size_t ilen;
924
int error;
925
926
/* Name (trimmed of any path prefix) must be valid */
927
if (!bhnd_nvram_validate_name(bhnd_nvram_trim_path_name(name)))
928
return (EINVAL);
929
930
/* Value must be bcm-formatted string */
931
error = bhnd_nvram_val_convert_new(&str, &bhnd_nvram_val_bcm_string_fmt,
932
value, BHND_NVRAM_VAL_DYNAMIC);
933
if (error)
934
return (error);
935
936
/* Value string must not contain our record delimiter character ('\n'),
937
* or our comment character ('#') */
938
inp = bhnd_nvram_val_bytes(str, &ilen, &itype);
939
BHND_NV_ASSERT(itype == BHND_NVRAM_TYPE_STRING, ("non-string value"));
940
for (size_t i = 0; i < ilen; i++) {
941
switch (inp[i]) {
942
case '\n':
943
case '#':
944
BHND_NV_LOG("invalid character (%#hhx) in value\n",
945
inp[i]);
946
bhnd_nvram_val_release(str);
947
return (EINVAL);
948
}
949
}
950
951
/* Success. Transfer result ownership to the caller. */
952
*result = str;
953
return (0);
954
}
955
956
static int
957
bhnd_nvram_btxt_filter_unsetvar(struct bhnd_nvram_data *nv, const char *name)
958
{
959
/* We permit deletion of any variable */
960
return (0);
961
}
962
963