Index: head/sys/dev/bhnd/nvram/bhnd_nvram_data.h =================================================================== --- head/sys/dev/bhnd/nvram/bhnd_nvram_data.h +++ head/sys/dev/bhnd/nvram/bhnd_nvram_data.h @@ -105,6 +105,11 @@ bhnd_nvram_data_class *classes[], size_t num_classes); +int bhnd_nvram_data_getvar_direct( + bhnd_nvram_data_class *cls, + struct bhnd_nvram_io *io, const char *name, + void *buf, size_t *len, bhnd_nvram_type type); + int bhnd_nvram_data_new(bhnd_nvram_data_class *cls, struct bhnd_nvram_data **nv, struct bhnd_nvram_io *io); Index: head/sys/dev/bhnd/nvram/bhnd_nvram_data.c =================================================================== --- head/sys/dev/bhnd/nvram/bhnd_nvram_data.c +++ head/sys/dev/bhnd/nvram/bhnd_nvram_data.c @@ -228,6 +228,41 @@ } /** + * Read a variable directly from @p io and decode as @p type. + * + * This may be used to perform reading of NVRAM variables during the very + * early boot process, prior to the availability of the kernel allocator. + * + * @param cls An NVRAM class capable of parsing @p io. + * @param io NVRAM data to be parsed. + * @param name The raw name of the variable to be fetched, + * including any device path (/pci/1/1/varname) or + * alias prefix (0:varname). + * @param[out] buf On success, the requested value will be written + * to this buffer. This argment may be NULL if + * the value is not desired. + * @param[in,out] len The capacity of @p buf. On success, will be set + * to the actual size of the requested value. + * @param type The data type to be written to @p buf. + * + * @retval 0 success + * @retval ENOMEM If @p buf is non-NULL and a buffer of @p len is too + * small to hold the requested value. + * @retval ENOENT If @p name is not found in @p io. + * @retval EFTYPE If the variable data cannot be coerced to @p type. + * @retval ERANGE If value coercion would overflow @p type. + * @retval non-zero If parsing @p io otherwise fails, a regular unix error + * code will be returned. + */ +int +bhnd_nvram_data_getvar_direct(bhnd_nvram_data_class *cls, + struct bhnd_nvram_io *io, const char *name, void *buf, size_t *len, + bhnd_nvram_type type) +{ + return (cls->op_getvar_direct(io, name, buf, len, type)); +} + +/** * Allocate and initialize a new instance of data class @p cls, copying and * parsing NVRAM data from @p io. * Index: head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcm.c =================================================================== --- head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcm.c +++ head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcm.c @@ -144,9 +144,229 @@ if (le32toh(hdr.magic) != BCM_NVRAM_MAGIC) return (ENXIO); + if (le32toh(hdr.size) > bhnd_nvram_io_getsize(io)) + return (ENXIO); + return (BHND_NVRAM_DATA_PROBE_DEFAULT); } +/** + * Parser states for bhnd_nvram_bcm_getvar_direct_common(). + */ +typedef enum { + BCM_PARSE_KEY_START, + BCM_PARSE_KEY_CONT, + BCM_PARSE_KEY, + BCM_PARSE_NEXT_KEY, + BCM_PARSE_VALUE_START, + BCM_PARSE_VALUE +} bcm_parse_state; + +static int +bhnd_nvram_bcm_getvar_direct(struct bhnd_nvram_io *io, const char *name, + void *outp, size_t *olen, bhnd_nvram_type otype) +{ + return (bhnd_nvram_bcm_getvar_direct_common(io, name, outp, olen, otype, + true)); +} + +/** + * Common BCM/BCMRAW implementation of bhnd_nvram_getvar_direct(). + */ +int +bhnd_nvram_bcm_getvar_direct_common(struct bhnd_nvram_io *io, const char *name, + void *outp, size_t *olen, bhnd_nvram_type otype, bool have_header) +{ + struct bhnd_nvram_bcmhdr hdr; + char buf[512]; + bcm_parse_state pstate; + size_t limit, offset; + size_t buflen, bufpos; + size_t namelen, namepos; + size_t vlen; + int error; + + limit = bhnd_nvram_io_getsize(io); + offset = 0; + + /* Fetch and validate the header */ + if (have_header) { + if ((error = bhnd_nvram_io_read(io, offset, &hdr, sizeof(hdr)))) + return (error); + + if (le32toh(hdr.magic) != BCM_NVRAM_MAGIC) + return (ENXIO); + + offset += sizeof(hdr); + limit = bhnd_nv_ummin(le32toh(hdr.size), limit); + } + + /* Loop our parser until we find the requested variable, or hit EOF */ + pstate = BCM_PARSE_KEY_START; + buflen = 0; + bufpos = 0; + namelen = strlen(name); + namepos = 0; + vlen = 0; + + while ((offset - bufpos) < limit) { + BHND_NV_ASSERT(bufpos <= buflen, + ("buf position invalid (%zu > %zu)", bufpos, buflen)); + BHND_NV_ASSERT(buflen <= sizeof(buf), + ("buf length invalid (%zu > %zu", buflen, sizeof(buf))); + + /* Repopulate our parse buffer? */ + if (buflen - bufpos == 0) { + BHND_NV_ASSERT(offset < limit, ("offset overrun")); + + buflen = bhnd_nv_ummin(sizeof(buf), limit - offset); + bufpos = 0; + + error = bhnd_nvram_io_read(io, offset, buf, buflen); + if (error) + return (error); + + offset += buflen; + } + + switch (pstate) { + case BCM_PARSE_KEY_START: + BHND_NV_ASSERT(buflen - bufpos > 0, ("empty buffer!")); + + /* An extra '\0' denotes NVRAM EOF */ + if (buf[bufpos] == '\0') + return (ENOENT); + + /* Reset name matching position */ + namepos = 0; + + /* Start name matching */ + pstate = BCM_PARSE_KEY_CONT; + break; + + case BCM_PARSE_KEY_CONT: { + size_t navail, nleft; + + nleft = namelen - namepos; + navail = bhnd_nv_ummin(buflen - bufpos, nleft); + + if (strncmp(name+namepos, buf+bufpos, navail) == 0) { + /* Matched */ + namepos += navail; + bufpos += navail; + + /* If we've matched the full variable name, + * look for its trailing delimiter */ + if (namepos == namelen) + pstate = BCM_PARSE_KEY; + } else { + /* No match; advance to next entry and restart + * name matching */ + pstate = BCM_PARSE_NEXT_KEY; + } + + break; + } + + case BCM_PARSE_KEY: + BHND_NV_ASSERT(buflen - bufpos > 0, ("empty buffer!")); + + if (buf[bufpos] == '=') { + /* Key fully matched; advance past '=' and + * parse the value */ + bufpos++; + pstate = BCM_PARSE_VALUE_START; + } else { + /* No match; advance to next entry and restart + * name matching */ + pstate = BCM_PARSE_NEXT_KEY; + } + + break; + + case BCM_PARSE_NEXT_KEY: { + const char *p; + + /* Scan for a '\0' terminator */ + p = memchr(buf+bufpos, '\0', buflen - bufpos); + + if (p != NULL) { + /* Found entry terminator; restart name + * matching at next entry */ + pstate = BCM_PARSE_KEY_START; + bufpos = (p - buf) + 1 /* skip '\0' */; + } else { + /* Consumed full buffer looking for '\0'; + * force repopulation of the buffer and + * retry */ + bufpos = buflen; + } + + break; + } + + case BCM_PARSE_VALUE_START: { + const char *p; + + /* Scan for a '\0' terminator */ + p = memchr(buf+bufpos, '\0', buflen - bufpos); + + if (p != NULL) { + /* Found entry terminator; parse the value */ + vlen = p - &buf[bufpos]; + pstate = BCM_PARSE_VALUE; + + } else if (p == NULL && offset == limit) { + /* Hit EOF without a terminating '\0'; + * treat the entry as implicitly terminated */ + vlen = buflen - bufpos; + pstate = BCM_PARSE_VALUE; + + } else if (p == NULL && bufpos > 0) { + size_t nread; + + /* Move existing value data to start of + * buffer */ + memmove(buf, buf+bufpos, buflen - bufpos); + buflen = bufpos; + bufpos = 0; + + /* Populate full buffer to allow retry of + * value parsing */ + nread = bhnd_nv_ummin(sizeof(buf) - buflen, + limit - offset); + + error = bhnd_nvram_io_read(io, offset, + buf+buflen, nread); + if (error) + return (error); + + offset += nread; + buflen += nread; + } else { + /* Value exceeds our buffer capacity */ + BHND_NV_LOG("cannot parse value for '%s' " + "(exceeds %zu byte limit)\n", name, + sizeof(buf)); + + return (ENXIO); + } + + break; + } + + case BCM_PARSE_VALUE: + BHND_NV_ASSERT(vlen <= buflen, ("value buf overrun")); + + return (bhnd_nvram_value_coerce(buf+bufpos, vlen, + BHND_NVRAM_TYPE_STRING, outp, olen, otype)); + } + } + + /* Variable not found */ + return (ENOENT); +} + static int bhnd_nvram_bcm_serialize(bhnd_nvram_data_class *cls, bhnd_nvram_plist *props, bhnd_nvram_plist *options, void *outp, size_t *olen) Index: head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmraw.c =================================================================== --- head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmraw.c +++ head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmraw.c @@ -50,6 +50,7 @@ #include "bhnd_nvram_private.h" #include "bhnd_nvram_datavar.h" +#include "bhnd_nvram_data_bcmvar.h" /* * Broadcom-RAW NVRAM data class. @@ -133,6 +134,14 @@ } static int +bhnd_nvram_bcmraw_getvar_direct(struct bhnd_nvram_io *io, const char *name, + void *buf, size_t *len, bhnd_nvram_type type) +{ + return (bhnd_nvram_bcm_getvar_direct_common(io, name, buf, len, type, + false)); +} + +static int bhnd_nvram_bcmraw_serialize(bhnd_nvram_data_class *cls, bhnd_nvram_plist *props, bhnd_nvram_plist *options, void *outp, size_t *olen) { Index: head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmvar.h =================================================================== --- head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmvar.h +++ head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmvar.h @@ -69,4 +69,8 @@ uint32_t sdram_ncdl; /**< sdram_ncdl */ } __packed; +int bhnd_nvram_bcm_getvar_direct_common(struct bhnd_nvram_io *io, + const char *name, void *outp, size_t *olen, bhnd_nvram_type otype, + bool have_header); + #endif /* _BHND_NVRAM_BHND_NVRAM_BCMVAR_H_ */ Index: head/sys/dev/bhnd/nvram/bhnd_nvram_data_btxt.c =================================================================== --- head/sys/dev/bhnd/nvram/bhnd_nvram_data_btxt.c +++ head/sys/dev/bhnd/nvram/bhnd_nvram_data_btxt.c @@ -124,6 +124,226 @@ return (BHND_NVRAM_DATA_PROBE_MAYBE); } + +/** + * Parser states for bhnd_nvram_bcm_getvar_direct_common(). + */ +typedef enum { + BTXT_PARSE_LINE_START, + BTXT_PARSE_KEY, + BTXT_PARSE_KEY_END, + BTXT_PARSE_NEXT_LINE, + BTXT_PARSE_VALUE_START, + BTXT_PARSE_VALUE +} btxt_parse_state; + +static int +bhnd_nvram_btxt_getvar_direct(struct bhnd_nvram_io *io, const char *name, + void *outp, size_t *olen, bhnd_nvram_type otype) +{ + char buf[512]; + btxt_parse_state pstate; + size_t limit, offset; + size_t buflen, bufpos; + size_t namelen, namepos; + size_t vlen; + int error; + + limit = bhnd_nvram_io_getsize(io); + offset = 0; + + /* Loop our parser until we find the requested variable, or hit EOF */ + pstate = BTXT_PARSE_LINE_START; + buflen = 0; + bufpos = 0; + namelen = strlen(name); + namepos = 0; + vlen = 0; + + while ((offset - bufpos) < limit) { + BHND_NV_ASSERT(bufpos <= buflen, + ("buf position invalid (%zu > %zu)", bufpos, buflen)); + BHND_NV_ASSERT(buflen <= sizeof(buf), + ("buf length invalid (%zu > %zu", buflen, sizeof(buf))); + + /* Repopulate our parse buffer? */ + if (buflen - bufpos == 0) { + BHND_NV_ASSERT(offset < limit, ("offset overrun")); + + buflen = bhnd_nv_ummin(sizeof(buf), limit - offset); + bufpos = 0; + + error = bhnd_nvram_io_read(io, offset, buf, buflen); + if (error) + return (error); + + offset += buflen; + } + + switch (pstate) { + case BTXT_PARSE_LINE_START: + BHND_NV_ASSERT(bufpos < buflen, ("empty buffer!")); + + /* Reset name matching position */ + namepos = 0; + + /* Trim any leading whitespace */ + while (bufpos < buflen && bhnd_nv_isspace(buf[bufpos])) + { + bufpos++; + } + + if (bufpos == buflen) { + /* Continue parsing the line */ + pstate = BTXT_PARSE_LINE_START; + } else if (bufpos < buflen && buf[bufpos] == '#') { + /* Comment; skip to next line */ + pstate = BTXT_PARSE_NEXT_LINE; + } else { + /* Start name matching */ + pstate = BTXT_PARSE_KEY; + } + + + break; + + case BTXT_PARSE_KEY: { + size_t navail, nleft; + + nleft = namelen - namepos; + navail = bhnd_nv_ummin(buflen - bufpos, nleft); + + if (strncmp(name+namepos, buf+bufpos, navail) == 0) { + /* Matched */ + namepos += navail; + bufpos += navail; + + if (namepos == namelen) { + /* Matched the full variable; look for + * its trailing delimiter */ + pstate = BTXT_PARSE_KEY_END; + } else { + /* Continue matching the name */ + pstate = BTXT_PARSE_KEY; + } + } else { + /* No match; advance to next entry and restart + * name matching */ + pstate = BTXT_PARSE_NEXT_LINE; + } + + break; + } + + case BTXT_PARSE_KEY_END: + BHND_NV_ASSERT(bufpos < buflen, ("empty buffer!")); + + if (buf[bufpos] == '=') { + /* Key fully matched; advance past '=' and + * parse the value */ + bufpos++; + pstate = BTXT_PARSE_VALUE_START; + } else { + /* No match; advance to next line and restart + * name matching */ + pstate = BTXT_PARSE_NEXT_LINE; + } + + break; + + case BTXT_PARSE_NEXT_LINE: { + const char *p; + + /* Scan for a '\r', '\n', or '\r\n' terminator */ + p = memchr(buf+bufpos, '\n', buflen - bufpos); + if (p == NULL) + p = memchr(buf+bufpos, '\r', buflen - bufpos); + + if (p != NULL) { + /* Found entry terminator; restart name + * matching at next line */ + pstate = BTXT_PARSE_LINE_START; + bufpos = (p - buf); + } else { + /* Consumed full buffer looking for newline; + * force repopulation of the buffer and + * retry */ + pstate = BTXT_PARSE_NEXT_LINE; + bufpos = buflen; + } + + break; + } + + case BTXT_PARSE_VALUE_START: { + const char *p; + + /* Scan for a terminating newline */ + p = memchr(buf+bufpos, '\n', buflen - bufpos); + if (p == NULL) + p = memchr(buf+bufpos, '\r', buflen - bufpos); + + if (p != NULL) { + /* Found entry terminator; parse the value */ + vlen = p - &buf[bufpos]; + pstate = BTXT_PARSE_VALUE; + + } else if (p == NULL && offset == limit) { + /* Hit EOF without a terminating newline; + * treat the entry as implicitly terminated */ + vlen = buflen - bufpos; + pstate = BTXT_PARSE_VALUE; + + } else if (p == NULL && bufpos > 0) { + size_t nread; + + /* Move existing value data to start of + * buffer */ + memmove(buf, buf+bufpos, buflen - bufpos); + buflen = bufpos; + bufpos = 0; + + /* Populate full buffer to allow retry of + * value parsing */ + nread = bhnd_nv_ummin(sizeof(buf) - buflen, + limit - offset); + + error = bhnd_nvram_io_read(io, offset, + buf+buflen, nread); + if (error) + return (error); + + offset += nread; + buflen += nread; + } else { + /* Value exceeds our buffer capacity */ + BHND_NV_LOG("cannot parse value for '%s' " + "(exceeds %zu byte limit)\n", name, + sizeof(buf)); + + return (ENXIO); + } + + break; + } + + case BTXT_PARSE_VALUE: + BHND_NV_ASSERT(vlen <= buflen, ("value buf overrun")); + + /* Trim any trailing whitespace */ + while (vlen > 0 && bhnd_nv_isspace(buf[bufpos+vlen-1])) + vlen--; + + /* Write the value to the caller's buffer */ + return (bhnd_nvram_value_coerce(buf+bufpos, vlen, + BHND_NVRAM_TYPE_STRING, outp, olen, otype)); + } + } + + /* Variable not found */ + return (ENOENT); +} + static int bhnd_nvram_btxt_serialize(bhnd_nvram_data_class *cls, bhnd_nvram_plist *props, bhnd_nvram_plist *options, void *outp, size_t *olen) Index: head/sys/dev/bhnd/nvram/bhnd_nvram_data_sprom.c =================================================================== --- head/sys/dev/bhnd/nvram/bhnd_nvram_data_sprom.c +++ head/sys/dev/bhnd/nvram/bhnd_nvram_data_sprom.c @@ -68,8 +68,7 @@ static int bhnd_nvram_sprom_ident( struct bhnd_nvram_io *io, - const bhnd_sprom_layout **ident, - struct bhnd_nvram_io **shadow); + const bhnd_sprom_layout **ident); static int bhnd_nvram_sprom_write_var( bhnd_sprom_opcode_state *state, @@ -77,6 +76,13 @@ bhnd_nvram_val *value, struct bhnd_nvram_io *io); +static int bhnd_nvram_sprom_read_var( + struct bhnd_sprom_opcode_state *state, + struct bhnd_sprom_opcode_idx_entry *entry, + struct bhnd_nvram_io *io, + union bhnd_nvram_sprom_storage *storage, + bhnd_nvram_val *val); + static int bhnd_nvram_sprom_write_offset( const struct bhnd_nvram_vardefn *var, struct bhnd_nvram_io *data, @@ -153,10 +159,6 @@ * * @param io An I/O context mapping the SPROM data to be identified. * @param[out] ident On success, the identified SPROM layout. - * @param[out] shadow On success, a correctly sized iobuf instance mapping - * a copy of the identified SPROM image. The caller is - * responsible for deallocating this instance via - * bhnd_nvram_io_free() * * @retval 0 success * @retval non-zero If identifying @p io otherwise fails, a regular unix @@ -164,77 +166,69 @@ */ static int bhnd_nvram_sprom_ident(struct bhnd_nvram_io *io, - const bhnd_sprom_layout **ident, struct bhnd_nvram_io **shadow) + const bhnd_sprom_layout **ident) { - struct bhnd_nvram_io *buf; - uint8_t crc; - size_t crc_errors; - size_t sprom_sz_max; - int error; + uint8_t crc; + size_t crc_errors; + size_t nbytes; + int error; - /* Find the largest SPROM layout size */ - sprom_sz_max = 0; - for (size_t i = 0; i < bhnd_sprom_num_layouts; i++) { - sprom_sz_max = bhnd_nv_ummax(sprom_sz_max, - bhnd_sprom_layouts[i].size); - } - - /* Allocate backing buffer and initialize CRC state */ - buf = bhnd_nvram_iobuf_empty(0, sprom_sz_max); crc = BHND_NVRAM_CRC8_INITIAL; crc_errors = 0; + nbytes = 0; /* We iterate the SPROM layouts smallest to largest, allowing us to * perform incremental checksum calculation */ for (size_t i = 0; i < bhnd_sprom_num_layouts; i++) { const bhnd_sprom_layout *layout; - void *ptr; - size_t nbytes, nr; + u_char buf[512]; + size_t nread; uint16_t magic; uint8_t srev; bool crc_valid; bool have_magic; layout = &bhnd_sprom_layouts[i]; - nbytes = bhnd_nvram_io_getsize(buf); - if ((layout->flags & SPROM_LAYOUT_MAGIC_NONE)) { + have_magic = true; + if ((layout->flags & SPROM_LAYOUT_MAGIC_NONE)) have_magic = false; - } else { - have_magic = true; - } - /* Layout instances must be ordered from smallest to largest by - * the nvram_map compiler */ + /* + * Read image data and update CRC (errors are reported + * after the signature check) + * + * Layout instances must be ordered from smallest to largest by + * the nvram_map compiler, allowing us to incrementally update + * our CRC. + */ if (nbytes > layout->size) - BHND_NV_PANIC("SPROM layout is defined out-of-order"); + BHND_NV_PANIC("SPROM layout defined out-of-order"); - /* Calculate number of additional bytes to be read */ - nr = layout->size - nbytes; + nread = layout->size - nbytes; - /* Adjust the buffer size and fetch a write pointer */ - if ((error = bhnd_nvram_io_setsize(buf, layout->size))) - goto failed; + while (nread > 0) { + size_t nr; - error = bhnd_nvram_io_write_ptr(buf, nbytes, &ptr, nr, NULL); - if (error) - goto failed; + nr = bhnd_nv_ummin(nread, sizeof(buf)); + + if ((error = bhnd_nvram_io_read(io, nbytes, buf, nr))) + return (error); + + crc = bhnd_nvram_crc8(buf, nr, crc); + crc_valid = (crc == BHND_NVRAM_CRC8_VALID); + if (!crc_valid) + crc_errors++; - /* Read image data and update CRC (errors are reported - * after the signature check) */ - if ((error = bhnd_nvram_io_read(io, nbytes, ptr, nr))) - goto failed; - - crc = bhnd_nvram_crc8(ptr, nr, crc); - crc_valid = (crc == BHND_NVRAM_CRC8_VALID); - if (!crc_valid) - crc_errors++; + nread -= nr; + nbytes += nr; + } - /* Fetch SPROM revision */ - error = bhnd_nvram_io_read(buf, layout->srev_offset, &srev, + /* Read SPROM revision */ + error = bhnd_nvram_io_read(io, layout->srev_offset, &srev, sizeof(srev)); if (error) - goto failed; + return (error); /* Early sromrev 1 devices (specifically some BCM440x enet * cards) are reported to have been incorrectly programmed @@ -248,7 +242,7 @@ /* Check the magic value, skipping to the next layout on * failure. */ - error = bhnd_nvram_sprom_check_magic(buf, layout, &magic); + error = bhnd_nvram_sprom_check_magic(io, layout, &magic); if (error) { /* If the CRC is was valid, log the mismatch */ if (crc_valid || BHND_NV_VERBOSE) { @@ -256,8 +250,7 @@ "0x%hx (expected 0x%hx)\n", srev, magic, layout->magic_value); - error = ENXIO; - goto failed; + return (ENXIO); } continue; @@ -277,40 +270,93 @@ } /* Identified */ - *shadow = buf; *ident = layout; return (0); } - /* No match -- set error and fallthrough */ - error = ENXIO; + /* No match */ if (crc_errors > 0 && BHND_NV_VERBOSE) { BHND_NV_LOG("sprom parsing failed with %zu CRC errors\n", crc_errors); } -failed: - bhnd_nvram_io_free(buf); - return (error); + return (ENXIO); } static int bhnd_nvram_sprom_probe(struct bhnd_nvram_io *io) { const bhnd_sprom_layout *layout; - struct bhnd_nvram_io *shadow; int error; /* Try to parse the input */ - if ((error = bhnd_nvram_sprom_ident(io, &layout, &shadow))) + if ((error = bhnd_nvram_sprom_ident(io, &layout))) return (error); - /* Clean up the shadow iobuf */ - bhnd_nvram_io_free(shadow); - return (BHND_NVRAM_DATA_PROBE_DEFAULT); } +static int +bhnd_nvram_sprom_getvar_direct(struct bhnd_nvram_io *io, const char *name, + void *buf, size_t *len, bhnd_nvram_type type) +{ + const bhnd_sprom_layout *layout; + bhnd_sprom_opcode_state state; + const struct bhnd_nvram_vardefn *var; + size_t vid; + int error; + + /* Look up the variable definition and ID */ + if ((var = bhnd_nvram_find_vardefn(name)) == NULL) + return (ENOENT); + + vid = bhnd_nvram_get_vardefn_id(var); + + /* Identify the SPROM image layout */ + if ((error = bhnd_nvram_sprom_ident(io, &layout))) + return (error); + + /* Initialize SPROM layout interpreter */ + if ((error = bhnd_sprom_opcode_init(&state, layout))) { + BHND_NV_LOG("error initializing opcode state: %d\n", error); + return (ENXIO); + } + + /* Find SPROM layout entry for the requested variable */ + while ((error = bhnd_sprom_opcode_next_var(&state)) == 0) { + bhnd_sprom_opcode_idx_entry entry; + union bhnd_nvram_sprom_storage storage; + bhnd_nvram_val val; + + /* Fetch the variable's entry state */ + if ((error = bhnd_sprom_opcode_init_entry(&state, &entry))) + return (error); + + /* Match against expected VID */ + if (entry.vid != vid) + continue; + + /* Decode variable to a new value instance */ + error = bhnd_nvram_sprom_read_var(&state, &entry, io, &storage, + &val); + if (error) + return (error); + + /* Perform value coercion */ + error = bhnd_nvram_val_encode(&val, buf, len, type); + + /* Clean up */ + bhnd_nvram_val_release(&val); + return (error); + } + + /* Hit EOF without matching the requested variable? */ + if (error == ENOENT) + return (ENOENT); + + /* Some other parse error occured */ + return (error); +} /** * Return the SPROM layout definition for the given @p sromrev, or NULL if @@ -365,7 +411,7 @@ var_base_type = bhnd_nvram_base_type(var->type); /* Fetch the element count from the SPROM variable layout definition */ - if ((error = bhnd_sprom_opcode_parse_var(state, entry))) + if ((error = bhnd_sprom_opcode_eval_var(state, entry))) return (error); nelem = state->var.nelem; @@ -717,7 +763,12 @@ sp = (struct bhnd_nvram_sprom *)nv; /* Identify the SPROM input data */ - if ((error = bhnd_nvram_sprom_ident(io, &sp->layout, &sp->data))) + if ((error = bhnd_nvram_sprom_ident(io, &sp->layout))) + return (error); + + /* Copy SPROM image to our shadow buffer */ + sp->data = bhnd_nvram_iobuf_copy_range(io, 0, sp->layout->size); + if (sp->data == NULL) goto failed; /* Initialize SPROM binding eval state */ @@ -989,9 +1040,15 @@ } /** - * Common variable decoding; fetches and decodes variable to @p val, - * using @p storage for actual data storage. + * Read a SPROM variable value from @p io. * + * @param state The SPROM opcode state describing the layout of @p io. + * @param entry The variable's SPROM opcode index entry. + * @param io The input I/O context. + * @param storage Storage to be used with @p val. + * @param[out] val Value instance to be initialized with the + * parsed variable data. + * * The returned @p val instance will hold a borrowed reference to @p storage, * and must be copied via bhnd_nvram_val_copy() if it will be referenced beyond * the lifetime of @p storage. @@ -1000,13 +1057,12 @@ * via bhnd_nvram_val_release(). */ static int -bhnd_nvram_sprom_getvar_common(struct bhnd_nvram_data *nv, void *cookiep, +bhnd_nvram_sprom_read_var(struct bhnd_sprom_opcode_state *state, + struct bhnd_sprom_opcode_idx_entry *entry, struct bhnd_nvram_io *io, union bhnd_nvram_sprom_storage *storage, bhnd_nvram_val *val) { - struct bhnd_nvram_sprom *sp; - bhnd_sprom_opcode_idx_entry *entry; - const struct bhnd_nvram_vardefn *var; union bhnd_nvram_sprom_storage *inp; + const struct bhnd_nvram_vardefn *var; bhnd_nvram_type var_btype; uint32_t intv; size_t ilen, ipos, iwidth; @@ -1014,14 +1070,9 @@ bool all_bits_set; int error; - sp = (struct bhnd_nvram_sprom *)nv; - entry = cookiep; - - BHND_NV_ASSERT(cookiep != NULL, ("NULL variable cookiep")); - /* Fetch canonical variable definition */ - var = SPROM_COOKIE_TO_NVRAM_VAR(cookiep); - BHND_NV_ASSERT(var != NULL, ("invalid cookiep %p", cookiep)); + var = bhnd_nvram_get_vardefn(entry->vid); + BHND_NV_ASSERT(var != NULL, ("invalid entry")); /* * Fetch the array length from the SPROM variable definition. @@ -1030,12 +1081,12 @@ * canonical NVRAM variable definition, but some SPROM layouts may * define a smaller element count. */ - if ((error = bhnd_sprom_opcode_parse_var(&sp->state, entry))) { + if ((error = bhnd_sprom_opcode_eval_var(state, entry))) { BHND_NV_LOG("variable evaluation failed: %d\n", error); return (error); } - nelem = sp->state.var.nelem; + nelem = state->var.nelem; if (nelem > var->nelem) { BHND_NV_LOG("SPROM array element count %zu cannot be " "represented by '%s' element count of %hhu\n", nelem, @@ -1070,7 +1121,7 @@ /* * Decode the SPROM data, iteratively decoding up to nelem values. */ - if ((error = bhnd_sprom_opcode_seek(&sp->state, entry))) { + if ((error = bhnd_sprom_opcode_seek(state, entry))) { BHND_NV_LOG("variable seek failed: %d\n", error); return (error); } @@ -1081,7 +1132,7 @@ all_bits_set = true; else all_bits_set = false; - while ((error = bhnd_sprom_opcode_next_binding(&sp->state)) == 0) { + while ((error = bhnd_sprom_opcode_next_binding(state)) == 0) { bhnd_sprom_opcode_bind *binding; bhnd_sprom_opcode_var *binding_var; bhnd_nvram_type intv_type; @@ -1091,12 +1142,12 @@ void *ptr; BHND_NV_ASSERT( - sp->state.var_state >= SPROM_OPCODE_VAR_STATE_OPEN, + state->var_state >= SPROM_OPCODE_VAR_STATE_OPEN, ("invalid var state")); - BHND_NV_ASSERT(sp->state.var.have_bind, ("invalid bind state")); + BHND_NV_ASSERT(state->var.have_bind, ("invalid bind state")); - binding_var = &sp->state.var; - binding = &sp->state.var.bind; + binding_var = &state->var; + binding = &state->var.bind; if (ipos >= nelem) { BHND_NV_LOG("output skip %u positioned " @@ -1107,17 +1158,16 @@ /* Calculate input skip bytes for this binding */ skip_in_bytes = binding->skip_in; - error = bhnd_sprom_opcode_apply_scale(&sp->state, - &skip_in_bytes); + error = bhnd_sprom_opcode_apply_scale(state, &skip_in_bytes); if (error) return (error); /* Bind */ - offset = sp->state.offset; + offset = state->offset; for (size_t i = 0; i < binding->count; i++) { /* Read the offset value, OR'ing with the current * value of intv */ - error = bhnd_nvram_sprom_read_offset(var, sp->data, + error = bhnd_nvram_sprom_read_offset(var, io, binding_var->base_type, offset, binding_var->mask, @@ -1209,6 +1259,39 @@ return (error); } + +/** + * Common variable decoding; fetches and decodes variable to @p val, + * using @p storage for actual data storage. + * + * The returned @p val instance will hold a borrowed reference to @p storage, + * and must be copied via bhnd_nvram_val_copy() if it will be referenced beyond + * the lifetime of @p storage. + * + * The caller is responsible for releasing any allocated value state + * via bhnd_nvram_val_release(). + */ +static int +bhnd_nvram_sprom_getvar_common(struct bhnd_nvram_data *nv, void *cookiep, + union bhnd_nvram_sprom_storage *storage, bhnd_nvram_val *val) +{ + struct bhnd_nvram_sprom *sp; + bhnd_sprom_opcode_idx_entry *entry; + const struct bhnd_nvram_vardefn *var; + + BHND_NV_ASSERT(cookiep != NULL, ("NULL variable cookiep")); + + sp = (struct bhnd_nvram_sprom *)nv; + entry = cookiep; + + /* Fetch canonical variable definition */ + var = SPROM_COOKIE_TO_NVRAM_VAR(cookiep); + BHND_NV_ASSERT(var != NULL, ("invalid cookiep %p", cookiep)); + + return (bhnd_nvram_sprom_read_var(&sp->state, entry, sp->data, storage, + val)); +} + static int bhnd_nvram_sprom_getvar_order(struct bhnd_nvram_data *nv, void *cookiep1, void *cookiep2) Index: head/sys/dev/bhnd/nvram/bhnd_nvram_data_sprom_subr.c =================================================================== --- head/sys/dev/bhnd/nvram/bhnd_nvram_data_sprom_subr.c +++ head/sys/dev/bhnd/nvram/bhnd_nvram_data_sprom_subr.c @@ -51,7 +51,6 @@ const void *rhs); static int bhnd_sprom_opcode_reset(bhnd_sprom_opcode_state *state); -static int bhnd_sprom_opcode_next_var(bhnd_sprom_opcode_state *state); static int bhnd_sprom_opcode_set_type(bhnd_sprom_opcode_state *state, bhnd_nvram_type type); @@ -108,8 +107,6 @@ return (ENOMEM); for (num_vars = 0; num_vars < num_idx; num_vars++) { - size_t opcodes; - /* Seek to next entry */ if ((error = bhnd_sprom_opcode_next_var(state))) { SPROM_OP_BAD(state, "error reading expected variable " @@ -118,36 +115,14 @@ return (error); } - /* We limit the SPROM index representations to the minimal - * type widths capable of covering all known layouts */ - - /* Save SPROM image offset */ - if (state->offset > UINT16_MAX) { - SPROM_OP_BAD(state, "cannot index large offset %u\n", - state->offset); - bhnd_nv_free(idx); - return (ENXIO); - } - idx[num_vars].offset = state->offset; - - /* Save current variable ID */ - if (state->vid > UINT16_MAX) { - SPROM_OP_BAD(state, "cannot index large vid %zu\n", - state->vid); - bhnd_nv_free(idx); - return (ENXIO); - } - idx[num_vars].vid = state->vid; - - /* Save opcode position */ - opcodes = (state->input - state->layout->bindings); - if (opcodes > UINT16_MAX) { - SPROM_OP_BAD(state, "cannot index large opcode offset " - "%zu\n", opcodes); + /* Record entry state in our index */ + error = bhnd_sprom_opcode_init_entry(state, &idx[num_vars]); + if (error) { + SPROM_OP_BAD(state, "error initializing index for " + "entry: %d\n", error); bhnd_nv_free(idx); - return (ENXIO); + return (error); } - idx[num_vars].opcodes = opcodes; } /* Should have reached end of binding table; next read must return @@ -314,6 +289,54 @@ return (&state->idx[idxpos]); } + +/** + * Initialize @p entry with the current variable's opcode state. + * + * @param state The opcode state to be saved. + * @param[out] entry The opcode index entry to be initialized from @p state. + * + * @retval 0 success + * @retval ENXIO if @p state cannot be serialized as an index entry. + */ +int +bhnd_sprom_opcode_init_entry(bhnd_sprom_opcode_state *state, + bhnd_sprom_opcode_idx_entry *entry) +{ + size_t opcodes; + + /* We limit the SPROM index representations to the minimal type widths + * capable of covering all known layouts */ + + /* Save SPROM image offset */ + if (state->offset > UINT16_MAX) { + SPROM_OP_BAD(state, "cannot index large offset %u\n", + state->offset); + return (ENXIO); + } + + entry->offset = state->offset; + + /* Save current variable ID */ + if (state->vid > UINT16_MAX) { + SPROM_OP_BAD(state, "cannot index large vid %zu\n", + state->vid); + return (ENXIO); + } + entry->vid = state->vid; + + /* Save opcode position */ + opcodes = (state->input - state->layout->bindings); + if (opcodes > UINT16_MAX) { + SPROM_OP_BAD(state, "cannot index large opcode offset " + "%zu\n", opcodes); + return (ENXIO); + } + entry->opcodes = opcodes; + + return (0); +} + /** * Reset SPROM opcode evaluation state and seek to the @p entry's position. * @@ -1255,7 +1278,7 @@ * returned. */ int -bhnd_sprom_opcode_parse_var(bhnd_sprom_opcode_state *state, +bhnd_sprom_opcode_eval_var(bhnd_sprom_opcode_state *state, bhnd_sprom_opcode_idx_entry *entry) { uint8_t opcode; @@ -1291,7 +1314,7 @@ * @retval non-zero if evaluation otherwise fails, a regular unix error * code will be returned. */ -static int +int bhnd_sprom_opcode_next_var(bhnd_sprom_opcode_state *state) { uint8_t opcode; Index: head/sys/dev/bhnd/nvram/bhnd_nvram_data_spromvar.h =================================================================== --- head/sys/dev/bhnd/nvram/bhnd_nvram_data_spromvar.h +++ head/sys/dev/bhnd/nvram/bhnd_nvram_data_spromvar.h @@ -64,13 +64,19 @@ bhnd_sprom_opcode_state *state, bhnd_sprom_opcode_idx_entry *prev); -int bhnd_sprom_opcode_parse_var( +int bhnd_sprom_opcode_init_entry( + bhnd_sprom_opcode_state *state, + bhnd_sprom_opcode_idx_entry *entry); + +int bhnd_sprom_opcode_eval_var( bhnd_sprom_opcode_state *state, bhnd_sprom_opcode_idx_entry *entry); int bhnd_sprom_opcode_seek( bhnd_sprom_opcode_state *state, bhnd_sprom_opcode_idx_entry *entry); +int bhnd_sprom_opcode_next_var( + bhnd_sprom_opcode_state *state); int bhnd_sprom_opcode_next_binding( bhnd_sprom_opcode_state *state); int bhnd_sprom_opcode_apply_scale( Index: head/sys/dev/bhnd/nvram/bhnd_nvram_data_tlv.c =================================================================== --- head/sys/dev/bhnd/nvram/bhnd_nvram_data_tlv.c +++ head/sys/dev/bhnd/nvram/bhnd_nvram_data_tlv.c @@ -165,6 +165,78 @@ } static int +bhnd_nvram_tlv_getvar_direct(struct bhnd_nvram_io *io, const char *name, + void *buf, size_t *len, bhnd_nvram_type type) +{ + struct bhnd_nvram_tlv_env env; + char data[NVRAM_TLV_ENVP_DATA_MAX_LEN]; + size_t data_len; + const char *key, *value; + size_t keylen, vlen; + size_t namelen; + size_t next, off; + uint8_t tag; + int error; + + namelen = strlen(name); + + /* Iterate over the input looking for the requested variable */ + next = 0; + while (!(error = bhnd_nvram_tlv_next_record(io, &next, &off, &tag))) { + switch (tag) { + case NVRAM_TLV_TYPE_END: + /* Not found */ + return (ENOENT); + + case NVRAM_TLV_TYPE_ENV: + /* Read the record header */ + error = bhnd_nvram_io_read(io, off, &env, sizeof(env)); + if (error) { + BHND_NV_LOG("error reading TLV_ENV record " + "header: %d\n", error); + return (error); + } + + /* Read the record data */ + data_len = NVRAM_TLV_ENVP_DATA_LEN(&env); + error = bhnd_nvram_io_read(io, off + sizeof(env), data, + data_len); + if (error) { + BHND_NV_LOG("error reading TLV_ENV record " + "data: %d\n", error); + return (error); + } + + /* Parse the key=value string */ + error = bhnd_nvram_parse_env(data, data_len, '=', &key, + &keylen, &value, &vlen); + if (error) { + BHND_NV_LOG("error parsing TLV_ENV data: %d\n", + error); + return (error); + } + + /* Match against requested variable name */ + if (keylen == namelen && + strncmp(key, name, namelen) == 0) + { + return (bhnd_nvram_value_coerce(value, vlen, + BHND_NVRAM_TYPE_STRING, buf, len, type)); + } + + break; + + default: + /* Skip unknown tags */ + break; + } + } + + /* Hit I/O error */ + return (error); +} + +static int bhnd_nvram_tlv_serialize(bhnd_nvram_data_class *cls, bhnd_nvram_plist *props, bhnd_nvram_plist *options, void *outp, size_t *olen) { Index: head/sys/dev/bhnd/nvram/bhnd_nvram_datavar.h =================================================================== --- head/sys/dev/bhnd/nvram/bhnd_nvram_datavar.h +++ head/sys/dev/bhnd/nvram/bhnd_nvram_datavar.h @@ -55,6 +55,11 @@ /** @see bhnd_nvram_data_probe() */ typedef int (bhnd_nvram_data_op_probe)(struct bhnd_nvram_io *io); +/** @see bhnd_nvram_data_probe() */ +typedef int (bhnd_nvram_data_op_getvar_direct)( + struct bhnd_nvram_io *io, const char *name, + void *outp, size_t *olen, bhnd_nvram_type otype); + /** @see bhnd_nvram_data_serialize() */ typedef int (bhnd_nvram_data_op_serialize)( bhnd_nvram_data_class *cls, @@ -131,6 +136,7 @@ size_t size; /**< instance size */ bhnd_nvram_data_op_probe *op_probe; + bhnd_nvram_data_op_getvar_direct *op_getvar_direct; bhnd_nvram_data_op_serialize *op_serialize; bhnd_nvram_data_op_new *op_new; bhnd_nvram_data_op_free *op_free; @@ -184,6 +190,7 @@ */ #define BHND_NVRAM_DATA_CLASS_ITER_METHODS(_cname, _macro) \ _macro(_cname, probe) \ + _macro(_cname, getvar_direct) \ _macro(_cname, serialize) \ _macro(_cname, new) \ _macro(_cname, free) \ Index: head/sys/dev/bhnd/nvram/bhnd_sprom.c =================================================================== --- head/sys/dev/bhnd/nvram/bhnd_sprom.c +++ head/sys/dev/bhnd/nvram/bhnd_sprom.c @@ -120,9 +120,9 @@ sprom_size = r_size - offset; - /* Allocate an I/O context for the SPROM parser. All SPROM reads - * must be 16-bit aligned */ - io = bhnd_nvram_iores_new(r, offset, sprom_size, sizeof(uint16_t)); + /* Allocate an I/O context for the SPROM parser. SPROM reads do not + * appear to require any specific alignment. */ + io = bhnd_nvram_iores_new(r, offset, sprom_size, 1); if (io == NULL) { error = ENXIO; goto failed; Index: head/sys/mips/broadcom/bcm_machdep.h =================================================================== --- head/sys/mips/broadcom/bcm_machdep.h +++ head/sys/mips/broadcom/bcm_machdep.h @@ -40,6 +40,8 @@ #include +#include "bcm_nvram_cfevar.h" + extern const struct bhnd_pmu_io bcm_pmu_soc_io; struct bcm_platform { @@ -65,6 +67,9 @@ bhnd_erom_t obj; } erom; + struct bhnd_nvram_io *nvram_io; /**< NVRAM I/O context, or NULL if unavailable */ + bhnd_nvram_data_class *nvram_cls; /**< NVRAM data class, or NULL if unavailable */ + #ifdef CFE int cfe_console; /**< Console handle, or -1 */ #endif @@ -79,6 +84,10 @@ u_int bcm_get_uart_rclk(struct bcm_platform *bp); +int bcm_get_nvram(struct bcm_platform *bp, + const char *name, void *outp, size_t *olen, + bhnd_nvram_type type); + #define BCM_ERR(fmt, ...) \ printf("%s: " fmt, __FUNCTION__, ##__VA_ARGS__) Index: head/sys/mips/broadcom/bcm_machdep.c =================================================================== --- head/sys/mips/broadcom/bcm_machdep.c +++ head/sys/mips/broadcom/bcm_machdep.c @@ -88,6 +88,7 @@ #ifdef CFE #include +#include #endif #if 0 @@ -112,6 +113,10 @@ static struct bcm_platform bcm_platform_data; static bool bcm_platform_data_avail = false; +#ifdef CFE +static struct bcm_nvram_iocfe bcm_cfe_nvram; +#endif + static const struct bhnd_core_match bcm_chipc_cores[] = { { BHND_MATCH_CORE(BHND_MFGID_BCM, BHND_COREID_CC) }, { BHND_MATCH_CORE(BHND_MFGID_BCM, BHND_COREID_4706_CC) }, @@ -190,6 +195,40 @@ } /** + * Read a variable directly from NVRAM, decoding as @p type. + * + * @param bp Platform state. + * @param name The raw name of the variable to be fetched, + * including any device path (/pci/1/1/varname) or + * alias prefix (0:varname). + * @param[out] buf On success, the requested value will be written + * to this buffer. This argment may be NULL if + * the value is not desired. + * @param[in,out] len The capacity of @p buf. On success, will be set + * to the actual size of the requested value. + * @param type The data type to be written to @p buf. + * + * @retval 0 success + * @retval ENOMEM If @p buf is non-NULL and a buffer of @p len is too + * small to hold the requested value. + * @retval ENOENT If @p name is not found. + * @retval EFTYPE If the variable data cannot be coerced to @p type. + * @retval ERANGE If value coercion would overflow @p type. + * @retval non-zero If parsing NVRAM otherwise fails, a regular unix error + * code will be returned. + */ +int +bcm_get_nvram(struct bcm_platform *bp, const char *name, void *buf, size_t *len, + bhnd_nvram_type type) +{ + if (bp->nvram_io == NULL || bp->nvram_cls == NULL) + return (ENOENT); + + return (bhnd_nvram_data_getvar_direct(bp->nvram_cls, bp->nvram_io, name, + buf, len, type)); +} + +/** * Probe and attach a bhnd_erom parser instance for the bhnd bus. * * @param[out] erom_cls The probed EROM class. @@ -280,12 +319,20 @@ bool aob, pmu; int error; +#ifdef CFE /* Fetch CFE console handle (if any). Must be initialized before * any calls to printf/early_putc. */ -#ifdef CFE if ((bp->cfe_console = cfe_getstdhandle(CFE_STDHANDLE_CONSOLE)) < 0) bp->cfe_console = -1; -#endif + + /* Probe CFE NVRAM sources */ + bp->nvram_io = &bcm_cfe_nvram.io; + error = bcm_nvram_find_cfedev(&bcm_cfe_nvram, &bp->nvram_cls); + if (error) { + bp->nvram_io = NULL; + bp->nvram_cls = NULL; + } +#endif /* CFE */ /* Probe and attach device table provider, populating our * chip identification */ Index: head/sys/mips/broadcom/bcm_nvram_cfe.c =================================================================== --- head/sys/mips/broadcom/bcm_nvram_cfe.c +++ head/sys/mips/broadcom/bcm_nvram_cfe.c @@ -54,36 +54,18 @@ #include #include -#include - #include "bhnd_nvram_if.h" +#include "bcm_machdep.h" #include "bcm_nvram_cfevar.h" -/** - * CFE-backed bhnd_nvram_io implementation. - */ -struct bhnd_nvram_iocfe { - struct bhnd_nvram_io io; /**< common I/O instance state */ - - char *dname; /**< CFE device name (borrowed) */ - int fd; /**< CFE file descriptor */ - size_t offset; /**< base offset */ - size_t size; /**< device size */ - bool req_blk_erase; /**< flash blocks must be erased - before writing */ -}; - BHND_NVRAM_IOPS_DEFN(iocfe) #define IOCFE_LOG(_io, _fmt, ...) \ printf("%s/%s: " _fmt, __FUNCTION__, (_io)->dname, ##__VA_ARGS__) -static int bhnd_nvram_iocfe_new(struct bhnd_nvram_io **io, - char *dname); - -static struct bhnd_nvram_io *bhnd_nvram_find_cfedev(device_t dev, - char **dname, bhnd_nvram_data_class **cls); +static int bcm_nvram_iocfe_init(struct bcm_nvram_iocfe *iocfe, + char *dname); /** Known CFE NVRAM device names, in probe order. */ static char *nvram_cfe_devs[] = { @@ -99,31 +81,20 @@ &bhnd_nvram_tlv_class }; - static int bhnd_nvram_cfe_probe(device_t dev) { - struct bhnd_nvram_io *io; - bhnd_nvram_data_class *cls; - const char *cls_desc; - char *dname; - char *desc; - - /* Locate a usable CFE device */ - io = bhnd_nvram_find_cfedev(dev, &dname, &cls); - if (io == NULL) + struct bcm_platform *bp; + + /* Fetch platform NVRAM I/O context */ + bp = bcm_get_platform(); + if (bp->nvram_io == NULL) return (ENXIO); - bhnd_nvram_io_free(io); - /* Format the device description */ - cls_desc = bhnd_nvram_data_class_desc(cls); - asprintf(&desc, M_DEVBUF, "%s CFE %s", cls_desc, dname); - if (desc != NULL) { - device_set_desc_copy(dev, desc); - free(desc, M_DEVBUF); - } else { - device_set_desc(dev, cls_desc); - } + KASSERT(bp->nvram_cls != NULL, ("missing NVRAM class")); + + /* Set the device description */ + device_set_desc(dev, bhnd_nvram_data_class_desc(bp->nvram_cls)); /* Refuse wildcard attachments */ return (BUS_PROBE_NOWILDCARD); @@ -133,25 +104,19 @@ static int bhnd_nvram_cfe_attach(device_t dev) { + struct bcm_platform *bp; struct bhnd_nvram_cfe_softc *sc; - bhnd_nvram_data_class *cls; - struct bhnd_nvram_io *io; - char *dname; int error; + bp = bcm_get_platform(); + KASSERT(bp->nvram_io != NULL, ("missing NVRAM I/O context")); + KASSERT(bp->nvram_cls != NULL, ("missing NVRAM class")); + sc = device_get_softc(dev); sc->dev = dev; - /* Locate NVRAM device via CFE */ - io = bhnd_nvram_find_cfedev(dev, &dname, &cls); - if (io == NULL) { - device_printf(dev, "CFE NVRAM device not found\n"); - return (ENXIO); - } - - /* Initialize NVRAM store and free the I/O context */ - error = bhnd_nvram_store_parse_new(&sc->store, io, cls); - bhnd_nvram_io_free(io); + error = bhnd_nvram_store_parse_new(&sc->store, bp->nvram_io, + bp->nvram_cls); if (error) return (error); @@ -201,79 +166,79 @@ } /** - * Find, open, identify, and return an I/O context mapping our - * CFE NVRAM device. + * Find, open, identify, and initialize an I/O context mapping the CFE NVRAM + * device. * - * @param dev bhnd_nvram_cfe device. - * @param[out] dname On success, the CFE device name. + * @param[out] iocfe On success, an I/O context mapping the CFE NVRAM + * device. * @param[out] cls On success, the identified NVRAM data format * class. * - * @retval non-NULL success. the caller inherits ownership of the returned - * NVRAM I/O context. - * @retval NULL if no usable CFE NVRAM device could be found. + * @retval 0 success. the caller inherits ownership of @p iocfe. + * @retval non-zero if no usable CFE NVRAM device can be found, a standard + * unix error will be returned. */ -static struct bhnd_nvram_io * -bhnd_nvram_find_cfedev(device_t dev, char **dname, bhnd_nvram_data_class **cls) -{ - struct bhnd_nvram_io *io; - int devinfo; - int error, result; +int +bcm_nvram_find_cfedev(struct bcm_nvram_iocfe *iocfe, + bhnd_nvram_data_class **cls) +{ + char *dname; + int devinfo; + int error, result; for (u_int i = 0; i < nitems(nvram_cfe_fmts); i++) { *cls = nvram_cfe_fmts[i]; for (u_int j = 0; j < nitems(nvram_cfe_devs); j++) { - *dname = nvram_cfe_devs[j]; + dname = nvram_cfe_devs[j]; /* Does the device exist? */ - if ((devinfo = cfe_getdevinfo(*dname)) < 0) { + if ((devinfo = cfe_getdevinfo(dname)) < 0) { if (devinfo != CFE_ERR_DEVNOTFOUND) { - device_printf(dev, "cfe_getdevinfo(%s) " - "failed: %d\n", *dname, devinfo); + BCM_ERR("cfe_getdevinfo(%s) failed: " + "%d\n", dname, devinfo); } continue; } /* Open for reading */ - if ((error = bhnd_nvram_iocfe_new(&io, *dname))) + if ((error = bcm_nvram_iocfe_init(iocfe, dname))) continue; /* Probe */ - result = bhnd_nvram_data_probe(*cls, io); + result = bhnd_nvram_data_probe(*cls, &iocfe->io); if (result <= 0) { /* Found a supporting NVRAM data class */ - return (io); + return (0); } /* Keep searching */ - bhnd_nvram_io_free(io); - io = NULL; + bhnd_nvram_io_free(&iocfe->io); } } - return (NULL); + return (ENODEV); } /** - * Allocate and return a new I/O context backed by a CFE device. + * Initialize a new CFE device-backed I/O context. * - * The caller is responsible for deallocating the returned I/O context via - * bhnd_nvram_io_free(). + * The caller is responsible for releasing all resources held by the returned + * I/O context via bhnd_nvram_io_free(). * - * @param[out] io On success, a valid I/O context for @p dname. - * @param dname The name of the CFE device to be opened for reading. + * @param[out] io On success, will be initialized as an I/O context for + * CFE device @p dname. + * @param dname The name of the CFE device to be opened for reading. * - * @retval 0 success. - * @retval non-zero if opening @p dname otherwise fails, a standard unix error - * will be returned. + * @retval 0 success. + * @retval non-zero if opening @p dname otherwise fails, a standard unix + * error will be returned. */ static int -bhnd_nvram_iocfe_new(struct bhnd_nvram_io **io, char *dname) +bcm_nvram_iocfe_init(struct bcm_nvram_iocfe *iocfe, char *dname) { - struct bhnd_nvram_iocfe *iocfe; nvram_info_t nvram_info; int cerr, devinfo, dtype, rlen; int64_t nv_offset; @@ -281,7 +246,6 @@ bool req_blk_erase; int error; - iocfe = malloc(sizeof(*iocfe), M_DEVBUF, M_WAITOK); iocfe->io.iops = &bhnd_nvram_iocfe_ops; iocfe->dname = dname; @@ -290,8 +254,7 @@ if (iocfe->fd <= 0) { IOCFE_LOG(iocfe, "cfe_open() failed: %d\n", iocfe->fd); - error = ENXIO; - goto failed; + return (ENXIO); } /* Try to fetch device info */ @@ -374,32 +337,29 @@ iocfe->size = nv_size; iocfe->req_blk_erase = req_blk_erase; - *io = &iocfe->io; return (CFE_OK); failed: if (iocfe->fd >= 0) cfe_close(iocfe->fd); - free(iocfe, M_DEVBUF); - - *io = NULL; return (error); } static void bhnd_nvram_iocfe_free(struct bhnd_nvram_io *io) { - struct bhnd_nvram_iocfe *iocfe = (struct bhnd_nvram_iocfe *)io; + struct bcm_nvram_iocfe *iocfe = (struct bcm_nvram_iocfe *)io; + /* CFE I/O instances are statically allocated; we do not need to free + * the instance itself */ cfe_close(iocfe->fd); - free(io, M_DEVBUF); } static size_t bhnd_nvram_iocfe_getsize(struct bhnd_nvram_io *io) { - struct bhnd_nvram_iocfe *iocfe = (struct bhnd_nvram_iocfe *)io; + struct bcm_nvram_iocfe *iocfe = (struct bcm_nvram_iocfe *)io; return (iocfe->size); } @@ -438,12 +398,12 @@ bhnd_nvram_iocfe_read(struct bhnd_nvram_io *io, size_t offset, void *buffer, size_t nbytes) { - struct bhnd_nvram_iocfe *iocfe; + struct bcm_nvram_iocfe *iocfe; size_t remain; int64_t cfe_offset; int nr, nreq; - iocfe = (struct bhnd_nvram_iocfe *)io; + iocfe = (struct bcm_nvram_iocfe *)io; /* Determine (and validate) the base CFE offset */ #if (SIZE_MAX > INT64_MAX) Index: head/sys/mips/broadcom/bcm_nvram_cfevar.h =================================================================== --- head/sys/mips/broadcom/bcm_nvram_cfevar.h +++ head/sys/mips/broadcom/bcm_nvram_cfevar.h @@ -36,8 +36,28 @@ #include #include +#include #include +struct bcm_nvram_iocfe; + +int bcm_nvram_find_cfedev(struct bcm_nvram_iocfe *iocfe, + bhnd_nvram_data_class **cls); + +/** + * CFE-backed bhnd_nvram_io implementation. + */ +struct bcm_nvram_iocfe { + struct bhnd_nvram_io io; /**< common I/O instance state */ + + char *dname; /**< CFE device name (borrowed) */ + int fd; /**< CFE file descriptor */ + size_t offset; /**< base offset */ + size_t size; /**< device size */ + bool req_blk_erase; /**< flash blocks must be erased + before writing */ +}; + /** bhnd_nvram_cfe driver instance state. */ struct bhnd_nvram_cfe_softc { device_t dev;