[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