diff options
Diffstat (limited to 'arch/powerpc/kvm/book3s_64_mmu.c')
| -rw-r--r-- | arch/powerpc/kvm/book3s_64_mmu.c | 212 | 
1 files changed, 167 insertions, 45 deletions
diff --git a/arch/powerpc/kvm/book3s_64_mmu.c b/arch/powerpc/kvm/book3s_64_mmu.c index 7e345e00661..774a253ca4e 100644 --- a/arch/powerpc/kvm/book3s_64_mmu.c +++ b/arch/powerpc/kvm/book3s_64_mmu.c @@ -38,7 +38,7 @@  static void kvmppc_mmu_book3s_64_reset_msr(struct kvm_vcpu *vcpu)  { -	kvmppc_set_msr(vcpu, MSR_SF); +	kvmppc_set_msr(vcpu, vcpu->arch.intr_msr);  }  static struct kvmppc_slb *kvmppc_mmu_book3s_64_find_slbe( @@ -107,9 +107,20 @@ static u64 kvmppc_mmu_book3s_64_ea_to_vp(struct kvm_vcpu *vcpu, gva_t eaddr,  	return kvmppc_slb_calc_vpn(slb, eaddr);  } +static int mmu_pagesize(int mmu_pg) +{ +	switch (mmu_pg) { +	case MMU_PAGE_64K: +		return 16; +	case MMU_PAGE_16M: +		return 24; +	} +	return 12; +} +  static int kvmppc_mmu_book3s_64_get_pagesize(struct kvmppc_slb *slbe)  { -	return slbe->large ? 24 : 12; +	return mmu_pagesize(slbe->base_page_size);  }  static u32 kvmppc_mmu_book3s_64_get_page(struct kvmppc_slb *slbe, gva_t eaddr) @@ -119,11 +130,11 @@ static u32 kvmppc_mmu_book3s_64_get_page(struct kvmppc_slb *slbe, gva_t eaddr)  	return ((eaddr & kvmppc_slb_offset_mask(slbe)) >> p);  } -static hva_t kvmppc_mmu_book3s_64_get_pteg( -				struct kvmppc_vcpu_book3s *vcpu_book3s, +static hva_t kvmppc_mmu_book3s_64_get_pteg(struct kvm_vcpu *vcpu,  				struct kvmppc_slb *slbe, gva_t eaddr,  				bool second)  { +	struct kvmppc_vcpu_book3s *vcpu_book3s = to_book3s(vcpu);  	u64 hash, pteg, htabsize;  	u32 ssize;  	hva_t r; @@ -148,10 +159,10 @@ static hva_t kvmppc_mmu_book3s_64_get_pteg(  	/* When running a PAPR guest, SDR1 contains a HVA address instead             of a GPA */ -	if (vcpu_book3s->vcpu.arch.papr_enabled) +	if (vcpu->arch.papr_enabled)  		r = pteg;  	else -		r = gfn_to_hva(vcpu_book3s->vcpu.kvm, pteg >> PAGE_SHIFT); +		r = gfn_to_hva(vcpu->kvm, pteg >> PAGE_SHIFT);  	if (kvm_is_error_hva(r))  		return r; @@ -166,18 +177,38 @@ static u64 kvmppc_mmu_book3s_64_get_avpn(struct kvmppc_slb *slbe, gva_t eaddr)  	avpn = kvmppc_mmu_book3s_64_get_page(slbe, eaddr);  	avpn |= slbe->vsid << (kvmppc_slb_sid_shift(slbe) - p); -	if (p < 24) -		avpn >>= ((80 - p) - 56) - 8; +	if (p < 16) +		avpn >>= ((80 - p) - 56) - 8;	/* 16 - p */  	else -		avpn <<= 8; +		avpn <<= p - 16;  	return avpn;  } +/* + * Return page size encoded in the second word of a HPTE, or + * -1 for an invalid encoding for the base page size indicated by + * the SLB entry.  This doesn't handle mixed pagesize segments yet. + */ +static int decode_pagesize(struct kvmppc_slb *slbe, u64 r) +{ +	switch (slbe->base_page_size) { +	case MMU_PAGE_64K: +		if ((r & 0xf000) == 0x1000) +			return MMU_PAGE_64K; +		break; +	case MMU_PAGE_16M: +		if ((r & 0xff000) == 0) +			return MMU_PAGE_16M; +		break; +	} +	return -1; +} +  static int kvmppc_mmu_book3s_64_xlate(struct kvm_vcpu *vcpu, gva_t eaddr, -				struct kvmppc_pte *gpte, bool data) +				      struct kvmppc_pte *gpte, bool data, +				      bool iswrite)  { -	struct kvmppc_vcpu_book3s *vcpu_book3s = to_book3s(vcpu);  	struct kvmppc_slb *slbe;  	hva_t ptegp;  	u64 pteg[16]; @@ -189,12 +220,13 @@ static int kvmppc_mmu_book3s_64_xlate(struct kvm_vcpu *vcpu, gva_t eaddr,  	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)) && -	    !(vcpu->arch.shared->msr & MSR_PR)) { +	    !(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); @@ -202,6 +234,7 @@ static int kvmppc_mmu_book3s_64_xlate(struct kvm_vcpu *vcpu, gva_t eaddr,  		gpte->may_execute = true;  		gpte->may_read = true;  		gpte->may_write = true; +		gpte->page_size = MMU_PAGE_4K;  		return 0;  	} @@ -222,8 +255,12 @@ static int kvmppc_mmu_book3s_64_xlate(struct kvm_vcpu *vcpu, gva_t eaddr,  	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_book3s, slbe, eaddr, second); +	ptegp = kvmppc_mmu_book3s_64_get_pteg(vcpu, slbe, eaddr, second);  	if (kvm_is_error_hva(ptegp))  		goto no_page_found; @@ -232,14 +269,24 @@ do_second:  		goto no_page_found;  	} -	if ((vcpu->arch.shared->msr & MSR_PR) && slbe->Kp) +	if ((kvmppc_get_msr(vcpu) & MSR_PR) && slbe->Kp)  		key = 4; -	else if (!(vcpu->arch.shared->msr & MSR_PR) && slbe->Ks) +	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 ((pteg[i] & v_mask) == v_val) { +		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;  		} @@ -253,17 +300,22 @@ do_second:  		goto do_second;  	} -	v = pteg[i]; -	r = pteg[i+1]; +	v = be64_to_cpu(pteg[i]); +	r = be64_to_cpu(pteg[i+1]);  	pp = (r & HPTE_R_PP) | key; -	eaddr_mask = 0xFFF; +	if (r & HPTE_R_PP0) +		pp |= 8;  	gpte->eaddr = eaddr;  	gpte->vpage = kvmppc_mmu_book3s_64_ea_to_vp(vcpu, eaddr, data); -	if (slbe->large) -		eaddr_mask = 0xFFFFFF; + +	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; @@ -277,6 +329,7 @@ do_second:  	case 3:  	case 5:  	case 7: +	case 10:  		gpte->may_read = true;  		break;  	} @@ -287,30 +340,37 @@ do_second:  	/* Update PTE R and C bits, so the guest's swapper knows we used the  	 * page */ -	if (gpte->may_read) { -		/* Set the accessed flag */ +	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 (data && gpte->may_write) { -		/* Set the dirty flag -- XXX even if not writing */ +	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);  	} -	/* Write back into the PTEG */ -	if (pteg[i+1] != r) { -		pteg[i+1] = r; -		copy_to_user((void __user *)ptegp, pteg, sizeof(pteg)); -	} +	mutex_unlock(&vcpu->kvm->arch.hpt_mutex); -	if (!gpte->may_read) +	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;  } @@ -345,6 +405,21 @@ static void kvmppc_mmu_book3s_64_slbmte(struct kvm_vcpu *vcpu, u64 rs, u64 rb)  	slbe->nx    = (rs & SLB_VSID_N) ? 1 : 0;  	slbe->class = (rs & SLB_VSID_C) ? 1 : 0; +	slbe->base_page_size = MMU_PAGE_4K; +	if (slbe->large) { +		if (vcpu->arch.hflags & BOOK3S_HFLAG_MULTI_PGSIZE) { +			switch (rs & SLB_VSID_LP) { +			case SLB_VSID_LP_00: +				slbe->base_page_size = MMU_PAGE_16M; +				break; +			case SLB_VSID_LP_01: +				slbe->base_page_size = MMU_PAGE_64K; +				break; +			} +		} else +			slbe->base_page_size = MMU_PAGE_16M; +	} +  	slbe->orige = rb & (ESID_MASK | SLB_ESID_V);  	slbe->origv = rs; @@ -410,7 +485,7 @@ static void kvmppc_mmu_book3s_64_slbia(struct kvm_vcpu *vcpu)  		vcpu->arch.slb[i].origv = 0;  	} -	if (vcpu->arch.shared->msr & MSR_IR) { +	if (kvmppc_get_msr(vcpu) & MSR_IR) {  		kvmppc_mmu_flush_segments(vcpu);  		kvmppc_mmu_map_segment(vcpu, kvmppc_get_pc(vcpu));  	} @@ -460,13 +535,44 @@ static void kvmppc_mmu_book3s_64_tlbie(struct kvm_vcpu *vcpu, ulong va,  				       bool large)  {  	u64 mask = 0xFFFFFFFFFULL; +	long i; +	struct kvm_vcpu *v;  	dprintk("KVM MMU: tlbie(0x%lx)\n", va); -	if (large) -		mask = 0xFFFFFF000ULL; -	kvmppc_mmu_pte_vflush(vcpu, va >> 12, mask); +	/* +	 * The tlbie instruction changed behaviour starting with +	 * POWER6.  POWER6 and later don't have the large page flag +	 * in the instruction but in the RB value, along with bits +	 * indicating page and segment sizes. +	 */ +	if (vcpu->arch.hflags & BOOK3S_HFLAG_NEW_TLBIE) { +		/* POWER6 or later */ +		if (va & 1) {		/* L bit */ +			if ((va & 0xf000) == 0x1000) +				mask = 0xFFFFFFFF0ULL;	/* 64k page */ +			else +				mask = 0xFFFFFF000ULL;	/* 16M page */ +		} +	} else { +		/* older processors, e.g. PPC970 */ +		if (large) +			mask = 0xFFFFFF000ULL; +	} +	/* flush this VA on all vcpus */ +	kvm_for_each_vcpu(i, v, vcpu->kvm) +		kvmppc_mmu_pte_vflush(v, va >> 12, mask); +} + +#ifdef CONFIG_PPC_64K_PAGES +static int segment_contains_magic_page(struct kvm_vcpu *vcpu, ulong esid) +{ +	ulong mp_ea = vcpu->arch.magic_page_ea; + +	return mp_ea && !(kvmppc_get_msr(vcpu) & MSR_PR) && +		(mp_ea >> SID_SHIFT) == esid;  } +#endif  static int kvmppc_mmu_book3s_64_esid_to_vsid(struct kvm_vcpu *vcpu, ulong esid,  					     u64 *vsid) @@ -475,11 +581,14 @@ static int kvmppc_mmu_book3s_64_esid_to_vsid(struct kvm_vcpu *vcpu, ulong esid,  	struct kvmppc_slb *slb;  	u64 gvsid = esid;  	ulong mp_ea = vcpu->arch.magic_page_ea; +	int pagesize = MMU_PAGE_64K; +	u64 msr = kvmppc_get_msr(vcpu); -	if (vcpu->arch.shared->msr & (MSR_DR|MSR_IR)) { +	if (msr & (MSR_DR|MSR_IR)) {  		slb = kvmppc_mmu_book3s_64_find_slbe(vcpu, ea);  		if (slb) {  			gvsid = slb->vsid; +			pagesize = slb->base_page_size;  			if (slb->tb) {  				gvsid <<= SID_SHIFT_1T - SID_SHIFT;  				gvsid |= esid & ((1ul << (SID_SHIFT_1T - SID_SHIFT)) - 1); @@ -488,37 +597,50 @@ static int kvmppc_mmu_book3s_64_esid_to_vsid(struct kvm_vcpu *vcpu, ulong esid,  		}  	} -	switch (vcpu->arch.shared->msr & (MSR_DR|MSR_IR)) { +	switch (msr & (MSR_DR|MSR_IR)) {  	case 0: -		*vsid = VSID_REAL | esid; +		gvsid = VSID_REAL | esid;  		break;  	case MSR_IR: -		*vsid = VSID_REAL_IR | gvsid; +		gvsid |= VSID_REAL_IR;  		break;  	case MSR_DR: -		*vsid = VSID_REAL_DR | gvsid; +		gvsid |= VSID_REAL_DR;  		break;  	case MSR_DR|MSR_IR:  		if (!slb)  			goto no_slb; -		*vsid = gvsid;  		break;  	default:  		BUG();  		break;  	} -	if (vcpu->arch.shared->msr & MSR_PR) -		*vsid |= VSID_PR; +#ifdef CONFIG_PPC_64K_PAGES +	/* +	 * Mark this as a 64k segment if the host is using +	 * 64k pages, the host MMU supports 64k pages and +	 * the guest segment page size is >= 64k, +	 * but not if this segment contains the magic page. +	 */ +	if (pagesize >= MMU_PAGE_64K && +	    mmu_psize_defs[MMU_PAGE_64K].shift && +	    !segment_contains_magic_page(vcpu, esid)) +		gvsid |= VSID_64K; +#endif + +	if (kvmppc_get_msr(vcpu) & MSR_PR) +		gvsid |= VSID_PR; +	*vsid = gvsid;  	return 0;  no_slb:  	/* Catch magic page case */  	if (unlikely(mp_ea) &&  	    unlikely(esid == (mp_ea >> SID_SHIFT)) && -	    !(vcpu->arch.shared->msr & MSR_PR)) { +	    !(kvmppc_get_msr(vcpu) & MSR_PR)) {  		*vsid = VSID_REAL | esid;  		return 0;  	}  | 
