/* Return true if the IPA is out of the OA range */ staticbool check_output_size(u64 ipa, struct s1_walk_info *wi)
{ return wi->max_oa_bits < 48 && (ipa & GENMASK_ULL(47, wi->max_oa_bits));
}
/* Return the translation regime that applies to an AT instruction */ staticenum trans_regime compute_translation_regime(struct kvm_vcpu *vcpu, u32 op)
{ /* * We only get here from guest EL2, so the translation * regime AT applies to is solely defined by {E2H,TGE}.
*/ switch (op) { case OP_AT_S1E2R: case OP_AT_S1E2W: case OP_AT_S1E2A: return vcpu_el2_e2h_is_set(vcpu) ? TR_EL20 : TR_EL2; break; default: return (vcpu_el2_e2h_is_set(vcpu) &&
vcpu_el2_tge_is_set(vcpu)) ? TR_EL20 : TR_EL10;
}
}
if (!tbi && (u64)sign_extend64(va, 55) != va) goto addrsz;
va = (u64)sign_extend64(va, 55);
/* Let's put the MMU disabled case aside immediately */ switch (wi->regime) { case TR_EL10: /* * If dealing with the EL1&0 translation regime, 3 things * can disable the S1 translation: * * - HCR_EL2.DC = 1 * - HCR_EL2.{E2H,TGE} = {0,1} * - SCTLR_EL1.M = 0 * * The TGE part is interesting. If we have decided that this * is EL1&0, then it means that either {E2H,TGE} == {1,0} or * {0,x}, and we only need to test for TGE == 1.
*/ if (hcr & (HCR_DC | HCR_TGE)) {
wr->level = S1_MMU_DISABLED; break;
}
fallthrough; case TR_EL2: case TR_EL20: if (!(sctlr & SCTLR_ELx_M))
wr->level = S1_MMU_DISABLED; break;
}
if (wr->level == S1_MMU_DISABLED) { if (va >= BIT(kvm_get_pa_bits(vcpu->kvm))) goto addrsz;
/* 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;
}
}
staticvoid __mmu_config_restore(struct mmu_config *config)
{ /* * ARM errata 1165522 and 1530923 require TGE to be 1 before * we update the guest state.
*/ asm(ALTERNATIVE("nop", "isb", ARM64_WORKAROUND_SPECULATIVE_AT));
if (final != MEMATTR_NC) { /* Inherit RaWa hints form S1 */ if (transient) { switch (s1 & GENMASK(3, 2)) { case MEMATTR_Wt:
final = 0; break; case MEMATTR_Wb:
final = MEMATTR_NC; break;
}
}
/* If S2 has failed to translate, report the damage */ if (tr->esr) {
par = SYS_PAR_EL1_RES1;
par |= SYS_PAR_EL1_F;
par |= SYS_PAR_EL1_S;
par |= FIELD_PREP(SYS_PAR_EL1_FST, tr->esr); return par;
}
if (__vcpu_sys_reg(vcpu, HCR_EL2) & HCR_FWB) { if (!kvm_has_feat(vcpu->kvm, ID_AA64PFR2_EL1, MTEPERM, IMP))
s2_memattr &= ~BIT(3);
/* Combination of R_VRJSW and R_RHWZM */ switch (s2_memattr) { case 0b0101: if (MEMATTR_IS_DEVICE(s1_parattr))
final_attr = s1_parattr; else
final_attr = MEMATTR(NC, NC); break; case 0b0110: case 0b1110:
final_attr = MEMATTR(WbRaWa, WbRaWa); break; case 0b0111: case 0b1111: /* Preserve S1 attribute */
final_attr = s1_parattr; break; case 0b0100: case 0b1100: case 0b1101: /* Reserved, do something non-silly */
final_attr = s1_parattr; break; default: /* * MemAttr[2]=0, Device from S2. * * FWB does not influence the way that stage 1 * memory types and attributes are combined * with stage 2 Device type and attributes.
*/
final_attr = min(s2_memattr_to_attr(s2_memattr),
s1_parattr);
}
} else { /* Combination of R_HMNDG, R_TNHFM and R_GQFSF */
u8 s2_parattr = s2_memattr_to_attr(s2_memattr);
if (MEMATTR_IS_DEVICE(s1_parattr) ||
MEMATTR_IS_DEVICE(s2_parattr)) {
final_attr = min(s1_parattr, s2_parattr);
} else { /* At this stage, this is memory vs memory */
final_attr = combine_s1_s2_attr(s1_parattr & 0xf,
s2_parattr & 0xf);
final_attr |= combine_s1_s2_attr(s1_parattr >> 4,
s2_parattr >> 4) << 4;
}
}
if (wr->failed) {
par = SYS_PAR_EL1_RES1;
par |= SYS_PAR_EL1_F;
par |= FIELD_PREP(SYS_PAR_EL1_FST, wr->fst);
par |= wr->ptw ? SYS_PAR_EL1_PTW : 0;
par |= wr->s2 ? SYS_PAR_EL1_S : 0;
} elseif (wr->level == S1_MMU_DISABLED) { /* MMU off or HCR_EL2.DC == 1 */
par = SYS_PAR_EL1_NSE;
par |= wr->pa & GENMASK_ULL(47, 12);
if (regime == TR_EL10 &&
(__vcpu_sys_reg(vcpu, HCR_EL2) & HCR_DC)) {
par |= FIELD_PREP(SYS_PAR_EL1_ATTR,
MEMATTR(WbRaWa, WbRaWa));
par |= FIELD_PREP(SYS_PAR_EL1_SH, ATTR_NSH);
} else {
par |= FIELD_PREP(SYS_PAR_EL1_ATTR, 0); /* nGnRnE */
par |= FIELD_PREP(SYS_PAR_EL1_SH, ATTR_OSH);
}
} else {
u64 mair, sctlr;
u8 sh;
ret = setup_s1_walk(vcpu, &wi, &wr, vaddr); if (ret) goto compute_par;
if (wr.level == S1_MMU_DISABLED) goto compute_par;
idx = srcu_read_lock(&vcpu->kvm->srcu);
ret = walk_s1(vcpu, &wi, &wr, vaddr);
srcu_read_unlock(&vcpu->kvm->srcu, idx);
if (ret) goto compute_par;
compute_s1_permissions(vcpu, &wi, &wr);
switch (op) { case OP_AT_S1E1RP: case OP_AT_S1E1R: case OP_AT_S1E2R:
perm_fail = !wr.pr; break; case OP_AT_S1E1WP: case OP_AT_S1E1W: case OP_AT_S1E2W:
perm_fail = !wr.pw; break; case OP_AT_S1E0R:
perm_fail = !wr.ur; break; case OP_AT_S1E0W:
perm_fail = !wr.uw; break; case OP_AT_S1E1A: case OP_AT_S1E2A: break; default:
BUG();
}
if (perm_fail)
fail_s1_walk(&wr, ESR_ELx_FSC_PERM_L(wr.level), false);
/* * Return the PAR_EL1 value as the result of a valid translation. * * If the translation is unsuccessful, the value may only contain * PAR_EL1.F, and cannot be taken at face value. It isn't an * indication of the translation having failed, only that the fast * path did not succeed, *unless* it indicates a S1 permission or * access fault.
*/ static u64 __kvm_at_s1e01_fast(struct kvm_vcpu *vcpu, u32 op, u64 vaddr)
{ struct mmu_config config; struct kvm_s2_mmu *mmu; bool fail;
u64 par;
par = SYS_PAR_EL1_F;
/* * We've trapped, so everything is live on the CPU. As we will * be switching contexts behind everybody's back, disable * interrupts while holding the mmu lock.
*/
guard(write_lock_irqsave)(&vcpu->kvm->mmu_lock);
/* * If HCR_EL2.{E2H,TGE} == {1,1}, the MMU context is already * the right one (as we trapped from vEL2). If not, save the * full MMU context.
*/ if (vcpu_el2_e2h_is_set(vcpu) && vcpu_el2_tge_is_set(vcpu)) goto skip_mmu_switch;
/* * Obtaining the S2 MMU for a L2 is horribly racy, and we may not * find it (recycled by another vcpu, for example). When this * happens, admit defeat immediately and use the SW (slow) path.
*/
mmu = lookup_s2_mmu(vcpu); if (!mmu) return par;
/* * If PAR_EL1 reports that AT failed on a S1 permission or access * fault, we know for sure that the PTW was able to walk the S1 * tables and there's nothing else to do. * * If AT failed for any other reason, then we must walk the guest S1 * to emulate the instruction.
*/ if ((par & SYS_PAR_EL1_F) &&
!par_check_s1_perm_fault(par) &&
!par_check_s1_access_fault(par))
par = handle_at_slow(vcpu, op, vaddr);
/* * We've trapped, so everything is live on the CPU. As we will be * switching context behind everybody's back, disable interrupts...
*/
scoped_guard(write_lock_irqsave, &vcpu->kvm->mmu_lock) {
u64 val, hcr; bool fail;
val = hcr = read_sysreg(hcr_el2);
val &= ~HCR_TGE;
val |= HCR_VM;
if (!vcpu_el2_e2h_is_set(vcpu))
val |= HCR_NV | HCR_NV1;
write_sysreg_hcr(val);
isb();
par = SYS_PAR_EL1_F;
switch (op) { case OP_AT_S1E2R:
fail = __kvm_at(OP_AT_S1E1R, vaddr); break; case OP_AT_S1E2W:
fail = __kvm_at(OP_AT_S1E1W, vaddr); break; case OP_AT_S1E2A:
fail = __kvm_at(OP_AT_S1E1A, vaddr); break; default:
WARN_ON_ONCE(1);
fail = true;
}
isb();
if (!fail)
par = read_sysreg_par();
write_sysreg_hcr(hcr);
isb();
}
/* We failed the translation, let's replay it in slow motion */ if ((par & SYS_PAR_EL1_F) && !par_check_s1_perm_fault(par))
par = handle_at_slow(vcpu, op, vaddr);
/* Do the stage-1 translation */ switch (op) { case OP_AT_S12E1R:
op = OP_AT_S1E1R;
write = false; break; case OP_AT_S12E1W:
op = OP_AT_S1E1W;
write = true; break; case OP_AT_S12E0R:
op = OP_AT_S1E0R;
write = false; break; case OP_AT_S12E0W:
op = OP_AT_S1E0W;
write = true; break; default:
WARN_ON_ONCE(1); return;
}
__kvm_at_s1e01(vcpu, op, vaddr);
par = vcpu_read_sys_reg(vcpu, PAR_EL1); if (par & SYS_PAR_EL1_F) return;
/* * If we only have a single stage of translation (EL2&0), exit * early. Same thing if {VM,DC}=={0,0}.
*/ if (compute_translation_regime(vcpu, op) == TR_EL20 ||
!(vcpu_read_sys_reg(vcpu, HCR_EL2) & (HCR_VM | HCR_DC))) return;
/* Do the stage-2 translation */
ipa = (par & GENMASK_ULL(47, 12)) | (vaddr & GENMASK_ULL(11, 0));
out.esr = 0;
ret = kvm_walk_nested_s2(vcpu, ipa, &out); if (ret < 0) return;
par = compute_par_s12(vcpu, par, &out);
vcpu_write_sys_reg(vcpu, par, PAR_EL1);
}
/* * Translate a VA for a given EL in a given translation regime, with * or without PAN. This requires wi->{regime, as_el0, pan} to be * set. The rest of the wi and wr should be 0-initialised.
*/ int __kvm_translate_va(struct kvm_vcpu *vcpu, struct s1_walk_info *wi, struct s1_walk_result *wr, u64 va)
{ int ret;
ret = setup_s1_walk(vcpu, wi, wr, va); if (ret) return ret;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.