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