1 /* 2 * (C) Copyright 2000-2010 3 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. 4 * 5 * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com> 6 * Andreas Heppel <aheppel@sysgo.de> 7 8 * SPDX-License-Identifier: GPL-2.0+ 9 */ 10 11 /* #define DEBUG */ 12 13 #include <common.h> 14 #include <command.h> 15 #include <environment.h> 16 #include <linux/stddef.h> 17 #include <malloc.h> 18 #include <search.h> 19 #include <errno.h> 20 21 DECLARE_GLOBAL_DATA_PTR; 22 23 #ifndef CONFIG_SPL_BUILD 24 # if defined(CONFIG_CMD_SAVEENV) && defined(CONFIG_CMD_FLASH) 25 # define CMD_SAVEENV 26 # elif defined(CONFIG_ENV_ADDR_REDUND) 27 # error CONFIG_ENV_ADDR_REDUND must have CONFIG_CMD_SAVEENV & CONFIG_CMD_FLASH 28 # endif 29 #endif 30 31 #if defined(CONFIG_ENV_SIZE_REDUND) && \ 32 (CONFIG_ENV_SIZE_REDUND < CONFIG_ENV_SIZE) 33 #error CONFIG_ENV_SIZE_REDUND should not be less then CONFIG_ENV_SIZE 34 #endif 35 36 /* TODO(sjg@chromium.org): Figure out all these special cases */ 37 #if (!defined(CONFIG_MICROBLAZE) && !defined(CONFIG_ARCH_ZYNQ) && \ 38 !defined(CONFIG_TARGET_MCCMON6) && !defined(CONFIG_TARGET_X600) && \ 39 !defined(CONFIG_TARGET_EDMINIV2)) || \ 40 !defined(CONFIG_SPL_BUILD) 41 #define LOADENV 42 #endif 43 44 #if !defined(CONFIG_TARGET_X600) || !defined(CONFIG_SPL_BUILD) 45 #define INITENV 46 #endif 47 48 #ifdef ENV_IS_EMBEDDED 49 env_t *env_ptr = &environment; 50 51 static __maybe_unused env_t *flash_addr = (env_t *)CONFIG_ENV_ADDR; 52 53 #else /* ! ENV_IS_EMBEDDED */ 54 55 env_t *env_ptr = (env_t *)CONFIG_ENV_ADDR; 56 static __maybe_unused env_t *flash_addr = (env_t *)CONFIG_ENV_ADDR; 57 #endif /* ENV_IS_EMBEDDED */ 58 59 /* CONFIG_ENV_ADDR is supposed to be on sector boundary */ 60 static ulong __maybe_unused end_addr = 61 CONFIG_ENV_ADDR + CONFIG_ENV_SECT_SIZE - 1; 62 63 #ifdef CONFIG_ENV_ADDR_REDUND 64 65 static env_t __maybe_unused *flash_addr_new = (env_t *)CONFIG_ENV_ADDR_REDUND; 66 67 /* CONFIG_ENV_ADDR_REDUND is supposed to be on sector boundary */ 68 static ulong __maybe_unused end_addr_new = 69 CONFIG_ENV_ADDR_REDUND + CONFIG_ENV_SECT_SIZE - 1; 70 #endif /* CONFIG_ENV_ADDR_REDUND */ 71 72 #ifdef CONFIG_ENV_ADDR_REDUND 73 #ifdef INITENV 74 static int env_flash_init(void) 75 { 76 int crc1_ok = 0, crc2_ok = 0; 77 78 uchar flag1 = flash_addr->flags; 79 uchar flag2 = flash_addr_new->flags; 80 81 ulong addr_default = (ulong)&default_environment[0]; 82 ulong addr1 = (ulong)&(flash_addr->data); 83 ulong addr2 = (ulong)&(flash_addr_new->data); 84 85 crc1_ok = crc32(0, flash_addr->data, ENV_SIZE) == flash_addr->crc; 86 crc2_ok = 87 crc32(0, flash_addr_new->data, ENV_SIZE) == flash_addr_new->crc; 88 89 if (crc1_ok && !crc2_ok) { 90 gd->env_addr = addr1; 91 gd->env_valid = ENV_VALID; 92 } else if (!crc1_ok && crc2_ok) { 93 gd->env_addr = addr2; 94 gd->env_valid = ENV_VALID; 95 } else if (!crc1_ok && !crc2_ok) { 96 gd->env_addr = addr_default; 97 gd->env_valid = ENV_INVALID; 98 } else if (flag1 == ACTIVE_FLAG && flag2 == OBSOLETE_FLAG) { 99 gd->env_addr = addr1; 100 gd->env_valid = ENV_VALID; 101 } else if (flag1 == OBSOLETE_FLAG && flag2 == ACTIVE_FLAG) { 102 gd->env_addr = addr2; 103 gd->env_valid = ENV_VALID; 104 } else if (flag1 == flag2) { 105 gd->env_addr = addr1; 106 gd->env_valid = ENV_REDUND; 107 } else if (flag1 == 0xFF) { 108 gd->env_addr = addr1; 109 gd->env_valid = ENV_REDUND; 110 } else if (flag2 == 0xFF) { 111 gd->env_addr = addr2; 112 gd->env_valid = ENV_REDUND; 113 } 114 115 return 0; 116 } 117 #endif 118 119 #ifdef CMD_SAVEENV 120 static int env_flash_save(void) 121 { 122 env_t env_new; 123 char *saved_data = NULL; 124 char flag = OBSOLETE_FLAG, new_flag = ACTIVE_FLAG; 125 int rc = 1; 126 #if CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE 127 ulong up_data = 0; 128 #endif 129 130 debug("Protect off %08lX ... %08lX\n", (ulong)flash_addr, end_addr); 131 132 if (flash_sect_protect(0, (ulong)flash_addr, end_addr)) 133 goto done; 134 135 debug("Protect off %08lX ... %08lX\n", 136 (ulong)flash_addr_new, end_addr_new); 137 138 if (flash_sect_protect(0, (ulong)flash_addr_new, end_addr_new)) 139 goto done; 140 141 rc = env_export(&env_new); 142 if (rc) 143 return rc; 144 env_new.flags = new_flag; 145 146 #if CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE 147 up_data = end_addr_new + 1 - ((long)flash_addr_new + CONFIG_ENV_SIZE); 148 debug("Data to save 0x%lX\n", up_data); 149 if (up_data) { 150 saved_data = malloc(up_data); 151 if (saved_data == NULL) { 152 printf("Unable to save the rest of sector (%ld)\n", 153 up_data); 154 goto done; 155 } 156 memcpy(saved_data, 157 (void *)((long)flash_addr_new + CONFIG_ENV_SIZE), 158 up_data); 159 debug("Data (start 0x%lX, len 0x%lX) saved at 0x%p\n", 160 (long)flash_addr_new + CONFIG_ENV_SIZE, 161 up_data, saved_data); 162 } 163 #endif 164 puts("Erasing Flash..."); 165 debug(" %08lX ... %08lX ...", (ulong)flash_addr_new, end_addr_new); 166 167 if (flash_sect_erase((ulong)flash_addr_new, end_addr_new)) 168 goto done; 169 170 puts("Writing to Flash... "); 171 debug(" %08lX ... %08lX ...", 172 (ulong)&(flash_addr_new->data), 173 sizeof(env_ptr->data) + (ulong)&(flash_addr_new->data)); 174 rc = flash_write((char *)&env_new, (ulong)flash_addr_new, 175 sizeof(env_new)); 176 if (rc) 177 goto perror; 178 179 rc = flash_write(&flag, (ulong)&(flash_addr->flags), 180 sizeof(flash_addr->flags)); 181 if (rc) 182 goto perror; 183 184 #if CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE 185 if (up_data) { /* restore the rest of sector */ 186 debug("Restoring the rest of data to 0x%lX len 0x%lX\n", 187 (long)flash_addr_new + CONFIG_ENV_SIZE, up_data); 188 if (flash_write(saved_data, 189 (long)flash_addr_new + CONFIG_ENV_SIZE, 190 up_data)) 191 goto perror; 192 } 193 #endif 194 puts("done\n"); 195 196 { 197 env_t *etmp = flash_addr; 198 ulong ltmp = end_addr; 199 200 flash_addr = flash_addr_new; 201 flash_addr_new = etmp; 202 203 end_addr = end_addr_new; 204 end_addr_new = ltmp; 205 } 206 207 rc = 0; 208 goto done; 209 perror: 210 flash_perror(rc); 211 done: 212 if (saved_data) 213 free(saved_data); 214 /* try to re-protect */ 215 flash_sect_protect(1, (ulong)flash_addr, end_addr); 216 flash_sect_protect(1, (ulong)flash_addr_new, end_addr_new); 217 218 return rc; 219 } 220 #endif /* CMD_SAVEENV */ 221 222 #else /* ! CONFIG_ENV_ADDR_REDUND */ 223 224 #ifdef INITENV 225 static int env_flash_init(void) 226 { 227 if (crc32(0, env_ptr->data, ENV_SIZE) == env_ptr->crc) { 228 gd->env_addr = (ulong)&(env_ptr->data); 229 gd->env_valid = ENV_VALID; 230 return 0; 231 } 232 233 gd->env_addr = (ulong)&default_environment[0]; 234 gd->env_valid = ENV_INVALID; 235 return 0; 236 } 237 #endif 238 239 #ifdef CMD_SAVEENV 240 static int env_flash_save(void) 241 { 242 env_t env_new; 243 int rc = 1; 244 char *saved_data = NULL; 245 #if CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE 246 ulong up_data = 0; 247 248 up_data = end_addr + 1 - ((long)flash_addr + CONFIG_ENV_SIZE); 249 debug("Data to save 0x%lx\n", up_data); 250 if (up_data) { 251 saved_data = malloc(up_data); 252 if (saved_data == NULL) { 253 printf("Unable to save the rest of sector (%ld)\n", 254 up_data); 255 goto done; 256 } 257 memcpy(saved_data, 258 (void *)((long)flash_addr + CONFIG_ENV_SIZE), up_data); 259 debug("Data (start 0x%lx, len 0x%lx) saved at 0x%lx\n", 260 (ulong)flash_addr + CONFIG_ENV_SIZE, 261 up_data, 262 (ulong)saved_data); 263 } 264 #endif /* CONFIG_ENV_SECT_SIZE */ 265 266 debug("Protect off %08lX ... %08lX\n", (ulong)flash_addr, end_addr); 267 268 if (flash_sect_protect(0, (long)flash_addr, end_addr)) 269 goto done; 270 271 rc = env_export(&env_new); 272 if (rc) 273 goto done; 274 275 puts("Erasing Flash..."); 276 if (flash_sect_erase((long)flash_addr, end_addr)) 277 goto done; 278 279 puts("Writing to Flash... "); 280 rc = flash_write((char *)&env_new, (long)flash_addr, CONFIG_ENV_SIZE); 281 if (rc != 0) 282 goto perror; 283 284 #if CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE 285 if (up_data) { /* restore the rest of sector */ 286 debug("Restoring the rest of data to 0x%lx len 0x%lx\n", 287 (ulong)flash_addr + CONFIG_ENV_SIZE, up_data); 288 if (flash_write(saved_data, 289 (long)flash_addr + CONFIG_ENV_SIZE, 290 up_data)) 291 goto perror; 292 } 293 #endif 294 puts("done\n"); 295 rc = 0; 296 goto done; 297 perror: 298 flash_perror(rc); 299 done: 300 if (saved_data) 301 free(saved_data); 302 /* try to re-protect */ 303 flash_sect_protect(1, (long)flash_addr, end_addr); 304 return rc; 305 } 306 #endif /* CMD_SAVEENV */ 307 308 #endif /* CONFIG_ENV_ADDR_REDUND */ 309 310 #ifdef LOADENV 311 static int env_flash_load(void) 312 { 313 #ifdef CONFIG_ENV_ADDR_REDUND 314 if (gd->env_addr != (ulong)&(flash_addr->data)) { 315 env_t *etmp = flash_addr; 316 ulong ltmp = end_addr; 317 318 flash_addr = flash_addr_new; 319 flash_addr_new = etmp; 320 321 end_addr = end_addr_new; 322 end_addr_new = ltmp; 323 } 324 325 if (flash_addr_new->flags != OBSOLETE_FLAG && 326 crc32(0, flash_addr_new->data, ENV_SIZE) == flash_addr_new->crc) { 327 char flag = OBSOLETE_FLAG; 328 329 gd->env_valid = ENV_REDUND; 330 flash_sect_protect(0, (ulong)flash_addr_new, end_addr_new); 331 flash_write(&flag, 332 (ulong)&(flash_addr_new->flags), 333 sizeof(flash_addr_new->flags)); 334 flash_sect_protect(1, (ulong)flash_addr_new, end_addr_new); 335 } 336 337 if (flash_addr->flags != ACTIVE_FLAG && 338 (flash_addr->flags & ACTIVE_FLAG) == ACTIVE_FLAG) { 339 char flag = ACTIVE_FLAG; 340 341 gd->env_valid = ENV_REDUND; 342 flash_sect_protect(0, (ulong)flash_addr, end_addr); 343 flash_write(&flag, 344 (ulong)&(flash_addr->flags), 345 sizeof(flash_addr->flags)); 346 flash_sect_protect(1, (ulong)flash_addr, end_addr); 347 } 348 349 if (gd->env_valid == ENV_REDUND) 350 puts("*** Warning - some problems detected " 351 "reading environment; recovered successfully\n\n"); 352 #endif /* CONFIG_ENV_ADDR_REDUND */ 353 354 return env_import((char *)flash_addr, 1); 355 } 356 #endif /* LOADENV */ 357 358 U_BOOT_ENV_LOCATION(flash) = { 359 .location = ENVL_FLASH, 360 ENV_NAME("Flash") 361 #ifdef LOADENV 362 .load = env_flash_load, 363 #endif 364 #ifdef CMD_SAVEENV 365 .save = env_save_ptr(env_flash_save), 366 #endif 367 #ifdef INITENV 368 .init = env_flash_init, 369 #endif 370 }; 371