1 /* 2 * Copyright (c) 2022 Rockchip Electronics Co., Ltd 3 * 4 * SPDX-License-Identifier: GPL-2.0+ 5 */ 6 7 #include <common.h> 8 #include <boot_rkimg.h> 9 #include <environment.h> 10 #include <memalign.h> 11 #include <part.h> 12 13 DECLARE_GLOBAL_DATA_PTR; 14 15 #define ENVF_MSG(fmt, args...) printf("ENVF: "fmt, ##args) 16 #define BLK_CNT(desc, sz) ((sz) / (desc)->blksz) 17 #define ENVPARAM_SIZE SZ_4K 18 19 static ulong env_offset, env_offset_redund; 20 static ulong env_size; 21 static u32 envparam_addr_common[] = { 0, SZ_4K }; 22 static u32 envparam_addr_spinand[] = { 0, SZ_256K }; 23 24 #ifdef CONFIG_SPL_BUILD 25 #ifdef CONFIG_SPL_ENV_PARTITION 26 /* 27 * In case of env and env-backup partitions are too large that exceeds the limit 28 * of CONFIG_SPL_SYS_MALLOC_F_LEN. we prefer to use a static address as an env 29 * buffer. The tail of bss section is good choice from experience. 30 */ 31 static void *env_buffer = 32 (void *)CONFIG_SPL_BSS_START_ADDR + CONFIG_SPL_BSS_MAX_SIZE; 33 34 static const char *get_strval(env_t *env, u32 size, const char *str) 35 { 36 const char *dp; 37 u32 env_size; 38 39 dp = (const char *)env->data; 40 env_size = size - ENV_HEADER_SIZE; 41 do { 42 /* skip leading white space */ 43 while (*dp == ' ' || *dp == '\t') 44 ++dp; 45 46 debug("ENTRY: %s\n", dp); 47 if (strstr(dp, str)) { 48 debug("FIND: %s\n", dp); 49 return dp; 50 } 51 52 /* point to next ENTRY */ 53 dp += strlen(dp) + 1; 54 } while (((ulong)dp < (ulong)env->data + env_size) && *dp); 55 56 debug("NOT-FIND: %s\n", str); 57 58 return NULL; 59 } 60 61 static ulong get_strval_ulong(env_t *env, u32 size, 62 const char *str, ulong default_val) 63 { 64 const char *p; 65 66 p = get_strval(env, size, str); 67 if (!p) 68 return default_val; 69 p = strstr(p, "="); 70 if (!p) 71 return default_val; 72 73 ++p; /* skip '=' */ 74 75 return simple_strtoul(p, NULL, 16); 76 } 77 78 static int env_do_load(struct blk_desc *desc, u32 offset, u32 size, 79 void *buf, void **envp) 80 { 81 lbaint_t env_size; 82 lbaint_t env_off; 83 env_t *env = buf; 84 u32 blk_cnt; 85 86 env_size = size - ENV_HEADER_SIZE; 87 env_off = BLK_CNT(desc, offset); 88 blk_cnt = BLK_CNT(desc, size); 89 90 if (blk_dread(desc, env_off, blk_cnt, (void *)env) != blk_cnt) { 91 ENVF_MSG("io error @ 0x%x\n", offset); 92 return -EIO; 93 } 94 95 if (crc32(0, env->data, env_size) != env->crc) { 96 ENVF_MSG("!bad CRC @ 0x%x\n", offset); 97 return -EINVAL; 98 } 99 100 if (envp) 101 *envp = env; 102 103 return 0; 104 } 105 106 int envf_load(struct blk_desc *desc) 107 { 108 void *env, *envp0, *envp1; 109 const char *part_list; 110 int read0_fail, read1_fail; 111 int envp_blks; 112 int ret = -ENOENT; 113 u32 *addr; 114 115 if (!desc) 116 return -ENODEV; 117 118 /* Import envparam */ 119 if (desc->if_type == IF_TYPE_MTD && desc->devnum == BLK_MTD_SPI_NAND) 120 addr = envparam_addr_spinand; 121 else 122 addr = envparam_addr_common; 123 124 envp_blks = BLK_CNT(desc, ENVPARAM_SIZE); 125 read0_fail = env_do_load(desc, addr[0], ENVPARAM_SIZE, 126 env_buffer, &envp0); 127 read1_fail = env_do_load(desc, addr[1], ENVPARAM_SIZE, 128 env_buffer + ENVPARAM_SIZE, &envp1); 129 if (read0_fail && read1_fail) { 130 ENVF_MSG("No valid envparam!\n"); 131 } else if (!read0_fail && read1_fail) { 132 env = envp0; 133 ret = blk_dwrite(desc, BLK_CNT(desc, addr[1]), 134 BLK_CNT(desc, ENVPARAM_SIZE), envp0); 135 ENVF_MSG("Repair backup envparam %s\n", 136 ret == envp_blks ? "ok" : "fail"); 137 } else if (read0_fail && !read1_fail) { 138 env = envp1; 139 ret = blk_dwrite(desc, BLK_CNT(desc, addr[0]), 140 BLK_CNT(desc, ENVPARAM_SIZE), envp1); 141 ENVF_MSG("Repair primary envparam %s\n", 142 ret == envp_blks ? "ok" : "fail"); 143 } else { 144 env = envp0; /* both ok, use primary envparam */ 145 } 146 147 /* Import env data */ 148 env_size = get_strval_ulong(env, ENVPARAM_SIZE, "ENV_SIZE", 0); 149 env_offset = get_strval_ulong(env, ENVPARAM_SIZE, "ENV_OFFSET", 0); 150 env_offset_redund = get_strval_ulong(env, ENVPARAM_SIZE, "ENV_OFFSET_REDUND", 0); 151 if (!env_offset || !env_size) { 152 ENVF_MSG("Using default\n"); 153 env_offset = CONFIG_ENV_OFFSET; 154 env_size = CONFIG_ENV_SIZE; 155 env_offset_redund = CONFIG_ENV_OFFSET_REDUND; 156 } 157 if (env_offset == env_offset_redund) 158 env_offset_redund = 0; 159 160 ENVF_MSG("Primary 0x%08lx - 0x%08lx\n", env_offset, env_offset + env_size); 161 if (env_offset_redund) 162 ENVF_MSG("Backup 0x%08lx - 0x%08lx\n", 163 env_offset_redund, env_offset_redund + env_size); 164 165 ret = env_do_load(desc, env_offset, env_size, env_buffer, &env); 166 if (ret < 0 && env_offset_redund) 167 ret = env_do_load(desc, env_offset_redund, env_size, 168 env_buffer + env_size, &env); 169 if (ret < 0) { 170 ENVF_MSG("No valid env data, ret=%d\n", ret); 171 return ret; 172 } 173 174 part_list = get_strval(env, env_size, "mtdparts"); 175 if (!part_list) 176 part_list = get_strval(env, env_size, "blkdevparts"); 177 if (!part_list) { 178 ENVF_MSG("No valid env part list\n"); 179 return ret; 180 } 181 182 ENVF_MSG("OK\n"); 183 env_part_list_init(part_list); 184 /* 185 * Call blk_dread() to read env content: 186 * => mmc_init() => part_init() with CONFIG_ENV_PARTITION_LIST => mmc init ok 187 * => read env content! including partition tables(blkdevparts/mtdparts). 188 * 189 * So we must reinit env partition after we setup the partition 190 * tables(blkdevparts/mtdparts) from env. 191 */ 192 part_init(desc); 193 194 return 0; 195 } 196 #endif 197 198 #else 199 /* 200 * [Example] 201 * generate envparam: 202 * ./tools/mkenvimage -s 0x1000 -p 0x0 -o envparam.bin envparam.txt 203 * generate data: 204 * ./tools/mkenvimage -s 0x8000 -p 0x0 -o envf.bin envf.txt 205 */ 206 #define ENVF_MAX 64 207 #define ENVF_EMSG "error: please use \"sys_bootargs\" but not " \ 208 "\"bootargs\" in CONFIG_ENVF_LIST and envf.bin" 209 static const char *envf_list[ENVF_MAX]; 210 static u32 envf_num; 211 212 static int envf_extract_list(void) 213 { 214 char *tok, *p; 215 u32 i = 0; 216 217 tok = strdup(CONFIG_ENVF_LIST); 218 if (!tok) 219 return -ENOMEM; 220 221 p = strtok(tok, " "); 222 while (p && i < ENVF_MAX) { 223 if (!strcmp(p, "bootargs")) { 224 printf("%s\n", ENVF_EMSG); 225 run_command("download", 0); 226 } 227 envf_list[i++] = p; 228 p = strtok(NULL, " "); 229 } 230 231 envf_num = i; 232 233 return 0; 234 } 235 236 static int env_do_load(struct blk_desc *desc, u32 offset, u32 size, 237 const char *list[], int num, void **envp) 238 { 239 lbaint_t env_size; 240 lbaint_t env_off; 241 u32 blk_cnt; 242 int ret = 0; 243 env_t *env; 244 245 env_size = size - ENV_HEADER_SIZE; 246 env_off = BLK_CNT(desc, offset); 247 blk_cnt = BLK_CNT(desc, size); 248 249 env = memalign(ARCH_DMA_MINALIGN, size); 250 if (!env) 251 return -ENOMEM; 252 253 if (blk_dread(desc, env_off, blk_cnt, (void *)env) != blk_cnt) { 254 ret = -EIO; 255 goto out; 256 } 257 258 if (crc32(0, env->data, env_size) != env->crc) { 259 ENVF_MSG("!bad CRC @ 0x%x\n", offset); 260 ret = -EINVAL; 261 goto out; 262 } 263 264 if (!himport_r(&env_htab, (char *)env->data, env_size, '\0', 265 H_NOCLEAR, 0, num, (char * const *)list)) { 266 ENVF_MSG("himport error: %d\n", errno); 267 ret = -EINTR; 268 } 269 270 if (envp) 271 *envp = env; 272 out: 273 return ret; 274 } 275 276 static int envf_load(void) 277 { 278 const char *envparam[] = { 279 "ENV_OFFSET", "ENV_SIZE", "ENV_OFFSET_REDUND", 280 }; 281 struct blk_desc *desc; 282 int read0_fail, read1_fail; 283 int envp_blks; 284 void *envp0 = NULL; 285 void *envp1 = NULL; 286 int ret = -ENOENT; 287 u32 *addr; 288 289 desc = rockchip_get_bootdev(); 290 if (!desc) { 291 printf("dev desc null!\n"); 292 return 0; 293 } 294 295 /* Import envparam */ 296 if (desc->if_type == IF_TYPE_MTD && desc->devnum == BLK_MTD_SPI_NAND) 297 addr = envparam_addr_spinand; 298 else 299 addr = envparam_addr_common; 300 301 envp_blks = BLK_CNT(desc, ENVPARAM_SIZE); 302 read0_fail = env_do_load(desc, addr[0], ENVPARAM_SIZE, 303 envparam, ARRAY_SIZE(envparam), &envp0); 304 read1_fail = env_do_load(desc, addr[1], ENVPARAM_SIZE, 305 envparam, ARRAY_SIZE(envparam), &envp1); 306 if (read0_fail && read1_fail) { 307 ENVF_MSG("No valid envparam!\n"); 308 } else if (!read0_fail && read1_fail) { 309 ret = blk_dwrite(desc, BLK_CNT(desc, addr[1]), 310 BLK_CNT(desc, ENVPARAM_SIZE), envp0); 311 ENVF_MSG("Repair backup envparam %s\n", 312 ret == envp_blks ? "ok" : "fail"); 313 } else if (read0_fail && !read1_fail) { 314 ret = blk_dwrite(desc, BLK_CNT(desc, addr[0]), 315 BLK_CNT(desc, ENVPARAM_SIZE), envp1); 316 ENVF_MSG("Repair primary envparam %s\n", 317 ret == envp_blks ? "ok" : "fail"); 318 } 319 320 if (envp0) 321 free(envp0); 322 if (envp1) 323 free(envp1); 324 325 /* Import env data */ 326 env_size = env_get_ulong("ENV_SIZE", 16, 0); 327 env_offset = env_get_ulong("ENV_OFFSET", 16, 0); 328 env_offset_redund = env_get_ulong("ENV_OFFSET_REDUND", 16, 0); 329 if (!env_offset || !env_size) { 330 ENVF_MSG("Using default\n"); 331 env_offset = CONFIG_ENV_OFFSET; 332 env_size = CONFIG_ENV_SIZE; 333 env_offset_redund = CONFIG_ENV_OFFSET_REDUND; 334 } 335 if (env_offset == env_offset_redund) 336 env_offset_redund = 0; 337 338 ENVF_MSG("Primary 0x%08lx - 0x%08lx\n", env_offset, env_offset + env_size); 339 if (env_offset_redund) 340 ENVF_MSG("Backup 0x%08lx - 0x%08lx\n", 341 env_offset_redund, env_offset_redund + env_size); 342 343 envf_extract_list(); 344 ret = env_do_load(desc, env_offset, env_size, envf_list, envf_num, NULL); 345 if (ret < 0 && env_offset_redund) { 346 ret = env_do_load(desc, env_offset_redund, 347 env_size, envf_list, envf_num, NULL); 348 } 349 if (ret < 0) { 350 ENVF_MSG("No valid env data, ret=%d\n", ret); 351 return ret; 352 } 353 354 ENVF_MSG("OK\n"); 355 printf(" - %s\n", CONFIG_ENVF_LIST); 356 #ifdef CONFIG_ENV_PARTITION 357 /* 358 * Call blk_dread() to read envf content: 359 * => mmc_init() => part_init() with CONFIG_ENV_PARTITION_LIST => mmc init ok 360 * => read envf content! including partition tables(blkdevparts/mtdparts). 361 * 362 * So we must reinit env partition after we setup the partition 363 * tables(blkdevparts/mtdparts) from envf. 364 */ 365 part_init(desc); 366 #endif 367 return 0; 368 } 369 370 static int envf_save(void) 371 { 372 ALLOC_CACHE_ALIGN_BUFFER(env_t, env, 1); 373 struct blk_desc *desc; 374 u32 blk_cnt; 375 ssize_t len; 376 char *res; 377 int ret = 0; 378 379 desc = rockchip_get_bootdev(); 380 if (!desc) { 381 printf("dev desc null!\n"); 382 return -EINVAL; 383 } 384 385 res = (char *)env->data; 386 len = hexport_r(&env_htab, '\0', H_MATCH_KEY | H_MATCH_IDENT, 387 &res, env_size - ENV_HEADER_SIZE, 388 envf_num, (char * const *)envf_list); 389 if (len < 0) { 390 ENVF_MSG("hexpor error: %d\n", errno); 391 return -EINVAL; 392 } 393 394 env->crc = crc32(0, env->data, env_size - ENV_HEADER_SIZE); 395 blk_cnt = BLK_CNT(desc, env_size); 396 if (blk_dwrite(desc, BLK_CNT(desc, env_offset), 397 blk_cnt, (char *)env) != blk_cnt) { 398 ret = -EIO; 399 ENVF_MSG("io error\n"); 400 } 401 402 if (env_offset_redund) { 403 if (blk_dwrite(desc, BLK_CNT(desc, env_offset_redund), 404 blk_cnt, (char *)env) != blk_cnt) 405 ENVF_MSG("redundant: io error\n"); 406 else 407 ret = 0; 408 } 409 410 return ret; 411 } 412 413 static int envf_nowhere_init(void) 414 { 415 gd->env_addr = (ulong)&default_environment[0]; 416 gd->env_valid = ENV_INVALID; 417 418 return 0; 419 } 420 421 U_BOOT_ENV_LOCATION(nowhere) = { 422 .location = ENVL_NOWHERE, 423 .init = envf_nowhere_init, 424 .load = envf_load, 425 .save = env_save_ptr(envf_save), 426 ENV_NAME("envf") 427 }; 428 #endif 429