blob: 36b6b7523d50b9354fbf18c90a5c1a9ff7d7d5ee [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
9#include <common.h>
Simon Glass63334482019-11-14 12:57:39 -070010#include <cpu_func.h>
Simon Glass85f13782019-12-28 10:45:03 -070011#include <image.h>
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +020012
13#if !(defined(CONFIG_FIT) && defined(CONFIG_OF_LIBFDT))
14#error "CONFIG_FIT and CONFIG_OF_LIBFDT are required for auto-update feature"
15#endif
16
Masahiro Yamada8cea9b52017-02-11 22:43:54 +090017#if defined(CONFIG_UPDATE_TFTP) && !defined(CONFIG_MTD_NOR_FLASH)
18#error "CONFIG_UPDATE_TFTP and !CONFIG_MTD_NOR_FLASH needed for legacy behaviour"
Lukasz Majewskie32125a2015-08-24 00:21:47 +020019#endif
20
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +020021#include <command.h>
Simon Glass5e6201b2019-08-01 09:46:51 -060022#include <env.h>
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +020023#include <flash.h>
24#include <net.h>
Kim Phillips40c2c032012-10-29 13:34:33 +000025#include <net/tftp.h>
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +020026#include <malloc.h>
Heinrich Schuchardt8784b0b2020-07-22 17:46:02 +020027#include <mapmem.h>
Lukasz Majewskie32125a2015-08-24 00:21:47 +020028#include <dfu.h>
29#include <errno.h>
Marek Vasutc6468512018-02-10 16:22:07 +010030#include <mtd/cfi_flash.h>
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +020031
32/* env variable holding the location of the update file */
33#define UPDATE_FILE_ENV "updatefile"
34
35/* set configuration defaults if needed */
36#ifndef CONFIG_UPDATE_LOAD_ADDR
37#define CONFIG_UPDATE_LOAD_ADDR 0x100000
38#endif
39
40#ifndef CONFIG_UPDATE_TFTP_MSEC_MAX
41#define CONFIG_UPDATE_TFTP_MSEC_MAX 100
42#endif
43
44#ifndef CONFIG_UPDATE_TFTP_CNT_MAX
45#define CONFIG_UPDATE_TFTP_CNT_MAX 0
46#endif
47
Joe Hershberger64701592015-04-08 01:41:07 -050048extern ulong tftp_timeout_ms;
49extern int tftp_timeout_count_max;
Masahiro Yamada8cea9b52017-02-11 22:43:54 +090050#ifdef CONFIG_MTD_NOR_FLASH
Lukasz Majewskidc65f6d2015-08-24 00:21:44 +020051extern flash_info_t flash_info[];
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +020052static uchar *saved_prot_info;
Lukasz Majewskidc65f6d2015-08-24 00:21:44 +020053#endif
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +020054static int update_load(char *filename, ulong msec_max, int cnt_max, ulong addr)
55{
56 int size, rv;
57 ulong saved_timeout_msecs;
58 int saved_timeout_count;
59 char *saved_netretry, *saved_bootfile;
60
61 rv = 0;
62 /* save used globals and env variable */
Joe Hershberger64701592015-04-08 01:41:07 -050063 saved_timeout_msecs = tftp_timeout_ms;
64 saved_timeout_count = tftp_timeout_count_max;
Simon Glass64b723f2017-08-03 12:22:12 -060065 saved_netretry = strdup(env_get("netretry"));
Joe Hershberger290c8992015-04-08 01:41:02 -050066 saved_bootfile = strdup(net_boot_file_name);
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +020067
68 /* set timeouts for auto-update */
Joe Hershberger64701592015-04-08 01:41:07 -050069 tftp_timeout_ms = msec_max;
70 tftp_timeout_count_max = cnt_max;
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +020071
72 /* we don't want to retry the connection if errors occur */
Simon Glass6a38e412017-08-03 12:22:09 -060073 env_set("netretry", "no");
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +020074
75 /* download the update file */
Simon Glass892265d2019-12-28 10:45:02 -070076 image_load_addr = addr;
Joe Hershberger290c8992015-04-08 01:41:02 -050077 copy_filename(net_boot_file_name, filename, sizeof(net_boot_file_name));
Joe Hershbergerc80b41b02015-04-08 01:41:21 -050078 size = net_loop(TFTPGET);
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +020079
80 if (size < 0)
81 rv = 1;
82 else if (size > 0)
83 flush_cache(addr, size);
84
85 /* restore changed globals and env variable */
Joe Hershberger64701592015-04-08 01:41:07 -050086 tftp_timeout_ms = saved_timeout_msecs;
87 tftp_timeout_count_max = saved_timeout_count;
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +020088
Simon Glass6a38e412017-08-03 12:22:09 -060089 env_set("netretry", saved_netretry);
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +020090 if (saved_netretry != NULL)
91 free(saved_netretry);
92
93 if (saved_bootfile != NULL) {
Joe Hershberger290c8992015-04-08 01:41:02 -050094 copy_filename(net_boot_file_name, saved_bootfile,
95 sizeof(net_boot_file_name));
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +020096 free(saved_bootfile);
97 }
98
99 return rv;
100}
101
Masahiro Yamada8cea9b52017-02-11 22:43:54 +0900102#ifdef CONFIG_MTD_NOR_FLASH
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200103static int update_flash_protect(int prot, ulong addr_first, ulong addr_last)
104{
105 uchar *sp_info_ptr;
106 ulong s;
107 int i, bank, cnt;
108 flash_info_t *info;
109
110 sp_info_ptr = NULL;
111
112 if (prot == 0) {
113 saved_prot_info =
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200114 calloc(CONFIG_SYS_MAX_FLASH_BANKS * CONFIG_SYS_MAX_FLASH_SECT, 1);
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200115 if (!saved_prot_info)
116 return 1;
117 }
118
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200119 for (bank = 0; bank < CONFIG_SYS_MAX_FLASH_BANKS; ++bank) {
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200120 cnt = 0;
121 info = &flash_info[bank];
122
123 /* Nothing to do if the bank doesn't exist */
124 if (info->sector_count == 0)
125 return 0;
126
127 /* Point to current bank protection information */
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200128 sp_info_ptr = saved_prot_info + (bank * CONFIG_SYS_MAX_FLASH_SECT);
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200129
130 /*
131 * Adjust addr_first or addr_last if we are on bank boundary.
132 * Address space between banks must be continuous for other
133 * flash functions (like flash_sect_erase or flash_write) to
134 * succeed. Banks must also be numbered in correct order,
135 * according to increasing addresses.
136 */
137 if (addr_last > info->start[0] + info->size - 1)
138 addr_last = info->start[0] + info->size - 1;
139 if (addr_first < info->start[0])
140 addr_first = info->start[0];
141
142 for (i = 0; i < info->sector_count; i++) {
143 /* Save current information about protected sectors */
144 if (prot == 0) {
145 s = info->start[i];
146 if ((s >= addr_first) && (s <= addr_last))
147 sp_info_ptr[i] = info->protect[i];
148
149 }
150
151 /* Protect/unprotect sectors */
152 if (sp_info_ptr[i] == 1) {
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200153#if defined(CONFIG_SYS_FLASH_PROTECTION)
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200154 if (flash_real_protect(info, i, prot))
155 return 1;
156#else
157 info->protect[i] = prot;
158#endif
159 cnt++;
160 }
161 }
162
163 if (cnt) {
164 printf("%sProtected %d sectors\n",
165 prot ? "": "Un-", cnt);
166 }
167 }
168
169 if((prot == 1) && saved_prot_info)
170 free(saved_prot_info);
171
172 return 0;
173}
Lukasz Majewskidc65f6d2015-08-24 00:21:44 +0200174#endif
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200175
176static int update_flash(ulong addr_source, ulong addr_first, ulong size)
177{
Masahiro Yamada8cea9b52017-02-11 22:43:54 +0900178#ifdef CONFIG_MTD_NOR_FLASH
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200179 ulong addr_last = addr_first + size - 1;
180
181 /* round last address to the sector boundary */
182 if (flash_sect_roundb(&addr_last) > 0)
183 return 1;
184
185 if (addr_first >= addr_last) {
186 printf("Error: end address exceeds addressing space\n");
187 return 1;
188 }
189
190 /* remove protection on processed sectors */
191 if (update_flash_protect(0, addr_first, addr_last) > 0) {
192 printf("Error: could not unprotect flash sectors\n");
193 return 1;
194 }
195
196 printf("Erasing 0x%08lx - 0x%08lx", addr_first, addr_last);
197 if (flash_sect_erase(addr_first, addr_last) > 0) {
198 printf("Error: could not erase flash\n");
199 return 1;
200 }
201
202 printf("Copying to flash...");
203 if (flash_write((char *)addr_source, addr_first, size) > 0) {
204 printf("Error: could not copy to flash\n");
205 return 1;
206 }
207 printf("done\n");
208
209 /* enable protection on processed sectors */
210 if (update_flash_protect(1, addr_first, addr_last) > 0) {
211 printf("Error: could not protect flash sectors\n");
212 return 1;
213 }
Lukasz Majewskidc65f6d2015-08-24 00:21:44 +0200214#endif
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200215 return 0;
216}
217
218static int update_fit_getparams(const void *fit, int noffset, ulong *addr,
219 ulong *fladdr, ulong *size)
220{
221 const void *data;
222
223 if (fit_image_get_data(fit, noffset, &data, (size_t *)size))
224 return 1;
225
226 if (fit_image_get_load(fit, noffset, (ulong *)fladdr))
227 return 1;
228
229 *addr = (ulong)data;
230
231 return 0;
232}
233
Lukasz Majewskie32125a2015-08-24 00:21:47 +0200234int update_tftp(ulong addr, char *interface, char *devstring)
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200235{
Lukasz Majewskie32125a2015-08-24 00:21:47 +0200236 char *filename, *env_addr, *fit_image_name;
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200237 ulong update_addr, update_fladdr, update_size;
Lukasz Majewskie32125a2015-08-24 00:21:47 +0200238 int images_noffset, ndepth, noffset;
239 bool update_tftp_dfu;
Andreas Pretzsche3fd7c02011-07-16 05:50:59 +0000240 int ret = 0;
Lukasz Majewskie32125a2015-08-24 00:21:47 +0200241 void *fit;
242
243 if (interface == NULL && devstring == NULL) {
244 update_tftp_dfu = false;
245 } else if (interface && devstring) {
246 update_tftp_dfu = true;
247 } else {
Masahiro Yamada81e10422017-09-16 14:10:41 +0900248 pr_err("Interface: %s and devstring: %s not supported!\n",
Lukasz Majewskie32125a2015-08-24 00:21:47 +0200249 interface, devstring);
250 return -EINVAL;
251 }
Andreas Pretzsche3fd7c02011-07-16 05:50:59 +0000252
253 /* use already present image */
254 if (addr)
255 goto got_update_file;
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200256
257 printf("Auto-update from TFTP: ");
258
259 /* get the file name of the update file */
Simon Glass64b723f2017-08-03 12:22:12 -0600260 filename = env_get(UPDATE_FILE_ENV);
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200261 if (filename == NULL) {
262 printf("failed, env. variable '%s' not found\n",
263 UPDATE_FILE_ENV);
Andreas Pretzsche3fd7c02011-07-16 05:50:59 +0000264 return 1;
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200265 }
266
267 printf("trying update file '%s'\n", filename);
268
269 /* get load address of downloaded update file */
Simon Glass64b723f2017-08-03 12:22:12 -0600270 env_addr = env_get("loadaddr");
271 if (env_addr)
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200272 addr = simple_strtoul(env_addr, NULL, 16);
273 else
274 addr = CONFIG_UPDATE_LOAD_ADDR;
275
276
277 if (update_load(filename, CONFIG_UPDATE_TFTP_MSEC_MAX,
278 CONFIG_UPDATE_TFTP_CNT_MAX, addr)) {
279 printf("Can't load update file, aborting auto-update\n");
Andreas Pretzsche3fd7c02011-07-16 05:50:59 +0000280 return 1;
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200281 }
282
Andreas Pretzsche3fd7c02011-07-16 05:50:59 +0000283got_update_file:
Heinrich Schuchardt8784b0b2020-07-22 17:46:02 +0200284 fit = map_sysmem(addr, 0);
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200285
286 if (!fit_check_format((void *)fit)) {
287 printf("Bad FIT format of the update file, aborting "
288 "auto-update\n");
Andreas Pretzsche3fd7c02011-07-16 05:50:59 +0000289 return 1;
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200290 }
291
292 /* process updates */
293 images_noffset = fdt_path_offset(fit, FIT_IMAGES_PATH);
294
295 ndepth = 0;
296 noffset = fdt_next_node(fit, images_noffset, &ndepth);
297 while (noffset >= 0 && ndepth > 0) {
298 if (ndepth != 1)
299 goto next_node;
300
Lukasz Majewskie32125a2015-08-24 00:21:47 +0200301 fit_image_name = (char *)fit_get_name(fit, noffset, NULL);
302 printf("Processing update '%s' :", fit_image_name);
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200303
Simon Glass7428ad12013-05-07 06:11:57 +0000304 if (!fit_image_verify(fit, noffset)) {
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200305 printf("Error: invalid update hash, aborting\n");
Andreas Pretzsche3fd7c02011-07-16 05:50:59 +0000306 ret = 1;
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200307 goto next_node;
308 }
309
310 printf("\n");
311 if (update_fit_getparams(fit, noffset, &update_addr,
312 &update_fladdr, &update_size)) {
Heinrich Schuchardt814ddd32020-07-17 19:55:54 +0200313 printf("Error: can't get update parameters, aborting\n");
Andreas Pretzsche3fd7c02011-07-16 05:50:59 +0000314 ret = 1;
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200315 goto next_node;
316 }
Lukasz Majewskie32125a2015-08-24 00:21:47 +0200317
318 if (!update_tftp_dfu) {
319 if (update_flash(update_addr, update_fladdr,
320 update_size)) {
321 printf("Error: can't flash update, aborting\n");
322 ret = 1;
323 goto next_node;
324 }
325 } else if (fit_image_check_type(fit, noffset,
326 IH_TYPE_FIRMWARE)) {
327 ret = dfu_tftp_write(fit_image_name, update_addr,
328 update_size, interface, devstring);
329 if (ret)
330 return ret;
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200331 }
332next_node:
333 noffset = fdt_next_node(fit, noffset, &ndepth);
334 }
335
Andreas Pretzsche3fd7c02011-07-16 05:50:59 +0000336 return ret;
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200337}