Merge tag 'u-boot-stm32-20231004' of https://source.denx.de/u-boot/custodians/u-boot-stm
[pandora-u-boot.git] / common / update.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2008 Semihalf
4  *
5  * Written by: Rafal Czubak <rcz@semihalf.com>
6  *             Bartlomiej Sieka <tur@semihalf.com>
7  */
8
9 #include <common.h>
10 #include <cpu_func.h>
11 #include <image.h>
12 #include <linux/printk.h>
13
14 #include <command.h>
15 #include <env.h>
16 #include <net.h>
17 #include <net/tftp.h>
18 #include <malloc.h>
19 #include <mapmem.h>
20 #include <dfu.h>
21 #include <errno.h>
22
23 #if defined(CONFIG_DFU_TFTP) || defined(CONFIG_UPDATE_TFTP)
24 /* env variable holding the location of the update file */
25 #define UPDATE_FILE_ENV         "updatefile"
26
27 extern ulong tftp_timeout_ms;
28 extern int tftp_timeout_count_max;
29 #ifdef CONFIG_MTD_NOR_FLASH
30 #include <flash.h>
31 #include <mtd/cfi_flash.h>
32 static uchar *saved_prot_info;
33 #endif
34 static int update_load(char *filename, ulong msec_max, int cnt_max, ulong addr)
35 {
36         int size, rv;
37         ulong saved_timeout_msecs;
38         int saved_timeout_count;
39         char *saved_netretry, *saved_bootfile;
40
41         rv = 0;
42         /* save used globals and env variable */
43         saved_timeout_msecs = tftp_timeout_ms;
44         saved_timeout_count = tftp_timeout_count_max;
45         saved_netretry = strdup(env_get("netretry"));
46         saved_bootfile = strdup(net_boot_file_name);
47
48         /* set timeouts for auto-update */
49         tftp_timeout_ms = msec_max;
50         tftp_timeout_count_max = cnt_max;
51
52         /* we don't want to retry the connection if errors occur */
53         env_set("netretry", "no");
54
55         /* download the update file */
56         image_load_addr = addr;
57         copy_filename(net_boot_file_name, filename, sizeof(net_boot_file_name));
58         size = net_loop(TFTPGET);
59
60         if (size < 0)
61                 rv = 1;
62         else if (size > 0)
63                 flush_cache(addr, size);
64
65         /* restore changed globals and env variable */
66         tftp_timeout_ms = saved_timeout_msecs;
67         tftp_timeout_count_max = saved_timeout_count;
68
69         env_set("netretry", saved_netretry);
70         if (saved_netretry != NULL)
71                 free(saved_netretry);
72
73         if (saved_bootfile != NULL) {
74                 copy_filename(net_boot_file_name, saved_bootfile,
75                               sizeof(net_boot_file_name));
76                 free(saved_bootfile);
77         }
78
79         return rv;
80 }
81
82 #ifdef CONFIG_MTD_NOR_FLASH
83 static int update_flash_protect(int prot, ulong addr_first, ulong addr_last)
84 {
85         uchar *sp_info_ptr;
86         ulong s;
87         int i, bank, cnt;
88         flash_info_t *info;
89
90         sp_info_ptr = NULL;
91
92         if (prot == 0) {
93                 saved_prot_info =
94                         calloc(CFI_FLASH_BANKS * CONFIG_SYS_MAX_FLASH_SECT, 1);
95                 if (!saved_prot_info)
96                         return 1;
97         }
98
99         for (bank = 0; bank < CFI_FLASH_BANKS; ++bank) {
100                 cnt = 0;
101                 info = &flash_info[bank];
102
103                 /* Nothing to do if the bank doesn't exist */
104                 if (info->sector_count == 0)
105                         return 0;
106
107                 /* Point to current bank protection information */
108                 sp_info_ptr = saved_prot_info + (bank * CONFIG_SYS_MAX_FLASH_SECT);
109
110                 /*
111                  * Adjust addr_first or addr_last if we are on bank boundary.
112                  * Address space between banks must be continuous for other
113                  * flash functions (like flash_sect_erase or flash_write) to
114                  * succeed. Banks must also be numbered in correct order,
115                  * according to increasing addresses.
116                  */
117                 if (addr_last > info->start[0] + info->size - 1)
118                         addr_last = info->start[0] + info->size - 1;
119                 if (addr_first < info->start[0])
120                         addr_first = info->start[0];
121
122                 for (i = 0; i < info->sector_count; i++) {
123                         /* Save current information about protected sectors */
124                         if (prot == 0) {
125                                 s = info->start[i];
126                                 if ((s >= addr_first) && (s <= addr_last))
127                                         sp_info_ptr[i] = info->protect[i];
128
129                         }
130
131                         /* Protect/unprotect sectors */
132                         if (sp_info_ptr[i] == 1) {
133 #if defined(CONFIG_SYS_FLASH_PROTECTION)
134                                 if (flash_real_protect(info, i, prot))
135                                         return 1;
136 #else
137                                 info->protect[i] = prot;
138 #endif
139                                 cnt++;
140                         }
141                 }
142
143                 if (cnt) {
144                         printf("%sProtected %d sectors\n",
145                                                 prot ? "": "Un-", cnt);
146                 }
147         }
148
149         if((prot == 1) && saved_prot_info)
150                 free(saved_prot_info);
151
152         return 0;
153 }
154 #endif
155
156 static int update_flash(ulong addr_source, ulong addr_first, ulong size)
157 {
158 #ifdef CONFIG_MTD_NOR_FLASH
159         ulong addr_last = addr_first + size - 1;
160
161         /* round last address to the sector boundary */
162         if (flash_sect_roundb(&addr_last) > 0)
163                 return 1;
164
165         if (addr_first >= addr_last) {
166                 printf("Error: end address exceeds addressing space\n");
167                 return 1;
168         }
169
170         /* remove protection on processed sectors */
171         if (update_flash_protect(0, addr_first, addr_last) > 0) {
172                 printf("Error: could not unprotect flash sectors\n");
173                 return 1;
174         }
175
176         printf("Erasing 0x%08lx - 0x%08lx", addr_first, addr_last);
177         if (flash_sect_erase(addr_first, addr_last) > 0) {
178                 printf("Error: could not erase flash\n");
179                 return 1;
180         }
181
182         printf("Copying to flash...");
183         if (flash_write((char *)addr_source, addr_first, size) > 0) {
184                 printf("Error: could not copy to flash\n");
185                 return 1;
186         }
187         printf("done\n");
188
189         /* enable protection on processed sectors */
190         if (update_flash_protect(1, addr_first, addr_last) > 0) {
191                 printf("Error: could not protect flash sectors\n");
192                 return 1;
193         }
194 #endif
195         return 0;
196 }
197 #endif /* CONFIG_DFU_TFTP || CONFIG_UPDATE_TFTP */
198
199 static int update_fit_getparams(const void *fit, int noffset, ulong *addr,
200                                                 ulong *fladdr, ulong *size)
201 {
202         const void *data;
203
204         if (fit_image_get_data(fit, noffset, &data, (size_t *)size))
205                 return 1;
206
207         if (fit_image_get_load(fit, noffset, (ulong *)fladdr))
208                 return 1;
209
210         *addr = (ulong)data;
211
212         return 0;
213 }
214
215 #if defined(CONFIG_DFU_TFTP) || defined(CONFIG_UPDATE_TFTP)
216 int update_tftp(ulong addr, char *interface, char *devstring)
217 {
218         char *filename, *env_addr, *fit_image_name;
219         ulong update_addr, update_fladdr, update_size;
220         int images_noffset, ndepth, noffset;
221         bool update_tftp_dfu;
222         int ret = 0;
223         void *fit;
224
225         if (interface == NULL && devstring == NULL) {
226                 update_tftp_dfu = false;
227         } else if (interface && devstring) {
228                 update_tftp_dfu = true;
229         } else {
230                 pr_err("Interface: %s and devstring: %s not supported!\n",
231                       interface, devstring);
232                 return -EINVAL;
233         }
234
235         /* use already present image */
236         if (addr)
237                 goto got_update_file;
238
239         printf("Auto-update from TFTP: ");
240
241         /* get the file name of the update file */
242         filename = env_get(UPDATE_FILE_ENV);
243         if (filename == NULL) {
244                 printf("failed, env. variable '%s' not found\n",
245                                                         UPDATE_FILE_ENV);
246                 return 1;
247         }
248
249         printf("trying update file '%s'\n", filename);
250
251         /* get load address of downloaded update file */
252         env_addr = env_get("loadaddr");
253         if (env_addr)
254                 addr = hextoul(env_addr, NULL);
255         else
256                 addr = CONFIG_UPDATE_LOAD_ADDR;
257
258
259         if (update_load(filename, CONFIG_UPDATE_TFTP_MSEC_MAX,
260                                         CONFIG_UPDATE_TFTP_CNT_MAX, addr)) {
261                 printf("Can't load update file, aborting auto-update\n");
262                 return 1;
263         }
264
265 got_update_file:
266         fit = map_sysmem(addr, 0);
267
268         if (fit_check_format((void *)fit, IMAGE_SIZE_INVAL)) {
269                 printf("Bad FIT format of the update file, aborting "
270                                                         "auto-update\n");
271                 return 1;
272         }
273
274         /* process updates */
275         images_noffset = fdt_path_offset(fit, FIT_IMAGES_PATH);
276
277         ndepth = 0;
278         noffset = fdt_next_node(fit, images_noffset, &ndepth);
279         while (noffset >= 0 && ndepth > 0) {
280                 if (ndepth != 1)
281                         goto next_node;
282
283                 fit_image_name = (char *)fit_get_name(fit, noffset, NULL);
284                 printf("Processing update '%s' :", fit_image_name);
285
286                 if (!fit_image_verify(fit, noffset)) {
287                         printf("Error: invalid update hash, aborting\n");
288                         ret = 1;
289                         goto next_node;
290                 }
291
292                 printf("\n");
293                 if (update_fit_getparams(fit, noffset, &update_addr,
294                                         &update_fladdr, &update_size)) {
295                         printf("Error: can't get update parameters, aborting\n");
296                         ret = 1;
297                         goto next_node;
298                 }
299
300                 if (!update_tftp_dfu) {
301                         if (update_flash(update_addr, update_fladdr,
302                                          update_size)) {
303                                 printf("Error: can't flash update, aborting\n");
304                                 ret = 1;
305                                 goto next_node;
306                         }
307                 } else if (fit_image_check_type(fit, noffset,
308                                                 IH_TYPE_FIRMWARE)) {
309                         ret = dfu_write_by_name(fit_image_name,
310                                                 (void *)update_addr,
311                                                 update_size, interface,
312                                                 devstring);
313                         if (ret)
314                                 return ret;
315                 }
316 next_node:
317                 noffset = fdt_next_node(fit, noffset, &ndepth);
318         }
319
320         return ret;
321 }
322 #endif /* CONFIG_DFU_UPDATE || CONFIG_UPDATE_TFTP */
323
324 #ifdef CONFIG_UPDATE_FIT
325 /**
326  * fit_update - update storage with FIT image
327  * @fit:        Pointer to FIT image
328  *
329  * Update firmware on storage using FIT image as input.
330  * The storage area to be update will be identified by the name
331  * in FIT and matching it to "dfu_alt_info" variable.
332  *
333  * Return:      0 - on success, non-zero - otherwise
334  */
335 int fit_update(const void *fit)
336 {
337         char *fit_image_name;
338         ulong update_addr, update_fladdr, update_size;
339         int images_noffset, ndepth, noffset;
340         int ret = 0;
341
342         if (!fit)
343                 return -EINVAL;
344
345         if (fit_check_format((void *)fit, IMAGE_SIZE_INVAL)) {
346                 printf("Bad FIT format of the update file, aborting auto-update\n");
347                 return -EINVAL;
348         }
349
350         /* process updates */
351         images_noffset = fdt_path_offset(fit, FIT_IMAGES_PATH);
352
353         ndepth = 0;
354         noffset = fdt_next_node(fit, images_noffset, &ndepth);
355         while (noffset >= 0 && ndepth > 0) {
356                 if (ndepth != 1)
357                         goto next_node;
358
359                 fit_image_name = (char *)fit_get_name(fit, noffset, NULL);
360                 printf("Processing update '%s' :", fit_image_name);
361
362                 if (!fit_image_verify(fit, noffset)) {
363                         printf("Error: invalid update hash, aborting\n");
364                         ret = 1;
365                         goto next_node;
366                 }
367
368                 printf("\n");
369                 if (update_fit_getparams(fit, noffset, &update_addr,
370                                          &update_fladdr, &update_size)) {
371                         printf("Error: can't get update parameters, aborting\n");
372                         ret = 1;
373                         goto next_node;
374                 }
375
376                 if (fit_image_check_type(fit, noffset, IH_TYPE_FIRMWARE)) {
377                         ret = dfu_write_by_name(fit_image_name,
378                                                 (void *)update_addr,
379                                                 update_size, NULL, NULL);
380                         if (ret)
381                                 return ret;
382                 }
383 next_node:
384                 noffset = fdt_next_node(fit, noffset, &ndepth);
385         }
386
387         return ret;
388 }
389 #endif /* CONFIG_UPDATE_FIT */