diff options
author | Martin Mares <mj@ucw.cz> | 2022-10-30 13:34:44 +0100 |
---|---|---|
committer | Martin Mares <mj@ucw.cz> | 2022-10-30 13:34:44 +0100 |
commit | a165591213039657f65a466ba1762e02a1c6e310 (patch) | |
tree | 036bbae9d57447b5929f182109e73e86d58bbcbb | |
parent | 3711e86f10643442d13f5e49d0c8ae73d380ab6a (diff) | |
parent | 963d7cb7cd7133ba8e4662e69c50177654311cf1 (diff) | |
download | pciutils-a165591213039657f65a466ba1762e02a1c6e310.tar.gz |
Merge remote-tracking branch 'pali/win32-sysdbg'
-rw-r--r-- | lib/Makefile | 5 | ||||
-rwxr-xr-x | lib/configure | 3 | ||||
-rw-r--r-- | lib/init.c | 6 | ||||
-rw-r--r-- | lib/internal.h | 2 | ||||
-rw-r--r-- | lib/pci.h | 1 | ||||
-rw-r--r-- | lib/win32-sysdbg.c | 304 | ||||
-rw-r--r-- | pcilib.man | 7 |
7 files changed, 326 insertions, 2 deletions
diff --git a/lib/Makefile b/lib/Makefile index b29a48f..400c32d 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -59,6 +59,10 @@ OBJS += emulated OBJS += win32-cfgmgr32 endif +ifdef PCI_HAVE_PM_WIN32_SYSDBG +OBJS += win32-sysdbg +endif + all: $(PCILIB) $(PCILIBPC) ifeq ($(SHARED),no) @@ -108,6 +112,7 @@ filter.o: filter.c $(INCL) nbsd-libpci.o: nbsd-libpci.c $(INCL) hurd.o: hurd.c $(INCL) win32-cfgmgr32.o: win32-cfgmgr32.c $(INCL) +win32-sysdbg.o: win32-sysdbg.c $(INCL) # MinGW32 toolchain has some required Win32 header files in /ddk subdirectory. # But these header files include another header files from /ddk subdirectory diff --git a/lib/configure b/lib/configure index 45a416a..2c74d9d 100755 --- a/lib/configure +++ b/lib/configure @@ -145,9 +145,10 @@ case $sys in EXEEXT=.exe ;; cygwin|windows) - echo_n " win32-cfgmgr32" + echo_n " win32-cfgmgr32 win32-sysdbg" echo >>$c '#define PCI_HAVE_64BIT_ADDRESS' echo >>$c '#define PCI_HAVE_PM_WIN32_CFGMGR32' + echo >>$c '#define PCI_HAVE_PM_WIN32_SYSDBG' # Warning: MinGW-w64 (incorrectly) provides cfgmgr32 functions # also in other import libraries, not only in libcfgmgr32.a. # So always set -lcfgmgr32 as a first library parameter which @@ -81,6 +81,11 @@ static struct pci_methods *pci_methods[PCI_ACCESS_MAX] = { #else NULL, #endif +#ifdef PCI_HAVE_PM_WIN32_SYSDBG + &pm_win32_sysdbg, +#else + NULL, +#endif }; // If PCI_ACCESS_AUTO is selected, we probe the access methods in this order @@ -96,6 +101,7 @@ static int probe_sequence[] = { PCI_ACCESS_SYLIXOS_DEVICE, PCI_ACCESS_HURD, PCI_ACCESS_WIN32_CFGMGR32, + PCI_ACCESS_WIN32_SYSDBG, // Low-level methods poking the hardware directly PCI_ACCESS_I386_TYPE1, PCI_ACCESS_I386_TYPE2, diff --git a/lib/internal.h b/lib/internal.h index e0185dd..0a5dce4 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -116,4 +116,4 @@ void pci_free_caps(struct pci_dev *); extern struct pci_methods pm_intel_conf1, pm_intel_conf2, pm_linux_proc, pm_fbsd_device, pm_aix_device, pm_nbsd_libpci, pm_obsd_device, pm_dump, pm_linux_sysfs, pm_darwin, pm_sylixos_device, pm_hurd, - pm_win32_cfgmgr32; + pm_win32_cfgmgr32, pm_win32_sysdbg; @@ -44,6 +44,7 @@ enum pci_access_type { PCI_ACCESS_SYLIXOS_DEVICE, /* SylixOS pci */ PCI_ACCESS_HURD, /* GNU/Hurd */ PCI_ACCESS_WIN32_CFGMGR32, /* Win32 cfgmgr32.dll */ + PCI_ACCESS_WIN32_SYSDBG, /* Win32 NT SysDbg */ PCI_ACCESS_MAX }; diff --git a/lib/win32-sysdbg.c b/lib/win32-sysdbg.c new file mode 100644 index 0000000..5d0c07a --- /dev/null +++ b/lib/win32-sysdbg.c @@ -0,0 +1,304 @@ +/* + * The PCI Library -- PCI config space access using NT SysDbg interface + * + * Copyright (c) 2022 Pali Rohár <pali@kernel.org> + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include <windows.h> + +#include "internal.h" +#include "i386-io-windows.h" + +#ifndef NTSTATUS +#define NTSTATUS LONG +#endif +#ifndef STATUS_UNSUCCESSFUL +#define STATUS_UNSUCCESSFUL (NTSTATUS)0xC0000001 +#endif +#ifndef STATUS_NOT_IMPLEMENTED +#define STATUS_NOT_IMPLEMENTED (NTSTATUS)0xC0000002 +#endif +#ifndef STATUS_INVALID_INFO_CLASS +#define STATUS_INVALID_INFO_CLASS (NTSTATUS)0xC0000003 +#endif +#ifndef STATUS_ACCESS_DENIED +#define STATUS_ACCESS_DENIED (NTSTATUS)0xC0000022 +#endif +#ifndef STATUS_DEBUGGER_INACTIVE +#define STATUS_DEBUGGER_INACTIVE (NTSTATUS)0xC0000354 +#endif + +#ifndef BUS_DATA_TYPE +#define BUS_DATA_TYPE LONG +#endif +#ifndef PCIConfiguration +#define PCIConfiguration (BUS_DATA_TYPE)4 +#endif + +#ifndef SYSDBG_COMMAND +#define SYSDBG_COMMAND ULONG +#endif +#ifndef SysDbgReadBusData +#define SysDbgReadBusData (SYSDBG_COMMAND)18 +#endif +#ifndef SysDbgWriteBusData +#define SysDbgWriteBusData (SYSDBG_COMMAND)19 +#endif + +#ifndef SYSDBG_BUS_DATA +typedef struct _SYSDBG_BUS_DATA { + ULONG Address; + PVOID Buffer; + ULONG Request; + BUS_DATA_TYPE BusDataType; + ULONG BusNumber; + ULONG SlotNumber; +} SYSDBG_BUS_DATA, *PSYSDBG_BUS_DATA; +#define SYSDBG_BUS_DATA SYSDBG_BUS_DATA +#endif + +#ifndef PCI_SLOT_NUMBER +typedef struct _PCI_SLOT_NUMBER { + union { + struct { + ULONG DeviceNumber:5; + ULONG FunctionNumber:3; + ULONG Reserved:24; + } bits; + ULONG AsULONG; + } u; +} PCI_SLOT_NUMBER, *PPCI_SLOT_NUMBER; +#define PCI_SLOT_NUMBER PCI_SLOT_NUMBER +#endif + +#ifdef NtSystemDebugControl +#undef NtSystemDebugControl +#endif +static NTSTATUS (NTAPI *MyNtSystemDebugControl)(SYSDBG_COMMAND Command, PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, ULONG OutputBufferLength, PULONG ReturnLength); +#define NtSystemDebugControl MyNtSystemDebugControl + +static BOOL debug_privilege_enabled; +static LUID luid_debug_privilege; +static BOOL revert_only_privilege; +static HANDLE revert_token; +static HMODULE ntdll; + +static int win32_sysdbg_initialized; + +static NTSTATUS +win32_sysdbg_pci_bus_data(BOOL WriteBusData, BYTE BusNumber, BYTE DeviceNumber, BYTE FunctionNumber, BYTE Address, PVOID Buffer, BYTE BufferSize, PULONG Length) +{ + SYSDBG_BUS_DATA sysdbg_cmd; + PCI_SLOT_NUMBER pci_slot; + + if (!NtSystemDebugControl) + return STATUS_NOT_IMPLEMENTED; + + memset(&pci_slot, 0, sizeof(pci_slot)); + memset(&sysdbg_cmd, 0, sizeof(sysdbg_cmd)); + + sysdbg_cmd.Address = Address; + sysdbg_cmd.Buffer = Buffer; + sysdbg_cmd.Request = BufferSize; + sysdbg_cmd.BusDataType = PCIConfiguration; + sysdbg_cmd.BusNumber = BusNumber; + pci_slot.u.bits.DeviceNumber = DeviceNumber; + pci_slot.u.bits.FunctionNumber = FunctionNumber; + sysdbg_cmd.SlotNumber = pci_slot.u.AsULONG; + + *Length = 0; + return NtSystemDebugControl(WriteBusData ? SysDbgWriteBusData : SysDbgReadBusData, &sysdbg_cmd, sizeof(sysdbg_cmd), NULL, 0, Length); +} + +static int +win32_sysdbg_setup(struct pci_access *a) +{ + UINT prev_error_mode; + NTSTATUS status; + ULONG ret_len; + DWORD id; + + if (win32_sysdbg_initialized) + return 1; + + prev_error_mode = change_error_mode(SEM_FAILCRITICALERRORS); + ntdll = LoadLibrary(TEXT("ntdll.dll")); + change_error_mode(prev_error_mode); + if (!ntdll) + { + a->debug("Cannot open ntdll.dll library."); + return 0; + } + + NtSystemDebugControl = (LPVOID)GetProcAddress(ntdll, "NtSystemDebugControl"); + if (!NtSystemDebugControl) + { + a->debug("Function NtSystemDebugControl() is not supported."); + FreeLibrary(ntdll); + ntdll = NULL; + return 0; + } + + /* + * Try to read PCI id register from PCI device 00:00.0. + * If this device does not exist and NT SysDbg API is working then + * NT SysDbg returns STATUS_UNSUCCESSFUL. + */ + status = win32_sysdbg_pci_bus_data(FALSE, 0, 0, 0, 0, &id, sizeof(id), &ret_len); + if ((status >= 0 && ret_len == sizeof(id)) || status == STATUS_UNSUCCESSFUL) + { + win32_sysdbg_initialized = 1; + return 1; + } + else if (status != STATUS_ACCESS_DENIED) + { + if (status == STATUS_NOT_IMPLEMENTED || status == STATUS_INVALID_INFO_CLASS) + a->debug("NT SysDbg is not supported."); + else if (status == STATUS_DEBUGGER_INACTIVE) + a->debug("NT SysDbg is disabled."); + else + a->debug("NT SysDbg returned error 0x%lx.", status); + FreeLibrary(ntdll); + ntdll = NULL; + NtSystemDebugControl = NULL; + return 0; + } + + a->debug("NT SysDbg returned Access Denied, trying again with Debug privilege..."); + + if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid_debug_privilege)) + { + a->debug("Debug privilege is not supported."); + FreeLibrary(ntdll); + ntdll = NULL; + NtSystemDebugControl = NULL; + return 0; + } + + if (!enable_privilege(luid_debug_privilege, &revert_token, &revert_only_privilege)) + { + a->debug("Cannot enable Debug privilege."); + FreeLibrary(ntdll); + ntdll = NULL; + NtSystemDebugControl = NULL; + return 0; + } + + status = win32_sysdbg_pci_bus_data(FALSE, 0, 0, 0, 0, &id, sizeof(id), &ret_len); + if ((status >= 0 && ret_len == sizeof(id)) || status == STATUS_UNSUCCESSFUL) + { + a->debug("Succeeded."); + debug_privilege_enabled = TRUE; + win32_sysdbg_initialized = 1; + return 1; + } + + revert_privilege(luid_debug_privilege, revert_token, revert_only_privilege); + revert_token = NULL; + revert_only_privilege = FALSE; + + FreeLibrary(ntdll); + ntdll = NULL; + NtSystemDebugControl = NULL; + + if (status == STATUS_NOT_IMPLEMENTED || status == STATUS_INVALID_INFO_CLASS) + a->debug("NT SysDbg is not supported."); + else if (status == STATUS_DEBUGGER_INACTIVE) + a->debug("NT SysDbg is disabled."); + else if (status == STATUS_ACCESS_DENIED) + a->debug("NT SysDbg returned Access Denied."); + else + a->debug("NT SysDbg returned error 0x%lx.", status); + + return 0; +} + +static int +win32_sysdbg_detect(struct pci_access *a) +{ + if (!win32_sysdbg_setup(a)) + return 0; + + return 1; +} + +static void +win32_sysdbg_init(struct pci_access *a) +{ + if (!win32_sysdbg_setup(a)) + { + a->debug("\n"); + a->error("NT SysDbg PCI Bus Data interface cannot be accessed."); + } +} + +static void +win32_sysdbg_cleanup(struct pci_access *a UNUSED) +{ + if (!win32_sysdbg_initialized) + return; + + if (debug_privilege_enabled) + { + revert_privilege(luid_debug_privilege, revert_token, revert_only_privilege); + revert_token = NULL; + revert_only_privilege = FALSE; + debug_privilege_enabled = FALSE; + } + + FreeLibrary(ntdll); + ntdll = NULL; + NtSystemDebugControl = NULL; + + win32_sysdbg_initialized = 0; +} + +static int +win32_sysdbg_read(struct pci_dev *d, int pos, byte *buf, int len) +{ + NTSTATUS status; + ULONG ret_len; + + if ((unsigned int)d->domain > 0 || (unsigned int)pos > 255 || (unsigned int)(pos+len) > 256) + return 0; + + status = win32_sysdbg_pci_bus_data(FALSE, d->bus, d->dev, d->func, pos, buf, len, &ret_len); + if (status < 0 || ret_len != (unsigned int)len) + return 0; + + return 1; +} + +static int +win32_sysdbg_write(struct pci_dev *d, int pos, byte *buf, int len) +{ + NTSTATUS status; + ULONG ret_len; + + if ((unsigned int)d->domain > 0 || (unsigned int)pos > 255 || (unsigned int)(pos+len) > 256) + return 0; + + status = win32_sysdbg_pci_bus_data(TRUE, d->bus, d->dev, d->func, pos, buf, len, &ret_len); + if (status < 0 || ret_len != (unsigned int)len) + return 0; + + return 1; +} + +struct pci_methods pm_win32_sysdbg = { + "win32-sysdbg", + "Win32 PCI config space access using NT SysDbg Bus Data interface", + NULL, /* config */ + win32_sysdbg_detect, + win32_sysdbg_init, + win32_sysdbg_cleanup, + pci_generic_scan, + pci_generic_fill_info, + win32_sysdbg_read, + win32_sysdbg_write, + NULL, /* read_vpd */ + NULL, /* init_dev */ + NULL /* cleanup_dev */ +}; @@ -76,6 +76,13 @@ is no access to the PCI configuration space but libpci provides read-only virtual emulation based on information from Configuration Manager. Starting with Windows 8 (NT 6.2) it is not possible to retrieve resources from 32-bit application or library on 64-bit system. +.TP +.B win32-sysdbg +Access to the PCI configuration space via NT SysDbg interface on Windows +systems. Process needs to have Debug privilege, which local Administrators +have by default. Not available on 64-bit systems and neither on recent 32-bit +systems. Only devices from the first domain are accessible and only first +256 bytes of the PCI configuration space is accessible via this method. .SH PARAMETERS |