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