[U-Boot] [PATCH 0/4] efi_loader: PXE boot support

One of the most common boot cases with EFI on arm64 is to boot from the network using PXE boot. While TianoCore implements that just fine, we were lacking support for it in U-Boot so far.
But no longer! Here is a patch set, enabling PXE booting of EFI payloads. With this patch set, I was successfully able to automatically boot our normal (usually used with TianoCore based systems) environment to boot and run grub2 and a kernel from there with U-Boot.
Alexander Graf (4): efi_loader: Add network access support bootp: Move vendor class identifier set to function net: Move the VCI and client arch values to Kconfig distro: Add efi pxe boot code
cmd/bootefi.c | 7 + configs/ls2080a_emu_defconfig | 1 + configs/ls2080a_simu_defconfig | 1 + configs/thunderx_88xx_defconfig | 1 + configs/vexpress_aemv8a_dram_defconfig | 1 + configs/vexpress_aemv8a_juno_defconfig | 1 + configs/vexpress_aemv8a_semi_defconfig | 1 + configs/vexpress_ca15_tc2_defconfig | 1 + configs/vexpress_ca5x2_defconfig | 1 + configs/vexpress_ca9x4_defconfig | 1 + include/config_distro_bootcmd.h | 37 ++++- include/config_distro_defaults.h | 21 --- include/configs/ls2080a_emu.h | 1 - include/configs/ls2080a_simu.h | 1 - include/configs/thunderx_88xx.h | 2 - include/configs/vexpress_aemv8a.h | 2 - include/configs/vexpress_ca15_tc2.h | 1 - include/configs/vexpress_ca5x2.h | 1 - include/configs/vexpress_ca9x4.h | 1 - include/efi_api.h | 119 ++++++++++++++ include/efi_loader.h | 7 + include/net.h | 2 +- lib/efi_loader/Makefile | 1 + lib/efi_loader/efi_net.c | 291 +++++++++++++++++++++++++++++++++ net/Kconfig | 12 ++ net/bootp.c | 37 +++-- net/net.c | 4 +- net/tftp.c | 2 + 28 files changed, 511 insertions(+), 47 deletions(-) create mode 100644 lib/efi_loader/efi_net.c

We can now successfully boot EFI applications from disk, but users may want to also run them from a PXE setup.
This patch implements rudimentary network support, allowing a payload to send and receive network packets.
With this patch, I was able to successfully run grub2 with network access inside of QEMU's -M xlnx-ep108.
Signed-off-by: Alexander Graf agraf@suse.de --- cmd/bootefi.c | 7 ++ include/efi_api.h | 119 +++++++++++++++++++ include/efi_loader.h | 7 ++ include/net.h | 2 +- lib/efi_loader/Makefile | 1 + lib/efi_loader/efi_net.c | 291 +++++++++++++++++++++++++++++++++++++++++++++++ net/bootp.c | 2 + net/net.c | 4 +- net/tftp.c | 2 + 9 files changed, 432 insertions(+), 3 deletions(-) create mode 100644 lib/efi_loader/efi_net.c
diff --git a/cmd/bootefi.c b/cmd/bootefi.c index 7f552fc..d3a2331 100644 --- a/cmd/bootefi.c +++ b/cmd/bootefi.c @@ -197,6 +197,13 @@ static unsigned long do_bootefi_exec(void *efi, void *fdt) #ifdef CONFIG_LCD efi_gop_register(); #endif +#ifdef CONFIG_NET + void *nethandle = loaded_image_info.device_handle; + efi_net_register(&nethandle); + + if (!memcmp(bootefi_device_path[0].str, "N\0e\0t", 6)) + loaded_image_info.device_handle = nethandle; +#endif
/* Call our payload! */ #ifdef DEBUG_EFI diff --git a/include/efi_api.h b/include/efi_api.h index 51d7586..20035d7 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -412,4 +412,123 @@ struct efi_gop struct efi_gop_mode *mode; };
+#define EFI_SIMPLE_NETWORK_GUID \ + EFI_GUID(0xa19832b9, 0xac25, 0x11d3, \ + 0x9a, 0x2d, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d) + +struct efi_mac_address { + char mac_addr[32]; +}; + +struct efi_ip_address { + u8 ip_addr[16]; +}; + +enum efi_simple_network_state { + EFI_NETWORK_STOPPED, + EFI_NETWORK_STARTED, + EFI_NETWORK_INITIALIZED, +}; + +struct efi_simple_network_mode { + enum efi_simple_network_state state; + u32 hwaddr_size; + u32 media_header_size; + u32 max_packet_size; + u32 nvram_size; + u32 nvram_access_size; + u32 receive_filter_mask; + u32 receive_filter_setting; + u32 max_mcast_filter_count; + u32 mcast_filter_count; + struct efi_mac_address mcast_filter[16]; + struct efi_mac_address current_address; + struct efi_mac_address broadcast_address; + struct efi_mac_address permanent_address; + u8 if_type; + u8 mac_changeable; + u8 multitx_supported; + u8 media_present_supported; + u8 media_present; +}; + +#define EFI_SIMPLE_NETWORK_RECEIVE_UNICAST 0x01, +#define EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST 0x02, +#define EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST 0x04, +#define EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS 0x08, +#define EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST 0x10, + +struct efi_simple_network +{ + u64 revision; + efi_status_t (EFIAPI *start)(struct efi_simple_network *this); + efi_status_t (EFIAPI *stop)(struct efi_simple_network *this); + efi_status_t (EFIAPI *initialize)(struct efi_simple_network *this, + ulong extra_rx, ulong extra_tx); + efi_status_t (EFIAPI *reset)(struct efi_simple_network *this, + int extended_verification); + efi_status_t (EFIAPI *shutdown)(struct efi_simple_network *this); + efi_status_t (EFIAPI *receive_filters)(struct efi_simple_network *this, + u32 enable, u32 disable, int reset_mcast_filter, + ulong mcast_filter_count, + struct efi_mac_address *mcast_filter); + efi_status_t (EFIAPI *station_address)(struct efi_simple_network *this, + int reset, struct efi_mac_address *new_mac); + efi_status_t (EFIAPI *statistics)(struct efi_simple_network *this, + int reset, ulong *stat_size, void *stat_table); + efi_status_t (EFIAPI *mcastiptomac)(struct efi_simple_network *this, + int ipv6, struct efi_ip_address *ip, + struct efi_mac_address *mac); + efi_status_t (EFIAPI *nvdata)(struct efi_simple_network *this, + int read_write, ulong offset, ulong buffer_size, + char *buffer); + efi_status_t (EFIAPI *get_status)(struct efi_simple_network *this, + u32 *int_status, void **txbuf); + efi_status_t (EFIAPI *transmit)(struct efi_simple_network *this, + ulong header_size, ulong buffer_size, void *buffer, + struct efi_mac_address *src_addr, + struct efi_mac_address *dest_addr, u16 *protocol); + efi_status_t (EFIAPI *receive)(struct efi_simple_network *this, + ulong *header_size, ulong *buffer_size, void *buffer, + struct efi_mac_address *src_addr, + struct efi_mac_address *dest_addr, u16 *protocol); + void (EFIAPI *waitforpacket)(void); + struct efi_simple_network_mode *mode; +}; + +#define EFI_PXE_GUID \ + EFI_GUID(0x03c4e603, 0xac28, 0x11d3, \ + 0x9a, 0x2d, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d) + +struct efi_pxe_packet { + u8 packet[1472]; +}; + +struct efi_pxe_mode +{ + u8 unused[52]; + struct efi_pxe_packet dhcp_discover; + struct efi_pxe_packet dhcp_ack; + struct efi_pxe_packet proxy_offer; + struct efi_pxe_packet pxe_discover; + struct efi_pxe_packet pxe_reply; +}; + +struct efi_pxe { + u64 rev; + void (EFIAPI *start)(void); + void (EFIAPI *stop)(void); + void (EFIAPI *dhcp)(void); + void (EFIAPI *discover)(void); + void (EFIAPI *mftp)(void); + void (EFIAPI *udpwrite)(void); + void (EFIAPI *udpread)(void); + void (EFIAPI *setipfilter)(void); + void (EFIAPI *arp)(void); + void (EFIAPI *setparams)(void); + void (EFIAPI *setstationip)(void); + void (EFIAPI *setpackets)(void); + struct efi_pxe_mode *mode; +}; + #endif diff --git a/include/efi_loader.h b/include/efi_loader.h index 88b8149..8005454 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -91,6 +91,12 @@ extern struct list_head efi_obj_list; int efi_disk_register(void); /* Called by bootefi to make GOP (graphical) interface available */ int efi_gop_register(void); +/* Called by bootefi to make the network interface available */ +int efi_net_register(void **handle); + +/* Called by networking code to memorize the dhcp ack package */ +void efi_net_set_dhcp_ack(void *pkt, int len); + /* * Stub implementation for a protocol opener that just returns the handle as * interface @@ -157,5 +163,6 @@ static inline void ascii2unicode(u16 *unicode, char *ascii) static inline void efi_restore_gd(void) { } static inline void efi_set_bootdev(const char *dev, const char *devnr, const char *path) { } +static inline void efi_net_set_dhcp_ack(void *pkt, int len) { }
#endif diff --git a/include/net.h b/include/net.h index 1fb4194..f8826ca 100644 --- a/include/net.h +++ b/include/net.h @@ -269,7 +269,7 @@ int eth_getenv_enetaddr_by_index(const char *base_name, int index, int eth_init(void); /* Initialize the device */ int eth_send(void *packet, int length); /* Send a packet */
-#ifdef CONFIG_API +#if defined(CONFIG_API) || defined(CONFIG_EFI_LOADER) int eth_receive(void *packet, int length); /* Receive a packet*/ extern void (*push_packet)(void *packet, int length); #endif diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile index 83e31f6..2a3849e 100644 --- a/lib/efi_loader/Makefile +++ b/lib/efi_loader/Makefile @@ -11,3 +11,4 @@ obj-y += efi_image_loader.o efi_boottime.o efi_runtime.o efi_console.o obj-y += efi_memory.o obj-$(CONFIG_LCD) += efi_gop.o obj-$(CONFIG_PARTITIONS) += efi_disk.o +obj-$(CONFIG_NET) += efi_net.o diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c new file mode 100644 index 0000000..dd3b485 --- /dev/null +++ b/lib/efi_loader/efi_net.c @@ -0,0 +1,291 @@ +/* + * EFI application network access support + * + * Copyright (c) 2016 Alexander Graf + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <efi_loader.h> +#include <inttypes.h> +#include <lcd.h> +#include <malloc.h> + +DECLARE_GLOBAL_DATA_PTR; + +static const efi_guid_t efi_net_guid = EFI_SIMPLE_NETWORK_GUID; +static const efi_guid_t efi_pxe_guid = EFI_PXE_GUID; +static struct efi_pxe_packet *dhcp_ack; +static bool new_rx_packet; +static void *new_tx_packet; + +struct efi_net_obj { + /* Generic EFI object parent class data */ + struct efi_object parent; + /* EFI Interface callback struct for network */ + struct efi_simple_network net; + struct efi_simple_network_mode net_mode; + /* Device path to the network adapter */ + struct efi_device_path_file_path dp[2]; + /* PXE struct to transmit dhcp data */ + struct efi_pxe pxe; + struct efi_pxe_mode pxe_mode; +}; + +static efi_status_t EFIAPI efi_net_start(struct efi_simple_network *this) +{ + EFI_ENTRY("%p", this); + + return EFI_EXIT(EFI_SUCCESS); +} + +static efi_status_t EFIAPI efi_net_stop(struct efi_simple_network *this) +{ + EFI_ENTRY("%p", this); + + return EFI_EXIT(EFI_SUCCESS); +} + +static efi_status_t EFIAPI efi_net_initialize(struct efi_simple_network *this, + ulong extra_rx, ulong extra_tx) +{ + EFI_ENTRY("%p, %lx, %lx", this, extra_rx, extra_tx); + + eth_init(); + + return EFI_EXIT(EFI_SUCCESS); +} + +static efi_status_t EFIAPI efi_net_reset(struct efi_simple_network *this, + int extended_verification) +{ + EFI_ENTRY("%p, %x", this, extended_verification); + + return EFI_EXIT(EFI_SUCCESS); +} + +static efi_status_t EFIAPI efi_net_shutdown(struct efi_simple_network *this) +{ + EFI_ENTRY("%p", this); + + return EFI_EXIT(EFI_SUCCESS); +} + +static efi_status_t EFIAPI efi_net_receive_filters( + struct efi_simple_network *this, u32 enable, u32 disable, + int reset_mcast_filter, ulong mcast_filter_count, + struct efi_mac_address *mcast_filter) +{ + EFI_ENTRY("%p, %x, %x, %x, %lx, %p", this, enable, disable, + reset_mcast_filter, mcast_filter_count, mcast_filter); + + /* XXX Do we care? */ + + return EFI_EXIT(EFI_SUCCESS); +} + +static efi_status_t EFIAPI efi_net_station_address( + struct efi_simple_network *this, int reset, + struct efi_mac_address *new_mac) +{ + EFI_ENTRY("%p, %x, %p", this, reset, new_mac); + + return EFI_EXIT(EFI_INVALID_PARAMETER); +} + +static efi_status_t EFIAPI efi_net_statistics(struct efi_simple_network *this, + int reset, ulong *stat_size, + void *stat_table) +{ + EFI_ENTRY("%p, %x, %p, %p", this, reset, stat_size, stat_table); + + return EFI_EXIT(EFI_INVALID_PARAMETER); +} + +static efi_status_t EFIAPI efi_net_mcastiptomac(struct efi_simple_network *this, + int ipv6, + struct efi_ip_address *ip, + struct efi_mac_address *mac) +{ + EFI_ENTRY("%p, %x, %p, %p", this, ipv6, ip, mac); + + return EFI_EXIT(EFI_INVALID_PARAMETER); +} + +static efi_status_t EFIAPI efi_net_nvdata(struct efi_simple_network *this, + int read_write, ulong offset, + ulong buffer_size, char *buffer) +{ + EFI_ENTRY("%p, %x, %lx, %lx, %p", this, read_write, offset, buffer_size, + buffer); + + return EFI_EXIT(EFI_INVALID_PARAMETER); +} + +static efi_status_t EFIAPI efi_net_get_status(struct efi_simple_network *this, + u32 *int_status, void **txbuf) +{ + EFI_ENTRY("%p, %p, %p", this, int_status, txbuf); + + /* We send packets synchronously, so nothing is outstanding */ + if (int_status) + *int_status = 0; + if (txbuf) + *txbuf = new_tx_packet; + + new_tx_packet = NULL; + + return EFI_EXIT(EFI_SUCCESS); +} + +static efi_status_t EFIAPI efi_net_transmit(struct efi_simple_network *this, + ulong header_size, ulong buffer_size, void *buffer, + struct efi_mac_address *src_addr, + struct efi_mac_address *dest_addr, u16 *protocol) +{ + EFI_ENTRY("%p, %lx, %lx, %p, %p, %p, %p", this, header_size, + buffer_size, buffer, src_addr, dest_addr, protocol); + + if (header_size) { + /* We would need to create the header if header_size != 0 */ + return EFI_EXIT(EFI_INVALID_PARAMETER); + } + + net_send_packet(buffer, buffer_size); + new_tx_packet = buffer; + + return EFI_EXIT(EFI_SUCCESS); +} + +static void efi_net_push(void *pkt, int len) +{ + new_rx_packet = true; +} + +static efi_status_t EFIAPI efi_net_receive(struct efi_simple_network *this, + ulong *header_size, ulong *buffer_size, void *buffer, + struct efi_mac_address *src_addr, + struct efi_mac_address *dest_addr, u16 *protocol) +{ + EFI_ENTRY("%p, %p, %p, %p, %p, %p, %p", this, header_size, + buffer_size, buffer, src_addr, dest_addr, protocol); + + push_packet = efi_net_push; + eth_rx(); + push_packet = NULL; + + if (!new_rx_packet) + return EFI_EXIT(EFI_NOT_READY); + + if (*buffer_size < net_rx_packet_len) { + /* Packet doesn't fit, try again with bigger buf */ + *buffer_size = net_rx_packet_len; + return EFI_EXIT(EFI_BUFFER_TOO_SMALL); + } + + memcpy(buffer, net_rx_packet, net_rx_packet_len); + *buffer_size = net_rx_packet_len; + new_rx_packet = false; + + return EFI_EXIT(EFI_SUCCESS); +} + +static efi_status_t efi_net_open_dp(void *handle, efi_guid_t *protocol, + void **protocol_interface, void *agent_handle, + void *controller_handle, uint32_t attributes) +{ + struct efi_simple_network *net = handle; + struct efi_net_obj *netobj = container_of(net, struct efi_net_obj, net); + + *protocol_interface = netobj->dp; + + return EFI_SUCCESS; +} + +static efi_status_t efi_net_open_pxe(void *handle, efi_guid_t *protocol, + void **protocol_interface, void *agent_handle, + void *controller_handle, uint32_t attributes) +{ + struct efi_simple_network *net = handle; + struct efi_net_obj *netobj = container_of(net, struct efi_net_obj, net); + + *protocol_interface = &netobj->pxe; + + return EFI_SUCCESS; +} + +void efi_net_set_dhcp_ack(void *pkt, int len) +{ + int maxsize = sizeof(*dhcp_ack); + + if (!dhcp_ack) + dhcp_ack = malloc(maxsize); + + memcpy(dhcp_ack, pkt, min(len, maxsize)); +} + +/* This gets called from do_bootefi_exec(). */ +int efi_net_register(void **handle) +{ + struct efi_net_obj *netobj; + struct efi_device_path_file_path dp_net = { + .dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE, + .dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH, + .dp.length = sizeof(dp_net), + .str = { 'N', 'e', 't' }, + }; + struct efi_device_path_file_path dp_end = { + .dp.type = DEVICE_PATH_TYPE_END, + .dp.sub_type = DEVICE_PATH_SUB_TYPE_END, + .dp.length = sizeof(dp_end), + }; + + if (!eth_get_dev()) { + /* No eth device active, don't expose any */ + return 0; + } + + /* We only expose the "active" eth device, so one is enough */ + netobj = calloc(1, sizeof(*netobj)); + + /* Fill in object data */ + netobj->parent.protocols[0].guid = &efi_net_guid; + netobj->parent.protocols[0].open = efi_return_handle; + netobj->parent.protocols[1].guid = &efi_guid_device_path; + netobj->parent.protocols[1].open = efi_net_open_dp; + netobj->parent.protocols[2].guid = &efi_pxe_guid; + netobj->parent.protocols[2].open = efi_net_open_pxe; + netobj->parent.handle = &netobj->net; + netobj->net.start = efi_net_start; + netobj->net.stop = efi_net_stop; + netobj->net.initialize = efi_net_initialize; + netobj->net.reset = efi_net_reset; + netobj->net.shutdown = efi_net_shutdown; + netobj->net.receive_filters = efi_net_receive_filters; + netobj->net.station_address = efi_net_station_address; + netobj->net.statistics = efi_net_statistics; + netobj->net.mcastiptomac = efi_net_mcastiptomac; + netobj->net.nvdata = efi_net_nvdata; + netobj->net.get_status = efi_net_get_status; + netobj->net.transmit = efi_net_transmit; + netobj->net.receive = efi_net_receive; + netobj->net.mode = &netobj->net_mode; + netobj->net_mode.state = EFI_NETWORK_STARTED; + netobj->dp[0] = dp_net; + netobj->dp[1] = dp_end; + memcpy(netobj->net_mode.current_address.mac_addr, eth_get_ethaddr(), 6); + netobj->net_mode.max_packet_size = PKTSIZE; + + netobj->pxe.mode = &netobj->pxe_mode; + if (dhcp_ack) + netobj->pxe_mode.dhcp_ack = *dhcp_ack; + + /* Hook net up to the device list */ + list_add_tail(&netobj->parent.link, &efi_obj_list); + + if (handle) + *handle = &netobj->net; + + return 0; +} diff --git a/net/bootp.c b/net/bootp.c index d7852db..d91b307 100644 --- a/net/bootp.c +++ b/net/bootp.c @@ -10,6 +10,7 @@
#include <common.h> #include <command.h> +#include <efi_loader.h> #include <net.h> #include <net/tftp.h> #include "bootp.h" @@ -1025,6 +1026,7 @@ static void dhcp_handler(uchar *pkt, unsigned dest, struct in_addr sip, strlen(CONFIG_SYS_BOOTFILE_PREFIX)) == 0) { #endif /* CONFIG_SYS_BOOTFILE_PREFIX */ dhcp_packet_process_options(bp); + efi_net_set_dhcp_ack(pkt, len);
debug("TRANSITIONING TO REQUESTING STATE\n"); dhcp_state = REQUESTING; diff --git a/net/net.c b/net/net.c index fba111e..1e1d23d 100644 --- a/net/net.c +++ b/net/net.c @@ -146,7 +146,7 @@ static unsigned net_ip_id; /* Ethernet bcast address */ const u8 net_bcast_ethaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; const u8 net_null_ethaddr[6]; -#ifdef CONFIG_API +#if defined(CONFIG_API) || defined(CONFIG_EFI_LOADER) void (*push_packet)(void *, int len) = 0; #endif /* Network loop state */ @@ -1054,7 +1054,7 @@ void net_process_received_packet(uchar *in_packet, int len) if (len < ETHER_HDR_SIZE) return;
-#ifdef CONFIG_API +#if defined(CONFIG_API) || defined(CONFIG_EFI_LOADER) if (push_packet) { (*push_packet)(in_packet, len); return; diff --git a/net/tftp.c b/net/tftp.c index f2889fe..ced45ec 100644 --- a/net/tftp.c +++ b/net/tftp.c @@ -8,6 +8,7 @@
#include <common.h> #include <command.h> +#include <efi_loader.h> #include <mapmem.h> #include <net.h> #include <net/tftp.h> @@ -804,6 +805,7 @@ void tftp_start(enum proto_t protocol) printf("Load address: 0x%lx\n", load_addr); puts("Loading: *\b"); tftp_state = STATE_SEND_RRQ; + efi_set_bootdev("Net", "", tftp_filename); }
time_start = get_timer(0);

Both the dhcp as well as the bootp case add vendor class identifier parameters into their packets. Let's move that into a separate function to make overlaying easier.
Signed-off-by: Alexander Graf agraf@suse.de --- net/bootp.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-)
diff --git a/net/bootp.c b/net/bootp.c index d91b307..d718e35 100644 --- a/net/bootp.c +++ b/net/bootp.c @@ -411,6 +411,17 @@ static void bootp_timeout_handler(void) e += vci_strlen; \ } while (0)
+static u8 *add_vci(u8 *e) +{ +#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_NET_VCI_STRING) + put_vci(e, CONFIG_SPL_NET_VCI_STRING); +#elif defined(CONFIG_BOOTP_VCI_STRING) + put_vci(e, CONFIG_BOOTP_VCI_STRING); +#endif + + return e; +} + /* * Initialize BOOTP extension fields in the request. */ @@ -508,11 +519,7 @@ static int dhcp_extended(u8 *e, int message_type, struct in_addr server_ip, } #endif
-#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_NET_VCI_STRING) - put_vci(e, CONFIG_SPL_NET_VCI_STRING); -#elif defined(CONFIG_BOOTP_VCI_STRING) - put_vci(e, CONFIG_BOOTP_VCI_STRING); -#endif + e = add_vci(e);
#if defined(CONFIG_BOOTP_VENDOREX) x = dhcp_vendorex_prep(e); @@ -598,14 +605,7 @@ static int bootp_extended(u8 *e) *e++ = (576 - 312 + OPT_FIELD_SIZE) & 0xff; #endif
-#if defined(CONFIG_BOOTP_VCI_STRING) || \ - (defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_NET_VCI_STRING)) -#ifdef CONFIG_SPL_BUILD - put_vci(e, CONFIG_SPL_NET_VCI_STRING); -#else - put_vci(e, CONFIG_BOOTP_VCI_STRING); -#endif -#endif + add_vci(e);
#if defined(CONFIG_BOOTP_SUBNETMASK) *e++ = 1; /* Subnet mask request */

On Wed, May 04, 2016 at 07:10:51PM +0200, Alexander Graf wrote:
Both the dhcp as well as the bootp case add vendor class identifier parameters into their packets. Let's move that into a separate function to make overlaying easier.
Signed-off-by: Alexander Graf agraf@suse.de
Reviewed-by: Tom Rini trini@konsulko.com

We have a bunch of boards that define their vendor class identifier and client archs in the board files or in the distro config. Move everything to the generic Kconfig options.
We're missing the distinction between i386 and x86_64, as I couldn't find any config variable that would tell us the difference. Is that really important to people? I guess not, so I left it out.
Signed-off-by: Alexander Graf agraf@suse.de --- configs/ls2080a_emu_defconfig | 1 + configs/ls2080a_simu_defconfig | 1 + configs/thunderx_88xx_defconfig | 1 + configs/vexpress_aemv8a_dram_defconfig | 1 + configs/vexpress_aemv8a_juno_defconfig | 1 + configs/vexpress_aemv8a_semi_defconfig | 1 + configs/vexpress_ca15_tc2_defconfig | 1 + configs/vexpress_ca5x2_defconfig | 1 + configs/vexpress_ca9x4_defconfig | 1 + include/config_distro_defaults.h | 21 --------------------- include/configs/ls2080a_emu.h | 1 - include/configs/ls2080a_simu.h | 1 - include/configs/thunderx_88xx.h | 2 -- include/configs/vexpress_aemv8a.h | 2 -- include/configs/vexpress_ca15_tc2.h | 1 - include/configs/vexpress_ca5x2.h | 1 - include/configs/vexpress_ca9x4.h | 1 - net/Kconfig | 12 ++++++++++++ 18 files changed, 21 insertions(+), 30 deletions(-)
diff --git a/configs/ls2080a_emu_defconfig b/configs/ls2080a_emu_defconfig index f9b4eac..30a6381 100644 --- a/configs/ls2080a_emu_defconfig +++ b/configs/ls2080a_emu_defconfig @@ -25,3 +25,4 @@ CONFIG_CMD_CACHE=y # CONFIG_CMD_MISC is not set CONFIG_SYS_NS16550=y CONFIG_OF_LIBFDT=y +CONFIG_BOOTP_VCI_STRING="U-Boot.LS2080A-EMU" diff --git a/configs/ls2080a_simu_defconfig b/configs/ls2080a_simu_defconfig index 728fa25..a095b8a 100644 --- a/configs/ls2080a_simu_defconfig +++ b/configs/ls2080a_simu_defconfig @@ -28,3 +28,4 @@ CONFIG_CMD_FAT=y CONFIG_NET_RANDOM_ETHADDR=y CONFIG_SYS_NS16550=y CONFIG_OF_LIBFDT=y +CONFIG_BOOTP_VCI_STRING="U-Boot.LS2080A-SIMU" diff --git a/configs/thunderx_88xx_defconfig b/configs/thunderx_88xx_defconfig index b7078e0..cb33f60 100644 --- a/configs/thunderx_88xx_defconfig +++ b/configs/thunderx_88xx_defconfig @@ -22,3 +22,4 @@ CONFIG_DEBUG_UART_BASE=0x87e024000000 CONFIG_DEBUG_UART_CLOCK=24000000 CONFIG_DEBUG_UART_SKIP_INIT=y CONFIG_REGEX=y +CONFIG_BOOTP_VCI_STRING="Diagnostics" diff --git a/configs/vexpress_aemv8a_dram_defconfig b/configs/vexpress_aemv8a_dram_defconfig index 989f068..c0708b2 100644 --- a/configs/vexpress_aemv8a_dram_defconfig +++ b/configs/vexpress_aemv8a_dram_defconfig @@ -24,3 +24,4 @@ CONFIG_CMD_CACHE=y CONFIG_CMD_FAT=y CONFIG_DM=y CONFIG_OF_LIBFDT=y +CONFIG_BOOTP_VCI_STRING="U-Boot.armv8.vexpress_aemv8a" diff --git a/configs/vexpress_aemv8a_juno_defconfig b/configs/vexpress_aemv8a_juno_defconfig index c70851f..5af9f58 100644 --- a/configs/vexpress_aemv8a_juno_defconfig +++ b/configs/vexpress_aemv8a_juno_defconfig @@ -24,3 +24,4 @@ CONFIG_CMD_CACHE=y CONFIG_CMD_FAT=y CONFIG_DM=y CONFIG_OF_LIBFDT=y +CONFIG_BOOTP_VCI_STRING="U-Boot.armv8.vexpress_aemv8a" diff --git a/configs/vexpress_aemv8a_semi_defconfig b/configs/vexpress_aemv8a_semi_defconfig index b0a2f67..379dff2 100644 --- a/configs/vexpress_aemv8a_semi_defconfig +++ b/configs/vexpress_aemv8a_semi_defconfig @@ -24,3 +24,4 @@ CONFIG_CMD_CACHE=y CONFIG_CMD_FAT=y CONFIG_DM=y CONFIG_OF_LIBFDT=y +CONFIG_BOOTP_VCI_STRING="U-Boot.armv8.vexpress_aemv8a" diff --git a/configs/vexpress_ca15_tc2_defconfig b/configs/vexpress_ca15_tc2_defconfig index 2f141dd..c39faaa 100644 --- a/configs/vexpress_ca15_tc2_defconfig +++ b/configs/vexpress_ca15_tc2_defconfig @@ -24,3 +24,4 @@ CONFIG_CMD_EXT4=y CONFIG_CMD_FAT=y CONFIG_CMD_FS_GENERIC=y CONFIG_OF_LIBFDT=y +CONFIG_BOOTP_VCI_STRING="U-Boot.armv7.vexpress_ca15x2_tc2" diff --git a/configs/vexpress_ca5x2_defconfig b/configs/vexpress_ca5x2_defconfig index c495ee5..e71d45e 100644 --- a/configs/vexpress_ca5x2_defconfig +++ b/configs/vexpress_ca5x2_defconfig @@ -24,3 +24,4 @@ CONFIG_CMD_EXT4=y CONFIG_CMD_FAT=y CONFIG_CMD_FS_GENERIC=y CONFIG_OF_LIBFDT=y +CONFIG_BOOTP_VCI_STRING="U-Boot.armv7.vexpress_ca5x2" diff --git a/configs/vexpress_ca9x4_defconfig b/configs/vexpress_ca9x4_defconfig index fcd6e26..20100a3 100644 --- a/configs/vexpress_ca9x4_defconfig +++ b/configs/vexpress_ca9x4_defconfig @@ -24,3 +24,4 @@ CONFIG_CMD_EXT4=y CONFIG_CMD_FAT=y CONFIG_CMD_FS_GENERIC=y CONFIG_OF_LIBFDT=y +CONFIG_BOOTP_VCI_STRING="U-Boot.armv7.vexpress_ca9x4" diff --git a/include/config_distro_defaults.h b/include/config_distro_defaults.h index 766a212..dfc2cbc 100644 --- a/include/config_distro_defaults.h +++ b/include/config_distro_defaults.h @@ -20,27 +20,6 @@ #define CONFIG_BOOTP_PXE #define CONFIG_BOOTP_SUBNETMASK
-#if defined(__arm__) || defined(__aarch64__) -#define CONFIG_BOOTP_PXE_CLIENTARCH 0x100 -#if defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) -#if !defined(CONFIG_BOOTP_VCI_STRING) -#define CONFIG_BOOTP_VCI_STRING "U-Boot.armv7" -#endif -#elif defined(__aarch64__) -#if !defined(CONFIG_BOOTP_VCI_STRING) -#define CONFIG_BOOTP_VCI_STRING "U-Boot.armv8" -#endif -#else -#if !defined(CONFIG_BOOTP_VCI_STRING) -#define CONFIG_BOOTP_VCI_STRING "U-Boot.arm" -#endif -#endif -#elif defined(__i386__) -#define CONFIG_BOOTP_PXE_CLIENTARCH 0x0 -#elif defined(__x86_64__) -#define CONFIG_BOOTP_PXE_CLIENTARCH 0x9 -#endif - #ifdef CONFIG_ARM64 #define CONFIG_CMD_BOOTI #endif diff --git a/include/configs/ls2080a_emu.h b/include/configs/ls2080a_emu.h index f4ace85..16e37bf 100644 --- a/include/configs/ls2080a_emu.h +++ b/include/configs/ls2080a_emu.h @@ -10,7 +10,6 @@ #include "ls2080a_common.h"
#define CONFIG_IDENT_STRING " LS2080A-EMU" -#define CONFIG_BOOTP_VCI_STRING "U-Boot.LS2080A-EMU"
#define CONFIG_SYS_CLK_FREQ 100000000 #define CONFIG_DDR_CLK_FREQ 133333333 diff --git a/include/configs/ls2080a_simu.h b/include/configs/ls2080a_simu.h index bc0d678..7563aaf 100644 --- a/include/configs/ls2080a_simu.h +++ b/include/configs/ls2080a_simu.h @@ -10,7 +10,6 @@ #include "ls2080a_common.h"
#define CONFIG_IDENT_STRING " LS2080A-SIMU" -#define CONFIG_BOOTP_VCI_STRING "U-Boot.LS2080A-SIMU"
#define CONFIG_SYS_CLK_FREQ 100000000 #define CONFIG_DDR_CLK_FREQ 133333333 diff --git a/include/configs/thunderx_88xx.h b/include/configs/thunderx_88xx.h index 7c35d8c..e43a7fd 100644 --- a/include/configs/thunderx_88xx.h +++ b/include/configs/thunderx_88xx.h @@ -17,7 +17,6 @@
#define CONFIG_IDENT_STRING \ " for Cavium Thunder CN88XX ARM v8 Multi-Core" -#define CONFIG_BOOTP_VCI_STRING "Diagnostics"
#define MEM_BASE 0x00500000
@@ -62,7 +61,6 @@ #define CONFIG_BOOTP_GATEWAY #define CONFIG_BOOTP_HOSTNAME #define CONFIG_BOOTP_PXE -#define CONFIG_BOOTP_PXE_CLIENTARCH 0x100
/* Miscellaneous configurable options */ #define CONFIG_SYS_LOAD_ADDR (MEM_BASE) diff --git a/include/configs/vexpress_aemv8a.h b/include/configs/vexpress_aemv8a.h index 1b5fc2e..6a37582 100644 --- a/include/configs/vexpress_aemv8a.h +++ b/include/configs/vexpress_aemv8a.h @@ -23,7 +23,6 @@ #define CONFIG_SYS_CACHELINE_SIZE 64
#define CONFIG_IDENT_STRING " vexpress_aemv8a" -#define CONFIG_BOOTP_VCI_STRING "U-Boot.armv8.vexpress_aemv8a"
/* Link Definitions */ #if defined(CONFIG_TARGET_VEXPRESS64_BASE_FVP) || \ @@ -146,7 +145,6 @@ #define CONFIG_BOOTP_GATEWAY #define CONFIG_BOOTP_HOSTNAME #define CONFIG_BOOTP_PXE -#define CONFIG_BOOTP_PXE_CLIENTARCH 0x100
/* Miscellaneous configurable options */ #define CONFIG_SYS_LOAD_ADDR (V2M_BASE + 0x10000000) diff --git a/include/configs/vexpress_ca15_tc2.h b/include/configs/vexpress_ca15_tc2.h index 883e58e..b509a9c 100644 --- a/include/configs/vexpress_ca15_tc2.h +++ b/include/configs/vexpress_ca15_tc2.h @@ -12,7 +12,6 @@ #define __VEXPRESS_CA15X2_TC2_h
#define CONFIG_VEXPRESS_EXTENDED_MEMORY_MAP -#define CONFIG_BOOTP_VCI_STRING "U-Boot.armv7.vexpress_ca15x2_tc2" #include "vexpress_common.h"
#define CONFIG_SYSFLAGS_ADDR 0x1c010030 diff --git a/include/configs/vexpress_ca5x2.h b/include/configs/vexpress_ca5x2.h index 4385027..20b92dc 100644 --- a/include/configs/vexpress_ca5x2.h +++ b/include/configs/vexpress_ca5x2.h @@ -12,7 +12,6 @@ #define __VEXPRESS_CA5X2_h
#define CONFIG_VEXPRESS_EXTENDED_MEMORY_MAP -#define CONFIG_BOOTP_VCI_STRING "U-Boot.armv7.vexpress_ca5x2" #include "vexpress_common.h"
#endif /* __VEXPRESS_CA5X2_h */ diff --git a/include/configs/vexpress_ca9x4.h b/include/configs/vexpress_ca9x4.h index 99be50a..993398c 100644 --- a/include/configs/vexpress_ca9x4.h +++ b/include/configs/vexpress_ca9x4.h @@ -12,7 +12,6 @@ #define __VEXPRESS_CA9X4_H
#define CONFIG_VEXPRESS_ORIGINAL_MEMORY_MAP -#define CONFIG_BOOTP_VCI_STRING "U-Boot.armv7.vexpress_ca9x4" #include "vexpress_common.h"
#endif /* VEXPRESS_CA9X4_H */ diff --git a/net/Kconfig b/net/Kconfig index a44a783..b3bd376 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -32,4 +32,16 @@ config NET_TFTP_VARS If unset, timeout and maximum are hard-defined as 1 second and 10 timouts per TFTP transfer.
+config BOOTP_PXE_CLIENTARCH + hex + default 0x100 if ARM + default 0 + +config BOOTP_VCI_STRING + string + default "U-Boot.armv7" if CPU_V7 || CPU_V7M + default "U-Boot.armv8" if ARM64 + default "U-Boot.arm" if ARM + default "U-Boot" + endif # if NET

On Wed, May 04, 2016 at 07:10:52PM +0200, Alexander Graf wrote:
We have a bunch of boards that define their vendor class identifier and client archs in the board files or in the distro config. Move everything to the generic Kconfig options.
This part is good and fine, thanks. Can you please do a follow-up to convert CONFIG_SPL_NET_VCI_STRING as well?
We're missing the distinction between i386 and x86_64, as I couldn't find any config variable that would tell us the difference. Is that really important to people? I guess not, so I left it out.
This part however is is from the RFC (and ARM isn't in it, so 0x100 is special magic). See https://tools.ietf.org/html/rfc4578 section 2.1. I suppose reading that now, the right value to use here for your use case actually makes what we're doing now a little harder as "Intel x86PC" is 0x0, and "EFI IA32" is 0x6. Or we could, if I'm reading the RFC right, just drop this part of the code entirely.

On 06.05.16 16:54, Tom Rini wrote:
On Wed, May 04, 2016 at 07:10:52PM +0200, Alexander Graf wrote:
We have a bunch of boards that define their vendor class identifier and client archs in the board files or in the distro config. Move everything to the generic Kconfig options.
This part is good and fine, thanks. Can you please do a follow-up to convert CONFIG_SPL_NET_VCI_STRING as well?
We're missing the distinction between i386 and x86_64, as I couldn't find any config variable that would tell us the difference. Is that really important to people? I guess not, so I left it out.
This part however is is from the RFC (and ARM isn't in it, so 0x100 is special magic). See https://tools.ietf.org/html/rfc4578 section 2.1. I suppose reading that now, the right value to use here for your use case actually makes what we're doing now a little harder as "Intel x86PC" is 0x0, and "EFI IA32" is 0x6. Or we could, if I'm reading the RFC right, just drop this part of the code entirely.
Which part? Just drop the ID? Definitely works for me :)
Alex

On Fri, May 06, 2016 at 05:22:28PM +0200, Alexander Graf wrote:
On 06.05.16 16:54, Tom Rini wrote:
On Wed, May 04, 2016 at 07:10:52PM +0200, Alexander Graf wrote:
We have a bunch of boards that define their vendor class identifier and client archs in the board files or in the distro config. Move everything to the generic Kconfig options.
This part is good and fine, thanks. Can you please do a follow-up to convert CONFIG_SPL_NET_VCI_STRING as well?
We're missing the distinction between i386 and x86_64, as I couldn't find any config variable that would tell us the difference. Is that really important to people? I guess not, so I left it out.
This part however is is from the RFC (and ARM isn't in it, so 0x100 is special magic). See https://tools.ietf.org/html/rfc4578 section 2.1. I suppose reading that now, the right value to use here for your use case actually makes what we're doing now a little harder as "Intel x86PC" is 0x0, and "EFI IA32" is 0x6. Or we could, if I'm reading the RFC right, just drop this part of the code entirely.
Which part? Just drop the ID? Definitely works for me :)
Ah, oops, nope, read the section too quickly. We MUST have this present. So we should try and make it correct which in your case means nothing yet, but you could future proof things with: default 0x6 if EFI_LOADER && X86 default 0x0

Am 06.05.2016 um 17:38 schrieb Tom Rini trini@konsulko.com:
On Fri, May 06, 2016 at 05:22:28PM +0200, Alexander Graf wrote:
On 06.05.16 16:54, Tom Rini wrote:
On Wed, May 04, 2016 at 07:10:52PM +0200, Alexander Graf wrote:
We have a bunch of boards that define their vendor class identifier and client archs in the board files or in the distro config. Move everything to the generic Kconfig options.
This part is good and fine, thanks. Can you please do a follow-up to convert CONFIG_SPL_NET_VCI_STRING as well?
We're missing the distinction between i386 and x86_64, as I couldn't find any config variable that would tell us the difference. Is that really important to people? I guess not, so I left it out.
This part however is is from the RFC (and ARM isn't in it, so 0x100 is special magic). See https://tools.ietf.org/html/rfc4578 section 2.1. I suppose reading that now, the right value to use here for your use case actually makes what we're doing now a little harder as "Intel x86PC" is 0x0, and "EFI IA32" is 0x6. Or we could, if I'm reading the RFC right, just drop this part of the code entirely.
Which part? Just drop the ID? Definitely works for me :)
Ah, oops, nope, read the section too quickly. We MUST have this present. So we should try and make it correct which in your case means nothing yet, but you could future proof things with: default 0x6 if EFI_LOADER && X86 default 0x0
Not really, since efi vs non-efi is a runtime decision. But similar to how I prefer a variable for vci I can do the same for the client id.
Alex
-- Tom

On Fri, May 06, 2016 at 10:54:23AM -0400, Tom Rini wrote:
On Wed, May 04, 2016 at 07:10:52PM +0200, Alexander Graf wrote:
We have a bunch of boards that define their vendor class identifier and client archs in the board files or in the distro config. Move everything to the generic Kconfig options.
This part is good and fine, thanks. Can you please do a follow-up to convert CONFIG_SPL_NET_VCI_STRING as well?
We're missing the distinction between i386 and x86_64, as I couldn't find any config variable that would tell us the difference. Is that really important to people? I guess not, so I left it out.
This part however is is from the RFC (and ARM isn't in it, so 0x100 is special magic). See https://tools.ietf.org/html/rfc4578 section 2.1. I suppose reading that now, the right value to use here for your use case actually makes what we're doing now a little harder as "Intel x86PC" is 0x0, and "EFI IA32" is 0x6. Or we could, if I'm reading the RFC right, just drop this part of the code entirely.
As far as I can tell (and referenced from EDK2 codebase), the canonical list is http://www.ietf.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xml#proc...
/ Leif

On Fri, May 06, 2016 at 05:07:28PM +0100, Leif Lindholm wrote:
On Fri, May 06, 2016 at 10:54:23AM -0400, Tom Rini wrote:
On Wed, May 04, 2016 at 07:10:52PM +0200, Alexander Graf wrote:
We have a bunch of boards that define their vendor class identifier and client archs in the board files or in the distro config. Move everything to the generic Kconfig options.
This part is good and fine, thanks. Can you please do a follow-up to convert CONFIG_SPL_NET_VCI_STRING as well?
We're missing the distinction between i386 and x86_64, as I couldn't find any config variable that would tell us the difference. Is that really important to people? I guess not, so I left it out.
This part however is is from the RFC (and ARM isn't in it, so 0x100 is special magic). See https://tools.ietf.org/html/rfc4578 section 2.1. I suppose reading that now, the right value to use here for your use case actually makes what we're doing now a little harder as "Intel x86PC" is 0x0, and "EFI IA32" is 0x6. Or we could, if I'm reading the RFC right, just drop this part of the code entirely.
As far as I can tell (and referenced from EDK2 codebase), the canonical list is http://www.ietf.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xml#proc...
That's even better, thanks!

Now that we can expose network functionality to EFI applications, the logical next step is to load them via pxe to execute them as well.
This patch adds the necessary bits to the distro script to automatically load and execute EFI payloads. It identifies the dhcp client as a uEFI capable PXE client, hoping the server returns a tftp path to a workable EFI binary that we can then execute.
To enable boards that don't come with a working device tree preloaded, this patch also adds support to load a device tree from the /dtb directory on the remote tftp server.
Signed-off-by: Alexander Graf agraf@suse.de --- include/config_distro_bootcmd.h | 37 ++++++++++++++++++++++++++++++++++++- net/bootp.c | 13 +++++++++++-- 2 files changed, 47 insertions(+), 3 deletions(-)
diff --git a/include/config_distro_bootcmd.h b/include/config_distro_bootcmd.h index 7f67344..c18731e 100644 --- a/include/config_distro_bootcmd.h +++ b/include/config_distro_bootcmd.h @@ -230,13 +230,48 @@ #endif
#if defined(CONFIG_CMD_DHCP) +#if defined(CONFIG_EFI_LOADER) +#if defined(CONFIG_ARM64) +#define BOOTENV_EFI_PXE_VCI "PXEClient:Arch:00011:UNDI:003000" +#elif defined(CONFIG_ARM) +#define BOOTENV_EFI_PXE_VCI "PXEClient:Arch:00010:UNDI:003000" +#else +#define BOOTENV_EFI_PXE_VCI "PXEClient:Arch:00000:UNDI:003000" +#endif + +/* + * Ask the dhcp server for an EFI binary. If we get one, check for a + * device tree in the same folder. Then boot everything. If the file was + * not an EFI binary, we just return from the bootefi command and continue. + */ +#define BOOTENV_EFI_RUN_DHCP \ + "setenv efi_fdtfile ${fdtfile}; " \ + BOOTENV_EFI_SET_FDTFILE_FALLBACK \ + "setenv efi_old_vci ${bootp_vci};" \ + "setenv bootp_vci " BOOTENV_EFI_PXE_VCI ";" \ + "if dhcp ${kernel_addr_r}; then " \ + "tftpboot ${fdt_addr_r} dtb/${efi_fdtfile};" \ + "if fdt addr ${fdt_addr_r}; then " \ + "bootefi ${kernel_addr_r} ${fdt_addr_r}; " \ + "else " \ + "bootefi ${kernel_addr_r} ${fdtcontroladdr};" \ + "fi;" \ + "fi;" \ + "setenv bootp_vci ${efi_old_vci};" \ + "setenv efi_fdtfile;" \ + "setenv efi_old_vci;" +#else +#define BOOTENV_EFI_RUN_DHCP +#endif #define BOOTENV_DEV_DHCP(devtypeu, devtypel, instance) \ "bootcmd_dhcp=" \ BOOTENV_RUN_NET_USB_START \ BOOTENV_RUN_NET_PCI_ENUM \ "if dhcp ${scriptaddr} ${boot_script_dhcp}; then " \ "source ${scriptaddr}; " \ - "fi\0" + "fi;" \ + BOOTENV_EFI_RUN_DHCP \ + "\0" #define BOOTENV_DEV_NAME_DHCP(devtypeu, devtypel, instance) \ "dhcp " #else diff --git a/net/bootp.c b/net/bootp.c index d718e35..85dc524 100644 --- a/net/bootp.c +++ b/net/bootp.c @@ -413,12 +413,21 @@ static void bootp_timeout_handler(void)
static u8 *add_vci(u8 *e) { + char *vci = NULL; + char *env_vci = getenv("bootp_vci"); + #if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_NET_VCI_STRING) - put_vci(e, CONFIG_SPL_NET_VCI_STRING); + vci = CONFIG_SPL_NET_VCI_STRING; #elif defined(CONFIG_BOOTP_VCI_STRING) - put_vci(e, CONFIG_BOOTP_VCI_STRING); + vci = CONFIG_BOOTP_VCI_STRING; #endif
+ if (env_vci) + vci = env_vci; + + if (vci) + put_vci(e, vci); + return e; }
participants (3)
-
Alexander Graf
-
Leif Lindholm
-
Tom Rini