blob: 2ef80f73b3595e159633cece29ffbbe1be76af11 [file] [log] [blame]
/*
* cmd_hnvram.c -- makes hnvram contents available in uboot.
* Loads contents of hnvram from mmc 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 <mmc.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 HNVRAM_DEV 0
#define HNVRAM_BLK 0x00000040
#define HNVRAM_CNT 0x00001000
#define HNVRAM_BLOCKSIZE 0x00020000
#define HNVRAM_B1_OFFSET 0x00100000
#define HNVRAM_B2_OFFSET 0x00140000
/* 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, 0xAB, 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) {
printf("failed to find final hnvram record\n");
return -1;
}
return 0;
}
static int _read_parse_hnvram(struct mmc *mmc, char *buf)
{
int n;
DEBUG("reading mmc: dev: %d, blk: 0x%x, cnt: 0x%x\n",
HNVRAM_DEV, HNVRAM_BLK, HNVRAM_CNT);
n = mmc->block_dev.block_read(
HNVRAM_DEV, HNVRAM_BLK, HNVRAM_CNT, (void *)buf);
if (n < 1) {
printf("failed to load hnvram from mmc\n");
return -1;
}
/* flush cache after read */
flush_cache((ulong)buf, HNVRAM_CNT * mmc->read_bl_len);
if (_parse_hnvram(buf, HNVRAM_BLOCKSIZE) != 0) {
printf("failed parsing hnvram at offset: 0\n");
return -1;
}
if (_parse_hnvram(buf+HNVRAM_B1_OFFSET, HNVRAM_BLOCKSIZE) != 0) {
printf("failed parsing hnvram at offset: %x\n", HNVRAM_B1_OFFSET);
return -1;
}
if (_parse_hnvram(buf+HNVRAM_B2_OFFSET, HNVRAM_BLOCKSIZE) != 0) {
printf("failed parsing hnvram at offset: %x\n", HNVRAM_B2_OFFSET);
return -1;
}
return 0;
}
#if defined(CONFIG_CMD_HNVRAM)
int do_hnvram(void) {
struct mmc *mmc;
char *buf;
mmc = find_mmc_device(HNVRAM_DEV);
if (!mmc) {
printf("no mmc device found in slot %d\n", HNVRAM_DEV);
return CMD_RET_FAILURE;
}
mmc_init(mmc);
/* HNVRAM_CNT is the number of blocks and the mmc block size is
* mmc->read_bl_len (512) */
buf = (char *)xmalloc(HNVRAM_CNT * mmc->read_bl_len);
if (!buf) {
printf("failed to allocate memory for hnvram\n");
return CMD_RET_FAILURE;
}
DEBUG("malloc'd new hnvram buffer at 0x%p\n", buf);
if (_read_parse_hnvram(mmc, buf) != 0) {
printf("failed to parse hnvram contents\n");
free(buf);
return CMD_RET_FAILURE;
}
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 mmc",
"\n"
"load hnvram from mmc into environment vars named HNV_<name>\n"
);
#endif /* CONFIG_CMD_HNVRAM */