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