arm64: KVM: trap VM system registers until MMU and caches are ON
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>
This commit is contained in:
Родитель
2072d29c46
Коммит
4d44923b17
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
Загрузка…
Ссылка в новой задаче