diff options
Diffstat (limited to 'arch/mips/kvm/mmu.c')
| -rw-r--r-- | arch/mips/kvm/mmu.c | 66 | 
1 files changed, 43 insertions, 23 deletions
diff --git a/arch/mips/kvm/mmu.c b/arch/mips/kvm/mmu.c index 57319ee57c4f..121008c0fcc9 100644 --- a/arch/mips/kvm/mmu.c +++ b/arch/mips/kvm/mmu.c @@ -40,7 +40,7 @@ static int kvm_mips_map_page(struct kvm *kvm, gfn_t gfn)  	srcu_idx = srcu_read_lock(&kvm->srcu);  	pfn = gfn_to_pfn(kvm, gfn); -	if (is_error_pfn(pfn)) { +	if (is_error_noslot_pfn(pfn)) {  		kvm_err("Couldn't get pfn for gfn %#llx!\n", gfn);  		err = -EFAULT;  		goto out; @@ -99,7 +99,7 @@ int kvm_mips_handle_kseg0_tlb_fault(unsigned long badvaddr,  	}  	gfn = (KVM_GUEST_CPHYSADDR(badvaddr) >> PAGE_SHIFT); -	if (gfn >= kvm->arch.guest_pmap_npages) { +	if ((gfn | 1) >= kvm->arch.guest_pmap_npages) {  		kvm_err("%s: Invalid gfn: %#llx, BadVaddr: %#lx\n", __func__,  			gfn, badvaddr);  		kvm_mips_dump_host_tlbs(); @@ -138,35 +138,49 @@ int kvm_mips_handle_mapped_seg_tlb_fault(struct kvm_vcpu *vcpu,  	unsigned long entryhi = 0, entrylo0 = 0, entrylo1 = 0;  	struct kvm *kvm = vcpu->kvm;  	kvm_pfn_t pfn0, pfn1; +	gfn_t gfn0, gfn1; +	long tlb_lo[2];  	int ret; -	if ((tlb->tlb_hi & VPN2_MASK) == 0) { -		pfn0 = 0; -		pfn1 = 0; -	} else { -		if (kvm_mips_map_page(kvm, mips3_tlbpfn_to_paddr(tlb->tlb_lo[0]) -					   >> PAGE_SHIFT) < 0) -			return -1; +	tlb_lo[0] = tlb->tlb_lo[0]; +	tlb_lo[1] = tlb->tlb_lo[1]; -		if (kvm_mips_map_page(kvm, mips3_tlbpfn_to_paddr(tlb->tlb_lo[1]) -					   >> PAGE_SHIFT) < 0) -			return -1; +	/* +	 * The commpage address must not be mapped to anything else if the guest +	 * TLB contains entries nearby, or commpage accesses will break. +	 */ +	if (!((tlb->tlb_hi ^ KVM_GUEST_COMMPAGE_ADDR) & +			VPN2_MASK & (PAGE_MASK << 1))) +		tlb_lo[(KVM_GUEST_COMMPAGE_ADDR >> PAGE_SHIFT) & 1] = 0; -		pfn0 = kvm->arch.guest_pmap[ -			mips3_tlbpfn_to_paddr(tlb->tlb_lo[0]) >> PAGE_SHIFT]; -		pfn1 = kvm->arch.guest_pmap[ -			mips3_tlbpfn_to_paddr(tlb->tlb_lo[1]) >> PAGE_SHIFT]; +	gfn0 = mips3_tlbpfn_to_paddr(tlb_lo[0]) >> PAGE_SHIFT; +	gfn1 = mips3_tlbpfn_to_paddr(tlb_lo[1]) >> PAGE_SHIFT; +	if (gfn0 >= kvm->arch.guest_pmap_npages || +	    gfn1 >= kvm->arch.guest_pmap_npages) { +		kvm_err("%s: Invalid gfn: [%#llx, %#llx], EHi: %#lx\n", +			__func__, gfn0, gfn1, tlb->tlb_hi); +		kvm_mips_dump_guest_tlbs(vcpu); +		return -1;  	} +	if (kvm_mips_map_page(kvm, gfn0) < 0) +		return -1; + +	if (kvm_mips_map_page(kvm, gfn1) < 0) +		return -1; + +	pfn0 = kvm->arch.guest_pmap[gfn0]; +	pfn1 = kvm->arch.guest_pmap[gfn1]; +  	/* Get attributes from the Guest TLB */  	entrylo0 = mips3_paddr_to_tlbpfn(pfn0 << PAGE_SHIFT) |  		((_page_cachable_default >> _CACHE_SHIFT) << ENTRYLO_C_SHIFT) | -		(tlb->tlb_lo[0] & ENTRYLO_D) | -		(tlb->tlb_lo[0] & ENTRYLO_V); +		(tlb_lo[0] & ENTRYLO_D) | +		(tlb_lo[0] & ENTRYLO_V);  	entrylo1 = mips3_paddr_to_tlbpfn(pfn1 << PAGE_SHIFT) |  		((_page_cachable_default >> _CACHE_SHIFT) << ENTRYLO_C_SHIFT) | -		(tlb->tlb_lo[1] & ENTRYLO_D) | -		(tlb->tlb_lo[1] & ENTRYLO_V); +		(tlb_lo[1] & ENTRYLO_D) | +		(tlb_lo[1] & ENTRYLO_V);  	kvm_debug("@ %#lx tlb_lo0: 0x%08lx tlb_lo1: 0x%08lx\n", vcpu->arch.pc,  		  tlb->tlb_lo[0], tlb->tlb_lo[1]); @@ -354,9 +368,15 @@ u32 kvm_get_inst(u32 *opc, struct kvm_vcpu *vcpu)  				local_irq_restore(flags);  				return KVM_INVALID_INST;  			} -			kvm_mips_handle_mapped_seg_tlb_fault(vcpu, -							     &vcpu->arch. -							     guest_tlb[index]); +			if (kvm_mips_handle_mapped_seg_tlb_fault(vcpu, +						&vcpu->arch.guest_tlb[index])) { +				kvm_err("%s: handling mapped seg tlb fault failed for %p, index: %u, vcpu: %p, ASID: %#lx\n", +					__func__, opc, index, vcpu, +					read_c0_entryhi()); +				kvm_mips_dump_guest_tlbs(vcpu); +				local_irq_restore(flags); +				return KVM_INVALID_INST; +			}  			inst = *(opc);  		}  		local_irq_restore(flags);  | 
