hv: Use Interrupt Remapping format for programming interrupt sources

When a corresponding IOMMU is found for the device, this patch adds
support to program Interrupt Remapping hardware RTEs and the original
interrupt sources (MSI or IOAPIC) with IR format.

Tracked-On: #2426
Signed-off-by: Sainath Grandhi <sainath.grandhi@intel.com>
Reviewed-by: Binbin Wu <binbin.wu@intel.com>
This commit is contained in:
Sainath Grandhi 2019-01-15 12:51:25 -08:00 committed by Eddie Dong
parent 7104f0a512
commit 970821462b
9 changed files with 350 additions and 25 deletions

View File

@ -70,11 +70,15 @@ static uint32_t calculate_logical_dest_mask(uint64_t pdmask)
}
static void ptirq_build_physical_msi(struct acrn_vm *vm, struct ptirq_msi_info *info,
uint32_t vector)
const struct ptirq_remapping_info *entry, uint32_t vector)
{
uint64_t vdmask, pdmask;
uint32_t dest, delmode, dest_mask;
bool phys;
union dmar_ir_entry irte;
union irte_index ir_index;
int32_t ret;
struct intr_source intr_src;
/* get physical destination cpu mask */
dest = info->vmsi_addr.bits.dest_field;
@ -89,19 +93,51 @@ static void ptirq_build_physical_msi(struct acrn_vm *vm, struct ptirq_msi_info *
delmode = MSI_DATA_DELMODE_LOPRI;
}
/* update physical delivery mode & vector */
dest_mask = calculate_logical_dest_mask(pdmask);
/* Using phys_irq as index in the corresponding IOMMU */
irte.entry.lower = 0UL;
irte.entry.upper = 0UL;
irte.bits.vector = vector;
irte.bits.delivery_mode = delmode;
irte.bits.dest_mode = MSI_ADDR_DESTMODE_LOGICAL;
irte.bits.rh = MSI_ADDR_RH;
irte.bits.dest = dest_mask;
intr_src.is_msi = true;
intr_src.src.msi.value = entry->phys_sid.msi_id.bdf;
ret = dmar_assign_irte(intr_src, irte, (uint16_t)entry->allocated_pirq);
if (ret == 0) {
/*
* Update the MSI interrupt source to point to the IRTE
* SHV is set to 0 as ACRN disables MMC (Multi-Message Capable
* for MSI devices.
*/
info->pmsi_data.full = 0U;
ir_index.index = (uint16_t)entry->allocated_pirq;
info->pmsi_addr.full = 0UL;
info->pmsi_addr.ir_bits.intr_index_high = ir_index.bits.index_high;
info->pmsi_addr.ir_bits.shv = 0U;
info->pmsi_addr.ir_bits.intr_format = 0x1U;
info->pmsi_addr.ir_bits.intr_index_low = ir_index.bits.index_low;
info->pmsi_addr.ir_bits.constant = 0xFEEU;
} else {
/* In case there is no corresponding IOMMU, for example, if the
* IOMMU is ignored, pass the MSI info in Compatibility Format
*/
info->pmsi_data = info->vmsi_data;
info->pmsi_data.bits.delivery_mode = delmode;
info->pmsi_data.bits.vector = vector;
dest_mask = calculate_logical_dest_mask(pdmask);
/* update physical dest mode & dest field */
info->pmsi_addr = info->vmsi_addr;
info->pmsi_addr.bits.dest_mode = MSI_ADDR_DESTMODE_LOGICAL;
info->pmsi_addr.bits.rh = MSI_ADDR_RH;
info->pmsi_addr.bits.dest_field = dest_mask;
dev_dbg(ACRN_DBG_IRQ, "MSI addr:data = 0x%llx:%x(V) -> 0x%llx:%x(P)",
info->pmsi_addr.bits.rh = MSI_ADDR_RH;
info->pmsi_addr.bits.dest_mode = MSI_ADDR_DESTMODE_LOGICAL;
}
dev_dbg(ACRN_DBG_IRQ, "MSI %s addr:data = 0x%llx:%x(V) -> 0x%llx:%x(P)",
(info->pmsi_addr.ir_bits.intr_format != 0U) ? " Remappable Format" : "Compatibility Format",
info->vmsi_addr.full, info->vmsi_data.full,
info->pmsi_addr.full, info->pmsi_data.full);
}
@ -112,6 +148,10 @@ ptirq_build_physical_rte(struct acrn_vm *vm, struct ptirq_remapping_info *entry)
union ioapic_rte rte;
uint32_t phys_irq = entry->allocated_pirq;
union source_id *virt_sid = &entry->virt_sid;
union irte_index ir_index;
union dmar_ir_entry irte;
struct intr_source intr_src;
int32_t ret;
if (virt_sid->intx_id.src == PTDEV_VPIN_IOAPIC) {
uint64_t vdmask, pdmask;
@ -150,15 +190,36 @@ ptirq_build_physical_rte(struct acrn_vm *vm, struct ptirq_remapping_info *entry)
/* update physical delivery mode, dest mode(logical) & vector */
vector = irq_to_vector(phys_irq);
dest_mask = calculate_logical_dest_mask(pdmask);
irte.entry.lower = 0UL;
irte.entry.upper = 0UL;
irte.bits.vector = vector;
irte.bits.delivery_mode = delmode;
irte.bits.dest_mode = IOAPIC_RTE_DESTMODE_LOGICAL;
irte.bits.dest = dest_mask;
irte.bits.trigger_mode = rte.bits.trigger_mode;
intr_src.is_msi = false;
intr_src.src.ioapic_id = ioapic_irq_to_ioapic_id(phys_irq);
ret = dmar_assign_irte(intr_src, irte, (uint16_t)phys_irq);
if (ret == 0) {
ir_index.index = (uint16_t)phys_irq;
rte.ir_bits.vector = vector;
rte.ir_bits.constant = 0U;
rte.ir_bits.intr_index_high = ir_index.bits.index_high;
rte.ir_bits.intr_format = 1U;
rte.ir_bits.intr_index_low = ir_index.bits.index_low;
} else {
rte.bits.dest_mode = IOAPIC_RTE_DESTMODE_LOGICAL;
rte.bits.delivery_mode = delmode;
rte.bits.vector = vector;
dest_mask = calculate_logical_dest_mask(pdmask);
/* update physical dest field */
rte.bits.dest_field = dest_mask;
}
dev_dbg(ACRN_DBG_IRQ, "IOAPIC RTE = 0x%x:%x(V) -> 0x%x:%x(P)",
dev_dbg(ACRN_DBG_IRQ, "IOAPIC RTE %s = 0x%x:%x(V) -> 0x%x:%x(P)",
(rte.ir_bits.intr_format != 0U) ? "Remappable Format" : "Compatibility Format",
virt_rte.u.hi_32, virt_rte.u.lo_32,
rte.u.hi_32, rte.u.lo_32);
} else {
@ -174,7 +235,8 @@ ptirq_build_physical_rte(struct acrn_vm *vm, struct ptirq_remapping_info *entry)
rte.bits.trigger_mode = IOAPIC_RTE_TRGRMODE_LEVEL;
}
dev_dbg(ACRN_DBG_IRQ, "IOAPIC RTE = 0x%x:%x(P) -> 0x%x:%x(P)",
dev_dbg(ACRN_DBG_IRQ, "IOAPIC RTE %s = 0x%x:%x(P) -> 0x%x:%x(P)",
(rte.ir_bits.intr_format != 0U) ? "Remappable Format" : "Compatibility Format",
phys_rte.u.hi_32, phys_rte.u.lo_32,
rte.u.hi_32, rte.u.lo_32);
}
@ -239,6 +301,7 @@ remove_msix_remapping(const struct acrn_vm *vm, uint16_t virt_bdf, uint32_t entr
{
struct ptirq_remapping_info *entry;
DEFINE_MSI_SID(virt_sid, virt_bdf, entry_nr);
struct intr_source intr_src;
entry = ptirq_lookup_entry_by_sid(PTDEV_INTR_MSI, &virt_sid, vm);
if (entry != NULL) {
@ -247,6 +310,10 @@ remove_msix_remapping(const struct acrn_vm *vm, uint16_t virt_bdf, uint32_t entr
ptirq_deactivate_entry(entry);
}
intr_src.is_msi = true;
intr_src.src.msi.value = entry->phys_sid.msi_id.bdf;
dmar_free_irte(intr_src, (uint16_t)entry->allocated_pirq);
dev_dbg(ACRN_DBG_IRQ,
"VM%d MSIX remove vector mapping vbdf-pbdf:0x%x-0x%x idx=%d",
entry->vm->vm_id, virt_bdf,
@ -326,6 +393,7 @@ static void remove_intx_remapping(struct acrn_vm *vm, uint32_t virt_pin, bool pi
{
uint32_t phys_irq;
struct ptirq_remapping_info *entry;
struct intr_source intr_src;
if (((!pic_pin) && (virt_pin >= vioapic_pincount(vm))) || (pic_pin && (virt_pin >= vpic_pincount()))) {
pr_err("virtual irq pin is invalid!\n");
@ -338,6 +406,10 @@ static void remove_intx_remapping(struct acrn_vm *vm, uint32_t virt_pin, bool pi
ioapic_gsi_mask_irq(phys_irq);
ptirq_deactivate_entry(entry);
intr_src.is_msi = false;
intr_src.src.ioapic_id = ioapic_irq_to_ioapic_id(phys_irq);
dmar_free_irte(intr_src, (uint16_t)phys_irq);
dev_dbg(ACRN_DBG_IRQ,
"deactive %s intx entry:ppin=%d, pirq=%d ",
pic_pin ? "vPIC" : "vIOAPIC",
@ -543,9 +615,9 @@ int32_t ptirq_msix_remap(struct acrn_vm *vm, uint16_t virt_bdf,
/* build physical config MSI, update to info->pmsi_xxx */
if (is_lapic_pt(vm)) {
/* for vm with lapic-pt, keep vector from guest */
ptirq_build_physical_msi(vm, info, info->vmsi_data.bits.vector);
ptirq_build_physical_msi(vm, info, entry, (uint32_t)info->vmsi_data.bits.vector);
} else {
ptirq_build_physical_msi(vm, info, irq_to_vector(entry->allocated_pirq));
ptirq_build_physical_msi(vm, info, entry, irq_to_vector(entry->allocated_pirq));
}
entry->msi = *info;

View File

@ -9,6 +9,7 @@
#include <acpi.h>
#define IOAPIC_MAX_PIN 240U
#define IOAPIC_INVALID_ID 0xFFU
/*
* IOAPIC_MAX_LINES is architecturally defined.
@ -370,6 +371,19 @@ ioapic_nr_pins(void *ioapic_base)
return nr_pins;
}
uint8_t ioapic_irq_to_ioapic_id(uint32_t irq)
{
uint8_t ret;
if (ioapic_irq_is_gsi(irq)) {
ret = gsi_table_data[irq].ioapic_id;
} else {
ret = IOAPIC_INVALID_ID;
}
return ret;
}
int32_t init_ioapic_id_info(void)
{
int32_t ret = 0;

View File

@ -76,6 +76,7 @@ static inline uint64_t dmar_set_bitslice(uint64_t var, uint64_t mask, uint32_t p
#define DMAR_INV_STATUS_WRITE_SHIFT 5U
#define DMAR_INV_CONTEXT_CACHE_DESC 0x01UL
#define DMAR_INV_IOTLB_DESC 0x02UL
#define DMAR_INV_IEC_DESC 0x04UL
#define DMAR_INV_WAIT_DESC 0x05UL
#define DMAR_INV_STATUS_WRITE (1UL << DMAR_INV_STATUS_WRITE_SHIFT)
#define DMAR_INV_STATUS_INCOMPLETE 0UL
@ -87,6 +88,9 @@ static inline uint64_t dmar_set_bitslice(uint64_t var, uint64_t mask, uint32_t p
#define DMAR_IR_ENABLE_EIM_SHIFT 11UL
#define DMAR_IR_ENABLE_EIM (1UL << DMAR_IR_ENABLE_EIM_SHIFT)
#define DMAR_IECI_INDEXED 1U
#define DMAR_IEC_GLOBAL_INVL 0U
enum dmar_cirg_type {
DMAR_CIRG_RESERVED = 0,
DMAR_CIRG_GLOBAL,
@ -503,6 +507,41 @@ static int32_t dmar_register_hrhd(struct dmar_drhd_rt *dmar_unit)
return ret;
}
static struct dmar_drhd_rt *ioapic_to_dmaru(uint16_t ioapic_id, union pci_bdf *sid)
{
struct dmar_info *info = get_dmar_info();
struct dmar_drhd_rt *dmar_unit = NULL;
uint32_t i, j;
bool found = false;
if (info == NULL) {
pr_fatal("%s: can't find dmar info\n", __func__);
} else {
for (j = 0U; j < info->drhd_count; j++) {
dmar_unit = &dmar_drhd_units[j];
for (i = 0U; i < dmar_unit->drhd->dev_cnt; i++) {
if ((dmar_unit->drhd->devices[i].type == ACPI_DMAR_SCOPE_TYPE_IOAPIC) &&
(dmar_unit->drhd->devices[i].id == ioapic_id)) {
sid->bits.f = pci_func((uint8_t)dmar_unit->drhd->devices[i].devfun);
sid->bits.d = pci_slot((uint8_t)dmar_unit->drhd->devices[i].devfun);
sid->bits.b = dmar_unit->drhd->devices[i].bus;
found = true;
break;
}
}
if (found) {
break;
}
}
if (j == info->drhd_count) {
dmar_unit = NULL;
}
}
return dmar_unit;
}
static struct dmar_drhd_rt *device_to_dmaru(uint16_t segment, uint8_t bus, uint8_t devfun)
{
struct dmar_info *info = get_dmar_info();
@ -695,6 +734,34 @@ static void dmar_set_intr_remap_table(struct dmar_drhd_rt *dmar_unit)
spinlock_release(&(dmar_unit->lock));
}
static void dmar_invalid_iec(struct dmar_drhd_rt *dmar_unit, uint16_t intr_index,
uint8_t index_mask, bool is_global)
{
struct dmar_qi_desc invalidate_desc;
invalidate_desc.upper = 0UL;
invalidate_desc.lower = DMAR_INV_IEC_DESC;
if (is_global) {
invalidate_desc.lower |= DMAR_IEC_GLOBAL_INVL;
} else {
invalidate_desc.lower |= DMAR_IECI_INDEXED | dma_iec_index(intr_index, index_mask);
}
if (invalidate_desc.lower != 0UL) {
spinlock_obtain(&(dmar_unit->lock));
dmar_issue_qi_request(dmar_unit, invalidate_desc);
spinlock_release(&(dmar_unit->lock));
}
}
static void dmar_invalid_iec_global(struct dmar_drhd_rt *dmar_unit)
{
dmar_invalid_iec(dmar_unit, 0U, 0U, true);
}
static void dmar_set_root_table(struct dmar_drhd_rt *dmar_unit)
{
uint64_t address;
@ -1280,3 +1347,78 @@ void init_iommu_sos_vm_domain(struct acrn_vm *sos_vm)
}
}
}
int32_t dmar_assign_irte(struct intr_source intr_src, union dmar_ir_entry irte, uint16_t index)
{
struct dmar_drhd_rt *dmar_unit;
union dmar_ir_entry *ir_table, *ir_entry;
union pci_bdf sid;
uint64_t trigger_mode;
int32_t ret = 0;
if (intr_src.is_msi) {
dmar_unit = device_to_dmaru(0U, (uint8_t)intr_src.src.msi.bits.b, pci_devfn(intr_src.src.msi.value));
sid.value = intr_src.src.msi.value;
trigger_mode = 0x0UL;
} else {
dmar_unit = ioapic_to_dmaru(intr_src.src.ioapic_id, &sid);
trigger_mode = irte.bits.trigger_mode;
}
if (dmar_unit == NULL) {
pr_err("no dmar unit found for device: %x:%x.%x", sid.bits.b, sid.bits.d, sid.bits.f);
ret = -EINVAL;
} else if (dmar_unit->drhd->ignore) {
dev_dbg(ACRN_DBG_IOMMU, "device is ignored :0x%x:%x.%x", sid.bits.b, sid.bits.d, sid.bits.f);
ret = -EINVAL;
} else if (dmar_unit->ir_table_addr == 0UL) {
pr_err("IR table is not set for dmar unit");
ret = -EINVAL;
} else {
irte.bits.svt = 0x1UL;
irte.bits.sq = 0x0UL;
irte.bits.sid = sid.value;
irte.bits.present = 0x1UL;
irte.bits.mode = 0x0UL;
irte.bits.trigger_mode = trigger_mode;
irte.bits.fpd = 0x0UL;
ir_table = (union dmar_ir_entry *)hpa2hva(dmar_unit->ir_table_addr);
ir_entry = ir_table + index;
ir_entry->entry.upper = irte.entry.upper;
ir_entry->entry.lower = irte.entry.lower;
iommu_flush_cache(dmar_unit, ir_entry, sizeof(union dmar_ir_entry));
dmar_invalid_iec_global(dmar_unit);
}
return ret;
}
void dmar_free_irte(struct intr_source intr_src, uint16_t index)
{
struct dmar_drhd_rt *dmar_unit;
union dmar_ir_entry *ir_table, *ir_entry;
union pci_bdf sid;
if (intr_src.is_msi) {
dmar_unit = device_to_dmaru(0U, (uint8_t)intr_src.src.msi.bits.b, pci_devfn(intr_src.src.msi.value));
} else {
dmar_unit = ioapic_to_dmaru(intr_src.src.ioapic_id, &sid);
}
if (dmar_unit == NULL) {
pr_err("no dmar unit found for device: %x:%x.%x", intr_src.src.msi.bits.b,
intr_src.src.msi.bits.d, intr_src.src.msi.bits.f);
} else if (dmar_unit->drhd->ignore) {
dev_dbg(ACRN_DBG_IOMMU, "device is ignored :0x%x:%x.%x", intr_src.src.msi.bits.b,
intr_src.src.msi.bits.d, intr_src.src.msi.bits.f);
} else if (dmar_unit->ir_table_addr == 0UL) {
pr_err("IR table is not set for dmar unit");
} else {
ir_table = (union dmar_ir_entry *)hpa2hva(dmar_unit->ir_table_addr);
ir_entry = ir_table + index;
ir_entry->bits.present = 0x0UL;
iommu_flush_cache(dmar_unit, ir_entry, sizeof(union dmar_ir_entry));
dmar_invalid_iec_global(dmar_unit);
}
}

View File

@ -19,7 +19,6 @@ enum acpi_dmar_type {
ACPI_DMAR_TYPE_RESERVED = 5
};
struct acpi_table_dmar {
/* Common ACPI table header */
struct acpi_table_header header;

View File

@ -209,6 +209,19 @@ union ioapic_rte {
uint64_t rsvd_1:39;
uint64_t dest_field:8;
} bits __packed;
struct {
uint32_t vector:8;
uint32_t constant:3;
uint32_t intr_index_high:1;
uint32_t intr_polarity:1;
uint32_t remote_irr:1;
uint32_t trigger_mode:1;
uint32_t intr_mask:1;
uint32_t rsvd_1:15;
uint32_t rsvd_2:16;
uint32_t intr_format:1;
uint32_t intr_index_low:15;
} ir_bits __packed;
};
/******************************************************************************

View File

@ -21,6 +21,7 @@ void ioapic_setup_irqs(void);
bool ioapic_irq_is_gsi(uint32_t irq);
uint32_t ioapic_irq_to_pin(uint32_t irq);
int32_t init_ioapic_id_info(void);
uint8_t ioapic_irq_to_ioapic_id(uint32_t irq);
/**
* @brief Get irq num from pin num

View File

@ -347,6 +347,11 @@ static inline uint8_t dma_iotlb_invl_addr_am(uint8_t am)
return (am & 0x3fU);
}
static inline uint64_t dma_iec_index(uint16_t index, uint8_t index_mask)
{
return ((((uint64_t)index & 0xFFFFU) << 32U) | (((uint64_t)index_mask & 0x1FU) << 27U));
}
#define DMA_IOTLB_INVL_ADDR_IH_UNMODIFIED (((uint64_t)1UL) << 6U)
/* FECTL_REG */
@ -474,6 +479,31 @@ struct dmar_info {
struct dmar_drhd *drhd_units;
};
union dmar_ir_entry {
struct {
uint64_t lower;
uint64_t upper;
} entry;
struct {
uint64_t present:1;
uint64_t fpd:1;
uint64_t dest_mode:1;
uint64_t rh:1;
uint64_t trigger_mode:1;
uint64_t delivery_mode:3;
uint64_t sw_bits:4;
uint64_t rsvd_1:3;
uint64_t mode:1;
uint64_t vector:8;
uint64_t rsvd_2:8;
uint64_t dest:32;
uint64_t sid:16;
uint64_t sq:2;
uint64_t svt:2;
uint64_t rsvd_3:44;
} bits __packed;
};
extern struct dmar_info *get_dmar_info(void);
/**
@ -635,6 +665,27 @@ void init_iommu_sos_vm_domain(struct acrn_vm *sos_vm);
*/
bool iommu_snoop_supported(const struct acrn_vm *vm);
/**
* @brief Assign RTE for Interrupt Remapping Table.
*
* @param[in] intr_src filled with type of interrupt source and the source
* @param[in] irte filled with info about interrupt deliverymode, destination and destination mode
* @param[in] index into Interrupt Remapping Table
*
* @retval -EINVAL if corresponding DMAR is not present
* @retval 0 otherwise
*
*/
int32_t dmar_assign_irte(struct intr_source intr_src, union dmar_ir_entry irte, uint16_t index);
/**
* @brief Free RTE for Interrupt Remapping Table.
*
* @param[in] intr_src filled with type of interrupt source and the source
* @param[in] index into Interrupt Remapping Table
*
*/
void dmar_free_irte(struct intr_source intr_src, uint16_t index);
/**
* @}
*/

View File

@ -23,6 +23,24 @@ union source_id (name) = {.msi_id = {.bdf = (a), .entry_nr = (b)} }
#define DEFINE_IOAPIC_SID(name, a, b) \
union source_id (name) = {.intx_id = {.pin = (a), .src = (b)} }
union source {
uint16_t ioapic_id;
union pci_bdf msi;
};
struct intr_source {
bool is_msi;
union source src;
};
union irte_index {
uint16_t index;
struct {
uint16_t index_low:15;
uint16_t index_high:1;
} bits __packed;
};
union source_id {
uint64_t value;
struct {
@ -56,6 +74,16 @@ union msi_addr_reg {
uint32_t addr_base:12;
uint32_t hi_32;
} bits __packed;
struct {
uint32_t rsvd_1:2;
uint32_t intr_index_high:1;
uint32_t shv:1;
uint32_t intr_format:1;
uint32_t intr_index_low:15;
uint32_t constant:12;
uint32_t hi_32;
} ir_bits __packed;
};
/*

View File

@ -174,6 +174,11 @@ static inline uint8_t pci_func(uint16_t bdf)
return (uint8_t)(bdf & 0x7U);
}
static inline uint8_t pci_devfn(uint16_t bdf)
{
return (uint8_t)(bdf & 0xFFU);
}
uint32_t pci_pdev_read_cfg(union pci_bdf bdf, uint32_t offset, uint32_t bytes);
void pci_pdev_write_cfg(union pci_bdf bdf, uint32_t offset, uint32_t bytes, uint32_t val);
void enable_disable_pci_intx(union pci_bdf bdf, bool enable);