]> git.ipfire.org Git - people/ms/u-boot.git/blobdiff - lib/efi_loader/efi_device_path.c
efi_loader: support device path for IDE and SCSI disks
[people/ms/u-boot.git] / lib / efi_loader / efi_device_path.c
index b4e2f933cb602113af249c3e9d7aa92b5981440b..2a8efea6e7cf8c5755d12c7b188a85498eb63d0c 100644 (file)
@@ -36,6 +36,24 @@ static const struct efi_device_path_vendor ROOT = {
        .guid = U_BOOT_GUID,
 };
 
+#if defined(CONFIG_DM_MMC) && defined(CONFIG_MMC)
+/*
+ * Determine if an MMC device is an SD card.
+ *
+ * @desc       block device descriptor
+ * @return     true if the device is an SD card
+ */
+static bool is_sd(struct blk_desc *desc)
+{
+       struct mmc *mmc = find_mmc_device(desc->devnum);
+
+       if (!mmc)
+               return false;
+
+       return IS_SD(mmc) != 0U;
+}
+#endif
+
 static void *dp_alloc(size_t sz)
 {
        void *buf;
@@ -126,6 +144,7 @@ static struct efi_object *find_obj(struct efi_device_path *dp, bool short_path,
                                   struct efi_device_path **rem)
 {
        struct efi_object *efiobj;
+       unsigned int dp_size = efi_dp_size(dp);
 
        list_for_each_entry(efiobj, &efi_obj_list, link) {
                struct efi_handler *handler;
@@ -141,10 +160,18 @@ static struct efi_object *find_obj(struct efi_device_path *dp, bool short_path,
                do {
                        if (efi_dp_match(dp, obj_dp) == 0) {
                                if (rem) {
+                                       /*
+                                        * Allow partial matches, but inform
+                                        * the caller.
+                                        */
                                        *rem = ((void *)dp) +
                                                efi_dp_size(obj_dp);
+                                       return efiobj;
+                               } else {
+                                       /* Only return on exact matches */
+                                       if (efi_dp_size(obj_dp) == dp_size)
+                                               return efiobj;
                                }
-                               return efiobj;
                        }
 
                        obj_dp = shorten_path(efi_dp_next(obj_dp));
@@ -164,8 +191,14 @@ struct efi_object *efi_dp_find_obj(struct efi_device_path *dp,
 {
        struct efi_object *efiobj;
 
-       efiobj = find_obj(dp, false, rem);
+       /* Search for an exact match first */
+       efiobj = find_obj(dp, false, NULL);
 
+       /* Then for a fuzzy match */
+       if (!efiobj)
+               efiobj = find_obj(dp, false, rem);
+
+       /* And now for a fuzzy short match */
        if (!efiobj)
                efiobj = find_obj(dp, true, rem);
 
@@ -264,6 +297,23 @@ static unsigned dp_size(struct udevice *dev)
        case UCLASS_SIMPLE_BUS:
                /* stop traversing parents at this point: */
                return sizeof(ROOT);
+#ifdef CONFIG_BLK
+       case UCLASS_BLK:
+               switch (dev->parent->uclass->uc_drv->id) {
+#ifdef CONFIG_IDE
+               case UCLASS_IDE:
+                       return dp_size(dev->parent) +
+                               sizeof(struct efi_device_path_atapi);
+#endif
+#if defined(CONFIG_SCSI) && defined(CONFIG_DM_SCSI)
+               case UCLASS_SCSI:
+                       return dp_size(dev->parent) +
+                               sizeof(struct efi_device_path_scsi);
+#endif
+               default:
+                       return dp_size(dev->parent);
+               }
+#endif
        case UCLASS_MMC:
                return dp_size(dev->parent) +
                        sizeof(struct efi_device_path_sd_mmc_path);
@@ -277,6 +327,13 @@ static unsigned dp_size(struct udevice *dev)
        }
 }
 
+/*
+ * Recursively build a device path.
+ *
+ * @buf                pointer to the end of the device path
+ * @dev                device
+ * @return     pointer to the end of the device path
+ */
 static void *dp_fill(void *buf, struct udevice *dev)
 {
        if (!dev || !dev->driver)
@@ -290,6 +347,46 @@ static void *dp_fill(void *buf, struct udevice *dev)
                *vdp = ROOT;
                return &vdp[1];
        }
+#ifdef CONFIG_BLK
+       case UCLASS_BLK:
+               switch (dev->parent->uclass->uc_drv->id) {
+#ifdef CONFIG_IDE
+               case UCLASS_IDE: {
+                       struct efi_device_path_atapi *dp =
+                       dp_fill(buf, dev->parent);
+                       struct blk_desc *desc = dev_get_uclass_platdata(dev);
+
+                       dp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
+                       dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_ATAPI;
+                       dp->dp.length = sizeof(*dp);
+                       dp->logical_unit_number = desc->devnum;
+                       dp->primary_secondary = IDE_BUS(desc->devnum);
+                       dp->slave_master = desc->devnum %
+                               (CONFIG_SYS_IDE_MAXDEVICE /
+                                CONFIG_SYS_IDE_MAXBUS);
+                       return &dp[1];
+                       }
+#endif
+#if defined(CONFIG_SCSI) && defined(CONFIG_DM_SCSI)
+               case UCLASS_SCSI: {
+                       struct efi_device_path_scsi *dp =
+                               dp_fill(buf, dev->parent);
+                       struct blk_desc *desc = dev_get_uclass_platdata(dev);
+
+                       dp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
+                       dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_SCSI;
+                       dp->dp.length = sizeof(*dp);
+                       dp->logical_unit_number = desc->lun;
+                       dp->target_id = desc->target;
+                       return &dp[1];
+                       }
+#endif
+               default:
+                       printf("unhandled parent class: %s (%u)\n",
+                              dev->name, dev->driver->id);
+                       return dp_fill(buf, dev->parent);
+               }
+#endif
 #if defined(CONFIG_DM_MMC) && defined(CONFIG_MMC)
        case UCLASS_MMC: {
                struct efi_device_path_sd_mmc_path *sddp =
@@ -298,9 +395,9 @@ static void *dp_fill(void *buf, struct udevice *dev)
                struct blk_desc *desc = mmc_get_blk_desc(mmc);
 
                sddp->dp.type     = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
-               sddp->dp.sub_type = (desc->if_type == IF_TYPE_MMC) ?
-                       DEVICE_PATH_SUB_TYPE_MSG_MMC :
-                       DEVICE_PATH_SUB_TYPE_MSG_SD;
+               sddp->dp.sub_type = is_sd(desc) ?
+                       DEVICE_PATH_SUB_TYPE_MSG_SD :
+                       DEVICE_PATH_SUB_TYPE_MSG_MMC;
                sddp->dp.length   = sizeof(*sddp);
                sddp->slot_number = dev->seq;
 
@@ -350,7 +447,14 @@ static unsigned dp_part_size(struct blk_desc *desc, int part)
        unsigned dpsize;
 
 #ifdef CONFIG_BLK
-       dpsize = dp_size(desc->bdev->parent);
+       {
+               struct udevice *dev;
+               int ret = blk_find_device(desc->if_type, desc->devnum, &dev);
+
+               if (ret)
+                       dev = desc->bdev->parent;
+               dpsize = dp_size(dev);
+       }
 #else
        dpsize = sizeof(ROOT) + sizeof(struct efi_device_path_usb);
 #endif
@@ -366,19 +470,33 @@ static unsigned dp_part_size(struct blk_desc *desc, int part)
        return dpsize;
 }
 
+/*
+ * Create a device path for a block device or one of its partitions.
+ *
+ * @buf                buffer to which the device path is wirtten
+ * @desc       block device descriptor
+ * @part       partition number, 0 identifies a block device
+ */
 static void *dp_part_fill(void *buf, struct blk_desc *desc, int part)
 {
        disk_partition_t info;
 
 #ifdef CONFIG_BLK
-       buf = dp_fill(buf, desc->bdev->parent);
+       {
+               struct udevice *dev;
+               int ret = blk_find_device(desc->if_type, desc->devnum, &dev);
+
+               if (ret)
+                       dev = desc->bdev->parent;
+               buf = dp_fill(buf, dev);
+       }
 #else
        /*
         * 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
         * is just to have a unique device-path for if_type+devnum.
-        * So map things to a fictional USB device:
+        * So map things to a fictitious USB device.
         */
        struct efi_device_path_usb *udp;
 
@@ -402,7 +520,7 @@ static void *dp_part_fill(void *buf, struct blk_desc *desc, int part)
        if (desc->part_type == PART_TYPE_ISO) {
                struct efi_device_path_cdrom_path *cddp = buf;
 
-               cddp->boot_entry = part - 1;
+               cddp->boot_entry = part;
                cddp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE;
                cddp->dp.sub_type = DEVICE_PATH_SUB_TYPE_CDROM_PATH;
                cddp->dp.length = sizeof(*cddp);
@@ -416,7 +534,7 @@ static void *dp_part_fill(void *buf, struct blk_desc *desc, int part)
                hddp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE;
                hddp->dp.sub_type = DEVICE_PATH_SUB_TYPE_HARD_DRIVE_PATH;
                hddp->dp.length = sizeof(*hddp);
-               hddp->partition_number = part - 1;
+               hddp->partition_number = part;
                hddp->partition_start = info.start;
                hddp->partition_end = info.size;
                if (desc->part_type == PART_TYPE_EFI)