QEMU edu device is a pci device that is originally written for educational purpose, however it also suits for IOMMU unit test. Adding helpers for this specific device to implement the device logic. The device supports lots of functions, here only DMA operation is supported. The spec of the device can be found at: https://github.com/qemu/qemu/blob/master/docs/specs/edu.txt Signed-off-by: Peter Xu <peterx@xxxxxxxxxx> --- lib/pci-edu.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/pci-edu.h | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 153 insertions(+) create mode 100644 lib/pci-edu.c create mode 100644 lib/pci-edu.h diff --git a/lib/pci-edu.c b/lib/pci-edu.c new file mode 100644 index 0000000..c594e08 --- /dev/null +++ b/lib/pci-edu.c @@ -0,0 +1,74 @@ +/* + * Edu PCI device. + * + * Copyright (C) 2016 Red Hat, Inc. + * + * Authors: + * Peter Xu <peterx@xxxxxxxxxx>, + * + * 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_write(dev, EDU_REG_ALIVE, live_count++); + value = edu_reg_read(dev, EDU_REG_ALIVE); + return (live_count - 1 == ~value); +} + +int edu_init(struct pci_edu_dev *dev) +{ + int ret; + + ret = pci_find_dev(&dev->pci_dev, PCI_VENDOR_ID_QEMU, + PCI_DEVICE_ID_EDU); + if (ret) + return ret; + + pci_enable_defaults(&dev->pci_dev); + assert(edu_check_alive(dev)); + + return 0; +} + +void edu_dma(struct pci_edu_dev *dev, iova_t iova, + size_t size, 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 && + dev_offset >= 0); + + 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_write(dev, EDU_REG_DMA_CMD, cmd); + + /* Wait until DMA finished */ + while (edu_reg_read(dev, EDU_REG_DMA_CMD) & EDU_CMD_DMA_START) + cpu_relax(); +} diff --git a/lib/pci-edu.h b/lib/pci-edu.h new file mode 100644 index 0000000..f98d2d1 --- /dev/null +++ b/lib/pci-edu.h @@ -0,0 +1,79 @@ +/* + * Edu PCI device header. + * + * Copyright (C) 2016 Red Hat, Inc. + * + * Authors: + * Peter Xu <peterx@xxxxxxxxxx>, + * + * 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" + +#define PCI_VENDOR_ID_QEMU (0x1234) +#define PCI_DEVICE_ID_EDU (0x11e8) + +/* The only bar used by EDU device */ +#define EDU_BAR_MEM (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; +}; + +static inline uint64_t edu_reg_readq(struct pci_edu_dev *dev, int reg) +{ + return *(volatile uint64_t *)(dev->pci_dev.pci_bar[EDU_BAR_MEM] + reg); +} + +static inline uint32_t edu_reg_read(struct pci_edu_dev *dev, int reg) +{ + return *(volatile uint32_t *)(dev->pci_dev.pci_bar[EDU_BAR_MEM] + reg); +} + +static inline void edu_reg_writeq(struct pci_edu_dev *dev, int reg, + uint64_t val) +{ + *(volatile uint64_t *)(dev->pci_dev.pci_bar[EDU_BAR_MEM] + reg) = val; +} + +static inline void edu_reg_write(struct pci_edu_dev *dev, int reg, + uint32_t val) +{ + *(volatile uint32_t *)(dev->pci_dev.pci_bar[EDU_BAR_MEM] + reg) = val; +} + +int edu_init(struct pci_edu_dev *dev); +void edu_dma(struct pci_edu_dev *dev, iova_t iova, + size_t size, int dev_offset, bool from_device); + +#endif -- 2.7.4 -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html