[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
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |