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 5/5] take 2: PCIe IO space multiplexing: ioemu part

To: xen-devel@xxxxxxxxxxxxxxxxxxx
Subject: [Xen-devel] [PATCH 5/5] take 2: PCIe IO space multiplexing: ioemu part
From: Isaku Yamahata <yamahata@xxxxxxxxxxxxx>
Date: Fri, 17 Apr 2009 17:09:20 +0900
Cc: shimada-yxb@xxxxxxxxxxxxxxx, Ian.Jackson@xxxxxxxxxxxxx, Isaku Yamahata <yamahata@xxxxxxxxxxxxx>
Delivery-date: Fri, 17 Apr 2009 01:14:58 -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 f692fc6162eb9da1f0a628f3f97124e19146a404 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 |  199 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/pass-through.h |   10 +++
 3 files changed, 260 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 710acbf..1377da2 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,176 @@ 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)
+{
+    /* 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 +1243,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 */
@@ -1800,6 +1980,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];
@@ -2853,6 +3034,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;
@@ -2999,6 +3182,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;
@@ -3029,6 +3214,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;
 }
 
@@ -3624,6 +3815,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;
@@ -3807,6 +4003,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;
@@ -3970,6 +4167,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 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>
  • [Xen-devel] [PATCH 5/5] take 2: PCIe IO space multiplexing: ioemu part, Isaku Yamahata <=