"Das U-Boot" Source Tree
at master 678 lines 16 kB view raw
1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * (C) Copyright 2000 4 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. 5 */ 6 7#define LOG_CATEGORY UCLASS_ETH 8 9/* 10 * Boot support 11 */ 12#include <bootstage.h> 13#include <command.h> 14#include <dm.h> 15#include <dm/devres.h> 16#include <env.h> 17#include <image.h> 18#include <log.h> 19#include <net.h> 20#include <net6.h> 21#include <net/udp.h> 22#include <net/sntp.h> 23#include <net/ncsi.h> 24 25static int netboot_common(enum proto_t, struct cmd_tbl *, int, char * const []); 26 27#ifdef CONFIG_CMD_BOOTP 28static int do_bootp(struct cmd_tbl *cmdtp, int flag, int argc, 29 char *const argv[]) 30{ 31 return netboot_common(BOOTP, cmdtp, argc, argv); 32} 33 34U_BOOT_CMD( 35 bootp, 3, 1, do_bootp, 36 "boot image via network using BOOTP/TFTP protocol", 37 "[loadAddress] [[hostIPaddr:]bootfilename]" 38); 39#endif 40 41#ifdef CONFIG_CMD_TFTPBOOT 42int do_tftpb(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) 43{ 44 int ret; 45 46 bootstage_mark_name(BOOTSTAGE_KERNELREAD_START, "tftp_start"); 47 ret = netboot_common(TFTPGET, cmdtp, argc, argv); 48 bootstage_mark_name(BOOTSTAGE_KERNELREAD_STOP, "tftp_done"); 49 return ret; 50} 51 52#if IS_ENABLED(CONFIG_IPV6) 53U_BOOT_CMD( 54 tftpboot, 4, 1, do_tftpb, 55 "boot image via network using TFTP protocol\n" 56 "To use IPv6 add -ipv6 parameter or use IPv6 hostIPaddr framed " 57 "with [] brackets", 58 "[loadAddress] [[hostIPaddr:]bootfilename] [" USE_IP6_CMD_PARAM "]" 59); 60#else 61U_BOOT_CMD( 62 tftpboot, 3, 1, do_tftpb, 63 "load file via network using TFTP protocol", 64 "[loadAddress] [[hostIPaddr:]bootfilename]" 65); 66#endif 67#endif 68 69#ifdef CONFIG_CMD_TFTPPUT 70static int do_tftpput(struct cmd_tbl *cmdtp, int flag, int argc, 71 char *const argv[]) 72{ 73 return netboot_common(TFTPPUT, cmdtp, argc, argv); 74} 75 76U_BOOT_CMD( 77 tftpput, 4, 1, do_tftpput, 78 "TFTP put command, for uploading files to a server", 79 "Address Size [[hostIPaddr:]filename]" 80); 81#endif 82 83#ifdef CONFIG_CMD_TFTPSRV 84static int do_tftpsrv(struct cmd_tbl *cmdtp, int flag, int argc, 85 char *const argv[]) 86{ 87 return netboot_common(TFTPSRV, cmdtp, argc, argv); 88} 89 90U_BOOT_CMD( 91 tftpsrv, 2, 1, do_tftpsrv, 92 "act as a TFTP server and boot the first received file", 93 "[loadAddress]\n" 94 "Listen for an incoming TFTP transfer, receive a file and boot it.\n" 95 "The transfer is aborted if a transfer has not been started after\n" 96 "about 50 seconds or if Ctrl-C is pressed." 97); 98#endif 99 100#ifdef CONFIG_CMD_RARP 101int do_rarpb(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) 102{ 103 return netboot_common(RARP, cmdtp, argc, argv); 104} 105 106U_BOOT_CMD( 107 rarpboot, 3, 1, do_rarpb, 108 "boot image via network using RARP/TFTP protocol", 109 "[loadAddress] [[hostIPaddr:]bootfilename]" 110); 111#endif 112 113#if defined(CONFIG_CMD_DHCP6) 114static int do_dhcp6(struct cmd_tbl *cmdtp, int flag, int argc, 115 char *const argv[]) 116{ 117 int i; 118 int dhcp_argc; 119 char *dhcp_argv[] = {NULL, NULL, NULL, NULL}; 120 121 /* Add -ipv6 flag for autoload */ 122 for (i = 0; i < argc; i++) 123 dhcp_argv[i] = argv[i]; 124 dhcp_argc = argc + 1; 125 dhcp_argv[dhcp_argc - 1] = USE_IP6_CMD_PARAM; 126 127 return netboot_common(DHCP6, cmdtp, dhcp_argc, dhcp_argv); 128} 129 130U_BOOT_CMD(dhcp6, 3, 1, do_dhcp6, 131 "boot image via network using DHCPv6/TFTP protocol.\n" 132 "Use IPv6 hostIPaddr framed with [] brackets", 133 "[loadAddress] [[hostIPaddr:]bootfilename]"); 134#endif 135 136#if defined(CONFIG_CMD_DHCP) 137static int do_dhcp(struct cmd_tbl *cmdtp, int flag, int argc, 138 char *const argv[]) 139{ 140 return netboot_common(DHCP, cmdtp, argc, argv); 141} 142 143U_BOOT_CMD( 144 dhcp, 3, 1, do_dhcp, 145 "boot image via network using DHCP/TFTP protocol", 146 "[loadAddress] [[hostIPaddr:]bootfilename]" 147); 148 149int dhcp_run(ulong addr, const char *fname, bool autoload) 150{ 151 char *dhcp_argv[] = {"dhcp", NULL, (char *)fname, NULL}; 152 struct cmd_tbl cmdtp = {}; /* dummy */ 153 char file_addr[17]; 154 int old_autoload; 155 int ret, result; 156 157 log_debug("addr=%lx, fname=%s, autoload=%d\n", addr, fname, autoload); 158 old_autoload = env_get_yesno("autoload"); 159 ret = env_set("autoload", autoload ? "y" : "n"); 160 if (ret) 161 return log_msg_ret("en1", -EINVAL); 162 163 if (autoload) { 164 sprintf(file_addr, "%lx", addr); 165 dhcp_argv[1] = file_addr; 166 } 167 168 result = do_dhcp(&cmdtp, 0, !autoload ? 1 : fname ? 3 : 2, dhcp_argv); 169 170 ret = env_set("autoload", old_autoload == -1 ? NULL : 171 old_autoload ? "y" : "n"); 172 if (ret) 173 return log_msg_ret("en2", -EINVAL); 174 175 if (result) 176 return log_msg_ret("res", -ENOENT); 177 178 return 0; 179} 180#endif 181 182#if defined(CONFIG_CMD_NFS) 183static int do_nfs(struct cmd_tbl *cmdtp, int flag, int argc, 184 char *const argv[]) 185{ 186 return netboot_common(NFS, cmdtp, argc, argv); 187} 188 189U_BOOT_CMD( 190 nfs, 3, 1, do_nfs, 191 "boot image via network using NFS protocol", 192 "[loadAddress] [[hostIPaddr:]bootfilename]" 193); 194#endif 195 196#if defined(CONFIG_CMD_WGET) 197static int do_wget(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[]) 198{ 199 wget_info = &default_wget_info; 200 201 return netboot_common(WGET, cmdtp, argc, argv); 202} 203 204U_BOOT_CMD( 205 wget, 3, 1, do_wget, 206 "boot image via network using HTTP protocol", 207 "[loadAddress] [[hostIPaddr:]path and image name]" 208); 209#endif 210 211static void netboot_update_env(void) 212{ 213 char tmp[46]; 214 215 if (net_gateway.s_addr) { 216 ip_to_string(net_gateway, tmp); 217 env_set("gatewayip", tmp); 218 } 219 220 if (net_netmask.s_addr) { 221 ip_to_string(net_netmask, tmp); 222 env_set("netmask", tmp); 223 } 224 225#ifdef CONFIG_CMD_BOOTP 226 if (net_hostname[0]) 227 env_set("hostname", net_hostname); 228#endif 229 230#ifdef CONFIG_CMD_BOOTP 231 if (net_root_path[0]) 232 env_set("rootpath", net_root_path); 233#endif 234 235 if (net_ip.s_addr) { 236 ip_to_string(net_ip, tmp); 237 env_set("ipaddr", tmp); 238 } 239 /* 240 * Only attempt to change serverip if net/bootp.c:store_net_params() 241 * could have set it 242 */ 243 if (!IS_ENABLED(CONFIG_BOOTP_SERVERIP) && net_server_ip.s_addr) { 244 ip_to_string(net_server_ip, tmp); 245 env_set("serverip", tmp); 246 } 247 if (net_dns_server.s_addr) { 248 ip_to_string(net_dns_server, tmp); 249 env_set("dnsip", tmp); 250 } 251#if defined(CONFIG_BOOTP_DNS2) 252 if (net_dns_server2.s_addr) { 253 ip_to_string(net_dns_server2, tmp); 254 env_set("dnsip2", tmp); 255 } 256#endif 257#ifdef CONFIG_CMD_BOOTP 258 if (net_nis_domain[0]) 259 env_set("domain", net_nis_domain); 260#endif 261 262#if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_TIMEOFFSET) 263 if (net_ntp_time_offset) { 264 sprintf(tmp, "%d", net_ntp_time_offset); 265 env_set("timeoffset", tmp); 266 } 267#endif 268#if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_NTPSERVER) 269 if (net_ntp_server.s_addr) { 270 ip_to_string(net_ntp_server, tmp); 271 env_set("ntpserverip", tmp); 272 } 273#endif 274 275 if (IS_ENABLED(CONFIG_IPV6)) { 276 if (!ip6_is_unspecified_addr(&net_ip6) || 277 net_prefix_length != 0) { 278 if (net_prefix_length != 0) 279 snprintf(tmp, sizeof(tmp), "%pI6c/%d", &net_ip6, net_prefix_length); 280 else 281 snprintf(tmp, sizeof(tmp), "%pI6c", &net_ip6); 282 env_set("ip6addr", tmp); 283 } 284 285 if (!ip6_is_unspecified_addr(&net_server_ip6)) { 286 snprintf(tmp, sizeof(tmp), "%pI6c", &net_server_ip6); 287 env_set("serverip6", tmp); 288 } 289 290 if (!ip6_is_unspecified_addr(&net_gateway6)) { 291 snprintf(tmp, sizeof(tmp), "%pI6c", &net_gateway6); 292 env_set("gatewayip6", tmp); 293 } 294 } 295} 296 297/** 298 * parse_addr_size() - parse address and size arguments for tftpput 299 * 300 * @argv: command line arguments 301 * Return: 0 on success 302 */ 303static int parse_addr_size(char * const argv[]) 304{ 305 if (strict_strtoul(argv[1], 16, &image_save_addr) < 0 || 306 strict_strtoul(argv[2], 16, &image_save_size) < 0) { 307 printf("Invalid address/size\n"); 308 return CMD_RET_USAGE; 309 } 310 return 0; 311} 312 313/** 314 * parse_args() - parse command line arguments 315 * 316 * @proto: command prototype 317 * @argc: number of arguments 318 * @argv: command line arguments 319 * Return: 0 on success 320 */ 321static int parse_args(enum proto_t proto, int argc, char *const argv[]) 322{ 323 ulong addr; 324 char *end; 325 326 switch (argc) { 327 case 1: 328 if (IS_ENABLED(CONFIG_CMD_TFTPPUT) && proto == TFTPPUT) 329 return 1; 330 331 /* refresh bootfile name from env */ 332 copy_filename(net_boot_file_name, env_get("bootfile"), 333 sizeof(net_boot_file_name)); 334 break; 335 336 case 2: 337 if (IS_ENABLED(CONFIG_CMD_TFTPPUT) && proto == TFTPPUT) 338 return 1; 339 /* 340 * Only one arg - accept two forms: 341 * Just load address, or just boot file name. The latter 342 * form must be written in a format which can not be 343 * mis-interpreted as a valid number. 344 */ 345 addr = hextoul(argv[1], &end); 346 if (end == (argv[1] + strlen(argv[1]))) { 347 image_load_addr = addr; 348 /* refresh bootfile name from env */ 349 copy_filename(net_boot_file_name, env_get("bootfile"), 350 sizeof(net_boot_file_name)); 351 } else { 352 net_boot_file_name_explicit = true; 353 copy_filename(net_boot_file_name, argv[1], 354 sizeof(net_boot_file_name)); 355 } 356 break; 357 358 case 3: 359 if (IS_ENABLED(CONFIG_CMD_TFTPPUT) && proto == TFTPPUT) { 360 if (parse_addr_size(argv)) 361 return 1; 362 } else { 363 image_load_addr = hextoul(argv[1], NULL); 364 net_boot_file_name_explicit = true; 365 copy_filename(net_boot_file_name, argv[2], 366 sizeof(net_boot_file_name)); 367 } 368 break; 369 370#ifdef CONFIG_CMD_TFTPPUT 371 case 4: 372 if (parse_addr_size(argv)) 373 return 1; 374 net_boot_file_name_explicit = true; 375 copy_filename(net_boot_file_name, argv[3], 376 sizeof(net_boot_file_name)); 377 break; 378#endif 379 default: 380 return 1; 381 } 382 return 0; 383} 384 385static int netboot_common(enum proto_t proto, struct cmd_tbl *cmdtp, int argc, 386 char *const argv[]) 387{ 388 char *s; 389 int rcode = 0; 390 int size; 391 392 net_boot_file_name_explicit = false; 393 *net_boot_file_name = '\0'; 394 395 /* pre-set image_load_addr */ 396 s = env_get("loadaddr"); 397 if (s != NULL) 398 image_load_addr = hextoul(s, NULL); 399 400 if (IS_ENABLED(CONFIG_IPV6)) { 401 use_ip6 = false; 402 403 /* IPv6 parameter has to be always *last* */ 404 if (!strcmp(argv[argc - 1], USE_IP6_CMD_PARAM)) { 405 use_ip6 = true; 406 /* It is a hack not to break switch/case code */ 407 --argc; 408 } 409 } 410 411 if (parse_args(proto, argc, argv)) { 412 bootstage_error(BOOTSTAGE_ID_NET_START); 413 return CMD_RET_USAGE; 414 } 415 416 bootstage_mark(BOOTSTAGE_ID_NET_START); 417 418 if (IS_ENABLED(CONFIG_IPV6) && !use_ip6) { 419 char *s, *e; 420 size_t len; 421 422 s = strchr(net_boot_file_name, '['); 423 e = strchr(net_boot_file_name, ']'); 424 if (s && e) { 425 len = e - s; 426 if (!string_to_ip6(s + 1, len - 1, &net_server_ip6)) 427 use_ip6 = true; 428 } 429 } 430 431 size = net_loop(proto); 432 if (size < 0) { 433 bootstage_error(BOOTSTAGE_ID_NET_NETLOOP_OK); 434 return CMD_RET_FAILURE; 435 } 436 bootstage_mark(BOOTSTAGE_ID_NET_NETLOOP_OK); 437 438 /* net_loop ok, update environment */ 439 netboot_update_env(); 440 441 /* done if no file was loaded (no errors though) */ 442 if (size == 0) { 443 bootstage_error(BOOTSTAGE_ID_NET_LOADED); 444 return CMD_RET_SUCCESS; 445 } 446 447 bootstage_mark(BOOTSTAGE_ID_NET_LOADED); 448 449 rcode = bootm_maybe_autostart(cmdtp, argv[0]); 450 451 if (rcode == CMD_RET_SUCCESS) 452 bootstage_mark(BOOTSTAGE_ID_NET_DONE); 453 else 454 bootstage_error(BOOTSTAGE_ID_NET_DONE_ERR); 455 return rcode; 456} 457 458#if defined(CONFIG_CMD_PING) 459static int do_ping(struct cmd_tbl *cmdtp, int flag, int argc, 460 char *const argv[]) 461{ 462 if (argc < 2) 463 return CMD_RET_USAGE; 464 465 net_ping_ip = string_to_ip(argv[1]); 466 if (net_ping_ip.s_addr == 0) 467 return CMD_RET_USAGE; 468 469 if (net_loop(PING) < 0) { 470 printf("ping failed; host %s is not alive\n", argv[1]); 471 return CMD_RET_FAILURE; 472 } 473 474 printf("host %s is alive\n", argv[1]); 475 476 return CMD_RET_SUCCESS; 477} 478 479U_BOOT_CMD( 480 ping, 2, 1, do_ping, 481 "send ICMP ECHO_REQUEST to network host", 482 "pingAddress" 483); 484#endif 485 486#if IS_ENABLED(CONFIG_CMD_PING6) 487int do_ping6(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[]) 488{ 489 if (string_to_ip6(argv[1], strlen(argv[1]), &net_ping_ip6)) 490 return CMD_RET_USAGE; 491 492 use_ip6 = true; 493 if (net_loop(PING6) < 0) { 494 use_ip6 = false; 495 printf("ping6 failed; host %pI6c is not alive\n", 496 &net_ping_ip6); 497 return 1; 498 } 499 500 use_ip6 = false; 501 printf("host %pI6c is alive\n", &net_ping_ip6); 502 return 0; 503} 504 505U_BOOT_CMD( 506 ping6, 2, 1, do_ping6, 507 "send ICMPv6 ECHO_REQUEST to network host", 508 "pingAddress" 509); 510#endif /* CONFIG_CMD_PING6 */ 511 512#if defined(CONFIG_CMD_CDP) 513 514static void cdp_update_env(void) 515{ 516 char tmp[16]; 517 518 if (cdp_appliance_vlan != htons(-1)) { 519 printf("CDP offered appliance VLAN %d\n", 520 ntohs(cdp_appliance_vlan)); 521 vlan_to_string(cdp_appliance_vlan, tmp); 522 env_set("vlan", tmp); 523 net_our_vlan = cdp_appliance_vlan; 524 } 525 526 if (cdp_native_vlan != htons(-1)) { 527 printf("CDP offered native VLAN %d\n", ntohs(cdp_native_vlan)); 528 vlan_to_string(cdp_native_vlan, tmp); 529 env_set("nvlan", tmp); 530 net_native_vlan = cdp_native_vlan; 531 } 532} 533 534int do_cdp(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) 535{ 536 int r; 537 538 r = net_loop(CDP); 539 if (r < 0) { 540 printf("cdp failed; perhaps not a CISCO switch?\n"); 541 return CMD_RET_FAILURE; 542 } 543 544 cdp_update_env(); 545 546 return CMD_RET_SUCCESS; 547} 548 549U_BOOT_CMD( 550 cdp, 1, 1, do_cdp, 551 "Perform CDP network configuration", 552 "\n" 553); 554#endif 555 556#if defined(CONFIG_CMD_SNTP) 557static struct udp_ops sntp_ops = { 558 .prereq = sntp_prereq, 559 .start = sntp_start, 560 .data = NULL, 561}; 562 563int do_sntp(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) 564{ 565 char *toff; 566 567 if (argc < 2) { 568 net_ntp_server = env_get_ip("ntpserverip"); 569 if (net_ntp_server.s_addr == 0) { 570 printf("ntpserverip not set\n"); 571 return CMD_RET_FAILURE; 572 } 573 } else { 574 net_ntp_server = string_to_ip(argv[1]); 575 if (net_ntp_server.s_addr == 0) { 576 printf("Bad NTP server IP address\n"); 577 return CMD_RET_FAILURE; 578 } 579 } 580 581 toff = env_get("timeoffset"); 582 if (toff == NULL) 583 net_ntp_time_offset = 0; 584 else 585 net_ntp_time_offset = simple_strtol(toff, NULL, 10); 586 587 if (udp_loop(&sntp_ops) < 0) { 588 printf("SNTP failed: host %pI4 not responding\n", 589 &net_ntp_server); 590 return CMD_RET_FAILURE; 591 } 592 593 return CMD_RET_SUCCESS; 594} 595 596U_BOOT_CMD( 597 sntp, 2, 1, do_sntp, 598 "synchronize RTC via network", 599 "[NTP server IP]\n" 600); 601#endif 602 603#if defined(CONFIG_CMD_DNS) 604int do_dns(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) 605{ 606 if (argc == 1) 607 return CMD_RET_USAGE; 608 609 /* 610 * We should check for a valid hostname: 611 * - Each label must be between 1 and 63 characters long 612 * - the entire hostname has a maximum of 255 characters 613 * - only the ASCII letters 'a' through 'z' (case-insensitive), 614 * the digits '0' through '9', and the hyphen 615 * - cannot begin or end with a hyphen 616 * - no other symbols, punctuation characters, or blank spaces are 617 * permitted 618 * but hey - this is a minimalist implmentation, so only check length 619 * and let the name server deal with things. 620 */ 621 if (strlen(argv[1]) >= 255) { 622 printf("dns error: hostname too long\n"); 623 return CMD_RET_FAILURE; 624 } 625 626 net_dns_resolve = argv[1]; 627 628 if (argc == 3) 629 net_dns_env_var = argv[2]; 630 else 631 net_dns_env_var = NULL; 632 633 if (net_loop(DNS) < 0) { 634 printf("dns lookup of %s failed, check setup\n", argv[1]); 635 return CMD_RET_FAILURE; 636 } 637 638 return CMD_RET_SUCCESS; 639} 640 641U_BOOT_CMD( 642 dns, 3, 1, do_dns, 643 "lookup the IP of a hostname", 644 "hostname [envvar]" 645); 646 647#endif /* CONFIG_CMD_DNS */ 648 649#if defined(CONFIG_CMD_LINK_LOCAL) 650static int do_link_local(struct cmd_tbl *cmdtp, int flag, int argc, 651 char *const argv[]) 652{ 653 char tmp[22]; 654 655 if (net_loop(LINKLOCAL) < 0) 656 return CMD_RET_FAILURE; 657 658 net_gateway.s_addr = 0; 659 ip_to_string(net_gateway, tmp); 660 env_set("gatewayip", tmp); 661 662 ip_to_string(net_netmask, tmp); 663 env_set("netmask", tmp); 664 665 ip_to_string(net_ip, tmp); 666 env_set("ipaddr", tmp); 667 env_set("llipaddr", tmp); /* store this for next time */ 668 669 return CMD_RET_SUCCESS; 670} 671 672U_BOOT_CMD( 673 linklocal, 1, 1, do_link_local, 674 "acquire a network IP address using the link-local protocol", 675 "" 676); 677 678#endif /* CONFIG_CMD_LINK_LOCAL */