From patchwork Thu Dec 5 11:56:09 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?VG9tw6HFoSBHb2xlbWJpb3Zza8O9?= X-Patchwork-Id: 1204588 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org (client-ip=209.51.188.17; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="KRwg2lqE"; dkim-atps=neutral Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 47TDjM50Zrz9s4Y for ; Thu, 5 Dec 2019 22:57:09 +1100 (AEDT) Received: from localhost ([::1]:53510 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1icpko-0006Kg-8z for incoming@patchwork.ozlabs.org; Thu, 05 Dec 2019 06:57:06 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:46266) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1icpk6-0006KJ-AH for qemu-devel@nongnu.org; Thu, 05 Dec 2019 06:56:24 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1icpjz-0007nW-8b for qemu-devel@nongnu.org; Thu, 05 Dec 2019 06:56:21 -0500 Received: from us-smtp-1.mimecast.com ([205.139.110.61]:50625 helo=us-smtp-delivery-1.mimecast.com) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1icpjz-0007jg-0s for qemu-devel@nongnu.org; Thu, 05 Dec 2019 06:56:15 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1575546974; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=xeR2ulcsDma+654BUOJ2JR5nk/YICGogc2btZJ9mlCw=; b=KRwg2lqEbnUWMWzq8YSQjS1UU0sVNX4C+jrE19z3urxyflSZztArRQC7YfogrxF5jRy5dj 4NB/2l4LD1TGpHGiS5PZPF3mvYQ9wjghU0npaKZb0odmZXcwX7H6tAIURdaWFhm7vNhppo 3IB//j50Tm1UJfPS2Ak150fb/uMPF3Q= Received: from mail-wm1-f72.google.com (mail-wm1-f72.google.com [209.85.128.72]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-160-gBOVVopwP36w-CR7SdQ8Kw-1; Thu, 05 Dec 2019 06:56:13 -0500 Received: by mail-wm1-f72.google.com with SMTP id n4so774608wmd.7 for ; Thu, 05 Dec 2019 03:56:13 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=rLlR9xJj439Y1JuUkq4kiEF/FWVrtI6qni/k12h1wuw=; b=ltfAh9B8I7s/uvg8rnBFSic7lOzfZSSSLB31o0VhQ6fWiE1C/ZpibGH1zhSUvBDRNN GFemv42bGQjwO9ZhFb6SPhFX6mOvJsswojaPPTcGGMssYc8tafN5BdFJsj3ubvvGDOZe 7jj2cpzdur6b8Ah0loiOC4dhaQwzhwuN8Hph0t7keo2qqi3o8Kuc7ic0nhd6wlaCIIa1 b0qHKeUqCN7eT7qiNHm1n3FSrF6kdFN21dXmiJ3x/H/HIoJgyXZucFdrXF200mhOTIS8 veXzXm+igxri+nPUgv4Uf9fPWW/g9LOWNjPMBylsk6ub40TXYSWh7ppvHhmx6KKwPD6z rpCw== X-Gm-Message-State: APjAAAXW5ORonoYVmy5N5OlMOnYjmbUc52QwGYj2T9MnkwSkjpb2Dt6Y O4oKPdcWtCj8ITrn9JlISkiC5dwl7j7F0O7sus1n3XpZnglUrzpdJx/qS0lJ1U6WJxqkRqk/nM0 gtQK6GijJ93XHOr4= X-Received: by 2002:a05:600c:22d3:: with SMTP id 19mr4690099wmg.92.1575546971551; Thu, 05 Dec 2019 03:56:11 -0800 (PST) X-Google-Smtp-Source: APXvYqxKfAHCpc4opz+zxviKsy7xSTEUHs26/gmSa6RZcG0dLJTKCbgNScFW2+VEFDF0ZilDKRO8eA== X-Received: by 2002:a05:600c:22d3:: with SMTP id 19mr4690055wmg.92.1575546971107; Thu, 05 Dec 2019 03:56:11 -0800 (PST) Received: from auriga.redhat.com (nat-pool-brq-t.redhat.com. [213.175.37.10]) by smtp.gmail.com with ESMTPSA id f1sm9823335wml.11.2019.12.05.03.56.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 05 Dec 2019 03:56:10 -0800 (PST) From: =?utf-8?b?VG9tw6HFoSBHb2xlbWJpb3Zza8O9?= To: qemu-devel@nongnu.org Subject: [PATCH v6] qga: add command guest-get-devices for reporting VirtIO devices Date: Thu, 5 Dec 2019 12:56:09 +0100 Message-Id: <03fbd542d6bae4cf07be56c51d57fc2962720445.1575546855.git.tgolembi@redhat.com> X-Mailer: git-send-email 2.24.0 MIME-Version: 1.0 X-MC-Unique: gBOVVopwP36w-CR7SdQ8Kw-1 X-Mimecast-Spam-Score: 0 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 205.139.110.61 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: =?utf-8?b?VG9tw6HFoSBHb2xlbWJpb3Zza8O9?= , =?utf-8?q?Daniel_P=2E_Berrang=C3=A9?= , Michael Roth , =?utf-8?q?Marc-Andr=C3=A9_L?= =?utf-8?q?ureau?= Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: "Qemu-devel" Add command for reporting devices on Windows guest. The intent is not so much to report the devices but more importantly the driver (and its version) that is assigned to the device. This gives caller the information whether VirtIO drivers are installed and/or whether inadequate driver is used on a device (e.g. QXL device with base VGA driver). Example: [ { "driver-date": "2019-08-12", "driver-name": "Red Hat VirtIO SCSI controller", "driver-version": "100.80.104.17300", "address": { "type": "pci", "data": { "device-id": 4162, "vendor-id": 6900 } } }, ... ] Signed-off-by: Tomáš Golembiovský --- Changes in v6: - modified schema: - date, version and address are optional - address is union having only PCI address for now --- qga/commands-posix.c | 9 ++ qga/commands-win32.c | 204 ++++++++++++++++++++++++++++++++++++++++++- qga/qapi-schema.json | 51 +++++++++++ 3 files changed, 263 insertions(+), 1 deletion(-) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 1c1a165dae..efcd9174a8 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -2758,6 +2758,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; } @@ -2978,3 +2980,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 55ba5b263a..cd942b3fb3 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -21,10 +21,11 @@ #ifdef CONFIG_QGA_NTDDSCSI #include #include +#endif #include #include #include -#endif +#include #include #include #include @@ -38,6 +39,36 @@ #include "qemu/host-utils.h" #include "qemu/base64.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 @@ -92,6 +123,8 @@ static OpenFlags guest_file_open_modes[] = { g_free(suffix); \ } while (0) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GuestDeviceInfo, qapi_free_GuestDeviceInfo) + static OpenFlags *find_open_flag(const char *mode_str) { int mode; @@ -2234,3 +2267,172 @@ 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) +{ + GStrv hw_ids = NULL; + 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); + } + hw_ids = (GStrv)g_array_free(values, FALSE); + values = NULL; + return hw_ids; +} + +/* + * 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)"); + goto out; + } + 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; + if (!g_regex_match(device_pci_re, hw_ids[j], 0, &match_info)) { + continue; + } + skip = false; + vendor_id = g_match_info_fetch(match_info, 1); + device_id = g_match_info_fetch(match_info, 2); + device->vendor_id = g_ascii_strtoull(vendor_id, NULL, 16); + device->device_id = g_ascii_strtoull(device_id, NULL, 16); + g_match_info_free(match_info); + } + 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)"); + goto out; + } + + 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); + + 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; + } + +out: + if (dev_info != INVALID_HANDLE_VALUE) { + SetupDiDestroyDeviceInfoList(dev_info); + } + return head; +} diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index fb4605cc19..92ed76c419 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -1242,3 +1242,54 @@ ## { 'command': 'guest-get-osinfo', 'returns': 'GuestOSInfo' } + +## +# @GuestDeviceAddressPCI: +# +# @vendor-id: vendor ID +# @device-id: device ID +# +# Since: 5.0 +## +{ 'struct': 'GuestDeviceAddressPCI', + 'data': { 'vendor-id': 'uint16', 'device-id': 'uint16' } } + +## +# @GuestDeviceAddress: +# +# Address of the device +# - @pci: address of PCI device, since: 5.0 +# +# Since: 5.0 +## +{ '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.0 +## +{ '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.0 +## +{ 'command': 'guest-get-devices', + 'returns': ['GuestDeviceInfo'] }