blob: b206ff58e68176ce6db4f232e220af78a97e5c9e [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
wdenk38635852002-08-27 05:55:31 +00002/*
3 * (C) Copyright 2000
4 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
wdenk38635852002-08-27 05:55:31 +00005 */
6
Simon Glass31ef3942023-01-17 10:47:39 -07007#define LOG_CATEGORY UCLASS_ETH
8
wdenk38635852002-08-27 05:55:31 +00009/*
10 * Boot support
11 */
Simon Glass1ea97892020-05-10 11:40:00 -060012#include <bootstage.h>
wdenk38635852002-08-27 05:55:31 +000013#include <command.h>
Tim Harveyc4e71e72021-06-18 15:26:21 -070014#include <dm.h>
Ioana Ciornei7a9fdec2023-05-23 16:47:47 +030015#include <dm/devres.h>
Simon Glass5e6201b2019-08-01 09:46:51 -060016#include <env.h>
Simon Glass85f13782019-12-28 10:45:03 -070017#include <image.h>
Simon Glass31ef3942023-01-17 10:47:39 -070018#include <log.h>
wdenk38635852002-08-27 05:55:31 +000019#include <net.h>
Viacheslav Mitrofanov12b95ae2022-12-02 12:18:07 +030020#include <net6.h>
Philippe Reynes2829d992020-09-18 14:13:02 +020021#include <net/udp.h>
22#include <net/sntp.h>
Samuel Mendoza-Jonasfeebd6c2022-08-08 21:46:04 +093023#include <net/ncsi.h>
wdenk38635852002-08-27 05:55:31 +000024
Simon Glassed38aef2020-05-10 11:40:03 -060025static int netboot_common(enum proto_t, struct cmd_tbl *, int, char * const []);
wdenk38635852002-08-27 05:55:31 +000026
Joe Hershbergerb0429462018-04-13 15:26:30 -050027#ifdef CONFIG_CMD_BOOTP
Simon Glassed38aef2020-05-10 11:40:03 -060028static int do_bootp(struct cmd_tbl *cmdtp, int flag, int argc,
29 char *const argv[])
wdenk38635852002-08-27 05:55:31 +000030{
Kim Phillipsdc00a682012-10-29 13:34:31 +000031 return netboot_common(BOOTP, cmdtp, argc, argv);
wdenk38635852002-08-27 05:55:31 +000032}
33
wdenkf287a242003-07-01 21:06:45 +000034U_BOOT_CMD(
35 bootp, 3, 1, do_bootp,
Peter Tyserdfb72b82009-01-27 18:03:12 -060036 "boot image via network using BOOTP/TFTP protocol",
Wolfgang Denkc54781c2009-05-24 17:06:54 +020037 "[loadAddress] [[hostIPaddr:]bootfilename]"
wdenk57b2d802003-06-27 21:31:46 +000038);
Joe Hershbergerb0429462018-04-13 15:26:30 -050039#endif
wdenk57b2d802003-06-27 21:31:46 +000040
Joe Hershbergerb0429462018-04-13 15:26:30 -050041#ifdef CONFIG_CMD_TFTPBOOT
Simon Glassed38aef2020-05-10 11:40:03 -060042int do_tftpb(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
wdenk38635852002-08-27 05:55:31 +000043{
Simon Glass768cbf02011-12-10 11:08:06 +000044 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;
wdenk38635852002-08-27 05:55:31 +000050}
51
Viacheslav Mitrofanov12b95ae2022-12-02 12:18:07 +030052#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
wdenkf287a242003-07-01 21:06:45 +000061U_BOOT_CMD(
62 tftpboot, 3, 1, do_tftpb,
Heinrich Schuchardt7d289f12022-09-04 09:08:11 +020063 "load file via network using TFTP protocol",
Wolfgang Denkc54781c2009-05-24 17:06:54 +020064 "[loadAddress] [[hostIPaddr:]bootfilename]"
wdenk57b2d802003-06-27 21:31:46 +000065);
Joe Hershbergerb0429462018-04-13 15:26:30 -050066#endif
Viacheslav Mitrofanov12b95ae2022-12-02 12:18:07 +030067#endif
wdenk57b2d802003-06-27 21:31:46 +000068
Simon Glass85d96ec2011-10-24 18:00:08 +000069#ifdef CONFIG_CMD_TFTPPUT
Simon Glassed38aef2020-05-10 11:40:03 -060070static int do_tftpput(struct cmd_tbl *cmdtp, int flag, int argc,
71 char *const argv[])
Simon Glass85d96ec2011-10-24 18:00:08 +000072{
Joe Hershbergerba867832015-03-22 17:09:09 -050073 return netboot_common(TFTPPUT, cmdtp, argc, argv);
Simon Glass85d96ec2011-10-24 18:00:08 +000074}
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
Luca Ceresoli7aa81a42011-05-17 00:03:40 +000083#ifdef CONFIG_CMD_TFTPSRV
Simon Glassed38aef2020-05-10 11:40:03 -060084static int do_tftpsrv(struct cmd_tbl *cmdtp, int flag, int argc,
85 char *const argv[])
Luca Ceresoli7aa81a42011-05-17 00:03:40 +000086{
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
Peter Tyserf7a48ca2010-09-30 11:25:48 -0500101#ifdef CONFIG_CMD_RARP
Simon Glassed38aef2020-05-10 11:40:03 -0600102int do_rarpb(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
wdenk38635852002-08-27 05:55:31 +0000103{
Kim Phillipsdc00a682012-10-29 13:34:31 +0000104 return netboot_common(RARP, cmdtp, argc, argv);
wdenk38635852002-08-27 05:55:31 +0000105}
106
wdenkf287a242003-07-01 21:06:45 +0000107U_BOOT_CMD(
108 rarpboot, 3, 1, do_rarpb,
Peter Tyserdfb72b82009-01-27 18:03:12 -0600109 "boot image via network using RARP/TFTP protocol",
Wolfgang Denkc54781c2009-05-24 17:06:54 +0200110 "[loadAddress] [[hostIPaddr:]bootfilename]"
wdenk57b2d802003-06-27 21:31:46 +0000111);
Peter Tyserf7a48ca2010-09-30 11:25:48 -0500112#endif
wdenk57b2d802003-06-27 21:31:46 +0000113
Sean Edmondba802862023-04-11 10:48:47 -0700114#if defined(CONFIG_CMD_DHCP6)
115static int do_dhcp6(struct cmd_tbl *cmdtp, int flag, int argc,
116 char *const argv[])
117{
118 int i;
119 int dhcp_argc;
120 char *dhcp_argv[] = {NULL, NULL, NULL, NULL};
121
122 /* Add -ipv6 flag for autoload */
123 for (i = 0; i < argc; i++)
124 dhcp_argv[i] = argv[i];
125 dhcp_argc = argc + 1;
126 dhcp_argv[dhcp_argc - 1] = USE_IP6_CMD_PARAM;
127
128 return netboot_common(DHCP6, cmdtp, dhcp_argc, dhcp_argv);
129}
130
131U_BOOT_CMD(dhcp6, 3, 1, do_dhcp6,
132 "boot image via network using DHCPv6/TFTP protocol.\n"
133 "Use IPv6 hostIPaddr framed with [] brackets",
134 "[loadAddress] [[hostIPaddr:]bootfilename]");
135#endif
136
Jon Loeligerd76b5c12007-07-08 18:02:23 -0500137#if defined(CONFIG_CMD_DHCP)
Simon Glassed38aef2020-05-10 11:40:03 -0600138static int do_dhcp(struct cmd_tbl *cmdtp, int flag, int argc,
139 char *const argv[])
wdenk38635852002-08-27 05:55:31 +0000140{
141 return netboot_common(DHCP, cmdtp, argc, argv);
142}
wdenk57b2d802003-06-27 21:31:46 +0000143
wdenkf287a242003-07-01 21:06:45 +0000144U_BOOT_CMD(
145 dhcp, 3, 1, do_dhcp,
Peter Tyserdfb72b82009-01-27 18:03:12 -0600146 "boot image via network using DHCP/TFTP protocol",
Wolfgang Denkc54781c2009-05-24 17:06:54 +0200147 "[loadAddress] [[hostIPaddr:]bootfilename]"
wdenk57b2d802003-06-27 21:31:46 +0000148);
Simon Glass31ef3942023-01-17 10:47:39 -0700149
150int dhcp_run(ulong addr, const char *fname, bool autoload)
151{
152 char *dhcp_argv[] = {"dhcp", NULL, (char *)fname, NULL};
153 struct cmd_tbl cmdtp = {}; /* dummy */
154 char file_addr[17];
155 int old_autoload;
156 int ret, result;
157
158 log_debug("addr=%lx, fname=%s, autoload=%d\n", addr, fname, autoload);
159 old_autoload = env_get_yesno("autoload");
160 ret = env_set("autoload", autoload ? "y" : "n");
161 if (ret)
162 return log_msg_ret("en1", -EINVAL);
163
164 if (autoload) {
165 sprintf(file_addr, "%lx", addr);
166 dhcp_argv[1] = file_addr;
167 }
168
169 result = do_dhcp(&cmdtp, 0, !autoload ? 1 : fname ? 3 : 2, dhcp_argv);
170
171 ret = env_set("autoload", old_autoload == -1 ? NULL :
172 old_autoload ? "y" : "n");
173 if (ret)
174 return log_msg_ret("en2", -EINVAL);
175
176 if (result)
177 return log_msg_ret("res", -ENOENT);
178
179 return 0;
180}
Jon Loeligerd704d912007-07-10 11:02:44 -0500181#endif
wdenk38635852002-08-27 05:55:31 +0000182
Jon Loeligerd76b5c12007-07-08 18:02:23 -0500183#if defined(CONFIG_CMD_NFS)
Simon Glassed38aef2020-05-10 11:40:03 -0600184static int do_nfs(struct cmd_tbl *cmdtp, int flag, int argc,
185 char *const argv[])
wdenkbe9c1cb2004-02-24 02:00:03 +0000186{
187 return netboot_common(NFS, cmdtp, argc, argv);
188}
189
190U_BOOT_CMD(
191 nfs, 3, 1, do_nfs,
Peter Tyserdfb72b82009-01-27 18:03:12 -0600192 "boot image via network using NFS protocol",
Wolfgang Denkc54781c2009-05-24 17:06:54 +0200193 "[loadAddress] [[hostIPaddr:]bootfilename]"
wdenkbe9c1cb2004-02-24 02:00:03 +0000194);
Jon Loeligerd704d912007-07-10 11:02:44 -0500195#endif
wdenkbe9c1cb2004-02-24 02:00:03 +0000196
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800197#if defined(CONFIG_CMD_WGET)
198static int do_wget(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
199{
200 return netboot_common(WGET, cmdtp, argc, argv);
201}
202
203U_BOOT_CMD(
204 wget, 3, 1, do_wget,
205 "boot image via network using HTTP protocol",
206 "[loadAddress] [[hostIPaddr:]path and image name]"
207);
208#endif
209
Kim Phillipsdc00a682012-10-29 13:34:31 +0000210static void netboot_update_env(void)
wdenk38635852002-08-27 05:55:31 +0000211{
Sean Edmond08605882023-05-18 12:35:40 -0700212 char tmp[46];
wdenk38635852002-08-27 05:55:31 +0000213
Joe Hershberger5874dec2015-04-08 01:41:01 -0500214 if (net_gateway.s_addr) {
215 ip_to_string(net_gateway, tmp);
Simon Glass6a38e412017-08-03 12:22:09 -0600216 env_set("gatewayip", tmp);
wdenk05939202004-04-18 17:39:38 +0000217 }
wdenk38635852002-08-27 05:55:31 +0000218
Joe Hershberger5874dec2015-04-08 01:41:01 -0500219 if (net_netmask.s_addr) {
220 ip_to_string(net_netmask, tmp);
Simon Glass6a38e412017-08-03 12:22:09 -0600221 env_set("netmask", tmp);
wdenk05939202004-04-18 17:39:38 +0000222 }
wdenk38635852002-08-27 05:55:31 +0000223
Krebs, Olafd9249382020-03-09 14:27:55 +0000224#ifdef CONFIG_CMD_BOOTP
Joe Hershberger6d236432015-04-08 01:41:03 -0500225 if (net_hostname[0])
Simon Glass6a38e412017-08-03 12:22:09 -0600226 env_set("hostname", net_hostname);
Krebs, Olafd9249382020-03-09 14:27:55 +0000227#endif
wdenk38635852002-08-27 05:55:31 +0000228
Krebs, Olafd9249382020-03-09 14:27:55 +0000229#ifdef CONFIG_CMD_BOOTP
Joe Hershberger6d236432015-04-08 01:41:03 -0500230 if (net_root_path[0])
Simon Glass6a38e412017-08-03 12:22:09 -0600231 env_set("rootpath", net_root_path);
Krebs, Olafd9249382020-03-09 14:27:55 +0000232#endif
wdenk38635852002-08-27 05:55:31 +0000233
Joe Hershberger5874dec2015-04-08 01:41:01 -0500234 if (net_ip.s_addr) {
235 ip_to_string(net_ip, tmp);
Simon Glass6a38e412017-08-03 12:22:09 -0600236 env_set("ipaddr", tmp);
wdenk05939202004-04-18 17:39:38 +0000237 }
Joe Hershbergerd4762042012-05-23 07:59:17 +0000238 /*
Baruch Siachbc713252016-12-27 11:03:29 +0200239 * Only attempt to change serverip if net/bootp.c:store_net_params()
Joe Hershbergerd4762042012-05-23 07:59:17 +0000240 * could have set it
241 */
Simon Glass297fc202021-12-18 11:27:52 -0700242 if (!IS_ENABLED(CONFIG_BOOTP_SERVERIP) && net_server_ip.s_addr) {
Joe Hershberger5874dec2015-04-08 01:41:01 -0500243 ip_to_string(net_server_ip, tmp);
Simon Glass6a38e412017-08-03 12:22:09 -0600244 env_set("serverip", tmp);
wdenk05939202004-04-18 17:39:38 +0000245 }
Joe Hershberger5874dec2015-04-08 01:41:01 -0500246 if (net_dns_server.s_addr) {
247 ip_to_string(net_dns_server, tmp);
Simon Glass6a38e412017-08-03 12:22:09 -0600248 env_set("dnsip", tmp);
wdenk05939202004-04-18 17:39:38 +0000249 }
Jon Loeliger5336a762007-07-09 22:08:34 -0500250#if defined(CONFIG_BOOTP_DNS2)
Joe Hershberger5874dec2015-04-08 01:41:01 -0500251 if (net_dns_server2.s_addr) {
252 ip_to_string(net_dns_server2, tmp);
Simon Glass6a38e412017-08-03 12:22:09 -0600253 env_set("dnsip2", tmp);
wdenk05939202004-04-18 17:39:38 +0000254 }
stroesee0aadfb2003-08-28 14:17:32 +0000255#endif
Krebs, Olafd9249382020-03-09 14:27:55 +0000256#ifdef CONFIG_CMD_BOOTP
Joe Hershberger6d236432015-04-08 01:41:03 -0500257 if (net_nis_domain[0])
Simon Glass6a38e412017-08-03 12:22:09 -0600258 env_set("domain", net_nis_domain);
Krebs, Olafd9249382020-03-09 14:27:55 +0000259#endif
wdenkb4ad9622005-04-01 00:25:43 +0000260
Joe Hershberger013d3872015-04-08 01:41:17 -0500261#if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_TIMEOFFSET)
Joe Hershbergerc80b41b02015-04-08 01:41:21 -0500262 if (net_ntp_time_offset) {
263 sprintf(tmp, "%d", net_ntp_time_offset);
Simon Glass6a38e412017-08-03 12:22:09 -0600264 env_set("timeoffset", tmp);
wdenkb4ad9622005-04-01 00:25:43 +0000265 }
266#endif
Joe Hershberger013d3872015-04-08 01:41:17 -0500267#if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_NTPSERVER)
Joe Hershberger5874dec2015-04-08 01:41:01 -0500268 if (net_ntp_server.s_addr) {
269 ip_to_string(net_ntp_server, tmp);
Simon Glass6a38e412017-08-03 12:22:09 -0600270 env_set("ntpserverip", tmp);
wdenkb4ad9622005-04-01 00:25:43 +0000271 }
272#endif
Sean Edmond65815e72023-02-15 20:38:36 -0800273
274 if (IS_ENABLED(CONFIG_IPV6)) {
275 if (!ip6_is_unspecified_addr(&net_ip6) ||
276 net_prefix_length != 0) {
Sean Edmond65815e72023-02-15 20:38:36 -0800277 if (net_prefix_length != 0)
Sean Edmond08605882023-05-18 12:35:40 -0700278 snprintf(tmp, sizeof(tmp), "%pI6c/%d", &net_ip6, net_prefix_length);
279 else
280 snprintf(tmp, sizeof(tmp), "%pI6c", &net_ip6);
Sean Edmond65815e72023-02-15 20:38:36 -0800281 env_set("ip6addr", tmp);
282 }
283
284 if (!ip6_is_unspecified_addr(&net_server_ip6)) {
Sean Edmond08605882023-05-18 12:35:40 -0700285 snprintf(tmp, sizeof(tmp), "%pI6c", &net_server_ip6);
Sean Edmond65815e72023-02-15 20:38:36 -0800286 env_set("serverip6", tmp);
287 }
288
289 if (!ip6_is_unspecified_addr(&net_gateway6)) {
Sean Edmond08605882023-05-18 12:35:40 -0700290 snprintf(tmp, sizeof(tmp), "%pI6c", &net_gateway6);
Sean Edmond65815e72023-02-15 20:38:36 -0800291 env_set("gatewayip6", tmp);
292 }
293 }
wdenk38635852002-08-27 05:55:31 +0000294}
wdenk05939202004-04-18 17:39:38 +0000295
Heinrich Schuchardt107b3a42022-09-03 12:21:09 +0000296/**
297 * parse_addr_size() - parse address and size arguments for tftpput
298 *
299 * @argv: command line arguments
300 * Return: 0 on success
301 */
302static int parse_addr_size(char * const argv[])
wdenk38635852002-08-27 05:55:31 +0000303{
Heinrich Schuchardt107b3a42022-09-03 12:21:09 +0000304 if (strict_strtoul(argv[1], 16, &image_save_addr) < 0 ||
305 strict_strtoul(argv[2], 16, &image_save_size) < 0) {
306 printf("Invalid address/size\n");
307 return CMD_RET_USAGE;
308 }
309 return 0;
310}
Alexander Graff43bf5d2018-06-15 10:29:27 +0200311
Heinrich Schuchardt107b3a42022-09-03 12:21:09 +0000312/**
313 * parse_args() - parse command line arguments
314 *
315 * @proto: command prototype
316 * @argc: number of arguments
317 * @argv: command line arguments
318 * Return: 0 on success
319 */
320static int parse_args(enum proto_t proto, int argc, char *const argv[])
321{
322 ulong addr;
323 char *end;
wdenk38635852002-08-27 05:55:31 +0000324
325 switch (argc) {
326 case 1:
Simon Glassdf1bbae2023-02-05 15:36:45 -0700327 if (IS_ENABLED(CONFIG_CMD_TFTPPUT) && proto == TFTPPUT)
Heinrich Schuchardt107b3a42022-09-03 12:21:09 +0000328 return 1;
329
Joe Hershberger2e135272018-07-03 19:36:42 -0500330 /* refresh bootfile name from env */
331 copy_filename(net_boot_file_name, env_get("bootfile"),
332 sizeof(net_boot_file_name));
wdenk38635852002-08-27 05:55:31 +0000333 break;
334
Heinrich Schuchardt107b3a42022-09-03 12:21:09 +0000335 case 2:
Simon Glassdf1bbae2023-02-05 15:36:45 -0700336 if (IS_ENABLED(CONFIG_CMD_TFTPPUT) && proto == TFTPPUT)
Heinrich Schuchardt107b3a42022-09-03 12:21:09 +0000337 return 1;
338 /*
Peter Tyser24c297d2008-12-02 12:59:51 -0600339 * Only one arg - accept two forms:
340 * Just load address, or just boot file name. The latter
341 * form must be written in a format which can not be
342 * mis-interpreted as a valid number.
wdenk38635852002-08-27 05:55:31 +0000343 */
Simon Glass3ff49ec2021-07-24 09:03:29 -0600344 addr = hextoul(argv[1], &end);
Alexander Graff43bf5d2018-06-15 10:29:27 +0200345 if (end == (argv[1] + strlen(argv[1]))) {
Simon Glass892265d2019-12-28 10:45:02 -0700346 image_load_addr = addr;
Joe Hershberger2e135272018-07-03 19:36:42 -0500347 /* refresh bootfile name from env */
348 copy_filename(net_boot_file_name, env_get("bootfile"),
349 sizeof(net_boot_file_name));
Alexander Graff43bf5d2018-06-15 10:29:27 +0200350 } else {
351 net_boot_file_name_explicit = true;
Joe Hershberger290c8992015-04-08 01:41:02 -0500352 copy_filename(net_boot_file_name, argv[1],
353 sizeof(net_boot_file_name));
Alexander Graff43bf5d2018-06-15 10:29:27 +0200354 }
wdenk38635852002-08-27 05:55:31 +0000355 break;
356
Joe Hershberger013d3872015-04-08 01:41:17 -0500357 case 3:
Simon Glassdf1bbae2023-02-05 15:36:45 -0700358 if (IS_ENABLED(CONFIG_CMD_TFTPPUT) && proto == TFTPPUT) {
Heinrich Schuchardt107b3a42022-09-03 12:21:09 +0000359 if (parse_addr_size(argv))
360 return 1;
361 } else {
362 image_load_addr = hextoul(argv[1], NULL);
363 net_boot_file_name_explicit = true;
364 copy_filename(net_boot_file_name, argv[2],
365 sizeof(net_boot_file_name));
366 }
wdenk38635852002-08-27 05:55:31 +0000367 break;
368
Simon Glass85d96ec2011-10-24 18:00:08 +0000369#ifdef CONFIG_CMD_TFTPPUT
370 case 4:
Heinrich Schuchardt107b3a42022-09-03 12:21:09 +0000371 if (parse_addr_size(argv))
372 return 1;
Alexander Graff43bf5d2018-06-15 10:29:27 +0200373 net_boot_file_name_explicit = true;
Joe Hershberger290c8992015-04-08 01:41:02 -0500374 copy_filename(net_boot_file_name, argv[3],
375 sizeof(net_boot_file_name));
Simon Glass85d96ec2011-10-24 18:00:08 +0000376 break;
377#endif
Wolfgang Denk3b683112010-07-17 01:06:04 +0200378 default:
Heinrich Schuchardt107b3a42022-09-03 12:21:09 +0000379 return 1;
380 }
381 return 0;
382}
383
384static int netboot_common(enum proto_t proto, struct cmd_tbl *cmdtp, int argc,
385 char *const argv[])
386{
387 char *s;
388 int rcode = 0;
389 int size;
390
391 net_boot_file_name_explicit = false;
392 *net_boot_file_name = '\0';
393
394 /* pre-set image_load_addr */
395 s = env_get("loadaddr");
396 if (s != NULL)
397 image_load_addr = hextoul(s, NULL);
398
Viacheslav Mitrofanov12b95ae2022-12-02 12:18:07 +0300399 if (IS_ENABLED(CONFIG_IPV6)) {
400 use_ip6 = false;
401
402 /* IPv6 parameter has to be always *last* */
403 if (!strcmp(argv[argc - 1], USE_IP6_CMD_PARAM)) {
404 use_ip6 = true;
405 /* It is a hack not to break switch/case code */
406 --argc;
407 }
408 }
409
Heinrich Schuchardt107b3a42022-09-03 12:21:09 +0000410 if (parse_args(proto, argc, argv)) {
Simon Glass0169e6b2012-02-13 13:51:18 +0000411 bootstage_error(BOOTSTAGE_ID_NET_START);
Simon Glassa06dfc72011-12-10 08:44:01 +0000412 return CMD_RET_USAGE;
wdenk38635852002-08-27 05:55:31 +0000413 }
Heinrich Schuchardt107b3a42022-09-03 12:21:09 +0000414
Simon Glass0169e6b2012-02-13 13:51:18 +0000415 bootstage_mark(BOOTSTAGE_ID_NET_START);
wdenk38635852002-08-27 05:55:31 +0000416
Viacheslav Mitrofanov12b95ae2022-12-02 12:18:07 +0300417 if (IS_ENABLED(CONFIG_IPV6) && !use_ip6) {
418 char *s, *e;
419 size_t len;
420
421 s = strchr(net_boot_file_name, '[');
422 e = strchr(net_boot_file_name, ']');
423 if (s && e) {
424 len = e - s;
425 if (!string_to_ip6(s + 1, len - 1, &net_server_ip6))
426 use_ip6 = true;
427 }
428 }
429
Joe Hershbergerc80b41b02015-04-08 01:41:21 -0500430 size = net_loop(proto);
Joe Hershberger013d3872015-04-08 01:41:17 -0500431 if (size < 0) {
Simon Glass0169e6b2012-02-13 13:51:18 +0000432 bootstage_error(BOOTSTAGE_ID_NET_NETLOOP_OK);
Joe Hershbergerba867832015-03-22 17:09:09 -0500433 return CMD_RET_FAILURE;
Heiko Schocher633e03a2007-06-22 19:11:54 +0200434 }
Simon Glass0169e6b2012-02-13 13:51:18 +0000435 bootstage_mark(BOOTSTAGE_ID_NET_NETLOOP_OK);
wdenk38635852002-08-27 05:55:31 +0000436
Joe Hershbergerc80b41b02015-04-08 01:41:21 -0500437 /* net_loop ok, update environment */
wdenk38635852002-08-27 05:55:31 +0000438 netboot_update_env();
439
wdenkc217f6d2002-11-11 02:11:37 +0000440 /* done if no file was loaded (no errors though) */
Heiko Schocher633e03a2007-06-22 19:11:54 +0200441 if (size == 0) {
Simon Glass0169e6b2012-02-13 13:51:18 +0000442 bootstage_error(BOOTSTAGE_ID_NET_LOADED);
Joe Hershbergerba867832015-03-22 17:09:09 -0500443 return CMD_RET_SUCCESS;
Heiko Schocher633e03a2007-06-22 19:11:54 +0200444 }
wdenkc217f6d2002-11-11 02:11:37 +0000445
Simon Glass0169e6b2012-02-13 13:51:18 +0000446 bootstage_mark(BOOTSTAGE_ID_NET_LOADED);
Simon Glass964d1d42012-01-14 15:24:52 +0000447
Mike Frysinger194c2e82011-06-05 13:43:02 +0000448 rcode = bootm_maybe_autostart(cmdtp, argv[0]);
wdenk38635852002-08-27 05:55:31 +0000449
Joe Hershbergerba867832015-03-22 17:09:09 -0500450 if (rcode == CMD_RET_SUCCESS)
Simon Glass0169e6b2012-02-13 13:51:18 +0000451 bootstage_mark(BOOTSTAGE_ID_NET_DONE);
Joe Hershbergerba867832015-03-22 17:09:09 -0500452 else
453 bootstage_error(BOOTSTAGE_ID_NET_DONE_ERR);
wdenk38635852002-08-27 05:55:31 +0000454 return rcode;
455}
wdenke6466f62003-06-05 19:27:42 +0000456
Jon Loeligerd76b5c12007-07-08 18:02:23 -0500457#if defined(CONFIG_CMD_PING)
Simon Glassed38aef2020-05-10 11:40:03 -0600458static int do_ping(struct cmd_tbl *cmdtp, int flag, int argc,
459 char *const argv[])
wdenke6466f62003-06-05 19:27:42 +0000460{
461 if (argc < 2)
Joe Hershbergerba867832015-03-22 17:09:09 -0500462 return CMD_RET_USAGE;
wdenke6466f62003-06-05 19:27:42 +0000463
Joe Hershberger5874dec2015-04-08 01:41:01 -0500464 net_ping_ip = string_to_ip(argv[1]);
465 if (net_ping_ip.s_addr == 0)
Simon Glassa06dfc72011-12-10 08:44:01 +0000466 return CMD_RET_USAGE;
wdenke6466f62003-06-05 19:27:42 +0000467
Joe Hershbergerc80b41b02015-04-08 01:41:21 -0500468 if (net_loop(PING) < 0) {
wdenke6466f62003-06-05 19:27:42 +0000469 printf("ping failed; host %s is not alive\n", argv[1]);
Joe Hershbergerba867832015-03-22 17:09:09 -0500470 return CMD_RET_FAILURE;
wdenke6466f62003-06-05 19:27:42 +0000471 }
472
473 printf("host %s is alive\n", argv[1]);
474
Joe Hershbergerba867832015-03-22 17:09:09 -0500475 return CMD_RET_SUCCESS;
wdenke6466f62003-06-05 19:27:42 +0000476}
wdenkca9bc762003-07-15 07:45:49 +0000477
478U_BOOT_CMD(
479 ping, 2, 1, do_ping,
Peter Tyserdfb72b82009-01-27 18:03:12 -0600480 "send ICMP ECHO_REQUEST to network host",
Wolfgang Denkc54781c2009-05-24 17:06:54 +0200481 "pingAddress"
wdenkca9bc762003-07-15 07:45:49 +0000482);
Jon Loeligerd704d912007-07-10 11:02:44 -0500483#endif
wdenk38635852002-08-27 05:55:31 +0000484
Viacheslav Mitrofanove03c8aa2022-12-02 12:18:08 +0300485#if IS_ENABLED(CONFIG_CMD_PING6)
486int do_ping6(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
487{
488 if (string_to_ip6(argv[1], strlen(argv[1]), &net_ping_ip6))
489 return CMD_RET_USAGE;
490
491 use_ip6 = true;
492 if (net_loop(PING6) < 0) {
493 use_ip6 = false;
494 printf("ping6 failed; host %pI6c is not alive\n",
495 &net_ping_ip6);
496 return 1;
497 }
498
499 use_ip6 = false;
500 printf("host %pI6c is alive\n", &net_ping_ip6);
501 return 0;
502}
503
504U_BOOT_CMD(
505 ping6, 2, 1, do_ping6,
506 "send ICMPv6 ECHO_REQUEST to network host",
507 "pingAddress"
508);
509#endif /* CONFIG_CMD_PING6 */
510
Jon Loeligerd76b5c12007-07-08 18:02:23 -0500511#if defined(CONFIG_CMD_CDP)
wdenk145d2c12004-04-15 21:48:45 +0000512
513static void cdp_update_env(void)
514{
515 char tmp[16];
516
Joe Hershberger527336f2015-04-08 01:41:14 -0500517 if (cdp_appliance_vlan != htons(-1)) {
518 printf("CDP offered appliance VLAN %d\n",
519 ntohs(cdp_appliance_vlan));
Joe Hershberger013d3872015-04-08 01:41:17 -0500520 vlan_to_string(cdp_appliance_vlan, tmp);
Simon Glass6a38e412017-08-03 12:22:09 -0600521 env_set("vlan", tmp);
Joe Hershberger013d3872015-04-08 01:41:17 -0500522 net_our_vlan = cdp_appliance_vlan;
wdenk145d2c12004-04-15 21:48:45 +0000523 }
524
Joe Hershberger527336f2015-04-08 01:41:14 -0500525 if (cdp_native_vlan != htons(-1)) {
526 printf("CDP offered native VLAN %d\n", ntohs(cdp_native_vlan));
Joe Hershberger013d3872015-04-08 01:41:17 -0500527 vlan_to_string(cdp_native_vlan, tmp);
Simon Glass6a38e412017-08-03 12:22:09 -0600528 env_set("nvlan", tmp);
Joe Hershberger013d3872015-04-08 01:41:17 -0500529 net_native_vlan = cdp_native_vlan;
wdenk145d2c12004-04-15 21:48:45 +0000530 }
wdenk145d2c12004-04-15 21:48:45 +0000531}
532
Simon Glassed38aef2020-05-10 11:40:03 -0600533int do_cdp(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
wdenk145d2c12004-04-15 21:48:45 +0000534{
535 int r;
536
Joe Hershbergerc80b41b02015-04-08 01:41:21 -0500537 r = net_loop(CDP);
wdenk145d2c12004-04-15 21:48:45 +0000538 if (r < 0) {
539 printf("cdp failed; perhaps not a CISCO switch?\n");
Joe Hershbergerba867832015-03-22 17:09:09 -0500540 return CMD_RET_FAILURE;
wdenk145d2c12004-04-15 21:48:45 +0000541 }
542
543 cdp_update_env();
544
Joe Hershbergerba867832015-03-22 17:09:09 -0500545 return CMD_RET_SUCCESS;
wdenk145d2c12004-04-15 21:48:45 +0000546}
547
548U_BOOT_CMD(
549 cdp, 1, 1, do_cdp,
Wolfgang Denkd29afc32010-10-26 23:42:23 +0200550 "Perform CDP network configuration",
Wolfgang Denkf003d922010-12-23 17:02:18 +0100551 "\n"
wdenk145d2c12004-04-15 21:48:45 +0000552);
Jon Loeligerd704d912007-07-10 11:02:44 -0500553#endif
wdenk145d2c12004-04-15 21:48:45 +0000554
Jon Loeligerd76b5c12007-07-08 18:02:23 -0500555#if defined(CONFIG_CMD_SNTP)
Philippe Reynes2829d992020-09-18 14:13:02 +0200556static struct udp_ops sntp_ops = {
557 .prereq = sntp_prereq,
558 .start = sntp_start,
559 .data = NULL,
560};
561
Simon Glassed38aef2020-05-10 11:40:03 -0600562int do_sntp(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
wdenkb4ad9622005-04-01 00:25:43 +0000563{
564 char *toff;
565
566 if (argc < 2) {
Simon Glassda1a1342017-08-03 12:22:15 -0600567 net_ntp_server = env_get_ip("ntpserverip");
Joe Hershberger5874dec2015-04-08 01:41:01 -0500568 if (net_ntp_server.s_addr == 0) {
Kim Phillipsdc00a682012-10-29 13:34:31 +0000569 printf("ntpserverip not set\n");
Joe Hershbergerba867832015-03-22 17:09:09 -0500570 return CMD_RET_FAILURE;
wdenkb4ad9622005-04-01 00:25:43 +0000571 }
572 } else {
Joe Hershberger5874dec2015-04-08 01:41:01 -0500573 net_ntp_server = string_to_ip(argv[1]);
574 if (net_ntp_server.s_addr == 0) {
Kim Phillipsdc00a682012-10-29 13:34:31 +0000575 printf("Bad NTP server IP address\n");
Joe Hershbergerba867832015-03-22 17:09:09 -0500576 return CMD_RET_FAILURE;
wdenkb4ad9622005-04-01 00:25:43 +0000577 }
578 }
579
Simon Glass64b723f2017-08-03 12:22:12 -0600580 toff = env_get("timeoffset");
Kim Phillipsdc00a682012-10-29 13:34:31 +0000581 if (toff == NULL)
Joe Hershbergerc80b41b02015-04-08 01:41:21 -0500582 net_ntp_time_offset = 0;
Kim Phillipsdc00a682012-10-29 13:34:31 +0000583 else
Joe Hershbergerc80b41b02015-04-08 01:41:21 -0500584 net_ntp_time_offset = simple_strtol(toff, NULL, 10);
wdenkb4ad9622005-04-01 00:25:43 +0000585
Philippe Reynes2829d992020-09-18 14:13:02 +0200586 if (udp_loop(&sntp_ops) < 0) {
Luuk Paulussen7cb3f892011-05-16 18:27:11 +0000587 printf("SNTP failed: host %pI4 not responding\n",
Joe Hershberger013d3872015-04-08 01:41:17 -0500588 &net_ntp_server);
Joe Hershbergerba867832015-03-22 17:09:09 -0500589 return CMD_RET_FAILURE;
wdenkb4ad9622005-04-01 00:25:43 +0000590 }
591
Joe Hershbergerba867832015-03-22 17:09:09 -0500592 return CMD_RET_SUCCESS;
wdenkb4ad9622005-04-01 00:25:43 +0000593}
594
595U_BOOT_CMD(
596 sntp, 2, 1, do_sntp,
Peter Tyserdfb72b82009-01-27 18:03:12 -0600597 "synchronize RTC via network",
wdenkb4ad9622005-04-01 00:25:43 +0000598 "[NTP server IP]\n"
599);
Jon Loeligerd704d912007-07-10 11:02:44 -0500600#endif
Robin Getz82f0d232009-07-20 14:53:54 -0400601
602#if defined(CONFIG_CMD_DNS)
Simon Glassed38aef2020-05-10 11:40:03 -0600603int do_dns(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
Robin Getz82f0d232009-07-20 14:53:54 -0400604{
Wolfgang Denk3b683112010-07-17 01:06:04 +0200605 if (argc == 1)
Simon Glassa06dfc72011-12-10 08:44:01 +0000606 return CMD_RET_USAGE;
Robin Getz82f0d232009-07-20 14:53:54 -0400607
608 /*
609 * We should check for a valid hostname:
610 * - Each label must be between 1 and 63 characters long
611 * - the entire hostname has a maximum of 255 characters
612 * - only the ASCII letters 'a' through 'z' (case-insensitive),
613 * the digits '0' through '9', and the hyphen
614 * - cannot begin or end with a hyphen
615 * - no other symbols, punctuation characters, or blank spaces are
616 * permitted
617 * but hey - this is a minimalist implmentation, so only check length
618 * and let the name server deal with things.
619 */
620 if (strlen(argv[1]) >= 255) {
621 printf("dns error: hostname too long\n");
Joe Hershbergerba867832015-03-22 17:09:09 -0500622 return CMD_RET_FAILURE;
Robin Getz82f0d232009-07-20 14:53:54 -0400623 }
624
Joe Hershbergerf725e342015-04-08 01:41:15 -0500625 net_dns_resolve = argv[1];
Robin Getz82f0d232009-07-20 14:53:54 -0400626
627 if (argc == 3)
Joe Hershbergerf725e342015-04-08 01:41:15 -0500628 net_dns_env_var = argv[2];
Robin Getz82f0d232009-07-20 14:53:54 -0400629 else
Joe Hershbergerf725e342015-04-08 01:41:15 -0500630 net_dns_env_var = NULL;
Robin Getz82f0d232009-07-20 14:53:54 -0400631
Joe Hershbergerc80b41b02015-04-08 01:41:21 -0500632 if (net_loop(DNS) < 0) {
Robin Getz82f0d232009-07-20 14:53:54 -0400633 printf("dns lookup of %s failed, check setup\n", argv[1]);
Joe Hershbergerba867832015-03-22 17:09:09 -0500634 return CMD_RET_FAILURE;
Robin Getz82f0d232009-07-20 14:53:54 -0400635 }
636
Joe Hershbergerba867832015-03-22 17:09:09 -0500637 return CMD_RET_SUCCESS;
Robin Getz82f0d232009-07-20 14:53:54 -0400638}
639
640U_BOOT_CMD(
641 dns, 3, 1, do_dns,
642 "lookup the IP of a hostname",
643 "hostname [envvar]"
644);
645
646#endif /* CONFIG_CMD_DNS */
Joe Hershbergerb35a3a62012-05-23 08:00:12 +0000647
648#if defined(CONFIG_CMD_LINK_LOCAL)
Simon Glassed38aef2020-05-10 11:40:03 -0600649static int do_link_local(struct cmd_tbl *cmdtp, int flag, int argc,
650 char *const argv[])
Joe Hershbergerb35a3a62012-05-23 08:00:12 +0000651{
652 char tmp[22];
653
Joe Hershbergerc80b41b02015-04-08 01:41:21 -0500654 if (net_loop(LINKLOCAL) < 0)
Joe Hershbergerba867832015-03-22 17:09:09 -0500655 return CMD_RET_FAILURE;
Joe Hershbergerb35a3a62012-05-23 08:00:12 +0000656
Joe Hershberger5874dec2015-04-08 01:41:01 -0500657 net_gateway.s_addr = 0;
658 ip_to_string(net_gateway, tmp);
Simon Glass6a38e412017-08-03 12:22:09 -0600659 env_set("gatewayip", tmp);
Joe Hershbergerb35a3a62012-05-23 08:00:12 +0000660
Joe Hershberger5874dec2015-04-08 01:41:01 -0500661 ip_to_string(net_netmask, tmp);
Simon Glass6a38e412017-08-03 12:22:09 -0600662 env_set("netmask", tmp);
Joe Hershbergerb35a3a62012-05-23 08:00:12 +0000663
Joe Hershberger5874dec2015-04-08 01:41:01 -0500664 ip_to_string(net_ip, tmp);
Simon Glass6a38e412017-08-03 12:22:09 -0600665 env_set("ipaddr", tmp);
666 env_set("llipaddr", tmp); /* store this for next time */
Joe Hershbergerb35a3a62012-05-23 08:00:12 +0000667
Joe Hershbergerba867832015-03-22 17:09:09 -0500668 return CMD_RET_SUCCESS;
Joe Hershbergerb35a3a62012-05-23 08:00:12 +0000669}
670
671U_BOOT_CMD(
672 linklocal, 1, 1, do_link_local,
673 "acquire a network IP address using the link-local protocol",
674 ""
675);
676
677#endif /* CONFIG_CMD_LINK_LOCAL */
Tim Harveyc4e71e72021-06-18 15:26:21 -0700678
Tim Harveyc4e71e72021-06-18 15:26:21 -0700679static int do_net_list(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
680{
681 const struct udevice *current = eth_get_dev();
682 unsigned char env_enetaddr[ARP_HLEN];
683 const struct udevice *dev;
684 struct uclass *uc;
685
686 uclass_id_foreach_dev(UCLASS_ETH, dev, uc) {
687 eth_env_get_enetaddr_by_index("eth", dev_seq(dev), env_enetaddr);
688 printf("eth%d : %s %pM %s\n", dev_seq(dev), dev->name, env_enetaddr,
689 current == dev ? "active" : "");
690 }
691 return CMD_RET_SUCCESS;
692}
693
Ioana Ciornei7a9fdec2023-05-23 16:47:47 +0300694static int do_net_stats(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
695{
696 int nstats, err, i, off;
697 struct udevice *dev;
698 u64 *values;
699 u8 *strings;
700
701 if (argc < 2)
702 return CMD_RET_USAGE;
703
704 err = uclass_get_device_by_name(UCLASS_ETH, argv[1], &dev);
705 if (err) {
706 printf("Could not find device %s\n", argv[1]);
707 return CMD_RET_FAILURE;
708 }
709
710 if (!eth_get_ops(dev)->get_sset_count ||
711 !eth_get_ops(dev)->get_strings ||
712 !eth_get_ops(dev)->get_stats) {
713 printf("Driver does not implement stats dump!\n");
714 return CMD_RET_FAILURE;
715 }
716
717 nstats = eth_get_ops(dev)->get_sset_count(dev);
718 strings = kcalloc(nstats, ETH_GSTRING_LEN, GFP_KERNEL);
719 if (!strings)
720 return CMD_RET_FAILURE;
721
722 values = kcalloc(nstats, sizeof(u64), GFP_KERNEL);
723 if (!values)
724 goto err_free_strings;
725
726 eth_get_ops(dev)->get_strings(dev, strings);
727 eth_get_ops(dev)->get_stats(dev, values);
728
729 off = 0;
730 for (i = 0; i < nstats; i++) {
731 printf(" %s: %llu\n", &strings[off], values[i]);
732 off += ETH_GSTRING_LEN;
733 };
734
735 return CMD_RET_SUCCESS;
736
737err_free_strings:
738 kfree(strings);
739
740 return CMD_RET_FAILURE;
741}
742
Tim Harveyc4e71e72021-06-18 15:26:21 -0700743static struct cmd_tbl cmd_net[] = {
744 U_BOOT_CMD_MKENT(list, 1, 0, do_net_list, "", ""),
Ioana Ciornei7a9fdec2023-05-23 16:47:47 +0300745 U_BOOT_CMD_MKENT(stats, 2, 0, do_net_stats, "", ""),
Tim Harveyc4e71e72021-06-18 15:26:21 -0700746};
747
748static int do_net(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
749{
750 struct cmd_tbl *cp;
751
752 cp = find_cmd_tbl(argv[1], cmd_net, ARRAY_SIZE(cmd_net));
753
754 /* Drop the net command */
755 argc--;
756 argv++;
757
758 if (!cp || argc > cp->maxargs)
759 return CMD_RET_USAGE;
760 if (flag == CMD_FLAG_REPEAT && !cmd_is_repeatable(cp))
761 return CMD_RET_SUCCESS;
762
763 return cp->cmd(cmdtp, flag, argc, argv);
764}
765
766U_BOOT_CMD(
Ioana Ciornei7a9fdec2023-05-23 16:47:47 +0300767 net, 3, 1, do_net,
Tim Harveyc4e71e72021-06-18 15:26:21 -0700768 "NET sub-system",
769 "list - list available devices\n"
Ioana Ciornei7a9fdec2023-05-23 16:47:47 +0300770 "stats <device> - dump statistics for specified device\n"
Tim Harveyc4e71e72021-06-18 15:26:21 -0700771);
Samuel Mendoza-Jonasfeebd6c2022-08-08 21:46:04 +0930772
773#if defined(CONFIG_CMD_NCSI)
774static int do_ncsi(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
775{
776 if (!phy_interface_is_ncsi() || !ncsi_active()) {
777 printf("Device not configured for NC-SI\n");
778 return CMD_RET_FAILURE;
779 }
780
781 if (net_loop(NCSI) < 0)
782 return CMD_RET_FAILURE;
783
784 return CMD_RET_SUCCESS;
785}
786
787U_BOOT_CMD(
788 ncsi, 1, 1, do_ncsi,
789 "Configure attached NIC via NC-SI",
790 ""
791);
792#endif /* CONFIG_CMD_NCSI */