[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