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