1 /*
2 * The PCI Library -- PCI config space access using Kernel Local Debugging Driver
3 *
4 * Copyright (c) 2022 Pali Rohár <[email protected]>
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
win32_get_current_process_machine(void)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
win32_check_driver(BYTE * driver_data)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
win32_kldbg_unpack_driver(struct pci_access * a,LPTSTR driver_path)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
win32_kldbg_register_driver(struct pci_access * a,SC_HANDLE manager,SC_HANDLE * service)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
win32_kldbg_start_driver(struct pci_access * a)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
win32_kldbg_setup(struct pci_access * a)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
win32_kldbg_detect(struct pci_access * a)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
win32_kldbg_init(struct pci_access * a)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
win32_kldbg_cleanup(struct pci_access * a UNUSED)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
win32_kldbg_scan(struct pci_access * a)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
win32_kldbg_pci_bus_data(BOOL WriteBusData,USHORT SegmentNumber,BYTE BusNumber,BYTE DeviceNumber,BYTE FunctionNumber,USHORT Address,PVOID Buffer,ULONG BufferSize,LPDWORD Length)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
win32_kldbg_read(struct pci_dev * d,int pos,byte * buf,int len)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
win32_kldbg_write(struct pci_dev * d,int pos,byte * buf,int len)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 };
728