*
* Copyright (c) 2021 Pali Rohár <pali@kernel.org>
*
- * Can be freely distributed and used under the terms of the GNU GPL.
+ * Can be freely distributed and used under the terms of the GNU GPL v2+.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <windows.h>
#include <wchar.h> /* for wcslen(), wcscpy() */
#include "internal.h"
+#include "win32-helpers.h"
/* Unfortunately MinGW32 toolchain does not provide these cfgmgr32 constants. */
* cfgmgr32.dll uses custom non-Win32 error numbers which are unsupported by
* Win32 APIs like GetLastError() and FormatMessage() functions.
*
- * Windows 7 instroduced new cfgmgr32.dll function CM_MapCrToWin32Err() for
+ * Windows 7 introduced new cfgmgr32.dll function CM_MapCrToWin32Err() for
* translating mapping CR_* errors to Win32 errors but most error codes are
* not mapped. So this function is unusable.
*
return cr_errors[cr_error_id];
}
-static const char *
-win32_strerror(DWORD win32_error_id)
-{
- /*
- * Use static buffer which is large enough.
- * Hopefully no Win32 API error message string is longer than 4 kB.
- */
- static char buffer[4096];
- DWORD len;
-
- len = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, win32_error_id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buffer, sizeof(buffer), NULL);
-
- /* FormatMessage() automatically appends ".\r\n" to the error message. */
- if (len && buffer[len-1] == '\n')
- buffer[--len] = '\0';
- if (len && buffer[len-1] == '\r')
- buffer[--len] = '\0';
- if (len && buffer[len-1] == '.')
- buffer[--len] = '\0';
-
- if (!len)
- sprintf(buffer, "Unknown Win32 error %lu", win32_error_id);
-
- return buffer;
-}
-
static int
fmt_validate(const char *s, int len, const char *fmt)
{
return 1;
}
-static BOOL
-is_non_nt_system(void)
-{
- OSVERSIONINFOA version;
- version.dwOSVersionInfoSize = sizeof(version);
- return GetVersionExA(&version) && version.dwPlatformId < VER_PLATFORM_WIN32_NT;
-}
-
-static BOOL
-is_32bit_on_win8_64bit_system(void)
-{
-#ifdef _WIN64
- return FALSE;
-#else
- BOOL (WINAPI *MyIsWow64Process)(HANDLE, PBOOL);
- OSVERSIONINFOA version;
- HMODULE kernel32;
- BOOL is_wow64;
-
- /* Check for Windows 8 (NT 6.2). */
- version.dwOSVersionInfoSize = sizeof(version);
- if (!GetVersionExA(&version) ||
- version.dwPlatformId != VER_PLATFORM_WIN32_NT ||
- version.dwMajorVersion < 6 ||
- (version.dwMajorVersion == 6 && version.dwMinorVersion < 2))
- return FALSE;
-
- /*
- * Check for 64-bit system via IsWow64Process() function exported
- * from 32-bit kernel32.dll library available on the 64-bit systems.
- * Resolve pointer to this function at runtime as this code path is
- * primary running on 32-bit systems where are not available 64-bit
- * functions.
- */
-
- kernel32 = GetModuleHandleA("kernel32.dll");
- if (!kernel32)
- return FALSE;
-
- MyIsWow64Process = (void *)GetProcAddress(kernel32, "IsWow64Process");
- if (!MyIsWow64Process)
- return FALSE;
-
- if (!MyIsWow64Process(GetCurrentProcess(), &is_wow64))
- return FALSE;
-
- return is_wow64;
-#endif
-}
-
static LPWSTR
get_device_service_name(struct pci_access *a, DEVINST devinst, DEVINSTID_A devinst_id, BOOL *supported)
{
SERVICE_STATUS service_status;
SC_HANDLE service = NULL;
char *driver_path = NULL;
+ int trim_system32 = 0;
UINT systemroot_len;
int driver_path_len;
+ UINT system32_len;
HMODULE kernel32;
+ WCHAR *trim_ptr;
DWORD error;
service = OpenServiceW(manager, service_name, SERVICE_QUERY_CONFIG | SERVICE_QUERY_STATUS);
*/
/*
- * Old Windows versions return path to NT SystemRoot namespace via
- * GetWindowsDirectoryW() function. New Windows versions via
- * GetSystemWindowsDirectoryW(). GetSystemWindowsDirectoryW() is not
- * provided in old Windows versions, so use GetProcAddress() for
- * compatibility with all Windows versions.
+ * GetSystemWindowsDirectoryW() returns path to NT SystemRoot namespace.
+ * Alternativelly path to NT SystemRoot namespace can be constructed by
+ * GetSystemDirectoryW() by trimming "\\system32" from the end of path.
+ * GetSystemWindowsDirectoryW() is not provided in old Windows versions,
+ * so use GetProcAddress() for compatibility with all Windows versions.
*/
kernel32 = GetModuleHandleW(L"kernel32.dll");
if (kernel32)
get_system_root_path = (void *)GetProcAddress(kernel32, "GetSystemWindowsDirectoryW");
- if (!get_system_root_path)
- get_system_root_path = &GetWindowsDirectoryW;
-
- systemroot_len = get_system_root_path(NULL, 0);
+ else
+ {
+ get_system_root_path = &GetSystemDirectoryW;
+ trim_system32 = 1;
+ }
if (!service_config->lpBinaryPathName || !service_config->lpBinaryPathName[0])
{
- /* No ImagePath is specified, NT kernel assumes implicit kernel driver path by service name. */
- service_image_path = pci_malloc(a, sizeof(WCHAR) * (systemroot_len + sizeof("\\System32\\drivers\\")-1 + wcslen(service_name) + sizeof(".sys")-1 + 1));
- systemroot_len = get_system_root_path(service_image_path, systemroot_len+1);
- if (systemroot_len && service_image_path[systemroot_len-1] != L'\\')
- service_image_path[systemroot_len++] = L'\\';
- wcscpy(service_image_path + systemroot_len, L"System32\\drivers\\");
- wcscpy(service_image_path + systemroot_len + sizeof("System32\\drivers\\")-1, service_name);
- wcscpy(service_image_path + systemroot_len + sizeof("System32\\drivers\\")-1 + wcslen(service_name), L".sys");
+ /* No ImagePath is specified, NT kernel assumes implicit kernel driver path by service name, which is relative to "\\system32\\drivers". */
+ /* GetSystemDirectoryW() returns path to "\\system32" directory on all Windows versions. */
+ system32_len = GetSystemDirectoryW(NULL, 0); /* Returns number of WCHARs plus 1 for nul-term. */
+ service_image_path = pci_malloc(a, sizeof(WCHAR) * (system32_len + sizeof("\\drivers\\")-1 + wcslen(service_name) + sizeof(".sys")-1));
+ system32_len = GetSystemDirectoryW(service_image_path, system32_len); /* Now it returns number of WCHARs without nul-term. */
+ if (system32_len && service_image_path[system32_len-1] != L'\\')
+ service_image_path[system32_len++] = L'\\';
+ wcscpy(service_image_path + system32_len, L"drivers\\");
+ wcscpy(service_image_path + system32_len + sizeof("drivers\\")-1, service_name);
+ wcscpy(service_image_path + system32_len + sizeof("drivers\\")-1 + wcslen(service_name), L".sys");
}
else if (wcsncmp(service_config->lpBinaryPathName, L"\\SystemRoot\\", sizeof("\\SystemRoot\\")-1) == 0)
{
- /* ImagePath is in NT SystemRoot namespace, convert to Win32 path via GetSystemWindowsDirectoryW()/GetWindowsDirectoryW(). */
+ /* ImagePath is in NT SystemRoot namespace, convert to Win32 path via GetSystemWindowsDirectoryW()/GetSystemDirectoryW(). */
+ systemroot_len = get_system_root_path(NULL, 0); /* Returns number of WCHARs plus 1 for nul-term. */
service_image_path = pci_malloc(a, sizeof(WCHAR) * (systemroot_len + wcslen(service_config->lpBinaryPathName) - (sizeof("\\SystemRoot")-1)));
- systemroot_len = get_system_root_path(service_image_path, systemroot_len+1);
+ systemroot_len = get_system_root_path(service_image_path, systemroot_len); /* Now it returns number of WCHARs without nul-term. */
+ if (trim_system32 && systemroot_len && (trim_ptr = wcsrchr(service_image_path, L'\\')) != NULL)
+ systemroot_len = trim_ptr - service_image_path;
if (systemroot_len && service_image_path[systemroot_len-1] != L'\\')
service_image_path[systemroot_len++] = L'\\';
wcscpy(service_image_path + systemroot_len, service_config->lpBinaryPathName + sizeof("\\SystemRoot\\")-1);
}
- else if (wcsncmp(service_config->lpBinaryPathName, L"\\??\\UNC\\", sizeof("\\??\\UNC\\")-1) == 0)
+ else if (wcsncmp(service_config->lpBinaryPathName, L"\\??\\UNC\\", sizeof("\\??\\UNC\\")-1) == 0 ||
+ wcsncmp(service_config->lpBinaryPathName, L"\\??\\\\UNC\\", sizeof("\\??\\\\UNC\\")-1) == 0)
{
/* ImagePath is in NT UNC namespace, convert to Win32 UNC path via "\\\\" prefix. */
service_image_path = pci_malloc(a, sizeof(WCHAR) * (sizeof("\\\\") + wcslen(service_config->lpBinaryPathName) - (sizeof("\\??\\UNC\\")-1)));
+ /* Namespace separator may be single or double backslash. */
+ driver_path_len = sizeof("\\??\\")-1;
+ if (service_config->lpBinaryPathName[driver_path_len] == L'\\')
+ driver_path_len++;
+ driver_path_len += sizeof("UNC\\")-1;
wcscpy(service_image_path, L"\\\\");
- wcscpy(service_image_path + sizeof("\\\\")-1, service_config->lpBinaryPathName + sizeof("\\??\\UNC\\")-1);
+ wcscpy(service_image_path + sizeof("\\\\")-1, service_config->lpBinaryPathName + driver_path_len);
}
else if (wcsncmp(service_config->lpBinaryPathName, L"\\??\\", sizeof("\\??\\")-1) == 0)
{
/* ImagePath is in NT Global?? namespace, root of the Win32 file namespace, so just remove "\\??\\" prefix to get Win32 path. */
service_image_path = pci_malloc(a, sizeof(WCHAR) * (wcslen(service_config->lpBinaryPathName) - (sizeof("\\??\\")-1)));
- wcscpy(service_image_path, service_config->lpBinaryPathName + sizeof("\\??\\")-1);
+ /* Namespace separator may be single or double backslash. */
+ driver_path_len = sizeof("\\??\\")-1;
+ if (service_config->lpBinaryPathName[driver_path_len] == L'\\')
+ driver_path_len++;
+ wcscpy(service_image_path, service_config->lpBinaryPathName + driver_path_len);
}
else if (service_config->lpBinaryPathName[0] != L'\\')
{
- /* ImagePath is relative to the NT SystemRoot namespace, convert to Win32 path via GetSystemWindowsDirectoryW()/GetWindowsDirectoryW(). */
- service_image_path = pci_malloc(a, sizeof(WCHAR) * (systemroot_len + sizeof("\\") + wcslen(service_config->lpBinaryPathName)));
- systemroot_len = get_system_root_path(service_image_path, systemroot_len+1);
+ /* ImagePath is relative to the NT SystemRoot namespace, convert to Win32 path via GetSystemWindowsDirectoryW()/GetSystemDirectoryW(). */
+ systemroot_len = get_system_root_path(NULL, 0); /* Returns number of WCHARs plus 1 for nul-term. */
+ service_image_path = pci_malloc(a, sizeof(WCHAR) * (systemroot_len + sizeof("\\")-1 + wcslen(service_config->lpBinaryPathName)));
+ systemroot_len = get_system_root_path(service_image_path, systemroot_len); /* Now it returns number of WCHARs without nul-term. */
+ if (trim_system32 && systemroot_len && (trim_ptr = wcsrchr(service_image_path, L'\\')) != NULL)
+ systemroot_len = trim_ptr - service_image_path;
if (systemroot_len && service_image_path[systemroot_len-1] != L'\\')
service_image_path[systemroot_len++] = L'\\';
wcscpy(service_image_path + systemroot_len, service_config->lpBinaryPathName);
{
error = GetLastError();
if (error == 0)
- a->warning("Cannot read driver %s key for PCI device %s: DevLoader key is stored as unknown type 0x%lx.", subname, devinst_id, unkn_reg_type);
+ a->warning("Cannot read driver %s key for PCI device %s: %s key is stored as unknown type 0x%lx.", subname, devinst_id, subname, unkn_reg_type);
else if (error != ERROR_FILE_NOT_FOUND)
a->warning("Cannot read driver %s key for PCI device %s: %s.", subname, devinst_id, win32_strerror(error));
else if (strcmp(subname, "minivdd") == 0)
LPWSTR service_name = NULL;
ULONG devinst_id_len = 0;
char *driver_path = NULL;
- DEVINST devinst = (DEVINST)d->aux;
+ DEVINST devinst = (DEVINST)d->backend_data;
ULONG problem = 0;
ULONG status = 0;
HKEY key = NULL;
CloseServiceHandle(manager);
}
+static const char *
+res_id_to_str(RESOURCEID res_id)
+{
+ static char hex_res_id[sizeof("0xffffffff")];
+
+ if (res_id == ResType_IO)
+ return "IO";
+ else if (res_id == ResType_Mem)
+ return "MEM";
+ else if (res_id == ResType_IRQ)
+ return "IRQ";
+
+ sprintf(hex_res_id, "0x%lx", res_id);
+ return hex_res_id;
+}
+
static void
fill_resources(struct pci_dev *d, DEVINST devinst, DEVINSTID_A devinst_id)
{
* application using the hardware resource APIs. For example: An AMD64
* application for AMD64 systems.
*/
- if (cr == CR_CALL_NOT_IMPLEMENTED && is_32bit_on_win8_64bit_system())
+ if (cr == CR_CALL_NOT_IMPLEMENTED && win32_is_32bit_on_win8_64bit_system())
{
static BOOL warn_once = FALSE;
if (!warn_once)
}
bar_res_count = 0;
- non_nt_system = is_non_nt_system();
+ non_nt_system = win32_is_non_nt_system();
is_bar_res = TRUE;
if (non_nt_system)
prev_res_des = res_des;
+ /* Skip other resources early */
+ if (res_id != ResType_IO && res_id != ResType_Mem && res_id != ResType_IRQ)
+ continue;
+
cr = CM_Get_Res_Des_Data_Size(&res_des_data_size, res_des, 0);
if (cr != CR_SUCCESS)
{
- a->warning("Cannot retrieve resource data of PCI device %s: %s.", devinst_id, cr_strerror(cr));
+ a->warning("Cannot retrieve %s resource data of PCI device %s: %s.", res_id_to_str(res_id), devinst_id, cr_strerror(cr));
continue;
}
if (!res_des_data_size)
{
- a->warning("Cannot retrieve resource data of PCI device %s: %s.", devinst_id, "Empty data");
+ a->warning("Cannot retrieve %s resource data of PCI device %s: %s.", res_id_to_str(res_id), devinst_id, "Empty data");
continue;
}
cr = CM_Get_Res_Des_Data(res_des, res_des_data, res_des_data_size, 0);
if (cr != CR_SUCCESS)
{
- a->warning("Cannot retrieve resource data of PCI device %s: %s.", devinst_id, cr_strerror(cr));
+ a->warning("Cannot retrieve %s resource data of PCI device %s: %s.", res_id_to_str(res_id), devinst_id, cr_strerror(cr));
pci_mfree(res_des_data);
continue;
}
d = pci_get_dev(a, domain, bus, dev, func);
pci_link_dev(a, d);
- d->no_config_access = 1;
- d->aux = (void *)devinst;
+ if (!d->access->backend_data)
+ d->no_config_access = 1;
+ d->backend_data = (void *)devinst;
/* Parse device id part of devinst id and fill details into pci_dev. */
if (!a->buscentric)
fill_resources(d, devinst, devinst_id);
/*
- * Set parent field to cfgmgr32 parent devinst handle and aux field to current
+ * Set parent field to cfgmgr32 parent devinst handle and backend_data field to current
* devinst handle. At later stage in win32_cfgmgr32_scan() when all pci_dev
* devices are linked, change every devinst handle by pci_dev.
*/
for (d1 = a->devices; d1; d1 = d1->next)
{
for (d2 = a->devices; d2; d2 = d2->next)
- if ((DEVINST)d1->parent == (DEVINST)d2->aux)
+ if ((DEVINST)d1->parent == (DEVINST)d2->backend_data)
break;
d1->parent = d2;
if (d1->parent)
}
}
- /* devinst stored in ->aux is not needed anymore, clear it. */
+ /* devinst stored in ->backend_data is not needed anymore, clear it. */
for (d = a->devices; d; d = d->next)
- d->aux = NULL;
+ d->backend_data = NULL;
pci_mfree(devinst_id_list);
}
+static void
+win32_cfgmgr32_config(struct pci_access *a)
+{
+ pci_define_param(a, "win32.cfgmethod", "auto", "PCI config space access method");
+}
+
static int
win32_cfgmgr32_detect(struct pci_access *a)
{
}
static void
-win32_cfgmgr32_fill_info(struct pci_dev *d UNUSED, unsigned int flags UNUSED)
+win32_cfgmgr32_fill_info(struct pci_dev *d, unsigned int flags)
{
/*
- * All available flags were filled by win32_cfgmgr32_scan()
- * and reading config space is not supported via cfgmgr32.
+ * All available flags were filled by win32_cfgmgr32_scan().
+ * Filling more flags is possible only from config space.
*/
+ if (!d->access->backend_data)
+ return;
+
+ pci_generic_fill_info(d, flags);
}
static int
-win32_cfgmgr32_write(struct pci_dev *d UNUSED, int pos UNUSED, byte *buf UNUSED, int len UNUSED)
+win32_cfgmgr32_read(struct pci_dev *d, int pos, byte *buf, int len)
+{
+ struct pci_access *a = d->access;
+ struct pci_access *acfg = a->backend_data;
+ struct pci_dev *dcfg = d->backend_data;
+
+ if (!acfg)
+ return pci_emulated_read(d, pos, buf, len);
+
+ if (!dcfg)
+ d->backend_data = dcfg = pci_get_dev(acfg, d->domain, d->bus, d->dev, d->func);
+
+ return pci_read_block(dcfg, pos, buf, len);
+}
+
+static int
+win32_cfgmgr32_write(struct pci_dev *d, int pos, byte *buf, int len)
+{
+ struct pci_access *a = d->access;
+ struct pci_access *acfg = a->backend_data;
+ struct pci_dev *dcfg = d->backend_data;
+
+ if (!acfg)
+ return 0;
+
+ if (!dcfg)
+ d->backend_data = dcfg = pci_get_dev(acfg, d->domain, d->bus, d->dev, d->func);
+
+ return pci_write_block(dcfg, pos, buf, len);
+}
+
+static void
+win32_cfgmgr32_cleanup_dev(struct pci_dev *d)
{
- /* Writing to config space is not supported via cfgmgr32. */
- return 0;
+ struct pci_dev *dcfg = d->backend_data;
+
+ if (dcfg)
+ pci_free_dev(dcfg);
}
static void
-win32_cfgmgr32_init(struct pci_access *a UNUSED)
+win32_cfgmgr32_init(struct pci_access *a)
{
+ char *cfgmethod = pci_get_param(a, "win32.cfgmethod");
+ struct pci_access *acfg;
+
+ if (strcmp(cfgmethod, "") == 0 ||
+ strcmp(cfgmethod, "auto") == 0)
+ {
+ acfg = pci_clone_access(a);
+ acfg->method = PCI_ACCESS_AUTO;
+ }
+ else if (strcmp(cfgmethod, "none") == 0 ||
+ strcmp(cfgmethod, "win32-cfgmgr32") == 0)
+ {
+ if (a->writeable)
+ a->error("Write access requested but option win32.cfgmethod was not set.");
+ return;
+ }
+ else
+ {
+ int m = pci_lookup_method(cfgmethod);
+ if (m < 0)
+ a->error("Option win32.cfgmethod is set to an unknown access method \"%s\".", cfgmethod);
+ acfg = pci_clone_access(a);
+ acfg->method = m;
+ }
+
+ a->debug("Loading config space access method...\n");
+ if (!pci_init_internal(acfg, PCI_ACCESS_WIN32_CFGMGR32))
+ {
+ pci_cleanup(acfg);
+ a->debug("Cannot find any working config space access method.\n");
+ if (a->writeable)
+ a->error("Write access requested but no usable access method found.");
+ return;
+ }
+
+ a->backend_data = acfg;
}
static void
-win32_cfgmgr32_cleanup(struct pci_access *a UNUSED)
+win32_cfgmgr32_cleanup(struct pci_access *a)
{
+ struct pci_access *acfg = a->backend_data;
+
+ if (acfg)
+ pci_cleanup(acfg);
}
struct pci_methods pm_win32_cfgmgr32 = {
"win32-cfgmgr32",
"Win32 device listing via Configuration Manager",
- NULL, /* config */
+ win32_cfgmgr32_config,
win32_cfgmgr32_detect,
win32_cfgmgr32_init,
win32_cfgmgr32_cleanup,
win32_cfgmgr32_scan,
win32_cfgmgr32_fill_info,
- pci_emulated_read, /* Reading of config space is not supported via cfgmgr32. */
+ win32_cfgmgr32_read,
win32_cfgmgr32_write,
NULL, /* read_vpd */
NULL, /* init_dev */
- NULL, /* cleanup_dev */
+ win32_cfgmgr32_cleanup_dev,
};