* Re: [PATCH v2 09/19] PCI/TSM: Support creating encrypted MMIO descriptors via TDISP Report
From: Jason Gunthorpe @ 2026-03-23 18:20 UTC (permalink / raw)
To: Alexey Kardashevskiy
Cc: Xu Yilun, Aneesh Kumar K.V, Dan Williams, linux-coco, linux-pci,
gregkh, bhelgaas, alistair23, lukas, Arnd Bergmann
In-Reply-To: <228298b7-76bc-4ffb-bec0-0c35fd487c4c@amd.com>
On Mon, Mar 16, 2026 at 04:19:30PM +1100, Alexey Kardashevskiy wrote:
> and btw this only works if the entity generating the MMIO reporting
> offset (==TSM) knows about BARs sizes, which is not the case for AMD
> - the FW has no access to the config space (so the HV needs to feed
> this to the FW? may be). Thanks,
Then your platform just shouldn't use the mmio offset feature. Set it
to 0 always.
Jason
^ permalink raw reply
* Re: [PATCH v2 08/19] PCI/TSM: Add "evidence" support
From: Jason Gunthorpe @ 2026-03-23 18:18 UTC (permalink / raw)
To: Dan Williams
Cc: Lukas Wunner, Jakub Kicinski, linux-coco, linux-pci, gregkh, aik,
aneesh.kumar, yilun.xu, bhelgaas, alistair23, Donald Hunter
In-Reply-To: <69ba5abaf3ae7_7ee3100d@dwillia2-mobl4.notmuch>
On Wed, Mar 18, 2026 at 12:56:42AM -0700, Dan Williams wrote:
> Lukas Wunner wrote:
> [..]
> > At this point perhaps your conclusion is that netlink isn't the right
> > protocol for this job. It's great for transmitting sets of small items,
> > some of which may be optional, but it's obviously not well-suited for
> > large items.
>
> Right, and sysfs is not well suited for transaction in/out semantics.
>
> > Jason Gunthorpe was quite insistent that we use netlink and you know
>
> Jason can of course correct me, but the insistence was less that netlink
> was the right tool for the job, and more that sysfs was the wrong tool
> for the job.
+1
netlink is a good starting point, but if it isn't fitting well then
the next stop would be an ioctl char dev..
Jason
^ permalink raw reply
* Re: [PATCH v2 03/19] device core: Introduce confidential device acceptance
From: Jason Gunthorpe @ 2026-03-23 18:14 UTC (permalink / raw)
To: Dan Williams
Cc: Greg KH, linux-coco, linux-pci, aik, aneesh.kumar, yilun.xu,
bhelgaas, alistair23, lukas, Christoph Hellwig, Marek Szyprowski,
Robin Murphy, Roman Kisel, Samuel Ortiz, Rafael J. Wysocki,
Danilo Krummrich
In-Reply-To: <69b4baab2b950_b2b610013@dwillia2-mobl4.notmuch>
On Fri, Mar 13, 2026 at 06:32:27PM -0700, Dan Williams wrote:
> The problem is that for all the buses that do not currently have a
> "device authorization" concept only userspace can decide that a device
> should skip bind by default. For that, I propose module autoprobe policy
> [1]. Not yet convinced the kernel needs its own per-device "no bind"
> policy.
I think it is just part of the broader definition of the level that
extends into the iommu and so on. It makes sense to have this kind of
no-binding security level, IMHO.
> > The DMA API just wants a flag in the struct device that says if the
> > device can access encrypted memory or only decrypted.
>
> You mean separate "trusted to access private" and "currently enabled to
> access private" properties? I am trying to think of a situation where
> "dev->trust >= 3" and a flag saying "disable bouncing for encrypted
> memory" would ever disagree.
I'm steering the trust level toward more of an acceptance criteria.
If the trust level is you have access to private memory but the device
can't actually do that then fail the trust level change.
Same for the reverse, if the trust level says no private memory and the
device is T=1 then fail the trust level change.
> That bit though has lock-to-run consistency expectations. So if the
> kernel does not yet fully trust the device by time the relying party is
> satisfied, and the uAPI to transition the device into the TCB (level 3)
> is driver-core generic it raises TOCTOU issues in my mind. The
> driver-core would need to ask the bus "user now trusts this device, do
> you?".
Huh? No, there is no concept of trust in the kernel. The userspace
setting level 3 is "I now ack that this device is trusted", there is
no further trust cross check. If TSM side says it is in RUN/T=1 then
we are done.
If we fall out of RUN then the level auto-resets back to 0 and
userspace has to go around and fix it again. (ignoring driver RAS)
> Aneesh and I are currently debating on Discord whether the kernel needs
> to protect against guest userspace confusing itself.
Userspace that controls acceptance must be part of the TCB or the
whole model is fully broken. If your guest userspace is so security
broken it can accept devices it doesn't mean to then just forget it.
> However, to Aneesh's point we could protect against that with a
> transactional uAPI like netlink that can express "trust if and only if
> the device has not been relocked before final accept" by passing a
> cookie obtained at lock to accept. That would be awkward to coordinate
> with driver-core generic uAPI for trust.
You could, but why make it so complicated? The whole LOCKED/RUN thing
is already supposed to deal with TOCTOU, doesn't it? The CSP cannot
trick a device to fall out of LOCKED an the re-enter LOCKED without
the VM knowing.
The VM attacking itself on something as security critical as device
accepance can't be in scope :\
> > This way nothing is coupled and the kernel can offer all kinds of
> > different uAPI for device verification. Userspaces picks the
> > appropriate one and acks it with the level change.
>
> Thunderbolt already has authorized uAPI. I expect adding dev->trust
> support to thunderbolt is more related to ATS privilege and private
> memory privilege.
It brings it into the whole 'measure the device and then decide what
to do with it' framework. The trust level is still the generic ack
that the device is allowed the participate in the system with whatever
level of security.
Jason
^ permalink raw reply
* Re: [PATCH v3 2/3] swiotlb: dma: its: Enforce host page-size alignment for shared buffers
From: Aneesh Kumar K.V @ 2026-03-23 13:52 UTC (permalink / raw)
To: kernel test robot, linux-kernel, iommu, linux-coco,
linux-arm-kernel, kvmarm
Cc: llvm, oe-kbuild-all, Marc Zyngier, Thomas Gleixner,
Catalin Marinas, Will Deacon, Jason Gunthorpe, Marek Szyprowski,
Robin Murphy, Steven Price, Suzuki K Poulose
In-Reply-To: <202603092320.JgtItJg0-lkp@intel.com>
kernel test robot <lkp@intel.com> writes:
> Hi Aneesh,
>
> kernel test robot noticed the following build errors:
>
> [auto build test ERROR on arm64/for-next/core]
> [also build test ERROR on tip/irq/core arm/for-next arm/fixes kvmarm/next soc/for-next linus/master v7.0-rc3 next-20260306]
> [If your patch is applied to the wrong git tree, kindly drop us a note.
> And when submitting patch, we suggest to use '--base' as documented in
> https://git-scm.com/docs/git-format-patch#_base_tree_information]
>
> url: https://github.com/intel-lab-lkp/linux/commits/Aneesh-Kumar-K-V-Arm/dma-direct-swiotlb-handle-swiotlb-alloc-free-outside-__dma_direct_alloc_pages/20260309-182834
> base: https://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git for-next/core
> patch link: https://lore.kernel.org/r/20260309102625.2315725-3-aneesh.kumar%40kernel.org
> patch subject: [PATCH v3 2/3] swiotlb: dma: its: Enforce host page-size alignment for shared buffers
> config: i386-randconfig-012-20260309 (https://download.01.org/0day-ci/archive/20260309/202603092320.JgtItJg0-lkp@intel.com/config)
> compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261)
> reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260309/202603092320.JgtItJg0-lkp@intel.com/reproduce)
>
> If you fix the issue in a separate patch/commit (i.e. not just a new version of
> the same patch/commit), kindly add following tags
> | Reported-by: kernel test robot <lkp@intel.com>
> | Closes: https://lore.kernel.org/oe-kbuild-all/202603092320.JgtItJg0-lkp@intel.com/
>
> All errors (new ones prefixed by >>):
>
> In file included from arch/x86/kernel/asm-offsets.c:9:
> In file included from include/linux/crypto.h:15:
> In file included from include/linux/completion.h:12:
> In file included from include/linux/swait.h:7:
> In file included from include/linux/spinlock.h:59:
> In file included from include/linux/irqflags.h:18:
> In file included from arch/x86/include/asm/irqflags.h:5:
> In file included from arch/x86/include/asm/processor-flags.h:6:
>>> include/linux/mem_encrypt.h:60:9: error: use of undeclared identifier 'PAGE_SIZE'
> 60 | return PAGE_SIZE;
> | ^
>>> include/linux/mem_encrypt.h:66:9: error: call to undeclared function 'ALIGN'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
> 66 | return ALIGN(size, mem_decrypt_granule_size());
> | ^
>
Fixed this by including
modified include/linux/mem_encrypt.h
@@ -11,6 +11,8 @@
#define __MEM_ENCRYPT_H__
#ifndef __ASSEMBLY__
+#include <linux/align.h>
+#include <vdso/page.h>
#ifdef CONFIG_ARCH_HAS_MEM_ENCRYPT
Will include this in the next patch update.
The other alternative is to switch that to a macro
modified include/linux/mem_encrypt.h
@@ -55,16 +55,10 @@
#endif
#ifndef mem_decrypt_granule_size
-static inline size_t mem_decrypt_granule_size(void)
-{
- return PAGE_SIZE;
-}
+#define mem_decrypt_granule_size() PAGE_SIZE
#endif
-static inline size_t mem_decrypt_align(size_t size)
-{
- return ALIGN(size, mem_decrypt_granule_size());
-}
+#define mem_decrypt_align(size) ALIGN((size), mem_decrypt_granule_size())
^ permalink raw reply
* Re: [PATCH v13 17/48] arm64: RMI: Allocate/free RECs to match vCPUs
From: Suzuki K Poulose @ 2026-03-23 11:56 UTC (permalink / raw)
To: Steven Price, kvm, kvmarm
Cc: Catalin Marinas, Marc Zyngier, Will Deacon, James Morse,
Oliver Upton, Zenghui Yu, linux-arm-kernel, linux-kernel,
Joey Gouly, Alexandru Elisei, Christoffer Dall, Fuad Tabba,
linux-coco, Ganapatrao Kulkarni, Gavin Shan, Shanker Donthineni,
Alper Gun, Aneesh Kumar K . V, Emi Kisanuki, Vishal Annapurve
In-Reply-To: <20260318155413.793430-18-steven.price@arm.com>
Hi,
This is a NOTE for the fellow reviewers. This patch will undergo some
changes to handle how the AUX granules (metadata storage for RMM) for
the REC (aka vCPU) will be donated/reclaimed with RMM-v2.0.
Please see PATCH 48/48 for the changes with the new Stateful RMI
Operations (SRO), for REC create and destory.
I have tried to mark the areas affected below.
On 18/03/2026 15:53, Steven Price wrote:
> The RMM maintains a data structure known as the Realm Execution Context
> (or REC). It is similar to struct kvm_vcpu and tracks the state of the
> virtual CPUs. KVM must delegate memory and request the structures are
> created when vCPUs are created, and suitably tear down on destruction.
>
> RECs must also be supplied with addition pages - auxiliary (or AUX)
> granules - for storing the larger registers state (e.g. for SVE). The
> number of AUX granules for a REC depends on the parameters with which
> the Realm was created - the RMM makes this information available via the
> RMI_REC_AUX_COUNT call performed after creating the Realm Descriptor (RD).
>
> Note that only some of register state for the REC can be set by KVM, the
> rest is defined by the RMM (zeroed). The register state then cannot be
> changed by KVM after the REC is created (except when the guest
> explicitly requests this e.g. by performing a PSCI call).
>
> Signed-off-by: Steven Price <steven.price@arm.com>
> ---
> Changes since v12:
> * Use the new range-based delegation RMI.
> Changes since v11:
> * Remove the KVM_ARM_VCPU_REC feature. User space no longer needs to
> configure each VCPU separately, RECs are created on the first VCPU
> run of the guest.
> Changes since v9:
> * Size the aux_pages array according to the PAGE_SIZE of the host.
> Changes since v7:
> * Add comment explaining the aux_pages array.
> * Rename "undeleted_failed" variable to "should_free" to avoid a
> confusing double negative.
> Changes since v6:
> * Avoid reporting the KVM_ARM_VCPU_REC feature if the guest isn't a
> realm guest.
> * Support host page size being larger than RMM's granule size when
> allocating/freeing aux granules.
> Changes since v5:
> * Separate the concept of vcpu_is_rec() and
> kvm_arm_vcpu_rec_finalized() by using the KVM_ARM_VCPU_REC feature as
> the indication that the VCPU is a REC.
> Changes since v2:
> * Free rec->run earlier in kvm_destroy_realm() and adapt to previous patches.
> ---
> arch/arm64/include/asm/kvm_emulate.h | 2 +-
> arch/arm64/include/asm/kvm_host.h | 3 +
> arch/arm64/include/asm/kvm_rmi.h | 21 +++
> arch/arm64/kvm/arm.c | 10 +-
> arch/arm64/kvm/reset.c | 1 +
> arch/arm64/kvm/rmi.c | 196 +++++++++++++++++++++++++++
> 6 files changed, 230 insertions(+), 3 deletions(-)
>
> diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h
> index 39310d9b4e16..d194d91fbc2a 100644
> --- a/arch/arm64/include/asm/kvm_emulate.h
> +++ b/arch/arm64/include/asm/kvm_emulate.h
> @@ -708,7 +708,7 @@ static inline bool kvm_realm_is_created(struct kvm *kvm)
>
> static inline bool vcpu_is_rec(struct kvm_vcpu *vcpu)
> {
> - return false;
> + return kvm_is_realm(vcpu->kvm);
> }
>
> #endif /* __ARM64_KVM_EMULATE_H__ */
> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index 9267a2f2d65b..64304848aad4 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -924,6 +924,9 @@ struct kvm_vcpu_arch {
>
> /* Per-vcpu TLB for VNCR_EL2 -- NULL when !NV */
> struct vncr_tlb *vncr_tlb;
> +
> + /* Realm meta data */
> + struct realm_rec rec;
> };
>
> /*
> diff --git a/arch/arm64/include/asm/kvm_rmi.h b/arch/arm64/include/asm/kvm_rmi.h
> index 6c13847480f7..4e2c61e71a38 100644
> --- a/arch/arm64/include/asm/kvm_rmi.h
> +++ b/arch/arm64/include/asm/kvm_rmi.h
> @@ -63,6 +63,26 @@ struct realm {
> unsigned int ia_bits;
> };
>
> +/**
> + * struct realm_rec - Additional per VCPU data for a Realm
> + *
> + * @mpidr: MPIDR (Multiprocessor Affinity Register) value to identify this VCPU
> + * @rec_page: Kernel VA of the RMM's private page for this REC
> + * @aux_pages: Additional pages private to the RMM for this REC
> + * @run: Kernel VA of the RmiRecRun structure shared with the RMM
> + */
> +struct realm_rec {
> + unsigned long mpidr;
> + void *rec_page;
> + /*
> + * REC_PARAMS_AUX_GRANULES is the maximum number of 4K granules that
> + * the RMM can require. The array is sized to be large enough for the
> + * maximum number of host sized pages that could be required.
> + */
> + struct page *aux_pages[(REC_PARAMS_AUX_GRANULES * SZ_4K) >> PAGE_SHIFT];
> + struct rec_run *run;
> +};
> +
> void kvm_init_rmi(void);
> u32 kvm_realm_ipa_limit(void);
>
> @@ -70,6 +90,7 @@ int kvm_init_realm_vm(struct kvm *kvm);
> int kvm_activate_realm(struct kvm *kvm);
> void kvm_destroy_realm(struct kvm *kvm);
> void kvm_realm_destroy_rtts(struct kvm *kvm);
> +void kvm_destroy_rec(struct kvm_vcpu *vcpu);
>
> static inline bool kvm_realm_is_private_address(struct realm *realm,
> unsigned long addr)
> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> index c8e51ed009c0..8c50ebd9fba0 100644
> --- a/arch/arm64/kvm/arm.c
> +++ b/arch/arm64/kvm/arm.c
> @@ -575,6 +575,8 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
> /* Force users to call KVM_ARM_VCPU_INIT */
> vcpu_clear_flag(vcpu, VCPU_INITIALIZED);
>
> + vcpu->arch.rec.mpidr = INVALID_HWID;
> +
> vcpu->arch.mmu_page_cache.gfp_zero = __GFP_ZERO;
>
> /* Set up the timer */
> @@ -1549,7 +1551,7 @@ int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level,
> return -EINVAL;
> }
>
> -static unsigned long system_supported_vcpu_features(void)
> +static unsigned long system_supported_vcpu_features(struct kvm *kvm)
> {
> unsigned long features = KVM_VCPU_VALID_FEATURES;
>
> @@ -1587,7 +1589,7 @@ static int kvm_vcpu_init_check_features(struct kvm_vcpu *vcpu,
> return -ENOENT;
> }
>
> - if (features & ~system_supported_vcpu_features())
> + if (features & ~system_supported_vcpu_features(vcpu->kvm))
> return -EINVAL;
>
> /*
> @@ -1609,6 +1611,10 @@ static int kvm_vcpu_init_check_features(struct kvm_vcpu *vcpu,
> if (test_bit(KVM_ARM_VCPU_HAS_EL2, &features))
> return -EINVAL;
>
> + /* Realms are incompatible with AArch32 */
> + if (vcpu_is_rec(vcpu))
> + return -EINVAL;
> +
> return 0;
> }
>
> diff --git a/arch/arm64/kvm/reset.c b/arch/arm64/kvm/reset.c
> index 959532422d3a..4bbf58892928 100644
> --- a/arch/arm64/kvm/reset.c
> +++ b/arch/arm64/kvm/reset.c
> @@ -161,6 +161,7 @@ void kvm_arm_vcpu_destroy(struct kvm_vcpu *vcpu)
> free_page((unsigned long)vcpu->arch.ctxt.vncr_array);
> kfree(vcpu->arch.vncr_tlb);
> kfree(vcpu->arch.ccsidr);
> + kvm_destroy_rec(vcpu);
> }
>
> static void kvm_vcpu_reset_sve(struct kvm_vcpu *vcpu)
> diff --git a/arch/arm64/kvm/rmi.c b/arch/arm64/kvm/rmi.c
> index 937fababf960..6daf14c4b413 100644
> --- a/arch/arm64/kvm/rmi.c
> +++ b/arch/arm64/kvm/rmi.c
> @@ -207,6 +207,28 @@ static int get_start_level(struct realm *realm)
> return 4 - stage2_pgtable_levels(realm->ia_bits);
> }
>
> +static int delegate_range(phys_addr_t phys, unsigned long size)
> +{
> + unsigned long ret;
> + unsigned long top = phys + size;
> + unsigned long out_top;
> +
> + while (phys < top) {
> + ret = rmi_granule_range_delegate(phys, top, &out_top);
> + if (ret == RMI_SUCCESS)
> + phys = out_top;
> + else if (ret != RMI_BUSY && ret != RMI_BLOCKED)
> + return ret;
> + }
> +
> + return ret;
> +}
> +
> +static int delegate_page(phys_addr_t phys)
> +{
> + return delegate_range(phys, PAGE_SIZE);
> +}
> +
> static int undelegate_range(phys_addr_t phys, unsigned long size)
> {
> unsigned long ret;
> @@ -372,9 +394,177 @@ static int realm_ensure_created(struct kvm *kvm)
> return -ENXIO;
> }
>
--->8--- Cut here
> +static void free_rec_aux(struct page **aux_pages,
> + unsigned int num_aux)
> +{
> + unsigned int i;
> + unsigned int page_count = 0;
> +
> + for (i = 0; i < num_aux; i++) {
> + struct page *aux_page = aux_pages[page_count++];
> + phys_addr_t aux_page_phys = page_to_phys(aux_page);
> +
> + if (!WARN_ON(undelegate_page(aux_page_phys)))
> + __free_page(aux_page);
> + aux_page_phys += PAGE_SIZE;
> + }
> +}
> +
> +static int alloc_rec_aux(struct page **aux_pages,
> + u64 *aux_phys_pages,
> + unsigned int num_aux)
> +{
> + struct page *aux_page;
> + unsigned int i;
> + int ret;
> +
> + for (i = 0; i < num_aux; i++) {
> + phys_addr_t aux_page_phys;
> +
> + aux_page = alloc_page(GFP_KERNEL);
> + if (!aux_page) {
> + ret = -ENOMEM;
> + goto out_err;
> + }
> +
> + aux_page_phys = page_to_phys(aux_page);
> + if (delegate_page(aux_page_phys)) {
> + ret = -ENXIO;
> + goto err_undelegate;
> + }
> + aux_phys_pages[i] = aux_page_phys;
> + aux_pages[i] = aux_page;
> + }
> +
> + return 0;
> +err_undelegate:
> + while (i > 0) {
> + i--;
> + if (WARN_ON(undelegate_page(aux_phys_pages[i]))) {
> + /* Leak the page if the undelegate fails */
> + goto out_err;
> + }
> + }
> + __free_page(aux_page);
> +out_err:
> + free_rec_aux(aux_pages, i);
> + return ret;
> +}
> +
---8<---
> +static int kvm_create_rec(struct kvm_vcpu *vcpu)
> +{
...
---8>--- CUT here
> + r = alloc_rec_aux(rec->aux_pages, params->aux, realm->num_aux);
> + if (r)
> + goto out_undelegate_rmm_rec;
> +
> + params->num_rec_aux = realm->num_aux;
---8<---
> + params->mpidr = mpidr;
> +
> + if (rmi_rec_create(virt_to_phys(realm->rd),
> + rec_page_phys,
> + virt_to_phys(params))) {
> + r = -ENXIO;
> + goto out_free_rec_aux;
> + }
> +
> + rec->mpidr = mpidr;
> +
> + free_page((unsigned long)params);
> + return 0;
> +
> +out_free_rec_aux:
> + free_rec_aux(rec->aux_pages, realm->num_aux);
> +out_undelegate_rmm_rec:
> + if (WARN_ON(undelegate_page(rec_page_phys)))
> + rec->rec_page = NULL;
> +out_free_pages:
> + free_page((unsigned long)rec->run);
> + free_page((unsigned long)rec->rec_page);
> + free_page((unsigned long)params);
> + rec->run = NULL;
> + return r;
> +}
> +
> +void kvm_destroy_rec(struct kvm_vcpu *vcpu)
> +{
> + struct realm *realm = &vcpu->kvm->arch.realm;
> + struct realm_rec *rec = &vcpu->arch.rec;
> + unsigned long rec_page_phys;
> +
> + if (!vcpu_is_rec(vcpu))
> + return;
> +
> + if (!rec->run) {
> + /* Nothing to do if the VCPU hasn't been finalized */
> + return;
> + }
> +
> + free_page((unsigned long)rec->run);
> +
> + rec_page_phys = virt_to_phys(rec->rec_page);
> +
--8>-- Cut here
> + /*
> + * The REC and any AUX pages cannot be reclaimed until the REC is
> + * destroyed. So if the REC destroy fails then the REC page and any AUX
> + * pages will be leaked.
> + */
> + if (WARN_ON(rmi_rec_destroy(rec_page_phys)))
> + return;
> +
> + free_rec_aux(rec->aux_pages, realm->num_aux);
---8<---
Suzuki
> +
> + free_delegated_page(rec_page_phys);
> +}
> +
> int kvm_activate_realm(struct kvm *kvm)
> {
> struct realm *realm = &kvm->arch.realm;
> + struct kvm_vcpu *vcpu;
> + unsigned long i;
> int ret;
>
> if (kvm_realm_state(kvm) >= REALM_STATE_ACTIVE)
> @@ -397,6 +587,12 @@ int kvm_activate_realm(struct kvm *kvm)
> /* Mark state as dead in case we fail */
> WRITE_ONCE(realm->state, REALM_STATE_DEAD);
>
> + kvm_for_each_vcpu(i, vcpu, kvm) {
> + ret = kvm_create_rec(vcpu);
> + if (ret)
> + return ret;
> + }
> +
> ret = rmi_realm_activate(virt_to_phys(realm->rd));
> if (ret)
> return -ENXIO;
^ permalink raw reply
* Re: [PATCH v13 24/48] arm64: RMI: Allow populating initial contents
From: Suzuki K Poulose @ 2026-03-23 11:32 UTC (permalink / raw)
To: Steven Price, kvm, kvmarm
Cc: Catalin Marinas, Marc Zyngier, Will Deacon, James Morse,
Oliver Upton, Zenghui Yu, linux-arm-kernel, linux-kernel,
Joey Gouly, Alexandru Elisei, Christoffer Dall, Fuad Tabba,
linux-coco, Ganapatrao Kulkarni, Gavin Shan, Shanker Donthineni,
Alper Gun, Aneesh Kumar K . V, Emi Kisanuki, Vishal Annapurve
In-Reply-To: <20260318155413.793430-25-steven.price@arm.com>
On 18/03/2026 15:53, Steven Price wrote:
> The VMM needs to populate the realm with some data before starting (e.g.
> a kernel and initrd). This is measured by the RMM and used as part of
> the attestation later on.
>
> Signed-off-by: Steven Price <steven.price@arm.com>
> ---
> Changes since v12:
> * The ioctl now updates the structure with the amount populated rather
> than returning this through the ioctl return code.
> * Use the new RMM v2.0 range based RMI calls.
> * Adapt to upstream changes in kvm_gmem_populate().
> Changes since v11:
> * The multiplex CAP is gone and there's a new ioctl which makes use of
> the generic kvm_gmem_populate() functionality.
> Changes since v7:
> * Improve the error codes.
> * Other minor changes from review.
> Changes since v6:
> * Handle host potentially having a larger page size than the RMM
> granule.
> * Drop historic "par" (protected address range) from
> populate_par_region() - it doesn't exist within the current
> architecture.
> * Add a cond_resched() call in kvm_populate_realm().
> Changes since v5:
> * Refactor to use PFNs rather than tracking struct page in
> realm_create_protected_data_page().
> * Pull changes from a later patch (in the v5 series) for accessing
> pages from a guest memfd.
> * Do the populate in chunks to avoid holding locks for too long and
> triggering RCU stall warnings.
> ---
> arch/arm64/include/asm/kvm_rmi.h | 4 ++
> arch/arm64/kvm/Kconfig | 1 +
> arch/arm64/kvm/arm.c | 13 ++++
> arch/arm64/kvm/rmi.c | 111 +++++++++++++++++++++++++++++++
> 4 files changed, 129 insertions(+)
>
> diff --git a/arch/arm64/include/asm/kvm_rmi.h b/arch/arm64/include/asm/kvm_rmi.h
> index 46b0cbe6c202..bf663bb240c4 100644
> --- a/arch/arm64/include/asm/kvm_rmi.h
> +++ b/arch/arm64/include/asm/kvm_rmi.h
> @@ -96,6 +96,10 @@ int kvm_rec_enter(struct kvm_vcpu *vcpu);
> int kvm_rec_pre_enter(struct kvm_vcpu *vcpu);
> int handle_rec_exit(struct kvm_vcpu *vcpu, int rec_run_status);
>
> +struct kvm_arm_rmi_populate;
> +
> +int kvm_arm_rmi_populate(struct kvm *kvm,
> + struct kvm_arm_rmi_populate *arg);
> void kvm_realm_unmap_range(struct kvm *kvm,
> unsigned long ipa,
> unsigned long size,
> diff --git a/arch/arm64/kvm/Kconfig b/arch/arm64/kvm/Kconfig
> index 1cac6dfc0972..b495dfd3a8b4 100644
> --- a/arch/arm64/kvm/Kconfig
> +++ b/arch/arm64/kvm/Kconfig
> @@ -39,6 +39,7 @@ menuconfig KVM
> select GUEST_PERF_EVENTS if PERF_EVENTS
> select KVM_GUEST_MEMFD
> select KVM_GENERIC_MEMORY_ATTRIBUTES
> + select HAVE_KVM_ARCH_GMEM_POPULATE
> help
> Support hosting virtualized guest machines.
>
> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> index badb94b398bc..43d05da7e694 100644
> --- a/arch/arm64/kvm/arm.c
> +++ b/arch/arm64/kvm/arm.c
> @@ -2089,6 +2089,19 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
> return -EFAULT;
> return kvm_vm_ioctl_get_reg_writable_masks(kvm, &range);
> }
> + case KVM_ARM_RMI_POPULATE: {
> + struct kvm_arm_rmi_populate req;
> + int ret;
> +
> + if (!kvm_is_realm(kvm))
> + return -ENXIO;
> + if (copy_from_user(&req, argp, sizeof(req)))
> + return -EFAULT;
> + ret = kvm_arm_rmi_populate(kvm, &req);
> + if (copy_to_user(argp, &req, sizeof(req)))
> + return -EFAULT;
> + return ret;
> + }
> default:
> return -EINVAL;
> }
> diff --git a/arch/arm64/kvm/rmi.c b/arch/arm64/kvm/rmi.c
> index 13eed6f0b9eb..b48f4e12e4e0 100644
> --- a/arch/arm64/kvm/rmi.c
> +++ b/arch/arm64/kvm/rmi.c
> @@ -718,6 +718,80 @@ void kvm_realm_unmap_range(struct kvm *kvm, unsigned long start,
> realm_unmap_private_range(kvm, start, end, may_block);
> }
>
> +static int realm_create_protected_data_page(struct kvm *kvm,
minor nit: To align with the RMM ABI, could we rename this to :
realm_data_map_init() ?
> + unsigned long ipa,
> + kvm_pfn_t dst_pfn,
> + kvm_pfn_t src_pfn,
> + unsigned long flags)
> +{
> + struct realm *realm = &kvm->arch.realm;
> + phys_addr_t rd = virt_to_phys(realm->rd);
> + phys_addr_t dst_phys, src_phys;
> + int ret;
> +
> + dst_phys = __pfn_to_phys(dst_pfn);
> + src_phys = __pfn_to_phys(src_pfn);
> +
> + if (delegate_page(dst_phys))
> + return -ENXIO;
> +
> + ret = rmi_rtt_data_map_init(rd, dst_phys, ipa, src_phys, flags);
> + if (RMI_RETURN_STATUS(ret) == RMI_ERROR_RTT) {
> + /* Create missing RTTs and retry */
> + int level = RMI_RETURN_INDEX(ret);
> +
> + KVM_BUG_ON(level == RMM_RTT_MAX_LEVEL, kvm);
A buggy VMM can trigger this by calling RMI_POPULATE twice ? Should we
return -ENXIO here rather ? The delegate_page() above could prevent
normal cases, but is the VMM allowed to somehow trigger a "pfn" change
backing the KVM ? Either way, this need not be Fatal ?
Otherwise looks good to me.
Suzuki
> +
> + ret = realm_create_rtt_levels(realm, ipa, level,
> + RMM_RTT_MAX_LEVEL, NULL);
> + if (!ret) {
> + ret = rmi_rtt_data_map_init(rd, dst_phys, ipa, src_phys,
> + flags);
> + }
> + }
> +
> + if (ret) {
> + if (WARN_ON(undelegate_page(dst_phys))) {
> + /* Undelegate failed, so we leak the page */
> + get_page(pfn_to_page(dst_pfn));
> + }
> + }
> +
> + return ret;
> +}
> +
> +static int populate_region_cb(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn,
> + struct page *src_page, void *opaque)
> +{
> + unsigned long data_flags = *(unsigned long *)opaque;
> + phys_addr_t ipa = gfn_to_gpa(gfn);
> +
> + if (!src_page)
> + return -EOPNOTSUPP;
> +
> + return realm_create_protected_data_page(kvm, ipa, pfn,
> + page_to_pfn(src_page),
> + data_flags);
> +}
> +
> +static long populate_region(struct kvm *kvm,
> + gfn_t base_gfn,
> + unsigned long pages,
> + u64 uaddr,
> + unsigned long data_flags)
> +{
> + long ret = 0;
> +
> + mutex_lock(&kvm->slots_lock);
> + mmap_read_lock(current->mm);
> + ret = kvm_gmem_populate(kvm, base_gfn, u64_to_user_ptr(uaddr), pages,
> + populate_region_cb, &data_flags);
> + mmap_read_unlock(current->mm);
> + mutex_unlock(&kvm->slots_lock);
> +
> + return ret;
> +}
> +
> enum ripas_action {
> RIPAS_INIT,
> RIPAS_SET,
> @@ -815,6 +889,43 @@ static int realm_ensure_created(struct kvm *kvm)
> return -ENXIO;
> }
>
> +int kvm_arm_rmi_populate(struct kvm *kvm,
> + struct kvm_arm_rmi_populate *args)
> +{
> + unsigned long data_flags = 0;
> + unsigned long ipa_start = args->base;
> + unsigned long ipa_end = ipa_start + args->size;
> + long pages_populated;
> + int ret;
> +
> + if (args->reserved ||
> + (args->flags & ~KVM_ARM_RMI_POPULATE_FLAGS_MEASURE) ||
> + !IS_ALIGNED(ipa_start, PAGE_SIZE) ||
> + !IS_ALIGNED(ipa_end, PAGE_SIZE) ||
> + !IS_ALIGNED(args->source_uaddr, PAGE_SIZE))
> + return -EINVAL;
> +
> + ret = realm_ensure_created(kvm);
> + if (ret)
> + return ret;
> +
> + if (args->flags & KVM_ARM_RMI_POPULATE_FLAGS_MEASURE)
> + data_flags |= RMI_MEASURE_CONTENT;
> +
> + pages_populated = populate_region(kvm, gpa_to_gfn(ipa_start),
> + args->size >> PAGE_SHIFT,
> + args->source_uaddr, data_flags);
> +
> + if (pages_populated < 0)
> + return pages_populated;
> +
> + args->size -= pages_populated << PAGE_SHIFT;
> + args->source_uaddr += pages_populated << PAGE_SHIFT;
> + args->base += pages_populated << PAGE_SHIFT;
> +
> + return 0;
> +}
> +
> static void kvm_complete_ripas_change(struct kvm_vcpu *vcpu)
> {
> struct kvm *kvm = vcpu->kvm;
^ permalink raw reply
* Re: [PATCH v13 05/48] arm64: RMI: Temporarily add SMCs from RMM v1.0 spec
From: Suzuki K Poulose @ 2026-03-23 10:30 UTC (permalink / raw)
To: Marc Zyngier, Steven Price
Cc: kvm, kvmarm, Catalin Marinas, Will Deacon, James Morse,
Oliver Upton, Zenghui Yu, linux-arm-kernel, linux-kernel,
Joey Gouly, Alexandru Elisei, Christoffer Dall, Fuad Tabba,
linux-coco, Ganapatrao Kulkarni, Gavin Shan, Shanker Donthineni,
Alper Gun, Aneesh Kumar K . V, Emi Kisanuki, Vishal Annapurve
In-Reply-To: <87cy0xco7f.wl-maz@kernel.org>
Hi Marc,
On 21/03/2026 13:21, Marc Zyngier wrote:
> On Wed, 18 Mar 2026 15:53:29 +0000,
> Steven Price <steven.price@arm.com> wrote:
>>
>> Not all the functionality has been migrated to the v2.0 specification,
>> so for now we still rely on some v1.0 SMCs. This mixture is not
>> spec-compliant, but is necessary until an updated RMM is available.
>>
>
> This sort of things really makes it awkward to review the series.
I agree, that this makes it painful to review as it doesn't get you a
clear picture of what will stay or what will change and is not the best
use of the precious time of the Maintainers. We will make sure to flag
the appropriate portions in the next iteration. Apologies!
>
> Do you really expect the reviewers to mentally triage what is current
> and what is only throwaway code? I want to see patches that are merge
> candidates, not patches that are only there to cope with the broken
> state of the RMM.
Agree, we could clearly mark the "areas" of code that we anticipate to
change and by the next posting this will be clearly marked.
> If extra hacks are required to work with the current Franken-RMM, keep
> them as a separate series that doesn't pollute what is targeted at
> upstream.
In fact there are only a few areas that would change with a true RMM-
v2.0 spec compliant firmware. But yes, I acknowledge that feedback from
the maintainers would be fedback to the RMM spec and this might trigger
minor changes to align with them.
>
> It also means that any testing you have done will be invalidated when
> the one true RMM shows up...
Agreed, True, but not very much of the functionality/
spec are changing until we land in fully compliant 2.0 RMM.
See more on this below.
>
> "This is great."
>
> M.
> diff --git a/arch/arm64/include/asm/rmi_smc.h b/arch/arm64/include/asm/rmi_smc.h
> index 8a42b83218f8..049d71470486 100644
> --- a/arch/arm64/include/asm/rmi_smc.h
> +++ b/arch/arm64/include/asm/rmi_smc.h
> @@ -30,14 +30,15 @@
> #define SMC_RMI_REC_ENTER SMC_RMI_CALL(0x015c)
> #define SMC_RMI_RTT_CREATE SMC_RMI_CALL(0x015d)
> #define SMC_RMI_RTT_DESTROY SMC_RMI_CALL(0x015e)
> +#define SMC_RMI_RTT_MAP_UNPROTECTED SMC_RMI_CALL(0x015f) //
>
> #define SMC_RMI_RTT_READ_ENTRY SMC_RMI_CALL(0x0161)
> -
> +#define SMC_RMI_RTT_UNMAP_UNPROTECTED SMC_RMI_CALL(0x0162) //
The above two RMI commands help with mapping/unmapping Unprotected
memory and will be replaced with two renamed commands with "Range based"
ABI => SMC_RMI_RTT_UNPROT_{MAP,UNMAP}. So, as far as the KVM code is
concerned, we do the mapping a granule at a time (without hugetlb
support) and this is simply a change in the command in the true 2.0 RMM.
> #define SMC_RMI_RTT_DEV_VALIDATE SMC_RMI_CALL(0x0163)
> #define SMC_RMI_PSCI_COMPLETE SMC_RMI_CALL(0x0164)
> #define SMC_RMI_FEATURES SMC_RMI_CALL(0x0165)
> #define SMC_RMI_RTT_FOLD SMC_RMI_CALL(0x0166)
> -
> +#define SMC_RMI_REC_AUX_COUNT SMC_RMI_CALL(0x0167) //
This is part of the REC creation, where we donate AUXilliary granules
for the VCPU to save state. This is replaced by the SRO method, which we
have a WIP path at the top of the series and it will be cleaned up in
the next version.
We wanted to send this version out which is the first one with RMM-v2.0
spec, which has addressed the concerns and feedback around the RMM-v1.0
spec. But we have clearly felt short of marking "what is volatile" and
what is stable, to help the review. We will address this in the next
iteration.
Thanks for taking the time to respond.
Kind regards
Suzuki
^ permalink raw reply
* Re: [PATCH v13 20/48] arm64: RMI: Handle realm enter/exit
From: Suzuki K Poulose @ 2026-03-23 10:03 UTC (permalink / raw)
To: Steven Price, kvm, kvmarm
Cc: Catalin Marinas, Marc Zyngier, Will Deacon, James Morse,
Oliver Upton, Zenghui Yu, linux-arm-kernel, linux-kernel,
Joey Gouly, Alexandru Elisei, Christoffer Dall, Fuad Tabba,
linux-coco, Ganapatrao Kulkarni, Gavin Shan, Shanker Donthineni,
Alper Gun, Aneesh Kumar K . V, Emi Kisanuki, Vishal Annapurve
In-Reply-To: <02582577-3701-4d5c-88c6-3ce0b3328377@arm.com>
On 20/03/2026 16:32, Steven Price wrote:
> On 20/03/2026 14:08, Suzuki K Poulose wrote:
>> On 18/03/2026 15:53, Steven Price wrote:
>>> Entering a realm is done using a SMC call to the RMM. On exit the
>>> exit-codes need to be handled slightly differently to the normal KVM
>>> path so define our own functions for realm enter/exit and hook them
>>> in if the guest is a realm guest.
>>>
>>> Signed-off-by: Steven Price <steven.price@arm.com>
>>> Reviewed-by: Gavin Shan <gshan@redhat.com>
>>> ---
>>> Changes since v12:
>>> * Call guest_state_{enter,exit}_irqoff() around rmi_rec_enter().
>>> * Add handling of the IRQ exception case where IRQs need to be briefly
>>> enabled before exiting guest timing.
>>> Changes since v8:
>>> * Introduce kvm_rec_pre_enter() called before entering an atomic
>>> section to handle operations that might require memory allocation
>>> (specifically completing a RIPAS change introduced in a later patch).
>>> * Updates to align with upstream changes to hpfar_el2 which now
>>> (ab)uses
>>> HPFAR_EL2_NS as a valid flag.
>>> * Fix exit reason when racing with PSCI shutdown to return
>>> KVM_EXIT_SHUTDOWN rather than KVM_EXIT_UNKNOWN.
>>> Changes since v7:
>>> * A return of 0 from kvm_handle_sys_reg() doesn't mean the register has
>>> been read (although that can never happen in the current code). Tidy
>>> up the condition to handle any future refactoring.
>>> Changes since v6:
>>> * Use vcpu_err() rather than pr_err/kvm_err when there is an associated
>>> vcpu to the error.
>>> * Return -EFAULT for KVM_EXIT_MEMORY_FAULT as per the documentation for
>>> this exit type.
>>> * Split code handling a RIPAS change triggered by the guest to the
>>> following patch.
>>> Changes since v5:
>>> * For a RIPAS_CHANGE request from the guest perform the actual RIPAS
>>> change on next entry rather than immediately on the exit. This allows
>>> the VMM to 'reject' a RIPAS change by refusing to continue
>>> scheduling.
>>> Changes since v4:
>>> * Rename handle_rme_exit() to handle_rec_exit()
>>> * Move the loop to copy registers into the REC enter structure from the
>>> to rec_exit_handlers callbacks to kvm_rec_enter(). This fixes a bug
>>> where the handler exits to user space and user space wants to modify
>>> the GPRS.
>>> * Some code rearrangement in rec_exit_ripas_change().
>>> Changes since v2:
>>> * realm_set_ipa_state() now provides an output parameter for the
>>> top_iap that was changed. Use this to signal the VMM with the correct
>>> range that has been transitioned.
>>> * Adapt to previous patch changes.
>>> ---
>>> arch/arm64/include/asm/kvm_rmi.h | 4 +
>>> arch/arm64/kvm/Makefile | 2 +-
>>> arch/arm64/kvm/arm.c | 26 ++++-
>>> arch/arm64/kvm/rmi-exit.c | 178 +++++++++++++++++++++++++++++++
>>> arch/arm64/kvm/rmi.c | 43 ++++++++
>>> 5 files changed, 247 insertions(+), 6 deletions(-)
>>> create mode 100644 arch/arm64/kvm/rmi-exit.c
>>>
>>> diff --git a/arch/arm64/include/asm/kvm_rmi.h b/arch/arm64/include/
>>> asm/kvm_rmi.h
>>> index 4e2c61e71a38..7bec3a3976e7 100644
>>> --- a/arch/arm64/include/asm/kvm_rmi.h
>>> +++ b/arch/arm64/include/asm/kvm_rmi.h
>>> @@ -92,6 +92,10 @@ void kvm_destroy_realm(struct kvm *kvm);
>>> void kvm_realm_destroy_rtts(struct kvm *kvm);
>>> void kvm_destroy_rec(struct kvm_vcpu *vcpu);
>>> +int kvm_rec_enter(struct kvm_vcpu *vcpu);
>>> +int kvm_rec_pre_enter(struct kvm_vcpu *vcpu);
>>> +int handle_rec_exit(struct kvm_vcpu *vcpu, int rec_run_status);
>>> +
>>> static inline bool kvm_realm_is_private_address(struct realm *realm,
>>> unsigned long addr)
>>> {
>>> diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile
>>> index e17c4077d8e7..4b103bcbe760 100644
>>> --- a/arch/arm64/kvm/Makefile
>>> +++ b/arch/arm64/kvm/Makefile
>>> @@ -16,7 +16,7 @@ CFLAGS_handle_exit.o += -Wno-override-init
>>> kvm-y += arm.o mmu.o mmio.o psci.o hypercalls.o pvtime.o \
>>> inject_fault.o va_layout.o handle_exit.o config.o \
>>> guest.o debug.o reset.o sys_regs.o stacktrace.o \
>>> - vgic-sys-reg-v3.o fpsimd.o pkvm.o rmi.o \
>>> + vgic-sys-reg-v3.o fpsimd.o pkvm.o rmi.o rmi-exit.o \
>>> arch_timer.o trng.o vmid.o emulate-nested.o nested.o at.o \
>>> vgic/vgic.o vgic/vgic-init.o \
>>> vgic/vgic-irqfd.o vgic/vgic-v2.o \
>>> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
>>> index 45eff4c41cde..badb94b398bc 100644
>>> --- a/arch/arm64/kvm/arm.c
>>> +++ b/arch/arm64/kvm/arm.c
>>> @@ -1311,6 +1311,9 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
>>> if (ret > 0)
>>> ret = check_vcpu_requests(vcpu);
>>> + if (ret > 0 && vcpu_is_rec(vcpu))
>>> + ret = kvm_rec_pre_enter(vcpu);
>>> +
>>> /*
>>> * Preparing the interrupts to be injected also
>>> * involves poking the GIC, which must be done in a
>>> @@ -1358,7 +1361,10 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
>>> trace_kvm_entry(*vcpu_pc(vcpu));
>>> guest_timing_enter_irqoff();
>>> - ret = kvm_arm_vcpu_enter_exit(vcpu);
>>> + if (vcpu_is_rec(vcpu))
>>> + ret = kvm_rec_enter(vcpu);
>>> + else
>>> + ret = kvm_arm_vcpu_enter_exit(vcpu);
>>> vcpu->mode = OUTSIDE_GUEST_MODE;
>>> vcpu->stat.exits++;
>>> @@ -1404,7 +1410,9 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
>>> * context synchronization event) is necessary to ensure that
>>> * pending interrupts are taken.
>>> */
>>> - if (ARM_EXCEPTION_CODE(ret) == ARM_EXCEPTION_IRQ) {
>>> + if (ARM_EXCEPTION_CODE(ret) == ARM_EXCEPTION_IRQ ||
>>> + (vcpu_is_rec(vcpu) &&
>>> + vcpu->arch.rec.run->exit.exit_reason == RMI_EXIT_IRQ)) {
>>> local_irq_enable();
>>> isb();
>>> local_irq_disable();
>>> @@ -1416,8 +1424,13 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
>>> trace_kvm_exit(ret, kvm_vcpu_trap_get_class(vcpu),
>>> *vcpu_pc(vcpu));
>>> - /* Exit types that need handling before we can be preempted */
>>> - handle_exit_early(vcpu, ret);
>>> + if (!vcpu_is_rec(vcpu)) {
>>> + /*
>>> + * Exit types that need handling before we can be
>>> + * preempted
>>> + */
>>> + handle_exit_early(vcpu, ret);
>>> + }
>>> kvm_nested_sync_hwstate(vcpu);
>>> @@ -1442,7 +1455,10 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu
>>> *vcpu)
>>> ret = ARM_EXCEPTION_IL;
>>> }
>>> - ret = handle_exit(vcpu, ret);
>>> + if (vcpu_is_rec(vcpu))
>>> + ret = handle_rec_exit(vcpu, ret);
>>> + else
>>> + ret = handle_exit(vcpu, ret);
>>> }
>>> /* Tell userspace about in-kernel device output levels */
>>> diff --git a/arch/arm64/kvm/rmi-exit.c b/arch/arm64/kvm/rmi-exit.c
>>> new file mode 100644
>>> index 000000000000..f5701153dec0
>>> --- /dev/null
>>> +++ b/arch/arm64/kvm/rmi-exit.c
>>> @@ -0,0 +1,178 @@
>>> +// SPDX-License-Identifier: GPL-2.0-only
>>> +/*
>>> + * Copyright (C) 2023 ARM Ltd.
>>> + */
>>> +
>>> +#include <linux/kvm_host.h>
>>> +#include <kvm/arm_hypercalls.h>
>>> +#include <kvm/arm_psci.h>
>>> +
>>> +#include <asm/rmi_smc.h>
>>> +#include <asm/kvm_emulate.h>
>>> +#include <asm/kvm_rmi.h>
>>> +#include <asm/kvm_mmu.h>
>>> +
>>> +typedef int (*exit_handler_fn)(struct kvm_vcpu *vcpu);
>>> +
>>> +static int rec_exit_reason_notimpl(struct kvm_vcpu *vcpu)
>>> +{
>>> + struct realm_rec *rec = &vcpu->arch.rec;
>>> +
>>> + vcpu_err(vcpu, "Unhandled exit reason from realm (ESR: %#llx)\n",
>>> + rec->run->exit.esr);
>>> + return -ENXIO;
>>> +}
>>> +
>>> +static int rec_exit_sync_dabt(struct kvm_vcpu *vcpu)
>>> +{
>>> + return kvm_handle_guest_abort(vcpu);
>>> +}
>>> +
>>> +static int rec_exit_sync_iabt(struct kvm_vcpu *vcpu)
>>> +{
>>> + struct realm_rec *rec = &vcpu->arch.rec;
>>> +
>>> + vcpu_err(vcpu, "Unhandled instruction abort (ESR: %#llx).\n",
>>> + rec->run->exit.esr);
>>> + return -ENXIO;
>>> +}
>>> +
>>> +static int rec_exit_sys_reg(struct kvm_vcpu *vcpu)
>>> +{
>>> + struct realm_rec *rec = &vcpu->arch.rec;
>>> + unsigned long esr = kvm_vcpu_get_esr(vcpu);
>>> + int rt = kvm_vcpu_sys_get_rt(vcpu);
>>> + bool is_write = !(esr & 1);
>>> + int ret;
>>> +
>>> + if (is_write)
>>> + vcpu_set_reg(vcpu, rt, rec->run->exit.gprs[0]);
>>
>> The RMM has been fixed to indicate the correct value in ESR_ELx_SRT. So
>> this could be :
>> vcpu_set_reg(vcpu, rt, rec->run->ext.gprs[rt]); ?
>>
>>> +
>>> + ret = kvm_handle_sys_reg(vcpu);
>>> + if (!is_write)
>>> + rec->run->enter.gprs[0] = vcpu_get_reg(vcpu, rt);
>>
>> Same here ^
>
> True, although no functional change because it's always going to be 0.
>
>>> +
>>> + return ret;
>>> +}
>>> +
>>> +static exit_handler_fn rec_exit_handlers[] = {
>>> + [0 ... ESR_ELx_EC_MAX] = rec_exit_reason_notimpl,
>>> + [ESR_ELx_EC_SYS64] = rec_exit_sys_reg,
>>> + [ESR_ELx_EC_DABT_LOW] = rec_exit_sync_dabt,
>>> + [ESR_ELx_EC_IABT_LOW] = rec_exit_sync_iabt
>>> +};
>>> +
>>> +static int rec_exit_psci(struct kvm_vcpu *vcpu)
>>> +{
>>> + struct realm_rec *rec = &vcpu->arch.rec;
>>> + int i;
>>> +
>>> + for (i = 0; i < REC_RUN_GPRS; i++)
>>> + vcpu_set_reg(vcpu, i, rec->run->exit.gprs[i]);
>>> +
>>> + return kvm_smccc_call_handler(vcpu);
>>> +}
>>> +
>>> +static int rec_exit_ripas_change(struct kvm_vcpu *vcpu)
>>> +{
>>> + struct kvm *kvm = vcpu->kvm;
>>> + struct realm *realm = &kvm->arch.realm;
>>> + struct realm_rec *rec = &vcpu->arch.rec;
>>> + unsigned long base = rec->run->exit.ripas_base;
>>> + unsigned long top = rec->run->exit.ripas_top;
>>> + unsigned long ripas = rec->run->exit.ripas_value;
>>> +
>>> + if (!kvm_realm_is_private_address(realm, base) ||
>>> + !kvm_realm_is_private_address(realm, top - 1)) {
>>> + vcpu_err(vcpu, "Invalid RIPAS_CHANGE for %#lx - %#lx, ripas:
>>> %#lx\n",
>>> + base, top, ripas);
>>> + /* Set RMI_REJECT bit */
>>> + rec->run->enter.flags = REC_ENTER_FLAG_RIPAS_RESPONSE;
>>> + return -EINVAL;
>>> + }
>>> +
>>> + /* Exit to VMM, the actual RIPAS change is done on next entry */
>>> + kvm_prepare_memory_fault_exit(vcpu, base, top - base, false, false,
>>> + ripas == RMI_RAM);
>>> +
>>> + /*
>>> + * KVM_EXIT_MEMORY_FAULT requires an return code of -EFAULT, see the
>>> + * API documentation
>>> + */
>>> + return -EFAULT;
>>> +}
>>> +
>>> +static void update_arch_timer_irq_lines(struct kvm_vcpu *vcpu)
>>> +{
>>> + struct realm_rec *rec = &vcpu->arch.rec;
>>> +
>>> + __vcpu_assign_sys_reg(vcpu, CNTV_CTL_EL0, rec->run->exit.cntv_ctl);
>>> + __vcpu_assign_sys_reg(vcpu, CNTV_CVAL_EL0, rec->run-
>>>> exit.cntv_cval);
>>> + __vcpu_assign_sys_reg(vcpu, CNTP_CTL_EL0, rec->run->exit.cntp_ctl);
>>> + __vcpu_assign_sys_reg(vcpu, CNTP_CVAL_EL0, rec->run-
>>>> exit.cntp_cval);
>>> +
>>> + kvm_realm_timers_update(vcpu);
>>> +}
>>> +
>>> +/*
>>> + * Return > 0 to return to guest, < 0 on error, 0 (and set
>>> exit_reason) on
>>> + * proper exit to userspace.
>>> + */
>>> +int handle_rec_exit(struct kvm_vcpu *vcpu, int rec_run_ret)
>>> +{
>>> + struct realm_rec *rec = &vcpu->arch.rec;
>>> + u8 esr_ec = ESR_ELx_EC(rec->run->exit.esr);
>>> + unsigned long status, index;
>>> +
>>> + status = RMI_RETURN_STATUS(rec_run_ret);
>>> + index = RMI_RETURN_INDEX(rec_run_ret);
>>> +
>>> + /*
>>> + * If a PSCI_SYSTEM_OFF request raced with a vcpu executing, we
>>> might
>>> + * see the following status code and index indicating an attempt
>>> to run
>>> + * a REC when the RD state is SYSTEM_OFF. In this case, we just
>>> need to
>>> + * return to user space which can deal with the system event or
>>> will try
>>> + * to run the KVM VCPU again, at which point we will no longer
>>> attempt
>>> + * to enter the Realm because we will have a sleep request
>>> pending on
>>> + * the VCPU as a result of KVM's PSCI handling.
>>> + */
>>> + if (status == RMI_ERROR_REALM && index == 1) {
>>> + vcpu->run->exit_reason = KVM_EXIT_SHUTDOWN;
>>> + return 0;
>>> + }
>>> +
>>> + if (rec_run_ret)
>>> + return -ENXIO;
>>> +
>>> + vcpu->arch.fault.esr_el2 = rec->run->exit.esr;
>>
>> Even ESR_EL2 is only valid when the exit reason is RMI_EXIT_SYNC or
>> RMI_EXIT_SERROR.
>> Doing this unconditional copying is fine, as long as we don't consume
>> the esr_el2 in exit handling without consulting the exit reason, which
>> may not be available to the rest of the KVM. It may be safer to set it
>> to 0 ?
>
> For HPFAR_EL2 there is code in the kernel which hijacks the EL2_NS bit a
> 'valid' bit, hence we have to handle that one specially to record
> whether the value is valid or not.
>
> esr_el2/far_el2 may or may not be valid depending on the exit, but
> there's no 'valid' flag for the generic kernel code to look for - so
> that generic code either depends on the value (in which case 0 is just
> as invalid) or doesn't use it.
>
> My preference is to avoid trying to keep track of the exit reasons where
> such flags are valid and just provide the generic code with whatever the
> RMM provides. In any case the values are generally 'sanitised' by the
> RMM so they don't represent the real CPU registers.
>
>>> + vcpu->arch.fault.far_el2 = rec->run->exit.far;
>>> + /* HPFAR_EL2 is only valid for RMI_EXIT_SYNC */
>>> + vcpu->arch.fault.hpfar_el2 = 0;
>>> +
>>> + update_arch_timer_irq_lines(vcpu);
>>> +
>>> + /* Reset the emulation flags for the next run of the REC */
>>> + rec->run->enter.flags = 0;
>>> +
>>> + switch (rec->run->exit.exit_reason) {
>>> + case RMI_EXIT_SYNC:
>>> + /*
>>> + * HPFAR_EL2_NS is hijacked to indicate a valid HPFAR value,
>>> + * see __get_fault_info()
>>> + */
>>> + vcpu->arch.fault.hpfar_el2 = rec->run->exit.hpfar |
>>> HPFAR_EL2_NS;
>>> + return rec_exit_handlers[esr_ec](vcpu);
>>> + case RMI_EXIT_IRQ:
>>> + case RMI_EXIT_FIQ:
>>> + return 1;
>>> + case RMI_EXIT_PSCI:
>>> + return rec_exit_psci(vcpu);
>>> + case RMI_EXIT_RIPAS_CHANGE:
>>> + return rec_exit_ripas_change(vcpu);
>>
>> RMI_EXIT_SERROR is missing in the list above.
>
> Indeed, I think I need to read up on how that's meant to be handled.
>
>>> + }
>>> +
>>> + kvm_pr_unimpl("Unsupported exit reason: %u\n",
>>> + rec->run->exit.exit_reason);
>>
>>
>>
>>> + vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
>>> + return 0;
>>> +}
>>> diff --git a/arch/arm64/kvm/rmi.c b/arch/arm64/kvm/rmi.c
>>> index 6daf14c4b413..ee8aab098117 100644
>>> --- a/arch/arm64/kvm/rmi.c
>>> +++ b/arch/arm64/kvm/rmi.c
>>> @@ -394,6 +394,49 @@ static int realm_ensure_created(struct kvm *kvm)
>>> return -ENXIO;
>>> }
>>> +/*
>>> + * kvm_rec_pre_enter - Complete operations before entering a REC
>>> + *
>>> + * Some operations require work to be completed before entering a
>>> realm. That
>>> + * work may require memory allocation so cannot be done in the
>>> kvm_rec_enter()
>>> + * call.
>>> + *
>>> + * Return: 1 if we should enter the guest
>>> + * 0 if we should exit to userspace
>>> + * < 0 if we should exit to userspace, where the return value
>>> indicates
>>> + * an error
>>> + */
>>> +int kvm_rec_pre_enter(struct kvm_vcpu *vcpu)
>>> +{
>>> + struct realm_rec *rec = &vcpu->arch.rec;
>>> +
>>> + if (kvm_realm_state(vcpu->kvm) != REALM_STATE_ACTIVE)
>>> + return -EINVAL;
>>> +
>>> + switch (rec->run->exit.exit_reason) {
>>> + case RMI_EXIT_HOST_CALL:
>>> + case RMI_EXIT_PSCI:
>>> + for (int i = 0; i < REC_RUN_GPRS; i++)
>>> + rec->run->enter.gprs[i] = vcpu_get_reg(vcpu, i);
>>> + break;
>>> + }
>>> +
>>> + return 1;
>>> +}
>>> +
>>> +int noinstr kvm_rec_enter(struct kvm_vcpu *vcpu)
>>> +{
>>> + struct realm_rec *rec = &vcpu->arch.rec;
>>> + int ret;
>>> +
>>> + guest_state_enter_irqoff();
>>> + ret = rmi_rec_enter(virt_to_phys(rec->rec_page),
>>> + virt_to_phys(rec->run));
>>
>> In the normal VM case, we try to fixup some of the exits (e.g., GIC
>> CPUIF register accesses) which may be applicable to Realms. Do we
>> need such fixups here ? Given the cost of world switch, it is
>> debatable whether it matters or not.
>
> I'm not really sure what you are referring to here. Can you point me at
> the normal VM case? This function is the equivalent of
> kvm_arm_vcpu_enter_exit().
This happens via fixup_guest_exit() in either vhe/nvhe cases. The VGIC
registers are emulated in the fast path for normal VMs (when trapping is
enabled)
Cheers
Suzuki
>
> Thanks,
> Steve
>
>> Suzuki
>>> + guest_state_exit_irqoff();
>>> +
>>> + return ret;
>>> +}
>>> +
>>> static void free_rec_aux(struct page **aux_pages,
>>> unsigned int num_aux)
>>> {
>>
>
^ permalink raw reply
* Re: [PATCH v5 13/22] x86/virt/seamldr: Install a new TDX module
From: Chao Gao @ 2026-03-23 2:14 UTC (permalink / raw)
To: Kiryl Shutsemau
Cc: linux-kernel, linux-coco, kvm, binbin.wu, dan.j.williams,
dave.hansen, ira.weiny, kai.huang, nik.borisov, paulmck, pbonzini,
reinette.chatre, rick.p.edgecombe, sagis, seanjc, tony.lindgren,
vannapurve, vishal.l.verma, yilun.xu, Thomas Gleixner,
Ingo Molnar, Borislav Petkov, x86, H. Peter Anvin
In-Reply-To: <abv6fT8Rvk01DGah@thinkstation>
On Thu, Mar 19, 2026 at 01:32:07PM +0000, Kiryl Shutsemau wrote:
>On Sun, Mar 15, 2026 at 06:58:33AM -0700, Chao Gao wrote:
>> @@ -225,6 +227,7 @@ static void ack_state(void)
>> static int do_seamldr_install_module(void *seamldr_params)
>> {
>> enum module_update_state newstate, curstate = MODULE_UPDATE_START;
>> + struct tdx_module_args args = {};
>> int cpu = smp_processor_id();
>> bool primary;
>> int ret = 0;
>> @@ -243,6 +246,10 @@ static int do_seamldr_install_module(void *seamldr_params)
>> if (primary)
>> ret = tdx_module_shutdown();
>> break;
>> + case MODULE_UPDATE_CPU_INSTALL:
>> + args.rcx = __pa(seamldr_params);
>> + ret = seamldr_call(P_SEAMLDR_INSTALL, &args);
>
>I think it should be in a helper alongside with the tdx_module_args.
Will do. Here is the updated version:
diff --git a/arch/x86/virt/vmx/tdx/seamldr.c b/arch/x86/virt/vmx/tdx/seamldr.c
index bb4aa6327eee..cd2aa2eabb65 100644
--- a/arch/x86/virt/vmx/tdx/seamldr.c
+++ b/arch/x86/virt/vmx/tdx/seamldr.c
@@ -19,6 +19,7 @@
/* P-SEAMLDR SEAMCALL leaf function */
#define P_SEAMLDR_INFO 0x8000000000000000
+#define P_SEAMLDR_INSTALL 0x8000000000000001
#define SEAMLDR_MAX_NR_MODULE_4KB_PAGES 496
#define SEAMLDR_MAX_NR_SIG_4KB_PAGES 4
@@ -72,6 +73,13 @@ int seamldr_get_info(struct seamldr_info *seamldr_info)
}
EXPORT_SYMBOL_FOR_MODULES(seamldr_get_info, "tdx-host");
+static int seamldr_install(const struct seamldr_params *params)
+{
+ struct tdx_module_args args = { .rcx = __pa(params) };
+
+ return seamldr_call(P_SEAMLDR_INSTALL, &args);
+}
+
static void free_seamldr_params(struct seamldr_params *params)
{
free_page((unsigned long)params);
@@ -186,6 +194,7 @@ static struct seamldr_params *init_seamldr_params(const u8 *data, u32 size)
enum module_update_state {
MODULE_UPDATE_START,
MODULE_UPDATE_SHUTDOWN,
+ MODULE_UPDATE_CPU_INSTALL,
MODULE_UPDATE_DONE,
};
@@ -243,6 +252,9 @@ static int do_seamldr_install_module(void *seamldr_params)
if (primary)
ret = tdx_module_shutdown();
break;
+ case MODULE_UPDATE_CPU_INSTALL:
+ ret = seamldr_install(seamldr_params);
+ break;
default:
break;
}
^ permalink raw reply related
* Re: [PATCH v5 11/22] x86/virt/seamldr: Shut down the current TDX module
From: Chao Gao @ 2026-03-23 1:33 UTC (permalink / raw)
To: Kiryl Shutsemau
Cc: linux-kernel, linux-coco, kvm, binbin.wu, dan.j.williams,
dave.hansen, ira.weiny, kai.huang, nik.borisov, paulmck, pbonzini,
reinette.chatre, rick.p.edgecombe, sagis, seanjc, tony.lindgren,
vannapurve, vishal.l.verma, yilun.xu, Thomas Gleixner,
Ingo Molnar, Borislav Petkov, x86, H. Peter Anvin
In-Reply-To: <ab0DpDNQ35/xD6hS@intel.com>
On Fri, Mar 20, 2026 at 04:22:10PM +0800, Chao Gao wrote:
>>> +static int get_tdx_sys_info_handoff(struct tdx_sys_info_handoff *sysinfo_handoff)
>>> +{
>>> + int ret = 0;
>>> + u64 val;
>>> +
>>> + if (!ret && !(ret = read_sys_metadata_field(0x8900000100000000, &val)))
>>
>>!ret check is redundant as well as the ret initialization above.
>
>Ok. Will remove them:
>
>static int get_tdx_sys_info_handoff(struct tdx_sys_info_handoff *sysinfo_handoff)
>{
> int ret;
> u64 val;
>
> if (!(ret = read_sys_metadata_field(0x8900000100000000, &val)))
> sysinfo_handoff->module_hv = val;
> return ret;
>}
While making this change, I'll also fix the checkpatch.pl error:
ERROR: do not use assignment in if condition
#23: FILE: arch/x86/virt/vmx/tdx/tdx_global_metadata.c:108:
+ if (!(ret = read_sys_metadata_field(0x8900000100000000, &val)))
Updated version:
static int get_tdx_sys_info_handoff(struct tdx_sys_info_handoff *sysinfo_handoff)
{
int ret;
u64 val;
ret = read_sys_metadata_field(0x8900000100000000, &val);
if (ret)
return ret;
sysinfo_handoff->module_hv = val;
return 0;
}
^ permalink raw reply
* Re: [PATCH v13 12/48] arm64: RMI: Basic infrastructure for creating a realm.
From: Wei-Lin Chang @ 2026-03-21 16:34 UTC (permalink / raw)
To: Steven Price, kvm, kvmarm
Cc: Catalin Marinas, Marc Zyngier, Will Deacon, James Morse,
Oliver Upton, Suzuki K Poulose, Zenghui Yu, linux-arm-kernel,
linux-kernel, Joey Gouly, Alexandru Elisei, Christoffer Dall,
Fuad Tabba, linux-coco, Ganapatrao Kulkarni, Gavin Shan,
Shanker Donthineni, Alper Gun, Aneesh Kumar K . V, Emi Kisanuki,
Vishal Annapurve
In-Reply-To: <20260318155413.793430-13-steven.price@arm.com>
On Wed, Mar 18, 2026 at 03:53:36PM +0000, Steven Price wrote:
> Introduce the skeleton functions for creating and destroying a realm.
> The IPA size requested is checked against what the RMM supports.
>
> The actual work of constructing the realm will be added in future
> patches.
>
> Signed-off-by: Steven Price <steven.price@arm.com>
> ---
> Changes since v12:
> * Drop the RMM_PAGE_{SHIFT,SIZE} defines - the RMM is now configured to
> be the same as the host's page size.
> * Rework delegate/undelegate functions to use the new RMI range based
> operations.
> Changes since v11:
> * Major rework to drop the realm configuration and make the
> construction of realms implicit rather than driven by the VMM
> directly.
> * The code to create RDs, handle VMIDs etc is moved to later patches.
> Changes since v10:
> * Rename from RME to RMI.
> * Move the stage2 cleanup to a later patch.
> Changes since v9:
> * Avoid walking the stage 2 page tables when destroying the realm -
> the real ones are not accessible to the non-secure world, and the RMM
> may leave junk in the physical pages when returning them.
> * Fix an error path in realm_create_rd() to actually return an error value.
> Changes since v8:
> * Fix free_delegated_granule() to not call kvm_account_pgtable_pages();
> a separate wrapper will be introduced in a later patch to deal with
> RTTs.
> * Minor code cleanups following review.
> Changes since v7:
> * Minor code cleanup following Gavin's review.
> Changes since v6:
> * Separate RMM RTT calculations from host PAGE_SIZE. This allows the
> host page size to be larger than 4k while still communicating with an
> RMM which uses 4k granules.
> Changes since v5:
> * Introduce free_delegated_granule() to replace many
> undelegate/free_page() instances and centralise the comment on
> leaking when the undelegate fails.
> * Several other minor improvements suggested by reviews - thanks for
> the feedback!
> Changes since v2:
> * Improved commit description.
> * Improved return failures for rmi_check_version().
> * Clear contents of PGD after it has been undelegated in case the RMM
> left stale data.
> * Minor changes to reflect changes in previous patches.
> ---
> arch/arm64/include/asm/kvm_emulate.h | 5 ++
> arch/arm64/include/asm/kvm_rmi.h | 16 +++++
> arch/arm64/kvm/arm.c | 12 ++++
> arch/arm64/kvm/mmu.c | 11 +++-
> arch/arm64/kvm/rmi.c | 88 ++++++++++++++++++++++++++++
> 5 files changed, 129 insertions(+), 3 deletions(-)
>
> diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h
> index f38b50151ce8..39310d9b4e16 100644
> --- a/arch/arm64/include/asm/kvm_emulate.h
> +++ b/arch/arm64/include/asm/kvm_emulate.h
> @@ -701,6 +701,11 @@ static inline enum realm_state kvm_realm_state(struct kvm *kvm)
> return READ_ONCE(kvm->arch.realm.state);
> }
>
> +static inline bool kvm_realm_is_created(struct kvm *kvm)
> +{
> + return kvm_is_realm(kvm) && kvm_realm_state(kvm) != REALM_STATE_NONE;
> +}
> +
> static inline bool vcpu_is_rec(struct kvm_vcpu *vcpu)
> {
> return false;
> diff --git a/arch/arm64/include/asm/kvm_rmi.h b/arch/arm64/include/asm/kvm_rmi.h
> index 3506f50b05cd..0ada525af18f 100644
> --- a/arch/arm64/include/asm/kvm_rmi.h
> +++ b/arch/arm64/include/asm/kvm_rmi.h
> @@ -6,6 +6,8 @@
> #ifndef __ASM_KVM_RMI_H
> #define __ASM_KVM_RMI_H
>
> +#include <asm/rmi_smc.h>
> +
> /**
> * enum realm_state - State of a Realm
> */
> @@ -46,11 +48,25 @@ enum realm_state {
> * struct realm - Additional per VM data for a Realm
> *
> * @state: The lifetime state machine for the realm
> + * @rd: Kernel mapping of the Realm Descriptor (RD)
> + * @params: Parameters for the RMI_REALM_CREATE command
> + * @num_aux: The number of auxiliary pages required by the RMM
> + * @ia_bits: Number of valid Input Address bits in the IPA
> */
> struct realm {
> enum realm_state state;
> +
> + void *rd;
> + struct realm_params *params;
> +
> + unsigned long num_aux;
> + unsigned int ia_bits;
> };
>
> void kvm_init_rmi(void);
> +u32 kvm_realm_ipa_limit(void);
> +
> +int kvm_init_realm_vm(struct kvm *kvm);
> +void kvm_destroy_realm(struct kvm *kvm);
>
> #endif /* __ASM_KVM_RMI_H */
> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> index 274d7866efdc..9b17bdfaf0c2 100644
> --- a/arch/arm64/kvm/arm.c
> +++ b/arch/arm64/kvm/arm.c
> @@ -253,6 +253,13 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
>
> bitmap_zero(kvm->arch.vcpu_features, KVM_VCPU_MAX_FEATURES);
>
> + /* Initialise the realm bits after the generic bits are enabled */
> + if (kvm_is_realm(kvm)) {
> + ret = kvm_init_realm_vm(kvm);
> + if (ret)
> + goto err_free_cpumask;
> + }
> +
> return 0;
>
> err_free_cpumask:
> @@ -312,6 +319,8 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
> kvm_unshare_hyp(kvm, kvm + 1);
>
> kvm_arm_teardown_hypercalls(kvm);
> + if (kvm_is_realm(kvm))
> + kvm_destroy_realm(kvm);
> }
>
> static bool kvm_has_full_ptr_auth(void)
> @@ -473,6 +482,9 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
> else
> r = kvm_supports_cacheable_pfnmap();
> break;
> + case KVM_CAP_ARM_RMI:
> + r = static_key_enabled(&kvm_rmi_is_available);
> + break;
>
> default:
> r = 0;
> diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
> index 070a01e53fcb..d6094b60c4ce 100644
> --- a/arch/arm64/kvm/mmu.c
> +++ b/arch/arm64/kvm/mmu.c
> @@ -872,12 +872,16 @@ static struct kvm_pgtable_mm_ops kvm_s2_mm_ops = {
> .icache_inval_pou = invalidate_icache_guest_page,
> };
>
> -static int kvm_init_ipa_range(struct kvm_s2_mmu *mmu, unsigned long type)
> +static int kvm_init_ipa_range(struct kvm *kvm,
> + struct kvm_s2_mmu *mmu, unsigned long type)
> {
> u32 kvm_ipa_limit = get_kvm_ipa_limit();
> u64 mmfr0, mmfr1;
> u32 phys_shift;
>
> + if (kvm_is_realm(kvm))
> + kvm_ipa_limit = kvm_realm_ipa_limit();
> +
> if (type & ~KVM_VM_TYPE_ARM_IPA_SIZE_MASK)
> return -EINVAL;
>
> @@ -974,7 +978,7 @@ int kvm_init_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu, unsigned long t
> return -EINVAL;
> }
>
> - err = kvm_init_ipa_range(mmu, type);
> + err = kvm_init_ipa_range(kvm, mmu, type);
> if (err)
> return err;
>
> @@ -1113,7 +1117,8 @@ void kvm_free_stage2_pgd(struct kvm_s2_mmu *mmu)
> write_unlock(&kvm->mmu_lock);
>
> if (pgt) {
> - kvm_stage2_destroy(pgt);
> + if (!kvm_is_realm(kvm))
> + kvm_stage2_destroy(pgt);
Hi,
Question:
Since kvm_stage2_destroy() is only called for non-realm VMs, then where
does the root level RTT pages get freed?
After searching for a while I feel like it is missed, but I am not
certain.
Thanks,
Wei-Lin Chang
> kfree(pgt);
> }
> }
> diff --git a/arch/arm64/kvm/rmi.c b/arch/arm64/kvm/rmi.c
> index 80aedc85e94a..700b8c935d29 100644
> --- a/arch/arm64/kvm/rmi.c
> +++ b/arch/arm64/kvm/rmi.c
> @@ -6,6 +6,8 @@
> #include <linux/kvm_host.h>
> #include <linux/memblock.h>
>
> +#include <asm/kvm_emulate.h>
> +#include <asm/kvm_mmu.h>
> #include <asm/kvm_pgtable.h>
> #include <asm/rmi_cmds.h>
> #include <asm/virt.h>
> @@ -182,6 +184,92 @@ static int rmi_init_metadata(void)
> return 0;
> }
>
> +u32 kvm_realm_ipa_limit(void)
> +{
> + return u64_get_bits(rmm_feat_reg0, RMI_FEATURE_REGISTER_0_S2SZ);
> +}
> +
> +static int undelegate_range(phys_addr_t phys, unsigned long size)
> +{
> + unsigned long ret;
> + unsigned long top = phys + size;
> + unsigned long out_top;
> +
> + while (phys < top) {
> + ret = rmi_granule_range_undelegate(phys, top, &out_top);
> + if (ret == RMI_SUCCESS)
> + phys = out_top;
> + else if (ret != RMI_BUSY && ret != RMI_BLOCKED)
> + return ret;
> + }
> +
> + return ret;
> +}
> +
> +static int undelegate_page(phys_addr_t phys)
> +{
> + return undelegate_range(phys, PAGE_SIZE);
> +}
> +
> +static int free_delegated_page(phys_addr_t phys)
> +{
> + if (WARN_ON(undelegate_page(phys))) {
> + /* Undelegate failed: leak the page */
> + return -EBUSY;
> + }
> +
> + free_page((unsigned long)phys_to_virt(phys));
> +
> + return 0;
> +}
> +
> +void kvm_destroy_realm(struct kvm *kvm)
> +{
> + struct realm *realm = &kvm->arch.realm;
> + size_t pgd_size = kvm_pgtable_stage2_pgd_size(kvm->arch.mmu.vtcr);
> +
> + write_lock(&kvm->mmu_lock);
> + kvm_stage2_unmap_range(&kvm->arch.mmu, 0,
> + BIT(realm->ia_bits - 1), true);
> + write_unlock(&kvm->mmu_lock);
> +
> + if (realm->params) {
> + free_page((unsigned long)realm->params);
> + realm->params = NULL;
> + }
> +
> + if (!kvm_realm_is_created(kvm))
> + return;
> +
> + WRITE_ONCE(realm->state, REALM_STATE_DYING);
> +
> + if (realm->rd) {
> + phys_addr_t rd_phys = virt_to_phys(realm->rd);
> +
> + if (WARN_ON(rmi_realm_destroy(rd_phys)))
> + return;
> + free_delegated_page(rd_phys);
> + realm->rd = NULL;
> + }
> +
> + if (WARN_ON(undelegate_range(kvm->arch.mmu.pgd_phys, pgd_size)))
> + return;
> +
> + WRITE_ONCE(realm->state, REALM_STATE_DEAD);
> +
> + /* Now that the Realm is destroyed, free the entry level RTTs */
> + kvm_free_stage2_pgd(&kvm->arch.mmu);
> +}
> +
> +int kvm_init_realm_vm(struct kvm *kvm)
> +{
> + kvm->arch.realm.params = (void *)get_zeroed_page(GFP_KERNEL);
> +
> + if (!kvm->arch.realm.params)
> + return -ENOMEM;
> + return 0;
> +}
> +
> static int rmm_check_features(void)
> {
> if (kvm_lpa2_is_enabled() && !rmi_has_feature(RMI_FEATURE_REGISTER_0_LPA2)) {
> --
> 2.43.0
>
^ permalink raw reply
* Re: [PATCH v13 37/48] arm64: RMI: Prevent Device mappings for Realms
From: Wei-Lin Chang @ 2026-03-21 16:23 UTC (permalink / raw)
To: Steven Price, kvm, kvmarm
Cc: Catalin Marinas, Marc Zyngier, Will Deacon, James Morse,
Oliver Upton, Suzuki K Poulose, Zenghui Yu, linux-arm-kernel,
linux-kernel, Joey Gouly, Alexandru Elisei, Christoffer Dall,
Fuad Tabba, linux-coco, Ganapatrao Kulkarni, Gavin Shan,
Shanker Donthineni, Alper Gun, Aneesh Kumar K . V, Emi Kisanuki,
Vishal Annapurve
In-Reply-To: <a0584809-31b0-4055-a73f-cddd9760cb9b@arm.com>
On Fri, Mar 20, 2026 at 04:45:07PM +0000, Steven Price wrote:
> On 19/03/2026 18:46, Wei-Lin Chang wrote:
> > On Wed, Mar 18, 2026 at 03:54:01PM +0000, Steven Price wrote:
> >> Physical device assignment is not supported by RMM v1.0, so it
> >> doesn't make much sense to allow device mappings within the realm.
> >> Prevent them when the guest is a realm.
> >>
> >> Signed-off-by: Steven Price <steven.price@arm.com>
> >> ---
> >> Changes from v6:
> >> * Fix the check in user_mem_abort() to prevent all pages that are not
> >> guest_memfd() from being mapped into the protected half of the IPA.
> >> Changes from v5:
> >> * Also prevent accesses in user_mem_abort()
> >> ---
> >> arch/arm64/kvm/mmu.c | 13 +++++++++++++
> >> 1 file changed, 13 insertions(+)
> >>
> >> diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
> >> index ad1300f366df..7d7caab8f573 100644
> >> --- a/arch/arm64/kvm/mmu.c
> >> +++ b/arch/arm64/kvm/mmu.c
> >> @@ -1222,6 +1222,10 @@ int kvm_phys_addr_ioremap(struct kvm *kvm, phys_addr_t guest_ipa,
> >> if (is_protected_kvm_enabled())
> >> return -EPERM;
> >>
> >> + /* We don't support mapping special pages into a Realm */
> >> + if (kvm_is_realm(kvm))
> >> + return -EPERM;
> >> +
> >> size += offset_in_page(guest_ipa);
> >> guest_ipa &= PAGE_MASK;
> >>
> >> @@ -1965,6 +1969,15 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
> >> return 1;
> >> }
> >>
> >> + /*
> >> + * For now we shouldn't be hitting protected addresses because they are
> >> + * handled in private_memslot_fault(). In the future this check may be
> >
> > Hi,
> >
> > What is private_memslot_fault()? I don't see it anywhere in the series &
> > upstream.
>
> Oh dear, that comment is out of date ;) It's now become gmem_abort()...
Ah no wonder!
Thanks,
Wei-Lin Chang
>
> >> + * relaxed to support e.g. protected devices.
> >> + */
> >> + if (vcpu_is_rec(vcpu) &&
> >> + kvm_gpa_from_fault(kvm, fault_ipa) == fault_ipa)
> >> + return -EINVAL;
> >> +
> >
> > Additionally, there is a hunk almost identical to this one here in added
> > in patch 27.
>
> Which is what this chunk says. It appears I screwed up a rebase at some
> point! This whole patch can really be dropped and the
> kvm_phys_addr_ioremap() change moved into another patch.
>
> Thanks,
> Steve
>
> > Thanks,
> > Wei-Lin Chang
> >
> >> if (nested)
> >> adjust_nested_fault_perms(nested, &prot, &writable);
> >>
> >> --
> >> 2.43.0
> >>
>
^ permalink raw reply
* Re: [PATCH v13 26/48] arm64: RMI: Create the realm descriptor
From: Wei-Lin Chang @ 2026-03-21 16:20 UTC (permalink / raw)
To: Steven Price, kvm, kvmarm
Cc: Catalin Marinas, Marc Zyngier, Will Deacon, James Morse,
Oliver Upton, Suzuki K Poulose, Zenghui Yu, linux-arm-kernel,
linux-kernel, Joey Gouly, Alexandru Elisei, Christoffer Dall,
Fuad Tabba, linux-coco, Ganapatrao Kulkarni, Gavin Shan,
Shanker Donthineni, Alper Gun, Aneesh Kumar K . V, Emi Kisanuki,
Vishal Annapurve
In-Reply-To: <a8ae70c5-b225-4c35-bf37-c5b1c91c473e@arm.com>
On Fri, Mar 20, 2026 at 04:41:12PM +0000, Steven Price wrote:
> On 19/03/2026 18:25, Wei-Lin Chang wrote:
> > On Wed, Mar 18, 2026 at 03:53:50PM +0000, Steven Price wrote:
> >> Creating a realm involves first creating a realm descriptor (RD). This
> >> involves passing the configuration information to the RMM. Do this as
> >> part of realm_ensure_created() so that the realm is created when it is
> >> first needed.
> >>
> >> Signed-off-by: Steven Price <steven.price@arm.com>
> >> ---
> >> Changes since v12:
> >> * Since RMM page size is now equal to the host's page size various
> >> calculations are simplified.
> >> * Switch to using range based APIs to delegate/undelegate.
> >> * VMID handling is now handled entirely by the RMM.
> >> ---
> >> arch/arm64/kvm/rmi.c | 94 +++++++++++++++++++++++++++++++++++++++++++-
> >> 1 file changed, 92 insertions(+), 2 deletions(-)
> >>
> >> diff --git a/arch/arm64/kvm/rmi.c b/arch/arm64/kvm/rmi.c
> >> index 38349c7b34f4..d5fee203824b 100644
> >> --- a/arch/arm64/kvm/rmi.c
> >> +++ b/arch/arm64/kvm/rmi.c
> >> @@ -649,6 +649,83 @@ static void realm_unmap_shared_range(struct kvm *kvm,
> >> start, end);
> >> }
> >>
> >> +static int realm_create_rd(struct kvm *kvm)
> >> +{
> >> + struct realm *realm = &kvm->arch.realm;
> >> + struct realm_params *params = realm->params;
> >> + void *rd = NULL;
> >> + phys_addr_t rd_phys, params_phys;
> >> + size_t pgd_size = kvm_pgtable_stage2_pgd_size(kvm->arch.mmu.vtcr);
> >> + int i, r;
> >> +
> >> + realm->ia_bits = VTCR_EL2_IPA(kvm->arch.mmu.vtcr);
> >> +
> >> + if (WARN_ON(realm->rd || !realm->params))
> >> + return -EEXIST;
> >> +
> >> + rd = (void *)__get_free_page(GFP_KERNEL);
> >
> > Hi,
> >
> > Should this be GFP_KERNEL_ACCOUNT?
>
> Yes that would be better.
>
> >> + if (!rd)
> >> + return -ENOMEM;
> >> +
> >> + rd_phys = virt_to_phys(rd);
> >> + if (delegate_page(rd_phys)) {
> >> + r = -ENXIO;
> >> + goto free_rd;
> >> + }
> >> +
> >> + if (delegate_range(kvm->arch.mmu.pgd_phys, pgd_size)) {
> >> + r = -ENXIO;
> >> + goto out_undelegate_tables;
> >> + }
> >> +
> >> + params->s2sz = VTCR_EL2_IPA(kvm->arch.mmu.vtcr);
> >> + params->rtt_level_start = get_start_level(realm);
> >> + params->rtt_num_start = pgd_size / PAGE_SIZE;
> >> + params->rtt_base = kvm->arch.mmu.pgd_phys;
> >> +
> >> + if (kvm->arch.arm_pmu) {
> >> + params->pmu_num_ctrs = kvm->arch.nr_pmu_counters;
> >> + params->flags |= RMI_REALM_PARAM_FLAG_PMU;
> >> + }
> >> +
> >> + if (kvm_lpa2_is_enabled())
> >> + params->flags |= RMI_REALM_PARAM_FLAG_LPA2;
> >> +
> >> + params_phys = virt_to_phys(params);
> >> +
> >> + if (rmi_realm_create(rd_phys, params_phys)) {
> >> + r = -ENXIO;
> >> + goto out_undelegate_tables;
> >> + }
> >> +
> >> + if (WARN_ON(rmi_rec_aux_count(rd_phys, &realm->num_aux))) {
> >> + WARN_ON(rmi_realm_destroy(rd_phys));
> >> + r = -ENXIO;
> >> + goto out_undelegate_tables;
> >> + }
> >> +
> >> + realm->rd = rd;
> >> + WRITE_ONCE(realm->state, REALM_STATE_NEW);
> >> + /* The realm is up, free the parameters. */
> >> + free_page((unsigned long)realm->params);
> >> + realm->params = NULL;
> >> +
> >> + return 0;
> >> +
> >> +out_undelegate_tables:
> >> + if (WARN_ON(undelegate_range(kvm->arch.mmu.pgd_phys, i))) {
> >> + /* Leak the pages if they cannot be returned */
> >> + kvm->arch.mmu.pgt = NULL;
> >
> > Did you mean kvm->arch.mmu.pgd_phys = NULL; ?
>
> No, although I agree this isn't exactly ideal. kvm_free_stage2_pgd()
> uses mmu->pgt to decide whether to free the memory - pgd_phys isn't used
> in that path. Technically here we end up leaking more than just the PGD
> pages in this case, but as it's a "should never happen" case I didn't
> see the need to worry about the leak being a bit larger than necessary.
Thank you for the clarification.
Thanks,
Wei-Lin Chang
>
> Thanks,
> Steve
>
> > Thanks,
> > Wei-Lin Chang
> >
> >> + }
> >> + if (WARN_ON(undelegate_page(rd_phys))) {
> >> + /* Leak the page if it isn't returned */
> >> + return r;
> >> + }
> >> +free_rd:
> >> + free_page((unsigned long)rd);
> >> + return r;
> >> +}
> >> +
> >> static void realm_unmap_private_range(struct kvm *kvm,
> >> unsigned long start,
> >> unsigned long end,
> >> @@ -893,8 +970,21 @@ static int realm_init_ipa_state(struct kvm *kvm,
> >>
> >> static int realm_ensure_created(struct kvm *kvm)
> >> {
> >> - /* Provided in later patch */
> >> - return -ENXIO;
> >> + int ret;
> >> +
> >> + switch (kvm_realm_state(kvm)) {
> >> + case REALM_STATE_NONE:
> >> + break;
> >> + case REALM_STATE_NEW:
> >> + return 0;
> >> + case REALM_STATE_DEAD:
> >> + return -ENXIO;
> >> + default:
> >> + return -EBUSY;
> >> + }
> >> +
> >> + ret = realm_create_rd(kvm);
> >> + return ret;
> >> }
> >>
> >> static int set_ripas_of_protected_regions(struct kvm *kvm)
> >> --
> >> 2.43.0
> >>
>
^ permalink raw reply
* Re: [PATCH v13 05/48] arm64: RMI: Temporarily add SMCs from RMM v1.0 spec
From: Marc Zyngier @ 2026-03-21 13:21 UTC (permalink / raw)
To: Steven Price
Cc: kvm, kvmarm, Catalin Marinas, Will Deacon, James Morse,
Oliver Upton, Suzuki K Poulose, Zenghui Yu, linux-arm-kernel,
linux-kernel, Joey Gouly, Alexandru Elisei, Christoffer Dall,
Fuad Tabba, linux-coco, Ganapatrao Kulkarni, Gavin Shan,
Shanker Donthineni, Alper Gun, Aneesh Kumar K . V, Emi Kisanuki,
Vishal Annapurve
In-Reply-To: <20260318155413.793430-6-steven.price@arm.com>
On Wed, 18 Mar 2026 15:53:29 +0000,
Steven Price <steven.price@arm.com> wrote:
>
> Not all the functionality has been migrated to the v2.0 specification,
> so for now we still rely on some v1.0 SMCs. This mixture is not
> spec-compliant, but is necessary until an updated RMM is available.
>
This sort of things really makes it awkward to review the series.
Do you really expect the reviewers to mentally triage what is current
and what is only throwaway code? I want to see patches that are merge
candidates, not patches that are only there to cope with the broken
state of the RMM.
If extra hacks are required to work with the current Franken-RMM, keep
them as a separate series that doesn't pollute what is targeted at
upstream.
It also means that any testing you have done will be invalidated when
the one true RMM shows up...
"This is great."
M.
--
Jazz isn't dead. It just smells funny.
^ permalink raw reply
* Re: [PATCH v13 15/48] arm64: RMI: RTT tear down
From: Wei-Lin Chang @ 2026-03-21 13:04 UTC (permalink / raw)
To: Steven Price, kvm, kvmarm
Cc: Catalin Marinas, Marc Zyngier, Will Deacon, James Morse,
Oliver Upton, Suzuki K Poulose, Zenghui Yu, linux-arm-kernel,
linux-kernel, Joey Gouly, Alexandru Elisei, Christoffer Dall,
Fuad Tabba, linux-coco, Ganapatrao Kulkarni, Gavin Shan,
Shanker Donthineni, Alper Gun, Aneesh Kumar K . V, Emi Kisanuki,
Vishal Annapurve
In-Reply-To: <ef49030f-1ee7-4473-acc7-c8900acb977d@arm.com>
On Fri, Mar 20, 2026 at 04:12:48PM +0000, Steven Price wrote:
> On 19/03/2026 17:35, Wei-Lin Chang wrote:
> > On Wed, Mar 18, 2026 at 03:53:39PM +0000, Steven Price wrote:
> >> The RMM owns the stage 2 page tables for a realm, and KVM must request
> >> that the RMM creates/destroys entries as necessary. The physical pages
> >> to store the page tables are delegated to the realm as required, and can
> >> be undelegated when no longer used.
> >>
> >> Creating new RTTs is the easy part, tearing down is a little more
> >> tricky. The result of realm_rtt_destroy() can be used to effectively
> >> walk the tree and destroy the entries (undelegating pages that were
> >> given to the realm).
> >>
> >> Signed-off-by: Steven Price <steven.price@arm.com>
> >> ---
> >> Changes since v12:
> >> * Simplify some functions now we know RMM page size is the same as the
> >> host's.
> >> Changes since v11:
> >> * Moved some code from earlier in the series to this one so that it's
> >> added when it's first used.
> >> Changes since v10:
> >> * RME->RMI rename.
> >> * Some code to handle freeing stage 2 PGD moved into this patch where
> >> it belongs.
> >> Changes since v9:
> >> * Add a comment clarifying that root level RTTs are not destroyed until
> >> after the RD is destroyed.
> >> Changes since v8:
> >> * Introduce free_rtt() wrapper which calls free_delegated_granule()
> >> followed by kvm_account_pgtable_pages(). This makes it clear where an
> >> RTT is being freed rather than just a delegated granule.
> >> Changes since v6:
> >> * Move rme_rtt_level_mapsize() and supporting defines from kvm_rme.h
> >> into rme.c as they are only used in that file.
> >> Changes since v5:
> >> * Rename some RME_xxx defines to do with page sizes as RMM_xxx - they are
> >> a property of the RMM specification not the RME architecture.
> >> Changes since v2:
> >> * Moved {alloc,free}_delegated_page() and ensure_spare_page() to a
> >> later patch when they are actually used.
> >> * Some simplifications now rmi_xxx() functions allow NULL as an output
> >> parameter.
> >> * Improved comments and code layout.
> >> ---
> >> arch/arm64/include/asm/kvm_rmi.h | 7 ++
> >> arch/arm64/kvm/mmu.c | 15 +++-
> >> arch/arm64/kvm/rmi.c | 145 +++++++++++++++++++++++++++++++
> >> 3 files changed, 166 insertions(+), 1 deletion(-)
> >>
> >> diff --git a/arch/arm64/include/asm/kvm_rmi.h b/arch/arm64/include/asm/kvm_rmi.h
> >> index 0ada525af18f..16a297f3091a 100644
> >> --- a/arch/arm64/include/asm/kvm_rmi.h
> >> +++ b/arch/arm64/include/asm/kvm_rmi.h
> >> @@ -68,5 +68,12 @@ u32 kvm_realm_ipa_limit(void);
> >>
> >> int kvm_init_realm_vm(struct kvm *kvm);
> >> void kvm_destroy_realm(struct kvm *kvm);
> >> +void kvm_realm_destroy_rtts(struct kvm *kvm);
> >> +
> >> +static inline bool kvm_realm_is_private_address(struct realm *realm,
> >> + unsigned long addr)
> >> +{
> >> + return !(addr & BIT(realm->ia_bits - 1));
> >> +}
> >>
> >> #endif /* __ASM_KVM_RMI_H */
> >> diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
> >> index 9dc242c3b9c8..41152abf55b2 100644
> >> --- a/arch/arm64/kvm/mmu.c
> >> +++ b/arch/arm64/kvm/mmu.c
> >> @@ -1098,10 +1098,23 @@ void stage2_unmap_vm(struct kvm *kvm)
> >> void kvm_free_stage2_pgd(struct kvm_s2_mmu *mmu)
> >> {
> >> struct kvm *kvm = kvm_s2_mmu_to_kvm(mmu);
> >> - struct kvm_pgtable *pgt = NULL;
> >> + struct kvm_pgtable *pgt;
> >>
> >> write_lock(&kvm->mmu_lock);
> >> pgt = mmu->pgt;
> >> + if (kvm_is_realm(kvm) &&
> >> + (kvm_realm_state(kvm) != REALM_STATE_DEAD &&
> >> + kvm_realm_state(kvm) != REALM_STATE_NONE)) {
> >> + write_unlock(&kvm->mmu_lock);
> >> + kvm_realm_destroy_rtts(kvm);
> >> +
> >> + /*
> >> + * The PGD pages can be reclaimed only after the realm (RD) is
> >> + * destroyed. We call this again from kvm_destroy_realm() after
> >> + * the RD is destroyed.
> >> + */
> >> + return;
> >> + }
> >
> > Hi,
> >
> > I see that kvm_free_stage2_pgd() will be called twice:
> >
> > kvm_destroy_vm()
> > mmu_notifier_unregister()
> > kvm_mmu_notifier_release()
> > kvm_flush_shadow_all()
> > kvm_arch_flush_shadow_all()
> > kvm_uninit_stage2_mmu()
> > kvm_free_stage2_pgd()
> > kvm_arch_destroy_vm()
> > kvm_destroy_realm()
> > kvm_free_stage2_pgd()
> >
> > At the first call the realm state is REALM_STATE_ACTIVE, at the second
> > it is REALM_STATE_DEAD. Reading the comment added to
> > kvm_free_stage2_pgd() here, does it mean this function is called twice
> > on purpose? If so do you think it's better to extract this and create
> > another function instead, then use kvm_is_realm() to choose which to
> > run? I think it is confusing to have this function run twice for a
> > realm.
>
> So the issue here is that the RMM requires we do things in a different
> order to a normal VM. For a realm the PGD cannot be destroyed until the
> realm itself is destroyed - the RMM revent the host undelegating them.
>
> So the first call cannot actually do the free - this is the
> REALM_STATE_ACTIVE case.
>
> In kvm_destroy_realm() we tear down the actual realm and undelegate the
> granules. We then need to actually free the PGD - the "obvious" way of
> doing that is calling kvm_free_stage2_pgd() as that handles the KVM
> intricacies - e.g. updating the mmu object.
>
> I'm not sure how to structure the code better without causing
> duplication - I don't want another copy of the cleanup from
> kvm_free_stage2_pgd() in a CCA specific file because it will most likely
> get out of sync with the normal VM case. There is a comment added
> explaining "we call this again" which I hoped would make it less confusing.
>
Oh, I see, thanks for letting me know!
During this I found in the first call of kvm_free_stage2_pgd() it's doing
kvm_stage2_unmap_range() and kvm_realm_destroy_rtts(), but they are also
called in kvm_destroy_realm(), is that intentional?
If they can be called at kvm_destroy_realm() time, could we just not do
kvm_free_stage2_pgd() in kvm_uninit_stage2_mmu() for realms?
And if they should be called in kvm_free_stage2_pgd(), could we refactor
it to something like:
(just showing the idea, didn't try compiling or anything)
diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
index 7d7caab8f573..280d2bef8492 100644
--- a/arch/arm64/kvm/mmu.c
+++ b/arch/arm64/kvm/mmu.c
@@ -1030,9 +1030,25 @@ int kvm_init_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu, unsigned long t
return err;
}
+static void kvm_realm_uninit_stage2(struct kvm_s2_mmu *mmu)
+{
+ struct kvm *kvm = kvm_s2_mmu_to_kvm(mmu);
+ struct realm *realm = &kvm->arch.realm;
+
+ WARN_ON(kvm_realm_state(kvm) != REALM_STATE_ACTIVE);
+ write_lock(&kvm->mmu_lock);
+ kvm_stage2_unmap_range(mmu, 0, BIT(realm->ia_bits - 1), true);
+ write_unlock(&kvm->mmu_lock);
+ kvm_realm_destroy_rtts(kvm);
+}
+
void kvm_uninit_stage2_mmu(struct kvm *kvm)
{
- kvm_free_stage2_pgd(&kvm->arch.mmu);
+ if (kvm_is_realm(kvm))
+ kvm_realm_uninit_stage2(&kvm->arch.mmu);
+ else
+ kvm_free_stage2_pgd(&kvm->arch.mmu);
+
kvm_mmu_free_memory_cache(&kvm->arch.mmu.split_page_cache);
}
@@ -1117,22 +1133,7 @@ void kvm_free_stage2_pgd(struct kvm_s2_mmu *mmu)
write_lock(&kvm->mmu_lock);
pgt = mmu->pgt;
- if (kvm_is_realm(kvm) &&
- (kvm_realm_state(kvm) != REALM_STATE_DEAD &&
- kvm_realm_state(kvm) != REALM_STATE_NONE)) {
- struct realm *realm = &kvm->arch.realm;
-
- kvm_stage2_unmap_range(mmu, 0, BIT(realm->ia_bits - 1), true);
- write_unlock(&kvm->mmu_lock);
- kvm_realm_destroy_rtts(kvm);
- /*
- * The PGD pages can be reclaimed only after the realm (RD) is
- * destroyed. We call this again from kvm_destroy_realm() after
- * the RD is destroyed.
- */
- return;
- }
if (pgt) {
mmu->pgd_phys = 0;
mmu->pgt = NULL;
Sorry if I missed anything!
Thanks,
Wei-Lin Chang
> Thanks,
> Steve
>
> > Thanks,
> > Wei-Lin Chang
> >
> >> if (pgt) {
> >> mmu->pgd_phys = 0;
> >> mmu->pgt = NULL;
> >> diff --git a/arch/arm64/kvm/rmi.c b/arch/arm64/kvm/rmi.c
> >> index 700b8c935d29..1fd2c18f7381 100644
> >> --- a/arch/arm64/kvm/rmi.c
> >> +++ b/arch/arm64/kvm/rmi.c
> >> @@ -15,6 +15,19 @@
> >> static unsigned long rmm_feat_reg0;
> >> static unsigned long rmm_feat_reg1;
> >>
> >> +#define RMM_RTT_BLOCK_LEVEL 2
> >> +#define RMM_RTT_MAX_LEVEL 3
> >> +
> >> +#define RMM_L2_BLOCK_SIZE PMD_SIZE
> >> +
> >> +static inline unsigned long rmi_rtt_level_mapsize(int level)
> >> +{
> >> + if (WARN_ON(level > RMM_RTT_MAX_LEVEL))
> >> + return PAGE_SIZE;
> >> +
> >> + return (1UL << ARM64_HW_PGTABLE_LEVEL_SHIFT(level));
> >> +}
> >> +
> >> static bool rmi_has_feature(unsigned long feature)
> >> {
> >> return !!u64_get_bits(rmm_feat_reg0, feature);
> >> @@ -189,6 +202,11 @@ u32 kvm_realm_ipa_limit(void)
> >> return u64_get_bits(rmm_feat_reg0, RMI_FEATURE_REGISTER_0_S2SZ);
> >> }
> >>
> >> +static int get_start_level(struct realm *realm)
> >> +{
> >> + return 4 - stage2_pgtable_levels(realm->ia_bits);
> >> +}
> >> +
> >> static int undelegate_range(phys_addr_t phys, unsigned long size)
> >> {
> >> unsigned long ret;
> >> @@ -223,6 +241,131 @@ static int free_delegated_page(phys_addr_t phys)
> >> return 0;
> >> }
> >>
> >> +static void free_rtt(phys_addr_t phys)
> >> +{
> >> + if (free_delegated_page(phys))
> >> + return;
> >> +
> >> + kvm_account_pgtable_pages(phys_to_virt(phys), -1);
> >> +}
> >> +
> >> +static int realm_rtt_destroy(struct realm *realm, unsigned long addr,
> >> + int level, phys_addr_t *rtt_granule,
> >> + unsigned long *next_addr)
> >> +{
> >> + unsigned long out_rtt;
> >> + int ret;
> >> +
> >> + ret = rmi_rtt_destroy(virt_to_phys(realm->rd), addr, level,
> >> + &out_rtt, next_addr);
> >> +
> >> + *rtt_granule = out_rtt;
> >> +
> >> + return ret;
> >> +}
> >> +
> >> +static int realm_tear_down_rtt_level(struct realm *realm, int level,
> >> + unsigned long start, unsigned long end)
> >> +{
> >> + ssize_t map_size;
> >> + unsigned long addr, next_addr;
> >> +
> >> + if (WARN_ON(level > RMM_RTT_MAX_LEVEL))
> >> + return -EINVAL;
> >> +
> >> + map_size = rmi_rtt_level_mapsize(level - 1);
> >> +
> >> + for (addr = start; addr < end; addr = next_addr) {
> >> + phys_addr_t rtt_granule;
> >> + int ret;
> >> + unsigned long align_addr = ALIGN(addr, map_size);
> >> +
> >> + next_addr = ALIGN(addr + 1, map_size);
> >> +
> >> + if (next_addr > end || align_addr != addr) {
> >> + /*
> >> + * The target range is smaller than what this level
> >> + * covers, recurse deeper.
> >> + */
> >> + ret = realm_tear_down_rtt_level(realm,
> >> + level + 1,
> >> + addr,
> >> + min(next_addr, end));
> >> + if (ret)
> >> + return ret;
> >> + continue;
> >> + }
> >> +
> >> + ret = realm_rtt_destroy(realm, addr, level,
> >> + &rtt_granule, &next_addr);
> >> +
> >> + switch (RMI_RETURN_STATUS(ret)) {
> >> + case RMI_SUCCESS:
> >> + free_rtt(rtt_granule);
> >> + break;
> >> + case RMI_ERROR_RTT:
> >> + if (next_addr > addr) {
> >> + /* Missing RTT, skip */
> >> + break;
> >> + }
> >> + /*
> >> + * We tear down the RTT range for the full IPA
> >> + * space, after everything is unmapped. Also we
> >> + * descend down only if we cannot tear down a
> >> + * top level RTT. Thus RMM must be able to walk
> >> + * to the requested level. e.g., a block mapping
> >> + * exists at L1 or L2.
> >> + */
> >> + if (WARN_ON(RMI_RETURN_INDEX(ret) != level))
> >> + return -EBUSY;
> >> + if (WARN_ON(level == RMM_RTT_MAX_LEVEL))
> >> + return -EBUSY;
> >> +
> >> + /*
> >> + * The table has active entries in it, recurse deeper
> >> + * and tear down the RTTs.
> >> + */
> >> + next_addr = ALIGN(addr + 1, map_size);
> >> + ret = realm_tear_down_rtt_level(realm,
> >> + level + 1,
> >> + addr,
> >> + next_addr);
> >> + if (ret)
> >> + return ret;
> >> + /*
> >> + * Now that the child RTTs are destroyed,
> >> + * retry at this level.
> >> + */
> >> + next_addr = addr;
> >> + break;
> >> + default:
> >> + WARN_ON(1);
> >> + return -ENXIO;
> >> + }
> >> + }
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +static int realm_tear_down_rtt_range(struct realm *realm,
> >> + unsigned long start, unsigned long end)
> >> +{
> >> + /*
> >> + * Root level RTTs can only be destroyed after the RD is destroyed. So
> >> + * tear down everything below the root level
> >> + */
> >> + return realm_tear_down_rtt_level(realm, get_start_level(realm) + 1,
> >> + start, end);
> >> +}
> >> +
> >> +void kvm_realm_destroy_rtts(struct kvm *kvm)
> >> +{
> >> + struct realm *realm = &kvm->arch.realm;
> >> + unsigned int ia_bits = realm->ia_bits;
> >> +
> >> + WARN_ON(realm_tear_down_rtt_range(realm, 0, (1UL << ia_bits)));
> >> +}
> >> +
> >> void kvm_destroy_realm(struct kvm *kvm)
> >> {
> >> struct realm *realm = &kvm->arch.realm;
> >> @@ -246,6 +389,8 @@ void kvm_destroy_realm(struct kvm *kvm)
> >> if (realm->rd) {
> >> phys_addr_t rd_phys = virt_to_phys(realm->rd);
> >>
> >> + kvm_realm_destroy_rtts(kvm);
> >> +
> >> if (WARN_ON(rmi_realm_destroy(rd_phys)))
> >> return;
> >> free_delegated_page(rd_phys);
> >> --
> >> 2.43.0
> >>
>
^ permalink raw reply related
* Re: [PATCH v13 00/48] arm64: Support for Arm CCA in KVM
From: Mathieu Poirier @ 2026-03-20 19:15 UTC (permalink / raw)
To: Steven Price
Cc: kvm, kvmarm, Catalin Marinas, Marc Zyngier, Will Deacon,
James Morse, Oliver Upton, Suzuki K Poulose, Zenghui Yu,
linux-arm-kernel, linux-kernel, Joey Gouly, Alexandru Elisei,
Christoffer Dall, Fuad Tabba, linux-coco, Ganapatrao Kulkarni,
Gavin Shan, Shanker Donthineni, Alper Gun, Aneesh Kumar K . V,
Emi Kisanuki, Vishal Annapurve
In-Reply-To: <37bc1222-6fc7-48f0-94d3-6eaac420aa55@arm.com>
On Fri, Mar 20, 2026 at 04:45:49PM +0000, Steven Price wrote:
> On 19/03/2026 23:02, Mathieu Poirier wrote:
> > Good day,
> >
> > On Wed, Mar 18, 2026 at 03:53:24PM +0000, Steven Price wrote:
> >> This series adds support for running protected VMs using KVM under the
> >> Arm Confidential Compute Architecture (CCA).
> >>
> >> New major version number! This now targets RMM v2.0-bet0[1]. And unlike
> >> for Linux this represents a significant change.
> >>
> >> RMM v2.0 brings with it the ability to configure the RMM to have the
> >> same page size as the host (so no more RMM_PAGE_SIZE and dealing with
> >> granules being different from host pages). It also introduces range
> >> based APIs for many operations which should be more efficient and
> >> simplifies the code in places.
> >>
> >> The handling of the GIC has changed, so the system registers are used to
> >> pass the GIC state rather than memory. This means fewer changes to the
> >> KVM code as it looks much like a normal VM in this respect.
> >>
> >> And of course the new uAPI introduced in the previous v12 posting is
> >> retained so that also remains simplified compared to earlier postings.
> >>
> >> The RMM support for v2.0 is still early and so this series includes a
> >> few hacks to ease the integration. Of note are that there are some RMM
> >> v1.0 SMCs added to paper over areas where the RMM implementation isn't
> >> quite ready for v2.0, and "SROs" (see below) are deferred to the final
> >> patch in the series.
> >>
> >> The PMU in RMM v2.0 requires more handling on the RMM-side (and
> >> therefore simplifies the implementation on Linux), but this isn't quite
> >> ready yet. The Linux side is implemented (but untested).
> >>
> >> PSCI still requires the VMM to provide the "target" REC for operations
> >> that affect another vCPU. This is likely to change in a future version
> >> of the specification. There's also a desire to force PSCI to be handled
> >> in the VMM for realm guests - this isn't implemented yet as I'm waiting
> >> for the dust to settle on the RMM interface first.
> >>
> >> Stateful RMI Operations
> >> -----------------------
> >>
> >> The RMM v2.0 spec brings a new concept of Stateful RMI Operations (SROs)
> >> which allow the RMM to complete an operation over several SMC calls and
> >> requesting/returning memory to the host. This has the benefit of
> >> allowing interrupts to be handled in the middle of an operation (by
> >> returning to the host to handle the interrupt without completing the
> >> operation) and enables the RMM to dynamically allocate memory for
> >> internal tracking purposes. One example of this is RMI_REC_CREATE no
> >> longer needs "auxiliary granules" provided upfront but can request the
> >> memory needed during the RMI_REC_CREATE operation.
> >>
> >> There are a fairly large number of operations that are defined as SROs
> >> in the specification, but current both Linux and RMM only have support
> >> for RMI_REC_CREATE and RMI_REC_DESTROY. There a number of TODOs/FIXMEs
> >> in the code where support is missing.
> >>
> >> Given the early stage support for this, the SRO handling is all confined
> >> to the final patch. This patch can be dropped to return to a pre-SRO
> >> state (albeit a mixture of RMM v1.0 and v2.0 APIs) for testing purposes.
> >>
> >> A future posting will reorder the series to move the generic SRO support
> >> to an early patch and will implement the proper support for this in all
> >> RMI SMCs.
> >>
> >> One aspect of SROs which is not yet well captured is that in some
> >> circumstances the Linux kernel will need to call an SRO call in a
> >> context where memory allocation is restricted (e.g. because a spinlock
> >> is held). In this case the intention is that the SRO will be cancelled,
> >> the spinlock dropped so the memory allocation can be completed, and then
> >> the SRO restarted (obviously after rechecking the state that the
> >> spinlock was protecting). For this reason the code stores the memory
> >> allocations within a struct rmi_sro_state object - see the final patch
> >> for more details.
> >>
> >> This series is based on v7.0-rc1. It is also available as a git
> >> repository:
> >>
> >> https://gitlab.arm.com/linux-arm/linux-cca cca-host/v13
> >>
> >> Work in progress changes for kvmtool are available from the git
> >> repository below:
> >>
> >> https://gitlab.arm.com/linux-arm/kvmtool-cca cca/v11
> >>
> >> Note that the kvmtool code has been tidied up (thanks to Suzuki) and
> >> this involves a minor change in flags. The "--restricted_mem" flag is no
> >> longer recognised (or necessary).
> >>
> >> The TF-RMM has not yet merged the RMMv2.0 support, so you will need to
> >> use the following branch:
> >>
> >> https://git.trustedfirmware.org/TF-RMM/tf-rmm.git topics/rmm-v2.0-poc
> >
> > This RMM version is expecting a RMM EL3 interface version of at least 2.0. Do
> > you have a TF-A to use with it?
>
> You should be able to use the 'master' branch of the TF-A repository.
> For now you need to set RMM_V1_COMPAT=0 to enable 2.0 support.
>
That worked - thanks for the clarification.
> Thanks,
> Steve
>
> > Thanks,
> > Mathieu
> >
> >>
> >> [1] https://developer.arm.com/documentation/den0137/2-0bet0/
> >>
> >> Jean-Philippe Brucker (7):
> >> arm64: RMI: Propagate number of breakpoints and watchpoints to
> >> userspace
> >> arm64: RMI: Set breakpoint parameters through SET_ONE_REG
> >> arm64: RMI: Initialize PMCR.N with number counter supported by RMM
> >> arm64: RMI: Propagate max SVE vector length from RMM
> >> arm64: RMI: Configure max SVE vector length for a Realm
> >> arm64: RMI: Provide register list for unfinalized RMI RECs
> >> arm64: RMI: Provide accurate register list
> >>
> >> Joey Gouly (2):
> >> arm64: RMI: allow userspace to inject aborts
> >> arm64: RMI: support RSI_HOST_CALL
> >>
> >> Steven Price (36):
> >> kvm: arm64: Avoid including linux/kvm_host.h in kvm_pgtable.h
> >> arm64: RME: Handle Granule Protection Faults (GPFs)
> >> arm64: RMI: Add SMC definitions for calling the RMM
> >> arm64: RMI: Temporarily add SMCs from RMM v1.0 spec
> >> arm64: RMI: Add wrappers for RMI calls
> >> arm64: RMI: Check for RMI support at KVM init
> >> arm64: RMI: Configure the RMM with the host's page size
> >> arm64: RMI: Check for LPA2 support
> >> arm64: RMI: Ensure that the RMM has GPT entries for memory
> >> arm64: RMI: Define the user ABI
> >> arm64: RMI: Basic infrastructure for creating a realm.
> >> KVM: arm64: Allow passing machine type in KVM creation
> >> arm64: RMI: RTT tear down
> >> arm64: RMI: Activate realm on first VCPU run
> >> arm64: RMI: Allocate/free RECs to match vCPUs
> >> arm64: RMI: Support for the VGIC in realms
> >> KVM: arm64: Support timers in realm RECs
> >> arm64: RMI: Handle realm enter/exit
> >> arm64: RMI: Handle RMI_EXIT_RIPAS_CHANGE
> >> KVM: arm64: Handle realm MMIO emulation
> >> KVM: arm64: Expose support for private memory
> >> arm64: RMI: Allow populating initial contents
> >> arm64: RMI: Set RIPAS of initial memslots
> >> arm64: RMI: Create the realm descriptor
> >> arm64: RMI: Runtime faulting of memory
> >> KVM: arm64: Handle realm VCPU load
> >> KVM: arm64: Validate register access for a Realm VM
> >> KVM: arm64: Handle Realm PSCI requests
> >> KVM: arm64: WARN on injected undef exceptions
> >> arm64: Don't expose stolen time for realm guests
> >> arm64: RMI: Always use 4k pages for realms
> >> arm64: RMI: Prevent Device mappings for Realms
> >> arm64: RMI: Enable PMU support with a realm guest
> >> KVM: arm64: Expose KVM_ARM_VCPU_REC to user space
> >> arm64: RMI: Enable realms to be created
> >> [WIP] arm64: RMI: Add support for SRO
> >>
> >> Suzuki K Poulose (3):
> >> kvm: arm64: Include kvm_emulate.h in kvm/arm_psci.h
> >> kvm: arm64: Don't expose unsupported capabilities for realm guests
> >> arm64: RMI: Allow checking SVE on VM instance
> >>
> >> Documentation/virt/kvm/api.rst | 86 +-
> >> arch/arm64/include/asm/kvm_emulate.h | 31 +
> >> arch/arm64/include/asm/kvm_host.h | 15 +-
> >> arch/arm64/include/asm/kvm_pgtable.h | 5 +-
> >> arch/arm64/include/asm/kvm_pkvm.h | 2 +-
> >> arch/arm64/include/asm/kvm_rmi.h | 129 ++
> >> arch/arm64/include/asm/rmi_cmds.h | 692 +++++++++
> >> arch/arm64/include/asm/rmi_smc.h | 430 ++++++
> >> arch/arm64/include/asm/virt.h | 1 +
> >> arch/arm64/kernel/cpufeature.c | 1 +
> >> arch/arm64/kvm/Kconfig | 2 +
> >> arch/arm64/kvm/Makefile | 2 +-
> >> arch/arm64/kvm/arch_timer.c | 28 +-
> >> arch/arm64/kvm/arm.c | 178 ++-
> >> arch/arm64/kvm/guest.c | 95 +-
> >> arch/arm64/kvm/hyp/pgtable.c | 1 +
> >> arch/arm64/kvm/hypercalls.c | 4 +-
> >> arch/arm64/kvm/inject_fault.c | 5 +-
> >> arch/arm64/kvm/mmio.c | 16 +-
> >> arch/arm64/kvm/mmu.c | 214 ++-
> >> arch/arm64/kvm/pmu-emul.c | 6 +
> >> arch/arm64/kvm/psci.c | 30 +
> >> arch/arm64/kvm/reset.c | 13 +-
> >> arch/arm64/kvm/rmi-exit.c | 207 +++
> >> arch/arm64/kvm/rmi.c | 1948 ++++++++++++++++++++++++++
> >> arch/arm64/kvm/sys_regs.c | 53 +-
> >> arch/arm64/kvm/vgic/vgic-init.c | 2 +-
> >> arch/arm64/mm/fault.c | 28 +-
> >> include/kvm/arm_arch_timer.h | 2 +
> >> include/kvm/arm_pmu.h | 4 +
> >> include/kvm/arm_psci.h | 2 +
> >> include/uapi/linux/kvm.h | 41 +-
> >> 32 files changed, 4176 insertions(+), 97 deletions(-)
> >> create mode 100644 arch/arm64/include/asm/kvm_rmi.h
> >> create mode 100644 arch/arm64/include/asm/rmi_cmds.h
> >> create mode 100644 arch/arm64/include/asm/rmi_smc.h
> >> create mode 100644 arch/arm64/kvm/rmi-exit.c
> >> create mode 100644 arch/arm64/kvm/rmi.c
> >>
> >> --
> >> 2.43.0
> >>
> >>
>
^ permalink raw reply
* Re: [PATCH 1/2] x86/virt/tdx: Use PFN directly for mapping guest private memory
From: Edgecombe, Rick P @ 2026-03-20 17:48 UTC (permalink / raw)
To: Hansen, Dave, kas@kernel.org
Cc: kvm@vger.kernel.org, Li, Xiaoyao, Huang, Kai,
linux-coco@lists.linux.dev, Zhao, Yan Y,
dave.hansen@linux.intel.com, linux-kernel@vger.kernel.org,
seanjc@google.com, binbin.wu@linux.intel.com, pbonzini@redhat.com,
mingo@redhat.com, ackerleytng@google.com, Yamahata, Isaku,
sagis@google.com, Annapurve, Vishal, bp@alien8.de,
tglx@kernel.org, yilun.xu@linux.intel.com, x86@kernel.org
In-Reply-To: <c8c15f0c-6fd4-4a1f-98c3-58467b46afe2@intel.com>
On Fri, 2026-03-20 at 10:38 -0700, Dave Hansen wrote:
> Can we add something that will BUG_ON() or fail to compile when the huge
> page support comes around?
>
> I'd much rather have:
>
> BUG_ON(level != PG_LEVEL_4K);
> tdx_clflush_pfn(pfn);
>
> That go implementing a level argument to tdx_clflush_pfn() now. Then
> nobody has to remember. The list to remember is in the code.
I like it, but I'm afraid to add BUG_ON()s. Could we do a WARN instead?
Especially since... the ridiculous thing about this is that the clflush is only
needed if CLFLUSH_BEFORE_ALLOC is set in the tdx metadata, which we have yet to
see in any modules. The spec is apparently to give TDX module flexibility for
the future.
In the base series we debated just ignoring it, but at the time it was simpler
to just always flush. So if CLFLUSH_BEFORE_ALLOC never comes along, it is
possible the function will never do any useful work in its life. Tough case for
a BUG_ON().
^ permalink raw reply
* Re: [PATCH 1/2] x86/virt/tdx: Use PFN directly for mapping guest private memory
From: Dave Hansen @ 2026-03-20 17:38 UTC (permalink / raw)
To: Edgecombe, Rick P, kas@kernel.org
Cc: kvm@vger.kernel.org, Li, Xiaoyao, Huang, Kai,
linux-coco@lists.linux.dev, Zhao, Yan Y,
dave.hansen@linux.intel.com, linux-kernel@vger.kernel.org,
mingo@redhat.com, seanjc@google.com, pbonzini@redhat.com,
binbin.wu@linux.intel.com, Yamahata, Isaku,
ackerleytng@google.com, Annapurve, Vishal, sagis@google.com,
bp@alien8.de, tglx@kernel.org, yilun.xu@linux.intel.com,
x86@kernel.org
In-Reply-To: <d614658f2d1f3213b0d8ed404c51b3c4bfbe5d7d.camel@intel.com>
On 3/20/26 10:31, Edgecombe, Rick P wrote:
> On Fri, 2026-03-20 at 12:59 +0000, Kiryl Shutsemau wrote:
>>> I feel like we argued about this before. But it would be more correct to
>>> just drop level and make it 4k only until huge pages? Otherwise we are
>>> tweaking dead behavior.
>> I guess. But you add one more thing on the list to remember when adding
>> huge page support. This kind of stuff is easy to miss.
> I guess I'm fine either way. I'm not sure Dave will like adding dead branches
> though. I suppose huge pages is close enough that we are looking to merge prep
> work anyway.
Can we add something that will BUG_ON() or fail to compile when the huge
page support comes around?
I'd much rather have:
BUG_ON(level != PG_LEVEL_4K);
tdx_clflush_pfn(pfn);
That go implementing a level argument to tdx_clflush_pfn() now. Then
nobody has to remember. The list to remember is in the code.
^ permalink raw reply
* Re: [PATCH 1/2] x86/virt/tdx: Use PFN directly for mapping guest private memory
From: Edgecombe, Rick P @ 2026-03-20 17:31 UTC (permalink / raw)
To: kas@kernel.org
Cc: kvm@vger.kernel.org, Li, Xiaoyao, Huang, Kai,
linux-coco@lists.linux.dev, Zhao, Yan Y,
dave.hansen@linux.intel.com, linux-kernel@vger.kernel.org,
mingo@redhat.com, seanjc@google.com, pbonzini@redhat.com,
binbin.wu@linux.intel.com, Yamahata, Isaku,
ackerleytng@google.com, Annapurve, Vishal, sagis@google.com,
bp@alien8.de, tglx@kernel.org, yilun.xu@linux.intel.com,
x86@kernel.org
In-Reply-To: <ab1EcZ3G_Nc-929X@thinkstation>
On Fri, 2026-03-20 at 12:59 +0000, Kiryl Shutsemau wrote:
> > I feel like we argued about this before. But it would be more correct to
> > just drop level and make it 4k only until huge pages? Otherwise we are
> > tweaking dead behavior.
>
> I guess. But you add one more thing on the list to remember when adding
> huge page support. This kind of stuff is easy to miss.
I guess I'm fine either way. I'm not sure Dave will like adding dead branches
though. I suppose huge pages is close enough that we are looking to merge prep
work anyway.
^ permalink raw reply
* Re: [PATCH v13 00/48] arm64: Support for Arm CCA in KVM
From: Steven Price @ 2026-03-20 16:45 UTC (permalink / raw)
To: Mathieu Poirier
Cc: kvm, kvmarm, Catalin Marinas, Marc Zyngier, Will Deacon,
James Morse, Oliver Upton, Suzuki K Poulose, Zenghui Yu,
linux-arm-kernel, linux-kernel, Joey Gouly, Alexandru Elisei,
Christoffer Dall, Fuad Tabba, linux-coco, Ganapatrao Kulkarni,
Gavin Shan, Shanker Donthineni, Alper Gun, Aneesh Kumar K . V,
Emi Kisanuki, Vishal Annapurve
In-Reply-To: <abyAbPIkDn2bGZwd@p14s>
On 19/03/2026 23:02, Mathieu Poirier wrote:
> Good day,
>
> On Wed, Mar 18, 2026 at 03:53:24PM +0000, Steven Price wrote:
>> This series adds support for running protected VMs using KVM under the
>> Arm Confidential Compute Architecture (CCA).
>>
>> New major version number! This now targets RMM v2.0-bet0[1]. And unlike
>> for Linux this represents a significant change.
>>
>> RMM v2.0 brings with it the ability to configure the RMM to have the
>> same page size as the host (so no more RMM_PAGE_SIZE and dealing with
>> granules being different from host pages). It also introduces range
>> based APIs for many operations which should be more efficient and
>> simplifies the code in places.
>>
>> The handling of the GIC has changed, so the system registers are used to
>> pass the GIC state rather than memory. This means fewer changes to the
>> KVM code as it looks much like a normal VM in this respect.
>>
>> And of course the new uAPI introduced in the previous v12 posting is
>> retained so that also remains simplified compared to earlier postings.
>>
>> The RMM support for v2.0 is still early and so this series includes a
>> few hacks to ease the integration. Of note are that there are some RMM
>> v1.0 SMCs added to paper over areas where the RMM implementation isn't
>> quite ready for v2.0, and "SROs" (see below) are deferred to the final
>> patch in the series.
>>
>> The PMU in RMM v2.0 requires more handling on the RMM-side (and
>> therefore simplifies the implementation on Linux), but this isn't quite
>> ready yet. The Linux side is implemented (but untested).
>>
>> PSCI still requires the VMM to provide the "target" REC for operations
>> that affect another vCPU. This is likely to change in a future version
>> of the specification. There's also a desire to force PSCI to be handled
>> in the VMM for realm guests - this isn't implemented yet as I'm waiting
>> for the dust to settle on the RMM interface first.
>>
>> Stateful RMI Operations
>> -----------------------
>>
>> The RMM v2.0 spec brings a new concept of Stateful RMI Operations (SROs)
>> which allow the RMM to complete an operation over several SMC calls and
>> requesting/returning memory to the host. This has the benefit of
>> allowing interrupts to be handled in the middle of an operation (by
>> returning to the host to handle the interrupt without completing the
>> operation) and enables the RMM to dynamically allocate memory for
>> internal tracking purposes. One example of this is RMI_REC_CREATE no
>> longer needs "auxiliary granules" provided upfront but can request the
>> memory needed during the RMI_REC_CREATE operation.
>>
>> There are a fairly large number of operations that are defined as SROs
>> in the specification, but current both Linux and RMM only have support
>> for RMI_REC_CREATE and RMI_REC_DESTROY. There a number of TODOs/FIXMEs
>> in the code where support is missing.
>>
>> Given the early stage support for this, the SRO handling is all confined
>> to the final patch. This patch can be dropped to return to a pre-SRO
>> state (albeit a mixture of RMM v1.0 and v2.0 APIs) for testing purposes.
>>
>> A future posting will reorder the series to move the generic SRO support
>> to an early patch and will implement the proper support for this in all
>> RMI SMCs.
>>
>> One aspect of SROs which is not yet well captured is that in some
>> circumstances the Linux kernel will need to call an SRO call in a
>> context where memory allocation is restricted (e.g. because a spinlock
>> is held). In this case the intention is that the SRO will be cancelled,
>> the spinlock dropped so the memory allocation can be completed, and then
>> the SRO restarted (obviously after rechecking the state that the
>> spinlock was protecting). For this reason the code stores the memory
>> allocations within a struct rmi_sro_state object - see the final patch
>> for more details.
>>
>> This series is based on v7.0-rc1. It is also available as a git
>> repository:
>>
>> https://gitlab.arm.com/linux-arm/linux-cca cca-host/v13
>>
>> Work in progress changes for kvmtool are available from the git
>> repository below:
>>
>> https://gitlab.arm.com/linux-arm/kvmtool-cca cca/v11
>>
>> Note that the kvmtool code has been tidied up (thanks to Suzuki) and
>> this involves a minor change in flags. The "--restricted_mem" flag is no
>> longer recognised (or necessary).
>>
>> The TF-RMM has not yet merged the RMMv2.0 support, so you will need to
>> use the following branch:
>>
>> https://git.trustedfirmware.org/TF-RMM/tf-rmm.git topics/rmm-v2.0-poc
>
> This RMM version is expecting a RMM EL3 interface version of at least 2.0. Do
> you have a TF-A to use with it?
You should be able to use the 'master' branch of the TF-A repository.
For now you need to set RMM_V1_COMPAT=0 to enable 2.0 support.
Thanks,
Steve
> Thanks,
> Mathieu
>
>>
>> [1] https://developer.arm.com/documentation/den0137/2-0bet0/
>>
>> Jean-Philippe Brucker (7):
>> arm64: RMI: Propagate number of breakpoints and watchpoints to
>> userspace
>> arm64: RMI: Set breakpoint parameters through SET_ONE_REG
>> arm64: RMI: Initialize PMCR.N with number counter supported by RMM
>> arm64: RMI: Propagate max SVE vector length from RMM
>> arm64: RMI: Configure max SVE vector length for a Realm
>> arm64: RMI: Provide register list for unfinalized RMI RECs
>> arm64: RMI: Provide accurate register list
>>
>> Joey Gouly (2):
>> arm64: RMI: allow userspace to inject aborts
>> arm64: RMI: support RSI_HOST_CALL
>>
>> Steven Price (36):
>> kvm: arm64: Avoid including linux/kvm_host.h in kvm_pgtable.h
>> arm64: RME: Handle Granule Protection Faults (GPFs)
>> arm64: RMI: Add SMC definitions for calling the RMM
>> arm64: RMI: Temporarily add SMCs from RMM v1.0 spec
>> arm64: RMI: Add wrappers for RMI calls
>> arm64: RMI: Check for RMI support at KVM init
>> arm64: RMI: Configure the RMM with the host's page size
>> arm64: RMI: Check for LPA2 support
>> arm64: RMI: Ensure that the RMM has GPT entries for memory
>> arm64: RMI: Define the user ABI
>> arm64: RMI: Basic infrastructure for creating a realm.
>> KVM: arm64: Allow passing machine type in KVM creation
>> arm64: RMI: RTT tear down
>> arm64: RMI: Activate realm on first VCPU run
>> arm64: RMI: Allocate/free RECs to match vCPUs
>> arm64: RMI: Support for the VGIC in realms
>> KVM: arm64: Support timers in realm RECs
>> arm64: RMI: Handle realm enter/exit
>> arm64: RMI: Handle RMI_EXIT_RIPAS_CHANGE
>> KVM: arm64: Handle realm MMIO emulation
>> KVM: arm64: Expose support for private memory
>> arm64: RMI: Allow populating initial contents
>> arm64: RMI: Set RIPAS of initial memslots
>> arm64: RMI: Create the realm descriptor
>> arm64: RMI: Runtime faulting of memory
>> KVM: arm64: Handle realm VCPU load
>> KVM: arm64: Validate register access for a Realm VM
>> KVM: arm64: Handle Realm PSCI requests
>> KVM: arm64: WARN on injected undef exceptions
>> arm64: Don't expose stolen time for realm guests
>> arm64: RMI: Always use 4k pages for realms
>> arm64: RMI: Prevent Device mappings for Realms
>> arm64: RMI: Enable PMU support with a realm guest
>> KVM: arm64: Expose KVM_ARM_VCPU_REC to user space
>> arm64: RMI: Enable realms to be created
>> [WIP] arm64: RMI: Add support for SRO
>>
>> Suzuki K Poulose (3):
>> kvm: arm64: Include kvm_emulate.h in kvm/arm_psci.h
>> kvm: arm64: Don't expose unsupported capabilities for realm guests
>> arm64: RMI: Allow checking SVE on VM instance
>>
>> Documentation/virt/kvm/api.rst | 86 +-
>> arch/arm64/include/asm/kvm_emulate.h | 31 +
>> arch/arm64/include/asm/kvm_host.h | 15 +-
>> arch/arm64/include/asm/kvm_pgtable.h | 5 +-
>> arch/arm64/include/asm/kvm_pkvm.h | 2 +-
>> arch/arm64/include/asm/kvm_rmi.h | 129 ++
>> arch/arm64/include/asm/rmi_cmds.h | 692 +++++++++
>> arch/arm64/include/asm/rmi_smc.h | 430 ++++++
>> arch/arm64/include/asm/virt.h | 1 +
>> arch/arm64/kernel/cpufeature.c | 1 +
>> arch/arm64/kvm/Kconfig | 2 +
>> arch/arm64/kvm/Makefile | 2 +-
>> arch/arm64/kvm/arch_timer.c | 28 +-
>> arch/arm64/kvm/arm.c | 178 ++-
>> arch/arm64/kvm/guest.c | 95 +-
>> arch/arm64/kvm/hyp/pgtable.c | 1 +
>> arch/arm64/kvm/hypercalls.c | 4 +-
>> arch/arm64/kvm/inject_fault.c | 5 +-
>> arch/arm64/kvm/mmio.c | 16 +-
>> arch/arm64/kvm/mmu.c | 214 ++-
>> arch/arm64/kvm/pmu-emul.c | 6 +
>> arch/arm64/kvm/psci.c | 30 +
>> arch/arm64/kvm/reset.c | 13 +-
>> arch/arm64/kvm/rmi-exit.c | 207 +++
>> arch/arm64/kvm/rmi.c | 1948 ++++++++++++++++++++++++++
>> arch/arm64/kvm/sys_regs.c | 53 +-
>> arch/arm64/kvm/vgic/vgic-init.c | 2 +-
>> arch/arm64/mm/fault.c | 28 +-
>> include/kvm/arm_arch_timer.h | 2 +
>> include/kvm/arm_pmu.h | 4 +
>> include/kvm/arm_psci.h | 2 +
>> include/uapi/linux/kvm.h | 41 +-
>> 32 files changed, 4176 insertions(+), 97 deletions(-)
>> create mode 100644 arch/arm64/include/asm/kvm_rmi.h
>> create mode 100644 arch/arm64/include/asm/rmi_cmds.h
>> create mode 100644 arch/arm64/include/asm/rmi_smc.h
>> create mode 100644 arch/arm64/kvm/rmi-exit.c
>> create mode 100644 arch/arm64/kvm/rmi.c
>>
>> --
>> 2.43.0
>>
>>
^ permalink raw reply
* Re: [PATCH v13 45/48] arm64: RMI: Provide accurate register list
From: Steven Price @ 2026-03-20 16:45 UTC (permalink / raw)
To: Wei-Lin Chang, kvm, kvmarm
Cc: Jean-Philippe Brucker, Catalin Marinas, Marc Zyngier, Will Deacon,
James Morse, Oliver Upton, Suzuki K Poulose, Zenghui Yu,
linux-arm-kernel, linux-kernel, Joey Gouly, Alexandru Elisei,
Christoffer Dall, Fuad Tabba, linux-coco, Ganapatrao Kulkarni,
Gavin Shan, Shanker Donthineni, Alper Gun, Aneesh Kumar K . V,
Emi Kisanuki, Vishal Annapurve
In-Reply-To: <iiyaphsgpo2f3wrrywggsten6c6lb4gxhiztah6jurtt25xepf@gpzdhw77oa4a>
On 19/03/2026 18:53, Wei-Lin Chang wrote:
> On Wed, Mar 18, 2026 at 03:54:09PM +0000, Steven Price wrote:
>> From: Jean-Philippe Brucker <jean-philippe@linaro.org>
>>
>> Userspace can set a few registers with KVM_SET_ONE_REG (9 GP registers
>> at runtime, and 3 system registers during initialization). Update the
>> register list returned by KVM_GET_REG_LIST.
>>
>> Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
>> Signed-off-by: Steven Price <steven.price@arm.com>
>> ---
>> Changes since v11:
>> * Reworked due to upstream changes.
>> Changes since v8:
>> * Minor type changes following review.
>> Changes since v7:
>> * Reworked on upstream changes.
>> ---
>> arch/arm64/kvm/guest.c | 6 ++++++
>> arch/arm64/kvm/hypercalls.c | 4 ++--
>> arch/arm64/kvm/sys_regs.c | 29 +++++++++++++++++++++++------
>> 3 files changed, 31 insertions(+), 8 deletions(-)
>>
>> diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
>> index 2c4db2d1a6ca..23fdb2ee8a61 100644
>> --- a/arch/arm64/kvm/guest.c
>> +++ b/arch/arm64/kvm/guest.c
>> @@ -620,6 +620,9 @@ static unsigned long num_sve_regs(const struct kvm_vcpu *vcpu)
>> if (!kvm_arm_vcpu_sve_finalized(vcpu))
>> return 1; /* KVM_REG_ARM64_SVE_VLS */
>>
>> + if (kvm_is_realm(vcpu->kvm))
>> + return 1; /* KVM_REG_ARM64_SVE_VLS */
>> +
>> return slices * (SVE_NUM_PREGS + SVE_NUM_ZREGS + 1 /* FFR */)
>> + 1; /* KVM_REG_ARM64_SVE_VLS */
>> }
>> @@ -647,6 +650,9 @@ static int copy_sve_reg_indices(const struct kvm_vcpu *vcpu,
>> if (!kvm_arm_vcpu_sve_finalized(vcpu))
>> return num_regs;
>>
>> + if (kvm_is_realm(vcpu->kvm))
>> + return num_regs;
>> +
>> for (i = 0; i < slices; i++) {
>> for (n = 0; n < SVE_NUM_ZREGS; n++) {
>> reg = KVM_REG_ARM64_SVE_ZREG(n, i);
>> diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c
>> index 58c5fe7d7572..70ac7971416c 100644
>> --- a/arch/arm64/kvm/hypercalls.c
>> +++ b/arch/arm64/kvm/hypercalls.c
>> @@ -414,14 +414,14 @@ void kvm_arm_teardown_hypercalls(struct kvm *kvm)
>>
>> int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
>> {
>> - return ARRAY_SIZE(kvm_arm_fw_reg_ids);
>> + return kvm_is_realm(vcpu->kvm) ? 0 : ARRAY_SIZE(kvm_arm_fw_reg_ids);
>> }
>>
>> int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
>> {
>> int i;
>>
>> - for (i = 0; i < ARRAY_SIZE(kvm_arm_fw_reg_ids); i++) {
>> + for (i = 0; i < kvm_arm_get_fw_num_regs(vcpu); i++) {
>> if (put_user(kvm_arm_fw_reg_ids[i], uindices++))
>> return -EFAULT;
>> }
>> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
>> index ebb428b861f5..088d900b9c3a 100644
>> --- a/arch/arm64/kvm/sys_regs.c
>> +++ b/arch/arm64/kvm/sys_regs.c
>> @@ -5436,18 +5436,18 @@ int kvm_arm_sys_reg_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg
>> sys_reg_descs, ARRAY_SIZE(sys_reg_descs));
>> }
>>
>> -static unsigned int num_demux_regs(void)
>> +static inline unsigned int num_demux_regs(struct kvm_vcpu *vcpu)
>> {
>> - return CSSELR_MAX;
>> + return kvm_is_realm(vcpu->kvm) ? 0 : CSSELR_MAX;
>> }
>>
>> -static int write_demux_regids(u64 __user *uindices)
>> +static int write_demux_regids(struct kvm_vcpu *vcpu, u64 __user *uindices)
>> {
>> u64 val = KVM_REG_ARM64 | KVM_REG_SIZE_U32 | KVM_REG_ARM_DEMUX;
>> unsigned int i;
>>
>> val |= KVM_REG_ARM_DEMUX_ID_CCSIDR;
>> - for (i = 0; i < CSSELR_MAX; i++) {
>> + for (i = 0; i < num_demux_regs(vcpu); i++) {
>> if (put_user(val | i, uindices))
>> return -EFAULT;
>> uindices++;
>> @@ -5491,11 +5491,28 @@ static bool copy_reg_to_user(const struct sys_reg_desc *reg, u64 __user **uind)
>> return true;
>> }
>>
>> +static inline bool kvm_realm_sys_reg_hidden_user(const struct kvm_vcpu *vcpu,
>> + u64 reg)
>> +{
>> + if (!kvm_is_realm(vcpu->kvm))
>> + return false;
>> +
>> + switch (reg) {
>> + case SYS_ID_AA64DFR0_EL1:
>> + case SYS_PMCR_EL0:
>> + return false;
>> + }
>> + return true;
>> +}
>> +
>> static int walk_one_sys_reg(const struct kvm_vcpu *vcpu,
>> const struct sys_reg_desc *rd,
>> u64 __user **uind,
>> unsigned int *total)
>> {
>> + if (kvm_realm_sys_reg_hidden_user(vcpu, reg_to_encoding(rd)))
>
> Hi,
>
> Same as my comment for patch 39, I would suggest moving the
> kvm_is_realm() check out of this function.
Sure, although at least this time the functions were right next to each
other ;)
Thanks,
Steve
> Thanks,
> Wei-Lin Chang
>
>> + return 0;
>> +
>> /*
>> * Ignore registers we trap but don't save,
>> * and for which no custom user accessor is provided.
>> @@ -5533,7 +5550,7 @@ static int walk_sys_regs(struct kvm_vcpu *vcpu, u64 __user *uind)
>>
>> unsigned long kvm_arm_num_sys_reg_descs(struct kvm_vcpu *vcpu)
>> {
>> - return num_demux_regs()
>> + return num_demux_regs(vcpu)
>> + walk_sys_regs(vcpu, (u64 __user *)NULL);
>> }
>>
>> @@ -5546,7 +5563,7 @@ int kvm_arm_copy_sys_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
>> return err;
>> uindices += err;
>>
>> - return write_demux_regids(uindices);
>> + return write_demux_regids(vcpu, uindices);
>> }
>>
>> #define KVM_ARM_FEATURE_ID_RANGE_INDEX(r) \
>> --
>> 2.43.0
>>
^ permalink raw reply
* Re: [PATCH v13 39/48] arm64: RMI: Propagate number of breakpoints and watchpoints to userspace
From: Steven Price @ 2026-03-20 16:45 UTC (permalink / raw)
To: Wei-Lin Chang, kvm, kvmarm
Cc: Jean-Philippe Brucker, Catalin Marinas, Marc Zyngier, Will Deacon,
James Morse, Oliver Upton, Suzuki K Poulose, Zenghui Yu,
linux-arm-kernel, linux-kernel, Joey Gouly, Alexandru Elisei,
Christoffer Dall, Fuad Tabba, linux-coco, Ganapatrao Kulkarni,
Gavin Shan, Shanker Donthineni, Alper Gun, Aneesh Kumar K . V,
Emi Kisanuki, Vishal Annapurve
In-Reply-To: <yqg4omdkqvslldhxbdzctbgxirxomw6mparqvhuybwyzthhdy3@6glrgqmfydsj>
On 19/03/2026 18:50, Wei-Lin Chang wrote:
> On Wed, Mar 18, 2026 at 03:54:03PM +0000, Steven Price wrote:
>> From: Jean-Philippe Brucker <jean-philippe@linaro.org>
>>
>> The RMM describes the maximum number of BPs/WPs available to the guest
>> in the Feature Register 0. Propagate those numbers into ID_AA64DFR0_EL1,
>> which is visible to userspace. A VMM needs this information in order to
>> set up realm parameters.
>>
>> Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
>> Signed-off-by: Steven Price <steven.price@arm.com>
>> Reviewed-by: Gavin Shan <gshan@redhat.com>
>> Reviewed-by: Suzuki K Poulose <suzuki.poulose@arm.com>
>> Reviewed-by: Joey Gouly <joey.gouly@arm.com>
>> ---
>> arch/arm64/include/asm/kvm_rmi.h | 2 ++
>> arch/arm64/kvm/rmi.c | 22 ++++++++++++++++++++++
>> arch/arm64/kvm/sys_regs.c | 2 +-
>> 3 files changed, 25 insertions(+), 1 deletion(-)
>>
>> diff --git a/arch/arm64/include/asm/kvm_rmi.h b/arch/arm64/include/asm/kvm_rmi.h
>> index 17bb7e2a2aa0..8fb526764c30 100644
>> --- a/arch/arm64/include/asm/kvm_rmi.h
>> +++ b/arch/arm64/include/asm/kvm_rmi.h
>> @@ -87,6 +87,8 @@ struct realm_rec {
>> void kvm_init_rmi(void);
>> u32 kvm_realm_ipa_limit(void);
>>
>> +u64 kvm_realm_reset_id_aa64dfr0_el1(const struct kvm_vcpu *vcpu, u64 val);
>> +
>> bool kvm_rmi_supports_sve(void);
>> bool kvm_rmi_supports_pmu(void);
>>
>> diff --git a/arch/arm64/kvm/rmi.c b/arch/arm64/kvm/rmi.c
>> index 8dc090da6e5f..01519d934d3a 100644
>> --- a/arch/arm64/kvm/rmi.c
>> +++ b/arch/arm64/kvm/rmi.c
>> @@ -212,6 +212,28 @@ u32 kvm_realm_ipa_limit(void)
>> return u64_get_bits(rmm_feat_reg0, RMI_FEATURE_REGISTER_0_S2SZ);
>> }
>>
>> +u64 kvm_realm_reset_id_aa64dfr0_el1(const struct kvm_vcpu *vcpu, u64 val)
>> +{
>> + u32 bps = u64_get_bits(rmm_feat_reg0, RMI_FEATURE_REGISTER_0_NUM_BPS);
>> + u32 wps = u64_get_bits(rmm_feat_reg0, RMI_FEATURE_REGISTER_0_NUM_WPS);
>> + u32 ctx_cmps;
>> +
>> + if (!kvm_is_realm(vcpu->kvm))
>> + return val;
>> +
>> + /* Ensure CTX_CMPs is still valid */
>> + ctx_cmps = FIELD_GET(ID_AA64DFR0_EL1_CTX_CMPs, val);
>> + ctx_cmps = min(bps, ctx_cmps);
>> +
>> + val &= ~(ID_AA64DFR0_EL1_BRPs_MASK | ID_AA64DFR0_EL1_WRPs_MASK |
>> + ID_AA64DFR0_EL1_CTX_CMPs);
>> + val |= FIELD_PREP(ID_AA64DFR0_EL1_BRPs_MASK, bps) |
>> + FIELD_PREP(ID_AA64DFR0_EL1_WRPs_MASK, wps) |
>> + FIELD_PREP(ID_AA64DFR0_EL1_CTX_CMPs, ctx_cmps);
>> +
>> + return val;
>> +}
>> +
>> static int get_start_level(struct realm *realm)
>> {
>> return 4 - stage2_pgtable_levels(realm->ia_bits);
>> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
>> index 46f5e2ab3e2c..83b5c36f43bf 100644
>> --- a/arch/arm64/kvm/sys_regs.c
>> +++ b/arch/arm64/kvm/sys_regs.c
>> @@ -2043,7 +2043,7 @@ static u64 sanitise_id_aa64dfr0_el1(const struct kvm_vcpu *vcpu, u64 val)
>> /* Hide BRBE from guests */
>> val &= ~ID_AA64DFR0_EL1_BRBE_MASK;
>>
>> - return val;
>> + return kvm_realm_reset_id_aa64dfr0_el1(vcpu, val);
>
> Hi,
>
> Nit:
> In other places we condition on kvm_is_realm() to separate
> realm/non-realm paths but here everyone goes into kvm_realm_*, do you
> think it's more consistent to move the kvm_is_realm() check out of this
> function?
Yes I agree that would be more consistent.
Thanks,
Steve
> Thanks,
> Wei-Lin Chang
>
>> }
>>
>> /*
>> --
>> 2.43.0
>>
^ permalink raw reply
* Re: [PATCH v13 37/48] arm64: RMI: Prevent Device mappings for Realms
From: Steven Price @ 2026-03-20 16:45 UTC (permalink / raw)
To: Wei-Lin Chang, kvm, kvmarm
Cc: Catalin Marinas, Marc Zyngier, Will Deacon, James Morse,
Oliver Upton, Suzuki K Poulose, Zenghui Yu, linux-arm-kernel,
linux-kernel, Joey Gouly, Alexandru Elisei, Christoffer Dall,
Fuad Tabba, linux-coco, Ganapatrao Kulkarni, Gavin Shan,
Shanker Donthineni, Alper Gun, Aneesh Kumar K . V, Emi Kisanuki,
Vishal Annapurve
In-Reply-To: <t3ge5bfjteubidjhcupkvzi4zdvepv2mtp7tan4mma6xd4jgnb@a6kjjbmt4aqk>
On 19/03/2026 18:46, Wei-Lin Chang wrote:
> On Wed, Mar 18, 2026 at 03:54:01PM +0000, Steven Price wrote:
>> Physical device assignment is not supported by RMM v1.0, so it
>> doesn't make much sense to allow device mappings within the realm.
>> Prevent them when the guest is a realm.
>>
>> Signed-off-by: Steven Price <steven.price@arm.com>
>> ---
>> Changes from v6:
>> * Fix the check in user_mem_abort() to prevent all pages that are not
>> guest_memfd() from being mapped into the protected half of the IPA.
>> Changes from v5:
>> * Also prevent accesses in user_mem_abort()
>> ---
>> arch/arm64/kvm/mmu.c | 13 +++++++++++++
>> 1 file changed, 13 insertions(+)
>>
>> diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
>> index ad1300f366df..7d7caab8f573 100644
>> --- a/arch/arm64/kvm/mmu.c
>> +++ b/arch/arm64/kvm/mmu.c
>> @@ -1222,6 +1222,10 @@ int kvm_phys_addr_ioremap(struct kvm *kvm, phys_addr_t guest_ipa,
>> if (is_protected_kvm_enabled())
>> return -EPERM;
>>
>> + /* We don't support mapping special pages into a Realm */
>> + if (kvm_is_realm(kvm))
>> + return -EPERM;
>> +
>> size += offset_in_page(guest_ipa);
>> guest_ipa &= PAGE_MASK;
>>
>> @@ -1965,6 +1969,15 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
>> return 1;
>> }
>>
>> + /*
>> + * For now we shouldn't be hitting protected addresses because they are
>> + * handled in private_memslot_fault(). In the future this check may be
>
> Hi,
>
> What is private_memslot_fault()? I don't see it anywhere in the series &
> upstream.
Oh dear, that comment is out of date ;) It's now become gmem_abort()...
>> + * relaxed to support e.g. protected devices.
>> + */
>> + if (vcpu_is_rec(vcpu) &&
>> + kvm_gpa_from_fault(kvm, fault_ipa) == fault_ipa)
>> + return -EINVAL;
>> +
>
> Additionally, there is a hunk almost identical to this one here in added
> in patch 27.
Which is what this chunk says. It appears I screwed up a rebase at some
point! This whole patch can really be dropped and the
kvm_phys_addr_ioremap() change moved into another patch.
Thanks,
Steve
> Thanks,
> Wei-Lin Chang
>
>> if (nested)
>> adjust_nested_fault_perms(nested, &prot, &writable);
>>
>> --
>> 2.43.0
>>
^ permalink raw reply
* Re: [PATCH v13 27/48] arm64: RMI: Runtime faulting of memory
From: Steven Price @ 2026-03-20 16:44 UTC (permalink / raw)
To: Wei-Lin Chang, kvm, kvmarm
Cc: Catalin Marinas, Marc Zyngier, Will Deacon, James Morse,
Oliver Upton, Suzuki K Poulose, Zenghui Yu, linux-arm-kernel,
linux-kernel, Joey Gouly, Alexandru Elisei, Christoffer Dall,
Fuad Tabba, linux-coco, Ganapatrao Kulkarni, Gavin Shan,
Shanker Donthineni, Alper Gun, Aneesh Kumar K . V, Emi Kisanuki,
Vishal Annapurve
In-Reply-To: <j6jkvfhj5jae37vtp7ranar55fzqu4ps6ngdolfcspfvzob2es@srtfblbqiunn>
On 19/03/2026 18:41, Wei-Lin Chang wrote:
> On Wed, Mar 18, 2026 at 03:53:51PM +0000, Steven Price wrote:
>> At runtime if the realm guest accesses memory which hasn't yet been
>> mapped then KVM needs to either populate the region or fault the guest.
>>
>> For memory in the lower (protected) region of IPA a fresh page is
>> provided to the RMM which will zero the contents. For memory in the
>> upper (shared) region of IPA, the memory from the memslot is mapped
>> into the realm VM non secure.
>>
>> Signed-off-by: Steven Price <steven.price@arm.com>
>> ---
>> Changes since v12:
>> * Switch to RMM v2.0 range based APIs.
>> Changes since v11:
>> * Adapt to upstream changes.
>> Changes since v10:
>> * RME->RMI renaming.
>> * Adapt to upstream gmem changes.
>> Changes since v9:
>> * Fix call to kvm_stage2_unmap_range() in kvm_free_stage2_pgd() to set
>> may_block to avoid stall warnings.
>> * Minor coding style fixes.
>> Changes since v8:
>> * Propagate the may_block flag.
>> * Minor comments and coding style changes.
>> Changes since v7:
>> * Remove redundant WARN_ONs for realm_create_rtt_levels() - it will
>> internally WARN when necessary.
>> Changes since v6:
>> * Handle PAGE_SIZE being larger than RMM granule size.
>> * Some minor renaming following review comments.
>> Changes since v5:
>> * Reduce use of struct page in preparation for supporting the RMM
>> having a different page size to the host.
>> * Handle a race when delegating a page where another CPU has faulted on
>> a the same page (and already delegated the physical page) but not yet
>> mapped it. In this case simply return to the guest to either use the
>> mapping from the other CPU (or refault if the race is lost).
>> * The changes to populate_par_region() are moved into the previous
>> patch where they belong.
>> Changes since v4:
>> * Code cleanup following review feedback.
>> * Drop the PTE_SHARED bit when creating unprotected page table entries.
>> This is now set by the RMM and the host has no control of it and the
>> spec requires the bit to be set to zero.
>> Changes since v2:
>> * Avoid leaking memory if failing to map it in the realm.
>> * Correctly mask RTT based on LPA2 flag (see rtt_get_phys()).
>> * Adapt to changes in previous patches.
>> ---
>> arch/arm64/include/asm/kvm_emulate.h | 8 ++
>> arch/arm64/include/asm/kvm_rmi.h | 12 ++
>> arch/arm64/kvm/mmu.c | 139 ++++++++++++++++--
>> arch/arm64/kvm/rmi.c | 206 +++++++++++++++++++++++++++
>> 4 files changed, 351 insertions(+), 14 deletions(-)
>>
>> diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h
>> index d194d91fbc2a..0734c4a65174 100644
>> --- a/arch/arm64/include/asm/kvm_emulate.h
>> +++ b/arch/arm64/include/asm/kvm_emulate.h
>> @@ -706,6 +706,14 @@ static inline bool kvm_realm_is_created(struct kvm *kvm)
>> return kvm_is_realm(kvm) && kvm_realm_state(kvm) != REALM_STATE_NONE;
>> }
>>
>> +static inline gpa_t kvm_gpa_from_fault(struct kvm *kvm, phys_addr_t ipa)
>
> I think we should use gpa_t as the type for ipa.
This is just matching the usage in mmu.c, e.g. user_mem_abort() has
fault_ipa as a phys_addr_t.
>> +{
>> + if (!kvm_is_realm(kvm))
>> + return ipa;
>> +
>> + return ipa & ~BIT(kvm->arch.realm.ia_bits - 1);
>> +}
>> +
>> static inline bool vcpu_is_rec(struct kvm_vcpu *vcpu)
>> {
>> return kvm_is_realm(vcpu->kvm);
>> diff --git a/arch/arm64/include/asm/kvm_rmi.h b/arch/arm64/include/asm/kvm_rmi.h
>> index bf663bb240c4..38208be3c602 100644
>> --- a/arch/arm64/include/asm/kvm_rmi.h
>> +++ b/arch/arm64/include/asm/kvm_rmi.h
>> @@ -6,6 +6,7 @@
>> #ifndef __ASM_KVM_RMI_H
>> #define __ASM_KVM_RMI_H
>>
>> +#include <asm/kvm_pgtable.h>
>> #include <asm/rmi_smc.h>
>>
>> /**
>> @@ -105,6 +106,17 @@ void kvm_realm_unmap_range(struct kvm *kvm,
>> unsigned long size,
>> bool unmap_private,
>> bool may_block);
>> +int realm_map_protected(struct kvm *kvm,
>> + unsigned long base_ipa,
>> + kvm_pfn_t pfn,
>> + unsigned long size,
>> + struct kvm_mmu_memory_cache *memcache);
>> +int realm_map_non_secure(struct realm *realm,
>> + unsigned long ipa,
>> + kvm_pfn_t pfn,
>> + unsigned long size,
>> + enum kvm_pgtable_prot prot,
>> + struct kvm_mmu_memory_cache *memcache);
>>
>> static inline bool kvm_realm_is_private_address(struct realm *realm,
>> unsigned long addr)
>> diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
>> index bad93938acdb..73c18c2861a2 100644
>> --- a/arch/arm64/kvm/mmu.c
>> +++ b/arch/arm64/kvm/mmu.c
>> @@ -334,8 +334,15 @@ static void __unmap_stage2_range(struct kvm_s2_mmu *mmu, phys_addr_t start, u64
>>
>> lockdep_assert_held_write(&kvm->mmu_lock);
>> WARN_ON(size & ~PAGE_MASK);
>> - WARN_ON(stage2_apply_range(mmu, start, end, KVM_PGT_FN(kvm_pgtable_stage2_unmap),
>> - may_block));
>> +
>> + if (kvm_is_realm(kvm)) {
>> + kvm_realm_unmap_range(kvm, start, size, !only_shared,
>> + may_block);
>> + } else {
>> + WARN_ON(stage2_apply_range(mmu, start, end,
>> + KVM_PGT_FN(kvm_pgtable_stage2_unmap),
>> + may_block));
>> + }
>> }
>>
>> void kvm_stage2_unmap_range(struct kvm_s2_mmu *mmu, phys_addr_t start,
>> @@ -355,7 +362,10 @@ static void stage2_flush_memslot(struct kvm *kvm,
>> phys_addr_t addr = memslot->base_gfn << PAGE_SHIFT;
>> phys_addr_t end = addr + PAGE_SIZE * memslot->npages;
>>
>> - kvm_stage2_flush_range(&kvm->arch.mmu, addr, end);
>> + if (kvm_is_realm(kvm))
>> + kvm_realm_unmap_range(kvm, addr, end - addr, false, true);
>> + else
>> + kvm_stage2_flush_range(&kvm->arch.mmu, addr, end);
>> }
>>
>> /**
>> @@ -1081,6 +1091,10 @@ void stage2_unmap_vm(struct kvm *kvm)
>> struct kvm_memory_slot *memslot;
>> int idx, bkt;
>>
>> + /* For realms this is handled by the RMM so nothing to do here */
>> + if (kvm_is_realm(kvm))
>> + return;
>> +
>> idx = srcu_read_lock(&kvm->srcu);
>> mmap_read_lock(current->mm);
>> write_lock(&kvm->mmu_lock);
>> @@ -1106,6 +1120,9 @@ void kvm_free_stage2_pgd(struct kvm_s2_mmu *mmu)
>> if (kvm_is_realm(kvm) &&
>> (kvm_realm_state(kvm) != REALM_STATE_DEAD &&
>> kvm_realm_state(kvm) != REALM_STATE_NONE)) {
>> + struct realm *realm = &kvm->arch.realm;
>> +
>> + kvm_stage2_unmap_range(mmu, 0, BIT(realm->ia_bits - 1), true);
>> write_unlock(&kvm->mmu_lock);
>> kvm_realm_destroy_rtts(kvm);
>>
>> @@ -1516,6 +1533,29 @@ static bool kvm_vma_mte_allowed(struct vm_area_struct *vma)
>> return vma->vm_flags & VM_MTE_ALLOWED;
>> }
>>
>> +static int realm_map_ipa(struct kvm *kvm, phys_addr_t ipa,
>> + kvm_pfn_t pfn, unsigned long map_size,
>> + enum kvm_pgtable_prot prot,
>> + struct kvm_mmu_memory_cache *memcache)
>> +{
>> + struct realm *realm = &kvm->arch.realm;
>> +
>> + /*
>> + * Write permission is required for now even though it's possible to
>> + * map unprotected pages (granules) as read-only. It's impossible to
>> + * map protected pages (granules) as read-only.
>> + */
>> + if (WARN_ON(!(prot & KVM_PGTABLE_PROT_W)))
>> + return -EFAULT;
>> +
>> + ipa = ALIGN_DOWN(ipa, PAGE_SIZE);
>> + if (!kvm_realm_is_private_address(realm, ipa))
>> + return realm_map_non_secure(realm, ipa, pfn, map_size, prot,
>> + memcache);
>> +
>> + return realm_map_protected(kvm, ipa, pfn, map_size, memcache);
>> +}
>> +
>> static bool kvm_vma_is_cacheable(struct vm_area_struct *vma)
>> {
>> switch (FIELD_GET(PTE_ATTRINDX_MASK, pgprot_val(vma->vm_page_prot))) {
>> @@ -1588,6 +1628,7 @@ static int gmem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
>> enum kvm_pgtable_walk_flags flags = KVM_PGTABLE_WALK_SHARED;
>> enum kvm_pgtable_prot prot = KVM_PGTABLE_PROT_R;
>> struct kvm_pgtable *pgt = vcpu->arch.hw_mmu->pgt;
>> + gpa_t gpa = kvm_gpa_from_fault(vcpu->kvm, fault_ipa);
>> unsigned long mmu_seq;
>> struct page *page;
>> struct kvm *kvm = vcpu->kvm;
>> @@ -1596,6 +1637,29 @@ static int gmem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
>> gfn_t gfn;
>> int ret;
>>
>> + if (kvm_is_realm(vcpu->kvm)) {
>> + /* check for memory attribute mismatch */
>> + bool is_priv_gfn = kvm_mem_is_private(kvm, gpa >> PAGE_SHIFT);
>> + /*
>> + * For Realms, the shared address is an alias of the private
>> + * PA with the top bit set. Thus is the fault address matches
>
> Hi,
>
> Thus is -> Thus if.
Ack
>> + * the GPA then it is the private alias.
>> + */
>> + bool is_priv_fault = (gpa == fault_ipa);
>> +
>> + if (is_priv_gfn != is_priv_fault) {
>> + kvm_prepare_memory_fault_exit(vcpu, gpa, PAGE_SIZE,
>> + kvm_is_write_fault(vcpu),
>> + false,
>> + is_priv_fault);
>> + /*
>> + * KVM_EXIT_MEMORY_FAULT requires an return code of
>> + * -EFAULT, see the API documentation
>> + */
>> + return -EFAULT;
>> + }
>> + }
>> +
>> ret = prepare_mmu_memcache(vcpu, true, &memcache);
>> if (ret)
>> return ret;
>> @@ -1603,7 +1667,7 @@ static int gmem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
>> if (nested)
>> gfn = kvm_s2_trans_output(nested) >> PAGE_SHIFT;
>> else
>> - gfn = fault_ipa >> PAGE_SHIFT;
>> + gfn = gpa >> PAGE_SHIFT;
>>
>> write_fault = kvm_is_write_fault(vcpu);
>> exec_fault = kvm_vcpu_trap_is_exec_fault(vcpu);
>> @@ -1616,7 +1680,7 @@ static int gmem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
>>
>> ret = kvm_gmem_get_pfn(kvm, memslot, gfn, &pfn, &page, NULL);
>> if (ret) {
>> - kvm_prepare_memory_fault_exit(vcpu, fault_ipa, PAGE_SIZE,
>> + kvm_prepare_memory_fault_exit(vcpu, gpa, PAGE_SIZE,
>> write_fault, exec_fault, false);
>> return ret;
>> }
>> @@ -1638,15 +1702,25 @@ static int gmem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
>> kvm_fault_lock(kvm);
>> if (mmu_invalidate_retry(kvm, mmu_seq)) {
>> ret = -EAGAIN;
>> - goto out_unlock;
>> + goto out_release_page;
>> + }
>> +
>> + if (kvm_is_realm(kvm)) {
>> + ret = realm_map_ipa(kvm, fault_ipa, pfn,
>> + PAGE_SIZE, KVM_PGTABLE_PROT_W, memcache);
>> + /* if successful don't release the page */
>> + if (!ret)
>> + goto out_unlock;
>> + goto out_release_page;
>> }
>>
>> ret = KVM_PGT_FN(kvm_pgtable_stage2_map)(pgt, fault_ipa, PAGE_SIZE,
>> __pfn_to_phys(pfn), prot,
>> memcache, flags);
>>
>> -out_unlock:
>> +out_release_page:
>> kvm_release_faultin_page(kvm, page, !!ret, writable);
>> +out_unlock:
>> kvm_fault_unlock(kvm);
>>
>> if (writable && !ret)
>> @@ -1685,6 +1759,14 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
>> if (fault_is_perm)
>> fault_granule = kvm_vcpu_trap_get_perm_fault_granule(vcpu);
>> write_fault = kvm_is_write_fault(vcpu);
>> +
>> + /*
>> + * Realms cannot map protected pages read-only
>> + * FIXME: It should be possible to map unprotected pages read-only
>> + */
>> + if (vcpu_is_rec(vcpu))
>> + write_fault = true;
>> +
>> exec_fault = kvm_vcpu_trap_is_exec_fault(vcpu);
>> VM_WARN_ON_ONCE(write_fault && exec_fault);
>>
>> @@ -1779,7 +1861,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
>> ipa &= ~(vma_pagesize - 1);
>> }
>>
>> - gfn = ipa >> PAGE_SHIFT;
>> + gfn = kvm_gpa_from_fault(kvm, ipa) >> PAGE_SHIFT;
>> mte_allowed = kvm_vma_mte_allowed(vma);
>>
>> vfio_allow_any_uc = vma->vm_flags & VM_ALLOW_ANY_UNCACHED;
>> @@ -1855,6 +1937,15 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
>> if (exec_fault && s2_force_noncacheable)
>> ret = -ENOEXEC;
>>
>> + /*
>> + * For now we shouldn't be hitting protected addresses because they are
>> + * handled in gmem_abort(). In the future this check may be relaxed to
>> + * support e.g. protected devices.
>> + */
>> + if (!ret && vcpu_is_rec(vcpu) &&
>> + kvm_gpa_from_fault(kvm, fault_ipa) == fault_ipa)
>
> Maybe use !shared_ipa_fault() here?
Ack.
Thanks,
Steve
>
> Thanks,
> Wei-Lin Chang
>
>> + ret = -EINVAL;
>> +
>> if (ret) {
>> kvm_release_page_unused(page);
>> return ret;
>> @@ -1939,6 +2030,9 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
>> */
>> prot &= ~KVM_NV_GUEST_MAP_SZ;
>> ret = KVM_PGT_FN(kvm_pgtable_stage2_relax_perms)(pgt, fault_ipa, prot, flags);
>> + } else if (kvm_is_realm(kvm)) {
>> + ret = realm_map_ipa(kvm, fault_ipa, pfn, vma_pagesize,
>> + prot, memcache);
>> } else {
>> ret = KVM_PGT_FN(kvm_pgtable_stage2_map)(pgt, fault_ipa, vma_pagesize,
>> __pfn_to_phys(pfn), prot,
>> @@ -2049,6 +2143,13 @@ int kvm_handle_guest_sea(struct kvm_vcpu *vcpu)
>> return 0;
>> }
>>
>> +static bool shared_ipa_fault(struct kvm *kvm, phys_addr_t fault_ipa)
>> +{
>> + gpa_t gpa = kvm_gpa_from_fault(kvm, fault_ipa);
>> +
>> + return (gpa != fault_ipa);
>> +}
>> +
>> /**
>> * kvm_handle_guest_abort - handles all 2nd stage aborts
>> * @vcpu: the VCPU pointer
>> @@ -2159,8 +2260,9 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
>> nested = &nested_trans;
>> }
>>
>> - gfn = ipa >> PAGE_SHIFT;
>> + gfn = kvm_gpa_from_fault(vcpu->kvm, ipa) >> PAGE_SHIFT;
>> memslot = gfn_to_memslot(vcpu->kvm, gfn);
>> +
>> hva = gfn_to_hva_memslot_prot(memslot, gfn, &writable);
>> write_fault = kvm_is_write_fault(vcpu);
>> if (kvm_is_error_hva(hva) || (write_fault && !writable)) {
>> @@ -2203,7 +2305,7 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
>> * of the page size.
>> */
>> ipa |= FAR_TO_FIPA_OFFSET(kvm_vcpu_get_hfar(vcpu));
>> - ret = io_mem_abort(vcpu, ipa);
>> + ret = io_mem_abort(vcpu, kvm_gpa_from_fault(vcpu->kvm, ipa));
>> goto out_unlock;
>> }
>>
>> @@ -2219,7 +2321,7 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
>> VM_WARN_ON_ONCE(kvm_vcpu_trap_is_permission_fault(vcpu) &&
>> !write_fault && !kvm_vcpu_trap_is_exec_fault(vcpu));
>>
>> - if (kvm_slot_has_gmem(memslot))
>> + if (kvm_slot_has_gmem(memslot) && !shared_ipa_fault(vcpu->kvm, fault_ipa))
>> ret = gmem_abort(vcpu, fault_ipa, nested, memslot,
>> esr_fsc_is_permission_fault(esr));
>> else
>> @@ -2256,6 +2358,10 @@ bool kvm_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
>> if (!kvm->arch.mmu.pgt)
>> return false;
>>
>> + /* We don't support aging for Realms */
>> + if (kvm_is_realm(kvm))
>> + return true;
>> +
>> return KVM_PGT_FN(kvm_pgtable_stage2_test_clear_young)(kvm->arch.mmu.pgt,
>> range->start << PAGE_SHIFT,
>> size, true);
>> @@ -2272,6 +2378,10 @@ bool kvm_test_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
>> if (!kvm->arch.mmu.pgt)
>> return false;
>>
>> + /* We don't support aging for Realms */
>> + if (kvm_is_realm(kvm))
>> + return true;
>> +
>> return KVM_PGT_FN(kvm_pgtable_stage2_test_clear_young)(kvm->arch.mmu.pgt,
>> range->start << PAGE_SHIFT,
>> size, false);
>> @@ -2438,10 +2548,11 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm,
>> return -EFAULT;
>>
>> /*
>> - * Only support guest_memfd backed memslots with mappable memory, since
>> - * there aren't any CoCo VMs that support only private memory on arm64.
>> + * Only support guest_memfd backed memslots with mappable memory,
>> + * unless the guest is a CCA realm guest.
>> */
>> - if (kvm_slot_has_gmem(new) && !kvm_memslot_is_gmem_only(new))
>> + if (kvm_slot_has_gmem(new) && !kvm_memslot_is_gmem_only(new) &&
>> + !kvm_is_realm(kvm))
>> return -EINVAL;
>>
>> hva = new->userspace_addr;
>> diff --git a/arch/arm64/kvm/rmi.c b/arch/arm64/kvm/rmi.c
>> index d5fee203824b..30292814b1ec 100644
>> --- a/arch/arm64/kvm/rmi.c
>> +++ b/arch/arm64/kvm/rmi.c
>> @@ -837,6 +837,212 @@ static int realm_create_protected_data_page(struct kvm *kvm,
>> return ret;
>> }
>>
>> +static int fold_rtt(struct realm *realm, unsigned long addr, int level)
>> +{
>> + phys_addr_t rtt_addr;
>> + int ret;
>> +
>> + ret = realm_rtt_fold(realm, addr, level, &rtt_addr);
>> + if (ret)
>> + return ret;
>> +
>> + free_rtt(rtt_addr);
>> +
>> + return 0;
>> +}
>> +
>> +static unsigned long addr_range_desc(unsigned long phys, unsigned long size)
>> +{
>> + unsigned long out = 0;
>> +
>> + switch (size) {
>> + case P4D_SIZE:
>> + out = 0 | (1 << 2);
>> + break;
>> + case PUD_SIZE:
>> + out = 1 | (1 << 2);
>> + break;
>> + case PMD_SIZE:
>> + out = 2 | (1 << 2);
>> + break;
>> + case PAGE_SIZE:
>> + out = 3 | (1 << 2);
>> + break;
>> + default:
>> + /*
>> + * Only support mapping at the page level granulatity when
>> + * it's an unusual length. This should get us back onto a larger
>> + * block size for the subsequent mappings.
>> + */
>> + out = 3 | ((MIN(size >> PAGE_SHIFT, PTRS_PER_PTE - 1)) << 2);
>> + break;
>> + }
>> +
>> + WARN_ON(phys & ~PAGE_MASK);
>> +
>> + out |= phys & PAGE_MASK;
>> +
>> + return out;
>> +}
>> +
>> +int realm_map_protected(struct kvm *kvm,
>> + unsigned long ipa,
>> + kvm_pfn_t pfn,
>> + unsigned long map_size,
>> + struct kvm_mmu_memory_cache *memcache)
>> +{
>> + struct realm *realm = &kvm->arch.realm;
>> + phys_addr_t phys = __pfn_to_phys(pfn);
>> + phys_addr_t rd = virt_to_phys(realm->rd);
>> + unsigned long base_ipa = ipa;
>> + unsigned long ipa_top = ipa + map_size;
>> + int map_level = IS_ALIGNED(map_size, RMM_L2_BLOCK_SIZE) ?
>> + RMM_RTT_BLOCK_LEVEL : RMM_RTT_MAX_LEVEL;
>> + int ret = 0;
>> +
>> + if (WARN_ON(!IS_ALIGNED(map_size, PAGE_SIZE) ||
>> + !IS_ALIGNED(ipa, map_size)))
>> + return -EINVAL;
>> +
>> + if (map_level < RMM_RTT_MAX_LEVEL) {
>> + /*
>> + * A temporary RTT is needed during the map, precreate it,
>> + * however if there is an error (e.g. missing parent tables)
>> + * this will be handled below.
>> + */
>> + realm_create_rtt_levels(realm, ipa, map_level,
>> + RMM_RTT_MAX_LEVEL, memcache);
>> + }
>> +
>> + if (delegate_range(phys, map_size)) {
>> + /*
>> + * It's likely we raced with another VCPU on the same
>> + * fault. Assume the other VCPU has handled the fault
>> + * and return to the guest.
>> + */
>> + return 0;
>> + }
>> +
>> + while (ipa < ipa_top) {
>> + unsigned long flags = RMI_ADDR_TYPE_SINGLE;
>> + unsigned long range_desc = addr_range_desc(phys, ipa_top - ipa);
>> + unsigned long out_top;
>> +
>> + ret = rmi_rtt_data_map(rd, ipa, ipa_top, flags, range_desc,
>> + &out_top);
>> +
>> + if (RMI_RETURN_STATUS(ret) == RMI_ERROR_RTT) {
>> + /* Create missing RTTs and retry */
>> + int level = RMI_RETURN_INDEX(ret);
>> +
>> + WARN_ON(level == RMM_RTT_MAX_LEVEL);
>> + ret = realm_create_rtt_levels(realm, ipa, level,
>> + RMM_RTT_MAX_LEVEL,
>> + memcache);
>> + if (ret)
>> + goto err_undelegate;
>> +
>> + ret = rmi_rtt_data_map(rd, ipa, ipa_top, flags,
>> + range_desc, &out_top);
>> + }
>> +
>> + if (WARN_ON(ret))
>> + goto err_undelegate;
>> +
>> + phys += out_top - ipa;
>> + ipa = out_top;
>> + }
>> +
>> + if (map_size == RMM_L2_BLOCK_SIZE) {
>> + ret = fold_rtt(realm, base_ipa, map_level + 1);
>> + if (WARN_ON(ret))
>> + goto err;
>> + }
>> +
>> + return 0;
>> +
>> +err_undelegate:
>> + if (WARN_ON(undelegate_range(phys, map_size))) {
>> + /* Page can't be returned to NS world so is lost */
>> + get_page(phys_to_page(phys));
>> + }
>> +err:
>> + realm_unmap_private_range(kvm, base_ipa, ipa, true);
>> + return -ENXIO;
>> +}
>> +
>> +int realm_map_non_secure(struct realm *realm,
>> + unsigned long ipa,
>> + kvm_pfn_t pfn,
>> + unsigned long size,
>> + enum kvm_pgtable_prot prot,
>> + struct kvm_mmu_memory_cache *memcache)
>> +{
>> + unsigned long attr;
>> + phys_addr_t rd = virt_to_phys(realm->rd);
>> + phys_addr_t phys = __pfn_to_phys(pfn);
>> + unsigned long offset;
>> + /* TODO: Support block mappings */
>> + int map_level = RMM_RTT_MAX_LEVEL;
>> + int map_size = rmi_rtt_level_mapsize(map_level);
>> + int ret = 0;
>> +
>> + if (WARN_ON(!IS_ALIGNED(size, PAGE_SIZE) ||
>> + !IS_ALIGNED(ipa, size)))
>> + return -EINVAL;
>> +
>> + switch (prot & (KVM_PGTABLE_PROT_DEVICE | KVM_PGTABLE_PROT_NORMAL_NC)) {
>> + case KVM_PGTABLE_PROT_DEVICE | KVM_PGTABLE_PROT_NORMAL_NC:
>> + return -EINVAL;
>> + case KVM_PGTABLE_PROT_DEVICE:
>> + attr = PTE_S2_MEMATTR(MT_S2_FWB_DEVICE_nGnRE);
>> + break;
>> + case KVM_PGTABLE_PROT_NORMAL_NC:
>> + attr = PTE_S2_MEMATTR(MT_S2_FWB_NORMAL_NC);
>> + break;
>> + default:
>> + attr = PTE_S2_MEMATTR(MT_S2_FWB_NORMAL);
>> + }
>> +
>> + for (offset = 0; offset < size; offset += map_size) {
>> + /*
>> + * realm_map_ipa() enforces that the memory is writable,
>> + * so for now we permit both read and write.
>> + */
>> + unsigned long desc = kvm_phys_to_pte(phys) | attr |
>> + KVM_PTE_LEAF_ATTR_LO_S2_S2AP_R |
>> + KVM_PTE_LEAF_ATTR_LO_S2_S2AP_W;
>> + ret = rmi_rtt_map_unprotected(rd, ipa, map_level, desc);
>> +
>> + if (RMI_RETURN_STATUS(ret) == RMI_ERROR_RTT) {
>> + /* Create missing RTTs and retry */
>> + int level = RMI_RETURN_INDEX(ret);
>> +
>> + ret = realm_create_rtt_levels(realm, ipa, level,
>> + map_level, memcache);
>> + if (ret)
>> + return -ENXIO;
>> +
>> + ret = rmi_rtt_map_unprotected(rd, ipa, map_level, desc);
>> + }
>> + /*
>> + * RMI_ERROR_RTT can be reported for two reasons: either the
>> + * RTT tables are not there, or there is an RTTE already
>> + * present for the address. The above call to create RTTs
>> + * handles the first case, and in the second case this
>> + * indicates that another thread has already populated the RTTE
>> + * for us, so we can ignore the error and continue.
>> + */
>> + if (ret && RMI_RETURN_STATUS(ret) != RMI_ERROR_RTT)
>> + return -ENXIO;
>> +
>> + ipa += map_size;
>> + phys += map_size;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> static int populate_region_cb(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn,
>> struct page *src_page, void *opaque)
>> {
>> --
>> 2.43.0
>>
^ permalink raw reply
* Re: [PATCH v13 26/48] arm64: RMI: Create the realm descriptor
From: Steven Price @ 2026-03-20 16:41 UTC (permalink / raw)
To: Wei-Lin Chang, kvm, kvmarm
Cc: Catalin Marinas, Marc Zyngier, Will Deacon, James Morse,
Oliver Upton, Suzuki K Poulose, Zenghui Yu, linux-arm-kernel,
linux-kernel, Joey Gouly, Alexandru Elisei, Christoffer Dall,
Fuad Tabba, linux-coco, Ganapatrao Kulkarni, Gavin Shan,
Shanker Donthineni, Alper Gun, Aneesh Kumar K . V, Emi Kisanuki,
Vishal Annapurve
In-Reply-To: <oukw53xfroe32rb26jy365nqsuttlakkjgpmh6sszdprx6nqfc@qfmwsob2ewa4>
On 19/03/2026 18:25, Wei-Lin Chang wrote:
> On Wed, Mar 18, 2026 at 03:53:50PM +0000, Steven Price wrote:
>> Creating a realm involves first creating a realm descriptor (RD). This
>> involves passing the configuration information to the RMM. Do this as
>> part of realm_ensure_created() so that the realm is created when it is
>> first needed.
>>
>> Signed-off-by: Steven Price <steven.price@arm.com>
>> ---
>> Changes since v12:
>> * Since RMM page size is now equal to the host's page size various
>> calculations are simplified.
>> * Switch to using range based APIs to delegate/undelegate.
>> * VMID handling is now handled entirely by the RMM.
>> ---
>> arch/arm64/kvm/rmi.c | 94 +++++++++++++++++++++++++++++++++++++++++++-
>> 1 file changed, 92 insertions(+), 2 deletions(-)
>>
>> diff --git a/arch/arm64/kvm/rmi.c b/arch/arm64/kvm/rmi.c
>> index 38349c7b34f4..d5fee203824b 100644
>> --- a/arch/arm64/kvm/rmi.c
>> +++ b/arch/arm64/kvm/rmi.c
>> @@ -649,6 +649,83 @@ static void realm_unmap_shared_range(struct kvm *kvm,
>> start, end);
>> }
>>
>> +static int realm_create_rd(struct kvm *kvm)
>> +{
>> + struct realm *realm = &kvm->arch.realm;
>> + struct realm_params *params = realm->params;
>> + void *rd = NULL;
>> + phys_addr_t rd_phys, params_phys;
>> + size_t pgd_size = kvm_pgtable_stage2_pgd_size(kvm->arch.mmu.vtcr);
>> + int i, r;
>> +
>> + realm->ia_bits = VTCR_EL2_IPA(kvm->arch.mmu.vtcr);
>> +
>> + if (WARN_ON(realm->rd || !realm->params))
>> + return -EEXIST;
>> +
>> + rd = (void *)__get_free_page(GFP_KERNEL);
>
> Hi,
>
> Should this be GFP_KERNEL_ACCOUNT?
Yes that would be better.
>> + if (!rd)
>> + return -ENOMEM;
>> +
>> + rd_phys = virt_to_phys(rd);
>> + if (delegate_page(rd_phys)) {
>> + r = -ENXIO;
>> + goto free_rd;
>> + }
>> +
>> + if (delegate_range(kvm->arch.mmu.pgd_phys, pgd_size)) {
>> + r = -ENXIO;
>> + goto out_undelegate_tables;
>> + }
>> +
>> + params->s2sz = VTCR_EL2_IPA(kvm->arch.mmu.vtcr);
>> + params->rtt_level_start = get_start_level(realm);
>> + params->rtt_num_start = pgd_size / PAGE_SIZE;
>> + params->rtt_base = kvm->arch.mmu.pgd_phys;
>> +
>> + if (kvm->arch.arm_pmu) {
>> + params->pmu_num_ctrs = kvm->arch.nr_pmu_counters;
>> + params->flags |= RMI_REALM_PARAM_FLAG_PMU;
>> + }
>> +
>> + if (kvm_lpa2_is_enabled())
>> + params->flags |= RMI_REALM_PARAM_FLAG_LPA2;
>> +
>> + params_phys = virt_to_phys(params);
>> +
>> + if (rmi_realm_create(rd_phys, params_phys)) {
>> + r = -ENXIO;
>> + goto out_undelegate_tables;
>> + }
>> +
>> + if (WARN_ON(rmi_rec_aux_count(rd_phys, &realm->num_aux))) {
>> + WARN_ON(rmi_realm_destroy(rd_phys));
>> + r = -ENXIO;
>> + goto out_undelegate_tables;
>> + }
>> +
>> + realm->rd = rd;
>> + WRITE_ONCE(realm->state, REALM_STATE_NEW);
>> + /* The realm is up, free the parameters. */
>> + free_page((unsigned long)realm->params);
>> + realm->params = NULL;
>> +
>> + return 0;
>> +
>> +out_undelegate_tables:
>> + if (WARN_ON(undelegate_range(kvm->arch.mmu.pgd_phys, i))) {
>> + /* Leak the pages if they cannot be returned */
>> + kvm->arch.mmu.pgt = NULL;
>
> Did you mean kvm->arch.mmu.pgd_phys = NULL; ?
No, although I agree this isn't exactly ideal. kvm_free_stage2_pgd()
uses mmu->pgt to decide whether to free the memory - pgd_phys isn't used
in that path. Technically here we end up leaking more than just the PGD
pages in this case, but as it's a "should never happen" case I didn't
see the need to worry about the leak being a bit larger than necessary.
Thanks,
Steve
> Thanks,
> Wei-Lin Chang
>
>> + }
>> + if (WARN_ON(undelegate_page(rd_phys))) {
>> + /* Leak the page if it isn't returned */
>> + return r;
>> + }
>> +free_rd:
>> + free_page((unsigned long)rd);
>> + return r;
>> +}
>> +
>> static void realm_unmap_private_range(struct kvm *kvm,
>> unsigned long start,
>> unsigned long end,
>> @@ -893,8 +970,21 @@ static int realm_init_ipa_state(struct kvm *kvm,
>>
>> static int realm_ensure_created(struct kvm *kvm)
>> {
>> - /* Provided in later patch */
>> - return -ENXIO;
>> + int ret;
>> +
>> + switch (kvm_realm_state(kvm)) {
>> + case REALM_STATE_NONE:
>> + break;
>> + case REALM_STATE_NEW:
>> + return 0;
>> + case REALM_STATE_DEAD:
>> + return -ENXIO;
>> + default:
>> + return -EBUSY;
>> + }
>> +
>> + ret = realm_create_rd(kvm);
>> + return ret;
>> }
>>
>> static int set_ripas_of_protected_regions(struct kvm *kvm)
>> --
>> 2.43.0
>>
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox