KVM: arm64: nv: Make AT+PAN instruction aware of FEAT_PAN3
FEAT_PAN3 added a check for executable permissions to FEAT_PAN2.
This requires some extra checks, and forces the PAN-aware AT
instructions on the slow path.
Signed-off-by: Marc Zyngier <maz@kernel.org>
diff --git a/arch/arm64/kvm/at.c b/arch/arm64/kvm/at.c
index 8615f49..e838eca 100644
--- a/arch/arm64/kvm/at.c
+++ b/arch/arm64/kvm/at.c
@@ -585,9 +585,33 @@ static u64 compute_par_s1(struct kvm_vcpu *vcpu, struct s1_walk_result *wr)
return par;
}
+static bool pan3_enabled(struct kvm_vcpu *vcpu)
+{
+ int el;
+
+ if (!kvm_has_feat(vcpu->kvm, ID_AA64MMFR1_EL1, PAN, PAN3))
+ return false;
+
+ el = (vcpu_el2_e2h_is_set(vcpu) &&
+ vcpu_el2_tge_is_set(vcpu)) ? 2 : 1;
+
+ if (el == 1 || vcpu_el2_e2h_is_set(vcpu)) {
+ u64 sctlr;
+
+ if (el == 1)
+ sctlr = vcpu_read_sys_reg(vcpu, SCTLR_EL1);
+ else
+ sctlr = vcpu_read_sys_reg(vcpu, SCTLR_EL2);
+
+ return sctlr & SCTLR_EL1_EPAN;
+ }
+
+ return false;
+}
+
static u64 handle_at_slow(struct kvm_vcpu *vcpu, u32 op, u64 vaddr)
{
- bool perm_fail, ur, uw, pr, pw, pan;
+ bool perm_fail, ur, uw, ux, pr, pw, pan;
struct s1_walk_result wr = {};
struct s1_walk_info wi = {};
int ret, idx, el;
@@ -616,7 +640,7 @@ static u64 handle_at_slow(struct kvm_vcpu *vcpu, u32 op, u64 vaddr)
goto compute_par;
/* FIXME: revisit when adding indirect permission support */
-
+ ux = pan3_enabled(vcpu) && !(wr.desc & PTE_UXN);
pw = !(wr.desc & PTE_RDONLY);
if (wi.single_range) {
@@ -640,6 +664,7 @@ static u64 handle_at_slow(struct kvm_vcpu *vcpu, u32 op, u64 vaddr)
ur &= !(wr.APTable & BIT(0));
uw &= !(wr.APTable != 0);
+ ux &= !wr.UXNTable;
pw &= !(wr.APTable & BIT(1));
@@ -649,14 +674,14 @@ static u64 handle_at_slow(struct kvm_vcpu *vcpu, u32 op, u64 vaddr)
switch (op) {
case OP_AT_S1E1RP:
- perm_fail |= pan && (ur || uw);
+ perm_fail |= pan && (ur || uw || ux);
fallthrough;
case OP_AT_S1E1R:
case OP_AT_S1E2R:
perm_fail |= !pr;
break;
case OP_AT_S1E1WP:
- perm_fail |= pan && (ur || uw);
+ perm_fail |= pan && (ur || uw || ux);
fallthrough;
case OP_AT_S1E1W:
case OP_AT_S1E2W:
@@ -773,7 +798,18 @@ static u64 __kvm_at_s1e01_fast(struct kvm_vcpu *vcpu, u32 op, u64 vaddr)
par = SYS_PAR_EL1_F;
- /*
+ switch (op) {
+ case OP_AT_S1E1RP:
+ case OP_AT_S1E1WP:
+ /*
+ * There is no fast path with PAN3 (no AT instruction
+ * checks for it). Let's cut out losses early.
+ */
+ if (pan3_enabled(vcpu))
+ return par;
+ }
+
+ /*
* 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.