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_tlv.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/cdefs.h>
31
#ifdef _KERNEL
32
#include <sys/param.h>
33
#include <sys/ctype.h>
34
#include <sys/limits.h>
35
#include <sys/malloc.h>
36
#include <sys/systm.h>
37
#else /* !_KERNEL */
38
#include <ctype.h>
39
#include <errno.h>
40
#include <stdint.h>
41
#include <stdio.h>
42
#include <stdlib.h>
43
#include <string.h>
44
#endif /* _KERNEL */
45
46
#include "bhnd_nvram_private.h"
47
48
#include "bhnd_nvram_datavar.h"
49
50
#include "bhnd_nvram_data_tlvreg.h"
51
52
/*
53
* CFE TLV NVRAM data class.
54
*
55
* The CFE-defined TLV NVRAM format is used on the WGT634U.
56
*/
57
58
struct bhnd_nvram_tlv {
59
struct bhnd_nvram_data nv; /**< common instance state */
60
struct bhnd_nvram_io *data; /**< backing buffer */
61
size_t count; /**< variable count */
62
};
63
64
BHND_NVRAM_DATA_CLASS_DEFN(tlv, "WGT634U", BHND_NVRAM_DATA_CAP_DEVPATHS,
65
sizeof(struct bhnd_nvram_tlv))
66
67
/** Minimal TLV_ENV record header */
68
struct bhnd_nvram_tlv_env_hdr {
69
uint8_t tag;
70
uint8_t size;
71
} __packed;
72
73
/** Minimal TLV_ENV record */
74
struct bhnd_nvram_tlv_env {
75
struct bhnd_nvram_tlv_env_hdr hdr;
76
uint8_t flags;
77
char envp[];
78
} __packed;
79
80
/* Return the length in bytes of an TLV_ENV's envp data */
81
#define NVRAM_TLV_ENVP_DATA_LEN(_env) \
82
(((_env)->hdr.size < sizeof((_env)->flags)) ? 0 : \
83
((_env)->hdr.size - sizeof((_env)->flags)))
84
85
/* Maximum supported length of the envp data field, in bytes */
86
#define NVRAM_TLV_ENVP_DATA_MAX_LEN \
87
(UINT8_MAX - sizeof(uint8_t) /* flags */)
88
89
static int bhnd_nvram_tlv_parse_size(
90
struct bhnd_nvram_io *io,
91
size_t *size);
92
93
static int bhnd_nvram_tlv_next_record(
94
struct bhnd_nvram_io *io,
95
size_t *next, size_t *offset,
96
uint8_t *tag);
97
98
static struct bhnd_nvram_tlv_env *bhnd_nvram_tlv_next_env(
99
struct bhnd_nvram_tlv *tlv,
100
size_t *next, void **cookiep);
101
102
static struct bhnd_nvram_tlv_env *bhnd_nvram_tlv_get_env(
103
struct bhnd_nvram_tlv *tlv,
104
void *cookiep);
105
106
static void *bhnd_nvram_tlv_to_cookie(
107
struct bhnd_nvram_tlv *tlv,
108
size_t io_offset);
109
static size_t bhnd_nvram_tlv_to_offset(
110
struct bhnd_nvram_tlv *tlv,
111
void *cookiep);
112
113
static int
114
bhnd_nvram_tlv_probe(struct bhnd_nvram_io *io)
115
{
116
struct bhnd_nvram_tlv_env ident;
117
size_t nbytes;
118
int error;
119
120
nbytes = bhnd_nvram_io_getsize(io);
121
122
/* Handle what might be an empty TLV image */
123
if (nbytes < sizeof(ident)) {
124
uint8_t tag;
125
126
/* Fetch just the first tag */
127
error = bhnd_nvram_io_read(io, 0x0, &tag, sizeof(tag));
128
if (error)
129
return (error);
130
131
/* This *could* be an empty TLV image, but all we're
132
* testing for here is a single 0x0 byte followed by EOF */
133
if (tag == NVRAM_TLV_TYPE_END)
134
return (BHND_NVRAM_DATA_PROBE_MAYBE);
135
136
return (ENXIO);
137
}
138
139
/* Otherwise, look at the initial header for a valid TLV ENV tag,
140
* plus one byte of the entry data */
141
error = bhnd_nvram_io_read(io, 0x0, &ident,
142
sizeof(ident) + sizeof(ident.envp[0]));
143
if (error)
144
return (error);
145
146
/* First entry should be a variable record (which we statically
147
* assert as being defined to use a single byte size field) */
148
if (ident.hdr.tag != NVRAM_TLV_TYPE_ENV)
149
return (ENXIO);
150
151
_Static_assert(NVRAM_TLV_TYPE_ENV & NVRAM_TLV_TF_U8_LEN,
152
"TYPE_ENV is not a U8-sized field");
153
154
/* The entry must be at least 3 characters ('x=\0') in length */
155
if (ident.hdr.size < 3)
156
return (ENXIO);
157
158
/* The first character should be a valid key char (alpha) */
159
if (!bhnd_nv_isalpha(ident.envp[0]))
160
return (ENXIO);
161
162
return (BHND_NVRAM_DATA_PROBE_DEFAULT);
163
}
164
165
static int
166
bhnd_nvram_tlv_getvar_direct(struct bhnd_nvram_io *io, const char *name,
167
void *buf, size_t *len, bhnd_nvram_type type)
168
{
169
struct bhnd_nvram_tlv_env env;
170
char data[NVRAM_TLV_ENVP_DATA_MAX_LEN];
171
size_t data_len;
172
const char *key, *value;
173
size_t keylen, vlen;
174
size_t namelen;
175
size_t next, off;
176
uint8_t tag;
177
int error;
178
179
namelen = strlen(name);
180
181
/* Iterate over the input looking for the requested variable */
182
next = 0;
183
while (!(error = bhnd_nvram_tlv_next_record(io, &next, &off, &tag))) {
184
switch (tag) {
185
case NVRAM_TLV_TYPE_END:
186
/* Not found */
187
return (ENOENT);
188
189
case NVRAM_TLV_TYPE_ENV:
190
/* Read the record header */
191
error = bhnd_nvram_io_read(io, off, &env, sizeof(env));
192
if (error) {
193
BHND_NV_LOG("error reading TLV_ENV record "
194
"header: %d\n", error);
195
return (error);
196
}
197
198
/* Read the record data */
199
data_len = NVRAM_TLV_ENVP_DATA_LEN(&env);
200
error = bhnd_nvram_io_read(io, off + sizeof(env), data,
201
data_len);
202
if (error) {
203
BHND_NV_LOG("error reading TLV_ENV record "
204
"data: %d\n", error);
205
return (error);
206
}
207
208
/* Parse the key=value string */
209
error = bhnd_nvram_parse_env(data, data_len, '=', &key,
210
&keylen, &value, &vlen);
211
if (error) {
212
BHND_NV_LOG("error parsing TLV_ENV data: %d\n",
213
error);
214
return (error);
215
}
216
217
/* Match against requested variable name */
218
if (keylen == namelen &&
219
strncmp(key, name, namelen) == 0)
220
{
221
return (bhnd_nvram_value_coerce(value, vlen,
222
BHND_NVRAM_TYPE_STRING, buf, len, type));
223
}
224
225
break;
226
227
default:
228
/* Skip unknown tags */
229
break;
230
}
231
}
232
233
/* Hit I/O error */
234
return (error);
235
}
236
237
static int
238
bhnd_nvram_tlv_serialize(bhnd_nvram_data_class *cls, bhnd_nvram_plist *props,
239
bhnd_nvram_plist *options, void *outp, size_t *olen)
240
{
241
bhnd_nvram_prop *prop;
242
size_t limit, nbytes;
243
int error;
244
245
/* Determine output byte limit */
246
if (outp != NULL)
247
limit = *olen;
248
else
249
limit = 0;
250
251
nbytes = 0;
252
253
/* Write all properties */
254
prop = NULL;
255
while ((prop = bhnd_nvram_plist_next(props, prop)) != NULL) {
256
struct bhnd_nvram_tlv_env env;
257
const char *name;
258
uint8_t *p;
259
size_t name_len, value_len;
260
size_t rec_size;
261
262
env.hdr.tag = NVRAM_TLV_TYPE_ENV;
263
env.hdr.size = sizeof(env.flags);
264
env.flags = 0x0;
265
266
/* Fetch name value and add to record length */
267
name = bhnd_nvram_prop_name(prop);
268
name_len = strlen(name) + 1 /* '=' */;
269
270
if (UINT8_MAX - env.hdr.size < name_len) {
271
BHND_NV_LOG("%s name exceeds maximum TLV record "
272
"length\n", name);
273
return (EFTYPE); /* would overflow TLV size */
274
}
275
276
env.hdr.size += name_len;
277
278
/* Add string value to record length */
279
error = bhnd_nvram_prop_encode(prop, NULL, &value_len,
280
BHND_NVRAM_TYPE_STRING);
281
if (error) {
282
BHND_NV_LOG("error serializing %s to required type "
283
"%s: %d\n", name,
284
bhnd_nvram_type_name(BHND_NVRAM_TYPE_STRING),
285
error);
286
return (error);
287
}
288
289
if (UINT8_MAX - env.hdr.size < value_len) {
290
BHND_NV_LOG("%s value exceeds maximum TLV record "
291
"length\n", name);
292
return (EFTYPE); /* would overflow TLV size */
293
}
294
295
env.hdr.size += value_len;
296
297
/* Calculate total record size */
298
rec_size = sizeof(env.hdr) + env.hdr.size;
299
if (SIZE_MAX - nbytes < rec_size)
300
return (EFTYPE); /* would overflow size_t */
301
302
/* Calculate our output pointer */
303
if (nbytes > limit || limit - nbytes < rec_size) {
304
/* buffer is full; cannot write */
305
p = NULL;
306
} else {
307
p = (uint8_t *)outp + nbytes;
308
}
309
310
/* Write to output */
311
if (p != NULL) {
312
memcpy(p, &env, sizeof(env));
313
p += sizeof(env);
314
315
memcpy(p, name, name_len - 1);
316
p[name_len - 1] = '=';
317
p += name_len;
318
319
error = bhnd_nvram_prop_encode(prop, p, &value_len,
320
BHND_NVRAM_TYPE_STRING);
321
if (error) {
322
BHND_NV_LOG("error serializing %s to required "
323
"type %s: %d\n", name,
324
bhnd_nvram_type_name(
325
BHND_NVRAM_TYPE_STRING),
326
error);
327
return (error);
328
}
329
}
330
331
nbytes += rec_size;
332
}
333
334
/* Write terminating END record */
335
if (limit > nbytes)
336
*((uint8_t *)outp + nbytes) = NVRAM_TLV_TYPE_END;
337
338
if (nbytes == SIZE_MAX)
339
return (EFTYPE); /* would overflow size_t */
340
nbytes++;
341
342
/* Provide required length */
343
*olen = nbytes;
344
if (limit < *olen) {
345
if (outp == NULL)
346
return (0);
347
348
return (ENOMEM);
349
}
350
351
return (0);
352
}
353
354
/**
355
* Initialize @p tlv with the provided NVRAM TLV data mapped by @p src.
356
*
357
* @param tlv A newly allocated data instance.
358
*/
359
static int
360
bhnd_nvram_tlv_init(struct bhnd_nvram_tlv *tlv, struct bhnd_nvram_io *src)
361
{
362
struct bhnd_nvram_tlv_env *env;
363
size_t size;
364
size_t next;
365
int error;
366
367
BHND_NV_ASSERT(tlv->data == NULL, ("tlv data already initialized"));
368
369
/* Determine the actual size of the TLV source data */
370
if ((error = bhnd_nvram_tlv_parse_size(src, &size)))
371
return (error);
372
373
/* Copy to our own internal buffer */
374
if ((tlv->data = bhnd_nvram_iobuf_copy_range(src, 0x0, size)) == NULL)
375
return (ENOMEM);
376
377
/* Initialize our backing buffer */
378
tlv->count = 0;
379
next = 0;
380
while ((env = bhnd_nvram_tlv_next_env(tlv, &next, NULL)) != NULL) {
381
size_t env_len;
382
size_t name_len;
383
384
/* TLV_ENV data must not be empty */
385
env_len = NVRAM_TLV_ENVP_DATA_LEN(env);
386
if (env_len == 0) {
387
BHND_NV_LOG("cannot parse zero-length TLV_ENV record "
388
"data\n");
389
return (EINVAL);
390
}
391
392
/* Parse the key=value string, and then replace the '='
393
* delimiter with '\0' to allow us to provide direct
394
* name pointers from our backing buffer */
395
error = bhnd_nvram_parse_env(env->envp, env_len, '=', NULL,
396
&name_len, NULL, NULL);
397
if (error) {
398
BHND_NV_LOG("error parsing TLV_ENV data: %d\n", error);
399
return (error);
400
}
401
402
/* Replace '=' with '\0' */
403
*(env->envp + name_len) = '\0';
404
405
/* Add to variable count */
406
tlv->count++;
407
};
408
409
return (0);
410
}
411
412
static int
413
bhnd_nvram_tlv_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io)
414
{
415
416
struct bhnd_nvram_tlv *tlv;
417
int error;
418
419
/* Allocate and initialize the TLV data instance */
420
tlv = (struct bhnd_nvram_tlv *)nv;
421
422
/* Parse the TLV input data and initialize our backing
423
* data representation */
424
if ((error = bhnd_nvram_tlv_init(tlv, io))) {
425
bhnd_nvram_tlv_free(nv);
426
return (error);
427
}
428
429
return (0);
430
}
431
432
static void
433
bhnd_nvram_tlv_free(struct bhnd_nvram_data *nv)
434
{
435
struct bhnd_nvram_tlv *tlv = (struct bhnd_nvram_tlv *)nv;
436
if (tlv->data != NULL)
437
bhnd_nvram_io_free(tlv->data);
438
}
439
440
size_t
441
bhnd_nvram_tlv_count(struct bhnd_nvram_data *nv)
442
{
443
struct bhnd_nvram_tlv *tlv = (struct bhnd_nvram_tlv *)nv;
444
return (tlv->count);
445
}
446
447
static bhnd_nvram_plist *
448
bhnd_nvram_tlv_options(struct bhnd_nvram_data *nv)
449
{
450
return (NULL);
451
}
452
453
static uint32_t
454
bhnd_nvram_tlv_caps(struct bhnd_nvram_data *nv)
455
{
456
return (BHND_NVRAM_DATA_CAP_READ_PTR|BHND_NVRAM_DATA_CAP_DEVPATHS);
457
}
458
459
static const char *
460
bhnd_nvram_tlv_next(struct bhnd_nvram_data *nv, void **cookiep)
461
{
462
struct bhnd_nvram_tlv *tlv;
463
struct bhnd_nvram_tlv_env *env;
464
size_t io_offset;
465
466
tlv = (struct bhnd_nvram_tlv *)nv;
467
468
/* Find next readable TLV record */
469
if (*cookiep == NULL) {
470
/* Start search at offset 0x0 */
471
io_offset = 0x0;
472
env = bhnd_nvram_tlv_next_env(tlv, &io_offset, cookiep);
473
} else {
474
/* Seek past the previous env record */
475
io_offset = bhnd_nvram_tlv_to_offset(tlv, *cookiep);
476
env = bhnd_nvram_tlv_next_env(tlv, &io_offset, NULL);
477
if (env == NULL)
478
BHND_NV_PANIC("invalid cookiep; record missing");
479
480
/* Advance to next env record, update the caller's cookiep */
481
env = bhnd_nvram_tlv_next_env(tlv, &io_offset, cookiep);
482
}
483
484
/* Check for EOF */
485
if (env == NULL)
486
return (NULL);
487
488
/* Return the NUL terminated name */
489
return (env->envp);
490
}
491
492
static void *
493
bhnd_nvram_tlv_find(struct bhnd_nvram_data *nv, const char *name)
494
{
495
return (bhnd_nvram_data_generic_find(nv, name));
496
}
497
498
static int
499
bhnd_nvram_tlv_getvar_order(struct bhnd_nvram_data *nv, void *cookiep1,
500
void *cookiep2)
501
{
502
if (cookiep1 < cookiep2)
503
return (-1);
504
505
if (cookiep1 > cookiep2)
506
return (1);
507
508
return (0);
509
}
510
511
static int
512
bhnd_nvram_tlv_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
513
size_t *len, bhnd_nvram_type type)
514
{
515
return (bhnd_nvram_data_generic_rp_getvar(nv, cookiep, buf, len, type));
516
}
517
518
static int
519
bhnd_nvram_tlv_copy_val(struct bhnd_nvram_data *nv, void *cookiep,
520
bhnd_nvram_val **value)
521
{
522
return (bhnd_nvram_data_generic_rp_copy_val(nv, cookiep, value));
523
}
524
525
static const void *
526
bhnd_nvram_tlv_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep,
527
size_t *len, bhnd_nvram_type *type)
528
{
529
struct bhnd_nvram_tlv *tlv;
530
struct bhnd_nvram_tlv_env *env;
531
const char *val;
532
int error;
533
534
tlv = (struct bhnd_nvram_tlv *)nv;
535
536
/* Fetch pointer to the TLV_ENV record */
537
if ((env = bhnd_nvram_tlv_get_env(tlv, cookiep)) == NULL)
538
BHND_NV_PANIC("invalid cookiep: %p", cookiep);
539
540
/* Parse value pointer and length from key\0value data */
541
error = bhnd_nvram_parse_env(env->envp, NVRAM_TLV_ENVP_DATA_LEN(env),
542
'\0', NULL, NULL, &val, len);
543
if (error)
544
BHND_NV_PANIC("unexpected error parsing '%s'", env->envp);
545
546
/* Type is always CSTR */
547
*type = BHND_NVRAM_TYPE_STRING;
548
549
return (val);
550
}
551
552
static const char *
553
bhnd_nvram_tlv_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)
554
{
555
struct bhnd_nvram_tlv *tlv;
556
const struct bhnd_nvram_tlv_env *env;
557
558
tlv = (struct bhnd_nvram_tlv *)nv;
559
560
/* Fetch pointer to the TLV_ENV record */
561
if ((env = bhnd_nvram_tlv_get_env(tlv, cookiep)) == NULL)
562
BHND_NV_PANIC("invalid cookiep: %p", cookiep);
563
564
/* Return name pointer */
565
return (&env->envp[0]);
566
}
567
568
static int
569
bhnd_nvram_tlv_filter_setvar(struct bhnd_nvram_data *nv, const char *name,
570
bhnd_nvram_val *value, bhnd_nvram_val **result)
571
{
572
bhnd_nvram_val *str;
573
const char *inp;
574
bhnd_nvram_type itype;
575
size_t ilen;
576
size_t name_len, tlv_nremain;
577
int error;
578
579
tlv_nremain = NVRAM_TLV_ENVP_DATA_MAX_LEN;
580
581
/* Name (trimmed of any path prefix) must be valid */
582
if (!bhnd_nvram_validate_name(bhnd_nvram_trim_path_name(name)))
583
return (EINVAL);
584
585
/* 'name=' must fit within the maximum TLV_ENV record length */
586
name_len = strlen(name) + 1; /* '=' */
587
if (tlv_nremain < name_len) {
588
BHND_NV_LOG("'%s=' exceeds maximum TLV_ENV record length\n",
589
name);
590
return (EINVAL);
591
}
592
tlv_nremain -= name_len;
593
594
/* Convert value to a (bcm-formatted) string */
595
error = bhnd_nvram_val_convert_new(&str, &bhnd_nvram_val_bcm_string_fmt,
596
value, BHND_NVRAM_VAL_DYNAMIC);
597
if (error)
598
return (error);
599
600
/* The string value must fit within remaining TLV_ENV record length */
601
inp = bhnd_nvram_val_bytes(str, &ilen, &itype);
602
if (tlv_nremain < ilen) {
603
BHND_NV_LOG("'%.*s\\0' exceeds maximum TLV_ENV record length\n",
604
BHND_NV_PRINT_WIDTH(ilen), inp);
605
606
bhnd_nvram_val_release(str);
607
return (EINVAL);
608
}
609
tlv_nremain -= name_len;
610
611
/* Success. Transfer result ownership to the caller. */
612
*result = str;
613
return (0);
614
}
615
616
static int
617
bhnd_nvram_tlv_filter_unsetvar(struct bhnd_nvram_data *nv, const char *name)
618
{
619
/* We permit deletion of any variable */
620
return (0);
621
}
622
623
/**
624
* Iterate over the records starting at @p next, returning the parsed
625
* record's @p tag, @p size, and @p offset.
626
*
627
* @param io The I/O context to parse.
628
* @param[in,out] next The next offset to be parsed, or 0x0
629
* to begin parsing. Upon successful
630
* return, will be set to the offset of the
631
* next record (or EOF, if
632
* NVRAM_TLV_TYPE_END was parsed).
633
* @param[out] offset The record's value offset.
634
* @param[out] tag The record's tag.
635
*
636
* @retval 0 success
637
* @retval EINVAL if parsing @p io as TLV fails.
638
* @retval non-zero if reading @p io otherwise fails, a regular unix error
639
* code will be returned.
640
*/
641
static int
642
bhnd_nvram_tlv_next_record(struct bhnd_nvram_io *io, size_t *next, size_t
643
*offset, uint8_t *tag)
644
{
645
size_t io_offset, io_size;
646
uint16_t parsed_len;
647
uint8_t len_hdr[2];
648
int error;
649
650
io_offset = *next;
651
io_size = bhnd_nvram_io_getsize(io);
652
653
/* Save the record offset */
654
if (offset != NULL)
655
*offset = io_offset;
656
657
/* Fetch initial tag */
658
error = bhnd_nvram_io_read(io, io_offset, tag, sizeof(*tag));
659
if (error)
660
return (error);
661
io_offset++;
662
663
/* EOF */
664
if (*tag == NVRAM_TLV_TYPE_END) {
665
*next = io_offset;
666
return (0);
667
}
668
669
/* Read length field */
670
if (*tag & NVRAM_TLV_TF_U8_LEN) {
671
error = bhnd_nvram_io_read(io, io_offset, &len_hdr,
672
sizeof(len_hdr[0]));
673
if (error) {
674
BHND_NV_LOG("error reading TLV record size: %d\n",
675
error);
676
return (error);
677
}
678
679
parsed_len = len_hdr[0];
680
io_offset++;
681
} else {
682
error = bhnd_nvram_io_read(io, io_offset, &len_hdr,
683
sizeof(len_hdr));
684
if (error) {
685
BHND_NV_LOG("error reading 16-bit TLV record "
686
"size: %d\n", error);
687
return (error);
688
}
689
690
parsed_len = (len_hdr[0] << 8) | len_hdr[1];
691
io_offset += 2;
692
}
693
694
/* Advance to next record */
695
if (parsed_len > io_size || io_size - parsed_len < io_offset) {
696
/* Hit early EOF */
697
BHND_NV_LOG("TLV record length %hu truncated by input "
698
"size of %zu\n", parsed_len, io_size);
699
return (EINVAL);
700
}
701
702
*next = io_offset + parsed_len;
703
704
/* Valid record found */
705
return (0);
706
}
707
708
/**
709
* Parse the TLV data in @p io to determine the total size of the TLV
710
* data mapped by @p io (which may be less than the size of @p io).
711
*/
712
static int
713
bhnd_nvram_tlv_parse_size(struct bhnd_nvram_io *io, size_t *size)
714
{
715
size_t next;
716
uint8_t tag;
717
int error;
718
719
/* We have to perform a minimal parse to determine the actual length */
720
next = 0x0;
721
*size = 0x0;
722
723
/* Iterate over the input until we hit END tag or the read fails */
724
do {
725
error = bhnd_nvram_tlv_next_record(io, &next, NULL, &tag);
726
if (error)
727
return (error);
728
} while (tag != NVRAM_TLV_TYPE_END);
729
730
/* Offset should now point to EOF */
731
BHND_NV_ASSERT(next <= bhnd_nvram_io_getsize(io),
732
("parse returned invalid EOF offset"));
733
734
*size = next;
735
return (0);
736
}
737
738
/**
739
* Iterate over the records in @p tlv, returning a pointer to the next
740
* NVRAM_TLV_TYPE_ENV record, or NULL if EOF is reached.
741
*
742
* @param tlv The TLV instance.
743
* @param[in,out] next The next offset to be parsed, or 0x0
744
* to begin parsing. Upon successful
745
* return, will be set to the offset of the
746
* next record.
747
*/
748
static struct bhnd_nvram_tlv_env *
749
bhnd_nvram_tlv_next_env(struct bhnd_nvram_tlv *tlv, size_t *next,
750
void **cookiep)
751
{
752
uint8_t tag;
753
int error;
754
755
/* Find the next TLV_ENV record, starting at @p next */
756
do {
757
void *c;
758
size_t offset;
759
760
/* Fetch the next TLV record */
761
error = bhnd_nvram_tlv_next_record(tlv->data, next, &offset,
762
&tag);
763
if (error) {
764
BHND_NV_LOG("unexpected error in next_record(): %d\n",
765
error);
766
return (NULL);
767
}
768
769
/* Only interested in ENV records */
770
if (tag != NVRAM_TLV_TYPE_ENV)
771
continue;
772
773
/* Map and return TLV_ENV record pointer */
774
c = bhnd_nvram_tlv_to_cookie(tlv, offset);
775
776
/* Provide the cookiep value for the returned record */
777
if (cookiep != NULL)
778
*cookiep = c;
779
780
return (bhnd_nvram_tlv_get_env(tlv, c));
781
} while (tag != NVRAM_TLV_TYPE_END);
782
783
/* No remaining ENV records */
784
return (NULL);
785
}
786
787
/**
788
* Return a pointer to the TLV_ENV record for @p cookiep, or NULL
789
* if none vailable.
790
*/
791
static struct bhnd_nvram_tlv_env *
792
bhnd_nvram_tlv_get_env(struct bhnd_nvram_tlv *tlv, void *cookiep)
793
{
794
struct bhnd_nvram_tlv_env *env;
795
void *ptr;
796
size_t navail;
797
size_t io_offset, io_size;
798
int error;
799
800
io_size = bhnd_nvram_io_getsize(tlv->data);
801
io_offset = bhnd_nvram_tlv_to_offset(tlv, cookiep);
802
803
/* At EOF? */
804
if (io_offset == io_size)
805
return (NULL);
806
807
/* Fetch non-const pointer to the record entry */
808
error = bhnd_nvram_io_write_ptr(tlv->data, io_offset, &ptr,
809
sizeof(env->hdr), &navail);
810
if (error) {
811
/* Should never occur with a valid cookiep */
812
BHND_NV_LOG("error mapping record for cookiep: %d\n", error);
813
return (NULL);
814
}
815
816
/* Validate the record pointer */
817
env = ptr;
818
if (env->hdr.tag != NVRAM_TLV_TYPE_ENV) {
819
/* Should never occur with a valid cookiep */
820
BHND_NV_LOG("non-ENV record mapped for %p\n", cookiep);
821
return (NULL);
822
}
823
824
/* Is the required variable name data is mapped? */
825
if (navail < sizeof(struct bhnd_nvram_tlv_env_hdr) + env->hdr.size ||
826
env->hdr.size == sizeof(env->flags))
827
{
828
/* Should never occur with a valid cookiep */
829
BHND_NV_LOG("TLV_ENV variable data not mapped for %p\n",
830
cookiep);
831
return (NULL);
832
}
833
834
return (env);
835
}
836
837
/**
838
* Return a cookiep for the given I/O offset.
839
*/
840
static void *
841
bhnd_nvram_tlv_to_cookie(struct bhnd_nvram_tlv *tlv, size_t io_offset)
842
{
843
const void *ptr;
844
int error;
845
846
BHND_NV_ASSERT(io_offset < bhnd_nvram_io_getsize(tlv->data),
847
("io_offset %zu out-of-range", io_offset));
848
BHND_NV_ASSERT(io_offset < UINTPTR_MAX,
849
("io_offset %#zx exceeds UINTPTR_MAX", io_offset));
850
851
error = bhnd_nvram_io_read_ptr(tlv->data, 0x0, &ptr, io_offset, NULL);
852
if (error)
853
BHND_NV_PANIC("error mapping offset %zu: %d", io_offset, error);
854
855
ptr = (const uint8_t *)ptr + io_offset;
856
return (__DECONST(void *, ptr));
857
}
858
859
/* Convert a cookiep back to an I/O offset */
860
static size_t
861
bhnd_nvram_tlv_to_offset(struct bhnd_nvram_tlv *tlv, void *cookiep)
862
{
863
const void *ptr;
864
intptr_t offset;
865
size_t io_size;
866
int error;
867
868
BHND_NV_ASSERT(cookiep != NULL, ("null cookiep"));
869
870
io_size = bhnd_nvram_io_getsize(tlv->data);
871
872
error = bhnd_nvram_io_read_ptr(tlv->data, 0x0, &ptr, io_size, NULL);
873
if (error)
874
BHND_NV_PANIC("error mapping offset %zu: %d", io_size, error);
875
876
offset = (const uint8_t *)cookiep - (const uint8_t *)ptr;
877
BHND_NV_ASSERT(offset >= 0, ("invalid cookiep"));
878
BHND_NV_ASSERT((uintptr_t)offset < SIZE_MAX, ("cookiep > SIZE_MAX)"));
879
BHND_NV_ASSERT((uintptr_t)offset <= io_size, ("cookiep > io_size)"));
880
881
return ((size_t)offset);
882
}
883
884