1 /*- 2 * Copyright (c) 2015 Landon Fuller <landon@landonf.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 #include <sys/param.h> 34 #include <sys/bus.h> 35 #include <sys/endian.h> 36 #include <sys/rman.h> 37 #include <sys/systm.h> 38 39 #include <machine/bus.h> 40 #include <machine/resource.h> 41 42 #include <dev/bhnd/bhndvar.h> 43 44 #include "nvramvar.h" 45 46 #include "bhnd_spromreg.h" 47 #include "bhnd_spromvar.h" 48 49 /* 50 * BHND SPROM Parsing 51 * 52 * Provides identification and parsing of BHND SPROM data. 53 */ 54 55 static int sprom_direct_read(struct bhnd_sprom *sc, size_t offset, 56 void *buf, size_t nbytes, uint8_t *crc); 57 static int sprom_extend_shadow(struct bhnd_sprom *sc, size_t image_size, 58 uint8_t *crc); 59 static int sprom_populate_shadow(struct bhnd_sprom *sc); 60 61 static int sprom_var_defn(struct bhnd_sprom *sc, const char *name, 62 const struct bhnd_nvram_var **var, 63 const struct bhnd_sprom_var **sprom, size_t *size); 64 65 /* SPROM revision is always located at the second-to-last byte */ 66 #define SPROM_REV(_sc) SPROM_READ_1((_sc), (_sc)->sp_size - 2) 67 68 /* SPROM CRC is always located at the last byte */ 69 #define SPROM_CRC_OFF(_sc) SPROM_CRC_LEN(_sc) 70 71 /* SPROM CRC covers all but the final CRC byte */ 72 #define SPROM_CRC_LEN(_sc) ((_sc)->sp_size - 1) 73 74 /* SPROM shadow I/O (with byte-order translation) */ 75 #define SPROM_READ_1(_sc, _off) SPROM_READ_ENC_1(_sc, _off) 76 #define SPROM_READ_2(_sc, _off) le16toh(SPROM_READ_ENC_2(_sc, _off)) 77 #define SPROM_READ_4(_sc, _off) le32toh(SPROM_READ_ENC_4(_sc, _off)) 78 79 #define SPROM_WRITE_1(_sc, _off, _v) SPROM_WRITE_ENC_1(_sc, _off, (_v)) 80 #define SPROM_WRITE_2(_sc, _off, _v) SPROM_WRITE_ENC_2(_sc, _off, \ 81 htole16(_v)) 82 #define SPROM_WRITE_4(_sc, _off, _v) SPROM_WRITE_ENC_4(_sc, _off, \ 83 htole32(_v)) 84 85 /* SPROM shadow I/O (without byte-order translation) */ 86 #define SPROM_READ_ENC_1(_sc, _off) (*(uint8_t *)((_sc)->sp_shadow + _off)) 87 #define SPROM_READ_ENC_2(_sc, _off) (*(uint16_t *)((_sc)->sp_shadow + _off)) 88 #define SPROM_READ_ENC_4(_sc, _off) (*(uint32_t *)((_sc)->sp_shadow + _off)) 89 90 #define SPROM_WRITE_ENC_1(_sc, _off, _v) \ 91 *((uint8_t *)((_sc)->sp_shadow + _off)) = (_v) 92 #define SPROM_WRITE_ENC_2(_sc, _off, _v) \ 93 *((uint16_t *)((_sc)->sp_shadow + _off)) = (_v) 94 #define SPROM_WRITE_ENC_4(_sc, _off, _v) \ 95 *((uint32_t *)((_sc)->sp_shadow + _off)) = (_v) 96 97 /* Call @p _next macro with the C type, widened (signed or unsigned) C 98 * type, and width associated with @p _dtype */ 99 #define SPROM_SWITCH_TYPE(_dtype, _next, ...) \ 100 do { \ 101 switch (_dtype) { \ 102 case BHND_NVRAM_DT_UINT8: \ 103 _next (uint8_t, uint32_t, 1, \ 104 ## __VA_ARGS__); \ 105 break; \ 106 case BHND_NVRAM_DT_UINT16: \ 107 _next (uint16_t, uint32_t, 2, \ 108 ## __VA_ARGS__); \ 109 break; \ 110 case BHND_NVRAM_DT_UINT32: \ 111 _next (uint32_t, uint32_t, 4, \ 112 ## __VA_ARGS__); \ 113 break; \ 114 case BHND_NVRAM_DT_INT8: \ 115 _next (int8_t, int32_t, 1, \ 116 ## __VA_ARGS__); \ 117 break; \ 118 case BHND_NVRAM_DT_INT16: \ 119 _next (int16_t, int32_t, 2, \ 120 ## __VA_ARGS__); \ 121 break; \ 122 case BHND_NVRAM_DT_INT32: \ 123 _next (int32_t, int32_t, 4, \ 124 ## __VA_ARGS__); \ 125 break; \ 126 case BHND_NVRAM_DT_CHAR: \ 127 _next (uint8_t, uint32_t, 1, \ 128 ## __VA_ARGS__); \ 129 break; \ 130 } \ 131 } while (0) 132 133 /* 134 * Table of supported SPROM image formats, sorted by image size, ascending. 135 */ 136 #define SPROM_FMT(_sz, _revmin, _revmax, _sig) \ 137 { SPROM_SZ_ ## _sz, _revmin, _revmax, \ 138 SPROM_SIG_ ## _sig ## _OFF, \ 139 SPROM_SIG_ ## _sig } 140 141 static const struct sprom_fmt { 142 size_t size; 143 uint8_t rev_min; 144 uint8_t rev_max; 145 size_t sig_offset; 146 uint16_t sig_req; 147 } sprom_fmts[] = { 148 SPROM_FMT(R1_3, 1, 3, NONE), 149 SPROM_FMT(R4_8_9, 4, 4, R4), 150 SPROM_FMT(R4_8_9, 8, 9, R8_9), 151 SPROM_FMT(R10, 10, 10, R10), 152 SPROM_FMT(R11, 11, 11, R11) 153 }; 154 155 /** 156 * Identify the SPROM format at @p offset within @p r, verify the CRC, 157 * and allocate a local shadow copy of the SPROM data. 158 * 159 * After successful initialization, @p r will not be accessed; any pin 160 * configuration required for SPROM access may be reset. 161 * 162 * @param[out] sprom On success, will be initialized with shadow of the SPROM 163 * data. 164 * @param r An active resource mapping the SPROM data. 165 * @param offset Offset of the SPROM data within @p resource. 166 */ 167 int 168 bhnd_sprom_init(struct bhnd_sprom *sprom, struct bhnd_resource *r, 169 bus_size_t offset) 170 { 171 bus_size_t res_size; 172 int error; 173 174 sprom->dev = rman_get_device(r->res); 175 sprom->sp_res = r; 176 sprom->sp_res_off = offset; 177 178 /* Determine maximum possible SPROM image size */ 179 res_size = rman_get_size(r->res); 180 if (offset >= res_size) 181 return (EINVAL); 182 183 sprom->sp_size_max = MIN(res_size - offset, SPROM_SZ_MAX); 184 185 /* Allocate and populate SPROM shadow */ 186 sprom->sp_size = 0; 187 sprom->sp_capacity = sprom->sp_size_max; 188 sprom->sp_shadow = malloc(sprom->sp_capacity, M_BHND, M_NOWAIT); 189 if (sprom->sp_shadow == NULL) 190 return (ENOMEM); 191 192 /* Read and identify SPROM image */ 193 if ((error = sprom_populate_shadow(sprom))) 194 return (error); 195 196 return (0); 197 } 198 199 /** 200 * Release all resources held by @p sprom. 201 * 202 * @param sprom A SPROM instance previously initialized via bhnd_sprom_init(). 203 */ 204 void 205 bhnd_sprom_fini(struct bhnd_sprom *sprom) 206 { 207 free(sprom->sp_shadow, M_BHND); 208 } 209 210 /* Perform a read using a SPROM offset descriptor, safely widening the 211 * result to its 32-bit representation before assigning it to @p _dest. */ 212 #define SPROM_GETVAR_READ(_type, _widen, _width, _sc, _off, _dest) \ 213 do { \ 214 _type _v = (_type)SPROM_READ_ ## _width(_sc, _off->offset); \ 215 if (_off->shift > 0) { \ 216 _v >>= _off->shift; \ 217 } else if (off->shift < 0) { \ 218 _v <<= -_off->shift; \ 219 } \ 220 _dest = ((uint32_t) (_widen) _v) & _off->mask; \ 221 } while(0) 222 223 /* Emit a value read using a SPROM offset descriptor, narrowing the 224 * result output representation and, if necessary, OR'ing it with the 225 * previously read value from @p _buf. */ 226 #define SPROM_GETVAR_WRITE(_type, _widen, _width, _off, _src, _buf) \ 227 do { \ 228 _type _v = (_type) (_widen) _src; \ 229 if (_off->cont) \ 230 _v |= *((_type *)_buf); \ 231 *((_type *)_buf) = _v; \ 232 } while(0) 233 234 /** 235 * Read a SPROM variable, performing conversion to host byte order. 236 * 237 * @param sc The SPROM parser state. 238 * @param name The SPROM variable name. 239 * @param[out] buf On success, the requested value will be written 240 * to this buffer. This argment may be NULL if 241 * the value is not desired. 242 * @param[in,out] len The capacity of @p buf. On success, will be set 243 * to the actual size of the requested value. 244 * 245 * @retval 0 success 246 * @retval ENOENT The requested variable was not found. 247 * @retval ENOMEM If @p buf is non-NULL and a buffer of @p len is too 248 * small to hold the requested value. 249 * @retval non-zero If reading @p name otherwise fails, a regular unix 250 * error code will be returned. 251 */ 252 int 253 bhnd_sprom_getvar(struct bhnd_sprom *sc, const char *name, void *buf, 254 size_t *len) 255 { 256 const struct bhnd_nvram_var *nv; 257 const struct bhnd_sprom_var *sv; 258 size_t all1_offs; 259 size_t req_size; 260 int error; 261 262 if ((error = sprom_var_defn(sc, name, &nv, &sv, &req_size))) 263 return (error); 264 265 /* Provide required size */ 266 if (buf == NULL) { 267 *len = req_size; 268 return (0); 269 } 270 271 /* Check (and update) target buffer len */ 272 if (*len < req_size) 273 return (ENOMEM); 274 else 275 *len = req_size; 276 277 /* Read data */ 278 all1_offs = 0; 279 for (size_t i = 0; i < sv->num_offsets; i++) { 280 const struct bhnd_sprom_offset *off; 281 uint32_t val; 282 283 off = &sv->offsets[i]; 284 KASSERT(!off->cont || i > 0, ("cont marked on first offset")); 285 286 /* If not a continuation, advance the output buffer */ 287 if (i > 0 && !off->cont) { 288 buf = ((uint8_t *)buf) + 289 bhnd_nvram_type_width(sv->offsets[i-1].type); 290 } 291 292 /* Read the value, widening to a common uint32 293 * representation */ 294 SPROM_SWITCH_TYPE(off->type, SPROM_GETVAR_READ, sc, off, val); 295 296 /* If IGNALL1, record whether value has all bits set. */ 297 if (nv->flags & BHND_NVRAM_VF_IGNALL1) { 298 uint32_t all1; 299 300 all1 = off->mask; 301 if (off->shift > 0) 302 all1 >>= off->shift; 303 else if (off->shift < 0) 304 all1 <<= -off->shift; 305 306 if ((val & all1) == all1) 307 all1_offs++; 308 } 309 310 /* Write the value, narrowing to the appropriate output 311 * width. */ 312 SPROM_SWITCH_TYPE(nv->type, SPROM_GETVAR_WRITE, off, val, buf); 313 } 314 315 /* Should value should be treated as uninitialized? */ 316 if (nv->flags & BHND_NVRAM_VF_IGNALL1 && all1_offs == sv->num_offsets) 317 return (ENOENT); 318 319 return (0); 320 } 321 322 /* Perform a read of a variable offset from _src, safely widening the result 323 * to its 32-bit representation before assigning it to @p 324 * _dest. */ 325 #define SPROM_SETVAR_READ(_type, _widen, _width, _off, _src, _dest) \ 326 do { \ 327 _type _v = *(const _type *)_src; \ 328 if (_off->shift > 0) { \ 329 _v <<= _off->shift; \ 330 } else if (off->shift < 0) { \ 331 _v >>= -_off->shift; \ 332 } \ 333 _dest = ((uint32_t) (_widen) _v) & _off->mask; \ 334 } while(0) 335 336 337 /* Emit a value read using a SPROM offset descriptor, narrowing the 338 * result output representation and, if necessary, OR'ing it with the 339 * previously read value from @p _buf. */ 340 #define SPROM_SETVAR_WRITE(_type, _widen, _width, _sc, _off, _src) \ 341 do { \ 342 _type _v = (_type) (_widen) _src; \ 343 if (_off->cont) \ 344 _v |= SPROM_READ_ ## _width(_sc, _off->offset); \ 345 SPROM_WRITE_ ## _width(_sc, _off->offset, _v); \ 346 } while(0) 347 348 /** 349 * Set a local value for a SPROM variable, performing conversion to SPROM byte 350 * order. 351 * 352 * The new value will be written to the backing SPROM shadow. 353 * 354 * @param sc The SPROM parser state. 355 * @param name The SPROM variable name. 356 * @param[out] buf The new value. 357 * @param[in,out] len The size of @p buf. 358 * 359 * @retval 0 success 360 * @retval ENOENT The requested variable was not found. 361 * @retval EINVAL If @p len does not match the expected variable size. 362 */ 363 int 364 bhnd_sprom_setvar(struct bhnd_sprom *sc, const char *name, const void *buf, 365 size_t len) 366 { 367 const struct bhnd_nvram_var *nv; 368 const struct bhnd_sprom_var *sv; 369 size_t req_size; 370 int error; 371 uint8_t crc; 372 373 if ((error = sprom_var_defn(sc, name, &nv, &sv, &req_size))) 374 return (error); 375 376 /* Provide required size */ 377 if (len != req_size) 378 return (EINVAL); 379 380 /* Write data */ 381 for (size_t i = 0; i < sv->num_offsets; i++) { 382 const struct bhnd_sprom_offset *off; 383 uint32_t val; 384 385 off = &sv->offsets[i]; 386 KASSERT(!off->cont || i > 0, ("cont marked on first offset")); 387 388 /* If not a continuation, advance the input pointer */ 389 if (i > 0 && !off->cont) { 390 buf = ((const uint8_t *)buf) + 391 bhnd_nvram_type_width(sv->offsets[i-1].type); 392 } 393 394 /* Read the value, widening to a common uint32 395 * representation */ 396 SPROM_SWITCH_TYPE(nv->type, SPROM_SETVAR_READ, off, buf, val); 397 398 /* Write the value, narrowing to the appropriate output 399 * width. */ 400 SPROM_SWITCH_TYPE(off->type, SPROM_SETVAR_WRITE, sc, off, val); 401 } 402 403 /* Update CRC */ 404 crc = ~bhnd_nvram_crc8(sc->sp_shadow, SPROM_CRC_LEN(sc), 405 BHND_NVRAM_CRC8_INITIAL); 406 SPROM_WRITE_1(sc, SPROM_CRC_OFF(sc), crc); 407 408 return (0); 409 } 410 411 /* Read and identify the SPROM image by incrementally performing 412 * read + CRC of all supported image formats */ 413 static int 414 sprom_populate_shadow(struct bhnd_sprom *sc) 415 { 416 const struct sprom_fmt *fmt; 417 int error; 418 uint16_t sig; 419 uint8_t srom_rev; 420 uint8_t crc; 421 422 crc = BHND_NVRAM_CRC8_INITIAL; 423 424 /* Identify the SPROM revision (and populate the SPROM shadow) */ 425 for (size_t i = 0; i < nitems(sprom_fmts); i++) { 426 fmt = &sprom_fmts[i]; 427 428 /* Read image data and check CRC */ 429 if ((error = sprom_extend_shadow(sc, fmt->size, &crc))) 430 return (error); 431 432 /* Skip on invalid CRC */ 433 if (crc != BHND_NVRAM_CRC8_VALID) 434 continue; 435 436 /* Fetch SROM revision */ 437 srom_rev = SPROM_REV(sc); 438 439 /* Early sromrev 1 devices (specifically some BCM440x enet 440 * cards) are reported to have been incorrectly programmed 441 * with a revision of 0x10. */ 442 if (fmt->size == SPROM_SZ_R1_3 && srom_rev == 0x10) 443 srom_rev = 0x1; 444 445 /* Verify revision range */ 446 if (srom_rev < fmt->rev_min || srom_rev > fmt->rev_max) 447 continue; 448 449 /* Verify signature (if any) */ 450 sig = SPROM_SIG_NONE; 451 if (fmt->sig_offset != SPROM_SIG_NONE_OFF) 452 sig = SPROM_READ_2(sc, fmt->sig_offset); 453 454 if (sig != fmt->sig_req) { 455 device_printf(sc->dev, 456 "invalid sprom %hhu signature: 0x%hx " 457 "(expected 0x%hx)\n", 458 srom_rev, sig, fmt->sig_req); 459 return (EINVAL); 460 } 461 462 /* Identified */ 463 sc->sp_rev = srom_rev; 464 return (0); 465 } 466 467 /* identification failed */ 468 device_printf(sc->dev, "unrecognized sprom format\n"); 469 return (EINVAL); 470 } 471 472 /* 473 * Extend the shadowed SPROM buffer to image_size, reading any required 474 * data from the backing SPROM resource and updating the CRC. 475 */ 476 static int 477 sprom_extend_shadow(struct bhnd_sprom *sc, size_t image_size, 478 uint8_t *crc) 479 { 480 int error; 481 482 KASSERT(image_size >= sc->sp_size, (("shadow truncation unsupported"))); 483 484 /* Verify the request fits within our shadow buffer */ 485 if (image_size > sc->sp_capacity) 486 return (ENOSPC); 487 488 /* Skip no-op requests */ 489 if (sc->sp_size == image_size) 490 return (0); 491 492 /* Populate the extended range */ 493 error = sprom_direct_read(sc, sc->sp_size, sc->sp_shadow + sc->sp_size, 494 image_size - sc->sp_size, crc); 495 if (error) 496 return (error); 497 498 sc->sp_size = image_size; 499 return (0); 500 } 501 502 /** 503 * Read nbytes at the given offset from the backing SPROM resource, and 504 * update the CRC. 505 */ 506 static int 507 sprom_direct_read(struct bhnd_sprom *sc, size_t offset, void *buf, 508 size_t nbytes, uint8_t *crc) 509 { 510 bus_size_t res_offset; 511 uint16_t *p; 512 513 KASSERT(nbytes % sizeof(uint16_t) == 0, ("unaligned sprom size")); 514 KASSERT(offset % sizeof(uint16_t) == 0, ("unaligned sprom offset")); 515 516 /* Check for read overrun */ 517 if (offset >= sc->sp_size_max || sc->sp_size_max - offset < nbytes) { 518 device_printf(sc->dev, "requested SPROM read would overrun\n"); 519 return (EINVAL); 520 } 521 522 /* Perform read and update CRC */ 523 p = (uint16_t *)buf; 524 res_offset = sc->sp_res_off + offset; 525 526 bhnd_bus_read_region_stream_2(sc->sp_res, res_offset, p, nbytes); 527 *crc = bhnd_nvram_crc8(p, nbytes, *crc); 528 529 return (0); 530 } 531 532 533 /** 534 * Locate the variable and SPROM revision-specific definitions 535 * for variable with @p name. 536 */ 537 static int 538 sprom_var_defn(struct bhnd_sprom *sc, const char *name, 539 const struct bhnd_nvram_var **var, 540 const struct bhnd_sprom_var **sprom, 541 size_t *size) 542 { 543 /* Find variable definition */ 544 *var = bhnd_nvram_var_defn(name); 545 if (*var == NULL) 546 return (ENOENT); 547 548 /* Find revision-specific SPROM definition */ 549 for (size_t i = 0; i < (*var)->num_sp_descs; i++) { 550 const struct bhnd_sprom_var *sp = &(*var)->sprom_descs[i]; 551 552 if (sc->sp_rev < sp->compat.first) 553 continue; 554 555 if (sc->sp_rev > sp->compat.last) 556 continue; 557 558 /* Found */ 559 *sprom = sp; 560 561 /* Calculate size in bytes */ 562 *size = bhnd_nvram_type_width((*var)->type) * sp->num_offsets; 563 return (0); 564 } 565 566 /* Not supported by this SPROM revision */ 567 return (ENOENT); 568 } 569