summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPali Rohár <pali@kernel.org>2022-01-02 20:51:02 +0100
committerPali Rohár <pali@kernel.org>2022-04-18 20:29:28 +0200
commit2d0af6fc3abc1e9bde968668eb27ebf18061d0da (patch)
treefdf56ab628b8002e121c4a056c4f121d9a1da631
parentd224993d423bcd0e88d8eacc0464bc3a66972e95 (diff)
downloadpciutils-2d0af6fc3abc1e9bde968668eb27ebf18061d0da.tar.gz
libpci: Add new windows NT sysdbg implementation
NT SysDbg interface allow access to the PCI config space. Only devices on the first domain are available and only first 256 bytes of the PCI config space can be accessed. Compared to intel-conf1 access, this API is race free as NT kernel serialize access to PCI I/O ports. This NT SysDbg API is used by the !pci command of 32-bit WinDbg kernel debugger for displaying PCI config space. Debug privilege is required to use this NT interface.
-rw-r--r--lib/Makefile5
-rwxr-xr-xlib/configure3
-rw-r--r--lib/init.c6
-rw-r--r--lib/internal.h2
-rw-r--r--lib/pci.h1
-rw-r--r--lib/win32-sysdbg.c304
6 files changed, 319 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
diff --git a/lib/init.c b/lib/init.c
index f997b2b..de7d6f2 100644
--- a/lib/init.c
+++ b/lib/init.c
@@ -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;
diff --git a/lib/pci.h b/lib/pci.h
index eeb4a55..41a162b 100644
--- a/lib/pci.h
+++ b/lib/pci.h
@@ -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 */
+};