Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3] KVM: arm64: Fix protected VM fault on system with pages larger than 4K
@ 2026-09-15  9:16 Vincent Donnefort
  2026-09-15 11:17 ` Fuad Tabba
  2026-09-17  8:49 ` Suzuki K Poulose
  0 siblings, 2 replies; 4+ messages in thread
From: Vincent Donnefort @ 2026-09-15  9:16 UTC (permalink / raw)
  To: maz, oupton, kvmarm, linux-arm-kernel
  Cc: joey.gouly, seiden, suzuki.poulose, yuzenghui, catalin.marinas,
	will, kernel-team, fuad.tabba, qperret, Vincent Donnefort

Just like commit 08f97454b7fa ("KVM: arm64: Fix protected mode handling
of pages larger than 4kB") fixed the boot of non-protected VMs on system
larger than 4K pages, align the fault IPA down to the page-size for
protected VMs.

To paraphrase Marc, pkvm_pgtable_stage2_map() assumes the address passed
as a parameter is aligned to the size of the intended mapping, while
HPFAR_EL2 gives the IPA minus the bottom 12 bits, regardless of the
system page size configuration.

Add a check at the start of pkvm_pgtable_stage2_map() as we do not
support !PAGE_ALIGNED arguments and ensure callers pass a page-aligned
IPA.

Fixes: ea03466e806f ("KVM: arm64: Handle aborts from protected VMs")
Signed-off-by: Vincent Donnefort <vdonnefort@google.com>
---
 arch/arm64/kvm/mmu.c  | 12 +++++++-----
 arch/arm64/kvm/pkvm.c |  3 +++
 2 files changed, 10 insertions(+), 5 deletions(-)

Changelog:

v3: 
  - Fix nested case in gmem_abort() (Sashiko)

v2: https://lore.kernel.org/all/20260914075839.4019904-1-vdonnefort@google.com/

  - Use gfn_to_gpa(gfn)
  - Drop "phys" from the PAGE_ALIGNED check, it isn't a requirement.
  - Fix gmem_abort() as well (Sashiko)

v1: https://lore.kernel.org/all/20260913173516.3122436-1-vdonnefort@google.com/

diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
index 9ba86450fe4a..e199dd339583 100644
--- a/arch/arm64/kvm/mmu.c
+++ b/arch/arm64/kvm/mmu.c
@@ -1610,6 +1610,7 @@ static int gmem_abort(const struct kvm_s2_fault_desc *s2fd)
 	bool write_fault, exec_fault;
 	bool perm_fault = kvm_vcpu_trap_is_permission_fault(s2fd->vcpu);
 	enum kvm_pgtable_walk_flags flags = KVM_PGTABLE_WALK_SHARED;
+	phys_addr_t ipa = ALIGN_DOWN(s2fd->fault_ipa, PAGE_SIZE);
 	enum kvm_pgtable_prot prot = KVM_PGTABLE_PROT_R;
 	struct kvm_pgtable *pgt = s2fd->vcpu->arch.hw_mmu->pgt;
 	unsigned long mmu_seq;
@@ -1672,12 +1673,12 @@ static int gmem_abort(const struct kvm_s2_fault_desc *s2fd)
 		 * PTE, which will be preserved.
 		 */
 		prot &= ~KVM_NV_GUEST_MAP_SZ;
-		ret = KVM_PGT_FN(kvm_pgtable_stage2_relax_perms)(pgt, s2fd->fault_ipa,
+		ret = KVM_PGT_FN(kvm_pgtable_stage2_relax_perms)(pgt, ipa,
 								 prot, flags);
 	} else {
-		ret = KVM_PGT_FN(kvm_pgtable_stage2_map)(pgt, s2fd->fault_ipa, PAGE_SIZE,
-							 __pfn_to_phys(pfn), prot,
-							 memcache, flags);
+		ret = KVM_PGT_FN(kvm_pgtable_stage2_map)(pgt, ipa, PAGE_SIZE,
+							 __pfn_to_phys(pfn),
+							 prot, memcache, flags);
 	}
 
 out_unlock:
@@ -1710,6 +1711,7 @@ static int pkvm_mem_abort(const struct kvm_s2_fault_desc *s2fd)
 	unsigned int flags = FOLL_HWPOISON | FOLL_LONGTERM | FOLL_WRITE;
 	struct kvm_vcpu *vcpu = s2fd->vcpu;
 	struct kvm_pgtable *pgt = vcpu->arch.hw_mmu->pgt;
+	gfn_t gfn = gpa_to_gfn(s2fd->fault_ipa);
 	struct mm_struct *mm = current->mm;
 	struct kvm *kvm = vcpu->kvm;
 	void *hyp_memcache;
@@ -1756,7 +1758,7 @@ static int pkvm_mem_abort(const struct kvm_s2_fault_desc *s2fd)
 	}
 
 	write_lock(&kvm->mmu_lock);
-	ret = pkvm_pgtable_stage2_map(pgt, s2fd->fault_ipa, PAGE_SIZE,
+	ret = pkvm_pgtable_stage2_map(pgt, gfn_to_gpa(gfn), PAGE_SIZE,
 				      page_to_phys(page), KVM_PGTABLE_PROT_RWX,
 				      hyp_memcache, 0);
 	write_unlock(&kvm->mmu_lock);
diff --git a/arch/arm64/kvm/pkvm.c b/arch/arm64/kvm/pkvm.c
index 8e4c6e4bec12..b7340c430ed6 100644
--- a/arch/arm64/kvm/pkvm.c
+++ b/arch/arm64/kvm/pkvm.c
@@ -414,6 +414,9 @@ int pkvm_pgtable_stage2_map(struct kvm_pgtable *pgt, u64 addr, u64 size,
 	u64 end = addr + size;
 	int ret;
 
+	if (!PAGE_ALIGNED(addr | size))
+		return -EINVAL;
+
 	lockdep_assert_held_write(&kvm->mmu_lock);
 	mapping = pkvm_mapping_iter_first(&pgt->pkvm_mappings, addr, end - 1);
 

base-commit: df2908090cda368b01ff43709f51890076c56157
-- 
2.55.0.1032.g73a4cd73de-goog



^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH v3] KVM: arm64: Fix protected VM fault on system with pages larger than 4K
  2026-09-15  9:16 [PATCH v3] KVM: arm64: Fix protected VM fault on system with pages larger than 4K Vincent Donnefort
@ 2026-09-15 11:17 ` Fuad Tabba
  2026-09-17  8:25   ` Vincent Donnefort
  2026-09-17  8:49 ` Suzuki K Poulose
  1 sibling, 1 reply; 4+ messages in thread
From: Fuad Tabba @ 2026-09-15 11:17 UTC (permalink / raw)
  To: Vincent Donnefort
  Cc: maz, oupton, kvmarm, linux-arm-kernel, joey.gouly, seiden,
	suzuki.poulose, yuzenghui, catalin.marinas, will, kernel-team,
	qperret

Hi Vincent,

On Tue, 15 Sept 2026 at 10:16, 'Vincent Donnefort' via kernel-team
<kernel-team@android.com> wrote:
>
> Just like commit 08f97454b7fa ("KVM: arm64: Fix protected mode handling
> of pages larger than 4kB") fixed the boot of non-protected VMs on system
> larger than 4K pages, align the fault IPA down to the page-size for
> protected VMs.
>
> To paraphrase Marc, pkvm_pgtable_stage2_map() assumes the address passed
> as a parameter is aligned to the size of the intended mapping, while
> HPFAR_EL2 gives the IPA minus the bottom 12 bits, regardless of the
> system page size configuration.
>
> Add a check at the start of pkvm_pgtable_stage2_map() as we do not
> support !PAGE_ALIGNED arguments and ensure callers pass a page-aligned
> IPA.

The fix is correct, and I can reproduce the bug. On a 16K-page host an
unpatched v7.3-rc2 never gets a pVM to a prompt: no guest console
output at all, a core pegged at 100% when I sampled it a minute in,
killed at the 120s timeout. A guest_memfd-backed non-protected VM
times out the same way, while one without guest_memfd boots fine,
which puts the failure on the two paths you fix. With the patch all
three boot clean, and 4K still boots both. That is QEMU with kvmtool
guests; the same three 16K legs also pass on an M4 running pKVM at EL2
on the silicon.

Tested-by: Fuad Tabba <fuad.tabba@linux.dev>

> Fixes: ea03466e806f ("KVM: arm64: Handle aborts from protected VMs")

A second Fixes: for the gmem_abort() half? a7b57e099592 ("KVM: arm64:
Handle guest_memfd-backed guest page faults") added that call site,
and the ranges differ: ea03466e806f is in v7.1, a7b57e099592 in v6.18.

Should this carry Cc: stable@vger.kernel.org? 08f97454b7fa, the fix
this one follows, did, and 16K-page hosts are a shipping Android
configuration.

> Signed-off-by: Vincent Donnefort <vdonnefort@google.com>
> ---
>  arch/arm64/kvm/mmu.c  | 12 +++++++-----
>  arch/arm64/kvm/pkvm.c |  3 +++
>  2 files changed, 10 insertions(+), 5 deletions(-)
>
> Changelog:
>
> v3:
>   - Fix nested case in gmem_abort() (Sashiko)
>
> v2: https://lore.kernel.org/all/20260914075839.4019904-1-vdonnefort@google.com/
>
>   - Use gfn_to_gpa(gfn)
>   - Drop "phys" from the PAGE_ALIGNED check, it isn't a requirement.
>   - Fix gmem_abort() as well (Sashiko)
>
> v1: https://lore.kernel.org/all/20260913173516.3122436-1-vdonnefort@google.com/
>
> diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
> index 9ba86450fe4a..e199dd339583 100644
> --- a/arch/arm64/kvm/mmu.c
> +++ b/arch/arm64/kvm/mmu.c
> @@ -1610,6 +1610,7 @@ static int gmem_abort(const struct kvm_s2_fault_desc *s2fd)
>         bool write_fault, exec_fault;
>         bool perm_fault = kvm_vcpu_trap_is_permission_fault(s2fd->vcpu);
>         enum kvm_pgtable_walk_flags flags = KVM_PGTABLE_WALK_SHARED;
> +       phys_addr_t ipa = ALIGN_DOWN(s2fd->fault_ipa, PAGE_SIZE);
>         enum kvm_pgtable_prot prot = KVM_PGTABLE_PROT_R;
>         struct kvm_pgtable *pgt = s2fd->vcpu->arch.hw_mmu->pgt;
>         unsigned long mmu_seq;

One more in gmem_abort(): the memory fault exit at mmu.c:1647 still
reports the unaligned address, and that one is userspace-visible.

    kvm_prepare_memory_fault_exit(s2fd->vcpu, s2fd->fault_ipa, PAGE_SIZE,
         write_fault, exec_fault, false);

api.rst defines the range as [gpa, gpa + size), so on a 16K host it
starts mid-page. gfn_to_gpa(gfn) is the one to use: gfn is what
kvm_gmem_get_pfn() failed on, and the L1 IPA in the nested case. x86
passes fault->gfn << PAGE_SHIFT.

> @@ -1672,12 +1673,12 @@ static int gmem_abort(const struct kvm_s2_fault_desc *s2fd)
>                  * PTE, which will be preserved.
>                  */
>                 prot &= ~KVM_NV_GUEST_MAP_SZ;
> -               ret = KVM_PGT_FN(kvm_pgtable_stage2_relax_perms)(pgt, s2fd->fault_ipa,
> +               ret = KVM_PGT_FN(kvm_pgtable_stage2_relax_perms)(pgt, ipa,
>                                                                  prot, flags);
>         } else {
> -               ret = KVM_PGT_FN(kvm_pgtable_stage2_map)(pgt, s2fd->fault_ipa, PAGE_SIZE,
> -                                                        __pfn_to_phys(pfn), prot,
> -                                                        memcache, flags);
> +               ret = KVM_PGT_FN(kvm_pgtable_stage2_map)(pgt, ipa, PAGE_SIZE,
> +                                                        __pfn_to_phys(pfn),
> +                                                        prot, memcache, flags);
>         }
>
>  out_unlock:
> @@ -1710,6 +1711,7 @@ static int pkvm_mem_abort(const struct kvm_s2_fault_desc *s2fd)
>         unsigned int flags = FOLL_HWPOISON | FOLL_LONGTERM | FOLL_WRITE;
>         struct kvm_vcpu *vcpu = s2fd->vcpu;
>         struct kvm_pgtable *pgt = vcpu->arch.hw_mmu->pgt;
> +       gfn_t gfn = gpa_to_gfn(s2fd->fault_ipa);
>         struct mm_struct *mm = current->mm;
>         struct kvm *kvm = vcpu->kvm;
>         void *hyp_memcache;
> @@ -1756,7 +1758,7 @@ static int pkvm_mem_abort(const struct kvm_s2_fault_desc *s2fd)
>         }
>
>         write_lock(&kvm->mmu_lock);
> -       ret = pkvm_pgtable_stage2_map(pgt, s2fd->fault_ipa, PAGE_SIZE,
> +       ret = pkvm_pgtable_stage2_map(pgt, gfn_to_gpa(gfn), PAGE_SIZE,
>                                       page_to_phys(page), KVM_PGTABLE_PROT_RWX,
>                                       hyp_memcache, 0);
>         write_unlock(&kvm->mmu_lock);
> diff --git a/arch/arm64/kvm/pkvm.c b/arch/arm64/kvm/pkvm.c
> index 8e4c6e4bec12..b7340c430ed6 100644
> --- a/arch/arm64/kvm/pkvm.c
> +++ b/arch/arm64/kvm/pkvm.c
> @@ -414,6 +414,9 @@ int pkvm_pgtable_stage2_map(struct kvm_pgtable *pgt, u64 addr, u64 size,
>         u64 end = addr + size;
>         int ret;
>
> +       if (!PAGE_ALIGNED(addr | size))
> +               return -EINVAL;
> +

Could this be if (WARN_ON_ONCE(!PAGE_ALIGNED(addr | size)))? The three
checks just below WARN on the same class of caller bug, and this one
runs first, so a bad size now returns -EINVAL with no splat.

With the memory fault exit fixed:
Reviewed-by: Fuad Tabba <fuad.tabba@linux.dev>

Cheers,
/fuad

>         lockdep_assert_held_write(&kvm->mmu_lock);
>         mapping = pkvm_mapping_iter_first(&pgt->pkvm_mappings, addr, end - 1);
>
>
> base-commit: df2908090cda368b01ff43709f51890076c56157
> --
> 2.55.0.1032.g73a4cd73de-goog
>
> To unsubscribe from this group and stop receiving emails from it, send an email to kernel-team+unsubscribe@android.com.
>


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH v3] KVM: arm64: Fix protected VM fault on system with pages larger than 4K
  2026-09-15 11:17 ` Fuad Tabba
@ 2026-09-17  8:25   ` Vincent Donnefort
  0 siblings, 0 replies; 4+ messages in thread
From: Vincent Donnefort @ 2026-09-17  8:25 UTC (permalink / raw)
  To: Fuad Tabba
  Cc: maz, oupton, kvmarm, linux-arm-kernel, joey.gouly, seiden,
	suzuki.poulose, yuzenghui, catalin.marinas, will, kernel-team,
	qperret

On Tue, Sep 15, 2026 at 12:17:21PM +0100, Fuad Tabba wrote:
> Hi Vincent,
> 
> On Tue, 15 Sept 2026 at 10:16, 'Vincent Donnefort' via kernel-team
> <kernel-team@android.com> wrote:
> >
> > Just like commit 08f97454b7fa ("KVM: arm64: Fix protected mode handling
> > of pages larger than 4kB") fixed the boot of non-protected VMs on system
> > larger than 4K pages, align the fault IPA down to the page-size for
> > protected VMs.
> >
> > To paraphrase Marc, pkvm_pgtable_stage2_map() assumes the address passed
> > as a parameter is aligned to the size of the intended mapping, while
> > HPFAR_EL2 gives the IPA minus the bottom 12 bits, regardless of the
> > system page size configuration.
> >
> > Add a check at the start of pkvm_pgtable_stage2_map() as we do not
> > support !PAGE_ALIGNED arguments and ensure callers pass a page-aligned
> > IPA.
> 
> The fix is correct, and I can reproduce the bug. On a 16K-page host an
> unpatched v7.3-rc2 never gets a pVM to a prompt: no guest console
> output at all, a core pegged at 100% when I sampled it a minute in,
> killed at the 120s timeout. A guest_memfd-backed non-protected VM
> times out the same way, while one without guest_memfd boots fine,
> which puts the failure on the two paths you fix. With the patch all
> three boot clean, and 4K still boots both. That is QEMU with kvmtool
> guests; the same three 16K legs also pass on an M4 running pKVM at EL2
> on the silicon.
> 
> Tested-by: Fuad Tabba <fuad.tabba@linux.dev>
> 
> > Fixes: ea03466e806f ("KVM: arm64: Handle aborts from protected VMs")
> 
> A second Fixes: for the gmem_abort() half? a7b57e099592 ("KVM: arm64:
> Handle guest_memfd-backed guest page faults") added that call site,
> and the ranges differ: ea03466e806f is in v7.1, a7b57e099592 in v6.18.
> 
> Should this carry Cc: stable@vger.kernel.org? 08f97454b7fa, the fix
> this one follows, did, and 16K-page hosts are a shipping Android
> configuration.
> 
> > Signed-off-by: Vincent Donnefort <vdonnefort@google.com>
> > ---
> >  arch/arm64/kvm/mmu.c  | 12 +++++++-----
> >  arch/arm64/kvm/pkvm.c |  3 +++
> >  2 files changed, 10 insertions(+), 5 deletions(-)
> >
> > Changelog:
> >
> > v3:
> >   - Fix nested case in gmem_abort() (Sashiko)
> >
> > v2: https://lore.kernel.org/all/20260914075839.4019904-1-vdonnefort@google.com/
> >
> >   - Use gfn_to_gpa(gfn)
> >   - Drop "phys" from the PAGE_ALIGNED check, it isn't a requirement.
> >   - Fix gmem_abort() as well (Sashiko)
> >
> > v1: https://lore.kernel.org/all/20260913173516.3122436-1-vdonnefort@google.com/
> >
> > diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
> > index 9ba86450fe4a..e199dd339583 100644
> > --- a/arch/arm64/kvm/mmu.c
> > +++ b/arch/arm64/kvm/mmu.c
> > @@ -1610,6 +1610,7 @@ static int gmem_abort(const struct kvm_s2_fault_desc *s2fd)
> >         bool write_fault, exec_fault;
> >         bool perm_fault = kvm_vcpu_trap_is_permission_fault(s2fd->vcpu);
> >         enum kvm_pgtable_walk_flags flags = KVM_PGTABLE_WALK_SHARED;
> > +       phys_addr_t ipa = ALIGN_DOWN(s2fd->fault_ipa, PAGE_SIZE);
> >         enum kvm_pgtable_prot prot = KVM_PGTABLE_PROT_R;
> >         struct kvm_pgtable *pgt = s2fd->vcpu->arch.hw_mmu->pgt;
> >         unsigned long mmu_seq;
> 
> One more in gmem_abort(): the memory fault exit at mmu.c:1647 still
> reports the unaligned address, and that one is userspace-visible.
> 
>     kvm_prepare_memory_fault_exit(s2fd->vcpu, s2fd->fault_ipa, PAGE_SIZE,
>          write_fault, exec_fault, false);
> 
> api.rst defines the range as [gpa, gpa + size), so on a 16K host it
> starts mid-page. gfn_to_gpa(gfn) is the one to use: gfn is what
> kvm_gmem_get_pfn() failed on, and the L1 IPA in the nested case. x86
> passes fault->gfn << PAGE_SHIFT.
> 
> > @@ -1672,12 +1673,12 @@ static int gmem_abort(const struct kvm_s2_fault_desc *s2fd)
> >                  * PTE, which will be preserved.
> >                  */
> >                 prot &= ~KVM_NV_GUEST_MAP_SZ;
> > -               ret = KVM_PGT_FN(kvm_pgtable_stage2_relax_perms)(pgt, s2fd->fault_ipa,
> > +               ret = KVM_PGT_FN(kvm_pgtable_stage2_relax_perms)(pgt, ipa,
> >                                                                  prot, flags);
> >         } else {
> > -               ret = KVM_PGT_FN(kvm_pgtable_stage2_map)(pgt, s2fd->fault_ipa, PAGE_SIZE,
> > -                                                        __pfn_to_phys(pfn), prot,
> > -                                                        memcache, flags);
> > +               ret = KVM_PGT_FN(kvm_pgtable_stage2_map)(pgt, ipa, PAGE_SIZE,
> > +                                                        __pfn_to_phys(pfn),
> > +                                                        prot, memcache, flags);
> >         }
> >
> >  out_unlock:
> > @@ -1710,6 +1711,7 @@ static int pkvm_mem_abort(const struct kvm_s2_fault_desc *s2fd)
> >         unsigned int flags = FOLL_HWPOISON | FOLL_LONGTERM | FOLL_WRITE;
> >         struct kvm_vcpu *vcpu = s2fd->vcpu;
> >         struct kvm_pgtable *pgt = vcpu->arch.hw_mmu->pgt;
> > +       gfn_t gfn = gpa_to_gfn(s2fd->fault_ipa);
> >         struct mm_struct *mm = current->mm;
> >         struct kvm *kvm = vcpu->kvm;
> >         void *hyp_memcache;
> > @@ -1756,7 +1758,7 @@ static int pkvm_mem_abort(const struct kvm_s2_fault_desc *s2fd)
> >         }
> >
> >         write_lock(&kvm->mmu_lock);
> > -       ret = pkvm_pgtable_stage2_map(pgt, s2fd->fault_ipa, PAGE_SIZE,
> > +       ret = pkvm_pgtable_stage2_map(pgt, gfn_to_gpa(gfn), PAGE_SIZE,
> >                                       page_to_phys(page), KVM_PGTABLE_PROT_RWX,
> >                                       hyp_memcache, 0);
> >         write_unlock(&kvm->mmu_lock);
> > diff --git a/arch/arm64/kvm/pkvm.c b/arch/arm64/kvm/pkvm.c
> > index 8e4c6e4bec12..b7340c430ed6 100644
> > --- a/arch/arm64/kvm/pkvm.c
> > +++ b/arch/arm64/kvm/pkvm.c
> > @@ -414,6 +414,9 @@ int pkvm_pgtable_stage2_map(struct kvm_pgtable *pgt, u64 addr, u64 size,
> >         u64 end = addr + size;
> >         int ret;
> >
> > +       if (!PAGE_ALIGNED(addr | size))
> > +               return -EINVAL;
> > +
> 
> Could this be if (WARN_ON_ONCE(!PAGE_ALIGNED(addr | size)))? The three
> checks just below WARN on the same class of caller bug, and this one
> runs first, so a bad size now returns -EINVAL with no splat.
> 
> With the memory fault exit fixed:
> Reviewed-by: Fuad Tabba <fuad.tabba@linux.dev>
> 
> Cheers,
> /fuad

Thanks Fuad,

I'll modify that. Although in the new respin I will also add support for
kvm_s2_fault_vma_info(), which should naturally fix that issue, just like Marc
suggested [1]


[1] https://lore.kernel.org/all/864ifs6xn4.wl-maz@kernel.org/

-- 
Vincent

> 
> >         lockdep_assert_held_write(&kvm->mmu_lock);
> >         mapping = pkvm_mapping_iter_first(&pgt->pkvm_mappings, addr, end - 1);
> >
> >
> > base-commit: df2908090cda368b01ff43709f51890076c56157
> > --
> > 2.55.0.1032.g73a4cd73de-goog
> >
> > To unsubscribe from this group and stop receiving emails from it, send an email to kernel-team+unsubscribe@android.com.
> >
> 
> To unsubscribe from this group and stop receiving emails from it, send an email to kernel-team+unsubscribe@android.com.
> 


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH v3] KVM: arm64: Fix protected VM fault on system with pages larger than 4K
  2026-09-15  9:16 [PATCH v3] KVM: arm64: Fix protected VM fault on system with pages larger than 4K Vincent Donnefort
  2026-09-15 11:17 ` Fuad Tabba
@ 2026-09-17  8:49 ` Suzuki K Poulose
  1 sibling, 0 replies; 4+ messages in thread
From: Suzuki K Poulose @ 2026-09-17  8:49 UTC (permalink / raw)
  To: Vincent Donnefort, maz, oupton, kvmarm, linux-arm-kernel
  Cc: joey.gouly, seiden, yuzenghui, catalin.marinas, will, kernel-team,
	fuad.tabba, qperret

On 15/09/2026 10:16, Vincent Donnefort wrote:
> Just like commit 08f97454b7fa ("KVM: arm64: Fix protected mode handling
> of pages larger than 4kB") fixed the boot of non-protected VMs on system
> larger than 4K pages, align the fault IPA down to the page-size for
> protected VMs.
> 
> To paraphrase Marc, pkvm_pgtable_stage2_map() assumes the address passed
> as a parameter is aligned to the size of the intended mapping, while
> HPFAR_EL2 gives the IPA minus the bottom 12 bits, regardless of the
> system page size configuration.
> 
> Add a check at the start of pkvm_pgtable_stage2_map() as we do not
> support !PAGE_ALIGNED arguments and ensure callers pass a page-aligned
> IPA.
> 
> Fixes: ea03466e806f ("KVM: arm64: Handle aborts from protected VMs")
> Signed-off-by: Vincent Donnefort <vdonnefort@google.com>
> ---
>   arch/arm64/kvm/mmu.c  | 12 +++++++-----
>   arch/arm64/kvm/pkvm.c |  3 +++
>   2 files changed, 10 insertions(+), 5 deletions(-)
> 
> Changelog:
> 
> v3:
>    - Fix nested case in gmem_abort() (Sashiko)
> 
> v2: https://lore.kernel.org/all/20260914075839.4019904-1-vdonnefort@google.com/
> 
>    - Use gfn_to_gpa(gfn)
>    - Drop "phys" from the PAGE_ALIGNED check, it isn't a requirement.
>    - Fix gmem_abort() as well (Sashiko)
> 
> v1: https://lore.kernel.org/all/20260913173516.3122436-1-vdonnefort@google.com/
> 
> diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
> index 9ba86450fe4a..e199dd339583 100644
> --- a/arch/arm64/kvm/mmu.c
> +++ b/arch/arm64/kvm/mmu.c
> @@ -1610,6 +1610,7 @@ static int gmem_abort(const struct kvm_s2_fault_desc *s2fd)
>   	bool write_fault, exec_fault;
>   	bool perm_fault = kvm_vcpu_trap_is_permission_fault(s2fd->vcpu);
>   	enum kvm_pgtable_walk_flags flags = KVM_PGTABLE_WALK_SHARED;
> +	phys_addr_t ipa = ALIGN_DOWN(s2fd->fault_ipa, PAGE_SIZE);
>   	enum kvm_pgtable_prot prot = KVM_PGTABLE_PROT_R;
>   	struct kvm_pgtable *pgt = s2fd->vcpu->arch.hw_mmu->pgt;
>   	unsigned long mmu_seq;
> @@ -1672,12 +1673,12 @@ static int gmem_abort(const struct kvm_s2_fault_desc *s2fd)
>   		 * PTE, which will be preserved.
>   		 */
>   		prot &= ~KVM_NV_GUEST_MAP_SZ;
> -		ret = KVM_PGT_FN(kvm_pgtable_stage2_relax_perms)(pgt, s2fd->fault_ipa,
> +		ret = KVM_PGT_FN(kvm_pgtable_stage2_relax_perms)(pgt, ipa,
>   								 prot, flags);
>   	} else {
> -		ret = KVM_PGT_FN(kvm_pgtable_stage2_map)(pgt, s2fd->fault_ipa, PAGE_SIZE,
> -							 __pfn_to_phys(pfn), prot,
> -							 memcache, flags);
> +		ret = KVM_PGT_FN(kvm_pgtable_stage2_map)(pgt, ipa, PAGE_SIZE,
> +							 __pfn_to_phys(pfn),
> +							 prot, memcache, flags);
>   	}
>   
>   out_unlock:
> @@ -1710,6 +1711,7 @@ static int pkvm_mem_abort(const struct kvm_s2_fault_desc *s2fd)
>   	unsigned int flags = FOLL_HWPOISON | FOLL_LONGTERM | FOLL_WRITE;
>   	struct kvm_vcpu *vcpu = s2fd->vcpu;
>   	struct kvm_pgtable *pgt = vcpu->arch.hw_mmu->pgt;
> +	gfn_t gfn = gpa_to_gfn(s2fd->fault_ipa);

minor nit: Is there any reason why this is not consistent with the 
gmem_abort() ? i.e., using gfn instead of "ipa" ?

The patch as such looks correct to me. So, either way :

Reviewed-by: Suzuki K Poulose <suzuki.poulose@arm.com>


>   	struct mm_struct *mm = current->mm;
>   	struct kvm *kvm = vcpu->kvm;
>   	void *hyp_memcache;
> @@ -1756,7 +1758,7 @@ static int pkvm_mem_abort(const struct kvm_s2_fault_desc *s2fd)
>   	}
>   
>   	write_lock(&kvm->mmu_lock);
> -	ret = pkvm_pgtable_stage2_map(pgt, s2fd->fault_ipa, PAGE_SIZE,
> +	ret = pkvm_pgtable_stage2_map(pgt, gfn_to_gpa(gfn), PAGE_SIZE,
>   				      page_to_phys(page), KVM_PGTABLE_PROT_RWX,
>   				      hyp_memcache, 0);
>   	write_unlock(&kvm->mmu_lock);
> diff --git a/arch/arm64/kvm/pkvm.c b/arch/arm64/kvm/pkvm.c
> index 8e4c6e4bec12..b7340c430ed6 100644
> --- a/arch/arm64/kvm/pkvm.c
> +++ b/arch/arm64/kvm/pkvm.c
> @@ -414,6 +414,9 @@ int pkvm_pgtable_stage2_map(struct kvm_pgtable *pgt, u64 addr, u64 size,
>   	u64 end = addr + size;
>   	int ret;
>   
> +	if (!PAGE_ALIGNED(addr | size))
> +		return -EINVAL;
> +
>   	lockdep_assert_held_write(&kvm->mmu_lock);
>   	mapping = pkvm_mapping_iter_first(&pgt->pkvm_mappings, addr, end - 1);
>   
> 
> base-commit: df2908090cda368b01ff43709f51890076c56157



^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-09-17  8:50 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-15  9:16 [PATCH v3] KVM: arm64: Fix protected VM fault on system with pages larger than 4K Vincent Donnefort
2026-09-15 11:17 ` Fuad Tabba
2026-09-17  8:25   ` Vincent Donnefort
2026-09-17  8:49 ` Suzuki K Poulose

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox