Re: [PATCH v7 23/45] KVM: arm64: Validate register access for a Realm VM

Gavin Shan
2025-03-04T05:29:51Z

On 2/14/25 2:14 AM, Steven Price wrote:
> The RMM only allows setting the GPRS (x0-x30) and PC for a realm
> guest. Check this in kvm_arm_set_reg() so that the VMM can receive a
> suitable error return if other registers are accessed.
>
> Signed-off-by: Steven Price <steven.price@arm.com>
> ---
> Changes since v5:
> * Upper GPRS can be set as part of a HOST_CALL return, so fix up the
> test to allow them.
> ---
> arch/arm64/kvm/guest.c | 40 ++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 40 insertions(+)
>

The subject isn't 100% matching with what has been done in this patch.
It's actually to limit the scope for the write operations. The question
is do we need similar limitation for the read operations? If not, it's
nice to explain in the change log :)

> diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
> index 2196979a24a3..ff0306650b39 100644
> --- a/arch/arm64/kvm/guest.c
> +++ b/arch/arm64/kvm/guest.c
> @@ -73,6 +73,24 @@ static u64 core_reg_offset_from_id(u64 id)
> return id & ~(KVM_REG_ARCH_MASK | KVM_REG_SIZE_MASK | KVM_REG_ARM_CORE);
> }
>
> +static bool kvm_realm_validate_core_reg(u64 off)
> +{
> + /*
> + * Note that GPRs can only sometimes be controlled by the VMM.
> + * For PSCI only X0-X6 are used, higher registers are ignored (restored
> + * from the REC).
> + * For HOST_CALL all of X0-X30 are copied to the RsiHostCall structure.
> + * For emulated MMIO X0 is always used.
> + */
> + switch (off) {
> + case KVM_REG_ARM_CORE_REG(regs.regs[0]) ...
> + KVM_REG_ARM_CORE_REG(regs.regs[30]):
> + case KVM_REG_ARM_CORE_REG(regs.pc):
> + return true;
> + }
> + return false;
> +}
> +
> static int core_reg_size_from_offset(const struct kvm_vcpu *vcpu, u64 off)
> {
> int size;
> @@ -783,12 +801,34 @@ int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> return kvm_arm_sys_reg_get_reg(vcpu, reg);
> }
>
> +/*
> + * The RMI ABI only enables setting some GPRs and PC. The selection of GPRs
> + * that are available depends on the Realm state and the reason for the last
> + * exit. All other registers are reset to architectural or otherwise defined
> + * reset values by the RMM, except for a few configuration fields that
> + * correspond to Realm parameters.
> + */
> +static bool validate_realm_set_reg(struct kvm_vcpu *vcpu,
> + const struct kvm_one_reg *reg)
> +{
> + if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_CORE) {
> + u64 off = core_reg_offset_from_id(reg->id);
> +
> + return kvm_realm_validate_core_reg(off);
> + }
> +
> + return false;
> +}
> +
> int kvm_arm_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> {
> /* We currently use nothing arch-specific in upper 32 bits */
> if ((reg->id & ~KVM_REG_SIZE_MASK) >> 32 != KVM_REG_ARM64 >> 32)
> return -EINVAL;
>
> + if (kvm_is_realm(vcpu->kvm) && !validate_realm_set_reg(vcpu, reg))
> + return -EINVAL;
> +
> switch (reg->id & KVM_REG_ARM_COPROC_MASK) {
> case KVM_REG_ARM_CORE: return set_core_reg(vcpu, reg);
> case KVM_REG_ARM_FW:

Thanks,
Gavin