"Das U-Boot" Source Tree
at master 387 lines 9.5 kB view raw
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 <cpu_func.h> 10#include <image.h> 11#include <linux/printk.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 26extern ulong tftp_timeout_ms; 27extern int tftp_timeout_count_max; 28#ifdef CONFIG_MTD_NOR_FLASH 29#include <flash.h> 30#include <mtd/cfi_flash.h> 31static uchar *saved_prot_info; 32#endif 33static 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 82static 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 155static 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 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 214#if defined(CONFIG_DFU_TFTP) || defined(CONFIG_UPDATE_TFTP) 215int 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 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"); 260 return 1; 261 } 262 263got_update_file: 264 fit = map_sysmem(addr, 0); 265 266 if (fit_check_format((void *)fit, IMAGE_SIZE_INVAL)) { 267 printf("Bad FIT format of the update file, aborting " 268 "auto-update\n"); 269 return 1; 270 } 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 281 fit_image_name = (char *)fit_get_name(fit, noffset, NULL); 282 printf("Processing update '%s' :", fit_image_name); 283 284 if (!fit_image_verify(fit, noffset)) { 285 printf("Error: invalid update hash, aborting\n"); 286 ret = 1; 287 goto next_node; 288 } 289 290 printf("\n"); 291 if (update_fit_getparams(fit, noffset, &update_addr, 292 &update_fladdr, &update_size)) { 293 printf("Error: can't get update parameters, aborting\n"); 294 ret = 1; 295 goto next_node; 296 } 297 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)) { 307 ret = dfu_write_by_name(fit_image_name, 308 (void *)update_addr, 309 update_size, interface, 310 devstring); 311 if (ret) 312 return ret; 313 } 314next_node: 315 noffset = fdt_next_node(fit, noffset, &ndepth); 316 } 317 318 return ret; 319} 320#endif /* CONFIG_DFU_UPDATE || CONFIG_UPDATE_TFTP */ 321 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 343 if (fit_check_format((void *)fit, IMAGE_SIZE_INVAL)) { 344 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 */