Commit b99260eb authored by Thomas Huth's avatar Thomas Huth Committed by David Gibson
Browse files

hw/ppc/spapr: Fix boot path of usb-host storage devices

When passing through an USB storage device to a pseries guest, it
is currently not possible to automatically boot from the device
if the "bootindex" property has been specified, too (e.g. when using
"-device nec-usb-xhci -device usb-host,hostbus=1,hostaddr=2,bootindex=0"
at the command line). The problem is that QEMU builds a device tree path
like "/pci@800000020000000/usb@0/usb-host@1" and passes it to SLOF
in the /chosen/qemu,boot-list property. SLOF, however, probes the
USB device, recognizes that it is a storage device and thus changes
its name to "storage", and additionally adds a child node for the
SCSI LUN, so the correct boot path in SLOF is something like
"/pci@800000020000000/usb@0/storage@1/disk@101000000000000" instead.
So when we detect an USB mass storage device with SCSI interface,
we've got to adjust the firmware boot-device path properly that
SLOF can automatically boot from the device.

Buglink: https://bugzilla.redhat.com/show_bug.cgi?id=1354177


Signed-off-by: default avatarThomas Huth <thuth@redhat.com>
Signed-off-by: default avatarDavid Gibson <david@gibson.dropbear.id.au>
parent e122090d
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -2198,6 +2198,19 @@ static char *spapr_get_fw_dev_path(FWPathProvider *p, BusState *bus,
        }
    }

    /*
     * SLOF probes the USB devices, and if it recognizes that the device is a
     * storage device, it changes its name to "storage" instead of "usb-host",
     * and additionally adds a child node for the SCSI LUN, so the correct
     * boot path in SLOF is something like .../storage@1/disk@xxx" instead.
     */
    if (strcmp("usb-host", qdev_fw_name(dev)) == 0) {
        USBDevice *usbdev = CAST(USBDevice, dev, TYPE_USB_DEVICE);
        if (usb_host_dev_is_scsi_storage(usbdev)) {
            return g_strdup_printf("storage@%s/disk", usbdev->port->path);
        }
    }

    if (phb) {
        /* Replace "pci" with "pci@800000020000000" */
        return g_strdup_printf("pci@%"PRIX64, phb->buid);
+29 −0
Original line number Diff line number Diff line
@@ -1707,6 +1707,35 @@ static void usb_host_auto_check(void *unused)
    timer_mod(usb_auto_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 2000);
}

/**
 * Check whether USB host device has a USB mass storage SCSI interface
 */
bool usb_host_dev_is_scsi_storage(USBDevice *ud)
{
    USBHostDevice *uhd = USB_HOST_DEVICE(ud);
    struct libusb_config_descriptor *conf;
    const struct libusb_interface_descriptor *intf;
    bool is_scsi_storage = false;
    int i;

    if (!uhd || libusb_get_active_config_descriptor(uhd->dev, &conf) != 0) {
        return false;
    }

    for (i = 0; i < conf->bNumInterfaces; i++) {
        intf = &conf->interface[i].altsetting[ud->altsetting[i]];
        if (intf->bInterfaceClass == LIBUSB_CLASS_MASS_STORAGE &&
            intf->bInterfaceSubClass == 6) {                 /* 6 means SCSI */
            is_scsi_storage = true;
            break;
        }
    }

    libusb_free_config_descriptor(conf);

    return is_scsi_storage;
}

void hmp_info_usbhost(Monitor *mon, const QDict *qdict)
{
    libusb_device **devs = NULL;
+5 −0
Original line number Diff line number Diff line
@@ -46,3 +46,8 @@ USBDevice *usb_host_device_open(USBBus *bus, const char *devname)
{
    return NULL;
}

bool usb_host_dev_is_scsi_storage(USBDevice *ud)
{
    return false;
}
+1 −0
Original line number Diff line number Diff line
@@ -471,6 +471,7 @@ void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p);
/* usb-linux.c */
USBDevice *usb_host_device_open(USBBus *bus, const char *devname);
void hmp_info_usbhost(Monitor *mon, const QDict *qdict);
bool usb_host_dev_is_scsi_storage(USBDevice *usbdev);

/* usb ports of the VM */