part: Drop disk_partition_t typedef
[pandora-u-boot.git] / lib / efi_loader / efi_device_path.c
index d8c052d..76ab2d8 100644 (file)
@@ -8,10 +8,19 @@
 #include <common.h>
 #include <blk.h>
 #include <dm.h>
+#include <net.h>
 #include <usb.h>
 #include <mmc.h>
+#include <nvme.h>
 #include <efi_loader.h>
 #include <part.h>
+#include <sandboxblockdev.h>
+#include <asm-generic/unaligned.h>
+#include <linux/compat.h> /* U16_MAX */
+
+#ifdef CONFIG_SANDBOX
+const efi_guid_t efi_guid_host_dev = U_BOOT_HOST_DEV_GUID;
+#endif
 
 /* template END node: */
 static const struct efi_device_path END = {
@@ -335,6 +344,9 @@ struct efi_device_path *efi_dp_create_device_node(const u8 type,
 {
        struct efi_device_path *ret;
 
+       if (length < sizeof(struct efi_device_path))
+               return NULL;
+
        ret = dp_alloc(length);
        if (!ret)
                return ret;
@@ -411,7 +423,7 @@ bool efi_dp_is_multi_instance(const struct efi_device_path *dp)
 /* size of device-path not including END node for device and all parents
  * up to the root device.
  */
-static unsigned dp_size(struct udevice *dev)
+__maybe_unused static unsigned int dp_size(struct udevice *dev)
 {
        if (!dev || !dev->driver)
                return sizeof(ROOT);
@@ -441,6 +453,21 @@ static unsigned dp_size(struct udevice *dev)
                case UCLASS_MMC:
                        return dp_size(dev->parent) +
                                sizeof(struct efi_device_path_sd_mmc_path);
+#endif
+#if defined(CONFIG_NVME)
+               case UCLASS_NVME:
+                       return dp_size(dev->parent) +
+                               sizeof(struct efi_device_path_nvme);
+#endif
+#ifdef CONFIG_SANDBOX
+               case UCLASS_ROOT:
+                        /*
+                         * Sandbox's host device will be represented
+                         * as vendor device with extra one byte for
+                         * device number
+                         */
+                       return dp_size(dev->parent)
+                               + sizeof(struct efi_device_path_vendor) + 1;
 #endif
                default:
                        return dp_size(dev->parent);
@@ -468,7 +495,7 @@ static unsigned dp_size(struct udevice *dev)
  * @dev                device
  * @return     pointer to the end of the device path
  */
-static void *dp_fill(void *buf, struct udevice *dev)
+__maybe_unused static void *dp_fill(void *buf, struct udevice *dev)
 {
        if (!dev || !dev->driver)
                return buf;
@@ -501,6 +528,24 @@ static void *dp_fill(void *buf, struct udevice *dev)
 #ifdef CONFIG_BLK
        case UCLASS_BLK:
                switch (dev->parent->uclass->uc_drv->id) {
+#ifdef CONFIG_SANDBOX
+               case UCLASS_ROOT: {
+                       /* stop traversing parents at this point: */
+                       struct efi_device_path_vendor *dp;
+                       struct blk_desc *desc = dev_get_uclass_platdata(dev);
+
+                       dp_fill(buf, dev->parent);
+                       dp = buf;
+                       ++dp;
+                       dp->dp.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE;
+                       dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_VENDOR;
+                       dp->dp.length = sizeof(*dp) + 1;
+                       memcpy(&dp->guid, &efi_guid_host_dev,
+                              sizeof(efi_guid_t));
+                       dp->vendor_data[0] = desc->devnum;
+                       return &dp->vendor_data[1];
+                       }
+#endif
 #ifdef CONFIG_IDE
                case UCLASS_IDE: {
                        struct efi_device_path_atapi *dp =
@@ -546,6 +591,20 @@ static void *dp_fill(void *buf, struct udevice *dev)
                        sddp->slot_number = dev->seq;
                        return &sddp[1];
                        }
+#endif
+#if defined(CONFIG_NVME)
+               case UCLASS_NVME: {
+                       struct efi_device_path_nvme *dp =
+                               dp_fill(buf, dev->parent);
+                       u32 ns_id;
+
+                       dp->dp.type     = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
+                       dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_NVME;
+                       dp->dp.length   = sizeof(*dp);
+                       nvme_get_namespace_id(dev, &ns_id, dp->eui64);
+                       memcpy(&dp->ns_id, &ns_id, sizeof(ns_id));
+                       return &dp[1];
+                       }
 #endif
                default:
                        debug("%s(%u) %s: unhandled parent class: %s (%u)\n",
@@ -596,20 +655,6 @@ static void *dp_fill(void *buf, struct udevice *dev)
                return dp_fill(buf, dev->parent);
        }
 }
-
-/* Construct a device-path from a device: */
-struct efi_device_path *efi_dp_from_dev(struct udevice *dev)
-{
-       void *buf, *start;
-
-       start = buf = dp_alloc(dp_size(dev) + sizeof(END));
-       if (!buf)
-               return NULL;
-       buf = dp_fill(buf, dev);
-       *((struct efi_device_path *)buf) = END;
-
-       return start;
-}
 #endif
 
 static unsigned dp_part_size(struct blk_desc *desc, int part)
@@ -649,7 +694,7 @@ static unsigned dp_part_size(struct blk_desc *desc, int part)
  */
 static void *dp_part_node(void *buf, struct blk_desc *desc, int part)
 {
-       disk_partition_t info;
+       struct disk_partition info;
 
        part_get_info(desc, part, &info);
 
@@ -661,7 +706,7 @@ static void *dp_part_node(void *buf, struct blk_desc *desc, int part)
                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 {
@@ -790,16 +835,36 @@ struct efi_device_path *efi_dp_part_node(struct blk_desc *desc, int part)
        return buf;
 }
 
-/* convert path to an UEFI style path (i.e. DOS style backslashes and UTF-16) */
-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;
 }
 
 /*
@@ -811,12 +876,16 @@ struct efi_device_path *efi_dp_from_file(struct blk_desc *desc, int part,
 {
        struct efi_device_path_file_path *fp;
        void *buf, *start;
-       unsigned dpsize = 0, fpsize;
+       size_t dpsize = 0, fpsize;
 
        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);
+       if (fpsize > U16_MAX)
+               return NULL;
+
        dpsize += fpsize;
 
        start = buf = dp_alloc(dpsize + sizeof(END));
@@ -830,7 +899,7 @@ struct efi_device_path *efi_dp_from_file(struct blk_desc *desc, int part,
        fp = buf;
        fp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE;
        fp->dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH;
-       fp->dp.length = fpsize;
+       fp->dp.length = (u16)fpsize;
        path_to_uefi(fp->str, path);
        buf += fpsize;
 
@@ -917,14 +986,14 @@ struct efi_device_path *efi_dp_from_mem(uint32_t memory_type,
  *
  * @full_path:         device path including device and file path
  * @device_path:       path of the device
- * @file_path:         relative path of the file
+ * @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;
@@ -935,7 +1004,7 @@ efi_status_t efi_dp_split_file_path(struct efi_device_path *full_path,
        while (!EFI_DP_TYPE(p, MEDIA_DEVICE, FILE_PATH)) {
                p = efi_dp_next(p);
                if (!p)
-                       return EFI_INVALID_PARAMETER;
+                       goto out;
        }
        fp = efi_dp_dup(p);
        if (!fp)
@@ -944,11 +1013,22 @@ efi_status_t efi_dp_split_file_path(struct efi_device_path *full_path,
        p->sub_type = DEVICE_PATH_SUB_TYPE_END;
        p->length = sizeof(*p);
 
+out:
        *device_path = dp;
        *file_path = fp;
        return EFI_SUCCESS;
 }
 
+/**
+ * efi_dp_from_name() - convert U-Boot device and file path to device path
+ *
+ * @dev:       U-Boot device, e.g. 'mmc'
+ * @devnr:     U-Boot device number, e.g. 1 for 'mmc:1'
+ * @path:      file path relative to U-Boot device, may be NULL
+ * @device:    pointer to receive device path of the device
+ * @file:      pointer to receive device path for the file
+ * Return:     status code
+ */
 efi_status_t efi_dp_from_name(const char *dev, const char *devnr,
                              const char *path,
                              struct efi_device_path **device,
@@ -956,7 +1036,7 @@ efi_status_t efi_dp_from_name(const char *dev, const char *devnr,
 {
        int is_net;
        struct blk_desc *desc = NULL;
-       disk_partition_t fs_partition;
+       struct disk_partition fs_partition;
        int part = 0;
        char filename[32] = { 0 }; /* dp->str is u16[32] long */
        char *s;
@@ -988,8 +1068,10 @@ efi_status_t efi_dp_from_name(const char *dev, const char *devnr,
        s = filename;
        while ((s = strchr(s, '/')))
                *s++ = '\\';
-       *file = efi_dp_from_file(((!is_net && device) ? desc : NULL),
-                                part, filename);
+       *file = efi_dp_from_file(is_net ? NULL : desc, part, filename);
+
+       if (!*file)
+               return EFI_INVALID_PARAMETER;
 
        return EFI_SUCCESS;
 }