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 #include <part_efi.h> 13 #include <asm/unaligned.h> 14 15 DECLARE_GLOBAL_DATA_PTR; 16 17 /* 18 * Example: ./tools/mkenvimage -s 0x8000 -p 0x0 -o env.img env.txt 19 */ 20 #define ENVF_MSG(fmt, args...) printf("ENVF: "fmt, ##args) 21 #define ENVF_DBG(fmt, args...) debug("ENVF: "fmt, ##args) 22 23 #define EMSG_ARGS "error: please use \"sys_bootargs\" but not \"bootargs\"" 24 #define BLK_CNT(desc, sz) ((sz) / (desc)->blksz) 25 #define ENVF_MAX 64 26 27 static ulong env_size, env_offset, env_offset_redund; 28 29 #if CONFIG_IS_ENABLED(ENV_PARTITION) 30 static const char *part_type[] = { "mtdparts", "blkdevparts", }; 31 #endif 32 33 /* 34 * In case of env and env-backup partitions are too large that exceeds the limit 35 * of CONFIG_SPL_SYS_MALLOC_F_LEN. we prefer to use a static address as an env 36 * buffer. The tail of bss section is good choice from experience. 37 */ 38 #ifdef CONFIG_SPL_BUILD 39 static void *spl_env = 40 (void *)CONFIG_SPL_BSS_START_ADDR + CONFIG_SPL_BSS_MAX_SIZE; 41 #else 42 static u32 envf_num; 43 static const char *envf_list[ENVF_MAX]; 44 #endif 45 46 #ifdef CONFIG_DM_MMC 47 static int pmbr_part_valid(struct partition *part) 48 { 49 if (part->sys_ind == EFI_PMBR_OSTYPE_EFI_GPT && 50 get_unaligned_le32(&part->start_sect) == 1UL) { 51 return 1; 52 } 53 54 return 0; 55 } 56 57 static int is_pmbr_valid(legacy_mbr * mbr) 58 { 59 int i = 0; 60 61 if (!mbr || le16_to_cpu(mbr->signature) != MSDOS_MBR_SIGNATURE) 62 return 0; 63 64 for (i = 0; i < 4; i++) { 65 if (pmbr_part_valid(&mbr->partition_record[i])) { 66 return 1; 67 } 68 } 69 return 0; 70 } 71 72 static int can_find_pmbr(struct blk_desc *dev_desc) 73 { 74 ALLOC_CACHE_ALIGN_BUFFER_PAD(legacy_mbr, legacymbr, 1, dev_desc->blksz); 75 76 /* Read legacy MBR from block 0 and validate it */ 77 if ((blk_dread(dev_desc, 0, 1, (ulong *)legacymbr) != 1) 78 || (is_pmbr_valid(legacymbr) != 1)) { 79 return -1; 80 } 81 82 return 0; 83 } 84 #endif 85 86 static void envf_init_location(struct blk_desc *desc) 87 { 88 /* eMMC (default) */ 89 env_size = CONFIG_ENV_SIZE; 90 env_offset = CONFIG_ENV_OFFSET; 91 env_offset_redund = CONFIG_ENV_OFFSET_REDUND; 92 93 #if defined(CONFIG_MTD_SPI_NAND) || defined(CONFIG_CMD_NAND) 94 /* nand or spi-nand */ 95 if (desc->if_type == IF_TYPE_MTD && 96 (desc->devnum == BLK_MTD_SPI_NAND || desc->devnum == BLK_MTD_NAND)) { 97 env_size = CONFIG_ENV_NAND_SIZE; 98 env_offset = CONFIG_ENV_NAND_OFFSET; 99 env_offset_redund = CONFIG_ENV_NAND_OFFSET_REDUND; 100 } 101 #endif 102 #if defined(CONFIG_SPI_FLASH) 103 /* spi-nor */ 104 if (desc->if_type == IF_TYPE_MTD && desc->devnum == BLK_MTD_SPI_NOR) { 105 env_size = CONFIG_ENV_NOR_SIZE; 106 env_offset = CONFIG_ENV_NOR_OFFSET; 107 env_offset_redund = CONFIG_ENV_NOR_OFFSET_REDUND; 108 } 109 #endif 110 if (env_offset == env_offset_redund) 111 env_offset_redund = 0; 112 } 113 114 static int env_read(struct blk_desc *desc, u32 offset, u32 size, env_t **envp) 115 { 116 lbaint_t data_size; 117 lbaint_t blk_off; 118 lbaint_t blk_cnt; 119 env_t *env; 120 int ret; 121 122 #ifdef CONFIG_SPL_BUILD 123 env = spl_env; 124 #else 125 env = malloc(size); 126 if (!env) 127 return -ENOMEM; 128 #endif 129 data_size = size - ENV_HEADER_SIZE; 130 blk_off = BLK_CNT(desc, offset); 131 blk_cnt = BLK_CNT(desc, size); 132 133 if (blk_dread(desc, blk_off, blk_cnt, (void *)env) != blk_cnt) { 134 ret = -EIO; 135 goto fail; 136 } 137 138 if (crc32(0, env->data, data_size) != env->crc) { 139 ENVF_MSG("!bad CRC @ 0x%x\n", offset); 140 ret = -EINVAL; 141 goto fail; 142 } 143 144 *envp = env; 145 146 return 0; 147 fail: 148 #ifndef CONFIG_SPL_BUILD 149 free(env); 150 #endif 151 return ret; 152 } 153 154 static __maybe_unused env_t *envf_read(struct blk_desc *desc) 155 { 156 env_t *env = NULL; 157 int ret; 158 159 if (!desc) 160 return NULL; 161 162 envf_init_location(desc); 163 164 #ifdef CONFIG_DM_MMC 165 /* SD upgrade card: LBA0 is MBR, and LBA1 is env.img with 16KB size */ 166 if (desc->if_type == IF_TYPE_MMC && desc->devnum == 1 && 167 !env_offset && can_find_pmbr(desc)) { 168 env_offset = 512; 169 env_size = SZ_16K; 170 } 171 #endif 172 ret = env_read(desc, env_offset, env_size, &env); 173 if (ret < 0 && env_offset_redund) 174 ret = env_read(desc, env_offset_redund, env_size, &env); 175 176 return env; 177 } 178 179 #if CONFIG_IS_ENABLED(ENV_PARTITION) 180 static const char *env_get_string(env_t *env, u32 size, const char *str) 181 { 182 const char *dp; 183 u32 env_size; 184 185 dp = (const char *)env->data; 186 env_size = size - ENV_HEADER_SIZE; 187 do { 188 /* skip leading white space */ 189 while (*dp == ' ' || *dp == '\t') 190 ++dp; 191 192 debug("ENTRY: %s\n", dp); 193 if (strstr(dp, str)) { 194 debug("FIND: %s\n", dp); 195 return dp; 196 } 197 198 /* point to next ENTRY */ 199 dp += strlen(dp) + 1; 200 } while (((ulong)dp < (ulong)env->data + env_size) && *dp); 201 202 debug("NOT-FIND: %s\n", str); 203 204 return NULL; 205 } 206 207 char *envf_get_part_table(struct blk_desc *desc) 208 { 209 const char *list = NULL; 210 env_t *env; 211 212 if (!desc) 213 goto out; 214 215 env = envf_read(desc); 216 if (!env) 217 goto out; 218 219 ENVF_MSG("Primary 0x%08lx - 0x%08lx\n", env_offset, env_offset + env_size); 220 if (env_offset_redund) 221 ENVF_MSG("Backup 0x%08lx - 0x%08lx\n", 222 env_offset_redund, env_offset_redund + env_size); 223 224 list = env_get_string(env, env_size, part_type[0]); 225 if (!list) 226 list = env_get_string(env, env_size, part_type[1]); 227 if (!list) 228 ENVF_MSG("Unavailable env part table\n"); 229 else 230 ENVF_MSG("OK\n"); 231 out: 232 return (char *)list; 233 } 234 #endif 235 236 #ifndef CONFIG_SPL_BUILD 237 static int envf_init_vars(void) 238 { 239 char *tok, *p; 240 241 tok = strdup(CONFIG_ENVF_LIST); 242 if (!tok) 243 return 0; 244 245 envf_num = 0; 246 p = strtok(tok, " "); 247 while (p && envf_num < ENVF_MAX) { 248 if (!strcmp(p, "bootargs")) { 249 printf("%s\n", EMSG_ARGS); 250 run_command("download", 0); 251 #ifdef CONFIG_FIT_SIGNATURE 252 } else if (!strcmp(p, "sys_bootargs")) { 253 /* Do nothing, ignore 'sys_bootargs' from env.img */ 254 #endif 255 } else { 256 envf_list[envf_num++] = p; 257 } 258 259 p = strtok(NULL, " "); 260 } 261 262 return envf_num; 263 } 264 265 #ifdef CONFIG_ENV_PARTITION 266 static int envf_add_partition_bootargs(void) 267 { 268 char *part_list; 269 char *bootargs; 270 int i; 271 272 for (i = 0; i < ARRAY_SIZE(part_type); i++) { 273 part_list = env_get(part_type[i]); 274 if (part_list) 275 break; 276 } 277 if (!part_list) 278 return -EINVAL; 279 280 bootargs = calloc(1, strlen(part_list) + strlen(part_type[i]) + 2); 281 if (!bootargs) 282 return -ENOMEM; 283 284 strcat(bootargs, part_type[i]); 285 strcat(bootargs, "="); 286 strcat(bootargs, part_list); 287 env_update("bootargs", bootargs); 288 free(bootargs); 289 290 return 0; 291 } 292 #endif 293 294 static int envf_load(void) 295 { 296 struct blk_desc *desc; 297 env_t *env; 298 299 desc = rockchip_get_bootdev(); 300 if (!desc) { 301 printf("dev desc null!\n"); 302 return 0; 303 } 304 305 env = envf_read(desc); 306 if (!env) 307 return -EINVAL; 308 309 if (envf_init_vars() > 0) { 310 if (!himport_r(&env_htab, (char *)env->data, env_size, '\0', 311 H_NOCLEAR, 0, envf_num, (char * const *)envf_list)) { 312 ENVF_MSG("envf himport error: %d\n", errno); 313 return -EINTR; 314 } 315 } 316 317 #ifdef CONFIG_ENV_PARTITION 318 envf_add_partition_bootargs(); 319 #endif 320 321 return 0; 322 } 323 324 static int envf_save(void) 325 { 326 ALLOC_CACHE_ALIGN_BUFFER(env_t, env, 1); 327 struct blk_desc *desc; 328 ssize_t len; 329 u32 blk_cnt; 330 char *res; 331 int ret = 0; 332 333 desc = rockchip_get_bootdev(); 334 if (!desc) { 335 printf("dev desc null!\n"); 336 return -EINVAL; 337 } 338 339 res = (char *)env->data; 340 len = hexport_r(&env_htab, '\0', H_MATCH_KEY | H_MATCH_IDENT, 341 &res, env_size - ENV_HEADER_SIZE, 342 envf_num, (char * const *)envf_list); 343 if (len < 0) { 344 ENVF_MSG("hexpor error: %d\n", errno); 345 return -EINVAL; 346 } 347 348 env->crc = crc32(0, env->data, env_size - ENV_HEADER_SIZE); 349 blk_cnt = BLK_CNT(desc, env_size); 350 if (blk_dwrite(desc, BLK_CNT(desc, env_offset), 351 blk_cnt, (char *)env) != blk_cnt) { 352 ret = -EIO; 353 ENVF_MSG("io error\n"); 354 } 355 356 if (env_offset_redund) { 357 if (blk_dwrite(desc, BLK_CNT(desc, env_offset_redund), 358 blk_cnt, (char *)env) != blk_cnt) 359 ENVF_MSG("redundant: io error\n"); 360 else 361 ret = 0; 362 } 363 364 return ret; 365 } 366 367 static int envf_nowhere_init(void) 368 { 369 gd->env_addr = (ulong)&default_environment[0]; 370 gd->env_valid = ENV_INVALID; 371 372 return 0; 373 } 374 375 U_BOOT_ENV_LOCATION(nowhere) = { 376 .location = ENVL_NOWHERE, 377 .init = envf_nowhere_init, 378 .load = envf_load, 379 .save = env_save_ptr(envf_save), 380 ENV_NAME("envf") 381 }; 382 #endif 383 384