Merge "uboot/prism: add hnvram support"
diff --git a/board/mv_feroceon/mv_kw2/mv_main.c b/board/mv_feroceon/mv_kw2/mv_main.c
index 89c29ae..3cb9c54 100644
--- a/board/mv_feroceon/mv_kw2/mv_main.c
+++ b/board/mv_feroceon/mv_kw2/mv_main.c
@@ -378,41 +378,80 @@
 	return;
 }
 
+/* This function is used to specify whether to read system variables from hnvram
+ * or the legacy sysvar. For all future FiberJacks (GFLT300 and above) we should
+ * be using hnvram. */
+int use_hnvram(void)
+{
+	if (mvBoardIdGet() == GFLT110_ID)
+		return 0;
+	return 1;
+}
 
-// Try to be compatible with both ginstall and prisminstall.
-// Prismintall sets a BOOT_SIDE sysvar, and ginstall sets
-// ACTIVATED_KERNEL_NAME, which is the same as the TV boxes.
-static void set_boot_variables() {
-	// Try to get the boot partition, either from BOOT_SIDE
-	// (set by prisminstall) or ACTIVATED_KERNEL_NAME (set by ginstall)
-	// First look for BOOT_SIDE.
-	char value[SYSVAR_VALUE];
-	if (sf_getvar("BOOT_SIDE", value, SYSVAR_VALUE) == 0) {
-		printf("BOOT_SIDE = %s ", value);
-		if (value[0] == '2' && value[1] == '\0') {
-			printf("Boot from MTD image2 ...\n");
-			setenv("bootcmd","sf read "LOAD_ADDR_STR" 0xF80000 0xE00000;"
-				   "setenv bootargs ${console} ${mtdparts} debug=1 ${mvNetConfig} ${mvPhoneConfig};"
-				   "bootm "LOAD_ADDR_STR";");
-			return;
-		}
-		if (value[0] == '1' && value[1] == '\0') {
-			printf("Boot from MTD image1 ...\n");
-			setenv("bootcmd","sf read "LOAD_ADDR_STR" 0x180000 0xE00000;"
-				   "setenv bootargs ${console} ${mtdparts} debug=1 ${mvNetConfig} ${mvPhoneConfig};"
-				   "bootm "LOAD_ADDR_STR";");
-			return;
-		}
+char* get_model(void)
+{
+	char *model = NULL;
+	switch(mvBoardIdGet()) {
+		case GFLT110_ID:
+			model = "gflt110";
+			break;
+		case GFLT300_ID:
+			model = "gflt300";
+			break;
+		default:
+			/* Pick the safest default, currently GFLT110 */
+			model = "gflt110";
 	}
+	return model;
+}
 
-	// Now look for ACTIVATED_KERNEL_NAME.  Setenv that value and the bootscript
-	// will branch to the right boot location.
-	if (sf_getvar("ACTIVATED_KERNEL_NAME", value, SYSVAR_VALUE) == 0) {
-		setenv("ACTIVATED_KERNEL_NAME", value);
+static void set_boot_variables(void) {
+	char value[SYSVAR_VALUE];
+	char *env;
+
+	/* If we are reading from hnvram, then get the activated kernel name
+	 * from hnvram, otherwise use sysvar. */
+	if (use_hnvram() == 1) {
+		env = getenv("HNV_ACTIVATED_KERNEL_NAME");
+		if (!env)
+			setenv("HNV_ACTIVATED_KERNEL_NAME", "kernel0");
+		setenv("gfparams",
+			"if test $HNV_ACTIVATED_KERNEL_NAME = kernel1; "
+			"then gfkernel=0x1100000; "
+			"else gfkernel=0x0300000; fi");
 	} else {
-		setenv("ACTIVATED_KERNEL_NAME", "kernel0");
-    }
-	return;
+		if (sf_getvar("ACTIVATED_KERNEL_NAME",
+				value, SYSVAR_VALUE) == 0)
+			setenv("ACTIVATED_KERNEL_NAME", value);
+		else
+			setenv("ACTIVATED_KERNEL_NAME", "kernel0");
+		setenv("gfparams",
+			"if test $ACTIVATED_KERNEL_NAME = kernel1; "
+			"then gfkernel=0xF80000; "
+			"else gfkernel=0x180000; fi");
+	}
+	/* Set the model information to pass down to through a kernel cmdline
+	 * parameter. */
+	setenv("model", get_model());
+
+	setenv("bootcmd",
+		"run gfparams; "
+		"sf read $loadaddr $gfkernel 0xe00000; "
+		"setenv bootargs $console $mtdparts $gfroot $mvNetConfig "
+		"$bootargs_extra; model=$model; "
+		"bootm $loadaddr;");
+}
+
+char* set_mtdparts(void)
+{
+	if (mvBoardIdGet() == GFLT110_ID) {
+		return ("mtdparts=spi_flash:768k(uboot),256k(env),128k(var1),"
+			"128k(var2),128k(sysvar1),128k(sysvar2),14m(image1),"
+			"14m(image2),-(user_data)");
+	}
+	return ("mtdparts=spi_flash:768k(uboot),"
+		"256k(env),2m(hnvram),14m(kernel0),14m(kernel1),"
+		"-(data+jffs2)");
 }
 
 
@@ -465,6 +504,17 @@
 			setenv("console","console=ttyS0,115200");
 	}
 
+	/* debug boot arguments */
+	env = getenv("bootargs_debug");
+	if (!env)
+		setenv("bootargs_debug", "debug=1 login=1 earlyprintk");
+
+	/* if we are not a GFLT110/120, then load hnvram into the u-boot
+	 * environment. */
+	if (mvBoardIdGet() != GFLT110_ID) {
+		do_hnvram();
+	}
+
 #ifdef CONFIG_MTD_PARTITIONS
 	env = getenv("mtdids");
 	if(!env) {
@@ -472,7 +522,7 @@
 	}
 	env = getenv("mtdparts");
 	if(!env) {
-		setenv("mtdparts", MTDPARTS_DEFAULT);
+		setenv("mtdparts", set_mtdparts());
 	}
 	setenv("partition", NULL);
 #endif
@@ -841,26 +891,51 @@
 	sprintf(ethaddr_1,"00:50:43:%02x:%02x:%02x",xl,xi,xj);
 	sprintf(pon_addr,"00:50:43:%02x:%02x:%02x",xj,xk,xl);
 
+	if (use_hnvram() == 1) {
+		/* Check imported hnvram variables for mac addresses. If they're
+		 * not available in the u-boot environment then use the randomly
+		 * generated ones from above. */
+		env = getenv("HNV_ETH_MAC_ADDR");
+		if (!env)
+			setenv("ethaddr", ethaddr_0);
+		else
+			setenv("ethaddr", env);
 
-	/* MAC addresses */
-	env = getenv("ethaddr");
-	if(!env)
-		setenv("ethaddr",ethaddr_0);
-	/* Override ethaddr_0 from sysvar, only overwrites if sysvar is set. */
-	if (!sf_getvar("ETH_MAC_ADDR", ethaddr_0, sizeof(ethaddr_0)))
-		setenv("ethaddr",ethaddr_0);
+		env = getenv("HNV_PON_MAC_ADDR");
+		if (!env)
+			setenv("mv_pon_addr", pon_addr);
+		else
+			setenv("mv_pon_addr", env);
+	} else {
+		/* Check sysvar for mac addresses. If they're not available,
+		 * then use the ones we randomly generated above. */
+		env = getenv("ethaddr");
+		if (!env) {
+			/* Override ethaddr from sysvar only if there is a
+			 * sysvar variable available to replace it with. */
+			if (sf_getvar("ETH_MAC_ADDR", ethaddr_0,
+					sizeof(ethaddr_0)) != 0)
+				setenv("ethaddr", ethaddr_0);
+			else
+				setenv("ethaddr", ethaddr_0);
+		}
+
+		env = getenv("mv_pon_addr");
+		if (!env) {
+			/* Override mv_pon_addr from sysvar only if there is a
+			 * sysvar variable available to replace it with. */
+			if (sf_getvar("PON_MAC_ADDR", pon_addr,
+					sizeof(pon_addr)) != 0)
+				setenv("mv_pon_addr", pon_addr);
+			else
+				setenv("mv_pon_addr", pon_addr);
+		}
+	}
 
 	env = getenv("eth1addr");
 	if(!env)
 		setenv("eth1addr",ethaddr_1);
 
-	env = getenv("mv_pon_addr");
-	if(!env)
-		setenv("mv_pon_addr",pon_addr);
-	/* Override pon_addr from sysvars, only overwrites if sysvar is set. */
-	if (!sf_getvar("PON_MAC_ADDR", pon_addr, sizeof(pon_addr)))
-		setenv("mv_pon_addr",pon_addr);
-
 	env = getenv("ethmtu");
 	if(!env)
 		setenv("ethmtu","1500");
diff --git a/common/Makefile b/common/Makefile
index 77efd12..7e171b3 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -149,6 +149,7 @@
 COBJS-$(CONFIG_VFD) += cmd_vfd.o
 COBJS-y += sysvar.o
 COBJS-y += cmd_sysvar.o
+COBJS-$(CONFIG_CMD_HNVRAM) += cmd_hnvram.o
 
 # others
 COBJS-$(CONFIG_DDR_SPD) += ddr_spd.o
diff --git a/common/cmd_hnvram.c b/common/cmd_hnvram.c
new file mode 100644
index 0000000..dec728a
--- /dev/null
+++ b/common/cmd_hnvram.c
@@ -0,0 +1,265 @@
+/*
+ * 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
+
+#define HNVRAM_BLOCKSIZE 0x00020000
+
+#define HNVRAM_MTD_OFFSET 0x00100000
+#define HNVRAM_B1_OFFSET 0x00100000
+#define HNVRAM_B2_OFFSET 0x00140000
+
+#define MAX_HNVRAM_SIZE  0x00200000   // this is the mtd partition 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) {
+			DEBUG("done processing hnvram block!");
+			done = 1;
+			break;
+		}
+		if (rectype != 0x01) {
+			DEBUG("error: hnvram invalid rectype %x\n", rectype);
+			return -1;
+		}
+
+		reclen = read_s32_be(&p);
+		if (reclen <= 6 || (p - buf) + reclen >= len) {
+			DEBUG("error: hnvram invalid reclen %d\n", reclen);
+			return -1;
+		}
+		namelen = read_u8(&p);
+		if (namelen < 1 || (p - buf) + namelen >= len) {
+			DEBUG("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) {
+			DEBUG("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) {
+		DEBUG("error: failed to find final hnvram record?\n");
+		return -1;
+	}
+	return 0;
+}
+
+#if defined(CONFIG_CMD_HNVRAM)
+int do_hnvram(void) {
+	unsigned int bus = 0;
+	unsigned int cs = 0;
+	unsigned int speed = 0x1312d00;  // hz
+	unsigned int mode = CONFIG_SF_DEFAULT_MODE;
+	char *buf;
+	struct spi_flash *flash;
+
+	flash = spi_flash_probe(bus, cs, speed, mode);
+	if (!flash) {
+		printf("failed to initialize SPI flash at %u:%u\n", bus, cs);
+		return CMD_RET_FAILURE;
+	}
+	buf = (char *)xmalloc(MAX_HNVRAM_SIZE);
+	if (!buf) {
+		printf("failed to allocate memory for hnvram contents\n");
+		return CMD_RET_FAILURE;
+	}
+	DEBUG("malloc'd new hnvram buffer at 0x%p\n", buf);
+
+	// Maximum size of hnvram partition is 2MB, so read up to that.
+	if (spi_flash_read(flash, HNVRAM_MTD_OFFSET, MAX_HNVRAM_SIZE, buf) != 0) {
+		printf("reading hnvram from SPI flash failed: off:%x, len:%x\n",
+			HNVRAM_MTD_OFFSET, MAX_HNVRAM_SIZE);
+		return -1;
+	}
+
+	// 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/common.h b/include/common.h
index 13ce953..4c617f7 100644
--- a/include/common.h
+++ b/include/common.h
@@ -691,6 +691,11 @@
  */
 void show_boot_progress(int val);
 
+/* Include hnvram functions. */
+#ifdef CONFIG_CMD_HNVRAM
+int do_hnvram(void);
+#endif
+
 /* Multicore arch functions */
 #ifdef CONFIG_MP
 int cpu_status(int nr);
diff --git a/include/configs/mv_kw2.h b/include/configs/mv_kw2.h
index 522f259..cba7988 100644
--- a/include/configs/mv_kw2.h
+++ b/include/configs/mv_kw2.h
@@ -112,6 +112,7 @@
 #define CONFIG_CMD_SAR
 //#define CONFIG_CMD_UBIFS
 #define CONFIG_CMD_PRISM
+#define CONFIG_CMD_HNVRAM
 
 #if (defined(MV_INCLUDE_PEX) | defined(MV_INCLUDE_PCI))
 #define CONFIG_PCI           /* pci support               */