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. Signed-off-by: Peter Xu <peterx@xxxxxxxxxx> --- lib/x86/intel-iommu.c | 138 ++++++++++++++++++++++++++++++++++++++++++++++++++ lib/x86/intel-iommu.h | 106 ++++++++++++++++++++++++++++++++++++++ x86/Makefile.x86_64 | 2 + x86/intel-iommu.c | 41 +++++++++++++++ 4 files changed, 287 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..9f55394 --- /dev/null +++ b/lib/x86/intel-iommu.c @@ -0,0 +1,138 @@ +/* + * 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" + +void vtd_reg_write_4(unsigned int reg, uint32_t value) +{ + *(uint32_t *)(Q35_HOST_BRIDGE_IOMMU_ADDR + reg) = value; +} + +void vtd_reg_write_8(unsigned int reg, uint64_t value) +{ + *(uint64_t *)(Q35_HOST_BRIDGE_IOMMU_ADDR + reg) = value; +} + +uint32_t vtd_reg_read_4(unsigned int reg) +{ + return *(uint32_t *)(Q35_HOST_BRIDGE_IOMMU_ADDR + reg); +} + +uint64_t vtd_reg_read_8(unsigned int reg) +{ + return *(uint64_t *)(Q35_HOST_BRIDGE_IOMMU_ADDR + reg); +} + +uint32_t vtd_version(void) +{ + return vtd_reg_read_4(DMAR_VER_REG); +} + +uint64_t vtd_cap(void) +{ + return vtd_reg_read_8(DMAR_CAP_REG); +} + +uint64_t vtd_ecap(void) +{ + return vtd_reg_read_8(DMAR_ECAP_REG); +} + +uint32_t vtd_status(void) +{ + return vtd_reg_read_4(DMAR_GSTS_REG); +} + +uint32_t vtd_fault_status(void) +{ + return vtd_reg_read_4(DMAR_FSTS_REG); +} + +uint64_t vtd_root_table(void) +{ + /* No extend root table support yet */ + return vtd_reg_read_8(DMAR_RTADDR_REG); +} + +uint64_t vtd_ir_table(void) +{ + return vtd_reg_read_8(DMAR_IRTA_REG) & 0xfffffffffffff000; +} + +void vtd_gcmd_or(uint32_t cmd) +{ + uint32_t status = vtd_status(); + /* + * Logically, we need to read DMAR_GSTS_REG until IOMMU + * handles the write request. However for QEMU/KVM, this write + * is always sync, so when this returns, we should be sure + * that the GCMD write is done. + */ + vtd_reg_write_4(DMAR_GCMD_REG, status | cmd); +} + +void vtd_dump_init_info(void) +{ + printf("VT-d version: 0x%x\n", vtd_version()); + printf(" cap: 0x%016lx\n", vtd_cap()); + printf(" ecap: 0x%016lx\n", vtd_ecap()); +} + +void vtd_enable_qi(void) +{ + vtd_gcmd_or(VTD_GCMD_QI); +} + +void vtd_setup_root_table(void) +{ + void *root = alloc_page(); + + memset(root, 0, PAGE_SIZE); + vtd_reg_write_8(DMAR_RTADDR_REG, virt_to_phys(root)); + vtd_gcmd_or(VTD_GCMD_ROOT); + printf("DMAR table address: 0x%016lx\n", vtd_root_table()); +} + +void vtd_setup_ir_table(void) +{ + void *root = alloc_page(); + + memset(root, 0, PAGE_SIZE); + vtd_reg_write_8(DMAR_IRTA_REG, virt_to_phys(root)); + /* 0xf stands for table size (2^(0xf+1) == 65536) */ + vtd_gcmd_or(VTD_GCMD_IR_TABLE | 0xf); + printf("IR table address: 0x%016lx\n", vtd_ir_table()); +} + +void vtd_enable_dmar(void) +{ + vtd_gcmd_or(VTD_GCMD_DMAR); +} + +void vtd_enable_ir(void) +{ + vtd_gcmd_or(VTD_GCMD_IR); +} + +void vtd_init(void) +{ + setup_vm(); + smp_init(); + setup_idt(); + + vtd_dump_init_info(); + vtd_enable_qi(); + vtd_setup_root_table(); + vtd_setup_ir_table(); + vtd_enable_dmar(); + vtd_enable_ir(); +} diff --git a/lib/x86/intel-iommu.h b/lib/x86/intel-iommu.h new file mode 100644 index 0000000..0f687d1 --- /dev/null +++ b/lib/x86/intel-iommu.h @@ -0,0 +1,106 @@ +/* + * 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. + */ + +#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_ROOT (0x40000000) +#define VTD_GCMD_DMAR (0x80000000) + +void vtd_reg_write_4(unsigned int reg, uint32_t value); +void vtd_reg_write_8(unsigned int reg, uint64_t value); +uint32_t vtd_reg_read_4(unsigned int reg); +uint64_t vtd_reg_read_8(unsigned int reg); +uint32_t vtd_version(void); +uint64_t vtd_cap(void); +uint64_t vtd_ecap(void); +uint32_t vtd_status(void); +uint32_t vtd_fault_status(void); +uint64_t vtd_root_table(void); +uint64_t vtd_ir_table(void); +void vtd_dump_init_info(void); +void vtd_enable_qi(void); +void vtd_setup_root_table(void); +void vtd_setup_ir_table(void); +void vtd_enable_dmar(void); +void vtd_enable_ir(void); +void vtd_init(void); + +#endif diff --git a/x86/Makefile.x86_64 b/x86/Makefile.x86_64 index e166911..a8e9445 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..60d512a --- /dev/null +++ b/x86/intel-iommu.c @@ -0,0 +1,41 @@ +/* + * 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[]) +{ + setup_vm(); + smp_init(); + setup_idt(); + + vtd_dump_init_info(); + report("init status check", vtd_status() == 0); + report("fault status check", vtd_fault_status() == 0); + + vtd_enable_qi(); + report("QI enablement", vtd_status() | VTD_GCMD_QI); + + vtd_setup_root_table(); + report("DMAR table setup", vtd_status() | VTD_GCMD_ROOT); + + vtd_setup_ir_table(); + report("IR table setup", vtd_status() | VTD_GCMD_IR_TABLE); + + vtd_enable_dmar(); + report("DMAR enablement", vtd_status() & VTD_GCMD_DMAR); + + vtd_enable_ir(); + report("IR enablement", vtd_status() & 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