[PATCH 00/21] EFI driver to support multiple network interfaces

This series introduces an EFI driver to manage EFI net objects. The main functionalities implemented here are the following: - Support multiple efi_net_obj - Manage efi_net_obj with EFI driver - Support calling [dis]connect_controller with a handle having a snp protocol installed. The patches can be found at https://github.com/0n41rd4/u-boot/tree/efi-net-driver on top of upstream/next.
Best regards, Adriano
Adriano Cordova (21): legacy-net: wget: fix wget_info handling after new tcp legacy stack efi_loader: expose symbols to be used by the EFI network stack efi_loader: efi_setup: Add efi_start_obj_list() to efi_setup.c efi_loader: efi_net: Add efi_net_do_start() to efi_net.c efi_loader: efi_device_path: Let network device paths receive the net udevice as an argument efi_loader: efi_net: Add support for multiple efi_net_obj efi_driver: efi_net: Add EFI network device driver efi_loader: efi_net: Reuse efi_net_obj's efi_loader: efi_net: Let efi_net_obj store pointers to efi_simple_network, efi_simple_network_mode, and the efi handle efi_net: efi_loader: Add efi_netobj_alloc to allocate an efi_net_obj efi_loader: efi_net: Manage EFI net objects with efi_[dis]connect_controller eth_uclass: Add eth_create_device to create a net udevice efi_driver: efi_net: UCLASS_ETH "efi_netdev" U-Boot driver efi_driver: efi_uclass: Add unbind method to efi_driver_ops efi_driver: efi_net: Add efi_net_unbind_drv method to efi_net driver efi_loader: efi_net: Add functions to [un]register http and ip4_config2 protocols, and add pxe protocol on demand efi_loader: efi_net: add snp_owner field to efi_net_obj struct efi_selftest: Add selftest for counting EFI netdevices py/tests: test_efi_selftest: Add test for counting EFI netdevices py/tests: test_efi_selftest: Add test for removing EFI netdevices efi_selftest: Add test for efi_netdev driver
include/efi_driver.h | 4 + include/efi_loader.h | 80 +- include/net-common.h | 3 + lib/efi_driver/Makefile | 1 + lib/efi_driver/efi_net_device.c | 289 ++++++ lib/efi_driver/efi_uclass.c | 6 +- lib/efi_loader/efi_bootbin.c | 3 +- lib/efi_loader/efi_boottime.c | 23 +- lib/efi_loader/efi_device_path.c | 23 +- lib/efi_loader/efi_http.c | 36 +- lib/efi_loader/efi_ipconfig.c | 41 +- lib/efi_loader/efi_net.c | 1063 ++++++++++++++++---- lib/efi_loader/efi_setup.c | 27 +- lib/efi_selftest/Makefile | 2 +- lib/efi_selftest/efi_selftest_net_device.c | 65 ++ lib/efi_selftest/efi_selftest_net_driver.c | 310 ++++++ lib/efi_selftest/efi_selftest_snp.c | 1 - net/eth-uclass.c | 32 + net/wget.c | 18 +- test/py/tests/test_efi_selftest.py | 45 + 20 files changed, 1810 insertions(+), 262 deletions(-) create mode 100644 lib/efi_driver/efi_net_device.c create mode 100644 lib/efi_selftest/efi_selftest_net_device.c create mode 100644 lib/efi_selftest/efi_selftest_net_driver.c

Check wget_info->buffer_size for overflow and do not clean the wget_info struct on failure, let the owner of the struct handle the error. The latter is necessary, e.g., for when a request fails because the provided buffer was too small.
Signed-off-by: Adriano Cordova adriano.cordova@canonical.com --- net/wget.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-)
diff --git a/net/wget.c b/net/wget.c index 0b082c6194..f51cd342df 100644 --- a/net/wget.c +++ b/net/wget.c @@ -53,6 +53,9 @@ static inline int store_block(uchar *src, unsigned int offset, unsigned int len) ulong store_addr = image_load_addr + offset; uchar *ptr;
+ // Avoid overflow + if (wget_info->buffer_size && wget_info->buffer_size < offset + len) + return -1; if (CONFIG_IS_ENABLED(LMB) && wget_info->set_bootdev) { if (store_addr < image_load_addr || lmb_read_check(store_addr, len)) { @@ -98,12 +101,6 @@ static void tcp_stream_on_closed(struct tcp_stream *tcp) net_set_state(wget_loop_state); if (wget_loop_state != NETLOOP_SUCCESS) { net_boot_file_size = 0; - if (wget_info->status_code == HTTP_STATUS_OK) { - wget_info->status_code = HTTP_STATUS_BAD; - wget_info->hdr_cont_len = 0; - if (wget_info->headers) - wget_info->headers[0] = 0; - } printf("\nwget: Transfer Fail, TCP status - %d\n", tcp->status); return; } @@ -212,6 +209,11 @@ static void tcp_stream_on_rcv_nxt_update(struct tcp_stream *tcp, u32 rx_bytes) "wget: Connected Len %lu\n", content_length); wget_info->hdr_cont_len = content_length; + if (wget_info->buffer_size && wget_info->buffer_size < wget_info->hdr_cont_len) { + tcp_stream_reset(tcp); + goto end; + } + }
net_boot_file_size = rx_bytes - http_hdr_size; @@ -227,7 +229,9 @@ static int tcp_stream_rx(struct tcp_stream *tcp, u32 rx_offs, void *buf, int len if ((max_rx_pos == (u32)(-1)) || (max_rx_pos < rx_offs + len - 1)) max_rx_pos = rx_offs + len - 1;
- store_block(buf, rx_offs - http_hdr_size, len); + // Avoid overflow + if (store_block(buf, rx_offs - http_hdr_size, len) < 0) + return -1;
return len; }

The following symbols are exposed: - efi_reinstall_protocol_interface This is done so that the device path protocol interface of the network device can be changed internally by u-boot when a new bootfile gets downloaded. - eth_set_dev To support multiple network udevices - efi_close_event This comes in preparation to support unregistering an EFI network device from the EFI network stack when the underlying U-boot device gets removed - efi_[dis]connect_controller The EFI network driver uses ConnectController to add a NIC to the EFI network stack. - efi_uninstall_protocol_interface connect_controler for the efi network driver can install protocols, which need to be uninstalled in disconnect_controller - EFI_SIMPLE_NETWORK_PROTOCOL_GUID
Signed-off-by: Adriano Cordova adriano.cordova@canonical.com --- include/efi_loader.h | 22 ++++++++++++++++++++++ include/net-common.h | 1 + lib/efi_loader/efi_boottime.c | 23 +++++++++++------------ lib/efi_loader/efi_net.c | 2 +- lib/efi_selftest/efi_selftest_snp.c | 1 - 5 files changed, 35 insertions(+), 14 deletions(-)
diff --git a/include/efi_loader.h b/include/efi_loader.h index 9afbec35eb..626d2bbb1a 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -308,6 +308,8 @@ 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; @@ -711,6 +713,15 @@ efi_status_t efi_search_protocol(const efi_handle_t handle, efi_status_t efi_add_protocol(const efi_handle_t handle, const efi_guid_t *protocol, void *protocol_interface); +/* Uninstall new protocol on a handle */ +efi_status_t efi_uninstall_protocol + (efi_handle_t handle, const efi_guid_t *protocol, + void *protocol_interface, bool preserve); +/* Reinstall a protocol on a handle */ +efi_status_t EFIAPI efi_reinstall_protocol_interface( + efi_handle_t handle, + const efi_guid_t *protocol, + void *old_interface, void *new_interface); /* Open protocol */ efi_status_t efi_protocol_open(struct efi_handler *handler, void **protocol_interface, void *agent_handle, @@ -721,6 +732,15 @@ efi_status_t EFIAPI efi_install_multiple_protocol_interfaces(efi_handle_t *handle, ...); efi_status_t EFIAPI efi_uninstall_multiple_protocol_interfaces(efi_handle_t handle, ...); +/* Connect and disconnect controller */ +efi_status_t EFIAPI efi_connect_controller(efi_handle_t controller_handle, + efi_handle_t *driver_image_handle, + struct efi_device_path *remain_device_path, + bool recursive); +efi_status_t EFIAPI efi_disconnect_controller( + efi_handle_t controller_handle, + efi_handle_t driver_image_handle, + efi_handle_t child_handle); /* Get handles that support a given protocol */ efi_status_t EFIAPI efi_locate_handle_buffer( enum efi_locate_search_type search_type, @@ -741,6 +761,8 @@ efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl, void *context), void *notify_context, const efi_guid_t *group, struct efi_event **event); +/* Call this to close an event */ +efi_status_t EFIAPI efi_close_event(struct efi_event *event); /* Call this to set a timer */ efi_status_t efi_set_timer(struct efi_event *event, enum efi_timer_delay type, uint64_t trigger_time); diff --git a/include/net-common.h b/include/net-common.h index 29d31f3726..1d507b13b0 100644 --- a/include/net-common.h +++ b/include/net-common.h @@ -291,6 +291,7 @@ struct eth_ops { #define eth_get_ops(dev) ((struct eth_ops *)(dev)->driver->ops)
struct udevice *eth_get_dev(void); /* get the current device */ +void eth_set_dev(struct udevice *dev); /* set a device */ unsigned char *eth_get_ethaddr(void); /* get the current device MAC */ int eth_rx(void); /* Check for received packets */ void eth_halt(void); /* stop SCC */ diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 080e7f78ae..79ebe94889 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -58,9 +58,9 @@ static efi_handle_t current_image; static volatile gd_t *efi_gd, *app_gd; #endif
-static efi_status_t efi_uninstall_protocol - (efi_handle_t handle, const efi_guid_t *protocol, - void *protocol_interface, bool preserve); +efi_status_t efi_uninstall_protocol + (efi_handle_t handle, const efi_guid_t *protocol, + void *protocol_interface, bool preserve);
/* 1 if inside U-Boot code, 0 if inside EFI payload code */ static int entry_count = 1; @@ -98,12 +98,11 @@ const efi_guid_t efi_guid_load_file2_protocol = EFI_LOAD_FILE2_PROTOCOL_GUID; /* GUID of the SMBIOS table */ const efi_guid_t smbios_guid = SMBIOS_TABLE_GUID;
-static efi_status_t EFIAPI efi_disconnect_controller( +efi_status_t EFIAPI efi_disconnect_controller( efi_handle_t controller_handle, efi_handle_t driver_image_handle, efi_handle_t child_handle);
-static efi_status_t EFIAPI efi_connect_controller(efi_handle_t controller_handle, efi_handle_t *driver_image_handle, struct efi_device_path *remain_device_path, @@ -1037,7 +1036,7 @@ static efi_status_t EFIAPI efi_signal_event_ext(struct efi_event *event) * * Return: status code */ -static efi_status_t EFIAPI efi_close_event(struct efi_event *event) +efi_status_t EFIAPI efi_close_event(struct efi_event *event) { struct efi_register_notify_event *item, *next;
@@ -1378,9 +1377,9 @@ static efi_status_t efi_disconnect_all_drivers * * Return: status code */ -static efi_status_t efi_uninstall_protocol - (efi_handle_t handle, const efi_guid_t *protocol, - void *protocol_interface, bool preserve) +efi_status_t efi_uninstall_protocol + (efi_handle_t handle, const efi_guid_t *protocol, + void *protocol_interface, bool preserve) { struct efi_handler *handler; struct efi_open_protocol_info_item *item; @@ -3665,7 +3664,7 @@ static efi_status_t efi_connect_single_controller( * * Return: status code */ -static efi_status_t EFIAPI efi_connect_controller( +efi_status_t EFIAPI efi_connect_controller( efi_handle_t controller_handle, efi_handle_t *driver_image_handle, struct efi_device_path *remain_device_path, @@ -3733,7 +3732,7 @@ out: * * Return: status code */ -static efi_status_t EFIAPI efi_reinstall_protocol_interface( +efi_status_t EFIAPI efi_reinstall_protocol_interface( efi_handle_t handle, const efi_guid_t *protocol, void *old_interface, void *new_interface) { @@ -3844,7 +3843,7 @@ static efi_status_t efi_get_child_controllers( * * Return: status code */ -static efi_status_t EFIAPI efi_disconnect_controller( +efi_status_t EFIAPI efi_disconnect_controller( efi_handle_t controller_handle, efi_handle_t driver_image_handle, efi_handle_t child_handle) diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c index 67593ef50c..918d43c70f 100644 --- a/lib/efi_loader/efi_net.c +++ b/lib/efi_loader/efi_net.c @@ -22,7 +22,7 @@ #include <vsprintf.h> #include <net.h>
-static const efi_guid_t efi_net_guid = EFI_SIMPLE_NETWORK_PROTOCOL_GUID; +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 struct efi_pxe_packet *dhcp_ack; diff --git a/lib/efi_selftest/efi_selftest_snp.c b/lib/efi_selftest/efi_selftest_snp.c index 15af8d3e18..b00c76c2f1 100644 --- a/lib/efi_selftest/efi_selftest_snp.c +++ b/lib/efi_selftest/efi_selftest_snp.c @@ -67,7 +67,6 @@ struct dhcp { static struct efi_boot_services *boottime; static struct efi_simple_network *net; static struct efi_event *timer; -static const efi_guid_t efi_net_guid = EFI_SIMPLE_NETWORK_PROTOCOL_GUID; /* IP packet ID */ static unsigned int net_ip_id;

The coomand bootefi calls efi_init_obj_list to do the efi set up before launching an .efi payload, but efi_init_obj_list is called only once. There are some initializations which depend on the environment and should be done each time a payload gets launched and not only once. A motivation for this changes is the following order of events:
1. Launch an EFI application (e.g. bootefi hello) 2. Change the ip address 3. Launch another application which uses the pxe protocol
As the EFI pxe protocol was initialized when the handles for efi net were created in 1., the ip was hardcoded there.
In this example, another possibility would be to make a callback for ip address changes to go all the way up to efi_net.
Signed-off-by: Adriano Cordova adriano.cordova@canonical.com --- lib/efi_loader/efi_setup.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-)
diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c index aa59bc7779..164586742a 100644 --- a/lib/efi_loader/efi_setup.c +++ b/lib/efi_loader/efi_setup.c @@ -12,6 +12,7 @@ #include <log.h> #include <asm-generic/unaligned.h>
+#define OBJ_LIST_INITIALIZED 0 #define OBJ_LIST_NOT_INITIALIZED 1
efi_status_t efi_obj_list_initialized = OBJ_LIST_NOT_INITIALIZED; @@ -208,6 +209,18 @@ out: return -1; }
+/** + * efi_start_obj_list() - Start EFI object list + * + * Return: status code + */ +static efi_status_t efi_start_obj_list(void) +{ + efi_status_t ret = EFI_SUCCESS; + + return ret; +} + /** * efi_init_obj_list() - Initialize and populate EFI object list * @@ -217,7 +230,9 @@ efi_status_t efi_init_obj_list(void) { efi_status_t ret = EFI_SUCCESS;
- /* Initialize once only */ + /* Initialize only once, but start every time if correctly initialized*/ + if (efi_obj_list_initialized == OBJ_LIST_INITIALIZED) + return efi_start_obj_list(); if (efi_obj_list_initialized != OBJ_LIST_NOT_INITIALIZED) return efi_obj_list_initialized;
@@ -349,6 +364,10 @@ efi_status_t efi_init_obj_list(void) if (IS_ENABLED(CONFIG_EFI_CAPSULE_ON_DISK) && !IS_ENABLED(CONFIG_EFI_CAPSULE_ON_DISK_EARLY)) ret = efi_launch_capsules(); + if (ret != EFI_SUCCESS) + goto out; + + ret = efi_start_obj_list(); out: efi_obj_list_initialized = ret; return ret;

This gets called each time a payload is to get executed by bootefi. For now this only updates the PXE IP address.
Signed-off-by: Adriano Cordova adriano.cordova@canonical.com --- include/efi_loader.h | 1 + lib/efi_loader/efi_net.c | 31 ++++++++++++++++++++++++------- lib/efi_loader/efi_setup.c | 3 +++ 3 files changed, 28 insertions(+), 7 deletions(-)
diff --git a/include/efi_loader.h b/include/efi_loader.h index 626d2bbb1a..97152bbb6a 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -628,6 +628,7 @@ int efi_disk_create_partitions(efi_handle_t parent, struct blk_desc *desc, efi_status_t efi_gop_register(void); /* Called by bootefi to make the network interface available */ efi_status_t efi_net_register(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); diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c index 918d43c70f..f0cf317d4f 100644 --- a/lib/efi_loader/efi_net.c +++ b/lib/efi_loader/efi_net.c @@ -876,6 +876,30 @@ static efi_status_t EFIAPI efi_pxe_base_code_set_packets( return EFI_UNSUPPORTED; }
+/** + * efi_net_do_start() - start the efi network stack + * + * This gets called from do_bootefi_exec() each time a payload gets executed. + * + * Return: status code + */ +efi_status_t efi_net_do_start(void) +{ + efi_status_t r = EFI_SUCCESS; + +#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); +#endif + + return r; +} + /** * efi_net_register() - register the simple network protocol * @@ -1017,13 +1041,6 @@ efi_status_t efi_net_register(void) r = efi_http_register(&netobj->header, &netobj->http_service_binding); if (r != EFI_SUCCESS) goto failure_to_add_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); #endif
return EFI_SUCCESS; diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c index 164586742a..eeed82c073 100644 --- a/lib/efi_loader/efi_setup.c +++ b/lib/efi_loader/efi_setup.c @@ -218,6 +218,9 @@ static efi_status_t efi_start_obj_list(void) { efi_status_t ret = EFI_SUCCESS;
+ if (IS_ENABLED(CONFIG_NETDEVICES)) + ret = efi_net_do_start(); + return ret; }

Support setting the device path with efi_dp_from_eth, efi_dp_from_ipv4, and efi_dp_from_http to an ethernet device other than the current ethernet udevice. Calling eth_dp_from_eth with eth_get_dev() as the argument recovers the pevious functionality.
Signed-off-by: Adriano Cordova adriano.cordova@canonical.com --- include/efi_loader.h | 10 +++++----- lib/efi_loader/efi_bootbin.c | 3 ++- lib/efi_loader/efi_device_path.c | 21 ++++++++++++--------- lib/efi_loader/efi_net.c | 24 ++++++++++++++---------- 4 files changed, 33 insertions(+), 25 deletions(-)
diff --git a/include/efi_loader.h b/include/efi_loader.h index 97152bbb6a..8be52e2766 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -128,9 +128,9 @@ static inline void efi_set_bootdev(const char *dev, const char *devnr,
#if CONFIG_IS_ENABLED(NETDEVICES) && CONFIG_IS_ENABLED(EFI_LOADER) /* Call this to update the current device path of the efi net device */ -efi_status_t efi_net_set_dp(const char *dev, const char *server); +efi_status_t efi_net_set_dp(const char *dev, const char *server, struct udevice *udev); /* Call this to get the current device path of the efi net device */ -void efi_net_get_dp(struct efi_device_path **dp); +void efi_net_get_dp(struct efi_device_path **dp, struct udevice *udev); void efi_net_get_addr(struct efi_ipv4_address *ip, struct efi_ipv4_address *mask, struct efi_ipv4_address *gw); @@ -150,7 +150,7 @@ struct http_header {
void efi_net_parse_headers(ulong *num_headers, struct http_header *headers); #else -static inline void efi_net_get_dp(struct efi_device_path **dp) { } +static inline void efi_net_get_dp(struct efi_device_path **dp, struct udevice *udev) { } static inline void efi_net_get_addr(struct efi_ipv4_address *ip, struct efi_ipv4_address *mask, struct efi_ipv4_address *gw) { } @@ -918,8 +918,8 @@ struct efi_device_path *efi_dp_from_part(struct blk_desc *desc, int part); struct efi_device_path *efi_dp_part_node(struct blk_desc *desc, int part); struct efi_device_path *efi_dp_from_file(const struct efi_device_path *dp, const char *path); -struct efi_device_path *efi_dp_from_eth(void); -struct efi_device_path *efi_dp_from_http(const char *server); +struct efi_device_path *efi_dp_from_eth(struct udevice *dev); +struct efi_device_path *efi_dp_from_http(const char *server, struct udevice *dev); struct efi_device_path *efi_dp_from_mem(uint32_t mem_type, uint64_t start_address, size_t size); diff --git a/lib/efi_loader/efi_bootbin.c b/lib/efi_loader/efi_bootbin.c index b677bbc312..b6b40c030f 100644 --- a/lib/efi_loader/efi_bootbin.c +++ b/lib/efi_loader/efi_bootbin.c @@ -13,6 +13,7 @@ #include <image.h> #include <log.h> #include <malloc.h> +#include <net.h>
static struct efi_device_path *bootefi_image_path; static struct efi_device_path *bootefi_device_path; @@ -95,7 +96,7 @@ void efi_set_bootdev(const char *dev, const char *devnr, const char *path,
#if IS_ENABLED(CONFIG_NETDEVICES) if (!strcmp(dev, "Net") || !strcmp(dev, "Http")) { - ret = efi_net_set_dp(dev, devnr); + ret = efi_net_set_dp(dev, devnr, eth_get_dev()); if (ret != EFI_SUCCESS) goto error; } diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index c0633a736b..4a5d50fdf5 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -954,20 +954,20 @@ struct efi_device_path *efi_dp_from_uart(void) return buf; }
-struct efi_device_path __maybe_unused *efi_dp_from_eth(void) +struct efi_device_path __maybe_unused *efi_dp_from_eth(struct udevice *dev) { void *buf, *start; unsigned dpsize = 0;
- assert(eth_get_dev()); + assert(dev);
- dpsize += dp_size(eth_get_dev()); + dpsize += dp_size(dev);
start = buf = efi_alloc(dpsize + sizeof(END)); if (!buf) return NULL;
- buf = dp_fill(buf, eth_get_dev()); + buf = dp_fill(buf, dev);
*((struct efi_device_path *)buf) = END;
@@ -984,11 +984,13 @@ struct efi_device_path __maybe_unused *efi_dp_from_eth(void) * @ip: IPv4 local address * @mask: network mask * @srv: IPv4 remote/server address + * @dev: net udevice * Return: pointer to device path, NULL on error */ static struct efi_device_path *efi_dp_from_ipv4(struct efi_ipv4_address *ip, struct efi_ipv4_address *mask, - struct efi_ipv4_address *srv) + struct efi_ipv4_address *srv, + struct udevice *dev) { struct efi_device_path *dp1, *dp2, *pos; struct { @@ -1010,7 +1012,7 @@ static struct efi_device_path *efi_dp_from_ipv4(struct efi_ipv4_address *ip, pos = &dp.end; memcpy(pos, &END, sizeof(END));
- dp1 = efi_dp_from_eth(); + dp1 = efi_dp_from_eth(dev); if (!dp1) return NULL;
@@ -1029,9 +1031,10 @@ static struct efi_device_path *efi_dp_from_ipv4(struct efi_ipv4_address *ip, * and an END node. * * @server: URI of remote server + * @dev: net udevice * Return: pointer to HTTP device path, NULL on error */ -struct efi_device_path *efi_dp_from_http(const char *server) +struct efi_device_path *efi_dp_from_http(const char *server, struct udevice *dev) { struct efi_device_path *dp1, *dp2; struct efi_device_path_uri *uridp; @@ -1047,7 +1050,7 @@ struct efi_device_path *efi_dp_from_http(const char *server)
efi_net_get_addr(&ip, &mask, NULL);
- dp1 = efi_dp_from_ipv4(&ip, &mask, NULL); + dp1 = efi_dp_from_ipv4(&ip, &mask, NULL, dev); if (!dp1) return NULL;
@@ -1186,7 +1189,7 @@ efi_status_t efi_dp_from_name(const char *dev, const char *devnr, (uintptr_t)image_addr, image_size); } else if (IS_ENABLED(CONFIG_NETDEVICES) && (!strcmp(dev, "Net") || !strcmp(dev, "Http"))) { - efi_net_get_dp(&dp); + efi_net_get_dp(&dp, eth_get_dev()); } else if (!strcmp(dev, "Uart")) { dp = efi_dp_from_uart(); } else { diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c index f0cf317d4f..a399f6f593 100644 --- a/lib/efi_loader/efi_net.c +++ b/lib/efi_loader/efi_net.c @@ -949,10 +949,12 @@ efi_status_t efi_net_register(void) &netobj->net); if (r != EFI_SUCCESS) goto failure_to_add_protocol; - if (!net_dp) - efi_net_set_dp("Net", NULL); - r = efi_add_protocol(&netobj->header, &efi_guid_device_path, - net_dp); + + if (net_dp) + r = efi_add_protocol(&netobj->header, &efi_guid_device_path, + net_dp); + else + r = efi_net_set_dp("Net", NULL, eth_get_dev()); if (r != EFI_SUCCESS) goto failure_to_add_protocol; r = efi_add_protocol(&netobj->header, &efi_pxe_base_code_protocol_guid, @@ -1068,17 +1070,18 @@ out_of_resources: * * @dev: dev to set the device path from * @server: remote server address + * @udev: net udevice * Return: status code */ -efi_status_t efi_net_set_dp(const char *dev, const char *server) +efi_status_t efi_net_set_dp(const char *dev, const char *server, struct udevice *udev) { efi_free_pool(net_dp);
net_dp = NULL; if (!strcmp(dev, "Net")) - net_dp = efi_dp_from_eth(); + net_dp = efi_dp_from_eth(udev); else if (!strcmp(dev, "Http")) - net_dp = efi_dp_from_http(server); + net_dp = efi_dp_from_http(server, udev);
if (!net_dp) return EFI_OUT_OF_RESOURCES; @@ -1091,14 +1094,15 @@ efi_status_t efi_net_set_dp(const char *dev, const char *server) * * Produce a copy of the current device path * - * @dp: copy of the current device path, or NULL on error + * @dp: copy of the current device path + * @udev: net udevice */ -void efi_net_get_dp(struct efi_device_path **dp) +void efi_net_get_dp(struct efi_device_path **dp, struct udevice *udev) { if (!dp) return; if (!net_dp) - efi_net_set_dp("Net", NULL); + efi_net_set_dp("Net", NULL, udev); if (net_dp) *dp = efi_dp_dup(net_dp); }

Add support for multiple efi_net_obj structs
Signed-off-by: Adriano Cordova adriano.cordova@canonical.com --- include/efi_loader.h | 28 +- lib/efi_loader/efi_bootbin.c | 2 +- lib/efi_loader/efi_device_path.c | 4 +- lib/efi_loader/efi_http.c | 6 +- lib/efi_loader/efi_ipconfig.c | 5 +- lib/efi_loader/efi_net.c | 543 +++++++++++++++++++++++++------ lib/efi_loader/efi_setup.c | 5 +- 7 files changed, 467 insertions(+), 126 deletions(-)
diff --git a/include/efi_loader.h b/include/efi_loader.h index 8be52e2766..0d6b01fcfc 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -128,17 +128,22 @@ static inline void efi_set_bootdev(const char *dev, const char *devnr,
#if CONFIG_IS_ENABLED(NETDEVICES) && CONFIG_IS_ENABLED(EFI_LOADER) /* Call this to update the current device path of the efi net device */ -efi_status_t efi_net_set_dp(const char *dev, const char *server, struct udevice *udev); +efi_status_t efi_net_new_dp(const char *dev, const char *server, struct udevice *udev); /* Call this to get the current device path of the efi net device */ -void efi_net_get_dp(struct efi_device_path **dp, struct udevice *udev); +void efi_net_dp_from_dev(struct efi_device_path **dp, struct udevice *udev, bool cache_only); void efi_net_get_addr(struct efi_ipv4_address *ip, struct efi_ipv4_address *mask, - struct efi_ipv4_address *gw); + struct efi_ipv4_address *gw, + struct udevice *dev); void efi_net_set_addr(struct efi_ipv4_address *ip, struct efi_ipv4_address *mask, - struct efi_ipv4_address *gw); + struct efi_ipv4_address *gw, + struct udevice *dev); +#if IS_ENABLED(CONFIG_EFI_HTTP_PROTOCOL) efi_status_t efi_net_do_request(u8 *url, enum efi_http_method method, void **buffer, - u32 *status_code, ulong *file_size, char *headers_buffer); + u32 *status_code, ulong *file_size, char *headers_buffer, + struct efi_service_binding_protocol *parent); +#endif #define MAX_HTTP_HEADERS_SIZE SZ_64K #define MAX_HTTP_HEADERS 100 #define MAX_HTTP_HEADER_NAME 128 @@ -150,13 +155,16 @@ struct http_header {
void efi_net_parse_headers(ulong *num_headers, struct http_header *headers); #else -static inline void efi_net_get_dp(struct efi_device_path **dp, struct udevice *udev) { } +static inline void efi_net_dp_from_dev(struct efi_device_path **dp, + struct udevice *udev, bool cache_only) { } static inline void efi_net_get_addr(struct efi_ipv4_address *ip, struct efi_ipv4_address *mask, - struct efi_ipv4_address *gw) { } + struct efi_ipv4_address *gw, + struct udevice *dev) { } static inline void efi_net_set_addr(struct efi_ipv4_address *ip, struct efi_ipv4_address *mask, - struct efi_ipv4_address *gw) { } + struct efi_ipv4_address *gw, + struct udevice *dev) { } #endif
/* Maximum number of configuration tables */ @@ -627,8 +635,8 @@ int efi_disk_create_partitions(efi_handle_t parent, struct blk_desc *desc, /* Called by bootefi to make GOP (graphical) interface available */ efi_status_t efi_gop_register(void); /* Called by bootefi to make the network interface available */ -efi_status_t efi_net_register(void); -efi_status_t efi_net_do_start(void); +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 */ efi_status_t efi_ipconfig_register(const efi_handle_t handle, struct efi_ip4_config2_protocol *ip4config); diff --git a/lib/efi_loader/efi_bootbin.c b/lib/efi_loader/efi_bootbin.c index b6b40c030f..8a17c48a39 100644 --- a/lib/efi_loader/efi_bootbin.c +++ b/lib/efi_loader/efi_bootbin.c @@ -96,7 +96,7 @@ void efi_set_bootdev(const char *dev, const char *devnr, const char *path,
#if IS_ENABLED(CONFIG_NETDEVICES) if (!strcmp(dev, "Net") || !strcmp(dev, "Http")) { - ret = efi_net_set_dp(dev, devnr, eth_get_dev()); + ret = efi_net_new_dp(dev, devnr, eth_get_dev()); if (ret != EFI_SUCCESS) goto error; } diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index 4a5d50fdf5..e26f9a1ee0 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -1048,7 +1048,7 @@ struct efi_device_path *efi_dp_from_http(const char *server, struct udevice *dev (!server && IS_ENABLED(CONFIG_NET_LWIP))) return NULL;
- efi_net_get_addr(&ip, &mask, NULL); + efi_net_get_addr(&ip, &mask, NULL, dev);
dp1 = efi_dp_from_ipv4(&ip, &mask, NULL, dev); if (!dp1) @@ -1189,7 +1189,7 @@ efi_status_t efi_dp_from_name(const char *dev, const char *devnr, (uintptr_t)image_addr, image_size); } else if (IS_ENABLED(CONFIG_NETDEVICES) && (!strcmp(dev, "Net") || !strcmp(dev, "Http"))) { - efi_net_get_dp(&dp, eth_get_dev()); + efi_net_dp_from_dev(&dp, eth_get_dev(), false); } else if (!strcmp(dev, "Uart")) { dp = efi_dp_from_uart(); } else { diff --git a/lib/efi_loader/efi_http.c b/lib/efi_loader/efi_http.c index 694e199341..20450604ec 100644 --- a/lib/efi_loader/efi_http.c +++ b/lib/efi_loader/efi_http.c @@ -34,6 +34,7 @@ static const efi_guid_t efi_http_guid = EFI_HTTP_PROTOCOL_GUID; struct efi_http_instance { struct efi_http_protocol http; efi_handle_t handle; + struct efi_service_binding_protocol *parent; bool configured; void *http_load_addr; ulong file_size; @@ -186,7 +187,7 @@ static efi_status_t EFIAPI efi_http_configure(struct efi_http_protocol *this,
if (!ipv4_node->use_default_address) { efi_net_set_addr((struct efi_ipv4_address *)&ipv4_node->local_address, - (struct efi_ipv4_address *)&ipv4_node->local_subnet, NULL); + (struct efi_ipv4_address *)&ipv4_node->local_subnet, NULL, NULL); }
http_instance->current_offset = 0; @@ -241,7 +242,7 @@ static efi_status_t EFIAPI efi_http_request(struct efi_http_protocol *this,
ret = efi_net_do_request(url_8, current_method, &http_instance->http_load_addr, &http_instance->status_code, &http_instance->file_size, - http_instance->headers_buffer); + http_instance->headers_buffer, http_instance->parent); if (ret != EFI_SUCCESS) goto out;
@@ -406,6 +407,7 @@ static efi_status_t EFIAPI efi_http_service_binding_create_child( goto failure_to_add_protocol; }
+ new_instance->parent = this; efi_add_handle(new_instance->handle); *child_handle = new_instance->handle;
diff --git a/lib/efi_loader/efi_ipconfig.c b/lib/efi_loader/efi_ipconfig.c index 0b247a4c02..10035e8a7b 100644 --- a/lib/efi_loader/efi_ipconfig.c +++ b/lib/efi_loader/efi_ipconfig.c @@ -58,7 +58,7 @@ static efi_status_t EFIAPI efi_ip4_config2_set_data(struct efi_ip4_config2_proto memcpy((void *)¤t_http_ip, data, sizeof(struct efi_ip4_config2_manual_address)); efi_net_set_addr(¤t_http_ip.address, - ¤t_http_ip.subnet_mask, NULL); + ¤t_http_ip.subnet_mask, NULL, NULL); return EFI_EXIT(EFI_SUCCESS); } return EFI_EXIT(EFI_BAD_BUFFER_SIZE); @@ -131,7 +131,8 @@ static efi_status_t EFIAPI efi_ip4_config2_get_data(struct efi_ip4_config2_proto return EFI_EXIT(EFI_BUFFER_TOO_SMALL); }
- efi_net_get_addr(¤t_http_ip.address, ¤t_http_ip.subnet_mask, NULL); + efi_net_get_addr(¤t_http_ip.address, ¤t_http_ip.subnet_mask, + NULL, NULL); memcpy(data, (void *)¤t_http_ip, sizeof(struct efi_ip4_config2_manual_address));
diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c index a399f6f593..da0dabd383 100644 --- a/lib/efi_loader/efi_net.c +++ b/lib/efi_loader/efi_net.c @@ -22,24 +22,13 @@ #include <vsprintf.h> #include <net.h>
+#define MAX_EFI_NET_OBJS 10 +#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; -static struct efi_pxe_packet *dhcp_ack; -static void *new_tx_packet; -static void *transmit_buffer; -static uchar **receive_buffer; -static size_t *receive_lengths; -static int rx_packet_idx; -static int rx_packet_num; -static struct efi_net_obj *netobj; - -/* - * The current network device path. This device path is updated when a new - * bootfile is downloaded from the network. If then the bootfile is loaded - * as an efi image, net_dp is passed as the device path of the loaded image. - */ -static struct efi_device_path *net_dp;
static struct wget_http_info efi_wget_info = { .set_bootdev = false, @@ -47,20 +36,35 @@ static struct wget_http_info efi_wget_info = {
};
+struct dhcp_entry { + struct efi_pxe_packet *dhcp_ack; + struct udevice *dev; + bool is_valid; +}; + +static struct dhcp_entry dhcp_cache[MAX_NUM_DHCP_ENTRIES]; +static int next_dhcp_entry; + +struct dp_entry { + struct efi_device_path *net_dp; + struct udevice *dev; + bool is_valid; +}; + /* - * The notification function of this event is called in every timer cycle - * to check if a new network packet has been received. - */ -static struct efi_event *network_timer_event; -/* - * This event is signaled when a packet has been received. + * The network device path cache. An entry is added when a new bootfile + * is downloaded from the network. If the bootfile is then loaded as an + * efi image, the most recent entry corresponding to the device is passed + * as the device path of the loaded image. */ -static struct efi_event *wait_for_packet; +static struct dp_entry dp_cache[MAX_NUM_DP_ENTRIES]; +static int next_dp_entry;
/** * struct efi_net_obj - EFI object representing a network interface * * @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 @@ -70,6 +74,7 @@ static struct efi_event *wait_for_packet; */ 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; @@ -80,8 +85,61 @@ struct efi_net_obj { #if IS_ENABLED(CONFIG_EFI_HTTP_PROTOCOL) struct efi_service_binding_protocol http_service_binding; #endif + void *new_tx_packet; + void *transmit_buffer; + uchar **receive_buffer; + size_t *receive_lengths; + int rx_packet_idx; + int rx_packet_num; + /* + * This event is signaled when a packet has been received. + */ + struct efi_event *wait_for_packet; + /* + * The notification function of this event is called in every timer cycle + * to check if a new network packet has been received. + */ + 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]; + +/** + * efi_netobj_is_active() - checks if a netobj is active in the efi subsystem + * + * @netobj: pointer to efi_net_obj + * Return: true if active + */ +static bool efi_netobj_is_active(struct efi_net_obj *netobj) +{ + if (!netobj || !efi_search_obj(&netobj->header)) + return false; + + 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 * @@ -95,20 +153,22 @@ struct efi_net_obj { 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; - wait_for_packet->is_signaled = false; + nt->wait_for_packet->is_signaled = false; this->mode->state = EFI_NETWORK_STARTED; } out: @@ -128,6 +188,7 @@ out: 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);
@@ -137,13 +198,17 @@ static efi_status_t EFIAPI efi_net_stop(struct efi_simple_network *this) 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 */ - rx_packet_num = 0; + nt->rx_packet_num = 0; this->mode->state = EFI_NETWORK_STOPPED; } out: @@ -167,6 +232,7 @@ static efi_status_t EFIAPI efi_net_initialize(struct efi_simple_network *this, { int ret; efi_status_t r = EFI_SUCCESS; + struct efi_net_obj *nt;
EFI_ENTRY("%p, %lx, %lx", this, extra_rx, extra_tx);
@@ -175,6 +241,7 @@ static efi_status_t EFIAPI efi_net_initialize(struct efi_simple_network *this, r = EFI_INVALID_PARAMETER; goto out; } + nt = efi_netobj_from_snp(this);
switch (this->mode->state) { case EFI_NETWORK_INITIALIZED: @@ -187,14 +254,13 @@ static efi_status_t EFIAPI efi_net_initialize(struct efi_simple_network *this,
/* Setup packet buffers */ net_init(); - /* Disable hardware and put it into the reset state */ - eth_halt(); /* Clear cache of packets */ - rx_packet_num = 0; - /* Set current device according to environment variables */ - eth_set_current(); + 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_init(); + ret = eth_start_udev(nt->dev); if (ret < 0) { eth_halt(); this->mode->state = EFI_NETWORK_STOPPED; @@ -202,7 +268,7 @@ static efi_status_t EFIAPI efi_net_initialize(struct efi_simple_network *this, goto out; } else { this->int_status = 0; - wait_for_packet->is_signaled = false; + nt->wait_for_packet->is_signaled = false; this->mode->state = EFI_NETWORK_INITIALIZED; } out: @@ -263,6 +329,7 @@ out: 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);
@@ -271,6 +338,7 @@ static efi_status_t EFIAPI efi_net_shutdown(struct efi_simple_network *this) ret = EFI_INVALID_PARAMETER; goto out; } + nt = efi_netobj_from_snp(this);
switch (this->mode->state) { case EFI_NETWORK_INITIALIZED: @@ -283,9 +351,12 @@ static efi_status_t EFIAPI efi_net_shutdown(struct efi_simple_network *this) goto out; }
+ eth_set_dev(nt->dev); + env_set("ethact", eth_get_name()); eth_halt(); + this->int_status = 0; - wait_for_packet->is_signaled = false; + nt->wait_for_packet->is_signaled = false; this->mode->state = EFI_NETWORK_STARTED;
out: @@ -461,6 +532,7 @@ 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);
@@ -472,6 +544,8 @@ static efi_status_t EFIAPI efi_net_get_status(struct efi_simple_network *this, goto out; }
+ nt = efi_netobj_from_snp(this); + switch (this->mode->state) { case EFI_NETWORK_STOPPED: ret = EFI_NOT_STARTED; @@ -488,9 +562,9 @@ static efi_status_t EFIAPI efi_net_get_status(struct efi_simple_network *this, this->int_status = 0; } if (txbuf) - *txbuf = new_tx_packet; + *txbuf = nt->new_tx_packet;
- new_tx_packet = NULL; + nt->new_tx_packet = NULL; out: return EFI_EXIT(ret); } @@ -517,6 +591,7 @@ static efi_status_t EFIAPI efi_net_transmit 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, @@ -530,6 +605,8 @@ static efi_status_t EFIAPI efi_net_transmit goto out; }
+ nt = efi_netobj_from_snp(this); + /* We do not support jumbo packets */ if (buffer_size > PKTSIZE_ALIGN) { ret = EFI_INVALID_PARAMETER; @@ -574,11 +651,14 @@ static efi_status_t EFIAPI efi_net_transmit break; }
+ eth_set_dev(nt->dev); + env_set("ethact", eth_get_name()); + /* Ethernet packets always fit, just bounce */ - memcpy(transmit_buffer, buffer, buffer_size); - net_send_packet(transmit_buffer, buffer_size); + memcpy(nt->transmit_buffer, buffer, buffer_size); + net_send_packet(nt->transmit_buffer, buffer_size);
- new_tx_packet = buffer; + nt->new_tx_packet = buffer; this->int_status |= EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT; out: return EFI_EXIT(ret); @@ -609,6 +689,7 @@ static efi_status_t EFIAPI efi_net_receive 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); @@ -622,6 +703,8 @@ static efi_status_t EFIAPI efi_net_receive goto out; }
+ nt = efi_netobj_from_snp(this); + switch (this->mode->state) { case EFI_NETWORK_STOPPED: ret = EFI_NOT_STARTED; @@ -633,16 +716,16 @@ static efi_status_t EFIAPI efi_net_receive break; }
- if (!rx_packet_num) { + if (!nt->rx_packet_num) { ret = EFI_NOT_READY; goto out; } /* Fill export parameters */ - eth_hdr = (struct ethernet_hdr *)receive_buffer[rx_packet_idx]; + 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 *)&receive_buffer[rx_packet_idx][hdr_size - 2]); + protlen = ntohs(*(u16 *)&nt->receive_buffer[nt->rx_packet_idx][hdr_size - 2]); } if (header_size) *header_size = hdr_size; @@ -652,20 +735,20 @@ static efi_status_t EFIAPI efi_net_receive memcpy(src_addr, eth_hdr->et_src, ARP_HLEN); if (protocol) *protocol = protlen; - if (*buffer_size < receive_lengths[rx_packet_idx]) { + if (*buffer_size < nt->receive_lengths[nt->rx_packet_idx]) { /* Packet doesn't fit, try again with bigger buffer */ - *buffer_size = receive_lengths[rx_packet_idx]; + *buffer_size = nt->receive_lengths[nt->rx_packet_idx]; ret = EFI_BUFFER_TOO_SMALL; goto out; } /* Copy packet */ - memcpy(buffer, receive_buffer[rx_packet_idx], - receive_lengths[rx_packet_idx]); - *buffer_size = receive_lengths[rx_packet_idx]; - rx_packet_idx = (rx_packet_idx + 1) % ETH_PACKETS_BATCH_RECV; - rx_packet_num--; - if (rx_packet_num) - wait_for_packet->is_signaled = true; + 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: @@ -682,18 +765,35 @@ out: */ void efi_net_set_dhcp_ack(void *pkt, int len) { - int maxsize = sizeof(*dhcp_ack); + struct efi_pxe_packet **dhcp_ack; + struct udevice *dev; + int i; + + dhcp_ack = &dhcp_cache[next_dhcp_entry].dhcp_ack; + + /* For now this function gets called only by the current device */ + dev = eth_get_dev();
- if (!dhcp_ack) { - dhcp_ack = malloc(maxsize); - if (!dhcp_ack) + int maxsize = sizeof(**dhcp_ack); + + if (!*dhcp_ack) { + *dhcp_ack = malloc(maxsize); + if (!*dhcp_ack) return; } - memset(dhcp_ack, 0, maxsize); - memcpy(dhcp_ack, pkt, min(len, maxsize)); + memset(*dhcp_ack, 0, maxsize); + memcpy(*dhcp_ack, pkt, min(len, maxsize)); + + dhcp_cache[next_dhcp_entry].is_valid = true; + dhcp_cache[next_dhcp_entry].dev = dev; + next_dhcp_entry++; + next_dhcp_entry %= MAX_NUM_DHCP_ENTRIES;
- if (netobj) - netobj->pxe_mode.dhcp_ack = *dhcp_ack; + 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; + } + } }
/** @@ -707,6 +807,11 @@ void efi_net_set_dhcp_ack(void *pkt, int len) 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)) @@ -717,15 +822,15 @@ static void efi_net_push(void *pkt, int len) return;
/* Can't store more than pre-alloced buffer */ - if (rx_packet_num >= ETH_PACKETS_BATCH_RECV) + if (nt->rx_packet_num >= ETH_PACKETS_BATCH_RECV) return;
- rx_packet_next = (rx_packet_idx + rx_packet_num) % + rx_packet_next = (nt->rx_packet_idx + nt->rx_packet_num) % ETH_PACKETS_BATCH_RECV; - memcpy(receive_buffer[rx_packet_next], pkt, len); - receive_lengths[rx_packet_next] = len; + memcpy(nt->receive_buffer[rx_packet_next], pkt, len); + nt->receive_lengths[rx_packet_next] = len;
- rx_packet_num++; + nt->rx_packet_num++; }
/** @@ -740,6 +845,7 @@ 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);
@@ -750,14 +856,19 @@ static void EFIAPI efi_network_timer_notify(struct efi_event *event, if (!this || this->mode->state != EFI_NETWORK_INITIALIZED) goto out;
- if (!rx_packet_num) { + 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 (rx_packet_num) { + if (nt->rx_packet_num) { this->int_status |= EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT; - wait_for_packet->is_signaled = true; + nt->wait_for_packet->is_signaled = true; } } out: @@ -876,17 +987,115 @@ static efi_status_t EFIAPI efi_pxe_base_code_set_packets( return EFI_UNSUPPORTED; }
+/** + * efi_netobj_set_dp() - set device path of a netobj + * + * @netobj: pointer to efi_net_obj + * @dp: device path to set, allocated by caller + * Return: status code + */ +efi_status_t efi_netobj_set_dp(struct efi_net_obj *netobj, struct efi_device_path *dp) +{ + efi_status_t ret; + struct efi_handler *phandler; + struct efi_device_path *new_net_dp; + + if (!efi_netobj_is_active(netobj)) + return EFI_SUCCESS; + + // Create a device path for the netobj + new_net_dp = dp; + if (!new_net_dp) + return EFI_OUT_OF_RESOURCES; + + phandler = NULL; + efi_search_protocol(&netobj->header, &efi_guid_device_path, &phandler); + + // If the device path protocol is not yet installed, install it + if (!phandler) + goto add; + + // If it is already installed, try to update it + ret = efi_reinstall_protocol_interface(&netobj->header, &efi_guid_device_path, + phandler->protocol_interface, new_net_dp); + if (ret != EFI_SUCCESS) + return ret; + + return EFI_SUCCESS; +add: + ret = efi_add_protocol(&netobj->header, &efi_guid_device_path, + new_net_dp); + if (ret != EFI_SUCCESS) + return ret; + + return EFI_SUCCESS; +} + +/** + * efi_netobj_get_dp() - get device path of a netobj + * + * @netobj: pointer to efi_net_obj + * Return: device path, NULL on error + */ +static struct efi_device_path *efi_netobj_get_dp(struct efi_net_obj *netobj) +{ + struct efi_handler *phandler; + + if (!efi_netobj_is_active(netobj)) + return NULL; + + phandler = NULL; + efi_search_protocol(&netobj->header, &efi_guid_device_path, &phandler); + + if (phandler && phandler->protocol_interface) + return efi_dp_dup(phandler->protocol_interface); + + return NULL; +} + /** * efi_net_do_start() - start the efi network stack * * This gets called from do_bootefi_exec() each time a payload gets executed. * + * @dev: net udevice * Return: status code */ -efi_status_t efi_net_do_start(void) +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; + int i; + + netobj = NULL; + for (i = 0; i < MAX_EFI_NET_OBJS; i++) { + if (net_objs[i] && net_objs[i]->dev == dev) { + netobj = net_objs[i]; + break; + } + } + + if (!efi_netobj_is_active(netobj)) + return r; + + efi_net_dp_from_dev(&net_dp, netobj->dev, true); + // If no dp cache entry applies and there already + // is a device path installed, continue + if (!net_dp) { + if (efi_netobj_get_dp(netobj)) + goto set_addr; + else + net_dp = efi_dp_from_eth(netobj->dev); + } + + if (!net_dp) + return EFI_OUT_OF_RESOURCES;
+ r = efi_netobj_set_dp(netobj, net_dp); + 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 @@ -894,7 +1103,7 @@ efi_status_t efi_net_do_start(void) * 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); + (struct efi_ipv4_address *)&netobj->pxe_mode.subnet_mask, NULL, dev); #endif
return r; @@ -904,27 +1113,53 @@ efi_status_t efi_net_do_start(void) * efi_net_register() - register the simple network protocol * * This gets called from do_bootefi_exec(). + * @dev: net udevice */ -efi_status_t efi_net_register(void) +efi_status_t efi_net_register(struct udevice *dev) { efi_status_t r; - int i; - - if (!eth_get_dev()) { + int seq_num; + struct efi_net_obj *netobj; + void *transmit_buffer; + uchar **receive_buffer; + size_t *receive_lengths; + int i, j; + + if (!dev) { /* No network device active, don't expose any */ return EFI_SUCCESS; }
+ for (i = 0; i < MAX_EFI_NET_OBJS; i++) { + if (net_objs[i] && net_objs[i]->dev == dev) { + // Do not register duplicate devices + return EFI_SUCCESS; + } + } + + seq_num = -1; + for (i = 0; i < MAX_EFI_NET_OBJS; i++) { + if (!net_objs[i]) { + seq_num = i; + break; + } + } + if (seq_num < 0) + return EFI_OUT_OF_RESOURCES; + /* 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, @@ -936,10 +1171,13 @@ efi_status_t efi_net_register(void) if (!receive_buffer[i]) goto 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;
/* Hook net up to the device list */ efi_add_handle(&netobj->header); @@ -950,13 +1188,6 @@ efi_status_t efi_net_register(void) if (r != EFI_SUCCESS) goto failure_to_add_protocol;
- if (net_dp) - r = efi_add_protocol(&netobj->header, &efi_guid_device_path, - net_dp); - else - r = efi_net_set_dp("Net", NULL, eth_get_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) @@ -977,7 +1208,9 @@ efi_status_t efi_net_register(void) netobj->net.receive = efi_net_receive; netobj->net.mode = &netobj->net_mode; netobj->net_mode.state = EFI_NETWORK_STOPPED; - memcpy(netobj->net_mode.current_address.mac_addr, eth_get_ethaddr(), 6); + 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; @@ -997,20 +1230,31 @@ efi_status_t efi_net_register(void) 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; - if (dhcp_ack) - netobj->pxe_mode.dhcp_ack = *dhcp_ack; + + /* + * 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; 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; + } + }
/* * Create WaitForPacket event. */ r = efi_create_event(EVT_NOTIFY_WAIT, TPL_CALLBACK, efi_network_timer_notify, NULL, NULL, - &wait_for_packet); + &netobj->wait_for_packet); if (r != EFI_SUCCESS) { printf("ERROR: Failed to register network event\n"); return r; } - netobj->net.wait_for_packet = wait_for_packet; + /* * Create a timer event. * @@ -1021,13 +1265,13 @@ efi_status_t efi_net_register(void) */ r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_NOTIFY, efi_network_timer_notify, &netobj->net, NULL, - &network_timer_event); + &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(network_timer_event, EFI_TIMER_PERIODIC, 0); + 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; @@ -1044,7 +1288,8 @@ efi_status_t efi_net_register(void) 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"); @@ -1063,7 +1308,7 @@ out_of_resources: }
/** - * efi_net_set_dp() - set device path of efi net device + * efi_net_new_dp() - update device path associated to a net udevice * * This gets called to update the device path when a new boot * file is downloaded @@ -1073,38 +1318,93 @@ out_of_resources: * @udev: net udevice * Return: status code */ -efi_status_t efi_net_set_dp(const char *dev, const char *server, struct udevice *udev) +efi_status_t efi_net_new_dp(const char *dev, const char *server, struct udevice *udev) { - efi_free_pool(net_dp); + efi_status_t ret; + struct efi_net_obj *netobj; + struct efi_device_path *old_net_dp, *new_net_dp; + struct efi_device_path **dp; + int i; + + dp = &dp_cache[next_dp_entry].net_dp; + + dp_cache[next_dp_entry].dev = udev; + dp_cache[next_dp_entry].is_valid = true; + next_dp_entry++; + next_dp_entry %= MAX_NUM_DP_ENTRIES;
- net_dp = NULL; + old_net_dp = *dp; + new_net_dp = NULL; if (!strcmp(dev, "Net")) - net_dp = efi_dp_from_eth(udev); + new_net_dp = efi_dp_from_eth(udev); else if (!strcmp(dev, "Http")) - net_dp = efi_dp_from_http(server, udev); + new_net_dp = efi_dp_from_http(server, udev); + if (!new_net_dp) + return EFI_OUT_OF_RESOURCES;
- if (!net_dp) + *dp = new_net_dp; + // Free the old cache entry + efi_free_pool(old_net_dp); + + netobj = NULL; + for (i = 0; i < MAX_EFI_NET_OBJS; i++) { + if (net_objs[i] && net_objs[i]->dev == udev) { + netobj = net_objs[i]; + break; + } + } + if (!netobj) + return EFI_SUCCESS; + + new_net_dp = efi_dp_dup(*dp); + if (!new_net_dp) return EFI_OUT_OF_RESOURCES; + ret = efi_netobj_set_dp(netobj, new_net_dp); + if (ret != EFI_SUCCESS) + efi_free_pool(new_net_dp);
- return EFI_SUCCESS; + return ret; }
/** - * efi_net_get_dp() - get device path of efi net device + * efi_net_dp_from_dev() - get device path associated to a net udevice * * Produce a copy of the current device path * * @dp: copy of the current device path * @udev: net udevice + * @cache_only: get device path from cache only */ -void efi_net_get_dp(struct efi_device_path **dp, struct udevice *udev) +void efi_net_dp_from_dev(struct efi_device_path **dp, struct udevice *udev, bool cache_only) { + int i, j; + if (!dp) return; - if (!net_dp) - efi_net_set_dp("Net", NULL, udev); - if (net_dp) - *dp = efi_dp_dup(net_dp); + + *dp = NULL; + + if (cache_only) + goto cache; + + // If a netobj matches: + for (i = 0; i < MAX_EFI_NET_OBJS; i++) { + if (net_objs[i] && net_objs[i]->dev == udev) { + *dp = efi_netobj_get_dp(net_objs[i]); + if (*dp) + return; + } + } +cache: + // Search in the cache + i = (next_dp_entry + MAX_NUM_DP_ENTRIES - 1) % MAX_NUM_DP_ENTRIES; + for (j = 0; dp_cache[i].is_valid && j < MAX_NUM_DP_ENTRIES; + i = (i + MAX_NUM_DP_ENTRIES - 1) % MAX_NUM_DP_ENTRIES, j++) { + if (dp_cache[i].dev == udev) { + *dp = efi_dp_dup(dp_cache[i].net_dp); + return; + } + } }
/** @@ -1120,11 +1420,15 @@ void efi_net_get_dp(struct efi_device_path **dp, struct udevice *udev) * be filled with the current network mask * @gw: pointer to an efi_ipv4_address struct to be * filled with the current network gateway + * @dev: udevice */ void efi_net_get_addr(struct efi_ipv4_address *ip, struct efi_ipv4_address *mask, - struct efi_ipv4_address *gw) + struct efi_ipv4_address *gw, + struct udevice *dev) { + if (!dev) + dev = eth_get_dev(); #ifdef CONFIG_NET_LWIP char ipstr[] = "ipaddr\0\0"; char maskstr[] = "netmask\0\0"; @@ -1133,7 +1437,7 @@ void efi_net_get_addr(struct efi_ipv4_address *ip, struct in_addr tmp; char *env;
- idx = dev_seq(eth_get_dev()); + idx = dev_seq(dev);
if (idx < 0 || idx > 99) { log_err("unexpected idx %d\n", idx); @@ -1180,11 +1484,15 @@ void efi_net_get_addr(struct efi_ipv4_address *ip, * @ip: pointer to new IP address * @mask: pointer to new network mask to set * @gw: pointer to new network gateway + * @dev: udevice */ void efi_net_set_addr(struct efi_ipv4_address *ip, struct efi_ipv4_address *mask, - struct efi_ipv4_address *gw) + struct efi_ipv4_address *gw, + struct udevice *dev) { + if (!dev) + dev = eth_get_dev(); #ifdef CONFIG_NET_LWIP char ipstr[] = "ipaddr\0\0"; char maskstr[] = "netmask\0\0"; @@ -1193,7 +1501,7 @@ void efi_net_set_addr(struct efi_ipv4_address *ip, struct in_addr *addr; char tmp[46];
- idx = dev_seq(eth_get_dev()); + idx = dev_seq(dev);
if (idx < 0 || idx > 99) { log_err("unexpected idx %d\n", idx); @@ -1231,6 +1539,7 @@ void efi_net_set_addr(struct efi_ipv4_address *ip, #endif }
+#if IS_ENABLED(CONFIG_EFI_HTTP_PROTOCOL) /** * efi_net_set_buffer() - allocate a buffer of min 64K * @@ -1318,26 +1627,41 @@ void efi_net_parse_headers(ulong *num_headers, struct http_header *headers) * @status_code: HTTP status code * @file_size: file size in bytes * @headers_buffer: headers buffer + * @parent: service binding protocol * Return: status code */ efi_status_t efi_net_do_request(u8 *url, enum efi_http_method method, void **buffer, - u32 *status_code, ulong *file_size, char *headers_buffer) + u32 *status_code, ulong *file_size, char *headers_buffer, + struct efi_service_binding_protocol *parent) { efi_status_t ret = EFI_SUCCESS; int wget_ret; static bool last_head; + struct udevice *dev; + int i;
- if (!buffer || !file_size) + if (!buffer || !file_size || !parent) return EFI_ABORTED;
efi_wget_info.method = (enum wget_http_method)method; efi_wget_info.headers = headers_buffer;
+ // 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) + dev = net_objs[i]->dev; + } + if (!dev) + return EFI_ABORTED; + switch (method) { case HTTP_METHOD_GET: ret = efi_net_set_buffer(buffer, last_head ? (size_t)efi_wget_info.hdr_cont_len : 0); if (ret != EFI_SUCCESS) goto out; + eth_set_dev(dev); + env_set("ethact", eth_get_name()); wget_ret = wget_request((ulong)*buffer, url, &efi_wget_info); if ((ulong)efi_wget_info.hdr_cont_len > efi_wget_info.buffer_size) { // Try again with updated buffer size @@ -1345,6 +1669,8 @@ efi_status_t efi_net_do_request(u8 *url, enum efi_http_method method, void **buf ret = efi_net_set_buffer(buffer, (size_t)efi_wget_info.hdr_cont_len); if (ret != EFI_SUCCESS) goto out; + eth_set_dev(dev); + env_set("ethact", eth_get_name()); if (wget_request((ulong)*buffer, url, &efi_wget_info)) { efi_free_pool(*buffer); ret = EFI_DEVICE_ERROR; @@ -1364,6 +1690,8 @@ efi_status_t efi_net_do_request(u8 *url, enum efi_http_method method, void **buf ret = efi_net_set_buffer(buffer, 0); if (ret != EFI_SUCCESS) goto out; + eth_set_dev(dev); + env_set("ethact", eth_get_name()); wget_request((ulong)*buffer, url, &efi_wget_info); *file_size = 0; *status_code = efi_wget_info.status_code; @@ -1377,3 +1705,4 @@ efi_status_t efi_net_do_request(u8 *url, enum efi_http_method method, void **buf out: return ret; } +#endif diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c index eeed82c073..48f91da5df 100644 --- a/lib/efi_loader/efi_setup.c +++ b/lib/efi_loader/efi_setup.c @@ -11,6 +11,7 @@ #include <efi_variable.h> #include <log.h> #include <asm-generic/unaligned.h> +#include <net.h>
#define OBJ_LIST_INITIALIZED 0 #define OBJ_LIST_NOT_INITIALIZED 1 @@ -219,7 +220,7 @@ static efi_status_t efi_start_obj_list(void) efi_status_t ret = EFI_SUCCESS;
if (IS_ENABLED(CONFIG_NETDEVICES)) - ret = efi_net_do_start(); + ret = efi_net_do_start(eth_get_dev());
return ret; } @@ -336,7 +337,7 @@ efi_status_t efi_init_obj_list(void) goto out; } if (IS_ENABLED(CONFIG_NETDEVICES)) { - ret = efi_net_register(); + ret = efi_net_register(eth_get_dev()); if (ret != EFI_SUCCESS) goto out; }

Register the U-boot ethernet udevices in the EFI network stack as efi_net_obj as they get probed, and unregister them when the udevice gets removed
Signed-off-by: Adriano Cordova adriano.cordova@canonical.com --- include/efi_loader.h | 10 +- lib/efi_driver/Makefile | 1 + lib/efi_driver/efi_net_device.c | 86 +++++++++++ lib/efi_driver/efi_uclass.c | 2 +- lib/efi_loader/efi_net.c | 261 +++++++++++++++++++++++++------- lib/efi_loader/efi_setup.c | 4 +- 6 files changed, 302 insertions(+), 62 deletions(-) create mode 100644 lib/efi_driver/efi_net_device.c
diff --git a/include/efi_loader.h b/include/efi_loader.h index 0d6b01fcfc..09a9f75e19 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -634,9 +634,13 @@ int efi_disk_create_partitions(efi_handle_t parent, struct blk_desc *desc, const char *pdevname); /* Called by bootefi to make GOP (graphical) interface available */ 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 to register an EFI network device */ +int efi_net_register(void *ctx, struct event *event); +/* Called to unregister an EFI network device */ +int efi_net_unregister(void *ctx, struct event *event); +/* Called to initialized registered network devices */ +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); diff --git a/lib/efi_driver/Makefile b/lib/efi_driver/Makefile index 0da20fe91d..08489064c7 100644 --- a/lib/efi_driver/Makefile +++ b/lib/efi_driver/Makefile @@ -9,4 +9,5 @@ obj-y += efi_uclass.o ifeq ($(CONFIG_PARTITIONS),y) obj-y += efi_block_device.o endif +obj-$(CONFIG_NETDEVICES) += efi_net_device.o obj-$(CONFIG_SYSRESET_SBI) += efi_reset_riscv.o diff --git a/lib/efi_driver/efi_net_device.c b/lib/efi_driver/efi_net_device.c new file mode 100644 index 0000000000..64a443d074 --- /dev/null +++ b/lib/efi_driver/efi_net_device.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * EFI network driver + * + */ + +#include <dm.h> +#include <efi_driver.h> +#include <malloc.h> +#include <dm/device-internal.h> +#include <dm/root.h> +#include <dm/tag.h> +#include <dm/uclass-internal.h> + +/** + * efi_net_bind_drv() - TODO + * + * @this: driver binding protocol + * @handle: handle + * @interface: block io protocol + * Return: status code + */ +static efi_status_t efi_net_bind_drv( + struct efi_driver_binding_extended_protocol *this, + efi_handle_t handle, void *interface) +{ + EFI_PRINT("%s: handle %p, interface %p\n", __func__, handle, interface); + + return EFI_UNSUPPORTED; +} + +/** + * efi_net_init_drv() - initialize network device driver + * + * @this: extended driver binding protocol + * Return: status code + */ +static efi_status_t +efi_net_init_drv(struct efi_driver_binding_extended_protocol *this) +{ + int ret; + struct udevice *dev; + struct event event; + + for (uclass_find_first_device(UCLASS_ETH, &dev); dev; + uclass_find_next_device(&dev)) { + if (dev_get_flags(dev) & DM_FLAG_ACTIVATED) { + memcpy(&event.data, &dev, sizeof(dev)); + ret = efi_net_register(NULL, &event); + if (ret) { + log_err("Failed registering %s in EFI net\n", dev->name); + return EFI_OUT_OF_RESOURCES; + } + } + } + + ret = event_register("efi_net register", EVT_DM_POST_PROBE, + efi_net_register, this); + if (ret) { + log_err("Event registration for efi_net register failed\n"); + return EFI_OUT_OF_RESOURCES; + } + + ret = event_register("efi_net unregister", EVT_DM_PRE_REMOVE, + efi_net_unregister, this); + if (ret) { + log_err("Event registration for efi_net unregister failed\n"); + return EFI_OUT_OF_RESOURCES; + } + + return EFI_SUCCESS; +} + +/* EFI driver operators */ +static const struct efi_driver_ops driver_ops = { + .protocol = &efi_net_guid, + .init = efi_net_init_drv, + .bind = efi_net_bind_drv, +}; + +/* Identify as EFI driver */ +U_BOOT_DRIVER(efi_net) = { + .name = "efi_net", + .id = UCLASS_EFI_LOADER, + .ops = &driver_ops, +}; diff --git a/lib/efi_driver/efi_uclass.c b/lib/efi_driver/efi_uclass.c index e1e28df20b..6553141f4e 100644 --- a/lib/efi_driver/efi_uclass.c +++ b/lib/efi_driver/efi_uclass.c @@ -74,7 +74,7 @@ static efi_status_t EFIAPI efi_uc_supported( * U-Boot internal devices install protocols interfaces without calling * ConnectController(). Hence we should not bind an extra driver. */ - if (controller_handle->dev) { + if (controller_handle->dev && bp->ops->protocol != &efi_net_guid) { ret = EFI_UNSUPPORTED; goto out; } diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c index da0dabd383..20d2c3a5f6 100644 --- a/lib/efi_loader/efi_net.c +++ b/lib/efi_loader/efi_net.c @@ -859,6 +859,11 @@ static void EFIAPI efi_network_timer_notify(struct efi_event *event, nt = efi_netobj_from_snp(this); curr_efi_net_obj = nt->efi_seq_num;
+ // The following only happens if the net obj was removed but the event + // was not successfully removed. + if (!net_objs[curr_efi_net_obj] || !efi_netobj_is_active(nt)) + goto out; + if (!nt->rx_packet_num) { eth_set_dev(nt->dev); env_set("ethact", eth_get_name()); @@ -1054,28 +1059,18 @@ static struct efi_device_path *efi_netobj_get_dp(struct efi_net_obj *netobj) }
/** - * efi_net_do_start() - start the efi network stack + * efi_netobj_start() - start an efi net device * - * This gets called from do_bootefi_exec() each time a payload gets executed. * - * @dev: net udevice + * @netobj: efi_net_obj * Return: status code */ -efi_status_t efi_net_do_start(struct udevice *dev) +efi_status_t efi_netobj_start(struct efi_net_obj *netobj) { efi_status_t r = EFI_SUCCESS; - struct efi_net_obj *netobj; struct efi_device_path *net_dp; int i;
- netobj = NULL; - for (i = 0; i < MAX_EFI_NET_OBJS; i++) { - if (net_objs[i] && net_objs[i]->dev == dev) { - netobj = net_objs[i]; - break; - } - } - if (!efi_netobj_is_active(netobj)) return r;
@@ -1103,57 +1098,59 @@ set_addr: * 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); + (struct efi_ipv4_address *)&netobj->pxe_mode.subnet_mask, + NULL, netobj->dev); #endif
- return r; + return 0; +} + +/** + * efi_net_do_start() - start the efi network stack + * + * This gets called from do_bootefi_exec() each time a payload gets executed. + * + * Return: status code + */ +efi_status_t efi_net_do_start(void) +{ + int i, r; + + for (i = 0; i < MAX_EFI_NET_OBJS; i++) { + if (net_objs[i]) { + r = efi_netobj_start(net_objs[i]); + if (r) + return EFI_DEVICE_ERROR; + } + } + + return EFI_SUCCESS; }
/** - * efi_net_register() - register the simple network protocol + * efi_netobj_init() - initialize an efi_net_obj * - * This gets called from do_bootefi_exec(). - * @dev: net udevice + * @netobj: efi net object + * Return: 0 on success, negative on error */ -efi_status_t efi_net_register(struct udevice *dev) +static int efi_netobj_init(struct efi_net_obj *netobj) { efi_status_t r; - int seq_num; - struct efi_net_obj *netobj; + struct udevice *dev; void *transmit_buffer; uchar **receive_buffer; size_t *receive_lengths; int i, j;
+ if (!netobj || efi_netobj_is_active(netobj)) + return 0; + + dev = netobj->dev; if (!dev) { /* No network device active, don't expose any */ - return EFI_SUCCESS; - } - - for (i = 0; i < MAX_EFI_NET_OBJS; i++) { - if (net_objs[i] && net_objs[i]->dev == dev) { - // Do not register duplicate devices - return EFI_SUCCESS; - } + return 0; }
- seq_num = -1; - for (i = 0; i < MAX_EFI_NET_OBJS; i++) { - if (!net_objs[i]) { - seq_num = i; - break; - } - } - if (seq_num < 0) - return EFI_OUT_OF_RESOURCES; - - /* 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) @@ -1252,7 +1249,7 @@ efi_status_t efi_net_register(struct udevice *dev) &netobj->wait_for_packet); if (r != EFI_SUCCESS) { printf("ERROR: Failed to register network event\n"); - return r; + return -1; }
/* @@ -1268,13 +1265,13 @@ efi_status_t efi_net_register(struct udevice *dev) &netobj->network_timer_event); if (r != EFI_SUCCESS) { printf("ERROR: Failed to register network event\n"); - return r; + return -1; } /* 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; + return -1; }
#if IS_ENABLED(CONFIG_EFI_IP4_CONFIG2_PROTOCOL) @@ -1288,15 +1285,12 @@ efi_status_t efi_net_register(struct udevice *dev) if (r != EFI_SUCCESS) goto failure_to_add_protocol; #endif - netobj->efi_seq_num = seq_num; - net_objs[seq_num] = netobj; - return EFI_SUCCESS; + printf("efi_net init device number %d\n", netobj->efi_seq_num); + return 0; failure_to_add_protocol: printf("ERROR: Failure to add protocol\n"); - return r; + return -1; out_of_resources: - free(netobj); - netobj = NULL; free(transmit_buffer); if (receive_buffer) for (i = 0; i < ETH_PACKETS_BATCH_RECV; i++) @@ -1304,7 +1298,162 @@ out_of_resources: free(receive_buffer); free(receive_lengths); printf("ERROR: Out of memory\n"); - return EFI_OUT_OF_RESOURCES; + return -1; +} + +/** + * efi_net_init() - initialize registered efi objects + * + * Return: status code + */ +efi_status_t efi_net_init(void) +{ + int i, r; + + for (i = 0; i < MAX_EFI_NET_OBJS; i++) { + if (net_objs[i]) { + r = efi_netobj_init(net_objs[i]); + if (r) + return EFI_DEVICE_ERROR; + } + } + + return EFI_SUCCESS; +} + +/** + * efi_net_register() - register a net device + * + * This function is called when the device is probed + * + * @ctx: context set at registration time + * @event: event + * Return: 0 on success, negative on error + */ +int efi_net_register(void *ctx, struct event *event) +{ + struct udevice *dev; + int seq_num; + enum uclass_id id; + struct efi_net_obj *netobj; + int i; + + dev = event->data.dm.dev; + if (!dev) { + /* No network device active, don't expose any */ + return 0; + } + + id = device_get_uclass_id(dev); + if (id != UCLASS_ETH) + return 0; + + for (i = 0; i < MAX_EFI_NET_OBJS; i++) { + if (net_objs[i] && net_objs[i]->dev == dev) { + // Do not register duplicate devices + return 0; + } + } + + // Find a slot for this efi_net_obj + seq_num = -1; + for (i = 0; i < MAX_EFI_NET_OBJS; i++) { + if (!net_objs[i]) { + seq_num = i; + break; + } + } + if (seq_num < 0) + return -1; + + netobj = calloc(1, sizeof(*netobj)); + if (!netobj) + goto out_of_resources; + + netobj->dev = dev; + netobj->efi_seq_num = seq_num; + net_objs[seq_num] = netobj; + printf("efi_net registered device number %d\n", netobj->efi_seq_num); + return 0; +out_of_resources: + printf("ERROR: Out of memory\n"); + return -1; +} + +/** + * efi_net_unregister() - unregister a net device + * + * + * @ctx: context set at registration time + * @event: event + * Return: 0 on success, negative on error + */ +int efi_net_unregister(void *ctx, struct event *event) +{ + efi_status_t ret = EFI_SUCCESS; + struct udevice *dev; + enum uclass_id id; + struct efi_net_obj *netobj; + struct efi_handler *phandler; + void *interface; + int i; + + dev = event->data.dm.dev; + if (!dev) { + /* No network device active, don't expose any */ + return 0; + } + + id = device_get_uclass_id(dev); + if (id != UCLASS_ETH) + return 0; + + netobj = NULL; + for (i = 0; i < MAX_EFI_NET_OBJS; i++) { + if (net_objs[i] && net_objs[i]->dev == dev) { + netobj = net_objs[i]; + break; + } + } + + if (!netobj) + return 0; + + // Remove from the list + net_objs[i] = NULL; + + if (efi_netobj_is_active(netobj)) { + free(netobj->transmit_buffer); + if (netobj->receive_buffer) + for (i = 0; i < ETH_PACKETS_BATCH_RECV; i++) + free(netobj->receive_buffer[i]); + free(netobj->receive_buffer); + free(netobj->receive_lengths); + + 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; + + phandler = NULL; + efi_search_protocol(&netobj->header, &efi_guid_device_path, &phandler); + if (phandler && phandler->protocol_interface) + interface = phandler->protocol_interface; + + ret = efi_delete_handle(&netobj->header); + if (ret != EFI_SUCCESS) + return -1; + + efi_free_pool(interface); + } + + // Free the efi_net_obj + free(netobj); + + return 0; }
/** diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c index 48f91da5df..e6ef76bcca 100644 --- a/lib/efi_loader/efi_setup.c +++ b/lib/efi_loader/efi_setup.c @@ -220,7 +220,7 @@ static efi_status_t efi_start_obj_list(void) efi_status_t ret = EFI_SUCCESS;
if (IS_ENABLED(CONFIG_NETDEVICES)) - ret = efi_net_do_start(eth_get_dev()); + ret = efi_net_do_start();
return ret; } @@ -337,7 +337,7 @@ efi_status_t efi_init_obj_list(void) goto out; } if (IS_ENABLED(CONFIG_NETDEVICES)) { - ret = efi_net_register(eth_get_dev()); + ret = efi_net_init(); if (ret != EFI_SUCCESS) goto out; }

When an efi_net_obj gets unregistered, save it and try to recycle it
Signed-off-by: Adriano Cordova adriano.cordova@canonical.com --- lib/efi_loader/efi_net.c | 81 +++++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 39 deletions(-)
diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c index 20d2c3a5f6..6bae5a5cd7 100644 --- a/lib/efi_loader/efi_net.c +++ b/lib/efi_loader/efi_net.c @@ -1152,29 +1152,35 @@ static int efi_netobj_init(struct efi_net_obj *netobj) }
/* 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; + if (!netobj->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]) + if (!netobj->receive_buffer) { + 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; + } + netobj->receive_buffer = receive_buffer; } - 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; + if (!netobj->receive_lengths) { + receive_lengths = calloc(ETH_PACKETS_BATCH_RECV, + sizeof(*receive_lengths)); + if (!receive_lengths) + goto out_of_resources; + netobj->receive_lengths = receive_lengths; + }
/* Hook net up to the device list */ efi_add_handle(&netobj->header); @@ -1291,12 +1297,6 @@ failure_to_add_protocol: printf("ERROR: Failure to add protocol\n"); return -1; out_of_resources: - 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 -1; } @@ -1357,22 +1357,35 @@ int efi_net_register(void *ctx, struct event *event)
// Find a slot for this efi_net_obj seq_num = -1; + // Try to recycle for (i = 0; i < MAX_EFI_NET_OBJS; i++) { - if (!net_objs[i]) { + if (net_objs[i] && !net_objs[i]->dev) { seq_num = i; break; } } + if (seq_num < 0) { + for (i = 0; i < MAX_EFI_NET_OBJS; i++) { + if (!net_objs[i]) { + seq_num = i; + break; + } + } + } if (seq_num < 0) return -1;
- netobj = calloc(1, sizeof(*netobj)); + if (!net_objs[seq_num]) { + netobj = calloc(1, sizeof(*netobj)); + net_objs[seq_num] = netobj; + } else { + netobj = net_objs[seq_num]; + } if (!netobj) goto out_of_resources;
netobj->dev = dev; netobj->efi_seq_num = seq_num; - net_objs[seq_num] = netobj; printf("efi_net registered device number %d\n", netobj->efi_seq_num); return 0; out_of_resources: @@ -1419,17 +1432,10 @@ int efi_net_unregister(void *ctx, struct event *event) if (!netobj) return 0;
- // Remove from the list - net_objs[i] = NULL; + // Mark as free in the list + netobj->dev = NULL;
if (efi_netobj_is_active(netobj)) { - free(netobj->transmit_buffer); - if (netobj->receive_buffer) - for (i = 0; i < ETH_PACKETS_BATCH_RECV; i++) - free(netobj->receive_buffer[i]); - free(netobj->receive_buffer); - free(netobj->receive_lengths); - ret = EFI_CALL(efi_close_event(netobj->wait_for_packet)); if (ret != EFI_SUCCESS) return -1; @@ -1450,9 +1456,6 @@ int efi_net_unregister(void *ctx, struct event *event) efi_free_pool(interface); }
- // Free the efi_net_obj - free(netobj); - return 0; }

This way the protocol and the handle can be externally provided. This comes in preparation to support ConnectController.
Signed-off-by: Adriano Cordova adriano.cordova@canonical.com --- lib/efi_loader/efi_net.c | 117 +++++++++++++++++++++++++-------------- 1 file changed, 74 insertions(+), 43 deletions(-)
diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c index 6bae5a5cd7..3e39cdf212 100644 --- a/lib/efi_loader/efi_net.c +++ b/lib/efi_loader/efi_net.c @@ -63,7 +63,7 @@ static int next_dp_entry; /** * struct efi_net_obj - EFI object representing a network interface * - * @header: EFI object header + * @handle: EFI object handle * @dev: net udevice * @net: simple network protocol interface * @net_mode: status of the network interface @@ -73,10 +73,10 @@ static int next_dp_entry; * @http_service_binding: Http service binding protocol interface */ struct efi_net_obj { - struct efi_object header; + efi_handle_t handle; struct udevice *dev; - struct efi_simple_network net; - struct efi_simple_network_mode net_mode; + 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; #if IS_ENABLED(CONFIG_EFI_IP4_CONFIG2_PROTOCOL) @@ -114,7 +114,7 @@ static struct efi_net_obj *net_objs[MAX_EFI_NET_OBJS]; */ static bool efi_netobj_is_active(struct efi_net_obj *netobj) { - if (!netobj || !efi_search_obj(&netobj->header)) + if (!netobj || !efi_search_obj(netobj->handle)) return false;
return true; @@ -132,7 +132,7 @@ 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) { + if (net_objs[i] && net_objs[i]->net == snp) { // Do not register duplicate devices return net_objs[i]; } @@ -1014,21 +1014,21 @@ efi_status_t efi_netobj_set_dp(struct efi_net_obj *netobj, struct efi_device_pat return EFI_OUT_OF_RESOURCES;
phandler = NULL; - efi_search_protocol(&netobj->header, &efi_guid_device_path, &phandler); + efi_search_protocol(netobj->handle, &efi_guid_device_path, &phandler);
// If the device path protocol is not yet installed, install it if (!phandler) goto add;
// If it is already installed, try to update it - ret = efi_reinstall_protocol_interface(&netobj->header, &efi_guid_device_path, + ret = efi_reinstall_protocol_interface(netobj->handle, &efi_guid_device_path, phandler->protocol_interface, new_net_dp); if (ret != EFI_SUCCESS) return ret;
return EFI_SUCCESS; add: - ret = efi_add_protocol(&netobj->header, &efi_guid_device_path, + ret = efi_add_protocol(netobj->handle, &efi_guid_device_path, new_net_dp); if (ret != EFI_SUCCESS) return ret; @@ -1050,7 +1050,7 @@ static struct efi_device_path *efi_netobj_get_dp(struct efi_net_obj *netobj) return NULL;
phandler = NULL; - efi_search_protocol(&netobj->header, &efi_guid_device_path, &phandler); + efi_search_protocol(netobj->handle, &efi_guid_device_path, &phandler);
if (phandler && phandler->protocol_interface) return efi_dp_dup(phandler->protocol_interface); @@ -1151,6 +1151,16 @@ static int efi_netobj_init(struct efi_net_obj *netobj) return 0; }
+ if (!netobj->net) + netobj->net = calloc(1, sizeof(*netobj->net)); + if (!netobj->net) + goto out_of_resources; + + if (!netobj->net_mode) + netobj->net_mode = calloc(1, sizeof(*netobj->net_mode)); + if (!netobj->net_mode) + goto out_of_resources; + /* Allocate an aligned transmit buffer */ if (!netobj->transmit_buffer) { transmit_buffer = calloc(1, PKTSIZE_ALIGN + PKTALIGN); @@ -1183,41 +1193,44 @@ static int efi_netobj_init(struct efi_net_obj *netobj) }
/* Hook net up to the device list */ - efi_add_handle(&netobj->header); + efi_add_handle(netobj->handle); + if (efi_link_dev(netobj->handle, dev)) + goto out_of_resources;
/* Fill in object data */ - r = efi_add_protocol(&netobj->header, &efi_net_guid, - &netobj->net); + r = efi_add_protocol(netobj->handle, &efi_net_guid, + netobj->net); if (r != EFI_SUCCESS) goto failure_to_add_protocol;
- r = efi_add_protocol(&netobj->header, &efi_pxe_base_code_protocol_guid, + 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; - 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; + 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, + 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->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; @@ -1267,7 +1280,7 @@ static int efi_netobj_init(struct efi_net_obj *netobj) * 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, + efi_network_timer_notify, netobj->net, NULL, &netobj->network_timer_event); if (r != EFI_SUCCESS) { printf("ERROR: Failed to register network event\n"); @@ -1281,13 +1294,13 @@ static int efi_netobj_init(struct efi_net_obj *netobj) }
#if IS_ENABLED(CONFIG_EFI_IP4_CONFIG2_PROTOCOL) - r = efi_ipconfig_register(&netobj->header, &netobj->ip4_config2); + r = efi_ipconfig_register(netobj->handle, &netobj->ip4_config2); if (r != EFI_SUCCESS) goto failure_to_add_protocol; #endif
#ifdef CONFIG_EFI_HTTP_PROTOCOL - r = efi_http_register(&netobj->header, &netobj->http_service_binding); + r = efi_http_register(netobj->handle, &netobj->http_service_binding); if (r != EFI_SUCCESS) goto failure_to_add_protocol; #endif @@ -1384,6 +1397,12 @@ int efi_net_register(void *ctx, struct event *event) if (!netobj) goto out_of_resources;
+ netobj->handle = calloc(1, sizeof(*netobj->handle)); + if (!netobj->handle) { + free(netobj); + goto out_of_resources; + } + netobj->dev = dev; netobj->efi_seq_num = seq_num; printf("efi_net registered device number %d\n", netobj->efi_seq_num); @@ -1432,9 +1451,6 @@ int efi_net_unregister(void *ctx, struct event *event) if (!netobj) return 0;
- // Mark as free in the list - netobj->dev = NULL; - if (efi_netobj_is_active(netobj)) { ret = EFI_CALL(efi_close_event(netobj->wait_for_packet)); if (ret != EFI_SUCCESS) @@ -1445,17 +1461,32 @@ int efi_net_unregister(void *ctx, struct event *event) return -1;
phandler = NULL; - efi_search_protocol(&netobj->header, &efi_guid_device_path, &phandler); + efi_search_protocol(netobj->handle, &efi_guid_device_path, &phandler); if (phandler && phandler->protocol_interface) interface = phandler->protocol_interface;
- ret = efi_delete_handle(&netobj->header); + ret = efi_delete_handle(netobj->handle); if (ret != EFI_SUCCESS) return -1;
efi_free_pool(interface); }
+ 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; + netobj->net = NULL; + netobj->net_mode = NULL; + return 0; }

For clarity, put the allocation of an efi_net_obj in efi_netobj_alloc
Signed-off-by: Adriano Cordova adriano.cordova@canonical.com --- lib/efi_loader/efi_net.c | 122 ++++++++++++++++++++++++++------------- 1 file changed, 83 insertions(+), 39 deletions(-)
diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c index 3e39cdf212..98b9c8b82e 100644 --- a/lib/efi_loader/efi_net.c +++ b/lib/efi_loader/efi_net.c @@ -1335,44 +1335,26 @@ efi_status_t efi_net_init(void) }
/** - * efi_net_register() - register a net device - * - * This function is called when the device is probed + * efi_netobj_alloc() - allocate an efi_net_obj from either a simple + * network protocol interface or a net udevice * - * @ctx: context set at registration time - * @event: event - * Return: 0 on success, negative on error + * @handle: EFI handle + * @net: pointer to simple network protocol + * @dev: pointer to net udevice + * Return: pointer to EFI net object, NULL on error */ -int efi_net_register(void *ctx, struct event *event) +struct efi_net_obj *efi_netobj_alloc(efi_handle_t handle, + struct efi_simple_network *net, + struct udevice *dev) { - struct udevice *dev; - int seq_num; - enum uclass_id id; + int i, seq_num; struct efi_net_obj *netobj; - int i; - - dev = event->data.dm.dev; - if (!dev) { - /* No network device active, don't expose any */ - return 0; - } - - id = device_get_uclass_id(dev); - if (id != UCLASS_ETH) - return 0; - - for (i = 0; i < MAX_EFI_NET_OBJS; i++) { - if (net_objs[i] && net_objs[i]->dev == dev) { - // Do not register duplicate devices - return 0; - } - }
// Find a slot for this efi_net_obj seq_num = -1; // Try to recycle for (i = 0; i < MAX_EFI_NET_OBJS; i++) { - if (net_objs[i] && !net_objs[i]->dev) { + if (net_objs[i] && !net_objs[i]->net && !net_objs[i]->dev && !net_objs[i]->handle) { seq_num = i; break; } @@ -1386,7 +1368,7 @@ int efi_net_register(void *ctx, struct event *event) } } if (seq_num < 0) - return -1; + return NULL;
if (!net_objs[seq_num]) { netobj = calloc(1, sizeof(*netobj)); @@ -1395,21 +1377,83 @@ int efi_net_register(void *ctx, struct event *event) netobj = net_objs[seq_num]; } if (!netobj) - goto out_of_resources; + return NULL;
- netobj->handle = calloc(1, sizeof(*netobj->handle)); - if (!netobj->handle) { - free(netobj); - goto out_of_resources; + if (netobj->net) { + if (netobj->net->mode) + free(netobj->net->mode); + free(netobj->net); + } + + if (handle) { + netobj->handle = handle; + } else { + netobj->handle = calloc(1, sizeof(*netobj->handle)); + if (!netobj->handle) { + free(netobj); + return NULL; + } + } + + if (net) { + netobj->net = net; + netobj->net_mode = net->mode; + } else { + netobj->net = calloc(1, sizeof(*netobj->net)); + if (!netobj->net) { + free(netobj->handle); + free(netobj); + return NULL; + } }
netobj->dev = dev; netobj->efi_seq_num = seq_num; - printf("efi_net registered device number %d\n", netobj->efi_seq_num); + + printf("efi_net: allocated EFI net device %d\n", netobj->efi_seq_num); + + return netobj; +} + +/** + * efi_net_register() - register a net device + * + * This function is called when the device is probed + * + * @ctx: context set at registration time + * @event: event + * Return: 0 on success, negative on error + */ +int efi_net_register(void *ctx, struct event *event) +{ + struct udevice *dev; + enum uclass_id id; + struct efi_net_obj *netobj; + int i; + + dev = event->data.dm.dev; + if (!dev) { + /* No network device active, don't expose any */ + return 0; + } + + id = device_get_uclass_id(dev); + if (id != UCLASS_ETH) + return 0; + + for (i = 0; i < MAX_EFI_NET_OBJS; i++) { + if (net_objs[i] && net_objs[i]->dev == dev) { + // Do not register duplicate devices + return 0; + } + } + + netobj = efi_netobj_alloc(NULL, NULL, dev); + + if (!netobj) + return -1; + return 0; -out_of_resources: - printf("ERROR: Out of memory\n"); - return -1; }
/**

Call [dis]connect_controller for the efi_net_objs corresponding to U-Boot udevices
Signed-off-by: Adriano Cordova adriano.cordova@canonical.com --- include/efi_loader.h | 2 +- lib/efi_driver/efi_net_device.c | 2 +- lib/efi_loader/efi_net.c | 38 ++++++++++++++++++++------------- 3 files changed, 25 insertions(+), 17 deletions(-)
diff --git a/include/efi_loader.h b/include/efi_loader.h index 09a9f75e19..93f223389a 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -638,7 +638,7 @@ efi_status_t efi_gop_register(void); int efi_net_register(void *ctx, struct event *event); /* Called to unregister an EFI network device */ int efi_net_unregister(void *ctx, struct event *event); -/* Called to initialized registered network devices */ +/* Called to initialized registered EFI network devices */ 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 */ diff --git a/lib/efi_driver/efi_net_device.c b/lib/efi_driver/efi_net_device.c index 64a443d074..298eded265 100644 --- a/lib/efi_driver/efi_net_device.c +++ b/lib/efi_driver/efi_net_device.c @@ -26,7 +26,7 @@ static efi_status_t efi_net_bind_drv( { EFI_PRINT("%s: handle %p, interface %p\n", __func__, handle, interface);
- return EFI_UNSUPPORTED; + return EFI_SUCCESS; }
/** diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c index 98b9c8b82e..b5284ffe34 100644 --- a/lib/efi_loader/efi_net.c +++ b/lib/efi_loader/efi_net.c @@ -1117,7 +1117,7 @@ efi_status_t efi_net_do_start(void) int i, r;
for (i = 0; i < MAX_EFI_NET_OBJS; i++) { - if (net_objs[i]) { + if (net_objs[i] && net_objs[i]->dev) { r = efi_netobj_start(net_objs[i]); if (r) return EFI_DEVICE_ERROR; @@ -1142,19 +1142,10 @@ static int efi_netobj_init(struct efi_net_obj *netobj) size_t *receive_lengths; int i, j;
- if (!netobj || efi_netobj_is_active(netobj)) + if (!netobj || !netobj->net || efi_netobj_is_active(netobj)) return 0;
dev = netobj->dev; - if (!dev) { - /* No network device active, don't expose any */ - return 0; - } - - if (!netobj->net) - netobj->net = calloc(1, sizeof(*netobj->net)); - if (!netobj->net) - goto out_of_resources;
if (!netobj->net_mode) netobj->net_mode = calloc(1, sizeof(*netobj->net_mode)); @@ -1199,7 +1190,7 @@ static int efi_netobj_init(struct efi_net_obj *netobj)
/* Fill in object data */ r = efi_add_protocol(netobj->handle, &efi_net_guid, - netobj->net); + netobj->net); if (r != EFI_SUCCESS) goto failure_to_add_protocol;
@@ -1247,12 +1238,16 @@ static int efi_netobj_init(struct efi_net_obj *netobj) netobj->pxe.set_packets = efi_pxe_base_code_set_packets; netobj->pxe.mode = &netobj->pxe_mode;
+ ret = EFI_CALL(efi_connect_controller(net_objs[i]->handle, NULL, NULL, 0)); + if (ret != 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; dhcp_cache[i].is_valid && j < 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; @@ -1304,7 +1299,6 @@ static int efi_netobj_init(struct efi_net_obj *netobj) if (r != EFI_SUCCESS) goto failure_to_add_protocol; #endif - printf("efi_net init device number %d\n", netobj->efi_seq_num); return 0; failure_to_add_protocol: printf("ERROR: Failure to add protocol\n"); @@ -1322,6 +1316,7 @@ out_of_resources: efi_status_t efi_net_init(void) { int i, r; + efi_status_t ret;
for (i = 0; i < MAX_EFI_NET_OBJS; i++) { if (net_objs[i]) { @@ -1429,7 +1424,8 @@ int efi_net_register(void *ctx, struct event *event) struct udevice *dev; enum uclass_id id; struct efi_net_obj *netobj; - int i; + efi_status_t ret; + int i, r;
dev = event->data.dm.dev; if (!dev) { @@ -1453,6 +1449,14 @@ int efi_net_register(void *ctx, struct event *event) 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; + } + } + return 0; }
@@ -1496,6 +1500,10 @@ int efi_net_unregister(void *ctx, struct event *event) return 0;
if (efi_netobj_is_active(netobj)) { + 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;

Create an ETH_UCLASS udevice and bind it to a driver
Signed-off-by: Adriano Cordova adriano.cordova@canonical.com --- include/net-common.h | 2 ++ net/eth-uclass.c | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+)
diff --git a/include/net-common.h b/include/net-common.h index 1d507b13b0..ba343af4c6 100644 --- a/include/net-common.h +++ b/include/net-common.h @@ -203,6 +203,8 @@ int eth_receive(void *packet, int length); /* Receive a packet*/ extern void (*push_packet)(void *packet, int length); #endif int eth_rx(void); /* Check for received packets */ +int eth_create_device(struct udevice *parent, const char *drv_name, + const char *name, struct udevice **devp);
/** * reset_phy() - Reset the Ethernet PHY diff --git a/net/eth-uclass.c b/net/eth-uclass.c index 5555f82f23..952df419b7 100644 --- a/net/eth-uclass.c +++ b/net/eth-uclass.c @@ -12,10 +12,12 @@ #include <dm.h> #include <env.h> #include <log.h> +#include <malloc.h> #include <net.h> #include <nvmem.h> #include <asm/global_data.h> #include <dm/device-internal.h> +#include <dm/lists.h> #include <dm/uclass-internal.h> #include <net/pcap.h> #include "eth_internal.h" @@ -528,6 +530,36 @@ int eth_initialize(void) return num_devices; }
+int eth_create_device(struct udevice *parent, const char *drv_name, + const char *name, struct udevice **devp) +{ + char temp[40], *str; + int devnum; + struct uclass *uc; + int ret; + + ret = uclass_get(UCLASS_ETH, &uc); + if (ret) + return -1; + + devnum = uclass_find_next_free_seq(uc); + if (devnum < 0) + return -1; + + snprintf(temp, sizeof(temp), "%s_eth%d", name, devnum); + temp[sizeof(temp) - 1] = '\0'; + str = strdup(temp); + + ret = device_bind_driver(parent, drv_name, str, devp); + if (ret) { + free(str); + return ret; + } + device_set_name_alloced(*devp); + + return 0; +} + static int eth_post_bind(struct udevice *dev) { struct eth_uclass_priv *priv = uclass_get_priv(dev->uclass);

UCLASS_ETH "efi_netdev" U-Boot driver. This driver can control a handle with a simple_network_protocol installed and working. See efi_netdev_create.
Signed-off-by: Adriano Cordova adriano.cordova@canonical.com --- include/efi_loader.h | 13 +++ lib/efi_driver/efi_net_device.c | 159 ++++++++++++++++++++++++++++++++ 2 files changed, 172 insertions(+)
diff --git a/include/efi_loader.h b/include/efi_loader.h index 93f223389a..3bb95cfeb9 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -634,6 +634,18 @@ int efi_disk_create_partitions(efi_handle_t parent, struct blk_desc *desc, const char *pdevname); /* Called by bootefi to make GOP (graphical) interface available */ efi_status_t efi_gop_register(void); + +/** + * struct efi_netdev_plat - attributes of a network device + * + * @handle: handle of the controller on which this driver is installed + * @snp: simple network protocol proxied by this driver + */ +struct efi_netdev_plat { + efi_handle_t handle; + struct efi_simple_network *snp; + void *buffer; +}; /* Called to register an EFI network device */ int efi_net_register(void *ctx, struct event *event); /* Called to unregister an EFI network device */ @@ -647,6 +659,7 @@ efi_status_t efi_ipconfig_register(const efi_handle_t handle, /* 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); + /* 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_driver/efi_net_device.c b/lib/efi_driver/efi_net_device.c index 298eded265..af51b1f855 100644 --- a/lib/efi_driver/efi_net_device.c +++ b/lib/efi_driver/efi_net_device.c @@ -7,11 +7,146 @@ #include <dm.h> #include <efi_driver.h> #include <malloc.h> +#include <net.h> #include <dm/device-internal.h> #include <dm/root.h> #include <dm/tag.h> #include <dm/uclass-internal.h>
+#define DEFAULT_EFI_NET_BUFFER_SIZE 1024 + +int efi_net_start(struct udevice *dev) +{ + struct efi_netdev_plat *plat; + efi_status_t ret; + + plat = dev_get_plat(dev); + if (!plat || !plat->snp) + return -1; + + ret = plat->snp->start(plat->snp); + if (ret != EFI_SUCCESS) + return -1; + ret = plat->snp->initialize(plat->snp, 0, 0); + if (ret != EFI_SUCCESS) + return -1; + + return 0; +} + +int efi_net_send(struct udevice *dev, void *packet, int length) +{ + struct efi_netdev_plat *plat; + efi_status_t ret; + + plat = dev_get_plat(dev); + if (!plat || !plat->snp) + return -1; + + ret = plat->snp->transmit(plat->snp, 0, length, packet, NULL, NULL, NULL); + if (ret != EFI_SUCCESS) + return -1; + + return 0; +} + +int efi_net_recv(struct udevice *dev, int flags, uchar **packetp) +{ + struct efi_netdev_plat *plat; + efi_status_t ret; + size_t buffer_size; + + plat = dev_get_plat(dev); + if (!plat || !plat->snp) + return -1; + + if (plat->buffer) + free(plat->buffer); + + buffer_size = DEFAULT_EFI_NET_BUFFER_SIZE; + plat->buffer = calloc(1, buffer_size); + ret = plat->snp->receive(plat->snp, NULL, &buffer_size, plat->buffer, + NULL, NULL, NULL); + + if (ret == EFI_BUFFER_TOO_SMALL) { + free(plat->buffer); + plat->buffer = calloc(1, buffer_size); + ret = plat->snp->receive(plat->snp, NULL, &buffer_size, plat->buffer, + NULL, NULL, NULL); + } + + if (ret != EFI_SUCCESS || ret != EFI_NOT_READY) + return -1; + + *packetp = plat->buffer; + return buffer_size; +} + +void efi_net_stop(struct udevice *dev) +{ + struct efi_netdev_plat *plat; + + plat = dev_get_plat(dev); + if (!plat || !plat->snp) + return; + + plat->snp->stop(plat->snp); +} + +/** + * efi_netdev_create() - create a net udevice for a handle + * + * @handle: handle + * @interface: simple network protocol + * Return: status code + */ +static efi_status_t +efi_netdev_create(efi_handle_t handle, void *interface) +{ + struct udevice *dev = NULL, *parent = dm_root(); + efi_status_t ret; + char *name; + struct efi_netdev_plat *plat; + static int devnum; + + name = calloc(1, 18); /* strlen("efinet#2147483648") + 1 */ + if (!name) + return EFI_OUT_OF_RESOURCES; + sprintf(name, "efinet#%d", devnum); + devnum++; + + /* Create driver model udevice for the EFI block io device */ + if (eth_create_device(parent, "efi_netdev", name, &dev)) { + ret = EFI_OUT_OF_RESOURCES; + free(name); + goto err; + } + + plat = dev_get_plat(dev); + plat->handle = handle; + plat->snp = interface; + + if (efi_link_dev(handle, dev)) { + ret = EFI_OUT_OF_RESOURCES; + goto err; + } + + if (device_probe(dev)) { + ret = EFI_DEVICE_ERROR; + goto err; + } + EFI_PRINT("%s: net udevice '%s' created\n", __func__, dev->name); + + return EFI_SUCCESS; + +err: + efi_unlink_dev(handle); + if (dev) + device_unbind(dev); + + return ret; +} + /** * efi_net_bind_drv() - TODO * @@ -26,6 +161,14 @@ static efi_status_t efi_net_bind_drv( { EFI_PRINT("%s: handle %p, interface %p\n", __func__, handle, interface);
+ efi_status_t ret; + + if (!handle->dev) { + ret = efi_netdev_create(handle, interface); + if (ret != EFI_SUCCESS) + return ret; + } + return EFI_SUCCESS; }
@@ -71,6 +214,22 @@ efi_net_init_drv(struct efi_driver_binding_extended_protocol *this) return EFI_SUCCESS; }
+/* Net device driver operators */ +static const struct eth_ops efi_eth_ops = { + .start = efi_net_start, + .send = efi_net_send, + .recv = efi_net_recv, + .stop = efi_net_stop, +}; + +/* Identify as net device driver */ +U_BOOT_DRIVER(efi_netdev) = { + .name = "efi_netdev", + .id = UCLASS_ETH, + .ops = &efi_eth_ops, + .plat_auto = sizeof(struct efi_netdev_plat), +}; + /* EFI driver operators */ static const struct efi_driver_ops driver_ops = { .protocol = &efi_net_guid,

This way when the stop() method of the EFI driver gets called whatever bind() did can be cleaned up.
Signed-off-by: Adriano Cordova adriano.cordova@canonical.com --- include/efi_driver.h | 4 ++++ lib/efi_driver/efi_uclass.c | 4 ++++ 2 files changed, 8 insertions(+)
diff --git a/include/efi_driver.h b/include/efi_driver.h index 63a95e4cf8..8f785f7945 100644 --- a/include/efi_driver.h +++ b/include/efi_driver.h @@ -36,6 +36,8 @@ struct efi_driver_binding_extended_protocol { * driver binding protocol. * @bind: Function called by the EFI uclass to attach the * driver to EFI driver to a handle. + * @unbind: Function called by the EFI uclass to detach the + * driver to EFI driver to a handle. */ struct efi_driver_ops { const efi_guid_t *protocol; @@ -43,6 +45,8 @@ struct efi_driver_ops { efi_status_t (*init)(struct efi_driver_binding_extended_protocol *this); efi_status_t (*bind)(struct efi_driver_binding_extended_protocol *this, efi_handle_t handle, void *interface); + efi_status_t (*unbind)(struct efi_driver_binding_extended_protocol *this, + efi_handle_t handle); };
#endif /* _EFI_DRIVER_H */ diff --git a/lib/efi_driver/efi_uclass.c b/lib/efi_driver/efi_uclass.c index 6553141f4e..393d62d3ba 100644 --- a/lib/efi_driver/efi_uclass.c +++ b/lib/efi_driver/efi_uclass.c @@ -247,6 +247,10 @@ static efi_status_t EFIAPI efi_uc_stop( if (ret != EFI_SUCCESS) log_err("Cannot free EFI memory pool\n");
+ ret = bp->ops->unbind(bp, controller_handle); + if (ret != EFI_SUCCESS) + goto out; + /* Detach driver from controller */ ret = efi_close_protocol(controller_handle, bp->ops->protocol, this->driver_binding_handle,

In case the handle was controlled by the "efi_netdev" driver, unlink the handle from the udevice and remove the udevice.
Signed-off-by: Adriano Cordova adriano.cordova@canonical.com --- lib/efi_driver/efi_net_device.c | 44 +++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+)
diff --git a/lib/efi_driver/efi_net_device.c b/lib/efi_driver/efi_net_device.c index af51b1f855..7113ca155e 100644 --- a/lib/efi_driver/efi_net_device.c +++ b/lib/efi_driver/efi_net_device.c @@ -9,6 +9,7 @@ #include <malloc.h> #include <net.h> #include <dm/device-internal.h> +#include <dm/lists.h> #include <dm/root.h> #include <dm/tag.h> #include <dm/uclass-internal.h> @@ -172,6 +173,48 @@ static efi_status_t efi_net_bind_drv( return EFI_SUCCESS; }
+/** + * efi_net_unbind_drv() - TODO + * + * @this: driver binding protocol + * @handle: handle + * Return: status code + */ +static efi_status_t efi_net_unbind_drv( + struct efi_driver_binding_extended_protocol *this, + efi_handle_t handle) +{ + EFI_PRINT("%s: handle %p\n", __func__, handle); + + struct driver *drv; + struct udevice *dev; + int r; + + if (!handle || !handle->dev) + return EFI_DEVICE_ERROR; + + drv = lists_driver_lookup_name("efi_netdev"); + if (!drv) { + log_err("Cannot find driver 'efi_netdev'\n"); + return EFI_DEVICE_ERROR; + } + + if (drv == handle->dev->driver) { + dev = handle->dev; + r = efi_unlink_dev(handle); + if (r) + return EFI_DEVICE_ERROR; + r = device_remove(dev, DM_REMOVE_NORMAL); + if (r) + return EFI_DEVICE_ERROR; + r = device_unbind(dev); + if (r) + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + /** * efi_net_init_drv() - initialize network device driver * @@ -235,6 +278,7 @@ static const struct efi_driver_ops driver_ops = { .protocol = &efi_net_guid, .init = efi_net_init_drv, .bind = efi_net_bind_drv, + .unbind = efi_net_unbind_drv, };
/* Identify as EFI driver */

Add efi_ipconfig_[un]register and efi_http_[un]register to [un]register the ip4_config2 and the http_service_binding protocol. And add the pxe protocol only if a dhcp_ack was received. The latter makes sense as pxe is otherwise not currently implemented.
Signed-off-by: Adriano Cordova adriano.cordova@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 | 212 ++++++++++++++++++++++------------ 4 files changed, 201 insertions(+), 85 deletions(-)
diff --git a/include/efi_loader.h b/include/efi_loader.h index 3bb95cfeb9..063c0a05fd 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -655,10 +655,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); diff --git a/lib/efi_loader/efi_http.c b/lib/efi_loader/efi_http.c index 20450604ec..8831ad922c 100644 --- a/lib/efi_loader/efi_http.c +++ b/lib/efi_loader/efi_http.c @@ -484,23 +484,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_CALL(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 10035e8a7b..ead2e147bd 100644 --- a/lib/efi_loader/efi_ipconfig.c +++ b/lib/efi_loader/efi_ipconfig.c @@ -193,12 +193,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; @@ -206,10 +212,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_CALL(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 b5284ffe34..c172da4e66 100644 --- a/lib/efi_loader/efi_net.c +++ b/lib/efi_loader/efi_net.c @@ -17,6 +17,7 @@
#include <efi_loader.h> #include <dm.h> +#include <dm/lists.h> #include <linux/sizes.h> #include <malloc.h> #include <vsprintf.h> @@ -29,6 +30,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;
static struct wget_http_info efi_wget_info = { .set_bootdev = false, @@ -80,10 +83,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; @@ -755,6 +758,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 * @@ -789,9 +794,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; + } } } } @@ -1058,6 +1066,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 r; +} + /** * efi_netobj_start() - start an efi net device * @@ -1069,7 +1123,7 @@ efi_status_t efi_netobj_start(struct efi_net_obj *netobj) { efi_status_t r = EFI_SUCCESS; struct efi_device_path *net_dp; - int i; +
if (!efi_netobj_is_active(netobj)) return r; @@ -1091,16 +1145,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 + (struct efi_ipv4_address *)&netobj->pxe_mode.subnet_mask, NULL, netobj->dev);
return 0; } @@ -1137,16 +1183,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; uchar **receive_buffer; size_t *receive_lengths; - 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) @@ -1193,11 +1243,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; @@ -1223,38 +1268,13 @@ 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; - - ret = EFI_CALL(efi_connect_controller(net_objs[i]->handle, NULL, NULL, 0)); - if (ret != EFI_SUCCESS) - return -1; + efi_net_add_pxe(netobj);
- /* - * 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; - } - } + r = EFI_CALL(efi_connect_controller(netobj->handle, NULL, NULL, 0)); + if (r != EFI_SUCCESS) + return -1;
+set_timers: /* * Create WaitForPacket event. */ @@ -1289,16 +1309,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: #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: return 0; failure_to_add_protocol: printf("ERROR: Failure to add protocol\n"); @@ -1316,7 +1346,6 @@ out_of_resources: efi_status_t efi_net_init(void) { int i, r; - efi_status_t ret;
for (i = 0; i < MAX_EFI_NET_OBJS; i++) { if (net_objs[i]) { @@ -1422,9 +1451,12 @@ 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; - efi_status_t ret; int i, r;
dev = event->data.dm.dev; @@ -1444,17 +1476,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; @@ -1471,7 +1510,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; @@ -1496,18 +1537,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; @@ -1517,22 +1562,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;

Let a simple network protocol interface be shared by more than one efi net device, but with only one owner. The owner should be the efi net device whose underlying udevice does not use the snp protocol for its ethernet ops. See efi_selftest_net_driver for an use.
Signed-off-by: Adriano Cordova adriano.cordova@canonical.com --- lib/efi_loader/efi_net.c | 50 +++++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 18 deletions(-)
diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c index c172da4e66..2f981b043a 100644 --- a/lib/efi_loader/efi_net.c +++ b/lib/efi_loader/efi_net.c @@ -104,6 +104,7 @@ struct efi_net_obj { */ struct efi_event *network_timer_event; int efi_seq_num; + bool snp_owner; };
static int curr_efi_net_obj; @@ -128,14 +129,17 @@ static bool efi_netobj_is_active(struct efi_net_obj *netobj) * * * @snp: pointer to the simple network protocol + * @bool: snp owner * Return: pointer to efi_net_obj, NULL on error */ -static struct efi_net_obj *efi_netobj_from_snp(struct efi_simple_network *snp) +static struct efi_net_obj *efi_netobj_from_snp(struct efi_simple_network *snp, + bool snp_owner) { int i;
for (i = 0; i < MAX_EFI_NET_OBJS; i++) { - if (net_objs[i] && net_objs[i]->net == snp) { + if (net_objs[i] && net_objs[i]->net == snp && + (!snp_owner || net_objs[i]->snp_owner)) { // Do not register duplicate devices return net_objs[i]; } @@ -165,7 +169,7 @@ static efi_status_t EFIAPI efi_net_start(struct efi_simple_network *this) goto out; }
- nt = efi_netobj_from_snp(this); + nt = efi_netobj_from_snp(this, true);
if (this->mode->state != EFI_NETWORK_STOPPED) { ret = EFI_ALREADY_STARTED; @@ -201,7 +205,7 @@ static efi_status_t EFIAPI efi_net_stop(struct efi_simple_network *this) goto out; }
- nt = efi_netobj_from_snp(this); + nt = efi_netobj_from_snp(this, true);
if (this->mode->state == EFI_NETWORK_STOPPED) { ret = EFI_NOT_STARTED; @@ -244,7 +248,7 @@ static efi_status_t EFIAPI efi_net_initialize(struct efi_simple_network *this, r = EFI_INVALID_PARAMETER; goto out; } - nt = efi_netobj_from_snp(this); + nt = efi_netobj_from_snp(this, true);
switch (this->mode->state) { case EFI_NETWORK_INITIALIZED: @@ -341,7 +345,7 @@ static efi_status_t EFIAPI efi_net_shutdown(struct efi_simple_network *this) ret = EFI_INVALID_PARAMETER; goto out; } - nt = efi_netobj_from_snp(this); + nt = efi_netobj_from_snp(this, true);
switch (this->mode->state) { case EFI_NETWORK_INITIALIZED: @@ -547,7 +551,7 @@ static efi_status_t EFIAPI efi_net_get_status(struct efi_simple_network *this, goto out; }
- nt = efi_netobj_from_snp(this); + nt = efi_netobj_from_snp(this, true);
switch (this->mode->state) { case EFI_NETWORK_STOPPED: @@ -608,7 +612,7 @@ static efi_status_t EFIAPI efi_net_transmit goto out; }
- nt = efi_netobj_from_snp(this); + nt = efi_netobj_from_snp(this, true);
/* We do not support jumbo packets */ if (buffer_size > PKTSIZE_ALIGN) { @@ -706,7 +710,7 @@ static efi_status_t EFIAPI efi_net_receive goto out; }
- nt = efi_netobj_from_snp(this); + nt = efi_netobj_from_snp(this, true);
switch (this->mode->state) { case EFI_NETWORK_STOPPED: @@ -864,7 +868,7 @@ static void EFIAPI efi_network_timer_notify(struct efi_event *event, if (!this || this->mode->state != EFI_NETWORK_INITIALIZED) goto out;
- nt = efi_netobj_from_snp(this); + nt = efi_netobj_from_snp(this, true); curr_efi_net_obj = nt->efi_seq_num;
// The following only happens if the net obj was removed but the event @@ -1194,7 +1198,7 @@ static int efi_netobj_init(struct efi_net_obj *netobj)
dev = netobj->dev;
- if (efi_netobj_is_active(netobj)) + if (efi_netobj_is_active(netobj) || !netobj->snp_owner) goto set_timers;
if (!netobj->net_mode) @@ -1408,6 +1412,7 @@ struct efi_net_obj *efi_netobj_alloc(efi_handle_t handle, free(netobj->net->mode); free(netobj->net); } + netobj->net = NULL;
if (handle) { netobj->handle = handle; @@ -1419,6 +1424,8 @@ struct efi_net_obj *efi_netobj_alloc(efi_handle_t handle, } }
+ netobj->snp_owner = efi_netobj_from_snp(net, true) ? false : true; + if (net) { netobj->net = net; netobj->net_mode = net->mode; @@ -1546,14 +1553,9 @@ int efi_net_unregister(void *ctx, struct event *event) return -1; }
+ interface = NULL; 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;
@@ -1569,6 +1571,15 @@ int efi_net_unregister(void *ctx, struct event *event) } }
+ if (netobj->snp_owner) { + 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; + } + #if IS_ENABLED(CONFIG_EFI_IP4_CONFIG2_PROTOCOL) if (netobj->ip4_config2) { r = efi_ipconfig_unregister(netobj->handle, netobj->ip4_config2); @@ -1591,8 +1602,11 @@ int efi_net_unregister(void *ctx, struct event *event) ret = efi_delete_handle(netobj->handle); if (ret != EFI_SUCCESS) return -1; + } + + efi_free_pool(interface);
- efi_free_pool(interface); + if (netobj->snp_owner) { if (netobj->net) free(netobj->net); if (netobj->net_mode)

Count the number of simple_network_protocol's
Signed-off-by: Adriano Cordova adriano.cordova@canonical.com --- lib/efi_selftest/Makefile | 2 +- lib/efi_selftest/efi_selftest_net_device.c | 65 ++++++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 lib/efi_selftest/efi_selftest_net_device.c
diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile index 17fbfad116..f7e8f02cb2 100644 --- a/lib/efi_selftest/Makefile +++ b/lib/efi_selftest/Makefile @@ -51,7 +51,7 @@ efi_selftest_variables_runtime.o \ efi_selftest_watchdog.o
obj-$(CONFIG_EFI_ECPT) += efi_selftest_ecpt.o -obj-$(CONFIG_NETDEVICES) += efi_selftest_snp.o +obj-$(CONFIG_NETDEVICES) += efi_selftest_snp.o efi_selftest_net_device.o obj-$(CONFIG_EFI_HTTP_PROTOCOL) += efi_selftest_http.o obj-$(CONFIG_EFI_HTTP_PROTOCOL) += efi_selftest_ipconfig.o
diff --git a/lib/efi_selftest/efi_selftest_net_device.c b/lib/efi_selftest/efi_selftest_net_device.c new file mode 100644 index 0000000000..51963eb328 --- /dev/null +++ b/lib/efi_selftest/efi_selftest_net_device.c @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * efi_selftest_net_device + * + */ + +#include <efi_selftest.h> +#include <net.h> + +static struct efi_boot_services *boottime; + +/* + * Setup unit test. + * + * @handle: handle of the loaded image + * @systable: system table + * Return: EFI_ST_SUCCESS for success + */ +static int setup(const efi_handle_t handle, + const struct efi_system_table *systable) +{ + efi_uintn_t num_handles; + efi_handle_t *handles; + + boottime = systable->boottime; + + num_handles = 0; + boottime->locate_handle_buffer(BY_PROTOCOL, &efi_net_guid, + NULL, &num_handles, &handles); + efi_st_printf("Detected %u active EFI net devices\n", (unsigned int)num_handles); + + return EFI_ST_SUCCESS; +} + +/* + * Execute unit test. + * + * + * Return: EFI_ST_SUCCESS for success + */ +static int execute(void) +{ + return EFI_ST_SUCCESS; +} + +/* + * Tear down unit test. + * + * Return: EFI_ST_SUCCESS for success + */ +static int teardown(void) +{ + int exit_status = EFI_ST_SUCCESS; + + return exit_status; +} + +EFI_UNIT_TEST(netdevices) = { + .name = "netdevices", + .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT, + .setup = setup, + .execute = execute, + .teardown = teardown, + .on_request = true, +};

check that the number of ethernet udevices matches the number of simple network protocols
Signed-off-by: Adriano Cordova adriano.cordova@canonical.com --- test/py/tests/test_efi_selftest.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+)
diff --git a/test/py/tests/test_efi_selftest.py b/test/py/tests/test_efi_selftest.py index 310d8ed294..af9992a6f7 100644 --- a/test/py/tests/test_efi_selftest.py +++ b/test/py/tests/test_efi_selftest.py @@ -195,3 +195,24 @@ def test_efi_selftest_tcg2(u_boot_console): if u_boot_console.p.expect(['Summary: 0 failures', 'Press any key']): raise Exception('Failures occurred during the EFI selftest') u_boot_console.restart_uboot() + +@pytest.mark.buildconfigspec('cmd_bootefi_selftest') +@pytest.mark.buildconfigspec('cmd_dm') +def test_efi_selftest_count_netdevices(u_boot_console): + """Test the EFI net device driver + + u_boot_console -- U-Boot console + + This function counts the number of ETH_UCLASS probed udevices + calls the EFI net device selftest to check that the EFI driver + sees the same. + """ + u_boot_console.restart_uboot() + response = u_boot_console.run_command('dm tree') + lines = response.splitlines()[2:] + ethernet_count = sum(1 for line in lines if line.strip().startswith('ethernet') and '[ + ]' in line) + + u_boot_console.run_command(cmd='setenv efi_selftest netdevices') + u_boot_console.run_command('bootefi selftest', wait_for_prompt=False) + if u_boot_console.p.expect([f'Detected {ethernet_count} active EFI net devices']): + raise Exception('Failures occurred during the EFI selftest')

Hi Adriano,
On Wed, 22 Jan 2025 at 10:10, Adriano Cordova adrianox@gmail.com wrote:
check that the number of ethernet udevices matches the number of simple network protocols
Signed-off-by: Adriano Cordova adriano.cordova@canonical.com
test/py/tests/test_efi_selftest.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+)
diff --git a/test/py/tests/test_efi_selftest.py b/test/py/tests/test_efi_selftest.py index 310d8ed294..af9992a6f7 100644 --- a/test/py/tests/test_efi_selftest.py +++ b/test/py/tests/test_efi_selftest.py @@ -195,3 +195,24 @@ def test_efi_selftest_tcg2(u_boot_console): if u_boot_console.p.expect(['Summary: 0 failures', 'Press any key']): raise Exception('Failures occurred during the EFI selftest') u_boot_console.restart_uboot()
+@pytest.mark.buildconfigspec('cmd_bootefi_selftest') +@pytest.mark.buildconfigspec('cmd_dm') +def test_efi_selftest_count_netdevices(u_boot_console):
- """Test the EFI net device driver
- u_boot_console -- U-Boot console
- This function counts the number of ETH_UCLASS probed udevices
- calls the EFI net device selftest to check that the EFI driver
- sees the same.
- """
- u_boot_console.restart_uboot()
We should avoid restarting U-Boot in a test unless there is a strong need. It makes things a lot slower!
- response = u_boot_console.run_command('dm tree')
- lines = response.splitlines()[2:]
- ethernet_count = sum(1 for line in lines if line.strip().startswith('ethernet') and '[ + ]' in line)
This test should be written in C, e.g:
struct udevice *dev; int count; uclass_foreach_dev(dev, UCLASS_ETH) if (device_active(dev)) count++;
- u_boot_console.run_command(cmd='setenv efi_selftest netdevices')
- u_boot_console.run_command('bootefi selftest', wait_for_prompt=False)
- if u_boot_console.p.expect([f'Detected {ethernet_count} active EFI net devices']):
raise Exception('Failures occurred during the EFI selftest')
Again, this should be written in C and use run_command().
Regards, Simon

On Thu, Jan 23, 2025 at 07:37:26AM -0700, Simon Glass wrote:
Hi Adriano,
On Wed, 22 Jan 2025 at 10:10, Adriano Cordova adrianox@gmail.com wrote:
check that the number of ethernet udevices matches the number of simple network protocols
Signed-off-by: Adriano Cordova adriano.cordova@canonical.com
test/py/tests/test_efi_selftest.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+)
diff --git a/test/py/tests/test_efi_selftest.py b/test/py/tests/test_efi_selftest.py index 310d8ed294..af9992a6f7 100644 --- a/test/py/tests/test_efi_selftest.py +++ b/test/py/tests/test_efi_selftest.py @@ -195,3 +195,24 @@ def test_efi_selftest_tcg2(u_boot_console): if u_boot_console.p.expect(['Summary: 0 failures', 'Press any key']): raise Exception('Failures occurred during the EFI selftest') u_boot_console.restart_uboot()
+@pytest.mark.buildconfigspec('cmd_bootefi_selftest') +@pytest.mark.buildconfigspec('cmd_dm') +def test_efi_selftest_count_netdevices(u_boot_console):
- """Test the EFI net device driver
- u_boot_console -- U-Boot console
- This function counts the number of ETH_UCLASS probed udevices
- calls the EFI net device selftest to check that the EFI driver
- sees the same.
- """
- u_boot_console.restart_uboot()
We should avoid restarting U-Boot in a test unless there is a strong need. It makes things a lot slower!
Yes, if we can not reboot here that would be good.
- response = u_boot_console.run_command('dm tree')
- lines = response.splitlines()[2:]
- ethernet_count = sum(1 for line in lines if line.strip().startswith('ethernet') and '[ + ]' in line)
This test should be written in C, e.g:
struct udevice *dev; int count; uclass_foreach_dev(dev, UCLASS_ETH) if (device_active(dev)) count++;
- u_boot_console.run_command(cmd='setenv efi_selftest netdevices')
- u_boot_console.run_command('bootefi selftest', wait_for_prompt=False)
- if u_boot_console.p.expect([f'Detected {ethernet_count} active EFI net devices']):
raise Exception('Failures occurred during the EFI selftest')
Again, this should be written in C and use run_command().
No, if these are written in python already, we can leave them in python. C is bad for strings.

Hi Tom,
On Thu, 23 Jan 2025 at 07:42, Tom Rini trini@konsulko.com wrote:
On Thu, Jan 23, 2025 at 07:37:26AM -0700, Simon Glass wrote:
Hi Adriano,
On Wed, 22 Jan 2025 at 10:10, Adriano Cordova adrianox@gmail.com wrote:
check that the number of ethernet udevices matches the number of simple network protocols
Signed-off-by: Adriano Cordova adriano.cordova@canonical.com
test/py/tests/test_efi_selftest.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+)
diff --git a/test/py/tests/test_efi_selftest.py b/test/py/tests/test_efi_selftest.py index 310d8ed294..af9992a6f7 100644 --- a/test/py/tests/test_efi_selftest.py +++ b/test/py/tests/test_efi_selftest.py @@ -195,3 +195,24 @@ def test_efi_selftest_tcg2(u_boot_console): if u_boot_console.p.expect(['Summary: 0 failures', 'Press any key']): raise Exception('Failures occurred during the EFI selftest') u_boot_console.restart_uboot()
+@pytest.mark.buildconfigspec('cmd_bootefi_selftest') +@pytest.mark.buildconfigspec('cmd_dm') +def test_efi_selftest_count_netdevices(u_boot_console):
- """Test the EFI net device driver
- u_boot_console -- U-Boot console
- This function counts the number of ETH_UCLASS probed udevices
- calls the EFI net device selftest to check that the EFI driver
- sees the same.
- """
- u_boot_console.restart_uboot()
We should avoid restarting U-Boot in a test unless there is a strong need. It makes things a lot slower!
Yes, if we can not reboot here that would be good.
- response = u_boot_console.run_command('dm tree')
- lines = response.splitlines()[2:]
- ethernet_count = sum(1 for line in lines if line.strip().startswith('ethernet') and '[ + ]' in line)
This test should be written in C, e.g:
struct udevice *dev; int count; uclass_foreach_dev(dev, UCLASS_ETH) if (device_active(dev)) count++;
- u_boot_console.run_command(cmd='setenv efi_selftest netdevices')
- u_boot_console.run_command('bootefi selftest', wait_for_prompt=False)
- if u_boot_console.p.expect([f'Detected {ethernet_count} active EFI net devices']):
raise Exception('Failures occurred during the EFI selftest')
Again, this should be written in C and use run_command().
No, if these are written in python already, we can leave them in python.
This is a new test. We should not be making things any worse.
C is bad for strings.
ut_assert_skip_to_line("Detected %d active EFI net devices", count);
For C tests we don't even need 'dm tree'.
This matters. The C test is easier to understand and runs in a tiny fraction of the time. Our CI tests have been getting slower and slower and it is due to this kind of test. I went to the effort of writing this up carefully [1].
So, NAK from me. Please use C tests where possible.
Regards, Simon
[1] https://docs.u-boot.org/en/latest/develop/tests_writing.html

On Fri, Jan 24, 2025 at 11:57:28AM -0700, Simon Glass wrote:
Hi Tom,
On Thu, 23 Jan 2025 at 07:42, Tom Rini trini@konsulko.com wrote:
On Thu, Jan 23, 2025 at 07:37:26AM -0700, Simon Glass wrote:
Hi Adriano,
On Wed, 22 Jan 2025 at 10:10, Adriano Cordova adrianox@gmail.com wrote:
check that the number of ethernet udevices matches the number of simple network protocols
Signed-off-by: Adriano Cordova adriano.cordova@canonical.com
test/py/tests/test_efi_selftest.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+)
diff --git a/test/py/tests/test_efi_selftest.py b/test/py/tests/test_efi_selftest.py index 310d8ed294..af9992a6f7 100644 --- a/test/py/tests/test_efi_selftest.py +++ b/test/py/tests/test_efi_selftest.py @@ -195,3 +195,24 @@ def test_efi_selftest_tcg2(u_boot_console): if u_boot_console.p.expect(['Summary: 0 failures', 'Press any key']): raise Exception('Failures occurred during the EFI selftest') u_boot_console.restart_uboot()
+@pytest.mark.buildconfigspec('cmd_bootefi_selftest') +@pytest.mark.buildconfigspec('cmd_dm') +def test_efi_selftest_count_netdevices(u_boot_console):
- """Test the EFI net device driver
- u_boot_console -- U-Boot console
- This function counts the number of ETH_UCLASS probed udevices
- calls the EFI net device selftest to check that the EFI driver
- sees the same.
- """
- u_boot_console.restart_uboot()
We should avoid restarting U-Boot in a test unless there is a strong need. It makes things a lot slower!
Yes, if we can not reboot here that would be good.
- response = u_boot_console.run_command('dm tree')
- lines = response.splitlines()[2:]
- ethernet_count = sum(1 for line in lines if line.strip().startswith('ethernet') and '[ + ]' in line)
This test should be written in C, e.g:
struct udevice *dev; int count; uclass_foreach_dev(dev, UCLASS_ETH) if (device_active(dev)) count++;
- u_boot_console.run_command(cmd='setenv efi_selftest netdevices')
- u_boot_console.run_command('bootefi selftest', wait_for_prompt=False)
- if u_boot_console.p.expect([f'Detected {ethernet_count} active EFI net devices']):
raise Exception('Failures occurred during the EFI selftest')
Again, this should be written in C and use run_command().
No, if these are written in python already, we can leave them in python.
This is a new test. We should not be making things any worse.
C is bad for strings.
ut_assert_skip_to_line("Detected %d active EFI net devices", count);
For C tests we don't even need 'dm tree'.
This matters. The C test is easier to understand and runs in a tiny fraction of the time. Our CI tests have been getting slower and slower and it is due to this kind of test. I went to the effort of writing this up carefully [1].
So, NAK from me. Please use C tests where possible.
Regards, Simon
[1] https://docs.u-boot.org/en/latest/develop/tests_writing.html
I suppose re-doing these in C would be a good test to see if writing tests in C rather than Python is easier or not. But given that our C tests are also randomly failing (the hash md5 thing) that's not the strongest endorsement of them either.

Hi Tom,
On Fri, 24 Jan 2025 at 12:17, Tom Rini trini@konsulko.com wrote:
On Fri, Jan 24, 2025 at 11:57:28AM -0700, Simon Glass wrote:
Hi Tom,
On Thu, 23 Jan 2025 at 07:42, Tom Rini trini@konsulko.com wrote:
On Thu, Jan 23, 2025 at 07:37:26AM -0700, Simon Glass wrote:
Hi Adriano,
On Wed, 22 Jan 2025 at 10:10, Adriano Cordova adrianox@gmail.com wrote:
check that the number of ethernet udevices matches the number of simple network protocols
Signed-off-by: Adriano Cordova adriano.cordova@canonical.com
test/py/tests/test_efi_selftest.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+)
diff --git a/test/py/tests/test_efi_selftest.py b/test/py/tests/test_efi_selftest.py index 310d8ed294..af9992a6f7 100644 --- a/test/py/tests/test_efi_selftest.py +++ b/test/py/tests/test_efi_selftest.py @@ -195,3 +195,24 @@ def test_efi_selftest_tcg2(u_boot_console): if u_boot_console.p.expect(['Summary: 0 failures', 'Press any key']): raise Exception('Failures occurred during the EFI selftest') u_boot_console.restart_uboot()
+@pytest.mark.buildconfigspec('cmd_bootefi_selftest') +@pytest.mark.buildconfigspec('cmd_dm') +def test_efi_selftest_count_netdevices(u_boot_console):
- """Test the EFI net device driver
- u_boot_console -- U-Boot console
- This function counts the number of ETH_UCLASS probed udevices
- calls the EFI net device selftest to check that the EFI driver
- sees the same.
- """
- u_boot_console.restart_uboot()
We should avoid restarting U-Boot in a test unless there is a strong need. It makes things a lot slower!
Yes, if we can not reboot here that would be good.
- response = u_boot_console.run_command('dm tree')
- lines = response.splitlines()[2:]
- ethernet_count = sum(1 for line in lines if line.strip().startswith('ethernet') and '[ + ]' in line)
This test should be written in C, e.g:
struct udevice *dev; int count; uclass_foreach_dev(dev, UCLASS_ETH) if (device_active(dev)) count++;
- u_boot_console.run_command(cmd='setenv efi_selftest netdevices')
- u_boot_console.run_command('bootefi selftest', wait_for_prompt=False)
- if u_boot_console.p.expect([f'Detected {ethernet_count} active EFI net devices']):
raise Exception('Failures occurred during the EFI selftest')
Again, this should be written in C and use run_command().
No, if these are written in python already, we can leave them in python.
This is a new test. We should not be making things any worse.
C is bad for strings.
ut_assert_skip_to_line("Detected %d active EFI net devices", count);
For C tests we don't even need 'dm tree'.
This matters. The C test is easier to understand and runs in a tiny fraction of the time. Our CI tests have been getting slower and slower and it is due to this kind of test. I went to the effort of writing this up carefully [1].
So, NAK from me. Please use C tests where possible.
Regards, Simon
[1] https://docs.u-boot.org/en/latest/develop/tests_writing.html
I suppose re-doing these in C would be a good test to see if writing tests in C rather than Python is easier or not.
In this case it should be easy enough. Other tests are much easier to write in Python. Some are easier to write in C, if they need to check internal information.
But given that our C tests are also randomly failing (the hash md5 thing) that's not the strongest endorsement of them either.
Indeed. Hopefully this is just a problem with the test system in U-Boot, rather than a bug in the 'real' part of U-Boot.
Is it possible to bisect this problem?
Regards, Simon

On Fri, Jan 24, 2025 at 12:43:29PM -0700, Simon Glass wrote:
Hi Tom,
On Fri, 24 Jan 2025 at 12:17, Tom Rini trini@konsulko.com wrote:
On Fri, Jan 24, 2025 at 11:57:28AM -0700, Simon Glass wrote:
Hi Tom,
On Thu, 23 Jan 2025 at 07:42, Tom Rini trini@konsulko.com wrote:
On Thu, Jan 23, 2025 at 07:37:26AM -0700, Simon Glass wrote:
Hi Adriano,
On Wed, 22 Jan 2025 at 10:10, Adriano Cordova adrianox@gmail.com wrote:
check that the number of ethernet udevices matches the number of simple network protocols
Signed-off-by: Adriano Cordova adriano.cordova@canonical.com
test/py/tests/test_efi_selftest.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+)
diff --git a/test/py/tests/test_efi_selftest.py b/test/py/tests/test_efi_selftest.py index 310d8ed294..af9992a6f7 100644 --- a/test/py/tests/test_efi_selftest.py +++ b/test/py/tests/test_efi_selftest.py @@ -195,3 +195,24 @@ def test_efi_selftest_tcg2(u_boot_console): if u_boot_console.p.expect(['Summary: 0 failures', 'Press any key']): raise Exception('Failures occurred during the EFI selftest') u_boot_console.restart_uboot()
+@pytest.mark.buildconfigspec('cmd_bootefi_selftest') +@pytest.mark.buildconfigspec('cmd_dm') +def test_efi_selftest_count_netdevices(u_boot_console):
- """Test the EFI net device driver
- u_boot_console -- U-Boot console
- This function counts the number of ETH_UCLASS probed udevices
- calls the EFI net device selftest to check that the EFI driver
- sees the same.
- """
- u_boot_console.restart_uboot()
We should avoid restarting U-Boot in a test unless there is a strong need. It makes things a lot slower!
Yes, if we can not reboot here that would be good.
- response = u_boot_console.run_command('dm tree')
- lines = response.splitlines()[2:]
- ethernet_count = sum(1 for line in lines if line.strip().startswith('ethernet') and '[ + ]' in line)
This test should be written in C, e.g:
struct udevice *dev; int count; uclass_foreach_dev(dev, UCLASS_ETH) if (device_active(dev)) count++;
- u_boot_console.run_command(cmd='setenv efi_selftest netdevices')
- u_boot_console.run_command('bootefi selftest', wait_for_prompt=False)
- if u_boot_console.p.expect([f'Detected {ethernet_count} active EFI net devices']):
raise Exception('Failures occurred during the EFI selftest')
Again, this should be written in C and use run_command().
No, if these are written in python already, we can leave them in python.
This is a new test. We should not be making things any worse.
C is bad for strings.
ut_assert_skip_to_line("Detected %d active EFI net devices", count);
For C tests we don't even need 'dm tree'.
This matters. The C test is easier to understand and runs in a tiny fraction of the time. Our CI tests have been getting slower and slower and it is due to this kind of test. I went to the effort of writing this up carefully [1].
So, NAK from me. Please use C tests where possible.
Regards, Simon
[1] https://docs.u-boot.org/en/latest/develop/tests_writing.html
I suppose re-doing these in C would be a good test to see if writing tests in C rather than Python is easier or not.
In this case it should be easy enough. Other tests are much easier to write in Python. Some are easier to write in C, if they need to check internal information.
But given that our C tests are also randomly failing (the hash md5 thing) that's not the strongest endorsement of them either.
Indeed. Hopefully this is just a problem with the test system in U-Boot, rather than a bug in the 'real' part of U-Boot.
Is it possible to bisect this problem?
No, bisect'ing finds that the commit which added the test is the failing commit.

unbind ethernet udevices and count them to check that were properly removed also from the EFI network stack
Signed-off-by: Adriano Cordova adriano.cordova@canonical.com --- test/py/tests/test_efi_selftest.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+)
diff --git a/test/py/tests/test_efi_selftest.py b/test/py/tests/test_efi_selftest.py index af9992a6f7..2b7944d198 100644 --- a/test/py/tests/test_efi_selftest.py +++ b/test/py/tests/test_efi_selftest.py @@ -216,3 +216,27 @@ def test_efi_selftest_count_netdevices(u_boot_console): u_boot_console.run_command('bootefi selftest', wait_for_prompt=False) if u_boot_console.p.expect([f'Detected {ethernet_count} active EFI net devices']): raise Exception('Failures occurred during the EFI selftest') + +@pytest.mark.buildconfigspec('cmd_bootefi_selftest') +@pytest.mark.buildconfigspec('cmd_dm') +@pytest.mark.buildconfigspec('cmd_bind') +def test_efi_selftest_remove_netdevices(u_boot_console): + """Test the EFI net device driver + + u_boot_console -- U-Boot console + + This function removes all the ETH_UCLASS udevices and + calls the EFI net device selftest to check that the EFI driver + sees the same. + """ + u_boot_console.restart_uboot() + response = u_boot_console.run_command('dm tree') + lines = response.splitlines()[2:] + ethernet_count = sum(1 for line in lines if line.strip().startswith('ethernet') and '[ + ]' in line) + + for i in range(ethernet_count): + u_boot_console.run_command(f'unbind ethernet {i}') + u_boot_console.run_command(cmd='setenv efi_selftest netdevices') + u_boot_console.run_command('bootefi selftest', wait_for_prompt=False) + if u_boot_console.p.expect(['Detected 0 active EFI net devices']): + raise Exception('Failures occurred during the EFI selftest')

Test the 'efi_netdev' driver. Take an existing and working snp protocol, create a handle and install the interface in the handle, and call connect_controller.
Signed-off-by: Adriano Cordova adriano.cordova@canonical.com --- lib/efi_selftest/Makefile | 2 +- lib/efi_selftest/efi_selftest_net_driver.c | 310 +++++++++++++++++++++ 2 files changed, 311 insertions(+), 1 deletion(-) create mode 100644 lib/efi_selftest/efi_selftest_net_driver.c
diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile index f7e8f02cb2..df51e0cfd1 100644 --- a/lib/efi_selftest/Makefile +++ b/lib/efi_selftest/Makefile @@ -51,7 +51,7 @@ efi_selftest_variables_runtime.o \ efi_selftest_watchdog.o
obj-$(CONFIG_EFI_ECPT) += efi_selftest_ecpt.o -obj-$(CONFIG_NETDEVICES) += efi_selftest_snp.o efi_selftest_net_device.o +obj-$(CONFIG_NETDEVICES) += efi_selftest_snp.o efi_selftest_net_device.o efi_selftest_net_driver.o obj-$(CONFIG_EFI_HTTP_PROTOCOL) += efi_selftest_http.o obj-$(CONFIG_EFI_HTTP_PROTOCOL) += efi_selftest_ipconfig.o
diff --git a/lib/efi_selftest/efi_selftest_net_driver.c b/lib/efi_selftest/efi_selftest_net_driver.c new file mode 100644 index 0000000000..0bd130cb06 --- /dev/null +++ b/lib/efi_selftest/efi_selftest_net_driver.c @@ -0,0 +1,310 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * efi_selftest_net_driver + * + */ + +#include <efi_selftest.h> +#include <net.h> + +static struct efi_boot_services *boottime; +static efi_handle_t test_handle; +static struct efi_service_binding_protocol *http_service; +static struct efi_http_protocol *http; +static efi_handle_t http_protocol_handle; +static int callback_done; + +static const efi_guid_t efi_http_guid = EFI_HTTP_PROTOCOL_GUID; +static const efi_guid_t efi_http_service_binding_guid = EFI_HTTP_SERVICE_BINDING_PROTOCOL_GUID; + +/* + * Setup unit test. + * + * @handle: handle of the loaded image + * @systable: system table + * Return: EFI_ST_SUCCESS for success + */ +static int setup(const efi_handle_t handle, + const struct efi_system_table *systable) +{ + efi_status_t ret; + struct efi_http_config_data http_config; + struct efi_httpv4_access_point ipv4_node; + void *snp; + + boottime = systable->boottime; + + ret = boottime->locate_protocol(&efi_net_guid, + NULL, &snp); + if (ret != EFI_SUCCESS || !snp) { + efi_st_printf("No simple network protocol available, can't proceed with the test\n"); + return EFI_ST_SUCCESS; + } + + test_handle = NULL; + ret = boottime->install_protocol_interface(&test_handle, &efi_net_guid, + EFI_NATIVE_INTERFACE, snp); + if (ret != EFI_SUCCESS) { + efi_st_printf("Can't install simple network protocol\n"); + return EFI_ST_FAILURE; + } + + ret = boottime->connect_controller(test_handle, NULL, NULL, false); + if (ret != EFI_SUCCESS) { + efi_st_printf("Failed connecting controller\n"); + return EFI_ST_FAILURE; + } + + ret = boottime->open_protocol(test_handle, + &efi_http_service_binding_guid, + (void **)&http_service, 0, 0, + EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (ret != EFI_SUCCESS || !http_service) { + efi_st_printf("Failed opening http service binding protocol\n"); + return EFI_ST_FAILURE; + } + + http_protocol_handle = NULL; + ret = http_service->create_child(http_service, &http_protocol_handle); + if (ret != EFI_SUCCESS || !http_protocol_handle) { + efi_st_error("Failed to create an http service instance\n"); + return EFI_ST_FAILURE; + } + + ret = boottime->open_protocol(http_protocol_handle, &efi_http_guid, + (void **)&http, 0, 0, EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (ret != EFI_SUCCESS || !http) { + efi_st_error("Failed to open http protocol\n"); + return EFI_ST_FAILURE; + } + efi_st_printf("HTTP Service Binding: child created successfully\n"); + + http_config.http_version = HTTPVERSION11; + http_config.is_ipv6 = false; + http_config.access_point.ipv4_node = &ipv4_node; + ipv4_node.use_default_address = true; + + ret = http->configure(http, &http_config); + if (ret != EFI_SUCCESS) { + efi_st_error("Failed to configure http instance\n"); + return EFI_ST_FAILURE; + } + + return EFI_ST_SUCCESS; +} + +static void EFIAPI efi_test_http_callback(struct efi_event *event, void *context) +{ + callback_done = 1; +} + +/* + * Execute unit test. + * + * + * Return: EFI_ST_SUCCESS for success + */ +static int execute(void) +{ + efi_status_t ret; + struct efi_http_request_data request_data; + struct efi_http_message request_message; + struct efi_http_token request_token; + struct efi_http_response_data response_data; + struct efi_http_message response_message; + struct efi_http_token response_token; + enum efi_http_status_code status_code; + void *response_buffer; + efi_uintn_t len, sum; + char *url = "http://example.com/"; + u16 url_16[64]; + u16 *tmp; + + /* Setup may have failed */ + if (!http) { + efi_st_error("Cannot proceed with test after setup failure\n"); + return EFI_ST_FAILURE; + } + + tmp = url_16; + utf8_utf16_strcpy(&tmp, url); + request_data.url = url_16; + request_data.method = HTTP_METHOD_GET; + + request_message.data.request = &request_data; + request_message.header_count = 3; + request_message.body_length = 0; + request_message.body = NULL; + + /* request token */ + request_token.event = NULL; + request_token.status = EFI_NOT_READY; + request_token.message = &request_message; + callback_done = 0; + ret = boottime->create_event(EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + efi_test_http_callback, + NULL, + &request_token.event); + + if (ret != EFI_SUCCESS) { + efi_st_error("Failed to create request event\n"); + return EFI_ST_FAILURE; + } + + ret = http->request(http, &request_token); + + if (ret != EFI_SUCCESS) { + boottime->close_event(request_token.event); + efi_st_printf("Failed to proceed with the http request\n"); + return EFI_ST_SUCCESS; + } + + while (!callback_done) + http->poll(http); + + response_data.status_code = HTTP_STATUS_UNSUPPORTED_STATUS; + response_message.data.response = &response_data; + response_message.header_count = 0; + response_message.headers = NULL; + response_message.body_length = 0; + response_message.body = NULL; + response_token.event = NULL; + + ret = boottime->create_event(EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + efi_test_http_callback, + NULL, + &response_token.event); + + if (ret != EFI_SUCCESS) { + boottime->close_event(request_token.event); + efi_st_error("Failed to create response event\n"); + return EFI_ST_FAILURE; + } + + response_token.status = EFI_SUCCESS; + response_token.message = &response_message; + + callback_done = 0; + ret = http->response(http, &response_token); + + if (ret != EFI_SUCCESS) { + efi_st_error("Failed http first response\n"); + goto fail; + } + + while (!callback_done) + http->poll(http); + + if (response_message.data.response->status_code != HTTP_STATUS_200_OK) { + status_code = response_message.data.response->status_code; + if (status_code == HTTP_STATUS_404_NOT_FOUND) { + efi_st_error("File not found\n"); + } else { + efi_st_error("Bad http status %d\n", + response_message.data.response->status_code); + } + goto fail_free_hdr; + } + + ret = boottime->allocate_pool(EFI_LOADER_CODE, response_message.body_length, + &response_buffer); + if (ret != EFI_SUCCESS) { + efi_st_error("Failed allocating response buffer\n"); + goto fail_free_hdr; + } + + len = response_message.body_length; + sum = 0; + while (len) { + response_message.data.response = NULL; + response_message.header_count = 0; + response_message.headers = NULL; + response_message.body_length = len; + response_message.body = response_buffer + sum; + + response_token.message = &response_message; + response_token.status = EFI_NOT_READY; + + callback_done = 0; + ret = http->response(http, &response_token); + if (ret != EFI_SUCCESS) { + efi_st_error("Failed http second response\n"); + goto fail_free_buf; + } + + while (!callback_done) + http->poll(http); + + if (!response_message.body_length) + break; + + len -= response_message.body_length; + sum += response_message.body_length; + } + + if (len) + goto fail_free_buf; + + boottime->free_pool(response_buffer); + if (response_message.headers) + boottime->free_pool(response_message.headers); + boottime->close_event(request_token.event); + boottime->close_event(response_token.event); + efi_st_printf("Efi Http request executed successfully\n"); + return EFI_ST_SUCCESS; + +fail_free_buf: + boottime->free_pool(response_buffer); +fail_free_hdr: + if (response_message.headers) + boottime->free_pool(response_message.headers); +fail: + boottime->close_event(request_token.event); + boottime->close_event(response_token.event); + return EFI_ST_FAILURE; +} + +/* + * Tear down unit test. + * + * Return: EFI_ST_SUCCESS for success + */ +static int teardown(void) +{ + efi_status_t ret; + int exit_status = EFI_ST_SUCCESS; + + if (!http_service || !http_protocol_handle) { + efi_st_error("No handles to destroy http instance"); + exit_status = EFI_ST_FAILURE; + goto out; + } + + ret = http_service->destroy_child(http_service, http_protocol_handle); + if (ret != EFI_SUCCESS) { + efi_st_error("Failed to destroy http instance"); + exit_status = EFI_ST_FAILURE; + goto out; + } + efi_st_printf("HTTP Service Binding: child destroyed successfully\n"); + + ret = boottime->disconnect_controller(test_handle, NULL, NULL); + if (ret != EFI_SUCCESS) { + efi_st_printf("Failed disconnecting controller\n"); + return EFI_ST_FAILURE; + } + +out: + return exit_status; +} + +EFI_UNIT_TEST(netdriver) = { + .name = "netdriver", + .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT, + .setup = setup, + .execute = execute, + .teardown = teardown, + .on_request = true, +};
participants (3)
-
Adriano Cordova
-
Simon Glass
-
Tom Rini