|
@@ -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 */
|