Mailing List Archive

[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@valinux.co.jp>
---
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