[PATCH v6 00/14] efi_loader: efi http and ipconfig drivers

Add support for EFI_HTTP_PROTOCOL, EFI_HTTP_SERVICE_BINDING_PROTOCOL, and EFI_IP4_CONFIG2_PROTOCOL.
This series depends on the series 'wget: Expose wget to applications', also found at: https://github.com/0n41rd4/u-boot/commits/http-driver-wget
A branch with both series of patches, concatenated, is at: https://github.com/0n41rd4/u-boot/commits/efi-http-driver
Adriano Cordova (12): net: wget: let wget_with_dns work with dns disabled efi_loader: device_path: add definition of DEVICE_PATH_SUB_TYPE_MSG_IPV4 efi_loader: device_path: add efi_dp_from_ipv4 efi_api: add definitions for HTTP and IP4_CONFIG2 protocols efi_loader: efi_net: add efi_net_set_addr, efi_net_get_addr efi_loader: device_path: add support for HTTP device path efi_loader: net: set EFI bootdevice device path to HTTP when loaded from wget efi_loader: net: add support to send http requests and parse http headers efi_loader: efi_net: add EFI_IP4_CONFIG2_PROTOCOL efi_loader: efi_net: add EFI_HTTP_PROTOCOL efi_selftest: add test for HTTP protocol efi_selftest: add test for IPv4 Config2 protocol
Heinrich Schuchardt (2): efi_loader: add IPv4() to device path to text protocol lib: uuid: display HTTP and IPV4 Config II protocols
include/efi_api.h | 220 +++++++++ include/efi_loader.h | 41 ++ include/net-common.h | 7 +- lib/efi_loader/Kconfig | 17 + lib/efi_loader/Makefile | 2 + lib/efi_loader/efi_bootbin.c | 40 +- lib/efi_loader/efi_bootmgr.c | 2 +- lib/efi_loader/efi_device_path.c | 116 ++++- lib/efi_loader/efi_device_path_to_text.c | 23 + lib/efi_loader/efi_http.c | 548 +++++++++++++++++++++++ lib/efi_loader/efi_ipconfig.c | 214 +++++++++ lib/efi_loader/efi_net.c | 371 ++++++++++++++- lib/efi_selftest/Makefile | 2 + lib/efi_selftest/efi_selftest_http.c | 315 +++++++++++++ lib/efi_selftest/efi_selftest_ipconfig.c | 170 +++++++ lib/uuid.c | 14 + net/lwip/wget.c | 9 +- net/net-common.c | 2 +- net/wget.c | 36 +- 19 files changed, 2101 insertions(+), 48 deletions(-) create mode 100644 lib/efi_loader/efi_http.c create mode 100644 lib/efi_loader/efi_ipconfig.c create mode 100644 lib/efi_selftest/efi_selftest_http.c create mode 100644 lib/efi_selftest/efi_selftest_ipconfig.c

This was marked as TODO in the code: - Enable use of wget_with_dns even if CMD_DNS is disabled if the given uri has the ip address for the http server. - Move the check for CMD_DNS inside wget_with_dns. - Rename wget_with_dns to wget_do_request
Signed-off-by: Adriano Cordova adrianox@gmail.com Reviewed-by: Heinrich Schuchardt heinrich.schuchardt@canonical.com Reviewed-by: Jerome Forissier jerome.forissier@linaro.org --- include/net-common.h | 7 +++++-- lib/efi_loader/efi_bootmgr.c | 2 +- net/lwip/wget.c | 4 ++-- net/net-common.c | 2 +- net/wget.c | 34 ++++++++++++++++++++-------------- 5 files changed, 29 insertions(+), 20 deletions(-)
diff --git a/include/net-common.h b/include/net-common.h index c5e314b360..8fc1bac47f 100644 --- a/include/net-common.h +++ b/include/net-common.h @@ -501,13 +501,16 @@ int dhcp_run(ulong addr, const char *fname, bool autoload); int do_tftpb(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]);
/** - * wget_with_dns() - runs dns host IP address resulution before wget + * wget_do_request() - sends a wget request + * + * Sends a wget request, if DNS resolution is enabled it resolves the + * given uri. * * @dst_addr: destination address to download the file * @uri: uri string of target file of wget * Return: zero on success, negative if failed */ -int wget_with_dns(ulong dst_addr, char *uri); +int wget_do_request(ulong dst_addr, char *uri); /** * wget_validate_uri() - varidate the uri * diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c index 8c51a6ef2e..c6124c590d 100644 --- a/lib/efi_loader/efi_bootmgr.c +++ b/lib/efi_loader/efi_bootmgr.c @@ -479,7 +479,7 @@ static efi_status_t try_load_from_uri_path(struct efi_device_path_uri *uridp, }
image_addr = hextoul(s, NULL); - err = wget_with_dns(image_addr, uridp->uri); + err = wget_do_request(image_addr, uridp->uri); if (err < 0) { ret = EFI_INVALID_PARAMETER; goto err; diff --git a/net/lwip/wget.c b/net/lwip/wget.c index 83ae8a6d15..2becb02f7b 100644 --- a/net/lwip/wget.c +++ b/net/lwip/wget.c @@ -355,7 +355,7 @@ static int wget_loop(struct udevice *udev, ulong dst_addr, char *uri) return -1; }
-int wget_with_dns(ulong dst_addr, char *uri) +int wget_do_request(ulong dst_addr, char *uri) { eth_set_current();
@@ -389,7 +389,7 @@ int do_wget(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[]) return CMD_RET_FAILURE;
wget_info = &default_wget_info; - if (wget_with_dns(dst_addr, nurl)) + if (wget_do_request(dst_addr, nurl)) return CMD_RET_FAILURE;
return CMD_RET_SUCCESS; diff --git a/net/net-common.c b/net/net-common.c index 45288fe5f8..e01b0da7d7 100644 --- a/net/net-common.c +++ b/net/net-common.c @@ -23,5 +23,5 @@ struct wget_http_info *wget_info; int wget_request(ulong dst_addr, char *uri, struct wget_http_info *info) { wget_info = info ? info : &default_wget_info; - return wget_with_dns(dst_addr, uri); + return wget_do_request(dst_addr, uri); } diff --git a/net/wget.c b/net/wget.c index 5d70b7a82e..f3b43b06b8 100644 --- a/net/wget.c +++ b/net/wget.c @@ -535,8 +535,7 @@ void wget_start(void) wget_send(TCP_SYN, 0, 0, 0); }
-#if (IS_ENABLED(CONFIG_CMD_DNS)) -int wget_with_dns(ulong dst_addr, char *uri) +int wget_do_request(ulong dst_addr, char *uri) { int ret; char *s, *host_name, *file_name, *str_copy; @@ -555,24 +554,32 @@ int wget_with_dns(ulong dst_addr, char *uri) s = str_copy + strlen("http://"); host_name = strsep(&s, "/"); if (!s) { - log_err("Error: invalied uri, no file path\n"); ret = -EINVAL; goto out; } file_name = s;
- /* TODO: If the given uri has ip address for the http server, skip dns */ - net_dns_resolve = host_name; - net_dns_env_var = "httpserverip"; - if (net_loop(DNS) < 0) { - log_err("Error: dns lookup of %s failed, check setup\n", net_dns_resolve); - ret = -EINVAL; - goto out; - } - s = env_get("httpserverip"); - if (!s) { + host_name = strsep(&host_name, ":"); + + if (string_to_ip(host_name).s_addr) { + s = host_name; + } else { +#if IS_ENABLED(CONFIG_CMD_DNS) + net_dns_resolve = host_name; + net_dns_env_var = "httpserverip"; + if (net_loop(DNS) < 0) { + ret = -EINVAL; + goto out; + } + s = env_get("httpserverip"); + if (!s) { + ret = -EINVAL; + goto out; + } +#else ret = -EINVAL; goto out; +#endif }
strlcpy(net_boot_file_name, s, sizeof(net_boot_file_name)); @@ -586,7 +593,6 @@ out:
return ret < 0 ? ret : 0; } -#endif
/** * wget_validate_uri() - validate the uri for wget

Add definition for DEVICE_PATH_SUB_TYPE_MSG_IPV4 device path subtype.
Signed-off-by: Adriano Cordova adrianox@gmail.com Reviewed-by: Ilias Apalodimas ilias.apalodimas@linaro.org ---
(no changes since v3)
Changes in v3: - Removed alignment in struct efi_ipv4_address include/efi_api.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/include/efi_api.h b/include/efi_api.h index f07d074f93..99ee749522 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -616,6 +616,7 @@ struct efi_device_path_acpi_path { # define DEVICE_PATH_SUB_TYPE_MSG_SCSI 0x02 # define DEVICE_PATH_SUB_TYPE_MSG_USB 0x05 # define DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR 0x0b +# define DEVICE_PATH_SUB_TYPE_MSG_IPV4 0x0c # define DEVICE_PATH_SUB_TYPE_MSG_UART 0x0e # define DEVICE_PATH_SUB_TYPE_MSG_USB_CLASS 0x0f # define DEVICE_PATH_SUB_TYPE_MSG_USB_WWI 0x10 @@ -691,6 +692,22 @@ struct efi_device_path_uri { u8 uri[]; } __packed;
+struct efi_ipv4_address { + u8 ip_addr[4]; +}; + +struct efi_device_path_ipv4 { + struct efi_device_path dp; + struct efi_ipv4_address local_ip_address; + struct efi_ipv4_address remote_ip_address; + u16 local_port; + u16 remote_port; + u16 protocol; + u8 static_ip_address; + struct efi_ipv4_address gateway_ip_address; + struct efi_ipv4_address subnet_mask; +} __packed; + #define DEVICE_PATH_TYPE_MEDIA_DEVICE 0x04 # define DEVICE_PATH_SUB_TYPE_HARD_DRIVE_PATH 0x01 # define DEVICE_PATH_SUB_TYPE_CDROM_PATH 0x02

Add efi_dp_from_ipv4 to form a device path from an ipv4 address.
Signed-off-by: Adriano Cordova adrianox@gmail.com --- Changes in v6: - Complete documentation of efi_dp_from_ipv4 - Return NULL in efi_dp_from_ipv4 if allocation fails
Changes in v5: - Add function description to efi_dp_from_ipv4 - Allocate ipv4dp in the stack - Make efi_dp_from_ipv4 static - Remove u-boot log messages inside efi routine - Change (void *)(uintptr_t) to char *
Changes in v4: - Fix memcpy mistake
Changes in v3: - Remove some unnecessary void* casts. - Change sizeof(struct efi_device_path_ipv4) to sizeof(*ipv4dp) in efi_dp_from_ipv4. lib/efi_loader/efi_device_path.c | 47 ++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+)
diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index ee387e1dfd..6fcbc91e0b 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -974,6 +974,53 @@ struct efi_device_path __maybe_unused *efi_dp_from_eth(void) return start; }
+/** + * efi_dp_from_ipv4() - set device path from IPv4 address + * + * Set the device path to an ethernet device path as provided by + * efi_dp_from_eth() concatenated with a device path of subtype + * DEVICE_PATH_SUB_TYPE_MSG_IPV4, and an END node. + * + * @ip: IPv4 local address + * @mask: network mask + * @srv: IPv4 remote/server address + * 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_device_path *dp1, *dp2, *pos; + struct { + struct efi_device_path_ipv4 ipv4dp; + struct efi_device_path end; + } dp; + + memset(&dp.ipv4dp, 0, sizeof(dp.ipv4dp)); + dp.ipv4dp.dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; + dp.ipv4dp.dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_IPV4; + dp.ipv4dp.dp.length = sizeof(dp.ipv4dp); + dp.ipv4dp.protocol = 6; + if (ip) + memcpy(&dp.ipv4dp.local_ip_address, ip, sizeof(*ip)); + if (mask) + memcpy(&dp.ipv4dp.subnet_mask, mask, sizeof(*mask)); + if (srv) + memcpy(&dp.ipv4dp.remote_ip_address, srv, sizeof(*srv)); + pos = &dp.end; + memcpy(pos, &END, sizeof(END)); + + dp1 = efi_dp_from_eth(); + if (!dp1) + return NULL; + + dp2 = efi_dp_concat(dp1, (const struct efi_device_path *)&dp, 0); + + efi_free_pool(dp1); + + return dp2; +} + /* Construct a device-path for memory-mapped image */ struct efi_device_path *efi_dp_from_mem(uint32_t memory_type, uint64_t start_address,

On Thu, 28 Nov 2024 at 16:44, Adriano Cordova adrianox@gmail.com wrote:
Add efi_dp_from_ipv4 to form a device path from an ipv4 address.
Signed-off-by: Adriano Cordova adrianox@gmail.com
Changes in v6:
- Complete documentation of efi_dp_from_ipv4
- Return NULL in efi_dp_from_ipv4 if allocation fails
Changes in v5:
- Add function description to efi_dp_from_ipv4
- Allocate ipv4dp in the stack
- Make efi_dp_from_ipv4 static
- Remove u-boot log messages inside efi routine
- Change (void *)(uintptr_t) to char *
Changes in v4:
- Fix memcpy mistake
Changes in v3:
- Remove some unnecessary void* casts.
- Change sizeof(struct efi_device_path_ipv4) to sizeof(*ipv4dp) in efi_dp_from_ipv4.
lib/efi_loader/efi_device_path.c | 47 ++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+)
diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index ee387e1dfd..6fcbc91e0b 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -974,6 +974,53 @@ struct efi_device_path __maybe_unused *efi_dp_from_eth(void) return start; }
+/**
- efi_dp_from_ipv4() - set device path from IPv4 address
- Set the device path to an ethernet device path as provided by
- efi_dp_from_eth() concatenated with a device path of subtype
- DEVICE_PATH_SUB_TYPE_MSG_IPV4, and an END node.
- @ip: IPv4 local address
- @mask: network mask
- @srv: IPv4 remote/server address
- 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_device_path *dp1, *dp2, *pos;
struct {
struct efi_device_path_ipv4 ipv4dp;
struct efi_device_path end;
} dp;
memset(&dp.ipv4dp, 0, sizeof(dp.ipv4dp));
dp.ipv4dp.dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
dp.ipv4dp.dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_IPV4;
dp.ipv4dp.dp.length = sizeof(dp.ipv4dp);
dp.ipv4dp.protocol = 6;
if (ip)
memcpy(&dp.ipv4dp.local_ip_address, ip, sizeof(*ip));
if (mask)
memcpy(&dp.ipv4dp.subnet_mask, mask, sizeof(*mask));
if (srv)
memcpy(&dp.ipv4dp.remote_ip_address, srv, sizeof(*srv));
pos = &dp.end;
memcpy(pos, &END, sizeof(END));
dp1 = efi_dp_from_eth();
if (!dp1)
return NULL;
dp2 = efi_dp_concat(dp1, (const struct efi_device_path *)&dp, 0);
efi_free_pool(dp1);
return dp2;
+}
/* Construct a device-path for memory-mapped image */ struct efi_device_path *efi_dp_from_mem(uint32_t memory_type, uint64_t start_address, -- 2.43.0
Reviewed-by: Ilias Apalodimas ilias.apalodimas@linaro.org

From: Heinrich Schuchardt heinrich.schuchardt@canonical.com
Implement Ipv4() node support in the device path to text protocol.
Signed-off-by: Heinrich Schuchardt heinrich.schuchardt@canonical.com Signed-off-by: Adriano Cordova adrianox@gmail.com Reviewed-by: Ilias Apalodimas ilias.apalodimas@linaro.org ---
(no changes since v2) lib/efi_loader/efi_device_path_to_text.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+)
diff --git a/lib/efi_loader/efi_device_path_to_text.c b/lib/efi_loader/efi_device_path_to_text.c index 0c7b30a26e..481a9712d9 100644 --- a/lib/efi_loader/efi_device_path_to_text.c +++ b/lib/efi_loader/efi_device_path_to_text.c @@ -8,6 +8,7 @@ #include <blk.h> #include <efi_loader.h> #include <malloc.h> +#include <net.h>
#define MAC_OUTPUT_LEN 22 #define UNKNOWN_OUTPUT_LEN 23 @@ -170,6 +171,28 @@ static char *dp_msging(char *s, struct efi_device_path *dp)
break; } + case DEVICE_PATH_SUB_TYPE_MSG_IPV4: { + struct efi_device_path_ipv4 *idp = + (struct efi_device_path_ipv4 *)dp; + + s += sprintf(s, "IPv4(%pI4,", &idp->remote_ip_address); + switch (idp->protocol) { + case IPPROTO_TCP: + s += sprintf(s, "TCP,"); + case IPPROTO_UDP: + s += sprintf(s, "UDP,"); + default: + s += sprintf(s, "0x%x,", idp->protocol); + } + s += sprintf(s, idp->static_ip_address ? "Static" : "DHCP"); + s += sprintf(s, ",%pI4", &idp->local_ip_address); + if (idp->dp.length == sizeof(struct efi_device_path_ipv4)) + s += sprintf(s, ",%pI4,%pI4", &idp->gateway_ip_address, + &idp->subnet_mask); + s += sprintf(s, ")"); + + break; + } case DEVICE_PATH_SUB_TYPE_MSG_USB_CLASS: { struct efi_device_path_usb_class *ucdp = (struct efi_device_path_usb_class *)dp;

Add EFI definitions for EFI_IP4_CONFIG2_PROTOCOL, EFI_HTTP_SERVICE_BINDING_PROTOCOL, and EFI_HTTP_PROTOCOL. According to UEFI spec 2.10.
Signed-off-by: Adriano Cordova adrianox@gmail.com Reviewed-by: Ilias Apalodimas ilias.apalodimas@linaro.org ---
(no changes since v2)
include/efi_api.h | 203 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 203 insertions(+)
diff --git a/include/efi_api.h b/include/efi_api.h index 99ee749522..61c4eda8f8 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -1725,6 +1725,209 @@ struct efi_pxe_base_code_protocol { struct efi_pxe_mode *mode; };
+#define EFI_IP4_CONFIG2_PROTOCOL_GUID \ + EFI_GUID(0x5b446ed1, 0xe30b, 0x4faa, \ + 0x87, 0x1a, 0x36, 0x54, 0xec, 0xa3, 0x60, 0x80) + +enum efi_ip4_config2_data_type { + EFI_IP4_CONFIG2_DATA_TYPE_INTERFACEINFO, + EFI_IP4_CONFIG2_DATA_TYPE_POLICY, + EFI_IP4_CONFIG2_DATA_TYPE_MANUAL_ADDRESS, + EFI_IP4_CONFIG2_DATA_TYPE_GATEWAY, + EFI_IP4_CONFIG2_DATA_TYPE_DNSSERVER, + EFI_IP4_CONFIG2_DATA_TYPE_MAXIMUM, +}; + +struct efi_ip4_config2_protocol { + efi_status_t (EFIAPI * set_data)(struct efi_ip4_config2_protocol *this, + enum efi_ip4_config2_data_type data_type, + efi_uintn_t data_size, + void *data); + efi_status_t (EFIAPI * get_data)(struct efi_ip4_config2_protocol *this, + enum efi_ip4_config2_data_type data_type, + efi_uintn_t *data_size, + void *data); + efi_status_t (EFIAPI * register_data_notify)(struct efi_ip4_config2_protocol *this, + enum efi_ip4_config2_data_type data_type, + struct efi_event *event); + efi_status_t (EFIAPI * unregister_data_notify)(struct efi_ip4_config2_protocol *this, + enum efi_ip4_config2_data_type data_type, + struct efi_event *event); +}; + +struct efi_ip4_route_table { + struct efi_ipv4_address subnet_address; + struct efi_ipv4_address subnet_mask; + struct efi_ipv4_address gateway_address; +}; + +#define EFI_IP4_CONFIG2_INTERFACE_INFO_NAME_SIZE 32 + +struct efi_ip4_config2_interface_info { + u16 name[EFI_IP4_CONFIG2_INTERFACE_INFO_NAME_SIZE]; + u8 if_type; + u32 hw_address_size; + struct efi_mac_address hw_address; + struct efi_ipv4_address station_address; + struct efi_ipv4_address subnet_mask; + u32 route_table_size; + struct efi_ip4_route_table *route_table; +}; + +enum efi_ip4_config2_policy { + EFI_IP4_CONFIG2_POLICY_STATIC, + EFI_IP4_CONFIG2_POLICY_DHCP, + EFI_IP4_CONFIG2_POLICY_MAX +}; + +struct efi_ip4_config2_manual_address { + struct efi_ipv4_address address; + struct efi_ipv4_address subnet_mask; +}; + +#define EFI_HTTP_SERVICE_BINDING_PROTOCOL_GUID \ + EFI_GUID(0xbdc8e6af, 0xd9bc, 0x4379, \ + 0xa7, 0x2a, 0xe0, 0xc4, 0xe7, 0x5d, 0xae, 0x1c) + +struct efi_service_binding_protocol { + efi_status_t (EFIAPI * create_child)(struct efi_service_binding_protocol *this, + efi_handle_t *child_handle); + efi_status_t (EFIAPI * destroy_child)(struct efi_service_binding_protocol *this, + efi_handle_t child_handle); +}; + +#define EFI_HTTP_PROTOCOL_GUID \ + EFI_GUID(0x7A59B29B, 0x910B, 0x4171, \ + 0x82, 0x42, 0xA8, 0x5A, 0x0D, 0xF2, 0x5B, 0x5B) + +enum efi_http_version { + HTTPVERSION10, + HTTPVERSION11, + HTTPVERSIONUNSUPPORTED +}; + +struct efi_httpv4_access_point { + bool use_default_address; + struct efi_ipv4_address local_address; + struct efi_ipv4_address local_subnet; + u16 local_port; +}; + +union efi_http_access_point { + struct efi_httpv4_access_point *ipv4_node; + struct efi_httpv6_access_point *ipv6_node; +}; + +struct efi_http_config_data { + enum efi_http_version http_version; + u32 timeout; + bool is_ipv6; + union efi_http_access_point access_point; +}; + +enum efi_http_method { + HTTP_METHOD_GET, + HTTP_METHOD_POST, + HTTP_METHOD_PATCH, + HTTP_METHOD_OPTIONS, + HTTP_METHOD_CONNECT, + HTTP_METHOD_HEAD, + HTTP_METHOD_PUT, + HTTP_METHOD_DELETE, + HTTP_METHOD_TRACE, + HTTP_METHOD_MAX +}; + +enum efi_http_status_code { + HTTP_STATUS_UNSUPPORTED_STATUS = 0, + HTTP_STATUS_100_CONTINUE, + HTTP_STATUS_101_SWITCHING_PROTOCOLS, + HTTP_STATUS_200_OK, + HTTP_STATUS_201_CREATED, + HTTP_STATUS_202_ACCEPTED, + HTTP_STATUS_203_NON_AUTHORITATIVE_INFORMATION, + HTTP_STATUS_204_NO_CONTENT, + HTTP_STATUS_205_RESET_CONTENT, + HTTP_STATUS_206_PARTIAL_CONTENT, + HTTP_STATUS_300_MULTIPLE_CHOICES, + HTTP_STATUS_301_MOVED_PERMANENTLY, + HTTP_STATUS_302_FOUND, + HTTP_STATUS_303_SEE_OTHER, + HTTP_STATUS_304_NOT_MODIFIED, + HTTP_STATUS_305_USE_PROXY, + HTTP_STATUS_307_TEMPORARY_REDIRECT, + HTTP_STATUS_400_BAD_REQUEST, + HTTP_STATUS_401_UNAUTHORIZED, + HTTP_STATUS_402_PAYMENT_REQUIRED, + HTTP_STATUS_403_FORBIDDEN, + HTTP_STATUS_404_NOT_FOUND, + HTTP_STATUS_405_METHOD_NOT_ALLOWED, + HTTP_STATUS_406_NOT_ACCEPTABLE, + HTTP_STATUS_407_PROXY_AUTHENTICATION_REQUIRED, + HTTP_STATUS_408_REQUEST_TIME_OUT, + HTTP_STATUS_409_CONFLICT, + HTTP_STATUS_410_GONE, + HTTP_STATUS_411_LENGTH_REQUIRED, + HTTP_STATUS_412_PRECONDITION_FAILED, + HTTP_STATUS_413_REQUEST_ENTITY_TOO_LARGE, + HTTP_STATUS_414_REQUEST_URI_TOO_LARGE, + HTTP_STATUS_415_UNSUPPORTED_MEDIA_TYPE, + HTTP_STATUS_416_REQUESTED_RANGE_NOT_SATISFIED, + HTTP_STATUS_417_EXPECTATION_FAILED, + HTTP_STATUS_500_INTERNAL_SERVER_ERROR, + HTTP_STATUS_501_NOT_IMPLEMENTED, + HTTP_STATUS_502_BAD_GATEWAY, + HTTP_STATUS_503_SERVICE_UNAVAILABLE, + HTTP_STATUS_504_GATEWAY_TIME_OUT, + HTTP_STATUS_505_HTTP_VERSION_NOT_SUPPORTED, + HTTP_STATUS_308_PERMANENT_REDIRECT +}; + +struct efi_http_request_data { + enum efi_http_method method; + u16 *url; +}; + +struct efi_http_response_data { + enum efi_http_status_code status_code; +}; + +struct efi_http_header { + char *field_name; + char *field_value; +}; + +struct efi_http_message { + union { + struct efi_http_request_data *request; + struct efi_http_response_data *response; + } data; + efi_uintn_t header_count; + struct efi_http_header *headers; + efi_uintn_t body_length; + void *body; +}; + +struct efi_http_token { + struct efi_event *event; + efi_status_t status; + struct efi_http_message *message; +}; + +struct efi_http_protocol { + efi_status_t (EFIAPI * get_mode_data)(struct efi_http_protocol *this, + struct efi_http_config_data *data); + efi_status_t (EFIAPI * configure)(struct efi_http_protocol *this, + struct efi_http_config_data *data); + efi_status_t (EFIAPI * request)(struct efi_http_protocol *this, + struct efi_http_token *token); + efi_status_t (EFIAPI * cancel)(struct efi_http_protocol *this, + struct efi_http_token *token); + efi_status_t (EFIAPI * response)(struct efi_http_protocol *this, + struct efi_http_token *token); + efi_status_t (EFIAPI * poll)(struct efi_http_protocol *this); +}; + #define EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID \ EFI_GUID(0x964e5b22, 0x6459, 0x11d2, \ 0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b)

On Thu, 28 Nov 2024 at 16:44, Adriano Cordova adrianox@gmail.com wrote:
Add EFI definitions for EFI_IP4_CONFIG2_PROTOCOL, EFI_HTTP_SERVICE_BINDING_PROTOCOL, and EFI_HTTP_PROTOCOL. According to UEFI spec 2.10.
Signed-off-by: Adriano Cordova adrianox@gmail.com Reviewed-by: Ilias Apalodimas ilias.apalodimas@linaro.org
(no changes since v2)
include/efi_api.h | 203 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 203 insertions(+)
diff --git a/include/efi_api.h b/include/efi_api.h index 99ee749522..61c4eda8f8 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -1725,6 +1725,209 @@ struct efi_pxe_base_code_protocol { struct efi_pxe_mode *mode; };
+#define EFI_IP4_CONFIG2_PROTOCOL_GUID \
EFI_GUID(0x5b446ed1, 0xe30b, 0x4faa, \
0x87, 0x1a, 0x36, 0x54, 0xec, 0xa3, 0x60, 0x80)
+enum efi_ip4_config2_data_type {
EFI_IP4_CONFIG2_DATA_TYPE_INTERFACEINFO,
EFI_IP4_CONFIG2_DATA_TYPE_POLICY,
EFI_IP4_CONFIG2_DATA_TYPE_MANUAL_ADDRESS,
EFI_IP4_CONFIG2_DATA_TYPE_GATEWAY,
EFI_IP4_CONFIG2_DATA_TYPE_DNSSERVER,
EFI_IP4_CONFIG2_DATA_TYPE_MAXIMUM,
+};
+struct efi_ip4_config2_protocol {
efi_status_t (EFIAPI * set_data)(struct efi_ip4_config2_protocol *this,
enum efi_ip4_config2_data_type data_type,
efi_uintn_t data_size,
void *data);
efi_status_t (EFIAPI * get_data)(struct efi_ip4_config2_protocol *this,
enum efi_ip4_config2_data_type data_type,
efi_uintn_t *data_size,
void *data);
efi_status_t (EFIAPI * register_data_notify)(struct efi_ip4_config2_protocol *this,
enum efi_ip4_config2_data_type data_type,
struct efi_event *event);
efi_status_t (EFIAPI * unregister_data_notify)(struct efi_ip4_config2_protocol *this,
enum efi_ip4_config2_data_type data_type,
struct efi_event *event);
+};
+struct efi_ip4_route_table {
struct efi_ipv4_address subnet_address;
struct efi_ipv4_address subnet_mask;
struct efi_ipv4_address gateway_address;
+};
+#define EFI_IP4_CONFIG2_INTERFACE_INFO_NAME_SIZE 32
+struct efi_ip4_config2_interface_info {
u16 name[EFI_IP4_CONFIG2_INTERFACE_INFO_NAME_SIZE];
u8 if_type;
u32 hw_address_size;
struct efi_mac_address hw_address;
struct efi_ipv4_address station_address;
struct efi_ipv4_address subnet_mask;
u32 route_table_size;
struct efi_ip4_route_table *route_table;
+};
+enum efi_ip4_config2_policy {
EFI_IP4_CONFIG2_POLICY_STATIC,
EFI_IP4_CONFIG2_POLICY_DHCP,
EFI_IP4_CONFIG2_POLICY_MAX
+};
+struct efi_ip4_config2_manual_address {
struct efi_ipv4_address address;
struct efi_ipv4_address subnet_mask;
+};
+#define EFI_HTTP_SERVICE_BINDING_PROTOCOL_GUID \
EFI_GUID(0xbdc8e6af, 0xd9bc, 0x4379, \
0xa7, 0x2a, 0xe0, 0xc4, 0xe7, 0x5d, 0xae, 0x1c)
+struct efi_service_binding_protocol {
efi_status_t (EFIAPI * create_child)(struct efi_service_binding_protocol *this,
efi_handle_t *child_handle);
efi_status_t (EFIAPI * destroy_child)(struct efi_service_binding_protocol *this,
efi_handle_t child_handle);
+};
+#define EFI_HTTP_PROTOCOL_GUID \
EFI_GUID(0x7A59B29B, 0x910B, 0x4171, \
0x82, 0x42, 0xA8, 0x5A, 0x0D, 0xF2, 0x5B, 0x5B)
+enum efi_http_version {
HTTPVERSION10,
HTTPVERSION11,
HTTPVERSIONUNSUPPORTED
+};
+struct efi_httpv4_access_point {
bool use_default_address;
struct efi_ipv4_address local_address;
struct efi_ipv4_address local_subnet;
u16 local_port;
+};
+union efi_http_access_point {
struct efi_httpv4_access_point *ipv4_node;
struct efi_httpv6_access_point *ipv6_node;
+};
+struct efi_http_config_data {
enum efi_http_version http_version;
u32 timeout;
bool is_ipv6;
union efi_http_access_point access_point;
+};
+enum efi_http_method {
HTTP_METHOD_GET,
HTTP_METHOD_POST,
HTTP_METHOD_PATCH,
HTTP_METHOD_OPTIONS,
HTTP_METHOD_CONNECT,
HTTP_METHOD_HEAD,
HTTP_METHOD_PUT,
HTTP_METHOD_DELETE,
HTTP_METHOD_TRACE,
HTTP_METHOD_MAX
+};
+enum efi_http_status_code {
HTTP_STATUS_UNSUPPORTED_STATUS = 0,
HTTP_STATUS_100_CONTINUE,
HTTP_STATUS_101_SWITCHING_PROTOCOLS,
HTTP_STATUS_200_OK,
HTTP_STATUS_201_CREATED,
HTTP_STATUS_202_ACCEPTED,
HTTP_STATUS_203_NON_AUTHORITATIVE_INFORMATION,
HTTP_STATUS_204_NO_CONTENT,
HTTP_STATUS_205_RESET_CONTENT,
HTTP_STATUS_206_PARTIAL_CONTENT,
HTTP_STATUS_300_MULTIPLE_CHOICES,
HTTP_STATUS_301_MOVED_PERMANENTLY,
HTTP_STATUS_302_FOUND,
HTTP_STATUS_303_SEE_OTHER,
HTTP_STATUS_304_NOT_MODIFIED,
HTTP_STATUS_305_USE_PROXY,
HTTP_STATUS_307_TEMPORARY_REDIRECT,
HTTP_STATUS_400_BAD_REQUEST,
HTTP_STATUS_401_UNAUTHORIZED,
HTTP_STATUS_402_PAYMENT_REQUIRED,
HTTP_STATUS_403_FORBIDDEN,
HTTP_STATUS_404_NOT_FOUND,
HTTP_STATUS_405_METHOD_NOT_ALLOWED,
HTTP_STATUS_406_NOT_ACCEPTABLE,
HTTP_STATUS_407_PROXY_AUTHENTICATION_REQUIRED,
HTTP_STATUS_408_REQUEST_TIME_OUT,
HTTP_STATUS_409_CONFLICT,
HTTP_STATUS_410_GONE,
HTTP_STATUS_411_LENGTH_REQUIRED,
HTTP_STATUS_412_PRECONDITION_FAILED,
HTTP_STATUS_413_REQUEST_ENTITY_TOO_LARGE,
HTTP_STATUS_414_REQUEST_URI_TOO_LARGE,
HTTP_STATUS_415_UNSUPPORTED_MEDIA_TYPE,
HTTP_STATUS_416_REQUESTED_RANGE_NOT_SATISFIED,
HTTP_STATUS_417_EXPECTATION_FAILED,
HTTP_STATUS_500_INTERNAL_SERVER_ERROR,
HTTP_STATUS_501_NOT_IMPLEMENTED,
HTTP_STATUS_502_BAD_GATEWAY,
HTTP_STATUS_503_SERVICE_UNAVAILABLE,
HTTP_STATUS_504_GATEWAY_TIME_OUT,
HTTP_STATUS_505_HTTP_VERSION_NOT_SUPPORTED,
HTTP_STATUS_308_PERMANENT_REDIRECT
+};
+struct efi_http_request_data {
enum efi_http_method method;
u16 *url;
+};
+struct efi_http_response_data {
enum efi_http_status_code status_code;
+};
+struct efi_http_header {
char *field_name;
char *field_value;
+};
+struct efi_http_message {
union {
struct efi_http_request_data *request;
struct efi_http_response_data *response;
} data;
efi_uintn_t header_count;
struct efi_http_header *headers;
efi_uintn_t body_length;
void *body;
+};
+struct efi_http_token {
struct efi_event *event;
efi_status_t status;
struct efi_http_message *message;
+};
+struct efi_http_protocol {
efi_status_t (EFIAPI * get_mode_data)(struct efi_http_protocol *this,
struct efi_http_config_data *data);
efi_status_t (EFIAPI * configure)(struct efi_http_protocol *this,
struct efi_http_config_data *data);
efi_status_t (EFIAPI * request)(struct efi_http_protocol *this,
struct efi_http_token *token);
efi_status_t (EFIAPI * cancel)(struct efi_http_protocol *this,
struct efi_http_token *token);
efi_status_t (EFIAPI * response)(struct efi_http_protocol *this,
struct efi_http_token *token);
efi_status_t (EFIAPI * poll)(struct efi_http_protocol *this);
+};
#define EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID \ EFI_GUID(0x964e5b22, 0x6459, 0x11d2, \ 0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b) -- 2.43.0
Acked-by: Ilias Apalodimas ilias.apalodimas@linaro.org

Add the functions efi_net_set_addr and efi_net_get_addr to set and get the ip address from efi code in a network agnostic way. This could also go in net_common, or be compiled conditionally for each network stack.
Signed-off-by: Adriano Cordova adrianox@gmail.com --- (no changes since v5) Changes in v5: - When possible use sizeof in memcpy instead of hardcoded value
Changes in v4: - Removed unnecessary casts - Changed char *ipstr by char ipstr[].
(no changes since v2) include/efi_loader.h | 16 +++++ lib/efi_loader/efi_net.c | 125 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 141 insertions(+)
diff --git a/include/efi_loader.h b/include/efi_loader.h index 39809eac1b..612bc42816 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -125,6 +125,22 @@ static inline void efi_set_bootdev(const char *dev, const char *devnr, size_t buffer_size) { } #endif
+#if CONFIG_IS_ENABLED(NETDEVICES) && CONFIG_IS_ENABLED(EFI_LOADER) +void efi_net_get_addr(struct efi_ipv4_address *ip, + struct efi_ipv4_address *mask, + struct efi_ipv4_address *gw); +void efi_net_set_addr(struct efi_ipv4_address *ip, + struct efi_ipv4_address *mask, + struct efi_ipv4_address *gw); +#else +static inline void efi_net_get_addr(struct efi_ipv4_address *ip, + struct efi_ipv4_address *mask, + struct efi_ipv4_address *gw) { } +static inline void efi_net_set_addr(struct efi_ipv4_address *ip, + struct efi_ipv4_address *mask, + struct efi_ipv4_address *gw) { } +#endif + /* Maximum number of configuration tables */ #define EFI_MAX_CONFIGURATION_TABLES 16
diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c index 7cd536705f..3491d4c481 100644 --- a/lib/efi_loader/efi_net.c +++ b/lib/efi_loader/efi_net.c @@ -17,6 +17,7 @@
#include <efi_loader.h> #include <malloc.h> +#include <vsprintf.h> #include <net.h>
static const efi_guid_t efi_net_guid = EFI_SIMPLE_NETWORK_PROTOCOL_GUID; @@ -997,3 +998,127 @@ out_of_resources: printf("ERROR: Out of memory\n"); return EFI_OUT_OF_RESOURCES; } + +/** + * efi_net_get_addr() - get IP address information + * + * Copy the current IP address, mask, and gateway into the + * efi_ipv4_address structs pointed to by ip, mask and gw, + * respectively. + * + * @ip: pointer to an efi_ipv4_address struct to + * be filled with the current IP address + * @mask: pointer to an efi_ipv4_address struct to + * be filled with the current network mask + * @gw: pointer to an efi_ipv4_address struct to be + * filled with the current network gateway + */ +void efi_net_get_addr(struct efi_ipv4_address *ip, + struct efi_ipv4_address *mask, + struct efi_ipv4_address *gw) +{ +#ifdef CONFIG_NET_LWIP + char ipstr[] = "ipaddr\0\0"; + char maskstr[] = "netmask\0\0"; + char gwstr[] = "gatewayip\0\0"; + int idx; + struct in_addr tmp; + char *env; + + idx = dev_seq(eth_get_dev()); + + if (idx < 0 || idx > 99) { + log_err("unexpected idx %d\n", idx); + return; + } + + if (idx) { + sprintf(ipstr, "ipaddr%d", idx); + sprintf(maskstr, "netmask%d", idx); + sprintf(gwstr, "gatewayip%d", idx); + } + + env = env_get(ipstr); + if (env && ip) { + tmp = string_to_ip(env); + memcpy(ip, &tmp, sizeof(tmp)); + } + + env = env_get(maskstr); + if (env && mask) { + tmp = string_to_ip(env); + memcpy(mask, &tmp, sizeof(tmp)); + } + env = env_get(gwstr); + if (env && gw) { + tmp = string_to_ip(env); + memcpy(gw, &tmp, sizeof(tmp)); + } +#else + if (ip) + memcpy(ip, &net_ip, sizeof(net_ip)); + if (mask) + memcpy(mask, &net_netmask, sizeof(net_netmask)); +#endif +} + +/** + * efi_net_set_addr() - set IP address information + * + * Set the current IP address, mask, and gateway to the + * efi_ipv4_address structs pointed to by ip, mask and gw, + * respectively. + * + * @ip: pointer to new IP address + * @mask: pointer to new network mask to set + * @gw: pointer to new network gateway + */ +void efi_net_set_addr(struct efi_ipv4_address *ip, + struct efi_ipv4_address *mask, + struct efi_ipv4_address *gw) +{ +#ifdef CONFIG_NET_LWIP + char ipstr[] = "ipaddr\0\0"; + char maskstr[] = "netmask\0\0"; + char gwstr[] = "gatewayip\0\0"; + int idx; + struct in_addr *addr; + char tmp[46]; + + idx = dev_seq(eth_get_dev()); + + if (idx < 0 || idx > 99) { + log_err("unexpected idx %d\n", idx); + return; + } + + if (idx) { + sprintf(ipstr, "ipaddr%d", idx); + sprintf(maskstr, "netmask%d", idx); + sprintf(gwstr, "gatewayip%d", idx); + } + + if (ip) { + addr = (struct in_addr *)ip; + ip_to_string(*addr, tmp); + env_set(ipstr, tmp); + } + + if (mask) { + addr = (struct in_addr *)mask; + ip_to_string(*addr, tmp); + env_set(maskstr, tmp); + } + + if (gw) { + addr = (struct in_addr *)gw; + ip_to_string(*addr, tmp); + env_set(gwstr, tmp); + } +#else + if (ip) + memcpy(&net_ip, ip, sizeof(*ip)); + if (mask) + memcpy(&net_netmask, mask, sizeof(*mask)); +#endif +}

On 28.11.24 15:44, Adriano Cordova wrote:
Add the functions efi_net_set_addr and efi_net_get_addr to set and get the ip address from efi code in a network agnostic way. This could also go in net_common, or be compiled conditionally for each network stack.
Signed-off-by: Adriano Cordova adrianox@gmail.com
(no changes since v5) Changes in v5:
- When possible use sizeof in memcpy instead of hardcoded value
Changes in v4:
- Removed unnecessary casts
- Changed char *ipstr by char ipstr[].
(no changes since v2) include/efi_loader.h | 16 +++++ lib/efi_loader/efi_net.c | 125 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 141 insertions(+)
diff --git a/include/efi_loader.h b/include/efi_loader.h index 39809eac1b..612bc42816 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -125,6 +125,22 @@ static inline void efi_set_bootdev(const char *dev, const char *devnr, size_t buffer_size) { } #endif
+#if CONFIG_IS_ENABLED(NETDEVICES) && CONFIG_IS_ENABLED(EFI_LOADER) +void efi_net_get_addr(struct efi_ipv4_address *ip,
struct efi_ipv4_address *mask,
struct efi_ipv4_address *gw);
+void efi_net_set_addr(struct efi_ipv4_address *ip,
struct efi_ipv4_address *mask,
struct efi_ipv4_address *gw);
+#else +static inline void efi_net_get_addr(struct efi_ipv4_address *ip,
struct efi_ipv4_address *mask,
struct efi_ipv4_address *gw) { }
+static inline void efi_net_set_addr(struct efi_ipv4_address *ip,
struct efi_ipv4_address *mask,
struct efi_ipv4_address *gw) { }
+#endif
- /* Maximum number of configuration tables */ #define EFI_MAX_CONFIGURATION_TABLES 16
diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c index 7cd536705f..3491d4c481 100644 --- a/lib/efi_loader/efi_net.c +++ b/lib/efi_loader/efi_net.c @@ -17,6 +17,7 @@
#include <efi_loader.h> #include <malloc.h> +#include <vsprintf.h> #include <net.h>
static const efi_guid_t efi_net_guid = EFI_SIMPLE_NETWORK_PROTOCOL_GUID; @@ -997,3 +998,127 @@ out_of_resources: printf("ERROR: Out of memory\n"); return EFI_OUT_OF_RESOURCES; }
+/**
- efi_net_get_addr() - get IP address information
- Copy the current IP address, mask, and gateway into the
- efi_ipv4_address structs pointed to by ip, mask and gw,
- respectively.
- @ip: pointer to an efi_ipv4_address struct to
be filled with the current IP address
- @mask: pointer to an efi_ipv4_address struct to
be filled with the current network mask
- @gw: pointer to an efi_ipv4_address struct to be
filled with the current network gateway
- */
+void efi_net_get_addr(struct efi_ipv4_address *ip,
struct efi_ipv4_address *mask,
struct efi_ipv4_address *gw)
+{ +#ifdef CONFIG_NET_LWIP
- char ipstr[] = "ipaddr\0\0";
- char maskstr[] = "netmask\0\0";
- char gwstr[] = "gatewayip\0\0";
- int idx;
- struct in_addr tmp;
- char *env;
- idx = dev_seq(eth_get_dev());
- if (idx < 0 || idx > 99) {
log_err("unexpected idx %d\n", idx);
return;
- }
- if (idx) {
sprintf(ipstr, "ipaddr%d", idx);
sprintf(maskstr, "netmask%d", idx);
sprintf(gwstr, "gatewayip%d", idx);
- }
- env = env_get(ipstr);
- if (env && ip) {
tmp = string_to_ip(env);
memcpy(ip, &tmp, sizeof(tmp));
- }
- env = env_get(maskstr);
- if (env && mask) {
tmp = string_to_ip(env);
memcpy(mask, &tmp, sizeof(tmp));
- }
- env = env_get(gwstr);
- if (env && gw) {
tmp = string_to_ip(env);
memcpy(gw, &tmp, sizeof(tmp));
- }
+#else
- if (ip)
memcpy(ip, &net_ip, sizeof(net_ip));
- if (mask)
memcpy(mask, &net_netmask, sizeof(net_netmask));
+#endif +}
+/**
- efi_net_set_addr() - set IP address information
- Set the current IP address, mask, and gateway to the
- efi_ipv4_address structs pointed to by ip, mask and gw,
- respectively.
- @ip: pointer to new IP address
- @mask: pointer to new network mask to set
- @gw: pointer to new network gateway
- */
+void efi_net_set_addr(struct efi_ipv4_address *ip,
struct efi_ipv4_address *mask,
struct efi_ipv4_address *gw)
+{ +#ifdef CONFIG_NET_LWIP
- char ipstr[] = "ipaddr\0\0";
- char maskstr[] = "netmask\0\0";
- char gwstr[] = "gatewayip\0\0";
- int idx;
- struct in_addr *addr;
- char tmp[46];
- idx = dev_seq(eth_get_dev());
- if (idx < 0 || idx > 99) {
log_err("unexpected idx %d\n", idx);
return;
- }
The same logic can be found in LWIP's dhcp_loop(), get_udev_ipv4_info().
After this series I think it is time to carve it out into a function.
It is unclear to me form where the idx > 99 in the LWIP stack is derived. struct udevice device defines seq_ as integer.
- if (idx) {
sprintf(ipstr, "ipaddr%d", idx);
sprintf(maskstr, "netmask%d", idx);
sprintf(gwstr, "gatewayip%d", idx);
@Jerome,
The usage of environment variables ipaddr#, netmask#, gatewayip# by the LWIP stack should be documented in doc/usage/environment.rst. Could you, please, provide a patch.
For this patch
Reviewed-by: Heinrich Schuchardt xypron.glpk@gmx.de
- }
- if (ip) {
addr = (struct in_addr *)ip;
ip_to_string(*addr, tmp);
env_set(ipstr, tmp);
- }
- if (mask) {
addr = (struct in_addr *)mask;
ip_to_string(*addr, tmp);
env_set(maskstr, tmp);
- }
- if (gw) {
addr = (struct in_addr *)gw;
ip_to_string(*addr, tmp);
env_set(gwstr, tmp);
- }
+#else
- if (ip)
memcpy(&net_ip, ip, sizeof(*ip));
- if (mask)
memcpy(&net_netmask, mask, sizeof(*mask));
+#endif +}

On 12/3/24 11:51, Heinrich Schuchardt wrote:
On 28.11.24 15:44, Adriano Cordova wrote:
Add the functions efi_net_set_addr and efi_net_get_addr to set and get the ip address from efi code in a network agnostic way. This could also go in net_common, or be compiled conditionally for each network stack.
Signed-off-by: Adriano Cordova adrianox@gmail.com
(no changes since v5) Changes in v5:
- When possible use sizeof in memcpy instead of hardcoded value
Changes in v4: - Removed unnecessary casts - Changed char *ipstr by char ipstr[].
(no changes since v2) include/efi_loader.h | 16 +++++ lib/efi_loader/efi_net.c | 125 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 141 insertions(+)
diff --git a/include/efi_loader.h b/include/efi_loader.h index 39809eac1b..612bc42816 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -125,6 +125,22 @@ static inline void efi_set_bootdev(const char *dev, const char *devnr, size_t buffer_size) { } #endif
+#if CONFIG_IS_ENABLED(NETDEVICES) && CONFIG_IS_ENABLED(EFI_LOADER) +void efi_net_get_addr(struct efi_ipv4_address *ip, + struct efi_ipv4_address *mask, + struct efi_ipv4_address *gw); +void efi_net_set_addr(struct efi_ipv4_address *ip, + struct efi_ipv4_address *mask, + struct efi_ipv4_address *gw); +#else +static inline void efi_net_get_addr(struct efi_ipv4_address *ip, + struct efi_ipv4_address *mask, + struct efi_ipv4_address *gw) { } +static inline void efi_net_set_addr(struct efi_ipv4_address *ip, + struct efi_ipv4_address *mask, + struct efi_ipv4_address *gw) { } +#endif
/* Maximum number of configuration tables */ #define EFI_MAX_CONFIGURATION_TABLES 16
diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c index 7cd536705f..3491d4c481 100644 --- a/lib/efi_loader/efi_net.c +++ b/lib/efi_loader/efi_net.c @@ -17,6 +17,7 @@
#include <efi_loader.h> #include <malloc.h> +#include <vsprintf.h> #include <net.h>
static const efi_guid_t efi_net_guid = EFI_SIMPLE_NETWORK_PROTOCOL_GUID; @@ -997,3 +998,127 @@ out_of_resources: printf("ERROR: Out of memory\n"); return EFI_OUT_OF_RESOURCES; }
+/**
- efi_net_get_addr() - get IP address information
- Copy the current IP address, mask, and gateway into the
- efi_ipv4_address structs pointed to by ip, mask and gw,
- respectively.
- @ip: pointer to an efi_ipv4_address struct to
- * be filled with the current IP address
- @mask: pointer to an efi_ipv4_address struct to
- * be filled with the current network mask
- @gw: pointer to an efi_ipv4_address struct to be
- * filled with the current network gateway
- */
+void efi_net_get_addr(struct efi_ipv4_address *ip, + struct efi_ipv4_address *mask, + struct efi_ipv4_address *gw) +{ +#ifdef CONFIG_NET_LWIP + char ipstr[] = "ipaddr\0\0"; + char maskstr[] = "netmask\0\0"; + char gwstr[] = "gatewayip\0\0"; + int idx; + struct in_addr tmp; + char *env;
+ idx = dev_seq(eth_get_dev());
+ if (idx < 0 || idx > 99) { + log_err("unexpected idx %d\n", idx); + return; + }
+ if (idx) { + sprintf(ipstr, "ipaddr%d", idx); + sprintf(maskstr, "netmask%d", idx); + sprintf(gwstr, "gatewayip%d", idx); + }
+ env = env_get(ipstr); + if (env && ip) { + tmp = string_to_ip(env); + memcpy(ip, &tmp, sizeof(tmp)); + }
+ env = env_get(maskstr); + if (env && mask) { + tmp = string_to_ip(env); + memcpy(mask, &tmp, sizeof(tmp)); + } + env = env_get(gwstr); + if (env && gw) { + tmp = string_to_ip(env); + memcpy(gw, &tmp, sizeof(tmp)); + } +#else + if (ip) + memcpy(ip, &net_ip, sizeof(net_ip)); + if (mask) + memcpy(mask, &net_netmask, sizeof(net_netmask)); +#endif +}
+/**
- efi_net_set_addr() - set IP address information
- Set the current IP address, mask, and gateway to the
- efi_ipv4_address structs pointed to by ip, mask and gw,
- respectively.
- @ip: pointer to new IP address
- @mask: pointer to new network mask to set
- @gw: pointer to new network gateway
- */
+void efi_net_set_addr(struct efi_ipv4_address *ip, + struct efi_ipv4_address *mask, + struct efi_ipv4_address *gw) +{ +#ifdef CONFIG_NET_LWIP + char ipstr[] = "ipaddr\0\0"; + char maskstr[] = "netmask\0\0"; + char gwstr[] = "gatewayip\0\0"; + int idx; + struct in_addr *addr; + char tmp[46];
+ idx = dev_seq(eth_get_dev());
+ if (idx < 0 || idx > 99) { + log_err("unexpected idx %d\n", idx); + return; + }
The same logic can be found in LWIP's dhcp_loop(), get_udev_ipv4_info().
After this series I think it is time to carve it out into a function.
It is unclear to me form where the idx > 99 in the LWIP stack is derived. struct udevice device defines seq_ as integer.
+ if (idx) { + sprintf(ipstr, "ipaddr%d", idx); + sprintf(maskstr, "netmask%d", idx); + sprintf(gwstr, "gatewayip%d", idx);
@Jerome,
The usage of environment variables ipaddr#, netmask#, gatewayip# by the LWIP stack should be documented in doc/usage/environment.rst. Could you, please, provide a patch.
https://lists.denx.de/pipermail/u-boot/2024-December/573696.html
Thanks!

Add efi_dp_from_http to form a device path from HTTP. The device path is the concatenation of the device path returned by efi_dp_from_ipv4 together with an URI node and an END node.
Signed-off-by: Adriano Cordova adrianox@gmail.com Reviewed-by: Heinrich Schuchardt heinrich.schuchardt@canonical.com --- include/efi_loader.h | 1 + lib/efi_loader/efi_device_path.c | 64 ++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+)
diff --git a/include/efi_loader.h b/include/efi_loader.h index 612bc42816..96b204dfc3 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -872,6 +872,7 @@ 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_mem(uint32_t mem_type, uint64_t start_address, size_t size); diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index 6fcbc91e0b..a6d4300686 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -1021,6 +1021,70 @@ static struct efi_device_path *efi_dp_from_ipv4(struct efi_ipv4_address *ip, return dp2; }
+/** + * efi_dp_from_http() - set device path from http + * + * Set the device path to an IPv4 path as provided by efi_dp_from_ipv4 + * concatenated with a device path of subtype DEVICE_PATH_SUB_TYPE_MSG_URI, + * and an END node. + * + * @server: URI of remote server + * Return: pointer to HTTP device path, NULL on error + */ +struct efi_device_path *efi_dp_from_http(const char *server) +{ + struct efi_device_path *dp1, *dp2; + struct efi_device_path_uri *uridp; + efi_uintn_t uridp_len; + char *pos; + char tmp[128]; + struct efi_ipv4_address ip; + struct efi_ipv4_address mask; + + if ((server && strlen("http://") + strlen(server) + 1 > sizeof(tmp)) || + (!server && IS_ENABLED(CONFIG_NET_LWIP))) + return NULL; + + efi_net_get_addr(&ip, &mask, NULL); + + dp1 = efi_dp_from_ipv4(&ip, &mask, NULL); + if (!dp1) + return NULL; + + strcpy(tmp, "http://"); + + if (server) { + strlcat(tmp, server, sizeof(tmp)); +#if !IS_ENABLED(CONFIG_NET_LWIP) + } + else { + ip_to_string(net_server_ip, tmp + strlen("http://")); +#endif + } + + uridp_len = sizeof(struct efi_device_path) + strlen(tmp) + 1; + uridp = efi_alloc(uridp_len + sizeof(END)); + if (!uridp) { + log_err("Out of memory\n"); + return NULL; + } + uridp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; + uridp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_URI; + uridp->dp.length = uridp_len; + debug("device path: setting uri device path to %s\n", tmp); + memcpy(uridp->uri, tmp, strlen(tmp) + 1); + + pos = (char *)uridp + uridp_len; + memcpy(pos, &END, sizeof(END)); + + dp2 = efi_dp_concat(dp1, (const struct efi_device_path *)uridp, 0); + + efi_free_pool(uridp); + efi_free_pool(dp1); + + return dp2; +} + /* Construct a device-path for memory-mapped image */ struct efi_device_path *efi_dp_from_mem(uint32_t memory_type, uint64_t start_address,

On Thu, 28 Nov 2024 at 16:44, Adriano Cordova adrianox@gmail.com wrote:
Add efi_dp_from_http to form a device path from HTTP. The device path is the concatenation of the device path returned by efi_dp_from_ipv4 together with an URI node and an END node.
Signed-off-by: Adriano Cordova adrianox@gmail.com Reviewed-by: Heinrich Schuchardt heinrich.schuchardt@canonical.com
include/efi_loader.h | 1 + lib/efi_loader/efi_device_path.c | 64 ++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+)
diff --git a/include/efi_loader.h b/include/efi_loader.h index 612bc42816..96b204dfc3 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -872,6 +872,7 @@ 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_mem(uint32_t mem_type, uint64_t start_address, size_t size); diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index 6fcbc91e0b..a6d4300686 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -1021,6 +1021,70 @@ static struct efi_device_path *efi_dp_from_ipv4(struct efi_ipv4_address *ip, return dp2; }
+/**
- efi_dp_from_http() - set device path from http
- Set the device path to an IPv4 path as provided by efi_dp_from_ipv4
- concatenated with a device path of subtype DEVICE_PATH_SUB_TYPE_MSG_URI,
- and an END node.
- @server: URI of remote server
- Return: pointer to HTTP device path, NULL on error
- */
+struct efi_device_path *efi_dp_from_http(const char *server) +{
struct efi_device_path *dp1, *dp2;
struct efi_device_path_uri *uridp;
efi_uintn_t uridp_len;
char *pos;
char tmp[128];
struct efi_ipv4_address ip;
struct efi_ipv4_address mask;
if ((server && strlen("http://") + strlen(server) + 1 > sizeof(tmp)) ||
(!server && IS_ENABLED(CONFIG_NET_LWIP)))
return NULL;
efi_net_get_addr(&ip, &mask, NULL);
dp1 = efi_dp_from_ipv4(&ip, &mask, NULL);
if (!dp1)
return NULL;
strcpy(tmp, "http://");
if (server) {
strlcat(tmp, server, sizeof(tmp));
+#if !IS_ENABLED(CONFIG_NET_LWIP)
}
else {
ip_to_string(net_server_ip, tmp + strlen("http://"));
+#endif
}
uridp_len = sizeof(struct efi_device_path) + strlen(tmp) + 1;
uridp = efi_alloc(uridp_len + sizeof(END));
if (!uridp) {
log_err("Out of memory\n");
return NULL;
}
uridp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
uridp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_URI;
uridp->dp.length = uridp_len;
debug("device path: setting uri device path to %s\n", tmp);
memcpy(uridp->uri, tmp, strlen(tmp) + 1);
pos = (char *)uridp + uridp_len;
memcpy(pos, &END, sizeof(END));
dp2 = efi_dp_concat(dp1, (const struct efi_device_path *)uridp, 0);
efi_free_pool(uridp);
efi_free_pool(dp1);
return dp2;
+}
/* Construct a device-path for memory-mapped image */ struct efi_device_path *efi_dp_from_mem(uint32_t memory_type, uint64_t start_address, -- 2.43.0
Reviewed-by: Ilias Apalodimas ilias.apalodimas@linaro.org

Set the device path of the efi boot device to an HTTP device path (as formed by efi_dp_from_http) when the next boot stage is loaded using wget (i.e., when wget is used with wget_info.set_bootdev=1).
When loaded from HTTP, the device path should account for it so that the next boot stage is aware (e.g. grub only loads its http stack if it itself was loaded from http, and it checks this from its device path).
Signed-off-by: Adriano Cordova adrianox@gmail.com --- Changes in v6: - Add error handling to efi_net_set_dp and put it inside efi_set_bootdev, reducing the number of calls to efi functions outside efi code. - Free net_dp before an update - Complete documentation for efi_net_get_dp. Error handling: returns NULL on error Changes in v5: - Add description of net_dp - Change a void** for an efi_device_path** (no changes since v2)
include/efi_loader.h | 5 +++ lib/efi_loader/efi_bootbin.c | 40 +++++++++++++---------- lib/efi_loader/efi_device_path.c | 5 +-- lib/efi_loader/efi_net.c | 55 +++++++++++++++++++++++++++++++- net/lwip/wget.c | 5 ++- net/wget.c | 2 +- 6 files changed, 89 insertions(+), 23 deletions(-)
diff --git a/include/efi_loader.h b/include/efi_loader.h index 96b204dfc3..0da0248db4 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -126,6 +126,10 @@ static inline void efi_set_bootdev(const char *dev, const char *devnr, #endif
#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); +/* 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_addr(struct efi_ipv4_address *ip, struct efi_ipv4_address *mask, struct efi_ipv4_address *gw); @@ -133,6 +137,7 @@ void efi_net_set_addr(struct efi_ipv4_address *ip, struct efi_ipv4_address *mask, struct efi_ipv4_address *gw); #else +static inline void efi_net_get_dp(struct efi_device_path **dp) { } static inline void efi_net_get_addr(struct efi_ipv4_address *ip, struct efi_ipv4_address *mask, struct efi_ipv4_address *gw) { } diff --git a/lib/efi_loader/efi_bootbin.c b/lib/efi_loader/efi_bootbin.c index a87006b3c0..f8d3ee0bcf 100644 --- a/lib/efi_loader/efi_bootbin.c +++ b/lib/efi_loader/efi_bootbin.c @@ -93,24 +93,32 @@ void efi_set_bootdev(const char *dev, const char *devnr, const char *path, image_addr = buffer; image_size = buffer_size;
+#if IS_ENABLED(CONFIG_NETDEVICES) + ret = efi_net_set_dp(dev, devnr); + if (ret != EFI_SUCCESS) + goto error; +#endif + ret = efi_dp_from_name(dev, devnr, path, &device, &image); - if (ret == EFI_SUCCESS) { - bootefi_device_path = device; - if (image) { - /* FIXME: image should not contain device */ - struct efi_device_path *image_tmp = image; - - efi_dp_split_file_path(image, &device, &image); - efi_free_pool(image_tmp); - } - bootefi_image_path = image; - log_debug("- boot device %pD\n", device); - if (image) - log_debug("- image %pD\n", image); - } else { - log_debug("- efi_dp_from_name() failed, err=%lx\n", ret); - efi_clear_bootdev(); + if (ret != EFI_SUCCESS) + goto error; + + bootefi_device_path = device; + if (image) { + /* FIXME: image should not contain device */ + struct efi_device_path *image_tmp = image; + + efi_dp_split_file_path(image, &device, &image); + efi_free_pool(image_tmp); } + bootefi_image_path = image; + log_debug("- boot device %pD\n", device); + if (image) + log_debug("- image %pD\n", image); + return; +error: + log_debug("- efi_dp_from_name() failed, err=%lx\n", ret); + efi_clear_bootdev(); }
/** diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index a6d4300686..5eae6fd39c 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -1185,8 +1185,9 @@ efi_status_t efi_dp_from_name(const char *dev, const char *devnr,
dp = efi_dp_from_mem(EFI_RESERVED_MEMORY_TYPE, (uintptr_t)image_addr, image_size); - } else if (IS_ENABLED(CONFIG_NETDEVICES) && !strcmp(dev, "Net")) { - dp = efi_dp_from_eth(); + } else if (IS_ENABLED(CONFIG_NETDEVICES) && + (!strcmp(dev, "Net") || !strcmp(dev, "Http"))) { + efi_net_get_dp(&dp); } 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 3491d4c481..e8af2e3d95 100644 --- a/lib/efi_loader/efi_net.c +++ b/lib/efi_loader/efi_net.c @@ -16,6 +16,7 @@ */
#include <efi_loader.h> +#include <dm.h> #include <malloc.h> #include <vsprintf.h> #include <net.h> @@ -32,6 +33,13 @@ 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; + /* * The notification function of this event is called in every timer cycle * to check if a new network packet has been received. @@ -902,8 +910,10 @@ 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, - efi_dp_from_eth()); + net_dp); if (r != EFI_SUCCESS) goto failure_to_add_protocol; r = efi_add_protocol(&netobj->header, &efi_pxe_base_code_protocol_guid, @@ -999,6 +1009,49 @@ out_of_resources: return EFI_OUT_OF_RESOURCES; }
+/** + * efi_net_set_dp() - set device path of efi net device + * + * This gets called to update the device path when a new boot + * file is downloaded + * + * @dev: dev to set the device path from + * @server: remote server address + * Return: status code + */ +efi_status_t efi_net_set_dp(const char *dev, const char *server) +{ + efi_free_pool(net_dp); + + net_dp = NULL; + if (!strcmp(dev, "Net")) + net_dp = efi_dp_from_eth(); + else if (!strcmp(dev, "Http")) + net_dp = efi_dp_from_http(server); + + if (!net_dp) + return EFI_OUT_OF_RESOURCES; + + return EFI_SUCCESS; +} + +/** + * efi_net_get_dp() - get device path of efi net device + * + * Produce a copy of the current device path + * + * @dp: copy of the current device path, or NULL on error + */ +void efi_net_get_dp(struct efi_device_path **dp) +{ + if (!dp) + return; + if (!net_dp) + efi_net_set_dp("Net", NULL); + if (net_dp) + *dp = efi_dp_dup(net_dp); +} + /** * efi_net_get_addr() - get IP address information * diff --git a/net/lwip/wget.c b/net/lwip/wget.c index 2becb02f7b..fcc0e62512 100644 --- a/net/lwip/wget.c +++ b/net/lwip/wget.c @@ -260,10 +260,9 @@ static void httpc_result_cb(void *arg, httpc_result_t httpc_result, printf("%u bytes transferred in %lu ms (", rx_content_len, elapsed); print_size(rx_content_len / elapsed * 1000, "/s)\n"); printf("Bytes transferred = %lu (%lx hex)\n", ctx->size, ctx->size); - if (wget_info->set_bootdev) { - efi_set_bootdev("Net", "", ctx->path, map_sysmem(ctx->saved_daddr, 0), + if (wget_info->set_bootdev) + efi_set_bootdev("Http", ctx->server_name, ctx->path, map_sysmem(ctx->saved_daddr, 0), rx_content_len); - } wget_lwip_set_file_size(rx_content_len); if (env_set_hex("filesize", rx_content_len) || env_set_hex("fileaddr", ctx->saved_daddr)) { diff --git a/net/wget.c b/net/wget.c index f3b43b06b8..d338eaf4ef 100644 --- a/net/wget.c +++ b/net/wget.c @@ -447,7 +447,7 @@ static void wget_handler(uchar *pkt, u16 dport, net_set_state(wget_loop_state); wget_info->file_size = net_boot_file_size; if (wget_info->method == WGET_HTTP_METHOD_GET && wget_info->set_bootdev) { - efi_set_bootdev("Net", "", image_url, + efi_set_bootdev("Http", NULL, image_url, map_sysmem(image_load_addr, 0), net_boot_file_size); env_set_hex("filesize", net_boot_file_size);

On 28.11.24 15:44, Adriano Cordova wrote:
Set the device path of the efi boot device to an HTTP device path (as formed by efi_dp_from_http) when the next boot stage is loaded using wget (i.e., when wget is used with wget_info.set_bootdev=1).
When loaded from HTTP, the device path should account for it so that the next boot stage is aware (e.g. grub only loads its http stack if it itself was loaded from http, and it checks this from its device path).
Signed-off-by: Adriano Cordova adrianox@gmail.com
Reviewed-by: Heinrich Schuchardt xypron.glpk@gmx.de
Changes in v6:
- Add error handling to efi_net_set_dp and put it inside efi_set_bootdev, reducing the number of calls to efi functions outside efi code.
- Free net_dp before an update
- Complete documentation for efi_net_get_dp. Error handling: returns NULL on error
Changes in v5:
- Add description of net_dp
- Change a void** for an efi_device_path**
(no changes since v2)
include/efi_loader.h | 5 +++ lib/efi_loader/efi_bootbin.c | 40 +++++++++++++---------- lib/efi_loader/efi_device_path.c | 5 +-- lib/efi_loader/efi_net.c | 55 +++++++++++++++++++++++++++++++- net/lwip/wget.c | 5 ++- net/wget.c | 2 +- 6 files changed, 89 insertions(+), 23 deletions(-)
diff --git a/include/efi_loader.h b/include/efi_loader.h index 96b204dfc3..0da0248db4 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -126,6 +126,10 @@ static inline void efi_set_bootdev(const char *dev, const char *devnr, #endif
#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); +/* 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_addr(struct efi_ipv4_address *ip, struct efi_ipv4_address *mask, struct efi_ipv4_address *gw); @@ -133,6 +137,7 @@ void efi_net_set_addr(struct efi_ipv4_address *ip, struct efi_ipv4_address *mask, struct efi_ipv4_address *gw); #else +static inline void efi_net_get_dp(struct efi_device_path **dp) { } static inline void efi_net_get_addr(struct efi_ipv4_address *ip, struct efi_ipv4_address *mask, struct efi_ipv4_address *gw) { } diff --git a/lib/efi_loader/efi_bootbin.c b/lib/efi_loader/efi_bootbin.c index a87006b3c0..f8d3ee0bcf 100644 --- a/lib/efi_loader/efi_bootbin.c +++ b/lib/efi_loader/efi_bootbin.c @@ -93,24 +93,32 @@ void efi_set_bootdev(const char *dev, const char *devnr, const char *path, image_addr = buffer; image_size = buffer_size;
+#if IS_ENABLED(CONFIG_NETDEVICES)
- ret = efi_net_set_dp(dev, devnr);
- if (ret != EFI_SUCCESS)
goto error;
+#endif
- ret = efi_dp_from_name(dev, devnr, path, &device, &image);
- if (ret == EFI_SUCCESS) {
bootefi_device_path = device;
if (image) {
/* FIXME: image should not contain device */
struct efi_device_path *image_tmp = image;
efi_dp_split_file_path(image, &device, &image);
efi_free_pool(image_tmp);
}
bootefi_image_path = image;
log_debug("- boot device %pD\n", device);
if (image)
log_debug("- image %pD\n", image);
- } else {
log_debug("- efi_dp_from_name() failed, err=%lx\n", ret);
efi_clear_bootdev();
- if (ret != EFI_SUCCESS)
goto error;
- bootefi_device_path = device;
- if (image) {
/* FIXME: image should not contain device */
struct efi_device_path *image_tmp = image;
efi_dp_split_file_path(image, &device, &image);
}efi_free_pool(image_tmp);
- bootefi_image_path = image;
- log_debug("- boot device %pD\n", device);
- if (image)
log_debug("- image %pD\n", image);
- return;
+error:
log_debug("- efi_dp_from_name() failed, err=%lx\n", ret);
efi_clear_bootdev(); }
/**
diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index a6d4300686..5eae6fd39c 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -1185,8 +1185,9 @@ efi_status_t efi_dp_from_name(const char *dev, const char *devnr,
dp = efi_dp_from_mem(EFI_RESERVED_MEMORY_TYPE, (uintptr_t)image_addr, image_size);
- } else if (IS_ENABLED(CONFIG_NETDEVICES) && !strcmp(dev, "Net")) {
dp = efi_dp_from_eth();
- } else if (IS_ENABLED(CONFIG_NETDEVICES) &&
(!strcmp(dev, "Net") || !strcmp(dev, "Http"))) {
} else if (!strcmp(dev, "Uart")) { dp = efi_dp_from_uart(); } else {efi_net_get_dp(&dp);
diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c index 3491d4c481..e8af2e3d95 100644 --- a/lib/efi_loader/efi_net.c +++ b/lib/efi_loader/efi_net.c @@ -16,6 +16,7 @@ */
#include <efi_loader.h> +#include <dm.h> #include <malloc.h> #include <vsprintf.h> #include <net.h> @@ -32,6 +33,13 @@ 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;
- /*
- The notification function of this event is called in every timer cycle
- to check if a new network packet has been received.
@@ -902,8 +910,10 @@ efi_status_t efi_net_register(void) &netobj->net); if (r != EFI_SUCCESS) goto failure_to_add_protocol;
- if (!net_dp)
r = efi_add_protocol(&netobj->header, &efi_guid_device_path,efi_net_set_dp("Net", NULL);
efi_dp_from_eth());
if (r != EFI_SUCCESS) goto failure_to_add_protocol; r = efi_add_protocol(&netobj->header, &efi_pxe_base_code_protocol_guid,net_dp);
@@ -999,6 +1009,49 @@ out_of_resources: return EFI_OUT_OF_RESOURCES; }
+/**
- efi_net_set_dp() - set device path of efi net device
- This gets called to update the device path when a new boot
- file is downloaded
- @dev: dev to set the device path from
- @server: remote server address
- Return: status code
- */
+efi_status_t efi_net_set_dp(const char *dev, const char *server) +{
- efi_free_pool(net_dp);
- net_dp = NULL;
- if (!strcmp(dev, "Net"))
net_dp = efi_dp_from_eth();
- else if (!strcmp(dev, "Http"))
net_dp = efi_dp_from_http(server);
- if (!net_dp)
return EFI_OUT_OF_RESOURCES;
- return EFI_SUCCESS;
+}
+/**
- efi_net_get_dp() - get device path of efi net device
- Produce a copy of the current device path
- @dp: copy of the current device path, or NULL on error
- */
+void efi_net_get_dp(struct efi_device_path **dp) +{
- if (!dp)
return;
- if (!net_dp)
efi_net_set_dp("Net", NULL);
- if (net_dp)
*dp = efi_dp_dup(net_dp);
+}
- /**
- efi_net_get_addr() - get IP address information
diff --git a/net/lwip/wget.c b/net/lwip/wget.c index 2becb02f7b..fcc0e62512 100644 --- a/net/lwip/wget.c +++ b/net/lwip/wget.c @@ -260,10 +260,9 @@ static void httpc_result_cb(void *arg, httpc_result_t httpc_result, printf("%u bytes transferred in %lu ms (", rx_content_len, elapsed); print_size(rx_content_len / elapsed * 1000, "/s)\n"); printf("Bytes transferred = %lu (%lx hex)\n", ctx->size, ctx->size);
- if (wget_info->set_bootdev) {
efi_set_bootdev("Net", "", ctx->path, map_sysmem(ctx->saved_daddr, 0),
- if (wget_info->set_bootdev)
efi_set_bootdev("Http", ctx->server_name, ctx->path, map_sysmem(ctx->saved_daddr, 0), rx_content_len);
- } wget_lwip_set_file_size(rx_content_len); if (env_set_hex("filesize", rx_content_len) || env_set_hex("fileaddr", ctx->saved_daddr)) {
diff --git a/net/wget.c b/net/wget.c index f3b43b06b8..d338eaf4ef 100644 --- a/net/wget.c +++ b/net/wget.c @@ -447,7 +447,7 @@ static void wget_handler(uchar *pkt, u16 dport, net_set_state(wget_loop_state); wget_info->file_size = net_boot_file_size; if (wget_info->method == WGET_HTTP_METHOD_GET && wget_info->set_bootdev) {
efi_set_bootdev("Net", "", image_url,
efi_set_bootdev("Http", NULL, image_url, map_sysmem(image_load_addr, 0), net_boot_file_size); env_set_hex("filesize", net_boot_file_size);

Add network-stack agnostic way to send an http request and parse http headers from efi drivers. This uses wget as a backend and communicates with it via efi_wget_info.
The function efi_net_do_request allocates a buffer on behalf of an efi application using efi_alloc and passes it to wget to receive the data. If the method is GET and the buffer is too small, it re-allocates the buffer based on the last received Content-Length header and tries again. If the method is HEAD it just issues one request. So issuing a HEAD request (to update Content-Length) and then a GET request is preferred but not required.
The function efi_net_parse_headers parses a raw buffer containing an http header into an array of EFI specific 'http_header' structs.
Signed-off-by: Adriano Cordova adrianox@gmail.com ---
(no changes since v4)
Changes in v4: - Remove void* casts - Add documentation
Changes in v3: - Add a struct wget_http_info efi_wget_info for efi_net to pass to wget when issuing wget requests, instead of using the old wget_info (see v2 of the series 'wget: Expose wget to applications'). - Let a pointer to the http status code be passed to efi_net_do_request for it to fill it for the client after a request. include/efi_loader.h | 13 ++++ lib/efi_loader/efi_net.c | 155 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 168 insertions(+)
diff --git a/include/efi_loader.h b/include/efi_loader.h index 0da0248db4..b6f865ca76 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -16,6 +16,7 @@ #include <image.h> #include <pe.h> #include <linux/list.h> +#include <linux/sizes.h> #include <linux/oid_registry.h>
struct blk_desc; @@ -136,6 +137,18 @@ void efi_net_get_addr(struct efi_ipv4_address *ip, void efi_net_set_addr(struct efi_ipv4_address *ip, struct efi_ipv4_address *mask, struct efi_ipv4_address *gw); +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); +#define MAX_HTTP_HEADERS_SIZE SZ_64K +#define MAX_HTTP_HEADERS 100 +#define MAX_HTTP_HEADER_NAME 128 +#define MAX_HTTP_HEADER_VALUE 512 +struct http_header { + uchar name[MAX_HTTP_HEADER_NAME]; + uchar value[MAX_HTTP_HEADER_VALUE]; +}; + +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_addr(struct efi_ipv4_address *ip, diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c index e8af2e3d95..368f62c5b2 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 <linux/sizes.h> #include <malloc.h> #include <vsprintf.h> #include <net.h> @@ -40,6 +41,12 @@ static struct efi_net_obj *netobj; */ static struct efi_device_path *net_dp;
+static struct wget_http_info efi_wget_info = { + .set_bootdev = false, + .check_buffer_size = true, + +}; + /* * The notification function of this event is called in every timer cycle * to check if a new network packet has been received. @@ -1175,3 +1182,151 @@ void efi_net_set_addr(struct efi_ipv4_address *ip, memcpy(&net_netmask, mask, sizeof(*mask)); #endif } + +/** + * efi_net_set_buffer() - allocate a buffer of min 64K + * + * @buffer: allocated buffer + * @size: desired buffer size + * Return: status code + */ +static efi_status_t efi_net_set_buffer(void **buffer, size_t size) +{ + efi_status_t ret = EFI_SUCCESS; + + if (size < SZ_64K) + size = SZ_64K; + + efi_free_pool(*buffer); + + *buffer = efi_alloc(size); + if (!*buffer) + ret = EFI_OUT_OF_RESOURCES; + + efi_wget_info.buffer_size = (ulong)size; + + return ret; +} + +/** + * efi_net_parse_headers() - parse HTTP headers + * + * Parses the raw buffer efi_wget_info.headers into an array headers + * of efi structs http_headers. The array should be at least + * MAX_HTTP_HEADERS long. + * + * @num_headers: number of headers + * @headers: caller provided array of struct http_headers + */ +void efi_net_parse_headers(ulong *num_headers, struct http_header *headers) +{ + if (!num_headers || !headers) + return; + + // Populate info with http headers. + *num_headers = 0; + const uchar *line_start = efi_wget_info.headers; + const uchar *line_end; + ulong count; + struct http_header *current_header; + const uchar *separator; + size_t name_length, value_length; + + // Skip the first line (request or status line) + line_end = strstr(line_start, "\r\n"); + + if (line_end) + line_start = line_end + 2; + + while ((line_end = strstr(line_start, "\r\n")) != NULL) { + count = *num_headers; + if (line_start == line_end || count >= MAX_HTTP_HEADERS) + break; + current_header = headers + count; + separator = strchr(line_start, ':'); + if (separator) { + name_length = separator - line_start; + ++separator; + while (*separator == ' ') + ++separator; + value_length = line_end - separator; + if (name_length < MAX_HTTP_HEADER_NAME && + value_length < MAX_HTTP_HEADER_VALUE) { + strncpy(current_header->name, line_start, name_length); + current_header->name[name_length] = '\0'; + strncpy(current_header->value, separator, value_length); + current_header->value[value_length] = '\0'; + (*num_headers)++; + } + } + line_start = line_end + 2; + } +} + +/** + * efi_net_do_request() - issue an HTTP request using wget + * + * @url: url + * @method: HTTP method + * @buffer: data buffer + * @status_code: HTTP status code + * @file_size: file size in bytes + * @headers_buffer: headers buffer + * 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) +{ + efi_status_t ret = EFI_SUCCESS; + int wget_ret; + static bool last_head; + + if (!buffer || !file_size) + return EFI_ABORTED; + + efi_wget_info.method = (enum wget_http_method)method; + efi_wget_info.headers = headers_buffer; + + 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; + 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 + ret = efi_net_set_buffer(buffer, (size_t)efi_wget_info.hdr_cont_len); + if (ret != EFI_SUCCESS) + goto out; + if (wget_request((ulong)*buffer, url, &efi_wget_info)) { + efi_free_pool(*buffer); + ret = EFI_DEVICE_ERROR; + goto out; + } + } else if (wget_ret) { + efi_free_pool(*buffer); + ret = EFI_DEVICE_ERROR; + goto out; + } + // Pass the actual number of received bytes to the application + *file_size = efi_wget_info.file_size; + *status_code = efi_wget_info.status_code; + last_head = false; + break; + case HTTP_METHOD_HEAD: + ret = efi_net_set_buffer(buffer, 0); + if (ret != EFI_SUCCESS) + goto out; + wget_request((ulong)*buffer, url, &efi_wget_info); + *file_size = 0; + *status_code = efi_wget_info.status_code; + last_head = true; + break; + default: + ret = EFI_UNSUPPORTED; + break; + } + +out: + return ret; +}

On 28.11.24 15:44, Adriano Cordova wrote:
Add network-stack agnostic way to send an http request and parse http headers from efi drivers. This uses wget as a backend and communicates with it via efi_wget_info.
The function efi_net_do_request allocates a buffer on behalf of an efi application using efi_alloc and passes it to wget to receive the data. If the method is GET and the buffer is too small, it re-allocates the buffer based on the last received Content-Length header and tries again. If the method is HEAD it just issues one request. So issuing a HEAD request (to update Content-Length) and then a GET request is preferred but not required.
The function efi_net_parse_headers parses a raw buffer containing an http header into an array of EFI specific 'http_header' structs.
Signed-off-by: Adriano Cordova adrianox@gmail.com
Reviewed-by: Heinrich Schuchardt xypron.glpk@gmx.de
(no changes since v4)
Changes in v4:
- Remove void* casts
- Add documentation
Changes in v3:
- Add a struct wget_http_info efi_wget_info for efi_net to pass to wget when issuing wget requests, instead of using the old wget_info (see v2 of the series 'wget: Expose wget to applications').
- Let a pointer to the http status code be passed to efi_net_do_request for it to fill it for the client after a request. include/efi_loader.h | 13 ++++ lib/efi_loader/efi_net.c | 155 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 168 insertions(+)
diff --git a/include/efi_loader.h b/include/efi_loader.h index 0da0248db4..b6f865ca76 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -16,6 +16,7 @@ #include <image.h> #include <pe.h> #include <linux/list.h> +#include <linux/sizes.h> #include <linux/oid_registry.h>
struct blk_desc; @@ -136,6 +137,18 @@ void efi_net_get_addr(struct efi_ipv4_address *ip, void efi_net_set_addr(struct efi_ipv4_address *ip, struct efi_ipv4_address *mask, struct efi_ipv4_address *gw); +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);
+#define MAX_HTTP_HEADERS_SIZE SZ_64K +#define MAX_HTTP_HEADERS 100 +#define MAX_HTTP_HEADER_NAME 128 +#define MAX_HTTP_HEADER_VALUE 512 +struct http_header {
- uchar name[MAX_HTTP_HEADER_NAME];
- uchar value[MAX_HTTP_HEADER_VALUE];
+};
+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_addr(struct efi_ipv4_address *ip, diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c index e8af2e3d95..368f62c5b2 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 <linux/sizes.h> #include <malloc.h> #include <vsprintf.h> #include <net.h> @@ -40,6 +41,12 @@ static struct efi_net_obj *netobj; */ static struct efi_device_path *net_dp;
+static struct wget_http_info efi_wget_info = {
- .set_bootdev = false,
- .check_buffer_size = true,
+};
- /*
- The notification function of this event is called in every timer cycle
- to check if a new network packet has been received.
@@ -1175,3 +1182,151 @@ void efi_net_set_addr(struct efi_ipv4_address *ip, memcpy(&net_netmask, mask, sizeof(*mask)); #endif }
+/**
- efi_net_set_buffer() - allocate a buffer of min 64K
- @buffer: allocated buffer
- @size: desired buffer size
- Return: status code
- */
+static efi_status_t efi_net_set_buffer(void **buffer, size_t size) +{
- efi_status_t ret = EFI_SUCCESS;
- if (size < SZ_64K)
size = SZ_64K;
- efi_free_pool(*buffer);
- *buffer = efi_alloc(size);
- if (!*buffer)
ret = EFI_OUT_OF_RESOURCES;
- efi_wget_info.buffer_size = (ulong)size;
- return ret;
+}
+/**
- efi_net_parse_headers() - parse HTTP headers
- Parses the raw buffer efi_wget_info.headers into an array headers
- of efi structs http_headers. The array should be at least
- MAX_HTTP_HEADERS long.
- @num_headers: number of headers
- @headers: caller provided array of struct http_headers
- */
+void efi_net_parse_headers(ulong *num_headers, struct http_header *headers) +{
- if (!num_headers || !headers)
return;
- // Populate info with http headers.
- *num_headers = 0;
- const uchar *line_start = efi_wget_info.headers;
- const uchar *line_end;
- ulong count;
- struct http_header *current_header;
- const uchar *separator;
- size_t name_length, value_length;
- // Skip the first line (request or status line)
- line_end = strstr(line_start, "\r\n");
- if (line_end)
line_start = line_end + 2;
- while ((line_end = strstr(line_start, "\r\n")) != NULL) {
count = *num_headers;
if (line_start == line_end || count >= MAX_HTTP_HEADERS)
break;
current_header = headers + count;
separator = strchr(line_start, ':');
if (separator) {
name_length = separator - line_start;
++separator;
while (*separator == ' ')
++separator;
value_length = line_end - separator;
if (name_length < MAX_HTTP_HEADER_NAME &&
value_length < MAX_HTTP_HEADER_VALUE) {
strncpy(current_header->name, line_start, name_length);
current_header->name[name_length] = '\0';
strncpy(current_header->value, separator, value_length);
current_header->value[value_length] = '\0';
(*num_headers)++;
}
}
line_start = line_end + 2;
- }
+}
+/**
- efi_net_do_request() - issue an HTTP request using wget
- @url: url
- @method: HTTP method
- @buffer: data buffer
- @status_code: HTTP status code
- @file_size: file size in bytes
- @headers_buffer: headers buffer
- 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)
+{
- efi_status_t ret = EFI_SUCCESS;
- int wget_ret;
- static bool last_head;
- if (!buffer || !file_size)
return EFI_ABORTED;
- efi_wget_info.method = (enum wget_http_method)method;
- efi_wget_info.headers = headers_buffer;
- 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;
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
ret = efi_net_set_buffer(buffer, (size_t)efi_wget_info.hdr_cont_len);
if (ret != EFI_SUCCESS)
goto out;
if (wget_request((ulong)*buffer, url, &efi_wget_info)) {
efi_free_pool(*buffer);
ret = EFI_DEVICE_ERROR;
goto out;
}
} else if (wget_ret) {
efi_free_pool(*buffer);
ret = EFI_DEVICE_ERROR;
goto out;
}
// Pass the actual number of received bytes to the application
*file_size = efi_wget_info.file_size;
*status_code = efi_wget_info.status_code;
last_head = false;
break;
- case HTTP_METHOD_HEAD:
ret = efi_net_set_buffer(buffer, 0);
if (ret != EFI_SUCCESS)
goto out;
wget_request((ulong)*buffer, url, &efi_wget_info);
*file_size = 0;
*status_code = efi_wget_info.status_code;
last_head = true;
break;
- default:
ret = EFI_UNSUPPORTED;
break;
- }
+out:
- return ret;
+}

Add an implementation of the EFI_IP4_CONFIG2_PROTOCOL. The protocol is attached to the handle of the efi network device. This is the same handle where snp and pxe are attached to.
Signed-off-by: Adriano Cordova adrianox@gmail.com --- (no changes since v4) Changes in v4: - efi_ipconfig.c is compiled if ARCH_QEMU || SANDBOX - An #ifdef changed to IF_ENABLED - Removed a log_err - Reworked sanity checks in efi_ip4_config2_set_data
(no changes since v2) include/efi_loader.h | 3 + lib/efi_loader/Kconfig | 9 ++ lib/efi_loader/Makefile | 1 + lib/efi_loader/efi_ipconfig.c | 214 ++++++++++++++++++++++++++++++++++ lib/efi_loader/efi_net.c | 20 +++- 5 files changed, 242 insertions(+), 5 deletions(-) create mode 100644 lib/efi_loader/efi_ipconfig.c
diff --git a/include/efi_loader.h b/include/efi_loader.h index b6f865ca76..9be09a799b 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -626,6 +626,9 @@ 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); +/* 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); /* Called by bootefi to make the watchdog available */ efi_status_t efi_watchdog_register(void); efi_status_t efi_initrd_register(void); diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index d93f28b842..0b932df85c 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -476,6 +476,15 @@ config EFI_RISCV_BOOT_PROTOCOL replace the transfer via the device-tree. The latter is not possible on systems using ACPI.
+config EFI_IP4_CONFIG2_PROTOCOL + bool "EFI_IP4_CONFIG2_PROTOCOL support" + default y if ARCH_QEMU || SANDBOX + depends on NET || NET_LWIP + help + Provides an implementation of the EFI_IP4_CONFIG2_PROTOCOL, this + protocol can be used to set and get the current ip address and + other network information. + endmenu
menu "Misc options" diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile index 87131ab911..30cd1de9d6 100644 --- a/lib/efi_loader/Makefile +++ b/lib/efi_loader/Makefile @@ -59,6 +59,7 @@ obj-$(CONFIG_EFI_ESRT) += efi_esrt.o obj-$(CONFIG_VIDEO) += efi_gop.o obj-$(CONFIG_BLK) += efi_disk.o obj-$(CONFIG_NETDEVICES) += efi_net.o +obj-$(CONFIG_EFI_IP4_CONFIG2_PROTOCOL) += efi_ipconfig.o obj-$(CONFIG_ACPI) += efi_acpi.o obj-$(CONFIG_SMBIOS) += efi_smbios.o obj-$(CONFIG_EFI_RNG_PROTOCOL) += efi_rng.o diff --git a/lib/efi_loader/efi_ipconfig.c b/lib/efi_loader/efi_ipconfig.c new file mode 100644 index 0000000000..b4b929b144 --- /dev/null +++ b/lib/efi_loader/efi_ipconfig.c @@ -0,0 +1,214 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Implementation of EFI_IP4_CONFIG2_PROTOCOL + * + */ + +#include <efi_loader.h> +#include <image.h> +#include <malloc.h> +#include <mapmem.h> +#include <net.h> + +static const efi_guid_t efi_ip4_config2_guid = EFI_IP4_CONFIG2_PROTOCOL_GUID; + +struct efi_ip4_config2_manual_address current_http_ip; +static enum efi_ip4_config2_policy current_policy; +static char current_mac_addr[32]; + +/* EFI_IP4_CONFIG2_PROTOCOL */ + +/* + * efi_ip4_config2_set_data() - Set the configuration for the EFI IPv4 network + * stack running on the communication device + * + * This function implements EFI_IP4_CONFIG2_PROTOCOL.SetData() + * See the Unified Extensible Firmware Interface + * (UEFI) specification for details. + * + * @this: pointer to the protocol instance + * @data_type: the type of data to set + * @data_size: size of the buffer pointed to by data in bytes + * @data: the data buffer to set + * Return: status code + */ +static efi_status_t EFIAPI efi_ip4_config2_set_data(struct efi_ip4_config2_protocol *this, + enum efi_ip4_config2_data_type data_type, + efi_uintn_t data_size, + void *data) +{ + EFI_ENTRY("%p, %d, %zu, %p", this, data_type, data_size, data); + efi_status_t ret = EFI_SUCCESS; + + if (!this || (data && !data_size) || (!data && data_size)) + return EFI_EXIT(EFI_INVALID_PARAMETER); + + switch (data_type) { + case EFI_IP4_CONFIG2_DATA_TYPE_INTERFACEINFO: + return EFI_EXIT(EFI_WRITE_PROTECTED); + case EFI_IP4_CONFIG2_DATA_TYPE_MANUAL_ADDRESS: + if (current_policy != EFI_IP4_CONFIG2_POLICY_STATIC) + return EFI_EXIT(EFI_WRITE_PROTECTED); + if (data_size == 0 && !data) { + memset((void *)¤t_http_ip, 0, + sizeof(current_http_ip)); + return EFI_EXIT(EFI_SUCCESS); + } + if (data && data_size == sizeof(struct efi_ip4_config2_manual_address)) { + 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); + return EFI_EXIT(EFI_SUCCESS); + } + return EFI_EXIT(EFI_INVALID_PARAMETER); + case EFI_IP4_CONFIG2_DATA_TYPE_POLICY: + if (data && data_size == sizeof(enum efi_ip4_config2_policy)) { + current_policy = *(enum efi_ip4_config2_policy *)data; + return EFI_EXIT(EFI_SUCCESS); + } + return EFI_EXIT(EFI_INVALID_PARAMETER); + + default: + return EFI_EXIT(EFI_UNSUPPORTED); + } + + return EFI_EXIT(ret); +} + +/* + * efi_ip4_config2_get_data() - Get the configuration for the EFI IPv4 network + * stack running on the communication device + * + * This function implements EFI_IP4_CONFIG2_PROTOCOL.GetData() + * See the Unified Extensible Firmware Interface + * (UEFI) specification for details. + * + * @this: pointer to the protocol instance + * @data_type: the type of data to get + * @data_size: size + * @data: the data buffer + * Return: status code + */ +static efi_status_t EFIAPI efi_ip4_config2_get_data(struct efi_ip4_config2_protocol *this, + enum efi_ip4_config2_data_type data_type, + efi_uintn_t *data_size, + void *data) +{ + EFI_ENTRY("%p, %d, %p, %p", this, data_type, data_size, data); + + efi_status_t ret = EFI_SUCCESS; + struct efi_ip4_config2_interface_info *info; + int tmp; + + if (!this || !data_size) + return EFI_EXIT(EFI_INVALID_PARAMETER); + + if (*data_size && !data) + return EFI_EXIT(EFI_INVALID_PARAMETER); + + tmp = sizeof(struct efi_ip4_config2_interface_info) + sizeof(struct efi_ip4_route_table); + + switch (data_type) { + case EFI_IP4_CONFIG2_DATA_TYPE_INTERFACEINFO: + if (*data_size < tmp) { + *data_size = tmp; + return EFI_EXIT(EFI_BUFFER_TOO_SMALL); + } + + info = (struct efi_ip4_config2_interface_info *)data; + memset(info, 0, sizeof(*info)); + + info->hw_address_size = 6; + memcpy(info->hw_address.mac_addr, current_mac_addr, 6); + // Set the route table size + + info->route_table_size = 0; + break; + case EFI_IP4_CONFIG2_DATA_TYPE_MANUAL_ADDRESS: + if (*data_size < sizeof(struct efi_ip4_config2_manual_address)) { + *data_size = sizeof(struct efi_ip4_config2_manual_address); + return EFI_EXIT(EFI_BUFFER_TOO_SMALL); + } + + efi_net_get_addr(¤t_http_ip.address, ¤t_http_ip.subnet_mask, NULL); + memcpy(data, (void *)¤t_http_ip, + sizeof(struct efi_ip4_config2_manual_address)); + + break; + default: + return EFI_EXIT(EFI_UNSUPPORTED); + } + return EFI_EXIT(ret); +} + +/* + * efi_ip4_config2_register_notify() - Register an event that is to be signaled whenever + * a configuration process on the specified configuration + * data is done + * + * This function implements EFI_IP4_CONFIG2_PROTOCOL.RegisterDataNotify() + * See the Unified Extensible Firmware Interface + * (UEFI) specification for details. + * + * @this: pointer to the protocol instance + * @data_type: the type of data to register the event for + * @event: the event to register + * Return: status code + */ +static efi_status_t EFIAPI efi_ip4_config2_register_notify(struct efi_ip4_config2_protocol *this, + enum efi_ip4_config2_data_type data_type, + struct efi_event *event) +{ + EFI_ENTRY("%p, %d, %p", this, data_type, event); + + return EFI_EXIT(EFI_UNSUPPORTED); +} + +/* + * efi_ip4_config2_unregister_notify() - Remove a previously registered eventfor + * the specified configuration data + * + * This function implements EFI_IP4_CONFIG2_PROTOCOL.UnregisterDataNotify() + * See the Unified Extensible Firmware Interface + * (UEFI) specification for details. + * + * @this: pointer to the protocol instance + * @data_type: the type of data to remove the event for + * @event: the event to unregister + * Return: status code + */ +static efi_status_t EFIAPI efi_ip4_config2_unregister_notify(struct efi_ip4_config2_protocol *this, + enum efi_ip4_config2_data_type data_type, + struct efi_event *event) +{ + EFI_ENTRY("%p, %d, %p", this, data_type, event); + + return EFI_EXIT(EFI_UNSUPPORTED); +} + +/** + * efi_ipconfig_register() - register the ip4_config2 protocol + * + */ +efi_status_t efi_ipconfig_register(const efi_handle_t handle, + struct efi_ip4_config2_protocol *ip4config) +{ + efi_status_t r = EFI_SUCCESS; + + r = efi_add_protocol(handle, &efi_ip4_config2_guid, + ip4config); + if (r != EFI_SUCCESS) { + log_err("ERROR: Failure to add protocol\n"); + return r; + } + + 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; + + return EFI_SUCCESS; +} diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c index 368f62c5b2..d3a8deecd0 100644 --- a/lib/efi_loader/efi_net.c +++ b/lib/efi_loader/efi_net.c @@ -60,11 +60,12 @@ static struct efi_event *wait_for_packet; /** * struct efi_net_obj - EFI object representing a network interface * - * @header: EFI object header - * @net: simple network protocol interface - * @net_mode: status of the network interface - * @pxe: PXE base code protocol interface - * @pxe_mode: status of the PXE base code protocol + * @header: EFI object header + * @net: simple network protocol interface + * @net_mode: status of the network interface + * @pxe: PXE base code protocol interface + * @pxe_mode: status of the PXE base code protocol + * @ip4_config2: IP4 Config2 protocol interface */ struct efi_net_obj { struct efi_object header; @@ -72,6 +73,9 @@ struct efi_net_obj { 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) + struct efi_ip4_config2_protocol ip4_config2; +#endif };
/* @@ -999,6 +1003,12 @@ efi_status_t efi_net_register(void) return r; }
+#ifdef CONFIG_EFI_IP4_CONFIG2_PROTOCOL + r = efi_ipconfig_register(&netobj->header, &netobj->ip4_config2); + if (r != EFI_SUCCESS) + goto failure_to_add_protocol; +#endif + return EFI_SUCCESS; failure_to_add_protocol: printf("ERROR: Failure to add protocol\n");

[...]
--- /dev/null +++ b/lib/efi_loader/efi_ipconfig.c @@ -0,0 +1,214 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/*
- Implementation of EFI_IP4_CONFIG2_PROTOCOL
- */
+#include <efi_loader.h> +#include <image.h> +#include <malloc.h> +#include <mapmem.h> +#include <net.h>
+static const efi_guid_t efi_ip4_config2_guid = EFI_IP4_CONFIG2_PROTOCOL_GUID;
+struct efi_ip4_config2_manual_address current_http_ip; +static enum efi_ip4_config2_policy current_policy; +static char current_mac_addr[32];
+/* EFI_IP4_CONFIG2_PROTOCOL */
+/*
- efi_ip4_config2_set_data() - Set the configuration for the EFI IPv4 network
- stack running on the communication device
- This function implements EFI_IP4_CONFIG2_PROTOCOL.SetData()
- See the Unified Extensible Firmware Interface
- (UEFI) specification for details.
- @this: pointer to the protocol instance
- @data_type: the type of data to set
- @data_size: size of the buffer pointed to by data in bytes
- @data: the data buffer to set
- Return: status code
- */
+static efi_status_t EFIAPI efi_ip4_config2_set_data(struct efi_ip4_config2_protocol *this,
enum efi_ip4_config2_data_type data_type,
efi_uintn_t data_size,
void *data)
+{
EFI_ENTRY("%p, %d, %zu, %p", this, data_type, data_size, data);
efi_status_t ret = EFI_SUCCESS;
if (!this || (data && !data_size) || (!data && data_size))
return EFI_EXIT(EFI_INVALID_PARAMETER);
switch (data_type) {
case EFI_IP4_CONFIG2_DATA_TYPE_INTERFACEINFO:
return EFI_EXIT(EFI_WRITE_PROTECTED);
case EFI_IP4_CONFIG2_DATA_TYPE_MANUAL_ADDRESS:
if (current_policy != EFI_IP4_CONFIG2_POLICY_STATIC)
return EFI_EXIT(EFI_WRITE_PROTECTED);
if (data_size == 0 && !data) {
we usually do if (!data_size && ...)
memset((void *)¤t_http_ip, 0,
sizeof(current_http_ip));
return EFI_EXIT(EFI_SUCCESS);
}
if (data && data_size == sizeof(struct efi_ip4_config2_manual_address)) {
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);
return EFI_EXIT(EFI_SUCCESS);
}
return EFI_EXIT(EFI_INVALID_PARAMETER);
case EFI_IP4_CONFIG2_DATA_TYPE_POLICY:
if (data && data_size == sizeof(enum efi_ip4_config2_policy)) {
current_policy = *(enum efi_ip4_config2_policy *)data;
return EFI_EXIT(EFI_SUCCESS);
}
The spec is a bit cryptic here, but it says
EFI_INVALID_PARAMETER One or more of the following are TRUE: - This is NULL. - One or more fields in Data and DataSize do not match the requirement of the data type indicated by DataType
and
EFI_BAD_BUFFER_SIZE - The DataSize does not match the size of the type indicated by DataType
I think we should return EFI_BAD_BUFFER_SIZE on both of the checks above.
Heinrich any idea?
return EFI_EXIT(EFI_INVALID_PARAMETER);
default:
return EFI_EXIT(EFI_UNSUPPORTED);
}
return EFI_EXIT(ret);
+}
+/*
- efi_ip4_config2_get_data() - Get the configuration for the EFI IPv4 network
- stack running on the communication device
- This function implements EFI_IP4_CONFIG2_PROTOCOL.GetData()
- See the Unified Extensible Firmware Interface
- (UEFI) specification for details.
- @this: pointer to the protocol instance
- @data_type: the type of data to get
- @data_size: size
- @data: the data buffer
- Return: status code
- */
+static efi_status_t EFIAPI efi_ip4_config2_get_data(struct efi_ip4_config2_protocol *this,
enum efi_ip4_config2_data_type data_type,
efi_uintn_t *data_size,
void *data)
+{
EFI_ENTRY("%p, %d, %p, %p", this, data_type, data_size, data);
efi_status_t ret = EFI_SUCCESS;
struct efi_ip4_config2_interface_info *info;
int tmp;
if (!this || !data_size)
return EFI_EXIT(EFI_INVALID_PARAMETER);
if (*data_size && !data)
return EFI_EXIT(EFI_INVALID_PARAMETER);
tmp = sizeof(struct efi_ip4_config2_interface_info) + sizeof(struct efi_ip4_route_table);
switch (data_type) {
case EFI_IP4_CONFIG2_DATA_TYPE_INTERFACEINFO:
if (*data_size < tmp) {
*data_size = tmp;
return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
}
info = (struct efi_ip4_config2_interface_info *)data;
memset(info, 0, sizeof(*info));
info->hw_address_size = 6;
memcpy(info->hw_address.mac_addr, current_mac_addr, 6);
// Set the route table size
info->route_table_size = 0;
break;
case EFI_IP4_CONFIG2_DATA_TYPE_MANUAL_ADDRESS:
if (*data_size < sizeof(struct efi_ip4_config2_manual_address)) {
*data_size = sizeof(struct efi_ip4_config2_manual_address);
return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
}
efi_net_get_addr(¤t_http_ip.address, ¤t_http_ip.subnet_mask, NULL);
memcpy(data, (void *)¤t_http_ip,
sizeof(struct efi_ip4_config2_manual_address));
break;
default:
return EFI_EXIT(EFI_UNSUPPORTED);
The spec doesn't have EFI_UNSUPPORTED for GetData. Instead it says EFI_NOT_FOUND "The specified configuration data is not found.". So we should return that for every data type that was marked as EFI_UNSUPPORTED (I think)
}
return EFI_EXIT(ret);
+}
+/*
- efi_ip4_config2_register_notify() - Register an event that is to be signaled whenever
- a configuration process on the specified configuration
- data is done
- This function implements EFI_IP4_CONFIG2_PROTOCOL.RegisterDataNotify()
- See the Unified Extensible Firmware Interface
- (UEFI) specification for details.
- @this: pointer to the protocol instance
- @data_type: the type of data to register the event for
- @event: the event to register
- Return: status code
- */
+static efi_status_t EFIAPI efi_ip4_config2_register_notify(struct efi_ip4_config2_protocol *this,
enum efi_ip4_config2_data_type data_type,
struct efi_event *event)
+{
EFI_ENTRY("%p, %d, %p", this, data_type, event);
return EFI_EXIT(EFI_UNSUPPORTED);
+}
+/*
- efi_ip4_config2_unregister_notify() - Remove a previously registered eventfor
- the specified configuration data
- This function implements EFI_IP4_CONFIG2_PROTOCOL.UnregisterDataNotify()
- See the Unified Extensible Firmware Interface
- (UEFI) specification for details.
- @this: pointer to the protocol instance
- @data_type: the type of data to remove the event for
- @event: the event to unregister
- Return: status code
- */
+static efi_status_t EFIAPI efi_ip4_config2_unregister_notify(struct efi_ip4_config2_protocol *this,
enum efi_ip4_config2_data_type data_type,
struct efi_event *event)
+{
EFI_ENTRY("%p, %d, %p", this, data_type, event);
return EFI_EXIT(EFI_UNSUPPORTED);
+}
+/**
- efi_ipconfig_register() - register the ip4_config2 protocol
- */
+efi_status_t efi_ipconfig_register(const efi_handle_t handle,
struct efi_ip4_config2_protocol *ip4config)
+{
efi_status_t r = EFI_SUCCESS;
r = efi_add_protocol(handle, &efi_ip4_config2_guid,
ip4config);
if (r != EFI_SUCCESS) {
log_err("ERROR: Failure to add protocol\n");
return r;
}
memcpy(current_mac_addr, eth_get_ethaddr(), 6);
You don't need the static variable for this, just copy it on the fly when required.
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;
+} diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c index 368f62c5b2..d3a8deecd0 100644 --- a/lib/efi_loader/efi_net.c +++ b/lib/efi_loader/efi_net.c @@ -60,11 +60,12 @@ static struct efi_event *wait_for_packet; /**
- struct efi_net_obj - EFI object representing a network interface
- @header: EFI object header
- @net: simple network protocol interface
- @net_mode: status of the network interface
- @pxe: PXE base code protocol interface
- @pxe_mode: status of the PXE base code protocol
- @header: EFI object header
- @net: simple network protocol interface
- @net_mode: status of the network interface
- @pxe: PXE base code protocol interface
- @pxe_mode: status of the PXE base code protocol
*/
- @ip4_config2: IP4 Config2 protocol interface
struct efi_net_obj { struct efi_object header; @@ -72,6 +73,9 @@ struct efi_net_obj { 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)
struct efi_ip4_config2_protocol ip4_config2;
+#endif };
/* @@ -999,6 +1003,12 @@ efi_status_t efi_net_register(void) return r; }
+#ifdef CONFIG_EFI_IP4_CONFIG2_PROTOCOL
#if IS_ENABLED here as well etc
r = efi_ipconfig_register(&netobj->header, &netobj->ip4_config2);
if (r != EFI_SUCCESS)
goto failure_to_add_protocol;
+#endif
return EFI_SUCCESS;
failure_to_add_protocol: printf("ERROR: Failure to add protocol\n"); -- 2.43.0
Thanks /Ilias

El mar, 3 dic 2024 a las 11:05, Ilias Apalodimas (< ilias.apalodimas@linaro.org>) escribió:
[...]
--- /dev/null +++ b/lib/efi_loader/efi_ipconfig.c @@ -0,0 +1,214 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/*
- Implementation of EFI_IP4_CONFIG2_PROTOCOL
- */
+#include <efi_loader.h> +#include <image.h> +#include <malloc.h> +#include <mapmem.h> +#include <net.h>
+static const efi_guid_t efi_ip4_config2_guid =
EFI_IP4_CONFIG2_PROTOCOL_GUID;
+struct efi_ip4_config2_manual_address current_http_ip; +static enum efi_ip4_config2_policy current_policy; +static char current_mac_addr[32];
+/* EFI_IP4_CONFIG2_PROTOCOL */
+/*
- efi_ip4_config2_set_data() - Set the configuration for the EFI IPv4
network
- stack running on the communication device
- This function implements EFI_IP4_CONFIG2_PROTOCOL.SetData()
- See the Unified Extensible Firmware Interface
- (UEFI) specification for details.
- @this: pointer to the protocol instance
- @data_type: the type of data to set
- @data_size: size of the buffer pointed to by data in bytes
- @data: the data buffer to set
- Return: status code
- */
+static efi_status_t EFIAPI efi_ip4_config2_set_data(struct
efi_ip4_config2_protocol *this,
enum
efi_ip4_config2_data_type data_type,
efi_uintn_t
data_size,
void *data)
+{
EFI_ENTRY("%p, %d, %zu, %p", this, data_type, data_size, data);
efi_status_t ret = EFI_SUCCESS;
if (!this || (data && !data_size) || (!data && data_size))
return EFI_EXIT(EFI_INVALID_PARAMETER);
switch (data_type) {
case EFI_IP4_CONFIG2_DATA_TYPE_INTERFACEINFO:
return EFI_EXIT(EFI_WRITE_PROTECTED);
case EFI_IP4_CONFIG2_DATA_TYPE_MANUAL_ADDRESS:
if (current_policy != EFI_IP4_CONFIG2_POLICY_STATIC)
return EFI_EXIT(EFI_WRITE_PROTECTED);
if (data_size == 0 && !data) {
we usually do if (!data_size && ...)
memset((void *)¤t_http_ip, 0,
sizeof(current_http_ip));
return EFI_EXIT(EFI_SUCCESS);
}
if (data && data_size == sizeof(struct
efi_ip4_config2_manual_address)) {
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);
return EFI_EXIT(EFI_SUCCESS);
}
return EFI_EXIT(EFI_INVALID_PARAMETER);
case EFI_IP4_CONFIG2_DATA_TYPE_POLICY:
if (data && data_size == sizeof(enum
efi_ip4_config2_policy)) {
current_policy = *(enum
efi_ip4_config2_policy *)data;
return EFI_EXIT(EFI_SUCCESS);
}
The spec is a bit cryptic here, but it says
EFI_INVALID_PARAMETER One or more of the following are TRUE:
- This is NULL.
- One or more fields in Data and DataSize do not match the requirement
of the data type indicated by DataType
and
EFI_BAD_BUFFER_SIZE
- The DataSize does not match the size of the type indicated by DataType
I think we should return EFI_BAD_BUFFER_SIZE on both of the checks above.
Heinrich any idea?
return EFI_EXIT(EFI_INVALID_PARAMETER);
default:
return EFI_EXIT(EFI_UNSUPPORTED);
}
return EFI_EXIT(ret);
+}
+/*
- efi_ip4_config2_get_data() - Get the configuration for the EFI IPv4
network
- stack running on the communication device
- This function implements EFI_IP4_CONFIG2_PROTOCOL.GetData()
- See the Unified Extensible Firmware Interface
- (UEFI) specification for details.
- @this: pointer to the protocol instance
- @data_type: the type of data to get
- @data_size: size
- @data: the data buffer
- Return: status code
- */
+static efi_status_t EFIAPI efi_ip4_config2_get_data(struct
efi_ip4_config2_protocol *this,
enum
efi_ip4_config2_data_type data_type,
efi_uintn_t
*data_size,
void *data)
+{
EFI_ENTRY("%p, %d, %p, %p", this, data_type, data_size, data);
efi_status_t ret = EFI_SUCCESS;
struct efi_ip4_config2_interface_info *info;
int tmp;
if (!this || !data_size)
return EFI_EXIT(EFI_INVALID_PARAMETER);
if (*data_size && !data)
return EFI_EXIT(EFI_INVALID_PARAMETER);
tmp = sizeof(struct efi_ip4_config2_interface_info) +
sizeof(struct efi_ip4_route_table);
switch (data_type) {
case EFI_IP4_CONFIG2_DATA_TYPE_INTERFACEINFO:
if (*data_size < tmp) {
*data_size = tmp;
return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
}
info = (struct efi_ip4_config2_interface_info *)data;
memset(info, 0, sizeof(*info));
info->hw_address_size = 6;
memcpy(info->hw_address.mac_addr, current_mac_addr, 6);
// Set the route table size
info->route_table_size = 0;
break;
case EFI_IP4_CONFIG2_DATA_TYPE_MANUAL_ADDRESS:
if (*data_size < sizeof(struct
efi_ip4_config2_manual_address)) {
*data_size = sizeof(struct
efi_ip4_config2_manual_address);
return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
}
efi_net_get_addr(¤t_http_ip.address,
¤t_http_ip.subnet_mask, NULL);
memcpy(data, (void *)¤t_http_ip,
sizeof(struct efi_ip4_config2_manual_address));
break;
default:
return EFI_EXIT(EFI_UNSUPPORTED);
The spec doesn't have EFI_UNSUPPORTED for GetData. Instead it says EFI_NOT_FOUND "The specified configuration data is not found.". So we should return that for every data type that was marked as EFI_UNSUPPORTED (I think)
}
return EFI_EXIT(ret);
+}
+/*
- efi_ip4_config2_register_notify() - Register an event that is to be
signaled whenever
- a configuration process on the specified configuration
- data is done
- This function implements
EFI_IP4_CONFIG2_PROTOCOL.RegisterDataNotify()
- See the Unified Extensible Firmware Interface
- (UEFI) specification for details.
- @this: pointer to the protocol instance
- @data_type: the type of data to register the event for
- @event: the event to register
- Return: status code
- */
+static efi_status_t EFIAPI efi_ip4_config2_register_notify(struct
efi_ip4_config2_protocol *this,
enum
efi_ip4_config2_data_type data_type,
struct
efi_event *event)
+{
EFI_ENTRY("%p, %d, %p", this, data_type, event);
return EFI_EXIT(EFI_UNSUPPORTED);
+}
+/*
- efi_ip4_config2_unregister_notify() - Remove a previously
registered eventfor
- the specified configuration data
- This function implements
EFI_IP4_CONFIG2_PROTOCOL.UnregisterDataNotify()
- See the Unified Extensible Firmware Interface
- (UEFI) specification for details.
- @this: pointer to the protocol instance
- @data_type: the type of data to remove the event for
- @event: the event to unregister
- Return: status code
- */
+static efi_status_t EFIAPI efi_ip4_config2_unregister_notify(struct
efi_ip4_config2_protocol *this,
enum
efi_ip4_config2_data_type data_type,
struct
efi_event *event)
+{
EFI_ENTRY("%p, %d, %p", this, data_type, event);
return EFI_EXIT(EFI_UNSUPPORTED);
+}
+/**
- efi_ipconfig_register() - register the ip4_config2 protocol
- */
+efi_status_t efi_ipconfig_register(const efi_handle_t handle,
struct efi_ip4_config2_protocol
*ip4config)
+{
efi_status_t r = EFI_SUCCESS;
r = efi_add_protocol(handle, &efi_ip4_config2_guid,
ip4config);
if (r != EFI_SUCCESS) {
log_err("ERROR: Failure to add protocol\n");
return r;
}
memcpy(current_mac_addr, eth_get_ethaddr(), 6);
You don't need the static variable for this, just copy it on the fly when required.
Do you mean calling eth_get_ethaddr() when needed? In that case, this
needs to record the address of the eth device that was set at this moment. I am not sure, but I think this static var could be queried in the future from the client side while another device is set by the u-boot side.
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;
+} diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c index 368f62c5b2..d3a8deecd0 100644 --- a/lib/efi_loader/efi_net.c +++ b/lib/efi_loader/efi_net.c @@ -60,11 +60,12 @@ static struct efi_event *wait_for_packet; /**
- struct efi_net_obj - EFI object representing a network interface
- @header: EFI object header
- @net: simple network protocol interface
- @net_mode: status of the network interface
- @pxe: PXE base code protocol interface
- @pxe_mode: status of the PXE base code protocol
- @header: EFI object header
- @net: simple network protocol interface
- @net_mode: status of the network interface
- @pxe: PXE base code protocol interface
- @pxe_mode: status of the PXE base code protocol
*/
- @ip4_config2: IP4 Config2 protocol interface
struct efi_net_obj { struct efi_object header; @@ -72,6 +73,9 @@ struct efi_net_obj { 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)
struct efi_ip4_config2_protocol ip4_config2;
+#endif };
/* @@ -999,6 +1003,12 @@ efi_status_t efi_net_register(void) return r; }
+#ifdef CONFIG_EFI_IP4_CONFIG2_PROTOCOL
#if IS_ENABLED here as well etc
r = efi_ipconfig_register(&netobj->header, &netobj->ip4_config2);
if (r != EFI_SUCCESS)
goto failure_to_add_protocol;
+#endif
return EFI_SUCCESS;
failure_to_add_protocol: printf("ERROR: Failure to add protocol\n"); -- 2.43.0
Thanks /Ilias

Add an EFI HTTP driver. This commit implements the EFI_HTTP_PROTOCOL and the EFI_HTTP_SERVICE_BINDING_PROTOCOL. The latter is attached to the handle of th efi network device. This is the same handle where snp, pxe, and ipconfig are attached to.
Signed-off-by: Adriano Cordova adrianox@gmail.com --- (no changes since v4) Changes in v4: - Reworked pointers to be void * instead of ulong. - Removed some console output - Check invalid parameters at the beginning, and do not signal an event in case parameters are invalid.
Changes in v3: - Add documentation - Clean up the way the buffer pointed to http_load_addr in the current http instance was being freed. - Remove unnecessary initialization new_instance->handle = NULL in efi_http_service_binding_create_child. include/efi_loader.h | 3 + lib/efi_loader/Kconfig | 8 + lib/efi_loader/Makefile | 1 + lib/efi_loader/efi_http.c | 548 ++++++++++++++++++++++++++++++++++++++ lib/efi_loader/efi_net.c | 20 +- 5 files changed, 578 insertions(+), 2 deletions(-) create mode 100644 lib/efi_loader/efi_http.c
diff --git a/include/efi_loader.h b/include/efi_loader.h index 9be09a799b..9afbec35eb 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -629,6 +629,9 @@ efi_status_t efi_net_register(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); +/* 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_loader/Kconfig b/lib/efi_loader/Kconfig index 0b932df85c..5c73457d9b 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -485,6 +485,14 @@ config EFI_IP4_CONFIG2_PROTOCOL protocol can be used to set and get the current ip address and other network information.
+config EFI_HTTP_PROTOCOL + bool "EFI_HTTP_PROTOCOL support" + default y if ARCH_QEMU || SANDBOX + depends on WGET + help + Provides an EFI HTTP driver implementing the EFI_HTTP_PROTOCOL. and + EFI_HTTP_SERVICE_BINDING_PROTOCOL. + endmenu
menu "Misc options" diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile index 30cd1de9d6..2a0b4172bd 100644 --- a/lib/efi_loader/Makefile +++ b/lib/efi_loader/Makefile @@ -60,6 +60,7 @@ obj-$(CONFIG_VIDEO) += efi_gop.o obj-$(CONFIG_BLK) += efi_disk.o obj-$(CONFIG_NETDEVICES) += efi_net.o obj-$(CONFIG_EFI_IP4_CONFIG2_PROTOCOL) += efi_ipconfig.o +obj-$(CONFIG_EFI_HTTP_PROTOCOL) += efi_http.o obj-$(CONFIG_ACPI) += efi_acpi.o obj-$(CONFIG_SMBIOS) += efi_smbios.o obj-$(CONFIG_EFI_RNG_PROTOCOL) += efi_rng.o diff --git a/lib/efi_loader/efi_http.c b/lib/efi_loader/efi_http.c new file mode 100644 index 0000000000..694e199341 --- /dev/null +++ b/lib/efi_loader/efi_http.c @@ -0,0 +1,548 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * An HTTP driver + * + * HTTP_PROTOCOL + * HTTP_SERVICE_BINDING_PROTOCOL + * IP4_CONFIG2_PROTOCOL + */ + +#include <charset.h> +#include <efi_loader.h> +#include <image.h> +#include <malloc.h> +#include <mapmem.h> +#include <net.h> + +static const efi_guid_t efi_http_service_binding_guid = EFI_HTTP_SERVICE_BINDING_PROTOCOL_GUID; +static const efi_guid_t efi_http_guid = EFI_HTTP_PROTOCOL_GUID; + +/** + * struct efi_http_instance - EFI object representing an HTTP protocol instance + * + * @http: EFI_HTTP_PROTOCOL interface + * @handle: handle to efi object + * @configured: configuration status + * @http_load_addr: data buffer + * @file_size: size of data + * @current_offset: offset in data buffer + * @status_code: HTTP status code + * @num_headers: number of received headers + * @headers: array of headers + * @headers_buffer: raw buffer with headers + */ +struct efi_http_instance { + struct efi_http_protocol http; + efi_handle_t handle; + bool configured; + void *http_load_addr; + ulong file_size; + ulong current_offset; + u32 status_code; + ulong num_headers; + struct http_header headers[MAX_HTTP_HEADERS]; + char headers_buffer[MAX_HTTP_HEADERS_SIZE]; +}; + +static int num_instances; + +/* + * efi_u32_to_httpstatus() - convert u32 to status + * + */ +enum efi_http_status_code efi_u32_to_httpstatus(u32 status); + +/* + * efi_http_send_data() - sends data to client + * + * + * @client_buffer: client buffer to send data to + * @client_buffer_size: size of the client buffer + * @inst: HTTP instance for which to send data + * + * Return: status code + */ +static efi_status_t efi_http_send_data(void *client_buffer, + efi_uintn_t *client_buffer_size, + struct efi_http_instance *inst) +{ + efi_status_t ret = EFI_SUCCESS; + ulong total_size, transfer_size; + uchar *ptr; + + // Amount of data left; + total_size = inst->file_size; + transfer_size = total_size - inst->current_offset; + debug("efi_http: sending data to client, total size %lu\n", total_size); + // Amount of data the client is willing to receive + if (transfer_size > *client_buffer_size) + transfer_size = *client_buffer_size; + else + *client_buffer_size = transfer_size; + debug("efi_http: transfer size %lu\n", transfer_size); + if (!transfer_size) // Ok, only headers + goto out; + + if (!client_buffer) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + // Send data + ptr = (uchar *)inst->http_load_addr + inst->current_offset; + memcpy(client_buffer, ptr, transfer_size); + + inst->current_offset += transfer_size; + + // Whole file served, clean the buffer: + if (inst->current_offset == inst->file_size) { + efi_free_pool(inst->http_load_addr); + inst->http_load_addr = NULL; + inst->current_offset = 0; + inst->file_size = 0; + } + +out: + return ret; +} + +/* EFI_HTTP_PROTOCOL */ + +/* + * efi_http_get_mode_data() - Gets the current operational status. + * + * This function implements EFI_HTTP_PROTOCOL.GetModeData(). + * See the Unified Extensible Firmware Interface + * (UEFI) specification for details. + * + * @this: pointer to the protocol instance + * @data: pointer to the buffer for operational parameters + * of this HTTP instance + * Return: status code + */ +static efi_status_t EFIAPI efi_http_get_mode_data(struct efi_http_protocol *this, + struct efi_http_config_data *data) +{ + EFI_ENTRY("%p, %p", this, data); + + efi_status_t ret = EFI_UNSUPPORTED; + + return EFI_EXIT(ret); +} + +/* + * efi_http_configure() - Initializes operational status for this + * EFI HTTP instance. + * + * This function implements EFI_HTTP_PROTOCOL.Configure(). + * See the Unified Extensible Firmware Interface + * (UEFI) specification for details. + * + * @this: pointer to the protocol instance + * @data: pointer to the buffer for operational parameters of + * this HTTP instance + * Return: status code + */ +static efi_status_t EFIAPI efi_http_configure(struct efi_http_protocol *this, + struct efi_http_config_data *data) +{ + EFI_ENTRY("%p, %p", this, data); + + efi_status_t ret = EFI_SUCCESS; + enum efi_http_version http_version; + struct efi_httpv4_access_point *ipv4_node; + struct efi_http_instance *http_instance; + + if (!this) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + http_instance = (struct efi_http_instance *)this; + + if (!data) { + efi_free_pool(http_instance->http_load_addr); + http_instance->http_load_addr = NULL; + http_instance->current_offset = 0; + http_instance->configured = false; + + goto out; + } + + if (http_instance->configured) { + ret = EFI_ALREADY_STARTED; + goto out; + } + + http_version = data->http_version; + ipv4_node = data->access_point.ipv4_node; + + if ((http_version != HTTPVERSION10 && + http_version != HTTPVERSION11) || + data->is_ipv6 || !ipv4_node) { /* Only support ipv4 */ + ret = EFI_UNSUPPORTED; + goto out; + } + + 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); + } + + http_instance->current_offset = 0; + http_instance->configured = true; + +out: + return EFI_EXIT(ret); +} + +/* + * efi_http_request() - Queues an HTTP request to this HTTP instance + * + * This function implements EFI_HTTP_PROTOCOL.Request(). + * See the Unified Extensible Firmware Interface + * (UEFI) specification for details. + * + * @this: pointer to the protocol instance + * @token: pointer to storage containing HTTP request token + * Return: status code + */ +static efi_status_t EFIAPI efi_http_request(struct efi_http_protocol *this, + struct efi_http_token *token) +{ + EFI_ENTRY("%p, %p", this, token); + + efi_status_t ret = EFI_SUCCESS; + u8 *tmp; + u8 url_8[1024]; + u16 *url_16; + enum efi_http_method current_method; + struct efi_http_instance *http_instance; + + if (!token || !this || !token->message || + !token->message->data.request) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + http_instance = (struct efi_http_instance *)this; + + if (!http_instance->configured) { + ret = EFI_NOT_STARTED; + goto out; + } + + current_method = token->message->data.request->method; + url_16 = token->message->data.request->url; + + /* Parse URL. It comes in UCS-2 encoding and follows RFC3986 */ + tmp = url_8; + utf16_utf8_strncpy((char **)&tmp, url_16, 1024); + + 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); + if (ret != EFI_SUCCESS) + goto out; + + // We have a successful request + efi_net_parse_headers(&http_instance->num_headers, http_instance->headers); + http_instance->current_offset = 0; + token->status = EFI_SUCCESS; + goto out_signal; + +out_signal: + efi_signal_event(token->event); +out: + return EFI_EXIT(ret); +} + +/* + * efi_http_cancel() - Abort an asynchronous HTTP request or response token + * + * This function implements EFI_HTTP_PROTOCOL.Cancel(). + * See the Unified Extensible Firmware Interface + * (UEFI) specification for details. + * + * @this: pointer to the protocol instance + * @token: pointer to storage containing HTTP request token + * Return: status code + */ +static efi_status_t EFIAPI efi_http_cancel(struct efi_http_protocol *this, + struct efi_http_token *token) +{ + EFI_ENTRY("%p, %p", this, token); + + efi_status_t ret = EFI_UNSUPPORTED; + + return EFI_EXIT(ret); +} + +/* + * efi_http_response() - Queues an HTTP response to this HTTP instance + * + * This function implements EFI_HTTP_PROTOCOL.Response(). + * See the Unified Extensible Firmware Interface + * (UEFI) specification for details. + * + * @this: pointer to the protocol instance + * @token: pointer to storage containing HTTP request token + * Return: status code + */ +static efi_status_t EFIAPI efi_http_response(struct efi_http_protocol *this, + struct efi_http_token *token) +{ + EFI_ENTRY("%p, %p", this, token); + + efi_status_t ret = EFI_SUCCESS; + struct efi_http_instance *http_instance; + struct efi_http_header **client_headers; + struct efi_http_response_data *response; + + if (!token || !this || !token->message) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + http_instance = (struct efi_http_instance *)this; + + // Set HTTP status code + if (token->message->data.response) { // TODO extra check, see spec. + response = token->message->data.response; + response->status_code = efi_u32_to_httpstatus(http_instance->status_code); + } + + client_headers = &token->message->headers; + + ret = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, + (http_instance->num_headers) * sizeof(struct efi_http_header), + (void **)client_headers); // This is deallocated by the client. + if (ret != EFI_SUCCESS) + goto out_bad_signal; + + // Send headers + token->message->header_count = http_instance->num_headers; + for (int i = 0; i < http_instance->num_headers; i++) { + (*client_headers)[i].field_name = http_instance->headers[i].name; + (*client_headers)[i].field_value = http_instance->headers[i].value; + } + + ret = efi_http_send_data(token->message->body, &token->message->body_length, http_instance); + if (ret != EFI_SUCCESS) + goto out_bad_signal; + + token->status = EFI_SUCCESS; + goto out_signal; + +out_bad_signal: + token->status = EFI_ABORTED; +out_signal: + efi_signal_event(token->event); +out: + return EFI_EXIT(ret); +} + +/* + * efi_http_poll() - Polls for incoming data packets and processes outgoing data packets + * + * This function implements EFI_HTTP_PROTOCOL.Poll(). + * See the Unified Extensible Firmware Interface + * (UEFI) specification for details. + * + * @this: pointer to the protocol instance + * @token: pointer to storage containing HTTP request token + * Return: status code + */ +static efi_status_t EFIAPI efi_http_poll(struct efi_http_protocol *this) +{ + EFI_ENTRY("%p", this); + + efi_status_t ret = EFI_UNSUPPORTED; + + return EFI_EXIT(ret); +} + +/* EFI_HTTP_SERVICE_BINDING_PROTOCOL */ + +/* + * efi_http_service_binding_create_child() - Creates a child handle + * and installs a protocol + * + * This function implements EFI_HTTP_SERVICE_BINDING.CreateChild(). + * See the Unified Extensible Firmware Interface + * (UEFI) specification for details. + * + * @this: pointer to the protocol instance + * @child_handle: pointer to child handle + * Return: status code + */ +static efi_status_t EFIAPI efi_http_service_binding_create_child( + struct efi_service_binding_protocol *this, + efi_handle_t *child_handle) +{ + EFI_ENTRY("%p, %p", this, child_handle); + + efi_status_t ret = EFI_SUCCESS; + struct efi_http_instance *new_instance; + + if (!child_handle) + return EFI_EXIT(EFI_INVALID_PARAMETER); + + new_instance = calloc(1, sizeof(struct efi_http_instance)); + if (!new_instance) { + ret = EFI_OUT_OF_RESOURCES; + goto failure_to_add_protocol; + } + + if (*child_handle) { + new_instance->handle = *child_handle; + goto install; + } + + new_instance->handle = calloc(1, sizeof(struct efi_object)); + if (!new_instance->handle) { + efi_free_pool((void *)new_instance); + ret = EFI_OUT_OF_RESOURCES; + goto failure_to_add_protocol; + } + + efi_add_handle(new_instance->handle); + *child_handle = new_instance->handle; + +install: + ret = efi_add_protocol(new_instance->handle, &efi_http_guid, + &new_instance->http); + if (ret != EFI_SUCCESS) + goto failure_to_add_protocol; + + new_instance->http.get_mode_data = efi_http_get_mode_data; + new_instance->http.configure = efi_http_configure; + new_instance->http.request = efi_http_request; + new_instance->http.cancel = efi_http_cancel; + new_instance->http.response = efi_http_response; + new_instance->http.poll = efi_http_poll; + ++num_instances; + + return EFI_EXIT(EFI_SUCCESS); + +failure_to_add_protocol: + return EFI_EXIT(ret); +} + +/* + * efi_http_service_binding_destroy_child() - Destroys a child handle with + * a protocol installed on it + * + * This function implements EFI_HTTP_SERVICE_BINDING.DestroyChild(). + * See the Unified Extensible Firmware Interface + * (UEFI) specification for details. + * + * @this: pointer to the protocol instance + * @child_handle: child handle + * Return: status code + */ +static efi_status_t EFIAPI efi_http_service_binding_destroy_child( + struct efi_service_binding_protocol *this, + efi_handle_t child_handle) +{ + EFI_ENTRY("%p, %p", this, child_handle); + efi_status_t ret = EFI_SUCCESS; + struct efi_http_instance *http_instance; + struct efi_handler *phandler; + void *protocol_interface; + + if (num_instances == 0) + return EFI_EXIT(EFI_NOT_FOUND); + + if (!child_handle) + return EFI_EXIT(EFI_INVALID_PARAMETER); + + efi_search_protocol(child_handle, &efi_http_guid, &phandler); + + if (phandler) + protocol_interface = phandler->protocol_interface; + + ret = efi_delete_handle(child_handle); + if (ret != EFI_SUCCESS) + return EFI_EXIT(ret); + + http_instance = (struct efi_http_instance *)protocol_interface; + efi_free_pool(http_instance->http_load_addr); + http_instance->http_load_addr = NULL; + + free(protocol_interface); + + num_instances--; + + return EFI_EXIT(EFI_SUCCESS); +} + +/** + * efi_http_register() - register the http protocol + * + */ +efi_status_t efi_http_register(const efi_handle_t handle, + struct efi_service_binding_protocol *http_service_binding) +{ + efi_status_t r = EFI_SUCCESS; + + r = efi_add_protocol(handle, &efi_http_service_binding_guid, + 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; + + return EFI_SUCCESS; +failure_to_add_protocol: + return r; +} + +enum efi_http_status_code efi_u32_to_httpstatus(u32 status) +{ + switch (status) { + case 100: return HTTP_STATUS_100_CONTINUE; + case 101: return HTTP_STATUS_101_SWITCHING_PROTOCOLS; + case 200: return HTTP_STATUS_200_OK; + case 201: return HTTP_STATUS_201_CREATED; + case 202: return HTTP_STATUS_202_ACCEPTED; + case 203: return HTTP_STATUS_203_NON_AUTHORITATIVE_INFORMATION; + case 204: return HTTP_STATUS_204_NO_CONTENT; + case 205: return HTTP_STATUS_205_RESET_CONTENT; + case 206: return HTTP_STATUS_206_PARTIAL_CONTENT; + case 300: return HTTP_STATUS_300_MULTIPLE_CHOICES; + case 301: return HTTP_STATUS_301_MOVED_PERMANENTLY; + case 302: return HTTP_STATUS_302_FOUND; + case 303: return HTTP_STATUS_303_SEE_OTHER; + case 304: return HTTP_STATUS_304_NOT_MODIFIED; + case 305: return HTTP_STATUS_305_USE_PROXY; + case 307: return HTTP_STATUS_307_TEMPORARY_REDIRECT; + case 400: return HTTP_STATUS_400_BAD_REQUEST; + case 401: return HTTP_STATUS_401_UNAUTHORIZED; + case 402: return HTTP_STATUS_402_PAYMENT_REQUIRED; + case 403: return HTTP_STATUS_403_FORBIDDEN; + case 404: return HTTP_STATUS_404_NOT_FOUND; + case 405: return HTTP_STATUS_405_METHOD_NOT_ALLOWED; + case 406: return HTTP_STATUS_406_NOT_ACCEPTABLE; + case 407: return HTTP_STATUS_407_PROXY_AUTHENTICATION_REQUIRED; + case 408: return HTTP_STATUS_408_REQUEST_TIME_OUT; + case 409: return HTTP_STATUS_409_CONFLICT; + case 410: return HTTP_STATUS_410_GONE; + case 411: return HTTP_STATUS_411_LENGTH_REQUIRED; + case 412: return HTTP_STATUS_412_PRECONDITION_FAILED; + case 413: return HTTP_STATUS_413_REQUEST_ENTITY_TOO_LARGE; + case 414: return HTTP_STATUS_414_REQUEST_URI_TOO_LARGE; + case 415: return HTTP_STATUS_415_UNSUPPORTED_MEDIA_TYPE; + case 416: return HTTP_STATUS_416_REQUESTED_RANGE_NOT_SATISFIED; + case 417: return HTTP_STATUS_417_EXPECTATION_FAILED; + case 500: return HTTP_STATUS_500_INTERNAL_SERVER_ERROR; + case 501: return HTTP_STATUS_501_NOT_IMPLEMENTED; + case 502: return HTTP_STATUS_502_BAD_GATEWAY; + case 503: return HTTP_STATUS_503_SERVICE_UNAVAILABLE; + case 504: return HTTP_STATUS_504_GATEWAY_TIME_OUT; + case 505: return HTTP_STATUS_505_HTTP_VERSION_NOT_SUPPORTED; + case 308: return HTTP_STATUS_308_PERMANENT_REDIRECT; + default: return HTTP_STATUS_UNSUPPORTED_STATUS; + } +} diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c index d3a8deecd0..70f8480b87 100644 --- a/lib/efi_loader/efi_net.c +++ b/lib/efi_loader/efi_net.c @@ -66,6 +66,7 @@ static struct efi_event *wait_for_packet; * @pxe: PXE base code protocol interface * @pxe_mode: status of the PXE base code protocol * @ip4_config2: IP4 Config2 protocol interface + * @http_service_binding: Http service binding protocol interface */ struct efi_net_obj { struct efi_object header; @@ -76,6 +77,9 @@ struct efi_net_obj { #if IS_ENABLED(CONFIG_EFI_IP4_CONFIG2_PROTOCOL) struct efi_ip4_config2_protocol ip4_config2; #endif +#if IS_ENABLED(CONFIG_EFI_HTTP_PROTOCOL) + struct efi_service_binding_protocol http_service_binding; +#endif };
/* @@ -1009,6 +1013,19 @@ efi_status_t efi_net_register(void) goto failure_to_add_protocol; #endif
+#ifdef CONFIG_EFI_HTTP_PROTOCOL + 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; failure_to_add_protocol: printf("ERROR: Failure to add protocol\n"); @@ -1207,8 +1224,6 @@ static efi_status_t efi_net_set_buffer(void **buffer, size_t size) if (size < SZ_64K) size = SZ_64K;
- efi_free_pool(*buffer); - *buffer = efi_alloc(size); if (!*buffer) ret = EFI_OUT_OF_RESOURCES; @@ -1305,6 +1320,7 @@ efi_status_t efi_net_do_request(u8 *url, enum efi_http_method method, void **buf 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 + efi_free_pool(*buffer); ret = efi_net_set_buffer(buffer, (size_t)efi_wget_info.hdr_cont_len); if (ret != EFI_SUCCESS) goto out;

From: Heinrich Schuchardt heinrich.schuchardt@canonical.com
Add long texts for
* EFI HTTP Protocol * EFI HTTP Service Binding Protocol * EFI IPv4 Configuration II Protocol
to the uuid library.
Signed-off-by: Heinrich Schuchardt heinrich.schuchardt@canonical.com Signed-off-by: Adriano Cordova adrianox@gmail.com Reviewed-by: Ilias Apalodimas ilias.apalodimas@linaro.org ---
(no changes since v2) lib/uuid.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
diff --git a/lib/uuid.c b/lib/uuid.c index 538a1ba6aa..97388f597a 100644 --- a/lib/uuid.c +++ b/lib/uuid.c @@ -175,6 +175,20 @@ static const struct { "Firmware Management", EFI_FIRMWARE_MANAGEMENT_PROTOCOL_GUID }, +#if IS_ENABLED(CONFIG_EFI_HTTP_PROTOCOL) + { + "HTTP", + EFI_HTTP_PROTOCOL_GUID, + }, + { + "HTTP Service Binding", + EFI_HTTP_SERVICE_BINDING_PROTOCOL_GUID, + }, + { + "IPv4 Config2", + EFI_IP4_CONFIG2_PROTOCOL_GUID, + }, +#endif /* Configuration table GUIDs */ { "ACPI table",

Add a test for the EFI_HTTP_PROTOCOL and EFI_SEVICE_BINDING_PROTOCOL.
Signed-off-by: Adriano Cordova adrianox@gmail.com ---
(no changes since v2) lib/efi_selftest/Makefile | 2 +- lib/efi_selftest/efi_selftest_http.c | 315 +++++++++++++++++++++++++++ 2 files changed, 316 insertions(+), 1 deletion(-) create mode 100644 lib/efi_selftest/efi_selftest_http.c
diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile index 414701893f..140c08effc 100644 --- a/lib/efi_selftest/Makefile +++ b/lib/efi_selftest/Makefile @@ -52,7 +52,7 @@ efi_selftest_watchdog.o
obj-$(CONFIG_EFI_ECPT) += efi_selftest_ecpt.o obj-$(CONFIG_NETDEVICES) += efi_selftest_snp.o - +obj-$(CONFIG_EFI_HTTP_PROTOCOL) += efi_selftest_http.o obj-$(CONFIG_EFI_DEVICE_PATH_TO_TEXT) += efi_selftest_devicepath.o obj-$(CONFIG_EFI_UNICODE_COLLATION_PROTOCOL2) += \ efi_selftest_unicode_collation.o diff --git a/lib/efi_selftest/efi_selftest_http.c b/lib/efi_selftest/efi_selftest_http.c new file mode 100644 index 0000000000..ce3cf33887 --- /dev/null +++ b/lib/efi_selftest/efi_selftest_http.c @@ -0,0 +1,315 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * efi_selftest_http + * + * This unit test covers the IPv4 Config2 Protocol, Http Service Binding Protocol, + * and Http Protocol. + * + * An Http HEAD and an Http GET request are sent to the same destination. The test + * is successful if the HEAD request gets a response with a valid Content-Length header + * and the subsequent GET request receives the amount of bytes informed by the previous + * Content-Length header. + * + */ + +#include <efi_selftest.h> +#include <charset.h> +#include <net.h> + +static struct efi_boot_services *boottime; + +static struct efi_http_protocol *http; +static struct efi_service_binding_protocol *http_service; +static struct efi_ip4_config2_protocol *ip4_config2; +static efi_handle_t http_protocol_handle; + +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; +static const efi_guid_t efi_ip4_config2_guid = EFI_IP4_CONFIG2_PROTOCOL_GUID; +static int callback_done; + +/* + * 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; + efi_handle_t *net_handle; + efi_uintn_t num_handles; + efi_handle_t *handles; + struct efi_http_config_data http_config; + struct efi_httpv4_access_point ipv4_node; + + boottime = systable->boottime; + + boottime->locate_handle_buffer(BY_PROTOCOL, &efi_ip4_config2_guid, + NULL, &num_handles, &handles); + + for (net_handle = handles; num_handles--; net_handle++) { + ret = boottime->open_protocol(*net_handle, &efi_ip4_config2_guid, + (void **)&ip4_config2, 0, 0, + EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (ret != EFI_SUCCESS || !ip4_config2) + continue; + ret = boottime->open_protocol(*net_handle, + &efi_http_service_binding_guid, + (void **)&http_service, 0, 0, + EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (ret != EFI_SUCCESS || !http_service) + continue; + break; // Get first handle that supports both protocols + } + + if (!ip4_config2 || !http_service) { + efi_st_error("Failed to locate ipv4 config2 or 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; +} + +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 (!ip4_config2 || !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; + } else { + 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; + } + efi_st_printf("HTTP Service Binding: child destroyed successfully\n"); + } + + return exit_status; +} + +EFI_UNIT_TEST(http) = { + .name = "http protocol", + .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT, + .setup = setup, + .execute = execute, + .teardown = teardown, +#ifdef CONFIG_SANDBOX + /* + * Running this test on the sandbox requires setting environment + * variable ethact to a network interface connected to a DHCP server and + * ethrotate to 'no'. + */ + .on_request = true, +#endif +};

On Thu, 28 Nov 2024 at 16:44, Adriano Cordova adrianox@gmail.com wrote:
Add a test for the EFI_HTTP_PROTOCOL and EFI_SEVICE_BINDING_PROTOCOL.
Signed-off-by: Adriano Cordova adrianox@gmail.com
(no changes since v2) lib/efi_selftest/Makefile | 2 +- lib/efi_selftest/efi_selftest_http.c | 315 +++++++++++++++++++++++++++ 2 files changed, 316 insertions(+), 1 deletion(-) create mode 100644 lib/efi_selftest/efi_selftest_http.c
diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile index 414701893f..140c08effc 100644 --- a/lib/efi_selftest/Makefile +++ b/lib/efi_selftest/Makefile @@ -52,7 +52,7 @@ efi_selftest_watchdog.o
obj-$(CONFIG_EFI_ECPT) += efi_selftest_ecpt.o obj-$(CONFIG_NETDEVICES) += efi_selftest_snp.o
+obj-$(CONFIG_EFI_HTTP_PROTOCOL) += efi_selftest_http.o obj-$(CONFIG_EFI_DEVICE_PATH_TO_TEXT) += efi_selftest_devicepath.o obj-$(CONFIG_EFI_UNICODE_COLLATION_PROTOCOL2) += \ efi_selftest_unicode_collation.o diff --git a/lib/efi_selftest/efi_selftest_http.c b/lib/efi_selftest/efi_selftest_http.c new file mode 100644 index 0000000000..ce3cf33887 --- /dev/null +++ b/lib/efi_selftest/efi_selftest_http.c @@ -0,0 +1,315 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/*
- efi_selftest_http
- This unit test covers the IPv4 Config2 Protocol, Http Service Binding Protocol,
- and Http Protocol.
- An Http HEAD and an Http GET request are sent to the same destination. The test
- is successful if the HEAD request gets a response with a valid Content-Length header
- and the subsequent GET request receives the amount of bytes informed by the previous
- Content-Length header.
- */
+#include <efi_selftest.h> +#include <charset.h> +#include <net.h>
+static struct efi_boot_services *boottime;
+static struct efi_http_protocol *http; +static struct efi_service_binding_protocol *http_service; +static struct efi_ip4_config2_protocol *ip4_config2; +static efi_handle_t http_protocol_handle;
+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; +static const efi_guid_t efi_ip4_config2_guid = EFI_IP4_CONFIG2_PROTOCOL_GUID; +static int callback_done;
+/*
- 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;
efi_handle_t *net_handle;
efi_uintn_t num_handles;
efi_handle_t *handles;
struct efi_http_config_data http_config;
struct efi_httpv4_access_point ipv4_node;
boottime = systable->boottime;
boottime->locate_handle_buffer(BY_PROTOCOL, &efi_ip4_config2_guid,
NULL, &num_handles, &handles);
Same remarks as the other test-related patch, Check the number of handles early and exit
for (net_handle = handles; num_handles--; net_handle++) {
ret = boottime->open_protocol(*net_handle, &efi_ip4_config2_guid,
(void **)&ip4_config2, 0, 0,
EFI_OPEN_PROTOCOL_GET_PROTOCOL);
if (ret != EFI_SUCCESS || !ip4_config2)
continue;
ret = boottime->open_protocol(*net_handle,
&efi_http_service_binding_guid,
(void **)&http_service, 0, 0,
EFI_OPEN_PROTOCOL_GET_PROTOCOL);
if (ret != EFI_SUCCESS || !http_service)
continue;
break; // Get first handle that supports both protocols
}
if (!ip4_config2 || !http_service) {
efi_st_error("Failed to locate ipv4 config2 or http service binding protocol\n");
return EFI_ST_FAILURE;
}
http_protocol_handle = NULL;
ret = http_service->create_child(http_service, &http_protocol_handle);
I haven't checked the creation patches yet, but does it make sense to return EFI_SUCCESS if the protocol handle for the creation failed? IOW if http_protocol_handle == NULL shouldn't we return something else?
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;
+}
+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 (!ip4_config2 || !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;
} else {
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;
}
efi_st_printf("HTTP Service Binding: child destroyed successfully\n");
}
return exit_status;
+}
+EFI_UNIT_TEST(http) = {
.name = "http protocol",
.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
.setup = setup,
.execute = execute,
.teardown = teardown,
+#ifdef CONFIG_SANDBOX
/*
* Running this test on the sandbox requires setting environment
* variable ethact to a network interface connected to a DHCP server and
* ethrotate to 'no'.
*/
.on_request = true,
+#endif
+};
2.43.0

El mar, 3 dic 2024 a las 10:11, Ilias Apalodimas (< ilias.apalodimas@linaro.org>) escribió:
On Thu, 28 Nov 2024 at 16:44, Adriano Cordova adrianox@gmail.com wrote:
Add a test for the EFI_HTTP_PROTOCOL and EFI_SEVICE_BINDING_PROTOCOL.
Signed-off-by: Adriano Cordova adrianox@gmail.com
(no changes since v2) lib/efi_selftest/Makefile | 2 +- lib/efi_selftest/efi_selftest_http.c | 315 +++++++++++++++++++++++++++ 2 files changed, 316 insertions(+), 1 deletion(-) create mode 100644 lib/efi_selftest/efi_selftest_http.c
diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile index 414701893f..140c08effc 100644 --- a/lib/efi_selftest/Makefile +++ b/lib/efi_selftest/Makefile @@ -52,7 +52,7 @@ efi_selftest_watchdog.o
obj-$(CONFIG_EFI_ECPT) += efi_selftest_ecpt.o obj-$(CONFIG_NETDEVICES) += efi_selftest_snp.o
+obj-$(CONFIG_EFI_HTTP_PROTOCOL) += efi_selftest_http.o obj-$(CONFIG_EFI_DEVICE_PATH_TO_TEXT) += efi_selftest_devicepath.o obj-$(CONFIG_EFI_UNICODE_COLLATION_PROTOCOL2) += \ efi_selftest_unicode_collation.o diff --git a/lib/efi_selftest/efi_selftest_http.c
b/lib/efi_selftest/efi_selftest_http.c
new file mode 100644 index 0000000000..ce3cf33887 --- /dev/null +++ b/lib/efi_selftest/efi_selftest_http.c @@ -0,0 +1,315 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/*
- efi_selftest_http
- This unit test covers the IPv4 Config2 Protocol, Http Service
Binding Protocol,
- and Http Protocol.
- An Http HEAD and an Http GET request are sent to the same
destination. The test
- is successful if the HEAD request gets a response with a valid
Content-Length header
- and the subsequent GET request receives the amount of bytes informed
by the previous
- Content-Length header.
- */
+#include <efi_selftest.h> +#include <charset.h> +#include <net.h>
+static struct efi_boot_services *boottime;
+static struct efi_http_protocol *http; +static struct efi_service_binding_protocol *http_service; +static struct efi_ip4_config2_protocol *ip4_config2; +static efi_handle_t http_protocol_handle;
+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;
+static const efi_guid_t efi_ip4_config2_guid =
EFI_IP4_CONFIG2_PROTOCOL_GUID;
+static int callback_done;
+/*
- 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;
efi_handle_t *net_handle;
efi_uintn_t num_handles;
efi_handle_t *handles;
struct efi_http_config_data http_config;
struct efi_httpv4_access_point ipv4_node;
boottime = systable->boottime;
boottime->locate_handle_buffer(BY_PROTOCOL,
&efi_ip4_config2_guid,
NULL, &num_handles, &handles);
Same remarks as the other test-related patch, Check the number of handles early and exit
for (net_handle = handles; num_handles--; net_handle++) {
ret = boottime->open_protocol(*net_handle,
&efi_ip4_config2_guid,
(void **)&ip4_config2, 0,
0,
EFI_OPEN_PROTOCOL_GET_PROTOCOL);
if (ret != EFI_SUCCESS || !ip4_config2)
continue;
ret = boottime->open_protocol(*net_handle,
&efi_http_service_binding_guid,
(void **)&http_service, 0,
0,
EFI_OPEN_PROTOCOL_GET_PROTOCOL);
if (ret != EFI_SUCCESS || !http_service)
continue;
break; // Get first handle that supports both protocols
}
if (!ip4_config2 || !http_service) {
efi_st_error("Failed to locate ipv4 config2 or http
service binding protocol\n");
return EFI_ST_FAILURE;
}
http_protocol_handle = NULL;
ret = http_service->create_child(http_service,
&http_protocol_handle);
I haven't checked the creation patches yet, but does it make sense to return EFI_SUCCESS if the protocol handle for the creation failed? IOW if http_protocol_handle == NULL shouldn't we return something else?
Hi Ilias, thanks again for the reviews,
I am not sure I understand your point here. If the creation fails then we return EFI_ST_FAILURE
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;
+}
+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 (!ip4_config2 || !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;
} else {
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;
}
efi_st_printf("HTTP Service Binding: child destroyed
successfully\n");
}
return exit_status;
+}
+EFI_UNIT_TEST(http) = {
.name = "http protocol",
.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
.setup = setup,
.execute = execute,
.teardown = teardown,
+#ifdef CONFIG_SANDBOX
/*
* Running this test on the sandbox requires setting environment
* variable ethact to a network interface connected to a DHCP
server and
* ethrotate to 'no'.
*/
.on_request = true,
+#endif
+};
2.43.0

Add a test for the EFI_IP4_CONFIG2_PROTOCOL. The test sets the ip policy to static, adds an ip address, and then reads the current ip address and checks for it to be the same as the one that was set.
Signed-off-by: Adriano Cordova adrianox@gmail.com --- (no changes since v4) Changes in v4: - Removed unnecessary void* casts
(no changes since v2) lib/efi_selftest/Makefile | 2 + lib/efi_selftest/efi_selftest_ipconfig.c | 170 +++++++++++++++++++++++ 2 files changed, 172 insertions(+) create mode 100644 lib/efi_selftest/efi_selftest_ipconfig.c
diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile index 140c08effc..17fbfad116 100644 --- a/lib/efi_selftest/Makefile +++ b/lib/efi_selftest/Makefile @@ -53,6 +53,8 @@ efi_selftest_watchdog.o obj-$(CONFIG_EFI_ECPT) += efi_selftest_ecpt.o obj-$(CONFIG_NETDEVICES) += efi_selftest_snp.o obj-$(CONFIG_EFI_HTTP_PROTOCOL) += efi_selftest_http.o +obj-$(CONFIG_EFI_HTTP_PROTOCOL) += efi_selftest_ipconfig.o + obj-$(CONFIG_EFI_DEVICE_PATH_TO_TEXT) += efi_selftest_devicepath.o obj-$(CONFIG_EFI_UNICODE_COLLATION_PROTOCOL2) += \ efi_selftest_unicode_collation.o diff --git a/lib/efi_selftest/efi_selftest_ipconfig.c b/lib/efi_selftest/efi_selftest_ipconfig.c new file mode 100644 index 0000000000..67a7d4f411 --- /dev/null +++ b/lib/efi_selftest/efi_selftest_ipconfig.c @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * efi_selftest_ipconfig + * + * This unit test covers the IPv4 Config2 Protocol. + * + */ + +#include <efi_selftest.h> +#include <charset.h> +#include <net.h> + +static struct efi_boot_services *boottime; + +static struct efi_ip4_config2_protocol *ip4_config2; +static const efi_guid_t efi_ip4_config2_guid = EFI_IP4_CONFIG2_PROTOCOL_GUID; + +/* + * Setup unit test. + * + * Open IPv4 Config2 protocol + * + * @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; + efi_handle_t *net_handle; + efi_uintn_t num_handles; + efi_handle_t *handles; + + boottime = systable->boottime; + + boottime->locate_handle_buffer(BY_PROTOCOL, &efi_ip4_config2_guid, + NULL, &num_handles, &handles); + + for (net_handle = handles; num_handles--; net_handle++) { + ret = boottime->open_protocol(*net_handle, &efi_ip4_config2_guid, + (void **)&ip4_config2, 0, 0, + EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (ret != EFI_SUCCESS || !ip4_config2) + continue; + break; // Get first handle that supports ipv4 + } + + if (!ip4_config2) { + efi_st_error("Failed to locate ipv4 config2 protocol\n"); + return EFI_ST_FAILURE; + } + + return EFI_ST_SUCCESS; +} + +/* + * Execute unit test. + * + * + * Return: EFI_ST_SUCCESS for success + */ +static int execute(void) +{ + efi_status_t ret; + enum efi_ip4_config2_policy policy; + efi_uintn_t data_size; + struct efi_ip4_config2_manual_address manual_address; + struct efi_ip4_config2_manual_address orig_address; + u8 netmask[] = {255, 255, 255, 0}; + u8 ip[] = {10, 0, 0, 1}; + + /* Setup may have failed */ + if (!ip4_config2) { + efi_st_error("Setup failure, cannot proceed with test\n"); + return EFI_ST_FAILURE; + } + + /* Set policy to static */ + policy = EFI_IP4_CONFIG2_POLICY_STATIC; + ret = ip4_config2->set_data(ip4_config2, EFI_IP4_CONFIG2_DATA_TYPE_POLICY, + sizeof(enum efi_ip4_config2_policy), (void *)&policy); + if (ret != EFI_SUCCESS) { + efi_st_error("Failed to set policy\n"); + return EFI_ST_FAILURE; + } + + /* Save original ip address and netmask */ + data_size = sizeof(struct efi_ip4_config2_manual_address); + ret = ip4_config2->get_data(ip4_config2, EFI_IP4_CONFIG2_DATA_TYPE_MANUAL_ADDRESS, + &data_size, &orig_address); + if (ret != EFI_SUCCESS) { + efi_st_error("Failed to save original ip address and netmask\n"); + return EFI_ST_FAILURE; + } + + /* Set static ip and netmask */ + memcpy(&manual_address.address, ip, + sizeof(struct efi_ipv4_address)); + memcpy(&manual_address.subnet_mask, netmask, + sizeof(struct efi_ipv4_address)); + ret = ip4_config2->set_data(ip4_config2, EFI_IP4_CONFIG2_DATA_TYPE_MANUAL_ADDRESS, + sizeof(struct efi_ip4_config2_manual_address), &manual_address); + if (ret != EFI_SUCCESS) { + efi_st_error("Failed to get ip address and netmask\n"); + return EFI_ST_FAILURE; + } + + /* Try to set interface info, this should fail */ + ret = ip4_config2->set_data(ip4_config2, EFI_IP4_CONFIG2_DATA_TYPE_INTERFACEINFO, 0, NULL); + if (ret == EFI_SUCCESS) { + efi_st_error("Interface info is read-only\n"); + return EFI_ST_FAILURE; + } + + /* Get ip address and netmask and check that they match with the previously set ones */ + data_size = sizeof(struct efi_ip4_config2_manual_address); + ret = ip4_config2->get_data(ip4_config2, EFI_IP4_CONFIG2_DATA_TYPE_MANUAL_ADDRESS, + &data_size, &manual_address); + if (ret != EFI_SUCCESS) { + efi_st_error("Failed to get ip address and netmask\n"); + return EFI_ST_FAILURE; + } + if (memcmp(ip, &manual_address.address, + sizeof(struct efi_ipv4_address)) || + memcmp(netmask, &manual_address.subnet_mask, + sizeof(struct efi_ipv4_address))) { + efi_st_error("Ip address mismatch\n"); + return EFI_ST_FAILURE; + } + + /* Restore original ip address and netmask */ + ret = ip4_config2->set_data(ip4_config2, EFI_IP4_CONFIG2_DATA_TYPE_MANUAL_ADDRESS, + sizeof(struct efi_ip4_config2_manual_address), &orig_address); + if (ret != EFI_SUCCESS) { + efi_st_error("Failed to restore original ip address and netmask\n"); + return EFI_ST_FAILURE; + } + + efi_st_printf("Efi ipconfig test execute succeeded\n"); + 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(ipconfig) = { + .name = "IPv4 config2 protocol", + .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT, + .setup = setup, + .execute = execute, + .teardown = teardown, +#ifdef CONFIG_SANDBOX + /* + * Running this test on the sandbox requires setting environment + * variable ethact to a network interface connected to a DHCP server and + * ethrotate to 'no'. + */ + .on_request = true, +#endif +};

Hi Adriano
On Thu, 28 Nov 2024 at 16:44, Adriano Cordova adrianox@gmail.com wrote:
Add a test for the EFI_IP4_CONFIG2_PROTOCOL. The test sets the ip policy to static, adds an ip address, and then reads the current ip address and checks for it to be the same as the one that was set.
Signed-off-by: Adriano Cordova adrianox@gmail.com
(no changes since v4) Changes in v4:
- Removed unnecessary void* casts
(no changes since v2) lib/efi_selftest/Makefile | 2 + lib/efi_selftest/efi_selftest_ipconfig.c | 170 +++++++++++++++++++++++ 2 files changed, 172 insertions(+) create mode 100644 lib/efi_selftest/efi_selftest_ipconfig.c
diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile index 140c08effc..17fbfad116 100644 --- a/lib/efi_selftest/Makefile +++ b/lib/efi_selftest/Makefile @@ -53,6 +53,8 @@ efi_selftest_watchdog.o obj-$(CONFIG_EFI_ECPT) += efi_selftest_ecpt.o obj-$(CONFIG_NETDEVICES) += efi_selftest_snp.o obj-$(CONFIG_EFI_HTTP_PROTOCOL) += efi_selftest_http.o +obj-$(CONFIG_EFI_HTTP_PROTOCOL) += efi_selftest_ipconfig.o
obj-$(CONFIG_EFI_DEVICE_PATH_TO_TEXT) += efi_selftest_devicepath.o obj-$(CONFIG_EFI_UNICODE_COLLATION_PROTOCOL2) += \ efi_selftest_unicode_collation.o diff --git a/lib/efi_selftest/efi_selftest_ipconfig.c b/lib/efi_selftest/efi_selftest_ipconfig.c new file mode 100644 index 0000000000..67a7d4f411 --- /dev/null +++ b/lib/efi_selftest/efi_selftest_ipconfig.c @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/*
- efi_selftest_ipconfig
- This unit test covers the IPv4 Config2 Protocol.
- */
+#include <efi_selftest.h> +#include <charset.h> +#include <net.h>
+static struct efi_boot_services *boottime;
+static struct efi_ip4_config2_protocol *ip4_config2; +static const efi_guid_t efi_ip4_config2_guid = EFI_IP4_CONFIG2_PROTOCOL_GUID;
+/*
- Setup unit test.
- Open IPv4 Config2 protocol
- @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;
efi_handle_t *net_handle;
efi_uintn_t num_handles;
efi_handle_t *handles;
boottime = systable->boottime;
boottime->locate_handle_buffer(BY_PROTOCOL, &efi_ip4_config2_guid,
NULL, &num_handles, &handles);
You can avoid looping over a null handle here by checking num_handles and remove the if (!ip4_config2) { later.
for (net_handle = handles; num_handles--; net_handle++) {
ret = boottime->open_protocol(*net_handle, &efi_ip4_config2_guid,
(void **)&ip4_config2, 0, 0,
EFI_OPEN_PROTOCOL_GET_PROTOCOL);
if (ret != EFI_SUCCESS || !ip4_config2)
continue;
break; // Get first handle that supports ipv4
}
if (!ip4_config2) {
efi_st_error("Failed to locate ipv4 config2 protocol\n");
return EFI_ST_FAILURE;
}
return EFI_ST_SUCCESS;
+}
+/*
- Execute unit test.
- Return: EFI_ST_SUCCESS for success
- */
+static int execute(void) +{
efi_status_t ret;
enum efi_ip4_config2_policy policy;
efi_uintn_t data_size;
struct efi_ip4_config2_manual_address manual_address;
struct efi_ip4_config2_manual_address orig_address;
u8 netmask[] = {255, 255, 255, 0};
u8 ip[] = {10, 0, 0, 1};
/* Setup may have failed */
if (!ip4_config2) {
efi_st_error("Setup failure, cannot proceed with test\n");
return EFI_ST_FAILURE;
}
Will this run if setup fails?
/* Set policy to static */
policy = EFI_IP4_CONFIG2_POLICY_STATIC;
ret = ip4_config2->set_data(ip4_config2, EFI_IP4_CONFIG2_DATA_TYPE_POLICY,
sizeof(enum efi_ip4_config2_policy), (void *)&policy);
if (ret != EFI_SUCCESS) {
efi_st_error("Failed to set policy\n");
return EFI_ST_FAILURE;
}
/* Save original ip address and netmask */
data_size = sizeof(struct efi_ip4_config2_manual_address);
sizeof(manual_address); same for the rest of the code
ret = ip4_config2->get_data(ip4_config2, EFI_IP4_CONFIG2_DATA_TYPE_MANUAL_ADDRESS,
&data_size, &orig_address);
if (ret != EFI_SUCCESS) {
efi_st_error("Failed to save original ip address and netmask\n");
return EFI_ST_FAILURE;
}
/* Set static ip and netmask */
memcpy(&manual_address.address, ip,
sizeof(struct efi_ipv4_address));
memcpy(&manual_address.subnet_mask, netmask,
sizeof(struct efi_ipv4_address));
ret = ip4_config2->set_data(ip4_config2, EFI_IP4_CONFIG2_DATA_TYPE_MANUAL_ADDRESS,
sizeof(struct efi_ip4_config2_manual_address), &manual_address);
if (ret != EFI_SUCCESS) {
efi_st_error("Failed to get ip address and netmask\n");
return EFI_ST_FAILURE;
}
/* Try to set interface info, this should fail */
ret = ip4_config2->set_data(ip4_config2, EFI_IP4_CONFIG2_DATA_TYPE_INTERFACEINFO, 0, NULL);
if (ret == EFI_SUCCESS) {
efi_st_error("Interface info is read-only\n");
return EFI_ST_FAILURE;
}
/* Get ip address and netmask and check that they match with the previously set ones */
data_size = sizeof(struct efi_ip4_config2_manual_address);
ret = ip4_config2->get_data(ip4_config2, EFI_IP4_CONFIG2_DATA_TYPE_MANUAL_ADDRESS,
&data_size, &manual_address);
if (ret != EFI_SUCCESS) {
efi_st_error("Failed to get ip address and netmask\n");
return EFI_ST_FAILURE;
}
if (memcmp(ip, &manual_address.address,
sizeof(struct efi_ipv4_address)) ||
memcmp(netmask, &manual_address.subnet_mask,
sizeof(struct efi_ipv4_address))) {
efi_st_error("Ip address mismatch\n");
return EFI_ST_FAILURE;
}
/* Restore original ip address and netmask */
ret = ip4_config2->set_data(ip4_config2, EFI_IP4_CONFIG2_DATA_TYPE_MANUAL_ADDRESS,
sizeof(struct efi_ip4_config2_manual_address), &orig_address);
if (ret != EFI_SUCCESS) {
efi_st_error("Failed to restore original ip address and netmask\n");
return EFI_ST_FAILURE;
}
efi_st_printf("Efi ipconfig test execute succeeded\n");
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(ipconfig) = {
.name = "IPv4 config2 protocol",
.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
.setup = setup,
.execute = execute,
.teardown = teardown,
+#ifdef CONFIG_SANDBOX
/*
* Running this test on the sandbox requires setting environment
* variable ethact to a network interface connected to a DHCP server and
* ethrotate to 'no'.
*/
.on_request = true,
+#endif
+};
2.43.0
Thanks /Ilias

El mar, 3 dic 2024 a las 10:03, Ilias Apalodimas (< ilias.apalodimas@linaro.org>) escribió:
Hi Adriano
On Thu, 28 Nov 2024 at 16:44, Adriano Cordova adrianox@gmail.com wrote:
Add a test for the EFI_IP4_CONFIG2_PROTOCOL. The test sets the ip policy to static, adds an ip address, and then reads the current ip address and checks for it to be the same as the one that was set.
Signed-off-by: Adriano Cordova adrianox@gmail.com
(no changes since v4) Changes in v4:
- Removed unnecessary void* casts
(no changes since v2) lib/efi_selftest/Makefile | 2 + lib/efi_selftest/efi_selftest_ipconfig.c | 170 +++++++++++++++++++++++ 2 files changed, 172 insertions(+) create mode 100644 lib/efi_selftest/efi_selftest_ipconfig.c
diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile index 140c08effc..17fbfad116 100644 --- a/lib/efi_selftest/Makefile +++ b/lib/efi_selftest/Makefile @@ -53,6 +53,8 @@ efi_selftest_watchdog.o obj-$(CONFIG_EFI_ECPT) += efi_selftest_ecpt.o obj-$(CONFIG_NETDEVICES) += efi_selftest_snp.o obj-$(CONFIG_EFI_HTTP_PROTOCOL) += efi_selftest_http.o +obj-$(CONFIG_EFI_HTTP_PROTOCOL) += efi_selftest_ipconfig.o
obj-$(CONFIG_EFI_DEVICE_PATH_TO_TEXT) += efi_selftest_devicepath.o obj-$(CONFIG_EFI_UNICODE_COLLATION_PROTOCOL2) += \ efi_selftest_unicode_collation.o diff --git a/lib/efi_selftest/efi_selftest_ipconfig.c
b/lib/efi_selftest/efi_selftest_ipconfig.c
new file mode 100644 index 0000000000..67a7d4f411 --- /dev/null +++ b/lib/efi_selftest/efi_selftest_ipconfig.c @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/*
- efi_selftest_ipconfig
- This unit test covers the IPv4 Config2 Protocol.
- */
+#include <efi_selftest.h> +#include <charset.h> +#include <net.h>
+static struct efi_boot_services *boottime;
+static struct efi_ip4_config2_protocol *ip4_config2; +static const efi_guid_t efi_ip4_config2_guid =
EFI_IP4_CONFIG2_PROTOCOL_GUID;
+/*
- Setup unit test.
- Open IPv4 Config2 protocol
- @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;
efi_handle_t *net_handle;
efi_uintn_t num_handles;
efi_handle_t *handles;
boottime = systable->boottime;
boottime->locate_handle_buffer(BY_PROTOCOL,
&efi_ip4_config2_guid,
NULL, &num_handles, &handles);
You can avoid looping over a null handle here by checking num_handles and remove the if (!ip4_config2) { later.
The (!ip4_config2) { is necessay because the protocol can fail to open too
for (net_handle = handles; num_handles--; net_handle++) {
ret = boottime->open_protocol(*net_handle,
&efi_ip4_config2_guid,
(void **)&ip4_config2, 0,
0,
EFI_OPEN_PROTOCOL_GET_PROTOCOL);
if (ret != EFI_SUCCESS || !ip4_config2)
continue;
break; // Get first handle that supports ipv4
}
if (!ip4_config2) {
efi_st_error("Failed to locate ipv4 config2 protocol\n");
return EFI_ST_FAILURE;
}
return EFI_ST_SUCCESS;
+}
+/*
- Execute unit test.
- Return: EFI_ST_SUCCESS for success
- */
+static int execute(void) +{
efi_status_t ret;
enum efi_ip4_config2_policy policy;
efi_uintn_t data_size;
struct efi_ip4_config2_manual_address manual_address;
struct efi_ip4_config2_manual_address orig_address;
u8 netmask[] = {255, 255, 255, 0};
u8 ip[] = {10, 0, 0, 1};
/* Setup may have failed */
if (!ip4_config2) {
efi_st_error("Setup failure, cannot proceed with
test\n");
return EFI_ST_FAILURE;
}
Will this run if setup fails?
I do not know, I took the skeleton from another test where this was present
/* Set policy to static */
policy = EFI_IP4_CONFIG2_POLICY_STATIC;
ret = ip4_config2->set_data(ip4_config2,
EFI_IP4_CONFIG2_DATA_TYPE_POLICY,
sizeof(enum efi_ip4_config2_policy), (void
*)&policy);
if (ret != EFI_SUCCESS) {
efi_st_error("Failed to set policy\n");
return EFI_ST_FAILURE;
}
/* Save original ip address and netmask */
data_size = sizeof(struct efi_ip4_config2_manual_address);
sizeof(manual_address); same for the rest of the code
ret = ip4_config2->get_data(ip4_config2,
EFI_IP4_CONFIG2_DATA_TYPE_MANUAL_ADDRESS,
&data_size, &orig_address);
if (ret != EFI_SUCCESS) {
efi_st_error("Failed to save original ip address and
netmask\n");
return EFI_ST_FAILURE;
}
/* Set static ip and netmask */
memcpy(&manual_address.address, ip,
sizeof(struct efi_ipv4_address));
memcpy(&manual_address.subnet_mask, netmask,
sizeof(struct efi_ipv4_address));
ret = ip4_config2->set_data(ip4_config2,
EFI_IP4_CONFIG2_DATA_TYPE_MANUAL_ADDRESS,
sizeof(struct
efi_ip4_config2_manual_address), &manual_address);
if (ret != EFI_SUCCESS) {
efi_st_error("Failed to get ip address and netmask\n");
return EFI_ST_FAILURE;
}
/* Try to set interface info, this should fail */
ret = ip4_config2->set_data(ip4_config2,
EFI_IP4_CONFIG2_DATA_TYPE_INTERFACEINFO, 0, NULL);
if (ret == EFI_SUCCESS) {
efi_st_error("Interface info is read-only\n");
return EFI_ST_FAILURE;
}
/* Get ip address and netmask and check that they match with the
previously set ones */
data_size = sizeof(struct efi_ip4_config2_manual_address);
ret = ip4_config2->get_data(ip4_config2,
EFI_IP4_CONFIG2_DATA_TYPE_MANUAL_ADDRESS,
&data_size, &manual_address);
if (ret != EFI_SUCCESS) {
efi_st_error("Failed to get ip address and netmask\n");
return EFI_ST_FAILURE;
}
if (memcmp(ip, &manual_address.address,
sizeof(struct efi_ipv4_address)) ||
memcmp(netmask, &manual_address.subnet_mask,
sizeof(struct efi_ipv4_address))) {
efi_st_error("Ip address mismatch\n");
return EFI_ST_FAILURE;
}
/* Restore original ip address and netmask */
ret = ip4_config2->set_data(ip4_config2,
EFI_IP4_CONFIG2_DATA_TYPE_MANUAL_ADDRESS,
sizeof(struct
efi_ip4_config2_manual_address), &orig_address);
if (ret != EFI_SUCCESS) {
efi_st_error("Failed to restore original ip address and
netmask\n");
return EFI_ST_FAILURE;
}
efi_st_printf("Efi ipconfig test execute succeeded\n");
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(ipconfig) = {
.name = "IPv4 config2 protocol",
.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
.setup = setup,
.execute = execute,
.teardown = teardown,
+#ifdef CONFIG_SANDBOX
/*
* Running this test on the sandbox requires setting environment
* variable ethact to a network interface connected to a DHCP
server and
* ethrotate to 'no'.
*/
.on_request = true,
+#endif
+};
2.43.0
Thanks /Ilias
participants (5)
-
Adriano Cordova
-
Adriano Córdova
-
Heinrich Schuchardt
-
Ilias Apalodimas
-
Jerome Forissier