diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2020-09-14 10:34:35 +0100 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2020-09-14 10:34:35 +0100 |
commit | 95f217983919b5553d3d77ae0555e4119d383c87 (patch) | |
tree | 1c6c128bfa1e4e9d23df795da23df39a0e591dec | |
parent | f00f57f344236bbbe4c20845a0276a490dd5ffea (diff) | |
parent | 2e4211cee49a666bf0e333011689b0981025879e (diff) | |
download | qemu-95f217983919b5553d3d77ae0555e4119d383c87.zip qemu-95f217983919b5553d3d77ae0555e4119d383c87.tar.gz qemu-95f217983919b5553d3d77ae0555e4119d383c87.tar.bz2 |
Merge remote-tracking branch 'remotes/mdroth/tags/qga-pull-2020-09-12-tag' into staging
patch queue for qemu-ga
* add guest-get-devices for reporting virtio devices (w32-only)
* extend guest-get-fsinfo to support non-PCI virtio disk controllers
# gpg: Signature made Mon 14 Sep 2020 02:53:51 BST
# gpg: using RSA key CEACC9E15534EBABB82D3FA03353C9CEF108B584
# gpg: issuer "mdroth@linux.vnet.ibm.com"
# gpg: Good signature from "Michael Roth <flukshun@gmail.com>" [full]
# gpg: aka "Michael Roth <mdroth@utexas.edu>" [full]
# gpg: aka "Michael Roth <mdroth@linux.vnet.ibm.com>" [full]
# Primary key fingerprint: CEAC C9E1 5534 EBAB B82D 3FA0 3353 C9CE F108 B584
* remotes/mdroth/tags/qga-pull-2020-09-12-tag:
qga: add command guest-get-devices for reporting VirtIO devices
qga/commands-posix: Support fsinfo for non-PCI virtio devices, too
qga/commands-posix: Move the udev code from the pci to the generic function
qga/commands-posix: Rework build_guest_fsinfo_for_real_device() function
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r-- | qga/commands-posix.c | 166 | ||||
-rw-r--r-- | qga/commands-win32.c | 210 | ||||
-rw-r--r-- | qga/qapi-schema.json | 51 |
3 files changed, 378 insertions, 49 deletions
diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 1a62a3a..af5a58a 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -861,28 +861,26 @@ static int build_hosts(char const *syspath, char const *host, bool ata, return i; } -/* Store disk device info specified by @sysfs into @fs */ -static void build_guest_fsinfo_for_real_device(char const *syspath, - GuestFilesystemInfo *fs, - Error **errp) +/* + * Store disk device info for devices on the PCI bus. + * Returns true if information has been stored, or false for failure. + */ +static bool build_guest_fsinfo_for_pci_dev(char const *syspath, + GuestDiskAddress *disk, + Error **errp) { unsigned int pci[4], host, hosts[8], tgt[3]; int i, nhosts = 0, pcilen; - GuestDiskAddress *disk; - GuestPCIAddress *pciaddr; - GuestDiskAddressList *list = NULL; + GuestPCIAddress *pciaddr = disk->pci_controller; bool has_ata = false, has_host = false, has_tgt = false; char *p, *q, *driver = NULL; -#ifdef CONFIG_LIBUDEV - struct udev *udev = NULL; - struct udev_device *udevice = NULL; -#endif + bool ret = false; p = strstr(syspath, "/devices/pci"); if (!p || sscanf(p + 12, "%*x:%*x/%x:%x:%x.%x%n", pci, pci + 1, pci + 2, pci + 3, &pcilen) < 4) { g_debug("only pci device is supported: sysfs path '%s'", syspath); - return; + return false; } p += 12 + pcilen; @@ -903,7 +901,7 @@ static void build_guest_fsinfo_for_real_device(char const *syspath, } g_debug("unsupported driver or sysfs path '%s'", syspath); - return; + return false; } p = strstr(syspath, "/target"); @@ -929,38 +927,11 @@ static void build_guest_fsinfo_for_real_device(char const *syspath, } } - pciaddr = g_malloc0(sizeof(*pciaddr)); pciaddr->domain = pci[0]; pciaddr->bus = pci[1]; pciaddr->slot = pci[2]; pciaddr->function = pci[3]; - disk = g_malloc0(sizeof(*disk)); - disk->pci_controller = pciaddr; - - list = g_malloc0(sizeof(*list)); - list->value = disk; - -#ifdef CONFIG_LIBUDEV - udev = udev_new(); - udevice = udev_device_new_from_syspath(udev, syspath); - if (udev == NULL || udevice == NULL) { - g_debug("failed to query udev"); - } else { - const char *devnode, *serial; - devnode = udev_device_get_devnode(udevice); - if (devnode != NULL) { - disk->dev = g_strdup(devnode); - disk->has_dev = true; - } - serial = udev_device_get_property_value(udevice, "ID_SERIAL"); - if (serial != NULL && *serial != 0) { - disk->serial = g_strdup(serial); - disk->has_serial = true; - } - } -#endif - if (strcmp(driver, "ata_piix") == 0) { /* a host per ide bus, target*:0:<unit>:0 */ if (!has_host || !has_tgt) { @@ -1018,21 +989,111 @@ static void build_guest_fsinfo_for_real_device(char const *syspath, goto cleanup; } - list->next = fs->disk; - fs->disk = list; - goto out; + ret = true; cleanup: - if (list) { - qapi_free_GuestDiskAddressList(list); - } -out: g_free(driver); + return ret; +} + +/* + * Store disk device info for non-PCI virtio devices (for example s390x + * channel I/O devices). Returns true if information has been stored, or + * false for failure. + */ +static bool build_guest_fsinfo_for_nonpci_virtio(char const *syspath, + GuestDiskAddress *disk, + Error **errp) +{ + unsigned int tgt[3]; + char *p; + + if (!strstr(syspath, "/virtio") || !strstr(syspath, "/block")) { + g_debug("Unsupported virtio device '%s'", syspath); + return false; + } + + p = strstr(syspath, "/target"); + if (p && sscanf(p + 7, "%*u:%*u:%*u/%*u:%u:%u:%u", + &tgt[0], &tgt[1], &tgt[2]) == 3) { + /* virtio-scsi: target*:0:<target>:<unit> */ + disk->bus_type = GUEST_DISK_BUS_TYPE_SCSI; + disk->bus = tgt[0]; + disk->target = tgt[1]; + disk->unit = tgt[2]; + } else { + /* virtio-blk: 1 disk per 1 device */ + disk->bus_type = GUEST_DISK_BUS_TYPE_VIRTIO; + } + + return true; +} + +/* Store disk device info specified by @sysfs into @fs */ +static void build_guest_fsinfo_for_real_device(char const *syspath, + GuestFilesystemInfo *fs, + Error **errp) +{ + GuestDiskAddress *disk; + GuestPCIAddress *pciaddr; + GuestDiskAddressList *list = NULL; + bool has_hwinf; +#ifdef CONFIG_LIBUDEV + struct udev *udev = NULL; + struct udev_device *udevice = NULL; +#endif + + pciaddr = g_new0(GuestPCIAddress, 1); + pciaddr->domain = -1; /* -1 means field is invalid */ + pciaddr->bus = -1; + pciaddr->slot = -1; + pciaddr->function = -1; + + disk = g_new0(GuestDiskAddress, 1); + disk->pci_controller = pciaddr; + disk->bus_type = GUEST_DISK_BUS_TYPE_UNKNOWN; + + list = g_new0(GuestDiskAddressList, 1); + list->value = disk; + #ifdef CONFIG_LIBUDEV + udev = udev_new(); + udevice = udev_device_new_from_syspath(udev, syspath); + if (udev == NULL || udevice == NULL) { + g_debug("failed to query udev"); + } else { + const char *devnode, *serial; + devnode = udev_device_get_devnode(udevice); + if (devnode != NULL) { + disk->dev = g_strdup(devnode); + disk->has_dev = true; + } + serial = udev_device_get_property_value(udevice, "ID_SERIAL"); + if (serial != NULL && *serial != 0) { + disk->serial = g_strdup(serial); + disk->has_serial = true; + } + } + udev_unref(udev); udev_device_unref(udevice); #endif - return; + + if (strstr(syspath, "/devices/pci")) { + has_hwinf = build_guest_fsinfo_for_pci_dev(syspath, disk, errp); + } else if (strstr(syspath, "/virtio")) { + has_hwinf = build_guest_fsinfo_for_nonpci_virtio(syspath, disk, errp); + } else { + g_debug("Unsupported device type for '%s'", syspath); + has_hwinf = false; + } + + if (has_hwinf || disk->has_dev || disk->has_serial) { + list->next = fs->disk; + fs->disk = list; + } else { + qapi_free_GuestDiskAddressList(list); + } } static void build_guest_fsinfo_for_device(char const *devpath, @@ -2761,6 +2822,8 @@ GList *ga_command_blacklist_init(GList *blacklist) blacklist = g_list_append(blacklist, g_strdup("guest-fstrim")); #endif + blacklist = g_list_append(blacklist, g_strdup("guest-get-devices")); + return blacklist; } @@ -2981,3 +3044,10 @@ GuestOSInfo *qmp_guest_get_osinfo(Error **errp) return info; } + +GuestDeviceInfoList *qmp_guest_get_devices(Error **errp) +{ + error_setg(errp, QERR_UNSUPPORTED); + + return NULL; +} diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 15c9d79..48d8bbe 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -21,10 +21,11 @@ #ifdef CONFIG_QGA_NTDDSCSI #include <winioctl.h> #include <ntddscsi.h> +#endif #include <setupapi.h> #include <cfgmgr32.h> #include <initguid.h> -#endif +#include <devpropdef.h> #include <lm.h> #include <wtsapi32.h> #include <wininet.h> @@ -39,6 +40,36 @@ #include "qemu/base64.h" #include "commands-common.h" +/* + * The following should be in devpkey.h, but it isn't. The key names were + * prefixed to avoid (future) name clashes. Once the definitions get into + * mingw the following lines can be removed. + */ +DEFINE_DEVPROPKEY(qga_DEVPKEY_NAME, 0xb725f130, 0x47ef, 0x101a, 0xa5, + 0xf1, 0x02, 0x60, 0x8c, 0x9e, 0xeb, 0xac, 10); + /* DEVPROP_TYPE_STRING */ +DEFINE_DEVPROPKEY(qga_DEVPKEY_Device_HardwareIds, 0xa45c254e, 0xdf1c, + 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 3); + /* DEVPROP_TYPE_STRING_LIST */ +DEFINE_DEVPROPKEY(qga_DEVPKEY_Device_DriverDate, 0xa8b865dd, 0x2e3d, + 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 2); + /* DEVPROP_TYPE_FILETIME */ +DEFINE_DEVPROPKEY(qga_DEVPKEY_Device_DriverVersion, 0xa8b865dd, 0x2e3d, + 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 3); + /* DEVPROP_TYPE_STRING */ +/* The following shoud be in cfgmgr32.h, but it isn't */ +#ifndef CM_Get_DevNode_Property +CMAPI CONFIGRET WINAPI CM_Get_DevNode_PropertyW( + DEVINST dnDevInst, + CONST DEVPROPKEY * PropertyKey, + DEVPROPTYPE * PropertyType, + PBYTE PropertyBuffer, + PULONG PropertyBufferSize, + ULONG ulFlags +); +#define CM_Get_DevNode_Property CM_Get_DevNode_PropertyW +#endif + #ifndef SHTDN_REASON_FLAG_PLANNED #define SHTDN_REASON_FLAG_PLANNED 0x80000000 #endif @@ -2246,3 +2277,180 @@ GuestOSInfo *qmp_guest_get_osinfo(Error **errp) return info; } + +/* + * Safely get device property. Returned strings are using wide characters. + * Caller is responsible for freeing the buffer. + */ +static LPBYTE cm_get_property(DEVINST devInst, const DEVPROPKEY *propName, + PDEVPROPTYPE propType) +{ + CONFIGRET cr; + g_autofree LPBYTE buffer = NULL; + ULONG buffer_len = 0; + + /* First query for needed space */ + cr = CM_Get_DevNode_PropertyW(devInst, propName, propType, + buffer, &buffer_len, 0); + if (cr != CR_SUCCESS && cr != CR_BUFFER_SMALL) { + + slog("failed to get property size, error=0x%lx", cr); + return NULL; + } + buffer = g_new0(BYTE, buffer_len + 1); + cr = CM_Get_DevNode_PropertyW(devInst, propName, propType, + buffer, &buffer_len, 0); + if (cr != CR_SUCCESS) { + slog("failed to get device property, error=0x%lx", cr); + return NULL; + } + return g_steal_pointer(&buffer); +} + +static GStrv ga_get_hardware_ids(DEVINST devInstance) +{ + GArray *values = NULL; + DEVPROPTYPE cm_type; + LPWSTR id; + g_autofree LPWSTR property = (LPWSTR)cm_get_property(devInstance, + &qga_DEVPKEY_Device_HardwareIds, &cm_type); + if (property == NULL) { + slog("failed to get hardware IDs"); + return NULL; + } + if (*property == '\0') { + /* empty list */ + return NULL; + } + values = g_array_new(TRUE, TRUE, sizeof(gchar *)); + for (id = property; '\0' != *id; id += lstrlenW(id) + 1) { + gchar *id8 = g_utf16_to_utf8(id, -1, NULL, NULL, NULL); + g_array_append_val(values, id8); + } + return (GStrv)g_array_free(values, FALSE); +} + +/* + * https://docs.microsoft.com/en-us/windows-hardware/drivers/install/identifiers-for-pci-devices + */ +#define DEVICE_PCI_RE "PCI\\\\VEN_(1AF4|1B36)&DEV_([0-9A-B]{4})(&|$)" + +GuestDeviceInfoList *qmp_guest_get_devices(Error **errp) +{ + GuestDeviceInfoList *head = NULL, *cur_item = NULL, *item = NULL; + HDEVINFO dev_info = INVALID_HANDLE_VALUE; + SP_DEVINFO_DATA dev_info_data; + int i, j; + GError *gerr = NULL; + g_autoptr(GRegex) device_pci_re = NULL; + DEVPROPTYPE cm_type; + + device_pci_re = g_regex_new(DEVICE_PCI_RE, + G_REGEX_ANCHORED | G_REGEX_OPTIMIZE, 0, + &gerr); + g_assert(device_pci_re != NULL); + + dev_info_data.cbSize = sizeof(SP_DEVINFO_DATA); + dev_info = SetupDiGetClassDevs(0, 0, 0, DIGCF_PRESENT | DIGCF_ALLCLASSES); + if (dev_info == INVALID_HANDLE_VALUE) { + error_setg(errp, "failed to get device tree"); + return NULL; + } + + slog("enumerating devices"); + for (i = 0; SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data); i++) { + bool skip = true; + SYSTEMTIME utc_date; + g_autofree LPWSTR name = NULL; + g_autofree LPFILETIME date = NULL; + g_autofree LPWSTR version = NULL; + g_auto(GStrv) hw_ids = NULL; + g_autoptr(GuestDeviceInfo) device = g_new0(GuestDeviceInfo, 1); + g_autofree char *vendor_id = NULL; + g_autofree char *device_id = NULL; + + name = (LPWSTR)cm_get_property(dev_info_data.DevInst, + &qga_DEVPKEY_NAME, &cm_type); + if (name == NULL) { + slog("failed to get device description"); + continue; + } + device->driver_name = g_utf16_to_utf8(name, -1, NULL, NULL, NULL); + if (device->driver_name == NULL) { + error_setg(errp, "conversion to utf8 failed (driver name)"); + continue; + } + slog("querying device: %s", device->driver_name); + hw_ids = ga_get_hardware_ids(dev_info_data.DevInst); + if (hw_ids == NULL) { + continue; + } + for (j = 0; hw_ids[j] != NULL; j++) { + GMatchInfo *match_info; + GuestDeviceAddressPCI *address; + if (!g_regex_match(device_pci_re, hw_ids[j], 0, &match_info)) { + continue; + } + skip = false; + + address = g_new0(GuestDeviceAddressPCI, 1); + vendor_id = g_match_info_fetch(match_info, 1); + device_id = g_match_info_fetch(match_info, 2); + address->vendor_id = g_ascii_strtoull(vendor_id, NULL, 16); + address->device_id = g_ascii_strtoull(device_id, NULL, 16); + + device->address = g_new0(GuestDeviceAddress, 1); + device->has_address = true; + device->address->type = GUEST_DEVICE_ADDRESS_KIND_PCI; + device->address->u.pci.data = address; + + g_match_info_free(match_info); + break; + } + if (skip) { + continue; + } + + version = (LPWSTR)cm_get_property(dev_info_data.DevInst, + &qga_DEVPKEY_Device_DriverVersion, &cm_type); + if (version == NULL) { + slog("failed to get driver version"); + continue; + } + device->driver_version = g_utf16_to_utf8(version, -1, NULL, + NULL, NULL); + if (device->driver_version == NULL) { + error_setg(errp, "conversion to utf8 failed (driver version)"); + continue; + } + device->has_driver_version = true; + + date = (LPFILETIME)cm_get_property(dev_info_data.DevInst, + &qga_DEVPKEY_Device_DriverDate, &cm_type); + if (date == NULL) { + slog("failed to get driver date"); + continue; + } + FileTimeToSystemTime(date, &utc_date); + device->driver_date = g_strdup_printf("%04d-%02d-%02d", + utc_date.wYear, utc_date.wMonth, utc_date.wDay); + device->has_driver_date = true; + + slog("driver: %s\ndriver version: %s,%s\n", device->driver_name, + device->driver_date, device->driver_version); + item = g_new0(GuestDeviceInfoList, 1); + item->value = g_steal_pointer(&device); + if (!cur_item) { + head = cur_item = item; + } else { + cur_item->next = item; + cur_item = item; + } + continue; + } + + if (dev_info != INVALID_HANDLE_VALUE) { + SetupDiDestroyDeviceInfoList(dev_info); + } + return head; +} diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index b1e9ed8..d2ea744 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -1253,3 +1253,54 @@ ## { 'command': 'guest-get-osinfo', 'returns': 'GuestOSInfo' } + +## +# @GuestDeviceAddressPCI: +# +# @vendor-id: vendor ID +# @device-id: device ID +# +# Since: 5.2 +## +{ 'struct': 'GuestDeviceAddressPCI', + 'data': { 'vendor-id': 'uint16', 'device-id': 'uint16' } } + +## +# @GuestDeviceAddress: +# +# Address of the device +# - @pci: address of PCI device, since: 5.2 +# +# Since: 5.2 +## +{ 'union': 'GuestDeviceAddress', + 'data': { 'pci': 'GuestDeviceAddressPCI' } } + +## +# @GuestDeviceInfo: +# +# @driver-name: name of the associated driver +# @driver-date: driver release date in format YYYY-MM-DD +# @driver-version: driver version +# +# Since: 5.2 +## +{ 'struct': 'GuestDeviceInfo', + 'data': { + 'driver-name': 'str', + '*driver-date': 'str', + '*driver-version': 'str', + '*address': 'GuestDeviceAddress' + } } + +## +# @guest-get-devices: +# +# Retrieve information about device drivers in Windows guest +# +# Returns: @GuestDeviceInfo +# +# Since: 5.2 +## +{ 'command': 'guest-get-devices', + 'returns': ['GuestDeviceInfo'] } |