Commit e39ce703 authored by Marc Zyngier's avatar Marc Zyngier Committed by Oliver Upton
Browse files

KVM: arm64: Handle stage-1 permission overlays



We now have the intrastructure in place to emulate S1POE:

- direct permissions are always overlay-capable
- indirect permissions are overlay-capable if the permissions are
  in the 0b0xxx range
- the overlays are strictly substractive

Signed-off-by: default avatarMarc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20241023145345.1613824-37-maz@kernel.org


Signed-off-by: default avatarOliver Upton <oliver.upton@linux.dev>
parent 7cd5c279
Loading
Loading
Loading
Loading
+53 −0
Original line number Diff line number Diff line
@@ -40,9 +40,11 @@ struct s1_walk_result {
			u8	APTable;
			bool	UXNTable;
			bool	PXNTable;
			bool	uov;
			bool	ur;
			bool	uw;
			bool	ux;
			bool	pov;
			bool	pr;
			bool	pw;
			bool	px;
@@ -881,6 +883,9 @@ static void compute_s1_direct_permissions(struct kvm_vcpu *vcpu,
		/* XN maps to UXN */
		wr->px = !(wr->desc & PTE_UXN);
	}

	wr->pov = wi->poe;
	wr->uov = wi->e0poe;
}

static void compute_s1_hierarchical_permissions(struct kvm_vcpu *vcpu,
@@ -1016,6 +1021,9 @@ static void compute_s1_indirect_permissions(struct kvm_vcpu *vcpu,
	else
		set_unpriv_perms(wr, false, false, false);

	wr->pov = wi->poe && !(pp & BIT(3));
	wr->uov = wi->e0poe && !(up & BIT(3));

	/* R_VFPJF */
	if (wr->px && wr->uw) {
		set_priv_perms(wr, false, false, false);
@@ -1023,6 +1031,48 @@ static void compute_s1_indirect_permissions(struct kvm_vcpu *vcpu,
	}
}

static void compute_s1_overlay_permissions(struct kvm_vcpu *vcpu,
					   struct s1_walk_info *wi,
					   struct s1_walk_result *wr)
{
	u8 idx, pov_perms, uov_perms;

	idx = FIELD_GET(PTE_PO_IDX_MASK, wr->desc);

	switch (wi->regime) {
	case TR_EL10:
		pov_perms = perm_idx(vcpu, POR_EL1, idx);
		uov_perms = perm_idx(vcpu, POR_EL0, idx);
		break;
	case TR_EL20:
		pov_perms = perm_idx(vcpu, POR_EL2, idx);
		uov_perms = perm_idx(vcpu, POR_EL0, idx);
		break;
	case TR_EL2:
		pov_perms = perm_idx(vcpu, POR_EL2, idx);
		uov_perms = 0;
		break;
	}

	if (pov_perms & ~POE_RXW)
		pov_perms = POE_NONE;

	if (wi->poe && wr->pov) {
		wr->pr &= pov_perms & POE_R;
		wr->px &= pov_perms & POE_X;
		wr->pw &= pov_perms & POE_W;
	}

	if (uov_perms & ~POE_RXW)
		uov_perms = POE_NONE;

	if (wi->e0poe && wr->uov) {
		wr->ur &= uov_perms & POE_R;
		wr->ux &= uov_perms & POE_X;
		wr->uw &= uov_perms & POE_W;
	}
}

static void compute_s1_permissions(struct kvm_vcpu *vcpu,
				   struct s1_walk_info *wi,
				   struct s1_walk_result *wr)
@@ -1037,6 +1087,9 @@ static void compute_s1_permissions(struct kvm_vcpu *vcpu,
	if (!wi->hpd)
		compute_s1_hierarchical_permissions(vcpu, wi, wr);

	if (wi->poe || wi->e0poe)
		compute_s1_overlay_permissions(vcpu, wi, wr);

	pan = wi->pan && (wr->ur || wr->uw ||
			  (pan3_enabled(vcpu, wi->regime) && wr->ux));
	wr->pw &= !pan;