Adding fundamental init test for Intel IOMMU. This includes basic initialization of Intel IOMMU device, like DMAR (DMA Remapping), IR (Interrupt Remapping), QI (Queue Invalidation), etc. Further tests can use vtd_init() to initialize Intel IOMMU environment. Signed-off-by: Peter Xu <peterx@xxxxxxxxxx> --- lib/x86/intel-iommu.c | 88 +++++++++++++++++++++++++++++++++++++ lib/x86/intel-iommu.h | 118 ++++++++++++++++++++++++++++++++++++++++++++++++++ x86/Makefile.x86_64 | 2 + x86/intel-iommu.c | 27 ++++++++++++ 4 files changed, 235 insertions(+) create mode 100644 lib/x86/intel-iommu.c create mode 100644 lib/x86/intel-iommu.h create mode 100644 x86/intel-iommu.c diff --git a/lib/x86/intel-iommu.c b/lib/x86/intel-iommu.c new file mode 100644 index 0000000..6f52697 --- /dev/null +++ b/lib/x86/intel-iommu.c @@ -0,0 +1,88 @@ +/* + * Intel IOMMU APIs + * + * Copyright (C) 2016 Red Hat, Inc. + * + * Authors: + * Peter Xu <peterx@xxxxxxxxxx>, + * + * This work is licensed under the terms of the GNU LGPL, version 2 or + * later. + */ + +#include "intel-iommu.h" + +#define VTD_RTA_MASK (PAGE_MASK) +#define VTD_IRTA_MASK (PAGE_MASK) + +static uint64_t vtd_root_table(void) +{ + /* No extend root table support yet */ + return vtd_readq(DMAR_RTADDR_REG) & VTD_RTA_MASK; +} + +static uint64_t vtd_ir_table(void) +{ + return vtd_readq(DMAR_IRTA_REG) & VTD_IRTA_MASK; +} + +static void vtd_gcmd_or(uint32_t cmd) +{ + uint32_t status; + + /* We only allow set one bit for each time */ + assert(is_power_of_2(cmd)); + + status = vtd_readl(DMAR_GSTS_REG); + vtd_writel(DMAR_GCMD_REG, status | cmd); + + if (cmd & VTD_GCMD_ONE_SHOT_BITS) { + /* One-shot bits are taking effect immediately */ + return; + } + + /* Make sure IOMMU handled our command request */ + while (!(vtd_readl(DMAR_GSTS_REG) & cmd)) + cpu_relax(); +} + +static void vtd_dump_init_info(void) +{ + printf("VT-d version: 0x%x\n", vtd_readl(DMAR_VER_REG)); + printf(" cap: 0x%016lx\n", vtd_readq(DMAR_CAP_REG)); + printf(" ecap: 0x%016lx\n", vtd_readq(DMAR_ECAP_REG)); +} + +static void vtd_setup_root_table(void) +{ + void *root = alloc_page(); + + memset(root, 0, PAGE_SIZE); + vtd_writeq(DMAR_RTADDR_REG, virt_to_phys(root)); + vtd_gcmd_or(VTD_GCMD_ROOT); + printf("DMAR table address: 0x%016lx\n", vtd_root_table()); +} + +static void vtd_setup_ir_table(void) +{ + void *root = alloc_page(); + + memset(root, 0, PAGE_SIZE); + /* 0xf stands for table size (2^(0xf+1) == 65536) */ + vtd_writeq(DMAR_IRTA_REG, virt_to_phys(root) | 0xf); + vtd_gcmd_or(VTD_GCMD_IR_TABLE); + printf("IR table address: 0x%016lx\n", vtd_ir_table()); +} + +void vtd_init(void) +{ + setup_vm(); + smp_init(); + + vtd_dump_init_info(); + vtd_gcmd_or(VTD_GCMD_QI); /* Enable QI */ + vtd_setup_root_table(); + vtd_setup_ir_table(); + vtd_gcmd_or(VTD_GCMD_DMAR); /* Enable DMAR */ + vtd_gcmd_or(VTD_GCMD_IR); /* Enable IR */ +} diff --git a/lib/x86/intel-iommu.h b/lib/x86/intel-iommu.h new file mode 100644 index 0000000..d95d76c --- /dev/null +++ b/lib/x86/intel-iommu.h @@ -0,0 +1,118 @@ +/* + * Intel IOMMU header + * + * Copyright (C) 2016 Red Hat, Inc. + * + * Authors: + * Peter Xu <peterx@xxxxxxxxxx>, + * + * This work is licensed under the terms of the GNU LGPL, version 2 or + * later. + * + * (From include/linux/intel-iommu.h) + */ + +#ifndef __INTEL_IOMMU_H__ +#define __INTEL_IOMMU_H__ + +#include "libcflat.h" +#include "vm.h" +#include "isr.h" +#include "smp.h" +#include "desc.h" +#include "asm/io.h" + +#define Q35_HOST_BRIDGE_IOMMU_ADDR 0xfed90000ULL + +/* + * Intel IOMMU register specification + */ +#define DMAR_VER_REG 0x0 /* Arch version supported by this IOMMU */ +#define DMAR_CAP_REG 0x8 /* Hardware supported capabilities */ +#define DMAR_CAP_REG_HI 0xc /* High 32-bit of DMAR_CAP_REG */ +#define DMAR_ECAP_REG 0x10 /* Extended capabilities supported */ +#define DMAR_ECAP_REG_HI 0X14 +#define DMAR_GCMD_REG 0x18 /* Global command */ +#define DMAR_GSTS_REG 0x1c /* Global status */ +#define DMAR_RTADDR_REG 0x20 /* Root entry table */ +#define DMAR_RTADDR_REG_HI 0X24 +#define DMAR_CCMD_REG 0x28 /* Context command */ +#define DMAR_CCMD_REG_HI 0x2c +#define DMAR_FSTS_REG 0x34 /* Fault status */ +#define DMAR_FECTL_REG 0x38 /* Fault control */ +#define DMAR_FEDATA_REG 0x3c /* Fault event interrupt data */ +#define DMAR_FEADDR_REG 0x40 /* Fault event interrupt addr */ +#define DMAR_FEUADDR_REG 0x44 /* Upper address */ +#define DMAR_AFLOG_REG 0x58 /* Advanced fault control */ +#define DMAR_AFLOG_REG_HI 0X5c +#define DMAR_PMEN_REG 0x64 /* Enable protected memory region */ +#define DMAR_PLMBASE_REG 0x68 /* PMRR low addr */ +#define DMAR_PLMLIMIT_REG 0x6c /* PMRR low limit */ +#define DMAR_PHMBASE_REG 0x70 /* PMRR high base addr */ +#define DMAR_PHMBASE_REG_HI 0X74 +#define DMAR_PHMLIMIT_REG 0x78 /* PMRR high limit */ +#define DMAR_PHMLIMIT_REG_HI 0x7c +#define DMAR_IQH_REG 0x80 /* Invalidation queue head */ +#define DMAR_IQH_REG_HI 0X84 +#define DMAR_IQT_REG 0x88 /* Invalidation queue tail */ +#define DMAR_IQT_REG_HI 0X8c +#define DMAR_IQA_REG 0x90 /* Invalidation queue addr */ +#define DMAR_IQA_REG_HI 0x94 +#define DMAR_ICS_REG 0x9c /* Invalidation complete status */ +#define DMAR_IRTA_REG 0xb8 /* Interrupt remapping table addr */ +#define DMAR_IRTA_REG_HI 0xbc +#define DMAR_IECTL_REG 0xa0 /* Invalidation event control */ +#define DMAR_IEDATA_REG 0xa4 /* Invalidation event data */ +#define DMAR_IEADDR_REG 0xa8 /* Invalidation event address */ +#define DMAR_IEUADDR_REG 0xac /* Invalidation event address */ +#define DMAR_PQH_REG 0xc0 /* Page request queue head */ +#define DMAR_PQH_REG_HI 0xc4 +#define DMAR_PQT_REG 0xc8 /* Page request queue tail*/ +#define DMAR_PQT_REG_HI 0xcc +#define DMAR_PQA_REG 0xd0 /* Page request queue address */ +#define DMAR_PQA_REG_HI 0xd4 +#define DMAR_PRS_REG 0xdc /* Page request status */ +#define DMAR_PECTL_REG 0xe0 /* Page request event control */ +#define DMAR_PEDATA_REG 0xe4 /* Page request event data */ +#define DMAR_PEADDR_REG 0xe8 /* Page request event address */ +#define DMAR_PEUADDR_REG 0xec /* Page event upper address */ +#define DMAR_MTRRCAP_REG 0x100 /* MTRR capability */ +#define DMAR_MTRRCAP_REG_HI 0x104 +#define DMAR_MTRRDEF_REG 0x108 /* MTRR default type */ +#define DMAR_MTRRDEF_REG_HI 0x10c + +#define VTD_GCMD_IR_TABLE (0x1000000) +#define VTD_GCMD_IR (0x2000000) +#define VTD_GCMD_QI (0x4000000) +#define VTD_GCMD_WBF (0x8000000) /* Write Buffer Flush */ +#define VTD_GCMD_SFL (0x20000000) /* Set Fault Log */ +#define VTD_GCMD_ROOT (0x40000000) +#define VTD_GCMD_DMAR (0x80000000) +#define VTD_GCMD_ONE_SHOT_BITS (VTD_GCMD_IR_TABLE | VTD_GCMD_WBF | \ + VTD_GCMD_SFL | VTD_GCMD_ROOT) + +#define vtd_reg(reg) ((volatile void *)(Q35_HOST_BRIDGE_IOMMU_ADDR + reg)) + +static inline void vtd_writel(unsigned int reg, uint32_t value) +{ + __raw_writel(value, vtd_reg(reg)); +} + +static inline void vtd_writeq(unsigned int reg, uint64_t value) +{ + __raw_writeq(value, vtd_reg(reg)); +} + +static inline uint32_t vtd_readl(unsigned int reg) +{ + return __raw_readl(vtd_reg(reg)); +} + +static inline uint64_t vtd_readq(unsigned int reg) +{ + return __raw_readq(vtd_reg(reg)); +} + +void vtd_init(void); + +#endif diff --git a/x86/Makefile.x86_64 b/x86/Makefile.x86_64 index f82492b..3e2821e 100644 --- a/x86/Makefile.x86_64 +++ b/x86/Makefile.x86_64 @@ -4,6 +4,7 @@ ldarch = elf64-x86-64 CFLAGS += -mno-red-zone cflatobjs += lib/x86/setjmp64.o +cflatobjs += lib/x86/intel-iommu.o tests = $(TEST_DIR)/access.flat $(TEST_DIR)/apic.flat \ $(TEST_DIR)/emulator.flat $(TEST_DIR)/idt_test.flat \ @@ -14,6 +15,7 @@ tests = $(TEST_DIR)/access.flat $(TEST_DIR)/apic.flat \ tests += $(TEST_DIR)/svm.flat tests += $(TEST_DIR)/vmx.flat tests += $(TEST_DIR)/tscdeadline_latency.flat +tests += $(TEST_DIR)/intel-iommu.flat include $(TEST_DIR)/Makefile.common diff --git a/x86/intel-iommu.c b/x86/intel-iommu.c new file mode 100644 index 0000000..f247913 --- /dev/null +++ b/x86/intel-iommu.c @@ -0,0 +1,27 @@ +/* + * Intel IOMMU unit test. + * + * Copyright (C) 2016 Red Hat, Inc. + * + * Authors: + * Peter Xu <peterx@xxxxxxxxxx>, + * + * This work is licensed under the terms of the GNU LGPL, version 2 or + * later. + */ + +#include "intel-iommu.h" + +int main(int argc, char *argv[]) +{ + vtd_init(); + + report("fault status check", vtd_readl(DMAR_FSTS_REG) == 0); + report("QI enablement", vtd_readl(DMAR_GSTS_REG) & VTD_GCMD_QI); + report("DMAR table setup", vtd_readl(DMAR_GSTS_REG) & VTD_GCMD_ROOT); + report("IR table setup", vtd_readl(DMAR_GSTS_REG) & VTD_GCMD_IR_TABLE); + report("DMAR enablement", vtd_readl(DMAR_GSTS_REG) & VTD_GCMD_DMAR); + report("IR enablement", vtd_readl(DMAR_GSTS_REG) & VTD_GCMD_IR); + + return report_summary(); +} -- 2.7.4 -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html