[PATCH 4/9] efi_loader/net: Remove simple network and pxe protocols from efi_net.c

Heinrich Schuchardt xypron.glpk at gmx.de
Mon May 26 11:03:05 CEST 2025


On 16.05.25 10:18, Adriano Cordova wrote:
> efi_net.c should only [un]install protocols, leaving the
> actual implementation to efi_simple_network.c and efi_pxe.c.
> 
> Signed-off-by: Adriano Cordova <adriano.cordova at canonical.com>
> ---
>   include/efi_api.h                       |   28 +-
>   include/efi_loader.h                    |   10 +-
>   lib/efi_loader/net/Makefile             |    2 +
>   lib/efi_loader/net/efi_net.c            | 1038 +----------------------
>   lib/efi_loader/net/efi_pxe.c            |  190 +++++
>   lib/efi_loader/net/efi_simple_network.c |  880 +++++++++++++++++++
>   lib/efi_selftest/efi_selftest_snp.c     |    4 +-
>   7 files changed, 1142 insertions(+), 1010 deletions(-)
>   create mode 100644 lib/efi_loader/net/efi_pxe.c
>   create mode 100644 lib/efi_loader/net/efi_simple_network.c
> 
> diff --git a/include/efi_api.h b/include/efi_api.h
> index eb61eafa028..b20d639fbea 100644
> --- a/include/efi_api.h
> +++ b/include/efi_api.h
> @@ -1519,36 +1519,36 @@ struct efi_simple_network_mode {
>   /* revision of the simple network protocol */
>   #define EFI_SIMPLE_NETWORK_PROTOCOL_REVISION	0x00010000
>   
> -struct efi_simple_network {
> +struct efi_simple_network_protocol {
>   	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,
> +	efi_status_t (EFIAPI *start)(struct efi_simple_network_protocol *this);
> +	efi_status_t (EFIAPI *stop)(struct efi_simple_network_protocol *this);
> +	efi_status_t (EFIAPI *initialize)(struct efi_simple_network_protocol *this,
>   			ulong extra_rx, ulong extra_tx);
> -	efi_status_t (EFIAPI *reset)(struct efi_simple_network *this,
> +	efi_status_t (EFIAPI *reset)(struct efi_simple_network_protocol *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,
> +	efi_status_t (EFIAPI *shutdown)(struct efi_simple_network_protocol *this);
> +	efi_status_t (EFIAPI *receive_filters)(struct efi_simple_network_protocol *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,
> +	efi_status_t (EFIAPI *station_address)(struct efi_simple_network_protocol *this,
>   			int reset, struct efi_mac_address *new_mac);
> -	efi_status_t (EFIAPI *statistics)(struct efi_simple_network *this,
> +	efi_status_t (EFIAPI *statistics)(struct efi_simple_network_protocol *this,
>   			int reset, ulong *stat_size, void *stat_table);
> -	efi_status_t (EFIAPI *mcastiptomac)(struct efi_simple_network *this,
> +	efi_status_t (EFIAPI *mcastiptomac)(struct efi_simple_network_protocol *this,
>   			int ipv6, struct efi_ip_address *ip,
>   			struct efi_mac_address *mac);
> -	efi_status_t (EFIAPI *nvdata)(struct efi_simple_network *this,
> +	efi_status_t (EFIAPI *nvdata)(struct efi_simple_network_protocol *this,
>   			int read_write, ulong offset, ulong buffer_size,
>   			char *buffer);
> -	efi_status_t (EFIAPI *get_status)(struct efi_simple_network *this,
> +	efi_status_t (EFIAPI *get_status)(struct efi_simple_network_protocol *this,
>   			u32 *int_status, void **txbuf);
> -	efi_status_t (EFIAPI *transmit)(struct efi_simple_network *this,
> +	efi_status_t (EFIAPI *transmit)(struct efi_simple_network_protocol *this,
>   			size_t header_size, size_t 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,
> +	efi_status_t (EFIAPI *receive)(struct efi_simple_network_protocol *this,
>   			size_t *header_size, size_t *buffer_size, void *buffer,
>   			struct efi_mac_address *src_addr,
>   			struct efi_mac_address *dest_addr, u16 *protocol);
> diff --git a/include/efi_loader.h b/include/efi_loader.h
> index 5c1244824f3..cc90bdf6a3a 100644
> --- a/include/efi_loader.h
> +++ b/include/efi_loader.h
> @@ -329,8 +329,6 @@ extern const efi_guid_t efi_guid_host_dev;
>   #endif
>   /* GUID of the EFI_BLOCK_IO_PROTOCOL */
>   extern const efi_guid_t efi_block_io_guid;
> -/* GUID of the EFI_SIMPLE_NETWORK_PROTOCOL */
> -extern const efi_guid_t efi_net_guid;
>   extern const efi_guid_t efi_global_variable_guid;
>   extern const efi_guid_t efi_guid_console_control;
>   extern const efi_guid_t efi_guid_device_path;
> @@ -661,9 +659,13 @@ efi_status_t efi_gop_register(void);
>   /* Called by bootefi to make the network interface available */
>   efi_status_t efi_net_register(struct udevice *dev);
>   efi_status_t efi_net_do_start(struct udevice *dev);
> -/* Called by efi_net_register to make the ip4 config2 protocol available */
> +/* Called by efi_net_register to install EFI_SIMPLE_NETWORK_PROTOCOL */
> +efi_status_t efi_simple_network_install(const efi_handle_t handle, struct udevice *dev);
> +/* Called by efi_net_register to install EFI_PXE_BASE_CODE_PROTOCOL */
> +efi_status_t efi_pxe_install(const efi_handle_t handle, struct efi_pxe_packet *dhcp_ack);
> +/* Called by efi_net_register to install EFI_IP4_CONFIG2_PROTOCOL */
>   efi_status_t efi_ip4_config2_install(const efi_handle_t handle);
> -/* Called by efi_net_register to make the http protocol available */
> +/* Called by efi_net_register to install EFI_HTTP_SERVICE_BINDING_PROTOCOL */
>   efi_status_t efi_http_install(const efi_handle_t handle);
>   /* Called by bootefi to make the watchdog available */
>   efi_status_t efi_watchdog_register(void);
> diff --git a/lib/efi_loader/net/Makefile b/lib/efi_loader/net/Makefile
> index 75131877e30..d024f1818d9 100644
> --- a/lib/efi_loader/net/Makefile
> +++ b/lib/efi_loader/net/Makefile
> @@ -1,3 +1,5 @@
>   obj-y += efi_net.o
> +obj-y += efi_simple_network.o
> +obj-y += efi_pxe.o
>   obj-$(CONFIG_EFI_IP4_CONFIG2_PROTOCOL) += efi_ip4_config2.o
>   obj-$(CONFIG_EFI_HTTP_PROTOCOL) += efi_http.o
> \ No newline at end of file
> diff --git a/lib/efi_loader/net/efi_net.c b/lib/efi_loader/net/efi_net.c
> index 40db80f4726..a7ceef4cbba 100644
> --- a/lib/efi_loader/net/efi_net.c
> +++ b/lib/efi_loader/net/efi_net.c
> @@ -1,18 +1,8 @@
>   // SPDX-License-Identifier: GPL-2.0+
>   /*
> - * Simple network protocol
> - * PXE base code protocol
>    *
>    * Copyright (c) 2016 Alexander Graf
>    *
> - * The simple network protocol has the following statuses and services
> - * to move between them:
> - *
> - * Start():	 EfiSimpleNetworkStopped     -> EfiSimpleNetworkStarted
> - * Initialize(): EfiSimpleNetworkStarted     -> EfiSimpleNetworkInitialized
> - * Shutdown():	 EfiSimpleNetworkInitialized -> EfiSimpleNetworkStarted
> - * Stop():	 EfiSimpleNetworkStarted     -> EfiSimpleNetworkStopped
> - * Reset():	 EfiSimpleNetworkInitialized -> EfiSimpleNetworkInitialized
>    */
>   
>   #define LOG_CATEGORY LOGC_EFI
> @@ -28,11 +18,12 @@
>   #define MAX_NUM_DHCP_ENTRIES 10
>   #define MAX_NUM_DP_ENTRIES 10
>   
> -const efi_guid_t efi_net_guid = EFI_SIMPLE_NETWORK_PROTOCOL_GUID;
>   static const efi_guid_t efi_pxe_base_code_protocol_guid =
>   					EFI_PXE_BASE_CODE_PROTOCOL_GUID;
> +#if IS_ENABLED(CONFIG_EFI_HTTP_PROTOCOL)
>   static const efi_guid_t efi_http_service_binding_guid =
>   					EFI_HTTP_SERVICE_BINDING_PROTOCOL_GUID;
> +#endif
>   
>   struct dp_entry {
>   	struct efi_device_path *net_dp;
> @@ -71,39 +62,12 @@ static int next_dhcp_entry;
>    *
>    * @header:			EFI object header
>    * @dev:			net udevice
> - * @net:			simple network protocol interface
> - * @net_mode:			status of the network interface
> - * @pxe:			PXE base code protocol interface
> - * @pxe_mode:			status of the PXE base code protocol
> - * @new_tx_packet:		new transmit packet
> - * @transmit_buffer:	transmit buffer
> - * @receive_buffer:		array of receive buffers
> - * @receive_lengths:	array of lengths for received packets
> - * @rx_packet_idx:		index of the current receive packet
> - * @rx_packet_num:		number of received packets
> - * @wait_for_packet:	signaled when a packet has been received
> - * @network_timer_event:	event to check for new network packets.
> - * @efi_seq_num:		sequence number of the EFI net object.
>    */
>   struct efi_net_obj {
>   	struct efi_object header;
>   	struct udevice *dev;
> -	struct efi_simple_network net;
> -	struct efi_simple_network_mode net_mode;
> -	struct efi_pxe_base_code_protocol pxe;
> -	struct efi_pxe_mode pxe_mode;
> -	void *new_tx_packet;
> -	void *transmit_buffer;
> -	uchar **receive_buffer;
> -	size_t *receive_lengths;
> -	int rx_packet_idx;
> -	int rx_packet_num;
> -	struct efi_event *wait_for_packet;
> -	struct efi_event *network_timer_event;
> -	int efi_seq_num;
>   };
>   
> -static int curr_efi_net_obj;
>   static struct efi_net_obj *net_objs[MAX_EFI_NET_OBJS];
>   
>   /**
> @@ -120,641 +84,6 @@ static bool efi_netobj_is_active(struct efi_net_obj *netobj)
>   	return true;
>   }
>   
> -/*
> - * efi_netobj_from_snp() - get efi_net_obj from simple network protocol
> - *
> - *
> - * @snp:	pointer to the simple network protocol
> - * Return:	pointer to efi_net_obj, NULL on error
> - */
> -static struct efi_net_obj *efi_netobj_from_snp(struct efi_simple_network *snp)
> -{
> -	int i;
> -
> -	for (i = 0; i < MAX_EFI_NET_OBJS; i++) {
> -		if (net_objs[i] && &net_objs[i]->net == snp) {
> -			// Do not register duplicate devices
> -			return net_objs[i];
> -		}
> -	}
> -	return NULL;
> -}
> -
> -/*
> - * efi_net_start() - start the network interface
> - *
> - * This function implements the Start service of the
> - * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
> - * (UEFI) specification for details.
> - *
> - * @this:	pointer to the protocol instance
> - * Return:	status code
> - */
> -static efi_status_t EFIAPI efi_net_start(struct efi_simple_network *this)
> -{
> -	efi_status_t ret = EFI_SUCCESS;
> -	struct efi_net_obj *nt;
> -
> -	EFI_ENTRY("%p", this);
> -	/* Check parameters */
> -	if (!this) {
> -		ret = EFI_INVALID_PARAMETER;
> -		goto out;
> -	}
> -
> -	nt = efi_netobj_from_snp(this);
> -
> -	if (this->mode->state != EFI_NETWORK_STOPPED) {
> -		ret = EFI_ALREADY_STARTED;
> -	} else {
> -		this->int_status = 0;
> -		nt->wait_for_packet->is_signaled = false;
> -		this->mode->state = EFI_NETWORK_STARTED;
> -	}
> -out:
> -	return EFI_EXIT(ret);
> -}
> -
> -/*
> - * efi_net_stop() - stop the network interface
> - *
> - * This function implements the Stop service of the
> - * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
> - * (UEFI) specification for details.
> - *
> - * @this:	pointer to the protocol instance
> - * Return:	status code
> - */
> -static efi_status_t EFIAPI efi_net_stop(struct efi_simple_network *this)
> -{
> -	efi_status_t ret = EFI_SUCCESS;
> -	struct efi_net_obj *nt;
> -
> -	EFI_ENTRY("%p", this);
> -
> -	/* Check parameters */
> -	if (!this) {
> -		ret = EFI_INVALID_PARAMETER;
> -		goto out;
> -	}
> -
> -	nt = efi_netobj_from_snp(this);
> -
> -	if (this->mode->state == EFI_NETWORK_STOPPED) {
> -		ret = EFI_NOT_STARTED;
> -	} else {
> -		/* Disable hardware and put it into the reset state */
> -		eth_set_dev(nt->dev);
> -		env_set("ethact", eth_get_name());
> -		eth_halt();
> -		/* Clear cache of packets */
> -		nt->rx_packet_num = 0;
> -		this->mode->state = EFI_NETWORK_STOPPED;
> -	}
> -out:
> -	return EFI_EXIT(ret);
> -}
> -
> -/*
> - * efi_net_initialize() - initialize the network interface
> - *
> - * This function implements the Initialize service of the
> - * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
> - * (UEFI) specification for details.
> - *
> - * @this:	pointer to the protocol instance
> - * @extra_rx:	extra receive buffer to be allocated
> - * @extra_tx:	extra transmit buffer to be allocated
> - * Return:	status code
> - */
> -static efi_status_t EFIAPI efi_net_initialize(struct efi_simple_network *this,
> -					      ulong extra_rx, ulong extra_tx)
> -{
> -	int ret;
> -	efi_status_t r = EFI_SUCCESS;
> -	struct efi_net_obj *nt;
> -
> -	EFI_ENTRY("%p, %lx, %lx", this, extra_rx, extra_tx);
> -
> -	/* Check parameters */
> -	if (!this) {
> -		r = EFI_INVALID_PARAMETER;
> -		goto out;
> -	}
> -	nt = efi_netobj_from_snp(this);
> -
> -	switch (this->mode->state) {
> -	case EFI_NETWORK_INITIALIZED:
> -	case EFI_NETWORK_STARTED:
> -		break;
> -	default:
> -		r = EFI_NOT_STARTED;
> -		goto out;
> -	}
> -
> -	/* Setup packet buffers */
> -	net_init();
> -	/* Clear cache of packets */
> -	nt->rx_packet_num = 0;
> -	/* Set the net device corresponding to the efi net object */
> -	eth_set_dev(nt->dev);
> -	env_set("ethact", eth_get_name());
> -	/* Get hardware ready for send and receive operations */
> -	ret = eth_start_udev(nt->dev);
> -	if (ret < 0) {
> -		eth_halt();
> -		this->mode->state = EFI_NETWORK_STOPPED;
> -		r = EFI_DEVICE_ERROR;
> -		goto out;
> -	} else {
> -		this->int_status = 0;
> -		nt->wait_for_packet->is_signaled = false;
> -		this->mode->state = EFI_NETWORK_INITIALIZED;
> -	}
> -out:
> -	return EFI_EXIT(r);
> -}
> -
> -/*
> - * efi_net_reset() - reinitialize the network interface
> - *
> - * This function implements the Reset service of the
> - * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
> - * (UEFI) specification for details.
> - *
> - * @this:			pointer to the protocol instance
> - * @extended_verification:	execute exhaustive verification
> - * Return:			status code
> - */
> -static efi_status_t EFIAPI efi_net_reset(struct efi_simple_network *this,
> -					 int extended_verification)
> -{
> -	efi_status_t ret;
> -
> -	EFI_ENTRY("%p, %x", this, extended_verification);
> -
> -	/* Check parameters */
> -	if (!this) {
> -		ret = EFI_INVALID_PARAMETER;
> -		goto out;
> -	}
> -
> -	switch (this->mode->state) {
> -	case EFI_NETWORK_INITIALIZED:
> -		break;
> -	case EFI_NETWORK_STOPPED:
> -		ret = EFI_NOT_STARTED;
> -		goto out;
> -	default:
> -		ret = EFI_DEVICE_ERROR;
> -		goto out;
> -	}
> -
> -	this->mode->state = EFI_NETWORK_STARTED;
> -	ret = EFI_CALL(efi_net_initialize(this, 0, 0));
> -out:
> -	return EFI_EXIT(ret);
> -}
> -
> -/*
> - * efi_net_shutdown() - shut down the network interface
> - *
> - * This function implements the Shutdown service of the
> - * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
> - * (UEFI) specification for details.
> - *
> - * @this:	pointer to the protocol instance
> - * Return:	status code
> - */
> -static efi_status_t EFIAPI efi_net_shutdown(struct efi_simple_network *this)
> -{
> -	efi_status_t ret = EFI_SUCCESS;
> -	struct efi_net_obj *nt;
> -
> -	EFI_ENTRY("%p", this);
> -
> -	/* Check parameters */
> -	if (!this) {
> -		ret = EFI_INVALID_PARAMETER;
> -		goto out;
> -	}
> -	nt = efi_netobj_from_snp(this);
> -
> -	switch (this->mode->state) {
> -	case EFI_NETWORK_INITIALIZED:
> -		break;
> -	case EFI_NETWORK_STOPPED:
> -		ret = EFI_NOT_STARTED;
> -		goto out;
> -	default:
> -		ret = EFI_DEVICE_ERROR;
> -		goto out;
> -	}
> -
> -	eth_set_dev(nt->dev);
> -	env_set("ethact", eth_get_name());
> -	eth_halt();
> -
> -	this->int_status = 0;
> -	nt->wait_for_packet->is_signaled = false;
> -	this->mode->state = EFI_NETWORK_STARTED;
> -
> -out:
> -	return EFI_EXIT(ret);
> -}
> -
> -/*
> - * efi_net_receive_filters() - mange multicast receive filters
> - *
> - * This function implements the ReceiveFilters service of the
> - * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
> - * (UEFI) specification for details.
> - *
> - * @this:		pointer to the protocol instance
> - * @enable:		bit mask of receive filters to enable
> - * @disable:		bit mask of receive filters to disable
> - * @reset_mcast_filter:	true resets contents of the filters
> - * @mcast_filter_count:	number of hardware MAC addresses in the new filters list
> - * @mcast_filter:	list of new filters
> - * Return:		status code
> - */
> -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);
> -
> -	return EFI_EXIT(EFI_UNSUPPORTED);
> -}
> -
> -/*
> - * efi_net_station_address() - set the hardware MAC address
> - *
> - * This function implements the StationAddress service of the
> - * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
> - * (UEFI) specification for details.
> - *
> - * @this:	pointer to the protocol instance
> - * @reset:	if true reset the address to default
> - * @new_mac:	new MAC address
> - * Return:	status code
> - */
> -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_UNSUPPORTED);
> -}
> -
> -/*
> - * efi_net_statistics() - reset or collect statistics of the network interface
> - *
> - * This function implements the Statistics service of the
> - * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
> - * (UEFI) specification for details.
> - *
> - * @this:	pointer to the protocol instance
> - * @reset:	if true, the statistics are reset
> - * @stat_size:	size of the statistics table
> - * @stat_table:	table to receive the statistics
> - * Return:	status code
> - */
> -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_UNSUPPORTED);
> -}
> -
> -/*
> - * efi_net_mcastiptomac() - translate multicast IP address to MAC address
> - *
> - * This function implements the MCastIPtoMAC service of the
> - * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
> - * (UEFI) specification for details.
> - *
> - * @this:	pointer to the protocol instance
> - * @ipv6:	true if the IP address is an IPv6 address
> - * @ip:		IP address
> - * @mac:	MAC address
> - * Return:	status code
> - */
> -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_status_t ret = EFI_SUCCESS;
> -
> -	EFI_ENTRY("%p, %x, %p, %p", this, ipv6, ip, mac);
> -
> -	if (!this || !ip || !mac) {
> -		ret = EFI_INVALID_PARAMETER;
> -		goto out;
> -	}
> -
> -	if (ipv6) {
> -		ret = EFI_UNSUPPORTED;
> -		goto out;
> -	}
> -
> -	/* Multi-cast addresses are in the range 224.0.0.0 - 239.255.255.255 */
> -	if ((ip->ip_addr[0] & 0xf0) != 0xe0) {
> -		ret = EFI_INVALID_PARAMETER;
> -		goto out;
> -	};
> -
> -	switch (this->mode->state) {
> -	case EFI_NETWORK_INITIALIZED:
> -	case EFI_NETWORK_STARTED:
> -		break;
> -	default:
> -		ret = EFI_NOT_STARTED;
> -		goto out;
> -	}
> -
> -	memset(mac, 0, sizeof(struct efi_mac_address));
> -
> -	/*
> -	 * Copy lower 23 bits of IPv4 multi-cast address
> -	 * RFC 1112, RFC 7042 2.1.1.
> -	 */
> -	mac->mac_addr[0] = 0x01;
> -	mac->mac_addr[1] = 0x00;
> -	mac->mac_addr[2] = 0x5E;
> -	mac->mac_addr[3] = ip->ip_addr[1] & 0x7F;
> -	mac->mac_addr[4] = ip->ip_addr[2];
> -	mac->mac_addr[5] = ip->ip_addr[3];
> -out:
> -	return EFI_EXIT(ret);
> -}
> -
> -/**
> - * efi_net_nvdata() - read or write NVRAM
> - *
> - * This function implements the GetStatus service of the Simple Network
> - * Protocol. See the UEFI spec for details.
> - *
> - * @this:		the instance of the Simple Network Protocol
> - * @read_write:		true for read, false for write
> - * @offset:		offset in NVRAM
> - * @buffer_size:	size of buffer
> - * @buffer:		buffer
> - * Return:		status code
> - */
> -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_UNSUPPORTED);
> -}
> -
> -/**
> - * efi_net_get_status() - get interrupt status
> - *
> - * This function implements the GetStatus service of the Simple Network
> - * Protocol. See the UEFI spec for details.
> - *
> - * @this:		the instance of the Simple Network Protocol
> - * @int_status:		interface status
> - * @txbuf:		transmission buffer
> - */
> -static efi_status_t EFIAPI efi_net_get_status(struct efi_simple_network *this,
> -					      u32 *int_status, void **txbuf)
> -{
> -	efi_status_t ret = EFI_SUCCESS;
> -	struct efi_net_obj *nt;
> -
> -	EFI_ENTRY("%p, %p, %p", this, int_status, txbuf);
> -
> -	efi_timer_check();
> -
> -	/* Check parameters */
> -	if (!this) {
> -		ret = EFI_INVALID_PARAMETER;
> -		goto out;
> -	}
> -
> -	nt = efi_netobj_from_snp(this);
> -
> -	switch (this->mode->state) {
> -	case EFI_NETWORK_STOPPED:
> -		ret = EFI_NOT_STARTED;
> -		goto out;
> -	case EFI_NETWORK_STARTED:
> -		ret = EFI_DEVICE_ERROR;
> -		goto out;
> -	default:
> -		break;
> -	}
> -
> -	if (int_status) {
> -		*int_status = this->int_status;
> -		this->int_status = 0;
> -	}
> -	if (txbuf)
> -		*txbuf = nt->new_tx_packet;
> -
> -	nt->new_tx_packet = NULL;
> -out:
> -	return EFI_EXIT(ret);
> -}
> -
> -/**
> - * efi_net_transmit() - transmit a packet
> - *
> - * This function implements the Transmit service of the Simple Network Protocol.
> - * See the UEFI spec for details.
> - *
> - * @this:		the instance of the Simple Network Protocol
> - * @header_size:	size of the media header
> - * @buffer_size:	size of the buffer to receive the packet
> - * @buffer:		buffer to receive the packet
> - * @src_addr:		source hardware MAC address
> - * @dest_addr:		destination hardware MAC address
> - * @protocol:		type of header to build
> - * Return:		status code
> - */
> -static efi_status_t EFIAPI efi_net_transmit
> -		(struct efi_simple_network *this, size_t header_size,
> -		 size_t buffer_size, void *buffer,
> -		 struct efi_mac_address *src_addr,
> -		 struct efi_mac_address *dest_addr, u16 *protocol)
> -{
> -	efi_status_t ret = EFI_SUCCESS;
> -	struct efi_net_obj *nt;
> -
> -	EFI_ENTRY("%p, %lu, %lu, %p, %p, %p, %p", this,
> -		  (unsigned long)header_size, (unsigned long)buffer_size,
> -		  buffer, src_addr, dest_addr, protocol);
> -
> -	efi_timer_check();
> -
> -	/* Check parameters */
> -	if (!this || !buffer) {
> -		ret = EFI_INVALID_PARAMETER;
> -		goto out;
> -	}
> -
> -	nt = efi_netobj_from_snp(this);
> -
> -	/* We do not support jumbo packets */
> -	if (buffer_size > PKTSIZE_ALIGN) {
> -		ret = EFI_INVALID_PARAMETER;
> -		goto out;
> -	}
> -
> -	/* At least the IP header has to fit into the buffer */
> -	if (buffer_size < this->mode->media_header_size) {
> -		ret = EFI_BUFFER_TOO_SMALL;
> -		goto out;
> -	}
> -
> -	/*
> -	 * TODO:
> -	 * Support VLANs. Use net_set_ether() for copying the header. Use a
> -	 * U_BOOT_ENV_CALLBACK to update the media header size.
> -	 */
> -	if (header_size) {
> -		struct ethernet_hdr *header = buffer;
> -
> -		if (!dest_addr || !protocol ||
> -		    header_size != this->mode->media_header_size) {
> -			ret = EFI_INVALID_PARAMETER;
> -			goto out;
> -		}
> -		if (!src_addr)
> -			src_addr = &this->mode->current_address;
> -
> -		memcpy(header->et_dest, dest_addr, ARP_HLEN);
> -		memcpy(header->et_src, src_addr, ARP_HLEN);
> -		header->et_protlen = htons(*protocol);
> -	}
> -
> -	switch (this->mode->state) {
> -	case EFI_NETWORK_STOPPED:
> -		ret = EFI_NOT_STARTED;
> -		goto out;
> -	case EFI_NETWORK_STARTED:
> -		ret = EFI_DEVICE_ERROR;
> -		goto out;
> -	default:
> -		break;
> -	}
> -
> -	eth_set_dev(nt->dev);
> -	env_set("ethact", eth_get_name());
> -
> -	/* Ethernet packets always fit, just bounce */
> -	memcpy(nt->transmit_buffer, buffer, buffer_size);
> -	net_send_packet(nt->transmit_buffer, buffer_size);
> -
> -	nt->new_tx_packet = buffer;
> -	this->int_status |= EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT;
> -out:
> -	return EFI_EXIT(ret);
> -}
> -
> -/**
> - * efi_net_receive() - receive a packet from a network interface
> - *
> - * This function implements the Receive service of the Simple Network Protocol.
> - * See the UEFI spec for details.
> - *
> - * @this:		the instance of the Simple Network Protocol
> - * @header_size:	size of the media header
> - * @buffer_size:	size of the buffer to receive the packet
> - * @buffer:		buffer to receive the packet
> - * @src_addr:		source MAC address
> - * @dest_addr:		destination MAC address
> - * @protocol:		protocol
> - * Return:		status code
> - */
> -static efi_status_t EFIAPI efi_net_receive
> -		(struct efi_simple_network *this, size_t *header_size,
> -		 size_t *buffer_size, void *buffer,
> -		 struct efi_mac_address *src_addr,
> -		 struct efi_mac_address *dest_addr, u16 *protocol)
> -{
> -	efi_status_t ret = EFI_SUCCESS;
> -	struct ethernet_hdr *eth_hdr;
> -	size_t hdr_size = sizeof(struct ethernet_hdr);
> -	u16 protlen;
> -	struct efi_net_obj *nt;
> -
> -	EFI_ENTRY("%p, %p, %p, %p, %p, %p, %p", this, header_size,
> -		  buffer_size, buffer, src_addr, dest_addr, protocol);
> -
> -	/* Execute events */
> -	efi_timer_check();
> -
> -	/* Check parameters */
> -	if (!this || !buffer || !buffer_size) {
> -		ret = EFI_INVALID_PARAMETER;
> -		goto out;
> -	}
> -
> -	nt = efi_netobj_from_snp(this);
> -
> -	switch (this->mode->state) {
> -	case EFI_NETWORK_STOPPED:
> -		ret = EFI_NOT_STARTED;
> -		goto out;
> -	case EFI_NETWORK_STARTED:
> -		ret = EFI_DEVICE_ERROR;
> -		goto out;
> -	default:
> -		break;
> -	}
> -
> -	if (!nt->rx_packet_num) {
> -		ret = EFI_NOT_READY;
> -		goto out;
> -	}
> -	/* Fill export parameters */
> -	eth_hdr = (struct ethernet_hdr *)nt->receive_buffer[nt->rx_packet_idx];
> -	protlen = ntohs(eth_hdr->et_protlen);
> -	if (protlen == 0x8100) {
> -		hdr_size += 4;
> -		protlen = ntohs(*(u16 *)&nt->receive_buffer[nt->rx_packet_idx][hdr_size - 2]);
> -	}
> -	if (header_size)
> -		*header_size = hdr_size;
> -	if (dest_addr)
> -		memcpy(dest_addr, eth_hdr->et_dest, ARP_HLEN);
> -	if (src_addr)
> -		memcpy(src_addr, eth_hdr->et_src, ARP_HLEN);
> -	if (protocol)
> -		*protocol = protlen;
> -	if (*buffer_size < nt->receive_lengths[nt->rx_packet_idx]) {
> -		/* Packet doesn't fit, try again with bigger buffer */
> -		*buffer_size = nt->receive_lengths[nt->rx_packet_idx];
> -		ret = EFI_BUFFER_TOO_SMALL;
> -		goto out;
> -	}
> -	/* Copy packet */
> -	memcpy(buffer, nt->receive_buffer[nt->rx_packet_idx],
> -	       nt->receive_lengths[nt->rx_packet_idx]);
> -	*buffer_size = nt->receive_lengths[nt->rx_packet_idx];
> -	nt->rx_packet_idx = (nt->rx_packet_idx + 1) % ETH_PACKETS_BATCH_RECV;
> -	nt->rx_packet_num--;
> -	if (nt->rx_packet_num)
> -		nt->wait_for_packet->is_signaled = true;
> -	else
> -		this->int_status &= ~EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT;
> -out:
> -	return EFI_EXIT(ret);
> -}
> -
>   /**
>    * efi_net_set_dhcp_ack() - take note of a selected DHCP IP address
>    *
> @@ -765,7 +94,10 @@ out:
>    */
>   void efi_net_set_dhcp_ack(void *pkt, int len)
>   {
> +	efi_status_t r = EFI_SUCCESS;
>   	struct efi_pxe_packet **dhcp_ack;
> +	struct efi_handler *phandler;
> +	struct efi_pxe_base_code_protocol *pxe;
>   	struct udevice *dev;
>   	int i;
>   
> @@ -791,200 +123,17 @@ void efi_net_set_dhcp_ack(void *pkt, int len)
>   
>   	for (i = 0; i < MAX_EFI_NET_OBJS; i++) {
>   		if (net_objs[i] && net_objs[i]->dev == dev) {
> -			net_objs[i]->pxe_mode.dhcp_ack = **dhcp_ack;
> -		}
> -	}
> -}
> -
> -/**
> - * efi_net_push() - callback for received network packet
> - *
> - * This function is called when a network packet is received by eth_rx().
> - *
> - * @pkt:	network packet
> - * @len:	length
> - */
> -static void efi_net_push(void *pkt, int len)
> -{
> -	int rx_packet_next;
> -	struct efi_net_obj *nt;
> -
> -	nt = net_objs[curr_efi_net_obj];
> -	if (!nt)
> -		return;
> -
> -	/* Check that we at least received an Ethernet header */
> -	if (len < sizeof(struct ethernet_hdr))
> -		return;
> -
> -	/* Check that the buffer won't overflow */
> -	if (len > PKTSIZE_ALIGN)
> -		return;
> -
> -	/* Can't store more than pre-alloced buffer */
> -	if (nt->rx_packet_num >= ETH_PACKETS_BATCH_RECV)
> -		return;
> -
> -	rx_packet_next = (nt->rx_packet_idx + nt->rx_packet_num) %
> -	    ETH_PACKETS_BATCH_RECV;
> -	memcpy(nt->receive_buffer[rx_packet_next], pkt, len);
> -	nt->receive_lengths[rx_packet_next] = len;
> -
> -	nt->rx_packet_num++;
> -}
> -
> -/**
> - * efi_network_timer_notify() - check if a new network packet has been received
> - *
> - * This notification function is called in every timer cycle.
> - *
> - * @event:	the event for which this notification function is registered
> - * @context:	event context - not used in this function
> - */
> -static void EFIAPI efi_network_timer_notify(struct efi_event *event,
> -					    void *context)
> -{
> -	struct efi_simple_network *this = (struct efi_simple_network *)context;
> -	struct efi_net_obj *nt;
> -
> -	EFI_ENTRY("%p, %p", event, context);
> -
> -	/*
> -	 * Some network drivers do not support calling eth_rx() before
> -	 * initialization.
> -	 */
> -	if (!this || this->mode->state != EFI_NETWORK_INITIALIZED)
> -		goto out;
> -
> -	nt = efi_netobj_from_snp(this);
> -	curr_efi_net_obj = nt->efi_seq_num;
> -
> -	if (!nt->rx_packet_num) {
> -		eth_set_dev(nt->dev);
> -		env_set("ethact", eth_get_name());
> -		push_packet = efi_net_push;
> -		eth_rx();
> -		push_packet = NULL;
> -		if (nt->rx_packet_num) {
> -			this->int_status |=
> -				EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT;
> -			nt->wait_for_packet->is_signaled = true;
> +			phandler = NULL;
> +			r = efi_search_protocol(&net_objs[i]->header,
> +						&efi_pxe_base_code_protocol_guid,
> +						&phandler);
> +			if (r == EFI_SUCCESS && phandler) {
> +				pxe = phandler->protocol_interface;
> +				pxe->mode->dhcp_ack = **dhcp_ack;
> +			}
> +			break;
>   		}
>   	}
> -out:
> -	EFI_EXIT(EFI_SUCCESS);
> -}
> -
> -static efi_status_t EFIAPI efi_pxe_base_code_start(
> -				struct efi_pxe_base_code_protocol *this,
> -				u8 use_ipv6)
> -{
> -	return EFI_UNSUPPORTED;
> -}
> -
> -static efi_status_t EFIAPI efi_pxe_base_code_stop(
> -				struct efi_pxe_base_code_protocol *this)
> -{
> -	return EFI_UNSUPPORTED;
> -}
> -
> -static efi_status_t EFIAPI efi_pxe_base_code_dhcp(
> -				struct efi_pxe_base_code_protocol *this,
> -				u8 sort_offers)
> -{
> -	return EFI_UNSUPPORTED;
> -}
> -
> -static efi_status_t EFIAPI efi_pxe_base_code_discover(
> -				struct efi_pxe_base_code_protocol *this,
> -				u16 type, u16 *layer, u8 bis,
> -				struct efi_pxe_base_code_discover_info *info)
> -{
> -	return EFI_UNSUPPORTED;
> -}
> -
> -static efi_status_t EFIAPI efi_pxe_base_code_mtftp(
> -				struct efi_pxe_base_code_protocol *this,
> -				u32 operation, void *buffer_ptr,
> -				u8 overwrite, efi_uintn_t *buffer_size,
> -				struct efi_ip_address server_ip, char *filename,
> -				struct efi_pxe_base_code_mtftp_info *info,
> -				u8 dont_use_buffer)
> -{
> -	return EFI_UNSUPPORTED;
> -}
> -
> -static efi_status_t EFIAPI efi_pxe_base_code_udp_write(
> -				struct efi_pxe_base_code_protocol *this,
> -				u16 op_flags, struct efi_ip_address *dest_ip,
> -				u16 *dest_port,
> -				struct efi_ip_address *gateway_ip,
> -				struct efi_ip_address *src_ip, u16 *src_port,
> -				efi_uintn_t *header_size, void *header_ptr,
> -				efi_uintn_t *buffer_size, void *buffer_ptr)
> -{
> -	return EFI_UNSUPPORTED;
> -}
> -
> -static efi_status_t EFIAPI efi_pxe_base_code_udp_read(
> -				struct efi_pxe_base_code_protocol *this,
> -				u16 op_flags, struct efi_ip_address *dest_ip,
> -				u16 *dest_port, struct efi_ip_address *src_ip,
> -				u16 *src_port, efi_uintn_t *header_size,
> -				void *header_ptr, efi_uintn_t *buffer_size,
> -				void *buffer_ptr)
> -{
> -	return EFI_UNSUPPORTED;
> -}
> -
> -static efi_status_t EFIAPI efi_pxe_base_code_set_ip_filter(
> -				struct efi_pxe_base_code_protocol *this,
> -				struct efi_pxe_base_code_filter *new_filter)
> -{
> -	return EFI_UNSUPPORTED;
> -}
> -
> -static efi_status_t EFIAPI efi_pxe_base_code_arp(
> -				struct efi_pxe_base_code_protocol *this,
> -				struct efi_ip_address *ip_addr,
> -				struct efi_mac_address *mac_addr)
> -{
> -	return EFI_UNSUPPORTED;
> -}
> -
> -static efi_status_t EFIAPI efi_pxe_base_code_set_parameters(
> -				struct efi_pxe_base_code_protocol *this,
> -				u8 *new_auto_arp, u8 *new_send_guid,
> -				u8 *new_ttl, u8 *new_tos,
> -				u8 *new_make_callback)
> -{
> -	return EFI_UNSUPPORTED;
> -}
> -
> -static efi_status_t EFIAPI efi_pxe_base_code_set_station_ip(
> -				struct efi_pxe_base_code_protocol *this,
> -				struct efi_ip_address *new_station_ip,
> -				struct efi_ip_address *new_subnet_mask)
> -{
> -	return EFI_UNSUPPORTED;
> -}
> -
> -static efi_status_t EFIAPI efi_pxe_base_code_set_packets(
> -				struct efi_pxe_base_code_protocol *this,
> -				u8 *new_dhcp_discover_valid,
> -				u8 *new_dhcp_ack_received,
> -				u8 *new_proxy_offer_received,
> -				u8 *new_pxe_discover_valid,
> -				u8 *new_pxe_reply_received,
> -				u8 *new_pxe_bis_reply_received,
> -				EFI_PXE_BASE_CODE_PACKET *new_dchp_discover,
> -				EFI_PXE_BASE_CODE_PACKET *new_dhcp_acc,
> -				EFI_PXE_BASE_CODE_PACKET *new_proxy_offer,
> -				EFI_PXE_BASE_CODE_PACKET *new_pxe_discover,
> -				EFI_PXE_BASE_CODE_PACKET *new_pxe_reply,
> -				EFI_PXE_BASE_CODE_PACKET *new_pxe_bis_reply)
> -{
> -	return EFI_UNSUPPORTED;
>   }
>   
>   /**
> @@ -1066,6 +215,10 @@ efi_status_t efi_net_do_start(struct udevice *dev)
>   	efi_status_t r = EFI_SUCCESS;
>   	struct efi_net_obj *netobj;
>   	struct efi_device_path *net_dp;
> +#if IS_ENABLED(CONFIG_EFI_HTTP_PROTOCOL)
> +	struct efi_handler *phandler;
> +	struct efi_pxe_base_code_protocol *pxe;
> +#endif
>   	int i;
>   
>   	netobj = NULL;
> @@ -1097,17 +250,24 @@ efi_status_t efi_net_do_start(struct udevice *dev)
>   	if (r != EFI_SUCCESS)
>   		return r;
>   set_addr:
> -#ifdef CONFIG_EFI_HTTP_PROTOCOL
> +#if IS_ENABLED(CONFIG_EFI_HTTP_PROTOCOL)
>   	/*
>   	 * No harm on doing the following. If the PXE handle is present, the client could
>   	 * find it and try to get its IP address from it. In here the PXE handle is present
>   	 * but the PXE protocol is not yet implmenented, so we add this in the meantime.
>   	 */
> -	efi_net_get_addr((struct efi_ipv4_address *)&netobj->pxe_mode.station_ip,
> -			 (struct efi_ipv4_address *)&netobj->pxe_mode.subnet_mask, NULL, dev);
> +	pxe = NULL;
> +	r = efi_search_protocol(&netobj->header,
> +				&efi_pxe_base_code_protocol_guid,
> +				&phandler);
> +	if (r == EFI_SUCCESS && phandler) {
> +		pxe = phandler->protocol_interface;
> +		efi_net_get_addr((struct efi_ipv4_address *)&pxe->mode->station_ip,
> +				 (struct efi_ipv4_address *)&pxe->mode->subnet_mask, NULL, dev);
> +	}
>   #endif
>   
> -	return r;
> +	return EFI_SUCCESS;
>   }
>   
>   /**
> @@ -1121,9 +281,7 @@ efi_status_t efi_net_register(struct udevice *dev)
>   	efi_status_t r;
>   	int seq_num;
>   	struct efi_net_obj *netobj;
> -	void *transmit_buffer = NULL;
> -	uchar **receive_buffer = NULL;
> -	size_t *receive_lengths;
> +	struct efi_pxe_packet *dhcp_ack;
>   	int i, j;
>   
>   	if (!dev) {
> @@ -1150,161 +308,58 @@ efi_status_t efi_net_register(struct udevice *dev)
>   
>   	/* We only expose the "active" network device, so one is enough */
>   	netobj = calloc(1, sizeof(*netobj));
> -	if (!netobj)
> -		goto out_of_resources;
> -
> -	netobj->dev = dev;
> -
> -	/* Allocate an aligned transmit buffer */
> -	transmit_buffer = calloc(1, PKTSIZE_ALIGN + PKTALIGN);
> -	if (!transmit_buffer)
> -		goto out_of_resources;
> -	transmit_buffer = (void *)ALIGN((uintptr_t)transmit_buffer, PKTALIGN);
> -	netobj->transmit_buffer = transmit_buffer;
> -
> -	/* Allocate a number of receive buffers */
> -	receive_buffer = calloc(ETH_PACKETS_BATCH_RECV,
> -				sizeof(*receive_buffer));
> -	if (!receive_buffer)
> -		goto out_of_resources;
> -	for (i = 0; i < ETH_PACKETS_BATCH_RECV; i++) {
> -		receive_buffer[i] = malloc(PKTSIZE_ALIGN);
> -		if (!receive_buffer[i])
> -			goto out_of_resources;
> +	if (!netobj) {
> +		printf("ERROR: Out of memory\n");
> +		return EFI_OUT_OF_RESOURCES;
>   	}
> -	netobj->receive_buffer = receive_buffer;
>   
> -	receive_lengths = calloc(ETH_PACKETS_BATCH_RECV,
> -				 sizeof(*receive_lengths));
> -	if (!receive_lengths)
> -		goto out_of_resources;
> -	netobj->receive_lengths = receive_lengths;
> +	netobj->dev = dev;
>   
>   	/* Hook net up to the device list */
>   	efi_add_handle(&netobj->header);
>   
> -	/* Fill in object data */
> -	r = efi_add_protocol(&netobj->header, &efi_net_guid,
> -			     &netobj->net);
> +	/* Install EFI_SIMPLE_NETWORK_PROTOCOL */
> +	r = efi_simple_network_install(&netobj->header, dev);
>   	if (r != EFI_SUCCESS)
>   		goto failure_to_add_protocol;
>   
> -	r = efi_add_protocol(&netobj->header, &efi_pxe_base_code_protocol_guid,
> -			     &netobj->pxe);
> -	if (r != EFI_SUCCESS)
> -		goto failure_to_add_protocol;
> -	netobj->net.revision = EFI_SIMPLE_NETWORK_PROTOCOL_REVISION;
> -	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_STOPPED;
> -	if (dev_get_plat(dev))
> -		memcpy(netobj->net_mode.current_address.mac_addr,
> -		       ((struct eth_pdata *)dev_get_plat(dev))->enetaddr, 6);
> -	netobj->net_mode.hwaddr_size = ARP_HLEN;
> -	netobj->net_mode.media_header_size = ETHER_HDR_SIZE;
> -	netobj->net_mode.max_packet_size = PKTSIZE;
> -	netobj->net_mode.if_type = ARP_ETHER;
> -
> -	netobj->pxe.revision = EFI_PXE_BASE_CODE_PROTOCOL_REVISION;
> -	netobj->pxe.start = efi_pxe_base_code_start;
> -	netobj->pxe.stop = efi_pxe_base_code_stop;
> -	netobj->pxe.dhcp = efi_pxe_base_code_dhcp;
> -	netobj->pxe.discover = efi_pxe_base_code_discover;
> -	netobj->pxe.mtftp = efi_pxe_base_code_mtftp;
> -	netobj->pxe.udp_write = efi_pxe_base_code_udp_write;
> -	netobj->pxe.udp_read = efi_pxe_base_code_udp_read;
> -	netobj->pxe.set_ip_filter = efi_pxe_base_code_set_ip_filter;
> -	netobj->pxe.arp = efi_pxe_base_code_arp;
> -	netobj->pxe.set_parameters = efi_pxe_base_code_set_parameters;
> -	netobj->pxe.set_station_ip = efi_pxe_base_code_set_station_ip;
> -	netobj->pxe.set_packets = efi_pxe_base_code_set_packets;
> -	netobj->pxe.mode = &netobj->pxe_mode;
> -
>   	/*
>   	 * Scan dhcp entries for one corresponding
>   	 * to this udevice, from newest to oldest
>   	 */
> +	dhcp_ack = NULL;
>   	i = (next_dhcp_entry + MAX_NUM_DHCP_ENTRIES - 1) % MAX_NUM_DHCP_ENTRIES;
>   	for (j = 0; dhcp_cache[i].is_valid && j < MAX_NUM_DHCP_ENTRIES;
>   	     i = (i + MAX_NUM_DHCP_ENTRIES - 1) % MAX_NUM_DHCP_ENTRIES, j++) {
>   		if (dev == dhcp_cache[i].dev) {
> -			netobj->pxe_mode.dhcp_ack = *dhcp_cache[i].dhcp_ack;
> +			dhcp_ack = dhcp_cache[i].dhcp_ack;
>   			break;
>   		}
>   	}
>   
> -	/*
> -	 * Create WaitForPacket event.
> -	 */
> -	r = efi_create_event(EVT_NOTIFY_WAIT, TPL_CALLBACK,
> -			     efi_network_timer_notify, NULL, NULL,
> -			     &netobj->wait_for_packet);
> -	if (r != EFI_SUCCESS) {
> -		printf("ERROR: Failed to register network event\n");
> -		return r;
> -	}
> -	netobj->net.wait_for_packet = netobj->wait_for_packet;
> -	/*
> -	 * Create a timer event.
> -	 *
> -	 * The notification function is used to check if a new network packet
> -	 * has been received.
> -	 *
> -	 * iPXE is running at TPL_CALLBACK most of the time. Use a higher TPL.
> -	 */
> -	r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_NOTIFY,
> -			     efi_network_timer_notify, &netobj->net, NULL,
> -			     &netobj->network_timer_event);
> -	if (r != EFI_SUCCESS) {
> -		printf("ERROR: Failed to register network event\n");
> -		return r;
> -	}
> -	/* Network is time critical, create event in every timer cycle */
> -	r = efi_set_timer(netobj->network_timer_event, EFI_TIMER_PERIODIC, 0);
> -	if (r != EFI_SUCCESS) {
> -		printf("ERROR: Failed to set network timer\n");
> -		return r;
> -	}
> +	/* Install EFI_PXE_BASE_CODE_PROTOCOL */
> +	r = efi_pxe_install(&netobj->header, dhcp_ack);
> +	if (r != EFI_SUCCESS)
> +		goto failure_to_add_protocol;
>   
>   #if IS_ENABLED(CONFIG_EFI_IP4_CONFIG2_PROTOCOL)
> +	/* Install EFI_IP4_CONFIG2_PROTOCOL */
>   	r = efi_ip4_config2_install(&netobj->header);
>   	if (r != EFI_SUCCESS)
>   		goto failure_to_add_protocol;
>   #endif
>   #ifdef CONFIG_EFI_HTTP_PROTOCOL
> +	/* Install EFI_HTTP_PROTOCOL */
>   	r = efi_http_install(&netobj->header);
>   	if (r != EFI_SUCCESS)
>   		goto failure_to_add_protocol;
>   #endif
> -	netobj->efi_seq_num = seq_num;
> +
>   	net_objs[seq_num] = netobj;
>   	return EFI_SUCCESS;
>   failure_to_add_protocol:
>   	printf("ERROR: Failure to add protocol\n");
>   	return r;
> -out_of_resources:
> -	free(netobj);
> -	netobj = NULL;
> -	free(transmit_buffer);
> -	if (receive_buffer)
> -		for (i = 0; i < ETH_PACKETS_BATCH_RECV; i++)
> -			free(receive_buffer[i]);
> -	free(receive_buffer);
> -	free(receive_lengths);
> -	printf("ERROR: Out of memory\n");
> -	return EFI_OUT_OF_RESOURCES;
>   }
>   
>   /**
> @@ -1656,7 +711,8 @@ efi_status_t efi_net_do_request(u8 *url, enum efi_http_method method, void **buf
>   		ret = efi_search_protocol(&net_objs[i]->header,
>   					  &efi_http_service_binding_guid,
>   					  &phandler);
> -		if (ret == EFI_SUCCESS && phandler == (void *)parent) {
> +		if (ret == EFI_SUCCESS && phandler &&
> +		    phandler->protocol_interface == (void *)parent) {
>   			dev = net_objs[i]->dev;
>   			break;
>   		}
> diff --git a/lib/efi_loader/net/efi_pxe.c b/lib/efi_loader/net/efi_pxe.c
> new file mode 100644
> index 00000000000..69cc5f22f6d
> --- /dev/null
> +++ b/lib/efi_loader/net/efi_pxe.c
> @@ -0,0 +1,190 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * PXE base code protocol
> + *
> + * Copyright (c) 2016 Alexander Graf
> + *
> + */
> +
> +#define LOG_CATEGORY LOGC_EFI
> +
> +#include <efi_loader.h>
> +#include <dm.h>
> +#include <linux/sizes.h>
> +#include <malloc.h>
> +#include <vsprintf.h>
> +#include <net.h>
> +
> +static const efi_guid_t efi_pxe_base_code_protocol_guid =
> +					EFI_PXE_BASE_CODE_PROTOCOL_GUID;
> +
> +/**
> + * struct efi_pxe_base_code_extended_protocol - EFI object representing a
> + *												EFI_PXE_BASE_CODE_PROTOCOL
> + *												interface
> + *
> + * @pxe:			PXE base code protocol interface
> + * @pxe_mode:			status of the PXE base code protocol
> +
> + */
> +struct efi_pxe_base_code_extended_protocol {
> +	struct efi_pxe_base_code_protocol pxe;
> +	struct efi_pxe_mode pxe_mode;
> +};
> +
> +static efi_status_t EFIAPI efi_pxe_base_code_start(
> +				struct efi_pxe_base_code_protocol *this,
> +				u8 use_ipv6)
> +{
> +	return EFI_UNSUPPORTED;
> +}
> +
> +static efi_status_t EFIAPI efi_pxe_base_code_stop(
> +				struct efi_pxe_base_code_protocol *this)
> +{
> +	return EFI_UNSUPPORTED;
> +}
> +
> +static efi_status_t EFIAPI efi_pxe_base_code_dhcp(
> +				struct efi_pxe_base_code_protocol *this,
> +				u8 sort_offers)
> +{
> +	return EFI_UNSUPPORTED;
> +}
> +
> +static efi_status_t EFIAPI efi_pxe_base_code_discover(
> +				struct efi_pxe_base_code_protocol *this,
> +				u16 type, u16 *layer, u8 bis,
> +				struct efi_pxe_base_code_discover_info *info)
> +{
> +	return EFI_UNSUPPORTED;
> +}
> +
> +static efi_status_t EFIAPI efi_pxe_base_code_mtftp(
> +				struct efi_pxe_base_code_protocol *this,
> +				u32 operation, void *buffer_ptr,
> +				u8 overwrite, efi_uintn_t *buffer_size,
> +				struct efi_ip_address server_ip, char *filename,
> +				struct efi_pxe_base_code_mtftp_info *info,
> +				u8 dont_use_buffer)
> +{
> +	return EFI_UNSUPPORTED;
> +}
> +
> +static efi_status_t EFIAPI efi_pxe_base_code_udp_write(
> +				struct efi_pxe_base_code_protocol *this,
> +				u16 op_flags, struct efi_ip_address *dest_ip,
> +				u16 *dest_port,
> +				struct efi_ip_address *gateway_ip,
> +				struct efi_ip_address *src_ip, u16 *src_port,
> +				efi_uintn_t *header_size, void *header_ptr,
> +				efi_uintn_t *buffer_size, void *buffer_ptr)
> +{
> +	return EFI_UNSUPPORTED;
> +}
> +
> +static efi_status_t EFIAPI efi_pxe_base_code_udp_read(
> +				struct efi_pxe_base_code_protocol *this,
> +				u16 op_flags, struct efi_ip_address *dest_ip,
> +				u16 *dest_port, struct efi_ip_address *src_ip,
> +				u16 *src_port, efi_uintn_t *header_size,
> +				void *header_ptr, efi_uintn_t *buffer_size,
> +				void *buffer_ptr)
> +{
> +	return EFI_UNSUPPORTED;
> +}
> +
> +static efi_status_t EFIAPI efi_pxe_base_code_set_ip_filter(
> +				struct efi_pxe_base_code_protocol *this,
> +				struct efi_pxe_base_code_filter *new_filter)
> +{
> +	return EFI_UNSUPPORTED;
> +}
> +
> +static efi_status_t EFIAPI efi_pxe_base_code_arp(
> +				struct efi_pxe_base_code_protocol *this,
> +				struct efi_ip_address *ip_addr,
> +				struct efi_mac_address *mac_addr)
> +{
> +	return EFI_UNSUPPORTED;
> +}
> +
> +static efi_status_t EFIAPI efi_pxe_base_code_set_parameters(
> +				struct efi_pxe_base_code_protocol *this,
> +				u8 *new_auto_arp, u8 *new_send_guid,
> +				u8 *new_ttl, u8 *new_tos,
> +				u8 *new_make_callback)
> +{
> +	return EFI_UNSUPPORTED;
> +}
> +
> +static efi_status_t EFIAPI efi_pxe_base_code_set_station_ip(
> +				struct efi_pxe_base_code_protocol *this,
> +				struct efi_ip_address *new_station_ip,
> +				struct efi_ip_address *new_subnet_mask)
> +{
> +	return EFI_UNSUPPORTED;
> +}
> +
> +static efi_status_t EFIAPI efi_pxe_base_code_set_packets(
> +				struct efi_pxe_base_code_protocol *this,
> +				u8 *new_dhcp_discover_valid,
> +				u8 *new_dhcp_ack_received,
> +				u8 *new_proxy_offer_received,
> +				u8 *new_pxe_discover_valid,
> +				u8 *new_pxe_reply_received,
> +				u8 *new_pxe_bis_reply_received,
> +				EFI_PXE_BASE_CODE_PACKET *new_dchp_discover,
> +				EFI_PXE_BASE_CODE_PACKET *new_dhcp_acc,
> +				EFI_PXE_BASE_CODE_PACKET *new_proxy_offer,
> +				EFI_PXE_BASE_CODE_PACKET *new_pxe_discover,
> +				EFI_PXE_BASE_CODE_PACKET *new_pxe_reply,
> +				EFI_PXE_BASE_CODE_PACKET *new_pxe_bis_reply)
> +{
> +	return EFI_UNSUPPORTED;
> +}
> +
> +
> +/**
> + * efi_pxe_install() - install the EFI_PXE_BASE_CODE_PROTOCOL
> + *
> + * @handle:	handle to install the protocol
> + */
> +efi_status_t efi_pxe_install(const efi_handle_t handle,
> +			     struct efi_pxe_packet *dhcp_ack)
> +{
> +	efi_status_t r;
> +	struct efi_pxe_base_code_extended_protocol *pxe;
> +
> +	/* We only expose the "active" network device, so one is enough */
> +	pxe = calloc(1, sizeof(*pxe));
> +	if (!pxe) {
> +		printf("ERROR: Out of memory\n");
> +		return EFI_OUT_OF_RESOURCES;
> +	}
> +
> +	pxe->pxe.revision = EFI_PXE_BASE_CODE_PROTOCOL_REVISION;
> +	pxe->pxe.start = efi_pxe_base_code_start;
> +	pxe->pxe.stop = efi_pxe_base_code_stop;
> +	pxe->pxe.dhcp = efi_pxe_base_code_dhcp;
> +	pxe->pxe.discover = efi_pxe_base_code_discover;
> +	pxe->pxe.mtftp = efi_pxe_base_code_mtftp;
> +	pxe->pxe.udp_write = efi_pxe_base_code_udp_write;
> +	pxe->pxe.udp_read = efi_pxe_base_code_udp_read;
> +	pxe->pxe.set_ip_filter = efi_pxe_base_code_set_ip_filter;
> +	pxe->pxe.arp = efi_pxe_base_code_arp;
> +	pxe->pxe.set_parameters = efi_pxe_base_code_set_parameters;
> +	pxe->pxe.set_station_ip = efi_pxe_base_code_set_station_ip;
> +	pxe->pxe.set_packets = efi_pxe_base_code_set_packets;
> +	pxe->pxe.mode = &pxe->pxe_mode;
> +
> +	if (dhcp_ack)
> +		pxe->pxe_mode.dhcp_ack = *dhcp_ack;
> +
> +	r = efi_add_protocol(handle, &efi_pxe_base_code_protocol_guid,
> +			     &pxe->pxe);
> +	if (r != EFI_SUCCESS)
> +		return r;
> +
> +	return EFI_SUCCESS;
> +}
> diff --git a/lib/efi_loader/net/efi_simple_network.c b/lib/efi_loader/net/efi_simple_network.c
> new file mode 100644
> index 00000000000..c7c03062b11
> --- /dev/null
> +++ b/lib/efi_loader/net/efi_simple_network.c
> @@ -0,0 +1,880 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Simple network protocol
> + *
> + * Copyright (c) 2016 Alexander Graf
> + *
> + * The simple network protocol has the following statuses and services
> + * to move between them:
> + *
> + * Start():	 EfiSimpleNetworkStopped     -> EfiSimpleNetworkStarted
> + * Initialize(): EfiSimpleNetworkStarted     -> EfiSimpleNetworkInitialized
> + * Shutdown():	 EfiSimpleNetworkInitialized -> EfiSimpleNetworkStarted
> + * Stop():	 EfiSimpleNetworkStarted     -> EfiSimpleNetworkStopped
> + * Reset():	 EfiSimpleNetworkInitialized -> EfiSimpleNetworkInitialized
> + */
> +
> +#define LOG_CATEGORY LOGC_EFI
> +
> +#include <efi_loader.h>
> +#include <dm.h>
> +#include <linux/sizes.h>
> +#include <malloc.h>
> +#include <vsprintf.h>
> +#include <net.h>
> +
> +static const efi_guid_t efi_net_guid = EFI_SIMPLE_NETWORK_PROTOCOL_GUID;
> +
> +/**
> + * struct efi_simple_network_extended_protocol - EFI object representing an
> + *						 EFI_SIMPLE_NETWORK_PROTOCOL
> + *						 interface
> + *
> + * @net:			simple network protocol interface
> + * @net_mode:			status of the network interface
> + * @dev:			net udevice
> + * @new_tx_packet:		new transmit packet
> + * @transmit_buffer:	transmit buffer
> + * @receive_buffer:		array of receive buffers
> + * @receive_lengths:	array of lengths for received packets
> + * @rx_packet_idx:		index of the current receive packet
> + * @rx_packet_num:		number of received packets
> + * @wait_for_packet:	signaled when a packet has been received
> + * @network_timer_event:	event to check for new network packets.
> + */
> +struct efi_simple_network_extended_protocol {
> +	struct efi_simple_network_protocol net;
> +	struct efi_simple_network_mode net_mode;
> +	struct udevice *dev;
> +	void *new_tx_packet;
> +	void *transmit_buffer;
> +	uchar **receive_buffer;
> +	size_t *receive_lengths;
> +	int rx_packet_idx;
> +	int rx_packet_num;
> +	struct efi_event *wait_for_packet;
> +	struct efi_event *network_timer_event;
> +};
> +
> +static struct efi_simple_network_extended_protocol *current_network;
> +
> +/*
> + * efi_net_start() - start the network interface
> + *
> + * This function implements the Start service of the
> + * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
> + * (UEFI) specification for details.
> + *
> + * @this:	pointer to the protocol instance
> + * Return:	status code
> + */
> +static efi_status_t EFIAPI efi_net_start(struct efi_simple_network_protocol *this)
> +{
> +	efi_status_t ret = EFI_SUCCESS;
> +	struct efi_simple_network_extended_protocol *nt;
> +
> +	EFI_ENTRY("%p", this);
> +	/* Check parameters */
> +	if (!this) {
> +		ret = EFI_INVALID_PARAMETER;
> +		goto out;
> +	}
> +
> +	nt = (struct efi_simple_network_extended_protocol *)this;
> +
> +	if (this->mode->state != EFI_NETWORK_STOPPED) {
> +		ret = EFI_ALREADY_STARTED;
> +	} else {
> +		this->int_status = 0;
> +		nt->wait_for_packet->is_signaled = false;
> +		this->mode->state = EFI_NETWORK_STARTED;
> +	}
> +out:
> +	return EFI_EXIT(ret);
> +}
> +
> +/*
> + * efi_net_stop() - stop the network interface
> + *
> + * This function implements the Stop service of the
> + * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
> + * (UEFI) specification for details.
> + *
> + * @this:	pointer to the protocol instance
> + * Return:	status code
> + */
> +static efi_status_t EFIAPI efi_net_stop(struct efi_simple_network_protocol *this)
> +{
> +	efi_status_t ret = EFI_SUCCESS;
> +	struct efi_simple_network_extended_protocol *nt;
> +
> +	EFI_ENTRY("%p", this);
> +
> +	/* Check parameters */
> +	if (!this) {
> +		ret = EFI_INVALID_PARAMETER;
> +		goto out;
> +	}
> +
> +	nt = (struct efi_simple_network_extended_protocol *)this;
> +
> +	if (this->mode->state == EFI_NETWORK_STOPPED) {
> +		ret = EFI_NOT_STARTED;
> +	} else {
> +		/* Disable hardware and put it into the reset state */
> +		eth_set_dev(nt->dev);
> +		env_set("ethact", eth_get_name());
> +		eth_halt();
> +		/* Clear cache of packets */
> +		nt->rx_packet_num = 0;
> +		this->mode->state = EFI_NETWORK_STOPPED;
> +	}
> +out:
> +	return EFI_EXIT(ret);
> +}
> +
> +/*
> + * efi_net_initialize() - initialize the network interface
> + *
> + * This function implements the Initialize service of the
> + * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
> + * (UEFI) specification for details.
> + *
> + * @this:	pointer to the protocol instance
> + * @extra_rx:	extra receive buffer to be allocated
> + * @extra_tx:	extra transmit buffer to be allocated
> + * Return:	status code
> + */
> +static efi_status_t EFIAPI efi_net_initialize(struct efi_simple_network_protocol *this,
> +					      ulong extra_rx, ulong extra_tx)
> +{
> +	int ret;
> +	efi_status_t r = EFI_SUCCESS;
> +	struct efi_simple_network_extended_protocol *nt;
> +
> +	EFI_ENTRY("%p, %lx, %lx", this, extra_rx, extra_tx);
> +
> +	/* Check parameters */
> +	if (!this) {
> +		r = EFI_INVALID_PARAMETER;
> +		goto out;
> +	}
> +	nt = (struct efi_simple_network_extended_protocol *)this;
> +
> +	switch (this->mode->state) {
> +	case EFI_NETWORK_INITIALIZED:
> +	case EFI_NETWORK_STARTED:
> +		break;
> +	default:
> +		r = EFI_NOT_STARTED;
> +		goto out;
> +	}
> +
> +	/* Setup packet buffers */
> +	net_init();
> +	/* Clear cache of packets */
> +	nt->rx_packet_num = 0;
> +	/* Set the net device corresponding to the efi net object */
> +	eth_set_dev(nt->dev);
> +	env_set("ethact", eth_get_name());
> +	/* Get hardware ready for send and receive operations */
> +	ret = eth_start_udev(nt->dev);
> +	if (ret < 0) {
> +		eth_halt();
> +		this->mode->state = EFI_NETWORK_STOPPED;
> +		r = EFI_DEVICE_ERROR;
> +		goto out;
> +	} else {
> +		this->int_status = 0;
> +		nt->wait_for_packet->is_signaled = false;
> +		this->mode->state = EFI_NETWORK_INITIALIZED;
> +	}
> +out:
> +	return EFI_EXIT(r);
> +}
> +
> +/*
> + * efi_net_reset() - reinitialize the network interface
> + *
> + * This function implements the Reset service of the
> + * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
> + * (UEFI) specification for details.
> + *
> + * @this:			pointer to the protocol instance
> + * @extended_verification:	execute exhaustive verification
> + * Return:			status code
> + */
> +static efi_status_t EFIAPI efi_net_reset(struct efi_simple_network_protocol *this,
> +					 int extended_verification)
> +{
> +	efi_status_t ret;
> +
> +	EFI_ENTRY("%p, %x", this, extended_verification);
> +
> +	/* Check parameters */
> +	if (!this) {
> +		ret = EFI_INVALID_PARAMETER;
> +		goto out;
> +	}
> +
> +	switch (this->mode->state) {
> +	case EFI_NETWORK_INITIALIZED:
> +		break;
> +	case EFI_NETWORK_STOPPED:
> +		ret = EFI_NOT_STARTED;
> +		goto out;
> +	default:
> +		ret = EFI_DEVICE_ERROR;
> +		goto out;
> +	}
> +
> +	this->mode->state = EFI_NETWORK_STARTED;
> +	ret = EFI_CALL(efi_net_initialize(this, 0, 0));
> +out:
> +	return EFI_EXIT(ret);
> +}
> +
> +/*
> + * efi_net_shutdown() - shut down the network interface
> + *
> + * This function implements the Shutdown service of the
> + * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
> + * (UEFI) specification for details.
> + *
> + * @this:	pointer to the protocol instance
> + * Return:	status code
> + */
> +static efi_status_t EFIAPI efi_net_shutdown(struct efi_simple_network_protocol *this)
> +{
> +	efi_status_t ret = EFI_SUCCESS;
> +	struct efi_simple_network_extended_protocol *nt;
> +
> +	EFI_ENTRY("%p", this);
> +
> +	/* Check parameters */
> +	if (!this) {
> +		ret = EFI_INVALID_PARAMETER;
> +		goto out;
> +	}
> +	nt = (struct efi_simple_network_extended_protocol *)this;
> +
> +	switch (this->mode->state) {
> +	case EFI_NETWORK_INITIALIZED:
> +		break;
> +	case EFI_NETWORK_STOPPED:
> +		ret = EFI_NOT_STARTED;
> +		goto out;
> +	default:
> +		ret = EFI_DEVICE_ERROR;
> +		goto out;
> +	}
> +
> +	eth_set_dev(nt->dev);
> +	env_set("ethact", eth_get_name());
> +	eth_halt();
> +
> +	this->int_status = 0;
> +	nt->wait_for_packet->is_signaled = false;
> +	this->mode->state = EFI_NETWORK_STARTED;
> +
> +out:
> +	return EFI_EXIT(ret);
> +}
> +
> +/*
> + * efi_net_receive_filters() - mange multicast receive filters
> + *
> + * This function implements the ReceiveFilters service of the
> + * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
> + * (UEFI) specification for details.
> + *
> + * @this:		pointer to the protocol instance
> + * @enable:		bit mask of receive filters to enable
> + * @disable:		bit mask of receive filters to disable
> + * @reset_mcast_filter:	true resets contents of the filters
> + * @mcast_filter_count:	number of hardware MAC addresses in the new filters list
> + * @mcast_filter:	list of new filters
> + * Return:		status code
> + */
> +static efi_status_t EFIAPI efi_net_receive_filters
> +		(struct efi_simple_network_protocol *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);
> +
> +	return EFI_EXIT(EFI_UNSUPPORTED);
> +}
> +
> +/*
> + * efi_net_station_address() - set the hardware MAC address
> + *
> + * This function implements the StationAddress service of the
> + * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
> + * (UEFI) specification for details.
> + *
> + * @this:	pointer to the protocol instance
> + * @reset:	if true reset the address to default
> + * @new_mac:	new MAC address
> + * Return:	status code
> + */
> +static efi_status_t EFIAPI efi_net_station_address
> +		(struct efi_simple_network_protocol *this, int reset,
> +		 struct efi_mac_address *new_mac)
> +{
> +	EFI_ENTRY("%p, %x, %p", this, reset, new_mac);
> +
> +	return EFI_EXIT(EFI_UNSUPPORTED);
> +}
> +
> +/*
> + * efi_net_statistics() - reset or collect statistics of the network interface
> + *
> + * This function implements the Statistics service of the
> + * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
> + * (UEFI) specification for details.
> + *
> + * @this:	pointer to the protocol instance
> + * @reset:	if true, the statistics are reset
> + * @stat_size:	size of the statistics table
> + * @stat_table:	table to receive the statistics
> + * Return:	status code
> + */
> +static efi_status_t EFIAPI efi_net_statistics(struct efi_simple_network_protocol *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_UNSUPPORTED);
> +}
> +
> +/*
> + * efi_net_mcastiptomac() - translate multicast IP address to MAC address
> + *
> + * This function implements the MCastIPtoMAC service of the
> + * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
> + * (UEFI) specification for details.
> + *
> + * @this:	pointer to the protocol instance
> + * @ipv6:	true if the IP address is an IPv6 address
> + * @ip:		IP address
> + * @mac:	MAC address
> + * Return:	status code
> + */
> +static efi_status_t EFIAPI efi_net_mcastiptomac(struct efi_simple_network_protocol *this,
> +						int ipv6,
> +						struct efi_ip_address *ip,
> +						struct efi_mac_address *mac)
> +{
> +	efi_status_t ret = EFI_SUCCESS;
> +
> +	EFI_ENTRY("%p, %x, %p, %p", this, ipv6, ip, mac);
> +
> +	if (!this || !ip || !mac) {
> +		ret = EFI_INVALID_PARAMETER;
> +		goto out;
> +	}
> +
> +	if (ipv6) {
> +		ret = EFI_UNSUPPORTED;
> +		goto out;
> +	}
> +
> +	/* Multi-cast addresses are in the range 224.0.0.0 - 239.255.255.255 */
> +	if ((ip->ip_addr[0] & 0xf0) != 0xe0) {
> +		ret = EFI_INVALID_PARAMETER;
> +		goto out;
> +	};
> +
> +	switch (this->mode->state) {
> +	case EFI_NETWORK_INITIALIZED:
> +	case EFI_NETWORK_STARTED:
> +		break;
> +	default:
> +		ret = EFI_NOT_STARTED;
> +		goto out;
> +	}
> +
> +	memset(mac, 0, sizeof(struct efi_mac_address));
> +
> +	/*
> +	 * Copy lower 23 bits of IPv4 multi-cast address
> +	 * RFC 1112, RFC 7042 2.1.1.
> +	 */
> +	mac->mac_addr[0] = 0x01;
> +	mac->mac_addr[1] = 0x00;
> +	mac->mac_addr[2] = 0x5E;
> +	mac->mac_addr[3] = ip->ip_addr[1] & 0x7F;
> +	mac->mac_addr[4] = ip->ip_addr[2];
> +	mac->mac_addr[5] = ip->ip_addr[3];
> +out:
> +	return EFI_EXIT(ret);
> +}
> +
> +/**
> + * efi_net_nvdata() - read or write NVRAM
> + *
> + * This function implements the GetStatus service of the Simple Network
> + * Protocol. See the UEFI spec for details.
> + *
> + * @this:		the instance of the Simple Network Protocol
> + * @read_write:		true for read, false for write
> + * @offset:		offset in NVRAM
> + * @buffer_size:	size of buffer
> + * @buffer:		buffer
> + * Return:		status code
> + */
> +static efi_status_t EFIAPI efi_net_nvdata(struct efi_simple_network_protocol *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_UNSUPPORTED);
> +}
> +
> +/**
> + * efi_net_get_status() - get interrupt status
> + *
> + * This function implements the GetStatus service of the Simple Network
> + * Protocol. See the UEFI spec for details.
> + *
> + * @this:		the instance of the Simple Network Protocol
> + * @int_status:		interface status
> + * @txbuf:		transmission buffer
> + */
> +static efi_status_t EFIAPI efi_net_get_status(struct efi_simple_network_protocol *this,
> +					      u32 *int_status, void **txbuf)
> +{
> +	efi_status_t ret = EFI_SUCCESS;
> +	struct efi_simple_network_extended_protocol *nt;
> +
> +	EFI_ENTRY("%p, %p, %p", this, int_status, txbuf);
> +
> +	efi_timer_check();
> +
> +	/* Check parameters */
> +	if (!this) {
> +		ret = EFI_INVALID_PARAMETER;
> +		goto out;
> +	}
> +
> +	nt = (struct efi_simple_network_extended_protocol *)this;
> +
> +	switch (this->mode->state) {
> +	case EFI_NETWORK_STOPPED:
> +		ret = EFI_NOT_STARTED;
> +		goto out;
> +	case EFI_NETWORK_STARTED:
> +		ret = EFI_DEVICE_ERROR;
> +		goto out;
> +	default:
> +		break;
> +	}
> +
> +	if (int_status) {
> +		*int_status = this->int_status;
> +		this->int_status = 0;
> +	}
> +	if (txbuf)
> +		*txbuf = nt->new_tx_packet;
> +
> +	nt->new_tx_packet = NULL;
> +out:
> +	return EFI_EXIT(ret);
> +}
> +
> +/**
> + * efi_net_transmit() - transmit a packet
> + *
> + * This function implements the Transmit service of the Simple Network Protocol.
> + * See the UEFI spec for details.
> + *
> + * @this:		the instance of the Simple Network Protocol
> + * @header_size:	size of the media header
> + * @buffer_size:	size of the buffer to receive the packet
> + * @buffer:		buffer to receive the packet
> + * @src_addr:		source hardware MAC address
> + * @dest_addr:		destination hardware MAC address
> + * @protocol:		type of header to build
> + * Return:		status code
> + */
> +static efi_status_t EFIAPI efi_net_transmit
> +		(struct efi_simple_network_protocol *this, size_t header_size,
> +		 size_t buffer_size, void *buffer,
> +		 struct efi_mac_address *src_addr,
> +		 struct efi_mac_address *dest_addr, u16 *protocol)
> +{
> +	efi_status_t ret = EFI_SUCCESS;
> +	struct efi_simple_network_extended_protocol *nt;
> +
> +	EFI_ENTRY("%p, %lu, %lu, %p, %p, %p, %p", this,
> +		  (unsigned long)header_size, (unsigned long)buffer_size,
> +		  buffer, src_addr, dest_addr, protocol);
> +
> +	efi_timer_check();
> +
> +	/* Check parameters */
> +	if (!this || !buffer) {
> +		ret = EFI_INVALID_PARAMETER;
> +		goto out;
> +	}
> +
> +	nt = (struct efi_simple_network_extended_protocol *)this;
> +
> +	/* We do not support jumbo packets */
> +	if (buffer_size > PKTSIZE_ALIGN) {
> +		ret = EFI_INVALID_PARAMETER;
> +		goto out;
> +	}
> +
> +	/* At least the IP header has to fit into the buffer */
> +	if (buffer_size < this->mode->media_header_size) {
> +		ret = EFI_BUFFER_TOO_SMALL;
> +		goto out;
> +	}
> +
> +	/*
> +	 * TODO:
> +	 * Support VLANs. Use net_set_ether() for copying the header. Use a
> +	 * U_BOOT_ENV_CALLBACK to update the media header size.
> +	 */
> +	if (header_size) {
> +		struct ethernet_hdr *header = buffer;
> +
> +		if (!dest_addr || !protocol ||
> +		    header_size != this->mode->media_header_size) {
> +			ret = EFI_INVALID_PARAMETER;
> +			goto out;
> +		}
> +		if (!src_addr)
> +			src_addr = &this->mode->current_address;
> +
> +		memcpy(header->et_dest, dest_addr, ARP_HLEN);
> +		memcpy(header->et_src, src_addr, ARP_HLEN);
> +		header->et_protlen = htons(*protocol);
> +	}
> +
> +	switch (this->mode->state) {
> +	case EFI_NETWORK_STOPPED:
> +		ret = EFI_NOT_STARTED;
> +		goto out;
> +	case EFI_NETWORK_STARTED:
> +		ret = EFI_DEVICE_ERROR;
> +		goto out;
> +	default:
> +		break;
> +	}
> +
> +	eth_set_dev(nt->dev);
> +	env_set("ethact", eth_get_name());
> +
> +	/* Ethernet packets always fit, just bounce */
> +	memcpy(nt->transmit_buffer, buffer, buffer_size);
> +	net_send_packet(nt->transmit_buffer, buffer_size);
> +
> +	nt->new_tx_packet = buffer;
> +	this->int_status |= EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT;
> +out:
> +	return EFI_EXIT(ret);
> +}
> +
> +/**
> + * efi_net_receive() - receive a packet from a network interface
> + *
> + * This function implements the Receive service of the Simple Network Protocol.
> + * See the UEFI spec for details.
> + *
> + * @this:		the instance of the Simple Network Protocol
> + * @header_size:	size of the media header
> + * @buffer_size:	size of the buffer to receive the packet
> + * @buffer:		buffer to receive the packet
> + * @src_addr:		source MAC address
> + * @dest_addr:		destination MAC address
> + * @protocol:		protocol
> + * Return:		status code
> + */
> +static efi_status_t EFIAPI efi_net_receive
> +		(struct efi_simple_network_protocol *this, size_t *header_size,
> +		 size_t *buffer_size, void *buffer,
> +		 struct efi_mac_address *src_addr,
> +		 struct efi_mac_address *dest_addr, u16 *protocol)
> +{
> +	efi_status_t ret = EFI_SUCCESS;
> +	struct ethernet_hdr *eth_hdr;
> +	size_t hdr_size = sizeof(struct ethernet_hdr);
> +	u16 protlen;
> +	struct efi_simple_network_extended_protocol *nt;
> +
> +	EFI_ENTRY("%p, %p, %p, %p, %p, %p, %p", this, header_size,
> +		  buffer_size, buffer, src_addr, dest_addr, protocol);
> +
> +	/* Execute events */
> +	efi_timer_check();
> +
> +	/* Check parameters */
> +	if (!this || !buffer || !buffer_size) {
> +		ret = EFI_INVALID_PARAMETER;
> +		goto out;
> +	}
> +
> +	nt = (struct efi_simple_network_extended_protocol *)this;
> +
> +	switch (this->mode->state) {
> +	case EFI_NETWORK_STOPPED:
> +		ret = EFI_NOT_STARTED;
> +		goto out;
> +	case EFI_NETWORK_STARTED:
> +		ret = EFI_DEVICE_ERROR;
> +		goto out;
> +	default:
> +		break;
> +	}
> +
> +	if (!nt->rx_packet_num) {
> +		ret = EFI_NOT_READY;
> +		goto out;
> +	}
> +	/* Fill export parameters */
> +	eth_hdr = (struct ethernet_hdr *)nt->receive_buffer[nt->rx_packet_idx];
> +	protlen = ntohs(eth_hdr->et_protlen);
> +	if (protlen == 0x8100) {
> +		hdr_size += 4;
> +		protlen = ntohs(*(u16 *)&nt->receive_buffer[nt->rx_packet_idx][hdr_size - 2]);
> +	}
> +	if (header_size)
> +		*header_size = hdr_size;
> +	if (dest_addr)
> +		memcpy(dest_addr, eth_hdr->et_dest, ARP_HLEN);
> +	if (src_addr)
> +		memcpy(src_addr, eth_hdr->et_src, ARP_HLEN);
> +	if (protocol)
> +		*protocol = protlen;
> +	if (*buffer_size < nt->receive_lengths[nt->rx_packet_idx]) {
> +		/* Packet doesn't fit, try again with bigger buffer */
> +		*buffer_size = nt->receive_lengths[nt->rx_packet_idx];
> +		ret = EFI_BUFFER_TOO_SMALL;
> +		goto out;
> +	}
> +	/* Copy packet */
> +	memcpy(buffer, nt->receive_buffer[nt->rx_packet_idx],
> +	       nt->receive_lengths[nt->rx_packet_idx]);
> +	*buffer_size = nt->receive_lengths[nt->rx_packet_idx];
> +	nt->rx_packet_idx = (nt->rx_packet_idx + 1) % ETH_PACKETS_BATCH_RECV;
> +	nt->rx_packet_num--;
> +	if (nt->rx_packet_num)
> +		nt->wait_for_packet->is_signaled = true;
> +	else
> +		this->int_status &= ~EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT;
> +out:
> +	return EFI_EXIT(ret);
> +}
> +
> +/**
> + * efi_net_push() - callback for received network packet
> + *
> + * This function is called when a network packet is received by eth_rx().
> + *
> + * @pkt:	network packet
> + * @len:	length
> + */
> +static void efi_net_push(void *pkt, int len)
> +{
> +	int rx_packet_next;
> +	struct efi_simple_network_extended_protocol *nt;
> +
> +	nt = current_network;
> +	if (!nt)
> +		return;
> +
> +	/* Check that we at least received an Ethernet header */
> +	if (len < sizeof(struct ethernet_hdr))
> +		return;
> +
> +	/* Check that the buffer won't overflow */
> +	if (len > PKTSIZE_ALIGN)
> +		return;
> +
> +	/* Can't store more than pre-alloced buffer */
> +	if (nt->rx_packet_num >= ETH_PACKETS_BATCH_RECV)
> +		return;
> +
> +	rx_packet_next = (nt->rx_packet_idx + nt->rx_packet_num) %
> +	    ETH_PACKETS_BATCH_RECV;
> +	memcpy(nt->receive_buffer[rx_packet_next], pkt, len);
> +	nt->receive_lengths[rx_packet_next] = len;
> +
> +	nt->rx_packet_num++;
> +}
> +
> +/**
> + * efi_network_timer_notify() - check if a new network packet has been received
> + *
> + * This notification function is called in every timer cycle.
> + *
> + * @event:	the event for which this notification function is registered
> + * @context:	event context - not used in this function
> + */
> +static void EFIAPI efi_network_timer_notify(struct efi_event *event,
> +					    void *context)
> +{
> +	struct efi_simple_network_extended_protocol *nt;
> +
> +	EFI_ENTRY("%p, %p", event, context);
> +
> +	nt = (struct efi_simple_network_extended_protocol *)context;
> +
> +	/*
> +	 * Some network drivers do not support calling eth_rx() before
> +	 * initialization.
> +	 */
> +	if (!nt || nt->net.mode->state != EFI_NETWORK_INITIALIZED)
> +		goto out;
> +
> +	current_network = nt;
> +
> +	if (!nt->rx_packet_num) {
> +		eth_set_dev(nt->dev);
> +		env_set("ethact", eth_get_name());
> +		push_packet = efi_net_push;
> +		eth_rx();
> +		push_packet = NULL;
> +		if (nt->rx_packet_num) {
> +			nt->net.int_status |=
> +				EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT;
> +			nt->wait_for_packet->is_signaled = true;
> +		}
> +	}
> +out:
> +	EFI_EXIT(EFI_SUCCESS);
> +}
> +
> +/**
> + * efi_simple_network_install() - install the EFI_SIMPLE_NETWORK_PROTOCOL
> + *
> + * @handle:	handle to install the protocol
> + * @dev:	net udevice
> + */
> +efi_status_t efi_simple_network_install(const efi_handle_t handle, struct udevice *dev)
> +{
> +	efi_status_t r;
> +	struct efi_simple_network_extended_protocol *simple_network;
> +	void *transmit_buffer = NULL;
> +	uchar **receive_buffer = NULL;
> +	size_t *receive_lengths;
> +	int i;
> +
> +	if (!dev) {
> +		/* No network device active, don't expose any */
> +		return EFI_SUCCESS;
> +	}
> +
> +	/* We only expose the "active" network device, so one is enough */
> +	simple_network = calloc(1, sizeof(*simple_network));
> +	if (!simple_network)
> +		goto out_of_resources;
> +
> +	simple_network->dev = dev;
> +
> +	/* Allocate an aligned transmit buffer */
> +	transmit_buffer = calloc(1, PKTSIZE_ALIGN + PKTALIGN);
> +	if (!transmit_buffer)
> +		goto out_of_resources;
> +	transmit_buffer = (void *)ALIGN((uintptr_t)transmit_buffer, PKTALIGN);
> +	simple_network->transmit_buffer = transmit_buffer;
> +
> +	/* Allocate a number of receive buffers */
> +	receive_buffer = calloc(ETH_PACKETS_BATCH_RECV,
> +				sizeof(*receive_buffer));
> +	if (!receive_buffer)
> +		goto out_of_resources;
> +	for (i = 0; i < ETH_PACKETS_BATCH_RECV; i++) {
> +		receive_buffer[i] = malloc(PKTSIZE_ALIGN);
> +		if (!receive_buffer[i])
> +			goto out_of_resources;
> +	}
> +	simple_network->receive_buffer = receive_buffer;
> +
> +	receive_lengths = calloc(ETH_PACKETS_BATCH_RECV,
> +				 sizeof(*receive_lengths));
> +	if (!receive_lengths)
> +		goto out_of_resources;
> +	simple_network->receive_lengths = receive_lengths;
> +
> +	simple_network->net.revision = EFI_SIMPLE_NETWORK_PROTOCOL_REVISION;
> +	simple_network->net.start = efi_net_start;
> +	simple_network->net.stop = efi_net_stop;
> +	simple_network->net.initialize = efi_net_initialize;
> +	simple_network->net.reset = efi_net_reset;
> +	simple_network->net.shutdown = efi_net_shutdown;
> +	simple_network->net.receive_filters = efi_net_receive_filters;
> +	simple_network->net.station_address = efi_net_station_address;
> +	simple_network->net.statistics = efi_net_statistics;
> +	simple_network->net.mcastiptomac = efi_net_mcastiptomac;
> +	simple_network->net.nvdata = efi_net_nvdata;
> +	simple_network->net.get_status = efi_net_get_status;
> +	simple_network->net.transmit = efi_net_transmit;
> +	simple_network->net.receive = efi_net_receive;
> +	simple_network->net.mode = &simple_network->net_mode;
> +	simple_network->net_mode.state = EFI_NETWORK_STOPPED;
> +	if (dev_get_plat(dev))
> +		memcpy(simple_network->net_mode.current_address.mac_addr,
> +		       ((struct eth_pdata *)dev_get_plat(dev))->enetaddr, 6);
> +	simple_network->net_mode.hwaddr_size = ARP_HLEN;
> +	simple_network->net_mode.media_header_size = ETHER_HDR_SIZE;
> +	simple_network->net_mode.max_packet_size = PKTSIZE;
> +	simple_network->net_mode.if_type = ARP_ETHER;
> +
> +	/*
> +	 * Create WaitForPacket event.
> +	 */
> +	r = efi_create_event(EVT_NOTIFY_WAIT, TPL_CALLBACK,
> +			     efi_network_timer_notify, NULL, NULL,
> +			     &simple_network->wait_for_packet);
> +	if (r != EFI_SUCCESS) {
> +		printf("ERROR: Failed to register network event\n");
> +		return r;
> +	}
> +	simple_network->net.wait_for_packet = simple_network->wait_for_packet;
> +	/*
> +	 * Create a timer event.
> +	 *
> +	 * The notification function is used to check if a new network packet
> +	 * has been received.
> +	 *
> +	 * iPXE is running at TPL_CALLBACK most of the time. Use a higher TPL.
> +	 */
> +	r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_NOTIFY,
> +			     efi_network_timer_notify, simple_network, NULL,
> +			     &simple_network->network_timer_event);
> +	if (r != EFI_SUCCESS) {
> +		printf("ERROR: Failed to register network event\n");
> +		return r;
> +	}
> +	/* Network is time critical, create event in every timer cycle */
> +	r = efi_set_timer(simple_network->network_timer_event, EFI_TIMER_PERIODIC, 0);
> +	if (r != EFI_SUCCESS) {
> +		printf("ERROR: Failed to set network timer\n");

If the installation fails, we should free whatever we allocated. So I 
guess we should jump to out_of_resources.

> +		return r;
> +	}
> +
> +	r = efi_add_protocol(handle, &efi_net_guid,
> +			     &simple_network->net);
> +	if (r != EFI_SUCCESS)

ditto

The missing part is deleting the event.

Best regards

Heinrich

> +		return r;
> +
> +	return EFI_SUCCESS;
> +out_of_resources:
> +	free(simple_network);
> +	free(transmit_buffer);
> +	if (receive_buffer)
> +		for (i = 0; i < ETH_PACKETS_BATCH_RECV; i++)
> +			free(receive_buffer[i]);
> +	free(receive_buffer);
> +	free(receive_lengths);
> +	printf("ERROR: Out of memory\n");
> +	return EFI_OUT_OF_RESOURCES;
> +}
> diff --git a/lib/efi_selftest/efi_selftest_snp.c b/lib/efi_selftest/efi_selftest_snp.c
> index b00c76c2f17..82ed3587d6f 100644
> --- a/lib/efi_selftest/efi_selftest_snp.c
> +++ b/lib/efi_selftest/efi_selftest_snp.c
> @@ -17,6 +17,8 @@
>   #include <efi_selftest.h>
>   #include <net.h>
>   
> +static const efi_guid_t efi_net_guid = EFI_SIMPLE_NETWORK_PROTOCOL_GUID;
> +
>   /*
>    * MAC address for broadcasts
>    */
> @@ -65,7 +67,7 @@ struct dhcp {
>   } __packed;
>   
>   static struct efi_boot_services *boottime;
> -static struct efi_simple_network *net;
> +static struct efi_simple_network_protocol *net;
>   static struct efi_event *timer;
>   /* IP packet ID */
>   static unsigned int net_ip_id;



More information about the U-Boot mailing list