| /* |
| * 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 */ |