* (C) Copyright 2017 Rob Clark
*/
-#define LOG_CATEGORY LOGL_ERR
-
#include <common.h>
#include <blk.h>
#include <dm.h>
#include <mmc.h>
#include <efi_loader.h>
#include <part.h>
+#include <asm-generic/unaligned.h>
/* template END node: */
static const struct efi_device_path END = {
/*
* Compare two device-paths, stopping when the shorter of the two hits
- * an End* node. This is useful to, for example, compare a device-path
+ * an End* node. This is useful to, for example, compare a device-path
* representing a device with one representing a file on the device, or
* a device with a parent device.
*/
}
/*
- * See UEFI spec (section 3.1.2, about short-form device-paths..
- * tl;dr: we can have a device-path that starts with a USB WWID
- * or USB Class node, and a few other cases which don't encode
- * the full device path with bus hierarchy:
+ * We can have device paths that start with a USB WWID or a USB Class node,
+ * and a few other cases which don't encode the full device path with bus
+ * hierarchy:
*
* - MESSAGING:USB_WWID
* - MESSAGING:USB_CLASS
* - MEDIA:FILE_PATH
* - MEDIA:HARD_DRIVE
* - MESSAGING:URI
+ *
+ * See UEFI spec (section 3.1.2, about short-form device-paths)
*/
static struct efi_device_path *shorten_path(struct efi_device_path *dp)
{
struct efi_device_path *obj_dp;
efi_status_t ret;
- ret = efi_search_protocol(efiobj->handle,
+ ret = efi_search_protocol(efiobj,
&efi_guid_device_path, &handler);
if (ret != EFI_SUCCESS)
continue;
{
struct efi_device_path *ret;
+ if (length < sizeof(struct efi_device_path))
+ return NULL;
+
ret = dp_alloc(length);
if (!ret)
return ret;
*size = 0;
if (!dp || !*dp)
return NULL;
- p = *dp;
sz = efi_dp_instance_size(*dp);
p = dp_alloc(sz + sizeof(END));
if (!p)
/*
* Create a device node for a block device partition.
*
- * @buf buffer to which the device path is wirtten
+ * @buf buffer to which the device path is written
* @desc block device descriptor
* @part partition number, 0 identifies a block device
*/
cddp->dp.sub_type = DEVICE_PATH_SUB_TYPE_CDROM_PATH;
cddp->dp.length = sizeof(*cddp);
cddp->partition_start = info.start;
- cddp->partition_end = info.size;
+ cddp->partition_size = info.size;
buf = &cddp[1];
} else {
/*
* Create a device path for a block device or one of its partitions.
*
- * @buf buffer to which the device path is wirtten
+ * @buf buffer to which the device path is written
* @desc block device descriptor
* @part partition number, 0 identifies a block device
*/
/*
* We *could* make a more accurate path, by looking at if_type
* and handling all the different cases like we do for non-
- * legacy (ie CONFIG_BLK=y) case. But most important thing
+ * legacy (i.e. CONFIG_BLK=y) case. But most important thing
* is just to have a unique device-path for if_type+devnum.
* So map things to a fictitious USB device.
*/
return dp_part_node(buf, desc, part);
}
-/* Construct a device-path from a partition on a blk device: */
+/* Construct a device-path from a partition on a block device: */
struct efi_device_path *efi_dp_from_part(struct blk_desc *desc, int part)
{
void *buf, *start;
/*
* Create a device node for a block device partition.
*
- * @buf buffer to which the device path is wirtten
+ * @buf buffer to which the device path is written
* @desc block device descriptor
* @part partition number, 0 identifies a block device
*/
return buf;
}
-/* convert path to an UEFI style path (ie. DOS style backslashes and utf16) */
-static void path_to_uefi(u16 *uefi, const char *path)
+/**
+ * path_to_uefi() - convert UTF-8 path to an UEFI style path
+ *
+ * Convert UTF-8 path to a UEFI style path (i.e. with backslashes as path
+ * separators and UTF-16).
+ *
+ * @src: source buffer
+ * @uefi: target buffer, possibly unaligned
+ */
+static void path_to_uefi(void *uefi, const char *src)
{
- while (*path) {
- char c = *(path++);
- if (c == '/')
- c = '\\';
- *(uefi++) = c;
+ u16 *pos = uefi;
+
+ /*
+ * efi_set_bootdev() calls this routine indirectly before the UEFI
+ * subsystem is initialized. So we cannot assume unaligned access to be
+ * enabled.
+ */
+ allow_unaligned();
+
+ while (*src) {
+ s32 code = utf8_get(&src);
+
+ if (code < 0)
+ code = '?';
+ else if (code == '/')
+ code = '\\';
+ utf16_put(code, &pos);
}
- *uefi = '\0';
+ *pos = 0;
}
/*
if (desc)
dpsize = dp_part_size(desc, part);
- fpsize = sizeof(struct efi_device_path) + 2 * (strlen(path) + 1);
+ fpsize = sizeof(struct efi_device_path) +
+ 2 * (utf8_utf16_strlen(path) + 1);
dpsize += fpsize;
start = buf = dp_alloc(dpsize + sizeof(END));
return start;
}
-/*
- * Helper to split a full device path (containing both device and file
- * parts) into it's constituent parts.
+/**
+ * efi_dp_split_file_path() - split of relative file path from device path
+ *
+ * Given a device path indicating a file on a device, separate the device
+ * path in two: the device path of the actual device and the file path
+ * relative to this device.
+ *
+ * @full_path: device path including device and file path
+ * @device_path: path of the device
+ * @file_path: relative path of the file or NULL if there is none
+ * Return: status code
*/
efi_status_t efi_dp_split_file_path(struct efi_device_path *full_path,
struct efi_device_path **device_path,
struct efi_device_path **file_path)
{
- struct efi_device_path *p, *dp, *fp;
+ struct efi_device_path *p, *dp, *fp = NULL;
*device_path = NULL;
*file_path = NULL;
while (!EFI_DP_TYPE(p, MEDIA_DEVICE, FILE_PATH)) {
p = efi_dp_next(p);
if (!p)
- return EFI_OUT_OF_RESOURCES;
+ goto out;
}
fp = efi_dp_dup(p);
if (!fp)
p->sub_type = DEVICE_PATH_SUB_TYPE_END;
p->length = sizeof(*p);
+out:
*device_path = dp;
*file_path = fp;
return EFI_SUCCESS;
}
+
+efi_status_t efi_dp_from_name(const char *dev, const char *devnr,
+ const char *path,
+ struct efi_device_path **device,
+ struct efi_device_path **file)
+{
+ int is_net;
+ struct blk_desc *desc = NULL;
+ disk_partition_t fs_partition;
+ int part = 0;
+ char filename[32] = { 0 }; /* dp->str is u16[32] long */
+ char *s;
+
+ if (path && !file)
+ return EFI_INVALID_PARAMETER;
+
+ is_net = !strcmp(dev, "Net");
+ if (!is_net) {
+ part = blk_get_device_part_str(dev, devnr, &desc, &fs_partition,
+ 1);
+ if (part < 0 || !desc)
+ return EFI_INVALID_PARAMETER;
+
+ if (device)
+ *device = efi_dp_from_part(desc, part);
+ } else {
+#ifdef CONFIG_NET
+ if (device)
+ *device = efi_dp_from_eth();
+#endif
+ }
+
+ if (!path)
+ return EFI_SUCCESS;
+
+ snprintf(filename, sizeof(filename), "%s", path);
+ /* DOS style file path: */
+ s = filename;
+ while ((s = strchr(s, '/')))
+ *s++ = '\\';
+ *file = efi_dp_from_file(((!is_net && device) ? desc : NULL),
+ part, filename);
+
+ return EFI_SUCCESS;
+}