From: Pali Rohár Date: Sun, 6 Nov 2022 12:58:55 +0000 (+0100) Subject: i386-io-linux: Prefer usage of ioperm() X-Git-Tag: v3.9.0~11^2 X-Git-Url: http://mj.ucw.cz/gitweb/?a=commitdiff_plain;h=ebd12ed869fd62fcd1cfeaaaed406a80cd1c7ae5;p=pciutils.git 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. --- 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 +#include + +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)