summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPali Rohár <pali@kernel.org>2022-12-30 21:21:10 +0100
committerPali Rohár <pali@kernel.org>2023-01-04 00:48:12 +0100
commit848123ebb5a034c48797fb67ad1b19f36e2cb80c (patch)
tree3bcdcecb17653000d8f2afc3f33be1e5f0ab1f69
parent5abf19113f5fa88817d6f6e3f190c40205c46b8d (diff)
downloadpciutils-848123ebb5a034c48797fb67ad1b19f36e2cb80c.tar.gz
libpci: win32-cfgmgr32: Add support for accessing config space via other backend
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.
-rw-r--r--lib/init.c22
-rw-r--r--lib/internal.h1
-rw-r--r--lib/win32-cfgmgr32.c117
-rw-r--r--pcilib.man17
4 files changed, 140 insertions, 17 deletions
diff --git a/lib/init.c b/lib/init.c
index e6efb8b..1e35fc9 100644
--- a/lib/init.c
+++ b/lib/init.c
@@ -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);
diff --git a/lib/internal.h b/lib/internal.h
index ed0e94f..b07b1a9 100644
--- a/lib/internal.h
+++ b/lib/internal.h
@@ -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;
diff --git a/lib/win32-cfgmgr32.c b/lib/win32-cfgmgr32.c
index a3404d1..d63756b 100644
--- a/lib/win32-cfgmgr32.c
+++ b/lib/win32-cfgmgr32.c
@@ -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,
};
diff --git a/pcilib.man b/pcilib.man
index b4c9f56..3d1b49a 100644
--- a/pcilib.man
+++ b/pcilib.man
@@ -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