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 <common.h> 13#include <bootstage.h> 14#include <command.h> 15#include <dm.h> 16#include <dm/devres.h> 17#include <env.h> 18#include <image.h> 19#include <log.h> 20#include <net.h> 21#include <net6.h> 22#include <net/udp.h> 23#include <net/sntp.h> 24#include <net/ncsi.h> 25 26static int netboot_common(enum proto_t, struct cmd_tbl *, int, char * const []); 27 28#ifdef CONFIG_CMD_BOOTP 29static int do_bootp(struct cmd_tbl *cmdtp, int flag, int argc, 30 char *const argv[]) 31{ 32 return netboot_common(BOOTP, cmdtp, argc, argv); 33} 34 35U_BOOT_CMD( 36 bootp, 3, 1, do_bootp, 37 "boot image via network using BOOTP/TFTP protocol", 38 "[loadAddress] [[hostIPaddr:]bootfilename]" 39); 40#endif 41 42#ifdef CONFIG_CMD_TFTPBOOT 43int do_tftpb(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) 44{ 45 int ret; 46 47 bootstage_mark_name(BOOTSTAGE_KERNELREAD_START, "tftp_start"); 48 ret = netboot_common(TFTPGET, cmdtp, argc, argv); 49 bootstage_mark_name(BOOTSTAGE_KERNELREAD_STOP, "tftp_done"); 50 return ret; 51} 52 53#if IS_ENABLED(CONFIG_IPV6) 54U_BOOT_CMD( 55 tftpboot, 4, 1, do_tftpb, 56 "boot image via network using TFTP protocol\n" 57 "To use IPv6 add -ipv6 parameter or use IPv6 hostIPaddr framed " 58 "with [] brackets", 59 "[loadAddress] [[hostIPaddr:]bootfilename] [" USE_IP6_CMD_PARAM "]" 60); 61#else 62U_BOOT_CMD( 63 tftpboot, 3, 1, do_tftpb, 64 "load file via network using TFTP protocol", 65 "[loadAddress] [[hostIPaddr:]bootfilename]" 66); 67#endif 68#endif 69 70#ifdef CONFIG_CMD_TFTPPUT 71static int do_tftpput(struct cmd_tbl *cmdtp, int flag, int argc, 72 char *const argv[]) 73{ 74 return netboot_common(TFTPPUT, cmdtp, argc, argv); 75} 76 77U_BOOT_CMD( 78 tftpput, 4, 1, do_tftpput, 79 "TFTP put command, for uploading files to a server", 80 "Address Size [[hostIPaddr:]filename]" 81); 82#endif 83 84#ifdef CONFIG_CMD_TFTPSRV 85static int do_tftpsrv(struct cmd_tbl *cmdtp, int flag, int argc, 86 char *const argv[]) 87{ 88 return netboot_common(TFTPSRV, cmdtp, argc, argv); 89} 90 91U_BOOT_CMD( 92 tftpsrv, 2, 1, do_tftpsrv, 93 "act as a TFTP server and boot the first received file", 94 "[loadAddress]\n" 95 "Listen for an incoming TFTP transfer, receive a file and boot it.\n" 96 "The transfer is aborted if a transfer has not been started after\n" 97 "about 50 seconds or if Ctrl-C is pressed." 98); 99#endif 100 101 102#ifdef CONFIG_CMD_RARP 103int do_rarpb(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) 104{ 105 return netboot_common(RARP, cmdtp, argc, argv); 106} 107 108U_BOOT_CMD( 109 rarpboot, 3, 1, do_rarpb, 110 "boot image via network using RARP/TFTP protocol", 111 "[loadAddress] [[hostIPaddr:]bootfilename]" 112); 113#endif 114 115#if defined(CONFIG_CMD_DHCP6) 116static int do_dhcp6(struct cmd_tbl *cmdtp, int flag, int argc, 117 char *const argv[]) 118{ 119 int i; 120 int dhcp_argc; 121 char *dhcp_argv[] = {NULL, NULL, NULL, NULL}; 122 123 /* Add -ipv6 flag for autoload */ 124 for (i = 0; i < argc; i++) 125 dhcp_argv[i] = argv[i]; 126 dhcp_argc = argc + 1; 127 dhcp_argv[dhcp_argc - 1] = USE_IP6_CMD_PARAM; 128 129 return netboot_common(DHCP6, cmdtp, dhcp_argc, dhcp_argv); 130} 131 132U_BOOT_CMD(dhcp6, 3, 1, do_dhcp6, 133 "boot image via network using DHCPv6/TFTP protocol.\n" 134 "Use IPv6 hostIPaddr framed with [] brackets", 135 "[loadAddress] [[hostIPaddr:]bootfilename]"); 136#endif 137 138#if defined(CONFIG_CMD_DHCP) 139static int do_dhcp(struct cmd_tbl *cmdtp, int flag, int argc, 140 char *const argv[]) 141{ 142 return netboot_common(DHCP, cmdtp, argc, argv); 143} 144 145U_BOOT_CMD( 146 dhcp, 3, 1, do_dhcp, 147 "boot image via network using DHCP/TFTP protocol", 148 "[loadAddress] [[hostIPaddr:]bootfilename]" 149); 150 151int dhcp_run(ulong addr, const char *fname, bool autoload) 152{ 153 char *dhcp_argv[] = {"dhcp", NULL, (char *)fname, NULL}; 154 struct cmd_tbl cmdtp = {}; /* dummy */ 155 char file_addr[17]; 156 int old_autoload; 157 int ret, result; 158 159 log_debug("addr=%lx, fname=%s, autoload=%d\n", addr, fname, autoload); 160 old_autoload = env_get_yesno("autoload"); 161 ret = env_set("autoload", autoload ? "y" : "n"); 162 if (ret) 163 return log_msg_ret("en1", -EINVAL); 164 165 if (autoload) { 166 sprintf(file_addr, "%lx", addr); 167 dhcp_argv[1] = file_addr; 168 } 169 170 result = do_dhcp(&cmdtp, 0, !autoload ? 1 : fname ? 3 : 2, dhcp_argv); 171 172 ret = env_set("autoload", old_autoload == -1 ? NULL : 173 old_autoload ? "y" : "n"); 174 if (ret) 175 return log_msg_ret("en2", -EINVAL); 176 177 if (result) 178 return log_msg_ret("res", -ENOENT); 179 180 return 0; 181} 182#endif 183 184#if defined(CONFIG_CMD_NFS) 185static int do_nfs(struct cmd_tbl *cmdtp, int flag, int argc, 186 char *const argv[]) 187{ 188 return netboot_common(NFS, cmdtp, argc, argv); 189} 190 191U_BOOT_CMD( 192 nfs, 3, 1, do_nfs, 193 "boot image via network using NFS protocol", 194 "[loadAddress] [[hostIPaddr:]bootfilename]" 195); 196#endif 197 198#if defined(CONFIG_CMD_WGET) 199static int do_wget(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[]) 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 */ 679 680static int do_net_list(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) 681{ 682 const struct udevice *current = eth_get_dev(); 683 unsigned char env_enetaddr[ARP_HLEN]; 684 const struct udevice *dev; 685 struct uclass *uc; 686 687 uclass_id_foreach_dev(UCLASS_ETH, dev, uc) { 688 eth_env_get_enetaddr_by_index("eth", dev_seq(dev), env_enetaddr); 689 printf("eth%d : %s %pM %s\n", dev_seq(dev), dev->name, env_enetaddr, 690 current == dev ? "active" : ""); 691 } 692 return CMD_RET_SUCCESS; 693} 694 695static int do_net_stats(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) 696{ 697 int nstats, err, i, off; 698 struct udevice *dev; 699 u64 *values; 700 u8 *strings; 701 702 if (argc < 2) 703 return CMD_RET_USAGE; 704 705 err = uclass_get_device_by_name(UCLASS_ETH, argv[1], &dev); 706 if (err) { 707 printf("Could not find device %s\n", argv[1]); 708 return CMD_RET_FAILURE; 709 } 710 711 if (!eth_get_ops(dev)->get_sset_count || 712 !eth_get_ops(dev)->get_strings || 713 !eth_get_ops(dev)->get_stats) { 714 printf("Driver does not implement stats dump!\n"); 715 return CMD_RET_FAILURE; 716 } 717 718 nstats = eth_get_ops(dev)->get_sset_count(dev); 719 strings = kcalloc(nstats, ETH_GSTRING_LEN, GFP_KERNEL); 720 if (!strings) 721 return CMD_RET_FAILURE; 722 723 values = kcalloc(nstats, sizeof(u64), GFP_KERNEL); 724 if (!values) 725 goto err_free_strings; 726 727 eth_get_ops(dev)->get_strings(dev, strings); 728 eth_get_ops(dev)->get_stats(dev, values); 729 730 off = 0; 731 for (i = 0; i < nstats; i++) { 732 printf(" %s: %llu\n", &strings[off], values[i]); 733 off += ETH_GSTRING_LEN; 734 }; 735 736 return CMD_RET_SUCCESS; 737 738err_free_strings: 739 kfree(strings); 740 741 return CMD_RET_FAILURE; 742} 743 744static struct cmd_tbl cmd_net[] = { 745 U_BOOT_CMD_MKENT(list, 1, 0, do_net_list, "", ""), 746 U_BOOT_CMD_MKENT(stats, 2, 0, do_net_stats, "", ""), 747}; 748 749static int do_net(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) 750{ 751 struct cmd_tbl *cp; 752 753 cp = find_cmd_tbl(argv[1], cmd_net, ARRAY_SIZE(cmd_net)); 754 755 /* Drop the net command */ 756 argc--; 757 argv++; 758 759 if (!cp || argc > cp->maxargs) 760 return CMD_RET_USAGE; 761 if (flag == CMD_FLAG_REPEAT && !cmd_is_repeatable(cp)) 762 return CMD_RET_SUCCESS; 763 764 return cp->cmd(cmdtp, flag, argc, argv); 765} 766 767U_BOOT_CMD( 768 net, 3, 1, do_net, 769 "NET sub-system", 770 "list - list available devices\n" 771 "stats <device> - dump statistics for specified device\n" 772); 773 774#if defined(CONFIG_CMD_NCSI) 775static int do_ncsi(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[]) 776{ 777 if (!phy_interface_is_ncsi() || !ncsi_active()) { 778 printf("Device not configured for NC-SI\n"); 779 return CMD_RET_FAILURE; 780 } 781 782 if (net_loop(NCSI) < 0) 783 return CMD_RET_FAILURE; 784 785 return CMD_RET_SUCCESS; 786} 787 788U_BOOT_CMD( 789 ncsi, 1, 1, do_ncsi, 790 "Configure attached NIC via NC-SI", 791 "" 792); 793#endif /* CONFIG_CMD_NCSI */ 794