diff options
Diffstat (limited to 'arch/x86/kvm/svm.c')
| -rw-r--r-- | arch/x86/kvm/svm.c | 54 | 
1 files changed, 46 insertions, 8 deletions
| diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c index b5b128a0a051..e0a791c3d4fc 100644 --- a/arch/x86/kvm/svm.c +++ b/arch/x86/kvm/svm.c @@ -6422,11 +6422,11 @@ e_free:  	return ret;  } -static int get_num_contig_pages(int idx, struct page **inpages, -				unsigned long npages) +static unsigned long get_num_contig_pages(unsigned long idx, +				struct page **inpages, unsigned long npages)  {  	unsigned long paddr, next_paddr; -	int i = idx + 1, pages = 1; +	unsigned long i = idx + 1, pages = 1;  	/* find the number of contiguous pages starting from idx */  	paddr = __sme_page_pa(inpages[idx]); @@ -6445,12 +6445,12 @@ static int get_num_contig_pages(int idx, struct page **inpages,  static int sev_launch_update_data(struct kvm *kvm, struct kvm_sev_cmd *argp)  { -	unsigned long vaddr, vaddr_end, next_vaddr, npages, size; +	unsigned long vaddr, vaddr_end, next_vaddr, npages, pages, size, i;  	struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;  	struct kvm_sev_launch_update_data params;  	struct sev_data_launch_update_data *data;  	struct page **inpages; -	int i, ret, pages; +	int ret;  	if (!sev_guest(kvm))  		return -ENOTTY; @@ -6799,7 +6799,8 @@ static int sev_dbg_crypt(struct kvm *kvm, struct kvm_sev_cmd *argp, bool dec)  	struct page **src_p, **dst_p;  	struct kvm_sev_dbg debug;  	unsigned long n; -	int ret, size; +	unsigned int size; +	int ret;  	if (!sev_guest(kvm))  		return -ENOTTY; @@ -6807,6 +6808,11 @@ static int sev_dbg_crypt(struct kvm *kvm, struct kvm_sev_cmd *argp, bool dec)  	if (copy_from_user(&debug, (void __user *)(uintptr_t)argp->data, sizeof(debug)))  		return -EFAULT; +	if (!debug.len || debug.src_uaddr + debug.len < debug.src_uaddr) +		return -EINVAL; +	if (!debug.dst_uaddr) +		return -EINVAL; +  	vaddr = debug.src_uaddr;  	size = debug.len;  	vaddr_end = vaddr + size; @@ -6857,8 +6863,8 @@ static int sev_dbg_crypt(struct kvm *kvm, struct kvm_sev_cmd *argp, bool dec)  						     dst_vaddr,  						     len, &argp->error); -		sev_unpin_memory(kvm, src_p, 1); -		sev_unpin_memory(kvm, dst_p, 1); +		sev_unpin_memory(kvm, src_p, n); +		sev_unpin_memory(kvm, dst_p, n);  		if (ret)  			goto err; @@ -7098,6 +7104,36 @@ static int nested_enable_evmcs(struct kvm_vcpu *vcpu,  	return -ENODEV;  } +static bool svm_need_emulation_on_page_fault(struct kvm_vcpu *vcpu) +{ +	bool is_user, smap; + +	is_user = svm_get_cpl(vcpu) == 3; +	smap = !kvm_read_cr4_bits(vcpu, X86_CR4_SMAP); + +	/* +	 * Detect and workaround Errata 1096 Fam_17h_00_0Fh +	 * +	 * In non SEV guest, hypervisor will be able to read the guest +	 * memory to decode the instruction pointer when insn_len is zero +	 * so we return true to indicate that decoding is possible. +	 * +	 * But in the SEV guest, the guest memory is encrypted with the +	 * guest specific key and hypervisor will not be able to decode the +	 * instruction pointer so we will not able to workaround it. Lets +	 * print the error and request to kill the guest. +	 */ +	if (is_user && smap) { +		if (!sev_guest(vcpu->kvm)) +			return true; + +		pr_err_ratelimited("KVM: Guest triggered AMD Erratum 1096\n"); +		kvm_make_request(KVM_REQ_TRIPLE_FAULT, vcpu); +	} + +	return false; +} +  static struct kvm_x86_ops svm_x86_ops __ro_after_init = {  	.cpu_has_kvm_support = has_svm,  	.disabled_by_bios = is_disabled, @@ -7231,6 +7267,8 @@ static struct kvm_x86_ops svm_x86_ops __ro_after_init = {  	.nested_enable_evmcs = nested_enable_evmcs,  	.nested_get_evmcs_version = nested_get_evmcs_version, + +	.need_emulation_on_page_fault = svm_need_emulation_on_page_fault,  };  static int __init svm_init(void) |