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