From 7104f0a512c4750152f96c20e77bc07e69141ead Mon Sep 17 00:00:00 2001 From: "Grandhi, Sainath" Date: Sat, 26 Jan 2019 00:04:49 -0800 Subject: [PATCH] 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 Reviewed-by: Binbin Wu --- hypervisor/arch/x86/Kconfig | 6 +++++ hypervisor/arch/x86/vtd.c | 49 +++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/hypervisor/arch/x86/Kconfig b/hypervisor/arch/x86/Kconfig index 98495ba1d..9a9db58b3 100644 --- a/hypervisor/arch/x86/Kconfig +++ b/hypervisor/arch/x86/Kconfig @@ -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 diff --git a/hypervisor/arch/x86/vtd.c b/hypervisor/arch/x86/vtd.c index 74b74665f..c81d32b96 100644 --- a/hypervisor/arch/x86/vtd.c +++ b/hypervisor/arch/x86/vtd.c @@ -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)