qemu-ga patch queue for 4.0

* fix w32 build breakages with VSS enabled
 * fix PCI topology reporting for Windows
 * fix OS version reporting for Windows
 * add systemd info to qga schema documentation
 * add proper 'id' handling to QGA so it conforms to QMP spec
 -----BEGIN PGP SIGNATURE-----
 
 iQFOBAABCgA4FiEEzqzJ4VU066u4LT+gM1PJzvEItYQFAlyPxVAaHG1kcm90aEBs
 aW51eC52bmV0LmlibS5jb20ACgkQM1PJzvEItYTUegf+NTCVX15dKrOTgXfbh9A8
 CTnSY+1qj3oOW/z9obvAto6Dgq/BUefC18fKSxWMgtcaMQcH8rTQXQRYuw7b9TKP
 rLoBa/AmSsJqjl/X6b9IVnFwpoqZzmyjsvjbcSh5RUMy0dMg3DridQQGmu0vduFk
 TAoL5eeFup/dbdKOIqUBCv3b0X3eCYMYRrwMkIX1GLaWaC/Gb3gasXDdKSi39E00
 sgJSsnXbsepOpht9BA74CpgVaawUsFfq/RiRQbUgLSNt+4AqkTVsVN2WkmcxN/MA
 Zgmwvl0jsuf1l0jO3aUFAK9uUDTB8oYqF0oCiun3nK2eqF4CLb+PG9DTjk8vycLA
 vA==
 =IICQ
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/mdroth/tags/qga-pull-2019-03-18-tag' into staging

qemu-ga patch queue for 4.0

* fix w32 build breakages with VSS enabled
* fix PCI topology reporting for Windows
* fix OS version reporting for Windows
* add systemd info to qga schema documentation
* add proper 'id' handling to QGA so it conforms to QMP spec

# gpg: Signature made Mon 18 Mar 2019 16:20:32 GMT
# 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-2019-03-18-tag:
  qmp: common 'id' handling & make QGA conform to QMP spec
  qga: process_event() simplification
  qga: Fix guest-get-fsinfo PCI address collection in Windows
  qga-win: fix VSS build breakage due to unintended gnu99 C++ flag
  qga-win: include glib when building VSS DLL
  qga-win: Adding support for Windows Server 2019 get-osinfo command
  qga: update docs with systemd suspend support info

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2019-03-19 09:53:29 +00:00
commit 54281eefc2
8 changed files with 288 additions and 192 deletions

2
configure vendored
View File

@ -4934,7 +4934,7 @@ int main(void) {
EOF EOF
if compile_prog "" "" ; then if compile_prog "" "" ; then
guest_agent_ntddscsi=yes guest_agent_ntddscsi=yes
libs_qga="-lsetupapi $libs_qga" libs_qga="-lsetupapi -lcfgmgr32 $libs_qga"
fi fi
fi fi

View File

@ -250,8 +250,6 @@ QEMUBH *qmp_dispatcher_bh;
struct QMPRequest { struct QMPRequest {
/* Owner of the request */ /* Owner of the request */
Monitor *mon; Monitor *mon;
/* "id" field of the request */
QObject *id;
/* /*
* Request object to be handled or Error to be reported * Request object to be handled or Error to be reported
* (exactly one of them is non-null) * (exactly one of them is non-null)
@ -353,7 +351,6 @@ int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func,
static void qmp_request_free(QMPRequest *req) static void qmp_request_free(QMPRequest *req)
{ {
qobject_unref(req->id);
qobject_unref(req->req); qobject_unref(req->req);
error_free(req->err); error_free(req->err);
g_free(req); g_free(req);
@ -4108,18 +4105,14 @@ static int monitor_can_read(void *opaque)
* Null @rsp can only happen for commands with QCO_NO_SUCCESS_RESP. * Null @rsp can only happen for commands with QCO_NO_SUCCESS_RESP.
* Nothing is emitted then. * Nothing is emitted then.
*/ */
static void monitor_qmp_respond(Monitor *mon, QDict *rsp, QObject *id) static void monitor_qmp_respond(Monitor *mon, QDict *rsp)
{ {
if (rsp) { if (rsp) {
if (id) {
qdict_put_obj(rsp, "id", qobject_ref(id));
}
qmp_send_response(mon, rsp); qmp_send_response(mon, rsp);
} }
} }
static void monitor_qmp_dispatch(Monitor *mon, QObject *req, QObject *id) static void monitor_qmp_dispatch(Monitor *mon, QObject *req)
{ {
Monitor *old_mon; Monitor *old_mon;
QDict *rsp; QDict *rsp;
@ -4144,7 +4137,7 @@ static void monitor_qmp_dispatch(Monitor *mon, QObject *req, QObject *id)
} }
} }
monitor_qmp_respond(mon, rsp, id); monitor_qmp_respond(mon, rsp);
qobject_unref(rsp); qobject_unref(rsp);
} }
@ -4208,13 +4201,15 @@ static void monitor_qmp_bh_dispatcher(void *data)
mon->qmp.qmp_requests->length == QMP_REQ_QUEUE_LEN_MAX - 1; mon->qmp.qmp_requests->length == QMP_REQ_QUEUE_LEN_MAX - 1;
qemu_mutex_unlock(&mon->qmp.qmp_queue_lock); qemu_mutex_unlock(&mon->qmp.qmp_queue_lock);
if (req_obj->req) { if (req_obj->req) {
trace_monitor_qmp_cmd_in_band(qobject_get_try_str(req_obj->id) ?: ""); QDict *qdict = qobject_to(QDict, req_obj->req);
monitor_qmp_dispatch(mon, req_obj->req, req_obj->id); QObject *id = qdict ? qdict_get(qdict, "id") : NULL;
trace_monitor_qmp_cmd_in_band(qobject_get_try_str(id) ?: "");
monitor_qmp_dispatch(mon, req_obj->req);
} else { } else {
assert(req_obj->err); assert(req_obj->err);
rsp = qmp_error_response(req_obj->err); rsp = qmp_error_response(req_obj->err);
req_obj->err = NULL; req_obj->err = NULL;
monitor_qmp_respond(mon, rsp, NULL); monitor_qmp_respond(mon, rsp);
qobject_unref(rsp); qobject_unref(rsp);
} }
@ -4239,8 +4234,7 @@ static void handle_qmp_command(void *opaque, QObject *req, Error *err)
qdict = qobject_to(QDict, req); qdict = qobject_to(QDict, req);
if (qdict) { if (qdict) {
id = qobject_ref(qdict_get(qdict, "id")); id = qdict_get(qdict, "id");
qdict_del(qdict, "id");
} /* else will fail qmp_dispatch() */ } /* else will fail qmp_dispatch() */
if (req && trace_event_get_state_backends(TRACE_HANDLE_QMP_COMMAND)) { if (req && trace_event_get_state_backends(TRACE_HANDLE_QMP_COMMAND)) {
@ -4251,17 +4245,14 @@ static void handle_qmp_command(void *opaque, QObject *req, Error *err)
if (qdict && qmp_is_oob(qdict)) { if (qdict && qmp_is_oob(qdict)) {
/* OOB commands are executed immediately */ /* OOB commands are executed immediately */
trace_monitor_qmp_cmd_out_of_band(qobject_get_try_str(id) trace_monitor_qmp_cmd_out_of_band(qobject_get_try_str(id) ?: "");
?: ""); monitor_qmp_dispatch(mon, req);
monitor_qmp_dispatch(mon, req, id);
qobject_unref(req); qobject_unref(req);
qobject_unref(id);
return; return;
} }
req_obj = g_new0(QMPRequest, 1); req_obj = g_new0(QMPRequest, 1);
req_obj->mon = mon; req_obj->mon = mon;
req_obj->id = id;
req_obj->req = req; req_obj->req = req;
req_obj->err = err; req_obj->err = err;
@ -4281,7 +4272,7 @@ static void handle_qmp_command(void *opaque, QObject *req, Error *err)
/* /*
* Put the request to the end of queue so that requests will be * Put the request to the end of queue so that requests will be
* handled in time order. Ownership for req_obj, req, id, * handled in time order. Ownership for req_obj, req,
* etc. will be delivered to the handler side. * etc. will be delivered to the handler side.
*/ */
assert(mon->qmp.qmp_requests->length < QMP_REQ_QUEUE_LEN_MAX); assert(mon->qmp.qmp_requests->length < QMP_REQ_QUEUE_LEN_MAX);

View File

@ -58,6 +58,8 @@ static QDict *qmp_dispatch_check_obj(const QObject *request, bool allow_oob,
"QMP input member 'arguments' must be an object"); "QMP input member 'arguments' must be an object");
return NULL; return NULL;
} }
} else if (!strcmp(arg_name, "id")) {
continue;
} else { } else {
error_setg(errp, "QMP input member '%s' is unexpected", error_setg(errp, "QMP input member '%s' is unexpected",
arg_name); arg_name);
@ -165,11 +167,11 @@ QDict *qmp_dispatch(QmpCommandList *cmds, QObject *request,
bool allow_oob) bool allow_oob)
{ {
Error *err = NULL; Error *err = NULL;
QObject *ret; QDict *dict = qobject_to(QDict, request);
QObject *ret, *id = dict ? qdict_get(dict, "id") : NULL;
QDict *rsp; QDict *rsp;
ret = do_qmp_dispatch(cmds, request, allow_oob, &err); ret = do_qmp_dispatch(cmds, request, allow_oob, &err);
if (err) { if (err) {
rsp = qmp_error_response(err); rsp = qmp_error_response(err);
} else if (ret) { } else if (ret) {
@ -180,5 +182,9 @@ QDict *qmp_dispatch(QmpCommandList *cmds, QObject *request,
rsp = NULL; rsp = NULL;
} }
if (rsp && id) {
qdict_put_obj(rsp, "id", qobject_ref(id));
}
return rsp; return rsp;
} }

View File

@ -22,6 +22,7 @@
#include <winioctl.h> #include <winioctl.h>
#include <ntddscsi.h> #include <ntddscsi.h>
#include <setupapi.h> #include <setupapi.h>
#include <cfgmgr32.h>
#include <initguid.h> #include <initguid.h>
#endif #endif
#include <lm.h> #include <lm.h>
@ -485,56 +486,29 @@ static GuestDiskBusType find_bus_type(STORAGE_BUS_TYPE bus)
return win2qemu[(int)bus]; return win2qemu[(int)bus];
} }
/* XXX: The following function is BROKEN!
*
* It does not work and probably has never worked. When we query for list of
* disks we get cryptic names like "\Device\0000001d" instead of
* "\PhysicalDriveX" or "\HarddiskX". Whether the names can be translated one
* way or the other for comparison is an open question.
*
* When we query volume names (the original version) we are able to match those
* but then the property queries report error "Invalid function". (duh!)
*/
/*
DEFINE_GUID(GUID_DEVINTERFACE_VOLUME,
0x53f5630dL, 0xb6bf, 0x11d0, 0x94, 0xf2,
0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b);
*/
DEFINE_GUID(GUID_DEVINTERFACE_DISK, DEFINE_GUID(GUID_DEVINTERFACE_DISK,
0x53f56307L, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x53f56307L, 0xb6bf, 0x11d0, 0x94, 0xf2,
0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b);
DEFINE_GUID(GUID_DEVINTERFACE_STORAGEPORT,
0x2accfe60L, 0xc130, 0x11d2, 0xb0, 0x82,
0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b);
static GuestPCIAddress *get_pci_info(int number, Error **errp)
static GuestPCIAddress *get_pci_info(char *guid, Error **errp)
{ {
HDEVINFO dev_info; HDEVINFO dev_info;
SP_DEVINFO_DATA dev_info_data; SP_DEVINFO_DATA dev_info_data;
DWORD size = 0; SP_DEVICE_INTERFACE_DATA dev_iface_data;
HANDLE dev_file;
int i; int i;
char dev_name[MAX_PATH];
char *buffer = NULL;
GuestPCIAddress *pci = NULL; GuestPCIAddress *pci = NULL;
char *name = NULL;
bool partial_pci = false; bool partial_pci = false;
pci = g_malloc0(sizeof(*pci)); pci = g_malloc0(sizeof(*pci));
pci->domain = -1; pci->domain = -1;
pci->slot = -1; pci->slot = -1;
pci->function = -1; pci->function = -1;
pci->bus = -1; pci->bus = -1;
if (g_str_has_prefix(guid, "\\\\.\\") ||
g_str_has_prefix(guid, "\\\\?\\")) {
name = g_strdup(guid + 4);
} else {
name = g_strdup(guid);
}
if (!QueryDosDevice(name, dev_name, ARRAY_SIZE(dev_name))) {
error_setg_win32(errp, GetLastError(), "failed to get dos device name");
goto out;
}
dev_info = SetupDiGetClassDevs(&GUID_DEVINTERFACE_DISK, 0, 0, dev_info = SetupDiGetClassDevs(&GUID_DEVINTERFACE_DISK, 0, 0,
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (dev_info == INVALID_HANDLE_VALUE) { if (dev_info == INVALID_HANDLE_VALUE) {
@ -544,90 +518,220 @@ static GuestPCIAddress *get_pci_info(char *guid, Error **errp)
g_debug("enumerating devices"); g_debug("enumerating devices");
dev_info_data.cbSize = sizeof(SP_DEVINFO_DATA); dev_info_data.cbSize = sizeof(SP_DEVINFO_DATA);
dev_iface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
for (i = 0; SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data); i++) { for (i = 0; SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data); i++) {
DWORD addr, bus, slot, data, size2; PSP_DEVICE_INTERFACE_DETAIL_DATA pdev_iface_detail_data = NULL;
int func, dev; STORAGE_DEVICE_NUMBER sdn;
while (!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data, char *parent_dev_id = NULL;
SPDRP_PHYSICAL_DEVICE_OBJECT_NAME, HDEVINFO parent_dev_info;
&data, (PBYTE)buffer, size, SP_DEVINFO_DATA parent_dev_info_data;
&size2)) { DWORD j;
size = MAX(size, size2); DWORD size = 0;
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
g_free(buffer); g_debug("getting device path");
/* Double the size to avoid problems on if (SetupDiEnumDeviceInterfaces(dev_info, &dev_info_data,
* W2k MBCS systems per KB 888609. &GUID_DEVINTERFACE_DISK, 0,
* https://support.microsoft.com/en-us/kb/259695 */ &dev_iface_data)) {
buffer = g_malloc(size * 2); while (!SetupDiGetDeviceInterfaceDetail(dev_info, &dev_iface_data,
} else { pdev_iface_detail_data,
size, &size,
&dev_info_data)) {
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
pdev_iface_detail_data = g_malloc(size);
pdev_iface_detail_data->cbSize =
sizeof(*pdev_iface_detail_data);
} else {
error_setg_win32(errp, GetLastError(),
"failed to get device interfaces");
goto free_dev_info;
}
}
dev_file = CreateFile(pdev_iface_detail_data->DevicePath, 0,
FILE_SHARE_READ, NULL, OPEN_EXISTING, 0,
NULL);
g_free(pdev_iface_detail_data);
if (!DeviceIoControl(dev_file, IOCTL_STORAGE_GET_DEVICE_NUMBER,
NULL, 0, &sdn, sizeof(sdn), &size, NULL)) {
CloseHandle(dev_file);
error_setg_win32(errp, GetLastError(), error_setg_win32(errp, GetLastError(),
"failed to get device name"); "failed to get device slot number");
goto free_dev_info; goto free_dev_info;
} }
CloseHandle(dev_file);
if (sdn.DeviceNumber != number) {
continue;
}
} else {
error_setg_win32(errp, GetLastError(),
"failed to get device interfaces");
goto free_dev_info;
}
g_debug("found device slot %d. Getting storage controller", number);
{
CONFIGRET cr;
DEVINST dev_inst, parent_dev_inst;
ULONG dev_id_size = 0;
size = 0;
while (!SetupDiGetDeviceInstanceId(dev_info, &dev_info_data,
parent_dev_id, size, &size)) {
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
parent_dev_id = g_malloc(size);
} else {
error_setg_win32(errp, GetLastError(),
"failed to get device instance ID");
goto out;
}
}
/*
* CM API used here as opposed to
* SetupDiGetDeviceProperty(..., DEVPKEY_Device_Parent, ...)
* which exports are only available in mingw-w64 6+
*/
cr = CM_Locate_DevInst(&dev_inst, parent_dev_id, 0);
if (cr != CR_SUCCESS) {
g_error("CM_Locate_DevInst failed with code %lx", cr);
error_setg_win32(errp, GetLastError(),
"failed to get device instance");
goto out;
}
cr = CM_Get_Parent(&parent_dev_inst, dev_inst, 0);
if (cr != CR_SUCCESS) {
g_error("CM_Get_Parent failed with code %lx", cr);
error_setg_win32(errp, GetLastError(),
"failed to get parent device instance");
goto out;
}
cr = CM_Get_Device_ID_Size(&dev_id_size, parent_dev_inst, 0);
if (cr != CR_SUCCESS) {
g_error("CM_Get_Device_ID_Size failed with code %lx", cr);
error_setg_win32(errp, GetLastError(),
"failed to get parent device ID length");
goto out;
}
++dev_id_size;
if (dev_id_size > size) {
g_free(parent_dev_id);
parent_dev_id = g_malloc(dev_id_size);
}
cr = CM_Get_Device_ID(parent_dev_inst, parent_dev_id, dev_id_size,
0);
if (cr != CR_SUCCESS) {
g_error("CM_Get_Device_ID failed with code %lx", cr);
error_setg_win32(errp, GetLastError(),
"failed to get parent device ID");
goto out;
}
} }
if (g_strcmp0(buffer, dev_name)) { g_debug("querying storage controller %s for PCI information",
continue; parent_dev_id);
} parent_dev_info =
g_debug("found device %s", dev_name); SetupDiGetClassDevs(&GUID_DEVINTERFACE_STORAGEPORT, parent_dev_id,
NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
g_free(parent_dev_id);
/* There is no need to allocate buffer in the next functions. The size if (parent_dev_info == INVALID_HANDLE_VALUE) {
* is known and ULONG according to error_setg_win32(errp, GetLastError(),
* https://support.microsoft.com/en-us/kb/253232 "failed to get parent device");
* https://msdn.microsoft.com/en-us/library/windows/hardware/ff543095(v=vs.85).aspx goto out;
*/
if (!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data,
SPDRP_BUSNUMBER, &data, (PBYTE)&bus, size, NULL)) {
debug_error("failed to get bus");
bus = -1;
partial_pci = true;
} }
/* The function retrieves the device's address. This value will be parent_dev_info_data.cbSize = sizeof(SP_DEVINFO_DATA);
* transformed into device function and number */ if (!SetupDiEnumDeviceInfo(parent_dev_info, 0, &parent_dev_info_data)) {
if (!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data, error_setg_win32(errp, GetLastError(),
SPDRP_ADDRESS, &data, (PBYTE)&addr, size, NULL)) { "failed to get parent device data");
debug_error("failed to get address"); goto out;
addr = -1;
partial_pci = true;
} }
/* This call returns UINumber of DEVICE_CAPABILITIES structure. for (j = 0;
* This number is typically a user-perceived slot number. */ SetupDiEnumDeviceInfo(parent_dev_info, j, &parent_dev_info_data);
if (!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data, j++) {
SPDRP_UI_NUMBER, &data, (PBYTE)&slot, size, NULL)) { DWORD addr, bus, ui_slot, type;
debug_error("failed to get slot"); int func, slot;
slot = -1;
partial_pci = true;
}
/* SetupApi gives us the same information as driver with /*
* IoGetDeviceProperty. According to Microsoft * There is no need to allocate buffer in the next functions. The
* https://support.microsoft.com/en-us/kb/253232 * size is known and ULONG according to
* FunctionNumber = (USHORT)((propertyAddress) & 0x0000FFFF); * https://msdn.microsoft.com/en-us/library/windows/hardware/ff543095(v=vs.85).aspx
* DeviceNumber = (USHORT)(((propertyAddress) >> 16) & 0x0000FFFF); */
* SPDRP_ADDRESS is propertyAddress, so we do the same.*/ if (!SetupDiGetDeviceRegistryProperty(
parent_dev_info, &parent_dev_info_data, SPDRP_BUSNUMBER,
&type, (PBYTE)&bus, size, NULL)) {
debug_error("failed to get PCI bus");
bus = -1;
partial_pci = true;
}
if (partial_pci) { /*
pci->domain = -1; * The function retrieves the device's address. This value will be
pci->slot = -1; * transformed into device function and number
pci->function = -1; */
pci->bus = -1; if (!SetupDiGetDeviceRegistryProperty(
} else { parent_dev_info, &parent_dev_info_data, SPDRP_ADDRESS,
func = ((int) addr == -1) ? -1 : addr & 0x0000FFFF; &type, (PBYTE)&addr, size, NULL)) {
dev = ((int) addr == -1) ? -1 : (addr >> 16) & 0x0000FFFF; debug_error("failed to get PCI address");
pci->domain = dev; addr = -1;
pci->slot = (int) slot; partial_pci = true;
pci->function = func; }
pci->bus = (int) bus;
/*
* This call returns UINumber of DEVICE_CAPABILITIES structure.
* This number is typically a user-perceived slot number.
*/
if (!SetupDiGetDeviceRegistryProperty(
parent_dev_info, &parent_dev_info_data, SPDRP_UI_NUMBER,
&type, (PBYTE)&ui_slot, size, NULL)) {
debug_error("failed to get PCI slot");
ui_slot = -1;
partial_pci = true;
}
/*
* SetupApi gives us the same information as driver with
* IoGetDeviceProperty. According to Microsoft:
*
* FunctionNumber = (USHORT)((propertyAddress) & 0x0000FFFF)
* DeviceNumber = (USHORT)(((propertyAddress) >> 16) & 0x0000FFFF)
* SPDRP_ADDRESS is propertyAddress, so we do the same.
*
* https://docs.microsoft.com/en-us/windows/desktop/api/setupapi/nf-setupapi-setupdigetdeviceregistrypropertya
*/
if (partial_pci) {
pci->domain = -1;
pci->slot = -1;
pci->function = -1;
pci->bus = -1;
continue;
} else {
func = ((int)addr == -1) ? -1 : addr & 0x0000FFFF;
slot = ((int)addr == -1) ? -1 : (addr >> 16) & 0x0000FFFF;
if ((int)ui_slot != slot) {
g_debug("mismatch with reported slot values: %d vs %d",
(int)ui_slot, slot);
}
pci->domain = 0;
pci->slot = (int)ui_slot;
pci->function = func;
pci->bus = (int)bus;
break;
}
} }
SetupDiDestroyDeviceInfoList(parent_dev_info);
break; break;
} }
free_dev_info: free_dev_info:
SetupDiDestroyDeviceInfoList(dev_info); SetupDiDestroyDeviceInfoList(dev_info);
out: out:
g_free(buffer);
g_free(name);
return pci; return pci;
} }
@ -685,7 +789,8 @@ out_free:
return; return;
} }
static void get_single_disk_info(GuestDiskAddress *disk, Error **errp) static void get_single_disk_info(int disk_number,
GuestDiskAddress *disk, Error **errp)
{ {
SCSI_ADDRESS addr, *scsi_ad; SCSI_ADDRESS addr, *scsi_ad;
DWORD len; DWORD len;
@ -714,7 +819,7 @@ static void get_single_disk_info(GuestDiskAddress *disk, Error **errp)
* if that doesn't hold since that suggests some other unexpected * if that doesn't hold since that suggests some other unexpected
* breakage * breakage
*/ */
disk->pci_controller = get_pci_info(disk->dev, &local_err); disk->pci_controller = get_pci_info(disk_number, &local_err);
if (local_err) { if (local_err) {
error_propagate(errp, local_err); error_propagate(errp, local_err);
goto err_close; goto err_close;
@ -728,7 +833,7 @@ static void get_single_disk_info(GuestDiskAddress *disk, Error **errp)
/* We are able to use the same ioctls for different bus types /* We are able to use the same ioctls for different bus types
* according to Microsoft docs * according to Microsoft docs
* https://technet.microsoft.com/en-us/library/ee851589(v=ws.10).aspx */ * https://technet.microsoft.com/en-us/library/ee851589(v=ws.10).aspx */
g_debug("getting pci-controller info"); g_debug("getting SCSI info");
if (DeviceIoControl(disk_h, IOCTL_SCSI_GET_ADDRESS, NULL, 0, scsi_ad, if (DeviceIoControl(disk_h, IOCTL_SCSI_GET_ADDRESS, NULL, 0, scsi_ad,
sizeof(SCSI_ADDRESS), &len, NULL)) { sizeof(SCSI_ADDRESS), &len, NULL)) {
disk->unit = addr.Lun; disk->unit = addr.Lun;
@ -776,12 +881,10 @@ static GuestDiskAddressList *build_guest_disk_info(char *guid, Error **errp)
size = sizeof(VOLUME_DISK_EXTENTS); size = sizeof(VOLUME_DISK_EXTENTS);
extents = g_malloc0(size); extents = g_malloc0(size);
if (!DeviceIoControl(vol_h, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, if (!DeviceIoControl(vol_h, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL,
0, extents, size, NULL, NULL)) { 0, extents, size, &size, NULL)) {
DWORD last_err = GetLastError(); DWORD last_err = GetLastError();
if (last_err == ERROR_MORE_DATA) { if (last_err == ERROR_MORE_DATA) {
/* Try once more with big enough buffer */ /* Try once more with big enough buffer */
size = sizeof(VOLUME_DISK_EXTENTS)
+ extents->NumberOfDiskExtents*sizeof(DISK_EXTENT);
g_free(extents); g_free(extents);
extents = g_malloc0(size); extents = g_malloc0(size);
if (!DeviceIoControl( if (!DeviceIoControl(
@ -797,7 +900,7 @@ static GuestDiskAddressList *build_guest_disk_info(char *guid, Error **errp)
disk = g_malloc0(sizeof(GuestDiskAddress)); disk = g_malloc0(sizeof(GuestDiskAddress));
disk->has_dev = true; disk->has_dev = true;
disk->dev = g_strdup(name); disk->dev = g_strdup(name);
get_single_disk_info(disk, &local_err); get_single_disk_info(0xffffffff, disk, &local_err);
if (local_err) { if (local_err) {
g_debug("failed to get disk info, ignoring error: %s", g_debug("failed to get disk info, ignoring error: %s",
error_get_pretty(local_err)); error_get_pretty(local_err));
@ -831,9 +934,9 @@ static GuestDiskAddressList *build_guest_disk_info(char *guid, Error **errp)
*/ */
disk->has_dev = true; disk->has_dev = true;
disk->dev = g_strdup_printf("\\\\.\\PhysicalDrive%lu", disk->dev = g_strdup_printf("\\\\.\\PhysicalDrive%lu",
extents->Extents[i].DiskNumber); extents->Extents[i].DiskNumber);
get_single_disk_info(disk, &local_err); get_single_disk_info(extents->Extents[i].DiskNumber, disk, &local_err);
if (local_err) { if (local_err) {
error_propagate(errp, local_err); error_propagate(errp, local_err);
goto out; goto out;
@ -1941,12 +2044,24 @@ static ga_matrix_lookup_t const WIN_VERSION_MATRIX[2][8] = {
{ 6, 1, "Microsoft Windows Server 2008 R2", "2008r2"}, { 6, 1, "Microsoft Windows Server 2008 R2", "2008r2"},
{ 6, 2, "Microsoft Windows Server 2012", "2012"}, { 6, 2, "Microsoft Windows Server 2012", "2012"},
{ 6, 3, "Microsoft Windows Server 2012 R2", "2012r2"}, { 6, 3, "Microsoft Windows Server 2012 R2", "2012r2"},
{10, 0, "Microsoft Windows Server 2016", "2016"}, { 0, 0, 0},
{ 0, 0, 0}, { 0, 0, 0},
{ 0, 0, 0} { 0, 0, 0}
} }
}; };
typedef struct _ga_win_10_0_server_t {
int final_build;
char const *version;
char const *version_id;
} ga_win_10_0_server_t;
static ga_win_10_0_server_t const WIN_10_0_SERVER_VERSION_MATRIX[3] = {
{14393, "Microsoft Windows Server 2016", "2016"},
{17763, "Microsoft Windows Server 2019", "2019"},
{0, 0}
};
static void ga_get_win_version(RTL_OSVERSIONINFOEXW *info, Error **errp) static void ga_get_win_version(RTL_OSVERSIONINFOEXW *info, Error **errp)
{ {
typedef NTSTATUS(WINAPI * rtl_get_version_t)( typedef NTSTATUS(WINAPI * rtl_get_version_t)(
@ -1971,10 +2086,23 @@ static char *ga_get_win_name(OSVERSIONINFOEXW const *os_version, bool id)
{ {
DWORD major = os_version->dwMajorVersion; DWORD major = os_version->dwMajorVersion;
DWORD minor = os_version->dwMinorVersion; DWORD minor = os_version->dwMinorVersion;
DWORD build = os_version->dwBuildNumber;
int tbl_idx = (os_version->wProductType != VER_NT_WORKSTATION); int tbl_idx = (os_version->wProductType != VER_NT_WORKSTATION);
ga_matrix_lookup_t const *table = WIN_VERSION_MATRIX[tbl_idx]; ga_matrix_lookup_t const *table = WIN_VERSION_MATRIX[tbl_idx];
ga_win_10_0_server_t const *win_10_0_table = WIN_10_0_SERVER_VERSION_MATRIX;
while (table->version != NULL) { while (table->version != NULL) {
if (major == table->major && minor == table->minor) { if (major == 10 && minor == 0 && tbl_idx) {
while (win_10_0_table->version != NULL) {
if (build <= win_10_0_table->final_build) {
if (id) {
return g_strdup(win_10_0_table->version_id);
} else {
return g_strdup(win_10_0_table->version);
}
}
win_10_0_table++;
}
} else if (major == table->major && minor == table->minor) {
if (id) { if (id) {
return g_strdup(table->version_id); return g_strdup(table->version_id);
} else { } else {

View File

@ -523,15 +523,15 @@ fail:
#endif #endif
} }
static int send_response(GAState *s, QDict *payload) static int send_response(GAState *s, const QDict *rsp)
{ {
const char *buf; const char *buf;
QString *payload_qstr, *response_qstr; QString *payload_qstr, *response_qstr;
GIOStatus status; GIOStatus status;
g_assert(payload && s->channel); g_assert(rsp && s->channel);
payload_qstr = qobject_to_json(QOBJECT(payload)); payload_qstr = qobject_to_json(QOBJECT(rsp));
if (!payload_qstr) { if (!payload_qstr) {
return -EINVAL; return -EINVAL;
} }
@ -557,53 +557,24 @@ static int send_response(GAState *s, QDict *payload)
return 0; return 0;
} }
static void process_command(GAState *s, QDict *req)
{
QDict *rsp;
int ret;
g_assert(req);
g_debug("processing command");
rsp = qmp_dispatch(&ga_commands, QOBJECT(req), false);
if (rsp) {
ret = send_response(s, rsp);
if (ret < 0) {
g_warning("error sending response: %s", strerror(-ret));
}
qobject_unref(rsp);
}
}
/* handle requests/control events coming in over the channel */ /* handle requests/control events coming in over the channel */
static void process_event(void *opaque, QObject *obj, Error *err) static void process_event(void *opaque, QObject *obj, Error *err)
{ {
GAState *s = opaque; GAState *s = opaque;
QDict *req, *rsp; QDict *rsp;
int ret; int ret;
g_debug("process_event: called"); g_debug("process_event: called");
assert(!obj != !err); assert(!obj != !err);
if (err) { if (err) {
goto err; rsp = qmp_error_response(err);
} goto end;
req = qobject_to(QDict, obj);
if (!req) {
error_setg(&err, "Input must be a JSON object");
goto err;
}
if (!qdict_haskey(req, "execute")) {
g_warning("unrecognized payload format");
error_setg(&err, QERR_UNSUPPORTED);
goto err;
} }
process_command(s, req); g_debug("processing command");
qobject_unref(obj); rsp = qmp_dispatch(&ga_commands, obj, false);
return;
err: end:
g_warning("failed to parse event: %s", error_get_pretty(err));
rsp = qmp_error_response(err);
ret = send_response(s, rsp); ret = send_response(s, rsp);
if (ret < 0) { if (ret < 0) {
g_warning("error sending error response: %s", strerror(-ret)); g_warning("error sending error response: %s", strerror(-ret));

View File

@ -532,12 +532,12 @@
# #
# Suspend guest to disk. # Suspend guest to disk.
# #
# This command tries to execute the scripts provided by the pm-utils package. # This command attempts to suspend the guest using three strategies, in this
# If it's not available, the suspend operation will be performed by manually # order:
# writing to a sysfs file.
# #
# For the best results it's strongly recommended to have the pm-utils # - systemd hibernate
# package installed in the guest. # - pm-utils (via pm-hibernate)
# - manual write into sysfs
# #
# This command does NOT return a response on success. There is a high chance # This command does NOT return a response on success. There is a high chance
# the command succeeded if the VM exits with a zero exit status or, when # the command succeeded if the VM exits with a zero exit status or, when
@ -560,12 +560,12 @@
# #
# Suspend guest to ram. # Suspend guest to ram.
# #
# This command tries to execute the scripts provided by the pm-utils package. # This command attempts to suspend the guest using three strategies, in this
# If it's not available, the suspend operation will be performed by manually # order:
# writing to a sysfs file.
# #
# For the best results it's strongly recommended to have the pm-utils # - systemd suspend
# package installed in the guest. # - pm-utils (via pm-suspend)
# - manual write into sysfs
# #
# IMPORTANT: guest-suspend-ram requires working wakeup support in # IMPORTANT: guest-suspend-ram requires working wakeup support in
# QEMU. You should check QMP command query-current-machine returns # QEMU. You should check QMP command query-current-machine returns
@ -594,7 +594,10 @@
# #
# Save guest state to disk and suspend to ram. # Save guest state to disk and suspend to ram.
# #
# This command requires the pm-utils package to be installed in the guest. # This command attempts to suspend the guest by executing, in this order:
#
# - systemd hybrid-sleep
# - pm-utils (via pm-suspend-hybrid)
# #
# IMPORTANT: guest-suspend-hybrid requires working wakeup support in # IMPORTANT: guest-suspend-hybrid requires working wakeup support in
# QEMU. You should check QMP command query-current-machine returns # QEMU. You should check QMP command query-current-machine returns

View File

@ -3,9 +3,9 @@
qga-vss-dll-obj-y += requester.o provider.o install.o qga-vss-dll-obj-y += requester.o provider.o install.o
obj-qga-vss-dll-obj-y = $(addprefix $(obj)/, $(qga-vss-dll-obj-y)) obj-qga-vss-dll-obj-y = $(addprefix $(obj)/, $(qga-vss-dll-obj-y))
$(obj-qga-vss-dll-obj-y): QEMU_CXXFLAGS = $(filter-out -Wstrict-prototypes -Wmissing-prototypes -Wnested-externs -Wold-style-declaration -Wold-style-definition -Wredundant-decls -fstack-protector-all -fstack-protector-strong, $(QEMU_CFLAGS)) -Wno-unknown-pragmas -Wno-delete-non-virtual-dtor $(obj-qga-vss-dll-obj-y): QEMU_CXXFLAGS := $(filter-out -fstack-protector-all -fstack-protector-strong, $(QEMU_CXXFLAGS)) -Wno-unknown-pragmas -Wno-delete-non-virtual-dtor
$(obj)/qga-vss.dll: LDFLAGS = -shared -Wl,--add-stdcall-alias,--enable-stdcall-fixup -lole32 -loleaut32 -lshlwapi -luuid -static $(obj)/qga-vss.dll: LDFLAGS = -shared -Wl,--add-stdcall-alias,--enable-stdcall-fixup -lglib-2.0 -lole32 -loleaut32 -lshlwapi -luuid -lintl -lws2_32 -static
$(obj)/qga-vss.dll: $(obj-qga-vss-dll-obj-y) $(SRC_PATH)/$(obj)/qga-vss.def $(obj)/qga-vss.dll: $(obj-qga-vss-dll-obj-y) $(SRC_PATH)/$(obj)/qga-vss.def
$(call quiet-command,$(CXX) -o $@ $(qga-vss-dll-obj-y) $(SRC_PATH)/qga/vss-win32/qga-vss.def $(CXXFLAGS) $(LDFLAGS),"LINK","$(TARGET_DIR)$@") $(call quiet-command,$(CXX) -o $@ $(qga-vss-dll-obj-y) $(SRC_PATH)/qga/vss-win32/qga-vss.def $(CXXFLAGS) $(LDFLAGS),"LINK","$(TARGET_DIR)$@")

View File

@ -225,18 +225,15 @@ static void test_qga_ping(gconstpointer fix)
qobject_unref(ret); qobject_unref(ret);
} }
static void test_qga_invalid_id(gconstpointer fix) static void test_qga_id(gconstpointer fix)
{ {
const TestFixture *fixture = fix; const TestFixture *fixture = fix;
QDict *ret, *error; QDict *ret;
const char *class;
ret = qmp_fd(fixture->fd, "{'execute': 'guest-ping', 'id': 1}"); ret = qmp_fd(fixture->fd, "{'execute': 'guest-ping', 'id': 1}");
g_assert_nonnull(ret); g_assert_nonnull(ret);
qmp_assert_no_error(ret);
error = qdict_get_qdict(ret, "error"); g_assert_cmpint(qdict_get_int(ret, "id"), ==, 1);
class = qdict_get_try_str(error, "class");
g_assert_cmpstr(class, ==, "GenericError");
qobject_unref(ret); qobject_unref(ret);
} }
@ -992,7 +989,7 @@ int main(int argc, char **argv)
g_test_add_data_func("/qga/file-ops", &fix, test_qga_file_ops); g_test_add_data_func("/qga/file-ops", &fix, test_qga_file_ops);
g_test_add_data_func("/qga/file-write-read", &fix, test_qga_file_write_read); g_test_add_data_func("/qga/file-write-read", &fix, test_qga_file_write_read);
g_test_add_data_func("/qga/get-time", &fix, test_qga_get_time); g_test_add_data_func("/qga/get-time", &fix, test_qga_get_time);
g_test_add_data_func("/qga/invalid-id", &fix, test_qga_invalid_id); g_test_add_data_func("/qga/id", &fix, test_qga_id);
g_test_add_data_func("/qga/invalid-oob", &fix, test_qga_invalid_oob); g_test_add_data_func("/qga/invalid-oob", &fix, test_qga_invalid_oob);
g_test_add_data_func("/qga/invalid-cmd", &fix, test_qga_invalid_cmd); g_test_add_data_func("/qga/invalid-cmd", &fix, test_qga_invalid_cmd);
g_test_add_data_func("/qga/invalid-args", &fix, test_qga_invalid_args); g_test_add_data_func("/qga/invalid-args", &fix, test_qga_invalid_args);