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