]> mj.ucw.cz Git - pciutils.git/commitdiff
libpci: win32-cfgmgr32: Add support for accessing config space via other backend
authorPali Rohár <pali@kernel.org>
Fri, 30 Dec 2022 20:21:10 +0000 (21:21 +0100)
committerPali Rohár <pali@kernel.org>
Tue, 3 Jan 2023 23:48:12 +0000 (00:48 +0100)
Extend win32-cfgmgr32 backend and add a new option win32.cfgmethod for
specifying other backend for accessing PCI config space. There are more
config space access methods available on Windows and each is working only
sometimes (either requires special privileges or special setup).

So by default try to choose the first working one via order defined in pci
probe_sequence[] array. If none is available then emulate PCI config space
like before this change.

Function pci_init_v35() is extended and renamed to pci_init_internal() to
optionally do not throw errors and allow to specify one access method which
will be skipped in AUTO mode. This is used to prevent choosing win32-cfgmgr32
as config space access method for win32-cfgmgr32.

lib/init.c
lib/internal.h
lib/win32-cfgmgr32.c
pcilib.man

index e6efb8b2ab6b8cbeb841d15ba98bbf6d5de93114..1e35fc90afe1742bcc172d2e689192d2915fb799 100644 (file)
@@ -274,7 +274,7 @@ pci_alloc(void)
 }
 
 void
-pci_init_v35(struct pci_access *a)
+pci_init_internal(struct pci_access *a, int throw_errors, int skip_method)
 {
   if (!a->error)
     a->error = pci_generic_error;
@@ -288,7 +288,11 @@ pci_init_v35(struct pci_access *a)
   if (a->method)
     {
       if (a->method >= PCI_ACCESS_MAX || !pci_methods[a->method])
-       a->error("This access method is not supported.");
+        {
+          if (throw_errors)
+            a->error("This access method is not supported.");
+          return;
+        }
       a->methods = pci_methods[a->method];
     }
   else
@@ -299,6 +303,8 @@ pci_init_v35(struct pci_access *a)
          struct pci_methods *m = pci_methods[probe_sequence[i]];
          if (!m)
            continue;
+         if (skip_method == probe_sequence[i])
+           continue;
          a->debug("Trying method %s...", m->name);
          if (m->detect(a))
            {
@@ -310,12 +316,22 @@ pci_init_v35(struct pci_access *a)
          a->debug("...No.\n");
        }
       if (!a->methods)
-       a->error("Cannot find any working access method.");
+        {
+          if (throw_errors)
+            a->error("Cannot find any working access method.");
+          return;
+        }
     }
   a->debug("Decided to use %s\n", a->methods->name);
   a->methods->init(a);
 }
 
+void
+pci_init_v35(struct pci_access *a)
+{
+  pci_init_internal(a, 1, -1);
+}
+
 STATIC_ALIAS(void pci_init(struct pci_access *a), pci_init_v35(a));
 DEFINE_ALIAS(void pci_init_v30(struct pci_access *a), pci_init_v35);
 SYMBOL_VERSION(pci_init_v30, pci_init@LIBPCI_3.0);
index ed0e94f4b7efcc4c730f98fc6f0e90e55a7e9abe..b07b1a97c1cb12f2867bc5bc0f4824a5b8ecc5eb 100644 (file)
@@ -81,6 +81,7 @@ int pci_emulated_read(struct pci_dev *d, int pos, byte *buf, int len);
 void *pci_malloc(struct pci_access *, int);
 void pci_mfree(void *);
 char *pci_strdup(struct pci_access *a, const char *s);
+void pci_init_internal(struct pci_access *a, int throw_errors, int skip_method);
 
 void pci_init_v30(struct pci_access *a) VERSIONED_ABI;
 void pci_init_v35(struct pci_access *a) VERSIONED_ABI;
index a3404d1698b4075517b43ede5efb61744d6c52da..d63756bd8973662926a912bc9e93aab269c7fb64 100644 (file)
@@ -1531,7 +1531,8 @@ scan_devinst_id(struct pci_access *a, DEVINSTID_A devinst_id)
 
   d = pci_get_dev(a, domain, bus, dev, func);
   pci_link_dev(a, d);
-  d->no_config_access = 1;
+  if (!d->access->aux)
+    d->no_config_access = 1;
   d->aux = (void *)devinst;
 
   /* Parse device id part of devinst id and fill details into pci_dev. */
@@ -1633,6 +1634,12 @@ win32_cfgmgr32_scan(struct pci_access *a)
   pci_mfree(devinst_id_list);
 }
 
+static void
+win32_cfgmgr32_config(struct pci_access *a)
+{
+  pci_define_param(a, "win32.cfgmethod", "auto", "PCI config space access method");
+}
+
 static int
 win32_cfgmgr32_detect(struct pci_access *a)
 {
@@ -1666,43 +1673,129 @@ win32_cfgmgr32_detect(struct pci_access *a)
 }
 
 static void
-win32_cfgmgr32_fill_info(struct pci_dev *d UNUSED, unsigned int flags UNUSED)
+win32_cfgmgr32_fill_info(struct pci_dev *d, unsigned int flags)
 {
   /*
-   * All available flags were filled by win32_cfgmgr32_scan()
-   * and reading config space is not supported via cfgmgr32.
+   * All available flags were filled by win32_cfgmgr32_scan().
+   * Filling more flags is possible only from config space.
    */
+  if (!d->access->aux)
+    return;
+
+  pci_generic_fill_info(d, flags);
+}
+
+static int
+win32_cfgmgr32_read(struct pci_dev *d, int pos, byte *buf, int len)
+{
+  struct pci_access *a = d->access;
+  struct pci_access *acfg = a->aux;
+  struct pci_dev *dcfg = d->aux;
+
+  if (!acfg)
+    return pci_emulated_read(d, pos, buf, len);
+
+  if (!dcfg)
+    d->aux = dcfg = pci_get_dev(acfg, d->domain, d->bus, d->dev, d->func);
+
+  return pci_read_block(dcfg, pos, buf, len);
 }
 
 static int
-win32_cfgmgr32_write(struct pci_dev *d UNUSED, int pos UNUSED, byte *buf UNUSED, int len UNUSED)
+win32_cfgmgr32_write(struct pci_dev *d, int pos, byte *buf, int len)
+{
+  struct pci_access *a = d->access;
+  struct pci_access *acfg = a->aux;
+  struct pci_dev *dcfg = d->aux;
+
+  if (!acfg)
+    return 0;
+
+  if (!dcfg)
+    d->aux = dcfg = pci_get_dev(acfg, d->domain, d->bus, d->dev, d->func);
+
+  return pci_write_block(dcfg, pos, buf, len);
+}
+
+static void
+win32_cfgmgr32_cleanup_dev(struct pci_dev *d)
 {
-  /* Writing to config space is not supported via cfgmgr32. */
-  return 0;
+  struct pci_dev *dcfg = d->aux;
+
+  if (dcfg)
+    pci_free_dev(dcfg);
 }
 
 static void
-win32_cfgmgr32_init(struct pci_access *a UNUSED)
+win32_cfgmgr32_init(struct pci_access *a)
 {
+  char *cfgmethod = pci_get_param(a, "win32.cfgmethod");
+  struct pci_access *acfg;
+
+  if (strcmp(cfgmethod, "") == 0 ||
+      strcmp(cfgmethod, "auto") == 0)
+    {
+      acfg = pci_alloc();
+      acfg->method = PCI_ACCESS_AUTO;
+    }
+  else if (strcmp(cfgmethod, "none") != 0 &&
+           strcmp(cfgmethod, "win32-cfgmgr32") != 0)
+    {
+      int m = pci_lookup_method(cfgmethod);
+      if (m < 0)
+        a->error("Option win32.cfgmethod is set to unknown access method \"%s\".", cfgmethod);
+      acfg = pci_alloc();
+      acfg->method = m;
+    }
+  else
+    {
+      if (a->writeable)
+        a->error("Write access requested but option win32.cfgmethod was not set.");
+      return;
+    }
+
+  acfg->writeable = a->writeable;
+  acfg->buscentric = a->buscentric;
+  acfg->debugging = a->debugging;
+  acfg->error = a->error;
+  acfg->warning = a->warning;
+  acfg->debug = a->debug;
+
+  a->debug("Loading config space access method...\n");
+  pci_init_internal(acfg, 0, PCI_ACCESS_WIN32_CFGMGR32);
+  if (!acfg->methods)
+    {
+      pci_cleanup(acfg);
+      a->debug("Cannot find any working config space access method.\n");
+      if (a->writeable)
+        a->error("Write access requested but no usable access method.");
+      return;
+    }
+
+  a->aux = acfg;
 }
 
 static void
-win32_cfgmgr32_cleanup(struct pci_access *a UNUSED)
+win32_cfgmgr32_cleanup(struct pci_access *a)
 {
+  struct pci_access *acfg = a->aux;
+
+  if (acfg)
+    pci_cleanup(acfg);
 }
 
 struct pci_methods pm_win32_cfgmgr32 = {
   "win32-cfgmgr32",
   "Win32 device listing via Configuration Manager",
-  NULL,                                 /* config */
+  win32_cfgmgr32_config,
   win32_cfgmgr32_detect,
   win32_cfgmgr32_init,
   win32_cfgmgr32_cleanup,
   win32_cfgmgr32_scan,
   win32_cfgmgr32_fill_info,
-  pci_emulated_read,                    /* Reading of config space is not supported via cfgmgr32. */
+  win32_cfgmgr32_read,
   win32_cfgmgr32_write,
   NULL,                                 /* read_vpd */
   NULL,                                 /* init_dev */
-  NULL,                                 /* cleanup_dev */
+  win32_cfgmgr32_cleanup_dev,
 };
index b4c9f5617669fc770c9c1b19575b46c99b6823a2..3d1b49a4f6e1d5a845b544e41921c9397a1c3bb0 100644 (file)
@@ -86,8 +86,12 @@ Device listing on Windows systems using the Windows Configuration Manager
 via cfgmgr32.dll system library. This method does not require any special
 Administrator rights or privileges. Configuration Manager provides only basic
 information about devices, assigned resources and device tree structure. There
-is no access to the PCI configuration space but libpci provides read-only
-virtual emulation based on information from Configuration Manager. Starting
+is no access to the PCI configuration space but libpci either tries to use
+other access method to access configuration space or it provides read-only
+virtual emulation based on information from Configuration Manager. Other
+access method can be chosen by the
+.B win32.cfgmethod
+parameter. By default the first working one is selected (if any). 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
@@ -165,6 +169,15 @@ Physical addresses of memory-mapped I/O ports for Extended PCIe Intel configurat
 It has same format as
 .B mmio-conf1.addrs
 parameter.
+.TP
+.B win32.cfgmethod
+Config space access method for win32-cfgmgr32 on Windows systems. Value
+.I auto
+or emtpy string probe and choose the first access method which supports access
+to the config space access on Windows. Value
+.I win32-cfgmgr32
+only builds read-only virtual emulated config space with information from the
+Configuration Manager.
 
 .SS Parameters for resolving of ID's via DNS
 .TP