xref: /pciutils/lib/win32-kldbg.c (revision 8eee6d97)
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