[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-changelog] [xen-unstable] MSI 5/6: add MSI support to passthrough HVM domain
# HG changeset patch # User Keir Fraser <keir.fraser@xxxxxxxxxx> # Date 1209634383 -3600 # Node ID ad55c06c9bbc31e4e3db2944f3a1fcbf842bd4aa # Parent a0ebceaf41ff8ebda5e2478c03fd6e382ddc4b7f MSI 5/6: add MSI support to passthrough HVM domain Currently it only inercept access to MSI config space, no MSI-x support. Signed-off-by: Jiang Yunhong <yunhong.jiang@xxxxxxxxx> Signed-off-by: Shan Haitao <haitao.shan@xxxxxxxxx> --- tools/ioemu/Makefile.target | 2 tools/ioemu/hw/pass-through.c | 40 ++ tools/ioemu/hw/pass-through.h | 9 tools/ioemu/hw/pt-msi.c | 488 ++++++++++++++++++++++++++++++++++ tools/ioemu/hw/pt-msi.h | 65 ++++ tools/libxc/xc_domain.c | 26 + tools/libxc/xc_physdev.c | 32 ++ tools/libxc/xenctrl.h | 16 + xen/arch/x86/hvm/Makefile | 1 xen/arch/x86/hvm/vlapic.c | 3 xen/arch/x86/hvm/vmsi.c | 189 +++++++++++++ xen/arch/x86/hvm/vmx/intr.c | 12 xen/drivers/passthrough/io.c | 118 +++++--- xen/drivers/passthrough/iommu.c | 2 xen/drivers/passthrough/vtd/x86/vtd.c | 2 xen/include/asm-x86/hvm/io.h | 1 xen/include/asm-x86/hvm/irq.h | 13 xen/include/public/domctl.h | 7 18 files changed, 977 insertions(+), 49 deletions(-) diff -r a0ebceaf41ff -r ad55c06c9bbc tools/ioemu/Makefile.target --- a/tools/ioemu/Makefile.target Thu May 01 10:32:10 2008 +0100 +++ b/tools/ioemu/Makefile.target Thu May 01 10:33:03 2008 +0100 @@ -370,7 +370,7 @@ endif ifdef CONFIG_PASSTHROUGH LIBS+=-lpci -VL_OBJS+= pass-through.o +VL_OBJS+= pass-through.o pt-msi.o CFLAGS += -DCONFIG_PASSTHROUGH $(info *** PCI passthrough capability has been enabled ***) endif diff -r a0ebceaf41ff -r ad55c06c9bbc tools/ioemu/hw/pass-through.c --- a/tools/ioemu/hw/pass-through.c Thu May 01 10:32:10 2008 +0100 +++ b/tools/ioemu/hw/pass-through.c Thu May 01 10:33:03 2008 +0100 @@ -26,6 +26,7 @@ #include "pass-through.h" #include "pci/header.h" #include "pci/pci.h" +#include "pt-msi.h" extern FILE *logfile; @@ -286,6 +287,9 @@ static void pt_pci_write_config(PCIDevic pci_default_write_config(d, address, val, len); return; } + + if ( pt_msi_write(assigned_device, address, val, len) ) + return; /* PCI config pass-through */ if (address == 0x4) { @@ -333,6 +337,7 @@ static uint32_t pt_pci_read_config(PCIDe break; } + pt_msi_read(assigned_device, address, len, &val); exit: #ifdef PT_DEBUG_PCI_CONFIG_ACCESS @@ -445,11 +450,41 @@ static int pt_unregister_regions(struct } +uint8_t find_cap_offset(struct pci_dev *pci_dev, uint8_t cap) +{ + int id; + int max_cap = 48; + int pos = PCI_CAPABILITY_LIST; + int status; + + status = pci_read_byte(pci_dev, PCI_STATUS); + if ( (status & PCI_STATUS_CAP_LIST) == 0 ) + return 0; + + while ( max_cap-- ) + { + pos = pci_read_byte(pci_dev, pos); + if ( pos < 0x40 ) + break; + + pos &= ~3; + id = pci_read_byte(pci_dev, pos + PCI_CAP_LIST_ID); + + if ( id == 0xff ) + break; + if ( id == cap ) + return pos; + + pos += PCI_CAP_LIST_NEXT; + } + return 0; +} + struct pt_dev * register_real_device(PCIBus *e_bus, const char *e_dev_name, int e_devfn, uint8_t r_bus, uint8_t r_dev, uint8_t r_func, uint32_t machine_irq, struct pci_access *pci_access) { - int rc = -1, i; + int rc = -1, i, pos; struct pt_dev *assigned_device = NULL; struct pci_dev *pci_dev; uint8_t e_device, e_intx; @@ -510,6 +545,9 @@ struct pt_dev * register_real_device(PCI /* Initialize virtualized PCI configuration (Extended 256 Bytes) */ for ( i = 0; i < PCI_CONFIG_SIZE; i++ ) assigned_device->dev.config[i] = pci_read_byte(pci_dev, i); + + if ( (pos = find_cap_offset(pci_dev, PCI_CAP_ID_MSI)) ) + pt_msi_init(assigned_device, pos); /* Handle real device's MMIO/PIO BARs */ pt_register_regions(assigned_device); diff -r a0ebceaf41ff -r ad55c06c9bbc tools/ioemu/hw/pass-through.h --- a/tools/ioemu/hw/pass-through.h Thu May 01 10:32:10 2008 +0100 +++ b/tools/ioemu/hw/pass-through.h Thu May 01 10:33:03 2008 +0100 @@ -57,6 +57,14 @@ struct pt_region { } access; }; +struct pt_msi_info { + uint32_t flags; + int offset; + int size; + int pvec; /* physical vector used */ + int pirq; /* guest pirq corresponding */ +}; + /* This structure holds the context of the mapping functions and data that is relevant for qemu device management. @@ -65,6 +73,7 @@ struct pt_dev { PCIDevice dev; struct pci_dev *pci_dev; /* libpci struct */ struct pt_region bases[PCI_NUM_REGIONS]; /* Access regions */ + struct pt_msi_info *msi; /* MSI virtualization */ }; /* Used for formatting PCI BDF into cf8 format */ diff -r a0ebceaf41ff -r ad55c06c9bbc tools/ioemu/hw/pt-msi.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/ioemu/hw/pt-msi.c Thu May 01 10:33:03 2008 +0100 @@ -0,0 +1,488 @@ +/* + * Copyright (c) 2007, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + * + * Jiang Yunhong <yunhong.jiang@xxxxxxxxx> + * + * This file implements direct PCI assignment to a HVM guest + */ + +#include "pt-msi.h" + +#define PT_MSI_CTRL_WR_MASK_HI (0x1) +#define PT_MSI_CTRL_WR_MASK_LO (0x8E) +#define PT_MSI_DATA_WR_MASK (0x38) +int pt_msi_init(struct pt_dev *dev, int pos) +{ + uint8_t id; + uint16_t flags; + struct pci_dev *pd = dev->pci_dev; + PCIDevice *d = (struct PCIDevice *)dev; + + id = pci_read_byte(pd, pos + PCI_CAP_LIST_ID); + + if ( id != PCI_CAP_ID_MSI ) + { + PT_LOG("pt_msi_init: error id %x pos %x\n", id, pos); + return -1; + } + + dev->msi = malloc(sizeof(struct pt_msi_info)); + if ( !dev->msi ) + { + PT_LOG("pt_msi_init: error allocation pt_msi_info\n"); + return -1; + } + memset(dev->msi, 0, sizeof(struct pt_msi_info)); + + dev->msi->offset = pos; + dev->msi->size = 0xa; + + flags = pci_read_byte(pd, pos + PCI_MSI_FLAGS); + if ( flags & PCI_MSI_FLAGS_ENABLE ) + { + PT_LOG("pt_msi_init: MSI enabled already, disable first\n"); + pci_write_byte(pd, pos + PCI_MSI_FLAGS, flags & ~PCI_MSI_FLAGS_ENABLE); + } + dev->msi->flags |= (flags | MSI_FLAG_UNINIT); + + if ( flags & PCI_MSI_FLAGS_64BIT ) + dev->msi->size += 4; + if ( flags & PCI_MSI_FLAGS_PVMASK ) + dev->msi->size += 10; + + /* All register is 0 after reset, except first 4 byte */ + *(uint32_t *)(&d->config[pos]) = pci_read_long(pd, pos); + d->config[pos + 2] &= PT_MSI_CTRL_WR_MASK_LO; + d->config[pos + 3] &= PT_MSI_CTRL_WR_MASK_HI; + + return 0; +} + +/* + * setup physical msi, but didn't enable it + */ +static int pt_msi_setup(struct pt_dev *dev) +{ + int vector = -1, pirq = -1; + + if ( !(dev->msi->flags & MSI_FLAG_UNINIT) ) + { + PT_LOG("setup physical after initialized?? \n"); + return -1; + } + + if ( xc_physdev_map_pirq_msi(xc_handle, domid, MAP_PIRQ_TYPE_MSI, + vector, &pirq, + dev->pci_dev->dev << 3 | dev->pci_dev->func, + dev->pci_dev->bus, 1) ) + { + PT_LOG("error map vector %x\n", vector); + return -1; + } + dev->msi->pirq = pirq; + PT_LOG("vector %x pirq %x\n", vector, pirq); + + return 0; +} + +/* + * caller should make sure mask is supported + */ +static uint32_t get_msi_gmask(struct pt_dev *d) +{ + struct PCIDevice *pd = (struct PCIDevice *)d; + + if ( d->msi->flags & PCI_MSI_FLAGS_64BIT ) + return *(uint32_t *)(pd->config + d->msi->offset + 0xc); + else + return *(uint32_t *)(pd->config + d->msi->offset + 0x10); + +} + +static uint16_t get_msi_gdata(struct pt_dev *d) +{ + struct PCIDevice *pd = (struct PCIDevice *)d; + + if ( d->msi->flags & PCI_MSI_FLAGS_64BIT ) + return *(uint16_t *)(pd->config + d->msi->offset + PCI_MSI_DATA_64); + else + return *(uint16_t *)(pd->config + d->msi->offset + PCI_MSI_DATA_32); +} + +static uint64_t get_msi_gaddr(struct pt_dev *d) +{ + struct PCIDevice *pd = (struct PCIDevice *)d; + uint32_t addr_hi; + uint64_t addr = 0; + + addr =(uint64_t)(*(uint32_t *)(pd->config + + d->msi->offset + PCI_MSI_ADDRESS_LO)); + + if ( d->msi->flags & PCI_MSI_FLAGS_64BIT ) + { + addr_hi = *(uint32_t *)(pd->config + d->msi->offset + + PCI_MSI_ADDRESS_HI); + addr |= (uint64_t)addr_hi << 32; + } + return addr; +} + +static uint8_t get_msi_gctrl(struct pt_dev *d) +{ + struct PCIDevice *pd = (struct PCIDevice *)d; + + return *(uint8_t *)(pd->config + d->msi->offset + PCI_MSI_FLAGS); +} + +static uint32_t get_msi_gflags(struct pt_dev *d) +{ + uint32_t result = 0; + int rh, dm, dest_id, deliv_mode, trig_mode; + uint16_t data; + uint64_t addr; + + data = get_msi_gdata(d); + addr = get_msi_gaddr(d); + + rh = (addr >> MSI_ADDR_REDIRECTION_SHIFT) & 0x1; + dm = (addr >> MSI_ADDR_DESTMODE_SHIFT) & 0x1; + dest_id = (addr >> MSI_TARGET_CPU_SHIFT) & 0xff; + deliv_mode = (data >> MSI_DATA_DELIVERY_SHIFT) & 0x7; + trig_mode = (data >> MSI_DATA_TRIGGER_SHIFT) & 0x1; + + result |= dest_id | (rh << GFLAGS_SHIFT_RH) | (dm << GFLAGS_SHIFT_DM) | \ + (deliv_mode << GLFAGS_SHIFT_DELIV_MODE) | + (trig_mode << GLFAGS_SHIFT_TRG_MODE); + + return result; +} + +/* + * This may be arch different + */ +static inline uint8_t get_msi_gvec(struct pt_dev *d) +{ + return get_msi_gdata(d) & 0xff; +} + +static inline uint8_t get_msi_hvec(struct pt_dev *d) +{ + struct pci_dev *pd = d->pci_dev; + uint16_t data; + + if ( d->msi->flags & PCI_MSI_FLAGS_64BIT ) + data = pci_read_word(pd, PCI_MSI_DATA_64); + else + data = pci_read_word(pd, PCI_MSI_DATA_32); + + return data & 0xff; +} + +/* + * Update msi mapping, usually called when MSI enabled, + * except the first time + */ +static int pt_msi_update(struct pt_dev *d) +{ + PT_LOG("now update msi with pirq %x gvec %x\n", + get_msi_gvec(d), d->msi->pirq); + return xc_domain_update_msi_irq(xc_handle, domid, get_msi_gvec(d), + d->msi->pirq, get_msi_gflags(d)); +} + +static int pt_msi_enable(struct pt_dev *d, int enable) +{ + uint16_t ctrl; + struct pci_dev *pd = d->pci_dev; + + if ( !pd ) + return -1; + + ctrl = pci_read_word(pd, d->msi->offset + PCI_MSI_FLAGS); + + if ( enable ) + ctrl |= PCI_MSI_FLAGS_ENABLE; + else + ctrl &= ~PCI_MSI_FLAGS_ENABLE; + + pci_write_word(pd, d->msi->offset + PCI_MSI_FLAGS, ctrl); + return 0; +} + +static int pt_msi_control_update(struct pt_dev *d, uint16_t old_ctrl) +{ + uint16_t new_ctrl; + PCIDevice *pd = (PCIDevice *)d; + + new_ctrl = get_msi_gctrl(d); + + PT_LOG("old_ctrl %x new_Ctrl %x\n", old_ctrl, new_ctrl); + + if ( new_ctrl & PCI_MSI_FLAGS_ENABLE ) + { + if ( d->msi->flags & MSI_FLAG_UNINIT ) + { + /* Init physical one */ + PT_LOG("setup msi for dev %x\n", pd->devfn); + if ( pt_msi_setup(d) ) + { + PT_LOG("pt_msi_setup error!!!\n"); + return -1; + } + pt_msi_update(d); + + d->msi->flags &= ~MSI_FLAG_UNINIT; + d->msi->flags |= PT_MSI_MAPPED; + + /* Enable physical MSI only after bind */ + pt_msi_enable(d, 1); + } + else if ( !(old_ctrl & PCI_MSI_FLAGS_ENABLE) ) + pt_msi_enable(d, 1); + } + else if ( old_ctrl & PCI_MSI_FLAGS_ENABLE ) + pt_msi_enable(d, 0); + + /* Currently no support for multi-vector */ + if ( (new_ctrl & PCI_MSI_FLAGS_QSIZE) != 0x0 ) + PT_LOG("try to set more than 1 vector ctrl %x\n", new_ctrl); + + return 0; +} + +static int +pt_msi_map_update(struct pt_dev *d, uint32_t old_data, uint64_t old_addr) +{ + uint16_t pctrl; + uint32_t data; + uint64_t addr; + + data = get_msi_gdata(d); + addr = get_msi_gaddr(d); + + PT_LOG("old_data %x old_addr %lx data %x addr %lx\n", + old_data, old_addr, data, addr); + + if ( data != old_data || addr != old_addr ) + if ( get_msi_gctrl(d) & PCI_MSI_FLAGS_ENABLE ) + pt_msi_update(d); + + return 0; +} + +static int pt_msi_mask_update(struct pt_dev *d, uint32_t old_mask) +{ + struct pci_dev *pd = d->pci_dev; + uint32_t mask; + int offset; + + if ( !(d->msi->flags & PCI_MSI_FLAGS_PVMASK) ) + return -1; + + mask = get_msi_gmask(d); + + if ( d->msi->flags & PCI_MSI_FLAGS_64BIT ) + offset = d->msi->offset + 0xc; + else + offset = d->msi->offset + 0x10; + + if ( old_mask != mask ) + pci_write_long(pd, offset, mask); +} + +#define ACCESSED_DATA 0x2 +#define ACCESSED_MASK 0x4 +#define ACCESSED_ADDR 0x8 +#define ACCESSED_CTRL 0x10 + +int pt_msi_write(struct pt_dev *d, uint32_t addr, uint32_t val, uint32_t len) +{ + struct pci_dev *pd; + int i, cur = addr; + uint8_t value, flags = 0; + uint16_t old_ctrl = 0, old_data = 0; + uint32_t old_mask = 0; + uint64_t old_addr = 0; + PCIDevice *dev = (PCIDevice *)d; + int can_write = 1; + + if ( !d || !d->msi ) + return 0; + + if ( (addr >= (d->msi->offset + d->msi->size) ) || + (addr + len) < d->msi->offset) + return 0; + + PT_LOG("addr %x val %x len %x offset %x size %x\n", + addr, val, len, d->msi->offset, d->msi->size); + + pd = d->pci_dev; + old_ctrl = get_msi_gctrl(d); + old_addr = get_msi_gaddr(d); + old_data = get_msi_gdata(d); + + if ( d->msi->flags & PCI_MSI_FLAGS_PVMASK ) + old_mask = get_msi_gmask(d); + + for ( i = 0; i < len; i++, cur++ ) + { + int off; + uint8_t orig_value; + + if ( cur < d->msi->offset ) + continue; + else if ( cur >= (d->msi->offset + d->msi->size) ) + break; + + off = cur - d->msi->offset; + value = (val >> (i * 8)) & 0xff; + + switch ( off ) + { + case 0x0 ... 0x1: + can_write = 0; + break; + case 0x2: + case 0x3: + flags |= ACCESSED_CTRL; + + orig_value = pci_read_byte(pd, d->msi->offset + off); + + orig_value &= (off == 2) ? PT_MSI_CTRL_WR_MASK_LO: + PT_MSI_CTRL_WR_MASK_HI; + + orig_value |= value & ( (off == 2) ? ~PT_MSI_CTRL_WR_MASK_LO: + ~PT_MSI_CTRL_WR_MASK_HI); + value = orig_value; + break; + case 0x4 ... 0x7: + flags |= ACCESSED_ADDR; + /* bit 4 ~ 11 is reserved for MSI in x86 */ + if ( off == 0x4 ) + value &= 0x0f; + if ( off == 0x5 ) + value &= 0xf0; + break; + case 0x8 ... 0xb: + if ( d->msi->flags & PCI_MSI_FLAGS_64BIT ) + { + /* Up 32bit is reserved in x86 */ + flags |= ACCESSED_ADDR; + if ( value ) + PT_LOG("Write up32 addr with %x \n", value); + } + else + { + if ( off == 0xa || off == 0xb ) + can_write = 0; + else + flags |= ACCESSED_DATA; + if ( off == 0x9 ) + value &= ~PT_MSI_DATA_WR_MASK; + } + break; + case 0xc ... 0xf: + if ( d->msi->flags & PCI_MSI_FLAGS_64BIT ) + { + if ( off == 0xe || off == 0xf ) + can_write = 0; + else + { + flags |= ACCESSED_DATA; + if (off == 0xd) + value &= ~PT_MSI_DATA_WR_MASK; + } + } + else + { + if ( d->msi->flags & PCI_MSI_FLAGS_PVMASK ) + flags |= ACCESSED_MASK; + else + PT_LOG("why comes to MASK without mask support??\n"); + } + break; + case 0x10 ... 0x13: + if ( d->msi->flags & PCI_MSI_FLAGS_64BIT ) + { + if ( d->msi->flags & PCI_MSI_FLAGS_PVMASK ) + flags |= ACCESSED_MASK; + else + PT_LOG("why comes to MASK without mask support??\n"); + } + else + can_write = 0; + break; + case 0x14 ... 0x18: + can_write = 0; + break; + default: + PT_LOG("Non MSI register!!!\n"); + break; + } + + if ( can_write ) + dev->config[cur] = value; + } + + if ( flags & ACCESSED_DATA || flags & ACCESSED_ADDR ) + pt_msi_map_update(d, old_data, old_addr); + + if ( flags & ACCESSED_MASK ) + pt_msi_mask_update(d, old_mask); + + /* This will enable physical one, do it in last step */ + if ( flags & ACCESSED_CTRL ) + pt_msi_control_update(d, old_ctrl); + + return 1; +} + +int pt_msi_read(struct pt_dev *d, int addr, int len, uint32_t *val) +{ + int e_addr = addr, e_len = len, offset = 0, i; + uint8_t e_val = 0; + PCIDevice *pd = (PCIDevice *)d; + + if ( !d || !d->msi ) + return 0; + + if ( (addr > (d->msi->offset + d->msi->size) ) || + (addr + len) <= d->msi->offset ) + return 0; + + PT_LOG("pt_msi_read addr %x len %x val %x offset %x size %x\n", + addr, len, *val, d->msi->offset, d->msi->size); + + if ( (addr + len ) > (d->msi->offset + d->msi->size) ) + e_len -= addr + len - d->msi->offset - d->msi->size; + + if ( addr < d->msi->offset ) + { + e_addr = d->msi->offset; + offset = d->msi->offset - addr; + e_len -= offset; + } + + for ( i = 0; i < e_len; i++ ) + { + e_val = *(uint8_t *)(&pd->config[e_addr] + i); + *val &= ~(0xff << ( (offset + i) * 8)); + *val |= (e_val << ( (offset + i) * 8)); + } + + return e_len; +} + diff -r a0ebceaf41ff -r ad55c06c9bbc tools/ioemu/hw/pt-msi.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/ioemu/hw/pt-msi.h Thu May 01 10:33:03 2008 +0100 @@ -0,0 +1,65 @@ +#ifndef _PT_MSI_H +#define _PT_MSI_H + +#include "vl.h" +#include "pci/header.h" +#include "pci/pci.h" +#include "pass-through.h" + +#define MSI_FLAG_UNINIT 0x1000 +#define PT_MSI_MAPPED 0x2000 + +#define MSI_DATA_VECTOR_SHIFT 0 +#define MSI_DATA_VECTOR(v) (((u8)v) << MSI_DATA_VECTOR_SHIFT) + +#define MSI_DATA_DELIVERY_SHIFT 8 +#define MSI_DATA_DELIVERY_FIXED (0 << MSI_DATA_DELIVERY_SHIFT) +#define MSI_DATA_DELIVERY_LOWPRI (1 << MSI_DATA_DELIVERY_SHIFT) + +#define MSI_DATA_LEVEL_SHIFT 14 +#define MSI_DATA_LEVEL_DEASSERT (0 << MSI_DATA_LEVEL_SHIFT) +#define MSI_DATA_LEVEL_ASSERT (1 << MSI_DATA_LEVEL_SHIFT) + +#define MSI_DATA_TRIGGER_SHIFT 15 +#define MSI_DATA_TRIGGER_EDGE (0 << MSI_DATA_TRIGGER_SHIFT) +#define MSI_DATA_TRIGGER_LEVEL (1 << MSI_DATA_TRIGGER_SHIFT) + +/* + + * Shift/mask fields for APIC-based bus address + + */ + +#define MSI_ADDR_HEADER 0xfee00000 +#define MSI_TARGET_CPU_SHIFT 12 + +#define MSI_ADDR_DESTID_MASK 0xfff0000f +#define MSI_ADDR_DESTID_CPU(cpu) ((cpu) << MSI_TARGET_CPU_SHIFT) + +#define MSI_ADDR_DESTMODE_SHIFT 2 +#define MSI_ADDR_DESTMODE_PHYS (0 << MSI_ADDR_DESTMODE_SHIFT) +#define MSI_ADDR_DESTMODE_LOGIC (1 << MSI_ADDR_DESTMODE_SHIFT) + +#define MSI_ADDR_REDIRECTION_SHIFT 3 +#define MSI_ADDR_REDIRECTION_CPU (0 << MSI_ADDR_REDIRECTION_SHIFT) +#define MSI_ADDR_REDIRECTION_LOWPRI (1 << MSI_ADDR_REDIRECTION_SHIFT) + +#define PCI_MSI_FLAGS_PVMASK 0x100 + +#define AUTO_ASSIGN -1 + +/* shift count for gflags */ +#define GFLAGS_SHIFT_DEST_ID 0 +#define GFLAGS_SHIFT_RH 8 +#define GFLAGS_SHIFT_DM 9 +#define GLFAGS_SHIFT_DELIV_MODE 12 +#define GLFAGS_SHIFT_TRG_MODE 15 + +int +pt_msi_init(struct pt_dev *dev, int pos); + +int +pt_msi_write(struct pt_dev *d, uint32_t addr, uint32_t val, uint32_t len); + +int +pt_msi_read(struct pt_dev *d, int addr, int len, uint32_t *val); + +#endif diff -r a0ebceaf41ff -r ad55c06c9bbc tools/libxc/xc_domain.c --- a/tools/libxc/xc_domain.c Thu May 01 10:32:10 2008 +0100 +++ b/tools/libxc/xc_domain.c Thu May 01 10:33:03 2008 +0100 @@ -795,6 +795,32 @@ int xc_deassign_device( return do_domctl(xc_handle, &domctl); } +int xc_domain_update_msi_irq( + int xc_handle, + uint32_t domid, + uint32_t gvec, + uint32_t pirq, + uint32_t gflags) +{ + int rc; + xen_domctl_bind_pt_irq_t *bind; + + DECLARE_DOMCTL; + + domctl.cmd = XEN_DOMCTL_bind_pt_irq; + domctl.domain = (domid_t)domid; + + bind = &(domctl.u.bind_pt_irq); + bind->hvm_domid = domid; + bind->irq_type = PT_IRQ_TYPE_MSI; + bind->machine_irq = pirq; + bind->u.msi.gvec = gvec; + bind->u.msi.gflags = gflags; + + rc = do_domctl(xc_handle, &domctl); + return rc; +} + /* Pass-through: binds machine irq to guests irq */ int xc_domain_bind_pt_irq( int xc_handle, diff -r a0ebceaf41ff -r ad55c06c9bbc tools/libxc/xc_physdev.c --- a/tools/libxc/xc_physdev.c Thu May 01 10:32:10 2008 +0100 +++ b/tools/libxc/xc_physdev.c Thu May 01 10:33:03 2008 +0100 @@ -45,6 +45,37 @@ int xc_physdev_map_pirq(int xc_handle, return rc; } +int xc_physdev_map_pirq_msi(int xc_handle, + int domid, + int type, + int index, + int *pirq, + int devfn, + int bus, + int msi_type) +{ + int rc; + struct physdev_map_pirq map; + + if ( !pirq ) + return -EINVAL; + + map.domid = domid; + map.type = type; + map.index = index; + map.pirq = *pirq; + map.msi_info.devfn = devfn; + map.msi_info.bus = bus; + map.msi_info.msi = msi_type; + + rc = do_physdev_op(xc_handle, PHYSDEVOP_map_pirq, &map); + + if ( !rc ) + *pirq = map.pirq; + + return rc; +} + int xc_physdev_unmap_pirq(int xc_handle, int domid, int pirq) @@ -59,3 +90,4 @@ int xc_physdev_unmap_pirq(int xc_handle, return rc; } + diff -r a0ebceaf41ff -r ad55c06c9bbc tools/libxc/xenctrl.h --- a/tools/libxc/xenctrl.h Thu May 01 10:32:10 2008 +0100 +++ b/tools/libxc/xenctrl.h Thu May 01 10:33:03 2008 +0100 @@ -856,6 +856,15 @@ int xc_physdev_map_pirq(int xc_handle, int index, int *pirq); +int xc_physdev_map_pirq_msi(int xc_handle, + int domid, + int type, + int index, + int *pirq, + int devfn, + int bus, + int msi_type); + int xc_physdev_unmap_pirq(int xc_handle, int domid, int pirq); @@ -959,6 +968,13 @@ int xc_domain_ioport_mapping(int xc_hand uint32_t first_mport, uint32_t nr_ports, uint32_t add_mapping); + +int xc_domain_update_msi_irq( + int xc_handle, + uint32_t domid, + uint32_t gvec, + uint32_t pirq, + uint32_t gflags); int xc_domain_bind_pt_irq(int xc_handle, uint32_t domid, diff -r a0ebceaf41ff -r ad55c06c9bbc xen/arch/x86/hvm/Makefile --- a/xen/arch/x86/hvm/Makefile Thu May 01 10:32:10 2008 +0100 +++ b/xen/arch/x86/hvm/Makefile Thu May 01 10:33:03 2008 +0100 @@ -16,4 +16,5 @@ obj-y += vlapic.o obj-y += vlapic.o obj-y += vpic.o obj-y += save.o +obj-y += vmsi.o obj-y += stdvga.o diff -r a0ebceaf41ff -r ad55c06c9bbc xen/arch/x86/hvm/vlapic.c --- a/xen/arch/x86/hvm/vlapic.c Thu May 01 10:32:10 2008 +0100 +++ b/xen/arch/x86/hvm/vlapic.c Thu May 01 10:33:03 2008 +0100 @@ -476,6 +476,9 @@ void vlapic_EOI_set(struct vlapic *vlapi if ( vlapic_test_and_clear_vector(vector, &vlapic->regs->data[APIC_TMR]) ) vioapic_update_EOI(vlapic_domain(vlapic), vector); + + if ( vtd_enabled ) + hvm_dpci_msi_eoi(current->domain, vector); } static int vlapic_ipi( diff -r a0ebceaf41ff -r ad55c06c9bbc xen/arch/x86/hvm/vmsi.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xen/arch/x86/hvm/vmsi.c Thu May 01 10:33:03 2008 +0100 @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2001 MandrakeSoft S.A. + * + * MandrakeSoft S.A. + * 43, rue d'Aboukir + * 75002 Paris - France + * http://www.linux-mandrake.com/ + * http://www.mandrakesoft.com/ + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Support for virtual MSI logic + * Will be merged it with virtual IOAPIC logic, since most is the same +*/ + +#include <xen/config.h> +#include <xen/types.h> +#include <xen/mm.h> +#include <xen/xmalloc.h> +#include <xen/lib.h> +#include <xen/errno.h> +#include <xen/sched.h> +#include <public/hvm/ioreq.h> +#include <asm/hvm/io.h> +#include <asm/hvm/vpic.h> +#include <asm/hvm/vlapic.h> +#include <asm/hvm/support.h> +#include <asm/current.h> +#include <asm/event.h> + +static uint32_t vmsi_get_delivery_bitmask( + struct domain *d, uint16_t dest, uint8_t dest_mode) +{ + uint32_t mask = 0; + struct vcpu *v; + + HVM_DBG_LOG(DBG_LEVEL_IOAPIC, "ioapic_get_delivery_bitmask " + "dest %d dest_mode %d\n", dest, dest_mode); + + if ( dest_mode == 0 ) /* Physical mode. */ + { + if ( dest == 0xFF ) /* Broadcast. */ + { + for_each_vcpu ( d, v ) + mask |= 1 << v->vcpu_id; + goto out; + } + + for_each_vcpu ( d, v ) + { + if ( VLAPIC_ID(vcpu_vlapic(v)) == dest ) + { + mask = 1 << v->vcpu_id; + break; + } + } + } + else if ( dest != 0 ) /* Logical mode, MDA non-zero. */ + { + for_each_vcpu ( d, v ) + if ( vlapic_match_logical_addr(vcpu_vlapic(v), dest) ) + mask |= 1 << v->vcpu_id; + } + + out: + HVM_DBG_LOG(DBG_LEVEL_IOAPIC, "ioapic_get_delivery_bitmask mask %x\n", + mask); + return mask; +} + +static void vmsi_inj_irq( + struct domain *d, + struct vlapic *target, + uint8_t vector, + uint8_t trig_mode, + uint8_t delivery_mode) +{ + HVM_DBG_LOG(DBG_LEVEL_IOAPIC, "ioapic_inj_irq " + "irq %d trig %d delive mode %d\n", + vector, trig_mode, delivery_mode); + + switch ( delivery_mode ) + { + case dest_Fixed: + case dest_LowestPrio: + if ( vlapic_set_irq(target, vector, trig_mode) ) + vcpu_kick(vlapic_vcpu(target)); + break; + default: + gdprintk(XENLOG_WARNING, "error delivery mode %d\n", delivery_mode); + break; + } +} + +#define VMSI_DEST_ID_MASK 0xff +#define VMSI_RH_MASK 0x100 +#define VMSI_DM_MASK 0x200 +#define VMSI_DELIV_MASK 0x7000 +#define VMSI_TRIG_MODE 0x8000 + +int vmsi_deliver(struct domain *d, int pirq) +{ + struct hvm_irq_dpci *hvm_irq_dpci = d->arch.hvm_domain.irq.dpci; + uint32_t flags = hvm_irq_dpci->mirq[pirq].gmsi.gflags; + int vector = hvm_irq_dpci->mirq[pirq].gmsi.gvec; + uint16_t dest = flags & VMSI_DEST_ID_MASK; + uint8_t dest_mode = flags & VMSI_DM_MASK; + uint8_t delivery_mode = flags & VMSI_DELIV_MASK; + uint8_t trig_mode = flags & VMSI_TRIG_MODE; + uint32_t deliver_bitmask; + struct vlapic *target; + struct vcpu *v; + + HVM_DBG_LOG(DBG_LEVEL_IOAPIC, + "msi: dest=%x dest_mode=%x delivery_mode=%x " + "vector=%x trig_mode=%x\n", + dest, dest_mode, delivery_mode, vector, trig_mode); + + if ( !(hvm_irq_dpci->mirq[pirq].flags & HVM_IRQ_DPCI_MSI) ) + { + gdprintk(XENLOG_WARNING, "pirq %x not msi \n", pirq); + return 0; + } + + deliver_bitmask = vmsi_get_delivery_bitmask(d, dest, dest_mode); + if ( !deliver_bitmask ) + { + HVM_DBG_LOG(DBG_LEVEL_IOAPIC, "ioapic deliver " + "no target on destination\n"); + return 0; + } + + switch ( delivery_mode ) + { + case dest_LowestPrio: + { + target = apic_round_robin(d, vector, deliver_bitmask); + if ( target != NULL ) + vmsi_inj_irq(d, target, vector, trig_mode, delivery_mode); + else + HVM_DBG_LOG(DBG_LEVEL_IOAPIC, "null round robin: " + "mask=%x vector=%x delivery_mode=%x\n", + deliver_bitmask, vector, dest_LowestPrio); + break; + } + + case dest_Fixed: + case dest_ExtINT: + { + uint8_t bit; + for ( bit = 0; deliver_bitmask != 0; bit++ ) + { + if ( !(deliver_bitmask & (1 << bit)) ) + continue; + deliver_bitmask &= ~(1 << bit); + v = d->vcpu[bit]; + if ( v != NULL ) + { + target = vcpu_vlapic(v); + vmsi_inj_irq(d, target, vector, trig_mode, delivery_mode); + } + } + break; + } + + case dest_SMI: + case dest_NMI: + case dest_INIT: + case dest__reserved_2: + default: + gdprintk(XENLOG_WARNING, "Unsupported delivery mode %d\n", + delivery_mode); + break; + } + return 1; +} + diff -r a0ebceaf41ff -r ad55c06c9bbc xen/arch/x86/hvm/vmx/intr.c --- a/xen/arch/x86/hvm/vmx/intr.c Thu May 01 10:32:10 2008 +0100 +++ b/xen/arch/x86/hvm/vmx/intr.c Thu May 01 10:33:03 2008 +0100 @@ -103,6 +103,12 @@ static void enable_intr_window(struct vc } } +extern int vmsi_deliver(struct domain *d, int pirq); +static int hvm_pci_msi_assert(struct domain *d, int pirq) +{ + return vmsi_deliver(d, pirq); +} + static void vmx_dirq_assist(struct vcpu *v) { unsigned int irq; @@ -120,6 +126,12 @@ static void vmx_dirq_assist(struct vcpu { if ( !test_and_clear_bit(irq, &hvm_irq_dpci->dirq_mask) ) continue; + + if ( test_bit(_HVM_IRQ_DPCI_MSI, &hvm_irq_dpci->mirq[irq].flags) ) + { + hvm_pci_msi_assert(d, irq); + continue; + } stop_timer(&hvm_irq_dpci->hvm_timer[domain_irq_to_vector(d, irq)]); diff -r a0ebceaf41ff -r ad55c06c9bbc xen/drivers/passthrough/io.c --- a/xen/drivers/passthrough/io.c Thu May 01 10:32:10 2008 +0100 +++ b/xen/drivers/passthrough/io.c Thu May 01 10:33:03 2008 +0100 @@ -71,44 +71,59 @@ int pt_irq_create_bind_vtd( xfree(hvm_irq_dpci); } - machine_gsi = pt_irq_bind->machine_irq; - device = pt_irq_bind->u.pci.device; - intx = pt_irq_bind->u.pci.intx; - guest_gsi = hvm_pci_intx_gsi(device, intx); - link = hvm_pci_intx_link(device, intx); - hvm_irq_dpci->link_cnt[link]++; - - digl = xmalloc(struct dev_intx_gsi_link); - if ( !digl ) - return -ENOMEM; - - digl->device = device; - digl->intx = intx; - digl->gsi = guest_gsi; - digl->link = link; - list_add_tail(&digl->list, - &hvm_irq_dpci->mirq[machine_gsi].digl_list); - - hvm_irq_dpci->girq[guest_gsi].valid = 1; - hvm_irq_dpci->girq[guest_gsi].device = device; - hvm_irq_dpci->girq[guest_gsi].intx = intx; - hvm_irq_dpci->girq[guest_gsi].machine_gsi = machine_gsi; - - /* Bind the same mirq once in the same domain */ - if ( !hvm_irq_dpci->mirq[machine_gsi].valid ) - { - hvm_irq_dpci->mirq[machine_gsi].valid = 1; - hvm_irq_dpci->mirq[machine_gsi].dom = d; - - init_timer(&hvm_irq_dpci->hvm_timer[domain_irq_to_vector(d, machine_gsi)], - pt_irq_time_out, &hvm_irq_dpci->mirq[machine_gsi], 0); - /* Deal with gsi for legacy devices */ - pirq_guest_bind(d->vcpu[0], machine_gsi, BIND_PIRQ__WILL_SHARE); - } - - gdprintk(XENLOG_INFO VTDPREFIX, - "VT-d irq bind: m_irq = %x device = %x intx = %x\n", - machine_gsi, device, intx); + if ( pt_irq_bind->irq_type == PT_IRQ_TYPE_MSI ) + { + int pirq = pt_irq_bind->machine_irq; + + hvm_irq_dpci->mirq[pirq].flags |= HVM_IRQ_DPCI_VALID |HVM_IRQ_DPCI_MSI ; + hvm_irq_dpci->mirq[pirq].gmsi.gvec = pt_irq_bind->u.msi.gvec; + hvm_irq_dpci->mirq[pirq].gmsi.gflags = pt_irq_bind->u.msi.gflags; + + hvm_irq_dpci->msi_gvec_pirq[pt_irq_bind->u.msi.gvec] = pirq; + + pirq_guest_bind(d->vcpu[0], pirq, BIND_PIRQ__WILL_SHARE); + } + else + { + machine_gsi = pt_irq_bind->machine_irq; + device = pt_irq_bind->u.pci.device; + intx = pt_irq_bind->u.pci.intx; + guest_gsi = hvm_pci_intx_gsi(device, intx); + link = hvm_pci_intx_link(device, intx); + hvm_irq_dpci->link_cnt[link]++; + + digl = xmalloc(struct dev_intx_gsi_link); + if ( !digl ) + return -ENOMEM; + + digl->device = device; + digl->intx = intx; + digl->gsi = guest_gsi; + digl->link = link; + list_add_tail(&digl->list, + &hvm_irq_dpci->mirq[machine_gsi].digl_list); + + hvm_irq_dpci->girq[guest_gsi].valid = 1; + hvm_irq_dpci->girq[guest_gsi].device = device; + hvm_irq_dpci->girq[guest_gsi].intx = intx; + hvm_irq_dpci->girq[guest_gsi].machine_gsi = machine_gsi; + + /* Bind the same mirq once in the same domain */ + if ( !(hvm_irq_dpci->mirq[machine_gsi].flags & HVM_IRQ_DPCI_VALID) ) + { + hvm_irq_dpci->mirq[machine_gsi].flags |= HVM_IRQ_DPCI_VALID; + hvm_irq_dpci->mirq[machine_gsi].dom = d; + + init_timer(&hvm_irq_dpci->hvm_timer[domain_irq_to_vector(d, machine_gsi)], + pt_irq_time_out, &hvm_irq_dpci->mirq[machine_gsi], 0); + /* Deal with gsi for legacy devices */ + pirq_guest_bind(d->vcpu[0], machine_gsi, BIND_PIRQ__WILL_SHARE); + } + + gdprintk(XENLOG_INFO VTDPREFIX, + "VT-d irq bind: m_irq = %x device = %x intx = %x\n", + machine_gsi, device, intx); + } return 0; } @@ -139,7 +154,7 @@ int pt_irq_destroy_bind_vtd( sizeof(struct hvm_girq_dpci_mapping)); /* clear the mirq info */ - if ( hvm_irq_dpci->mirq[machine_gsi].valid ) + if ( (hvm_irq_dpci->mirq[machine_gsi].flags & HVM_IRQ_DPCI_VALID) ) { list_for_each_safe ( digl_list, tmp, &hvm_irq_dpci->mirq[machine_gsi].digl_list ) @@ -161,7 +176,7 @@ int pt_irq_destroy_bind_vtd( pirq_guest_unbind(d, machine_gsi); kill_timer(&hvm_irq_dpci->hvm_timer[domain_irq_to_vector(d, machine_gsi)]); hvm_irq_dpci->mirq[machine_gsi].dom = NULL; - hvm_irq_dpci->mirq[machine_gsi].valid = 0; + hvm_irq_dpci->mirq[machine_gsi].flags = 0; } } @@ -177,7 +192,7 @@ int hvm_do_IRQ_dpci(struct domain *d, un struct hvm_irq_dpci *dpci = domain_get_irq_dpci(d); if ( !iommu_enabled || (d == dom0) || !dpci || - !dpci->mirq[mirq].valid ) + !dpci->mirq[mirq].flags & HVM_IRQ_DPCI_VALID ) return 0; /* @@ -187,11 +202,28 @@ int hvm_do_IRQ_dpci(struct domain *d, un * PIC) and we need to detect that. */ set_bit(mirq, dpci->dirq_mask); - set_timer(&dpci->hvm_timer[domain_irq_to_vector(d, mirq)], - NOW() + PT_IRQ_TIME_OUT); + if ( !test_bit(_HVM_IRQ_DPCI_MSI, &dpci->mirq[mirq].flags) ) + set_timer(&dpci->hvm_timer[domain_irq_to_vector(d, mirq)], + NOW() + PT_IRQ_TIME_OUT); vcpu_kick(d->vcpu[0]); return 1; +} + + +void hvm_dpci_msi_eoi(struct domain *d, int vector) +{ + struct hvm_irq_dpci *hvm_irq_dpci = d->arch.hvm_domain.irq.dpci; + int pirq; + + if ( !vtd_enabled || (hvm_irq_dpci == NULL) ) + return; + + pirq = hvm_irq_dpci->msi_gvec_pirq[vector]; + if ( ( pirq >= 0 ) && (pirq < NR_PIRQS) && + (hvm_irq_dpci->mirq[pirq].flags & HVM_IRQ_DPCI_VALID) && + (hvm_irq_dpci->mirq[pirq].flags & HVM_IRQ_DPCI_MSI) ) + pirq_guest_eoi(d, pirq); } void hvm_dpci_eoi(struct domain *d, unsigned int guest_gsi, diff -r a0ebceaf41ff -r ad55c06c9bbc xen/drivers/passthrough/iommu.c --- a/xen/drivers/passthrough/iommu.c Thu May 01 10:32:10 2008 +0100 +++ b/xen/drivers/passthrough/iommu.c Thu May 01 10:33:03 2008 +0100 @@ -77,7 +77,7 @@ void iommu_domain_destroy(struct domain { for ( i = 0; i < NR_IRQS; i++ ) { - if ( !hvm_irq_dpci->mirq[i].valid ) + if ( !(hvm_irq_dpci->mirq[i].flags & HVM_IRQ_DPCI_VALID) ) continue; pirq_guest_unbind(d, i); diff -r a0ebceaf41ff -r ad55c06c9bbc xen/drivers/passthrough/vtd/x86/vtd.c --- a/xen/drivers/passthrough/vtd/x86/vtd.c Thu May 01 10:32:10 2008 +0100 +++ b/xen/drivers/passthrough/vtd/x86/vtd.c Thu May 01 10:33:03 2008 +0100 @@ -101,7 +101,7 @@ void hvm_dpci_isairq_eoi(struct domain * /* Multiple mirq may be mapped to one isa irq */ for ( i = 0; i < NR_IRQS; i++ ) { - if ( !dpci->mirq[i].valid ) + if ( !dpci->mirq[i].flags & HVM_IRQ_DPCI_VALID ) continue; list_for_each_entry_safe ( digl, tmp, diff -r a0ebceaf41ff -r ad55c06c9bbc xen/include/asm-x86/hvm/io.h --- a/xen/include/asm-x86/hvm/io.h Thu May 01 10:32:10 2008 +0100 +++ b/xen/include/asm-x86/hvm/io.h Thu May 01 10:33:03 2008 +0100 @@ -120,5 +120,6 @@ void stdvga_init(struct domain *d); void stdvga_init(struct domain *d); void stdvga_deinit(struct domain *d); +extern void hvm_dpci_msi_eoi(struct domain *d, int vector); #endif /* __ASM_X86_HVM_IO_H__ */ diff -r a0ebceaf41ff -r ad55c06c9bbc xen/include/asm-x86/hvm/irq.h --- a/xen/include/asm-x86/hvm/irq.h Thu May 01 10:32:10 2008 +0100 +++ b/xen/include/asm-x86/hvm/irq.h Thu May 01 10:33:03 2008 +0100 @@ -38,11 +38,21 @@ struct dev_intx_gsi_link { uint8_t link; }; +#define HVM_IRQ_DPCI_VALID 0x1 +#define HVM_IRQ_DPCI_MSI 0x2 +#define _HVM_IRQ_DPCI_MSI 0x1 + +struct hvm_gmsi_info { + uint32_t gvec; + uint32_t gflags; +}; + struct hvm_mirq_dpci_mapping { - uint8_t valid; + uint32_t flags; int pending; struct list_head digl_list; struct domain *dom; + struct hvm_gmsi_info gmsi; }; struct hvm_girq_dpci_mapping { @@ -60,6 +70,7 @@ struct hvm_irq_dpci { struct hvm_mirq_dpci_mapping mirq[NR_IRQS]; /* Guest IRQ to guest device/intx mapping. */ struct hvm_girq_dpci_mapping girq[NR_IRQS]; + uint8_t msi_gvec_pirq[NR_VECTORS]; DECLARE_BITMAP(dirq_mask, NR_IRQS); /* Record of mapped ISA IRQs */ DECLARE_BITMAP(isairq_map, NR_ISAIRQS); diff -r a0ebceaf41ff -r ad55c06c9bbc xen/include/public/domctl.h --- a/xen/include/public/domctl.h Thu May 01 10:32:10 2008 +0100 +++ b/xen/include/public/domctl.h Thu May 01 10:33:03 2008 +0100 @@ -454,7 +454,8 @@ DEFINE_XEN_GUEST_HANDLE(xen_domctl_assig #define XEN_DOMCTL_unbind_pt_irq 48 typedef enum pt_irq_type_e { PT_IRQ_TYPE_PCI, - PT_IRQ_TYPE_ISA + PT_IRQ_TYPE_ISA, + PT_IRQ_TYPE_MSI, } pt_irq_type_t; struct xen_domctl_bind_pt_irq { uint32_t machine_irq; @@ -470,6 +471,10 @@ struct xen_domctl_bind_pt_irq { uint8_t device; uint8_t intx; } pci; + struct { + uint8_t gvec; + uint32_t gflags; + } msi; } u; }; typedef struct xen_domctl_bind_pt_irq xen_domctl_bind_pt_irq_t; _______________________________________________ Xen-changelog mailing list Xen-changelog@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-changelog
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |