1 /*
2 * The PCI Library -- PCI config space access using Kernel Local Debugging Driver
3 *
4 * Copyright (c) 2022 Pali Rohár <[email protected]>
5 *
6 * Can be freely distributed and used under the terms of the GNU GPL v2+.
7 *
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
10
11 #include <windows.h>
12 #include <winioctl.h>
13
14 #include <stdio.h> /* for sprintf() */
15 #include <string.h> /* for memset() and memcpy() */
16
17 #include "internal.h"
18 #include "win32-helpers.h"
19
20 #ifndef ERROR_NOT_FOUND
21 #define ERROR_NOT_FOUND 1168
22 #endif
23
24 #ifndef LOAD_LIBRARY_AS_IMAGE_RESOURCE
25 #define LOAD_LIBRARY_AS_IMAGE_RESOURCE 0x20
26 #endif
27 #ifndef LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE
28 #define LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE 0x40
29 #endif
30
31 #ifndef IOCTL_KLDBG
32 #define IOCTL_KLDBG CTL_CODE(FILE_DEVICE_UNKNOWN, 0x1, METHOD_NEITHER, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
33 #endif
34
35 #ifndef BUS_DATA_TYPE
36 #define BUS_DATA_TYPE LONG
37 #endif
38 #ifndef PCIConfiguration
39 #define PCIConfiguration (BUS_DATA_TYPE)4
40 #endif
41
42 #ifndef SYSDBG_COMMAND
43 #define SYSDBG_COMMAND ULONG
44 #endif
45 #ifndef SysDbgReadBusData
46 #define SysDbgReadBusData (SYSDBG_COMMAND)18
47 #endif
48 #ifndef SysDbgWriteBusData
49 #define SysDbgWriteBusData (SYSDBG_COMMAND)19
50 #endif
51
52 #ifndef _WIN64
53 typedef struct _SYSDBG_BUS_DATA64 {
54 ULONG Address;
55 u64 Buffer64; /* 32-bit process has to pass 64-bit wide pointer on 64-bit systems */
56 ULONG Request;
57 BUS_DATA_TYPE BusDataType;
58 ULONG BusNumber;
59 ULONG SlotNumber;
60 } SYSDBG_BUS_DATA64, *PSYSDBG_BUS_DATA64;
61 #endif
62 #ifndef SYSDBG_BUS_DATA
63 typedef struct _SYSDBG_BUS_DATA {
64 ULONG Address;
65 PVOID Buffer;
66 ULONG Request;
67 BUS_DATA_TYPE BusDataType;
68 ULONG BusNumber;
69 ULONG SlotNumber;
70 } SYSDBG_BUS_DATA, *PSYSDBG_BUS_DATA;
71 #define SYSDBG_BUS_DATA SYSDBG_BUS_DATA
72 #endif
73
74 #ifndef PCI_SEGMENT_BUS_NUMBER
75 typedef struct _PCI_SEGMENT_BUS_NUMBER {
76 union {
77 struct {
78 ULONG BusNumber:8;
79 ULONG SegmentNumber:16;
80 ULONG Reserved:8;
81 } bits;
82 ULONG AsULONG;
83 } u;
84 } PCI_SEGMENT_BUS_NUMBER, *PPCI_SEGMENT_BUS_NUMBER;
85 #define PCI_SEGMENT_BUS_NUMBER PCI_SEGMENT_BUS_NUMBER
86 #endif
87
88 #ifndef PCI_SLOT_NUMBER
89 typedef struct _PCI_SLOT_NUMBER {
90 union {
91 struct {
92 ULONG DeviceNumber:5;
93 ULONG FunctionNumber:3;
94 ULONG Reserved:24;
95 } bits;
96 ULONG AsULONG;
97 } u;
98 } PCI_SLOT_NUMBER, *PPCI_SLOT_NUMBER;
99 #define PCI_SLOT_NUMBER PCI_SLOT_NUMBER
100 #endif
101
102 #ifndef _WIN64
103 typedef struct _KLDBG64 {
104 SYSDBG_COMMAND Command;
105 u64 Buffer64; /* 32-bit process has to pass 64-bit wide pointer on 64-bit systems */
106 DWORD BufferLength;
107 } KLDBG64, *PKLDBG64;
108 #endif
109 #ifndef KLDBG
110 typedef struct _KLDBG {
111 SYSDBG_COMMAND Command;
112 PVOID Buffer;
113 DWORD BufferLength;
114 } KLDBG, *PKLDBG;
115 #define KLDBG KLDBG
116 #endif
117
118 static BOOL debug_privilege_enabled;
119 static LUID luid_debug_privilege;
120 static BOOL revert_only_privilege;
121 static HANDLE revert_token;
122
123 static HANDLE kldbg_dev = INVALID_HANDLE_VALUE;
124
125 static BOOL
126 win32_kldbg_pci_bus_data(BOOL WriteBusData, USHORT SegmentNumber, BYTE BusNumber, BYTE DeviceNumber, BYTE FunctionNumber, USHORT Address, PVOID Buffer, ULONG BufferSize, LPDWORD Length);
127
128 static BOOL
win32_check_driver(BYTE * driver_data,USHORT native_machine,USHORT * driver_machine_ptr)129 win32_check_driver(BYTE *driver_data, USHORT native_machine, USHORT *driver_machine_ptr)
130 {
131 IMAGE_DOS_HEADER *dos_header;
132 IMAGE_NT_HEADERS *nt_headers;
133
134 if (native_machine == IMAGE_FILE_MACHINE_UNKNOWN)
135 return FALSE;
136
137 dos_header = (IMAGE_DOS_HEADER *)driver_data;
138 if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
139 return FALSE;
140
141 nt_headers = (IMAGE_NT_HEADERS *)((BYTE *)dos_header + dos_header->e_lfanew);
142 if (nt_headers->Signature != IMAGE_NT_SIGNATURE)
143 return FALSE;
144
145 if (!(nt_headers->FileHeader.Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE))
146 return FALSE;
147
148 if (nt_headers->OptionalHeader.Subsystem != IMAGE_SUBSYSTEM_NATIVE)
149 return FALSE;
150
151 *driver_machine_ptr = nt_headers->FileHeader.Machine;
152 if (*driver_machine_ptr != native_machine)
153 return FALSE;
154
155 return TRUE;
156 }
157
158 static BOOL
win32_kldbg_unpack_driver(struct pci_access * a,LPTSTR driver_path,USHORT native_machine)159 win32_kldbg_unpack_driver(struct pci_access *a, LPTSTR driver_path, USHORT native_machine)
160 {
161 BOOL use_kd_exe = FALSE;
162 HMODULE exe_with_driver = NULL;
163 HRSRC driver_resource_info = NULL;
164 HGLOBAL driver_resource = NULL;
165 BYTE *driver_data = NULL;
166 DWORD driver_size = 0;
167 HANDLE driver_handle = INVALID_HANDLE_VALUE;
168 USHORT driver_machine = IMAGE_FILE_MACHINE_UNKNOWN;
169 DWORD written = 0;
170 DWORD error = 0;
171 BOOL ret = FALSE;
172
173 /* Try to find and open windbg.exe or kd.exe file in PATH. */
174 exe_with_driver = LoadLibraryEx(TEXT("windbg.exe"), NULL, LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE | LOAD_LIBRARY_AS_IMAGE_RESOURCE);
175 if (!exe_with_driver)
176 {
177 use_kd_exe = TRUE;
178 exe_with_driver = LoadLibraryEx(TEXT("kd.exe"), NULL, LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE | LOAD_LIBRARY_AS_IMAGE_RESOURCE);
179 }
180 if (!exe_with_driver)
181 {
182 error = GetLastError();
183 if (error == ERROR_FILE_NOT_FOUND ||
184 error == ERROR_MOD_NOT_FOUND)
185 a->debug("Cannot find windbg.exe or kd.exe file in PATH.");
186 else
187 a->debug("Cannot load %s file: %s.", use_kd_exe ? "kd.exe" : "windbg.exe", win32_strerror(error));
188 goto out;
189 }
190
191 /* kldbgdrv.sys is embedded in windbg.exe/kd.exe as a resource with name id 0x7777 and type id 0x4444. */
192 driver_resource_info = FindResource(exe_with_driver, MAKEINTRESOURCE(0x7777), MAKEINTRESOURCE(0x4444));
193 if (!driver_resource_info)
194 {
195 a->debug("Cannot find kldbgdrv.sys resource in %s file: %s.", use_kd_exe ? "kd.exe" : "windbg.exe", win32_strerror(GetLastError()));
196 goto out;
197 }
198
199 driver_resource = LoadResource(exe_with_driver, driver_resource_info);
200 if (!driver_resource)
201 {
202 a->debug("Cannot load kldbgdrv.sys resource from %s file: %s.", use_kd_exe ? "kd.exe" : "windbg.exe", win32_strerror(GetLastError()));
203 goto out;
204 }
205
206 driver_size = SizeofResource(exe_with_driver, driver_resource_info);
207 if (!driver_size)
208 {
209 a->debug("Cannot determinate size of kldbgdrv.sys resource from %s file: %s.", use_kd_exe ? "kd.exe" : "windbg.exe", win32_strerror(GetLastError()));
210 goto out;
211 }
212
213 driver_data = LockResource(driver_resource);
214 if (!driver_data)
215 {
216 a->debug("Cannot load kldbgdrv.sys resouce data from %s file: %s.", use_kd_exe ? "kd.exe" : "windbg.exe", win32_strerror(GetLastError()));
217 goto out;
218 }
219
220 if (!win32_check_driver(driver_data, native_machine, &driver_machine))
221 {
222 if (native_machine == IMAGE_FILE_MACHINE_UNKNOWN)
223 a->debug("Cannot use kldbgdrv.sys driver from %s file: Cannot determinate native machine architecture.", use_kd_exe ? "kd.exe" : "windbg.exe");
224 else if (driver_machine == IMAGE_FILE_MACHINE_UNKNOWN)
225 a->debug("Cannot use kldbgdrv.sys driver from %s file: Attached resource is not valid kernel driver.", use_kd_exe ? "kd.exe" : "windbg.exe");
226 else
227 a->debug("Cannot use kldbgdrv.sys driver from %s file: Driver machine architecture 0x%04x differs from native machine architecture 0x%04x.", use_kd_exe ? "kd.exe" : "windbg.exe", (unsigned)driver_machine, (unsigned)native_machine);
228 goto out;
229 }
230
231 driver_handle = CreateFile(driver_path, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
232 if (driver_handle == INVALID_HANDLE_VALUE)
233 {
234 error = GetLastError();
235 if (error != ERROR_FILE_EXISTS)
236 {
237 a->debug("Cannot create kldbgdrv.sys driver file in system32 directory: %s.", win32_strerror(error));
238 goto out;
239 }
240 /* If driver file in system32 directory already exists then treat it as successfull unpack. */
241 ret = TRUE;
242 goto out;
243 }
244
245 if (!WriteFile(driver_handle, driver_data, driver_size, &written, NULL) ||
246 written != driver_size)
247 {
248 a->debug("Cannot store kldbgdrv.sys driver file to system32 directory: %s.", win32_strerror(GetLastError()));
249 /* On error, delete file from system32 directory to allow another unpack attempt. */
250 CloseHandle(driver_handle);
251 driver_handle = INVALID_HANDLE_VALUE;
252 DeleteFile(driver_path);
253 goto out;
254 }
255
256 a->debug("Driver kldbgdrv.sys was successfully unpacked from %s and stored in system32 directory...", use_kd_exe ? "kd.exe" : "windbg.exe");
257 ret = TRUE;
258
259 out:
260 if (driver_handle != INVALID_HANDLE_VALUE)
261 CloseHandle(driver_handle);
262
263 if (driver_resource)
264 FreeResource(driver_resource);
265
266 if (exe_with_driver)
267 FreeLibrary(exe_with_driver);
268
269 return ret;
270 }
271
272 static int
win32_kldbg_register_driver(struct pci_access * a,SC_HANDLE manager,SC_HANDLE * service)273 win32_kldbg_register_driver(struct pci_access *a, SC_HANDLE manager, SC_HANDLE *service)
274 {
275 UINT system32_len;
276 LPTSTR driver_path;
277 HANDLE driver_handle;
278 BOOL has_driver;
279 HMODULE kernel32;
280 USHORT native_machine = IMAGE_FILE_MACHINE_UNKNOWN;
281 BOOL fs_revert_needed = FALSE;
282 PVOID fs_revert_value = NULL;
283 BOOL (WINAPI *MyWow64DisableWow64FsRedirection)(PVOID *) = NULL;
284 BOOL (WINAPI *MyWow64RevertWow64FsRedirection)(PVOID) = NULL;
285
286 /*
287 * COM library dbgeng.dll unpacks kldbg driver to file "\\system32\\kldbgdrv.sys"
288 * and register this driver with service name kldbgdrv. Implement same behavior.
289 * GetSystemDirectory() returns path to "\\system32" directory on all Windows versions.
290 */
291
292 system32_len = GetSystemDirectory(NULL, 0); /* Returns number of TCHARs plus 1 for nul-term. */
293 if (!system32_len)
294 system32_len = sizeof("C:\\Windows\\System32");
295
296 driver_path = pci_malloc(a, (system32_len + sizeof("\\kldbgdrv.sys")-1) * sizeof(TCHAR));
297
298 system32_len = GetSystemDirectory(driver_path, system32_len); /* Now it returns number of TCHARs without nul-term. */
299 if (!system32_len)
300 {
301 system32_len = sizeof("C:\\Windows\\System32")-1;
302 memcpy(driver_path, TEXT("C:\\Windows\\System32"), system32_len);
303 }
304
305 /* GetSystemDirectory returns path without backslash unless the system directory is the root directory. */
306 if (driver_path[system32_len-1] != '\\')
307 driver_path[system32_len++] = '\\';
308
309 memcpy(driver_path + system32_len, TEXT("kldbgdrv.sys"), sizeof(TEXT("kldbgdrv.sys")));
310
311 /*
312 * For non-native processes to access System32 directory, it is required to
313 * disable FsRedirection. FsRedirection is by default enabled and automatically
314 * redirects all access to System32 directory from non-native processes to
315 * different directory (e.g. to SysWOW64 directory).
316 *
317 * FsRedirection can be temporary disabled for he current thread by
318 * Wow64DisableWow64FsRedirection() function and then reverted to the
319 * previous state by Wow64RevertWow64FsRedirection() function. These
320 * functions properly handle repeated calls.
321 */
322
323 if (win32_is_not_native_process(&native_machine))
324 {
325 if (native_machine == IMAGE_FILE_MACHINE_UNKNOWN)
326 {
327 a->debug("Cannot register new driver: Unable to detect native machine architecture from non-native process.");
328 pci_mfree(driver_path);
329 return 0;
330 }
331 kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
332 if (kernel32)
333 {
334 MyWow64DisableWow64FsRedirection = (void *)GetProcAddress(kernel32, "Wow64DisableWow64FsRedirection");
335 MyWow64RevertWow64FsRedirection = (void *)GetProcAddress(kernel32, "Wow64RevertWow64FsRedirection");
336 }
337 if (!MyWow64DisableWow64FsRedirection || !MyWow64RevertWow64FsRedirection)
338 {
339 a->debug("Cannot register new driver: Unable to locate FsRedirection functions in kernel32.dll library.");
340 pci_mfree(driver_path);
341 return 0;
342 }
343 if (!MyWow64DisableWow64FsRedirection(&fs_revert_value))
344 {
345 a->debug("Cannot register new driver: Disabling FsRedirection for the current thread failed: %s.", win32_strerror(GetLastError()));
346 pci_mfree(driver_path);
347 return 0;
348 }
349 fs_revert_needed = TRUE;
350 }
351 else
352 {
353 native_machine = win32_get_process_machine();
354 }
355
356 has_driver = FALSE;
357 driver_handle = CreateFile(driver_path, 0, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
358 if (driver_handle != INVALID_HANDLE_VALUE)
359 CloseHandle(driver_handle);
360
361 if (driver_handle == INVALID_HANDLE_VALUE && GetLastError() == ERROR_FILE_NOT_FOUND)
362 {
363 a->debug("Driver kldbgdrv.sys is missing, trying to unpack it from windbg.exe or kd.exe...");
364 has_driver = win32_kldbg_unpack_driver(a, driver_path, native_machine);
365 }
366 else
367 {
368 /*
369 * driver_path was either successfully opened or it cannot be opened for
370 * any other reason than ERROR_FILE_NOT_FOUND. So expects that it exists.
371 */
372 has_driver = TRUE;
373 }
374
375 if (fs_revert_needed)
376 {
377 if (!MyWow64RevertWow64FsRedirection(fs_revert_value))
378 a->warning("Reverting of FsRedirection for the current thread failed: %s.", win32_strerror(GetLastError()));
379 }
380
381 if (!has_driver)
382 {
383 pci_mfree(driver_path);
384 return 0;
385 }
386
387 *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);
388 if (!*service)
389 {
390 if (GetLastError() != ERROR_SERVICE_EXISTS)
391 {
392 a->debug("Cannot create kldbgdrv service: %s.", win32_strerror(GetLastError()));
393 pci_mfree(driver_path);
394 return 0;
395 }
396
397 *service = OpenService(manager, TEXT("kldbgdrv"), SERVICE_START);
398 if (!*service)
399 {
400 a->debug("Cannot open kldbgdrv service: %s.", win32_strerror(GetLastError()));
401 pci_mfree(driver_path);
402 return 0;
403 }
404 }
405
406 a->debug("Service kldbgdrv was successfully registered...");
407 pci_mfree(driver_path);
408 return 1;
409 }
410
411 static int
win32_kldbg_start_driver(struct pci_access * a)412 win32_kldbg_start_driver(struct pci_access *a)
413 {
414 SC_HANDLE manager = NULL;
415 SC_HANDLE service = NULL;
416 DWORD error = 0;
417 int ret = 0;
418
419 manager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE);
420 if (!manager)
421 manager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
422 if (!manager)
423 {
424 a->debug("Cannot open Service Manager: %s.", win32_strerror(GetLastError()));
425 return 0;
426 }
427
428 service = OpenService(manager, TEXT("kldbgdrv"), SERVICE_START);
429 if (!service)
430 {
431 error = GetLastError();
432 if (error != ERROR_SERVICE_DOES_NOT_EXIST)
433 {
434 a->debug("Cannot open kldbgdrv service: %s.", win32_strerror(error));
435 goto out;
436 }
437
438 a->debug("Kernel Local Debugging Driver (kldbgdrv.sys) is not registered, trying to register it...");
439
440 if (!win32_kldbg_register_driver(a, manager, &service))
441 goto out;
442 }
443
444 if (!StartService(service, 0, NULL))
445 {
446 error = GetLastError();
447 if (error != ERROR_SERVICE_ALREADY_RUNNING)
448 {
449 a->debug("Cannot start kldbgdrv service: %s.", win32_strerror(error));
450 goto out;
451 }
452 }
453
454 a->debug("Service kldbgdrv successfully started...");
455 ret = 1;
456
457 out:
458 if (service)
459 CloseServiceHandle(service);
460
461 if (manager)
462 CloseServiceHandle(manager);
463
464 return ret;
465 }
466
467 static int
win32_kldbg_setup(struct pci_access * a)468 win32_kldbg_setup(struct pci_access *a)
469 {
470 OSVERSIONINFO version;
471 DWORD ret_len;
472 DWORD error;
473 DWORD id;
474
475 if (kldbg_dev != INVALID_HANDLE_VALUE)
476 return 1;
477
478 /* Check for Windows Vista (NT 6.0). */
479 version.dwOSVersionInfoSize = sizeof(version);
480 if (!GetVersionEx(&version) ||
481 version.dwPlatformId != VER_PLATFORM_WIN32_NT ||
482 version.dwMajorVersion < 6)
483 {
484 a->debug("Accessing PCI config space via Kernel Local Debugging Driver requires Windows Vista or higher version.");
485 return 0;
486 }
487
488 kldbg_dev = CreateFile(TEXT("\\\\.\\kldbgdrv"), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
489 if (kldbg_dev == INVALID_HANDLE_VALUE)
490 {
491 error = GetLastError();
492 if (error != ERROR_FILE_NOT_FOUND)
493 {
494 a->debug("Cannot open \"\\\\.\\kldbgdrv\" device: %s.", win32_strerror(error));
495 return 0;
496 }
497
498 a->debug("Kernel Local Debugging Driver (kldbgdrv.sys) is not running, trying to start it...");
499
500 if (!win32_kldbg_start_driver(a))
501 return 0;
502
503 kldbg_dev = CreateFile(TEXT("\\\\.\\kldbgdrv"), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
504 if (kldbg_dev == INVALID_HANDLE_VALUE)
505 {
506 error = GetLastError();
507 a->debug("Cannot open \"\\\\.\\kldbgdrv\" device: %s.", win32_strerror(error));
508 return 0;
509 }
510 }
511
512 /*
513 * Try to read PCI id register from PCI device 0000:00:00.0.
514 * If this device does not exist and kldbg API is working then
515 * kldbg returns success with read value 0xffffffff.
516 */
517 if (win32_kldbg_pci_bus_data(FALSE, 0, 0, 0, 0, 0, &id, sizeof(id), &ret_len) && ret_len == sizeof(id))
518 return 1;
519
520 error = GetLastError();
521
522 a->debug("Cannot read PCI config space via Kernel Local Debugging Driver: %s.", win32_strerror(error));
523
524 if (error != ERROR_ACCESS_DENIED)
525 {
526 CloseHandle(kldbg_dev);
527 kldbg_dev = INVALID_HANDLE_VALUE;
528 return 0;
529 }
530
531 a->debug("..Trying again with Debug privilege...");
532
533 if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid_debug_privilege))
534 {
535 a->debug("Debug privilege is not supported.");
536 CloseHandle(kldbg_dev);
537 kldbg_dev = INVALID_HANDLE_VALUE;
538 return 0;
539 }
540
541 if (!win32_enable_privilege(luid_debug_privilege, &revert_token, &revert_only_privilege))
542 {
543 a->debug("Process does not have right to enable Debug privilege.");
544 CloseHandle(kldbg_dev);
545 kldbg_dev = INVALID_HANDLE_VALUE;
546 return 0;
547 }
548
549 if (win32_kldbg_pci_bus_data(FALSE, 0, 0, 0, 0, 0, &id, sizeof(id), &ret_len) && ret_len == sizeof(id))
550 {
551 a->debug("Succeeded.");
552 debug_privilege_enabled = TRUE;
553 return 1;
554 }
555
556 error = GetLastError();
557
558 a->debug("Cannot read PCI config space via Kernel Local Debugging Driver: %s.", win32_strerror(error));
559
560 CloseHandle(kldbg_dev);
561 kldbg_dev = INVALID_HANDLE_VALUE;
562
563 win32_revert_privilege(luid_debug_privilege, revert_token, revert_only_privilege);
564 revert_token = NULL;
565 revert_only_privilege = FALSE;
566 return 0;
567 }
568
569 static int
win32_kldbg_detect(struct pci_access * a)570 win32_kldbg_detect(struct pci_access *a)
571 {
572 if (!win32_kldbg_setup(a))
573 return 0;
574
575 return 1;
576 }
577
578 static void
win32_kldbg_init(struct pci_access * a)579 win32_kldbg_init(struct pci_access *a)
580 {
581 if (!win32_kldbg_setup(a))
582 {
583 a->debug("\n");
584 a->error("PCI config space via Kernel Local Debugging Driver cannot be accessed.");
585 }
586 }
587
588 static void
win32_kldbg_cleanup(struct pci_access * a UNUSED)589 win32_kldbg_cleanup(struct pci_access *a UNUSED)
590 {
591 if (kldbg_dev == INVALID_HANDLE_VALUE)
592 return;
593
594 CloseHandle(kldbg_dev);
595 kldbg_dev = INVALID_HANDLE_VALUE;
596
597 if (debug_privilege_enabled)
598 {
599 win32_revert_privilege(luid_debug_privilege, revert_token, revert_only_privilege);
600 revert_token = NULL;
601 revert_only_privilege = FALSE;
602 debug_privilege_enabled = FALSE;
603 }
604 }
605
606 struct acpi_mcfg {
607 char signature[4];
608 u32 length;
609 u8 revision;
610 u8 checksum;
611 char oem_id[6];
612 char oem_table_id[8];
613 u32 oem_revision;
614 char asl_compiler_id[4];
615 u32 asl_compiler_revision;
616 u64 reserved;
617 struct {
618 u64 address;
619 u16 pci_segment;
620 u8 start_bus_number;
621 u8 end_bus_number;
622 u32 reserved;
623 } allocations[0];
624 } PCI_PACKED;
625
626 static void
win32_kldbg_scan(struct pci_access * a)627 win32_kldbg_scan(struct pci_access *a)
628 {
629 /*
630 * There is no kldbg API to retrieve list of PCI segments. WinDBG pci plugin
631 * kext.dll loads debug symbols from pci.pdb file for kernel module pci.sys.
632 * Then it reads kernel memory which belongs to PciSegmentList local variable
633 * which is the first entry of struct _PCI_SEGMENT linked list. And then it
634 * iterates all entries in linked list and reads SegmentNumber for each entry.
635 *
636 * This is extremly ugly hack and does not work on systems without installed
637 * kernel debug symbol files.
638 *
639 * Do something less ugly. Retrieve ACPI MCFG table via GetSystemFirmwareTable
640 * and parse all PCI segment numbers from it. ACPI MCFG table contains PCIe
641 * ECAM definitions, so all PCI segment numbers.
642 */
643
644 UINT (WINAPI *MyGetSystemFirmwareTable)(DWORD FirmwareTableProviderSignature, DWORD FirmwareTableID, PVOID pFirmwareTableBuffer, DWORD BufferSize);
645 int i, allocations_count;
646 struct acpi_mcfg *mcfg;
647 HMODULE kernel32;
648 byte *segments;
649 DWORD error;
650 DWORD size;
651
652 /* Always scan PCI segment 0. */
653 pci_generic_scan_domain(a, 0);
654
655 kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
656 if (!kernel32)
657 return;
658
659 /* Function GetSystemFirmwareTable() is available since Windows Vista. */
660 MyGetSystemFirmwareTable = (void *)GetProcAddress(kernel32, "GetSystemFirmwareTable");
661 if (!MyGetSystemFirmwareTable)
662 return;
663
664 /* 0x41435049 = 'ACPI', 0x4746434D = 'MCFG' */
665 size = MyGetSystemFirmwareTable(0x41435049, 0x4746434D, NULL, 0);
666 if (size == 0)
667 {
668 error = GetLastError();
669 if (error == ERROR_INVALID_FUNCTION) /* ACPI is not present, so only PCI segment 0 is available. */
670 return;
671 else if (error == ERROR_NOT_FOUND) /* MCFG table is not present, so only PCI segment 0 is available. */
672 return;
673 a->debug("Cannot retrieve ACPI MCFG table: %s.\n", win32_strerror(error));
674 return;
675 }
676
677 mcfg = pci_malloc(a, size);
678
679 if (MyGetSystemFirmwareTable(0x41435049, 0x4746434D, mcfg, size) != size)
680 {
681 error = GetLastError();
682 a->debug("Cannot retrieve ACPI MCFG table: %s.\n", win32_strerror(error));
683 pci_mfree(mcfg);
684 return;
685 }
686
687 if (size < sizeof(*mcfg) || size < mcfg->length)
688 {
689 a->debug("ACPI MCFG table is broken.\n");
690 pci_mfree(mcfg);
691 return;
692 }
693
694 segments = pci_malloc(a, 0xFFFF/8);
695 memset(segments, 0, 0xFFFF/8);
696
697 /* Scan all MCFG allocations and set available PCI segments into bit field. */
698 allocations_count = (mcfg->length - ((unsigned char *)&mcfg->allocations - (unsigned char *)mcfg)) / sizeof(mcfg->allocations[0]);
699 for (i = 0; i < allocations_count; i++)
700 segments[mcfg->allocations[i].pci_segment / 8] |= 1 << (mcfg->allocations[i].pci_segment % 8);
701
702 /* Skip PCI segment 0 which was already scanned. */
703 for (i = 1; i < 0xFFFF; i++)
704 if (segments[i / 8] & (1 << (i % 8)))
705 pci_generic_scan_domain(a, i);
706
707 pci_mfree(segments);
708 pci_mfree(mcfg);
709 }
710
711 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)712 win32_kldbg_pci_bus_data(BOOL WriteBusData, USHORT SegmentNumber, BYTE BusNumber, BYTE DeviceNumber, BYTE FunctionNumber, USHORT Address, PVOID Buffer, ULONG BufferSize, LPDWORD Length)
713 {
714 union {
715 KLDBG native;
716 #ifndef _WIN64
717 KLDBG64 ext64;
718 #endif
719 } kldbg_cmd;
720 union {
721 SYSDBG_BUS_DATA native;
722 #ifndef _WIN64
723 SYSDBG_BUS_DATA64 ext64;
724 #endif
725 } sysdbg_cmd;
726 PCI_SLOT_NUMBER pci_slot;
727 PCI_SEGMENT_BUS_NUMBER pci_seg_bus;
728 DWORD kldbg_cmd_size;
729 DWORD sysdbg_cmd_size;
730 #ifndef _WIN64
731 BOOL is_32bit_on_64bit_system = win32_is_32bit_on_64bit_system();
732 #endif
733
734 memset(&pci_slot, 0, sizeof(pci_slot));
735 memset(&kldbg_cmd, 0, sizeof(kldbg_cmd));
736 memset(&sysdbg_cmd, 0, sizeof(sysdbg_cmd));
737 memset(&pci_seg_bus, 0, sizeof(pci_seg_bus));
738
739 pci_seg_bus.u.bits.BusNumber = BusNumber;
740 pci_seg_bus.u.bits.SegmentNumber = SegmentNumber;
741
742 pci_slot.u.bits.DeviceNumber = DeviceNumber;
743 pci_slot.u.bits.FunctionNumber = FunctionNumber;
744
745 #ifndef _WIN64
746 if (is_32bit_on_64bit_system)
747 {
748 sysdbg_cmd.ext64.Address = Address;
749 sysdbg_cmd.ext64.Buffer64 = (u64)(ULONG)Buffer; /* extend 32-bit pointer to 64-bit */
750 sysdbg_cmd.ext64.Request = BufferSize;
751 sysdbg_cmd.ext64.BusDataType = PCIConfiguration;
752 sysdbg_cmd.ext64.BusNumber = pci_seg_bus.u.AsULONG;
753 sysdbg_cmd.ext64.SlotNumber = pci_slot.u.AsULONG;
754 sysdbg_cmd_size = sizeof(sysdbg_cmd.ext64);
755 }
756 else
757 #endif
758 {
759 sysdbg_cmd.native.Address = Address;
760 sysdbg_cmd.native.Buffer = Buffer;
761 sysdbg_cmd.native.Request = BufferSize;
762 sysdbg_cmd.native.BusDataType = PCIConfiguration;
763 sysdbg_cmd.native.BusNumber = pci_seg_bus.u.AsULONG;
764 sysdbg_cmd.native.SlotNumber = pci_slot.u.AsULONG;
765 sysdbg_cmd_size = sizeof(sysdbg_cmd.native);
766 }
767
768 #ifndef _WIN64
769 if (is_32bit_on_64bit_system)
770 {
771 kldbg_cmd.ext64.Command = WriteBusData ? SysDbgWriteBusData : SysDbgReadBusData;
772 kldbg_cmd.ext64.Buffer64 = (u64)(ULONG)&sysdbg_cmd; /* extend 32-bit pointer to 64-bit */
773 kldbg_cmd.ext64.BufferLength = sysdbg_cmd_size;
774 kldbg_cmd_size = sizeof(kldbg_cmd.ext64);
775 }
776 else
777 #endif
778 {
779 kldbg_cmd.native.Command = WriteBusData ? SysDbgWriteBusData : SysDbgReadBusData;
780 kldbg_cmd.native.Buffer = &sysdbg_cmd;
781 kldbg_cmd.native.BufferLength = sysdbg_cmd_size;
782 kldbg_cmd_size = sizeof(kldbg_cmd.native);
783 }
784
785 *Length = 0;
786 return DeviceIoControl(kldbg_dev, IOCTL_KLDBG, &kldbg_cmd, kldbg_cmd_size, &sysdbg_cmd, sysdbg_cmd_size, Length, NULL);
787 }
788
789 static int
win32_kldbg_read(struct pci_dev * d,int pos,byte * buf,int len)790 win32_kldbg_read(struct pci_dev *d, int pos, byte *buf, int len)
791 {
792 DWORD ret_len;
793
794 if ((unsigned int)d->domain > 0xffff)
795 return 0;
796
797 if (!win32_kldbg_pci_bus_data(FALSE, d->domain, d->bus, d->dev, d->func, pos, buf, len, &ret_len))
798 return 0;
799
800 if (ret_len != (unsigned int)len)
801 return 0;
802
803 return 1;
804 }
805
806 static int
win32_kldbg_write(struct pci_dev * d,int pos,byte * buf,int len)807 win32_kldbg_write(struct pci_dev *d, int pos, byte *buf, int len)
808 {
809 DWORD ret_len;
810
811 if ((unsigned int)d->domain > 0xffff)
812 return 0;
813
814 if (!win32_kldbg_pci_bus_data(TRUE, d->domain, d->bus, d->dev, d->func, pos, buf, len, &ret_len))
815 return 0;
816
817 if (ret_len != (unsigned int)len)
818 return 0;
819
820 return 1;
821 }
822
823 struct pci_methods pm_win32_kldbg = {
824 .name = "win32-kldbg",
825 .help = "Win32 PCI config space access using Kernel Local Debugging Driver",
826 .detect = win32_kldbg_detect,
827 .init = win32_kldbg_init,
828 .cleanup = win32_kldbg_cleanup,
829 .scan = win32_kldbg_scan,
830 .fill_info = pci_generic_fill_info,
831 .read = win32_kldbg_read,
832 .write = win32_kldbg_write,
833 };
834