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

[Xen-changelog] [qemu-xen-unstable] ioemu: passthrough: PCI IO space multiplex.



commit c675fef021550faeafa60577abd18347a172f220
Author: Ian Jackson <ian.jackson@xxxxxxxxxxxxx>
Date:   Thu May 28 18:34:03 2009 +0100

    ioemu: passthrough: PCI IO space multiplex.
    
    use PCI IO space multiplexer driver and command register emulation twist
    
    This depends on xen-unstable c/s
     19666:8bf58f09e45a2cb021df59ddfe518ebdc6334b78
    
    Signed-off-by: Isaku Yamahata <yamahata@xxxxxxxxxxxxx>
---
 hw/iomulti.h      |   51 ++++++++++++++
 hw/pass-through.c |  192 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/pass-through.h |   10 +++
 3 files changed, 253 insertions(+), 0 deletions(-)

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 51a39db..bd3388a 100644
--- a/hw/pass-through.c
+++ b/hw/pass-through.c
@@ -88,7 +88,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;
@@ -1051,6 +1054,170 @@ 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;
+}
+
+#define DEFINE_PT_IOMUL_WRITE(size)                                     \
+    static void pt_iomul_ioport_write ## size                           \
+    (void *opaque, uint32_t addr, uint32_t val)                         \
+    {                                                                   \
+        pt_iomul_ioport_write((struct pt_dev *)opaque, addr, val,       \
+                              (size));                                  \
+    }
+
+DEFINE_PT_IOMUL_WRITE(1)
+DEFINE_PT_IOMUL_WRITE(2)
+DEFINE_PT_IOMUL_WRITE(4)
+
+#define DEFINE_PT_IOMUL_READ(size)                                      \
+    static uint32_t pt_iomul_ioport_read ## size                        \
+    (void *opaque, uint32_t addr)                                       \
+    {                                                                   \
+        return pt_iomul_ioport_read((struct pt_dev *)opaque, addr,      \
+                                    (size));                            \
+    }
+
+DEFINE_PT_IOMUL_READ(1)
+DEFINE_PT_IOMUL_READ(2)
+DEFINE_PT_IOMUL_READ(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)
+{
+    /* 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)
@@ -1070,6 +1237,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 */
@@ -2868,6 +3042,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;
@@ -3014,6 +3190,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 = ~reg->ro_mask & valid_mask;
@@ -3044,6 +3222,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\n", __func__, strerror(errno));
+    }
     return 0;
 }
 
@@ -3662,6 +3846,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\n", __func__, strerror(errno));
+    }
 
     if (!ptdev->machine_irq)
         *value |= PCI_COMMAND_DISABLE_INTx;
@@ -3845,6 +4034,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;
@@ -4001,6 +4191,8 @@ static int unregister_real_device(int slot)
     /* unregister real device's MMIO/PIO BARs */
     pt_unregister_regions(assigned_device);
 
+    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 a503e80..a87e5b8 100644
--- a/hw/pass-through.h
+++ b/hw/pass-through.h
@@ -226,8 +226,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 {
--
generated by git-patchbot for /home/xen/git/qemu-xen-unstable.git

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


 


Rackspace

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