diff --git a/arch/arm/config.mk b/arch/arm/config.mk index 45f9dca..4097793 100644 --- a/arch/arm/config.mk +++ b/arch/arm/config.mk @@ -31,7 +31,7 @@ CONFIG_STANDALONE_LOAD_ADDR = 0xc100000 endif endif -PLATFORM_CPPFLAGS += -DCONFIG_ARM -D__ARM__ +PLATFORM_CPPFLAGS += -DCONFIG_ARM -D__ARM__ -DCONFIG_BOOTP_VENDOREX -DCONFIG_BOOTP_VENDOREX_PXE_SHARED # Explicitly specifiy 32-bit ARM ISA since toolchain default can be -mthumb: PF_CPPFLAGS_ARM := $(call cc-option,-marm,) diff --git a/common/cmd_pxe.c b/common/cmd_pxe.c index 907a464..ae6efe6 100644 --- a/common/cmd_pxe.c +++ b/common/cmd_pxe.c @@ -23,6 +23,9 @@ #include #include "menu.h" +#if defined(CONFIG_BOOTP_VENDOREX) && defined(CONFIG_BOOTP_VENDOREX_PXE_SHARED) +#include "../net/magic.h" +#endif #define MAX_TFTP_PATH_LEN 127 @@ -94,18 +97,36 @@ static int format_mac_pxe(char *outbuf, size_t outbuf_len) return 1; } +#if defined(CONFIG_BOOTP_VENDOREX) && defined(CONFIG_BOOTP_VENDOREX_PXE_SHARED) +/* + * Returns the directory the file specified in the bootfile env variable is + * in, unless DHCP MAGIC was received and we have a PathPrefix value, in which + * case the bootenv (if it was specified) is overridden. If bootfile + * isn't defined in the environment or obtained from PathPrefix DHCP + * vendor extension, return NULL, which should be interpreted as + * "don't prepend anything to paths". + */ +#else /* * 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". */ +#endif static int get_bootfile_path(char *bootfile_path, size_t bootfile_path_size) { char *bootfile, *last_slash; size_t path_len; +#if defined(CONFIG_BOOTP_VENDOREX) && defined(CONFIG_BOOTP_VENDOREX_PXE_SHARED) + char *path_prefix = getenv(PXE_PATH_PREFIX); - bootfile = from_env("bootfile"); + /* Override the initial environment */ + if (path_prefix) { + setenv("bootfile", path_prefix); + } +#endif + bootfile = from_env("bootfile"); if (!bootfile) { bootfile_path[0] = '\0'; return 1; @@ -241,6 +262,25 @@ static int get_pxelinux_path(char *file, void *pxefile_addr_r) return get_pxe_file(path, pxefile_addr_r); } +#if defined(CONFIG_BOOTP_VENDOREX) && defined(CONFIG_BOOTP_VENDOREX_PXE_SHARED) +/* + * Attempt to load a config file obtained from DHCP vendor extension option + * + * Returns 1 on success or < 0 on error. + */ +static int pxe_dhcp_config_path(void *pxefile_addr_r) +{ + char *name = getenv(PXE_CONFIG_FILE); + int retval = -ENOENT; + + if (name && *name) { + retval = get_pxelinux_path(name, pxefile_addr_r); + } + + return retval; +} +#endif + /* * Looks for a pxe file with a name based on the pxeuuid environment variable. * @@ -342,7 +382,12 @@ do_pxe_get(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) * Keep trying paths until we successfully get a file we're looking * for. */ +#if defined(CONFIG_BOOTP_VENDOREX) && defined(CONFIG_BOOTP_VENDOREX_PXE_SHARED) + if (pxe_dhcp_config_path(pxefile_addr_r) > 0 + || pxe_uuid_path(pxefile_addr_r) > 0 +#else if (pxe_uuid_path(pxefile_addr_r) > 0 +#endif || pxe_mac_path(pxefile_addr_r) > 0 || pxe_ipaddr_paths(pxefile_addr_r) > 0 || get_pxelinux_path("default", pxefile_addr_r) > 0) { diff --git a/common/main.c b/common/main.c index e96c95a..da41756 100644 --- a/common/main.c +++ b/common/main.c @@ -42,6 +42,10 @@ #include #include +#if defined(CONFIG_BOOTP_VENDOREX) && defined(CONFIG_BOOTP_VENDOREX_PXE_SHARED) +#include "../net/magic.h" +#endif + #if defined(CONFIG_SILENT_CONSOLE) || defined(CONFIG_POST) || defined(CONFIG_CMDLINE_EDITING) DECLARE_GLOBAL_DATA_PTR; #endif @@ -488,6 +492,13 @@ void init_cmd_timeout(void) */ void reset_cmd_timeout(void) { +#if defined(CONFIG_BOOTP_VENDOREX) && defined(CONFIG_BOOTP_VENDOREX_PXE_SHARED) + char *pxe_reboot_time = getenv(PXE_REBOOT_TIME); + + if (pxe_reboot_time && *pxe_reboot_time) { + retry_time = atoi(pxe_reboot_time); + } +#endif endtime = endtick(retry_time); } #endif diff --git a/net/Makefile b/net/Makefile index 0544f6b..9a56e0d 100644 --- a/net/Makefile +++ b/net/Makefile @@ -30,6 +30,7 @@ LIB = $(obj)libnet.o COBJS-$(CONFIG_CMD_NET) += bootp.o COBJS-$(CONFIG_CMD_DNS) += dns.o COBJS-$(CONFIG_CMD_NET) += eth.o +COBJS-$(CONFIG_CMD_NET) += magic.o COBJS-$(CONFIG_CMD_NET) += net.o COBJS-$(CONFIG_CMD_NFS) += nfs.o COBJS-$(CONFIG_CMD_RARP) += rarp.o diff --git a/net/bootp.c b/net/bootp.c index 34124b8..ed906f8 100644 --- a/net/bootp.c +++ b/net/bootp.c @@ -68,6 +68,9 @@ static char *dhcpmsg2str(int type) #if defined(CONFIG_BOOTP_VENDOREX) extern u8 *dhcp_vendorex_prep (u8 *e); /*rtn new e after add own opts. */ extern u8 *dhcp_vendorex_proc (u8 *e); /*rtn next e if mine,else NULL */ +#if defined(CONFIG_BOOTP_VENDOREX_PXE_SHARED) +# include "../net/magic.h" +#endif #endif #endif @@ -728,6 +731,9 @@ static void DhcpOptionsProcess (uchar * popt, Bootp_t *bp) int *to_ptr; #endif +#if defined(CONFIG_BOOTP_VENDOREX) && defined(CONFIG_BOOTP_VENDOREX_PXE_SHARED) + dhcp_vendorex_opts_done(); +#endif while (popt < end && *popt != 0xff) { oplen = *(popt + 1); switch (*popt) { @@ -819,6 +825,9 @@ static void DhcpOptionsProcess (uchar * popt, Bootp_t *bp) } popt += oplen + 2; /* Process next option */ } +#if defined(CONFIG_BOOTP_VENDOREX) && defined(CONFIG_BOOTP_VENDOREX_PXE_SHARED) + dhcp_vendorex_opts_done(); +#endif } static int DhcpMessageType(unsigned char *popt) diff --git a/net/magic.c b/net/magic.c new file mode 100644 index 0000000..2242c65 --- /dev/null +++ b/net/magic.c @@ -0,0 +1,229 @@ +/* + * magic.c + * Copyright (c) 2012 Canonical LTD. + * vim: autoindent:nowrap:noexpandtab + * + * See: RFC 5071 + */ +#include +#include +#include +#include "bootp.h" + +#include + +#if defined(CONFIG_BOOTP_VENDOREX) && defined(CONFIG_BOOTP_VENDOREX_PXE_SHARED) +#include "magic.h" + +#define OPT_MAGIC 208 +#define OPT_CONFIG_FILE 209 +#define OPT_PATH_PREFIX 210 +#define OPT_REBOOT_TIME 211 + +#define OPT_MAGIC_LEN 4 +#define OPT_REBOOT_TIME_LEN 4 + + +static const u8 opt_magic_string[] = {0xf1, 0x0, 0x74, 0x7e}; + +static int opt_magic_seen = 0; +static u8 opt_config_file_value[UCHAR_MAX + 1]; +static u8 opt_path_prefix_value[UCHAR_MAX + 1]; +static size_t opt_reboot_time = 0; + +static u8 *parse_magic(u8 *); +static u8 *parse_config_file(u8 *); +static u8 *parse_path_prefix(u8 *); +static u8 *parse_reboot_time(u8 *); + + +/* + * Init/done processing any vendor extension options that we care + * about here. It is possible (at least in my env) for the PXE options + * processing to occur more than once. + */ +void +dhcp_vendorex_opts_done(void) +{ + opt_magic_seen = 0; + memset(opt_config_file_value, 0, sizeof(opt_config_file_value)); + memset(opt_path_prefix_value, 0, sizeof(opt_path_prefix_value)); + opt_reboot_time = 0; +} + +/* + * Add vendor option(s) to the request + * - add own opts and return e as ptr just beyond them + * + * For our common PXE MAGIC implementation, we don't add anything: it's a noop + */ +u8 * +dhcp_vendorex_prep (u8 *e) +{ + return e; +} + +/* + * Parse vendor options from a response + * - e points into the bp_vend[] array, which is OPT_SIZE + * returns next 'e' if recognized and accepted, NULL otherwise + */ +u8 * +dhcp_vendorex_proc (u8 *e) +{ + switch (*e) { + case OPT_MAGIC: + printf("INFO: DHCP PXE vendorex PXE MAGIC option encountered\n"); + e = parse_magic(++e); + break; + + case OPT_CONFIG_FILE: + printf("INFO: DHCP PXE vendorex PXE ConfigFile option encountered\n"); + e = parse_config_file(++e); + break; + + case OPT_PATH_PREFIX: + printf("INFO: DHCP PXE vendorex PXE PathPrefix option encountered\n"); + e = parse_path_prefix(++e); + break; + + case OPT_REBOOT_TIME: + printf("INFO: DHCP PXE vendorex RebootTime option encountered\n"); + e = parse_reboot_time(++e); + break; + + default: + e = NULL; + break; + } + + return e; +} + +static u8 * +parse_magic(u8 *e) +{ + size_t len = *e++; + + if (opt_magic_seen) { + printf("INFO: DHCP PXE vendorex MAGIC option already seen\n"); + } + + if (len != OPT_MAGIC_LEN) { + e = NULL; + printf("ERROR: DHCP PXE vendorex MAGIC has invalid length [%u]\n", len); + + } else if (memcmp(e, opt_magic_string, sizeof(opt_magic_string))) { + e = NULL; + opt_magic_seen = 0; + printf("ERROR: DHCP PXE vendorex MAGIC invalid magic value %02x:%02x:%02x:%02x\n", + (unsigned int) e[0], (unsigned int) e[1], (unsigned int) e[2], (unsigned int) e[3]); + + } else { + e += sizeof(opt_magic_string); + opt_magic_seen = 1; + printf("INFO: DHCP PXE vendorex MAGIC confirmed\n"); + } + + return e; +} + +static u8 * +parse_config_file(u8 *e) +{ + size_t len = *e++; + + if (! opt_magic_seen) { + printf("ERROR: DHCP PXE vendorex ConfigFile without MAGIC\n"); + e = NULL; + + } else if (len == 0) { + printf("WARNING: DHCP PXE vendorex ConfigFile string has zero length, ignoring\n"); + + } else { + memset(opt_config_file_value, 0, sizeof(opt_config_file_value)); + memcpy(opt_config_file_value, e, len); + e += len; + opt_config_file_value[len] = 0; + setenv(PXE_CONFIG_FILE, (char *)opt_config_file_value); + printf("INFO: DHCP PXE vendorex ConfigFile is [%s]\n", opt_config_file_value); + } + + return e; +} + +static u8 * +parse_path_prefix(u8 *e) +{ + size_t len = *e++; + + if (! opt_magic_seen) { + printf("ERROR: DHCP PXE vendorex ConfigFile without MAGIC\n"); + e = NULL; + + } if (len == 0) { + printf("WARNING: DHCP PXE vendorex PathPrefix string has zero length, ignoring\n"); + + } else { + memset(opt_path_prefix_value, 0, sizeof(opt_path_prefix_value)); + memcpy(opt_path_prefix_value, e, len); + e += len; + opt_path_prefix_value[len] = 0; + setenv(PXE_PATH_PREFIX, (char *)opt_path_prefix_value); + printf("INFO: DHCP PXE vendorex PathPrefix is [%s]\n", opt_path_prefix_value); + } + + return e; +} + +static u8 * +parse_reboot_time(u8 *e) +{ + size_t len = *e++; + char bfr[12]; + + if (! opt_magic_seen) { + printf("ERROR: DHCP PXE vendorex ConfigFile without MAGIC\n"); + e = NULL; + + } else if (len != OPT_REBOOT_TIME_LEN) { + e += len; + printf("WARNING: DHCP PXE vendorex RebootTime has invalid length [%u], ignoring\n", len); + + } else { + unsigned long value; + + memcpy(&value, e, OPT_REBOOT_TIME_LEN); + opt_reboot_time = (size_t) ntohl(value); + e += OPT_REBOOT_TIME_LEN; + + sprintf(bfr, "%u", opt_reboot_time); + setenv(PXE_REBOOT_TIME, bfr); + + printf("INFO: DHCP PXE vendorex RebootTime is [%u]\n", opt_reboot_time); + +#if defined(CONFIG_BOOT_RETRY_TIME) + if (opt_reboot_time < CONFIG_BOOT_RETRY_MIN) { + opt_reboot_time = CONFIG_BOOT_RETRY_MIN; + sprintf(bfr, "%u", opt_reboot_time); + setenv(PXE_REBOOT_TIME, bfr); + printf("INFO: DHCP PXE vendorex RebootTime adjusted up to allowable minimum value [%d]\n", + CONFIG_BOOT_RETRY_MIN); + } + reset_cmd_timeout(); +#else + printf("INFO: DHCP PXE vendorex RebootTime is N/A and ignored due to u-boot configs\n"); +#endif + } + + return e; +} + +#else + +const int no_arm_vendorex_options = 1; + +#endif +/* + * file ends + */ diff --git a/net/magic.h b/net/magic.h new file mode 100644 index 0000000..6827174 --- /dev/null +++ b/net/magic.h @@ -0,0 +1,27 @@ +/* + * magic.h + * Copyright (c) 2012 Canonical LTD. + * vim: tabstop=8:noexpandtab + * + */ +#ifndef _NET_MAGIC_H +#define _NET_MAGIC_H + + +#if defined(CONFIG_BOOTP_VENDOREX) && defined(CONFIG_BOOTP_VENDOREX_PXE_SHARED) +#ifndef UCHAR_MAX +# define UCHAR_MAX ((size_t)0xff) +#endif + +#define PXE_CONFIG_FILE "pxeconfigfile" +#define PXE_PATH_PREFIX "pxepathprefix" /* see "bootfile" in common/cmd_pxe.c */ +#define PXE_REBOOT_TIME "pxereboottime" + +extern void dhcp_vendorex_opts_done(void); + +#endif + +#endif +/* + * file ends + */