Re: [PATCH v15 16/37] KVM: arm64: CCA: Handle realm MMIO emulation

Marc Zyngier <[email protected]> Mon, 03 Aug 2026 12:27:35 +0100
Newsgroups dev.linux.lists.linux-coco,dev.linux.lists.kvmarm,org.infradead.lists.linux-arm-kernel,org.kernel.vger.kvm,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
On Wed, 15 Jul 2026 15:28:18 +0100,
Steven Price <[email protected]> wrote:
> 
> MMIO emulation for a realm cannot be done directly with the VM's
> registers as they are protected from the host. However, for emulatable
> data aborts, the RMM uses GPRS[0] to provide the read/written value.
> We can transfer this from/to the equivalent VCPU's register entry and
> then depend on the generic MMIO handling code in KVM.
> 
> For a MMIO read, the value is placed in the shared RecExit structure
> during kvm_handle_mmio_return() rather than in the VCPU's register
> entry.
> 
> Signed-off-by: Steven Price <[email protected]>
> Reviewed-by: Gavin Shan <[email protected]>
> Reviewed-by: Suzuki K Poulose <[email protected]>
> ---
> Changes since v7:
>  * New comment for rec_exit_sync_dabt() explaining the call to
>    vcpu_set_reg().
> Changes since v5:
>  * Inject SEA to the guest is an emulatable MMIO access triggers a data
>    abort.
>  * kvm_handle_mmio_return() - disable kvm_incr_pc() for a REC (as the PC
>    isn't under the host's control) and move the REC_ENTER_EMULATED_MMIO
>    flag setting to this location (as that tells the RMM to skip the
>    instruction).
> ---
>  arch/arm64/kvm/inject_fault.c |  4 +++-
>  arch/arm64/kvm/mmio.c         | 16 ++++++++++++----
>  arch/arm64/kvm/rmi-exit.c     | 15 +++++++++++++++
>  3 files changed, 30 insertions(+), 5 deletions(-)
> 
> diff --git a/arch/arm64/kvm/inject_fault.c b/arch/arm64/kvm/inject_fault.c
> index 89982bd3345f..6492397b73d7 100644
> --- a/arch/arm64/kvm/inject_fault.c
> +++ b/arch/arm64/kvm/inject_fault.c
> @@ -228,7 +228,9 @@ static void inject_abt32(struct kvm_vcpu *vcpu, bool is_pabt, u32 addr)
>  
>  static void __kvm_inject_sea(struct kvm_vcpu *vcpu, bool iabt, u64 addr)
>  {
> -	if (vcpu_el1_is_32bit(vcpu))
> +	if (unlikely(vcpu_is_rec(vcpu)))
> +		vcpu->arch.rec.run->enter.flags |= REC_ENTER_FLAG_INJECT_SEA;
> +	else if (vcpu_el1_is_32bit(vcpu))
>  		inject_abt32(vcpu, iabt, addr);
>  	else
>  		inject_abt64(vcpu, iabt, addr);

Why don't you let inject_dabt64() do its job and reconcile the REC
stuff at run time?

> diff --git a/arch/arm64/kvm/mmio.c b/arch/arm64/kvm/mmio.c
> index e2285ed8c91d..a8c125205695 100644
> --- a/arch/arm64/kvm/mmio.c
> +++ b/arch/arm64/kvm/mmio.c
> @@ -6,6 +6,7 @@
>  
>  #include <linux/kvm_host.h>
>  #include <asm/kvm_emulate.h>
> +#include <linux/arm-smccc-rmi.h>
>  #include <trace/events/kvm.h>
>  
>  #include "trace.h"
> @@ -138,14 +139,21 @@ int kvm_handle_mmio_return(struct kvm_vcpu *vcpu)
>  		trace_kvm_mmio(KVM_TRACE_MMIO_READ, len, run->mmio.phys_addr,
>  			       &data);
>  		data = vcpu_data_host_to_guest(vcpu, data, len);
> -		vcpu_set_reg(vcpu, kvm_vcpu_dabt_get_rd(vcpu), data);
> +
> +		if (vcpu_is_rec(vcpu))
> +			vcpu->arch.rec.run->enter.gprs[0] = data;
> +		else
> +			vcpu_set_reg(vcpu, kvm_vcpu_dabt_get_rd(vcpu), data);

This is yet another example of things I do not want to see. KVM works
on the GPRs described in the vcpu structure. And that's it.

So let the ESR information be correct for CCA, the data being written
back to x0, and once you're ready to enter the guest again, copy
whatever you need into the CCA data structure.

But not any of this stuff.

>  	}
>  
>  	/*
>  	 * The MMIO instruction is emulated and should not be re-executed
>  	 * in the guest.
>  	 */
> -	kvm_incr_pc(vcpu);
> +	if (vcpu_is_rec(vcpu))
> +		vcpu->arch.rec.run->enter.flags |= REC_ENTER_FLAG_EMULATED_MMIO;
> +	else
> +		kvm_incr_pc(vcpu);

Same thing. kvm_incr_pc() sets a flag. Use that to reconcile the state
with CCA.

	M.

-- 
Without deviation from the norm, progress is not possible.