1 // SPDX-License-Identifier: GPL-2.0+
4 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
7 #define LOG_CATEGORY UCLASS_ETH
13 #include <bootstage.h>
16 #include <dm/devres.h>
26 static int netboot_common(enum proto_t
, struct cmd_tbl
*, int, char * const []);
28 #ifdef CONFIG_CMD_BOOTP
29 static int do_bootp(struct cmd_tbl
*cmdtp
, int flag
, int argc
,
32 return netboot_common(BOOTP
, cmdtp
, argc
, argv
);
36 bootp
, 3, 1, do_bootp
,
37 "boot image via network using BOOTP/TFTP protocol",
38 "[loadAddress] [[hostIPaddr:]bootfilename]"
42 #ifdef CONFIG_CMD_TFTPBOOT
43 int do_tftpb(struct cmd_tbl
*cmdtp
, int flag
, int argc
, char *const argv
[])
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");
53 #if IS_ENABLED(CONFIG_IPV6)
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 "
59 "[loadAddress] [[hostIPaddr:]bootfilename] [" USE_IP6_CMD_PARAM
"]"
63 tftpboot
, 3, 1, do_tftpb
,
64 "load file via network using TFTP protocol",
65 "[loadAddress] [[hostIPaddr:]bootfilename]"
70 #ifdef CONFIG_CMD_TFTPPUT
71 static int do_tftpput(struct cmd_tbl
*cmdtp
, int flag
, int argc
,
74 return netboot_common(TFTPPUT
, cmdtp
, argc
, argv
);
78 tftpput
, 4, 1, do_tftpput
,
79 "TFTP put command, for uploading files to a server",
80 "Address Size [[hostIPaddr:]filename]"
84 #ifdef CONFIG_CMD_TFTPSRV
85 static int do_tftpsrv(struct cmd_tbl
*cmdtp
, int flag
, int argc
,
88 return netboot_common(TFTPSRV
, cmdtp
, argc
, argv
);
92 tftpsrv
, 2, 1, do_tftpsrv
,
93 "act as a TFTP server and boot the first received file",
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."
102 #ifdef CONFIG_CMD_RARP
103 int do_rarpb(struct cmd_tbl
*cmdtp
, int flag
, int argc
, char *const argv
[])
105 return netboot_common(RARP
, cmdtp
, argc
, argv
);
109 rarpboot
, 3, 1, do_rarpb
,
110 "boot image via network using RARP/TFTP protocol",
111 "[loadAddress] [[hostIPaddr:]bootfilename]"
115 #if defined(CONFIG_CMD_DHCP6)
116 static int do_dhcp6(struct cmd_tbl
*cmdtp
, int flag
, int argc
,
121 char *dhcp_argv
[] = {NULL
, NULL
, NULL
, NULL
};
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
;
129 return netboot_common(DHCP6
, cmdtp
, dhcp_argc
, dhcp_argv
);
132 U_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]");
138 #if defined(CONFIG_CMD_DHCP)
139 static int do_dhcp(struct cmd_tbl
*cmdtp
, int flag
, int argc
,
142 return netboot_common(DHCP
, cmdtp
, argc
, argv
);
147 "boot image via network using DHCP/TFTP protocol",
148 "[loadAddress] [[hostIPaddr:]bootfilename]"
151 int dhcp_run(ulong addr
, const char *fname
, bool autoload
)
153 char *dhcp_argv
[] = {"dhcp", NULL
, (char *)fname
, NULL
};
154 struct cmd_tbl cmdtp
= {}; /* dummy */
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");
163 return log_msg_ret("en1", -EINVAL
);
166 sprintf(file_addr
, "%lx", addr
);
167 dhcp_argv
[1] = file_addr
;
170 result
= do_dhcp(&cmdtp
, 0, !autoload
? 1 : fname
? 3 : 2, dhcp_argv
);
172 ret
= env_set("autoload", old_autoload
== -1 ? NULL
:
173 old_autoload
? "y" : "n");
175 return log_msg_ret("en2", -EINVAL
);
178 return log_msg_ret("res", -ENOENT
);
184 #if defined(CONFIG_CMD_NFS)
185 static int do_nfs(struct cmd_tbl
*cmdtp
, int flag
, int argc
,
188 return netboot_common(NFS
, cmdtp
, argc
, argv
);
193 "boot image via network using NFS protocol",
194 "[loadAddress] [[hostIPaddr:]bootfilename]"
198 #if defined(CONFIG_CMD_WGET)
199 static int do_wget(struct cmd_tbl
*cmdtp
, int flag
, int argc
, char * const argv
[])
201 return netboot_common(WGET
, cmdtp
, argc
, argv
);
206 "boot image via network using HTTP protocol",
207 "[loadAddress] [[hostIPaddr:]path and image name]"
211 static void netboot_update_env(void)
215 if (net_gateway
.s_addr
) {
216 ip_to_string(net_gateway
, tmp
);
217 env_set("gatewayip", tmp
);
220 if (net_netmask
.s_addr
) {
221 ip_to_string(net_netmask
, tmp
);
222 env_set("netmask", tmp
);
225 #ifdef CONFIG_CMD_BOOTP
227 env_set("hostname", net_hostname
);
230 #ifdef CONFIG_CMD_BOOTP
231 if (net_root_path
[0])
232 env_set("rootpath", net_root_path
);
236 ip_to_string(net_ip
, tmp
);
237 env_set("ipaddr", tmp
);
240 * Only attempt to change serverip if net/bootp.c:store_net_params()
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
);
247 if (net_dns_server
.s_addr
) {
248 ip_to_string(net_dns_server
, tmp
);
249 env_set("dnsip", tmp
);
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
);
257 #ifdef CONFIG_CMD_BOOTP
258 if (net_nis_domain
[0])
259 env_set("domain", net_nis_domain
);
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
);
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
);
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
);
281 snprintf(tmp
, sizeof(tmp
), "%pI6c", &net_ip6
);
282 env_set("ip6addr", tmp
);
285 if (!ip6_is_unspecified_addr(&net_server_ip6
)) {
286 snprintf(tmp
, sizeof(tmp
), "%pI6c", &net_server_ip6
);
287 env_set("serverip6", tmp
);
290 if (!ip6_is_unspecified_addr(&net_gateway6
)) {
291 snprintf(tmp
, sizeof(tmp
), "%pI6c", &net_gateway6
);
292 env_set("gatewayip6", tmp
);
298 * parse_addr_size() - parse address and size arguments for tftpput
300 * @argv: command line arguments
301 * Return: 0 on success
303 static int parse_addr_size(char * const argv
[])
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
;
314 * parse_args() - parse command line arguments
316 * @proto: command prototype
317 * @argc: number of arguments
318 * @argv: command line arguments
319 * Return: 0 on success
321 static int parse_args(enum proto_t proto
, int argc
, char *const argv
[])
328 if (IS_ENABLED(CONFIG_CMD_TFTPPUT
) && proto
== TFTPPUT
)
331 /* refresh bootfile name from env */
332 copy_filename(net_boot_file_name
, env_get("bootfile"),
333 sizeof(net_boot_file_name
));
337 if (IS_ENABLED(CONFIG_CMD_TFTPPUT
) && proto
== TFTPPUT
)
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.
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
));
352 net_boot_file_name_explicit
= true;
353 copy_filename(net_boot_file_name
, argv
[1],
354 sizeof(net_boot_file_name
));
359 if (IS_ENABLED(CONFIG_CMD_TFTPPUT
) && proto
== TFTPPUT
) {
360 if (parse_addr_size(argv
))
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
));
370 #ifdef CONFIG_CMD_TFTPPUT
372 if (parse_addr_size(argv
))
374 net_boot_file_name_explicit
= true;
375 copy_filename(net_boot_file_name
, argv
[3],
376 sizeof(net_boot_file_name
));
385 static int netboot_common(enum proto_t proto
, struct cmd_tbl
*cmdtp
, int argc
,
392 net_boot_file_name_explicit
= false;
393 *net_boot_file_name
= '\0';
395 /* pre-set image_load_addr */
396 s
= env_get("loadaddr");
398 image_load_addr
= hextoul(s
, NULL
);
400 if (IS_ENABLED(CONFIG_IPV6
)) {
403 /* IPv6 parameter has to be always *last* */
404 if (!strcmp(argv
[argc
- 1], USE_IP6_CMD_PARAM
)) {
406 /* It is a hack not to break switch/case code */
411 if (parse_args(proto
, argc
, argv
)) {
412 bootstage_error(BOOTSTAGE_ID_NET_START
);
413 return CMD_RET_USAGE
;
416 bootstage_mark(BOOTSTAGE_ID_NET_START
);
418 if (IS_ENABLED(CONFIG_IPV6
) && !use_ip6
) {
422 s
= strchr(net_boot_file_name
, '[');
423 e
= strchr(net_boot_file_name
, ']');
426 if (!string_to_ip6(s
+ 1, len
- 1, &net_server_ip6
))
431 size
= net_loop(proto
);
433 bootstage_error(BOOTSTAGE_ID_NET_NETLOOP_OK
);
434 return CMD_RET_FAILURE
;
436 bootstage_mark(BOOTSTAGE_ID_NET_NETLOOP_OK
);
438 /* net_loop ok, update environment */
439 netboot_update_env();
441 /* done if no file was loaded (no errors though) */
443 bootstage_error(BOOTSTAGE_ID_NET_LOADED
);
444 return CMD_RET_SUCCESS
;
447 bootstage_mark(BOOTSTAGE_ID_NET_LOADED
);
449 rcode
= bootm_maybe_autostart(cmdtp
, argv
[0]);
451 if (rcode
== CMD_RET_SUCCESS
)
452 bootstage_mark(BOOTSTAGE_ID_NET_DONE
);
454 bootstage_error(BOOTSTAGE_ID_NET_DONE_ERR
);
458 #if defined(CONFIG_CMD_PING)
459 static int do_ping(struct cmd_tbl
*cmdtp
, int flag
, int argc
,
463 return CMD_RET_USAGE
;
465 net_ping_ip
= string_to_ip(argv
[1]);
466 if (net_ping_ip
.s_addr
== 0)
467 return CMD_RET_USAGE
;
469 if (net_loop(PING
) < 0) {
470 printf("ping failed; host %s is not alive\n", argv
[1]);
471 return CMD_RET_FAILURE
;
474 printf("host %s is alive\n", argv
[1]);
476 return CMD_RET_SUCCESS
;
481 "send ICMP ECHO_REQUEST to network host",
486 #if IS_ENABLED(CONFIG_CMD_PING6)
487 int do_ping6(struct cmd_tbl
*cmdtp
, int flag
, int argc
, char * const argv
[])
489 if (string_to_ip6(argv
[1], strlen(argv
[1]), &net_ping_ip6
))
490 return CMD_RET_USAGE
;
493 if (net_loop(PING6
) < 0) {
495 printf("ping6 failed; host %pI6c is not alive\n",
501 printf("host %pI6c is alive\n", &net_ping_ip6
);
506 ping6
, 2, 1, do_ping6
,
507 "send ICMPv6 ECHO_REQUEST to network host",
510 #endif /* CONFIG_CMD_PING6 */
512 #if defined(CONFIG_CMD_CDP)
514 static void cdp_update_env(void)
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
;
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
;
534 int do_cdp(struct cmd_tbl
*cmdtp
, int flag
, int argc
, char *const argv
[])
540 printf("cdp failed; perhaps not a CISCO switch?\n");
541 return CMD_RET_FAILURE
;
546 return CMD_RET_SUCCESS
;
551 "Perform CDP network configuration",
556 #if defined(CONFIG_CMD_SNTP)
557 static struct udp_ops sntp_ops
= {
558 .prereq
= sntp_prereq
,
563 int do_sntp(struct cmd_tbl
*cmdtp
, int flag
, int argc
, char *const argv
[])
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
;
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
;
581 toff
= env_get("timeoffset");
583 net_ntp_time_offset
= 0;
585 net_ntp_time_offset
= simple_strtol(toff
, NULL
, 10);
587 if (udp_loop(&sntp_ops
) < 0) {
588 printf("SNTP failed: host %pI4 not responding\n",
590 return CMD_RET_FAILURE
;
593 return CMD_RET_SUCCESS
;
598 "synchronize RTC via network",
603 #if defined(CONFIG_CMD_DNS)
604 int do_dns(struct cmd_tbl
*cmdtp
, int flag
, int argc
, char *const argv
[])
607 return CMD_RET_USAGE
;
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
618 * but hey - this is a minimalist implmentation, so only check length
619 * and let the name server deal with things.
621 if (strlen(argv
[1]) >= 255) {
622 printf("dns error: hostname too long\n");
623 return CMD_RET_FAILURE
;
626 net_dns_resolve
= argv
[1];
629 net_dns_env_var
= argv
[2];
631 net_dns_env_var
= NULL
;
633 if (net_loop(DNS
) < 0) {
634 printf("dns lookup of %s failed, check setup\n", argv
[1]);
635 return CMD_RET_FAILURE
;
638 return CMD_RET_SUCCESS
;
643 "lookup the IP of a hostname",
647 #endif /* CONFIG_CMD_DNS */
649 #if defined(CONFIG_CMD_LINK_LOCAL)
650 static int do_link_local(struct cmd_tbl
*cmdtp
, int flag
, int argc
,
655 if (net_loop(LINKLOCAL
) < 0)
656 return CMD_RET_FAILURE
;
658 net_gateway
.s_addr
= 0;
659 ip_to_string(net_gateway
, tmp
);
660 env_set("gatewayip", tmp
);
662 ip_to_string(net_netmask
, tmp
);
663 env_set("netmask", tmp
);
665 ip_to_string(net_ip
, tmp
);
666 env_set("ipaddr", tmp
);
667 env_set("llipaddr", tmp
); /* store this for next time */
669 return CMD_RET_SUCCESS
;
673 linklocal
, 1, 1, do_link_local
,
674 "acquire a network IP address using the link-local protocol",
678 #endif /* CONFIG_CMD_LINK_LOCAL */
680 static int do_net_list(struct cmd_tbl
*cmdtp
, int flag
, int argc
, char *const argv
[])
682 const struct udevice
*current
= eth_get_dev();
683 unsigned char env_enetaddr
[ARP_HLEN
];
684 const struct udevice
*dev
;
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" : "");
692 return CMD_RET_SUCCESS
;
695 static int do_net_stats(struct cmd_tbl
*cmdtp
, int flag
, int argc
, char *const argv
[])
697 int nstats
, err
, i
, off
;
703 return CMD_RET_USAGE
;
705 err
= uclass_get_device_by_name(UCLASS_ETH
, argv
[1], &dev
);
707 printf("Could not find device %s\n", argv
[1]);
708 return CMD_RET_FAILURE
;
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
;
718 nstats
= eth_get_ops(dev
)->get_sset_count(dev
);
719 strings
= kcalloc(nstats
, ETH_GSTRING_LEN
, GFP_KERNEL
);
721 return CMD_RET_FAILURE
;
723 values
= kcalloc(nstats
, sizeof(u64
), GFP_KERNEL
);
725 goto err_free_strings
;
727 eth_get_ops(dev
)->get_strings(dev
, strings
);
728 eth_get_ops(dev
)->get_stats(dev
, values
);
731 for (i
= 0; i
< nstats
; i
++) {
732 printf(" %s: %llu\n", &strings
[off
], values
[i
]);
733 off
+= ETH_GSTRING_LEN
;
736 return CMD_RET_SUCCESS
;
741 return CMD_RET_FAILURE
;
744 static 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
, "", ""),
749 static int do_net(struct cmd_tbl
*cmdtp
, int flag
, int argc
, char *const argv
[])
753 cp
= find_cmd_tbl(argv
[1], cmd_net
, ARRAY_SIZE(cmd_net
));
755 /* Drop the net command */
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
;
764 return cp
->cmd(cmdtp
, flag
, argc
, argv
);
770 "list - list available devices\n"
771 "stats <device> - dump statistics for specified device\n"
774 #if defined(CONFIG_CMD_NCSI)
775 static int do_ncsi(struct cmd_tbl
*cmdtp
, int flag
, int argc
, char * const argv
[])
777 if (!phy_interface_is_ncsi() || !ncsi_active()) {
778 printf("Device not configured for NC-SI\n");
779 return CMD_RET_FAILURE
;
782 if (net_loop(NCSI
) < 0)
783 return CMD_RET_FAILURE
;
785 return CMD_RET_SUCCESS
;
790 "Configure attached NIC via NC-SI",
793 #endif /* CONFIG_CMD_NCSI */