uboot/prism: add hnvram support
This patch adds native hnvram support to u-boot. It loads the hnvram
contents when the board type is a GFLT300 and reconfigures mtdparts
and bootargs to match.
Change-Id: Ia9f9c9ee49bf199a3f2de7a1d4f50b138fd2d0d1
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 */