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