All of lore.kernel.org
 help / color / mirror / Atom feed
From: Eduard - Gabriel Munteanu <eduard.munteanu@linux360.ro>
To: mst@redhat.com
Cc: seabios@seabios.org, kevin@koconnor.net, joro@8bytes.org,
	blauwirbel@gmail.com, paul@codesourcery.com, avi@redhat.com,
	anthony@codemonkey.ws, av1474@comtv.ru, yamahata@valinux.co.jp,
	kvm@vger.kernel.org, qemu-devel@nongnu.org,
	benh@kernel.crashing.org, aik@ozlabs.ru, agraf@suse.de,
	aliguori@us.ibm.com, dwg@au1.ibm.com, rth@twiddle.net,
	david@gibson.dropbear.id.au,
	Eduard - Gabriel Munteanu <eduard.munteanu@linux360.ro>
Subject: [RFC PATCH 03/13] AMD IOMMU emulation
Date: Wed,  1 Jun 2011 04:38:25 +0300	[thread overview]
Message-ID: <1306892315-7306-4-git-send-email-eduard.munteanu@linux360.ro> (raw)
In-Reply-To: <1306892315-7306-1-git-send-email-eduard.munteanu@linux360.ro>

This introduces emulation for the AMD IOMMU, described in "AMD I/O
Virtualization Technology (IOMMU) Specification".

Signed-off-by: Eduard - Gabriel Munteanu <eduard.munteanu@linux360.ro>
---
 Makefile.target |    2 +-
 hw/amd_iommu.c  |  712 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/pc.c         |    2 +
 3 files changed, 715 insertions(+), 1 deletions(-)
 create mode 100644 hw/amd_iommu.c

diff --git a/Makefile.target b/Makefile.target
index ee0c80d..5f9c868 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -224,7 +224,7 @@ obj-i386-y += cirrus_vga.o apic.o ioapic.o piix_pci.o
 obj-i386-y += vmport.o
 obj-i386-y += device-hotplug.o pci-hotplug.o smbios.o wdt_ib700.o
 obj-i386-y += debugcon.o multiboot.o
-obj-i386-y += pc_piix.o kvmclock.o dma_rw.o
+obj-i386-y += pc_piix.o kvmclock.o dma_rw.o amd_iommu.o
 obj-i386-$(CONFIG_SPICE) += qxl.o qxl-logger.o qxl-render.o
 
 # shared objects
diff --git a/hw/amd_iommu.c b/hw/amd_iommu.c
new file mode 100644
index 0000000..650a8f4
--- /dev/null
+++ b/hw/amd_iommu.c
@@ -0,0 +1,712 @@
+/*
+ * AMD IOMMU emulation
+ *
+ * Copyright (c) 2011 Eduard - Gabriel Munteanu
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "pc.h"
+#include "hw.h"
+#include "pci.h"
+#include "qlist.h"
+#include "dma_rw.h"
+
+/* Capability registers */
+#define CAPAB_HEADER            0x00
+#define   CAPAB_REV_TYPE        0x02
+#define   CAPAB_FLAGS           0x03
+#define CAPAB_BAR_LOW           0x04
+#define CAPAB_BAR_HIGH          0x08
+#define CAPAB_RANGE             0x0C
+#define CAPAB_MISC              0x10
+
+#define CAPAB_SIZE              0x14
+#define CAPAB_REG_SIZE          0x04
+
+/* Capability header data */
+#define CAPAB_FLAG_IOTLBSUP     (1 << 0)
+#define CAPAB_FLAG_HTTUNNEL     (1 << 1)
+#define CAPAB_FLAG_NPCACHE      (1 << 2)
+#define CAPAB_INIT_REV          (1 << 3)
+#define CAPAB_INIT_TYPE         3
+#define CAPAB_INIT_REV_TYPE     (CAPAB_REV | CAPAB_TYPE)
+#define CAPAB_INIT_FLAGS        (CAPAB_FLAG_NPCACHE | CAPAB_FLAG_HTTUNNEL)
+#define CAPAB_INIT_MISC         ((64 << 15) | (48 << 8))
+#define CAPAB_BAR_MASK          (~((1UL << 14) - 1))
+
+/* MMIO registers */
+#define MMIO_DEVICE_TABLE       0x0000
+#define MMIO_COMMAND_BASE       0x0008
+#define MMIO_EVENT_BASE         0x0010
+#define MMIO_CONTROL            0x0018
+#define MMIO_EXCL_BASE          0x0020
+#define MMIO_EXCL_LIMIT         0x0028
+#define MMIO_COMMAND_HEAD       0x2000
+#define MMIO_COMMAND_TAIL       0x2008
+#define MMIO_EVENT_HEAD         0x2010
+#define MMIO_EVENT_TAIL         0x2018
+#define MMIO_STATUS             0x2020
+
+#define MMIO_SIZE               0x4000
+
+#define MMIO_DEVTAB_SIZE_MASK   ((1ULL << 12) - 1)
+#define MMIO_DEVTAB_BASE_MASK   (((1ULL << 52) - 1) & ~MMIO_DEVTAB_SIZE_MASK)
+#define MMIO_DEVTAB_ENTRY_SIZE  32
+#define MMIO_DEVTAB_SIZE_UNIT   4096
+
+#define MMIO_CMDBUF_SIZE_BYTE       (MMIO_COMMAND_BASE + 7)
+#define MMIO_CMDBUF_SIZE_MASK       0x0F
+#define MMIO_CMDBUF_BASE_MASK       MMIO_DEVTAB_BASE_MASK
+#define MMIO_CMDBUF_DEFAULT_SIZE    8
+#define MMIO_CMDBUF_HEAD_MASK       (((1ULL << 19) - 1) & ~0x0F)
+#define MMIO_CMDBUF_TAIL_MASK       MMIO_EVTLOG_HEAD_MASK
+
+#define MMIO_EVTLOG_SIZE_BYTE       (MMIO_EVENT_BASE + 7)
+#define MMIO_EVTLOG_SIZE_MASK       MMIO_CMDBUF_SIZE_MASK
+#define MMIO_EVTLOG_BASE_MASK       MMIO_CMDBUF_BASE_MASK
+#define MMIO_EVTLOG_DEFAULT_SIZE    MMIO_CMDBUF_DEFAULT_SIZE
+#define MMIO_EVTLOG_HEAD_MASK       (((1ULL << 19) - 1) & ~0x0F)
+#define MMIO_EVTLOG_TAIL_MASK       MMIO_EVTLOG_HEAD_MASK
+
+#define MMIO_EXCL_BASE_MASK         MMIO_DEVTAB_BASE_MASK
+#define MMIO_EXCL_ENABLED_MASK      (1ULL << 0)
+#define MMIO_EXCL_ALLOW_MASK        (1ULL << 1)
+#define MMIO_EXCL_LIMIT_MASK        MMIO_DEVTAB_BASE_MASK
+#define MMIO_EXCL_LIMIT_LOW         0xFFF
+
+#define MMIO_CONTROL_IOMMUEN        (1ULL << 0)
+#define MMIO_CONTROL_HTTUNEN        (1ULL << 1)
+#define MMIO_CONTROL_EVENTLOGEN     (1ULL << 2)
+#define MMIO_CONTROL_EVENTINTEN     (1ULL << 3)
+#define MMIO_CONTROL_COMWAITINTEN   (1ULL << 4)
+#define MMIO_CONTROL_CMDBUFEN       (1ULL << 12)
+
+#define MMIO_STATUS_EVTLOG_OF       (1ULL << 0)
+#define MMIO_STATUS_EVTLOG_INTR     (1ULL << 1)
+#define MMIO_STATUS_COMWAIT_INTR    (1ULL << 2)
+#define MMIO_STATUS_EVTLOG_RUN      (1ULL << 3)
+#define MMIO_STATUS_CMDBUF_RUN      (1ULL << 4)
+
+#define CMDBUF_ID_BYTE              0x07
+#define CMDBUF_ID_RSHIFT            4
+#define CMDBUF_ENTRY_SIZE           0x10
+
+#define CMD_COMPLETION_WAIT         0x01
+#define CMD_INVAL_DEVTAB_ENTRY      0x02
+#define CMD_INVAL_IOMMU_PAGES       0x03
+#define CMD_INVAL_IOTLB_PAGES       0x04
+#define CMD_INVAL_INTR_TABLE        0x05
+
+#define DEVTAB_ENTRY_SIZE           32
+
+/* Device table entry bits 0:63 */
+#define DEV_VALID                   (1ULL << 0)
+#define DEV_TRANSLATION_VALID       (1ULL << 1)
+#define DEV_MODE_MASK               0x7
+#define DEV_MODE_RSHIFT             9
+#define DEV_PT_ROOT_MASK            0xFFFFFFFFFF000
+#define DEV_PT_ROOT_RSHIFT          12
+#define DEV_PERM_SHIFT              61
+#define DEV_PERM_READ               (1ULL << 61)
+#define DEV_PERM_WRITE              (1ULL << 62)
+
+/* Device table entry bits 64:127 */
+#define DEV_DOMAIN_ID_MASK          ((1ULL << 16) - 1)
+#define DEV_IOTLB_SUPPORT           (1ULL << 17)
+#define DEV_SUPPRESS_PF             (1ULL << 18)
+#define DEV_SUPPRESS_ALL_PF         (1ULL << 19)
+#define DEV_IOCTL_MASK              (~3)
+#define DEV_IOCTL_RSHIFT            20
+#define   DEV_IOCTL_DENY            0
+#define   DEV_IOCTL_PASSTHROUGH     1
+#define   DEV_IOCTL_TRANSLATE       2
+#define DEV_CACHE                   (1ULL << 37)
+#define DEV_SNOOP_DISABLE           (1ULL << 38)
+#define DEV_EXCL                    (1ULL << 39)
+
+/* Event codes and flags, as stored in the info field */
+#define EVENT_ILLEGAL_DEVTAB_ENTRY  (0x1U << 24)
+#define EVENT_IOPF                  (0x2U << 24)
+#define   EVENT_IOPF_I              (1U << 3)
+#define   EVENT_IOPF_PR             (1U << 4)
+#define   EVENT_IOPF_RW             (1U << 5)
+#define   EVENT_IOPF_PE             (1U << 6)
+#define   EVENT_IOPF_RZ             (1U << 7)
+#define   EVENT_IOPF_TR             (1U << 8)
+#define EVENT_DEV_TAB_HW_ERROR      (0x3U << 24)
+#define EVENT_PAGE_TAB_HW_ERROR     (0x4U << 24)
+#define EVENT_ILLEGAL_COMMAND_ERROR (0x5U << 24)
+#define EVENT_COMMAND_HW_ERROR      (0x6U << 24)
+#define EVENT_IOTLB_INV_TIMEOUT     (0x7U << 24)
+#define EVENT_INVALID_DEV_REQUEST   (0x8U << 24)
+
+#define EVENT_LEN                   16
+
+#define IOMMU_PERM_READ             (1 << 0)
+#define IOMMU_PERM_WRITE            (1 << 1)
+#define IOMMU_PERM_RW               (IOMMU_PERM_READ | IOMMU_PERM_WRITE)
+
+/* FIXME: Remove these once they go upstream. */
+#define PCI_CLASS_SYSTEM_IOMMU      0x0806
+#define PCI_DEVICE_ID_AMD_IOMMU     0x0000  /* FIXME */
+#define PCI_CAP_ID_SEC              0x0F
+
+typedef struct AMDIOMMUState {
+    PCIDevice                   dev;
+
+    int                         capab_offset;
+    unsigned char               *capab;
+
+    int                         mmio_index;
+    target_phys_addr_t          mmio_addr;
+    unsigned char               *mmio_buf;
+    int                         mmio_enabled;
+
+    int                         enabled;
+    int                         ats_enabled;
+
+    target_phys_addr_t          devtab;
+    size_t                      devtab_len;
+
+    target_phys_addr_t          cmdbuf;
+    int                         cmdbuf_enabled;
+    size_t                      cmdbuf_len;
+    size_t                      cmdbuf_head;
+    size_t                      cmdbuf_tail;
+    int                         completion_wait_intr;
+
+    target_phys_addr_t          evtlog;
+    int                         evtlog_enabled;
+    int                         evtlog_intr;
+    target_phys_addr_t          evtlog_len;
+    target_phys_addr_t          evtlog_head;
+    target_phys_addr_t          evtlog_tail;
+
+    target_phys_addr_t          excl_base;
+    target_phys_addr_t          excl_limit;
+    int                         excl_enabled;
+    int                         excl_allow;
+} AMDIOMMUState;
+
+typedef struct AMDIOMMUEvent {
+    uint16_t    devfn;
+    uint16_t    reserved;
+    uint16_t    domid;
+    uint16_t    info;
+    uint64_t    addr;
+} __attribute__((packed)) AMDIOMMUEvent;
+
+static void amd_iommu_completion_wait(AMDIOMMUState *s,
+                                      uint8_t *cmd)
+{
+    uint64_t addr;
+
+    if (cmd[0] & 1) {
+        addr = le64_to_cpu(*(uint64_t *) cmd) & 0xFFFFFFFFFFFF8;
+        cpu_physical_memory_write(addr, cmd + 8, 8);
+    }
+
+    if (cmd[0] & 2) {
+        s->mmio_buf[MMIO_STATUS] |= MMIO_STATUS_COMWAIT_INTR;
+    }
+}
+
+static void amd_iommu_invalidate_iotlb(AMDIOMMUState *s,
+                                       uint8_t *cmd)
+{
+    PCIDevice *dev;
+    PCIBus *bus = s->dev.bus;
+    int bus_num = pci_bus_num(bus);
+    int devfn = *(uint16_t *) cmd;
+
+    dev = pci_find_device(bus, bus_num, devfn);
+    if (dev) {
+        dma_invalidate_memory_range(&dev->dma, 0, -1);
+    }
+}
+
+static void amd_iommu_cmdbuf_exec(AMDIOMMUState *s)
+{
+    uint8_t cmd[16];
+    int type;
+
+    cpu_physical_memory_read(s->cmdbuf + s->cmdbuf_head, cmd, 16);
+    type = cmd[CMDBUF_ID_BYTE] >> CMDBUF_ID_RSHIFT;
+    switch (type) {
+    case CMD_COMPLETION_WAIT:
+        amd_iommu_completion_wait(s, cmd);
+        break;
+    case CMD_INVAL_DEVTAB_ENTRY:
+        break;
+    case CMD_INVAL_IOMMU_PAGES:
+        break;
+    case CMD_INVAL_IOTLB_PAGES:
+        amd_iommu_invalidate_iotlb(s, cmd);
+        break;
+    case CMD_INVAL_INTR_TABLE:
+        break;
+    default:
+        break;
+    }
+}
+
+static void amd_iommu_cmdbuf_run(AMDIOMMUState *s)
+{
+    uint64_t *mmio_cmdbuf_head = (uint64_t *) s->mmio_buf + MMIO_COMMAND_HEAD;
+
+    if (!s->cmdbuf_enabled) {
+        return;
+    }
+
+    /* Check if there's work to do. */
+    while (s->cmdbuf_head != s->cmdbuf_tail) {
+        /* Wrap head pointer. */
+        if (s->cmdbuf_head >= s->cmdbuf_len * CMDBUF_ENTRY_SIZE) {
+            s->cmdbuf_head = 0;
+        }
+
+        amd_iommu_cmdbuf_exec(s);
+
+        /* Increment head pointer. */
+        s->cmdbuf_head += CMDBUF_ENTRY_SIZE;
+    }
+
+    *mmio_cmdbuf_head = cpu_to_le64(s->cmdbuf_head);
+}
+
+static uint32_t amd_iommu_mmio_buf_read(AMDIOMMUState *s,
+                                        size_t offset,
+                                        size_t size)
+{
+    ssize_t i;
+    uint32_t ret;
+
+    if (!size) {
+        return 0;
+    }
+
+    ret = s->mmio_buf[offset + size - 1];
+    for (i = size - 2; i >= 0; i--) {
+        ret <<= 8;
+        ret |= s->mmio_buf[offset + i];
+    }
+
+    return ret;
+}
+
+static void amd_iommu_mmio_buf_write(AMDIOMMUState *s,
+                                     size_t offset,
+                                     size_t size,
+                                     uint32_t val)
+{
+    size_t i;
+
+    for (i = 0; i < size; i++) {
+        s->mmio_buf[offset + i] = val & 0xFF;
+        val >>= 8;
+    }
+}
+
+static void amd_iommu_update_mmio(AMDIOMMUState *s,
+                                  target_phys_addr_t addr)
+{
+    size_t reg = addr & ~0x07;
+    uint64_t *base = (uint64_t *) &s->mmio_buf[reg];
+    uint64_t val = le64_to_cpu(*base);
+
+    switch (reg) {
+    case MMIO_CONTROL:
+        s->enabled              = !!(val & MMIO_CONTROL_IOMMUEN);
+        s->ats_enabled          = !!(val & MMIO_CONTROL_HTTUNEN);
+        s->evtlog_enabled       = s->enabled &&
+                                  !!(val & MMIO_CONTROL_EVENTLOGEN);
+        s->evtlog_intr          = !!(val & MMIO_CONTROL_EVENTINTEN);
+        s->completion_wait_intr = !!(val & MMIO_CONTROL_COMWAITINTEN);
+        s->cmdbuf_enabled       = s->enabled &&
+                                  !!(val & MMIO_CONTROL_CMDBUFEN);
+
+        /* Update status flags depending on the control register. */
+        if (s->cmdbuf_enabled) {
+            s->mmio_buf[MMIO_STATUS] |= MMIO_STATUS_CMDBUF_RUN;
+        } else {
+            s->mmio_buf[MMIO_STATUS] &= ~MMIO_STATUS_CMDBUF_RUN;
+        }
+        if (s->evtlog_enabled) {
+            s->mmio_buf[MMIO_STATUS] |= MMIO_STATUS_EVTLOG_RUN;
+        } else {
+            s->mmio_buf[MMIO_STATUS] &= ~MMIO_STATUS_EVTLOG_RUN;
+        }
+
+        amd_iommu_cmdbuf_run(s);
+        break;
+    case MMIO_DEVICE_TABLE:
+        s->devtab = (target_phys_addr_t) (val & MMIO_DEVTAB_BASE_MASK);
+        s->devtab_len = ((val & MMIO_DEVTAB_SIZE_MASK) + 1) *
+                         (MMIO_DEVTAB_SIZE_UNIT / MMIO_DEVTAB_ENTRY_SIZE);
+        break;
+    case MMIO_COMMAND_BASE:
+        s->cmdbuf = (target_phys_addr_t) (val & MMIO_CMDBUF_BASE_MASK);
+        s->cmdbuf_len = 1UL << (s->mmio_buf[MMIO_CMDBUF_SIZE_BYTE] &
+                                MMIO_CMDBUF_SIZE_MASK);
+
+        /* We must reset the head and tail pointers. */
+        s->cmdbuf_head = s->cmdbuf_tail = 0;
+        memset(s->mmio_buf + MMIO_COMMAND_HEAD, 0, 8);
+        memset(s->mmio_buf + MMIO_COMMAND_TAIL, 0, 8);
+        break;
+    case MMIO_COMMAND_HEAD:
+        s->cmdbuf_head = val & MMIO_CMDBUF_HEAD_MASK;
+        amd_iommu_cmdbuf_run(s);
+        break;
+    case MMIO_COMMAND_TAIL:
+        s->cmdbuf_tail = val & MMIO_CMDBUF_TAIL_MASK;
+        amd_iommu_cmdbuf_run(s);
+        break;
+    case MMIO_EVENT_BASE:
+        s->evtlog = (target_phys_addr_t) (val & MMIO_EVTLOG_BASE_MASK);
+        s->evtlog_len = 1UL << (s->mmio_buf[MMIO_EVTLOG_SIZE_BYTE] &
+                                MMIO_EVTLOG_SIZE_MASK);
+        break;
+    case MMIO_EVENT_HEAD:
+        s->evtlog_head = val & MMIO_EVTLOG_HEAD_MASK;
+        break;
+    case MMIO_EVENT_TAIL:
+        s->evtlog_tail = val & MMIO_EVTLOG_TAIL_MASK;
+        break;
+    case MMIO_EXCL_BASE:
+        s->excl_base = (target_phys_addr_t) (val & MMIO_EXCL_BASE_MASK);
+        s->excl_enabled = val & MMIO_EXCL_ENABLED_MASK;
+        s->excl_allow = val & MMIO_EXCL_ALLOW_MASK;
+        break;
+    case MMIO_EXCL_LIMIT:
+        s->excl_limit = (target_phys_addr_t) ((val & MMIO_EXCL_LIMIT_MASK) |
+                                              MMIO_EXCL_LIMIT_LOW);
+        break;
+    default:
+        break;
+    }
+}
+
+static uint32_t amd_iommu_mmio_readb(void *opaque, target_phys_addr_t addr)
+{
+    AMDIOMMUState *s = opaque;
+
+    return amd_iommu_mmio_buf_read(s, addr, 1);
+}
+
+static uint32_t amd_iommu_mmio_readw(void *opaque, target_phys_addr_t addr)
+{
+    AMDIOMMUState *s = opaque;
+
+    return amd_iommu_mmio_buf_read(s, addr, 2);
+}
+
+static uint32_t amd_iommu_mmio_readl(void *opaque, target_phys_addr_t addr)
+{
+    AMDIOMMUState *s = opaque;
+
+    return amd_iommu_mmio_buf_read(s, addr, 4);
+}
+
+static void amd_iommu_mmio_writeb(void *opaque,
+                                  target_phys_addr_t addr,
+                                  uint32_t val)
+{
+    AMDIOMMUState *s = opaque;
+
+    amd_iommu_mmio_buf_write(s, addr, 1, val);
+    amd_iommu_update_mmio(s, addr);
+}
+
+static void amd_iommu_mmio_writew(void *opaque,
+                                  target_phys_addr_t addr,
+                                  uint32_t val)
+{
+    AMDIOMMUState *s = opaque;
+
+    amd_iommu_mmio_buf_write(s, addr, 2, val);
+    amd_iommu_update_mmio(s, addr);
+}
+
+static void amd_iommu_mmio_writel(void *opaque,
+                                  target_phys_addr_t addr,
+                                  uint32_t val)
+{
+    AMDIOMMUState *s = opaque;
+
+    amd_iommu_mmio_buf_write(s, addr, 4, val);
+    amd_iommu_update_mmio(s, addr);
+}
+
+static CPUReadMemoryFunc * const amd_iommu_mmio_read[] = {
+    amd_iommu_mmio_readb,
+    amd_iommu_mmio_readw,
+    amd_iommu_mmio_readl,
+};
+
+static CPUWriteMemoryFunc * const amd_iommu_mmio_write[] = {
+    amd_iommu_mmio_writeb,
+    amd_iommu_mmio_writew,
+    amd_iommu_mmio_writel,
+};
+
+static void amd_iommu_enable_mmio(AMDIOMMUState *s)
+{
+    target_phys_addr_t addr;
+    uint8_t *capab_wmask = s->dev.wmask + s->capab_offset;
+
+    s->mmio_index = cpu_register_io_memory(amd_iommu_mmio_read,
+                                           amd_iommu_mmio_write,
+                                           s, DEVICE_LITTLE_ENDIAN);
+    if (s->mmio_index < 0) {
+        return;
+    }
+
+    addr = le64_to_cpu(*(uint64_t *) &s->capab[CAPAB_BAR_LOW]) & CAPAB_BAR_MASK;
+    cpu_register_physical_memory(addr, MMIO_SIZE, s->mmio_index);
+
+    s->mmio_addr = addr;
+    s->mmio_enabled = 1;
+
+    /* Further changes to the capability are prohibited. */
+    memset(capab_wmask + CAPAB_BAR_LOW, 0x00, CAPAB_REG_SIZE);
+    memset(capab_wmask + CAPAB_BAR_HIGH, 0x00, CAPAB_REG_SIZE);
+}
+
+static void amd_iommu_write_capab(PCIDevice *dev,
+                                  uint32_t addr, uint32_t val, int len)
+{
+    AMDIOMMUState *s = DO_UPCAST(AMDIOMMUState, dev, dev);
+
+    pci_default_write_config(dev, addr, val, len);
+
+    if (!s->mmio_enabled && s->capab[CAPAB_BAR_LOW] & 0x1) {
+        amd_iommu_enable_mmio(s);
+    }
+}
+
+static void amd_iommu_reset(DeviceState *dev)
+{
+    AMDIOMMUState *s = DO_UPCAST(AMDIOMMUState, dev.qdev, dev);
+    unsigned char *capab = s->capab;
+    uint8_t *capab_wmask = s->dev.wmask + s->capab_offset;
+
+    s->enabled      = 0;
+    s->ats_enabled  = 0;
+    s->mmio_enabled = 0;
+
+    capab[CAPAB_REV_TYPE]  = CAPAB_REV_TYPE;
+    capab[CAPAB_FLAGS]     = CAPAB_FLAGS;
+    capab[CAPAB_BAR_LOW]   = 0;
+    capab[CAPAB_BAR_HIGH]  = 0;
+    capab[CAPAB_RANGE]     = 0;
+    *((uint32_t *) &capab[CAPAB_MISC]) = cpu_to_le32(CAPAB_INIT_MISC);
+
+    /* Changes to the capability are allowed after system reset. */
+    memset(capab_wmask + CAPAB_BAR_LOW, 0xFF, CAPAB_REG_SIZE);
+    memset(capab_wmask + CAPAB_BAR_HIGH, 0xFF, CAPAB_REG_SIZE);
+
+    memset(s->mmio_buf, 0, MMIO_SIZE);
+    s->mmio_buf[MMIO_CMDBUF_SIZE_BYTE] = MMIO_CMDBUF_DEFAULT_SIZE;
+    s->mmio_buf[MMIO_EVTLOG_SIZE_BYTE] = MMIO_EVTLOG_DEFAULT_SIZE;
+}
+
+static void amd_iommu_log_event(AMDIOMMUState *s, AMDIOMMUEvent *evt)
+{
+    if (!s->evtlog_enabled ||
+        (s->mmio_buf[MMIO_STATUS] | MMIO_STATUS_EVTLOG_OF)) {
+        return;
+    }
+
+    if (s->evtlog_tail >= s->evtlog_len) {
+        s->mmio_buf[MMIO_STATUS] |= MMIO_STATUS_EVTLOG_OF;
+    }
+
+    cpu_physical_memory_write(s->evtlog + s->evtlog_tail,
+                              (uint8_t *) evt, EVENT_LEN);
+
+    s->evtlog_tail += EVENT_LEN;
+    s->mmio_buf[MMIO_STATUS] |= MMIO_STATUS_EVTLOG_INTR;
+}
+
+static void amd_iommu_page_fault(AMDIOMMUState *s,
+                                 int devfn,
+                                 unsigned domid,
+                                 target_phys_addr_t addr,
+                                 int present,
+                                 int is_write)
+{
+    AMDIOMMUEvent evt;
+    unsigned info;
+    uint16_t status;
+
+    evt.devfn = cpu_to_le16(devfn);
+    evt.reserved = 0;
+    evt.domid = cpu_to_le16(domid);
+    evt.addr = cpu_to_le64(addr);
+
+    info = EVENT_IOPF;
+    if (present) {
+        info |= EVENT_IOPF_PR;
+    }
+    if (is_write) {
+        info |= EVENT_IOPF_RW;
+    }
+    evt.info = cpu_to_le16(info);
+
+    amd_iommu_log_event(s, &evt);
+
+    /*
+     * Signal a target abort.
+     *
+     * FIXME: There should be a way to turn this off when acked.
+     */
+    status = pci_get_word(s->dev.config + PCI_STATUS);
+    pci_set_word(s->dev.config + PCI_STATUS,
+                 status | PCI_STATUS_SIG_TARGET_ABORT);
+}
+
+static inline uint64_t amd_iommu_get_perms(uint64_t entry)
+{
+    return (entry & (DEV_PERM_READ | DEV_PERM_WRITE)) >> DEV_PERM_SHIFT;
+}
+
+static inline AMDIOMMUState *amd_iommu_dma_to_state(DMADevice *dev)
+{
+    PCIDevice *pci_dev = DO_UPCAST(PCIDevice, qdev, dev->mmu->iommu);
+
+    return DO_UPCAST(AMDIOMMUState, dev, pci_dev);
+}
+
+static int amd_iommu_translate(DMADevice *dev,
+                               dma_addr_t addr,
+                               dma_addr_t *paddr,
+                               dma_addr_t *len,
+                               int is_write)
+{
+    PCIDevice *pci_dev = container_of(dev, PCIDevice, dma);
+    PCIDevice *iommu_dev = DO_UPCAST(PCIDevice, qdev, dev->mmu->iommu);
+    AMDIOMMUState *s = DO_UPCAST(AMDIOMMUState, dev, iommu_dev);
+    int devfn, present;
+    target_phys_addr_t entry_addr, pte_addr;
+    uint64_t entry[4], pte, page_offset, pte_perms;
+    unsigned level, domid;
+    unsigned perms;
+
+    if (!s->enabled) {
+        goto no_translation;
+    }
+
+    /*
+     * It's okay to check for either read or write permissions
+     * even for memory maps, since we don't support R/W maps.
+     */
+    perms = is_write ? IOMMU_PERM_WRITE : IOMMU_PERM_READ;
+
+    /* Get device table entry. */
+    devfn = pci_dev->devfn;
+    entry_addr = s->devtab + devfn * DEVTAB_ENTRY_SIZE;
+    cpu_physical_memory_read(entry_addr, (uint8_t *) entry, 32);
+
+    pte = entry[0];
+    if (!(pte & DEV_VALID) || !(pte & DEV_TRANSLATION_VALID)) {
+        goto no_translation;
+    }
+    domid = entry[1] & DEV_DOMAIN_ID_MASK;
+    level = (pte >> DEV_MODE_RSHIFT) & DEV_MODE_MASK;
+    while (level > 0) {
+        /*
+         * Check permissions: the bitwise
+         * implication perms -> entry_perms must be true.
+         */
+        pte_perms = amd_iommu_get_perms(pte);
+        present = pte & 1;
+        if (!present || perms != (perms & pte_perms)) {
+            amd_iommu_page_fault(s, devfn, domid, addr,
+                                 present, !!(perms & IOMMU_PERM_WRITE));
+            return -EPERM;
+        }
+
+        /* Go to the next lower level. */
+        pte_addr = pte & DEV_PT_ROOT_MASK;
+        pte_addr += ((addr >> (3 + 9 * level)) & 0x1FF) << 3;
+        pte = ldq_phys(pte_addr);
+        level = (pte >> DEV_MODE_RSHIFT) & DEV_MODE_MASK;
+    }
+    page_offset = addr & 4095;
+    *paddr = (pte & DEV_PT_ROOT_MASK) + page_offset;
+    *len = 4096 - page_offset;
+
+    return 0;
+
+no_translation:
+    *paddr = addr;
+    *len = -1;
+    return 0;
+}
+
+static int amd_iommu_pci_initfn(PCIDevice *dev)
+{
+    AMDIOMMUState *s = DO_UPCAST(AMDIOMMUState, dev, dev);
+
+    pci_config_set_vendor_id(s->dev.config, PCI_VENDOR_ID_AMD);
+    pci_config_set_device_id(s->dev.config, PCI_DEVICE_ID_AMD_IOMMU);
+    pci_config_set_class(s->dev.config, PCI_CLASS_SYSTEM_IOMMU);
+
+    /* Secure Device capability */
+    s->capab_offset = pci_add_capability(&s->dev,
+                                         PCI_CAP_ID_SEC, 0, CAPAB_SIZE);
+    s->capab = s->dev.config + s->capab_offset;
+    dev->config_write = amd_iommu_write_capab;
+
+    /* Allocate backing space for the MMIO registers. */
+    s->mmio_buf = qemu_malloc(MMIO_SIZE);
+
+    pci_register_iommu(dev, amd_iommu_translate);
+
+    return 0;
+}
+
+static const VMStateDescription vmstate_amd_iommu = {
+    .name                       = "amd-iommu",
+    .version_id                 = 1,
+    .minimum_version_id         = 1,
+    .minimum_version_id_old     = 1,
+    .fields                     = (VMStateField []) {
+        VMSTATE_PCI_DEVICE(dev, AMDIOMMUState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static PCIDeviceInfo amd_iommu_pci_info = {
+    .qdev.name    = "amd-iommu",
+    .qdev.desc    = "AMD IOMMU",
+    .qdev.size    = sizeof(AMDIOMMUState),
+    .qdev.reset   = amd_iommu_reset,
+    .qdev.vmsd    = &vmstate_amd_iommu,
+    .init         = amd_iommu_pci_initfn,
+};
+
+static void amd_iommu_register(void)
+{
+    pci_qdev_register(&amd_iommu_pci_info);
+}
+
+device_init(amd_iommu_register);
diff --git a/hw/pc.c b/hw/pc.c
index 6939c04..b93949c 100644
--- a/hw/pc.c
+++ b/hw/pc.c
@@ -1161,6 +1161,8 @@ void pc_pci_device_init(PCIBus *pci_bus)
     int max_bus;
     int bus;
 
+    pci_create_simple(pci_bus, -1, "amd-iommu");
+
     max_bus = drive_get_max_bus(IF_SCSI);
     for (bus = 0; bus <= max_bus; bus++) {
         pci_create_simple(pci_bus, -1, "lsi53c895a");
-- 
1.7.3.4


WARNING: multiple messages have this Message-ID (diff)
From: Eduard - Gabriel Munteanu <eduard.munteanu@linux360.ro>
To: mst@redhat.com
Cc: aliguori@us.ibm.com, david@gibson.dropbear.id.au,
	kvm@vger.kernel.org, rth@twiddle.net, aik@ozlabs.ru,
	joro@8bytes.org, seabios@seabios.org, qemu-devel@nongnu.org,
	agraf@suse.de, blauwirbel@gmail.com, yamahata@valinux.co.jp,
	kevin@koconnor.net, avi@redhat.com,
	Eduard - Gabriel Munteanu <eduard.munteanu@linux360.ro>,
	dwg@au1.ibm.com, paul@codesourcery.com
Subject: [Qemu-devel] [RFC PATCH 03/13] AMD IOMMU emulation
Date: Wed,  1 Jun 2011 04:38:25 +0300	[thread overview]
Message-ID: <1306892315-7306-4-git-send-email-eduard.munteanu@linux360.ro> (raw)
In-Reply-To: <1306892315-7306-1-git-send-email-eduard.munteanu@linux360.ro>

This introduces emulation for the AMD IOMMU, described in "AMD I/O
Virtualization Technology (IOMMU) Specification".

Signed-off-by: Eduard - Gabriel Munteanu <eduard.munteanu@linux360.ro>
---
 Makefile.target |    2 +-
 hw/amd_iommu.c  |  712 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/pc.c         |    2 +
 3 files changed, 715 insertions(+), 1 deletions(-)
 create mode 100644 hw/amd_iommu.c

diff --git a/Makefile.target b/Makefile.target
index ee0c80d..5f9c868 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -224,7 +224,7 @@ obj-i386-y += cirrus_vga.o apic.o ioapic.o piix_pci.o
 obj-i386-y += vmport.o
 obj-i386-y += device-hotplug.o pci-hotplug.o smbios.o wdt_ib700.o
 obj-i386-y += debugcon.o multiboot.o
-obj-i386-y += pc_piix.o kvmclock.o dma_rw.o
+obj-i386-y += pc_piix.o kvmclock.o dma_rw.o amd_iommu.o
 obj-i386-$(CONFIG_SPICE) += qxl.o qxl-logger.o qxl-render.o
 
 # shared objects
diff --git a/hw/amd_iommu.c b/hw/amd_iommu.c
new file mode 100644
index 0000000..650a8f4
--- /dev/null
+++ b/hw/amd_iommu.c
@@ -0,0 +1,712 @@
+/*
+ * AMD IOMMU emulation
+ *
+ * Copyright (c) 2011 Eduard - Gabriel Munteanu
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "pc.h"
+#include "hw.h"
+#include "pci.h"
+#include "qlist.h"
+#include "dma_rw.h"
+
+/* Capability registers */
+#define CAPAB_HEADER            0x00
+#define   CAPAB_REV_TYPE        0x02
+#define   CAPAB_FLAGS           0x03
+#define CAPAB_BAR_LOW           0x04
+#define CAPAB_BAR_HIGH          0x08
+#define CAPAB_RANGE             0x0C
+#define CAPAB_MISC              0x10
+
+#define CAPAB_SIZE              0x14
+#define CAPAB_REG_SIZE          0x04
+
+/* Capability header data */
+#define CAPAB_FLAG_IOTLBSUP     (1 << 0)
+#define CAPAB_FLAG_HTTUNNEL     (1 << 1)
+#define CAPAB_FLAG_NPCACHE      (1 << 2)
+#define CAPAB_INIT_REV          (1 << 3)
+#define CAPAB_INIT_TYPE         3
+#define CAPAB_INIT_REV_TYPE     (CAPAB_REV | CAPAB_TYPE)
+#define CAPAB_INIT_FLAGS        (CAPAB_FLAG_NPCACHE | CAPAB_FLAG_HTTUNNEL)
+#define CAPAB_INIT_MISC         ((64 << 15) | (48 << 8))
+#define CAPAB_BAR_MASK          (~((1UL << 14) - 1))
+
+/* MMIO registers */
+#define MMIO_DEVICE_TABLE       0x0000
+#define MMIO_COMMAND_BASE       0x0008
+#define MMIO_EVENT_BASE         0x0010
+#define MMIO_CONTROL            0x0018
+#define MMIO_EXCL_BASE          0x0020
+#define MMIO_EXCL_LIMIT         0x0028
+#define MMIO_COMMAND_HEAD       0x2000
+#define MMIO_COMMAND_TAIL       0x2008
+#define MMIO_EVENT_HEAD         0x2010
+#define MMIO_EVENT_TAIL         0x2018
+#define MMIO_STATUS             0x2020
+
+#define MMIO_SIZE               0x4000
+
+#define MMIO_DEVTAB_SIZE_MASK   ((1ULL << 12) - 1)
+#define MMIO_DEVTAB_BASE_MASK   (((1ULL << 52) - 1) & ~MMIO_DEVTAB_SIZE_MASK)
+#define MMIO_DEVTAB_ENTRY_SIZE  32
+#define MMIO_DEVTAB_SIZE_UNIT   4096
+
+#define MMIO_CMDBUF_SIZE_BYTE       (MMIO_COMMAND_BASE + 7)
+#define MMIO_CMDBUF_SIZE_MASK       0x0F
+#define MMIO_CMDBUF_BASE_MASK       MMIO_DEVTAB_BASE_MASK
+#define MMIO_CMDBUF_DEFAULT_SIZE    8
+#define MMIO_CMDBUF_HEAD_MASK       (((1ULL << 19) - 1) & ~0x0F)
+#define MMIO_CMDBUF_TAIL_MASK       MMIO_EVTLOG_HEAD_MASK
+
+#define MMIO_EVTLOG_SIZE_BYTE       (MMIO_EVENT_BASE + 7)
+#define MMIO_EVTLOG_SIZE_MASK       MMIO_CMDBUF_SIZE_MASK
+#define MMIO_EVTLOG_BASE_MASK       MMIO_CMDBUF_BASE_MASK
+#define MMIO_EVTLOG_DEFAULT_SIZE    MMIO_CMDBUF_DEFAULT_SIZE
+#define MMIO_EVTLOG_HEAD_MASK       (((1ULL << 19) - 1) & ~0x0F)
+#define MMIO_EVTLOG_TAIL_MASK       MMIO_EVTLOG_HEAD_MASK
+
+#define MMIO_EXCL_BASE_MASK         MMIO_DEVTAB_BASE_MASK
+#define MMIO_EXCL_ENABLED_MASK      (1ULL << 0)
+#define MMIO_EXCL_ALLOW_MASK        (1ULL << 1)
+#define MMIO_EXCL_LIMIT_MASK        MMIO_DEVTAB_BASE_MASK
+#define MMIO_EXCL_LIMIT_LOW         0xFFF
+
+#define MMIO_CONTROL_IOMMUEN        (1ULL << 0)
+#define MMIO_CONTROL_HTTUNEN        (1ULL << 1)
+#define MMIO_CONTROL_EVENTLOGEN     (1ULL << 2)
+#define MMIO_CONTROL_EVENTINTEN     (1ULL << 3)
+#define MMIO_CONTROL_COMWAITINTEN   (1ULL << 4)
+#define MMIO_CONTROL_CMDBUFEN       (1ULL << 12)
+
+#define MMIO_STATUS_EVTLOG_OF       (1ULL << 0)
+#define MMIO_STATUS_EVTLOG_INTR     (1ULL << 1)
+#define MMIO_STATUS_COMWAIT_INTR    (1ULL << 2)
+#define MMIO_STATUS_EVTLOG_RUN      (1ULL << 3)
+#define MMIO_STATUS_CMDBUF_RUN      (1ULL << 4)
+
+#define CMDBUF_ID_BYTE              0x07
+#define CMDBUF_ID_RSHIFT            4
+#define CMDBUF_ENTRY_SIZE           0x10
+
+#define CMD_COMPLETION_WAIT         0x01
+#define CMD_INVAL_DEVTAB_ENTRY      0x02
+#define CMD_INVAL_IOMMU_PAGES       0x03
+#define CMD_INVAL_IOTLB_PAGES       0x04
+#define CMD_INVAL_INTR_TABLE        0x05
+
+#define DEVTAB_ENTRY_SIZE           32
+
+/* Device table entry bits 0:63 */
+#define DEV_VALID                   (1ULL << 0)
+#define DEV_TRANSLATION_VALID       (1ULL << 1)
+#define DEV_MODE_MASK               0x7
+#define DEV_MODE_RSHIFT             9
+#define DEV_PT_ROOT_MASK            0xFFFFFFFFFF000
+#define DEV_PT_ROOT_RSHIFT          12
+#define DEV_PERM_SHIFT              61
+#define DEV_PERM_READ               (1ULL << 61)
+#define DEV_PERM_WRITE              (1ULL << 62)
+
+/* Device table entry bits 64:127 */
+#define DEV_DOMAIN_ID_MASK          ((1ULL << 16) - 1)
+#define DEV_IOTLB_SUPPORT           (1ULL << 17)
+#define DEV_SUPPRESS_PF             (1ULL << 18)
+#define DEV_SUPPRESS_ALL_PF         (1ULL << 19)
+#define DEV_IOCTL_MASK              (~3)
+#define DEV_IOCTL_RSHIFT            20
+#define   DEV_IOCTL_DENY            0
+#define   DEV_IOCTL_PASSTHROUGH     1
+#define   DEV_IOCTL_TRANSLATE       2
+#define DEV_CACHE                   (1ULL << 37)
+#define DEV_SNOOP_DISABLE           (1ULL << 38)
+#define DEV_EXCL                    (1ULL << 39)
+
+/* Event codes and flags, as stored in the info field */
+#define EVENT_ILLEGAL_DEVTAB_ENTRY  (0x1U << 24)
+#define EVENT_IOPF                  (0x2U << 24)
+#define   EVENT_IOPF_I              (1U << 3)
+#define   EVENT_IOPF_PR             (1U << 4)
+#define   EVENT_IOPF_RW             (1U << 5)
+#define   EVENT_IOPF_PE             (1U << 6)
+#define   EVENT_IOPF_RZ             (1U << 7)
+#define   EVENT_IOPF_TR             (1U << 8)
+#define EVENT_DEV_TAB_HW_ERROR      (0x3U << 24)
+#define EVENT_PAGE_TAB_HW_ERROR     (0x4U << 24)
+#define EVENT_ILLEGAL_COMMAND_ERROR (0x5U << 24)
+#define EVENT_COMMAND_HW_ERROR      (0x6U << 24)
+#define EVENT_IOTLB_INV_TIMEOUT     (0x7U << 24)
+#define EVENT_INVALID_DEV_REQUEST   (0x8U << 24)
+
+#define EVENT_LEN                   16
+
+#define IOMMU_PERM_READ             (1 << 0)
+#define IOMMU_PERM_WRITE            (1 << 1)
+#define IOMMU_PERM_RW               (IOMMU_PERM_READ | IOMMU_PERM_WRITE)
+
+/* FIXME: Remove these once they go upstream. */
+#define PCI_CLASS_SYSTEM_IOMMU      0x0806
+#define PCI_DEVICE_ID_AMD_IOMMU     0x0000  /* FIXME */
+#define PCI_CAP_ID_SEC              0x0F
+
+typedef struct AMDIOMMUState {
+    PCIDevice                   dev;
+
+    int                         capab_offset;
+    unsigned char               *capab;
+
+    int                         mmio_index;
+    target_phys_addr_t          mmio_addr;
+    unsigned char               *mmio_buf;
+    int                         mmio_enabled;
+
+    int                         enabled;
+    int                         ats_enabled;
+
+    target_phys_addr_t          devtab;
+    size_t                      devtab_len;
+
+    target_phys_addr_t          cmdbuf;
+    int                         cmdbuf_enabled;
+    size_t                      cmdbuf_len;
+    size_t                      cmdbuf_head;
+    size_t                      cmdbuf_tail;
+    int                         completion_wait_intr;
+
+    target_phys_addr_t          evtlog;
+    int                         evtlog_enabled;
+    int                         evtlog_intr;
+    target_phys_addr_t          evtlog_len;
+    target_phys_addr_t          evtlog_head;
+    target_phys_addr_t          evtlog_tail;
+
+    target_phys_addr_t          excl_base;
+    target_phys_addr_t          excl_limit;
+    int                         excl_enabled;
+    int                         excl_allow;
+} AMDIOMMUState;
+
+typedef struct AMDIOMMUEvent {
+    uint16_t    devfn;
+    uint16_t    reserved;
+    uint16_t    domid;
+    uint16_t    info;
+    uint64_t    addr;
+} __attribute__((packed)) AMDIOMMUEvent;
+
+static void amd_iommu_completion_wait(AMDIOMMUState *s,
+                                      uint8_t *cmd)
+{
+    uint64_t addr;
+
+    if (cmd[0] & 1) {
+        addr = le64_to_cpu(*(uint64_t *) cmd) & 0xFFFFFFFFFFFF8;
+        cpu_physical_memory_write(addr, cmd + 8, 8);
+    }
+
+    if (cmd[0] & 2) {
+        s->mmio_buf[MMIO_STATUS] |= MMIO_STATUS_COMWAIT_INTR;
+    }
+}
+
+static void amd_iommu_invalidate_iotlb(AMDIOMMUState *s,
+                                       uint8_t *cmd)
+{
+    PCIDevice *dev;
+    PCIBus *bus = s->dev.bus;
+    int bus_num = pci_bus_num(bus);
+    int devfn = *(uint16_t *) cmd;
+
+    dev = pci_find_device(bus, bus_num, devfn);
+    if (dev) {
+        dma_invalidate_memory_range(&dev->dma, 0, -1);
+    }
+}
+
+static void amd_iommu_cmdbuf_exec(AMDIOMMUState *s)
+{
+    uint8_t cmd[16];
+    int type;
+
+    cpu_physical_memory_read(s->cmdbuf + s->cmdbuf_head, cmd, 16);
+    type = cmd[CMDBUF_ID_BYTE] >> CMDBUF_ID_RSHIFT;
+    switch (type) {
+    case CMD_COMPLETION_WAIT:
+        amd_iommu_completion_wait(s, cmd);
+        break;
+    case CMD_INVAL_DEVTAB_ENTRY:
+        break;
+    case CMD_INVAL_IOMMU_PAGES:
+        break;
+    case CMD_INVAL_IOTLB_PAGES:
+        amd_iommu_invalidate_iotlb(s, cmd);
+        break;
+    case CMD_INVAL_INTR_TABLE:
+        break;
+    default:
+        break;
+    }
+}
+
+static void amd_iommu_cmdbuf_run(AMDIOMMUState *s)
+{
+    uint64_t *mmio_cmdbuf_head = (uint64_t *) s->mmio_buf + MMIO_COMMAND_HEAD;
+
+    if (!s->cmdbuf_enabled) {
+        return;
+    }
+
+    /* Check if there's work to do. */
+    while (s->cmdbuf_head != s->cmdbuf_tail) {
+        /* Wrap head pointer. */
+        if (s->cmdbuf_head >= s->cmdbuf_len * CMDBUF_ENTRY_SIZE) {
+            s->cmdbuf_head = 0;
+        }
+
+        amd_iommu_cmdbuf_exec(s);
+
+        /* Increment head pointer. */
+        s->cmdbuf_head += CMDBUF_ENTRY_SIZE;
+    }
+
+    *mmio_cmdbuf_head = cpu_to_le64(s->cmdbuf_head);
+}
+
+static uint32_t amd_iommu_mmio_buf_read(AMDIOMMUState *s,
+                                        size_t offset,
+                                        size_t size)
+{
+    ssize_t i;
+    uint32_t ret;
+
+    if (!size) {
+        return 0;
+    }
+
+    ret = s->mmio_buf[offset + size - 1];
+    for (i = size - 2; i >= 0; i--) {
+        ret <<= 8;
+        ret |= s->mmio_buf[offset + i];
+    }
+
+    return ret;
+}
+
+static void amd_iommu_mmio_buf_write(AMDIOMMUState *s,
+                                     size_t offset,
+                                     size_t size,
+                                     uint32_t val)
+{
+    size_t i;
+
+    for (i = 0; i < size; i++) {
+        s->mmio_buf[offset + i] = val & 0xFF;
+        val >>= 8;
+    }
+}
+
+static void amd_iommu_update_mmio(AMDIOMMUState *s,
+                                  target_phys_addr_t addr)
+{
+    size_t reg = addr & ~0x07;
+    uint64_t *base = (uint64_t *) &s->mmio_buf[reg];
+    uint64_t val = le64_to_cpu(*base);
+
+    switch (reg) {
+    case MMIO_CONTROL:
+        s->enabled              = !!(val & MMIO_CONTROL_IOMMUEN);
+        s->ats_enabled          = !!(val & MMIO_CONTROL_HTTUNEN);
+        s->evtlog_enabled       = s->enabled &&
+                                  !!(val & MMIO_CONTROL_EVENTLOGEN);
+        s->evtlog_intr          = !!(val & MMIO_CONTROL_EVENTINTEN);
+        s->completion_wait_intr = !!(val & MMIO_CONTROL_COMWAITINTEN);
+        s->cmdbuf_enabled       = s->enabled &&
+                                  !!(val & MMIO_CONTROL_CMDBUFEN);
+
+        /* Update status flags depending on the control register. */
+        if (s->cmdbuf_enabled) {
+            s->mmio_buf[MMIO_STATUS] |= MMIO_STATUS_CMDBUF_RUN;
+        } else {
+            s->mmio_buf[MMIO_STATUS] &= ~MMIO_STATUS_CMDBUF_RUN;
+        }
+        if (s->evtlog_enabled) {
+            s->mmio_buf[MMIO_STATUS] |= MMIO_STATUS_EVTLOG_RUN;
+        } else {
+            s->mmio_buf[MMIO_STATUS] &= ~MMIO_STATUS_EVTLOG_RUN;
+        }
+
+        amd_iommu_cmdbuf_run(s);
+        break;
+    case MMIO_DEVICE_TABLE:
+        s->devtab = (target_phys_addr_t) (val & MMIO_DEVTAB_BASE_MASK);
+        s->devtab_len = ((val & MMIO_DEVTAB_SIZE_MASK) + 1) *
+                         (MMIO_DEVTAB_SIZE_UNIT / MMIO_DEVTAB_ENTRY_SIZE);
+        break;
+    case MMIO_COMMAND_BASE:
+        s->cmdbuf = (target_phys_addr_t) (val & MMIO_CMDBUF_BASE_MASK);
+        s->cmdbuf_len = 1UL << (s->mmio_buf[MMIO_CMDBUF_SIZE_BYTE] &
+                                MMIO_CMDBUF_SIZE_MASK);
+
+        /* We must reset the head and tail pointers. */
+        s->cmdbuf_head = s->cmdbuf_tail = 0;
+        memset(s->mmio_buf + MMIO_COMMAND_HEAD, 0, 8);
+        memset(s->mmio_buf + MMIO_COMMAND_TAIL, 0, 8);
+        break;
+    case MMIO_COMMAND_HEAD:
+        s->cmdbuf_head = val & MMIO_CMDBUF_HEAD_MASK;
+        amd_iommu_cmdbuf_run(s);
+        break;
+    case MMIO_COMMAND_TAIL:
+        s->cmdbuf_tail = val & MMIO_CMDBUF_TAIL_MASK;
+        amd_iommu_cmdbuf_run(s);
+        break;
+    case MMIO_EVENT_BASE:
+        s->evtlog = (target_phys_addr_t) (val & MMIO_EVTLOG_BASE_MASK);
+        s->evtlog_len = 1UL << (s->mmio_buf[MMIO_EVTLOG_SIZE_BYTE] &
+                                MMIO_EVTLOG_SIZE_MASK);
+        break;
+    case MMIO_EVENT_HEAD:
+        s->evtlog_head = val & MMIO_EVTLOG_HEAD_MASK;
+        break;
+    case MMIO_EVENT_TAIL:
+        s->evtlog_tail = val & MMIO_EVTLOG_TAIL_MASK;
+        break;
+    case MMIO_EXCL_BASE:
+        s->excl_base = (target_phys_addr_t) (val & MMIO_EXCL_BASE_MASK);
+        s->excl_enabled = val & MMIO_EXCL_ENABLED_MASK;
+        s->excl_allow = val & MMIO_EXCL_ALLOW_MASK;
+        break;
+    case MMIO_EXCL_LIMIT:
+        s->excl_limit = (target_phys_addr_t) ((val & MMIO_EXCL_LIMIT_MASK) |
+                                              MMIO_EXCL_LIMIT_LOW);
+        break;
+    default:
+        break;
+    }
+}
+
+static uint32_t amd_iommu_mmio_readb(void *opaque, target_phys_addr_t addr)
+{
+    AMDIOMMUState *s = opaque;
+
+    return amd_iommu_mmio_buf_read(s, addr, 1);
+}
+
+static uint32_t amd_iommu_mmio_readw(void *opaque, target_phys_addr_t addr)
+{
+    AMDIOMMUState *s = opaque;
+
+    return amd_iommu_mmio_buf_read(s, addr, 2);
+}
+
+static uint32_t amd_iommu_mmio_readl(void *opaque, target_phys_addr_t addr)
+{
+    AMDIOMMUState *s = opaque;
+
+    return amd_iommu_mmio_buf_read(s, addr, 4);
+}
+
+static void amd_iommu_mmio_writeb(void *opaque,
+                                  target_phys_addr_t addr,
+                                  uint32_t val)
+{
+    AMDIOMMUState *s = opaque;
+
+    amd_iommu_mmio_buf_write(s, addr, 1, val);
+    amd_iommu_update_mmio(s, addr);
+}
+
+static void amd_iommu_mmio_writew(void *opaque,
+                                  target_phys_addr_t addr,
+                                  uint32_t val)
+{
+    AMDIOMMUState *s = opaque;
+
+    amd_iommu_mmio_buf_write(s, addr, 2, val);
+    amd_iommu_update_mmio(s, addr);
+}
+
+static void amd_iommu_mmio_writel(void *opaque,
+                                  target_phys_addr_t addr,
+                                  uint32_t val)
+{
+    AMDIOMMUState *s = opaque;
+
+    amd_iommu_mmio_buf_write(s, addr, 4, val);
+    amd_iommu_update_mmio(s, addr);
+}
+
+static CPUReadMemoryFunc * const amd_iommu_mmio_read[] = {
+    amd_iommu_mmio_readb,
+    amd_iommu_mmio_readw,
+    amd_iommu_mmio_readl,
+};
+
+static CPUWriteMemoryFunc * const amd_iommu_mmio_write[] = {
+    amd_iommu_mmio_writeb,
+    amd_iommu_mmio_writew,
+    amd_iommu_mmio_writel,
+};
+
+static void amd_iommu_enable_mmio(AMDIOMMUState *s)
+{
+    target_phys_addr_t addr;
+    uint8_t *capab_wmask = s->dev.wmask + s->capab_offset;
+
+    s->mmio_index = cpu_register_io_memory(amd_iommu_mmio_read,
+                                           amd_iommu_mmio_write,
+                                           s, DEVICE_LITTLE_ENDIAN);
+    if (s->mmio_index < 0) {
+        return;
+    }
+
+    addr = le64_to_cpu(*(uint64_t *) &s->capab[CAPAB_BAR_LOW]) & CAPAB_BAR_MASK;
+    cpu_register_physical_memory(addr, MMIO_SIZE, s->mmio_index);
+
+    s->mmio_addr = addr;
+    s->mmio_enabled = 1;
+
+    /* Further changes to the capability are prohibited. */
+    memset(capab_wmask + CAPAB_BAR_LOW, 0x00, CAPAB_REG_SIZE);
+    memset(capab_wmask + CAPAB_BAR_HIGH, 0x00, CAPAB_REG_SIZE);
+}
+
+static void amd_iommu_write_capab(PCIDevice *dev,
+                                  uint32_t addr, uint32_t val, int len)
+{
+    AMDIOMMUState *s = DO_UPCAST(AMDIOMMUState, dev, dev);
+
+    pci_default_write_config(dev, addr, val, len);
+
+    if (!s->mmio_enabled && s->capab[CAPAB_BAR_LOW] & 0x1) {
+        amd_iommu_enable_mmio(s);
+    }
+}
+
+static void amd_iommu_reset(DeviceState *dev)
+{
+    AMDIOMMUState *s = DO_UPCAST(AMDIOMMUState, dev.qdev, dev);
+    unsigned char *capab = s->capab;
+    uint8_t *capab_wmask = s->dev.wmask + s->capab_offset;
+
+    s->enabled      = 0;
+    s->ats_enabled  = 0;
+    s->mmio_enabled = 0;
+
+    capab[CAPAB_REV_TYPE]  = CAPAB_REV_TYPE;
+    capab[CAPAB_FLAGS]     = CAPAB_FLAGS;
+    capab[CAPAB_BAR_LOW]   = 0;
+    capab[CAPAB_BAR_HIGH]  = 0;
+    capab[CAPAB_RANGE]     = 0;
+    *((uint32_t *) &capab[CAPAB_MISC]) = cpu_to_le32(CAPAB_INIT_MISC);
+
+    /* Changes to the capability are allowed after system reset. */
+    memset(capab_wmask + CAPAB_BAR_LOW, 0xFF, CAPAB_REG_SIZE);
+    memset(capab_wmask + CAPAB_BAR_HIGH, 0xFF, CAPAB_REG_SIZE);
+
+    memset(s->mmio_buf, 0, MMIO_SIZE);
+    s->mmio_buf[MMIO_CMDBUF_SIZE_BYTE] = MMIO_CMDBUF_DEFAULT_SIZE;
+    s->mmio_buf[MMIO_EVTLOG_SIZE_BYTE] = MMIO_EVTLOG_DEFAULT_SIZE;
+}
+
+static void amd_iommu_log_event(AMDIOMMUState *s, AMDIOMMUEvent *evt)
+{
+    if (!s->evtlog_enabled ||
+        (s->mmio_buf[MMIO_STATUS] | MMIO_STATUS_EVTLOG_OF)) {
+        return;
+    }
+
+    if (s->evtlog_tail >= s->evtlog_len) {
+        s->mmio_buf[MMIO_STATUS] |= MMIO_STATUS_EVTLOG_OF;
+    }
+
+    cpu_physical_memory_write(s->evtlog + s->evtlog_tail,
+                              (uint8_t *) evt, EVENT_LEN);
+
+    s->evtlog_tail += EVENT_LEN;
+    s->mmio_buf[MMIO_STATUS] |= MMIO_STATUS_EVTLOG_INTR;
+}
+
+static void amd_iommu_page_fault(AMDIOMMUState *s,
+                                 int devfn,
+                                 unsigned domid,
+                                 target_phys_addr_t addr,
+                                 int present,
+                                 int is_write)
+{
+    AMDIOMMUEvent evt;
+    unsigned info;
+    uint16_t status;
+
+    evt.devfn = cpu_to_le16(devfn);
+    evt.reserved = 0;
+    evt.domid = cpu_to_le16(domid);
+    evt.addr = cpu_to_le64(addr);
+
+    info = EVENT_IOPF;
+    if (present) {
+        info |= EVENT_IOPF_PR;
+    }
+    if (is_write) {
+        info |= EVENT_IOPF_RW;
+    }
+    evt.info = cpu_to_le16(info);
+
+    amd_iommu_log_event(s, &evt);
+
+    /*
+     * Signal a target abort.
+     *
+     * FIXME: There should be a way to turn this off when acked.
+     */
+    status = pci_get_word(s->dev.config + PCI_STATUS);
+    pci_set_word(s->dev.config + PCI_STATUS,
+                 status | PCI_STATUS_SIG_TARGET_ABORT);
+}
+
+static inline uint64_t amd_iommu_get_perms(uint64_t entry)
+{
+    return (entry & (DEV_PERM_READ | DEV_PERM_WRITE)) >> DEV_PERM_SHIFT;
+}
+
+static inline AMDIOMMUState *amd_iommu_dma_to_state(DMADevice *dev)
+{
+    PCIDevice *pci_dev = DO_UPCAST(PCIDevice, qdev, dev->mmu->iommu);
+
+    return DO_UPCAST(AMDIOMMUState, dev, pci_dev);
+}
+
+static int amd_iommu_translate(DMADevice *dev,
+                               dma_addr_t addr,
+                               dma_addr_t *paddr,
+                               dma_addr_t *len,
+                               int is_write)
+{
+    PCIDevice *pci_dev = container_of(dev, PCIDevice, dma);
+    PCIDevice *iommu_dev = DO_UPCAST(PCIDevice, qdev, dev->mmu->iommu);
+    AMDIOMMUState *s = DO_UPCAST(AMDIOMMUState, dev, iommu_dev);
+    int devfn, present;
+    target_phys_addr_t entry_addr, pte_addr;
+    uint64_t entry[4], pte, page_offset, pte_perms;
+    unsigned level, domid;
+    unsigned perms;
+
+    if (!s->enabled) {
+        goto no_translation;
+    }
+
+    /*
+     * It's okay to check for either read or write permissions
+     * even for memory maps, since we don't support R/W maps.
+     */
+    perms = is_write ? IOMMU_PERM_WRITE : IOMMU_PERM_READ;
+
+    /* Get device table entry. */
+    devfn = pci_dev->devfn;
+    entry_addr = s->devtab + devfn * DEVTAB_ENTRY_SIZE;
+    cpu_physical_memory_read(entry_addr, (uint8_t *) entry, 32);
+
+    pte = entry[0];
+    if (!(pte & DEV_VALID) || !(pte & DEV_TRANSLATION_VALID)) {
+        goto no_translation;
+    }
+    domid = entry[1] & DEV_DOMAIN_ID_MASK;
+    level = (pte >> DEV_MODE_RSHIFT) & DEV_MODE_MASK;
+    while (level > 0) {
+        /*
+         * Check permissions: the bitwise
+         * implication perms -> entry_perms must be true.
+         */
+        pte_perms = amd_iommu_get_perms(pte);
+        present = pte & 1;
+        if (!present || perms != (perms & pte_perms)) {
+            amd_iommu_page_fault(s, devfn, domid, addr,
+                                 present, !!(perms & IOMMU_PERM_WRITE));
+            return -EPERM;
+        }
+
+        /* Go to the next lower level. */
+        pte_addr = pte & DEV_PT_ROOT_MASK;
+        pte_addr += ((addr >> (3 + 9 * level)) & 0x1FF) << 3;
+        pte = ldq_phys(pte_addr);
+        level = (pte >> DEV_MODE_RSHIFT) & DEV_MODE_MASK;
+    }
+    page_offset = addr & 4095;
+    *paddr = (pte & DEV_PT_ROOT_MASK) + page_offset;
+    *len = 4096 - page_offset;
+
+    return 0;
+
+no_translation:
+    *paddr = addr;
+    *len = -1;
+    return 0;
+}
+
+static int amd_iommu_pci_initfn(PCIDevice *dev)
+{
+    AMDIOMMUState *s = DO_UPCAST(AMDIOMMUState, dev, dev);
+
+    pci_config_set_vendor_id(s->dev.config, PCI_VENDOR_ID_AMD);
+    pci_config_set_device_id(s->dev.config, PCI_DEVICE_ID_AMD_IOMMU);
+    pci_config_set_class(s->dev.config, PCI_CLASS_SYSTEM_IOMMU);
+
+    /* Secure Device capability */
+    s->capab_offset = pci_add_capability(&s->dev,
+                                         PCI_CAP_ID_SEC, 0, CAPAB_SIZE);
+    s->capab = s->dev.config + s->capab_offset;
+    dev->config_write = amd_iommu_write_capab;
+
+    /* Allocate backing space for the MMIO registers. */
+    s->mmio_buf = qemu_malloc(MMIO_SIZE);
+
+    pci_register_iommu(dev, amd_iommu_translate);
+
+    return 0;
+}
+
+static const VMStateDescription vmstate_amd_iommu = {
+    .name                       = "amd-iommu",
+    .version_id                 = 1,
+    .minimum_version_id         = 1,
+    .minimum_version_id_old     = 1,
+    .fields                     = (VMStateField []) {
+        VMSTATE_PCI_DEVICE(dev, AMDIOMMUState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static PCIDeviceInfo amd_iommu_pci_info = {
+    .qdev.name    = "amd-iommu",
+    .qdev.desc    = "AMD IOMMU",
+    .qdev.size    = sizeof(AMDIOMMUState),
+    .qdev.reset   = amd_iommu_reset,
+    .qdev.vmsd    = &vmstate_amd_iommu,
+    .init         = amd_iommu_pci_initfn,
+};
+
+static void amd_iommu_register(void)
+{
+    pci_qdev_register(&amd_iommu_pci_info);
+}
+
+device_init(amd_iommu_register);
diff --git a/hw/pc.c b/hw/pc.c
index 6939c04..b93949c 100644
--- a/hw/pc.c
+++ b/hw/pc.c
@@ -1161,6 +1161,8 @@ void pc_pci_device_init(PCIBus *pci_bus)
     int max_bus;
     int bus;
 
+    pci_create_simple(pci_bus, -1, "amd-iommu");
+
     max_bus = drive_get_max_bus(IF_SCSI);
     for (bus = 0; bus <= max_bus; bus++) {
         pci_create_simple(pci_bus, -1, "lsi53c895a");
-- 
1.7.3.4

  parent reply	other threads:[~2011-06-01  1:38 UTC|newest]

Thread overview: 48+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-06-01  1:38 [RFC PATCH 00/13] AMD IOMMU emulation patches, another try Eduard - Gabriel Munteanu
2011-06-01  1:38 ` [Qemu-devel] " Eduard - Gabriel Munteanu
2011-06-01  1:38 ` [RFC PATCH 01/13] Generic DMA memory access interface Eduard - Gabriel Munteanu
2011-06-01  1:38   ` [Qemu-devel] " Eduard - Gabriel Munteanu
2011-06-01 14:01   ` Richard Henderson
2011-06-01 14:01     ` Richard Henderson
2011-06-01 14:29     ` Avi Kivity
2011-06-01 14:29       ` Avi Kivity
2011-06-01 15:16       ` Richard Henderson
2011-06-01 15:16         ` Richard Henderson
2011-06-02 10:22         ` David Gibson
2011-06-02 10:22           ` David Gibson
2011-06-01 14:52     ` Eduard - Gabriel Munteanu
2011-06-01 14:52       ` Eduard - Gabriel Munteanu
2011-06-01 15:09       ` Richard Henderson
2011-06-01 15:09         ` Richard Henderson
2011-06-01 15:35         ` Eduard - Gabriel Munteanu
2011-06-01 15:35           ` Eduard - Gabriel Munteanu
2011-06-01 15:45           ` Richard Henderson
2011-06-01 15:45             ` Richard Henderson
2011-06-02  9:38             ` David Gibson
2011-06-02  9:38               ` David Gibson
2011-06-01  1:38 ` [RFC PATCH 02/13] pci: add IOMMU support via the generic DMA layer Eduard - Gabriel Munteanu
2011-06-01  1:38   ` [Qemu-devel] " Eduard - Gabriel Munteanu
2011-06-01  1:38 ` Eduard - Gabriel Munteanu [this message]
2011-06-01  1:38   ` [Qemu-devel] [RFC PATCH 03/13] AMD IOMMU emulation Eduard - Gabriel Munteanu
2011-06-01  1:38 ` [RFC PATCH 04/13] ide: use the DMA memory access interface for PCI IDE controllers Eduard - Gabriel Munteanu
2011-06-01  1:38   ` [Qemu-devel] " Eduard - Gabriel Munteanu
2011-06-01  1:38 ` [RFC PATCH 05/13] rtl8139: use the DMA memory access interface Eduard - Gabriel Munteanu
2011-06-01  1:38   ` [Qemu-devel] " Eduard - Gabriel Munteanu
2011-06-01  1:38 ` [RFC PATCH 06/13] eepro100: " Eduard - Gabriel Munteanu
2011-06-01  1:38   ` [Qemu-devel] " Eduard - Gabriel Munteanu
2011-06-01  1:38 ` [RFC PATCH 07/13] ac97: " Eduard - Gabriel Munteanu
2011-06-01  1:38   ` [Qemu-devel] " Eduard - Gabriel Munteanu
2011-06-01  1:38 ` [RFC PATCH 08/13] es1370: " Eduard - Gabriel Munteanu
2011-06-01  1:38   ` [Qemu-devel] " Eduard - Gabriel Munteanu
2011-06-01  1:38 ` [RFC PATCH 09/13] e1000: " Eduard - Gabriel Munteanu
2011-06-01  1:38   ` [Qemu-devel] " Eduard - Gabriel Munteanu
2011-06-01  1:38 ` [RFC PATCH 10/13] lsi53c895a: " Eduard - Gabriel Munteanu
2011-06-01  1:38   ` [Qemu-devel] " Eduard - Gabriel Munteanu
2011-06-01  1:38 ` [RFC PATCH 11/13] pcnet: " Eduard - Gabriel Munteanu
2011-06-01  1:38   ` [Qemu-devel] " Eduard - Gabriel Munteanu
2011-06-01  1:38 ` [RFC PATCH 12/13] usb-uhci: " Eduard - Gabriel Munteanu
2011-06-01  1:38   ` [Qemu-devel] " Eduard - Gabriel Munteanu
2011-06-01  1:38 ` [RFC PATCH 13/13] usb-ohci: " Eduard - Gabriel Munteanu
2011-06-01  1:38   ` [Qemu-devel] " Eduard - Gabriel Munteanu
2011-06-01 18:49 ` [Qemu-devel] [RFC PATCH 00/13] AMD IOMMU emulation patches, another try Richard Henderson
2011-06-01 18:49   ` Richard Henderson

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1306892315-7306-4-git-send-email-eduard.munteanu@linux360.ro \
    --to=eduard.munteanu@linux360.ro \
    --cc=agraf@suse.de \
    --cc=aik@ozlabs.ru \
    --cc=aliguori@us.ibm.com \
    --cc=anthony@codemonkey.ws \
    --cc=av1474@comtv.ru \
    --cc=avi@redhat.com \
    --cc=benh@kernel.crashing.org \
    --cc=blauwirbel@gmail.com \
    --cc=david@gibson.dropbear.id.au \
    --cc=dwg@au1.ibm.com \
    --cc=joro@8bytes.org \
    --cc=kevin@koconnor.net \
    --cc=kvm@vger.kernel.org \
    --cc=mst@redhat.com \
    --cc=paul@codesourcery.com \
    --cc=qemu-devel@nongnu.org \
    --cc=rth@twiddle.net \
    --cc=seabios@seabios.org \
    --cc=yamahata@valinux.co.jp \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.