From patchwork Fri Sep 6 02:11:46 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Arthur Chunqi Li X-Patchwork-Id: 2854342 Return-Path: X-Original-To: patchwork-kvm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id B70229F495 for ; Fri, 6 Sep 2013 02:12:59 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 755AB202E6 for ; Fri, 6 Sep 2013 02:12:58 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 5DC03202C8 for ; Fri, 6 Sep 2013 02:12:57 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754006Ab3IFCMx (ORCPT ); Thu, 5 Sep 2013 22:12:53 -0400 Received: from mail-pb0-f50.google.com ([209.85.160.50]:57206 "EHLO mail-pb0-f50.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753240Ab3IFCMw (ORCPT ); Thu, 5 Sep 2013 22:12:52 -0400 Received: by mail-pb0-f50.google.com with SMTP id uo5so2563727pbc.23 for ; Thu, 05 Sep 2013 19:12:52 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id; bh=FVfU9KugUme3gn681nD3U2pS/bEa7OpbVf/ZZyRRmc0=; b=gTSNnNLdVf/oLv9TrddPfAV0im9jjx97r9ySJOsJpw9q7dcSJHgAE9FNwEVaF89eRu tDY2lrfTlispM4qluekwha/U7/buJOMQb0cpCzy5dgVvNu58cETPSUnr9SMGdWYWHiar sWii7X8sBp1VH9oUOK0lJHaxg5TdcPmU+VKP6mUsKWMQjLUf2scDpNewIt9IXC7L6dO3 cJ7xe7MS9qQT2t4jxRwK0yVhX65aK1jeAQgfIv0oapgcuDSu0o+qh5GciUilZIlUKBnt 97dzKbGnl2Noj8wXS5K+TYidNIVJpT6zbFDAQqzRYMEom/4OLLWiT/2cPsj4GeWEXHvV drJA== X-Received: by 10.66.170.138 with SMTP id am10mr1102799pac.51.1378433572299; Thu, 05 Sep 2013 19:12:52 -0700 (PDT) Received: from Blade1-01.Blade1-01 ([162.105.146.101]) by mx.google.com with ESMTPSA id nv6sm270912pbc.6.1969.12.31.16.00.00 (version=TLSv1.1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Thu, 05 Sep 2013 19:12:51 -0700 (PDT) From: Arthur Chunqi Li To: kvm@vger.kernel.org Cc: jan.kiszka@web.de, gleb@redhat.com, pbonzini@redhat.com, Arthur Chunqi Li Subject: [PATCH v2] kvm-unit-tests: VMX: Test suite for preemption timer Date: Fri, 6 Sep 2013 10:11:46 +0800 Message-Id: <1378433506-18401-1-git-send-email-yzt356@gmail.com> X-Mailer: git-send-email 1.7.9.5 Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org X-Spam-Status: No, score=-9.2 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, T_DKIM_INVALID, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Test cases for preemption timer in nested VMX. Two aspects are tested: 1. Save preemption timer on VMEXIT if relevant bit set in EXIT_CONTROL 2. Test a relevant bug of KVM. The bug will not save preemption timer value if exit L2->L0 for some reason and enter L0->L2. Thus preemption timer will never trigger if the value is large enough. 3. Some other aspects are tested, e.g. preempt without save, preempt when value is 0. Signed-off-by: Arthur Chunqi Li --- ChangeLog to v1: 1. Add test of EXI_SAVE_PREEMPT enable and PIN_PREEMPT disable 2. Add test of PIN_PREEMPT enable and EXI_SAVE_PREEMPT enable/disable 3. Add test of preemption value is 0 x86/vmx.h | 3 + x86/vmx_tests.c | 175 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 178 insertions(+) diff --git a/x86/vmx.h b/x86/vmx.h index 28595d8..ebc8cfd 100644 --- a/x86/vmx.h +++ b/x86/vmx.h @@ -210,6 +210,7 @@ enum Encoding { GUEST_ACTV_STATE = 0x4826ul, GUEST_SMBASE = 0x4828ul, GUEST_SYSENTER_CS = 0x482aul, + PREEMPT_TIMER_VALUE = 0x482eul, /* 32-Bit Host State Fields */ HOST_SYSENTER_CS = 0x4c00ul, @@ -331,6 +332,7 @@ enum Ctrl_exi { EXI_LOAD_PERF = 1UL << 12, EXI_INTA = 1UL << 15, EXI_LOAD_EFER = 1UL << 21, + EXI_SAVE_PREEMPT = 1UL << 22, }; enum Ctrl_ent { @@ -342,6 +344,7 @@ enum Ctrl_pin { PIN_EXTINT = 1ul << 0, PIN_NMI = 1ul << 3, PIN_VIRT_NMI = 1ul << 5, + PIN_PREEMPT = 1ul << 6, }; enum Ctrl0 { diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c index c1b39f4..2e32031 100644 --- a/x86/vmx_tests.c +++ b/x86/vmx_tests.c @@ -1,4 +1,30 @@ #include "vmx.h" +#include "msr.h" +#include "processor.h" + +volatile u32 stage; + +static inline void vmcall() +{ + asm volatile("vmcall"); +} + +static inline void set_stage(u32 s) +{ + barrier(); + stage = s; + barrier(); +} + +static inline u32 get_stage() +{ + u32 s; + + barrier(); + s = stage; + barrier(); + return s; +} void basic_init() { @@ -76,6 +102,153 @@ int vmenter_exit_handler() return VMX_TEST_VMEXIT; } +u32 preempt_scale; +volatile unsigned long long tsc_val; +volatile u32 preempt_val; + +void preemption_timer_init() +{ + u32 ctrl_exit; + + // Enable EXI_SAVE_PREEMPT with PIN_PREEMPT dieabled + ctrl_exit = (vmcs_read(EXI_CONTROLS) | + EXI_SAVE_PREEMPT) & ctrl_exit_rev.clr; + vmcs_write(EXI_CONTROLS, ctrl_exit); + preempt_val = 10000000; + vmcs_write(PREEMPT_TIMER_VALUE, preempt_val); + set_stage(0); + preempt_scale = rdmsr(MSR_IA32_VMX_MISC) & 0x1F; +} + +void preemption_timer_main() +{ + int i, j; + + if (!(ctrl_pin_rev.clr & PIN_PREEMPT)) { + printf("\tPreemption timer is not supported\n"); + return; + } + if (!(ctrl_exit_rev.clr & EXI_SAVE_PREEMPT)) + printf("\tSave preemption value is not supported\n"); + else { + // Test EXI_SAVE_PREEMPT enable and PIN_PREEMPT disable + set_stage(0); + // Consume enough time to let L2->L0->L2 occurs + for(i = 0; i < 100000; i++) + for (j = 0; j < 10000; j++); + vmcall(); + // Test PIN_PREEMPT enable and EXI_SAVE_PREEMPT enable/disable + set_stage(1); + vmcall(); + // Test both enable + if (get_stage() == 2) + vmcall(); + } + // Test the bug of reseting preempt value when L2->L0->L2 + set_stage(3); + vmcall(); + tsc_val = rdtsc(); + while (1) { + if (((rdtsc() - tsc_val) >> preempt_scale) + > 10 * preempt_val) { + report("Preemption timer timeout", 0); + break; + } + if (get_stage() == 4) + break; + } + // Test preempt val is 0 + set_stage(4); + report("Preemption timer, val=0", 0); +} + +int preemption_timer_exit_handler() +{ + u64 guest_rip; + ulong reason; + u32 insn_len; + u32 ctrl_exit; + u32 ctrl_pin; + + guest_rip = vmcs_read(GUEST_RIP); + reason = vmcs_read(EXI_REASON) & 0xff; + insn_len = vmcs_read(EXI_INST_LEN); + switch (reason) { + case VMX_PREEMPT: + switch (get_stage()) { + case 3: + if (((rdtsc() - tsc_val) >> preempt_scale) < preempt_val) + report("Preemption timer timeout", 0); + else + report("Preemption timer timeout", 1); + set_stage(get_stage() + 1); + break; + case 4: + if (vmcs_read(PREEMPT_TIMER_VALUE) == 0) + report("Preemption timer, val=0", 1); + else + report("Preemption timer, val=0", 0); + return VMX_TEST_VMEXIT; + } + return VMX_TEST_RESUME; + case VMX_VMCALL: + switch (get_stage()) { + case 0: + if (vmcs_read(PREEMPT_TIMER_VALUE) != preempt_val) + report("Preemption timer off", 0); + else + report("Preemption timer off", 1); + // Enable PIN_PREEMPT and disable EXI_SAVE_PREEMPT + ctrl_pin = (vmcs_read(PIN_CONTROLS) | PIN_PREEMPT) & + ctrl_pin_rev.clr; + vmcs_write(PIN_CONTROLS, ctrl_pin); + ctrl_exit = (vmcs_read(EXI_CONTROLS) & + ~EXI_SAVE_PREEMPT) & ctrl_exit_rev.clr; + vmcs_write(EXI_CONTROLS, ctrl_exit); + vmcs_write(PREEMPT_TIMER_VALUE, preempt_val); + break; + case 1: + if (vmcs_read(PREEMPT_TIMER_VALUE) != preempt_val) + report("Save preemption value", 0); + else { + set_stage(get_stage() + 1); + // Enable EXI_SAVE_PREEMPT with PIN_PREEMPT + // already enabled + ctrl_exit = (vmcs_read(EXI_CONTROLS) | + EXI_SAVE_PREEMPT) & ctrl_exit_rev.clr; + vmcs_write(EXI_CONTROLS, ctrl_exit); + } + vmcs_write(PREEMPT_TIMER_VALUE, preempt_val); + break; + case 2: + if (vmcs_read(PREEMPT_TIMER_VALUE) >= preempt_val) + report("Save preemption value", 0); + else + report("Save preemption value", 1); + break; + case 3: + vmcs_write(PREEMPT_TIMER_VALUE, preempt_val); + ctrl_pin = (vmcs_read(PIN_CONTROLS) | PIN_PREEMPT) & + ctrl_pin_rev.clr; + vmcs_write(PIN_CONTROLS, ctrl_pin); + ctrl_exit = (vmcs_read(EXI_CONTROLS) | + EXI_SAVE_PREEMPT) & ctrl_exit_rev.clr; + vmcs_write(EXI_CONTROLS, ctrl_exit); + break; + default: + printf("Invalid stage.\n"); + 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 +256,7 @@ struct vmx_test vmx_tests[] = { basic_syscall_handler, {0} }, { "vmenter", basic_init, vmenter_main, vmenter_exit_handler, basic_syscall_handler, {0} }, + { "preemption timer", preemption_timer_init, preemption_timer_main, + preemption_timer_exit_handler, basic_syscall_handler, {0} }, { NULL, NULL, NULL, NULL, NULL, {0} }, };