summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPali Rohár <pali@kernel.org>2022-03-04 01:07:53 +0100
committerMartin Mares <mj@ucw.cz>2022-11-18 12:06:52 +0100
commitaa5a16ef419d2fe30cb6a17be5194f87b9b7785c (patch)
treea61976fe78c39575266e6d6b07e0521967c98441
parent92bd9d99a1140593996ccaf6aed0f50df27c004d (diff)
downloadpciutils-aa5a16ef419d2fe30cb6a17be5194f87b9b7785c.tar.gz
libpci: Add new windows kldbgdrv.sys implementation
Microsoft Kernel Local Debugging Driver (kldbgdrv.sys) allow access for userspace processes to the PCI config space. It supports access up to 65536 domains and whole 4096 bytes long extended PCIe config space. Driver is signed by Microsoft and is available for both 32-bit and 64-bit systems. Driver is not part of Windows system and has to be installed via WinDbg installation package. Standalone installers for WinDbg 6.12.2.633 version: https://download.microsoft.com/download/A/6/A/A6AC035D-DA3F-4F0C-ADA4-37C8E5D34E3D/setup/WinSDKDebuggingTools_amd64/dbg_amd64.msi https://download.microsoft.com/download/A/6/A/A6AC035D-DA3F-4F0C-ADA4-37C8E5D34E3D/setup/WinSDKDebuggingTools/dbg_x86.msi This kldbgdrv.sys API is used by the !pci command of new WinDbg kernel debugger for displaying PCI config space. API of this driver is available only for processes with Debug privilege and only if system was booted with Debugging option.
-rw-r--r--lib/Makefile5
-rwxr-xr-xlib/configure3
-rw-r--r--lib/init.c6
-rw-r--r--lib/internal.h2
-rw-r--r--lib/pci.h1
-rw-r--r--lib/types.h2
-rw-r--r--lib/win32-kldbg.c796
7 files changed, 813 insertions, 2 deletions
diff --git a/lib/Makefile b/lib/Makefile
index 76ef0d4..43829ef 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -63,6 +63,10 @@ OBJS += emulated
OBJS += win32-cfgmgr32
endif
+ifdef PCI_HAVE_PM_WIN32_KLDBG
+OBJS += win32-kldbg
+endif
+
ifdef PCI_HAVE_PM_WIN32_SYSDBG
OBJS += win32-sysdbg
endif
@@ -135,6 +139,7 @@ filter.o: filter.c $(INCL)
nbsd-libpci.o: nbsd-libpci.c $(INCL)
hurd.o: hurd.c $(INCL)
win32-cfgmgr32.o: win32-cfgmgr32.c $(INCL)
+win32-kldbg.o: win32-kldbg.c $(INCL)
win32-sysdbg.o: win32-sysdbg.c $(INCL)
# MinGW32 toolchain has some required Win32 header files in /ddk subdirectory.
diff --git a/lib/configure b/lib/configure
index aa3cd53..eb4b9bf 100755
--- a/lib/configure
+++ b/lib/configure
@@ -153,9 +153,10 @@ case $sys in
EXEEXT=.exe
;;
cygwin|windows)
- echo_n " win32-cfgmgr32 win32-sysdbg"
+ echo_n " win32-cfgmgr32 win32-kldbg win32-sysdbg"
echo >>$c '#define PCI_HAVE_64BIT_ADDRESS'
echo >>$c '#define PCI_HAVE_PM_WIN32_CFGMGR32'
+ echo >>$c '#define PCI_HAVE_PM_WIN32_KLDBG'
echo >>$c '#define PCI_HAVE_PM_WIN32_SYSDBG'
# Warning: MinGW-w64 (incorrectly) provides cfgmgr32 functions
# also in other import libraries, not only in libcfgmgr32.a.
diff --git a/lib/init.c b/lib/init.c
index c81d90c..5ce260a 100644
--- a/lib/init.c
+++ b/lib/init.c
@@ -81,6 +81,11 @@ static struct pci_methods *pci_methods[PCI_ACCESS_MAX] = {
#else
NULL,
#endif
+#ifdef PCI_HAVE_PM_WIN32_KLDBG
+ &pm_win32_kldbg,
+#else
+ NULL,
+#endif
#ifdef PCI_HAVE_PM_WIN32_SYSDBG
&pm_win32_sysdbg,
#else
@@ -106,6 +111,7 @@ static int probe_sequence[] = {
PCI_ACCESS_SYLIXOS_DEVICE,
PCI_ACCESS_HURD,
PCI_ACCESS_WIN32_CFGMGR32,
+ PCI_ACCESS_WIN32_KLDBG,
PCI_ACCESS_WIN32_SYSDBG,
// Low-level methods poking the hardware directly
PCI_ACCESS_I386_TYPE1,
diff --git a/lib/internal.h b/lib/internal.h
index 6bfcde7..81316dc 100644
--- a/lib/internal.h
+++ b/lib/internal.h
@@ -135,4 +135,4 @@ extern struct pci_methods pm_intel_conf1, pm_intel_conf2, pm_linux_proc,
pm_fbsd_device, pm_aix_device, pm_nbsd_libpci, pm_obsd_device,
pm_dump, pm_linux_sysfs, pm_darwin, pm_sylixos_device, pm_hurd,
pm_mmio_conf1,
- pm_win32_cfgmgr32, pm_win32_sysdbg;
+ pm_win32_cfgmgr32, pm_win32_kldbg, pm_win32_sysdbg;
diff --git a/lib/pci.h b/lib/pci.h
index f6197c4..f198f11 100644
--- a/lib/pci.h
+++ b/lib/pci.h
@@ -44,6 +44,7 @@ enum pci_access_type {
PCI_ACCESS_SYLIXOS_DEVICE, /* SylixOS pci */
PCI_ACCESS_HURD, /* GNU/Hurd */
PCI_ACCESS_WIN32_CFGMGR32, /* Win32 cfgmgr32.dll */
+ PCI_ACCESS_WIN32_KLDBG, /* Win32 kldbgdrv.sys */
PCI_ACCESS_WIN32_SYSDBG, /* Win32 NT SysDbg */
PCI_ACCESS_MMIO_TYPE1, /* MMIO ports, type 1 */
PCI_ACCESS_MAX
diff --git a/lib/types.h b/lib/types.h
index 243997f..ab53f7c 100644
--- a/lib/types.h
+++ b/lib/types.h
@@ -75,7 +75,9 @@ typedef u32 pciaddr_t;
#if defined(__GNUC__) && __GNUC__ > 2
#define PCI_PRINTF(x,y) __attribute__((format(printf, x, y)))
#define PCI_NONRET __attribute((noreturn))
+#define PCI_PACKED __attribute((packed))
#else
#define PCI_PRINTF(x,y)
#define PCI_NONRET
+#define PCI_PACKED
#endif
diff --git a/lib/win32-kldbg.c b/lib/win32-kldbg.c
new file mode 100644
index 0000000..3890b6b
--- /dev/null
+++ b/lib/win32-kldbg.c
@@ -0,0 +1,796 @@
+/*
+ * The PCI Library -- PCI config space access using Kernel Local Debugging Driver
+ *
+ * Copyright (c) 2022 Pali Rohár <pali@kernel.org>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include <windows.h>
+#include <winioctl.h>
+
+#include <stdio.h> /* for sprintf() */
+#include <string.h> /* for memset() and memcpy() */
+
+#include "internal.h"
+#include "i386-io-windows.h"
+
+#ifndef LOAD_LIBRARY_AS_IMAGE_RESOURCE
+#define LOAD_LIBRARY_AS_IMAGE_RESOURCE 0x20
+#endif
+#ifndef LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE
+#define LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE 0x40
+#endif
+
+#ifndef IOCTL_KLDBG
+#define IOCTL_KLDBG CTL_CODE(FILE_DEVICE_UNKNOWN, 0x1, METHOD_NEITHER, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+#endif
+
+#ifndef BUS_DATA_TYPE
+#define BUS_DATA_TYPE LONG
+#endif
+#ifndef PCIConfiguration
+#define PCIConfiguration (BUS_DATA_TYPE)4
+#endif
+
+#ifndef SYSDBG_COMMAND
+#define SYSDBG_COMMAND ULONG
+#endif
+#ifndef SysDbgReadBusData
+#define SysDbgReadBusData (SYSDBG_COMMAND)18
+#endif
+#ifndef SysDbgWriteBusData
+#define SysDbgWriteBusData (SYSDBG_COMMAND)19
+#endif
+
+#ifndef SYSDBG_BUS_DATA
+typedef struct _SYSDBG_BUS_DATA {
+ ULONG Address;
+ PVOID Buffer;
+ ULONG Request;
+ BUS_DATA_TYPE BusDataType;
+ ULONG BusNumber;
+ ULONG SlotNumber;
+} SYSDBG_BUS_DATA, *PSYSDBG_BUS_DATA;
+#define SYSDBG_BUS_DATA SYSDBG_BUS_DATA
+#endif
+
+#ifndef PCI_SEGMENT_BUS_NUMBER
+typedef struct _PCI_SEGMENT_BUS_NUMBER {
+ union {
+ struct {
+ ULONG BusNumber:8;
+ ULONG SegmentNumber:16;
+ ULONG Reserved:8;
+ } bits;
+ ULONG AsULONG;
+ } u;
+} PCI_SEGMENT_BUS_NUMBER, *PPCI_SEGMENT_BUS_NUMBER;
+#define PCI_SEGMENT_BUS_NUMBER PCI_SEGMENT_BUS_NUMBER
+#endif
+
+#ifndef PCI_SLOT_NUMBER
+typedef struct _PCI_SLOT_NUMBER {
+ union {
+ struct {
+ ULONG DeviceNumber:5;
+ ULONG FunctionNumber:3;
+ ULONG Reserved:24;
+ } bits;
+ ULONG AsULONG;
+ } u;
+} PCI_SLOT_NUMBER, *PPCI_SLOT_NUMBER;
+#define PCI_SLOT_NUMBER PCI_SLOT_NUMBER
+#endif
+
+#ifndef KLDBG
+typedef struct _KLDBG {
+ SYSDBG_COMMAND Command;
+ PVOID Buffer;
+ DWORD BufferLength;
+} KLDBG, *PKLDBG;
+#define KLDBG KLDBG
+#endif
+
+static BOOL debug_privilege_enabled;
+static LUID luid_debug_privilege;
+static BOOL revert_only_privilege;
+static HANDLE revert_token;
+
+static HANDLE kldbg_dev = INVALID_HANDLE_VALUE;
+
+static BOOL
+win32_kldbg_pci_bus_data(BOOL WriteBusData, USHORT SegmentNumber, BYTE BusNumber, BYTE DeviceNumber, BYTE FunctionNumber, USHORT Address, PVOID Buffer, ULONG BufferSize, LPDWORD Length);
+
+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 BOOL
+win32_is_32bit_on_64bit_system(void)
+{
+ BOOL (WINAPI *MyIsWow64Process)(HANDLE, PBOOL);
+ HMODULE kernel32;
+ BOOL is_wow64;
+
+ kernel32 = GetModuleHandle(TEXT("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;
+}
+
+static WORD
+win32_get_current_process_machine(void)
+{
+ IMAGE_DOS_HEADER *dos_header;
+ IMAGE_NT_HEADERS *nt_header;
+
+ dos_header = (IMAGE_DOS_HEADER *)GetModuleHandle(NULL);
+ if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
+ return IMAGE_FILE_MACHINE_UNKNOWN;
+
+ nt_header = (IMAGE_NT_HEADERS *)((BYTE *)dos_header + dos_header->e_lfanew);
+ if (nt_header->Signature != IMAGE_NT_SIGNATURE)
+ return IMAGE_FILE_MACHINE_UNKNOWN;
+
+ return nt_header->FileHeader.Machine;
+}
+
+static BOOL
+win32_check_driver(BYTE *driver_data)
+{
+ IMAGE_DOS_HEADER *dos_header;
+ IMAGE_NT_HEADERS *nt_headers;
+ WORD current_machine;
+
+ current_machine = win32_get_current_process_machine();
+ if (current_machine == IMAGE_FILE_MACHINE_UNKNOWN)
+ return FALSE;
+
+ dos_header = (IMAGE_DOS_HEADER *)driver_data;
+ if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
+ return FALSE;
+
+ nt_headers = (IMAGE_NT_HEADERS *)((BYTE *)dos_header + dos_header->e_lfanew);
+ if (nt_headers->Signature != IMAGE_NT_SIGNATURE)
+ return FALSE;
+
+ if (nt_headers->FileHeader.Machine != current_machine)
+ return FALSE;
+
+ if (!(nt_headers->FileHeader.Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE))
+ return FALSE;
+
+#ifndef _WIN64
+ if (!(nt_headers->FileHeader.Characteristics & IMAGE_FILE_32BIT_MACHINE))
+ return FALSE;
+#endif
+
+ /* IMAGE_NT_OPTIONAL_HDR_MAGIC is alias for the header magic used on the target compiler architecture. */
+ if (nt_headers->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)
+ return FALSE;
+
+ if (nt_headers->OptionalHeader.Subsystem != IMAGE_SUBSYSTEM_NATIVE)
+ return FALSE;
+
+ return TRUE;
+}
+
+static int
+win32_kldbg_unpack_driver(struct pci_access *a, void *driver_path)
+{
+ BOOL use_kd_exe = FALSE;
+ HMODULE exe_with_driver = NULL;
+ HRSRC driver_resource_info = NULL;
+ HGLOBAL driver_resource = NULL;
+ BYTE *driver_data = NULL;
+ DWORD driver_size = 0;
+ HANDLE driver_handle = INVALID_HANDLE_VALUE;
+ DWORD written = 0;
+ DWORD error = 0;
+ int ret = 0;
+
+ /* Try to find and open windbg.exe or kd.exe file in PATH. */
+ exe_with_driver = LoadLibraryEx(TEXT("windbg.exe"), NULL, LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE | LOAD_LIBRARY_AS_IMAGE_RESOURCE);
+ if (!exe_with_driver)
+ {
+ use_kd_exe = TRUE;
+ exe_with_driver = LoadLibraryEx(TEXT("kd.exe"), NULL, LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE | LOAD_LIBRARY_AS_IMAGE_RESOURCE);
+ }
+ if (!exe_with_driver)
+ {
+ error = GetLastError();
+ if (error == ERROR_FILE_NOT_FOUND ||
+ error == ERROR_MOD_NOT_FOUND)
+ a->debug("Cannot find windbg.exe or kd.exe file in PATH");
+ else
+ a->debug("Cannot load %s file: %s.", use_kd_exe ? "kd.exe" : "windbg.exe", win32_strerror(error));
+ goto out;
+ }
+
+ /* kldbgdrv.sys is embedded in windbg.exe/kd.exe as a resource with name id 0x7777 and type id 0x4444. */
+ driver_resource_info = FindResource(exe_with_driver, MAKEINTRESOURCE(0x7777), MAKEINTRESOURCE(0x4444));
+ if (!driver_resource_info)
+ {
+ a->debug("Cannot find kldbgdrv.sys resource in %s file: %s.", use_kd_exe ? "kd.exe" : "windbg.exe", win32_strerror(GetLastError()));
+ goto out;
+ }
+
+ driver_resource = LoadResource(exe_with_driver, driver_resource_info);
+ if (!driver_resource)
+ {
+ a->debug("Cannot load kldbgdrv.sys resource from %s file: %s.", use_kd_exe ? "kd.exe" : "windbg.exe", win32_strerror(GetLastError()));
+ goto out;
+ }
+
+ driver_size = SizeofResource(exe_with_driver, driver_resource_info);
+ if (!driver_size)
+ {
+ a->debug("Cannot determinate size of kldbgdrv.sys resource from %s file: %s.", use_kd_exe ? "kd.exe" : "windbg.exe", win32_strerror(GetLastError()));
+ goto out;
+ }
+
+ driver_data = LockResource(driver_resource);
+ if (!driver_data)
+ {
+ a->debug("Cannot load kldbgdrv.sys resouce data from %s file: %s.", use_kd_exe ? "kd.exe" : "windbg.exe", win32_strerror(GetLastError()));
+ goto out;
+ }
+
+ if (!win32_check_driver(driver_data))
+ {
+ a->debug("Cannot use kldbgdrv.sys driver from %s file: Driver is from different architecture.", use_kd_exe ? "kd.exe" : "windbg.exe");
+ goto out;
+ }
+
+ driver_handle = CreateFile(driver_path, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (driver_handle == INVALID_HANDLE_VALUE)
+ {
+ error = GetLastError();
+ if (error != ERROR_FILE_EXISTS)
+ {
+ a->debug("Cannot create kldbgdrv.sys driver file in system32 directory: %s.", win32_strerror(error));
+ goto out;
+ }
+ /* If driver file in system32 directory already exists then treat it as successfull unpack. */
+ ret = 1;
+ goto out;
+ }
+
+ if (!WriteFile(driver_handle, driver_data, driver_size, &written, NULL) ||
+ written != driver_size)
+ {
+ a->debug("Cannot store kldbgdrv.sys driver file to system32 directory: %s.", win32_strerror(GetLastError()));
+ /* On error, delete file from system32 directory to allow another unpack attempt. */
+ CloseHandle(driver_handle);
+ driver_handle = INVALID_HANDLE_VALUE;
+ DeleteFile(driver_path);
+ goto out;
+ }
+
+ a->debug("Driver kldbgdrv.sys was successfully unpacked from %s and stored in system32 directory...", use_kd_exe ? "kd.exe" : "windbg.exe");
+ ret = 1;
+
+out:
+ if (driver_handle != INVALID_HANDLE_VALUE)
+ CloseHandle(driver_handle);
+
+ if (driver_resource)
+ FreeResource(driver_resource);
+
+ if (exe_with_driver)
+ FreeLibrary(exe_with_driver);
+
+ return ret;
+}
+
+static int
+win32_kldbg_register_driver(struct pci_access *a, SC_HANDLE manager, SC_HANDLE *service)
+{
+ UINT (WINAPI *get_system_root_path)(void *buffer, UINT size) = NULL;
+ UINT systemroot_len;
+ void *driver_path;
+ HANDLE driver_handle;
+ HMODULE kernel32;
+
+ /*
+ * COM library dbgeng.dll unpacks kldbg driver to file \\system32\\kldbgdrv.sys
+ * and register this driver with service name kldbgdrv. Implement same behavior.
+ */
+
+ /*
+ * Old Windows versions return path to NT SystemRoot namespace via
+ * GetWindowsDirectory() function. New Windows versions via
+ * GetSystemWindowsDirectory(). GetSystemWindowsDirectory() is not
+ * provided in old Windows versions, so use GetProcAddress() for
+ * compatibility with all Windows versions.
+ */
+
+ kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
+ if (kernel32)
+ get_system_root_path = (void *)GetProcAddress(kernel32, "GetSystemWindowsDirectory"
+#ifdef UNICODE
+ "W"
+#else
+ "A"
+#endif
+ );
+
+ if (!get_system_root_path)
+ get_system_root_path = (void *)&GetWindowsDirectory;
+
+ systemroot_len = get_system_root_path(NULL, 0);
+ if (!systemroot_len)
+ systemroot_len = sizeof(TEXT("C:\\Windows\\"));
+
+ driver_path = pci_malloc(a, systemroot_len + sizeof(TEXT("\\system32\\kldbgdrv.sys")));
+
+ systemroot_len = get_system_root_path(driver_path, systemroot_len + sizeof(TEXT("")));
+ if (!systemroot_len)
+ {
+ systemroot_len = sizeof(TEXT("C:\\Windows\\"));
+ memcpy(driver_path, TEXT("C:\\Windows\\"), systemroot_len);
+ }
+
+ if (((char *)driver_path)[systemroot_len-sizeof(TEXT(""))+1] != '\\')
+ {
+ ((char *)driver_path)[systemroot_len-sizeof(TEXT(""))+1] = '\\';
+ systemroot_len += sizeof(TEXT(""));
+ }
+
+ memcpy((char *)driver_path + systemroot_len, TEXT("system32\\kldbgdrv.sys"), sizeof(TEXT("system32\\kldbgdrv.sys")));
+
+ driver_handle = CreateFile(driver_path, 0, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (driver_handle != INVALID_HANDLE_VALUE)
+ CloseHandle(driver_handle);
+ else if (GetLastError() == ERROR_FILE_NOT_FOUND)
+ {
+ a->debug("Driver kldbgdrv.sys is missing, trying to unpack it from windbg.exe or kd.exe...");
+ if (!win32_kldbg_unpack_driver(a, driver_path))
+ {
+ pci_mfree(driver_path);
+ return 0;
+ }
+ }
+
+ *service = CreateService(manager, TEXT("kldbgdrv"), TEXT("kldbgdrv"), SERVICE_START, SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, driver_path, NULL, NULL, NULL, NULL, NULL);
+ if (!*service)
+ {
+ if (GetLastError() != ERROR_SERVICE_EXISTS)
+ {
+ a->debug("Cannot create kldbgdrv service: %s.", win32_strerror(GetLastError()));
+ pci_mfree(driver_path);
+ return 0;
+ }
+
+ *service = OpenService(manager, TEXT("kldbgdrv"), SERVICE_START);
+ if (!*service)
+ {
+ a->debug("Cannot open kldbgdrv service: %s.", win32_strerror(GetLastError()));
+ pci_mfree(driver_path);
+ return 0;
+ }
+ }
+
+ a->debug("Service kldbgdrv was successfully registered...");
+ pci_mfree(driver_path);
+ return 1;
+}
+
+static int
+win32_kldbg_start_driver(struct pci_access *a)
+{
+ SC_HANDLE manager = NULL;
+ SC_HANDLE service = NULL;
+ DWORD error = 0;
+ int ret = 0;
+
+ manager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE);
+ if (!manager)
+ manager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
+ if (!manager)
+ {
+ a->debug("Cannot open Service Manager: %s.", win32_strerror(GetLastError()));
+ return 0;
+ }
+
+ service = OpenService(manager, TEXT("kldbgdrv"), SERVICE_START);
+ if (!service)
+ {
+ error = GetLastError();
+ if (error != ERROR_SERVICE_DOES_NOT_EXIST)
+ {
+ a->debug("Cannot open kldbgdrv service: %s.", win32_strerror(error));
+ goto out;
+ }
+
+ a->debug("Kernel Local Debugging Driver (kldbgdrv.sys) is not registered, trying to register it...");
+
+ if (win32_is_32bit_on_64bit_system())
+ {
+ /* TODO */
+ a->debug("Registering driver from 32-bit process on 64-bit system is not implemented yet.");
+ goto out;
+ }
+
+ if (!win32_kldbg_register_driver(a, manager, &service))
+ goto out;
+ }
+
+ if (!StartService(service, 0, NULL))
+ {
+ error = GetLastError();
+ if (error != ERROR_SERVICE_ALREADY_RUNNING)
+ {
+ a->debug("Cannot start kldbgdrv service: %s.", win32_strerror(error));
+ goto out;
+ }
+ }
+
+ a->debug("Service kldbgdrv successfully started...");
+ ret = 1;
+
+out:
+ if (service)
+ CloseServiceHandle(service);
+
+ if (manager)
+ CloseServiceHandle(manager);
+
+ return ret;
+}
+
+static int
+win32_kldbg_setup(struct pci_access *a)
+{
+ OSVERSIONINFO version;
+ DWORD ret_len;
+ DWORD error;
+ DWORD id;
+
+ if (kldbg_dev != INVALID_HANDLE_VALUE)
+ return 1;
+
+ /* Check for Windows Vista (NT 6.0). */
+ version.dwOSVersionInfoSize = sizeof(version);
+ if (!GetVersionEx(&version) ||
+ version.dwPlatformId != VER_PLATFORM_WIN32_NT ||
+ version.dwMajorVersion < 6)
+ {
+ a->debug("Accessing PCI config space via Kernel Local Debugging Driver requires Windows Vista or higher version.");
+ return 0;
+ }
+
+ kldbg_dev = CreateFile(TEXT("\\\\.\\kldbgdrv"), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (kldbg_dev == INVALID_HANDLE_VALUE)
+ {
+ error = GetLastError();
+ if (error != ERROR_FILE_NOT_FOUND)
+ {
+ a->debug("Cannot open \"\\\\.\\kldbgdrv\" device: %s.", win32_strerror(error));
+ return 0;
+ }
+
+ a->debug("Kernel Local Debugging Driver (kldbgdrv.sys) is not running, trying to start it...");
+
+ if (!win32_kldbg_start_driver(a))
+ return 0;
+
+ kldbg_dev = CreateFile(TEXT("\\\\.\\kldbgdrv"), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (kldbg_dev == INVALID_HANDLE_VALUE)
+ {
+ error = GetLastError();
+ a->debug("Cannot open \"\\\\.\\kldbgdrv\" device: %s.", win32_strerror(error));
+ return 0;
+ }
+ }
+
+ /*
+ * Try to read PCI id register from PCI device 0000:00:00.0.
+ * If this device does not exist and kldbg API is working then
+ * kldbg returns success with read value 0xffffffff.
+ */
+ if (win32_kldbg_pci_bus_data(FALSE, 0, 0, 0, 0, 0, &id, sizeof(id), &ret_len) && ret_len == sizeof(id))
+ return 1;
+
+ error = GetLastError();
+
+ a->debug("Cannot read PCI config space via Kernel Local Debugging Driver: %s.", win32_strerror(error));
+
+ if (error != ERROR_ACCESS_DENIED)
+ {
+ CloseHandle(kldbg_dev);
+ kldbg_dev = INVALID_HANDLE_VALUE;
+ return 0;
+ }
+
+ a->debug("..Trying again with Debug privilege...");
+
+ if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid_debug_privilege))
+ {
+ a->debug("Debug privilege is not supported.");
+ CloseHandle(kldbg_dev);
+ kldbg_dev = INVALID_HANDLE_VALUE;
+ return 0;
+ }
+
+ if (!enable_privilege(luid_debug_privilege, &revert_token, &revert_only_privilege))
+ {
+ a->debug("Process does not have right to enable Debug privilege.");
+ CloseHandle(kldbg_dev);
+ kldbg_dev = INVALID_HANDLE_VALUE;
+ return 0;
+ }
+
+ if (win32_kldbg_pci_bus_data(FALSE, 0, 0, 0, 0, 0, &id, sizeof(id), &ret_len) && ret_len == sizeof(id))
+ {
+ a->debug("Succeeded.");
+ debug_privilege_enabled = TRUE;
+ return 1;
+ }
+
+ error = GetLastError();
+
+ a->debug("Cannot read PCI config space via Kernel Local Debugging Driver: %s.", win32_strerror(error));
+
+ CloseHandle(kldbg_dev);
+ kldbg_dev = INVALID_HANDLE_VALUE;
+
+ revert_privilege(luid_debug_privilege, revert_token, revert_only_privilege);
+ revert_token = NULL;
+ revert_only_privilege = FALSE;
+ return 0;
+}
+
+static int
+win32_kldbg_detect(struct pci_access *a)
+{
+ if (!win32_kldbg_setup(a))
+ return 0;
+
+ return 1;
+}
+
+static void
+win32_kldbg_init(struct pci_access *a)
+{
+ if (!win32_kldbg_setup(a))
+ {
+ a->debug("\n");
+ a->error("PCI config space via Kernel Local Debugging Driver cannot be accessed.");
+ }
+}
+
+static void
+win32_kldbg_cleanup(struct pci_access *a UNUSED)
+{
+ if (kldbg_dev == INVALID_HANDLE_VALUE)
+ return;
+
+ CloseHandle(kldbg_dev);
+ kldbg_dev = INVALID_HANDLE_VALUE;
+
+ if (debug_privilege_enabled)
+ {
+ revert_privilege(luid_debug_privilege, revert_token, revert_only_privilege);
+ revert_token = NULL;
+ revert_only_privilege = FALSE;
+ debug_privilege_enabled = FALSE;
+ }
+}
+
+struct acpi_mcfg {
+ char signature[4];
+ u32 length;
+ u8 revision;
+ u8 checksum;
+ char oem_id[6];
+ char oem_table_id[8];
+ u32 oem_revision;
+ char asl_compiler_id[4];
+ u32 asl_compiler_revision;
+ u64 reserved;
+ struct {
+ u64 address;
+ u16 pci_segment;
+ u8 start_bus_number;
+ u8 end_bus_number;
+ u32 reserved;
+ } allocations[0];
+} PCI_PACKED;
+
+static void
+win32_kldbg_scan(struct pci_access *a)
+{
+ /*
+ * There is no kldbg API to retrieve list of PCI segments. WinDBG pci plugin
+ * kext.dll loads debug symbols from pci.pdb file for kernel module pci.sys.
+ * Then it reads kernel memory which belongs to PciSegmentList local variable
+ * which is the first entry of struct _PCI_SEGMENT linked list. And then it
+ * iterates all entries in linked list and reads SegmentNumber for each entry.
+ *
+ * This is extremly ugly hack and does not work on systems without installed
+ * kernel debug symbol files.
+ *
+ * Do something less ugly. Retrieve ACPI MCFG table via GetSystemFirmwareTable
+ * and parse all PCI segment numbers from it. ACPI MCFG table contains PCIe
+ * ECAM definitions, so all PCI segment numbers.
+ */
+
+ UINT (*WINAPI MyGetSystemFirmwareTable)(DWORD FirmwareTableProviderSignature, DWORD FirmwareTableID, PVOID pFirmwareTableBuffer, DWORD BufferSize);
+ int i, allocations_count;
+ struct acpi_mcfg *mcfg;
+ HMODULE kernel32;
+ byte *segments;
+ DWORD error;
+ DWORD size;
+
+ /* Always scan PCI segment 0. */
+ pci_generic_scan_domain(a, 0);
+
+ kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
+ if (!kernel32)
+ return;
+
+ /* Function GetSystemFirmwareTable() is available since Windows Vista. */
+ MyGetSystemFirmwareTable = (void *)GetProcAddress(kernel32, "GetSystemFirmwareTable");
+ if (!MyGetSystemFirmwareTable)
+ return;
+
+ /* 0x41435049 = 'ACPI', 0x4746434D = 'MCFG' */
+ size = MyGetSystemFirmwareTable(0x41435049, 0x4746434D, NULL, 0);
+ if (size == 0)
+ {
+ error = GetLastError();
+ if (error == ERROR_INVALID_FUNCTION) /* ACPI is not present, so only PCI segment 0 is available. */
+ return;
+ else if (error == ERROR_NOT_FOUND) /* MCFG table is not present, so only PCI segment 0 is available. */
+ return;
+ a->debug("Cannot retrieve ACPI MCFG table: %s.\n", win32_strerror(error));
+ return;
+ }
+
+ mcfg = pci_malloc(a, size);
+
+ if (MyGetSystemFirmwareTable(0x41435049, 0x4746434D, mcfg, size) != size)
+ {
+ error = GetLastError();
+ a->debug("Cannot retrieve ACPI MCFG table: %s.\n", win32_strerror(error));
+ pci_mfree(mcfg);
+ return;
+ }
+
+ if (size < sizeof(*mcfg) || size < mcfg->length)
+ {
+ a->debug("ACPI MCFG table is broken.\n");
+ pci_mfree(mcfg);
+ return;
+ }
+
+ segments = pci_malloc(a, 0xFFFF/8);
+ memset(segments, 0, 0xFFFF/8);
+
+ /* Scan all MCFG allocations and set available PCI segments into bit field. */
+ allocations_count = (mcfg->length - ((unsigned char *)&mcfg->allocations - (unsigned char *)mcfg)) / sizeof(mcfg->allocations[0]);
+ for (i = 0; i < allocations_count; i++)
+ segments[mcfg->allocations[i].pci_segment / 8] |= 1 << (mcfg->allocations[i].pci_segment % 8);
+
+ /* Skip PCI segment 0 which was already scanned. */
+ for (i = 1; i < 0xFFFF; i++)
+ if (segments[i / 8] & (1 << (i % 8)))
+ pci_generic_scan_domain(a, i);
+
+ pci_mfree(segments);
+ pci_mfree(mcfg);
+}
+
+static BOOL
+win32_kldbg_pci_bus_data(BOOL WriteBusData, USHORT SegmentNumber, BYTE BusNumber, BYTE DeviceNumber, BYTE FunctionNumber, USHORT Address, PVOID Buffer, ULONG BufferSize, LPDWORD Length)
+{
+ KLDBG kldbg_cmd;
+ SYSDBG_BUS_DATA sysdbg_cmd;
+ PCI_SLOT_NUMBER pci_slot;
+ PCI_SEGMENT_BUS_NUMBER pci_seg_bus;
+
+ memset(&pci_slot, 0, sizeof(pci_slot));
+ memset(&sysdbg_cmd, 0, sizeof(sysdbg_cmd));
+ memset(&pci_seg_bus, 0, sizeof(pci_seg_bus));
+
+ sysdbg_cmd.Address = Address;
+ sysdbg_cmd.Buffer = Buffer;
+ sysdbg_cmd.Request = BufferSize;
+ sysdbg_cmd.BusDataType = PCIConfiguration;
+ pci_seg_bus.u.bits.BusNumber = BusNumber;
+ pci_seg_bus.u.bits.SegmentNumber = SegmentNumber;
+ sysdbg_cmd.BusNumber = pci_seg_bus.u.AsULONG;
+ pci_slot.u.bits.DeviceNumber = DeviceNumber;
+ pci_slot.u.bits.FunctionNumber = FunctionNumber;
+ sysdbg_cmd.SlotNumber = pci_slot.u.AsULONG;
+
+ kldbg_cmd.Command = WriteBusData ? SysDbgWriteBusData : SysDbgReadBusData;
+ kldbg_cmd.Buffer = &sysdbg_cmd;
+ kldbg_cmd.BufferLength = sizeof(sysdbg_cmd);
+
+ *Length = 0;
+ return DeviceIoControl(kldbg_dev, IOCTL_KLDBG, &kldbg_cmd, sizeof(kldbg_cmd), &sysdbg_cmd, sizeof(sysdbg_cmd), Length, NULL);
+}
+
+static int
+win32_kldbg_read(struct pci_dev *d, int pos, byte *buf, int len)
+{
+ DWORD ret_len;
+
+ if ((unsigned int)d->domain > 0xffff)
+ return 0;
+
+ if (!win32_kldbg_pci_bus_data(FALSE, d->domain, d->bus, d->dev, d->func, pos, buf, len, &ret_len))
+ return 0;
+
+ if (ret_len != (unsigned int)len)
+ return 0;
+
+ return 1;
+}
+
+static int
+win32_kldbg_write(struct pci_dev *d, int pos, byte *buf, int len)
+{
+ DWORD ret_len;
+
+ if ((unsigned int)d->domain > 0xffff)
+ return 0;
+
+ if (!win32_kldbg_pci_bus_data(TRUE, d->domain, d->bus, d->dev, d->func, pos, buf, len, &ret_len))
+ return 0;
+
+ if (ret_len != (unsigned int)len)
+ return 0;
+
+ return 1;
+}
+
+struct pci_methods pm_win32_kldbg = {
+ "win32-kldbg",
+ "Win32 PCI config space access using Kernel Local Debugging Driver",
+ NULL, /* config */
+ win32_kldbg_detect,
+ win32_kldbg_init,
+ win32_kldbg_cleanup,
+ win32_kldbg_scan,
+ pci_generic_fill_info,
+ win32_kldbg_read,
+ win32_kldbg_write,
+ NULL, /* read_vpd */
+ NULL, /* init_dev */
+ NULL /* cleanup_dev */
+};