static int kvmppc_mmu_book3s_64_xlate()

in kvm/book3s_64_mmu.c [191:361]


static int kvmppc_mmu_book3s_64_xlate(struct kvm_vcpu *vcpu, gva_t eaddr,
				      struct kvmppc_pte *gpte, bool data,
				      bool iswrite)
{
	struct kvmppc_slb *slbe;
	hva_t ptegp;
	u64 pteg[16];
	u64 avpn = 0;
	u64 r;
	u64 v_val, v_mask;
	u64 eaddr_mask;
	int i;
	u8 pp, key = 0;
	bool found = false;
	bool second = false;
	int pgsize;
	ulong mp_ea = vcpu->arch.magic_page_ea;

	/* Magic page override */
	if (unlikely(mp_ea) &&
	    unlikely((eaddr & ~0xfffULL) == (mp_ea & ~0xfffULL)) &&
	    !(kvmppc_get_msr(vcpu) & MSR_PR)) {
		gpte->eaddr = eaddr;
		gpte->vpage = kvmppc_mmu_book3s_64_ea_to_vp(vcpu, eaddr, data);
		gpte->raddr = vcpu->arch.magic_page_pa | (gpte->raddr & 0xfff);
		gpte->raddr &= KVM_PAM;
		gpte->may_execute = true;
		gpte->may_read = true;
		gpte->may_write = true;
		gpte->page_size = MMU_PAGE_4K;
		gpte->wimg = HPTE_R_M;

		return 0;
	}

	slbe = kvmppc_mmu_book3s_64_find_slbe(vcpu, eaddr);
	if (!slbe)
		goto no_seg_found;

	avpn = kvmppc_mmu_book3s_64_get_avpn(slbe, eaddr);
	v_val = avpn & HPTE_V_AVPN;

	if (slbe->tb)
		v_val |= SLB_VSID_B_1T;
	if (slbe->large)
		v_val |= HPTE_V_LARGE;
	v_val |= HPTE_V_VALID;

	v_mask = SLB_VSID_B | HPTE_V_AVPN | HPTE_V_LARGE | HPTE_V_VALID |
		HPTE_V_SECONDARY;

	pgsize = slbe->large ? MMU_PAGE_16M : MMU_PAGE_4K;

	mutex_lock(&vcpu->kvm->arch.hpt_mutex);

do_second:
	ptegp = kvmppc_mmu_book3s_64_get_pteg(vcpu, slbe, eaddr, second);
	if (kvm_is_error_hva(ptegp))
		goto no_page_found;

	if(copy_from_user(pteg, (void __user *)ptegp, sizeof(pteg))) {
		printk_ratelimited(KERN_ERR
			"KVM: Can't copy data from 0x%lx!\n", ptegp);
		goto no_page_found;
	}

	if ((kvmppc_get_msr(vcpu) & MSR_PR) && slbe->Kp)
		key = 4;
	else if (!(kvmppc_get_msr(vcpu) & MSR_PR) && slbe->Ks)
		key = 4;

	for (i=0; i<16; i+=2) {
		u64 pte0 = be64_to_cpu(pteg[i]);
		u64 pte1 = be64_to_cpu(pteg[i + 1]);

		/* Check all relevant fields of 1st dword */
		if ((pte0 & v_mask) == v_val) {
			/* If large page bit is set, check pgsize encoding */
			if (slbe->large &&
			    (vcpu->arch.hflags & BOOK3S_HFLAG_MULTI_PGSIZE)) {
				pgsize = decode_pagesize(slbe, pte1);
				if (pgsize < 0)
					continue;
			}
			found = true;
			break;
		}
	}

	if (!found) {
		if (second)
			goto no_page_found;
		v_val |= HPTE_V_SECONDARY;
		second = true;
		goto do_second;
	}

	r = be64_to_cpu(pteg[i+1]);
	pp = (r & HPTE_R_PP) | key;
	if (r & HPTE_R_PP0)
		pp |= 8;

	gpte->eaddr = eaddr;
	gpte->vpage = kvmppc_mmu_book3s_64_ea_to_vp(vcpu, eaddr, data);

	eaddr_mask = (1ull << mmu_pagesize(pgsize)) - 1;
	gpte->raddr = (r & HPTE_R_RPN & ~eaddr_mask) | (eaddr & eaddr_mask);
	gpte->page_size = pgsize;
	gpte->may_execute = ((r & HPTE_R_N) ? false : true);
	if (unlikely(vcpu->arch.disable_kernel_nx) &&
	    !(kvmppc_get_msr(vcpu) & MSR_PR))
		gpte->may_execute = true;
	gpte->may_read = false;
	gpte->may_write = false;
	gpte->wimg = r & HPTE_R_WIMG;

	switch (pp) {
	case 0:
	case 1:
	case 2:
	case 6:
		gpte->may_write = true;
		fallthrough;
	case 3:
	case 5:
	case 7:
	case 10:
		gpte->may_read = true;
		break;
	}

	dprintk("KVM MMU: Translated 0x%lx [0x%llx] -> 0x%llx "
		"-> 0x%lx\n",
		eaddr, avpn, gpte->vpage, gpte->raddr);

	/* Update PTE R and C bits, so the guest's swapper knows we used the
	 * page */
	if (gpte->may_read && !(r & HPTE_R_R)) {
		/*
		 * Set the accessed flag.
		 * We have to write this back with a single byte write
		 * because another vcpu may be accessing this on
		 * non-PAPR platforms such as mac99, and this is
		 * what real hardware does.
		 */
                char __user *addr = (char __user *) (ptegp + (i + 1) * sizeof(u64));
		r |= HPTE_R_R;
		put_user(r >> 8, addr + 6);
	}
	if (iswrite && gpte->may_write && !(r & HPTE_R_C)) {
		/* Set the dirty flag */
		/* Use a single byte write */
                char __user *addr = (char __user *) (ptegp + (i + 1) * sizeof(u64));
		r |= HPTE_R_C;
		put_user(r, addr + 7);
	}

	mutex_unlock(&vcpu->kvm->arch.hpt_mutex);

	if (!gpte->may_read || (iswrite && !gpte->may_write))
		return -EPERM;
	return 0;

no_page_found:
	mutex_unlock(&vcpu->kvm->arch.hpt_mutex);
	return -ENOENT;

no_seg_found:
	dprintk("KVM MMU: Trigger segment fault\n");
	return -EINVAL;
}