From patchwork Mon Oct 26 09:56:09 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrey Smetanin X-Patchwork-Id: 7486761 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.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 8537E9F37F for ; Mon, 26 Oct 2015 09:56:56 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 494732065D for ; Mon, 26 Oct 2015 09:56:55 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id E67BE207D4 for ; Mon, 26 Oct 2015 09:56:53 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753805AbbJZJ4u (ORCPT ); Mon, 26 Oct 2015 05:56:50 -0400 Received: from mailhub.sw.ru ([195.214.232.25]:7312 "EHLO relay.sw.ru" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753799AbbJZJ4t (ORCPT ); Mon, 26 Oct 2015 05:56:49 -0400 Received: from asm-pc.sw.ru ([10.30.16.30]) by relay.sw.ru (8.13.4/8.13.4) with ESMTP id t9Q9uOak024820; Mon, 26 Oct 2015 12:56:25 +0300 (MSK) From: Andrey Smetanin To: kvm@vger.kernel.org Cc: "Denis V. Lunev" , Vitaly Kuznetsov , "K. Y. Srinivasan" , Gleb Natapov , Paolo Bonzini , Roman Kagan , qemu-devel@nongnu.org, virtualization@lists.linux-foundation.org Subject: [kvm-unit-tests PATCH] x86: hyperv_synic: Hyper-V SynIC test Date: Mon, 26 Oct 2015 12:56:09 +0300 Message-Id: <1445853369-24435-1-git-send-email-asmetanin@virtuozzo.com> X-Mailer: git-send-email 2.4.3 Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable 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 Hyper-V SynIC is a Hyper-V synthetic interrupt controller. The test runs on every vCPU and performs the following steps: * read from all Hyper-V SynIC MSR's * setup Hyper-V SynIC evt/msg pages * setup SINT's routing * inject SINT's into destination vCPU by 'hyperv-synic-test-device' * wait for SINT's isr's completion * clear Hyper-V SynIC evt/msg pages and destroy SINT's routing Signed-off-by: Andrey Smetanin Reviewed-by: Roman Kagan Signed-off-by: Denis V. Lunev CC: Vitaly Kuznetsov CC: "K. Y. Srinivasan" CC: Gleb Natapov CC: Paolo Bonzini CC: Roman Kagan CC: Denis V. Lunev CC: qemu-devel@nongnu.org CC: virtualization@lists.linux-foundation.org --- config/config-x86-common.mak | 5 +- lib/x86/msr.h | 23 +++++ x86/hyperv_synic.c | 229 +++++++++++++++++++++++++++++++++++++++++++ x86/run | 10 +- x86/unittests.cfg | 5 + 5 files changed, 270 insertions(+), 2 deletions(-) create mode 100644 x86/hyperv_synic.c diff --git a/config/config-x86-common.mak b/config/config-x86-common.mak index c2f9908..dc80eaa 100644 --- a/config/config-x86-common.mak +++ b/config/config-x86-common.mak @@ -36,7 +36,8 @@ tests-common = $(TEST_DIR)/vmexit.flat $(TEST_DIR)/tsc.flat \ $(TEST_DIR)/kvmclock_test.flat $(TEST_DIR)/eventinj.flat \ $(TEST_DIR)/s3.flat $(TEST_DIR)/pmu.flat \ $(TEST_DIR)/tsc_adjust.flat $(TEST_DIR)/asyncpf.flat \ - $(TEST_DIR)/init.flat $(TEST_DIR)/smap.flat + $(TEST_DIR)/init.flat $(TEST_DIR)/smap.flat \ + $(TEST_DIR)/hyperv_synic.flat ifdef API tests-common += api/api-sample @@ -108,6 +109,8 @@ $(TEST_DIR)/vmx.elf: $(cstart.o) $(TEST_DIR)/vmx.o $(TEST_DIR)/vmx_tests.o $(TEST_DIR)/debug.elf: $(cstart.o) $(TEST_DIR)/debug.o +$(TEST_DIR)/hyperv_synic.elf: $(cstart.o) $(TEST_DIR)/hyperv_synic.o + arch_clean: $(RM) $(TEST_DIR)/*.o $(TEST_DIR)/*.flat $(TEST_DIR)/*.elf \ $(TEST_DIR)/.*.d lib/x86/.*.d diff --git a/lib/x86/msr.h b/lib/x86/msr.h index 281255a..54da420 100644 --- a/lib/x86/msr.h +++ b/lib/x86/msr.h @@ -408,4 +408,27 @@ #define MSR_VM_IGNNE 0xc0010115 #define MSR_VM_HSAVE_PA 0xc0010117 +/* Define synthetic interrupt controller model specific registers. */ +#define HV_X64_MSR_SCONTROL 0x40000080 +#define HV_X64_MSR_SVERSION 0x40000081 +#define HV_X64_MSR_SIEFP 0x40000082 +#define HV_X64_MSR_SIMP 0x40000083 +#define HV_X64_MSR_EOM 0x40000084 +#define HV_X64_MSR_SINT0 0x40000090 +#define HV_X64_MSR_SINT1 0x40000091 +#define HV_X64_MSR_SINT2 0x40000092 +#define HV_X64_MSR_SINT3 0x40000093 +#define HV_X64_MSR_SINT4 0x40000094 +#define HV_X64_MSR_SINT5 0x40000095 +#define HV_X64_MSR_SINT6 0x40000096 +#define HV_X64_MSR_SINT7 0x40000097 +#define HV_X64_MSR_SINT8 0x40000098 +#define HV_X64_MSR_SINT9 0x40000099 +#define HV_X64_MSR_SINT10 0x4000009A +#define HV_X64_MSR_SINT11 0x4000009B +#define HV_X64_MSR_SINT12 0x4000009C +#define HV_X64_MSR_SINT13 0x4000009D +#define HV_X64_MSR_SINT14 0x4000009E +#define HV_X64_MSR_SINT15 0x4000009F + #endif /* _ASM_X86_MSR_INDEX_H */ diff --git a/x86/hyperv_synic.c b/x86/hyperv_synic.c new file mode 100644 index 0000000..5c5a43a --- /dev/null +++ b/x86/hyperv_synic.c @@ -0,0 +1,229 @@ +#include "libcflat.h" +#include "processor.h" +#include "msr.h" +#include "isr.h" +#include "vm.h" +#include "apic.h" +#include "desc.h" +#include "io.h" +#include "smp.h" +#include "atomic.h" + +#define MAX_CPUS 4 +#define HYPERV_CPUID_FEATURES 0x40000003 +#define HV_X64_MSR_SYNIC_AVAILABLE (1 << 2) +#define HV_SYNIC_CONTROL_ENABLE (1ULL << 0) +#define HV_SYNIC_SIMP_ENABLE (1ULL << 0) +#define HV_SYNIC_SIEFP_ENABLE (1ULL << 0) +#define HV_SYNIC_SINT_MASKED (1ULL << 16) +#define HV_SYNIC_SINT_AUTO_EOI (1ULL << 17) +#define HV_SYNIC_SINT_VECTOR_MASK (0xFF) +#define HV_SYNIC_SINT_COUNT 16 + +enum { + HV_TEST_DEV_SINT_ROUTE_CREATE = 1, + HV_TEST_DEV_SINT_ROUTE_DESTROY, + HV_TEST_DEV_SINT_ROUTE_SET_SINT +}; + +static atomic_t isr_enter_count[MAX_CPUS]; +static atomic_t cpus_comp_count; + +static bool synic_supported(void) +{ + return cpuid(HYPERV_CPUID_FEATURES).a & HV_X64_MSR_SYNIC_AVAILABLE; +} + +static void synic_sint_auto_eoi_isr(isr_regs_t *regs) +{ + atomic_inc(&isr_enter_count[smp_id()]); +} + +static void synic_sint_isr(isr_regs_t *regs) +{ + atomic_inc(&isr_enter_count[smp_id()]); + eoi(); +} + +static void synic_ctl(u8 ctl, u8 vcpu_id, u8 sint) +{ + outl((ctl << 16)|((vcpu_id) << 8)|sint, 0x3000); +} + +struct sint_vec_entry { + int vec; + bool auto_eoi; +}; + +struct sint_vec_entry sint_vecs[HV_SYNIC_SINT_COUNT] = { + {0xB0, false}, + {0xB1, false}, + {0xB2, false}, + {0xB3, true}, + {0xB4, false}, + {0xB5, false}, + {0xB6, false}, + {0xB7, false}, + {0xB8, true}, + {0xB9, false}, + {0xBA, true}, + {0xBB, false}, + {0xBC, false}, + {0xBD, false}, + {0xBE, true}, + {0xBF, false}, +}; + +static void synic_prepare_sint_vecs(void) +{ + bool auto_eoi; + int i, vec; + + for (i = 0; i < HV_SYNIC_SINT_COUNT; i++) { + vec = sint_vecs[i].vec; + auto_eoi = sint_vecs[i].auto_eoi; + handle_irq(vec, (auto_eoi) ? synic_sint_auto_eoi_isr : synic_sint_isr); + } +} + +static void synic_sints_prepare(u8 vcpu) +{ + bool auto_eoi; + int i, vec; + + for (i = 0; i < HV_SYNIC_SINT_COUNT; i++) { + vec = sint_vecs[i].vec; + auto_eoi = sint_vecs[i].auto_eoi; + wrmsr(HV_X64_MSR_SINT0 + i, + (u64)vec | ((auto_eoi) ? HV_SYNIC_SINT_AUTO_EOI : 0)); + synic_ctl(HV_TEST_DEV_SINT_ROUTE_CREATE, vcpu, i); + } +} + +static void synic_test_prepare(void *ctx) +{ + u64 r; + int i = 0; + + write_cr3((ulong)ctx); + irq_enable(); + + rdmsr(HV_X64_MSR_SVERSION); + rdmsr(HV_X64_MSR_SIMP); + rdmsr(HV_X64_MSR_SIEFP); + rdmsr(HV_X64_MSR_SCONTROL); + for (i = 0; i < HV_SYNIC_SINT_COUNT; i++) { + rdmsr(HV_X64_MSR_SINT0 + i); + } + r = rdmsr(HV_X64_MSR_EOM); + if (r != 0) { + report("Hyper-V SynIC test, EOM read 0x%llx", false, r); + goto ret; + } + + wrmsr(HV_X64_MSR_SIMP, (u64)virt_to_phys(alloc_page()) | + HV_SYNIC_SIMP_ENABLE); + wrmsr(HV_X64_MSR_SIEFP, (u64)virt_to_phys(alloc_page())| + HV_SYNIC_SIEFP_ENABLE); + wrmsr(HV_X64_MSR_SCONTROL, HV_SYNIC_CONTROL_ENABLE); + + synic_sints_prepare(smp_id()); +ret: + atomic_inc(&cpus_comp_count); +} + +static void synic_sints_test(u8 dst_vcpu) +{ + int i; + + atomic_set(&isr_enter_count[dst_vcpu], 0); + for (i = 0; i < HV_SYNIC_SINT_COUNT; i++) { + synic_ctl(HV_TEST_DEV_SINT_ROUTE_SET_SINT, dst_vcpu, i); + } + + while (atomic_read(&isr_enter_count[dst_vcpu]) != HV_SYNIC_SINT_COUNT) { + pause(); + } +} + +static void synic_test(void *ctx) +{ + u8 dst_vcpu = (ulong)ctx; + + irq_enable(); + synic_sints_test(dst_vcpu); + atomic_inc(&cpus_comp_count); +} + +static void synic_test_cleanup(void *ctx) +{ + u8 vcpu = smp_id(); + int i; + + irq_enable(); + for (i = 0; i < HV_SYNIC_SINT_COUNT; i++) { + synic_ctl(HV_TEST_DEV_SINT_ROUTE_DESTROY, vcpu, i); + wrmsr(HV_X64_MSR_SINT0 + i, 0xFF|HV_SYNIC_SINT_MASKED); + } + + wrmsr(HV_X64_MSR_SCONTROL, 0); + wrmsr(HV_X64_MSR_SIMP, 0); + wrmsr(HV_X64_MSR_SIEFP, 0); + atomic_inc(&cpus_comp_count); +} + +int main(int ac, char **av) +{ + + if (synic_supported()) { + int ncpus, i; + + setup_vm(); + smp_init(); + setup_idt(); + enable_apic(); + + synic_prepare_sint_vecs(); + + ncpus = cpu_count(); + if (ncpus > MAX_CPUS) { + ncpus = MAX_CPUS; + } + printf("ncpus = %d\n", ncpus); + + atomic_set(&cpus_comp_count, 0); + for (i = 0; i < ncpus; i++) { + on_cpu_async(i, synic_test_prepare, (void *)read_cr3()); + } + while (atomic_read(&cpus_comp_count) != ncpus) { + pause(); + } + + atomic_set(&cpus_comp_count, 0); + for (i = 0; i < ncpus; i++) { + on_cpu_async(i, synic_test, (void *)(ulong)(ncpus - 1 - i)); + } + while (atomic_read(&cpus_comp_count) != ncpus) { + pause(); + } + + atomic_set(&cpus_comp_count, 0); + for (i = 0; i < ncpus; i++) { + on_cpu_async(i, synic_test_cleanup, NULL); + } + while (atomic_read(&cpus_comp_count) != ncpus) { + pause(); + } + + for (i = 0; i < ncpus; ++i) { + printf("isr_enter_count[%d] = %d\n", + i, atomic_read(&isr_enter_count[i])); + } + + report("Hyper-V SynIC test", true); + } else { + report("Hyper-V SynIC is not supported", true); + } + + return report_summary(); +} diff --git a/x86/run b/x86/run index b4a35b2..e50ce5f 100755 --- a/x86/run +++ b/x86/run @@ -42,7 +42,15 @@ else pc_testdev="-device testdev,chardev=testlog -chardev file,id=testlog,path=msr.out" fi -command="${qemu} -enable-kvm $pc_testdev -vnc none -serial stdio $pci_testdev -kernel" +if + ${qemu} -device '?' 2>&1 | grep -F "hyperv-testdev" > /dev/null; +then + hyperv_testdev="-device hyperv-testdev" +else + hyperv_testdev="" +fi + +command="${qemu} -enable-kvm $pc_testdev -vnc none -serial stdio $pci_testdev $hyperv_testdev -kernel" echo ${command} "$@" if [ "$DRYRUN" != "yes" ]; then diff --git a/x86/unittests.cfg b/x86/unittests.cfg index a38544f..6b19f42 100644 --- a/x86/unittests.cfg +++ b/x86/unittests.cfg @@ -166,3 +166,8 @@ arch = x86_64 [debug] file = debug.flat arch = x86_64 + +[hyperv_synic] +file = hyperv_synic.flat +smp = 2 +extra_params = -cpu host,hv_synic