Skip to content
Snippets Groups Projects
pxe.c 37.3 KiB
Newer Older
Jason Hobbs's avatar
Jason Hobbs committed
/*
 * Copyright 2010-2011 Calxeda, Inc.
 * Copyright (c) 2014, NVIDIA CORPORATION.  All rights reserved.
Jason Hobbs's avatar
Jason Hobbs committed
 *
 * SPDX-License-Identifier:	GPL-2.0+
Jason Hobbs's avatar
Jason Hobbs committed
 */
Jason Hobbs's avatar
Jason Hobbs committed
#include <common.h>
#include <command.h>
#include <malloc.h>
Jason Hobbs's avatar
Jason Hobbs committed
#include <linux/string.h>
#include <linux/ctype.h>
#include <errno.h>
#include <linux/list.h>
#include <asm/io.h>
Jason Hobbs's avatar
Jason Hobbs committed

#include "menu.h"
Jason Hobbs's avatar
Jason Hobbs committed

#define MAX_TFTP_PATH_LEN 127

const char *pxe_default_paths[] = {
#ifdef CONFIG_SYS_SOC
	"default-" CONFIG_SYS_ARCH "-" CONFIG_SYS_SOC,
	"default-" CONFIG_SYS_ARCH,
	"default",
	NULL
};

static bool is_pxe;

Jason Hobbs's avatar
Jason Hobbs committed
/*
 * Like getenv, but prints an error if envvar isn't defined in the
 * environment.  It always returns what getenv does, so it can be used in
 * place of getenv without changing error handling otherwise.
 */
static char *from_env(const char *envvar)
Jason Hobbs's avatar
Jason Hobbs committed
{
	char *ret;

	ret = getenv(envvar);

	if (!ret)
		printf("missing environment variable: %s\n", envvar);

	return ret;
}

#ifdef CONFIG_CMD_NET
Jason Hobbs's avatar
Jason Hobbs committed
/*
 * Convert an ethaddr from the environment to the format used by pxelinux
 * filenames based on mac addresses. Convert's ':' to '-', and adds "01-" to
 * the beginning of the ethernet address to indicate a hardware type of
 * Ethernet. Also converts uppercase hex characters into lowercase, to match
 * pxelinux's behavior.
 *
 * Returns 1 for success, -ENOENT if 'ethaddr' is undefined in the
 * environment, or some other value < 0 on error.
 */
static int format_mac_pxe(char *outbuf, size_t outbuf_len)
{
	uchar ethaddr[6];
Jason Hobbs's avatar
Jason Hobbs committed

	if (outbuf_len < 21) {
		printf("outbuf is too small (%zd < 21)\n", outbuf_len);
Jason Hobbs's avatar
Jason Hobbs committed

		return -EINVAL;
	}

	if (!eth_getenv_enetaddr_by_index("eth", eth_get_dev_index(),
					  ethaddr))
		return -ENOENT;
Jason Hobbs's avatar
Jason Hobbs committed

	sprintf(outbuf, "01-%02x-%02x-%02x-%02x-%02x-%02x",
		ethaddr[0], ethaddr[1], ethaddr[2],
		ethaddr[3], ethaddr[4], ethaddr[5]);
Jason Hobbs's avatar
Jason Hobbs committed

	return 1;
}
Jason Hobbs's avatar
Jason Hobbs committed

/*
 * Returns the directory the file specified in the bootfile env variable is
 * in. If bootfile isn't defined in the environment, return NULL, which should
 * be interpreted as "don't prepend anything to paths".
 */
Rob Herring's avatar
Rob Herring committed
static int get_bootfile_path(const char *file_path, char *bootfile_path,
			     size_t bootfile_path_size)
Jason Hobbs's avatar
Jason Hobbs committed
{
	char *bootfile, *last_slash;
Rob Herring's avatar
Rob Herring committed
	size_t path_len = 0;

	/* Only syslinux allows absolute paths */
	if (file_path[0] == '/' && !is_pxe)
Rob Herring's avatar
Rob Herring committed
		goto ret;
Jason Hobbs's avatar
Jason Hobbs committed

	bootfile = from_env("bootfile");

Rob Herring's avatar
Rob Herring committed
	if (!bootfile)
		goto ret;
Jason Hobbs's avatar
Jason Hobbs committed

	last_slash = strrchr(bootfile, '/');

Rob Herring's avatar
Rob Herring committed
	if (last_slash == NULL)
		goto ret;
Jason Hobbs's avatar
Jason Hobbs committed

	path_len = (last_slash - bootfile) + 1;

	if (bootfile_path_size < path_len) {
		printf("bootfile_path too small. (%zd < %zd)\n",
Jason Hobbs's avatar
Jason Hobbs committed
				bootfile_path_size, path_len);

		return -1;
	}

	strncpy(bootfile_path, bootfile, path_len);

Rob Herring's avatar
Rob Herring committed
 ret:
Jason Hobbs's avatar
Jason Hobbs committed
	bootfile_path[path_len] = '\0';

	return 1;
}

static int (*do_getfile)(cmd_tbl_t *cmdtp, const char *file_path, char *file_addr);
static int do_get_tftp(cmd_tbl_t *cmdtp, const char *file_path, char *file_addr)
{
	char *tftp_argv[] = {"tftp", NULL, NULL, NULL};

	tftp_argv[1] = file_addr;
	tftp_argv[2] = (void *)file_path;
	if (do_tftpb(cmdtp, 0, 3, tftp_argv))
static int do_get_ext2(cmd_tbl_t *cmdtp, const char *file_path, char *file_addr)
{
#ifdef CONFIG_CMD_EXT2
	fs_argv[0] = "ext2load";
	fs_argv[3] = file_addr;
	fs_argv[4] = (void *)file_path;
	if (!do_ext2load(cmdtp, 0, 5, fs_argv))
static int do_get_fat(cmd_tbl_t *cmdtp, const char *file_path, char *file_addr)
{
#ifdef CONFIG_CMD_FAT
	fs_argv[0] = "fatload";
	fs_argv[3] = file_addr;
	fs_argv[4] = (void *)file_path;
	if (!do_fat_fsload(cmdtp, 0, 5, fs_argv))
static int do_get_any(cmd_tbl_t *cmdtp, const char *file_path, char *file_addr)
{
#ifdef CONFIG_CMD_FS_GENERIC
	fs_argv[0] = "load";
	fs_argv[3] = file_addr;
	fs_argv[4] = (void *)file_path;

	if (!do_load(cmdtp, 0, 5, fs_argv, FS_TYPE_ANY))
		return 1;
#endif
	return -ENOENT;
}

Jason Hobbs's avatar
Jason Hobbs committed
/*
 * As in pxelinux, paths to files referenced from files we retrieve are
 * relative to the location of bootfile. get_relfile takes such a path and
 * joins it with the bootfile path to get the full path to the target file. If
 * the bootfile path is NULL, we use file_path as is.
 *
 * Returns 1 for success, or < 0 on error.
 */
static int get_relfile(cmd_tbl_t *cmdtp, const char *file_path,
	unsigned long file_addr)
Jason Hobbs's avatar
Jason Hobbs committed
{
	size_t path_len;
	char relfile[MAX_TFTP_PATH_LEN+1];
Jason Hobbs's avatar
Jason Hobbs committed
	int err;

Loading
Loading full blame...