KVM unconditionally uses the "full" size of the Xen shared info page when activating the cache in kvm_xen_vcpu_set_attr(), but using the current mode matches what Xen does. While KVM did always use the 64-bit size when activating the cache, what matters is that Xen does not look beyond the size of the 32-bit struct if the vCPU was initialized in 32-bit mode. If the guest sets up the runstate info of a 32-bit VM so that the struct ends at the end of a page, the 64-bit struct size passed to kvm_gpc_activate() will cause the ioctl or hypercall to fail, because gfn-to-pfn caches can only be set up for data that fits in a single page. Nevertheless, keeping the Xen word size constant throughout the life of the gpc cache, i.e. not using a different size at check()+refresh() than at activate(), is desirable because it makes the length/size of the cache immutable. This in turn yields a cleaner set of APIs and avoids potential bugs that could occur if check() were invoked with a different size than refresh(). So, use the short size at activation time as well. This means re-activating the cache if the guest requests the hypercall page multiple times with different word sizes (this can happen when kexec-ing, for example). Signed-off-by: Paolo Bonzini <pbonzini@xxxxxxxxxx> --- arch/x86/kvm/xen.c | 47 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/arch/x86/kvm/xen.c b/arch/x86/kvm/xen.c index b2be60c6efa4..512b4afa6785 100644 --- a/arch/x86/kvm/xen.c +++ b/arch/x86/kvm/xen.c @@ -198,6 +198,37 @@ static void kvm_xen_update_runstate(struct kvm_vcpu *v, int state) vx->runstate_entry_time = now; } +static inline size_t kvm_xen_runstate_info_size(struct kvm *kvm) +{ + if (IS_ENABLED(CONFIG_64BIT) && kvm->arch.xen.long_mode) + return sizeof(struct vcpu_runstate_info); + else + return sizeof(struct compat_vcpu_runstate_info); +} + +static int kvm_xen_activate_runstate_gpc(struct kvm_vcpu *vcpu, unsigned long gpa) +{ + size_t user_len = kvm_xen_runstate_info_size(vcpu->kvm); + return kvm_gpc_activate(vcpu->kvm, &vcpu->arch.xen.runstate_cache, + NULL, KVM_HOST_USES_PFN, gpa, user_len); +} + +static int kvm_xen_reactivate_runstate_gpcs(struct kvm *kvm) +{ + struct kvm_vcpu *vcpu; + unsigned long i; + + kvm_for_each_vcpu(i, vcpu, kvm) { + if (vcpu->arch.xen.runstate_cache.active) { + int r = kvm_xen_activate_runstate_gpc(vcpu, + vcpu->arch.xen.runstate_cache.gpa); + if (r < 0) + return r; + } + } + return 0; +} + void kvm_xen_update_runstate_guest(struct kvm_vcpu *v, int state) { struct kvm_vcpu_xen *vx = &v->arch.xen; @@ -212,11 +243,7 @@ void kvm_xen_update_runstate_guest(struct kvm_vcpu *v, int state) if (!vx->runstate_cache.active) return; - if (IS_ENABLED(CONFIG_64BIT) && v->kvm->arch.xen.long_mode) - user_len = sizeof(struct vcpu_runstate_info); - else - user_len = sizeof(struct compat_vcpu_runstate_info); - + user_len = kvm_xen_runstate_info_size(v->kvm); read_lock_irqsave(&gpc->lock, flags); while (!kvm_gfn_to_pfn_cache_check(v->kvm, gpc, gpc->gpa, user_len)) { @@ -461,7 +488,7 @@ int kvm_xen_hvm_set_attr(struct kvm *kvm, struct kvm_xen_hvm_attr *data) mutex_lock(&kvm->lock); kvm->arch.xen.long_mode = !!data->u.long_mode; mutex_unlock(&kvm->lock); - r = 0; + r = kvm_xen_reactivate_runstate_gpcs(kvm); } break; @@ -596,9 +623,7 @@ int kvm_xen_vcpu_set_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data) break; } - r = kvm_gpc_activate(vcpu->kvm, &vcpu->arch.xen.runstate_cache, - NULL, KVM_HOST_USES_PFN, data->u.gpa, - sizeof(struct vcpu_runstate_info)); + r = kvm_xen_activate_runstate_gpc(vcpu, data->u.gpa); break; case KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_CURRENT: @@ -843,9 +868,13 @@ int kvm_xen_write_hypercall_page(struct kvm_vcpu *vcpu, u64 data) u32 page_num = data & ~PAGE_MASK; u64 page_addr = data & PAGE_MASK; bool lm = is_long_mode(vcpu); + int r; /* Latch long_mode for shared_info pages etc. */ vcpu->kvm->arch.xen.long_mode = lm; + r = kvm_xen_reactivate_runstate_gpcs(kvm); + if (r < 0) + return 1; /* * If Xen hypercall intercept is enabled, fill the hypercall -- 2.31.1