summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/.gitignore3
-rw-r--r--lib/Makefile177
-rw-r--r--lib/access.c270
-rw-r--r--lib/aix-device.c276
-rw-r--r--lib/aos-expansion.c237
-rw-r--r--lib/caps.c148
-rwxr-xr-xlib/configure365
-rw-r--r--lib/darwin.c213
-rw-r--r--lib/dump.c194
-rw-r--r--lib/ecam.c1104
-rw-r--r--lib/emulated.c301
-rw-r--r--lib/fbsd-device.c366
-rw-r--r--lib/filter.c334
-rw-r--r--lib/generic.c259
-rw-r--r--lib/header.h1579
-rw-r--r--lib/hurd.c372
-rw-r--r--lib/i386-io-access.h75
-rw-r--r--lib/i386-io-beos.h68
-rw-r--r--lib/i386-io-cygwin.h33
-rw-r--r--lib/i386-io-djgpp.h40
-rw-r--r--lib/i386-io-haiku.h142
-rw-r--r--lib/i386-io-hurd.h37
-rw-r--r--lib/i386-io-linux.h77
-rw-r--r--lib/i386-io-openbsd.h54
-rw-r--r--lib/i386-io-sunos.h35
-rw-r--r--lib/i386-io-windows.h248
-rw-r--r--lib/i386-ports.c327
-rw-r--r--lib/init.c564
-rw-r--r--lib/internal.h148
-rw-r--r--lib/libpci.pc.in11
-rw-r--r--lib/libpci.ver100
-rw-r--r--lib/mmio-ports.c432
-rw-r--r--lib/names-cache.c266
-rw-r--r--lib/names-hash.c130
-rw-r--r--lib/names-hwdb.c118
-rw-r--r--lib/names-net.c252
-rw-r--r--lib/names-parse.c253
-rw-r--r--lib/names.c229
-rw-r--r--lib/names.h77
-rw-r--r--lib/nbsd-libpci.c159
-rw-r--r--lib/obsd-device.c154
-rw-r--r--lib/params.c104
-rw-r--r--lib/pci.h312
-rw-r--r--lib/physmem-access.h52
-rw-r--r--lib/physmem-posix.c95
-rw-r--r--lib/physmem.h19
-rw-r--r--lib/proc.c230
-rw-r--r--lib/sylixos-device.c160
-rw-r--r--lib/sysdep.h129
-rw-r--r--lib/sysfs.c609
-rw-r--r--lib/types.h67
-rwxr-xr-xlib/ver2def.pl47
-rw-r--r--lib/win32-cfgmgr32.c1765
-rw-r--r--lib/win32-helpers.c1387
-rw-r--r--lib/win32-helpers.h13
-rw-r--r--lib/win32-kldbg.c731
-rw-r--r--lib/win32-sysdbg.c306
-rw-r--r--lib/winrsrc.rc.in39
58 files changed, 16292 insertions, 0 deletions
diff --git a/lib/.gitignore b/lib/.gitignore
new file mode 100644
index 0000000..9bed745
--- /dev/null
+++ b/lib/.gitignore
@@ -0,0 +1,3 @@
+config.h
+config.mk
+libpci.pc
diff --git a/lib/Makefile b/lib/Makefile
new file mode 100644
index 0000000..33698db
--- /dev/null
+++ b/lib/Makefile
@@ -0,0 +1,177 @@
+# Makefile for The PCI Library
+# (c) 1999--2014 Martin Mares <mj@ucw.cz>
+
+# Expects to be invoked from the top-level Makefile and uses lots of its variables.
+
+OBJS=init access generic dump names filter names-hash names-parse names-net names-cache names-hwdb params caps
+INCL=internal.h pci.h config.h header.h sysdep.h types.h
+
+ifdef PCI_HAVE_PM_LINUX_SYSFS
+OBJS += sysfs
+endif
+
+ifdef PCI_HAVE_PM_LINUX_PROC
+OBJS += proc
+endif
+
+ifdef PCI_HAVE_PM_INTEL_CONF
+OBJS += i386-ports
+endif
+
+ifdef PCI_HAVE_PM_MMIO_CONF
+OBJS += mmio-ports
+PCI_USE_PHYSMEM = 1
+endif
+
+ifdef PCI_HAVE_PM_ECAM
+OBJS += ecam
+PCI_USE_PHYSMEM = 1
+endif
+
+ifdef PCI_HAVE_PM_DUMP
+OBJS += dump
+endif
+
+ifdef PCI_HAVE_PM_FBSD_DEVICE
+OBJS += fbsd-device
+CFLAGS += -I/usr/src/sys
+ifdef FREEBSD_SYS
+CFLAGS += -I${FREEBSD_SYS}
+endif
+endif
+
+ifdef PCI_HAVE_PM_OBSD_DEVICE
+OBJS += obsd-device
+endif
+
+ifdef PCI_HAVE_PM_AIX_DEVICE
+OBJS += aix-device
+endif
+
+ifdef PCI_HAVE_PM_NBSD_LIBPCI
+OBJS += nbsd-libpci
+endif
+
+ifdef PCI_HAVE_PM_DARWIN_DEVICE
+OBJS += darwin
+endif
+
+ifdef PCI_HAVE_PM_SYLIXOS_DEVICE
+OBJS += sylixos-device
+endif
+
+ifdef PCI_HAVE_PM_HURD_CONF
+OBJS += hurd
+endif
+
+ifdef PCI_HAVE_PM_WIN32_CFGMGR32
+OBJS += emulated
+OBJS += win32-cfgmgr32
+endif
+
+ifdef PCI_HAVE_PM_WIN32_KLDBG
+OBJS += win32-kldbg
+endif
+
+ifdef PCI_HAVE_PM_WIN32_SYSDBG
+OBJS += win32-sysdbg
+endif
+
+ifdef PCI_OS_WINDOWS
+OBJS += win32-helpers
+endif
+
+ifdef PCI_USE_PHYSMEM
+ifndef PCI_OS_WINDOWS
+ifndef PCI_OS_DJGPP
+OBJS += physmem-posix
+endif
+endif
+endif
+
+ifdef PCI_HAVE_PM_AOS_EXPANSION
+OBJS += aos-expansion
+endif
+
+all: $(PCILIB) $(PCILIBPC)
+
+ifeq ($(SHARED),no)
+$(PCILIB): $(addsuffix .o,$(OBJS))
+ rm -f $@
+ $(AR) rcs $@ $^
+ $(RANLIB) $@
+else
+ifeq ($(LIBEXT),dll)
+all: $(PCIIMPDEF) $(PCIIMPLIB)
+build.def: $(PCIIMPDEF)
+$(PCIIMPDEF): libpci.ver ver2def.pl
+ perl ver2def.pl libpci.ver $(PCILIB) build.def $(PCIIMPDEF)
+$(PCIIMPLIB): $(PCIIMPDEF)
+ $(DLLTOOL) --input-def $< --output-lib $@
+comma := ,
+dllrsrc.rc: winrsrc.rc.in
+ sed <$< >$@ -e 's,@PCILIB_VERSION@,$(PCILIB_VERSION),' \
+ -e 's,@PCILIB_VERSION_WINRC@,$(subst .,\$(comma),$(PCILIB_VERSION).0),' \
+ -e 's,@FILENAME@,$(PCILIB),' \
+ -e 's,@DESCRIPTION@,libpci,' \
+ -e 's,@LIBRARY_BUILD@,1,' \
+ -e 's,@DEBUG_BUILD@,$(if $(findstring -g,$(CFLAGS)),1,0),'
+dllrsrc.o: dllrsrc.rc
+ $(WINDRES) --input=$< --output=$@ --input-format=rc --output-format=coff
+OBJS += dllrsrc
+endif
+CFLAGS += -fPIC -fvisibility=hidden
+$(PCILIB): $(addsuffix .o,$(OBJS))
+ $(CC) -shared $(CFLAGS) $(LDFLAGS) $(PCILIB_LDFLAGS) -o $@ $^ $(LIB_LDLIBS)
+ifeq ($(LIBEXT),dll)
+$(PCILIB): build.def
+endif
+endif
+
+$(PCILIBPC): libpci.pc.in
+ sed <$< >$@ -e 's,@PREFIX@,$(PREFIX),' \
+ -e 's,@INCDIR@,$(INCDIR),' \
+ -e 's,@LIBDIR@,$(LIBDIR),' \
+ -e 's,@IDSDIR@,$(IDSDIR),' \
+ -e 's,@VERSION@,$(VERSION),' \
+ -e 's,@LDLIBS@,$(LDLIBS),' \
+ -e 's,@WITH_LIBS@,$(WITH_LIBS),'
+
+init.o: init.c $(INCL)
+access.o: access.c $(INCL)
+params.o: params.c $(INCL)
+i386-ports.o: i386-ports.c $(INCL) i386-io-access.h i386-io-beos.h i386-io-cygwin.h i386-io-djgpp.h i386-io-haiku.h i386-io-hurd.h i386-io-linux.h i386-io-openbsd.h i386-io-sunos.h i386-io-windows.h
+mmio-ports.o: mmio-ports.c $(INCL) physmem.h physmem-access.h
+ecam.o: ecam.c $(INCL) physmem.h physmem-access.h
+proc.o: proc.c $(INCL)
+sysfs.o: sysfs.c $(INCL)
+generic.o: generic.c $(INCL)
+emulated.o: emulated.c $(INCL)
+syscalls.o: syscalls.c $(INCL)
+obsd-device.o: obsd-device.c $(INCL)
+fbsd-device.o: fbsd-device.c $(INCL)
+aix-device.o: aix-device.c $(INCL)
+dump.o: dump.c $(INCL)
+names.o: names.c $(INCL) names.h
+names-cache.o: names-cache.c $(INCL) names.h
+names-hash.o: names-hash.c $(INCL) names.h
+names-net.o: names-net.c $(INCL) names.h
+names-parse.o: names-parse.c $(INCL) names.h
+names-hwdb.o: names-hwdb.c $(INCL) names.h
+filter.o: filter.c $(INCL)
+nbsd-libpci.o: nbsd-libpci.c $(INCL)
+hurd.o: hurd.c $(INCL)
+win32-helpers.o: win32-helpers.c $(INCL) win32-helpers.h
+win32-cfgmgr32.o: win32-cfgmgr32.c $(INCL) win32-helpers.h
+win32-kldbg.o: win32-kldbg.c $(INCL) win32-helpers.h
+win32-sysdbg.o: win32-sysdbg.c $(INCL) win32-helpers.h
+i386-io-windows.h: win32-helpers.h
+
+# MinGW32 toolchain has some required Win32 header files in /ddk subdirectory.
+# But these header files include another header files from /ddk subdirectory
+# and expect that build system has already set /ddk subdirectory into includes.
+# So include /ddk subdirectory of each system predefined include path via -I.
+ifdef PCI_HAVE_PM_WIN32_CFGMGR32
+DDKCFLAGS:=$(shell echo | $(CC) $(CFLAGS) -E -Wp,-v -o /dev/null - 2>&1 | sed -n 's/^ \(.*\)/-I\1\/ddk/p')
+win32-cfgmgr32.o: override CFLAGS+=$(DDKCFLAGS)
+endif
diff --git a/lib/access.c b/lib/access.c
new file mode 100644
index 0000000..7d66123
--- /dev/null
+++ b/lib/access.c
@@ -0,0 +1,270 @@
+/*
+ * The PCI Library -- User Access
+ *
+ * Copyright (c) 1997--2022 Martin Mares <mj@ucw.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include "internal.h"
+
+void
+pci_scan_bus(struct pci_access *a)
+{
+ a->methods->scan(a);
+}
+
+struct pci_dev *
+pci_alloc_dev(struct pci_access *a)
+{
+ struct pci_dev *d = pci_malloc(a, sizeof(struct pci_dev));
+
+ memset(d, 0, sizeof(*d));
+ d->access = a;
+ d->methods = a->methods;
+ d->hdrtype = -1;
+ d->numa_node = -1;
+ if (d->methods->init_dev)
+ d->methods->init_dev(d);
+ return d;
+}
+
+int
+pci_link_dev(struct pci_access *a, struct pci_dev *d)
+{
+ d->next = a->devices;
+ a->devices = d;
+
+ /*
+ * Applications compiled with older versions of libpci do not expect
+ * 32-bit domain numbers. To keep them working, we keep a 16-bit
+ * version of the domain number at the previous location in struct
+ * pci_dev. This will keep backward compatibility on systems which
+ * don't require large domain numbers.
+ */
+ if (d->domain > 0xffff)
+ d->domain_16 = 0xffff;
+ else
+ d->domain_16 = d->domain;
+
+ return 1;
+}
+
+struct pci_dev *
+pci_get_dev(struct pci_access *a, int domain, int bus, int dev, int func)
+{
+ struct pci_dev *d = pci_alloc_dev(a);
+
+ d->domain = domain;
+ d->bus = bus;
+ d->dev = dev;
+ d->func = func;
+ return d;
+}
+
+static void
+pci_free_properties(struct pci_dev *d)
+{
+ struct pci_property *p;
+
+ while (p = d->properties)
+ {
+ d->properties = p->next;
+ pci_mfree(p);
+ }
+}
+
+void pci_free_dev(struct pci_dev *d)
+{
+ if (d->methods->cleanup_dev)
+ d->methods->cleanup_dev(d);
+
+ pci_free_caps(d);
+ pci_free_properties(d);
+ pci_mfree(d);
+}
+
+static inline void
+pci_read_data(struct pci_dev *d, void *buf, int pos, int len)
+{
+ if (pos & (len-1))
+ d->access->error("Unaligned read: pos=%02x, len=%d", pos, len);
+ if (pos + len <= d->cache_len)
+ memcpy(buf, d->cache + pos, len);
+ else if (!d->methods->read(d, pos, buf, len))
+ memset(buf, 0xff, len);
+}
+
+byte
+pci_read_byte(struct pci_dev *d, int pos)
+{
+ byte buf;
+ pci_read_data(d, &buf, pos, 1);
+ return buf;
+}
+
+word
+pci_read_word(struct pci_dev *d, int pos)
+{
+ word buf;
+ pci_read_data(d, &buf, pos, 2);
+ return le16_to_cpu(buf);
+}
+
+u32
+pci_read_long(struct pci_dev *d, int pos)
+{
+ u32 buf;
+ pci_read_data(d, &buf, pos, 4);
+ return le32_to_cpu(buf);
+}
+
+int
+pci_read_block(struct pci_dev *d, int pos, byte *buf, int len)
+{
+ return d->methods->read(d, pos, buf, len);
+}
+
+int
+pci_read_vpd(struct pci_dev *d, int pos, byte *buf, int len)
+{
+ return d->methods->read_vpd ? d->methods->read_vpd(d, pos, buf, len) : 0;
+}
+
+static inline int
+pci_write_data(struct pci_dev *d, void *buf, int pos, int len)
+{
+ if (pos & (len-1))
+ d->access->error("Unaligned write: pos=%02x,len=%d", pos, len);
+ if (pos + len <= d->cache_len)
+ memcpy(d->cache + pos, buf, len);
+ return d->methods->write(d, pos, buf, len);
+}
+
+int
+pci_write_byte(struct pci_dev *d, int pos, byte data)
+{
+ return pci_write_data(d, &data, pos, 1);
+}
+
+int
+pci_write_word(struct pci_dev *d, int pos, word data)
+{
+ word buf = cpu_to_le16(data);
+ return pci_write_data(d, &buf, pos, 2);
+}
+
+int
+pci_write_long(struct pci_dev *d, int pos, u32 data)
+{
+ u32 buf = cpu_to_le32(data);
+ return pci_write_data(d, &buf, pos, 4);
+}
+
+int
+pci_write_block(struct pci_dev *d, int pos, byte *buf, int len)
+{
+ if (pos < d->cache_len)
+ {
+ int l = (pos + len >= d->cache_len) ? (d->cache_len - pos) : len;
+ memcpy(d->cache + pos, buf, l);
+ }
+ return d->methods->write(d, pos, buf, len);
+}
+
+static void
+pci_reset_properties(struct pci_dev *d)
+{
+ d->known_fields = 0;
+ d->phy_slot = NULL;
+ d->module_alias = NULL;
+ d->label = NULL;
+ pci_free_caps(d);
+ pci_free_properties(d);
+}
+
+int
+pci_fill_info_v38(struct pci_dev *d, int flags)
+{
+ unsigned int uflags = flags;
+ if (uflags & PCI_FILL_RESCAN)
+ {
+ uflags &= ~PCI_FILL_RESCAN;
+ pci_reset_properties(d);
+ }
+ if (uflags & ~d->known_fields)
+ d->methods->fill_info(d, uflags);
+ return d->known_fields;
+}
+
+/* In version 3.1, pci_fill_info got new flags => versioned alias */
+/* In versions 3.2, 3.3, 3.4, 3.5 and 3.8, the same has happened */
+STATIC_ALIAS(int pci_fill_info(struct pci_dev *d, int flags), pci_fill_info_v38(d, flags));
+DEFINE_ALIAS(int pci_fill_info_v30(struct pci_dev *d, int flags), pci_fill_info_v38);
+DEFINE_ALIAS(int pci_fill_info_v31(struct pci_dev *d, int flags), pci_fill_info_v38);
+DEFINE_ALIAS(int pci_fill_info_v32(struct pci_dev *d, int flags), pci_fill_info_v38);
+DEFINE_ALIAS(int pci_fill_info_v33(struct pci_dev *d, int flags), pci_fill_info_v38);
+DEFINE_ALIAS(int pci_fill_info_v34(struct pci_dev *d, int flags), pci_fill_info_v38);
+DEFINE_ALIAS(int pci_fill_info_v35(struct pci_dev *d, int flags), pci_fill_info_v38);
+SYMBOL_VERSION(pci_fill_info_v30, pci_fill_info@LIBPCI_3.0);
+SYMBOL_VERSION(pci_fill_info_v31, pci_fill_info@LIBPCI_3.1);
+SYMBOL_VERSION(pci_fill_info_v32, pci_fill_info@LIBPCI_3.2);
+SYMBOL_VERSION(pci_fill_info_v33, pci_fill_info@LIBPCI_3.3);
+SYMBOL_VERSION(pci_fill_info_v34, pci_fill_info@LIBPCI_3.4);
+SYMBOL_VERSION(pci_fill_info_v35, pci_fill_info@LIBPCI_3.5);
+SYMBOL_VERSION(pci_fill_info_v38, pci_fill_info@@LIBPCI_3.8);
+
+void
+pci_setup_cache(struct pci_dev *d, byte *cache, int len)
+{
+ d->cache = cache;
+ d->cache_len = len;
+}
+
+char *
+pci_set_property(struct pci_dev *d, u32 key, char *value)
+{
+ struct pci_property *p;
+ struct pci_property **pp = &d->properties;
+
+ while (p = *pp)
+ {
+ if (p->key == key)
+ {
+ *pp = p->next;
+ pci_mfree(p);
+ }
+ else
+ pp = &p->next;
+ }
+
+ if (!value)
+ return NULL;
+
+ p = pci_malloc(d->access, sizeof(*p) + strlen(value));
+ *pp = p;
+ p->next = NULL;
+ p->key = key;
+ strcpy(p->value, value);
+
+ return p->value;
+}
+
+char *
+pci_get_string_property(struct pci_dev *d, u32 prop)
+{
+ struct pci_property *p;
+
+ for (p = d->properties; p; p = p->next)
+ if (p->key == prop)
+ return p->value;
+
+ return NULL;
+}
diff --git a/lib/aix-device.c b/lib/aix-device.c
new file mode 100644
index 0000000..028d92a
--- /dev/null
+++ b/lib/aix-device.c
@@ -0,0 +1,276 @@
+/*
+ * The PCI Library -- AIX /dev/pci[0-n] access
+ *
+ * Copyright (c) 1999 Jari Kirma <kirma@cs.hut.fi>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/*
+ * Read functionality of this driver is briefly tested, and seems
+ * to supply basic information correctly, but I promise no more.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <sys/types.h>
+#include <sys/mdio.h>
+
+#include "internal.h"
+
+#define AIX_LSDEV_CMD "/usr/sbin/lsdev -C -c bus -t pci\\* -S available -F name"
+#define AIX_ODMGET_CMD \
+ "/usr/bin/odmget -q 'name=%s and attribute=bus_number' CuAt | \
+ /usr/bin/awk '$1 == \"value\" { print $3 }'"
+
+
+/* AIX PCI bus device information */
+
+typedef struct aix_pci_bus {
+ char *bus_name;
+ int bus_number;
+ int bus_fd;
+} aix_pci_bus;
+
+#define PCI_BUS_MAX 16 /* arbitrary choice */
+static aix_pci_bus pci_buses[PCI_BUS_MAX];
+static int pci_bus_count = 0;
+
+
+/* Utility Routines */
+
+static aix_pci_bus *
+aix_find_bus(struct pci_access *a, int bus_number)
+{
+ int i;
+
+ for (i = 0; i < pci_bus_count; i++)
+ {
+ if (pci_buses[i].bus_number == bus_number)
+ {
+ return &pci_buses[i];
+ }
+ }
+
+ a->error("aix_find_bus: bus number %d not found", bus_number);
+}
+
+static int
+aix_bus_open(struct pci_dev *d)
+{
+ struct pci_access *a = d->access;
+ aix_pci_bus *bp = aix_find_bus(a, d->bus);
+
+ if (bp->bus_fd < 0)
+ {
+ char devbuf[256];
+ int mode = a->writeable ? O_RDWR : O_RDONLY;
+
+ snprintf(devbuf, sizeof (devbuf), "/dev/%s", bp->bus_name);
+ bp->bus_fd = open(devbuf, mode, 0);
+ if (bp->bus_fd < 0)
+ a->error("aix_open_bus: %s open failed", devbuf);
+ }
+
+ return bp->bus_fd;
+}
+
+static int
+aix_bus_number(char *name)
+{
+ int bus_number;
+ FILE *odmget_pipe;
+ char command[256];
+ char buf[256];
+ char *bp;
+ char *ep;
+
+ snprintf(command, sizeof (command), AIX_ODMGET_CMD, name);
+ odmget_pipe = popen(command, "r");
+ if (odmget_pipe == NULL)
+ {
+ /* popen failed */
+ return -1;
+ }
+
+ if (fgets(buf, sizeof (buf) - 1, odmget_pipe) != NULL)
+ {
+ bp = buf + 1; /* skip leading double quote */
+ bus_number = strtol(bp, &ep, 0);
+ if (bp == ep)
+ {
+ /* strtol failed */
+ bus_number = -1;
+ }
+ }
+ else
+ {
+ /* first PCI bus_number is not recorded in ODM CuAt; default to 0 */
+ bus_number = 0;
+ }
+
+ (void) pclose(odmget_pipe);
+
+ return bus_number;
+}
+
+
+/* Method entries */
+
+static int
+aix_detect(struct pci_access *a)
+{
+ int len;
+ int mode = a->writeable ? W_OK : R_OK;
+ char *command = AIX_LSDEV_CMD;
+ FILE *lsdev_pipe;
+ char buf[256];
+ char *name;
+
+ lsdev_pipe = popen(command, "r");
+ if (lsdev_pipe == NULL)
+ {
+ a->error("aix_config: popen(\"%s\") failed", command);
+ }
+
+ while (fgets(buf, sizeof (buf) - 1, lsdev_pipe) != NULL)
+ {
+ len = strlen(buf);
+ while (buf[len-1] == '\n' || buf[len-1] == '\r')
+ len--;
+ buf[len] = '\0'; /* clobber the newline */
+
+ name = (char *) pci_malloc(a, len + 1);
+ strcpy(name, buf);
+ pci_buses[pci_bus_count].bus_name = name;
+ pci_buses[pci_bus_count].bus_number = 0;
+ pci_buses[pci_bus_count].bus_fd = -1;
+ if (!pci_bus_count)
+ a->debug("...using %s", name);
+ else
+ a->debug(", %s", name);
+ pci_bus_count++;
+ if (pci_bus_count >= PCI_BUS_MAX)
+ break;
+ }
+
+ (void) pclose(lsdev_pipe);
+
+ return pci_bus_count;
+}
+
+static void
+aix_init(struct pci_access *a)
+{
+ char *name;
+ int i;
+
+ for (i = 0; i < pci_bus_count; i++)
+ {
+ name = pci_buses[i].bus_name;
+ pci_buses[i].bus_number = aix_bus_number(name);
+ }
+}
+
+static void
+aix_cleanup(struct pci_access *a)
+{
+ aix_pci_bus *bp;
+
+ while (pci_bus_count-- > 0)
+ {
+ bp = &pci_buses[pci_bus_count];
+ (void) free(bp->bus_name);
+ if (bp->bus_fd >= 0)
+ {
+ (void) close(bp->bus_fd);
+ bp->bus_fd = -1;
+ }
+ }
+}
+
+void
+aix_scan(struct pci_access *a)
+{
+ int i;
+ int bus_number;
+ byte busmap[256];
+
+ memset(busmap, 0, sizeof(busmap));
+ for (i = 0; i < pci_bus_count; i++)
+ {
+ bus_number = pci_buses[i].bus_number;
+ if (!busmap[bus_number])
+ {
+ pci_generic_scan_bus(a, busmap, 0, bus_number);
+ }
+ }
+}
+
+static int
+aix_read(struct pci_dev *d, int pos, byte *buf, int len)
+{
+ struct mdio mdio;
+ int fd;
+
+ if (d->domain || pos + len > 256)
+ return 0;
+
+ fd = aix_bus_open(d);
+ mdio.md_addr = (ulong) pos;
+ mdio.md_size = len;
+ mdio.md_incr = MV_BYTE;
+ mdio.md_data = (char *) buf;
+ mdio.md_sla = PCI_DEVFN(d->dev, d->func);
+
+ if (ioctl(fd, MIOPCFGET, &mdio) < 0)
+ d->access->error("aix_read: ioctl(MIOPCFGET) failed");
+
+ return 1;
+}
+
+static int
+aix_write(struct pci_dev *d, int pos, byte *buf, int len)
+{
+ struct mdio mdio;
+ int fd;
+
+ if (d->domain || pos + len > 256)
+ return 0;
+
+ fd = aix_bus_open(d);
+ mdio.md_addr = (ulong) pos;
+ mdio.md_size = len;
+ mdio.md_incr = MV_BYTE;
+ mdio.md_data = (char *) buf;
+ mdio.md_sla = PCI_DEVFN(d->dev, d->func);
+
+ if (ioctl(fd, MIOPCFPUT, &mdio) < 0)
+ {
+ d->access->error("aix_write: ioctl(MIOPCFPUT) failed");
+ }
+
+ return 1;
+}
+
+struct pci_methods pm_aix_device = {
+ "aix-device",
+ "AIX /dev/pci[0-n]",
+ NULL,
+ aix_detect,
+ aix_init,
+ aix_cleanup,
+ aix_scan,
+ pci_generic_fill_info,
+ aix_read,
+ aix_write,
+ NULL, /* read_vpd */
+ NULL, /* dev_init */
+ NULL /* dev_cleanup */
+};
diff --git a/lib/aos-expansion.c b/lib/aos-expansion.c
new file mode 100644
index 0000000..3e11f47
--- /dev/null
+++ b/lib/aos-expansion.c
@@ -0,0 +1,237 @@
+/*
+ * The PCI Library -- Configuration Access via AmigaOS 4.x expansion.library
+ *
+ * Copyright (c) 2024 Olrick Lefebvre <olrick.lefebvre@olrick.fr>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#define _GNU_SOURCE
+
+#include <proto/exec.h>
+#include <exec/types.h>
+#include <proto/expansion.h>
+#include <interfaces/expansion.h>
+
+
+// have to undef PCI values to avoid redefine warning
+#undef PCI_BASE_ADDRESS_MEM_MASK
+#undef PCI_BASE_ADDRESS_IO_MASK
+#undef PCI_ROM_ADDRESS_MASK
+#include <expansion/pci.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/unistd.h>
+
+#include "internal.h"
+
+
+// custom Amiga x.y version tag
+#define VERSTAG "\0$VER: pciutils " PCILIB_VERSION " (" PCILIB_DATE_AMIGAOS ") AmigaOS4 port"
+
+
+/*** AmigaOS access support ***/
+
+typedef struct _PCIAccess {
+ struct ExpansionBase *expansion;
+ struct PCIIFace *ipci;
+} PCIAccess;
+
+static void
+aos_close_pci_interface(struct pci_access *a)
+{
+ PCIAccess *pci = a->backend_data;
+
+ if (pci) {
+ if (pci->expansion) {
+ if (pci->ipci) {
+ IExec->DropInterface((struct Interface *)pci->ipci);
+ pci->ipci = NULL;
+ }
+ IExec->CloseLibrary((struct Library *)pci->expansion);
+ pci->expansion = NULL;
+ }
+ pci_mfree(pci);
+ a->backend_data = NULL;
+ }
+}
+
+static BOOL
+aos_open_pci_interface(struct pci_access *a)
+{
+ PCIAccess *pci;
+ BOOL res = FALSE;
+
+ if (NULL == a->backend_data) {
+ pci = pci_malloc(a, sizeof(PCIAccess));
+ a->backend_data = pci;
+ pci->expansion = (struct ExpansionBase *)IExec->OpenLibrary("expansion.library", 0);
+ if(NULL == pci->expansion) {
+ a->warning("Unable to open expansion.library");
+ aos_close_pci_interface(a);
+ } else {
+ pci->ipci = (struct PCIIFace *)IExec->GetInterface((struct Library *)pci->expansion, "pci", 1, TAG_DONE);
+ if(NULL == pci->ipci) {
+ a->warning("Unable to obtain pci interface");
+ aos_close_pci_interface(a);
+ } else {
+ res = TRUE;
+ }
+ }
+ } else {
+ res = TRUE; // already opened
+ }
+
+ return res;
+}
+
+static int
+aos_expansion_detect(struct pci_access *a)
+{
+ int res = FALSE;
+ struct PCIDevice *device = NULL;
+ PCIAccess *pci;
+
+ if(TRUE == aos_open_pci_interface(a)) {
+ pci = a->backend_data;
+
+ // Try to read PCI first device
+ device = pci->ipci->FindDeviceTags(FDT_Index, 0);
+ if(NULL == device) {
+ a->warning("AmigaOS Expansion PCI interface cannot find any device");
+ aos_close_pci_interface(a);
+ } else {
+ pci->ipci->FreeDevice(device);
+ res = TRUE;
+ }
+ }
+
+ return res;
+}
+
+static void
+aos_expansion_init(struct pci_access *a)
+{
+ // to avoid flushing of version tag
+ static STRPTR USED ver = (STRPTR)VERSTAG;
+
+ if (!aos_open_pci_interface(a)) {
+ a->debug("\n");
+ a->error("AmigaOS Expansion PCI interface cannot be accessed.");
+ }
+}
+
+static void
+aos_expansion_cleanup(struct pci_access *a)
+{
+ aos_close_pci_interface(a);
+}
+
+static void
+aos_expansion_scan(struct pci_access *a)
+{
+ struct PCIDevice *device = NULL;
+ PCIAccess *pci = NULL;
+ UBYTE bus_num;
+ UBYTE dev_num;
+ UBYTE fn_num;
+ struct pci_dev *d;
+ int found_devs = 0;
+
+ pci = a->backend_data;
+
+ // X1000 has a bug which left shifts secondary bus by one bit, so we don't scan but get all devices identified by the system
+ device = pci->ipci->FindDeviceTags(FDT_Index, found_devs);
+ while (device) {
+ d = pci_alloc_dev(a);
+ d->domain = 0; // only one domain for AmigaOS
+ device->GetAddress(&bus_num, &dev_num, &fn_num);
+ d->bus = bus_num;
+ d->dev = dev_num;
+ d->func = fn_num;
+ d->backend_data = device;
+ d->vendor_id = device->ReadConfigWord(PCI_VENDOR_ID);
+ d->device_id = device->ReadConfigWord(PCI_DEVICE_ID);
+ d->known_fields = PCI_FILL_IDENT;
+ d->hdrtype = device->ReadConfigByte(PCI_HEADER_TYPE) & ~PCI_HEADER_TYPE_MULTIFUNCTION;
+ pci_link_dev(a, d);
+ a->debug(" Found device %02x:%02x.%d %04x:%04x\n", d->bus, d->dev, d->func, d->vendor_id, d->device_id);
+
+ found_devs++;
+ device = pci->ipci->FindDeviceTags(FDT_Index, found_devs);
+ }
+}
+
+static int
+aos_expansion_read(struct pci_dev *d, int pos, byte *buf, int len)
+{
+ int res = FALSE;
+ byte *ptr = buf;
+ if (d->backend_data) {
+ for (int i = 0; i < len; i++) {
+ // byte by byte to avoid endianness troubles
+ *ptr = ((struct PCIDevice *)(d->backend_data))->ReadConfigByte(pos + i);
+ ptr++;
+ res = TRUE;
+ }
+ }
+
+ return res;
+}
+
+static int
+aos_expansion_write(struct pci_dev *d, int pos, byte *buf, int len)
+{
+ int res = FALSE;
+ byte *ptr = buf;
+
+ if (d->backend_data) {
+ for (int i = 0; i < len; i++) {
+ // byte by byte to avoid endianness troubles
+ ((struct PCIDevice *)(d->backend_data))->WriteConfigByte(pos + i, *ptr);
+ ptr++;
+ res = TRUE;
+ }
+ }
+
+ return res;
+}
+
+static void
+aos_expansion_init_dev(struct pci_dev *d)
+{
+ d->backend_data = NULL; // struct PCIDevice * to be obtained
+}
+
+static void
+aos_expansion_cleanup_dev(struct pci_dev *d)
+{
+ PCIAccess *pci;
+
+ if (d->backend_data && d->access->backend_data) {
+ pci = d->access->backend_data;
+ pci->ipci->FreeDevice((struct PCIDevice *)d->backend_data);
+ d->backend_data = NULL;
+ }
+}
+
+struct pci_methods pm_aos_expansion = {
+ "aos-expansion",
+ "The Expansion.library on AmigaOS 4.x",
+ NULL, // config, called after allocation of pci_access, if assigned
+ aos_expansion_detect, // detect, mandatory because called without check
+ aos_expansion_init, // init, called once access chosen, eventually after detect
+ aos_expansion_cleanup, // cleanup, called at the end
+ aos_expansion_scan,
+ pci_generic_fill_info,
+ aos_expansion_read,
+ aos_expansion_write,
+ NULL, // read_vpd
+ aos_expansion_init_dev,
+ aos_expansion_cleanup_dev,
+};
diff --git a/lib/caps.c b/lib/caps.c
new file mode 100644
index 0000000..cf1df5d
--- /dev/null
+++ b/lib/caps.c
@@ -0,0 +1,148 @@
+/*
+ * The PCI Library -- Capabilities
+ *
+ * Copyright (c) 2008 Martin Mares <mj@ucw.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <string.h>
+
+#include "internal.h"
+
+static void
+pci_add_cap(struct pci_dev *d, unsigned int addr, unsigned int id, unsigned int type)
+{
+ struct pci_cap *cap = pci_malloc(d->access, sizeof(*cap));
+
+ if (d->last_cap)
+ d->last_cap->next = cap;
+ else
+ d->first_cap = cap;
+ d->last_cap = cap;
+ cap->next = NULL;
+ cap->addr = addr;
+ cap->id = id;
+ cap->type = type;
+ d->access->debug("%04x:%02x:%02x.%d: Found capability %04x of type %d at %04x\n",
+ d->domain, d->bus, d->dev, d->func, id, type, addr);
+}
+
+static void
+pci_scan_trad_caps(struct pci_dev *d)
+{
+ word status = pci_read_word(d, PCI_STATUS);
+ byte been_there[256];
+ int where;
+
+ if (!(status & PCI_STATUS_CAP_LIST))
+ return;
+
+ memset(been_there, 0, 256);
+ where = pci_read_byte(d, PCI_CAPABILITY_LIST) & ~3;
+ while (where)
+ {
+ byte id = pci_read_byte(d, where + PCI_CAP_LIST_ID);
+ byte next = pci_read_byte(d, where + PCI_CAP_LIST_NEXT) & ~3;
+ if (been_there[where]++)
+ break;
+ if (id == 0xff)
+ break;
+ pci_add_cap(d, where, id, PCI_CAP_NORMAL);
+ where = next;
+ }
+}
+
+static void
+pci_scan_ext_caps(struct pci_dev *d)
+{
+ byte been_there[0x1000];
+ int where = 0x100;
+
+ if (!pci_find_cap(d, PCI_CAP_ID_EXP, PCI_CAP_NORMAL))
+ return;
+
+ memset(been_there, 0, 0x1000);
+ do
+ {
+ u32 header;
+ int id;
+
+ header = pci_read_long(d, where);
+ if (!header || header == 0xffffffff)
+ break;
+ id = header & 0xffff;
+ if (been_there[where]++)
+ break;
+ pci_add_cap(d, where, id, PCI_CAP_EXTENDED);
+ where = (header >> 20) & ~3;
+ }
+ while (where);
+}
+
+void
+pci_scan_caps(struct pci_dev *d, unsigned int want_fields)
+{
+ if (want_fields & PCI_FILL_EXT_CAPS)
+ want_fields |= PCI_FILL_CAPS;
+
+ if (want_fill(d, want_fields, PCI_FILL_CAPS))
+ pci_scan_trad_caps(d);
+ if (want_fill(d, want_fields, PCI_FILL_EXT_CAPS))
+ pci_scan_ext_caps(d);
+}
+
+void
+pci_free_caps(struct pci_dev *d)
+{
+ struct pci_cap *cap;
+
+ while (cap = d->first_cap)
+ {
+ d->first_cap = cap->next;
+ pci_mfree(cap);
+ }
+}
+
+struct pci_cap *
+pci_find_cap(struct pci_dev *d, unsigned int id, unsigned int type)
+{
+ return pci_find_cap_nr(d, id, type, NULL);
+}
+
+/**
+ * Finds a particular capability of a device
+ *
+ * To select one capability if there are more than one with the same id, you
+ * can provide a pointer to an unsigned int that contains the index which you
+ * want as cap_number. If you don't care and are fine with the first one you
+ * can supply NULL. The cap_number will be replaced by the actual number
+ * of capabilities with that id.
+ */
+struct pci_cap *
+pci_find_cap_nr(struct pci_dev *d, unsigned int id, unsigned int type,
+ unsigned int *cap_number)
+{
+ struct pci_cap *c;
+ struct pci_cap *found = NULL;
+ unsigned int target = (cap_number ? *cap_number : 0);
+ unsigned int index = 0;
+
+ pci_fill_info_v38(d, ((type == PCI_CAP_NORMAL) ? PCI_FILL_CAPS : PCI_FILL_EXT_CAPS));
+
+ for (c=d->first_cap; c; c=c->next)
+ {
+ if (c->type == type && c->id == id)
+ {
+ if (target == index)
+ found = c;
+ index++;
+ }
+ }
+
+ if (cap_number)
+ *cap_number = index;
+ return found;
+}
diff --git a/lib/configure b/lib/configure
new file mode 100755
index 0000000..3df057a
--- /dev/null
+++ b/lib/configure
@@ -0,0 +1,365 @@
+#!/bin/sh
+# Configuration script for the PCI library
+# (c) 1998--2013 Martin Mares <mj@ucw.cz>
+
+LC_ALL=C
+export LC_ALL
+
+echo_n() {
+ printf '%s' "$*"
+}
+
+if [ -z "$VERSION" ] ; then
+ echo >&2 "Please run the configure script from the top-level Makefile"
+ exit 1
+fi
+
+echo_n "Configuring libpci for your system..."
+if [ -z "$HOST" ] ; then
+ sys=`uname -s`
+ rel=`uname -r`
+ realsys="$sys"
+ if [ "$sys" = "AIX" -a -x /usr/bin/oslevel -a -x /usr/sbin/lsattr ]
+ then
+ rel=`/usr/bin/oslevel`
+ proc=`/usr/sbin/lsdev -C -c processor -S available -F name | head -1`
+ cpu=`/usr/sbin/lsattr -F value -l $proc -a type | sed 's/_.*//'`
+ else
+ cpu=`uname -m | sed 's/^i.86-AT386/i386/;s/^i.86$/i386/;s/^sun4u$/sparc64/;s/^i86pc$/i386/;s/^BePC$/i386/;s/^BeMac$/powerpc/;s/^BeBox$/powerpc/'`
+ fi
+ if [ "$sys" = "DragonFly" ]
+ then
+ sys=freebsd
+ fi
+ if [ "$sys" = "GNU/kFreeBSD" ]
+ then
+ sys=kfreebsd
+ fi
+ if [ "$sys" = "GNU" ]
+ then
+ sys=gnu
+ fi
+ if [ "$sys" = "CYGWIN_NT-5.1" -o "$sys" = "CYGWIN_NT-6.0" ]
+ then
+ sys=cygwin
+ fi
+ HOST=${3:-$cpu-$sys}
+fi
+[ -n "$RELEASE" ] && rel="${RELEASE}"
+# CAVEAT: tr on Solaris is a bit weird and the extra [] is otherwise harmless.
+host=`echo $HOST | sed -e 's/^\([^-]*\)-\([^-]*\)-\([^-]*\)-\([^-]*\)$/\1-\3/' -e 's/^\([^-]*\)-\([^-]*\)-\([^-]*\)$/\1-\2/' -e 's/^\([^-]*\)-\([^-]*\)$/\1--\2/' | tr '[A-Z]' '[a-z]'`
+cpu=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+sys=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+echo " $host $rel $cpu $sys"
+
+c=config.h
+m=config.mk
+echo >$c '#define PCI_CONFIG_H'
+echo >>$c "#define PCI_ARCH_`echo $cpu | tr '[a-z]' '[A-Z]'`"
+echo >>$c "#define PCI_OS_`echo $sys | tr '[a-z]' '[A-Z]'`"
+echo >$m 'WITH_LIBS='
+
+echo_n "Looking for access methods..."
+LIBRESOLV=-lresolv
+LIBEXT=so
+EXEEXT=
+SYSINCLUDE=/usr/include
+LSPCIDIR=SBINDIR
+
+case $sys in
+ linux*)
+ echo_n " sysfs proc mem-ports ecam"
+ echo >>$c '#define PCI_HAVE_PM_LINUX_SYSFS'
+ echo >>$c '#define PCI_HAVE_PM_LINUX_PROC'
+ echo >>$c '#define PCI_HAVE_PM_MMIO_CONF'
+ echo >>$c '#define PCI_HAVE_PM_ECAM'
+ echo >>$c '#define PCI_HAVE_LINUX_BYTEORDER_H'
+ echo >>$c '#define PCI_PATH_PROC_BUS_PCI "/proc/bus/pci"'
+ echo >>$c '#define PCI_PATH_SYS_BUS_PCI "/sys/bus/pci"'
+ echo >>$c '#define PCI_PATH_DEVMEM_DEVICE "/dev/mem"'
+ echo >>$c '#define PCI_PATH_ACPI_MCFG "/sys/firmware/acpi/tables/MCFG"'
+ echo >>$c '#define PCI_PATH_EFI_SYSTAB "/sys/firmware/efi/systab"'
+ case $cpu in
+ i?86|x86_64) echo_n " i386-ports"
+ echo >>$c '#define PCI_HAVE_PM_INTEL_CONF'
+ ;;
+ esac
+ echo >>$c '#define PCI_HAVE_64BIT_ADDRESS'
+ LSPCIDIR=BINDIR
+ ;;
+ sunos)
+ echo_n " mem-ports ecam"
+ case $cpu in
+ i?86) echo_n " i386-ports"
+ echo >>$c "#define PCI_HAVE_PM_INTEL_CONF"
+ ;;
+ esac
+ echo >>$c '#define PCI_HAVE_PM_MMIO_CONF'
+ echo >>$c '#define PCI_HAVE_PM_ECAM'
+ echo >>$c '#define PCI_PATH_DEVMEM_DEVICE "/dev/xsvc"'
+ echo >>$c '#define PCI_PATH_ACPI_MCFG ""'
+ echo >>$c '#define PCI_PATH_EFI_SYSTAB ""'
+ ;;
+ freebsd*|kfreebsd*)
+ echo_n " fbsd-device mem-ports ecam"
+ echo >>$c '#define PCI_HAVE_PM_FBSD_DEVICE'
+ echo >>$c '#define PCI_HAVE_PM_MMIO_CONF'
+ echo >>$c '#define PCI_HAVE_PM_ECAM'
+ echo >>$c '#define PCI_PATH_FBSD_DEVICE "/dev/pci"'
+ echo >>$c '#define PCI_PATH_DEVMEM_DEVICE "/dev/mem"'
+ echo >>$c '#define PCI_PATH_ACPI_MCFG ""'
+ echo >>$c '#define PCI_PATH_EFI_SYSTAB ""'
+ if [ "$sys" != "kfreebsd" ] ; then
+ LIBRESOLV=
+ fi
+ ;;
+ openbsd)
+ echo_n " obsd-device mem-ports ecam"
+ echo >>$c '#define PCI_HAVE_PM_OBSD_DEVICE'
+ echo >>$c '#define PCI_HAVE_PM_MMIO_CONF'
+ echo >>$c '#define PCI_HAVE_PM_ECAM'
+ echo >>$c '#define PCI_PATH_OBSD_DEVICE "/dev/pci"'
+ echo >>$c '#define PCI_PATH_DEVMEM_DEVICE "/dev/mem"'
+ echo >>$c '#define PCI_PATH_ACPI_MCFG "/var/db/acpi/MCFG.*"'
+ echo >>$c '#define PCI_PATH_EFI_SYSTAB ""'
+ case $cpu in
+ i386|amd64) echo_n " i386-ports"
+ echo >>$c '#define PCI_HAVE_PM_INTEL_CONF'
+ echo >>$m 'WITH_LIBS+=-l'$cpu
+ ;;
+ esac
+ LIBRESOLV=
+ ;;
+
+ darwin*)
+ echo_n " darwin"
+ echo >>$c '#define PCI_HAVE_PM_DARWIN_DEVICE'
+ echo >>$m 'WITH_LIBS+=-lresolv -framework CoreFoundation -framework IOKit'
+ echo >>$c '#define PCI_HAVE_64BIT_ADDRESS'
+ LIBRESOLV=
+ LIBEXT=dylib
+ SYSINCLUDE=$(xcrun --sdk macosx --show-sdk-path)/usr/include
+ ;;
+ aix)
+ echo_n " aix-device"
+ echo >>$c '#define PCI_HAVE_PM_AIX_DEVICE'
+ echo >>$m 'CFLAGS=-g'
+ echo >>$m 'INSTALL=installbsd'
+ echo >>$m 'DIRINSTALL=mkdir -p'
+ ;;
+ netbsd)
+ echo_n " nbsd-libpci mem-ports ecam"
+ echo >>$c '#define PCI_HAVE_PM_NBSD_LIBPCI'
+ echo >>$c '#define PCI_HAVE_PM_MMIO_CONF'
+ echo >>$c '#define PCI_HAVE_PM_ECAM'
+ echo >>$c '#define PCI_PATH_NBSD_DEVICE "/dev/pci0"'
+ echo >>$c '#define PCI_PATH_DEVMEM_DEVICE "/dev/mem"'
+ echo >>$c '#define PCI_PATH_ACPI_MCFG ""'
+ echo >>$c '#define PCI_PATH_EFI_SYSTAB ""'
+ echo >>$c '#define PCI_HAVE_64BIT_ADDRESS'
+ echo >>$m 'LIBNAME=libpciutils'
+ echo >>$m 'WITH_LIBS+=-lpci'
+ LIBRESOLV=
+ ;;
+ gnu)
+ echo_n " hurd i386-ports"
+ echo >>$c '#define PCI_HAVE_PM_HURD_CONF'
+ echo >>$c '#define PCI_HAVE_PM_INTEL_CONF'
+ ;;
+ djgpp)
+ echo_n " i386-ports"
+ echo >>$c '#define PCI_HAVE_PM_INTEL_CONF'
+ EXEEXT=.exe
+ ;;
+ cygwin|windows)
+ echo_n " win32-cfgmgr32 win32-kldbg win32-sysdbg"
+ echo >>$c '#define PCI_HAVE_64BIT_ADDRESS'
+ echo >>$c '#define PCI_HAVE_PM_WIN32_CFGMGR32'
+ echo >>$c '#define PCI_HAVE_PM_WIN32_KLDBG'
+ 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
+ # instruct linker to prefer symbols from cfgmgr32.dll.
+ echo >>$m 'WITH_LIBS+=-lcfgmgr32'
+ case $cpu in i?86|x86_64)
+ echo_n " i386-ports"
+ echo >>$c '#define PCI_HAVE_PM_INTEL_CONF'
+ if [ "$sys" = "cygwin" ] ; then
+ # ioperm is cygwin specific library and used only by lib/i386-io-cygwin.h
+ echo >>$m 'WITH_LIBS+=-lioperm'
+ elif [ "$sys" = "windows" ] ; then
+ # advapi32 is windows system library and used only by lib/i386-io-windows.h
+ echo >>$m 'WITH_LIBS+=-ladvapi32'
+ fi
+ ;; esac
+ EXEEXT=.exe
+ LIBEXT=dll
+ ;;
+ beos|haiku)
+ echo_n " mem-ports ecam"
+ case $cpu in
+ i?86|x86_64) echo_n " i386-ports"
+ echo >>$c '#define PCI_HAVE_PM_INTEL_CONF'
+ ;;
+ esac
+ echo >>$c '#define PCI_HAVE_PM_MMIO_CONF'
+ echo >>$c '#define PCI_HAVE_PM_ECAM'
+ echo >>$c '#define PCI_PATH_DEVMEM_DEVICE "/dev/misc/mem"'
+ echo >>$c '#define PCI_PATH_ACPI_MCFG ""'
+ echo >>$c '#define PCI_PATH_EFI_SYSTAB ""'
+ ;;
+ sylixos)
+ echo >>$c '#define PCI_PATH_SYLIXOS_DEVICE "/proc/pci"'
+ echo >>$c '#define PCI_HAVE_64BIT_ADDRESS'
+ echo >>$c '#define PCI_HAVE_PM_SYLIXOS_DEVICE'
+ IDSDIR="/etc/pci"
+ LIBRESOLV=
+ ;;
+ amigaos)
+ echo_n " aos-expansion"
+ echo >>$c '#define PCI_HAVE_STDINT_H'
+ echo >>$c '#define PCI_HAVE_PM_AOS_EXPANSION'
+ IDSDIR="DEVS:"
+ echo >>$m 'CC=gcc'
+ ;;
+ *)
+ echo " Unfortunately, your OS is not supported by the PCI Library"
+ exit 1
+ ;;
+esac
+
+echo >>$m "LIBEXT="$LIBEXT
+echo >>$m "EXEEXT="$EXEEXT
+echo >>$m "LSPCIDIR=\$($LSPCIDIR)"
+echo >>$c '#define PCI_HAVE_PM_DUMP'
+echo " dump"
+
+echo_n "Checking for zlib support... "
+if [ "$ZLIB" = yes -o "$ZLIB" = no ] ; then
+ echo "$ZLIB (set manually)"
+else
+ if [ -f "$SYSINCLUDE/zlib.h" -o -f /usr/local/include/zlib.h ] ; then
+ ZLIB=yes
+ else
+ ZLIB=no
+ fi
+ echo "$ZLIB (auto-detected)"
+fi
+if [ "$ZLIB" = yes ] ; then
+ echo >>$c '#define PCI_COMPRESSED_IDS'
+ echo >>$c '#define PCI_IDS "pci.ids.gz"'
+ echo >>$m 'LIBZ=-lz'
+ echo >>$m 'WITH_LIBS+=$(LIBZ)'
+else
+ echo >>$c '#define PCI_IDS "pci.ids"'
+fi
+echo >>$c "#define PCI_PATH_IDS_DIR \"$IDSDIR\""
+
+echo_n "Checking for DNS support... "
+if [ "$DNS" = yes -o "$DNS" = no ] ; then
+ echo "$DNS (set manually)"
+else
+ if [ "$sys" != "windows" -a -f "$SYSINCLUDE/resolv.h" ] ; then
+ DNS=yes
+ else
+ DNS=no
+ fi
+ echo "$DNS (auto-detected)"
+fi
+if [ "$DNS" = yes ] ; then
+ echo >>$c "#define PCI_USE_DNS"
+ echo >>$c "#define PCI_ID_DOMAIN \"pci.id.ucw.cz\""
+ echo >>$m "WITH_LIBS+=$LIBRESOLV"
+fi
+
+if [ "$sys" = linux ] ; then
+ echo_n "Checking for libkmod... "
+ LIBKMOD_DETECTED=
+ if [ -z "$PKG_CONFIG" ] ; then
+ PKG_CONFIG=pkg-config
+ fi
+ if [ "$LIBKMOD" != no ] ; then
+ if ! command -v $PKG_CONFIG >/dev/null ; then
+ echo_n "($PKG_CONFIG not found) "
+ elif $PKG_CONFIG libkmod ; then
+ LIBKMOD_DETECTED=1
+ fi
+ fi
+ if [ "$LIBKMOD" = yes -o "$LIBKMOD" = no ] ; then
+ echo "$LIBKMOD (set manually)"
+ if [ "$LIBKMOD" = yes -a -z "$LIBKMOD_DETECTED" ] ; then
+ echo "Requested use of libkmod, but it is not available. Giving up."
+ exit 1
+ fi
+ else
+ if [ -n "$LIBKMOD_DETECTED" ] ; then
+ LIBKMOD=yes
+ else
+ LIBKMOD=no
+ fi
+ echo "$LIBKMOD (auto-detected)"
+ fi
+ if [ "$LIBKMOD" = yes ] ; then
+ echo >>$c "#define PCI_USE_LIBKMOD"
+ echo >>$m "LIBKMOD_CFLAGS=$($PKG_CONFIG --cflags libkmod)"
+ echo >>$m "LIBKMOD_LIBS=$($PKG_CONFIG --libs libkmod)"
+ fi
+
+ echo_n "Checking for udev hwdb support... "
+ if [ "$HWDB" = yes -o "$HWDB" = no ] ; then
+ echo "$HWDB (set manually)"
+ else
+ if `command -v $PKG_CONFIG >/dev/null && $PKG_CONFIG --atleast-version=196 libudev` ; then
+ HWDB=yes
+ else
+ HWDB=no
+ fi
+ echo "$HWDB (auto-detected)"
+ fi
+ if [ "$HWDB" = yes ] ; then
+ echo >>$c '#define PCI_HAVE_HWDB'
+ echo >>$m 'LIBUDEV=-ludev'
+ echo >>$m 'WITH_LIBS+=$(LIBUDEV)'
+ fi
+fi
+
+echo "Checking whether to build a shared library... $SHARED (set manually)"
+if [ "$SHARED" = no ] ; then
+ echo >>$m 'PCILIB=$(LIBNAME).a'
+ echo >>$m 'LDLIBS=$(WITH_LIBS)'
+ echo >>$m 'LIB_LDLIBS='
+else
+ if [ "$LIBEXT" = so ]; then
+ echo >>$m 'PCILIB=$(LIBNAME).$(LIBEXT).$(VERSION)'
+ elif [ "$LIBEXT" = dll ]; then
+ echo >>$m 'PCILIB=$(LIBNAME)$(ABI_VERSION).$(LIBEXT)'
+ else
+ echo >>$m 'PCILIB=$(LIBNAME).$(VERSION).$(LIBEXT)'
+ fi
+ # We link the dependencies _to_ the library, so we do not need explicit deps in .pc
+ echo >>$m 'LDLIBS='
+ echo >>$m 'LIB_LDLIBS=$(WITH_LIBS)'
+ echo >>$c '#define PCI_SHARED_LIB'
+ if [ "$LIBEXT" = so ]; then
+ echo >>$m 'PCILIB_LDFLAGS+=-Wl,-soname,$(LIBNAME).$(LIBEXT).$(ABI_VERSION)'
+ echo >>$m 'PCILIB_LDFLAGS+=-Wl,--version-script=libpci.ver'
+ elif [ "$LIBEXT" = dylib ]; then
+ echo >>$m 'PCILIB_LDFLAGS+=-Wl,-install_name,$(LIBDIR)/$(PCILIB)'
+ elif [ "$LIBEXT" = dll ]; then
+ echo >>$m 'PCIIMPDEF=$(LIBNAME)$(ABI_VERSION).def'
+ # GCC's -fvisibility=hidden is broken for Windows targets, use -Wl,--exclude-all-symbols instead (supported since GNU LD 2.21)
+ echo >>$m 'PCILIB_LDFLAGS+=-Wl,--exclude-all-symbols'
+ fi
+fi
+echo >>$m 'PCILIBPC=$(LIBNAME).pc'
+
+if [ "$SHARED" != no ] && [ "$LIBEXT" = dll ]; then
+ echo >>$m 'PCIIMPLIB=$(PCILIB).a'
+else
+ echo >>$m 'PCIIMPLIB=$(PCILIB)'
+fi
+
+echo >>$c "#define PCILIB_VERSION \"$VERSION\""
+echo >>$c "#define PCILIB_DATE_AMIGAOS \"`echo $DATE | sed 's/\(....\)-\(..\)-\(..\)/\3.\2.\1/'`\""
+sed '/"/{s/^#define \([^ ]*\) "\(.*\)"$/\1=\2/;p;d;};s/^#define \(.*\)/\1=1/' <$c >>$m
diff --git a/lib/darwin.c b/lib/darwin.c
new file mode 100644
index 0000000..8ae9008
--- /dev/null
+++ b/lib/darwin.c
@@ -0,0 +1,213 @@
+/*
+ * The PCI Library -- Darwin kIOACPI access
+ *
+ * Copyright (c) 2013 Apple, Inc.
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdint.h>
+
+#include "internal.h"
+
+#include <mach/mach_error.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <IOKit/IOKitLib.h>
+#include <IOKit/IOKitKeys.h>
+
+enum {
+ kACPIMethodAddressSpaceRead = 0,
+ kACPIMethodAddressSpaceWrite = 1,
+ kACPIMethodDebuggerCommand = 2,
+ kACPIMethodCount
+};
+
+#pragma pack(1)
+
+typedef UInt32 IOACPIAddressSpaceID;
+
+enum {
+ kIOACPIAddressSpaceIDSystemMemory = 0,
+ kIOACPIAddressSpaceIDSystemIO = 1,
+ kIOACPIAddressSpaceIDPCIConfiguration = 2,
+ kIOACPIAddressSpaceIDEmbeddedController = 3,
+ kIOACPIAddressSpaceIDSMBus = 4
+};
+
+/*
+ * 64-bit ACPI address
+ */
+union IOACPIAddress {
+ UInt64 addr64;
+ struct {
+ unsigned int offset :16;
+ unsigned int function :3;
+ unsigned int device :5;
+ unsigned int bus :8;
+ unsigned int segment :16;
+ unsigned int reserved :16;
+ } pci;
+};
+typedef union IOACPIAddress IOACPIAddress;
+
+#pragma pack()
+
+struct AddressSpaceParam {
+ UInt64 value;
+ UInt32 spaceID;
+ IOACPIAddress address;
+ UInt32 bitWidth;
+ UInt32 bitOffset;
+ UInt32 options;
+};
+typedef struct AddressSpaceParam AddressSpaceParam;
+
+static void
+darwin_config(struct pci_access *a UNUSED)
+{
+}
+
+static int
+darwin_detect(struct pci_access *a)
+{
+ io_registry_entry_t service;
+ io_connect_t connect;
+ kern_return_t status;
+
+ service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("AppleACPIPlatformExpert"));
+ if (service)
+ {
+ status = IOServiceOpen(service, mach_task_self(), 0, &connect);
+ IOObjectRelease(service);
+ }
+
+ if (!service || (kIOReturnSuccess != status))
+ {
+ a->warning("Cannot open AppleACPIPlatformExpert (add boot arg debug=0x144 & run as root)");
+ return 0;
+ }
+ a->debug("...using AppleACPIPlatformExpert");
+ a->fd = connect;
+ return 1;
+}
+
+static void
+darwin_init(struct pci_access *a UNUSED)
+{
+}
+
+static void
+darwin_cleanup(struct pci_access *a UNUSED)
+{
+}
+
+static int
+darwin_read(struct pci_dev *d, int pos, byte *buf, int len)
+{
+ if (!(len == 1 || len == 2 || len == 4))
+ return pci_generic_block_read(d, pos, buf, len);
+
+ AddressSpaceParam param;
+ kern_return_t status;
+
+ param.spaceID = kIOACPIAddressSpaceIDPCIConfiguration;
+ param.bitWidth = len * 8;
+ param.bitOffset = 0;
+ param.options = 0;
+
+ param.address.pci.offset = pos;
+ param.address.pci.function = d->func;
+ param.address.pci.device = d->dev;
+ param.address.pci.bus = d->bus;
+ param.address.pci.segment = d->domain;
+ param.address.pci.reserved = 0;
+ param.value = -1ULL;
+
+ size_t outSize = sizeof(param);
+ status = IOConnectCallStructMethod(d->access->fd, kACPIMethodAddressSpaceRead,
+ &param, sizeof(param),
+ &param, &outSize);
+ if ((kIOReturnSuccess != status))
+ d->access->error("darwin_read: kACPIMethodAddressSpaceRead failed: %s", mach_error_string(status));
+
+ switch (len)
+ {
+ case 1:
+ buf[0] = (u8) param.value;
+ break;
+ case 2:
+ ((u16 *) buf)[0] = cpu_to_le16((u16) param.value);
+ break;
+ case 4:
+ ((u32 *) buf)[0] = cpu_to_le32((u32) param.value);
+ break;
+ }
+ return 1;
+}
+
+static int
+darwin_write(struct pci_dev *d, int pos, byte *buf, int len)
+{
+ if (!(len == 1 || len == 2 || len == 4))
+ return pci_generic_block_write(d, pos, buf, len);
+
+ AddressSpaceParam param;
+ kern_return_t status;
+
+ param.spaceID = kIOACPIAddressSpaceIDPCIConfiguration;
+ param.bitWidth = len * 8;
+ param.bitOffset = 0;
+ param.options = 0;
+
+ param.address.pci.offset = pos;
+ param.address.pci.function = d->func;
+ param.address.pci.device = d->dev;
+ param.address.pci.bus = d->bus;
+ param.address.pci.segment = d->domain;
+ param.address.pci.reserved = 0;
+
+ switch (len)
+ {
+ case 1:
+ param.value = buf[0];
+ break;
+ case 2:
+ param.value = le16_to_cpu(((u16 *) buf)[0]);
+ break;
+ case 4:
+ param.value = le32_to_cpu(((u32 *) buf)[0]);
+ break;
+ }
+
+ size_t outSize = 0;
+ status = IOConnectCallStructMethod(d->access->fd, kACPIMethodAddressSpaceWrite,
+ &param, sizeof(param),
+ NULL, &outSize);
+ if ((kIOReturnSuccess != status))
+ d->access->error("darwin_read: kACPIMethodAddressSpaceWrite failed: %s", mach_error_string(status));
+
+ return 1;
+}
+
+struct pci_methods pm_darwin = {
+ "darwin",
+ "Darwin",
+ darwin_config,
+ darwin_detect,
+ darwin_init,
+ darwin_cleanup,
+ pci_generic_scan,
+ pci_generic_fill_info,
+ darwin_read,
+ darwin_write,
+ NULL, /* read_vpd */
+ NULL, /* dev_init */
+ NULL /* dev_cleanup */
+};
diff --git a/lib/dump.c b/lib/dump.c
new file mode 100644
index 0000000..829071f
--- /dev/null
+++ b/lib/dump.c
@@ -0,0 +1,194 @@
+/*
+ * The PCI Library -- Reading of Bus Dumps
+ *
+ * Copyright (c) 1997--2008 Martin Mares <mj@ucw.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <errno.h>
+
+#include "internal.h"
+
+struct dump_data {
+ int len, allocated;
+ byte data[1];
+};
+
+static void
+dump_config(struct pci_access *a)
+{
+ pci_define_param(a, "dump.name", "", "Name of the bus dump file to read from");
+}
+
+static int
+dump_detect(struct pci_access *a)
+{
+ char *name = pci_get_param(a, "dump.name");
+ return name && name[0];
+}
+
+static void
+dump_alloc_data(struct pci_dev *dev, int len)
+{
+ struct dump_data *dd = pci_malloc(dev->access, sizeof(struct dump_data) + len - 1);
+ dd->allocated = len;
+ dd->len = 0;
+ memset(dd->data, 0xff, len);
+ dev->backend_data = dd;
+}
+
+static int
+dump_validate(char *s, char *fmt)
+{
+ while (*fmt)
+ {
+ if (*fmt == '#' ? !isxdigit(*s) : *fmt != *s)
+ return 0;
+ fmt++, s++;
+ }
+ return 1;
+}
+
+static void
+dump_init(struct pci_access *a)
+{
+ char *name = pci_get_param(a, "dump.name");
+ FILE *f;
+ char buf[256];
+ struct pci_dev *dev = NULL;
+ int len, mn, bn, dn, fn, i, j;
+
+ if (!name)
+ a->error("dump: File name not given.");
+ if (!(f = fopen(name, "r")))
+ a->error("dump: Cannot open %s: %s", name, strerror(errno));
+ while (fgets(buf, sizeof(buf)-1, f))
+ {
+ char *z = strchr(buf, '\n');
+ if (!z)
+ {
+ fclose(f);
+ a->error("dump: line too long or unterminated");
+ }
+ *z-- = 0;
+ if (z >= buf && *z == '\r')
+ *z-- = 0;
+ len = z - buf + 1;
+ mn = 0;
+ if (dump_validate(buf, "##:##.# ") && sscanf(buf, "%x:%x.%d", &bn, &dn, &fn) == 3 ||
+ dump_validate(buf, "####:##:##.# ") && sscanf(buf, "%x:%x:%x.%d", &mn, &bn, &dn, &fn) == 4 ||
+ dump_validate(buf, "#####:##:##.# ") && sscanf(buf, "%x:%x:%x.%d", &mn, &bn, &dn, &fn) == 4)
+ {
+ dev = pci_get_dev(a, mn, bn, dn, fn);
+ dump_alloc_data(dev, 256);
+ pci_link_dev(a, dev);
+ }
+ else if (!len)
+ dev = NULL;
+ else if (dev &&
+ (dump_validate(buf, "##: ") || dump_validate(buf, "###: ") || dump_validate(buf, "####: ") ||
+ dump_validate(buf, "#####: ") || dump_validate(buf, "######: ") ||
+ dump_validate(buf, "#######: ") || dump_validate(buf, "########: ")) &&
+ sscanf(buf, "%x: ", &i) == 1)
+ {
+ struct dump_data *dd = dev->backend_data;
+ z = strchr(buf, ' ') + 1;
+ while (isxdigit(z[0]) && isxdigit(z[1]) && (!z[2] || z[2] == ' ') &&
+ sscanf(z, "%x", &j) == 1 && j < 256)
+ {
+ if (i >= 4096)
+ {
+ fclose(f);
+ a->error("dump: At most 4096 bytes of config space are supported");
+ }
+ if (i >= dd->allocated) /* Need to re-allocate the buffer */
+ {
+ dump_alloc_data(dev, 4096);
+ memcpy(((struct dump_data *) dev->backend_data)->data, dd->data, 256);
+ pci_mfree(dd);
+ dd = dev->backend_data;
+ }
+ dd->data[i++] = j;
+ if (i > dd->len)
+ dd->len = i;
+ z += 2;
+ if (*z)
+ z++;
+ }
+ if (*z)
+ {
+ fclose(f);
+ a->error("dump: Malformed line");
+ }
+ }
+ }
+ fclose(f);
+}
+
+static void
+dump_cleanup(struct pci_access *a UNUSED)
+{
+}
+
+static void
+dump_scan(struct pci_access *a UNUSED)
+{
+}
+
+static int
+dump_read(struct pci_dev *d, int pos, byte *buf, int len)
+{
+ struct dump_data *dd;
+ if (!(dd = d->backend_data))
+ {
+ struct pci_dev *e = d->access->devices;
+ while (e && (e->domain != d->domain || e->bus != d->bus || e->dev != d->dev || e->func != d->func))
+ e = e->next;
+ if (!e)
+ return 0;
+ dd = e->backend_data;
+ }
+ if (pos + len > dd->len)
+ return 0;
+ memcpy(buf, dd->data + pos, len);
+ return 1;
+}
+
+static int
+dump_write(struct pci_dev *d UNUSED, int pos UNUSED, byte *buf UNUSED, int len UNUSED)
+{
+ d->access->error("Writing to dump files is not supported.");
+ return 0;
+}
+
+static void
+dump_cleanup_dev(struct pci_dev *d)
+{
+ if (d->backend_data)
+ {
+ pci_mfree(d->backend_data);
+ d->backend_data = NULL;
+ }
+}
+
+struct pci_methods pm_dump = {
+ "dump",
+ "Reading of register dumps (set the `dump.name' parameter)",
+ dump_config,
+ dump_detect,
+ dump_init,
+ dump_cleanup,
+ dump_scan,
+ pci_generic_fill_info,
+ dump_read,
+ dump_write,
+ NULL, /* read_vpd */
+ NULL, /* init_dev */
+ dump_cleanup_dev
+};
diff --git a/lib/ecam.c b/lib/ecam.c
new file mode 100644
index 0000000..f088c0c
--- /dev/null
+++ b/lib/ecam.c
@@ -0,0 +1,1104 @@
+/*
+ * The PCI Library -- Direct Configuration access via PCIe ECAM
+ *
+ * Copyright (c) 2023 Pali Rohár <pali@kernel.org>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "internal.h"
+#include "physmem.h"
+#include "physmem-access.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#include <glob.h>
+#include <unistd.h>
+
+#if defined (__FreeBSD__) || defined (__DragonFly__) || defined(__NetBSD__)
+#include <sys/sysctl.h>
+#endif
+
+#if defined (__FreeBSD__) || defined (__DragonFly__)
+#include <kenv.h>
+#endif
+
+struct acpi_rsdp {
+ char signature[8];
+ u8 checksum;
+ char oem_id[6];
+ u8 revision;
+ u32 rsdt_address;
+ struct {
+ u32 length;
+ u64 xsdt_address;
+ u8 ext_checksum;
+ u8 reserved[3];
+ } rsdp20[0];
+} PCI_PACKED;
+
+struct acpi_sdt {
+ char signature[4];
+ u32 length;
+ u8 revision;
+ u8 checksum;
+ char oem_id[6];
+ char oem_table_id[8];
+ u32 oem_revision;
+ char asl_compiler_id[4];
+ u32 asl_compiler_revision;
+} PCI_PACKED;
+
+struct acpi_rsdt {
+ struct acpi_sdt sdt;
+ u32 sdt_addresses[0];
+} PCI_PACKED;
+
+struct acpi_xsdt {
+ struct acpi_sdt sdt;
+ u64 sdt_addresses[0];
+} PCI_PACKED;
+
+struct acpi_mcfg {
+ struct acpi_sdt sdt;
+ u64 reserved;
+ struct {
+ u64 address;
+ u16 pci_segment;
+ u8 start_bus_number;
+ u8 end_bus_number;
+ u32 reserved;
+ } allocations[0];
+} PCI_PACKED;
+
+struct mmap_cache {
+ void *map;
+ u64 addr;
+ u32 length;
+ int domain;
+ u8 bus;
+ int w;
+};
+
+// Back-end data linked to struct pci_access
+struct ecam_access {
+ struct acpi_mcfg *mcfg;
+ struct mmap_cache *cache;
+ struct physmem *physmem;
+ long pagesize;
+};
+
+static unsigned int
+get_rsdt_addresses_count(struct acpi_rsdt *rsdt)
+{
+ return (rsdt->sdt.length - ((unsigned char*)&rsdt->sdt_addresses - (unsigned char *)rsdt)) / sizeof(rsdt->sdt_addresses[0]);
+}
+
+static unsigned int
+get_xsdt_addresses_count(struct acpi_xsdt *xsdt)
+{
+ return (xsdt->sdt.length - ((unsigned char*)&xsdt->sdt_addresses - (unsigned char *)xsdt)) / sizeof(xsdt->sdt_addresses[0]);
+}
+
+static unsigned int
+get_mcfg_allocations_count(struct acpi_mcfg *mcfg)
+{
+ return (mcfg->sdt.length - ((unsigned char *)&mcfg->allocations - (unsigned char *)mcfg)) / sizeof(mcfg->allocations[0]);
+}
+
+static u8
+calculate_checksum(const u8 *bytes, int len)
+{
+ u8 checksum = 0;
+
+ while (len-- > 0)
+ checksum -= *(bytes++);
+ return checksum;
+}
+
+static struct acpi_sdt *
+check_and_map_sdt(struct physmem *physmem, long pagesize, u64 addr, const char *signature, void **map_addr, u32 *map_length)
+{
+ struct acpi_sdt *sdt;
+ char sdt_signature[sizeof(sdt->signature)];
+ u32 length;
+ void *map;
+
+ if (addr + sizeof(*sdt) < addr)
+ return NULL;
+
+ map = physmem_map(physmem, addr & ~(pagesize-1), sizeof(*sdt) + (addr & (pagesize-1)), 0);
+ if (map == (void *)-1)
+ return NULL;
+
+ sdt = (struct acpi_sdt *)((unsigned char *)map + (addr & (pagesize-1)));
+ length = sdt->length;
+ memcpy(sdt_signature, sdt->signature, sizeof(sdt->signature));
+
+ physmem_unmap(physmem, map, sizeof(*sdt) + (addr & (pagesize-1)));
+
+ if (memcmp(sdt_signature, signature, sizeof(sdt_signature)) != 0)
+ return NULL;
+ if (length < sizeof(*sdt))
+ return NULL;
+
+ map = physmem_map(physmem, addr & ~(pagesize-1), length + (addr & (pagesize-1)), 0);
+ if (map == (void *)-1)
+ return NULL;
+
+ sdt = (struct acpi_sdt *)((unsigned char *)map + (addr & (pagesize-1)));
+
+ if (calculate_checksum((u8 *)sdt, sdt->length) != 0)
+ {
+ physmem_unmap(physmem, map, length + (addr & (pagesize-1)));
+ return NULL;
+ }
+
+ *map_addr = map;
+ *map_length = length + (addr & (pagesize-1));
+ return sdt;
+}
+
+static int
+check_rsdp(struct acpi_rsdp *rsdp)
+{
+ if (memcmp(rsdp->signature, "RSD PTR ", sizeof(rsdp->signature)) != 0)
+ return 0;
+ if (calculate_checksum((u8 *)rsdp, sizeof(*rsdp)) != 0)
+ return 0;
+ return 1;
+}
+
+static int
+check_and_parse_rsdp(struct physmem *physmem, long pagesize, u64 addr, u32 *rsdt_address, u64 *xsdt_address)
+{
+ struct acpi_rsdp *rsdp;
+ unsigned char buf[sizeof(*rsdp) + sizeof(*rsdp->rsdp20)];
+ void *map;
+
+ map = physmem_map(physmem, addr & ~(pagesize-1), sizeof(buf) + (addr & (pagesize-1)), 0);
+ if (map == (void *)-1)
+ return 0;
+
+ rsdp = (struct acpi_rsdp *)buf;
+ memcpy(rsdp, (unsigned char *)map + (addr & (pagesize-1)), sizeof(buf));
+
+ physmem_unmap(physmem, map, sizeof(buf));
+
+ if (!check_rsdp(rsdp))
+ return 0;
+
+ *rsdt_address = rsdp->rsdt_address;
+
+ if (rsdp->revision != 0 &&
+ (*rsdp->rsdp20).length == sizeof(*rsdp) + sizeof(*rsdp->rsdp20) &&
+ calculate_checksum((u8 *)rsdp, (*rsdp->rsdp20).length) == 0)
+ *xsdt_address = (*rsdp->rsdp20).xsdt_address;
+ else
+ *xsdt_address = 0;
+
+ return 1;
+}
+
+static u64
+find_rsdp_address(struct pci_access *a, const char *efisystab, int use_bsd UNUSED, int use_x86bios UNUSED)
+{
+ u64 ullnum;
+#if defined (__FreeBSD__) || defined (__DragonFly__) || defined(__NetBSD__)
+ unsigned long ulnum;
+#endif
+ char buf[1024];
+ char *endptr;
+ u64 acpi20;
+ u64 acpi;
+#if defined(__amd64__) || defined(__i386__)
+ struct ecam_access *eacc = a->backend_data;
+ struct physmem *physmem = eacc->physmem;
+ u64 rsdp_addr;
+ u64 addr;
+ void *map;
+#endif
+ size_t len;
+ FILE *f;
+
+ if (efisystab[0])
+ {
+ acpi = 0;
+ acpi20 = 0;
+ a->debug("reading EFI system table: %s...", efisystab);
+ f = fopen(efisystab, "r");
+ if (f)
+ {
+ while (fgets(buf, sizeof(buf), f))
+ {
+ len = strlen(buf);
+ while (len > 0 && buf[len-1] == '\n')
+ buf[--len] = '\0';
+ if (strncmp(buf, "ACPI20=", 7) == 0 && isxdigit(buf[7]))
+ {
+ errno = 0;
+ ullnum = strtoull(buf+7, &endptr, 16);
+ if (!errno && !*endptr)
+ acpi20 = ullnum;
+ }
+ else if (strncmp(buf, "ACPI=", 5) == 0 && isxdigit(buf[5]))
+ {
+ errno = 0;
+ ullnum = strtoull(buf+5, &endptr, 16);
+ if (!errno && !*endptr)
+ acpi = ullnum;
+ }
+ }
+ fclose(f);
+ }
+ else
+ a->debug("opening failed: %s...", strerror(errno));
+
+ if (acpi20)
+ return acpi20;
+ else if (acpi)
+ return acpi;
+ }
+
+#if defined (__FreeBSD__) || defined (__DragonFly__)
+ if (use_bsd)
+ {
+ /* First try FreeBSD kenv hint.acpi.0.rsdp */
+ a->debug("calling kenv hint.acpi.0.rsdp...");
+ if (kenv(KENV_GET, "hint.acpi.0.rsdp", buf, sizeof(buf)) > 0)
+ {
+ errno = 0;
+ ullnum = strtoull(buf, &endptr, 16);
+ if (!errno && !*endptr)
+ return ullnum;
+ }
+
+ /* Then try FreeBSD sysctl machdep.acpi_root */
+ a->debug("calling sysctl machdep.acpi_root...");
+ len = sizeof(ulnum);
+ if (sysctlbyname("machdep.acpi_root", &ulnum, &len, NULL, 0) == 0)
+ return ulnum;
+ }
+#endif
+
+#if defined(__NetBSD__)
+ if (use_bsd)
+ {
+ /* Try NetBSD sysctl hw.acpi.root */
+ a->debug("calling sysctl hw.acpi.root...");
+ len = sizeof(ulnum);
+ if (sysctlbyname("hw.acpi.root", &ulnum, &len, NULL, 0) == 0)
+ return ulnum;
+ }
+#endif
+
+#if defined(__amd64__) || defined(__i386__)
+ if (use_x86bios)
+ {
+ rsdp_addr = 0;
+
+ /* Scan first kB of Extended BIOS Data Area */
+ a->debug("scanning first kB of EBDA...");
+ map = physmem_map(physmem, 0, 0x40E + 1024, 0);
+ if (map != (void *)-1)
+ {
+ for (addr = 0x40E; addr < 0x40E + 1024; addr += 16)
+ {
+ if (check_rsdp((struct acpi_rsdp *)((unsigned char *)map + addr)))
+ {
+ rsdp_addr = addr;
+ break;
+ }
+ }
+ if (physmem_unmap(physmem, map, 0x40E + 1024) != 0)
+ a->debug("unmapping of EBDA failed: %s...", strerror(errno));
+ }
+ else
+ a->debug("mapping of EBDA failed: %s...", strerror(errno));
+
+ if (rsdp_addr)
+ return rsdp_addr;
+
+ /* Scan the main BIOS area below 1 MB */
+ a->debug("scanning BIOS below 1 MB...");
+ map = physmem_map(physmem, 0xE0000, 0x20000, 0);
+ if (map != (void *)-1)
+ {
+ for (addr = 0x0; addr < 0x20000; addr += 16)
+ {
+ if (check_rsdp((struct acpi_rsdp *)((unsigned char *)map + addr)))
+ {
+ rsdp_addr = 0xE0000 + addr;
+ break;
+ }
+ }
+ if (physmem_unmap(physmem, map, 0x20000) != 0)
+ a->debug("unmapping of BIOS failed: %s...", strerror(errno));
+ }
+ else
+ a->debug("mapping of BIOS failed: %s...", strerror(errno));
+
+ if (rsdp_addr)
+ return rsdp_addr;
+ }
+#endif
+
+ return 0;
+}
+
+static struct acpi_mcfg *
+find_mcfg(struct pci_access *a, const char *acpimcfg, const char *efisystab, int use_bsd, int use_x86bios)
+{
+ struct ecam_access *eacc = a->backend_data;
+ struct physmem *physmem = eacc->physmem;
+ long pagesize = eacc->pagesize;
+ struct acpi_xsdt *xsdt;
+ struct acpi_rsdt *rsdt;
+ struct acpi_mcfg *mcfg;
+ struct acpi_sdt *sdt;
+ unsigned int i, count;
+ u64 rsdp_address;
+ u64 xsdt_address;
+ u32 rsdt_address;
+ void *map_addr;
+ u32 map_length;
+ void *map2_addr;
+ u32 map2_length;
+ long length;
+ FILE *mcfg_file;
+ const char *path;
+ glob_t mcfg_glob;
+ int ret;
+
+ if (acpimcfg[0])
+ {
+ ret = glob(acpimcfg, GLOB_NOCHECK, NULL, &mcfg_glob);
+ if (ret == 0)
+ {
+ path = mcfg_glob.gl_pathv[0];
+ a->debug("reading acpi mcfg file: %s...", path);
+ mcfg_file = fopen(path, "rb");
+ globfree(&mcfg_glob);
+ if (mcfg_file)
+ {
+ if (fseek(mcfg_file, 0, SEEK_END) == 0)
+ length = ftell(mcfg_file);
+ else
+ length = -1;
+ if (length > 0 && (size_t)length > sizeof(*mcfg))
+ {
+ rewind(mcfg_file);
+ mcfg = pci_malloc(a, length);
+ if (fread(mcfg, 1, length, mcfg_file) == (size_t)length &&
+ memcmp(mcfg->sdt.signature, "MCFG", 4) == 0 &&
+ mcfg->sdt.length <= (size_t)length &&
+ calculate_checksum((u8 *)mcfg, mcfg->sdt.length) == 0)
+ {
+ fclose(mcfg_file);
+ return mcfg;
+ }
+ }
+ fclose(mcfg_file);
+ }
+ a->debug("failed...");
+ }
+ else
+ a->debug("glob(%s) failed: %d...", acpimcfg, ret);
+ }
+
+ a->debug("searching for ACPI RSDP...");
+ rsdp_address = find_rsdp_address(a, efisystab, use_bsd, use_x86bios);
+ if (!rsdp_address)
+ {
+ a->debug("not found...");
+ return NULL;
+ }
+ a->debug("found at 0x%" PCI_U64_FMT_X "...", rsdp_address);
+
+ if (!check_and_parse_rsdp(physmem, pagesize, rsdp_address, &rsdt_address, &xsdt_address))
+ {
+ a->debug("invalid...");
+ return NULL;
+ }
+
+ mcfg = NULL;
+ a->debug("searching for ACPI MCFG (XSDT=0x%" PCI_U64_FMT_X ", RSDT=0x%lx)...", xsdt_address, (unsigned long)rsdt_address);
+
+ xsdt = xsdt_address ? (struct acpi_xsdt *)check_and_map_sdt(physmem, pagesize, xsdt_address, "XSDT", &map_addr, &map_length) : NULL;
+ if (xsdt)
+ {
+ a->debug("via XSDT...");
+ count = get_xsdt_addresses_count(xsdt);
+ for (i = 0; i < count; i++)
+ {
+ sdt = check_and_map_sdt(physmem, pagesize, xsdt->sdt_addresses[i], "MCFG", &map2_addr, &map2_length);
+ if (sdt)
+ {
+ mcfg = pci_malloc(a, sdt->length);
+ memcpy(mcfg, sdt, sdt->length);
+ physmem_unmap(physmem, map2_addr, map2_length);
+ break;
+ }
+ }
+ physmem_unmap(physmem, map_addr, map_length);
+ if (mcfg)
+ {
+ a->debug("found...");
+ return mcfg;
+ }
+ }
+
+ rsdt = (struct acpi_rsdt *)check_and_map_sdt(physmem, pagesize, rsdt_address, "RSDT", &map_addr, &map_length);
+ if (rsdt)
+ {
+ a->debug("via RSDT...");
+ count = get_rsdt_addresses_count(rsdt);
+ for (i = 0; i < count; i++)
+ {
+ sdt = check_and_map_sdt(physmem, pagesize, rsdt->sdt_addresses[i], "MCFG", &map2_addr, &map2_length);
+ if (sdt)
+ {
+ mcfg = pci_malloc(a, sdt->length);
+ memcpy(mcfg, sdt, sdt->length);
+ physmem_unmap(physmem, map2_addr, map2_length);
+ break;
+ }
+ }
+ physmem_unmap(physmem, map_addr, map_length);
+ if (mcfg)
+ {
+ a->debug("found...");
+ return mcfg;
+ }
+ }
+
+ a->debug("not found...");
+ return NULL;
+}
+
+static void
+get_mcfg_allocation(struct acpi_mcfg *mcfg, unsigned int i, int *domain, u8 *start_bus, u8 *end_bus, u64 *addr, u32 *length)
+{
+ int buses = (int)mcfg->allocations[i].end_bus_number - (int)mcfg->allocations[i].start_bus_number + 1;
+
+ if (domain)
+ *domain = mcfg->allocations[i].pci_segment;
+ if (start_bus)
+ *start_bus = mcfg->allocations[i].start_bus_number;
+ if (end_bus)
+ *end_bus = mcfg->allocations[i].end_bus_number;
+ if (addr)
+ *addr = mcfg->allocations[i].address;
+ if (length)
+ *length = (buses > 0) ? (buses * 32 * 8 * 4096) : 0;
+}
+
+static int
+parse_next_addrs(const char *addrs, const char **next, int *domain, u8 *start_bus, u8 *end_bus, u64 *addr, u32 *length)
+{
+ u64 ullnum;
+ const char *sep1, *sep2;
+ int addr_len;
+ char *endptr;
+ long num;
+ int buses;
+ u64 start_addr;
+
+ if (!*addrs)
+ {
+ if (next)
+ *next = NULL;
+ return 0;
+ }
+
+ endptr = strchr(addrs, ',');
+ if (endptr)
+ addr_len = endptr - addrs;
+ else
+ addr_len = strlen(addrs);
+
+ if (next)
+ *next = endptr ? (endptr+1) : NULL;
+
+ sep1 = memchr(addrs, ':', addr_len);
+ if (!sep1)
+ return 0;
+
+ sep2 = memchr(sep1+1, ':', addr_len - (sep1+1 - addrs));
+ if (!sep2)
+ {
+ sep2 = sep1;
+ sep1 = NULL;
+ }
+
+ if (!sep1)
+ {
+ if (domain)
+ *domain = 0;
+ }
+ else
+ {
+ if (!isxdigit(*addrs))
+ return 0;
+ errno = 0;
+ num = strtol(addrs, &endptr, 16);
+ if (errno || endptr != sep1 || num < 0 || num > INT_MAX)
+ return 0;
+ if (domain)
+ *domain = num;
+ }
+
+ errno = 0;
+ num = strtol(sep1 ? (sep1+1) : addrs, &endptr, 16);
+ if (errno || num < 0 || num > 0xff)
+ return 0;
+ if (start_bus)
+ *start_bus = num;
+
+ buses = -num;
+
+ if (endptr != sep2)
+ {
+ if (*endptr != '-')
+ return 0;
+ errno = 0;
+ num = strtol(endptr+1, &endptr, 16);
+ if (errno || endptr != sep2 || num < 0 || num > 0xff)
+ return 0;
+ buses = num - -buses + 1;
+ if (buses <= 0)
+ return 0;
+ if (end_bus)
+ *end_bus = num;
+ }
+
+ if (!isxdigit(*(sep2+1)))
+ return 0;
+
+ errno = 0;
+ ullnum = strtoull(sep2+1, &endptr, 16);
+ if (errno || (ullnum & 3))
+ return 0;
+ if (addr)
+ *addr = ullnum;
+ start_addr = ullnum;
+
+ if (endptr == addrs + addr_len)
+ {
+ if (buses <= 0)
+ {
+ buses = 0xff - -buses + 1;
+ if (end_bus)
+ *end_bus = 0xff;
+ }
+ if (start_addr + (unsigned)buses * 32 * 8 * 4096 < start_addr)
+ return 0;
+ if (length)
+ *length = buses * 32 * 8 * 4096;
+ }
+ else
+ {
+ if (*endptr != '+' || !isxdigit(*(endptr+1)))
+ return 0;
+ errno = 0;
+ ullnum = strtoull(endptr+1, &endptr, 16);
+ if (errno || endptr != addrs + addr_len || (ullnum & 3) || ullnum > 256 * 32 * 8 * 4096)
+ return 0;
+ if (start_addr + ullnum < start_addr)
+ return 0;
+ if (buses > 0 && ullnum > (unsigned)buses * 32 * 8 * 4096)
+ return 0;
+ if (buses <= 0 && ullnum > (0xff - (unsigned)-buses + 1) * 32 * 8 * 4096)
+ return 0;
+ if (length)
+ *length = ullnum;
+ if (buses <= 0 && end_bus)
+ *end_bus = -buses + (ullnum + 32 * 8 * 4096 - 1) / (32 * 8 * 4096);
+ }
+
+ return 1;
+}
+
+static int
+validate_addrs(const char *addrs)
+{
+ if (!*addrs)
+ return 1;
+
+ while (addrs)
+ if (!parse_next_addrs(addrs, &addrs, NULL, NULL, NULL, NULL, NULL))
+ return 0;
+
+ return 1;
+}
+
+static int
+calculate_bus_addr(u8 start_bus, u64 start_addr, u32 total_length, u8 bus, u64 *addr, u32 *length)
+{
+ u32 offset;
+
+ offset = 32*8*4096 * (bus - start_bus);
+ if (offset >= total_length)
+ return 0;
+
+ *addr = start_addr + offset;
+ *length = total_length - offset;
+
+ if (*length > 32*8*4096)
+ *length = 32*8*4096;
+
+ return 1;
+}
+
+static int
+get_bus_addr(struct acpi_mcfg *mcfg, const char *addrs, int domain, u8 bus, u64 *addr, u32 *length)
+{
+ int cur_domain;
+ u8 start_bus;
+ u8 end_bus;
+ u64 start_addr;
+ u32 total_length;
+ int i, count;
+
+ if (mcfg)
+ {
+ count = get_mcfg_allocations_count(mcfg);
+ for (i = 0; i < count; i++)
+ {
+ get_mcfg_allocation(mcfg, i, &cur_domain, &start_bus, &end_bus, &start_addr, &total_length);
+ if (domain == cur_domain && bus >= start_bus && bus <= end_bus)
+ return calculate_bus_addr(start_bus, start_addr, total_length, bus, addr, length);
+ }
+ return 0;
+ }
+ else
+ {
+ while (addrs)
+ {
+ if (!parse_next_addrs(addrs, &addrs, &cur_domain, &start_bus, &end_bus, &start_addr, &total_length))
+ return 0;
+ if (domain == cur_domain && bus >= start_bus && bus <= end_bus)
+ return calculate_bus_addr(start_bus, start_addr, total_length, bus, addr, length);
+ }
+ return 0;
+ }
+}
+
+static void
+munmap_reg(struct pci_access *a)
+{
+ struct ecam_access *eacc = a->backend_data;
+ struct mmap_cache *cache = eacc->cache;
+ struct physmem *physmem = eacc->physmem;
+ long pagesize = eacc->pagesize;
+
+ if (!cache)
+ return;
+
+ physmem_unmap(physmem, cache->map, cache->length + (cache->addr & (pagesize-1)));
+ pci_mfree(cache);
+ eacc->cache = NULL;
+}
+
+static int
+mmap_reg(struct pci_access *a, int w, int domain, u8 bus, u8 dev, u8 func, int pos, volatile void **reg)
+{
+ struct ecam_access *eacc = a->backend_data;
+ struct mmap_cache *cache = eacc->cache;
+ struct physmem *physmem = eacc->physmem;
+ long pagesize = eacc->pagesize;
+ const char *addrs;
+ void *map;
+ u64 addr;
+ u32 length;
+ u32 offset;
+
+ if (cache && cache->domain == domain && cache->bus == bus && !!cache->w == !!w)
+ {
+ map = cache->map;
+ addr = cache->addr;
+ length = cache->length;
+ }
+ else
+ {
+ addrs = pci_get_param(a, "ecam.addrs");
+ if (!get_bus_addr(eacc->mcfg, addrs, domain, bus, &addr, &length))
+ return 0;
+
+ map = physmem_map(physmem, addr & ~(pagesize-1), length + (addr & (pagesize-1)), w);
+ if (map == (void *)-1)
+ return 0;
+
+ if (cache)
+ physmem_unmap(physmem, cache->map, cache->length + (cache->addr & (pagesize-1)));
+ else
+ cache = eacc->cache = pci_malloc(a, sizeof(*cache));
+
+ cache->map = map;
+ cache->addr = addr;
+ cache->length = length;
+ cache->domain = domain;
+ cache->bus = bus;
+ cache->w = w;
+ }
+
+ /*
+ * Enhanced Configuration Access Mechanism (ECAM) offset according to:
+ * PCI Express Base Specification, Revision 5.0, Version 1.0, Section 7.2.2, Table 7-1, p. 677
+ */
+ offset = ((dev & 0x1f) << 15) | ((func & 0x7) << 12) | (pos & 0xfff);
+
+ if (offset + 4 > length)
+ return 0;
+
+ *reg = (unsigned char *)map + (addr & (pagesize-1)) + offset;
+ return 1;
+}
+
+static void
+ecam_config(struct pci_access *a)
+{
+ physmem_init_config(a);
+ pci_define_param(a, "ecam.acpimcfg", PCI_PATH_ACPI_MCFG, "Path to the ACPI MCFG table");
+ pci_define_param(a, "ecam.efisystab", PCI_PATH_EFI_SYSTAB, "Path to the EFI system table");
+#if defined (__FreeBSD__) || defined (__DragonFly__) || defined(__NetBSD__)
+ pci_define_param(a, "ecam.bsd", "1", "Use BSD kenv or sysctl to find ACPI MCFG table");
+#endif
+#if defined(__amd64__) || defined(__i386__)
+ pci_define_param(a, "ecam.x86bios", "1", "Scan x86 BIOS memory for ACPI MCFG table");
+#endif
+ pci_define_param(a, "ecam.addrs", "", "Physical addresses of memory mapped PCIe ECAM interface"); /* format: [domain:]start_bus[-end_bus]:start_addr[+length],... */
+}
+
+static int
+ecam_detect(struct pci_access *a)
+{
+ int use_addrs = 1, use_acpimcfg = 1, use_efisystab = 1, use_bsd = 1, use_x86bios = 1;
+ const char *acpimcfg = pci_get_param(a, "ecam.acpimcfg");
+ const char *efisystab = pci_get_param(a, "ecam.efisystab");
+#if defined (__FreeBSD__) || defined (__DragonFly__) || defined(__NetBSD__)
+ const char *bsd = pci_get_param(a, "ecam.bsd");
+#endif
+#if defined(__amd64__) || defined(__i386__)
+ const char *x86bios = pci_get_param(a, "ecam.x86bios");
+#endif
+ const char *addrs = pci_get_param(a, "ecam.addrs");
+ struct ecam_access *eacc;
+ glob_t mcfg_glob;
+ int ret;
+
+ if (!*addrs)
+ {
+ a->debug("ecam.addrs was not specified...");
+ use_addrs = 0;
+ }
+
+ if (acpimcfg[0])
+ {
+ ret = glob(acpimcfg, GLOB_NOCHECK, NULL, &mcfg_glob);
+ if (ret == 0)
+ {
+ if (access(mcfg_glob.gl_pathv[0], R_OK))
+ {
+ a->debug("cannot access acpimcfg: %s: %s...", mcfg_glob.gl_pathv[0], strerror(errno));
+ use_acpimcfg = 0;
+ }
+ globfree(&mcfg_glob);
+ }
+ else
+ {
+ a->debug("glob(%s) failed: %d...", acpimcfg, ret);
+ use_acpimcfg = 0;
+ }
+ }
+ else
+ use_acpimcfg = 0;
+
+ if (!efisystab[0] || access(efisystab, R_OK))
+ {
+ if (efisystab[0])
+ a->debug("cannot access efisystab: %s: %s...", efisystab, strerror(errno));
+ use_efisystab = 0;
+ }
+
+#if defined (__FreeBSD__) || defined (__DragonFly__) || defined(__NetBSD__)
+ if (strcmp(bsd, "0") == 0)
+ {
+ a->debug("not using BSD kenv/sysctl...");
+ use_bsd = 0;
+ }
+#else
+ use_bsd = 0;
+#endif
+
+#if defined(__amd64__) || defined(__i386__)
+ if (strcmp(x86bios, "0") == 0)
+ {
+ a->debug("not using x86 BIOS...");
+ use_x86bios = 0;
+ }
+#else
+ use_x86bios = 0;
+#endif
+
+ if (!use_addrs && !use_acpimcfg && !use_efisystab && !use_bsd && !use_x86bios)
+ {
+ a->debug("no ecam source provided");
+ return 0;
+ }
+
+ if (!validate_addrs(addrs))
+ {
+ a->debug("ecam.addrs has invalid format %s", addrs);
+ return 0;
+ }
+
+ if (physmem_access(a, 0))
+ {
+ a->debug("cannot access physical memory: %s", strerror(errno));
+ return 0;
+ }
+
+ if (!use_addrs)
+ {
+ eacc = pci_malloc(a, sizeof(*eacc));
+
+ eacc->physmem = physmem_open(a, a->writeable);
+ if (!eacc->physmem)
+ {
+ a->debug("cannot open physcal memory: %s.", strerror(errno));
+ pci_mfree(eacc);
+ return 0;
+ }
+
+ eacc->pagesize = physmem_get_pagesize(eacc->physmem);
+ if (eacc->pagesize <= 0)
+ {
+ a->debug("Cannot get page size: %s.", strerror(errno));
+ physmem_close(eacc->physmem);
+ pci_mfree(eacc);
+ return 0;
+ }
+
+ eacc->mcfg = NULL;
+ eacc->cache = NULL;
+ a->backend_data = eacc;
+ eacc->mcfg = find_mcfg(a, acpimcfg, efisystab, use_bsd, use_x86bios);
+ if (!eacc->mcfg)
+ {
+ physmem_close(eacc->physmem);
+ pci_mfree(eacc);
+ a->backend_data = NULL;
+ return 0;
+ }
+ }
+
+ if (use_addrs)
+ a->debug("using with ecam addresses %s", addrs);
+ else
+ a->debug("using with%s%s%s%s%s%s", use_acpimcfg ? " acpimcfg=" : "", use_acpimcfg ? acpimcfg : "", use_efisystab ? " efisystab=" : "", use_efisystab ? efisystab : "", use_bsd ? " bsd" : "", use_x86bios ? " x86bios" : "");
+
+ return 1;
+}
+
+static void
+ecam_init(struct pci_access *a)
+{
+ const char *acpimcfg = pci_get_param(a, "ecam.acpimcfg");
+ const char *efisystab = pci_get_param(a, "ecam.efisystab");
+#if defined (__FreeBSD__) || defined (__DragonFly__) || defined(__NetBSD__)
+ const char *bsd = pci_get_param(a, "ecam.bsd");
+#endif
+#if defined(__amd64__) || defined(__i386__)
+ const char *x86bios = pci_get_param(a, "ecam.x86bios");
+#endif
+ const char *addrs = pci_get_param(a, "ecam.addrs");
+ struct physmem *physmem = NULL;
+ struct ecam_access *eacc = a->backend_data;
+ long pagesize = 0;
+ int use_bsd = 0;
+ int use_x86bios = 0;
+ int test_domain = 0;
+ u8 test_bus = 0;
+ volatile void *test_reg;
+
+ if (!validate_addrs(addrs))
+ a->error("Option ecam.addrs has invalid address format \"%s\".", addrs);
+
+ if (!eacc)
+ {
+ physmem = physmem_open(a, a->writeable);
+ if (!physmem)
+ a->error("Cannot open physcal memory: %s.", strerror(errno));
+
+ pagesize = physmem_get_pagesize(physmem);
+ if (pagesize <= 0)
+ a->error("Cannot get page size: %s.", strerror(errno));
+
+ eacc = pci_malloc(a, sizeof(*eacc));
+ eacc->mcfg = NULL;
+ eacc->cache = NULL;
+ eacc->physmem = physmem;
+ eacc->pagesize = pagesize;
+ a->backend_data = eacc;
+ }
+
+ if (!*addrs)
+ {
+#if defined (__FreeBSD__) || defined (__DragonFly__)
+ if (strcmp(bsd, "0") != 0)
+ use_bsd = 1;
+#endif
+#if defined(__amd64__) || defined(__i386__)
+ if (strcmp(x86bios, "0") != 0)
+ use_x86bios = 1;
+#endif
+ if (!eacc->mcfg)
+ eacc->mcfg = find_mcfg(a, acpimcfg, efisystab, use_bsd, use_x86bios);
+ if (!eacc->mcfg)
+ a->error("Option ecam.addrs was not specified and ACPI MCFG table cannot be found.");
+ }
+
+ if (eacc->mcfg)
+ get_mcfg_allocation(eacc->mcfg, 0, &test_domain, &test_bus, NULL, NULL, NULL);
+ else
+ parse_next_addrs(addrs, NULL, &test_domain, &test_bus, NULL, NULL, NULL);
+
+ errno = 0;
+ if (!mmap_reg(a, 0, test_domain, test_bus, 0, 0, 0, &test_reg))
+ a->error("Cannot map ecam region: %s.", errno ? strerror(errno) : "Unknown error");
+}
+
+static void
+ecam_cleanup(struct pci_access *a)
+{
+ struct ecam_access *eacc = a->backend_data;
+
+ munmap_reg(a);
+ physmem_close(eacc->physmem);
+ pci_mfree(eacc->mcfg);
+ pci_mfree(eacc);
+ a->backend_data = NULL;
+}
+
+static void
+ecam_scan(struct pci_access *a)
+{
+ const char *addrs = pci_get_param(a, "ecam.addrs");
+ struct ecam_access *eacc = a->backend_data;
+ u32 *segments;
+ int i, j, count;
+ int domain;
+
+ segments = pci_malloc(a, 0xFFFF/8);
+ memset(segments, 0, 0xFFFF/8);
+
+ if (eacc->mcfg)
+ {
+ count = get_mcfg_allocations_count(eacc->mcfg);
+ for (i = 0; i < count; i++)
+ segments[eacc->mcfg->allocations[i].pci_segment / 32] |= 1 << (eacc->mcfg->allocations[i].pci_segment % 32);
+ }
+ else
+ {
+ while (addrs)
+ {
+ if (parse_next_addrs(addrs, &addrs, &domain, NULL, NULL, NULL, NULL))
+ segments[domain / 32] |= 1 << (domain % 32);
+ }
+ }
+
+ for (i = 0; i < 0xFFFF/32; i++)
+ {
+ if (!segments[i])
+ continue;
+ for (j = 0; j < 32; j++)
+ if (segments[i] & (1 << j))
+ pci_generic_scan_domain(a, 32*i + j);
+ }
+
+ pci_mfree(segments);
+}
+
+static int
+ecam_read(struct pci_dev *d, int pos, byte *buf, int len)
+{
+ volatile void *reg;
+
+ if (pos >= 4096)
+ return 0;
+
+ if (len != 1 && len != 2 && len != 4)
+ return pci_generic_block_read(d, pos, buf, len);
+
+ if (!mmap_reg(d->access, 0, d->domain, d->bus, d->dev, d->func, pos, &reg))
+ return 0;
+
+ switch (len)
+ {
+ case 1:
+ buf[0] = physmem_readb(reg);
+ break;
+ case 2:
+ ((u16 *) buf)[0] = physmem_readw(reg);
+ break;
+ case 4:
+ ((u32 *) buf)[0] = physmem_readl(reg);
+ break;
+ }
+
+ return 1;
+}
+
+static int
+ecam_write(struct pci_dev *d, int pos, byte *buf, int len)
+{
+ volatile void *reg;
+
+ if (pos >= 4096)
+ return 0;
+
+ if (len != 1 && len != 2 && len != 4)
+ return pci_generic_block_read(d, pos, buf, len);
+
+ if (!mmap_reg(d->access, 1, d->domain, d->bus, d->dev, d->func, pos, &reg))
+ return 0;
+
+ switch (len)
+ {
+ case 1:
+ physmem_writeb(buf[0], reg);
+ break;
+ case 2:
+ physmem_writew(((u16 *) buf)[0], reg);
+ break;
+ case 4:
+ physmem_writel(((u32 *) buf)[0], reg);
+ break;
+ }
+
+ return 1;
+}
+
+struct pci_methods pm_ecam = {
+ "ecam",
+ "Raw memory mapped access using PCIe ECAM interface",
+ ecam_config,
+ ecam_detect,
+ ecam_init,
+ ecam_cleanup,
+ ecam_scan,
+ pci_generic_fill_info,
+ ecam_read,
+ ecam_write,
+ NULL, /* read_vpd */
+ NULL, /* init_dev */
+ NULL /* cleanup_dev */
+};
diff --git a/lib/emulated.c b/lib/emulated.c
new file mode 100644
index 0000000..92cd06e
--- /dev/null
+++ b/lib/emulated.c
@@ -0,0 +1,301 @@
+/*
+ * The PCI Library -- Virtual Emulated Config Space Access Functions
+ *
+ * Copyright (c) 2022 Pali Rohár
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "internal.h"
+
+static u32
+ioflg_to_pciflg(pciaddr_t ioflg)
+{
+ u32 flg = 0;
+
+ if ((ioflg & PCI_IORESOURCE_TYPE_BITS) == PCI_IORESOURCE_IO)
+ flg = PCI_BASE_ADDRESS_SPACE_IO;
+ else if ((ioflg & PCI_IORESOURCE_TYPE_BITS) == PCI_IORESOURCE_MEM)
+ {
+ flg = PCI_BASE_ADDRESS_SPACE_MEMORY;
+ if (ioflg & PCI_IORESOURCE_MEM_64)
+ flg |= PCI_BASE_ADDRESS_MEM_TYPE_64;
+ else
+ flg |= PCI_BASE_ADDRESS_MEM_TYPE_32;
+ if (ioflg & PCI_IORESOURCE_PREFETCH)
+ flg |= PCI_BASE_ADDRESS_MEM_PREFETCH;
+ }
+
+ return flg;
+}
+
+static u32
+baseres_to_pcires(pciaddr_t addr, pciaddr_t ioflg, int *have_sec, u32 *sec_val)
+{
+ u32 val = ioflg_to_pciflg(ioflg);
+
+ if (have_sec)
+ *have_sec = 0;
+
+ if ((val & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO && addr <= 0xffffffff)
+ val |= addr & PCI_BASE_ADDRESS_IO_MASK;
+ else if ((val & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_MEMORY)
+ {
+ val |= addr & PCI_BASE_ADDRESS_MEM_MASK;
+ if ((val & PCI_BASE_ADDRESS_MEM_TYPE_64) && have_sec)
+ {
+ *have_sec = 1;
+ *sec_val = addr >> 32;
+ }
+ }
+
+ return val;
+}
+
+static inline u32
+even_baseres_to_pcires(pciaddr_t addr, pciaddr_t ioflg)
+{
+ return baseres_to_pcires(addr, ioflg, NULL, NULL);
+}
+
+static inline u32
+odd_baseres_to_pcires(pciaddr_t addr0, pciaddr_t ioflg0, pciaddr_t addr, pciaddr_t ioflg)
+{
+ int have_sec;
+ u32 val;
+ baseres_to_pcires(addr0, ioflg0, &have_sec, &val);
+ if (!have_sec)
+ val = baseres_to_pcires(addr, ioflg, NULL, NULL);
+ return val;
+}
+
+int
+pci_emulated_read(struct pci_dev *d, int pos, byte *buf, int len)
+{
+ u32 ht = PCI_HEADER_TYPE_NORMAL;
+ u32 val = 0;
+ int i;
+
+ if (pos >= 64)
+ return 0;
+
+ if (len > 4)
+ return pci_generic_block_read(d, pos, buf, len);
+
+ if (d->device_class == PCI_CLASS_BRIDGE_PCI)
+ ht = PCI_HEADER_TYPE_BRIDGE;
+ else if (d->device_class == PCI_CLASS_BRIDGE_CARDBUS)
+ ht = PCI_HEADER_TYPE_CARDBUS;
+
+ switch (pos & ~3)
+ {
+ case PCI_COMMAND:
+ for (i = 0; i < 6; i++)
+ {
+ if (!d->size[i])
+ continue;
+ if ((d->flags[i] & PCI_IORESOURCE_TYPE_BITS) == PCI_IORESOURCE_IO)
+ val |= PCI_COMMAND_IO;
+ else if ((d->flags[i] & PCI_IORESOURCE_TYPE_BITS) == PCI_IORESOURCE_MEM)
+ val |= PCI_COMMAND_MEMORY;
+ }
+ break;
+ case PCI_VENDOR_ID:
+ val = (d->device_id << 16) | d->vendor_id;
+ break;
+ case PCI_CLASS_REVISION:
+ val = (d->device_class << 16) | (d->prog_if << 8) | d->rev_id;
+ break;
+ case PCI_CACHE_LINE_SIZE:
+ val = ht << 16;
+ break;
+ case PCI_BASE_ADDRESS_0:
+ val = even_baseres_to_pcires(d->base_addr[0], d->flags[0]);
+ break;
+ case PCI_INTERRUPT_LINE:
+ val = (d->irq >= 0 && d->irq <= 0xff) ? d->irq : 0;
+ break;
+ }
+
+ if ((pos & ~3) == PCI_BASE_ADDRESS_1 && (ht == PCI_HEADER_TYPE_NORMAL || ht == PCI_HEADER_TYPE_BRIDGE))
+ val = odd_baseres_to_pcires(d->base_addr[0], d->flags[0], d->base_addr[1], d->flags[1]);
+
+ if (ht == PCI_HEADER_TYPE_NORMAL)
+ switch (pos & ~3)
+ {
+ case PCI_BASE_ADDRESS_2:
+ val = even_baseres_to_pcires(d->base_addr[2], d->flags[2]);
+ break;
+ case PCI_BASE_ADDRESS_3:
+ val = odd_baseres_to_pcires(d->base_addr[2], d->flags[2], d->base_addr[3], d->flags[3]);
+ break;
+ case PCI_BASE_ADDRESS_4:
+ val = even_baseres_to_pcires(d->base_addr[4], d->flags[4]);
+ break;
+ case PCI_BASE_ADDRESS_5:
+ val = odd_baseres_to_pcires(d->base_addr[4], d->flags[4], d->base_addr[5], d->flags[5]);
+ break;
+ case PCI_SUBSYSTEM_VENDOR_ID:
+ val = (d->subsys_id << 16) | d->subsys_vendor_id;
+ break;
+ case PCI_ROM_ADDRESS:
+ val = d->rom_base_addr & PCI_ROM_ADDRESS_MASK;
+ if (val)
+ val |= PCI_ROM_ADDRESS_ENABLE;
+ break;
+ }
+ else if (ht == PCI_HEADER_TYPE_BRIDGE)
+ switch (pos & ~3)
+ {
+ case PCI_COMMAND:
+ if (d->bridge_size[0])
+ val |= PCI_COMMAND_IO;
+ if (d->bridge_size[1] || d->bridge_size[2])
+ val |= PCI_COMMAND_MEMORY;
+ break;
+ case PCI_PRIMARY_BUS:
+ val = d->bus;
+ break;
+ case PCI_IO_BASE:
+ if (d->bridge_size[0])
+ {
+ val = (((((d->bridge_base_addr[0] + d->bridge_size[0] - 1) >> 8) & PCI_IO_RANGE_MASK) << 8) & 0xff00) |
+ (((d->bridge_base_addr[0] >> 8) & PCI_IO_RANGE_MASK) & 0x00ff);
+ if ((d->bridge_flags[0] & PCI_IORESOURCE_IO_16BIT_ADDR) &&
+ d->bridge_base_addr[0] + d->bridge_size[0] - 1 <= 0xffff)
+ val |= (PCI_IO_RANGE_TYPE_16 << 8) | PCI_IO_RANGE_TYPE_16;
+ else
+ val |= (PCI_IO_RANGE_TYPE_32 << 8) | PCI_IO_RANGE_TYPE_32;
+ }
+ else
+ val = 0xff & PCI_IO_RANGE_MASK;
+ break;
+ case PCI_MEMORY_BASE:
+ if (d->bridge_size[1])
+ val = (((((d->bridge_base_addr[1] + d->bridge_size[1] - 1) >> 16) & PCI_MEMORY_RANGE_MASK) << 16) & 0xffff0000) |
+ (((d->bridge_base_addr[1] >> 16) & PCI_MEMORY_RANGE_MASK) & 0x0000ffff);
+ else
+ val = 0xffff & PCI_MEMORY_RANGE_MASK;
+ break;
+ case PCI_PREF_MEMORY_BASE:
+ if (d->bridge_size[2])
+ {
+ val = (((((d->bridge_base_addr[2] + d->bridge_size[2] - 1) >> 16) & PCI_PREF_RANGE_MASK) << 16) & 0xffff0000) |
+ (((d->bridge_base_addr[2] >> 16) & PCI_PREF_RANGE_MASK) & 0x0000ffff);
+ if ((d->bridge_flags[2] & PCI_IORESOURCE_MEM_64) ||
+ d->bridge_base_addr[2] + d->bridge_size[2] - 1 > 0xffffffff)
+ val |= (PCI_PREF_RANGE_TYPE_64 << 16) | PCI_PREF_RANGE_TYPE_64;
+ else
+ val |= (PCI_PREF_RANGE_TYPE_32 << 16) | PCI_PREF_RANGE_TYPE_32;
+ }
+ else
+ val = 0xffff & PCI_PREF_RANGE_MASK;
+ break;
+ case PCI_PREF_BASE_UPPER32:
+ if (d->bridge_size[2])
+ val = d->bridge_base_addr[2] >> 32;
+ break;
+ case PCI_PREF_LIMIT_UPPER32:
+ if (d->bridge_size[2])
+ val = (d->bridge_base_addr[2] + d->bridge_size[2] - 1) >> 32;
+ break;
+ case PCI_IO_BASE_UPPER16:
+ if (d->bridge_size[0])
+ val = ((((d->bridge_base_addr[0] + d->bridge_size[0] - 1) >> 16) << 16) & 0xffff0000) |
+ ((d->bridge_base_addr[0] >> 16) & 0x0000ffff);
+ break;
+ case PCI_ROM_ADDRESS1:
+ val = d->rom_base_addr & PCI_ROM_ADDRESS_MASK;
+ if (val)
+ val |= PCI_ROM_ADDRESS_ENABLE;
+ break;
+ }
+ else if (ht == PCI_HEADER_TYPE_CARDBUS)
+ switch (pos & ~3)
+ {
+ case PCI_COMMAND:
+ if (d->bridge_size[0] || d->bridge_size[1])
+ val |= PCI_COMMAND_MEMORY;
+ if (d->bridge_size[2] || d->bridge_size[3])
+ val |= PCI_COMMAND_IO;
+ break;
+ case PCI_CB_PRIMARY_BUS:
+ val = d->bus;
+ break;
+ case PCI_CB_MEMORY_BASE_0:
+ if (d->bridge_size[0])
+ val = d->bridge_base_addr[0] & ~0xfff;
+ else
+ val = 0xffffffff & ~0xfff;
+ break;
+ case PCI_CB_MEMORY_LIMIT_0:
+ if (d->bridge_size[0])
+ val = (d->bridge_base_addr[0] + d->bridge_size[0] - 1) & ~0xfff;
+ break;
+ case PCI_CB_MEMORY_BASE_1:
+ if (d->bridge_size[1])
+ val = d->bridge_base_addr[1] & ~0xfff;
+ else
+ val = 0xffffffff & ~0xfff;
+ break;
+ case PCI_CB_MEMORY_LIMIT_1:
+ if (d->bridge_size[1])
+ val = (d->bridge_base_addr[1] + d->bridge_size[1] - 1) & ~0xfff;
+ break;
+ case PCI_CB_IO_BASE_0:
+ if (d->bridge_size[2])
+ {
+ val = d->bridge_base_addr[2] & PCI_CB_IO_RANGE_MASK;
+ if ((d->bridge_flags[2] & PCI_IORESOURCE_IO_16BIT_ADDR) ||
+ d->bridge_base_addr[2] + d->bridge_size[2] - 1 <= 0xffff)
+ val |= PCI_IO_RANGE_TYPE_16;
+ else
+ val |= PCI_IO_RANGE_TYPE_32;
+ }
+ else
+ val = 0x0000ffff & PCI_CB_IO_RANGE_MASK;
+ break;
+ case PCI_CB_IO_LIMIT_0:
+ if (d->bridge_size[2])
+ val = (d->bridge_base_addr[2] + d->bridge_size[2] - 1) & PCI_CB_IO_RANGE_MASK;
+ break;
+ case PCI_CB_IO_BASE_1:
+ if (d->bridge_size[3])
+ {
+ val = d->bridge_base_addr[3] & PCI_CB_IO_RANGE_MASK;
+ if ((d->bridge_flags[3] & PCI_IORESOURCE_IO_16BIT_ADDR) ||
+ d->bridge_base_addr[3] + d->bridge_size[3] - 1 <= 0xffff)
+ val |= PCI_IO_RANGE_TYPE_16;
+ else
+ val |= PCI_IO_RANGE_TYPE_32;
+ }
+ else
+ val = 0x0000ffff & PCI_CB_IO_RANGE_MASK;
+ break;
+ case PCI_CB_IO_LIMIT_1:
+ if (d->bridge_size[3])
+ val = (d->bridge_base_addr[3] + d->bridge_size[3] - 1) & PCI_CB_IO_RANGE_MASK;
+ break;
+ case PCI_CB_BRIDGE_CONTROL:
+ if (d->bridge_flags[0] & PCI_IORESOURCE_PREFETCH)
+ val |= PCI_CB_BRIDGE_CTL_PREFETCH_MEM0;
+ if (d->bridge_flags[1] & PCI_IORESOURCE_PREFETCH)
+ val |= PCI_CB_BRIDGE_CTL_PREFETCH_MEM1;
+ break;
+ case PCI_CB_SUBSYSTEM_VENDOR_ID:
+ val = (d->subsys_id << 16) | d->subsys_vendor_id;
+ break;
+ }
+
+ if (len <= 2)
+ val = (val >> (8 * (pos & 3))) & ((1 << (len * 8)) - 1);
+
+ while (len-- > 0)
+ {
+ *(buf++) = val & 0xff;
+ val >>= 8;
+ }
+ return 1;
+}
diff --git a/lib/fbsd-device.c b/lib/fbsd-device.c
new file mode 100644
index 0000000..2ea5e84
--- /dev/null
+++ b/lib/fbsd-device.c
@@ -0,0 +1,366 @@
+/*
+ * The PCI Library -- FreeBSD /dev/pci access
+ *
+ * Copyright (c) 1999 Jari Kirma <kirma@cs.hut.fi>
+ * Updated in 2003 by Samy Al Bahra <samy@kerneled.com>
+ * Updated in 2017 by Imre Vadász <imrevdsz@gmail.com>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <osreldate.h>
+#include <stdint.h>
+
+#ifdef __FreeBSD_kernel_version
+# ifndef __FreeBSD_version
+# define __FreeBSD_version __FreeBSD_kernel_version
+# endif
+#endif
+
+#if __FreeBSD_version < 430000 && !defined(__DragonFly__)
+# include <pci/pcivar.h>
+# include <pci/pci_ioctl.h>
+#else
+# include <sys/pciio.h>
+#endif
+
+#include "internal.h"
+
+static void
+fbsd_config(struct pci_access *a)
+{
+ pci_define_param(a, "fbsd.path", PCI_PATH_FBSD_DEVICE, "Path to the FreeBSD PCI device");
+}
+
+static int
+fbsd_detect(struct pci_access *a)
+{
+ char *name = pci_get_param(a, "fbsd.path");
+
+ if (access(name, R_OK))
+ {
+ a->warning("Cannot open %s", name);
+ return 0;
+ }
+ a->debug("...using %s", name);
+ return 1;
+}
+
+static void
+fbsd_init(struct pci_access *a)
+{
+ char *name = pci_get_param(a, "fbsd.path");
+ int fd;
+
+ a->fd = -1;
+ a->fd_rw = -1;
+ /*
+ * When opening /dev/pci as read-write fails, retry with readonly which
+ * will still allow us to gain some information via the PCIOCGETCONF and
+ * PCIOCGETBAR IOCTLs, even without generic read access to the PCI config
+ * space.
+ */
+ fd = open(name, O_RDWR, 0);
+ if (fd < 0)
+ {
+ fd = open(name, O_RDONLY, 0);
+ if (fd < 0)
+ a->error("fbsd_init: %s open failed", name);
+ else
+ {
+ a->debug("fbsd_init: Fallback to read-only opened %s", name);
+ a->fd = fd;
+ }
+ }
+ else
+ a->fd_rw = fd;
+}
+
+static void
+fbsd_cleanup(struct pci_access *a)
+{
+ if (a->fd >= 0)
+ {
+ close(a->fd);
+ a->fd = -1;
+ }
+ if (a->fd_rw >= 0)
+ {
+ close(a->fd_rw);
+ a->fd_rw = -1;
+ }
+}
+
+static void
+fbsd_scan(struct pci_access *a)
+{
+ struct pci_conf_io conf;
+ struct pci_conf *matches;
+ struct pci_dev *t;
+ uint32_t offset = 0;
+ unsigned int i;
+
+ matches = calloc(32, sizeof(struct pci_conf));
+ if (matches == NULL)
+ {
+ a->error("calloc: %s", strerror(errno));
+ return;
+ }
+
+ conf.generation = 0;
+ do
+ {
+ conf.pat_buf_len = 0;
+ conf.num_patterns = 0;
+ conf.patterns = NULL;
+ conf.match_buf_len = 32 * sizeof(struct pci_conf);
+ conf.num_matches = 32;
+ conf.matches = matches;
+ conf.offset = offset;
+ conf.status = 0;
+ if (ioctl(a->fd_rw >= 0 ? a->fd_rw : a->fd, PCIOCGETCONF, &conf) < 0)
+ {
+ if (errno == ENODEV)
+ break;
+ a->error("fbsd_scan: ioctl(PCIOCGETCONF) failed: %s",
+ strerror(errno));
+ }
+ /* PCI_GETCONF_LIST_CHANGED would require us to start over. */
+ if (conf.status == PCI_GETCONF_ERROR ||
+ conf.status == PCI_GETCONF_LIST_CHANGED)
+ {
+ a->error("fbsd_scan: ioctl(PCIOCGETCONF) failed");
+ break;
+ }
+ for (i = 0; i < conf.num_matches; i++)
+ {
+ t = pci_alloc_dev(a);
+ t->bus = matches[i].pc_sel.pc_bus;
+ t->dev = matches[i].pc_sel.pc_dev;
+ t->func = matches[i].pc_sel.pc_func;
+ t->domain = matches[i].pc_sel.pc_domain;
+ t->domain_16 = matches[i].pc_sel.pc_domain;
+ t->vendor_id = matches[i].pc_vendor;
+ t->device_id = matches[i].pc_device;
+ t->known_fields = PCI_FILL_IDENT;
+ t->hdrtype = matches[i].pc_hdr;
+ pci_link_dev(a, t);
+ }
+ offset += conf.num_matches;
+ }
+ while (conf.status == PCI_GETCONF_MORE_DEVS);
+
+ free(matches);
+}
+
+static void
+fbsd_fill_info(struct pci_dev *d, unsigned int flags)
+{
+ struct pci_conf_io conf;
+ struct pci_bar_io bar;
+ struct pci_match_conf pattern;
+ struct pci_conf match;
+ int i;
+
+ if (d->access->fd_rw >= 0)
+ return pci_generic_fill_info(d, flags);
+
+ /*
+ * Can only handle PCI_FILL_IDENT, PCI_FILL_CLASS, PCI_FILL_BASES and
+ * PCI_FILL_SIZES requests with the PCIOCGETCONF and PCIOCGETBAR IOCTLs.
+ */
+
+ conf.pat_buf_len = sizeof(struct pci_match_conf);
+ conf.num_patterns = 1;
+ conf.patterns = &pattern;
+ conf.match_buf_len = sizeof(struct pci_conf);
+ conf.num_matches = 1;
+ conf.matches = &match;
+ conf.offset = 0;
+ conf.generation = 0;
+ conf.status = 0;
+
+ pattern.pc_sel.pc_domain = d->domain;
+ pattern.pc_sel.pc_bus = d->bus;
+ pattern.pc_sel.pc_dev = d->dev;
+ pattern.pc_sel.pc_func = d->func;
+ pattern.flags = PCI_GETCONF_MATCH_DOMAIN | PCI_GETCONF_MATCH_BUS |
+ PCI_GETCONF_MATCH_DEV | PCI_GETCONF_MATCH_FUNC;
+
+ if (ioctl(d->access->fd, PCIOCGETCONF, &conf) < 0)
+ {
+ if (errno != ENODEV)
+ d->access->error("fbsd_fill_info: ioctl(PCIOCGETCONF) failed: %s", strerror(errno));
+ return;
+ }
+
+ if (want_fill(d, flags, PCI_FILL_IDENT))
+ {
+ d->vendor_id = match.pc_vendor;
+ d->device_id = match.pc_device;
+ }
+ if (want_fill(d, flags, PCI_FILL_CLASS))
+ d->device_class = (match.pc_class << 8) | match.pc_subclass;
+ if (want_fill(d, flags, PCI_FILL_BASES | PCI_FILL_SIZES))
+ {
+ d->rom_base_addr = 0;
+ d->rom_size = 0;
+ for (i = 0; i < 6; i++)
+ {
+ bar.pbi_sel.pc_domain = d->domain;
+ bar.pbi_sel.pc_bus = d->bus;
+ bar.pbi_sel.pc_dev = d->dev;
+ bar.pbi_sel.pc_func = d->func;
+ bar.pbi_reg = 0x10 + 4*i;
+ bar.pbi_enabled = 0;
+ bar.pbi_base = 0;
+ bar.pbi_length = 0;
+ if (ioctl(d->access->fd, PCIOCGETBAR, &bar) < 0)
+ {
+ if (errno == ENODEV)
+ return;
+ if (errno == EINVAL)
+ {
+ d->base_addr[i] = 0;
+ d->size[i] = 0;
+ }
+ else
+ d->access->error("fbsd_fill_info: ioctl(PCIOCGETBAR) failed: %s", strerror(errno));
+ }
+ else
+ {
+ d->base_addr[i] = bar.pbi_base;
+ d->size[i] = bar.pbi_length;
+ }
+ }
+ }
+}
+
+static int
+fbsd_read(struct pci_dev *d, int pos, byte *buf, int len)
+{
+ struct pci_io pi;
+
+ if (d->access->fd_rw < 0)
+ {
+ d->access->warning("fbsd_read: missing permissions");
+ return 0;
+ }
+
+ if (!(len == 1 || len == 2 || len == 4))
+ return pci_generic_block_read(d, pos, buf, len);
+
+ if (pos >= 4096)
+ return 0;
+
+#if __FreeBSD_version >= 700053 || defined(__DragonFly__)
+ pi.pi_sel.pc_domain = d->domain;
+#else
+ if (d->domain)
+ return 0;
+#endif
+ pi.pi_sel.pc_bus = d->bus;
+ pi.pi_sel.pc_dev = d->dev;
+ pi.pi_sel.pc_func = d->func;
+
+ pi.pi_reg = pos;
+ pi.pi_width = len;
+
+ if (ioctl(d->access->fd_rw, PCIOCREAD, &pi) < 0)
+ {
+ if (errno == ENODEV)
+ return 0;
+ d->access->error("fbsd_read: ioctl(PCIOCREAD) failed: %s", strerror(errno));
+ }
+
+ switch (len)
+ {
+ case 1:
+ buf[0] = (u8) pi.pi_data;
+ break;
+ case 2:
+ ((u16 *) buf)[0] = cpu_to_le16((u16) pi.pi_data);
+ break;
+ case 4:
+ ((u32 *) buf)[0] = cpu_to_le32((u32) pi.pi_data);
+ break;
+ }
+ return 1;
+}
+
+static int
+fbsd_write(struct pci_dev *d, int pos, byte *buf, int len)
+{
+ struct pci_io pi;
+
+ if (d->access->fd_rw < 0)
+ {
+ d->access->warning("fbsd_write: missing permissions");
+ return 0;
+ }
+
+ if (!(len == 1 || len == 2 || len == 4))
+ return pci_generic_block_write(d, pos, buf, len);
+
+ if (pos >= 4096)
+ return 0;
+
+#if __FreeBSD_version >= 700053 || defined(__DragonFly__)
+ pi.pi_sel.pc_domain = d->domain;
+#else
+ if (d->domain)
+ return 0;
+#endif
+ pi.pi_sel.pc_bus = d->bus;
+ pi.pi_sel.pc_dev = d->dev;
+ pi.pi_sel.pc_func = d->func;
+
+ pi.pi_reg = pos;
+ pi.pi_width = len;
+
+ switch (len)
+ {
+ case 1:
+ pi.pi_data = buf[0];
+ break;
+ case 2:
+ pi.pi_data = le16_to_cpu(((u16 *) buf)[0]);
+ break;
+ case 4:
+ pi.pi_data = le32_to_cpu(((u32 *) buf)[0]);
+ break;
+ }
+
+ if (ioctl(d->access->fd_rw, PCIOCWRITE, &pi) < 0)
+ {
+ if (errno == ENODEV)
+ return 0;
+ d->access->error("fbsd_write: ioctl(PCIOCWRITE) failed: %s", strerror(errno));
+ }
+
+ return 1;
+}
+
+struct pci_methods pm_fbsd_device = {
+ "fbsd-device",
+ "FreeBSD /dev/pci device",
+ fbsd_config,
+ fbsd_detect,
+ fbsd_init,
+ fbsd_cleanup,
+ fbsd_scan,
+ fbsd_fill_info,
+ fbsd_read,
+ fbsd_write,
+ NULL, /* read_vpd */
+ NULL, /* dev_init */
+ NULL /* dev_cleanup */
+};
diff --git a/lib/filter.c b/lib/filter.c
new file mode 100644
index 0000000..0301f49
--- /dev/null
+++ b/lib/filter.c
@@ -0,0 +1,334 @@
+/*
+ * The PCI Library -- Device Filtering
+ *
+ * Copyright (c) 1998--2022 Martin Mares <mj@ucw.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "internal.h"
+
+void pci_filter_init_v38(struct pci_access *a UNUSED, struct pci_filter *f) VERSIONED_ABI;
+char *pci_filter_parse_slot_v38(struct pci_filter *f, char *str) VERSIONED_ABI;
+char *pci_filter_parse_id_v38(struct pci_filter *f, char *str) VERSIONED_ABI;
+int pci_filter_match_v38(struct pci_filter *f, struct pci_dev *d) VERSIONED_ABI;
+
+void
+pci_filter_init_v38(struct pci_access *a UNUSED, struct pci_filter *f)
+{
+ memset((byte *) f, 0, sizeof(*f));
+ f->domain = f->bus = f->slot = f->func = -1;
+ f->vendor = f->device = -1;
+ f->device_class = -1;
+ f->device_class_mask = ~0U;
+ f->prog_if = -1;
+}
+
+#define BUF_SIZE 64
+
+static char *
+split_to_fields(char *str, char *buffer, int sep, char **fields, int num_fields)
+{
+ if (buffer)
+ {
+ if (strlen(str) >= BUF_SIZE)
+ return "Expression too long";
+ strcpy(buffer, str);
+ str = buffer;
+ }
+
+ int i = 0;
+
+ for (;;)
+ {
+ if (i >= num_fields)
+ return "Too many fields";
+ fields[i++] = str;
+ while (*str && *str != sep)
+ str++;
+ if (!*str)
+ break;
+ *str++ = 0;
+ }
+
+ while (i < num_fields)
+ fields[i++] = NULL;
+
+ return NULL;
+}
+
+static int
+field_defined(char *field)
+{
+ return field && field[0] && strcmp(field, "*");
+}
+
+static int
+parse_hex_field(char *str, int *outp, unsigned int *maskp, unsigned int max)
+{
+ unsigned int out = 0;
+ unsigned int mask = ~0U;
+ unsigned int bound = 0;
+
+ if (!field_defined(str))
+ return 1; // and keep the defaults
+
+ // Historically, filters allowed writing hexadecimal numbers with leading "0x".
+ // This was never intentional nor documented, but some people relied on it.
+ if (!maskp && str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))
+ str += 2;
+
+ while (*str)
+ {
+ int c = *str++;
+ int d;
+
+ if ((c == 'x' || c == 'X') && maskp)
+ {
+ out = out << 4;
+ bound = (bound << 4) | 1;
+ mask = mask << 4;
+ }
+ else
+ {
+ if (c >= '0' && c <= '9')
+ d = c - '0';
+ else if (c >= 'A' && c <= 'F')
+ d = c - 'A' + 10;
+ else if (c >= 'a' && c <= 'f')
+ d = c - 'a' + 10;
+ else
+ return 0;
+
+ out = (out << 4) | d;
+ bound = (bound << 4) | d;
+ mask = (mask << 4) | 0xf;
+ }
+
+ if (bound > max)
+ return 0;
+ }
+
+ *outp = out;
+ if (maskp)
+ *maskp = mask;
+ return 1;
+}
+
+/* Slot filter syntax: [[[domain]:][bus]:][slot][.[func]] */
+
+char *
+pci_filter_parse_slot_v38(struct pci_filter *f, char *str)
+{
+ char buf[BUF_SIZE];
+ char *fields[3];
+ char *err;
+
+ if (err = split_to_fields(str, buf, ':', fields, 3))
+ return err;
+
+ int i = 0;
+ if (fields[2])
+ {
+ if (!parse_hex_field(fields[0], &f->domain, NULL, 0x7fffffff))
+ return "Invalid domain number";
+ i++;
+ }
+
+ if (fields[i+1])
+ {
+ if (!parse_hex_field(fields[i], &f->bus, NULL, 0xff))
+ return "Invalid bus number";
+ i++;
+ }
+
+ char *fdev = fields[i];
+ if (field_defined(fdev))
+ {
+ char *sfields[2];
+ if (split_to_fields(fdev, NULL, '.', sfields, 2))
+ return "Invalid slot/function number";
+
+ if (!parse_hex_field(sfields[0], &f->slot, NULL, 0x1f))
+ return "Invalid slot number";
+
+ if (!parse_hex_field(sfields[1], &f->func, NULL, 7))
+ return "Invalid function number";
+ }
+
+ return NULL;
+}
+
+/* ID filter syntax: [vendor]:[device][:class[:progif]] */
+
+char *
+pci_filter_parse_id_v38(struct pci_filter *f, char *str)
+{
+ char buf[BUF_SIZE];
+ char *fields[4];
+ char *err;
+
+ if (err = split_to_fields(str, buf, ':', fields, 4))
+ return err;
+
+ if (!fields[1])
+ return "At least two fields must be given";
+
+ if (!parse_hex_field(fields[0], &f->vendor, NULL, 0xffff))
+ return "Invalid vendor ID";
+
+ if (!parse_hex_field(fields[1], &f->device, NULL, 0xffff))
+ return "Invalid device ID";
+
+ if (!parse_hex_field(fields[2], &f->device_class, &f->device_class_mask, 0xffff))
+ return "Invalid class code";
+
+ if (!parse_hex_field(fields[3], &f->prog_if, NULL, 0xff))
+ return "Invalid programming interface code";
+
+ return NULL;
+}
+
+int
+pci_filter_match_v38(struct pci_filter *f, struct pci_dev *d)
+{
+ if ((f->domain >= 0 && f->domain != d->domain) ||
+ (f->bus >= 0 && f->bus != d->bus) ||
+ (f->slot >= 0 && f->slot != d->dev) ||
+ (f->func >= 0 && f->func != d->func))
+ return 0;
+ if (f->device >= 0 || f->vendor >= 0)
+ {
+ pci_fill_info_v38(d, PCI_FILL_IDENT);
+ if ((f->device >= 0 && f->device != d->device_id) ||
+ (f->vendor >= 0 && f->vendor != d->vendor_id))
+ return 0;
+ }
+ if (f->device_class >= 0)
+ {
+ pci_fill_info_v38(d, PCI_FILL_CLASS);
+ if ((f->device_class ^ d->device_class) & f->device_class_mask)
+ return 0;
+ }
+ if (f->prog_if >= 0)
+ {
+ pci_fill_info_v38(d, PCI_FILL_CLASS_EXT);
+ if (f->prog_if != d->prog_if)
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Before pciutils v3.3, struct pci_filter had fewer fields,
+ * so we have to provide compatibility wrappers.
+ */
+
+struct pci_filter_v30 {
+ int domain, bus, slot, func; /* -1 = ANY */
+ int vendor, device;
+};
+
+void pci_filter_init_v30(struct pci_access *a, struct pci_filter_v30 *f) VERSIONED_ABI;
+char *pci_filter_parse_slot_v30(struct pci_filter_v30 *f, char *str) VERSIONED_ABI;
+char *pci_filter_parse_id_v30(struct pci_filter_v30 *f, char *str) VERSIONED_ABI;
+int pci_filter_match_v30(struct pci_filter_v30 *f, struct pci_dev *d) VERSIONED_ABI;
+
+static void
+pci_filter_import_v30(struct pci_filter_v30 *old, struct pci_filter *new)
+{
+ new->domain = old->domain;
+ new->bus = old->bus;
+ new->slot = old->slot;
+ new->func = old->func;
+ new->vendor = old->vendor;
+ new->device = old->device;
+ new->device_class = -1;
+ new->device_class_mask = ~0U;
+ new->prog_if = -1;
+}
+
+static void
+pci_filter_export_v30(struct pci_filter *new, struct pci_filter_v30 *old)
+{
+ old->domain = new->domain;
+ old->bus = new->bus;
+ old->slot = new->slot;
+ old->func = new->func;
+ old->vendor = new->vendor;
+ old->device = new->device;
+}
+
+void
+pci_filter_init_v30(struct pci_access *a, struct pci_filter_v30 *f)
+{
+ struct pci_filter new;
+ pci_filter_init_v38(a, &new);
+ pci_filter_export_v30(&new, f);
+}
+
+char *
+pci_filter_parse_slot_v30(struct pci_filter_v30 *f, char *str)
+{
+ struct pci_filter new;
+ char *err;
+ pci_filter_import_v30(f, &new);
+ if (err = pci_filter_parse_slot_v38(&new, str))
+ return err;
+ pci_filter_export_v30(&new, f);
+ return NULL;
+}
+
+char *
+pci_filter_parse_id_v30(struct pci_filter_v30 *f, char *str)
+{
+ struct pci_filter new;
+ char *err;
+ pci_filter_import_v30(f, &new);
+ if (err = pci_filter_parse_id_v38(&new, str))
+ return err;
+ if (new.device_class >= 0 || new.prog_if >= 0)
+ return "Filtering by class or programming interface not supported in this program";
+ pci_filter_export_v30(&new, f);
+ return NULL;
+}
+
+int
+pci_filter_match_v30(struct pci_filter_v30 *f, struct pci_dev *d)
+{
+ struct pci_filter new;
+ pci_filter_import_v30(f, &new);
+ return pci_filter_match_v38(&new, d);
+}
+
+// Version 3.3 is the same as version 3.8, only device_class_mask and prog_if were not implemented
+// (their positions in struct pci_filter were declared as RFU).
+
+STATIC_ALIAS(void pci_filter_init(struct pci_access *a, struct pci_filter *f), pci_filter_init_v38(a, f));
+DEFINE_ALIAS(void pci_filter_init_v33(struct pci_access *a, struct pci_filter *f), pci_filter_init_v38);
+SYMBOL_VERSION(pci_filter_init_v30, pci_filter_init@LIBPCI_3.0);
+SYMBOL_VERSION(pci_filter_init_v33, pci_filter_init@LIBPCI_3.3);
+SYMBOL_VERSION(pci_filter_init_v38, pci_filter_init@@LIBPCI_3.8);
+
+STATIC_ALIAS(char *pci_filter_parse_slot(struct pci_filter *f, char *str), pci_filter_parse_slot_v38(f, str));
+DEFINE_ALIAS(char *pci_filter_parse_slot_v33(struct pci_filter *f, char *str), pci_filter_parse_slot_v38);
+SYMBOL_VERSION(pci_filter_parse_slot_v30, pci_filter_parse_slot@LIBPCI_3.0);
+SYMBOL_VERSION(pci_filter_parse_slot_v33, pci_filter_parse_slot@LIBPCI_3.3);
+SYMBOL_VERSION(pci_filter_parse_slot_v38, pci_filter_parse_slot@@LIBPCI_3.8);
+
+STATIC_ALIAS(char *pci_filter_parse_id(struct pci_filter *f, char *str), pci_filter_parse_id_v38(f, str));
+DEFINE_ALIAS(char *pci_filter_parse_id_v33(struct pci_filter *f, char *str), pci_filter_parse_id_v38);
+SYMBOL_VERSION(pci_filter_parse_id_v30, pci_filter_parse_id@LIBPCI_3.0);
+SYMBOL_VERSION(pci_filter_parse_id_v33, pci_filter_parse_id@LIBPCI_3.3);
+SYMBOL_VERSION(pci_filter_parse_id_v38, pci_filter_parse_id@@LIBPCI_3.8);
+
+STATIC_ALIAS(int pci_filter_match(struct pci_filter *f, struct pci_dev *d), pci_filter_match_v38(f, d));
+DEFINE_ALIAS(int pci_filter_match_v33(struct pci_filter *f, struct pci_dev *d), pci_filter_match_v38);
+SYMBOL_VERSION(pci_filter_match_v30, pci_filter_match@LIBPCI_3.0);
+SYMBOL_VERSION(pci_filter_match_v33, pci_filter_match@LIBPCI_3.3);
+SYMBOL_VERSION(pci_filter_match_v38, pci_filter_match@@LIBPCI_3.8);
diff --git a/lib/generic.c b/lib/generic.c
new file mode 100644
index 0000000..f7340a2
--- /dev/null
+++ b/lib/generic.c
@@ -0,0 +1,259 @@
+/*
+ * The PCI Library -- Generic Direct Access Functions
+ *
+ * Copyright (c) 1997--2022 Martin Mares <mj@ucw.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <string.h>
+
+#include "internal.h"
+
+void
+pci_generic_scan_bus(struct pci_access *a, byte *busmap, int domain, int bus)
+{
+ int dev, multi, ht;
+ struct pci_dev *t;
+
+ a->debug("Scanning bus %02x for devices...\n", bus);
+ if (busmap[bus])
+ {
+ a->warning("Bus %02x seen twice (firmware bug). Ignored.", bus);
+ return;
+ }
+ busmap[bus] = 1;
+ t = pci_alloc_dev(a);
+ t->domain = domain;
+ t->bus = bus;
+ for (dev=0; dev<32; dev++)
+ {
+ t->dev = dev;
+ multi = 0;
+ for (t->func=0; !t->func || multi && t->func<8; t->func++)
+ {
+ u32 vd = pci_read_long(t, PCI_VENDOR_ID);
+ struct pci_dev *d;
+
+ if (!vd || vd == 0xffffffff)
+ continue;
+ ht = pci_read_byte(t, PCI_HEADER_TYPE);
+ if (!t->func)
+ multi = ht & 0x80;
+ ht &= 0x7f;
+ d = pci_alloc_dev(a);
+ d->domain = t->domain;
+ d->bus = t->bus;
+ d->dev = t->dev;
+ d->func = t->func;
+ d->vendor_id = vd & 0xffff;
+ d->device_id = vd >> 16U;
+ d->known_fields = PCI_FILL_IDENT;
+ d->hdrtype = ht;
+ pci_link_dev(a, d);
+ switch (ht)
+ {
+ case PCI_HEADER_TYPE_NORMAL:
+ break;
+ case PCI_HEADER_TYPE_BRIDGE:
+ case PCI_HEADER_TYPE_CARDBUS:
+ pci_generic_scan_bus(a, busmap, domain, pci_read_byte(t, PCI_SECONDARY_BUS));
+ break;
+ default:
+ a->debug("Device %04x:%02x:%02x.%d has unknown header type %02x.\n", d->domain, d->bus, d->dev, d->func, ht);
+ }
+ }
+ }
+ pci_free_dev(t);
+}
+
+void
+pci_generic_scan_domain(struct pci_access *a, int domain)
+{
+ byte busmap[256];
+
+ memset(busmap, 0, sizeof(busmap));
+ pci_generic_scan_bus(a, busmap, domain, 0);
+}
+
+void
+pci_generic_scan(struct pci_access *a)
+{
+ pci_generic_scan_domain(a, 0);
+}
+
+static int
+get_hdr_type(struct pci_dev *d)
+{
+ if (d->hdrtype < 0)
+ d->hdrtype = pci_read_byte(d, PCI_HEADER_TYPE) & 0x7f;
+ return d->hdrtype;
+}
+
+void
+pci_generic_fill_info(struct pci_dev *d, unsigned int flags)
+{
+ struct pci_access *a = d->access;
+ struct pci_cap *cap;
+
+ if (want_fill(d, flags, PCI_FILL_IDENT))
+ {
+ d->vendor_id = pci_read_word(d, PCI_VENDOR_ID);
+ d->device_id = pci_read_word(d, PCI_DEVICE_ID);
+ }
+
+ if (want_fill(d, flags, PCI_FILL_CLASS))
+ d->device_class = pci_read_word(d, PCI_CLASS_DEVICE);
+
+ if (want_fill(d, flags, PCI_FILL_CLASS_EXT))
+ {
+ d->prog_if = pci_read_byte(d, PCI_CLASS_PROG);
+ d->rev_id = pci_read_byte(d, PCI_REVISION_ID);
+ }
+
+ if (want_fill(d, flags, PCI_FILL_SUBSYS))
+ {
+ switch (get_hdr_type(d))
+ {
+ case PCI_HEADER_TYPE_NORMAL:
+ d->subsys_vendor_id = pci_read_word(d, PCI_SUBSYSTEM_VENDOR_ID);
+ d->subsys_id = pci_read_word(d, PCI_SUBSYSTEM_ID);
+ break;
+ case PCI_HEADER_TYPE_BRIDGE:
+ cap = pci_find_cap(d, PCI_CAP_ID_SSVID, PCI_CAP_NORMAL);
+ if (cap)
+ {
+ d->subsys_vendor_id = pci_read_word(d, cap->addr + PCI_SSVID_VENDOR);
+ d->subsys_id = pci_read_word(d, cap->addr + PCI_SSVID_DEVICE);
+ }
+ break;
+ case PCI_HEADER_TYPE_CARDBUS:
+ d->subsys_vendor_id = pci_read_word(d, PCI_CB_SUBSYSTEM_VENDOR_ID);
+ d->subsys_id = pci_read_word(d, PCI_CB_SUBSYSTEM_ID);
+ break;
+ default:
+ clear_fill(d, PCI_FILL_SUBSYS);
+ }
+ }
+
+ if (want_fill(d, flags, PCI_FILL_IRQ))
+ d->irq = pci_read_byte(d, PCI_INTERRUPT_LINE);
+
+ if (want_fill(d, flags, PCI_FILL_BASES))
+ {
+ int cnt = 0, i;
+ memset(d->base_addr, 0, sizeof(d->base_addr));
+ switch (get_hdr_type(d))
+ {
+ case PCI_HEADER_TYPE_NORMAL:
+ cnt = 6;
+ break;
+ case PCI_HEADER_TYPE_BRIDGE:
+ cnt = 2;
+ break;
+ case PCI_HEADER_TYPE_CARDBUS:
+ cnt = 1;
+ break;
+ }
+ if (cnt)
+ {
+ for (i=0; i<cnt; i++)
+ {
+ u32 x = pci_read_long(d, PCI_BASE_ADDRESS_0 + i*4);
+ if (!x || x == (u32) ~0)
+ continue;
+ if ((x & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO)
+ d->base_addr[i] = x;
+ else
+ {
+ if ((x & PCI_BASE_ADDRESS_MEM_TYPE_MASK) != PCI_BASE_ADDRESS_MEM_TYPE_64)
+ d->base_addr[i] = x;
+ else if (i >= cnt-1)
+ a->warning("%04x:%02x:%02x.%d: Invalid 64-bit address seen for BAR %d.", d->domain, d->bus, d->dev, d->func, i);
+ else
+ {
+ u32 y = pci_read_long(d, PCI_BASE_ADDRESS_0 + (++i)*4);
+#ifdef PCI_HAVE_64BIT_ADDRESS
+ d->base_addr[i-1] = x | (((pciaddr_t) y) << 32);
+#else
+ if (y)
+ a->warning("%04x:%02x:%02x.%d 64-bit device address ignored.", d->domain, d->bus, d->dev, d->func);
+ else
+ d->base_addr[i-1] = x;
+#endif
+ }
+ }
+ }
+ }
+ }
+
+ if (want_fill(d, flags, PCI_FILL_ROM_BASE))
+ {
+ int reg = 0;
+ d->rom_base_addr = 0;
+ switch (get_hdr_type(d))
+ {
+ case PCI_HEADER_TYPE_NORMAL:
+ reg = PCI_ROM_ADDRESS;
+ break;
+ case PCI_HEADER_TYPE_BRIDGE:
+ reg = PCI_ROM_ADDRESS1;
+ break;
+ }
+ if (reg)
+ {
+ u32 u = pci_read_long(d, reg);
+ if (u != 0xffffffff)
+ d->rom_base_addr = u;
+ }
+ }
+
+ pci_scan_caps(d, flags);
+}
+
+static int
+pci_generic_block_op(struct pci_dev *d, int pos, byte *buf, int len,
+ int (*r)(struct pci_dev *d, int pos, byte *buf, int len))
+{
+ if ((pos & 1) && len >= 1)
+ {
+ if (!r(d, pos, buf, 1))
+ return 0;
+ pos++; buf++; len--;
+ }
+ if ((pos & 3) && len >= 2)
+ {
+ if (!r(d, pos, buf, 2))
+ return 0;
+ pos += 2; buf += 2; len -= 2;
+ }
+ while (len >= 4)
+ {
+ if (!r(d, pos, buf, 4))
+ return 0;
+ pos += 4; buf += 4; len -= 4;
+ }
+ if (len >= 2)
+ {
+ if (!r(d, pos, buf, 2))
+ return 0;
+ pos += 2; buf += 2; len -= 2;
+ }
+ if (len && !r(d, pos, buf, 1))
+ return 0;
+ return 1;
+}
+
+int
+pci_generic_block_read(struct pci_dev *d, int pos, byte *buf, int len)
+{
+ return pci_generic_block_op(d, pos, buf, len, d->access->methods->read);
+}
+
+int
+pci_generic_block_write(struct pci_dev *d, int pos, byte *buf, int len)
+{
+ return pci_generic_block_op(d, pos, buf, len, d->access->methods->write);
+}
diff --git a/lib/header.h b/lib/header.h
new file mode 100644
index 0000000..2cee94f
--- /dev/null
+++ b/lib/header.h
@@ -0,0 +1,1579 @@
+/*
+ * The PCI Library -- PCI Header Structure (based on <linux/pci.h>)
+ *
+ * Copyright (c) 1997--2010 Martin Mares <mj@ucw.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/*
+ * Under PCI, each device has 256 bytes of configuration address space,
+ * of which the first 64 bytes are standardized as follows:
+ */
+#define PCI_VENDOR_ID 0x00 /* 16 bits */
+#define PCI_DEVICE_ID 0x02 /* 16 bits */
+#define PCI_COMMAND 0x04 /* 16 bits */
+#define PCI_COMMAND_IO 0x1 /* Enable response in I/O space */
+#define PCI_COMMAND_MEMORY 0x2 /* Enable response in Memory space */
+#define PCI_COMMAND_MASTER 0x4 /* Enable bus mastering */
+#define PCI_COMMAND_SPECIAL 0x8 /* Enable response to special cycles */
+#define PCI_COMMAND_INVALIDATE 0x10 /* Use memory write and invalidate */
+#define PCI_COMMAND_VGA_PALETTE 0x20 /* Enable palette snooping */
+#define PCI_COMMAND_PARITY 0x40 /* Enable parity checking */
+#define PCI_COMMAND_WAIT 0x80 /* Enable address/data stepping */
+#define PCI_COMMAND_SERR 0x100 /* Enable SERR */
+#define PCI_COMMAND_FAST_BACK 0x200 /* Enable back-to-back writes */
+#define PCI_COMMAND_DISABLE_INTx 0x400 /* PCIE: Disable INTx interrupts */
+
+#define PCI_STATUS 0x06 /* 16 bits */
+#define PCI_STATUS_INTx 0x08 /* PCIE: INTx interrupt pending */
+#define PCI_STATUS_CAP_LIST 0x10 /* Support Capability List */
+#define PCI_STATUS_66MHZ 0x20 /* Support 66 Mhz PCI 2.1 bus */
+#define PCI_STATUS_UDF 0x40 /* Support User Definable Features [obsolete] */
+#define PCI_STATUS_FAST_BACK 0x80 /* Accept fast-back to back */
+#define PCI_STATUS_PARITY 0x100 /* Detected parity error */
+#define PCI_STATUS_DEVSEL_MASK 0x600 /* DEVSEL timing */
+#define PCI_STATUS_DEVSEL_FAST 0x000
+#define PCI_STATUS_DEVSEL_MEDIUM 0x200
+#define PCI_STATUS_DEVSEL_SLOW 0x400
+#define PCI_STATUS_SIG_TARGET_ABORT 0x800 /* Set on target abort */
+#define PCI_STATUS_REC_TARGET_ABORT 0x1000 /* Master ack of " */
+#define PCI_STATUS_REC_MASTER_ABORT 0x2000 /* Set on master abort */
+#define PCI_STATUS_SIG_SYSTEM_ERROR 0x4000 /* Set when we drive SERR */
+#define PCI_STATUS_DETECTED_PARITY 0x8000 /* Set on parity error */
+
+#define PCI_CLASS_REVISION 0x08 /* High 24 bits are class, low 8
+ revision */
+#define PCI_REVISION_ID 0x08 /* Revision ID */
+#define PCI_CLASS_PROG 0x09 /* Reg. Level Programming Interface */
+#define PCI_CLASS_DEVICE 0x0a /* Device class */
+
+#define PCI_CACHE_LINE_SIZE 0x0c /* 8 bits */
+#define PCI_LATENCY_TIMER 0x0d /* 8 bits */
+#define PCI_HEADER_TYPE 0x0e /* 8 bits */
+#define PCI_HEADER_TYPE_NORMAL 0
+#define PCI_HEADER_TYPE_BRIDGE 1
+#define PCI_HEADER_TYPE_CARDBUS 2
+
+#define PCI_BIST 0x0f /* 8 bits */
+#define PCI_BIST_CODE_MASK 0x0f /* Return result */
+#define PCI_BIST_START 0x40 /* 1 to start BIST, 2 secs or less */
+#define PCI_BIST_CAPABLE 0x80 /* 1 if BIST capable */
+
+/*
+ * Base addresses specify locations in memory or I/O space.
+ * Decoded size can be determined by writing a value of
+ * 0xffffffff to the register, and reading it back. Only
+ * 1 bits are decoded.
+ */
+#define PCI_BASE_ADDRESS_0 0x10 /* 32 bits */
+#define PCI_BASE_ADDRESS_1 0x14 /* 32 bits [htype 0,1 only] */
+#define PCI_BASE_ADDRESS_2 0x18 /* 32 bits [htype 0 only] */
+#define PCI_BASE_ADDRESS_3 0x1c /* 32 bits */
+#define PCI_BASE_ADDRESS_4 0x20 /* 32 bits */
+#define PCI_BASE_ADDRESS_5 0x24 /* 32 bits */
+#define PCI_BASE_ADDRESS_SPACE 0x01 /* 0 = memory, 1 = I/O */
+#define PCI_BASE_ADDRESS_SPACE_IO 0x01
+#define PCI_BASE_ADDRESS_SPACE_MEMORY 0x00
+#define PCI_BASE_ADDRESS_MEM_TYPE_MASK 0x06
+#define PCI_BASE_ADDRESS_MEM_TYPE_32 0x00 /* 32 bit address */
+#define PCI_BASE_ADDRESS_MEM_TYPE_1M 0x02 /* Below 1M [obsolete] */
+#define PCI_BASE_ADDRESS_MEM_TYPE_64 0x04 /* 64 bit address */
+#define PCI_BASE_ADDRESS_MEM_PREFETCH 0x08 /* prefetchable? */
+#define PCI_BASE_ADDRESS_MEM_MASK (~(pciaddr_t)0x0f)
+#define PCI_BASE_ADDRESS_IO_MASK (~(pciaddr_t)0x03)
+/* bit 1 is reserved if address_space = 1 */
+
+/* Header type 0 (normal devices) */
+#define PCI_CARDBUS_CIS 0x28
+#define PCI_SUBSYSTEM_VENDOR_ID 0x2c
+#define PCI_SUBSYSTEM_ID 0x2e
+#define PCI_ROM_ADDRESS 0x30 /* Bits 31..11 are address, 10..1 reserved */
+#define PCI_ROM_ADDRESS_ENABLE 0x01
+#define PCI_ROM_ADDRESS_MASK (~(pciaddr_t)0x7ff)
+
+#define PCI_CAPABILITY_LIST 0x34 /* Offset of first capability list entry */
+
+/* 0x35-0x3b are reserved */
+#define PCI_INTERRUPT_LINE 0x3c /* 8 bits */
+#define PCI_INTERRUPT_PIN 0x3d /* 8 bits */
+#define PCI_MIN_GNT 0x3e /* 8 bits */
+#define PCI_MAX_LAT 0x3f /* 8 bits */
+
+/* Header type 1 (PCI-to-PCI bridges) */
+#define PCI_PRIMARY_BUS 0x18 /* Primary bus number */
+#define PCI_SECONDARY_BUS 0x19 /* Secondary bus number */
+#define PCI_SUBORDINATE_BUS 0x1a /* Highest bus number behind the bridge */
+#define PCI_SEC_LATENCY_TIMER 0x1b /* Latency timer for secondary interface */
+#define PCI_IO_BASE 0x1c /* I/O range behind the bridge */
+#define PCI_IO_LIMIT 0x1d
+#define PCI_IO_RANGE_TYPE_MASK 0x0f /* I/O bridging type */
+#define PCI_IO_RANGE_TYPE_16 0x00
+#define PCI_IO_RANGE_TYPE_32 0x01
+#define PCI_IO_RANGE_MASK ~0x0f
+#define PCI_SEC_STATUS 0x1e /* Secondary status register */
+#define PCI_MEMORY_BASE 0x20 /* Memory range behind */
+#define PCI_MEMORY_LIMIT 0x22
+#define PCI_MEMORY_RANGE_TYPE_MASK 0x0f
+#define PCI_MEMORY_RANGE_MASK ~0x0f
+#define PCI_PREF_MEMORY_BASE 0x24 /* Prefetchable memory range behind */
+#define PCI_PREF_MEMORY_LIMIT 0x26
+#define PCI_PREF_RANGE_TYPE_MASK 0x0f
+#define PCI_PREF_RANGE_TYPE_32 0x00
+#define PCI_PREF_RANGE_TYPE_64 0x01
+#define PCI_PREF_RANGE_MASK ~0x0f
+#define PCI_PREF_BASE_UPPER32 0x28 /* Upper half of prefetchable memory range */
+#define PCI_PREF_LIMIT_UPPER32 0x2c
+#define PCI_IO_BASE_UPPER16 0x30 /* Upper half of I/O addresses */
+#define PCI_IO_LIMIT_UPPER16 0x32
+/* 0x34 same as for htype 0 */
+/* 0x35-0x3b is reserved */
+#define PCI_ROM_ADDRESS1 0x38 /* Same as PCI_ROM_ADDRESS, but for htype 1 */
+/* 0x3c-0x3d are same as for htype 0 */
+#define PCI_BRIDGE_CONTROL 0x3e
+#define PCI_BRIDGE_CTL_PARITY 0x01 /* Enable parity detection on secondary interface */
+#define PCI_BRIDGE_CTL_SERR 0x02 /* The same for SERR forwarding */
+#define PCI_BRIDGE_CTL_NO_ISA 0x04 /* Disable bridging of ISA ports */
+#define PCI_BRIDGE_CTL_VGA 0x08 /* Forward VGA addresses */
+#define PCI_BRIDGE_CTL_VGA_16BIT 0x10 /* VGA 16-bit decode */
+#define PCI_BRIDGE_CTL_MASTER_ABORT 0x20 /* Report master aborts */
+#define PCI_BRIDGE_CTL_BUS_RESET 0x40 /* Secondary bus reset */
+#define PCI_BRIDGE_CTL_FAST_BACK 0x80 /* Fast Back2Back enabled on secondary interface */
+#define PCI_BRIDGE_CTL_PRI_DISCARD_TIMER 0x100 /* PCI-X? */
+#define PCI_BRIDGE_CTL_SEC_DISCARD_TIMER 0x200 /* PCI-X? */
+#define PCI_BRIDGE_CTL_DISCARD_TIMER_STATUS 0x400 /* PCI-X? */
+#define PCI_BRIDGE_CTL_DISCARD_TIMER_SERR_EN 0x800 /* PCI-X? */
+
+/* Header type 2 (CardBus bridges) */
+#define PCI_CB_CAPABILITY_LIST 0x14
+/* 0x15 reserved */
+#define PCI_CB_SEC_STATUS 0x16 /* Secondary status */
+#define PCI_CB_PRIMARY_BUS 0x18 /* PCI bus number */
+#define PCI_CB_CARD_BUS 0x19 /* CardBus bus number */
+#define PCI_CB_SUBORDINATE_BUS 0x1a /* Subordinate bus number */
+#define PCI_CB_LATENCY_TIMER 0x1b /* CardBus latency timer */
+#define PCI_CB_MEMORY_BASE_0 0x1c
+#define PCI_CB_MEMORY_LIMIT_0 0x20
+#define PCI_CB_MEMORY_BASE_1 0x24
+#define PCI_CB_MEMORY_LIMIT_1 0x28
+#define PCI_CB_IO_BASE_0 0x2c
+#define PCI_CB_IO_BASE_0_HI 0x2e
+#define PCI_CB_IO_LIMIT_0 0x30
+#define PCI_CB_IO_LIMIT_0_HI 0x32
+#define PCI_CB_IO_BASE_1 0x34
+#define PCI_CB_IO_BASE_1_HI 0x36
+#define PCI_CB_IO_LIMIT_1 0x38
+#define PCI_CB_IO_LIMIT_1_HI 0x3a
+#define PCI_CB_IO_RANGE_MASK ~0x03
+/* 0x3c-0x3d are same as for htype 0 */
+#define PCI_CB_BRIDGE_CONTROL 0x3e
+#define PCI_CB_BRIDGE_CTL_PARITY 0x01 /* Similar to standard bridge control register */
+#define PCI_CB_BRIDGE_CTL_SERR 0x02
+#define PCI_CB_BRIDGE_CTL_ISA 0x04
+#define PCI_CB_BRIDGE_CTL_VGA 0x08
+#define PCI_CB_BRIDGE_CTL_MASTER_ABORT 0x20
+#define PCI_CB_BRIDGE_CTL_CB_RESET 0x40 /* CardBus reset */
+#define PCI_CB_BRIDGE_CTL_16BIT_INT 0x80 /* Enable interrupt for 16-bit cards */
+#define PCI_CB_BRIDGE_CTL_PREFETCH_MEM0 0x100 /* Prefetch enable for both memory regions */
+#define PCI_CB_BRIDGE_CTL_PREFETCH_MEM1 0x200
+#define PCI_CB_BRIDGE_CTL_POST_WRITES 0x400
+#define PCI_CB_SUBSYSTEM_VENDOR_ID 0x40
+#define PCI_CB_SUBSYSTEM_ID 0x42
+#define PCI_CB_LEGACY_MODE_BASE 0x44 /* 16-bit PC Card legacy mode base address (ExCa) */
+/* 0x48-0x7f reserved */
+
+/* Capability lists */
+
+#define PCI_CAP_LIST_ID 0 /* Capability ID */
+#define PCI_CAP_ID_NULL 0x00 /* Null Capability */
+#define PCI_CAP_ID_PM 0x01 /* Power Management */
+#define PCI_CAP_ID_AGP 0x02 /* Accelerated Graphics Port */
+#define PCI_CAP_ID_VPD 0x03 /* Vital Product Data */
+#define PCI_CAP_ID_SLOTID 0x04 /* Slot Identification */
+#define PCI_CAP_ID_MSI 0x05 /* Message Signaled Interrupts */
+#define PCI_CAP_ID_CHSWP 0x06 /* CompactPCI HotSwap */
+#define PCI_CAP_ID_PCIX 0x07 /* PCI-X */
+#define PCI_CAP_ID_HT 0x08 /* HyperTransport */
+#define PCI_CAP_ID_VNDR 0x09 /* Vendor specific */
+#define PCI_CAP_ID_DBG 0x0A /* Debug port */
+#define PCI_CAP_ID_CCRC 0x0B /* CompactPCI Central Resource Control */
+#define PCI_CAP_ID_HOTPLUG 0x0C /* PCI hot-plug */
+#define PCI_CAP_ID_SSVID 0x0D /* Bridge subsystem vendor/device ID */
+#define PCI_CAP_ID_AGP3 0x0E /* AGP 8x */
+#define PCI_CAP_ID_SECURE 0x0F /* Secure device (?) */
+#define PCI_CAP_ID_EXP 0x10 /* PCI Express */
+#define PCI_CAP_ID_MSIX 0x11 /* MSI-X */
+#define PCI_CAP_ID_SATA 0x12 /* Serial-ATA HBA */
+#define PCI_CAP_ID_AF 0x13 /* Advanced features of PCI devices integrated in PCIe root cplx */
+#define PCI_CAP_ID_EA 0x14 /* Enhanced Allocation */
+#define PCI_CAP_LIST_NEXT 1 /* Next capability in the list */
+#define PCI_CAP_FLAGS 2 /* Capability defined flags (16 bits) */
+#define PCI_CAP_SIZEOF 4
+
+/* Capabilities residing in the PCI Express extended configuration space */
+
+#define PCI_EXT_CAP_ID_NULL 0x00 /* Null Capability */
+#define PCI_EXT_CAP_ID_AER 0x01 /* Advanced Error Reporting */
+#define PCI_EXT_CAP_ID_VC 0x02 /* Virtual Channel */
+#define PCI_EXT_CAP_ID_DSN 0x03 /* Device Serial Number */
+#define PCI_EXT_CAP_ID_PB 0x04 /* Power Budgeting */
+#define PCI_EXT_CAP_ID_RCLINK 0x05 /* Root Complex Link Declaration */
+#define PCI_EXT_CAP_ID_RCILINK 0x06 /* Root Complex Internal Link Declaration */
+#define PCI_EXT_CAP_ID_RCEC 0x07 /* Root Complex Event Collector */
+#define PCI_EXT_CAP_ID_MFVC 0x08 /* Multi-Function Virtual Channel */
+#define PCI_EXT_CAP_ID_VC2 0x09 /* Virtual Channel (2nd ID) */
+#define PCI_EXT_CAP_ID_RCRB 0x0a /* Root Complex Register Block */
+#define PCI_EXT_CAP_ID_VNDR 0x0b /* Vendor specific */
+#define PCI_EXT_CAP_ID_ACS 0x0d /* Access Controls */
+#define PCI_EXT_CAP_ID_ARI 0x0e /* Alternative Routing-ID Interpretation */
+#define PCI_EXT_CAP_ID_ATS 0x0f /* Address Translation Service */
+#define PCI_EXT_CAP_ID_SRIOV 0x10 /* Single Root I/O Virtualization */
+#define PCI_EXT_CAP_ID_MRIOV 0x11 /* Multi-Root I/O Virtualization */
+#define PCI_EXT_CAP_ID_MCAST 0x12 /* Multicast */
+#define PCI_EXT_CAP_ID_PRI 0x13 /* Page Request Interface */
+#define PCI_EXT_CAP_ID_REBAR 0x15 /* Resizable BAR */
+#define PCI_EXT_CAP_ID_DPA 0x16 /* Dynamic Power Allocation */
+#define PCI_EXT_CAP_ID_TPH 0x17 /* Transaction processing hints */
+#define PCI_EXT_CAP_ID_LTR 0x18 /* Latency Tolerance Reporting */
+#define PCI_EXT_CAP_ID_SECPCI 0x19 /* Secondary PCI Express */
+#define PCI_EXT_CAP_ID_PMUX 0x1a /* Protocol Multiplexing */
+#define PCI_EXT_CAP_ID_PASID 0x1b /* Process Address Space ID */
+#define PCI_EXT_CAP_ID_LNR 0x1c /* LN Requester */
+#define PCI_EXT_CAP_ID_DPC 0x1d /* Downstream Port Containment */
+#define PCI_EXT_CAP_ID_L1PM 0x1e /* L1 PM Substates */
+#define PCI_EXT_CAP_ID_PTM 0x1f /* Precision Time Measurement */
+#define PCI_EXT_CAP_ID_M_PCIE 0x20 /* PCIe over M-PHY */
+#define PCI_EXT_CAP_ID_FRS 0x21 /* FRS Queuing */
+#define PCI_EXT_CAP_ID_RTR 0x22 /* Readiness Time Reporting */
+#define PCI_EXT_CAP_ID_DVSEC 0x23 /* Designated Vendor-Specific */
+#define PCI_EXT_CAP_ID_VF_REBAR 0x24 /* VF Resizable BAR */
+#define PCI_EXT_CAP_ID_DLNK 0x25 /* Data Link Feature */
+#define PCI_EXT_CAP_ID_16GT 0x26 /* Physical Layer 16.0 GT/s */
+#define PCI_EXT_CAP_ID_LMR 0x27 /* Lane Margining at Receiver */
+#define PCI_EXT_CAP_ID_HIER_ID 0x28 /* Hierarchy ID */
+#define PCI_EXT_CAP_ID_NPEM 0x29 /* Native PCIe Enclosure Management */
+#define PCI_EXT_CAP_ID_32GT 0x2a /* Physical Layer 32.0 GT/s */
+#define PCI_EXT_CAP_ID_DOE 0x2e /* Data Object Exchange */
+
+/*** Definitions of capabilities ***/
+
+/* Power Management Registers */
+
+#define PCI_PM_CAP_VER_MASK 0x0007 /* Version (2=PM1.1) */
+#define PCI_PM_CAP_PME_CLOCK 0x0008 /* Clock required for PME generation */
+#define PCI_PM_CAP_DSI 0x0020 /* Device specific initialization required */
+#define PCI_PM_CAP_AUX_C_MASK 0x01c0 /* Maximum aux current required in D3cold */
+#define PCI_PM_CAP_D1 0x0200 /* D1 power state support */
+#define PCI_PM_CAP_D2 0x0400 /* D2 power state support */
+#define PCI_PM_CAP_PME_D0 0x0800 /* PME can be asserted from D0 */
+#define PCI_PM_CAP_PME_D1 0x1000 /* PME can be asserted from D1 */
+#define PCI_PM_CAP_PME_D2 0x2000 /* PME can be asserted from D2 */
+#define PCI_PM_CAP_PME_D3_HOT 0x4000 /* PME can be asserted from D3hot */
+#define PCI_PM_CAP_PME_D3_COLD 0x8000 /* PME can be asserted from D3cold */
+#define PCI_PM_CTRL 4 /* PM control and status register */
+#define PCI_PM_CTRL_STATE_MASK 0x0003 /* Current power state (D0 to D3) */
+#define PCI_PM_CTRL_NO_SOFT_RST 0x0008 /* No Soft Reset from D3hot to D0 */
+#define PCI_PM_CTRL_PME_ENABLE 0x0100 /* PME pin enable */
+#define PCI_PM_CTRL_DATA_SEL_MASK 0x1e00 /* PM table data index */
+#define PCI_PM_CTRL_DATA_SCALE_MASK 0x6000 /* PM table data scaling factor */
+#define PCI_PM_CTRL_PME_STATUS 0x8000 /* PME pin status */
+#define PCI_PM_PPB_EXTENSIONS 6 /* PPB support extensions */
+#define PCI_PM_PPB_B2_B3 0x40 /* If bridge enters D3hot, bus enters: 0=B3, 1=B2 */
+#define PCI_PM_BPCC_ENABLE 0x80 /* Secondary bus is power managed */
+#define PCI_PM_DATA_REGISTER 7 /* PM table contents read here */
+#define PCI_PM_SIZEOF 8
+
+/* AGP registers */
+
+#define PCI_AGP_VERSION 2 /* BCD version number */
+#define PCI_AGP_RFU 3 /* Rest of capability flags */
+#define PCI_AGP_STATUS 4 /* Status register */
+#define PCI_AGP_STATUS_RQ_MASK 0xff000000 /* Maximum number of requests - 1 */
+#define PCI_AGP_STATUS_ISOCH 0x10000 /* Isochronous transactions supported */
+#define PCI_AGP_STATUS_ARQSZ_MASK 0xe000 /* log2(optimum async req size in bytes) - 4 */
+#define PCI_AGP_STATUS_CAL_MASK 0x1c00 /* Calibration cycle timing */
+#define PCI_AGP_STATUS_SBA 0x0200 /* Sideband addressing supported */
+#define PCI_AGP_STATUS_ITA_COH 0x0100 /* In-aperture accesses always coherent */
+#define PCI_AGP_STATUS_GART64 0x0080 /* 64-bit GART entries supported */
+#define PCI_AGP_STATUS_HTRANS 0x0040 /* If 0, core logic can xlate host CPU accesses thru aperture */
+#define PCI_AGP_STATUS_64BIT 0x0020 /* 64-bit addressing cycles supported */
+#define PCI_AGP_STATUS_FW 0x0010 /* Fast write transfers supported */
+#define PCI_AGP_STATUS_AGP3 0x0008 /* AGP3 mode supported */
+#define PCI_AGP_STATUS_RATE4 0x0004 /* 4x transfer rate supported (RFU in AGP3 mode) */
+#define PCI_AGP_STATUS_RATE2 0x0002 /* 2x transfer rate supported (8x in AGP3 mode) */
+#define PCI_AGP_STATUS_RATE1 0x0001 /* 1x transfer rate supported (4x in AGP3 mode) */
+#define PCI_AGP_COMMAND 8 /* Control register */
+#define PCI_AGP_COMMAND_RQ_MASK 0xff000000 /* Master: Maximum number of requests */
+#define PCI_AGP_COMMAND_ARQSZ_MASK 0xe000 /* log2(optimum async req size in bytes) - 4 */
+#define PCI_AGP_COMMAND_CAL_MASK 0x1c00 /* Calibration cycle timing */
+#define PCI_AGP_COMMAND_SBA 0x0200 /* Sideband addressing enabled */
+#define PCI_AGP_COMMAND_AGP 0x0100 /* Allow processing of AGP transactions */
+#define PCI_AGP_COMMAND_GART64 0x0080 /* 64-bit GART entries enabled */
+#define PCI_AGP_COMMAND_64BIT 0x0020 /* Allow generation of 64-bit addr cycles */
+#define PCI_AGP_COMMAND_FW 0x0010 /* Enable FW transfers */
+#define PCI_AGP_COMMAND_RATE4 0x0004 /* Use 4x rate (RFU in AGP3 mode) */
+#define PCI_AGP_COMMAND_RATE2 0x0002 /* Use 2x rate (8x in AGP3 mode) */
+#define PCI_AGP_COMMAND_RATE1 0x0001 /* Use 1x rate (4x in AGP3 mode) */
+#define PCI_AGP_SIZEOF 12
+
+/* Vital Product Data */
+
+#define PCI_VPD_ADDR 2 /* Address to access (15 bits!) */
+#define PCI_VPD_ADDR_MASK 0x7fff /* Address mask */
+#define PCI_VPD_ADDR_F 0x8000 /* Write 0, 1 indicates completion */
+#define PCI_VPD_DATA 4 /* 32-bits of data returned here */
+
+/* Slot Identification */
+
+#define PCI_SID_ESR 2 /* Expansion Slot Register */
+#define PCI_SID_ESR_NSLOTS 0x1f /* Number of expansion slots available */
+#define PCI_SID_ESR_FIC 0x20 /* First In Chassis Flag */
+#define PCI_SID_CHASSIS_NR 3 /* Chassis Number */
+
+/* Message Signaled Interrupts registers */
+
+#define PCI_MSI_FLAGS 2 /* Various flags */
+#define PCI_MSI_FLAGS_MASK_BIT 0x100 /* interrupt masking & reporting supported */
+#define PCI_MSI_FLAGS_64BIT 0x080 /* 64-bit addresses allowed */
+#define PCI_MSI_FLAGS_QSIZE 0x070 /* Message queue size configured */
+#define PCI_MSI_FLAGS_QMASK 0x00e /* Maximum queue size available */
+#define PCI_MSI_FLAGS_ENABLE 0x001 /* MSI feature enabled */
+#define PCI_MSI_RFU 3 /* Rest of capability flags */
+#define PCI_MSI_ADDRESS_LO 4 /* Lower 32 bits */
+#define PCI_MSI_ADDRESS_HI 8 /* Upper 32 bits (if PCI_MSI_FLAGS_64BIT set) */
+#define PCI_MSI_DATA_32 8 /* 16 bits of data for 32-bit devices */
+#define PCI_MSI_DATA_64 12 /* 16 bits of data for 64-bit devices */
+#define PCI_MSI_MASK_BIT_32 12 /* per-vector masking for 32-bit devices */
+#define PCI_MSI_MASK_BIT_64 16 /* per-vector masking for 64-bit devices */
+#define PCI_MSI_PENDING_32 16 /* per-vector interrupt pending for 32-bit devices */
+#define PCI_MSI_PENDING_64 20 /* per-vector interrupt pending for 64-bit devices */
+
+/* PCI-X */
+#define PCI_PCIX_COMMAND 2 /* Command register offset */
+#define PCI_PCIX_COMMAND_DPERE 0x0001 /* Data Parity Error Recover Enable */
+#define PCI_PCIX_COMMAND_ERO 0x0002 /* Enable Relaxed Ordering */
+#define PCI_PCIX_COMMAND_MAX_MEM_READ_BYTE_COUNT 0x000c /* Maximum Memory Read Byte Count */
+#define PCI_PCIX_COMMAND_MAX_OUTSTANDING_SPLIT_TRANS 0x0070
+#define PCI_PCIX_COMMAND_RESERVED 0xf80
+#define PCI_PCIX_STATUS 4 /* Status register offset */
+#define PCI_PCIX_STATUS_FUNCTION 0x00000007
+#define PCI_PCIX_STATUS_DEVICE 0x000000f8
+#define PCI_PCIX_STATUS_BUS 0x0000ff00
+#define PCI_PCIX_STATUS_64BIT 0x00010000
+#define PCI_PCIX_STATUS_133MHZ 0x00020000
+#define PCI_PCIX_STATUS_SC_DISCARDED 0x00040000 /* Split Completion Discarded */
+#define PCI_PCIX_STATUS_UNEXPECTED_SC 0x00080000 /* Unexpected Split Completion */
+#define PCI_PCIX_STATUS_DEVICE_COMPLEXITY 0x00100000 /* 0 = simple device, 1 = bridge device */
+#define PCI_PCIX_STATUS_DESIGNED_MAX_MEM_READ_BYTE_COUNT 0x00600000 /* 0 = 512 bytes, 1 = 1024, 2 = 2048, 3 = 4096 */
+#define PCI_PCIX_STATUS_DESIGNED_MAX_OUTSTANDING_SPLIT_TRANS 0x03800000
+#define PCI_PCIX_STATUS_DESIGNED_MAX_CUMULATIVE_READ_SIZE 0x1c000000
+#define PCI_PCIX_STATUS_RCVD_SC_ERR_MESS 0x20000000 /* Received Split Completion Error Message */
+#define PCI_PCIX_STATUS_266MHZ 0x40000000 /* 266 MHz capable */
+#define PCI_PCIX_STATUS_533MHZ 0x80000000 /* 533 MHz capable */
+#define PCI_PCIX_SIZEOF 4
+
+/* PCI-X Bridges */
+#define PCI_PCIX_BRIDGE_SEC_STATUS 2 /* Secondary bus status register offset */
+#define PCI_PCIX_BRIDGE_SEC_STATUS_64BIT 0x0001
+#define PCI_PCIX_BRIDGE_SEC_STATUS_133MHZ 0x0002
+#define PCI_PCIX_BRIDGE_SEC_STATUS_SC_DISCARDED 0x0004 /* Split Completion Discarded on secondary bus */
+#define PCI_PCIX_BRIDGE_SEC_STATUS_UNEXPECTED_SC 0x0008 /* Unexpected Split Completion on secondary bus */
+#define PCI_PCIX_BRIDGE_SEC_STATUS_SC_OVERRUN 0x0010 /* Split Completion Overrun on secondary bus */
+#define PCI_PCIX_BRIDGE_SEC_STATUS_SPLIT_REQUEST_DELAYED 0x0020
+#define PCI_PCIX_BRIDGE_SEC_STATUS_CLOCK_FREQ 0x01c0
+#define PCI_PCIX_BRIDGE_SEC_STATUS_RESERVED 0xfe00
+#define PCI_PCIX_BRIDGE_STATUS 4 /* Primary bus status register offset */
+#define PCI_PCIX_BRIDGE_STATUS_FUNCTION 0x00000007
+#define PCI_PCIX_BRIDGE_STATUS_DEVICE 0x000000f8
+#define PCI_PCIX_BRIDGE_STATUS_BUS 0x0000ff00
+#define PCI_PCIX_BRIDGE_STATUS_64BIT 0x00010000
+#define PCI_PCIX_BRIDGE_STATUS_133MHZ 0x00020000
+#define PCI_PCIX_BRIDGE_STATUS_SC_DISCARDED 0x00040000 /* Split Completion Discarded */
+#define PCI_PCIX_BRIDGE_STATUS_UNEXPECTED_SC 0x00080000 /* Unexpected Split Completion */
+#define PCI_PCIX_BRIDGE_STATUS_SC_OVERRUN 0x00100000 /* Split Completion Overrun */
+#define PCI_PCIX_BRIDGE_STATUS_SPLIT_REQUEST_DELAYED 0x00200000
+#define PCI_PCIX_BRIDGE_STATUS_RESERVED 0xffc00000
+#define PCI_PCIX_BRIDGE_UPSTREAM_SPLIT_TRANS_CTRL 8 /* Upstream Split Transaction Register offset */
+#define PCI_PCIX_BRIDGE_DOWNSTREAM_SPLIT_TRANS_CTRL 12 /* Downstream Split Transaction Register offset */
+#define PCI_PCIX_BRIDGE_STR_CAPACITY 0x0000ffff
+#define PCI_PCIX_BRIDGE_STR_COMMITMENT_LIMIT 0xffff0000
+#define PCI_PCIX_BRIDGE_SIZEOF 12
+
+/* HyperTransport (as of spec rev. 2.00) */
+#define PCI_HT_CMD 2 /* Command Register */
+#define PCI_HT_CMD_TYP_HI 0xe000 /* Capability Type high part */
+#define PCI_HT_CMD_TYP_HI_PRI 0x0000 /* Slave or Primary Interface */
+#define PCI_HT_CMD_TYP_HI_SEC 0x2000 /* Host or Secondary Interface */
+#define PCI_HT_CMD_TYP 0xf800 /* Capability Type */
+#define PCI_HT_CMD_TYP_SW 0x4000 /* Switch */
+#define PCI_HT_CMD_TYP_IDC 0x8000 /* Interrupt Discovery and Configuration */
+#define PCI_HT_CMD_TYP_RID 0x8800 /* Revision ID */
+#define PCI_HT_CMD_TYP_UIDC 0x9000 /* UnitID Clumping */
+#define PCI_HT_CMD_TYP_ECSA 0x9800 /* Extended Configuration Space Access */
+#define PCI_HT_CMD_TYP_AM 0xa000 /* Address Mapping */
+#define PCI_HT_CMD_TYP_MSIM 0xa800 /* MSI Mapping */
+#define PCI_HT_CMD_TYP_DR 0xb000 /* DirectRoute */
+#define PCI_HT_CMD_TYP_VCS 0xb800 /* VCSet */
+#define PCI_HT_CMD_TYP_RM 0xc000 /* Retry Mode */
+#define PCI_HT_CMD_TYP_X86 0xc800 /* X86 (reserved) */
+
+ /* Link Control Register */
+#define PCI_HT_LCTR_CFLE 0x0002 /* CRC Flood Enable */
+#define PCI_HT_LCTR_CST 0x0004 /* CRC Start Test */
+#define PCI_HT_LCTR_CFE 0x0008 /* CRC Force Error */
+#define PCI_HT_LCTR_LKFAIL 0x0010 /* Link Failure */
+#define PCI_HT_LCTR_INIT 0x0020 /* Initialization Complete */
+#define PCI_HT_LCTR_EOC 0x0040 /* End of Chain */
+#define PCI_HT_LCTR_TXO 0x0080 /* Transmitter Off */
+#define PCI_HT_LCTR_CRCERR 0x0f00 /* CRC Error */
+#define PCI_HT_LCTR_ISOCEN 0x1000 /* Isochronous Flow Control Enable */
+#define PCI_HT_LCTR_LSEN 0x2000 /* LDTSTOP# Tristate Enable */
+#define PCI_HT_LCTR_EXTCTL 0x4000 /* Extended CTL Time */
+#define PCI_HT_LCTR_64B 0x8000 /* 64-bit Addressing Enable */
+
+ /* Link Configuration Register */
+#define PCI_HT_LCNF_MLWI 0x0007 /* Max Link Width In */
+#define PCI_HT_LCNF_LW_8B 0x0 /* Link Width 8 bits */
+#define PCI_HT_LCNF_LW_16B 0x1 /* Link Width 16 bits */
+#define PCI_HT_LCNF_LW_32B 0x3 /* Link Width 32 bits */
+#define PCI_HT_LCNF_LW_2B 0x4 /* Link Width 2 bits */
+#define PCI_HT_LCNF_LW_4B 0x5 /* Link Width 4 bits */
+#define PCI_HT_LCNF_LW_NC 0x7 /* Link physically not connected */
+#define PCI_HT_LCNF_DFI 0x0008 /* Doubleword Flow Control In */
+#define PCI_HT_LCNF_MLWO 0x0070 /* Max Link Width Out */
+#define PCI_HT_LCNF_DFO 0x0080 /* Doubleword Flow Control Out */
+#define PCI_HT_LCNF_LWI 0x0700 /* Link Width In */
+#define PCI_HT_LCNF_DFIE 0x0800 /* Doubleword Flow Control In Enable */
+#define PCI_HT_LCNF_LWO 0x7000 /* Link Width Out */
+#define PCI_HT_LCNF_DFOE 0x8000 /* Doubleword Flow Control Out Enable */
+
+ /* Revision ID Register */
+#define PCI_HT_RID_MIN 0x1f /* Minor Revision */
+#define PCI_HT_RID_MAJ 0xe0 /* Major Revision */
+
+ /* Link Frequency/Error Register */
+#define PCI_HT_LFRER_FREQ 0x0f /* Transmitter Clock Frequency */
+#define PCI_HT_LFRER_200 0x00 /* 200MHz */
+#define PCI_HT_LFRER_300 0x01 /* 300MHz */
+#define PCI_HT_LFRER_400 0x02 /* 400MHz */
+#define PCI_HT_LFRER_500 0x03 /* 500MHz */
+#define PCI_HT_LFRER_600 0x04 /* 600MHz */
+#define PCI_HT_LFRER_800 0x05 /* 800MHz */
+#define PCI_HT_LFRER_1000 0x06 /* 1.0GHz */
+#define PCI_HT_LFRER_1200 0x07 /* 1.2GHz */
+#define PCI_HT_LFRER_1400 0x08 /* 1.4GHz */
+#define PCI_HT_LFRER_1600 0x09 /* 1.6GHz */
+#define PCI_HT_LFRER_VEND 0x0f /* Vendor-Specific */
+#define PCI_HT_LFRER_ERR 0xf0 /* Link Error */
+#define PCI_HT_LFRER_PROT 0x10 /* Protocol Error */
+#define PCI_HT_LFRER_OV 0x20 /* Overflow Error */
+#define PCI_HT_LFRER_EOC 0x40 /* End of Chain Error */
+#define PCI_HT_LFRER_CTLT 0x80 /* CTL Timeout */
+
+ /* Link Frequency Capability Register */
+#define PCI_HT_LFCAP_200 0x0001 /* 200MHz */
+#define PCI_HT_LFCAP_300 0x0002 /* 300MHz */
+#define PCI_HT_LFCAP_400 0x0004 /* 400MHz */
+#define PCI_HT_LFCAP_500 0x0008 /* 500MHz */
+#define PCI_HT_LFCAP_600 0x0010 /* 600MHz */
+#define PCI_HT_LFCAP_800 0x0020 /* 800MHz */
+#define PCI_HT_LFCAP_1000 0x0040 /* 1.0GHz */
+#define PCI_HT_LFCAP_1200 0x0080 /* 1.2GHz */
+#define PCI_HT_LFCAP_1400 0x0100 /* 1.4GHz */
+#define PCI_HT_LFCAP_1600 0x0200 /* 1.6GHz */
+#define PCI_HT_LFCAP_VEND 0x8000 /* Vendor-Specific */
+
+ /* Feature Register */
+#define PCI_HT_FTR_ISOCFC 0x0001 /* Isochronous Flow Control Mode */
+#define PCI_HT_FTR_LDTSTOP 0x0002 /* LDTSTOP# Supported */
+#define PCI_HT_FTR_CRCTM 0x0004 /* CRC Test Mode */
+#define PCI_HT_FTR_ECTLT 0x0008 /* Extended CTL Time Required */
+#define PCI_HT_FTR_64BA 0x0010 /* 64-bit Addressing */
+#define PCI_HT_FTR_UIDRD 0x0020 /* UnitID Reorder Disable */
+
+ /* Error Handling Register */
+#define PCI_HT_EH_PFLE 0x0001 /* Protocol Error Flood Enable */
+#define PCI_HT_EH_OFLE 0x0002 /* Overflow Error Flood Enable */
+#define PCI_HT_EH_PFE 0x0004 /* Protocol Error Fatal Enable */
+#define PCI_HT_EH_OFE 0x0008 /* Overflow Error Fatal Enable */
+#define PCI_HT_EH_EOCFE 0x0010 /* End of Chain Error Fatal Enable */
+#define PCI_HT_EH_RFE 0x0020 /* Response Error Fatal Enable */
+#define PCI_HT_EH_CRCFE 0x0040 /* CRC Error Fatal Enable */
+#define PCI_HT_EH_SERRFE 0x0080 /* System Error Fatal Enable (B */
+#define PCI_HT_EH_CF 0x0100 /* Chain Fail */
+#define PCI_HT_EH_RE 0x0200 /* Response Error */
+#define PCI_HT_EH_PNFE 0x0400 /* Protocol Error Nonfatal Enable */
+#define PCI_HT_EH_ONFE 0x0800 /* Overflow Error Nonfatal Enable */
+#define PCI_HT_EH_EOCNFE 0x1000 /* End of Chain Error Nonfatal Enable */
+#define PCI_HT_EH_RNFE 0x2000 /* Response Error Nonfatal Enable */
+#define PCI_HT_EH_CRCNFE 0x4000 /* CRC Error Nonfatal Enable */
+#define PCI_HT_EH_SERRNFE 0x8000 /* System Error Nonfatal Enable */
+
+/* HyperTransport: Slave or Primary Interface */
+#define PCI_HT_PRI_CMD 2 /* Command Register */
+#define PCI_HT_PRI_CMD_BUID 0x001f /* Base UnitID */
+#define PCI_HT_PRI_CMD_UC 0x03e0 /* Unit Count */
+#define PCI_HT_PRI_CMD_MH 0x0400 /* Master Host */
+#define PCI_HT_PRI_CMD_DD 0x0800 /* Default Direction */
+#define PCI_HT_PRI_CMD_DUL 0x1000 /* Drop on Uninitialized Link */
+
+#define PCI_HT_PRI_LCTR0 4 /* Link Control 0 Register */
+#define PCI_HT_PRI_LCNF0 6 /* Link Config 0 Register */
+#define PCI_HT_PRI_LCTR1 8 /* Link Control 1 Register */
+#define PCI_HT_PRI_LCNF1 10 /* Link Config 1 Register */
+#define PCI_HT_PRI_RID 12 /* Revision ID Register */
+#define PCI_HT_PRI_LFRER0 13 /* Link Frequency/Error 0 Register */
+#define PCI_HT_PRI_LFCAP0 14 /* Link Frequency Capability 0 Register */
+#define PCI_HT_PRI_FTR 16 /* Feature Register */
+#define PCI_HT_PRI_LFRER1 17 /* Link Frequency/Error 1 Register */
+#define PCI_HT_PRI_LFCAP1 18 /* Link Frequency Capability 1 Register */
+#define PCI_HT_PRI_ES 20 /* Enumeration Scratchpad Register */
+#define PCI_HT_PRI_EH 22 /* Error Handling Register */
+#define PCI_HT_PRI_MBU 24 /* Memory Base Upper Register */
+#define PCI_HT_PRI_MLU 25 /* Memory Limit Upper Register */
+#define PCI_HT_PRI_BN 26 /* Bus Number Register */
+#define PCI_HT_PRI_SIZEOF 28
+
+/* HyperTransport: Host or Secondary Interface */
+#define PCI_HT_SEC_CMD 2 /* Command Register */
+#define PCI_HT_SEC_CMD_WR 0x0001 /* Warm Reset */
+#define PCI_HT_SEC_CMD_DE 0x0002 /* Double-Ended */
+#define PCI_HT_SEC_CMD_DN 0x007c /* Device Number */
+#define PCI_HT_SEC_CMD_CS 0x0080 /* Chain Side */
+#define PCI_HT_SEC_CMD_HH 0x0100 /* Host Hide */
+#define PCI_HT_SEC_CMD_AS 0x0400 /* Act as Slave */
+#define PCI_HT_SEC_CMD_HIECE 0x0800 /* Host Inbound End of Chain Error */
+#define PCI_HT_SEC_CMD_DUL 0x1000 /* Drop on Uninitialized Link */
+
+#define PCI_HT_SEC_LCTR 4 /* Link Control Register */
+#define PCI_HT_SEC_LCNF 6 /* Link Config Register */
+#define PCI_HT_SEC_RID 8 /* Revision ID Register */
+#define PCI_HT_SEC_LFRER 9 /* Link Frequency/Error Register */
+#define PCI_HT_SEC_LFCAP 10 /* Link Frequency Capability Register */
+#define PCI_HT_SEC_FTR 12 /* Feature Register */
+#define PCI_HT_SEC_FTR_EXTRS 0x0100 /* Extended Register Set */
+#define PCI_HT_SEC_FTR_UCNFE 0x0200 /* Upstream Configuration Enable */
+#define PCI_HT_SEC_ES 16 /* Enumeration Scratchpad Register */
+#define PCI_HT_SEC_EH 18 /* Error Handling Register */
+#define PCI_HT_SEC_MBU 20 /* Memory Base Upper Register */
+#define PCI_HT_SEC_MLU 21 /* Memory Limit Upper Register */
+#define PCI_HT_SEC_SIZEOF 24
+
+/* HyperTransport: Switch */
+#define PCI_HT_SW_CMD 2 /* Switch Command Register */
+#define PCI_HT_SW_CMD_VIBERR 0x0080 /* VIB Error */
+#define PCI_HT_SW_CMD_VIBFL 0x0100 /* VIB Flood */
+#define PCI_HT_SW_CMD_VIBFT 0x0200 /* VIB Fatal */
+#define PCI_HT_SW_CMD_VIBNFT 0x0400 /* VIB Nonfatal */
+#define PCI_HT_SW_PMASK 4 /* Partition Mask Register */
+#define PCI_HT_SW_SWINF 8 /* Switch Info Register */
+#define PCI_HT_SW_SWINF_DP 0x0000001f /* Default Port */
+#define PCI_HT_SW_SWINF_EN 0x00000020 /* Enable Decode */
+#define PCI_HT_SW_SWINF_CR 0x00000040 /* Cold Reset */
+#define PCI_HT_SW_SWINF_PCIDX 0x00000f00 /* Performance Counter Index */
+#define PCI_HT_SW_SWINF_BLRIDX 0x0003f000 /* Base/Limit Range Index */
+#define PCI_HT_SW_SWINF_SBIDX 0x00002000 /* Secondary Base Range Index */
+#define PCI_HT_SW_SWINF_HP 0x00040000 /* Hot Plug */
+#define PCI_HT_SW_SWINF_HIDE 0x00080000 /* Hide Port */
+#define PCI_HT_SW_PCD 12 /* Performance Counter Data Register */
+#define PCI_HT_SW_BLRD 16 /* Base/Limit Range Data Register */
+#define PCI_HT_SW_SBD 20 /* Secondary Base Data Register */
+#define PCI_HT_SW_SIZEOF 24
+
+ /* Counter indices */
+#define PCI_HT_SW_PC_PCR 0x0 /* Posted Command Receive */
+#define PCI_HT_SW_PC_NPCR 0x1 /* Nonposted Command Receive */
+#define PCI_HT_SW_PC_RCR 0x2 /* Response Command Receive */
+#define PCI_HT_SW_PC_PDWR 0x3 /* Posted DW Receive */
+#define PCI_HT_SW_PC_NPDWR 0x4 /* Nonposted DW Receive */
+#define PCI_HT_SW_PC_RDWR 0x5 /* Response DW Receive */
+#define PCI_HT_SW_PC_PCT 0x6 /* Posted Command Transmit */
+#define PCI_HT_SW_PC_NPCT 0x7 /* Nonposted Command Transmit */
+#define PCI_HT_SW_PC_RCT 0x8 /* Response Command Transmit */
+#define PCI_HT_SW_PC_PDWT 0x9 /* Posted DW Transmit */
+#define PCI_HT_SW_PC_NPDWT 0xa /* Nonposted DW Transmit */
+#define PCI_HT_SW_PC_RDWT 0xb /* Response DW Transmit */
+
+ /* Base/Limit Range indices */
+#define PCI_HT_SW_BLR_BASE0_LO 0x0 /* Base 0[31:1], Enable */
+#define PCI_HT_SW_BLR_BASE0_HI 0x1 /* Base 0 Upper */
+#define PCI_HT_SW_BLR_LIM0_LO 0x2 /* Limit 0 Lower */
+#define PCI_HT_SW_BLR_LIM0_HI 0x3 /* Limit 0 Upper */
+
+ /* Secondary Base indices */
+#define PCI_HT_SW_SB_LO 0x0 /* Secondary Base[31:1], Enable */
+#define PCI_HT_SW_S0_HI 0x1 /* Secondary Base Upper */
+
+/* HyperTransport: Interrupt Discovery and Configuration */
+#define PCI_HT_IDC_IDX 2 /* Index Register */
+#define PCI_HT_IDC_DATA 4 /* Data Register */
+#define PCI_HT_IDC_SIZEOF 8
+
+ /* Register indices */
+#define PCI_HT_IDC_IDX_LINT 0x01 /* Last Interrupt Register */
+#define PCI_HT_IDC_LINT 0x00ff0000 /* Last interrupt definition */
+#define PCI_HT_IDC_IDX_IDR 0x10 /* Interrupt Definition Registers */
+ /* Low part (at index) */
+#define PCI_HT_IDC_IDR_MASK 0x10000001 /* Mask */
+#define PCI_HT_IDC_IDR_POL 0x10000002 /* Polarity */
+#define PCI_HT_IDC_IDR_II_2 0x1000001c /* IntrInfo[4:2]: Message Type */
+#define PCI_HT_IDC_IDR_II_5 0x10000020 /* IntrInfo[5]: Request EOI */
+#define PCI_HT_IDC_IDR_II_6 0x00ffffc0 /* IntrInfo[23:6] */
+#define PCI_HT_IDC_IDR_II_24 0xff000000 /* IntrInfo[31:24] */
+ /* High part (at index + 1) */
+#define PCI_HT_IDC_IDR_II_32 0x00ffffff /* IntrInfo[55:32] */
+#define PCI_HT_IDC_IDR_PASSPW 0x40000000 /* PassPW setting for messages */
+#define PCI_HT_IDC_IDR_WEOI 0x80000000 /* Waiting for EOI */
+
+/* HyperTransport: Revision ID */
+#define PCI_HT_RID_RID 2 /* Revision Register */
+#define PCI_HT_RID_SIZEOF 4
+
+/* HyperTransport: UnitID Clumping */
+#define PCI_HT_UIDC_CS 4 /* Clumping Support Register */
+#define PCI_HT_UIDC_CE 8 /* Clumping Enable Register */
+#define PCI_HT_UIDC_SIZEOF 12
+
+/* HyperTransport: Extended Configuration Space Access */
+#define PCI_HT_ECSA_ADDR 4 /* Configuration Address Register */
+#define PCI_HT_ECSA_ADDR_REG 0x00000ffc /* Register */
+#define PCI_HT_ECSA_ADDR_FUN 0x00007000 /* Function */
+#define PCI_HT_ECSA_ADDR_DEV 0x000f1000 /* Device */
+#define PCI_HT_ECSA_ADDR_BUS 0x0ff00000 /* Bus Number */
+#define PCI_HT_ECSA_ADDR_TYPE 0x10000000 /* Access Type */
+#define PCI_HT_ECSA_DATA 8 /* Configuration Data Register */
+#define PCI_HT_ECSA_SIZEOF 12
+
+/* HyperTransport: Address Mapping */
+#define PCI_HT_AM_CMD 2 /* Command Register */
+#define PCI_HT_AM_CMD_NDMA 0x000f /* Number of DMA Mappings */
+#define PCI_HT_AM_CMD_IOSIZ 0x01f0 /* I/O Size */
+#define PCI_HT_AM_CMD_MT 0x0600 /* Map Type */
+#define PCI_HT_AM_CMD_MT_40B 0x0000 /* 40-bit */
+#define PCI_HT_AM_CMD_MT_64B 0x0200 /* 64-bit */
+
+ /* Window Control Register bits */
+#define PCI_HT_AM_SBW_CTR_COMP 0x1 /* Compat */
+#define PCI_HT_AM_SBW_CTR_NCOH 0x2 /* NonCoherent */
+#define PCI_HT_AM_SBW_CTR_ISOC 0x4 /* Isochronous */
+#define PCI_HT_AM_SBW_CTR_EN 0x8 /* Enable */
+
+/* HyperTransport: 40-bit Address Mapping */
+#define PCI_HT_AM40_SBNPW 4 /* Secondary Bus Non-Prefetchable Window Register */
+#define PCI_HT_AM40_SBW_BASE 0x000fffff /* Window Base */
+#define PCI_HT_AM40_SBW_CTR 0xf0000000 /* Window Control */
+#define PCI_HT_AM40_SBPW 8 /* Secondary Bus Prefetchable Window Register */
+#define PCI_HT_AM40_DMA_PBASE0 12 /* DMA Window Primary Base 0 Register */
+#define PCI_HT_AM40_DMA_CTR0 15 /* DMA Window Control 0 Register */
+#define PCI_HT_AM40_DMA_CTR_CTR 0xf0 /* Window Control */
+#define PCI_HT_AM40_DMA_SLIM0 16 /* DMA Window Secondary Limit 0 Register */
+#define PCI_HT_AM40_DMA_SBASE0 18 /* DMA Window Secondary Base 0 Register */
+#define PCI_HT_AM40_SIZEOF 12 /* size is variable: 12 + 8 * NDMA */
+
+/* HyperTransport: 64-bit Address Mapping */
+#define PCI_HT_AM64_IDX 4 /* Index Register */
+#define PCI_HT_AM64_DATA_LO 8 /* Data Lower Register */
+#define PCI_HT_AM64_DATA_HI 12 /* Data Upper Register */
+#define PCI_HT_AM64_SIZEOF 16
+
+ /* Register indices */
+#define PCI_HT_AM64_IDX_SBNPW 0x00 /* Secondary Bus Non-Prefetchable Window Register */
+#define PCI_HT_AM64_W_BASE_LO 0xfff00000 /* Window Base Lower */
+#define PCI_HT_AM64_W_CTR 0x0000000f /* Window Control */
+#define PCI_HT_AM64_IDX_SBPW 0x01 /* Secondary Bus Prefetchable Window Register */
+#define PCI_HT_AM64_IDX_PBNPW 0x02 /* Primary Bus Non-Prefetchable Window Register */
+#define PCI_HT_AM64_IDX_DMAPB0 0x04 /* DMA Window Primary Base 0 Register */
+#define PCI_HT_AM64_IDX_DMASB0 0x05 /* DMA Window Secondary Base 0 Register */
+#define PCI_HT_AM64_IDX_DMASL0 0x06 /* DMA Window Secondary Limit 0 Register */
+
+/* HyperTransport: MSI Mapping */
+#define PCI_HT_MSIM_CMD 2 /* Command Register */
+#define PCI_HT_MSIM_CMD_EN 0x0001 /* Mapping Active */
+#define PCI_HT_MSIM_CMD_FIXD 0x0002 /* MSI Mapping Address Fixed */
+#define PCI_HT_MSIM_ADDR_LO 4 /* MSI Mapping Address Lower Register */
+#define PCI_HT_MSIM_ADDR_HI 8 /* MSI Mapping Address Upper Register */
+#define PCI_HT_MSIM_SIZEOF 12
+
+/* HyperTransport: DirectRoute */
+#define PCI_HT_DR_CMD 2 /* Command Register */
+#define PCI_HT_DR_CMD_NDRS 0x000f /* Number of DirectRoute Spaces */
+#define PCI_HT_DR_CMD_IDX 0x01f0 /* Index */
+#define PCI_HT_DR_EN 4 /* Enable Vector Register */
+#define PCI_HT_DR_DATA 8 /* Data Register */
+#define PCI_HT_DR_SIZEOF 12
+
+ /* Register indices */
+#define PCI_HT_DR_IDX_BASE_LO 0x00 /* DirectRoute Base Lower Register */
+#define PCI_HT_DR_OTNRD 0x00000001 /* Opposite to Normal Request Direction */
+#define PCI_HT_DR_BL_LO 0xffffff00 /* Base/Limit Lower */
+#define PCI_HT_DR_IDX_BASE_HI 0x01 /* DirectRoute Base Upper Register */
+#define PCI_HT_DR_IDX_LIMIT_LO 0x02 /* DirectRoute Limit Lower Register */
+#define PCI_HT_DR_IDX_LIMIT_HI 0x03 /* DirectRoute Limit Upper Register */
+
+/* HyperTransport: VCSet */
+#define PCI_HT_VCS_SUP 4 /* VCSets Supported Register */
+#define PCI_HT_VCS_L1EN 5 /* Link 1 VCSets Enabled Register */
+#define PCI_HT_VCS_L0EN 6 /* Link 0 VCSets Enabled Register */
+#define PCI_HT_VCS_SBD 8 /* Stream Bucket Depth Register */
+#define PCI_HT_VCS_SINT 9 /* Stream Interval Register */
+#define PCI_HT_VCS_SSUP 10 /* Number of Streaming VCs Supported Register */
+#define PCI_HT_VCS_SSUP_0 0x00 /* Streaming VC 0 */
+#define PCI_HT_VCS_SSUP_3 0x01 /* Streaming VCs 0-3 */
+#define PCI_HT_VCS_SSUP_15 0x02 /* Streaming VCs 0-15 */
+#define PCI_HT_VCS_NFCBD 12 /* Non-FC Bucket Depth Register */
+#define PCI_HT_VCS_NFCINT 13 /* Non-FC Bucket Interval Register */
+#define PCI_HT_VCS_SIZEOF 16
+
+/* HyperTransport: Retry Mode */
+#define PCI_HT_RM_CTR0 4 /* Control 0 Register */
+#define PCI_HT_RM_CTR_LRETEN 0x01 /* Link Retry Enable */
+#define PCI_HT_RM_CTR_FSER 0x02 /* Force Single Error */
+#define PCI_HT_RM_CTR_ROLNEN 0x04 /* Rollover Nonfatal Enable */
+#define PCI_HT_RM_CTR_FSS 0x08 /* Force Single Stomp */
+#define PCI_HT_RM_CTR_RETNEN 0x10 /* Retry Nonfatal Enable */
+#define PCI_HT_RM_CTR_RETFEN 0x20 /* Retry Fatal Enable */
+#define PCI_HT_RM_CTR_AA 0xc0 /* Allowed Attempts */
+#define PCI_HT_RM_STS0 5 /* Status 0 Register */
+#define PCI_HT_RM_STS_RETSNT 0x01 /* Retry Sent */
+#define PCI_HT_RM_STS_CNTROL 0x02 /* Count Rollover */
+#define PCI_HT_RM_STS_SRCV 0x04 /* Stomp Received */
+#define PCI_HT_RM_CTR1 6 /* Control 1 Register */
+#define PCI_HT_RM_STS1 7 /* Status 1 Register */
+#define PCI_HT_RM_CNT0 8 /* Retry Count 0 Register */
+#define PCI_HT_RM_CNT1 10 /* Retry Count 1 Register */
+#define PCI_HT_RM_SIZEOF 12
+
+/* Vendor-Specific Capability (see PCI_EVNDR_xxx for the PCIe version) */
+#define PCI_VNDR_LENGTH 2 /* Length byte */
+
+/* PCI Express */
+#define PCI_EXP_FLAGS 0x2 /* Capabilities register */
+#define PCI_EXP_FLAGS_VERS 0x000f /* Capability version */
+#define PCI_EXP_FLAGS_TYPE 0x00f0 /* Device/Port type */
+#define PCI_EXP_TYPE_ENDPOINT 0x0 /* Express Endpoint */
+#define PCI_EXP_TYPE_LEG_END 0x1 /* Legacy Endpoint */
+#define PCI_EXP_TYPE_ROOT_PORT 0x4 /* Root Port */
+#define PCI_EXP_TYPE_UPSTREAM 0x5 /* Upstream Port */
+#define PCI_EXP_TYPE_DOWNSTREAM 0x6 /* Downstream Port */
+#define PCI_EXP_TYPE_PCI_BRIDGE 0x7 /* PCIe to PCI/PCI-X Bridge */
+#define PCI_EXP_TYPE_PCIE_BRIDGE 0x8 /* PCI/PCI-X to PCIe Bridge */
+#define PCI_EXP_TYPE_ROOT_INT_EP 0x9 /* Root Complex Integrated Endpoint */
+#define PCI_EXP_TYPE_ROOT_EC 0xa /* Root Complex Event Collector */
+#define PCI_EXP_FLAGS_SLOT 0x0100 /* Slot implemented */
+#define PCI_EXP_FLAGS_IRQ 0x3e00 /* Interrupt message number */
+#define PCI_EXP_DEVCAP 0x4 /* Device capabilities */
+#define PCI_EXP_DEVCAP_PAYLOAD 0x07 /* Max_Payload_Size */
+#define PCI_EXP_DEVCAP_PHANTOM 0x18 /* Phantom functions */
+#define PCI_EXP_DEVCAP_EXT_TAG 0x20 /* Extended tags */
+#define PCI_EXP_DEVCAP_L0S 0x1c0 /* L0s Acceptable Latency */
+#define PCI_EXP_DEVCAP_L1 0xe00 /* L1 Acceptable Latency */
+#define PCI_EXP_DEVCAP_ATN_BUT 0x1000 /* Attention Button Present */
+#define PCI_EXP_DEVCAP_ATN_IND 0x2000 /* Attention Indicator Present */
+#define PCI_EXP_DEVCAP_PWR_IND 0x4000 /* Power Indicator Present */
+#define PCI_EXP_DEVCAP_RBE 0x8000 /* Role-Based Error Reporting */
+#define PCI_EXP_DEVCAP_PWR_VAL 0x3fc0000 /* Slot Power Limit Value */
+#define PCI_EXP_DEVCAP_PWR_SCL 0xc000000 /* Slot Power Limit Scale */
+#define PCI_EXP_DEVCAP_FLRESET 0x10000000 /* Function-Level Reset */
+#define PCI_EXP_DEVCTL 0x8 /* Device Control */
+#define PCI_EXP_DEVCTL_CERE 0x0001 /* Correctable Error Reporting En. */
+#define PCI_EXP_DEVCTL_NFERE 0x0002 /* Non-Fatal Error Reporting Enable */
+#define PCI_EXP_DEVCTL_FERE 0x0004 /* Fatal Error Reporting Enable */
+#define PCI_EXP_DEVCTL_URRE 0x0008 /* Unsupported Request Reporting En. */
+#define PCI_EXP_DEVCTL_RELAXED 0x0010 /* Enable Relaxed Ordering */
+#define PCI_EXP_DEVCTL_PAYLOAD 0x00e0 /* Max_Payload_Size */
+#define PCI_EXP_DEVCTL_EXT_TAG 0x0100 /* Extended Tag Field Enable */
+#define PCI_EXP_DEVCTL_PHANTOM 0x0200 /* Phantom Functions Enable */
+#define PCI_EXP_DEVCTL_AUX_PME 0x0400 /* Auxiliary Power PM Enable */
+#define PCI_EXP_DEVCTL_NOSNOOP 0x0800 /* Enable No Snoop */
+#define PCI_EXP_DEVCTL_READRQ 0x7000 /* Max_Read_Request_Size */
+#define PCI_EXP_DEVCTL_BCRE 0x8000 /* Bridge Configuration Retry Enable */
+#define PCI_EXP_DEVCTL_FLRESET 0x8000 /* Function-Level Reset [bit shared with BCRE] */
+#define PCI_EXP_DEVSTA 0xa /* Device Status */
+#define PCI_EXP_DEVSTA_CED 0x01 /* Correctable Error Detected */
+#define PCI_EXP_DEVSTA_NFED 0x02 /* Non-Fatal Error Detected */
+#define PCI_EXP_DEVSTA_FED 0x04 /* Fatal Error Detected */
+#define PCI_EXP_DEVSTA_URD 0x08 /* Unsupported Request Detected */
+#define PCI_EXP_DEVSTA_AUXPD 0x10 /* AUX Power Detected */
+#define PCI_EXP_DEVSTA_TRPND 0x20 /* Transactions Pending */
+#define PCI_EXP_LNKCAP 0xc /* Link Capabilities */
+#define PCI_EXP_LNKCAP_SPEED 0x0000f /* Maximum Link Speed */
+#define PCI_EXP_LNKCAP_WIDTH 0x003f0 /* Maximum Link Width */
+#define PCI_EXP_LNKCAP_ASPM 0x00c00 /* Active State Power Management */
+#define PCI_EXP_LNKCAP_L0S 0x07000 /* L0s Exit Latency */
+#define PCI_EXP_LNKCAP_L1 0x38000 /* L1 Exit Latency */
+#define PCI_EXP_LNKCAP_CLOCKPM 0x40000 /* Clock Power Management */
+#define PCI_EXP_LNKCAP_SURPRISE 0x80000 /* Surprise Down Error Reporting */
+#define PCI_EXP_LNKCAP_DLLA 0x100000 /* Data Link Layer Active Reporting */
+#define PCI_EXP_LNKCAP_LBNC 0x200000 /* Link Bandwidth Notification Capability */
+#define PCI_EXP_LNKCAP_AOC 0x400000 /* ASPM Optionality Compliance */
+#define PCI_EXP_LNKCAP_PORT 0xff000000 /* Port Number */
+#define PCI_EXP_LNKCTL 0x10 /* Link Control */
+#define PCI_EXP_LNKCTL_ASPM 0x0003 /* ASPM Control */
+#define PCI_EXP_LNKCTL_RCB 0x0008 /* Read Completion Boundary */
+#define PCI_EXP_LNKCTL_DISABLE 0x0010 /* Link Disable */
+#define PCI_EXP_LNKCTL_RETRAIN 0x0020 /* Retrain Link */
+#define PCI_EXP_LNKCTL_CLOCK 0x0040 /* Common Clock Configuration */
+#define PCI_EXP_LNKCTL_XSYNCH 0x0080 /* Extended Synch */
+#define PCI_EXP_LNKCTL_CLOCKPM 0x0100 /* Clock Power Management */
+#define PCI_EXP_LNKCTL_HWAUTWD 0x0200 /* Hardware Autonomous Width Disable */
+#define PCI_EXP_LNKCTL_BWMIE 0x0400 /* Bandwidth Mgmt Interrupt Enable */
+#define PCI_EXP_LNKCTL_AUTBWIE 0x0800 /* Autonomous Bandwidth Mgmt Interrupt Enable */
+#define PCI_EXP_LNKSTA 0x12 /* Link Status */
+#define PCI_EXP_LNKSTA_SPEED 0x000f /* Negotiated Link Speed */
+#define PCI_EXP_LNKSTA_WIDTH 0x03f0 /* Negotiated Link Width */
+#define PCI_EXP_LNKSTA_TR_ERR 0x0400 /* Training Error (obsolete) */
+#define PCI_EXP_LNKSTA_TRAIN 0x0800 /* Link Training */
+#define PCI_EXP_LNKSTA_SL_CLK 0x1000 /* Slot Clock Configuration */
+#define PCI_EXP_LNKSTA_DL_ACT 0x2000 /* Data Link Layer in DL_Active State */
+#define PCI_EXP_LNKSTA_BWMGMT 0x4000 /* Bandwidth Mgmt Status */
+#define PCI_EXP_LNKSTA_AUTBW 0x8000 /* Autonomous Bandwidth Mgmt Status */
+#define PCI_EXP_SLTCAP 0x14 /* Slot Capabilities */
+#define PCI_EXP_SLTCAP_ATNB 0x0001 /* Attention Button Present */
+#define PCI_EXP_SLTCAP_PWRC 0x0002 /* Power Controller Present */
+#define PCI_EXP_SLTCAP_MRL 0x0004 /* MRL Sensor Present */
+#define PCI_EXP_SLTCAP_ATNI 0x0008 /* Attention Indicator Present */
+#define PCI_EXP_SLTCAP_PWRI 0x0010 /* Power Indicator Present */
+#define PCI_EXP_SLTCAP_HPS 0x0020 /* Hot-Plug Surprise */
+#define PCI_EXP_SLTCAP_HPC 0x0040 /* Hot-Plug Capable */
+#define PCI_EXP_SLTCAP_PWR_VAL 0x00007f80 /* Slot Power Limit Value */
+#define PCI_EXP_SLTCAP_PWR_SCL 0x00018000 /* Slot Power Limit Scale */
+#define PCI_EXP_SLTCAP_INTERLOCK 0x020000 /* Electromechanical Interlock Present */
+#define PCI_EXP_SLTCAP_NOCMDCOMP 0x040000 /* No Command Completed Support */
+#define PCI_EXP_SLTCAP_PSN 0xfff80000 /* Physical Slot Number */
+#define PCI_EXP_SLTCTL 0x18 /* Slot Control */
+#define PCI_EXP_SLTCTL_ATNB 0x0001 /* Attention Button Pressed Enable */
+#define PCI_EXP_SLTCTL_PWRF 0x0002 /* Power Fault Detected Enable */
+#define PCI_EXP_SLTCTL_MRLS 0x0004 /* MRL Sensor Changed Enable */
+#define PCI_EXP_SLTCTL_PRSD 0x0008 /* Presence Detect Changed Enable */
+#define PCI_EXP_SLTCTL_CMDC 0x0010 /* Command Completed Interrupt Enable */
+#define PCI_EXP_SLTCTL_HPIE 0x0020 /* Hot-Plug Interrupt Enable */
+#define PCI_EXP_SLTCTL_ATNI 0x00c0 /* Attention Indicator Control */
+#define PCI_EXP_SLTCTL_PWRI 0x0300 /* Power Indicator Control */
+#define PCI_EXP_SLTCTL_PWRC 0x0400 /* Power Controller Control */
+#define PCI_EXP_SLTCTL_INTERLOCK 0x0800 /* Electromechanical Interlock Control */
+#define PCI_EXP_SLTCTL_LLCHG 0x1000 /* Data Link Layer State Changed Enable */
+#define PCI_EXP_SLTSTA 0x1a /* Slot Status */
+#define PCI_EXP_SLTSTA_ATNB 0x0001 /* Attention Button Pressed */
+#define PCI_EXP_SLTSTA_PWRF 0x0002 /* Power Fault Detected */
+#define PCI_EXP_SLTSTA_MRLS 0x0004 /* MRL Sensor Changed */
+#define PCI_EXP_SLTSTA_PRSD 0x0008 /* Presence Detect Changed */
+#define PCI_EXP_SLTSTA_CMDC 0x0010 /* Command Completed */
+#define PCI_EXP_SLTSTA_MRL_ST 0x0020 /* MRL Sensor State */
+#define PCI_EXP_SLTSTA_PRES 0x0040 /* Presence Detect State */
+#define PCI_EXP_SLTSTA_INTERLOCK 0x0080 /* Electromechanical Interlock Status */
+#define PCI_EXP_SLTSTA_LLCHG 0x0100 /* Data Link Layer State Changed */
+#define PCI_EXP_RTCTL 0x1c /* Root Control */
+#define PCI_EXP_RTCTL_SECEE 0x0001 /* System Error on Correctable Error */
+#define PCI_EXP_RTCTL_SENFEE 0x0002 /* System Error on Non-Fatal Error */
+#define PCI_EXP_RTCTL_SEFEE 0x0004 /* System Error on Fatal Error */
+#define PCI_EXP_RTCTL_PMEIE 0x0008 /* PME Interrupt Enable */
+#define PCI_EXP_RTCTL_CRSVIS 0x0010 /* Configuration Request Retry Status Visible to SW */
+#define PCI_EXP_RTCAP 0x1e /* Root Capabilities */
+#define PCI_EXP_RTCAP_CRSVIS 0x0001 /* Configuration Request Retry Status Visible to SW */
+#define PCI_EXP_RTSTA 0x20 /* Root Status */
+#define PCI_EXP_RTSTA_PME_REQID 0x0000ffff /* PME Requester ID */
+#define PCI_EXP_RTSTA_PME_STATUS 0x00010000 /* PME Status */
+#define PCI_EXP_RTSTA_PME_PENDING 0x00020000 /* PME is Pending */
+#define PCI_EXP_DEVCAP2 0x24 /* Device capabilities 2 */
+#define PCI_EXP_DEVCAP2_TIMEOUT_RANGE(x) ((x) & 0xf) /* Completion Timeout Ranges Supported */
+#define PCI_EXP_DEVCAP2_TIMEOUT_DIS 0x0010 /* Completion Timeout Disable Supported */
+#define PCI_EXP_DEVCAP2_ARI 0x0020 /* ARI Forwarding Supported */
+#define PCI_EXP_DEVCAP2_ATOMICOP_ROUTING 0x0040 /* AtomicOp Routing Supported */
+#define PCI_EXP_DEVCAP2_32BIT_ATOMICOP_COMP 0x0080 /* 32bit AtomicOp Completer Supported */
+#define PCI_EXP_DEVCAP2_64BIT_ATOMICOP_COMP 0x0100 /* 64bit AtomicOp Completer Supported */
+#define PCI_EXP_DEVCAP2_128BIT_CAS_COMP 0x0200 /* 128bit CAS Completer Supported */
+#define PCI_EXP_DEVCAP2_NROPRPRP 0x0400 /* No RO-enabled PR-PR Passing */
+#define PCI_EXP_DEVCAP2_LTR 0x0800 /* LTR supported */
+#define PCI_EXP_DEVCAP2_TPH_COMP(x) (((x) >> 12) & 3) /* TPH Completer Supported */
+#define PCI_EXP_DEVCAP2_LN_CLS(x) (((x) >> 14) & 3) /* LN System CLS Supported */
+#define PCI_EXP_DEVCAP2_10BIT_TAG_COMP 0x00010000 /* 10 Bit Tag Completer */
+#define PCI_EXP_DEVCAP2_10BIT_TAG_REQ 0x00020000 /* 10 Bit Tag Requester */
+#define PCI_EXP_DEVCAP2_OBFF(x) (((x) >> 18) & 3) /* OBFF supported */
+#define PCI_EXP_DEVCAP2_EXTFMT 0x00100000 /* Extended Fmt Field Supported */
+#define PCI_EXP_DEVCAP2_EE_TLP 0x00200000 /* End-End TLP Prefix Supported */
+#define PCI_EXP_DEVCAP2_MEE_TLP(x) (((x) >> 22) & 3) /* Max End-End TLP Prefixes */
+#define PCI_EXP_DEVCAP2_EPR(x) (((x) >> 24) & 3) /* Emergency Power Reduction Supported */
+#define PCI_EXP_DEVCAP2_EPR_INIT 0x04000000 /* Emergency Power Reduction Initialization Required */
+#define PCI_EXP_DEVCAP2_FRS 0x80000000 /* FRS supported */
+#define PCI_EXP_DEVCTL2 0x28 /* Device Control */
+#define PCI_EXP_DEVCTL2_TIMEOUT_VALUE(x) ((x) & 0xf) /* Completion Timeout Value */
+#define PCI_EXP_DEVCTL2_TIMEOUT_DIS 0x0010 /* Completion Timeout Disable */
+#define PCI_EXP_DEVCTL2_ARI 0x0020 /* ARI Forwarding */
+#define PCI_EXP_DEVCTL2_ATOMICOP_REQUESTER_EN 0x0040 /* AtomicOp RequesterEnable */
+#define PCI_EXP_DEVCTL2_ATOMICOP_EGRESS_BLOCK 0x0080 /* AtomicOp Egress Blocking */
+#define PCI_EXP_DEVCTL2_IDO_REQ_EN 0x0100 /* Allow IDO for requests */
+#define PCI_EXP_DEVCTL2_IDO_CMP_EN 0x0200 /* Allow IDO for completions */
+#define PCI_EXP_DEVCTL2_LTR 0x0400 /* LTR enabled */
+#define PCI_EXP_DEVCTL2_EPR_REQ 0x0800 /* Emergency Power Reduction Request */
+#define PCI_EXP_DEVCTL2_10BIT_TAG_REQ 0x1000 /* 10 Bit Tag Requester enabled */
+#define PCI_EXP_DEVCTL2_OBFF(x) (((x) >> 13) & 3) /* OBFF enabled */
+#define PCI_EXP_DEVCTL2_EE_TLP_BLK 0x8000 /* End-End TLP Prefix Blocking */
+#define PCI_EXP_DEVSTA2 0x2a /* Device Status */
+#define PCI_EXP_LNKCAP2 0x2c /* Link Capabilities */
+#define PCI_EXP_LNKCAP2_SPEED(x) (((x) >> 1) & 0x7f)
+#define PCI_EXP_LNKCAP2_CROSSLINK 0x00000100 /* Crosslink Supported */
+#define PCI_EXP_LNKCAP2_RETIMER 0x00800000 /* Retimer Supported */
+#define PCI_EXP_LNKCAP2_2RETIMERS 0x01000000 /* 2 Retimers Supported */
+#define PCI_EXP_LNKCAP2_DRS 0x80000000 /* Device Readiness Status */
+#define PCI_EXP_LNKCTL2 0x30 /* Link Control */
+#define PCI_EXP_LNKCTL2_SPEED(x) ((x) & 0xf) /* Target Link Speed */
+#define PCI_EXP_LNKCTL2_CMPLNC 0x0010 /* Enter Compliance */
+#define PCI_EXP_LNKCTL2_SPEED_DIS 0x0020 /* Hardware Autonomous Speed Disable */
+#define PCI_EXP_LNKCTL2_DEEMPHASIS(x) (((x) >> 6) & 1) /* Selectable De-emphasis */
+#define PCI_EXP_LNKCTL2_MARGIN(x) (((x) >> 7) & 7) /* Transmit Margin */
+#define PCI_EXP_LNKCTL2_MOD_CMPLNC 0x0400 /* Enter Modified Compliance */
+#define PCI_EXP_LNKCTL2_CMPLNC_SOS 0x0800 /* Compliance SOS */
+#define PCI_EXP_LNKCTL2_COM_DEEMPHASIS(x) (((x) >> 12) & 0xf) /* Compliance Preset/De-emphasis */
+#define PCI_EXP_LNKSTA2 0x32 /* Link Status */
+#define PCI_EXP_LINKSTA2_DEEMPHASIS(x) ((x) & 1) /* Current De-emphasis Level */
+#define PCI_EXP_LINKSTA2_EQU_COMP 0x02 /* Equalization Complete */
+#define PCI_EXP_LINKSTA2_EQU_PHASE1 0x04 /* Equalization Phase 1 Successful */
+#define PCI_EXP_LINKSTA2_EQU_PHASE2 0x08 /* Equalization Phase 2 Successful */
+#define PCI_EXP_LINKSTA2_EQU_PHASE3 0x10 /* Equalization Phase 3 Successful */
+#define PCI_EXP_LINKSTA2_EQU_REQ 0x20 /* Link Equalization Request */
+#define PCI_EXP_LINKSTA2_RETIMER 0x0040 /* Retimer Detected */
+#define PCI_EXP_LINKSTA2_2RETIMERS 0x0080 /* 2 Retimers Detected */
+#define PCI_EXP_LINKSTA2_CROSSLINK(x) (((x) >> 8) & 0x3) /* Crosslink Res */
+#define PCI_EXP_LINKSTA2_COMPONENT(x) (((x) >> 12) & 0x7) /* Presence */
+#define PCI_EXP_LINKSTA2_DRS_RCVD 0x8000 /* DRS Msg Received */
+#define PCI_EXP_SLTCAP2 0x34 /* Slot Capabilities */
+#define PCI_EXP_SLTCTL2 0x38 /* Slot Control */
+#define PCI_EXP_SLTSTA2 0x3a /* Slot Status */
+
+/* MSI-X */
+#define PCI_MSIX_ENABLE 0x8000
+#define PCI_MSIX_MASK 0x4000
+#define PCI_MSIX_TABSIZE 0x07ff
+#define PCI_MSIX_TABLE 4
+#define PCI_MSIX_PBA 8
+#define PCI_MSIX_BIR 0x7
+
+/* Subsystem vendor/device ID for PCI bridges */
+#define PCI_SSVID_VENDOR 4
+#define PCI_SSVID_DEVICE 6
+
+/* PCI Advanced Features */
+#define PCI_AF_CAP 3
+#define PCI_AF_CAP_TP 0x01
+#define PCI_AF_CAP_FLR 0x02
+#define PCI_AF_CTRL 4
+#define PCI_AF_CTRL_FLR 0x01
+#define PCI_AF_STATUS 5
+#define PCI_AF_STATUS_TP 0x01
+
+/* SATA Host Bus Adapter */
+#define PCI_SATA_HBA_BARS 4
+#define PCI_SATA_HBA_REG0 8
+
+/* Enhanced Allocation (EA) */
+#define PCI_EA_CAP_TYPE1_SECONDARY 4
+#define PCI_EA_CAP_TYPE1_SUBORDINATE 5
+/* EA Entry header */
+#define PCI_EA_CAP_ENT_WRITABLE 0x40000000 /* Writable: 1 = RW, 0 = HwInit */
+#define PCI_EA_CAP_ENT_ENABLE 0x80000000 /* Enable for this entry */
+
+/*** Definitions of extended capabilities ***/
+
+/* Advanced Error Reporting */
+#define PCI_ERR_UNCOR_STATUS 4 /* Uncorrectable Error Status */
+#define PCI_ERR_UNC_TRAIN 0x00000001 /* Undefined in PCIe rev1.1 & 2.0 spec */
+#define PCI_ERR_UNC_DLP 0x00000010 /* Data Link Protocol */
+#define PCI_ERR_UNC_SDES 0x00000020 /* Surprise Down Error */
+#define PCI_ERR_UNC_POISON_TLP 0x00001000 /* Poisoned TLP */
+#define PCI_ERR_UNC_FCP 0x00002000 /* Flow Control Protocol */
+#define PCI_ERR_UNC_COMP_TIME 0x00004000 /* Completion Timeout */
+#define PCI_ERR_UNC_COMP_ABORT 0x00008000 /* Completer Abort */
+#define PCI_ERR_UNC_UNX_COMP 0x00010000 /* Unexpected Completion */
+#define PCI_ERR_UNC_RX_OVER 0x00020000 /* Receiver Overflow */
+#define PCI_ERR_UNC_MALF_TLP 0x00040000 /* Malformed TLP */
+#define PCI_ERR_UNC_ECRC 0x00080000 /* ECRC Error Status */
+#define PCI_ERR_UNC_UNSUP 0x00100000 /* Unsupported Request */
+#define PCI_ERR_UNC_ACS_VIOL 0x00200000 /* ACS Violation */
+#define PCI_ERR_UNCOR_MASK 8 /* Uncorrectable Error Mask */
+ /* Same bits as above */
+#define PCI_ERR_UNCOR_SEVER 12 /* Uncorrectable Error Severity */
+ /* Same bits as above */
+#define PCI_ERR_COR_STATUS 16 /* Correctable Error Status */
+#define PCI_ERR_COR_RCVR 0x00000001 /* Receiver Error Status */
+#define PCI_ERR_COR_BAD_TLP 0x00000040 /* Bad TLP Status */
+#define PCI_ERR_COR_BAD_DLLP 0x00000080 /* Bad DLLP Status */
+#define PCI_ERR_COR_REP_ROLL 0x00000100 /* REPLAY_NUM Rollover */
+#define PCI_ERR_COR_REP_TIMER 0x00001000 /* Replay Timer Timeout */
+#define PCI_ERR_COR_REP_ANFE 0x00002000 /* Advisory Non-Fatal Error */
+#define PCI_ERR_COR_MASK 20 /* Correctable Error Mask */
+ /* Same bits as above */
+#define PCI_ERR_CAP 24 /* Advanced Error Capabilities */
+#define PCI_ERR_CAP_FEP(x) ((x) & 31) /* First Error Pointer */
+#define PCI_ERR_CAP_ECRC_GENC 0x00000020 /* ECRC Generation Capable */
+#define PCI_ERR_CAP_ECRC_GENE 0x00000040 /* ECRC Generation Enable */
+#define PCI_ERR_CAP_ECRC_CHKC 0x00000080 /* ECRC Check Capable */
+#define PCI_ERR_CAP_ECRC_CHKE 0x00000100 /* ECRC Check Enable */
+#define PCI_ERR_CAP_MULT_HDRC 0x00000200 /* Multiple Header Capable */
+#define PCI_ERR_CAP_MULT_HDRE 0x00000400 /* Multiple Header Enable */
+#define PCI_ERR_CAP_TLP_PFX 0x00000800 /* TLP Prefix Log Present */
+#define PCI_ERR_CAP_HDR_LOG 0x00001000 /* Completion Timeout Prefix/Header Log Capable */
+#define PCI_ERR_HEADER_LOG 28 /* Header Log Register (16 bytes) */
+#define PCI_ERR_ROOT_COMMAND 44 /* Root Error Command */
+#define PCI_ERR_ROOT_CMD_COR_EN 0x00000001 /* Correctable Error Reporting Enable */
+#define PCI_ERR_ROOT_CMD_NONFATAL_EN 0x00000002 /* Non-Fatal Error Reporting Enable*/
+#define PCI_ERR_ROOT_CMD_FATAL_EN 0x00000004 /* Fatal Error Reporting Enable */
+#define PCI_ERR_ROOT_STATUS 48 /* Root Error Status */
+#define PCI_ERR_ROOT_COR_RCV 0x00000001 /* ERR_COR Received */
+#define PCI_ERR_ROOT_MULTI_COR_RCV 0x00000002 /* Multiple ERR_COR Received */
+#define PCI_ERR_ROOT_UNCOR_RCV 0x00000004 /* ERR_FATAL/NONFATAL Received */
+#define PCI_ERR_ROOT_MULTI_UNCOR_RCV 0x00000008 /* Multiple ERR_FATAL/NONFATAL Received */
+#define PCI_ERR_ROOT_FIRST_FATAL 0x00000010 /* First Uncorrectable Fatal */
+#define PCI_ERR_ROOT_NONFATAL_RCV 0x00000020 /* Non-Fatal Error Messages Received */
+#define PCI_ERR_ROOT_FATAL_RCV 0x00000040 /* Fatal Error Messages Received */
+#define PCI_ERR_MSG_NUM(x) (((x) >> 27) & 0x1f) /* MSI/MSI-X vector */
+#define PCI_ERR_ROOT_COR_SRC 52
+#define PCI_ERR_ROOT_SRC 54
+
+/* Virtual Channel */
+#define PCI_VC_PORT_REG1 4
+#define PCI_VC_PORT_REG2 8
+#define PCI_VC_PORT_CTRL 12
+#define PCI_VC_PORT_STATUS 14
+#define PCI_VC_RES_CAP 16
+#define PCI_VC_RES_CTRL 20
+#define PCI_VC_RES_STATUS 26
+
+/* Power Budgeting */
+#define PCI_PWR_DSR 4 /* Data Select Register */
+#define PCI_PWR_DATA 8 /* Data Register */
+#define PCI_PWR_DATA_BASE(x) ((x) & 0xff) /* Base Power */
+#define PCI_PWR_DATA_SCALE(x) (((x) >> 8) & 3) /* Data Scale */
+#define PCI_PWR_DATA_PM_SUB(x) (((x) >> 10) & 7) /* PM Sub State */
+#define PCI_PWR_DATA_PM_STATE(x) (((x) >> 13) & 3) /* PM State */
+#define PCI_PWR_DATA_TYPE(x) (((x) >> 15) & 7) /* Type */
+#define PCI_PWR_DATA_RAIL(x) (((x) >> 18) & 7) /* Power Rail */
+#define PCI_PWR_CAP 12 /* Capability */
+#define PCI_PWR_CAP_BUDGET(x) ((x) & 1) /* Included in system budget */
+
+/* Root Complex Link */
+#define PCI_RCLINK_ESD 4 /* Element Self Description */
+#define PCI_RCLINK_LINK1 16 /* First Link Entry */
+#define PCI_RCLINK_LINK_DESC 0 /* Link Entry: Description */
+#define PCI_RCLINK_LINK_ADDR 8 /* Link Entry: Address (64-bit) */
+#define PCI_RCLINK_LINK_SIZE 16 /* Link Entry: sizeof */
+
+/* Root Complex Event Collector Endpoint Association */
+#define PCI_RCEC_EP_CAP_VER(reg) (((reg) >> 16) & 0xf)
+#define PCI_RCEC_BUSN_REG_VER 0x02 /* as per PCIe sec 7.9.10.1 */
+#define PCI_RCEC_RCIEP_BMAP 0x0004 /* as per PCIe sec 7.9.10.2 */
+#define PCI_RCEC_BUSN_REG 0x0008 /* as per PCIe sec 7.9.10.3 */
+
+/* PCIe Vendor-Specific Capability */
+#define PCI_EVNDR_HEADER 4 /* Vendor-Specific Header */
+#define PCI_EVNDR_REGISTERS 8 /* Vendor-Specific Registers */
+
+/* PCIe Designated Vendor-Specific Capability */
+#define PCI_DVSEC_HEADER1 4 /* Designated Vendor-Specific Header 1 */
+#define PCI_DVSEC_HEADER2 8 /* Designated Vendor-Specific Header 2 */
+#define PCI_DVSEC_VENDOR_ID_CXL 0x1e98 /* Designated Vendor-Specific Vendor ID for CXL */
+#define PCI_DVSEC_ID_CXL 0 /* Designated Vendor-Specific ID for Intel CXL */
+
+/* PCIe CXL Designated Vendor-Specific Capabilities for Devices: Control, Status */
+#define PCI_CXL_DEV_LEN 0x38 /* CXL Device DVSEC Length for Rev1 */
+#define PCI_CXL_DEV_LEN_REV2 0x3c /* ... for Rev2 */
+#define PCI_CXL_DEV_CAP 0x0a /* CXL Capability Register */
+#define PCI_CXL_DEV_CAP_CACHE 0x0001 /* CXL.cache Protocol Support */
+#define PCI_CXL_DEV_CAP_IO 0x0002 /* CXL.io Protocol Support */
+#define PCI_CXL_DEV_CAP_MEM 0x0004 /* CXL.mem Protocol Support */
+#define PCI_CXL_DEV_CAP_MEM_HWINIT 0x0008 /* CXL.mem Initializes with HW/FW Support */
+#define PCI_CXL_DEV_CAP_HDM_CNT(x) (((x) & (3 << 4)) >> 4) /* CXL Number of HDM ranges */
+#define PCI_CXL_DEV_CAP_VIRAL 0x4000 /* CXL Viral Handling Support */
+#define PCI_CXL_DEV_CTRL 0x0c /* CXL Control Register */
+#define PCI_CXL_DEV_CTRL_CACHE 0x0001 /* CXL.cache Protocol Enable */
+#define PCI_CXL_DEV_CTRL_IO 0x0002 /* CXL.io Protocol Enable */
+#define PCI_CXL_DEV_CTRL_MEM 0x0004 /* CXL.mem Protocol Enable */
+#define PCI_CXL_DEV_CTRL_CACHE_SF_COV(x) (((x) & (0x1f << 3)) >> 3) /* Snoop Filter Coverage */
+#define PCI_CXL_DEV_CTRL_CACHE_SF_GRAN(x) (((x) & (0x7 << 8)) >> 8) /* Snoop Filter Granularity */
+#define PCI_CXL_DEV_CTRL_CACHE_CLN 0x0800 /* CXL.cache Performance Hint on Clean Evictions */
+#define PCI_CXL_DEV_CTRL_VIRAL 0x4000 /* CXL Viral Handling Enable */
+#define PCI_CXL_DEV_STATUS 0x0e /* CXL Status Register */
+#define PCI_CXL_DEV_STATUS_VIRAL 0x4000 /* CXL Viral Handling Status */
+#define PCI_CXL_DEV_CTRL2 0x10 /* CXL Control Register 2 */
+#define PCI_CXL_DEV_CTRL2_DISABLE_CACHING 0x0001
+#define PCI_CXL_DEV_CTRL2_INIT_WB_INVAL 0x0002
+#define PCI_CXL_DEV_CTRL2_INIT_CXL_RST 0x0003
+#define PCI_CXL_DEV_CTRL2_INIT_CXL_RST_CLR_EN 0x0004
+#define PCI_CXL_DEV_CTRL2_INIT_CXL_HDM_STATE_HOTRST 0x0005
+#define PCI_CXL_DEV_STATUS2 0x12
+#define PCI_CXL_DEV_STATUS_CACHE_INV 0x0001
+#define PCI_CXL_DEV_STATUS_RC 0x0002 /* Device Reset Complete */
+#define PCI_CXL_DEV_STATUS_RE 0x0004 /* Device Reset Error */
+#define PCI_CXL_DEV_STATUS_PMC 0x8000 /* Power Management Init Complete */
+#define PCI_CXL_DEV_CAP2 0x16
+#define PCI_CXL_DEV_CAP2_CACHE_UNK 0x0000 /* Cache Size Isn't Reported */
+#define PCI_CXL_DEV_CAP2_CACHE_64K 0x0001 /* Unit Size 64K */
+#define PCI_CXL_DEV_CAP2_CACHE_1M 0x0002 /* Unit Size 1M */
+#define PCI_CXL_DEV_RANGE1_SIZE_HI 0x18
+#define PCI_CXL_DEV_RANGE1_SIZE_LO 0x1c
+#define PCI_CXL_RANGE_VALID 0x0001
+#define PCI_CXL_RANGE_ACTIVE 0x0002
+#define PCI_CXL_RANGE_TYPE(x) (((x) >> 2) & 0x7)
+#define PCI_CXL_RANGE_CLASS(x) (((x) >> 5) & 0x7)
+#define PCI_CXL_RANGE_INTERLEAVE(x) (((x) >> 8) & 0x1f)
+#define PCI_CXL_RANGE_TIMEOUT(x) (((x) >> 13) & 0x7)
+#define PCI_CXL_DEV_RANGE1_BASE_HI 0x20
+#define PCI_CXL_DEV_RANGE1_BASE_LO 0x24
+#define PCI_CXL_DEV_RANGE2_SIZE_HI 0x28
+#define PCI_CXL_DEV_RANGE2_SIZE_LO 0x2c
+#define PCI_CXL_DEV_RANGE2_BASE_HI 0x30
+#define PCI_CXL_DEV_RANGE2_BASE_LO 0x34
+/* From Rev2 */
+#define PCI_CXL_DEV_CAP3 0x38
+#define PCI_CXL_DEV_CAP3_HDM_STATE_RST_COLD 0x0001
+#define PCI_CXL_DEV_CAP3_HDM_STATE_RST_WARM 0x0002
+#define PCI_CXL_DEV_CAP3_HDM_STATE_RST_HOT 0x0003
+#define PCI_CXL_DEV_CAP3_HDM_STATE_RST_HOT_CFG 0x0004
+
+
+/* PCIe CXL 2.0 Designated Vendor-Specific Capabilities for Ports */
+#define PCI_CXL_PORT_EXT_LEN 0x28 /* CXL Extensions DVSEC for Ports Length */
+#define PCI_CXL_PORT_EXT_STATUS 0x0a /* Port Extension Status */
+#define PCI_CXL_PORT_PM_INIT_COMPLETE 0x1 /* Port Power Management Initialization Complete */
+#define PCI_CXL_PORT_CTRL 0x0c /* Port Control Override */
+#define PCI_CXL_PORT_UNMASK_SBR 0x0001 /* Unmask SBR */
+#define PCI_CXL_PORT_UNMASK_LINK 0x0002 /* Unmask Link Disable */
+#define PCI_CXL_PORT_ALT_MEMORY 0x0004 /* Alt Memory and ID Space Enable */
+#define PCI_CXL_PORT_ALT_BME 0x0008 /* Alt BME */
+#define PCI_CXL_PORT_VIRAL_EN 0x4000 /* Viral Enable */
+#define PCI_CXL_PORT_ALT_BUS_BASE 0xe
+#define PCI_CXL_PORT_ALT_BUS_LIMIT 0xf
+#define PCI_CXL_PORT_ALT_MEM_BASE 0x10
+#define PCI_CXL_PORT_ALT_MEM_LIMIT 0x12
+
+/* PCIe CXL 2.0 Designated Vendor-Specific Capabilities for Register Locator */
+#define PCI_CXL_RL_BLOCK1_LO 0x0c
+
+/* PCIe CXL Designated Vendor-Specific Capabilities for Global Persistent Flush */
+#define PCI_CXL_GPF_DEV_LEN 0x10
+#define PCI_CXL_GPF_DEV_PHASE2_DUR 0x0a /* GPF Phase 2 Duration Register */
+#define PCI_CXL_GPF_DEV_PHASE2_POW 0x0c /* GPF Phase 2 Power Register */
+#define PCI_CXL_GPF_DEV_1US 0x0
+#define PCI_CXL_GPF_DEV_10US 0x1
+#define PCI_CXL_GPF_DEV_100US 0x2
+#define PCI_CXL_GPF_DEV_1MS 0x3
+#define PCI_CXL_GPF_DEV_10MS 0x4
+#define PCI_CXL_GPF_DEV_100MS 0x5
+#define PCI_CXL_GPF_DEV_1S 0x6
+#define PCI_CXL_GPF_DEV_10S 0x7
+#define PCI_CXL_GPF_PORT_LEN 0x10
+#define PCI_CXL_GPF_PORT_PHASE1_CTRL 0x0c /* GPF Phase 1 Control Register */
+#define PCI_CXL_GPF_PORT_PHASE2_CTRL 0x0e /* GPF Phase 2 Control Register */
+#define PCI_CXL_GPF_PORT_1US 0x0
+#define PCI_CXL_GPF_PORT_10US 0x1
+#define PCI_CXL_GPF_PORT_100US 0x2
+#define PCI_CXL_GPF_PORT_1MS 0x3
+#define PCI_CXL_GPF_PORT_10MS 0x4
+#define PCI_CXL_GPF_PORT_100MS 0x5
+#define PCI_CXL_GPF_PORT_1S 0x6
+#define PCI_CXL_GPF_PORT_10S 0x7
+
+/* PCIe CXL Designated Vendor-Specific Capabilities for Flex Bus Port */
+#define PCI_CXL_FB_LEN 0x20
+#define PCI_CXL_FB_PORT_CAP 0x0a /* CXL Flex Bus Port Capability Register */
+#define PCI_CXL_FB_CAP_CACHE 0x0001 /* CXL.cache Capable */
+#define PCI_CXL_FB_CAP_IO 0x0002 /* CXL.io Capable */
+#define PCI_CXL_FB_CAP_MEM 0x0004 /* CXL.mem Capable */
+#define PCI_CXL_FB_CAP_68B_FLIT 0x0020 /* CXL 68B Flit and VH Capable */
+#define PCI_CXL_FB_CAP_MULT_LOG_DEV 0x0040 /* CXL Multi-Logical Device Capable */
+#define PCI_CXL_FB_CAP_256B_FLIT 0x2000 /* CXL Latency Optimized 256B Flit Capable */
+#define PCI_CXL_FB_CAP_PBR_FLIT 0x4000 /* CXL PBR Flit Capable */
+#define PCI_CXL_FB_PORT_CTRL 0x0c /* CXL Flex Bus Port Control Register */
+#define PCI_CXL_FB_CTRL_CACHE 0x0001 /* CXL.cache Enable */
+#define PCI_CXL_FB_CTRL_IO 0x0002 /* CXL.io Enable */
+#define PCI_CXL_FB_CTRL_MEM 0x0004 /* CXL.mem Enable */
+#define PCI_CXL_FB_CTRL_SYNC_HDR_BYP 0x0008 /* CXL Sync Header Bypass Enable */
+#define PCI_CXL_FB_CTRL_DRFT_BUF 0x0010 /* Drift Buffer Enable */
+#define PCI_CXL_FB_CTRL_68B_FLIT 0x0020 /* CXL 68B Flit and VH Enable */
+#define PCI_CXL_FB_CTRL_MULT_LOG_DEV 0x0040 /* CXL Multi Logical Device Enable */
+#define PCI_CXL_FB_CTRL_RCD 0x0080 /* Disable RCD Training */
+#define PCI_CXL_FB_CTRL_RETIMER1 0x0100 /* Retimer1 Present */
+#define PCI_CXL_FB_CTRL_RETIMER2 0x0200 /* Retimer2 Present */
+#define PCI_CXL_FB_CTRL_256B_FLIT 0x2000 /* CXL Latency Optimized 256B Flit Enable */
+#define PCI_CXL_FB_CTRL_PBR_FLIT 0x4000 /* CXL PBR Flit Enable */
+#define PCI_CXL_FB_PORT_STATUS 0x0e /* CXL Flex Bus Port Status Register */
+#define PCI_CXL_FB_STAT_CACHE 0x0001 /* CXL.cache Enabled */
+#define PCI_CXL_FB_STAT_IO 0x0002 /* CXL.io Enabled */
+#define PCI_CXL_FB_STAT_MEM 0x0004 /* CXL.mem Enabled */
+#define PCI_CXL_FB_STAT_SYNC_HDR_BYP 0x0008 /* CXL Sync Header Bypass Enabled */
+#define PCI_CXL_FB_STAT_DRFT_BUF 0x0010 /* Drift Buffer Enabled */
+#define PCI_CXL_FB_STAT_68B_FLIT 0x0020 /* CXL 68B Flit and VH Enabled */
+#define PCI_CXL_FB_STAT_MULT_LOG_DEV 0x0040 /* CXL Multi Logical Device Enabled */
+#define PCI_CXL_FB_STAT_256B_FLIT 0x2000 /* CXL Latency Optimized 256B Flit Enabled */
+#define PCI_CXL_FB_STAT_PBR_FLIT 0x4000 /* CXL PBR Flit Enabled */
+#define PCI_CXL_FB_MOD_TS_DATA 0x10 /* CXL Flex Bus Port Received Modified TS Data Phase1 Register */
+#define PCI_CXL_FB_PORT_CAP2 0x14 /* CXL Flex Bus Port Capability2 Register */
+#define PCI_CXL_FB_CAP2_NOP_HINT 0x01 /* NOP Hint Capable */
+#define PCI_CXL_FB_PORT_CTRL2 0x18 /* CXL Flex Bus Port Control2 Register */
+#define PCI_CXL_FB_CTRL2_NOP_HINT 0x01 /* NOP Hint Enable */
+#define PCI_CXL_FB_PORT_STATUS2 0x1c /* CXL Flex Bus Port Status2 Register */
+#define PCI_CXL_FB_NEXT_UNSUPPORTED 0x20
+
+/* PCIe CXL Designated Vendor-Specific Capabilities for Multi-Logical Device */
+#define PCI_CXL_MLD_LEN 0x10
+#define PCI_CXL_MLD_NUM_LD 0xa
+#define PCI_CXL_MLD_MAX_LD 0x10
+
+/* PCIe CXL Designated Vendor-Specific Capabilities for Non-CXL Function Map */
+#define PCI_CXL_FUN_MAP_LEN 0x2c
+#define PCI_CXL_FUN_MAP_REG_0 0x0c
+#define PCI_CXL_FUN_MAP_REG_1 0x10
+#define PCI_CXL_FUN_MAP_REG_2 0x14
+#define PCI_CXL_FUN_MAP_REG_3 0x18
+#define PCI_CXL_FUN_MAP_REG_4 0x1c
+#define PCI_CXL_FUN_MAP_REG_5 0x20
+#define PCI_CXL_FUN_MAP_REG_6 0x24
+#define PCI_CXL_FUN_MAP_REG_7 0x28
+
+/* Access Control Services */
+#define PCI_ACS_CAP 0x04 /* ACS Capability Register */
+#define PCI_ACS_CAP_VALID 0x0001 /* ACS Source Validation */
+#define PCI_ACS_CAP_BLOCK 0x0002 /* ACS Translation Blocking */
+#define PCI_ACS_CAP_REQ_RED 0x0004 /* ACS P2P Request Redirect */
+#define PCI_ACS_CAP_CMPLT_RED 0x0008 /* ACS P2P Completion Redirect */
+#define PCI_ACS_CAP_FORWARD 0x0010 /* ACS Upstream Forwarding */
+#define PCI_ACS_CAP_EGRESS 0x0020 /* ACS P2P Egress Control */
+#define PCI_ACS_CAP_TRANS 0x0040 /* ACS Direct Translated P2P */
+#define PCI_ACS_CAP_VECTOR(x) (((x) >> 8) & 0xff) /* Egress Control Vector Size */
+#define PCI_ACS_CTRL 0x06 /* ACS Control Register */
+#define PCI_ACS_CTRL_VALID 0x0001 /* ACS Source Validation Enable */
+#define PCI_ACS_CTRL_BLOCK 0x0002 /* ACS Translation Blocking Enable */
+#define PCI_ACS_CTRL_REQ_RED 0x0004 /* ACS P2P Request Redirect Enable */
+#define PCI_ACS_CTRL_CMPLT_RED 0x0008 /* ACS P2P Completion Redirect Enable */
+#define PCI_ACS_CTRL_FORWARD 0x0010 /* ACS Upstream Forwarding Enable */
+#define PCI_ACS_CTRL_EGRESS 0x0020 /* ACS P2P Egress Control Enable */
+#define PCI_ACS_CTRL_TRANS 0x0040 /* ACS Direct Translated P2P Enable */
+#define PCI_ACS_EGRESS_CTRL 0x08 /* Egress Control Vector */
+
+/* Alternative Routing-ID Interpretation */
+#define PCI_ARI_CAP 0x04 /* ARI Capability Register */
+#define PCI_ARI_CAP_MFVC 0x0001 /* MFVC Function Groups Capability */
+#define PCI_ARI_CAP_ACS 0x0002 /* ACS Function Groups Capability */
+#define PCI_ARI_CAP_NFN(x) (((x) >> 8) & 0xff) /* Next Function Number */
+#define PCI_ARI_CTRL 0x06 /* ARI Control Register */
+#define PCI_ARI_CTRL_MFVC 0x0001 /* MFVC Function Groups Enable */
+#define PCI_ARI_CTRL_ACS 0x0002 /* ACS Function Groups Enable */
+#define PCI_ARI_CTRL_FG(x) (((x) >> 4) & 7) /* Function Group */
+
+/* Address Translation Service */
+#define PCI_ATS_CAP 0x04 /* ATS Capability Register */
+#define PCI_ATS_CAP_IQD(x) ((x) & 0x1f) /* Invalidate Queue Depth */
+#define PCI_ATS_CTRL 0x06 /* ATS Control Register */
+#define PCI_ATS_CTRL_STU(x) ((x) & 0x1f) /* Smallest Translation Unit */
+#define PCI_ATS_CTRL_ENABLE 0x8000 /* ATS Enable */
+
+/* Single Root I/O Virtualization */
+#define PCI_IOV_CAP 0x04 /* SR-IOV Capability Register */
+#define PCI_IOV_CAP_VFM 0x00000001 /* VF Migration Capable */
+#define PCI_IOV_CAP_VF_10BIT_TAG_REQ 0x00000004 /* VF 10-Bit Tag Requester Supported */
+#define PCI_IOV_CAP_IMN(x) ((x) >> 21) /* VF Migration Interrupt Message Number */
+#define PCI_IOV_CTRL 0x08 /* SR-IOV Control Register */
+#define PCI_IOV_CTRL_VFE 0x0001 /* VF Enable */
+#define PCI_IOV_CTRL_VFME 0x0002 /* VF Migration Enable */
+#define PCI_IOV_CTRL_VFMIE 0x0004 /* VF Migration Interrupt Enable */
+#define PCI_IOV_CTRL_MSE 0x0008 /* VF MSE */
+#define PCI_IOV_CTRL_ARI 0x0010 /* ARI Capable Hierarchy */
+#define PCI_IOV_CTRL_VF_10BIT_TAG_REQ_EN 0x0020 /* VF 10-Bit Tag Requester Enable */
+#define PCI_IOV_STATUS 0x0a /* SR-IOV Status Register */
+#define PCI_IOV_STATUS_MS 0x0001 /* VF Migration Status */
+#define PCI_IOV_INITIALVF 0x0c /* Number of VFs that are initially associated */
+#define PCI_IOV_TOTALVF 0x0e /* Maximum number of VFs that could be associated */
+#define PCI_IOV_NUMVF 0x10 /* Number of VFs that are available */
+#define PCI_IOV_FDL 0x12 /* Function Dependency Link */
+#define PCI_IOV_OFFSET 0x14 /* First VF Offset */
+#define PCI_IOV_STRIDE 0x16 /* Routing ID offset from one VF to the next one */
+#define PCI_IOV_DID 0x1a /* VF Device ID */
+#define PCI_IOV_SUPPS 0x1c /* Supported Page Sizes */
+#define PCI_IOV_SYSPS 0x20 /* System Page Size */
+#define PCI_IOV_BAR_BASE 0x24 /* VF BAR0, VF BAR1, ... VF BAR5 */
+#define PCI_IOV_NUM_BAR 6 /* Number of VF BARs */
+#define PCI_IOV_MSAO 0x3c /* VF Migration State Array Offset */
+#define PCI_IOV_MSA_BIR(x) ((x) & 7) /* VF Migration State BIR */
+#define PCI_IOV_MSA_OFFSET(x) ((x) & 0xfffffff8) /* VF Migration State Offset */
+
+/* Multicast */
+#define PCI_MCAST_CAP 0x04 /* Multicast Capability */
+#define PCI_MCAST_CAP_MAX_GROUP(x) ((x) & 0x3f)
+#define PCI_MCAST_CAP_WIN_SIZE(x) (((x) >> 8) & 0x3f)
+#define PCI_MCAST_CAP_ECRC 0x8000 /* ECRC Regeneration Supported */
+#define PCI_MCAST_CTRL 0x06 /* Multicast Control */
+#define PCI_MCAST_CTRL_NUM_GROUP(x) ((x) & 0x3f)
+#define PCI_MCAST_CTRL_ENABLE 0x8000 /* MC Enabled */
+#define PCI_MCAST_BAR 0x08 /* Base Address */
+#define PCI_MCAST_BAR_INDEX_POS(x) ((u32) ((x) & 0x3f))
+#define PCI_MCAST_BAR_MASK (~0xfffUL)
+#define PCI_MCAST_RCV 0x10 /* Receive */
+#define PCI_MCAST_BLOCK 0x18 /* Block All */
+#define PCI_MCAST_BLOCK_UNTRANS 0x20 /* Block Untranslated */
+#define PCI_MCAST_OVL_BAR 0x28 /* Overlay BAR */
+#define PCI_MCAST_OVL_SIZE(x) ((u32) ((x) & 0x3f))
+#define PCI_MCAST_OVL_MASK (~0x3fUL)
+
+/* Page Request Interface */
+#define PCI_PRI_CTRL 0x04 /* PRI Control Register */
+#define PCI_PRI_CTRL_ENABLE 0x01 /* Enable */
+#define PCI_PRI_CTRL_RESET 0x02 /* Reset */
+#define PCI_PRI_STATUS 0x06 /* PRI status register */
+#define PCI_PRI_STATUS_RF 0x0001 /* Response Failure */
+#define PCI_PRI_STATUS_UPRGI 0x0002 /* Unexpected PRG index */
+#define PCI_PRI_STATUS_STOPPED 0x0100 /* PRI Stopped */
+#define PCI_PRI_STATUS_PASID 0x8000 /* PASID required in PRG response */
+#define PCI_PRI_MAX_REQ 0x08 /* PRI max reqs supported */
+#define PCI_PRI_ALLOC_REQ 0x0c /* PRI max reqs allowed */
+
+/* Transaction Processing Hints */
+#define PCI_TPH_CAPABILITIES 4
+#define PCI_TPH_INTVEC_SUP (1<<1) /* Supports interrupt vector mode */
+#define PCI_TPH_DEV_SUP (1<<2) /* Device specific mode supported */
+#define PCI_TPH_EXT_REQ_SUP (1<<8) /* Supports extended requests */
+#define PCI_TPH_ST_LOC_MASK (3<<9) /* Steering table location bits */
+#define PCI_TPH_ST_NONE (0<<9) /* No steering table */
+#define PCI_TPH_ST_CAP (1<<9) /* Steering table in TPH cap */
+#define PCI_TPH_ST_MSIX (2<<9) /* Steering table in MSI-X table */
+#define PCI_TPH_ST_SIZE_SHIFT (16) /* Encoded as size - 1 */
+
+/* Latency Tolerance Reporting */
+#define PCI_LTR_MAX_SNOOP 4 /* 16 bit value */
+#define PCI_LTR_VALUE_MASK (0x3ff)
+#define PCI_LTR_SCALE_SHIFT (10)
+#define PCI_LTR_SCALE_MASK (7)
+#define PCI_LTR_MAX_NOSNOOP 6 /* 16 bit value */
+
+/* Secondary PCI Express Extended Capability */
+#define PCI_SEC_LNKCTL3 4 /* Link Control 3 register */
+#define PCI_SEC_LNKCTL3_PERFORM_LINK_EQU 0x01
+#define PCI_SEC_LNKCTL3_LNK_EQU_REQ_INTR_EN 0x02
+#define PCI_SEC_LNKCTL3_ENBL_LOWER_SKP_OS_GEN_VEC(x) ((x >> 8) & 0x7F)
+#define PCI_SEC_LANE_ERR 8 /* Lane Error status register */
+#define PCI_SEC_LANE_EQU_CTRL 12 /* Lane Equalization control register */
+
+/* Process Address Space ID */
+#define PCI_PASID_CAP 0x04 /* PASID feature register */
+#define PCI_PASID_CAP_EXEC 0x02 /* Exec permissions Supported */
+#define PCI_PASID_CAP_PRIV 0x04 /* Privilege Mode Supported */
+#define PCI_PASID_CAP_WIDTH(x) (((x) >> 8) & 0x1f) /* Max PASID Width */
+#define PCI_PASID_CTRL 0x06 /* PASID control register */
+#define PCI_PASID_CTRL_ENABLE 0x01 /* Enable bit */
+#define PCI_PASID_CTRL_EXEC 0x02 /* Exec permissions Enable */
+#define PCI_PASID_CTRL_PRIV 0x04 /* Privilege Mode Enable */
+
+#define PCI_DPC_CAP 4 /* DPC Capability */
+#define PCI_DPC_CAP_INT_MSG(x) ((x) & 0x1f) /* DPC Interrupt Message Number */
+#define PCI_DPC_CAP_RP_EXT 0x20 /* DPC Root Port Extensions */
+#define PCI_DPC_CAP_TLP_BLOCK 0x40 /* DPC Poisoned TLP Egress Blocking */
+#define PCI_DPC_CAP_SW_TRIGGER 0x80 /* DPC Software Trigger */
+#define PCI_DPC_CAP_RP_LOG(x) (((x) >> 8) & 0xf) /* DPC RP PIO Log Size */
+#define PCI_DPC_CAP_DL_ACT_ERR 0x1000 /* DPC DL_Active ERR_COR Signal */
+#define PCI_DPC_CTL 6 /* DPC Control */
+#define PCI_DPC_CTL_TRIGGER(x) ((x) & 0x3) /* DPC Trigger Enable */
+#define PCI_DPC_CTL_CMPL 0x4 /* DPC Completion Control */
+#define PCI_DPC_CTL_INT 0x8 /* DPC Interrupt Enabled */
+#define PCI_DPC_CTL_ERR_COR 0x10 /* DPC ERR_COR Enabled */
+#define PCI_DPC_CTL_TLP 0x20 /* DPC Poisoned TLP Egress Blocking Enabled */
+#define PCI_DPC_CTL_SW_TRIGGER 0x40 /* DPC Software Trigger */
+#define PCI_DPC_CTL_DL_ACTIVE 0x80 /* DPC DL_Active ERR_COR Enable */
+#define PCI_DPC_STATUS 8 /* DPC STATUS */
+#define PCI_DPC_STS_TRIGGER 0x01 /* DPC Trigger Status */
+#define PCI_DPC_STS_REASON(x) (((x) >> 1) & 0x3) /* DPC Trigger Reason */
+#define PCI_DPC_STS_INT 0x08 /* DPC Interrupt Status */
+#define PCI_DPC_STS_RP_BUSY 0x10 /* DPC Root Port Busy */
+#define PCI_DPC_STS_TRIGGER_EXT(x) (((x) >> 5) & 0x3) /* Trigger Reason Extension */
+#define PCI_DPC_STS_PIO_FEP(x) (((x) >> 8) & 0x1f) /* DPC PIO First Error Pointer */
+#define PCI_DPC_SOURCE 10 /* DPC Source ID */
+
+/* L1 PM Substates Extended Capability */
+#define PCI_L1PM_SUBSTAT_CAP 0x4 /* L1 PM Substate Capability */
+#define PCI_L1PM_SUBSTAT_CAP_PM_L12 0x1 /* PCI-PM L1.2 Supported */
+#define PCI_L1PM_SUBSTAT_CAP_PM_L11 0x2 /* PCI-PM L1.1 Supported */
+#define PCI_L1PM_SUBSTAT_CAP_ASPM_L12 0x4 /* ASPM L1.2 Supported */
+#define PCI_L1PM_SUBSTAT_CAP_ASPM_L11 0x8 /* ASPM L1.1 Supported */
+#define PCI_L1PM_SUBSTAT_CAP_L1PM_SUPP 0x10 /* L1 PM Substates supported */
+#define PCI_L1PM_SUBSTAT_CTL1 0x8 /* L1 PM Substate Control 1 */
+#define PCI_L1PM_SUBSTAT_CTL1_PM_L12 0x1 /* PCI-PM L1.2 Enable */
+#define PCI_L1PM_SUBSTAT_CTL1_PM_L11 0x2 /* PCI-PM L1.1 Enable */
+#define PCI_L1PM_SUBSTAT_CTL1_ASPM_L12 0x4 /* ASPM L1.2 Enable */
+#define PCI_L1PM_SUBSTAT_CTL1_ASPM_L11 0x8 /* ASPM L1.1 Enable */
+#define PCI_L1PM_SUBSTAT_CTL2 0xC /* L1 PM Substate Control 2 */
+
+/* Data Object Exchange Extended Capability */
+#define PCI_DOE_CAP 0x4 /* DOE Capabilities Register */
+#define PCI_DOE_CAP_INT_SUPP 0x1 /* Interrupt Support */
+#define PCI_DOE_CAP_INT_MSG(x) (((x) >> 1) & 0x7ff) /* DOE Interrupt Message Number */
+#define PCI_DOE_CTL 0x8 /* DOE Control Register */
+#define PCI_DOE_CTL_ABORT 0x1 /* DOE Abort */
+#define PCI_DOE_CTL_INT 0x2 /* DOE Interrupt Enable */
+#define PCI_DOE_CTL_GO 0x80000000 /* DOE Go */
+#define PCI_DOE_STS 0xC /* DOE Status Register */
+#define PCI_DOE_STS_BUSY 0x1 /* DOE Busy */
+#define PCI_DOE_STS_INT 0x2 /* DOE Interrupt Status */
+#define PCI_DOE_STS_ERROR 0x4 /* DOE Error */
+#define PCI_DOE_STS_OBJECT_READY 0x80000000 /* Data Object Ready */
+
+/* Lane Margining at the Receiver Extended Capability */
+#define PCI_LMR_CAPS 0x4 /* Margining Port Capabilities Register */
+#define PCI_LMR_CAPS_DRVR 0x1 /* Margining uses Driver Software */
+#define PCI_LMR_PORT_STS 0x6 /* Margining Port Status Register */
+#define PCI_LMR_PORT_STS_READY 0x1 /* Margining Ready */
+#define PCI_LMR_PORT_STS_SOFT_READY 0x2 /* Margining Software Ready */
+
+/*
+ * The PCI interface treats multi-function devices as independent
+ * devices. The slot/function address of each device is encoded
+ * in a single byte as follows:
+ *
+ * 7:3 = slot
+ * 2:0 = function
+ */
+#define PCI_DEVFN(slot,func) ((((slot) & 0x1f) << 3) | ((func) & 0x07))
+#define PCI_SLOT(devfn) (((devfn) >> 3) & 0x1f)
+#define PCI_FUNC(devfn) ((devfn) & 0x07)
+
+/* Device classes and subclasses */
+
+#define PCI_CLASS_NOT_DEFINED 0x0000
+#define PCI_CLASS_NOT_DEFINED_VGA 0x0001
+
+#define PCI_BASE_CLASS_STORAGE 0x01
+#define PCI_CLASS_STORAGE_SCSI 0x0100
+#define PCI_CLASS_STORAGE_IDE 0x0101
+#define PCI_CLASS_STORAGE_FLOPPY 0x0102
+#define PCI_CLASS_STORAGE_IPI 0x0103
+#define PCI_CLASS_STORAGE_RAID 0x0104
+#define PCI_CLASS_STORAGE_ATA 0x0105
+#define PCI_CLASS_STORAGE_SATA 0x0106
+#define PCI_CLASS_STORAGE_SAS 0x0107
+#define PCI_CLASS_STORAGE_OTHER 0x0180
+
+#define PCI_BASE_CLASS_NETWORK 0x02
+#define PCI_CLASS_NETWORK_ETHERNET 0x0200
+#define PCI_CLASS_NETWORK_TOKEN_RING 0x0201
+#define PCI_CLASS_NETWORK_FDDI 0x0202
+#define PCI_CLASS_NETWORK_ATM 0x0203
+#define PCI_CLASS_NETWORK_ISDN 0x0204
+#define PCI_CLASS_NETWORK_OTHER 0x0280
+
+#define PCI_BASE_CLASS_DISPLAY 0x03
+#define PCI_CLASS_DISPLAY_VGA 0x0300
+#define PCI_CLASS_DISPLAY_XGA 0x0301
+#define PCI_CLASS_DISPLAY_3D 0x0302
+#define PCI_CLASS_DISPLAY_OTHER 0x0380
+
+#define PCI_BASE_CLASS_MULTIMEDIA 0x04
+#define PCI_CLASS_MULTIMEDIA_VIDEO 0x0400
+#define PCI_CLASS_MULTIMEDIA_AUDIO 0x0401
+#define PCI_CLASS_MULTIMEDIA_PHONE 0x0402
+#define PCI_CLASS_MULTIMEDIA_AUDIO_DEV 0x0403
+#define PCI_CLASS_MULTIMEDIA_OTHER 0x0480
+
+#define PCI_BASE_CLASS_MEMORY 0x05
+#define PCI_CLASS_MEMORY_RAM 0x0500
+#define PCI_CLASS_MEMORY_FLASH 0x0501
+#define PCI_CLASS_MEMORY_OTHER 0x0580
+
+#define PCI_BASE_CLASS_BRIDGE 0x06
+#define PCI_CLASS_BRIDGE_HOST 0x0600
+#define PCI_CLASS_BRIDGE_ISA 0x0601
+#define PCI_CLASS_BRIDGE_EISA 0x0602
+#define PCI_CLASS_BRIDGE_MC 0x0603
+#define PCI_CLASS_BRIDGE_PCI 0x0604
+#define PCI_CLASS_BRIDGE_PCMCIA 0x0605
+#define PCI_CLASS_BRIDGE_NUBUS 0x0606
+#define PCI_CLASS_BRIDGE_CARDBUS 0x0607
+#define PCI_CLASS_BRIDGE_RACEWAY 0x0608
+#define PCI_CLASS_BRIDGE_PCI_SEMI 0x0609
+#define PCI_CLASS_BRIDGE_IB_TO_PCI 0x060a
+#define PCI_CLASS_BRIDGE_OTHER 0x0680
+
+#define PCI_BASE_CLASS_COMMUNICATION 0x07
+#define PCI_CLASS_COMMUNICATION_SERIAL 0x0700
+#define PCI_CLASS_COMMUNICATION_PARALLEL 0x0701
+#define PCI_CLASS_COMMUNICATION_MSERIAL 0x0702
+#define PCI_CLASS_COMMUNICATION_MODEM 0x0703
+#define PCI_CLASS_COMMUNICATION_OTHER 0x0780
+
+#define PCI_BASE_CLASS_SYSTEM 0x08
+#define PCI_CLASS_SYSTEM_PIC 0x0800
+#define PCI_CLASS_SYSTEM_DMA 0x0801
+#define PCI_CLASS_SYSTEM_TIMER 0x0802
+#define PCI_CLASS_SYSTEM_RTC 0x0803
+#define PCI_CLASS_SYSTEM_PCI_HOTPLUG 0x0804
+#define PCI_CLASS_SYSTEM_OTHER 0x0880
+
+#define PCI_BASE_CLASS_INPUT 0x09
+#define PCI_CLASS_INPUT_KEYBOARD 0x0900
+#define PCI_CLASS_INPUT_PEN 0x0901
+#define PCI_CLASS_INPUT_MOUSE 0x0902
+#define PCI_CLASS_INPUT_SCANNER 0x0903
+#define PCI_CLASS_INPUT_GAMEPORT 0x0904
+#define PCI_CLASS_INPUT_OTHER 0x0980
+
+#define PCI_BASE_CLASS_DOCKING 0x0a
+#define PCI_CLASS_DOCKING_GENERIC 0x0a00
+#define PCI_CLASS_DOCKING_OTHER 0x0a80
+
+#define PCI_BASE_CLASS_PROCESSOR 0x0b
+#define PCI_CLASS_PROCESSOR_386 0x0b00
+#define PCI_CLASS_PROCESSOR_486 0x0b01
+#define PCI_CLASS_PROCESSOR_PENTIUM 0x0b02
+#define PCI_CLASS_PROCESSOR_ALPHA 0x0b10
+#define PCI_CLASS_PROCESSOR_POWERPC 0x0b20
+#define PCI_CLASS_PROCESSOR_MIPS 0x0b30
+#define PCI_CLASS_PROCESSOR_CO 0x0b40
+
+#define PCI_BASE_CLASS_SERIAL 0x0c
+#define PCI_CLASS_SERIAL_FIREWIRE 0x0c00
+#define PCI_CLASS_SERIAL_ACCESS 0x0c01
+#define PCI_CLASS_SERIAL_SSA 0x0c02
+#define PCI_CLASS_SERIAL_USB 0x0c03
+#define PCI_CLASS_SERIAL_FIBER 0x0c04
+#define PCI_CLASS_SERIAL_SMBUS 0x0c05
+#define PCI_CLASS_SERIAL_INFINIBAND 0x0c06
+
+#define PCI_BASE_CLASS_WIRELESS 0x0d
+#define PCI_CLASS_WIRELESS_IRDA 0x0d00
+#define PCI_CLASS_WIRELESS_CONSUMER_IR 0x0d01
+#define PCI_CLASS_WIRELESS_RF 0x0d10
+#define PCI_CLASS_WIRELESS_OTHER 0x0d80
+
+#define PCI_BASE_CLASS_INTELLIGENT 0x0e
+#define PCI_CLASS_INTELLIGENT_I2O 0x0e00
+
+#define PCI_BASE_CLASS_SATELLITE 0x0f
+#define PCI_CLASS_SATELLITE_TV 0x0f00
+#define PCI_CLASS_SATELLITE_AUDIO 0x0f01
+#define PCI_CLASS_SATELLITE_VOICE 0x0f03
+#define PCI_CLASS_SATELLITE_DATA 0x0f04
+
+#define PCI_BASE_CLASS_CRYPT 0x10
+#define PCI_CLASS_CRYPT_NETWORK 0x1000
+#define PCI_CLASS_CRYPT_ENTERTAINMENT 0x1010
+#define PCI_CLASS_CRYPT_OTHER 0x1080
+
+#define PCI_BASE_CLASS_SIGNAL 0x11
+#define PCI_CLASS_SIGNAL_DPIO 0x1100
+#define PCI_CLASS_SIGNAL_PERF_CTR 0x1101
+#define PCI_CLASS_SIGNAL_SYNCHRONIZER 0x1110
+#define PCI_CLASS_SIGNAL_OTHER 0x1180
+
+#define PCI_CLASS_OTHERS 0xff
+
+/* Several ID's we need in the library */
+
+#define PCI_VENDOR_ID_INTEL 0x8086
+#define PCI_VENDOR_ID_COMPAQ 0x0e11
+
+/* I/O resource flags, compatible with <include/linux/ioport.h> */
+
+#define PCI_IORESOURCE_TYPE_BITS 0x00001f00
+#define PCI_IORESOURCE_IO 0x00000100
+#define PCI_IORESOURCE_MEM 0x00000200
+#define PCI_IORESOURCE_PREFETCH 0x00002000
+#define PCI_IORESOURCE_MEM_64 0x00100000
+#define PCI_IORESOURCE_IO_16BIT_ADDR (1<<0)
+#define PCI_IORESOURCE_PCI_EA_BEI (1<<5)
diff --git a/lib/hurd.c b/lib/hurd.c
new file mode 100644
index 0000000..3e65fb8
--- /dev/null
+++ b/lib/hurd.c
@@ -0,0 +1,372 @@
+/*
+ * The PCI Library -- Hurd access via RPCs
+ *
+ * Copyright (c) 2017 Joan Lledó <jlledom@member.fsf.org>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#define _GNU_SOURCE
+
+#include "internal.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <string.h>
+#include <hurd.h>
+#include <hurd/pci.h>
+#include <hurd/paths.h>
+
+/* Server path */
+#define _SERVERS_BUS_PCI _SERVERS_BUS "/pci"
+
+/* File names */
+#define FILE_CONFIG_NAME "config"
+#define FILE_ROM_NAME "rom"
+
+/* Level in the fs tree */
+typedef enum
+{
+ LEVEL_NONE,
+ LEVEL_DOMAIN,
+ LEVEL_BUS,
+ LEVEL_DEV,
+ LEVEL_FUNC
+} tree_level;
+
+/* Check whether there's a pci server */
+static int
+hurd_detect(struct pci_access *a)
+{
+ int err;
+ struct stat st;
+
+ err = stat(_SERVERS_BUS_PCI, &st);
+ if (err)
+ {
+ a->error("Could not open file `%s'", _SERVERS_BUS_PCI);
+ return 0;
+ }
+
+ /* The node must be a directory and a translator */
+ return S_ISDIR(st.st_mode) && ((st.st_mode & S_ITRANS) == S_IROOT);
+}
+
+/* Empty callbacks, we don't need any special init or cleanup */
+static void
+hurd_init(struct pci_access *a UNUSED)
+{
+}
+
+static void
+hurd_cleanup(struct pci_access *a UNUSED)
+{
+}
+
+/* Each device has its own server path. Allocate space for the port. */
+static void
+hurd_init_dev(struct pci_dev *d)
+{
+ d->backend_data = pci_malloc(d->access, sizeof(mach_port_t));
+ *((mach_port_t *) d->backend_data) = MACH_PORT_NULL;
+}
+
+/* Deallocate the port and free its space */
+static void
+hurd_cleanup_dev(struct pci_dev *d)
+{
+ mach_port_t device_port;
+
+ device_port = *((mach_port_t *) d->backend_data);
+ mach_port_deallocate(mach_task_self(), device_port);
+
+ pci_mfree(d->backend_data);
+ d->backend_data = NULL;
+}
+
+static mach_port_t
+device_port_lookup(struct pci_dev *d)
+{
+ char server[NAME_MAX];
+ mach_port_t device_port = *((mach_port_t *) d->backend_data);
+
+ if (device_port != MACH_PORT_NULL)
+ return device_port;
+
+ snprintf(server, NAME_MAX, "%s/%04x/%02x/%02x/%01u/%s",
+ _SERVERS_BUS_PCI, d->domain, d->bus, d->dev, d->func,
+ FILE_CONFIG_NAME);
+ device_port = file_name_lookup(server, 0, 0);
+
+ if (device_port == MACH_PORT_NULL)
+ d->access->error("Cannot find the PCI arbiter");
+
+ *((mach_port_t *) d->backend_data) = device_port;
+ return device_port;
+}
+
+/* Walk through the FS tree to see what is allowed for us */
+static void
+enum_devices(const char *parent, struct pci_access *a, int domain, int bus,
+ int dev, int func, tree_level lev)
+{
+ int ret;
+ DIR *dir;
+ struct dirent *entry;
+ char path[NAME_MAX];
+ struct pci_dev *d;
+
+ dir = opendir(parent);
+ if (!dir)
+ {
+ if (errno == EPERM || errno == EACCES)
+ /* The client lacks the permissions to access this function, skip */
+ return;
+ else
+ a->error("Cannot open directory: %s (%s)", parent, strerror(errno));
+ }
+
+ while ((entry = readdir(dir)) != 0)
+ {
+ snprintf(path, NAME_MAX, "%s/%s", parent, entry->d_name);
+ if (entry->d_type == DT_DIR)
+ {
+ if (!strncmp(entry->d_name, ".", NAME_MAX)
+ || !strncmp(entry->d_name, "..", NAME_MAX))
+ continue;
+
+ errno = 0;
+ ret = strtol(entry->d_name, 0, 16);
+ if (errno)
+ {
+ if (closedir(dir) < 0)
+ a->warning("Cannot close directory: %s (%s)", parent,
+ strerror(errno));
+ a->error("Wrong directory name: %s (number expected) probably "
+ "not connected to an arbiter", entry->d_name);
+ }
+
+ /*
+ * We found a valid directory.
+ * Update the address and switch to the next level.
+ */
+ switch (lev)
+ {
+ case LEVEL_DOMAIN:
+ domain = ret;
+ break;
+ case LEVEL_BUS:
+ bus = ret;
+ break;
+ case LEVEL_DEV:
+ dev = ret;
+ break;
+ case LEVEL_FUNC:
+ func = ret;
+ break;
+ default:
+ if (closedir(dir) < 0)
+ a->warning("Cannot close directory: %s (%s)", parent,
+ strerror(errno));
+ a->error("Wrong directory tree, probably not connected to an arbiter");
+ }
+
+ enum_devices(path, a, domain, bus, dev, func, lev + 1);
+ }
+ else
+ {
+ if (strncmp(entry->d_name, FILE_CONFIG_NAME, NAME_MAX))
+ /* We are looking for the config file */
+ continue;
+
+ /* We found an available virtual device, add it to our list */
+ d = pci_alloc_dev(a);
+ d->domain = domain;
+ d->bus = bus;
+ d->dev = dev;
+ d->func = func;
+ pci_link_dev(a, d);
+ }
+ }
+
+ if (closedir(dir) < 0)
+ a->error("Cannot close directory: %s (%s)", parent, strerror(errno));
+}
+
+/* Enumerate devices */
+static void
+hurd_scan(struct pci_access *a)
+{
+ enum_devices(_SERVERS_BUS_PCI, a, -1, -1, -1, -1, LEVEL_DOMAIN);
+}
+
+/*
+ * Read `len' bytes to `buf'.
+ *
+ * Returns error when the number of read bytes does not match `len'.
+ */
+static int
+hurd_read(struct pci_dev *d, int pos, byte * buf, int len)
+{
+ int err;
+ size_t nread;
+ char *data;
+ mach_port_t device_port = device_port_lookup(d);
+
+ if (len > 4)
+ return pci_generic_block_read(d, pos, buf, len);
+
+ data = (char *) buf;
+ err = pci_conf_read(device_port, pos, &data, &nread, len);
+
+ if (data != (char *) buf)
+ {
+ if (nread > (size_t) len) /* Sanity check for bogus server. */
+ {
+ vm_deallocate(mach_task_self(), (vm_address_t) data, nread);
+ return 0;
+ }
+
+ memcpy(buf, data, nread);
+ vm_deallocate(mach_task_self(), (vm_address_t) data, nread);
+ }
+
+ return !err && nread == (size_t) len;
+}
+
+/*
+ * Write `len' bytes from `buf'.
+ *
+ * Returns error when the number of written bytes does not match `len'.
+ */
+static int
+hurd_write(struct pci_dev *d, int pos, byte * buf, int len)
+{
+ int err;
+ size_t nwrote;
+ mach_port_t device_port = device_port_lookup(d);
+
+ if (len > 4)
+ return pci_generic_block_write(d, pos, buf, len);
+
+ err = pci_conf_write(device_port, pos, (char *) buf, len, &nwrote);
+
+ return !err && nwrote == (size_t) len;
+}
+
+/* Get requested info from the server */
+
+static int
+hurd_fill_regions(struct pci_dev *d)
+{
+ mach_port_t device_port = device_port_lookup(d);
+ struct pci_bar regions[6];
+ char *buf = (char *) &regions;
+ size_t size = sizeof(regions);
+
+ int err = pci_get_dev_regions(device_port, &buf, &size);
+ if (err)
+ return 0;
+
+ if ((char *) &regions != buf)
+ {
+ /* Sanity check for bogus server. */
+ if (size > sizeof(regions))
+ {
+ vm_deallocate(mach_task_self(), (vm_address_t) buf, size);
+ return 0;
+ }
+
+ memcpy(&regions, buf, size);
+ vm_deallocate(mach_task_self(), (vm_address_t) buf, size);
+ }
+
+ for (int i = 0; i < 6; i++)
+ {
+ if (regions[i].size == 0)
+ continue;
+
+ d->base_addr[i] = regions[i].base_addr;
+ d->base_addr[i] |= regions[i].is_IO;
+ d->base_addr[i] |= regions[i].is_64 << 2;
+ d->base_addr[i] |= regions[i].is_prefetchable << 3;
+
+ d->size[i] = regions[i].size;
+ }
+
+ return 1;
+}
+
+static int
+hurd_fill_rom(struct pci_dev *d)
+{
+ struct pci_xrom_bar rom;
+ mach_port_t device_port = device_port_lookup(d);
+ char *buf = (char *) &rom;
+ size_t size = sizeof(rom);
+
+ int err = pci_get_dev_rom(device_port, &buf, &size);
+ if (err)
+ return 0;
+
+ if ((char *) &rom != buf)
+ {
+ /* Sanity check for bogus server. */
+ if (size > sizeof(rom))
+ {
+ vm_deallocate(mach_task_self(), (vm_address_t) buf, size);
+ return 0;
+ }
+
+ memcpy(&rom, buf, size);
+ vm_deallocate(mach_task_self(), (vm_address_t) buf, size);
+ }
+
+ d->rom_base_addr = rom.base_addr;
+ d->rom_size = rom.size;
+
+ return 1;
+}
+
+static void
+hurd_fill_info(struct pci_dev *d, unsigned int flags)
+{
+ if (!d->access->buscentric)
+ {
+ if (want_fill(d, flags, PCI_FILL_BASES | PCI_FILL_SIZES))
+ {
+ if (hurd_fill_regions(d))
+ clear_fill(d, PCI_FILL_BASES | PCI_FILL_SIZES);
+ }
+ if (want_fill(d, flags, PCI_FILL_ROM_BASE))
+ {
+ if (hurd_fill_rom(d))
+ clear_fill(d, PCI_FILL_ROM_BASE);
+ }
+ }
+
+ pci_generic_fill_info(d, flags);
+}
+
+struct pci_methods pm_hurd = {
+ "hurd",
+ "Hurd access using RPCs",
+ NULL, /* config */
+ hurd_detect,
+ hurd_init,
+ hurd_cleanup,
+ hurd_scan,
+ hurd_fill_info,
+ hurd_read,
+ hurd_write,
+ NULL, /* read_vpd */
+ hurd_init_dev,
+ hurd_cleanup_dev
+};
diff --git a/lib/i386-io-access.h b/lib/i386-io-access.h
new file mode 100644
index 0000000..8b1ad5f
--- /dev/null
+++ b/lib/i386-io-access.h
@@ -0,0 +1,75 @@
+/*
+ * The PCI Library -- Compiler-specific wrappers around x86 I/O port access instructions
+ *
+ * Copyright (c) 2023 Pali Rohár <pali@kernel.org>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#if defined(__GNUC__)
+
+static inline unsigned char
+intel_inb(unsigned short int port)
+{
+ unsigned char value;
+ asm volatile ("inb %w1, %0" : "=a" (value) : "Nd" (port));
+ return value;
+}
+
+static inline unsigned short int
+intel_inw(unsigned short int port)
+{
+ unsigned short value;
+ asm volatile ("inw %w1, %0" : "=a" (value) : "Nd" (port));
+ return value;
+}
+
+static inline unsigned int
+intel_inl(unsigned short int port)
+{
+ u32 value;
+ asm volatile ("inl %w1, %0" : "=a" (value) : "Nd" (port));
+ return value;
+}
+
+static inline void
+intel_outb(unsigned char value, unsigned short int port)
+{
+ asm volatile ("outb %b0, %w1" : : "a" (value), "Nd" (port));
+}
+
+static inline void
+intel_outw(unsigned short int value, unsigned short int port)
+{
+ asm volatile ("outw %w0, %w1" : : "a" (value), "Nd" (port));
+}
+
+static inline void
+intel_outl(u32 value, unsigned short int port)
+{
+ asm volatile ("outl %0, %w1" : : "a" (value), "Nd" (port));
+}
+
+#elif defined(_MSC_VER)
+
+#pragma intrinsic(_outp)
+#pragma intrinsic(_outpw)
+#pragma intrinsic(_outpd)
+#pragma intrinsic(_inp)
+#pragma intrinsic(_inpw)
+#pragma intrinsic(_inpd)
+
+#define intel_outb(x, y) _outp(y, x)
+#define intel_outw(x, y) _outpw(y, x)
+#define intel_outl(x, y) _outpd(y, x)
+#define intel_inb(x) _inp(x)
+#define intel_inw(x) _inpw(x)
+#define intel_inl(x) _inpd(x)
+
+#else
+
+#error Do not know how to access I/O ports on this compiler
+
+#endif
diff --git a/lib/i386-io-beos.h b/lib/i386-io-beos.h
new file mode 100644
index 0000000..dac0e4b
--- /dev/null
+++ b/lib/i386-io-beos.h
@@ -0,0 +1,68 @@
+/*
+ * The PCI Library -- Access to i386 I/O ports on BeOS
+ *
+ * Copyright (c) 2009 Francois Revol <revol@free.fr>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/* those are private syscalls */
+extern int read_isa_io(int pci_bus, void *addr, int size);
+extern int write_isa_io(int pci_bus, void *addr, int size, u32 value);
+
+static int
+intel_setup_io(struct pci_access *a UNUSED)
+{
+ return 1;
+}
+
+static inline void
+intel_cleanup_io(struct pci_access *a UNUSED)
+{
+}
+
+static inline u8
+intel_inb (u16 port)
+{
+ return (u8)read_isa_io(0, (void *)(u32)port, sizeof(u8));
+}
+
+static inline u16
+intel_inw (u16 port)
+{
+ return (u16)read_isa_io(0, (void *)(u32)port, sizeof(u16));
+}
+
+static inline u32
+intel_inl (u16 port)
+{
+ return (u32)read_isa_io(0, (void *)(u32)port, sizeof(u32));
+}
+
+static inline void
+intel_outb (u8 value, u16 port)
+{
+ write_isa_io(0, (void *)(u32)port, sizeof(value), value);
+}
+
+static inline void
+intel_outw (u16 value, u16 port)
+{
+ write_isa_io(0, (void *)(u32)port, sizeof(value), value);
+}
+
+static inline void
+intel_outl (u32 value, u16 port)
+{
+ write_isa_io(0, (void *)(u32)port, sizeof(value), value);
+}
+
+static inline void intel_io_lock(void)
+{
+}
+
+static inline void intel_io_unlock(void)
+{
+}
diff --git a/lib/i386-io-cygwin.h b/lib/i386-io-cygwin.h
new file mode 100644
index 0000000..4118057
--- /dev/null
+++ b/lib/i386-io-cygwin.h
@@ -0,0 +1,33 @@
+/*
+ * The PCI Library -- Access to i386 I/O ports under Windows with CYGWIN
+ *
+ * Copyright (c) 1997--2006 Martin Mares <mj@ucw.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <sys/io.h>
+
+#include "i386-io-access.h"
+
+static int
+intel_setup_io(struct pci_access *a UNUSED)
+{
+ return (iopl(3) < 0) ? 0 : 1;
+}
+
+static inline void
+intel_cleanup_io(struct pci_access *a UNUSED)
+{
+ iopl(0);
+}
+
+static inline void intel_io_lock(void)
+{
+}
+
+static inline void intel_io_unlock(void)
+{
+}
diff --git a/lib/i386-io-djgpp.h b/lib/i386-io-djgpp.h
new file mode 100644
index 0000000..1afb00e
--- /dev/null
+++ b/lib/i386-io-djgpp.h
@@ -0,0 +1,40 @@
+/*
+ * The PCI Library -- Access to i386 I/O ports on DJGPP
+ *
+ * Copyright (c) 2010, 2017 Rudolf Marek <r.marek@assembler.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <dos.h>
+
+#include "i386-io-access.h"
+
+static int irq_enabled;
+
+static int
+intel_setup_io(struct pci_access *a UNUSED)
+{
+ return 1;
+}
+
+static inline void
+intel_cleanup_io(struct pci_access *a UNUSED)
+{
+}
+
+static inline void intel_io_lock(void)
+{
+ asm volatile("" : : : "memory");
+ irq_enabled = disable();
+}
+
+static inline void intel_io_unlock(void)
+{
+ asm volatile("" : : : "memory");
+ if (irq_enabled) {
+ enable();
+ }
+}
diff --git a/lib/i386-io-haiku.h b/lib/i386-io-haiku.h
new file mode 100644
index 0000000..23843ea
--- /dev/null
+++ b/lib/i386-io-haiku.h
@@ -0,0 +1,142 @@
+/*
+ * The PCI Library -- Access to i386 I/O ports on Haiku
+ *
+ * Copyright (c) 2009 Francois Revol <revol@free.fr>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <Drivers.h>
+#include <ISA.h>
+#include <PCI.h>
+
+/* from haiku/trunk/headers/private/drivers/poke.h */
+
+#define POKE_DEVICE_NAME "poke"
+#define POKE_DEVICE_FULLNAME "/dev/misc/poke"
+#define POKE_SIGNATURE 'wltp' // "We Like To Poke"
+
+enum {
+ POKE_PORT_READ = B_DEVICE_OP_CODES_END + 1,
+ POKE_PORT_WRITE,
+ POKE_PORT_INDEXED_READ,
+ POKE_PORT_INDEXED_WRITE,
+ POKE_PCI_READ_CONFIG,
+ POKE_PCI_WRITE_CONFIG,
+ POKE_GET_NTH_PCI_INFO,
+ POKE_GET_PHYSICAL_ADDRESS,
+ POKE_MAP_MEMORY,
+ POKE_UNMAP_MEMORY
+};
+
+
+typedef struct {
+ uint32 signature;
+ uint8 index;
+ pci_info* info;
+ status_t status;
+} pci_info_args;
+
+
+typedef struct {
+ uint32 signature;
+ uint16 port;
+ uint8 size; // == index for POKE_PORT_INDEXED_*
+ uint32 value;
+} port_io_args;
+
+
+typedef struct {
+ uint32 signature;
+ uint8 bus;
+ uint8 device;
+ uint8 function;
+ uint8 size;
+ uint8 offset;
+ uint32 value;
+} pci_io_args;
+
+
+/* en poke.h*/
+
+static int poke_driver_fd;
+
+static int
+intel_setup_io(struct pci_access *a UNUSED)
+{
+ /*
+ * Opening poke device on systems with the linked change below
+ * automatically changes process IOPL to 3 and closing its file
+ * descriptor changes process IOPL back to 0, which give access
+ * to all x86 IO ports via x86 in/out instructions for this
+ * userspace process. To support also older systems without this
+ * change, access IO ports via ioctl() instead of x86 in/out.
+ * https://review.haiku-os.org/c/haiku/+/1077
+ */
+ poke_driver_fd = open(POKE_DEVICE_FULLNAME, O_RDWR);
+ return (poke_driver_fd < 0) ? 0 : 1;
+}
+
+static inline void
+intel_cleanup_io(struct pci_access *a UNUSED)
+{
+ close(poke_driver_fd);
+}
+
+static inline u8
+intel_inb (u16 port)
+{
+ port_io_args args = { POKE_SIGNATURE, port, sizeof(u8), 0 };
+ if (ioctl(poke_driver_fd, POKE_PORT_READ, &args, sizeof(args)) < 0)
+ return 0;
+ return (u8)args.value;
+}
+
+static inline u16
+intel_inw (u16 port)
+{
+ port_io_args args = { POKE_SIGNATURE, port, sizeof(u16), 0 };
+ if (ioctl(poke_driver_fd, POKE_PORT_READ, &args, sizeof(args)) < 0)
+ return 0;
+ return (u16)args.value;
+}
+
+static inline u32
+intel_inl (u16 port)
+{
+ port_io_args args = { POKE_SIGNATURE, port, sizeof(u32), 0 };
+ if (ioctl(poke_driver_fd, POKE_PORT_READ, &args, sizeof(args)) < 0)
+ return 0;
+ return (u32)args.value;
+}
+
+static inline void
+intel_outb (u8 value, u16 port)
+{
+ port_io_args args = { POKE_SIGNATURE, port, sizeof(u8), value };
+ ioctl(poke_driver_fd, POKE_PORT_WRITE, &args, sizeof(args));
+}
+
+static inline void
+intel_outw (u16 value, u16 port)
+{
+ port_io_args args = { POKE_SIGNATURE, port, sizeof(u16), value };
+ ioctl(poke_driver_fd, POKE_PORT_WRITE, &args, sizeof(args));
+}
+
+static inline void
+intel_outl (u32 value, u16 port)
+{
+ port_io_args args = { POKE_SIGNATURE, port, sizeof(u32), value };
+ ioctl(poke_driver_fd, POKE_PORT_WRITE, &args, sizeof(args));
+}
+
+static inline void intel_io_lock(void)
+{
+}
+
+static inline void intel_io_unlock(void)
+{
+}
diff --git a/lib/i386-io-hurd.h b/lib/i386-io-hurd.h
new file mode 100644
index 0000000..01d684e
--- /dev/null
+++ b/lib/i386-io-hurd.h
@@ -0,0 +1,37 @@
+/*
+ * The PCI Library -- Access to i386 I/O ports on GNU Hurd
+ *
+ * Copyright (c) 2003 Marco Gerards <metgerards@student.han.nl>
+ * Copyright (c) 2003 Martin Mares <mj@ucw.cz>
+ * Copyright (c) 2006 Samuel Thibault <samuel.thibault@ens-lyon.org> and
+ * Thomas Schwinge <tschwinge@gnu.org>
+ * Copyright (c) 2007 Thomas Schwinge <tschwinge@gnu.org>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <sys/io.h>
+
+#include "i386-io-access.h"
+
+static inline int
+intel_setup_io(struct pci_access *a UNUSED)
+{
+ return (ioperm (0, 65535, 1) == -1) ? 0 : 1;
+}
+
+static inline void
+intel_cleanup_io(struct pci_access *a UNUSED)
+{
+ ioperm (0, 65535, 0);
+}
+
+static inline void intel_io_lock(void)
+{
+}
+
+static inline void intel_io_unlock(void)
+{
+}
diff --git a/lib/i386-io-linux.h b/lib/i386-io-linux.h
new file mode 100644
index 0000000..317f079
--- /dev/null
+++ b/lib/i386-io-linux.h
@@ -0,0 +1,77 @@
+/*
+ * The PCI Library -- Access to i386 I/O ports on Linux
+ *
+ * Copyright (c) 1997--2006 Martin Mares <mj@ucw.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <sys/io.h>
+#include <errno.h>
+
+#include "i386-io-access.h"
+
+static int ioperm_enabled;
+static int iopl_enabled;
+
+static int
+intel_setup_io(struct pci_access *a UNUSED)
+{
+ if (ioperm_enabled || iopl_enabled)
+ return 1;
+
+ /*
+ * Before Linux 2.6.8, only the first 0x3ff I/O ports permissions can be
+ * modified via ioperm(). Since 2.6.8 all ports are supported.
+ * Since Linux 5.5, EFLAGS-based iopl() implementation was removed and
+ * replaced by new TSS-IOPB-map-all-based emulator. Before Linux 5.5,
+ * EFLAGS-based iopl() allowed userspace to enable/disable interrupts,
+ * which is dangerous. So prefer usage of ioperm() and fallback to iopl().
+ */
+ if (ioperm(0xcf8, 8, 1) < 0) /* conf1 + conf2 ports */
+ {
+ if (errno == EINVAL) /* ioperm() unsupported */
+ {
+ if (iopl(3) < 0)
+ return 0;
+ iopl_enabled = 1;
+ return 1;
+ }
+ return 0;
+ }
+ if (ioperm(0xc000, 0xfff, 1) < 0) /* remaining conf2 ports */
+ {
+ ioperm(0xcf8, 8, 0);
+ return 0;
+ }
+
+ ioperm_enabled = 1;
+ return 1;
+}
+
+static inline void
+intel_cleanup_io(struct pci_access *a UNUSED)
+{
+ if (ioperm_enabled)
+ {
+ ioperm(0xcf8, 8, 0);
+ ioperm(0xc000, 0xfff, 0);
+ ioperm_enabled = 0;
+ }
+
+ if (iopl_enabled)
+ {
+ iopl(0);
+ iopl_enabled = 0;
+ }
+}
+
+static inline void intel_io_lock(void)
+{
+}
+
+static inline void intel_io_unlock(void)
+{
+}
diff --git a/lib/i386-io-openbsd.h b/lib/i386-io-openbsd.h
new file mode 100644
index 0000000..8a9b4a4
--- /dev/null
+++ b/lib/i386-io-openbsd.h
@@ -0,0 +1,54 @@
+/*
+ * The PCI Library -- Access to i386 I/O ports on OpenBSD
+ *
+ * Copyright (c) 2023 Grant Pannell <grant@pannell.net.au>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include <sys/types.h>
+#include <machine/sysarch.h>
+#include <machine/pio.h>
+
+#include "i386-io-access.h"
+
+#if defined(__amd64__)
+ #define obsd_iopl amd64_iopl
+#else
+ #define obsd_iopl i386_iopl
+#endif
+
+static int iopl_enabled;
+
+static int
+intel_setup_io(struct pci_access *a UNUSED)
+{
+ if (iopl_enabled)
+ return 1;
+
+ if (obsd_iopl(3) < 0)
+ {
+ return 0;
+ }
+
+ iopl_enabled = 1;
+ return 1;
+}
+
+static inline void
+intel_cleanup_io(struct pci_access *a UNUSED)
+{
+ if (iopl_enabled)
+ {
+ obsd_iopl(0);
+ iopl_enabled = 0;
+ }
+}
+
+static inline void intel_io_lock(void)
+{
+}
+
+static inline void intel_io_unlock(void)
+{
+}
diff --git a/lib/i386-io-sunos.h b/lib/i386-io-sunos.h
new file mode 100644
index 0000000..1caefd5
--- /dev/null
+++ b/lib/i386-io-sunos.h
@@ -0,0 +1,35 @@
+/*
+ * The PCI Library -- Access to i386 I/O ports on Solaris
+ *
+ * Copyright (c) 2003 Bill Moore <billm@eng.sun.com>
+ * Copyright (c) 2003--2006 Martin Mares <mj@ucw.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <sys/sysi86.h>
+#include <sys/psw.h>
+
+#include "i386-io-access.h"
+
+static int
+intel_setup_io(struct pci_access *a UNUSED)
+{
+ return (sysi86(SI86V86, V86SC_IOPL, PS_IOPL) < 0) ? 0 : 1;
+}
+
+static inline void
+intel_cleanup_io(struct pci_access *a UNUSED)
+{
+ sysi86(SI86V86, V86SC_IOPL, 0);
+}
+
+static inline void intel_io_lock(void)
+{
+}
+
+static inline void intel_io_unlock(void)
+{
+}
diff --git a/lib/i386-io-windows.h b/lib/i386-io-windows.h
new file mode 100644
index 0000000..d2da452
--- /dev/null
+++ b/lib/i386-io-windows.h
@@ -0,0 +1,248 @@
+/*
+ * The PCI Library -- Access to i386 I/O ports on Windows
+ *
+ * Copyright (c) 2004 Alexander Stock <stock.alexander@gmx.de>
+ * Copyright (c) 2006 Martin Mares <mj@ucw.cz>
+ * Copyright (c) 2021 Pali Rohár <pali@kernel.org>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <windows.h>
+#include "win32-helpers.h"
+
+#include "i386-io-access.h"
+
+/*
+ * Define __readeflags() for MSVC and GCC compilers.
+ * MSVC since version 14.00 included in WDK 6001 and since version 15.00
+ * included in VS 2008 provides __readeflags() intrinsic for both 32 and 64-bit
+ * modes. WDK 6001 defines macro __BUILDMACHINE__ to value WinDDK. VS 2008 does
+ * not define this macro at all. MSVC throws error if name of user defined
+ * function conflicts with some MSVC intrinsic.
+ * MSVC supports inline assembly via __asm keyword in 32-bit mode only.
+ * GCC version 4.9.0 and higher provides __builtin_ia32_readeflags_uXX()
+ * builtin for XX-mode. This builtin is also available as __readeflags()
+ * function indirectly via <x86intrin.h> header file.
+ *
+ * CAVEAT: Semicolon in MSVC __asm block means start of the comment, and not
+ * end of the __asm statement, like it is for all other C statements. Also
+ * function which uses MSVC inline assembly cannot be inlined to another function
+ * (compiler reports a warning about it, not a fatal error). So we add explicit
+ * curly brackets for __asm blocks, remove misleading semicolons and do not
+ * declare functions as inline.
+ */
+#if defined(_MSC_VER) && (_MSC_VER >= 1500 || (_MSC_VER >= 1400 && defined(__BUILDMACHINE__)))
+#pragma intrinsic(__readeflags)
+#elif defined(__GNUC__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 9) || (__GNUC__ > 4))
+#include <x86intrin.h>
+#elif defined(_MSC_VER) && defined(_M_IX86)
+static unsigned int
+__readeflags(void)
+{
+ __asm {
+ pushfd
+ pop eax
+ }
+}
+#elif defined(__GNUC__)
+static inline unsigned
+#ifdef __x86_64__
+long long
+#endif
+int
+__readeflags(void)
+{
+ unsigned
+#ifdef __x86_64__
+ long long
+#endif
+ int eflags;
+ asm volatile ("pushf\n\tpop %0\n" : "=r" (eflags));
+ return eflags;
+}
+#else
+#error "Unsupported compiler"
+#endif
+
+/* Read IOPL of the current process, IOPL is stored in eflag bits [13:12]. */
+#define read_iopl() ((__readeflags() >> 12) & 0x3)
+
+/*
+ * Unfortunately NtSetInformationProcess() function, ProcessUserModeIOPL
+ * constant and all other helpers for its usage are not specified in any
+ * standard WinAPI header file. So define all of required constants and types.
+ * Function NtSetInformationProcess() is available in ntdll.dll library on all
+ * Windows systems but marked as it can be removed in some future version.
+ */
+#ifndef NTSTATUS
+#define NTSTATUS LONG
+#endif
+#ifndef STATUS_NOT_IMPLEMENTED
+#define STATUS_NOT_IMPLEMENTED (NTSTATUS)0xC0000002
+#endif
+#ifndef STATUS_PRIVILEGE_NOT_HELD
+#define STATUS_PRIVILEGE_NOT_HELD (NTSTATUS)0xC0000061
+#endif
+#ifndef PROCESSINFOCLASS
+#define PROCESSINFOCLASS DWORD
+#endif
+#ifndef ProcessUserModeIOPL
+#define ProcessUserModeIOPL 16
+#endif
+typedef NTSTATUS (NTAPI *NtSetInformationProcessProt)(HANDLE ProcessHandle, PROCESSINFOCLASS ProcessInformationClass, PVOID ProcessInformation, ULONG ProcessInformationLength);
+typedef ULONG (NTAPI *RtlNtStatusToDosErrorProt)(NTSTATUS Status);
+
+/*
+ * ProcessUserModeIOPL is syscall for NT kernel to change x86 IOPL
+ * of the current running process to 3.
+ *
+ * Process handle argument for ProcessUserModeIOPL is ignored and
+ * IOPL is always changed for the current running process. So pass
+ * GetCurrentProcess() handle for documentation purpose. Process
+ * information buffer and length are unused for ProcessUserModeIOPL.
+ *
+ * ProcessUserModeIOPL may success (return value >= 0) or may fail
+ * because it is not implemented or because of missing privilege.
+ * Other errors are not defined, so handle them as unknown.
+ */
+static BOOL
+SetProcessUserModeIOPLFunc(LPVOID Arg)
+{
+ RtlNtStatusToDosErrorProt RtlNtStatusToDosErrorPtr = (RtlNtStatusToDosErrorProt)(((LPVOID *)Arg)[1]);
+ NtSetInformationProcessProt NtSetInformationProcessPtr = (NtSetInformationProcessProt)(((LPVOID *)Arg)[0]);
+ NTSTATUS nt_status = NtSetInformationProcessPtr(GetCurrentProcess(), ProcessUserModeIOPL, NULL, 0);
+ if (nt_status >= 0)
+ return TRUE;
+
+ /*
+ * If we have optional RtlNtStatusToDosError() function then use it for
+ * translating NT status to Win32 error. If we do not have it then translate
+ * two important status codes which we use later STATUS_NOT_IMPLEMENTED and
+ * STATUS_PRIVILEGE_NOT_HELD.
+ */
+ if (RtlNtStatusToDosErrorPtr)
+ SetLastError(RtlNtStatusToDosErrorPtr(nt_status));
+ else if (nt_status == STATUS_NOT_IMPLEMENTED)
+ SetLastError(ERROR_INVALID_FUNCTION);
+ else if (nt_status == STATUS_PRIVILEGE_NOT_HELD)
+ SetLastError(ERROR_PRIVILEGE_NOT_HELD);
+ else
+ SetLastError(ERROR_GEN_FAILURE);
+
+ return FALSE;
+}
+
+/*
+ * Set x86 I/O Privilege Level to 3 for the whole current NT process. Do it via
+ * NtSetInformationProcess() call with ProcessUserModeIOPL information class,
+ * which is supported by 32-bit Windows NT kernel versions and requires Tcb
+ * privilege.
+ */
+static BOOL
+SetProcessUserModeIOPL(VOID)
+{
+ LPVOID Arg[2];
+ UINT prev_error_mode;
+ HMODULE ntdll;
+ BOOL ret;
+
+ /*
+ * Load ntdll.dll library with disabled critical-error-handler message box.
+ * It means that NT kernel does not show unwanted GUI message box to user
+ * when LoadLibrary() function fails.
+ */
+ prev_error_mode = win32_change_error_mode(SEM_FAILCRITICALERRORS);
+ ntdll = LoadLibrary(TEXT("ntdll.dll"));
+ win32_change_error_mode(prev_error_mode);
+ if (!ntdll)
+ {
+ SetLastError(ERROR_INVALID_FUNCTION);
+ return FALSE;
+ }
+
+ /* Retrieve pointer to NtSetInformationProcess() function. */
+ Arg[0] = (LPVOID)GetProcAddress(ntdll, "NtSetInformationProcess");
+ if (!Arg[0])
+ {
+ FreeLibrary(ntdll);
+ SetLastError(ERROR_INVALID_FUNCTION);
+ return FALSE;
+ }
+
+ /* Retrieve pointer to optional RtlNtStatusToDosError() function, it may be NULL. */
+ Arg[1] = (LPVOID)GetProcAddress(ntdll, "RtlNtStatusToDosError");
+
+ /* Call ProcessUserModeIOPL with Tcb privilege. */
+ ret = win32_call_func_with_tcb_privilege(SetProcessUserModeIOPLFunc, (LPVOID)&Arg);
+
+ FreeLibrary(ntdll);
+
+ if (!ret)
+ return FALSE;
+
+ /*
+ * Some Windows NT kernel versions (e.g. Windows 2003 x64) do not
+ * implement ProcessUserModeIOPL syscall at all but incorrectly
+ * returns success when it is called by user process. So always
+ * after this call verify that IOPL is set to 3.
+ */
+ if (read_iopl() != 3)
+ {
+ SetLastError(ERROR_INVALID_FUNCTION);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static int
+intel_setup_io(struct pci_access *a)
+{
+#ifndef _WIN64
+ /* 16/32-bit non-NT systems allow applications to access PCI I/O ports without any special setup. */
+ if (win32_is_non_nt_system())
+ {
+ a->debug("Detected 16/32-bit non-NT system, skipping NT setup...");
+ return 1;
+ }
+#endif
+
+ /* Check if we have I/O permission */
+ if (read_iopl() == 3)
+ {
+ a->debug("IOPL is already set to 3, skipping NT setup...");
+ return 1;
+ }
+
+ /* On NT-based systems issue ProcessUserModeIOPL syscall which changes IOPL to 3. */
+ if (!SetProcessUserModeIOPL())
+ {
+ DWORD error = GetLastError();
+ a->debug("NT ProcessUserModeIOPL call failed: %s.", error == ERROR_INVALID_FUNCTION ? "Call is not supported" : win32_strerror(error));
+ return 0;
+ }
+
+ a->debug("NT ProcessUserModeIOPL call succeeded...");
+ return 1;
+}
+
+static inline void
+intel_cleanup_io(struct pci_access *a UNUSED)
+{
+ /*
+ * 16/32-bit non-NT systems do not use any special setup and on NT-based
+ * systems ProcessUserModeIOPL permanently changes IOPL to 3 for the current
+ * NT process, no revert for current process is possible.
+ */
+}
+
+static inline void intel_io_lock(void)
+{
+}
+
+static inline void intel_io_unlock(void)
+{
+}
diff --git a/lib/i386-ports.c b/lib/i386-ports.c
new file mode 100644
index 0000000..5f8aea4
--- /dev/null
+++ b/lib/i386-ports.c
@@ -0,0 +1,327 @@
+/*
+ * The PCI Library -- Direct Configuration access via i386 Ports
+ *
+ * Copyright (c) 1997--2006 Martin Mares <mj@ucw.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#define _GNU_SOURCE
+
+#include "internal.h"
+
+#include <string.h>
+
+#if defined(PCI_OS_LINUX)
+#include "i386-io-linux.h"
+#elif defined(PCI_OS_GNU)
+#include "i386-io-hurd.h"
+#elif defined(PCI_OS_SUNOS)
+#include "i386-io-sunos.h"
+#elif defined(PCI_OS_WINDOWS)
+#include "i386-io-windows.h"
+#elif defined(PCI_OS_CYGWIN)
+#include "i386-io-cygwin.h"
+#elif defined(PCI_OS_HAIKU)
+#include "i386-io-haiku.h"
+#elif defined(PCI_OS_BEOS)
+#include "i386-io-beos.h"
+#elif defined(PCI_OS_DJGPP)
+#include "i386-io-djgpp.h"
+#elif defined(PCI_OS_OPENBSD)
+#include "i386-io-openbsd.h"
+#else
+#error Do not know how to access I/O ports on this OS.
+#endif
+
+static int conf12_io_enabled = -1; /* -1=haven't tried, 0=failed, 1=succeeded */
+
+static int
+conf12_setup_io(struct pci_access *a)
+{
+ if (conf12_io_enabled < 0)
+ conf12_io_enabled = intel_setup_io(a);
+ return conf12_io_enabled;
+}
+
+static void
+conf12_init(struct pci_access *a)
+{
+ if (!conf12_setup_io(a))
+ a->error("No permission to access I/O ports (you probably have to be root).");
+}
+
+static void
+conf12_cleanup(struct pci_access *a)
+{
+ if (conf12_io_enabled > 0)
+ {
+ intel_cleanup_io(a);
+ conf12_io_enabled = -1;
+ }
+}
+
+/*
+ * Before we decide to use direct hardware access mechanisms, we try to do some
+ * trivial checks to ensure it at least _seems_ to be working -- we just test
+ * whether bus 00 contains a host bridge (this is similar to checking
+ * techniques used in XFree86, but ours should be more reliable since we
+ * attempt to make use of direct access hints provided by the PCI BIOS).
+ *
+ * This should be close to trivial, but it isn't, because there are buggy
+ * chipsets (yes, you guessed it, by Intel and Compaq) that have no class ID.
+ */
+
+static int
+intel_sanity_check(struct pci_access *a, struct pci_methods *m)
+{
+ struct pci_dev d;
+
+ memset(&d, 0, sizeof(d));
+ a->debug("...sanity check");
+ d.bus = 0;
+ d.func = 0;
+ for (d.dev = 0; d.dev < 32; d.dev++)
+ {
+ u16 class, vendor;
+ if (m->read(&d, PCI_CLASS_DEVICE, (byte *) &class, sizeof(class)) &&
+ (class == cpu_to_le16(PCI_CLASS_BRIDGE_HOST) || class == cpu_to_le16(PCI_CLASS_DISPLAY_VGA)) ||
+ m->read(&d, PCI_VENDOR_ID, (byte *) &vendor, sizeof(vendor)) &&
+ (vendor == cpu_to_le16(PCI_VENDOR_ID_INTEL) || vendor == cpu_to_le16(PCI_VENDOR_ID_COMPAQ)))
+ {
+ a->debug("...outside the Asylum at 0/%02x/0", d.dev);
+ return 1;
+ }
+ }
+ a->debug("...insane");
+ return 0;
+}
+
+/*
+ * Configuration type 1
+ */
+
+#define CONFIG_CMD(bus, device_fn, where) (0x80000000 | (bus << 16) | (device_fn << 8) | (where & ~3))
+
+static int
+conf1_detect(struct pci_access *a)
+{
+ unsigned int tmp;
+ int res = 0;
+
+ if (!conf12_setup_io(a))
+ {
+ a->debug("...no I/O permission");
+ return 0;
+ }
+
+ intel_io_lock();
+ intel_outb (0x01, 0xCFB);
+ tmp = intel_inl (0xCF8);
+ intel_outl (0x80000000, 0xCF8);
+ if (intel_inl (0xCF8) == 0x80000000)
+ res = 1;
+ intel_outl (tmp, 0xCF8);
+ intel_io_unlock();
+
+ if (res)
+ res = intel_sanity_check(a, &pm_intel_conf1);
+ return res;
+}
+
+static int
+conf1_read(struct pci_dev *d, int pos, byte *buf, int len)
+{
+ int addr = 0xcfc + (pos&3);
+ int res = 1;
+
+ if (d->domain || pos >= 256)
+ return 0;
+
+ if (len != 1 && len != 2 && len != 4)
+ return pci_generic_block_read(d, pos, buf, len);
+
+ intel_io_lock();
+ intel_outl(0x80000000 | ((d->bus & 0xff) << 16) | (PCI_DEVFN(d->dev, d->func) << 8) | (pos&~3), 0xcf8);
+
+ switch (len)
+ {
+ case 1:
+ buf[0] = intel_inb(addr);
+ break;
+ case 2:
+ ((u16 *) buf)[0] = cpu_to_le16(intel_inw(addr));
+ break;
+ case 4:
+ ((u32 *) buf)[0] = cpu_to_le32(intel_inl(addr));
+ break;
+ }
+
+ intel_io_unlock();
+ return res;
+}
+
+static int
+conf1_write(struct pci_dev *d, int pos, byte *buf, int len)
+{
+ int addr = 0xcfc + (pos&3);
+ int res = 1;
+
+ if (d->domain || pos >= 256)
+ return 0;
+
+ if (len != 1 && len != 2 && len != 4)
+ return pci_generic_block_write(d, pos, buf, len);
+
+ intel_io_lock();
+ intel_outl(0x80000000 | ((d->bus & 0xff) << 16) | (PCI_DEVFN(d->dev, d->func) << 8) | (pos&~3), 0xcf8);
+
+ switch (len)
+ {
+ case 1:
+ intel_outb(buf[0], addr);
+ break;
+ case 2:
+ intel_outw(le16_to_cpu(((u16 *) buf)[0]), addr);
+ break;
+ case 4:
+ intel_outl(le32_to_cpu(((u32 *) buf)[0]), addr);
+ break;
+ }
+ intel_io_unlock();
+ return res;
+}
+
+/*
+ * Configuration type 2. Obsolete and brain-damaged, but existing.
+ */
+
+static int
+conf2_detect(struct pci_access *a)
+{
+ int res = 0;
+
+ if (!conf12_setup_io(a))
+ {
+ a->debug("...no I/O permission");
+ return 0;
+ }
+
+ /* This is ugly and tends to produce false positives. Beware. */
+
+ intel_io_lock();
+ intel_outb(0x00, 0xCFB);
+ intel_outb(0x00, 0xCF8);
+ intel_outb(0x00, 0xCFA);
+ if (intel_inb(0xCF8) == 0x00 && intel_inb(0xCFA) == 0x00)
+ res = intel_sanity_check(a, &pm_intel_conf2);
+ intel_io_unlock();
+ return res;
+}
+
+static int
+conf2_read(struct pci_dev *d, int pos, byte *buf, int len)
+{
+ int res = 1;
+ int addr = 0xc000 | (d->dev << 8) | pos;
+
+ if (d->domain || pos >= 256)
+ return 0;
+
+ if (d->dev >= 16)
+ /* conf2 supports only 16 devices per bus */
+ return 0;
+
+ if (len != 1 && len != 2 && len != 4)
+ return pci_generic_block_read(d, pos, buf, len);
+
+ intel_io_lock();
+ intel_outb((d->func << 1) | 0xf0, 0xcf8);
+ intel_outb(d->bus, 0xcfa);
+ switch (len)
+ {
+ case 1:
+ buf[0] = intel_inb(addr);
+ break;
+ case 2:
+ ((u16 *) buf)[0] = cpu_to_le16(intel_inw(addr));
+ break;
+ case 4:
+ ((u32 *) buf)[0] = cpu_to_le32(intel_inl(addr));
+ break;
+ }
+ intel_outb(0, 0xcf8);
+ intel_io_unlock();
+ return res;
+}
+
+static int
+conf2_write(struct pci_dev *d, int pos, byte *buf, int len)
+{
+ int res = 1;
+ int addr = 0xc000 | (d->dev << 8) | pos;
+
+ if (d->domain || pos >= 256)
+ return 0;
+
+ if (d->dev >= 16)
+ /* conf2 supports only 16 devices per bus */
+ return 0;
+
+ if (len != 1 && len != 2 && len != 4)
+ return pci_generic_block_write(d, pos, buf, len);
+
+ intel_io_lock();
+ intel_outb((d->func << 1) | 0xf0, 0xcf8);
+ intel_outb(d->bus, 0xcfa);
+ switch (len)
+ {
+ case 1:
+ intel_outb(buf[0], addr);
+ break;
+ case 2:
+ intel_outw(le16_to_cpu(* (u16 *) buf), addr);
+ break;
+ case 4:
+ intel_outl(le32_to_cpu(* (u32 *) buf), addr);
+ break;
+ }
+
+ intel_outb(0, 0xcf8);
+ intel_io_unlock();
+ return res;
+}
+
+struct pci_methods pm_intel_conf1 = {
+ "intel-conf1",
+ "Raw I/O port access using Intel conf1 interface",
+ NULL, /* config */
+ conf1_detect,
+ conf12_init,
+ conf12_cleanup,
+ pci_generic_scan,
+ pci_generic_fill_info,
+ conf1_read,
+ conf1_write,
+ NULL, /* read_vpd */
+ NULL, /* init_dev */
+ NULL /* cleanup_dev */
+};
+
+struct pci_methods pm_intel_conf2 = {
+ "intel-conf2",
+ "Raw I/O port access using Intel conf2 interface",
+ NULL, /* config */
+ conf2_detect,
+ conf12_init,
+ conf12_cleanup,
+ pci_generic_scan,
+ pci_generic_fill_info,
+ conf2_read,
+ conf2_write,
+ NULL, /* read_vpd */
+ NULL, /* init_dev */
+ NULL /* cleanup_dev */
+};
diff --git a/lib/init.c b/lib/init.c
new file mode 100644
index 0000000..c26720a
--- /dev/null
+++ b/lib/init.c
@@ -0,0 +1,564 @@
+/*
+ * The PCI Library -- Initialization and related things
+ *
+ * Copyright (c) 1997--2024 Martin Mares <mj@ucw.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include "internal.h"
+
+#ifdef PCI_OS_DJGPP
+#include <crt0.h> /* for __dos_argv0 */
+#endif
+
+#ifdef PCI_OS_WINDOWS
+
+#include <windows.h>
+
+/* Force usage of ANSI (char*) variant of GetModuleFileName() function */
+#ifdef _WIN32
+#ifdef GetModuleFileName
+#undef GetModuleFileName
+#endif
+#define GetModuleFileName GetModuleFileNameA
+#endif
+
+/* Define __ImageBase for all linkers */
+#ifdef _WIN32
+/* GNU LD provides __ImageBase symbol since 2.19, in previous versions it is
+ * under name _image_base__, so add weak alias for compatibility. */
+#ifdef __GNUC__
+asm(".weak\t" PCI_STRINGIFY(__MINGW_USYMBOL(__ImageBase)) "\n\t"
+ ".set\t" PCI_STRINGIFY(__MINGW_USYMBOL(__ImageBase)) "," PCI_STRINGIFY(__MINGW_USYMBOL(_image_base__)));
+#endif
+/*
+ * MSVC link.exe provides __ImageBase symbol since 12.00 (MSVC 6.0), for
+ * previous versions resolve it at runtime via GetModuleHandleA() which
+ * returns base for main executable or via VirtualQuery() for DLL builds.
+ */
+#if defined(_MSC_VER) && _MSC_VER < 1200
+static HMODULE
+get_current_module_handle(void)
+{
+#ifdef PCI_SHARED_LIB
+ MEMORY_BASIC_INFORMATION info;
+ size_t len = VirtualQuery(&get_current_module_handle, &info, sizeof(info));
+ if (len != sizeof(info))
+ return NULL;
+ return (HMODULE)info.AllocationBase;
+#else
+ return GetModuleHandleA(NULL);
+#endif
+}
+#define __ImageBase (*(IMAGE_DOS_HEADER *)get_current_module_handle())
+#else
+extern IMAGE_DOS_HEADER __ImageBase;
+#endif
+#endif
+
+#if defined(_WINDLL)
+extern HINSTANCE _hModule;
+#elif defined(_WINDOWS)
+extern HINSTANCE _hInstance;
+#endif
+
+#endif
+
+static struct pci_methods *pci_methods[PCI_ACCESS_MAX] = {
+ NULL,
+#ifdef PCI_HAVE_PM_LINUX_SYSFS
+ &pm_linux_sysfs,
+#else
+ NULL,
+#endif
+#ifdef PCI_HAVE_PM_LINUX_PROC
+ &pm_linux_proc,
+#else
+ NULL,
+#endif
+#ifdef PCI_HAVE_PM_INTEL_CONF
+ &pm_intel_conf1,
+ &pm_intel_conf2,
+#else
+ NULL,
+ NULL,
+#endif
+#ifdef PCI_HAVE_PM_FBSD_DEVICE
+ &pm_fbsd_device,
+#else
+ NULL,
+#endif
+#ifdef PCI_HAVE_PM_AIX_DEVICE
+ &pm_aix_device,
+#else
+ NULL,
+#endif
+#ifdef PCI_HAVE_PM_NBSD_LIBPCI
+ &pm_nbsd_libpci,
+#else
+ NULL,
+#endif
+#ifdef PCI_HAVE_PM_OBSD_DEVICE
+ &pm_obsd_device,
+#else
+ NULL,
+#endif
+#ifdef PCI_HAVE_PM_DUMP
+ &pm_dump,
+#else
+ NULL,
+#endif
+#ifdef PCI_HAVE_PM_DARWIN_DEVICE
+ &pm_darwin,
+#else
+ NULL,
+#endif
+#ifdef PCI_HAVE_PM_SYLIXOS_DEVICE
+ &pm_sylixos_device,
+#else
+ NULL,
+#endif
+#ifdef PCI_HAVE_PM_HURD_CONF
+ &pm_hurd,
+#else
+ NULL,
+#endif
+#ifdef PCI_HAVE_PM_WIN32_CFGMGR32
+ &pm_win32_cfgmgr32,
+#else
+ NULL,
+#endif
+#ifdef PCI_HAVE_PM_WIN32_KLDBG
+ &pm_win32_kldbg,
+#else
+ NULL,
+#endif
+#ifdef PCI_HAVE_PM_WIN32_SYSDBG
+ &pm_win32_sysdbg,
+#else
+ NULL,
+#endif
+#ifdef PCI_HAVE_PM_MMIO_CONF
+ &pm_mmio_conf1,
+ &pm_mmio_conf1_ext,
+#else
+ NULL,
+ NULL,
+#endif
+#if defined(PCI_HAVE_PM_ECAM)
+ &pm_ecam,
+#else
+ NULL,
+#endif
+#if defined(PCI_HAVE_PM_AOS_EXPANSION)
+ &pm_aos_expansion,
+#else
+ NULL,
+#endif
+};
+
+// If PCI_ACCESS_AUTO is selected, we probe the access methods in this order
+static int probe_sequence[] = {
+ // System-specific methods
+ PCI_ACCESS_SYS_BUS_PCI,
+ PCI_ACCESS_PROC_BUS_PCI,
+ PCI_ACCESS_FBSD_DEVICE,
+ PCI_ACCESS_AIX_DEVICE,
+ PCI_ACCESS_NBSD_LIBPCI,
+ PCI_ACCESS_OBSD_DEVICE,
+ PCI_ACCESS_DARWIN,
+ PCI_ACCESS_SYLIXOS_DEVICE,
+ PCI_ACCESS_HURD,
+ PCI_ACCESS_WIN32_CFGMGR32,
+ PCI_ACCESS_WIN32_KLDBG,
+ PCI_ACCESS_WIN32_SYSDBG,
+ PCI_ACCESS_AOS_EXPANSION,
+ // Low-level methods poking the hardware directly
+ PCI_ACCESS_ECAM,
+ PCI_ACCESS_I386_TYPE1,
+ PCI_ACCESS_I386_TYPE2,
+ PCI_ACCESS_MMIO_TYPE1_EXT,
+ PCI_ACCESS_MMIO_TYPE1,
+ -1,
+};
+
+static void PCI_NONRET
+pci_generic_error(char *msg, ...)
+{
+ va_list args;
+
+ va_start(args, msg);
+ fputs("pcilib: ", stderr);
+ vfprintf(stderr, msg, args);
+ va_end(args);
+ fputc('\n', stderr);
+ exit(1);
+}
+
+static void
+pci_generic_warn(char *msg, ...)
+{
+ va_list args;
+
+ va_start(args, msg);
+ fputs("pcilib: ", stderr);
+ vfprintf(stderr, msg, args);
+ va_end(args);
+ fputc('\n', stderr);
+}
+
+static void
+pci_generic_debug(char *msg, ...)
+{
+ va_list args;
+
+ va_start(args, msg);
+ vfprintf(stdout, msg, args);
+ va_end(args);
+}
+
+static void
+pci_null_debug(char *msg UNUSED, ...)
+{
+}
+
+// Memory allocation functions are safe to call if pci_access is not fully initalized or even NULL
+
+void *
+pci_malloc(struct pci_access *a, int size)
+{
+ void *x = malloc(size);
+
+ if (!x)
+ (a && a->error ? a->error : pci_generic_error)("Out of memory (allocation of %d bytes failed)", size);
+ return x;
+}
+
+void
+pci_mfree(void *x)
+{
+ if (x)
+ free(x);
+}
+
+char *
+pci_strdup(struct pci_access *a, const char *s)
+{
+ int len = strlen(s) + 1;
+ char *t = pci_malloc(a, len);
+ memcpy(t, s, len);
+ return t;
+}
+
+int
+pci_lookup_method(char *name)
+{
+ int i;
+
+ for (i=0; i<PCI_ACCESS_MAX; i++)
+ if (pci_methods[i] && !strcmp(pci_methods[i]->name, name))
+ return i;
+ return -1;
+}
+
+char *
+pci_get_method_name(int index)
+{
+ if (index < 0 || index >= PCI_ACCESS_MAX)
+ return NULL;
+ else if (!pci_methods[index])
+ return "";
+ else
+ return pci_methods[index]->name;
+}
+
+#if defined(PCI_OS_WINDOWS) || defined(PCI_OS_DJGPP)
+
+static void
+pci_init_name_list_path(struct pci_access *a)
+{
+ if ((PCI_PATH_IDS_DIR)[0])
+ pci_set_name_list_path(a, PCI_PATH_IDS_DIR "\\" PCI_IDS, 0);
+ else
+ {
+ char *path, *sep;
+ size_t len;
+
+#if defined(PCI_OS_WINDOWS) && (defined(_WIN32) || defined(_WINDLL) || defined(_WINDOWS))
+
+ HMODULE module;
+ size_t size;
+
+#if defined(_WIN32)
+ module = (HINSTANCE)&__ImageBase;
+#elif defined(_WINDLL)
+ module = _hModule;
+#elif defined(_WINDOWS)
+ module = _hInstance;
+#endif
+
+ /*
+ * Module file name can have arbitrary length despite all MS examples say
+ * about MAX_PATH upper limit. This limit does not apply for example when
+ * executable is running from network disk with very long UNC paths or
+ * when using "\\??\\" prefix for specifying executable binary path.
+ * Function GetModuleFileName() returns passed size argument when passed
+ * buffer is too small and does not signal any error. In this case retry
+ * again with larger buffer.
+ */
+ size = 256; /* initial buffer size (more than sizeof(PCI_IDS)-4) */
+retry:
+ path = pci_malloc(a, size);
+ len = GetModuleFileName(module, path, size-sizeof(PCI_IDS)-4); /* 4 for "\\\\?\\" */
+ if (len >= size-sizeof(PCI_IDS)-4)
+ {
+ free(path);
+ size *= 2;
+ goto retry;
+ }
+ else if (len == 0)
+ path[0] = '\0';
+
+ /*
+ * GetModuleFileName() has bugs. On Windows 10 it prepends current drive
+ * letter if path is just pure NT namespace (with "\\??\\" prefix). Such
+ * extra drive letter makes path fully invalid and unusable. So remove
+ * extra drive letter to make path valid again.
+ * Reproduce: CreateProcessW("\\??\\C:\\lspci.exe", ...)
+ */
+ if (((path[0] >= 'a' && path[0] <= 'z') ||
+ (path[0] >= 'A' && path[0] <= 'Z')) &&
+ strncmp(path+1, ":\\??\\", 5) == 0)
+ {
+ memmove(path, path+2, len-2);
+ len -= 2;
+ path[len] = '\0';
+ }
+
+ /*
+ * GetModuleFileName() has bugs. On Windows 10 it does not add "\\\\?\\"
+ * prefix when path is in native NT UNC namespace. Such path is treated by
+ * WinAPI/DOS functions as standard DOS path relative to the current
+ * directory, hence something completely different. So prepend missing
+ * "\\\\?\\" prefix to make path valid again.
+ * Reproduce: CreateProcessW("\\??\\UNC\\10.0.2.4\\qemu\\lspci.exe", ...)
+ *
+ * If path starts with DOS drive letter and with appended PCI_IDS is
+ * longer than 260 bytes and is without "\\\\?\\" prefix then append it.
+ * This prefix is required for paths and file names with DOS drive letter
+ * longer than 260 bytes.
+ */
+ if (strncmp(path, "\\UNC\\", 5) == 0 ||
+ strncmp(path, "UNC\\", 4) == 0 ||
+ (((path[0] >= 'a' && path[0] <= 'z') || (path[0] >= 'A' && path[0] <= 'Z')) &&
+ len + sizeof(PCI_IDS) >= 260))
+ {
+ memmove(path+4, path, len);
+ memcpy(path, "\\\\?\\", 4);
+ len += 4;
+ path[len] = '\0';
+ }
+
+#elif defined(PCI_OS_DJGPP) || defined(PCI_OS_WINDOWS)
+
+ const char *exe_path;
+
+#ifdef PCI_OS_DJGPP
+ exe_path = __dos_argv0;
+#else
+ exe_path = _pgmptr;
+#endif
+
+ len = strlen(exe_path);
+ path = pci_malloc(a, len+sizeof(PCI_IDS));
+ memcpy(path, exe_path, len+1);
+
+#endif
+
+ sep = strrchr(path, '\\');
+ if (!sep)
+ {
+ /*
+ * If current module path (current executable for static builds or
+ * current DLL library for shared build) cannot be determined then
+ * fallback to the current directory.
+ */
+ free(path);
+ pci_set_name_list_path(a, PCI_IDS, 0);
+ }
+ else
+ {
+ memcpy(sep+1, PCI_IDS, sizeof(PCI_IDS));
+ pci_set_name_list_path(a, path, 1);
+ }
+ }
+}
+
+#elif defined PCI_OS_AMIGAOS
+
+static void
+pci_init_name_list_path(struct pci_access *a)
+{
+ int len = strlen(PCI_PATH_IDS_DIR);
+
+ if (!len)
+ pci_set_name_list_path(a, PCI_IDS, 0);
+ else
+ {
+ char last_char = PCI_PATH_IDS_DIR[len - 1];
+ if (last_char == ':' || last_char == '/') // root or parent char
+ pci_set_name_list_path(a, PCI_PATH_IDS_DIR PCI_IDS, 0);
+ else
+ pci_set_name_list_path(a, PCI_PATH_IDS_DIR "/" PCI_IDS, 0);
+ }
+}
+
+#else
+
+static void
+pci_init_name_list_path(struct pci_access *a)
+{
+ pci_set_name_list_path(a, PCI_PATH_IDS_DIR "/" PCI_IDS, 0);
+}
+
+#endif
+
+#ifdef PCI_USE_DNS
+
+static void
+pci_init_dns(struct pci_access *a)
+{
+ pci_define_param(a, "net.domain", PCI_ID_DOMAIN, "DNS domain used for resolving of ID's");
+ a->id_lookup_mode = PCI_LOOKUP_CACHE;
+
+ char *cache_dir = getenv("XDG_CACHE_HOME");
+ if (!cache_dir)
+ cache_dir = "~/.cache";
+
+ int name_len = strlen(cache_dir) + 32;
+ char *cache_name = pci_malloc(NULL, name_len);
+ snprintf(cache_name, name_len, "%s/pci-ids", cache_dir);
+ struct pci_param *param = pci_define_param(a, "net.cache_name", cache_name, "Name of the ID cache file");
+ param->value_malloced = 1;
+}
+
+#endif
+
+struct pci_access *
+pci_alloc(void)
+{
+ struct pci_access *a = pci_malloc(NULL, sizeof(struct pci_access));
+ int i;
+
+ memset(a, 0, sizeof(*a));
+ pci_init_name_list_path(a);
+#ifdef PCI_USE_DNS
+ pci_init_dns(a);
+#endif
+#ifdef PCI_HAVE_HWDB
+ pci_define_param(a, "hwdb.disable", "0", "Do not look up names in UDEV's HWDB if non-zero");
+#endif
+ for (i=0; i<PCI_ACCESS_MAX; i++)
+ if (pci_methods[i] && pci_methods[i]->config)
+ pci_methods[i]->config(a);
+ return a;
+}
+
+int
+pci_init_internal(struct pci_access *a, int skip_method)
+{
+ if (!a->error)
+ a->error = pci_generic_error;
+ if (!a->warning)
+ a->warning = pci_generic_warn;
+ if (!a->debug)
+ a->debug = pci_generic_debug;
+ if (!a->debugging)
+ a->debug = pci_null_debug;
+
+ if (a->method != PCI_ACCESS_AUTO)
+ {
+ if (a->method >= PCI_ACCESS_MAX || !pci_methods[a->method])
+ a->error("This access method is not supported.");
+ a->methods = pci_methods[a->method];
+ }
+ else
+ {
+ unsigned int i;
+ for (i=0; probe_sequence[i] >= 0; i++)
+ {
+ 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))
+ {
+ a->debug("...OK\n");
+ a->methods = m;
+ a->method = probe_sequence[i];
+ break;
+ }
+ a->debug("...No.\n");
+ }
+ if (!a->methods)
+ return 0;
+ }
+ a->debug("Decided to use %s\n", a->methods->name);
+ a->methods->init(a);
+ return 1;
+}
+
+void
+pci_init_v35(struct pci_access *a)
+{
+ if (!pci_init_internal(a, -1))
+ a->error("Cannot find any working access method.");
+}
+
+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);
+SYMBOL_VERSION(pci_init_v35, pci_init@@LIBPCI_3.5);
+
+struct pci_access *
+pci_clone_access(struct pci_access *a)
+{
+ struct pci_access *b = pci_alloc();
+
+ b->writeable = a->writeable;
+ b->buscentric = a->buscentric;
+ b->debugging = a->debugging;
+ b->error = a->error;
+ b->warning = a->warning;
+ b->debug = a->debug;
+
+ return b;
+}
+
+void
+pci_cleanup(struct pci_access *a)
+{
+ struct pci_dev *d, *e;
+
+ for (d=a->devices; d; d=e)
+ {
+ e = d->next;
+ pci_free_dev(d);
+ }
+ if (a->methods)
+ a->methods->cleanup(a);
+ pci_free_name_list(a);
+ pci_free_params(a);
+ pci_set_name_list_path(a, NULL, 0);
+ pci_mfree(a);
+}
diff --git a/lib/internal.h b/lib/internal.h
new file mode 100644
index 0000000..68e9fa0
--- /dev/null
+++ b/lib/internal.h
@@ -0,0 +1,148 @@
+/*
+ * The PCI Library -- Internal Stuff
+ *
+ * Copyright (c) 1997--2022 Martin Mares <mj@ucw.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef _INTERNAL_H
+#define _INTERNAL_H
+
+#include "config.h"
+
+#ifdef PCI_SHARED_LIB
+#define PCI_ABI __attribute__((visibility("default")))
+// Functions, which are bound to externally visible symbols by the versioning
+// mechanism, have to be declared as VERSIONED. Otherwise, GCC with global
+// optimizations is happy to optimize them away, leading to linker failures.
+#define VERSIONED_ABI __attribute__((used)) PCI_ABI
+#ifdef __APPLE__
+#define STATIC_ALIAS(_decl, _for) VERSIONED_ABI _decl { return _for; }
+#define DEFINE_ALIAS(_decl, _for)
+#define SYMBOL_VERSION(_int, _ext)
+#else
+#define DEFINE_ALIAS(_decl, _for) extern _decl __attribute__((alias(#_for))) VERSIONED_ABI
+#ifdef _WIN32
+#define STATIC_ALIAS(_decl, _for) VERSIONED_ABI _decl { return _for; }
+/* GCC does not support asm .symver directive for Windows targets, so define new external global function symbol as alias to internal symbol */
+#define SYMBOL_VERSION(_int, _ext) asm(".globl\t" PCI_STRINGIFY(__MINGW_USYMBOL(_ext)) "\n\t" \
+ ".def\t" PCI_STRINGIFY(__MINGW_USYMBOL(_ext)) ";\t.scl\t2;\t.type\t32;\t.endef\n\t" \
+ ".set\t" PCI_STRINGIFY(__MINGW_USYMBOL(_ext)) "," PCI_STRINGIFY(__MINGW_USYMBOL(_int)))
+#else
+#define STATIC_ALIAS(_decl, _for)
+#define SYMBOL_VERSION(_int, _ext) asm(".symver " #_int "," #_ext)
+#endif
+#endif
+#else
+#define VERSIONED_ABI
+#define STATIC_ALIAS(_decl, _for) _decl { return _for; }
+#define DEFINE_ALIAS(_decl, _for)
+#define SYMBOL_VERSION(_int, _ext)
+#endif
+
+#include "pci.h"
+#include "sysdep.h"
+
+/* Old 32-bit-only versions of MinGW32 do not define __MINGW_USYMBOL macro */
+#ifdef __MINGW32__
+#ifndef __MINGW_USYMBOL
+#define __MINGW_USYMBOL(sym) _##sym
+#endif
+#endif
+
+#define _PCI_STRINGIFY(x) #x
+#define PCI_STRINGIFY(x) _PCI_STRINGIFY(x)
+
+struct pci_methods {
+ char *name;
+ char *help;
+ void (*config)(struct pci_access *);
+ int (*detect)(struct pci_access *);
+ void (*init)(struct pci_access *);
+ void (*cleanup)(struct pci_access *);
+ void (*scan)(struct pci_access *);
+ void (*fill_info)(struct pci_dev *, unsigned int flags);
+ int (*read)(struct pci_dev *, int pos, byte *buf, int len);
+ int (*write)(struct pci_dev *, int pos, byte *buf, int len);
+ int (*read_vpd)(struct pci_dev *, int pos, byte *buf, int len);
+ void (*init_dev)(struct pci_dev *);
+ void (*cleanup_dev)(struct pci_dev *);
+};
+
+/* generic.c */
+void pci_generic_scan_bus(struct pci_access *, byte *busmap, int domain, int bus);
+void pci_generic_scan_domain(struct pci_access *, int domain);
+void pci_generic_scan(struct pci_access *);
+void pci_generic_fill_info(struct pci_dev *, unsigned int flags);
+int pci_generic_block_read(struct pci_dev *, int pos, byte *buf, int len);
+int pci_generic_block_write(struct pci_dev *, int pos, byte *buf, int len);
+
+/* emulated.c */
+int pci_emulated_read(struct pci_dev *d, int pos, byte *buf, int len);
+
+/* init.c */
+void *pci_malloc(struct pci_access *, int);
+void pci_mfree(void *);
+char *pci_strdup(struct pci_access *a, const char *s);
+struct pci_access *pci_clone_access(struct pci_access *a);
+int pci_init_internal(struct pci_access *a, int skip_method);
+
+void pci_init_v30(struct pci_access *a) VERSIONED_ABI;
+void pci_init_v35(struct pci_access *a) VERSIONED_ABI;
+
+/* access.c */
+struct pci_dev *pci_alloc_dev(struct pci_access *);
+int pci_link_dev(struct pci_access *, struct pci_dev *);
+
+int pci_fill_info_v30(struct pci_dev *, int flags) VERSIONED_ABI;
+int pci_fill_info_v31(struct pci_dev *, int flags) VERSIONED_ABI;
+int pci_fill_info_v32(struct pci_dev *, int flags) VERSIONED_ABI;
+int pci_fill_info_v33(struct pci_dev *, int flags) VERSIONED_ABI;
+int pci_fill_info_v34(struct pci_dev *, int flags) VERSIONED_ABI;
+int pci_fill_info_v35(struct pci_dev *, int flags) VERSIONED_ABI;
+int pci_fill_info_v38(struct pci_dev *, int flags) VERSIONED_ABI;
+
+static inline int want_fill(struct pci_dev *d, unsigned want_fields, unsigned int try_fields)
+{
+ want_fields &= try_fields;
+ if ((d->known_fields & want_fields) == want_fields)
+ return 0;
+ else
+ {
+ d->known_fields |= try_fields;
+ return 1;
+ }
+}
+
+static inline void clear_fill(struct pci_dev *d, unsigned clear_fields)
+{
+ d->known_fields &= ~clear_fields;
+}
+
+struct pci_property {
+ struct pci_property *next;
+ u32 key;
+ char value[1];
+};
+
+char *pci_set_property(struct pci_dev *d, u32 key, char *value);
+
+/* params.c */
+struct pci_param *pci_define_param(struct pci_access *acc, char *param, char *val, char *help);
+int pci_set_param_internal(struct pci_access *acc, char *param, char *val, int copy);
+void pci_free_params(struct pci_access *acc);
+
+/* caps.c */
+void pci_scan_caps(struct pci_dev *, unsigned int want_fields);
+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_mmio_conf1, pm_mmio_conf1_ext, pm_ecam,
+ pm_win32_cfgmgr32, pm_win32_kldbg, pm_win32_sysdbg, pm_aos_expansion;
+
+#endif
diff --git a/lib/libpci.pc.in b/lib/libpci.pc.in
new file mode 100644
index 0000000..29c6910
--- /dev/null
+++ b/lib/libpci.pc.in
@@ -0,0 +1,11 @@
+prefix=@PREFIX@
+includedir=@INCDIR@
+libdir=@LIBDIR@
+idsdir=@IDSDIR@
+
+Name: libpci
+Description: libpci
+Version: @VERSION@
+Libs: -L${libdir} -lpci
+Libs.private: @LDLIBS@ @WITH_LIBS@
+Cflags: -I${includedir}
diff --git a/lib/libpci.ver b/lib/libpci.ver
new file mode 100644
index 0000000..33ee024
--- /dev/null
+++ b/lib/libpci.ver
@@ -0,0 +1,100 @@
+/* Version script for the libpci */
+
+/*
+ * Visibility declarations in the source take precedence over this script,
+ * so we can boldly declare pci_* as public and still keep the internal
+ * functions properly hidden.
+ *
+ * To preserve compatibility of Windows DLL file, always add new symbol at
+ * the end of file and never change order of symbols nor version sections.
+ * On Windows the last referenced version of the symbol is the default one.
+
+ * For PE/COFF targets this file is processed by ver2def.pl script and not
+ * by GNU LD linker like for ELF targets.
+ */
+
+LIBPCI_3.0 {
+ global:
+ pci_alloc;
+ pci_cleanup;
+ pci_fill_info;
+ pci_filter_init;
+ pci_filter_match;
+ pci_filter_parse_id;
+ pci_filter_parse_slot;
+ pci_free_dev;
+ pci_free_name_list;
+ pci_get_dev;
+ pci_get_method_name;
+ pci_get_param;
+ pci_id_cache_flush;
+ pci_init;
+ pci_load_name_list;
+ pci_lookup_method;
+ pci_lookup_name;
+ pci_read_block;
+ pci_read_byte;
+ pci_read_long;
+ pci_read_word;
+ pci_scan_bus;
+ pci_set_name_list_path;
+ pci_set_param;
+ pci_setup_cache;
+ pci_walk_params;
+ pci_write_block;
+ pci_write_byte;
+ pci_write_long;
+ pci_write_word;
+ local: *;
+};
+
+LIBPCI_3.1 {
+ global:
+ pci_fill_info;
+ pci_find_cap;
+ pci_read_vpd;
+};
+
+LIBPCI_3.2 {
+ global:
+ pci_fill_info;
+};
+
+LIBPCI_3.3 {
+ global:
+ pci_fill_info;
+ pci_filter_init;
+ pci_filter_match;
+ pci_filter_parse_id;
+ pci_filter_parse_slot;
+};
+
+LIBPCI_3.4 {
+ global:
+ pci_fill_info;
+};
+
+LIBPCI_3.5 {
+ global:
+ pci_init;
+ pci_fill_info;
+};
+
+LIBPCI_3.6 {
+ global:
+ pci_get_string_property;
+};
+
+LIBPCI_3.7 {
+ global:
+ pci_find_cap_nr;
+};
+
+LIBPCI_3.8 {
+ global:
+ pci_fill_info;
+ pci_filter_init;
+ pci_filter_match;
+ pci_filter_parse_id;
+ pci_filter_parse_slot;
+};
diff --git a/lib/mmio-ports.c b/lib/mmio-ports.c
new file mode 100644
index 0000000..cac8a7e
--- /dev/null
+++ b/lib/mmio-ports.c
@@ -0,0 +1,432 @@
+/*
+ * The PCI Library -- Direct Configuration access via memory mapped ports
+ *
+ * Copyright (c) 2022 Pali Rohár <pali@kernel.org>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "internal.h"
+#include "physmem.h"
+#include "physmem-access.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct mmio_cache {
+ u64 addr_page;
+ u64 data_page;
+ void *addr_map;
+ void *data_map;
+};
+
+struct mmio_access {
+ struct mmio_cache *cache;
+ struct physmem *physmem;
+ long pagesize;
+};
+
+static void
+munmap_regs(struct pci_access *a)
+{
+ struct mmio_access *macc = a->backend_data;
+ struct mmio_cache *cache = macc->cache;
+ struct physmem *physmem = macc->physmem;
+ long pagesize = macc->pagesize;
+
+ if (!cache)
+ return;
+
+ physmem_unmap(physmem, cache->addr_map, pagesize);
+ if (cache->addr_page != cache->data_page)
+ physmem_unmap(physmem, cache->data_map, pagesize);
+
+ pci_mfree(macc->cache);
+ macc->cache = NULL;
+}
+
+static int
+mmap_regs(struct pci_access *a, u64 addr_reg, u64 data_reg, int data_off, volatile void **addr, volatile void **data)
+{
+ struct mmio_access *macc = a->backend_data;
+ struct mmio_cache *cache = macc->cache;
+ struct physmem *physmem = macc->physmem;
+ long pagesize = macc->pagesize;
+ u64 addr_page = addr_reg & ~(pagesize-1);
+ u64 data_page = data_reg & ~(pagesize-1);
+ void *addr_map = (void *)-1;
+ void *data_map = (void *)-1;
+
+ if (cache && cache->addr_page == addr_page)
+ addr_map = cache->addr_map;
+
+ if (cache && cache->data_page == data_page)
+ data_map = cache->data_map;
+
+ if (addr_map == (void *)-1)
+ addr_map = physmem_map(physmem, addr_page, pagesize, 1);
+
+ if (addr_map == (void *)-1)
+ return 0;
+
+ if (data_map == (void *)-1)
+ {
+ if (data_page == addr_page)
+ data_map = addr_map;
+ else
+ data_map = physmem_map(physmem, data_page, pagesize, 1);
+ }
+
+ if (data_map == (void *)-1)
+ {
+ if (!cache || cache->addr_map != addr_map)
+ physmem_unmap(physmem, addr_map, pagesize);
+ return 0;
+ }
+
+ if (cache && cache->addr_page != addr_page)
+ physmem_unmap(physmem, cache->addr_map, pagesize);
+
+ if (cache && cache->data_page != data_page && cache->data_page != cache->addr_page)
+ physmem_unmap(physmem, cache->data_map, pagesize);
+
+ if (!cache)
+ cache = macc->cache = pci_malloc(a, sizeof(*cache));
+
+ cache->addr_page = addr_page;
+ cache->data_page = data_page;
+ cache->addr_map = addr_map;
+ cache->data_map = data_map;
+
+ *addr = (unsigned char *)addr_map + (addr_reg & (pagesize-1));
+ *data = (unsigned char *)data_map + (data_reg & (pagesize-1)) + data_off;
+ return 1;
+}
+
+static int
+validate_addrs(const char *addrs)
+{
+ const char *sep, *next;
+ u64 num;
+ char *endptr;
+
+ if (!*addrs)
+ return 0;
+
+ while (1)
+ {
+ next = strchr(addrs, ',');
+ if (!next)
+ next = addrs + strlen(addrs);
+
+ sep = strchr(addrs, '/');
+ if (!sep)
+ return 0;
+
+ if (!isxdigit(*addrs) || !isxdigit(*(sep+1)))
+ return 0;
+
+ errno = 0;
+ num = strtoull(addrs, &endptr, 16);
+ if (errno || endptr != sep || (num & 3))
+ return 0;
+
+ errno = 0;
+ num = strtoull(sep+1, &endptr, 16);
+ if (errno || endptr != next || (num & 3))
+ return 0;
+
+ if (!*next)
+ return 1;
+
+ addrs = next + 1;
+ }
+}
+
+static int
+get_domain_count(const char *addrs)
+{
+ int count = 1;
+ while (addrs = strchr(addrs, ','))
+ {
+ addrs++;
+ count++;
+ }
+ return count;
+}
+
+static int
+get_domain_addr(const char *addrs, int domain, u64 *addr_reg, u64 *data_reg)
+{
+ char *endptr;
+
+ while (domain-- > 0)
+ {
+ addrs = strchr(addrs, ',');
+ if (!addrs)
+ return 0;
+ addrs++;
+ }
+
+ *addr_reg = strtoull(addrs, &endptr, 16);
+ *data_reg = strtoull(endptr+1, NULL, 16);
+
+ return 1;
+}
+
+static void
+conf1_config(struct pci_access *a)
+{
+ physmem_init_config(a);
+ pci_define_param(a, "mmio-conf1.addrs", "", "Physical addresses of memory mapped Intel conf1 interface"); /* format: 0xaddr1/0xdata1,0xaddr2/0xdata2,... */
+}
+
+static void
+conf1_ext_config(struct pci_access *a)
+{
+ physmem_init_config(a);
+ pci_define_param(a, "mmio-conf1-ext.addrs", "", "Physical addresses of memory mapped Intel conf1 extended interface"); /* format: 0xaddr1/0xdata1,0xaddr2/0xdata2,... */
+}
+
+static int
+detect(struct pci_access *a, char *addrs_param_name)
+{
+ char *addrs = pci_get_param(a, addrs_param_name);
+
+ if (!*addrs)
+ {
+ a->debug("%s was not specified", addrs_param_name);
+ return 0;
+ }
+
+ if (!validate_addrs(addrs))
+ {
+ a->debug("%s has invalid address format %s", addrs_param_name, addrs);
+ return 0;
+ }
+
+ if (physmem_access(a, 1))
+ {
+ a->debug("cannot access physical memory: %s", strerror(errno));
+ return 0;
+ }
+
+ a->debug("using with %s", addrs);
+ return 1;
+}
+
+static int
+conf1_detect(struct pci_access *a)
+{
+ return detect(a, "mmio-conf1.addrs");
+}
+
+static int
+conf1_ext_detect(struct pci_access *a)
+{
+ return detect(a, "mmio-conf1-ext.addrs");
+}
+
+static char*
+get_addrs_param_name(struct pci_access *a)
+{
+ if (a->methods->config == conf1_ext_config)
+ return "mmio-conf1-ext.addrs";
+ else
+ return "mmio-conf1.addrs";
+}
+
+static void
+conf1_init(struct pci_access *a)
+{
+ char *addrs_param_name = get_addrs_param_name(a);
+ char *addrs = pci_get_param(a, addrs_param_name);
+ struct mmio_access *macc;
+ struct physmem *physmem;
+ long pagesize;
+
+ if (!*addrs)
+ a->error("Option %s was not specified.", addrs_param_name);
+
+ if (!validate_addrs(addrs))
+ a->error("Option %s has invalid address format \"%s\".", addrs_param_name, addrs);
+
+ physmem = physmem_open(a, 1);
+ if (!physmem)
+ a->error("Cannot open physcal memory: %s.", strerror(errno));
+
+ pagesize = physmem_get_pagesize(physmem);
+ if (pagesize <= 0)
+ a->error("Cannot get page size: %s.", strerror(errno));
+
+ macc = pci_malloc(a, sizeof(*macc));
+ macc->cache = NULL;
+ macc->physmem = physmem;
+ macc->pagesize = pagesize;
+ a->backend_data = macc;
+}
+
+static void
+conf1_cleanup(struct pci_access *a)
+{
+ struct mmio_access *macc = a->backend_data;
+
+ munmap_regs(a);
+ physmem_close(macc->physmem);
+ pci_mfree(macc);
+}
+
+static void
+conf1_scan(struct pci_access *a)
+{
+ char *addrs_param_name = get_addrs_param_name(a);
+ char *addrs = pci_get_param(a, addrs_param_name);
+ int domain_count = get_domain_count(addrs);
+ int domain;
+
+ for (domain = 0; domain < domain_count; domain++)
+ pci_generic_scan_domain(a, domain);
+}
+
+static int
+conf1_ext_read(struct pci_dev *d, int pos, byte *buf, int len)
+{
+ char *addrs_param_name = get_addrs_param_name(d->access);
+ char *addrs = pci_get_param(d->access, addrs_param_name);
+ volatile void *addr, *data;
+ u64 addr_reg, data_reg;
+
+ if (pos >= 4096)
+ return 0;
+
+ if (len != 1 && len != 2 && len != 4)
+ return pci_generic_block_read(d, pos, buf, len);
+
+ if (!get_domain_addr(addrs, d->domain, &addr_reg, &data_reg))
+ return 0;
+
+ if (!mmap_regs(d->access, addr_reg, data_reg, pos&3, &addr, &data))
+ return 0;
+
+ physmem_writel(0x80000000 | ((pos & 0xf00) << 16) | ((d->bus & 0xff) << 16) | (PCI_DEVFN(d->dev, d->func) << 8) | (pos & 0xfc), addr);
+ physmem_readl(addr); /* write barrier for address */
+
+ switch (len)
+ {
+ case 1:
+ buf[0] = physmem_readb(data);
+ break;
+ case 2:
+ ((u16 *) buf)[0] = physmem_readw(data);
+ break;
+ case 4:
+ ((u32 *) buf)[0] = physmem_readl(data);
+ break;
+ }
+
+ return 1;
+}
+
+static int
+conf1_read(struct pci_dev *d, int pos, byte *buf, int len)
+{
+ if (pos >= 256)
+ return 0;
+
+ return conf1_ext_read(d, pos, buf, len);
+}
+
+static int
+conf1_ext_write(struct pci_dev *d, int pos, byte *buf, int len)
+{
+ char *addrs_param_name = get_addrs_param_name(d->access);
+ char *addrs = pci_get_param(d->access, addrs_param_name);
+ volatile void *addr, *data;
+ u64 addr_reg, data_reg;
+
+ if (pos >= 4096)
+ return 0;
+
+ if (len != 1 && len != 2 && len != 4)
+ return pci_generic_block_write(d, pos, buf, len);
+
+ if (!get_domain_addr(addrs, d->domain, &addr_reg, &data_reg))
+ return 0;
+
+ if (!mmap_regs(d->access, addr_reg, data_reg, pos&3, &addr, &data))
+ return 0;
+
+ physmem_writel(0x80000000 | ((pos & 0xf00) << 16) | ((d->bus & 0xff) << 16) | (PCI_DEVFN(d->dev, d->func) << 8) | (pos & 0xfc), addr);
+ physmem_readl(addr); /* write barrier for address */
+
+ switch (len)
+ {
+ case 1:
+ physmem_writeb(buf[0], data);
+ break;
+ case 2:
+ physmem_writew(((u16 *) buf)[0], data);
+ break;
+ case 4:
+ physmem_writel(((u32 *) buf)[0], data);
+ break;
+ }
+
+ /*
+ * write barrier for data
+ * Note that we cannot read from data port because it may have side effect.
+ * Instead we read from address port (which should not have side effect) to
+ * create a barrier between two conf1_write() calls. But this does not have
+ * to be 100% correct as it does not ensure barrier on data port itself.
+ * Correct way is to issue CPU instruction for full hw sync barrier but gcc
+ * does not provide any (builtin) function yet.
+ */
+ physmem_readl(addr);
+
+ return 1;
+}
+
+static int
+conf1_write(struct pci_dev *d, int pos, byte *buf, int len)
+{
+ if (pos >= 256)
+ return 0;
+
+ return conf1_ext_write(d, pos, buf, len);
+}
+
+struct pci_methods pm_mmio_conf1 = {
+ "mmio-conf1",
+ "Raw memory mapped I/O port access using Intel conf1 interface",
+ conf1_config,
+ conf1_detect,
+ conf1_init,
+ conf1_cleanup,
+ conf1_scan,
+ pci_generic_fill_info,
+ conf1_read,
+ conf1_write,
+ NULL, /* read_vpd */
+ NULL, /* init_dev */
+ NULL /* cleanup_dev */
+};
+
+struct pci_methods pm_mmio_conf1_ext = {
+ "mmio-conf1-ext",
+ "Raw memory mapped I/O port access using Intel conf1 extended interface",
+ conf1_ext_config,
+ conf1_ext_detect,
+ conf1_init,
+ conf1_cleanup,
+ conf1_scan,
+ pci_generic_fill_info,
+ conf1_ext_read,
+ conf1_ext_write,
+ NULL, /* read_vpd */
+ NULL, /* init_dev */
+ NULL /* cleanup_dev */
+};
diff --git a/lib/names-cache.c b/lib/names-cache.c
new file mode 100644
index 0000000..16e9e9a
--- /dev/null
+++ b/lib/names-cache.c
@@ -0,0 +1,266 @@
+/*
+ * The PCI Library -- ID to Name Cache
+ *
+ * Copyright (c) 2008--2009 Martin Mares <mj@ucw.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "internal.h"
+#include "names.h"
+
+#ifdef PCI_USE_DNS
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <pwd.h>
+#include <unistd.h>
+
+static const char cache_version[] = "#PCI-CACHE-1.0";
+
+static char *get_cache_name(struct pci_access *a)
+{
+ if (!a->id_cache_name)
+ {
+ char *name = pci_get_param(a, "net.cache_name");
+ if (!name || !name[0])
+ return NULL;
+
+ if (strncmp(name, "~/", 2))
+ a->id_cache_name = pci_strdup(a, name);
+ else
+ {
+ uid_t uid = getuid();
+ struct passwd *pw = getpwuid(uid);
+ if (!pw)
+ return name;
+
+ a->id_cache_name = pci_malloc(a, strlen(pw->pw_dir) + strlen(name+1) + 1);
+ sprintf(a->id_cache_name, "%s%s", pw->pw_dir, name+1);
+ }
+ }
+
+ return a->id_cache_name;
+}
+
+static void create_parent_dirs(struct pci_access *a, char *name)
+{
+ // Assumes that we have a private copy of the name we can modify
+
+ char *p = name + strlen(name);
+ while (p > name && *p != '/')
+ p--;
+ if (p == name)
+ return;
+
+ while (p > name)
+ {
+ // We stand at a slash. Check if the current prefix exists.
+ *p = 0;
+ struct stat st;
+ int res = stat(name, &st);
+ *p = '/';
+ if (res >= 0)
+ break;
+
+ // Does not exist yet, move up one directory
+ p--;
+ while (p > name && *p != '/')
+ p--;
+ }
+
+ // We now stand at the end of the longest existing prefix.
+ // Create all directories to the right of it.
+ for (;;)
+ {
+ p++;
+ while (*p && *p != '/')
+ p++;
+ if (!*p)
+ break;
+
+ *p = 0;
+ int res = mkdir(name, 0777);
+ if (res < 0)
+ {
+ a->warning("Cannot create directory %s: %s", name, strerror(errno));
+ *p = '/';
+ break;
+ }
+ *p = '/';
+ }
+}
+
+int
+pci_id_cache_load(struct pci_access *a, int flags)
+{
+ char *name;
+ char line[MAX_LINE];
+ FILE *f;
+ int lino;
+
+ if (a->id_cache_status > 0)
+ return 0;
+ a->id_cache_status = 1;
+
+ name = get_cache_name(a);
+ if (!name)
+ return 0;
+ a->debug("Using cache %s\n", name);
+
+ if (flags & PCI_LOOKUP_REFRESH_CACHE)
+ {
+ a->debug("Not loading cache, will refresh everything\n");
+ a->id_cache_status = 2;
+ return 0;
+ }
+
+ f = fopen(name, "rb");
+ if (!f)
+ {
+ a->debug("Cache file does not exist\n");
+ return 0;
+ }
+ /* FIXME: Compare timestamp with the pci.ids file? */
+
+ lino = 0;
+ while (fgets(line, sizeof(line), f))
+ {
+ char *p = strchr(line, '\n');
+ lino++;
+ if (p)
+ {
+ *p = 0;
+ if (lino == 1)
+ {
+ if (strcmp(line, cache_version))
+ {
+ a->debug("Unrecognized cache version %s, ignoring\n", line);
+ break;
+ }
+ continue;
+ }
+ else
+ {
+ int cat, id1, id2, id3, id4, cnt;
+ if (sscanf(line, "%d%x%x%x%x%n", &cat, &id1, &id2, &id3, &id4, &cnt) >= 5)
+ {
+ p = line + cnt;
+ while (*p && *p == ' ')
+ p++;
+ pci_id_insert(a, cat, id1, id2, id3, id4, p, SRC_CACHE);
+ continue;
+ }
+ }
+ }
+ a->warning("Malformed cache file %s (line %d), ignoring", name, lino);
+ break;
+ }
+
+ if (ferror(f))
+ a->warning("Error while reading %s", name);
+ fclose(f);
+ return 1;
+}
+
+void
+pci_id_cache_flush(struct pci_access *a)
+{
+ int orig_status = a->id_cache_status;
+ FILE *f;
+ unsigned int h;
+ struct id_entry *e, *e2;
+ char hostname[256], *tmpname, *name;
+ int this_pid;
+
+ a->id_cache_status = 0;
+ if (orig_status < 2)
+ return;
+ name = get_cache_name(a);
+ if (!name)
+ return;
+
+ create_parent_dirs(a, name);
+
+ this_pid = getpid();
+ if (gethostname(hostname, sizeof(hostname)) < 0)
+ hostname[0] = 0;
+ else
+ hostname[sizeof(hostname)-1] = 0;
+ tmpname = pci_malloc(a, strlen(name) + strlen(hostname) + 64);
+ sprintf(tmpname, "%s.tmp-%s-%d", name, hostname, this_pid);
+
+ f = fopen(tmpname, "wb");
+ if (!f)
+ {
+ a->warning("Cannot write to %s: %s", name, strerror(errno));
+ pci_mfree(tmpname);
+ return;
+ }
+ a->debug("Writing cache to %s\n", name);
+ fprintf(f, "%s\n", cache_version);
+
+ for (h=0; h<HASH_SIZE; h++)
+ for (e=a->id_hash[h]; e; e=e->next)
+ if (e->src == SRC_CACHE || e->src == SRC_NET)
+ {
+ /* Negative entries are not written */
+ if (!e->name[0])
+ continue;
+
+ /* Verify that every entry is written at most once */
+ for (e2=a->id_hash[h]; e2 != e; e2=e2->next)
+ if ((e2->src == SRC_CACHE || e2->src == SRC_NET) &&
+ e2->cat == e->cat &&
+ e2->id12 == e->id12 && e2->id34 == e->id34)
+ break;
+ if (e2 == e)
+ fprintf(f, "%d %x %x %x %x %s\n",
+ e->cat,
+ pair_first(e->id12), pair_second(e->id12),
+ pair_first(e->id34), pair_second(e->id34),
+ e->name);
+ }
+
+ fflush(f);
+ if (ferror(f))
+ a->warning("Error writing %s", name);
+ fclose(f);
+
+ if (rename(tmpname, name) < 0)
+ {
+ a->warning("Cannot rename %s to %s: %s", tmpname, name, strerror(errno));
+ unlink(tmpname);
+ }
+ pci_mfree(tmpname);
+}
+
+#else
+
+int pci_id_cache_load(struct pci_access *a UNUSED, int flags UNUSED)
+{
+ a->id_cache_status = 1;
+ return 0;
+}
+
+void pci_id_cache_flush(struct pci_access *a)
+{
+ a->id_cache_status = 0;
+ pci_mfree(a->id_cache_name);
+ a->id_cache_name = NULL;
+}
+
+#endif
+
+void
+pci_id_cache_dirty(struct pci_access *a)
+{
+ if (a->id_cache_status >= 1)
+ a->id_cache_status = 2;
+}
diff --git a/lib/names-hash.c b/lib/names-hash.c
new file mode 100644
index 0000000..8c75676
--- /dev/null
+++ b/lib/names-hash.c
@@ -0,0 +1,130 @@
+/*
+ * The PCI Library -- ID to Name Hash
+ *
+ * Copyright (c) 1997--2008 Martin Mares <mj@ucw.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <string.h>
+
+#include "internal.h"
+#include "names.h"
+
+struct id_bucket {
+ struct id_bucket *next;
+ unsigned int full;
+};
+
+#ifdef __GNUC__
+#define BUCKET_ALIGNMENT __alignof__(struct id_bucket)
+#else
+union id_align {
+ struct id_bucket *next;
+ unsigned int full;
+};
+#define BUCKET_ALIGNMENT sizeof(union id_align)
+#endif
+#define BUCKET_ALIGN(n) ((n)+BUCKET_ALIGNMENT-(n)%BUCKET_ALIGNMENT)
+
+static void *id_alloc(struct pci_access *a, unsigned int size)
+{
+ struct id_bucket *buck = a->current_id_bucket;
+ unsigned int pos;
+
+ if (!a->id_hash)
+ {
+ a->id_hash = pci_malloc(a, sizeof(struct id_entry *) * HASH_SIZE);
+ memset(a->id_hash, 0, sizeof(struct id_entry *) * HASH_SIZE);
+ }
+
+ if (!buck || buck->full + size > BUCKET_SIZE)
+ {
+ buck = pci_malloc(a, BUCKET_SIZE);
+ buck->next = a->current_id_bucket;
+ a->current_id_bucket = buck;
+ buck->full = BUCKET_ALIGN(sizeof(struct id_bucket));
+ }
+ pos = buck->full;
+ buck->full = BUCKET_ALIGN(buck->full + size);
+ return (byte *)buck + pos;
+}
+
+static inline unsigned int id_hash(int cat, u32 id12, u32 id34)
+{
+ unsigned int h;
+
+ h = id12 ^ (id34 << 3) ^ (cat << 5);
+ return h % HASH_SIZE;
+}
+
+int
+pci_id_insert(struct pci_access *a, int cat, int id1, int id2, int id3, int id4, char *text, enum id_entry_src src)
+{
+ u32 id12 = id_pair(id1, id2);
+ u32 id34 = id_pair(id3, id4);
+ unsigned int h = id_hash(cat, id12, id34);
+ struct id_entry *n = a->id_hash ? a->id_hash[h] : NULL;
+ int len = strlen(text);
+
+ while (n && (n->id12 != id12 || n->id34 != id34 || n->cat != cat))
+ n = n->next;
+ if (n)
+ return 1;
+ n = id_alloc(a, sizeof(struct id_entry) + len);
+ n->id12 = id12;
+ n->id34 = id34;
+ n->cat = cat;
+ n->src = src;
+ memcpy(n->name, text, len+1);
+ n->next = a->id_hash[h];
+ a->id_hash[h] = n;
+ return 0;
+}
+
+char
+*pci_id_lookup(struct pci_access *a, int flags, int cat, int id1, int id2, int id3, int id4)
+{
+ struct id_entry *n, *best;
+ u32 id12 = id_pair(id1, id2);
+ u32 id34 = id_pair(id3, id4);
+
+ if (a->id_hash)
+ {
+ n = a->id_hash[id_hash(cat, id12, id34)];
+ best = NULL;
+ for (; n; n=n->next)
+ {
+ if (n->id12 != id12 || n->id34 != id34 || n->cat != cat)
+ continue;
+ if (n->src == SRC_LOCAL && (flags & PCI_LOOKUP_SKIP_LOCAL))
+ continue;
+ if (n->src == SRC_NET && !(flags & PCI_LOOKUP_NETWORK))
+ continue;
+ if (n->src == SRC_CACHE && !(flags & PCI_LOOKUP_CACHE))
+ continue;
+ if (n->src == SRC_HWDB && (flags & (PCI_LOOKUP_SKIP_LOCAL | PCI_LOOKUP_NO_HWDB)))
+ continue;
+ if (!best || best->src < n->src)
+ best = n;
+ }
+ if (best)
+ return best->name;
+ }
+ return NULL;
+}
+
+void
+pci_id_hash_free(struct pci_access *a)
+{
+ pci_mfree(a->id_hash);
+ a->id_hash = NULL;
+ while (a->current_id_bucket)
+ {
+ struct id_bucket *buck = a->current_id_bucket;
+ a->current_id_bucket = buck->next;
+ pci_mfree(buck);
+ }
+}
diff --git a/lib/names-hwdb.c b/lib/names-hwdb.c
new file mode 100644
index 0000000..71e7229
--- /dev/null
+++ b/lib/names-hwdb.c
@@ -0,0 +1,118 @@
+/*
+ * The PCI Library -- Looking up Names via UDEV and HWDB
+ *
+ * Copyright (c) 2013--2014 Tom Gundersen <teg@jklm.no>
+ * Copyright (c) 2014 Martin Mares <mj@ucw.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <string.h>
+
+#include "internal.h"
+#include "names.h"
+
+#ifdef PCI_HAVE_HWDB
+
+#include <libudev.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+char *
+pci_id_hwdb_lookup(struct pci_access *a, int cat, int id1, int id2, int id3, int id4)
+{
+ char modalias[64];
+ const char *key = NULL;
+
+ const char *disabled = pci_get_param(a, "hwdb.disable");
+ if (disabled && atoi(disabled))
+ return NULL;
+
+ switch (cat)
+ {
+ case ID_VENDOR:
+ sprintf(modalias, "pci:v%08X*", id1);
+ key = "ID_VENDOR_FROM_DATABASE";
+ break;
+ case ID_DEVICE:
+ sprintf(modalias, "pci:v%08Xd%08X*", id1, id2);
+ key = "ID_MODEL_FROM_DATABASE";
+ break;
+ case ID_SUBSYSTEM:
+ sprintf(modalias, "pci:v%08Xd%08Xsv%08Xsd%08X*", id1, id2, id3, id4);
+ key = "ID_MODEL_FROM_DATABASE";
+ break;
+ case ID_GEN_SUBSYSTEM:
+ sprintf(modalias, "pci:v*d*sv%08Xsd%08X*", id1, id2);
+ key = "ID_MODEL_FROM_DATABASE";
+ break;
+ case ID_CLASS:
+ sprintf(modalias, "pci:v*d*sv*sd*bc%02X*", id1);
+ key = "ID_PCI_CLASS_FROM_DATABASE";
+ break;
+ case ID_SUBCLASS:
+ sprintf(modalias, "pci:v*d*sv*sd*bc%02Xsc%02X*", id1, id2);
+ key = "ID_PCI_SUBCLASS_FROM_DATABASE";
+ break;
+ case ID_PROGIF:
+ sprintf(modalias, "pci:v*d*sv*sd*bc%02Xsc%02Xi%02X*", id1, id2, id3);
+ key = "ID_PCI_INTERFACE_FROM_DATABASE";
+ break;
+ }
+
+ if (key)
+ {
+ if (!a->id_udev_hwdb)
+ {
+ a->debug("Initializing UDEV HWDB\n");
+ a->id_udev = udev_new();
+ a->id_udev_hwdb = udev_hwdb_new(a->id_udev);
+ }
+
+ struct udev_list_entry *entry;
+ udev_list_entry_foreach(entry, udev_hwdb_get_properties_list_entry(a->id_udev_hwdb, modalias, 0))
+ {
+ const char *entry_name = udev_list_entry_get_name(entry);
+ if (entry_name && !strcmp(entry_name, key))
+ {
+ const char *entry_value = udev_list_entry_get_value(entry);
+ if (entry_value)
+ return pci_strdup(a, entry_value);
+ }
+ }
+ }
+
+ return NULL;
+}
+
+void
+pci_id_hwdb_free(struct pci_access *a)
+{
+ if (a->id_udev_hwdb)
+ {
+ udev_hwdb_unref(a->id_udev_hwdb);
+ a->id_udev_hwdb = NULL;
+ }
+ if (a->id_udev)
+ {
+ udev_unref(a->id_udev);
+ a->id_udev = NULL;
+ }
+}
+
+#else
+
+char *
+pci_id_hwdb_lookup(struct pci_access *a UNUSED, int cat UNUSED, int id1 UNUSED, int id2 UNUSED, int id3 UNUSED, int id4 UNUSED)
+{
+ return NULL;
+}
+
+void
+pci_id_hwdb_free(struct pci_access *a UNUSED)
+{
+}
+
+#endif
diff --git a/lib/names-net.c b/lib/names-net.c
new file mode 100644
index 0000000..14141f4
--- /dev/null
+++ b/lib/names-net.c
@@ -0,0 +1,252 @@
+/*
+ * The PCI Library -- Resolving ID's via DNS
+ *
+ * Copyright (c) 2007--2008 Martin Mares <mj@ucw.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "internal.h"
+#include "names.h"
+
+#ifdef PCI_USE_DNS
+
+/*
+ * Our definition of BYTE_ORDER confuses arpa/nameser_compat.h on
+ * Solaris so we must undef it before including arpa/nameser.h.
+ */
+#ifdef PCI_OS_SUNOS
+#undef BYTE_ORDER
+#endif
+
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+#include <netdb.h>
+
+/*
+ * Unfortunately, there are no portable functions for DNS RR parsing,
+ * so we will do the bit shuffling with our own bare hands.
+ */
+
+#define GET16(x) do { if (p+2 > end) goto err; x = (p[0] << 8) | p[1]; p += 2; } while (0)
+#define GET32(x) do { if (p+4 > end) goto err; x = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; p += 4; } while (0)
+
+enum dns_section {
+ DNS_SEC_QUESTION,
+ DNS_SEC_ANSWER,
+ DNS_SEC_AUTHORITY,
+ DNS_SEC_ADDITIONAL,
+ DNS_NUM_SECTIONS
+};
+
+struct dns_state {
+ u16 counts[DNS_NUM_SECTIONS];
+ byte *sections[DNS_NUM_SECTIONS+1];
+ byte *sec_ptr, *sec_end;
+
+ /* Result of dns_parse_rr(): */
+ u16 rr_type;
+ u16 rr_class;
+ u32 rr_ttl;
+ u16 rr_len;
+ byte *rr_data;
+};
+
+static byte *
+dns_skip_name(byte *p, byte *end)
+{
+ while (p < end)
+ {
+ unsigned int x = *p++;
+ if (!x)
+ return p;
+ switch (x & 0xc0)
+ {
+ case 0: /* Uncompressed: x = length */
+ p += x;
+ break;
+ case 0xc0: /* Indirection: 1 byte more for offset */
+ p++;
+ return (p < end) ? p : NULL;
+ default: /* RFU */
+ return NULL;
+ }
+ }
+ return NULL;
+}
+
+static int
+dns_parse_packet(struct dns_state *s, byte *p, unsigned int plen)
+{
+ byte *end = p + plen;
+ unsigned int i, j, len;
+ unsigned int UNUSED x;
+
+#if 0
+ /* Dump the packet */
+ for (i=0; i<plen; i++)
+ {
+ if (!(i%16)) printf("%04x:", i);
+ printf(" %02x", p[i]);
+ if ((i%16)==15 || i==plen-1) putchar('\n');
+ }
+#endif
+
+ GET32(x); /* ID and flags are ignored */
+ for (i=0; i<DNS_NUM_SECTIONS; i++)
+ GET16(s->counts[i]);
+ for (i=0; i<DNS_NUM_SECTIONS; i++)
+ {
+ s->sections[i] = p;
+ for (j=0; j < s->counts[i]; j++)
+ {
+ p = dns_skip_name(p, end); /* Name */
+ if (!p)
+ goto err;
+ GET32(x); /* Type and class */
+ if (i != DNS_SEC_QUESTION)
+ {
+ GET32(x); /* TTL */
+ GET16(len); /* Length of data */
+ p += len;
+ if (p > end)
+ goto err;
+ }
+ }
+ }
+ s->sections[i] = p;
+ return 0;
+
+err:
+ return -1;
+}
+
+static void
+dns_init_section(struct dns_state *s, int i)
+{
+ s->sec_ptr = s->sections[i];
+ s->sec_end = s->sections[i+1];
+}
+
+static int
+dns_parse_rr(struct dns_state *s)
+{
+ byte *p = s->sec_ptr;
+ byte *end = s->sec_end;
+
+ if (p == end)
+ return 0;
+ p = dns_skip_name(p, end);
+ if (!p)
+ goto err;
+ GET16(s->rr_type);
+ GET16(s->rr_class);
+ GET32(s->rr_ttl);
+ GET16(s->rr_len);
+ s->rr_data = p;
+ s->sec_ptr = p + s->rr_len;
+ return 1;
+
+err:
+ return -1;
+}
+
+char
+*pci_id_net_lookup(struct pci_access *a, int cat, int id1, int id2, int id3, int id4)
+{
+ static int resolver_inited;
+ char name[256], dnsname[256], txt[256], *domain;
+ byte answer[4096];
+ const byte *data;
+ int res, j, dlen;
+ struct dns_state ds;
+
+ domain = pci_get_param(a, "net.domain");
+ if (!domain || !domain[0])
+ return NULL;
+
+ switch (cat)
+ {
+ case ID_VENDOR:
+ sprintf(name, "%04x", id1);
+ break;
+ case ID_DEVICE:
+ sprintf(name, "%04x.%04x", id2, id1);
+ break;
+ case ID_SUBSYSTEM:
+ sprintf(name, "%04x.%04x.%04x.%04x", id4, id3, id2, id1);
+ break;
+ case ID_GEN_SUBSYSTEM:
+ sprintf(name, "%04x.%04x.s", id2, id1);
+ break;
+ case ID_CLASS:
+ sprintf(name, "%02x.c", id1);
+ break;
+ case ID_SUBCLASS:
+ sprintf(name, "%02x.%02x.c", id2, id1);
+ break;
+ case ID_PROGIF:
+ sprintf(name, "%02x.%02x.%02x.c", id3, id2, id1);
+ break;
+ default:
+ return NULL;
+ }
+ sprintf(dnsname, "%.100s.%.100s", name, domain);
+
+ a->debug("Resolving %s\n", dnsname);
+ if (!resolver_inited)
+ {
+ resolver_inited = 1;
+ res_init();
+ }
+ res = res_query(dnsname, ns_c_in, ns_t_txt, answer, sizeof(answer));
+ if (res < 0)
+ {
+ a->debug("\tfailed, h_errno=%d\n", h_errno);
+ return NULL;
+ }
+ if (dns_parse_packet(&ds, answer, res) < 0)
+ {
+ a->debug("\tMalformed DNS packet received\n");
+ return NULL;
+ }
+ dns_init_section(&ds, DNS_SEC_ANSWER);
+ while (dns_parse_rr(&ds) > 0)
+ {
+ if (ds.rr_class != ns_c_in || ds.rr_type != ns_t_txt)
+ {
+ a->debug("\tUnexpected RR in answer: class %d, type %d\n", ds.rr_class, ds.rr_type);
+ continue;
+ }
+ data = ds.rr_data;
+ dlen = ds.rr_len;
+ j = 0;
+ while (j < dlen && j+1+data[j] <= dlen)
+ {
+ memcpy(txt, &data[j+1], data[j]);
+ txt[data[j]] = 0;
+ j += 1+data[j];
+ a->debug("\t\"%s\"\n", txt);
+ if (txt[0] == 'i' && txt[1] == '=')
+ return strdup(txt+2);
+ }
+ }
+
+ return NULL;
+}
+
+#else
+
+char *pci_id_net_lookup(struct pci_access *a UNUSED, int cat UNUSED, int id1 UNUSED, int id2 UNUSED, int id3 UNUSED, int id4 UNUSED)
+{
+ return NULL;
+}
+
+#endif
diff --git a/lib/names-parse.c b/lib/names-parse.c
new file mode 100644
index 0000000..1f8925a
--- /dev/null
+++ b/lib/names-parse.c
@@ -0,0 +1,253 @@
+/*
+ * The PCI Library -- Parsing of the ID list
+ *
+ * Copyright (c) 1997--2008 Martin Mares <mj@ucw.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "internal.h"
+#include "names.h"
+
+#ifdef PCI_COMPRESSED_IDS
+#include <zlib.h>
+typedef gzFile pci_file;
+#define pci_gets(f, l, s) gzgets(f, l, s)
+#define pci_eof(f) gzeof(f)
+
+static pci_file pci_open(struct pci_access *a)
+{
+ pci_file result;
+ size_t len;
+ char *new_name;
+
+ result = gzopen(a->id_file_name, "rb");
+ if (result)
+ return result;
+ len = strlen(a->id_file_name);
+ if (len < 3 || memcmp(a->id_file_name + len - 3, ".gz", 3) != 0)
+ return result;
+ new_name = malloc(len - 2);
+ memcpy(new_name, a->id_file_name, len - 3);
+ new_name[len - 3] = 0;
+ pci_set_name_list_path(a, new_name, 1);
+ return gzopen(a->id_file_name, "rb");
+}
+
+#define pci_close(f) gzclose(f)
+#define PCI_ERROR(f, err) \
+ if (!err) { \
+ int errnum = 0; \
+ gzerror(f, &errnum); \
+ if (errnum >= 0) err = NULL; \
+ else if (errnum == Z_ERRNO) err = "I/O error"; \
+ else err = zError(errnum); \
+ }
+#else
+typedef FILE * pci_file;
+#define pci_gets(f, l, s) fgets(l, s, f)
+#define pci_eof(f) feof(f)
+#define pci_open(a) fopen(a->id_file_name, "r")
+#define pci_close(f) fclose(f)
+#define PCI_ERROR(f, err) if (!err && ferror(f)) err = "I/O error";
+#endif
+
+static int id_hex(char *p, int cnt)
+{
+ int x = 0;
+ while (cnt--)
+ {
+ x <<= 4;
+ if (*p >= '0' && *p <= '9')
+ x += (*p - '0');
+ else if (*p >= 'a' && *p <= 'f')
+ x += (*p - 'a' + 10);
+ else if (*p >= 'A' && *p <= 'F')
+ x += (*p - 'A' + 10);
+ else
+ return -1;
+ p++;
+ }
+ return x;
+}
+
+static inline int id_white_p(int c)
+{
+ return (c == ' ') || (c == '\t');
+}
+
+
+static const char *id_parse_list(struct pci_access *a, pci_file f, int *lino)
+{
+ char line[MAX_LINE];
+ char *p;
+ int id1=0, id2=0, id3=0, id4=0;
+ int cat = -1;
+ int nest;
+ static const char parse_error[] = "Parse error";
+
+ *lino = 0;
+ while (pci_gets(f, line, sizeof(line)))
+ {
+ (*lino)++;
+ p = line;
+ while (*p && *p != '\n' && *p != '\r')
+ p++;
+ if (!*p && !pci_eof(f))
+ return "Line too long";
+ *p = 0;
+ if (p > line && (p[-1] == ' ' || p[-1] == '\t'))
+ *--p = 0;
+
+ p = line;
+ while (id_white_p(*p))
+ p++;
+ if (!*p || *p == '#')
+ continue;
+
+ p = line;
+ while (*p == '\t')
+ p++;
+ nest = p - line;
+
+ if (!nest) /* Top-level entries */
+ {
+ if (p[0] == 'C' && p[1] == ' ') /* Class block */
+ {
+ if ((id1 = id_hex(p+2, 2)) < 0 || !id_white_p(p[4]))
+ return parse_error;
+ cat = ID_CLASS;
+ p += 5;
+ }
+ else if (p[0] == 'S' && p[1] == ' ')
+ { /* Generic subsystem block */
+ if ((id1 = id_hex(p+2, 4)) < 0 || p[6])
+ return parse_error;
+ if (!pci_id_lookup(a, 0, ID_VENDOR, id1, 0, 0, 0))
+ return "Vendor does not exist";
+ cat = ID_GEN_SUBSYSTEM;
+ continue;
+ }
+ else if (p[0] >= 'A' && p[0] <= 'Z' && p[1] == ' ')
+ { /* Unrecognized block (RFU) */
+ cat = ID_UNKNOWN;
+ continue;
+ }
+ else /* Vendor ID */
+ {
+ if ((id1 = id_hex(p, 4)) < 0 || !id_white_p(p[4]))
+ return parse_error;
+ cat = ID_VENDOR;
+ p += 5;
+ }
+ id2 = id3 = id4 = 0;
+ }
+ else if (cat == ID_UNKNOWN) /* Nested entries in RFU blocks are skipped */
+ continue;
+ else if (nest == 1) /* Nesting level 1 */
+ switch (cat)
+ {
+ case ID_VENDOR:
+ case ID_DEVICE:
+ case ID_SUBSYSTEM:
+ if ((id2 = id_hex(p, 4)) < 0 || !id_white_p(p[4]))
+ return parse_error;
+ p += 5;
+ cat = ID_DEVICE;
+ id3 = id4 = 0;
+ break;
+ case ID_GEN_SUBSYSTEM:
+ if ((id2 = id_hex(p, 4)) < 0 || !id_white_p(p[4]))
+ return parse_error;
+ p += 5;
+ id3 = id4 = 0;
+ break;
+ case ID_CLASS:
+ case ID_SUBCLASS:
+ case ID_PROGIF:
+ if ((id2 = id_hex(p, 2)) < 0 || !id_white_p(p[2]))
+ return parse_error;
+ p += 3;
+ cat = ID_SUBCLASS;
+ id3 = id4 = 0;
+ break;
+ default:
+ return parse_error;
+ }
+ else if (nest == 2) /* Nesting level 2 */
+ switch (cat)
+ {
+ case ID_DEVICE:
+ case ID_SUBSYSTEM:
+ if ((id3 = id_hex(p, 4)) < 0 || !id_white_p(p[4]) || (id4 = id_hex(p+5, 4)) < 0 || !id_white_p(p[9]))
+ return parse_error;
+ p += 10;
+ cat = ID_SUBSYSTEM;
+ break;
+ case ID_CLASS:
+ case ID_SUBCLASS:
+ case ID_PROGIF:
+ if ((id3 = id_hex(p, 2)) < 0 || !id_white_p(p[2]))
+ return parse_error;
+ p += 3;
+ cat = ID_PROGIF;
+ id4 = 0;
+ break;
+ default:
+ return parse_error;
+ }
+ else /* Nesting level 3 or more */
+ return parse_error;
+ while (id_white_p(*p))
+ p++;
+ if (!*p)
+ return parse_error;
+ if (pci_id_insert(a, cat, id1, id2, id3, id4, p, SRC_LOCAL))
+ return "Duplicate entry";
+ }
+ return NULL;
+}
+
+int
+pci_load_name_list(struct pci_access *a)
+{
+ pci_file f;
+ int lino;
+ const char *err;
+
+ pci_free_name_list(a);
+ a->id_load_attempted = 1;
+ if (!(f = pci_open(a)))
+ return 0;
+ err = id_parse_list(a, f, &lino);
+ PCI_ERROR(f, err);
+ pci_close(f);
+ if (err)
+ a->error("%s at %s, line %d\n", err, a->id_file_name, lino);
+ return 1;
+}
+
+void
+pci_free_name_list(struct pci_access *a)
+{
+ pci_id_cache_flush(a);
+ pci_id_hash_free(a);
+ pci_id_hwdb_free(a);
+ a->id_load_attempted = 0;
+}
+
+void
+pci_set_name_list_path(struct pci_access *a, char *name, int to_be_freed)
+{
+ if (a->free_id_name)
+ free(a->id_file_name);
+ a->id_file_name = name;
+ a->free_id_name = to_be_freed;
+}
diff --git a/lib/names.c b/lib/names.c
new file mode 100644
index 0000000..a287cb0
--- /dev/null
+++ b/lib/names.c
@@ -0,0 +1,229 @@
+/*
+ * The PCI Library -- ID to Name Translation
+ *
+ * Copyright (c) 1997--2014 Martin Mares <mj@ucw.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include "internal.h"
+#include "names.h"
+
+static char *id_lookup(struct pci_access *a, int flags, int cat, int id1, int id2, int id3, int id4)
+{
+ char *name;
+ int tried_hwdb = 0;
+
+ while (!(name = pci_id_lookup(a, flags, cat, id1, id2, id3, id4)))
+ {
+ if ((flags & PCI_LOOKUP_CACHE) && !a->id_cache_status)
+ {
+ if (pci_id_cache_load(a, flags))
+ continue;
+ }
+ if (!tried_hwdb && !(flags & (PCI_LOOKUP_SKIP_LOCAL | PCI_LOOKUP_NO_HWDB)))
+ {
+ tried_hwdb = 1;
+ if (name = pci_id_hwdb_lookup(a, cat, id1, id2, id3, id4))
+ {
+ pci_id_insert(a, cat, id1, id2, id3, id4, name, SRC_HWDB);
+ pci_mfree(name);
+ continue;
+ }
+ }
+ if (flags & PCI_LOOKUP_NETWORK)
+ {
+ if (name = pci_id_net_lookup(a, cat, id1, id2, id3, id4))
+ {
+ pci_id_insert(a, cat, id1, id2, id3, id4, name, SRC_NET);
+ pci_mfree(name);
+ pci_id_cache_dirty(a);
+ }
+ else
+ pci_id_insert(a, cat, id1, id2, id3, id4, "", SRC_NET);
+ /* We want to iterate the lookup to get the allocated ID entry from the hash */
+ continue;
+ }
+ return NULL;
+ }
+ return (name[0] ? name : NULL);
+}
+
+static char *
+id_lookup_subsys(struct pci_access *a, int flags, int iv, int id, int isv, int isd)
+{
+ char *d = NULL;
+ if (iv > 0 && id > 0) /* Per-device lookup */
+ d = id_lookup(a, flags, ID_SUBSYSTEM, iv, id, isv, isd);
+ if (!d) /* Generic lookup */
+ d = id_lookup(a, flags, ID_GEN_SUBSYSTEM, isv, isd, 0, 0);
+ if (!d && iv == isv && id == isd) /* Check for subsystem == device */
+ d = id_lookup(a, flags, ID_DEVICE, iv, id, 0, 0);
+ return d;
+}
+
+static char *
+format_name(char *buf, int size, int flags, char *name, char *num, char *unknown)
+{
+ int res;
+ if ((flags & PCI_LOOKUP_NO_NUMBERS) && !name)
+ return NULL;
+ else if (flags & PCI_LOOKUP_NUMERIC)
+ res = snprintf(buf, size, "%s", num);
+ else if (!name)
+ res = snprintf(buf, size, ((flags & PCI_LOOKUP_MIXED) ? "%s [%s]" : "%s %s"), unknown, num);
+ else if (!(flags & PCI_LOOKUP_MIXED))
+ res = snprintf(buf, size, "%s", name);
+ else
+ res = snprintf(buf, size, "%s [%s]", name, num);
+ if (res >= size && size >= 4)
+ buf[size-2] = buf[size-3] = buf[size-4] = '.';
+ else if (res < 0 || res >= size)
+ return "<pci_lookup_name: buffer too small>";
+ return buf;
+}
+
+static char *
+format_name_pair(char *buf, int size, int flags, char *v, char *d, char *num)
+{
+ int res;
+ if ((flags & PCI_LOOKUP_NO_NUMBERS) && (!v || !d))
+ return NULL;
+ if (flags & PCI_LOOKUP_NUMERIC)
+ res = snprintf(buf, size, "%s", num);
+ else if (flags & PCI_LOOKUP_MIXED)
+ {
+ if (v && d)
+ res = snprintf(buf, size, "%s %s [%s]", v, d, num);
+ else if (!v)
+ res = snprintf(buf, size, "Device [%s]", num);
+ else /* v && !d */
+ res = snprintf(buf, size, "%s Device [%s]", v, num);
+ }
+ else
+ {
+ if (v && d)
+ res = snprintf(buf, size, "%s %s", v, d);
+ else if (!v)
+ res = snprintf(buf, size, "Device %s", num);
+ else /* v && !d */
+ res = snprintf(buf, size, "%s Device %s", v, num+5);
+ }
+ if (res >= size && size >= 4)
+ buf[size-2] = buf[size-3] = buf[size-4] = '.';
+ else if (res < 0 || res >= size)
+ return "<pci_lookup_name: buffer too small>";
+ return buf;
+}
+
+char *
+pci_lookup_name(struct pci_access *a, char *buf, int size, int flags, ...)
+{
+ va_list args;
+ char *v, *d, *cls, *pif;
+ int iv, id, isv, isd, icls, ipif;
+ char numbuf[16], pifbuf[32];
+
+ va_start(args, flags);
+
+ flags |= a->id_lookup_mode;
+ if (!(flags & PCI_LOOKUP_NO_NUMBERS))
+ {
+ if (a->numeric_ids > 1)
+ flags |= PCI_LOOKUP_MIXED;
+ else if (a->numeric_ids)
+ flags |= PCI_LOOKUP_NUMERIC;
+ }
+ if (flags & PCI_LOOKUP_MIXED)
+ flags &= ~PCI_LOOKUP_NUMERIC;
+
+ if (!a->id_load_attempted && !(flags & (PCI_LOOKUP_NUMERIC | PCI_LOOKUP_SKIP_LOCAL)))
+ pci_load_name_list(a);
+
+ switch (flags & 0xffff)
+ {
+ case PCI_LOOKUP_VENDOR:
+ iv = va_arg(args, int);
+ sprintf(numbuf, "%04x", iv);
+ va_end(args);
+ return format_name(buf, size, flags, id_lookup(a, flags, ID_VENDOR, iv, 0, 0, 0), numbuf, "Vendor");
+ case PCI_LOOKUP_DEVICE:
+ iv = va_arg(args, int);
+ id = va_arg(args, int);
+ sprintf(numbuf, "%04x", id);
+ va_end(args);
+ return format_name(buf, size, flags, id_lookup(a, flags, ID_DEVICE, iv, id, 0, 0), numbuf, "Device");
+ case PCI_LOOKUP_VENDOR | PCI_LOOKUP_DEVICE:
+ iv = va_arg(args, int);
+ id = va_arg(args, int);
+ sprintf(numbuf, "%04x:%04x", iv, id);
+ v = id_lookup(a, flags, ID_VENDOR, iv, 0, 0, 0);
+ d = id_lookup(a, flags, ID_DEVICE, iv, id, 0, 0);
+ va_end(args);
+ return format_name_pair(buf, size, flags, v, d, numbuf);
+ case PCI_LOOKUP_SUBSYSTEM | PCI_LOOKUP_VENDOR:
+ isv = va_arg(args, int);
+ sprintf(numbuf, "%04x", isv);
+ v = id_lookup(a, flags, ID_VENDOR, isv, 0, 0, 0);
+ va_end(args);
+ return format_name(buf, size, flags, v, numbuf, "Unknown vendor");
+ case PCI_LOOKUP_SUBSYSTEM | PCI_LOOKUP_DEVICE:
+ iv = va_arg(args, int);
+ id = va_arg(args, int);
+ isv = va_arg(args, int);
+ isd = va_arg(args, int);
+ sprintf(numbuf, "%04x", isd);
+ va_end(args);
+ return format_name(buf, size, flags, id_lookup_subsys(a, flags, iv, id, isv, isd), numbuf, "Device");
+ case PCI_LOOKUP_VENDOR | PCI_LOOKUP_DEVICE | PCI_LOOKUP_SUBSYSTEM:
+ iv = va_arg(args, int);
+ id = va_arg(args, int);
+ isv = va_arg(args, int);
+ isd = va_arg(args, int);
+ v = id_lookup(a, flags, ID_VENDOR, isv, 0, 0, 0);
+ d = id_lookup_subsys(a, flags, iv, id, isv, isd);
+ sprintf(numbuf, "%04x:%04x", isv, isd);
+ va_end(args);
+ return format_name_pair(buf, size, flags, v, d, numbuf);
+ case PCI_LOOKUP_CLASS:
+ icls = va_arg(args, int);
+ sprintf(numbuf, "%04x", icls);
+ cls = id_lookup(a, flags, ID_SUBCLASS, icls >> 8, icls & 0xff, 0, 0);
+ if (!cls && (cls = id_lookup(a, flags, ID_CLASS, icls >> 8, 0, 0, 0)))
+ {
+ if (!(flags & PCI_LOOKUP_NUMERIC)) /* Include full class number */
+ flags |= PCI_LOOKUP_MIXED;
+ }
+ va_end(args);
+ return format_name(buf, size, flags, cls, numbuf, "Class");
+ case PCI_LOOKUP_PROGIF:
+ icls = va_arg(args, int);
+ ipif = va_arg(args, int);
+ sprintf(numbuf, "%02x", ipif);
+ pif = id_lookup(a, flags, ID_PROGIF, icls >> 8, icls & 0xff, ipif, 0);
+ if (!pif && icls == 0x0101 && !(ipif & 0x70))
+ {
+ /* IDE controllers have complex prog-if semantics */
+ sprintf(pifbuf, "%s%s%s%s%s",
+ (ipif & 0x80) ? " Master" : "",
+ (ipif & 0x08) ? " SecP" : "",
+ (ipif & 0x04) ? " SecO" : "",
+ (ipif & 0x02) ? " PriP" : "",
+ (ipif & 0x01) ? " PriO" : "");
+ pif = pifbuf;
+ if (*pif)
+ pif++;
+ }
+ va_end(args);
+ return format_name(buf, size, flags, pif, numbuf, "ProgIf");
+ default:
+ va_end(args);
+ return "<pci_lookup_name: invalid request>";
+ }
+}
diff --git a/lib/names.h b/lib/names.h
new file mode 100644
index 0000000..b34e3e6
--- /dev/null
+++ b/lib/names.h
@@ -0,0 +1,77 @@
+/*
+ * The PCI Library -- ID to Name Translation
+ *
+ * Copyright (c) 1997--2014 Martin Mares <mj@ucw.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#define MAX_LINE 1024
+
+/* names-hash.c */
+
+struct id_entry {
+ struct id_entry *next;
+ u32 id12, id34;
+ byte cat;
+ byte src;
+ char name[1];
+};
+
+enum id_entry_type {
+ ID_UNKNOWN,
+ ID_VENDOR,
+ ID_DEVICE,
+ ID_SUBSYSTEM,
+ ID_GEN_SUBSYSTEM,
+ ID_CLASS,
+ ID_SUBCLASS,
+ ID_PROGIF
+};
+
+enum id_entry_src {
+ SRC_UNKNOWN,
+ SRC_CACHE,
+ SRC_NET,
+ SRC_HWDB,
+ SRC_LOCAL,
+};
+
+#define BUCKET_SIZE 8192
+#define HASH_SIZE 4099
+
+static inline u32 id_pair(unsigned int x, unsigned int y)
+{
+ return ((x << 16) | y);
+}
+
+static inline unsigned int pair_first(unsigned int x)
+{
+ return (x >> 16) & 0xffff;
+}
+
+static inline unsigned int pair_second(unsigned int x)
+{
+ return x & 0xffff;
+}
+
+int pci_id_insert(struct pci_access *a, int cat, int id1, int id2, int id3, int id4, char *text, enum id_entry_src src);
+char *pci_id_lookup(struct pci_access *a, int flags, int cat, int id1, int id2, int id3, int id4);
+
+/* names-cache.c */
+
+int pci_id_cache_load(struct pci_access *a, int flags);
+void pci_id_cache_dirty(struct pci_access *a);
+void pci_id_cache_flush(struct pci_access *a);
+void pci_id_hash_free(struct pci_access *a);
+
+/* names-dns.c */
+
+char *pci_id_net_lookup(struct pci_access *a, int cat, int id1, int id2, int id3, int id4);
+
+/* names-hwdb.c */
+
+char *pci_id_hwdb_lookup(struct pci_access *a, int cat, int id1, int id2, int id3, int id4);
+void pci_id_hwdb_free(struct pci_access *a);
diff --git a/lib/nbsd-libpci.c b/lib/nbsd-libpci.c
new file mode 100644
index 0000000..0f9f27b
--- /dev/null
+++ b/lib/nbsd-libpci.c
@@ -0,0 +1,159 @@
+/*
+ * The PCI Library -- NetBSD libpci access
+ * (based on FreeBSD /dev/pci access)
+ *
+ * Copyright (c) 1999 Jari Kirma <kirma@cs.hut.fi>
+ * Copyright (c) 2002 Quentin Garnier <cube@cubidou.net>
+ * Copyright (c) 2002 Martin Mares <mj@ucw.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/*
+ * Read functionality of this driver is briefly tested, and seems
+ * to supply basic information correctly, but I promise no more.
+ */
+
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <pci.h>
+
+#include "internal.h"
+
+static void
+nbsd_config(struct pci_access *a)
+{
+ pci_define_param(a, "nbsd.path", PCI_PATH_NBSD_DEVICE, "Path to the NetBSD PCI device");
+}
+
+static int
+nbsd_detect(struct pci_access *a)
+{
+ char *name = pci_get_param(a, "nbsd.path");
+
+ if (access(name, R_OK))
+ {
+ a->warning("Cannot open %s", name);
+ return 0;
+ }
+
+ if (!access(name, W_OK))
+ a->writeable = O_RDWR;
+ a->debug("...using %s", name);
+ return 1;
+}
+
+static void
+nbsd_init(struct pci_access *a)
+{
+ char *name = pci_get_param(a, "nbsd.path");
+ int mode = a->writeable ? O_RDWR : O_RDONLY;
+
+ a->fd = open(name, mode, 0);
+ if (a->fd < 0)
+ a->error("nbsd_init: %s open failed", name);
+}
+
+static void
+nbsd_cleanup(struct pci_access *a)
+{
+ close(a->fd);
+}
+
+static int
+nbsd_read(struct pci_dev *d, int pos, byte *buf, int len)
+{
+ pcireg_t val;
+ int shift;
+
+ if (!(len == 1 || len == 2 || len == 4))
+ return pci_generic_block_read(d, pos, buf, len);
+
+ if (d->domain || pos >= 4096)
+ return 0;
+
+ shift = 8*(pos % 4);
+ pos &= ~3;
+
+ if (pcibus_conf_read(d->access->fd, d->bus, d->dev, d->func, pos, &val) < 0)
+ d->access->error("nbsd_read: pci_bus_conf_read() failed");
+
+ switch (len)
+ {
+ case 1:
+ *buf = val >> shift;
+ break;
+ case 2:
+ *(u16*)buf = cpu_to_le16(val >> shift);
+ break;
+ case 4:
+ *(u32*)buf = cpu_to_le32(val);
+ break;
+ }
+ return 1;
+}
+
+static int
+nbsd_write(struct pci_dev *d, int pos, byte *buf, int len)
+{
+ pcireg_t val = 0;
+ int shift;
+
+ if (!(len == 1 || len == 2 || len == 4))
+ return pci_generic_block_write(d, pos, buf, len);
+
+ if (d->domain || pos >= 256)
+ return 0;
+
+ /*
+ * BEWARE: NetBSD seems to support only 32-bit access, so we have
+ * to emulate byte and word writes by read-modify-write, possibly
+ * causing troubles.
+ */
+
+ shift = 8*(pos % 4);
+ pos &= ~3;
+ if (len != 4)
+ {
+ if (pcibus_conf_read(d->access->fd, d->bus, d->dev, d->func, pos, &val) < 0)
+ d->access->error("nbsd_write: pci_bus_conf_read() failed");
+ }
+
+ switch (len)
+ {
+ case 1:
+ val = (val & ~(0xff << shift)) | (buf[0] << shift);
+ break;
+ case 2:
+ val = (val & ~(0xffff << shift)) | (le16_to_cpu(*(u16*)buf) << shift);
+ break;
+ case 4:
+ val = le32_to_cpu(*(u32*)buf);
+ break;
+ }
+
+ if (pcibus_conf_write(d->access->fd, d->bus, d->dev, d->func, pos, val) < 0)
+ d->access->error("nbsd_write: pci_bus_conf_write() failed");
+
+ return 1;
+}
+
+struct pci_methods pm_nbsd_libpci = {
+ "nbsd-libpci",
+ "NetBSD libpci",
+ nbsd_config,
+ nbsd_detect,
+ nbsd_init,
+ nbsd_cleanup,
+ pci_generic_scan,
+ pci_generic_fill_info,
+ nbsd_read,
+ nbsd_write,
+ NULL, /* read_vpd */
+ NULL, /* dev_init */
+ NULL /* dev_cleanup */
+};
diff --git a/lib/obsd-device.c b/lib/obsd-device.c
new file mode 100644
index 0000000..e2e7652
--- /dev/null
+++ b/lib/obsd-device.c
@@ -0,0 +1,154 @@
+/*
+ * The PCI Library -- OpenBSD /dev/pci access
+ *
+ * Adapted from fbsd-device.c by Matthieu Herrb <matthieu.herrb@laas.fr>, 2006
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/endian.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/pciio.h>
+#include "internal.h"
+
+static void
+obsd_config(struct pci_access *a)
+{
+ pci_define_param(a, "obsd.path", PCI_PATH_OBSD_DEVICE, "Path to the OpenBSD PCI device");
+}
+
+static int
+obsd_detect(struct pci_access *a)
+{
+ char *name = pci_get_param(a, "obsd.path");
+
+ if (access(name, R_OK))
+ {
+ a->warning("Cannot open %s", name);
+ return 0;
+ }
+ a->debug("...using %s", name);
+ return 1;
+}
+
+static void
+obsd_init(struct pci_access *a)
+{
+ char *name = pci_get_param(a, "obsd.path");
+
+ a->fd = open(name, O_RDWR, 0);
+ if (a->fd < 0)
+ a->error("obsd_init: %s open failed", name);
+}
+
+static void
+obsd_cleanup(struct pci_access *a)
+{
+ close(a->fd);
+}
+
+static int
+obsd_read(struct pci_dev *d, int pos, byte *buf, int len)
+{
+ struct pci_io pi;
+ union {
+ u_int32_t u32;
+ u_int16_t u16[2];
+ u_int8_t u8[4];
+ } u;
+
+ if (!(len == 1 || len == 2 || len == 4))
+ return pci_generic_block_read(d, pos, buf, len);
+
+ if (d->domain || pos >= 256)
+ return 0;
+
+ pi.pi_sel.pc_bus = d->bus;
+ pi.pi_sel.pc_dev = d->dev;
+ pi.pi_sel.pc_func = d->func;
+
+ pi.pi_reg = pos - (pos % 4);
+ pi.pi_width = 4;
+
+ if (ioctl(d->access->fd, PCIOCREAD, &pi) < 0) {
+ if (errno == ENXIO)
+ pi.pi_data = 0xffffffff;
+ else
+ d->access->error("obsd_read: ioctl(PCIOCREAD) failed");
+ }
+ u.u32 = pi.pi_data;
+
+ switch (len)
+ {
+ case 1:
+ buf[0] = (u8) u.u8[pos % 4];
+ break;
+ case 2:
+ ((u16 *) buf)[0] = letoh16(u.u16[(pos % 4) / 2]);
+ break;
+ case 4:
+ ((u32 *) buf)[0] = (u32) letoh32(pi.pi_data);
+ break;
+ }
+ return 1;
+}
+
+static int
+obsd_write(struct pci_dev *d, int pos, byte *buf, int len)
+{
+ struct pci_io pi;
+
+ if (!(len == 1 || len == 2 || len == 4))
+ return pci_generic_block_write(d, pos, buf, len);
+
+ if (d->domain || pos >= 256)
+ return 0;
+
+ pi.pi_sel.pc_bus = d->bus;
+ pi.pi_sel.pc_dev = d->dev;
+ pi.pi_sel.pc_func = d->func;
+
+ pi.pi_reg = pos;
+ pi.pi_width = len;
+
+ switch (len)
+ {
+ case 1:
+ pi.pi_data = buf[0];
+ break;
+ case 2:
+ pi.pi_data = ((u16 *) buf)[0];
+ break;
+ case 4:
+ pi.pi_data = ((u32 *) buf)[0];
+ break;
+ }
+
+ if (ioctl(d->access->fd, PCIOCWRITE, &pi) < 0)
+ d->access->error("obsd_write: ioctl(PCIOCWRITE) failed");
+
+ return 1;
+}
+
+struct pci_methods pm_obsd_device = {
+ "obsd-device",
+ "/dev/pci on OpenBSD",
+ obsd_config,
+ obsd_detect,
+ obsd_init,
+ obsd_cleanup,
+ pci_generic_scan,
+ pci_generic_fill_info,
+ obsd_read,
+ obsd_write,
+ NULL, /* read_vpd */
+ NULL, /* dev_init */
+ NULL /* dev_cleanup */
+};
diff --git a/lib/params.c b/lib/params.c
new file mode 100644
index 0000000..9b4c2e2
--- /dev/null
+++ b/lib/params.c
@@ -0,0 +1,104 @@
+/*
+ * The PCI Library -- Parameters
+ *
+ * Copyright (c) 2008--2023 Martin Mares <mj@ucw.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "internal.h"
+
+char *
+pci_get_param(struct pci_access *acc, char *param)
+{
+ struct pci_param *p;
+
+ for (p=acc->params; p; p=p->next)
+ if (!strcmp(p->param, param))
+ return p->value;
+ return NULL;
+}
+
+struct pci_param *
+pci_define_param(struct pci_access *acc, char *param, char *value, char *help)
+{
+ struct pci_param *p, **pp;
+
+ for (pp=&acc->params; p = *pp; pp=&p->next)
+ {
+ int cmp = strcmp(p->param, param);
+ if (!cmp)
+ {
+ if (strcmp(p->value, value) || strcmp(p->help, help))
+ acc->error("Parameter %s re-defined differently", param);
+ return p;
+ }
+ if (cmp > 0)
+ break;
+ }
+
+ p = pci_malloc(acc, sizeof(*p));
+ p->next = *pp;
+ *pp = p;
+ p->param = param;
+ p->value = value;
+ p->value_malloced = 0;
+ p->help = help;
+ return p;
+}
+
+int
+pci_set_param_internal(struct pci_access *acc, char *param, char *value, int copy)
+{
+ struct pci_param *p;
+
+ for (p=acc->params; p; p=p->next)
+ if (!strcmp(p->param, param))
+ {
+ if (p->value_malloced)
+ pci_mfree(p->value);
+ p->value_malloced = copy;
+ if (copy)
+ p->value = pci_strdup(acc, value);
+ else
+ p->value = value;
+ return 0;
+ }
+ return -1;
+}
+
+int
+pci_set_param(struct pci_access *acc, char *param, char *value)
+{
+ return pci_set_param_internal(acc, param, value, 1);
+}
+
+void
+pci_free_params(struct pci_access *acc)
+{
+ struct pci_param *p;
+
+ while (p = acc->params)
+ {
+ acc->params = p->next;
+ if (p->value_malloced)
+ pci_mfree(p->value);
+ pci_mfree(p);
+ }
+}
+
+struct pci_param *
+pci_walk_params(struct pci_access *acc, struct pci_param *prev)
+{
+ /* So far, the params form a simple linked list, but this can change in the future */
+ if (!prev)
+ return acc->params;
+ else
+ return prev->next;
+}
diff --git a/lib/pci.h b/lib/pci.h
new file mode 100644
index 0000000..ae1333f
--- /dev/null
+++ b/lib/pci.h
@@ -0,0 +1,312 @@
+/*
+ * The PCI Library
+ *
+ * Copyright (c) 1997--2024 Martin Mares <mj@ucw.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef _PCI_LIB_H
+#define _PCI_LIB_H
+
+#ifndef PCI_CONFIG_H
+#include "config.h"
+#endif
+
+#include "header.h"
+#include "types.h"
+
+#define PCI_LIB_VERSION 0x030b01
+
+#ifndef PCI_ABI
+#define PCI_ABI
+#endif
+
+/*
+ * PCI Access Structure
+ */
+
+struct pci_methods;
+
+enum pci_access_type {
+ /* Known access methods, remember to update init.c as well */
+ PCI_ACCESS_AUTO, /* Autodetection */
+ PCI_ACCESS_SYS_BUS_PCI, /* Linux /sys/bus/pci */
+ PCI_ACCESS_PROC_BUS_PCI, /* Linux /proc/bus/pci */
+ PCI_ACCESS_I386_TYPE1, /* i386 ports, type 1 */
+ PCI_ACCESS_I386_TYPE2, /* i386 ports, type 2 */
+ PCI_ACCESS_FBSD_DEVICE, /* FreeBSD /dev/pci */
+ PCI_ACCESS_AIX_DEVICE, /* /dev/pci0, /dev/bus0, etc. */
+ PCI_ACCESS_NBSD_LIBPCI, /* NetBSD libpci */
+ PCI_ACCESS_OBSD_DEVICE, /* OpenBSD /dev/pci */
+ PCI_ACCESS_DUMP, /* Dump file */
+ PCI_ACCESS_DARWIN, /* Darwin */
+ PCI_ACCESS_SYLIXOS_DEVICE, /* SylixOS pci */
+ PCI_ACCESS_HURD, /* GNU/Hurd */
+ PCI_ACCESS_WIN32_CFGMGR32, /* Win32 cfgmgr32.dll */
+ PCI_ACCESS_WIN32_KLDBG, /* Win32 kldbgdrv.sys */
+ PCI_ACCESS_WIN32_SYSDBG, /* Win32 NT SysDbg */
+ PCI_ACCESS_MMIO_TYPE1, /* MMIO ports, type 1 */
+ PCI_ACCESS_MMIO_TYPE1_EXT, /* MMIO ports, type 1 extended */
+ PCI_ACCESS_ECAM, /* PCIe ECAM via /dev/mem */
+ PCI_ACCESS_AOS_EXPANSION, /* AmigaOS Expansion library */
+ PCI_ACCESS_MAX
+};
+
+struct pci_access {
+ /* Options you can change: */
+ unsigned int method; /* Access method */
+ int writeable; /* Open in read/write mode */
+ int buscentric; /* Bus-centric view of the world */
+
+ char *id_file_name; /* Name of ID list file (use pci_set_name_list_path()) */
+ int free_id_name; /* Set if id_file_name is malloced */
+ int numeric_ids; /* Enforce PCI_LOOKUP_NUMERIC (>1 => PCI_LOOKUP_MIXED) */
+
+ unsigned int id_lookup_mode; /* pci_lookup_mode flags which are set automatically */
+ /* Default: PCI_LOOKUP_CACHE */
+
+ int debugging; /* Turn on debugging messages */
+
+ /* Functions you can override: */
+ void (*error)(char *msg, ...) PCI_PRINTF(1,2) PCI_NONRET; /* Write error message and quit */
+ void (*warning)(char *msg, ...) PCI_PRINTF(1,2); /* Write a warning message */
+ void (*debug)(char *msg, ...) PCI_PRINTF(1,2); /* Write a debugging message */
+
+ struct pci_dev *devices; /* Devices found on this bus */
+
+ /* Fields used internally: */
+ struct pci_methods *methods;
+ struct pci_param *params;
+ struct id_entry **id_hash; /* names.c */
+ struct id_bucket *current_id_bucket;
+ int id_load_attempted;
+ int id_cache_status; /* 0=not read, 1=read, 2=dirty */
+ char *id_cache_name;
+ struct udev *id_udev; /* names-hwdb.c */
+ struct udev_hwdb *id_udev_hwdb;
+ int fd; /* proc/sys: fd for config space */
+ int fd_rw; /* proc/sys: fd opened read-write */
+ int fd_vpd; /* sys: fd for VPD */
+ struct pci_dev *cached_dev; /* proc/sys: device the fds are for */
+ void *backend_data; /* Private data of the back end */
+};
+
+/* Initialize PCI access */
+struct pci_access *pci_alloc(void) PCI_ABI;
+void pci_init(struct pci_access *) PCI_ABI;
+void pci_cleanup(struct pci_access *) PCI_ABI;
+
+/* Scanning of devices */
+void pci_scan_bus(struct pci_access *acc) PCI_ABI;
+struct pci_dev *pci_get_dev(struct pci_access *acc, int domain, int bus, int dev, int func) PCI_ABI; /* Raw access to specified device */
+void pci_free_dev(struct pci_dev *) PCI_ABI;
+
+/* Names of access methods */
+int pci_lookup_method(char *name) PCI_ABI; /* Returns -1 if not found */
+char *pci_get_method_name(int index) PCI_ABI; /* Returns "" if unavailable, NULL if index out of range */
+
+/*
+ * Named parameters
+ */
+
+struct pci_param {
+ struct pci_param *next; /* Please use pci_walk_params() for traversing the list */
+ char *param; /* Name of the parameter */
+ char *value; /* Value of the parameter */
+ int value_malloced; /* used internally */
+ char *help; /* Explanation of the parameter */
+};
+
+char *pci_get_param(struct pci_access *acc, char *param) PCI_ABI;
+int pci_set_param(struct pci_access *acc, char *param, char *value) PCI_ABI; /* 0 on success, -1 if no such parameter */
+/* To traverse the list, call pci_walk_params repeatedly, first with prev=NULL, and do not modify the parameters during traversal. */
+struct pci_param *pci_walk_params(struct pci_access *acc, struct pci_param *prev) PCI_ABI;
+
+/*
+ * Devices
+ */
+
+struct pci_dev {
+ struct pci_dev *next; /* Next device in the chain */
+ u16 domain_16; /* 16-bit version of the PCI domain for backward compatibility */
+ /* 0xffff if the real domain doesn't fit in 16 bits */
+ u8 bus, dev, func; /* Bus inside domain, device and function */
+
+ /* These fields are set by pci_fill_info() */
+ unsigned int known_fields; /* Set of info fields already known (see pci_fill_info()) */
+ u16 vendor_id, device_id; /* Identity of the device */
+ u16 device_class; /* PCI device class */
+ int irq; /* IRQ number */
+ pciaddr_t base_addr[6]; /* Base addresses including flags in lower bits */
+ pciaddr_t size[6]; /* Region sizes */
+ pciaddr_t rom_base_addr; /* Expansion ROM base address */
+ pciaddr_t rom_size; /* Expansion ROM size */
+ struct pci_cap *first_cap; /* List of capabilities */
+ char *phy_slot; /* Physical slot */
+ char *module_alias; /* Linux kernel module alias */
+ char *label; /* Device name as exported by BIOS */
+ int numa_node; /* NUMA node */
+ pciaddr_t flags[6]; /* PCI_IORESOURCE_* flags for regions */
+ pciaddr_t rom_flags; /* PCI_IORESOURCE_* flags for expansion ROM */
+ int domain; /* PCI domain (host bridge) */
+ pciaddr_t bridge_base_addr[4]; /* Bridge base addresses (without flags) */
+ pciaddr_t bridge_size[4]; /* Bridge sizes */
+ pciaddr_t bridge_flags[4]; /* PCI_IORESOURCE_* flags for bridge addresses */
+ u8 prog_if, rev_id; /* Programming interface for device_class and revision id */
+ u16 subsys_vendor_id, subsys_id; /* Subsystem vendor id and subsystem id */
+ struct pci_dev *parent; /* Parent device, does not have to be always accessible */
+ int no_config_access; /* No access to config space for this device */
+
+ /* Fields used internally */
+ struct pci_access *access;
+ struct pci_methods *methods;
+ u8 *cache; /* Cached config registers */
+ int cache_len;
+ int hdrtype; /* Cached low 7 bits of header type, -1 if unknown */
+ void *backend_data; /* Private data for of the back end */
+ struct pci_property *properties; /* A linked list of extra properties */
+ struct pci_cap *last_cap; /* Last capability in the list */
+};
+
+#define PCI_ADDR_IO_MASK (~(pciaddr_t) 0x3)
+#define PCI_ADDR_MEM_MASK (~(pciaddr_t) 0xf)
+#define PCI_ADDR_FLAG_MASK 0xf
+
+/* Access to configuration space */
+u8 pci_read_byte(struct pci_dev *, int pos) PCI_ABI;
+u16 pci_read_word(struct pci_dev *, int pos) PCI_ABI;
+u32 pci_read_long(struct pci_dev *, int pos) PCI_ABI;
+int pci_read_vpd(struct pci_dev *d, int pos, u8 *buf, int len) PCI_ABI;
+int pci_write_byte(struct pci_dev *, int pos, u8 data) PCI_ABI;
+int pci_write_word(struct pci_dev *, int pos, u16 data) PCI_ABI;
+int pci_write_long(struct pci_dev *, int pos, u32 data) PCI_ABI;
+
+/* Configuration space as a sequence of bytes (little-endian) */
+int pci_read_block(struct pci_dev *, int pos, u8 *buf, int len) PCI_ABI;
+int pci_write_block(struct pci_dev *, int pos, u8 *buf, int len) PCI_ABI;
+
+/*
+ * Most device properties take some effort to obtain, so libpci does not
+ * initialize them during default bus scan. Instead, you have to call
+ * pci_fill_info() with the proper PCI_FILL_xxx constants OR'ed together.
+ *
+ * Some properties are stored directly in the pci_dev structure.
+ * The remaining ones can be accessed through pci_get_string_property().
+ *
+ * pci_fill_info() returns the current value of pci_dev->known_fields.
+ * This is a bit mask of all fields, which were already obtained during
+ * the lifetime of the device. This includes fields which are not supported
+ * by the particular device -- in that case, the field is left at its default
+ * value, which is 0 for integer fields and NULL for pointers. On the other
+ * hand, we never consider known fields unsupported by the current back-end;
+ * such fields always contain the default value.
+ *
+ * XXX: flags and the result should be unsigned, but we do not want to break the ABI.
+ */
+
+int pci_fill_info(struct pci_dev *, int flags) PCI_ABI;
+char *pci_get_string_property(struct pci_dev *d, u32 prop) PCI_ABI;
+
+#define PCI_FILL_IDENT 0x0001
+#define PCI_FILL_IRQ 0x0002
+#define PCI_FILL_BASES 0x0004
+#define PCI_FILL_ROM_BASE 0x0008
+#define PCI_FILL_SIZES 0x0010
+#define PCI_FILL_CLASS 0x0020
+#define PCI_FILL_CAPS 0x0040
+#define PCI_FILL_EXT_CAPS 0x0080
+#define PCI_FILL_PHYS_SLOT 0x0100
+#define PCI_FILL_MODULE_ALIAS 0x0200
+#define PCI_FILL_LABEL 0x0400
+#define PCI_FILL_NUMA_NODE 0x0800
+#define PCI_FILL_IO_FLAGS 0x1000
+#define PCI_FILL_DT_NODE 0x2000 /* Device tree node */
+#define PCI_FILL_IOMMU_GROUP 0x4000
+#define PCI_FILL_BRIDGE_BASES 0x8000
+#define PCI_FILL_RESCAN 0x00010000
+#define PCI_FILL_CLASS_EXT 0x00020000 /* prog_if and rev_id */
+#define PCI_FILL_SUBSYS 0x00040000 /* subsys_vendor_id and subsys_id */
+#define PCI_FILL_PARENT 0x00080000
+#define PCI_FILL_DRIVER 0x00100000 /* OS driver currently in use (string property) */
+
+void pci_setup_cache(struct pci_dev *, u8 *cache, int len) PCI_ABI;
+
+/*
+ * Capabilities
+ */
+
+struct pci_cap {
+ struct pci_cap *next;
+ u16 id; /* PCI_CAP_ID_xxx */
+ u16 type; /* PCI_CAP_xxx */
+ unsigned int addr; /* Position in the config space */
+};
+
+#define PCI_CAP_NORMAL 1 /* Traditional PCI capabilities */
+#define PCI_CAP_EXTENDED 2 /* PCIe extended capabilities */
+
+struct pci_cap *pci_find_cap(struct pci_dev *, unsigned int id, unsigned int type) PCI_ABI;
+struct pci_cap *pci_find_cap_nr(struct pci_dev *, unsigned int id, unsigned int type,
+ unsigned int *cap_number) PCI_ABI;
+
+/*
+ * Filters
+ */
+
+struct pci_filter {
+ int domain, bus, slot, func; /* -1 = ANY */
+ int vendor, device;
+ int device_class;
+ unsigned int device_class_mask; /* Which bits of the device_class are compared, default=all */
+ int prog_if;
+ int rfu[1];
+};
+
+void pci_filter_init(struct pci_access *, struct pci_filter *) PCI_ABI;
+char *pci_filter_parse_slot(struct pci_filter *, char *) PCI_ABI;
+char *pci_filter_parse_id(struct pci_filter *, char *) PCI_ABI;
+int pci_filter_match(struct pci_filter *, struct pci_dev *) PCI_ABI;
+
+/*
+ * Conversion of PCI ID's to names (according to the pci.ids file)
+ *
+ * Call pci_lookup_name() to identify different types of ID's:
+ *
+ * VENDOR (vendorID) -> vendor
+ * DEVICE (vendorID, deviceID) -> device
+ * VENDOR | DEVICE (vendorID, deviceID) -> combined vendor and device
+ * SUBSYSTEM | VENDOR (subvendorID) -> subsystem vendor
+ * SUBSYSTEM | DEVICE (vendorID, deviceID, subvendorID, subdevID) -> subsystem device
+ * SUBSYSTEM | VENDOR | DEVICE (vendorID, deviceID, subvendorID, subdevID) -> combined subsystem v+d
+ * SUBSYSTEM | ... (-1, -1, subvendorID, subdevID) -> generic subsystem
+ * CLASS (classID) -> class
+ * PROGIF (classID, progif) -> programming interface
+ */
+
+char *pci_lookup_name(struct pci_access *a, char *buf, int size, int flags, ...) PCI_ABI;
+
+int pci_load_name_list(struct pci_access *a) PCI_ABI; /* Called automatically by pci_lookup_*() when needed; returns success */
+void pci_free_name_list(struct pci_access *a) PCI_ABI; /* Called automatically by pci_cleanup() */
+void pci_set_name_list_path(struct pci_access *a, char *name, int to_be_freed) PCI_ABI;
+void pci_id_cache_flush(struct pci_access *a) PCI_ABI;
+
+enum pci_lookup_mode {
+ PCI_LOOKUP_VENDOR = 1, /* Vendor name (args: vendorID) */
+ PCI_LOOKUP_DEVICE = 2, /* Device name (args: vendorID, deviceID) */
+ PCI_LOOKUP_CLASS = 4, /* Device class (args: classID) */
+ PCI_LOOKUP_SUBSYSTEM = 8,
+ PCI_LOOKUP_PROGIF = 16, /* Programming interface (args: classID, prog_if) */
+ PCI_LOOKUP_NUMERIC = 0x10000, /* Want only formatted numbers; default if access->numeric_ids is set */
+ PCI_LOOKUP_NO_NUMBERS = 0x20000, /* Return NULL if not found in the database; default is to print numerically */
+ PCI_LOOKUP_MIXED = 0x40000, /* Include both numbers and names */
+ PCI_LOOKUP_NETWORK = 0x80000, /* Try to resolve unknown ID's by DNS */
+ PCI_LOOKUP_SKIP_LOCAL = 0x100000, /* Do not consult local database */
+ PCI_LOOKUP_CACHE = 0x200000, /* Consult the local cache before using DNS */
+ PCI_LOOKUP_REFRESH_CACHE = 0x400000, /* Forget all previously cached entries, but still allow updating the cache */
+ PCI_LOOKUP_NO_HWDB = 0x800000, /* Do not ask udev's hwdb */
+};
+
+#endif
diff --git a/lib/physmem-access.h b/lib/physmem-access.h
new file mode 100644
index 0000000..a4e9744
--- /dev/null
+++ b/lib/physmem-access.h
@@ -0,0 +1,52 @@
+/*
+ * The PCI Library -- Compiler-specific wrappers for memory mapped I/O
+ *
+ * Copyright (c) 2023 Pali Rohár <pali@kernel.org>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/*
+ * FIXME
+ * Unfortunately gcc does not provide architecture independent way to read from
+ * or write to memory mapped I/O. The best approximation is to use volatile and
+ * for the write operation follow it by the read operation from the same address.
+ */
+
+static inline void
+physmem_writeb(unsigned char value, volatile void *ptr)
+{
+ *(volatile unsigned char *)ptr = value;
+}
+
+static inline void
+physmem_writew(unsigned short value, volatile void *ptr)
+{
+ *(volatile unsigned short *)ptr = value;
+}
+
+static inline void
+physmem_writel(u32 value, volatile void *ptr)
+{
+ *(volatile u32 *)ptr = value;
+}
+
+static inline unsigned char
+physmem_readb(volatile void *ptr)
+{
+ return *(volatile unsigned char *)ptr;
+}
+
+static inline unsigned short
+physmem_readw(volatile void *ptr)
+{
+ return *(volatile unsigned short *)ptr;
+}
+
+static inline u32
+physmem_readl(volatile void *ptr)
+{
+ return *(volatile u32 *)ptr;
+}
diff --git a/lib/physmem-posix.c b/lib/physmem-posix.c
new file mode 100644
index 0000000..7cd7e99
--- /dev/null
+++ b/lib/physmem-posix.c
@@ -0,0 +1,95 @@
+/*
+ * The PCI Library -- Physical memory mapping for POSIX systems
+ *
+ * Copyright (c) 2023 Pali Rohár <pali@kernel.org>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/*
+ * Tell 32-bit platforms that we are interested in 64-bit variant of off_t type
+ * as 32-bit variant of off_t type is signed and so it cannot represent all
+ * possible 32-bit offsets. It is required because off_t type is used by mmap().
+ */
+#define _FILE_OFFSET_BITS 64
+
+#include "internal.h"
+#include "physmem.h"
+
+#include <limits.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#ifndef OFF_MAX
+#define OFF_MAX ((((off_t)1 << (sizeof(off_t) * CHAR_BIT - 2)) - 1) * 2 + 1)
+#endif
+
+struct physmem {
+ int fd;
+};
+
+void
+physmem_init_config(struct pci_access *a)
+{
+ pci_define_param(a, "devmem.path", PCI_PATH_DEVMEM_DEVICE, "Path to the /dev/mem device");
+}
+
+int
+physmem_access(struct pci_access *a, int w)
+{
+ const char *devmem = pci_get_param(a, "devmem.path");
+ a->debug("checking access permission of physical memory device %s for %s mode...", devmem, w ? "read/write" : "read-only");
+ return access(devmem, R_OK | (w ? W_OK : 0));
+}
+
+struct physmem *
+physmem_open(struct pci_access *a, int w)
+{
+ const char *devmem = pci_get_param(a, "devmem.path");
+ struct physmem *physmem = pci_malloc(a, sizeof(struct physmem));
+
+ a->debug("trying to open physical memory device %s in %s mode...", devmem, w ? "read/write" : "read-only");
+ physmem->fd = open(devmem, (w ? O_RDWR : O_RDONLY) | O_DSYNC); /* O_DSYNC bypass CPU cache for mmap() on Linux */
+ if (physmem->fd < 0)
+ {
+ pci_mfree(physmem);
+ return NULL;
+ }
+
+ return physmem;
+}
+
+void
+physmem_close(struct physmem *physmem)
+{
+ close(physmem->fd);
+ pci_mfree(physmem);
+}
+
+long
+physmem_get_pagesize(struct physmem *physmem UNUSED)
+{
+ return sysconf(_SC_PAGESIZE);
+}
+
+void *
+physmem_map(struct physmem *physmem, u64 addr, size_t length, int w)
+{
+ if (addr > OFF_MAX)
+ {
+ errno = EOVERFLOW;
+ return (void *)-1;
+ }
+ return mmap(NULL, length, PROT_READ | (w ? PROT_WRITE : 0), MAP_SHARED, physmem->fd, addr);
+}
+
+int
+physmem_unmap(struct physmem *physmem UNUSED, void *ptr, size_t length)
+{
+ return munmap(ptr, length);
+}
diff --git a/lib/physmem.h b/lib/physmem.h
new file mode 100644
index 0000000..46ee021
--- /dev/null
+++ b/lib/physmem.h
@@ -0,0 +1,19 @@
+/*
+ * The PCI Library -- Physical memory mapping API
+ *
+ * Copyright (c) 2023 Pali Rohár <pali@kernel.org>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+struct physmem;
+
+void physmem_init_config(struct pci_access *a);
+int physmem_access(struct pci_access *a, int w);
+struct physmem *physmem_open(struct pci_access *a, int w);
+void physmem_close(struct physmem *physmem);
+long physmem_get_pagesize(struct physmem *physmem);
+void *physmem_map(struct physmem *physmem, u64 addr, size_t length, int w);
+int physmem_unmap(struct physmem *physmem, void *ptr, size_t length);
diff --git a/lib/proc.c b/lib/proc.c
new file mode 100644
index 0000000..429cea6
--- /dev/null
+++ b/lib/proc.c
@@ -0,0 +1,230 @@
+/*
+ * The PCI Library -- Configuration Access via /proc/bus/pci
+ *
+ * Copyright (c) 1997--2023 Martin Mares <mj@ucw.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h>
+
+#include "internal.h"
+
+static void
+proc_config(struct pci_access *a)
+{
+ pci_define_param(a, "proc.path", PCI_PATH_PROC_BUS_PCI, "Path to the procfs bus tree");
+}
+
+static int
+proc_detect(struct pci_access *a)
+{
+ char *name = pci_get_param(a, "proc.path");
+
+ if (access(name, R_OK))
+ {
+ a->warning("Cannot open %s", name);
+ return 0;
+ }
+ a->debug("...using %s", name);
+ return 1;
+}
+
+static void
+proc_init(struct pci_access *a)
+{
+ a->fd = -1;
+}
+
+static void
+proc_cleanup(struct pci_access *a)
+{
+ if (a->fd >= 0)
+ {
+ close(a->fd);
+ a->fd = -1;
+ }
+}
+
+static void
+proc_scan(struct pci_access *a)
+{
+ FILE *f;
+ char buf[512];
+
+ if (snprintf(buf, sizeof(buf), "%s/devices", pci_get_param(a, "proc.path")) == sizeof(buf))
+ a->error("File name too long");
+ f = fopen(buf, "r");
+ if (!f)
+ a->error("Cannot open %s", buf);
+ while (fgets(buf, sizeof(buf)-1, f))
+ {
+ struct pci_dev *d = pci_alloc_dev(a);
+ unsigned int dfn, vend, cnt, known;
+ char *driver;
+ int offset;
+
+#define F " " PCIADDR_T_FMT
+ cnt = sscanf(buf, "%x %x %x" F F F F F F F F F F F F F F "%n",
+ &dfn,
+ &vend,
+ &d->irq,
+ &d->base_addr[0],
+ &d->base_addr[1],
+ &d->base_addr[2],
+ &d->base_addr[3],
+ &d->base_addr[4],
+ &d->base_addr[5],
+ &d->rom_base_addr,
+ &d->size[0],
+ &d->size[1],
+ &d->size[2],
+ &d->size[3],
+ &d->size[4],
+ &d->size[5],
+ &d->rom_size,
+ &offset);
+#undef F
+ if (cnt != 9 && cnt != 10 && cnt != 17)
+ a->error("proc: parse error (read only %d items)", cnt);
+ d->bus = dfn >> 8U;
+ d->dev = PCI_SLOT(dfn & 0xff);
+ d->func = PCI_FUNC(dfn & 0xff);
+ d->vendor_id = vend >> 16U;
+ d->device_id = vend & 0xffff;
+ known = 0;
+ if (!a->buscentric)
+ {
+ known |= PCI_FILL_IDENT | PCI_FILL_IRQ | PCI_FILL_BASES;
+ if (cnt >= 10)
+ known |= PCI_FILL_ROM_BASE;
+ if (cnt >= 17)
+ known |= PCI_FILL_SIZES;
+ }
+ if (cnt >= 17)
+ {
+ while (buf[offset] && isspace(buf[offset]))
+ ++offset;
+ driver = &buf[offset];
+ while (buf[offset] && !isspace(buf[offset]))
+ ++offset;
+ buf[offset] = '\0';
+ if (driver[0])
+ {
+ pci_set_property(d, PCI_FILL_DRIVER, driver);
+ known |= PCI_FILL_DRIVER;
+ }
+ }
+ d->known_fields = known;
+ pci_link_dev(a, d);
+ }
+ fclose(f);
+}
+
+static int
+proc_setup(struct pci_dev *d, int rw)
+{
+ struct pci_access *a = d->access;
+
+ if (a->cached_dev != d || a->fd_rw < rw)
+ {
+ char buf[1024];
+ int e;
+ if (a->fd >= 0)
+ close(a->fd);
+ e = snprintf(buf, sizeof(buf), "%s/%02x/%02x.%d",
+ pci_get_param(a, "proc.path"),
+ d->bus, d->dev, d->func);
+ if (e < 0 || e >= (int) sizeof(buf))
+ a->error("File name too long");
+ a->fd_rw = a->writeable || rw;
+ a->fd = open(buf, a->fd_rw ? O_RDWR : O_RDONLY);
+ if (a->fd < 0)
+ {
+ e = snprintf(buf, sizeof(buf), "%s/%04x:%02x/%02x.%d",
+ pci_get_param(a, "proc.path"),
+ d->domain, d->bus, d->dev, d->func);
+ if (e < 0 || e >= (int) sizeof(buf))
+ a->error("File name too long");
+ a->fd = open(buf, a->fd_rw ? O_RDWR : O_RDONLY);
+ }
+ if (a->fd < 0)
+ a->warning("Cannot open %s", buf);
+ a->cached_dev = d;
+ }
+ return a->fd;
+}
+
+static int
+proc_read(struct pci_dev *d, int pos, byte *buf, int len)
+{
+ int fd = proc_setup(d, 0);
+ int res;
+
+ if (fd < 0)
+ return 0;
+ res = pread(fd, buf, len, pos);
+ if (res < 0)
+ {
+ d->access->warning("proc_read: read failed: %s", strerror(errno));
+ return 0;
+ }
+ else if (res != len)
+ return 0;
+ return 1;
+}
+
+static int
+proc_write(struct pci_dev *d, int pos, byte *buf, int len)
+{
+ int fd = proc_setup(d, 1);
+ int res;
+
+ if (fd < 0)
+ return 0;
+ res = pwrite(fd, buf, len, pos);
+ if (res < 0)
+ {
+ d->access->warning("proc_write: write failed: %s", strerror(errno));
+ return 0;
+ }
+ else if (res != len)
+ {
+ d->access->warning("proc_write: tried to write %d bytes at %d, but only %d succeeded", len, pos, res);
+ return 0;
+ }
+ return 1;
+}
+
+static void
+proc_cleanup_dev(struct pci_dev *d)
+{
+ if (d->access->cached_dev == d)
+ d->access->cached_dev = NULL;
+}
+
+struct pci_methods pm_linux_proc = {
+ "linux-proc",
+ "The proc file system on Linux",
+ proc_config,
+ proc_detect,
+ proc_init,
+ proc_cleanup,
+ proc_scan,
+ pci_generic_fill_info,
+ proc_read,
+ proc_write,
+ NULL, /* read_vpd */
+ NULL, /* init_dev */
+ proc_cleanup_dev
+};
diff --git a/lib/sylixos-device.c b/lib/sylixos-device.c
new file mode 100644
index 0000000..170ae02
--- /dev/null
+++ b/lib/sylixos-device.c
@@ -0,0 +1,160 @@
+/*
+ * The PCI Library -- Direct Configuration access via SylixOS Ports
+ *
+ * Copyright (c) 2018 YuJian.Gong <gongyujian@acoinfo.com>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#define _GNU_SOURCE
+#define __SYLIXOS_KERNEL
+#define __SYLIXOS_PCI_DRV
+#include <SylixOS.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "internal.h"
+
+static void
+sylixos_scan(struct pci_access *a)
+{
+ u8 busmap[256];
+ int bus;
+
+ memset(busmap, 0, sizeof(busmap));
+
+ for (bus = 0; bus < PCI_MAX_BUS; bus++)
+ if (!busmap[bus])
+ pci_generic_scan_bus(a, busmap, 0, bus);
+}
+
+static void
+sylixos_config(struct pci_access *a)
+{
+ pci_define_param(a, "sylixos.path", PCI_PATH_SYLIXOS_DEVICE, "Path to the SylixOS PCI device");
+}
+
+static int
+sylixos_detect(struct pci_access *a)
+{
+ char *name = pci_get_param(a, "sylixos.path");
+
+ if (access(name, R_OK))
+ {
+ a->warning("Cannot open %s", name);
+ return 0;
+ }
+
+ a->debug("...using %s", name);
+ return 1;
+}
+
+static void
+sylixos_init(struct pci_access *a UNUSED)
+{
+}
+
+static void
+sylixos_cleanup(struct pci_access *a UNUSED)
+{
+}
+
+static int
+sylixos_read(struct pci_dev *d, int pos, byte *buf, int len)
+{
+ int ret = -1;
+ u8 data_byte = -1;
+ u16 data_word = -1;
+ u32 data_dword = -1;
+
+ if (!(len == 1 || len == 2 || len == 4))
+ return pci_generic_block_read(d, pos, buf, len);
+
+ if (pos >= 256)
+ return 0;
+
+ switch (len)
+ {
+ case 1:
+ ret = pciConfigInByte(d->bus, d->dev, d->func, pos, &data_byte);
+ if (ret != ERROR_NONE)
+ return 0;
+ buf[0] = (u8)data_byte;
+ break;
+
+ case 2:
+ ret = pciConfigInWord(d->bus, d->dev, d->func, pos, &data_word);
+ if (ret != ERROR_NONE)
+ return 0;
+ ((u16 *) buf)[0] = cpu_to_le16(data_word);
+ break;
+
+ case 4:
+ ret = pciConfigInDword(d->bus, d->dev, d->func, pos, &data_dword);
+ if (ret != ERROR_NONE)
+ return 0;
+ ((u32 *) buf)[0] = cpu_to_le32(data_dword);
+ break;
+ }
+
+ return 1;
+}
+
+static int
+sylixos_write(struct pci_dev *d, int pos, byte *buf, int len)
+{
+ int ret = PX_ERROR;
+ u8 data_byte;
+ u16 data_word;
+ u32 data_dword;
+
+ if (!(len == 1 || len == 2 || len == 4))
+ return pci_generic_block_write(d, pos, buf, len);
+
+ if (pos >= 256)
+ return 0;
+
+ switch (len)
+ {
+ case 1:
+ data_byte = buf[0];
+ ret = pciConfigOutByte(d->bus, d->dev, d->func, pos, data_byte);
+ if (ret != ERROR_NONE)
+ return 0;
+ break;
+
+ case 2:
+ data_word = le16_to_cpu(((u16 *) buf)[0]);
+ ret = pciConfigOutWord(d->bus, d->dev, d->func, pos, data_word);
+ if (ret != ERROR_NONE)
+ return 0;
+ break;
+
+ case 4:
+ data_dword = le32_to_cpu(((u32 *) buf)[0]);
+ ret = pciConfigOutDword(d->bus, d->dev, d->func, pos, data_dword);
+ if (ret != ERROR_NONE)
+ return 0;
+ break;
+ }
+
+ return 1;
+}
+
+struct pci_methods pm_sylixos_device = {
+ "sylixos-device",
+ "SylixOS /proc/pci device",
+ sylixos_config,
+ sylixos_detect,
+ sylixos_init,
+ sylixos_cleanup,
+ sylixos_scan,
+ pci_generic_fill_info,
+ sylixos_read,
+ sylixos_write,
+ NULL, // no read_vpd
+ NULL, // no init_dev
+ NULL, // no cleanup_dev
+};
diff --git a/lib/sysdep.h b/lib/sysdep.h
new file mode 100644
index 0000000..40e1407
--- /dev/null
+++ b/lib/sysdep.h
@@ -0,0 +1,129 @@
+/*
+ * The PCI Library -- System-Dependent Stuff
+ *
+ * Copyright (c) 1997--2020 Martin Mares <mj@ucw.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifdef __GNUC__
+#define UNUSED __attribute__((unused))
+#define NONRET __attribute__((noreturn))
+#define FORMAT_CHECK(x,y,z) __attribute__((format(x,y,z)))
+#else
+#define UNUSED
+#define NONRET
+#define FORMAT_CHECK(x,y,z)
+#define inline
+#endif
+
+typedef u8 byte;
+typedef u16 word;
+
+#ifdef PCI_OS_WINDOWS
+#define strcasecmp _strcmpi
+#define strncasecmp _strnicmp
+#if defined(_MSC_VER) && _MSC_VER < 1800
+#if _MSC_VER < 1300
+#define strtoull strtoul
+#else
+#define strtoull _strtoui64
+#endif
+#endif
+#if defined(_MSC_VER) && _MSC_VER < 1900
+#define snprintf _snprintf
+#define vsnprintf _vsnprintf
+#endif
+#endif
+
+#ifdef PCI_HAVE_LINUX_BYTEORDER_H
+
+#include <asm/byteorder.h>
+#define cpu_to_le16 __cpu_to_le16
+#define cpu_to_le32 __cpu_to_le32
+#define le16_to_cpu __le16_to_cpu
+#define le32_to_cpu __le32_to_cpu
+
+#else
+
+#ifdef PCI_OS_LINUX
+#include <endian.h>
+#define BYTE_ORDER __BYTE_ORDER
+#define BIG_ENDIAN __BIG_ENDIAN
+#endif
+
+#ifdef PCI_OS_SUNOS
+#include <sys/byteorder.h>
+#if defined(__i386) && defined(LITTLE_ENDIAN)
+# define BYTE_ORDER LITTLE_ENDIAN
+#elif defined(__sparc) && defined(BIG_ENDIAN)
+# define BYTE_ORDER BIG_ENDIAN
+#else
+#define BIG_ENDIAN 4321
+#endif
+#ifndef BYTE_ORDER
+#ifdef _LITTLE_ENDIAN
+#define BYTE_ORDER 1234
+#else
+#define BYTE_ORDER 4321
+#endif
+#endif /* BYTE_ORDER */
+#endif /* PCI_OS_SUNOS */
+
+#ifdef PCI_OS_WINDOWS
+#ifdef __MINGW32__
+ #include <sys/param.h>
+#else
+ #include <io.h>
+ #define BIG_ENDIAN 4321
+ #define LITTLE_ENDIAN 1234
+ #define BYTE_ORDER LITTLE_ENDIAN
+#endif
+#endif
+
+#ifdef PCI_OS_SYLIXOS
+#include <endian.h>
+#endif
+
+#ifdef PCI_OS_DJGPP
+ #define BIG_ENDIAN 4321
+ #define LITTLE_ENDIAN 1234
+ #define BYTE_ORDER LITTLE_ENDIAN
+#endif
+
+#ifdef PCI_OS_AMIGAOS
+ #include <machine/endian.h>
+#endif
+
+#if !defined(BYTE_ORDER)
+#error "BYTE_ORDER not defined for your platform"
+#endif
+
+#if BYTE_ORDER == BIG_ENDIAN
+#define cpu_to_le16 swab16
+#define cpu_to_le32 swab32
+#define le16_to_cpu swab16
+#define le32_to_cpu swab32
+
+static inline word swab16(word w)
+{
+ return (w << 8) | ((w >> 8) & 0xff);
+}
+
+static inline u32 swab32(u32 w)
+{
+ return ((w & 0xff000000) >> 24) |
+ ((w & 0x00ff0000) >> 8) |
+ ((w & 0x0000ff00) << 8) |
+ ((w & 0x000000ff) << 24);
+}
+#else
+#define cpu_to_le16(x) (x)
+#define cpu_to_le32(x) (x)
+#define le16_to_cpu(x) (x)
+#define le32_to_cpu(x) (x)
+#endif
+
+#endif
diff --git a/lib/sysfs.c b/lib/sysfs.c
new file mode 100644
index 0000000..cd2379e
--- /dev/null
+++ b/lib/sysfs.c
@@ -0,0 +1,609 @@
+/*
+ * The PCI Library -- Configuration Access via /sys/bus/pci
+ *
+ * Copyright (c) 2003 Matthew Wilcox <matthew@wil.cx>
+ * Copyright (c) 1997--2023 Martin Mares <mj@ucw.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <errno.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/types.h>
+
+#include "internal.h"
+
+static void
+sysfs_config(struct pci_access *a)
+{
+ pci_define_param(a, "sysfs.path", PCI_PATH_SYS_BUS_PCI, "Path to the sysfs device tree");
+}
+
+static inline char *
+sysfs_name(struct pci_access *a)
+{
+ return pci_get_param(a, "sysfs.path");
+}
+
+static int
+sysfs_detect(struct pci_access *a)
+{
+ if (access(sysfs_name(a), R_OK))
+ {
+ a->debug("...cannot open %s", sysfs_name(a));
+ return 0;
+ }
+ a->debug("...using %s", sysfs_name(a));
+ return 1;
+}
+
+static void
+sysfs_init(struct pci_access *a)
+{
+ a->fd = -1;
+ a->fd_vpd = -1;
+}
+
+static void
+sysfs_flush_cache(struct pci_access *a)
+{
+ if (a->fd >= 0)
+ {
+ close(a->fd);
+ a->fd = -1;
+ }
+ if (a->fd_vpd >= 0)
+ {
+ close(a->fd_vpd);
+ a->fd_vpd = -1;
+ }
+ a->cached_dev = NULL;
+}
+
+static void
+sysfs_cleanup(struct pci_access *a)
+{
+ sysfs_flush_cache(a);
+}
+
+#define OBJNAMELEN 1024
+static void
+sysfs_obj_name(struct pci_dev *d, char *object, char *buf)
+{
+ int n = snprintf(buf, OBJNAMELEN, "%s/devices/%04x:%02x:%02x.%d/%s",
+ sysfs_name(d->access), d->domain, d->bus, d->dev, d->func, object);
+ if (n < 0 || n >= OBJNAMELEN)
+ d->access->error("File name too long");
+}
+
+#define OBJBUFSIZE 1024
+
+static int
+sysfs_get_string(struct pci_dev *d, char *object, char *buf, int mandatory)
+{
+ struct pci_access *a = d->access;
+ int fd, n;
+ char namebuf[OBJNAMELEN];
+ void (*warn)(char *msg, ...) = (mandatory ? a->error : a->warning);
+
+ sysfs_obj_name(d, object, namebuf);
+ fd = open(namebuf, O_RDONLY);
+ if (fd < 0)
+ {
+ if (mandatory || errno != ENOENT)
+ warn("Cannot open %s: %s", namebuf, strerror(errno));
+ return 0;
+ }
+ n = read(fd, buf, OBJBUFSIZE);
+ close(fd);
+ if (n < 0)
+ {
+ warn("Error reading %s: %s", namebuf, strerror(errno));
+ return 0;
+ }
+ if (n >= OBJBUFSIZE)
+ {
+ warn("Value in %s too long", namebuf);
+ return 0;
+ }
+ buf[n] = 0;
+ return 1;
+}
+
+static char *
+sysfs_deref_link(struct pci_dev *d, char *link_name)
+{
+ char path[2*OBJNAMELEN], rel_path[OBJNAMELEN];
+
+ sysfs_obj_name(d, link_name, path);
+ memset(rel_path, 0, sizeof(rel_path));
+
+ if (readlink(path, rel_path, sizeof(rel_path)) < 0)
+ return NULL;
+
+ sysfs_obj_name(d, "", path);
+ strcat(path, rel_path);
+
+ // Returns a pointer to malloc'ed memory
+ return realpath(path, NULL);
+}
+
+static int
+sysfs_get_value(struct pci_dev *d, char *object, int mandatory)
+{
+ char buf[OBJBUFSIZE];
+
+ if (sysfs_get_string(d, object, buf, mandatory))
+ return strtol(buf, NULL, 0);
+ else
+ return -1;
+}
+
+static void
+sysfs_get_resources(struct pci_dev *d)
+{
+ struct pci_access *a = d->access;
+ char namebuf[OBJNAMELEN], buf[256];
+ struct { pciaddr_t flags, base_addr, size; } lines[10];
+ int have_bar_bases, have_rom_base, have_bridge_bases;
+ FILE *file;
+ int i;
+
+ have_bar_bases = have_rom_base = have_bridge_bases = 0;
+ sysfs_obj_name(d, "resource", namebuf);
+ file = fopen(namebuf, "r");
+ if (!file)
+ a->error("Cannot open %s: %s", namebuf, strerror(errno));
+ for (i = 0; i < 7+6+4+1; i++)
+ {
+ unsigned long long start, end, size, flags;
+ if (!fgets(buf, sizeof(buf), file))
+ break;
+ if (sscanf(buf, "%llx %llx %llx", &start, &end, &flags) != 3)
+ a->error("Syntax error in %s", namebuf);
+ if (end > start)
+ size = end - start + 1;
+ else
+ size = 0;
+ if (i < 6)
+ {
+ d->flags[i] = flags;
+ flags &= PCI_ADDR_FLAG_MASK;
+ d->base_addr[i] = start | flags;
+ d->size[i] = size;
+ have_bar_bases = 1;
+ }
+ else if (i == 6)
+ {
+ d->rom_flags = flags;
+ flags &= PCI_ADDR_FLAG_MASK;
+ d->rom_base_addr = start | flags;
+ d->rom_size = size;
+ have_rom_base = 1;
+ }
+ else if (i < 7+6+4)
+ {
+ /*
+ * If kernel was compiled without CONFIG_PCI_IOV option then after
+ * the ROM line for configured bridge device (that which had set
+ * subordinary bus number to non-zero value) are four additional lines
+ * which describe resources behind bridge. For PCI-to-PCI bridges they
+ * are: IO, MEM, PREFMEM and empty. For CardBus bridges they are: IO0,
+ * IO1, MEM0 and MEM1. For unconfigured bridges and other devices
+ * there is no additional line after the ROM line. If kernel was
+ * compiled with CONFIG_PCI_IOV option then after the ROM line and
+ * before the first bridge resource line are six additional lines
+ * which describe IOV resources. Read all remaining lines in resource
+ * file and based on the number of remaining lines (0, 4, 6, 10) parse
+ * resources behind bridge.
+ */
+ lines[i-7].flags = flags;
+ lines[i-7].base_addr = start;
+ lines[i-7].size = size;
+ }
+ }
+ if (i == 7+4 || i == 7+6+4)
+ {
+ int offset = (i == 7+6+4) ? 6 : 0;
+ for (i = 0; i < 4; i++)
+ {
+ d->bridge_flags[i] = lines[offset+i].flags;
+ d->bridge_base_addr[i] = lines[offset+i].base_addr;
+ d->bridge_size[i] = lines[offset+i].size;
+ }
+ have_bridge_bases = 1;
+ }
+ fclose(file);
+ if (!have_bar_bases)
+ clear_fill(d, PCI_FILL_BASES | PCI_FILL_SIZES | PCI_FILL_IO_FLAGS);
+ if (!have_rom_base)
+ clear_fill(d, PCI_FILL_ROM_BASE);
+ if (!have_bridge_bases)
+ clear_fill(d, PCI_FILL_BRIDGE_BASES);
+}
+
+static void sysfs_scan(struct pci_access *a)
+{
+ char dirname[1024];
+ DIR *dir;
+ struct dirent *entry;
+ int n;
+
+ n = snprintf(dirname, sizeof(dirname), "%s/devices", sysfs_name(a));
+ if (n < 0 || n >= (int) sizeof(dirname))
+ a->error("Directory name too long");
+ dir = opendir(dirname);
+ if (!dir)
+ a->error("Cannot open %s", dirname);
+ while ((entry = readdir(dir)))
+ {
+ struct pci_dev *d;
+ unsigned int dom, bus, dev, func;
+
+ /* ".", ".." or a special non-device perhaps */
+ if (entry->d_name[0] == '.')
+ continue;
+
+ d = pci_alloc_dev(a);
+ if (sscanf(entry->d_name, "%x:%x:%x.%d", &dom, &bus, &dev, &func) < 4)
+ a->error("sysfs_scan: Couldn't parse entry name %s", entry->d_name);
+
+ /* Ensure kernel provided domain that fits in a signed integer */
+ if (dom > 0x7fffffff)
+ a->error("sysfs_scan: Invalid domain %x", dom);
+
+ d->domain = dom;
+ d->bus = bus;
+ d->dev = dev;
+ d->func = func;
+ pci_link_dev(a, d);
+ }
+ closedir(dir);
+}
+
+static void
+sysfs_fill_slots(struct pci_access *a)
+{
+ char dirname[1024];
+ DIR *dir;
+ struct dirent *entry;
+ int n;
+
+ n = snprintf(dirname, sizeof(dirname), "%s/slots", sysfs_name(a));
+ if (n < 0 || n >= (int) sizeof(dirname))
+ a->error("Directory name too long");
+ dir = opendir(dirname);
+ if (!dir)
+ return;
+
+ while (entry = readdir(dir))
+ {
+ char namebuf[OBJNAMELEN], buf[16];
+ FILE *file;
+ unsigned int dom, bus, dev;
+ int res = 0;
+ struct pci_dev *d;
+
+ /* ".", ".." or a special non-device perhaps */
+ if (entry->d_name[0] == '.')
+ continue;
+
+ n = snprintf(namebuf, OBJNAMELEN, "%s/%s/%s", dirname, entry->d_name, "address");
+ if (n < 0 || n >= OBJNAMELEN)
+ a->error("File name too long");
+ file = fopen(namebuf, "r");
+ /*
+ * Old versions of Linux had a fakephp which didn't have an 'address'
+ * file. There's no useful information to be gleaned from these
+ * devices, pretend they're not there.
+ */
+ if (!file)
+ continue;
+
+ if (!fgets(buf, sizeof(buf), file) || (res = sscanf(buf, "%x:%x:%x", &dom, &bus, &dev)) < 3)
+ {
+ /*
+ * In some cases, the slot is not tied to a specific device before
+ * a card gets inserted. This happens for example on IBM pSeries
+ * and we need not warn about it.
+ */
+ if (res != 2)
+ a->warning("sysfs_fill_slots: Couldn't parse entry address %s", buf);
+ }
+ else
+ {
+ for (d = a->devices; d; d = d->next)
+ if (dom == (unsigned)d->domain && bus == d->bus && dev == d->dev && !d->phy_slot)
+ d->phy_slot = pci_set_property(d, PCI_FILL_PHYS_SLOT, entry->d_name);
+ }
+ fclose(file);
+ }
+ closedir(dir);
+}
+
+static void
+sysfs_fill_info(struct pci_dev *d, unsigned int flags)
+{
+ int value, want_class, want_class_ext;
+
+ if (!d->access->buscentric)
+ {
+ /*
+ * These fields can be read from the config registers, but we want to show
+ * the kernel's view, which has regions and IRQs remapped and other fields
+ * (most importantly classes) possibly fixed if the device is known broken.
+ */
+ if (want_fill(d, flags, PCI_FILL_IDENT))
+ {
+ d->vendor_id = sysfs_get_value(d, "vendor", 1);
+ d->device_id = sysfs_get_value(d, "device", 1);
+ }
+ want_class = want_fill(d, flags, PCI_FILL_CLASS);
+ want_class_ext = want_fill(d, flags, PCI_FILL_CLASS_EXT);
+ if (want_class || want_class_ext)
+ {
+ value = sysfs_get_value(d, "class", 1);
+ if (want_class)
+ d->device_class = value >> 8;
+ if (want_class_ext)
+ {
+ d->prog_if = value & 0xff;
+ value = sysfs_get_value(d, "revision", 0);
+ if (value < 0)
+ value = pci_read_byte(d, PCI_REVISION_ID);
+ if (value >= 0)
+ d->rev_id = value;
+ }
+ }
+ if (want_fill(d, flags, PCI_FILL_SUBSYS))
+ {
+ value = sysfs_get_value(d, "subsystem_vendor", 0);
+ if (value >= 0)
+ {
+ d->subsys_vendor_id = value;
+ value = sysfs_get_value(d, "subsystem_device", 0);
+ if (value >= 0)
+ d->subsys_id = value;
+ }
+ else
+ clear_fill(d, PCI_FILL_SUBSYS);
+ }
+ if (want_fill(d, flags, PCI_FILL_IRQ))
+ d->irq = sysfs_get_value(d, "irq", 1);
+ if (want_fill(d, flags, PCI_FILL_BASES | PCI_FILL_ROM_BASE | PCI_FILL_SIZES | PCI_FILL_IO_FLAGS | PCI_FILL_BRIDGE_BASES))
+ sysfs_get_resources(d);
+ if (want_fill(d, flags, PCI_FILL_PARENT))
+ {
+ unsigned int domain, bus, dev, func;
+ char *path_abs, *path_canon, *name;
+ char path_rel[OBJNAMELEN];
+ struct pci_dev *parent;
+
+ /* Construct sysfs path for parent device */
+ sysfs_obj_name(d, "..", path_rel);
+ path_abs = realpath(path_rel, NULL);
+ name = path_abs ? strrchr(path_abs, '/') : NULL;
+ name = name ? name+1 : name;
+ parent = NULL;
+
+ if (name && sscanf(name, "%x:%x:%x.%d", &domain, &bus, &dev, &func) == 4 && domain <= 0x7fffffff)
+ for (parent = d->access->devices; parent; parent = parent->next)
+ if (parent->domain == (int)domain && parent->bus == bus && parent->dev == dev && parent->func == func)
+ break;
+
+ if (parent)
+ {
+ /* Check if parsed BDF address from parent sysfs device is really expected PCI device */
+ sysfs_obj_name(parent, ".", path_rel);
+ path_canon = realpath(path_rel, NULL);
+ if (!path_canon || strcmp(path_canon, path_abs) != 0)
+ parent = NULL;
+
+ if (path_canon)
+ free(path_canon);
+ }
+
+ if (parent)
+ d->parent = parent;
+ else
+ clear_fill(d, PCI_FILL_PARENT);
+
+ if (path_abs)
+ free(path_abs);
+ }
+ }
+
+ if (want_fill(d, flags, PCI_FILL_PHYS_SLOT))
+ {
+ struct pci_dev *pd;
+ sysfs_fill_slots(d->access);
+ for (pd = d->access->devices; pd; pd = pd->next)
+ pd->known_fields |= PCI_FILL_PHYS_SLOT;
+ }
+
+ if (want_fill(d, flags, PCI_FILL_MODULE_ALIAS))
+ {
+ char buf[OBJBUFSIZE];
+ if (sysfs_get_string(d, "modalias", buf, 0))
+ d->module_alias = pci_set_property(d, PCI_FILL_MODULE_ALIAS, buf);
+ }
+
+ if (want_fill(d, flags, PCI_FILL_LABEL))
+ {
+ char buf[OBJBUFSIZE];
+ if (sysfs_get_string(d, "label", buf, 0))
+ d->label = pci_set_property(d, PCI_FILL_LABEL, buf);
+ }
+
+ if (want_fill(d, flags, PCI_FILL_NUMA_NODE))
+ d->numa_node = sysfs_get_value(d, "numa_node", 0);
+
+ if (want_fill(d, flags, PCI_FILL_IOMMU_GROUP))
+ {
+ char *group_link = sysfs_deref_link(d, "iommu_group");
+ if (group_link)
+ {
+ pci_set_property(d, PCI_FILL_IOMMU_GROUP, basename(group_link));
+ free(group_link);
+ }
+ }
+
+ if (want_fill(d, flags, PCI_FILL_DT_NODE))
+ {
+ char *node = sysfs_deref_link(d, "of_node");
+ if (node)
+ {
+ pci_set_property(d, PCI_FILL_DT_NODE, node);
+ free(node);
+ }
+ }
+
+ if (want_fill(d, flags, PCI_FILL_DRIVER))
+ {
+ char *driver_path = sysfs_deref_link(d, "driver");
+ if (driver_path)
+ {
+ char *driver = strrchr(driver_path, '/');
+ driver = driver ? driver+1 : driver_path;
+ pci_set_property(d, PCI_FILL_DRIVER, driver);
+ free(driver_path);
+ }
+ else
+ clear_fill(d, PCI_FILL_DRIVER);
+ }
+
+ pci_generic_fill_info(d, flags);
+}
+
+/* Intent of the sysfs_setup() caller */
+enum
+ {
+ SETUP_READ_CONFIG = 0,
+ SETUP_WRITE_CONFIG = 1,
+ SETUP_READ_VPD = 2
+ };
+
+static int
+sysfs_setup(struct pci_dev *d, int intent)
+{
+ struct pci_access *a = d->access;
+ char namebuf[OBJNAMELEN];
+
+ if (a->cached_dev != d || (intent == SETUP_WRITE_CONFIG && !a->fd_rw))
+ {
+ sysfs_flush_cache(a);
+ a->cached_dev = d;
+ }
+
+ if (intent == SETUP_READ_VPD)
+ {
+ if (a->fd_vpd < 0)
+ {
+ sysfs_obj_name(d, "vpd", namebuf);
+ a->fd_vpd = open(namebuf, O_RDONLY);
+ /* No warning on error; vpd may be absent or accessible only to root */
+ }
+ return a->fd_vpd;
+ }
+
+ if (a->fd < 0)
+ {
+ sysfs_obj_name(d, "config", namebuf);
+ a->fd_rw = a->writeable || intent == SETUP_WRITE_CONFIG;
+ a->fd = open(namebuf, a->fd_rw ? O_RDWR : O_RDONLY);
+ if (a->fd < 0)
+ a->warning("Cannot open %s", namebuf);
+ }
+ return a->fd;
+}
+
+static int sysfs_read(struct pci_dev *d, int pos, byte *buf, int len)
+{
+ int fd = sysfs_setup(d, SETUP_READ_CONFIG);
+ int res;
+
+ if (fd < 0)
+ return 0;
+ res = pread(fd, buf, len, pos);
+ if (res < 0)
+ {
+ d->access->warning("sysfs_read: read failed: %s", strerror(errno));
+ return 0;
+ }
+ else if (res != len)
+ return 0;
+ return 1;
+}
+
+static int sysfs_write(struct pci_dev *d, int pos, byte *buf, int len)
+{
+ int fd = sysfs_setup(d, SETUP_WRITE_CONFIG);
+ int res;
+
+ if (fd < 0)
+ return 0;
+ res = pwrite(fd, buf, len, pos);
+ if (res < 0)
+ {
+ d->access->warning("sysfs_write: write failed: %s", strerror(errno));
+ return 0;
+ }
+ else if (res != len)
+ {
+ d->access->warning("sysfs_write: tried to write %d bytes at %d, but only %d succeeded", len, pos, res);
+ return 0;
+ }
+ return 1;
+}
+
+static int sysfs_read_vpd(struct pci_dev *d, int pos, byte *buf, int len)
+{
+ int fd = sysfs_setup(d, SETUP_READ_VPD);
+ int res;
+
+ if (fd < 0)
+ return 0;
+ res = pread(fd, buf, len, pos);
+ if (res < 0)
+ {
+ d->access->warning("sysfs_read_vpd: read failed: %s", strerror(errno));
+ return 0;
+ }
+ else if (res != len)
+ return 0;
+ return 1;
+}
+
+static void sysfs_cleanup_dev(struct pci_dev *d)
+{
+ struct pci_access *a = d->access;
+
+ if (a->cached_dev == d)
+ sysfs_flush_cache(a);
+}
+
+struct pci_methods pm_linux_sysfs = {
+ "linux-sysfs",
+ "The sys filesystem on Linux",
+ sysfs_config,
+ sysfs_detect,
+ sysfs_init,
+ sysfs_cleanup,
+ sysfs_scan,
+ sysfs_fill_info,
+ sysfs_read,
+ sysfs_write,
+ sysfs_read_vpd,
+ NULL, /* init_dev */
+ sysfs_cleanup_dev
+};
diff --git a/lib/types.h b/lib/types.h
new file mode 100644
index 0000000..260c981
--- /dev/null
+++ b/lib/types.h
@@ -0,0 +1,67 @@
+/*
+ * The PCI Library -- Types and Format Strings
+ *
+ * Copyright (c) 1997--2022 Martin Mares <mj@ucw.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <sys/types.h>
+#include <stddef.h>
+
+#ifndef PCI_HAVE_Uxx_TYPES
+
+#ifdef PCI_OS_WINDOWS
+/* On Windows compilers, use <windows.h> */
+#include <windows.h>
+typedef BYTE u8;
+typedef WORD u16;
+typedef DWORD u32;
+typedef unsigned __int64 u64;
+#define PCI_U64_FMT_X "I64x"
+#define PCI_U64_FMT_U "I64u"
+
+#else
+/* Use standard types in C99 and newer */
+#include <stdint.h>
+#include <inttypes.h>
+typedef uint8_t u8;
+typedef uint16_t u16;
+typedef uint32_t u32;
+typedef uint64_t u64;
+#define PCI_U64_FMT_X PRIx64
+#define PCI_U64_FMT_U PRIu64
+#endif
+
+#endif /* PCI_HAVE_Uxx_TYPES */
+
+#ifdef PCI_HAVE_64BIT_ADDRESS
+typedef u64 pciaddr_t;
+#define PCIADDR_T_FMT "%08" PCI_U64_FMT_X
+#define PCIADDR_PORT_FMT "%04" PCI_U64_FMT_X
+#else
+typedef u32 pciaddr_t;
+#define PCIADDR_T_FMT "%08x"
+#define PCIADDR_PORT_FMT "%04x"
+#endif
+
+#ifdef PCI_ARCH_SPARC64
+/* On sparc64 Linux the kernel reports remapped port addresses and IRQ numbers */
+#undef PCIADDR_PORT_FMT
+#define PCIADDR_PORT_FMT PCIADDR_T_FMT
+#define PCIIRQ_FMT "%08x"
+#else
+#define PCIIRQ_FMT "%d"
+#endif
+
+#if defined(__GNUC__) && __GNUC__ > 2
+#define PCI_PRINTF(x,y) __attribute__((format(printf, x, y)))
+#define PCI_NONRET __attribute((noreturn))
+#define PCI_PACKED __attribute((packed))
+#else
+#define PCI_PRINTF(x,y)
+#define PCI_NONRET
+#define PCI_PACKED
+#endif
diff --git a/lib/ver2def.pl b/lib/ver2def.pl
new file mode 100755
index 0000000..18231bc
--- /dev/null
+++ b/lib/ver2def.pl
@@ -0,0 +1,47 @@
+#!/usr/bin/perl
+use strict;
+use warnings;
+die "Usage: $0 script.ver dllname build.def import.def\n" if @ARGV != 4;
+my ($verfile, $dllname, $builddef, $importdef) = @ARGV;
+open my $verfh, '<', $verfile or die "Cannot open input file $verfile: $!\n";
+my $input = join '', <$verfh>;
+close $verfh;
+my @syms;
+my (%cnt, %last, %ords);
+$input =~ s/\/\*.*?\*\///sg; # Remove C comments
+while ($input =~ m/(\S+)\s*\{((?:[^\{\}]|\{(?2)\})+)\}\s*;/sg) { # Split {...}
+ my ($ver, $block) = ($1, $2);
+ while ($block =~ s/(\S+)\s*:((?:[^\{\}:]|\{(?2)\})+)$//sg) { # Split section:
+ my ($section, $syms) = ($1, $2);
+ next if $section ne 'global';
+ $syms =~ s/\s+//g;
+ foreach (split /;\s*/, $syms) { # Split symbols
+ $cnt{$_}++;
+ $last{$_} = $ver;
+ push @syms, [$_, $ver];
+ }
+ }
+}
+open my $importfh, '>', $importdef or die "Cannot open output file $importdef: $!\n";
+open my $buildfh, '>', $builddef or die "Cannot open output file $builddef: $!\n";
+print $importfh "LIBRARY \"$dllname\"\n";
+print $importfh "EXPORTS\n";
+print $buildfh "EXPORTS\n";
+my $ord = 1;
+foreach (@syms) {
+ my ($sym, $ver) = @{$_};
+ print $importfh "\"$sym\@$ver\" \@$ord\n";
+ if ($last{$sym} ne $ver) {
+ print $buildfh "\"$sym\@$ver\" \@$ord\n";
+ } else {
+ $ords{$sym} = $ord;
+ print $buildfh "\"$sym\@$ver\" = " . (($cnt{$sym} > 1) ? "\"$sym\@\@$ver\"" : $sym) . " \@$ord\n"
+ }
+ $ord++;
+}
+# GNU dlltool has broken calculation of ordinals for aliased symbols, so specify ordinals explicitly
+# GNU LD prior 2.21 has broken handling of symbols with dot character
+# Operator == for defining symbol alias is supported since GNU dlltool 2.21
+print $importfh "$_ \@$ords{$_} == \"$_\@$last{$_}\"\n" foreach sort keys %last;
+close $importfh;
+close $buildfh;
diff --git a/lib/win32-cfgmgr32.c b/lib/win32-cfgmgr32.c
new file mode 100644
index 0000000..dbddc54
--- /dev/null
+++ b/lib/win32-cfgmgr32.c
@@ -0,0 +1,1765 @@
+/*
+ * The PCI Library -- List PCI devices on Win32 via Configuration Manager
+ *
+ * Copyright (c) 2021 Pali Rohár <pali@kernel.org>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <windows.h>
+#include <cfgmgr32.h>
+
+#include <ctype.h> /* for isxdigit() */
+#include <stdio.h> /* for sprintf() */
+#include <string.h> /* for strlen(), strchr(), strncmp() */
+#include <wchar.h> /* for wcslen(), wcscpy() */
+
+#include "internal.h"
+#include "win32-helpers.h"
+
+/* Unfortunately MinGW32 toolchain does not provide these cfgmgr32 constants. */
+
+#ifndef RegDisposition_OpenExisting
+#define RegDisposition_OpenExisting 0x00000001
+#endif
+
+#ifndef CM_REGISTRY_SOFTWARE
+#define CM_REGISTRY_SOFTWARE 0x00000001
+#endif
+
+#ifndef CM_DRP_HARDWAREID
+#define CM_DRP_HARDWAREID 0x00000002
+#endif
+#ifndef CM_DRP_SERVICE
+#define CM_DRP_SERVICE 0x00000005
+#endif
+#ifndef CM_DRP_BUSNUMBER
+#define CM_DRP_BUSNUMBER 0x00000016
+#endif
+#ifndef CM_DRP_ADDRESS
+#define CM_DRP_ADDRESS 0x0000001D
+#endif
+
+#ifndef CR_INVALID_CONFLICT_LIST
+#define CR_INVALID_CONFLICT_LIST 0x00000039
+#endif
+#ifndef CR_INVALID_INDEX
+#define CR_INVALID_INDEX 0x0000003A
+#endif
+#ifndef CR_INVALID_STRUCTURE_SIZE
+#define CR_INVALID_STRUCTURE_SIZE 0x0000003B
+#endif
+
+#ifndef fIOD_10_BIT_DECODE
+#define fIOD_10_BIT_DECODE 0x0004
+#endif
+#ifndef fIOD_12_BIT_DECODE
+#define fIOD_12_BIT_DECODE 0x0008
+#endif
+#ifndef fIOD_16_BIT_DECODE
+#define fIOD_16_BIT_DECODE 0x0010
+#endif
+#ifndef fIOD_POSITIVE_DECODE
+#define fIOD_POSITIVE_DECODE 0x0020
+#endif
+#ifndef fIOD_PASSIVE_DECODE
+#define fIOD_PASSIVE_DECODE 0x0040
+#endif
+#ifndef fIOD_WINDOW_DECODE
+#define fIOD_WINDOW_DECODE 0x0080
+#endif
+#ifndef fIOD_PORT_BAR
+#define fIOD_PORT_BAR 0x0100
+#endif
+
+#ifndef fMD_WINDOW_DECODE
+#define fMD_WINDOW_DECODE 0x0040
+#endif
+#ifndef fMD_MEMORY_BAR
+#define fMD_MEMORY_BAR 0x0080
+#endif
+
+#ifndef mMD_Prefetchable
+#define mMD_Prefetchable fMD_Prefetchable
+#endif
+
+/*
+ * Unfortunately MinGW32 toolchain does not provide import library for these
+ * cfgmgr32.dll functions. So resolve pointers to these functions at runtime.
+ * MinGW-w64 toolchain provides them also in 32-bit mode.
+ */
+
+#if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
+
+#ifdef CM_Get_DevNode_Registry_PropertyA
+#undef CM_Get_DevNode_Registry_PropertyA
+#endif
+static CONFIGRET (WINAPI *MyCM_Get_DevNode_Registry_PropertyA)(DEVINST dnDevInst, ULONG ulProperty, PULONG pulRegDataType, PVOID Buffer, PULONG pulLength, ULONG ulFlags);
+#define CM_Get_DevNode_Registry_PropertyA MyCM_Get_DevNode_Registry_PropertyA
+
+#ifdef CM_Get_DevNode_Registry_PropertyW
+#undef CM_Get_DevNode_Registry_PropertyW
+#endif
+static CONFIGRET (WINAPI *MyCM_Get_DevNode_Registry_PropertyW)(DEVINST dnDevInst, ULONG ulProperty, PULONG pulRegDataType, PVOID Buffer, PULONG pulLength, ULONG ulFlags);
+#define CM_Get_DevNode_Registry_PropertyW MyCM_Get_DevNode_Registry_PropertyW
+
+#ifndef CM_Open_DevNode_Key
+#undef CM_Open_DevNode_Key
+#endif
+static CONFIGRET (WINAPI *MyCM_Open_DevNode_Key)(DEVINST dnDevNode, REGSAM samDesired, ULONG ulHardwareProfile, REGDISPOSITION Disposition, PHKEY phkDevice, ULONG ulFlags);
+#define CM_Open_DevNode_Key MyCM_Open_DevNode_Key
+
+static BOOL
+resolve_cfgmgr32_functions(void)
+{
+ HMODULE cfgmgr32;
+
+ if (CM_Get_DevNode_Registry_PropertyA && CM_Get_DevNode_Registry_PropertyW && CM_Open_DevNode_Key)
+ return TRUE;
+
+ cfgmgr32 = GetModuleHandleA("cfgmgr32.dll");
+ if (!cfgmgr32)
+ return FALSE;
+
+ CM_Get_DevNode_Registry_PropertyA = (void *)GetProcAddress(cfgmgr32, "CM_Get_DevNode_Registry_PropertyA");
+ CM_Get_DevNode_Registry_PropertyW = (void *)GetProcAddress(cfgmgr32, "CM_Get_DevNode_Registry_PropertyW");
+ CM_Open_DevNode_Key = (void *)GetProcAddress(cfgmgr32, "CM_Open_DevNode_Key");
+ if (!CM_Get_DevNode_Registry_PropertyA || !CM_Get_DevNode_Registry_PropertyW || !CM_Open_DevNode_Key)
+ return FALSE;
+
+ return TRUE;
+}
+
+#endif
+
+/*
+ * cfgmgr32.dll uses custom non-Win32 error numbers which are unsupported by
+ * Win32 APIs like GetLastError() and FormatMessage() functions.
+ *
+ * Windows 7 introduced new cfgmgr32.dll function CM_MapCrToWin32Err() for
+ * translating mapping CR_* errors to Win32 errors but most error codes are
+ * not mapped. So this function is unusable.
+ *
+ * Error strings for CR_* errors are defined in cmapi.rc file which is
+ * statically linked into some system libraries (e.g. filemgmt.dll,
+ * acledit.dll, netui0.dll or netui2.dll) but due to static linking it is
+ * not possible to access these error strings easily at runtime.
+ *
+ * So define own function for translating CR_* errors directly to strings.
+ */
+static const char *
+cr_strerror(CONFIGRET cr_error_id)
+{
+ static char unknown_error[sizeof("Unknown CR error XXXXXXXXXX")];
+ static const char *cr_errors[] = {
+ "The operation completed successfully",
+ "CR_DEFAULT",
+ "Not enough memory is available to process this command",
+ "A required pointer parameter is invalid",
+ "The ulFlags parameter specified is invalid for this operation",
+ "The device instance handle parameter is not valid",
+ "The supplied resource descriptor parameter is invalid",
+ "The supplied logical configuration parameter is invalid",
+ "CR_INVALID_ARBITRATOR",
+ "CR_INVALID_NODELIST",
+ "CR_DEVNODE_HAS_REQS/CR_DEVINST_HAS_REQS",
+ "The RESOURCEID parameter does not contain a valid RESOURCEID",
+ "CR_DLVXD_NOT_FOUND",
+ "The specified device instance handle does not correspond to a present device",
+ "There are no more logical configurations available",
+ "There are no more resource descriptions available",
+ "This device instance already exists",
+ "The supplied range list parameter is invalid",
+ "CR_INVALID_RANGE",
+ "A general internal error occurred",
+ "CR_NO_SUCH_LOGICAL_DEV",
+ "The device is disabled for this configuration",
+ "CR_NOT_SYSTEM_VM",
+ "A service or application refused to allow removal of this device",
+ "CR_APM_VETOED",
+ "CR_INVALID_LOAD_TYPE",
+ "An output parameter was too small to hold all the data available",
+ "CR_NO_ARBITRATOR",
+ "CR_NO_REGISTRY_HANDLE",
+ "A required entry in the registry is missing or an attempt to write to the registry failed",
+ "The specified Device ID is not a valid Device ID",
+ "One or more parameters were invalid",
+ "CR_INVALID_API",
+ "CR_DEVLOADER_NOT_READY",
+ "CR_NEED_RESTART",
+ "There are no more hardware profiles available",
+ "CR_DEVICE_NOT_THERE",
+ "The specified value does not exist in the registry",
+ "CR_WRONG_TYPE",
+ "The specified priority is invalid for this operation",
+ "This device cannot be disabled",
+ "CR_FREE_RESOURCES",
+ "CR_QUERY_VETOED",
+ "CR_CANT_SHARE_IRQ",
+ "CR_NO_DEPENDENT",
+ "CR_SAME_RESOURCES",
+ "The specified key does not exist in the registry",
+ "The specified machine name does not meet the UNC naming conventions",
+ "A general remote communication error occurred",
+ "The machine selected for remote communication is not available at this time",
+ "The Plug and Play service or another required service is not available",
+ "Access denied",
+ "This routine is not implemented in this version of the operating system",
+ "The specified property type is invalid for this operation",
+ "Device interface is active",
+ "No such device interface",
+ "Invalid reference string",
+ "Invalid conflict list",
+ "Invalid index",
+ "Invalid structure size"
+ };
+ if (cr_error_id <= 0 || cr_error_id >= sizeof(cr_errors)/sizeof(*cr_errors))
+ {
+ sprintf(unknown_error, "Unknown CR error %lu", cr_error_id);
+ return unknown_error;
+ }
+ return cr_errors[cr_error_id];
+}
+
+static int
+fmt_validate(const char *s, int len, const char *fmt)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ if (!fmt[i] || (fmt[i] == '#' ? !isxdigit(s[i]) : fmt[i] != s[i]))
+ return 0;
+
+ return 1;
+}
+
+static int
+seq_xdigit_validate(const char *s, int mult, int min)
+{
+ int i, len;
+
+ len = strlen(s);
+ if (len < min*mult || len % mult)
+ return 0;
+
+ for (i = 0; i < len; i++)
+ if (!isxdigit(s[i]))
+ return 0;
+
+ return 1;
+}
+
+static LPWSTR
+get_device_service_name(struct pci_access *a, DEVINST devinst, DEVINSTID_A devinst_id, BOOL *supported)
+{
+ ULONG reg_type, reg_size, reg_len;
+ LPWSTR service_name;
+ CONFIGRET cr;
+
+ /*
+ * All data are stored as 7-bit ASCII strings in system but service name is
+ * exception. It can contain UNICODE. Moreover it is passed to other Win32 API
+ * functions and therefore it cannot be converted to 8-bit ANSI string without
+ * data loss. So use wide function CM_Get_DevNode_Registry_PropertyW() in this
+ * case and deal with all wchar_t problems...
+ */
+
+ reg_size = 0;
+ cr = CM_Get_DevNode_Registry_PropertyW(devinst, CM_DRP_SERVICE, &reg_type, NULL, &reg_size, 0);
+ if (cr == CR_CALL_NOT_IMPLEMENTED)
+ {
+ *supported = FALSE;
+ return NULL;
+ }
+ else if (cr == CR_NO_SUCH_VALUE)
+ {
+ *supported = TRUE;
+ return NULL;
+ }
+ else if (cr != CR_SUCCESS &&
+ cr != CR_BUFFER_SMALL)
+ {
+ a->warning("Cannot retrieve service name for PCI device %s: %s.", devinst_id, cr_strerror(cr));
+ *supported = TRUE;
+ return NULL;
+ }
+ else if (reg_type != REG_SZ)
+ {
+ a->warning("Cannot retrieve service name for PCI device %s: Service name is stored as unknown type 0x%lx.", devinst_id, reg_type);
+ *supported = TRUE;
+ return NULL;
+ }
+
+retry:
+ /*
+ * Returned size is on older Windows versions without nul-term char.
+ * So explicitly increase size and fill nul-term byte.
+ */
+ reg_size += sizeof(service_name[0]);
+ service_name = pci_malloc(a, reg_size);
+ reg_len = reg_size;
+ cr = CM_Get_DevNode_Registry_PropertyW(devinst, CM_DRP_SERVICE, &reg_type, service_name, &reg_len, 0);
+ service_name[reg_size/sizeof(service_name[0]) - 1] = 0;
+ if (reg_len > reg_size)
+ {
+ pci_mfree(service_name);
+ reg_size = reg_len;
+ goto retry;
+ }
+ else if (cr != CR_SUCCESS)
+ {
+ a->warning("Cannot retrieve service name for PCI device %s: %s.", devinst_id, cr_strerror(cr));
+ pci_mfree(service_name);
+ *supported = TRUE;
+ return NULL;
+ }
+ else if (reg_type != REG_SZ)
+ {
+ a->warning("Cannot retrieve service name for PCI device %s: Service name is stored as unknown type 0x%lx.", devinst_id, reg_type);
+ pci_mfree(service_name);
+ *supported = TRUE;
+ return NULL;
+ }
+
+
+ return service_name;
+}
+
+static char*
+get_driver_path_for_service(struct pci_access *a, LPCWSTR service_name, SC_HANDLE manager)
+{
+ UINT (WINAPI *get_system_root_path)(LPWSTR buffer, UINT size) = NULL;
+ DWORD service_config_size, service_config_len;
+ LPQUERY_SERVICE_CONFIGW service_config = NULL;
+ LPWSTR service_image_path = NULL;
+ SERVICE_STATUS service_status;
+ SC_HANDLE service = NULL;
+ char *driver_path = NULL;
+ int trim_system32 = 0;
+ UINT systemroot_len;
+ int driver_path_len;
+ UINT system32_len;
+ HMODULE kernel32;
+ WCHAR *trim_ptr;
+ DWORD error;
+
+ service = OpenServiceW(manager, service_name, SERVICE_QUERY_CONFIG | SERVICE_QUERY_STATUS);
+ if (!service)
+ {
+ error = GetLastError();
+ if (error != ERROR_SERVICE_DOES_NOT_EXIST)
+ a->warning("Cannot open service %ls with query rights: %s.", service_name, win32_strerror(error));
+ goto out;
+ }
+
+ if (!QueryServiceStatus(service, &service_status))
+ {
+ error = GetLastError();
+ a->warning("Cannot query status of service %ls: %s.", service_name, win32_strerror(error));
+ goto out;
+ }
+
+ if (service_status.dwCurrentState == SERVICE_STOPPED)
+ goto out;
+
+ if (service_status.dwServiceType != SERVICE_KERNEL_DRIVER)
+ goto out;
+
+ if (!QueryServiceConfigW(service, NULL, 0, &service_config_size))
+ {
+ error = GetLastError();
+ if (error != ERROR_INSUFFICIENT_BUFFER)
+ {
+ a->warning("Cannot query config of service %ls: %s.", service_name, win32_strerror(error));
+ goto out;
+ }
+ }
+
+retry_service_config:
+ service_config = pci_malloc(a, service_config_size);
+ if (!QueryServiceConfigW(service, service_config, service_config_size, &service_config_len))
+ {
+ error = GetLastError();
+ if (error == ERROR_INSUFFICIENT_BUFFER)
+ {
+ pci_mfree(service_config);
+ service_config_size = service_config_len;
+ goto retry_service_config;
+ }
+ a->warning("Cannot query config of service %ls: %s.", service_name, win32_strerror(error));
+ goto out;
+ }
+
+ if (service_config->dwServiceType != SERVICE_KERNEL_DRIVER)
+ goto out;
+
+ /*
+ * Despite QueryServiceConfig() is Win32 API, it returns lpBinaryPathName
+ * (ImagePath registry) in NT format. Unfortunately there is no Win32
+ * function for converting NT paths to Win32 paths. So do it manually and
+ * convert this NT format to human-readable Win32 path format.
+ */
+
+ /*
+ * GetSystemWindowsDirectoryW() returns path to NT SystemRoot namespace.
+ * Alternativelly path to NT SystemRoot namespace can be constructed by
+ * GetSystemDirectoryW() by trimming "\\system32" from the end of path.
+ * GetSystemWindowsDirectoryW() is not provided in old Windows versions,
+ * so use GetProcAddress() for compatibility with all Windows versions.
+ */
+ kernel32 = GetModuleHandleW(L"kernel32.dll");
+ if (kernel32)
+ get_system_root_path = (void *)GetProcAddress(kernel32, "GetSystemWindowsDirectoryW");
+ else
+ {
+ get_system_root_path = &GetSystemDirectoryW;
+ trim_system32 = 1;
+ }
+
+ if (!service_config->lpBinaryPathName || !service_config->lpBinaryPathName[0])
+ {
+ /* No ImagePath is specified, NT kernel assumes implicit kernel driver path by service name, which is relative to "\\system32\\drivers". */
+ /* GetSystemDirectoryW() returns path to "\\system32" directory on all Windows versions. */
+ system32_len = GetSystemDirectoryW(NULL, 0); /* Returns number of WCHARs plus 1 for nul-term. */
+ service_image_path = pci_malloc(a, sizeof(WCHAR) * (system32_len + sizeof("\\drivers\\")-1 + wcslen(service_name) + sizeof(".sys")-1));
+ system32_len = GetSystemDirectoryW(service_image_path, system32_len); /* Now it returns number of WCHARs without nul-term. */
+ if (system32_len && service_image_path[system32_len-1] != L'\\')
+ service_image_path[system32_len++] = L'\\';
+ wcscpy(service_image_path + system32_len, L"drivers\\");
+ wcscpy(service_image_path + system32_len + sizeof("drivers\\")-1, service_name);
+ wcscpy(service_image_path + system32_len + sizeof("drivers\\")-1 + wcslen(service_name), L".sys");
+ }
+ else if (wcsncmp(service_config->lpBinaryPathName, L"\\SystemRoot\\", sizeof("\\SystemRoot\\")-1) == 0)
+ {
+ /* ImagePath is in NT SystemRoot namespace, convert to Win32 path via GetSystemWindowsDirectoryW()/GetSystemDirectoryW(). */
+ systemroot_len = get_system_root_path(NULL, 0); /* Returns number of WCHARs plus 1 for nul-term. */
+ service_image_path = pci_malloc(a, sizeof(WCHAR) * (systemroot_len + wcslen(service_config->lpBinaryPathName) - (sizeof("\\SystemRoot")-1)));
+ systemroot_len = get_system_root_path(service_image_path, systemroot_len); /* Now it returns number of WCHARs without nul-term. */
+ if (trim_system32 && systemroot_len && (trim_ptr = wcsrchr(service_image_path, L'\\')) != NULL)
+ systemroot_len = trim_ptr - service_image_path;
+ if (systemroot_len && service_image_path[systemroot_len-1] != L'\\')
+ service_image_path[systemroot_len++] = L'\\';
+ wcscpy(service_image_path + systemroot_len, service_config->lpBinaryPathName + sizeof("\\SystemRoot\\")-1);
+ }
+ else if (wcsncmp(service_config->lpBinaryPathName, L"\\??\\UNC\\", sizeof("\\??\\UNC\\")-1) == 0 ||
+ wcsncmp(service_config->lpBinaryPathName, L"\\??\\\\UNC\\", sizeof("\\??\\\\UNC\\")-1) == 0)
+ {
+ /* ImagePath is in NT UNC namespace, convert to Win32 UNC path via "\\\\" prefix. */
+ service_image_path = pci_malloc(a, sizeof(WCHAR) * (sizeof("\\\\") + wcslen(service_config->lpBinaryPathName) - (sizeof("\\??\\UNC\\")-1)));
+ /* Namespace separator may be single or double backslash. */
+ driver_path_len = sizeof("\\??\\")-1;
+ if (service_config->lpBinaryPathName[driver_path_len] == L'\\')
+ driver_path_len++;
+ driver_path_len += sizeof("UNC\\")-1;
+ wcscpy(service_image_path, L"\\\\");
+ wcscpy(service_image_path + sizeof("\\\\")-1, service_config->lpBinaryPathName + driver_path_len);
+ }
+ else if (wcsncmp(service_config->lpBinaryPathName, L"\\??\\", sizeof("\\??\\")-1) == 0)
+ {
+ /* ImagePath is in NT Global?? namespace, root of the Win32 file namespace, so just remove "\\??\\" prefix to get Win32 path. */
+ service_image_path = pci_malloc(a, sizeof(WCHAR) * (wcslen(service_config->lpBinaryPathName) - (sizeof("\\??\\")-1)));
+ /* Namespace separator may be single or double backslash. */
+ driver_path_len = sizeof("\\??\\")-1;
+ if (service_config->lpBinaryPathName[driver_path_len] == L'\\')
+ driver_path_len++;
+ wcscpy(service_image_path, service_config->lpBinaryPathName + driver_path_len);
+ }
+ else if (service_config->lpBinaryPathName[0] != L'\\')
+ {
+ /* ImagePath is relative to the NT SystemRoot namespace, convert to Win32 path via GetSystemWindowsDirectoryW()/GetSystemDirectoryW(). */
+ systemroot_len = get_system_root_path(NULL, 0); /* Returns number of WCHARs plus 1 for nul-term. */
+ service_image_path = pci_malloc(a, sizeof(WCHAR) * (systemroot_len + sizeof("\\")-1 + wcslen(service_config->lpBinaryPathName)));
+ systemroot_len = get_system_root_path(service_image_path, systemroot_len); /* Now it returns number of WCHARs without nul-term. */
+ if (trim_system32 && systemroot_len && (trim_ptr = wcsrchr(service_image_path, L'\\')) != NULL)
+ systemroot_len = trim_ptr - service_image_path;
+ if (systemroot_len && service_image_path[systemroot_len-1] != L'\\')
+ service_image_path[systemroot_len++] = L'\\';
+ wcscpy(service_image_path + systemroot_len, service_config->lpBinaryPathName);
+ }
+ else
+ {
+ /* ImagePath is in some unhandled NT namespace, copy it as is. It cannot be used in Win32 API but libpci user can be still interested in it. */
+ service_image_path = pci_malloc(a, sizeof(WCHAR) * wcslen(service_config->lpBinaryPathName));
+ wcscpy(service_image_path, service_config->lpBinaryPathName);
+ }
+
+ /* Calculate len of buffer needed for conversion from LPWSTR to char*. */
+ driver_path_len = WideCharToMultiByte(CP_ACP, 0, service_image_path, -1, NULL, 0, NULL, NULL);
+ if (driver_path_len <= 0)
+ {
+ error = GetLastError();
+ a->warning("Cannot convert kernel driver path from wide string to 8-bit string: %s.", win32_strerror(error));
+ goto out;
+ }
+
+ driver_path = pci_malloc(a, driver_path_len);
+ driver_path_len = WideCharToMultiByte(CP_ACP, 0, service_image_path, -1, driver_path, driver_path_len, NULL, NULL);
+ if (driver_path_len <= 0)
+ {
+ error = GetLastError();
+ a->warning("Cannot convert kernel driver path from wide string to 8-bit string: %s.", win32_strerror(error));
+ pci_mfree(driver_path);
+ driver_path = NULL;
+ goto out;
+ }
+
+out:
+ if (service_image_path)
+ pci_mfree(service_image_path);
+ if (service_config)
+ pci_mfree(service_config);
+ if (service)
+ CloseServiceHandle(service);
+ return driver_path;
+}
+
+static HKEY
+get_device_driver_devreg(struct pci_access *a, DEVINST devinst, DEVINSTID_A devinst_id)
+{
+ CONFIGRET cr;
+ HKEY key;
+
+ cr = CM_Open_DevNode_Key(devinst, KEY_READ, 0, RegDisposition_OpenExisting, &key, CM_REGISTRY_SOFTWARE);
+ if (cr != CR_SUCCESS)
+ {
+ if (cr != CR_NO_SUCH_VALUE)
+ a->warning("Cannot retrieve driver key for device %s: %s.", devinst_id, cr_strerror(cr));
+ return NULL;
+ }
+
+ return key;
+}
+
+static char*
+read_reg_key_string_value(struct pci_access *a, HKEY key, const char *name, DWORD *unkn_reg_type)
+{
+ DWORD reg_type, reg_size, reg_len;
+ char *value;
+ LONG error;
+
+ reg_size = 0;
+ error = RegQueryValueExA(key, name, NULL, &reg_type, NULL, &reg_size);
+ if (error != ERROR_SUCCESS &&
+ error != ERROR_MORE_DATA)
+ {
+ SetLastError(error);
+ return NULL;
+ }
+ else if (reg_type != REG_SZ)
+ {
+ SetLastError(0);
+ *unkn_reg_type = reg_type;
+ return NULL;
+ }
+
+retry:
+ value = pci_malloc(a, reg_size + 1);
+ reg_len = reg_size;
+ error = RegQueryValueExA(key, name, NULL, &reg_type, (PBYTE)value, &reg_len);
+ if (error != ERROR_SUCCESS)
+ {
+ pci_mfree(value);
+ if (error == ERROR_MORE_DATA)
+ {
+ reg_size = reg_len;
+ goto retry;
+ }
+ SetLastError(error);
+ return NULL;
+ }
+ else if (reg_type != REG_SZ)
+ {
+ pci_mfree(value);
+ SetLastError(0);
+ *unkn_reg_type = reg_type;
+ return NULL;
+ }
+ value[reg_len] = '\0';
+
+ return value;
+}
+
+static int
+driver_cmp(const char *driver, const char *match)
+{
+ int len = strlen(driver);
+ if (driver[0] == '*')
+ driver++;
+ if (len >= 4 && strcasecmp(driver + len - 4, ".vxd") == 0)
+ len -= 4;
+ return strncasecmp(driver, match, len);
+}
+
+static char*
+get_driver_path_for_regkey(struct pci_access *a, DEVINSTID_A devinst_id, HKEY key)
+{
+ char *driver_list, *driver, *driver_next;
+ char *subdriver, *subname;
+ char *driver_ptr;
+ char *driver_path;
+ DWORD unkn_reg_type;
+ UINT systemdir_len;
+ HKEY subkey;
+ LONG error;
+ BOOL vmm32;
+ BOOL noext;
+ int len;
+
+ driver_list = read_reg_key_string_value(a, key, "DevLoader", &unkn_reg_type);
+ if (!driver_list)
+ {
+ error = GetLastError();
+ if (error == 0)
+ a->warning("Cannot read driver DevLoader key for PCI device %s: DevLoader key is stored as unknown type 0x%lx.", devinst_id, unkn_reg_type);
+ else if (error != ERROR_FILE_NOT_FOUND)
+ a->warning("Cannot read driver DevLoader key for PCI device %s: %s.", devinst_id, win32_strerror(error));
+ return NULL;
+ }
+
+ subdriver = NULL;
+ driver = driver_list;
+ while (*driver)
+ {
+ driver_next = strchr(driver, ',');
+ if (driver_next)
+ *(driver_next++) = '\0';
+
+ if (driver_cmp(driver, "ios") == 0 ||
+ driver_cmp(driver, "vcomm") == 0)
+ subname = "PortDriver";
+ else if (driver_cmp(driver, "ntkern") == 0)
+ subname = "NTMPDriver";
+ else if (driver_cmp(driver, "ndis") == 0)
+ subname = "DeviceVxDs";
+ else if (driver_cmp(driver, "vdd") == 0)
+ subname = "minivdd";
+ else
+ subname = NULL;
+
+ subkey = key;
+ if (subname && strcmp(subname, "minivdd") == 0)
+ {
+ error = RegOpenKeyA(key, "Default", &subkey);
+ if (error != ERROR_SUCCESS)
+ {
+ a->warning("Cannot open driver subkey Default for PCI device %s: %s.", devinst_id, win32_strerror(error));
+ subkey = NULL;
+ }
+ }
+
+ if (!subname)
+ break;
+
+ if (subkey)
+ {
+retry_subname:
+ subdriver = read_reg_key_string_value(a, subkey, subname, &unkn_reg_type);
+ if (!subdriver)
+ {
+ error = GetLastError();
+ if (error == 0)
+ a->warning("Cannot read driver %s key for PCI device %s: %s key is stored as unknown type 0x%lx.", subname, devinst_id, subname, unkn_reg_type);
+ else if (error != ERROR_FILE_NOT_FOUND)
+ a->warning("Cannot read driver %s key for PCI device %s: %s.", subname, devinst_id, win32_strerror(error));
+ else if (strcmp(subname, "minivdd") == 0)
+ {
+ subname = "drv";
+ goto retry_subname;
+ }
+ else if (strcmp(subname, "drv") == 0)
+ {
+ subname = "vdd";
+ goto retry_subname;
+ }
+ }
+
+ if (subkey != key)
+ RegCloseKey(subkey);
+
+ if (subdriver)
+ {
+ char *endptr = strchr(subdriver, ',');
+ if (endptr)
+ *endptr = '\0';
+ break;
+ }
+ }
+
+ driver = driver_next;
+ }
+
+ if (subdriver && subdriver[0])
+ driver_ptr = subdriver;
+ else if (driver[0])
+ driver_ptr = driver;
+ else
+ driver_ptr = NULL;
+
+ if (driver_ptr && driver_ptr[0] == '*')
+ {
+ vmm32 = TRUE;
+ driver_ptr++;
+ }
+ else
+ vmm32 = FALSE;
+
+ if (!driver_ptr[0])
+ driver_ptr = NULL;
+
+ len = driver_ptr ? strlen(driver_ptr) : 0;
+ noext = driver_ptr && (len < 4 || driver_ptr[len-4] != '.');
+
+ if (!driver_ptr)
+ driver_path = NULL;
+ else
+ {
+ if (tolower(driver_ptr[0]) >= 'a' && tolower(driver_ptr[0]) <= 'z' && driver_ptr[1] == ':')
+ {
+ /* Driver is already with absolute path. */
+ driver_path = pci_strdup(a, driver_ptr);
+ }
+ else if (driver_cmp(driver, "ntkern") == 0 && subdriver)
+ {
+ /* Driver is relative to system32\drivers\ directory which is relative to windows directory. */
+ systemdir_len = GetWindowsDirectoryA(NULL, 0);
+ driver_path = pci_malloc(a, systemdir_len + 1 + sizeof("system32\\drivers\\")-1 + strlen(driver_ptr) + 4 + 1);
+ systemdir_len = GetWindowsDirectoryA(driver_path, systemdir_len + 1);
+ if (systemdir_len && driver_path[systemdir_len - 1] != '\\')
+ driver_path[systemdir_len++] = '\\';
+ sprintf(driver_path + systemdir_len, "system32\\drivers\\%s%s", driver_ptr, noext ? ".sys" : "");
+ }
+ else if (vmm32)
+ {
+ /* Driver is packed in vmm32.vxd which is stored in system directory. */
+ systemdir_len = GetSystemDirectoryA(NULL, 0);
+ driver_path = pci_malloc(a, systemdir_len + 1 + sizeof("vmm32.vxd ()")-1 + strlen(driver_ptr) + 4 + 1);
+ systemdir_len = GetSystemDirectoryA(driver_path, systemdir_len + 1);
+ if (systemdir_len && driver_path[systemdir_len - 1] != '\\')
+ driver_path[systemdir_len++] = '\\';
+ sprintf(driver_path + systemdir_len, "vmm32.vxd (%s%s)", driver_ptr, noext ? ".vxd" : "");
+ }
+ else
+ {
+ /* Otherwise driver is relative to system directory. */
+ systemdir_len = GetSystemDirectoryA(NULL, 0);
+ driver_path = pci_malloc(a, systemdir_len + 1 + strlen(driver_ptr) + 4 + 1);
+ systemdir_len = GetSystemDirectoryA(driver_path, systemdir_len + 1);
+ if (systemdir_len && driver_path[systemdir_len - 1] != '\\')
+ driver_path[systemdir_len++] = '\\';
+ sprintf(driver_path + systemdir_len, "%s%s", driver_ptr, noext ? ".vxd" : "");
+ }
+ }
+
+ if (subdriver)
+ pci_mfree(subdriver);
+ pci_mfree(driver_list);
+ return driver_path;
+}
+
+static char *
+get_device_driver_path(struct pci_dev *d, SC_HANDLE manager, BOOL manager_supported)
+{
+ struct pci_access *a = d->access;
+ BOOL service_supported = TRUE;
+ DEVINSTID_A devinst_id = NULL;
+ LPWSTR service_name = NULL;
+ ULONG devinst_id_len = 0;
+ char *driver_path = NULL;
+ DEVINST devinst = (DEVINST)d->backend_data;
+ ULONG problem = 0;
+ ULONG status = 0;
+ HKEY key = NULL;
+
+ if (CM_Get_DevNode_Status(&status, &problem, devinst, 0) != CR_SUCCESS || !(status & DN_DRIVER_LOADED))
+ return NULL;
+
+ if (CM_Get_Device_ID_Size(&devinst_id_len, devinst, 0) == CR_SUCCESS)
+ {
+ devinst_id = pci_malloc(a, devinst_id_len + 1);
+ if (CM_Get_Device_IDA(devinst, devinst_id, devinst_id_len + 1, 0) != CR_SUCCESS)
+ {
+ pci_mfree(devinst_id);
+ devinst_id = pci_strdup(a, "UNKNOWN");
+ }
+ }
+ else
+ devinst_id = pci_strdup(a, "UNKNOWN");
+
+ service_name = get_device_service_name(d->access, devinst, devinst_id, &service_supported);
+ if ((!service_name || !manager) && service_supported && manager_supported)
+ goto out;
+ else if (service_name && manager)
+ {
+ driver_path = get_driver_path_for_service(d->access, service_name, manager);
+ goto out;
+ }
+
+ key = get_device_driver_devreg(d->access, devinst, devinst_id);
+ if (key)
+ {
+ driver_path = get_driver_path_for_regkey(d->access, devinst_id, key);
+ goto out;
+ }
+
+out:
+ if (key)
+ RegCloseKey(key);
+ if (service_name)
+ pci_mfree(service_name);
+ pci_mfree(devinst_id);
+ return driver_path;
+}
+
+static void
+fill_drivers(struct pci_access *a)
+{
+ BOOL manager_supported;
+ SC_HANDLE manager;
+ struct pci_dev *d;
+ char *driver;
+ DWORD error;
+
+ /* ERROR_CALL_NOT_IMPLEMENTED is returned on systems without Service Manager support. */
+ manager_supported = TRUE;
+ manager = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
+ if (!manager)
+ {
+ error = GetLastError();
+ if (error != ERROR_CALL_NOT_IMPLEMENTED)
+ a->warning("Cannot open Service Manager with connect right: %s.", win32_strerror(error));
+ else
+ manager_supported = FALSE;
+ }
+
+ for (d = a->devices; d; d = d->next)
+ {
+ driver = get_device_driver_path(d, manager, manager_supported);
+ if (driver)
+ {
+ pci_set_property(d, PCI_FILL_DRIVER, driver);
+ pci_mfree(driver);
+ }
+ d->known_fields |= PCI_FILL_DRIVER;
+ }
+
+ if (manager)
+ CloseServiceHandle(manager);
+}
+
+static const char *
+res_id_to_str(RESOURCEID res_id)
+{
+ static char hex_res_id[sizeof("0xffffffff")];
+
+ if (res_id == ResType_IO)
+ return "IO";
+ else if (res_id == ResType_Mem)
+ return "MEM";
+ else if (res_id == ResType_IRQ)
+ return "IRQ";
+
+ sprintf(hex_res_id, "0x%lx", res_id);
+ return hex_res_id;
+}
+
+static void
+fill_resources(struct pci_dev *d, DEVINST devinst, DEVINSTID_A devinst_id)
+{
+ struct pci_access *a = d->access;
+
+ CONFIGRET cr;
+
+ LOG_CONF config;
+ ULONG problem;
+ ULONG status;
+
+ RES_DES prev_res_des;
+ RES_DES res_des;
+ RESOURCEID res_id;
+ DWORD bar_res_count;
+
+ BOOL is_bar_res;
+ BOOL non_nt_system;
+
+ int last_irq = -1;
+ int last_shared_irq = -1;
+
+ cr = CM_Get_DevNode_Status(&status, &problem, devinst, 0);
+ if (cr != CR_SUCCESS)
+ {
+ a->warning("Cannot retrieve status of PCI device %s: %s.", devinst_id, cr_strerror(cr));
+ return;
+ }
+
+ cr = CR_NO_MORE_LOG_CONF;
+
+ /*
+ * If the device is running then retrieve allocated configuration by PnP
+ * manager which is currently in use by a device.
+ */
+ if (!(status & DN_HAS_PROBLEM))
+ cr = CM_Get_First_Log_Conf(&config, devinst, ALLOC_LOG_CONF);
+
+ /*
+ * If the device is not running or it does not have allocated configuration by
+ * PnP manager then retrieve forced configuration which prevents PnP manager
+ * from assigning resources.
+ */
+ if (cr == CR_NO_MORE_LOG_CONF)
+ cr = CM_Get_First_Log_Conf(&config, devinst, FORCED_LOG_CONF);
+
+ /*
+ * If the device does not have neither allocated configuration by PnP manager
+ * nor forced configuration and it is not disabled in the BIOS then retrieve
+ * boot configuration supplied by the BIOS.
+ */
+ if (cr == CR_NO_MORE_LOG_CONF &&
+ (!(status & DN_HAS_PROBLEM) || problem != CM_PROB_HARDWARE_DISABLED))
+ cr = CM_Get_First_Log_Conf(&config, devinst, BOOT_LOG_CONF);
+
+ if (cr != CR_SUCCESS)
+ {
+ /*
+ * Note: Starting with Windows 8, CM_Get_First_Log_Conf returns
+ * CR_CALL_NOT_IMPLEMENTED when used in a Wow64 scenario.
+ * To request information about the hardware resources on a local machine
+ * it is necessary implement an architecture-native version of the
+ * application using the hardware resource APIs. For example: An AMD64
+ * application for AMD64 systems.
+ */
+ if (cr == CR_CALL_NOT_IMPLEMENTED && win32_is_32bit_on_win8_64bit_system())
+ {
+ static BOOL warn_once = FALSE;
+ if (!warn_once)
+ {
+ warn_once = TRUE;
+ a->warning("Cannot retrieve resources of PCI devices from 32-bit application on 64-bit system.");
+ }
+ }
+ else if (cr != CR_NO_MORE_LOG_CONF)
+ a->warning("Cannot retrieve resources of PCI device %s: %s.", devinst_id, cr_strerror(cr));
+ return;
+ }
+
+ bar_res_count = 0;
+ non_nt_system = win32_is_non_nt_system();
+
+ is_bar_res = TRUE;
+ if (non_nt_system)
+ {
+ BOOL has_child;
+ DEVINST child;
+ ULONG child_name_len;
+ PSTR child_name;
+ BOOL is_bridge;
+
+ if (CM_Get_Child(&child, devinst, 0) != CR_SUCCESS)
+ has_child = FALSE;
+ else if (CM_Get_Device_ID_Size(&child_name_len, child, 0) != CR_SUCCESS)
+ has_child = FALSE;
+ else
+ {
+ child_name_len++;
+ child_name = pci_malloc(a, child_name_len);
+ if (CM_Get_Device_IDA(child, child_name, child_name_len, 0) != CR_SUCCESS)
+ has_child = FALSE;
+ else if (strncmp(child_name, "PCI\\", 4) != 0)
+ has_child = FALSE;
+ else
+ has_child = TRUE;
+ pci_mfree(child_name);
+ }
+
+ if (has_child || d->device_class == PCI_CLASS_BRIDGE_PCI || d->device_class == PCI_CLASS_BRIDGE_CARDBUS)
+ is_bridge = TRUE;
+ else
+ is_bridge = FALSE;
+
+ if (is_bridge)
+ is_bar_res = FALSE;
+ }
+
+ prev_res_des = (RES_DES)config;
+ while ((cr = CM_Get_Next_Res_Des(&res_des, prev_res_des, ResType_All, &res_id, 0)) == CR_SUCCESS)
+ {
+ pciaddr_t start, end, size, flags;
+ ULONG res_des_data_size;
+ PBYTE res_des_data;
+
+ if (prev_res_des != config)
+ CM_Free_Res_Des_Handle(prev_res_des);
+
+ prev_res_des = res_des;
+
+ /* Skip other resources early */
+ if (res_id != ResType_IO && res_id != ResType_Mem && res_id != ResType_IRQ)
+ continue;
+
+ cr = CM_Get_Res_Des_Data_Size(&res_des_data_size, res_des, 0);
+ if (cr != CR_SUCCESS)
+ {
+ a->warning("Cannot retrieve %s resource data of PCI device %s: %s.", res_id_to_str(res_id), devinst_id, cr_strerror(cr));
+ continue;
+ }
+
+ if (!res_des_data_size)
+ {
+ a->warning("Cannot retrieve %s resource data of PCI device %s: %s.", res_id_to_str(res_id), devinst_id, "Empty data");
+ continue;
+ }
+
+ res_des_data = pci_malloc(a, res_des_data_size);
+ cr = CM_Get_Res_Des_Data(res_des, res_des_data, res_des_data_size, 0);
+ if (cr != CR_SUCCESS)
+ {
+ a->warning("Cannot retrieve %s resource data of PCI device %s: %s.", res_id_to_str(res_id), devinst_id, cr_strerror(cr));
+ pci_mfree(res_des_data);
+ continue;
+ }
+
+ /*
+ * There can be more resources with the same id. In this case we are
+ * interested in the last one in the list as at the beginning of the list
+ * can be some virtual resources (which are not set in PCI config space).
+ */
+
+ if (res_id == ResType_IO)
+ {
+ PIO_RESOURCE io_data = (PIO_RESOURCE)res_des_data;
+
+ start = io_data->IO_Header.IOD_Alloc_Base;
+ end = io_data->IO_Header.IOD_Alloc_End;
+ size = (end > start) ? (end - start + 1) : 0;
+ flags = PCI_IORESOURCE_IO;
+
+ /*
+ * If neither 10-bit, 12-bit, nor 16-bit support is presented then
+ * expects that this is 32-bit I/O resource. If resource does not fit
+ * into 16-bit space then it must be 32-bit. If PCI I/O resource is
+ * not 32-bit then it is 16-bit.
+ */
+ if (end <= 0xffff && (io_data->IO_Header.IOD_DesFlags & (fIOD_10_BIT_DECODE|fIOD_12_BIT_DECODE|fIOD_16_BIT_DECODE)))
+ flags |= PCI_IORESOURCE_IO_16BIT_ADDR;
+
+ /*
+ * 16/32-bit non-NT systems do not support these two flags.
+ * Most NT-based Windows versions support only the fIOD_WINDOW_DECODE
+ * flag and put all BAR resources before window resources in this
+ * resource list. So use this fIOD_WINDOW_DECODE flag as separator
+ * between IO/MEM windows and IO/MEM BARs of PCI Bridges.
+ */
+ if (io_data->IO_Header.IOD_DesFlags & fIOD_WINDOW_DECODE)
+ is_bar_res = FALSE;
+ else if (io_data->IO_Header.IOD_DesFlags & fIOD_PORT_BAR)
+ is_bar_res = TRUE;
+
+ if (is_bar_res && bar_res_count < 6)
+ {
+ d->flags[bar_res_count] = flags;
+ d->base_addr[bar_res_count] = start;
+ d->size[bar_res_count] = size;
+ bar_res_count++;
+ }
+ else if (!is_bar_res)
+ {
+ d->bridge_flags[0] = flags;
+ d->bridge_base_addr[0] = start;
+ d->bridge_size[0] = size;
+ d->known_fields |= PCI_FILL_BRIDGE_BASES;
+ }
+ }
+ else if (res_id == ResType_Mem)
+ {
+ PMEM_RESOURCE mem_data = (PMEM_RESOURCE)res_des_data;
+
+ start = mem_data->MEM_Header.MD_Alloc_Base;
+ end = mem_data->MEM_Header.MD_Alloc_End;
+ size = (end > start) ? (end - start + 1) : 0;
+ flags = PCI_IORESOURCE_MEM;
+
+ /*
+ * If fMD_PrefetchAllowed flag is set then this is
+ * PCI Prefetchable Memory resource.
+ */
+ if ((mem_data->MEM_Header.MD_Flags & mMD_Prefetchable) == fMD_PrefetchAllowed)
+ flags |= PCI_IORESOURCE_PREFETCH;
+
+ /* If resource does not fit into 32-bit space then it must be 64-bit. */
+ if (is_bar_res && end > 0xffffffff)
+ flags |= PCI_IORESOURCE_MEM_64;
+
+ /*
+ * These two flags (fMD_WINDOW_DECODE and fMD_MEMORY_BAR) are
+ * unsupported on most Windows versions, so distinguish between
+ * window and BAR based on previous resource type.
+ */
+ if (mem_data->MEM_Header.MD_Flags & fMD_WINDOW_DECODE)
+ is_bar_res = FALSE;
+ else if (mem_data->MEM_Header.MD_Flags & fMD_MEMORY_BAR)
+ is_bar_res = TRUE;
+
+ /* 64-bit BAR resource must be at even position. */
+ if (is_bar_res && (flags & PCI_IORESOURCE_MEM_64) && bar_res_count % 2)
+ bar_res_count++;
+
+ if (is_bar_res && bar_res_count < 6)
+ {
+ d->flags[bar_res_count] = flags;
+ d->base_addr[bar_res_count] = start;
+ d->size[bar_res_count] = size;
+ bar_res_count++;
+ /* 64-bit BAR resource occupies two slots. */
+ if (flags & PCI_IORESOURCE_MEM_64)
+ bar_res_count++;
+ }
+ else if (!is_bar_res && !(flags & PCI_IORESOURCE_PREFETCH))
+ {
+ d->bridge_flags[1] = flags;
+ d->bridge_base_addr[1] = start;
+ d->bridge_size[1] = size;
+ d->known_fields |= PCI_FILL_BRIDGE_BASES;
+ }
+ else if (!is_bar_res && (flags & PCI_IORESOURCE_PREFETCH))
+ {
+ d->bridge_flags[2] = flags;
+ d->bridge_base_addr[2] = start;
+ d->bridge_size[2] = size;
+ d->known_fields |= PCI_FILL_BRIDGE_BASES;
+ }
+ }
+ else if (res_id == ResType_IRQ)
+ {
+ PIRQ_RESOURCE irq_data = (PIRQ_RESOURCE)res_des_data;
+
+ /*
+ * libpci's d->irq should be set to the non-MSI PCI IRQ and therefore
+ * it should be level IRQ which may be shared with other PCI devices
+ * and drivers in the system. As always we want to retrieve the last
+ * IRQ number from the resource list.
+ *
+ * On 16/32-bit non-NT systems is fIRQD_Level set to 2 but on NT
+ * systems to 0. Moreover it looks like that different PCI drivers
+ * on both NT and non-NT systems set bits 0 and 1 to wrong values
+ * and so reported value in this list may be incorrect.
+ *
+ * Therefore take the last level-shared IRQ number from the resource
+ * list and if there is none of this type then take the last IRQ
+ * number from the list.
+ */
+ last_irq = irq_data->IRQ_Header.IRQD_Alloc_Num;
+ if ((irq_data->IRQ_Header.IRQD_Flags & (mIRQD_Share|mIRQD_Edge_Level)) == (fIRQD_Share|fIRQD_Level))
+ last_shared_irq = irq_data->IRQ_Header.IRQD_Alloc_Num;
+
+ /*
+ * IRQ resource on 16/32-bit non-NT systems is separator between
+ * IO/MEM windows and IO/MEM BARs of PCI Bridges. After the IRQ
+ * resource are IO/MEM BAR resources.
+ */
+ if (!is_bar_res && non_nt_system)
+ is_bar_res = TRUE;
+ }
+
+ pci_mfree(res_des_data);
+ }
+ if (cr != CR_NO_MORE_RES_DES)
+ a->warning("Cannot retrieve resources of PCI device %s: %s.", devinst_id, cr_strerror(cr));
+
+ if (prev_res_des != config)
+ CM_Free_Res_Des_Handle(prev_res_des);
+
+ CM_Free_Log_Conf_Handle(config);
+
+ /* Set the last IRQ from the resource list to pci_dev. */
+ if (last_shared_irq >= 0)
+ d->irq = last_shared_irq;
+ else if (last_irq >= 0)
+ d->irq = last_irq;
+ if (last_shared_irq >= 0 || last_irq >= 0)
+ d->known_fields |= PCI_FILL_IRQ;
+
+ if (bar_res_count > 0)
+ d->known_fields |= PCI_FILL_BASES | PCI_FILL_SIZES | PCI_FILL_IO_FLAGS;
+}
+
+static BOOL
+get_device_location(struct pci_access *a, DEVINST devinst, DEVINSTID_A devinst_id, unsigned int *domain, unsigned int *bus, unsigned int *dev, unsigned int *func)
+{
+ ULONG reg_type, reg_len;
+ CONFIGRET cr;
+ BOOL have_bus, have_devfunc;
+ DWORD drp_bus_num, drp_address;
+
+ *domain = 0;
+ have_bus = FALSE;
+ have_devfunc = FALSE;
+
+ /*
+ * DRP_BUSNUMBER consists of PCI domain number in high 24 bits
+ * and PCI bus number in low 8 bits.
+ */
+ reg_len = sizeof(drp_bus_num);
+ cr = CM_Get_DevNode_Registry_PropertyA(devinst, CM_DRP_BUSNUMBER, &reg_type, &drp_bus_num, &reg_len, 0);
+ if (cr == CR_SUCCESS && reg_type == REG_DWORD && reg_len == sizeof(drp_bus_num))
+ {
+ *domain = drp_bus_num >> 8;
+ *bus = drp_bus_num & 0xff;
+ have_bus = TRUE;
+ }
+
+ /*
+ * DRP_ADDRESS consists of PCI device number in high 16 bits
+ * and PCI function number in low 16 bits.
+ */
+ reg_len = sizeof(drp_address);
+ cr = CM_Get_DevNode_Registry_PropertyA(devinst, CM_DRP_ADDRESS, &reg_type, &drp_address, &reg_len, 0);
+ if (cr == CR_SUCCESS && reg_type == REG_DWORD && reg_len == sizeof(drp_address))
+ {
+ *dev = drp_address >> 16;
+ *func = drp_address & 0xffff;
+ have_devfunc = TRUE;
+ }
+
+ /*
+ * Device Instance Id for PCI devices is of format:
+ * "<enumerator>\\<device_id>\\<instance_id>"
+ * where:
+ * "<enumerator>" is "PCI"
+ * "<device_id>" is "VEN_####&DEV_####&SUBSYS_########&REV_##"
+ * and "<instance_id>" for PCI devices is at least in one of following format:
+ * "BUS_##&DEV_##&FUNC_##"
+ * "##.." (sequence of devfn hex bytes, where bytes represents tree path to the root)
+ * "#..&#..&#..&#.." (four hex numbers separated by "&"; meaning is unknown yet)
+ *
+ * First two formats are used only on systems without support for multiple
+ * domains. The second format uses intel-conf encoding of device and function
+ * number: Low 3 bits is function number and high 5 bits is device number.
+ * Bus number is not really encoded in second format!
+ *
+ * The third format is used on systems with support for multiple domains but
+ * format is variable length and currently its meaning is unknown. Apparently
+ * it looks like that DRP_BUSNUMBER and DRP_ADDRESS registry properties are
+ * supported on these systems.
+ *
+ * If DRP_BUSNUMBER or DRP_ADDRESS failed then try to parse PCI bus, device
+ * and function numbers from Instance Id part.
+ */
+ if (!have_bus || !have_devfunc)
+ {
+ const char *device_id0 = strchr(devinst_id, '\\');
+ const char *instance_id0 = device_id0 ? strchr(device_id0 + 1, '\\') : NULL;
+ const char *instance_id = instance_id0 ? instance_id0 + 1 : NULL;
+ unsigned int devfn;
+
+ if (instance_id)
+ {
+ if (fmt_validate(instance_id, strlen(instance_id), "BUS_##&DEV_##&FUNC_##") &&
+ sscanf(instance_id, "BUS_%x&DEV_%x&FUNC_%x", bus, dev, func) == 3)
+ {
+ have_bus = TRUE;
+ have_devfunc = TRUE;
+ }
+ else if (seq_xdigit_validate(instance_id, 2, 2) &&
+ sscanf(instance_id, "%2x", &devfn) == 1)
+ {
+ *dev = devfn >> 3;
+ *func = devfn & 0x7;
+ have_devfunc = TRUE;
+ }
+ }
+ }
+
+ /*
+ * Virtual IRQ holder devices do not have assigned any bus/dev/func number and
+ * have "IRQHOLDER" in their Device Id part. So skip them.
+ */
+ if (!have_bus && !have_devfunc && strncmp(devinst_id, "PCI\\IRQHOLDER\\", 14) == 0)
+ return FALSE;
+
+ /*
+ * When some numbers cannot be retrieved via cfgmgr32 then set them to zeros
+ * to have structure initialized. It makes sense to report via libpci also
+ * such "incomplete" device as cfgmgr32 can provide additional information
+ * like device/vendor ids or assigned resources.
+ */
+ if (!have_bus && !have_devfunc)
+ {
+ *bus = *dev = *func = 0;
+ a->warning("Cannot retrieve bus, device and function numbers for PCI device %s: %s.", devinst_id, cr_strerror(cr));
+ }
+ else if (!have_bus)
+ {
+ *bus = 0;
+ a->warning("Cannot retrieve bus number for PCI device %s: %s.", devinst_id, cr_strerror(cr));
+ }
+ else if (!have_devfunc)
+ {
+ *dev = *func = 0;
+ a->warning("Cannot retrieve device and function numbers for PCI device %s: %s.", devinst_id, cr_strerror(cr));
+ }
+
+ return TRUE;
+}
+
+static void
+fill_data_from_string(struct pci_dev *d, const char *str)
+{
+ BOOL have_device_id;
+ BOOL have_vendor_id;
+ BOOL have_prog_if;
+ BOOL have_rev_id;
+ const char *endptr, *endptr2;
+ unsigned int hex;
+ int len;
+
+ have_device_id = have_vendor_id = (d->known_fields & PCI_FILL_IDENT);
+ have_prog_if = have_rev_id = (d->known_fields & PCI_FILL_CLASS_EXT);
+
+ while (1)
+ {
+ endptr = strchr(str, '&');
+ endptr2 = strchr(str, '\\');
+ if (endptr2 && (!endptr || endptr > endptr2))
+ endptr = endptr2;
+ len = endptr ? endptr-str : (int)strlen(str);
+
+ if (!have_vendor_id &&
+ fmt_validate(str, len, "VEN_####") &&
+ sscanf(str, "VEN_%x", &hex) == 1)
+ {
+ d->vendor_id = hex;
+ have_vendor_id = TRUE;
+ }
+ else if (!have_device_id &&
+ fmt_validate(str, len, "DEV_####") &&
+ sscanf(str, "DEV_%x", &hex) == 1)
+ {
+ d->device_id = hex;
+ have_device_id = TRUE;
+ }
+ else if (!(d->known_fields & PCI_FILL_SUBSYS) &&
+ fmt_validate(str, len, "SUBSYS_########") &&
+ sscanf(str, "SUBSYS_%x", &hex) == 1)
+ {
+ d->subsys_vendor_id = hex & 0xffff;
+ d->subsys_id = hex >> 16;
+ d->known_fields |= PCI_FILL_SUBSYS;
+ }
+ else if (!have_rev_id &&
+ fmt_validate(str, len, "REV_##") &&
+ sscanf(str, "REV_%x", &hex) == 1)
+ {
+ d->rev_id = hex;
+ have_rev_id = TRUE;
+ }
+ else if (!((d->known_fields & PCI_FILL_CLASS) && have_prog_if) &&
+ (fmt_validate(str, len, "CC_####") || fmt_validate(str, len, "CC_######")) &&
+ sscanf(str, "CC_%x", &hex) == 1)
+ {
+ if (len == 9)
+ {
+ if (!have_prog_if)
+ {
+ d->prog_if = hex & 0xff;
+ have_prog_if = TRUE;
+ }
+ hex >>= 8;
+ }
+ if (!(d->known_fields & PCI_FILL_CLASS))
+ {
+ d->device_class = hex;
+ d->known_fields |= PCI_FILL_CLASS;
+ }
+ }
+
+ if (!endptr || endptr == endptr2)
+ break;
+
+ str = endptr + 1;
+ }
+
+ if ((have_device_id || d->device_id) && (have_vendor_id || d->vendor_id))
+ d->known_fields |= PCI_FILL_IDENT;
+
+ if ((have_prog_if || d->prog_if) && (have_rev_id || d->rev_id))
+ d->known_fields |= PCI_FILL_CLASS_EXT;
+}
+
+static void
+fill_data_from_devinst_id(struct pci_dev *d, DEVINSTID_A devinst_id)
+{
+ const char *device_id;
+
+ device_id = strchr(devinst_id, '\\');
+ if (!device_id)
+ return;
+ device_id++;
+
+ /*
+ * Device Id part of Device Instance Id is in format:
+ * "VEN_####&DEV_####&SUBSYS_########&REV_##"
+ */
+ fill_data_from_string(d, device_id);
+}
+
+static void
+fill_data_from_hardware_ids(struct pci_dev *d, DEVINST devinst, DEVINSTID_A devinst_id)
+{
+ ULONG reg_type, reg_size, reg_len;
+ struct pci_access *a = d->access;
+ char *hardware_ids = NULL;
+ const char *str;
+ CONFIGRET cr;
+
+ reg_size = 0;
+ cr = CM_Get_DevNode_Registry_PropertyA(devinst, CM_DRP_HARDWAREID, &reg_type, NULL, &reg_size, 0);
+ if (cr != CR_SUCCESS && cr != CR_BUFFER_SMALL)
+ {
+ a->warning("Cannot retrieve hardware ids for PCI device %s: %s.", devinst_id, cr_strerror(cr));
+ return;
+ }
+ else if (reg_type != REG_MULTI_SZ && reg_type != REG_SZ) /* Older Windows versions return REG_SZ and new versions REG_MULTI_SZ. */
+ {
+ a->warning("Cannot retrieve hardware ids for PCI device %s: Hardware ids are stored as unknown type 0x%lx.", devinst_id, reg_type);
+ return;
+ }
+
+retry:
+ /*
+ * Returned size is on older Windows versions without nul-term char.
+ * So explicitly increase size and fill nul-term byte.
+ */
+ reg_size++;
+ hardware_ids = pci_malloc(a, reg_size);
+ reg_len = reg_size;
+ cr = CM_Get_DevNode_Registry_PropertyA(devinst, CM_DRP_HARDWAREID, &reg_type, hardware_ids, &reg_len, 0);
+ hardware_ids[reg_size - 1] = 0;
+ if (reg_len > reg_size)
+ {
+ pci_mfree(hardware_ids);
+ reg_size = reg_len;
+ goto retry;
+ }
+ else if (cr != CR_SUCCESS)
+ {
+ a->warning("Cannot retrieve hardware ids for PCI device %s: %s.", devinst_id, cr_strerror(cr));
+ pci_mfree(hardware_ids);
+ return;
+ }
+ else if (reg_type != REG_MULTI_SZ && reg_type != REG_SZ) /* Older Windows versions return REG_SZ and new versions REG_MULTI_SZ. */
+ {
+ a->warning("Cannot retrieve hardware ids for PCI device %s: Hardware ids are stored as unknown type 0x%lx.", devinst_id, reg_type);
+ pci_mfree(hardware_ids);
+ return;
+ }
+
+ /*
+ * Hardware ids is nul-separated nul-term string list where each string has
+ * one of the following format:
+ * "PCI\\VEN_####&DEV_####&SUBSYS_########&REV_##"
+ * "PCI\\VEN_####&DEV_####&SUBSYS_########"
+ * "PCI\\VEN_####&DEV_####&REV_##&CC_####"
+ * "PCI\\VEN_####&DEV_####&CC_######"
+ * "PCI\\VEN_####&DEV_####&CC_####"
+ * "PCI\\VEN_####&DEV_####&REV_##"
+ * "PCI\\VEN_####&DEV_####"
+ */
+ for (str = hardware_ids; *str != '\0'; str += strlen(str) + 1)
+ {
+ if (strncmp(str, "PCI\\", 4) != 0)
+ continue;
+ str += 4;
+ fill_data_from_string(d, str);
+ }
+
+ pci_mfree(hardware_ids);
+}
+
+static void
+scan_devinst_id(struct pci_access *a, DEVINSTID_A devinst_id)
+{
+ unsigned int domain, bus, dev, func;
+ struct pci_dev *d;
+ DEVINST devinst;
+ CONFIGRET cr;
+
+ cr = CM_Locate_DevNodeA(&devinst, devinst_id, CM_LOCATE_DEVNODE_NORMAL);
+ if (cr != CR_SUCCESS)
+ {
+ /* Do not show warning when device is not present (= does not match NORMAL flag). */
+ if (cr != CR_NO_SUCH_DEVNODE)
+ a->warning("Cannot retrieve handle for device %s: %s.", devinst_id, cr_strerror(cr));
+ return;
+ }
+
+ /* get_device_location() returns FALSE if devinst is not real PCI device. */
+ if (!get_device_location(a, devinst, devinst_id, &domain, &bus, &dev, &func))
+ return;
+
+ d = pci_get_dev(a, domain, bus, dev, func);
+ pci_link_dev(a, d);
+ if (!d->access->backend_data)
+ d->no_config_access = 1;
+ d->backend_data = (void *)devinst;
+
+ /* Parse device id part of devinst id and fill details into pci_dev. */
+ if (!a->buscentric)
+ fill_data_from_devinst_id(d, devinst_id);
+
+ /* Retrieve hardware ids of devinst, parse them and fill details into pci_dev. */
+ if (!a->buscentric)
+ fill_data_from_hardware_ids(d, devinst, devinst_id);
+
+ if (!a->buscentric)
+ fill_resources(d, devinst, devinst_id);
+
+ /*
+ * Set parent field to cfgmgr32 parent devinst handle and backend_data field to current
+ * devinst handle. At later stage in win32_cfgmgr32_scan() when all pci_dev
+ * devices are linked, change every devinst handle by pci_dev.
+ */
+ if (!a->buscentric)
+ {
+ DEVINST parent_devinst;
+ if (CM_Get_Parent(&parent_devinst, devinst, 0) != CR_SUCCESS)
+ {
+ parent_devinst = 0;
+ a->warning("Cannot retrieve parent handle for device %s: %s.", devinst_id, cr_strerror(cr));
+ }
+ d->parent = (void *)parent_devinst;
+ }
+}
+
+static void
+win32_cfgmgr32_scan(struct pci_access *a)
+{
+ ULONG devinst_id_list_size;
+ PCHAR devinst_id_list;
+ DEVINSTID_A devinst_id;
+ struct pci_dev *d;
+ CONFIGRET cr;
+
+#if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
+ if (!resolve_cfgmgr32_functions())
+ {
+ a->warning("Required cfgmgr32.dll functions are unavailable.");
+ return;
+ }
+#endif
+
+ /*
+ * Explicitly initialize size to zero as wine cfgmgr32 implementation does not
+ * support this API but returns CR_SUCCESS without touching size argument.
+ */
+ devinst_id_list_size = 0;
+ cr = CM_Get_Device_ID_List_SizeA(&devinst_id_list_size, "PCI", CM_GETIDLIST_FILTER_ENUMERATOR);
+ if (cr != CR_SUCCESS)
+ {
+ a->warning("Cannot retrieve list of PCI devices: %s.", cr_strerror(cr));
+ return;
+ }
+ else if (devinst_id_list_size <= 1)
+ {
+ a->warning("Cannot retrieve list of PCI devices: No device was found.");
+ return;
+ }
+
+ devinst_id_list = pci_malloc(a, devinst_id_list_size);
+ cr = CM_Get_Device_ID_ListA("PCI", devinst_id_list, devinst_id_list_size, CM_GETIDLIST_FILTER_ENUMERATOR);
+ if (cr != CR_SUCCESS)
+ {
+ a->warning("Cannot retrieve list of PCI devices: %s.", cr_strerror(cr));
+ pci_mfree(devinst_id_list);
+ return;
+ }
+
+ /* Register pci_dev for each cfgmgr32 devinst handle. */
+ for (devinst_id = devinst_id_list; *devinst_id; devinst_id += strlen(devinst_id) + 1)
+ scan_devinst_id(a, devinst_id);
+
+ /* Fill all drivers. */
+ if (!a->buscentric)
+ fill_drivers(a);
+
+ /* Switch parent fields from cfgmgr32 devinst handle to pci_dev. */
+ if (!a->buscentric)
+ {
+ struct pci_dev *d1, *d2;
+ for (d1 = a->devices; d1; d1 = d1->next)
+ {
+ for (d2 = a->devices; d2; d2 = d2->next)
+ if ((DEVINST)d1->parent == (DEVINST)d2->backend_data)
+ break;
+ d1->parent = d2;
+ if (d1->parent)
+ d1->known_fields |= PCI_FILL_PARENT;
+ }
+ }
+
+ /* devinst stored in ->backend_data is not needed anymore, clear it. */
+ for (d = a->devices; d; d = d->next)
+ d->backend_data = NULL;
+
+ 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)
+{
+ ULONG devinst_id_list_size;
+ CONFIGRET cr;
+
+#if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
+ if (!resolve_cfgmgr32_functions())
+ {
+ a->debug("Required cfgmgr32.dll functions are unavailable.");
+ return 0;
+ }
+#endif
+
+ /*
+ * Explicitly initialize size to zero as wine cfgmgr32 implementation does not
+ * support this API but returns CR_SUCCESS without touching size argument.
+ */
+ devinst_id_list_size = 0;
+ cr = CM_Get_Device_ID_List_SizeA(&devinst_id_list_size, "PCI", CM_GETIDLIST_FILTER_ENUMERATOR);
+ if (cr != CR_SUCCESS)
+ {
+ a->debug("CM_Get_Device_ID_List_SizeA(\"PCI\"): %s.", cr_strerror(cr));
+ return 0;
+ }
+ else if (devinst_id_list_size <= 1)
+ {
+ a->debug("CM_Get_Device_ID_List_SizeA(\"PCI\"): No device was found.");
+ return 0;
+ }
+
+ return 1;
+}
+
+static void
+win32_cfgmgr32_fill_info(struct pci_dev *d, unsigned int flags)
+{
+ /*
+ * All available flags were filled by win32_cfgmgr32_scan().
+ * Filling more flags is possible only from config space.
+ */
+ if (!d->access->backend_data)
+ 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->backend_data;
+ struct pci_dev *dcfg = d->backend_data;
+
+ if (!acfg)
+ return pci_emulated_read(d, pos, buf, len);
+
+ if (!dcfg)
+ d->backend_data = 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, int pos, byte *buf, int len)
+{
+ struct pci_access *a = d->access;
+ struct pci_access *acfg = a->backend_data;
+ struct pci_dev *dcfg = d->backend_data;
+
+ if (!acfg)
+ return 0;
+
+ if (!dcfg)
+ d->backend_data = 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)
+{
+ struct pci_dev *dcfg = d->backend_data;
+
+ if (dcfg)
+ pci_free_dev(dcfg);
+}
+
+static void
+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_clone_access(a);
+ acfg->method = PCI_ACCESS_AUTO;
+ }
+ else if (strcmp(cfgmethod, "none") == 0 ||
+ strcmp(cfgmethod, "win32-cfgmgr32") == 0)
+ {
+ if (a->writeable)
+ a->error("Write access requested but option win32.cfgmethod was not set.");
+ return;
+ }
+ else
+ {
+ int m = pci_lookup_method(cfgmethod);
+ if (m < 0)
+ a->error("Option win32.cfgmethod is set to an unknown access method \"%s\".", cfgmethod);
+ acfg = pci_clone_access(a);
+ acfg->method = m;
+ }
+
+ a->debug("Loading config space access method...\n");
+ if (!pci_init_internal(acfg, PCI_ACCESS_WIN32_CFGMGR32))
+ {
+ 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 found.");
+ return;
+ }
+
+ a->backend_data = acfg;
+}
+
+static void
+win32_cfgmgr32_cleanup(struct pci_access *a)
+{
+ struct pci_access *acfg = a->backend_data;
+
+ if (acfg)
+ pci_cleanup(acfg);
+}
+
+struct pci_methods pm_win32_cfgmgr32 = {
+ "win32-cfgmgr32",
+ "Win32 device listing via Configuration Manager",
+ win32_cfgmgr32_config,
+ win32_cfgmgr32_detect,
+ win32_cfgmgr32_init,
+ win32_cfgmgr32_cleanup,
+ win32_cfgmgr32_scan,
+ win32_cfgmgr32_fill_info,
+ win32_cfgmgr32_read,
+ win32_cfgmgr32_write,
+ NULL, /* read_vpd */
+ NULL, /* init_dev */
+ win32_cfgmgr32_cleanup_dev,
+};
diff --git a/lib/win32-helpers.c b/lib/win32-helpers.c
new file mode 100644
index 0000000..d370d5c
--- /dev/null
+++ b/lib/win32-helpers.c
@@ -0,0 +1,1387 @@
+/*
+ * The PCI Library -- Win32 helper functions
+ *
+ * Copyright (c) 2023 Pali Rohár <pali@kernel.org>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <windows.h>
+
+#include <stdio.h> /* for sprintf() */
+
+#include "win32-helpers.h"
+
+/* Unfortunately i586-mingw32msvc toolchain does not provide this constant. */
+#ifndef PROCESS_QUERY_LIMITED_INFORMATION
+#define PROCESS_QUERY_LIMITED_INFORMATION 0x1000
+#endif
+
+/* Unfortunately some toolchains do not provide this constant. */
+#ifndef SE_IMPERSONATE_NAME
+#define SE_IMPERSONATE_NAME TEXT("SeImpersonatePrivilege")
+#endif
+
+/* Unfortunately some toolchains do not provide these constants. */
+#ifndef SE_DACL_AUTO_INHERIT_REQ
+#define SE_DACL_AUTO_INHERIT_REQ 0x0100
+#endif
+#ifndef SE_SACL_AUTO_INHERIT_REQ
+#define SE_SACL_AUTO_INHERIT_REQ 0x0200
+#endif
+#ifndef SE_DACL_AUTO_INHERITED
+#define SE_DACL_AUTO_INHERITED 0x0400
+#endif
+#ifndef SE_SACL_AUTO_INHERITED
+#define SE_SACL_AUTO_INHERITED 0x0800
+#endif
+
+/*
+ * These psapi functions are available in kernel32.dll library with K32 prefix
+ * on Windows 7 and higher systems. On older Windows systems these functions are
+ * available in psapi.dll libary without K32 prefix. So resolve pointers to
+ * these functions dynamically at runtime from the available system library.
+ * Function GetProcessImageFileNameW() is not available on Windows 2000 and
+ * older systems.
+ */
+typedef BOOL (WINAPI *EnumProcessesProt)(DWORD *lpidProcess, DWORD cb, DWORD *cbNeeded);
+typedef DWORD (WINAPI *GetProcessImageFileNameWProt)(HANDLE hProcess, LPWSTR lpImageFileName, DWORD nSize);
+typedef DWORD (WINAPI *GetModuleFileNameExWProt)(HANDLE hProcess, HMODULE hModule, LPWSTR lpImageFileName, DWORD nSize);
+
+/*
+ * These aclapi function is available in advapi.dll library on Windows 2000
+ * and higher systems.
+ */
+typedef BOOL (WINAPI *SetSecurityDescriptorControlProt)(PSECURITY_DESCRIPTOR pSecurityDescriptor, SECURITY_DESCRIPTOR_CONTROL ControlBitsOfInterest, SECURITY_DESCRIPTOR_CONTROL ControlBitsToSet);
+
+/*
+ * This errhandlingapi function is available in kernel32.dll library on
+ * Windows 7 and higher systems.
+ */
+typedef BOOL (WINAPI *SetThreadErrorModeProt)(DWORD dwNewMode, LPDWORD lpOldMode);
+
+
+static DWORD
+format_message_from_system(DWORD win32_error_id, DWORD lang_id, LPSTR buffer, DWORD size)
+{
+ return FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, win32_error_id, lang_id, buffer, size, NULL);
+}
+
+const char *
+win32_strerror(DWORD win32_error_id)
+{
+ /*
+ * Use static buffer which is large enough.
+ * Hopefully no Win32 API error message string is longer than 4 kB.
+ */
+ static char buffer[4096];
+ DWORD len;
+
+ /*
+ * If it is possible show error messages in US English language.
+ * International Windows editions do not have to provide error
+ * messages in English language, so fallback to the language
+ * which system provides (neutral).
+ */
+ len = format_message_from_system(win32_error_id, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), buffer, sizeof(buffer));
+ if (!len)
+ len = format_message_from_system(win32_error_id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buffer, sizeof(buffer));
+
+ /* FormatMessage() automatically appends ".\r\n" to the error message. */
+ if (len && buffer[len-1] == '\n')
+ buffer[--len] = '\0';
+ if (len && buffer[len-1] == '\r')
+ buffer[--len] = '\0';
+ if (len && buffer[len-1] == '.')
+ buffer[--len] = '\0';
+
+ if (!len)
+ sprintf(buffer, "Unknown Win32 error %lu", win32_error_id);
+
+ return buffer;
+}
+
+BOOL
+win32_is_non_nt_system(void)
+{
+ OSVERSIONINFOA version;
+ version.dwOSVersionInfoSize = sizeof(version);
+ return GetVersionExA(&version) && version.dwPlatformId < VER_PLATFORM_WIN32_NT;
+}
+
+BOOL
+win32_is_32bit_on_64bit_system(void)
+{
+ BOOL (WINAPI *MyIsWow64Process)(HANDLE, PBOOL);
+ HMODULE kernel32;
+ BOOL is_wow64;
+
+ /*
+ * Check for 64-bit system via IsWow64Process() function exported
+ * from 32-bit kernel32.dll library available on the 64-bit systems.
+ * Resolve pointer to this function at runtime as this code path is
+ * primary running on 32-bit systems where are not available 64-bit
+ * functions.
+ */
+
+ kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
+ if (!kernel32)
+ return FALSE;
+
+ MyIsWow64Process = (void *)GetProcAddress(kernel32, "IsWow64Process");
+ if (!MyIsWow64Process)
+ return FALSE;
+
+ if (!MyIsWow64Process(GetCurrentProcess(), &is_wow64))
+ return FALSE;
+
+ return is_wow64;
+}
+
+BOOL
+win32_is_32bit_on_win8_64bit_system(void)
+{
+#ifdef _WIN64
+ return FALSE;
+#else
+ OSVERSIONINFOA version;
+
+ /* Check for Windows 8 (NT 6.2). */
+ version.dwOSVersionInfoSize = sizeof(version);
+ if (!GetVersionExA(&version) ||
+ version.dwPlatformId != VER_PLATFORM_WIN32_NT ||
+ version.dwMajorVersion < 6 ||
+ (version.dwMajorVersion == 6 && version.dwMinorVersion < 2))
+ return FALSE;
+
+ return win32_is_32bit_on_64bit_system();
+#endif
+}
+
+/*
+ * Change error mode of the current thread. If it is not possible then change
+ * error mode of the whole process. Always returns previous error mode.
+ */
+UINT
+win32_change_error_mode(UINT new_mode)
+{
+ SetThreadErrorModeProt MySetThreadErrorMode = NULL;
+ HMODULE kernel32;
+ DWORD old_mode;
+
+ /*
+ * Function SetThreadErrorMode() was introduced in Windows 7, so use
+ * GetProcAddress() for compatibility with older systems.
+ */
+ kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
+ if (kernel32)
+ MySetThreadErrorMode = (SetThreadErrorModeProt)(LPVOID)GetProcAddress(kernel32, "SetThreadErrorMode");
+
+ if (MySetThreadErrorMode &&
+ MySetThreadErrorMode(new_mode, &old_mode))
+ return old_mode;
+
+ /*
+ * Fallback to function SetErrorMode() which modifies error mode of the
+ * whole process and returns old mode.
+ */
+ return SetErrorMode(new_mode);
+}
+
+/*
+ * Check if the current thread has particular privilege in current active access
+ * token. Case when it not possible to determinate it (e.g. current thread does
+ * not have permission to open its own current active access token) is evaluated
+ * as thread does not have that privilege.
+ */
+BOOL
+win32_have_privilege(LUID luid_privilege)
+{
+ PRIVILEGE_SET priv;
+ HANDLE token;
+ BOOL ret;
+
+ /*
+ * If the current thread does not have active access token then thread
+ * uses primary process access token for all permission checks.
+ */
+ if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &token) &&
+ (GetLastError() != ERROR_NO_TOKEN ||
+ !OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)))
+ return FALSE;
+
+ priv.PrivilegeCount = 1;
+ priv.Control = PRIVILEGE_SET_ALL_NECESSARY;
+ priv.Privilege[0].Luid = luid_privilege;
+ priv.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED;
+
+ if (!PrivilegeCheck(token, &priv, &ret))
+ return FALSE;
+
+ return ret;
+}
+
+/*
+ * Enable or disable particular privilege in specified access token.
+ *
+ * Note that it is not possible to disable privilege in access token with
+ * SE_PRIVILEGE_ENABLED_BY_DEFAULT attribute. This function does not check
+ * this case and incorrectly returns no error even when disabling failed.
+ * Rationale for this decision: Simplification of this function as WinAPI
+ * call AdjustTokenPrivileges() does not signal error in this case too.
+ */
+static BOOL
+set_privilege(HANDLE token, LUID luid_privilege, BOOL enable)
+{
+ TOKEN_PRIVILEGES token_privileges;
+
+ token_privileges.PrivilegeCount = 1;
+ token_privileges.Privileges[0].Luid = luid_privilege;
+ token_privileges.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0;
+
+ /*
+ * WinAPI function AdjustTokenPrivileges() success also when not all
+ * privileges were enabled. It is always required to check for failure
+ * via GetLastError() call. AdjustTokenPrivileges() always sets error
+ * also when it success, as opposite to other WinAPI functions.
+ */
+ if (!AdjustTokenPrivileges(token, FALSE, &token_privileges, sizeof(token_privileges), NULL, NULL) ||
+ GetLastError() != ERROR_SUCCESS)
+ return FALSE;
+
+ return TRUE;
+}
+
+/*
+ * Change access token for the current thread to new specified access token.
+ * Previously active access token is stored in old_token variable and can be
+ * used for reverting to this access token. It is set to NULL if the current
+ * thread previously used primary process access token.
+ */
+BOOL
+win32_change_token(HANDLE new_token, HANDLE *old_token)
+{
+ HANDLE token;
+
+ if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &token))
+ {
+ if (GetLastError() != ERROR_NO_TOKEN)
+ return FALSE;
+ token = NULL;
+ }
+
+ if (!ImpersonateLoggedOnUser(new_token))
+ {
+ if (token)
+ CloseHandle(token);
+ return FALSE;
+ }
+
+ *old_token = token;
+ return TRUE;
+}
+
+/*
+ * Change access token for the current thread to the primary process access
+ * token. This function fails also when the current thread already uses primary
+ * process access token.
+ */
+static BOOL
+change_token_to_primary(HANDLE *old_token)
+{
+ HANDLE token;
+
+ if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &token))
+ return FALSE;
+
+ RevertToSelf();
+
+ *old_token = token;
+ return TRUE;
+}
+
+/*
+ * Revert to the specified access token for the current thread. When access
+ * token is specified as NULL then revert to the primary process access token.
+ * Use to revert after win32_change_token() or change_token_to_primary() call.
+ */
+VOID
+win32_revert_to_token(HANDLE token)
+{
+ /*
+ * If SetThreadToken() call fails then there is no option to revert to
+ * the specified previous thread access token. So in this case revert to
+ * the primary process access token.
+ */
+ if (!token || !SetThreadToken(NULL, token))
+ RevertToSelf();
+ if (token)
+ CloseHandle(token);
+}
+
+/*
+ * Enable particular privilege for the current thread. And set method how to
+ * revert this privilege (if to revert whole token or only privilege).
+ */
+BOOL
+win32_enable_privilege(LUID luid_privilege, HANDLE *revert_token, BOOL *revert_only_privilege)
+{
+ HANDLE thread_token;
+ HANDLE new_token;
+
+ if (OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES, TRUE, &thread_token))
+ {
+ if (set_privilege(thread_token, luid_privilege, TRUE))
+ {
+ /*
+ * Indicate that correct revert method is just to
+ * disable privilege in access token.
+ */
+ if (revert_token && revert_only_privilege)
+ {
+ *revert_token = thread_token;
+ *revert_only_privilege = TRUE;
+ }
+ else
+ {
+ CloseHandle(thread_token);
+ }
+ return TRUE;
+ }
+ CloseHandle(thread_token);
+ /*
+ * If enabling privilege failed then try to enable it via
+ * primary process access token.
+ */
+ }
+
+ /*
+ * If the current thread has already active thread access token then
+ * open it with just impersonate right as it would be used only for
+ * future revert.
+ */
+ if (revert_token && revert_only_privilege)
+ {
+ if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &thread_token))
+ {
+ if (GetLastError() != ERROR_NO_TOKEN)
+ return FALSE;
+ thread_token = NULL;
+ }
+
+ /*
+ * If current thread has no access token (and uses primary
+ * process access token) or it does not have permission to
+ * adjust privileges or it does not have specified privilege
+ * then create a copy of the primary process access token,
+ * assign it for the current thread (= impersonate self)
+ * and then try adjusting privilege again.
+ */
+ if (!ImpersonateSelf(SecurityImpersonation))
+ {
+ if (thread_token)
+ CloseHandle(thread_token);
+ return FALSE;
+ }
+ }
+
+ if (!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES, TRUE, &new_token))
+ {
+ /* thread_token is set only when we were asked for revert method. */
+ if (revert_token && revert_only_privilege)
+ win32_revert_to_token(thread_token);
+ return FALSE;
+ }
+
+ if (!set_privilege(new_token, luid_privilege, TRUE))
+ {
+ CloseHandle(new_token);
+ /* thread_token is set only when we were asked for revert method. */
+ if (revert_token && revert_only_privilege)
+ win32_revert_to_token(thread_token);
+ return FALSE;
+ }
+
+ /*
+ * Indicate that correct revert method is to change to the previous
+ * access token. Either to the primary process access token or to the
+ * previous thread access token.
+ */
+ if (revert_token && revert_only_privilege)
+ {
+ *revert_token = thread_token;
+ *revert_only_privilege = FALSE;
+ }
+ return TRUE;
+}
+
+/*
+ * Revert particular privilege for the current thread was previously enabled by
+ * win32_enable_privilege() call. Either disable privilege in specified access token
+ * or revert to previous access token.
+ */
+VOID
+win32_revert_privilege(LUID luid_privilege, HANDLE revert_token, BOOL revert_only_privilege)
+{
+ if (revert_only_privilege)
+ {
+ set_privilege(revert_token, luid_privilege, FALSE);
+ CloseHandle(revert_token);
+ }
+ else
+ {
+ win32_revert_to_token(revert_token);
+ }
+}
+
+/*
+ * Return owner of the access token used by the current thread. Buffer for
+ * returned owner needs to be released by LocalFree() call.
+ */
+static TOKEN_OWNER *
+get_current_token_owner(VOID)
+{
+ HANDLE token;
+ DWORD length;
+ TOKEN_OWNER *owner;
+
+ /*
+ * If the current thread does not have active access token then thread
+ * uses primary process access token for all permission checks.
+ */
+ if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &token) &&
+ (GetLastError() != ERROR_NO_TOKEN ||
+ !OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)))
+ return NULL;
+
+ if (!GetTokenInformation(token, TokenOwner, NULL, 0, &length) &&
+ GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+ {
+ CloseHandle(token);
+ return NULL;
+ }
+
+retry:
+ owner = (TOKEN_OWNER *)LocalAlloc(LPTR, length);
+ if (!owner)
+ {
+ CloseHandle(token);
+ return NULL;
+ }
+
+ if (!GetTokenInformation(token, TokenOwner, owner, length, &length))
+ {
+ /*
+ * Length of token owner (SID) buffer between two get calls may
+ * changes (e.g. by another thread of process), so retry.
+ */
+ if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
+ {
+ LocalFree(owner);
+ goto retry;
+ }
+ LocalFree(owner);
+ CloseHandle(token);
+ return NULL;
+ }
+
+ CloseHandle(token);
+ return owner;
+}
+
+/*
+ * Create a new security descriptor in absolute form from relative form.
+ * Newly created security descriptor in absolute form is stored in linear buffer.
+ */
+static PSECURITY_DESCRIPTOR
+create_relsd_from_abssd(PSECURITY_DESCRIPTOR rel_security_descriptor)
+{
+ PBYTE abs_security_descriptor_buffer;
+ DWORD abs_security_descriptor_size=0, abs_dacl_size=0, abs_sacl_size=0, abs_owner_size=0, abs_primary_group_size=0;
+
+ if (!MakeAbsoluteSD(rel_security_descriptor,
+ NULL, &abs_security_descriptor_size,
+ NULL, &abs_dacl_size,
+ NULL, &abs_sacl_size,
+ NULL, &abs_owner_size,
+ NULL, &abs_primary_group_size) && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+ return NULL;
+
+ abs_security_descriptor_buffer = (PBYTE)LocalAlloc(LPTR, abs_security_descriptor_size+abs_dacl_size+abs_sacl_size+abs_owner_size+abs_primary_group_size);
+ if (!abs_security_descriptor_buffer)
+ return NULL;
+
+ if (!MakeAbsoluteSD(rel_security_descriptor,
+ (PSECURITY_DESCRIPTOR)abs_security_descriptor_buffer, &abs_security_descriptor_size,
+ (PACL)(abs_security_descriptor_buffer+abs_security_descriptor_size), &abs_dacl_size,
+ (PACL)(abs_security_descriptor_buffer+abs_security_descriptor_size+abs_dacl_size), &abs_sacl_size,
+ (PSID)(abs_security_descriptor_buffer+abs_security_descriptor_size+abs_dacl_size+abs_sacl_size), &abs_owner_size,
+ (PSID)(abs_security_descriptor_buffer+abs_security_descriptor_size+abs_dacl_size+abs_sacl_size+abs_owner_size), &abs_primary_group_size))
+ return NULL;
+
+ return (PSECURITY_DESCRIPTOR)abs_security_descriptor_buffer;
+}
+
+/*
+ * Prepare security descriptor obtained by GetKernelObjectSecurity() so it can be
+ * passed to SetKernelObjectSecurity() as identity operation. It modifies control
+ * flags of security descriptor, which is needed for Windows 2000 and new.
+ */
+static BOOL
+prepare_security_descriptor_for_set_operation(PSECURITY_DESCRIPTOR security_descriptor)
+{
+ SetSecurityDescriptorControlProt MySetSecurityDescriptorControl;
+ SECURITY_DESCRIPTOR_CONTROL bits_mask;
+ SECURITY_DESCRIPTOR_CONTROL bits_set;
+ SECURITY_DESCRIPTOR_CONTROL control;
+ OSVERSIONINFO version;
+ HMODULE advapi32;
+ DWORD revision;
+
+ /*
+ * SE_DACL_AUTO_INHERITED and SE_SACL_AUTO_INHERITED are flags introduced in
+ * Windows 2000 to control client-side automatic inheritance (client - user
+ * process - is responsible for propagating inherited ACEs to subobjects).
+ * To prevent applications which do not understand client-side automatic
+ * inheritance (applications created prior Windows 2000 or which use low
+ * level API like SetKernelObjectSecurity()) to unintentionally set those
+ * SE_DACL_AUTO_INHERITED and SE_SACL_AUTO_INHERITED control flags when
+ * coping them from other security descriptor.
+ *
+ * As we are not modifying existing ACEs, we are compatible with Windows 2000
+ * client-side automatic inheritance model and therefore prepare security
+ * descriptor for SetKernelObjectSecurity() to not clear existing automatic
+ * inheritance control flags.
+ *
+ * Control flags SE_DACL_AUTO_INHERITED and SE_SACL_AUTO_INHERITED are set
+ * into security object only when they are set together with set-only flags
+ * SE_DACL_AUTO_INHERIT_REQ and SE_SACL_AUTO_INHERIT_REQ. Those flags are
+ * never received by GetKernelObjectSecurity() and are just commands for
+ * SetKernelObjectSecurity() how to interpret SE_DACL_AUTO_INHERITED and
+ * SE_SACL_AUTO_INHERITED flags.
+ *
+ * Function symbol SetSecurityDescriptorControl is not available in the
+ * older versions of advapi32.dll library, so resolve it at runtime.
+ */
+
+ version.dwOSVersionInfoSize = sizeof(version);
+ if (!GetVersionEx(&version) ||
+ version.dwPlatformId != VER_PLATFORM_WIN32_NT ||
+ version.dwMajorVersion < 5)
+ return TRUE;
+
+ if (!GetSecurityDescriptorControl(security_descriptor, &control, &revision))
+ return FALSE;
+
+ bits_mask = 0;
+ bits_set = 0;
+
+ if (control & SE_DACL_AUTO_INHERITED)
+ {
+ bits_mask |= SE_DACL_AUTO_INHERIT_REQ;
+ bits_set |= SE_DACL_AUTO_INHERIT_REQ;
+ }
+
+ if (control & SE_SACL_AUTO_INHERITED)
+ {
+ bits_mask |= SE_SACL_AUTO_INHERIT_REQ;
+ bits_set |= SE_SACL_AUTO_INHERIT_REQ;
+ }
+
+ if (!bits_mask)
+ return TRUE;
+
+ advapi32 = GetModuleHandle(TEXT("advapi32.dll"));
+ if (!advapi32)
+ return FALSE;
+
+ MySetSecurityDescriptorControl = (SetSecurityDescriptorControlProt)(LPVOID)GetProcAddress(advapi32, "SetSecurityDescriptorControl");
+ if (!MySetSecurityDescriptorControl)
+ return FALSE;
+
+ if (!MySetSecurityDescriptorControl(security_descriptor, bits_mask, bits_set))
+ return FALSE;
+
+ return TRUE;
+}
+
+/*
+ * Grant particular permissions in the primary access token of the specified
+ * process for the owner of current thread token and set old DACL of the
+ * process access token for reverting permissions. Security descriptor is
+ * just memory buffer for old DACL.
+ */
+static BOOL
+grant_process_token_dacl_permissions(HANDLE process, DWORD permissions, HANDLE *token, PSECURITY_DESCRIPTOR *old_security_descriptor)
+{
+ TOKEN_OWNER *owner;
+ PACL old_dacl;
+ BOOL old_dacl_present;
+ BOOL old_dacl_defaulted;
+ PACL new_dacl;
+ WORD new_dacl_size;
+ PSECURITY_DESCRIPTOR new_security_descriptor;
+ DWORD length;
+
+ owner = get_current_token_owner();
+ if (!owner)
+ return FALSE;
+
+ /*
+ * READ_CONTROL is required for GetSecurityInfo(DACL_SECURITY_INFORMATION)
+ * and WRITE_DAC is required for SetSecurityInfo(DACL_SECURITY_INFORMATION).
+ */
+ if (!OpenProcessToken(process, READ_CONTROL | WRITE_DAC, token))
+ {
+ LocalFree(owner);
+ return FALSE;
+ }
+
+ if (!GetKernelObjectSecurity(*token, DACL_SECURITY_INFORMATION, NULL, 0, &length) && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+ {
+ LocalFree(owner);
+ CloseHandle(*token);
+ return FALSE;
+ }
+
+retry:
+ *old_security_descriptor = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, length);
+ if (!*old_security_descriptor)
+ {
+ LocalFree(owner);
+ CloseHandle(*token);
+ return FALSE;
+ }
+
+ if (!GetKernelObjectSecurity(*token, DACL_SECURITY_INFORMATION, *old_security_descriptor, length, &length))
+ {
+ /*
+ * Length of the security descriptor between two get calls
+ * may changes (e.g. by another thread of process), so retry.
+ */
+ if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
+ {
+ LocalFree(*old_security_descriptor);
+ goto retry;
+ }
+ LocalFree(*old_security_descriptor);
+ LocalFree(owner);
+ CloseHandle(*token);
+ return FALSE;
+ }
+
+ if (!prepare_security_descriptor_for_set_operation(*old_security_descriptor))
+ {
+ LocalFree(*old_security_descriptor);
+ LocalFree(owner);
+ CloseHandle(*token);
+ return FALSE;
+ }
+
+ /* Retrieve the current DACL from security descriptor including present and defaulted properties. */
+ if (!GetSecurityDescriptorDacl(*old_security_descriptor, &old_dacl_present, &old_dacl, &old_dacl_defaulted))
+ {
+ LocalFree(*old_security_descriptor);
+ LocalFree(owner);
+ CloseHandle(*token);
+ return FALSE;
+ }
+
+ /*
+ * If DACL is not present then system grants full access to everyone. It this
+ * case do not modify DACL as it just adds one ACL allow rule for us, which
+ * automatically disallow access to anybody else which had access before.
+ */
+ if (!old_dacl_present || !old_dacl)
+ {
+ LocalFree(*old_security_descriptor);
+ LocalFree(owner);
+ *old_security_descriptor = NULL;
+ return TRUE;
+ }
+
+ /* Create new DACL which would be copy of the current old one. */
+ new_dacl_size = old_dacl->AclSize + sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(owner->Owner) - sizeof(DWORD);
+ new_dacl = (PACL)LocalAlloc(LPTR, new_dacl_size);
+ if (!new_dacl)
+ {
+ LocalFree(*old_security_descriptor);
+ LocalFree(owner);
+ CloseHandle(*token);
+ return FALSE;
+ }
+
+ /*
+ * Initialize new DACL structure to the same format as was the old one.
+ * Set new explicit access for the owner of the current thread access
+ * token with non-inherited granting access to specified permissions.
+ * This permission is added in the first ACE, so has the highest priority.
+ */
+ if (!InitializeAcl(new_dacl, new_dacl_size, old_dacl->AclRevision) ||
+ !AddAccessAllowedAce(new_dacl, ACL_REVISION2, permissions, owner->Owner))
+ {
+ LocalFree(new_dacl);
+ LocalFree(*old_security_descriptor);
+ LocalFree(owner);
+ CloseHandle(*token);
+ return FALSE;
+ }
+
+ /*
+ * Now (after setting our new permissions) append all ACE entries from the
+ * old DACL to the new DACL, which preserve all other existing permissions.
+ */
+ if (old_dacl->AceCount > 0)
+ {
+ WORD ace_index;
+ LPVOID ace;
+
+ for (ace_index = 0; ace_index < old_dacl->AceCount; ace_index++)
+ {
+ if (!GetAce(old_dacl, ace_index, &ace) ||
+ !AddAce(new_dacl, old_dacl->AclRevision, MAXDWORD, ace, ((PACE_HEADER)ace)->AceSize))
+ {
+ LocalFree(new_dacl);
+ LocalFree(*old_security_descriptor);
+ LocalFree(owner);
+ CloseHandle(*token);
+ return FALSE;
+ }
+ }
+ }
+
+ /*
+ * Create copy of the old security descriptor, so we can modify its DACL.
+ * Function SetSecurityDescriptorDacl() works only with security descriptors
+ * in absolute format. So use our helper function create_relsd_from_abssd()
+ * for converting security descriptor from relative format (which is returned
+ * by GetKernelObjectSecurity() function) to the absolute format.
+ */
+ new_security_descriptor = create_relsd_from_abssd(*old_security_descriptor);
+ if (!new_security_descriptor)
+ {
+ LocalFree(new_dacl);
+ LocalFree(*old_security_descriptor);
+ LocalFree(owner);
+ CloseHandle(*token);
+ return FALSE;
+ }
+
+ /*
+ * In the new security descriptor replace old DACL by the new DACL (which has
+ * new permissions) and then set this new security descriptor to the token,
+ * so token would have new access permissions.
+ */
+ if (!SetSecurityDescriptorDacl(new_security_descriptor, TRUE, new_dacl, FALSE) ||
+ !SetKernelObjectSecurity(*token, DACL_SECURITY_INFORMATION, new_security_descriptor))
+ {
+ LocalFree(new_security_descriptor);
+ LocalFree(new_dacl);
+ LocalFree(*old_security_descriptor);
+ LocalFree(owner);
+ CloseHandle(*token);
+ return FALSE;
+ }
+
+ LocalFree(new_security_descriptor);
+ LocalFree(new_dacl);
+ LocalFree(owner);
+ return TRUE;
+}
+
+/*
+ * Revert particular granted permissions in specified access token done by
+ * grant_process_token_dacl_permissions() call.
+ */
+static VOID
+revert_token_dacl_permissions(HANDLE token, PSECURITY_DESCRIPTOR old_security_descriptor)
+{
+ SetKernelObjectSecurity(token, DACL_SECURITY_INFORMATION, old_security_descriptor);
+ LocalFree(old_security_descriptor);
+ CloseHandle(token);
+}
+
+/*
+ * Open process handle specified by the process id with the query right and
+ * optionally also with vm read right.
+ */
+static HANDLE
+open_process_for_query(DWORD pid, BOOL with_vm_read)
+{
+ BOOL revert_only_privilege;
+ LUID luid_debug_privilege;
+ OSVERSIONINFO version;
+ DWORD process_right;
+ HANDLE revert_token;
+ HANDLE process;
+
+ /*
+ * Some processes on Windows Vista and higher systems can be opened only
+ * with PROCESS_QUERY_LIMITED_INFORMATION right. This right is enough
+ * for accessing primary process token. But this right is not supported
+ * on older pre-Vista systems. When the current thread on these older
+ * systems does not have Debug privilege then OpenProcess() fails with
+ * ERROR_ACCESS_DENIED. If the current thread has Debug privilege then
+ * OpenProcess() success and returns handle to requested process.
+ * Problem is that this handle does not have PROCESS_QUERY_INFORMATION
+ * right and so cannot be used for accessing primary process token
+ * on those older systems. Moreover it has zero rights and therefore
+ * such handle is fully useless. So never try to use open process with
+ * PROCESS_QUERY_LIMITED_INFORMATION right on older systems than
+ * Windows Vista (NT 6.0).
+ */
+ version.dwOSVersionInfoSize = sizeof(version);
+ if (GetVersionEx(&version) &&
+ version.dwPlatformId == VER_PLATFORM_WIN32_NT &&
+ version.dwMajorVersion >= 6)
+ process_right = PROCESS_QUERY_LIMITED_INFORMATION;
+ else
+ process_right = PROCESS_QUERY_INFORMATION;
+
+ if (with_vm_read)
+ process_right |= PROCESS_VM_READ;
+
+ process = OpenProcess(process_right, FALSE, pid);
+ if (process)
+ return process;
+
+ /*
+ * It is possible to open only processes to which owner of the current
+ * thread access token has permissions. For opening other processing it
+ * is required to have Debug privilege enabled. By default local
+ * administrators have this privilege, but it is disabled. So try to
+ * enable it and then try to open process again.
+ */
+
+ if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid_debug_privilege))
+ return NULL;
+
+ if (!win32_enable_privilege(luid_debug_privilege, &revert_token, &revert_only_privilege))
+ return NULL;
+
+ process = OpenProcess(process_right, FALSE, pid);
+
+ win32_revert_privilege(luid_debug_privilege, revert_token, revert_only_privilege);
+
+ return process;
+}
+
+/*
+ * Check if process image path name (wide string) matches exe file name
+ * (7-bit ASCII string). Do case-insensitive string comparison. Process
+ * image path name can be in any namespace format (DOS, Win32, UNC, ...).
+ */
+static BOOL
+check_process_name(LPCWSTR path, DWORD path_length, LPCSTR exe_file)
+{
+ DWORD exe_file_length;
+ WCHAR c1;
+ UCHAR c2;
+ DWORD i;
+
+ exe_file_length = 0;
+ while (exe_file[exe_file_length] != '\0')
+ exe_file_length++;
+
+ /* Path must have backslash before exe file name. */
+ if (exe_file_length >= path_length ||
+ path[path_length-exe_file_length-1] != L'\\')
+ return FALSE;
+
+ for (i = 0; i < exe_file_length; i++)
+ {
+ c1 = path[path_length-exe_file_length+i];
+ c2 = exe_file[i];
+ /*
+ * Input string for comparison is 7-bit ASCII and file name part
+ * of path must not contain backslash as it is path separator.
+ */
+ if (c1 >= 0x80 || c2 >= 0x80 || c1 == L'\\')
+ return FALSE;
+ if (c1 >= L'a' && c1 <= L'z')
+ c1 -= L'a' - L'A';
+ if (c2 >= 'a' && c2 <= 'z')
+ c2 -= 'a' - 'A';
+ if (c1 != c2)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* Open process handle with the query right specified by process exe file. */
+HANDLE
+win32_find_and_open_process_for_query(LPCSTR exe_file)
+{
+ GetProcessImageFileNameWProt MyGetProcessImageFileNameW;
+ GetModuleFileNameExWProt MyGetModuleFileNameExW;
+ EnumProcessesProt MyEnumProcesses;
+ HMODULE kernel32, psapi;
+ UINT prev_error_mode;
+ DWORD partial_retry;
+ BOOL found_process;
+ DWORD size, length;
+ DWORD *processes;
+ HANDLE process;
+ LPWSTR path;
+ DWORD error;
+ DWORD count;
+ DWORD i;
+
+ psapi = NULL;
+ kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
+ if (!kernel32)
+ return NULL;
+
+ /*
+ * On Windows 7 and higher systems these functions are available in
+ * kernel32.dll library with K32 prefix.
+ */
+ MyGetModuleFileNameExW = NULL;
+ MyGetProcessImageFileNameW = (GetProcessImageFileNameWProt)(LPVOID)GetProcAddress(kernel32, "K32GetProcessImageFileNameW");
+ MyEnumProcesses = (EnumProcessesProt)(LPVOID)GetProcAddress(kernel32, "K32EnumProcesses");
+ if (!MyGetProcessImageFileNameW || !MyEnumProcesses)
+ {
+ /*
+ * On older NT-based systems these functions are available in
+ * psapi.dll library without K32 prefix.
+ */
+ prev_error_mode = win32_change_error_mode(SEM_FAILCRITICALERRORS);
+ psapi = LoadLibrary(TEXT("psapi.dll"));
+ win32_change_error_mode(prev_error_mode);
+
+ if (!psapi)
+ return NULL;
+
+ /*
+ * Function GetProcessImageFileNameW() is available in
+ * Windows XP and higher systems. On older versions is
+ * available function GetModuleFileNameExW().
+ */
+ MyGetProcessImageFileNameW = (GetProcessImageFileNameWProt)(LPVOID)GetProcAddress(psapi, "GetProcessImageFileNameW");
+ MyGetModuleFileNameExW = (GetModuleFileNameExWProt)(LPVOID)GetProcAddress(psapi, "GetModuleFileNameExW");
+ MyEnumProcesses = (EnumProcessesProt)(LPVOID)GetProcAddress(psapi, "EnumProcesses");
+ if ((!MyGetProcessImageFileNameW && !MyGetModuleFileNameExW) || !MyEnumProcesses)
+ {
+ FreeLibrary(psapi);
+ return NULL;
+ }
+ }
+
+ /* Make initial buffer size for 1024 processes. */
+ size = 1024 * sizeof(*processes);
+
+retry:
+ processes = (DWORD *)LocalAlloc(LPTR, size);
+ if (!processes)
+ {
+ if (psapi)
+ FreeLibrary(psapi);
+ return NULL;
+ }
+
+ if (!MyEnumProcesses(processes, size, &length))
+ {
+ LocalFree(processes);
+ if (psapi)
+ FreeLibrary(psapi);
+ return NULL;
+ }
+ else if (size == length)
+ {
+ /*
+ * There is no indication given when the buffer is too small to
+ * store all process identifiers. Therefore if returned length
+ * is same as buffer size there can be more processes. Call
+ * again with larger buffer.
+ */
+ LocalFree(processes);
+ size *= 2;
+ goto retry;
+ }
+
+ process = NULL;
+ count = length / sizeof(*processes);
+
+ for (i = 0; i < count; i++)
+ {
+ /* Skip System Idle Process. */
+ if (processes[i] == 0)
+ continue;
+
+ /*
+ * Function GetModuleFileNameExW() requires additional
+ * PROCESS_VM_READ right as opposite to function
+ * GetProcessImageFileNameW() which does not need it.
+ */
+ process = open_process_for_query(processes[i], MyGetProcessImageFileNameW ? FALSE : TRUE);
+ if (!process)
+ continue;
+
+ /*
+ * Set initial buffer size to 256 (wide) characters.
+ * Final path length on the modern NT-based systems can be also larger.
+ */
+ size = 256;
+ found_process = FALSE;
+ partial_retry = 0;
+
+retry_path:
+ path = (LPWSTR)LocalAlloc(LPTR, size * sizeof(*path));
+ if (!path)
+ goto end_path;
+
+ if (MyGetProcessImageFileNameW)
+ length = MyGetProcessImageFileNameW(process, path, size);
+ else
+ length = MyGetModuleFileNameExW(process, NULL, path, size);
+
+ error = GetLastError();
+
+ /*
+ * GetModuleFileNameEx() returns zero and signal error ERROR_PARTIAL_COPY
+ * when remote process is in the middle of updating its module table.
+ * Sleep 10 ms and try again, max 10 attempts.
+ */
+ if (!MyGetProcessImageFileNameW)
+ {
+ if (length == 0 && error == ERROR_PARTIAL_COPY && partial_retry++ < 10)
+ {
+ Sleep(10);
+ goto retry_path;
+ }
+ partial_retry = 0;
+ }
+
+ /*
+ * When buffer is too small then function GetModuleFileNameEx() returns
+ * its size argument on older systems (Windows XP) or its size minus
+ * argument one on new systems (Windows 10) without signalling any error.
+ * Function GetProcessImageFileNameW() on the other hand returns zero
+ * value and signals error ERROR_INSUFFICIENT_BUFFER. So in all these
+ * cases call function again with larger buffer.
+ */
+
+ if (MyGetProcessImageFileNameW && length == 0 && error != ERROR_INSUFFICIENT_BUFFER)
+ goto end_path;
+
+ if ((MyGetProcessImageFileNameW && length == 0) ||
+ (!MyGetProcessImageFileNameW && (length == size || length == size-1)))
+ {
+ LocalFree(path);
+ size *= 2;
+ goto retry_path;
+ }
+
+ if (length && check_process_name(path, length, exe_file))
+ found_process = TRUE;
+
+end_path:
+ if (path)
+ {
+ LocalFree(path);
+ path = NULL;
+ }
+
+ if (found_process)
+ break;
+
+ CloseHandle(process);
+ process = NULL;
+ }
+
+ LocalFree(processes);
+
+ if (psapi)
+ FreeLibrary(psapi);
+
+ return process;
+}
+
+/*
+ * Try to open primary access token of the particular process with specified
+ * rights. Before opening access token try to adjust DACL permissions of the
+ * primary process access token, so following open does not fail on error
+ * related to no open permissions. Revert DACL permissions after open attempt.
+ * As following steps are not atomic, try to execute them more times in case
+ * of possible race conditions caused by other threads or processes.
+ */
+static HANDLE
+try_grant_permissions_and_open_process_token(HANDLE process, DWORD rights)
+{
+ PSECURITY_DESCRIPTOR old_security_descriptor;
+ HANDLE grant_token;
+ HANDLE token;
+ DWORD retry;
+ DWORD error;
+
+ /*
+ * This code is not atomic. Between grant and open calls can other
+ * thread or process change or revert permissions. So try to execute
+ * it more times.
+ */
+ for (retry = 0; retry < 10; retry++)
+ {
+ if (!grant_process_token_dacl_permissions(process, rights, &grant_token, &old_security_descriptor))
+ return NULL;
+ if (!OpenProcessToken(process, rights, &token))
+ {
+ token = NULL;
+ error = GetLastError();
+ }
+ if (old_security_descriptor)
+ revert_token_dacl_permissions(grant_token, old_security_descriptor);
+ if (token)
+ return token;
+ else if (error != ERROR_ACCESS_DENIED)
+ return NULL;
+ }
+
+ return NULL;
+}
+
+/*
+ * Open primary access token of particular process handle with specified rights.
+ * If permissions for specified rights are missing then try to grant them.
+ */
+HANDLE
+win32_open_process_token_with_rights(HANDLE process, DWORD rights)
+{
+ HANDLE old_token;
+ HANDLE token;
+
+ /* First try to open primary access token of process handle directly. */
+ if (OpenProcessToken(process, rights, &token))
+ return token;
+
+ /*
+ * If opening failed then it means that owner of the current thread
+ * access token does not have permission for it. Try it again with
+ * primary process access token.
+ */
+ if (change_token_to_primary(&old_token))
+ {
+ if (!OpenProcessToken(process, rights, &token))
+ token = NULL;
+ win32_revert_to_token(old_token);
+ if (token)
+ return token;
+ }
+
+ /*
+ * If opening is still failing then try to grant specified permissions
+ * for the current thread and try to open it again.
+ */
+ token = try_grant_permissions_and_open_process_token(process, rights);
+ if (token)
+ return token;
+
+ /*
+ * And if it is still failing then try it again with granting
+ * permissions for the primary process token of the current process.
+ */
+ if (change_token_to_primary(&old_token))
+ {
+ token = try_grant_permissions_and_open_process_token(process, rights);
+ win32_revert_to_token(old_token);
+ if (token)
+ return token;
+ }
+
+ /*
+ * TODO: Sorry, no other option for now...
+ * It could be possible to use Take Ownership Name privilege to
+ * temporary change token owner of specified process to the owner of
+ * the current thread token, grant permissions for current thread in
+ * that process token, change ownership back to original one, open
+ * that process token and revert granted permissions. But this is
+ * not implemented yet.
+ */
+ return NULL;
+}
+
+/*
+ * Call supplied function with its argument and if it fails with
+ * ERROR_PRIVILEGE_NOT_HELD then try to enable Tcb privilege and
+ * call function with its argument again.
+ */
+BOOL
+win32_call_func_with_tcb_privilege(BOOL (*function)(LPVOID), LPVOID argument)
+{
+ LUID luid_tcb_privilege;
+ LUID luid_impersonate_privilege;
+
+ HANDLE revert_token_tcb_privilege;
+ BOOL revert_only_tcb_privilege;
+
+ HANDLE revert_token_impersonate_privilege;
+ BOOL revert_only_impersonate_privilege;
+
+ BOOL impersonate_privilege_enabled;
+
+ BOOL revert_to_old_token;
+ HANDLE old_token;
+
+ HANDLE lsass_process;
+ HANDLE lsass_token;
+
+ BOOL ret;
+
+ impersonate_privilege_enabled = FALSE;
+ revert_to_old_token = FALSE;
+ lsass_token = NULL;
+ old_token = NULL;
+
+ /* Call supplied function. */
+ ret = function(argument);
+ if (ret || GetLastError() != ERROR_PRIVILEGE_NOT_HELD)
+ goto ret;
+
+ /*
+ * If function call failed with ERROR_PRIVILEGE_NOT_HELD
+ * error then it means that the current thread token does not have
+ * Tcb privilege enabled. Try to enable it.
+ */
+
+ if (!LookupPrivilegeValue(NULL, SE_TCB_NAME, &luid_tcb_privilege))
+ goto err_privilege_not_held;
+
+ /*
+ * If the current thread has already Tcb privilege enabled then there
+ * is some additional unhanded restriction.
+ */
+ if (win32_have_privilege(luid_tcb_privilege))
+ goto err_privilege_not_held;
+
+ /* Try to enable Tcb privilege and try function call again. */
+ if (win32_enable_privilege(luid_tcb_privilege, &revert_token_tcb_privilege, &revert_only_tcb_privilege))
+ {
+ ret = function(argument);
+ win32_revert_privilege(luid_tcb_privilege, revert_token_tcb_privilege, revert_only_tcb_privilege);
+ goto ret;
+ }
+
+ /*
+ * If enabling of Tcb privilege failed then it means that current thread
+ * does not have this privilege. But current process may have it. So try it
+ * again with primary process access token.
+ */
+
+ /*
+ * If system supports Impersonate privilege (Windows 2000 SP4 or higher) then
+ * all future actions in this function require this Impersonate privilege.
+ * So try to enable it in case it is currently disabled.
+ */
+ if (LookupPrivilegeValue(NULL, SE_IMPERSONATE_NAME, &luid_impersonate_privilege) &&
+ !win32_have_privilege(luid_impersonate_privilege))
+ {
+ /*
+ * If current thread does not have Impersonate privilege enabled
+ * then first try to enable it just for the current thread. If
+ * it is not possible to enable it just for the current thread
+ * then try it to enable globally for whole process (which
+ * affects all process threads). Both actions will be reverted
+ * at the end of this function.
+ */
+ if (win32_enable_privilege(luid_impersonate_privilege, &revert_token_impersonate_privilege, &revert_only_impersonate_privilege))
+ {
+ impersonate_privilege_enabled = TRUE;
+ }
+ else if (win32_enable_privilege(luid_impersonate_privilege, NULL, NULL))
+ {
+ impersonate_privilege_enabled = TRUE;
+ revert_token_impersonate_privilege = NULL;
+ revert_only_impersonate_privilege = TRUE;
+ }
+ else
+ {
+ goto err_privilege_not_held;
+ }
+
+ /*
+ * Now when Impersonate privilege is enabled, try to enable Tcb
+ * privilege again. Enabling other privileges for the current
+ * thread requires Impersonate privilege, so enabling Tcb again
+ * could now pass.
+ */
+ if (win32_enable_privilege(luid_tcb_privilege, &revert_token_tcb_privilege, &revert_only_tcb_privilege))
+ {
+ ret = function(argument);
+ win32_revert_privilege(luid_tcb_privilege, revert_token_tcb_privilege, revert_only_tcb_privilege);
+ goto ret;
+ }
+ }
+
+ /*
+ * If enabling Tcb privilege failed then it means that the current
+ * thread access token does not have this privilege or does not
+ * have permission to adjust privileges.
+ *
+ * Try to use more privileged token from Local Security Authority
+ * Subsystem Service process (lsass.exe) which has Tcb privilege.
+ * Retrieving this more privileged token is possible for local
+ * administrators (unless it was disabled by local administrators).
+ */
+
+ lsass_process = win32_find_and_open_process_for_query("lsass.exe");
+ if (!lsass_process)
+ goto err_privilege_not_held;
+
+ /*
+ * Open primary lsass.exe process access token with query and duplicate
+ * rights. Just these two rights are required for impersonating other
+ * primary process token (impersonate right is really not required!).
+ */
+ lsass_token = win32_open_process_token_with_rights(lsass_process, TOKEN_QUERY | TOKEN_DUPLICATE);
+
+ CloseHandle(lsass_process);
+
+ if (!lsass_token)
+ goto err_privilege_not_held;
+
+ /*
+ * After successful open of the primary lsass.exe process access token,
+ * assign its copy for the current thread.
+ */
+ if (!win32_change_token(lsass_token, &old_token))
+ goto err_privilege_not_held;
+
+ revert_to_old_token = TRUE;
+
+ ret = function(argument);
+ if (ret || GetLastError() != ERROR_PRIVILEGE_NOT_HELD)
+ goto ret;
+
+ /*
+ * Now current thread is not using primary process token anymore
+ * but is using custom access token. There is no need to revert
+ * enabled Tcb privilege as the whole custom access token would
+ * be reverted. So there is no need to setup revert method for
+ * enabling privilege.
+ */
+ if (win32_have_privilege(luid_tcb_privilege) ||
+ !win32_enable_privilege(luid_tcb_privilege, NULL, NULL))
+ goto err_privilege_not_held;
+
+ ret = function(argument);
+ goto ret;
+
+err_privilege_not_held:
+ SetLastError(ERROR_PRIVILEGE_NOT_HELD);
+ ret = FALSE;
+ goto ret;
+
+ret:
+ if (revert_to_old_token)
+ win32_revert_to_token(old_token);
+
+ if (impersonate_privilege_enabled)
+ win32_revert_privilege(luid_impersonate_privilege, revert_token_impersonate_privilege, revert_only_impersonate_privilege);
+
+ if (lsass_token)
+ CloseHandle(lsass_token);
+
+ return ret;
+}
diff --git a/lib/win32-helpers.h b/lib/win32-helpers.h
new file mode 100644
index 0000000..c415439
--- /dev/null
+++ b/lib/win32-helpers.h
@@ -0,0 +1,13 @@
+const char *win32_strerror(DWORD win32_error_id);
+BOOL win32_is_non_nt_system(void);
+BOOL win32_is_32bit_on_64bit_system(void);
+BOOL win32_is_32bit_on_win8_64bit_system(void);
+UINT win32_change_error_mode(UINT new_mode);
+BOOL win32_have_privilege(LUID luid_privilege);
+BOOL win32_enable_privilege(LUID luid_privilege, HANDLE *revert_token, BOOL *revert_only_privilege);
+VOID win32_revert_privilege(LUID luid_privilege, HANDLE revert_token, BOOL revert_only_privilege);
+BOOL win32_change_token(HANDLE new_token, HANDLE *old_token);
+VOID win32_revert_to_token(HANDLE token);
+HANDLE win32_find_and_open_process_for_query(LPCSTR exe_file);
+HANDLE win32_open_process_token_with_rights(HANDLE process, DWORD rights);
+BOOL win32_call_func_with_tcb_privilege(BOOL (*function)(LPVOID), LPVOID argument);
diff --git a/lib/win32-kldbg.c b/lib/win32-kldbg.c
new file mode 100644
index 0000000..33f5751
--- /dev/null
+++ b/lib/win32-kldbg.c
@@ -0,0 +1,731 @@
+/*
+ * The PCI Library -- PCI config space access using Kernel Local Debugging Driver
+ *
+ * Copyright (c) 2022 Pali Rohár <pali@kernel.org>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <windows.h>
+#include <winioctl.h>
+
+#include <stdio.h> /* for sprintf() */
+#include <string.h> /* for memset() and memcpy() */
+
+#include "internal.h"
+#include "win32-helpers.h"
+
+#ifndef ERROR_NOT_FOUND
+#define ERROR_NOT_FOUND 1168
+#endif
+
+#ifndef LOAD_LIBRARY_AS_IMAGE_RESOURCE
+#define LOAD_LIBRARY_AS_IMAGE_RESOURCE 0x20
+#endif
+#ifndef LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE
+#define LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE 0x40
+#endif
+
+#ifndef IOCTL_KLDBG
+#define IOCTL_KLDBG CTL_CODE(FILE_DEVICE_UNKNOWN, 0x1, METHOD_NEITHER, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+#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_SEGMENT_BUS_NUMBER
+typedef struct _PCI_SEGMENT_BUS_NUMBER {
+ union {
+ struct {
+ ULONG BusNumber:8;
+ ULONG SegmentNumber:16;
+ ULONG Reserved:8;
+ } bits;
+ ULONG AsULONG;
+ } u;
+} PCI_SEGMENT_BUS_NUMBER, *PPCI_SEGMENT_BUS_NUMBER;
+#define PCI_SEGMENT_BUS_NUMBER PCI_SEGMENT_BUS_NUMBER
+#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
+
+#ifndef KLDBG
+typedef struct _KLDBG {
+ SYSDBG_COMMAND Command;
+ PVOID Buffer;
+ DWORD BufferLength;
+} KLDBG, *PKLDBG;
+#define KLDBG KLDBG
+#endif
+
+static BOOL debug_privilege_enabled;
+static LUID luid_debug_privilege;
+static BOOL revert_only_privilege;
+static HANDLE revert_token;
+
+static HANDLE kldbg_dev = INVALID_HANDLE_VALUE;
+
+static BOOL
+win32_kldbg_pci_bus_data(BOOL WriteBusData, USHORT SegmentNumber, BYTE BusNumber, BYTE DeviceNumber, BYTE FunctionNumber, USHORT Address, PVOID Buffer, ULONG BufferSize, LPDWORD Length);
+
+static WORD
+win32_get_current_process_machine(void)
+{
+ IMAGE_DOS_HEADER *dos_header;
+ IMAGE_NT_HEADERS *nt_header;
+
+ dos_header = (IMAGE_DOS_HEADER *)GetModuleHandle(NULL);
+ if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
+ return IMAGE_FILE_MACHINE_UNKNOWN;
+
+ nt_header = (IMAGE_NT_HEADERS *)((BYTE *)dos_header + dos_header->e_lfanew);
+ if (nt_header->Signature != IMAGE_NT_SIGNATURE)
+ return IMAGE_FILE_MACHINE_UNKNOWN;
+
+ return nt_header->FileHeader.Machine;
+}
+
+static BOOL
+win32_check_driver(BYTE *driver_data)
+{
+ IMAGE_DOS_HEADER *dos_header;
+ IMAGE_NT_HEADERS *nt_headers;
+ WORD current_machine;
+
+ current_machine = win32_get_current_process_machine();
+ if (current_machine == IMAGE_FILE_MACHINE_UNKNOWN)
+ return FALSE;
+
+ dos_header = (IMAGE_DOS_HEADER *)driver_data;
+ if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
+ return FALSE;
+
+ nt_headers = (IMAGE_NT_HEADERS *)((BYTE *)dos_header + dos_header->e_lfanew);
+ if (nt_headers->Signature != IMAGE_NT_SIGNATURE)
+ return FALSE;
+
+ if (nt_headers->FileHeader.Machine != current_machine)
+ return FALSE;
+
+ if (!(nt_headers->FileHeader.Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE))
+ return FALSE;
+
+#ifndef _WIN64
+ if (!(nt_headers->FileHeader.Characteristics & IMAGE_FILE_32BIT_MACHINE))
+ return FALSE;
+#endif
+
+ /* IMAGE_NT_OPTIONAL_HDR_MAGIC is alias for the header magic used on the target compiler architecture. */
+ if (nt_headers->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)
+ return FALSE;
+
+ if (nt_headers->OptionalHeader.Subsystem != IMAGE_SUBSYSTEM_NATIVE)
+ return FALSE;
+
+ return TRUE;
+}
+
+static int
+win32_kldbg_unpack_driver(struct pci_access *a, LPTSTR driver_path)
+{
+ BOOL use_kd_exe = FALSE;
+ HMODULE exe_with_driver = NULL;
+ HRSRC driver_resource_info = NULL;
+ HGLOBAL driver_resource = NULL;
+ BYTE *driver_data = NULL;
+ DWORD driver_size = 0;
+ HANDLE driver_handle = INVALID_HANDLE_VALUE;
+ DWORD written = 0;
+ DWORD error = 0;
+ int ret = 0;
+
+ /* Try to find and open windbg.exe or kd.exe file in PATH. */
+ exe_with_driver = LoadLibraryEx(TEXT("windbg.exe"), NULL, LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE | LOAD_LIBRARY_AS_IMAGE_RESOURCE);
+ if (!exe_with_driver)
+ {
+ use_kd_exe = TRUE;
+ exe_with_driver = LoadLibraryEx(TEXT("kd.exe"), NULL, LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE | LOAD_LIBRARY_AS_IMAGE_RESOURCE);
+ }
+ if (!exe_with_driver)
+ {
+ error = GetLastError();
+ if (error == ERROR_FILE_NOT_FOUND ||
+ error == ERROR_MOD_NOT_FOUND)
+ a->debug("Cannot find windbg.exe or kd.exe file in PATH");
+ else
+ a->debug("Cannot load %s file: %s.", use_kd_exe ? "kd.exe" : "windbg.exe", win32_strerror(error));
+ goto out;
+ }
+
+ /* kldbgdrv.sys is embedded in windbg.exe/kd.exe as a resource with name id 0x7777 and type id 0x4444. */
+ driver_resource_info = FindResource(exe_with_driver, MAKEINTRESOURCE(0x7777), MAKEINTRESOURCE(0x4444));
+ if (!driver_resource_info)
+ {
+ a->debug("Cannot find kldbgdrv.sys resource in %s file: %s.", use_kd_exe ? "kd.exe" : "windbg.exe", win32_strerror(GetLastError()));
+ goto out;
+ }
+
+ driver_resource = LoadResource(exe_with_driver, driver_resource_info);
+ if (!driver_resource)
+ {
+ a->debug("Cannot load kldbgdrv.sys resource from %s file: %s.", use_kd_exe ? "kd.exe" : "windbg.exe", win32_strerror(GetLastError()));
+ goto out;
+ }
+
+ driver_size = SizeofResource(exe_with_driver, driver_resource_info);
+ if (!driver_size)
+ {
+ a->debug("Cannot determinate size of kldbgdrv.sys resource from %s file: %s.", use_kd_exe ? "kd.exe" : "windbg.exe", win32_strerror(GetLastError()));
+ goto out;
+ }
+
+ driver_data = LockResource(driver_resource);
+ if (!driver_data)
+ {
+ a->debug("Cannot load kldbgdrv.sys resouce data from %s file: %s.", use_kd_exe ? "kd.exe" : "windbg.exe", win32_strerror(GetLastError()));
+ goto out;
+ }
+
+ if (!win32_check_driver(driver_data))
+ {
+ a->debug("Cannot use kldbgdrv.sys driver from %s file: Driver is from different architecture.", use_kd_exe ? "kd.exe" : "windbg.exe");
+ goto out;
+ }
+
+ driver_handle = CreateFile(driver_path, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (driver_handle == INVALID_HANDLE_VALUE)
+ {
+ error = GetLastError();
+ if (error != ERROR_FILE_EXISTS)
+ {
+ a->debug("Cannot create kldbgdrv.sys driver file in system32 directory: %s.", win32_strerror(error));
+ goto out;
+ }
+ /* If driver file in system32 directory already exists then treat it as successfull unpack. */
+ ret = 1;
+ goto out;
+ }
+
+ if (!WriteFile(driver_handle, driver_data, driver_size, &written, NULL) ||
+ written != driver_size)
+ {
+ a->debug("Cannot store kldbgdrv.sys driver file to system32 directory: %s.", win32_strerror(GetLastError()));
+ /* On error, delete file from system32 directory to allow another unpack attempt. */
+ CloseHandle(driver_handle);
+ driver_handle = INVALID_HANDLE_VALUE;
+ DeleteFile(driver_path);
+ goto out;
+ }
+
+ a->debug("Driver kldbgdrv.sys was successfully unpacked from %s and stored in system32 directory...", use_kd_exe ? "kd.exe" : "windbg.exe");
+ ret = 1;
+
+out:
+ if (driver_handle != INVALID_HANDLE_VALUE)
+ CloseHandle(driver_handle);
+
+ if (driver_resource)
+ FreeResource(driver_resource);
+
+ if (exe_with_driver)
+ FreeLibrary(exe_with_driver);
+
+ return ret;
+}
+
+static int
+win32_kldbg_register_driver(struct pci_access *a, SC_HANDLE manager, SC_HANDLE *service)
+{
+ UINT system32_len;
+ LPTSTR driver_path;
+ HANDLE driver_handle;
+
+ /*
+ * COM library dbgeng.dll unpacks kldbg driver to file "\\system32\\kldbgdrv.sys"
+ * and register this driver with service name kldbgdrv. Implement same behavior.
+ * GetSystemDirectory() returns path to "\\system32" directory on all Windows versions.
+ */
+
+ system32_len = GetSystemDirectory(NULL, 0); /* Returns number of TCHARs plus 1 for nul-term. */
+ if (!system32_len)
+ system32_len = sizeof("C:\\Windows\\System32");
+
+ driver_path = pci_malloc(a, (system32_len + sizeof("\\kldbgdrv.sys")-1) * sizeof(TCHAR));
+
+ system32_len = GetSystemDirectory(driver_path, system32_len); /* Now it returns number of TCHARs without nul-term. */
+ if (!system32_len)
+ {
+ system32_len = sizeof("C:\\Windows\\System32")-1;
+ memcpy(driver_path, TEXT("C:\\Windows\\System32"), system32_len);
+ }
+
+ /* GetSystemDirectory returns path without backslash unless the system directory is the root directory. */
+ if (driver_path[system32_len-1] != '\\')
+ driver_path[system32_len++] = '\\';
+
+ memcpy(driver_path + system32_len, TEXT("kldbgdrv.sys"), sizeof(TEXT("kldbgdrv.sys")));
+
+ driver_handle = CreateFile(driver_path, 0, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (driver_handle != INVALID_HANDLE_VALUE)
+ CloseHandle(driver_handle);
+ else if (GetLastError() == ERROR_FILE_NOT_FOUND)
+ {
+ a->debug("Driver kldbgdrv.sys is missing, trying to unpack it from windbg.exe or kd.exe...");
+ if (!win32_kldbg_unpack_driver(a, driver_path))
+ {
+ pci_mfree(driver_path);
+ return 0;
+ }
+ }
+
+ *service = CreateService(manager, TEXT("kldbgdrv"), TEXT("kldbgdrv"), SERVICE_START, SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, driver_path, NULL, NULL, NULL, NULL, NULL);
+ if (!*service)
+ {
+ if (GetLastError() != ERROR_SERVICE_EXISTS)
+ {
+ a->debug("Cannot create kldbgdrv service: %s.", win32_strerror(GetLastError()));
+ pci_mfree(driver_path);
+ return 0;
+ }
+
+ *service = OpenService(manager, TEXT("kldbgdrv"), SERVICE_START);
+ if (!*service)
+ {
+ a->debug("Cannot open kldbgdrv service: %s.", win32_strerror(GetLastError()));
+ pci_mfree(driver_path);
+ return 0;
+ }
+ }
+
+ a->debug("Service kldbgdrv was successfully registered...");
+ pci_mfree(driver_path);
+ return 1;
+}
+
+static int
+win32_kldbg_start_driver(struct pci_access *a)
+{
+ SC_HANDLE manager = NULL;
+ SC_HANDLE service = NULL;
+ DWORD error = 0;
+ int ret = 0;
+
+ manager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE);
+ if (!manager)
+ manager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
+ if (!manager)
+ {
+ a->debug("Cannot open Service Manager: %s.", win32_strerror(GetLastError()));
+ return 0;
+ }
+
+ service = OpenService(manager, TEXT("kldbgdrv"), SERVICE_START);
+ if (!service)
+ {
+ error = GetLastError();
+ if (error != ERROR_SERVICE_DOES_NOT_EXIST)
+ {
+ a->debug("Cannot open kldbgdrv service: %s.", win32_strerror(error));
+ goto out;
+ }
+
+ a->debug("Kernel Local Debugging Driver (kldbgdrv.sys) is not registered, trying to register it...");
+
+ if (win32_is_32bit_on_64bit_system())
+ {
+ /* TODO */
+ a->debug("Registering driver from 32-bit process on 64-bit system is not implemented yet.");
+ goto out;
+ }
+
+ if (!win32_kldbg_register_driver(a, manager, &service))
+ goto out;
+ }
+
+ if (!StartService(service, 0, NULL))
+ {
+ error = GetLastError();
+ if (error != ERROR_SERVICE_ALREADY_RUNNING)
+ {
+ a->debug("Cannot start kldbgdrv service: %s.", win32_strerror(error));
+ goto out;
+ }
+ }
+
+ a->debug("Service kldbgdrv successfully started...");
+ ret = 1;
+
+out:
+ if (service)
+ CloseServiceHandle(service);
+
+ if (manager)
+ CloseServiceHandle(manager);
+
+ return ret;
+}
+
+static int
+win32_kldbg_setup(struct pci_access *a)
+{
+ OSVERSIONINFO version;
+ DWORD ret_len;
+ DWORD error;
+ DWORD id;
+
+ if (kldbg_dev != INVALID_HANDLE_VALUE)
+ return 1;
+
+ /* Check for Windows Vista (NT 6.0). */
+ version.dwOSVersionInfoSize = sizeof(version);
+ if (!GetVersionEx(&version) ||
+ version.dwPlatformId != VER_PLATFORM_WIN32_NT ||
+ version.dwMajorVersion < 6)
+ {
+ a->debug("Accessing PCI config space via Kernel Local Debugging Driver requires Windows Vista or higher version.");
+ return 0;
+ }
+
+ kldbg_dev = CreateFile(TEXT("\\\\.\\kldbgdrv"), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (kldbg_dev == INVALID_HANDLE_VALUE)
+ {
+ error = GetLastError();
+ if (error != ERROR_FILE_NOT_FOUND)
+ {
+ a->debug("Cannot open \"\\\\.\\kldbgdrv\" device: %s.", win32_strerror(error));
+ return 0;
+ }
+
+ a->debug("Kernel Local Debugging Driver (kldbgdrv.sys) is not running, trying to start it...");
+
+ if (!win32_kldbg_start_driver(a))
+ return 0;
+
+ kldbg_dev = CreateFile(TEXT("\\\\.\\kldbgdrv"), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (kldbg_dev == INVALID_HANDLE_VALUE)
+ {
+ error = GetLastError();
+ a->debug("Cannot open \"\\\\.\\kldbgdrv\" device: %s.", win32_strerror(error));
+ return 0;
+ }
+ }
+
+ /*
+ * Try to read PCI id register from PCI device 0000:00:00.0.
+ * If this device does not exist and kldbg API is working then
+ * kldbg returns success with read value 0xffffffff.
+ */
+ if (win32_kldbg_pci_bus_data(FALSE, 0, 0, 0, 0, 0, &id, sizeof(id), &ret_len) && ret_len == sizeof(id))
+ return 1;
+
+ error = GetLastError();
+
+ a->debug("Cannot read PCI config space via Kernel Local Debugging Driver: %s.", win32_strerror(error));
+
+ if (error != ERROR_ACCESS_DENIED)
+ {
+ CloseHandle(kldbg_dev);
+ kldbg_dev = INVALID_HANDLE_VALUE;
+ return 0;
+ }
+
+ a->debug("..Trying again with Debug privilege...");
+
+ if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid_debug_privilege))
+ {
+ a->debug("Debug privilege is not supported.");
+ CloseHandle(kldbg_dev);
+ kldbg_dev = INVALID_HANDLE_VALUE;
+ return 0;
+ }
+
+ if (!win32_enable_privilege(luid_debug_privilege, &revert_token, &revert_only_privilege))
+ {
+ a->debug("Process does not have right to enable Debug privilege.");
+ CloseHandle(kldbg_dev);
+ kldbg_dev = INVALID_HANDLE_VALUE;
+ return 0;
+ }
+
+ if (win32_kldbg_pci_bus_data(FALSE, 0, 0, 0, 0, 0, &id, sizeof(id), &ret_len) && ret_len == sizeof(id))
+ {
+ a->debug("Succeeded.");
+ debug_privilege_enabled = TRUE;
+ return 1;
+ }
+
+ error = GetLastError();
+
+ a->debug("Cannot read PCI config space via Kernel Local Debugging Driver: %s.", win32_strerror(error));
+
+ CloseHandle(kldbg_dev);
+ kldbg_dev = INVALID_HANDLE_VALUE;
+
+ win32_revert_privilege(luid_debug_privilege, revert_token, revert_only_privilege);
+ revert_token = NULL;
+ revert_only_privilege = FALSE;
+ return 0;
+}
+
+static int
+win32_kldbg_detect(struct pci_access *a)
+{
+ if (!win32_kldbg_setup(a))
+ return 0;
+
+ return 1;
+}
+
+static void
+win32_kldbg_init(struct pci_access *a)
+{
+ if (!win32_kldbg_setup(a))
+ {
+ a->debug("\n");
+ a->error("PCI config space via Kernel Local Debugging Driver cannot be accessed.");
+ }
+}
+
+static void
+win32_kldbg_cleanup(struct pci_access *a UNUSED)
+{
+ if (kldbg_dev == INVALID_HANDLE_VALUE)
+ return;
+
+ CloseHandle(kldbg_dev);
+ kldbg_dev = INVALID_HANDLE_VALUE;
+
+ if (debug_privilege_enabled)
+ {
+ win32_revert_privilege(luid_debug_privilege, revert_token, revert_only_privilege);
+ revert_token = NULL;
+ revert_only_privilege = FALSE;
+ debug_privilege_enabled = FALSE;
+ }
+}
+
+struct acpi_mcfg {
+ char signature[4];
+ u32 length;
+ u8 revision;
+ u8 checksum;
+ char oem_id[6];
+ char oem_table_id[8];
+ u32 oem_revision;
+ char asl_compiler_id[4];
+ u32 asl_compiler_revision;
+ u64 reserved;
+ struct {
+ u64 address;
+ u16 pci_segment;
+ u8 start_bus_number;
+ u8 end_bus_number;
+ u32 reserved;
+ } allocations[0];
+} PCI_PACKED;
+
+static void
+win32_kldbg_scan(struct pci_access *a)
+{
+ /*
+ * There is no kldbg API to retrieve list of PCI segments. WinDBG pci plugin
+ * kext.dll loads debug symbols from pci.pdb file for kernel module pci.sys.
+ * Then it reads kernel memory which belongs to PciSegmentList local variable
+ * which is the first entry of struct _PCI_SEGMENT linked list. And then it
+ * iterates all entries in linked list and reads SegmentNumber for each entry.
+ *
+ * This is extremly ugly hack and does not work on systems without installed
+ * kernel debug symbol files.
+ *
+ * Do something less ugly. Retrieve ACPI MCFG table via GetSystemFirmwareTable
+ * and parse all PCI segment numbers from it. ACPI MCFG table contains PCIe
+ * ECAM definitions, so all PCI segment numbers.
+ */
+
+ UINT (*WINAPI MyGetSystemFirmwareTable)(DWORD FirmwareTableProviderSignature, DWORD FirmwareTableID, PVOID pFirmwareTableBuffer, DWORD BufferSize);
+ int i, allocations_count;
+ struct acpi_mcfg *mcfg;
+ HMODULE kernel32;
+ byte *segments;
+ DWORD error;
+ DWORD size;
+
+ /* Always scan PCI segment 0. */
+ pci_generic_scan_domain(a, 0);
+
+ kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
+ if (!kernel32)
+ return;
+
+ /* Function GetSystemFirmwareTable() is available since Windows Vista. */
+ MyGetSystemFirmwareTable = (void *)GetProcAddress(kernel32, "GetSystemFirmwareTable");
+ if (!MyGetSystemFirmwareTable)
+ return;
+
+ /* 0x41435049 = 'ACPI', 0x4746434D = 'MCFG' */
+ size = MyGetSystemFirmwareTable(0x41435049, 0x4746434D, NULL, 0);
+ if (size == 0)
+ {
+ error = GetLastError();
+ if (error == ERROR_INVALID_FUNCTION) /* ACPI is not present, so only PCI segment 0 is available. */
+ return;
+ else if (error == ERROR_NOT_FOUND) /* MCFG table is not present, so only PCI segment 0 is available. */
+ return;
+ a->debug("Cannot retrieve ACPI MCFG table: %s.\n", win32_strerror(error));
+ return;
+ }
+
+ mcfg = pci_malloc(a, size);
+
+ if (MyGetSystemFirmwareTable(0x41435049, 0x4746434D, mcfg, size) != size)
+ {
+ error = GetLastError();
+ a->debug("Cannot retrieve ACPI MCFG table: %s.\n", win32_strerror(error));
+ pci_mfree(mcfg);
+ return;
+ }
+
+ if (size < sizeof(*mcfg) || size < mcfg->length)
+ {
+ a->debug("ACPI MCFG table is broken.\n");
+ pci_mfree(mcfg);
+ return;
+ }
+
+ segments = pci_malloc(a, 0xFFFF/8);
+ memset(segments, 0, 0xFFFF/8);
+
+ /* Scan all MCFG allocations and set available PCI segments into bit field. */
+ allocations_count = (mcfg->length - ((unsigned char *)&mcfg->allocations - (unsigned char *)mcfg)) / sizeof(mcfg->allocations[0]);
+ for (i = 0; i < allocations_count; i++)
+ segments[mcfg->allocations[i].pci_segment / 8] |= 1 << (mcfg->allocations[i].pci_segment % 8);
+
+ /* Skip PCI segment 0 which was already scanned. */
+ for (i = 1; i < 0xFFFF; i++)
+ if (segments[i / 8] & (1 << (i % 8)))
+ pci_generic_scan_domain(a, i);
+
+ pci_mfree(segments);
+ pci_mfree(mcfg);
+}
+
+static BOOL
+win32_kldbg_pci_bus_data(BOOL WriteBusData, USHORT SegmentNumber, BYTE BusNumber, BYTE DeviceNumber, BYTE FunctionNumber, USHORT Address, PVOID Buffer, ULONG BufferSize, LPDWORD Length)
+{
+ KLDBG kldbg_cmd;
+ SYSDBG_BUS_DATA sysdbg_cmd;
+ PCI_SLOT_NUMBER pci_slot;
+ PCI_SEGMENT_BUS_NUMBER pci_seg_bus;
+
+ memset(&pci_slot, 0, sizeof(pci_slot));
+ memset(&sysdbg_cmd, 0, sizeof(sysdbg_cmd));
+ memset(&pci_seg_bus, 0, sizeof(pci_seg_bus));
+
+ sysdbg_cmd.Address = Address;
+ sysdbg_cmd.Buffer = Buffer;
+ sysdbg_cmd.Request = BufferSize;
+ sysdbg_cmd.BusDataType = PCIConfiguration;
+ pci_seg_bus.u.bits.BusNumber = BusNumber;
+ pci_seg_bus.u.bits.SegmentNumber = SegmentNumber;
+ sysdbg_cmd.BusNumber = pci_seg_bus.u.AsULONG;
+ pci_slot.u.bits.DeviceNumber = DeviceNumber;
+ pci_slot.u.bits.FunctionNumber = FunctionNumber;
+ sysdbg_cmd.SlotNumber = pci_slot.u.AsULONG;
+
+ kldbg_cmd.Command = WriteBusData ? SysDbgWriteBusData : SysDbgReadBusData;
+ kldbg_cmd.Buffer = &sysdbg_cmd;
+ kldbg_cmd.BufferLength = sizeof(sysdbg_cmd);
+
+ *Length = 0;
+ return DeviceIoControl(kldbg_dev, IOCTL_KLDBG, &kldbg_cmd, sizeof(kldbg_cmd), &sysdbg_cmd, sizeof(sysdbg_cmd), Length, NULL);
+}
+
+static int
+win32_kldbg_read(struct pci_dev *d, int pos, byte *buf, int len)
+{
+ DWORD ret_len;
+
+ if ((unsigned int)d->domain > 0xffff)
+ return 0;
+
+ if (!win32_kldbg_pci_bus_data(FALSE, d->domain, d->bus, d->dev, d->func, pos, buf, len, &ret_len))
+ return 0;
+
+ if (ret_len != (unsigned int)len)
+ return 0;
+
+ return 1;
+}
+
+static int
+win32_kldbg_write(struct pci_dev *d, int pos, byte *buf, int len)
+{
+ DWORD ret_len;
+
+ if ((unsigned int)d->domain > 0xffff)
+ return 0;
+
+ if (!win32_kldbg_pci_bus_data(TRUE, d->domain, d->bus, d->dev, d->func, pos, buf, len, &ret_len))
+ return 0;
+
+ if (ret_len != (unsigned int)len)
+ return 0;
+
+ return 1;
+}
+
+struct pci_methods pm_win32_kldbg = {
+ "win32-kldbg",
+ "Win32 PCI config space access using Kernel Local Debugging Driver",
+ NULL, /* config */
+ win32_kldbg_detect,
+ win32_kldbg_init,
+ win32_kldbg_cleanup,
+ win32_kldbg_scan,
+ pci_generic_fill_info,
+ win32_kldbg_read,
+ win32_kldbg_write,
+ NULL, /* read_vpd */
+ NULL, /* init_dev */
+ NULL /* cleanup_dev */
+};
diff --git a/lib/win32-sysdbg.c b/lib/win32-sysdbg.c
new file mode 100644
index 0000000..99ce607
--- /dev/null
+++ b/lib/win32-sysdbg.c
@@ -0,0 +1,306 @@
+/*
+ * 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 v2+.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <windows.h>
+
+#include "internal.h"
+#include "win32-helpers.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 = win32_change_error_mode(SEM_FAILCRITICALERRORS);
+ ntdll = LoadLibrary(TEXT("ntdll.dll"));
+ win32_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 (!win32_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;
+ }
+
+ win32_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)
+ {
+ win32_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 */
+};
diff --git a/lib/winrsrc.rc.in b/lib/winrsrc.rc.in
new file mode 100644
index 0000000..e061bff
--- /dev/null
+++ b/lib/winrsrc.rc.in
@@ -0,0 +1,39 @@
+#include <windows.h>
+VS_VERSION_INFO VERSIONINFO
+FILEVERSION @PCILIB_VERSION_WINRC@
+PRODUCTVERSION @PCILIB_VERSION_WINRC@
+FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+#if @DEBUG_BUILD@
+FILEFLAGS VS_FF_DEBUG
+#else
+FILEFLAGS 0
+#endif
+FILEOS VOS_NT_WINDOWS32
+#if @LIBRARY_BUILD@
+FILETYPE VFT_DLL
+#else
+FILETYPE VFT_APP
+#endif
+FILESUBTYPE 0
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ /*
+ * GNU windres seems that converts 7-bit ASCII strings to UTF-16,
+ * so specify UNICODE/UTF-16 encoding (0x04B0) for these strings.
+ */
+ BLOCK "040904B0" /* Default U.S. English language, UNICODE/UTF-16 codepage */
+ BEGIN
+ VALUE "FileDescription", "@DESCRIPTION@"
+ VALUE "FileVersion", "@PCILIB_VERSION@"
+ VALUE "InternalName", "@FILENAME@"
+ VALUE "OriginalFilename", "@FILENAME@"
+ VALUE "ProductName", "pciutils"
+ VALUE "ProductVersion", "@PCILIB_VERSION@"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x0409, 0x04B0 /* Default U.S. English language, UNICODE/UTF-16 codepage */
+ END
+END