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