WARNING - OLD ARCHIVES

This is an archived copy of the Xen.org mailing list, which we have preserved to ensure that existing links to archives are not broken. The live archive, which contains the latest emails, can be found at http://lists.xen.org/
   
 
 
Xen 
 
Home Products Support Community News
 
   
 

xen-devel

[Xen-devel] [PATCH 6/6] pcie io space multiplex: ioemu part: enhance pas

To: xen-devel@xxxxxxxxxxxxxxxxxxx
Subject: [Xen-devel] [PATCH 6/6] pcie io space multiplex: ioemu part: enhance passthrough
From: Isaku Yamahata <yamahata@xxxxxxxxxxxxx>
Date: Thu, 28 May 2009 12:46:48 +0900
Cc: shimada-yxb@xxxxxxxxxxxxxxx, Ian.Jackson@xxxxxxxxxxxxx, Isaku Yamahata <yamahata@xxxxxxxxxxxxx>
Delivery-date: Wed, 27 May 2009 20:52:25 -0700
Envelope-to: www-data@xxxxxxxxxxxxxxxxxxx
List-help: <mailto:xen-devel-request@lists.xensource.com?subject=help>
List-id: Xen developer discussion <xen-devel.lists.xensource.com>
List-post: <mailto:xen-devel@lists.xensource.com>
List-subscribe: <http://lists.xensource.com/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=subscribe>
List-unsubscribe: <http://lists.xensource.com/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=unsubscribe>
Sender: xen-devel-bounces@xxxxxxxxxxxxxxxxxxx
User-agent: Mutt/1.5.18 (2008-05-17)
>From 1d01986a15f2fac19663d512d57aee24fadef8a5 Mon Sep 17 00:00:00 2001
From: Isaku Yamahata <yamahata@xxxxxxxxxxxxx>
Date: Thu, 26 Mar 2009 15:50:58 +0900
Subject: [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 |  192 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/pass-through.h |   10 +++
 3 files changed, 253 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 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 {
-- 
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
<Prev in Thread] Current Thread [Next in Thread>