From: Fuad Tabba <fuad.tabba@linux.dev>
To: Marc Zyngier <maz@kernel.org>, Oliver Upton <oupton@kernel.org>
Cc: Joey Gouly <joey.gouly@arm.com>,
Steffen Eiden <seiden@linux.ibm.com>,
Suzuki K Poulose <suzuki.poulose@arm.com>,
Zenghui Yu <yuzenghui@huawei.com>,
Catalin Marinas <catalin.marinas@arm.com>,
Will Deacon <will@kernel.org>,
Mark Rutland <mark.rutland@arm.com>,
Christoffer Dall <christoffer.dall@arm.com>,
Wei-Lin Chang <weilin.chang@arm.com>,
linux-arm-kernel@lists.infradead.org, kvmarm@lists.linux.dev,
linux-kernel@vger.kernel.org, Fuad Tabba <tabba@google.com>
Subject: [PATCH v2] KVM: arm64: nv: Size nested stage-2 MMUs by the NV PARange cap
Date: Tue, 3 Nov 2026 10:24:36 +0000 [thread overview]
Message-ID: <20261103102436.1605347-1-fuad.tabba@linux.dev> (raw)
KVM caps the PARange it exposes to an NV guest at 48 bits, but sizes
the nested stage-2 MMUs with kvm_get_pa_bits(), which returns the host's
IPA limit.
On a 52-bit host, every TLBI ALLE1IS from the guest hypervisor then has
KVM unmap each valid nested stage-2 across a range 16 times the one the
guest can use. Under QEMU, the KVM init of an E2H0 guest hypervisor
doesn't complete within 20 minutes.
Make kvm_get_pa_bits() return the 48-bit cap for an NV VM, and bound the
walks KVM does on the guest's behalf by it rather than by the host's IPA
limit. Have kvm_handle_guest_abort() inject its address size fault above
the cap too, so that an L2 access above 48 bits doesn't get an SEA from
the smaller nested MMU. If userspace gave the NV VM an IPA size above 48
bits, an L1 access above 48 bits then also gets the address size fault,
instead of normal stage-2 handling.
Fixes: 4f128f8e1aaac ("KVM: arm64: nv: Support multiple nested Stage-2 mmu structures")
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
---
Notes:
Changes since v1 [1]:
- Fold in the kvm_handle_guest_abort() change from my reply to
Sashiko, so that an L2 access above 48 bits gets an address size
fault rather than an SEA.
- Bound setup_s1_walk() and setup_s2_walk() by kvm_get_pa_bits()
rather than by the host's IPA limit (Wei-Lin).
The 48 is written here and in limit_nv_id_reg(). I couldn't derive
it from the VM's ID_AA64MMFR0_EL1, since the nested MMUs are created
in kvm_setup_vcpu(), before kvm_reset_vcpu() initialises the VM's ID
registers. Userspace can also lower PARange afterwards.
Based on v7.4-rc1.
[1] https://lore.kernel.org/all/20261030105139.3543230-1-fuad.tabba@linux.dev/
arch/arm64/kvm/at.c | 2 +-
arch/arm64/kvm/mmu.c | 4 ++--
arch/arm64/kvm/nested.c | 3 +--
arch/arm64/kvm/reset.c | 4 ++++
4 files changed, 8 insertions(+), 5 deletions(-)
diff --git a/arch/arm64/kvm/at.c b/arch/arm64/kvm/at.c
index 30d8f062b2c29..69bc621226cc2 100644
--- a/arch/arm64/kvm/at.c
+++ b/arch/arm64/kvm/at.c
@@ -383,7 +383,7 @@ static int setup_s1_walk(struct kvm_vcpu *vcpu, struct s1_walk_info *wi,
ps = (wi->regime == TR_EL2 ?
FIELD_GET(TCR_EL2_PS_MASK, tcr) : FIELD_GET(TCR_IPS_MASK, tcr));
- wi->max_oa_bits = min(get_kvm_ipa_limit(), ps_to_output_size(ps, wi->pa52bit));
+ wi->max_oa_bits = min(kvm_get_pa_bits(vcpu->kvm), ps_to_output_size(ps, wi->pa52bit));
/* Compute minimal alignment */
x = 3 + ia_bits - ((3 - wi->sl) * stride + wi->pgshift);
diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
index 1db10d8d6db73..620fc6d1674bc 100644
--- a/arch/arm64/kvm/mmu.c
+++ b/arch/arm64/kvm/mmu.c
@@ -2440,8 +2440,8 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
is_iabt = esr_trap_is_iabt(esr);
if (esr_fsc_is_translation_fault(esr)) {
- /* Beyond sanitised PARange (which is the IPA limit) */
- if (fault_ipa >= BIT_ULL(get_kvm_ipa_limit())) {
+ /* Beyond the VM's PA limit */
+ if (fault_ipa >= BIT_ULL(kvm_get_pa_bits(kvm))) {
kvm_inject_size_fault(vcpu);
return 1;
}
diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c
index 4d83281a7a28b..a04ba4bf30a40 100644
--- a/arch/arm64/kvm/nested.c
+++ b/arch/arm64/kvm/nested.c
@@ -491,8 +491,7 @@ static void setup_s2_walk(struct kvm_vcpu *vcpu, struct s2_walk_info *wi)
wi->t0sz = vtcr & VTCR_EL2_T0SZ_MASK;
wi->pgshift = vtcr_to_tg0_pgshift(vcpu->kvm, vtcr);
wi->sl = FIELD_GET(VTCR_EL2_SL0_MASK, vtcr);
- /* Global limit for now, should eventually be per-VM */
- wi->max_oa_bits = min(get_kvm_ipa_limit(),
+ wi->max_oa_bits = min(kvm_get_pa_bits(vcpu->kvm),
ps_to_output_size(FIELD_GET(VTCR_EL2_PS_MASK, vtcr), false));
wi->ha = vtcr & VTCR_EL2_HA;
wi->be = vcpu_read_sys_reg(vcpu, SCTLR_EL2) & SCTLR_ELx_EE;
diff --git a/arch/arm64/kvm/reset.c b/arch/arm64/kvm/reset.c
index 10eb7249aa9e8..1918837757682 100644
--- a/arch/arm64/kvm/reset.c
+++ b/arch/arm64/kvm/reset.c
@@ -220,6 +220,10 @@ void kvm_reset_vcpu(struct kvm_vcpu *vcpu)
u32 kvm_get_pa_bits(struct kvm *kvm)
{
+ /* An NV guest's PARange is capped by limit_nv_id_reg() */
+ if (test_bit(KVM_ARM_VCPU_HAS_EL2, kvm->arch.vcpu_features))
+ return min(kvm_ipa_limit, 48U);
+
/* Fixed limit until we can configure ID_AA64MMFR0.PARange */
return kvm_ipa_limit;
}
base-commit: 6628e7d4c6b25c8d18f1345e3a20d21829aae7e4
--
2.47.3
reply other threads:[~2026-11-03 10:24 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261103102436.1605347-1-fuad.tabba@linux.dev \
--to=fuad.tabba@linux.dev \
--cc=catalin.marinas@arm.com \
--cc=christoffer.dall@arm.com \
--cc=joey.gouly@arm.com \
--cc=kvmarm@lists.linux.dev \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mark.rutland@arm.com \
--cc=maz@kernel.org \
--cc=oupton@kernel.org \
--cc=seiden@linux.ibm.com \
--cc=suzuki.poulose@arm.com \
--cc=tabba@google.com \
--cc=weilin.chang@arm.com \
--cc=will@kernel.org \
--cc=yuzenghui@huawei.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®