[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH 3/3] PCIe IO space multiplexing: ioemu part



[PATCH] ioemu: passthrough: PCI IO space multiplex.

use PCI IO space multiplexer driver and command register emulation twist

Signed-off-by: Isaku Yamahata <yamahata@xxxxxxxxxxxxx>
---
 hw/iomulti.h      |   51 +++++++++++++
 hw/pass-through.c |  212 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/pass-through.h |   10 +++
 3 files changed, 273 insertions(+), 0 deletions(-)
 create mode 100644 hw/iomulti.h

diff --git a/hw/iomulti.h b/hw/iomulti.h
new file mode 100644
index 0000000..b76b032
--- /dev/null
+++ b/hw/iomulti.h
@@ -0,0 +1,51 @@
+#ifndef PCI_IOMULTI_H
+#define PCI_IOMULTI_H
+/*
+ * 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
+ * (at your option) 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, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ * Copyright (c) 2009 Isaku Yamahata
+ *                    VA Linux Systems Japan K.K.
+ *
+ */
+
+struct pci_iomul_setup {
+       uint16_t        segment;
+       uint8_t         bus;
+       uint8_t         dev;
+       uint8_t         func;
+};
+
+struct pci_iomul_in {
+       uint8_t         bar;
+       uint64_t        offset;
+
+       uint8_t         size;
+       uint32_t        value;
+};
+
+struct pci_iomul_out {
+       uint8_t         bar;
+       uint64_t        offset;
+
+       uint8_t         size;
+       uint32_t        value;
+};
+
+#define PCI_IOMUL_SETUP                _IOW ('P', 0, struct pci_iomul_setup)
+#define PCI_IOMUL_DISABLE_IO   _IO  ('P', 1)
+#define PCI_IOMUL_IN           _IOWR('P', 2, struct pci_iomul_in)
+#define PCI_IOMUL_OUT          _IOW ('P', 3, struct pci_iomul_out)
+
+#endif /* PCI_IOMULTI_H */
diff --git a/hw/pass-through.c b/hw/pass-through.c
index 95b4a47..1346f20 100644
--- a/hw/pass-through.c
+++ b/hw/pass-through.c
@@ -27,7 +27,10 @@
 #include "pci/pci.h"
 #include "pt-msi.h"
 #include "qemu-xen.h"
+#include "iomulti.h"
+
 #include <unistd.h>
+#include <sys/ioctl.h>
 
 struct php_dev {
     struct pt_dev *pt_dev;
@@ -990,6 +993,189 @@ static void pt_iomem_map(PCIDevice *d, int i, uint32_t 
e_phys, uint32_t e_size,
     }
 }
 
+#define PCI_IOMUL_DEV_PATH      "/dev/xen/pci_iomul"
+static void pt_iomul_init(struct pt_dev *assigned_device,
+                          uint8_t r_bus, uint8_t r_dev, uint8_t r_func)
+{
+    int fd = PCI_IOMUL_INVALID_FD;
+    struct pci_iomul_setup setup = {
+        .segment = 0,
+        .bus = r_bus,
+        .dev = r_dev,
+        .func = r_func,
+    };
+
+    fd = open(PCI_IOMUL_DEV_PATH, O_RDWR);
+    if ( fd < 0 ) {
+        PT_LOG("Error: %s can't open file %s: %s: 0x%x:0x%x.0x%x\n",
+               __func__, PCI_IOMUL_DEV_PATH, strerror(errno),
+               r_bus, r_dev, r_func);
+        fd = PCI_IOMUL_INVALID_FD;
+    }
+
+    if ( fd >= 0 && ioctl(fd, PCI_IOMUL_SETUP, &setup) )
+    {
+        PT_LOG("Error: %s: %s: setup io multiplexing failed! 0x%x:0x%x.0x%x\n",
+               __func__, strerror(errno), r_bus, r_dev, r_func);
+        close(fd);
+        fd = PCI_IOMUL_INVALID_FD;
+    }
+
+    assigned_device->fd = fd;
+    if (fd != PCI_IOMUL_INVALID_FD)
+        PT_LOG("io mul: 0x%x:0x%x.0x%x\n", r_bus, r_dev, r_func);
+}
+
+static void pt_iomul_free(struct pt_dev *assigned_device)
+{
+    if ( !pt_is_iomul(assigned_device) )
+        return;
+
+    close(assigned_device->fd);
+    assigned_device->fd = PCI_IOMUL_INVALID_FD;
+}
+
+static void pt_iomul_get_bar_offset(struct pt_dev *assigned_device,
+                                    uint32_t addr,
+                                    uint8_t *bar, uint64_t *offset)
+{
+    for ( *bar = 0; *bar < PCI_BAR_ENTRIES; (*bar)++ )
+    {
+        const struct pt_region* r = &assigned_device->bases[*bar];
+        if ( r->bar_flag != PT_BAR_FLAG_IO )
+            continue;
+
+        if ( r->e_physbase <= addr && addr < r->e_physbase + r->e_size )
+        {
+            *offset = addr - r->e_physbase;
+            return;
+        }
+    }
+}
+
+static void pt_iomul_ioport_write(struct pt_dev *assigned_device,
+                                  uint32_t addr, uint32_t val, int size)
+{
+    uint8_t bar;
+    uint64_t offset;
+    struct pci_iomul_out out;
+
+    if ( !assigned_device->io_enable )
+        return;
+
+    pt_iomul_get_bar_offset(assigned_device, addr, &bar, &offset);
+    if ( bar >= PCI_BAR_ENTRIES )
+    {
+        PT_LOG("error: %s: addr 0x%x val 0x%x size %d\n",
+               __func__, addr, val, size);
+        return;
+    }
+
+    out.bar = bar;
+    out.offset = offset;
+    out.size = size;
+    out.value = val;
+    if ( ioctl(assigned_device->fd, PCI_IOMUL_OUT, &out) )
+        PT_LOG("error: %s: %s addr 0x%x size %d bar %d offset 0x%lx\n",
+               __func__, strerror(errno), addr, size, bar, offset);
+}
+
+static uint32_t pt_iomul_ioport_read(struct pt_dev *assigned_device,
+                                     uint32_t addr, int size)
+{
+    uint8_t bar;
+    uint64_t offset;
+    struct pci_iomul_in in;
+
+    if ( !assigned_device->io_enable )
+        return -1;
+
+    pt_iomul_get_bar_offset(assigned_device, addr, &bar, &offset);
+    if ( bar >= PCI_BAR_ENTRIES )
+    {
+        PT_LOG("error: %s: addr 0x%x size %d\n", __func__, addr, size);
+        return -1;
+    }
+
+    in.bar = bar;
+    in.offset = offset;
+    in.size = size;
+    if ( ioctl(assigned_device->fd, PCI_IOMUL_IN, &in) )
+    {
+        PT_LOG("error: %s: %s addr 0x%x size %d bar %d offset 0x%lx\n",
+               __func__, strerror(errno), addr, size, bar, offset);
+        in.value = -1;
+    }
+
+    return in.value;
+}
+
+static void pt_iomul_ioport_write1(void *opaque, uint32_t addr, uint32_t val)
+{
+    pt_iomul_ioport_write((struct pt_dev *)opaque, addr, val, 1);
+}
+
+static void pt_iomul_ioport_write2(void *opaque, uint32_t addr, uint32_t val)
+{
+    pt_iomul_ioport_write((struct pt_dev *)opaque, addr, val, 2);
+}
+
+static void pt_iomul_ioport_write4(void *opaque, uint32_t addr, uint32_t val)
+{
+    pt_iomul_ioport_write((struct pt_dev *)opaque, addr, val, 4);
+}
+
+static uint32_t pt_iomul_ioport_read1(void *opaque, uint32_t addr)
+{
+    return pt_iomul_ioport_read((struct pt_dev *)opaque, addr, 1);
+}
+
+static uint32_t pt_iomul_ioport_read2(void *opaque, uint32_t addr)
+{
+    return pt_iomul_ioport_read((struct pt_dev *)opaque, addr, 2);
+}
+
+static uint32_t pt_iomul_ioport_read4(void *opaque, uint32_t addr)
+{
+    return pt_iomul_ioport_read((struct pt_dev *)opaque, addr, 4);
+}
+
+static void pt_iomul_ioport_map(struct pt_dev *assigned_device,
+                                uint32_t old_ebase, uint32_t e_phys,
+                                uint32_t e_size, int first_map)
+{
+    if ( !first_map && old_ebase != -1 )
+    {
+#if 0
+        /* Remove old mapping */
+        unregister_ioport_write(old_ebase, 1, e_size);
+        unregister_ioport_write(old_ebase, 2, e_size);
+        unregister_ioport_write(old_ebase, 4, e_size);
+        unregister_ioport_read(old_ebase, 1, e_size);
+        unregister_ioport_read(old_ebase, 2, e_size);
+        unregister_ioport_read(old_ebase, 4, e_size);
+#endif
+    }
+
+    /* map only valid guest address (include 0) */
+    if (e_phys != -1)
+    {
+        /* Create new mapping */
+        register_ioport_write(e_phys, e_size, 1,
+                              pt_iomul_ioport_write1, assigned_device);
+        register_ioport_write(e_phys, e_size, 2,
+                              pt_iomul_ioport_write2, assigned_device);
+        register_ioport_write(e_phys, e_size, 4,
+                              pt_iomul_ioport_write4, assigned_device);
+        register_ioport_read(e_phys, e_size, 1,
+                             pt_iomul_ioport_read1, assigned_device);
+        register_ioport_read(e_phys, e_size, 2,
+                             pt_iomul_ioport_read2, assigned_device);
+        register_ioport_read(e_phys, e_size, 4,
+                             pt_iomul_ioport_read4, assigned_device);
+    }
+}
+
 /* Being called each time a pio region has been updated */
 static void pt_ioport_map(PCIDevice *d, int i,
                           uint32_t e_phys, uint32_t e_size, int type)
@@ -1009,6 +1195,13 @@ static void pt_ioport_map(PCIDevice *d, int i,
     if ( e_size == 0 )
         return;
 
+    if ( pt_is_iomul(assigned_device) )
+    {
+        pt_iomul_ioport_map(assigned_device,
+                            old_ebase, e_phys, e_size, first_map);
+        return;
+    }
+
     if ( !first_map && old_ebase != -1 )
     {
         /* Remove old mapping */
@@ -1739,6 +1932,7 @@ static void pt_bar_mapping(struct pt_dev *ptdev, int 
io_enable, int mem_enable)
     int ret = 0;
     int i;
 
+    ptdev->io_enable = !!io_enable;
     for (i=0; i<PCI_NUM_REGIONS; i++)
     {
         r = &dev->io_regions[i];
@@ -2792,6 +2986,8 @@ static int pt_cmd_reg_read(struct pt_dev *ptdev,
 
     if ( ptdev->is_virtfn )
         emu_mask |= PCI_COMMAND_MEMORY;
+    if ( pt_is_iomul(ptdev) )
+        emu_mask |= PCI_COMMAND_IO;
 
     /* emulate word register */
     valid_emu_mask = emu_mask & valid_mask;
@@ -2938,6 +3134,8 @@ static int pt_cmd_reg_write(struct pt_dev *ptdev,
 
     if ( ptdev->is_virtfn )
         emu_mask |= PCI_COMMAND_MEMORY;
+    if ( pt_is_iomul(ptdev) )
+        emu_mask |= PCI_COMMAND_IO;
 
     /* modify emulate register */
     writable_mask = emu_mask & ~reg->ro_mask & valid_mask;
@@ -2951,6 +3149,12 @@ static int pt_cmd_reg_write(struct pt_dev *ptdev,
     pt_bar_mapping(ptdev, wr_value & PCI_COMMAND_IO,
                           wr_value & PCI_COMMAND_MEMORY);
 
+    if ( pt_is_iomul(ptdev) )
+    {
+        *value &= ~PCI_COMMAND_IO;
+        if (ioctl(ptdev->fd, PCI_IOMUL_DISABLE_IO))
+            PT_LOG("error: %s: %s", __func__, strerror(errno));
+    }
     return 0;
 }
 
@@ -3542,6 +3746,11 @@ static int pt_cmd_reg_restore(struct pt_dev *ptdev,
      */
     restorable_mask = reg->emu_mask & ~PCI_COMMAND_FAST_BACK;
     *value = PT_MERGE_VALUE(*value, dev_value, restorable_mask);
+    if ( pt_is_iomul(ptdev) ) {
+        *value &= ~PCI_COMMAND_IO;
+        if (ioctl(ptdev->fd, PCI_IOMUL_DISABLE_IO))
+            PT_LOG("error: %s: %s", __func__, strerror(errno));
+    }
 
     return 0;
 }
@@ -3720,6 +3929,7 @@ static struct pt_dev * register_real_device(PCIBus *e_bus,
     assigned_device->msi_trans_cap = msi_translate;
     assigned_device->power_mgmt = power_mgmt;
     assigned_device->is_virtfn = pt_dev_is_virtfn(pci_dev);
+    pt_iomul_init(assigned_device, r_bus, r_dev, r_func);
 
     /* Assign device */
     machine_bdf.reg = 0;
@@ -3870,6 +4080,8 @@ static int unregister_real_device(int slot)
     if ( (rc = xc_deassign_device(xc_handle, domid, bdf)) != 0)
         PT_LOG("Error: Revoking the device failed! rc=%d\n", rc);
 
+    pt_iomul_free(assigned_device);
+
     /* mark this slot as free */
     php_dev->valid = 0;
     php_dev->pt_dev = NULL;
diff --git a/hw/pass-through.h b/hw/pass-through.h
index 3132387..220f1e9 100644
--- a/hw/pass-through.h
+++ b/hw/pass-through.h
@@ -221,8 +221,18 @@ struct pt_dev {
     unsigned power_mgmt:1;
     struct pt_pm_info *pm_state;                /* PM virtualization */
     unsigned is_virtfn:1;
+
+    /* io port multiplexing */
+#define PCI_IOMUL_INVALID_FD    (-1)
+    int fd;
+    unsigned io_enable:1;
 };
 
+static inline int pt_is_iomul(struct pt_dev *dev)
+{
+    return (dev->fd != PCI_IOMUL_INVALID_FD);
+}
+
 /* Used for formatting PCI BDF into cf8 format */
 struct pci_config_cf8 {
     union {
-- 
1.6.0.2

Attachment: 0001-ioemu-passthrough-PCI-IO-space-multiplex.patch
Description: Text Data

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.