diff --git a/configure b/configure index 62fa05d825..c89b1eab9b 100755 --- a/configure +++ b/configure @@ -3836,6 +3836,7 @@ int main(void) { EOF if compile_prog "" "" ; then guest_agent_ntddscsi=yes + libs_qga="-lsetupapi $libs_qga" fi fi diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 2bcc4a5daf..a7822d5ff7 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -23,6 +23,8 @@ #ifdef CONFIG_QGA_NTDDSCSI #include #include +#include +#include #endif #include "qga/guest-agent-core.h" #include "qga/vss-win32.h" @@ -424,9 +426,102 @@ static GuestDiskBusType find_bus_type(STORAGE_BUS_TYPE bus) return win2qemu[(int)bus]; } +DEFINE_GUID(GUID_DEVINTERFACE_VOLUME, + 0x53f5630dL, 0xb6bf, 0x11d0, 0x94, 0xf2, + 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); + static GuestPCIAddress *get_pci_info(char *guid, Error **errp) { - return NULL; + HDEVINFO dev_info; + SP_DEVINFO_DATA dev_info_data; + DWORD size = 0; + int i; + char dev_name[MAX_PATH]; + char *buffer = NULL; + GuestPCIAddress *pci = NULL; + char *name = g_strdup(&guid[4]); + + 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_VOLUME, 0, 0, + DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + if (dev_info == INVALID_HANDLE_VALUE) { + error_setg_win32(errp, GetLastError(), "failed to get devices tree"); + goto out; + } + + dev_info_data.cbSize = sizeof(SP_DEVINFO_DATA); + for (i = 0; SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data); i++) { + DWORD addr, bus, slot, func, dev, data, size2; + while (!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data, + SPDRP_PHYSICAL_DEVICE_OBJECT_NAME, + &data, (PBYTE)buffer, size, + &size2)) { + size = MAX(size, size2); + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + g_free(buffer); + /* Double the size to avoid problems on + * W2k MBCS systems per KB 888609. + * https://support.microsoft.com/en-us/kb/259695 */ + buffer = g_malloc(size * 2); + } else { + error_setg_win32(errp, GetLastError(), + "failed to get device name"); + goto out; + } + } + + if (g_strcmp0(buffer, dev_name)) { + continue; + } + + /* There is no need to allocate buffer in the next functions. The size + * is known and ULONG according to + * https://support.microsoft.com/en-us/kb/253232 + * https://msdn.microsoft.com/en-us/library/windows/hardware/ff543095(v=vs.85).aspx + */ + if (!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data, + SPDRP_BUSNUMBER, &data, (PBYTE)&bus, size, NULL)) { + break; + } + + /* The function retrieves the device's address. This value will be + * transformed into device function and number */ + if (!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data, + SPDRP_ADDRESS, &data, (PBYTE)&addr, size, NULL)) { + break; + } + + /* This call returns UINumber of DEVICE_CAPABILITIES structure. + * This number is typically a user-perceived slot number. */ + if (!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data, + SPDRP_UI_NUMBER, &data, (PBYTE)&slot, size, NULL)) { + break; + } + + /* SetupApi gives us the same information as driver with + * IoGetDeviceProperty. According to Microsoft + * https://support.microsoft.com/en-us/kb/253232 + * FunctionNumber = (USHORT)((propertyAddress) & 0x0000FFFF); + * DeviceNumber = (USHORT)(((propertyAddress) >> 16) & 0x0000FFFF); + * SPDRP_ADDRESS is propertyAddress, so we do the same.*/ + + func = addr & 0x0000FFFF; + dev = (addr >> 16) & 0x0000FFFF; + pci = g_malloc0(sizeof(*pci)); + pci->domain = dev; + pci->slot = slot; + pci->function = func; + pci->bus = bus; + break; + } +out: + g_free(buffer); + g_free(name); + return pci; } static int get_disk_bus_type(HANDLE vol_h, Error **errp)