[ioapi] Formalise the I/O API as used in i386-pcbios
authorMichael Brown <mcb30@etherboot.org>
Sun, 12 Oct 2008 00:03:17 +0000 (01:03 +0100)
committerMichael Brown <mcb30@etherboot.org>
Sun, 12 Oct 2008 00:03:17 +0000 (01:03 +0100)
src/arch/i386/core/x86_io.c [new file with mode: 0644]
src/arch/i386/include/bits/io.h [new file with mode: 0644]
src/arch/i386/include/gpxe/x86_io.h [new file with mode: 0644]
src/config/defaults.h [new file with mode: 0644]
src/config/defaults/pcbios.h [new file with mode: 0644]
src/config/ioapi.h [new file with mode: 0644]
src/include/gpxe/api.h [new file with mode: 0644]
src/include/gpxe/io.h [new file with mode: 0644]

diff --git a/src/arch/i386/core/x86_io.c b/src/arch/i386/core/x86_io.c
new file mode 100644 (file)
index 0000000..926f1d6
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <gpxe/io.h>
+#include <gpxe/x86_io.h>
+
+/** @file
+ *
+ * gPXE I/O API for x86
+ *
+ */
+
+/**
+ * Read 64-bit qword from memory-mapped device
+ *
+ * @v io_addr          I/O address
+ * @ret data           Value read
+ *
+ * This routine uses MMX instructions.
+ */
+static uint64_t x86_readq ( volatile uint64_t *io_addr ) {
+       uint64_t data;
+        __asm__ __volatile__ ( "pushl %%edx\n\t"
+                              "pushl %%eax\n\t"
+                              "movq (%1), %%mm0\n\t"
+                              "movq %%mm0, (%%esp)\n\t"
+                              "popl %%eax\n\t"
+                              "popl %%edx\n\t"
+                              "emms\n\t"
+                               : "=A" ( data ) : "r" ( io_addr ) );
+       return data;
+}
+
+/**
+ * Write 64-bit qword to memory-mapped device
+ *
+ * @v data             Value to write
+ * @v io_addr          I/O address
+ *
+ * This routine uses MMX instructions.
+ */
+static void x86_writeq ( uint64_t data, volatile uint64_t *io_addr ) {
+       __asm__ __volatile__ ( "pushl %%edx\n\t"
+                              "pushl %%eax\n\t"
+                              "movq (%%esp), %%mm0\n\t"
+                              "movq %%mm0, (%1)\n\t"
+                              "popl %%eax\n\t"
+                              "popl %%edx\n\t"
+                              "emms\n\t"
+                              : : "A" ( data ), "r" ( io_addr ) );
+}
+
+PROVIDE_IOAPI_INLINE ( x86, iounmap );
+PROVIDE_IOAPI_INLINE ( x86, io_to_bus );
+PROVIDE_IOAPI_INLINE ( x86, virt_to_bus );
+PROVIDE_IOAPI_INLINE ( x86, bus_to_virt );
+PROVIDE_IOAPI_INLINE ( x86, readb );
+PROVIDE_IOAPI_INLINE ( x86, readw );
+PROVIDE_IOAPI_INLINE ( x86, readl );
+PROVIDE_IOAPI ( x86, readq, x86_readq );
+PROVIDE_IOAPI_INLINE ( x86, writeb );
+PROVIDE_IOAPI_INLINE ( x86, writew );
+PROVIDE_IOAPI_INLINE ( x86, writel );
+PROVIDE_IOAPI ( x86, writeq, x86_writeq );
+PROVIDE_IOAPI_INLINE ( x86, inb );
+PROVIDE_IOAPI_INLINE ( x86, inw );
+PROVIDE_IOAPI_INLINE ( x86, inl );
+PROVIDE_IOAPI_INLINE ( x86, outb );
+PROVIDE_IOAPI_INLINE ( x86, outw );
+PROVIDE_IOAPI_INLINE ( x86, outl );
+PROVIDE_IOAPI_INLINE ( x86, insb );
+PROVIDE_IOAPI_INLINE ( x86, insw );
+PROVIDE_IOAPI_INLINE ( x86, insl );
+PROVIDE_IOAPI_INLINE ( x86, outsb );
+PROVIDE_IOAPI_INLINE ( x86, outsw );
+PROVIDE_IOAPI_INLINE ( x86, outsl );
+PROVIDE_IOAPI_INLINE ( x86, iodelay );
+PROVIDE_IOAPI_INLINE ( x86, mb );
diff --git a/src/arch/i386/include/bits/io.h b/src/arch/i386/include/bits/io.h
new file mode 100644 (file)
index 0000000..dd0ee44
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef _BITS_IO_H
+#define _BITS_IO_H
+
+/** @file
+ *
+ * i386-specific I/O API implementations
+ *
+ */
+
+#include <gpxe/x86_io.h>
+
+#endif /* _BITS_IO_H */
diff --git a/src/arch/i386/include/gpxe/x86_io.h b/src/arch/i386/include/gpxe/x86_io.h
new file mode 100644 (file)
index 0000000..3a907f8
--- /dev/null
@@ -0,0 +1,148 @@
+#ifndef _GPXE_X86_IO_H
+#define _GPXE_X86_IO_H
+
+/** @file
+ *
+ * gPXE I/O API for x86
+ *
+ * i386 uses direct pointer dereferences for accesses to memory-mapped
+ * I/O space, and the inX/outX instructions for accesses to
+ * port-mapped I/O space.
+ *
+ * 64-bit atomic accesses (readq() and writeq()) use MMX instructions,
+ * and will crash original Pentium and earlier CPUs.  Fortunately, no
+ * hardware that requires atomic 64-bit accesses will physically fit
+ * into a machine with such an old CPU anyway.
+ */
+
+#include <virtaddr.h>
+
+#ifdef IOAPI_X86
+#define IOAPI_PREFIX_x86
+#else
+#define IOAPI_PREFIX_x86 __x86_
+#endif
+
+/*
+ * Memory space mappings
+ *
+ */
+
+static inline __always_inline void *
+IOAPI_INLINE ( x86, ioremap ) ( unsigned long bus_addr, size_t len __unused ) {
+       return phys_to_virt ( bus_addr );
+}
+
+static inline __always_inline void
+IOAPI_INLINE ( x86, iounmap ) ( volatile const void *io_addr __unused ) {
+       /* Nothing to do */
+}
+
+static inline __always_inline unsigned long
+IOAPI_INLINE ( x86, io_to_bus ) ( volatile const void *io_addr ) {
+       return virt_to_phys ( io_addr );
+}
+
+static inline __always_inline unsigned long
+IOAPI_INLINE ( x86, virt_to_bus ) ( volatile const void *addr ) {
+       return virt_to_phys ( addr );
+}
+
+static inline __always_inline void *
+IOAPI_INLINE ( x86, bus_to_virt ) ( unsigned long bus_addr ) {
+       return phys_to_virt ( bus_addr );
+}
+
+/*
+ * MMIO reads and writes up to 32 bits
+ *
+ */
+
+#define X86_READX( _api_func, _type )                                        \
+static inline __always_inline _type                                          \
+IOAPI_INLINE ( x86, _api_func ) ( volatile _type *io_addr ) {                \
+       return *io_addr;                                                      \
+}
+X86_READX ( readb, uint8_t );
+X86_READX ( readw, uint16_t );
+X86_READX ( readl, uint32_t );
+
+#define X86_WRITEX( _api_func, _type )                                       \
+static inline __always_inline void                                           \
+IOAPI_INLINE ( x86, _api_func ) ( _type data,                                \
+                                 volatile _type *io_addr ) {                 \
+       *io_addr = data;                                                      \
+}
+X86_WRITEX ( writeb, uint8_t );
+X86_WRITEX ( writew, uint16_t );
+X86_WRITEX ( writel, uint32_t );
+
+/*
+ * PIO reads and writes up to 32 bits
+ *
+ */
+
+#define X86_INX( _insn_suffix, _type, _reg_prefix )                          \
+static inline __always_inline _type                                          \
+IOAPI_INLINE ( x86, in ## _insn_suffix ) ( volatile _type *io_addr ) {       \
+       _type data;                                                           \
+       __asm__ __volatile__ ( "in" #_insn_suffix " %w1, %" _reg_prefix "0"   \
+                              : "=a" ( data ) : "Nd" ( io_addr ) );          \
+       return data;                                                          \
+}                                                                            \
+static inline __always_inline void                                           \
+IOAPI_INLINE ( x86, ins ## _insn_suffix ) ( volatile _type *io_addr,         \
+                                           _type *data,                      \
+                                           unsigned int count ) {            \
+       unsigned int discard_D;                                               \
+       __asm__ __volatile__ ( "rep ins" #_insn_suffix                        \
+                              : "=D" ( discard_D )                           \
+                              : "d" ( io_addr ), "c" ( count ),              \
+                                "0" ( data ) );                              \
+}
+X86_INX ( b, uint8_t, "b" );
+X86_INX ( w, uint16_t, "w" );
+X86_INX ( l, uint32_t, "k" );
+
+#define X86_OUTX( _insn_suffix, _type, _reg_prefix )                         \
+static inline __always_inline void                                           \
+IOAPI_INLINE ( x86, out ## _insn_suffix ) ( _type data,                              \
+                                           volatile _type *io_addr ) {       \
+       __asm__ __volatile__ ( "out" #_insn_suffix " %" _reg_prefix "0, %w1"  \
+                              : : "a" ( data ), "Nd" ( io_addr ) );          \
+}                                                                            \
+static inline __always_inline void                                           \
+IOAPI_INLINE ( x86, outs ## _insn_suffix ) ( volatile _type *io_addr,        \
+                                            const _type *data,               \
+                                            unsigned int count ) {           \
+       unsigned int discard_D;                                               \
+       __asm__ __volatile__ ( "rep outs" #_insn_suffix                       \
+                              : "=D" ( discard_D )                           \
+                              : "d" ( io_addr ), "c" ( count ),              \
+                                "0" ( data ) );                              \
+}
+X86_OUTX ( b, uint8_t, "b" );
+X86_OUTX ( w, uint16_t, "w" );
+X86_OUTX ( l, uint32_t, "k" );
+
+/*
+ * Slow down I/O
+ *
+ */
+
+static inline __always_inline void
+IOAPI_INLINE ( x86, iodelay ) ( void ) {
+       __asm__ __volatile__ ( "outb %al, $0x80" );
+}
+
+/*
+ * Memory barrier
+ *
+ */
+
+static inline __always_inline void
+IOAPI_INLINE ( x86, mb ) ( void ) {
+       __asm__ __volatile__ ( "lock; addl $0, 0(%%esp)" : : : "memory" );
+}
+
+#endif /* _GPXE_X86_IO_H */
diff --git a/src/config/defaults.h b/src/config/defaults.h
new file mode 100644 (file)
index 0000000..1f55ef3
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef CONFIG_DEFAULTS_H
+#define CONFIG_DEFAULTS_H
+
+#define CONFIG_DEFAULTS(_platform) <config/defaults/_platform.h>
+
+#include CONFIG_DEFAULTS(PLATFORM)
+
+#endif /* CONFIG_DEFAULTS_H */
diff --git a/src/config/defaults/pcbios.h b/src/config/defaults/pcbios.h
new file mode 100644 (file)
index 0000000..f1a48bc
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef CONFIG_DEFAULTS_PCBIOS_H
+#define CONFIG_DEFAULTS_PCBIOS_H
+
+/** @file
+ *
+ * Configuration defaults for PCBIOS
+ *
+ */
+
+#define IOAPI_X86
+
+#endif /* CONFIG_DEFAULTS_PCBIOS_H */
diff --git a/src/config/ioapi.h b/src/config/ioapi.h
new file mode 100644 (file)
index 0000000..28c4f7b
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef CONFIG_IOAPI_H
+#define CONFIG_IOAPI_H
+
+/** @file
+ *
+ * I/O API configuration
+ *
+ */
+
+#include <config/defaults.h>
+
+#endif /* CONFIG_IOAPI_H */
diff --git a/src/include/gpxe/api.h b/src/include/gpxe/api.h
new file mode 100644 (file)
index 0000000..df5d1ae
--- /dev/null
@@ -0,0 +1,82 @@
+#ifndef _GPXE_API_H
+#define _GPXE_API_H
+
+/** @file
+ *
+ * gPXE internal APIs
+ *
+ * There are various formally-defined APIs internal to gPXE, with
+ * several differing implementations specific to particular execution
+ * environments (e.g. PC BIOS, EFI, LinuxBIOS).
+ *
+ */
+
+/** @defgroup Single-implementation APIs
+ *
+ * These are APIs for which only a single implementation may be
+ * compiled in at any given time.
+ *
+ * @{
+ */
+
+/**
+ * Calculate function implementation name
+ *
+ * @v _prefix          Subsystem prefix
+ * @v _api_func                API function
+ * @ret _subsys_func   Subsystem API function
+ *
+ * The subsystem prefix should be an empty string for the currently
+ * selected subsystem, and should be a subsystem-unique string for all
+ * other subsystems.
+ */
+#define SINGLE_API_NAME( _prefix, _api_func ) _prefix ## _api_func
+
+/**
+ * Calculate static inline function name
+ *
+ * @v _prefix          Subsystem prefix
+ * @v _api_func                API function
+ * @ret _subsys_func   Subsystem API function
+ */
+#define SINGLE_API_INLINE( _prefix, _api_func )        \
+       SINGLE_API_NAME ( _prefix, _api_func )
+
+/**
+ * Provide an API implementation
+ *
+ * @v _prefix          Subsystem prefix
+ * @v _api_func                API function
+ * @v _func            Implementing function
+ */
+#define PROVIDE_SINGLE_API( _prefix, _api_func, _func )                              \
+       /* Ensure that _api_func exists */                                    \
+       typeof ( _api_func ) _api_func;                                       \
+       /* Ensure that _func exists */                                        \
+       typeof ( _func ) _func;                                               \
+       /* Ensure that _func is type-compatible with _api_func */             \
+       typeof ( _api_func ) _func;                                           \
+       /* Ensure that _subsys_func is non-static */                          \
+       extern typeof ( _api_func ) SINGLE_API_NAME ( _prefix, _api_func );   \
+       /* Provide symbol alias from _subsys_func to _func */                 \
+       typeof ( _api_func ) SINGLE_API_NAME ( _prefix, _api_func )           \
+               __attribute__ (( alias ( #_func ) ));
+
+/**
+ * Provide a static inline API implementation
+ *
+ * @v _prefix          Subsystem prefix
+ * @v _api_func                API function
+ */
+#define PROVIDE_SINGLE_API_INLINE( _prefix, _api_func )                              \
+       /* Ensure that _api_func exists */                                    \
+       typeof ( _api_func ) _api_func;                                       \
+       /* Ensure that _subsys_func exists and is static */                   \
+       static typeof ( SINGLE_API_INLINE ( _prefix, _api_func ) )            \
+               SINGLE_API_INLINE ( _prefix, _api_func );                     \
+       /* Ensure that _subsys_func is type-compatible with _api_func */      \
+       typeof ( _api_func ) SINGLE_API_INLINE ( _prefix, _api_func );
+
+/** @} */
+
+#endif /* _GPXE_API_H */
diff --git a/src/include/gpxe/io.h b/src/include/gpxe/io.h
new file mode 100644 (file)
index 0000000..58755e6
--- /dev/null
@@ -0,0 +1,483 @@
+#ifndef _GPXE_IO_H
+#define _GPXE_IO_H
+
+/** @file
+ *
+ * gPXE I/O API
+ *
+ * The I/O API provides methods for reading from and writing to
+ * memory-mapped and I/O-mapped devices.
+ *
+ * The standard methods (readl()/writel() etc.) do not strictly check
+ * the type of the address parameter; this is because traditional
+ * usage does not necessarily provide the correct pointer type.  For
+ * example, code written for ISA devices at fixed I/O addresses (such
+ * as the keyboard controller) tend to use plain integer constants for
+ * the address parameter.
+ */
+
+#include <stdint.h>
+#include <gpxe/api.h>
+#include <config/ioapi.h>
+
+/**
+ * Calculate static inline I/O API function name
+ *
+ * @v _prefix          Subsystem prefix
+ * @v _api_func                API function
+ * @ret _subsys_func   Subsystem API function
+ */
+#define IOAPI_INLINE( _subsys, _api_func ) \
+       SINGLE_API_INLINE ( IOAPI_PREFIX_ ## _subsys, _api_func )
+
+/**
+ * Provide an I/O API implementation
+ *
+ * @v _prefix          Subsystem prefix
+ * @v _api_func                API function
+ * @v _func            Implementing function
+ */
+#define PROVIDE_IOAPI( _subsys, _api_func, _func ) \
+       PROVIDE_SINGLE_API ( IOAPI_PREFIX_ ## _subsys, _api_func, _func )
+
+/**
+ * Provide a static inline I/O API implementation
+ *
+ * @v _prefix          Subsystem prefix
+ * @v _api_func                API function
+ */
+#define PROVIDE_IOAPI_INLINE( _subsys, _api_func ) \
+       PROVIDE_SINGLE_API_INLINE ( IOAPI_PREFIX_ ## _subsys, _api_func )
+
+/* Include all architecture-independent I/O API headers */
+
+/* Include all architecture-dependent I/O API headers */
+#include <bits/io.h>
+
+/**
+ * Wrap an I/O read
+ *
+ * @v _func            I/O API function
+ * @v _type            Data type
+ * @v io_addr          I/O address
+ * @v _prefix          Prefix for address in debug message
+ * @v _ndigits         Number of hex digits for this data type
+ */
+#define IOAPI_READ( _func, _type, io_addr, _prefix, _ndigits ) ( {           \
+       volatile _type *_io_addr =                                            \
+               ( ( volatile _type * ) ( intptr_t ) (io_addr) );              \
+       _type _data = _func ( _io_addr );                                     \
+       DBGIO ( "[" _prefix " %08lx] => %0" #_ndigits "llx\n",                \
+               io_to_bus ( _io_addr ), ( unsigned long long ) _data );       \
+       _data; } )
+
+/**
+ * Wrap an I/O write
+ *
+ * @v _func            I/O API function
+ * @v _type            Data type
+ * @v data             Value to write
+ * @v io_addr          I/O address
+ * @v _prefix          Prefix for address in debug message
+ * @v _ndigits         Number of hex digits for this data type
+ */
+#define IOAPI_WRITE( _func, _type, data, io_addr, _prefix, _ndigits ) do {    \
+       volatile _type *_io_addr =                                            \
+               ( ( volatile _type * ) ( intptr_t ) (io_addr) );              \
+       _type _data = (data);                                                 \
+       DBGIO ( "[" _prefix " %08lx] <= %0" #_ndigits "llx\n",                \
+               io_to_bus ( _io_addr ), ( unsigned long long ) _data );       \
+       _func ( _data, _io_addr );                                            \
+       } while ( 0 )
+
+/**
+ * Wrap an I/O string read
+ *
+ * @v _func            I/O API function
+ * @v _type            Data type
+ * @v io_addr          I/O address
+ * @v data             Data buffer
+ * @v count            Number of elements to read
+ * @v _prefix          Prefix for address in debug message
+ * @v _ndigits         Number of hex digits for this data type
+ */
+#define IOAPI_READS( _func, _type, io_addr, data, count, _prefix, _ndigits )  \
+       do {                                                                  \
+       volatile _type *_io_addr =                                            \
+               ( ( volatile _type * ) ( intptr_t ) (io_addr) );              \
+       void *_data_void = (data); /* Check data is a pointer */              \
+       _type * _data = ( ( _type * ) _data_void );                           \
+       const _type * _dbg_data = _data;                                      \
+       unsigned int _count = (count);                                        \
+       unsigned int _dbg_count = _count;                                     \
+       _func ( _io_addr, _data, _count );                                    \
+       DBGIO ( "[" _prefix " %08lx] =>", io_to_bus ( _io_addr ) );           \
+       while ( _dbg_count-- ) {                                              \
+               DBGIO ( " %0" #_ndigits "llx",                                \
+                       ( ( unsigned long long ) *(_dbg_data++) ) );          \
+       }                                                                     \
+       DBGIO ( "\n" );                                                       \
+       } while ( 0 )
+
+/**
+ * Wrap an I/O string write
+ *
+ * @v _func            I/O API function
+ * @v _type            Data type
+ * @v io_addr          I/O address
+ * @v data             Data buffer
+ * @v count            Number of elements to write
+ * @v _prefix          Prefix for address in debug message
+ * @v _ndigits         Number of hex digits for this data type
+ */
+#define IOAPI_WRITES( _func, _type, io_addr, data, count, _prefix, _ndigits ) \
+       do {                                                                  \
+       volatile _type *_io_addr =                                            \
+               ( ( volatile _type * ) ( intptr_t ) (io_addr) );              \
+       const void *_data_void = (data); /* Check data is a pointer */        \
+       const _type * _data = ( ( const _type * ) _data_void );               \
+       const _type * _dbg_data = _data;                                      \
+       unsigned int _count = (count);                                        \
+       unsigned int _dbg_count = _count;                                     \
+       DBGIO ( "[" _prefix " %08lx] <=", io_to_bus ( _io_addr ) );           \
+       while ( _dbg_count-- ) {                                              \
+               DBGIO ( " %0" #_ndigits "llx",                                \
+                       ( ( unsigned long long ) *(_dbg_data++) ) );          \
+       }                                                                     \
+       DBGIO ( "\n" );                                                       \
+       _func ( _io_addr, _data, _count );                                    \
+       } while ( 0 )
+
+/**
+ * Map bus address as an I/O address
+ *
+ * @v bus_addr         Bus address
+ * @v len              Length of region
+ * @ret io_addr                I/O address
+ */
+void * ioremap ( unsigned long bus_addr, size_t len );
+
+/**
+ * Unmap I/O address
+ *
+ * @v io_addr          I/O address
+ */
+void iounmap ( volatile const void *io_addr );
+
+/**
+ * Convert I/O address to bus address (for debug only)
+ *
+ * @v io_addr          I/O address
+ * @ret bus_addr       Bus address
+ */
+unsigned long io_to_bus ( volatile const void *io_addr );
+
+/**
+ * Convert virtual address to a bus address
+ *
+ * @v addr             Virtual address
+ * @ret bus_addr       Bus address
+ */
+unsigned long virt_to_bus ( volatile const void *addr );
+
+/**
+ * Convert bus address to a virtual address
+ *
+ * @v bus_addr         Bus address
+ * @ret addr           Virtual address
+ *
+ * This operation isn't actually valid within our memory model, and is
+ * impossible to achieve under -DKEEP_IT_REAL.  Some drivers haven't
+ * been updated to avoid it yet, though.
+ */
+void * bus_to_virt ( unsigned long bus_addr );
+
+/**
+ * Read byte from memory-mapped device
+ *
+ * @v io_addr          I/O address
+ * @ret data           Value read
+ */
+uint8_t readb ( volatile uint8_t *io_addr );
+#define readb( io_addr ) IOAPI_READ ( readb, uint8_t, io_addr, "MEM", 2 )
+
+/**
+ * Read 16-bit word from memory-mapped device
+ *
+ * @v io_addr          I/O address
+ * @ret data           Value read
+ */
+uint16_t readw ( volatile uint16_t *io_addr );
+#define readw( io_addr ) IOAPI_READ ( readw, uint16_t, io_addr, "MEM", 4 )
+
+/**
+ * Read 32-bit dword from memory-mapped device
+ *
+ * @v io_addr          I/O address
+ * @ret data           Value read
+ */
+uint32_t readl ( volatile uint32_t *io_addr );
+#define readl( io_addr ) IOAPI_READ ( readl, uint32_t, io_addr, "MEM", 8 )
+
+/**
+ * Read 64-bit qword from memory-mapped device
+ *
+ * @v io_addr          I/O address
+ * @ret data           Value read
+ */
+uint64_t readq ( volatile uint64_t *io_addr );
+#define readq( io_addr ) IOAPI_READ ( readq, uint64_t, io_addr, "MEM", 16 )
+
+/**
+ * Write byte to memory-mapped device
+ *
+ * @v data             Value to write
+ * @v io_addr          I/O address
+ */
+void writeb ( uint8_t data, volatile uint8_t *io_addr );
+#define writeb( data, io_addr ) \
+       IOAPI_WRITE ( writeb, uint8_t, data, io_addr, "MEM", 2 )
+
+/**
+ * Write 16-bit word to memory-mapped device
+ *
+ * @v data             Value to write
+ * @v io_addr          I/O address
+ */
+void writew ( uint16_t data, volatile uint16_t *io_addr );
+#define writew( data, io_addr ) \
+       IOAPI_WRITE ( writew, uint16_t, data, io_addr, "MEM", 4 )
+
+/**
+ * Write 32-bit dword to memory-mapped device
+ *
+ * @v data             Value to write
+ * @v io_addr          I/O address
+ */
+void writel ( uint32_t data, volatile uint32_t *io_addr );
+#define writel( data, io_addr ) \
+       IOAPI_WRITE ( writel, uint32_t, data, io_addr, "MEM", 8 )
+
+/**
+ * Write 64-bit qword to memory-mapped device
+ *
+ * @v data             Value to write
+ * @v io_addr          I/O address
+ */
+void writeq ( uint64_t data, volatile uint64_t *io_addr );
+#define writeq( data, io_addr ) \
+       IOAPI_WRITE ( writeq, uint64_t, data, io_addr, "MEM", 16 )
+
+/**
+ * Read byte from I/O-mapped device
+ *
+ * @v io_addr          I/O address
+ * @ret data           Value read
+ */
+uint8_t inb ( volatile uint8_t *io_addr );
+#define inb( io_addr ) IOAPI_READ ( inb, uint8_t, io_addr, "IO", 2 )
+
+/**
+ * Read 16-bit word from I/O-mapped device
+ *
+ * @v io_addr          I/O address
+ * @ret data           Value read
+ */
+uint16_t inw ( volatile uint16_t *io_addr );
+#define inw( io_addr ) IOAPI_READ ( inw, uint16_t, io_addr, "IO", 4 )
+
+/**
+ * Read 32-bit dword from I/O-mapped device
+ *
+ * @v io_addr          I/O address
+ * @ret data           Value read
+ */
+uint32_t inl ( volatile uint32_t *io_addr );
+#define inl( io_addr ) IOAPI_READ ( inl, uint32_t, io_addr, "IO", 8 )
+
+/**
+ * Write byte to I/O-mapped device
+ *
+ * @v data             Value to write
+ * @v io_addr          I/O address
+ */
+void outb ( uint8_t data, volatile uint8_t *io_addr );
+#define outb( data, io_addr ) \
+       IOAPI_WRITE ( outb, uint8_t, data, io_addr, "IO", 2 )
+
+/**
+ * Write 16-bit word to I/O-mapped device
+ *
+ * @v data             Value to write
+ * @v io_addr          I/O address
+ */
+void outw ( uint16_t data, volatile uint16_t *io_addr );
+#define outw( data, io_addr ) \
+       IOAPI_WRITE ( outw, uint16_t, data, io_addr, "IO", 4 )
+
+/**
+ * Write 32-bit dword to I/O-mapped device
+ *
+ * @v data             Value to write
+ * @v io_addr          I/O address
+ */
+void outl ( uint32_t data, volatile uint32_t *io_addr );
+#define outl( data, io_addr ) \
+       IOAPI_WRITE ( outl, uint32_t, data, io_addr, "IO", 8 )
+
+/**
+ * Read bytes from I/O-mapped device
+ *
+ * @v io_addr          I/O address
+ * @v data             Data buffer
+ * @v count            Number of bytes to read
+ */
+void insb ( volatile uint8_t *io_addr, uint8_t *data, unsigned int count );
+#define insb( io_addr, data, count ) \
+       IOAPI_READS ( insb, uint8_t, io_addr, data, count, "IO", 2 )
+
+/**
+ * Read 16-bit words from I/O-mapped device
+ *
+ * @v io_addr          I/O address
+ * @v data             Data buffer
+ * @v count            Number of words to read
+ */
+void insw ( volatile uint16_t *io_addr, uint16_t *data, unsigned int count );
+#define insw( io_addr, data, count ) \
+       IOAPI_READS ( insw, uint16_t, io_addr, data, count, "IO", 4 )
+
+/**
+ * Read 32-bit words from I/O-mapped device
+ *
+ * @v io_addr          I/O address
+ * @v data             Data buffer
+ * @v count            Number of words to read
+ */
+void insl ( volatile uint32_t *io_addr, uint32_t *data, unsigned int count );
+#define insl( io_addr, data, count ) \
+       IOAPI_READS ( insl, uint32_t, io_addr, data, count, "IO", 8 )
+
+/**
+ * Write bytes to I/O-mapped device
+ *
+ * @v io_addr          I/O address
+ * @v data             Data buffer
+ * @v count            Number of bytes to write
+ */
+void outsb ( volatile uint8_t *io_addr, const uint8_t *data,
+            unsigned int count );
+#define outsb( io_addr, data, count ) \
+       IOAPI_WRITES ( outsb, uint8_t, io_addr, data, count, "IO", 2 )
+
+/**
+ * Write 16-bit words to I/O-mapped device
+ *
+ * @v io_addr          I/O address
+ * @v data             Data buffer
+ * @v count            Number of words to write
+ */
+void outsw ( volatile uint16_t *io_addr, const uint16_t *data,
+            unsigned int count );
+#define outsw( io_addr, data, count ) \
+       IOAPI_WRITES ( outsw, uint16_t, io_addr, data, count, "IO", 4 )
+
+/**
+ * Write 32-bit words to I/O-mapped device
+ *
+ * @v io_addr          I/O address
+ * @v data             Data buffer
+ * @v count            Number of words to write
+ */
+void outsl ( volatile uint32_t *io_addr, const uint32_t *data,
+            unsigned int count );
+#define outsl( io_addr, data, count ) \
+       IOAPI_WRITES ( outsl, uint32_t, io_addr, data, count, "IO", 8 )
+
+/**
+ * Slow down I/O
+ *
+ */
+void iodelay ( void );
+
+/**
+ * Read value from I/O-mapped device, slowly
+ *
+ * @v _func            Function to use to read value
+ * @v data             Value to write
+ * @v io_addr          I/O address
+ */
+#define INX_P( _func, _type, io_addr ) ( {                                   \
+       _type _data = _func ( (io_addr) );                                    \
+       iodelay();                                                            \
+       _data; } )
+
+/**
+ * Read byte from I/O-mapped device
+ *
+ * @v io_addr          I/O address
+ * @ret data           Value read
+ */
+#define inb_p( io_addr ) INX_P ( inb, uint8_t, io_addr )
+
+/**
+ * Read 16-bit word from I/O-mapped device
+ *
+ * @v io_addr          I/O address
+ * @ret data           Value read
+ */
+#define inw_p( io_addr ) INX_P ( inw, uint16_t, io_addr )
+
+/**
+ * Read 32-bit dword from I/O-mapped device
+ *
+ * @v io_addr          I/O address
+ * @ret data           Value read
+ */
+#define inl_p( io_addr ) INX_P ( inl, uint32_t, io_addr )
+
+/**
+ * Write value to I/O-mapped device, slowly
+ *
+ * @v _func            Function to use to write value
+ * @v data             Value to write
+ * @v io_addr          I/O address
+ */
+#define OUTX_P( _func, data, io_addr ) do {                                  \
+       _func ( (data), (io_addr) );                                          \
+       iodelay();                                                            \
+       } while ( 0 )
+
+/**
+ * Write byte to I/O-mapped device, slowly
+ *
+ * @v data             Value to write
+ * @v io_addr          I/O address
+ */
+#define outb_p( data, io_addr ) OUTX_P ( outb, data, io_addr )
+
+/**
+ * Write 16-bit word to I/O-mapped device, slowly
+ *
+ * @v data             Value to write
+ * @v io_addr          I/O address
+ */
+#define outw_p( data, io_addr ) OUTX_P ( outw, data, io_addr )
+
+/**
+ * Write 32-bit dword to I/O-mapped device, slowly
+ *
+ * @v data             Value to write
+ * @v io_addr          I/O address
+ */
+#define outl_p( data, io_addr ) OUTX_P ( outl, data, io_addr )
+
+/**
+ * Memory barrier
+ *
+ */
+void mb ( void );
+#define rmb()  mb()
+#define wmb()  mb()
+
+#endif /* _GPXE_IO_H */