2018-05-07 05:58:06 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0+
|
2011-08-31 23:37:30 +08:00
|
|
|
/*
|
|
|
|
* Copyright 2010-2011 Calxeda, Inc.
|
2014-08-01 08:39:59 +08:00
|
|
|
* Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
|
2011-08-31 23:37:30 +08:00
|
|
|
*/
|
2013-07-08 15:37:19 +08:00
|
|
|
|
2011-08-31 23:37:30 +08:00
|
|
|
#include <common.h>
|
|
|
|
#include <command.h>
|
2019-12-29 01:44:44 +08:00
|
|
|
#include <fs.h>
|
2019-12-29 01:44:43 +08:00
|
|
|
#include <net.h>
|
2011-08-31 23:37:30 +08:00
|
|
|
|
2019-11-25 16:07:37 +08:00
|
|
|
#include "pxe_utils.h"
|
2011-08-31 23:37:30 +08:00
|
|
|
|
2019-11-25 16:07:37 +08:00
|
|
|
#ifdef CONFIG_CMD_NET
|
2012-12-03 11:00:28 +08:00
|
|
|
const char *pxe_default_paths[] = {
|
2013-06-25 06:21:04 +08:00
|
|
|
#ifdef CONFIG_SYS_SOC
|
2019-05-02 21:29:12 +08:00
|
|
|
#ifdef CONFIG_SYS_BOARD
|
|
|
|
"default-" CONFIG_SYS_ARCH "-" CONFIG_SYS_SOC "-" CONFIG_SYS_BOARD,
|
|
|
|
#endif
|
2012-12-03 11:00:28 +08:00
|
|
|
"default-" CONFIG_SYS_ARCH "-" CONFIG_SYS_SOC,
|
2013-06-25 06:21:04 +08:00
|
|
|
#endif
|
2012-12-03 11:00:28 +08:00
|
|
|
"default-" CONFIG_SYS_ARCH,
|
|
|
|
"default",
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
2020-05-11 01:40:03 +08:00
|
|
|
static int do_get_tftp(struct cmd_tbl *cmdtp, const char *file_path,
|
|
|
|
char *file_addr)
|
2012-05-25 18:47:39 +08:00
|
|
|
{
|
|
|
|
char *tftp_argv[] = {"tftp", NULL, NULL, NULL};
|
|
|
|
|
|
|
|
tftp_argv[1] = file_addr;
|
2012-12-03 11:00:21 +08:00
|
|
|
tftp_argv[2] = (void *)file_path;
|
2012-05-25 18:47:39 +08:00
|
|
|
|
2013-10-07 21:51:48 +08:00
|
|
|
if (do_tftpb(cmdtp, 0, 3, tftp_argv))
|
2012-05-25 18:47:39 +08:00
|
|
|
return -ENOENT;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
2014-02-06 11:49:20 +08:00
|
|
|
|
2011-08-31 23:37:30 +08:00
|
|
|
/*
|
|
|
|
* Looks for a pxe file with a name based on the pxeuuid environment variable.
|
|
|
|
*
|
|
|
|
* Returns 1 on success or < 0 on error.
|
|
|
|
*/
|
2020-05-11 01:40:03 +08:00
|
|
|
static int pxe_uuid_path(struct cmd_tbl *cmdtp, unsigned long pxefile_addr_r)
|
2011-08-31 23:37:30 +08:00
|
|
|
{
|
|
|
|
char *uuid_str;
|
|
|
|
|
|
|
|
uuid_str = from_env("pxeuuid");
|
|
|
|
|
|
|
|
if (!uuid_str)
|
|
|
|
return -ENOENT;
|
|
|
|
|
2013-10-07 21:51:48 +08:00
|
|
|
return get_pxelinux_path(cmdtp, uuid_str, pxefile_addr_r);
|
2011-08-31 23:37:30 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Looks for a pxe file with a name based on the 'ethaddr' environment
|
|
|
|
* variable.
|
|
|
|
*
|
|
|
|
* Returns 1 on success or < 0 on error.
|
|
|
|
*/
|
2020-05-11 01:40:03 +08:00
|
|
|
static int pxe_mac_path(struct cmd_tbl *cmdtp, unsigned long pxefile_addr_r)
|
2011-08-31 23:37:30 +08:00
|
|
|
{
|
|
|
|
char mac_str[21];
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = format_mac_pxe(mac_str, sizeof(mac_str));
|
|
|
|
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
|
2013-10-07 21:51:48 +08:00
|
|
|
return get_pxelinux_path(cmdtp, mac_str, pxefile_addr_r);
|
2011-08-31 23:37:30 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Looks for pxe files with names based on our IP address. See pxelinux
|
|
|
|
* documentation for details on what these file names look like. We match
|
|
|
|
* that exactly.
|
|
|
|
*
|
|
|
|
* Returns 1 on success or < 0 on error.
|
|
|
|
*/
|
2020-05-11 01:40:03 +08:00
|
|
|
static int pxe_ipaddr_paths(struct cmd_tbl *cmdtp, unsigned long pxefile_addr_r)
|
2011-08-31 23:37:30 +08:00
|
|
|
{
|
|
|
|
char ip_addr[9];
|
|
|
|
int mask_pos, err;
|
|
|
|
|
2015-04-08 14:41:01 +08:00
|
|
|
sprintf(ip_addr, "%08X", ntohl(net_ip.s_addr));
|
2011-08-31 23:37:30 +08:00
|
|
|
|
|
|
|
for (mask_pos = 7; mask_pos >= 0; mask_pos--) {
|
2013-10-07 21:51:48 +08:00
|
|
|
err = get_pxelinux_path(cmdtp, ip_addr, pxefile_addr_r);
|
2011-08-31 23:37:30 +08:00
|
|
|
|
|
|
|
if (err > 0)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
ip_addr[mask_pos] = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Entry point for the 'pxe get' command.
|
|
|
|
* This Follows pxelinux's rules to download a config file from a tftp server.
|
|
|
|
* The file is stored at the location given by the pxefile_addr_r environment
|
|
|
|
* variable, which must be set.
|
|
|
|
*
|
|
|
|
* UUID comes from pxeuuid env variable, if defined
|
|
|
|
* MAC addr comes from ethaddr env variable, if defined
|
|
|
|
* IP
|
|
|
|
*
|
|
|
|
* see http://syslinux.zytor.com/wiki/index.php/PXELINUX
|
|
|
|
*
|
|
|
|
* Returns 0 on success or 1 on error.
|
|
|
|
*/
|
|
|
|
static int
|
2020-05-11 01:40:03 +08:00
|
|
|
do_pxe_get(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
|
2011-08-31 23:37:30 +08:00
|
|
|
{
|
|
|
|
char *pxefile_addr_str;
|
2012-03-05 16:12:28 +08:00
|
|
|
unsigned long pxefile_addr_r;
|
2012-12-03 11:00:28 +08:00
|
|
|
int err, i = 0;
|
2011-08-31 23:37:30 +08:00
|
|
|
|
2012-05-25 18:47:39 +08:00
|
|
|
do_getfile = do_get_tftp;
|
|
|
|
|
2011-08-31 23:37:30 +08:00
|
|
|
if (argc != 1)
|
2011-12-10 16:44:01 +08:00
|
|
|
return CMD_RET_USAGE;
|
2011-08-31 23:37:30 +08:00
|
|
|
|
|
|
|
pxefile_addr_str = from_env("pxefile_addr_r");
|
|
|
|
|
|
|
|
if (!pxefile_addr_str)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
err = strict_strtoul(pxefile_addr_str, 16,
|
2019-11-25 16:07:41 +08:00
|
|
|
(unsigned long *)&pxefile_addr_r);
|
2011-08-31 23:37:30 +08:00
|
|
|
if (err < 0)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Keep trying paths until we successfully get a file we're looking
|
|
|
|
* for.
|
|
|
|
*/
|
2015-04-14 04:54:25 +08:00
|
|
|
if (pxe_uuid_path(cmdtp, pxefile_addr_r) > 0 ||
|
|
|
|
pxe_mac_path(cmdtp, pxefile_addr_r) > 0 ||
|
|
|
|
pxe_ipaddr_paths(cmdtp, pxefile_addr_r) > 0) {
|
2011-08-31 23:37:30 +08:00
|
|
|
printf("Config file found\n");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-12-03 11:00:28 +08:00
|
|
|
while (pxe_default_paths[i]) {
|
2013-10-07 21:51:48 +08:00
|
|
|
if (get_pxelinux_path(cmdtp, pxe_default_paths[i],
|
2015-04-14 04:54:25 +08:00
|
|
|
pxefile_addr_r) > 0) {
|
2012-12-03 11:00:28 +08:00
|
|
|
printf("Config file found\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
2011-08-31 23:37:30 +08:00
|
|
|
printf("Config file not found\n");
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Boots a system using a pxe file
|
|
|
|
*
|
|
|
|
* Returns 0 on success, 1 on error.
|
|
|
|
*/
|
|
|
|
static int
|
2020-05-11 01:40:03 +08:00
|
|
|
do_pxe_boot(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
|
2011-08-31 23:37:30 +08:00
|
|
|
{
|
|
|
|
unsigned long pxefile_addr_r;
|
|
|
|
struct pxe_menu *cfg;
|
|
|
|
char *pxefile_addr_str;
|
|
|
|
|
2012-05-25 18:47:39 +08:00
|
|
|
do_getfile = do_get_tftp;
|
|
|
|
|
2011-08-31 23:37:30 +08:00
|
|
|
if (argc == 1) {
|
|
|
|
pxefile_addr_str = from_env("pxefile_addr_r");
|
|
|
|
if (!pxefile_addr_str)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
} else if (argc == 2) {
|
|
|
|
pxefile_addr_str = argv[1];
|
|
|
|
} else {
|
2011-12-10 16:44:01 +08:00
|
|
|
return CMD_RET_USAGE;
|
2011-08-31 23:37:30 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (strict_strtoul(pxefile_addr_str, 16, &pxefile_addr_r) < 0) {
|
|
|
|
printf("Invalid pxefile address: %s\n", pxefile_addr_str);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2015-04-14 04:54:25 +08:00
|
|
|
cfg = parse_pxefile(cmdtp, pxefile_addr_r);
|
2011-08-31 23:37:30 +08:00
|
|
|
|
2019-11-25 16:07:41 +08:00
|
|
|
if (!cfg) {
|
2011-08-31 23:37:30 +08:00
|
|
|
printf("Error parsing config file\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2013-09-24 21:05:08 +08:00
|
|
|
handle_pxe_menu(cmdtp, cfg);
|
2011-08-31 23:37:30 +08:00
|
|
|
|
|
|
|
destroy_pxe_menu(cfg);
|
|
|
|
|
2015-04-08 14:41:02 +08:00
|
|
|
copy_filename(net_boot_file_name, "", sizeof(net_boot_file_name));
|
pxe: clear Bootfile before returning
When "pxe boot" downloads the initrd/kernel/DTB, netboot_common() saves
the downloaded filename to global variable BootFile. If the boot
operation is aborted, this global state is not cleared. If "dhcp" is
executed later without any arguments, BootFile is not cleared, and when
the DHCP response is received, BootpCopyNetParams() writes the value into
environment variable bootfile.
This causes the following scenario:
* Boot script executes dhcp; pxe get; pxe boot
* User CTRL-C's the PXE menu, which causes the first menu item to be
booted, which causes some file to be downloaded.
(This boot-on-CTRL-C behaviour is arguably a bug too, but it's a
separate bug and the bug this patch fixes would still exist if the user
simply waited to press CTRL-C until "pxe boot" started downloading
files)
* User CTRL-C's the file downloads, but the filename is still written to
the bootfile environment variable.
* User re-runs the boot command, which in my case executes "dhcp; pxe get;
pxe boot" again, and "dhcp" picks up the saved bootfile environment
variable and proceeds to download a file that it shouldn't.
To solve this, modify the implementation of "pxe get" to clear BootFile
if the whole boot operation fails, which avoids this whole mess.
An alternative would be to modify netboot_common() such that the no-
arguments case explicitly clears the global variable BootFile. However,
that would prevent the following command sequences from working:
$ dhcp filename # downloads "filename"
$ dhcp # downloads $bootfile, i.e. "filename"
or:
$ setenv bootfile filename
$ dhcp # downloads $bootfile, i.e. "filename"
... and I assume someone relies on U-Boot working that way.
Signed-off-by: Stephen Warren <swarren@nvidia.com>
Acked-by: Joe Hershberger <joe.hershberger@ni.com>
2014-07-23 08:06:46 +08:00
|
|
|
|
2011-08-31 23:37:30 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-05-11 01:40:03 +08:00
|
|
|
static struct cmd_tbl cmd_pxe_sub[] = {
|
2011-08-31 23:37:30 +08:00
|
|
|
U_BOOT_CMD_MKENT(get, 1, 1, do_pxe_get, "", ""),
|
|
|
|
U_BOOT_CMD_MKENT(boot, 2, 1, do_pxe_boot, "", "")
|
|
|
|
};
|
|
|
|
|
2020-09-16 15:13:56 +08:00
|
|
|
static void __maybe_unused pxe_reloc(void)
|
|
|
|
{
|
|
|
|
static int relocated_pxe;
|
|
|
|
|
|
|
|
if (!relocated_pxe) {
|
|
|
|
fixup_cmdtable(cmd_pxe_sub, ARRAY_SIZE(cmd_pxe_sub));
|
|
|
|
relocated_pxe = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-11 01:40:03 +08:00
|
|
|
static int do_pxe(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
|
2011-08-31 23:37:30 +08:00
|
|
|
{
|
2020-05-11 01:40:03 +08:00
|
|
|
struct cmd_tbl *cp;
|
2011-08-31 23:37:30 +08:00
|
|
|
|
2020-09-16 15:13:56 +08:00
|
|
|
#if defined(CONFIG_NEEDS_MANUAL_RELOC)
|
|
|
|
pxe_reloc();
|
|
|
|
#endif
|
|
|
|
|
2011-08-31 23:37:30 +08:00
|
|
|
if (argc < 2)
|
2011-12-10 16:44:01 +08:00
|
|
|
return CMD_RET_USAGE;
|
2011-08-31 23:37:30 +08:00
|
|
|
|
2013-10-19 02:04:42 +08:00
|
|
|
is_pxe = true;
|
|
|
|
|
2011-08-31 23:37:30 +08:00
|
|
|
/* drop initial "pxe" arg */
|
|
|
|
argc--;
|
|
|
|
argv++;
|
|
|
|
|
|
|
|
cp = find_cmd_tbl(argv[0], cmd_pxe_sub, ARRAY_SIZE(cmd_pxe_sub));
|
|
|
|
|
|
|
|
if (cp)
|
|
|
|
return cp->cmd(cmdtp, flag, argc, argv);
|
|
|
|
|
2011-12-10 16:44:01 +08:00
|
|
|
return CMD_RET_USAGE;
|
2011-08-31 23:37:30 +08:00
|
|
|
}
|
|
|
|
|
2019-11-25 16:07:41 +08:00
|
|
|
U_BOOT_CMD(pxe, 3, 1, do_pxe,
|
|
|
|
"commands to get and boot from pxe files",
|
|
|
|
"get - try to retrieve a pxe file using tftp\n"
|
|
|
|
"pxe boot [pxefile_addr_r] - boot from the pxe file at pxefile_addr_r\n"
|
2011-08-31 23:37:30 +08:00
|
|
|
);
|
2014-02-06 11:49:20 +08:00
|
|
|
#endif
|