nv.c revision 214282
1204076Spjd/*- 2204076Spjd * Copyright (c) 2009-2010 The FreeBSD Foundation 3204076Spjd * All rights reserved. 4204076Spjd * 5204076Spjd * This software was developed by Pawel Jakub Dawidek under sponsorship from 6204076Spjd * the FreeBSD Foundation. 7204076Spjd * 8204076Spjd * Redistribution and use in source and binary forms, with or without 9204076Spjd * modification, are permitted provided that the following conditions 10204076Spjd * are met: 11204076Spjd * 1. Redistributions of source code must retain the above copyright 12204076Spjd * notice, this list of conditions and the following disclaimer. 13204076Spjd * 2. Redistributions in binary form must reproduce the above copyright 14204076Spjd * notice, this list of conditions and the following disclaimer in the 15204076Spjd * documentation and/or other materials provided with the distribution. 16204076Spjd * 17204076Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 18204076Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19204076Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20204076Spjd * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 21204076Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22204076Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23204076Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24204076Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25204076Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26204076Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27204076Spjd * SUCH DAMAGE. 28204076Spjd */ 29204076Spjd 30204076Spjd#include <sys/cdefs.h> 31204076Spjd__FBSDID("$FreeBSD: head/sbin/hastd/nv.c 214282 2010-10-24 17:22:34Z pjd $"); 32204076Spjd 33204076Spjd#include <sys/param.h> 34204076Spjd#include <sys/endian.h> 35204076Spjd 36204076Spjd#include <assert.h> 37204076Spjd#include <bitstring.h> 38204076Spjd#include <errno.h> 39204076Spjd#include <stdarg.h> 40204076Spjd#include <stdbool.h> 41204076Spjd#include <stdint.h> 42204076Spjd#include <stdlib.h> 43204076Spjd#include <string.h> 44204076Spjd#include <unistd.h> 45204076Spjd 46204076Spjd#include <ebuf.h> 47204076Spjd#include <nv.h> 48204076Spjd 49214282Spjd#define NV_TYPE_INT8 1 50214282Spjd#define NV_TYPE_UINT8 2 51214282Spjd#define NV_TYPE_INT16 3 52214282Spjd#define NV_TYPE_UINT16 4 53214282Spjd#define NV_TYPE_INT32 5 54214282Spjd#define NV_TYPE_UINT32 6 55214282Spjd#define NV_TYPE_INT64 7 56214282Spjd#define NV_TYPE_UINT64 8 57214282Spjd#define NV_TYPE_INT8_ARRAY 9 58214282Spjd#define NV_TYPE_UINT8_ARRAY 10 59214282Spjd#define NV_TYPE_INT16_ARRAY 11 60214282Spjd#define NV_TYPE_UINT16_ARRAY 12 61214282Spjd#define NV_TYPE_INT32_ARRAY 13 62214282Spjd#define NV_TYPE_UINT32_ARRAY 14 63214282Spjd#define NV_TYPE_INT64_ARRAY 15 64214282Spjd#define NV_TYPE_UINT64_ARRAY 16 65214282Spjd#define NV_TYPE_STRING 17 66214282Spjd 67214282Spjd#define NV_TYPE_MASK 0x7f 68214282Spjd#define NV_TYPE_FIRST NV_TYPE_INT8 69214282Spjd#define NV_TYPE_LAST NV_TYPE_STRING 70214282Spjd 71214282Spjd#define NV_ORDER_NETWORK 0x00 72214282Spjd#define NV_ORDER_HOST 0x80 73214282Spjd 74214282Spjd#define NV_ORDER_MASK 0x80 75214282Spjd 76204076Spjd#define NV_MAGIC 0xaea1e 77204076Spjdstruct nv { 78204076Spjd int nv_magic; 79204076Spjd int nv_error; 80204076Spjd struct ebuf *nv_ebuf; 81204076Spjd}; 82204076Spjd 83204076Spjdstruct nvhdr { 84204076Spjd uint8_t nvh_type; 85204076Spjd uint8_t nvh_namesize; 86204076Spjd uint32_t nvh_dsize; 87204076Spjd char nvh_name[0]; 88204076Spjd} __packed; 89204076Spjd#define NVH_DATA(nvh) ((unsigned char *)nvh + NVH_HSIZE(nvh)) 90204076Spjd#define NVH_HSIZE(nvh) \ 91204076Spjd (sizeof(struct nvhdr) + roundup2((nvh)->nvh_namesize, 8)) 92204076Spjd#define NVH_DSIZE(nvh) \ 93204076Spjd (((nvh)->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST ? \ 94204076Spjd (nvh)->nvh_dsize : \ 95204076Spjd le32toh((nvh)->nvh_dsize)) 96204076Spjd#define NVH_SIZE(nvh) (NVH_HSIZE(nvh) + roundup2(NVH_DSIZE(nvh), 8)) 97204076Spjd 98204076Spjd#define NV_CHECK(nv) do { \ 99204076Spjd assert((nv) != NULL); \ 100204076Spjd assert((nv)->nv_magic == NV_MAGIC); \ 101204076Spjd} while (0) 102204076Spjd 103204076Spjdstatic void nv_add(struct nv *nv, const unsigned char *value, size_t vsize, 104204076Spjd int type, const char *name); 105204076Spjdstatic void nv_addv(struct nv *nv, const unsigned char *value, size_t vsize, 106204076Spjd int type, const char *namefmt, va_list nameap); 107204076Spjdstatic struct nvhdr *nv_find(struct nv *nv, int type, const char *namefmt, 108204076Spjd va_list nameap); 109204076Spjdstatic void nv_swap(struct nvhdr *nvh, bool tohost); 110204076Spjd 111204076Spjd/* 112204076Spjd * Allocate and initialize new nv structure. 113204076Spjd * Return NULL in case of malloc(3) failure. 114204076Spjd */ 115204076Spjdstruct nv * 116204076Spjdnv_alloc(void) 117204076Spjd{ 118204076Spjd struct nv *nv; 119204076Spjd 120204076Spjd nv = malloc(sizeof(*nv)); 121204076Spjd if (nv == NULL) 122204076Spjd return (NULL); 123204076Spjd nv->nv_ebuf = ebuf_alloc(0); 124204076Spjd if (nv->nv_ebuf == NULL) { 125204076Spjd free(nv); 126204076Spjd return (NULL); 127204076Spjd } 128204076Spjd nv->nv_error = 0; 129204076Spjd nv->nv_magic = NV_MAGIC; 130204076Spjd return (nv); 131204076Spjd} 132204076Spjd 133204076Spjd/* 134204076Spjd * Free the given nv structure. 135204076Spjd */ 136204076Spjdvoid 137204076Spjdnv_free(struct nv *nv) 138204076Spjd{ 139204076Spjd 140204076Spjd if (nv == NULL) 141204076Spjd return; 142204076Spjd 143204076Spjd NV_CHECK(nv); 144204076Spjd 145204076Spjd nv->nv_magic = 0; 146204076Spjd ebuf_free(nv->nv_ebuf); 147204076Spjd free(nv); 148204076Spjd} 149204076Spjd 150204076Spjd/* 151204076Spjd * Return error for the given nv structure. 152204076Spjd */ 153204076Spjdint 154204076Spjdnv_error(const struct nv *nv) 155204076Spjd{ 156204076Spjd 157204076Spjd if (nv == NULL) 158204076Spjd return (ENOMEM); 159204076Spjd 160204076Spjd NV_CHECK(nv); 161204076Spjd 162204076Spjd return (nv->nv_error); 163204076Spjd} 164204076Spjd 165204076Spjd/* 166204076Spjd * Set error for the given nv structure and return previous error. 167204076Spjd */ 168204076Spjdint 169204076Spjdnv_set_error(struct nv *nv, int error) 170204076Spjd{ 171204076Spjd int preverr; 172204076Spjd 173204076Spjd if (nv == NULL) 174204076Spjd return (ENOMEM); 175204076Spjd 176204076Spjd NV_CHECK(nv); 177204076Spjd 178204076Spjd preverr = nv->nv_error; 179204076Spjd nv->nv_error = error; 180204076Spjd return (preverr); 181204076Spjd} 182204076Spjd 183204076Spjd/* 184204076Spjd * Validate correctness of the entire nv structure and all its elements. 185204076Spjd * If extrap is not NULL, store number of extra bytes at the end of the buffer. 186204076Spjd */ 187204076Spjdint 188204076Spjdnv_validate(struct nv *nv, size_t *extrap) 189204076Spjd{ 190204076Spjd struct nvhdr *nvh; 191204076Spjd unsigned char *data, *ptr; 192204076Spjd size_t dsize, size, vsize; 193204076Spjd int error; 194204076Spjd 195204076Spjd if (nv == NULL) { 196204076Spjd errno = ENOMEM; 197204076Spjd return (-1); 198204076Spjd } 199204076Spjd 200204076Spjd NV_CHECK(nv); 201204076Spjd assert(nv->nv_error == 0); 202204076Spjd 203204076Spjd /* TODO: Check that names are unique? */ 204204076Spjd 205204076Spjd error = 0; 206204076Spjd ptr = ebuf_data(nv->nv_ebuf, &size); 207204076Spjd while (size > 0) { 208204076Spjd /* 209204076Spjd * Zeros at the end of the buffer are acceptable. 210204076Spjd */ 211204076Spjd if (ptr[0] == '\0') 212204076Spjd break; 213204076Spjd /* 214204076Spjd * Minimum size at this point is size of nvhdr structure, one 215204076Spjd * character long name plus terminating '\0'. 216204076Spjd */ 217204076Spjd if (size < sizeof(*nvh) + 2) { 218204076Spjd error = EINVAL; 219204076Spjd break; 220204076Spjd } 221204076Spjd nvh = (struct nvhdr *)ptr; 222204076Spjd if (size < NVH_HSIZE(nvh)) { 223204076Spjd error = EINVAL; 224204076Spjd break; 225204076Spjd } 226204076Spjd if (nvh->nvh_name[nvh->nvh_namesize - 1] != '\0') { 227204076Spjd error = EINVAL; 228204076Spjd break; 229204076Spjd } 230204076Spjd if (strlen(nvh->nvh_name) != 231204076Spjd (size_t)(nvh->nvh_namesize - 1)) { 232204076Spjd error = EINVAL; 233204076Spjd break; 234204076Spjd } 235204076Spjd if ((nvh->nvh_type & NV_TYPE_MASK) < NV_TYPE_FIRST || 236204076Spjd (nvh->nvh_type & NV_TYPE_MASK) > NV_TYPE_LAST) { 237204076Spjd error = EINVAL; 238204076Spjd break; 239204076Spjd } 240204076Spjd dsize = NVH_DSIZE(nvh); 241204076Spjd if (dsize == 0) { 242204076Spjd error = EINVAL; 243204076Spjd break; 244204076Spjd } 245204076Spjd if (size < NVH_SIZE(nvh)) { 246204076Spjd error = EINVAL; 247204076Spjd break; 248204076Spjd } 249204076Spjd vsize = 0; 250204076Spjd switch (nvh->nvh_type & NV_TYPE_MASK) { 251204076Spjd case NV_TYPE_INT8: 252204076Spjd case NV_TYPE_UINT8: 253204076Spjd if (vsize == 0) 254204076Spjd vsize = 1; 255204076Spjd /* FALLTHOUGH */ 256204076Spjd case NV_TYPE_INT16: 257204076Spjd case NV_TYPE_UINT16: 258204076Spjd if (vsize == 0) 259204076Spjd vsize = 2; 260204076Spjd /* FALLTHOUGH */ 261204076Spjd case NV_TYPE_INT32: 262204076Spjd case NV_TYPE_UINT32: 263204076Spjd if (vsize == 0) 264204076Spjd vsize = 4; 265204076Spjd /* FALLTHOUGH */ 266204076Spjd case NV_TYPE_INT64: 267204076Spjd case NV_TYPE_UINT64: 268204076Spjd if (vsize == 0) 269204076Spjd vsize = 8; 270204076Spjd if (dsize != vsize) { 271204076Spjd error = EINVAL; 272204076Spjd break; 273204076Spjd } 274204076Spjd break; 275204076Spjd case NV_TYPE_INT8_ARRAY: 276204076Spjd case NV_TYPE_UINT8_ARRAY: 277204076Spjd break; 278204076Spjd case NV_TYPE_INT16_ARRAY: 279204076Spjd case NV_TYPE_UINT16_ARRAY: 280204076Spjd if (vsize == 0) 281204076Spjd vsize = 2; 282204076Spjd /* FALLTHOUGH */ 283204076Spjd case NV_TYPE_INT32_ARRAY: 284204076Spjd case NV_TYPE_UINT32_ARRAY: 285204076Spjd if (vsize == 0) 286204076Spjd vsize = 4; 287204076Spjd /* FALLTHOUGH */ 288204076Spjd case NV_TYPE_INT64_ARRAY: 289204076Spjd case NV_TYPE_UINT64_ARRAY: 290204076Spjd if (vsize == 0) 291204076Spjd vsize = 8; 292204076Spjd if ((dsize % vsize) != 0) { 293204076Spjd error = EINVAL; 294204076Spjd break; 295204076Spjd } 296204076Spjd break; 297204076Spjd case NV_TYPE_STRING: 298204076Spjd data = NVH_DATA(nvh); 299204076Spjd if (data[dsize - 1] != '\0') { 300204076Spjd error = EINVAL; 301204076Spjd break; 302204076Spjd } 303204076Spjd if (strlen((char *)data) != dsize - 1) { 304204076Spjd error = EINVAL; 305204076Spjd break; 306204076Spjd } 307204076Spjd break; 308204076Spjd default: 309204076Spjd assert(!"invalid condition"); 310204076Spjd } 311204076Spjd if (error != 0) 312204076Spjd break; 313204076Spjd ptr += NVH_SIZE(nvh); 314204076Spjd size -= NVH_SIZE(nvh); 315204076Spjd } 316204076Spjd if (error != 0) { 317204076Spjd errno = error; 318204076Spjd if (nv->nv_error == 0) 319204076Spjd nv->nv_error = error; 320204076Spjd return (-1); 321204076Spjd } 322204076Spjd if (extrap != NULL) 323204076Spjd *extrap = size; 324204076Spjd return (0); 325204076Spjd} 326204076Spjd 327204076Spjd/* 328204076Spjd * Convert the given nv structure to network byte order and return ebuf 329204076Spjd * structure. 330204076Spjd */ 331204076Spjdstruct ebuf * 332204076Spjdnv_hton(struct nv *nv) 333204076Spjd{ 334204076Spjd struct nvhdr *nvh; 335204076Spjd unsigned char *ptr; 336204076Spjd size_t size; 337204076Spjd 338204076Spjd NV_CHECK(nv); 339204076Spjd assert(nv->nv_error == 0); 340204076Spjd 341204076Spjd ptr = ebuf_data(nv->nv_ebuf, &size); 342204076Spjd while (size > 0) { 343204076Spjd /* 344204076Spjd * Minimum size at this point is size of nvhdr structure, 345204076Spjd * one character long name plus terminating '\0'. 346204076Spjd */ 347204076Spjd assert(size >= sizeof(*nvh) + 2); 348204076Spjd nvh = (struct nvhdr *)ptr; 349204076Spjd assert(NVH_SIZE(nvh) <= size); 350204076Spjd nv_swap(nvh, false); 351204076Spjd ptr += NVH_SIZE(nvh); 352204076Spjd size -= NVH_SIZE(nvh); 353204076Spjd } 354204076Spjd 355204076Spjd return (nv->nv_ebuf); 356204076Spjd} 357204076Spjd 358204076Spjd/* 359204076Spjd * Create nv structure based on ebuf received from the network. 360204076Spjd */ 361204076Spjdstruct nv * 362204076Spjdnv_ntoh(struct ebuf *eb) 363204076Spjd{ 364204076Spjd struct nv *nv; 365204076Spjd size_t extra; 366204076Spjd int rerrno; 367204076Spjd 368204076Spjd assert(eb != NULL); 369204076Spjd 370204076Spjd nv = malloc(sizeof(*nv)); 371204076Spjd if (nv == NULL) 372204076Spjd return (NULL); 373204076Spjd nv->nv_error = 0; 374204076Spjd nv->nv_ebuf = eb; 375204076Spjd nv->nv_magic = NV_MAGIC; 376204076Spjd 377204076Spjd if (nv_validate(nv, &extra) < 0) { 378204076Spjd rerrno = errno; 379204076Spjd nv->nv_magic = 0; 380204076Spjd free(nv); 381204076Spjd errno = rerrno; 382204076Spjd return (NULL); 383204076Spjd } 384204076Spjd /* 385204076Spjd * Remove extra zeros at the end of the buffer. 386204076Spjd */ 387204076Spjd ebuf_del_tail(eb, extra); 388204076Spjd 389204076Spjd return (nv); 390204076Spjd} 391204076Spjd 392204076Spjd#define NV_DEFINE_ADD(type, TYPE) \ 393204076Spjdvoid \ 394204076Spjdnv_add_##type(struct nv *nv, type##_t value, const char *namefmt, ...) \ 395204076Spjd{ \ 396204076Spjd va_list nameap; \ 397204076Spjd \ 398204076Spjd va_start(nameap, namefmt); \ 399204076Spjd nv_addv(nv, (unsigned char *)&value, sizeof(value), \ 400204076Spjd NV_TYPE_##TYPE, namefmt, nameap); \ 401204076Spjd va_end(nameap); \ 402204076Spjd} 403204076Spjd 404204076SpjdNV_DEFINE_ADD(int8, INT8) 405204076SpjdNV_DEFINE_ADD(uint8, UINT8) 406204076SpjdNV_DEFINE_ADD(int16, INT16) 407204076SpjdNV_DEFINE_ADD(uint16, UINT16) 408204076SpjdNV_DEFINE_ADD(int32, INT32) 409204076SpjdNV_DEFINE_ADD(uint32, UINT32) 410204076SpjdNV_DEFINE_ADD(int64, INT64) 411204076SpjdNV_DEFINE_ADD(uint64, UINT64) 412204076Spjd 413204076Spjd#undef NV_DEFINE_ADD 414204076Spjd 415204076Spjd#define NV_DEFINE_ADD_ARRAY(type, TYPE) \ 416204076Spjdvoid \ 417204076Spjdnv_add_##type##_array(struct nv *nv, const type##_t *value, \ 418204076Spjd size_t nsize, const char *namefmt, ...) \ 419204076Spjd{ \ 420204076Spjd va_list nameap; \ 421204076Spjd \ 422204076Spjd va_start(nameap, namefmt); \ 423204076Spjd nv_addv(nv, (const unsigned char *)value, \ 424204076Spjd sizeof(value[0]) * nsize, NV_TYPE_##TYPE##_ARRAY, namefmt, \ 425204076Spjd nameap); \ 426204076Spjd va_end(nameap); \ 427204076Spjd} 428204076Spjd 429204076SpjdNV_DEFINE_ADD_ARRAY(int8, INT8) 430204076SpjdNV_DEFINE_ADD_ARRAY(uint8, UINT8) 431204076SpjdNV_DEFINE_ADD_ARRAY(int16, INT16) 432204076SpjdNV_DEFINE_ADD_ARRAY(uint16, UINT16) 433204076SpjdNV_DEFINE_ADD_ARRAY(int32, INT32) 434204076SpjdNV_DEFINE_ADD_ARRAY(uint32, UINT32) 435204076SpjdNV_DEFINE_ADD_ARRAY(int64, INT64) 436204076SpjdNV_DEFINE_ADD_ARRAY(uint64, UINT64) 437204076Spjd 438204076Spjd#undef NV_DEFINE_ADD_ARRAY 439204076Spjd 440204076Spjdvoid 441204076Spjdnv_add_string(struct nv *nv, const char *value, const char *namefmt, ...) 442204076Spjd{ 443204076Spjd va_list nameap; 444204076Spjd size_t size; 445204076Spjd 446204076Spjd size = strlen(value) + 1; 447204076Spjd 448204076Spjd va_start(nameap, namefmt); 449204076Spjd nv_addv(nv, (const unsigned char *)value, size, NV_TYPE_STRING, 450204076Spjd namefmt, nameap); 451204076Spjd va_end(nameap); 452204076Spjd} 453204076Spjd 454204076Spjdvoid 455204076Spjdnv_add_stringf(struct nv *nv, const char *name, const char *valuefmt, ...) 456204076Spjd{ 457204076Spjd va_list valueap; 458204076Spjd 459204076Spjd va_start(valueap, valuefmt); 460204076Spjd nv_add_stringv(nv, name, valuefmt, valueap); 461204076Spjd va_end(valueap); 462204076Spjd} 463204076Spjd 464204076Spjdvoid 465204076Spjdnv_add_stringv(struct nv *nv, const char *name, const char *valuefmt, 466204076Spjd va_list valueap) 467204076Spjd{ 468204076Spjd char *value; 469204076Spjd ssize_t size; 470204076Spjd 471204076Spjd size = vasprintf(&value, valuefmt, valueap); 472204076Spjd if (size < 0) { 473204076Spjd if (nv->nv_error == 0) 474204076Spjd nv->nv_error = ENOMEM; 475204076Spjd return; 476204076Spjd } 477204076Spjd size++; 478204076Spjd nv_add(nv, (const unsigned char *)value, size, NV_TYPE_STRING, name); 479204076Spjd free(value); 480204076Spjd} 481204076Spjd 482204076Spjd#define NV_DEFINE_GET(type, TYPE) \ 483204076Spjdtype##_t \ 484204076Spjdnv_get_##type(struct nv *nv, const char *namefmt, ...) \ 485204076Spjd{ \ 486204076Spjd struct nvhdr *nvh; \ 487204076Spjd va_list nameap; \ 488204076Spjd type##_t value; \ 489204076Spjd \ 490204076Spjd va_start(nameap, namefmt); \ 491204076Spjd nvh = nv_find(nv, NV_TYPE_##TYPE, namefmt, nameap); \ 492204076Spjd va_end(nameap); \ 493204076Spjd if (nvh == NULL) \ 494204076Spjd return (0); \ 495204076Spjd assert((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST); \ 496204076Spjd assert(sizeof(value) == nvh->nvh_dsize); \ 497204076Spjd bcopy(NVH_DATA(nvh), &value, sizeof(value)); \ 498204076Spjd \ 499204076Spjd return (value); \ 500204076Spjd} 501204076Spjd 502204076SpjdNV_DEFINE_GET(int8, INT8) 503204076SpjdNV_DEFINE_GET(uint8, UINT8) 504204076SpjdNV_DEFINE_GET(int16, INT16) 505204076SpjdNV_DEFINE_GET(uint16, UINT16) 506204076SpjdNV_DEFINE_GET(int32, INT32) 507204076SpjdNV_DEFINE_GET(uint32, UINT32) 508204076SpjdNV_DEFINE_GET(int64, INT64) 509204076SpjdNV_DEFINE_GET(uint64, UINT64) 510204076Spjd 511204076Spjd#undef NV_DEFINE_GET 512204076Spjd 513204076Spjd#define NV_DEFINE_GET_ARRAY(type, TYPE) \ 514204076Spjdconst type##_t * \ 515204076Spjdnv_get_##type##_array(struct nv *nv, size_t *sizep, \ 516204076Spjd const char *namefmt, ...) \ 517204076Spjd{ \ 518204076Spjd struct nvhdr *nvh; \ 519204076Spjd va_list nameap; \ 520204076Spjd \ 521204076Spjd va_start(nameap, namefmt); \ 522204076Spjd nvh = nv_find(nv, NV_TYPE_##TYPE##_ARRAY, namefmt, nameap); \ 523204076Spjd va_end(nameap); \ 524204076Spjd if (nvh == NULL) \ 525204076Spjd return (NULL); \ 526204076Spjd assert((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST); \ 527204076Spjd assert((nvh->nvh_dsize % sizeof(type##_t)) == 0); \ 528204076Spjd if (sizep != NULL) \ 529204076Spjd *sizep = nvh->nvh_dsize / sizeof(type##_t); \ 530204076Spjd return ((type##_t *)(void *)NVH_DATA(nvh)); \ 531204076Spjd} 532204076Spjd 533204076SpjdNV_DEFINE_GET_ARRAY(int8, INT8) 534204076SpjdNV_DEFINE_GET_ARRAY(uint8, UINT8) 535204076SpjdNV_DEFINE_GET_ARRAY(int16, INT16) 536204076SpjdNV_DEFINE_GET_ARRAY(uint16, UINT16) 537204076SpjdNV_DEFINE_GET_ARRAY(int32, INT32) 538204076SpjdNV_DEFINE_GET_ARRAY(uint32, UINT32) 539204076SpjdNV_DEFINE_GET_ARRAY(int64, INT64) 540204076SpjdNV_DEFINE_GET_ARRAY(uint64, UINT64) 541204076Spjd 542204076Spjd#undef NV_DEFINE_GET_ARRAY 543204076Spjd 544204076Spjdconst char * 545204076Spjdnv_get_string(struct nv *nv, const char *namefmt, ...) 546204076Spjd{ 547204076Spjd struct nvhdr *nvh; 548204076Spjd va_list nameap; 549204076Spjd char *str; 550204076Spjd 551204076Spjd va_start(nameap, namefmt); 552204076Spjd nvh = nv_find(nv, NV_TYPE_STRING, namefmt, nameap); 553204076Spjd va_end(nameap); 554204076Spjd if (nvh == NULL) 555204076Spjd return (NULL); 556204076Spjd assert((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST); 557204076Spjd assert(nvh->nvh_dsize >= 1); 558204076Spjd str = NVH_DATA(nvh); 559204076Spjd assert(str[nvh->nvh_dsize - 1] == '\0'); 560204076Spjd assert(strlen(str) == nvh->nvh_dsize - 1); 561204076Spjd return (str); 562204076Spjd} 563204076Spjd 564204076Spjd/* 565204076Spjd * Dump content of the nv structure. 566204076Spjd */ 567204076Spjdvoid 568204076Spjdnv_dump(struct nv *nv) 569204076Spjd{ 570204076Spjd struct nvhdr *nvh; 571204076Spjd unsigned char *data, *ptr; 572204076Spjd size_t dsize, size; 573204076Spjd unsigned int ii; 574204076Spjd bool swap; 575204076Spjd 576204076Spjd if (nv_validate(nv, NULL) < 0) { 577204076Spjd printf("error: %d\n", errno); 578204076Spjd return; 579204076Spjd } 580204076Spjd 581204076Spjd NV_CHECK(nv); 582204076Spjd assert(nv->nv_error == 0); 583204076Spjd 584204076Spjd ptr = ebuf_data(nv->nv_ebuf, &size); 585204076Spjd while (size > 0) { 586204076Spjd assert(size >= sizeof(*nvh) + 2); 587204076Spjd nvh = (struct nvhdr *)ptr; 588204076Spjd assert(size >= NVH_SIZE(nvh)); 589204076Spjd swap = ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_NETWORK); 590204076Spjd dsize = NVH_DSIZE(nvh); 591204076Spjd data = NVH_DATA(nvh); 592204076Spjd printf(" %s", nvh->nvh_name); 593204076Spjd switch (nvh->nvh_type & NV_TYPE_MASK) { 594204076Spjd case NV_TYPE_INT8: 595204076Spjd printf("(int8): %jd", (intmax_t)(*(int8_t *)data)); 596204076Spjd break; 597204076Spjd case NV_TYPE_UINT8: 598204076Spjd printf("(uint8): %ju", (uintmax_t)(*(uint8_t *)data)); 599204076Spjd break; 600204076Spjd case NV_TYPE_INT16: 601204076Spjd printf("(int16): %jd", swap ? 602204076Spjd (intmax_t)le16toh(*(int16_t *)(void *)data) : 603204076Spjd (intmax_t)*(int16_t *)(void *)data); 604204076Spjd break; 605204076Spjd case NV_TYPE_UINT16: 606204076Spjd printf("(uint16): %ju", swap ? 607204076Spjd (uintmax_t)le16toh(*(uint16_t *)(void *)data) : 608204076Spjd (uintmax_t)*(uint16_t *)(void *)data); 609204076Spjd break; 610204076Spjd case NV_TYPE_INT32: 611204076Spjd printf("(int32): %jd", swap ? 612204076Spjd (intmax_t)le32toh(*(int32_t *)(void *)data) : 613204076Spjd (intmax_t)*(int32_t *)(void *)data); 614204076Spjd break; 615204076Spjd case NV_TYPE_UINT32: 616204076Spjd printf("(uint32): %ju", swap ? 617204076Spjd (uintmax_t)le32toh(*(uint32_t *)(void *)data) : 618204076Spjd (uintmax_t)*(uint32_t *)(void *)data); 619204076Spjd break; 620204076Spjd case NV_TYPE_INT64: 621204076Spjd printf("(int64): %jd", swap ? 622204076Spjd (intmax_t)le64toh(*(int64_t *)(void *)data) : 623204076Spjd (intmax_t)*(int64_t *)(void *)data); 624204076Spjd break; 625204076Spjd case NV_TYPE_UINT64: 626204076Spjd printf("(uint64): %ju", swap ? 627204076Spjd (uintmax_t)le64toh(*(uint64_t *)(void *)data) : 628204076Spjd (uintmax_t)*(uint64_t *)(void *)data); 629204076Spjd break; 630204076Spjd case NV_TYPE_INT8_ARRAY: 631204076Spjd printf("(int8 array):"); 632204076Spjd for (ii = 0; ii < dsize; ii++) 633204076Spjd printf(" %jd", (intmax_t)((int8_t *)data)[ii]); 634204076Spjd break; 635204076Spjd case NV_TYPE_UINT8_ARRAY: 636204076Spjd printf("(uint8 array):"); 637204076Spjd for (ii = 0; ii < dsize; ii++) 638204076Spjd printf(" %ju", (uintmax_t)((uint8_t *)data)[ii]); 639204076Spjd break; 640204076Spjd case NV_TYPE_INT16_ARRAY: 641204076Spjd printf("(int16 array):"); 642204076Spjd for (ii = 0; ii < dsize / 2; ii++) { 643204076Spjd printf(" %jd", swap ? 644204076Spjd (intmax_t)le16toh(((int16_t *)(void *)data)[ii]) : 645204076Spjd (intmax_t)((int16_t *)(void *)data)[ii]); 646204076Spjd } 647204076Spjd break; 648204076Spjd case NV_TYPE_UINT16_ARRAY: 649204076Spjd printf("(uint16 array):"); 650204076Spjd for (ii = 0; ii < dsize / 2; ii++) { 651204076Spjd printf(" %ju", swap ? 652204076Spjd (uintmax_t)le16toh(((uint16_t *)(void *)data)[ii]) : 653204076Spjd (uintmax_t)((uint16_t *)(void *)data)[ii]); 654204076Spjd } 655204076Spjd break; 656204076Spjd case NV_TYPE_INT32_ARRAY: 657204076Spjd printf("(int32 array):"); 658204076Spjd for (ii = 0; ii < dsize / 4; ii++) { 659204076Spjd printf(" %jd", swap ? 660204076Spjd (intmax_t)le32toh(((int32_t *)(void *)data)[ii]) : 661204076Spjd (intmax_t)((int32_t *)(void *)data)[ii]); 662204076Spjd } 663204076Spjd break; 664204076Spjd case NV_TYPE_UINT32_ARRAY: 665204076Spjd printf("(uint32 array):"); 666204076Spjd for (ii = 0; ii < dsize / 4; ii++) { 667204076Spjd printf(" %ju", swap ? 668204076Spjd (uintmax_t)le32toh(((uint32_t *)(void *)data)[ii]) : 669204076Spjd (uintmax_t)((uint32_t *)(void *)data)[ii]); 670204076Spjd } 671204076Spjd break; 672204076Spjd case NV_TYPE_INT64_ARRAY: 673204076Spjd printf("(int64 array):"); 674204076Spjd for (ii = 0; ii < dsize / 8; ii++) { 675204076Spjd printf(" %ju", swap ? 676204076Spjd (uintmax_t)le64toh(((uint64_t *)(void *)data)[ii]) : 677204076Spjd (uintmax_t)((uint64_t *)(void *)data)[ii]); 678204076Spjd } 679204076Spjd break; 680204076Spjd case NV_TYPE_UINT64_ARRAY: 681204076Spjd printf("(uint64 array):"); 682204076Spjd for (ii = 0; ii < dsize / 8; ii++) { 683204076Spjd printf(" %ju", swap ? 684204076Spjd (uintmax_t)le64toh(((uint64_t *)(void *)data)[ii]) : 685204076Spjd (uintmax_t)((uint64_t *)(void *)data)[ii]); 686204076Spjd } 687204076Spjd break; 688204076Spjd case NV_TYPE_STRING: 689204076Spjd printf("(string): %s", (char *)data); 690204076Spjd break; 691204076Spjd default: 692204076Spjd assert(!"invalid condition"); 693204076Spjd } 694204076Spjd printf("\n"); 695204076Spjd ptr += NVH_SIZE(nvh); 696204076Spjd size -= NVH_SIZE(nvh); 697204076Spjd } 698204076Spjd} 699204076Spjd 700204076Spjd/* 701204076Spjd * Local routines below. 702204076Spjd */ 703204076Spjd 704204076Spjdstatic void 705204076Spjdnv_add(struct nv *nv, const unsigned char *value, size_t vsize, int type, 706204076Spjd const char *name) 707204076Spjd{ 708204076Spjd static unsigned char align[7]; 709204076Spjd struct nvhdr *nvh; 710204076Spjd size_t namesize; 711204076Spjd 712204076Spjd if (nv == NULL) { 713204076Spjd errno = ENOMEM; 714204076Spjd return; 715204076Spjd } 716204076Spjd 717204076Spjd NV_CHECK(nv); 718204076Spjd 719204076Spjd namesize = strlen(name) + 1; 720204076Spjd 721204076Spjd nvh = malloc(sizeof(*nvh) + roundup2(namesize, 8)); 722204076Spjd if (nvh == NULL) { 723204076Spjd if (nv->nv_error == 0) 724204076Spjd nv->nv_error = ENOMEM; 725204076Spjd return; 726204076Spjd } 727204076Spjd nvh->nvh_type = NV_ORDER_HOST | type; 728204076Spjd nvh->nvh_namesize = (uint8_t)namesize; 729204076Spjd nvh->nvh_dsize = (uint32_t)vsize; 730204076Spjd bcopy(name, nvh->nvh_name, namesize); 731204076Spjd 732204076Spjd /* Add header first. */ 733204076Spjd if (ebuf_add_tail(nv->nv_ebuf, nvh, NVH_HSIZE(nvh)) < 0) { 734204076Spjd assert(errno != 0); 735204076Spjd if (nv->nv_error == 0) 736204076Spjd nv->nv_error = errno; 737209180Spjd free(nvh); 738204076Spjd return; 739204076Spjd } 740209180Spjd free(nvh); 741204076Spjd /* Add the actual data. */ 742204076Spjd if (ebuf_add_tail(nv->nv_ebuf, value, vsize) < 0) { 743204076Spjd assert(errno != 0); 744204076Spjd if (nv->nv_error == 0) 745204076Spjd nv->nv_error = errno; 746204076Spjd return; 747204076Spjd } 748204076Spjd /* Align the data (if needed). */ 749204076Spjd vsize = roundup2(vsize, 8) - vsize; 750204076Spjd if (vsize == 0) 751204076Spjd return; 752204076Spjd assert(vsize > 0 && vsize <= sizeof(align)); 753204076Spjd if (ebuf_add_tail(nv->nv_ebuf, align, vsize) < 0) { 754204076Spjd assert(errno != 0); 755204076Spjd if (nv->nv_error == 0) 756204076Spjd nv->nv_error = errno; 757204076Spjd return; 758204076Spjd } 759204076Spjd} 760204076Spjd 761204076Spjdstatic void 762204076Spjdnv_addv(struct nv *nv, const unsigned char *value, size_t vsize, int type, 763204076Spjd const char *namefmt, va_list nameap) 764204076Spjd{ 765204076Spjd char name[255]; 766204076Spjd size_t namesize; 767204076Spjd 768204076Spjd namesize = vsnprintf(name, sizeof(name), namefmt, nameap); 769204076Spjd assert(namesize > 0 && namesize < sizeof(name)); 770204076Spjd 771204076Spjd nv_add(nv, value, vsize, type, name); 772204076Spjd} 773204076Spjd 774204076Spjdstatic struct nvhdr * 775204076Spjdnv_find(struct nv *nv, int type, const char *namefmt, va_list nameap) 776204076Spjd{ 777204076Spjd char name[255]; 778204076Spjd struct nvhdr *nvh; 779204076Spjd unsigned char *ptr; 780204076Spjd size_t size, namesize; 781204076Spjd 782204076Spjd if (nv == NULL) { 783204076Spjd errno = ENOMEM; 784204076Spjd return (NULL); 785204076Spjd } 786204076Spjd 787204076Spjd NV_CHECK(nv); 788204076Spjd 789204076Spjd namesize = vsnprintf(name, sizeof(name), namefmt, nameap); 790204076Spjd assert(namesize > 0 && namesize < sizeof(name)); 791204076Spjd namesize++; 792204076Spjd 793204076Spjd ptr = ebuf_data(nv->nv_ebuf, &size); 794204076Spjd while (size > 0) { 795204076Spjd assert(size >= sizeof(*nvh) + 2); 796204076Spjd nvh = (struct nvhdr *)ptr; 797204076Spjd assert(size >= NVH_SIZE(nvh)); 798204076Spjd nv_swap(nvh, true); 799204076Spjd if (strcmp(nvh->nvh_name, name) == 0) { 800204076Spjd if ((nvh->nvh_type & NV_TYPE_MASK) != type) { 801204076Spjd errno = EINVAL; 802204076Spjd if (nv->nv_error == 0) 803204076Spjd nv->nv_error = EINVAL; 804204076Spjd return (NULL); 805204076Spjd } 806204076Spjd return (nvh); 807204076Spjd } 808204076Spjd ptr += NVH_SIZE(nvh); 809204076Spjd size -= NVH_SIZE(nvh); 810204076Spjd } 811204076Spjd errno = ENOENT; 812204076Spjd if (nv->nv_error == 0) 813204076Spjd nv->nv_error = ENOENT; 814204076Spjd return (NULL); 815204076Spjd} 816204076Spjd 817204076Spjdstatic void 818204076Spjdnv_swap(struct nvhdr *nvh, bool tohost) 819204076Spjd{ 820204076Spjd unsigned char *data, *end, *p; 821204076Spjd size_t vsize; 822204076Spjd 823204076Spjd data = NVH_DATA(nvh); 824204076Spjd if (tohost) { 825204076Spjd if ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST) 826204076Spjd return; 827204076Spjd nvh->nvh_dsize = le32toh(nvh->nvh_dsize); 828204076Spjd end = data + nvh->nvh_dsize; 829204076Spjd nvh->nvh_type &= ~NV_ORDER_MASK; 830204076Spjd nvh->nvh_type |= NV_ORDER_HOST; 831204076Spjd } else { 832204076Spjd if ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_NETWORK) 833204076Spjd return; 834204076Spjd end = data + nvh->nvh_dsize; 835204076Spjd nvh->nvh_dsize = htole32(nvh->nvh_dsize); 836204076Spjd nvh->nvh_type &= ~NV_ORDER_MASK; 837204076Spjd nvh->nvh_type |= NV_ORDER_NETWORK; 838204076Spjd } 839204076Spjd 840204076Spjd vsize = 0; 841204076Spjd 842204076Spjd switch (nvh->nvh_type & NV_TYPE_MASK) { 843204076Spjd case NV_TYPE_INT8: 844204076Spjd case NV_TYPE_UINT8: 845204076Spjd case NV_TYPE_INT8_ARRAY: 846204076Spjd case NV_TYPE_UINT8_ARRAY: 847204076Spjd break; 848204076Spjd case NV_TYPE_INT16: 849204076Spjd case NV_TYPE_UINT16: 850204076Spjd case NV_TYPE_INT16_ARRAY: 851204076Spjd case NV_TYPE_UINT16_ARRAY: 852204076Spjd if (vsize == 0) 853204076Spjd vsize = 2; 854204076Spjd /* FALLTHOUGH */ 855204076Spjd case NV_TYPE_INT32: 856204076Spjd case NV_TYPE_UINT32: 857204076Spjd case NV_TYPE_INT32_ARRAY: 858204076Spjd case NV_TYPE_UINT32_ARRAY: 859204076Spjd if (vsize == 0) 860204076Spjd vsize = 4; 861204076Spjd /* FALLTHOUGH */ 862204076Spjd case NV_TYPE_INT64: 863204076Spjd case NV_TYPE_UINT64: 864204076Spjd case NV_TYPE_INT64_ARRAY: 865204076Spjd case NV_TYPE_UINT64_ARRAY: 866204076Spjd if (vsize == 0) 867204076Spjd vsize = 8; 868204076Spjd for (p = data; p < end; p += vsize) { 869204076Spjd if (tohost) { 870204076Spjd switch (vsize) { 871204076Spjd case 2: 872204076Spjd *(uint16_t *)(void *)p = 873204076Spjd le16toh(*(uint16_t *)(void *)p); 874204076Spjd break; 875204076Spjd case 4: 876204076Spjd *(uint32_t *)(void *)p = 877204076Spjd le32toh(*(uint32_t *)(void *)p); 878204076Spjd break; 879204076Spjd case 8: 880204076Spjd *(uint64_t *)(void *)p = 881204076Spjd le64toh(*(uint64_t *)(void *)p); 882204076Spjd break; 883204076Spjd default: 884204076Spjd assert(!"invalid condition"); 885204076Spjd } 886204076Spjd } else { 887204076Spjd switch (vsize) { 888204076Spjd case 2: 889204076Spjd *(uint16_t *)(void *)p = 890204076Spjd htole16(*(uint16_t *)(void *)p); 891204076Spjd break; 892204076Spjd case 4: 893204076Spjd *(uint32_t *)(void *)p = 894204076Spjd htole32(*(uint32_t *)(void *)p); 895204076Spjd break; 896204076Spjd case 8: 897204076Spjd *(uint64_t *)(void *)p = 898204076Spjd htole64(*(uint64_t *)(void *)p); 899204076Spjd break; 900204076Spjd default: 901204076Spjd assert(!"invalid condition"); 902204076Spjd } 903204076Spjd } 904204076Spjd } 905204076Spjd break; 906204076Spjd case NV_TYPE_STRING: 907204076Spjd break; 908204076Spjd default: 909204076Spjd assert(!"unrecognized type"); 910204076Spjd } 911204076Spjd} 912