Skip to content

Commit

Permalink
efi_loader: fix device-path for USB devices
Browse files Browse the repository at this point in the history
EFI device paths for block devices must be unique. If a non-unique device
path is discovered, probing of the block device fails.

Currently we use UsbClass() device path nodes. As multiple devices may
have the same vendor and product id these are non-unique. Instead we
should use Usb() device path nodes. They include the USB port on the
parent hub. Hence they are unique.

A USB storage device may contain multiple logical units. These can be
modeled as Ctrl() nodes.

Reported-by: Patrick Delaunay <patrick.delaunay@foss.st.com>
Signed-off-by: Heinrich Schuchardt <heinrich.schuchardt@canonical.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
  • Loading branch information
xypron committed Mar 25, 2023
1 parent 3f26bca commit 180b711
Showing 1 changed file with 33 additions and 12 deletions.
45 changes: 33 additions & 12 deletions lib/efi_loader/efi_device_path.c
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ struct efi_device_path *efi_dp_shorten(struct efi_device_path *dp)
* in practice fallback.efi just uses MEDIA:HARD_DRIVE
* so not sure when we would see these other cases.
*/
if (EFI_DP_TYPE(dp, MESSAGING_DEVICE, MSG_USB_CLASS) ||
if (EFI_DP_TYPE(dp, MESSAGING_DEVICE, MSG_USB) ||
EFI_DP_TYPE(dp, MEDIA_DEVICE, HARD_DRIVE_PATH) ||
EFI_DP_TYPE(dp, MEDIA_DEVICE, FILE_PATH))
return dp;
Expand Down Expand Up @@ -564,6 +564,11 @@ __maybe_unused static unsigned int dp_size(struct udevice *dev)
return dp_size(dev->parent)
+ sizeof(struct efi_device_path_vendor) + 1;
#endif
#ifdef CONFIG_USB
case UCLASS_MASS_STORAGE:
return dp_size(dev->parent)
+ sizeof(struct efi_device_path_controller);
#endif
#ifdef CONFIG_VIRTIO_BLK
case UCLASS_VIRTIO:
/*
Expand All @@ -585,7 +590,7 @@ __maybe_unused static unsigned int dp_size(struct udevice *dev)
case UCLASS_MASS_STORAGE:
case UCLASS_USB_HUB:
return dp_size(dev->parent) +
sizeof(struct efi_device_path_usb_class);
sizeof(struct efi_device_path_usb);
default:
/* just skip over unknown classes: */
return dp_size(dev->parent);
Expand Down Expand Up @@ -741,6 +746,19 @@ __maybe_unused static void *dp_fill(void *buf, struct udevice *dev)
memcpy(&dp->ns_id, &ns_id, sizeof(ns_id));
return &dp[1];
}
#endif
#if defined(CONFIG_USB)
case UCLASS_MASS_STORAGE: {
struct blk_desc *desc = desc = dev_get_uclass_plat(dev);
struct efi_device_path_controller *dp =
dp_fill(buf, dev->parent);

dp->dp.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE;
dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_CONTROLLER;
dp->dp.length = sizeof(*dp);
dp->controller_number = desc->lun;
return &dp[1];
}
#endif
default:
debug("%s(%u) %s: unhandled parent class: %s (%u)\n",
Expand All @@ -767,19 +785,22 @@ __maybe_unused static void *dp_fill(void *buf, struct udevice *dev)
#endif
case UCLASS_MASS_STORAGE:
case UCLASS_USB_HUB: {
struct efi_device_path_usb_class *udp =
dp_fill(buf, dev->parent);
struct usb_device *udev = dev_get_parent_priv(dev);
struct usb_device_descriptor *desc = &udev->descriptor;
struct efi_device_path_usb *udp = dp_fill(buf, dev->parent);

switch (device_get_uclass_id(dev->parent)) {
case UCLASS_USB_HUB: {
struct usb_device *udev = dev_get_parent_priv(dev);

udp->parent_port_number = udev->portnr;
break;
}
default:
udp->parent_port_number = 0;
}
udp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
udp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_USB_CLASS;
udp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_USB;
udp->dp.length = sizeof(*udp);
udp->vendor_id = desc->idVendor;
udp->product_id = desc->idProduct;
udp->device_class = desc->bDeviceClass;
udp->device_subclass = desc->bDeviceSubClass;
udp->device_protocol = desc->bDeviceProtocol;
udp->usb_interface = 0;

return &udp[1];
}
Expand Down

0 comments on commit 180b711

Please sign in to comment.