diff options
author | Pali Rohár <pali@kernel.org> | 2022-11-06 13:58:55 +0100 |
---|---|---|
committer | Pali Rohár <pali@kernel.org> | 2022-11-06 13:58:55 +0100 |
commit | ebd12ed869fd62fcd1cfeaaaed406a80cd1c7ae5 (patch) | |
tree | 6fb6e725c9abd256b1ec8238bfdfd16a27101c82 | |
parent | 55704534e519d38ed4c2362925b2a68cf4c1e476 (diff) | |
download | pciutils-ebd12ed869fd62fcd1cfeaaaed406a80cd1c7ae5.tar.gz |
i386-io-linux: Prefer usage of ioperm()
Since Linux 2.6.8, it is possible to use ioperm() syscall to gain access
for all I/O ports. Because iopl() syscall before Linux 5.5 allowed
userspace to disable interrupts, prefer usage of ioperm() syscall and ask
for access only for PCI ports.
-rw-r--r-- | lib/i386-io-linux.h | 48 |
1 files changed, 46 insertions, 2 deletions
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) |