blob: ec302ca68fb0925a590eb1e94f3db67507cb500d [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Bartlomiej Sieka4bae9092008-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 Sieka4bae9092008-10-01 15:26:31 +02007 */
8
Tom Rinid678a592024-05-18 20:20:43 -06009#include <common.h>
Simon Glass1eb69ae2019-11-14 12:57:39 -070010#include <cpu_func.h>
Simon Glass8e8ccfe2019-12-28 10:45:03 -070011#include <image.h>
Simon Glass1e94b462023-09-14 18:21:46 -060012#include <linux/printk.h>
Bartlomiej Sieka4bae9092008-10-01 15:26:31 +020013
Bartlomiej Sieka4bae9092008-10-01 15:26:31 +020014#include <command.h>
Simon Glass9fb625c2019-08-01 09:46:51 -060015#include <env.h>
Bartlomiej Sieka4bae9092008-10-01 15:26:31 +020016#include <net.h>
Kim Phillips06370592012-10-29 13:34:33 +000017#include <net/tftp.h>
Bartlomiej Sieka4bae9092008-10-01 15:26:31 +020018#include <malloc.h>
Heinrich Schuchardt73f4ebb2020-07-22 17:46:02 +020019#include <mapmem.h>
Lukasz Majewskic7ff5522015-08-24 00:21:47 +020020#include <dfu.h>
21#include <errno.h>
Bartlomiej Sieka4bae9092008-10-01 15:26:31 +020022
AKASHI Takahiro50efdf22020-11-19 09:37:19 +090023#if defined(CONFIG_DFU_TFTP) || defined(CONFIG_UPDATE_TFTP)
Bartlomiej Sieka4bae9092008-10-01 15:26:31 +020024/* env variable holding the location of the update file */
25#define UPDATE_FILE_ENV "updatefile"
26
Joe Hershberger8885c5f2015-04-08 01:41:07 -050027extern ulong tftp_timeout_ms;
28extern int tftp_timeout_count_max;
Masahiro Yamadae856bdc2017-02-11 22:43:54 +090029#ifdef CONFIG_MTD_NOR_FLASH
Tom Rini17ead042022-07-23 13:05:03 -040030#include <flash.h>
31#include <mtd/cfi_flash.h>
Bartlomiej Sieka4bae9092008-10-01 15:26:31 +020032static uchar *saved_prot_info;
Lukasz Majewski66a64722015-08-24 00:21:44 +020033#endif
Bartlomiej Sieka4bae9092008-10-01 15:26:31 +020034static 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 */
Joe Hershberger8885c5f2015-04-08 01:41:07 -050043 saved_timeout_msecs = tftp_timeout_ms;
44 saved_timeout_count = tftp_timeout_count_max;
Simon Glass00caae62017-08-03 12:22:12 -060045 saved_netretry = strdup(env_get("netretry"));
Joe Hershberger14111572015-04-08 01:41:02 -050046 saved_bootfile = strdup(net_boot_file_name);
Bartlomiej Sieka4bae9092008-10-01 15:26:31 +020047
48 /* set timeouts for auto-update */
Joe Hershberger8885c5f2015-04-08 01:41:07 -050049 tftp_timeout_ms = msec_max;
50 tftp_timeout_count_max = cnt_max;
Bartlomiej Sieka4bae9092008-10-01 15:26:31 +020051
52 /* we don't want to retry the connection if errors occur */
Simon Glass382bee52017-08-03 12:22:09 -060053 env_set("netretry", "no");
Bartlomiej Sieka4bae9092008-10-01 15:26:31 +020054
55 /* download the update file */
Simon Glassbb872dd2019-12-28 10:45:02 -070056 image_load_addr = addr;
Joe Hershberger14111572015-04-08 01:41:02 -050057 copy_filename(net_boot_file_name, filename, sizeof(net_boot_file_name));
Joe Hershbergerbc0571f2015-04-08 01:41:21 -050058 size = net_loop(TFTPGET);
Bartlomiej Sieka4bae9092008-10-01 15:26:31 +020059
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 */
Joe Hershberger8885c5f2015-04-08 01:41:07 -050066 tftp_timeout_ms = saved_timeout_msecs;
67 tftp_timeout_count_max = saved_timeout_count;
Bartlomiej Sieka4bae9092008-10-01 15:26:31 +020068
Simon Glass382bee52017-08-03 12:22:09 -060069 env_set("netretry", saved_netretry);
Bartlomiej Sieka4bae9092008-10-01 15:26:31 +020070 if (saved_netretry != NULL)
71 free(saved_netretry);
72
73 if (saved_bootfile != NULL) {
Joe Hershberger14111572015-04-08 01:41:02 -050074 copy_filename(net_boot_file_name, saved_bootfile,
75 sizeof(net_boot_file_name));
Bartlomiej Sieka4bae9092008-10-01 15:26:31 +020076 free(saved_bootfile);
77 }
78
79 return rv;
80}
81
Masahiro Yamadae856bdc2017-02-11 22:43:54 +090082#ifdef CONFIG_MTD_NOR_FLASH
Bartlomiej Sieka4bae9092008-10-01 15:26:31 +020083static 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 =
Patrick Delaunay98150e72022-01-04 14:23:58 +010094 calloc(CFI_FLASH_BANKS * CONFIG_SYS_MAX_FLASH_SECT, 1);
Bartlomiej Sieka4bae9092008-10-01 15:26:31 +020095 if (!saved_prot_info)
96 return 1;
97 }
98
Patrick Delaunay98150e72022-01-04 14:23:58 +010099 for (bank = 0; bank < CFI_FLASH_BANKS; ++bank) {
Bartlomiej Sieka4bae9092008-10-01 15:26:31 +0200100 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 */
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +0200108 sp_info_ptr = saved_prot_info + (bank * CONFIG_SYS_MAX_FLASH_SECT);
Bartlomiej Sieka4bae9092008-10-01 15:26:31 +0200109
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) {
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +0200133#if defined(CONFIG_SYS_FLASH_PROTECTION)
Bartlomiej Sieka4bae9092008-10-01 15:26:31 +0200134 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}
Lukasz Majewski66a64722015-08-24 00:21:44 +0200154#endif
Bartlomiej Sieka4bae9092008-10-01 15:26:31 +0200155
156static int update_flash(ulong addr_source, ulong addr_first, ulong size)
157{
Masahiro Yamadae856bdc2017-02-11 22:43:54 +0900158#ifdef CONFIG_MTD_NOR_FLASH
Bartlomiej Sieka4bae9092008-10-01 15:26:31 +0200159 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 }
Lukasz Majewski66a64722015-08-24 00:21:44 +0200194#endif
Bartlomiej Sieka4bae9092008-10-01 15:26:31 +0200195 return 0;
196}
AKASHI Takahiro50efdf22020-11-19 09:37:19 +0900197#endif /* CONFIG_DFU_TFTP || CONFIG_UPDATE_TFTP */
Bartlomiej Sieka4bae9092008-10-01 15:26:31 +0200198
199static 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
AKASHI Takahiro50efdf22020-11-19 09:37:19 +0900215#if defined(CONFIG_DFU_TFTP) || defined(CONFIG_UPDATE_TFTP)
Lukasz Majewskic7ff5522015-08-24 00:21:47 +0200216int update_tftp(ulong addr, char *interface, char *devstring)
Bartlomiej Sieka4bae9092008-10-01 15:26:31 +0200217{
Lukasz Majewskic7ff5522015-08-24 00:21:47 +0200218 char *filename, *env_addr, *fit_image_name;
Bartlomiej Sieka4bae9092008-10-01 15:26:31 +0200219 ulong update_addr, update_fladdr, update_size;
Lukasz Majewskic7ff5522015-08-24 00:21:47 +0200220 int images_noffset, ndepth, noffset;
221 bool update_tftp_dfu;
Andreas Pretzsch8d6b7322011-07-16 05:50:59 +0000222 int ret = 0;
Lukasz Majewskic7ff5522015-08-24 00:21:47 +0200223 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 {
Masahiro Yamada9b643e32017-09-16 14:10:41 +0900230 pr_err("Interface: %s and devstring: %s not supported!\n",
Lukasz Majewskic7ff5522015-08-24 00:21:47 +0200231 interface, devstring);
232 return -EINVAL;
233 }
Andreas Pretzsch8d6b7322011-07-16 05:50:59 +0000234
235 /* use already present image */
236 if (addr)
237 goto got_update_file;
Bartlomiej Sieka4bae9092008-10-01 15:26:31 +0200238
239 printf("Auto-update from TFTP: ");
240
241 /* get the file name of the update file */
Simon Glass00caae62017-08-03 12:22:12 -0600242 filename = env_get(UPDATE_FILE_ENV);
Bartlomiej Sieka4bae9092008-10-01 15:26:31 +0200243 if (filename == NULL) {
244 printf("failed, env. variable '%s' not found\n",
245 UPDATE_FILE_ENV);
Andreas Pretzsch8d6b7322011-07-16 05:50:59 +0000246 return 1;
Bartlomiej Sieka4bae9092008-10-01 15:26:31 +0200247 }
248
249 printf("trying update file '%s'\n", filename);
250
251 /* get load address of downloaded update file */
Simon Glass00caae62017-08-03 12:22:12 -0600252 env_addr = env_get("loadaddr");
253 if (env_addr)
Simon Glass7e5f4602021-07-24 09:03:29 -0600254 addr = hextoul(env_addr, NULL);
Bartlomiej Sieka4bae9092008-10-01 15:26:31 +0200255 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");
Andreas Pretzsch8d6b7322011-07-16 05:50:59 +0000262 return 1;
Bartlomiej Sieka4bae9092008-10-01 15:26:31 +0200263 }
264
Andreas Pretzsch8d6b7322011-07-16 05:50:59 +0000265got_update_file:
Heinrich Schuchardt73f4ebb2020-07-22 17:46:02 +0200266 fit = map_sysmem(addr, 0);
Bartlomiej Sieka4bae9092008-10-01 15:26:31 +0200267
Simon Glassc5819702021-02-15 17:08:09 -0700268 if (fit_check_format((void *)fit, IMAGE_SIZE_INVAL)) {
Bartlomiej Sieka4bae9092008-10-01 15:26:31 +0200269 printf("Bad FIT format of the update file, aborting "
270 "auto-update\n");
Andreas Pretzsch8d6b7322011-07-16 05:50:59 +0000271 return 1;
Bartlomiej Sieka4bae9092008-10-01 15:26:31 +0200272 }
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
Lukasz Majewskic7ff5522015-08-24 00:21:47 +0200283 fit_image_name = (char *)fit_get_name(fit, noffset, NULL);
284 printf("Processing update '%s' :", fit_image_name);
Bartlomiej Sieka4bae9092008-10-01 15:26:31 +0200285
Simon Glassb8da8362013-05-07 06:11:57 +0000286 if (!fit_image_verify(fit, noffset)) {
Bartlomiej Sieka4bae9092008-10-01 15:26:31 +0200287 printf("Error: invalid update hash, aborting\n");
Andreas Pretzsch8d6b7322011-07-16 05:50:59 +0000288 ret = 1;
Bartlomiej Sieka4bae9092008-10-01 15:26:31 +0200289 goto next_node;
290 }
291
292 printf("\n");
293 if (update_fit_getparams(fit, noffset, &update_addr,
294 &update_fladdr, &update_size)) {
Heinrich Schuchardt4e620eb2020-07-17 19:55:54 +0200295 printf("Error: can't get update parameters, aborting\n");
Andreas Pretzsch8d6b7322011-07-16 05:50:59 +0000296 ret = 1;
Bartlomiej Sieka4bae9092008-10-01 15:26:31 +0200297 goto next_node;
298 }
Lukasz Majewskic7ff5522015-08-24 00:21:47 +0200299
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)) {
AKASHI Takahiro1c2d1292020-10-29 13:47:42 +0900309 ret = dfu_write_by_name(fit_image_name,
310 (void *)update_addr,
AKASHI Takahiro045fd8b2020-10-29 13:47:41 +0900311 update_size, interface,
312 devstring);
Lukasz Majewskic7ff5522015-08-24 00:21:47 +0200313 if (ret)
314 return ret;
Bartlomiej Sieka4bae9092008-10-01 15:26:31 +0200315 }
316next_node:
317 noffset = fdt_next_node(fit, noffset, &ndepth);
318 }
319
Andreas Pretzsch8d6b7322011-07-16 05:50:59 +0000320 return ret;
Bartlomiej Sieka4bae9092008-10-01 15:26:31 +0200321}
AKASHI Takahiro50efdf22020-11-19 09:37:19 +0900322#endif /* CONFIG_DFU_UPDATE || CONFIG_UPDATE_TFTP */
AKASHI Takahiro3149e522020-10-29 13:47:43 +0900323
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 */
335int 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
Simon Glassc5819702021-02-15 17:08:09 -0700345 if (fit_check_format((void *)fit, IMAGE_SIZE_INVAL)) {
AKASHI Takahiro3149e522020-10-29 13:47:43 +0900346 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 }
383next_node:
384 noffset = fdt_next_node(fit, noffset, &ndepth);
385 }
386
387 return ret;
388}
389#endif /* CONFIG_UPDATE_FIT */