summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog27
-rw-r--r--Makefile17
-rw-r--r--lib/Makefile11
-rwxr-xr-xlib/configure11
-rw-r--r--lib/header.h80
-rw-r--r--lib/i386-io-linux.h48
-rw-r--r--lib/i386-io-windows.h3
-rw-r--r--lib/i386-ports.c20
-rw-r--r--lib/init.c6
-rw-r--r--lib/internal.h2
-rw-r--r--lib/pci.h1
-rw-r--r--lib/types.h2
-rw-r--r--lib/win32-cfgmgr32.c18
-rw-r--r--lib/win32-kldbg.c796
-rw-r--r--lib/winrsrc.rc.in (renamed from lib/dllrsrc.rc.in)12
-rw-r--r--ls-ecaps.c227
-rw-r--r--pcilib.man25
-rw-r--r--tests/cap-dvsec-cxl258
18 files changed, 1527 insertions, 37 deletions
diff --git a/ChangeLog b/ChangeLog
index f5852d7..a614281 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,30 @@
+2022-11-18 Martin Mares <mj@ucw.cz>
+
+ * XXX: To be released as 3.9.0.
+
+ * We decode Compute Express Link (CXL) capabilities.
+
+ * The tree mode of lspci is now compatible with filtering options.
+
+ * When setpci is used with a named register, it checks whether
+ the register is present in the particular header type.
+
+ * Linux: The intel-conf[12] back-ends prefer to use ioperm() instead
+ of iopl() to gain access to I/O ports.
+
+ * Windows: We have two new back-ends thanks to Pali Rohár.
+ One uses the NT SysDbg interface, the other uses kldbgdrv.sys
+ (which is a part of the Microsoft WinDbg tool).
+
+ * Windows: We support building libpci as a DLL. Also, Windows
+ binaries now include meta-data with version.
+
+ * Hurd: The Hurd back-end works again.
+
+ * mmio-ports: Added a new back-end implementing the intel-conf1
+ interface over MMIO. This is useful on some ARM machines, but it
+ requires manual configuration of the MMIO addresses.
+
2022-04-18 Martin Mares <mj@ucw.cz>
* Released as 3.8.0.
diff --git a/Makefile b/Makefile
index e2e3bd7..11f15f1 100644
--- a/Makefile
+++ b/Makefile
@@ -112,6 +112,21 @@ example.o: example.c $(PCIINC)
%$(EXEEXT): %.o
$(CC) $(LDFLAGS) $(TARGET_ARCH) $^ $(LDLIBS) -o $@
+ifdef PCI_OS_WINDOWS
+comma := ,
+%-rsrc.rc: lib/winrsrc.rc.in
+ sed <$< >$@ -e 's,@PCILIB_VERSION@,$(PCILIB_VERSION),' \
+ -e 's,@PCILIB_VERSION_WINRC@,$(subst .,\$(comma),$(PCILIB_VERSION).0),' \
+ -e 's,@FILENAME@,$(subst -rsrc.rc,$(EXEEXT),$@),' \
+ -e 's,@DESCRIPTION@,$(subst -rsrc.rc,,$@),' \
+ -e 's,@LIBRARY_BUILD@,0,' \
+ -e 's,@DEBUG_BUILD@,$(if $(findstring -g,$(CFLAGS)),1,0),'
+%-rsrc.o: %-rsrc.rc
+ $(WINDRES) --input=$< --output=$@ --input-format=rc --output-format=coff
+lspci$(EXEEXT): lspci-rsrc.o
+setpci$(EXEEXT): setpci-rsrc.o
+endif
+
%.8 %.7 %.5: %.man
M=`echo $(DATE) | sed 's/-01-/-January-/;s/-02-/-February-/;s/-03-/-March-/;s/-04-/-April-/;s/-05-/-May-/;s/-06-/-June-/;s/-07-/-July-/;s/-08-/-August-/;s/-09-/-September-/;s/-10-/-October-/;s/-11-/-November-/;s/-12-/-December-/;s/\(.*\)-\(.*\)-\(.*\)/\3 \2 \1/'` ; sed <$< >$@ "s/@TODAY@/$$M/;s/@VERSION@/pciutils-$(VERSION)/;s#@IDSDIR@#$(IDSDIR)#;s#@PCI_IDS@#$(PCI_IDS)#"
@@ -125,7 +140,7 @@ TAGS:
clean:
rm -f `find . -name "*~" -o -name "*.[oa]" -o -name "\#*\#" -o -name TAGS -o -name core -o -name "*.orig"`
- rm -f update-pciids lspci$(EXEEXT) setpci$(EXEEXT) example$(EXEEXT) lib/config.* *.[578] pci.ids.gz lib/*.pc lib/*.so lib/*.so.* lib/*.dll lib/*.def lib/dllrsrc.rc tags
+ rm -f update-pciids lspci$(EXEEXT) setpci$(EXEEXT) example$(EXEEXT) lib/config.* *.[578] pci.ids.gz lib/*.pc lib/*.so lib/*.so.* lib/*.dll lib/*.def lib/dllrsrc.rc *-rsrc.rc tags
rm -rf maint/dist
distclean: clean
diff --git a/lib/Makefile b/lib/Makefile
index a119bdf..43829ef 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -63,6 +63,10 @@ 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
@@ -83,10 +87,12 @@ $(PCIIMPDEF): libpci.ver ver2def.pl
$(PCIIMPLIB): $(PCIIMPDEF)
$(DLLTOOL) --input-def $< --output-lib $@
comma := ,
-dllrsrc.rc: dllrsrc.rc.in
+dllrsrc.rc: winrsrc.rc.in
sed <$< >$@ -e 's,@PCILIB_VERSION@,$(PCILIB_VERSION),' \
-e 's,@PCILIB_VERSION_WINRC@,$(subst .,\$(comma),$(PCILIB_VERSION).0),' \
- -e 's,@PCILIB@,$(PCILIB),' \
+ -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
@@ -133,6 +139,7 @@ filter.o: filter.c $(INCL)
nbsd-libpci.o: nbsd-libpci.c $(INCL)
hurd.o: hurd.c $(INCL)
win32-cfgmgr32.o: win32-cfgmgr32.c $(INCL)
+win32-kldbg.o: win32-kldbg.c $(INCL)
win32-sysdbg.o: win32-sysdbg.c $(INCL)
# MinGW32 toolchain has some required Win32 header files in /ddk subdirectory.
diff --git a/lib/configure b/lib/configure
index 7676c0b..57b064b 100755
--- a/lib/configure
+++ b/lib/configure
@@ -118,8 +118,6 @@ case $sys in
darwin*)
echo_n " darwin"
echo >>$c '#define PCI_HAVE_PM_DARWIN_DEVICE'
- echo >>$c '#define PCI_HAVE_PM_MMIO_CONF'
- echo >>$c '#define PCI_PATH_DEVMEM_DEVICE "/dev/mem"'
echo >>$m 'WITH_LIBS+=-lresolv -framework CoreFoundation -framework IOKit'
echo >>$c '#define PCI_HAVE_64BIT_ADDRESS'
LIBRESOLV=
@@ -129,8 +127,6 @@ case $sys in
aix)
echo_n " aix-device"
echo >>$c '#define PCI_HAVE_PM_AIX_DEVICE'
- echo >>$c '#define PCI_HAVE_PM_MMIO_CONF'
- echo >>$c '#define PCI_PATH_DEVMEM_DEVICE "/dev/mem"'
echo >>$m 'CFLAGS=-g'
echo >>$m 'INSTALL=installbsd'
echo >>$m 'DIRINSTALL=mkdir -p'
@@ -150,8 +146,6 @@ case $sys in
echo_n " hurd i386-ports"
echo >>$c '#define PCI_HAVE_PM_HURD_CONF'
echo >>$c '#define PCI_HAVE_PM_INTEL_CONF'
- echo >>$c '#define PCI_HAVE_PM_MMIO_CONF'
- echo >>$c '#define PCI_PATH_DEVMEM_DEVICE "/dev/mem"'
;;
djgpp)
echo_n " i386-ports"
@@ -159,9 +153,10 @@ case $sys in
EXEEXT=.exe
;;
cygwin|windows)
- echo_n " win32-cfgmgr32 win32-sysdbg"
+ 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.
@@ -188,8 +183,6 @@ case $sys in
echo >>$c '#define PCI_HAVE_PM_INTEL_CONF'
;;
esac
- echo >>$c '#define PCI_HAVE_PM_MMIO_CONF'
- echo >>$c '#define PCI_PATH_DEVMEM_DEVICE "/dev/misc/mem"'
echo >>$c '#define PCI_HAVE_STDINT_H'
;;
sylixos)
diff --git a/lib/header.h b/lib/header.h
index e74f6d8..63ee03c 100644
--- a/lib/header.h
+++ b/lib/header.h
@@ -1129,6 +1129,86 @@
/* 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 */
+
+/* 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 */
diff --git a/lib/i386-io-linux.h b/lib/i386-io-linux.h
index 731e8e3..a2fd69e 100644
--- a/lib/i386-io-linux.h
+++ b/lib/i386-io-linux.h
@@ -7,17 +7,61 @@
*/
#include <sys/io.h>
+#include <errno.h>
+
+static int ioperm_enabled;
+static int iopl_enabled;
static int
intel_setup_io(struct pci_access *a UNUSED)
{
- return (iopl(3) < 0) ? 0 : 1;
+ 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)
{
- iopl(0);
+ 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)
diff --git a/lib/i386-io-windows.h b/lib/i386-io-windows.h
index 1509d7d..6bb578c 100644
--- a/lib/i386-io-windows.h
+++ b/lib/i386-io-windows.h
@@ -1343,7 +1343,8 @@ intel_setup_io(struct pci_access *a)
/* On NT-based systems issue ProcessUserModeIOPL syscall which changes IOPL to 3. */
if (!SetProcessUserModeIOPL())
{
- a->warning("NT ProcessUserModeIOPL call failed with error: %lu.", (unsigned long int)GetLastError());
+ DWORD error = GetLastError();
+ a->debug("NT ProcessUserModeIOPL call failed: %s.", error == ERROR_INVALID_FUNCTION ? "Not Implemented" : error == ERROR_PRIVILEGE_NOT_HELD ? "Access Denied" : "Operation Failed");
return 0;
}
diff --git a/lib/i386-ports.c b/lib/i386-ports.c
index 2e64fe4..0ca87dd 100644
--- a/lib/i386-ports.c
+++ b/lib/i386-ports.c
@@ -136,6 +136,9 @@ conf1_read(struct pci_dev *d, int pos, byte *buf, int len)
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();
outl(0x80000000 | ((d->bus & 0xff) << 16) | (PCI_DEVFN(d->dev, d->func) << 8) | (pos&~3), 0xcf8);
@@ -150,8 +153,6 @@ conf1_read(struct pci_dev *d, int pos, byte *buf, int len)
case 4:
((u32 *) buf)[0] = cpu_to_le32(inl(addr));
break;
- default:
- res = pci_generic_block_read(d, pos, buf, len);
}
intel_io_unlock();
@@ -167,6 +168,9 @@ conf1_write(struct pci_dev *d, int pos, byte *buf, int len)
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();
outl(0x80000000 | ((d->bus & 0xff) << 16) | (PCI_DEVFN(d->dev, d->func) << 8) | (pos&~3), 0xcf8);
@@ -181,8 +185,6 @@ conf1_write(struct pci_dev *d, int pos, byte *buf, int len)
case 4:
outl(le32_to_cpu(((u32 *) buf)[0]), addr);
break;
- default:
- res = pci_generic_block_write(d, pos, buf, len);
}
intel_io_unlock();
return res;
@@ -228,6 +230,9 @@ conf2_read(struct pci_dev *d, int pos, byte *buf, int len)
/* 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();
outb((d->func << 1) | 0xf0, 0xcf8);
outb(d->bus, 0xcfa);
@@ -242,8 +247,6 @@ conf2_read(struct pci_dev *d, int pos, byte *buf, int len)
case 4:
((u32 *) buf)[0] = cpu_to_le32(inl(addr));
break;
- default:
- res = pci_generic_block_read(d, pos, buf, len);
}
outb(0, 0xcf8);
intel_io_unlock();
@@ -263,6 +266,9 @@ conf2_write(struct pci_dev *d, int pos, byte *buf, int len)
/* 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();
outb((d->func << 1) | 0xf0, 0xcf8);
outb(d->bus, 0xcfa);
@@ -277,8 +283,6 @@ conf2_write(struct pci_dev *d, int pos, byte *buf, int len)
case 4:
outl(le32_to_cpu(* (u32 *) buf), addr);
break;
- default:
- res = pci_generic_block_write(d, pos, buf, len);
}
outb(0, 0xcf8);
diff --git a/lib/init.c b/lib/init.c
index c81d90c..5ce260a 100644
--- a/lib/init.c
+++ b/lib/init.c
@@ -81,6 +81,11 @@ static struct pci_methods *pci_methods[PCI_ACCESS_MAX] = {
#else
NULL,
#endif
+#ifdef PCI_HAVE_PM_WIN32_KLDBG
+ &pm_win32_kldbg,
+#else
+ NULL,
+#endif
#ifdef PCI_HAVE_PM_WIN32_SYSDBG
&pm_win32_sysdbg,
#else
@@ -106,6 +111,7 @@ static int probe_sequence[] = {
PCI_ACCESS_SYLIXOS_DEVICE,
PCI_ACCESS_HURD,
PCI_ACCESS_WIN32_CFGMGR32,
+ PCI_ACCESS_WIN32_KLDBG,
PCI_ACCESS_WIN32_SYSDBG,
// Low-level methods poking the hardware directly
PCI_ACCESS_I386_TYPE1,
diff --git a/lib/internal.h b/lib/internal.h
index 6bfcde7..81316dc 100644
--- a/lib/internal.h
+++ b/lib/internal.h
@@ -135,4 +135,4 @@ 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_win32_cfgmgr32, pm_win32_sysdbg;
+ pm_win32_cfgmgr32, pm_win32_kldbg, pm_win32_sysdbg;
diff --git a/lib/pci.h b/lib/pci.h
index f6197c4..f198f11 100644
--- a/lib/pci.h
+++ b/lib/pci.h
@@ -44,6 +44,7 @@ enum pci_access_type {
PCI_ACCESS_SYLIXOS_DEVICE, /* SylixOS pci */
PCI_ACCESS_HURD, /* GNU/Hurd */
PCI_ACCESS_WIN32_CFGMGR32, /* Win32 cfgmgr32.dll */
+ PCI_ACCESS_WIN32_KLDBG, /* Win32 kldbgdrv.sys */
PCI_ACCESS_WIN32_SYSDBG, /* Win32 NT SysDbg */
PCI_ACCESS_MMIO_TYPE1, /* MMIO ports, type 1 */
PCI_ACCESS_MAX
diff --git a/lib/types.h b/lib/types.h
index 243997f..ab53f7c 100644
--- a/lib/types.h
+++ b/lib/types.h
@@ -75,7 +75,9 @@ typedef u32 pciaddr_t;
#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/win32-cfgmgr32.c b/lib/win32-cfgmgr32.c
index 004f95f..a3404d1 100644
--- a/lib/win32-cfgmgr32.c
+++ b/lib/win32-cfgmgr32.c
@@ -126,7 +126,7 @@ resolve_cfgmgr32_functions(void)
* cfgmgr32.dll uses custom non-Win32 error numbers which are unsupported by
* Win32 APIs like GetLastError() and FormatMessage() functions.
*
- * Windows 7 instroduced new cfgmgr32.dll function CM_MapCrToWin32Err() for
+ * 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.
*
@@ -498,18 +498,28 @@ retry_service_config:
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)
+ 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 + sizeof("\\??\\UNC\\")-1);
+ 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)));
- wcscpy(service_image_path, 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'\\')
{
diff --git a/lib/win32-kldbg.c b/lib/win32-kldbg.c
new file mode 100644
index 0000000..3890b6b
--- /dev/null
+++ b/lib/win32-kldbg.c
@@ -0,0 +1,796 @@
+/*
+ * 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.
+ */
+
+#include <windows.h>
+#include <winioctl.h>
+
+#include <stdio.h> /* for sprintf() */
+#include <string.h> /* for memset() and memcpy() */
+
+#include "internal.h"
+#include "i386-io-windows.h"
+
+#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 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;
+
+ len = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, win32_error_id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buffer, sizeof(buffer), NULL);
+
+ /* 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;
+}
+
+static BOOL
+win32_is_32bit_on_64bit_system(void)
+{
+ BOOL (WINAPI *MyIsWow64Process)(HANDLE, PBOOL);
+ HMODULE kernel32;
+ BOOL is_wow64;
+
+ 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;
+}
+
+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, void *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 (WINAPI *get_system_root_path)(void *buffer, UINT size) = NULL;
+ UINT systemroot_len;
+ void *driver_path;
+ HANDLE driver_handle;
+ HMODULE kernel32;
+
+ /*
+ * COM library dbgeng.dll unpacks kldbg driver to file \\system32\\kldbgdrv.sys
+ * and register this driver with service name kldbgdrv. Implement same behavior.
+ */
+
+ /*
+ * Old Windows versions return path to NT SystemRoot namespace via
+ * GetWindowsDirectory() function. New Windows versions via
+ * GetSystemWindowsDirectory(). GetSystemWindowsDirectory() is not
+ * provided in old Windows versions, so use GetProcAddress() for
+ * compatibility with all Windows versions.
+ */
+
+ kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
+ if (kernel32)
+ get_system_root_path = (void *)GetProcAddress(kernel32, "GetSystemWindowsDirectory"
+#ifdef UNICODE
+ "W"
+#else
+ "A"
+#endif
+ );
+
+ if (!get_system_root_path)
+ get_system_root_path = (void *)&GetWindowsDirectory;
+
+ systemroot_len = get_system_root_path(NULL, 0);
+ if (!systemroot_len)
+ systemroot_len = sizeof(TEXT("C:\\Windows\\"));
+
+ driver_path = pci_malloc(a, systemroot_len + sizeof(TEXT("\\system32\\kldbgdrv.sys")));
+
+ systemroot_len = get_system_root_path(driver_path, systemroot_len + sizeof(TEXT("")));
+ if (!systemroot_len)
+ {
+ systemroot_len = sizeof(TEXT("C:\\Windows\\"));
+ memcpy(driver_path, TEXT("C:\\Windows\\"), systemroot_len);
+ }
+
+ if (((char *)driver_path)[systemroot_len-sizeof(TEXT(""))+1] != '\\')
+ {
+ ((char *)driver_path)[systemroot_len-sizeof(TEXT(""))+1] = '\\';
+ systemroot_len += sizeof(TEXT(""));
+ }
+
+ memcpy((char *)driver_path + systemroot_len, TEXT("system32\\kldbgdrv.sys"), sizeof(TEXT("system32\\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 (!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;
+
+ 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)
+ {
+ 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/dllrsrc.rc.in b/lib/winrsrc.rc.in
index 519772b..e061bff 100644
--- a/lib/dllrsrc.rc.in
+++ b/lib/winrsrc.rc.in
@@ -9,7 +9,11 @@ FILEFLAGS VS_FF_DEBUG
FILEFLAGS 0
#endif
FILEOS VOS_NT_WINDOWS32
+#if @LIBRARY_BUILD@
FILETYPE VFT_DLL
+#else
+FILETYPE VFT_APP
+#endif
FILESUBTYPE 0
BEGIN
BLOCK "StringFileInfo"
@@ -20,16 +24,16 @@ BEGIN
*/
BLOCK "040904B0" /* Default U.S. English language, UNICODE/UTF-16 codepage */
BEGIN
- VALUE "FileDescription", "libpci"
+ VALUE "FileDescription", "@DESCRIPTION@"
VALUE "FileVersion", "@PCILIB_VERSION@"
- VALUE "InternalName", "@PCILIB@"
- VALUE "OriginalFilename", "@PCILIB@"
+ VALUE "InternalName", "@FILENAME@"
+ VALUE "OriginalFilename", "@FILENAME@"
VALUE "ProductName", "pciutils"
VALUE "ProductVersion", "@PCILIB_VERSION@"
END
END
BLOCK "VarFileInfo"
BEGIN
- VALUE "Translation", 0x0409, 0x004B0 /* Default U.S. English language, UNICODE/UTF-16 codepage */
+ VALUE "Translation", 0x0409, 0x04B0 /* Default U.S. English language, UNICODE/UTF-16 codepage */
END
END
diff --git a/ls-ecaps.c b/ls-ecaps.c
index b9a4512..df41626 100644
--- a/ls-ecaps.c
+++ b/ls-ecaps.c
@@ -839,6 +839,223 @@ dvsec_cxl_register_locator(struct device *d, int where, int len)
}
static void
+dvsec_cxl_gpf_device(struct device *d, int where)
+{
+ u32 l;
+ u16 w, duration;
+ u8 time_base, time_scale;
+
+ w = get_conf_word(d, where + PCI_CXL_GPF_DEV_PHASE2_DUR);
+ time_base = BITS(w, 0, 4);
+ time_scale = BITS(w, 8, 4);
+
+ switch (time_scale)
+ {
+ case PCI_CXL_GPF_DEV_100US:
+ case PCI_CXL_GPF_DEV_100MS:
+ duration = time_base * 100;
+ break;
+ case PCI_CXL_GPF_DEV_10US:
+ case PCI_CXL_GPF_DEV_10MS:
+ case PCI_CXL_GPF_DEV_10S:
+ duration = time_base * 10;
+ break;
+ case PCI_CXL_GPF_DEV_1US:
+ case PCI_CXL_GPF_DEV_1MS:
+ case PCI_CXL_GPF_DEV_1S:
+ duration = time_base;
+ break;
+ default:
+ /* Reserved */
+ printf("\t\tReserved time scale encoding %x\n", time_scale);
+ duration = time_base;
+ }
+
+ printf("\t\tGPF Phase 2 Duration: %u%s\n", duration,
+ (time_scale < PCI_CXL_GPF_DEV_1MS) ? "us":
+ (time_scale < PCI_CXL_GPF_DEV_1S) ? "ms" :
+ (time_scale == PCI_CXL_GPF_DEV_1S) ? "s" : "<?>");
+
+ l = get_conf_long(d, where + PCI_CXL_GPF_DEV_PHASE2_POW);
+ printf("\t\tGPF Phase 2 Power: %umW\n", (unsigned int)l);
+}
+
+static void
+dvsec_cxl_gpf_port(struct device *d, int where)
+{
+ u16 w, timeout;
+ u8 time_base, time_scale;
+
+ w = get_conf_word(d, where + PCI_CXL_GPF_PORT_PHASE1_CTRL);
+ time_base = BITS(w, 0, 4);
+ time_scale = BITS(w, 8, 4);
+
+ switch (time_scale)
+ {
+ case PCI_CXL_GPF_PORT_100US:
+ case PCI_CXL_GPF_PORT_100MS:
+ timeout = time_base * 100;
+ break;
+ case PCI_CXL_GPF_PORT_10US:
+ case PCI_CXL_GPF_PORT_10MS:
+ case PCI_CXL_GPF_PORT_10S:
+ timeout = time_base * 10;
+ break;
+ case PCI_CXL_GPF_PORT_1US:
+ case PCI_CXL_GPF_PORT_1MS:
+ case PCI_CXL_GPF_PORT_1S:
+ timeout = time_base;
+ break;
+ default:
+ /* Reserved */
+ printf("\t\tReserved time scale encoding %x\n", time_scale);
+ timeout = time_base;
+ }
+
+ printf("\t\tGPF Phase 1 Timeout: %d%s\n", timeout,
+ (time_scale < PCI_CXL_GPF_PORT_1MS) ? "us":
+ (time_scale < PCI_CXL_GPF_PORT_1S) ? "ms" :
+ (time_scale == PCI_CXL_GPF_PORT_1S) ? "s" : "<?>");
+
+ w = get_conf_word(d, where + PCI_CXL_GPF_PORT_PHASE2_CTRL);
+ time_base = BITS(w, 0, 4);
+ time_scale = BITS(w, 8, 4);
+
+ switch (time_scale)
+ {
+ case PCI_CXL_GPF_PORT_100US:
+ case PCI_CXL_GPF_PORT_100MS:
+ timeout = time_base * 100;
+ break;
+ case PCI_CXL_GPF_PORT_10US:
+ case PCI_CXL_GPF_PORT_10MS:
+ case PCI_CXL_GPF_PORT_10S:
+ timeout = time_base * 10;
+ break;
+ case PCI_CXL_GPF_PORT_1US:
+ case PCI_CXL_GPF_PORT_1MS:
+ case PCI_CXL_GPF_PORT_1S:
+ timeout = time_base;
+ break;
+ default:
+ /* Reserved */
+ printf("\t\tReserved time scale encoding %x\n", time_scale);
+ timeout = time_base;
+ }
+
+ printf("\t\tGPF Phase 2 Timeout: %d%s\n", timeout,
+ (time_scale < PCI_CXL_GPF_PORT_1MS) ? "us":
+ (time_scale < PCI_CXL_GPF_PORT_1S) ? "ms" :
+ (time_scale == PCI_CXL_GPF_PORT_1S) ? "s" : "<?>");
+}
+
+static void
+dvsec_cxl_flex_bus(struct device *d, int where, int rev)
+{
+ u16 w;
+ u32 l, data;
+
+ if (rev < 1)
+ {
+ printf("\t\tRevision %d not supported\n", rev);
+ return;
+ }
+
+ w = get_conf_word(d, where + PCI_CXL_FB_PORT_CAP);
+ printf("\t\tFBCap:\tCache%c IO%c Mem%c 68BFlit%c MltLogDev%c",
+ FLAG(w, PCI_CXL_FB_CAP_CACHE), FLAG(w, PCI_CXL_FB_CAP_IO),
+ FLAG(w, PCI_CXL_FB_CAP_MEM), FLAG(w, PCI_CXL_FB_CAP_68B_FLIT),
+ FLAG(w, PCI_CXL_FB_CAP_MULT_LOG_DEV));
+
+ if (rev > 1)
+ printf(" 256BFlit%c PBRFlit%c",
+ FLAG(w, PCI_CXL_FB_CAP_256B_FLIT), FLAG(w, PCI_CXL_FB_CAP_PBR_FLIT));
+
+ w = get_conf_word(d, where + PCI_CXL_FB_PORT_CTRL);
+ printf("\n\t\tFBCtl:\tCache%c IO%c Mem%c SynHdrByp%c DrftBuf%c 68BFlit%c MltLogDev%c RCD%c Retimer1%c Retimer2%c",
+ FLAG(w, PCI_CXL_FB_CTRL_CACHE), FLAG(w, PCI_CXL_FB_CTRL_IO),
+ FLAG(w, PCI_CXL_FB_CTRL_MEM), FLAG(w, PCI_CXL_FB_CTRL_SYNC_HDR_BYP),
+ FLAG(w, PCI_CXL_FB_CTRL_DRFT_BUF), FLAG(w, PCI_CXL_FB_CTRL_68B_FLIT),
+ FLAG(w, PCI_CXL_FB_CTRL_MULT_LOG_DEV), FLAG(w, PCI_CXL_FB_CTRL_RCD),
+ FLAG(w, PCI_CXL_FB_CTRL_RETIMER1), FLAG(w, PCI_CXL_FB_CTRL_RETIMER2));
+
+ if (rev > 1)
+ printf(" 256BFlit%c PBRFlit%c",
+ FLAG(w, PCI_CXL_FB_CTRL_256B_FLIT), FLAG(w, PCI_CXL_FB_CTRL_PBR_FLIT));
+
+ w = get_conf_word(d, where + PCI_CXL_FB_PORT_STATUS);
+ printf("\n\t\tFBSta:\tCache%c IO%c Mem%c SynHdrByp%c DrftBuf%c 68BFlit%c MltLogDev%c",
+ FLAG(w, PCI_CXL_FB_STAT_CACHE), FLAG(w, PCI_CXL_FB_STAT_IO),
+ FLAG(w, PCI_CXL_FB_STAT_MEM), FLAG(w, PCI_CXL_FB_STAT_SYNC_HDR_BYP),
+ FLAG(w, PCI_CXL_FB_STAT_DRFT_BUF), FLAG(w, PCI_CXL_FB_STAT_68B_FLIT),
+ FLAG(w, PCI_CXL_FB_STAT_MULT_LOG_DEV));
+
+ if (rev > 1)
+ printf(" 256BFlit%c PBRFlit%c",
+ FLAG(w, PCI_CXL_FB_STAT_256B_FLIT), FLAG(w, PCI_CXL_FB_STAT_PBR_FLIT));
+
+ l = get_conf_long(d, where + PCI_CXL_FB_MOD_TS_DATA);
+ data = BITS(l, 0, 24);
+ printf("\n\t\tFBModTS:\tReceived FB Data: %06x\n", (unsigned int)data);
+
+ if (rev > 1)
+ {
+ u8 nop;
+
+ l = get_conf_long(d, where + PCI_CXL_FB_PORT_CAP2);
+ printf("\t\tFBCap2:\tNOPHint%c\n", FLAG(l, PCI_CXL_FB_CAP2_NOP_HINT));
+
+ l = get_conf_long(d, where + PCI_CXL_FB_PORT_CTRL2);
+ printf("\t\tFBCtl2:\tNOPHint%c\n", FLAG(l, PCI_CXL_FB_CTRL2_NOP_HINT));
+
+ l = get_conf_long(d, where + PCI_CXL_FB_PORT_STATUS2);
+ nop = BITS(l, 0, 2);
+ printf("\t\tFBSta2:\tNOPHintInfo: %x\n", nop);
+ }
+}
+
+static void
+dvsec_cxl_mld(struct device *d, int where)
+{
+ u16 w;
+
+ w = get_conf_word(d, where + PCI_CXL_MLD_NUM_LD);
+
+ /* Encodings greater than 16 are reserved */
+ if (w && w <= PCI_CXL_MLD_MAX_LD)
+ printf("\t\tNumLogDevs: %d\n", w);
+}
+
+static void
+dvsec_cxl_function_map(struct device *d, int where)
+{
+
+ printf("\t\tFuncMap 0: %08x\n",
+ (unsigned int)(get_conf_word(d, where + PCI_CXL_FUN_MAP_REG_0)));
+
+ printf("\t\tFuncMap 1: %08x\n",
+ (unsigned int)(get_conf_word(d, where + PCI_CXL_FUN_MAP_REG_1)));
+
+ printf("\t\tFuncMap 2: %08x\n",
+ (unsigned int)(get_conf_word(d, where + PCI_CXL_FUN_MAP_REG_2)));
+
+ printf("\t\tFuncMap 3: %08x\n",
+ (unsigned int)(get_conf_word(d, where + PCI_CXL_FUN_MAP_REG_3)));
+
+ printf("\t\tFuncMap 4: %08x\n",
+ (unsigned int)(get_conf_word(d, where + PCI_CXL_FUN_MAP_REG_4)));
+
+ printf("\t\tFuncMap 5: %08x\n",
+ (unsigned int)(get_conf_word(d, where + PCI_CXL_FUN_MAP_REG_5)));
+
+ printf("\t\tFuncMap 6: %08x\n",
+ (unsigned int)(get_conf_word(d, where + PCI_CXL_FUN_MAP_REG_6)));
+
+ printf("\t\tFuncMap 7: %08x\n",
+ (unsigned int)(get_conf_word(d, where + PCI_CXL_FUN_MAP_REG_7)));
+}
+
+static void
cap_dvsec_cxl(struct device *d, int id, int rev, int where, int len)
{
printf(": CXL\n");
@@ -854,25 +1071,25 @@ cap_dvsec_cxl(struct device *d, int id, int rev, int where, int len)
dvsec_cxl_device(d, rev, where, len);
break;
case 2:
- printf("\t\tNon-CXL Function Map DVSEC\n");
+ dvsec_cxl_function_map(d, where);
break;
case 3:
dvsec_cxl_port(d, where, len);
break;
case 4:
- printf("\t\tGPF DVSEC for Port\n");
+ dvsec_cxl_gpf_port(d, where);
break;
case 5:
- printf("\t\tGPF DVSEC for Device\n");
+ dvsec_cxl_gpf_device(d, where);
break;
case 7:
- printf("\t\tPCIe DVSEC Flex Bus Port\n");
+ dvsec_cxl_flex_bus(d, where, rev);
break;
case 8:
dvsec_cxl_register_locator(d, where, len);
break;
case 9:
- printf("\t\tMLD DVSEC\n");
+ dvsec_cxl_mld(d, where);
break;
default:
printf("\t\tUnknown ID %04x\n", id);
diff --git a/pcilib.man b/pcilib.man
index ae6af9f..7015294 100644
--- a/pcilib.man
+++ b/pcilib.man
@@ -90,6 +90,31 @@ systems. Process needs to have Debug privilege, which local Administrators
have by default. Not available on 64-bit systems and neither on recent 32-bit
systems. Only devices from the first domain are accessible and only first
256 bytes of the PCI configuration space is accessible via this method.
+.TP
+.B win32-kldbg
+Access to the PCI configuration space via Kernel Local Debugging Driver
+kldbgdrv.sys. This driver is not part of the Windows system but is part of
+the Microsoft WinDbg tool. It is required to have kldbgdrv.sys driver installed
+in the system32 directory or to have windbg.exe or kd.exe binary in PATH.
+kldbgdrv.sys driver has some restrictions. Process needs to have Debug privilege
+and Windows system has to be booted with Debugging option. Debugging option can
+be enabled by calling (takes effect after next boot):
+.B bcdedit /debug on
+.IP
+Download links for WinDbg 6.12.2.633 standalone installer from Microsoft Windows
+SDK for Windows 7 and .NET Framework 4:
+.br
+amd64: https://download.microsoft.com/download/A/6/A/A6AC035D-DA3F-4F0C-ADA4-37C8E5D34E3D/setup/WinSDKDebuggingTools_amd64/dbg_amd64.msi
+.br
+ia64: https://download.microsoft.com/download/A/6/A/A6AC035D-DA3F-4F0C-ADA4-37C8E5D34E3D/setup/WinSDKDebuggingTools_ia64/dbg_ia64.msi
+.br
+x86: https://download.microsoft.com/download/A/6/A/A6AC035D-DA3F-4F0C-ADA4-37C8E5D34E3D/setup/WinSDKDebuggingTools/dbg_x86.msi
+.IP
+Archived download links of previous WinDbg versions:
+.br
+https://web.archive.org/web/20110221133326/https://www.microsoft.com/whdc/devtools/debugging/installx86.mspx
+.br
+https://web.archive.org/web/20110214012715/https://www.microsoft.com/whdc/devtools/debugging/install64bit.mspx
.SH PARAMETERS
diff --git a/tests/cap-dvsec-cxl b/tests/cap-dvsec-cxl
index a24e3fb..c5fa9e7 100644
--- a/tests/cap-dvsec-cxl
+++ b/tests/cap-dvsec-cxl
@@ -351,3 +351,261 @@ fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+
+7f:00.0 CXL: Xilinx Corporation Device c084 (rev 70) (prog-if 10 [CXL Memory Device (CXL 2.x)])
+00: ee 10 84 c0 02 00 10 00 70 10 02 05 10 00 00 00
+10: 0c 00 00 b0 80 03 00 00 0c 00 10 b0 80 03 00 00
+20: 00 00 00 00 00 00 00 00 00 00 00 00 ee 10 84 c0
+30: 00 00 00 00 80 00 00 00 00 00 00 00 05 01 00 00
+40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+80: 10 e0 92 00 21 80 2c 11 30 29 00 00 00 00 00 00
+90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+a0: 00 00 00 00 10 00 11 00 00 00 00 00 00 00 00 00
+b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+e0: 05 f8 88 00 00 00 00 00 00 00 00 00 00 00 00 00
+f0: 00 00 00 00 00 00 00 00 01 00 03 00 08 00 00 00
+100: 0b 00 81 12 56 15 81 00 00 00 00 00 00 00 00 00
+110: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+120: 00 00 00 00 00 00 00 00 0e 00 01 1e 00 00 00 00
+130: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+140: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+150: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+160: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+170: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+180: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+190: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+1a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+1b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+1c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+1d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+1e0: 25 00 01 20 01 00 00 80 01 00 00 80 00 00 00 00
+1f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+200: 01 00 02 45 00 00 00 00 00 00 40 00 10 20 46 00
+210: 00 00 00 00 00 60 00 00 00 00 00 00 00 00 00 00
+220: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+230: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+240: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+250: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+260: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+270: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+280: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+290: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+2a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+2b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+2c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+2d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+2e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+2f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+300: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+310: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+320: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+330: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+340: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+350: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+360: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+370: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+380: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+390: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+3f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+400: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+410: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+420: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+430: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+440: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+450: 2e 00 01 50 03 00 00 00 00 00 00 00 02 00 00 00
+460: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+470: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+480: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+490: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+4a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+4b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+4c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+4d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+4e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+4f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+500: 23 00 01 54 98 1e 81 03 00 00 1e 40 06 00 00 00
+510: 00 00 00 80 00 00 00 00 04 00 00 00 03 00 00 00
+520: 00 00 00 00 00 00 00 00 00 00 00 00 02 00 00 00
+530: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+540: 23 00 01 56 98 1e 41 01 07 00 26 00 26 00 06 00
+550: 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+560: 23 00 01 59 98 1e 40 02 08 00 00 00 00 01 00 00
+570: 00 00 00 00 00 03 01 00 00 00 00 00 00 00 00 00
+580: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+590: 23 00 01 00 98 1e 00 01 05 00 03 02 00 00 00 00
+5a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+5b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+5c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+5d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+5e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+5f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+600: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+610: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+620: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+630: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+640: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+650: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+660: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+670: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+680: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+690: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+6a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+6b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+6c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+6d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+6e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+6f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+700: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+710: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+720: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+730: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+740: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+750: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+760: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+770: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+780: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+790: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+7a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+7b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+7c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+7d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+7e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+7f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+810: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+820: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+830: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+840: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+850: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+860: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+870: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+880: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+890: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+8a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+8b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+8c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+8d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+8e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+8f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+900: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+910: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+920: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+930: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+940: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+950: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+960: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+970: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+980: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+990: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+9a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+9b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+9c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+9d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+9e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+9f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+a00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+a10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+a20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+a30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+a40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+a50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+a60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+a70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+a80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+a90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+aa0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ab0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ac0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ad0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ae0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+af0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+b00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+b10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+b20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+b30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+b40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+b50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+b60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+b70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+b80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+b90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ba0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+bb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+bc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+bd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+be0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+bf0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+c00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+c10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+c20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+c30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+c40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+c50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+c60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+c70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+c80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+c90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ca0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+cb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+cc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+cd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ce0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+cf0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+d00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+d10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+d20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+d30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+d40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+d50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+d60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+d70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+d80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+d90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+da0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+db0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+dc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+dd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+de0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+df0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+e00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+e10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+e20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+e30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+e40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+e50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+e60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+e70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+e80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+e90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ea0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+eb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ec0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ed0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ee0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ef0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+f00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+f10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+f20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+f30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+f40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+f50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+f60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+f70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+f80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+f90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+fa0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00