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