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