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 <command.h> 12 #include <net.h> 13 14 static int netboot_common(enum proto_t, cmd_tbl_t *, int, char * const []); 15 16 #ifdef CONFIG_CMD_BOOTP 17 static int do_bootp(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 18 { 19 return netboot_common(BOOTP, cmdtp, argc, argv); 20 } 21 22 U_BOOT_CMD( 23 bootp, 3, 1, do_bootp, 24 "boot image via network using BOOTP/TFTP protocol", 25 "[loadAddress] [[hostIPaddr:]bootfilename]" 26 ); 27 #endif 28 29 #ifdef CONFIG_CMD_TFTPBOOT 30 int do_tftpb(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 31 { 32 int ret; 33 34 bootstage_mark_name(BOOTSTAGE_KERNELREAD_START, "tftp_start"); 35 ret = netboot_common(TFTPGET, cmdtp, argc, argv); 36 bootstage_mark_name(BOOTSTAGE_KERNELREAD_STOP, "tftp_done"); 37 return ret; 38 } 39 40 U_BOOT_CMD( 41 tftpboot, 3, 1, do_tftpb, 42 "boot image via network using TFTP protocol", 43 "[loadAddress] [[hostIPaddr:]bootfilename]" 44 ); 45 #endif 46 47 #ifdef CONFIG_CMD_TFTPPUT 48 static int do_tftpput(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 49 { 50 return netboot_common(TFTPPUT, cmdtp, argc, argv); 51 } 52 53 U_BOOT_CMD( 54 tftpput, 4, 1, do_tftpput, 55 "TFTP put command, for uploading files to a server", 56 "Address Size [[hostIPaddr:]filename]" 57 ); 58 #endif 59 60 #ifdef CONFIG_CMD_TFTPSRV 61 static int do_tftpsrv(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) 62 { 63 return netboot_common(TFTPSRV, cmdtp, argc, argv); 64 } 65 66 U_BOOT_CMD( 67 tftpsrv, 2, 1, do_tftpsrv, 68 "act as a TFTP server and boot the first received file", 69 "[loadAddress]\n" 70 "Listen for an incoming TFTP transfer, receive a file and boot it.\n" 71 "The transfer is aborted if a transfer has not been started after\n" 72 "about 50 seconds or if Ctrl-C is pressed." 73 ); 74 #endif 75 76 77 #ifdef CONFIG_CMD_RARP 78 int do_rarpb(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 79 { 80 return netboot_common(RARP, cmdtp, argc, argv); 81 } 82 83 U_BOOT_CMD( 84 rarpboot, 3, 1, do_rarpb, 85 "boot image via network using RARP/TFTP protocol", 86 "[loadAddress] [[hostIPaddr:]bootfilename]" 87 ); 88 #endif 89 90 #if defined(CONFIG_CMD_DHCP) 91 static int do_dhcp(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 92 { 93 return netboot_common(DHCP, cmdtp, argc, argv); 94 } 95 96 U_BOOT_CMD( 97 dhcp, 3, 1, do_dhcp, 98 "boot image via network using DHCP/TFTP protocol", 99 "[loadAddress] [[hostIPaddr:]bootfilename]" 100 ); 101 #endif 102 103 #if defined(CONFIG_CMD_NFS) 104 static int do_nfs(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 105 { 106 return netboot_common(NFS, cmdtp, argc, argv); 107 } 108 109 U_BOOT_CMD( 110 nfs, 3, 1, do_nfs, 111 "boot image via network using NFS protocol", 112 "[loadAddress] [[hostIPaddr:]bootfilename]" 113 ); 114 #endif 115 116 static void netboot_update_env(void) 117 { 118 char tmp[22]; 119 120 if (net_gateway.s_addr) { 121 ip_to_string(net_gateway, tmp); 122 env_set("gatewayip", tmp); 123 } 124 125 if (net_netmask.s_addr) { 126 ip_to_string(net_netmask, tmp); 127 env_set("netmask", tmp); 128 } 129 130 if (net_hostname[0]) 131 env_set("hostname", net_hostname); 132 133 if (net_root_path[0]) 134 env_set("rootpath", net_root_path); 135 136 if (net_ip.s_addr) { 137 ip_to_string(net_ip, tmp); 138 env_set("ipaddr", tmp); 139 } 140 #if !defined(CONFIG_BOOTP_SERVERIP) 141 /* 142 * Only attempt to change serverip if net/bootp.c:store_net_params() 143 * could have set it 144 */ 145 if (net_server_ip.s_addr) { 146 ip_to_string(net_server_ip, tmp); 147 env_set("serverip", tmp); 148 } 149 #endif 150 if (net_dns_server.s_addr) { 151 ip_to_string(net_dns_server, tmp); 152 env_set("dnsip", tmp); 153 } 154 #if defined(CONFIG_BOOTP_DNS2) 155 if (net_dns_server2.s_addr) { 156 ip_to_string(net_dns_server2, tmp); 157 env_set("dnsip2", tmp); 158 } 159 #endif 160 if (net_nis_domain[0]) 161 env_set("domain", net_nis_domain); 162 163 #if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_TIMEOFFSET) 164 if (net_ntp_time_offset) { 165 sprintf(tmp, "%d", net_ntp_time_offset); 166 env_set("timeoffset", tmp); 167 } 168 #endif 169 #if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_NTPSERVER) 170 if (net_ntp_server.s_addr) { 171 ip_to_string(net_ntp_server, tmp); 172 env_set("ntpserverip", tmp); 173 } 174 #endif 175 } 176 177 static int netboot_common(enum proto_t proto, cmd_tbl_t *cmdtp, int argc, 178 char * const argv[]) 179 { 180 char *s; 181 char *end; 182 int rcode = 0; 183 int size; 184 ulong addr; 185 186 net_boot_file_name_explicit = false; 187 188 /* pre-set load_addr */ 189 s = env_get("loadaddr"); 190 if (s != NULL) 191 load_addr = simple_strtoul(s, NULL, 16); 192 193 switch (argc) { 194 case 1: 195 /* refresh bootfile name from env */ 196 copy_filename(net_boot_file_name, env_get("bootfile"), 197 sizeof(net_boot_file_name)); 198 break; 199 200 case 2: /* 201 * Only one arg - accept two forms: 202 * Just load address, or just boot file name. The latter 203 * form must be written in a format which can not be 204 * mis-interpreted as a valid number. 205 */ 206 addr = simple_strtoul(argv[1], &end, 16); 207 if (end == (argv[1] + strlen(argv[1]))) { 208 load_addr = addr; 209 /* refresh bootfile name from env */ 210 copy_filename(net_boot_file_name, env_get("bootfile"), 211 sizeof(net_boot_file_name)); 212 } else { 213 net_boot_file_name_explicit = true; 214 copy_filename(net_boot_file_name, argv[1], 215 sizeof(net_boot_file_name)); 216 } 217 break; 218 219 case 3: 220 load_addr = simple_strtoul(argv[1], NULL, 16); 221 net_boot_file_name_explicit = true; 222 copy_filename(net_boot_file_name, argv[2], 223 sizeof(net_boot_file_name)); 224 225 break; 226 227 #ifdef CONFIG_CMD_TFTPPUT 228 case 4: 229 if (strict_strtoul(argv[1], 16, &save_addr) < 0 || 230 strict_strtoul(argv[2], 16, &save_size) < 0) { 231 printf("Invalid address/size\n"); 232 return CMD_RET_USAGE; 233 } 234 net_boot_file_name_explicit = true; 235 copy_filename(net_boot_file_name, argv[3], 236 sizeof(net_boot_file_name)); 237 break; 238 #endif 239 default: 240 bootstage_error(BOOTSTAGE_ID_NET_START); 241 return CMD_RET_USAGE; 242 } 243 bootstage_mark(BOOTSTAGE_ID_NET_START); 244 245 size = net_loop(proto); 246 if (size < 0) { 247 bootstage_error(BOOTSTAGE_ID_NET_NETLOOP_OK); 248 return CMD_RET_FAILURE; 249 } 250 bootstage_mark(BOOTSTAGE_ID_NET_NETLOOP_OK); 251 252 /* net_loop ok, update environment */ 253 netboot_update_env(); 254 255 /* done if no file was loaded (no errors though) */ 256 if (size == 0) { 257 bootstage_error(BOOTSTAGE_ID_NET_LOADED); 258 return CMD_RET_SUCCESS; 259 } 260 261 bootstage_mark(BOOTSTAGE_ID_NET_LOADED); 262 263 rcode = bootm_maybe_autostart(cmdtp, argv[0]); 264 265 if (rcode == CMD_RET_SUCCESS) 266 bootstage_mark(BOOTSTAGE_ID_NET_DONE); 267 else 268 bootstage_error(BOOTSTAGE_ID_NET_DONE_ERR); 269 return rcode; 270 } 271 272 #if defined(CONFIG_CMD_PING) 273 static int do_ping(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 274 { 275 if (argc < 2) 276 return CMD_RET_USAGE; 277 278 net_ping_ip = string_to_ip(argv[1]); 279 if (net_ping_ip.s_addr == 0) 280 return CMD_RET_USAGE; 281 282 if (net_loop(PING) < 0) { 283 printf("ping failed; host %s is not alive\n", argv[1]); 284 return CMD_RET_FAILURE; 285 } 286 287 printf("host %s is alive\n", argv[1]); 288 289 return CMD_RET_SUCCESS; 290 } 291 292 U_BOOT_CMD( 293 ping, 2, 1, do_ping, 294 "send ICMP ECHO_REQUEST to network host", 295 "pingAddress" 296 ); 297 #endif 298 299 #if defined(CONFIG_CMD_CDP) 300 301 static void cdp_update_env(void) 302 { 303 char tmp[16]; 304 305 if (cdp_appliance_vlan != htons(-1)) { 306 printf("CDP offered appliance VLAN %d\n", 307 ntohs(cdp_appliance_vlan)); 308 vlan_to_string(cdp_appliance_vlan, tmp); 309 env_set("vlan", tmp); 310 net_our_vlan = cdp_appliance_vlan; 311 } 312 313 if (cdp_native_vlan != htons(-1)) { 314 printf("CDP offered native VLAN %d\n", ntohs(cdp_native_vlan)); 315 vlan_to_string(cdp_native_vlan, tmp); 316 env_set("nvlan", tmp); 317 net_native_vlan = cdp_native_vlan; 318 } 319 } 320 321 int do_cdp(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 322 { 323 int r; 324 325 r = net_loop(CDP); 326 if (r < 0) { 327 printf("cdp failed; perhaps not a CISCO switch?\n"); 328 return CMD_RET_FAILURE; 329 } 330 331 cdp_update_env(); 332 333 return CMD_RET_SUCCESS; 334 } 335 336 U_BOOT_CMD( 337 cdp, 1, 1, do_cdp, 338 "Perform CDP network configuration", 339 "\n" 340 ); 341 #endif 342 343 #if defined(CONFIG_CMD_SNTP) 344 int do_sntp(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 345 { 346 char *toff; 347 348 if (argc < 2) { 349 net_ntp_server = env_get_ip("ntpserverip"); 350 if (net_ntp_server.s_addr == 0) { 351 printf("ntpserverip not set\n"); 352 return CMD_RET_FAILURE; 353 } 354 } else { 355 net_ntp_server = string_to_ip(argv[1]); 356 if (net_ntp_server.s_addr == 0) { 357 printf("Bad NTP server IP address\n"); 358 return CMD_RET_FAILURE; 359 } 360 } 361 362 toff = env_get("timeoffset"); 363 if (toff == NULL) 364 net_ntp_time_offset = 0; 365 else 366 net_ntp_time_offset = simple_strtol(toff, NULL, 10); 367 368 if (net_loop(SNTP) < 0) { 369 printf("SNTP failed: host %pI4 not responding\n", 370 &net_ntp_server); 371 return CMD_RET_FAILURE; 372 } 373 374 return CMD_RET_SUCCESS; 375 } 376 377 U_BOOT_CMD( 378 sntp, 2, 1, do_sntp, 379 "synchronize RTC via network", 380 "[NTP server IP]\n" 381 ); 382 #endif 383 384 #if defined(CONFIG_CMD_DNS) 385 int do_dns(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 386 { 387 if (argc == 1) 388 return CMD_RET_USAGE; 389 390 /* 391 * We should check for a valid hostname: 392 * - Each label must be between 1 and 63 characters long 393 * - the entire hostname has a maximum of 255 characters 394 * - only the ASCII letters 'a' through 'z' (case-insensitive), 395 * the digits '0' through '9', and the hyphen 396 * - cannot begin or end with a hyphen 397 * - no other symbols, punctuation characters, or blank spaces are 398 * permitted 399 * but hey - this is a minimalist implmentation, so only check length 400 * and let the name server deal with things. 401 */ 402 if (strlen(argv[1]) >= 255) { 403 printf("dns error: hostname too long\n"); 404 return CMD_RET_FAILURE; 405 } 406 407 net_dns_resolve = argv[1]; 408 409 if (argc == 3) 410 net_dns_env_var = argv[2]; 411 else 412 net_dns_env_var = NULL; 413 414 if (net_loop(DNS) < 0) { 415 printf("dns lookup of %s failed, check setup\n", argv[1]); 416 return CMD_RET_FAILURE; 417 } 418 419 return CMD_RET_SUCCESS; 420 } 421 422 U_BOOT_CMD( 423 dns, 3, 1, do_dns, 424 "lookup the IP of a hostname", 425 "hostname [envvar]" 426 ); 427 428 #endif /* CONFIG_CMD_DNS */ 429 430 #if defined(CONFIG_CMD_LINK_LOCAL) 431 static int do_link_local(cmd_tbl_t *cmdtp, int flag, int argc, 432 char * const argv[]) 433 { 434 char tmp[22]; 435 436 if (net_loop(LINKLOCAL) < 0) 437 return CMD_RET_FAILURE; 438 439 net_gateway.s_addr = 0; 440 ip_to_string(net_gateway, tmp); 441 env_set("gatewayip", tmp); 442 443 ip_to_string(net_netmask, tmp); 444 env_set("netmask", tmp); 445 446 ip_to_string(net_ip, tmp); 447 env_set("ipaddr", tmp); 448 env_set("llipaddr", tmp); /* store this for next time */ 449 450 return CMD_RET_SUCCESS; 451 } 452 453 U_BOOT_CMD( 454 linklocal, 1, 1, do_link_local, 455 "acquire a network IP address using the link-local protocol", 456 "" 457 ); 458 459 #endif /* CONFIG_CMD_LINK_LOCAL */ 460 461 #if defined(CONFIG_CMD_NCSI) 462 static int do_ncsi(cmd_tbl_t *cmdtp, int flag, int argc, 463 char * const argv[]) 464 { 465 if (net_loop(NCSI) < 0) 466 return CMD_RET_FAILURE; 467 468 return CMD_RET_SUCCESS; 469 } 470 471 U_BOOT_CMD( 472 ncsi, 1, 1, do_ncsi, 473 "Configure attached NIC via NC-SI", 474 "" 475 ); 476 477 #endif /* CONFIG_CMD_NCSI */ 478