[gPXE-devel] [PATCH 32/33] [linux] Add I/O API

Piotr Jaroszyński p.jaroszynski at gmail.com
Sun Aug 15 18:59:37 EDT 2010


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.jaroszynski at 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.jaroszynski at 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.jaroszynski at 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



More information about the gPXE-devel mailing list