blob: 6801b49479d6a8e8831ba30b37e9edcbd73a48ac [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +02002/*
3 * (C) Copyright 2008 Semihalf
4 *
5 * Written by: Rafal Czubak <rcz@semihalf.com>
6 * Bartlomiej Sieka <tur@semihalf.com>
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +02007 */
8
Simon Glass63334482019-11-14 12:57:39 -07009#include <cpu_func.h>
Simon Glass85f13782019-12-28 10:45:03 -070010#include <image.h>
Simon Glassbdd5f812023-09-14 18:21:46 -060011#include <linux/printk.h>
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +020012
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +020013#include <command.h>
Simon Glass5e6201b2019-08-01 09:46:51 -060014#include <env.h>
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +020015#include <net.h>
Kim Phillips40c2c032012-10-29 13:34:33 +000016#include <net/tftp.h>
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +020017#include <malloc.h>
Heinrich Schuchardt8784b0b2020-07-22 17:46:02 +020018#include <mapmem.h>
Lukasz Majewskie32125a2015-08-24 00:21:47 +020019#include <dfu.h>
20#include <errno.h>
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +020021
AKASHI Takahiro92c80802020-11-19 09:37:19 +090022#if defined(CONFIG_DFU_TFTP) || defined(CONFIG_UPDATE_TFTP)
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +020023/* env variable holding the location of the update file */
24#define UPDATE_FILE_ENV "updatefile"
25
Joe Hershberger64701592015-04-08 01:41:07 -050026extern ulong tftp_timeout_ms;
27extern int tftp_timeout_count_max;
Masahiro Yamada8cea9b52017-02-11 22:43:54 +090028#ifdef CONFIG_MTD_NOR_FLASH
Tom Rini063c9382022-07-23 13:05:03 -040029#include <flash.h>
30#include <mtd/cfi_flash.h>
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +020031static uchar *saved_prot_info;
Lukasz Majewskidc65f6d2015-08-24 00:21:44 +020032#endif
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +020033static 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 */
Joe Hershberger64701592015-04-08 01:41:07 -050042 saved_timeout_msecs = tftp_timeout_ms;
43 saved_timeout_count = tftp_timeout_count_max;
Simon Glass64b723f2017-08-03 12:22:12 -060044 saved_netretry = strdup(env_get("netretry"));
Joe Hershberger290c8992015-04-08 01:41:02 -050045 saved_bootfile = strdup(net_boot_file_name);
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +020046
47 /* set timeouts for auto-update */
Joe Hershberger64701592015-04-08 01:41:07 -050048 tftp_timeout_ms = msec_max;
49 tftp_timeout_count_max = cnt_max;
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +020050
51 /* we don't want to retry the connection if errors occur */
Simon Glass6a38e412017-08-03 12:22:09 -060052 env_set("netretry", "no");
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +020053
54 /* download the update file */
Simon Glass892265d2019-12-28 10:45:02 -070055 image_load_addr = addr;
Joe Hershberger290c8992015-04-08 01:41:02 -050056 copy_filename(net_boot_file_name, filename, sizeof(net_boot_file_name));
Joe Hershbergerc80b41b02015-04-08 01:41:21 -050057 size = net_loop(TFTPGET);
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +020058
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 */
Joe Hershberger64701592015-04-08 01:41:07 -050065 tftp_timeout_ms = saved_timeout_msecs;
66 tftp_timeout_count_max = saved_timeout_count;
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +020067
Simon Glass6a38e412017-08-03 12:22:09 -060068 env_set("netretry", saved_netretry);
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +020069 if (saved_netretry != NULL)
70 free(saved_netretry);
71
72 if (saved_bootfile != NULL) {
Joe Hershberger290c8992015-04-08 01:41:02 -050073 copy_filename(net_boot_file_name, saved_bootfile,
74 sizeof(net_boot_file_name));
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +020075 free(saved_bootfile);
76 }
77
78 return rv;
79}
80
Masahiro Yamada8cea9b52017-02-11 22:43:54 +090081#ifdef CONFIG_MTD_NOR_FLASH
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +020082static 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 =
Patrick Delaunay6c5f5602022-01-04 14:23:58 +010093 calloc(CFI_FLASH_BANKS * CONFIG_SYS_MAX_FLASH_SECT, 1);
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +020094 if (!saved_prot_info)
95 return 1;
96 }
97
Patrick Delaunay6c5f5602022-01-04 14:23:58 +010098 for (bank = 0; bank < CFI_FLASH_BANKS; ++bank) {
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +020099 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 */
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200107 sp_info_ptr = saved_prot_info + (bank * CONFIG_SYS_MAX_FLASH_SECT);
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200108
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) {
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200132#if defined(CONFIG_SYS_FLASH_PROTECTION)
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200133 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}
Lukasz Majewskidc65f6d2015-08-24 00:21:44 +0200153#endif
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200154
155static int update_flash(ulong addr_source, ulong addr_first, ulong size)
156{
Masahiro Yamada8cea9b52017-02-11 22:43:54 +0900157#ifdef CONFIG_MTD_NOR_FLASH
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200158 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 }
Lukasz Majewskidc65f6d2015-08-24 00:21:44 +0200193#endif
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200194 return 0;
195}
AKASHI Takahiro92c80802020-11-19 09:37:19 +0900196#endif /* CONFIG_DFU_TFTP || CONFIG_UPDATE_TFTP */
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200197
198static 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
AKASHI Takahiro92c80802020-11-19 09:37:19 +0900214#if defined(CONFIG_DFU_TFTP) || defined(CONFIG_UPDATE_TFTP)
Lukasz Majewskie32125a2015-08-24 00:21:47 +0200215int update_tftp(ulong addr, char *interface, char *devstring)
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200216{
Lukasz Majewskie32125a2015-08-24 00:21:47 +0200217 char *filename, *env_addr, *fit_image_name;
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200218 ulong update_addr, update_fladdr, update_size;
Lukasz Majewskie32125a2015-08-24 00:21:47 +0200219 int images_noffset, ndepth, noffset;
220 bool update_tftp_dfu;
Andreas Pretzsche3fd7c02011-07-16 05:50:59 +0000221 int ret = 0;
Lukasz Majewskie32125a2015-08-24 00:21:47 +0200222 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 {
Masahiro Yamada81e10422017-09-16 14:10:41 +0900229 pr_err("Interface: %s and devstring: %s not supported!\n",
Lukasz Majewskie32125a2015-08-24 00:21:47 +0200230 interface, devstring);
231 return -EINVAL;
232 }
Andreas Pretzsche3fd7c02011-07-16 05:50:59 +0000233
234 /* use already present image */
235 if (addr)
236 goto got_update_file;
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200237
238 printf("Auto-update from TFTP: ");
239
240 /* get the file name of the update file */
Simon Glass64b723f2017-08-03 12:22:12 -0600241 filename = env_get(UPDATE_FILE_ENV);
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200242 if (filename == NULL) {
243 printf("failed, env. variable '%s' not found\n",
244 UPDATE_FILE_ENV);
Andreas Pretzsche3fd7c02011-07-16 05:50:59 +0000245 return 1;
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200246 }
247
248 printf("trying update file '%s'\n", filename);
249
250 /* get load address of downloaded update file */
Simon Glass64b723f2017-08-03 12:22:12 -0600251 env_addr = env_get("loadaddr");
252 if (env_addr)
Simon Glass3ff49ec2021-07-24 09:03:29 -0600253 addr = hextoul(env_addr, NULL);
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200254 else
255 addr = CONFIG_UPDATE_LOAD_ADDR;
256
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200257 if (update_load(filename, CONFIG_UPDATE_TFTP_MSEC_MAX,
258 CONFIG_UPDATE_TFTP_CNT_MAX, addr)) {
259 printf("Can't load update file, aborting auto-update\n");
Andreas Pretzsche3fd7c02011-07-16 05:50:59 +0000260 return 1;
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200261 }
262
Andreas Pretzsche3fd7c02011-07-16 05:50:59 +0000263got_update_file:
Heinrich Schuchardt8784b0b2020-07-22 17:46:02 +0200264 fit = map_sysmem(addr, 0);
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200265
Simon Glassd563c252021-02-15 17:08:09 -0700266 if (fit_check_format((void *)fit, IMAGE_SIZE_INVAL)) {
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200267 printf("Bad FIT format of the update file, aborting "
268 "auto-update\n");
Andreas Pretzsche3fd7c02011-07-16 05:50:59 +0000269 return 1;
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200270 }
271
272 /* process updates */
273 images_noffset = fdt_path_offset(fit, FIT_IMAGES_PATH);
274
275 ndepth = 0;
276 noffset = fdt_next_node(fit, images_noffset, &ndepth);
277 while (noffset >= 0 && ndepth > 0) {
278 if (ndepth != 1)
279 goto next_node;
280
Lukasz Majewskie32125a2015-08-24 00:21:47 +0200281 fit_image_name = (char *)fit_get_name(fit, noffset, NULL);
282 printf("Processing update '%s' :", fit_image_name);
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200283
Simon Glass7428ad12013-05-07 06:11:57 +0000284 if (!fit_image_verify(fit, noffset)) {
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200285 printf("Error: invalid update hash, aborting\n");
Andreas Pretzsche3fd7c02011-07-16 05:50:59 +0000286 ret = 1;
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200287 goto next_node;
288 }
289
290 printf("\n");
291 if (update_fit_getparams(fit, noffset, &update_addr,
292 &update_fladdr, &update_size)) {
Heinrich Schuchardt814ddd32020-07-17 19:55:54 +0200293 printf("Error: can't get update parameters, aborting\n");
Andreas Pretzsche3fd7c02011-07-16 05:50:59 +0000294 ret = 1;
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200295 goto next_node;
296 }
Lukasz Majewskie32125a2015-08-24 00:21:47 +0200297
298 if (!update_tftp_dfu) {
299 if (update_flash(update_addr, update_fladdr,
300 update_size)) {
301 printf("Error: can't flash update, aborting\n");
302 ret = 1;
303 goto next_node;
304 }
305 } else if (fit_image_check_type(fit, noffset,
306 IH_TYPE_FIRMWARE)) {
AKASHI Takahirobd4dc182020-10-29 13:47:42 +0900307 ret = dfu_write_by_name(fit_image_name,
308 (void *)update_addr,
AKASHI Takahirofb6732f2020-10-29 13:47:41 +0900309 update_size, interface,
310 devstring);
Lukasz Majewskie32125a2015-08-24 00:21:47 +0200311 if (ret)
312 return ret;
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200313 }
314next_node:
315 noffset = fdt_next_node(fit, noffset, &ndepth);
316 }
317
Andreas Pretzsche3fd7c02011-07-16 05:50:59 +0000318 return ret;
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200319}
AKASHI Takahiro92c80802020-11-19 09:37:19 +0900320#endif /* CONFIG_DFU_UPDATE || CONFIG_UPDATE_TFTP */
AKASHI Takahiro768d58d2020-10-29 13:47:43 +0900321
322#ifdef CONFIG_UPDATE_FIT
323/**
324 * fit_update - update storage with FIT image
325 * @fit: Pointer to FIT image
326 *
327 * Update firmware on storage using FIT image as input.
328 * The storage area to be update will be identified by the name
329 * in FIT and matching it to "dfu_alt_info" variable.
330 *
331 * Return: 0 - on success, non-zero - otherwise
332 */
333int fit_update(const void *fit)
334{
335 char *fit_image_name;
336 ulong update_addr, update_fladdr, update_size;
337 int images_noffset, ndepth, noffset;
338 int ret = 0;
339
340 if (!fit)
341 return -EINVAL;
342
Simon Glassd563c252021-02-15 17:08:09 -0700343 if (fit_check_format((void *)fit, IMAGE_SIZE_INVAL)) {
AKASHI Takahiro768d58d2020-10-29 13:47:43 +0900344 printf("Bad FIT format of the update file, aborting auto-update\n");
345 return -EINVAL;
346 }
347
348 /* process updates */
349 images_noffset = fdt_path_offset(fit, FIT_IMAGES_PATH);
350
351 ndepth = 0;
352 noffset = fdt_next_node(fit, images_noffset, &ndepth);
353 while (noffset >= 0 && ndepth > 0) {
354 if (ndepth != 1)
355 goto next_node;
356
357 fit_image_name = (char *)fit_get_name(fit, noffset, NULL);
358 printf("Processing update '%s' :", fit_image_name);
359
360 if (!fit_image_verify(fit, noffset)) {
361 printf("Error: invalid update hash, aborting\n");
362 ret = 1;
363 goto next_node;
364 }
365
366 printf("\n");
367 if (update_fit_getparams(fit, noffset, &update_addr,
368 &update_fladdr, &update_size)) {
369 printf("Error: can't get update parameters, aborting\n");
370 ret = 1;
371 goto next_node;
372 }
373
374 if (fit_image_check_type(fit, noffset, IH_TYPE_FIRMWARE)) {
375 ret = dfu_write_by_name(fit_image_name,
376 (void *)update_addr,
377 update_size, NULL, NULL);
378 if (ret)
379 return ret;
380 }
381next_node:
382 noffset = fdt_next_node(fit, noffset, &ndepth);
383 }
384
385 return ret;
386}
387#endif /* CONFIG_UPDATE_FIT */