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_subr.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
#ifdef _KERNEL
33
34
#include <sys/systm.h>
35
36
#else /* !_KERNEL */
37
38
#include <errno.h>
39
#include <string.h>
40
41
#endif /* _KERNEL */
42
43
#include "bhnd_nvram_private.h"
44
#include "bhnd_nvram_valuevar.h"
45
46
/**
47
* Validate the alignment of a value of @p type.
48
*
49
* @param inp The value data.
50
* @param ilen The value length, in bytes.
51
* @param itype The value type.
52
*
53
* @retval 0 success
54
* @retval EFTYPE if @p type is not an array type, and @p len is not
55
* equal to the size of a single element of @p type.
56
* @retval EFAULT if @p data is not correctly aligned to the required
57
* host alignment.
58
* @retval EFAULT if @p len is not aligned to the @p type width.
59
*/
60
int
61
bhnd_nvram_value_check_aligned(const void *inp, size_t ilen,
62
bhnd_nvram_type itype)
63
{
64
size_t align, width;
65
66
/* As a special case, NULL values have no alignment, but must
67
* always have a length of zero */
68
if (itype == BHND_NVRAM_TYPE_NULL) {
69
if (ilen != 0)
70
return (EFAULT);
71
72
return (0);
73
}
74
75
/* Check pointer alignment against the required host alignment */
76
align = bhnd_nvram_type_host_align(itype);
77
BHND_NV_ASSERT(align != 0, ("invalid zero alignment"));
78
if ((uintptr_t)inp % align != 0)
79
return (EFAULT);
80
81
/* If type is not fixed width, nothing else to check */
82
width = bhnd_nvram_type_width(itype);
83
if (width == 0)
84
return (0);
85
86
/* Length must be aligned to the element width */
87
if (ilen % width != 0)
88
return (EFAULT);
89
90
/* If the type is not an array type, the length must be equal to the
91
* size of a single element of @p type. */
92
if (!bhnd_nvram_is_array_type(itype) && ilen != width)
93
return (EFTYPE);
94
95
return (0);
96
}
97
98
/**
99
* Calculate the number of elements represented by a value of @p ilen bytes
100
* with @p itype.
101
*
102
* @param inp The value data.
103
* @param ilen The value length.
104
* @param itype The value type.
105
* @param[out] nelem On success, the number of elements.
106
*
107
* @retval 0 success
108
* @retval EINVAL if @p inp is NULL and the element count of @p itype
109
* cannot be determined without parsing the value data.
110
* @retval EFTYPE if @p itype is not an array type, and @p ilen is not
111
* equal to the size of a single element of @p itype.
112
* @retval EFAULT if @p ilen is not correctly aligned for elements of
113
* @p itype.
114
*/
115
int
116
bhnd_nvram_value_nelem(const void *inp, size_t ilen, bhnd_nvram_type itype,
117
size_t *nelem)
118
{
119
int error;
120
121
BHND_NV_ASSERT(inp != NULL, ("NULL inp"));
122
123
/* Check alignment */
124
if ((error = bhnd_nvram_value_check_aligned(inp, ilen, itype)))
125
return (error);
126
127
switch (itype) {
128
case BHND_NVRAM_TYPE_DATA:
129
/* Always exactly one element */
130
*nelem = 1;
131
return (0);
132
133
case BHND_NVRAM_TYPE_NULL:
134
/* Must be zero length */
135
if (ilen != 0)
136
return (EFAULT);
137
138
/* Always exactly one element */
139
*nelem = 1;
140
return (0);
141
142
case BHND_NVRAM_TYPE_STRING:
143
/* Always exactly one element */
144
*nelem = 1;
145
return (0);
146
147
case BHND_NVRAM_TYPE_STRING_ARRAY: {
148
const char *p;
149
size_t nleft;
150
151
/* Iterate over the NUL-terminated strings to calculate
152
* total element count */
153
p = inp;
154
nleft = ilen;
155
*nelem = 0;
156
while (nleft > 0) {
157
size_t slen;
158
159
/* Increment element count */
160
(*nelem)++;
161
162
/* Determine string length */
163
slen = strnlen(p, nleft);
164
nleft -= slen;
165
166
/* Advance input */
167
p += slen;
168
169
/* Account for trailing NUL, if we haven't hit the end
170
* of the input */
171
if (nleft > 0) {
172
nleft--;
173
p++;
174
}
175
}
176
177
return (0);
178
}
179
180
case BHND_NVRAM_TYPE_UINT8_ARRAY:
181
case BHND_NVRAM_TYPE_UINT16_ARRAY:
182
case BHND_NVRAM_TYPE_UINT32_ARRAY:
183
case BHND_NVRAM_TYPE_UINT64_ARRAY:
184
case BHND_NVRAM_TYPE_INT8_ARRAY:
185
case BHND_NVRAM_TYPE_INT16_ARRAY:
186
case BHND_NVRAM_TYPE_INT32_ARRAY:
187
case BHND_NVRAM_TYPE_INT64_ARRAY:
188
case BHND_NVRAM_TYPE_CHAR_ARRAY:
189
case BHND_NVRAM_TYPE_BOOL_ARRAY: {
190
size_t width = bhnd_nvram_type_width(itype);
191
BHND_NV_ASSERT(width != 0, ("invalid width"));
192
193
*nelem = ilen / width;
194
return (0);
195
}
196
197
case BHND_NVRAM_TYPE_INT8:
198
case BHND_NVRAM_TYPE_UINT8:
199
case BHND_NVRAM_TYPE_CHAR:
200
case BHND_NVRAM_TYPE_INT16:
201
case BHND_NVRAM_TYPE_UINT16:
202
case BHND_NVRAM_TYPE_INT32:
203
case BHND_NVRAM_TYPE_UINT32:
204
case BHND_NVRAM_TYPE_INT64:
205
case BHND_NVRAM_TYPE_UINT64:
206
case BHND_NVRAM_TYPE_BOOL:
207
/* Length must be equal to the size of exactly one
208
* element (arrays can represent zero elements -- non-array
209
* types cannot) */
210
if (ilen != bhnd_nvram_type_width(itype))
211
return (EFTYPE);
212
*nelem = 1;
213
return (0);
214
}
215
216
/* Quiesce gcc4.2 */
217
BHND_NV_PANIC("bhnd nvram type %u unknown", itype);
218
}
219
220
/**
221
* Return the size, in bytes, of a value of @p itype with @p nelem elements.
222
*
223
* @param inp The actual data to be queried, or NULL if unknown. If
224
* NULL and the base type is not a fixed width type
225
* (e.g. BHND_NVRAM_TYPE_STRING), 0 will be returned.
226
* @param ilen The size of @p inp, in bytes, or 0 if @p inp is NULL.
227
* @param itype The value type.
228
* @param nelem The number of elements. If @p itype is not an array
229
* type, this value must be 1.
230
*
231
* @retval 0 If @p itype has a variable width, and @p inp is NULL.
232
* @retval 0 If a @p nelem value greater than 1 is provided for a
233
* non-array @p itype.
234
* @retval 0 If a @p nelem value of 0 is provided.
235
* @retval 0 If the result would exceed the maximum value
236
* representable by size_t.
237
* @retval 0 If @p itype is BHND_NVRAM_TYPE_NULL.
238
* @retval non-zero The size, in bytes, of @p itype with @p nelem elements.
239
*/
240
size_t
241
bhnd_nvram_value_size(const void *inp, size_t ilen, bhnd_nvram_type itype,
242
size_t nelem)
243
{
244
/* If nelem 0, nothing to do */
245
if (nelem == 0)
246
return (0);
247
248
/* Non-array types must have an nelem value of 1 */
249
if (!bhnd_nvram_is_array_type(itype) && nelem != 1)
250
return (0);
251
252
switch (itype) {
253
case BHND_NVRAM_TYPE_UINT8_ARRAY:
254
case BHND_NVRAM_TYPE_UINT16_ARRAY:
255
case BHND_NVRAM_TYPE_UINT32_ARRAY:
256
case BHND_NVRAM_TYPE_UINT64_ARRAY:
257
case BHND_NVRAM_TYPE_INT8_ARRAY:
258
case BHND_NVRAM_TYPE_INT16_ARRAY:
259
case BHND_NVRAM_TYPE_INT32_ARRAY:
260
case BHND_NVRAM_TYPE_INT64_ARRAY:
261
case BHND_NVRAM_TYPE_CHAR_ARRAY:
262
case BHND_NVRAM_TYPE_BOOL_ARRAY:{
263
size_t width;
264
265
width = bhnd_nvram_type_width(itype);
266
267
/* Would nelem * width overflow? */
268
if (SIZE_MAX / nelem < width) {
269
BHND_NV_LOG("cannot represent size %s[%zu]\n",
270
bhnd_nvram_type_name(bhnd_nvram_base_type(itype)),
271
nelem);
272
return (0);
273
}
274
275
return (nelem * width);
276
}
277
278
case BHND_NVRAM_TYPE_STRING_ARRAY: {
279
const char *p;
280
size_t total_size;
281
282
if (inp == NULL)
283
return (0);
284
285
/* Iterate over the NUL-terminated strings to calculate
286
* total byte length */
287
p = inp;
288
total_size = 0;
289
for (size_t i = 0; i < nelem; i++) {
290
size_t elem_size;
291
292
elem_size = strnlen(p, ilen - total_size);
293
p += elem_size;
294
295
/* Check for (and skip) terminating NUL */
296
if (total_size < ilen && *p == '\0') {
297
elem_size++;
298
p++;
299
}
300
301
/* Would total_size + elem_size overflow?
302
*
303
* A memory range larger than SIZE_MAX shouldn't be,
304
* possible, but include the check for completeness */
305
if (SIZE_MAX - total_size < elem_size)
306
return (0);
307
308
total_size += elem_size;
309
}
310
311
return (total_size);
312
}
313
314
case BHND_NVRAM_TYPE_STRING: {
315
size_t size;
316
317
if (inp == NULL)
318
return (0);
319
320
/* Find length */
321
size = strnlen(inp, ilen);
322
323
/* Is there a terminating NUL, or did we just hit the
324
* end of the string input */
325
if (size < ilen)
326
size++;
327
328
return (size);
329
}
330
331
case BHND_NVRAM_TYPE_NULL:
332
return (0);
333
334
case BHND_NVRAM_TYPE_DATA:
335
if (inp == NULL)
336
return (0);
337
338
return (ilen);
339
340
case BHND_NVRAM_TYPE_BOOL:
341
return (sizeof(bhnd_nvram_bool_t));
342
343
case BHND_NVRAM_TYPE_INT8:
344
case BHND_NVRAM_TYPE_UINT8:
345
case BHND_NVRAM_TYPE_CHAR:
346
return (sizeof(uint8_t));
347
348
case BHND_NVRAM_TYPE_INT16:
349
case BHND_NVRAM_TYPE_UINT16:
350
return (sizeof(uint16_t));
351
352
case BHND_NVRAM_TYPE_INT32:
353
case BHND_NVRAM_TYPE_UINT32:
354
return (sizeof(uint32_t));
355
356
case BHND_NVRAM_TYPE_UINT64:
357
case BHND_NVRAM_TYPE_INT64:
358
return (sizeof(uint64_t));
359
}
360
361
/* Quiesce gcc4.2 */
362
BHND_NV_PANIC("bhnd nvram type %u unknown", itype);
363
}
364
365
/**
366
* Format a string representation of @p inp using @p fmt, with, writing the
367
* result to @p outp.
368
*
369
* Refer to bhnd_nvram_val_vprintf() for full format string documentation.
370
*
371
* @param fmt The format string.
372
* @param inp The value to be formatted.
373
* @param ilen The size of @p inp, in bytes.
374
* @param itype The type of @p inp.
375
* @param[out] outp On success, the string value will be written to
376
* this buffer. This argment may be NULL if the
377
* value is not desired.
378
* @param[in,out] olen The capacity of @p outp. On success, will be set
379
* to the actual size of the formatted string.
380
*
381
* @retval 0 success
382
* @retval EINVAL If @p fmt contains unrecognized format string
383
* specifiers.
384
* @retval ENOMEM If the @p outp is non-NULL, and the provided @p olen
385
* is too small to hold the encoded value.
386
* @retval EFTYPE If value coercion from @p inp to a string value via
387
* @p fmt is unsupported.
388
* @retval ERANGE If value coercion of @p value would overflow (or
389
* underflow) the representation defined by @p fmt.
390
*/
391
int
392
bhnd_nvram_value_printf(const char *fmt, const void *inp, size_t ilen,
393
bhnd_nvram_type itype, char *outp, size_t *olen, ...)
394
{
395
va_list ap;
396
int error;
397
398
va_start(ap, olen);
399
error = bhnd_nvram_value_vprintf(fmt, inp, ilen, itype, outp, olen, ap);
400
va_end(ap);
401
402
return (error);
403
}
404
405
/**
406
* Format a string representation of @p inp using @p fmt, with, writing the
407
* result to @p outp.
408
*
409
* Refer to bhnd_nvram_val_vprintf() for full format string documentation.
410
*
411
* @param fmt The format string.
412
* @param inp The value to be formatted.
413
* @param ilen The size of @p inp, in bytes.
414
* @param itype The type of @p inp.
415
* @param[out] outp On success, the string value will be written to
416
* this buffer. This argment may be NULL if the
417
* value is not desired.
418
* @param[in,out] olen The capacity of @p outp. On success, will be set
419
* to the actual size of the formatted string.
420
* @param ap Argument list.
421
*
422
* @retval 0 success
423
* @retval EINVAL If @p fmt contains unrecognized format string
424
* specifiers.
425
* @retval ENOMEM If the @p outp is non-NULL, and the provided @p olen
426
* is too small to hold the encoded value.
427
* @retval EFTYPE If value coercion from @p inp to a string value via
428
* @p fmt is unsupported.
429
* @retval ERANGE If value coercion of @p value would overflow (or
430
* underflow) the representation defined by @p fmt.
431
*/
432
int
433
bhnd_nvram_value_vprintf(const char *fmt, const void *inp, size_t ilen,
434
bhnd_nvram_type itype, char *outp, size_t *olen, va_list ap)
435
{
436
bhnd_nvram_val val;
437
int error;
438
439
/* Map input buffer as a value instance */
440
error = bhnd_nvram_val_init(&val, NULL, inp, ilen, itype,
441
BHND_NVRAM_VAL_BORROW_DATA);
442
if (error)
443
return (error);
444
445
/* Attempt to format the value */
446
error = bhnd_nvram_val_vprintf(&val, fmt, outp, olen, ap);
447
448
/* Clean up */
449
bhnd_nvram_val_release(&val);
450
return (error);
451
}
452
453
/**
454
* Iterate over all elements in @p inp.
455
*
456
* @param inp The value to be iterated.
457
* @param ilen The size, in bytes, of @p inp.
458
* @param itype The data type of @p inp.
459
* @param prev The value previously returned by
460
* bhnd_nvram_value_array_next(), or NULL to begin
461
* iteration.
462
* @param[in,out] olen If @p prev is non-NULL, @p olen must be a
463
* pointer to the length previously returned by
464
* bhnd_nvram_value_array_next(). On success, will
465
* be set to the next element's length, in bytes.
466
*
467
* @retval non-NULL A borrowed reference to the next element of @p inp.
468
* @retval NULL If the end of the array is reached.
469
*/
470
const void *
471
bhnd_nvram_value_array_next(const void *inp, size_t ilen, bhnd_nvram_type itype,
472
const void *prev, size_t *olen)
473
{
474
const u_char *next;
475
size_t offset;
476
477
/* Handle first element */
478
if (prev == NULL) {
479
/* Zero-length array? */
480
if (ilen == 0)
481
return (NULL);
482
483
*olen = bhnd_nvram_value_size(inp, ilen, itype, 1);
484
return (inp);
485
}
486
487
/* Advance to next element */
488
BHND_NV_ASSERT(prev >= (const void *)inp, ("invalid cookiep"));
489
next = (const u_char *)prev + *olen;
490
offset = (size_t)(next - (const u_char *)inp);
491
492
if (offset >= ilen) {
493
/* Hit end of the array */
494
return (NULL);
495
}
496
497
/* Determine element size */
498
*olen = bhnd_nvram_value_size(next, ilen - offset, itype, 1);
499
if (ilen - offset < *olen) {
500
BHND_NV_LOG("short element of type %s -- misaligned "
501
"representation", bhnd_nvram_type_name(itype));
502
return (NULL);
503
}
504
505
return (next);
506
}
507
508
/**
509
* Coerce value @p inp of type @p itype to @p otype, writing the
510
* result to @p outp.
511
*
512
* @param inp The value to be coerced.
513
* @param ilen The size of @p inp, in bytes.
514
* @param itype The base data type of @p inp.
515
* @param[out] outp On success, the value will be written to this
516
* buffer. This argment may be NULL if the value
517
* is not desired.
518
* @param[in,out] olen The capacity of @p outp. On success, will be set
519
* to the actual size of the requested value.
520
* @param otype The data type to be written to @p outp.
521
*
522
* @retval 0 success
523
* @retval ENOMEM If @p outp is non-NULL and a buffer of @p olen is too
524
* small to hold the requested value.
525
* @retval EFTYPE If the variable data cannot be coerced to @p otype.
526
* @retval ERANGE If value coercion would overflow @p otype.
527
*/
528
int
529
bhnd_nvram_value_coerce(const void *inp, size_t ilen, bhnd_nvram_type itype,
530
void *outp, size_t *olen, bhnd_nvram_type otype)
531
{
532
bhnd_nvram_val val;
533
int error;
534
535
/* Wrap input buffer in a value instance */
536
error = bhnd_nvram_val_init(&val, NULL, inp, ilen,
537
itype, BHND_NVRAM_VAL_BORROW_DATA|BHND_NVRAM_VAL_FIXED);
538
if (error)
539
return (error);
540
541
/* Try to encode as requested type */
542
error = bhnd_nvram_val_encode(&val, outp, olen, otype);
543
544
/* Clean up and return error */
545
bhnd_nvram_val_release(&val);
546
return (error);
547
}
548
549