hv: Add support to add IR tables

Interrupt Remapping hardware in x86 can hold 64K entries with each entry
of size 16 bytes. So 256 entries occupy 4K. Adding a configuration for
developer to choose number of IR entries, in multiples of 256. ACRN does
not boot on platforms that does not support Interrupt Remapping and
Extended Interrupt Mode

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:
Grandhi, Sainath 2019-01-26 00:04:49 -08:00 committed by Eddie Dong
parent cb46937bf5
commit 7104f0a512
2 changed files with 55 additions and 0 deletions

View File

@ -329,6 +329,12 @@ config MAX_IOAPIC_NUM
range 1 8
default 1
config MAX_IR_ENTRIES
int "Maximum number of Interrupt Remapping Entries"
default 256
help
Minimum value is 256. Value must be 2^n.
config IOMMU_BUS_NUM
hex "Highest PCI bus ID used during IOMMU initialization"
default 0x10 if PLATFORM_SBL

View File

@ -71,6 +71,7 @@ static inline uint64_t dmar_set_bitslice(uint64_t var, uint64_t mask, uint32_t p
#define DMAR_INVALIDATION_QUEUE_SIZE 4096U
#define DMAR_QI_INV_ENTRY_SIZE 16U
#define DMAR_NUM_IR_ENTRIES_PER_PAGE 256U
#define DMAR_INV_STATUS_WRITE_SHIFT 5U
#define DMAR_INV_CONTEXT_CACHE_DESC 0x01UL
@ -83,6 +84,9 @@ static inline uint64_t dmar_set_bitslice(uint64_t var, uint64_t mask, uint32_t p
#define DMAR_INV_STATUS_DATA (DMAR_INV_STATUS_COMPLETED << DMAR_INV_STATUS_DATA_SHIFT)
#define DMAR_INV_WAIT_DESC_LOWER (DMAR_INV_STATUS_WRITE | DMAR_INV_WAIT_DESC | DMAR_INV_STATUS_DATA)
#define DMAR_IR_ENABLE_EIM_SHIFT 11UL
#define DMAR_IR_ENABLE_EIM (1UL << DMAR_IR_ENABLE_EIM_SHIFT)
enum dmar_cirg_type {
DMAR_CIRG_RESERVED = 0,
DMAR_CIRG_GLOBAL,
@ -105,6 +109,7 @@ struct dmar_drhd_rt {
struct dmar_drhd *drhd;
uint64_t root_table_addr;
uint64_t ir_table_addr;
uint64_t qi_queue;
uint16_t qi_tail;
@ -150,6 +155,10 @@ struct context_table {
struct page buses[CONFIG_IOMMU_BUS_NUM];
};
struct intr_remap_table {
struct page tables[CONFIG_MAX_IR_ENTRIES/DMAR_NUM_IR_ENTRIES_PER_PAGE];
};
static inline uint8_t* get_root_table(uint32_t dmar_index)
{
static struct page root_tables[CONFIG_MAX_IOMMU_NUM] __aligned(PAGE_SIZE);
@ -171,6 +180,12 @@ static inline uint8_t *get_qi_queue(uint32_t dmar_index)
return qi_queues[dmar_index].contents;
}
static inline uint8_t *get_ir_table(uint32_t dmar_index)
{
static struct intr_remap_table ir_tables[CONFIG_MAX_IOMMU_NUM] __aligned(PAGE_SIZE);
return ir_tables[dmar_index].tables[0].contents;
}
bool iommu_snoop_supported(const struct acrn_vm *vm)
{
bool ret;
@ -461,6 +476,12 @@ static int32_t dmar_register_hrhd(struct dmar_drhd_rt *dmar_unit)
} else if (iommu_ecap_qi(dmar_unit->ecap) == 0U) {
pr_fatal("%s: dmar unit doesn't support Queued Invalidation!", __func__);
ret = -ENODEV;
} else if (iommu_ecap_ir(dmar_unit->ecap) == 0U) {
pr_fatal("%s: dmar unit doesn't support Interrupt Remapping!", __func__);
ret = -ENODEV;
} else if (iommu_ecap_eim(dmar_unit->ecap) == 0U) {
pr_fatal("%s: dmar unit doesn't support Extended Interrupt Mode!", __func__);
ret = -ENODEV;
} else {
if ((iommu_ecap_c(dmar_unit->ecap) == 0U) && (dmar_unit->drhd->ignore != 0U)) {
iommu_page_walk_coherent = false;
@ -647,6 +668,33 @@ static void dmar_invalid_iotlb_global(struct dmar_drhd_rt *dmar_unit)
dmar_invalid_iotlb(dmar_unit, 0U, 0UL, 0U, false, DMAR_IIRG_GLOBAL);
}
static void dmar_set_intr_remap_table(struct dmar_drhd_rt *dmar_unit)
{
uint64_t address;
uint32_t status;
uint8_t size;
spinlock_obtain(&(dmar_unit->lock));
if (dmar_unit->ir_table_addr == 0UL) {
dmar_unit->ir_table_addr = hva2hpa(get_ir_table(dmar_unit->index));
}
address = dmar_unit->ir_table_addr | DMAR_IR_ENABLE_EIM;
/* Set number of bits needed to represent the entries minus 1 */
size = (uint8_t) fls32(CONFIG_MAX_IR_ENTRIES) - 1U;
address = address | size;
iommu_write64(dmar_unit, DMAR_IRTA_REG, address);
iommu_write32(dmar_unit, DMAR_GCMD_REG, dmar_unit->gcmd | DMA_GCMD_SIRTP);
dmar_wait_completion(dmar_unit, DMAR_GSTS_REG, DMA_GSTS_IRTPS, false, &status);
spinlock_release(&(dmar_unit->lock));
}
static void dmar_set_root_table(struct dmar_drhd_rt *dmar_unit)
{
uint64_t address;
@ -868,6 +916,7 @@ static void dmar_prepare(struct dmar_drhd_rt *dmar_unit)
dmar_setup_interrupt(dmar_unit);
dmar_set_root_table(dmar_unit);
dmar_enable_qi(dmar_unit);
dmar_set_intr_remap_table(dmar_unit);
}
static void dmar_enable(struct dmar_drhd_rt *dmar_unit)