mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-09-12 10:44:06 -04:00
Merge tag 'kvm-x86-selftests-7.3' of https://github.com/kvm-x86/linux into HEAD
KVM selftests changes for 7.3, part 1 - Clean up nested SVM's handling of GPRs on L2<=>L1 transitions, reuse the functionality for nested VMX, and drop the ucall hack that was fudging around the lack of GPR switching on nVMX. - Add a stress test to verify KVM doesn't clobber/drop #PF state, e.g. CR2, across save/restore, including when L2 is active. - Add a test to verify KVM_CREATE_VM accepts exactly what is reported by KVM_CAP_VM_TYPES. - Misc selftests fixes and cleanups
This commit is contained in:
@@ -66,6 +66,7 @@ TEST_GEN_PROGS_COMMON += kvm_page_table_test
|
||||
TEST_GEN_PROGS_COMMON += set_memory_region_test
|
||||
TEST_GEN_PROGS_COMMON += memslot_modification_stress_test
|
||||
TEST_GEN_PROGS_COMMON += memslot_perf_test
|
||||
TEST_GEN_PROGS_COMMON += vm_types_test
|
||||
|
||||
# Compiled test targets
|
||||
TEST_GEN_PROGS_x86 = $(TEST_GEN_PROGS_COMMON)
|
||||
@@ -106,6 +107,7 @@ TEST_GEN_PROGS_x86 += x86/pmu_counters_test
|
||||
TEST_GEN_PROGS_x86 += x86/pmu_event_filter_test
|
||||
TEST_GEN_PROGS_x86 += x86/private_mem_conversions_test
|
||||
TEST_GEN_PROGS_x86 += x86/private_mem_kvm_exits_test
|
||||
TEST_GEN_PROGS_x86 += x86/save_restore_pf_stress_test
|
||||
TEST_GEN_PROGS_x86 += x86/set_boot_cpu_id
|
||||
TEST_GEN_PROGS_x86 += x86/set_sregs_test
|
||||
TEST_GEN_PROGS_x86 += x86/smaller_maxphyaddr_emulation_test
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include <linux/mman.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/stringify.h>
|
||||
|
||||
#define msecs_to_usecs(msec) ((msec) * 1000ULL)
|
||||
|
||||
|
||||
@@ -1207,30 +1207,23 @@ static inline int evmcs_vmlaunch(void)
|
||||
|
||||
current_evmcs->hv_clean_fields = 0;
|
||||
|
||||
__asm__ __volatile__("push %%rbp;"
|
||||
"push %%rcx;"
|
||||
"push %%rdx;"
|
||||
"push %%rsi;"
|
||||
"push %%rdi;"
|
||||
"push $0;"
|
||||
__asm__ __volatile__("push $0;"
|
||||
"mov %%rsp, (%[host_rsp]);"
|
||||
"lea 1f(%%rip), %%rax;"
|
||||
"mov %%rax, (%[host_rip]);"
|
||||
VMX_SWITCH_GPRS_ASM
|
||||
"vmlaunch;"
|
||||
"incq (%%rsp);"
|
||||
"1: pop %%rax;"
|
||||
"pop %%rdi;"
|
||||
"pop %%rsi;"
|
||||
"pop %%rdx;"
|
||||
"pop %%rcx;"
|
||||
"pop %%rbp;"
|
||||
"1: ;"
|
||||
VMX_SWITCH_GPRS_ASM
|
||||
"pop %%rax;"
|
||||
: [ret]"=&a"(ret)
|
||||
: [host_rsp]"r"
|
||||
((u64)¤t_evmcs->host_rsp),
|
||||
[host_rip]"r"
|
||||
((u64)¤t_evmcs->host_rip)
|
||||
: "memory", "cc", "rbx", "r8", "r9", "r10",
|
||||
"r11", "r12", "r13", "r14", "r15");
|
||||
((u64)¤t_evmcs->host_rip),
|
||||
GUEST_REGS_OFFSETS
|
||||
: "memory", "cc");
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1246,30 +1239,23 @@ static inline int evmcs_vmresume(void)
|
||||
/* HOST_RSP */
|
||||
current_evmcs->hv_clean_fields &= ~HV_VMX_ENLIGHTENED_CLEAN_FIELD_HOST_POINTER;
|
||||
|
||||
__asm__ __volatile__("push %%rbp;"
|
||||
"push %%rcx;"
|
||||
"push %%rdx;"
|
||||
"push %%rsi;"
|
||||
"push %%rdi;"
|
||||
"push $0;"
|
||||
__asm__ __volatile__("push $0;"
|
||||
"mov %%rsp, (%[host_rsp]);"
|
||||
"lea 1f(%%rip), %%rax;"
|
||||
"mov %%rax, (%[host_rip]);"
|
||||
VMX_SWITCH_GPRS_ASM
|
||||
"vmresume;"
|
||||
"incq (%%rsp);"
|
||||
"1: pop %%rax;"
|
||||
"pop %%rdi;"
|
||||
"pop %%rsi;"
|
||||
"pop %%rdx;"
|
||||
"pop %%rcx;"
|
||||
"pop %%rbp;"
|
||||
"1: ;"
|
||||
VMX_SWITCH_GPRS_ASM
|
||||
"pop %%rax;"
|
||||
: [ret]"=&a"(ret)
|
||||
: [host_rsp]"r"
|
||||
((u64)¤t_evmcs->host_rsp),
|
||||
[host_rip]"r"
|
||||
((u64)¤t_evmcs->host_rip)
|
||||
: "memory", "cc", "rbx", "r8", "r9", "r10",
|
||||
"r11", "r12", "r13", "r14", "r15");
|
||||
((u64)¤t_evmcs->host_rip),
|
||||
GUEST_REGS_OFFSETS
|
||||
: "memory", "cc");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ extern bool host_cpu_is_amd;
|
||||
extern bool host_cpu_is_hygon;
|
||||
extern bool host_cpu_is_amd_compatible;
|
||||
extern u64 guest_tsc_khz;
|
||||
extern struct kvm_mmu guest_mmu;
|
||||
|
||||
#ifndef MAX_NR_CPUID_ENTRIES
|
||||
#define MAX_NR_CPUID_ENTRIES 100
|
||||
@@ -396,8 +397,7 @@ static inline unsigned int x86_model(unsigned int eax)
|
||||
#define PTE_GET_PA(pte) ((pte) & PHYSICAL_PAGE_MASK)
|
||||
#define PTE_GET_PFN(pte) (PTE_GET_PA(pte) >> PAGE_SHIFT)
|
||||
|
||||
/* General Registers in 64-Bit Mode */
|
||||
struct gpr64_regs {
|
||||
struct guest_regs {
|
||||
u64 rax;
|
||||
u64 rcx;
|
||||
u64 rdx;
|
||||
@@ -414,8 +414,38 @@ struct gpr64_regs {
|
||||
u64 r13;
|
||||
u64 r14;
|
||||
u64 r15;
|
||||
u64 rflags;
|
||||
};
|
||||
|
||||
extern struct guest_regs guest_regs;
|
||||
|
||||
#define GUEST_REG_OFFSET(name) \
|
||||
[off_##name] "i" (offsetof(struct guest_regs, name))
|
||||
|
||||
#define GUEST_REGS_OFFSETS \
|
||||
GUEST_REG_OFFSET(rax), \
|
||||
GUEST_REG_OFFSET(rcx), \
|
||||
GUEST_REG_OFFSET(rdx), \
|
||||
GUEST_REG_OFFSET(rbx), \
|
||||
GUEST_REG_OFFSET(rsp), \
|
||||
GUEST_REG_OFFSET(rbp), \
|
||||
GUEST_REG_OFFSET(rsi), \
|
||||
GUEST_REG_OFFSET(rdi), \
|
||||
GUEST_REG_OFFSET(r8), \
|
||||
GUEST_REG_OFFSET(r9), \
|
||||
GUEST_REG_OFFSET(r10), \
|
||||
GUEST_REG_OFFSET(r11), \
|
||||
GUEST_REG_OFFSET(r12), \
|
||||
GUEST_REG_OFFSET(r13), \
|
||||
GUEST_REG_OFFSET(r14), \
|
||||
GUEST_REG_OFFSET(r15), \
|
||||
GUEST_REG_OFFSET(rflags)
|
||||
|
||||
#define GUEST_REG(name) "guest_regs + %c[off_" #name "]"
|
||||
|
||||
#define GUEST_SWITCH_GPR_ASM(name) \
|
||||
"xchg %%" #name ", " GUEST_REG(name) "\n\t"
|
||||
|
||||
struct desc64 {
|
||||
u16 limit0;
|
||||
u16 base0;
|
||||
@@ -582,6 +612,14 @@ static inline void set_cr0(u64 val)
|
||||
__asm__ __volatile__("mov %0, %%cr0" : : "r" (val) : "memory");
|
||||
}
|
||||
|
||||
static inline u64 get_cr2(void)
|
||||
{
|
||||
u64 cr2;
|
||||
|
||||
__asm__ __volatile__("mov %%cr2, %[cr2]" : [cr2]"=r"(cr2));
|
||||
return cr2;
|
||||
}
|
||||
|
||||
static inline u64 get_cr3(void)
|
||||
{
|
||||
u64 cr3;
|
||||
@@ -877,6 +915,11 @@ static inline void write_sse_reg(int reg, const sse128_t *data)
|
||||
}
|
||||
}
|
||||
|
||||
static inline void invlpg(u64 addr)
|
||||
{
|
||||
__asm__ __volatile__("invlpg (%0)" : : "r"(addr) : "memory");
|
||||
}
|
||||
|
||||
static inline void cpu_relax(void)
|
||||
{
|
||||
asm volatile("rep; nop" ::: "memory");
|
||||
@@ -913,6 +956,11 @@ struct kvm_x86_state *vcpu_save_state(struct kvm_vcpu *vcpu);
|
||||
void vcpu_load_state(struct kvm_vcpu *vcpu, struct kvm_x86_state *state);
|
||||
void kvm_x86_state_cleanup(struct kvm_x86_state *state);
|
||||
|
||||
static inline bool kvm_x86_state_is_guest_mode(struct kvm_x86_state *state)
|
||||
{
|
||||
return state->nested.size && (state->nested.flags & KVM_STATE_NESTED_GUEST_MODE);
|
||||
}
|
||||
|
||||
const struct kvm_msr_list *kvm_get_msr_index_list(void);
|
||||
const struct kvm_msr_list *kvm_get_feature_msr_index_list(void);
|
||||
bool kvm_msr_is_in_save_restore_list(u32 msr_index);
|
||||
|
||||
@@ -290,6 +290,23 @@ struct vmx_msr_entry {
|
||||
u64 value;
|
||||
} __attribute__ ((aligned(16)));
|
||||
|
||||
#define VMX_SWITCH_GPRS_ASM \
|
||||
GUEST_SWITCH_GPR_ASM(rax) \
|
||||
GUEST_SWITCH_GPR_ASM(rbx) \
|
||||
GUEST_SWITCH_GPR_ASM(rcx) \
|
||||
GUEST_SWITCH_GPR_ASM(rdx) \
|
||||
GUEST_SWITCH_GPR_ASM(rbp) \
|
||||
GUEST_SWITCH_GPR_ASM(rsi) \
|
||||
GUEST_SWITCH_GPR_ASM(rdi) \
|
||||
GUEST_SWITCH_GPR_ASM(r8) \
|
||||
GUEST_SWITCH_GPR_ASM(r9) \
|
||||
GUEST_SWITCH_GPR_ASM(r10) \
|
||||
GUEST_SWITCH_GPR_ASM(r11) \
|
||||
GUEST_SWITCH_GPR_ASM(r12) \
|
||||
GUEST_SWITCH_GPR_ASM(r13) \
|
||||
GUEST_SWITCH_GPR_ASM(r14) \
|
||||
GUEST_SWITCH_GPR_ASM(r15)
|
||||
|
||||
#include "evmcs.h"
|
||||
|
||||
static inline int vmxon(u64 phys)
|
||||
@@ -363,9 +380,6 @@ static inline u64 vmptrstz(void)
|
||||
return value;
|
||||
}
|
||||
|
||||
/*
|
||||
* No guest state (e.g. GPRs) is established by this vmlaunch.
|
||||
*/
|
||||
static inline int vmlaunch(void)
|
||||
{
|
||||
int ret;
|
||||
@@ -373,34 +387,24 @@ static inline int vmlaunch(void)
|
||||
if (enable_evmcs)
|
||||
return evmcs_vmlaunch();
|
||||
|
||||
__asm__ __volatile__("push %%rbp;"
|
||||
"push %%rcx;"
|
||||
"push %%rdx;"
|
||||
"push %%rsi;"
|
||||
"push %%rdi;"
|
||||
"push $0;"
|
||||
__asm__ __volatile__("push $0;"
|
||||
"vmwrite %%rsp, %[host_rsp];"
|
||||
"lea 1f(%%rip), %%rax;"
|
||||
"vmwrite %%rax, %[host_rip];"
|
||||
VMX_SWITCH_GPRS_ASM
|
||||
"vmlaunch;"
|
||||
"incq (%%rsp);"
|
||||
"1: pop %%rax;"
|
||||
"pop %%rdi;"
|
||||
"pop %%rsi;"
|
||||
"pop %%rdx;"
|
||||
"pop %%rcx;"
|
||||
"pop %%rbp;"
|
||||
"1: ;"
|
||||
VMX_SWITCH_GPRS_ASM
|
||||
"pop %%rax;"
|
||||
: [ret]"=&a"(ret)
|
||||
: [host_rsp]"r"((u64)HOST_RSP),
|
||||
[host_rip]"r"((u64)HOST_RIP)
|
||||
: "memory", "cc", "rbx", "r8", "r9", "r10",
|
||||
"r11", "r12", "r13", "r14", "r15");
|
||||
[host_rip]"r"((u64)HOST_RIP),
|
||||
GUEST_REGS_OFFSETS
|
||||
: "memory", "cc");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* No guest state (e.g. GPRs) is established by this vmresume.
|
||||
*/
|
||||
static inline int vmresume(void)
|
||||
{
|
||||
int ret;
|
||||
@@ -408,28 +412,21 @@ static inline int vmresume(void)
|
||||
if (enable_evmcs)
|
||||
return evmcs_vmresume();
|
||||
|
||||
__asm__ __volatile__("push %%rbp;"
|
||||
"push %%rcx;"
|
||||
"push %%rdx;"
|
||||
"push %%rsi;"
|
||||
"push %%rdi;"
|
||||
"push $0;"
|
||||
__asm__ __volatile__("push $0;"
|
||||
"vmwrite %%rsp, %[host_rsp];"
|
||||
"lea 1f(%%rip), %%rax;"
|
||||
"vmwrite %%rax, %[host_rip];"
|
||||
VMX_SWITCH_GPRS_ASM
|
||||
"vmresume;"
|
||||
"incq (%%rsp);"
|
||||
"1: pop %%rax;"
|
||||
"pop %%rdi;"
|
||||
"pop %%rsi;"
|
||||
"pop %%rdx;"
|
||||
"pop %%rcx;"
|
||||
"pop %%rbp;"
|
||||
"1: ;"
|
||||
VMX_SWITCH_GPRS_ASM
|
||||
"pop %%rax;"
|
||||
: [ret]"=&a"(ret)
|
||||
: [host_rsp]"r"((u64)HOST_RSP),
|
||||
[host_rip]"r"((u64)HOST_RIP)
|
||||
: "memory", "cc", "rbx", "r8", "r9", "r10",
|
||||
"r11", "r12", "r13", "r14", "r15");
|
||||
[host_rip]"r"((u64)HOST_RIP),
|
||||
GUEST_REGS_OFFSETS
|
||||
: "memory", "cc");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ test_assert(bool exp, const char *exp_str,
|
||||
if (!(exp)) {
|
||||
va_start(ap, fmt);
|
||||
|
||||
fprintf(stderr, "==== Test Assertion Failure ====\n"
|
||||
fprintf(stderr, "\n==== Test Assertion Failure ====\n"
|
||||
" %s:%u: %s\n"
|
||||
" pid=%d tid=%d errno=%d - %s\n",
|
||||
file, line, exp_str, getpid(), _gettid(),
|
||||
|
||||
@@ -28,6 +28,9 @@ bool host_cpu_is_hygon;
|
||||
bool host_cpu_is_amd_compatible;
|
||||
bool is_forced_emulation_enabled;
|
||||
u64 guest_tsc_khz;
|
||||
struct kvm_mmu guest_mmu;
|
||||
|
||||
struct guest_regs guest_regs;
|
||||
|
||||
const char *ex_str(int vector)
|
||||
{
|
||||
@@ -829,6 +832,17 @@ void kvm_arch_vm_post_create(struct kvm_vm *vm, unsigned int nr_vcpus)
|
||||
TEST_ASSERT(r > 0, "KVM_GET_TSC_KHZ did not provide a valid TSC frequency.");
|
||||
guest_tsc_khz = r;
|
||||
sync_global_to_guest(vm, guest_tsc_khz);
|
||||
|
||||
/*
|
||||
* The guest MMU is just a placeholder to provide access to PTE masks
|
||||
* (for now). The guest does not have mappings for its own page tables
|
||||
* by default, so any meaningful use of guest page tables requires
|
||||
* explicit setup by the test. Zero the PGD to make it obvious the guest
|
||||
* page tables are not immediately usable by guest code.
|
||||
*/
|
||||
guest_mmu = vm->mmu;
|
||||
guest_mmu.pgd = 0;
|
||||
sync_global_to_guest(vm, guest_mmu);
|
||||
}
|
||||
|
||||
void vcpu_arch_set_entry_point(struct kvm_vcpu *vcpu, void *guest_code)
|
||||
|
||||
@@ -13,9 +13,6 @@
|
||||
|
||||
#define SEV_DEV_PATH "/dev/sev"
|
||||
|
||||
struct gpr64_regs guest_regs;
|
||||
u64 rflags;
|
||||
|
||||
/* Allocate memory regions for nested SVM tests.
|
||||
*
|
||||
* Input Args:
|
||||
@@ -137,46 +134,47 @@ void generic_svm_setup(struct svm_test_data *svm, void *guest_rip)
|
||||
* save/restore 64-bit general registers except rax, rip, rsp
|
||||
* which are directly handed through the VMCB guest processor state
|
||||
*/
|
||||
#define SAVE_GPR_C \
|
||||
"xchg %%rbx, guest_regs+0x20\n\t" \
|
||||
"xchg %%rcx, guest_regs+0x10\n\t" \
|
||||
"xchg %%rdx, guest_regs+0x18\n\t" \
|
||||
"xchg %%rbp, guest_regs+0x30\n\t" \
|
||||
"xchg %%rsi, guest_regs+0x38\n\t" \
|
||||
"xchg %%rdi, guest_regs+0x40\n\t" \
|
||||
"xchg %%r8, guest_regs+0x48\n\t" \
|
||||
"xchg %%r9, guest_regs+0x50\n\t" \
|
||||
"xchg %%r10, guest_regs+0x58\n\t" \
|
||||
"xchg %%r11, guest_regs+0x60\n\t" \
|
||||
"xchg %%r12, guest_regs+0x68\n\t" \
|
||||
"xchg %%r13, guest_regs+0x70\n\t" \
|
||||
"xchg %%r14, guest_regs+0x78\n\t" \
|
||||
"xchg %%r15, guest_regs+0x80\n\t"
|
||||
|
||||
#define LOAD_GPR_C SAVE_GPR_C
|
||||
#define SVM_SWITCH_GPRS_ASM \
|
||||
GUEST_SWITCH_GPR_ASM(rbx) \
|
||||
GUEST_SWITCH_GPR_ASM(rcx) \
|
||||
GUEST_SWITCH_GPR_ASM(rdx) \
|
||||
GUEST_SWITCH_GPR_ASM(rbp) \
|
||||
GUEST_SWITCH_GPR_ASM(rsi) \
|
||||
GUEST_SWITCH_GPR_ASM(rdi) \
|
||||
GUEST_SWITCH_GPR_ASM(r8) \
|
||||
GUEST_SWITCH_GPR_ASM(r9) \
|
||||
GUEST_SWITCH_GPR_ASM(r10) \
|
||||
GUEST_SWITCH_GPR_ASM(r11) \
|
||||
GUEST_SWITCH_GPR_ASM(r12) \
|
||||
GUEST_SWITCH_GPR_ASM(r13) \
|
||||
GUEST_SWITCH_GPR_ASM(r14) \
|
||||
GUEST_SWITCH_GPR_ASM(r15)
|
||||
|
||||
/*
|
||||
* selftests do not use interrupts so we dropped clgi/sti/cli/stgi
|
||||
* for now. registers involved in LOAD/SAVE_GPR_C are eventually
|
||||
* for now. Registers involved in SVM_SWITCH_GPRS_ASM are eventually
|
||||
* unmodified so they do not need to be in the clobber list.
|
||||
*/
|
||||
void run_guest(struct vmcb *vmcb, u64 vmcb_gpa)
|
||||
{
|
||||
asm volatile (
|
||||
"vmload %[vmcb_gpa]\n\t"
|
||||
"mov rflags, %%r15\n\t" // rflags
|
||||
"mov %%r15, 0x170(%[vmcb])\n\t"
|
||||
"mov guest_regs, %%r15\n\t" // rax
|
||||
"mov %%r15, 0x1f8(%[vmcb])\n\t"
|
||||
LOAD_GPR_C
|
||||
"mov " GUEST_REG(rflags) ", %%r15\n\t"
|
||||
"mov %%r15, %[vmcb_rflags]\n\t"
|
||||
"mov " GUEST_REG(rax) ", %%r15\n\t"
|
||||
"mov %%r15, %[vmcb_rax]\n\t"
|
||||
SVM_SWITCH_GPRS_ASM
|
||||
"vmrun %[vmcb_gpa]\n\t"
|
||||
SAVE_GPR_C
|
||||
"mov 0x170(%[vmcb]), %%r15\n\t" // rflags
|
||||
"mov %%r15, rflags\n\t"
|
||||
"mov 0x1f8(%[vmcb]), %%r15\n\t" // rax
|
||||
"mov %%r15, guest_regs\n\t"
|
||||
SVM_SWITCH_GPRS_ASM
|
||||
"mov %[vmcb_rflags], %%r15\n\t"
|
||||
"mov %%r15, " GUEST_REG(rflags) "\n\t"
|
||||
"mov %[vmcb_rax], %%r15\n\t" // rax
|
||||
"mov %%r15, " GUEST_REG(rax) "\n\t"
|
||||
"vmsave %[vmcb_gpa]\n\t"
|
||||
: : [vmcb] "r" (vmcb), [vmcb_gpa] "a" (vmcb_gpa)
|
||||
: [vmcb_rflags] "+m" (vmcb->save.rflags),
|
||||
[vmcb_rax] "+m" (vmcb->save.rax)
|
||||
: [vmcb_gpa] "a" (vmcb_gpa),
|
||||
GUEST_REGS_OFFSETS
|
||||
: "r15", "memory");
|
||||
}
|
||||
|
||||
|
||||
@@ -10,36 +10,8 @@
|
||||
|
||||
void ucall_arch_do_ucall(gva_t uc)
|
||||
{
|
||||
/*
|
||||
* FIXME: Revert this hack (the entire commit that added it) once nVMX
|
||||
* preserves L2 GPRs across a nested VM-Exit. If a ucall from L2, e.g.
|
||||
* to do a GUEST_SYNC(), lands the vCPU in L1, any and all GPRs can be
|
||||
* clobbered by L1. Save and restore non-volatile GPRs (clobbering RBP
|
||||
* in particular is problematic) along with RDX and RDI (which are
|
||||
* inputs), and clobber volatile GPRs. *sigh*
|
||||
*/
|
||||
#define HORRIFIC_L2_UCALL_CLOBBER_HACK \
|
||||
"rcx", "rsi", "r8", "r9", "r10", "r11"
|
||||
|
||||
asm volatile("push %%rbp\n\t"
|
||||
"push %%r15\n\t"
|
||||
"push %%r14\n\t"
|
||||
"push %%r13\n\t"
|
||||
"push %%r12\n\t"
|
||||
"push %%rbx\n\t"
|
||||
"push %%rdx\n\t"
|
||||
"push %%rdi\n\t"
|
||||
"in %[port], %%al\n\t"
|
||||
"pop %%rdi\n\t"
|
||||
"pop %%rdx\n\t"
|
||||
"pop %%rbx\n\t"
|
||||
"pop %%r12\n\t"
|
||||
"pop %%r13\n\t"
|
||||
"pop %%r14\n\t"
|
||||
"pop %%r15\n\t"
|
||||
"pop %%rbp\n\t"
|
||||
: : [port] "d" (UCALL_PIO_PORT), "D" (uc) : "rax", "memory",
|
||||
HORRIFIC_L2_UCALL_CLOBBER_HACK);
|
||||
asm volatile("in %[port], %%al"
|
||||
: : [port] "d" (UCALL_PIO_PORT), "D" (uc) : "rax", "memory");
|
||||
}
|
||||
|
||||
void *ucall_arch_get_ucall(struct kvm_vcpu *vcpu)
|
||||
|
||||
@@ -232,7 +232,7 @@ static inline void init_vmcs_control_fields(struct vmx_pages *vmx)
|
||||
|
||||
vmwrite(EXCEPTION_BITMAP, 0);
|
||||
vmwrite(PAGE_FAULT_ERROR_CODE_MASK, 0);
|
||||
vmwrite(PAGE_FAULT_ERROR_CODE_MATCH, -1); /* Never match */
|
||||
vmwrite(PAGE_FAULT_ERROR_CODE_MATCH, 0);
|
||||
vmwrite(CR3_TARGET_COUNT, 0);
|
||||
vmwrite(VM_EXIT_CONTROLS, rdmsr(MSR_IA32_VMX_EXIT_CTLS) |
|
||||
VM_EXIT_HOST_ADDR_SPACE_SIZE); /* 64-bit host */
|
||||
|
||||
@@ -492,7 +492,7 @@ static void test_pmu_events_snaphost(void)
|
||||
struct riscv_pmu_snapshot_data *snapshot_data = snapshot_gva;
|
||||
int i;
|
||||
|
||||
/* Verify presence of SBI PMU and minimum requrired SBI version */
|
||||
/* Verify presence of SBI PMU and minimum required SBI version */
|
||||
verify_sbi_requirement_assert();
|
||||
|
||||
snapshot_set_shmem(snapshot_gpa, 0);
|
||||
@@ -518,7 +518,7 @@ static void test_pmu_events_overflow(void)
|
||||
{
|
||||
int num_counters = 0, i = 0;
|
||||
|
||||
/* Verify presence of SBI PMU and minimum requrired SBI version */
|
||||
/* Verify presence of SBI PMU and minimum required SBI version */
|
||||
verify_sbi_requirement_assert();
|
||||
|
||||
snapshot_set_shmem(snapshot_gpa, 0);
|
||||
|
||||
@@ -508,23 +508,18 @@ int main(int ac, char **av)
|
||||
{
|
||||
struct kvm_vcpu *vcpus[NR_VCPUS];
|
||||
struct kvm_vm *vm;
|
||||
pthread_attr_t attr;
|
||||
pthread_t thread;
|
||||
cpu_set_t cpuset;
|
||||
unsigned int gpages;
|
||||
long stolen_time;
|
||||
long run_delay;
|
||||
bool verbose;
|
||||
int i;
|
||||
int i, cpu;
|
||||
|
||||
verbose = ac > 1 && (!strncmp(av[1], "-v", 3) || !strncmp(av[1], "--verbose", 10));
|
||||
|
||||
/* Set CPU affinity so we can force preemption of the VCPU */
|
||||
CPU_ZERO(&cpuset);
|
||||
CPU_SET(0, &cpuset);
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset);
|
||||
pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
|
||||
cpu = pin_self_to_any_cpu();
|
||||
|
||||
/* Create a VM and an identity mapped memslot for the steal time structure */
|
||||
vm = vm_create_with_vcpus(NR_VCPUS, guest_code, vcpus);
|
||||
@@ -558,7 +553,11 @@ int main(int ac, char **av)
|
||||
|
||||
/* Steal time from the VCPU. The steal time thread has the same CPU affinity as the VCPUs. */
|
||||
run_delay = get_run_delay();
|
||||
pthread_create(&thread, &attr, do_steal_time, NULL);
|
||||
pthread_create(&thread, NULL, do_steal_time, NULL);
|
||||
pthread_getaffinity_np(thread, sizeof(cpuset), &cpuset);
|
||||
TEST_ASSERT(CPU_COUNT(&cpuset) == 1 && CPU_ISSET(cpu, &cpuset),
|
||||
"Worker failed to inherit parent's CPU affinity");
|
||||
|
||||
do
|
||||
sched_yield();
|
||||
while (get_run_delay() - run_delay < MIN_RUN_DELAY_NS);
|
||||
|
||||
48
tools/testing/selftests/kvm/vm_types_test.c
Normal file
48
tools/testing/selftests/kvm/vm_types_test.c
Normal file
@@ -0,0 +1,48 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Verify that KVM_CREATE_VM accepts exactly the VM types enumerated by
|
||||
* KVM_CAP_VM_TYPES, and rejects every other type with -EINVAL.
|
||||
*/
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <linux/kvm.h>
|
||||
|
||||
#include "kvm_util.h"
|
||||
#include "test_util.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
unsigned long type, supported_types;
|
||||
int kvm_fd;
|
||||
|
||||
TEST_REQUIRE(kvm_has_cap(KVM_CAP_VM_TYPES));
|
||||
|
||||
kvm_fd = open_kvm_dev_path_or_exit();
|
||||
supported_types = kvm_check_cap(KVM_CAP_VM_TYPES);
|
||||
pr_info("Supported VM types: 0x%lx\n", supported_types);
|
||||
|
||||
/*
|
||||
* For compatibility with 32-bit kernels, KVM_CHECK_EXTENSION restricts
|
||||
* its return to 32-bit values, i.e. only types 0..31 can be advertised.
|
||||
* Walk past that range as well to confirm that any out-of-range type is
|
||||
* rejected rather than silently accepted (or truncated).
|
||||
*/
|
||||
for (type = 0; type < BITS_PER_TYPE(supported_types); type++) {
|
||||
int fd = __kvm_ioctl(kvm_fd, KVM_CREATE_VM, (void *)type);
|
||||
|
||||
if (supported_types & BIT(type)) {
|
||||
TEST_ASSERT(fd >= 0,
|
||||
"KVM_CREATE_VM(%lu) should succeed, supported types = 0x%lx",
|
||||
type, supported_types);
|
||||
kvm_close(fd);
|
||||
} else {
|
||||
TEST_ASSERT(fd < 0 && errno == EINVAL,
|
||||
"KVM_CREATE_VM(%lu) should fail with EINVAL, supported types = 0x%lx",
|
||||
type, supported_types);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -22,9 +22,6 @@
|
||||
|
||||
#define SYNC_PORT 0xe
|
||||
|
||||
#define STR(x) #x
|
||||
#define XSTR(s) STR(s)
|
||||
|
||||
/*
|
||||
* SMI handler: runs in real-address mode.
|
||||
* Reports SMRAM_STAGE via port IO, then does RSM.
|
||||
@@ -37,7 +34,7 @@ static u8 smi_handler[] = {
|
||||
|
||||
static inline void sync_with_host(u64 phase)
|
||||
{
|
||||
asm volatile("in $" XSTR(SYNC_PORT) ", %%al \n"
|
||||
asm volatile("in $" __stringify(SYNC_PORT) ", %%al \n"
|
||||
: "+a" (phase));
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
*/
|
||||
#include <asm/kvm_para.h>
|
||||
#include <linux/kvm_para.h>
|
||||
#include <linux/stringify.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "kvm_test_harness.h"
|
||||
|
||||
@@ -56,7 +56,7 @@ static inline void check_tsc_msr_rdtsc(void)
|
||||
tsc_freq = rdmsr(HV_X64_MSR_TSC_FREQUENCY);
|
||||
GUEST_ASSERT(tsc_freq > 0);
|
||||
|
||||
/* For increased accuracy, take mean rdtsc() before and afrer rdmsr() */
|
||||
/* For increased accuracy, take mean rdtsc() before and after rdmsr() */
|
||||
r1 = rdtsc();
|
||||
t1 = rdmsr(HV_X64_MSR_TIME_REF_COUNT);
|
||||
r1 = (r1 + rdtsc()) / 2;
|
||||
@@ -181,7 +181,7 @@ static void host_check_tsc_msr_rdtsc(struct kvm_vcpu *vcpu)
|
||||
tsc_freq = vcpu_get_msr(vcpu, HV_X64_MSR_TSC_FREQUENCY);
|
||||
TEST_ASSERT(tsc_freq > 0, "TSC frequency must be nonzero");
|
||||
|
||||
/* For increased accuracy, take mean rdtsc() before and afrer ioctl */
|
||||
/* For increased accuracy, take mean rdtsc() before and after ioctl */
|
||||
r1 = rdtsc();
|
||||
t1 = vcpu_get_msr(vcpu, HV_X64_MSR_TIME_REF_COUNT);
|
||||
r1 = (r1 + rdtsc()) / 2;
|
||||
|
||||
@@ -125,7 +125,7 @@ void guest_code(struct vmx_pages *vmx_pages, struct hyperv_test_pages *hv_pages,
|
||||
/*
|
||||
* NMI forces L2->L1 exit, resuming L2 and hope that EVMCS is
|
||||
* up-to-date (RIP points where it should and not at the beginning
|
||||
* of l2_guest_code(). GUEST_SYNC(9) checkes that.
|
||||
* of l2_guest_code(). GUEST_SYNC(9) checks that.
|
||||
*/
|
||||
GUEST_ASSERT(!vmresume());
|
||||
|
||||
|
||||
288
tools/testing/selftests/kvm/x86/save_restore_pf_stress_test.c
Normal file
288
tools/testing/selftests/kvm/x86/save_restore_pf_stress_test.c
Normal file
@@ -0,0 +1,288 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
|
||||
#include "test_util.h"
|
||||
#include "kvm_util.h"
|
||||
#include "processor.h"
|
||||
#include "svm_util.h"
|
||||
#include "vmx.h"
|
||||
|
||||
#define NR_ITERATIONS 500
|
||||
|
||||
#define PTRS_PER_PTE 512
|
||||
#define PXD_INDEX(vaddr, level) (((vaddr) >> PG_LEVEL_SHIFT(level)) & (PTRS_PER_PTE - 1))
|
||||
|
||||
#define TEST_MEM_BASE_GVA 0xc0000000ULL
|
||||
#define TEST_PGTABLE_GVA_OFFSET 0xd0000000ULL
|
||||
#define PATTERN 0xabcdefabcdefabcdULL
|
||||
|
||||
static u64 expected_vaddr;
|
||||
static u64 guest_faults;
|
||||
|
||||
static u64 *guest_get_pte(u64 vaddr)
|
||||
{
|
||||
u64 pgtable_pa, pte;
|
||||
u64 *pgtable;
|
||||
int level;
|
||||
|
||||
level = (get_cr4() & X86_CR4_LA57) ? PG_LEVEL_256T : PG_LEVEL_512G;
|
||||
|
||||
pgtable_pa = get_cr3() & PHYSICAL_PAGE_MASK;
|
||||
for (; level > PG_LEVEL_4K; level--) {
|
||||
pgtable = (u64 *)(pgtable_pa + TEST_PGTABLE_GVA_OFFSET);
|
||||
pte = pgtable[PXD_INDEX(vaddr, level)];
|
||||
GUEST_ASSERT(pte & PTE_PRESENT_MASK(&guest_mmu));
|
||||
GUEST_ASSERT(!(pte & PTE_HUGE_MASK(&guest_mmu)));
|
||||
pgtable_pa = PTE_GET_PA(pte);
|
||||
}
|
||||
|
||||
pgtable = (u64 *)(pgtable_pa + TEST_PGTABLE_GVA_OFFSET);
|
||||
return &pgtable[PXD_INDEX(vaddr, PG_LEVEL_4K)];
|
||||
}
|
||||
|
||||
static void guest_pf_handler(struct ex_regs *regs)
|
||||
{
|
||||
u64 fault_addr;
|
||||
u64 *ptep;
|
||||
|
||||
fault_addr = get_cr2();
|
||||
GUEST_ASSERT_EQ(fault_addr, READ_ONCE(expected_vaddr));
|
||||
|
||||
ptep = guest_get_pte(fault_addr);
|
||||
GUEST_ASSERT(ptep);
|
||||
GUEST_ASSERT(!(*ptep & PTE_PRESENT_MASK(&guest_mmu)));
|
||||
|
||||
*ptep |= PTE_PRESENT_MASK(&guest_mmu);
|
||||
guest_faults++;
|
||||
}
|
||||
|
||||
static void guest_access_memory(void *arg)
|
||||
{
|
||||
u64 vaddr, val;
|
||||
int i;
|
||||
|
||||
for (i = 0; ; i++) {
|
||||
vaddr = TEST_MEM_BASE_GVA + (i % PTRS_PER_PTE) * PAGE_SIZE;
|
||||
WRITE_ONCE(expected_vaddr, vaddr);
|
||||
|
||||
/* Read to trigger #PF */
|
||||
val = READ_ONCE(*(u64 *)vaddr);
|
||||
GUEST_ASSERT_EQ(val, PATTERN);
|
||||
|
||||
/* Clear the present bit again so it faults next time */
|
||||
*guest_get_pte(vaddr) &= ~PTE_PRESENT_MASK(&guest_mmu);
|
||||
invlpg(vaddr);
|
||||
}
|
||||
}
|
||||
|
||||
static void l1_svm_code(struct svm_test_data *svm)
|
||||
{
|
||||
generic_svm_setup(svm, guest_access_memory);
|
||||
svm->vmcb->control.intercept_exceptions |= BIT(UD_VECTOR);
|
||||
|
||||
while (1) {
|
||||
run_guest(svm->vmcb, svm->vmcb_gpa);
|
||||
GUEST_ASSERT_EQ(svm->vmcb->control.exit_code,
|
||||
(SVM_EXIT_EXCP_BASE + UD_VECTOR));
|
||||
}
|
||||
}
|
||||
|
||||
static void l1_vmx_code(struct vmx_pages *vmx)
|
||||
{
|
||||
GUEST_ASSERT(prepare_for_vmx_operation(vmx));
|
||||
GUEST_ASSERT(load_vmcs(vmx));
|
||||
prepare_vmcs(vmx, guest_access_memory);
|
||||
|
||||
GUEST_ASSERT(!vmwrite(EXCEPTION_BITMAP, BIT(UD_VECTOR)));
|
||||
|
||||
GUEST_ASSERT(!vmlaunch());
|
||||
while (1) {
|
||||
GUEST_ASSERT_EQ(vmreadz(VM_EXIT_REASON), EXIT_REASON_EXCEPTION_NMI);
|
||||
GUEST_ASSERT_EQ(vmreadz(VM_EXIT_INTR_INFO) & 0xff, UD_VECTOR);
|
||||
GUEST_ASSERT(!vmresume());
|
||||
}
|
||||
}
|
||||
|
||||
static void l1_guest_code(void *test_data)
|
||||
{
|
||||
if (this_cpu_has(X86_FEATURE_SVM))
|
||||
l1_svm_code(test_data);
|
||||
else
|
||||
l1_vmx_code(test_data);
|
||||
}
|
||||
|
||||
static void *sigusr_thread_fn(void *arg)
|
||||
{
|
||||
pthread_t vcpu_thread = (pthread_t)arg;
|
||||
|
||||
for (;;) {
|
||||
pthread_testcancel();
|
||||
pthread_kill(vcpu_thread, SIGUSR1);
|
||||
usleep(msecs_to_usecs(1));
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void dummy_signal_handler(int signo) {}
|
||||
static struct sigaction sa;
|
||||
|
||||
static void vcpu_sigusr_listen(void)
|
||||
{
|
||||
sa.sa_handler = dummy_signal_handler;
|
||||
sigaction(SIGUSR1, &sa, NULL);
|
||||
}
|
||||
|
||||
static void vcpu_sigusr_ignore(void)
|
||||
{
|
||||
sa.sa_handler = SIG_IGN;
|
||||
sigaction(SIGUSR1, &sa, NULL);
|
||||
}
|
||||
|
||||
static void kvm_x86_state_queue_ud(struct kvm_x86_state *state)
|
||||
{
|
||||
if (state->events.exception.pending || state->events.exception.injected)
|
||||
return;
|
||||
|
||||
state->events.flags |= KVM_VCPUEVENT_VALID_PAYLOAD;
|
||||
state->events.exception.pending = true;
|
||||
state->events.exception.injected = false;
|
||||
state->events.exception.nr = UD_VECTOR;
|
||||
state->events.exception.has_error_code = false;
|
||||
state->events.exception_has_payload = false;
|
||||
}
|
||||
|
||||
static void run_test(bool nested)
|
||||
{
|
||||
struct kvm_x86_state *state;
|
||||
int r, i, level;
|
||||
pthread_t sigusr_thread;
|
||||
gpa_t gpa, pgtable_gpa;
|
||||
struct kvm_vcpu *vcpu;
|
||||
struct kvm_vm *vm;
|
||||
struct ucall uc;
|
||||
u64 *pgtable;
|
||||
gva_t gva;
|
||||
u64 pte;
|
||||
|
||||
vm = vm_create_with_one_vcpu(&vcpu, nested ? l1_guest_code : guest_access_memory);
|
||||
vm_install_exception_handler(vm, PF_VECTOR, guest_pf_handler);
|
||||
|
||||
if (nested) {
|
||||
vm_enable_cap(vm, KVM_CAP_EXCEPTION_PAYLOAD, -2ul);
|
||||
if (kvm_cpu_has(X86_FEATURE_SVM))
|
||||
vcpu_alloc_svm(vm, &gva);
|
||||
else
|
||||
vcpu_alloc_vmx(vm, &gva);
|
||||
vcpu_args_set(vcpu, 1, gva);
|
||||
}
|
||||
|
||||
/* Allocate a page and write the pattern to it */
|
||||
gva = vm_alloc_page(vm);
|
||||
*(u64 *)addr_gva2hva(vm, gva) = PATTERN;
|
||||
gpa = addr_gva2gpa(vm, gva);
|
||||
|
||||
/*
|
||||
* Map all virtual addresses to the pattern page and clear the present
|
||||
* bit such that guest accesses will cause a #PF.
|
||||
*/
|
||||
for (i = 0; i < PTRS_PER_PTE; i++) {
|
||||
gva = TEST_MEM_BASE_GVA + i * getpagesize();
|
||||
virt_pg_map(vm, gva, gpa);
|
||||
*vm_get_pte(vm, gva) &= ~PTE_PRESENT_MASK(&vm->mmu);
|
||||
}
|
||||
|
||||
/*
|
||||
* Now create mappings for the page tables created above so that the
|
||||
* guest #PF handler can walk them. All PTEs for test virtual addresses
|
||||
* should lie on the same PTE page, so one page is mapped for each page
|
||||
* table level.
|
||||
*
|
||||
* Use an offset for the GVA instead of creating identity mappings to
|
||||
* avoid collision with existing mappings at low GVAs (e.g. ELF).
|
||||
*/
|
||||
pgtable_gpa = vm->mmu.pgd;
|
||||
for (level = vm->mmu.pgtable_levels; level >= PG_LEVEL_4K; level--) {
|
||||
virt_map(vm, pgtable_gpa + TEST_PGTABLE_GVA_OFFSET, pgtable_gpa, 1);
|
||||
pgtable = addr_gpa2hva(vm, pgtable_gpa);
|
||||
pte = pgtable[PXD_INDEX(TEST_MEM_BASE_GVA, level)];
|
||||
pgtable_gpa = PTE_GET_PA(pte);
|
||||
}
|
||||
|
||||
/* Initialize the thread sending SIGUSR and install the handler */
|
||||
vcpu_sigusr_ignore();
|
||||
r = pthread_create(&sigusr_thread, NULL, sigusr_thread_fn,
|
||||
(void *)pthread_self());
|
||||
TEST_ASSERT(!r, "pthread_create() failed: %d", r);
|
||||
|
||||
for (i = 1; i <= NR_ITERATIONS; i++) {
|
||||
/*
|
||||
* Only handle SIGUSR while the vCPU is running, otherwise
|
||||
* ignore it to avoid interrupting other ioctls/syscalls.
|
||||
*/
|
||||
vcpu_sigusr_listen();
|
||||
r = __vcpu_run(vcpu);
|
||||
TEST_ASSERT(!r || errno == EINTR, "Expected success or SIGUSR1");
|
||||
vcpu_sigusr_ignore();
|
||||
|
||||
/* The guest only exits due to a signal or failed assertion */
|
||||
if (!r) {
|
||||
TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
|
||||
TEST_ASSERT_EQ(get_ucall(vcpu, &uc), UCALL_ABORT);
|
||||
REPORT_GUEST_ASSERT(uc);
|
||||
break;
|
||||
}
|
||||
|
||||
state = vcpu_save_state(vcpu);
|
||||
|
||||
/*
|
||||
* If the vCPU is in guest mode, inject a #UD to trigger an
|
||||
* L2->L1 VM-Exit every other iteration.
|
||||
*/
|
||||
if (kvm_x86_state_is_guest_mode(state) && i % 2 == 0)
|
||||
kvm_x86_state_queue_ud(state);
|
||||
|
||||
kvm_vm_release(vm);
|
||||
vcpu = vm_recreate_with_one_vcpu(vm);
|
||||
if (nested)
|
||||
vm_enable_cap(vm, KVM_CAP_EXCEPTION_PAYLOAD, -2ul);
|
||||
vcpu_load_state(vcpu, state);
|
||||
kvm_x86_state_cleanup(state);
|
||||
|
||||
pr_info("\rSave+restore iterations: %d", i);
|
||||
}
|
||||
pr_info("\n");
|
||||
|
||||
sync_global_from_guest(vm, guest_faults);
|
||||
TEST_ASSERT(guest_faults, "No guest page faults triggered");
|
||||
pr_info("Guest page faults%s: %lu\n", nested ? " (in L2)" : "", guest_faults);
|
||||
|
||||
pthread_cancel(sigusr_thread);
|
||||
pthread_join(sigusr_thread, NULL);
|
||||
kvm_vm_free(vm);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
pr_info("Running save+restore stress test...\n");
|
||||
run_test(/*nested=*/false);
|
||||
|
||||
if (!kvm_has_cap(KVM_CAP_EXCEPTION_PAYLOAD) ||
|
||||
!kvm_has_cap(KVM_CAP_NESTED_STATE) ||
|
||||
(!kvm_cpu_has(X86_FEATURE_SVM) && !kvm_cpu_has(X86_FEATURE_VMX))) {
|
||||
pr_info("Nested virtualization not supported, skipping nested test\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
pr_info("Running save+restore stress test with a nested guest...\n");
|
||||
run_test(/*nested=*/true);
|
||||
return 0;
|
||||
}
|
||||
@@ -77,10 +77,6 @@ void test_vm_types(void)
|
||||
{
|
||||
test_init2(KVM_X86_SEV_VM, &(struct kvm_sev_init){});
|
||||
|
||||
/*
|
||||
* TODO: check that unsupported types cannot be created. Probably
|
||||
* a separate selftest.
|
||||
*/
|
||||
if (have_sev_es)
|
||||
test_init2(KVM_X86_SEV_ES_VM, &(struct kvm_sev_init){});
|
||||
|
||||
|
||||
@@ -22,9 +22,6 @@
|
||||
#define SMRAM_GPA 0x1000000
|
||||
#define SMRAM_STAGE 0xfe
|
||||
|
||||
#define STR(x) #x
|
||||
#define XSTR(s) STR(s)
|
||||
|
||||
#define SYNC_PORT 0xe
|
||||
#define DONE 0xff
|
||||
|
||||
@@ -42,7 +39,7 @@ u8 smi_handler[] = {
|
||||
|
||||
static inline void sync_with_host(u64 phase)
|
||||
{
|
||||
asm volatile("in $" XSTR(SYNC_PORT)", %%al \n"
|
||||
asm volatile("in $" __stringify(SYNC_PORT)", %%al \n"
|
||||
: "+a" (phase));
|
||||
}
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ int main(int argc, char *argv[])
|
||||
ARBITRARY_IO_PORT, run->io.port);
|
||||
|
||||
/*
|
||||
* Stuff invalid guest state for L2 by making TR unusuable. The next
|
||||
* Stuff invalid guest state for L2 by making TR unusable. The next
|
||||
* KVM_RUN should induce a TRIPLE_FAULT in L2 as KVM doesn't support
|
||||
* emulating invalid guest state for L2.
|
||||
*/
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
* amongst the available numa nodes on the machine.
|
||||
*
|
||||
* Migration is a command line option. When used on non-numa machines will
|
||||
* exit with error. Test is still usefull on non-numa for testing IPIs.
|
||||
* exit with error. Test is still useful on non-numa for testing IPIs.
|
||||
*/
|
||||
#include <getopt.h>
|
||||
#include <pthread.h>
|
||||
|
||||
Reference in New Issue
Block a user