mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
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@intel.com> Signed-off-by: Shan Haitao <haitao.shan@intel.com>
This commit is contained in:
1 parent
46ac8f31a0
commit
85715f4bc7
18 files changed
+973
-45
No files matched your search
@@ -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
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "pass-through.h"
|
||||
#include "pci/header.h"
|
||||
#include "pci/pci.h"
|
||||
#include "pt-msi.h"
|
||||
|
||||
extern FILE *logfile;
|
||||
|
||||
@@ -287,6 +288,9 @@ static void pt_pci_write_config(PCIDevice *d, uint32_t address, uint32_t val,
|
||||
return;
|
||||
}
|
||||
|
||||
if ( pt_msi_write(assigned_device, address, val, len) )
|
||||
return;
|
||||
|
||||
/* PCI config pass-through */
|
||||
if (address == 0x4) {
|
||||
switch (len){
|
||||
@@ -333,6 +337,7 @@ static uint32_t pt_pci_read_config(PCIDevice *d, uint32_t address, int len)
|
||||
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 pt_dev *assigned_device)
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -511,6 +546,9 @@ struct pt_dev * register_real_device(PCIBus *e_bus,
|
||||
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);
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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@intel.com>
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
@@ -960,6 +969,13 @@ int xc_domain_ioport_mapping(int xc_handle,
|
||||
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,
|
||||
uint8_t machine_irq,
|
||||
|
||||
@@ -16,4 +16,5 @@ obj-y += vioapic.o
|
||||
obj-y += vlapic.o
|
||||
obj-y += vpic.o
|
||||
obj-y += save.o
|
||||
obj-y += vmsi.o
|
||||
obj-y += stdvga.o
|
||||
@@ -476,6 +476,9 @@ void vlapic_EOI_set(struct vlapic *vlapic)
|
||||
|
||||
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(
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -103,6 +103,12 @@ static void enable_intr_window(struct vcpu *v, struct hvm_intack intack)
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -121,6 +127,12 @@ static void vmx_dirq_assist(struct vcpu *v)
|
||||
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)]);
|
||||
|
||||
list_for_each_entry ( digl, &hvm_irq_dpci->mirq[irq].digl_list, list )
|
||||
|
||||
@@ -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 )
|
||||
if ( pt_irq_bind->irq_type == PT_IRQ_TYPE_MSI )
|
||||
{
|
||||
hvm_irq_dpci->mirq[machine_gsi].valid = 1;
|
||||
hvm_irq_dpci->mirq[machine_gsi].dom = d;
|
||||
int pirq = pt_irq_bind->machine_irq;
|
||||
|
||||
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);
|
||||
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]++;
|
||||
|
||||
gdprintk(XENLOG_INFO VTDPREFIX,
|
||||
"VT-d irq bind: m_irq = %x device = %x intx = %x\n",
|
||||
machine_gsi, device, intx);
|
||||
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, unsigned int mirq)
|
||||
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,13 +202,30 @@ int hvm_do_IRQ_dpci(struct domain *d, unsigned int mirq)
|
||||
* 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,
|
||||
union vioapic_redir_entry *ent)
|
||||
{
|
||||
|
||||
@@ -77,7 +77,7 @@ void iommu_domain_destroy(struct domain *d)
|
||||
{
|
||||
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);
|
||||
|
||||
@@ -101,7 +101,7 @@ void hvm_dpci_isairq_eoi(struct domain *d, unsigned int isairq)
|
||||
/* 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,
|
||||
|
||||
@@ -120,5 +120,6 @@ struct hvm_hw_stdvga {
|
||||
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__ */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -454,7 +454,8 @@ DEFINE_XEN_GUEST_HANDLE(xen_domctl_assign_device_t);
|
||||
#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;
|
||||
|
||||