Add I/O API using the lpci devices to handle I/O memory mappings and in*/out* from x86 for I/O ports access.
Signed-off-by: Piotr Jaroszyński <p.jaroszyn...@gmail.com> --- src/config/defaults/linux.h | 1 + src/include/gpxe/io.h | 1 + src/include/gpxe/linux/linux_io.h | 156 +++++++++++++++++++++++++++++++++++++ src/interface/linux/linux_io.c | 102 ++++++++++++++++++++++++ 4 files changed, 260 insertions(+), 0 deletions(-) create mode 100644 src/include/gpxe/linux/linux_io.h create mode 100644 src/interface/linux/linux_io.c diff --git a/src/config/defaults/linux.h b/src/config/defaults/linux.h index a917c23..ae2698a 100644 --- a/src/config/defaults/linux.h +++ b/src/config/defaults/linux.h @@ -15,6 +15,7 @@ #define SMBIOS_LINUX #define PCIAPI_LINUX +#define IOAPI_LINUX #define IMAGE_SCRIPT diff --git a/src/include/gpxe/io.h b/src/include/gpxe/io.h index 2c707db..17c5fad 100644 --- a/src/include/gpxe/io.h +++ b/src/include/gpxe/io.h @@ -54,6 +54,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); /* Include all architecture-independent I/O API headers */ #include <gpxe/efi/efi_io.h> +#include <gpxe/linux/linux_io.h> /* Include all architecture-dependent I/O API headers */ #include <bits/io.h> diff --git a/src/include/gpxe/linux/linux_io.h b/src/include/gpxe/linux/linux_io.h new file mode 100644 index 0000000..e3e731a --- /dev/null +++ b/src/include/gpxe/linux/linux_io.h @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2010 Piotr Jaroszyński <p.jaroszyn...@gmail.com> + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _GPXE_LINUX_IO_H +#define _GPXE_LINUX_IO_H + +FILE_LICENCE(GPL2_OR_LATER); + +#include <gpxe/linux/uio-dma.h> +#include <gpxe/lpci.h> + +/** @file + * + * gPXE I/O API for Linux + */ + +#ifdef IOAPI_LINUX +#define IOAPI_PREFIX_linux +#else +#define IOAPI_PREFIX_linux __linux_ +#endif + +static inline __always_inline void +IOAPI_INLINE(linux, iounmap)(volatile const void *io_addr __unused) +{ + /* Nothing to do */ +} + +static inline __always_inline unsigned long +IOAPI_INLINE(linux, io_to_bus)(volatile const void *io_addr) +{ + return ((unsigned long) io_addr); +} + +static inline __always_inline uint8_t +IOAPI_INLINE(linux, readb)(volatile uint8_t *io_addr) +{ + return *io_addr; +} + +static inline __always_inline uint16_t +IOAPI_INLINE(linux, readw)(volatile uint16_t *io_addr) +{ + return *io_addr; +} + +static inline __always_inline uint32_t +IOAPI_INLINE(linux, readl)(volatile uint32_t *io_addr) +{ + return *io_addr; +} + +static inline __always_inline uint64_t +IOAPI_INLINE(linux, readq)(volatile uint64_t *io_addr) +{ + return *io_addr; +} + +static inline __always_inline void +IOAPI_INLINE(linux, writeb)(uint8_t data, volatile uint8_t *io_addr) +{ + *io_addr = data; +} + +static inline __always_inline void +IOAPI_INLINE(linux, writew)(uint16_t data, volatile uint16_t *io_addr) +{ + *io_addr = data; +} + +static inline __always_inline void +IOAPI_INLINE(linux, writel)(uint32_t data, volatile uint32_t *io_addr) +{ + *io_addr = data; +} + +static inline __always_inline void +IOAPI_INLINE(linux, writeq)(uint64_t data, volatile uint64_t *io_addr) +{ + *io_addr = data; +} + +/* in/out from x86. Aborting if I/O ports weren't initialized */ +#define LINUX_INX( _insn_suffix, _type, _reg_prefix ) \ +static inline __always_inline _type \ +IOAPI_INLINE ( linux, in ## _insn_suffix ) ( volatile _type *io_addr ) { \ + if (! lpci_ioports_ready) \ + return (_type)0xffffffff; \ + _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 ( linux, ins ## _insn_suffix ) ( volatile _type *io_addr, \ + _type *data, \ + unsigned int count ) { \ + if (! lpci_ioports_ready) \ + return; \ + unsigned int discard_D; \ + __asm__ __volatile__ ( "rep ins" #_insn_suffix \ + : "=D" ( discard_D ) \ + : "d" ( io_addr ), "c" ( count ), \ + "0" ( data ) ); \ +} +LINUX_INX(b, uint8_t, "b"); +LINUX_INX(w, uint16_t, "w"); +LINUX_INX(l, uint32_t, "k"); + +#define LINUX_OUTX( _insn_suffix, _type, _reg_prefix ) \ +static inline __always_inline void \ +IOAPI_INLINE ( linux, out ## _insn_suffix ) ( _type data, \ + volatile _type *io_addr ) { \ + if (! lpci_ioports_ready) \ + return; \ + __asm__ __volatile__ ( "out" #_insn_suffix " %" _reg_prefix "0, %w1" \ + : : "a" ( data ), "Nd" ( io_addr ) ); \ +} \ +static inline __always_inline void \ +IOAPI_INLINE ( linux, outs ## _insn_suffix ) ( volatile _type *io_addr, \ + const _type *data, \ + unsigned int count ) { \ + unsigned int discard_S; \ + if (! lpci_ioports_ready) \ + return; \ + __asm__ __volatile__ ( "rep outs" #_insn_suffix \ + : "=S" ( discard_S ) \ + : "d" ( io_addr ), "c" ( count ), \ + "0" ( data ) ); \ +} +LINUX_OUTX(b, uint8_t, "b"); +LINUX_OUTX(w, uint16_t, "w"); +LINUX_OUTX(l, uint32_t, "k"); + +static inline __always_inline void IOAPI_INLINE(linux, mb)(void) +{ + /* gcc's built-in for a memory barrier */ + __sync_synchronize(); +} + +#endif /* _GPXE_LINUX_IO_H */ diff --git a/src/interface/linux/linux_io.c b/src/interface/linux/linux_io.c new file mode 100644 index 0000000..919f30e --- /dev/null +++ b/src/interface/linux/linux_io.c @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2010 Piotr Jaroszyński <p.jaroszyn...@gmail.com> + * + * 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. + */ + +FILE_LICENCE(GPL2_OR_LATER); + +#include <gpxe/io.h> +#include <gpxe/linux/linux_io.h> +#include <gpxe/lpci.h> +#include <linux_api.h> +#include <gpxe/linux/uio-dma.h> + +/** @file + * + * gPXE I/O API for linux + * + */ + +static unsigned long linux_phys_to_bus(unsigned long phys_addr) +{ + return uio_dma_addr(lpci_dma_mapping, (uint8_t *)phys_addr, 1); +} + + +static unsigned long linux_bus_to_phys(unsigned long bus_addr) +{ + return (unsigned long)uio_maddr(lpci_dma_mapping, bus_addr, 1); +} + +static void *linux_ioremap(unsigned long bus_addr, size_t len) +{ + struct lpci_device *lpci; + struct lpci_iores *iomem; + void *res; + + /* Scan all the I/O memory mappings */ + for_each_lpci(lpci) { + list_for_each_entry(iomem, &lpci->iomems, list) { + if (bus_addr >= iomem->start && bus_addr + len <= iomem->start + iomem->len) { + res = iomem->mapped + (bus_addr - iomem->start); + DBG("linux_io: found a mapping for [0x%016lx, 0x%016lx) -> [%p, %p)\n", + bus_addr, bus_addr + len, res, res + len); + return res; + } + } + } + DBG("linux_io: didn't find a mapping for [0x%016lx, 0x%016lx)\n", bus_addr, bus_addr + len); + return NULL; +} + +static void linux_iodelay(void) +{ + linux_usleep(1); +} + +static void linux_get_memmap ( struct memory_map *memmap ) +{ + memmap->count = 0; +} + +PROVIDE_IOAPI(linux, phys_to_bus, linux_phys_to_bus); +PROVIDE_IOAPI(linux, bus_to_phys, linux_bus_to_phys); +PROVIDE_IOAPI(linux, ioremap, linux_ioremap); +PROVIDE_IOAPI_INLINE(linux, iounmap); +PROVIDE_IOAPI_INLINE(linux, io_to_bus); +PROVIDE_IOAPI_INLINE(linux, readb); +PROVIDE_IOAPI_INLINE(linux, readw); +PROVIDE_IOAPI_INLINE(linux, readl); +PROVIDE_IOAPI_INLINE(linux, readq); +PROVIDE_IOAPI_INLINE(linux, writeb); +PROVIDE_IOAPI_INLINE(linux, writew); +PROVIDE_IOAPI_INLINE(linux, writel); +PROVIDE_IOAPI_INLINE(linux, writeq); +PROVIDE_IOAPI_INLINE(linux, inb); +PROVIDE_IOAPI_INLINE(linux, inw); +PROVIDE_IOAPI_INLINE(linux, inl); +PROVIDE_IOAPI_INLINE(linux, outb); +PROVIDE_IOAPI_INLINE(linux, outw); +PROVIDE_IOAPI_INLINE(linux, outl); +PROVIDE_IOAPI_INLINE(linux, insb); +PROVIDE_IOAPI_INLINE(linux, insw); +PROVIDE_IOAPI_INLINE(linux, insl); +PROVIDE_IOAPI_INLINE(linux, outsb); +PROVIDE_IOAPI_INLINE(linux, outsw); +PROVIDE_IOAPI_INLINE(linux, outsl); +PROVIDE_IOAPI(linux, iodelay, linux_iodelay); +PROVIDE_IOAPI_INLINE(linux, mb); +PROVIDE_IOAPI(linux, get_memmap, linux_get_memmap); -- 1.7.1 _______________________________________________ gPXE-devel mailing list gPXE-devel@etherboot.org http://etherboot.org/mailman/listinfo/gpxe-devel