mirror of
https://github.com/ipxe/ipxe
synced 2026-02-02 19:57:34 +03:00
[ioapi] Provide combined MMIO and port I/O accessors
Some devices (such as a 16550 UART) may be accessed via either MMIO or port I/O. This is currently forced to be a compile-time decision. For example: we currently access a 16550 UART via port I/O on x86 and via MMIO on any other platform. PCI UARTs with MMIO BARs do exist but are not currently supported in an x86 build of iPXE. Some AWS EC2 systems (observed on a c6i.metal instance in eu-west-2) provide only a PCI MMIO UART, and it is therefore currently impossible to get serial output from iPXE on these instance types. Add ioread8(), ioread16(), etc accessors that will select between MMIO and port I/O at the point of use. For non-x86 platforms where we currently have no port I/O support, these simply become wrappers around the corresponding readb(), readw(), etc MMIO accessors. On x86, we use the fairly well-known trick of treating any 16-bit address (below 64kB) as a port I/O address. This trick works even in the i386 BIOS build of iPXE (where virtual addresses are offset from physical addresses by a runtime constant), since the first 64kB of the virtual address space will correspond to the iPXE binary itself (along with its uninitialised-data space), and so must be RAM rather than a valid MMIO address range. Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
@@ -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 );
|
||||
|
||||
Reference in New Issue
Block a user