diff options
Diffstat (limited to 'arch/x86/kvm/x86.c')
| -rw-r--r-- | arch/x86/kvm/x86.c | 147 | 
1 files changed, 112 insertions, 35 deletions
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index c1c4e2b05a63..dc7eb5fddfd3 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -3260,8 +3260,11 @@ static void kvm_vcpu_flush_tlb_guest(struct kvm_vcpu *vcpu)  static void record_steal_time(struct kvm_vcpu *vcpu)  { -	struct kvm_host_map map; -	struct kvm_steal_time *st; +	struct gfn_to_hva_cache *ghc = &vcpu->arch.st.cache; +	struct kvm_steal_time __user *st; +	struct kvm_memslots *slots; +	u64 steal; +	u32 version;  	if (kvm_xen_msr_enabled(vcpu->kvm)) {  		kvm_xen_runstate_set_running(vcpu); @@ -3271,47 +3274,86 @@ static void record_steal_time(struct kvm_vcpu *vcpu)  	if (!(vcpu->arch.st.msr_val & KVM_MSR_ENABLED))  		return; -	/* -EAGAIN is returned in atomic context so we can just return. */ -	if (kvm_map_gfn(vcpu, vcpu->arch.st.msr_val >> PAGE_SHIFT, -			&map, &vcpu->arch.st.cache, false)) +	if (WARN_ON_ONCE(current->mm != vcpu->kvm->mm))  		return; -	st = map.hva + -		offset_in_page(vcpu->arch.st.msr_val & KVM_STEAL_VALID_BITS); +	slots = kvm_memslots(vcpu->kvm); + +	if (unlikely(slots->generation != ghc->generation || +		     kvm_is_error_hva(ghc->hva) || !ghc->memslot)) { +		gfn_t gfn = vcpu->arch.st.msr_val & KVM_STEAL_VALID_BITS; + +		/* We rely on the fact that it fits in a single page. */ +		BUILD_BUG_ON((sizeof(*st) - 1) & KVM_STEAL_VALID_BITS); + +		if (kvm_gfn_to_hva_cache_init(vcpu->kvm, ghc, gfn, sizeof(*st)) || +		    kvm_is_error_hva(ghc->hva) || !ghc->memslot) +			return; +	} +	st = (struct kvm_steal_time __user *)ghc->hva;  	/*  	 * Doing a TLB flush here, on the guest's behalf, can avoid  	 * expensive IPIs.  	 */  	if (guest_pv_has(vcpu, KVM_FEATURE_PV_TLB_FLUSH)) { -		u8 st_preempted = xchg(&st->preempted, 0); +		u8 st_preempted = 0; +		int err = -EFAULT; + +		if (!user_access_begin(st, sizeof(*st))) +			return; + +		asm volatile("1: xchgb %0, %2\n" +			     "xor %1, %1\n" +			     "2:\n" +			     _ASM_EXTABLE_UA(1b, 2b) +			     : "+r" (st_preempted), +			       "+&r" (err) +			     : "m" (st->preempted)); +		if (err) +			goto out; + +		user_access_end(); + +		vcpu->arch.st.preempted = 0;  		trace_kvm_pv_tlb_flush(vcpu->vcpu_id,  				       st_preempted & KVM_VCPU_FLUSH_TLB);  		if (st_preempted & KVM_VCPU_FLUSH_TLB)  			kvm_vcpu_flush_tlb_guest(vcpu); + +		if (!user_access_begin(st, sizeof(*st))) +			goto dirty;  	} else { -		st->preempted = 0; -	} +		if (!user_access_begin(st, sizeof(*st))) +			return; -	vcpu->arch.st.preempted = 0; +		unsafe_put_user(0, &st->preempted, out); +		vcpu->arch.st.preempted = 0; +	} -	if (st->version & 1) -		st->version += 1;  /* first time write, random junk */ +	unsafe_get_user(version, &st->version, out); +	if (version & 1) +		version += 1;  /* first time write, random junk */ -	st->version += 1; +	version += 1; +	unsafe_put_user(version, &st->version, out);  	smp_wmb(); -	st->steal += current->sched_info.run_delay - +	unsafe_get_user(steal, &st->steal, out); +	steal += current->sched_info.run_delay -  		vcpu->arch.st.last_steal;  	vcpu->arch.st.last_steal = current->sched_info.run_delay; +	unsafe_put_user(steal, &st->steal, out); -	smp_wmb(); - -	st->version += 1; +	version += 1; +	unsafe_put_user(version, &st->version, out); -	kvm_unmap_gfn(vcpu, &map, &vcpu->arch.st.cache, true, false); + out: +	user_access_end(); + dirty: +	mark_page_dirty_in_slot(vcpu->kvm, ghc->memslot, gpa_to_gfn(ghc->gpa));  }  int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) @@ -3517,7 +3559,7 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info)  		if (!guest_pv_has(vcpu, KVM_FEATURE_PV_EOI))  			return 1; -		if (kvm_lapic_enable_pv_eoi(vcpu, data, sizeof(u8))) +		if (kvm_lapic_set_pv_eoi(vcpu, data, sizeof(u8)))  			return 1;  		break; @@ -4137,7 +4179,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)  		r = !static_call(kvm_x86_cpu_has_accelerated_tpr)();  		break;  	case KVM_CAP_NR_VCPUS: -		r = KVM_SOFT_MAX_VCPUS; +		r = num_online_cpus();  		break;  	case KVM_CAP_MAX_VCPUS:  		r = KVM_MAX_VCPUS; @@ -4351,8 +4393,10 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)  static void kvm_steal_time_set_preempted(struct kvm_vcpu *vcpu)  { -	struct kvm_host_map map; -	struct kvm_steal_time *st; +	struct gfn_to_hva_cache *ghc = &vcpu->arch.st.cache; +	struct kvm_steal_time __user *st; +	struct kvm_memslots *slots; +	static const u8 preempted = KVM_VCPU_PREEMPTED;  	if (!(vcpu->arch.st.msr_val & KVM_MSR_ENABLED))  		return; @@ -4360,16 +4404,23 @@ static void kvm_steal_time_set_preempted(struct kvm_vcpu *vcpu)  	if (vcpu->arch.st.preempted)  		return; -	if (kvm_map_gfn(vcpu, vcpu->arch.st.msr_val >> PAGE_SHIFT, &map, -			&vcpu->arch.st.cache, true)) +	/* This happens on process exit */ +	if (unlikely(current->mm != vcpu->kvm->mm))  		return; -	st = map.hva + -		offset_in_page(vcpu->arch.st.msr_val & KVM_STEAL_VALID_BITS); +	slots = kvm_memslots(vcpu->kvm); + +	if (unlikely(slots->generation != ghc->generation || +		     kvm_is_error_hva(ghc->hva) || !ghc->memslot)) +		return; -	st->preempted = vcpu->arch.st.preempted = KVM_VCPU_PREEMPTED; +	st = (struct kvm_steal_time __user *)ghc->hva; +	BUILD_BUG_ON(sizeof(st->preempted) != sizeof(preempted)); -	kvm_unmap_gfn(vcpu, &map, &vcpu->arch.st.cache, true, true); +	if (!copy_to_user_nofault(&st->preempted, &preempted, sizeof(preempted))) +		vcpu->arch.st.preempted = KVM_VCPU_PREEMPTED; + +	mark_page_dirty_in_slot(vcpu->kvm, ghc->memslot, gpa_to_gfn(ghc->gpa));  }  void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu) @@ -5728,6 +5779,12 @@ split_irqchip_unlock:  		if (kvm_x86_ops.vm_copy_enc_context_from)  			r = kvm_x86_ops.vm_copy_enc_context_from(kvm, cap->args[0]);  		return r; +	case KVM_CAP_VM_MOVE_ENC_CONTEXT_FROM: +		r = -EINVAL; +		if (kvm_x86_ops.vm_move_enc_context_from) +			r = kvm_x86_ops.vm_move_enc_context_from( +				kvm, cap->args[0]); +		return r;  	case KVM_CAP_EXIT_HYPERCALL:  		if (cap->args[0] & ~KVM_EXIT_HYPERCALL_VALID_MASK) {  			r = -EINVAL; @@ -7328,7 +7385,9 @@ static void emulator_set_smbase(struct x86_emulate_ctxt *ctxt, u64 smbase)  static int emulator_check_pmc(struct x86_emulate_ctxt *ctxt,  			      u32 pmc)  { -	return kvm_pmu_is_valid_rdpmc_ecx(emul_to_vcpu(ctxt), pmc); +	if (kvm_pmu_is_valid_rdpmc_ecx(emul_to_vcpu(ctxt), pmc)) +		return 0; +	return -EINVAL;  }  static int emulator_read_pmc(struct x86_emulate_ctxt *ctxt, @@ -9552,7 +9611,7 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)  	}  	if (kvm_request_pending(vcpu)) { -		if (kvm_check_request(KVM_REQ_VM_BUGGED, vcpu)) { +		if (kvm_check_request(KVM_REQ_VM_DEAD, vcpu)) {  			r = -EIO;  			goto out;  		} @@ -10564,6 +10623,24 @@ int kvm_arch_vcpu_ioctl_set_sregs(struct kvm_vcpu *vcpu,  	return ret;  } +static void kvm_arch_vcpu_guestdbg_update_apicv_inhibit(struct kvm *kvm) +{ +	bool inhibit = false; +	struct kvm_vcpu *vcpu; +	int i; + +	down_write(&kvm->arch.apicv_update_lock); + +	kvm_for_each_vcpu(i, vcpu, kvm) { +		if (vcpu->guest_debug & KVM_GUESTDBG_BLOCKIRQ) { +			inhibit = true; +			break; +		} +	} +	__kvm_request_apicv_update(kvm, !inhibit, APICV_INHIBIT_REASON_BLOCKIRQ); +	up_write(&kvm->arch.apicv_update_lock); +} +  int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu,  					struct kvm_guest_debug *dbg)  { @@ -10616,6 +10693,8 @@ int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu,  	static_call(kvm_x86_update_exception_bitmap)(vcpu); +	kvm_arch_vcpu_guestdbg_update_apicv_inhibit(vcpu->kvm); +  	r = 0;  out: @@ -10859,11 +10938,8 @@ void kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu)  void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)  { -	struct gfn_to_pfn_cache *cache = &vcpu->arch.st.cache;  	int idx; -	kvm_release_pfn(cache->pfn, cache->dirty, cache); -  	kvmclock_reset(vcpu);  	static_call(kvm_x86_vcpu_free)(vcpu); @@ -12275,7 +12351,8 @@ int kvm_handle_invpcid(struct kvm_vcpu *vcpu, unsigned long type, gva_t gva)  		return kvm_skip_emulated_instruction(vcpu);  	default: -		BUG(); /* We have already checked above that type <= 3 */ +		kvm_inject_gp(vcpu, 0); +		return 1;  	}  }  EXPORT_SYMBOL_GPL(kvm_handle_invpcid);  | 
