Hi Khushit
On 9/21/26 2:32 PM, Khushit Shah wrote:
> Named CPU models zero the ID registers QEMU knows about and then apply
> SYSREG_ properties. However, the host and KVM might expose ID
> registers that QEMU does not know about yet. In this scenario, named
this may also happen with reserved fields becoming named, no?
> CPU models might
\n
> leak host features to the guest.
>
> Part of the problem can be addressed because the Arm Architecture
> Reference Manual specifies that unused encodings in the space (op1==0,
> CRn==0, CRm=={2-7}) may be treated as RES0. This range includes all
> ID_AA64* registers.
actually the KVM writable API covers AArch64 System register space

with op0==3, op1=={0, 1, 3}, CRn==0, CRm=={0-7}, op2=={0-7}.
so do you mean you only check part of the space. In the positive don't we need 
to be more precise and check for a given opcode if it is known


>
> Error out if KVM returns a non-zero value for a register in the above
> space which QEMU does not know about yet.
>
> get_sysreg_idx() now returns -1 for encodings that are not known ID
> registers, and kvm_arm_read_sys_reg64() exposes a KVM GET_ONE_REG
> read for this probe.
>
> Signed-off-by: Khushit Shah <[email protected]>
> ---
>  target/arm/cpu-models.c  | 74 ++++++++++++++++++++++++++++++++++++++++
>  target/arm/cpu-sysregs.h |  2 +-
>  target/arm/cpu64.c       |  3 +-
>  target/arm/kvm-stub.c    |  7 ++++
>  target/arm/kvm.c         |  7 ++++
>  target/arm/kvm_arm.h     | 12 +++++++
>  6 files changed, 103 insertions(+), 2 deletions(-)
>
> diff --git a/target/arm/cpu-models.c b/target/arm/cpu-models.c
> index 4e92909755..855eb5ee6b 100644
> --- a/target/arm/cpu-models.c
> +++ b/target/arm/cpu-models.c
> @@ -362,6 +362,78 @@ static void reset_res1_fields(ARMCPU *cpu)
>      }
>  }
>  
> +/*
> + * From ARM ARM:
> + *
> + *     For unused encodings in the part of the space that has op1==0 and
> + *     CRm=={2-7}, direct reads that are not trapped behave as Reserved,
> + *     RES0 to ensure correct behavior if the encodings are used for ID
> + *     registers in the future.
> + *
> + * It includes every ID_AA64* register.
> + *
> + * For named CPU models, we can check if any of the unknown ID registers
> + * that fall into this space is non-zero on the host. If so, the named CPU
> + * model leaks those host features. Error out in such case.
> + *
> + * We cannot do the same for the remaining Feature ID space (op1==0 and
> + * CRm==0, or op1=={1,3}). Unused encodings there are UNDEFINED. The
> + * implemented registers in that space (MIDR_EL1, CTR_EL0, DCZID_EL0,
> + * CCSIDR_EL1, ...) are identification or cache geometry.
> + */
> +static void arm_named_cpu_zero_unknown_feature_ids(ARMCPU *cpu, Error **errp)
> +{
> +    uint32_t init_features0 = 0;
> +    static bool checked;
> +    uint32_t target;
> +    int fdarray[3];
> +    int crm, op2;
> +    int fd;
> +
> +    if (checked) {
> +        return;
> +    }
> +
> +    if (!kvm_arm_create_scratch_max_host_vcpu(fdarray, &init_features0,
> +                                              &target, errp)) {
can't we do that when we retrieve the host features on the 1st created
scratch vcpu. We shall avoid creating too many of them if possible
> +        return;
> +    }
> +
> +    fd = fdarray[2];
> +    for (crm = 2; crm <= 7; crm++) {
> +        for (op2 = 0; op2 <= 7; op2++) {
> +            uint64_t val = 0;
> +            int ret;
> +
> +            if (get_sysreg_idx(ENCODE_ID_REG(3, 0, 0, crm, op2)) != -1) {
> +                continue;
> +            }
> +
> +            ret = kvm_arm_read_sys_reg64(fd, &val, 3, 0, 0, crm, op2);
> +            if (ret) {
> +                continue;
> +            }
> +
> +            /*
> +             * TODO: if this encoding is writable, try to write 0?
> +             */
> +            if (val) {
> +                error_setg(errp,
> +                           "unknown Feature ID register "
> +                           "(op0=3, op1=0, CRn=0, CRm=%d, op2=%d) "
> +                           "reads as 0x%" PRIx64 " on the host; named CPU "
> +                           "models cannot hide it",
> +                           crm, op2, val);
> +                kvm_arm_destroy_scratch_host_vcpu(fdarray);
> +                return;
> +            }
> +        }
> +    }
> +
> +    kvm_arm_destroy_scratch_host_vcpu(fdarray);
> +    checked = true;
> +}
> +
>  static void arm_named_cpu_initfn(Object *obj)
>  {
>      ARMCPU *cpu = ARM_CPU(obj);
> @@ -401,6 +473,8 @@ static void arm_named_cpu_initfn(Object *obj)
>  
>      /* 3. Realize the model chain. */
>      arm_realize_model_chain(obj, model, &error_abort);
> +
> +    arm_named_cpu_zero_unknown_feature_ids(cpu, &error_abort);
>  }
>  
>  void arm_register_named_cpu_models(void)
> diff --git a/target/arm/cpu-sysregs.h b/target/arm/cpu-sysregs.h
> index 7877a3b06a..d48b44705f 100644
> --- a/target/arm/cpu-sysregs.h
> +++ b/target/arm/cpu-sysregs.h
> @@ -37,6 +37,6 @@ typedef enum ARMSysRegs {
>  
>  extern const uint32_t id_register_sysreg[NUM_ID_IDX];
>  
> -int get_sysreg_idx(ARMSysRegs sysreg);
> +int get_sysreg_idx(ARMSysRegs sysreg); /* -1 if @sysreg is not a known ID 
> register */
>  
>  #endif /* ARM_CPU_SYSREGS_H */
> diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c
> index a0114b0fa2..3cc698d65a 100644
> --- a/target/arm/cpu64.c
> +++ b/target/arm/cpu64.c
> @@ -57,8 +57,9 @@ int get_sysreg_idx(ARMSysRegs sysreg)
>  {
>      switch (sysreg) {
>  #include "cpu-sysregs.h.inc"
> +    default:
> +        return -1;
>      }
> -    g_assert_not_reached();
>  }
>  
>  #undef DEF
> diff --git a/target/arm/kvm-stub.c b/target/arm/kvm-stub.c
> index eae2e1ec2c..d263adc921 100644
> --- a/target/arm/kvm-stub.c
> +++ b/target/arm/kvm-stub.c
> @@ -50,6 +50,13 @@ void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu)
>      g_assert_not_reached();
>  }
>  
> +int kvm_arm_read_sys_reg64(int fd, uint64_t *val,
> +                           uint8_t op0, uint8_t op1,
> +                           uint8_t crn, uint8_t crm, uint8_t op2)
> +{
> +    g_assert_not_reached();
> +}
> +
>  void kvm_arm_add_vcpu_properties(ARMCPU *cpu)
>  {
>      g_assert_not_reached();
> diff --git a/target/arm/kvm.c b/target/arm/kvm.c
> index 70d9a07580..6f704c0509 100644
> --- a/target/arm/kvm.c
> +++ b/target/arm/kvm.c
> @@ -263,6 +263,13 @@ static int read_sys_reg64(int fd, uint64_t *pret, 
> uint64_t id)
>      return ioctl(fd, KVM_GET_ONE_REG, &idreg);
>  }
>  
> +int kvm_arm_read_sys_reg64(int fd, uint64_t *val,
> +                           uint8_t op0, uint8_t op1,
> +                           uint8_t crn, uint8_t crm, uint8_t op2)
> +{
> +    return read_sys_reg64(fd, val, ARM64_SYS_REG(op0, op1, crn, crm, op2));
> +}
> +
>  static uint64_t idregs_sysreg_to_kvm_reg(ARMSysRegs sysreg)
>  {
>      return ARM64_SYS_REG((sysreg & CP_REG_ARM64_SYSREG_OP0_MASK) >> 
> CP_REG_ARM64_SYSREG_OP0_SHIFT,
> diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h
> index 997120966f..4e563fab96 100644
> --- a/target/arm/kvm_arm.h
> +++ b/target/arm/kvm_arm.h
> @@ -154,6 +154,18 @@ bool kvm_arm_create_scratch_max_host_vcpu(int *fdarray,
>   */
>  void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu);
>  
> +/**
> + * kvm_arm_read_sys_reg64:
> + * @fd: vCPU fd
> + * @val: filled with the register value on success
> + * @op0/@op1/@crn/@crm/@op2: System ID register encoding
> + *
> + * Returns: 0 on success, -1 on failure.
> + */
> +int kvm_arm_read_sys_reg64(int fd, uint64_t *val,
> +                           uint8_t op0, uint8_t op1,
> +                           uint8_t crn, uint8_t crm, uint8_t op2);
> +
>  /**
>   * kvm_arm_add_vcpu_properties:
>   * @cpu: The CPU object to add the properties to
Thanks

Eric


Reply via email to