aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPali Rohár <pali@kernel.org>2022-11-06 13:58:55 +0100
committerPali Rohár <pali@kernel.org>2022-11-06 13:58:55 +0100
commitebd12ed869fd62fcd1cfeaaaed406a80cd1c7ae5 (patch)
tree6fb6e725c9abd256b1ec8238bfdfd16a27101c82
parent55704534e519d38ed4c2362925b2a68cf4c1e476 (diff)
downloadpciutils-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.h48
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)