lkml.org 
[lkml]   [2018]   [Nov]   [13]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[kvm-unit-tests PATCH] x86/hyper-v: stimer_direct test
    Date
    Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com>
    ---
    x86/Makefile.common | 3 +
    x86/hyperv.h | 7 ++
    x86/hyperv_stimer_direct.c | 250 +++++++++++++++++++++++++++++++++++++
    x86/unittests.cfg | 6 +
    4 files changed, 266 insertions(+)
    create mode 100644 x86/hyperv_stimer_direct.c

    diff --git a/x86/Makefile.common b/x86/Makefile.common
    index e612dbe..115189a 100644
    --- a/x86/Makefile.common
    +++ b/x86/Makefile.common
    @@ -57,6 +57,7 @@ tests-common = $(TEST_DIR)/vmexit.flat $(TEST_DIR)/tsc.flat \
    $(TEST_DIR)/tsc_adjust.flat $(TEST_DIR)/asyncpf.flat \
    $(TEST_DIR)/init.flat $(TEST_DIR)/smap.flat \
    $(TEST_DIR)/hyperv_synic.flat $(TEST_DIR)/hyperv_stimer.flat \
    + $(TEST_DIR)/hyperv_stimer_direct.flat \
    $(TEST_DIR)/hyperv_connections.flat \
    $(TEST_DIR)/umip.flat

    @@ -81,6 +82,8 @@ $(TEST_DIR)/hyperv_synic.elf: $(TEST_DIR)/hyperv.o

    $(TEST_DIR)/hyperv_stimer.elf: $(TEST_DIR)/hyperv.o

    +$(TEST_DIR)/hyperv_stimer_direct.elf: $(TEST_DIR)/hyperv.o
    +
    $(TEST_DIR)/hyperv_connections.elf: $(TEST_DIR)/hyperv.o

    arch_clean:
    diff --git a/x86/hyperv.h b/x86/hyperv.h
    index 9a83da4..718744c 100644
    --- a/x86/hyperv.h
    +++ b/x86/hyperv.h
    @@ -10,6 +10,8 @@
    #define HV_X64_MSR_SYNIC_AVAILABLE (1 << 2)
    #define HV_X64_MSR_SYNTIMER_AVAILABLE (1 << 3)

    +#define HV_STIMER_DIRECT_MODE_AVAILABLE (1 << 19)
    +
    #define HV_X64_MSR_GUEST_OS_ID 0x40000000
    #define HV_X64_MSR_HYPERCALL 0x40000001

    @@ -205,6 +207,11 @@ static inline bool stimer_supported(void)
    return cpuid(HYPERV_CPUID_FEATURES).a & HV_X64_MSR_SYNIC_AVAILABLE;
    }

    +static inline bool stimer_direct_supported(void)
    +{
    + return cpuid(HYPERV_CPUID_FEATURES).d & HV_STIMER_DIRECT_MODE_AVAILABLE;
    +}
    +
    static inline bool hv_time_ref_counter_supported(void)
    {
    return cpuid(HYPERV_CPUID_FEATURES).a & HV_X64_MSR_TIME_REF_COUNT_AVAILABLE;
    diff --git a/x86/hyperv_stimer_direct.c b/x86/hyperv_stimer_direct.c
    new file mode 100644
    index 0000000..e93f633
    --- /dev/null
    +++ b/x86/hyperv_stimer_direct.c
    @@ -0,0 +1,250 @@
    +#include "libcflat.h"
    +#include "processor.h"
    +#include "msr.h"
    +#include "isr.h"
    +#include "vm.h"
    +#include "apic.h"
    +#include "desc.h"
    +#include "smp.h"
    +#include "atomic.h"
    +#include "hyperv.h"
    +#include "asm/barrier.h"
    +#include "alloc_page.h"
    +
    +#define MAX_CPUS 4
    +
    +#define INT1_VEC 0xF1
    +#define INT2_VEC 0xF2
    +
    +#define ONE_MS_IN_100NS 10000
    +
    +struct stimer {
    + int sint;
    + int index;
    + atomic_t fire_count;
    +};
    +
    +struct svcpu {
    + int vcpu;
    + struct stimer timer[HV_SYNIC_STIMER_COUNT];
    +};
    +
    +static struct svcpu g_synic_vcpu[MAX_CPUS];
    +
    +static void stimer_init(struct stimer *timer, int index)
    +{
    + memset(timer, 0, sizeof(*timer));
    + timer->index = index;
    +}
    +
    +static void synic_enable(void)
    +{
    + int vcpu = smp_id(), i;
    + struct svcpu *svcpu = &g_synic_vcpu[vcpu];
    +
    + memset(svcpu, 0, sizeof(*svcpu));
    + svcpu->vcpu = vcpu;
    + for (i = 0; i < ARRAY_SIZE(svcpu->timer); i++) {
    + stimer_init(&svcpu->timer[i], i);
    + }
    +}
    +
    +static void stimer_shutdown(struct stimer *timer)
    +{
    + wrmsr(HV_X64_MSR_STIMER0_CONFIG + 2*timer->index, 0);
    +}
    +
    +static void process_stimer_expired(struct stimer *timer)
    +{
    + atomic_inc(&timer->fire_count);
    +}
    +
    +static void __stimer_isr_direct(int vcpu, int timer_index)
    +{
    + struct svcpu *svcpu = &g_synic_vcpu[vcpu];
    + struct stimer *timer = &svcpu->timer[timer_index];
    +
    + process_stimer_expired(timer);
    +}
    +
    +static void stimer_isr_direct1(isr_regs_t *regs)
    +{
    + int vcpu = smp_id();
    +
    + __stimer_isr_direct(vcpu, 0);
    +
    + eoi();
    +}
    +
    +static void stimer_isr_direct2(isr_regs_t *regs)
    +{
    + int vcpu = smp_id();
    +
    + __stimer_isr_direct(vcpu, 1);
    +
    + eoi();
    +}
    +
    +static void stimer_start(struct stimer *timer,
    + bool auto_enable, bool periodic,
    + u64 tick_100ns, int apic_vector)
    +{
    + u64 count;
    + union hv_stimer_config config = {.as_uint64 = 0};
    +
    + timer->sint = 0;
    + atomic_set(&timer->fire_count, 0);
    +
    + config.periodic = periodic;
    + config.enable = 1;
    + config.auto_enable = auto_enable;
    + config.direct_mode = 1;
    + config.sintx = 0;
    + config.apic_vector = apic_vector;
    +
    + if (periodic) {
    + count = tick_100ns;
    + } else {
    + count = rdmsr(HV_X64_MSR_TIME_REF_COUNT) + tick_100ns;
    + }
    +
    + if (!auto_enable) {
    + wrmsr(HV_X64_MSR_STIMER0_COUNT + timer->index*2, count);
    + wrmsr(HV_X64_MSR_STIMER0_CONFIG + timer->index*2, config.as_uint64);
    + } else {
    + wrmsr(HV_X64_MSR_STIMER0_CONFIG + timer->index*2, config.as_uint64);
    + wrmsr(HV_X64_MSR_STIMER0_COUNT + timer->index*2, count);
    + }
    +}
    +
    +static void stimers_shutdown(void)
    +{
    + int vcpu = smp_id(), i;
    + struct svcpu *svcpu = &g_synic_vcpu[vcpu];
    +
    + for (i = 0; i < ARRAY_SIZE(svcpu->timer); i++) {
    + stimer_shutdown(&svcpu->timer[i]);
    + }
    +}
    +
    +static void stimer_test_prepare(void *ctx)
    +{
    + write_cr3((ulong)ctx);
    + synic_enable();
    +}
    +
    +static void stimer_test_periodic(int vcpu, struct stimer *timer1,
    + struct stimer *timer2)
    +{
    + /* Check periodic timers */
    + stimer_start(timer1, false, true, ONE_MS_IN_100NS, INT1_VEC);
    + stimer_start(timer2, false, true, ONE_MS_IN_100NS, INT2_VEC);
    + while ((atomic_read(&timer1->fire_count) < 1000) ||
    + (atomic_read(&timer2->fire_count) < 1000)) {
    + pause();
    + }
    + report("Hyper-V SynIC periodic timers test vcpu %d", true, vcpu);
    + stimer_shutdown(timer1);
    + stimer_shutdown(timer2);
    +}
    +
    +static void stimer_test_one_shot(int vcpu, struct stimer *timer)
    +{
    + /* Check one-shot timer */
    + stimer_start(timer, false, false, ONE_MS_IN_100NS, INT1_VEC);
    + while (atomic_read(&timer->fire_count) < 1) {
    + pause();
    + }
    + report("Hyper-V SynIC one-shot test vcpu %d", true, vcpu);
    + stimer_shutdown(timer);
    +}
    +
    +static void stimer_test_auto_enable_one_shot(int vcpu, struct stimer *timer)
    +{
    + /* Check auto-enable one-shot timer */
    + stimer_start(timer, true, false, ONE_MS_IN_100NS, INT2_VEC);
    + while (atomic_read(&timer->fire_count) < 1) {
    + pause();
    + }
    + report("Hyper-V SynIC auto-enable one-shot timer test vcpu %d", true, vcpu);
    + stimer_shutdown(timer);
    +}
    +
    +static void stimer_test_auto_enable_periodic(int vcpu, struct stimer *timer)
    +{
    + /* Check auto-enable periodic timer */
    + stimer_start(timer, true, true, ONE_MS_IN_100NS, INT1_VEC);
    + while (atomic_read(&timer->fire_count) < 1000) {
    + pause();
    + }
    + report("Hyper-V SynIC auto-enable periodic timer test vcpu %d", true, vcpu);
    + stimer_shutdown(timer);
    +}
    +
    +static void stimer_test(void *ctx)
    +{
    + int vcpu = smp_id();
    + struct svcpu *svcpu = &g_synic_vcpu[vcpu];
    + struct stimer *timer1, *timer2;
    +
    + irq_enable();
    +
    + timer1 = &svcpu->timer[0];
    + timer2 = &svcpu->timer[1];
    +
    + stimer_test_periodic(vcpu, timer1, timer2);
    + stimer_test_one_shot(vcpu, timer1);
    + stimer_test_auto_enable_one_shot(vcpu, timer2);
    + stimer_test_auto_enable_periodic(vcpu, timer1);
    +
    + irq_disable();
    +}
    +
    +static void stimer_test_cleanup(void *ctx)
    +{
    + stimers_shutdown();
    +}
    +
    +static void stimer_test_all(void)
    +{
    + int ncpus;
    +
    + setup_vm();
    + smp_init();
    + enable_apic();
    +
    + ncpus = cpu_count();
    + if (ncpus > MAX_CPUS)
    + report_abort("number cpus exceeds %d", MAX_CPUS);
    + printf("cpus = %d\n", ncpus);
    +
    + handle_irq(INT1_VEC, stimer_isr_direct1);
    + handle_irq(INT2_VEC, stimer_isr_direct2);
    +
    + on_cpus(stimer_test_prepare, (void *)read_cr3());
    + on_cpus(stimer_test, NULL);
    + on_cpus(stimer_test_cleanup, NULL);
    +}
    +
    +int main(int ac, char **av)
    +{
    +
    + if (!stimer_supported()) {
    + report("Hyper-V SynIC timers are not supported", true);
    + goto done;
    + }
    +
    + if (!hv_time_ref_counter_supported()) {
    + report("Hyper-V time reference counter is not supported", true);
    + goto done;
    + }
    +
    + if (!stimer_direct_supported()) {
    + report("Hyper-V direct mode for synthetic timers is not supported", true);
    + goto done;
    + }
    +
    + stimer_test_all();
    +done:
    + return report_summary();
    +}
    diff --git a/x86/unittests.cfg b/x86/unittests.cfg
    index c744a6d..36c0657 100644
    --- a/x86/unittests.cfg
    +++ b/x86/unittests.cfg
    @@ -635,6 +635,12 @@ smp = 2
    extra_params = -cpu kvm64,hv_vpindex,hv_time,hv_synic,hv_stimer -device hyperv-testdev
    groups = hyperv

    +[hyperv_stimer_direct]
    +file = hyperv_stimer_direct.flat
    +smp = 2
    +extra_params = -cpu kvm64,hv_vpindex,hv_time,hv_synic,hv_stimer,hv_stimer_direct -device hyperv-testdev
    +groups = hyperv
    +
    [hyperv_clock]
    file = hyperv_clock.flat
    smp = 2
    --
    2.17.2
    \
     
     \ /
      Last update: 2018-11-13 16:25    [W:5.567 / U:0.232 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site