On CPUID leaf 0x40000003, EBX bit 20 signals that we support the HvExtCallQueryCapabilities hypercall. This returns a bit field which signals which further extended hypercalls are supported (as a way to conserve CPUID leaf bits).
We'll track such capabilities in kvm_hyperv_properties like all the others, but they will have a ".reg" entry of 0, since they aren't returned via CPUID and so don't have a corresponding register. For now, we don't support any extended hypercalls, but we'll add HvExtCallGetBootZeroedMemory in a followup patch. Signed-off-by: Florian Schmidt <[email protected]> --- docs/system/i386/hyperv.rst | 7 +++++++ hw/hyperv/hyperv.c | 28 ++++++++++++++++++++++++++++ include/hw/hyperv/hyperv-proto.h | 1 + include/hw/hyperv/hyperv.h | 5 +++++ target/i386/cpu.c | 2 ++ target/i386/cpu.h | 1 + target/i386/kvm/hyperv-proto.h | 1 + target/i386/kvm/hyperv.c | 6 ++++++ target/i386/kvm/kvm.c | 12 ++++++++++++ target/i386/kvm/kvm_i386.h | 2 ++ 10 files changed, 65 insertions(+) diff --git a/docs/system/i386/hyperv.rst b/docs/system/i386/hyperv.rst index 1c1de77feb..c5ca25067e 100644 --- a/docs/system/i386/hyperv.rst +++ b/docs/system/i386/hyperv.rst @@ -256,6 +256,13 @@ Existing enlightenments Recommended: ``hv-evmcs`` (Intel) +``hv-ext-query-caps`` + Certain "extended hypercalls" are not signaled to the guest directly via + CPUID bits, but use a separate hypercall that returns a bitfield to signal + support for them. This enables this hypercall. On its own, it does not + provide any useful new functionality, but it's it's required to be enabled + to use any extended hypercalls. + Supplementary features ---------------------- diff --git a/hw/hyperv/hyperv.c b/hw/hyperv/hyperv.c index a854a4927a..1764203d3c 100644 --- a/hw/hyperv/hyperv.c +++ b/hw/hyperv/hyperv.c @@ -703,6 +703,34 @@ int hyperv_set_event_flag_handler(uint32_t conn_id, EventNotifier *notifier) return set_event_flag_handler(conn_id, notifier); } +uint16_t hyperv_ext_hcall_query_caps(uint64_t sup, uint64_t outgpa, bool fast) +{ + uint16_t ret; + uint64_t *supported = NULL; + hwaddr len; + + if (fast) { + ret = HV_STATUS_INVALID_HYPERCALL_CODE; + goto cleanup; + } + + len = sizeof(*supported); + supported = cpu_physical_memory_map(outgpa, &len, 1); + if (!supported || len < sizeof(*supported)) { + ret = HV_STATUS_INSUFFICIENT_MEMORY; + goto cleanup; + } + + *supported = sup; + ret = HV_STATUS_SUCCESS; + +cleanup: + if (supported) { + cpu_physical_memory_unmap(supported, sizeof(*supported), 1, len); + } + return ret; +} + uint16_t hyperv_hcall_signal_event(uint64_t param, bool fast) { EventFlagHandler *handler; diff --git a/include/hw/hyperv/hyperv-proto.h b/include/hw/hyperv/hyperv-proto.h index fffc5ce342..f1d1d2eb26 100644 --- a/include/hw/hyperv/hyperv-proto.h +++ b/include/hw/hyperv/hyperv-proto.h @@ -35,6 +35,7 @@ #define HV_POST_DEBUG_DATA 0x0069 #define HV_RETRIEVE_DEBUG_DATA 0x006a #define HV_RESET_DEBUG_SESSION 0x006b +#define HV_EXT_CALL_QUERY_CAPABILITIES 0x8001 #define HV_HYPERCALL_FAST (1u << 16) /* diff --git a/include/hw/hyperv/hyperv.h b/include/hw/hyperv/hyperv.h index 23091301d0..e29d60f565 100644 --- a/include/hw/hyperv/hyperv.h +++ b/include/hw/hyperv/hyperv.h @@ -97,6 +97,11 @@ uint16_t hyperv_hcall_retreive_dbg_data(uint64_t ingpa, uint64_t outgpa, */ uint16_t hyperv_hcall_post_dbg_data(uint64_t ingpa, uint64_t outgpa, bool fast); +/* + * Process HVCALL_EXT_QUERY_CAPABILITIES hypercall. + */ +uint16_t hyperv_ext_hcall_query_caps(uint64_t sup, uint64_t outgpa, bool fast); + uint32_t hyperv_syndbg_send(uint64_t ingpa, uint32_t count); uint32_t hyperv_syndbg_recv(uint64_t ingpa, uint32_t count); void hyperv_syndbg_set_pending_page(uint64_t ingpa); diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 97c66c2226..4d77de52b4 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -10760,6 +10760,8 @@ static const Property x86_cpu_properties[] = { HYPERV_FEAT_TLBFLUSH_EXT, 0), DEFINE_PROP_BIT64("hv-tlbflush-direct", X86CPU, hyperv_features, HYPERV_FEAT_TLBFLUSH_DIRECT, 0), + DEFINE_PROP_BIT64("hv-ext-query-caps", X86CPU, hyperv_features, + HYPERV_FEAT_EXT_CALLS, 0), DEFINE_PROP_ON_OFF_AUTO("hv-no-nonarch-coresharing", X86CPU, hyperv_no_nonarch_cs, ON_OFF_AUTO_OFF), #ifdef CONFIG_SYNDBG diff --git a/target/i386/cpu.h b/target/i386/cpu.h index a836efd45e..eb870fa015 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1480,6 +1480,7 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w); #define HYPERV_FEAT_XMM_INPUT 18 #define HYPERV_FEAT_TLBFLUSH_EXT 19 #define HYPERV_FEAT_TLBFLUSH_DIRECT 20 +#define HYPERV_FEAT_EXT_CALLS 21 #ifndef HYPERV_SPINLOCK_NEVER_NOTIFY #define HYPERV_SPINLOCK_NEVER_NOTIFY 0xFFFFFFFF diff --git a/target/i386/kvm/hyperv-proto.h b/target/i386/kvm/hyperv-proto.h index a9f056f2f3..4eb2955ac5 100644 --- a/target/i386/kvm/hyperv-proto.h +++ b/target/i386/kvm/hyperv-proto.h @@ -46,6 +46,7 @@ */ #define HV_POST_MESSAGES (1u << 4) #define HV_SIGNAL_EVENTS (1u << 5) +#define HV_ENABLE_EXT_HYPERCALLS (1u << 20) /* * HV_CPUID_FEATURES.EDX bits diff --git a/target/i386/kvm/hyperv.c b/target/i386/kvm/hyperv.c index 420c76b5ff..807acaf6b1 100644 --- a/target/i386/kvm/hyperv.c +++ b/target/i386/kvm/hyperv.c @@ -17,6 +17,7 @@ #include "hyperv.h" #include "hw/hyperv/hyperv.h" #include "hyperv-proto.h" +#include "kvm_i386.h" int hyperv_x86_synic_add(X86CPU *cpu) { @@ -117,6 +118,11 @@ int kvm_hv_handle_exit(X86CPU *cpu, struct kvm_hyperv_exit *exit) exit->u.hcall.result = hyperv_hcall_reset_dbg_session(out_param); break; + case HV_EXT_CALL_QUERY_CAPABILITIES: + exit->u.hcall.result = + hyperv_ext_hcall_query_caps(hv_build_ext_call_caps(CPU(cpu)), + out_param, fast); + break; default: exit->u.hcall.result = HV_STATUS_INVALID_HYPERCALL_CODE; } diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 9e352882c8..586656258d 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -1136,6 +1136,13 @@ static struct { }, .dependencies = BIT(HYPERV_FEAT_VAPIC) }, + [HYPERV_FEAT_EXT_CALLS] = { + .desc = "query availability of extended hypercalls (hv-ext-query-caps)", + .flags = { + {.func = HV_CPUID_FEATURES, .reg = R_EBX, + .bits = HV_ENABLE_EXT_HYPERCALLS} + } + }, }; static struct kvm_cpuid2 *try_get_hv_cpuid(CPUState *cs, int max, @@ -1433,6 +1440,11 @@ static uint32_t hv_build_cpuid_leaf(CPUState *cs, uint32_t func, int reg) return r; } +uint64_t hv_build_ext_call_caps(CPUState *cs) +{ + return hv_build_cpuid_leaf(cs, HV_EXT_CALL_QUERY_CAPABILITIES, 0); +} + /* * Expand Hyper-V CPU features. In partucular, check that all the requested * features are supported by the host and the sanity of the configuration diff --git a/target/i386/kvm/kvm_i386.h b/target/i386/kvm/kvm_i386.h index 2b653442f4..8c71ad01af 100644 --- a/target/i386/kvm/kvm_i386.h +++ b/target/i386/kvm/kvm_i386.h @@ -72,6 +72,8 @@ struct kvm_cpuid_entry2 *cpuid_find_entry(struct kvm_cpuid2 *cpuid, uint32_t cpuid_entry_get_reg(struct kvm_cpuid_entry2 *entry, int reg); uint32_t kvm_x86_build_cpuid(CPUX86State *env, struct kvm_cpuid_entry2 *entries, uint32_t cpuid_i); + +uint64_t hv_build_ext_call_caps(CPUState *cs); #endif /* CONFIG_KVM */ void kvm_smm_cpu_address_space_init(X86CPU *cpu); -- 2.47.3
