On Wed, Oct 26, 2016 at 03:47:04PM +0800, Peter Xu wrote: > 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 | 115 ++++++++++++++++++++++++++++++++++++++++++++++++++ > x86/Makefile.x86_64 | 2 + > x86/intel-iommu.c | 27 ++++++++++++ > 4 files changed, 232 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..02eaf39 > --- /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 ONE_BIT_ONLY(x) ((x) && !((x) & ((x) - 1))) How about instead adding static inline bool is_power_of_2(unsigned long n) { return (n && !(n & (n - 1))); } to libcflat.h, because there's a few other places I've needed this check myself. > + > +#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(ONE_BIT_ONLY(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; Please use {} when adding comments between an if and its one line. > + > + /* Make sure IOMMU handled our command request */ > + while (!(vtd_readl(DMAR_GSTS_REG) & cmd)); > +} > + > +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..537ce62 > --- /dev/null > +++ b/lib/x86/intel-iommu.h > @@ -0,0 +1,115 @@ > +/* > + * 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" > + > +#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) > + > +static inline void vtd_writel(unsigned int reg, uint32_t value) > +{ > + *(uint32_t *)(Q35_HOST_BRIDGE_IOMMU_ADDR + reg) = value; > +} > + > +static inline void vtd_writeq(unsigned int reg, uint64_t value) > +{ > + *(uint64_t *)(Q35_HOST_BRIDGE_IOMMU_ADDR + reg) = value; > +} > + > +static inline uint32_t vtd_readl(unsigned int reg) > +{ > + return *(uint32_t *)(Q35_HOST_BRIDGE_IOMMU_ADDR + reg); > +} > + > +static inline uint64_t vtd_readq(unsigned int reg) > +{ > + return *(uint64_t *)(Q35_HOST_BRIDGE_IOMMU_ADDR + 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 > Looks good to me, framework-wise (I didn't open the spec...) Thanks, drew -- 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