[PATCH v4 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
The fist two patches of this series are not efi specific and have also been sent to the corresponding maintainers, but this series depends on them, so they are added here for redundancy. 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 | 42 ++ lib/efi_loader/Kconfig | 17 + lib/efi_loader/Makefile | 2 + lib/efi_loader/efi_device_path.c | 92 +++- 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 | 354 ++++++++++++++- 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 | 1 + net/wget.c | 37 +- 15 files changed, 2029 insertions(+), 22 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. -Check for port in the uri when transforming to legacy wget syntax inside wget_with_dns. -Move the check for CMD_DNS inside wget_with_dns.
Signed-off-by: Adriano Cordova adrianox@gmail.com ---
v3: Removed console output inside efi routine.
(no changes since v2) net/wget.c | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-)
diff --git a/net/wget.c b/net/wget.c index 3bc2522cde..1166aa5aa6 100644 --- a/net/wget.c +++ b/net/wget.c @@ -530,12 +530,10 @@ 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 ret; - char *s, *host_name, *file_name, *str_copy; - + char *s, *host_name, *file_name, *str_copy, *port; /* * Download file using wget. * @@ -550,24 +548,33 @@ 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) { + port = host_name; + host_name = strsep(&port, ":"); + + 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)); @@ -581,7 +588,6 @@ out:
return ret < 0 ? ret : 0; } -#endif
/** * wget_validate_uri() - validate the uri for wget

On 18.11.24 22:08, Adriano Cordova wrote:
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. -Check for port in the uri when transforming to legacy wget syntax inside wget_with_dns. -Move the check for CMD_DNS inside wget_with_dns.
Signed-off-by: Adriano Cordova adrianox@gmail.com
Changes in v4 are not described:
* drop console output * avoid checking non-zero via '!= 0'.
v3: Removed console output inside efi routine.
(no changes since v2) net/wget.c | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-)
diff --git a/net/wget.c b/net/wget.c index 3bc2522cde..1166aa5aa6 100644 --- a/net/wget.c +++ b/net/wget.c @@ -530,12 +530,10 @@ 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 ret;
- char *s, *host_name, *file_name, *str_copy;
- char *s, *host_name, *file_name, *str_copy, *port;
nits: Missing empty line.
/* * Download file using wget. * @@ -550,24 +548,33 @@ 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) {
- port = host_name;
- host_name = strsep(&port, ":");
RFC 1738 has:
login = [ user [ ":" password ] "@" ] hostport hostport = host [ ":" port ]
So here variable port may point to the password. And host_name to the user name. We might check for '@' and bail out with an error code if it occurs.
You are determining the host port as a string, but I cannot see where you are ever using it.
In wget_send_stored() I see this line:
server_port = env_get_ulong("httpdstp", 10, SERVER_PORT) & 0xffff;
Maybe make server_port a static variable and set it here if variable port does not contain a number.
Best regards
Heinrich
- 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)); @@ -581,7 +588,6 @@ out:
return ret < 0 ? ret : 0; } -#endif
/**
- wget_validate_uri() - validate the uri for wget

El mar, 19 nov 2024 a las 6:27, Heinrich Schuchardt (< heinrich.schuchardt@canonical.com>) escribió:
On 18.11.24 22:08, Adriano Cordova wrote:
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. -Check for port in the uri when transforming to legacy wget syntax inside wget_with_dns. -Move the check for CMD_DNS inside wget_with_dns.
Signed-off-by: Adriano Cordova adrianox@gmail.com
Changes in v4 are not described:
- drop console output
- avoid checking non-zero via '!= 0'.
v3: Removed console output inside efi routine.
(no changes since v2) net/wget.c | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-)
diff --git a/net/wget.c b/net/wget.c index 3bc2522cde..1166aa5aa6 100644 --- a/net/wget.c +++ b/net/wget.c @@ -530,12 +530,10 @@ 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 ret;
char *s, *host_name, *file_name, *str_copy;
char *s, *host_name, *file_name, *str_copy, *port;
nits: Missing empty line.
/* * Download file using wget. *
@@ -550,24 +548,33 @@ 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) {
port = host_name;
host_name = strsep(&port, ":");
RFC 1738 has:
login = [ user [ ":" password ] "@" ] hostport hostport = host [ ":" port ]
So here variable port may point to the password. And host_name to the user name. We might check for '@' and bail out with an error code if it occurs.
You are determining the host port as a string, but I cannot see where you are ever using it.
In wget_send_stored() I see this line:
server_port = env_get_ulong("httpdstp", 10, SERVER_PORT) & 0xffff;
Maybe make server_port a static variable and set it here if variable port does not contain a number.
Best regards
Heinrich
The current documentation and implementation of wget is not compliant with RFC 1738 and only supports ip:path. I will erase the port and leave the wget parsing as it is documented (ip:path). I think reworking the legacy wget parsing to be RFC 1738 compliant can be done in a separate patch.
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));
@@ -581,7 +588,6 @@ out:
return ret < 0 ? ret : 0;
} -#endif
/**
- wget_validate_uri() - validate the uri for wget

On 11/18/24 22:08, Adriano Cordova wrote:
This was marked as TODO in the code: -Enable use of wget_with_dns even if CMD_DNS is disabled if
Then I believe the function should be renamed. _with_dns is misleading.
the given uri has the ip address for the http server. -Check for port in the uri when transforming to legacy wget syntax inside wget_with_dns. -Move the check for CMD_DNS inside wget_with_dns.
Signed-off-by: Adriano Cordova adrianox@gmail.com
v3: Removed console output inside efi routine.
(no changes since v2) net/wget.c | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-)
diff --git a/net/wget.c b/net/wget.c index 3bc2522cde..1166aa5aa6 100644 --- a/net/wget.c +++ b/net/wget.c @@ -530,12 +530,10 @@ 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 ret;
- char *s, *host_name, *file_name, *str_copy;
- char *s, *host_name, *file_name, *str_copy, *port; /*
- Download file using wget.
@@ -550,24 +548,33 @@ 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) {
- port = host_name;
- host_name = strsep(&port, ":");
You should do something with the port (or reject URLs with a non-default port).
- 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)); @@ -581,7 +588,6 @@ out:
return ret < 0 ? ret : 0; } -#endif
/**
- wget_validate_uri() - validate the uri for wget
Thanks,

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 v4: - Fixed memcpy mistake
Changes in v3: - Removed some unnecessary void* casts. - Changed sizeof(struct efi_device_path_ipv4) to sizeof(*ipv4dp) in efi_dp_from_ipv4.
lib/efi_loader/efi_device_path.c | 38 ++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+)
diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index ee387e1dfd..cdeea4791f 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -974,6 +974,44 @@ struct efi_device_path __maybe_unused *efi_dp_from_eth(void) return start; }
+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; + efi_uintn_t ipv4dp_len; + struct efi_device_path_ipv4 *ipv4dp; + char *pos; + + ipv4dp_len = sizeof(*ipv4dp); + ipv4dp = efi_alloc(ipv4dp_len + sizeof(END)); + if (!ipv4dp) { + log_err("Out of memory\n"); + return NULL; + } + + ipv4dp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; + ipv4dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_IPV4; + ipv4dp->dp.length = ipv4dp_len; + ipv4dp->protocol = 6; + if (ip) + memcpy(&ipv4dp->local_ip_address, ip, sizeof(*ip)); + if (mask) + memcpy(&ipv4dp->subnet_mask, mask, sizeof(*mask)); + if (srv) + memcpy(&ipv4dp->remote_ip_address, srv, sizeof(*srv)); + pos = (void *)(uintptr_t)ipv4dp + ipv4dp_len; + memcpy(pos, &END, sizeof(END)); + + dp1 = efi_dp_from_eth(); + dp2 = efi_dp_concat(dp1, (const struct efi_device_path *)ipv4dp, 0); + + efi_free_pool(ipv4dp); + 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 18.11.24 22:08, Adriano Cordova 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 v4:
- Fixed memcpy mistake
Changes in v3:
Removed some unnecessary void* casts.
Changed sizeof(struct efi_device_path_ipv4) to sizeof(*ipv4dp) in efi_dp_from_ipv4.
lib/efi_loader/efi_device_path.c | 38 ++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+)
diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index ee387e1dfd..cdeea4791f 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -974,6 +974,44 @@ struct efi_device_path __maybe_unused *efi_dp_from_eth(void) return start; }
A function description is missing.
+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;
- efi_uintn_t ipv4dp_len;
- struct efi_device_path_ipv4 *ipv4dp;
- char *pos;
- ipv4dp_len = sizeof(*ipv4dp);
- ipv4dp = efi_alloc(ipv4dp_len + sizeof(END));
ipv4dp is a small structure and is freed below. Allocating it on the stack would reduce the code size.
- if (!ipv4dp) {
log_err("Out of memory\n");
Writing messages inside a protocol implementation should be avoided.
return NULL;
- }
- ipv4dp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
- ipv4dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_IPV4;
- ipv4dp->dp.length = ipv4dp_len;
- ipv4dp->protocol = 6;
- if (ip)
memcpy(&ipv4dp->local_ip_address, ip, sizeof(*ip));
- if (mask)
memcpy(&ipv4dp->subnet_mask, mask, sizeof(*mask));
- if (srv)
memcpy(&ipv4dp->remote_ip_address, srv, sizeof(*srv));
- pos = (void *)(uintptr_t)ipv4dp + ipv4dp_len;
Here you
1 - convert ipv4dp to uintptr_t 2 - convert the uintptr_t value to void * 3 - add an offset to the void * value
In the C standard adding an offset to a void * value results in undefined behavior. Unfortunately U-Boot code uses it a lot.
pos = (char *)ipv4dp + ipv4dp_len;
is all it takes to have a defined behavior here.
Best regards
Heinrich
- memcpy(pos, &END, sizeof(END));
- dp1 = efi_dp_from_eth();
- dp2 = efi_dp_concat(dp1, (const struct efi_device_path *)ipv4dp, 0);
- efi_free_pool(ipv4dp);
- 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 Tue, 19 Nov 2024 at 11:50, Heinrich Schuchardt heinrich.schuchardt@canonical.com wrote:
On 18.11.24 22:08, Adriano Cordova 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 v4:
- Fixed memcpy mistake
Changes in v3:
Removed some unnecessary void* casts.
Changed sizeof(struct efi_device_path_ipv4) to sizeof(*ipv4dp) in efi_dp_from_ipv4.
lib/efi_loader/efi_device_path.c | 38 ++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+)
diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index ee387e1dfd..cdeea4791f 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -974,6 +974,44 @@ struct efi_device_path __maybe_unused *efi_dp_from_eth(void) return start; }
A function description is missing.
+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;
efi_uintn_t ipv4dp_len;
struct efi_device_path_ipv4 *ipv4dp;
char *pos;
ipv4dp_len = sizeof(*ipv4dp);
ipv4dp = efi_alloc(ipv4dp_len + sizeof(END));
ipv4dp is a small structure and is freed below. Allocating it on the stack would reduce the code size.
if (!ipv4dp) {
log_err("Out of memory\n");
Writing messages inside a protocol implementation should be avoided.
return NULL;
}
ipv4dp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
ipv4dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_IPV4;
ipv4dp->dp.length = ipv4dp_len;
ipv4dp->protocol = 6;
if (ip)
memcpy(&ipv4dp->local_ip_address, ip, sizeof(*ip));
if (mask)
memcpy(&ipv4dp->subnet_mask, mask, sizeof(*mask));
if (srv)
memcpy(&ipv4dp->remote_ip_address, srv, sizeof(*srv));
pos = (void *)(uintptr_t)ipv4dp + ipv4dp_len;
Here you
1 - convert ipv4dp to uintptr_t 2 - convert the uintptr_t value to void * 3 - add an offset to the void * value
In the C standard adding an offset to a void * value results in undefined behavior. Unfortunately U-Boot code uses it a lot.
It's a known support gcc extension that treats void* similar to char*. Do you think it's worth changing all of the callsites we have?
Thanks /Ilias
pos = (char *)ipv4dp + ipv4dp_len;
is all it takes to have a defined behavior here.
Best regards
Heinrich
memcpy(pos, &END, sizeof(END));
dp1 = efi_dp_from_eth();
dp2 = efi_dp_concat(dp1, (const struct efi_device_path *)ipv4dp, 0);
efi_free_pool(ipv4dp);
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 19.11.24 11:34, Ilias Apalodimas wrote:
On Tue, 19 Nov 2024 at 11:50, Heinrich Schuchardt heinrich.schuchardt@canonical.com wrote:
On 18.11.24 22:08, Adriano Cordova 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 v4:
- Fixed memcpy mistake
Changes in v3:
Removed some unnecessary void* casts.
Changed sizeof(struct efi_device_path_ipv4) to sizeof(*ipv4dp) in efi_dp_from_ipv4.
lib/efi_loader/efi_device_path.c | 38 ++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+)
diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index ee387e1dfd..cdeea4791f 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -974,6 +974,44 @@ struct efi_device_path __maybe_unused *efi_dp_from_eth(void) return start; }
A function description is missing.
+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;
efi_uintn_t ipv4dp_len;
struct efi_device_path_ipv4 *ipv4dp;
char *pos;
ipv4dp_len = sizeof(*ipv4dp);
ipv4dp = efi_alloc(ipv4dp_len + sizeof(END));
ipv4dp is a small structure and is freed below. Allocating it on the stack would reduce the code size.
if (!ipv4dp) {
log_err("Out of memory\n");
Writing messages inside a protocol implementation should be avoided.
return NULL;
}
ipv4dp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
ipv4dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_IPV4;
ipv4dp->dp.length = ipv4dp_len;
ipv4dp->protocol = 6;
if (ip)
memcpy(&ipv4dp->local_ip_address, ip, sizeof(*ip));
if (mask)
memcpy(&ipv4dp->subnet_mask, mask, sizeof(*mask));
if (srv)
memcpy(&ipv4dp->remote_ip_address, srv, sizeof(*srv));
pos = (void *)(uintptr_t)ipv4dp + ipv4dp_len;
Here you
1 - convert ipv4dp to uintptr_t 2 - convert the uintptr_t value to void * 3 - add an offset to the void * value
In the C standard adding an offset to a void * value results in undefined behavior. Unfortunately U-Boot code uses it a lot.
It's a known support gcc extension that treats void* similar to char*. Do you think it's worth changing all of the call sites we have?
No, I don't request this. But as pos is already char*, we should not use void* here.
Best regards
Heinrich
pos = (char *)ipv4dp + ipv4dp_len;
is all it takes to have a defined behavior here.
Best regards
Heinrich
memcpy(pos, &END, sizeof(END));
dp1 = efi_dp_from_eth();
dp2 = efi_dp_concat(dp1, (const struct efi_device_path *)ipv4dp, 0);
efi_free_pool(ipv4dp);
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,

El mar, 19 nov 2024 a las 6:50, Heinrich Schuchardt (< heinrich.schuchardt@canonical.com>) escribió:
On 18.11.24 22:08, Adriano Cordova 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 v4:
- Fixed memcpy mistake
Changes in v3:
Removed some unnecessary void* casts.
Changed sizeof(struct efi_device_path_ipv4) to sizeof(*ipv4dp) in efi_dp_from_ipv4.
lib/efi_loader/efi_device_path.c | 38 ++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+)
diff --git a/lib/efi_loader/efi_device_path.c
b/lib/efi_loader/efi_device_path.c
index ee387e1dfd..cdeea4791f 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -974,6 +974,44 @@ struct efi_device_path __maybe_unused
*efi_dp_from_eth(void)
return start;
}
A function description is missing.
+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;
efi_uintn_t ipv4dp_len;
struct efi_device_path_ipv4 *ipv4dp;
char *pos;
ipv4dp_len = sizeof(*ipv4dp);
ipv4dp = efi_alloc(ipv4dp_len + sizeof(END));
ipv4dp is a small structure and is freed below. Allocating it on the stack would reduce the code size.
But as it is a device path it has to be allocated contiguously to the END node, see the next lines. It is then passed to efi_dp_concat where this is important.
if (!ipv4dp) {
log_err("Out of memory\n");
Writing messages inside a protocol implementation should be avoided.
return NULL;
}
ipv4dp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
ipv4dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_IPV4;
ipv4dp->dp.length = ipv4dp_len;
ipv4dp->protocol = 6;
if (ip)
memcpy(&ipv4dp->local_ip_address, ip, sizeof(*ip));
if (mask)
memcpy(&ipv4dp->subnet_mask, mask, sizeof(*mask));
if (srv)
memcpy(&ipv4dp->remote_ip_address, srv, sizeof(*srv));
pos = (void *)(uintptr_t)ipv4dp + ipv4dp_len;
Here you
1 - convert ipv4dp to uintptr_t 2 - convert the uintptr_t value to void * 3 - add an offset to the void * value
In the C standard adding an offset to a void * value results in undefined behavior. Unfortunately U-Boot code uses it a lot.
pos = (char *)ipv4dp + ipv4dp_len;
is all it takes to have a defined behavior here.
Best regards
Heinrich
It was originally a char *, I will change it back
memcpy(pos, &END, sizeof(END));
dp1 = efi_dp_from_eth();
dp2 = efi_dp_concat(dp1, (const struct efi_device_path *)ipv4dp,
0);
efi_free_pool(ipv4dp);
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,
Best, Adriano

On 20.11.24 15:55, Adriano Córdova wrote:
El mar, 19 nov 2024 a las 6:50, Heinrich Schuchardt (<heinrich.schuchardt@canonical.com mailto:heinrich.schuchardt@canonical.com>) escribió:
On 18.11.24 22:08, Adriano Cordova wrote: > Add efi_dp_from_ipv4 to form a device path from an ipv4 address. > > Signed-off-by: Adriano Cordova <adrianox@gmail.com <mailto:adrianox@gmail.com>> > --- > > Changes in v4: > - Fixed memcpy mistake > > Changes in v3: > - Removed some unnecessary void* casts. > - Changed sizeof(struct efi_device_path_ipv4) to sizeof(*ipv4dp) > in efi_dp_from_ipv4. > > lib/efi_loader/efi_device_path.c | 38 +++++++++++++++++++++++++ +++++++ > 1 file changed, 38 insertions(+) > > diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/ efi_device_path.c > index ee387e1dfd..cdeea4791f 100644 > --- a/lib/efi_loader/efi_device_path.c > +++ b/lib/efi_loader/efi_device_path.c > @@ -974,6 +974,44 @@ struct efi_device_path __maybe_unused *efi_dp_from_eth(void) > return start; > } > A function description is missing. > +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; > + efi_uintn_t ipv4dp_len; > + struct efi_device_path_ipv4 *ipv4dp; > + char *pos; > + > + ipv4dp_len = sizeof(*ipv4dp); > + ipv4dp = efi_alloc(ipv4dp_len + sizeof(END)); ipv4dp is a small structure and is freed below. Allocating it on the stack would reduce the code size.
But as it is a device path it has to be allocated contiguously to the END node, see the next lines. It is then passed to efi_dp_concat where this is important.
struct { struct efi_device_path_ipv4 ipv4dp; struct efi_device_path end; } dp;
should give you a variable of adequate size.
Best regards
Heinrich
> + if (!ipv4dp) { > + log_err("Out of memory\n"); Writing messages inside a protocol implementation should be avoided. > + return NULL; > + } > + > + ipv4dp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; > + ipv4dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_IPV4; > + ipv4dp->dp.length = ipv4dp_len; > + ipv4dp->protocol = 6; > + if (ip) > + memcpy(&ipv4dp->local_ip_address, ip, sizeof(*ip)); > + if (mask) > + memcpy(&ipv4dp->subnet_mask, mask, sizeof(*mask)); > + if (srv) > + memcpy(&ipv4dp->remote_ip_address, srv, sizeof(*srv)); > + pos = (void *)(uintptr_t)ipv4dp + ipv4dp_len; Here you 1 - convert ipv4dp to uintptr_t 2 - convert the uintptr_t value to void * 3 - add an offset to the void * value In the C standard adding an offset to a void * value results in undefined behavior. Unfortunately U-Boot code uses it a lot. pos = (char *)ipv4dp + ipv4dp_len; is all it takes to have a defined behavior here. Best regards Heinrich
It was originally a char *, I will change it back
> + memcpy(pos, &END, sizeof(END)); > + > + dp1 = efi_dp_from_eth(); > + dp2 = efi_dp_concat(dp1, (const struct efi_device_path *)ipv4dp, 0); > + > + efi_free_pool(ipv4dp); > + 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,
Best, Adriano

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 ---
(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;

On Mon, 18 Nov 2024 at 23:09, Adriano Cordova adrianox@gmail.com wrote:
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
(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;
-- 2.43.0
Reviewed-by: Ilias Apalodimas ilias.apalodimas@linaro.org #sprintf might overflow but that's w ider problem of all our device path functions

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)

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 ---
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..3f0b571cc6 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, 4); + } + + env = env_get(maskstr); + if (env && mask) { + tmp = string_to_ip(env); + memcpy(mask, &tmp, 4); + } + env = env_get(gwstr); + if (env && gw) { + tmp = string_to_ip(env); + memcpy(gw, &tmp, 4); + } +#else + if (ip) + memcpy(ip, &net_ip, 4); + if (mask) + memcpy(mask, &net_netmask, 4); +#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, 4); + if (mask) + memcpy(&net_netmask, mask, 4); +#endif +}

On Mon, 18 Nov 2024 at 23:09, Adriano Cordova adrianox@gmail.com 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
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..3f0b571cc6 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, 4);
}
env = env_get(maskstr);
if (env && mask) {
tmp = string_to_ip(env);
memcpy(mask, &tmp, 4);
Using sizeof(*ip), sizeof(*mask) etc is better than just hardcoding 4 here
Cheers /Ilias
}
env = env_get(gwstr);
if (env && gw) {
tmp = string_to_ip(env);
memcpy(gw, &tmp, 4);
}
+#else
if (ip)
memcpy(ip, &net_ip, 4);
if (mask)
memcpy(mask, &net_netmask, 4);
+#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, 4);
if (mask)
memcpy(&net_netmask, mask, 4);
+#endif
+}
2.43.0

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 --- Changes in v4: - Reworked an if-else
Changes in v3: - Moved argument checks in efi_dp_from_http to the beginning of the function include/efi_loader.h | 1 + lib/efi_loader/efi_device_path.c | 52 ++++++++++++++++++++++++++++++++ 2 files changed, 53 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 cdeea4791f..9ee03062ac 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -1012,6 +1012,58 @@ struct efi_device_path *efi_dp_from_ipv4(struct efi_ipv4_address *ip, return dp2; }
+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); + + strcpy(tmp, "http://"); + + if (server) { + memcpy(tmp + strlen("http://"), server, strlen(server) + 1); + } +#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 18.11.24 22:09, Adriano Cordova 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
Changes in v4:
- Reworked an if-else
Changes in v3:
- Moved argument checks in efi_dp_from_http to the beginning of the function include/efi_loader.h | 1 + lib/efi_loader/efi_device_path.c | 52 ++++++++++++++++++++++++++++++++ 2 files changed, 53 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 cdeea4791f..9ee03062ac 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -1012,6 +1012,58 @@ struct efi_device_path *efi_dp_from_ipv4(struct efi_ipv4_address *ip, return dp2; }
A function description is missing. Please,
+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);
This seems to be the only usage of efi_dp_from_ipv4(). So we should make it static.
dp1 is expected to be NULL if we are out of memory. This error needs to be handled.
- strcpy(tmp, "http://");
@Ilias: Linaro sent a patch series for supporting https. Will that go in after this series?
- if (server) {
memcpy(tmp + strlen("http://"), server, strlen(server) + 1);
Please, use strcat() to simplify this line.
- }
+#if !IS_ENABLED(CONFIG_NET_LWIP)
Please use 'else if' instead of '#if'.
Best regards
Heinrich
- 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,

El mar, 19 nov 2024 a las 7:14, Heinrich Schuchardt (< heinrich.schuchardt@canonical.com>) escribió:
On 18.11.24 22:09, Adriano Cordova 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
Changes in v4:
- Reworked an if-else
Changes in v3:
- Moved argument checks in efi_dp_from_http to the beginning of the
function
include/efi_loader.h | 1 + lib/efi_loader/efi_device_path.c | 52 ++++++++++++++++++++++++++++++++ 2 files changed, 53 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 cdeea4791f..9ee03062ac 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -1012,6 +1012,58 @@ struct efi_device_path *efi_dp_from_ipv4(struct
efi_ipv4_address *ip,
return dp2;
}
A function description is missing. Please,
+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);
This seems to be the only usage of efi_dp_from_ipv4(). So we should make it static.
dp1 is expected to be NULL if we are out of memory. This error needs to be handled.
strcpy(tmp, "http://");
@Ilias: Linaro sent a patch series for supporting https. Will that go in after this series?
if (server) {
memcpy(tmp + strlen("http://"), server, strlen(server) +
1);
Please, use strcat() to simplify this line.
}
+#if !IS_ENABLED(CONFIG_NET_LWIP)
Please use 'else if' instead of '#if'.
Best regards
Heinrich
Hi Heinrich, I can't use 'else if' as the symbol net_server_ip is only defined in legacy net
Best, Adriano
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 20.11.24 15:52, Adriano Córdova wrote:
El mar, 19 nov 2024 a las 7:14, Heinrich Schuchardt (<heinrich.schuchardt@canonical.com mailto:heinrich.schuchardt@canonical.com>) escribió:
On 18.11.24 22:09, Adriano Cordova 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 <mailto:adrianox@gmail.com>> > --- > Changes in v4: > - Reworked an if-else > > Changes in v3: > - Moved argument checks in efi_dp_from_http to the beginning of the function > include/efi_loader.h | 1 + > lib/efi_loader/efi_device_path.c | 52 +++++++++++++++++++++++++ +++++++ > 2 files changed, 53 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 cdeea4791f..9ee03062ac 100644 > --- a/lib/efi_loader/efi_device_path.c > +++ b/lib/efi_loader/efi_device_path.c > @@ -1012,6 +1012,58 @@ struct efi_device_path *efi_dp_from_ipv4(struct efi_ipv4_address *ip, > return dp2; > } > A function description is missing. Please, > +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); This seems to be the only usage of efi_dp_from_ipv4(). So we should make it static. dp1 is expected to be NULL if we are out of memory. This error needs to be handled. > + > + strcpy(tmp, "http://"); @Ilias: Linaro sent a patch series for supporting https. Will that go in after this series? > + > + if (server) { > + memcpy(tmp + strlen("http://"), server, strlen(server) + 1); Please, use strcat() to simplify this line. > + } > +#if !IS_ENABLED(CONFIG_NET_LWIP) Please use 'else if' instead of '#if'. Best regards Heinrich
Hi Heinrich, I can't use 'else if' as the symbol net_server_ip is only defined in legacy net
Understood.
@Jerome Should the function ip_to_string be() moved to net/net-common.c in future to implement the missing commands in lwIP?
Best regards
Heinrich
Best, Adriano
> + 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 11/20/24 17:07, Heinrich Schuchardt wrote:
On 20.11.24 15:52, Adriano Córdova wrote:
El mar, 19 nov 2024 a las 7:14, Heinrich Schuchardt (<heinrich.schuchardt@canonical.com mailto:heinrich.schuchardt@canonical.com>) escribió:
On 18.11.24 22:09, Adriano Cordova 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 mailto:adrianox@gmail.com> > --- > Changes in v4: > - Reworked an if-else > > Changes in v3: > - Moved argument checks in efi_dp_from_http to the beginning of the function > include/efi_loader.h | 1 + > lib/efi_loader/efi_device_path.c | 52 +++++++++++++++++++++++++ +++++++ > 2 files changed, 53 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 cdeea4791f..9ee03062ac 100644 > --- a/lib/efi_loader/efi_device_path.c > +++ b/lib/efi_loader/efi_device_path.c > @@ -1012,6 +1012,58 @@ struct efi_device_path *efi_dp_from_ipv4(struct efi_ipv4_address *ip, > return dp2; > } >
A function description is missing. Please,
> +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);
This seems to be the only usage of efi_dp_from_ipv4(). So we should make it static.
dp1 is expected to be NULL if we are out of memory. This error needs to be handled.
> + > + strcpy(tmp, "http://");
@Ilias: Linaro sent a patch series for supporting https. Will that go in after this series?
> + > + if (server) { > + memcpy(tmp + strlen("http://"), server, strlen(server) + 1);
Please, use strcat() to simplify this line.
> + } > +#if !IS_ENABLED(CONFIG_NET_LWIP)
Please use 'else if' instead of '#if'.
Best regards
Heinrich
Hi Heinrich, I can't use 'else if' as the symbol net_server_ip is only defined in legacy net
Understood.
@Jerome Should the function ip_to_string be() moved to net/net-common.c in future to implement the missing commands in lwIP?
That would certainly be a good place for it, yes.
Thanks,

Hi Heinrich
On Tue, 19 Nov 2024 at 12:14, Heinrich Schuchardt heinrich.schuchardt@canonical.com wrote:
On 18.11.24 22:09, Adriano Cordova 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
Changes in v4:
- Reworked an if-else
Changes in v3:
- Moved argument checks in efi_dp_from_http to the beginning of the function include/efi_loader.h | 1 + lib/efi_loader/efi_device_path.c | 52 ++++++++++++++++++++++++++++++++ 2 files changed, 53 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 cdeea4791f..9ee03062ac 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -1012,6 +1012,58 @@ struct efi_device_path *efi_dp_from_ipv4(struct efi_ipv4_address *ip, return dp2; }
A function description is missing. Please,
+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);
This seems to be the only usage of efi_dp_from_ipv4(). So we should make it static.
dp1 is expected to be NULL if we are out of memory. This error needs to be handled.
strcpy(tmp, "http://");
@Ilias: Linaro sent a patch series for supporting https. Will that go in after this series?
The https:// support in already in -master, but I can live without it for now on this series.
Cheers /Ilias
if (server) {
memcpy(tmp + strlen("http://"), server, strlen(server) + 1);
Please, use strcat() to simplify this line.
}
+#if !IS_ENABLED(CONFIG_NET_LWIP)
Please use 'else if' instead of '#if'.
Best regards
Heinrich
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,

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 ---
(no changes since v2)
include/efi_loader.h | 6 +++++ lib/efi_loader/efi_device_path.c | 2 +- lib/efi_loader/efi_net.c | 38 +++++++++++++++++++++++++++++++- net/lwip/wget.c | 1 + net/wget.c | 1 + 5 files changed, 46 insertions(+), 2 deletions(-)
diff --git a/include/efi_loader.h b/include/efi_loader.h index 96b204dfc3..f49f8e6be0 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 */ +void 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(void **dp); void efi_net_get_addr(struct efi_ipv4_address *ip, struct efi_ipv4_address *mask, struct efi_ipv4_address *gw); @@ -133,6 +137,8 @@ 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_set_dp(const char *dev, const char *server) { } +static inline void efi_net_get_dp(void **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_device_path.c b/lib/efi_loader/efi_device_path.c index 9ee03062ac..5fae3fbb5a 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -1165,7 +1165,7 @@ 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(); + efi_net_get_dp((void **)&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 3f0b571cc6..2207caf6fe 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> @@ -31,6 +32,7 @@ static size_t *receive_lengths; static int rx_packet_idx; static int rx_packet_num; static struct efi_net_obj *netobj; +static struct efi_device_path *net_dp;
/* * The notification function of this event is called in every timer cycle @@ -902,8 +904,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 +1003,38 @@ 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 dowloaded + * + * @dev: dev to set the device path from + * @server: remote server address + */ +void efi_net_set_dp(const char *dev, const char *server) +{ + if (!strcmp(dev, "Net")) + net_dp = efi_dp_from_eth(); + else if (!strcmp(dev, "Http")) + net_dp = efi_dp_from_http(server); +} + +/** + * 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 + */ +void efi_net_get_dp(void **dp) +{ + if (!net_dp) + efi_net_set_dp("Net", NULL); + if (dp && 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 53c3b169e0..5c44e324be 100644 --- a/net/lwip/wget.c +++ b/net/lwip/wget.c @@ -217,6 +217,7 @@ static void httpc_result_cb(void *arg, httpc_result_t httpc_result, 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_net_set_dp("Http", ctx->server_name); efi_set_bootdev("Net", "", ctx->path, map_sysmem(ctx->saved_daddr, 0), rx_content_len); } diff --git a/net/wget.c b/net/wget.c index 1166aa5aa6..a1328990d2 100644 --- a/net/wget.c +++ b/net/wget.c @@ -442,6 +442,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_net_set_dp("Http", NULL); efi_set_bootdev("Net", "", image_url, map_sysmem(image_load_addr, 0), net_boot_file_size);

On 18.11.24 22:09, 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
(no changes since v2)
include/efi_loader.h | 6 +++++ lib/efi_loader/efi_device_path.c | 2 +- lib/efi_loader/efi_net.c | 38 +++++++++++++++++++++++++++++++- net/lwip/wget.c | 1 + net/wget.c | 1 + 5 files changed, 46 insertions(+), 2 deletions(-)
diff --git a/include/efi_loader.h b/include/efi_loader.h index 96b204dfc3..f49f8e6be0 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 */ +void 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(void **dp); void efi_net_get_addr(struct efi_ipv4_address *ip, struct efi_ipv4_address *mask, struct efi_ipv4_address *gw); @@ -133,6 +137,8 @@ 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_set_dp(const char *dev, const char *server) { } +static inline void efi_net_get_dp(void **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_device_path.c b/lib/efi_loader/efi_device_path.c index 9ee03062ac..5fae3fbb5a 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -1165,7 +1165,7 @@ 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 (!strcmp(dev, "Uart")) { dp = efi_dp_from_uart(); } else {efi_net_get_dp((void **)&dp);
diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c index 3f0b571cc6..2207caf6fe 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> @@ -31,6 +32,7 @@ static size_t *receive_lengths; static int rx_packet_idx; static int rx_packet_num; static struct efi_net_obj *netobj;
The role of net_dp deserves a description.
+static struct efi_device_path *net_dp;
/*
- The notification function of this event is called in every timer cycle
@@ -902,8 +904,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 +1003,38 @@ 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 dowloaded
- @dev: dev to set the device path from
- @server: remote server address
- */
+void efi_net_set_dp(const char *dev, const char *server) +{
- if (!strcmp(dev, "Net"))
net_dp = efi_dp_from_eth();
- else if (!strcmp(dev, "Http"))
net_dp = efi_dp_from_http(server);
+}
+/**
- 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
- */
+void efi_net_get_dp(void **dp)
We know that dp must point to a device-path. Please, do not use void**.
efi_device_path** should be fine.
Best regards
Heinrich
+{
- if (!net_dp)
efi_net_set_dp("Net", NULL);
- if (dp && 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 53c3b169e0..5c44e324be 100644 --- a/net/lwip/wget.c +++ b/net/lwip/wget.c @@ -217,6 +217,7 @@ static void httpc_result_cb(void *arg, httpc_result_t httpc_result, 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), rx_content_len); }efi_net_set_dp("Http", ctx->server_name);
diff --git a/net/wget.c b/net/wget.c index 1166aa5aa6..a1328990d2 100644 --- a/net/wget.c +++ b/net/wget.c @@ -442,6 +442,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_net_set_dp("Http", NULL); efi_set_bootdev("Net", "", image_url, map_sysmem(image_load_addr, 0), 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 ---
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 f49f8e6be0..d96d4e08bd 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_set_dp(const char *dev, const char *server) { } static inline void efi_net_get_dp(void **dp) { } diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c index 2207caf6fe..d81277eb2e 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> @@ -34,6 +35,12 @@ static int rx_packet_num; 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. @@ -1158,3 +1165,151 @@ void efi_net_set_addr(struct efi_ipv4_address *ip, memcpy(&net_netmask, mask, 4); #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 --- 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 d96d4e08bd..0cd6023bb2 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -627,6 +627,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 58d49789f1..b8c8d2ba4a 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 d81277eb2e..999369f16a 100644 --- a/lib/efi_loader/efi_net.c +++ b/lib/efi_loader/efi_net.c @@ -54,11 +54,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; @@ -66,6 +67,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 };
/* @@ -993,6 +997,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");

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 --- 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 0cd6023bb2..206796641e 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -630,6 +630,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 b8c8d2ba4a..2084cb5773 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 999369f16a..86a6a9214c 100644 --- a/lib/efi_loader/efi_net.c +++ b/lib/efi_loader/efi_net.c @@ -60,6 +60,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; @@ -70,6 +71,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 };
/* @@ -1003,6 +1007,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"); @@ -1190,8 +1207,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; @@ -1288,6 +1303,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 c6a27b7d04..3b2cf60fe1 100644 --- a/lib/uuid.c +++ b/lib/uuid.c @@ -174,6 +174,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 +};

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 ---
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 +};
participants (5)
-
Adriano Cordova
-
Adriano Córdova
-
Heinrich Schuchardt
-
Ilias Apalodimas
-
Jerome Forissier