2 * The PCI Library -- PCI config space access using Kernel Local Debugging Driver
4 * Copyright (c) 2022 Pali Rohár <pali@kernel.org>
6 * Can be freely distributed and used under the terms of the GNU GPL.
12 #include <stdio.h> /* for sprintf() */
13 #include <string.h> /* for memset() and memcpy() */
16 #include "i386-io-windows.h"
18 #ifndef LOAD_LIBRARY_AS_IMAGE_RESOURCE
19 #define LOAD_LIBRARY_AS_IMAGE_RESOURCE 0x20
21 #ifndef LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE
22 #define LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE 0x40
26 #define IOCTL_KLDBG CTL_CODE(FILE_DEVICE_UNKNOWN, 0x1, METHOD_NEITHER, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
30 #define BUS_DATA_TYPE LONG
32 #ifndef PCIConfiguration
33 #define PCIConfiguration (BUS_DATA_TYPE)4
36 #ifndef SYSDBG_COMMAND
37 #define SYSDBG_COMMAND ULONG
39 #ifndef SysDbgReadBusData
40 #define SysDbgReadBusData (SYSDBG_COMMAND)18
42 #ifndef SysDbgWriteBusData
43 #define SysDbgWriteBusData (SYSDBG_COMMAND)19
46 #ifndef SYSDBG_BUS_DATA
47 typedef struct _SYSDBG_BUS_DATA {
51 BUS_DATA_TYPE BusDataType;
54 } SYSDBG_BUS_DATA, *PSYSDBG_BUS_DATA;
55 #define SYSDBG_BUS_DATA SYSDBG_BUS_DATA
58 #ifndef PCI_SEGMENT_BUS_NUMBER
59 typedef struct _PCI_SEGMENT_BUS_NUMBER {
63 ULONG SegmentNumber:16;
68 } PCI_SEGMENT_BUS_NUMBER, *PPCI_SEGMENT_BUS_NUMBER;
69 #define PCI_SEGMENT_BUS_NUMBER PCI_SEGMENT_BUS_NUMBER
72 #ifndef PCI_SLOT_NUMBER
73 typedef struct _PCI_SLOT_NUMBER {
77 ULONG FunctionNumber:3;
82 } PCI_SLOT_NUMBER, *PPCI_SLOT_NUMBER;
83 #define PCI_SLOT_NUMBER PCI_SLOT_NUMBER
87 typedef struct _KLDBG {
88 SYSDBG_COMMAND Command;
95 static BOOL debug_privilege_enabled;
96 static LUID luid_debug_privilege;
97 static BOOL revert_only_privilege;
98 static HANDLE revert_token;
100 static HANDLE kldbg_dev = INVALID_HANDLE_VALUE;
103 win32_kldbg_pci_bus_data(BOOL WriteBusData, USHORT SegmentNumber, BYTE BusNumber, BYTE DeviceNumber, BYTE FunctionNumber, USHORT Address, PVOID Buffer, ULONG BufferSize, LPDWORD Length);
106 win32_strerror(DWORD win32_error_id)
109 * Use static buffer which is large enough.
110 * Hopefully no Win32 API error message string is longer than 4 kB.
112 static char buffer[4096];
115 len = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, win32_error_id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buffer, sizeof(buffer), NULL);
117 /* FormatMessage() automatically appends ".\r\n" to the error message. */
118 if (len && buffer[len-1] == '\n')
119 buffer[--len] = '\0';
120 if (len && buffer[len-1] == '\r')
121 buffer[--len] = '\0';
122 if (len && buffer[len-1] == '.')
123 buffer[--len] = '\0';
126 sprintf(buffer, "Unknown Win32 error %lu", win32_error_id);
132 win32_is_32bit_on_64bit_system(void)
134 BOOL (WINAPI *MyIsWow64Process)(HANDLE, PBOOL);
138 kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
142 MyIsWow64Process = (void *)GetProcAddress(kernel32, "IsWow64Process");
143 if (!MyIsWow64Process)
146 if (!MyIsWow64Process(GetCurrentProcess(), &is_wow64))
153 win32_get_current_process_machine(void)
155 IMAGE_DOS_HEADER *dos_header;
156 IMAGE_NT_HEADERS *nt_header;
158 dos_header = (IMAGE_DOS_HEADER *)GetModuleHandle(NULL);
159 if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
160 return IMAGE_FILE_MACHINE_UNKNOWN;
162 nt_header = (IMAGE_NT_HEADERS *)((BYTE *)dos_header + dos_header->e_lfanew);
163 if (nt_header->Signature != IMAGE_NT_SIGNATURE)
164 return IMAGE_FILE_MACHINE_UNKNOWN;
166 return nt_header->FileHeader.Machine;
170 win32_check_driver(BYTE *driver_data)
172 IMAGE_DOS_HEADER *dos_header;
173 IMAGE_NT_HEADERS *nt_headers;
174 WORD current_machine;
176 current_machine = win32_get_current_process_machine();
177 if (current_machine == IMAGE_FILE_MACHINE_UNKNOWN)
180 dos_header = (IMAGE_DOS_HEADER *)driver_data;
181 if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
184 nt_headers = (IMAGE_NT_HEADERS *)((BYTE *)dos_header + dos_header->e_lfanew);
185 if (nt_headers->Signature != IMAGE_NT_SIGNATURE)
188 if (nt_headers->FileHeader.Machine != current_machine)
191 if (!(nt_headers->FileHeader.Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE))
195 if (!(nt_headers->FileHeader.Characteristics & IMAGE_FILE_32BIT_MACHINE))
199 /* IMAGE_NT_OPTIONAL_HDR_MAGIC is alias for the header magic used on the target compiler architecture. */
200 if (nt_headers->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)
203 if (nt_headers->OptionalHeader.Subsystem != IMAGE_SUBSYSTEM_NATIVE)
210 win32_kldbg_unpack_driver(struct pci_access *a, void *driver_path)
212 BOOL use_kd_exe = FALSE;
213 HMODULE exe_with_driver = NULL;
214 HRSRC driver_resource_info = NULL;
215 HGLOBAL driver_resource = NULL;
216 BYTE *driver_data = NULL;
217 DWORD driver_size = 0;
218 HANDLE driver_handle = INVALID_HANDLE_VALUE;
223 /* Try to find and open windbg.exe or kd.exe file in PATH. */
224 exe_with_driver = LoadLibraryEx(TEXT("windbg.exe"), NULL, LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE | LOAD_LIBRARY_AS_IMAGE_RESOURCE);
225 if (!exe_with_driver)
228 exe_with_driver = LoadLibraryEx(TEXT("kd.exe"), NULL, LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE | LOAD_LIBRARY_AS_IMAGE_RESOURCE);
230 if (!exe_with_driver)
232 error = GetLastError();
233 if (error == ERROR_FILE_NOT_FOUND ||
234 error == ERROR_MOD_NOT_FOUND)
235 a->debug("Cannot find windbg.exe or kd.exe file in PATH");
237 a->debug("Cannot load %s file: %s.", use_kd_exe ? "kd.exe" : "windbg.exe", win32_strerror(error));
241 /* kldbgdrv.sys is embedded in windbg.exe/kd.exe as a resource with name id 0x7777 and type id 0x4444. */
242 driver_resource_info = FindResource(exe_with_driver, MAKEINTRESOURCE(0x7777), MAKEINTRESOURCE(0x4444));
243 if (!driver_resource_info)
245 a->debug("Cannot find kldbgdrv.sys resource in %s file: %s.", use_kd_exe ? "kd.exe" : "windbg.exe", win32_strerror(GetLastError()));
249 driver_resource = LoadResource(exe_with_driver, driver_resource_info);
250 if (!driver_resource)
252 a->debug("Cannot load kldbgdrv.sys resource from %s file: %s.", use_kd_exe ? "kd.exe" : "windbg.exe", win32_strerror(GetLastError()));
256 driver_size = SizeofResource(exe_with_driver, driver_resource_info);
259 a->debug("Cannot determinate size of kldbgdrv.sys resource from %s file: %s.", use_kd_exe ? "kd.exe" : "windbg.exe", win32_strerror(GetLastError()));
263 driver_data = LockResource(driver_resource);
266 a->debug("Cannot load kldbgdrv.sys resouce data from %s file: %s.", use_kd_exe ? "kd.exe" : "windbg.exe", win32_strerror(GetLastError()));
270 if (!win32_check_driver(driver_data))
272 a->debug("Cannot use kldbgdrv.sys driver from %s file: Driver is from different architecture.", use_kd_exe ? "kd.exe" : "windbg.exe");
276 driver_handle = CreateFile(driver_path, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
277 if (driver_handle == INVALID_HANDLE_VALUE)
279 error = GetLastError();
280 if (error != ERROR_FILE_EXISTS)
282 a->debug("Cannot create kldbgdrv.sys driver file in system32 directory: %s.", win32_strerror(error));
285 /* If driver file in system32 directory already exists then treat it as successfull unpack. */
290 if (!WriteFile(driver_handle, driver_data, driver_size, &written, NULL) ||
291 written != driver_size)
293 a->debug("Cannot store kldbgdrv.sys driver file to system32 directory: %s.", win32_strerror(GetLastError()));
294 /* On error, delete file from system32 directory to allow another unpack attempt. */
295 CloseHandle(driver_handle);
296 driver_handle = INVALID_HANDLE_VALUE;
297 DeleteFile(driver_path);
301 a->debug("Driver kldbgdrv.sys was successfully unpacked from %s and stored in system32 directory...", use_kd_exe ? "kd.exe" : "windbg.exe");
305 if (driver_handle != INVALID_HANDLE_VALUE)
306 CloseHandle(driver_handle);
309 FreeResource(driver_resource);
312 FreeLibrary(exe_with_driver);
318 win32_kldbg_register_driver(struct pci_access *a, SC_HANDLE manager, SC_HANDLE *service)
320 UINT (WINAPI *get_system_root_path)(void *buffer, UINT size) = NULL;
323 HANDLE driver_handle;
327 * COM library dbgeng.dll unpacks kldbg driver to file \\system32\\kldbgdrv.sys
328 * and register this driver with service name kldbgdrv. Implement same behavior.
332 * Old Windows versions return path to NT SystemRoot namespace via
333 * GetWindowsDirectory() function. New Windows versions via
334 * GetSystemWindowsDirectory(). GetSystemWindowsDirectory() is not
335 * provided in old Windows versions, so use GetProcAddress() for
336 * compatibility with all Windows versions.
339 kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
341 get_system_root_path = (void *)GetProcAddress(kernel32, "GetSystemWindowsDirectory"
349 if (!get_system_root_path)
350 get_system_root_path = (void *)&GetWindowsDirectory;
352 systemroot_len = get_system_root_path(NULL, 0);
354 systemroot_len = sizeof(TEXT("C:\\Windows\\"));
356 driver_path = pci_malloc(a, systemroot_len + sizeof(TEXT("\\system32\\kldbgdrv.sys")));
358 systemroot_len = get_system_root_path(driver_path, systemroot_len + sizeof(TEXT("")));
361 systemroot_len = sizeof(TEXT("C:\\Windows\\"));
362 memcpy(driver_path, TEXT("C:\\Windows\\"), systemroot_len);
365 if (((char *)driver_path)[systemroot_len-sizeof(TEXT(""))+1] != '\\')
367 ((char *)driver_path)[systemroot_len-sizeof(TEXT(""))+1] = '\\';
368 systemroot_len += sizeof(TEXT(""));
371 memcpy((char *)driver_path + systemroot_len, TEXT("system32\\kldbgdrv.sys"), sizeof(TEXT("system32\\kldbgdrv.sys")));
373 driver_handle = CreateFile(driver_path, 0, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
374 if (driver_handle != INVALID_HANDLE_VALUE)
375 CloseHandle(driver_handle);
376 else if (GetLastError() == ERROR_FILE_NOT_FOUND)
378 a->debug("Driver kldbgdrv.sys is missing, trying to unpack it from windbg.exe or kd.exe...");
379 if (!win32_kldbg_unpack_driver(a, driver_path))
381 pci_mfree(driver_path);
386 *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);
389 if (GetLastError() != ERROR_SERVICE_EXISTS)
391 a->debug("Cannot create kldbgdrv service: %s.", win32_strerror(GetLastError()));
392 pci_mfree(driver_path);
396 *service = OpenService(manager, TEXT("kldbgdrv"), SERVICE_START);
399 a->debug("Cannot open kldbgdrv service: %s.", win32_strerror(GetLastError()));
400 pci_mfree(driver_path);
405 a->debug("Service kldbgdrv was successfully registered...");
406 pci_mfree(driver_path);
411 win32_kldbg_start_driver(struct pci_access *a)
413 SC_HANDLE manager = NULL;
414 SC_HANDLE service = NULL;
418 manager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE);
420 manager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
423 a->debug("Cannot open Service Manager: %s.", win32_strerror(GetLastError()));
427 service = OpenService(manager, TEXT("kldbgdrv"), SERVICE_START);
430 error = GetLastError();
431 if (error != ERROR_SERVICE_DOES_NOT_EXIST)
433 a->debug("Cannot open kldbgdrv service: %s.", win32_strerror(error));
437 a->debug("Kernel Local Debugging Driver (kldbgdrv.sys) is not registered, trying to register it...");
439 if (win32_is_32bit_on_64bit_system())
442 a->debug("Registering driver from 32-bit process on 64-bit system is not implemented yet.");
446 if (!win32_kldbg_register_driver(a, manager, &service))
450 if (!StartService(service, 0, NULL))
452 error = GetLastError();
453 if (error != ERROR_SERVICE_ALREADY_RUNNING)
455 a->debug("Cannot start kldbgdrv service: %s.", win32_strerror(error));
460 a->debug("Service kldbgdrv successfully started...");
465 CloseServiceHandle(service);
468 CloseServiceHandle(manager);
474 win32_kldbg_setup(struct pci_access *a)
476 OSVERSIONINFO version;
481 if (kldbg_dev != INVALID_HANDLE_VALUE)
484 /* Check for Windows Vista (NT 6.0). */
485 version.dwOSVersionInfoSize = sizeof(version);
486 if (!GetVersionEx(&version) ||
487 version.dwPlatformId != VER_PLATFORM_WIN32_NT ||
488 version.dwMajorVersion < 6)
490 a->debug("Accessing PCI config space via Kernel Local Debugging Driver requires Windows Vista or higher version.");
494 kldbg_dev = CreateFile(TEXT("\\\\.\\kldbgdrv"), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
495 if (kldbg_dev == INVALID_HANDLE_VALUE)
497 error = GetLastError();
498 if (error != ERROR_FILE_NOT_FOUND)
500 a->debug("Cannot open \"\\\\.\\kldbgdrv\" device: %s.", win32_strerror(error));
504 a->debug("Kernel Local Debugging Driver (kldbgdrv.sys) is not running, trying to start it...");
506 if (!win32_kldbg_start_driver(a))
509 kldbg_dev = CreateFile(TEXT("\\\\.\\kldbgdrv"), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
510 if (kldbg_dev == INVALID_HANDLE_VALUE)
512 error = GetLastError();
513 a->debug("Cannot open \"\\\\.\\kldbgdrv\" device: %s.", win32_strerror(error));
519 * Try to read PCI id register from PCI device 0000:00:00.0.
520 * If this device does not exist and kldbg API is working then
521 * kldbg returns success with read value 0xffffffff.
523 if (win32_kldbg_pci_bus_data(FALSE, 0, 0, 0, 0, 0, &id, sizeof(id), &ret_len) && ret_len == sizeof(id))
526 error = GetLastError();
528 a->debug("Cannot read PCI config space via Kernel Local Debugging Driver: %s.", win32_strerror(error));
530 if (error != ERROR_ACCESS_DENIED)
532 CloseHandle(kldbg_dev);
533 kldbg_dev = INVALID_HANDLE_VALUE;
537 a->debug("..Trying again with Debug privilege...");
539 if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid_debug_privilege))
541 a->debug("Debug privilege is not supported.");
542 CloseHandle(kldbg_dev);
543 kldbg_dev = INVALID_HANDLE_VALUE;
547 if (!enable_privilege(luid_debug_privilege, &revert_token, &revert_only_privilege))
549 a->debug("Process does not have right to enable Debug privilege.");
550 CloseHandle(kldbg_dev);
551 kldbg_dev = INVALID_HANDLE_VALUE;
555 if (win32_kldbg_pci_bus_data(FALSE, 0, 0, 0, 0, 0, &id, sizeof(id), &ret_len) && ret_len == sizeof(id))
557 a->debug("Succeeded.");
558 debug_privilege_enabled = TRUE;
562 error = GetLastError();
564 a->debug("Cannot read PCI config space via Kernel Local Debugging Driver: %s.", win32_strerror(error));
566 CloseHandle(kldbg_dev);
567 kldbg_dev = INVALID_HANDLE_VALUE;
569 revert_privilege(luid_debug_privilege, revert_token, revert_only_privilege);
571 revert_only_privilege = FALSE;
576 win32_kldbg_detect(struct pci_access *a)
578 if (!win32_kldbg_setup(a))
585 win32_kldbg_init(struct pci_access *a)
587 if (!win32_kldbg_setup(a))
590 a->error("PCI config space via Kernel Local Debugging Driver cannot be accessed.");
595 win32_kldbg_cleanup(struct pci_access *a UNUSED)
597 if (kldbg_dev == INVALID_HANDLE_VALUE)
600 CloseHandle(kldbg_dev);
601 kldbg_dev = INVALID_HANDLE_VALUE;
603 if (debug_privilege_enabled)
605 revert_privilege(luid_debug_privilege, revert_token, revert_only_privilege);
607 revert_only_privilege = FALSE;
608 debug_privilege_enabled = FALSE;
618 char oem_table_id[8];
620 char asl_compiler_id[4];
621 u32 asl_compiler_revision;
633 win32_kldbg_scan(struct pci_access *a)
636 * There is no kldbg API to retrieve list of PCI segments. WinDBG pci plugin
637 * kext.dll loads debug symbols from pci.pdb file for kernel module pci.sys.
638 * Then it reads kernel memory which belongs to PciSegmentList local variable
639 * which is the first entry of struct _PCI_SEGMENT linked list. And then it
640 * iterates all entries in linked list and reads SegmentNumber for each entry.
642 * This is extremly ugly hack and does not work on systems without installed
643 * kernel debug symbol files.
645 * Do something less ugly. Retrieve ACPI MCFG table via GetSystemFirmwareTable
646 * and parse all PCI segment numbers from it. ACPI MCFG table contains PCIe
647 * ECAM definitions, so all PCI segment numbers.
650 UINT (*WINAPI MyGetSystemFirmwareTable)(DWORD FirmwareTableProviderSignature, DWORD FirmwareTableID, PVOID pFirmwareTableBuffer, DWORD BufferSize);
651 int i, allocations_count;
652 struct acpi_mcfg *mcfg;
658 /* Always scan PCI segment 0. */
659 pci_generic_scan_domain(a, 0);
661 kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
665 /* Function GetSystemFirmwareTable() is available since Windows Vista. */
666 MyGetSystemFirmwareTable = (void *)GetProcAddress(kernel32, "GetSystemFirmwareTable");
667 if (!MyGetSystemFirmwareTable)
670 /* 0x41435049 = 'ACPI', 0x4746434D = 'MCFG' */
671 size = MyGetSystemFirmwareTable(0x41435049, 0x4746434D, NULL, 0);
674 error = GetLastError();
675 if (error == ERROR_INVALID_FUNCTION) /* ACPI is not present, so only PCI segment 0 is available. */
677 else if (error == ERROR_NOT_FOUND) /* MCFG table is not present, so only PCI segment 0 is available. */
679 a->debug("Cannot retrieve ACPI MCFG table: %s.\n", win32_strerror(error));
683 mcfg = pci_malloc(a, size);
685 if (MyGetSystemFirmwareTable(0x41435049, 0x4746434D, mcfg, size) != size)
687 error = GetLastError();
688 a->debug("Cannot retrieve ACPI MCFG table: %s.\n", win32_strerror(error));
693 if (size < sizeof(*mcfg) || size < mcfg->length)
695 a->debug("ACPI MCFG table is broken.\n");
700 segments = pci_malloc(a, 0xFFFF/8);
701 memset(segments, 0, 0xFFFF/8);
703 /* Scan all MCFG allocations and set available PCI segments into bit field. */
704 allocations_count = (mcfg->length - ((unsigned char *)&mcfg->allocations - (unsigned char *)mcfg)) / sizeof(mcfg->allocations[0]);
705 for (i = 0; i < allocations_count; i++)
706 segments[mcfg->allocations[i].pci_segment / 8] |= 1 << (mcfg->allocations[i].pci_segment % 8);
708 /* Skip PCI segment 0 which was already scanned. */
709 for (i = 1; i < 0xFFFF; i++)
710 if (segments[i / 8] & (1 << (i % 8)))
711 pci_generic_scan_domain(a, i);
718 win32_kldbg_pci_bus_data(BOOL WriteBusData, USHORT SegmentNumber, BYTE BusNumber, BYTE DeviceNumber, BYTE FunctionNumber, USHORT Address, PVOID Buffer, ULONG BufferSize, LPDWORD Length)
721 SYSDBG_BUS_DATA sysdbg_cmd;
722 PCI_SLOT_NUMBER pci_slot;
723 PCI_SEGMENT_BUS_NUMBER pci_seg_bus;
725 memset(&pci_slot, 0, sizeof(pci_slot));
726 memset(&sysdbg_cmd, 0, sizeof(sysdbg_cmd));
727 memset(&pci_seg_bus, 0, sizeof(pci_seg_bus));
729 sysdbg_cmd.Address = Address;
730 sysdbg_cmd.Buffer = Buffer;
731 sysdbg_cmd.Request = BufferSize;
732 sysdbg_cmd.BusDataType = PCIConfiguration;
733 pci_seg_bus.u.bits.BusNumber = BusNumber;
734 pci_seg_bus.u.bits.SegmentNumber = SegmentNumber;
735 sysdbg_cmd.BusNumber = pci_seg_bus.u.AsULONG;
736 pci_slot.u.bits.DeviceNumber = DeviceNumber;
737 pci_slot.u.bits.FunctionNumber = FunctionNumber;
738 sysdbg_cmd.SlotNumber = pci_slot.u.AsULONG;
740 kldbg_cmd.Command = WriteBusData ? SysDbgWriteBusData : SysDbgReadBusData;
741 kldbg_cmd.Buffer = &sysdbg_cmd;
742 kldbg_cmd.BufferLength = sizeof(sysdbg_cmd);
745 return DeviceIoControl(kldbg_dev, IOCTL_KLDBG, &kldbg_cmd, sizeof(kldbg_cmd), &sysdbg_cmd, sizeof(sysdbg_cmd), Length, NULL);
749 win32_kldbg_read(struct pci_dev *d, int pos, byte *buf, int len)
753 if ((unsigned int)d->domain > 0xffff)
756 if (!win32_kldbg_pci_bus_data(FALSE, d->domain, d->bus, d->dev, d->func, pos, buf, len, &ret_len))
759 if (ret_len != (unsigned int)len)
766 win32_kldbg_write(struct pci_dev *d, int pos, byte *buf, int len)
770 if ((unsigned int)d->domain > 0xffff)
773 if (!win32_kldbg_pci_bus_data(TRUE, d->domain, d->bus, d->dev, d->func, pos, buf, len, &ret_len))
776 if (ret_len != (unsigned int)len)
782 struct pci_methods pm_win32_kldbg = {
784 "Win32 PCI config space access using Kernel Local Debugging Driver",
790 pci_generic_fill_info,
795 NULL /* cleanup_dev */