Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/dev/bhnd/nvram/bhnd_nvram_value_fmts.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/param.h>
31
32
#include <net/ethernet.h>
33
34
#ifdef _KERNEL
35
36
#include <sys/ctype.h>
37
#include <sys/kernel.h>
38
#include <sys/malloc.h>
39
#include <sys/systm.h>
40
41
#include <machine/_inttypes.h>
42
43
#else /* !_KERNEL */
44
45
#include <ctype.h>
46
#include <errno.h>
47
#include <inttypes.h>
48
#include <stdlib.h>
49
#include <string.h>
50
51
#endif /* _KERNEL */
52
53
#include "bhnd_nvram_private.h"
54
55
#include "bhnd_nvram_valuevar.h"
56
57
static bool bhnd_nvram_ident_octet_string(const char *inp,
58
size_t ilen, char *delim, size_t *nelem);
59
static bool bhnd_nvram_ident_num_string(const char *inp,
60
size_t ilen, u_int base, u_int *obase);
61
62
static int bhnd_nvram_val_bcm_macaddr_filter(
63
const bhnd_nvram_val_fmt **fmt, const void *inp,
64
size_t ilen, bhnd_nvram_type itype);
65
static int bhnd_nvram_val_bcm_macaddr_encode(
66
bhnd_nvram_val *value, void *outp, size_t *olen,
67
bhnd_nvram_type otype);
68
69
static int bhnd_nvram_val_bcm_macaddr_string_filter(
70
const bhnd_nvram_val_fmt **fmt, const void *inp,
71
size_t ilen, bhnd_nvram_type itype);
72
static int bhnd_nvram_val_bcm_macaddr_string_encode_elem(
73
bhnd_nvram_val *value, const void *inp,
74
size_t ilen, void *outp, size_t *olen,
75
bhnd_nvram_type otype);
76
static const void *bhnd_nvram_val_bcm_macaddr_string_next(
77
bhnd_nvram_val *value, const void *prev,
78
size_t *len);
79
80
static int bhnd_nvram_val_bcm_int_filter(
81
const bhnd_nvram_val_fmt **fmt, const void *inp,
82
size_t ilen, bhnd_nvram_type itype);
83
static int bhnd_nvram_val_bcm_int_encode(bhnd_nvram_val *value,
84
void *outp, size_t *olen, bhnd_nvram_type otype);
85
86
static int bhnd_nvram_val_bcm_decimal_encode_elem(
87
bhnd_nvram_val *value, const void *inp,
88
size_t ilen, void *outp, size_t *olen,
89
bhnd_nvram_type otype);
90
static int bhnd_nvram_val_bcm_hex_encode_elem(
91
bhnd_nvram_val *value, const void *inp,
92
size_t ilen, void *outp, size_t *olen,
93
bhnd_nvram_type otype);
94
95
static int bhnd_nvram_val_bcm_leddc_filter(
96
const bhnd_nvram_val_fmt **fmt, const void *inp,
97
size_t ilen, bhnd_nvram_type itype);
98
static int bhnd_nvram_val_bcm_leddc_encode_elem(
99
bhnd_nvram_val *value, const void *inp,
100
size_t ilen, void *outp, size_t *olen,
101
bhnd_nvram_type otype);
102
103
static int bhnd_nvram_val_bcmstr_encode(bhnd_nvram_val *value,
104
void *outp, size_t *olen, bhnd_nvram_type otype);
105
106
static int bhnd_nvram_val_bcmstr_csv_filter(
107
const bhnd_nvram_val_fmt **fmt, const void *inp,
108
size_t ilen, bhnd_nvram_type itype);
109
static const void *bhnd_nvram_val_bcmstr_csv_next(bhnd_nvram_val *value,
110
const void *prev, size_t *len);
111
112
/**
113
* Broadcom NVRAM MAC address format.
114
*/
115
const bhnd_nvram_val_fmt bhnd_nvram_val_bcm_macaddr_fmt = {
116
.name = "bcm-macaddr",
117
.native_type = BHND_NVRAM_TYPE_UINT8_ARRAY,
118
.op_filter = bhnd_nvram_val_bcm_macaddr_filter,
119
.op_encode = bhnd_nvram_val_bcm_macaddr_encode,
120
};
121
122
/** Broadcom NVRAM MAC address string format. */
123
static const bhnd_nvram_val_fmt bhnd_nvram_val_bcm_macaddr_string_fmt = {
124
.name = "bcm-macaddr-string",
125
.native_type = BHND_NVRAM_TYPE_STRING,
126
.op_filter = bhnd_nvram_val_bcm_macaddr_string_filter,
127
.op_encode_elem = bhnd_nvram_val_bcm_macaddr_string_encode_elem,
128
.op_next = bhnd_nvram_val_bcm_macaddr_string_next,
129
};
130
131
/**
132
* Broadcom NVRAM LED duty-cycle format.
133
*/
134
const bhnd_nvram_val_fmt bhnd_nvram_val_bcm_leddc_fmt = {
135
.name = "bcm-leddc",
136
.native_type = BHND_NVRAM_TYPE_UINT32,
137
.op_filter = bhnd_nvram_val_bcm_leddc_filter,
138
.op_encode_elem = bhnd_nvram_val_bcm_leddc_encode_elem,
139
};
140
141
/**
142
* Broadcom NVRAM decimal integer format.
143
*
144
* Extends standard integer handling, encoding the string representation of
145
* the integer value as a decimal string:
146
* - Positive values will be string-encoded without a prefix.
147
* - Negative values will be string-encoded with a leading '-' sign.
148
*/
149
const bhnd_nvram_val_fmt bhnd_nvram_val_bcm_decimal_fmt = {
150
.name = "bcm-decimal",
151
.native_type = BHND_NVRAM_TYPE_UINT64,
152
.op_filter = bhnd_nvram_val_bcm_int_filter,
153
.op_encode = bhnd_nvram_val_bcm_int_encode,
154
.op_encode_elem = bhnd_nvram_val_bcm_decimal_encode_elem,
155
};
156
157
/**
158
* Broadcom NVRAM decimal integer format.
159
*
160
* Extends standard integer handling, encoding the string representation of
161
* unsigned and positive signed integer values as an 0x-prefixed hexadecimal
162
* string.
163
*
164
* For compatibility with standard Broadcom NVRAM parsing, if the integer is
165
* both signed and negative, it will be string encoded as a negative decimal
166
* value, not as a twos-complement hexadecimal value.
167
*/
168
const bhnd_nvram_val_fmt bhnd_nvram_val_bcm_hex_fmt = {
169
.name = "bcm-hex",
170
.native_type = BHND_NVRAM_TYPE_UINT64,
171
.op_filter = bhnd_nvram_val_bcm_int_filter,
172
.op_encode = bhnd_nvram_val_bcm_int_encode,
173
.op_encode_elem = bhnd_nvram_val_bcm_hex_encode_elem,
174
};
175
176
/**
177
* Broadcom NVRAM string format.
178
*
179
* Handles standard, comma-delimited, and octet-string values as used in
180
* Broadcom NVRAM data.
181
*/
182
const bhnd_nvram_val_fmt bhnd_nvram_val_bcm_string_fmt = {
183
.name = "bcm-string",
184
.native_type = BHND_NVRAM_TYPE_STRING,
185
.op_encode = bhnd_nvram_val_bcmstr_encode,
186
};
187
188
/** Broadcom comma-delimited string. */
189
static const bhnd_nvram_val_fmt bhnd_nvram_val_bcm_string_csv_fmt = {
190
.name = "bcm-string[]",
191
.native_type = BHND_NVRAM_TYPE_STRING,
192
.op_filter = bhnd_nvram_val_bcmstr_csv_filter,
193
.op_next = bhnd_nvram_val_bcmstr_csv_next,
194
};
195
196
/* Built-in format definitions */
197
#define BHND_NVRAM_VAL_FMT_NATIVE(_n, _type) \
198
const bhnd_nvram_val_fmt bhnd_nvram_val_ ## _n ## _fmt = { \
199
.name = __STRING(_n), \
200
.native_type = BHND_NVRAM_TYPE_ ## _type, \
201
}
202
203
BHND_NVRAM_VAL_FMT_NATIVE(uint8, UINT8);
204
BHND_NVRAM_VAL_FMT_NATIVE(uint16, UINT16);
205
BHND_NVRAM_VAL_FMT_NATIVE(uint32, UINT32);
206
BHND_NVRAM_VAL_FMT_NATIVE(uint64, UINT64);
207
BHND_NVRAM_VAL_FMT_NATIVE(int8, INT8);
208
BHND_NVRAM_VAL_FMT_NATIVE(int16, INT16);
209
BHND_NVRAM_VAL_FMT_NATIVE(int32, INT32);
210
BHND_NVRAM_VAL_FMT_NATIVE(int64, INT64);
211
BHND_NVRAM_VAL_FMT_NATIVE(char, CHAR);
212
BHND_NVRAM_VAL_FMT_NATIVE(bool, BOOL);
213
BHND_NVRAM_VAL_FMT_NATIVE(string, STRING);
214
BHND_NVRAM_VAL_FMT_NATIVE(data, DATA);
215
BHND_NVRAM_VAL_FMT_NATIVE(null, NULL);
216
217
BHND_NVRAM_VAL_FMT_NATIVE(uint8_array, UINT8_ARRAY);
218
BHND_NVRAM_VAL_FMT_NATIVE(uint16_array, UINT16_ARRAY);
219
BHND_NVRAM_VAL_FMT_NATIVE(uint32_array, UINT32_ARRAY);
220
BHND_NVRAM_VAL_FMT_NATIVE(uint64_array, UINT64_ARRAY);
221
BHND_NVRAM_VAL_FMT_NATIVE(int8_array, INT8_ARRAY);
222
BHND_NVRAM_VAL_FMT_NATIVE(int16_array, INT16_ARRAY);
223
BHND_NVRAM_VAL_FMT_NATIVE(int32_array, INT32_ARRAY);
224
BHND_NVRAM_VAL_FMT_NATIVE(int64_array, INT64_ARRAY);
225
BHND_NVRAM_VAL_FMT_NATIVE(char_array, CHAR_ARRAY);
226
BHND_NVRAM_VAL_FMT_NATIVE(bool_array, BOOL_ARRAY);
227
BHND_NVRAM_VAL_FMT_NATIVE(string_array, STRING_ARRAY);
228
229
/**
230
* Common hex/decimal integer filter implementation.
231
*/
232
static int
233
bhnd_nvram_val_bcm_int_filter(const bhnd_nvram_val_fmt **fmt, const void *inp,
234
size_t ilen, bhnd_nvram_type itype)
235
{
236
bhnd_nvram_type itype_base;
237
238
itype_base = bhnd_nvram_base_type(itype);
239
240
switch (itype_base) {
241
case BHND_NVRAM_TYPE_STRING:
242
/*
243
* If the input is a string, delegate to the Broadcom
244
* string format -- preserving the original string value
245
* takes priority over enforcing hexadecimal/integer string
246
* formatting.
247
*/
248
*fmt = &bhnd_nvram_val_bcm_string_fmt;
249
return (0);
250
251
default:
252
if (bhnd_nvram_is_int_type(itype_base))
253
return (0);
254
255
return (EFTYPE);
256
}
257
}
258
259
/**
260
* Broadcom hex/decimal integer encode implementation.
261
*/
262
static int
263
bhnd_nvram_val_bcm_int_encode(bhnd_nvram_val *value, void *outp, size_t *olen,
264
bhnd_nvram_type otype)
265
{
266
/* If encoding to a string, format multiple elements (if any) with a
267
* comma delimiter. */
268
if (otype == BHND_NVRAM_TYPE_STRING)
269
return (bhnd_nvram_val_printf(value, "%[]s", outp, olen, ","));
270
271
return (bhnd_nvram_val_generic_encode(value, outp, olen, otype));
272
}
273
274
/**
275
* Broadcom hex integer encode_elem implementation.
276
*/
277
static int
278
bhnd_nvram_val_bcm_hex_encode_elem(bhnd_nvram_val *value, const void *inp,
279
size_t ilen, void *outp, size_t *olen, bhnd_nvram_type otype)
280
{
281
bhnd_nvram_type itype;
282
ssize_t width;
283
int error;
284
285
itype = bhnd_nvram_val_elem_type(value);
286
BHND_NV_ASSERT(bhnd_nvram_is_int_type(itype), ("invalid type"));
287
288
/* If not encoding as a string, perform generic value encoding */
289
if (otype != BHND_NVRAM_TYPE_STRING)
290
return (bhnd_nvram_val_generic_encode_elem(value, inp, ilen,
291
outp, olen, otype));
292
293
/* If the value is a signed, negative value, encode as a decimal
294
* string */
295
if (bhnd_nvram_is_signed_type(itype)) {
296
int64_t sval;
297
size_t slen;
298
bhnd_nvram_type stype;
299
300
stype = BHND_NVRAM_TYPE_INT64;
301
slen = sizeof(sval);
302
303
/* Fetch 64-bit signed representation */
304
error = bhnd_nvram_value_coerce(inp, ilen, itype, &sval, &slen,
305
stype);
306
if (error)
307
return (error);
308
309
/* Decimal encoding required? */
310
if (sval < 0)
311
return (bhnd_nvram_value_printf("%I64d", &sval, slen,
312
stype, outp, olen, otype));
313
}
314
315
/*
316
* Encode the value as a hex string.
317
*
318
* Most producers of Broadcom NVRAM values zero-pad hex values out to
319
* their native width (width * two hex characters), and we do the same
320
* for compatibility
321
*/
322
width = bhnd_nvram_type_width(itype) * 2;
323
return (bhnd_nvram_value_printf("0x%0*I64X", inp, ilen, itype,
324
outp, olen, width));
325
}
326
327
/**
328
* Broadcom decimal integer encode_elem implementation.
329
*/
330
static int
331
bhnd_nvram_val_bcm_decimal_encode_elem(bhnd_nvram_val *value, const void *inp,
332
size_t ilen, void *outp, size_t *olen, bhnd_nvram_type otype)
333
{
334
const char *sfmt;
335
bhnd_nvram_type itype;
336
337
itype = bhnd_nvram_val_elem_type(value);
338
BHND_NV_ASSERT(bhnd_nvram_is_int_type(itype), ("invalid type"));
339
340
/* If not encoding as a string, perform generic value encoding */
341
if (otype != BHND_NVRAM_TYPE_STRING)
342
return (bhnd_nvram_val_generic_encode_elem(value, inp, ilen,
343
outp, olen, otype));
344
345
sfmt = bhnd_nvram_is_signed_type(itype) ? "%I64d" : "%I64u";
346
return (bhnd_nvram_value_printf(sfmt, inp, ilen, itype, outp, olen));
347
}
348
349
/**
350
* Broadcom LED duty-cycle filter.
351
*/
352
static int
353
bhnd_nvram_val_bcm_leddc_filter(const bhnd_nvram_val_fmt **fmt,
354
const void *inp, size_t ilen, bhnd_nvram_type itype)
355
{
356
const char *p;
357
size_t plen;
358
359
switch (itype) {
360
case BHND_NVRAM_TYPE_UINT16:
361
case BHND_NVRAM_TYPE_UINT32:
362
return (0);
363
364
case BHND_NVRAM_TYPE_STRING:
365
/* Trim any whitespace */
366
p = inp;
367
plen = bhnd_nvram_trim_field(&p, ilen, '\0');
368
369
/* If the value is not a valid integer string, delegate to the
370
* Broadcom string format */
371
if (!bhnd_nvram_ident_num_string(p, plen, 0, NULL))
372
*fmt = &bhnd_nvram_val_bcm_string_fmt;
373
374
return (0);
375
default:
376
return (EFTYPE);
377
}
378
}
379
380
/**
381
* Broadcom LED duty-cycle encode.
382
*/
383
static int
384
bhnd_nvram_val_bcm_leddc_encode_elem(bhnd_nvram_val *value, const void *inp,
385
size_t ilen, void *outp, size_t *olen, bhnd_nvram_type otype)
386
{
387
bhnd_nvram_type itype;
388
size_t limit, nbytes;
389
int error;
390
uint16_t led16;
391
uint32_t led32;
392
bool led16_lossy;
393
union {
394
uint16_t u16;
395
uint32_t u32;
396
} strval;
397
398
/*
399
* LED duty-cycle values represent the on/off periods as a 32-bit
400
* integer, with the top 16 bits representing on cycles, and the
401
* bottom 16 representing off cycles.
402
*
403
* LED duty cycle values have three different formats:
404
*
405
* - SPROM: A 16-bit unsigned integer, with on/off cycles encoded
406
* as 8-bit values.
407
* - NVRAM: A 16-bit decimal or hexadecimal string, with on/off
408
* cycles encoded as 8-bit values as per the SPROM format.
409
* - NVRAM: A 32-bit decimal or hexadecimal string, with on/off
410
* cycles encoded as 16-bit values.
411
*
412
* To convert from a 16-bit representation to a 32-bit representation:
413
* ((value & 0xFF00) << 16) | ((value & 0x00FF) << 8)
414
*
415
* To convert from a 32-bit representation to a 16-bit representation,
416
* perform the same operation in reverse, discarding the lower 8-bits
417
* of each half of the 32-bit representation:
418
* ((value >> 16) & 0xFF00) | ((value >> 8) & 0x00FF)
419
*/
420
421
itype = bhnd_nvram_val_elem_type(value);
422
nbytes = 0;
423
led16_lossy = false;
424
425
/* Determine output byte limit */
426
if (outp != NULL)
427
limit = *olen;
428
else
429
limit = 0;
430
431
/* If the input/output types match, just delegate to standard value
432
* encoding support */
433
if (otype == itype) {
434
return (bhnd_nvram_value_coerce(inp, ilen, itype, outp, olen,
435
otype));
436
}
437
438
/* If our value is a string, it may either be a 16-bit or a 32-bit
439
* representation of the duty cycle */
440
if (itype == BHND_NVRAM_TYPE_STRING) {
441
const char *p;
442
uint32_t ival;
443
size_t nlen, parsed;
444
445
/* Parse integer value */
446
p = inp;
447
nlen = sizeof(ival);
448
error = bhnd_nvram_parse_int(p, ilen, 0, &parsed, &ival, &nlen,
449
BHND_NVRAM_TYPE_UINT32);
450
if (error)
451
return (error);
452
453
/* Trailing garbage? */
454
if (parsed < ilen && *(p+parsed) != '\0')
455
return (EFTYPE);
456
457
/* Point inp and itype to either our parsed 32-bit or 16-bit
458
* value */
459
inp = &strval;
460
if (ival & 0xFFFF0000) {
461
strval.u32 = ival;
462
itype = BHND_NVRAM_TYPE_UINT32;
463
} else {
464
strval.u16 = ival;
465
itype = BHND_NVRAM_TYPE_UINT16;
466
}
467
}
468
469
/* Populate both u32 and (possibly lossy) u16 LEDDC representations */
470
switch (itype) {
471
case BHND_NVRAM_TYPE_UINT16: {
472
led16 = *(const uint16_t *)inp;
473
led32 = ((led16 & 0xFF00) << 16) | ((led16 & 0x00FF) << 8);
474
475
/* If all bits are set in the 16-bit value (indicating that
476
* the value is 'unset' in SPROM), we must update the 32-bit
477
* representation to match. */
478
if (led16 == UINT16_MAX)
479
led32 = UINT32_MAX;
480
481
break;
482
}
483
484
case BHND_NVRAM_TYPE_UINT32:
485
led32 = *(const uint32_t *)inp;
486
led16 = ((led32 >> 16) & 0xFF00) | ((led32 >> 8) & 0x00FF);
487
488
/*
489
* Determine whether the led16 conversion is lossy:
490
*
491
* - If the lower 8 bits of each half of the 32-bit value
492
* aren't set, we can safely use the 16-bit representation
493
* without losing data.
494
* - If all bits in the 32-bit value are set, the variable is
495
* treated as unset in SPROM. We can safely use the 16-bit
496
* representation without losing data.
497
*/
498
if ((led32 & 0x00FF00FF) != 0 && led32 != UINT32_MAX)
499
led16_lossy = true;
500
501
break;
502
default:
503
BHND_NV_PANIC("unsupported backing data type: %s",
504
bhnd_nvram_type_name(itype));
505
}
506
507
/*
508
* Encode as requested output type.
509
*/
510
switch (otype) {
511
case BHND_NVRAM_TYPE_STRING:
512
/*
513
* Prefer 16-bit format.
514
*/
515
if (!led16_lossy) {
516
return (bhnd_nvram_value_printf("0x%04hX", &led16,
517
sizeof(led16), BHND_NVRAM_TYPE_UINT16, outp, olen));
518
} else {
519
return (bhnd_nvram_value_printf("0x%04X", &led32,
520
sizeof(led32), BHND_NVRAM_TYPE_UINT32, outp, olen));
521
}
522
523
break;
524
525
case BHND_NVRAM_TYPE_UINT16: {
526
/* Can we encode as uint16 without losing data? */
527
if (led16_lossy)
528
return (ERANGE);
529
530
/* Write led16 format */
531
nbytes += sizeof(uint16_t);
532
if (limit >= nbytes)
533
*(uint16_t *)outp = led16;
534
535
break;
536
}
537
538
case BHND_NVRAM_TYPE_UINT32:
539
/* Write led32 format */
540
nbytes += sizeof(uint32_t);
541
if (limit >= nbytes)
542
*(uint32_t *)outp = led32;
543
break;
544
545
default:
546
/* No other output formats are supported */
547
return (EFTYPE);
548
}
549
550
/* Provide the actual length */
551
*olen = nbytes;
552
553
/* Report insufficient space (if output was requested) */
554
if (limit < nbytes && outp != NULL)
555
return (ENOMEM);
556
557
return (0);
558
}
559
560
/**
561
* Broadcom NVRAM string encoding.
562
*/
563
static int
564
bhnd_nvram_val_bcmstr_encode(bhnd_nvram_val *value, void *outp, size_t *olen,
565
bhnd_nvram_type otype)
566
{
567
bhnd_nvram_val array;
568
const bhnd_nvram_val_fmt *array_fmt;
569
const void *inp;
570
bhnd_nvram_type itype;
571
size_t ilen;
572
int error;
573
574
inp = bhnd_nvram_val_bytes(value, &ilen, &itype);
575
576
/* If the output is not an array type (or if it's a character array),
577
* we can fall back on standard string encoding */
578
if (!bhnd_nvram_is_array_type(otype) ||
579
otype == BHND_NVRAM_TYPE_CHAR_ARRAY)
580
{
581
return (bhnd_nvram_value_coerce(inp, ilen, itype, outp, olen,
582
otype));
583
}
584
585
/* Otherwise, we need to interpret our value as either a macaddr
586
* string, or a comma-delimited string. */
587
inp = bhnd_nvram_val_bytes(value, &ilen, &itype);
588
if (bhnd_nvram_ident_octet_string(inp, ilen, NULL, NULL))
589
array_fmt = &bhnd_nvram_val_bcm_macaddr_string_fmt;
590
else
591
array_fmt = &bhnd_nvram_val_bcm_string_csv_fmt;
592
593
/* Wrap in array-typed representation */
594
error = bhnd_nvram_val_init(&array, array_fmt, inp, ilen, itype,
595
BHND_NVRAM_VAL_BORROW_DATA);
596
if (error) {
597
BHND_NV_LOG("error initializing array representation: %d\n",
598
error);
599
return (error);
600
}
601
602
/* Ask the array-typed value to perform the encode */
603
error = bhnd_nvram_val_encode(&array, outp, olen, otype);
604
if (error)
605
BHND_NV_LOG("error encoding array representation: %d\n", error);
606
607
bhnd_nvram_val_release(&array);
608
609
return (error);
610
}
611
612
/**
613
* Broadcom NVRAM comma-delimited string filter.
614
*/
615
static int
616
bhnd_nvram_val_bcmstr_csv_filter(const bhnd_nvram_val_fmt **fmt,
617
const void *inp, size_t ilen, bhnd_nvram_type itype)
618
{
619
switch (itype) {
620
case BHND_NVRAM_TYPE_STRING:
621
case BHND_NVRAM_TYPE_STRING_ARRAY:
622
return (0);
623
default:
624
return (EFTYPE);
625
}
626
}
627
628
/**
629
* Broadcom NVRAM comma-delimited string iteration.
630
*/
631
static const void *
632
bhnd_nvram_val_bcmstr_csv_next(bhnd_nvram_val *value, const void *prev,
633
size_t *len)
634
{
635
const char *next;
636
const char *inp;
637
bhnd_nvram_type itype;
638
size_t ilen, remain;
639
char delim;
640
641
/* Fetch backing representation */
642
inp = bhnd_nvram_val_bytes(value, &ilen, &itype);
643
644
/* Fetch next value */
645
switch (itype) {
646
case BHND_NVRAM_TYPE_STRING:
647
/* Zero-length array? */
648
if (ilen == 0)
649
return (NULL);
650
651
if (prev == NULL) {
652
/* First element */
653
next = inp;
654
remain = ilen;
655
delim = ',';
656
} else {
657
/* Advance to the previous element's delimiter */
658
next = (const char *)prev + *len;
659
660
/* Did we hit the end of the string? */
661
if ((size_t)(next - inp) >= ilen)
662
return (NULL);
663
664
/* Fetch (and skip past) the delimiter */
665
delim = *next;
666
next++;
667
remain = ilen - (size_t)(next - inp);
668
669
/* Was the delimiter the final character? */
670
if (remain == 0)
671
return (NULL);
672
}
673
674
/* Parse the field value, up to the next delimiter */
675
*len = bhnd_nvram_parse_field(&next, remain, delim);
676
677
return (next);
678
679
case BHND_NVRAM_TYPE_STRING_ARRAY:
680
/* Delegate to default array iteration */
681
return (bhnd_nvram_value_array_next(inp, ilen, itype, prev,
682
len));
683
default:
684
BHND_NV_PANIC("unsupported type: %d", itype);
685
}
686
}
687
688
/**
689
* MAC address filter.
690
*/
691
static int
692
bhnd_nvram_val_bcm_macaddr_filter(const bhnd_nvram_val_fmt **fmt,
693
const void *inp, size_t ilen, bhnd_nvram_type itype)
694
{
695
switch (itype) {
696
case BHND_NVRAM_TYPE_UINT8_ARRAY:
697
return (0);
698
case BHND_NVRAM_TYPE_STRING:
699
/* Let bcm_macaddr_string format handle it */
700
*fmt = &bhnd_nvram_val_bcm_macaddr_string_fmt;
701
return (0);
702
default:
703
return (EFTYPE);
704
}
705
}
706
707
/**
708
* MAC address encoding.
709
*/
710
static int
711
bhnd_nvram_val_bcm_macaddr_encode(bhnd_nvram_val *value, void *outp,
712
size_t *olen, bhnd_nvram_type otype)
713
{
714
const void *inp;
715
bhnd_nvram_type itype;
716
size_t ilen;
717
718
/*
719
* If converting to a string (or a single-element string array),
720
* produce an octet string (00:00:...).
721
*/
722
if (bhnd_nvram_base_type(otype) == BHND_NVRAM_TYPE_STRING) {
723
return (bhnd_nvram_val_printf(value, "%[]02hhX", outp, olen,
724
":"));
725
}
726
727
/* Otherwise, use standard encoding support */
728
inp = bhnd_nvram_val_bytes(value, &ilen, &itype);
729
return (bhnd_nvram_value_coerce(inp, ilen, itype, outp, olen, otype));}
730
731
/**
732
* MAC address string filter.
733
*/
734
static int
735
bhnd_nvram_val_bcm_macaddr_string_filter(const bhnd_nvram_val_fmt **fmt,
736
const void *inp, size_t ilen, bhnd_nvram_type itype)
737
{
738
switch (itype) {
739
case BHND_NVRAM_TYPE_STRING:
740
/* Use the standard Broadcom string format implementation if
741
* the input is not an octet string. */
742
if (!bhnd_nvram_ident_octet_string(inp, ilen, NULL, NULL))
743
*fmt = &bhnd_nvram_val_bcm_string_fmt;
744
745
return (0);
746
default:
747
return (EFTYPE);
748
}
749
}
750
751
/**
752
* MAC address string octet encoding.
753
*/
754
static int
755
bhnd_nvram_val_bcm_macaddr_string_encode_elem(bhnd_nvram_val *value,
756
const void *inp, size_t ilen, void *outp, size_t *olen,
757
bhnd_nvram_type otype)
758
{
759
size_t nparsed;
760
int error;
761
762
/* If integer encoding is requested, explicitly parse our
763
* non-0x-prefixed as a base 16 integer value */
764
if (bhnd_nvram_is_int_type(otype)) {
765
error = bhnd_nvram_parse_int(inp, ilen, 16, &nparsed, outp,
766
olen, otype);
767
if (error)
768
return (error);
769
770
if (nparsed != ilen)
771
return (EFTYPE);
772
773
return (0);
774
}
775
776
/* Otherwise, use standard encoding support */
777
return (bhnd_nvram_value_coerce(inp, ilen,
778
bhnd_nvram_val_elem_type(value), outp, olen, otype));
779
}
780
781
/**
782
* MAC address string octet iteration.
783
*/
784
static const void *
785
bhnd_nvram_val_bcm_macaddr_string_next(bhnd_nvram_val *value, const void *prev,
786
size_t *len)
787
{
788
const char *next;
789
const char *str;
790
bhnd_nvram_type stype;
791
size_t slen, remain;
792
char delim;
793
794
/* Fetch backing string */
795
str = bhnd_nvram_val_bytes(value, &slen, &stype);
796
BHND_NV_ASSERT(stype == BHND_NVRAM_TYPE_STRING,
797
("unsupported type: %d", stype));
798
799
/* Zero-length array? */
800
if (slen == 0)
801
return (NULL);
802
803
if (prev == NULL) {
804
/* First element */
805
806
/* Determine delimiter */
807
if (!bhnd_nvram_ident_octet_string(str, slen, &delim, NULL)) {
808
/* Default to comma-delimited parsing */
809
delim = ',';
810
}
811
812
/* Parsing will start at the base string pointer */
813
next = str;
814
remain = slen;
815
} else {
816
/* Advance to the previous element's delimiter */
817
next = (const char *)prev + *len;
818
819
/* Did we hit the end of the string? */
820
if ((size_t)(next - str) >= slen)
821
return (NULL);
822
823
/* Fetch (and skip past) the delimiter */
824
delim = *next;
825
next++;
826
remain = slen - (size_t)(next - str);
827
828
/* Was the delimiter the final character? */
829
if (remain == 0)
830
return (NULL);
831
}
832
833
/* Parse the field value, up to the next delimiter */
834
*len = bhnd_nvram_parse_field(&next, remain, delim);
835
836
return (next);
837
}
838
839
/**
840
* Determine whether @p inp is in octet string format, consisting of a
841
* fields of two hex characters, separated with ':' or '-' delimiters.
842
*
843
* This may be used to identify MAC address octet strings
844
* (BHND_NVRAM_SFMT_MACADDR).
845
*
846
* @param inp The string to be parsed.
847
* @param ilen The length of @p inp, in bytes.
848
* @param[out] delim On success, the delimiter used by this octet
849
* string. May be set to NULL if the field
850
* delimiter is not desired.
851
* @param[out] nelem On success, the number of fields in this
852
* octet string. May be set to NULL if the field
853
* count is not desired.
854
*
855
*
856
* @retval true if @p inp is a valid octet string
857
* @retval false if @p inp is not a valid octet string.
858
*/
859
static bool
860
bhnd_nvram_ident_octet_string(const char *inp, size_t ilen, char *delim,
861
size_t *nelem)
862
{
863
size_t elem_count;
864
size_t max_elem_count, min_elem_count;
865
size_t field_count;
866
char idelim;
867
868
field_count = 0;
869
870
/* Require exactly two digits. If we relax this, there is room
871
* for ambiguity with signed integers and the '-' delimiter */
872
min_elem_count = 2;
873
max_elem_count = 2;
874
875
/* Identify the delimiter used. The standard delimiter for MAC
876
* addresses is ':', but some earlier NVRAM formats may use '-' */
877
for (const char *d = ":-";; d++) {
878
const char *loc;
879
880
/* No delimiter found, not an octet string */
881
if (*d == '\0')
882
return (false);
883
884
/* Look for the delimiter */
885
if ((loc = memchr(inp, *d, ilen)) == NULL)
886
continue;
887
888
/* Delimiter found */
889
idelim = *loc;
890
break;
891
}
892
893
/* To disambiguate from signed integers, if the delimiter is "-",
894
* the octets must be exactly 2 chars each */
895
if (idelim == '-')
896
min_elem_count = 2;
897
898
/* String must be composed of individual octets (zero or more hex
899
* digits) separated by our delimiter. */
900
elem_count = 0;
901
for (const char *p = inp; (size_t)(p - inp) < ilen; p++) {
902
switch (*p) {
903
case ':':
904
case '-':
905
case '\0':
906
/* Hit a delim character; all delims must match
907
* the first delimiter used */
908
if (*p != '\0' && *p != idelim)
909
return (false);
910
911
/* Must have parsed at least min_elem_count digits */
912
if (elem_count < min_elem_count)
913
return (false);
914
915
/* Reset element count */
916
elem_count = 0;
917
918
/* Bump field count */
919
field_count++;
920
break;
921
default:
922
/* More than maximum number of hex digits? */
923
if (elem_count >= max_elem_count)
924
return (false);
925
926
/* Octet values must be hex digits */
927
if (!bhnd_nv_isxdigit(*p))
928
return (false);
929
930
elem_count++;
931
break;
932
}
933
}
934
935
if (delim != NULL)
936
*delim = idelim;
937
938
if (nelem != NULL)
939
*nelem = field_count;
940
941
return (true);
942
}
943
944
/**
945
* Determine whether @p inp is in hexadecimal, octal, or decimal string
946
* format.
947
*
948
* - A @p str may be prefixed with a single optional '+' or '-' sign denoting
949
* signedness.
950
* - A hexadecimal @p str may include an '0x' or '0X' prefix, denoting that a
951
* base 16 integer follows.
952
* - An octal @p str may include a '0' prefix, denoting that an octal integer
953
* follows.
954
*
955
* @param inp The string to be parsed.
956
* @param ilen The length of @p inp, in bytes.
957
* @param base The input string's base (2-36), or 0.
958
* @param[out] obase On success, will be set to the base of the parsed
959
* integer. May be set to NULL if the base is not
960
* desired.
961
*
962
* @retval true if @p inp is a valid number string
963
* @retval false if @p inp is not a valid number string.
964
* @retval false if @p base is invalid.
965
*/
966
static bool
967
bhnd_nvram_ident_num_string(const char *inp, size_t ilen, u_int base,
968
u_int *obase)
969
{
970
size_t nbytes, ndigits;
971
972
nbytes = 0;
973
ndigits = 0;
974
975
/* Parse and skip sign */
976
if (nbytes >= ilen)
977
return (false);
978
979
if (inp[nbytes] == '-' || inp[nbytes] == '+')
980
nbytes++;
981
982
/* Truncated after sign character? */
983
if (nbytes == ilen)
984
return (false);
985
986
/* Identify (or validate) hex base, skipping 0x/0X prefix */
987
if (base == 16 || base == 0) {
988
/* Check for (and skip) 0x/0X prefix */
989
if (ilen - nbytes >= 2 && inp[nbytes] == '0' &&
990
(inp[nbytes+1] == 'x' || inp[nbytes+1] == 'X'))
991
{
992
base = 16;
993
nbytes += 2;
994
}
995
}
996
997
/* Truncated after hex prefix? */
998
if (nbytes == ilen)
999
return (false);
1000
1001
/* Differentiate decimal/octal by looking for a leading 0 */
1002
if (base == 0) {
1003
if (inp[nbytes] == '0') {
1004
base = 8;
1005
} else {
1006
base = 10;
1007
}
1008
}
1009
1010
/* Consume and validate all remaining digit characters */
1011
for (; nbytes < ilen; nbytes++) {
1012
u_int carry;
1013
char c;
1014
1015
/* Parse carry value */
1016
c = inp[nbytes];
1017
if (bhnd_nv_isdigit(c)) {
1018
carry = c - '0';
1019
} else if (bhnd_nv_isxdigit(c)) {
1020
if (bhnd_nv_isupper(c))
1021
carry = (c - 'A') + 10;
1022
else
1023
carry = (c - 'a') + 10;
1024
} else {
1025
/* Hit a non-digit character */
1026
return (false);
1027
}
1028
1029
/* If carry is outside the base, it's not a valid digit
1030
* in the current parse context; consider it a non-digit
1031
* character */
1032
if (carry >= base)
1033
return (false);
1034
1035
/* Increment parsed digit count */
1036
ndigits++;
1037
}
1038
1039
/* Empty integer string? */
1040
if (ndigits == 0)
1041
return (false);
1042
1043
/* Valid integer -- provide the base and return */
1044
if (obase != NULL)
1045
*obase = base;
1046
return (true);
1047
}
1048
1049