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