2 * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
33 #include <sys/param.h>
35 #include <sys/endian.h>
36 #include <sys/limits.h>
38 #include <sys/systm.h>
40 #include <machine/bus.h>
41 #include <machine/resource.h>
43 #include "bhnd_nvram_common.h"
45 #include "bhnd_sprom_parservar.h"
50 * Provides identification, decoding, and encoding of BHND SPROM data.
53 static int sprom_direct_read(struct bhnd_sprom *sc, size_t offset,
54 void *buf, size_t nbytes, uint8_t *crc);
55 static int sprom_extend_shadow(struct bhnd_sprom *sc,
56 size_t image_size, uint8_t *crc);
57 static int sprom_populate_shadow(struct bhnd_sprom *sc);
59 static int sprom_get_var_defn(struct bhnd_sprom *sc,
61 const struct bhnd_nvram_vardefn **var,
62 const struct bhnd_sprom_vardefn **sprom,
63 size_t *size, size_t *nelem,
64 bhnd_nvram_type req_type);
66 static char sprom_get_delim_char(struct bhnd_sprom *sc,
67 bhnd_nvram_sfmt sfmt);
69 /* SPROM revision is always located at the second-to-last byte */
70 #define SPROM_REV(_sc) SPROM_READ_1((_sc), (_sc)->sp_size - 2)
72 /* SPROM CRC is always located at the last byte */
73 #define SPROM_CRC_OFF(_sc) SPROM_CRC_LEN(_sc)
75 /* SPROM CRC covers all but the final CRC byte */
76 #define SPROM_CRC_LEN(_sc) ((_sc)->sp_size - 1)
78 /* SPROM shadow I/O (with byte-order translation) */
79 #define SPROM_READ_1(_sc, _off) SPROM_READ_ENC_1(_sc, _off)
80 #define SPROM_READ_2(_sc, _off) le16toh(SPROM_READ_ENC_2(_sc, _off))
81 #define SPROM_READ_4(_sc, _off) le32toh(SPROM_READ_ENC_4(_sc, _off))
83 #define SPROM_WRITE_1(_sc, _off, _v) SPROM_WRITE_ENC_1(_sc, _off, (_v))
84 #define SPROM_WRITE_2(_sc, _off, _v) SPROM_WRITE_ENC_2(_sc, _off, \
86 #define SPROM_WRITE_4(_sc, _off, _v) SPROM_WRITE_ENC_4(_sc, _off, \
89 /* SPROM shadow I/O (without byte-order translation) */
90 #define SPROM_READ_ENC_1(_sc, _off) (*(uint8_t *)((_sc)->sp_shadow + _off))
91 #define SPROM_READ_ENC_2(_sc, _off) (*(uint16_t *)((_sc)->sp_shadow + _off))
92 #define SPROM_READ_ENC_4(_sc, _off) (*(uint32_t *)((_sc)->sp_shadow + _off))
94 #define SPROM_WRITE_ENC_1(_sc, _off, _v) \
95 *((uint8_t *)((_sc)->sp_shadow + _off)) = (_v)
96 #define SPROM_WRITE_ENC_2(_sc, _off, _v) \
97 *((uint16_t *)((_sc)->sp_shadow + _off)) = (_v)
98 #define SPROM_WRITE_ENC_4(_sc, _off, _v) \
99 *((uint32_t *)((_sc)->sp_shadow + _off)) = (_v)
101 /* Call @p _next macro with the C type, widened (signed or unsigned) 32-bit C
102 * type, width, and min/max values associated with @p _dtype */
103 #define SPROM_SWITCH_TYPE(_dtype, _next, ...) \
106 case BHND_NVRAM_TYPE_UINT8: \
107 _next (uint8_t, uint32_t, 1, 0, \
108 UINT8_MAX, ## __VA_ARGS__); \
110 case BHND_NVRAM_TYPE_UINT16: \
111 _next (uint16_t, uint32_t, 2, 0, \
112 UINT16_MAX, ## __VA_ARGS__); \
114 case BHND_NVRAM_TYPE_UINT32: \
115 _next (uint32_t, uint32_t, 4, 0, \
116 UINT32_MAX, ## __VA_ARGS__); \
118 case BHND_NVRAM_TYPE_INT8: \
119 _next (int8_t, int32_t, 1, \
120 INT8_MIN, INT8_MAX, ## __VA_ARGS__); \
122 case BHND_NVRAM_TYPE_INT16: \
123 _next (int16_t, int32_t, 2, \
124 INT16_MIN, INT16_MAX, ## __VA_ARGS__); \
126 case BHND_NVRAM_TYPE_INT32: \
127 _next (int32_t, int32_t, 4, \
128 INT32_MIN, INT32_MAX, ## __VA_ARGS__); \
130 case BHND_NVRAM_TYPE_CHAR: \
131 _next (char, int32_t, 1, \
132 CHAR_MIN, CHAR_MAX, ## __VA_ARGS__); \
134 case BHND_NVRAM_TYPE_CSTR: \
135 panic("%s: BHND_NVRAM_TYPE_CSTR unhandled", \
142 /* Verify the range of _val of (_stype) within _type */
143 #define SPROM_VERIFY_RANGE(_type, _widen, _width, _min, _max, _val, \
146 if (BHND_NVRAM_SIGNED_TYPE(_stype)) { \
147 int32_t sval = (int32_t) (_val); \
148 if (sval > (_max) || sval < (_min)) \
151 if ((_val) > (_max)) \
157 * Table of supported SPROM image formats, sorted by image size, ascending.
159 #define SPROM_FMT(_sz, _revmin, _revmax, _sig) \
160 { SPROM_SZ_ ## _sz, _revmin, _revmax, \
161 SPROM_SIG_ ## _sig ## _OFF, \
164 static const struct sprom_fmt {
171 SPROM_FMT(R1_3, 1, 3, NONE),
172 SPROM_FMT(R4_8_9, 4, 4, R4),
173 SPROM_FMT(R4_8_9, 8, 9, R8_9),
174 SPROM_FMT(R10, 10, 10, R10),
175 SPROM_FMT(R11, 11, 11, R11)
179 * Identify the SPROM format at @p offset within @p r, verify the CRC,
180 * and allocate a local shadow copy of the SPROM data.
182 * After successful initialization, @p r will not be accessed; any pin
183 * configuration required for SPROM access may be reset.
185 * @param[out] sprom On success, will be initialized with shadow of the SPROM
187 * @param r An active resource mapping the SPROM data.
188 * @param offset Offset of the SPROM data within @p resource.
191 bhnd_sprom_init(struct bhnd_sprom *sprom, struct bhnd_resource *r,
197 sprom->dev = rman_get_device(r->res);
199 sprom->sp_res_off = offset;
201 /* Determine maximum possible SPROM image size */
202 res_size = rman_get_size(r->res);
203 if (offset >= res_size)
206 sprom->sp_size_max = MIN(res_size - offset, SPROM_SZ_MAX);
208 /* Allocate and populate SPROM shadow */
210 sprom->sp_capacity = sprom->sp_size_max;
211 sprom->sp_shadow = malloc(sprom->sp_capacity, M_BHND_NVRAM, M_NOWAIT);
212 if (sprom->sp_shadow == NULL)
215 /* Read and identify SPROM image */
216 if ((error = sprom_populate_shadow(sprom)))
223 * Release all resources held by @p sprom.
225 * @param sprom A SPROM instance previously initialized via bhnd_sprom_init().
228 bhnd_sprom_fini(struct bhnd_sprom *sprom)
230 free(sprom->sp_shadow, M_BHND_NVRAM);
233 /* Perform a read using a SPROM offset descriptor, safely widening the result
234 * to its 32-bit representation before assigning it to @p _dest. */
235 #define SPROM_GETVAR_READ(_type, _widen, _width, _min, _max, _sc, _off, \
238 _type _v = (_type)SPROM_READ_ ## _width(_sc, _off->offset); \
239 if (_off->shift > 0) { \
240 _v >>= _off->shift; \
241 } else if (off->shift < 0) { \
242 _v <<= -_off->shift; \
246 _dest |= ((uint32_t) (_widen) _v) & _off->mask; \
248 _dest = ((uint32_t) (_widen) _v) & _off->mask; \
251 /* Emit a value read using a SPROM offset descriptor, narrowing the
252 * result output representation. */
253 #define SPROM_GETVAR_WRITE(_type, _widen, _width, _min, _max, _off, \
256 _type _v = (_type) (_widen) _src; \
257 *((_type *)_buf) = _v; \
260 /* String format a value read using a SPROM offset descriptor */
261 #define SPROM_GETVAR_SNPRINTF(_type, _widen, _width, _min, _max, _src, \
262 _buf, _remain, _fmt, _nwrite) \
264 _nwrite = snprintf(_buf, _remain, _fmt, (_type) (_widen) _src); \
268 * Read a SPROM variable, performing conversion to host byte order.
270 * @param sc The SPROM parser state.
271 * @param name The SPROM variable name.
272 * @param[out] buf On success, the requested value will be written
273 * to this buffer. This argment may be NULL if
274 * the value is not desired.
275 * @param[in,out] len The capacity of @p buf. On success, will be set
276 * to the actual size of the requested value.
277 * @param type The requested data type to be written to @p buf.
280 * @retval ENOENT The requested variable was not found.
281 * @retval ENOMEM If @p buf is non-NULL and a buffer of @p len is too
282 * small to hold the requested value.
283 * @retval non-zero If reading @p name otherwise fails, a regular unix
284 * error code will be returned.
287 bhnd_sprom_getvar(struct bhnd_sprom *sc, const char *name, void *buf,
288 size_t *len, bhnd_nvram_type type)
290 const struct bhnd_nvram_vardefn *nv;
291 const struct bhnd_sprom_vardefn *sv;
294 size_t req_size, nelem;
300 error = sprom_get_var_defn(sc, name, &nv, &sv, &req_size, &nelem, type);
309 if (type != BHND_NVRAM_TYPE_CSTR) {
310 /* Provide required size */
316 /* Check (and update) target buffer len */
322 /* String length calculation requires performing
323 * the actual string formatting */
324 KASSERT(req_size == 0,
325 ("req_size set for variable-length type"));
327 /* If caller is querying length, the len argument
328 * may be uninitialized */
332 /* Fetch delimiter for the variable's string format */
333 str_delim = sprom_get_delim_char(sc, nv->sfmt);
339 for (size_t i = 0; i < sv->num_offsets; i++) {
340 const struct bhnd_sprom_offset *off;
342 off = &sv->offsets[i];
343 KASSERT(!off->cont || i > 0, ("cont marked on first offset"));
345 /* If not a continuation, advance the output buffer; if
346 * a C string, this requires appending a delimiter character */
347 if (i > 0 && !off->cont) {
348 size_t width = bhnd_nvram_type_width(type);
350 /* Non-fixed width types (such as CSTR) will have a 0
353 KASSERT(outp != NULL, ("NULL output buffer"));
354 outp = ((uint8_t *)outp) + width;
357 /* Append CSTR delim, if necessary */
358 if (type == BHND_NVRAM_TYPE_CSTR &&
362 if (outp != NULL && str_remain >= 1) {
363 *((char *)outp) = str_delim;
364 outp = ((char *)outp + 1);
366 /* Drop outp reference if we hit 0 */
367 if (str_remain-- == 0)
371 if (SIZE_MAX - 1 < req_size)
372 return (EFTYPE); /* too long */
377 /* Read the value, widening to a common uint32
379 SPROM_SWITCH_TYPE(off->type, SPROM_GETVAR_READ, sc, off, val);
381 /* If IGNALL1, record whether value has all bits set. */
382 if (nv->flags & BHND_NVRAM_VF_IGNALL1) {
388 else if (off->shift < 0)
389 all1 <<= -off->shift;
391 if ((val & all1) == all1)
395 /* Skip writing if additional continuations remain */
396 if (i+1 < sv->num_offsets && sv->offsets[i].cont)
400 if (type == BHND_NVRAM_TYPE_CSTR) {
404 fmtstr = bhnd_nvram_type_fmt(off->type, nv->sfmt, i);
405 if (fmtstr == NULL) {
406 device_printf(sc->dev, "no NVRAM format string "
407 "for '%s' (type=%d)\n", name, off->type);
411 SPROM_SWITCH_TYPE(off->type, SPROM_GETVAR_SNPRINTF, val,
412 outp, str_remain, fmtstr, written);
417 /* Calculate remaining capacity, drop outp reference
418 * if we hit 0 -- otherwise, advance the buffer
420 if (written >= str_remain) {
424 str_remain -= written;
426 outp = (char *)outp + written;
429 /* Add additional bytes to total length */
430 if (SIZE_MAX - written < req_size)
431 return (EFTYPE); /* string too long */
435 SPROM_SWITCH_TYPE(type, SPROM_VERIFY_RANGE, val,
438 /* Write the value, narrowing to the appropriate output
440 SPROM_SWITCH_TYPE(type, SPROM_GETVAR_WRITE, off, val,
445 /* Should value should be treated as uninitialized? */
446 if (nv->flags & BHND_NVRAM_VF_IGNALL1 && all1_offs == sv->num_offsets)
449 /* If this is a C string request, we need to provide the computed
451 if (type == BHND_NVRAM_TYPE_CSTR) {
452 /* Account for final trailing NUL */
453 if (SIZE_MAX - 1 < req_size)
454 return (EFTYPE); /* string too long */
457 /* Return an error if a too-small output buffer was provided */
458 if (buf != NULL && *len < req_size) {
469 /* Perform a read of a variable offset from _src, safely widening the result
470 * to its 32-bit representation before assigning it to @p _dest. */
471 #define SPROM_SETVAR_READ(_type, _widen, _width, _min, _max, _off, \
474 _type _v = *(const _type *)_src; \
475 if (_off->shift > 0) { \
476 _v <<= _off->shift; \
477 } else if (off->shift < 0) { \
478 _v >>= -_off->shift; \
480 _dest = ((uint32_t) (_widen) _v) & _off->mask; \
484 /* Emit a value read using a SPROM offset descriptor, narrowing the
485 * result output representation and, if necessary, OR'ing it with the
486 * previously read value from @p _buf. */
487 #define SPROM_SETVAR_WRITE(_type, _widen, _width, _min, _max, _sc, \
490 _type _v = (_type) (_widen) _src; \
492 _v |= SPROM_READ_ ## _width(_sc, _off->offset); \
493 SPROM_WRITE_ ## _width(_sc, _off->offset, _v); \
497 * Set a local value for a SPROM variable, performing conversion to SPROM byte
500 * The new value will be written to the backing SPROM shadow.
502 * @param sc The SPROM parser state.
503 * @param name The SPROM variable name.
504 * @param[out] buf The new value.
505 * @param[in,out] len The size of @p buf.
506 * @param type The data type of @p buf.
509 * @retval ENOENT The requested variable was not found.
510 * @retval EINVAL If @p len does not match the expected variable size.
513 bhnd_sprom_setvar(struct bhnd_sprom *sc, const char *name, const void *buf,
514 size_t len, bhnd_nvram_type type)
516 const struct bhnd_nvram_vardefn *nv;
517 const struct bhnd_sprom_vardefn *sv;
518 size_t req_size, nelem;
522 error = sprom_get_var_defn(sc, name, &nv, &sv, &req_size, &nelem, type);
526 /* String parsing is currently unsupported */
527 if (type == BHND_NVRAM_TYPE_CSTR)
530 /* Provide required size */
535 for (size_t i = 0; i < sv->num_offsets; i++) {
536 const struct bhnd_sprom_offset *off;
539 off = &sv->offsets[i];
540 KASSERT(!off->cont || i > 0, ("cont marked on first offset"));
542 /* If not a continuation, advance the input pointer */
543 if (i > 0 && !off->cont) {
544 buf = ((const uint8_t *)buf) +
545 bhnd_nvram_type_width(sv->offsets[i-1].type);
548 /* Read the value, widening to a common uint32
550 SPROM_SWITCH_TYPE(nv->type, SPROM_SETVAR_READ, off, buf, val);
553 SPROM_SWITCH_TYPE(nv->type, SPROM_VERIFY_RANGE, val, type);
555 /* Write the value, narrowing to the appropriate output
557 SPROM_SWITCH_TYPE(off->type, SPROM_SETVAR_WRITE, sc, off, val);
561 crc = ~bhnd_nvram_crc8(sc->sp_shadow, SPROM_CRC_LEN(sc),
562 BHND_NVRAM_CRC8_INITIAL);
563 SPROM_WRITE_1(sc, SPROM_CRC_OFF(sc), crc);
568 /* Read and identify the SPROM image by incrementally performing
569 * read + CRC of all supported image formats */
571 sprom_populate_shadow(struct bhnd_sprom *sc)
573 const struct sprom_fmt *fmt;
579 crc = BHND_NVRAM_CRC8_INITIAL;
581 /* Identify the SPROM revision (and populate the SPROM shadow) */
582 for (size_t i = 0; i < nitems(sprom_fmts); i++) {
583 fmt = &sprom_fmts[i];
585 /* Read image data and check CRC */
586 if ((error = sprom_extend_shadow(sc, fmt->size, &crc)))
589 /* Skip on invalid CRC */
590 if (crc != BHND_NVRAM_CRC8_VALID)
593 /* Fetch SROM revision */
594 srom_rev = SPROM_REV(sc);
596 /* Early sromrev 1 devices (specifically some BCM440x enet
597 * cards) are reported to have been incorrectly programmed
598 * with a revision of 0x10. */
599 if (fmt->size == SPROM_SZ_R1_3 && srom_rev == 0x10)
602 /* Verify revision range */
603 if (srom_rev < fmt->rev_min || srom_rev > fmt->rev_max)
606 /* Verify signature (if any) */
607 sig = SPROM_SIG_NONE;
608 if (fmt->sig_offset != SPROM_SIG_NONE_OFF)
609 sig = SPROM_READ_2(sc, fmt->sig_offset);
611 if (sig != fmt->sig_req) {
612 device_printf(sc->dev,
613 "invalid sprom %hhu signature: 0x%hx "
614 "(expected 0x%hx)\n",
615 srom_rev, sig, fmt->sig_req);
620 sc->sp_rev = srom_rev;
624 /* identification failed */
625 device_printf(sc->dev, "unrecognized SPROM format\n");
630 * Extend the shadowed SPROM buffer to image_size, reading any required
631 * data from the backing SPROM resource and updating the CRC.
634 sprom_extend_shadow(struct bhnd_sprom *sc, size_t image_size,
639 KASSERT(image_size >= sc->sp_size, (("shadow truncation unsupported")));
641 /* Verify the request fits within our shadow buffer */
642 if (image_size > sc->sp_capacity)
645 /* Skip no-op requests */
646 if (sc->sp_size == image_size)
649 /* Populate the extended range */
650 error = sprom_direct_read(sc, sc->sp_size, sc->sp_shadow + sc->sp_size,
651 image_size - sc->sp_size, crc);
655 sc->sp_size = image_size;
660 * Read nbytes at the given offset from the backing SPROM resource, and
664 sprom_direct_read(struct bhnd_sprom *sc, size_t offset, void *buf,
665 size_t nbytes, uint8_t *crc)
667 bus_size_t res_offset;
670 KASSERT(nbytes % sizeof(uint16_t) == 0, ("unaligned sprom size"));
671 KASSERT(offset % sizeof(uint16_t) == 0, ("unaligned sprom offset"));
673 /* Check for read overrun */
674 if (offset >= sc->sp_size_max || sc->sp_size_max - offset < nbytes) {
675 device_printf(sc->dev, "requested SPROM read would overrun\n");
679 /* Perform read and update CRC */
681 res_offset = sc->sp_res_off + offset;
683 bhnd_bus_read_region_stream_2(sc->sp_res, res_offset, p,
684 (nbytes / sizeof(uint16_t)));
685 *crc = bhnd_nvram_crc8(p, nbytes, *crc);
692 * Locate the variable and SPROM revision-specific definitions
693 * for variable with @p name.
696 sprom_get_var_defn(struct bhnd_sprom *sc, const char *name,
697 const struct bhnd_nvram_vardefn **var,
698 const struct bhnd_sprom_vardefn **sprom,
699 size_t *size, size_t *nelem, bhnd_nvram_type req_type)
701 /* Find variable definition */
702 *var = bhnd_nvram_find_vardefn(name);
706 /* Find revision-specific SPROM definition */
707 for (size_t i = 0; i < (*var)->num_sp_defs; i++) {
708 const struct bhnd_sprom_vardefn *sp = &(*var)->sp_defs[i];
710 if (sc->sp_rev < sp->compat.first)
713 if (sc->sp_rev > sp->compat.last)
719 /* Calculate element count and total size, in bytes */
721 for (size_t j = 0; j < sp->num_offsets; j++)
722 if (!sp->offsets[j].cont)
725 *size = bhnd_nvram_type_width(req_type) * (*nelem);
729 /* Not supported by this SPROM revision */
734 * Return the array element delimiter for @p sfmt, or '\0' if none.
737 sprom_get_delim_char(struct bhnd_sprom *sc, bhnd_nvram_sfmt sfmt)
740 case BHND_NVRAM_SFMT_HEX:
741 case BHND_NVRAM_SFMT_DEC:
744 case BHND_NVRAM_SFMT_CCODE:
745 case BHND_NVRAM_SFMT_LEDDC:
748 case BHND_NVRAM_SFMT_MACADDR:
752 device_printf(sc->dev, "unknown NVRAM string format: %d\n",