uboot/env/nand.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * (C) Copyright 2000-2010
   4 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
   5 *
   6 * (C) Copyright 2008
   7 * Stuart Wood, Lab X Technologies <stuart.wood@labxtechnologies.com>
   8 *
   9 * (C) Copyright 2004
  10 * Jian Zhang, Texas Instruments, jzhang@ti.com.
  11 *
  12 * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com>
  13 * Andreas Heppel <aheppel@sysgo.de>
  14 */
  15
  16#include <common.h>
  17#include <command.h>
  18#include <environment.h>
  19#include <linux/stddef.h>
  20#include <malloc.h>
  21#include <memalign.h>
  22#include <nand.h>
  23#include <search.h>
  24#include <errno.h>
  25
  26#if defined(CONFIG_CMD_SAVEENV) && defined(CONFIG_CMD_NAND) && \
  27                !defined(CONFIG_SPL_BUILD)
  28#define CMD_SAVEENV
  29#elif defined(CONFIG_ENV_OFFSET_REDUND)
  30#error CONFIG_ENV_OFFSET_REDUND must have CONFIG_CMD_SAVEENV & CONFIG_CMD_NAND
  31#endif
  32
  33#if defined(CONFIG_ENV_SIZE_REDUND) &&  \
  34        (CONFIG_ENV_SIZE_REDUND != CONFIG_ENV_SIZE)
  35#error CONFIG_ENV_SIZE_REDUND should be the same as CONFIG_ENV_SIZE
  36#endif
  37
  38#ifndef CONFIG_ENV_RANGE
  39#define CONFIG_ENV_RANGE        CONFIG_ENV_SIZE
  40#endif
  41
  42#if defined(ENV_IS_EMBEDDED)
  43env_t *env_ptr = &environment;
  44#elif defined(CONFIG_NAND_ENV_DST)
  45env_t *env_ptr = (env_t *)CONFIG_NAND_ENV_DST;
  46#else /* ! ENV_IS_EMBEDDED */
  47env_t *env_ptr;
  48#endif /* ENV_IS_EMBEDDED */
  49
  50DECLARE_GLOBAL_DATA_PTR;
  51
  52/*
  53 * This is called before nand_init() so we can't read NAND to
  54 * validate env data.
  55 *
  56 * Mark it OK for now. env_relocate() in env_common.c will call our
  57 * relocate function which does the real validation.
  58 *
  59 * When using a NAND boot image (like sequoia_nand), the environment
  60 * can be embedded or attached to the U-Boot image in NAND flash.
  61 * This way the SPL loads not only the U-Boot image from NAND but
  62 * also the environment.
  63 */
  64static int env_nand_init(void)
  65{
  66#if defined(ENV_IS_EMBEDDED) || defined(CONFIG_NAND_ENV_DST)
  67        int crc1_ok = 0, crc2_ok = 0;
  68        env_t *tmp_env1;
  69
  70#ifdef CONFIG_ENV_OFFSET_REDUND
  71        env_t *tmp_env2;
  72
  73        tmp_env2 = (env_t *)((ulong)env_ptr + CONFIG_ENV_SIZE);
  74        crc2_ok = crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc;
  75#endif
  76        tmp_env1 = env_ptr;
  77        crc1_ok = crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc;
  78
  79        if (!crc1_ok && !crc2_ok) {
  80                gd->env_addr    = 0;
  81                gd->env_valid   = ENV_INVALID;
  82
  83                return 0;
  84        } else if (crc1_ok && !crc2_ok) {
  85                gd->env_valid = ENV_VALID;
  86        }
  87#ifdef CONFIG_ENV_OFFSET_REDUND
  88        else if (!crc1_ok && crc2_ok) {
  89                gd->env_valid = ENV_REDUND;
  90        } else {
  91                /* both ok - check serial */
  92                if (tmp_env1->flags == 255 && tmp_env2->flags == 0)
  93                        gd->env_valid = ENV_REDUND;
  94                else if (tmp_env2->flags == 255 && tmp_env1->flags == 0)
  95                        gd->env_valid = ENV_VALID;
  96                else if (tmp_env1->flags > tmp_env2->flags)
  97                        gd->env_valid = ENV_VALID;
  98                else if (tmp_env2->flags > tmp_env1->flags)
  99                        gd->env_valid = ENV_REDUND;
 100                else /* flags are equal - almost impossible */
 101                        gd->env_valid = ENV_VALID;
 102        }
 103
 104        if (gd->env_valid == ENV_REDUND)
 105                env_ptr = tmp_env2;
 106        else
 107#endif
 108        if (gd->env_valid == ENV_VALID)
 109                env_ptr = tmp_env1;
 110
 111        gd->env_addr = (ulong)env_ptr->data;
 112
 113#else /* ENV_IS_EMBEDDED || CONFIG_NAND_ENV_DST */
 114        gd->env_addr    = (ulong)&default_environment[0];
 115        gd->env_valid   = ENV_VALID;
 116#endif /* ENV_IS_EMBEDDED || CONFIG_NAND_ENV_DST */
 117
 118        return 0;
 119}
 120
 121#ifdef CMD_SAVEENV
 122/*
 123 * The legacy NAND code saved the environment in the first NAND device i.e.,
 124 * nand_dev_desc + 0. This is also the behaviour using the new NAND code.
 125 */
 126static int writeenv(size_t offset, u_char *buf)
 127{
 128        size_t end = offset + CONFIG_ENV_RANGE;
 129        size_t amount_saved = 0;
 130        size_t blocksize, len;
 131        struct mtd_info *mtd;
 132        u_char *char_ptr;
 133
 134        mtd = get_nand_dev_by_index(0);
 135        if (!mtd)
 136                return 1;
 137
 138        blocksize = mtd->erasesize;
 139        len = min(blocksize, (size_t)CONFIG_ENV_SIZE);
 140
 141        while (amount_saved < CONFIG_ENV_SIZE && offset < end) {
 142                if (nand_block_isbad(mtd, offset)) {
 143                        offset += blocksize;
 144                } else {
 145                        char_ptr = &buf[amount_saved];
 146                        if (nand_write(mtd, offset, &len, char_ptr))
 147                                return 1;
 148
 149                        offset += blocksize;
 150                        amount_saved += len;
 151                }
 152        }
 153        if (amount_saved != CONFIG_ENV_SIZE)
 154                return 1;
 155
 156        return 0;
 157}
 158
 159struct nand_env_location {
 160        const char *name;
 161        const nand_erase_options_t erase_opts;
 162};
 163
 164static int erase_and_write_env(const struct nand_env_location *location,
 165                u_char *env_new)
 166{
 167        struct mtd_info *mtd;
 168        int ret = 0;
 169
 170        mtd = get_nand_dev_by_index(0);
 171        if (!mtd)
 172                return 1;
 173
 174        printf("Erasing %s...\n", location->name);
 175        if (nand_erase_opts(mtd, &location->erase_opts))
 176                return 1;
 177
 178        printf("Writing to %s... ", location->name);
 179        ret = writeenv(location->erase_opts.offset, env_new);
 180        puts(ret ? "FAILED!\n" : "OK\n");
 181
 182        return ret;
 183}
 184
 185static int env_nand_save(void)
 186{
 187        int     ret = 0;
 188        ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1);
 189        int     env_idx = 0;
 190        static const struct nand_env_location location[] = {
 191                {
 192                        .name = "NAND",
 193                        .erase_opts = {
 194                                .length = CONFIG_ENV_RANGE,
 195                                .offset = CONFIG_ENV_OFFSET,
 196                        },
 197                },
 198#ifdef CONFIG_ENV_OFFSET_REDUND
 199                {
 200                        .name = "redundant NAND",
 201                        .erase_opts = {
 202                                .length = CONFIG_ENV_RANGE,
 203                                .offset = CONFIG_ENV_OFFSET_REDUND,
 204                        },
 205                },
 206#endif
 207        };
 208
 209
 210        if (CONFIG_ENV_RANGE < CONFIG_ENV_SIZE)
 211                return 1;
 212
 213        ret = env_export(env_new);
 214        if (ret)
 215                return ret;
 216
 217#ifdef CONFIG_ENV_OFFSET_REDUND
 218        env_idx = (gd->env_valid == ENV_VALID);
 219#endif
 220
 221        ret = erase_and_write_env(&location[env_idx], (u_char *)env_new);
 222#ifdef CONFIG_ENV_OFFSET_REDUND
 223        if (!ret) {
 224                /* preset other copy for next write */
 225                gd->env_valid = gd->env_valid == ENV_REDUND ? ENV_VALID :
 226                                ENV_REDUND;
 227                return ret;
 228        }
 229
 230        env_idx = (env_idx + 1) & 1;
 231        ret = erase_and_write_env(&location[env_idx], (u_char *)env_new);
 232        if (!ret)
 233                printf("Warning: primary env write failed,"
 234                                " redundancy is lost!\n");
 235#endif
 236
 237        return ret;
 238}
 239#endif /* CMD_SAVEENV */
 240
 241#if defined(CONFIG_SPL_BUILD)
 242static int readenv(size_t offset, u_char *buf)
 243{
 244        return nand_spl_load_image(offset, CONFIG_ENV_SIZE, buf);
 245}
 246#else
 247static int readenv(size_t offset, u_char *buf)
 248{
 249        size_t end = offset + CONFIG_ENV_RANGE;
 250        size_t amount_loaded = 0;
 251        size_t blocksize, len;
 252        struct mtd_info *mtd;
 253        u_char *char_ptr;
 254
 255        mtd = get_nand_dev_by_index(0);
 256        if (!mtd)
 257                return 1;
 258
 259        blocksize = mtd->erasesize;
 260        len = min(blocksize, (size_t)CONFIG_ENV_SIZE);
 261
 262        while (amount_loaded < CONFIG_ENV_SIZE && offset < end) {
 263                if (nand_block_isbad(mtd, offset)) {
 264                        offset += blocksize;
 265                } else {
 266                        char_ptr = &buf[amount_loaded];
 267                        if (nand_read_skip_bad(mtd, offset,
 268                                               &len, NULL,
 269                                               mtd->size, char_ptr))
 270                                return 1;
 271
 272                        offset += blocksize;
 273                        amount_loaded += len;
 274                }
 275        }
 276
 277        if (amount_loaded != CONFIG_ENV_SIZE)
 278                return 1;
 279
 280        return 0;
 281}
 282#endif /* #if defined(CONFIG_SPL_BUILD) */
 283
 284#ifdef CONFIG_ENV_OFFSET_OOB
 285int get_nand_env_oob(struct mtd_info *mtd, unsigned long *result)
 286{
 287        struct mtd_oob_ops ops;
 288        uint32_t oob_buf[ENV_OFFSET_SIZE / sizeof(uint32_t)];
 289        int ret;
 290
 291        ops.datbuf      = NULL;
 292        ops.mode        = MTD_OOB_AUTO;
 293        ops.ooboffs     = 0;
 294        ops.ooblen      = ENV_OFFSET_SIZE;
 295        ops.oobbuf      = (void *)oob_buf;
 296
 297        ret = mtd->read_oob(mtd, ENV_OFFSET_SIZE, &ops);
 298        if (ret) {
 299                printf("error reading OOB block 0\n");
 300                return ret;
 301        }
 302
 303        if (oob_buf[0] == ENV_OOB_MARKER) {
 304                *result = ovoid ob_buf[1] * mtd->erasesize;
 305        } else if (oob_buf[0] == ENV_OOB_MARKER_OLD) {
 306                *result = oob_buf[1];
 307        } else {
 308                printf("No dynamic environment marker in OOB block 0\n");
 309                return -ENOENT;
 310        }
 311
 312        return 0;
 313}
 314#endif
 315
 316#ifdef CONFIG_ENV_OFFSET_REDUND
 317static int env_nand_load(void)
 318{
 319#if defined(ENV_IS_EMBEDDED)
 320        return 0;
 321#else
 322        int read1_fail, read2_fail;
 323        env_t *tmp_env1, *tmp_env2;
 324        int ret = 0;
 325
 326        tmp_env1 = (env_t *)malloc(CONFIG_ENV_SIZE);
 327        tmp_env2 = (env_t *)malloc(CONFIG_ENV_SIZE);
 328        if (tmp_env1 == NULL || tmp_env2 == NULL) {
 329                puts("Can't allocate buffers for environment\n");
 330                set_default_env("!malloc() failed");
 331                ret = -EIO;
 332                goto done;
 333        }
 334
 335        read1_fail = readenv(CONFIG_ENV_OFFSET, (u_char *) tmp_env1);
 336        read2_fail = readenv(CONFIG_ENV_OFFSET_REDUND, (u_char *) tmp_env2);
 337
 338        ret = env_import_redund((char *)tmp_env1, read1_fail, (char *)tmp_env2,
 339                                read2_fail);
 340
 341done:
 342        free(tmp_env1);
 343        free(tmp_env2);
 344
 345        return ret;
 346#endif /* ! ENV_IS_EMBEDDED */
 347}
 348#else /* ! CONFIG_ENV_OFFSET_REDUND */
 349/*
 350 * The legacy NAND code saved the environment in the first NAND
 351 * device i.e., nand_dev_desc + 0. This is also the behaviour using
 352 * the new NAND code.
 353 */
 354static int env_nand_load(void)
 355{
 356#if !defined(ENV_IS_EMBEDDED)
 357        int ret;
 358        ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE);
 359
 360#if defined(CONFIG_ENV_OFFSET_OOB)
 361        struct mtd_info *mtd  = get_nand_dev_by_index(0);
 362        /*
 363         * If unable to read environment offset from NAND OOB then fall through
 364         * to the normal environment reading code below
 365         */
 366        if (mtd && !get_nand_env_oob(mtd, &nand_env_oob_offset)) {
 367                printf("Found Environment offset in OOB..\n");
 368        } else {
 369                set_default_env("!no env offset in OOB");
 370                return;
 371        }
 372#endif
 373
 374        ret = readenv(CONFIG_ENV_OFFSET, (u_char *)buf);
 375        if (ret) {
 376                set_default_env("!readenv() failed");
 377                return -EIO;
 378        }
 379
 380        return env_import(buf, 1);
 381#endif /* ! ENV_IS_EMBEDDED */
 382
 383        return 0;
 384}
 385#endif /* CONFIG_ENV_OFFSET_REDUND */
 386
 387U_BOOT_ENV_LOCATION(nand) = {
 388        .location       = ENVL_NAND,
 389        ENV_NAME("NAND")
 390        .load           = env_nand_load,
 391#if defined(CMD_SAVEENV)
 392        .save           = env_save_ptr(env_nand_save),
 393#endif
 394        .init           = env_nand_init,
 395};
 396