2 * The PCI Library -- PCI config space access using Kernel Local Debugging Driver
4 * Copyright (c) 2022 Pali Rohár <pali@kernel.org>
6 * Can be freely distributed and used under the terms of the GNU GPL v2+.
8 * SPDX-License-Identifier: GPL-2.0-or-later
14 #include <stdio.h> /* for sprintf() */
15 #include <string.h> /* for memset() and memcpy() */
18 #include "win32-helpers.h"
20 #ifndef ERROR_NOT_FOUND
21 #define ERROR_NOT_FOUND 1168
24 #ifndef LOAD_LIBRARY_AS_IMAGE_RESOURCE
25 #define LOAD_LIBRARY_AS_IMAGE_RESOURCE 0x20
27 #ifndef LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE
28 #define LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE 0x40
32 #define IOCTL_KLDBG CTL_CODE(FILE_DEVICE_UNKNOWN, 0x1, METHOD_NEITHER, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
36 #define BUS_DATA_TYPE LONG
38 #ifndef PCIConfiguration
39 #define PCIConfiguration (BUS_DATA_TYPE)4
42 #ifndef SYSDBG_COMMAND
43 #define SYSDBG_COMMAND ULONG
45 #ifndef SysDbgReadBusData
46 #define SysDbgReadBusData (SYSDBG_COMMAND)18
48 #ifndef SysDbgWriteBusData
49 #define SysDbgWriteBusData (SYSDBG_COMMAND)19
52 #ifndef SYSDBG_BUS_DATA
53 typedef struct _SYSDBG_BUS_DATA {
57 BUS_DATA_TYPE BusDataType;
60 } SYSDBG_BUS_DATA, *PSYSDBG_BUS_DATA;
61 #define SYSDBG_BUS_DATA SYSDBG_BUS_DATA
64 #ifndef PCI_SEGMENT_BUS_NUMBER
65 typedef struct _PCI_SEGMENT_BUS_NUMBER {
69 ULONG SegmentNumber:16;
74 } PCI_SEGMENT_BUS_NUMBER, *PPCI_SEGMENT_BUS_NUMBER;
75 #define PCI_SEGMENT_BUS_NUMBER PCI_SEGMENT_BUS_NUMBER
78 #ifndef PCI_SLOT_NUMBER
79 typedef struct _PCI_SLOT_NUMBER {
83 ULONG FunctionNumber:3;
88 } PCI_SLOT_NUMBER, *PPCI_SLOT_NUMBER;
89 #define PCI_SLOT_NUMBER PCI_SLOT_NUMBER
93 typedef struct _KLDBG {
94 SYSDBG_COMMAND Command;
101 static BOOL debug_privilege_enabled;
102 static LUID luid_debug_privilege;
103 static BOOL revert_only_privilege;
104 static HANDLE revert_token;
106 static HANDLE kldbg_dev = INVALID_HANDLE_VALUE;
109 win32_kldbg_pci_bus_data(BOOL WriteBusData, USHORT SegmentNumber, BYTE BusNumber, BYTE DeviceNumber, BYTE FunctionNumber, USHORT Address, PVOID Buffer, ULONG BufferSize, LPDWORD Length);
112 win32_get_current_process_machine(void)
114 IMAGE_DOS_HEADER *dos_header;
115 IMAGE_NT_HEADERS *nt_header;
117 dos_header = (IMAGE_DOS_HEADER *)GetModuleHandle(NULL);
118 if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
119 return IMAGE_FILE_MACHINE_UNKNOWN;
121 nt_header = (IMAGE_NT_HEADERS *)((BYTE *)dos_header + dos_header->e_lfanew);
122 if (nt_header->Signature != IMAGE_NT_SIGNATURE)
123 return IMAGE_FILE_MACHINE_UNKNOWN;
125 return nt_header->FileHeader.Machine;
129 win32_check_driver(BYTE *driver_data)
131 IMAGE_DOS_HEADER *dos_header;
132 IMAGE_NT_HEADERS *nt_headers;
133 WORD current_machine;
135 current_machine = win32_get_current_process_machine();
136 if (current_machine == IMAGE_FILE_MACHINE_UNKNOWN)
139 dos_header = (IMAGE_DOS_HEADER *)driver_data;
140 if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
143 nt_headers = (IMAGE_NT_HEADERS *)((BYTE *)dos_header + dos_header->e_lfanew);
144 if (nt_headers->Signature != IMAGE_NT_SIGNATURE)
147 if (nt_headers->FileHeader.Machine != current_machine)
150 if (!(nt_headers->FileHeader.Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE))
154 if (!(nt_headers->FileHeader.Characteristics & IMAGE_FILE_32BIT_MACHINE))
158 /* IMAGE_NT_OPTIONAL_HDR_MAGIC is alias for the header magic used on the target compiler architecture. */
159 if (nt_headers->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)
162 if (nt_headers->OptionalHeader.Subsystem != IMAGE_SUBSYSTEM_NATIVE)
169 win32_kldbg_unpack_driver(struct pci_access *a, LPTSTR driver_path)
171 BOOL use_kd_exe = FALSE;
172 HMODULE exe_with_driver = NULL;
173 HRSRC driver_resource_info = NULL;
174 HGLOBAL driver_resource = NULL;
175 BYTE *driver_data = NULL;
176 DWORD driver_size = 0;
177 HANDLE driver_handle = INVALID_HANDLE_VALUE;
182 /* Try to find and open windbg.exe or kd.exe file in PATH. */
183 exe_with_driver = LoadLibraryEx(TEXT("windbg.exe"), NULL, LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE | LOAD_LIBRARY_AS_IMAGE_RESOURCE);
184 if (!exe_with_driver)
187 exe_with_driver = LoadLibraryEx(TEXT("kd.exe"), NULL, LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE | LOAD_LIBRARY_AS_IMAGE_RESOURCE);
189 if (!exe_with_driver)
191 error = GetLastError();
192 if (error == ERROR_FILE_NOT_FOUND ||
193 error == ERROR_MOD_NOT_FOUND)
194 a->debug("Cannot find windbg.exe or kd.exe file in PATH");
196 a->debug("Cannot load %s file: %s.", use_kd_exe ? "kd.exe" : "windbg.exe", win32_strerror(error));
200 /* kldbgdrv.sys is embedded in windbg.exe/kd.exe as a resource with name id 0x7777 and type id 0x4444. */
201 driver_resource_info = FindResource(exe_with_driver, MAKEINTRESOURCE(0x7777), MAKEINTRESOURCE(0x4444));
202 if (!driver_resource_info)
204 a->debug("Cannot find kldbgdrv.sys resource in %s file: %s.", use_kd_exe ? "kd.exe" : "windbg.exe", win32_strerror(GetLastError()));
208 driver_resource = LoadResource(exe_with_driver, driver_resource_info);
209 if (!driver_resource)
211 a->debug("Cannot load kldbgdrv.sys resource from %s file: %s.", use_kd_exe ? "kd.exe" : "windbg.exe", win32_strerror(GetLastError()));
215 driver_size = SizeofResource(exe_with_driver, driver_resource_info);
218 a->debug("Cannot determinate size of kldbgdrv.sys resource from %s file: %s.", use_kd_exe ? "kd.exe" : "windbg.exe", win32_strerror(GetLastError()));
222 driver_data = LockResource(driver_resource);
225 a->debug("Cannot load kldbgdrv.sys resouce data from %s file: %s.", use_kd_exe ? "kd.exe" : "windbg.exe", win32_strerror(GetLastError()));
229 if (!win32_check_driver(driver_data))
231 a->debug("Cannot use kldbgdrv.sys driver from %s file: Driver is from different architecture.", use_kd_exe ? "kd.exe" : "windbg.exe");
235 driver_handle = CreateFile(driver_path, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
236 if (driver_handle == INVALID_HANDLE_VALUE)
238 error = GetLastError();
239 if (error != ERROR_FILE_EXISTS)
241 a->debug("Cannot create kldbgdrv.sys driver file in system32 directory: %s.", win32_strerror(error));
244 /* If driver file in system32 directory already exists then treat it as successfull unpack. */
249 if (!WriteFile(driver_handle, driver_data, driver_size, &written, NULL) ||
250 written != driver_size)
252 a->debug("Cannot store kldbgdrv.sys driver file to system32 directory: %s.", win32_strerror(GetLastError()));
253 /* On error, delete file from system32 directory to allow another unpack attempt. */
254 CloseHandle(driver_handle);
255 driver_handle = INVALID_HANDLE_VALUE;
256 DeleteFile(driver_path);
260 a->debug("Driver kldbgdrv.sys was successfully unpacked from %s and stored in system32 directory...", use_kd_exe ? "kd.exe" : "windbg.exe");
264 if (driver_handle != INVALID_HANDLE_VALUE)
265 CloseHandle(driver_handle);
268 FreeResource(driver_resource);
271 FreeLibrary(exe_with_driver);
277 win32_kldbg_register_driver(struct pci_access *a, SC_HANDLE manager, SC_HANDLE *service)
281 HANDLE driver_handle;
284 * COM library dbgeng.dll unpacks kldbg driver to file "\\system32\\kldbgdrv.sys"
285 * and register this driver with service name kldbgdrv. Implement same behavior.
286 * GetSystemDirectory() returns path to "\\system32" directory on all Windows versions.
289 system32_len = GetSystemDirectory(NULL, 0); /* Returns number of TCHARs plus 1 for nul-term. */
291 system32_len = sizeof("C:\\Windows\\System32");
293 driver_path = pci_malloc(a, (system32_len + sizeof("\\kldbgdrv.sys")-1) * sizeof(TCHAR));
295 system32_len = GetSystemDirectory(driver_path, system32_len); /* Now it returns number of TCHARs without nul-term. */
298 system32_len = sizeof("C:\\Windows\\System32")-1;
299 memcpy(driver_path, TEXT("C:\\Windows\\System32"), system32_len);
302 /* GetSystemDirectory returns path without backslash unless the system directory is the root directory. */
303 if (driver_path[system32_len-1] != '\\')
304 driver_path[system32_len++] = '\\';
306 memcpy(driver_path + system32_len, TEXT("kldbgdrv.sys"), sizeof(TEXT("kldbgdrv.sys")));
308 driver_handle = CreateFile(driver_path, 0, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
309 if (driver_handle != INVALID_HANDLE_VALUE)
310 CloseHandle(driver_handle);
311 else if (GetLastError() == ERROR_FILE_NOT_FOUND)
313 a->debug("Driver kldbgdrv.sys is missing, trying to unpack it from windbg.exe or kd.exe...");
314 if (!win32_kldbg_unpack_driver(a, driver_path))
316 pci_mfree(driver_path);
321 *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);
324 if (GetLastError() != ERROR_SERVICE_EXISTS)
326 a->debug("Cannot create kldbgdrv service: %s.", win32_strerror(GetLastError()));
327 pci_mfree(driver_path);
331 *service = OpenService(manager, TEXT("kldbgdrv"), SERVICE_START);
334 a->debug("Cannot open kldbgdrv service: %s.", win32_strerror(GetLastError()));
335 pci_mfree(driver_path);
340 a->debug("Service kldbgdrv was successfully registered...");
341 pci_mfree(driver_path);
346 win32_kldbg_start_driver(struct pci_access *a)
348 SC_HANDLE manager = NULL;
349 SC_HANDLE service = NULL;
353 manager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE);
355 manager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
358 a->debug("Cannot open Service Manager: %s.", win32_strerror(GetLastError()));
362 service = OpenService(manager, TEXT("kldbgdrv"), SERVICE_START);
365 error = GetLastError();
366 if (error != ERROR_SERVICE_DOES_NOT_EXIST)
368 a->debug("Cannot open kldbgdrv service: %s.", win32_strerror(error));
372 a->debug("Kernel Local Debugging Driver (kldbgdrv.sys) is not registered, trying to register it...");
374 if (win32_is_32bit_on_64bit_system())
377 a->debug("Registering driver from 32-bit process on 64-bit system is not implemented yet.");
381 if (!win32_kldbg_register_driver(a, manager, &service))
385 if (!StartService(service, 0, NULL))
387 error = GetLastError();
388 if (error != ERROR_SERVICE_ALREADY_RUNNING)
390 a->debug("Cannot start kldbgdrv service: %s.", win32_strerror(error));
395 a->debug("Service kldbgdrv successfully started...");
400 CloseServiceHandle(service);
403 CloseServiceHandle(manager);
409 win32_kldbg_setup(struct pci_access *a)
411 OSVERSIONINFO version;
416 if (kldbg_dev != INVALID_HANDLE_VALUE)
419 /* Check for Windows Vista (NT 6.0). */
420 version.dwOSVersionInfoSize = sizeof(version);
421 if (!GetVersionEx(&version) ||
422 version.dwPlatformId != VER_PLATFORM_WIN32_NT ||
423 version.dwMajorVersion < 6)
425 a->debug("Accessing PCI config space via Kernel Local Debugging Driver requires Windows Vista or higher version.");
429 kldbg_dev = CreateFile(TEXT("\\\\.\\kldbgdrv"), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
430 if (kldbg_dev == INVALID_HANDLE_VALUE)
432 error = GetLastError();
433 if (error != ERROR_FILE_NOT_FOUND)
435 a->debug("Cannot open \"\\\\.\\kldbgdrv\" device: %s.", win32_strerror(error));
439 a->debug("Kernel Local Debugging Driver (kldbgdrv.sys) is not running, trying to start it...");
441 if (!win32_kldbg_start_driver(a))
444 kldbg_dev = CreateFile(TEXT("\\\\.\\kldbgdrv"), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
445 if (kldbg_dev == INVALID_HANDLE_VALUE)
447 error = GetLastError();
448 a->debug("Cannot open \"\\\\.\\kldbgdrv\" device: %s.", win32_strerror(error));
454 * Try to read PCI id register from PCI device 0000:00:00.0.
455 * If this device does not exist and kldbg API is working then
456 * kldbg returns success with read value 0xffffffff.
458 if (win32_kldbg_pci_bus_data(FALSE, 0, 0, 0, 0, 0, &id, sizeof(id), &ret_len) && ret_len == sizeof(id))
461 error = GetLastError();
463 a->debug("Cannot read PCI config space via Kernel Local Debugging Driver: %s.", win32_strerror(error));
465 if (error != ERROR_ACCESS_DENIED)
467 CloseHandle(kldbg_dev);
468 kldbg_dev = INVALID_HANDLE_VALUE;
472 a->debug("..Trying again with Debug privilege...");
474 if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid_debug_privilege))
476 a->debug("Debug privilege is not supported.");
477 CloseHandle(kldbg_dev);
478 kldbg_dev = INVALID_HANDLE_VALUE;
482 if (!win32_enable_privilege(luid_debug_privilege, &revert_token, &revert_only_privilege))
484 a->debug("Process does not have right to enable Debug privilege.");
485 CloseHandle(kldbg_dev);
486 kldbg_dev = INVALID_HANDLE_VALUE;
490 if (win32_kldbg_pci_bus_data(FALSE, 0, 0, 0, 0, 0, &id, sizeof(id), &ret_len) && ret_len == sizeof(id))
492 a->debug("Succeeded.");
493 debug_privilege_enabled = TRUE;
497 error = GetLastError();
499 a->debug("Cannot read PCI config space via Kernel Local Debugging Driver: %s.", win32_strerror(error));
501 CloseHandle(kldbg_dev);
502 kldbg_dev = INVALID_HANDLE_VALUE;
504 win32_revert_privilege(luid_debug_privilege, revert_token, revert_only_privilege);
506 revert_only_privilege = FALSE;
511 win32_kldbg_detect(struct pci_access *a)
513 if (!win32_kldbg_setup(a))
520 win32_kldbg_init(struct pci_access *a)
522 if (!win32_kldbg_setup(a))
525 a->error("PCI config space via Kernel Local Debugging Driver cannot be accessed.");
530 win32_kldbg_cleanup(struct pci_access *a UNUSED)
532 if (kldbg_dev == INVALID_HANDLE_VALUE)
535 CloseHandle(kldbg_dev);
536 kldbg_dev = INVALID_HANDLE_VALUE;
538 if (debug_privilege_enabled)
540 win32_revert_privilege(luid_debug_privilege, revert_token, revert_only_privilege);
542 revert_only_privilege = FALSE;
543 debug_privilege_enabled = FALSE;
553 char oem_table_id[8];
555 char asl_compiler_id[4];
556 u32 asl_compiler_revision;
568 win32_kldbg_scan(struct pci_access *a)
571 * There is no kldbg API to retrieve list of PCI segments. WinDBG pci plugin
572 * kext.dll loads debug symbols from pci.pdb file for kernel module pci.sys.
573 * Then it reads kernel memory which belongs to PciSegmentList local variable
574 * which is the first entry of struct _PCI_SEGMENT linked list. And then it
575 * iterates all entries in linked list and reads SegmentNumber for each entry.
577 * This is extremly ugly hack and does not work on systems without installed
578 * kernel debug symbol files.
580 * Do something less ugly. Retrieve ACPI MCFG table via GetSystemFirmwareTable
581 * and parse all PCI segment numbers from it. ACPI MCFG table contains PCIe
582 * ECAM definitions, so all PCI segment numbers.
585 UINT (*WINAPI MyGetSystemFirmwareTable)(DWORD FirmwareTableProviderSignature, DWORD FirmwareTableID, PVOID pFirmwareTableBuffer, DWORD BufferSize);
586 int i, allocations_count;
587 struct acpi_mcfg *mcfg;
593 /* Always scan PCI segment 0. */
594 pci_generic_scan_domain(a, 0);
596 kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
600 /* Function GetSystemFirmwareTable() is available since Windows Vista. */
601 MyGetSystemFirmwareTable = (void *)GetProcAddress(kernel32, "GetSystemFirmwareTable");
602 if (!MyGetSystemFirmwareTable)
605 /* 0x41435049 = 'ACPI', 0x4746434D = 'MCFG' */
606 size = MyGetSystemFirmwareTable(0x41435049, 0x4746434D, NULL, 0);
609 error = GetLastError();
610 if (error == ERROR_INVALID_FUNCTION) /* ACPI is not present, so only PCI segment 0 is available. */
612 else if (error == ERROR_NOT_FOUND) /* MCFG table is not present, so only PCI segment 0 is available. */
614 a->debug("Cannot retrieve ACPI MCFG table: %s.\n", win32_strerror(error));
618 mcfg = pci_malloc(a, size);
620 if (MyGetSystemFirmwareTable(0x41435049, 0x4746434D, mcfg, size) != size)
622 error = GetLastError();
623 a->debug("Cannot retrieve ACPI MCFG table: %s.\n", win32_strerror(error));
628 if (size < sizeof(*mcfg) || size < mcfg->length)
630 a->debug("ACPI MCFG table is broken.\n");
635 segments = pci_malloc(a, 0xFFFF/8);
636 memset(segments, 0, 0xFFFF/8);
638 /* Scan all MCFG allocations and set available PCI segments into bit field. */
639 allocations_count = (mcfg->length - ((unsigned char *)&mcfg->allocations - (unsigned char *)mcfg)) / sizeof(mcfg->allocations[0]);
640 for (i = 0; i < allocations_count; i++)
641 segments[mcfg->allocations[i].pci_segment / 8] |= 1 << (mcfg->allocations[i].pci_segment % 8);
643 /* Skip PCI segment 0 which was already scanned. */
644 for (i = 1; i < 0xFFFF; i++)
645 if (segments[i / 8] & (1 << (i % 8)))
646 pci_generic_scan_domain(a, i);
653 win32_kldbg_pci_bus_data(BOOL WriteBusData, USHORT SegmentNumber, BYTE BusNumber, BYTE DeviceNumber, BYTE FunctionNumber, USHORT Address, PVOID Buffer, ULONG BufferSize, LPDWORD Length)
656 SYSDBG_BUS_DATA sysdbg_cmd;
657 PCI_SLOT_NUMBER pci_slot;
658 PCI_SEGMENT_BUS_NUMBER pci_seg_bus;
660 memset(&pci_slot, 0, sizeof(pci_slot));
661 memset(&sysdbg_cmd, 0, sizeof(sysdbg_cmd));
662 memset(&pci_seg_bus, 0, sizeof(pci_seg_bus));
664 sysdbg_cmd.Address = Address;
665 sysdbg_cmd.Buffer = Buffer;
666 sysdbg_cmd.Request = BufferSize;
667 sysdbg_cmd.BusDataType = PCIConfiguration;
668 pci_seg_bus.u.bits.BusNumber = BusNumber;
669 pci_seg_bus.u.bits.SegmentNumber = SegmentNumber;
670 sysdbg_cmd.BusNumber = pci_seg_bus.u.AsULONG;
671 pci_slot.u.bits.DeviceNumber = DeviceNumber;
672 pci_slot.u.bits.FunctionNumber = FunctionNumber;
673 sysdbg_cmd.SlotNumber = pci_slot.u.AsULONG;
675 kldbg_cmd.Command = WriteBusData ? SysDbgWriteBusData : SysDbgReadBusData;
676 kldbg_cmd.Buffer = &sysdbg_cmd;
677 kldbg_cmd.BufferLength = sizeof(sysdbg_cmd);
680 return DeviceIoControl(kldbg_dev, IOCTL_KLDBG, &kldbg_cmd, sizeof(kldbg_cmd), &sysdbg_cmd, sizeof(sysdbg_cmd), Length, NULL);
684 win32_kldbg_read(struct pci_dev *d, int pos, byte *buf, int len)
688 if ((unsigned int)d->domain > 0xffff)
691 if (!win32_kldbg_pci_bus_data(FALSE, d->domain, d->bus, d->dev, d->func, pos, buf, len, &ret_len))
694 if (ret_len != (unsigned int)len)
701 win32_kldbg_write(struct pci_dev *d, int pos, byte *buf, int len)
705 if ((unsigned int)d->domain > 0xffff)
708 if (!win32_kldbg_pci_bus_data(TRUE, d->domain, d->bus, d->dev, d->func, pos, buf, len, &ret_len))
711 if (ret_len != (unsigned int)len)
717 struct pci_methods pm_win32_kldbg = {
718 .name = "win32-kldbg",
719 .help = "Win32 PCI config space access using Kernel Local Debugging Driver",
720 .detect = win32_kldbg_detect,
721 .init = win32_kldbg_init,
722 .cleanup = win32_kldbg_cleanup,
723 .scan = win32_kldbg_scan,
724 .fill_info = pci_generic_fill_info,
725 .read = win32_kldbg_read,
726 .write = win32_kldbg_write,