mirror of
git://git.yoctoproject.org/linux-yocto.git
synced 2026-01-27 12:47:24 +01:00
KVM: arm64: Report faults from S1 walk setup at the expected start level
Translation faults from TTBR must be reported on the start level, and not level-0. Enforcing this requires moving quite a lot of code around so that the start level can be computed early enough that it is usable. Reviewed-by: Oliver Upton <oliver.upton@linux.dev> Signed-off-by: Marc Zyngier <maz@kernel.org>
This commit is contained in:
parent
5da3a3b27a
commit
dabf9f73fe
|
|
@ -154,9 +154,6 @@ static int setup_s1_walk(struct kvm_vcpu *vcpu, struct s1_walk_info *wi,
|
|||
|
||||
va55 = va & BIT(55);
|
||||
|
||||
if (wi->regime == TR_EL2 && va55)
|
||||
goto addrsz;
|
||||
|
||||
wi->s2 = wi->regime == TR_EL10 && (hcr & (HCR_VM | HCR_DC));
|
||||
|
||||
switch (wi->regime) {
|
||||
|
|
@ -179,6 +176,46 @@ static int setup_s1_walk(struct kvm_vcpu *vcpu, struct s1_walk_info *wi,
|
|||
BUG();
|
||||
}
|
||||
|
||||
/* Someone was silly enough to encode TG0/TG1 differently */
|
||||
if (va55 && wi->regime != TR_EL2) {
|
||||
wi->txsz = FIELD_GET(TCR_T1SZ_MASK, tcr);
|
||||
tg = FIELD_GET(TCR_TG1_MASK, tcr);
|
||||
|
||||
switch (tg << TCR_TG1_SHIFT) {
|
||||
case TCR_TG1_4K:
|
||||
wi->pgshift = 12; break;
|
||||
case TCR_TG1_16K:
|
||||
wi->pgshift = 14; break;
|
||||
case TCR_TG1_64K:
|
||||
default: /* IMPDEF: treat any other value as 64k */
|
||||
wi->pgshift = 16; break;
|
||||
}
|
||||
} else {
|
||||
wi->txsz = FIELD_GET(TCR_T0SZ_MASK, tcr);
|
||||
tg = FIELD_GET(TCR_TG0_MASK, tcr);
|
||||
|
||||
switch (tg << TCR_TG0_SHIFT) {
|
||||
case TCR_TG0_4K:
|
||||
wi->pgshift = 12; break;
|
||||
case TCR_TG0_16K:
|
||||
wi->pgshift = 14; break;
|
||||
case TCR_TG0_64K:
|
||||
default: /* IMPDEF: treat any other value as 64k */
|
||||
wi->pgshift = 16; break;
|
||||
}
|
||||
}
|
||||
|
||||
wi->pa52bit = has_52bit_pa(vcpu, wi, tcr);
|
||||
|
||||
ia_bits = get_ia_size(wi);
|
||||
|
||||
/* AArch64.S1StartLevel() */
|
||||
stride = wi->pgshift - 3;
|
||||
wi->sl = 3 - (((ia_bits - 1) - wi->pgshift) / stride);
|
||||
|
||||
if (wi->regime == TR_EL2 && va55)
|
||||
goto addrsz;
|
||||
|
||||
tbi = (wi->regime == TR_EL2 ?
|
||||
FIELD_GET(TCR_EL2_TBI, tcr) :
|
||||
(va55 ?
|
||||
|
|
@ -248,46 +285,15 @@ static int setup_s1_walk(struct kvm_vcpu *vcpu, struct s1_walk_info *wi,
|
|||
/* R_BVXDG */
|
||||
wi->hpd |= (wi->poe || wi->e0poe);
|
||||
|
||||
/* Someone was silly enough to encode TG0/TG1 differently */
|
||||
if (va55) {
|
||||
wi->txsz = FIELD_GET(TCR_T1SZ_MASK, tcr);
|
||||
tg = FIELD_GET(TCR_TG1_MASK, tcr);
|
||||
|
||||
switch (tg << TCR_TG1_SHIFT) {
|
||||
case TCR_TG1_4K:
|
||||
wi->pgshift = 12; break;
|
||||
case TCR_TG1_16K:
|
||||
wi->pgshift = 14; break;
|
||||
case TCR_TG1_64K:
|
||||
default: /* IMPDEF: treat any other value as 64k */
|
||||
wi->pgshift = 16; break;
|
||||
}
|
||||
} else {
|
||||
wi->txsz = FIELD_GET(TCR_T0SZ_MASK, tcr);
|
||||
tg = FIELD_GET(TCR_TG0_MASK, tcr);
|
||||
|
||||
switch (tg << TCR_TG0_SHIFT) {
|
||||
case TCR_TG0_4K:
|
||||
wi->pgshift = 12; break;
|
||||
case TCR_TG0_16K:
|
||||
wi->pgshift = 14; break;
|
||||
case TCR_TG0_64K:
|
||||
default: /* IMPDEF: treat any other value as 64k */
|
||||
wi->pgshift = 16; break;
|
||||
}
|
||||
}
|
||||
|
||||
/* R_PLCGL, R_YXNYW */
|
||||
if (!kvm_has_feat_enum(vcpu->kvm, ID_AA64MMFR2_EL1, ST, 48_47)) {
|
||||
if (wi->txsz > 39)
|
||||
goto transfault_l0;
|
||||
goto transfault;
|
||||
} else {
|
||||
if (wi->txsz > 48 || (BIT(wi->pgshift) == SZ_64K && wi->txsz > 47))
|
||||
goto transfault_l0;
|
||||
goto transfault;
|
||||
}
|
||||
|
||||
wi->pa52bit = has_52bit_pa(vcpu, wi, tcr);
|
||||
|
||||
/* R_GTJBY, R_SXWGM */
|
||||
switch (BIT(wi->pgshift)) {
|
||||
case SZ_4K:
|
||||
|
|
@ -300,28 +306,22 @@ static int setup_s1_walk(struct kvm_vcpu *vcpu, struct s1_walk_info *wi,
|
|||
}
|
||||
|
||||
if ((lva && wi->txsz < 12) || (!lva && wi->txsz < 16))
|
||||
goto transfault_l0;
|
||||
|
||||
ia_bits = get_ia_size(wi);
|
||||
goto transfault;
|
||||
|
||||
/* R_YYVYV, I_THCZK */
|
||||
if ((!va55 && va > GENMASK(ia_bits - 1, 0)) ||
|
||||
(va55 && va < GENMASK(63, ia_bits)))
|
||||
goto transfault_l0;
|
||||
goto transfault;
|
||||
|
||||
/* I_ZFSYQ */
|
||||
if (wi->regime != TR_EL2 &&
|
||||
(tcr & (va55 ? TCR_EPD1_MASK : TCR_EPD0_MASK)))
|
||||
goto transfault_l0;
|
||||
goto transfault;
|
||||
|
||||
/* R_BNDVG and following statements */
|
||||
if (kvm_has_feat(vcpu->kvm, ID_AA64MMFR2_EL1, E0PD, IMP) &&
|
||||
wi->as_el0 && (tcr & (va55 ? TCR_E0PD1 : TCR_E0PD0)))
|
||||
goto transfault_l0;
|
||||
|
||||
/* AArch64.S1StartLevel() */
|
||||
stride = wi->pgshift - 3;
|
||||
wi->sl = 3 - (((ia_bits - 1) - wi->pgshift) / stride);
|
||||
goto transfault;
|
||||
|
||||
ps = (wi->regime == TR_EL2 ?
|
||||
FIELD_GET(TCR_EL2_PS_MASK, tcr) : FIELD_GET(TCR_IPS_MASK, tcr));
|
||||
|
|
@ -351,12 +351,17 @@ static int setup_s1_walk(struct kvm_vcpu *vcpu, struct s1_walk_info *wi,
|
|||
|
||||
return 0;
|
||||
|
||||
addrsz: /* Address Size Fault level 0 */
|
||||
addrsz:
|
||||
/*
|
||||
* Address Size Fault level 0 to indicate it comes from TTBR.
|
||||
* yes, this is an oddity.
|
||||
*/
|
||||
fail_s1_walk(wr, ESR_ELx_FSC_ADDRSZ_L(0), false);
|
||||
return -EFAULT;
|
||||
|
||||
transfault_l0: /* Translation Fault level 0 */
|
||||
fail_s1_walk(wr, ESR_ELx_FSC_FAULT_L(0), false);
|
||||
transfault:
|
||||
/* Translation Fault on start level */
|
||||
fail_s1_walk(wr, ESR_ELx_FSC_FAULT_L(wi->sl), false);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user