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