diff --git a/src/arch/x86/core/x86_io.c b/src/arch/x86/core/x86_io.c index 6c6b6e1e7..270ed7bef 100644 --- a/src/arch/x86/core/x86_io.c +++ b/src/arch/x86/core/x86_io.c @@ -32,6 +32,69 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ +/** Threshold for port I/O-mapped addresses + * + * On x86, port I/O instructions (inb/outb/etc) can take only an 8-bit + * or 16-bit address (in %dx). All I/O ports must therefore have a + * value in the first 64kB of the address space. + * + * Virtual addresses below 64kB can never be MMIO addresses: + * + * - In the UEFI memory model and x86_64 BIOS memory model, virtual + * addresses below 64kB are identity-mapped to the corresponding + * physical address. Since the first 64kB of address space is + * always RAM, no MMIO device can exist within this region. + * + * - In the i386 BIOS memory model, virtual addresses below 64kB cover + * the iPXE binary itself (which starts at address zero). Since the + * size of .textdata can never realistically be below 64kB (not + * least since the heap alone is 512kB), and since iPXE is placed + * into RAM as a contiguous block, no MMIO device can exist within + * this region. + * + * We therefore know that any (virtual) address returned by ioremap() + * must be outside the first 64kB of the address space. We can + * therefore use this as a threshold to determine whether a given + * address is a port I/O address or an MMIO address. + */ +#define PIO_THRESHOLD 0x10000 + +/** + * Read from I/O-mapped or memory-mapped device + * + * @v io_addr I/O address + * @ret data Value read + */ +#define X86_IOREADX( _api_func, _suffix, _type ) \ +static _type x86_ ## _api_func ( volatile _type *io_addr ) { \ + if ( ( ( intptr_t ) io_addr ) < PIO_THRESHOLD ) { \ + return in ## _suffix ( io_addr ); \ + } else { \ + return read ## _suffix ( io_addr ); \ + } \ +} +X86_IOREADX ( ioread8, b, uint8_t ); +X86_IOREADX ( ioread16, w, uint16_t ); +X86_IOREADX ( ioread32, l, uint32_t ); + +/** + * Write to I/O-mapped or memory-mapped device + * + * @v data Value to write + * @v io_addr I/O address + */ +#define X86_IOWRITEX( _api_func, _suffix, _type ) \ +static void x86_ ## _api_func ( _type data, volatile _type *io_addr ) { \ + if ( ( ( intptr_t ) io_addr ) < PIO_THRESHOLD ) { \ + out ## _suffix ( data, io_addr ); \ + } else { \ + write ## _suffix ( data, io_addr ); \ + } \ +} +X86_IOWRITEX ( iowrite8, b, uint8_t ); +X86_IOWRITEX ( iowrite16, w, uint16_t ); +X86_IOWRITEX ( iowrite32, l, uint32_t ); + /** * Read 64-bit qword from memory-mapped device * @@ -101,3 +164,9 @@ PROVIDE_IOAPI_INLINE ( x86, writeq ); PROVIDE_IOAPI ( x86, readq, i386_readq ); PROVIDE_IOAPI ( x86, writeq, i386_writeq ); #endif +PROVIDE_IOAPI ( x86, ioread8, x86_ioread8 ); +PROVIDE_IOAPI ( x86, ioread16, x86_ioread16 ); +PROVIDE_IOAPI ( x86, ioread32, x86_ioread32 ); +PROVIDE_IOAPI ( x86, iowrite8, x86_iowrite8 ); +PROVIDE_IOAPI ( x86, iowrite16, x86_iowrite16 ); +PROVIDE_IOAPI ( x86, iowrite32, x86_iowrite32 ); diff --git a/src/include/ipxe/dummy_pio.h b/src/include/ipxe/dummy_pio.h index e7a4cabef..7c80cdf35 100644 --- a/src/include/ipxe/dummy_pio.h +++ b/src/include/ipxe/dummy_pio.h @@ -39,6 +39,19 @@ IOAPI_INLINE ( _prefix, outs ## _suffix ) ( volatile _type *io_addr __unused, \ /* Do nothing */ \ } +#define DUMMY_IOREADX( _prefix, _width, _suffix, _type ) \ +static inline __always_inline _type \ +IOAPI_INLINE ( _prefix, ioread ## _width ) ( volatile _type *io_addr ) { \ + return IOAPI_INLINE ( _prefix, read ## _suffix ) ( io_addr ); \ +} + +#define DUMMY_IOWRITEX( _prefix, _width, _suffix, _type ) \ +static inline __always_inline void \ +IOAPI_INLINE ( _prefix, iowrite ## _width ) ( _type data, \ + volatile _type *io_addr ) { \ + IOAPI_INLINE ( _prefix, write ## _suffix ) ( data, io_addr ); \ +} + #define DUMMY_IODELAY( _prefix ) \ static inline __always_inline void \ IOAPI_INLINE ( _prefix, iodelay ) ( void ) { \ @@ -52,6 +65,12 @@ IOAPI_INLINE ( _prefix, iodelay ) ( void ) { \ DUMMY_OUTX ( _prefix, b, uint8_t ); \ DUMMY_OUTX ( _prefix, w, uint16_t ); \ DUMMY_OUTX ( _prefix, l, uint32_t ); \ + DUMMY_IOREADX ( _prefix, 8, b, uint8_t ); \ + DUMMY_IOREADX ( _prefix, 16, w, uint16_t ); \ + DUMMY_IOREADX ( _prefix, 32, l, uint32_t ); \ + DUMMY_IOWRITEX ( _prefix, 8, b, uint8_t ); \ + DUMMY_IOWRITEX ( _prefix, 16, w, uint16_t ); \ + DUMMY_IOWRITEX ( _prefix, 32, l, uint32_t ); \ DUMMY_IODELAY ( _prefix ); #define PROVIDE_DUMMY_PIO( _prefix ) \ @@ -61,6 +80,12 @@ IOAPI_INLINE ( _prefix, iodelay ) ( void ) { \ PROVIDE_IOAPI_INLINE ( _prefix, outb ); \ PROVIDE_IOAPI_INLINE ( _prefix, outw ); \ PROVIDE_IOAPI_INLINE ( _prefix, outl ); \ + PROVIDE_IOAPI_INLINE ( _prefix, ioread8 ); \ + PROVIDE_IOAPI_INLINE ( _prefix, ioread16 ); \ + PROVIDE_IOAPI_INLINE ( _prefix, ioread32 ); \ + PROVIDE_IOAPI_INLINE ( _prefix, iowrite8 ); \ + PROVIDE_IOAPI_INLINE ( _prefix, iowrite16 ); \ + PROVIDE_IOAPI_INLINE ( _prefix, iowrite32 ); \ PROVIDE_IOAPI_INLINE ( _prefix, iodelay ); #endif /* _IPXE_DUMMY_PIO_H */ diff --git a/src/include/ipxe/io.h b/src/include/ipxe/io.h index 41ee48ffb..ee2b7e156 100644 --- a/src/include/ipxe/io.h +++ b/src/include/ipxe/io.h @@ -329,6 +329,66 @@ 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 byte from I/O-mapped or memory-mapped device + * + * @v io_addr I/O address + * @ret data Value read + */ +uint8_t ioread8 ( volatile uint8_t *io_addr ); +#define ioread8( io_addr ) \ + IOAPI_READ ( ioread8, uint8_t, io_addr, "IO/MEM", 2 ) + +/** + * Read 16-bit word from I/O-mapped or memory-mapped device + * + * @v io_addr I/O address + * @ret data Value read + */ +uint16_t ioread16 ( volatile uint16_t *io_addr ); +#define ioread16( io_addr ) \ + IOAPI_READ ( ioread16, uint16_t, io_addr, "IO/MEM", 4 ) + +/** + * Read 32-bit dword from I/O-mapped or memory-mapped device + * + * @v io_addr I/O address + * @ret data Value read + */ +uint32_t ioread32 ( volatile uint32_t *io_addr ); +#define ioread32( io_addr ) \ + IOAPI_READ ( ioread32, uint32_t, io_addr, "IO/MEM", 8 ) + +/** + * Write byte to I/O-mapped or memory-mapped device + * + * @v data Value to write + * @v io_addr I/O address + */ +void iowrite8 ( uint8_t data, volatile uint8_t *io_addr ); +#define iowrite8( data, io_addr ) \ + IOAPI_WRITE ( iowrite8, uint8_t, data, io_addr, "IO/MEM", 2 ) + +/** + * Write 16-bit word to I/O-mapped or memory-mapped device + * + * @v data Value to write + * @v io_addr I/O address + */ +void iowrite16 ( uint16_t data, volatile uint16_t *io_addr ); +#define iowrite16( data, io_addr ) \ + IOAPI_WRITE ( iowrite16, uint16_t, data, io_addr, "IO/MEM", 4 ) + +/** + * Write 32-bit dword to I/O-mapped or memory-mapped device + * + * @v data Value to write + * @v io_addr I/O address + */ +void iowrite32 ( uint32_t data, volatile uint32_t *io_addr ); +#define iowrite32( data, io_addr ) \ + IOAPI_WRITE ( iowrite32, uint32_t, data, io_addr, "IO/MEM", 8 ) + /** * Read bytes from I/O-mapped device *