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