arm64: KVM: trap VM system registers until MMU and caches are ON
authorMarc Zyngier <marc.zyngier@arm.com>
Tue, 14 Jan 2014 18:00:55 +0000 (18:00 +0000)
committerMarc Zyngier <marc.zyngier@arm.com>
Mon, 3 Mar 2014 01:15:21 +0000 (01:15 +0000)
In order to be able to detect the point where the guest enables
its MMU and caches, trap all the VM related system registers.

Once we see the guest enabling both the MMU and the caches, we
can go back to a saner mode of operation, which is to leave these
registers in complete control of the guest.

Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
arch/arm64/include/asm/kvm_arm.h
arch/arm64/include/asm/kvm_asm.h
arch/arm64/kvm/sys_regs.c

index 0eb39865537839c202879251fbb60f919328d58e..00fbaa75dc7bcf55541c7bf00af609dfb63529a9 100644 (file)
@@ -62,6 +62,7 @@
  * RW:         64bit by default, can be overriden for 32bit VMs
  * TAC:                Trap ACTLR
  * TSC:                Trap SMC
+ * TVM:                Trap VM ops (until M+C set in SCTLR_EL1)
  * TSW:                Trap cache operations by set/way
  * TWE:                Trap WFE
  * TWI:                Trap WFI
@@ -74,7 +75,7 @@
  * SWIO:       Turn set/way invalidates into set/way clean+invalidate
  */
 #define HCR_GUEST_FLAGS (HCR_TSC | HCR_TSW | HCR_TWE | HCR_TWI | HCR_VM | \
-                        HCR_BSU_IS | HCR_FB | HCR_TAC | \
+                        HCR_TVM | HCR_BSU_IS | HCR_FB | HCR_TAC | \
                         HCR_AMO | HCR_IMO | HCR_FMO | \
                         HCR_SWIO | HCR_TIDCP | HCR_RW)
 #define HCR_VIRT_EXCP_MASK (HCR_VA | HCR_VI | HCR_VF)
index b25763bc0ec4977a4eca6139ef7d84b3a15eeea0..9fcd54b1e16d8800ffec64a249cb55739c88b058 100644 (file)
@@ -79,7 +79,8 @@
 #define c13_TID_URW    (TPIDR_EL0 * 2) /* Thread ID, User R/W */
 #define c13_TID_URO    (TPIDRRO_EL0 * 2)/* Thread ID, User R/O */
 #define c13_TID_PRIV   (TPIDR_EL1 * 2) /* Thread ID, Privileged */
-#define c10_AMAIR      (AMAIR_EL1 * 2) /* Aux Memory Attr Indirection Reg */
+#define c10_AMAIR0     (AMAIR_EL1 * 2) /* Aux Memory Attr Indirection Reg */
+#define c10_AMAIR1     (c10_AMAIR0 + 1)/* Aux Memory Attr Indirection Reg */
 #define c14_CNTKCTL    (CNTKCTL_EL1 * 2) /* Timer Control Register (PL1) */
 #define NR_CP15_REGS   (NR_SYS_REGS * 2)
 
index bf03e0fadf1f533c7f7fd4208ca36913685e618d..2097e5ecba42a6342537f447f67599b1824a5be7 100644 (file)
@@ -120,6 +120,46 @@ done:
        return true;
 }
 
+/*
+ * Generic accessor for VM registers. Only called as long as HCR_TVM
+ * is set.
+ */
+static bool access_vm_reg(struct kvm_vcpu *vcpu,
+                         const struct sys_reg_params *p,
+                         const struct sys_reg_desc *r)
+{
+       unsigned long val;
+
+       BUG_ON(!p->is_write);
+
+       val = *vcpu_reg(vcpu, p->Rt);
+       if (!p->is_aarch32) {
+               vcpu_sys_reg(vcpu, r->reg) = val;
+       } else {
+               vcpu_cp15(vcpu, r->reg) = val & 0xffffffffUL;
+               if (!p->is_32bit)
+                       vcpu_cp15(vcpu, r->reg + 1) = val >> 32;
+       }
+       return true;
+}
+
+/*
+ * SCTLR_EL1 accessor. Only called as long as HCR_TVM is set.  If the
+ * guest enables the MMU, we stop trapping the VM sys_regs and leave
+ * it in complete control of the caches.
+ */
+static bool access_sctlr(struct kvm_vcpu *vcpu,
+                        const struct sys_reg_params *p,
+                        const struct sys_reg_desc *r)
+{
+       access_vm_reg(vcpu, p, r);
+
+       if (vcpu_has_cache_enabled(vcpu))       /* MMU+Caches enabled? */
+               vcpu->arch.hcr_el2 &= ~HCR_TVM;
+
+       return true;
+}
+
 /*
  * We could trap ID_DFR0 and tell the guest we don't support performance
  * monitoring.  Unfortunately the patch to make the kernel check ID_DFR0 was
@@ -185,32 +225,32 @@ static const struct sys_reg_desc sys_reg_descs[] = {
          NULL, reset_mpidr, MPIDR_EL1 },
        /* SCTLR_EL1 */
        { Op0(0b11), Op1(0b000), CRn(0b0001), CRm(0b0000), Op2(0b000),
-         NULL, reset_val, SCTLR_EL1, 0x00C50078 },
+         access_sctlr, reset_val, SCTLR_EL1, 0x00C50078 },
        /* CPACR_EL1 */
        { Op0(0b11), Op1(0b000), CRn(0b0001), CRm(0b0000), Op2(0b010),
          NULL, reset_val, CPACR_EL1, 0 },
        /* TTBR0_EL1 */
        { Op0(0b11), Op1(0b000), CRn(0b0010), CRm(0b0000), Op2(0b000),
-         NULL, reset_unknown, TTBR0_EL1 },
+         access_vm_reg, reset_unknown, TTBR0_EL1 },
        /* TTBR1_EL1 */
        { Op0(0b11), Op1(0b000), CRn(0b0010), CRm(0b0000), Op2(0b001),
-         NULL, reset_unknown, TTBR1_EL1 },
+         access_vm_reg, reset_unknown, TTBR1_EL1 },
        /* TCR_EL1 */
        { Op0(0b11), Op1(0b000), CRn(0b0010), CRm(0b0000), Op2(0b010),
-         NULL, reset_val, TCR_EL1, 0 },
+         access_vm_reg, reset_val, TCR_EL1, 0 },
 
        /* AFSR0_EL1 */
        { Op0(0b11), Op1(0b000), CRn(0b0101), CRm(0b0001), Op2(0b000),
-         NULL, reset_unknown, AFSR0_EL1 },
+         access_vm_reg, reset_unknown, AFSR0_EL1 },
        /* AFSR1_EL1 */
        { Op0(0b11), Op1(0b000), CRn(0b0101), CRm(0b0001), Op2(0b001),
-         NULL, reset_unknown, AFSR1_EL1 },
+         access_vm_reg, reset_unknown, AFSR1_EL1 },
        /* ESR_EL1 */
        { Op0(0b11), Op1(0b000), CRn(0b0101), CRm(0b0010), Op2(0b000),
-         NULL, reset_unknown, ESR_EL1 },
+         access_vm_reg, reset_unknown, ESR_EL1 },
        /* FAR_EL1 */
        { Op0(0b11), Op1(0b000), CRn(0b0110), CRm(0b0000), Op2(0b000),
-         NULL, reset_unknown, FAR_EL1 },
+         access_vm_reg, reset_unknown, FAR_EL1 },
        /* PAR_EL1 */
        { Op0(0b11), Op1(0b000), CRn(0b0111), CRm(0b0100), Op2(0b000),
          NULL, reset_unknown, PAR_EL1 },
@@ -224,17 +264,17 @@ static const struct sys_reg_desc sys_reg_descs[] = {
 
        /* MAIR_EL1 */
        { Op0(0b11), Op1(0b000), CRn(0b1010), CRm(0b0010), Op2(0b000),
-         NULL, reset_unknown, MAIR_EL1 },
+         access_vm_reg, reset_unknown, MAIR_EL1 },
        /* AMAIR_EL1 */
        { Op0(0b11), Op1(0b000), CRn(0b1010), CRm(0b0011), Op2(0b000),
-         NULL, reset_amair_el1, AMAIR_EL1 },
+         access_vm_reg, reset_amair_el1, AMAIR_EL1 },
 
        /* VBAR_EL1 */
        { Op0(0b11), Op1(0b000), CRn(0b1100), CRm(0b0000), Op2(0b000),
          NULL, reset_val, VBAR_EL1, 0 },
        /* CONTEXTIDR_EL1 */
        { Op0(0b11), Op1(0b000), CRn(0b1101), CRm(0b0000), Op2(0b001),
-         NULL, reset_val, CONTEXTIDR_EL1, 0 },
+         access_vm_reg, reset_val, CONTEXTIDR_EL1, 0 },
        /* TPIDR_EL1 */
        { Op0(0b11), Op1(0b000), CRn(0b1101), CRm(0b0000), Op2(0b100),
          NULL, reset_unknown, TPIDR_EL1 },
@@ -305,14 +345,32 @@ static const struct sys_reg_desc sys_reg_descs[] = {
          NULL, reset_val, FPEXC32_EL2, 0x70 },
 };
 
-/* Trapped cp15 registers */
+/*
+ * Trapped cp15 registers. TTBR0/TTBR1 get a double encoding,
+ * depending on the way they are accessed (as a 32bit or a 64bit
+ * register).
+ */
 static const struct sys_reg_desc cp15_regs[] = {
+       { Op1( 0), CRn( 0), CRm( 2), Op2( 0), access_vm_reg, NULL, c2_TTBR0 },
+       { Op1( 0), CRn( 1), CRm( 0), Op2( 0), access_sctlr, NULL, c1_SCTLR },
+       { Op1( 0), CRn( 2), CRm( 0), Op2( 0), access_vm_reg, NULL, c2_TTBR0 },
+       { Op1( 0), CRn( 2), CRm( 0), Op2( 1), access_vm_reg, NULL, c2_TTBR1 },
+       { Op1( 0), CRn( 2), CRm( 0), Op2( 2), access_vm_reg, NULL, c2_TTBCR },
+       { Op1( 0), CRn( 3), CRm( 0), Op2( 0), access_vm_reg, NULL, c3_DACR },
+       { Op1( 0), CRn( 5), CRm( 0), Op2( 0), access_vm_reg, NULL, c5_DFSR },
+       { Op1( 0), CRn( 5), CRm( 0), Op2( 1), access_vm_reg, NULL, c5_IFSR },
+       { Op1( 0), CRn( 5), CRm( 1), Op2( 0), access_vm_reg, NULL, c5_ADFSR },
+       { Op1( 0), CRn( 5), CRm( 1), Op2( 1), access_vm_reg, NULL, c5_AIFSR },
+       { Op1( 0), CRn( 6), CRm( 0), Op2( 0), access_vm_reg, NULL, c6_DFAR },
+       { Op1( 0), CRn( 6), CRm( 0), Op2( 2), access_vm_reg, NULL, c6_IFAR },
+
        /*
         * DC{C,I,CI}SW operations:
         */
        { Op1( 0), CRn( 7), CRm( 6), Op2( 2), access_dcsw },
        { Op1( 0), CRn( 7), CRm(10), Op2( 2), access_dcsw },
        { Op1( 0), CRn( 7), CRm(14), Op2( 2), access_dcsw },
+
        { Op1( 0), CRn( 9), CRm(12), Op2( 0), pm_fake },
        { Op1( 0), CRn( 9), CRm(12), Op2( 1), pm_fake },
        { Op1( 0), CRn( 9), CRm(12), Op2( 2), pm_fake },
@@ -326,6 +384,14 @@ static const struct sys_reg_desc cp15_regs[] = {
        { Op1( 0), CRn( 9), CRm(14), Op2( 0), pm_fake },
        { Op1( 0), CRn( 9), CRm(14), Op2( 1), pm_fake },
        { Op1( 0), CRn( 9), CRm(14), Op2( 2), pm_fake },
+
+       { Op1( 0), CRn(10), CRm( 2), Op2( 0), access_vm_reg, NULL, c10_PRRR },
+       { Op1( 0), CRn(10), CRm( 2), Op2( 1), access_vm_reg, NULL, c10_NMRR },
+       { Op1( 0), CRn(10), CRm( 3), Op2( 0), access_vm_reg, NULL, c10_AMAIR0 },
+       { Op1( 0), CRn(10), CRm( 3), Op2( 1), access_vm_reg, NULL, c10_AMAIR1 },
+       { Op1( 0), CRn(13), CRm( 0), Op2( 1), access_vm_reg, NULL, c13_CID },
+
+       { Op1( 1), CRn( 0), CRm( 2), Op2( 0), access_vm_reg, NULL, c2_TTBR1 },
 };
 
 /* Target specific emulation tables */