Add hnvram for grabbing env variables during boot

cmd_hnvram.c modifed from uboot/armada

hnvram must be loaded into dram during boot since the uboot heap
uses sram, which is too small. Currently hnvram is stored at
0x87800000 or 120-122 of dram's 128 MB.

Change-Id: I24a6f8ebed08beb3b1026c9765143a1b9d3400d8
diff --git a/board/ruby/ruby.c b/board/ruby/ruby.c
index a4ecd69..c1e0c5c 100644
--- a/board/ruby/ruby.c
+++ b/board/ruby/ruby.c
@@ -198,6 +198,9 @@
 			spi_protect_mode_off();
 		}
 	}
+#ifdef CONFIG_CMD_HNVRAM
+        RUN("hnvram");
+#endif
 	return 0;
 }
 
diff --git a/common/Makefile b/common/Makefile
index 8a875ac..1d0cbd2 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -96,6 +96,7 @@
 ifdef CONFIG_FPGA
 COBJS-$(CONFIG_CMD_FPGA) += cmd_fpga.o
 endif
+COBJS-$(CONFIG_CMD_HNVRAM) += cmd_hnvram.o
 COBJS-$(CONFIG_CMD_I2C) += cmd_i2c.o
 COBJS-$(CONFIG_CMD_IDE) += cmd_ide.o
 COBJS-$(CONFIG_CMD_IMMAP) += cmd_immap.o
diff --git a/common/cmd_hnvram.c b/common/cmd_hnvram.c
new file mode 100644
index 0000000..52f0ae1
--- /dev/null
+++ b/common/cmd_hnvram.c
@@ -0,0 +1,266 @@
+/*
+ * cmd_hnvram.c -- makes hnvram contents available in u-boot.
+ *      Loads contents of hnvram from spi flash and saves it
+ *      to environment variables named HNV_<name>.
+ *
+ * Copyright (C) 2015 Google Inc.
+ * Author: Chris Gibson <cgibson@google.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <command.h>
+#include <common.h>
+#include <malloc.h>
+#include <spi_flash.h>
+
+/* local debug macro */
+#undef HNVRAM_DEBUG
+
+#ifdef HNVRAM_DEBUG
+#define DEBUG(fmt, args...)  printf(fmt, ##args)
+#else
+#define DEBUG(fmt, args...)
+#endif  /* HNVRAM_DEBUG */
+
+#define CONFIG_SF_DEFAULT_MODE SPI_MODE_3
+
+
+// Flash erase block size
+#define HNVRAM_BLOCKSIZE 0x00010000
+
+// Location in DRAM where hnvram variables are stored during boot
+// DRAM is 128 MB [0x80000000 - 0x88000000]
+// Store hnvram at 120 - 122 MB
+#define HNVRAM_DRAM_OFFSET 0x87800000
+
+// Where in flash does hnvram partition start
+#define HNVRAM_MTD_OFFSET 0x00200000
+
+// Total hnvram is 2MB with 4 partitions
+#define MAX_HNVRAM_SIZE     0x00200000
+
+#define HNVRAM_RO_SIZE      0x00100000
+#define HNVRAM_RW_SIZE      0x00040000
+#define HNVRAM_RWB_SIZE     0x00020000
+#define HNVRAM_RAW_FS_SIZE  0x00020000
+
+// B1 and B2 corresponding to RW_OFFSET and RWB_OFFSET
+#define HNVRAM_B1_OFFSET  (HNVRAM_RO_SIZE)
+#define HNVRAM_B2_OFFSET  (HNVRAM_RO_SIZE + HNVRAM_RW_SIZE)
+
+#define CMD_RET_SUCCESS 0
+#define CMD_RET_FAILURE 1
+#define CMD_RET_USAGE -1
+
+// These keys are stored in binary format for historical reasons
+const char *hnvram_binary_keys[] = {
+        "LOADER_VERSION",
+        "HDCP_KEY",
+        "DTCP_KEY",
+};
+
+static void *xmalloc(size_t size)
+{
+        void *p = NULL;
+        if (!(p = malloc(size))) {
+                printf("error: memory not allocated\n");
+                return 0;
+        }
+        memset(p, 0, size);
+        return p;
+}
+
+int read_u8(const char **p)
+{
+        int v = *(const unsigned char *)(*p);
+        *p += 1;
+        return v;
+}
+
+int read_s32_be(const char **p)
+{
+        const unsigned char *vp = (const unsigned char *)*p;
+        *p += 4;
+        return (vp[0]<<24) + (vp[1]<<16) + (vp[2]<<8) + vp[3];
+}
+
+int read_u16_le(const char **p)
+{
+        const unsigned char *up = (const unsigned char *)(*p);
+        *p += 2;
+        return up[0] + (up[1] << 8);
+}
+
+int is_hnvram_binary(const char *name, int namelen)
+{
+        int i;
+        for (i = 0; i < ARRAY_SIZE(hnvram_binary_keys); i++) {
+                const char *k = hnvram_binary_keys[i];
+                if ((int)strlen(k) == namelen && strncmp(k, name, namelen) == 0) {
+                        return 1;
+                }
+        }
+        return 0;
+}
+
+char *encode_hex(const char *s, int len)
+{
+        char *optr, *out = xmalloc(len * 2 + 1);
+        for (optr = out; len > 0; len--) {
+                sprintf(optr, "%02x", read_u8(&s));
+                optr += 2;
+        }
+        return out;
+}
+
+char *encode_macaddr(const char *mac)
+{
+        int i;
+        char *out = xmalloc(6 * 2 + 5 + 2);
+        for (i = 0; i < 6; i++) {
+                sprintf(out + i * 3, "%02X:", read_u8(&mac));
+        }
+        out[6*2 + 5] = '\0';
+        return out;
+}
+
+static void _copy_setenv(const char *name, int namelen,
+                const char *val, int vallen)
+{
+        char *n, *v;
+        if (namelen + vallen < 128) {
+                n = xmalloc(4 + namelen + 1);
+                v = xmalloc(vallen + 1);
+                memcpy(n, "HNV_", 4);
+                memcpy(n + 4, name, namelen);
+                memcpy(v, val, vallen);
+                n[namelen+4] = 0;
+                v[vallen] = 0;
+                setenv(n, v);
+                free(n);
+                free(v);
+        } else {
+                DEBUG("ignoring oversized val: %.15s, vallen: %d\n", val, vallen);
+        }
+}
+
+static int _parse_hnvram(const char *buf, int len)
+{
+        // An hnvram structure. Format is a tag-length-value sequence of:
+        //    [1 byte]   type (1 for notdone, 0 for done)
+        //    [4 bytes]  record length
+        //    [1 byte]   key length
+        //    [x bytes]  key
+        //    [4 bytes]  value length
+        //    [y bytes]  value
+        int rectype, reclen, namelen, vallen;
+        int done = 0;
+        const char *name, *val, *p = buf;
+        while (p - buf <= len + 11) {
+                rectype = read_u8(&p);
+                if (rectype == 0x00) {
+                        printf("done processing hnvram block!\n");
+                        done = 1;
+                        break;
+                }
+                if (rectype != 0x01) {
+                        printf("error: hnvram invalid rectype %x\n", rectype);
+                        return -1;
+                }
+
+                reclen = read_s32_be(&p);
+                if (reclen <= 6 || (p - buf) + reclen >= len) {
+                        printf("error: hnvram invalid reclen %d\n", reclen);
+                        return -1;
+                }
+                namelen = read_u8(&p);
+                if (namelen < 1 || (p - buf) + namelen >= len) {
+                        printf("error: hnvram invalid namelen %d\n", namelen);
+                        return -1;
+                }
+                name = p;
+                p += namelen;
+                vallen = read_s32_be(&p);
+                if (vallen < 0 || (p - buf) + vallen >= len) {
+                        printf("error: hnvram invalid vallen %d\n", vallen);
+                        return -1;
+                }
+                val = p;
+                p += vallen;
+                if (vallen == 6 && namelen >= 8 &&
+                                strncmp("MAC_ADDR", name, 8) == 0) {
+                        char *macstr = encode_macaddr(val);
+                        _copy_setenv(name, namelen, macstr, strlen(macstr));
+                        free(macstr);
+                } else if (is_hnvram_binary(name, namelen)) {
+                        char *hexstr = encode_hex(val, vallen);
+                        _copy_setenv(name, namelen, hexstr, strlen(hexstr));
+                        free(hexstr);
+                } else {
+                        _copy_setenv(name, namelen, val, vallen);
+                }
+        }
+        if (!done) {
+                printf("error: failed to find final hnvram record?\n");
+                return -1;
+        }
+        return 0;
+}
+
+#if defined(CONFIG_CMD_HNVRAM)
+int do_hnvram(void) {
+        char command[60];
+        sprintf(command, "spi_flash read 0x%x 0x%x 0x%x", HNVRAM_MTD_OFFSET, HNVRAM_DRAM_OFFSET, MAX_HNVRAM_SIZE);
+        int ret = run_command(command, 0);
+
+        if (ret) {
+          printf("failed reading from spi-flash at addr: %d\n", HNVRAM_MTD_OFFSET);
+          return ret;
+        }
+
+        char *buf = HNVRAM_DRAM_OFFSET;
+
+        // Next step: Seek to different parts of the buffer that contain the
+        // first, second, and third sections of hnvram.
+        if (_parse_hnvram(buf, HNVRAM_BLOCKSIZE) != 0) {
+                printf("failed parsing hnvram at offset: 0x%p\n", buf);
+                return -1;
+        }
+        if (_parse_hnvram(buf+HNVRAM_B1_OFFSET, HNVRAM_BLOCKSIZE) != 0) {
+                printf("failed parsing hnvram at offset: 0x%p\n", buf+HNVRAM_B1_OFFSET);
+                return -1;
+        }
+        if (_parse_hnvram(buf+HNVRAM_B2_OFFSET, HNVRAM_BLOCKSIZE) != 0) {
+                printf("failed parsing hnvram at offset: 0x%p\n", buf+HNVRAM_B2_OFFSET);
+                return -1;
+        }
+
+        free(buf);
+        return CMD_RET_SUCCESS;
+}
+
+static int do_hnvram_cmd(cmd_tbl_t *cmdtp, int flag, int argc,
+                char * const argv[]) {
+        return do_hnvram();
+}
+
+U_BOOT_CMD(
+        hnvram, 1, 0, do_hnvram_cmd,
+        "load hnvram from flash",
+        "\n"
+        "load hnvram from flash into environment vars named HNV_<name>\n"
+        );
+#endif  /* CONFIG_CMD_HNVRAM */
diff --git a/include/configs/ruby.h b/include/configs/ruby.h
index 4d0c629..ffda11b 100644
--- a/include/configs/ruby.h
+++ b/include/configs/ruby.h
@@ -33,6 +33,7 @@
 /* Commands */
 #define CONFIG_CMD_CONSOLE
 #define CONFIG_CMD_ECHO
+#define CONFIG_CMD_HNVRAM
 #define CONFIG_CMD_MEMORY
 #define CONFIG_CMD_MISC
 #define CONFIG_CMD_RUN
diff --git a/include/configs/ruby_mini.h b/include/configs/ruby_mini.h
index d14332d..2071c40 100644
--- a/include/configs/ruby_mini.h
+++ b/include/configs/ruby_mini.h
@@ -36,6 +36,7 @@
 
 #ifndef  TOPAZ_EP_MINI_UBOOT
 /* Commands */
+#define CONFIG_CMD_HNVRAM
 #define CONFIG_CMD_NET
 #define CONFIG_CMD_PING
 #define CONFIG_CMD_INTR