Commit c35bd19a authored by Evgeny Yakovlev's avatar Evgeny Yakovlev Committed by Eduardo Habkost
Browse files

target-i386: Report hyperv feature words through qom



This change adds hyperv feature words report through qom rpc.

When VM is configured with hyperv features enabled
libvirt will check that required feature words are set
in cpuid leaf 40000003 through qom request.

Currently qemu does not report hyperv feature words
which prevents windows guests from starting with libvirt.

To avoid conflicting with current hyperv properties all added feature
words cannot be set directly with -cpu +feature yet.

Signed-off-by: default avatarEvgeny Yakovlev <eyakovlev@virtuozzo.com>
Signed-off-by: default avatarDenis V. Lunev <den@openvz.org>
CC: Paolo Bonzini <pbonzini@redhat.com>
CC: Richard Henderson <rth@twiddle.net>
CC: Eduardo Habkost <ehabkost@redhat.com>
CC: Marcelo Tosatti <mtosatti@redhat.com>
Reviewed-by: default avatarEduardo Habkost <ehabkost@redhat.com>
Signed-off-by: default avatarEduardo Habkost <ehabkost@redhat.com>
parent d6276d26
Loading
Loading
Loading
Loading
+53 −0
Original line number Diff line number Diff line
@@ -245,6 +245,47 @@ static const char *kvm_feature_name[] = {
    NULL, NULL, NULL, NULL,
};

static const char *hyperv_priv_feature_name[] = {
    NULL /* hv_msr_vp_runtime_access */, NULL /* hv_msr_time_refcount_access */,
    NULL /* hv_msr_synic_access */, NULL /* hv_msr_stimer_access */,
    NULL /* hv_msr_apic_access */, NULL /* hv_msr_hypercall_access */,
    NULL /* hv_vpindex_access */, NULL /* hv_msr_reset_access */,
    NULL /* hv_msr_stats_access */, NULL /* hv_reftsc_access */,
    NULL /* hv_msr_idle_access */, NULL /* hv_msr_frequency_access */,
    NULL, NULL, NULL, NULL,
    NULL, NULL, NULL, NULL,
    NULL, NULL, NULL, NULL,
    NULL, NULL, NULL, NULL,
    NULL, NULL, NULL, NULL,
};

static const char *hyperv_ident_feature_name[] = {
    NULL /* hv_create_partitions */, NULL /* hv_access_partition_id */,
    NULL /* hv_access_memory_pool */, NULL /* hv_adjust_message_buffers */,
    NULL /* hv_post_messages */, NULL /* hv_signal_events */,
    NULL /* hv_create_port */, NULL /* hv_connect_port */,
    NULL /* hv_access_stats */, NULL, NULL, NULL /* hv_debugging */,
    NULL /* hv_cpu_power_management */, NULL /* hv_configure_profiler */,
    NULL, NULL,
    NULL, NULL, NULL, NULL,
    NULL, NULL, NULL, NULL,
    NULL, NULL, NULL, NULL,
    NULL, NULL, NULL, NULL,
};

static const char *hyperv_misc_feature_name[] = {
    NULL /* hv_mwait */, NULL /* hv_guest_debugging */,
    NULL /* hv_perf_monitor */, NULL /* hv_cpu_dynamic_part */,
    NULL /* hv_hypercall_params_xmm */, NULL /* hv_guest_idle_state */,
    NULL, NULL,
    NULL, NULL, NULL /* hv_guest_crash_msr */, NULL,
    NULL, NULL, NULL, NULL,
    NULL, NULL, NULL, NULL,
    NULL, NULL, NULL, NULL,
    NULL, NULL, NULL, NULL,
    NULL, NULL, NULL, NULL,
};

static const char *svm_feature_name[] = {
    "npt", "lbrv", "svm_lock", "nrip_save",
    "tsc_scale", "vmcb_clean",  "flushbyasid", "decodeassists",
@@ -412,6 +453,18 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = {
        .cpuid_eax = KVM_CPUID_FEATURES, .cpuid_reg = R_EAX,
        .tcg_features = TCG_KVM_FEATURES,
    },
    [FEAT_HYPERV_EAX] = {
        .feat_names = hyperv_priv_feature_name,
        .cpuid_eax = 0x40000003, .cpuid_reg = R_EAX,
    },
    [FEAT_HYPERV_EBX] = {
        .feat_names = hyperv_ident_feature_name,
        .cpuid_eax = 0x40000003, .cpuid_reg = R_EBX,
    },
    [FEAT_HYPERV_EDX] = {
        .feat_names = hyperv_misc_feature_name,
        .cpuid_eax = 0x40000003, .cpuid_reg = R_EDX,
    },
    [FEAT_SVM] = {
        .feat_names = svm_feature_name,
        .cpuid_eax = 0x8000000A, .cpuid_reg = R_EDX,
+3 −0
Original line number Diff line number Diff line
@@ -440,6 +440,9 @@ typedef enum FeatureWord {
    FEAT_8000_0007_EDX, /* CPUID[8000_0007].EDX */
    FEAT_C000_0001_EDX, /* CPUID[C000_0001].EDX */
    FEAT_KVM,           /* CPUID[4000_0001].EAX (KVM_CPUID_FEATURES) */
    FEAT_HYPERV_EAX,    /* CPUID[4000_0003].EAX */
    FEAT_HYPERV_EBX,    /* CPUID[4000_0003].EBX */
    FEAT_HYPERV_EDX,    /* CPUID[4000_0003].EDX */
    FEAT_SVM,           /* CPUID[8000_000A].EDX */
    FEAT_XSAVE,         /* CPUID[EAX=0xd,ECX=1].EAX */
    FEAT_6_EAX,         /* CPUID[6].EAX */
+64 −48
Original line number Diff line number Diff line
@@ -576,6 +576,64 @@ static int kvm_arch_set_tsc_khz(CPUState *cs)
    return 0;
}

static int hyperv_handle_properties(CPUState *cs)
{
    X86CPU *cpu = X86_CPU(cs);
    CPUX86State *env = &cpu->env;

    if (cpu->hyperv_relaxed_timing) {
        env->features[FEAT_HYPERV_EAX] |= HV_X64_MSR_HYPERCALL_AVAILABLE;
    }
    if (cpu->hyperv_vapic) {
        env->features[FEAT_HYPERV_EAX] |= HV_X64_MSR_HYPERCALL_AVAILABLE;
        env->features[FEAT_HYPERV_EAX] |= HV_X64_MSR_APIC_ACCESS_AVAILABLE;
        has_msr_hv_vapic = true;
    }
    if (cpu->hyperv_time &&
            kvm_check_extension(cs->kvm_state, KVM_CAP_HYPERV_TIME) > 0) {
        env->features[FEAT_HYPERV_EAX] |= HV_X64_MSR_HYPERCALL_AVAILABLE;
        env->features[FEAT_HYPERV_EAX] |= HV_X64_MSR_TIME_REF_COUNT_AVAILABLE;
        env->features[FEAT_HYPERV_EAX] |= 0x200;
        has_msr_hv_tsc = true;
    }
    if (cpu->hyperv_crash && has_msr_hv_crash) {
        env->features[FEAT_HYPERV_EDX] |= HV_X64_GUEST_CRASH_MSR_AVAILABLE;
    }
    env->features[FEAT_HYPERV_EDX] |= HV_X64_CPU_DYNAMIC_PARTITIONING_AVAILABLE;
    if (cpu->hyperv_reset && has_msr_hv_reset) {
        env->features[FEAT_HYPERV_EAX] |= HV_X64_MSR_RESET_AVAILABLE;
    }
    if (cpu->hyperv_vpindex && has_msr_hv_vpindex) {
        env->features[FEAT_HYPERV_EAX] |= HV_X64_MSR_VP_INDEX_AVAILABLE;
    }
    if (cpu->hyperv_runtime && has_msr_hv_runtime) {
        env->features[FEAT_HYPERV_EAX] |= HV_X64_MSR_VP_RUNTIME_AVAILABLE;
    }
    if (cpu->hyperv_synic) {
        int sint;

        if (!has_msr_hv_synic ||
            kvm_vcpu_enable_cap(cs, KVM_CAP_HYPERV_SYNIC, 0)) {
            fprintf(stderr, "Hyper-V SynIC is not supported by kernel\n");
            return -ENOSYS;
        }

        env->features[FEAT_HYPERV_EAX] |= HV_X64_MSR_SYNIC_AVAILABLE;
        env->msr_hv_synic_version = HV_SYNIC_VERSION_1;
        for (sint = 0; sint < ARRAY_SIZE(env->msr_hv_synic_sint); sint++) {
            env->msr_hv_synic_sint[sint] = HV_SYNIC_SINT_MASKED;
        }
    }
    if (cpu->hyperv_stimer) {
        if (!has_msr_hv_stimer) {
            fprintf(stderr, "Hyper-V timers aren't supported by kernel\n");
            return -ENOSYS;
        }
        env->features[FEAT_HYPERV_EAX] |= HV_X64_MSR_SYNTIMER_AVAILABLE;
    }
    return 0;
}

static Error *invtsc_mig_blocker;

#define KVM_MAX_CPUID_ENTRIES  100
@@ -635,56 +693,14 @@ int kvm_arch_init_vcpu(CPUState *cs)

        c = &cpuid_data.entries[cpuid_i++];
        c->function = HYPERV_CPUID_FEATURES;
        if (cpu->hyperv_relaxed_timing) {
            c->eax |= HV_X64_MSR_HYPERCALL_AVAILABLE;
        }
        if (cpu->hyperv_vapic) {
            c->eax |= HV_X64_MSR_HYPERCALL_AVAILABLE;
            c->eax |= HV_X64_MSR_APIC_ACCESS_AVAILABLE;
            has_msr_hv_vapic = true;
        }
        if (cpu->hyperv_time &&
            kvm_check_extension(cs->kvm_state, KVM_CAP_HYPERV_TIME) > 0) {
            c->eax |= HV_X64_MSR_HYPERCALL_AVAILABLE;
            c->eax |= HV_X64_MSR_TIME_REF_COUNT_AVAILABLE;
            c->eax |= 0x200;
            has_msr_hv_tsc = true;
        }
        if (cpu->hyperv_crash && has_msr_hv_crash) {
            c->edx |= HV_X64_GUEST_CRASH_MSR_AVAILABLE;
        }
        c->edx |= HV_X64_CPU_DYNAMIC_PARTITIONING_AVAILABLE;
        if (cpu->hyperv_reset && has_msr_hv_reset) {
            c->eax |= HV_X64_MSR_RESET_AVAILABLE;
        }
        if (cpu->hyperv_vpindex && has_msr_hv_vpindex) {
            c->eax |= HV_X64_MSR_VP_INDEX_AVAILABLE;
        }
        if (cpu->hyperv_runtime && has_msr_hv_runtime) {
            c->eax |= HV_X64_MSR_VP_RUNTIME_AVAILABLE;
        }
        if (cpu->hyperv_synic) {
            int sint;

            if (!has_msr_hv_synic ||
                kvm_vcpu_enable_cap(cs, KVM_CAP_HYPERV_SYNIC, 0)) {
                fprintf(stderr, "Hyper-V SynIC is not supported by kernel\n");
                return -ENOSYS;
        r = hyperv_handle_properties(cs);
        if (r) {
            return r;
        }
        c->eax = env->features[FEAT_HYPERV_EAX];
        c->ebx = env->features[FEAT_HYPERV_EBX];
        c->edx = env->features[FEAT_HYPERV_EDX];

            c->eax |= HV_X64_MSR_SYNIC_AVAILABLE;
            env->msr_hv_synic_version = HV_SYNIC_VERSION_1;
            for (sint = 0; sint < ARRAY_SIZE(env->msr_hv_synic_sint); sint++) {
                env->msr_hv_synic_sint[sint] = HV_SYNIC_SINT_MASKED;
            }
        }
        if (cpu->hyperv_stimer) {
            if (!has_msr_hv_stimer) {
                fprintf(stderr, "Hyper-V timers aren't supported by kernel\n");
                return -ENOSYS;
            }
            c->eax |= HV_X64_MSR_SYNTIMER_AVAILABLE;
        }
        c = &cpuid_data.entries[cpuid_i++];
        c->function = HYPERV_CPUID_ENLIGHTMENT_INFO;
        if (cpu->hyperv_relaxed_timing) {