]> mj.ucw.cz Git - pciutils.git/commitdiff
lib: Refactor access to x86 I/O ports
authorPali Rohár <pali@kernel.org>
Sun, 8 Oct 2023 13:10:12 +0000 (15:10 +0200)
committerGrant Pannell <300992+DigitalDJ@users.noreply.github.com>
Sat, 30 Dec 2023 15:26:49 +0000 (01:56 +1030)
On all systems except BeOS and Haiku are x86 I/O ports accessed in the
standard way by the x86 in/out instructions.

On more systems there are wrapper functions for x86 in/out instructions but
under different names and sometimes even for same system those names
depends on user version of toolchain/compiler. And also some systems have
same function names but switched order of arguments.

Simplify this code, define own wrapper functions for x86 in/out
instructions in new header file i386-io-access.h and use it for every
platform except BeOS and Haiku.

This change simplifies Windows port, duplicated code between SunOS and
Windows and also tons of redefined port functions in every port.

To not conlict with possible system functions included from some header
file, add intel_ prefix for every function included from the file
lib/i386-io-access.h into lib/i386-ports.c

lib/i386-io-access.h [new file with mode: 0644]
lib/i386-io-beos.h
lib/i386-io-cygwin.h
lib/i386-io-djgpp.h
lib/i386-io-haiku.h
lib/i386-io-hurd.h
lib/i386-io-linux.h
lib/i386-io-sunos.h
lib/i386-io-windows.h
lib/i386-ports.c

diff --git a/lib/i386-io-access.h b/lib/i386-io-access.h
new file mode 100644 (file)
index 0000000..8b1ad5f
--- /dev/null
@@ -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
index 49b70949273a74a8b03ce88dab3f5338014c5220..dac0e4b9b99f14ad3d089be5f4a8c09d0eca12a0 100644 (file)
@@ -24,37 +24,37 @@ intel_cleanup_io(struct pci_access *a UNUSED)
 }
 
 static inline u8
-inb (u16 port)
+intel_inb (u16 port)
 {
   return (u8)read_isa_io(0, (void *)(u32)port, sizeof(u8));
 }
 
 static inline u16
-inw (u16 port)
+intel_inw (u16 port)
 {
   return (u16)read_isa_io(0, (void *)(u32)port, sizeof(u16));
 }
 
 static inline u32
-inl (u16 port)
+intel_inl (u16 port)
 {
   return (u32)read_isa_io(0, (void *)(u32)port, sizeof(u32));
 }
 
 static inline void
-outb (u8 value, u16 port)
+intel_outb (u8 value, u16 port)
 {
   write_isa_io(0, (void *)(u32)port, sizeof(value), value);
 }
 
 static inline void
-outw (u16 value, u16 port)
+intel_outw (u16 value, u16 port)
 {
   write_isa_io(0, (void *)(u32)port, sizeof(value), value);
 }
 
 static inline void
-outl (u32 value, u16 port)
+intel_outl (u32 value, u16 port)
 {
   write_isa_io(0, (void *)(u32)port, sizeof(value), value);
 }
index 0b71d16c3ff635bc71e0d6173cee4bbcc9ee3ed9..41180570e8ff610c403dc3042024ef22313c9cf0 100644 (file)
@@ -10,6 +10,8 @@
 
 #include <sys/io.h>
 
+#include "i386-io-access.h"
+
 static int
 intel_setup_io(struct pci_access *a UNUSED)
 {
index bb295266901634dd70e7fff6593522a64f105d0f..1afb00e4a562c94face80fb93abb801427ed630e 100644 (file)
@@ -8,15 +8,9 @@
  *     SPDX-License-Identifier: GPL-2.0-or-later
  */
 
-#include <pc.h>
 #include <dos.h>
-#define outb(x,y) outportb(y, x)
-#define outw(x,y) outportw(y, x)
-#define outl(x,y) outportl(y, x)
 
-#define inb  inportb
-#define inw  inportw
-#define inl  inportl
+#include "i386-io-access.h"
 
 static int irq_enabled;
 
index ce5362be82a6d7f7dae5cc87d1e4406a2f7f2a32..23843eabb525c99d63c7e4c0d4b2cc5d4085f0ab 100644 (file)
@@ -66,6 +66,15 @@ 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;
 }
@@ -77,7 +86,7 @@ intel_cleanup_io(struct pci_access *a UNUSED)
 }
 
 static inline u8
-inb (u16 port)
+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)
@@ -86,7 +95,7 @@ inb (u16 port)
 }
 
 static inline u16
-inw (u16 port)
+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)
@@ -95,7 +104,7 @@ inw (u16 port)
 }
 
 static inline u32
-inl (u16 port)
+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)
@@ -104,21 +113,21 @@ inl (u16 port)
 }
 
 static inline void
-outb (u8 value, u16 port)
+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
-outw (u16 value, u16 port)
+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
-outl (u32 value, u16 port)
+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));
index d44b2f537e856848b06daafc4706564097ff1f9e..01d684ee814f799a38e9f0addbfa534d146cfe0f 100644 (file)
@@ -14,6 +14,8 @@
 
 #include <sys/io.h>
 
+#include "i386-io-access.h"
+
 static inline int
 intel_setup_io(struct pci_access *a UNUSED)
 {
index e6bb9b6e1187b9f9b98d7eb05622e2ca5c278496..317f0794b7f5cfa85f56998d92200e63f0ff436b 100644 (file)
@@ -11,6 +11,8 @@
 #include <sys/io.h>
 #include <errno.h>
 
+#include "i386-io-access.h"
+
 static int ioperm_enabled;
 static int iopl_enabled;
 
index 86948d9316c8063e406f9a7fedb4193ee9efa548..99fd5766a01e691665227a085f773c550ab30676 100644 (file)
@@ -12,6 +12,8 @@
 #include <sys/sysi86.h>
 #include <sys/psw.h>
 
+#include "i386-io-access.h"
+
 static int
 intel_setup_io(struct pci_access *a UNUSED)
 {
@@ -24,48 +26,6 @@ intel_cleanup_io(struct pci_access *a UNUSED)
   /* FIXME: How to switch off I/O port access? */
 }
 
-static inline u8
-inb (u16 port)
-{
-  u8 v;
-  __asm__ __volatile__ ("inb (%w1)":"=a" (v):"Nd" (port));
-  return v;
-}
-
-static inline u16
-inw (u16 port)
-{
-  u16 v;
-  __asm__ __volatile__ ("inw (%w1)":"=a" (v):"Nd" (port));
-  return v;
-}
-
-static inline u32
-inl (u16 port)
-{
-  u32 v;
-  __asm__ __volatile__ ("inl (%w1)":"=a" (v):"Nd" (port));
-  return v;
-}
-
-static inline void
-outb (u8 value, u16 port)
-{
-  __asm__ __volatile__ ("outb (%w1)": :"a" (value), "Nd" (port));
-}
-
-static inline void
-outw (u16 value, u16 port)
-{
-  __asm__ __volatile__ ("outw (%w1)": :"a" (value), "Nd" (port));
-}
-
-static inline void
-outl (u32 value, u16 port)
-{
-  __asm__ __volatile__ ("outl (%w1)": :"a" (value), "Nd" (port));
-}
-
 static inline void intel_io_lock(void)
 {
 }
index fd1a54e24b73e2a4664787936d75557d2eb2064d..ef011b16fa2b15958a004363528e7015cee0861a 100644 (file)
  *     SPDX-License-Identifier: GPL-2.0-or-later
  */
 
-#include <io.h>
 #include <windows.h>
 #include <aclapi.h>
 
-#ifdef _MSC_VER
-/* MSVC compiler provides I/O port intrinsics for both 32 and 64-bit modes. */
-#pragma intrinsic(_outp)
-#pragma intrinsic(_outpw)
-#pragma intrinsic(_outpd)
-#pragma intrinsic(_inp)
-#pragma intrinsic(_inpw)
-#pragma intrinsic(_inpd)
-#elif defined(_WIN64) || defined(_UCRT)
-/*
- * For other compilers I/O port intrinsics are available in <intrin.h> header
- * file either as inline/external functions or macros. Beware that <intrin.h>
- * names are different than MSVC intrinsics names and glibc function names.
- * Usage of <intrin.h> is also the prefered way for 64-bit mode or when using
- * new UCRT library.
- */
-#include <intrin.h>
-#define _outp(x,y) __outbyte(x,y)
-#define _outpw(x,y) __outword(x,y)
-#define _outpd(x,y) __outdword(x,y)
-#define _inp(x) __inbyte(x)
-#define _inpw(x) __inword(x)
-#define _inpd(x) __indword(x)
-#elif defined(__CRTDLL__) || (defined(__MSVCRT_VERSION__) && __MSVCRT_VERSION__ < 0x400)
-/*
- * Old 32-bit CRTDLL library and pre-4.00 MSVCRT library do not provide I/O
- * port functions. As these libraries exist only in 32-bit mode variant,
- * implement I/O port functions via 32-bit inline assembly.
- */
-static inline int _outp(unsigned short port, int databyte)
-{
-  asm volatile ("outb %b0, %w1" : : "a" (databyte), "Nd" (port));
-  return databyte;
-}
-static inline unsigned short _outpw(unsigned short port, unsigned short dataword)
-{
-  asm volatile ("outw %w0, %w1" : : "a" (dataword), "Nd" (port));
-  return dataword;
-}
-static inline unsigned long _outpd(unsigned short port, unsigned long dataword)
-{
-  asm volatile ("outl %0, %w1" : : "a" (dataword), "Nd" (port));
-  return dataword;
-}
-static inline int _inp(unsigned short port)
-{
-  unsigned char ret;
-  asm volatile ("inb %w1, %0" : "=a" (ret) : "Nd" (port));
-  return ret;
-}
-static inline unsigned short _inpw(unsigned short port)
-{
-  unsigned short ret;
-  asm volatile ("inw %w1, %0" : "=a" (ret) : "Nd" (port));
-  return ret;
-}
-static inline unsigned long _inpd(unsigned short port)
-{
-  unsigned long ret;
-  asm volatile ("inl %w1, %0" : "=a" (ret) : "Nd" (port));
-  return ret;
-}
-#elif !defined(__GNUC__)
-/*
- * Old 32-bit MSVCRT (non-UCRT) library provides I/O port functions. Function
- * prototypes are defined in <conio.h> header file but they are missing in
- * some MinGW toolchains. So for GCC compiler define them manually.
- */
-#include <conio.h>
-#else
-int _outp(unsigned short port, int databyte);
-unsigned short _outpw(unsigned short port, unsigned short dataword);
-unsigned long _outpd(unsigned short port, unsigned long dataword);
-int _inp(unsigned short port);
-unsigned short _inpw(unsigned short port);
-unsigned long _inpd(unsigned short port);
-#endif
-
-#define outb(x,y) _outp(y,x)
-#define outw(x,y) _outpw(y,x)
-#define outl(x,y) _outpd(y,x)
-
-#define inb(x) _inp(x)
-#define inw(x) _inpw(x)
-#define inl(x) _inpd(x)
+#include "i386-io-access.h"
 
 /*
  * Define __readeflags() for MSVC and GCC compilers.
index 1e2c4028e08dca240bee387c9b0d0e443cf76f5f..687dd48451f2b6fd04cab03cc40a4bd16efd7135 100644 (file)
@@ -116,12 +116,12 @@ conf1_detect(struct pci_access *a)
     }
 
   intel_io_lock();
-  outb (0x01, 0xCFB);
-  tmp = inl (0xCF8);
-  outl (0x80000000, 0xCF8);
-  if (inl (0xCF8) == 0x80000000)
+  intel_outb (0x01, 0xCFB);
+  tmp = intel_inl (0xCF8);
+  intel_outl (0x80000000, 0xCF8);
+  if (intel_inl (0xCF8) == 0x80000000)
     res = 1;
-  outl (tmp, 0xCF8);
+  intel_outl (tmp, 0xCF8);
   intel_io_unlock();
 
   if (res)
@@ -142,18 +142,18 @@ conf1_read(struct pci_dev *d, int pos, byte *buf, int len)
     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);
+  intel_outl(0x80000000 | ((d->bus & 0xff) << 16) | (PCI_DEVFN(d->dev, d->func) << 8) | (pos&~3), 0xcf8);
 
   switch (len)
     {
     case 1:
-      buf[0] = inb(addr);
+      buf[0] = intel_inb(addr);
       break;
     case 2:
-      ((u16 *) buf)[0] = cpu_to_le16(inw(addr));
+      ((u16 *) buf)[0] = cpu_to_le16(intel_inw(addr));
       break;
     case 4:
-      ((u32 *) buf)[0] = cpu_to_le32(inl(addr));
+      ((u32 *) buf)[0] = cpu_to_le32(intel_inl(addr));
       break;
     }
 
@@ -174,18 +174,18 @@ conf1_write(struct pci_dev *d, int pos, byte *buf, int len)
     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);
+  intel_outl(0x80000000 | ((d->bus & 0xff) << 16) | (PCI_DEVFN(d->dev, d->func) << 8) | (pos&~3), 0xcf8);
 
   switch (len)
     {
     case 1:
-      outb(buf[0], addr);
+      intel_outb(buf[0], addr);
       break;
     case 2:
-      outw(le16_to_cpu(((u16 *) buf)[0]), addr);
+      intel_outw(le16_to_cpu(((u16 *) buf)[0]), addr);
       break;
     case 4:
-      outl(le32_to_cpu(((u32 *) buf)[0]), addr);
+      intel_outl(le32_to_cpu(((u32 *) buf)[0]), addr);
       break;
     }
   intel_io_unlock();
@@ -210,10 +210,10 @@ conf2_detect(struct pci_access *a)
   /* This is ugly and tends to produce false positives. Beware. */
 
   intel_io_lock();
-  outb(0x00, 0xCFB);
-  outb(0x00, 0xCF8);
-  outb(0x00, 0xCFA);
-  if (inb(0xCF8) == 0x00 && inb(0xCFA) == 0x00)
+  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;
@@ -236,21 +236,21 @@ conf2_read(struct pci_dev *d, int pos, byte *buf, int len)
     return pci_generic_block_read(d, pos, buf, len);
 
   intel_io_lock();
-  outb((d->func << 1) | 0xf0, 0xcf8);
-  outb(d->bus, 0xcfa);
+  intel_outb((d->func << 1) | 0xf0, 0xcf8);
+  intel_outb(d->bus, 0xcfa);
   switch (len)
     {
     case 1:
-      buf[0] = inb(addr);
+      buf[0] = intel_inb(addr);
       break;
     case 2:
-      ((u16 *) buf)[0] = cpu_to_le16(inw(addr));
+      ((u16 *) buf)[0] = cpu_to_le16(intel_inw(addr));
       break;
     case 4:
-      ((u32 *) buf)[0] = cpu_to_le32(inl(addr));
+      ((u32 *) buf)[0] = cpu_to_le32(intel_inl(addr));
       break;
     }
-  outb(0, 0xcf8);
+  intel_outb(0, 0xcf8);
   intel_io_unlock();
   return res;
 }
@@ -272,22 +272,22 @@ conf2_write(struct pci_dev *d, int pos, byte *buf, int len)
     return pci_generic_block_write(d, pos, buf, len);
 
   intel_io_lock();
-  outb((d->func << 1) | 0xf0, 0xcf8);
-  outb(d->bus, 0xcfa);
+  intel_outb((d->func << 1) | 0xf0, 0xcf8);
+  intel_outb(d->bus, 0xcfa);
   switch (len)
     {
     case 1:
-      outb(buf[0], addr);
+      intel_outb(buf[0], addr);
       break;
     case 2:
-      outw(le16_to_cpu(* (u16 *) buf), addr);
+      intel_outw(le16_to_cpu(* (u16 *) buf), addr);
       break;
     case 4:
-      outl(le32_to_cpu(* (u32 *) buf), addr);
+      intel_outl(le32_to_cpu(* (u32 *) buf), addr);
       break;
     }
 
-  outb(0, 0xcf8);
+  intel_outb(0, 0xcf8);
   intel_io_unlock();
   return res;
 }