From 2ea84cd07ee3fcd8437faba3174d80008fca3982 Mon Sep 17 00:00:00 2001 From: Quantum Date: Wed, 28 Jul 2021 03:09:24 -0400 Subject: [PATCH] [common] ivshmem: use consistent device numbering on Windows We now enumerate all IVSHMEM devices, sort them based on PCI bus, slot, and function numbers, then index from the resulting order. This should be consistent across boots. To help the user identify the correct IVSHMEM device, we also print the list of IVSHMEM devices on startup. --- common/src/platform/windows/ivshmem.c | 93 ++++++++++++++++++++++++--- 1 file changed, 85 insertions(+), 8 deletions(-) diff --git a/common/src/platform/windows/ivshmem.c b/common/src/platform/windows/ivshmem.c index f970ea16..8fb8efc5 100644 --- a/common/src/platform/windows/ivshmem.c +++ b/common/src/platform/windows/ivshmem.c @@ -50,6 +50,26 @@ void ivshmemOptionsInit(void) option_register(options); } +struct IVSHMEMData +{ + SP_DEVINFO_DATA devInfoData; + DWORD64 busAddr; +}; + +static int ivshmemComparator(const void * a_, const void * b_) +{ + const struct IVSHMEMData * a = a_; + const struct IVSHMEMData * b = b_; + + if (a->busAddr < b->busAddr) + return -1; + + if (a->busAddr > b->busAddr) + return 1; + + return 0; +} + bool ivshmemInit(struct IVSHMEM * dev) { assert(dev && !dev->opaque); @@ -57,22 +77,79 @@ bool ivshmemInit(struct IVSHMEM * dev) HANDLE handle; HDEVINFO devInfoSet; PSP_DEVICE_INTERFACE_DETAIL_DATA infData = NULL; + SP_DEVINFO_DATA devInfoData = {0}; SP_DEVICE_INTERFACE_DATA devInterfaceData = {0}; + int deviceAllocated = 1; + int deviceCount = 0; + struct IVSHMEMData * devices = malloc(sizeof(struct IVSHMEMData) * deviceAllocated); - devInfoSet = SetupDiGetClassDevs(NULL, NULL, NULL, DIGCF_PRESENT | DIGCF_ALLCLASSES | DIGCF_DEVICEINTERFACE); + devInfoSet = SetupDiGetClassDevs(&GUID_DEVINTERFACE_IVSHMEM, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + devInfoData.cbSize = sizeof(SP_DEVINFO_DATA); devInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); - const int shmDevice = option_get_int("os", "shmDevice"); - if (SetupDiEnumDeviceInterfaces(devInfoSet, NULL, &GUID_DEVINTERFACE_IVSHMEM, shmDevice, &devInterfaceData) == FALSE) + if (!devices) { - DWORD error = GetLastError(); - if (error == ERROR_NO_MORE_ITEMS) + DEBUG_ERROR("Failed to allocate memory"); + return false; + } + + for (; SetupDiEnumDeviceInfo(devInfoSet, deviceCount, &devInfoData); ++deviceCount) + { + if (deviceCount >= deviceAllocated) { - DEBUG_WINERROR("Unable to enumerate the device, is it attached?", error); - return false; + int newCount = deviceAllocated * 2; + void * new = realloc(devices, newCount * sizeof(struct IVSHMEMData)); + if (!new) + { + DEBUG_ERROR("Failed to allocate memory"); + break; + } + deviceAllocated = newCount; + devices = new; } - DEBUG_WINERROR("SetupDiEnumDeviceInterfaces failed", error); + DWORD bus, addr; + if (!SetupDiGetDeviceRegistryProperty(devInfoSet, &devInfoData, SPDRP_BUSNUMBER, + NULL, (void*) &bus, sizeof(bus), NULL)) + { + DEBUG_WINERROR("Failed to SetupDiGetDeviceRegistryProperty", GetLastError()); + bus = 0xFFFF; + } + + if (!SetupDiGetDeviceRegistryProperty(devInfoSet, &devInfoData, SPDRP_ADDRESS, + NULL, (void*) &addr, sizeof(addr), NULL)) + { + DEBUG_WINERROR("Failed to SetupDiGetDeviceRegistryProperty", GetLastError()); + addr = 0xFFFFFFFF; + } + + devices[deviceCount].busAddr = (((DWORD64) bus) << 32) | addr; + memcpy(&devices[deviceCount].devInfoData, &devInfoData, sizeof(SP_DEVINFO_DATA)); + } + + if (GetLastError() != ERROR_NO_MORE_ITEMS) + { + DEBUG_WINERROR("SetupDiEnumDeviceInfo failed", GetLastError()); + return false; + } + + const int shmDevice = option_get_int("os", "shmDevice"); + qsort(devices, deviceCount, sizeof(struct IVSHMEMData), ivshmemComparator); + + for (int i = 0; i < deviceCount; ++i) + { + DWORD bus = devices[i].busAddr >> 32; + DWORD addr = devices[i].busAddr & 0xFFFFFFFF; + DEBUG_INFO("IVSHMEM %d%c on bus 0x%lx, device 0x%lx, function 0x%lx", i, + i == shmDevice ? '*' : ' ', bus, addr >> 16, addr & 0xFFFF); + } + + memcpy(&devInfoData, &devices[shmDevice].devInfoData, sizeof(SP_DEVINFO_DATA)); + free(devices); + + if (SetupDiEnumDeviceInterfaces(devInfoSet, &devInfoData, &GUID_DEVINTERFACE_IVSHMEM, 0, &devInterfaceData) == FALSE) + { + DEBUG_WINERROR("SetupDiEnumDeviceInterfaces failed", GetLastError()); return false; }