Re: [PATCH v4 10/28] KVM: arm64: iommu: Shadow host stage-2 page table

Will Deacon
2025-09-26T14:42:45Z

On Tue, Sep 16, 2025 at 02:24:46PM +0000, Mostafa Saleh wrote:
> On Tue, Sep 09, 2025 at 03:42:07PM +0100, Will Deacon wrote:
> > On Tue, Aug 19, 2025 at 09:51:38PM +0000, Mostafa Saleh wrote:
> > > diff --git a/arch/arm64/kvm/hyp/nvhe/iommu/iommu.c b/arch/arm64/kvm/hyp/nvhe/iommu/iommu.c
> > > index a01c036c55be..f7d1c8feb358 100644
> > > --- a/arch/arm64/kvm/hyp/nvhe/iommu/iommu.c
> > > +++ b/arch/arm64/kvm/hyp/nvhe/iommu/iommu.c
> > > @@ -4,15 +4,94 @@
> > > *
> > > * Copyright (C) 2022 Linaro Ltd.
> > > */
> > > +#include <linux/iommu.h>
> > > +
> > > #include <nvhe/iommu.h>
> > > +#include <nvhe/mem_protect.h>
> > > +#include <nvhe/spinlock.h>
> > >
> > > /* Only one set of ops supported */
> > > struct kvm_iommu_ops *kvm_iommu_ops;
> > >
> > > +/* Protected by host_mmu.lock */
> > > +static bool kvm_idmap_initialized;
> > > +
> > > +static inline int pkvm_to_iommu_prot(enum kvm_pgtable_prot prot)
> > > +{
> > > + int iommu_prot = 0;
> > > +
> > > + if (prot & KVM_PGTABLE_PROT_R)
> > > + iommu_prot |= IOMMU_READ;
> > > + if (prot & KVM_PGTABLE_PROT_W)
> > > + iommu_prot |= IOMMU_WRITE;
> > > + if (prot == PKVM_HOST_MMIO_PROT)
> > > + iommu_prot |= IOMMU_MMIO;
> >
> > This looks a little odd to me.
> >
> > On the CPU side, the only different between PKVM_HOST_MEM_PROT and
> > PKVM_HOST_MMIO_PROT is that the former has execute permission. Both are
> > mapped as cacheable at stage-2 because it's the job of the host to set
> > the more restrictive memory type at stage-1.
> >
> > Carrying that over to the SMMU would suggest that we don't care about
> > IOMMU_MMIO at stage-2 at all, so why do we need to set it here?
>
> Unlike the CPU, the host can set the SMMU to bypass, in that case the
> hypervisor will attach its stage-2 with no stage-1 configured. So,
> stage-2 must have the correct attrs for MMIO.

I'm not sure about that.

If the SMMU is in stage-1 bypass, we still have the incoming memory
attributes from the transaction (modulo MTCFG which we shouldn't be
setting) and they should combine with the stage-2 attributes in roughly
the same way as the CPU, no?

> > > +static int __snapshot_host_stage2(const struct kvm_pgtable_visit_ctx *ctx,
> > > + enum kvm_pgtable_walk_flags visit)
> > > +{
> > > + u64 start = ctx->addr;
> > > + kvm_pte_t pte = *ctx->ptep;
> > > + u32 level = ctx->level;
> > > + u64 end = start + kvm_granule_size(level);
> > > + int prot = IOMMU_READ | IOMMU_WRITE;
> > > +
> > > + /* Keep unmapped. */
> > > + if (pte && !kvm_pte_valid(pte))
> > > + return 0;
> > > +
> > > + if (kvm_pte_valid(pte))
> > > + prot = pkvm_to_iommu_prot(kvm_pgtable_stage2_pte_prot(pte));
> > > + else if (!addr_is_memory(start))
> > > + prot |= IOMMU_MMIO;
> >
> > Why do we need to map MMIO regions pro-actively here? I'd have thought
> > we could just do:
> >
> > if (!kvm_pte_valid(pte))
> > return 0;
> >
> > prot = pkvm_to_iommu_prot(kvm_pgtable_stage2_pte_prot(pte);
> > kvm_iommu_ops->host_stage2_idmap(start, end, prot);
> > return 0;
> >
> > but I think that IOMMU_MMIO is throwing me again...
>
> We have to map everything pro-actively as we don’t handle page faults
> in the SMMUv3 driver.
> This would be a future work where the CPU stage-2 page table is shared with
> the SMMUv3.

Ah yes, I'd forgotten about that.

Thanks,

Will