[U-Boot] [PATCH v2 0/7] 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.
Changes from v1 -> v2:
- Only set client id to 0 on x86 - new patch: net: Fix client identifiers for ARM - new patch: net: Move CONFIG_SPL_NET_VCI_STRING into Kconfig - new patch: net: Optionally use pxe client arch from variable - Error out for unknown arch - Also set client arch variable
Alexander Graf (7): efi_loader: Add network access support bootp: Move vendor class identifier set to function net: Move the VCI and client arch values to Kconfig net: Fix client identifiers for ARM net: Move CONFIG_SPL_NET_VCI_STRING into Kconfig net: Optionally use pxe client arch from variable distro: Add efi pxe boot code
cmd/bootefi.c | 7 + configs/am335x_evm_defconfig | 1 + configs/am335x_evm_nor_defconfig | 1 + configs/am335x_evm_norboot_defconfig | 1 + configs/am335x_evm_spiboot_defconfig | 1 + configs/am335x_evm_usbspl_defconfig | 1 + configs/am335x_sl50_defconfig | 1 + configs/am437x_gp_evm_defconfig | 1 + configs/am437x_sk_evm_defconfig | 1 + configs/am43xx_evm_defconfig | 1 + configs/am43xx_evm_ethboot_defconfig | 1 + configs/am43xx_evm_qspiboot_defconfig | 1 + configs/am43xx_evm_usbhost_boot_defconfig | 1 + configs/birdland_bav335a_defconfig | 1 + configs/birdland_bav335b_defconfig | 1 + configs/ls2080a_emu_defconfig | 1 + configs/ls2080a_simu_defconfig | 1 + configs/pcm051_rev1_defconfig | 1 + configs/pcm051_rev3_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 | 47 ++++- 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 | 16 ++ net/bootp.c | 59 +++--- net/net.c | 4 +- net/tftp.c | 2 + 44 files changed, 554 insertions(+), 56 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 05800c4..5ee5929 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);

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 and on a zcu102 system.
Signed-off-by: Alexander Graf agraf@suse.de
---
v2 -> v3:
- Align initialization sequence with net code - Set device to initialized after init call - Align tx buffers to DMA alignment (rx gets memcpy'd) - Add comment about eth_rx() --- 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 | 314 +++++++++++++++++++++++++++++++++++++++++++++++ net/bootp.c | 2 + net/net.c | 4 +- net/tftp.c | 2 + 9 files changed, 455 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 05800c4..5ee5929 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..a95ff3c --- /dev/null +++ b/lib/efi_loader/efi_net.c @@ -0,0 +1,314 @@ +/* + * 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) +{ + struct efi_net_obj *netobj = container_of(this, struct efi_net_obj, net); + int ret; + EFI_ENTRY("%p, %lx, %lx", this, extra_rx, extra_tx); + + eth_halt(); + eth_set_current(); + ret = eth_init(); + if (ret < 0) { + eth_halt(); + return EFI_EXIT(EFI_DEVICE_ERROR); + } + + netobj->net_mode.state = EFI_NETWORK_INITIALIZED; + + 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); + } + + /* + * Some drivers need to have DMA aligned buffers, so if + * it's misaligned, let's use our own. + */ + if ((ulong)buffer & (ARCH_DMA_MINALIGN - 1)) { + void *packet = memalign(ARCH_DMA_MINALIGN, buffer_size); + memcpy(packet, buffer, buffer_size); + net_send_packet(packet, buffer_size); + free(packet); + } else { + 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; + /* XXX With device model this reads multiple packets, we only want 1 */ + 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);

Hi Alexander,
On 10 May 2016 at 15:25, Alexander Graf agraf@suse.de wrote:
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 and on a zcu102 system.
Signed-off-by: Alexander Graf agraf@suse.de
v2 -> v3:
- Align initialization sequence with net code
- Set device to initialized after init call
- Align tx buffers to DMA alignment (rx gets memcpy'd)
- Add comment about eth_rx()
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 | 314 +++++++++++++++++++++++++++++++++++++++++++++++ net/bootp.c | 2 + net/net.c | 4 +- net/tftp.c | 2 + 9 files changed, 455 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 05800c4..5ee5929 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..a95ff3c --- /dev/null +++ b/lib/efi_loader/efi_net.c @@ -0,0 +1,314 @@ +/*
- 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)
+{
struct efi_net_obj *netobj = container_of(this, struct efi_net_obj, net);
int ret;
EFI_ENTRY("%p, %lx, %lx", this, extra_rx, extra_tx);
eth_halt();
eth_set_current();
ret = eth_init();
if (ret < 0) {
eth_halt();
return EFI_EXIT(EFI_DEVICE_ERROR);
}
netobj->net_mode.state = EFI_NETWORK_INITIALIZED;
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);
}
/*
* Some drivers need to have DMA aligned buffers, so if
* it's misaligned, let's use our own.
*/
if ((ulong)buffer & (ARCH_DMA_MINALIGN - 1)) {
void *packet = memalign(ARCH_DMA_MINALIGN, buffer_size);
memcpy(packet, buffer, buffer_size);
net_send_packet(packet, buffer_size);
free(packet);
} else {
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;
/* XXX With device model this reads multiple packets, we only want 1 */
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;
Why is this statically created? Shouldn't it come from the U-Boot ethernet tables dynamically when it is needed?
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);
-- 1.8.5.6
U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot
Regards, Simon

Am 14.05.2016 um 21:49 schrieb Simon Glass sjg@chromium.org:
Hi Alexander,
On 10 May 2016 at 15:25, Alexander Graf agraf@suse.de wrote: 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 and on a zcu102 system.
Signed-off-by: Alexander Graf agraf@suse.de
v2 -> v3:
- Align initialization sequence with net code
- Set device to initialized after init call
- Align tx buffers to DMA alignment (rx gets memcpy'd)
- Add comment about eth_rx()
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 | 314 +++++++++++++++++++++++++++++++++++++++++++++++ net/bootp.c | 2 + net/net.c | 4 +- net/tftp.c | 2 + 9 files changed, 455 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 05800c4..5ee5929 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..a95ff3c --- /dev/null +++ b/lib/efi_loader/efi_net.c @@ -0,0 +1,314 @@ +/*
- 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)
+{
struct efi_net_obj *netobj = container_of(this, struct efi_net_obj, net);
int ret;
EFI_ENTRY("%p, %lx, %lx", this, extra_rx, extra_tx);
eth_halt();
eth_set_current();
ret = eth_init();
if (ret < 0) {
eth_halt();
return EFI_EXIT(EFI_DEVICE_ERROR);
}
netobj->net_mode.state = EFI_NETWORK_INITIALIZED;
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);
}
/*
* Some drivers need to have DMA aligned buffers, so if
* it's misaligned, let's use our own.
*/
if ((ulong)buffer & (ARCH_DMA_MINALIGN - 1)) {
void *packet = memalign(ARCH_DMA_MINALIGN, buffer_size);
memcpy(packet, buffer, buffer_size);
net_send_packet(packet, buffer_size);
free(packet);
} else {
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;
/* XXX With device model this reads multiple packets, we only want 1 */
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;
Why is this statically created? Shouldn't it come from the U-Boot ethernet tables dynamically when it is needed?
The driver as is only exposes a single interface for the currently selected network device. My main goal behind this was pxe boot - where you usually load the payload from tftp and continue acting on the same interface later on.
I guess we could create devices for all network devices as well. I remember that I wanted to only expose network if it's really necessary because of resource usage. But I don't remember exactly what resource the peoblem was otoh ;).
Alex

Hi Alexander,
On 14 May 2016 at 14:34, Alexander Graf agraf@suse.de wrote:
Am 14.05.2016 um 21:49 schrieb Simon Glass sjg@chromium.org:
Hi Alexander,
On 10 May 2016 at 15:25, Alexander Graf agraf@suse.de wrote: 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 and on a zcu102 system.
Signed-off-by: Alexander Graf agraf@suse.de
v2 -> v3:
- Align initialization sequence with net code
- Set device to initialized after init call
- Align tx buffers to DMA alignment (rx gets memcpy'd)
- Add comment about eth_rx()
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 | 314 +++++++++++++++++++++++++++++++++++++++++++++++ net/bootp.c | 2 + net/net.c | 4 +- net/tftp.c | 2 + 9 files changed, 455 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 05800c4..5ee5929 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..a95ff3c --- /dev/null +++ b/lib/efi_loader/efi_net.c @@ -0,0 +1,314 @@ +/*
- 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)
+{
struct efi_net_obj *netobj = container_of(this, struct efi_net_obj, net);
int ret;
EFI_ENTRY("%p, %lx, %lx", this, extra_rx, extra_tx);
eth_halt();
eth_set_current();
ret = eth_init();
if (ret < 0) {
eth_halt();
return EFI_EXIT(EFI_DEVICE_ERROR);
}
netobj->net_mode.state = EFI_NETWORK_INITIALIZED;
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);
}
/*
* Some drivers need to have DMA aligned buffers, so if
* it's misaligned, let's use our own.
*/
if ((ulong)buffer & (ARCH_DMA_MINALIGN - 1)) {
void *packet = memalign(ARCH_DMA_MINALIGN, buffer_size);
memcpy(packet, buffer, buffer_size);
net_send_packet(packet, buffer_size);
free(packet);
} else {
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;
/* XXX With device model this reads multiple packets, we only want 1 */
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;
Why is this statically created? Shouldn't it come from the U-Boot ethernet tables dynamically when it is needed?
The driver as is only exposes a single interface for the currently selected network device. My main goal behind this was pxe boot - where you usually load the payload from tftp and continue acting on the same interface later on.
I guess we could create devices for all network devices as well. I remember that I wanted to only expose network if it's really necessary because of resource usage. But I don't remember exactly what resource the peoblem was otoh ;).
I think in general (given that you are writing this code and this is the best opportunity to get the design right) that things like this should look up U-Boot tables rather than creating their own. So a model where the device is scanned as needed seems better. Then if we want to change the constraints later, e.g. devices appear that were not previously present, then we can deal with that.
I understand with block devices your point that there is no single table of block devices. However, this support will be applied soon (see u-boot-dm/master), even without driver model. I think we should fix these things rather than work around them, as the code will be more maintainable that way.
In this case I wonder if we should have an EFI device uclass, and allow devices to have this as a child? It would allow for easy enumeration and avoid another system-specific device table (something that driver model is trying to reduce).
Regards, Simon

On 16.05.16 15:24, Simon Glass wrote:
Hi Alexander,
On 14 May 2016 at 14:34, Alexander Graf agraf@suse.de wrote:
Am 14.05.2016 um 21:49 schrieb Simon Glass sjg@chromium.org:
Hi Alexander,
On 10 May 2016 at 15:25, Alexander Graf agraf@suse.de wrote: 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 and on a zcu102 system.
Signed-off-by: Alexander Graf agraf@suse.de
v2 -> v3:
- Align initialization sequence with net code
- Set device to initialized after init call
- Align tx buffers to DMA alignment (rx gets memcpy'd)
- Add comment about eth_rx()
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 | 314 +++++++++++++++++++++++++++++++++++++++++++++++ net/bootp.c | 2 + net/net.c | 4 +- net/tftp.c | 2 + 9 files changed, 455 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 05800c4..5ee5929 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..a95ff3c --- /dev/null +++ b/lib/efi_loader/efi_net.c @@ -0,0 +1,314 @@ +/*
- 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)
+{
struct efi_net_obj *netobj = container_of(this, struct efi_net_obj, net);
int ret;
EFI_ENTRY("%p, %lx, %lx", this, extra_rx, extra_tx);
eth_halt();
eth_set_current();
ret = eth_init();
if (ret < 0) {
eth_halt();
return EFI_EXIT(EFI_DEVICE_ERROR);
}
netobj->net_mode.state = EFI_NETWORK_INITIALIZED;
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);
}
/*
* Some drivers need to have DMA aligned buffers, so if
* it's misaligned, let's use our own.
*/
if ((ulong)buffer & (ARCH_DMA_MINALIGN - 1)) {
void *packet = memalign(ARCH_DMA_MINALIGN, buffer_size);
memcpy(packet, buffer, buffer_size);
net_send_packet(packet, buffer_size);
free(packet);
} else {
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;
/* XXX With device model this reads multiple packets, we only want 1 */
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;
Why is this statically created? Shouldn't it come from the U-Boot ethernet tables dynamically when it is needed?
The driver as is only exposes a single interface for the currently selected network device. My main goal behind this was pxe boot - where you usually load the payload from tftp and continue acting on the same interface later on.
I guess we could create devices for all network devices as well. I remember that I wanted to only expose network if it's really necessary because of resource usage. But I don't remember exactly what resource the peoblem was otoh ;).
I think in general (given that you are writing this code and this is the best opportunity to get the design right) that things like this should look up U-Boot tables rather than creating their own. So a model where the device is scanned as needed seems better. Then if we want to change the constraints later, e.g. devices appear that were not previously present, then we can deal with that.
I understand with block devices your point that there is no single table of block devices. However, this support will be applied soon (see u-boot-dm/master), even without driver model. I think we should fix these things rather than work around them, as the code will be more maintainable that way.
I agree wholeheartedly :). The beauty of "proper" open source code is that you can actually modify the guts of it to make the code easier at the end of the day.
In this case I wonder if we should have an EFI device uclass, and allow devices to have this as a child? It would allow for easy enumeration and avoid another system-specific device table (something that driver model is trying to reduce).
I'm not 100% sure the EFI driver exposure is actually a child-parent relationship with the respective driver model objects. In Java speech it's more of an "interface" definition than a subclass.
But that said, I think that it may still make sense to move it into the respective driver cores and just have the EFI structs part of the block device structs.
However I don't consider it as very high priority at the moment. Functionally nothing should change from a payload point of view and the current code allows us to iron out bugs and have non-believers gain faith in the whole uEFI story without breaking non-uEFI code paths ;). And all of the actual interface code shouldn't change with a move into device objects.
So how about we start to tackle this once the non-DM code is gone? That should simplify things for everyone.
Alex

+Tom
Hi Alex,
On 16 May 2016 at 12:06, Alexander Graf agraf@suse.de wrote:
On 16.05.16 15:24, Simon Glass wrote:
Hi Alexander,
On 14 May 2016 at 14:34, Alexander Graf agraf@suse.de wrote:
Am 14.05.2016 um 21:49 schrieb Simon Glass sjg@chromium.org:
Hi Alexander,
On 10 May 2016 at 15:25, Alexander Graf agraf@suse.de wrote: 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 and on a zcu102 system.
Signed-off-by: Alexander Graf agraf@suse.de
v2 -> v3:
- Align initialization sequence with net code
- Set device to initialized after init call
- Align tx buffers to DMA alignment (rx gets memcpy'd)
- Add comment about eth_rx()
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 | 314 +++++++++++++++++++++++++++++++++++++++++++++++ net/bootp.c | 2 + net/net.c | 4 +- net/tftp.c | 2 + 9 files changed, 455 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 05800c4..5ee5929 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..a95ff3c --- /dev/null +++ b/lib/efi_loader/efi_net.c @@ -0,0 +1,314 @@ +/*
- 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)
+{
struct efi_net_obj *netobj = container_of(this, struct efi_net_obj, net);
int ret;
EFI_ENTRY("%p, %lx, %lx", this, extra_rx, extra_tx);
eth_halt();
eth_set_current();
ret = eth_init();
if (ret < 0) {
eth_halt();
return EFI_EXIT(EFI_DEVICE_ERROR);
}
netobj->net_mode.state = EFI_NETWORK_INITIALIZED;
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);
}
/*
* Some drivers need to have DMA aligned buffers, so if
* it's misaligned, let's use our own.
*/
if ((ulong)buffer & (ARCH_DMA_MINALIGN - 1)) {
void *packet = memalign(ARCH_DMA_MINALIGN, buffer_size);
memcpy(packet, buffer, buffer_size);
net_send_packet(packet, buffer_size);
free(packet);
} else {
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;
/* XXX With device model this reads multiple packets, we only want 1 */
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;
Why is this statically created? Shouldn't it come from the U-Boot ethernet tables dynamically when it is needed?
The driver as is only exposes a single interface for the currently selected network device. My main goal behind this was pxe boot - where you usually load the payload from tftp and continue acting on the same interface later on.
I guess we could create devices for all network devices as well. I remember that I wanted to only expose network if it's really necessary because of resource usage. But I don't remember exactly what resource the peoblem was otoh ;).
I think in general (given that you are writing this code and this is the best opportunity to get the design right) that things like this should look up U-Boot tables rather than creating their own. So a model where the device is scanned as needed seems better. Then if we want to change the constraints later, e.g. devices appear that were not previously present, then we can deal with that.
I understand with block devices your point that there is no single table of block devices. However, this support will be applied soon (see u-boot-dm/master), even without driver model. I think we should fix these things rather than work around them, as the code will be more maintainable that way.
I agree wholeheartedly :). The beauty of "proper" open source code is that you can actually modify the guts of it to make the code easier at the end of the day.
In this case I wonder if we should have an EFI device uclass, and allow devices to have this as a child? It would allow for easy enumeration and avoid another system-specific device table (something that driver model is trying to reduce).
I'm not 100% sure the EFI driver exposure is actually a child-parent relationship with the respective driver model objects. In Java speech it's more of an "interface" definition than a subclass.
The parent/child relationship is how this is done in driver model. For example, MMC devices (UCLASS_MMC) provide a block device interface, as a child device in UCLASS_BLK.
But that said, I think that it may still make sense to move it into the respective driver cores and just have the EFI structs part of the block device structs.
But won't you have different types of drivers? E.g. video, network?
However I don't consider it as very high priority at the moment. Functionally nothing should change from a payload point of view and the current code allows us to iron out bugs and have non-believers gain faith in the whole uEFI story without breaking non-uEFI code paths ;). And all of the actual interface code shouldn't change with a move into device objects.
So how about we start to tackle this once the non-DM code is gone? That should simplify things for everyone.
I think instead we should set this up so that it works nicely with DM. The non-DM code will be around for a long time. I'm not sure how long.
At present the EFI impl really doesn't use it at all. I'm not too bothered about when, but I think it is worth looking at before too long. I also suggest that we require DM with new features like this, to encourage people to move.
Regards, Simon

On 05/18/2016 07:21 PM, Simon Glass wrote:
+Tom
Hi Alex,
On 16 May 2016 at 12:06, Alexander Graf agraf@suse.de wrote:
On 16.05.16 15:24, Simon Glass wrote:
Hi Alexander,
On 14 May 2016 at 14:34, Alexander Graf agraf@suse.de wrote:
Am 14.05.2016 um 21:49 schrieb Simon Glass sjg@chromium.org:
Hi Alexander,
On 10 May 2016 at 15:25, Alexander Graf agraf@suse.de wrote: 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 and on a zcu102 system.
Signed-off-by: Alexander Graf agraf@suse.de
v2 -> v3:
- Align initialization sequence with net code
- Set device to initialized after init call
- Align tx buffers to DMA alignment (rx gets memcpy'd)
- Add comment about eth_rx()
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 | 314 +++++++++++++++++++++++++++++++++++++++++++++++ net/bootp.c | 2 + net/net.c | 4 +- net/tftp.c | 2 + 9 files changed, 455 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 05800c4..5ee5929 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..a95ff3c --- /dev/null +++ b/lib/efi_loader/efi_net.c @@ -0,0 +1,314 @@ +/*
- 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)
+{
struct efi_net_obj *netobj = container_of(this, struct efi_net_obj, net);
int ret;
EFI_ENTRY("%p, %lx, %lx", this, extra_rx, extra_tx);
eth_halt();
eth_set_current();
ret = eth_init();
if (ret < 0) {
eth_halt();
return EFI_EXIT(EFI_DEVICE_ERROR);
}
netobj->net_mode.state = EFI_NETWORK_INITIALIZED;
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);
}
/*
* Some drivers need to have DMA aligned buffers, so if
* it's misaligned, let's use our own.
*/
if ((ulong)buffer & (ARCH_DMA_MINALIGN - 1)) {
void *packet = memalign(ARCH_DMA_MINALIGN, buffer_size);
memcpy(packet, buffer, buffer_size);
net_send_packet(packet, buffer_size);
free(packet);
} else {
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;
/* XXX With device model this reads multiple packets, we only want 1 */
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;
Why is this statically created? Shouldn't it come from the U-Boot ethernet tables dynamically when it is needed?
The driver as is only exposes a single interface for the currently selected network device. My main goal behind this was pxe boot - where you usually load the payload from tftp and continue acting on the same interface later on.
I guess we could create devices for all network devices as well. I remember that I wanted to only expose network if it's really necessary because of resource usage. But I don't remember exactly what resource the peoblem was otoh ;).
I think in general (given that you are writing this code and this is the best opportunity to get the design right) that things like this should look up U-Boot tables rather than creating their own. So a model where the device is scanned as needed seems better. Then if we want to change the constraints later, e.g. devices appear that were not previously present, then we can deal with that.
I understand with block devices your point that there is no single table of block devices. However, this support will be applied soon (see u-boot-dm/master), even without driver model. I think we should fix these things rather than work around them, as the code will be more maintainable that way.
I agree wholeheartedly :). The beauty of "proper" open source code is that you can actually modify the guts of it to make the code easier at the end of the day.
In this case I wonder if we should have an EFI device uclass, and allow devices to have this as a child? It would allow for easy enumeration and avoid another system-specific device table (something that driver model is trying to reduce).
I'm not 100% sure the EFI driver exposure is actually a child-parent relationship with the respective driver model objects. In Java speech it's more of an "interface" definition than a subclass.
The parent/child relationship is how this is done in driver model. For example, MMC devices (UCLASS_MMC) provide a block device interface, as a child device in UCLASS_BLK.
I'd have to take a closer look to really say something intelligible here.
But that said, I think that it may still make sense to move it into the respective driver cores and just have the EFI structs part of the block device structs.
But won't you have different types of drivers? E.g. video, network?
We do have different types of drivers, that's why there are different files in lib/efi_loader :).
However I don't consider it as very high priority at the moment. Functionally nothing should change from a payload point of view and the current code allows us to iron out bugs and have non-believers gain faith in the whole uEFI story without breaking non-uEFI code paths ;). And all of the actual interface code shouldn't change with a move into device objects.
So how about we start to tackle this once the non-DM code is gone? That should simplify things for everyone.
I think instead we should set this up so that it works nicely with DM. The non-DM code will be around for a long time. I'm not sure how long.
At present the EFI impl really doesn't use it at all. I'm not too bothered about when, but I think it is worth looking at before too long. I also suggest that we require DM with new features like this, to encourage people to move.
You might overestimate the power of uEFI support. Most people run away screaming rather than embrace uEFI when you mention it. My most important goal for now is to make sure that they do not run away. Forcing anything on them will cause that to happen though.
Alex

Hi Alexander.
On 19 May 2016 at 10:18, Alexander Graf agraf@suse.de wrote:
On 05/18/2016 07:21 PM, Simon Glass wrote:
+Tom
Hi Alex,
On 16 May 2016 at 12:06, Alexander Graf agraf@suse.de wrote:
On 16.05.16 15:24, Simon Glass wrote:
Hi Alexander,
On 14 May 2016 at 14:34, Alexander Graf agraf@suse.de wrote:
Am 14.05.2016 um 21:49 schrieb Simon Glass sjg@chromium.org:
Hi Alexander,
> On 10 May 2016 at 15:25, Alexander Graf agraf@suse.de wrote: > 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 and on a zcu102 system. > > Signed-off-by: Alexander Graf agraf@suse.de > > --- > > v2 -> v3: > > - Align initialization sequence with net code > - Set device to initialized after init call > - Align tx buffers to DMA alignment (rx gets memcpy'd) > - Add comment about eth_rx() > --- > 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 | 314 +++++++++++++++++++++++++++++++++++++++++++++++ > net/bootp.c | 2 + > net/net.c | 4 +- > net/tftp.c | 2 + > 9 files changed, 455 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 05800c4..5ee5929 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..a95ff3c > --- /dev/null > +++ b/lib/efi_loader/efi_net.c > @@ -0,0 +1,314 @@ > +/* > + * 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) > +{ > + struct efi_net_obj *netobj = container_of(this, struct efi_net_obj, net); > + int ret; > + EFI_ENTRY("%p, %lx, %lx", this, extra_rx, extra_tx); > + > + eth_halt(); > + eth_set_current(); > + ret = eth_init(); > + if (ret < 0) { > + eth_halt(); > + return EFI_EXIT(EFI_DEVICE_ERROR); > + } > + > + netobj->net_mode.state = EFI_NETWORK_INITIALIZED; > + > + 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); > + } > + > + /* > + * Some drivers need to have DMA aligned buffers, so if > + * it's misaligned, let's use our own. > + */ > + if ((ulong)buffer & (ARCH_DMA_MINALIGN - 1)) { > + void *packet = memalign(ARCH_DMA_MINALIGN, buffer_size); > + memcpy(packet, buffer, buffer_size); > + net_send_packet(packet, buffer_size); > + free(packet); > + } else { > + 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; > + /* XXX With device model this reads multiple packets, we only want 1 */ > + 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;
Why is this statically created? Shouldn't it come from the U-Boot ethernet tables dynamically when it is needed?
The driver as is only exposes a single interface for the currently selected network device. My main goal behind this was pxe boot - where you usually load the payload from tftp and continue acting on the same interface later on.
I guess we could create devices for all network devices as well. I remember that I wanted to only expose network if it's really necessary because of resource usage. But I don't remember exactly what resource the peoblem was otoh ;).
I think in general (given that you are writing this code and this is the best opportunity to get the design right) that things like this should look up U-Boot tables rather than creating their own. So a model where the device is scanned as needed seems better. Then if we want to change the constraints later, e.g. devices appear that were not previously present, then we can deal with that.
I understand with block devices your point that there is no single table of block devices. However, this support will be applied soon (see u-boot-dm/master), even without driver model. I think we should fix these things rather than work around them, as the code will be more maintainable that way.
I agree wholeheartedly :). The beauty of "proper" open source code is that you can actually modify the guts of it to make the code easier at the end of the day.
In this case I wonder if we should have an EFI device uclass, and allow devices to have this as a child? It would allow for easy enumeration and avoid another system-specific device table (something that driver model is trying to reduce).
I'm not 100% sure the EFI driver exposure is actually a child-parent relationship with the respective driver model objects. In Java speech it's more of an "interface" definition than a subclass.
The parent/child relationship is how this is done in driver model. For example, MMC devices (UCLASS_MMC) provide a block device interface, as a child device in UCLASS_BLK.
I'd have to take a closer look to really say something intelligible here.
OK - when you get a chance, take a look at driver/block/blk-uclass.c in mainline - e.g. blk_get_devnum_by_type(). I think something similar might work for EFI.
But that said, I think that it may still make sense to move it into the respective driver cores and just have the EFI structs part of the block device structs.
But won't you have different types of drivers? E.g. video, network?
We do have different types of drivers, that's why there are different files in lib/efi_loader :).
Right, so I don't really see how you can put (for example) a video driver in a block device struct. I must be misunderstanding your idea here.
However I don't consider it as very high priority at the moment. Functionally nothing should change from a payload point of view and the current code allows us to iron out bugs and have non-believers gain faith in the whole uEFI story without breaking non-uEFI code paths ;). And all of the actual interface code shouldn't change with a move into device objects.
So how about we start to tackle this once the non-DM code is gone? That should simplify things for everyone.
I think instead we should set this up so that it works nicely with DM. The non-DM code will be around for a long time. I'm not sure how long.
At present the EFI impl really doesn't use it at all. I'm not too bothered about when, but I think it is worth looking at before too long. I also suggest that we require DM with new features like this, to encourage people to move.
You might overestimate the power of uEFI support. Most people run away screaming rather than embrace uEFI when you mention it. My most important goal for now is to make sure that they do not run away. Forcing anything on them will cause that to happen though.
Just to be clear, what are you wanting to avoid forcing on them? I'm really not following. My response here is that I'd like to see the EFI support use driver model at some point, rather than inventing its own tables. As I say, I'm not too worried about when. But I'd encourage you to take a look at some point.
Regards, Simon

On Fri, May 06, 2016 at 09:01:01PM +0200, Alexander Graf wrote:
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
Applied to u-boot/master, thanks!

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 Fri, May 06, 2016 at 09:01:02PM +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

On Fri, May 06, 2016 at 09:01:02PM +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
Applied to u-boot/master, thanks!

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
---
v1 -> v2:
- Only set client id to 0 on x86 --- 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..64fd0f9 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 if X86 + +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 Fri, May 06, 2016 at 09:01:03PM +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.
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
Reviewed-by: Tom Rini trini@konsulko.com

On Fri, May 06, 2016 at 09:01:03PM +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.
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 Reviewed-by: Tom Rini trini@konsulko.com
Applied to u-boot/master, thanks!

There are client identifiers specifically reserved for ARM U-Boot according to http://www.ietf.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xml#proc....
So let's actually make use of them rather than the bogus 0x100 that we emitted so far.
Signed-off-by: Alexander Graf agraf@suse.de --- net/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/net/Kconfig b/net/Kconfig index 64fd0f9..414c549 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -34,7 +34,8 @@ config NET_TFTP_VARS
config BOOTP_PXE_CLIENTARCH hex - default 0x100 if ARM + default 0x16 if ARM64 + default 0x15 if ARM default 0 if X86
config BOOTP_VCI_STRING

On Fri, May 06, 2016 at 09:01:04PM +0200, Alexander Graf wrote:
There are client identifiers specifically reserved for ARM U-Boot according to http://www.ietf.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xml#proc....
So let's actually make use of them rather than the bogus 0x100 that we emitted so far.
Signed-off-by: Alexander Graf agraf@suse.de
Reviewed-by: Tom Rini trini@konsulko.com

On Fri, May 06, 2016 at 09:01:04PM +0200, Alexander Graf wrote:
There are client identifiers specifically reserved for ARM U-Boot according to http://www.ietf.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xml#proc....
So let's actually make use of them rather than the bogus 0x100 that we emitted so far.
Signed-off-by: Alexander Graf agraf@suse.de Reviewed-by: Tom Rini trini@konsulko.com
Applied to u-boot/master, thanks!

This patch also adds the SPL time VCI string into Kconfig.
Signed-off-by: Alexander Graf agraf@suse.de --- configs/am335x_evm_defconfig | 1 + configs/am335x_evm_nor_defconfig | 1 + configs/am335x_evm_norboot_defconfig | 1 + configs/am335x_evm_spiboot_defconfig | 1 + configs/am335x_evm_usbspl_defconfig | 1 + configs/am335x_sl50_defconfig | 1 + configs/am437x_gp_evm_defconfig | 1 + configs/am437x_sk_evm_defconfig | 1 + configs/am43xx_evm_defconfig | 1 + configs/am43xx_evm_ethboot_defconfig | 1 + configs/am43xx_evm_qspiboot_defconfig | 1 + configs/am43xx_evm_usbhost_boot_defconfig | 1 + configs/birdland_bav335a_defconfig | 1 + configs/birdland_bav335b_defconfig | 1 + configs/pcm051_rev1_defconfig | 1 + configs/pcm051_rev3_defconfig | 1 + net/Kconfig | 3 +++ 17 files changed, 19 insertions(+)
diff --git a/configs/am335x_evm_defconfig b/configs/am335x_evm_defconfig index 62f26b5..2430381 100644 --- a/configs/am335x_evm_defconfig +++ b/configs/am335x_evm_defconfig @@ -42,3 +42,4 @@ CONFIG_G_DNL_MANUFACTURER="Texas Instruments" CONFIG_G_DNL_VENDOR_NUM=0x0451 CONFIG_G_DNL_PRODUCT_NUM=0xd022 CONFIG_OF_LIBFDT=y +CONFIG_SPL_NET_VCI_STRING="AM335x U-Boot SPL" diff --git a/configs/am335x_evm_nor_defconfig b/configs/am335x_evm_nor_defconfig index f230671..76a004e 100644 --- a/configs/am335x_evm_nor_defconfig +++ b/configs/am335x_evm_nor_defconfig @@ -38,3 +38,4 @@ CONFIG_G_DNL_MANUFACTURER="Texas Instruments" CONFIG_G_DNL_VENDOR_NUM=0x0451 CONFIG_G_DNL_PRODUCT_NUM=0xd022 CONFIG_OF_LIBFDT=y +CONFIG_SPL_NET_VCI_STRING="AM335x U-Boot SPL" diff --git a/configs/am335x_evm_norboot_defconfig b/configs/am335x_evm_norboot_defconfig index 3fbc07b..99fc555 100644 --- a/configs/am335x_evm_norboot_defconfig +++ b/configs/am335x_evm_norboot_defconfig @@ -35,3 +35,4 @@ CONFIG_G_DNL_MANUFACTURER="Texas Instruments" CONFIG_G_DNL_VENDOR_NUM=0x0451 CONFIG_G_DNL_PRODUCT_NUM=0xd022 CONFIG_OF_LIBFDT=y +CONFIG_SPL_NET_VCI_STRING="AM335x U-Boot SPL" diff --git a/configs/am335x_evm_spiboot_defconfig b/configs/am335x_evm_spiboot_defconfig index 65d88d8..d5aa3a2 100644 --- a/configs/am335x_evm_spiboot_defconfig +++ b/configs/am335x_evm_spiboot_defconfig @@ -38,3 +38,4 @@ CONFIG_G_DNL_MANUFACTURER="Texas Instruments" CONFIG_G_DNL_VENDOR_NUM=0x0451 CONFIG_G_DNL_PRODUCT_NUM=0xd022 CONFIG_OF_LIBFDT=y +CONFIG_SPL_NET_VCI_STRING="AM335x U-Boot SPL" diff --git a/configs/am335x_evm_usbspl_defconfig b/configs/am335x_evm_usbspl_defconfig index eee5e9b..cba5e84 100644 --- a/configs/am335x_evm_usbspl_defconfig +++ b/configs/am335x_evm_usbspl_defconfig @@ -38,3 +38,4 @@ CONFIG_G_DNL_MANUFACTURER="Texas Instruments" CONFIG_G_DNL_VENDOR_NUM=0x0451 CONFIG_G_DNL_PRODUCT_NUM=0xd022 CONFIG_OF_LIBFDT=y +CONFIG_SPL_NET_VCI_STRING="AM335x U-Boot SPL" diff --git a/configs/am335x_sl50_defconfig b/configs/am335x_sl50_defconfig index c227ef4..3cd40e9 100644 --- a/configs/am335x_sl50_defconfig +++ b/configs/am335x_sl50_defconfig @@ -25,3 +25,4 @@ CONFIG_CMD_FAT=y CONFIG_CMD_FS_GENERIC=y CONFIG_SYS_NS16550=y CONFIG_OF_LIBFDT=y +CONFIG_SPL_NET_VCI_STRING="AM335x U-Boot SPL" diff --git a/configs/am437x_gp_evm_defconfig b/configs/am437x_gp_evm_defconfig index 03b02ac..968f2f3 100644 --- a/configs/am437x_gp_evm_defconfig +++ b/configs/am437x_gp_evm_defconfig @@ -47,3 +47,4 @@ CONFIG_USB_GADGET_DOWNLOAD=y CONFIG_G_DNL_MANUFACTURER="Texas Instruments" CONFIG_G_DNL_VENDOR_NUM=0x0403 CONFIG_G_DNL_PRODUCT_NUM=0xbd00 +CONFIG_SPL_NET_VCI_STRING="AM43xx U-Boot SPL" diff --git a/configs/am437x_sk_evm_defconfig b/configs/am437x_sk_evm_defconfig index 48ec91f..1d50c0b 100644 --- a/configs/am437x_sk_evm_defconfig +++ b/configs/am437x_sk_evm_defconfig @@ -51,3 +51,4 @@ CONFIG_USB_GADGET_DOWNLOAD=y CONFIG_G_DNL_MANUFACTURER="Texas Instruments" CONFIG_G_DNL_VENDOR_NUM=0x0403 CONFIG_G_DNL_PRODUCT_NUM=0xbd00 +CONFIG_SPL_NET_VCI_STRING="AM43xx U-Boot SPL" diff --git a/configs/am43xx_evm_defconfig b/configs/am43xx_evm_defconfig index a6ae011..4911b61 100644 --- a/configs/am43xx_evm_defconfig +++ b/configs/am43xx_evm_defconfig @@ -39,3 +39,4 @@ CONFIG_G_DNL_MANUFACTURER="Texas Instruments" CONFIG_G_DNL_VENDOR_NUM=0x0403 CONFIG_G_DNL_PRODUCT_NUM=0xbd00 CONFIG_OF_LIBFDT=y +CONFIG_SPL_NET_VCI_STRING="AM43xx U-Boot SPL" diff --git a/configs/am43xx_evm_ethboot_defconfig b/configs/am43xx_evm_ethboot_defconfig index 662556a..ee4716d 100644 --- a/configs/am43xx_evm_ethboot_defconfig +++ b/configs/am43xx_evm_ethboot_defconfig @@ -38,3 +38,4 @@ CONFIG_G_DNL_MANUFACTURER="Texas Instruments" CONFIG_G_DNL_VENDOR_NUM=0x0403 CONFIG_G_DNL_PRODUCT_NUM=0xbd00 CONFIG_OF_LIBFDT=y +CONFIG_SPL_NET_VCI_STRING="AM43xx U-Boot SPL" diff --git a/configs/am43xx_evm_qspiboot_defconfig b/configs/am43xx_evm_qspiboot_defconfig index 00fa6be..4d18231 100644 --- a/configs/am43xx_evm_qspiboot_defconfig +++ b/configs/am43xx_evm_qspiboot_defconfig @@ -37,3 +37,4 @@ CONFIG_G_DNL_MANUFACTURER="Texas Instruments" CONFIG_G_DNL_VENDOR_NUM=0x0403 CONFIG_G_DNL_PRODUCT_NUM=0xbd00 CONFIG_OF_LIBFDT=y +CONFIG_SPL_NET_VCI_STRING="AM43xx U-Boot SPL" diff --git a/configs/am43xx_evm_usbhost_boot_defconfig b/configs/am43xx_evm_usbhost_boot_defconfig index e3d6b57..c336419 100644 --- a/configs/am43xx_evm_usbhost_boot_defconfig +++ b/configs/am43xx_evm_usbhost_boot_defconfig @@ -38,3 +38,4 @@ CONFIG_G_DNL_MANUFACTURER="Texas Instruments" CONFIG_G_DNL_VENDOR_NUM=0x0403 CONFIG_G_DNL_PRODUCT_NUM=0xbd00 CONFIG_OF_LIBFDT=y +CONFIG_SPL_NET_VCI_STRING="AM43xx U-Boot SPL" diff --git a/configs/birdland_bav335a_defconfig b/configs/birdland_bav335a_defconfig index 44ed671..bb5db5c 100644 --- a/configs/birdland_bav335a_defconfig +++ b/configs/birdland_bav335a_defconfig @@ -38,3 +38,4 @@ CONFIG_G_DNL_MANUFACTURER="Texas Instruments" CONFIG_G_DNL_VENDOR_NUM=0x0451 CONFIG_G_DNL_PRODUCT_NUM=0xd022 CONFIG_OF_LIBFDT=y +CONFIG_SPL_NET_VCI_STRING="BAV335x U-Boot SPL" diff --git a/configs/birdland_bav335b_defconfig b/configs/birdland_bav335b_defconfig index 861bdcf..39cc222 100644 --- a/configs/birdland_bav335b_defconfig +++ b/configs/birdland_bav335b_defconfig @@ -38,3 +38,4 @@ CONFIG_G_DNL_MANUFACTURER="Texas Instruments" CONFIG_G_DNL_VENDOR_NUM=0x0451 CONFIG_G_DNL_PRODUCT_NUM=0xd022 CONFIG_OF_LIBFDT=y +CONFIG_SPL_NET_VCI_STRING="BAV335x U-Boot SPL" diff --git a/configs/pcm051_rev1_defconfig b/configs/pcm051_rev1_defconfig index 7f29119..27f681f 100644 --- a/configs/pcm051_rev1_defconfig +++ b/configs/pcm051_rev1_defconfig @@ -30,3 +30,4 @@ CONFIG_USB_MUSB_HOST=y CONFIG_USB_MUSB_GADGET=y CONFIG_USB_GADGET=y CONFIG_OF_LIBFDT=y +CONFIG_SPL_NET_VCI_STRING="pcm051 U-Boot SPL" diff --git a/configs/pcm051_rev3_defconfig b/configs/pcm051_rev3_defconfig index eff099c..b277b3a 100644 --- a/configs/pcm051_rev3_defconfig +++ b/configs/pcm051_rev3_defconfig @@ -30,3 +30,4 @@ CONFIG_USB_MUSB_HOST=y CONFIG_USB_MUSB_GADGET=y CONFIG_USB_GADGET=y CONFIG_OF_LIBFDT=y +CONFIG_SPL_NET_VCI_STRING="pcm051 U-Boot SPL" diff --git a/net/Kconfig b/net/Kconfig index 414c549..c393269 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -45,4 +45,7 @@ config BOOTP_VCI_STRING default "U-Boot.arm" if ARM default "U-Boot"
+config SPL_NET_VCI_STRING + string + endif # if NET

On Fri, May 06, 2016 at 09:01:05PM +0200, Alexander Graf wrote:
This patch also adds the SPL time VCI string into Kconfig.
Signed-off-by: Alexander Graf agraf@suse.de
Reviewed-by: Tom Rini trini@konsulko.com

On Fri, May 06, 2016 at 09:01:05PM +0200, Alexander Graf wrote:
This patch also adds the SPL time VCI string into Kconfig.
Signed-off-by: Alexander Graf agraf@suse.de Reviewed-by: Tom Rini trini@konsulko.com
Applied to u-boot/master, thanks!

The client architecture that we pass to a dhcp server depends on the target payload that we want to execute. An EFI binary has a different client arch than a legacy binary or a u-boot binary.
So let's parameterize the pxe client arch field to allow an override via the distro script, so that our efi boot path can tell the dhcp server that it's actually an efi firmware.
Signed-off-by: Alexander Graf agraf@suse.de --- net/bootp.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-)
diff --git a/net/bootp.c b/net/bootp.c index d718e35..1f2d0aa 100644 --- a/net/bootp.c +++ b/net/bootp.c @@ -431,10 +431,8 @@ static int dhcp_extended(u8 *e, int message_type, struct in_addr server_ip, { u8 *start = e; u8 *cnt; -#if defined(CONFIG_BOOTP_PXE) char *uuid; - u16 clientarch; -#endif + int clientarch = -1;
#if defined(CONFIG_BOOTP_VENDOREX) u8 *x; @@ -490,12 +488,19 @@ static int dhcp_extended(u8 *e, int message_type, struct in_addr server_ip, } #endif
-#if defined(CONFIG_BOOTP_PXE) +#ifdef CONFIG_BOOTP_PXE_CLIENTARCH clientarch = CONFIG_BOOTP_PXE_CLIENTARCH; - *e++ = 93; /* Client System Architecture */ - *e++ = 2; - *e++ = (clientarch >> 8) & 0xff; - *e++ = clientarch & 0xff; +#endif + + if (getenv("bootp_arch")) + clientarch = getenv_ulong("bootp_arch", 16, clientarch); + + if (clientarch > 0) { + *e++ = 93; /* Client System Architecture */ + *e++ = 2; + *e++ = (clientarch >> 8) & 0xff; + *e++ = clientarch & 0xff; + }
*e++ = 94; /* Client Network Interface Identifier */ *e++ = 3; @@ -517,7 +522,6 @@ static int dhcp_extended(u8 *e, int message_type, struct in_addr server_ip, printf("Invalid pxeuuid: %s\n", uuid); } } -#endif
e = add_vci(e);

On Fri, May 06, 2016 at 09:01:06PM +0200, Alexander Graf wrote:
The client architecture that we pass to a dhcp server depends on the target payload that we want to execute. An EFI binary has a different client arch than a legacy binary or a u-boot binary.
So let's parameterize the pxe client arch field to allow an override via the distro script, so that our efi boot path can tell the dhcp server that it's actually an efi firmware.
Signed-off-by: Alexander Graf agraf@suse.de
Reviewed-by: Tom Rini trini@konsulko.com

The client architecture that we pass to a dhcp server depends on the target payload that we want to execute. An EFI binary has a different client arch than a legacy binary or a u-boot binary.
So let's parameterize the pxe client arch field to allow an override via the distro script, so that our efi boot path can tell the dhcp server that it's actually an efi firmware.
Signed-off-by: Alexander Graf agraf@suse.de
---
v3 -> v4:
- Fix compile build for !CONFIG_LIB_UUID --- net/bootp.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-)
diff --git a/net/bootp.c b/net/bootp.c index d718e35..c1c32a1 100644 --- a/net/bootp.c +++ b/net/bootp.c @@ -431,10 +431,10 @@ static int dhcp_extended(u8 *e, int message_type, struct in_addr server_ip, { u8 *start = e; u8 *cnt; -#if defined(CONFIG_BOOTP_PXE) +#ifdef CONFIG_LIB_UUID char *uuid; - u16 clientarch; #endif + int clientarch = -1;
#if defined(CONFIG_BOOTP_VENDOREX) u8 *x; @@ -490,12 +490,19 @@ static int dhcp_extended(u8 *e, int message_type, struct in_addr server_ip, } #endif
-#if defined(CONFIG_BOOTP_PXE) +#ifdef CONFIG_BOOTP_PXE_CLIENTARCH clientarch = CONFIG_BOOTP_PXE_CLIENTARCH; - *e++ = 93; /* Client System Architecture */ - *e++ = 2; - *e++ = (clientarch >> 8) & 0xff; - *e++ = clientarch & 0xff; +#endif + + if (getenv("bootp_arch")) + clientarch = getenv_ulong("bootp_arch", 16, clientarch); + + if (clientarch > 0) { + *e++ = 93; /* Client System Architecture */ + *e++ = 2; + *e++ = (clientarch >> 8) & 0xff; + *e++ = clientarch & 0xff; + }
*e++ = 94; /* Client Network Interface Identifier */ *e++ = 3; @@ -503,6 +510,7 @@ static int dhcp_extended(u8 *e, int message_type, struct in_addr server_ip, *e++ = 0; /* major revision */ *e++ = 0; /* minor revision */
+#ifdef CONFIG_LIB_UUID uuid = getenv("pxeuuid");
if (uuid) {

On Thu, May 12, 2016 at 03:51:45PM +0200, Alexander Graf wrote:
The client architecture that we pass to a dhcp server depends on the target payload that we want to execute. An EFI binary has a different client arch than a legacy binary or a u-boot binary.
So let's parameterize the pxe client arch field to allow an override via the distro script, so that our efi boot path can tell the dhcp server that it's actually an efi firmware.
Signed-off-by: Alexander Graf agraf@suse.de
Applied to u-boot/master, 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
---
v1 -> v2:
- Error out for unknown arch - Also set client arch variable --- include/config_distro_bootcmd.h | 47 ++++++++++++++++++++++++++++++++++++++++- net/bootp.c | 13 ++++++++++-- 2 files changed, 57 insertions(+), 3 deletions(-)
diff --git a/include/config_distro_bootcmd.h b/include/config_distro_bootcmd.h index 7f67344..e06c9eb 100644 --- a/include/config_distro_bootcmd.h +++ b/include/config_distro_bootcmd.h @@ -230,13 +230,58 @@ #endif
#if defined(CONFIG_CMD_DHCP) +#if defined(CONFIG_EFI_LOADER) +#if defined(CONFIG_ARM64) +#define BOOTENV_EFI_PXE_ARCH "0xb" +#define BOOTENV_EFI_PXE_VCI "PXEClient:Arch:00011:UNDI:003000" +#elif defined(CONFIG_ARM) +#define BOOTENV_EFI_PXE_ARCH "0xa" +#define BOOTENV_EFI_PXE_VCI "PXEClient:Arch:00010:UNDI:003000" +#elif defined(CONFIG_X86) +/* Always assume we're running 64bit */ +#define BOOTENV_EFI_PXE_ARCH "0x7" +#define BOOTENV_EFI_PXE_VCI "PXEClient:Arch:00007:UNDI:003000" +#else +#error Please specify an EFI client identifier +#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 efi_old_arch ${bootp_arch};" \ + "setenv bootp_vci " BOOTENV_EFI_PXE_VCI ";" \ + "setenv bootp_arch " BOOTENV_EFI_PXE_ARCH ";" \ + "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 bootp_arch ${efi_old_arch};" \ + "setenv efi_fdtfile;" \ + "setenv efi_old_arch;" \ + "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 1f2d0aa..e36793c 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; }

On Fri, May 06, 2016 at 09:01:07PM +0200, Alexander Graf wrote:
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
Reviewed-by: Tom Rini trini@konsulko.com

On Fri, May 06, 2016 at 09:01:07PM +0200, Alexander Graf wrote:
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 Reviewed-by: Tom Rini trini@konsulko.com
Applied to u-boot/master, thanks!
participants (3)
-
Alexander Graf
-
Simon Glass
-
Tom Rini