1 /*- 2 * Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org> 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 __FBSDID("$FreeBSD$"); 32 33 #ifdef _KERNEL 34 35 #include <sys/param.h> 36 #include <sys/ctype.h> 37 #include <sys/malloc.h> 38 #include <sys/systm.h> 39 40 #else /* !_KERNEL */ 41 42 #include <ctype.h> 43 #include <stdint.h> 44 #include <stdio.h> 45 #include <stdlib.h> 46 #include <string.h> 47 48 #endif /* _KERNEL */ 49 50 #include "bhnd_nvram_private.h" 51 52 #include "bhnd_nvram_datavar.h" 53 #include "bhnd_nvram_data_bcmvar.h" 54 55 /* 56 * Broadcom-RAW NVRAM data class. 57 * 58 * The Broadcom NVRAM NUL-delimited ASCII format is used by most 59 * Broadcom SoCs. 60 * 61 * The NVRAM data is encoded as a stream of of NUL-terminated 'key=value' 62 * strings; the end of the stream is denoted by a single extra NUL character. 63 */ 64 65 struct bhnd_nvram_bcmraw; 66 67 /** BCM-RAW NVRAM data class instance */ 68 struct bhnd_nvram_bcmraw { 69 struct bhnd_nvram_data nv; /**< common instance state */ 70 char *data; /**< backing buffer */ 71 size_t size; /**< buffer size */ 72 size_t count; /**< variable count */ 73 }; 74 75 BHND_NVRAM_DATA_CLASS_DEFN(bcmraw, "Broadcom (RAW)", 76 BHND_NVRAM_DATA_CAP_DEVPATHS, sizeof(struct bhnd_nvram_bcmraw)) 77 78 static int 79 bhnd_nvram_bcmraw_probe(struct bhnd_nvram_io *io) 80 { 81 char envp[16]; 82 size_t envp_len; 83 size_t io_size; 84 int error; 85 86 io_size = bhnd_nvram_io_getsize(io); 87 88 /* 89 * Fetch initial bytes 90 */ 91 envp_len = bhnd_nv_ummin(sizeof(envp), io_size); 92 if ((error = bhnd_nvram_io_read(io, 0x0, envp, envp_len))) 93 return (error); 94 95 /* An empty BCM-RAW buffer should still contain a single terminating 96 * NUL */ 97 if (envp_len == 0) 98 return (ENXIO); 99 100 if (envp_len == 1) { 101 if (envp[0] != '\0') 102 return (ENXIO); 103 104 return (BHND_NVRAM_DATA_PROBE_MAYBE); 105 } 106 107 /* Must contain only printable ASCII characters delimited 108 * by NUL record delimiters */ 109 for (size_t i = 0; i < envp_len; i++) { 110 char c = envp[i]; 111 112 /* If we hit a newline, this is probably BCM-TXT */ 113 if (c == '\n') 114 return (ENXIO); 115 116 if (c == '\0' && !bhnd_nv_isprint(c)) 117 continue; 118 } 119 120 /* A valid BCM-RAW buffer should contain a terminating NUL for 121 * the last record, followed by a final empty record terminated by 122 * NUL */ 123 envp_len = 2; 124 if (io_size < envp_len) 125 return (ENXIO); 126 127 if ((error = bhnd_nvram_io_read(io, io_size-envp_len, envp, envp_len))) 128 return (error); 129 130 if (envp[0] != '\0' || envp[1] != '\0') 131 return (ENXIO); 132 133 return (BHND_NVRAM_DATA_PROBE_MAYBE + 1); 134 } 135 136 static int 137 bhnd_nvram_bcmraw_getvar_direct(struct bhnd_nvram_io *io, const char *name, 138 void *buf, size_t *len, bhnd_nvram_type type) 139 { 140 return (bhnd_nvram_bcm_getvar_direct_common(io, name, buf, len, type, 141 false)); 142 } 143 144 static int 145 bhnd_nvram_bcmraw_serialize(bhnd_nvram_data_class *cls, bhnd_nvram_plist *props, 146 bhnd_nvram_plist *options, void *outp, size_t *olen) 147 { 148 bhnd_nvram_prop *prop; 149 size_t limit, nbytes; 150 int error; 151 152 /* Determine output byte limit */ 153 if (outp != NULL) 154 limit = *olen; 155 else 156 limit = 0; 157 158 nbytes = 0; 159 160 /* Write all properties */ 161 prop = NULL; 162 while ((prop = bhnd_nvram_plist_next(props, prop)) != NULL) { 163 const char *name; 164 char *p; 165 size_t prop_limit; 166 size_t name_len, value_len; 167 168 if (outp == NULL || limit < nbytes) { 169 p = NULL; 170 prop_limit = 0; 171 } else { 172 p = ((char *)outp) + nbytes; 173 prop_limit = limit - nbytes; 174 } 175 176 /* Fetch and write name + '=' to output */ 177 name = bhnd_nvram_prop_name(prop); 178 name_len = strlen(name) + 1; 179 180 if (prop_limit > name_len) { 181 memcpy(p, name, name_len - 1); 182 p[name_len - 1] = '='; 183 184 prop_limit -= name_len; 185 p += name_len; 186 } else { 187 prop_limit = 0; 188 p = NULL; 189 } 190 191 /* Advance byte count */ 192 if (SIZE_MAX - nbytes < name_len) 193 return (EFTYPE); /* would overflow size_t */ 194 195 nbytes += name_len; 196 197 /* Attempt to write NUL-terminated value to output */ 198 value_len = prop_limit; 199 error = bhnd_nvram_prop_encode(prop, p, &value_len, 200 BHND_NVRAM_TYPE_STRING); 201 202 /* If encoding failed for any reason other than ENOMEM (which 203 * we'll detect and report after encoding all properties), 204 * return immediately */ 205 if (error && error != ENOMEM) { 206 BHND_NV_LOG("error serializing %s to required type " 207 "%s: %d\n", name, 208 bhnd_nvram_type_name(BHND_NVRAM_TYPE_STRING), 209 error); 210 return (error); 211 } 212 213 /* Advance byte count */ 214 if (SIZE_MAX - nbytes < value_len) 215 return (EFTYPE); /* would overflow size_t */ 216 217 nbytes += value_len; 218 } 219 220 /* Write terminating '\0' */ 221 if (limit > nbytes) 222 *((char *)outp + nbytes) = '\0'; 223 224 if (nbytes == SIZE_MAX) 225 return (EFTYPE); /* would overflow size_t */ 226 else 227 nbytes++; 228 229 /* Provide required length */ 230 *olen = nbytes; 231 if (limit < *olen) { 232 if (outp == NULL) 233 return (0); 234 235 return (ENOMEM); 236 } 237 238 return (0); 239 } 240 241 /** 242 * Initialize @p bcm with the provided NVRAM data mapped by @p src. 243 * 244 * @param bcm A newly allocated data instance. 245 */ 246 static int 247 bhnd_nvram_bcmraw_init(struct bhnd_nvram_bcmraw *bcm, struct bhnd_nvram_io *src) 248 { 249 size_t io_size; 250 size_t capacity, offset; 251 int error; 252 253 /* Fetch the input image size */ 254 io_size = bhnd_nvram_io_getsize(src); 255 256 /* Allocate a buffer large enough to hold the NVRAM image, and 257 * an extra EOF-signaling NUL (on the chance it's missing from the 258 * source data) */ 259 if (io_size == SIZE_MAX) 260 return (ENOMEM); 261 262 capacity = io_size + 1 /* room for extra NUL */; 263 bcm->size = io_size; 264 if ((bcm->data = bhnd_nv_malloc(capacity)) == NULL) 265 return (ENOMEM); 266 267 /* Copy in the NVRAM image */ 268 if ((error = bhnd_nvram_io_read(src, 0x0, bcm->data, io_size))) 269 return (error); 270 271 /* Process the buffer */ 272 bcm->count = 0; 273 for (offset = 0; offset < bcm->size; offset++) { 274 char *envp; 275 const char *name, *value; 276 size_t envp_len; 277 size_t name_len, value_len; 278 279 /* Parse the key=value string */ 280 envp = (char *) (bcm->data + offset); 281 envp_len = strnlen(envp, bcm->size - offset); 282 error = bhnd_nvram_parse_env(envp, envp_len, '=', &name, 283 &name_len, &value, &value_len); 284 if (error) { 285 BHND_NV_LOG("error parsing envp at offset %#zx: %d\n", 286 offset, error); 287 return (error); 288 } 289 290 /* Insert a '\0' character, replacing the '=' delimiter and 291 * allowing us to vend references directly to the variable 292 * name */ 293 *(envp + name_len) = '\0'; 294 295 /* Add to variable count */ 296 bcm->count++; 297 298 /* Seek past the value's terminating '\0' */ 299 offset += envp_len; 300 if (offset == io_size) { 301 BHND_NV_LOG("missing terminating NUL at offset %#zx\n", 302 offset); 303 return (EINVAL); 304 } 305 306 /* If we hit EOF without finding a terminating NUL 307 * byte, we need to append it */ 308 if (++offset == bcm->size) { 309 BHND_NV_ASSERT(offset < capacity, 310 ("appending past end of buffer")); 311 bcm->size++; 312 *(bcm->data + offset) = '\0'; 313 } 314 315 /* Check for explicit EOF (encoded as a single empty NUL 316 * terminated string) */ 317 if (*(bcm->data + offset) == '\0') 318 break; 319 } 320 321 /* Reclaim any unused space in he backing buffer */ 322 if (offset < bcm->size) { 323 bcm->data = bhnd_nv_reallocf(bcm->data, bcm->size); 324 if (bcm->data == NULL) 325 return (ENOMEM); 326 } 327 328 return (0); 329 } 330 331 static int 332 bhnd_nvram_bcmraw_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io) 333 { 334 struct bhnd_nvram_bcmraw *bcm; 335 int error; 336 337 bcm = (struct bhnd_nvram_bcmraw *)nv; 338 339 /* Parse the BCM input data and initialize our backing 340 * data representation */ 341 if ((error = bhnd_nvram_bcmraw_init(bcm, io))) { 342 bhnd_nvram_bcmraw_free(nv); 343 return (error); 344 } 345 346 return (0); 347 } 348 349 static void 350 bhnd_nvram_bcmraw_free(struct bhnd_nvram_data *nv) 351 { 352 struct bhnd_nvram_bcmraw *bcm = (struct bhnd_nvram_bcmraw *)nv; 353 354 if (bcm->data != NULL) 355 bhnd_nv_free(bcm->data); 356 } 357 358 static bhnd_nvram_plist * 359 bhnd_nvram_bcmraw_options(struct bhnd_nvram_data *nv) 360 { 361 return (NULL); 362 } 363 364 static size_t 365 bhnd_nvram_bcmraw_count(struct bhnd_nvram_data *nv) 366 { 367 struct bhnd_nvram_bcmraw *bcm = (struct bhnd_nvram_bcmraw *)nv; 368 369 return (bcm->count); 370 } 371 372 static uint32_t 373 bhnd_nvram_bcmraw_caps(struct bhnd_nvram_data *nv) 374 { 375 return (BHND_NVRAM_DATA_CAP_READ_PTR|BHND_NVRAM_DATA_CAP_DEVPATHS); 376 } 377 378 static const char * 379 bhnd_nvram_bcmraw_next(struct bhnd_nvram_data *nv, void **cookiep) 380 { 381 struct bhnd_nvram_bcmraw *bcm; 382 const char *envp; 383 384 bcm = (struct bhnd_nvram_bcmraw *)nv; 385 386 if (*cookiep == NULL) { 387 /* Start at the first NVRAM data record */ 388 envp = bcm->data; 389 } else { 390 /* Seek to next record */ 391 envp = *cookiep; 392 envp += strlen(envp) + 1; /* key + '\0' */ 393 envp += strlen(envp) + 1; /* value + '\0' */ 394 } 395 396 /* EOF? */ 397 if (*envp == '\0') 398 return (NULL); 399 400 *cookiep = (void *)(uintptr_t)envp; 401 return (envp); 402 } 403 404 static void * 405 bhnd_nvram_bcmraw_find(struct bhnd_nvram_data *nv, const char *name) 406 { 407 return (bhnd_nvram_data_generic_find(nv, name)); 408 } 409 410 static int 411 bhnd_nvram_bcmraw_getvar_order(struct bhnd_nvram_data *nv, void *cookiep1, 412 void *cookiep2) 413 { 414 if (cookiep1 < cookiep2) 415 return (-1); 416 417 if (cookiep1 > cookiep2) 418 return (1); 419 420 return (0); 421 } 422 423 static int 424 bhnd_nvram_bcmraw_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf, 425 size_t *len, bhnd_nvram_type type) 426 { 427 return (bhnd_nvram_data_generic_rp_getvar(nv, cookiep, buf, len, type)); 428 } 429 430 static int 431 bhnd_nvram_bcmraw_copy_val(struct bhnd_nvram_data *nv, void *cookiep, 432 bhnd_nvram_val **value) 433 { 434 return (bhnd_nvram_data_generic_rp_copy_val(nv, cookiep, value)); 435 } 436 437 static const void * 438 bhnd_nvram_bcmraw_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep, 439 size_t *len, bhnd_nvram_type *type) 440 { 441 const char *envp; 442 443 /* Cookie points to key\0value\0 -- get the value address */ 444 envp = cookiep; 445 envp += strlen(envp) + 1; /* key + '\0' */ 446 *len = strlen(envp) + 1; /* value + '\0' */ 447 *type = BHND_NVRAM_TYPE_STRING; 448 449 return (envp); 450 } 451 452 static const char * 453 bhnd_nvram_bcmraw_getvar_name(struct bhnd_nvram_data *nv, void *cookiep) 454 { 455 /* Cookie points to key\0value\0 */ 456 return (cookiep); 457 } 458 459 static int 460 bhnd_nvram_bcmraw_filter_setvar(struct bhnd_nvram_data *nv, const char *name, 461 bhnd_nvram_val *value, bhnd_nvram_val **result) 462 { 463 bhnd_nvram_val *str; 464 int error; 465 466 /* Name (trimmed of any path prefix) must be valid */ 467 if (!bhnd_nvram_validate_name(bhnd_nvram_trim_path_name(name))) 468 return (EINVAL); 469 470 /* Value must be bcm-formatted string */ 471 error = bhnd_nvram_val_convert_new(&str, &bhnd_nvram_val_bcm_string_fmt, 472 value, BHND_NVRAM_VAL_DYNAMIC); 473 if (error) 474 return (error); 475 476 /* Success. Transfer result ownership to the caller. */ 477 *result = str; 478 return (0); 479 } 480 481 static int 482 bhnd_nvram_bcmraw_filter_unsetvar(struct bhnd_nvram_data *nv, const char *name) 483 { 484 /* We permit deletion of any variable */ 485 return (0); 486 } 487