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 break; 196 197 case 2: /* 198 * Only one arg - accept two forms: 199 * Just load address, or just boot file name. The latter 200 * form must be written in a format which can not be 201 * mis-interpreted as a valid number. 202 */ 203 addr = simple_strtoul(argv[1], &end, 16); 204 if (end == (argv[1] + strlen(argv[1]))) { 205 load_addr = addr; 206 } else { 207 net_boot_file_name_explicit = true; 208 copy_filename(net_boot_file_name, argv[1], 209 sizeof(net_boot_file_name)); 210 } 211 break; 212 213 case 3: 214 load_addr = simple_strtoul(argv[1], NULL, 16); 215 net_boot_file_name_explicit = true; 216 copy_filename(net_boot_file_name, argv[2], 217 sizeof(net_boot_file_name)); 218 219 break; 220 221 #ifdef CONFIG_CMD_TFTPPUT 222 case 4: 223 if (strict_strtoul(argv[1], 16, &save_addr) < 0 || 224 strict_strtoul(argv[2], 16, &save_size) < 0) { 225 printf("Invalid address/size\n"); 226 return CMD_RET_USAGE; 227 } 228 net_boot_file_name_explicit = true; 229 copy_filename(net_boot_file_name, argv[3], 230 sizeof(net_boot_file_name)); 231 break; 232 #endif 233 default: 234 bootstage_error(BOOTSTAGE_ID_NET_START); 235 return CMD_RET_USAGE; 236 } 237 bootstage_mark(BOOTSTAGE_ID_NET_START); 238 239 size = net_loop(proto); 240 if (size < 0) { 241 bootstage_error(BOOTSTAGE_ID_NET_NETLOOP_OK); 242 return CMD_RET_FAILURE; 243 } 244 bootstage_mark(BOOTSTAGE_ID_NET_NETLOOP_OK); 245 246 /* net_loop ok, update environment */ 247 netboot_update_env(); 248 249 /* done if no file was loaded (no errors though) */ 250 if (size == 0) { 251 bootstage_error(BOOTSTAGE_ID_NET_LOADED); 252 return CMD_RET_SUCCESS; 253 } 254 255 bootstage_mark(BOOTSTAGE_ID_NET_LOADED); 256 257 rcode = bootm_maybe_autostart(cmdtp, argv[0]); 258 259 if (rcode == CMD_RET_SUCCESS) 260 bootstage_mark(BOOTSTAGE_ID_NET_DONE); 261 else 262 bootstage_error(BOOTSTAGE_ID_NET_DONE_ERR); 263 return rcode; 264 } 265 266 #if defined(CONFIG_CMD_PING) 267 static int do_ping(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 268 { 269 if (argc < 2) 270 return CMD_RET_USAGE; 271 272 net_ping_ip = string_to_ip(argv[1]); 273 if (net_ping_ip.s_addr == 0) 274 return CMD_RET_USAGE; 275 276 if (net_loop(PING) < 0) { 277 printf("ping failed; host %s is not alive\n", argv[1]); 278 return CMD_RET_FAILURE; 279 } 280 281 printf("host %s is alive\n", argv[1]); 282 283 return CMD_RET_SUCCESS; 284 } 285 286 U_BOOT_CMD( 287 ping, 2, 1, do_ping, 288 "send ICMP ECHO_REQUEST to network host", 289 "pingAddress" 290 ); 291 #endif 292 293 #if defined(CONFIG_CMD_CDP) 294 295 static void cdp_update_env(void) 296 { 297 char tmp[16]; 298 299 if (cdp_appliance_vlan != htons(-1)) { 300 printf("CDP offered appliance VLAN %d\n", 301 ntohs(cdp_appliance_vlan)); 302 vlan_to_string(cdp_appliance_vlan, tmp); 303 env_set("vlan", tmp); 304 net_our_vlan = cdp_appliance_vlan; 305 } 306 307 if (cdp_native_vlan != htons(-1)) { 308 printf("CDP offered native VLAN %d\n", ntohs(cdp_native_vlan)); 309 vlan_to_string(cdp_native_vlan, tmp); 310 env_set("nvlan", tmp); 311 net_native_vlan = cdp_native_vlan; 312 } 313 } 314 315 int do_cdp(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 316 { 317 int r; 318 319 r = net_loop(CDP); 320 if (r < 0) { 321 printf("cdp failed; perhaps not a CISCO switch?\n"); 322 return CMD_RET_FAILURE; 323 } 324 325 cdp_update_env(); 326 327 return CMD_RET_SUCCESS; 328 } 329 330 U_BOOT_CMD( 331 cdp, 1, 1, do_cdp, 332 "Perform CDP network configuration", 333 "\n" 334 ); 335 #endif 336 337 #if defined(CONFIG_CMD_SNTP) 338 int do_sntp(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 339 { 340 char *toff; 341 342 if (argc < 2) { 343 net_ntp_server = env_get_ip("ntpserverip"); 344 if (net_ntp_server.s_addr == 0) { 345 printf("ntpserverip not set\n"); 346 return CMD_RET_FAILURE; 347 } 348 } else { 349 net_ntp_server = string_to_ip(argv[1]); 350 if (net_ntp_server.s_addr == 0) { 351 printf("Bad NTP server IP address\n"); 352 return CMD_RET_FAILURE; 353 } 354 } 355 356 toff = env_get("timeoffset"); 357 if (toff == NULL) 358 net_ntp_time_offset = 0; 359 else 360 net_ntp_time_offset = simple_strtol(toff, NULL, 10); 361 362 if (net_loop(SNTP) < 0) { 363 printf("SNTP failed: host %pI4 not responding\n", 364 &net_ntp_server); 365 return CMD_RET_FAILURE; 366 } 367 368 return CMD_RET_SUCCESS; 369 } 370 371 U_BOOT_CMD( 372 sntp, 2, 1, do_sntp, 373 "synchronize RTC via network", 374 "[NTP server IP]\n" 375 ); 376 #endif 377 378 #if defined(CONFIG_CMD_DNS) 379 int do_dns(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 380 { 381 if (argc == 1) 382 return CMD_RET_USAGE; 383 384 /* 385 * We should check for a valid hostname: 386 * - Each label must be between 1 and 63 characters long 387 * - the entire hostname has a maximum of 255 characters 388 * - only the ASCII letters 'a' through 'z' (case-insensitive), 389 * the digits '0' through '9', and the hyphen 390 * - cannot begin or end with a hyphen 391 * - no other symbols, punctuation characters, or blank spaces are 392 * permitted 393 * but hey - this is a minimalist implmentation, so only check length 394 * and let the name server deal with things. 395 */ 396 if (strlen(argv[1]) >= 255) { 397 printf("dns error: hostname too long\n"); 398 return CMD_RET_FAILURE; 399 } 400 401 net_dns_resolve = argv[1]; 402 403 if (argc == 3) 404 net_dns_env_var = argv[2]; 405 else 406 net_dns_env_var = NULL; 407 408 if (net_loop(DNS) < 0) { 409 printf("dns lookup of %s failed, check setup\n", argv[1]); 410 return CMD_RET_FAILURE; 411 } 412 413 return CMD_RET_SUCCESS; 414 } 415 416 U_BOOT_CMD( 417 dns, 3, 1, do_dns, 418 "lookup the IP of a hostname", 419 "hostname [envvar]" 420 ); 421 422 #endif /* CONFIG_CMD_DNS */ 423 424 #if defined(CONFIG_CMD_LINK_LOCAL) 425 static int do_link_local(cmd_tbl_t *cmdtp, int flag, int argc, 426 char * const argv[]) 427 { 428 char tmp[22]; 429 430 if (net_loop(LINKLOCAL) < 0) 431 return CMD_RET_FAILURE; 432 433 net_gateway.s_addr = 0; 434 ip_to_string(net_gateway, tmp); 435 env_set("gatewayip", tmp); 436 437 ip_to_string(net_netmask, tmp); 438 env_set("netmask", tmp); 439 440 ip_to_string(net_ip, tmp); 441 env_set("ipaddr", tmp); 442 env_set("llipaddr", tmp); /* store this for next time */ 443 444 return CMD_RET_SUCCESS; 445 } 446 447 U_BOOT_CMD( 448 linklocal, 1, 1, do_link_local, 449 "acquire a network IP address using the link-local protocol", 450 "" 451 ); 452 453 #endif /* CONFIG_CMD_LINK_LOCAL */ 454