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 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 int error; 83 84 /* 85 * Fetch the initial bytes to try to identify BCM data. 86 * 87 * We always assert a low probe priority, as we only scan the initial 88 * bytes of the file. 89 */ 90 envp_len = bhnd_nv_ummin(sizeof(envp), bhnd_nvram_io_getsize(io)); 91 if ((error = bhnd_nvram_io_read(io, 0x0, envp, envp_len))) 92 return (error); 93 94 /* A zero-length BCM-RAW buffer should 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 /* Don't match on non-ASCII, non-printable data */ 107 for (size_t i = 0; i < envp_len; i++) { 108 char c = envp[i]; 109 if (envp[i] == '\0') 110 break; 111 112 if (!bhnd_nv_isprint(c)) 113 return (ENXIO); 114 } 115 116 /* The first character should be a valid key char */ 117 if (!bhnd_nv_isalpha(envp[0])) 118 return (ENXIO); 119 120 return (BHND_NVRAM_DATA_PROBE_MAYBE); 121 } 122 123 /** 124 * Initialize @p bcm with the provided NVRAM data mapped by @p src. 125 * 126 * @param bcm A newly allocated data instance. 127 */ 128 static int 129 bhnd_nvram_bcmraw_init(struct bhnd_nvram_bcmraw *bcm, struct bhnd_nvram_io *src) 130 { 131 size_t io_size; 132 size_t capacity, offset; 133 int error; 134 135 /* Fetch the input image size */ 136 io_size = bhnd_nvram_io_getsize(src); 137 138 /* Allocate a buffer large enough to hold the NVRAM image, and 139 * an extra EOF-signaling NUL (on the chance it's missing from the 140 * source data) */ 141 if (io_size == SIZE_MAX) 142 return (ENOMEM); 143 144 capacity = io_size + 1 /* room for extra NUL */; 145 bcm->size = io_size; 146 if ((bcm->data = bhnd_nv_malloc(capacity)) == NULL) 147 return (ENOMEM); 148 149 /* Copy in the NVRAM image */ 150 if ((error = bhnd_nvram_io_read(src, 0x0, bcm->data, io_size))) 151 return (error); 152 153 /* Process the buffer */ 154 bcm->count = 0; 155 for (offset = 0; offset < bcm->size; offset++) { 156 char *envp; 157 const char *name, *value; 158 size_t envp_len; 159 size_t name_len, value_len; 160 161 /* Parse the key=value string */ 162 envp = (char *) (bcm->data + offset); 163 envp_len = strnlen(envp, bcm->size - offset); 164 error = bhnd_nvram_parse_env(envp, envp_len, '=', &name, 165 &name_len, &value, &value_len); 166 if (error) { 167 BHND_NV_LOG("error parsing envp at offset %#zx: %d\n", 168 offset, error); 169 return (error); 170 } 171 172 /* Insert a '\0' character, replacing the '=' delimiter and 173 * allowing us to vend references directly to the variable 174 * name */ 175 *(envp + name_len) = '\0'; 176 177 /* Add to variable count */ 178 bcm->count++; 179 180 /* Seek past the value's terminating '\0' */ 181 offset += envp_len; 182 if (offset == io_size) { 183 BHND_NV_LOG("missing terminating NUL at offset %#zx\n", 184 offset); 185 return (EINVAL); 186 } 187 188 /* If we hit EOF without finding a terminating NUL 189 * byte, we need to append it */ 190 if (++offset == bcm->size) { 191 BHND_NV_ASSERT(offset < capacity, 192 ("appending past end of buffer")); 193 bcm->size++; 194 *(bcm->data + offset) = '\0'; 195 } 196 197 /* Check for explicit EOF (encoded as a single empty NUL 198 * terminated string) */ 199 if (*(bcm->data + offset) == '\0') 200 break; 201 } 202 203 /* Reclaim any unused space in he backing buffer */ 204 if (offset < bcm->size) { 205 bcm->data = bhnd_nv_reallocf(bcm->data, bcm->size); 206 if (bcm->data == NULL) 207 return (ENOMEM); 208 } 209 210 return (0); 211 } 212 213 static int 214 bhnd_nvram_bcmraw_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io) 215 { 216 struct bhnd_nvram_bcmraw *bcm; 217 int error; 218 219 bcm = (struct bhnd_nvram_bcmraw *)nv; 220 221 /* Parse the BCM input data and initialize our backing 222 * data representation */ 223 if ((error = bhnd_nvram_bcmraw_init(bcm, io))) { 224 bhnd_nvram_bcmraw_free(nv); 225 return (error); 226 } 227 228 return (0); 229 } 230 231 static void 232 bhnd_nvram_bcmraw_free(struct bhnd_nvram_data *nv) 233 { 234 struct bhnd_nvram_bcmraw *bcm = (struct bhnd_nvram_bcmraw *)nv; 235 236 if (bcm->data != NULL) 237 bhnd_nv_free(bcm->data); 238 } 239 240 static size_t 241 bhnd_nvram_bcmraw_count(struct bhnd_nvram_data *nv) 242 { 243 struct bhnd_nvram_bcmraw *bcm = (struct bhnd_nvram_bcmraw *)nv; 244 245 return (bcm->count); 246 } 247 248 static int 249 bhnd_nvram_bcmraw_size(struct bhnd_nvram_data *nv, size_t *size) 250 { 251 return (bhnd_nvram_bcmraw_serialize(nv, NULL, size)); 252 } 253 254 static int 255 bhnd_nvram_bcmraw_serialize(struct bhnd_nvram_data *nv, void *buf, size_t *len) 256 { 257 struct bhnd_nvram_bcmraw *bcm; 258 char * const p = (char *)buf; 259 size_t limit; 260 size_t offset; 261 262 bcm = (struct bhnd_nvram_bcmraw *)nv; 263 264 /* Save the output buffer limit */ 265 if (buf == NULL) 266 limit = 0; 267 else 268 limit = *len; 269 270 /* The serialized form will be exactly the length 271 * of our backing buffer representation */ 272 *len = bcm->size; 273 274 /* Skip serialization if not requested, or report ENOMEM if 275 * buffer is too small */ 276 if (buf == NULL) { 277 return (0); 278 } else if (*len > limit) { 279 return (ENOMEM); 280 } 281 282 /* Write all variables to the output buffer */ 283 memcpy(buf, bcm->data, *len); 284 285 /* Rewrite all '\0' delimiters back to '=' */ 286 offset = 0; 287 while (offset < bcm->size) { 288 size_t name_len, value_len; 289 290 name_len = strlen(p + offset); 291 292 /* EOF? */ 293 if (name_len == 0) { 294 BHND_NV_ASSERT(*(p + offset) == '\0', 295 ("no NUL terminator")); 296 297 offset++; 298 break; 299 } 300 301 /* Rewrite 'name\0' to 'name=' */ 302 offset += name_len; 303 BHND_NV_ASSERT(*(p + offset) == '\0', ("incorrect offset")); 304 305 *(p + offset) = '='; 306 offset++; 307 308 value_len = strlen(p + offset); 309 offset += value_len + 1; 310 } 311 312 return (0); 313 } 314 315 static uint32_t 316 bhnd_nvram_bcmraw_caps(struct bhnd_nvram_data *nv) 317 { 318 return (BHND_NVRAM_DATA_CAP_READ_PTR|BHND_NVRAM_DATA_CAP_DEVPATHS); 319 } 320 321 static const char * 322 bhnd_nvram_bcmraw_next(struct bhnd_nvram_data *nv, void **cookiep) 323 { 324 struct bhnd_nvram_bcmraw *bcm; 325 const char *envp; 326 327 bcm = (struct bhnd_nvram_bcmraw *)nv; 328 329 if (*cookiep == NULL) { 330 /* Start at the first NVRAM data record */ 331 envp = bcm->data; 332 } else { 333 /* Seek to next record */ 334 envp = *cookiep; 335 envp += strlen(envp) + 1; /* key + '\0' */ 336 envp += strlen(envp) + 1; /* value + '\0' */ 337 } 338 339 /* EOF? */ 340 if (*envp == '\0') 341 return (NULL); 342 343 *cookiep = (void *)(uintptr_t)envp; 344 return (envp); 345 } 346 347 static void * 348 bhnd_nvram_bcmraw_find(struct bhnd_nvram_data *nv, const char *name) 349 { 350 return (bhnd_nvram_data_generic_find(nv, name)); 351 } 352 353 static int 354 bhnd_nvram_bcmraw_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf, 355 size_t *len, bhnd_nvram_type type) 356 { 357 return (bhnd_nvram_data_generic_rp_getvar(nv, cookiep, buf, len, type)); 358 } 359 360 static const void * 361 bhnd_nvram_bcmraw_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep, 362 size_t *len, bhnd_nvram_type *type) 363 { 364 const char *envp; 365 366 /* Cookie points to key\0value\0 -- get the value address */ 367 envp = cookiep; 368 envp += strlen(envp) + 1; /* key + '\0' */ 369 *len = strlen(envp) + 1; /* value + '\0' */ 370 *type = BHND_NVRAM_TYPE_STRING; 371 372 return (envp); 373 } 374 375 static const char * 376 bhnd_nvram_bcmraw_getvar_name(struct bhnd_nvram_data *nv, void *cookiep) 377 { 378 /* Cookie points to key\0value\0 */ 379 return (cookiep); 380 } 381