]> mj.ucw.cz Git - pciutils.git/commitdiff
libpci: Add new windows NT sysdbg implementation
authorPali Rohár <pali@kernel.org>
Sun, 2 Jan 2022 19:51:02 +0000 (20:51 +0100)
committerPali Rohár <pali@kernel.org>
Mon, 18 Apr 2022 18:29:28 +0000 (20:29 +0200)
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.

lib/Makefile
lib/configure
lib/init.c
lib/internal.h
lib/pci.h
lib/win32-sysdbg.c [new file with mode: 0644]

index b29a48fb8f9ca69b6e2d9f1753ddb07b261319c0..400c32d17df316b0e723532d73fc312f1f4aac4b 100644 (file)
@@ -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
index 45a416a7620e0763216d323ade801bdf90f6073e..2c74d9df64e8ab17e94fadd8ba9040aa8e8202cb 100755 (executable)
@@ -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
index f997b2bd83db39396c961bf0f01442aa9e52067c..de7d6f24a9e065c2e378e935a8c180ccbad9755b 100644 (file)
@@ -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,
index e0185dde7134f399fc5b18f0dc7356b67a3ea5bb..0a5dce462a541e7dfbc717975b6e0e641ef3e16f 100644 (file)
@@ -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;
index eeb4a55cb18db40c0bafd9babaa4a9454f357779..41a162b9a3970bf29178ae2373dbdf4bde94ac2d 100644 (file)
--- 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 (file)
index 0000000..5d0c07a
--- /dev/null
@@ -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 */
+};