[PATCH 3/5] kvm/e500: utilize shadow id

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



BOOKE consider ID '0' as a special ID,
which is supposed to share to all other IDs.

But since conventional OSes use ID '0' map privileged pages,
we can utilize extra PID1 to map guest '0' ID to shadow none '0' IDs.

We alway translate guest's PID to shadow and write it to hardware PID.
And in guest supervisor mode, we translate guest's '0' ID to shadow
and write it to hardware PID1.

Signed-off-by: Liu Yu <yu.liu@xxxxxxxxxxxxx>
---
 arch/powerpc/include/asm/kvm_ppc.h  |    1 +
 arch/powerpc/kernel/asm-offsets.c   |    1 +
 arch/powerpc/kvm/booke.h            |    4 ++++
 arch/powerpc/kvm/booke_interrupts.S |   11 +++++++++++
 arch/powerpc/kvm/e500_emulate.c     |   12 ++++++++----
 arch/powerpc/kvm/e500_tlb.c         |   32 +++++++++++++++++++++++++++++++-
 6 files changed, 56 insertions(+), 5 deletions(-)

diff --git a/arch/powerpc/include/asm/kvm_ppc.h b/arch/powerpc/include/asm/kvm_ppc.h
index 2c6ee34..40823c4 100644
--- a/arch/powerpc/include/asm/kvm_ppc.h
+++ b/arch/powerpc/include/asm/kvm_ppc.h
@@ -57,6 +57,7 @@ extern void kvmppc_emulate_dec(struct kvm_vcpu *vcpu);
 extern void kvmppc_mmu_map(struct kvm_vcpu *vcpu, u64 gvaddr, gpa_t gpaddr,
                            unsigned int gtlb_idx);
 extern void kvmppc_mmu_priv_switch(struct kvm_vcpu *vcpu, int usermode);
+extern void kvmppc_mmu_as_switch(struct kvm_vcpu *vcpu, unsigned int as);
 extern void kvmppc_mmu_switch_pid(struct kvm_vcpu *vcpu, u32 pid);
 extern void kvmppc_mmu_destroy(struct kvm_vcpu *vcpu);
 extern int kvmppc_mmu_dtlb_index(struct kvm_vcpu *vcpu, gva_t eaddr);
diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c
index 42fe4da..89d28df 100644
--- a/arch/powerpc/kernel/asm-offsets.c
+++ b/arch/powerpc/kernel/asm-offsets.c
@@ -379,6 +379,7 @@ int main(void)
 	DEFINE(VCPU_LAST_INST, offsetof(struct kvm_vcpu, arch.last_inst));
 	DEFINE(VCPU_FAULT_DEAR, offsetof(struct kvm_vcpu, arch.fault_dear));
 	DEFINE(VCPU_FAULT_ESR, offsetof(struct kvm_vcpu, arch.fault_esr));
+	DEFINE(VCPU_SWAP_PID, offsetof(struct kvm_vcpu, arch.swap_pid));
 #endif
 #ifdef CONFIG_44x
 	DEFINE(PGD_T_LOG2, PGD_T_LOG2);
diff --git a/arch/powerpc/kvm/booke.h b/arch/powerpc/kvm/booke.h
index d59bcca..96e6cc0 100644
--- a/arch/powerpc/kvm/booke.h
+++ b/arch/powerpc/kvm/booke.h
@@ -57,6 +57,10 @@ static inline void kvmppc_set_msr(struct kvm_vcpu *vcpu, u32 new_msr)
 	if ((new_msr & MSR_PR) != (vcpu->arch.msr & MSR_PR))
 		kvmppc_mmu_priv_switch(vcpu, new_msr & MSR_PR);
 
+	if ((new_msr & (MSR_IS | MSR_DS)) !=
+			(vcpu->arch.msr & (MSR_IS | MSR_DS)))
+		kvmppc_mmu_as_switch(vcpu, new_msr & (MSR_IS | MSR_DS));
+
 	vcpu->arch.msr = new_msr;
 
 	if (vcpu->arch.msr & MSR_WE) {
diff --git a/arch/powerpc/kvm/booke_interrupts.S b/arch/powerpc/kvm/booke_interrupts.S
index d0c6f84..12383fe 100644
--- a/arch/powerpc/kvm/booke_interrupts.S
+++ b/arch/powerpc/kvm/booke_interrupts.S
@@ -192,6 +192,12 @@ _GLOBAL(kvmppc_resume_host)
 	lwz	r3, VCPU_HOST_PID(r4)
 	mtspr	SPRN_PID, r3
 
+#ifdef CONFIG_E500
+	/* we cheat and know Linux doesn't use PID1 which is always 0 */
+	lis	r3, 0
+	mtspr	SPRN_PID1, r3
+#endif
+
 	/* Restore host IVPR before re-enabling interrupts. We cheat and know
 	 * that Linux IVPR is always 0xc0000000. */
 	lis	r3, 0xc000
@@ -350,6 +356,11 @@ lightweight_exit:
 	lwz	r3, VCPU_SHADOW_PID(r4)
 	mtspr	SPRN_PID, r3
 
+#ifdef CONFIG_E500
+	lwz	r3, VCPU_SWAP_PID(r4)
+	mtspr	SPRN_PID1, r3
+#endif
+
 #ifdef CONFIG_44x
 	iccci	0, 0 /* XXX hack */
 #endif
diff --git a/arch/powerpc/kvm/e500_emulate.c b/arch/powerpc/kvm/e500_emulate.c
index 3f76041..21a32ea 100644
--- a/arch/powerpc/kvm/e500_emulate.c
+++ b/arch/powerpc/kvm/e500_emulate.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 Freescale Semiconductor, Inc. All rights reserved.
+ * Copyright (C) 2008 - 2009 Freescale Semiconductor, Inc. All rights reserved.
  *
  * Author: Yu Liu, <yu.liu@xxxxxxxxxxxxx>
  *
@@ -76,10 +76,14 @@ int kvmppc_core_emulate_mtspr(struct kvm_vcpu *vcpu, int sprn, int rs)
 	int emulated = EMULATE_DONE;
 
 	switch (sprn) {
-	case SPRN_PID:
-		vcpu_e500->pid[0] = vcpu->arch.shadow_pid =
-			vcpu->arch.pid = vcpu->arch.gpr[rs];
+	case SPRN_PID: {
+		unsigned int as = !!(vcpu->arch.msr & (MSR_IS | MSR_DS));
+
+		vcpu_e500->pid[0] = vcpu->arch.pid = vcpu->arch.gpr[rs];
+		vcpu->arch.shadow_pid = kvmppc_e500_get_sid(vcpu_e500, as,
+						    get_cur_pid(vcpu));
 		break;
+	}
 	case SPRN_PID1:
 		vcpu_e500->pid[1] = vcpu->arch.gpr[rs]; break;
 	case SPRN_PID2:
diff --git a/arch/powerpc/kvm/e500_tlb.c b/arch/powerpc/kvm/e500_tlb.c
index e5c9211..d090d97 100644
--- a/arch/powerpc/kvm/e500_tlb.c
+++ b/arch/powerpc/kvm/e500_tlb.c
@@ -221,6 +221,10 @@ static inline void write_host_tlbe(struct kvmppc_vcpu_e500 *vcpu_e500,
 
 void kvmppc_e500_tlb_load(struct kvm_vcpu *vcpu, int cpu)
 {
+	struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu);
+
+	/* Shadow PID may be expired */
+	kvmppc_e500_update_spid(vcpu_e500);
 }
 
 void kvmppc_e500_tlb_put(struct kvm_vcpu *vcpu)
@@ -330,10 +334,14 @@ static inline void kvmppc_e500_setup_stlbe(struct kvmppc_vcpu_e500 *vcpu_e500,
 		u64 gvaddr, struct tlbe *stlbe)
 {
 	hpa_t hpaddr = page_to_phys(ref->page);
+	unsigned int stid;
+
+	stid = kvmppc_e500_get_sid(vcpu_e500, get_tlb_ts(gtlbe),
+				get_tlb_tid(gtlbe));
 
 	/* Force TS=1 IPROT=0 TSIZE=4KB for all guest mappings. */
 	stlbe->mas1 = MAS1_TSIZE(BOOKE_PAGESZ_4K)
-		| MAS1_TID(get_tlb_tid(gtlbe)) | MAS1_TS | MAS1_VALID;
+		| MAS1_TID(stid) | MAS1_TS | MAS1_VALID;
 	stlbe->mas2 = (gvaddr & MAS2_EPN)
 		| e500_shadow_mas2_attrib(gtlbe->mas2,
 				vcpu_e500->vcpu.arch.msr & MSR_PR);
@@ -401,11 +409,30 @@ static int kvmppc_e500_tlb1_map(struct kvmppc_vcpu_e500 *vcpu_e500,
  * proper permission bits. */
 void kvmppc_mmu_priv_switch(struct kvm_vcpu *vcpu, int usermode)
 {
+	struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu);
+
 	if (usermode) {
 		_tlbil_all();
+		/* clear PID for guest kernel mapping */
+		vcpu->arch.swap_pid = 0;
+	} else {
+		/* set PID for guest kernel mapping
+		 * We assume:
+		 * 1. AS  = 0 when enter supervise mode
+		 * 2. TID = 0 for supervise code/data mappings */
+		vcpu->arch.swap_pid = kvmppc_e500_get_sid(vcpu_e500, 0, 0);
 	}
 }
 
+void kvmppc_mmu_as_switch(struct kvm_vcpu *vcpu, u32 new_as)
+{
+	struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu);
+	u32 pid = get_cur_pid(vcpu);
+
+	vcpu->arch.shadow_pid = kvmppc_e500_get_sid(vcpu_e500, !!new_as, pid);
+	vcpu->arch.swap_pid = kvmppc_e500_get_sid(vcpu_e500, !!new_as, 0);
+}
+
 static inline int kvmppc_e500_gtlbe_invalidate(
 				struct kvmppc_vcpu_e500 *vcpu_e500,
 				int tlbsel, int esel)
@@ -715,6 +742,9 @@ void kvmppc_e500_tlb_setup(struct kvmppc_vcpu_e500 *vcpu_e500)
 	tlbe->mas2 = (0xe0004500 & 0xFFFFF000) | MAS2_I | MAS2_G;
 	tlbe->mas3 = (0xe0004500 & 0xFFFFF000) | E500_TLB_SUPER_PERM_MASK;
 	tlbe->mas7 = 0;
+
+	/* Setup shadow PID before start guest */
+	kvmppc_e500_update_spid(vcpu_e500);
 }
 
 int kvmppc_e500_tlb_init(struct kvmppc_vcpu_e500 *vcpu_e500)
-- 
1.5.4

--
To unsubscribe from this list: send the line "unsubscribe kvm-ppc" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [KVM Development]     [KVM ARM]     [KVM ia64]     [Linux Virtualization]     [Linux USB Devel]     [Linux Video]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux