[PATCH 11/16] efi_loader: efi_net: Add http, ip4_config2, and pxe on demand

Heinrich Schuchardt xypron.glpk at gmx.de
Tue Apr 15 11:06:08 CEST 2025


On 11.03.25 17:47, Adriano Cordova wrote:
> Add http and ip4_config2 only if they are not already provided
> in the handle. The current use of pxe is only to store an ip
> address if a dhcp ack is received, this address is used by grub.
> Add pxe only if a dhcp ack is actually received.
>
> Signed-off-by: Adriano Cordova <adriano.cordova at canonical.com>
> ---
>   include/efi_loader.h          |   8 +-
>   lib/efi_loader/efi_http.c     |  30 ++++-
>   lib/efi_loader/efi_ipconfig.c |  36 +++++-
>   lib/efi_loader/efi_net.c      | 203 +++++++++++++++++++++++-----------
>   4 files changed, 199 insertions(+), 78 deletions(-)
>
> diff --git a/include/efi_loader.h b/include/efi_loader.h
> index 35f500fd97d..ad234dbe6b3 100644
> --- a/include/efi_loader.h
> +++ b/include/efi_loader.h
> @@ -677,10 +677,14 @@ efi_status_t efi_net_init(void);
>   efi_status_t efi_net_do_start(void);
>   /* Called by efi_net_register to make the ip4 config2 protocol available */
>   efi_status_t efi_ipconfig_register(const efi_handle_t handle,
> -				   struct efi_ip4_config2_protocol *ip4config);
> +				   struct efi_ip4_config2_protocol **ip4config);
> +efi_status_t efi_ipconfig_unregister(const efi_handle_t handle,
> +				     struct efi_ip4_config2_protocol *ip4config);
>   /* Called by efi_net_register to make the http protocol available */
>   efi_status_t efi_http_register(const efi_handle_t handle,
> -			       struct efi_service_binding_protocol *http_service_binding);
> +			       struct efi_service_binding_protocol **http_service_binding);
> +efi_status_t efi_http_unregister(const efi_handle_t handle,
> +				 struct efi_service_binding_protocol *http_service_binding);
>   /* Called by bootefi to make the watchdog available */
>   efi_status_t efi_watchdog_register(void);
>   efi_status_t efi_initrd_register(void);
> diff --git a/lib/efi_loader/efi_http.c b/lib/efi_loader/efi_http.c
> index 189317fe2d2..dcef875f5b5 100644
> --- a/lib/efi_loader/efi_http.c
> +++ b/lib/efi_loader/efi_http.c
> @@ -486,23 +486,45 @@ static efi_status_t EFIAPI efi_http_service_binding_destroy_child(
>    *
>    */
>   efi_status_t efi_http_register(const efi_handle_t handle,
> -			       struct efi_service_binding_protocol *http_service_binding)
> +			       struct efi_service_binding_protocol **http_service_binding)
>   {
>   	efi_status_t r = EFI_SUCCESS;
>
> +	r = efi_allocate_pool(EFI_LOADER_DATA, sizeof(**http_service_binding),
> +			      (void **)http_service_binding);
> +	if (r != EFI_SUCCESS)
> +		return r;
>   	r = efi_add_protocol(handle, &efi_http_service_binding_guid,
> -			     http_service_binding);
> +			     *http_service_binding);
>   	if (r != EFI_SUCCESS)
>   		goto failure_to_add_protocol;
>
> -	http_service_binding->create_child = efi_http_service_binding_create_child;
> -	http_service_binding->destroy_child = efi_http_service_binding_destroy_child;
> +	(*http_service_binding)->create_child = efi_http_service_binding_create_child;
> +	(*http_service_binding)->destroy_child = efi_http_service_binding_destroy_child;
>
>   	return EFI_SUCCESS;
>   failure_to_add_protocol:
>   	return r;
>   }
>
> +/**
> + * efi_http_unregister() - unregister the http protocol
> + *
> + */
> +efi_status_t efi_http_unregister(const efi_handle_t handle,
> +				 struct efi_service_binding_protocol *http_service_binding)
> +{
> +	efi_status_t r = EFI_SUCCESS;
> +
> +	r = efi_uninstall_protocol(handle, &efi_http_service_binding_guid,
> +				   http_service_binding, true);
> +	if (r != EFI_SUCCESS)
> +		return r;
> +	efi_free_pool(http_service_binding);
> +
> +	return EFI_SUCCESS;
> +}
> +
>   enum efi_http_status_code efi_u32_to_httpstatus(u32 status)
>   {
>   	switch (status) {
> diff --git a/lib/efi_loader/efi_ipconfig.c b/lib/efi_loader/efi_ipconfig.c
> index 9f51f77fa9a..18f659f4ed1 100644
> --- a/lib/efi_loader/efi_ipconfig.c
> +++ b/lib/efi_loader/efi_ipconfig.c
> @@ -194,12 +194,18 @@ static efi_status_t EFIAPI efi_ip4_config2_unregister_notify(struct efi_ip4_conf
>    *
>    */
>   efi_status_t efi_ipconfig_register(const efi_handle_t handle,
> -				   struct efi_ip4_config2_protocol *ip4config)
> +				   struct efi_ip4_config2_protocol **ip4config)
>   {
>   	efi_status_t r = EFI_SUCCESS;
>
> +	if (!ip4config)
> +		return EFI_INVALID_PARAMETER;
> +
> +	r = efi_allocate_pool(EFI_LOADER_DATA, sizeof(**ip4config), (void **)ip4config);
> +	if (r != EFI_SUCCESS)
> +		return r;
>   	r = efi_add_protocol(handle, &efi_ip4_config2_guid,
> -			     ip4config);
> +			     *ip4config);
>   	if (r != EFI_SUCCESS) {
>   		log_err("ERROR: Failure to add protocol\n");
>   		return r;
> @@ -207,10 +213,28 @@ efi_status_t efi_ipconfig_register(const efi_handle_t handle,
>
>   	memcpy(current_mac_addr, eth_get_ethaddr(), 6);
>
> -	ip4config->set_data = efi_ip4_config2_set_data;
> -	ip4config->get_data = efi_ip4_config2_get_data;
> -	ip4config->register_data_notify = efi_ip4_config2_register_notify;
> -	ip4config->unregister_data_notify = efi_ip4_config2_unregister_notify;
> +	(*ip4config)->set_data = efi_ip4_config2_set_data;
> +	(*ip4config)->get_data = efi_ip4_config2_get_data;
> +	(*ip4config)->register_data_notify = efi_ip4_config2_register_notify;
> +	(*ip4config)->unregister_data_notify = efi_ip4_config2_unregister_notify;
> +
> +	return EFI_SUCCESS;
> +}
> +
> +/**
> + * efi_ipconfig_unregister() - unregister the ip4_config2 protocol
> + *
> + */
> +efi_status_t efi_ipconfig_unregister(const efi_handle_t handle,
> +				     struct efi_ip4_config2_protocol *ip4config)
> +{
> +	efi_status_t r = EFI_SUCCESS;
> +
> +	r = efi_uninstall_protocol(handle, &efi_ip4_config2_guid,
> +				   ip4config, true);
> +	if (r != EFI_SUCCESS)
> +		return r;
> +	efi_free_pool(ip4config);
>
>   	return EFI_SUCCESS;
>   }
> diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c
> index fd43eec4c03..5a00e7a570c 100644
> --- a/lib/efi_loader/efi_net.c
> +++ b/lib/efi_loader/efi_net.c
> @@ -19,6 +19,7 @@
>
>   #include <efi_loader.h>
>   #include <dm.h>
> +#include <dm/lists.h>
>   #include <linux/sizes.h>
>   #include <malloc.h>
>   #include <vsprintf.h>
> @@ -31,6 +32,8 @@
>   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;
> +static const efi_guid_t efi_ip4_config2_guid = EFI_IP4_CONFIG2_PROTOCOL_GUID;
> +static const efi_guid_t efi_http_service_binding_guid = EFI_HTTP_SERVICE_BINDING_PROTOCOL_GUID;
>
>   struct dp_entry {
>   	struct efi_device_path *net_dp;
> @@ -93,10 +96,10 @@ struct efi_net_obj {
>   	struct efi_pxe_base_code_protocol pxe;
>   	struct efi_pxe_mode pxe_mode;
>   #if IS_ENABLED(CONFIG_EFI_IP4_CONFIG2_PROTOCOL)
> -	struct efi_ip4_config2_protocol ip4_config2;
> +	struct efi_ip4_config2_protocol *ip4_config2;
>   #endif
>   #if IS_ENABLED(CONFIG_EFI_HTTP_PROTOCOL)
> -	struct efi_service_binding_protocol http_service_binding;
> +	struct efi_service_binding_protocol *http_service_binding;
>   #endif
>   	void *new_tx_packet;
>   	void *transmit_buffer;
> @@ -761,6 +764,8 @@ out:
>   	return EFI_EXIT(ret);
>   }
>
> +efi_status_t efi_net_add_pxe(struct efi_net_obj *netobj);
> +
>   /**
>    * efi_net_set_dhcp_ack() - take note of a selected DHCP IP address
>    *
> @@ -795,9 +800,12 @@ void efi_net_set_dhcp_ack(void *pkt, int len)
>   	next_dhcp_entry++;
>   	next_dhcp_entry %= MAX_NUM_DHCP_ENTRIES;
>
> -	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;
> +	if (efi_obj_list_initialized == EFI_SUCCESS) {
> +		for (i = 0; i < MAX_EFI_NET_OBJS; i++) {
> +			if (net_objs[i] && net_objs[i]->dev == dev) {
> +				efi_net_add_pxe(net_objs[i]);
> +				break;
> +			}
>   		}
>   	}
>   }
> @@ -1069,6 +1077,52 @@ static struct efi_device_path *efi_netobj_get_dp(struct efi_net_obj *netobj)
>   	return NULL;
>   }
>
> +/**
> + * efi_net_add_pxe() - install the pxe protocol to a netobj
> + *
> + * @netobj:	pointer to efi_net_obj
> + * Return:	status code
> + */
> +efi_status_t efi_net_add_pxe(struct efi_net_obj *netobj)
> +{
> +	efi_status_t r = EFI_SUCCESS;
> +	struct efi_handler *phandler;
> +	int i, j;
> +
> +	r = efi_search_protocol(netobj->handle, &efi_pxe_base_code_protocol_guid, &phandler);
> +	if (r == EFI_SUCCESS)
> +		return r;
> +
> +	i = (next_dhcp_entry + MAX_NUM_DHCP_ENTRIES - 1) % MAX_NUM_DHCP_ENTRIES;
> +	for (j = 0; netobj->dev && dhcp_cache[i].is_valid && j < MAX_NUM_DHCP_ENTRIES;
> +	     i = (i + MAX_NUM_DHCP_ENTRIES - 1) % MAX_NUM_DHCP_ENTRIES, j++) {
> +		if (netobj->dev == dhcp_cache[i].dev) {
> +			netobj->pxe_mode.dhcp_ack = *dhcp_cache[i].dhcp_ack;
> +			r = efi_add_protocol(netobj->handle, &efi_pxe_base_code_protocol_guid,
> +					     &netobj->pxe);
> +			if (r != EFI_SUCCESS)
> +				return r;
> +			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;
> +			break;
> +		}
> +	}
> +
> +	return EFI_SUCCESS;
> +}
> +
>   /**
>    * efi_netobj_start() - start an efi net device
>    *
> @@ -1102,15 +1156,8 @@ efi_status_t efi_netobj_start(struct efi_net_obj *netobj)
>   	if (r != EFI_SUCCESS)
>   		return r;
>   set_addr:
> -#ifdef 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, netobj->dev);
> -#endif
>
>   	return 0;
>   }
> @@ -1147,16 +1194,20 @@ static int efi_netobj_init(struct efi_net_obj *netobj)
>   {
>   	efi_status_t r;
>   	struct udevice *dev;
> +	struct efi_handler *phandler;
>   	void *transmit_buffer = NULL;
>   	uchar **receive_buffer = NULL;
>   	size_t *receive_lengths = NULL;
> -	int i, j;
> +	int i;
>
> -	if (!netobj || !netobj->net || efi_netobj_is_active(netobj))
> +	if (!netobj || !netobj->net)
>   		return 0;
>
>   	dev = netobj->dev;
>
> +	if (efi_netobj_is_active(netobj))
> +		goto set_timers;
> +
>   	if (!netobj->net_mode)
>   		netobj->net_mode = calloc(1, sizeof(*netobj->net_mode));
>   	if (!netobj->net_mode)
> @@ -1203,11 +1254,6 @@ static int efi_netobj_init(struct efi_net_obj *netobj)
>   			netobj->net);
>   	if (r != EFI_SUCCESS)
>   		goto failure_to_add_protocol;
> -
> -	r = efi_add_protocol(netobj->handle, &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;
> @@ -1233,38 +1279,15 @@ static int efi_netobj_init(struct efi_net_obj *netobj)
>   	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;
> +	r = efi_net_add_pxe(netobj);
> +	if (r != EFI_SUCCESS)
> +		return -1;
>
>   	r = EFI_CALL(efi_connect_controller(netobj->handle, NULL, NULL, 0));
>   	if (r != EFI_SUCCESS)
>   		return -1;
>
> -	/*
> -	 * Scan dhcp entries for one corresponding
> -	 * to this udevice, from newest to oldest
> -	 */
> -	i = (next_dhcp_entry + MAX_NUM_DHCP_ENTRIES - 1) % MAX_NUM_DHCP_ENTRIES;
> -	for (j = 0; dev && 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;
> -			break;
> -		}
> -	}
> -
> +set_timers:
>   	/*
>   	 * Create WaitForPacket event.
>   	 */
> @@ -1299,16 +1322,26 @@ static int efi_netobj_init(struct efi_net_obj *netobj)
>   	}
>
>   #if IS_ENABLED(CONFIG_EFI_IP4_CONFIG2_PROTOCOL)
> +	netobj->ip4_config2 = NULL;
> +	r = efi_search_protocol(netobj->handle, &efi_ip4_config2_guid, &phandler);
> +	if (r == EFI_SUCCESS)
> +		goto http;
>   	r = efi_ipconfig_register(netobj->handle, &netobj->ip4_config2);
>   	if (r != EFI_SUCCESS)
>   		goto failure_to_add_protocol;
>   #endif
>
> +http:

This does not build if CONFIG_EFI_IP4_CONFIG2_PROTOCOL=n:

+lib/efi_loader/efi_net.c:1337:1: error: label 'http' defined but not
used [-Werror=unused-label]
+ 1337 | http:
+      | ^~~~

See https://source.denx.de/u-boot/custodians/u-boot-efi/-/jobs/1101680

>   #ifdef CONFIG_EFI_HTTP_PROTOCOL
> +	netobj->http_service_binding = NULL;
> +	r = efi_search_protocol(netobj->handle, &efi_http_service_binding_guid, &phandler);
> +	if (r == EFI_SUCCESS)
> +		goto out;
>   	r = efi_http_register(netobj->handle, &netobj->http_service_binding);
>   	if (r != EFI_SUCCESS)
>   		goto failure_to_add_protocol;
>   #endif
> +out:

This does not build if CONFIG_EFI_HTTP_PROTOCOL=n

+lib/efi_loader/efi_net.c: In function 'efi_netobj_init':
+lib/efi_loader/efi_net.c:1347:1: error: label 'out' defined but not
used [-Werror=unused-label]
+ 1347 | out:

Best regards

Heinrich

>   	return 0;
>   failure_to_add_protocol:
>   	printf("ERROR: Failure to add protocol\n");
> @@ -1428,6 +1461,10 @@ struct efi_net_obj *efi_netobj_alloc(efi_handle_t handle,
>   int efi_net_register(void *ctx, struct event *event)
>   {
>   	struct udevice *dev;
> +	struct driver *drv;
> +	struct efi_netdev_plat *plat;
> +	efi_handle_t handle;
> +	struct efi_simple_network *net;
>   	enum uclass_id id;
>   	struct efi_net_obj *netobj;
>   	int i, r;
> @@ -1449,17 +1486,24 @@ int efi_net_register(void *ctx, struct event *event)
>   		}
>   	}
>
> -	netobj = efi_netobj_alloc(NULL, NULL, dev);
> +	handle = NULL;
> +	net = NULL;
> +	drv = lists_driver_lookup_name("efi_netdev");
> +	if (drv && dev->driver == drv) {
> +		plat = dev_get_plat(dev);
> +		handle = plat->handle;
> +		net = plat->snp;
> +	}
> +
> +	netobj = efi_netobj_alloc(handle, net, dev);
>
>   	if (!netobj)
>   		return -1;
>
>   	if (efi_obj_list_initialized == EFI_SUCCESS) {
> -		if (!efi_netobj_is_active(netobj)) {
> -			r = efi_netobj_init(netobj);
> -			if (r)
> -				return -1;
> -		}
> +		r = efi_netobj_init(netobj);
> +		if (r)
> +			return -1;
>   	}
>
>   	return 0;
> @@ -1476,7 +1520,9 @@ int efi_net_register(void *ctx, struct event *event)
>   int efi_net_unregister(void *ctx, struct event *event)
>   {
>   	efi_status_t ret = EFI_SUCCESS;
> +	int r;
>   	struct udevice *dev;
> +	struct driver *drv;
>   	enum uclass_id id;
>   	struct efi_net_obj *netobj;
>   	struct efi_handler *phandler;
> @@ -1501,18 +1547,22 @@ int efi_net_unregister(void *ctx, struct event *event)
>   		}
>   	}
>
> -	if (!netobj)
> +	if (!netobj || !efi_netobj_is_active(netobj))
>   		return 0;
>
> -	if (efi_netobj_is_active(netobj)) {
> +	drv = lists_driver_lookup_name("efi_netdev");
> +	if (!drv) {
> +		log_err("Cannot find driver 'efi_netdev'\n");
> +		return -1;
> +	}
> +
> +	if (drv != dev->driver) {
>   		ret = EFI_CALL(efi_disconnect_controller(netobj->handle, NULL, NULL));
>   		if (ret != EFI_SUCCESS)
>   			return -1;
> -
>   		ret = EFI_CALL(efi_close_event(netobj->wait_for_packet));
>   		if (ret != EFI_SUCCESS)
>   			return -1;
> -
>   		ret = EFI_CALL(efi_close_event(netobj->network_timer_event));
>   		if (ret != EFI_SUCCESS)
>   			return -1;
> @@ -1524,22 +1574,43 @@ int efi_net_unregister(void *ctx, struct event *event)
>   		if (phandler && phandler->protocol_interface)
>   			interface = phandler->protocol_interface;
>
> +		if (netobj->handle->dev) {
> +			r = efi_unlink_dev(netobj->handle);
> +			if (r)
> +				return -1;
> +		}
> +	}
> +
> +#if IS_ENABLED(CONFIG_EFI_IP4_CONFIG2_PROTOCOL)
> +	if (netobj->ip4_config2) {
> +		r = efi_ipconfig_unregister(netobj->handle, netobj->ip4_config2);
> +		if (r != EFI_SUCCESS)
> +			return -1;
> +		netobj->ip4_config2 = NULL;
> +	}
> +#endif
> +
> +#ifdef CONFIG_EFI_HTTP_PROTOCOL
> +	if (netobj->http_service_binding) {
> +		r = efi_http_unregister(netobj->handle, netobj->http_service_binding);
> +		if (r != EFI_SUCCESS)
> +			return -1;
> +		netobj->http_service_binding = NULL;
> +	}
> +#endif
> +
> +	if (drv != dev->driver) {
>   		ret = efi_delete_handle(netobj->handle);
>   		if (ret != EFI_SUCCESS)
>   			return -1;
>
>   		efi_free_pool(interface);
> +		if (netobj->net)
> +			free(netobj->net);
> +		if (netobj->net_mode)
> +			free(netobj->net_mode);
>   	}
>
> -	if (netobj->net) {
> -		if (netobj->net->mode)
> -			free(netobj->net->mode);
> -		free(netobj->net);
> -	}
> -
> -	if (netobj->net_mode)
> -		free(netobj->net_mode);
> -
>   	// Mark as free in the list
>   	netobj->handle = NULL;
>   	netobj->dev = NULL;
> @@ -1891,7 +1962,7 @@ efi_status_t efi_net_do_request(u8 *url, enum efi_http_method method, void **buf
>   	// Set corresponding udevice
>   	dev = NULL;
>   	for (i = 0; i < MAX_EFI_NET_OBJS; i++) {
> -		if (net_objs[i] && &net_objs[i]->http_service_binding == parent)
> +		if (net_objs[i] && net_objs[i]->http_service_binding == parent)
>   			dev = net_objs[i]->dev;
>   	}
>   	if (!dev)



More information about the U-Boot mailing list