Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[ioapi] Formalise the I/O API as used in i386-pcbios
- Loading branch information
Michael Brown
committed
Oct 12, 2008
1 parent
b40b4f2
commit 8956a36
Showing
8 changed files
with
850 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 ); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 */ |
Oops, something went wrong.