Author: landonf
Date: Thu Mar 23 19:29:12 2017
New Revision: 315866
URL: https://svnweb.freebsd.org/changeset/base/315866

Log:
  [mips/broadcom]: Early boot NVRAM support
  
  Add support for early boot access to NVRAM variables, using a new
  bhnd_nvram_data_getvar_direct() API to support zero-allocation direct
  reading of NVRAM variables from a bhnd_nvram_io instance backed by the
  CFE NVRAM device.
  
  Approved by:  adrian (mentor)
  Differential Revision:        https://reviews.freebsd.org/D9913

Modified:
  head/sys/dev/bhnd/nvram/bhnd_nvram_data.c
  head/sys/dev/bhnd/nvram/bhnd_nvram_data.h
  head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcm.c
  head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmraw.c
  head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmvar.h
  head/sys/dev/bhnd/nvram/bhnd_nvram_data_btxt.c
  head/sys/dev/bhnd/nvram/bhnd_nvram_data_sprom.c
  head/sys/dev/bhnd/nvram/bhnd_nvram_data_sprom_subr.c
  head/sys/dev/bhnd/nvram/bhnd_nvram_data_spromvar.h
  head/sys/dev/bhnd/nvram/bhnd_nvram_data_tlv.c
  head/sys/dev/bhnd/nvram/bhnd_nvram_datavar.h
  head/sys/dev/bhnd/nvram/bhnd_sprom.c
  head/sys/mips/broadcom/bcm_machdep.c
  head/sys/mips/broadcom/bcm_machdep.h
  head/sys/mips/broadcom/bcm_nvram_cfe.c
  head/sys/mips/broadcom/bcm_nvram_cfevar.h

Modified: head/sys/dev/bhnd/nvram/bhnd_nvram_data.c
==============================================================================
--- head/sys/dev/bhnd/nvram/bhnd_nvram_data.c   Thu Mar 23 17:11:34 2017        
(r315865)
+++ head/sys/dev/bhnd/nvram/bhnd_nvram_data.c   Thu Mar 23 19:29:12 2017        
(r315866)
@@ -228,6 +228,41 @@ bhnd_nvram_data_probe_classes(struct bhn
 }
 
 /**
+ * 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.
  *

Modified: head/sys/dev/bhnd/nvram/bhnd_nvram_data.h
==============================================================================
--- head/sys/dev/bhnd/nvram/bhnd_nvram_data.h   Thu Mar 23 17:11:34 2017        
(r315865)
+++ head/sys/dev/bhnd/nvram/bhnd_nvram_data.h   Thu Mar 23 19:29:12 2017        
(r315866)
@@ -105,6 +105,11 @@ int                         bhnd_nvram_data_probe_classes(
                             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);

Modified: head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcm.c
==============================================================================
--- head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcm.c       Thu Mar 23 17:11:34 
2017        (r315865)
+++ head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcm.c       Thu Mar 23 19:29:12 
2017        (r315866)
@@ -144,9 +144,229 @@ bhnd_nvram_bcm_probe(struct bhnd_nvram_i
        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)

Modified: head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmraw.c
==============================================================================
--- head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmraw.c    Thu Mar 23 17:11:34 
2017        (r315865)
+++ head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmraw.c    Thu Mar 23 19:29:12 
2017        (r315866)
@@ -50,6 +50,7 @@ __FBSDID("$FreeBSD$");
 #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 @@ bhnd_nvram_bcmraw_probe(struct bhnd_nvra
 }
 
 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)
 {

Modified: head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmvar.h
==============================================================================
--- head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmvar.h    Thu Mar 23 17:11:34 
2017        (r315865)
+++ head/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmvar.h    Thu Mar 23 19:29:12 
2017        (r315866)
@@ -69,4 +69,8 @@ struct bhnd_nvram_bcmhdr {
        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_ */

Modified: head/sys/dev/bhnd/nvram/bhnd_nvram_data_btxt.c
==============================================================================
--- head/sys/dev/bhnd/nvram/bhnd_nvram_data_btxt.c      Thu Mar 23 17:11:34 
2017        (r315865)
+++ head/sys/dev/bhnd/nvram/bhnd_nvram_data_btxt.c      Thu Mar 23 19:29:12 
2017        (r315866)
@@ -124,6 +124,226 @@ bhnd_nvram_btxt_probe(struct bhnd_nvram_
        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)

Modified: head/sys/dev/bhnd/nvram/bhnd_nvram_data_sprom.c
==============================================================================
--- head/sys/dev/bhnd/nvram/bhnd_nvram_data_sprom.c     Thu Mar 23 17:11:34 
2017        (r315865)
+++ head/sys/dev/bhnd/nvram/bhnd_nvram_data_sprom.c     Thu Mar 23 19:29:12 
2017        (r315866)
@@ -68,8 +68,7 @@ static const bhnd_sprom_layout  *bhnd_nv
 
 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 @@ static int                    bhnd_nvram_sprom_write_var
                                     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 @@ bhnd_nvram_sprom_check_magic(struct bhnd
  *
  * @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 @@ bhnd_nvram_sprom_check_magic(struct bhnd
  */
 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 @@ bhnd_nvram_sprom_ident(struct bhnd_nvram
 
                /* 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 @@ bhnd_nvram_sprom_ident(struct bhnd_nvram
                                            "0x%hx (expected 0x%hx)\n", srev,
                                            magic, layout->magic_value);
 
-                                       error = ENXIO;
-                                       goto failed;
+                                       return (ENXIO);
                        }
        
                        continue;
@@ -277,40 +270,93 @@ bhnd_nvram_sprom_ident(struct bhnd_nvram
                }
 
                /* 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 @@ bhnd_nvram_sprom_write_var(bhnd_sprom_op
        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 @@ bhnd_nvram_sprom_new(struct bhnd_nvram_d
        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 @@ bhnd_nvram_sprom_read_offset(const struc
 }
 
 /**
- * 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 @@ bhnd_nvram_sprom_read_offset(const struc
  * 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 @@ bhnd_nvram_sprom_getvar_common(struct bh
        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 @@ bhnd_nvram_sprom_getvar_common(struct bh
         * 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 @@ bhnd_nvram_sprom_getvar_common(struct bh
        /*
         * 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 @@ bhnd_nvram_sprom_getvar_common(struct bh
                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 @@ bhnd_nvram_sprom_getvar_common(struct bh
                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 @@ bhnd_nvram_sprom_getvar_common(struct bh
 
                /* 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 @@ bhnd_nvram_sprom_getvar_common(struct bh
                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;

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to