use fw_cfg DMA for fw_cfg_read_entry

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
diff --git a/cbfs.c b/cbfs.c
index 6c6a8cf..b4c235e 100644
--- a/cbfs.c
+++ b/cbfs.c
@@ -2,6 +2,7 @@
 #include "stdio.h"
 #include "ioport.h"
 #include "string.h"
+#include "bswap.h"
 #include "linuxboot.h"
 
 #define CBFS_HEADER_MAGIC    0x4F524243		// ORBC
@@ -38,20 +39,6 @@
 
 static struct cbfs_file *files;
 
-static inline uint32_t ldl_le_p(const void *p)
-{
-	uint32_t val;
-	memcpy(&val, p, 4);
-	return val;
-}
-
-static inline uint32_t ldl_be_p(const void *p)
-{
-	uint32_t val;
-	memcpy(&val, p, 4);
-	return __builtin_bswap32(val);
-}
-
 static bool cbfs_setup(const char *base, size_t sz)
 {
 	uint32_t ofs;
diff --git a/fw_cfg.c b/fw_cfg.c
index 85a2639..329311a 100644
--- a/fw_cfg.c
+++ b/fw_cfg.c
@@ -4,6 +4,7 @@
 #include "ioport.h"
 #include "string.h"
 #include "fw_cfg.h"
+#include "bswap.h"
 #include "linuxboot.h"
 #include "multiboot.h"
 
@@ -13,6 +14,7 @@
 	char name[57];
 };
 
+static int version;
 static int filecnt;
 static struct fw_cfg_file *files;
 
@@ -20,6 +22,9 @@
 {
 	int i, n;
 
+	fw_cfg_select(FW_CFG_ID);
+	version = fw_cfg_readl_le();
+
 	fw_cfg_select(FW_CFG_FILE_DIR);
 	n = fw_cfg_readl_be();
 	filecnt = n;
@@ -56,6 +61,48 @@
 	fw_cfg_select(files[id].select);
 }
 
+void fw_cfg_read_file(int id, void *buf, int len)
+{
+	fw_cfg_read_entry(files[id].select, buf, len);
+}
+
+struct fw_cfg_dma_descriptor {
+    uint32_t control;
+    uint32_t length;
+    uint64_t address;
+} __attribute__((packed));
+
+void fw_cfg_dma(int control, void *buf, int len)
+{
+	volatile struct fw_cfg_dma_descriptor dma;
+	uint32_t dma_desc_addr;
+
+	dma.control = bswap32(control);
+	dma.length = bswap32(len);
+	dma.address = bswap64((uintptr_t)buf);
+
+	dma_desc_addr = (uint32_t)&dma;
+	outl(FW_CFG_DMA_ADDR_LOW, bswap32(dma_desc_addr));
+	while (bswap32(dma.control) & ~FW_CFG_DMA_CTL_ERROR) {
+		asm("");
+	}
+}
+
+void
+fw_cfg_read_entry(int e, void *buf, int len)
+{
+	if (version & FW_CFG_VERSION_DMA) {
+		int control;
+		control = (e << 16);
+		control |= FW_CFG_DMA_CTL_SELECT;
+		control |= FW_CFG_DMA_CTL_READ;
+		fw_cfg_dma(control, buf, len);
+	} else {
+		fw_cfg_select(e);
+		fw_cfg_read(buf, len);
+	}
+}
+
 /* Multiboot trampoline.  QEMU does the ELF parsing.  */
 
 static void boot_multiboot_from_fw_cfg(void)
diff --git a/include/bswap.h b/include/bswap.h
new file mode 100644
index 0000000..2a72bd0
--- /dev/null
+++ b/include/bswap.h
@@ -0,0 +1,29 @@
+#ifndef BSWAP_H
+#define BSWAP_H 1
+
+static inline uint32_t bswap32(uint32_t x)
+{
+	return __builtin_bswap32(x);
+}
+
+static inline uint64_t bswap64(uint64_t x)
+{
+	return __builtin_bswap64(x);
+}
+
+static inline uint32_t ldl_le_p(const void *p)
+{
+	uint32_t val;
+	memcpy(&val, p, 4);
+	return val;
+}
+
+static inline uint32_t ldl_be_p(const void *p)
+{
+	uint32_t val;
+	memcpy(&val, p, 4);
+	return bswap32(val);
+}
+
+
+#endif
diff --git a/include/fw_cfg.h b/include/fw_cfg.h
index 46fa869..52ed4e3 100644
--- a/include/fw_cfg.h
+++ b/include/fw_cfg.h
@@ -36,8 +36,18 @@
 #define FW_CFG_IRQ0_OVERRIDE          (FW_CFG_ARCH_LOCAL + 2)
 #define FW_CFG_E820_TABLE             (FW_CFG_ARCH_LOCAL + 3)
 
-#define FW_CFG_CTL			0x510
-#define FW_CFG_DATA			0x511
+#define FW_CFG_VERSION                0x01
+#define FW_CFG_VERSION_DMA            0x02
+
+#define FW_CFG_DMA_CTL_ERROR          0x01
+#define FW_CFG_DMA_CTL_READ           0x02
+#define FW_CFG_DMA_CTL_SKIP           0x04
+#define FW_CFG_DMA_CTL_SELECT         0x08
+
+#define FW_CFG_CTL                    0x510
+#define FW_CFG_DATA                   0x511
+#define FW_CFG_DMA_ADDR_HIGH          0x514
+#define FW_CFG_DMA_ADDR_LOW           0x518
 
 #include "ioport.h"
 
@@ -102,23 +112,14 @@
 		inb(FW_CFG_DATA);
 }
 
-static inline void
-fw_cfg_read_entry(int e, void *buf, int len)
-{
-	fw_cfg_select(e);
-	fw_cfg_read(buf, len);
-}
-
 void fw_cfg_setup(void);
 int fw_cfg_file_id(char *name);
 uint32_t fw_cfg_file_size(int id);
 void fw_cfg_file_select(int id);
 
-static inline void
-fw_cfg_read_file(int id, void *buf, int len)
-{
-	fw_cfg_file_select(id);
-	fw_cfg_read(buf, len);
-}
+void fw_cfg_read(void *buf, int len);
+void fw_cfg_read_entry(int e, void *buf, int len);
+void fw_cfg_dma(int control, void *buf, int len);
+void fw_cfg_read_file(int e, void *buf, int len);
 
 #endif