new file mode 100644
@@ -0,0 +1,72 @@
+/*
+ * Edu PCI device.
+ *
+ * Copyright (C) 2016 Red Hat, Inc.
+ *
+ * Authors:
+ * Peter Xu <peterx@redhat.com>,
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2 or
+ * later.
+ */
+
+#include "pci-edu.h"
+#include "asm/barrier.h"
+
+/* Return true if alive */
+static inline bool edu_check_alive(struct pci_edu_dev *dev)
+{
+ static uint32_t live_count = 1;
+ uint32_t value;
+
+ edu_reg_writel(dev, EDU_REG_ALIVE, live_count++);
+ value = edu_reg_readl(dev, EDU_REG_ALIVE);
+ return (live_count - 1 == ~value);
+}
+
+bool edu_init(struct pci_edu_dev *dev)
+{
+ pcidevaddr_t dev_addr;
+
+ dev_addr = pci_find_dev(PCI_VENDOR_ID_QEMU, PCI_DEVICE_ID_EDU);
+ if (dev_addr == PCIDEVADDR_INVALID)
+ return false;
+
+ pci_dev_init(&dev->pci_dev, dev_addr);
+ pci_enable_defaults(&dev->pci_dev);
+ assert(edu_check_alive(dev));
+ return true;
+}
+
+void edu_dma(struct pci_edu_dev *dev, iova_t iova,
+ size_t size, unsigned int dev_offset, bool from_device)
+{
+ uint64_t from, to;
+ uint32_t cmd = EDU_CMD_DMA_START;
+
+ assert(size <= EDU_DMA_SIZE_MAX);
+ assert(dev_offset < EDU_DMA_SIZE_MAX);
+
+ printf("edu device DMA start %s addr %p size 0x%lu off 0x%x\n",
+ from_device ? "FROM" : "TO",
+ (void *)iova, size, dev_offset);
+
+ if (from_device) {
+ from = dev_offset + EDU_DMA_START;
+ to = iova;
+ cmd |= EDU_CMD_DMA_FROM;
+ } else {
+ from = iova;
+ to = EDU_DMA_START + dev_offset;
+ cmd |= EDU_CMD_DMA_TO;
+ }
+
+ edu_reg_writeq(dev, EDU_REG_DMA_SRC, from);
+ edu_reg_writeq(dev, EDU_REG_DMA_DST, to);
+ edu_reg_writeq(dev, EDU_REG_DMA_COUNT, size);
+ edu_reg_writel(dev, EDU_REG_DMA_CMD, cmd);
+
+ /* Wait until DMA finished */
+ while (edu_reg_readl(dev, EDU_REG_DMA_CMD) & EDU_CMD_DMA_START)
+ cpu_relax();
+}
new file mode 100644
@@ -0,0 +1,82 @@
+/*
+ * Edu PCI device header.
+ *
+ * Copyright (C) 2016 Red Hat, Inc.
+ *
+ * Authors:
+ * Peter Xu <peterx@redhat.com>,
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2 or
+ * later.
+ *
+ * Edu device is a virtualized device in QEMU. Please refer to
+ * docs/specs/edu.txt in QEMU repository for EDU device manual.
+ */
+#ifndef __PCI_EDU_H__
+#define __PCI_EDU_H__
+
+#include "pci.h"
+#include "asm/io.h"
+
+#define PCI_VENDOR_ID_QEMU 0x1234
+#define PCI_DEVICE_ID_EDU 0x11e8
+
+/* The only bar used by EDU device */
+#define EDU_BAR 0
+#define EDU_MAGIC 0xed
+#define EDU_VERSION 0x100
+#define EDU_DMA_BUF_SIZE (1 << 20)
+#define EDU_INPUT_BUF_SIZE 256
+
+#define EDU_REG_ID 0x0
+#define EDU_REG_ALIVE 0x4
+#define EDU_REG_FACTORIAL 0x8
+#define EDU_REG_STATUS 0x20
+#define EDU_REG_DMA_SRC 0x80
+#define EDU_REG_DMA_DST 0x88
+#define EDU_REG_DMA_COUNT 0x90
+#define EDU_REG_DMA_CMD 0x98
+
+#define EDU_CMD_DMA_START 0x01
+#define EDU_CMD_DMA_FROM 0x02
+#define EDU_CMD_DMA_TO 0x00
+
+#define EDU_STATUS_FACTORIAL 0x1
+#define EDU_STATUS_INT_ENABLE 0x80
+
+#define EDU_DMA_START 0x40000
+#define EDU_DMA_SIZE_MAX 4096
+
+struct pci_edu_dev {
+ struct pci_dev pci_dev;
+};
+
+#define edu_reg(d, r) (volatile void *)((d)->pci_dev.resource[EDU_BAR] + (r))
+
+static inline uint64_t edu_reg_readq(struct pci_edu_dev *dev, int reg)
+{
+ return __raw_readq(edu_reg(dev, reg));
+}
+
+static inline uint32_t edu_reg_readl(struct pci_edu_dev *dev, int reg)
+{
+ return __raw_readl(edu_reg(dev, reg));
+}
+
+static inline void edu_reg_writeq(struct pci_edu_dev *dev, int reg,
+ uint64_t val)
+{
+ __raw_writeq(val, edu_reg(dev, reg));
+}
+
+static inline void edu_reg_writel(struct pci_edu_dev *dev, int reg,
+ uint32_t val)
+{
+ __raw_writel(val, edu_reg(dev, reg));
+}
+
+bool edu_init(struct pci_edu_dev *dev);
+void edu_dma(struct pci_edu_dev *dev, iova_t iova,
+ size_t size, unsigned int dev_offset, bool from_device);
+
+#endif