--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2019 Broadcom Ltd.
+ */
+
+#include <common.h>
+
+#include <asm/arch/ddr.h>
+#include <linux/ctype.h>
+#include <mtd.h>
+#include <nand.h>
+#include <stdlib.h>
+#include <string.h>
+#include <environment.h>
+#include <cli.h>
+#include <linux/bitops.h>
+#include <linux/crc32.h>
+#include <ubi_uboot.h>
+#include "bca_common.h"
+#include "bca_sdk.h"
+#include "reimage.h"
+#include "spl_env.h"
+
+static struct reimager rei = { 0 };
+
+DECLARE_GLOBAL_DATA_PTR;
+
+__weak void boost_cpu_clock(void)
+{
+}
+
+int board_sdk_late_init_e(void)
+{
+ boost_cpu_clock();
+
+ /* CUSTOMIZE -- set default behavior here */
+ env_set("bootdelay", "5");
+ env_set("bootcmd", "reimage auto");
+ return 0;
+}
+
+void hook_dram_init(void)
+{
+ int shift;
+ u64 size;
+#if defined(CONFIG_BCM63138) || defined(CONFIG_BCM63148) || defined(CONFIG_BCM4908) || defined(CONFIG_BCM6858)
+ shift =
+ (MEMC->GLB_GCFG & MEMC_GLB_GCFG_SIZE1_MASK) >>
+ MEMC_GLB_GCFG_SIZE1_SHIFT;
+#else
+ shift =
+ (MEMC->GLB_FSBL_STATE & MEMC_GLB_FSBL_DRAM_SIZE_MASK) >>
+ MEMC_GLB_FSBL_DRAM_SIZE_SHIFT;
+#endif
+ size = 1 << shift; // in MB
+ printf("DDR size from controller %dMB\n", size);
+ gd->ram_size = (phys_size_t) (size << 20);
+ gd->ram_base = 0;
+ gd->bd->bi_dram[0].start = 0;
+ gd->bd->bi_dram[0].size = (phys_size_t) gd->ram_size;
+}
+
+static int check_nv_crc(struct nvram_s *nv);
+
+static int check_nv_crc(struct nvram_s *nv)
+{
+ int orig, new;
+ printf("original CRC 0x%x\n", nv->ulCheckSum);
+ orig = nv->ulCheckSum;
+ nv->ulCheckSum = 0;
+ /* use crc32_le to get standard CRC rather than the unusual one
+ * used for uboot environment files */
+ new = crc32_le(0xffffffff, nv, 1024);
+ printf("computed CRC 0x%x\n", new);
+ nv->ulCheckSum = orig;
+ return (new == orig);
+}
+
+static int ubi_dev_scan(struct mtd_info *info, const char *vid_header_offset);
+
+static int ubi_dev_scan(struct mtd_info *info, const char *vid_header_offset)
+{
+ char ubi_mtd_param_buffer[80];
+ int err;
+
+ if (!vid_header_offset)
+ sprintf(ubi_mtd_param_buffer, "%s", info->name);
+ else
+ sprintf(ubi_mtd_param_buffer, "%s,%s", info->name,
+ vid_header_offset);
+
+ err = ubi_mtd_param_parse(ubi_mtd_param_buffer, NULL);
+ if (err)
+ return -err;
+
+ err = ubi_init();
+ if (err)
+ return -err;
+
+ return 0;
+}
+
+static int erase(struct mtd_info *mtd, int first, int blocks);
+
+static int erase(struct mtd_info *mtd, int first, int blocks)
+{
+ struct erase_info erase_op = { };
+ int ret;
+
+ erase_op.mtd = mtd;
+ erase_op.addr = first * mtd->erasesize;
+ erase_op.len = blocks * mtd->erasesize;
+ erase_op.scrub = 0;
+ printf("Erasing %d blocks from block %d\n", blocks, first);
+
+ while (erase_op.len) {
+ ret = mtd_erase(mtd, &erase_op);
+
+ /* Abort if its not a bad block error */
+ if (ret != -EIO)
+ break;
+
+ printf("Skipping bad block at 0x%08llx\n", erase_op.fail_addr);
+
+ /* Skip bad block and continue behind it */
+ erase_op.len -= erase_op.fail_addr - erase_op.addr;
+ erase_op.len -= mtd->erasesize;
+ erase_op.addr = erase_op.fail_addr + mtd->erasesize;
+ }
+
+ if (ret && ret != -EIO)
+ ret = -1;
+ else
+ ret = 0;
+ return (ret);
+};
+
+static int do_nvram_parse(cmd_tbl_t * cmdtp, int flag, int argc,
+ char *const argv[]);
+static int do_prepare(cmd_tbl_t * cmdtp, int flag, int argc,
+ char *const argv[]);
+static int do_finish(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]);
+
+static int do_prepare(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[])
+{
+ struct erase_info erase_op = { };
+ char more_env[1024];
+ int more_env_size;
+ int i, ret;
+ size_t sz = 0;
+ int rblk, wblk;
+ char *bp;
+ char *cp;
+ int block;
+ int err;
+ long offset;
+ if (!rei.payload_blocks) {
+ printf("parse first\n");
+ return (-1);
+ }
+ struct mtd_info *mtd = NULL;
+ wblk = rei.burn_first_start;
+ bp = rei.loader_payload =
+ malloc(rei.blocksizeK * 1024 * rei.loader_blocks);
+ if (!bp) {
+ printf("loader payload malloc failed\n");
+ return (-1);
+ }
+ mtd_probe_devices();
+ if (rei.pure_payload_image) {
+ int count = 0;
+ int leb = 0;
+ int offset = 0;
+ int m, s;
+ m = rei.pure_payload_volume_index;
+ s = rei.ubi->volumes[m]->usable_leb_size;
+ cp = CONFIG_SYS_LOAD_ADDR;
+ for (leb = 0;
+ leb * s < 2048 + rei.blocksizeK * 1024 * rei.loader_blocks;
+ leb++) {
+ err =
+ ubi_eba_read_leb(rei.ubi, rei.ubi->volumes[m], leb,
+ cp + leb * s, 0, s, 0);
+ }
+ memcpy(rei.loader_payload, cp + 2048,
+ rei.blocksizeK * 1024 * rei.loader_blocks);
+ printf("start of image device = 0x%x\n",
+ 2048 + rei.blocksizeK * 1024 * rei.loader_blocks);
+ leb = (2048 + rei.blocksizeK * 1024 * rei.loader_blocks) / s;
+ offset = (2048 + rei.blocksizeK * 1024 * rei.loader_blocks) % s;
+ count = 0;
+ mtd = get_mtd_device_nm("nand0");
+ if (IS_ERR_OR_NULL(mtd)) {
+ printf("failed to get mtd\n");
+ return (-1);
+ }
+ erase(mtd, rei.erase_first_start, rei.erase_first_blocks);
+ put_mtd_device(mtd);
+ while (count < rei.payload_blocks * rei.blocksizeK * 1024) {
+ cp = CONFIG_SYS_LOAD_ADDR;
+ while (((int)cp < CONFIG_SYS_LOAD_ADDR + SZ_16M)
+ && (count <
+ rei.payload_blocks * rei.blocksizeK *
+ 1024)) {
+ err =
+ ubi_eba_read_leb(rei.ubi,
+ rei.ubi->volumes[m], leb,
+ cp, offset, s - offset, 0);
+ printf("R");
+ // printf
+ // (" leb %d off 0x%x new 0x%x count %x --> %p\n",
+ // leb, offset, s - offset, count, cp);
+ count = count + s - offset;
+ cp = cp + s - offset;
+ offset = 0;
+ leb++;
+ }
+ // printf("final cp is %p next leb %d\n", cp, leb);
+ if ((int)cp > CONFIG_SYS_LOAD_ADDR + SZ_16M) {
+ offset =
+ s - ((int)cp -
+ (CONFIG_SYS_LOAD_ADDR + SZ_16M));
+ leb--;
+ // printf
+ // ("adjusted offset to 0x%x and next leb to %d\n",
+ // offset, leb);
+ }
+ mtd = get_mtd_device_nm("nand0");
+ if (IS_ERR_OR_NULL(mtd)) {
+ printf("failed to get mtd\n");
+ return (-1);
+ }
+ cp = CONFIG_SYS_LOAD_ADDR;
+ while (((int)cp + mtd->erasesize) <=
+ CONFIG_SYS_LOAD_ADDR + SZ_16M) {
+ if (wblk <
+ (rei.burn_first_start +
+ rei.burn_first_blocks)) {
+ if (!mtd_block_isbad
+ (mtd, wblk * mtd->erasesize)) {
+ i = mtd_write(mtd,
+ wblk *
+ mtd->erasesize,
+ mtd->erasesize,
+ &sz, cp);
+ printf("W");
+ // printf
+ // (" 0x%x bytes at %p -> blk %d\n",
+ // sz, cp, wblk);
+ cp += sz;
+ } else {
+ printf("bad");
+ }
+ wblk++;
+ } else {
+ printf
+ ("\nFIXME -- remainder not implemented\n");
+ }
+ }
+ put_mtd_device(mtd);
+ }
+ }
+
+ else {
+ mtd = get_mtd_device_nm("nand0");
+ if (IS_ERR_OR_NULL(mtd)) {
+ printf("failed to get mtd\n");
+ return (-1);
+ }
+ // FIXME -- loop on payload until we have enough data
+ // first --> loader (DDR)
+ // next --> burn_first (until exhausted)
+ // finally --> allocate remainder and copy to DDR
+ rblk = rei.payload_start;
+ while (bp <
+ (rei.loader_payload +
+ mtd->erasesize * rei.loader_blocks)) {
+ i = mtd_read(mtd, rblk * mtd->erasesize, mtd->erasesize,
+ &sz,
+ rei.loader_payload +
+ mtd->erasesize * (rblk -
+ rei.payload_start));
+ printf("read loader -> mem %d bytes ret %d\n", sz, i);
+ rblk++;
+ bp += sz;
+ }
+ printf("loader in memory buffer at 0x%x and size 0x%x\n",
+ rei.loader_payload, mtd->erasesize * rei.loader_blocks);
+ erase(mtd, rei.erase_first_start, rei.erase_first_blocks);
+ // rblk = rei.payload_start + rei.loader_blocks;
+ block = rei.loader_blocks;
+ while (block < (rei.loader_blocks + rei.payload_blocks)) {
+ i = mtd_read(mtd, rblk * mtd->erasesize, mtd->erasesize,
+ &sz, (char *)CONFIG_SYS_LOAD_ADDR);
+ printf("R");
+ if (sz) {
+ block++;
+ i = 1;
+ while (i) {
+ if (wblk <
+ (rei.burn_first_start +
+ rei.burn_first_blocks)) {
+ if (!mtd_block_isbad
+ (mtd,
+ wblk * mtd->erasesize)) {
+ i = mtd_write(mtd,
+ wblk *
+ mtd->
+ erasesize,
+ mtd->
+ erasesize,
+ &sz,
+ (char *)
+ CONFIG_SYS_LOAD_ADDR);
+ printf("W", sz, i);
+ } else {
+ printf("bad");
+ }
+ wblk++;
+ } else {
+ if (!rei.remaining_payload) {
+ rei.remaining_payload =
+ malloc
+ (mtd->erasesize *
+ (rei.loader_blocks
+ +
+ rei.payload_blocks
+ - block + 1));
+ rei.remaining_payload_len = 0;
+ if (!rei.
+ remaining_payload) {
+ printf
+ ("malloc for remaining failed\n");
+ return (1);
+ }
+ }
+ memcpy(rei.remaining_payload +
+ rei.
+ remaining_payload_len,
+ (char *)
+ CONFIG_SYS_LOAD_ADDR,
+ mtd->erasesize);
+ rei.remaining_payload_len +=
+ mtd->erasesize;
+ printf("M");
+ i = 0;
+ }
+ }
+ }
+ rblk++;
+
+ }
+ printf("\n");
+ put_mtd_device(mtd);
+
+ }
+
+ cp = more_env;
+
+ cp += sprintf(cp, "ethaddr=%x:%x:%x:%x:%x:%x",
+ rei.nvram.ucaBaseMacAddr[0],
+ rei.nvram.ucaBaseMacAddr[1],
+ rei.nvram.ucaBaseMacAddr[2],
+ rei.nvram.ucaBaseMacAddr[3],
+ rei.nvram.ucaBaseMacAddr[4], rei.nvram.ucaBaseMacAddr[5]);
+ *(cp++) = '\0';
+
+ cp +=
+ sprintf(cp,
+ "bootcmd=printenv;run once;run check_flashback;printenv;sdk boot_img");
+ *(cp++) = '\0';
+
+ cp += sprintf(cp, "tries=3");
+ *(cp++) = '\0';
+
+ cp +=
+ sprintf(cp,
+ "check_flashback=test $tries -eq 0 || echo $tries ; setexpr tries $tries - 1 ; saveenv ; test $tries -eq 0 && run do_flashback");
+ *(cp++) = '\0';
+
+ cp +=
+ sprintf(cp,
+ "do_flashback=echo here is where I would have run flashback");
+ *(cp++) = '\0';
+
+ cp += sprintf(cp, "once=sdk metadata 1 1;setenv once true;saveenv");
+ *(cp++) = '\0';
+
+ *(cp++) = '\0';
+ more_env_size = cp - more_env + 1;
+
+ for (bp = rei.loader_payload;
+ bp < rei.loader_payload + mtd->erasesize * rei.loader_blocks;
+ bp += 4096) {
+ int *tenv;
+ env_t *ep;
+ uint32_t new, crc;
+ tenv = (int *)bp;
+ printf("env check at %x\r", bp - rei.loader_payload);
+ if (tenv[0] == BOOT_MAGIC_MAGIC) {
+ printf("\nGOT IT\n");
+ ep = (env_t *) & tenv[2];
+ memcpy(&crc, &ep->crc, sizeof(crc));
+ /* specifically use uboot's env crc function
+ * even though we have included the standard
+ * linux crc32 */
+ new = the_env_crc32(0, ep->data, tenv[1] - 4);
+ if (new != crc) {
+ printf("bad\n");
+ } else {
+ printf("good\n");
+
+ for (i = 0; i < tenv[1] - 4; i++) {
+ if ((ep->data[i] == '\0')
+ && (ep->data[i + 1] == '\0')) {
+ memcpy(&ep->data[i + 1],
+ more_env, more_env_size);
+ break;
+ }
+ }
+ new = the_env_crc32(0, ep->data, tenv[1] - 4);
+ memcpy(&ep->crc, &new, sizeof(new));
+ }
+ }
+ }
+
+ printf("\n");
+ return (0);
+}
+
+static int do_finish(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[])
+{
+ int i, ret;
+ unsigned long time_start, time_mid, time_end;
+ size_t sz = 0;
+ int rblk, wblk;
+ char *bp;
+ char cmd[256];
+ int block;
+ long offset;
+ if (!rei.payload_blocks) {
+ printf("parse first\n");
+ return (-1);
+ }
+ run_command("ubifsumount", 0);
+ run_command("ubi detach", 0);
+ struct mtd_info *mtd = NULL;
+ mtd_probe_devices();
+ mtd = get_mtd_device_nm("nand0");
+ if (IS_ERR_OR_NULL(mtd)) {
+ printf("failed to get mtd\n");
+ return (-1);
+ }
+ if (!rei.loader_payload) {
+ printf("loader payload isn't set\n");
+ return (-1);
+ }
+ time_start = get_timer(0);
+ erase(mtd, 0, rei.loader_blocks);
+ for (i = 0; i < rei.loader_blocks; i++) {
+ offset = i * mtd->erasesize;
+ ret = mtd_write(mtd,
+ i * mtd->erasesize,
+ mtd->erasesize, &sz,
+ &rei.loader_payload[i * mtd->erasesize]);
+ printf("w mem->ldr %d b ret %d\n", sz, ret);
+ }
+ erase(mtd, rei.burn_remaining_start, rei.burn_remaining_blocks);
+ wblk = rei.burn_remaining_start;
+ i = 0;
+ while (i < rei.remaining_payload_len) {
+ printf("rp %x => %d\n", i, wblk);
+ ret = mtd_write(mtd,
+ wblk * mtd->erasesize,
+ mtd->erasesize, &sz, &rei.remaining_payload[i]);
+ i += sz;
+ wblk++;
+ }
+
+ erase(mtd, rei.erase_last_start, rei.erase_last_blocks);
+ put_mtd_device(mtd);
+ time_mid = get_timer(0);
+ sprintf(cmd, "%s:%lld(loader),%lld@%lld(image)",
+ "brcmnand.0",
+ (long long)(rei.loader_blocks * (long long)mtd->erasesize),
+ (long long)(mtd->size -
+ (rei.loader_blocks + 8) * mtd->erasesize),
+ (long long)(rei.loader_blocks * mtd->erasesize));
+ run_command("mtdparts delall", 0);
+ env_set("mtdparts", cmd);
+ run_command("mtdparts", 0);
+ if (rei.preserved_data_max_len) {
+ run_command("ubi part image", 0);
+
+ sprintf(cmd, "ubi create transition 0x%x dynamic 33",
+ rei.preserved_data_max_len);
+ run_command(cmd, 0);
+ sprintf(cmd, "ubi write 0x%x transition 0x%x",
+ rei.preserved_data, rei.preserved_data_max_len);
+ run_command(cmd, 0);
+ }
+
+ time_end = get_timer(0);
+ run_command("ubi detach", 0);
+ printf("start %ld\nsafe %ld\ndone %\ld\nHZ %d\n", time_start,
+ time_mid, time_end, CONFIG_SYS_HZ);
+ return (0);
+
+}
+
+static void set_rei_ranges(int i);
+
+static void set_rei_ranges(int i)
+{
+ char *cp;
+ struct nvram_s *nv;
+ nv = &rei.nvram;
+ printf("ranges for image %d\n", i);
+ rei.erase_first_start = nv->ulNandPartOfsKb[3 - i] / rei.blocksizeK;
+ rei.erase_first_blocks = nv->ulNandPartSizeKb[3 - i] / rei.blocksizeK;
+ if (rei.erase_first_start < rei.loader_blocks) {
+ rei.burn_first_start = rei.loader_blocks;
+ rei.burn_first_blocks =
+ rei.erase_first_blocks -
+ (rei.loader_blocks - rei.erase_first_start);
+ } else {
+ rei.burn_first_start = rei.erase_first_start;
+ rei.burn_first_blocks = rei.erase_first_blocks;
+ }
+ if (cp = env_get("burn_first_blocks")) {
+ rei.burn_first_blocks = simple_strtoul(cp, NULL, 0);
+ }
+ printf("rei.erase_first_start=%d\n", rei.erase_first_start);
+ printf("rei.erase_first_blocks=%d\n", rei.erase_first_blocks);
+ printf("rei.burn_first_start=%d\n", rei.burn_first_start);
+ printf("rei.burn_first_blocks=%d\n", rei.burn_first_blocks);
+ rei.burn_remaining_start = nv->ulNandPartOfsKb[i] / rei.blocksizeK;
+ rei.burn_remaining_blocks = nv->ulNandPartSizeKb[i] / rei.blocksizeK;
+ if (rei.burn_remaining_start < rei.loader_blocks) {
+ rei.burn_remaining_start = rei.loader_blocks;
+ rei.burn_remaining_blocks =
+ rei.burn_remaining_blocks -
+ (rei.loader_blocks - rei.burn_remaining_start);
+ }
+}
+
+static int do_nvram_parse(cmd_tbl_t * cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ // int *haystack = (int *)0x1000000;
+ int i, j, k, m;
+ int ret = 0;
+ u_char *cp;
+ int blocksize;
+ int err;
+ long int needle;
+ char split[128];
+ int payload_loader_size = 0;
+ int payload_size = 0;
+ struct nvram_s *nv;
+ u_char tmp[2048];
+ // char cmd[256];
+ long int *ip;
+ struct mtd_info *mtd;
+ size_t sz = 0;
+ int crc;
+
+ memcpy(&needle, NVRAM_MAGIC, 8);
+ mtd_probe_devices();
+ mtd = get_mtd_device_nm("nand0");
+ if (IS_ERR_OR_NULL(mtd)) {
+ printf("failed to get mtd\n");
+ return (-1);
+ }
+ i = mtd_read(mtd, 0, 2 << 20, &sz, (u_char *) CONFIG_SYS_LOAD_ADDR);
+ nv = 0x1010580;
+ if (!check_nv_crc(nv)) {
+ nv = NULL;
+ for (ip = (long int *)CONFIG_SYS_LOAD_ADDR;
+ ip < (long int *)(CONFIG_SYS_LOAD_ADDR + SZ_2M);
+ ip = ip + (2048 / sizeof(long int))) {
+ if (*ip == needle) {
+ printf("got it %p\n", ip);
+ nv = &ip[1];
+ if (check_nv_crc(nv)) {
+ printf("nvram CRC OK\n");
+ } else {
+ printf("nvram CRC failed\n");
+ }
+ }
+ }
+ }
+ if (!nv) {
+ printf("failed\n");
+ return (1);
+ }
+ memcpy(&rei.nvram, nv, sizeof(rei.nvram));
+ printf("version %d\n", nv->version);
+ memset(tmp, 0, 24);
+ strncpy(tmp, nv->boardid, 16);
+ printf("boardid %s\n", tmp);
+ printf("bootline %s\n", nv->bootline);
+ printf("afeids %x,%x\n", nv->afeId[0], nv->afeId[1]);
+ printf("MCB %x\n", nv->ulMemoryConfig);
+ for (i = 0; i < NP_TOTAL; i++) {
+ printf("part %d offset %dK size %dK\n", i,
+ nv->ulNandPartOfsKb[i], nv->ulNandPartSizeKb[i]);
+ }
+ memset(tmp, 0, 24);
+ strncpy(tmp, nv->szVoiceBoardId, 16);
+ printf("voiceboardid %s\n", tmp);
+
+ blocksize = mtd->erasesize;
+ rei.blocksizeK = mtd->erasesize >> 10;
+ put_mtd_device(mtd);
+ env_set("mtdids", "nand0=brcmnand.0");
+ sprintf(tmp, "%s:%dK@%dK(nvram)ro,%dK@%dK(image1)ro,%dK@%dK(image2)ro,",
+ "brcmnand.0",
+ nv->ulNandPartSizeKb[0], nv->ulNandPartOfsKb[0],
+ nv->ulNandPartSizeKb[1], nv->ulNandPartOfsKb[1],
+ nv->ulNandPartSizeKb[2], nv->ulNandPartOfsKb[2]
+ );
+ k = nv->ulNandPartSizeKb[2] + nv->ulNandPartOfsKb[2];
+ for (i = 0; i < 3; i++) {
+ j = nv->part_info[i].size;
+ j = (j & 0xc000 == 0xc000) ? 0 : j;
+ printf("misc%d size is %d\n", i + 1, j);
+ if (j) {
+ sprintf(tmp + strlen(tmp), "%dK@%dK(misc%d),",
+ j * 1024, k, i + 1);
+ k = k + j * 1024;
+ }
+ }
+ sprintf(tmp + strlen(tmp), "%dK@%dK(data)",
+ nv->ulNandPartSizeKb[3], nv->ulNandPartOfsKb[3]
+ );
+ printf("setting mtdparts to %s\n", tmp);
+ env_set("mtdparts", tmp);
+ run_command("mtdparts", 0);
+ sprintf(tmp, "0x%x", nv->ulNandPartSizeKb[0] * 1024);
+ env_set("nvram_size", tmp);
+ run_command("nand info", 0);
+ printf("erase block %d\n", blocksize);
+ strcpy(split, MASQ_SPLIT_A);
+ strcat(split, MASQ_SPLIT_B);
+ mtd_probe_devices();
+ for (i = 1; i < 3; i++) {
+ sprintf(tmp, "image%d", i);
+ mtd = get_mtd_device_nm(tmp);
+ if (IS_ERR_OR_NULL(mtd)) {
+ printf("failed to get mtd\n");
+ return (-1);
+ }
+ for (j = 0; j < 20; j++) {
+ // printf("read i=%d j=%d\n",i,j);
+ ret = mtd_read(mtd, j * blocksize, 2048, &sz, tmp);
+ // printf("ret %d read %d\n",ret,sz);
+ if (0 == strncmp(split, tmp, strlen(split) + 1)) {
+ printf("found image%d block %d\n", i, j);
+ cp = &tmp[strlen(split) + 1];
+ payload_loader_size =
+ simple_strtoul(cp, NULL, 0);
+ cp = cp + strlen(cp) + 1;
+ payload_size = simple_strtoul(cp, NULL, 0);
+ printf("loader size %d payload size %d\n",
+ payload_loader_size, payload_size);
+ rei.loader_blocks =
+ payload_loader_size / blocksize;
+ rei.payload_blocks = payload_size / blocksize;
+ rei.split_image_start =
+ (nv->ulNandPartOfsKb[i] / rei.blocksizeK);
+ rei.split_image_end =
+ (nv->ulNandPartOfsKb[i] +
+ nv->ulNandPartSizeKb[i]) / rei.blocksizeK -
+ 1;
+ rei.payload_start =
+ (nv->ulNandPartOfsKb[i] / rei.blocksizeK) +
+ j + 1;
+/////
+ set_rei_ranges(i);
+ i = j = 30;
+ break;
+ }
+
+ }
+ put_mtd_device(mtd);
+ /* the following will probably be removed ... reimage will always be packaged as a split image */
+ /* if we do keep it, after attaching, it needs to check for volumes other than the rootfs */
+ /* or the number of volumes */
+ if ((0 == 1) && (i < 3)) {
+ if (rei.ubi)
+ ubi_exit();
+
+ rei.ubi = NULL;
+ /* didn't find marker yet */
+ err = ubi_dev_scan(mtd, NULL);
+ if (err) {
+ printf("UBI init error %d\n", err);
+ printf
+ ("Please check, if the correct MTD partition is used (size big enough?)\n");
+ }
+
+ rei.ubi = ubi_devices[0];
+
+ sprintf(tmp, "image%d", i);
+ if (rei.ubi) {
+ printf("part %s attaches as ubi\n", tmp);
+ for (m = 0; m < (rei.ubi->vtbl_slots + 1); m++) {
+ if (!rei.ubi->volumes[m])
+ continue; /* Empty record */
+ printf("name %s\n",
+ rei.ubi->volumes[m]->name);
+ if (0 ==
+ strcmp("payload",
+ rei.ubi->volumes[m]->name)) {
+ /* found payload volume */
+ rei.pure_payload_image = i;
+ rei.pure_payload_volume_index =
+ m;
+ i = 30;
+ }
+ }
+ }
+ }
+ if (rei.pure_payload_image) {
+ int m, s, t;
+ m = rei.pure_payload_volume_index;
+ s = rei.ubi->volumes[m]->usable_leb_size;
+ err =
+ ubi_eba_read_leb(rei.ubi, rei.ubi->volumes[m],
+ 0, CONFIG_SYS_LOAD_ADDR, 0, s, 0);
+ cp = CONFIG_SYS_LOAD_ADDR;
+ t = simple_strtoul(cp, &cp, 0);
+ rei.loader_blocks = t / blocksize;
+ printf("loader size %d is %d blocks\n", t, blocksize);
+ cp++;
+ t = simple_strtoul(cp, &cp, 0);
+ rei.payload_blocks = t / blocksize;
+ printf("payload size %d is %d blocks\n", t, blocksize);
+ set_rei_ranges(rei.pure_payload_image);
+
+ }
+
+ }
+
+ nv = &rei.nvram;
+ rei.erase_last_start =
+ (nv->ulNandPartOfsKb[2] + nv->ulNandPartSizeKb[2]) / rei.blocksizeK;
+ rei.erase_last_blocks =
+ ((nv->ulNandPartOfsKb[3] +
+ nv->ulNandPartSizeKb[3]) / rei.blocksizeK) - rei.erase_last_start;
+ return (ret);
+}
+
+static int do_preserve_allocate(cmd_tbl_t * cmdtp, int flag, int argc,
+ char *const argv[]);
+
+static int do_preserve_save(cmd_tbl_t * cmdtp, int flag, int argc,
+ char *const argv[]);
+static int do_preserve_allocate(cmd_tbl_t * cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int len;
+ char *cp;
+ len = simple_strtoul(argv[1], NULL, 0);
+ cp = malloc(len);
+ if (!cp) {
+ printf("malloc failed\n");
+ return (1);
+ }
+ cp[0] = '\0';
+ rei.preserved_data = cp;
+ rei.preserved_data_len = 0;
+ rei.preserved_data_max_len = len;
+ printf("allocated 0x%x bytes at %x\n", len, cp);
+ return (0);
+}
+
+static int do_preserve_save(cmd_tbl_t * cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ char *cp;
+ int len;
+ if (argc != 2) {
+ printf("filename required\n");
+ return (-1);
+ }
+ if (cp = env_get("filesize")) {
+ len = simple_strtoul(cp, NULL, 16);
+ } else {
+ printf("filesize in env is not set\n");
+ return (1);
+ }
+ if (rei.preserved_data_len + strlen(argv[1]) + 16 + len >
+ rei.preserved_data_max_len) {
+ printf("allocated space exhausted\n");
+ return (1);
+ }
+ rei.preserved_data_len +=
+ sprintf(rei.preserved_data + rei.preserved_data_len, "%s\n%d\n",
+ argv[1], len);
+ memcpy(rei.preserved_data + rei.preserved_data_len,
+ CONFIG_SYS_LOAD_ADDR, len);
+ rei.preserved_data_len += len;
+ *((char *)(rei.preserved_data + rei.preserved_data_len)) = '\n';
+ printf("preserved %d bytes as %s\n", len, argv[1]);
+ return (0);
+}
+
+static int do_commit(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]);
+static int do_commit(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[])
+{
+ struct mtd_info *mtd = NULL;
+ long offset;
+ int i;
+ size_t sz = 0;
+ int ret = 0;
+ int rblk;
+ if (!rei.loader_payload) {
+ printf("prepare revertable first\n");
+ return (-1);
+ }
+ mtd_probe_devices();
+ mtd = get_mtd_device_nm("nand0");
+ erase(mtd, rei.erase_first_start, rei.erase_first_blocks);
+ erase(mtd, rei.erase_last_start, rei.erase_last_blocks);
+ erase(mtd, 0, rei.loader_blocks);
+ rblk = 0;
+ for (i = 0; i < rei.loader_blocks; i++) {
+ if (mtd_block_isbad(mtd, (uint64_t) i * mtd->erasesize)) {
+ printf("skip bad block %d\n", i);
+ } else {
+ offset = i * mtd->erasesize;
+ ret = mtd_write(mtd,
+ i * mtd->erasesize,
+ mtd->erasesize, &sz,
+ &rei.loader_payload[rblk * mtd->erasesize]);
+ printf("w mem->ldr %d b ret %d\n", sz, ret);
+ if (ret == 0) {
+ rblk++;
+ }
+ }
+ }
+ put_mtd_device(mtd);
+ return (ret);
+}
+
+static int do_revertable(cmd_tbl_t * cmdtp, int flag, int argc,
+ char *const argv[]);
+static int do_revertable(cmd_tbl_t * cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct mtd_info *mtd = NULL;
+ char *bp;
+ int rblk;
+ int i, ret = 0;
+ size_t sz = 0;
+ char more_env[1024];
+ int more_env_size;
+ // parse nvram
+ if (!rei.payload_blocks) {
+ printf("parse first\n");
+ return (-1);
+ }
+ mtd_probe_devices();
+ mtd = get_mtd_device_nm("nand0");
+ if (IS_ERR_OR_NULL(mtd)) {
+ printf("failed to get mtd\n");
+ return (-1);
+ }
+ erase(mtd, rei.split_image_start,
+ rei.payload_start - rei.split_image_start);
+ // read loader to ddr
+ bp = rei.loader_payload =
+ malloc(rei.blocksizeK * 1024 * rei.loader_blocks);
+ if (!bp) {
+ printf("loader payload malloc failed\n");
+ return (-1);
+ }
+ rblk = rei.payload_start;
+ while (bp < (rei.loader_payload + mtd->erasesize * rei.loader_blocks)) {
+ i = mtd_read(mtd, rblk * mtd->erasesize, mtd->erasesize,
+ &sz,
+ rei.loader_payload +
+ mtd->erasesize * (rblk - rei.payload_start));
+ printf("read loader -> mem %d bytes ret %d\n", (int)sz, i);
+ rblk++;
+ bp += sz;
+ }
+ // erase start of reimage image to end of loader
+ erase(mtd, rei.payload_start, rei.loader_blocks);
+ // preserve files
+ // update loader in ddr (env)
+ reimage_env_append(&rei);
+ put_mtd_device(mtd);
+ return (ret);
+}
+
+static int do_read_recovery(cmd_tbl_t * cmdtp, int flag, int argc,
+ char *const argv[]);
+static int do_read_recovery(cmd_tbl_t * cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct mtd_info *mtd = NULL;
+ char *bp;
+ int rblk;
+ int blocks;
+ int pages;
+ int chunk = 0;
+ size_t sz = 0;
+ loff_t off;
+ int i, j, n, z, ret = 0;
+ u32 *up32;
+ int block = 0;
+ int page = 0;
+ struct mtd_oob_ops oob_ops;
+ u32 spare[200];
+ mtd = get_mtd_device_nm("nand0");
+ blocks = (mtd->size / mtd->erasesize);
+ pages = (mtd->erasesize / mtd->writesize);
+ rei.recovery_chunks_list = CONFIG_SYS_LOAD_ADDR;
+ rei.recovery_data_buf =
+ CONFIG_SYS_LOAD_ADDR + (blocks * pages +
+ 1) * sizeof(struct recovery_chunks);
+ rei.recovery_data_len = 0;
+ if (!rei.recovery_chunks_list) {
+ printf("recovery chunks malloc failed\n");
+ return (-1);
+ }
+ printf("Flash device is %d blocks of %d pages of %d bytes\n", blocks,
+ pages, mtd->writesize);
+ while (block < blocks) {
+ if ((block >= rei.split_image_start)
+ && (block <= rei.split_image_end)) {
+ // printf("block %d is part of reimage\n", block);
+ block++;
+ continue;
+ }
+ off =
+ (uint64_t) page *mtd->writesize +
+ (uint64_t) block *mtd->erasesize;
+ if (page == 0) {
+ if (mtd_block_isbad
+ (mtd, (uint64_t) block * mtd->erasesize)) {
+ /* check for bad block */
+ printf("block %d is bad\n", block);
+ block++;
+ continue;
+ }
+ }
+ sz = 0;
+ /* read to buffer ... may not keep it */
+ bp = rei.recovery_data_buf + rei.recovery_data_len;
+ oob_ops.mode = MTD_OPS_PLACE_OOB;
+ oob_ops.len = mtd->writesize;
+ oob_ops.retlen = 0;
+ oob_ops.ooblen = mtd->oobsize;
+ oob_ops.oobretlen = 0;
+ oob_ops.ooboffs = 0;
+ oob_ops.datbuf = bp;
+ oob_ops.oobbuf = spare;
+ i = mtd_read_oob(mtd, off, &oob_ops);
+ sz = oob_ops.retlen;
+
+ n = 0;
+ j = 0;
+ if (i != 0) {
+ printf("%d/%d return %d sz %d\n", block, page, i, sz);
+ }
+ if (page == 0) {
+ //printf("%d/%d ", block, page, i, sz);
+ printf(".");
+ // printf("%d/%d r %d sz %d", block, page, i, sz);
+ }
+ while ((n < 2) && (j < sz)) {
+ up32 = bp + j;
+ n = n + 32 - generic_hweight32(*up32);
+ j = j + 4;
+ /* check if not blank ... otherwise read oob too */
+ }
+ if (page == 0) {
+ // printf(" zeros %d", n);
+ }
+ z = 0;
+ if (n < 2) {
+ /* check for zeros in oob */
+ for (j = 0; j < oob_ops.oobretlen >> 2; j++) {
+ z = z + 32 - generic_hweight32(spare[j]);
+ }
+ if (page == 0) {
+ // printf(" spare zeros %d\n", z);
+ }
+ } else {
+ if (page == 0) {
+ // printf("\n");
+ }
+ }
+ /* deal with this page */
+ // if (n + z > 2) {
+ if ((i == 0) && (n != 0)) {
+ // not blank
+ rei.recovery_chunks_list[chunk].flashpage =
+ block * pages + page;
+ rei.recovery_chunks_list[chunk].size = mtd->writesize;
+ rei.recovery_chunks_list[chunk].type = 0x00;
+ chunk++;
+ rei.recovery_data_len += mtd->writesize;
+ }
+ page = (page + 1) % pages;
+ if (page == 0) {
+ block++;
+ }
+ }
+ rei.recovery_chunks_list[chunk].type = 0x7fffffff;
+ printf("chunks %d len 0x%x\n", chunk, rei.recovery_data_len);
+ // load old blocks (everything but reimage image) to ddr
+ put_mtd_device(mtd);
+ return (ret);
+}
+
+ // erase everything except the reimage payload (after loader)
+ // burn loader
+ // attach image
+ // store preserved files
+ // store old blocks
+
+static int do_flashback(cmd_tbl_t * cmdtp, int flag, int argc,
+ char *const argv[]);
+static int do_flashback(cmd_tbl_t * cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct mtd_info *mtd = NULL;
+ long offset;
+ int i;
+ size_t sz = 0;
+ int blocks, pages;
+ int ret = 0;
+ char *bp;
+ int chunk;
+ if (rei.recovery_data_len == 0) {
+ printf("read recovery chunks first\n");
+ return (2);
+ }
+ mtd = get_mtd_device_nm("nand0");
+ blocks = (mtd->size / mtd->erasesize);
+ pages = (mtd->erasesize / mtd->writesize);
+ erase(mtd, 0, blocks);
+ chunk = 0;
+ bp = rei.recovery_data_buf;
+ while (rei.recovery_chunks_list[chunk].type < 0x1000) {
+ i = mtd_write(mtd,
+ rei.recovery_chunks_list[chunk].flashpage *
+ mtd->writesize, mtd->writesize, &sz, bp);
+ bp += rei.recovery_chunks_list[chunk].size;
+ chunk++;
+ printf("%d ", chunk);
+ }
+ put_mtd_device(mtd);
+}
+
+static int do_store_preserved(cmd_tbl_t * cmdtp, int flag, int argc,
+ char *const argv[]);
+static int do_store_preserved(cmd_tbl_t * cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ char cmd[256];
+ struct mtd_info *mtd = NULL;
+ mtd = get_mtd_device_nm("nand0");
+ if (IS_ERR_OR_NULL(mtd)) {
+ printf("failed to get mtd\n");
+ return (-1);
+ }
+ sprintf(cmd, "%s:%lld(loader),%lld@%lld(image)",
+ "brcmnand.0",
+ (long long)(rei.loader_blocks * (long long)mtd->erasesize),
+ (long long)(mtd->size -
+ (rei.loader_blocks + 8) * mtd->erasesize),
+ (long long)(rei.loader_blocks * mtd->erasesize));
+ run_command("mtdparts delall", 0);
+ env_set("mtdparts", cmd);
+ run_command("mtdparts", 0);
+ run_command("ubi part image", 0);
+ if (rei.preserved_data_max_len) {
+ sprintf(cmd, "ubi create transition 0x%x dynamic 33",
+ rei.preserved_data_max_len);
+ run_command(cmd, 0);
+ sprintf(cmd, "ubi write 0x%x transition 0x%x",
+ rei.preserved_data, rei.preserved_data_max_len);
+ run_command(cmd, 0);
+ }
+ if (rei.recovery_data_len) {
+ sprintf(cmd, "ubi create recovery 0x%x dynamic 34",
+ rei.recovery_data_len + (void *)rei.recovery_data_buf -
+ (void *)rei.recovery_chunks_list);
+ run_command(cmd, 0);
+ sprintf(cmd, "ubi write 0x%x recovery 0x%x",
+ rei.recovery_chunks_list,
+ rei.recovery_data_len + (void *)rei.recovery_data_buf -
+ (void *)rei.recovery_chunks_list);
+ run_command(cmd, 0);
+ }
+}
+
+void reimage_splice_env(struct reimager *r, char *more_env, int more_env_size)
+{
+ char *bp;
+ int i;
+ for (bp = r->loader_payload;
+ bp <
+ r->loader_payload + r->blocksizeK * 1024 * r->loader_blocks;
+ bp += 4096) {
+ int *tenv;
+ env_t *ep;
+ uint32_t new, crc;
+ tenv = (int *)bp;
+ printf("env check at %x\r", bp - r->loader_payload);
+ if (tenv[0] == BOOT_MAGIC_MAGIC) {
+ printf("\nGOT IT\n");
+ ep = (env_t *) & tenv[2];
+ memcpy(&crc, &ep->crc, sizeof(crc));
+ /* specifically use uboot's env crc function
+ * even though we have included the standard
+ * linux crc32 */
+ new = the_env_crc32(0, ep->data, tenv[1] - 4);
+ if (new != crc) {
+ printf("bad\n");
+ } else {
+ printf("good\n");
+
+ for (i = 0; i < tenv[1] - 4; i++) {
+ if ((ep->data[i] == '\0')
+ && (ep->data[i + 1] == '\0')) {
+ memcpy(&ep->data[i + 1],
+ more_env, more_env_size);
+ break;
+ }
+ }
+ new = the_env_crc32(0, ep->data, tenv[1] - 4);
+ memcpy(&ep->crc, &new, sizeof(new));
+ }
+ }
+ }
+}
+
+static char usage[] = "line 1...\n" "line 2...\n";
+
+U_BOOT_CMD_WITH_SUBCMDS(safeimage, "safe reimage commands", usage,
+ U_BOOT_SUBCMD_MKENT(commit, 5, 0, do_commit),
+ U_BOOT_SUBCMD_MKENT(flashback, 5, 0, do_flashback),
+ U_BOOT_SUBCMD_MKENT(read_recovery, 5, 0,
+ do_read_recovery),
+ U_BOOT_SUBCMD_MKENT(revertable, 5, 0, do_revertable),
+ U_BOOT_SUBCMD_MKENT(nvram, 1, 0, do_nvram_parse),
+ U_BOOT_SUBCMD_MKENT(store_preserved, 1, 0,
+ do_store_preserved));
+
+U_BOOT_CMD_WITH_SUBCMDS(reimage, "reimage commands", usage,
+ U_BOOT_SUBCMD_MKENT(auto, 1, 0, do_reimage_auto),
+ U_BOOT_SUBCMD_MKENT(finish, 5, 0, do_finish),
+ U_BOOT_SUBCMD_MKENT(prepare, 5, 0, do_prepare),
+ U_BOOT_SUBCMD_MKENT(nvram, 1, 0, do_nvram_parse));
+
+U_BOOT_CMD_WITH_SUBCMDS(preserve, "preserve data commands", usage,
+ U_BOOT_SUBCMD_MKENT(allocate, 5, 0,
+ do_preserve_allocate),
+ U_BOOT_SUBCMD_MKENT(save, 5, 0, do_preserve_save));