Re: [PATCH v2 2/8] KVM: x86: Get the number of Hyper-V sparse banks from the VARHEAD field

Vitaly Kuznetsov
2021-11-01T09:54:34Z

Sean Christopherson <seanjc@google.com> writes:

> Get the number of sparse banks from the VARHEAD field, which the guest is
> required to provide as "The size of a variable header, in QWORDS.", where
> the variable header is:
>
> Variable Header Bytes = {Total Header Bytes - sizeof(Fixed Header)}
> rounded up to nearest multiple of 8
> Variable HeaderSize = Variable Header Bytes / 8
>
> In other words, the VARHEAD should match the number of sparse banks.
> Keep the manual count as a sanity check, but otherwise rely on the field
> so as to more closely align with the logic defined in the TLFS and to
> allow for future cleanups.
>
> Signed-off-by: Sean Christopherson <seanjc@google.com>
> ---
> arch/x86/kvm/hyperv.c | 35 ++++++++++++++++++-------------
> arch/x86/kvm/trace.h | 14 +++++++------
> include/asm-generic/hyperv-tlfs.h | 1 +
> 3 files changed, 30 insertions(+), 20 deletions(-)
>
> diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c
> index 814d1a1f2cb8..cf18aa1712bf 100644
> --- a/arch/x86/kvm/hyperv.c
> +++ b/arch/x86/kvm/hyperv.c
> @@ -1742,6 +1742,7 @@ struct kvm_hv_hcall {
> u64 ingpa;
> u64 outgpa;
> u16 code;
> + u16 var_cnt;
> u16 rep_cnt;
> u16 rep_idx;
> bool fast;
> @@ -1761,7 +1762,6 @@ static u64 kvm_hv_flush_tlb(struct kvm_vcpu *vcpu, struct kvm_hv_hcall *hc, bool
> unsigned long *vcpu_mask;
> u64 valid_bank_mask;
> u64 sparse_banks[64];
> - int sparse_banks_len;
> bool all_cpus;
>
> if (!ex) {
> @@ -1811,24 +1811,28 @@ static u64 kvm_hv_flush_tlb(struct kvm_vcpu *vcpu, struct kvm_hv_hcall *hc, bool
> all_cpus = flush_ex.hv_vp_set.format !=
> HV_GENERIC_SET_SPARSE_4K;
>
> - sparse_banks_len = bitmap_weight((unsigned long *)&valid_bank_mask, 64);
> + if (hc->var_cnt != bitmap_weight((unsigned long *)&valid_bank_mask, 64))
> + return HV_STATUS_INVALID_HYPERCALL_INPUT;

Let's hope Windows doesn't break this ruls when vp_set.format != HV_GENERIC_SET_SPARSE_4K

>
> - if (!sparse_banks_len && !all_cpus)
> + if (!hc->var_cnt && !all_cpus)
> goto ret_success;
>
> if (!all_cpus) {
> if (hc->fast) {
> - if (sparse_banks_len > HV_HYPERCALL_MAX_XMM_REGISTERS - 1)
> + if (hc->var_cnt > HV_HYPERCALL_MAX_XMM_REGISTERS - 1)
> return HV_STATUS_INVALID_HYPERCALL_INPUT;
> - for (i = 0; i < sparse_banks_len; i += 2) {
> + for (i = 0; i < hc->var_cnt; i += 2) {
> sparse_banks[i] = sse128_lo(hc->xmm[i / 2 + 1]);
> sparse_banks[i + 1] = sse128_hi(hc->xmm[i / 2 + 1]);
> }
> } else {
> + if (hc->var_cnt > 64)
> + return HV_STATUS_INVALID_HYPERCALL_INPUT;
> +
> gpa = hc->ingpa + offsetof(struct hv_tlb_flush_ex,
> hv_vp_set.bank_contents);
> if (unlikely(kvm_read_guest(kvm, gpa, sparse_banks,
> - sparse_banks_len *
> + hc->var_cnt *
> sizeof(sparse_banks[0]))))
> return HV_STATUS_INVALID_HYPERCALL_INPUT;
> }
> @@ -1884,7 +1888,6 @@ static u64 kvm_hv_send_ipi(struct kvm_vcpu *vcpu, struct kvm_hv_hcall *hc, bool
> unsigned long *vcpu_mask;
> unsigned long valid_bank_mask;
> u64 sparse_banks[64];
> - int sparse_banks_len;
> u32 vector;
> bool all_cpus;
>
> @@ -1917,22 +1920,25 @@ static u64 kvm_hv_send_ipi(struct kvm_vcpu *vcpu, struct kvm_hv_hcall *hc, bool
>
> vector = send_ipi_ex.vector;
> valid_bank_mask = send_ipi_ex.vp_set.valid_bank_mask;
> - sparse_banks_len = bitmap_weight(&valid_bank_mask, 64) *
> - sizeof(sparse_banks[0]);
> -
> all_cpus = send_ipi_ex.vp_set.format == HV_GENERIC_SET_ALL;
>
> + if (hc->var_cnt != bitmap_weight(&valid_bank_mask, 64))
> + return HV_STATUS_INVALID_HYPERCALL_INPUT;
> +
> if (all_cpus)
> goto check_and_send_ipi;
>
> - if (!sparse_banks_len)
> + if (!hc->var_cnt)
> goto ret_success;
>
> + if (hc->var_cnt > 64)
> + return HV_STATUS_INVALID_HYPERCALL_INPUT;
> +
> if (kvm_read_guest(kvm,
> hc->ingpa + offsetof(struct hv_send_ipi_ex,
> vp_set.bank_contents),
> sparse_banks,
> - sparse_banks_len))
> + hc->var_cnt * sizeof(sparse_banks[0])))
> return HV_STATUS_INVALID_HYPERCALL_INPUT;
> }
>
> @@ -2190,13 +2196,14 @@ int kvm_hv_hypercall(struct kvm_vcpu *vcpu)
> }
>
> hc.code = hc.param & 0xffff;
> + hc.var_cnt = (hc.param & HV_HYPERCALL_VARHEAD_MASK) >> HV_HYPERCALL_VARHEAD_OFFSET;
> hc.fast = !!(hc.param & HV_HYPERCALL_FAST_BIT);
> hc.rep_cnt = (hc.param >> HV_HYPERCALL_REP_COMP_OFFSET) & 0xfff;
> hc.rep_idx = (hc.param >> HV_HYPERCALL_REP_START_OFFSET) & 0xfff;
> hc.rep = !!(hc.rep_cnt || hc.rep_idx);
>
> - trace_kvm_hv_hypercall(hc.code, hc.fast, hc.rep_cnt, hc.rep_idx,
> - hc.ingpa, hc.outgpa);
> + trace_kvm_hv_hypercall(hc.code, hc.fast, hc.var_cnt, hc.rep_cnt,
> + hc.rep_idx, hc.ingpa, hc.outgpa);
>
> if (unlikely(!hv_check_hypercall_access(hv_vcpu, hc.code))) {
> ret = HV_STATUS_ACCESS_DENIED;
> diff --git a/arch/x86/kvm/trace.h b/arch/x86/kvm/trace.h
> index 953b0fcb21ee..f6625cfb686c 100644
> --- a/arch/x86/kvm/trace.h
> +++ b/arch/x86/kvm/trace.h
> @@ -64,9 +64,9 @@ TRACE_EVENT(kvm_hypercall,
> * Tracepoint for hypercall.
> */
> TRACE_EVENT(kvm_hv_hypercall,
> - TP_PROTO(__u16 code, bool fast, __u16 rep_cnt, __u16 rep_idx,
> - __u64 ingpa, __u64 outgpa),
> - TP_ARGS(code, fast, rep_cnt, rep_idx, ingpa, outgpa),
> + TP_PROTO(__u16 code, bool fast, __u16 var_cnt, __u16 rep_cnt,
> + __u16 rep_idx, __u64 ingpa, __u64 outgpa),
> + TP_ARGS(code, fast, var_cnt, rep_cnt, rep_idx, ingpa, outgpa),
>
> TP_STRUCT__entry(
> __field( __u16, rep_cnt )
> @@ -74,6 +74,7 @@ TRACE_EVENT(kvm_hv_hypercall,
> __field( __u64, ingpa )
> __field( __u64, outgpa )
> __field( __u16, code )
> + __field( __u16, var_cnt )
> __field( bool, fast )
> ),
>
> @@ -83,13 +84,14 @@ TRACE_EVENT(kvm_hv_hypercall,
> __entry->ingpa = ingpa;
> __entry->outgpa = outgpa;
> __entry->code = code;
> + __entry->var_cnt = var_cnt;
> __entry->fast = fast;
> ),
>
> - TP_printk("code 0x%x %s cnt 0x%x idx 0x%x in 0x%llx out 0x%llx",
> + TP_printk("code 0x%x %s var_cnt 0x%x cnt 0x%x idx 0x%x in 0x%llx out 0x%llx",

Nit: 'cnt' is (and was) a bit ambiguous, I'd suggest to explicitly say
'rep_cnt' (and probably 'rep_idx') instead.

> __entry->code, __entry->fast ? "fast" : "slow",
> - __entry->rep_cnt, __entry->rep_idx, __entry->ingpa,
> - __entry->outgpa)
> + __entry->var_cnt, __entry->rep_cnt, __entry->rep_idx,
> + __entry->ingpa, __entry->outgpa)
> );
>
> TRACE_EVENT(kvm_hv_hypercall_done,
> diff --git a/include/asm-generic/hyperv-tlfs.h b/include/asm-generic/hyperv-tlfs.h
> index 56348a541c50..1ba8e6da4427 100644
> --- a/include/asm-generic/hyperv-tlfs.h
> +++ b/include/asm-generic/hyperv-tlfs.h
> @@ -182,6 +182,7 @@ enum HV_GENERIC_SET_FORMAT {
> #define HV_HYPERCALL_RESULT_MASK GENMASK_ULL(15, 0)
> #define HV_HYPERCALL_FAST_BIT BIT(16)
> #define HV_HYPERCALL_VARHEAD_OFFSET 17
> +#define HV_HYPERCALL_VARHEAD_MASK GENMASK_ULL(26, 17)
> #define HV_HYPERCALL_REP_COMP_OFFSET 32
> #define HV_HYPERCALL_REP_COMP_1 BIT_ULL(32)
> #define HV_HYPERCALL_REP_COMP_MASK GENMASK_ULL(43, 32)

Reviewed-by: Vitaly Kuznetsov <vkuznets@redhat.com>

--
Vitaly