* [PATCH 0/2] kvm-unit-tests: VMX: Test nested EPT features
@ 2013-09-09 4:57 Arthur Chunqi Li
2013-09-09 4:57 ` [PATCH 1/2] kvm-unit-tests: VMX: The framework of EPT for nested VMX testing Arthur Chunqi Li
` (2 more replies)
0 siblings, 3 replies; 11+ messages in thread
From: Arthur Chunqi Li @ 2013-09-09 4:57 UTC (permalink / raw)
To: kvm; +Cc: jan.kiszka, gleb, pbonzini, Arthur Chunqi Li
This series of patches provide the framework of nested EPT and some test
cases for nested EPT features.
Arthur Chunqi Li (2):
kvm-unit-tests: VMX: The framework of EPT for nested VMX testing
kvm-unit-tests: VMX: Test cases for nested EPT
x86/vmx.c | 159 ++++++++++++++++++++++++++++++++-
x86/vmx.h | 76 ++++++++++++++++
x86/vmx_tests.c | 266 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 497 insertions(+), 4 deletions(-)
--
1.7.9.5
^ permalink raw reply [flat|nested] 11+ messages in thread* [PATCH 1/2] kvm-unit-tests: VMX: The framework of EPT for nested VMX testing 2013-09-09 4:57 [PATCH 0/2] kvm-unit-tests: VMX: Test nested EPT features Arthur Chunqi Li @ 2013-09-09 4:57 ` Arthur Chunqi Li 2013-09-09 13:45 ` Paolo Bonzini 2013-09-09 4:57 ` [PATCH 2/2] kvm-unit-tests: VMX: Test cases for nested EPT Arthur Chunqi Li 2013-09-09 7:17 ` [PATCH 0/2] kvm-unit-tests: VMX: Test nested EPT features Jan Kiszka 2 siblings, 1 reply; 11+ messages in thread From: Arthur Chunqi Li @ 2013-09-09 4:57 UTC (permalink / raw) To: kvm; +Cc: jan.kiszka, gleb, pbonzini, Arthur Chunqi Li The framework of EPT for nested VMX, including functions to build up EPT paging structures, read/set EPT PTEs and setup a range of 1:1 map EPT. Signed-off-by: Arthur Chunqi Li <yzt356@gmail.com> --- x86/vmx.c | 159 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- x86/vmx.h | 76 +++++++++++++++++++++++++++++ 2 files changed, 231 insertions(+), 4 deletions(-) diff --git a/x86/vmx.c b/x86/vmx.c index ca36d35..87d1d55 100644 --- a/x86/vmx.c +++ b/x86/vmx.c @@ -143,6 +143,159 @@ asm( " call hypercall\n\t" ); +/* EPT paging structure related functions */ +/* install_ept_entry : Install a page to a given level in EPT + @pml4 : addr of pml4 table + @pte_level : level of PTE to set + @guest_addr : physical address of guest + @pte : pte value to set + @pt_page : address of page table, NULL for a new page + */ +void install_ept_entry(unsigned long *pml4, + int pte_level, + unsigned long guest_addr, + unsigned long pte, + unsigned long *pt_page) +{ + int level; + unsigned long *pt = pml4; + unsigned offset; + + for (level = EPT_PAGE_LEVEL; level > pte_level; --level) { + offset = (guest_addr >> ((level-1) * EPT_PGDIR_WIDTH + 12)) + & EPT_PGDIR_MASK; + if (!(pt[offset] & (EPT_PRESENT))) { + unsigned long *new_pt = pt_page; + if (!new_pt) + new_pt = alloc_page(); + else + pt_page = 0; + memset(new_pt, 0, PAGE_SIZE); + pt[offset] = virt_to_phys(new_pt) + | EPT_RA | EPT_WA | EPT_EA; + } + pt = phys_to_virt(pt[offset] & 0xffffffffff000ull); + } + offset = ((unsigned long)guest_addr >> ((level-1) * + EPT_PGDIR_WIDTH + 12)) & EPT_PGDIR_MASK; + pt[offset] = pte; +} + +/* Map a page, @perm is the permission of the page */ +void install_ept(unsigned long *pml4, + unsigned long phys, + unsigned long guest_addr, + u64 perm) +{ + install_ept_entry(pml4, 1, guest_addr, (phys & PAGE_MASK) | perm, 0); +} + +/* Map a 1G-size page */ +void install_1g_ept(unsigned long *pml4, + unsigned long phys, + unsigned long guest_addr, + u64 perm) +{ + install_ept_entry(pml4, 3, guest_addr, + (phys & PAGE_MASK) | perm | EPT_LARGE_PAGE, 0); +} + +/* Map a 2M-size page */ +void install_2m_ept(unsigned long *pml4, + unsigned long phys, + unsigned long guest_addr, + u64 perm) +{ + install_ept_entry(pml4, 2, guest_addr, + (phys & PAGE_MASK) | perm | EPT_LARGE_PAGE, 0); +} + +/* setup_ept_range : Setup a range of 1:1 mapped page to EPT paging structure. + @start : start address of guest page + @len : length of address to be mapped + @map_1g : whether 1G page map is used + @map_2m : whether 2M page map is used + @perm : permission for every page + */ +int setup_ept_range(unsigned long *pml4, unsigned long start, + unsigned long len, int map_1g, int map_2m, u64 perm) +{ + u64 phys = start; + u64 max = (u64)len + (u64)start; + + if (map_1g) { + while (phys + PAGE_SIZE_1G <= max) { + install_1g_ept(pml4, phys, phys, perm); + phys += PAGE_SIZE_1G; + } + } + if (map_2m) { + while (phys + PAGE_SIZE_2M <= max) { + install_2m_ept(pml4, phys, phys, perm); + phys += PAGE_SIZE_2M; + } + } + while (phys + PAGE_SIZE <= max) { + install_ept(pml4, phys, phys, perm); + phys += PAGE_SIZE; + } + return 0; +} + +/* get_ept_pte : Get the PTE of a given level in EPT, + @level == 1 means get the latest level*/ +unsigned long get_ept_pte(unsigned long *pml4, + unsigned long guest_addr, int level) +{ + int l; + unsigned long *pt = pml4, pte; + unsigned offset; + + for (l = EPT_PAGE_LEVEL; l > 1; --l) { + offset = (guest_addr >> (((l-1) * EPT_PGDIR_WIDTH) + 12)) + & EPT_PGDIR_MASK; + pte = pt[offset]; + if (!(pte & (EPT_PRESENT))) + return 0; + if (l == level) + return pte; + if (l < 4 && (pte & EPT_LARGE_PAGE)) + return pte; + pt = (unsigned long *)(pte & 0xffffffffff000ull); + } + offset = (guest_addr >> (((l-1) * EPT_PGDIR_WIDTH) + 12)) + & EPT_PGDIR_MASK; + pte = pt[offset]; + return pte; +} + +int set_ept_pte(unsigned long *pml4, unsigned long guest_addr, + int level, u64 pte_val) +{ + int l; + unsigned long *pt = pml4; + unsigned offset; + + if (level < 1 || level > 3) + return -1; + for (l = EPT_PAGE_LEVEL; l > 1; --l) { + offset = (guest_addr >> (((l-1) * EPT_PGDIR_WIDTH) + 12)) + & EPT_PGDIR_MASK; + if (l == level) { + pt[offset] = pte_val; + return 0; + } + if (!(pt[offset] & (EPT_PRESENT))) + return -1; + pt = (unsigned long *)(pt[offset] & 0xffffffffff000ull); + } + offset = (guest_addr >> (((l-1) * EPT_PGDIR_WIDTH) + 12)) + & EPT_PGDIR_MASK; + pt[offset] = pte_val; + return 0; +} + + static void init_vmcs_ctrl(void) { /* 26.2 CHECKS ON VMX CONTROLS AND HOST-STATE AREA */ @@ -336,10 +489,8 @@ static void init_vmx(void) : MSR_IA32_VMX_ENTRY_CTLS); ctrl_cpu_rev[0].val = rdmsr(basic.ctrl ? MSR_IA32_VMX_TRUE_PROC : MSR_IA32_VMX_PROCBASED_CTLS); - if (ctrl_cpu_rev[0].set & CPU_SECONDARY) - ctrl_cpu_rev[1].val = rdmsr(MSR_IA32_VMX_PROCBASED_CTLS2); - if (ctrl_cpu_rev[1].set & CPU_EPT || ctrl_cpu_rev[1].set & CPU_VPID) - ept_vpid.val = rdmsr(MSR_IA32_VMX_EPT_VPID_CAP); + ctrl_cpu_rev[1].val = rdmsr(MSR_IA32_VMX_PROCBASED_CTLS2); + ept_vpid.val = rdmsr(MSR_IA32_VMX_EPT_VPID_CAP); write_cr0((read_cr0() & fix_cr0_clr) | fix_cr0_set); write_cr4((read_cr4() & fix_cr4_clr) | fix_cr4_set | X86_CR4_VMXE); diff --git a/x86/vmx.h b/x86/vmx.h index 28595d8..742c2b2 100644 --- a/x86/vmx.h +++ b/x86/vmx.h @@ -432,6 +432,59 @@ enum Ctrl1 { #define HYPERCALL_MASK 0xFFF #define HYPERCALL_VMEXIT 0x1 +#define EPTP_PG_WALK_LEN_SHIFT 3ul +#define EPTP_AD_FLAG (1ul << 6) + +#define EPT_MEM_TYPE_UC 0ul +#define EPT_MEM_TYPE_WC 1ul +#define EPT_MEM_TYPE_WT 4ul +#define EPT_MEM_TYPE_WP 5ul +#define EPT_MEM_TYPE_WB 6ul + +#define EPT_RA 1ul +#define EPT_WA 2ul +#define EPT_EA 4ul +#define EPT_PRESENT (EPT_RA | EPT_WA | EPT_EA) +#define EPT_ACCESS_FLAG (1ul << 8) +#define EPT_DIRTY_FLAG (1ul << 9) +#define EPT_LARGE_PAGE (1ul << 7) +#define EPT_MEM_TYPE_SHIFT 3ul +#define EPT_IGNORE_PAT (1ul << 6) +#define EPT_SUPPRESS_VE (1ull << 63) + +#define EPT_CAP_WT 1ull +#define EPT_CAP_PWL4 (1ull << 6) +#define EPT_CAP_UC (1ull << 8) +#define EPT_CAP_WB (1ull << 14) +#define EPT_CAP_2M_PAGE (1ull << 16) +#define EPT_CAP_1G_PAGE (1ull << 17) +#define EPT_CAP_INVEPT (1ull << 20) +#define EPT_CAP_INVEPT_SINGLE (1ull << 25) +#define EPT_CAP_INVEPT_ALL (1ull << 26) +#define EPT_CAP_AD_FLAG (1ull << 21) + +#define PAGE_SIZE_2M (512 * PAGE_SIZE) +#define PAGE_SIZE_1G (512 * PAGE_SIZE_2M) +#define EPT_PAGE_LEVEL 4 +#define EPT_PGDIR_WIDTH 9 +#define EPT_PGDIR_MASK 511 +#define PAGE_MASK (~(PAGE_SIZE-1)) + +#define EPT_VLT_RD 1 +#define EPT_VLT_WR (1 << 1) +#define EPT_VLT_FETCH (1 << 2) +#define EPT_VLT_PERM_RD (1 << 3) +#define EPT_VLT_PERM_WR (1 << 4) +#define EPT_VLT_PERM_EX (1 << 5) +#define EPT_VLT_LADDR_VLD (1 << 7) +#define EPT_VLT_PADDR (1 << 8) + +#define MAGIC_VAL_1 0x12345678ul +#define MAGIC_VAL_2 0x87654321ul +#define MAGIC_VAL_3 0xfffffffful + +#define INVEPT_SINGLE 1 +#define INVEPT_GLOBAL 2 extern struct regs regs; @@ -472,8 +525,31 @@ static inline int vmcs_save(struct vmcs **vmcs) return ret; } +static inline void invept(unsigned long type, u64 eptp) +{ + struct { + u64 eptp, gpa; + } operand = {eptp, 0}; + asm volatile("invept %0, %1\n" ::"m"(operand),"r"(type)); +} + void report(const char *name, int result); void print_vmexit_info(); +void install_ept_entry(unsigned long *pml4, int pte_level, + unsigned long guest_addr, unsigned long pte, + unsigned long *pt_page); +void install_1g_ept(unsigned long *pml4, unsigned long phys, + unsigned long guest_addr, u64 perm); +void install_2m_ept(unsigned long *pml4, unsigned long phys, + unsigned long guest_addr, u64 perm); +void install_ept(unsigned long *pml4, unsigned long phys, + unsigned long guest_addr, u64 perm); +int setup_ept_range(unsigned long *pml4, unsigned long start, + unsigned long len, int map_1g, int map_2m, u64 perm); +unsigned long get_ept_pte(unsigned long *pml4, + unsigned long guest_addr, int level); +int set_ept_pte(unsigned long *pml4, unsigned long guest_addr, + int level, u64 pte_val); #endif -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH 1/2] kvm-unit-tests: VMX: The framework of EPT for nested VMX testing 2013-09-09 4:57 ` [PATCH 1/2] kvm-unit-tests: VMX: The framework of EPT for nested VMX testing Arthur Chunqi Li @ 2013-09-09 13:45 ` Paolo Bonzini 0 siblings, 0 replies; 11+ messages in thread From: Paolo Bonzini @ 2013-09-09 13:45 UTC (permalink / raw) To: Arthur Chunqi Li; +Cc: kvm, jan.kiszka, gleb Il 09/09/2013 06:57, Arthur Chunqi Li ha scritto: > The framework of EPT for nested VMX, including functions to build up > EPT paging structures, read/set EPT PTEs and setup a range of 1:1 map > EPT. > > Signed-off-by: Arthur Chunqi Li <yzt356@gmail.com> > --- > x86/vmx.c | 159 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- > x86/vmx.h | 76 +++++++++++++++++++++++++++++ > 2 files changed, 231 insertions(+), 4 deletions(-) > > @@ -336,10 +489,8 @@ static void init_vmx(void) > : MSR_IA32_VMX_ENTRY_CTLS); > ctrl_cpu_rev[0].val = rdmsr(basic.ctrl ? MSR_IA32_VMX_TRUE_PROC > : MSR_IA32_VMX_PROCBASED_CTLS); > - if (ctrl_cpu_rev[0].set & CPU_SECONDARY) > - ctrl_cpu_rev[1].val = rdmsr(MSR_IA32_VMX_PROCBASED_CTLS2); > - if (ctrl_cpu_rev[1].set & CPU_EPT || ctrl_cpu_rev[1].set & CPU_VPID) > - ept_vpid.val = rdmsr(MSR_IA32_VMX_EPT_VPID_CAP); > + ctrl_cpu_rev[1].val = rdmsr(MSR_IA32_VMX_PROCBASED_CTLS2); > + ept_vpid.val = rdmsr(MSR_IA32_VMX_EPT_VPID_CAP); This is because these MSRs are confusing. Your definitions are: union vmx_ctrl_cpu { u64 val; struct { u32 set, clr; }; }; so "set" are the low 32-bit and "clr" are the high 32-bits. This is how the SDM's description should be read: set clr 0 0 if bit is 0, ok. if bit is 1, fail => reserved, must be 0 0 1 if bit is 0, ok. if bit is 1, ok => supported by processor 1 0 if bit is 0, fail. if bit is 1, fail => impossible 1 1 if bit is 0, fail. if bit is 1, ok => reserved, must be 1 So the right fix is: if ((ctrl_cpu_rev[0].clr & CPU_SECONDARY) != 0) ctrl_cpu_rev[1].val = rdmsr(MSR_IA32_VMX_PROCBASED_CTLS2); if ((ctrl_cpu_rev[1].clr & (CPU_EPT | CPU_VPID)) != 0) ept_vpid.val = rdmsr(MSR_IA32_VMX_EPT_VPID_CAP); While looking at this I found another related bug. This line: ctrl_cpu[1] |= ctrl_cpu_rev[1].set & ctrl_cpu_rev[1].clr; should be ctrl_cpu[1] = (ctrl_cpu[1] | ctrl_cpu_rev[1].set) & ctrl_cpu_rev[1].clr; which is the same as other lines using the MSRs. Paolo > write_cr0((read_cr0() & fix_cr0_clr) | fix_cr0_set); > write_cr4((read_cr4() & fix_cr4_clr) | fix_cr4_set | X86_CR4_VMXE); > diff --git a/x86/vmx.h b/x86/vmx.h > index 28595d8..742c2b2 100644 > --- a/x86/vmx.h > +++ b/x86/vmx.h > @@ -432,6 +432,59 @@ enum Ctrl1 { > #define HYPERCALL_MASK 0xFFF > #define HYPERCALL_VMEXIT 0x1 > > +#define EPTP_PG_WALK_LEN_SHIFT 3ul > +#define EPTP_AD_FLAG (1ul << 6) > + > +#define EPT_MEM_TYPE_UC 0ul > +#define EPT_MEM_TYPE_WC 1ul > +#define EPT_MEM_TYPE_WT 4ul > +#define EPT_MEM_TYPE_WP 5ul > +#define EPT_MEM_TYPE_WB 6ul > + > +#define EPT_RA 1ul > +#define EPT_WA 2ul > +#define EPT_EA 4ul > +#define EPT_PRESENT (EPT_RA | EPT_WA | EPT_EA) > +#define EPT_ACCESS_FLAG (1ul << 8) > +#define EPT_DIRTY_FLAG (1ul << 9) > +#define EPT_LARGE_PAGE (1ul << 7) > +#define EPT_MEM_TYPE_SHIFT 3ul > +#define EPT_IGNORE_PAT (1ul << 6) > +#define EPT_SUPPRESS_VE (1ull << 63) > + > +#define EPT_CAP_WT 1ull > +#define EPT_CAP_PWL4 (1ull << 6) > +#define EPT_CAP_UC (1ull << 8) > +#define EPT_CAP_WB (1ull << 14) > +#define EPT_CAP_2M_PAGE (1ull << 16) > +#define EPT_CAP_1G_PAGE (1ull << 17) > +#define EPT_CAP_INVEPT (1ull << 20) > +#define EPT_CAP_INVEPT_SINGLE (1ull << 25) > +#define EPT_CAP_INVEPT_ALL (1ull << 26) > +#define EPT_CAP_AD_FLAG (1ull << 21) > + > +#define PAGE_SIZE_2M (512 * PAGE_SIZE) > +#define PAGE_SIZE_1G (512 * PAGE_SIZE_2M) > +#define EPT_PAGE_LEVEL 4 > +#define EPT_PGDIR_WIDTH 9 > +#define EPT_PGDIR_MASK 511 > +#define PAGE_MASK (~(PAGE_SIZE-1)) > + > +#define EPT_VLT_RD 1 > +#define EPT_VLT_WR (1 << 1) > +#define EPT_VLT_FETCH (1 << 2) > +#define EPT_VLT_PERM_RD (1 << 3) > +#define EPT_VLT_PERM_WR (1 << 4) > +#define EPT_VLT_PERM_EX (1 << 5) > +#define EPT_VLT_LADDR_VLD (1 << 7) > +#define EPT_VLT_PADDR (1 << 8) > + > +#define MAGIC_VAL_1 0x12345678ul > +#define MAGIC_VAL_2 0x87654321ul > +#define MAGIC_VAL_3 0xfffffffful > + > +#define INVEPT_SINGLE 1 > +#define INVEPT_GLOBAL 2 > > extern struct regs regs; > > @@ -472,8 +525,31 @@ static inline int vmcs_save(struct vmcs **vmcs) > return ret; > } > > +static inline void invept(unsigned long type, u64 eptp) > +{ > + struct { > + u64 eptp, gpa; > + } operand = {eptp, 0}; > + asm volatile("invept %0, %1\n" ::"m"(operand),"r"(type)); > +} > + > void report(const char *name, int result); > void print_vmexit_info(); > +void install_ept_entry(unsigned long *pml4, int pte_level, > + unsigned long guest_addr, unsigned long pte, > + unsigned long *pt_page); > +void install_1g_ept(unsigned long *pml4, unsigned long phys, > + unsigned long guest_addr, u64 perm); > +void install_2m_ept(unsigned long *pml4, unsigned long phys, > + unsigned long guest_addr, u64 perm); > +void install_ept(unsigned long *pml4, unsigned long phys, > + unsigned long guest_addr, u64 perm); > +int setup_ept_range(unsigned long *pml4, unsigned long start, > + unsigned long len, int map_1g, int map_2m, u64 perm); > +unsigned long get_ept_pte(unsigned long *pml4, > + unsigned long guest_addr, int level); > +int set_ept_pte(unsigned long *pml4, unsigned long guest_addr, > + int level, u64 pte_val); > > #endif > > ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 2/2] kvm-unit-tests: VMX: Test cases for nested EPT 2013-09-09 4:57 [PATCH 0/2] kvm-unit-tests: VMX: Test nested EPT features Arthur Chunqi Li 2013-09-09 4:57 ` [PATCH 1/2] kvm-unit-tests: VMX: The framework of EPT for nested VMX testing Arthur Chunqi Li @ 2013-09-09 4:57 ` Arthur Chunqi Li 2013-09-09 13:56 ` Paolo Bonzini 2013-09-09 15:29 ` Arthur Chunqi Li 2013-09-09 7:17 ` [PATCH 0/2] kvm-unit-tests: VMX: Test nested EPT features Jan Kiszka 2 siblings, 2 replies; 11+ messages in thread From: Arthur Chunqi Li @ 2013-09-09 4:57 UTC (permalink / raw) To: kvm; +Cc: jan.kiszka, gleb, pbonzini, Arthur Chunqi Li Some test cases for nested EPT features, including: 1. EPT basic framework tests: read, write and remap. 2. EPT misconfigurations test cases: page permission mieconfiguration and memory type misconfiguration 3. EPT violations test cases: page permission violation and paging structure violation Signed-off-by: Arthur Chunqi Li <yzt356@gmail.com> --- x86/vmx_tests.c | 266 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 266 insertions(+) diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c index c1b39f4..a0b9824 100644 --- a/x86/vmx_tests.c +++ b/x86/vmx_tests.c @@ -1,4 +1,36 @@ #include "vmx.h" +#include "processor.h" +#include "vm.h" +#include "msr.h" +#include "fwcfg.h" + +volatile u32 stage; +volatile bool init_fail; +unsigned long *pml4; +u64 eptp; +void *data_page1, *data_page2; + +static inline void set_stage(u32 s) +{ + barrier(); + stage = s; + barrier(); +} + +static inline u32 get_stage() +{ + u32 s; + + barrier(); + s = stage; + barrier(); + return s; +} + +static inline void vmcall() +{ + asm volatile ("vmcall"); +} void basic_init() { @@ -76,6 +108,238 @@ int vmenter_exit_handler() return VMX_TEST_VMEXIT; } +static int setup_ept() +{ + int support_2m; + unsigned long end_of_memory; + + if (!(ept_vpid.val & EPT_CAP_UC) && + !(ept_vpid.val & EPT_CAP_WB)) { + printf("\tEPT paging-structure memory type " + "UC&WB are not supported\n"); + return 1; + } + if (ept_vpid.val & EPT_CAP_UC) + eptp = EPT_MEM_TYPE_UC; + else + eptp = EPT_MEM_TYPE_WB; + if (!(ept_vpid.val & EPT_CAP_PWL4)) { + printf("\tPWL4 is not supported\n"); + return 1; + } + eptp |= (3 << EPTP_PG_WALK_LEN_SHIFT); + pml4 = alloc_page(); + memset(pml4, 0, PAGE_SIZE); + eptp |= virt_to_phys(pml4); + vmcs_write(EPTP, eptp); + support_2m = !!(ept_vpid.val & EPT_CAP_2M_PAGE); + end_of_memory = fwcfg_get_u64(FW_CFG_RAM_SIZE); + if (end_of_memory < (1ul << 32)) + end_of_memory = (1ul << 32); + if (setup_ept_range(pml4, 0, end_of_memory, + 0, support_2m, EPT_WA | EPT_RA | EPT_EA)) { + printf("\tSet ept tables failed.\n"); + return 1; + } + return 0; +} + +static void ept_init() +{ + u32 ctrl_cpu[2]; + + init_fail = false; + ctrl_cpu[0] = vmcs_read(CPU_EXEC_CTRL0); + ctrl_cpu[1] = vmcs_read(CPU_EXEC_CTRL1); + ctrl_cpu[0] = (ctrl_cpu[0] | CPU_SECONDARY) + & ctrl_cpu_rev[0].clr; + ctrl_cpu[1] = (ctrl_cpu[1] | CPU_EPT) + & ctrl_cpu_rev[1].clr; + vmcs_write(CPU_EXEC_CTRL0, ctrl_cpu[0]); + vmcs_write(CPU_EXEC_CTRL1, ctrl_cpu[1] | CPU_EPT); + if (setup_ept()) + init_fail = true; + data_page1 = alloc_page(); + data_page2 = alloc_page(); + memset(data_page1, 0x0, PAGE_SIZE); + memset(data_page2, 0x0, PAGE_SIZE); + *((u32 *)data_page1) = MAGIC_VAL_1; + *((u32 *)data_page2) = MAGIC_VAL_2; + install_ept(pml4, (unsigned long)data_page1, (unsigned long)data_page2, + EPT_RA | EPT_WA | EPT_EA); +} + +static void ept_main() +{ + if (init_fail) + return; + if (!(ctrl_cpu_rev[0].clr & CPU_SECONDARY) + && !(ctrl_cpu_rev[1].clr & CPU_EPT)) { + printf("\tEPT is not supported"); + return; + } + set_stage(0); + if (*((u32 *)data_page2) != MAGIC_VAL_1 && + *((u32 *)data_page1) != MAGIC_VAL_1) + report("EPT basic framework - read", 0); + else { + *((u32 *)data_page2) = MAGIC_VAL_3; + vmcall(); + if (get_stage() == 1) { + if (*((u32 *)data_page1) == MAGIC_VAL_3 && + *((u32 *)data_page2) == MAGIC_VAL_2) + report("EPT basic framework", 1); + else + report("EPT basic framework - remap", 1); + } + } + // Test EPT Misconfigurations + set_stage(1); + vmcall(); + *((u32 *)data_page1) = MAGIC_VAL_1; + if (get_stage() != 2) { + report("EPT misconfigurations", 0); + goto t1; + } + set_stage(2); + vmcall(); + *((u32 *)data_page1) = MAGIC_VAL_1; + if (get_stage() != 3) { + report("EPT misconfigurations", 0); + goto t1; + } + report("EPT misconfigurations", 1); +t1: + // Test EPT violation + set_stage(3); + vmcall(); + *((u32 *)data_page1) = MAGIC_VAL_1; + if (get_stage() == 4) + report("EPT violation - page permission", 1); + else + report("EPT violation - page permission", 0); + // Violation caused by EPT paging structure + set_stage(4); + vmcall(); + *((u32 *)data_page1) = MAGIC_VAL_2; + if (get_stage() == 5) + report("EPT violation - paging structure", 1); + else + report("EPT violation - paging structure", 0); + return; +} + +static int ept_exit_handler() +{ + u64 guest_rip; + ulong reason; + u32 insn_len; + u32 exit_qual; + static unsigned long data_page1_pte, data_page1_pte_pte; + + guest_rip = vmcs_read(GUEST_RIP); + reason = vmcs_read(EXI_REASON) & 0xff; + insn_len = vmcs_read(EXI_INST_LEN); + exit_qual = vmcs_read(EXI_QUALIFICATION); + switch (reason) { + case VMX_VMCALL: + switch (get_stage()) { + case 0: + if (*((u32 *)data_page1) == MAGIC_VAL_3 && + *((u32 *)data_page2) == MAGIC_VAL_2) { + set_stage(get_stage() + 1); + install_ept(pml4, (unsigned long)data_page2, + (unsigned long)data_page2, + EPT_RA | EPT_WA | EPT_EA); + } else + report("EPT basic framework - write\n", 0); + break; + case 1: + install_ept(pml4, (unsigned long)data_page1, + (unsigned long)data_page1, EPT_WA); + invept(INVEPT_SINGLE, eptp); + break; + case 2: + install_ept(pml4, (unsigned long)data_page1, + (unsigned long)data_page1, + EPT_RA | EPT_WA | EPT_EA | + (2 << EPT_MEM_TYPE_SHIFT)); + invept(INVEPT_SINGLE, eptp); + break; + case 3: + data_page1_pte = get_ept_pte(pml4, + (unsigned long)data_page1, 1); + set_ept_pte(pml4, (unsigned long)data_page1, + 1, data_page1_pte & (~EPT_PRESENT)); + invept(INVEPT_SINGLE, eptp); + break; + case 4: + data_page1_pte = get_ept_pte(pml4, + (unsigned long)data_page1, 2); + data_page1_pte &= PAGE_MASK; + data_page1_pte_pte = get_ept_pte(pml4, data_page1_pte, 2); + set_ept_pte(pml4, data_page1_pte, 2, + data_page1_pte_pte & (~EPT_PRESENT)); + invept(INVEPT_SINGLE, eptp); + break; + // Should not reach here + default: + printf("ERROR - unknown stage, %d.\n", get_stage()); + print_vmexit_info(); + return VMX_TEST_VMEXIT; + } + vmcs_write(GUEST_RIP, guest_rip + insn_len); + return VMX_TEST_RESUME; + case VMX_EPT_MISCONFIG: + switch (get_stage()) { + case 1: + case 2: + set_stage(get_stage() + 1); + install_ept(pml4, (unsigned long)data_page1, + (unsigned long)data_page1, + EPT_RA | EPT_WA | EPT_EA); + invept(INVEPT_SINGLE, eptp); + break; + // Should not reach here + default: + printf("ERROR - unknown stage, %d.\n", get_stage()); + print_vmexit_info(); + return VMX_TEST_VMEXIT; + } + vmcs_write(GUEST_RIP, guest_rip + insn_len); + return VMX_TEST_RESUME; + case VMX_EPT_VIOLATION: + switch(get_stage()) { + case 3: + if (exit_qual == (EPT_VLT_WR | EPT_VLT_LADDR_VLD | + EPT_VLT_PADDR)) + set_stage(get_stage() + 1); + set_ept_pte(pml4, (unsigned long)data_page1, + 1, data_page1_pte | (EPT_PRESENT)); + invept(INVEPT_SINGLE, eptp); + break; + case 4: + if (exit_qual == (EPT_VLT_RD | EPT_VLT_LADDR_VLD)) + set_stage(get_stage() + 1); + set_ept_pte(pml4, data_page1_pte, 2, + data_page1_pte_pte | (EPT_PRESENT)); + invept(INVEPT_SINGLE, eptp); + break; + default: + // Should not reach here + printf("ERROR : unknown stage, %d\n", get_stage()); + print_vmexit_info(); + return VMX_TEST_VMEXIT; + } + vmcs_write(GUEST_RIP, guest_rip + insn_len); + return VMX_TEST_RESUME; + default: + printf("Unknown exit reason, %d\n", reason); + print_vmexit_info(); + } + return VMX_TEST_VMEXIT; +} + /* name/init/guest_main/exit_handler/syscall_handler/guest_regs basic_* just implement some basic functions */ struct vmx_test vmx_tests[] = { @@ -83,5 +347,7 @@ struct vmx_test vmx_tests[] = { basic_syscall_handler, {0} }, { "vmenter", basic_init, vmenter_main, vmenter_exit_handler, basic_syscall_handler, {0} }, + { "EPT framework", ept_init, ept_main, ept_exit_handler, + basic_syscall_handler, {0} }, { NULL, NULL, NULL, NULL, NULL, {0} }, }; -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH 2/2] kvm-unit-tests: VMX: Test cases for nested EPT 2013-09-09 4:57 ` [PATCH 2/2] kvm-unit-tests: VMX: Test cases for nested EPT Arthur Chunqi Li @ 2013-09-09 13:56 ` Paolo Bonzini 2013-09-09 14:11 ` Arthur Chunqi Li 2013-09-09 15:29 ` Arthur Chunqi Li 1 sibling, 1 reply; 11+ messages in thread From: Paolo Bonzini @ 2013-09-09 13:56 UTC (permalink / raw) To: Arthur Chunqi Li; +Cc: kvm, jan.kiszka, gleb Il 09/09/2013 06:57, Arthur Chunqi Li ha scritto: > Some test cases for nested EPT features, including: > 1. EPT basic framework tests: read, write and remap. > 2. EPT misconfigurations test cases: page permission mieconfiguration > and memory type misconfiguration > 3. EPT violations test cases: page permission violation and paging > structure violation > > Signed-off-by: Arthur Chunqi Li <yzt356@gmail.com> > --- > x86/vmx_tests.c | 266 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 266 insertions(+) > > diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c > index c1b39f4..a0b9824 100644 > --- a/x86/vmx_tests.c > +++ b/x86/vmx_tests.c > @@ -1,4 +1,36 @@ > #include "vmx.h" > +#include "processor.h" > +#include "vm.h" > +#include "msr.h" > +#include "fwcfg.h" > + > +volatile u32 stage; > +volatile bool init_fail; Why volatile? The patch looks good. > +unsigned long *pml4; > +u64 eptp; > +void *data_page1, *data_page2; > + > +static inline void set_stage(u32 s) > +{ > + barrier(); > + stage = s; > + barrier(); > +} > + > +static inline u32 get_stage() > +{ > + u32 s; > + > + barrier(); > + s = stage; > + barrier(); > + return s; > +} > + > +static inline void vmcall() > +{ > + asm volatile ("vmcall"); > +} > > void basic_init() > { > @@ -76,6 +108,238 @@ int vmenter_exit_handler() > return VMX_TEST_VMEXIT; > } > > +static int setup_ept() > +{ > + int support_2m; > + unsigned long end_of_memory; > + > + if (!(ept_vpid.val & EPT_CAP_UC) && > + !(ept_vpid.val & EPT_CAP_WB)) { > + printf("\tEPT paging-structure memory type " > + "UC&WB are not supported\n"); > + return 1; > + } > + if (ept_vpid.val & EPT_CAP_UC) > + eptp = EPT_MEM_TYPE_UC; > + else > + eptp = EPT_MEM_TYPE_WB; > + if (!(ept_vpid.val & EPT_CAP_PWL4)) { > + printf("\tPWL4 is not supported\n"); > + return 1; > + } > + eptp |= (3 << EPTP_PG_WALK_LEN_SHIFT); > + pml4 = alloc_page(); > + memset(pml4, 0, PAGE_SIZE); > + eptp |= virt_to_phys(pml4); > + vmcs_write(EPTP, eptp); > + support_2m = !!(ept_vpid.val & EPT_CAP_2M_PAGE); > + end_of_memory = fwcfg_get_u64(FW_CFG_RAM_SIZE); > + if (end_of_memory < (1ul << 32)) > + end_of_memory = (1ul << 32); > + if (setup_ept_range(pml4, 0, end_of_memory, > + 0, support_2m, EPT_WA | EPT_RA | EPT_EA)) { > + printf("\tSet ept tables failed.\n"); > + return 1; > + } > + return 0; > +} > + > +static void ept_init() > +{ > + u32 ctrl_cpu[2]; > + > + init_fail = false; > + ctrl_cpu[0] = vmcs_read(CPU_EXEC_CTRL0); > + ctrl_cpu[1] = vmcs_read(CPU_EXEC_CTRL1); > + ctrl_cpu[0] = (ctrl_cpu[0] | CPU_SECONDARY) > + & ctrl_cpu_rev[0].clr; > + ctrl_cpu[1] = (ctrl_cpu[1] | CPU_EPT) > + & ctrl_cpu_rev[1].clr; > + vmcs_write(CPU_EXEC_CTRL0, ctrl_cpu[0]); > + vmcs_write(CPU_EXEC_CTRL1, ctrl_cpu[1] | CPU_EPT); > + if (setup_ept()) > + init_fail = true; > + data_page1 = alloc_page(); > + data_page2 = alloc_page(); > + memset(data_page1, 0x0, PAGE_SIZE); > + memset(data_page2, 0x0, PAGE_SIZE); > + *((u32 *)data_page1) = MAGIC_VAL_1; > + *((u32 *)data_page2) = MAGIC_VAL_2; > + install_ept(pml4, (unsigned long)data_page1, (unsigned long)data_page2, > + EPT_RA | EPT_WA | EPT_EA); > +} > + > +static void ept_main() > +{ > + if (init_fail) > + return; > + if (!(ctrl_cpu_rev[0].clr & CPU_SECONDARY) > + && !(ctrl_cpu_rev[1].clr & CPU_EPT)) { > + printf("\tEPT is not supported"); > + return; > + } > + set_stage(0); > + if (*((u32 *)data_page2) != MAGIC_VAL_1 && > + *((u32 *)data_page1) != MAGIC_VAL_1) > + report("EPT basic framework - read", 0); > + else { > + *((u32 *)data_page2) = MAGIC_VAL_3; > + vmcall(); > + if (get_stage() == 1) { > + if (*((u32 *)data_page1) == MAGIC_VAL_3 && > + *((u32 *)data_page2) == MAGIC_VAL_2) > + report("EPT basic framework", 1); > + else > + report("EPT basic framework - remap", 1); > + } > + } > + // Test EPT Misconfigurations > + set_stage(1); > + vmcall(); > + *((u32 *)data_page1) = MAGIC_VAL_1; > + if (get_stage() != 2) { > + report("EPT misconfigurations", 0); > + goto t1; > + } > + set_stage(2); > + vmcall(); > + *((u32 *)data_page1) = MAGIC_VAL_1; > + if (get_stage() != 3) { > + report("EPT misconfigurations", 0); > + goto t1; > + } > + report("EPT misconfigurations", 1); > +t1: > + // Test EPT violation > + set_stage(3); > + vmcall(); > + *((u32 *)data_page1) = MAGIC_VAL_1; > + if (get_stage() == 4) > + report("EPT violation - page permission", 1); > + else > + report("EPT violation - page permission", 0); > + // Violation caused by EPT paging structure > + set_stage(4); > + vmcall(); > + *((u32 *)data_page1) = MAGIC_VAL_2; > + if (get_stage() == 5) > + report("EPT violation - paging structure", 1); > + else > + report("EPT violation - paging structure", 0); > + return; > +} > + > +static int ept_exit_handler() > +{ > + u64 guest_rip; > + ulong reason; > + u32 insn_len; > + u32 exit_qual; > + static unsigned long data_page1_pte, data_page1_pte_pte; > + > + guest_rip = vmcs_read(GUEST_RIP); > + reason = vmcs_read(EXI_REASON) & 0xff; > + insn_len = vmcs_read(EXI_INST_LEN); > + exit_qual = vmcs_read(EXI_QUALIFICATION); > + switch (reason) { > + case VMX_VMCALL: > + switch (get_stage()) { > + case 0: > + if (*((u32 *)data_page1) == MAGIC_VAL_3 && > + *((u32 *)data_page2) == MAGIC_VAL_2) { > + set_stage(get_stage() + 1); > + install_ept(pml4, (unsigned long)data_page2, > + (unsigned long)data_page2, > + EPT_RA | EPT_WA | EPT_EA); > + } else > + report("EPT basic framework - write\n", 0); > + break; > + case 1: > + install_ept(pml4, (unsigned long)data_page1, > + (unsigned long)data_page1, EPT_WA); > + invept(INVEPT_SINGLE, eptp); > + break; > + case 2: > + install_ept(pml4, (unsigned long)data_page1, > + (unsigned long)data_page1, > + EPT_RA | EPT_WA | EPT_EA | > + (2 << EPT_MEM_TYPE_SHIFT)); > + invept(INVEPT_SINGLE, eptp); > + break; > + case 3: > + data_page1_pte = get_ept_pte(pml4, > + (unsigned long)data_page1, 1); > + set_ept_pte(pml4, (unsigned long)data_page1, > + 1, data_page1_pte & (~EPT_PRESENT)); > + invept(INVEPT_SINGLE, eptp); > + break; > + case 4: > + data_page1_pte = get_ept_pte(pml4, > + (unsigned long)data_page1, 2); > + data_page1_pte &= PAGE_MASK; > + data_page1_pte_pte = get_ept_pte(pml4, data_page1_pte, 2); > + set_ept_pte(pml4, data_page1_pte, 2, > + data_page1_pte_pte & (~EPT_PRESENT)); > + invept(INVEPT_SINGLE, eptp); > + break; > + // Should not reach here > + default: > + printf("ERROR - unknown stage, %d.\n", get_stage()); > + print_vmexit_info(); > + return VMX_TEST_VMEXIT; > + } > + vmcs_write(GUEST_RIP, guest_rip + insn_len); > + return VMX_TEST_RESUME; > + case VMX_EPT_MISCONFIG: > + switch (get_stage()) { > + case 1: > + case 2: > + set_stage(get_stage() + 1); > + install_ept(pml4, (unsigned long)data_page1, > + (unsigned long)data_page1, > + EPT_RA | EPT_WA | EPT_EA); > + invept(INVEPT_SINGLE, eptp); > + break; > + // Should not reach here > + default: > + printf("ERROR - unknown stage, %d.\n", get_stage()); > + print_vmexit_info(); > + return VMX_TEST_VMEXIT; > + } > + vmcs_write(GUEST_RIP, guest_rip + insn_len); > + return VMX_TEST_RESUME; > + case VMX_EPT_VIOLATION: > + switch(get_stage()) { > + case 3: > + if (exit_qual == (EPT_VLT_WR | EPT_VLT_LADDR_VLD | > + EPT_VLT_PADDR)) > + set_stage(get_stage() + 1); > + set_ept_pte(pml4, (unsigned long)data_page1, > + 1, data_page1_pte | (EPT_PRESENT)); > + invept(INVEPT_SINGLE, eptp); > + break; > + case 4: > + if (exit_qual == (EPT_VLT_RD | EPT_VLT_LADDR_VLD)) > + set_stage(get_stage() + 1); > + set_ept_pte(pml4, data_page1_pte, 2, > + data_page1_pte_pte | (EPT_PRESENT)); > + invept(INVEPT_SINGLE, eptp); > + break; > + default: > + // Should not reach here > + printf("ERROR : unknown stage, %d\n", get_stage()); > + print_vmexit_info(); > + return VMX_TEST_VMEXIT; > + } > + vmcs_write(GUEST_RIP, guest_rip + insn_len); > + return VMX_TEST_RESUME; > + default: > + printf("Unknown exit reason, %d\n", reason); > + print_vmexit_info(); > + } > + return VMX_TEST_VMEXIT; > +} > + > /* name/init/guest_main/exit_handler/syscall_handler/guest_regs > basic_* just implement some basic functions */ > struct vmx_test vmx_tests[] = { > @@ -83,5 +347,7 @@ struct vmx_test vmx_tests[] = { > basic_syscall_handler, {0} }, > { "vmenter", basic_init, vmenter_main, vmenter_exit_handler, > basic_syscall_handler, {0} }, > + { "EPT framework", ept_init, ept_main, ept_exit_handler, > + basic_syscall_handler, {0} }, > { NULL, NULL, NULL, NULL, NULL, {0} }, > }; > ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 2/2] kvm-unit-tests: VMX: Test cases for nested EPT 2013-09-09 13:56 ` Paolo Bonzini @ 2013-09-09 14:11 ` Arthur Chunqi Li 2013-09-09 14:26 ` Paolo Bonzini 0 siblings, 1 reply; 11+ messages in thread From: Arthur Chunqi Li @ 2013-09-09 14:11 UTC (permalink / raw) To: Paolo Bonzini; +Cc: kvm, Jan Kiszka, Gleb Natapov On Mon, Sep 9, 2013 at 9:56 PM, Paolo Bonzini <pbonzini@redhat.com> wrote: > Il 09/09/2013 06:57, Arthur Chunqi Li ha scritto: >> Some test cases for nested EPT features, including: >> 1. EPT basic framework tests: read, write and remap. >> 2. EPT misconfigurations test cases: page permission mieconfiguration >> and memory type misconfiguration >> 3. EPT violations test cases: page permission violation and paging >> structure violation >> >> Signed-off-by: Arthur Chunqi Li <yzt356@gmail.com> >> --- >> x86/vmx_tests.c | 266 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ >> 1 file changed, 266 insertions(+) >> >> diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c >> index c1b39f4..a0b9824 100644 >> --- a/x86/vmx_tests.c >> +++ b/x86/vmx_tests.c >> @@ -1,4 +1,36 @@ >> #include "vmx.h" >> +#include "processor.h" >> +#include "vm.h" >> +#include "msr.h" >> +#include "fwcfg.h" >> + >> +volatile u32 stage; >> +volatile bool init_fail; > > Why volatile? Because init_fail is only set but not used later in ept_init(), and if I don't add volatile, compiler may optimize the setting to init_fail. This occasion firstly occurred when I write set_stage/get_stage. If one variant is set in a function but not used later, the compiler usually optimizes this setting as redundant assignment and remove it. Arthur > > The patch looks good. > >> +unsigned long *pml4; >> +u64 eptp; >> +void *data_page1, *data_page2; >> + >> +static inline void set_stage(u32 s) >> +{ >> + barrier(); >> + stage = s; >> + barrier(); >> +} >> + >> +static inline u32 get_stage() >> +{ >> + u32 s; >> + >> + barrier(); >> + s = stage; >> + barrier(); >> + return s; >> +} >> + >> +static inline void vmcall() >> +{ >> + asm volatile ("vmcall"); >> +} >> >> void basic_init() >> { >> @@ -76,6 +108,238 @@ int vmenter_exit_handler() >> return VMX_TEST_VMEXIT; >> } >> >> +static int setup_ept() >> +{ >> + int support_2m; >> + unsigned long end_of_memory; >> + >> + if (!(ept_vpid.val & EPT_CAP_UC) && >> + !(ept_vpid.val & EPT_CAP_WB)) { >> + printf("\tEPT paging-structure memory type " >> + "UC&WB are not supported\n"); >> + return 1; >> + } >> + if (ept_vpid.val & EPT_CAP_UC) >> + eptp = EPT_MEM_TYPE_UC; >> + else >> + eptp = EPT_MEM_TYPE_WB; >> + if (!(ept_vpid.val & EPT_CAP_PWL4)) { >> + printf("\tPWL4 is not supported\n"); >> + return 1; >> + } >> + eptp |= (3 << EPTP_PG_WALK_LEN_SHIFT); >> + pml4 = alloc_page(); >> + memset(pml4, 0, PAGE_SIZE); >> + eptp |= virt_to_phys(pml4); >> + vmcs_write(EPTP, eptp); >> + support_2m = !!(ept_vpid.val & EPT_CAP_2M_PAGE); >> + end_of_memory = fwcfg_get_u64(FW_CFG_RAM_SIZE); >> + if (end_of_memory < (1ul << 32)) >> + end_of_memory = (1ul << 32); >> + if (setup_ept_range(pml4, 0, end_of_memory, >> + 0, support_2m, EPT_WA | EPT_RA | EPT_EA)) { >> + printf("\tSet ept tables failed.\n"); >> + return 1; >> + } >> + return 0; >> +} >> + >> +static void ept_init() >> +{ >> + u32 ctrl_cpu[2]; >> + >> + init_fail = false; >> + ctrl_cpu[0] = vmcs_read(CPU_EXEC_CTRL0); >> + ctrl_cpu[1] = vmcs_read(CPU_EXEC_CTRL1); >> + ctrl_cpu[0] = (ctrl_cpu[0] | CPU_SECONDARY) >> + & ctrl_cpu_rev[0].clr; >> + ctrl_cpu[1] = (ctrl_cpu[1] | CPU_EPT) >> + & ctrl_cpu_rev[1].clr; >> + vmcs_write(CPU_EXEC_CTRL0, ctrl_cpu[0]); >> + vmcs_write(CPU_EXEC_CTRL1, ctrl_cpu[1] | CPU_EPT); >> + if (setup_ept()) >> + init_fail = true; >> + data_page1 = alloc_page(); >> + data_page2 = alloc_page(); >> + memset(data_page1, 0x0, PAGE_SIZE); >> + memset(data_page2, 0x0, PAGE_SIZE); >> + *((u32 *)data_page1) = MAGIC_VAL_1; >> + *((u32 *)data_page2) = MAGIC_VAL_2; >> + install_ept(pml4, (unsigned long)data_page1, (unsigned long)data_page2, >> + EPT_RA | EPT_WA | EPT_EA); >> +} >> + >> +static void ept_main() >> +{ >> + if (init_fail) >> + return; >> + if (!(ctrl_cpu_rev[0].clr & CPU_SECONDARY) >> + && !(ctrl_cpu_rev[1].clr & CPU_EPT)) { >> + printf("\tEPT is not supported"); >> + return; >> + } >> + set_stage(0); >> + if (*((u32 *)data_page2) != MAGIC_VAL_1 && >> + *((u32 *)data_page1) != MAGIC_VAL_1) >> + report("EPT basic framework - read", 0); >> + else { >> + *((u32 *)data_page2) = MAGIC_VAL_3; >> + vmcall(); >> + if (get_stage() == 1) { >> + if (*((u32 *)data_page1) == MAGIC_VAL_3 && >> + *((u32 *)data_page2) == MAGIC_VAL_2) >> + report("EPT basic framework", 1); >> + else >> + report("EPT basic framework - remap", 1); >> + } >> + } >> + // Test EPT Misconfigurations >> + set_stage(1); >> + vmcall(); >> + *((u32 *)data_page1) = MAGIC_VAL_1; >> + if (get_stage() != 2) { >> + report("EPT misconfigurations", 0); >> + goto t1; >> + } >> + set_stage(2); >> + vmcall(); >> + *((u32 *)data_page1) = MAGIC_VAL_1; >> + if (get_stage() != 3) { >> + report("EPT misconfigurations", 0); >> + goto t1; >> + } >> + report("EPT misconfigurations", 1); >> +t1: >> + // Test EPT violation >> + set_stage(3); >> + vmcall(); >> + *((u32 *)data_page1) = MAGIC_VAL_1; >> + if (get_stage() == 4) >> + report("EPT violation - page permission", 1); >> + else >> + report("EPT violation - page permission", 0); >> + // Violation caused by EPT paging structure >> + set_stage(4); >> + vmcall(); >> + *((u32 *)data_page1) = MAGIC_VAL_2; >> + if (get_stage() == 5) >> + report("EPT violation - paging structure", 1); >> + else >> + report("EPT violation - paging structure", 0); >> + return; >> +} >> + >> +static int ept_exit_handler() >> +{ >> + u64 guest_rip; >> + ulong reason; >> + u32 insn_len; >> + u32 exit_qual; >> + static unsigned long data_page1_pte, data_page1_pte_pte; >> + >> + guest_rip = vmcs_read(GUEST_RIP); >> + reason = vmcs_read(EXI_REASON) & 0xff; >> + insn_len = vmcs_read(EXI_INST_LEN); >> + exit_qual = vmcs_read(EXI_QUALIFICATION); >> + switch (reason) { >> + case VMX_VMCALL: >> + switch (get_stage()) { >> + case 0: >> + if (*((u32 *)data_page1) == MAGIC_VAL_3 && >> + *((u32 *)data_page2) == MAGIC_VAL_2) { >> + set_stage(get_stage() + 1); >> + install_ept(pml4, (unsigned long)data_page2, >> + (unsigned long)data_page2, >> + EPT_RA | EPT_WA | EPT_EA); >> + } else >> + report("EPT basic framework - write\n", 0); >> + break; >> + case 1: >> + install_ept(pml4, (unsigned long)data_page1, >> + (unsigned long)data_page1, EPT_WA); >> + invept(INVEPT_SINGLE, eptp); >> + break; >> + case 2: >> + install_ept(pml4, (unsigned long)data_page1, >> + (unsigned long)data_page1, >> + EPT_RA | EPT_WA | EPT_EA | >> + (2 << EPT_MEM_TYPE_SHIFT)); >> + invept(INVEPT_SINGLE, eptp); >> + break; >> + case 3: >> + data_page1_pte = get_ept_pte(pml4, >> + (unsigned long)data_page1, 1); >> + set_ept_pte(pml4, (unsigned long)data_page1, >> + 1, data_page1_pte & (~EPT_PRESENT)); >> + invept(INVEPT_SINGLE, eptp); >> + break; >> + case 4: >> + data_page1_pte = get_ept_pte(pml4, >> + (unsigned long)data_page1, 2); >> + data_page1_pte &= PAGE_MASK; >> + data_page1_pte_pte = get_ept_pte(pml4, data_page1_pte, 2); >> + set_ept_pte(pml4, data_page1_pte, 2, >> + data_page1_pte_pte & (~EPT_PRESENT)); >> + invept(INVEPT_SINGLE, eptp); >> + break; >> + // Should not reach here >> + default: >> + printf("ERROR - unknown stage, %d.\n", get_stage()); >> + print_vmexit_info(); >> + return VMX_TEST_VMEXIT; >> + } >> + vmcs_write(GUEST_RIP, guest_rip + insn_len); >> + return VMX_TEST_RESUME; >> + case VMX_EPT_MISCONFIG: >> + switch (get_stage()) { >> + case 1: >> + case 2: >> + set_stage(get_stage() + 1); >> + install_ept(pml4, (unsigned long)data_page1, >> + (unsigned long)data_page1, >> + EPT_RA | EPT_WA | EPT_EA); >> + invept(INVEPT_SINGLE, eptp); >> + break; >> + // Should not reach here >> + default: >> + printf("ERROR - unknown stage, %d.\n", get_stage()); >> + print_vmexit_info(); >> + return VMX_TEST_VMEXIT; >> + } >> + vmcs_write(GUEST_RIP, guest_rip + insn_len); >> + return VMX_TEST_RESUME; >> + case VMX_EPT_VIOLATION: >> + switch(get_stage()) { >> + case 3: >> + if (exit_qual == (EPT_VLT_WR | EPT_VLT_LADDR_VLD | >> + EPT_VLT_PADDR)) >> + set_stage(get_stage() + 1); >> + set_ept_pte(pml4, (unsigned long)data_page1, >> + 1, data_page1_pte | (EPT_PRESENT)); >> + invept(INVEPT_SINGLE, eptp); >> + break; >> + case 4: >> + if (exit_qual == (EPT_VLT_RD | EPT_VLT_LADDR_VLD)) >> + set_stage(get_stage() + 1); >> + set_ept_pte(pml4, data_page1_pte, 2, >> + data_page1_pte_pte | (EPT_PRESENT)); >> + invept(INVEPT_SINGLE, eptp); >> + break; >> + default: >> + // Should not reach here >> + printf("ERROR : unknown stage, %d\n", get_stage()); >> + print_vmexit_info(); >> + return VMX_TEST_VMEXIT; >> + } >> + vmcs_write(GUEST_RIP, guest_rip + insn_len); >> + return VMX_TEST_RESUME; >> + default: >> + printf("Unknown exit reason, %d\n", reason); >> + print_vmexit_info(); >> + } >> + return VMX_TEST_VMEXIT; >> +} >> + >> /* name/init/guest_main/exit_handler/syscall_handler/guest_regs >> basic_* just implement some basic functions */ >> struct vmx_test vmx_tests[] = { >> @@ -83,5 +347,7 @@ struct vmx_test vmx_tests[] = { >> basic_syscall_handler, {0} }, >> { "vmenter", basic_init, vmenter_main, vmenter_exit_handler, >> basic_syscall_handler, {0} }, >> + { "EPT framework", ept_init, ept_main, ept_exit_handler, >> + basic_syscall_handler, {0} }, >> { NULL, NULL, NULL, NULL, NULL, {0} }, >> }; >> > ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 2/2] kvm-unit-tests: VMX: Test cases for nested EPT 2013-09-09 14:11 ` Arthur Chunqi Li @ 2013-09-09 14:26 ` Paolo Bonzini 0 siblings, 0 replies; 11+ messages in thread From: Paolo Bonzini @ 2013-09-09 14:26 UTC (permalink / raw) To: Arthur Chunqi Li; +Cc: kvm, Jan Kiszka, Gleb Natapov Il 09/09/2013 16:11, Arthur Chunqi Li ha scritto: >>> >> +volatile u32 stage; >>> >> +volatile bool init_fail; >> > >> > Why volatile? > Because init_fail is only set but not used later in ept_init(), and if > I don't add volatile, compiler may optimize the setting to init_fail. > > This occasion firstly occurred when I write set_stage/get_stage. If > one variant is set in a function but not used later, the compiler > usually optimizes this setting as redundant assignment and remove it. No, the two are different. "stage" is written several times in the same function, with no code in the middle: stage++; *p = 1; stage++; To the compiler, the first store is dead. The compiler doesn't know that "*p = 1" traps to the hypervisor. But this is not the case for "init_fail". Paolo ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 2/2] kvm-unit-tests: VMX: Test cases for nested EPT 2013-09-09 4:57 ` [PATCH 2/2] kvm-unit-tests: VMX: Test cases for nested EPT Arthur Chunqi Li 2013-09-09 13:56 ` Paolo Bonzini @ 2013-09-09 15:29 ` Arthur Chunqi Li 2013-09-09 16:23 ` Paolo Bonzini 1 sibling, 1 reply; 11+ messages in thread From: Arthur Chunqi Li @ 2013-09-09 15:29 UTC (permalink / raw) To: kvm; +Cc: Jan Kiszka, Gleb Natapov, Paolo Bonzini, Arthur Chunqi Li On Mon, Sep 9, 2013 at 12:57 PM, Arthur Chunqi Li <yzt356@gmail.com> wrote: > Some test cases for nested EPT features, including: > 1. EPT basic framework tests: read, write and remap. > 2. EPT misconfigurations test cases: page permission mieconfiguration > and memory type misconfiguration > 3. EPT violations test cases: page permission violation and paging > structure violation > > Signed-off-by: Arthur Chunqi Li <yzt356@gmail.com> > --- > x86/vmx_tests.c | 266 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 266 insertions(+) > > diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c > index c1b39f4..a0b9824 100644 > --- a/x86/vmx_tests.c > +++ b/x86/vmx_tests.c > @@ -1,4 +1,36 @@ > #include "vmx.h" > +#include "processor.h" > +#include "vm.h" > +#include "msr.h" > +#include "fwcfg.h" > + > +volatile u32 stage; > +volatile bool init_fail; > +unsigned long *pml4; > +u64 eptp; > +void *data_page1, *data_page2; > + > +static inline void set_stage(u32 s) > +{ > + barrier(); > + stage = s; > + barrier(); > +} > + > +static inline u32 get_stage() > +{ > + u32 s; > + > + barrier(); > + s = stage; > + barrier(); > + return s; > +} > + > +static inline void vmcall() > +{ > + asm volatile ("vmcall"); > +} > > void basic_init() > { > @@ -76,6 +108,238 @@ int vmenter_exit_handler() > return VMX_TEST_VMEXIT; > } > > +static int setup_ept() > +{ > + int support_2m; > + unsigned long end_of_memory; > + > + if (!(ept_vpid.val & EPT_CAP_UC) && > + !(ept_vpid.val & EPT_CAP_WB)) { > + printf("\tEPT paging-structure memory type " > + "UC&WB are not supported\n"); > + return 1; > + } > + if (ept_vpid.val & EPT_CAP_UC) > + eptp = EPT_MEM_TYPE_UC; > + else > + eptp = EPT_MEM_TYPE_WB; > + if (!(ept_vpid.val & EPT_CAP_PWL4)) { > + printf("\tPWL4 is not supported\n"); > + return 1; > + } > + eptp |= (3 << EPTP_PG_WALK_LEN_SHIFT); > + pml4 = alloc_page(); > + memset(pml4, 0, PAGE_SIZE); > + eptp |= virt_to_phys(pml4); > + vmcs_write(EPTP, eptp); > + support_2m = !!(ept_vpid.val & EPT_CAP_2M_PAGE); > + end_of_memory = fwcfg_get_u64(FW_CFG_RAM_SIZE); > + if (end_of_memory < (1ul << 32)) > + end_of_memory = (1ul << 32); > + if (setup_ept_range(pml4, 0, end_of_memory, > + 0, support_2m, EPT_WA | EPT_RA | EPT_EA)) { > + printf("\tSet ept tables failed.\n"); > + return 1; > + } > + return 0; > +} > + > +static void ept_init() > +{ > + u32 ctrl_cpu[2]; > + > + init_fail = false; > + ctrl_cpu[0] = vmcs_read(CPU_EXEC_CTRL0); > + ctrl_cpu[1] = vmcs_read(CPU_EXEC_CTRL1); > + ctrl_cpu[0] = (ctrl_cpu[0] | CPU_SECONDARY) > + & ctrl_cpu_rev[0].clr; > + ctrl_cpu[1] = (ctrl_cpu[1] | CPU_EPT) > + & ctrl_cpu_rev[1].clr; > + vmcs_write(CPU_EXEC_CTRL0, ctrl_cpu[0]); > + vmcs_write(CPU_EXEC_CTRL1, ctrl_cpu[1] | CPU_EPT); > + if (setup_ept()) > + init_fail = true; > + data_page1 = alloc_page(); > + data_page2 = alloc_page(); > + memset(data_page1, 0x0, PAGE_SIZE); > + memset(data_page2, 0x0, PAGE_SIZE); > + *((u32 *)data_page1) = MAGIC_VAL_1; > + *((u32 *)data_page2) = MAGIC_VAL_2; > + install_ept(pml4, (unsigned long)data_page1, (unsigned long)data_page2, > + EPT_RA | EPT_WA | EPT_EA); > +} > + > +static void ept_main() > +{ > + if (init_fail) > + return; > + if (!(ctrl_cpu_rev[0].clr & CPU_SECONDARY) > + && !(ctrl_cpu_rev[1].clr & CPU_EPT)) { > + printf("\tEPT is not supported"); > + return; > + } > + set_stage(0); > + if (*((u32 *)data_page2) != MAGIC_VAL_1 && > + *((u32 *)data_page1) != MAGIC_VAL_1) > + report("EPT basic framework - read", 0); > + else { > + *((u32 *)data_page2) = MAGIC_VAL_3; > + vmcall(); > + if (get_stage() == 1) { > + if (*((u32 *)data_page1) == MAGIC_VAL_3 && > + *((u32 *)data_page2) == MAGIC_VAL_2) > + report("EPT basic framework", 1); > + else > + report("EPT basic framework - remap", 1); > + } > + } > + // Test EPT Misconfigurations > + set_stage(1); > + vmcall(); > + *((u32 *)data_page1) = MAGIC_VAL_1; > + if (get_stage() != 2) { > + report("EPT misconfigurations", 0); > + goto t1; > + } > + set_stage(2); > + vmcall(); > + *((u32 *)data_page1) = MAGIC_VAL_1; > + if (get_stage() != 3) { > + report("EPT misconfigurations", 0); > + goto t1; > + } > + report("EPT misconfigurations", 1); > +t1: > + // Test EPT violation > + set_stage(3); > + vmcall(); > + *((u32 *)data_page1) = MAGIC_VAL_1; > + if (get_stage() == 4) > + report("EPT violation - page permission", 1); > + else > + report("EPT violation - page permission", 0); > + // Violation caused by EPT paging structure > + set_stage(4); > + vmcall(); > + *((u32 *)data_page1) = MAGIC_VAL_2; > + if (get_stage() == 5) > + report("EPT violation - paging structure", 1); > + else > + report("EPT violation - paging structure", 0); > + return; > +} > + > +static int ept_exit_handler() > +{ > + u64 guest_rip; > + ulong reason; > + u32 insn_len; > + u32 exit_qual; > + static unsigned long data_page1_pte, data_page1_pte_pte; > + > + guest_rip = vmcs_read(GUEST_RIP); > + reason = vmcs_read(EXI_REASON) & 0xff; > + insn_len = vmcs_read(EXI_INST_LEN); > + exit_qual = vmcs_read(EXI_QUALIFICATION); > + switch (reason) { > + case VMX_VMCALL: > + switch (get_stage()) { > + case 0: > + if (*((u32 *)data_page1) == MAGIC_VAL_3 && > + *((u32 *)data_page2) == MAGIC_VAL_2) { > + set_stage(get_stage() + 1); > + install_ept(pml4, (unsigned long)data_page2, > + (unsigned long)data_page2, > + EPT_RA | EPT_WA | EPT_EA); > + } else > + report("EPT basic framework - write\n", 0); > + break; > + case 1: > + install_ept(pml4, (unsigned long)data_page1, > + (unsigned long)data_page1, EPT_WA); > + invept(INVEPT_SINGLE, eptp); > + break; > + case 2: > + install_ept(pml4, (unsigned long)data_page1, > + (unsigned long)data_page1, > + EPT_RA | EPT_WA | EPT_EA | > + (2 << EPT_MEM_TYPE_SHIFT)); > + invept(INVEPT_SINGLE, eptp); > + break; > + case 3: > + data_page1_pte = get_ept_pte(pml4, > + (unsigned long)data_page1, 1); > + set_ept_pte(pml4, (unsigned long)data_page1, > + 1, data_page1_pte & (~EPT_PRESENT)); > + invept(INVEPT_SINGLE, eptp); > + break; > + case 4: > + data_page1_pte = get_ept_pte(pml4, > + (unsigned long)data_page1, 2); > + data_page1_pte &= PAGE_MASK; > + data_page1_pte_pte = get_ept_pte(pml4, data_page1_pte, 2); > + set_ept_pte(pml4, data_page1_pte, 2, > + data_page1_pte_pte & (~EPT_PRESENT)); > + invept(INVEPT_SINGLE, eptp); > + break; > + // Should not reach here > + default: > + printf("ERROR - unknown stage, %d.\n", get_stage()); > + print_vmexit_info(); > + return VMX_TEST_VMEXIT; > + } > + vmcs_write(GUEST_RIP, guest_rip + insn_len); > + return VMX_TEST_RESUME; > + case VMX_EPT_MISCONFIG: > + switch (get_stage()) { > + case 1: > + case 2: > + set_stage(get_stage() + 1); > + install_ept(pml4, (unsigned long)data_page1, > + (unsigned long)data_page1, > + EPT_RA | EPT_WA | EPT_EA); > + invept(INVEPT_SINGLE, eptp); > + break; > + // Should not reach here > + default: > + printf("ERROR - unknown stage, %d.\n", get_stage()); > + print_vmexit_info(); > + return VMX_TEST_VMEXIT; > + } > + vmcs_write(GUEST_RIP, guest_rip + insn_len); > + return VMX_TEST_RESUME; > + case VMX_EPT_VIOLATION: > + switch(get_stage()) { > + case 3: > + if (exit_qual == (EPT_VLT_WR | EPT_VLT_LADDR_VLD | > + EPT_VLT_PADDR)) > + set_stage(get_stage() + 1); > + set_ept_pte(pml4, (unsigned long)data_page1, > + 1, data_page1_pte | (EPT_PRESENT)); > + invept(INVEPT_SINGLE, eptp); > + break; > + case 4: > + if (exit_qual == (EPT_VLT_RD | EPT_VLT_LADDR_VLD)) > + set_stage(get_stage() + 1); > + set_ept_pte(pml4, data_page1_pte, 2, > + data_page1_pte_pte | (EPT_PRESENT)); > + invept(INVEPT_SINGLE, eptp); > + break; > + default: > + // Should not reach here > + printf("ERROR : unknown stage, %d\n", get_stage()); > + print_vmexit_info(); > + return VMX_TEST_VMEXIT; > + } > + vmcs_write(GUEST_RIP, guest_rip + insn_len); Hi Paolo, I noticed another possible bug of this patch. Stage 4 of this patch test the scenario that the page of a paging structure is not present, then this will cause EPT violation vmexit with bit 8 of exit_qual unset. My question is: will instruction length be correctly set on this scenario? I got wrong insn_len in "case 4" of VMX_EPT_VIOLATION, which may cause triple fault vmexit. > + return VMX_TEST_RESUME; > + default: > + printf("Unknown exit reason, %d\n", reason); > + print_vmexit_info(); > + } > + return VMX_TEST_VMEXIT; > +} > + > /* name/init/guest_main/exit_handler/syscall_handler/guest_regs > basic_* just implement some basic functions */ > struct vmx_test vmx_tests[] = { > @@ -83,5 +347,7 @@ struct vmx_test vmx_tests[] = { > basic_syscall_handler, {0} }, > { "vmenter", basic_init, vmenter_main, vmenter_exit_handler, > basic_syscall_handler, {0} }, > + { "EPT framework", ept_init, ept_main, ept_exit_handler, > + basic_syscall_handler, {0} }, > { NULL, NULL, NULL, NULL, NULL, {0} }, > }; > -- > 1.7.9.5 > ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 2/2] kvm-unit-tests: VMX: Test cases for nested EPT 2013-09-09 15:29 ` Arthur Chunqi Li @ 2013-09-09 16:23 ` Paolo Bonzini 0 siblings, 0 replies; 11+ messages in thread From: Paolo Bonzini @ 2013-09-09 16:23 UTC (permalink / raw) To: Arthur Chunqi Li; +Cc: kvm, Jan Kiszka, Gleb Natapov Il 09/09/2013 17:29, Arthur Chunqi Li ha scritto: > Hi Paolo, > I noticed another possible bug of this patch. Stage 4 of this patch > test the scenario that the page of a paging structure is not present, > then this will cause EPT violation vmexit with bit 8 of exit_qual > unset. My question is: will instruction length be correctly set on > this scenario? I got wrong insn_len in "case 4" of VMX_EPT_VIOLATION, > which may cause triple fault vmexit. It's plausible that the instruction length is wrong, since the processor might be fetching the instruction itself and doesn't know the length. Paolo ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 0/2] kvm-unit-tests: VMX: Test nested EPT features 2013-09-09 4:57 [PATCH 0/2] kvm-unit-tests: VMX: Test nested EPT features Arthur Chunqi Li 2013-09-09 4:57 ` [PATCH 1/2] kvm-unit-tests: VMX: The framework of EPT for nested VMX testing Arthur Chunqi Li 2013-09-09 4:57 ` [PATCH 2/2] kvm-unit-tests: VMX: Test cases for nested EPT Arthur Chunqi Li @ 2013-09-09 7:17 ` Jan Kiszka 2013-09-09 7:44 ` Arthur Chunqi Li 2 siblings, 1 reply; 11+ messages in thread From: Jan Kiszka @ 2013-09-09 7:17 UTC (permalink / raw) To: Arthur Chunqi Li; +Cc: kvm, gleb, pbonzini [-- Attachment #1: Type: text/plain, Size: 666 bytes --] On 2013-09-09 06:57, Arthur Chunqi Li wrote: > This series of patches provide the framework of nested EPT and some test > cases for nested EPT features. > > Arthur Chunqi Li (2): > kvm-unit-tests: VMX: The framework of EPT for nested VMX testing > kvm-unit-tests: VMX: Test cases for nested EPT > > x86/vmx.c | 159 ++++++++++++++++++++++++++++++++- > x86/vmx.h | 76 ++++++++++++++++ > x86/vmx_tests.c | 266 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 497 insertions(+), 4 deletions(-) > I suppose this is v2 of the previous patch? What is the delta? A meta changelog could go here. Jan [-- Attachment #2: OpenPGP digital signature --] [-- Type: application/pgp-signature, Size: 263 bytes --] ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 0/2] kvm-unit-tests: VMX: Test nested EPT features 2013-09-09 7:17 ` [PATCH 0/2] kvm-unit-tests: VMX: Test nested EPT features Jan Kiszka @ 2013-09-09 7:44 ` Arthur Chunqi Li 0 siblings, 0 replies; 11+ messages in thread From: Arthur Chunqi Li @ 2013-09-09 7:44 UTC (permalink / raw) To: Jan Kiszka; +Cc: kvm, Gleb Natapov, Paolo Bonzini On Mon, Sep 9, 2013 at 3:17 PM, Jan Kiszka <jan.kiszka@web.de> wrote: > On 2013-09-09 06:57, Arthur Chunqi Li wrote: >> This series of patches provide the framework of nested EPT and some test >> cases for nested EPT features. >> >> Arthur Chunqi Li (2): >> kvm-unit-tests: VMX: The framework of EPT for nested VMX testing >> kvm-unit-tests: VMX: Test cases for nested EPT >> >> x86/vmx.c | 159 ++++++++++++++++++++++++++++++++- >> x86/vmx.h | 76 ++++++++++++++++ >> x86/vmx_tests.c | 266 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ >> 3 files changed, 497 insertions(+), 4 deletions(-) >> > > I suppose this is v2 of the previous patch? What is the delta? A meta > changelog could go here. Yes, v1 just provide the framework of EPT (similar to the first patch of this series), and some more tests about nested EPT is added in this series (the second patch). Arthur > > Jan > ^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2013-09-09 16:23 UTC | newest] Thread overview: 11+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2013-09-09 4:57 [PATCH 0/2] kvm-unit-tests: VMX: Test nested EPT features Arthur Chunqi Li 2013-09-09 4:57 ` [PATCH 1/2] kvm-unit-tests: VMX: The framework of EPT for nested VMX testing Arthur Chunqi Li 2013-09-09 13:45 ` Paolo Bonzini 2013-09-09 4:57 ` [PATCH 2/2] kvm-unit-tests: VMX: Test cases for nested EPT Arthur Chunqi Li 2013-09-09 13:56 ` Paolo Bonzini 2013-09-09 14:11 ` Arthur Chunqi Li 2013-09-09 14:26 ` Paolo Bonzini 2013-09-09 15:29 ` Arthur Chunqi Li 2013-09-09 16:23 ` Paolo Bonzini 2013-09-09 7:17 ` [PATCH 0/2] kvm-unit-tests: VMX: Test nested EPT features Jan Kiszka 2013-09-09 7:44 ` Arthur Chunqi Li
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox