diff options
Diffstat (limited to 'lib/efi_loader/efi_device_path.c')
-rw-r--r-- | lib/efi_loader/efi_device_path.c | 175 |
1 files changed, 150 insertions, 25 deletions
diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index 46aa59b9e40..c9bf2726fe2 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -17,7 +17,7 @@ #include <nvme.h> #include <efi_loader.h> #include <part.h> -#include <uuid.h> +#include <u-boot/uuid.h> #include <asm-generic/unaligned.h> #include <linux/compat.h> /* U16_MAX */ @@ -102,7 +102,7 @@ int efi_dp_match(const struct efi_device_path *a, * See UEFI spec, section 3.1.2 for "short-form device path". * * @dp: original device-path - * @Return: shortened device-path or NULL + * Return: shortened device-path or NULL */ struct efi_device_path *efi_dp_shorten(struct efi_device_path *dp) { @@ -276,10 +276,11 @@ struct efi_device_path *efi_dp_dup(const struct efi_device_path *dp) * * @dp1: First device path * @dp2: Second device path - * @split_end_node: If true the two device paths will be concatenated and - * separated by an end node (DEVICE_PATH_SUB_TYPE_END). - * If false the second device path will be concatenated to the - * first one as-is. + * @split_end_node: + * * 0 to concatenate + * * 1 to concatenate with end node added as separator + * * size of dp1 excluding last end node to concatenate with end node as + * separator in case dp1 contains an end node * * Return: * concatenated device path or NULL. Caller must free the returned value @@ -287,7 +288,7 @@ struct efi_device_path *efi_dp_dup(const struct efi_device_path *dp) struct efi_device_path *efi_dp_concat(const struct efi_device_path *dp1, const struct efi_device_path *dp2, - bool split_end_node) + size_t split_end_node) { struct efi_device_path *ret; size_t end_size; @@ -301,10 +302,15 @@ efi_device_path *efi_dp_concat(const struct efi_device_path *dp1, ret = efi_dp_dup(dp1); } else { /* both dp1 and dp2 are non-null */ - unsigned sz1 = efi_dp_size(dp1); - unsigned sz2 = efi_dp_size(dp2); + size_t sz1; + size_t sz2 = efi_dp_size(dp2); void *p; + if (split_end_node < sizeof(struct efi_device_path)) + sz1 = efi_dp_size(dp1); + else + sz1 = split_end_node; + if (split_end_node) end_size = 2 * sizeof(END); else @@ -948,30 +954,144 @@ struct efi_device_path *efi_dp_from_uart(void) return buf; } -struct efi_device_path __maybe_unused *efi_dp_from_eth(void) +struct efi_device_path __maybe_unused *efi_dp_from_eth(struct udevice *dev) { void *buf, *start; unsigned dpsize = 0; - assert(eth_get_dev()); + assert(dev); - dpsize += dp_size(eth_get_dev()); + dpsize += dp_size(dev); start = buf = efi_alloc(dpsize + sizeof(END)); if (!buf) return NULL; - buf = dp_fill(buf, eth_get_dev()); + buf = dp_fill(buf, dev); *((struct efi_device_path *)buf) = END; return start; } +/** + * efi_dp_from_ipv4() - set device path from IPv4 address + * + * Set the device path to an ethernet device path as provided by + * efi_dp_from_eth() concatenated with a device path of subtype + * DEVICE_PATH_SUB_TYPE_MSG_IPV4, and an END node. + * + * @ip: IPv4 local address + * @mask: network mask + * @srv: IPv4 remote/server address + * @dev: net udevice + * Return: pointer to device path, NULL on error + */ +static struct efi_device_path *efi_dp_from_ipv4(struct efi_ipv4_address *ip, + struct efi_ipv4_address *mask, + struct efi_ipv4_address *srv, + struct udevice *dev) +{ + struct efi_device_path *dp1, *dp2, *pos; + struct { + struct efi_device_path_ipv4 ipv4dp; + struct efi_device_path end; + } dp; + + memset(&dp.ipv4dp, 0, sizeof(dp.ipv4dp)); + dp.ipv4dp.dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; + dp.ipv4dp.dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_IPV4; + dp.ipv4dp.dp.length = sizeof(dp.ipv4dp); + dp.ipv4dp.protocol = 6; + if (ip) + memcpy(&dp.ipv4dp.local_ip_address, ip, sizeof(*ip)); + if (mask) + memcpy(&dp.ipv4dp.subnet_mask, mask, sizeof(*mask)); + if (srv) + memcpy(&dp.ipv4dp.remote_ip_address, srv, sizeof(*srv)); + pos = &dp.end; + memcpy(pos, &END, sizeof(END)); + + dp1 = efi_dp_from_eth(dev); + if (!dp1) + return NULL; + + dp2 = efi_dp_concat(dp1, (const struct efi_device_path *)&dp, 0); + + efi_free_pool(dp1); + + return dp2; +} + +/** + * efi_dp_from_http() - set device path from http + * + * Set the device path to an IPv4 path as provided by efi_dp_from_ipv4 + * concatenated with a device path of subtype DEVICE_PATH_SUB_TYPE_MSG_URI, + * and an END node. + * + * @server: URI of remote server + * @dev: net udevice + * Return: pointer to HTTP device path, NULL on error + */ +struct efi_device_path *efi_dp_from_http(const char *server, struct udevice *dev) +{ + struct efi_device_path *dp1, *dp2; + struct efi_device_path_uri *uridp; + 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, dev); + + dp1 = efi_dp_from_ipv4(&ip, &mask, NULL, dev); + if (!dp1) + return NULL; + + + strcpy(tmp, "http://"); + + if (server) { + strlcat(tmp, server, sizeof(tmp)); +#if !IS_ENABLED(CONFIG_NET_LWIP) + } else { + ip_to_string(net_server_ip, tmp + strlen("http://")); +#endif + } + + uridp_len = sizeof(struct efi_device_path) + strlen(tmp) + 1; + uridp = efi_alloc(uridp_len + sizeof(END)); + if (!uridp) { + log_err("Out of memory\n"); + return NULL; + } + uridp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; + uridp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_URI; + uridp->dp.length = uridp_len; + debug("device path: setting uri device path to %s\n", tmp); + memcpy(uridp->uri, tmp, strlen(tmp) + 1); + + pos = (char *)uridp + uridp_len; + memcpy(pos, &END, sizeof(END)); + + dp2 = efi_dp_concat(dp1, (const struct efi_device_path *)uridp, 0); + + efi_free_pool(uridp); + efi_free_pool(dp1); + + return dp2; +} + /* Construct a device-path for memory-mapped image */ struct efi_device_path *efi_dp_from_mem(uint32_t memory_type, uint64_t start_address, - uint64_t end_address) + size_t size) { struct efi_device_path_memory *mdp; void *buf, *start; @@ -986,7 +1106,7 @@ struct efi_device_path *efi_dp_from_mem(uint32_t memory_type, mdp->dp.length = sizeof(*mdp); mdp->memory_type = memory_type; mdp->start_address = start_address; - mdp->end_address = end_address; + mdp->end_address = start_address + size; buf = &mdp[1]; *((struct efi_device_path *)buf) = END; @@ -1068,8 +1188,9 @@ efi_status_t efi_dp_from_name(const char *dev, const char *devnr, dp = efi_dp_from_mem(EFI_RESERVED_MEMORY_TYPE, (uintptr_t)image_addr, image_size); - } else if (IS_ENABLED(CONFIG_NETDEVICES) && !strcmp(dev, "Net")) { - dp = efi_dp_from_eth(); + } else if (IS_ENABLED(CONFIG_NETDEVICES) && + (!strcmp(dev, "Net") || !strcmp(dev, "Http"))) { + efi_net_dp_from_dev(&dp, eth_get_dev(), false); } else if (!strcmp(dev, "Uart")) { dp = efi_dp_from_uart(); } else { @@ -1127,17 +1248,18 @@ ssize_t efi_dp_check_length(const struct efi_device_path *dp, } /** - * efi_dp_from_lo() - Get the instance of a VenMedia node in a - * multi-instance device path that matches - * a specific GUID. This kind of device paths - * is found in Boot#### options describing an - * initrd location + * efi_dp_from_lo() - get device-path from load option * - * @lo: EFI_LOAD_OPTION containing a valid device path - * @guid: guid to search for + * The load options in U-Boot may contain multiple concatenated device-paths. + * The first device-path indicates the EFI binary to execute. Subsequent + * device-paths start with a VenMedia node where the GUID identifies the + * function (initrd or fdt). + * + * @lo: EFI load option containing a valid device path + * @guid: GUID identifying device-path or NULL for the EFI binary * * Return: - * device path including the VenMedia node or NULL. + * device path excluding the matched VenMedia node or NULL. * Caller must free the returned value. */ struct @@ -1148,6 +1270,9 @@ efi_device_path *efi_dp_from_lo(struct efi_load_option *lo, struct efi_device_path_vendor *vendor; int lo_len = lo->file_path_length; + if (!guid) + return efi_dp_dup(fp); + for (; lo_len >= sizeof(struct efi_device_path); lo_len -= fp->length, fp = (void *)fp + fp->length) { if (lo_len < 0 || efi_dp_check_length(fp, lo_len) < 0) |