blob: a19f136647d083f38f567db0633b82217db8a1ef [file] [log] [blame]
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +02001/*
2 * (C) Copyright 2008 Semihalf
3 *
4 * Written by: Rafal Czubak <rcz@semihalf.com>
5 * Bartlomiej Sieka <tur@semihalf.com>
6 *
7 * See file CREDITS for list of people who contributed to this
8 * project.
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License as
12 * published by the Free Software Foundation; either version 2 of
13 * the License, or (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
23 * MA 02111-1307 USA
24 *
25 */
26
27#include <common.h>
28
29#if !(defined(CONFIG_FIT) && defined(CONFIG_OF_LIBFDT))
30#error "CONFIG_FIT and CONFIG_OF_LIBFDT are required for auto-update feature"
31#endif
32
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +020033#if defined(CONFIG_SYS_NO_FLASH)
34#error "CONFIG_SYS_NO_FLASH defined, but FLASH is required for auto-update feature"
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +020035#endif
36
37#include <command.h>
38#include <flash.h>
39#include <net.h>
40#include <malloc.h>
41
42/* env variable holding the location of the update file */
43#define UPDATE_FILE_ENV "updatefile"
44
45/* set configuration defaults if needed */
46#ifndef CONFIG_UPDATE_LOAD_ADDR
47#define CONFIG_UPDATE_LOAD_ADDR 0x100000
48#endif
49
50#ifndef CONFIG_UPDATE_TFTP_MSEC_MAX
51#define CONFIG_UPDATE_TFTP_MSEC_MAX 100
52#endif
53
54#ifndef CONFIG_UPDATE_TFTP_CNT_MAX
55#define CONFIG_UPDATE_TFTP_CNT_MAX 0
56#endif
57
58extern ulong TftpRRQTimeoutMSecs;
59extern int TftpRRQTimeoutCountMax;
60extern flash_info_t flash_info[];
61extern ulong load_addr;
62
63static uchar *saved_prot_info;
64
65static int update_load(char *filename, ulong msec_max, int cnt_max, ulong addr)
66{
67 int size, rv;
68 ulong saved_timeout_msecs;
69 int saved_timeout_count;
70 char *saved_netretry, *saved_bootfile;
71
72 rv = 0;
73 /* save used globals and env variable */
74 saved_timeout_msecs = TftpRRQTimeoutMSecs;
75 saved_timeout_count = TftpRRQTimeoutCountMax;
76 saved_netretry = strdup(getenv("netretry"));
77 saved_bootfile = strdup(BootFile);
78
79 /* set timeouts for auto-update */
80 TftpRRQTimeoutMSecs = msec_max;
81 TftpRRQTimeoutCountMax = cnt_max;
82
83 /* we don't want to retry the connection if errors occur */
84 setenv("netretry", "no");
85
86 /* download the update file */
87 load_addr = addr;
88 copy_filename(BootFile, filename, sizeof(BootFile));
89 size = NetLoop(TFTP);
90
91 if (size < 0)
92 rv = 1;
93 else if (size > 0)
94 flush_cache(addr, size);
95
96 /* restore changed globals and env variable */
97 TftpRRQTimeoutMSecs = saved_timeout_msecs;
98 TftpRRQTimeoutCountMax = saved_timeout_count;
99
100 setenv("netretry", saved_netretry);
101 if (saved_netretry != NULL)
102 free(saved_netretry);
103
104 if (saved_bootfile != NULL) {
105 copy_filename(BootFile, saved_bootfile, sizeof(BootFile));
106 free(saved_bootfile);
107 }
108
109 return rv;
110}
111
112static int update_flash_protect(int prot, ulong addr_first, ulong addr_last)
113{
114 uchar *sp_info_ptr;
115 ulong s;
116 int i, bank, cnt;
117 flash_info_t *info;
118
119 sp_info_ptr = NULL;
120
121 if (prot == 0) {
122 saved_prot_info =
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200123 calloc(CONFIG_SYS_MAX_FLASH_BANKS * CONFIG_SYS_MAX_FLASH_SECT, 1);
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200124 if (!saved_prot_info)
125 return 1;
126 }
127
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200128 for (bank = 0; bank < CONFIG_SYS_MAX_FLASH_BANKS; ++bank) {
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200129 cnt = 0;
130 info = &flash_info[bank];
131
132 /* Nothing to do if the bank doesn't exist */
133 if (info->sector_count == 0)
134 return 0;
135
136 /* Point to current bank protection information */
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200137 sp_info_ptr = saved_prot_info + (bank * CONFIG_SYS_MAX_FLASH_SECT);
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200138
139 /*
140 * Adjust addr_first or addr_last if we are on bank boundary.
141 * Address space between banks must be continuous for other
142 * flash functions (like flash_sect_erase or flash_write) to
143 * succeed. Banks must also be numbered in correct order,
144 * according to increasing addresses.
145 */
146 if (addr_last > info->start[0] + info->size - 1)
147 addr_last = info->start[0] + info->size - 1;
148 if (addr_first < info->start[0])
149 addr_first = info->start[0];
150
151 for (i = 0; i < info->sector_count; i++) {
152 /* Save current information about protected sectors */
153 if (prot == 0) {
154 s = info->start[i];
155 if ((s >= addr_first) && (s <= addr_last))
156 sp_info_ptr[i] = info->protect[i];
157
158 }
159
160 /* Protect/unprotect sectors */
161 if (sp_info_ptr[i] == 1) {
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200162#if defined(CONFIG_SYS_FLASH_PROTECTION)
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200163 if (flash_real_protect(info, i, prot))
164 return 1;
165#else
166 info->protect[i] = prot;
167#endif
168 cnt++;
169 }
170 }
171
172 if (cnt) {
173 printf("%sProtected %d sectors\n",
174 prot ? "": "Un-", cnt);
175 }
176 }
177
178 if((prot == 1) && saved_prot_info)
179 free(saved_prot_info);
180
181 return 0;
182}
183
184static int update_flash(ulong addr_source, ulong addr_first, ulong size)
185{
186 ulong addr_last = addr_first + size - 1;
187
188 /* round last address to the sector boundary */
189 if (flash_sect_roundb(&addr_last) > 0)
190 return 1;
191
192 if (addr_first >= addr_last) {
193 printf("Error: end address exceeds addressing space\n");
194 return 1;
195 }
196
197 /* remove protection on processed sectors */
198 if (update_flash_protect(0, addr_first, addr_last) > 0) {
199 printf("Error: could not unprotect flash sectors\n");
200 return 1;
201 }
202
203 printf("Erasing 0x%08lx - 0x%08lx", addr_first, addr_last);
204 if (flash_sect_erase(addr_first, addr_last) > 0) {
205 printf("Error: could not erase flash\n");
206 return 1;
207 }
208
209 printf("Copying to flash...");
210 if (flash_write((char *)addr_source, addr_first, size) > 0) {
211 printf("Error: could not copy to flash\n");
212 return 1;
213 }
214 printf("done\n");
215
216 /* enable protection on processed sectors */
217 if (update_flash_protect(1, addr_first, addr_last) > 0) {
218 printf("Error: could not protect flash sectors\n");
219 return 1;
220 }
221
222 return 0;
223}
224
225static int update_fit_getparams(const void *fit, int noffset, ulong *addr,
226 ulong *fladdr, ulong *size)
227{
228 const void *data;
229
230 if (fit_image_get_data(fit, noffset, &data, (size_t *)size))
231 return 1;
232
233 if (fit_image_get_load(fit, noffset, (ulong *)fladdr))
234 return 1;
235
236 *addr = (ulong)data;
237
238 return 0;
239}
240
Andreas Pretzsche3fd7c02011-07-16 05:50:59 +0000241int update_tftp(ulong addr)
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200242{
243 char *filename, *env_addr;
244 int images_noffset, ndepth, noffset;
245 ulong update_addr, update_fladdr, update_size;
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200246 void *fit;
Andreas Pretzsche3fd7c02011-07-16 05:50:59 +0000247 int ret = 0;
248
249 /* use already present image */
250 if (addr)
251 goto got_update_file;
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200252
253 printf("Auto-update from TFTP: ");
254
255 /* get the file name of the update file */
256 filename = getenv(UPDATE_FILE_ENV);
257 if (filename == NULL) {
258 printf("failed, env. variable '%s' not found\n",
259 UPDATE_FILE_ENV);
Andreas Pretzsche3fd7c02011-07-16 05:50:59 +0000260 return 1;
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200261 }
262
263 printf("trying update file '%s'\n", filename);
264
265 /* get load address of downloaded update file */
266 if ((env_addr = getenv("loadaddr")) != NULL)
267 addr = simple_strtoul(env_addr, NULL, 16);
268 else
269 addr = CONFIG_UPDATE_LOAD_ADDR;
270
271
272 if (update_load(filename, CONFIG_UPDATE_TFTP_MSEC_MAX,
273 CONFIG_UPDATE_TFTP_CNT_MAX, addr)) {
274 printf("Can't load update file, aborting auto-update\n");
Andreas Pretzsche3fd7c02011-07-16 05:50:59 +0000275 return 1;
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200276 }
277
Andreas Pretzsche3fd7c02011-07-16 05:50:59 +0000278got_update_file:
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200279 fit = (void *)addr;
280
281 if (!fit_check_format((void *)fit)) {
282 printf("Bad FIT format of the update file, aborting "
283 "auto-update\n");
Andreas Pretzsche3fd7c02011-07-16 05:50:59 +0000284 return 1;
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200285 }
286
287 /* process updates */
288 images_noffset = fdt_path_offset(fit, FIT_IMAGES_PATH);
289
290 ndepth = 0;
291 noffset = fdt_next_node(fit, images_noffset, &ndepth);
292 while (noffset >= 0 && ndepth > 0) {
293 if (ndepth != 1)
294 goto next_node;
295
296 printf("Processing update '%s' :",
297 fit_get_name(fit, noffset, NULL));
298
299 if (!fit_image_check_hashes(fit, noffset)) {
300 printf("Error: invalid update hash, aborting\n");
Andreas Pretzsche3fd7c02011-07-16 05:50:59 +0000301 ret = 1;
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200302 goto next_node;
303 }
304
305 printf("\n");
306 if (update_fit_getparams(fit, noffset, &update_addr,
307 &update_fladdr, &update_size)) {
308 printf("Error: can't get update parameteres, "
309 "aborting\n");
Andreas Pretzsche3fd7c02011-07-16 05:50:59 +0000310 ret = 1;
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200311 goto next_node;
312 }
313 if (update_flash(update_addr, update_fladdr, update_size)) {
314 printf("Error: can't flash update, aborting\n");
Andreas Pretzsche3fd7c02011-07-16 05:50:59 +0000315 ret = 1;
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200316 goto next_node;
317 }
318next_node:
319 noffset = fdt_next_node(fit, noffset, &ndepth);
320 }
321
Andreas Pretzsche3fd7c02011-07-16 05:50:59 +0000322 return ret;
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +0200323}