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:
Keir Fraser committed 2008-05-01 10:33:03 +01:00
1 parent 46ac8f31a0
commit 85715f4bc7
18 files changed
+973 -45

No files matched your search

+1 -1
View File
@@ -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
+39 -1
View File
@@ -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);
+9
View File
@@ -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 */
+488
View File
@@ -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;
}
+65
View File
@@ -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
+26
View File
@@ -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,
+32
View File
@@ -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;
}
+16
View File
@@ -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,
+1
View File
@@ -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
+3
View File
@@ -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(
+189
View File
@@ -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;
}
+12
View File
@@ -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 -39
View File
@@ -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)
{
+1 -1
View File
@@ -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);
+1 -1
View File
@@ -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,
+1
View File
@@ -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__ */
+12 -1
View File
@@ -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);
+6 -1
View File
@@ -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;
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.