From: Mostafa Saleh <smostafa@google.com>
To: Vincent Donnefort <vdonnefort@google.com>
Cc: linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, kvmarm@lists.linux.dev,
iommu@lists.linux.dev, catalin.marinas@arm.com, will@kernel.org,
maz@kernel.org, oliver.upton@linux.dev, joey.gouly@arm.com,
suzuki.poulose@arm.com, yuzenghui@huawei.com, joro@8bytes.org,
jean-philippe@linaro.org, jgg@ziepe.ca, mark.rutland@arm.com,
qperret@google.com, tabba@google.com, sebastianene@google.com,
keirf@google.com
Subject: Re: [PATCH v6 02/25] KVM: arm64: Donate MMIO to the hypervisor
Date: Sun, 12 Jul 2026 15:29:16 +0000 [thread overview]
Message-ID: <alOyzOL16dhYk724@google.com> (raw)
In-Reply-To: <alEnH98wFppYjmnE@google.com>
On Fri, Jul 10, 2026 at 06:08:47PM +0100, Vincent Donnefort wrote:
> On Fri, May 01, 2026 at 11:19:04AM +0000, Mostafa Saleh wrote:
> > Add a function to donate MMIO to the hypervisor so IOMMU hypervisor
> > drivers can protect and access the MMIO of IOMMUs.
> >
> > As donating MMIO is very rare, and we don’t need to encode the full
> > state, it’s reasonable to have a separate function to do this.
> > It will init the host s2 page table with an invalid leaf with the owner ID
> > to prevent the host from mapping the page on faults.
> >
> > Also, prevent kvm_pgtable_stage2_unmap() from removing owner ID from
> > stage-2 PTEs, as this can be triggered from recycle logic under memory
> > pressure. There is no code relying on this, as all ownership changes is
> > done via kvm_pgtable_stage2_set_owner()
> >
> > For the error path in IOMMU drivers, add a function to donate MMIO
> > back from hyp to host. However, that leaks the hypervisor virtual
> > address range which should be acceptable as this is quite rare and
> > it matches the behaviour of fix_map/block.
> >
> > Signed-off-by: Mostafa Saleh <smostafa@google.com>
> > ---
> > arch/arm64/kvm/hyp/include/nvhe/mem_protect.h | 2 +
> > arch/arm64/kvm/hyp/nvhe/mem_protect.c | 119 +++++++++++++++++-
> > arch/arm64/kvm/hyp/pgtable.c | 9 +-
> > 3 files changed, 121 insertions(+), 9 deletions(-)
> >
> > diff --git a/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h b/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h
> > index 3cbfae0e3dda..ff440204d2c7 100644
> > --- a/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h
> > +++ b/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h
> > @@ -36,6 +36,8 @@ int __pkvm_guest_share_host(struct pkvm_hyp_vcpu *vcpu, u64 gfn);
> > int __pkvm_guest_unshare_host(struct pkvm_hyp_vcpu *vcpu, u64 gfn);
> > int __pkvm_host_unshare_hyp(u64 pfn);
> > int __pkvm_host_donate_hyp(u64 pfn, u64 nr_pages);
> > +int __pkvm_host_donate_hyp_mmio(phys_addr_t addr, size_t size, unsigned long *haddr);
> > +int __pkvm_hyp_donate_host_mmio(phys_addr_t addr, size_t size);
>
> Can we have u64 pfn, u64 nr_pages here to match all the other functions?
>
I see the convention is that phys_addr_t is used for physical addresses
as (addr_is_memory(), host_stage2_idmap_locked(()...) while u64 is used
for pfn.
> Also, as this is used by a "module" perhaps a sort of kerneldoc would be nice,
> especially to explain this isn't mapped into the hyp idmap range.
Sure I can add a comment.
>
> > int __pkvm_hyp_donate_host(u64 pfn, u64 nr_pages);
> > int __pkvm_host_share_ffa(u64 pfn, u64 nr_pages);
> > int __pkvm_host_unshare_ffa(u64 pfn, u64 nr_pages);
> > diff --git a/arch/arm64/kvm/hyp/nvhe/mem_protect.c b/arch/arm64/kvm/hyp/nvhe/mem_protect.c
> > index 28a471d1927c..2fb20a63a417 100644
> > --- a/arch/arm64/kvm/hyp/nvhe/mem_protect.c
> > +++ b/arch/arm64/kvm/hyp/nvhe/mem_protect.c
> > @@ -353,6 +353,38 @@ int __pkvm_prot_finalize(void)
> > return 0;
> > }
> >
> > +/* Unmap MMIO region while skipping donated PTEs. */
> > +static int host_stage2_unmap_mmio_region(u64 start, u64 size)
> > +{
> > + struct kvm_pgtable *pgt = &host_mmu.pgt;
> > + u64 unmap_start = start;
> > + u64 addr = start;
> > + kvm_pte_t pte;
> > + int ret = 0;
> > + u8 level;
> > +
> > + while (addr < start + size) {
> > + ret = kvm_pgtable_get_leaf(pgt, addr, &pte, &level);
> > + if (ret)
> > + return ret;
> > + if (!kvm_pte_valid(pte) && pte != 0) {
> > + if (addr > unmap_start) {
> > + ret = kvm_pgtable_stage2_unmap(pgt, unmap_start,
> > + addr - unmap_start);
> > + if (ret)
> > + return ret;
> > + }
> > + addr += kvm_granule_size(level);
> > + unmap_start = addr;
> > + } else {
> > + addr += kvm_granule_size(level);
> > + }
> > + }
> > + if (addr > unmap_start)
> > + ret = kvm_pgtable_stage2_unmap(pgt, unmap_start, addr - unmap_start);
>
> Ah shame, that's a lot of table walking. I guess we could:
>
> * create our own custom walker here but that would duplicate a lot of logic
> from pgtable.c. So doesn't look great.
Yes, that seems too complicated.
>
> * Retry kvm_pgtable_stage2_unmap() as long as it return -EPERM... but then we
> it needs to return where it has stopped?
Yes, that is not possible with its current interface.
>
> * Modify kvm_pgtable_stage2_unmap() to just skip the refcounted entries?
> There's already a flag something that looks like this: KVM_PGTABLE_WALK_IGNORE_EAGAIN
>
I thought about this, my main concern is that now any unmap will fail
silently if it hits a refcounted page. Propably not too bad as that only
is used in recycling.
> I am not so convinced by the first two points but the last one is probably worth
> exploring.
>
> > + return ret;
> > +}
> > +
> > static int host_stage2_unmap_dev_all(void)
> > {
> > struct kvm_pgtable *pgt = &host_mmu.pgt;
> > @@ -363,11 +395,11 @@ static int host_stage2_unmap_dev_all(void)
> > /* Unmap all non-memory regions to recycle the pages */
> > for (i = 0; i < hyp_memblock_nr; i++, addr = reg->base + reg->size) {
> > reg = &hyp_memory[i];
> > - ret = kvm_pgtable_stage2_unmap(pgt, addr, reg->base - addr);
> > + ret = host_stage2_unmap_mmio_region(addr, reg->base - addr);
> > if (ret)
> > return ret;
> > }
> > - return kvm_pgtable_stage2_unmap(pgt, addr, BIT(pgt->ia_bits) - addr);
> > + return host_stage2_unmap_mmio_region(addr, BIT(pgt->ia_bits) - addr);
> > }
> >
> > /*
> > @@ -1087,6 +1119,89 @@ int __pkvm_host_donate_hyp(u64 pfn, u64 nr_pages)
> > return ret;
> > }
> >
> > +int __pkvm_host_donate_hyp_mmio(phys_addr_t addr, size_t size, unsigned long *haddr)
> > +{
> > + kvm_pte_t pte;
> > + u64 offset;
> > + int ret;
> > +
> > + /* Only before de-privilege. */
> > + if (static_branch_unlikely(&kvm_protected_mode_initialized))
> > + return -EPERM;
> > +
> > + if (!PAGE_ALIGNED(addr | size))
> > + return -EINVAL;
>
> Would be nice here to have pfn_range_is_valid() even though this is only
> used by modules? (ditto __pkvm_hyp_donate_host_mmio())
Will do.
>
> > +
> > + ret = __pkvm_create_private_mapping(addr, size, PAGE_HYP_DEVICE, haddr);
> > + if (ret)
> > + return ret;
>
> Perhaps better to do it once it's been annotated into the host s2, so that we
> don't burn the private range in case of failure.
This really should not fail, or the system is already have bigger
problems, and that also does not allocate much space (128K), I see
that is easier to do this way than having to fix the host stage-2
if the allocation fails.
>
> > +
> > + host_lock_component();
> > + for (offset = 0; offset < size; offset += PAGE_SIZE) {
> > + if (addr_is_memory(addr + offset)) {
> > + ret = -EINVAL;
> > + goto unlock;
> > + }
> > + ret = kvm_pgtable_get_leaf(&host_mmu.pgt, addr + offset, &pte, NULL);
> > + if (ret)
> > + goto unlock;
> > + if (pte && !kvm_pte_valid(pte)) {
> > + ret = -EPERM;
> > + goto unlock;
> > + }
> > + }
> > + /*
> > + * We set HYP as the owner of the MMIO pages in the host stage-2, for:
> > + * - host aborts: host_stage2_adjust_range() would fail for invalid non zero PTEs.
> > + * - recycle under memory pressure: host_stage2_unmap_dev_all() would call
> > + * kvm_pgtable_stage2_unmap() which will not clear non zero invalid ptes (counted).
> > + * - other MMIO donation: Would fail as we check that the PTE is valid or empty.
> > + */
> > + ret = host_stage2_try(kvm_pgtable_stage2_annotate, &host_mmu.pgt,
> > + addr, size, &host_s2_pool,
> > + KVM_HOST_INVALID_PTE_TYPE_DONATION,
> > + FIELD_PREP(KVM_HOST_DONATION_PTE_OWNER_MASK, PKVM_ID_HYP));
> > +unlock:
> > + host_unlock_component();
> > + return ret;
> > +}
> > +
> > +int __pkvm_hyp_donate_host_mmio(phys_addr_t addr, size_t size)
> > +{
> > + kvm_pte_t pte;
> > + u64 offset;
> > + int ret = 0;
> > +
> > + if (static_branch_unlikely(&kvm_protected_mode_initialized))
> > + return -EPERM;
> > +
> > + if (!PAGE_ALIGNED(addr | size))
> > + return -EINVAL;
> > +
> > + host_lock_component();
> > + for (offset = 0; offset < size; offset += PAGE_SIZE) {
> > + if (addr_is_memory(addr + offset)) {
> > + ret = -EINVAL;
> > + goto unlock;
> > + }
> > + ret = kvm_pgtable_get_leaf(&host_mmu.pgt, addr + offset, &pte, NULL);
> > + if (ret)
> > + goto unlock;
> > + if (!pte || kvm_pte_valid(pte)) {
> > + ret = -EINVAL;
> > + goto unlock;
> > + }
>
> It should probably verify it is also mapped as we expect into the hyp.
I thought about that, but the caller is be trusted and I was aiming
for simplicity, so the check for PKVM_ID_HYP seemed enough, but no
strong opinion.
>
>
> > + if (FIELD_GET(KVM_HOST_DONATION_PTE_OWNER_MASK, pte) != PKVM_ID_HYP) {
> > + ret = -EPERM;
> > + goto unlock;
> > + }
> > + }
> > + WARN_ON(host_stage2_idmap_locked(addr, size, PKVM_HOST_MMIO_PROT));
>
> Perhaps we are better of simply zapping the entry and not map it proactively?
I do not think it matters that much, this is a failure path, and the
space is too small anyway. But no strong opinion also.
>
> And even if we can't "reclaim" the VA-space from the private range, it would be
> good to unmap it. I have made a function for that as part of the allocator
> series:
>
> https://lore.kernel.org/all/20260706175415.2604046-3-vdonnefort@google.com/
>
I do not think that is a big problem at the moment all the other
users of the VA space leak it on failure, I can re-use this logic
when the allocator seires lands, otherwise I'd avoid adding more code.
Thanks,
Mostafa
next prev parent reply other threads:[~2026-07-12 15:29 UTC|newest]
Thread overview: 86+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-01 11:19 [PATCH v6 00/25] KVM: arm64: SMMUv3 driver for pKVM (trap and emulate) Mostafa Saleh
2026-05-01 11:19 ` [PATCH v6 01/25] KVM: arm64: Generalize trace clock Mostafa Saleh
2026-07-10 14:19 ` Vincent Donnefort
2026-07-10 14:24 ` Vincent Donnefort
2026-07-12 13:44 ` Mostafa Saleh
2026-05-01 11:19 ` [PATCH v6 02/25] KVM: arm64: Donate MMIO to the hypervisor Mostafa Saleh
2026-07-10 17:08 ` Vincent Donnefort
2026-07-12 15:29 ` Mostafa Saleh [this message]
2026-05-01 11:19 ` [PATCH v6 03/25] iommu/arm-smmu-v3: Split code with hyp Mostafa Saleh
2026-05-01 12:44 ` Jason Gunthorpe
2026-05-04 12:13 ` Mostafa Saleh
2026-05-01 11:19 ` [PATCH v6 04/25] iommu/arm-smmu-v3: Move TLB range invalidation into common code Mostafa Saleh
2026-05-01 12:41 ` Jason Gunthorpe
2026-05-04 12:15 ` Mostafa Saleh
2026-05-05 16:17 ` Jason Gunthorpe
2026-05-05 16:43 ` Mostafa Saleh
2026-05-06 9:53 ` Jason Gunthorpe
2026-05-07 9:40 ` Mostafa Saleh
2026-05-09 23:29 ` Jason Gunthorpe
2026-05-11 11:45 ` Mostafa Saleh
2026-05-11 14:24 ` Jason Gunthorpe
2026-05-01 11:19 ` [PATCH v6 05/25] iommu/arm-smmu-v3: Move IDR parsing to common functions Mostafa Saleh
2026-05-01 12:47 ` Jason Gunthorpe
2026-05-04 12:16 ` Mostafa Saleh
2026-05-05 16:27 ` Jason Gunthorpe
2026-05-05 16:48 ` Mostafa Saleh
2026-05-06 9:56 ` Jason Gunthorpe
2026-05-07 10:13 ` Mostafa Saleh
2026-05-09 23:34 ` Jason Gunthorpe
2026-05-11 11:53 ` Mostafa Saleh
2026-05-11 14:30 ` Jason Gunthorpe
2026-05-01 11:19 ` [PATCH v6 06/25] iommu/io-pgtable-arm: Rework to use the iommu-pages API Mostafa Saleh
2026-05-01 12:24 ` Jason Gunthorpe
2026-05-04 12:19 ` Mostafa Saleh
2026-05-09 23:21 ` Jason Gunthorpe
2026-05-11 11:16 ` Mostafa Saleh
2026-05-11 14:18 ` Jason Gunthorpe
2026-05-13 21:54 ` Mostafa Saleh
2026-05-01 11:19 ` [PATCH v6 07/25] KVM: arm64: iommu: Introduce IOMMU driver infrastructure Mostafa Saleh
2026-07-10 16:17 ` Vincent Donnefort
2026-07-13 10:13 ` Mostafa Saleh
2026-05-01 11:19 ` [PATCH v6 08/25] KVM: arm64: iommu: Shadow host stage-2 page table Mostafa Saleh
2026-05-01 13:00 ` Jason Gunthorpe
2026-05-04 12:28 ` Mostafa Saleh
2026-05-09 23:27 ` Jason Gunthorpe
2026-05-11 11:24 ` Mostafa Saleh
2026-05-11 14:22 ` Jason Gunthorpe
2026-05-12 10:42 ` Mostafa Saleh
2026-05-12 12:36 ` Jason Gunthorpe
2026-07-13 13:24 ` Vincent Donnefort
2026-07-13 14:00 ` Mostafa Saleh
2026-07-13 15:14 ` Vincent Donnefort
2026-07-13 15:27 ` Vincent Donnefort
2026-07-13 15:58 ` Mostafa Saleh
2026-07-13 15:51 ` Mostafa Saleh
2026-07-13 16:03 ` Vincent Donnefort
2026-07-13 18:19 ` Mostafa Saleh
2026-07-14 8:16 ` Vincent Donnefort
2026-07-14 8:29 ` Mostafa Saleh
2026-07-14 8:47 ` Vincent Donnefort
2026-07-14 8:56 ` Mostafa Saleh
2026-05-01 11:19 ` [PATCH v6 09/25] KVM: arm64: iommu: Add memory pool Mostafa Saleh
2026-07-10 16:33 ` Vincent Donnefort
2026-07-13 10:35 ` Mostafa Saleh
2026-07-14 8:43 ` Vincent Donnefort
2026-07-14 9:01 ` Mostafa Saleh
2026-05-01 11:19 ` [PATCH v6 10/25] KVM: arm64: iommu: Support DABT for IOMMU Mostafa Saleh
2026-05-01 11:19 ` [PATCH v6 11/25] iommu/arm-smmu-v3-kvm: Add SMMUv3 driver Mostafa Saleh
2026-05-01 11:19 ` [PATCH v6 12/25] iommu/arm-smmu-v3-kvm: Add the kernel driver Mostafa Saleh
2026-05-01 11:19 ` [PATCH v6 13/25] iommu/arm-smmu-v3-kvm: Probe SMMU HW Mostafa Saleh
2026-05-01 12:51 ` Jason Gunthorpe
2026-05-04 12:30 ` Mostafa Saleh
2026-05-01 11:19 ` [PATCH v6 14/25] iommu/arm-smmu-v3-kvm: Add MMIO emulation Mostafa Saleh
2026-05-01 11:19 ` [PATCH v6 15/25] iommu/arm-smmu-v3-kvm: Shadow the command queue Mostafa Saleh
2026-05-01 11:19 ` [PATCH v6 16/25] iommu/arm-smmu-v3-kvm: Add CMDQ functions Mostafa Saleh
2026-07-10 16:01 ` Vincent Donnefort
2026-07-13 11:07 ` Mostafa Saleh
2026-05-01 11:19 ` [PATCH v6 17/25] iommu/arm-smmu-v3-kvm: Emulate CMDQ for host Mostafa Saleh
2026-05-01 11:19 ` [PATCH v6 18/25] iommu/arm-smmu-v3-kvm: Shadow stream table Mostafa Saleh
2026-05-01 11:19 ` [PATCH v6 19/25] iommu/arm-smmu-v3-kvm: Shadow STEs Mostafa Saleh
2026-05-01 11:19 ` [PATCH v6 20/25] iommu/arm-smmu-v3-kvm: Share other queues Mostafa Saleh
2026-05-01 11:19 ` [PATCH v6 21/25] iommu/arm-smmu-v3-kvm: Emulate GBPA Mostafa Saleh
2026-05-01 11:19 ` [PATCH v6 22/25] iommu/io-pgtable-arm: Support io-pgtable-arm in the hypervisor Mostafa Saleh
2026-05-01 11:19 ` [PATCH v6 23/25] iommu/arm-smmu-v3-kvm: Shadow the CPU stage-2 page table Mostafa Saleh
2026-05-01 11:19 ` [PATCH v6 24/25] iommu/arm-smmu-v3-kvm: Enable nesting Mostafa Saleh
2026-05-01 11:19 ` [PATCH v6 25/25] KVM: arm64: Add documentation for pKVM DMA isolation Mostafa Saleh
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=alOyzOL16dhYk724@google.com \
--to=smostafa@google.com \
--cc=catalin.marinas@arm.com \
--cc=iommu@lists.linux.dev \
--cc=jean-philippe@linaro.org \
--cc=jgg@ziepe.ca \
--cc=joey.gouly@arm.com \
--cc=joro@8bytes.org \
--cc=keirf@google.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=oliver.upton@linux.dev \
--cc=qperret@google.com \
--cc=sebastianene@google.com \
--cc=suzuki.poulose@arm.com \
--cc=tabba@google.com \
--cc=vdonnefort@google.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.