All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH v4 00/26] KVM: arm64: Make CPU ID registers writable by userspace
@ 2022-01-06  4:26 ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

In KVM/arm64, values of ID registers for a guest are mostly same as
its host's values except for bits for feature that KVM doesn't support
and for opt-in features that userspace didn't configure.  Userspace
can use KVM_SET_ONE_REG to a set ID register value, but it fails
if userspace attempts to modify the register value.

This patch series adds support to allow userspace to modify a value of
ID registers (as long as KVM can support features that are indicated
in the registers) so userspace can have more control of configuring
and unconfiguring features for guests.  We need this because we would
like to expose a uniform set/level of features for a group of guests on
systems with different ARM CPUs.  Since some features are not binary
in nature (e.g. AA64DFR0_EL1.BRPs fields indicate number of
breakpoints minus 1), using KVM_ARM_VCPU_INIT to control such features
is inconvenient.

The patch series is for both VHE and non-VHE, except for protected VMs,
which have a different way of configuring ID registers based on its
different requirements [1].
There was a patch series that tried to achieve the same thing [2].
A few snippets of codes in this series were inspired by or came from [2].

The initial value of ID registers for a vCPU will be the host's value
with bits cleared for unsupported features and for opt-in features that
were not configured. So, the initial value userspace can see (via
KVM_GET_ONE_REG) is the upper limit that can be set for the register.
Any requests to change the value that conflicts with opt-in features'
configuration will fail (e.g. if KVM_ARM_VCPU_PMU_V3 is configured by
KVM_ARM_VCPU_INIT, ID_AA64DFR0_EL1.PMUVER cannot be set to zero.
Otherwise, the initial value of ID_AA64DFR0_EL1.PMUVER will be zero,
and cannot be changed from zero).

When a guest tries to use a CPU feature that is not exposed to the guest,
trapping it (to emulate a real CPU's behavior) would generally be a
desirable behavior (when it is possible with no or little side effects).
The later patches in the series add codes for this.  Only features that
can be trapped independently will be trapped by this series though.

This series adds kunit tests for new functions in sys_regs.c (except for
trivial ones), and these tests are enabled with a new configuration
option 'CONFIG_KVM_KUNIT_TEST'.

The series is based on v5.16-rc8 with the patch [3] applied.

v4:
  - Make ID registers storage per VM instead of per vCPU. [Marc]
  - Implement arm64_check_features() in arch/arm64/kernel/cpufeature.c
    by using existing codes in the file. [Marc]
  - Use a configuration function to enable traps for disabled
    features. [Marc]
  - Document ID registers become immutable after the first KVM_RUN [Eric]
  - Update ID_AA64PFR0.GIC at the point where a GICv3 is created. [Marc]
  - Get TGranX's bit position by substracting 12 from TGranX_2's bit
    position. [Eric]
  - Don't validate AArch32 ID registers when the system doesn't support
    32bit EL0. [Eric]
  - Add/fixes comments for patches. [Eric]
  - Made bug fixes/improvements of the selftest. [Eric]
  - Added .kunitconfig for arm64 KUnit tests

v3: https://lore.kernel.org/all/20211117064359.2362060-1-reijiw@google.com/
  - Remove ID register consistency checking across vCPUs. [Oliver]
  - Change KVM_CAP_ARM_ID_REG_WRITABLE to
    KVM_CAP_ARM_ID_REG_CONFIGURABLE. [Oliver]
  - Add KUnit testing for ID register validation and trap initialization.
  - Change read_id_reg() to take care of ID_AA64PFR0_EL1.GIC.
  - Add a helper of read_id_reg() (__read_id_reg()) and use the helper
    instead of directly using __vcpu_sys_reg().
  - Change not to run kvm_id_regs_consistency_check() and
    kvm_vcpu_init_traps() for protected VMs.
  - Update selftest to remove test cases for ID register consistency.
    checking across vCPUs and to add test cases for ID_AA64PFR0_EL1.GIC.

v2: https://lore.kernel.org/all/20211103062520.1445832-1-reijiw@google.com/
  - Remove unnecessary line breaks. [Andrew]
  - Use @params for comments. [Andrew]
  - Move arm64_check_features to arch/arm64/kvm/sys_regs.c and
    change that KVM specific feature check function.  [Andrew]
  - Remove unnecessary raz handling from __set_id_reg. [Andrew]
  - Remove sys_val field from the initial id_reg_info and add it
    in the later patch. [Andrew]
  - Call id_reg->init() from id_reg_info_init(). [Andrew]
  - Fix cpuid_feature_cap_perfmon_field() to convert 0xf to 0x0
    (and use it in the following patches).
  - Change kvm_vcpu_first_run_init to set has_run_once to false
    when kvm_id_regs_consistency_check() fails.
  - Add a patch to introduce id_reg_info for ID_AA64MMFR0_EL1,
    which requires special validity checking for TGran*_2 fields.
  - Add patches to introduce id_reg_info for ID_DFR1_EL1 and
    ID_MMFR0_EL1, which are required due to arm64_check_features
    implementation change.
  - Add a new argument, which is a pointer to id_reg_info, for
    id_reg_info's validate().

v1: https://lore.kernel.org/all/20211012043535.500493-1-reijiw@google.com/

[1] https://lore.kernel.org/kvmarm/20211010145636.1950948-1-tabba@google.com/
[2] https://lore.kernel.org/kvm/20201102033422.657391-1-liangpeng10@huawei.com/
[3] https://lore.kernel.org/all/20220104194918.373612-2-rananta@google.com/

Reiji Watanabe (26):
  KVM: arm64: Introduce a validation function for an ID register
  KVM: arm64: Save ID registers' sanitized value per guest
  KVM: arm64: Introduce struct id_reg_info
  KVM: arm64: Make ID_AA64PFR0_EL1 writable
  KVM: arm64: Make ID_AA64PFR1_EL1 writable
  KVM: arm64: Make ID_AA64ISAR0_EL1 writable
  KVM: arm64: Make ID_AA64ISAR1_EL1 writable
  KVM: arm64: Make ID_AA64MMFR0_EL1 writable
  KVM: arm64: Hide IMPLEMENTATION DEFINED PMU support for the guest
  KVM: arm64: Make ID_AA64DFR0_EL1 writable
  KVM: arm64: Make ID_DFR0_EL1 writable
  KVM: arm64: Make MVFR1_EL1 writable
  KVM: arm64: Make ID registers without id_reg_info writable
  KVM: arm64: Add consistency checking for frac fields of ID registers
  KVM: arm64: Introduce KVM_CAP_ARM_ID_REG_CONFIGURABLE capability
  KVM: arm64: Add kunit test for ID register validation
  KVM: arm64: Use vcpu->arch cptr_el2 to track value of cptr_el2 for VHE
  KVM: arm64: Use vcpu->arch.mdcr_el2 to track value of mdcr_el2
  KVM: arm64: Introduce framework to trap disabled features
  KVM: arm64: Trap disabled features of ID_AA64PFR0_EL1
  KVM: arm64: Trap disabled features of ID_AA64PFR1_EL1
  KVM: arm64: Trap disabled features of ID_AA64DFR0_EL1
  KVM: arm64: Trap disabled features of ID_AA64MMFR1_EL1
  KVM: arm64: Trap disabled features of ID_AA64ISAR1_EL1
  KVM: arm64: Add kunit test for trap initialization
  KVM: arm64: selftests: Introduce id_reg_test

 Documentation/virt/kvm/api.rst                |   12 +
 arch/arm64/include/asm/cpufeature.h           |    3 +-
 arch/arm64/include/asm/kvm_arm.h              |   32 +
 arch/arm64/include/asm/kvm_host.h             |   19 +
 arch/arm64/include/asm/sysreg.h               |    3 +
 arch/arm64/kernel/cpufeature.c                |  228 +++
 arch/arm64/kvm/.kunitconfig                   |    4 +
 arch/arm64/kvm/Kconfig                        |   11 +
 arch/arm64/kvm/arm.c                          |   24 +-
 arch/arm64/kvm/debug.c                        |   13 +-
 arch/arm64/kvm/hyp/vhe/switch.c               |   14 +-
 arch/arm64/kvm/sys_regs.c                     | 1329 +++++++++++++++--
 arch/arm64/kvm/sys_regs_test.c                | 1247 ++++++++++++++++
 arch/arm64/kvm/vgic/vgic-init.c               |    5 +
 include/uapi/linux/kvm.h                      |    1 +
 tools/arch/arm64/include/asm/sysreg.h         |    1 +
 tools/testing/selftests/kvm/.gitignore        |    1 +
 tools/testing/selftests/kvm/Makefile          |    1 +
 .../selftests/kvm/aarch64/id_reg_test.c       | 1239 +++++++++++++++
 19 files changed, 4041 insertions(+), 146 deletions(-)
 create mode 100644 arch/arm64/kvm/.kunitconfig
 create mode 100644 arch/arm64/kvm/sys_regs_test.c
 create mode 100644 tools/testing/selftests/kvm/aarch64/id_reg_test.c


base-commit: d399b107ee49bf5ea0391bd7614d512809e927b0
-- 
2.34.1.448.ga2b2bfdf31-goog


^ permalink raw reply	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 00/26] KVM: arm64: Make CPU ID registers writable by userspace
@ 2022-01-06  4:26 ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, Will Deacon, Peter Shier, Paolo Bonzini, linux-arm-kernel

In KVM/arm64, values of ID registers for a guest are mostly same as
its host's values except for bits for feature that KVM doesn't support
and for opt-in features that userspace didn't configure.  Userspace
can use KVM_SET_ONE_REG to a set ID register value, but it fails
if userspace attempts to modify the register value.

This patch series adds support to allow userspace to modify a value of
ID registers (as long as KVM can support features that are indicated
in the registers) so userspace can have more control of configuring
and unconfiguring features for guests.  We need this because we would
like to expose a uniform set/level of features for a group of guests on
systems with different ARM CPUs.  Since some features are not binary
in nature (e.g. AA64DFR0_EL1.BRPs fields indicate number of
breakpoints minus 1), using KVM_ARM_VCPU_INIT to control such features
is inconvenient.

The patch series is for both VHE and non-VHE, except for protected VMs,
which have a different way of configuring ID registers based on its
different requirements [1].
There was a patch series that tried to achieve the same thing [2].
A few snippets of codes in this series were inspired by or came from [2].

The initial value of ID registers for a vCPU will be the host's value
with bits cleared for unsupported features and for opt-in features that
were not configured. So, the initial value userspace can see (via
KVM_GET_ONE_REG) is the upper limit that can be set for the register.
Any requests to change the value that conflicts with opt-in features'
configuration will fail (e.g. if KVM_ARM_VCPU_PMU_V3 is configured by
KVM_ARM_VCPU_INIT, ID_AA64DFR0_EL1.PMUVER cannot be set to zero.
Otherwise, the initial value of ID_AA64DFR0_EL1.PMUVER will be zero,
and cannot be changed from zero).

When a guest tries to use a CPU feature that is not exposed to the guest,
trapping it (to emulate a real CPU's behavior) would generally be a
desirable behavior (when it is possible with no or little side effects).
The later patches in the series add codes for this.  Only features that
can be trapped independently will be trapped by this series though.

This series adds kunit tests for new functions in sys_regs.c (except for
trivial ones), and these tests are enabled with a new configuration
option 'CONFIG_KVM_KUNIT_TEST'.

The series is based on v5.16-rc8 with the patch [3] applied.

v4:
  - Make ID registers storage per VM instead of per vCPU. [Marc]
  - Implement arm64_check_features() in arch/arm64/kernel/cpufeature.c
    by using existing codes in the file. [Marc]
  - Use a configuration function to enable traps for disabled
    features. [Marc]
  - Document ID registers become immutable after the first KVM_RUN [Eric]
  - Update ID_AA64PFR0.GIC at the point where a GICv3 is created. [Marc]
  - Get TGranX's bit position by substracting 12 from TGranX_2's bit
    position. [Eric]
  - Don't validate AArch32 ID registers when the system doesn't support
    32bit EL0. [Eric]
  - Add/fixes comments for patches. [Eric]
  - Made bug fixes/improvements of the selftest. [Eric]
  - Added .kunitconfig for arm64 KUnit tests

v3: https://lore.kernel.org/all/20211117064359.2362060-1-reijiw@google.com/
  - Remove ID register consistency checking across vCPUs. [Oliver]
  - Change KVM_CAP_ARM_ID_REG_WRITABLE to
    KVM_CAP_ARM_ID_REG_CONFIGURABLE. [Oliver]
  - Add KUnit testing for ID register validation and trap initialization.
  - Change read_id_reg() to take care of ID_AA64PFR0_EL1.GIC.
  - Add a helper of read_id_reg() (__read_id_reg()) and use the helper
    instead of directly using __vcpu_sys_reg().
  - Change not to run kvm_id_regs_consistency_check() and
    kvm_vcpu_init_traps() for protected VMs.
  - Update selftest to remove test cases for ID register consistency.
    checking across vCPUs and to add test cases for ID_AA64PFR0_EL1.GIC.

v2: https://lore.kernel.org/all/20211103062520.1445832-1-reijiw@google.com/
  - Remove unnecessary line breaks. [Andrew]
  - Use @params for comments. [Andrew]
  - Move arm64_check_features to arch/arm64/kvm/sys_regs.c and
    change that KVM specific feature check function.  [Andrew]
  - Remove unnecessary raz handling from __set_id_reg. [Andrew]
  - Remove sys_val field from the initial id_reg_info and add it
    in the later patch. [Andrew]
  - Call id_reg->init() from id_reg_info_init(). [Andrew]
  - Fix cpuid_feature_cap_perfmon_field() to convert 0xf to 0x0
    (and use it in the following patches).
  - Change kvm_vcpu_first_run_init to set has_run_once to false
    when kvm_id_regs_consistency_check() fails.
  - Add a patch to introduce id_reg_info for ID_AA64MMFR0_EL1,
    which requires special validity checking for TGran*_2 fields.
  - Add patches to introduce id_reg_info for ID_DFR1_EL1 and
    ID_MMFR0_EL1, which are required due to arm64_check_features
    implementation change.
  - Add a new argument, which is a pointer to id_reg_info, for
    id_reg_info's validate().

v1: https://lore.kernel.org/all/20211012043535.500493-1-reijiw@google.com/

[1] https://lore.kernel.org/kvmarm/20211010145636.1950948-1-tabba@google.com/
[2] https://lore.kernel.org/kvm/20201102033422.657391-1-liangpeng10@huawei.com/
[3] https://lore.kernel.org/all/20220104194918.373612-2-rananta@google.com/

Reiji Watanabe (26):
  KVM: arm64: Introduce a validation function for an ID register
  KVM: arm64: Save ID registers' sanitized value per guest
  KVM: arm64: Introduce struct id_reg_info
  KVM: arm64: Make ID_AA64PFR0_EL1 writable
  KVM: arm64: Make ID_AA64PFR1_EL1 writable
  KVM: arm64: Make ID_AA64ISAR0_EL1 writable
  KVM: arm64: Make ID_AA64ISAR1_EL1 writable
  KVM: arm64: Make ID_AA64MMFR0_EL1 writable
  KVM: arm64: Hide IMPLEMENTATION DEFINED PMU support for the guest
  KVM: arm64: Make ID_AA64DFR0_EL1 writable
  KVM: arm64: Make ID_DFR0_EL1 writable
  KVM: arm64: Make MVFR1_EL1 writable
  KVM: arm64: Make ID registers without id_reg_info writable
  KVM: arm64: Add consistency checking for frac fields of ID registers
  KVM: arm64: Introduce KVM_CAP_ARM_ID_REG_CONFIGURABLE capability
  KVM: arm64: Add kunit test for ID register validation
  KVM: arm64: Use vcpu->arch cptr_el2 to track value of cptr_el2 for VHE
  KVM: arm64: Use vcpu->arch.mdcr_el2 to track value of mdcr_el2
  KVM: arm64: Introduce framework to trap disabled features
  KVM: arm64: Trap disabled features of ID_AA64PFR0_EL1
  KVM: arm64: Trap disabled features of ID_AA64PFR1_EL1
  KVM: arm64: Trap disabled features of ID_AA64DFR0_EL1
  KVM: arm64: Trap disabled features of ID_AA64MMFR1_EL1
  KVM: arm64: Trap disabled features of ID_AA64ISAR1_EL1
  KVM: arm64: Add kunit test for trap initialization
  KVM: arm64: selftests: Introduce id_reg_test

 Documentation/virt/kvm/api.rst                |   12 +
 arch/arm64/include/asm/cpufeature.h           |    3 +-
 arch/arm64/include/asm/kvm_arm.h              |   32 +
 arch/arm64/include/asm/kvm_host.h             |   19 +
 arch/arm64/include/asm/sysreg.h               |    3 +
 arch/arm64/kernel/cpufeature.c                |  228 +++
 arch/arm64/kvm/.kunitconfig                   |    4 +
 arch/arm64/kvm/Kconfig                        |   11 +
 arch/arm64/kvm/arm.c                          |   24 +-
 arch/arm64/kvm/debug.c                        |   13 +-
 arch/arm64/kvm/hyp/vhe/switch.c               |   14 +-
 arch/arm64/kvm/sys_regs.c                     | 1329 +++++++++++++++--
 arch/arm64/kvm/sys_regs_test.c                | 1247 ++++++++++++++++
 arch/arm64/kvm/vgic/vgic-init.c               |    5 +
 include/uapi/linux/kvm.h                      |    1 +
 tools/arch/arm64/include/asm/sysreg.h         |    1 +
 tools/testing/selftests/kvm/.gitignore        |    1 +
 tools/testing/selftests/kvm/Makefile          |    1 +
 .../selftests/kvm/aarch64/id_reg_test.c       | 1239 +++++++++++++++
 19 files changed, 4041 insertions(+), 146 deletions(-)
 create mode 100644 arch/arm64/kvm/.kunitconfig
 create mode 100644 arch/arm64/kvm/sys_regs_test.c
 create mode 100644 tools/testing/selftests/kvm/aarch64/id_reg_test.c


base-commit: d399b107ee49bf5ea0391bd7614d512809e927b0
-- 
2.34.1.448.ga2b2bfdf31-goog

_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 00/26] KVM: arm64: Make CPU ID registers writable by userspace
@ 2022-01-06  4:26 ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

In KVM/arm64, values of ID registers for a guest are mostly same as
its host's values except for bits for feature that KVM doesn't support
and for opt-in features that userspace didn't configure.  Userspace
can use KVM_SET_ONE_REG to a set ID register value, but it fails
if userspace attempts to modify the register value.

This patch series adds support to allow userspace to modify a value of
ID registers (as long as KVM can support features that are indicated
in the registers) so userspace can have more control of configuring
and unconfiguring features for guests.  We need this because we would
like to expose a uniform set/level of features for a group of guests on
systems with different ARM CPUs.  Since some features are not binary
in nature (e.g. AA64DFR0_EL1.BRPs fields indicate number of
breakpoints minus 1), using KVM_ARM_VCPU_INIT to control such features
is inconvenient.

The patch series is for both VHE and non-VHE, except for protected VMs,
which have a different way of configuring ID registers based on its
different requirements [1].
There was a patch series that tried to achieve the same thing [2].
A few snippets of codes in this series were inspired by or came from [2].

The initial value of ID registers for a vCPU will be the host's value
with bits cleared for unsupported features and for opt-in features that
were not configured. So, the initial value userspace can see (via
KVM_GET_ONE_REG) is the upper limit that can be set for the register.
Any requests to change the value that conflicts with opt-in features'
configuration will fail (e.g. if KVM_ARM_VCPU_PMU_V3 is configured by
KVM_ARM_VCPU_INIT, ID_AA64DFR0_EL1.PMUVER cannot be set to zero.
Otherwise, the initial value of ID_AA64DFR0_EL1.PMUVER will be zero,
and cannot be changed from zero).

When a guest tries to use a CPU feature that is not exposed to the guest,
trapping it (to emulate a real CPU's behavior) would generally be a
desirable behavior (when it is possible with no or little side effects).
The later patches in the series add codes for this.  Only features that
can be trapped independently will be trapped by this series though.

This series adds kunit tests for new functions in sys_regs.c (except for
trivial ones), and these tests are enabled with a new configuration
option 'CONFIG_KVM_KUNIT_TEST'.

The series is based on v5.16-rc8 with the patch [3] applied.

v4:
  - Make ID registers storage per VM instead of per vCPU. [Marc]
  - Implement arm64_check_features() in arch/arm64/kernel/cpufeature.c
    by using existing codes in the file. [Marc]
  - Use a configuration function to enable traps for disabled
    features. [Marc]
  - Document ID registers become immutable after the first KVM_RUN [Eric]
  - Update ID_AA64PFR0.GIC at the point where a GICv3 is created. [Marc]
  - Get TGranX's bit position by substracting 12 from TGranX_2's bit
    position. [Eric]
  - Don't validate AArch32 ID registers when the system doesn't support
    32bit EL0. [Eric]
  - Add/fixes comments for patches. [Eric]
  - Made bug fixes/improvements of the selftest. [Eric]
  - Added .kunitconfig for arm64 KUnit tests

v3: https://lore.kernel.org/all/20211117064359.2362060-1-reijiw@google.com/
  - Remove ID register consistency checking across vCPUs. [Oliver]
  - Change KVM_CAP_ARM_ID_REG_WRITABLE to
    KVM_CAP_ARM_ID_REG_CONFIGURABLE. [Oliver]
  - Add KUnit testing for ID register validation and trap initialization.
  - Change read_id_reg() to take care of ID_AA64PFR0_EL1.GIC.
  - Add a helper of read_id_reg() (__read_id_reg()) and use the helper
    instead of directly using __vcpu_sys_reg().
  - Change not to run kvm_id_regs_consistency_check() and
    kvm_vcpu_init_traps() for protected VMs.
  - Update selftest to remove test cases for ID register consistency.
    checking across vCPUs and to add test cases for ID_AA64PFR0_EL1.GIC.

v2: https://lore.kernel.org/all/20211103062520.1445832-1-reijiw@google.com/
  - Remove unnecessary line breaks. [Andrew]
  - Use @params for comments. [Andrew]
  - Move arm64_check_features to arch/arm64/kvm/sys_regs.c and
    change that KVM specific feature check function.  [Andrew]
  - Remove unnecessary raz handling from __set_id_reg. [Andrew]
  - Remove sys_val field from the initial id_reg_info and add it
    in the later patch. [Andrew]
  - Call id_reg->init() from id_reg_info_init(). [Andrew]
  - Fix cpuid_feature_cap_perfmon_field() to convert 0xf to 0x0
    (and use it in the following patches).
  - Change kvm_vcpu_first_run_init to set has_run_once to false
    when kvm_id_regs_consistency_check() fails.
  - Add a patch to introduce id_reg_info for ID_AA64MMFR0_EL1,
    which requires special validity checking for TGran*_2 fields.
  - Add patches to introduce id_reg_info for ID_DFR1_EL1 and
    ID_MMFR0_EL1, which are required due to arm64_check_features
    implementation change.
  - Add a new argument, which is a pointer to id_reg_info, for
    id_reg_info's validate().

v1: https://lore.kernel.org/all/20211012043535.500493-1-reijiw@google.com/

[1] https://lore.kernel.org/kvmarm/20211010145636.1950948-1-tabba@google.com/
[2] https://lore.kernel.org/kvm/20201102033422.657391-1-liangpeng10@huawei.com/
[3] https://lore.kernel.org/all/20220104194918.373612-2-rananta@google.com/

Reiji Watanabe (26):
  KVM: arm64: Introduce a validation function for an ID register
  KVM: arm64: Save ID registers' sanitized value per guest
  KVM: arm64: Introduce struct id_reg_info
  KVM: arm64: Make ID_AA64PFR0_EL1 writable
  KVM: arm64: Make ID_AA64PFR1_EL1 writable
  KVM: arm64: Make ID_AA64ISAR0_EL1 writable
  KVM: arm64: Make ID_AA64ISAR1_EL1 writable
  KVM: arm64: Make ID_AA64MMFR0_EL1 writable
  KVM: arm64: Hide IMPLEMENTATION DEFINED PMU support for the guest
  KVM: arm64: Make ID_AA64DFR0_EL1 writable
  KVM: arm64: Make ID_DFR0_EL1 writable
  KVM: arm64: Make MVFR1_EL1 writable
  KVM: arm64: Make ID registers without id_reg_info writable
  KVM: arm64: Add consistency checking for frac fields of ID registers
  KVM: arm64: Introduce KVM_CAP_ARM_ID_REG_CONFIGURABLE capability
  KVM: arm64: Add kunit test for ID register validation
  KVM: arm64: Use vcpu->arch cptr_el2 to track value of cptr_el2 for VHE
  KVM: arm64: Use vcpu->arch.mdcr_el2 to track value of mdcr_el2
  KVM: arm64: Introduce framework to trap disabled features
  KVM: arm64: Trap disabled features of ID_AA64PFR0_EL1
  KVM: arm64: Trap disabled features of ID_AA64PFR1_EL1
  KVM: arm64: Trap disabled features of ID_AA64DFR0_EL1
  KVM: arm64: Trap disabled features of ID_AA64MMFR1_EL1
  KVM: arm64: Trap disabled features of ID_AA64ISAR1_EL1
  KVM: arm64: Add kunit test for trap initialization
  KVM: arm64: selftests: Introduce id_reg_test

 Documentation/virt/kvm/api.rst                |   12 +
 arch/arm64/include/asm/cpufeature.h           |    3 +-
 arch/arm64/include/asm/kvm_arm.h              |   32 +
 arch/arm64/include/asm/kvm_host.h             |   19 +
 arch/arm64/include/asm/sysreg.h               |    3 +
 arch/arm64/kernel/cpufeature.c                |  228 +++
 arch/arm64/kvm/.kunitconfig                   |    4 +
 arch/arm64/kvm/Kconfig                        |   11 +
 arch/arm64/kvm/arm.c                          |   24 +-
 arch/arm64/kvm/debug.c                        |   13 +-
 arch/arm64/kvm/hyp/vhe/switch.c               |   14 +-
 arch/arm64/kvm/sys_regs.c                     | 1329 +++++++++++++++--
 arch/arm64/kvm/sys_regs_test.c                | 1247 ++++++++++++++++
 arch/arm64/kvm/vgic/vgic-init.c               |    5 +
 include/uapi/linux/kvm.h                      |    1 +
 tools/arch/arm64/include/asm/sysreg.h         |    1 +
 tools/testing/selftests/kvm/.gitignore        |    1 +
 tools/testing/selftests/kvm/Makefile          |    1 +
 .../selftests/kvm/aarch64/id_reg_test.c       | 1239 +++++++++++++++
 19 files changed, 4041 insertions(+), 146 deletions(-)
 create mode 100644 arch/arm64/kvm/.kunitconfig
 create mode 100644 arch/arm64/kvm/sys_regs_test.c
 create mode 100644 tools/testing/selftests/kvm/aarch64/id_reg_test.c


base-commit: d399b107ee49bf5ea0391bd7614d512809e927b0
-- 
2.34.1.448.ga2b2bfdf31-goog


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 01/26] KVM: arm64: Introduce a validation function for an ID register
  2022-01-06  4:26 ` Reiji Watanabe
  (?)
@ 2022-01-06  4:26   ` Reiji Watanabe
  -1 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

Introduce arm64_check_features(), which does a basic validity checking
of an ID register value against the register's limit value, which is
generally the host's sanitized value.

This function will be used by the following patches to check if an ID
register value that userspace tries to set for a guest can be supported
on the host.

The validation is done using arm64_ftr_bits_kvm, which is created from
arm64_ftr_regs, with some entries overwritten by entries from
arm64_ftr_bits_kvm_override.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/include/asm/cpufeature.h |   1 +
 arch/arm64/kernel/cpufeature.c      | 228 ++++++++++++++++++++++++++++
 2 files changed, 229 insertions(+)

diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
index ef6be92b1921..eda7ddbed8cf 100644
--- a/arch/arm64/include/asm/cpufeature.h
+++ b/arch/arm64/include/asm/cpufeature.h
@@ -631,6 +631,7 @@ void check_local_cpu_capabilities(void);
 
 u64 read_sanitised_ftr_reg(u32 id);
 u64 __read_sysreg_by_encoding(u32 sys_id);
+int arm64_check_features(u32 sys_reg, u64 val, u64 limit);
 
 static inline bool cpu_supports_mixed_endian_el0(void)
 {
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index 6f3e677d88f1..48dff8b101d9 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -3140,3 +3140,231 @@ ssize_t cpu_show_meltdown(struct device *dev, struct device_attribute *attr,
 		return sprintf(buf, "Vulnerable\n");
 	}
 }
+
+#ifdef CONFIG_KVM
+/*
+ * arm64_ftr_bits_kvm[] is used for KVM to check if features that are
+ * indicated in an ID register value for the guest are available on the host.
+ * arm64_ftr_bits_kvm[] is created based on arm64_ftr_regs[].  But, for
+ * registers for which arm64_ftr_bits_kvm_override[] has a corresponding
+ * entry, replace arm64_ftr_bits entries in arm64_ftr_bits_kvm[] with the
+ * ones in arm64_ftr_bits_kvm_override[].
+ */
+static struct __ftr_reg_bits_entry *arm64_ftr_bits_kvm;
+static size_t arm64_ftr_bits_kvm_nentries;
+static DEFINE_MUTEX(arm64_ftr_bits_kvm_lock);
+
+/*
+ * Number of arm64_ftr_bits entries for each register.
+ * (Number of 4 bits fields in 64 bit register + 1 entry for ARM64_FTR_END)
+ */
+#define	MAX_FTR_BITS_LEN	17
+
+/* Use FTR_LOWER_SAFE for AA64DFR0_EL1.PMUVER and AA64DFR0_EL1.DEBUGVER. */
+static struct arm64_ftr_bits ftr_id_aa64dfr0_kvm[MAX_FTR_BITS_LEN] = {
+	S_ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64DFR0_PMUVER_SHIFT, 4, 0),
+	ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x6),
+	ARM64_FTR_END,
+};
+
+#define	ARM64_FTR_REG_BITS(id, table)	{	\
+	.sys_id = id,				\
+	.ftr_bits = &((table)[0]),		\
+}
+
+struct __ftr_reg_bits_entry {
+	u32	sys_id;
+	struct arm64_ftr_bits	*ftr_bits;
+};
+
+/*
+ * All entries in arm64_ftr_bits_kvm_override[] are used to override
+ * the corresponding entries in arm64_ftr_bits_kvm[].
+ */
+static struct __ftr_reg_bits_entry arm64_ftr_bits_kvm_override[] = {
+	ARM64_FTR_REG_BITS(SYS_ID_AA64DFR0_EL1, ftr_id_aa64dfr0_kvm),
+};
+
+/*
+ * Override entries in @orig_ftrp with the ones in @new_ftrp when their shift
+ * fields match.  The last entry of @orig_ftrp and @new_ftrp must be
+ * ARM64_FTR_END (.width == 0).
+ */
+static void arm64_ftr_reg_bits_overrite(struct arm64_ftr_bits *orig_ftrp,
+					struct arm64_ftr_bits *new_ftrp)
+{
+	struct arm64_ftr_bits *o_ftrp, *n_ftrp;
+
+	for (n_ftrp = new_ftrp; n_ftrp->width; n_ftrp++) {
+		for (o_ftrp = orig_ftrp; o_ftrp->width; o_ftrp++) {
+			if (o_ftrp->shift == n_ftrp->shift) {
+				*o_ftrp = *n_ftrp;
+				break;
+			}
+		}
+	}
+}
+
+/*
+ * Copy arm64_ftr_bits entries from @src_ftrp to @dst_ftrp.  The last entries
+ * of @dst_ftrp and @src_ftrp must be ARM64_FTR_END (.width == 0).
+ */
+static void copy_arm64_ftr_bits(struct arm64_ftr_bits *dst_ftrp,
+				const struct arm64_ftr_bits *src_ftrp)
+{
+	int i = 0;
+
+	for (; src_ftrp[i].width; i++) {
+		if (WARN_ON_ONCE(i >= (MAX_FTR_BITS_LEN - 1)))
+			break;
+
+		dst_ftrp[i] = src_ftrp[i];
+	}
+
+	dst_ftrp[i].width = 0;
+}
+
+/*
+ * Initialize arm64_ftr_bits_kvm.  Copy arm64_ftr_bits for each ID register
+ * from arm64_ftr_regs to arm64_ftr_bits_kvm, and then override entries in
+ * arm64_ftr_bits_kvm with ones in arm64_ftr_bits_kvm_override.
+ */
+static int init_arm64_ftr_bits_kvm(void)
+{
+	struct arm64_ftr_bits ftr_temp[MAX_FTR_BITS_LEN];
+	static struct __ftr_reg_bits_entry *reg_bits_array, *bits, *o_bits;
+	int i, j, nent, ret;
+
+	mutex_lock(&arm64_ftr_bits_kvm_lock);
+	if (arm64_ftr_bits_kvm) {
+		/* Already initialized */
+		ret = 0;
+		goto unlock_exit;
+	}
+
+	nent = ARRAY_SIZE(arm64_ftr_regs);
+	reg_bits_array = kcalloc(nent, sizeof(struct __ftr_reg_bits_entry),
+				 GFP_KERNEL);
+	if (!reg_bits_array) {
+		ret = ENOMEM;
+		goto unlock_exit;
+	}
+
+	/* Copy entries from arm64_ftr_regs to reg_bits_array */
+	for (i = 0; i < nent; i++) {
+		bits = &reg_bits_array[i];
+		bits->sys_id = arm64_ftr_regs[i].sys_id;
+		bits->ftr_bits = (struct arm64_ftr_bits *)arm64_ftr_regs[i].reg->ftr_bits;
+	};
+
+	/*
+	 * Override the entries in reg_bits_array with the ones in
+	 * arm64_ftr_bits_kvm_override.
+	 */
+	for (i = 0; i < ARRAY_SIZE(arm64_ftr_bits_kvm_override); i++) {
+		o_bits = &arm64_ftr_bits_kvm_override[i];
+		for (j = 0; j < nent; j++) {
+			bits = &reg_bits_array[j];
+			if (bits->sys_id != o_bits->sys_id)
+				continue;
+
+			memset(ftr_temp, 0, sizeof(ftr_temp));
+
+			/*
+			 * Temporary save all entries in o_bits->ftr_bits
+			 * to ftr_temp.
+			 */
+			copy_arm64_ftr_bits(ftr_temp, o_bits->ftr_bits);
+
+			/*
+			 * Copy entries from bits->ftr_bits to o_bits->ftr_bits.
+			 */
+			copy_arm64_ftr_bits(o_bits->ftr_bits, bits->ftr_bits);
+
+			/*
+			 * Override entries in o_bits->ftr_bits with the
+			 * saved ones, and update bits->ftr_bits with
+			 * o_bits->ftr_bits.
+			 */
+			arm64_ftr_reg_bits_overrite(o_bits->ftr_bits, ftr_temp);
+			bits->ftr_bits = o_bits->ftr_bits;
+			break;
+		}
+	}
+
+	arm64_ftr_bits_kvm_nentries = nent;
+	arm64_ftr_bits_kvm = reg_bits_array;
+	ret = 0;
+
+unlock_exit:
+	mutex_unlock(&arm64_ftr_bits_kvm_lock);
+	return ret;
+}
+
+static int search_cmp_ftr_reg_bits(const void *id, const void *regp)
+{
+	return ((int)(unsigned long)id -
+		(int)((const struct __ftr_reg_bits_entry *)regp)->sys_id);
+}
+
+static const struct arm64_ftr_bits *get_arm64_ftr_bits_kvm(u32 sys_id)
+{
+	const struct __ftr_reg_bits_entry *ret;
+	int err;
+
+	if (!arm64_ftr_bits_kvm) {
+		/* arm64_ftr_bits_kvm is not initialized yet. */
+		err = init_arm64_ftr_bits_kvm();
+		if (err)
+			return NULL;
+	}
+
+	ret = bsearch((const void *)(unsigned long)sys_id,
+		      arm64_ftr_bits_kvm,
+		      arm64_ftr_bits_kvm_nentries,
+		      sizeof(arm64_ftr_bits_kvm[0]),
+		      search_cmp_ftr_reg_bits);
+	if (ret)
+		return ret->ftr_bits;
+
+	return NULL;
+}
+
+/*
+ * Check if features (or levels of features) that are indicated in the ID
+ * register value @val are also indicated in @limit.
+ * This function is for KVM to check if features that are indicated in @val,
+ * which will be used as the ID register value for its guest, are supported
+ * on the host.
+ * For AA64MMFR0_EL1.TGranX_2 fields, which don't follow the standard ID
+ * scheme, the function checks if values of the fields in @val are the same
+ * as the ones in @limit.
+ */
+int arm64_check_features(u32 sys_reg, u64 val, u64 limit)
+{
+	const struct arm64_ftr_bits *ftrp = get_arm64_ftr_bits_kvm(sys_reg);
+	u64 exposed_mask = 0;
+
+	if (!ftrp)
+		return -ENOENT;
+
+	for (; ftrp->width; ftrp++) {
+		s64 ftr_val = arm64_ftr_value(ftrp, val);
+		s64 ftr_lim = arm64_ftr_value(ftrp, limit);
+
+		exposed_mask |= arm64_ftr_mask(ftrp);
+
+		if (ftr_val == ftr_lim)
+			continue;
+
+		if (ftr_val != arm64_ftr_safe_value(ftrp, ftr_val, ftr_lim))
+			return -E2BIG;
+	}
+
+	/* Make sure that no unrecognized fields are set in @val. */
+	if (val & ~exposed_mask)
+		return -E2BIG;
+
+	return 0;
+}
+#endif /* CONFIG_KVM */
-- 
2.34.1.448.ga2b2bfdf31-goog


^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 01/26] KVM: arm64: Introduce a validation function for an ID register
@ 2022-01-06  4:26   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, Will Deacon, Peter Shier, Paolo Bonzini, linux-arm-kernel

Introduce arm64_check_features(), which does a basic validity checking
of an ID register value against the register's limit value, which is
generally the host's sanitized value.

This function will be used by the following patches to check if an ID
register value that userspace tries to set for a guest can be supported
on the host.

The validation is done using arm64_ftr_bits_kvm, which is created from
arm64_ftr_regs, with some entries overwritten by entries from
arm64_ftr_bits_kvm_override.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/include/asm/cpufeature.h |   1 +
 arch/arm64/kernel/cpufeature.c      | 228 ++++++++++++++++++++++++++++
 2 files changed, 229 insertions(+)

diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
index ef6be92b1921..eda7ddbed8cf 100644
--- a/arch/arm64/include/asm/cpufeature.h
+++ b/arch/arm64/include/asm/cpufeature.h
@@ -631,6 +631,7 @@ void check_local_cpu_capabilities(void);
 
 u64 read_sanitised_ftr_reg(u32 id);
 u64 __read_sysreg_by_encoding(u32 sys_id);
+int arm64_check_features(u32 sys_reg, u64 val, u64 limit);
 
 static inline bool cpu_supports_mixed_endian_el0(void)
 {
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index 6f3e677d88f1..48dff8b101d9 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -3140,3 +3140,231 @@ ssize_t cpu_show_meltdown(struct device *dev, struct device_attribute *attr,
 		return sprintf(buf, "Vulnerable\n");
 	}
 }
+
+#ifdef CONFIG_KVM
+/*
+ * arm64_ftr_bits_kvm[] is used for KVM to check if features that are
+ * indicated in an ID register value for the guest are available on the host.
+ * arm64_ftr_bits_kvm[] is created based on arm64_ftr_regs[].  But, for
+ * registers for which arm64_ftr_bits_kvm_override[] has a corresponding
+ * entry, replace arm64_ftr_bits entries in arm64_ftr_bits_kvm[] with the
+ * ones in arm64_ftr_bits_kvm_override[].
+ */
+static struct __ftr_reg_bits_entry *arm64_ftr_bits_kvm;
+static size_t arm64_ftr_bits_kvm_nentries;
+static DEFINE_MUTEX(arm64_ftr_bits_kvm_lock);
+
+/*
+ * Number of arm64_ftr_bits entries for each register.
+ * (Number of 4 bits fields in 64 bit register + 1 entry for ARM64_FTR_END)
+ */
+#define	MAX_FTR_BITS_LEN	17
+
+/* Use FTR_LOWER_SAFE for AA64DFR0_EL1.PMUVER and AA64DFR0_EL1.DEBUGVER. */
+static struct arm64_ftr_bits ftr_id_aa64dfr0_kvm[MAX_FTR_BITS_LEN] = {
+	S_ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64DFR0_PMUVER_SHIFT, 4, 0),
+	ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x6),
+	ARM64_FTR_END,
+};
+
+#define	ARM64_FTR_REG_BITS(id, table)	{	\
+	.sys_id = id,				\
+	.ftr_bits = &((table)[0]),		\
+}
+
+struct __ftr_reg_bits_entry {
+	u32	sys_id;
+	struct arm64_ftr_bits	*ftr_bits;
+};
+
+/*
+ * All entries in arm64_ftr_bits_kvm_override[] are used to override
+ * the corresponding entries in arm64_ftr_bits_kvm[].
+ */
+static struct __ftr_reg_bits_entry arm64_ftr_bits_kvm_override[] = {
+	ARM64_FTR_REG_BITS(SYS_ID_AA64DFR0_EL1, ftr_id_aa64dfr0_kvm),
+};
+
+/*
+ * Override entries in @orig_ftrp with the ones in @new_ftrp when their shift
+ * fields match.  The last entry of @orig_ftrp and @new_ftrp must be
+ * ARM64_FTR_END (.width == 0).
+ */
+static void arm64_ftr_reg_bits_overrite(struct arm64_ftr_bits *orig_ftrp,
+					struct arm64_ftr_bits *new_ftrp)
+{
+	struct arm64_ftr_bits *o_ftrp, *n_ftrp;
+
+	for (n_ftrp = new_ftrp; n_ftrp->width; n_ftrp++) {
+		for (o_ftrp = orig_ftrp; o_ftrp->width; o_ftrp++) {
+			if (o_ftrp->shift == n_ftrp->shift) {
+				*o_ftrp = *n_ftrp;
+				break;
+			}
+		}
+	}
+}
+
+/*
+ * Copy arm64_ftr_bits entries from @src_ftrp to @dst_ftrp.  The last entries
+ * of @dst_ftrp and @src_ftrp must be ARM64_FTR_END (.width == 0).
+ */
+static void copy_arm64_ftr_bits(struct arm64_ftr_bits *dst_ftrp,
+				const struct arm64_ftr_bits *src_ftrp)
+{
+	int i = 0;
+
+	for (; src_ftrp[i].width; i++) {
+		if (WARN_ON_ONCE(i >= (MAX_FTR_BITS_LEN - 1)))
+			break;
+
+		dst_ftrp[i] = src_ftrp[i];
+	}
+
+	dst_ftrp[i].width = 0;
+}
+
+/*
+ * Initialize arm64_ftr_bits_kvm.  Copy arm64_ftr_bits for each ID register
+ * from arm64_ftr_regs to arm64_ftr_bits_kvm, and then override entries in
+ * arm64_ftr_bits_kvm with ones in arm64_ftr_bits_kvm_override.
+ */
+static int init_arm64_ftr_bits_kvm(void)
+{
+	struct arm64_ftr_bits ftr_temp[MAX_FTR_BITS_LEN];
+	static struct __ftr_reg_bits_entry *reg_bits_array, *bits, *o_bits;
+	int i, j, nent, ret;
+
+	mutex_lock(&arm64_ftr_bits_kvm_lock);
+	if (arm64_ftr_bits_kvm) {
+		/* Already initialized */
+		ret = 0;
+		goto unlock_exit;
+	}
+
+	nent = ARRAY_SIZE(arm64_ftr_regs);
+	reg_bits_array = kcalloc(nent, sizeof(struct __ftr_reg_bits_entry),
+				 GFP_KERNEL);
+	if (!reg_bits_array) {
+		ret = ENOMEM;
+		goto unlock_exit;
+	}
+
+	/* Copy entries from arm64_ftr_regs to reg_bits_array */
+	for (i = 0; i < nent; i++) {
+		bits = &reg_bits_array[i];
+		bits->sys_id = arm64_ftr_regs[i].sys_id;
+		bits->ftr_bits = (struct arm64_ftr_bits *)arm64_ftr_regs[i].reg->ftr_bits;
+	};
+
+	/*
+	 * Override the entries in reg_bits_array with the ones in
+	 * arm64_ftr_bits_kvm_override.
+	 */
+	for (i = 0; i < ARRAY_SIZE(arm64_ftr_bits_kvm_override); i++) {
+		o_bits = &arm64_ftr_bits_kvm_override[i];
+		for (j = 0; j < nent; j++) {
+			bits = &reg_bits_array[j];
+			if (bits->sys_id != o_bits->sys_id)
+				continue;
+
+			memset(ftr_temp, 0, sizeof(ftr_temp));
+
+			/*
+			 * Temporary save all entries in o_bits->ftr_bits
+			 * to ftr_temp.
+			 */
+			copy_arm64_ftr_bits(ftr_temp, o_bits->ftr_bits);
+
+			/*
+			 * Copy entries from bits->ftr_bits to o_bits->ftr_bits.
+			 */
+			copy_arm64_ftr_bits(o_bits->ftr_bits, bits->ftr_bits);
+
+			/*
+			 * Override entries in o_bits->ftr_bits with the
+			 * saved ones, and update bits->ftr_bits with
+			 * o_bits->ftr_bits.
+			 */
+			arm64_ftr_reg_bits_overrite(o_bits->ftr_bits, ftr_temp);
+			bits->ftr_bits = o_bits->ftr_bits;
+			break;
+		}
+	}
+
+	arm64_ftr_bits_kvm_nentries = nent;
+	arm64_ftr_bits_kvm = reg_bits_array;
+	ret = 0;
+
+unlock_exit:
+	mutex_unlock(&arm64_ftr_bits_kvm_lock);
+	return ret;
+}
+
+static int search_cmp_ftr_reg_bits(const void *id, const void *regp)
+{
+	return ((int)(unsigned long)id -
+		(int)((const struct __ftr_reg_bits_entry *)regp)->sys_id);
+}
+
+static const struct arm64_ftr_bits *get_arm64_ftr_bits_kvm(u32 sys_id)
+{
+	const struct __ftr_reg_bits_entry *ret;
+	int err;
+
+	if (!arm64_ftr_bits_kvm) {
+		/* arm64_ftr_bits_kvm is not initialized yet. */
+		err = init_arm64_ftr_bits_kvm();
+		if (err)
+			return NULL;
+	}
+
+	ret = bsearch((const void *)(unsigned long)sys_id,
+		      arm64_ftr_bits_kvm,
+		      arm64_ftr_bits_kvm_nentries,
+		      sizeof(arm64_ftr_bits_kvm[0]),
+		      search_cmp_ftr_reg_bits);
+	if (ret)
+		return ret->ftr_bits;
+
+	return NULL;
+}
+
+/*
+ * Check if features (or levels of features) that are indicated in the ID
+ * register value @val are also indicated in @limit.
+ * This function is for KVM to check if features that are indicated in @val,
+ * which will be used as the ID register value for its guest, are supported
+ * on the host.
+ * For AA64MMFR0_EL1.TGranX_2 fields, which don't follow the standard ID
+ * scheme, the function checks if values of the fields in @val are the same
+ * as the ones in @limit.
+ */
+int arm64_check_features(u32 sys_reg, u64 val, u64 limit)
+{
+	const struct arm64_ftr_bits *ftrp = get_arm64_ftr_bits_kvm(sys_reg);
+	u64 exposed_mask = 0;
+
+	if (!ftrp)
+		return -ENOENT;
+
+	for (; ftrp->width; ftrp++) {
+		s64 ftr_val = arm64_ftr_value(ftrp, val);
+		s64 ftr_lim = arm64_ftr_value(ftrp, limit);
+
+		exposed_mask |= arm64_ftr_mask(ftrp);
+
+		if (ftr_val == ftr_lim)
+			continue;
+
+		if (ftr_val != arm64_ftr_safe_value(ftrp, ftr_val, ftr_lim))
+			return -E2BIG;
+	}
+
+	/* Make sure that no unrecognized fields are set in @val. */
+	if (val & ~exposed_mask)
+		return -E2BIG;
+
+	return 0;
+}
+#endif /* CONFIG_KVM */
-- 
2.34.1.448.ga2b2bfdf31-goog

_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 01/26] KVM: arm64: Introduce a validation function for an ID register
@ 2022-01-06  4:26   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

Introduce arm64_check_features(), which does a basic validity checking
of an ID register value against the register's limit value, which is
generally the host's sanitized value.

This function will be used by the following patches to check if an ID
register value that userspace tries to set for a guest can be supported
on the host.

The validation is done using arm64_ftr_bits_kvm, which is created from
arm64_ftr_regs, with some entries overwritten by entries from
arm64_ftr_bits_kvm_override.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/include/asm/cpufeature.h |   1 +
 arch/arm64/kernel/cpufeature.c      | 228 ++++++++++++++++++++++++++++
 2 files changed, 229 insertions(+)

diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
index ef6be92b1921..eda7ddbed8cf 100644
--- a/arch/arm64/include/asm/cpufeature.h
+++ b/arch/arm64/include/asm/cpufeature.h
@@ -631,6 +631,7 @@ void check_local_cpu_capabilities(void);
 
 u64 read_sanitised_ftr_reg(u32 id);
 u64 __read_sysreg_by_encoding(u32 sys_id);
+int arm64_check_features(u32 sys_reg, u64 val, u64 limit);
 
 static inline bool cpu_supports_mixed_endian_el0(void)
 {
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index 6f3e677d88f1..48dff8b101d9 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -3140,3 +3140,231 @@ ssize_t cpu_show_meltdown(struct device *dev, struct device_attribute *attr,
 		return sprintf(buf, "Vulnerable\n");
 	}
 }
+
+#ifdef CONFIG_KVM
+/*
+ * arm64_ftr_bits_kvm[] is used for KVM to check if features that are
+ * indicated in an ID register value for the guest are available on the host.
+ * arm64_ftr_bits_kvm[] is created based on arm64_ftr_regs[].  But, for
+ * registers for which arm64_ftr_bits_kvm_override[] has a corresponding
+ * entry, replace arm64_ftr_bits entries in arm64_ftr_bits_kvm[] with the
+ * ones in arm64_ftr_bits_kvm_override[].
+ */
+static struct __ftr_reg_bits_entry *arm64_ftr_bits_kvm;
+static size_t arm64_ftr_bits_kvm_nentries;
+static DEFINE_MUTEX(arm64_ftr_bits_kvm_lock);
+
+/*
+ * Number of arm64_ftr_bits entries for each register.
+ * (Number of 4 bits fields in 64 bit register + 1 entry for ARM64_FTR_END)
+ */
+#define	MAX_FTR_BITS_LEN	17
+
+/* Use FTR_LOWER_SAFE for AA64DFR0_EL1.PMUVER and AA64DFR0_EL1.DEBUGVER. */
+static struct arm64_ftr_bits ftr_id_aa64dfr0_kvm[MAX_FTR_BITS_LEN] = {
+	S_ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64DFR0_PMUVER_SHIFT, 4, 0),
+	ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x6),
+	ARM64_FTR_END,
+};
+
+#define	ARM64_FTR_REG_BITS(id, table)	{	\
+	.sys_id = id,				\
+	.ftr_bits = &((table)[0]),		\
+}
+
+struct __ftr_reg_bits_entry {
+	u32	sys_id;
+	struct arm64_ftr_bits	*ftr_bits;
+};
+
+/*
+ * All entries in arm64_ftr_bits_kvm_override[] are used to override
+ * the corresponding entries in arm64_ftr_bits_kvm[].
+ */
+static struct __ftr_reg_bits_entry arm64_ftr_bits_kvm_override[] = {
+	ARM64_FTR_REG_BITS(SYS_ID_AA64DFR0_EL1, ftr_id_aa64dfr0_kvm),
+};
+
+/*
+ * Override entries in @orig_ftrp with the ones in @new_ftrp when their shift
+ * fields match.  The last entry of @orig_ftrp and @new_ftrp must be
+ * ARM64_FTR_END (.width == 0).
+ */
+static void arm64_ftr_reg_bits_overrite(struct arm64_ftr_bits *orig_ftrp,
+					struct arm64_ftr_bits *new_ftrp)
+{
+	struct arm64_ftr_bits *o_ftrp, *n_ftrp;
+
+	for (n_ftrp = new_ftrp; n_ftrp->width; n_ftrp++) {
+		for (o_ftrp = orig_ftrp; o_ftrp->width; o_ftrp++) {
+			if (o_ftrp->shift == n_ftrp->shift) {
+				*o_ftrp = *n_ftrp;
+				break;
+			}
+		}
+	}
+}
+
+/*
+ * Copy arm64_ftr_bits entries from @src_ftrp to @dst_ftrp.  The last entries
+ * of @dst_ftrp and @src_ftrp must be ARM64_FTR_END (.width == 0).
+ */
+static void copy_arm64_ftr_bits(struct arm64_ftr_bits *dst_ftrp,
+				const struct arm64_ftr_bits *src_ftrp)
+{
+	int i = 0;
+
+	for (; src_ftrp[i].width; i++) {
+		if (WARN_ON_ONCE(i >= (MAX_FTR_BITS_LEN - 1)))
+			break;
+
+		dst_ftrp[i] = src_ftrp[i];
+	}
+
+	dst_ftrp[i].width = 0;
+}
+
+/*
+ * Initialize arm64_ftr_bits_kvm.  Copy arm64_ftr_bits for each ID register
+ * from arm64_ftr_regs to arm64_ftr_bits_kvm, and then override entries in
+ * arm64_ftr_bits_kvm with ones in arm64_ftr_bits_kvm_override.
+ */
+static int init_arm64_ftr_bits_kvm(void)
+{
+	struct arm64_ftr_bits ftr_temp[MAX_FTR_BITS_LEN];
+	static struct __ftr_reg_bits_entry *reg_bits_array, *bits, *o_bits;
+	int i, j, nent, ret;
+
+	mutex_lock(&arm64_ftr_bits_kvm_lock);
+	if (arm64_ftr_bits_kvm) {
+		/* Already initialized */
+		ret = 0;
+		goto unlock_exit;
+	}
+
+	nent = ARRAY_SIZE(arm64_ftr_regs);
+	reg_bits_array = kcalloc(nent, sizeof(struct __ftr_reg_bits_entry),
+				 GFP_KERNEL);
+	if (!reg_bits_array) {
+		ret = ENOMEM;
+		goto unlock_exit;
+	}
+
+	/* Copy entries from arm64_ftr_regs to reg_bits_array */
+	for (i = 0; i < nent; i++) {
+		bits = &reg_bits_array[i];
+		bits->sys_id = arm64_ftr_regs[i].sys_id;
+		bits->ftr_bits = (struct arm64_ftr_bits *)arm64_ftr_regs[i].reg->ftr_bits;
+	};
+
+	/*
+	 * Override the entries in reg_bits_array with the ones in
+	 * arm64_ftr_bits_kvm_override.
+	 */
+	for (i = 0; i < ARRAY_SIZE(arm64_ftr_bits_kvm_override); i++) {
+		o_bits = &arm64_ftr_bits_kvm_override[i];
+		for (j = 0; j < nent; j++) {
+			bits = &reg_bits_array[j];
+			if (bits->sys_id != o_bits->sys_id)
+				continue;
+
+			memset(ftr_temp, 0, sizeof(ftr_temp));
+
+			/*
+			 * Temporary save all entries in o_bits->ftr_bits
+			 * to ftr_temp.
+			 */
+			copy_arm64_ftr_bits(ftr_temp, o_bits->ftr_bits);
+
+			/*
+			 * Copy entries from bits->ftr_bits to o_bits->ftr_bits.
+			 */
+			copy_arm64_ftr_bits(o_bits->ftr_bits, bits->ftr_bits);
+
+			/*
+			 * Override entries in o_bits->ftr_bits with the
+			 * saved ones, and update bits->ftr_bits with
+			 * o_bits->ftr_bits.
+			 */
+			arm64_ftr_reg_bits_overrite(o_bits->ftr_bits, ftr_temp);
+			bits->ftr_bits = o_bits->ftr_bits;
+			break;
+		}
+	}
+
+	arm64_ftr_bits_kvm_nentries = nent;
+	arm64_ftr_bits_kvm = reg_bits_array;
+	ret = 0;
+
+unlock_exit:
+	mutex_unlock(&arm64_ftr_bits_kvm_lock);
+	return ret;
+}
+
+static int search_cmp_ftr_reg_bits(const void *id, const void *regp)
+{
+	return ((int)(unsigned long)id -
+		(int)((const struct __ftr_reg_bits_entry *)regp)->sys_id);
+}
+
+static const struct arm64_ftr_bits *get_arm64_ftr_bits_kvm(u32 sys_id)
+{
+	const struct __ftr_reg_bits_entry *ret;
+	int err;
+
+	if (!arm64_ftr_bits_kvm) {
+		/* arm64_ftr_bits_kvm is not initialized yet. */
+		err = init_arm64_ftr_bits_kvm();
+		if (err)
+			return NULL;
+	}
+
+	ret = bsearch((const void *)(unsigned long)sys_id,
+		      arm64_ftr_bits_kvm,
+		      arm64_ftr_bits_kvm_nentries,
+		      sizeof(arm64_ftr_bits_kvm[0]),
+		      search_cmp_ftr_reg_bits);
+	if (ret)
+		return ret->ftr_bits;
+
+	return NULL;
+}
+
+/*
+ * Check if features (or levels of features) that are indicated in the ID
+ * register value @val are also indicated in @limit.
+ * This function is for KVM to check if features that are indicated in @val,
+ * which will be used as the ID register value for its guest, are supported
+ * on the host.
+ * For AA64MMFR0_EL1.TGranX_2 fields, which don't follow the standard ID
+ * scheme, the function checks if values of the fields in @val are the same
+ * as the ones in @limit.
+ */
+int arm64_check_features(u32 sys_reg, u64 val, u64 limit)
+{
+	const struct arm64_ftr_bits *ftrp = get_arm64_ftr_bits_kvm(sys_reg);
+	u64 exposed_mask = 0;
+
+	if (!ftrp)
+		return -ENOENT;
+
+	for (; ftrp->width; ftrp++) {
+		s64 ftr_val = arm64_ftr_value(ftrp, val);
+		s64 ftr_lim = arm64_ftr_value(ftrp, limit);
+
+		exposed_mask |= arm64_ftr_mask(ftrp);
+
+		if (ftr_val == ftr_lim)
+			continue;
+
+		if (ftr_val != arm64_ftr_safe_value(ftrp, ftr_val, ftr_lim))
+			return -E2BIG;
+	}
+
+	/* Make sure that no unrecognized fields are set in @val. */
+	if (val & ~exposed_mask)
+		return -E2BIG;
+
+	return 0;
+}
+#endif /* CONFIG_KVM */
-- 
2.34.1.448.ga2b2bfdf31-goog


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 02/26] KVM: arm64: Save ID registers' sanitized value per guest
  2022-01-06  4:26 ` Reiji Watanabe
  (?)
@ 2022-01-06  4:26   ` Reiji Watanabe
  -1 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

Introduce id_regs[] in kvm_arch as a storage of guest's ID registers,
and save ID registers' sanitized value in the array at KVM_CREATE_VM.
Use the saved ones when ID registers are read by the guest or
userspace (via KVM_GET_ONE_REG).

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/include/asm/kvm_host.h | 16 ++++++++
 arch/arm64/kvm/arm.c              |  1 +
 arch/arm64/kvm/sys_regs.c         | 62 ++++++++++++++++++++++---------
 3 files changed, 62 insertions(+), 17 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 2a5f7f38006f..c789a0137f58 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -102,6 +102,17 @@ struct kvm_s2_mmu {
 struct kvm_arch_memory_slot {
 };
 
+/*
+ * (Op0, Op1, CRn, CRm, Op2) of ID registers is (3, 0, 0, crm, op2),
+ * where 0<=crm<8, 0<=op2<8.
+ */
+#define KVM_ARM_ID_REG_MAX_NUM	64
+#define IDREG_IDX(id)		((sys_reg_CRm(id) << 3) | sys_reg_Op2(id))
+#define is_id_reg(id)	\
+	(sys_reg_Op0(id) == 3 && sys_reg_Op1(id) == 0 &&	\
+	 sys_reg_CRn(id) == 0 && sys_reg_CRm(id) >= 0 &&	\
+	 sys_reg_CRm(id) < 8)
+
 struct kvm_arch {
 	struct kvm_s2_mmu mmu;
 
@@ -137,6 +148,9 @@ struct kvm_arch {
 
 	/* Memory Tagging Extension enabled for the guest */
 	bool mte_enabled;
+
+	/* ID registers for the guest. */
+	u64 id_regs[KVM_ARM_ID_REG_MAX_NUM];
 };
 
 struct kvm_vcpu_fault_info {
@@ -734,6 +748,8 @@ int kvm_arm_vcpu_arch_has_attr(struct kvm_vcpu *vcpu,
 long kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
 				struct kvm_arm_copy_mte_tags *copy_tags);
 
+void set_default_id_regs(struct kvm *kvm);
+
 /* Guest/host FPSIMD coordination helpers */
 int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu);
 void kvm_arch_vcpu_load_fp(struct kvm_vcpu *vcpu);
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index e4727dc771bf..5f497a0af254 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -156,6 +156,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
 	kvm->arch.max_vcpus = kvm_arm_default_max_vcpus();
 
 	set_default_spectre(kvm);
+	set_default_id_regs(kvm);
 
 	return ret;
 out_free_stage2_pgd:
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index e3ec1a44f94d..80dc62f98ef0 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -33,6 +33,8 @@
 
 #include "trace.h"
 
+static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id);
+
 /*
  * All of this file is extremely similar to the ARM coproc.c, but the
  * types are different. My gut feeling is that it should be pretty
@@ -273,7 +275,7 @@ static bool trap_loregion(struct kvm_vcpu *vcpu,
 			  struct sys_reg_params *p,
 			  const struct sys_reg_desc *r)
 {
-	u64 val = read_sanitised_ftr_reg(SYS_ID_AA64MMFR1_EL1);
+	u64 val = __read_id_reg(vcpu, SYS_ID_AA64MMFR1_EL1);
 	u32 sr = reg_to_encoding(r);
 
 	if (!(val & (0xfUL << ID_AA64MMFR1_LOR_SHIFT))) {
@@ -1059,17 +1061,9 @@ static bool access_arch_timer(struct kvm_vcpu *vcpu,
 	return true;
 }
 
-/* Read a sanitised cpufeature ID register by sys_reg_desc */
-static u64 read_id_reg(const struct kvm_vcpu *vcpu,
-		struct sys_reg_desc const *r, bool raz)
+static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
 {
-	u32 id = reg_to_encoding(r);
-	u64 val;
-
-	if (raz)
-		return 0;
-
-	val = read_sanitised_ftr_reg(id);
+	u64 val = vcpu->kvm->arch.id_regs[IDREG_IDX(id)];
 
 	switch (id) {
 	case SYS_ID_AA64PFR0_EL1:
@@ -1119,6 +1113,14 @@ static u64 read_id_reg(const struct kvm_vcpu *vcpu,
 	return val;
 }
 
+static u64 read_id_reg(const struct kvm_vcpu *vcpu,
+		       struct sys_reg_desc const *r, bool raz)
+{
+	u32 id = reg_to_encoding(r);
+
+	return raz ? 0 : __read_id_reg(vcpu, id);
+}
+
 static unsigned int id_visibility(const struct kvm_vcpu *vcpu,
 				  const struct sys_reg_desc *r)
 {
@@ -1223,9 +1225,8 @@ static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
 /*
  * cpufeature ID register user accessors
  *
- * For now, these registers are immutable for userspace, so no values
- * are stored, and for set_id_reg() we don't allow the effective value
- * to be changed.
+ * For now, these registers are immutable for userspace, so for set_id_reg()
+ * we don't allow the effective value to be changed.
  */
 static int __get_id_reg(const struct kvm_vcpu *vcpu,
 			const struct sys_reg_desc *rd, void __user *uaddr,
@@ -1237,7 +1238,7 @@ static int __get_id_reg(const struct kvm_vcpu *vcpu,
 	return reg_to_user(uaddr, &val, id);
 }
 
-static int __set_id_reg(const struct kvm_vcpu *vcpu,
+static int __set_id_reg(struct kvm_vcpu *vcpu,
 			const struct sys_reg_desc *rd, void __user *uaddr,
 			bool raz)
 {
@@ -1837,8 +1838,8 @@ static bool trap_dbgdidr(struct kvm_vcpu *vcpu,
 	if (p->is_write) {
 		return ignore_write(vcpu, p);
 	} else {
-		u64 dfr = read_sanitised_ftr_reg(SYS_ID_AA64DFR0_EL1);
-		u64 pfr = read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1);
+		u64 dfr = __read_id_reg(vcpu, SYS_ID_AA64DFR0_EL1);
+		u64 pfr = __read_id_reg(vcpu, SYS_ID_AA64PFR0_EL1);
 		u32 el3 = !!cpuid_feature_extract_unsigned_field(pfr, ID_AA64PFR0_EL3_SHIFT);
 
 		p->regval = ((((dfr >> ID_AA64DFR0_WRPS_SHIFT) & 0xf) << 28) |
@@ -2850,3 +2851,30 @@ void kvm_sys_reg_table_init(void)
 	/* Clear all higher bits. */
 	cache_levels &= (1 << (i*3))-1;
 }
+
+/*
+ * Set the guest's ID registers that are defined in sys_reg_descs[]
+ * with ID_SANITISED() to the host's sanitized value.
+ */
+void set_default_id_regs(struct kvm *kvm)
+{
+	int i;
+	u32 id;
+	const struct sys_reg_desc *rd;
+	u64 val;
+
+	for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
+		rd = &sys_reg_descs[i];
+		if (rd->access != access_id_reg)
+			/* Not ID register, or hidden/reserved ID register */
+			continue;
+
+		id = reg_to_encoding(rd);
+		if (WARN_ON_ONCE(!is_id_reg(id)))
+			/* Shouldn't happen */
+			continue;
+
+		val = read_sanitised_ftr_reg(id);
+		kvm->arch.id_regs[IDREG_IDX(id)] = val;
+	}
+}
-- 
2.34.1.448.ga2b2bfdf31-goog


^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 02/26] KVM: arm64: Save ID registers' sanitized value per guest
@ 2022-01-06  4:26   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, Will Deacon, Peter Shier, Paolo Bonzini, linux-arm-kernel

Introduce id_regs[] in kvm_arch as a storage of guest's ID registers,
and save ID registers' sanitized value in the array at KVM_CREATE_VM.
Use the saved ones when ID registers are read by the guest or
userspace (via KVM_GET_ONE_REG).

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/include/asm/kvm_host.h | 16 ++++++++
 arch/arm64/kvm/arm.c              |  1 +
 arch/arm64/kvm/sys_regs.c         | 62 ++++++++++++++++++++++---------
 3 files changed, 62 insertions(+), 17 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 2a5f7f38006f..c789a0137f58 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -102,6 +102,17 @@ struct kvm_s2_mmu {
 struct kvm_arch_memory_slot {
 };
 
+/*
+ * (Op0, Op1, CRn, CRm, Op2) of ID registers is (3, 0, 0, crm, op2),
+ * where 0<=crm<8, 0<=op2<8.
+ */
+#define KVM_ARM_ID_REG_MAX_NUM	64
+#define IDREG_IDX(id)		((sys_reg_CRm(id) << 3) | sys_reg_Op2(id))
+#define is_id_reg(id)	\
+	(sys_reg_Op0(id) == 3 && sys_reg_Op1(id) == 0 &&	\
+	 sys_reg_CRn(id) == 0 && sys_reg_CRm(id) >= 0 &&	\
+	 sys_reg_CRm(id) < 8)
+
 struct kvm_arch {
 	struct kvm_s2_mmu mmu;
 
@@ -137,6 +148,9 @@ struct kvm_arch {
 
 	/* Memory Tagging Extension enabled for the guest */
 	bool mte_enabled;
+
+	/* ID registers for the guest. */
+	u64 id_regs[KVM_ARM_ID_REG_MAX_NUM];
 };
 
 struct kvm_vcpu_fault_info {
@@ -734,6 +748,8 @@ int kvm_arm_vcpu_arch_has_attr(struct kvm_vcpu *vcpu,
 long kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
 				struct kvm_arm_copy_mte_tags *copy_tags);
 
+void set_default_id_regs(struct kvm *kvm);
+
 /* Guest/host FPSIMD coordination helpers */
 int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu);
 void kvm_arch_vcpu_load_fp(struct kvm_vcpu *vcpu);
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index e4727dc771bf..5f497a0af254 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -156,6 +156,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
 	kvm->arch.max_vcpus = kvm_arm_default_max_vcpus();
 
 	set_default_spectre(kvm);
+	set_default_id_regs(kvm);
 
 	return ret;
 out_free_stage2_pgd:
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index e3ec1a44f94d..80dc62f98ef0 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -33,6 +33,8 @@
 
 #include "trace.h"
 
+static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id);
+
 /*
  * All of this file is extremely similar to the ARM coproc.c, but the
  * types are different. My gut feeling is that it should be pretty
@@ -273,7 +275,7 @@ static bool trap_loregion(struct kvm_vcpu *vcpu,
 			  struct sys_reg_params *p,
 			  const struct sys_reg_desc *r)
 {
-	u64 val = read_sanitised_ftr_reg(SYS_ID_AA64MMFR1_EL1);
+	u64 val = __read_id_reg(vcpu, SYS_ID_AA64MMFR1_EL1);
 	u32 sr = reg_to_encoding(r);
 
 	if (!(val & (0xfUL << ID_AA64MMFR1_LOR_SHIFT))) {
@@ -1059,17 +1061,9 @@ static bool access_arch_timer(struct kvm_vcpu *vcpu,
 	return true;
 }
 
-/* Read a sanitised cpufeature ID register by sys_reg_desc */
-static u64 read_id_reg(const struct kvm_vcpu *vcpu,
-		struct sys_reg_desc const *r, bool raz)
+static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
 {
-	u32 id = reg_to_encoding(r);
-	u64 val;
-
-	if (raz)
-		return 0;
-
-	val = read_sanitised_ftr_reg(id);
+	u64 val = vcpu->kvm->arch.id_regs[IDREG_IDX(id)];
 
 	switch (id) {
 	case SYS_ID_AA64PFR0_EL1:
@@ -1119,6 +1113,14 @@ static u64 read_id_reg(const struct kvm_vcpu *vcpu,
 	return val;
 }
 
+static u64 read_id_reg(const struct kvm_vcpu *vcpu,
+		       struct sys_reg_desc const *r, bool raz)
+{
+	u32 id = reg_to_encoding(r);
+
+	return raz ? 0 : __read_id_reg(vcpu, id);
+}
+
 static unsigned int id_visibility(const struct kvm_vcpu *vcpu,
 				  const struct sys_reg_desc *r)
 {
@@ -1223,9 +1225,8 @@ static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
 /*
  * cpufeature ID register user accessors
  *
- * For now, these registers are immutable for userspace, so no values
- * are stored, and for set_id_reg() we don't allow the effective value
- * to be changed.
+ * For now, these registers are immutable for userspace, so for set_id_reg()
+ * we don't allow the effective value to be changed.
  */
 static int __get_id_reg(const struct kvm_vcpu *vcpu,
 			const struct sys_reg_desc *rd, void __user *uaddr,
@@ -1237,7 +1238,7 @@ static int __get_id_reg(const struct kvm_vcpu *vcpu,
 	return reg_to_user(uaddr, &val, id);
 }
 
-static int __set_id_reg(const struct kvm_vcpu *vcpu,
+static int __set_id_reg(struct kvm_vcpu *vcpu,
 			const struct sys_reg_desc *rd, void __user *uaddr,
 			bool raz)
 {
@@ -1837,8 +1838,8 @@ static bool trap_dbgdidr(struct kvm_vcpu *vcpu,
 	if (p->is_write) {
 		return ignore_write(vcpu, p);
 	} else {
-		u64 dfr = read_sanitised_ftr_reg(SYS_ID_AA64DFR0_EL1);
-		u64 pfr = read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1);
+		u64 dfr = __read_id_reg(vcpu, SYS_ID_AA64DFR0_EL1);
+		u64 pfr = __read_id_reg(vcpu, SYS_ID_AA64PFR0_EL1);
 		u32 el3 = !!cpuid_feature_extract_unsigned_field(pfr, ID_AA64PFR0_EL3_SHIFT);
 
 		p->regval = ((((dfr >> ID_AA64DFR0_WRPS_SHIFT) & 0xf) << 28) |
@@ -2850,3 +2851,30 @@ void kvm_sys_reg_table_init(void)
 	/* Clear all higher bits. */
 	cache_levels &= (1 << (i*3))-1;
 }
+
+/*
+ * Set the guest's ID registers that are defined in sys_reg_descs[]
+ * with ID_SANITISED() to the host's sanitized value.
+ */
+void set_default_id_regs(struct kvm *kvm)
+{
+	int i;
+	u32 id;
+	const struct sys_reg_desc *rd;
+	u64 val;
+
+	for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
+		rd = &sys_reg_descs[i];
+		if (rd->access != access_id_reg)
+			/* Not ID register, or hidden/reserved ID register */
+			continue;
+
+		id = reg_to_encoding(rd);
+		if (WARN_ON_ONCE(!is_id_reg(id)))
+			/* Shouldn't happen */
+			continue;
+
+		val = read_sanitised_ftr_reg(id);
+		kvm->arch.id_regs[IDREG_IDX(id)] = val;
+	}
+}
-- 
2.34.1.448.ga2b2bfdf31-goog

_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 02/26] KVM: arm64: Save ID registers' sanitized value per guest
@ 2022-01-06  4:26   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

Introduce id_regs[] in kvm_arch as a storage of guest's ID registers,
and save ID registers' sanitized value in the array at KVM_CREATE_VM.
Use the saved ones when ID registers are read by the guest or
userspace (via KVM_GET_ONE_REG).

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/include/asm/kvm_host.h | 16 ++++++++
 arch/arm64/kvm/arm.c              |  1 +
 arch/arm64/kvm/sys_regs.c         | 62 ++++++++++++++++++++++---------
 3 files changed, 62 insertions(+), 17 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 2a5f7f38006f..c789a0137f58 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -102,6 +102,17 @@ struct kvm_s2_mmu {
 struct kvm_arch_memory_slot {
 };
 
+/*
+ * (Op0, Op1, CRn, CRm, Op2) of ID registers is (3, 0, 0, crm, op2),
+ * where 0<=crm<8, 0<=op2<8.
+ */
+#define KVM_ARM_ID_REG_MAX_NUM	64
+#define IDREG_IDX(id)		((sys_reg_CRm(id) << 3) | sys_reg_Op2(id))
+#define is_id_reg(id)	\
+	(sys_reg_Op0(id) == 3 && sys_reg_Op1(id) == 0 &&	\
+	 sys_reg_CRn(id) == 0 && sys_reg_CRm(id) >= 0 &&	\
+	 sys_reg_CRm(id) < 8)
+
 struct kvm_arch {
 	struct kvm_s2_mmu mmu;
 
@@ -137,6 +148,9 @@ struct kvm_arch {
 
 	/* Memory Tagging Extension enabled for the guest */
 	bool mte_enabled;
+
+	/* ID registers for the guest. */
+	u64 id_regs[KVM_ARM_ID_REG_MAX_NUM];
 };
 
 struct kvm_vcpu_fault_info {
@@ -734,6 +748,8 @@ int kvm_arm_vcpu_arch_has_attr(struct kvm_vcpu *vcpu,
 long kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
 				struct kvm_arm_copy_mte_tags *copy_tags);
 
+void set_default_id_regs(struct kvm *kvm);
+
 /* Guest/host FPSIMD coordination helpers */
 int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu);
 void kvm_arch_vcpu_load_fp(struct kvm_vcpu *vcpu);
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index e4727dc771bf..5f497a0af254 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -156,6 +156,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
 	kvm->arch.max_vcpus = kvm_arm_default_max_vcpus();
 
 	set_default_spectre(kvm);
+	set_default_id_regs(kvm);
 
 	return ret;
 out_free_stage2_pgd:
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index e3ec1a44f94d..80dc62f98ef0 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -33,6 +33,8 @@
 
 #include "trace.h"
 
+static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id);
+
 /*
  * All of this file is extremely similar to the ARM coproc.c, but the
  * types are different. My gut feeling is that it should be pretty
@@ -273,7 +275,7 @@ static bool trap_loregion(struct kvm_vcpu *vcpu,
 			  struct sys_reg_params *p,
 			  const struct sys_reg_desc *r)
 {
-	u64 val = read_sanitised_ftr_reg(SYS_ID_AA64MMFR1_EL1);
+	u64 val = __read_id_reg(vcpu, SYS_ID_AA64MMFR1_EL1);
 	u32 sr = reg_to_encoding(r);
 
 	if (!(val & (0xfUL << ID_AA64MMFR1_LOR_SHIFT))) {
@@ -1059,17 +1061,9 @@ static bool access_arch_timer(struct kvm_vcpu *vcpu,
 	return true;
 }
 
-/* Read a sanitised cpufeature ID register by sys_reg_desc */
-static u64 read_id_reg(const struct kvm_vcpu *vcpu,
-		struct sys_reg_desc const *r, bool raz)
+static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
 {
-	u32 id = reg_to_encoding(r);
-	u64 val;
-
-	if (raz)
-		return 0;
-
-	val = read_sanitised_ftr_reg(id);
+	u64 val = vcpu->kvm->arch.id_regs[IDREG_IDX(id)];
 
 	switch (id) {
 	case SYS_ID_AA64PFR0_EL1:
@@ -1119,6 +1113,14 @@ static u64 read_id_reg(const struct kvm_vcpu *vcpu,
 	return val;
 }
 
+static u64 read_id_reg(const struct kvm_vcpu *vcpu,
+		       struct sys_reg_desc const *r, bool raz)
+{
+	u32 id = reg_to_encoding(r);
+
+	return raz ? 0 : __read_id_reg(vcpu, id);
+}
+
 static unsigned int id_visibility(const struct kvm_vcpu *vcpu,
 				  const struct sys_reg_desc *r)
 {
@@ -1223,9 +1225,8 @@ static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
 /*
  * cpufeature ID register user accessors
  *
- * For now, these registers are immutable for userspace, so no values
- * are stored, and for set_id_reg() we don't allow the effective value
- * to be changed.
+ * For now, these registers are immutable for userspace, so for set_id_reg()
+ * we don't allow the effective value to be changed.
  */
 static int __get_id_reg(const struct kvm_vcpu *vcpu,
 			const struct sys_reg_desc *rd, void __user *uaddr,
@@ -1237,7 +1238,7 @@ static int __get_id_reg(const struct kvm_vcpu *vcpu,
 	return reg_to_user(uaddr, &val, id);
 }
 
-static int __set_id_reg(const struct kvm_vcpu *vcpu,
+static int __set_id_reg(struct kvm_vcpu *vcpu,
 			const struct sys_reg_desc *rd, void __user *uaddr,
 			bool raz)
 {
@@ -1837,8 +1838,8 @@ static bool trap_dbgdidr(struct kvm_vcpu *vcpu,
 	if (p->is_write) {
 		return ignore_write(vcpu, p);
 	} else {
-		u64 dfr = read_sanitised_ftr_reg(SYS_ID_AA64DFR0_EL1);
-		u64 pfr = read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1);
+		u64 dfr = __read_id_reg(vcpu, SYS_ID_AA64DFR0_EL1);
+		u64 pfr = __read_id_reg(vcpu, SYS_ID_AA64PFR0_EL1);
 		u32 el3 = !!cpuid_feature_extract_unsigned_field(pfr, ID_AA64PFR0_EL3_SHIFT);
 
 		p->regval = ((((dfr >> ID_AA64DFR0_WRPS_SHIFT) & 0xf) << 28) |
@@ -2850,3 +2851,30 @@ void kvm_sys_reg_table_init(void)
 	/* Clear all higher bits. */
 	cache_levels &= (1 << (i*3))-1;
 }
+
+/*
+ * Set the guest's ID registers that are defined in sys_reg_descs[]
+ * with ID_SANITISED() to the host's sanitized value.
+ */
+void set_default_id_regs(struct kvm *kvm)
+{
+	int i;
+	u32 id;
+	const struct sys_reg_desc *rd;
+	u64 val;
+
+	for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
+		rd = &sys_reg_descs[i];
+		if (rd->access != access_id_reg)
+			/* Not ID register, or hidden/reserved ID register */
+			continue;
+
+		id = reg_to_encoding(rd);
+		if (WARN_ON_ONCE(!is_id_reg(id)))
+			/* Shouldn't happen */
+			continue;
+
+		val = read_sanitised_ftr_reg(id);
+		kvm->arch.id_regs[IDREG_IDX(id)] = val;
+	}
+}
-- 
2.34.1.448.ga2b2bfdf31-goog


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 03/26] KVM: arm64: Introduce struct id_reg_info
  2022-01-06  4:26 ` Reiji Watanabe
  (?)
@ 2022-01-06  4:26   ` Reiji Watanabe
  -1 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

This patch lays the groundwork to make ID registers writable.

Introduce struct id_reg_info for an ID register to manage the
register specific control of its value for the guest, and provide set
of functions commonly used for ID registers to make them writable.

The id_reg_info is used to do register specific initialization,
validation of the ID register and etc.  Not all ID registers must
have the id_reg_info. ID registers that don't have the id_reg_info
are handled in a common way that is applied to all ID registers.

At present, changing an ID register from userspace is allowed only
if the ID register has the id_reg_info, but that will be changed
by the following patches.

No ID register has the structure yet and the following patches
will add the id_reg_info for some ID registers.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/kvm/sys_regs.c | 243 ++++++++++++++++++++++++++++++++++++--
 1 file changed, 230 insertions(+), 13 deletions(-)

diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 80dc62f98ef0..971018288bee 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -265,6 +265,101 @@ static bool trap_raz_wi(struct kvm_vcpu *vcpu,
 		return read_zero(vcpu, p);
 }
 
+struct id_reg_info {
+	u32	sys_reg;	/* Register ID */
+
+	/*
+	 * Limit value of the register for a vcpu. The value is the sanitized
+	 * system value with bits cleared for unsupported features for the
+	 * guest.
+	 */
+	u64	vcpu_limit_val;
+
+	/* Fields that we don't to validate by arm64_check_features. */
+	u64	ignore_mask;
+
+	/* Initialization function of the id_reg_info */
+	void (*init)(struct id_reg_info *id_reg);
+
+	/*
+	 * This is an optional ID register specific validation function.
+	 * When userspace tries to set the ID register, arm64_check_features()
+	 * will check if the requested value indicates any features that cannot
+	 * be supported by KVM on the host.  But, some ID register fields need
+	 * a special checking, and this function can be used for such fields.
+	 * e.g. When SVE is configured for a vCPU by KVM_ARM_VCPU_INIT,
+	 * ID_AA64PFR0_EL1.SVE shouldn't be set to 0 for the vCPU.
+	 * The validation function for ID_AA64PFR0_EL1 could be used to check
+	 * the field is consistent with SVE configuration.
+	 */
+	int (*validate)(struct kvm_vcpu *vcpu, const struct id_reg_info *id_reg,
+			u64 val);
+
+	/*
+	 * Return a bitmask of the vCPU's ID register fields that are not
+	 * synced with saved (per VM) ID register value, which usually
+	 * indicates opt-in CPU features that is not configured for the vCPU.
+	 * ID registers are saved per VM, but some opt-in CPU features can
+	 * be configured per vCPU.  The saved (per VM) values for such
+	 * features are for vCPUs with the features (and zero for
+	 * vCPUs without the features).
+	 * Return value of this function is used to handle such fields
+	 * for per vCPU ID register read/write request with saved per VM
+	 * ID register.  See the __write_id_reg's comment for more detail.
+	 */
+	u64 (*vcpu_mask)(const struct kvm_vcpu *vcpu,
+			 const struct id_reg_info *id_reg);
+};
+
+static void id_reg_info_init(struct id_reg_info *id_reg)
+{
+	id_reg->vcpu_limit_val = read_sanitised_ftr_reg(id_reg->sys_reg);
+	if (id_reg->init)
+		id_reg->init(id_reg);
+}
+
+/*
+ * An ID register that needs special handling to control the value for the
+ * guest must have its own id_reg_info in id_reg_info_table.
+ * (i.e. the reset value is different from the host's sanitized value,
+ * the value is affected by opt-in features, some fields need specific
+ * validation, etc.)
+ */
+#define	GET_ID_REG_INFO(id)	(id_reg_info_table[IDREG_IDX(id)])
+static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {};
+
+static int validate_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
+{
+	const struct id_reg_info *id_reg = GET_ID_REG_INFO(id);
+	u64 limit, tmp_val;
+	int err;
+
+	if (id_reg) {
+		limit = id_reg->vcpu_limit_val;
+		/*
+		 * Replace the fields that are indicated in ignore_mask with
+		 * the value in the limit to not have arm64_check_features()
+		 * check the field in @val.
+		 */
+		tmp_val = val & ~id_reg->ignore_mask;
+		tmp_val |= (limit & id_reg->ignore_mask);
+	} else {
+		limit = read_sanitised_ftr_reg(id);
+		tmp_val = val;
+	}
+
+	/* Check if the value indicates any feature that is not in the limit. */
+	err = arm64_check_features(id, tmp_val, limit);
+	if (err)
+		return err;
+
+	if (id_reg && id_reg->validate)
+		/* Run the ID register specific validity check. */
+		err = id_reg->validate(vcpu, id_reg, val);
+
+	return err;
+}
+
 /*
  * ARMv8.1 mandates at least a trivial LORegion implementation, where all the
  * RW registers are RES0 (which we can implement as RAZ/WI). On an ARMv8.0
@@ -1061,9 +1156,81 @@ static bool access_arch_timer(struct kvm_vcpu *vcpu,
 	return true;
 }
 
+static u64 read_kvm_id_reg(struct kvm *kvm, u32 id)
+{
+	return kvm->arch.id_regs[IDREG_IDX(id)];
+}
+
+static int modify_kvm_id_reg(struct kvm *kvm, u32 id, u64 val,
+			     u64 preserve_mask)
+{
+	u64 old, new;
+
+	mutex_lock(&kvm->lock);
+
+	old = kvm->arch.id_regs[IDREG_IDX(id)];
+
+	/* Preserve the value at the bit position set in preserve_mask */
+	new = old & preserve_mask;
+	new |= (val & ~preserve_mask);
+
+	/* Don't allow to modify ID register value after KVM_RUN on any vCPUs */
+	if (kvm_vm_has_started(kvm) && new != old)
+		return -EBUSY;
+
+	WRITE_ONCE(kvm->arch.id_regs[IDREG_IDX(id)], new);
+	mutex_unlock(&kvm->lock);
+
+	return 0;
+}
+
+static int write_kvm_id_reg(struct kvm *kvm, u32 id, u64 val)
+{
+	return modify_kvm_id_reg(kvm, id, val, 0);
+}
+
+
+/*
+ * KVM basically forces all vCPUs of the guest to have a uniform value for
+ * each ID register (, which means KVM_SET_ONE_REG for a vCPU affects all
+ * the vCPUs of the guest), and the id_regs[] of kvm_arch holds values
+ * of ID registers for the guest.  However, there is an exception for
+ * ID register fields corresponding to CPU features that can be
+ * configured per vCPU by KVM_ARM_VCPU_INIT, or etc (e.g. PMUv3, SVE, etc).
+ * For such fields, all vCPUs that have the feature will have a non-zero
+ * uniform value (, which can be updated by userspace), but the vCPUs that
+ * don't have the feature will have zero for the fields.
+ * Values that @id_regs holds are for vCPUs that have such features.  So,
+ * to get the ID register value for a vCPU that doesn't have those features,
+ * the corresponding fields in id_regs[] needs to be cleared.
+ * A bitmask of the fields are provided by id_reg_info's vcpu_mask(), and
+ * __write_id_reg() and __read_id_reg() take care of those fields using
+ * the bitmask.
+ */
+static int __write_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
+{
+	const struct id_reg_info *id_reg = GET_ID_REG_INFO(id);
+	u64 mask = 0;
+
+	if (id_reg && id_reg->vcpu_mask)
+		mask = id_reg->vcpu_mask(vcpu, id_reg);
+
+	/*
+	 * Update the ID register for the guest with @val, except for fields
+	 * that are set in the mask, which indicates fields for opt-in
+	 * features that are not configured for the vCPU.
+	 */
+	return modify_kvm_id_reg(vcpu->kvm, id, val, mask);
+}
+
 static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
 {
-	u64 val = vcpu->kvm->arch.id_regs[IDREG_IDX(id)];
+	const struct id_reg_info *id_reg = GET_ID_REG_INFO(id);
+	u64 val = read_kvm_id_reg(vcpu->kvm, id);
+
+	if (id_reg && id_reg->vcpu_mask)
+		/* Clear fields for opt-in features that are not configured. */
+		val &= ~(id_reg->vcpu_mask(vcpu, id_reg));
 
 	switch (id) {
 	case SYS_ID_AA64PFR0_EL1:
@@ -1222,12 +1389,7 @@ static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
 	return 0;
 }
 
-/*
- * cpufeature ID register user accessors
- *
- * For now, these registers are immutable for userspace, so for set_id_reg()
- * we don't allow the effective value to be changed.
- */
+/* cpufeature ID register user accessors */
 static int __get_id_reg(const struct kvm_vcpu *vcpu,
 			const struct sys_reg_desc *rd, void __user *uaddr,
 			bool raz)
@@ -1238,11 +1400,31 @@ static int __get_id_reg(const struct kvm_vcpu *vcpu,
 	return reg_to_user(uaddr, &val, id);
 }
 
+/*
+ * Check if the given id indicates AArch32 ID register encoding.
+ */
+static bool is_aarch32_id_reg(u32 id)
+{
+	u32 crm, op2;
+
+	if (!is_id_reg(id))
+		return false;
+
+	crm = sys_reg_CRm(id);
+	op2 = sys_reg_Op2(id);
+	if (crm == 1 || crm == 2 || (crm == 3 && (op2 != 3 && op2 != 7)))
+		/* AArch32 ID register */
+		return true;
+
+	return false;
+}
+
 static int __set_id_reg(struct kvm_vcpu *vcpu,
 			const struct sys_reg_desc *rd, void __user *uaddr,
 			bool raz)
 {
 	const u64 id = sys_reg_to_index(rd);
+	u32 encoding = reg_to_encoding(rd);
 	int err;
 	u64 val;
 
@@ -1250,11 +1432,28 @@ static int __set_id_reg(struct kvm_vcpu *vcpu,
 	if (err)
 		return err;
 
-	/* This is what we mean by invariant: you can't change it. */
-	if (val != read_id_reg(vcpu, rd, raz))
+	if (val == read_id_reg(vcpu, rd, raz))
+		/* The value is same as the current value. Nothing to do. */
+		return 0;
+
+	/*
+	 * Don't allow to modify the register's value if the register is raz,
+	 * or the reg doesn't have the id_reg_info.
+	 */
+	if (raz || !GET_ID_REG_INFO(encoding))
 		return -EINVAL;
 
-	return 0;
+	/*
+	 * Skip the validation of AArch32 ID registers if the system doesn't
+	 * 32bit EL0 (their value are UNKNOWN).
+	 */
+	if (system_supports_32bit_el0() || !is_aarch32_id_reg(encoding)) {
+		err = validate_id_reg(vcpu, encoding, val);
+		if (err)
+			return err;
+	}
+
+	return __write_id_reg(vcpu, encoding, val);
 }
 
 static int get_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
@@ -2816,6 +3015,20 @@ int kvm_arm_copy_sys_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
 	return write_demux_regids(uindices);
 }
 
+static void id_reg_info_init_all(void)
+{
+	int i;
+	struct id_reg_info *id_reg;
+
+	for (i = 0; i < ARRAY_SIZE(id_reg_info_table); i++) {
+		id_reg = (struct id_reg_info *)id_reg_info_table[i];
+		if (!id_reg)
+			continue;
+
+		id_reg_info_init(id_reg);
+	}
+}
+
 void kvm_sys_reg_table_init(void)
 {
 	unsigned int i;
@@ -2850,6 +3063,8 @@ void kvm_sys_reg_table_init(void)
 			break;
 	/* Clear all higher bits. */
 	cache_levels &= (1 << (i*3))-1;
+
+	id_reg_info_init_all();
 }
 
 /*
@@ -2862,11 +3077,12 @@ void set_default_id_regs(struct kvm *kvm)
 	u32 id;
 	const struct sys_reg_desc *rd;
 	u64 val;
+	struct id_reg_info *idr;
 
 	for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
 		rd = &sys_reg_descs[i];
 		if (rd->access != access_id_reg)
-			/* Not ID register, or hidden/reserved ID register */
+			/* Not ID register or hidden/reserved ID register */
 			continue;
 
 		id = reg_to_encoding(rd);
@@ -2874,7 +3090,8 @@ void set_default_id_regs(struct kvm *kvm)
 			/* Shouldn't happen */
 			continue;
 
-		val = read_sanitised_ftr_reg(id);
-		kvm->arch.id_regs[IDREG_IDX(id)] = val;
+		idr = GET_ID_REG_INFO(id);
+		val = idr ? idr->vcpu_limit_val : read_sanitised_ftr_reg(id);
+		(void)write_kvm_id_reg(kvm, id, val);
 	}
 }
-- 
2.34.1.448.ga2b2bfdf31-goog


^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 03/26] KVM: arm64: Introduce struct id_reg_info
@ 2022-01-06  4:26   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, Will Deacon, Peter Shier, Paolo Bonzini, linux-arm-kernel

This patch lays the groundwork to make ID registers writable.

Introduce struct id_reg_info for an ID register to manage the
register specific control of its value for the guest, and provide set
of functions commonly used for ID registers to make them writable.

The id_reg_info is used to do register specific initialization,
validation of the ID register and etc.  Not all ID registers must
have the id_reg_info. ID registers that don't have the id_reg_info
are handled in a common way that is applied to all ID registers.

At present, changing an ID register from userspace is allowed only
if the ID register has the id_reg_info, but that will be changed
by the following patches.

No ID register has the structure yet and the following patches
will add the id_reg_info for some ID registers.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/kvm/sys_regs.c | 243 ++++++++++++++++++++++++++++++++++++--
 1 file changed, 230 insertions(+), 13 deletions(-)

diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 80dc62f98ef0..971018288bee 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -265,6 +265,101 @@ static bool trap_raz_wi(struct kvm_vcpu *vcpu,
 		return read_zero(vcpu, p);
 }
 
+struct id_reg_info {
+	u32	sys_reg;	/* Register ID */
+
+	/*
+	 * Limit value of the register for a vcpu. The value is the sanitized
+	 * system value with bits cleared for unsupported features for the
+	 * guest.
+	 */
+	u64	vcpu_limit_val;
+
+	/* Fields that we don't to validate by arm64_check_features. */
+	u64	ignore_mask;
+
+	/* Initialization function of the id_reg_info */
+	void (*init)(struct id_reg_info *id_reg);
+
+	/*
+	 * This is an optional ID register specific validation function.
+	 * When userspace tries to set the ID register, arm64_check_features()
+	 * will check if the requested value indicates any features that cannot
+	 * be supported by KVM on the host.  But, some ID register fields need
+	 * a special checking, and this function can be used for such fields.
+	 * e.g. When SVE is configured for a vCPU by KVM_ARM_VCPU_INIT,
+	 * ID_AA64PFR0_EL1.SVE shouldn't be set to 0 for the vCPU.
+	 * The validation function for ID_AA64PFR0_EL1 could be used to check
+	 * the field is consistent with SVE configuration.
+	 */
+	int (*validate)(struct kvm_vcpu *vcpu, const struct id_reg_info *id_reg,
+			u64 val);
+
+	/*
+	 * Return a bitmask of the vCPU's ID register fields that are not
+	 * synced with saved (per VM) ID register value, which usually
+	 * indicates opt-in CPU features that is not configured for the vCPU.
+	 * ID registers are saved per VM, but some opt-in CPU features can
+	 * be configured per vCPU.  The saved (per VM) values for such
+	 * features are for vCPUs with the features (and zero for
+	 * vCPUs without the features).
+	 * Return value of this function is used to handle such fields
+	 * for per vCPU ID register read/write request with saved per VM
+	 * ID register.  See the __write_id_reg's comment for more detail.
+	 */
+	u64 (*vcpu_mask)(const struct kvm_vcpu *vcpu,
+			 const struct id_reg_info *id_reg);
+};
+
+static void id_reg_info_init(struct id_reg_info *id_reg)
+{
+	id_reg->vcpu_limit_val = read_sanitised_ftr_reg(id_reg->sys_reg);
+	if (id_reg->init)
+		id_reg->init(id_reg);
+}
+
+/*
+ * An ID register that needs special handling to control the value for the
+ * guest must have its own id_reg_info in id_reg_info_table.
+ * (i.e. the reset value is different from the host's sanitized value,
+ * the value is affected by opt-in features, some fields need specific
+ * validation, etc.)
+ */
+#define	GET_ID_REG_INFO(id)	(id_reg_info_table[IDREG_IDX(id)])
+static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {};
+
+static int validate_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
+{
+	const struct id_reg_info *id_reg = GET_ID_REG_INFO(id);
+	u64 limit, tmp_val;
+	int err;
+
+	if (id_reg) {
+		limit = id_reg->vcpu_limit_val;
+		/*
+		 * Replace the fields that are indicated in ignore_mask with
+		 * the value in the limit to not have arm64_check_features()
+		 * check the field in @val.
+		 */
+		tmp_val = val & ~id_reg->ignore_mask;
+		tmp_val |= (limit & id_reg->ignore_mask);
+	} else {
+		limit = read_sanitised_ftr_reg(id);
+		tmp_val = val;
+	}
+
+	/* Check if the value indicates any feature that is not in the limit. */
+	err = arm64_check_features(id, tmp_val, limit);
+	if (err)
+		return err;
+
+	if (id_reg && id_reg->validate)
+		/* Run the ID register specific validity check. */
+		err = id_reg->validate(vcpu, id_reg, val);
+
+	return err;
+}
+
 /*
  * ARMv8.1 mandates at least a trivial LORegion implementation, where all the
  * RW registers are RES0 (which we can implement as RAZ/WI). On an ARMv8.0
@@ -1061,9 +1156,81 @@ static bool access_arch_timer(struct kvm_vcpu *vcpu,
 	return true;
 }
 
+static u64 read_kvm_id_reg(struct kvm *kvm, u32 id)
+{
+	return kvm->arch.id_regs[IDREG_IDX(id)];
+}
+
+static int modify_kvm_id_reg(struct kvm *kvm, u32 id, u64 val,
+			     u64 preserve_mask)
+{
+	u64 old, new;
+
+	mutex_lock(&kvm->lock);
+
+	old = kvm->arch.id_regs[IDREG_IDX(id)];
+
+	/* Preserve the value at the bit position set in preserve_mask */
+	new = old & preserve_mask;
+	new |= (val & ~preserve_mask);
+
+	/* Don't allow to modify ID register value after KVM_RUN on any vCPUs */
+	if (kvm_vm_has_started(kvm) && new != old)
+		return -EBUSY;
+
+	WRITE_ONCE(kvm->arch.id_regs[IDREG_IDX(id)], new);
+	mutex_unlock(&kvm->lock);
+
+	return 0;
+}
+
+static int write_kvm_id_reg(struct kvm *kvm, u32 id, u64 val)
+{
+	return modify_kvm_id_reg(kvm, id, val, 0);
+}
+
+
+/*
+ * KVM basically forces all vCPUs of the guest to have a uniform value for
+ * each ID register (, which means KVM_SET_ONE_REG for a vCPU affects all
+ * the vCPUs of the guest), and the id_regs[] of kvm_arch holds values
+ * of ID registers for the guest.  However, there is an exception for
+ * ID register fields corresponding to CPU features that can be
+ * configured per vCPU by KVM_ARM_VCPU_INIT, or etc (e.g. PMUv3, SVE, etc).
+ * For such fields, all vCPUs that have the feature will have a non-zero
+ * uniform value (, which can be updated by userspace), but the vCPUs that
+ * don't have the feature will have zero for the fields.
+ * Values that @id_regs holds are for vCPUs that have such features.  So,
+ * to get the ID register value for a vCPU that doesn't have those features,
+ * the corresponding fields in id_regs[] needs to be cleared.
+ * A bitmask of the fields are provided by id_reg_info's vcpu_mask(), and
+ * __write_id_reg() and __read_id_reg() take care of those fields using
+ * the bitmask.
+ */
+static int __write_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
+{
+	const struct id_reg_info *id_reg = GET_ID_REG_INFO(id);
+	u64 mask = 0;
+
+	if (id_reg && id_reg->vcpu_mask)
+		mask = id_reg->vcpu_mask(vcpu, id_reg);
+
+	/*
+	 * Update the ID register for the guest with @val, except for fields
+	 * that are set in the mask, which indicates fields for opt-in
+	 * features that are not configured for the vCPU.
+	 */
+	return modify_kvm_id_reg(vcpu->kvm, id, val, mask);
+}
+
 static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
 {
-	u64 val = vcpu->kvm->arch.id_regs[IDREG_IDX(id)];
+	const struct id_reg_info *id_reg = GET_ID_REG_INFO(id);
+	u64 val = read_kvm_id_reg(vcpu->kvm, id);
+
+	if (id_reg && id_reg->vcpu_mask)
+		/* Clear fields for opt-in features that are not configured. */
+		val &= ~(id_reg->vcpu_mask(vcpu, id_reg));
 
 	switch (id) {
 	case SYS_ID_AA64PFR0_EL1:
@@ -1222,12 +1389,7 @@ static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
 	return 0;
 }
 
-/*
- * cpufeature ID register user accessors
- *
- * For now, these registers are immutable for userspace, so for set_id_reg()
- * we don't allow the effective value to be changed.
- */
+/* cpufeature ID register user accessors */
 static int __get_id_reg(const struct kvm_vcpu *vcpu,
 			const struct sys_reg_desc *rd, void __user *uaddr,
 			bool raz)
@@ -1238,11 +1400,31 @@ static int __get_id_reg(const struct kvm_vcpu *vcpu,
 	return reg_to_user(uaddr, &val, id);
 }
 
+/*
+ * Check if the given id indicates AArch32 ID register encoding.
+ */
+static bool is_aarch32_id_reg(u32 id)
+{
+	u32 crm, op2;
+
+	if (!is_id_reg(id))
+		return false;
+
+	crm = sys_reg_CRm(id);
+	op2 = sys_reg_Op2(id);
+	if (crm == 1 || crm == 2 || (crm == 3 && (op2 != 3 && op2 != 7)))
+		/* AArch32 ID register */
+		return true;
+
+	return false;
+}
+
 static int __set_id_reg(struct kvm_vcpu *vcpu,
 			const struct sys_reg_desc *rd, void __user *uaddr,
 			bool raz)
 {
 	const u64 id = sys_reg_to_index(rd);
+	u32 encoding = reg_to_encoding(rd);
 	int err;
 	u64 val;
 
@@ -1250,11 +1432,28 @@ static int __set_id_reg(struct kvm_vcpu *vcpu,
 	if (err)
 		return err;
 
-	/* This is what we mean by invariant: you can't change it. */
-	if (val != read_id_reg(vcpu, rd, raz))
+	if (val == read_id_reg(vcpu, rd, raz))
+		/* The value is same as the current value. Nothing to do. */
+		return 0;
+
+	/*
+	 * Don't allow to modify the register's value if the register is raz,
+	 * or the reg doesn't have the id_reg_info.
+	 */
+	if (raz || !GET_ID_REG_INFO(encoding))
 		return -EINVAL;
 
-	return 0;
+	/*
+	 * Skip the validation of AArch32 ID registers if the system doesn't
+	 * 32bit EL0 (their value are UNKNOWN).
+	 */
+	if (system_supports_32bit_el0() || !is_aarch32_id_reg(encoding)) {
+		err = validate_id_reg(vcpu, encoding, val);
+		if (err)
+			return err;
+	}
+
+	return __write_id_reg(vcpu, encoding, val);
 }
 
 static int get_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
@@ -2816,6 +3015,20 @@ int kvm_arm_copy_sys_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
 	return write_demux_regids(uindices);
 }
 
+static void id_reg_info_init_all(void)
+{
+	int i;
+	struct id_reg_info *id_reg;
+
+	for (i = 0; i < ARRAY_SIZE(id_reg_info_table); i++) {
+		id_reg = (struct id_reg_info *)id_reg_info_table[i];
+		if (!id_reg)
+			continue;
+
+		id_reg_info_init(id_reg);
+	}
+}
+
 void kvm_sys_reg_table_init(void)
 {
 	unsigned int i;
@@ -2850,6 +3063,8 @@ void kvm_sys_reg_table_init(void)
 			break;
 	/* Clear all higher bits. */
 	cache_levels &= (1 << (i*3))-1;
+
+	id_reg_info_init_all();
 }
 
 /*
@@ -2862,11 +3077,12 @@ void set_default_id_regs(struct kvm *kvm)
 	u32 id;
 	const struct sys_reg_desc *rd;
 	u64 val;
+	struct id_reg_info *idr;
 
 	for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
 		rd = &sys_reg_descs[i];
 		if (rd->access != access_id_reg)
-			/* Not ID register, or hidden/reserved ID register */
+			/* Not ID register or hidden/reserved ID register */
 			continue;
 
 		id = reg_to_encoding(rd);
@@ -2874,7 +3090,8 @@ void set_default_id_regs(struct kvm *kvm)
 			/* Shouldn't happen */
 			continue;
 
-		val = read_sanitised_ftr_reg(id);
-		kvm->arch.id_regs[IDREG_IDX(id)] = val;
+		idr = GET_ID_REG_INFO(id);
+		val = idr ? idr->vcpu_limit_val : read_sanitised_ftr_reg(id);
+		(void)write_kvm_id_reg(kvm, id, val);
 	}
 }
-- 
2.34.1.448.ga2b2bfdf31-goog

_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 03/26] KVM: arm64: Introduce struct id_reg_info
@ 2022-01-06  4:26   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

This patch lays the groundwork to make ID registers writable.

Introduce struct id_reg_info for an ID register to manage the
register specific control of its value for the guest, and provide set
of functions commonly used for ID registers to make them writable.

The id_reg_info is used to do register specific initialization,
validation of the ID register and etc.  Not all ID registers must
have the id_reg_info. ID registers that don't have the id_reg_info
are handled in a common way that is applied to all ID registers.

At present, changing an ID register from userspace is allowed only
if the ID register has the id_reg_info, but that will be changed
by the following patches.

No ID register has the structure yet and the following patches
will add the id_reg_info for some ID registers.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/kvm/sys_regs.c | 243 ++++++++++++++++++++++++++++++++++++--
 1 file changed, 230 insertions(+), 13 deletions(-)

diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 80dc62f98ef0..971018288bee 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -265,6 +265,101 @@ static bool trap_raz_wi(struct kvm_vcpu *vcpu,
 		return read_zero(vcpu, p);
 }
 
+struct id_reg_info {
+	u32	sys_reg;	/* Register ID */
+
+	/*
+	 * Limit value of the register for a vcpu. The value is the sanitized
+	 * system value with bits cleared for unsupported features for the
+	 * guest.
+	 */
+	u64	vcpu_limit_val;
+
+	/* Fields that we don't to validate by arm64_check_features. */
+	u64	ignore_mask;
+
+	/* Initialization function of the id_reg_info */
+	void (*init)(struct id_reg_info *id_reg);
+
+	/*
+	 * This is an optional ID register specific validation function.
+	 * When userspace tries to set the ID register, arm64_check_features()
+	 * will check if the requested value indicates any features that cannot
+	 * be supported by KVM on the host.  But, some ID register fields need
+	 * a special checking, and this function can be used for such fields.
+	 * e.g. When SVE is configured for a vCPU by KVM_ARM_VCPU_INIT,
+	 * ID_AA64PFR0_EL1.SVE shouldn't be set to 0 for the vCPU.
+	 * The validation function for ID_AA64PFR0_EL1 could be used to check
+	 * the field is consistent with SVE configuration.
+	 */
+	int (*validate)(struct kvm_vcpu *vcpu, const struct id_reg_info *id_reg,
+			u64 val);
+
+	/*
+	 * Return a bitmask of the vCPU's ID register fields that are not
+	 * synced with saved (per VM) ID register value, which usually
+	 * indicates opt-in CPU features that is not configured for the vCPU.
+	 * ID registers are saved per VM, but some opt-in CPU features can
+	 * be configured per vCPU.  The saved (per VM) values for such
+	 * features are for vCPUs with the features (and zero for
+	 * vCPUs without the features).
+	 * Return value of this function is used to handle such fields
+	 * for per vCPU ID register read/write request with saved per VM
+	 * ID register.  See the __write_id_reg's comment for more detail.
+	 */
+	u64 (*vcpu_mask)(const struct kvm_vcpu *vcpu,
+			 const struct id_reg_info *id_reg);
+};
+
+static void id_reg_info_init(struct id_reg_info *id_reg)
+{
+	id_reg->vcpu_limit_val = read_sanitised_ftr_reg(id_reg->sys_reg);
+	if (id_reg->init)
+		id_reg->init(id_reg);
+}
+
+/*
+ * An ID register that needs special handling to control the value for the
+ * guest must have its own id_reg_info in id_reg_info_table.
+ * (i.e. the reset value is different from the host's sanitized value,
+ * the value is affected by opt-in features, some fields need specific
+ * validation, etc.)
+ */
+#define	GET_ID_REG_INFO(id)	(id_reg_info_table[IDREG_IDX(id)])
+static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {};
+
+static int validate_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
+{
+	const struct id_reg_info *id_reg = GET_ID_REG_INFO(id);
+	u64 limit, tmp_val;
+	int err;
+
+	if (id_reg) {
+		limit = id_reg->vcpu_limit_val;
+		/*
+		 * Replace the fields that are indicated in ignore_mask with
+		 * the value in the limit to not have arm64_check_features()
+		 * check the field in @val.
+		 */
+		tmp_val = val & ~id_reg->ignore_mask;
+		tmp_val |= (limit & id_reg->ignore_mask);
+	} else {
+		limit = read_sanitised_ftr_reg(id);
+		tmp_val = val;
+	}
+
+	/* Check if the value indicates any feature that is not in the limit. */
+	err = arm64_check_features(id, tmp_val, limit);
+	if (err)
+		return err;
+
+	if (id_reg && id_reg->validate)
+		/* Run the ID register specific validity check. */
+		err = id_reg->validate(vcpu, id_reg, val);
+
+	return err;
+}
+
 /*
  * ARMv8.1 mandates at least a trivial LORegion implementation, where all the
  * RW registers are RES0 (which we can implement as RAZ/WI). On an ARMv8.0
@@ -1061,9 +1156,81 @@ static bool access_arch_timer(struct kvm_vcpu *vcpu,
 	return true;
 }
 
+static u64 read_kvm_id_reg(struct kvm *kvm, u32 id)
+{
+	return kvm->arch.id_regs[IDREG_IDX(id)];
+}
+
+static int modify_kvm_id_reg(struct kvm *kvm, u32 id, u64 val,
+			     u64 preserve_mask)
+{
+	u64 old, new;
+
+	mutex_lock(&kvm->lock);
+
+	old = kvm->arch.id_regs[IDREG_IDX(id)];
+
+	/* Preserve the value at the bit position set in preserve_mask */
+	new = old & preserve_mask;
+	new |= (val & ~preserve_mask);
+
+	/* Don't allow to modify ID register value after KVM_RUN on any vCPUs */
+	if (kvm_vm_has_started(kvm) && new != old)
+		return -EBUSY;
+
+	WRITE_ONCE(kvm->arch.id_regs[IDREG_IDX(id)], new);
+	mutex_unlock(&kvm->lock);
+
+	return 0;
+}
+
+static int write_kvm_id_reg(struct kvm *kvm, u32 id, u64 val)
+{
+	return modify_kvm_id_reg(kvm, id, val, 0);
+}
+
+
+/*
+ * KVM basically forces all vCPUs of the guest to have a uniform value for
+ * each ID register (, which means KVM_SET_ONE_REG for a vCPU affects all
+ * the vCPUs of the guest), and the id_regs[] of kvm_arch holds values
+ * of ID registers for the guest.  However, there is an exception for
+ * ID register fields corresponding to CPU features that can be
+ * configured per vCPU by KVM_ARM_VCPU_INIT, or etc (e.g. PMUv3, SVE, etc).
+ * For such fields, all vCPUs that have the feature will have a non-zero
+ * uniform value (, which can be updated by userspace), but the vCPUs that
+ * don't have the feature will have zero for the fields.
+ * Values that @id_regs holds are for vCPUs that have such features.  So,
+ * to get the ID register value for a vCPU that doesn't have those features,
+ * the corresponding fields in id_regs[] needs to be cleared.
+ * A bitmask of the fields are provided by id_reg_info's vcpu_mask(), and
+ * __write_id_reg() and __read_id_reg() take care of those fields using
+ * the bitmask.
+ */
+static int __write_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
+{
+	const struct id_reg_info *id_reg = GET_ID_REG_INFO(id);
+	u64 mask = 0;
+
+	if (id_reg && id_reg->vcpu_mask)
+		mask = id_reg->vcpu_mask(vcpu, id_reg);
+
+	/*
+	 * Update the ID register for the guest with @val, except for fields
+	 * that are set in the mask, which indicates fields for opt-in
+	 * features that are not configured for the vCPU.
+	 */
+	return modify_kvm_id_reg(vcpu->kvm, id, val, mask);
+}
+
 static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
 {
-	u64 val = vcpu->kvm->arch.id_regs[IDREG_IDX(id)];
+	const struct id_reg_info *id_reg = GET_ID_REG_INFO(id);
+	u64 val = read_kvm_id_reg(vcpu->kvm, id);
+
+	if (id_reg && id_reg->vcpu_mask)
+		/* Clear fields for opt-in features that are not configured. */
+		val &= ~(id_reg->vcpu_mask(vcpu, id_reg));
 
 	switch (id) {
 	case SYS_ID_AA64PFR0_EL1:
@@ -1222,12 +1389,7 @@ static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
 	return 0;
 }
 
-/*
- * cpufeature ID register user accessors
- *
- * For now, these registers are immutable for userspace, so for set_id_reg()
- * we don't allow the effective value to be changed.
- */
+/* cpufeature ID register user accessors */
 static int __get_id_reg(const struct kvm_vcpu *vcpu,
 			const struct sys_reg_desc *rd, void __user *uaddr,
 			bool raz)
@@ -1238,11 +1400,31 @@ static int __get_id_reg(const struct kvm_vcpu *vcpu,
 	return reg_to_user(uaddr, &val, id);
 }
 
+/*
+ * Check if the given id indicates AArch32 ID register encoding.
+ */
+static bool is_aarch32_id_reg(u32 id)
+{
+	u32 crm, op2;
+
+	if (!is_id_reg(id))
+		return false;
+
+	crm = sys_reg_CRm(id);
+	op2 = sys_reg_Op2(id);
+	if (crm == 1 || crm == 2 || (crm == 3 && (op2 != 3 && op2 != 7)))
+		/* AArch32 ID register */
+		return true;
+
+	return false;
+}
+
 static int __set_id_reg(struct kvm_vcpu *vcpu,
 			const struct sys_reg_desc *rd, void __user *uaddr,
 			bool raz)
 {
 	const u64 id = sys_reg_to_index(rd);
+	u32 encoding = reg_to_encoding(rd);
 	int err;
 	u64 val;
 
@@ -1250,11 +1432,28 @@ static int __set_id_reg(struct kvm_vcpu *vcpu,
 	if (err)
 		return err;
 
-	/* This is what we mean by invariant: you can't change it. */
-	if (val != read_id_reg(vcpu, rd, raz))
+	if (val == read_id_reg(vcpu, rd, raz))
+		/* The value is same as the current value. Nothing to do. */
+		return 0;
+
+	/*
+	 * Don't allow to modify the register's value if the register is raz,
+	 * or the reg doesn't have the id_reg_info.
+	 */
+	if (raz || !GET_ID_REG_INFO(encoding))
 		return -EINVAL;
 
-	return 0;
+	/*
+	 * Skip the validation of AArch32 ID registers if the system doesn't
+	 * 32bit EL0 (their value are UNKNOWN).
+	 */
+	if (system_supports_32bit_el0() || !is_aarch32_id_reg(encoding)) {
+		err = validate_id_reg(vcpu, encoding, val);
+		if (err)
+			return err;
+	}
+
+	return __write_id_reg(vcpu, encoding, val);
 }
 
 static int get_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
@@ -2816,6 +3015,20 @@ int kvm_arm_copy_sys_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
 	return write_demux_regids(uindices);
 }
 
+static void id_reg_info_init_all(void)
+{
+	int i;
+	struct id_reg_info *id_reg;
+
+	for (i = 0; i < ARRAY_SIZE(id_reg_info_table); i++) {
+		id_reg = (struct id_reg_info *)id_reg_info_table[i];
+		if (!id_reg)
+			continue;
+
+		id_reg_info_init(id_reg);
+	}
+}
+
 void kvm_sys_reg_table_init(void)
 {
 	unsigned int i;
@@ -2850,6 +3063,8 @@ void kvm_sys_reg_table_init(void)
 			break;
 	/* Clear all higher bits. */
 	cache_levels &= (1 << (i*3))-1;
+
+	id_reg_info_init_all();
 }
 
 /*
@@ -2862,11 +3077,12 @@ void set_default_id_regs(struct kvm *kvm)
 	u32 id;
 	const struct sys_reg_desc *rd;
 	u64 val;
+	struct id_reg_info *idr;
 
 	for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
 		rd = &sys_reg_descs[i];
 		if (rd->access != access_id_reg)
-			/* Not ID register, or hidden/reserved ID register */
+			/* Not ID register or hidden/reserved ID register */
 			continue;
 
 		id = reg_to_encoding(rd);
@@ -2874,7 +3090,8 @@ void set_default_id_regs(struct kvm *kvm)
 			/* Shouldn't happen */
 			continue;
 
-		val = read_sanitised_ftr_reg(id);
-		kvm->arch.id_regs[IDREG_IDX(id)] = val;
+		idr = GET_ID_REG_INFO(id);
+		val = idr ? idr->vcpu_limit_val : read_sanitised_ftr_reg(id);
+		(void)write_kvm_id_reg(kvm, id, val);
 	}
 }
-- 
2.34.1.448.ga2b2bfdf31-goog


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 04/26] KVM: arm64: Make ID_AA64PFR0_EL1 writable
  2022-01-06  4:26 ` Reiji Watanabe
  (?)
@ 2022-01-06  4:26   ` Reiji Watanabe
  -1 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

This patch adds id_reg_info for ID_AA64PFR0_EL1 to make it writable by
userspace.

Return an error if userspace tries to set SVE/GIC field of the register
to a value that conflicts with SVE/GIC configuration for the guest.
SIMD/FP/SVE fields of the requested value are validated according to
Arm ARM.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/include/asm/kvm_host.h |   1 +
 arch/arm64/include/asm/sysreg.h   |   2 +
 arch/arm64/kvm/sys_regs.c         | 177 +++++++++++++++++++-----------
 arch/arm64/kvm/vgic/vgic-init.c   |   5 +
 4 files changed, 123 insertions(+), 62 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index c789a0137f58..4509f9e7472d 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -749,6 +749,7 @@ long kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
 				struct kvm_arm_copy_mte_tags *copy_tags);
 
 void set_default_id_regs(struct kvm *kvm);
+int kvm_set_id_reg_feature(struct kvm *kvm, u32 id, u8 field_shift, u8 fval);
 
 /* Guest/host FPSIMD coordination helpers */
 int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu);
diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h
index 16b3f1a1d468..e26027817171 100644
--- a/arch/arm64/include/asm/sysreg.h
+++ b/arch/arm64/include/asm/sysreg.h
@@ -798,6 +798,7 @@
 #define ID_AA64PFR0_ASIMD_SUPPORTED	0x0
 #define ID_AA64PFR0_ELx_64BIT_ONLY	0x1
 #define ID_AA64PFR0_ELx_32BIT_64BIT	0x2
+#define ID_AA64PFR0_GIC3		0x1
 
 /* id_aa64pfr1 */
 #define ID_AA64PFR1_MPAMFRAC_SHIFT	16
@@ -1197,6 +1198,7 @@
 #define ICH_VTR_TDS_MASK	(1 << ICH_VTR_TDS_SHIFT)
 
 #define ARM64_FEATURE_FIELD_BITS	4
+#define ARM64_FEATURE_FIELD_MASK	((1ull << ARM64_FEATURE_FIELD_BITS) - 1)
 
 /* Create a mask for the feature bits of the specified feature. */
 #define ARM64_FEATURE_MASK(x)	(GENMASK_ULL(x##_SHIFT + ARM64_FEATURE_FIELD_BITS - 1, x##_SHIFT))
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 971018288bee..1eb5c5fb614f 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -318,6 +318,92 @@ static void id_reg_info_init(struct id_reg_info *id_reg)
 		id_reg->init(id_reg);
 }
 
+static int validate_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
+				    const struct id_reg_info *id_reg, u64 val)
+{
+	int fp, simd;
+	unsigned int gic;
+	bool vcpu_has_sve = vcpu_has_sve(vcpu);
+	bool pfr0_has_sve = id_aa64pfr0_sve(val);
+
+	simd = cpuid_feature_extract_signed_field(val, ID_AA64PFR0_ASIMD_SHIFT);
+	fp = cpuid_feature_extract_signed_field(val, ID_AA64PFR0_FP_SHIFT);
+	if (simd != fp)
+		return -EINVAL;
+
+	/* fp must be supported when sve is supported */
+	if (pfr0_has_sve && (fp < 0))
+		return -EINVAL;
+
+	/* Check if there is a conflict with a request via KVM_ARM_VCPU_INIT */
+	if (vcpu_has_sve ^ pfr0_has_sve)
+		return -EPERM;
+
+	if ((irqchip_in_kernel(vcpu->kvm) &&
+	     vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3)) {
+		gic = cpuid_feature_extract_unsigned_field(val,
+							ID_AA64PFR0_GIC_SHIFT);
+		if (gic == 0)
+			return -EPERM;
+
+		if (gic > ID_AA64PFR0_GIC3)
+			return -E2BIG;
+	} else {
+		u64 mask = ARM64_FEATURE_MASK(ID_AA64PFR0_GIC);
+		int err = arm64_check_features(id_reg->sys_reg, val & mask,
+					       id_reg->vcpu_limit_val & mask);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+static void init_id_aa64pfr0_el1_info(struct id_reg_info *id_reg)
+{
+	u64 limit = id_reg->vcpu_limit_val;
+	unsigned int gic;
+
+	limit &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_AMU);
+	if (!system_supports_sve())
+		limit &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_SVE);
+
+	/*
+	 * The default is to expose CSV2 == 1 and CSV3 == 1 if the HW
+	 * isn't affected.  Userspace can override this as long as it
+	 * doesn't promise the impossible.
+	 */
+	limit &= ~(ARM64_FEATURE_MASK(ID_AA64PFR0_CSV2) |
+		   ARM64_FEATURE_MASK(ID_AA64PFR0_CSV3));
+
+	if (arm64_get_spectre_v2_state() == SPECTRE_UNAFFECTED)
+		limit |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_CSV2), 1);
+	if (arm64_get_meltdown_state() == SPECTRE_UNAFFECTED)
+		limit |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_CSV3), 1);
+
+	gic = cpuid_feature_extract_unsigned_field(limit, ID_AA64PFR0_GIC_SHIFT);
+	if (gic > 1) {
+		/* Limit to GICv3.0/4.0 */
+		limit &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_GIC);
+		limit |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_GIC), ID_AA64PFR0_GIC3);
+	}
+	id_reg->vcpu_limit_val = limit;
+}
+
+static u64 vcpu_mask_id_aa64pfr0_el1(const struct kvm_vcpu *vcpu,
+					 const struct id_reg_info *idr)
+{
+	return vcpu_has_sve(vcpu) ? 0 : ARM64_FEATURE_MASK(ID_AA64PFR0_SVE);
+}
+
+static struct id_reg_info id_aa64pfr0_el1_info = {
+	.sys_reg = SYS_ID_AA64PFR0_EL1,
+	.ignore_mask = ARM64_FEATURE_MASK(ID_AA64PFR0_GIC),
+	.init = init_id_aa64pfr0_el1_info,
+	.validate = validate_id_aa64pfr0_el1,
+	.vcpu_mask = vcpu_mask_id_aa64pfr0_el1,
+};
+
 /*
  * An ID register that needs special handling to control the value for the
  * guest must have its own id_reg_info in id_reg_info_table.
@@ -326,7 +412,9 @@ static void id_reg_info_init(struct id_reg_info *id_reg)
  * validation, etc.)
  */
 #define	GET_ID_REG_INFO(id)	(id_reg_info_table[IDREG_IDX(id)])
-static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {};
+static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {
+	[IDREG_IDX(SYS_ID_AA64PFR0_EL1)] = &id_aa64pfr0_el1_info,
+};
 
 static int validate_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
 {
@@ -1161,12 +1249,12 @@ static u64 read_kvm_id_reg(struct kvm *kvm, u32 id)
 	return kvm->arch.id_regs[IDREG_IDX(id)];
 }
 
-static int modify_kvm_id_reg(struct kvm *kvm, u32 id, u64 val,
+static int __modify_kvm_id_reg(struct kvm *kvm, u32 id, u64 val,
 			     u64 preserve_mask)
 {
 	u64 old, new;
 
-	mutex_lock(&kvm->lock);
+	lockdep_assert_held(&kvm->lock);
 
 	old = kvm->arch.id_regs[IDREG_IDX(id)];
 
@@ -1179,11 +1267,21 @@ static int modify_kvm_id_reg(struct kvm *kvm, u32 id, u64 val,
 		return -EBUSY;
 
 	WRITE_ONCE(kvm->arch.id_regs[IDREG_IDX(id)], new);
-	mutex_unlock(&kvm->lock);
 
 	return 0;
 }
 
+static int modify_kvm_id_reg(struct kvm *kvm, u32 id, u64 val,
+			     u64 preserve_mask)
+{
+	int ret;
+
+	mutex_lock(&kvm->lock);
+	ret = __modify_kvm_id_reg(kvm, id, val, preserve_mask);
+	mutex_unlock(&kvm->lock);
+
+	return ret;
+}
 static int write_kvm_id_reg(struct kvm *kvm, u32 id, u64 val)
 {
 	return modify_kvm_id_reg(kvm, id, val, 0);
@@ -1233,20 +1331,6 @@ static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
 		val &= ~(id_reg->vcpu_mask(vcpu, id_reg));
 
 	switch (id) {
-	case SYS_ID_AA64PFR0_EL1:
-		if (!vcpu_has_sve(vcpu))
-			val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_SVE);
-		val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_AMU);
-		val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_CSV2);
-		val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_CSV2), (u64)vcpu->kvm->arch.pfr0_csv2);
-		val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_CSV3);
-		val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_CSV3), (u64)vcpu->kvm->arch.pfr0_csv3);
-		if (irqchip_in_kernel(vcpu->kvm) &&
-		    vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3) {
-			val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_GIC);
-			val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_GIC), 1);
-		}
-		break;
 	case SYS_ID_AA64PFR1_EL1:
 		if (!kvm_has_mte(vcpu->kvm))
 			val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_MTE);
@@ -1347,48 +1431,6 @@ static unsigned int sve_visibility(const struct kvm_vcpu *vcpu,
 	return REG_HIDDEN;
 }
 
-static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
-			       const struct sys_reg_desc *rd,
-			       const struct kvm_one_reg *reg, void __user *uaddr)
-{
-	const u64 id = sys_reg_to_index(rd);
-	u8 csv2, csv3;
-	int err;
-	u64 val;
-
-	err = reg_from_user(&val, uaddr, id);
-	if (err)
-		return err;
-
-	/*
-	 * Allow AA64PFR0_EL1.CSV2 to be set from userspace as long as
-	 * it doesn't promise more than what is actually provided (the
-	 * guest could otherwise be covered in ectoplasmic residue).
-	 */
-	csv2 = cpuid_feature_extract_unsigned_field(val, ID_AA64PFR0_CSV2_SHIFT);
-	if (csv2 > 1 ||
-	    (csv2 && arm64_get_spectre_v2_state() != SPECTRE_UNAFFECTED))
-		return -EINVAL;
-
-	/* Same thing for CSV3 */
-	csv3 = cpuid_feature_extract_unsigned_field(val, ID_AA64PFR0_CSV3_SHIFT);
-	if (csv3 > 1 ||
-	    (csv3 && arm64_get_meltdown_state() != SPECTRE_UNAFFECTED))
-		return -EINVAL;
-
-	/* We can only differ with CSV[23], and anything else is an error */
-	val ^= read_id_reg(vcpu, rd, false);
-	val &= ~((0xFUL << ID_AA64PFR0_CSV2_SHIFT) |
-		 (0xFUL << ID_AA64PFR0_CSV3_SHIFT));
-	if (val)
-		return -EINVAL;
-
-	vcpu->kvm->arch.pfr0_csv2 = csv2;
-	vcpu->kvm->arch.pfr0_csv3 = csv3 ;
-
-	return 0;
-}
-
 /* cpufeature ID register user accessors */
 static int __get_id_reg(const struct kvm_vcpu *vcpu,
 			const struct sys_reg_desc *rd, void __user *uaddr,
@@ -1702,8 +1744,7 @@ static const struct sys_reg_desc sys_reg_descs[] = {
 
 	/* AArch64 ID registers */
 	/* CRm=4 */
-	{ SYS_DESC(SYS_ID_AA64PFR0_EL1), .access = access_id_reg,
-	  .get_user = get_id_reg, .set_user = set_id_aa64pfr0_el1, },
+	ID_SANITISED(ID_AA64PFR0_EL1),
 	ID_SANITISED(ID_AA64PFR1_EL1),
 	ID_UNALLOCATED(4,2),
 	ID_UNALLOCATED(4,3),
@@ -3095,3 +3136,15 @@ void set_default_id_regs(struct kvm *kvm)
 		(void)write_kvm_id_reg(kvm, id, val);
 	}
 }
+
+/*
+ * Update the ID register's field with @fval for the guest.
+ * The caller is expected to hold the kvm->lock.
+ */
+int kvm_set_id_reg_feature(struct kvm *kvm, u32 id, u8 field_shift, u8 fval)
+{
+	u64 val = ((u64)fval & ARM64_FEATURE_FIELD_MASK) << field_shift;
+	u64 preserve_mask = ~(ARM64_FEATURE_FIELD_MASK << field_shift);
+
+	return __modify_kvm_id_reg(kvm, id, val, preserve_mask);
+}
diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c
index 0a06d0648970..28d9bf0e178c 100644
--- a/arch/arm64/kvm/vgic/vgic-init.c
+++ b/arch/arm64/kvm/vgic/vgic-init.c
@@ -116,6 +116,11 @@ int kvm_vgic_create(struct kvm *kvm, u32 type)
 	else
 		INIT_LIST_HEAD(&kvm->arch.vgic.rd_regions);
 
+	if (type == KVM_DEV_TYPE_ARM_VGIC_V3)
+		/* Set ID_AA64PFR0_EL1.GIC to 1 */
+		(void)kvm_set_id_reg_feature(kvm, SYS_ID_AA64PFR0_EL1,
+				     ID_AA64PFR0_GIC3, ID_AA64PFR0_GIC_SHIFT);
+
 out_unlock:
 	unlock_all_vcpus(kvm);
 	return ret;
-- 
2.34.1.448.ga2b2bfdf31-goog


^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 04/26] KVM: arm64: Make ID_AA64PFR0_EL1 writable
@ 2022-01-06  4:26   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, Will Deacon, Peter Shier, Paolo Bonzini, linux-arm-kernel

This patch adds id_reg_info for ID_AA64PFR0_EL1 to make it writable by
userspace.

Return an error if userspace tries to set SVE/GIC field of the register
to a value that conflicts with SVE/GIC configuration for the guest.
SIMD/FP/SVE fields of the requested value are validated according to
Arm ARM.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/include/asm/kvm_host.h |   1 +
 arch/arm64/include/asm/sysreg.h   |   2 +
 arch/arm64/kvm/sys_regs.c         | 177 +++++++++++++++++++-----------
 arch/arm64/kvm/vgic/vgic-init.c   |   5 +
 4 files changed, 123 insertions(+), 62 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index c789a0137f58..4509f9e7472d 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -749,6 +749,7 @@ long kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
 				struct kvm_arm_copy_mte_tags *copy_tags);
 
 void set_default_id_regs(struct kvm *kvm);
+int kvm_set_id_reg_feature(struct kvm *kvm, u32 id, u8 field_shift, u8 fval);
 
 /* Guest/host FPSIMD coordination helpers */
 int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu);
diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h
index 16b3f1a1d468..e26027817171 100644
--- a/arch/arm64/include/asm/sysreg.h
+++ b/arch/arm64/include/asm/sysreg.h
@@ -798,6 +798,7 @@
 #define ID_AA64PFR0_ASIMD_SUPPORTED	0x0
 #define ID_AA64PFR0_ELx_64BIT_ONLY	0x1
 #define ID_AA64PFR0_ELx_32BIT_64BIT	0x2
+#define ID_AA64PFR0_GIC3		0x1
 
 /* id_aa64pfr1 */
 #define ID_AA64PFR1_MPAMFRAC_SHIFT	16
@@ -1197,6 +1198,7 @@
 #define ICH_VTR_TDS_MASK	(1 << ICH_VTR_TDS_SHIFT)
 
 #define ARM64_FEATURE_FIELD_BITS	4
+#define ARM64_FEATURE_FIELD_MASK	((1ull << ARM64_FEATURE_FIELD_BITS) - 1)
 
 /* Create a mask for the feature bits of the specified feature. */
 #define ARM64_FEATURE_MASK(x)	(GENMASK_ULL(x##_SHIFT + ARM64_FEATURE_FIELD_BITS - 1, x##_SHIFT))
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 971018288bee..1eb5c5fb614f 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -318,6 +318,92 @@ static void id_reg_info_init(struct id_reg_info *id_reg)
 		id_reg->init(id_reg);
 }
 
+static int validate_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
+				    const struct id_reg_info *id_reg, u64 val)
+{
+	int fp, simd;
+	unsigned int gic;
+	bool vcpu_has_sve = vcpu_has_sve(vcpu);
+	bool pfr0_has_sve = id_aa64pfr0_sve(val);
+
+	simd = cpuid_feature_extract_signed_field(val, ID_AA64PFR0_ASIMD_SHIFT);
+	fp = cpuid_feature_extract_signed_field(val, ID_AA64PFR0_FP_SHIFT);
+	if (simd != fp)
+		return -EINVAL;
+
+	/* fp must be supported when sve is supported */
+	if (pfr0_has_sve && (fp < 0))
+		return -EINVAL;
+
+	/* Check if there is a conflict with a request via KVM_ARM_VCPU_INIT */
+	if (vcpu_has_sve ^ pfr0_has_sve)
+		return -EPERM;
+
+	if ((irqchip_in_kernel(vcpu->kvm) &&
+	     vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3)) {
+		gic = cpuid_feature_extract_unsigned_field(val,
+							ID_AA64PFR0_GIC_SHIFT);
+		if (gic == 0)
+			return -EPERM;
+
+		if (gic > ID_AA64PFR0_GIC3)
+			return -E2BIG;
+	} else {
+		u64 mask = ARM64_FEATURE_MASK(ID_AA64PFR0_GIC);
+		int err = arm64_check_features(id_reg->sys_reg, val & mask,
+					       id_reg->vcpu_limit_val & mask);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+static void init_id_aa64pfr0_el1_info(struct id_reg_info *id_reg)
+{
+	u64 limit = id_reg->vcpu_limit_val;
+	unsigned int gic;
+
+	limit &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_AMU);
+	if (!system_supports_sve())
+		limit &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_SVE);
+
+	/*
+	 * The default is to expose CSV2 == 1 and CSV3 == 1 if the HW
+	 * isn't affected.  Userspace can override this as long as it
+	 * doesn't promise the impossible.
+	 */
+	limit &= ~(ARM64_FEATURE_MASK(ID_AA64PFR0_CSV2) |
+		   ARM64_FEATURE_MASK(ID_AA64PFR0_CSV3));
+
+	if (arm64_get_spectre_v2_state() == SPECTRE_UNAFFECTED)
+		limit |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_CSV2), 1);
+	if (arm64_get_meltdown_state() == SPECTRE_UNAFFECTED)
+		limit |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_CSV3), 1);
+
+	gic = cpuid_feature_extract_unsigned_field(limit, ID_AA64PFR0_GIC_SHIFT);
+	if (gic > 1) {
+		/* Limit to GICv3.0/4.0 */
+		limit &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_GIC);
+		limit |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_GIC), ID_AA64PFR0_GIC3);
+	}
+	id_reg->vcpu_limit_val = limit;
+}
+
+static u64 vcpu_mask_id_aa64pfr0_el1(const struct kvm_vcpu *vcpu,
+					 const struct id_reg_info *idr)
+{
+	return vcpu_has_sve(vcpu) ? 0 : ARM64_FEATURE_MASK(ID_AA64PFR0_SVE);
+}
+
+static struct id_reg_info id_aa64pfr0_el1_info = {
+	.sys_reg = SYS_ID_AA64PFR0_EL1,
+	.ignore_mask = ARM64_FEATURE_MASK(ID_AA64PFR0_GIC),
+	.init = init_id_aa64pfr0_el1_info,
+	.validate = validate_id_aa64pfr0_el1,
+	.vcpu_mask = vcpu_mask_id_aa64pfr0_el1,
+};
+
 /*
  * An ID register that needs special handling to control the value for the
  * guest must have its own id_reg_info in id_reg_info_table.
@@ -326,7 +412,9 @@ static void id_reg_info_init(struct id_reg_info *id_reg)
  * validation, etc.)
  */
 #define	GET_ID_REG_INFO(id)	(id_reg_info_table[IDREG_IDX(id)])
-static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {};
+static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {
+	[IDREG_IDX(SYS_ID_AA64PFR0_EL1)] = &id_aa64pfr0_el1_info,
+};
 
 static int validate_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
 {
@@ -1161,12 +1249,12 @@ static u64 read_kvm_id_reg(struct kvm *kvm, u32 id)
 	return kvm->arch.id_regs[IDREG_IDX(id)];
 }
 
-static int modify_kvm_id_reg(struct kvm *kvm, u32 id, u64 val,
+static int __modify_kvm_id_reg(struct kvm *kvm, u32 id, u64 val,
 			     u64 preserve_mask)
 {
 	u64 old, new;
 
-	mutex_lock(&kvm->lock);
+	lockdep_assert_held(&kvm->lock);
 
 	old = kvm->arch.id_regs[IDREG_IDX(id)];
 
@@ -1179,11 +1267,21 @@ static int modify_kvm_id_reg(struct kvm *kvm, u32 id, u64 val,
 		return -EBUSY;
 
 	WRITE_ONCE(kvm->arch.id_regs[IDREG_IDX(id)], new);
-	mutex_unlock(&kvm->lock);
 
 	return 0;
 }
 
+static int modify_kvm_id_reg(struct kvm *kvm, u32 id, u64 val,
+			     u64 preserve_mask)
+{
+	int ret;
+
+	mutex_lock(&kvm->lock);
+	ret = __modify_kvm_id_reg(kvm, id, val, preserve_mask);
+	mutex_unlock(&kvm->lock);
+
+	return ret;
+}
 static int write_kvm_id_reg(struct kvm *kvm, u32 id, u64 val)
 {
 	return modify_kvm_id_reg(kvm, id, val, 0);
@@ -1233,20 +1331,6 @@ static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
 		val &= ~(id_reg->vcpu_mask(vcpu, id_reg));
 
 	switch (id) {
-	case SYS_ID_AA64PFR0_EL1:
-		if (!vcpu_has_sve(vcpu))
-			val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_SVE);
-		val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_AMU);
-		val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_CSV2);
-		val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_CSV2), (u64)vcpu->kvm->arch.pfr0_csv2);
-		val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_CSV3);
-		val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_CSV3), (u64)vcpu->kvm->arch.pfr0_csv3);
-		if (irqchip_in_kernel(vcpu->kvm) &&
-		    vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3) {
-			val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_GIC);
-			val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_GIC), 1);
-		}
-		break;
 	case SYS_ID_AA64PFR1_EL1:
 		if (!kvm_has_mte(vcpu->kvm))
 			val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_MTE);
@@ -1347,48 +1431,6 @@ static unsigned int sve_visibility(const struct kvm_vcpu *vcpu,
 	return REG_HIDDEN;
 }
 
-static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
-			       const struct sys_reg_desc *rd,
-			       const struct kvm_one_reg *reg, void __user *uaddr)
-{
-	const u64 id = sys_reg_to_index(rd);
-	u8 csv2, csv3;
-	int err;
-	u64 val;
-
-	err = reg_from_user(&val, uaddr, id);
-	if (err)
-		return err;
-
-	/*
-	 * Allow AA64PFR0_EL1.CSV2 to be set from userspace as long as
-	 * it doesn't promise more than what is actually provided (the
-	 * guest could otherwise be covered in ectoplasmic residue).
-	 */
-	csv2 = cpuid_feature_extract_unsigned_field(val, ID_AA64PFR0_CSV2_SHIFT);
-	if (csv2 > 1 ||
-	    (csv2 && arm64_get_spectre_v2_state() != SPECTRE_UNAFFECTED))
-		return -EINVAL;
-
-	/* Same thing for CSV3 */
-	csv3 = cpuid_feature_extract_unsigned_field(val, ID_AA64PFR0_CSV3_SHIFT);
-	if (csv3 > 1 ||
-	    (csv3 && arm64_get_meltdown_state() != SPECTRE_UNAFFECTED))
-		return -EINVAL;
-
-	/* We can only differ with CSV[23], and anything else is an error */
-	val ^= read_id_reg(vcpu, rd, false);
-	val &= ~((0xFUL << ID_AA64PFR0_CSV2_SHIFT) |
-		 (0xFUL << ID_AA64PFR0_CSV3_SHIFT));
-	if (val)
-		return -EINVAL;
-
-	vcpu->kvm->arch.pfr0_csv2 = csv2;
-	vcpu->kvm->arch.pfr0_csv3 = csv3 ;
-
-	return 0;
-}
-
 /* cpufeature ID register user accessors */
 static int __get_id_reg(const struct kvm_vcpu *vcpu,
 			const struct sys_reg_desc *rd, void __user *uaddr,
@@ -1702,8 +1744,7 @@ static const struct sys_reg_desc sys_reg_descs[] = {
 
 	/* AArch64 ID registers */
 	/* CRm=4 */
-	{ SYS_DESC(SYS_ID_AA64PFR0_EL1), .access = access_id_reg,
-	  .get_user = get_id_reg, .set_user = set_id_aa64pfr0_el1, },
+	ID_SANITISED(ID_AA64PFR0_EL1),
 	ID_SANITISED(ID_AA64PFR1_EL1),
 	ID_UNALLOCATED(4,2),
 	ID_UNALLOCATED(4,3),
@@ -3095,3 +3136,15 @@ void set_default_id_regs(struct kvm *kvm)
 		(void)write_kvm_id_reg(kvm, id, val);
 	}
 }
+
+/*
+ * Update the ID register's field with @fval for the guest.
+ * The caller is expected to hold the kvm->lock.
+ */
+int kvm_set_id_reg_feature(struct kvm *kvm, u32 id, u8 field_shift, u8 fval)
+{
+	u64 val = ((u64)fval & ARM64_FEATURE_FIELD_MASK) << field_shift;
+	u64 preserve_mask = ~(ARM64_FEATURE_FIELD_MASK << field_shift);
+
+	return __modify_kvm_id_reg(kvm, id, val, preserve_mask);
+}
diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c
index 0a06d0648970..28d9bf0e178c 100644
--- a/arch/arm64/kvm/vgic/vgic-init.c
+++ b/arch/arm64/kvm/vgic/vgic-init.c
@@ -116,6 +116,11 @@ int kvm_vgic_create(struct kvm *kvm, u32 type)
 	else
 		INIT_LIST_HEAD(&kvm->arch.vgic.rd_regions);
 
+	if (type == KVM_DEV_TYPE_ARM_VGIC_V3)
+		/* Set ID_AA64PFR0_EL1.GIC to 1 */
+		(void)kvm_set_id_reg_feature(kvm, SYS_ID_AA64PFR0_EL1,
+				     ID_AA64PFR0_GIC3, ID_AA64PFR0_GIC_SHIFT);
+
 out_unlock:
 	unlock_all_vcpus(kvm);
 	return ret;
-- 
2.34.1.448.ga2b2bfdf31-goog

_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 04/26] KVM: arm64: Make ID_AA64PFR0_EL1 writable
@ 2022-01-06  4:26   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

This patch adds id_reg_info for ID_AA64PFR0_EL1 to make it writable by
userspace.

Return an error if userspace tries to set SVE/GIC field of the register
to a value that conflicts with SVE/GIC configuration for the guest.
SIMD/FP/SVE fields of the requested value are validated according to
Arm ARM.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/include/asm/kvm_host.h |   1 +
 arch/arm64/include/asm/sysreg.h   |   2 +
 arch/arm64/kvm/sys_regs.c         | 177 +++++++++++++++++++-----------
 arch/arm64/kvm/vgic/vgic-init.c   |   5 +
 4 files changed, 123 insertions(+), 62 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index c789a0137f58..4509f9e7472d 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -749,6 +749,7 @@ long kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
 				struct kvm_arm_copy_mte_tags *copy_tags);
 
 void set_default_id_regs(struct kvm *kvm);
+int kvm_set_id_reg_feature(struct kvm *kvm, u32 id, u8 field_shift, u8 fval);
 
 /* Guest/host FPSIMD coordination helpers */
 int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu);
diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h
index 16b3f1a1d468..e26027817171 100644
--- a/arch/arm64/include/asm/sysreg.h
+++ b/arch/arm64/include/asm/sysreg.h
@@ -798,6 +798,7 @@
 #define ID_AA64PFR0_ASIMD_SUPPORTED	0x0
 #define ID_AA64PFR0_ELx_64BIT_ONLY	0x1
 #define ID_AA64PFR0_ELx_32BIT_64BIT	0x2
+#define ID_AA64PFR0_GIC3		0x1
 
 /* id_aa64pfr1 */
 #define ID_AA64PFR1_MPAMFRAC_SHIFT	16
@@ -1197,6 +1198,7 @@
 #define ICH_VTR_TDS_MASK	(1 << ICH_VTR_TDS_SHIFT)
 
 #define ARM64_FEATURE_FIELD_BITS	4
+#define ARM64_FEATURE_FIELD_MASK	((1ull << ARM64_FEATURE_FIELD_BITS) - 1)
 
 /* Create a mask for the feature bits of the specified feature. */
 #define ARM64_FEATURE_MASK(x)	(GENMASK_ULL(x##_SHIFT + ARM64_FEATURE_FIELD_BITS - 1, x##_SHIFT))
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 971018288bee..1eb5c5fb614f 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -318,6 +318,92 @@ static void id_reg_info_init(struct id_reg_info *id_reg)
 		id_reg->init(id_reg);
 }
 
+static int validate_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
+				    const struct id_reg_info *id_reg, u64 val)
+{
+	int fp, simd;
+	unsigned int gic;
+	bool vcpu_has_sve = vcpu_has_sve(vcpu);
+	bool pfr0_has_sve = id_aa64pfr0_sve(val);
+
+	simd = cpuid_feature_extract_signed_field(val, ID_AA64PFR0_ASIMD_SHIFT);
+	fp = cpuid_feature_extract_signed_field(val, ID_AA64PFR0_FP_SHIFT);
+	if (simd != fp)
+		return -EINVAL;
+
+	/* fp must be supported when sve is supported */
+	if (pfr0_has_sve && (fp < 0))
+		return -EINVAL;
+
+	/* Check if there is a conflict with a request via KVM_ARM_VCPU_INIT */
+	if (vcpu_has_sve ^ pfr0_has_sve)
+		return -EPERM;
+
+	if ((irqchip_in_kernel(vcpu->kvm) &&
+	     vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3)) {
+		gic = cpuid_feature_extract_unsigned_field(val,
+							ID_AA64PFR0_GIC_SHIFT);
+		if (gic == 0)
+			return -EPERM;
+
+		if (gic > ID_AA64PFR0_GIC3)
+			return -E2BIG;
+	} else {
+		u64 mask = ARM64_FEATURE_MASK(ID_AA64PFR0_GIC);
+		int err = arm64_check_features(id_reg->sys_reg, val & mask,
+					       id_reg->vcpu_limit_val & mask);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+static void init_id_aa64pfr0_el1_info(struct id_reg_info *id_reg)
+{
+	u64 limit = id_reg->vcpu_limit_val;
+	unsigned int gic;
+
+	limit &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_AMU);
+	if (!system_supports_sve())
+		limit &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_SVE);
+
+	/*
+	 * The default is to expose CSV2 == 1 and CSV3 == 1 if the HW
+	 * isn't affected.  Userspace can override this as long as it
+	 * doesn't promise the impossible.
+	 */
+	limit &= ~(ARM64_FEATURE_MASK(ID_AA64PFR0_CSV2) |
+		   ARM64_FEATURE_MASK(ID_AA64PFR0_CSV3));
+
+	if (arm64_get_spectre_v2_state() == SPECTRE_UNAFFECTED)
+		limit |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_CSV2), 1);
+	if (arm64_get_meltdown_state() == SPECTRE_UNAFFECTED)
+		limit |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_CSV3), 1);
+
+	gic = cpuid_feature_extract_unsigned_field(limit, ID_AA64PFR0_GIC_SHIFT);
+	if (gic > 1) {
+		/* Limit to GICv3.0/4.0 */
+		limit &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_GIC);
+		limit |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_GIC), ID_AA64PFR0_GIC3);
+	}
+	id_reg->vcpu_limit_val = limit;
+}
+
+static u64 vcpu_mask_id_aa64pfr0_el1(const struct kvm_vcpu *vcpu,
+					 const struct id_reg_info *idr)
+{
+	return vcpu_has_sve(vcpu) ? 0 : ARM64_FEATURE_MASK(ID_AA64PFR0_SVE);
+}
+
+static struct id_reg_info id_aa64pfr0_el1_info = {
+	.sys_reg = SYS_ID_AA64PFR0_EL1,
+	.ignore_mask = ARM64_FEATURE_MASK(ID_AA64PFR0_GIC),
+	.init = init_id_aa64pfr0_el1_info,
+	.validate = validate_id_aa64pfr0_el1,
+	.vcpu_mask = vcpu_mask_id_aa64pfr0_el1,
+};
+
 /*
  * An ID register that needs special handling to control the value for the
  * guest must have its own id_reg_info in id_reg_info_table.
@@ -326,7 +412,9 @@ static void id_reg_info_init(struct id_reg_info *id_reg)
  * validation, etc.)
  */
 #define	GET_ID_REG_INFO(id)	(id_reg_info_table[IDREG_IDX(id)])
-static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {};
+static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {
+	[IDREG_IDX(SYS_ID_AA64PFR0_EL1)] = &id_aa64pfr0_el1_info,
+};
 
 static int validate_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
 {
@@ -1161,12 +1249,12 @@ static u64 read_kvm_id_reg(struct kvm *kvm, u32 id)
 	return kvm->arch.id_regs[IDREG_IDX(id)];
 }
 
-static int modify_kvm_id_reg(struct kvm *kvm, u32 id, u64 val,
+static int __modify_kvm_id_reg(struct kvm *kvm, u32 id, u64 val,
 			     u64 preserve_mask)
 {
 	u64 old, new;
 
-	mutex_lock(&kvm->lock);
+	lockdep_assert_held(&kvm->lock);
 
 	old = kvm->arch.id_regs[IDREG_IDX(id)];
 
@@ -1179,11 +1267,21 @@ static int modify_kvm_id_reg(struct kvm *kvm, u32 id, u64 val,
 		return -EBUSY;
 
 	WRITE_ONCE(kvm->arch.id_regs[IDREG_IDX(id)], new);
-	mutex_unlock(&kvm->lock);
 
 	return 0;
 }
 
+static int modify_kvm_id_reg(struct kvm *kvm, u32 id, u64 val,
+			     u64 preserve_mask)
+{
+	int ret;
+
+	mutex_lock(&kvm->lock);
+	ret = __modify_kvm_id_reg(kvm, id, val, preserve_mask);
+	mutex_unlock(&kvm->lock);
+
+	return ret;
+}
 static int write_kvm_id_reg(struct kvm *kvm, u32 id, u64 val)
 {
 	return modify_kvm_id_reg(kvm, id, val, 0);
@@ -1233,20 +1331,6 @@ static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
 		val &= ~(id_reg->vcpu_mask(vcpu, id_reg));
 
 	switch (id) {
-	case SYS_ID_AA64PFR0_EL1:
-		if (!vcpu_has_sve(vcpu))
-			val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_SVE);
-		val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_AMU);
-		val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_CSV2);
-		val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_CSV2), (u64)vcpu->kvm->arch.pfr0_csv2);
-		val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_CSV3);
-		val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_CSV3), (u64)vcpu->kvm->arch.pfr0_csv3);
-		if (irqchip_in_kernel(vcpu->kvm) &&
-		    vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3) {
-			val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_GIC);
-			val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_GIC), 1);
-		}
-		break;
 	case SYS_ID_AA64PFR1_EL1:
 		if (!kvm_has_mte(vcpu->kvm))
 			val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_MTE);
@@ -1347,48 +1431,6 @@ static unsigned int sve_visibility(const struct kvm_vcpu *vcpu,
 	return REG_HIDDEN;
 }
 
-static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
-			       const struct sys_reg_desc *rd,
-			       const struct kvm_one_reg *reg, void __user *uaddr)
-{
-	const u64 id = sys_reg_to_index(rd);
-	u8 csv2, csv3;
-	int err;
-	u64 val;
-
-	err = reg_from_user(&val, uaddr, id);
-	if (err)
-		return err;
-
-	/*
-	 * Allow AA64PFR0_EL1.CSV2 to be set from userspace as long as
-	 * it doesn't promise more than what is actually provided (the
-	 * guest could otherwise be covered in ectoplasmic residue).
-	 */
-	csv2 = cpuid_feature_extract_unsigned_field(val, ID_AA64PFR0_CSV2_SHIFT);
-	if (csv2 > 1 ||
-	    (csv2 && arm64_get_spectre_v2_state() != SPECTRE_UNAFFECTED))
-		return -EINVAL;
-
-	/* Same thing for CSV3 */
-	csv3 = cpuid_feature_extract_unsigned_field(val, ID_AA64PFR0_CSV3_SHIFT);
-	if (csv3 > 1 ||
-	    (csv3 && arm64_get_meltdown_state() != SPECTRE_UNAFFECTED))
-		return -EINVAL;
-
-	/* We can only differ with CSV[23], and anything else is an error */
-	val ^= read_id_reg(vcpu, rd, false);
-	val &= ~((0xFUL << ID_AA64PFR0_CSV2_SHIFT) |
-		 (0xFUL << ID_AA64PFR0_CSV3_SHIFT));
-	if (val)
-		return -EINVAL;
-
-	vcpu->kvm->arch.pfr0_csv2 = csv2;
-	vcpu->kvm->arch.pfr0_csv3 = csv3 ;
-
-	return 0;
-}
-
 /* cpufeature ID register user accessors */
 static int __get_id_reg(const struct kvm_vcpu *vcpu,
 			const struct sys_reg_desc *rd, void __user *uaddr,
@@ -1702,8 +1744,7 @@ static const struct sys_reg_desc sys_reg_descs[] = {
 
 	/* AArch64 ID registers */
 	/* CRm=4 */
-	{ SYS_DESC(SYS_ID_AA64PFR0_EL1), .access = access_id_reg,
-	  .get_user = get_id_reg, .set_user = set_id_aa64pfr0_el1, },
+	ID_SANITISED(ID_AA64PFR0_EL1),
 	ID_SANITISED(ID_AA64PFR1_EL1),
 	ID_UNALLOCATED(4,2),
 	ID_UNALLOCATED(4,3),
@@ -3095,3 +3136,15 @@ void set_default_id_regs(struct kvm *kvm)
 		(void)write_kvm_id_reg(kvm, id, val);
 	}
 }
+
+/*
+ * Update the ID register's field with @fval for the guest.
+ * The caller is expected to hold the kvm->lock.
+ */
+int kvm_set_id_reg_feature(struct kvm *kvm, u32 id, u8 field_shift, u8 fval)
+{
+	u64 val = ((u64)fval & ARM64_FEATURE_FIELD_MASK) << field_shift;
+	u64 preserve_mask = ~(ARM64_FEATURE_FIELD_MASK << field_shift);
+
+	return __modify_kvm_id_reg(kvm, id, val, preserve_mask);
+}
diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c
index 0a06d0648970..28d9bf0e178c 100644
--- a/arch/arm64/kvm/vgic/vgic-init.c
+++ b/arch/arm64/kvm/vgic/vgic-init.c
@@ -116,6 +116,11 @@ int kvm_vgic_create(struct kvm *kvm, u32 type)
 	else
 		INIT_LIST_HEAD(&kvm->arch.vgic.rd_regions);
 
+	if (type == KVM_DEV_TYPE_ARM_VGIC_V3)
+		/* Set ID_AA64PFR0_EL1.GIC to 1 */
+		(void)kvm_set_id_reg_feature(kvm, SYS_ID_AA64PFR0_EL1,
+				     ID_AA64PFR0_GIC3, ID_AA64PFR0_GIC_SHIFT);
+
 out_unlock:
 	unlock_all_vcpus(kvm);
 	return ret;
-- 
2.34.1.448.ga2b2bfdf31-goog


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 05/26] KVM: arm64: Make ID_AA64PFR1_EL1 writable
  2022-01-06  4:26 ` Reiji Watanabe
  (?)
@ 2022-01-06  4:26   ` Reiji Watanabe
  -1 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

This patch adds id_reg_info for ID_AA64PFR1_EL1 to make it writable
by userspace.

Return an error if userspace tries to set MTE field of the register
to a value that conflicts with KVM_CAP_ARM_MTE configuration for
the guest.
Skip fractional feature fields validation at present and they will
be handled by the following patches.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/include/asm/sysreg.h |  1 +
 arch/arm64/kvm/sys_regs.c       | 42 +++++++++++++++++++++++++++++----
 2 files changed, 39 insertions(+), 4 deletions(-)

diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h
index e26027817171..f55513002281 100644
--- a/arch/arm64/include/asm/sysreg.h
+++ b/arch/arm64/include/asm/sysreg.h
@@ -801,6 +801,7 @@
 #define ID_AA64PFR0_GIC3		0x1
 
 /* id_aa64pfr1 */
+#define ID_AA64PFR1_CSV2FRAC_SHIFT	32
 #define ID_AA64PFR1_MPAMFRAC_SHIFT	16
 #define ID_AA64PFR1_RASFRAC_SHIFT	12
 #define ID_AA64PFR1_MTE_SHIFT		8
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 1eb5c5fb614f..ae9ca341a2e4 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -359,6 +359,21 @@ static int validate_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
 	return 0;
 }
 
+static int validate_id_aa64pfr1_el1(struct kvm_vcpu *vcpu,
+				    const struct id_reg_info *id_reg, u64 val)
+{
+	bool kvm_mte = kvm_has_mte(vcpu->kvm);
+	unsigned int mte;
+
+	mte = cpuid_feature_extract_unsigned_field(val, ID_AA64PFR1_MTE_SHIFT);
+
+	/* Check if there is a conflict with a request via KVM_ARM_VCPU_INIT. */
+	if (kvm_mte ^ (mte > 0))
+		return -EPERM;
+
+	return 0;
+}
+
 static void init_id_aa64pfr0_el1_info(struct id_reg_info *id_reg)
 {
 	u64 limit = id_reg->vcpu_limit_val;
@@ -390,12 +405,24 @@ static void init_id_aa64pfr0_el1_info(struct id_reg_info *id_reg)
 	id_reg->vcpu_limit_val = limit;
 }
 
+static void init_id_aa64pfr1_el1_info(struct id_reg_info *id_reg)
+{
+	if (!system_supports_mte())
+		id_reg->vcpu_limit_val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_MTE);
+}
+
 static u64 vcpu_mask_id_aa64pfr0_el1(const struct kvm_vcpu *vcpu,
 					 const struct id_reg_info *idr)
 {
 	return vcpu_has_sve(vcpu) ? 0 : ARM64_FEATURE_MASK(ID_AA64PFR0_SVE);
 }
 
+static u64 vcpu_mask_id_aa64pfr1_el1(const struct kvm_vcpu *vcpu,
+					 const struct id_reg_info *idr)
+{
+	return kvm_has_mte(vcpu->kvm) ? 0 : (ARM64_FEATURE_MASK(ID_AA64PFR1_MTE));
+}
+
 static struct id_reg_info id_aa64pfr0_el1_info = {
 	.sys_reg = SYS_ID_AA64PFR0_EL1,
 	.ignore_mask = ARM64_FEATURE_MASK(ID_AA64PFR0_GIC),
@@ -404,6 +431,16 @@ static struct id_reg_info id_aa64pfr0_el1_info = {
 	.vcpu_mask = vcpu_mask_id_aa64pfr0_el1,
 };
 
+static struct id_reg_info id_aa64pfr1_el1_info = {
+	.sys_reg = SYS_ID_AA64PFR1_EL1,
+	.ignore_mask = ARM64_FEATURE_MASK(ID_AA64PFR1_RASFRAC) |
+		       ARM64_FEATURE_MASK(ID_AA64PFR1_MPAMFRAC) |
+		       ARM64_FEATURE_MASK(ID_AA64PFR1_CSV2FRAC),
+	.init = init_id_aa64pfr1_el1_info,
+	.validate = validate_id_aa64pfr1_el1,
+	.vcpu_mask = vcpu_mask_id_aa64pfr1_el1,
+};
+
 /*
  * An ID register that needs special handling to control the value for the
  * guest must have its own id_reg_info in id_reg_info_table.
@@ -414,6 +451,7 @@ static struct id_reg_info id_aa64pfr0_el1_info = {
 #define	GET_ID_REG_INFO(id)	(id_reg_info_table[IDREG_IDX(id)])
 static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {
 	[IDREG_IDX(SYS_ID_AA64PFR0_EL1)] = &id_aa64pfr0_el1_info,
+	[IDREG_IDX(SYS_ID_AA64PFR1_EL1)] = &id_aa64pfr1_el1_info,
 };
 
 static int validate_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
@@ -1331,10 +1369,6 @@ static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
 		val &= ~(id_reg->vcpu_mask(vcpu, id_reg));
 
 	switch (id) {
-	case SYS_ID_AA64PFR1_EL1:
-		if (!kvm_has_mte(vcpu->kvm))
-			val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_MTE);
-		break;
 	case SYS_ID_AA64ISAR1_EL1:
 		if (!vcpu_has_ptrauth(vcpu))
 			val &= ~(ARM64_FEATURE_MASK(ID_AA64ISAR1_APA) |
-- 
2.34.1.448.ga2b2bfdf31-goog


^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 05/26] KVM: arm64: Make ID_AA64PFR1_EL1 writable
@ 2022-01-06  4:26   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, Will Deacon, Peter Shier, Paolo Bonzini, linux-arm-kernel

This patch adds id_reg_info for ID_AA64PFR1_EL1 to make it writable
by userspace.

Return an error if userspace tries to set MTE field of the register
to a value that conflicts with KVM_CAP_ARM_MTE configuration for
the guest.
Skip fractional feature fields validation at present and they will
be handled by the following patches.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/include/asm/sysreg.h |  1 +
 arch/arm64/kvm/sys_regs.c       | 42 +++++++++++++++++++++++++++++----
 2 files changed, 39 insertions(+), 4 deletions(-)

diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h
index e26027817171..f55513002281 100644
--- a/arch/arm64/include/asm/sysreg.h
+++ b/arch/arm64/include/asm/sysreg.h
@@ -801,6 +801,7 @@
 #define ID_AA64PFR0_GIC3		0x1
 
 /* id_aa64pfr1 */
+#define ID_AA64PFR1_CSV2FRAC_SHIFT	32
 #define ID_AA64PFR1_MPAMFRAC_SHIFT	16
 #define ID_AA64PFR1_RASFRAC_SHIFT	12
 #define ID_AA64PFR1_MTE_SHIFT		8
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 1eb5c5fb614f..ae9ca341a2e4 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -359,6 +359,21 @@ static int validate_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
 	return 0;
 }
 
+static int validate_id_aa64pfr1_el1(struct kvm_vcpu *vcpu,
+				    const struct id_reg_info *id_reg, u64 val)
+{
+	bool kvm_mte = kvm_has_mte(vcpu->kvm);
+	unsigned int mte;
+
+	mte = cpuid_feature_extract_unsigned_field(val, ID_AA64PFR1_MTE_SHIFT);
+
+	/* Check if there is a conflict with a request via KVM_ARM_VCPU_INIT. */
+	if (kvm_mte ^ (mte > 0))
+		return -EPERM;
+
+	return 0;
+}
+
 static void init_id_aa64pfr0_el1_info(struct id_reg_info *id_reg)
 {
 	u64 limit = id_reg->vcpu_limit_val;
@@ -390,12 +405,24 @@ static void init_id_aa64pfr0_el1_info(struct id_reg_info *id_reg)
 	id_reg->vcpu_limit_val = limit;
 }
 
+static void init_id_aa64pfr1_el1_info(struct id_reg_info *id_reg)
+{
+	if (!system_supports_mte())
+		id_reg->vcpu_limit_val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_MTE);
+}
+
 static u64 vcpu_mask_id_aa64pfr0_el1(const struct kvm_vcpu *vcpu,
 					 const struct id_reg_info *idr)
 {
 	return vcpu_has_sve(vcpu) ? 0 : ARM64_FEATURE_MASK(ID_AA64PFR0_SVE);
 }
 
+static u64 vcpu_mask_id_aa64pfr1_el1(const struct kvm_vcpu *vcpu,
+					 const struct id_reg_info *idr)
+{
+	return kvm_has_mte(vcpu->kvm) ? 0 : (ARM64_FEATURE_MASK(ID_AA64PFR1_MTE));
+}
+
 static struct id_reg_info id_aa64pfr0_el1_info = {
 	.sys_reg = SYS_ID_AA64PFR0_EL1,
 	.ignore_mask = ARM64_FEATURE_MASK(ID_AA64PFR0_GIC),
@@ -404,6 +431,16 @@ static struct id_reg_info id_aa64pfr0_el1_info = {
 	.vcpu_mask = vcpu_mask_id_aa64pfr0_el1,
 };
 
+static struct id_reg_info id_aa64pfr1_el1_info = {
+	.sys_reg = SYS_ID_AA64PFR1_EL1,
+	.ignore_mask = ARM64_FEATURE_MASK(ID_AA64PFR1_RASFRAC) |
+		       ARM64_FEATURE_MASK(ID_AA64PFR1_MPAMFRAC) |
+		       ARM64_FEATURE_MASK(ID_AA64PFR1_CSV2FRAC),
+	.init = init_id_aa64pfr1_el1_info,
+	.validate = validate_id_aa64pfr1_el1,
+	.vcpu_mask = vcpu_mask_id_aa64pfr1_el1,
+};
+
 /*
  * An ID register that needs special handling to control the value for the
  * guest must have its own id_reg_info in id_reg_info_table.
@@ -414,6 +451,7 @@ static struct id_reg_info id_aa64pfr0_el1_info = {
 #define	GET_ID_REG_INFO(id)	(id_reg_info_table[IDREG_IDX(id)])
 static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {
 	[IDREG_IDX(SYS_ID_AA64PFR0_EL1)] = &id_aa64pfr0_el1_info,
+	[IDREG_IDX(SYS_ID_AA64PFR1_EL1)] = &id_aa64pfr1_el1_info,
 };
 
 static int validate_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
@@ -1331,10 +1369,6 @@ static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
 		val &= ~(id_reg->vcpu_mask(vcpu, id_reg));
 
 	switch (id) {
-	case SYS_ID_AA64PFR1_EL1:
-		if (!kvm_has_mte(vcpu->kvm))
-			val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_MTE);
-		break;
 	case SYS_ID_AA64ISAR1_EL1:
 		if (!vcpu_has_ptrauth(vcpu))
 			val &= ~(ARM64_FEATURE_MASK(ID_AA64ISAR1_APA) |
-- 
2.34.1.448.ga2b2bfdf31-goog

_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 05/26] KVM: arm64: Make ID_AA64PFR1_EL1 writable
@ 2022-01-06  4:26   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

This patch adds id_reg_info for ID_AA64PFR1_EL1 to make it writable
by userspace.

Return an error if userspace tries to set MTE field of the register
to a value that conflicts with KVM_CAP_ARM_MTE configuration for
the guest.
Skip fractional feature fields validation at present and they will
be handled by the following patches.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/include/asm/sysreg.h |  1 +
 arch/arm64/kvm/sys_regs.c       | 42 +++++++++++++++++++++++++++++----
 2 files changed, 39 insertions(+), 4 deletions(-)

diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h
index e26027817171..f55513002281 100644
--- a/arch/arm64/include/asm/sysreg.h
+++ b/arch/arm64/include/asm/sysreg.h
@@ -801,6 +801,7 @@
 #define ID_AA64PFR0_GIC3		0x1
 
 /* id_aa64pfr1 */
+#define ID_AA64PFR1_CSV2FRAC_SHIFT	32
 #define ID_AA64PFR1_MPAMFRAC_SHIFT	16
 #define ID_AA64PFR1_RASFRAC_SHIFT	12
 #define ID_AA64PFR1_MTE_SHIFT		8
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 1eb5c5fb614f..ae9ca341a2e4 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -359,6 +359,21 @@ static int validate_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
 	return 0;
 }
 
+static int validate_id_aa64pfr1_el1(struct kvm_vcpu *vcpu,
+				    const struct id_reg_info *id_reg, u64 val)
+{
+	bool kvm_mte = kvm_has_mte(vcpu->kvm);
+	unsigned int mte;
+
+	mte = cpuid_feature_extract_unsigned_field(val, ID_AA64PFR1_MTE_SHIFT);
+
+	/* Check if there is a conflict with a request via KVM_ARM_VCPU_INIT. */
+	if (kvm_mte ^ (mte > 0))
+		return -EPERM;
+
+	return 0;
+}
+
 static void init_id_aa64pfr0_el1_info(struct id_reg_info *id_reg)
 {
 	u64 limit = id_reg->vcpu_limit_val;
@@ -390,12 +405,24 @@ static void init_id_aa64pfr0_el1_info(struct id_reg_info *id_reg)
 	id_reg->vcpu_limit_val = limit;
 }
 
+static void init_id_aa64pfr1_el1_info(struct id_reg_info *id_reg)
+{
+	if (!system_supports_mte())
+		id_reg->vcpu_limit_val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_MTE);
+}
+
 static u64 vcpu_mask_id_aa64pfr0_el1(const struct kvm_vcpu *vcpu,
 					 const struct id_reg_info *idr)
 {
 	return vcpu_has_sve(vcpu) ? 0 : ARM64_FEATURE_MASK(ID_AA64PFR0_SVE);
 }
 
+static u64 vcpu_mask_id_aa64pfr1_el1(const struct kvm_vcpu *vcpu,
+					 const struct id_reg_info *idr)
+{
+	return kvm_has_mte(vcpu->kvm) ? 0 : (ARM64_FEATURE_MASK(ID_AA64PFR1_MTE));
+}
+
 static struct id_reg_info id_aa64pfr0_el1_info = {
 	.sys_reg = SYS_ID_AA64PFR0_EL1,
 	.ignore_mask = ARM64_FEATURE_MASK(ID_AA64PFR0_GIC),
@@ -404,6 +431,16 @@ static struct id_reg_info id_aa64pfr0_el1_info = {
 	.vcpu_mask = vcpu_mask_id_aa64pfr0_el1,
 };
 
+static struct id_reg_info id_aa64pfr1_el1_info = {
+	.sys_reg = SYS_ID_AA64PFR1_EL1,
+	.ignore_mask = ARM64_FEATURE_MASK(ID_AA64PFR1_RASFRAC) |
+		       ARM64_FEATURE_MASK(ID_AA64PFR1_MPAMFRAC) |
+		       ARM64_FEATURE_MASK(ID_AA64PFR1_CSV2FRAC),
+	.init = init_id_aa64pfr1_el1_info,
+	.validate = validate_id_aa64pfr1_el1,
+	.vcpu_mask = vcpu_mask_id_aa64pfr1_el1,
+};
+
 /*
  * An ID register that needs special handling to control the value for the
  * guest must have its own id_reg_info in id_reg_info_table.
@@ -414,6 +451,7 @@ static struct id_reg_info id_aa64pfr0_el1_info = {
 #define	GET_ID_REG_INFO(id)	(id_reg_info_table[IDREG_IDX(id)])
 static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {
 	[IDREG_IDX(SYS_ID_AA64PFR0_EL1)] = &id_aa64pfr0_el1_info,
+	[IDREG_IDX(SYS_ID_AA64PFR1_EL1)] = &id_aa64pfr1_el1_info,
 };
 
 static int validate_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
@@ -1331,10 +1369,6 @@ static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
 		val &= ~(id_reg->vcpu_mask(vcpu, id_reg));
 
 	switch (id) {
-	case SYS_ID_AA64PFR1_EL1:
-		if (!kvm_has_mte(vcpu->kvm))
-			val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_MTE);
-		break;
 	case SYS_ID_AA64ISAR1_EL1:
 		if (!vcpu_has_ptrauth(vcpu))
 			val &= ~(ARM64_FEATURE_MASK(ID_AA64ISAR1_APA) |
-- 
2.34.1.448.ga2b2bfdf31-goog


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 06/26] KVM: arm64: Make ID_AA64ISAR0_EL1 writable
  2022-01-06  4:26 ` Reiji Watanabe
  (?)
@ 2022-01-06  4:26   ` Reiji Watanabe
  -1 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

This patch adds id_reg_info for ID_AA64ISAR0_EL1 to make it writable
by userspace.

Updating sm3, sm4, sha1, sha2 and sha3 fields are allowed only
if values of those fields follow Arm ARM.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/kvm/sys_regs.c | 29 +++++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)

diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index ae9ca341a2e4..3f1313875be5 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -374,6 +374,29 @@ static int validate_id_aa64pfr1_el1(struct kvm_vcpu *vcpu,
 	return 0;
 }
 
+static int validate_id_aa64isar0_el1(struct kvm_vcpu *vcpu,
+				     const struct id_reg_info *id_reg, u64 val)
+{
+	unsigned int sm3, sm4, sha1, sha2, sha3;
+
+	/* Run consistency checkings according to Arm ARM */
+	sm3 = cpuid_feature_extract_unsigned_field(val, ID_AA64ISAR0_SM3_SHIFT);
+	sm4 = cpuid_feature_extract_unsigned_field(val, ID_AA64ISAR0_SM4_SHIFT);
+	if (sm3 != sm4)
+		return -EINVAL;
+
+	sha1 = cpuid_feature_extract_unsigned_field(val, ID_AA64ISAR0_SHA1_SHIFT);
+	sha2 = cpuid_feature_extract_unsigned_field(val, ID_AA64ISAR0_SHA2_SHIFT);
+	if ((sha1 == 0) ^ (sha2 == 0))
+		return -EINVAL;
+
+	sha3 = cpuid_feature_extract_unsigned_field(val, ID_AA64ISAR0_SHA3_SHIFT);
+	if (((sha2 == 2) ^ (sha3 == 1)) || (!sha1 && sha3))
+		return -EINVAL;
+
+	return 0;
+}
+
 static void init_id_aa64pfr0_el1_info(struct id_reg_info *id_reg)
 {
 	u64 limit = id_reg->vcpu_limit_val;
@@ -441,6 +464,11 @@ static struct id_reg_info id_aa64pfr1_el1_info = {
 	.vcpu_mask = vcpu_mask_id_aa64pfr1_el1,
 };
 
+static struct id_reg_info id_aa64isar0_el1_info = {
+	.sys_reg = SYS_ID_AA64ISAR0_EL1,
+	.validate = validate_id_aa64isar0_el1,
+};
+
 /*
  * An ID register that needs special handling to control the value for the
  * guest must have its own id_reg_info in id_reg_info_table.
@@ -452,6 +480,7 @@ static struct id_reg_info id_aa64pfr1_el1_info = {
 static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {
 	[IDREG_IDX(SYS_ID_AA64PFR0_EL1)] = &id_aa64pfr0_el1_info,
 	[IDREG_IDX(SYS_ID_AA64PFR1_EL1)] = &id_aa64pfr1_el1_info,
+	[IDREG_IDX(SYS_ID_AA64ISAR0_EL1)] = &id_aa64isar0_el1_info,
 };
 
 static int validate_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
-- 
2.34.1.448.ga2b2bfdf31-goog


^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 06/26] KVM: arm64: Make ID_AA64ISAR0_EL1 writable
@ 2022-01-06  4:26   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, Will Deacon, Peter Shier, Paolo Bonzini, linux-arm-kernel

This patch adds id_reg_info for ID_AA64ISAR0_EL1 to make it writable
by userspace.

Updating sm3, sm4, sha1, sha2 and sha3 fields are allowed only
if values of those fields follow Arm ARM.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/kvm/sys_regs.c | 29 +++++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)

diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index ae9ca341a2e4..3f1313875be5 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -374,6 +374,29 @@ static int validate_id_aa64pfr1_el1(struct kvm_vcpu *vcpu,
 	return 0;
 }
 
+static int validate_id_aa64isar0_el1(struct kvm_vcpu *vcpu,
+				     const struct id_reg_info *id_reg, u64 val)
+{
+	unsigned int sm3, sm4, sha1, sha2, sha3;
+
+	/* Run consistency checkings according to Arm ARM */
+	sm3 = cpuid_feature_extract_unsigned_field(val, ID_AA64ISAR0_SM3_SHIFT);
+	sm4 = cpuid_feature_extract_unsigned_field(val, ID_AA64ISAR0_SM4_SHIFT);
+	if (sm3 != sm4)
+		return -EINVAL;
+
+	sha1 = cpuid_feature_extract_unsigned_field(val, ID_AA64ISAR0_SHA1_SHIFT);
+	sha2 = cpuid_feature_extract_unsigned_field(val, ID_AA64ISAR0_SHA2_SHIFT);
+	if ((sha1 == 0) ^ (sha2 == 0))
+		return -EINVAL;
+
+	sha3 = cpuid_feature_extract_unsigned_field(val, ID_AA64ISAR0_SHA3_SHIFT);
+	if (((sha2 == 2) ^ (sha3 == 1)) || (!sha1 && sha3))
+		return -EINVAL;
+
+	return 0;
+}
+
 static void init_id_aa64pfr0_el1_info(struct id_reg_info *id_reg)
 {
 	u64 limit = id_reg->vcpu_limit_val;
@@ -441,6 +464,11 @@ static struct id_reg_info id_aa64pfr1_el1_info = {
 	.vcpu_mask = vcpu_mask_id_aa64pfr1_el1,
 };
 
+static struct id_reg_info id_aa64isar0_el1_info = {
+	.sys_reg = SYS_ID_AA64ISAR0_EL1,
+	.validate = validate_id_aa64isar0_el1,
+};
+
 /*
  * An ID register that needs special handling to control the value for the
  * guest must have its own id_reg_info in id_reg_info_table.
@@ -452,6 +480,7 @@ static struct id_reg_info id_aa64pfr1_el1_info = {
 static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {
 	[IDREG_IDX(SYS_ID_AA64PFR0_EL1)] = &id_aa64pfr0_el1_info,
 	[IDREG_IDX(SYS_ID_AA64PFR1_EL1)] = &id_aa64pfr1_el1_info,
+	[IDREG_IDX(SYS_ID_AA64ISAR0_EL1)] = &id_aa64isar0_el1_info,
 };
 
 static int validate_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
-- 
2.34.1.448.ga2b2bfdf31-goog

_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 06/26] KVM: arm64: Make ID_AA64ISAR0_EL1 writable
@ 2022-01-06  4:26   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

This patch adds id_reg_info for ID_AA64ISAR0_EL1 to make it writable
by userspace.

Updating sm3, sm4, sha1, sha2 and sha3 fields are allowed only
if values of those fields follow Arm ARM.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/kvm/sys_regs.c | 29 +++++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)

diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index ae9ca341a2e4..3f1313875be5 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -374,6 +374,29 @@ static int validate_id_aa64pfr1_el1(struct kvm_vcpu *vcpu,
 	return 0;
 }
 
+static int validate_id_aa64isar0_el1(struct kvm_vcpu *vcpu,
+				     const struct id_reg_info *id_reg, u64 val)
+{
+	unsigned int sm3, sm4, sha1, sha2, sha3;
+
+	/* Run consistency checkings according to Arm ARM */
+	sm3 = cpuid_feature_extract_unsigned_field(val, ID_AA64ISAR0_SM3_SHIFT);
+	sm4 = cpuid_feature_extract_unsigned_field(val, ID_AA64ISAR0_SM4_SHIFT);
+	if (sm3 != sm4)
+		return -EINVAL;
+
+	sha1 = cpuid_feature_extract_unsigned_field(val, ID_AA64ISAR0_SHA1_SHIFT);
+	sha2 = cpuid_feature_extract_unsigned_field(val, ID_AA64ISAR0_SHA2_SHIFT);
+	if ((sha1 == 0) ^ (sha2 == 0))
+		return -EINVAL;
+
+	sha3 = cpuid_feature_extract_unsigned_field(val, ID_AA64ISAR0_SHA3_SHIFT);
+	if (((sha2 == 2) ^ (sha3 == 1)) || (!sha1 && sha3))
+		return -EINVAL;
+
+	return 0;
+}
+
 static void init_id_aa64pfr0_el1_info(struct id_reg_info *id_reg)
 {
 	u64 limit = id_reg->vcpu_limit_val;
@@ -441,6 +464,11 @@ static struct id_reg_info id_aa64pfr1_el1_info = {
 	.vcpu_mask = vcpu_mask_id_aa64pfr1_el1,
 };
 
+static struct id_reg_info id_aa64isar0_el1_info = {
+	.sys_reg = SYS_ID_AA64ISAR0_EL1,
+	.validate = validate_id_aa64isar0_el1,
+};
+
 /*
  * An ID register that needs special handling to control the value for the
  * guest must have its own id_reg_info in id_reg_info_table.
@@ -452,6 +480,7 @@ static struct id_reg_info id_aa64pfr1_el1_info = {
 static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {
 	[IDREG_IDX(SYS_ID_AA64PFR0_EL1)] = &id_aa64pfr0_el1_info,
 	[IDREG_IDX(SYS_ID_AA64PFR1_EL1)] = &id_aa64pfr1_el1_info,
+	[IDREG_IDX(SYS_ID_AA64ISAR0_EL1)] = &id_aa64isar0_el1_info,
 };
 
 static int validate_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
-- 
2.34.1.448.ga2b2bfdf31-goog


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 07/26] KVM: arm64: Make ID_AA64ISAR1_EL1 writable
  2022-01-06  4:26 ` Reiji Watanabe
  (?)
@ 2022-01-06  4:26   ` Reiji Watanabe
  -1 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

This patch adds id_reg_info for ID_AA64ISAR1_EL1 to make it
writable by userspace.

Return an error if userspace tries to set PTRAUTH related fields
of the register to values that conflict with PTRAUTH configuration,
which was configured by KVM_ARM_VCPU_INIT, for the guest.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/kvm/sys_regs.c | 77 +++++++++++++++++++++++++++++++++++----
 1 file changed, 69 insertions(+), 8 deletions(-)

diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 3f1313875be5..2f79997016a4 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -265,6 +265,24 @@ static bool trap_raz_wi(struct kvm_vcpu *vcpu,
 		return read_zero(vcpu, p);
 }
 
+#define PTRAUTH_MASK	(ARM64_FEATURE_MASK(ID_AA64ISAR1_APA) |	\
+			 ARM64_FEATURE_MASK(ID_AA64ISAR1_API) | \
+			 ARM64_FEATURE_MASK(ID_AA64ISAR1_GPA) |	\
+			 ARM64_FEATURE_MASK(ID_AA64ISAR1_GPI))
+
+#define aa64isar1_has_apa(val)	\
+	(cpuid_feature_extract_unsigned_field(val, ID_AA64ISAR1_APA_SHIFT) >= \
+	 ID_AA64ISAR1_APA_ARCHITECTED)
+#define aa64isar1_has_api(val)	\
+	(cpuid_feature_extract_unsigned_field(val, ID_AA64ISAR1_API_SHIFT) >= \
+	 ID_AA64ISAR1_API_IMP_DEF)
+#define aa64isar1_has_gpa(val)	\
+	(cpuid_feature_extract_unsigned_field(val, ID_AA64ISAR1_GPA_SHIFT) >= \
+	 ID_AA64ISAR1_GPA_ARCHITECTED)
+#define aa64isar1_has_gpi(val)	\
+	(cpuid_feature_extract_unsigned_field(val, ID_AA64ISAR1_GPI_SHIFT) >= \
+	 ID_AA64ISAR1_GPI_IMP_DEF)
+
 struct id_reg_info {
 	u32	sys_reg;	/* Register ID */
 
@@ -397,6 +415,36 @@ static int validate_id_aa64isar0_el1(struct kvm_vcpu *vcpu,
 	return 0;
 }
 
+static int validate_id_aa64isar1_el1(struct kvm_vcpu *vcpu,
+				     const struct id_reg_info *id_reg, u64 val)
+{
+	bool has_gpi, has_gpa, has_api, has_apa;
+	bool generic, address;
+
+	has_gpi = aa64isar1_has_gpi(val);
+	has_gpa = aa64isar1_has_gpa(val);
+	has_api = aa64isar1_has_api(val);
+	has_apa = aa64isar1_has_apa(val);
+	if ((has_gpi && has_gpa) || (has_api && has_apa))
+		return -EINVAL;
+
+	generic = has_gpi || has_gpa;
+	address = has_api || has_apa;
+	/*
+	 * Since the current KVM guest implementation works by enabling
+	 * both address/generic pointer authentication features,
+	 * return an error if they conflict.
+	 */
+	if (generic ^ address)
+		return -EPERM;
+
+	/* Check if there is a conflict with a request via KVM_ARM_VCPU_INIT */
+	if (vcpu_has_ptrauth(vcpu) ^ (generic && address))
+		return -EPERM;
+
+	return 0;
+}
+
 static void init_id_aa64pfr0_el1_info(struct id_reg_info *id_reg)
 {
 	u64 limit = id_reg->vcpu_limit_val;
@@ -434,8 +482,14 @@ static void init_id_aa64pfr1_el1_info(struct id_reg_info *id_reg)
 		id_reg->vcpu_limit_val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_MTE);
 }
 
+static void init_id_aa64isar1_el1_info(struct id_reg_info *id_reg)
+{
+	if (!system_has_full_ptr_auth())
+		id_reg->vcpu_limit_val &= ~PTRAUTH_MASK;
+}
+
 static u64 vcpu_mask_id_aa64pfr0_el1(const struct kvm_vcpu *vcpu,
-					 const struct id_reg_info *idr)
+				     const struct id_reg_info *idr)
 {
 	return vcpu_has_sve(vcpu) ? 0 : ARM64_FEATURE_MASK(ID_AA64PFR0_SVE);
 }
@@ -446,6 +500,12 @@ static u64 vcpu_mask_id_aa64pfr1_el1(const struct kvm_vcpu *vcpu,
 	return kvm_has_mte(vcpu->kvm) ? 0 : (ARM64_FEATURE_MASK(ID_AA64PFR1_MTE));
 }
 
+static u64 vcpu_mask_id_aa64isar1_el1(const struct kvm_vcpu *vcpu,
+					  const struct id_reg_info *idr)
+{
+	return vcpu_has_ptrauth(vcpu) ? 0 : PTRAUTH_MASK;
+}
+
 static struct id_reg_info id_aa64pfr0_el1_info = {
 	.sys_reg = SYS_ID_AA64PFR0_EL1,
 	.ignore_mask = ARM64_FEATURE_MASK(ID_AA64PFR0_GIC),
@@ -469,6 +529,13 @@ static struct id_reg_info id_aa64isar0_el1_info = {
 	.validate = validate_id_aa64isar0_el1,
 };
 
+static struct id_reg_info id_aa64isar1_el1_info = {
+	.sys_reg = SYS_ID_AA64ISAR1_EL1,
+	.init = init_id_aa64isar1_el1_info,
+	.validate = validate_id_aa64isar1_el1,
+	.vcpu_mask = vcpu_mask_id_aa64isar1_el1,
+};
+
 /*
  * An ID register that needs special handling to control the value for the
  * guest must have its own id_reg_info in id_reg_info_table.
@@ -481,6 +548,7 @@ static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {
 	[IDREG_IDX(SYS_ID_AA64PFR0_EL1)] = &id_aa64pfr0_el1_info,
 	[IDREG_IDX(SYS_ID_AA64PFR1_EL1)] = &id_aa64pfr1_el1_info,
 	[IDREG_IDX(SYS_ID_AA64ISAR0_EL1)] = &id_aa64isar0_el1_info,
+	[IDREG_IDX(SYS_ID_AA64ISAR1_EL1)] = &id_aa64isar1_el1_info,
 };
 
 static int validate_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
@@ -1398,13 +1466,6 @@ static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
 		val &= ~(id_reg->vcpu_mask(vcpu, id_reg));
 
 	switch (id) {
-	case SYS_ID_AA64ISAR1_EL1:
-		if (!vcpu_has_ptrauth(vcpu))
-			val &= ~(ARM64_FEATURE_MASK(ID_AA64ISAR1_APA) |
-				 ARM64_FEATURE_MASK(ID_AA64ISAR1_API) |
-				 ARM64_FEATURE_MASK(ID_AA64ISAR1_GPA) |
-				 ARM64_FEATURE_MASK(ID_AA64ISAR1_GPI));
-		break;
 	case SYS_ID_AA64DFR0_EL1:
 		/* Limit debug to ARMv8.0 */
 		val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_DEBUGVER);
-- 
2.34.1.448.ga2b2bfdf31-goog


^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 07/26] KVM: arm64: Make ID_AA64ISAR1_EL1 writable
@ 2022-01-06  4:26   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, Will Deacon, Peter Shier, Paolo Bonzini, linux-arm-kernel

This patch adds id_reg_info for ID_AA64ISAR1_EL1 to make it
writable by userspace.

Return an error if userspace tries to set PTRAUTH related fields
of the register to values that conflict with PTRAUTH configuration,
which was configured by KVM_ARM_VCPU_INIT, for the guest.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/kvm/sys_regs.c | 77 +++++++++++++++++++++++++++++++++++----
 1 file changed, 69 insertions(+), 8 deletions(-)

diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 3f1313875be5..2f79997016a4 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -265,6 +265,24 @@ static bool trap_raz_wi(struct kvm_vcpu *vcpu,
 		return read_zero(vcpu, p);
 }
 
+#define PTRAUTH_MASK	(ARM64_FEATURE_MASK(ID_AA64ISAR1_APA) |	\
+			 ARM64_FEATURE_MASK(ID_AA64ISAR1_API) | \
+			 ARM64_FEATURE_MASK(ID_AA64ISAR1_GPA) |	\
+			 ARM64_FEATURE_MASK(ID_AA64ISAR1_GPI))
+
+#define aa64isar1_has_apa(val)	\
+	(cpuid_feature_extract_unsigned_field(val, ID_AA64ISAR1_APA_SHIFT) >= \
+	 ID_AA64ISAR1_APA_ARCHITECTED)
+#define aa64isar1_has_api(val)	\
+	(cpuid_feature_extract_unsigned_field(val, ID_AA64ISAR1_API_SHIFT) >= \
+	 ID_AA64ISAR1_API_IMP_DEF)
+#define aa64isar1_has_gpa(val)	\
+	(cpuid_feature_extract_unsigned_field(val, ID_AA64ISAR1_GPA_SHIFT) >= \
+	 ID_AA64ISAR1_GPA_ARCHITECTED)
+#define aa64isar1_has_gpi(val)	\
+	(cpuid_feature_extract_unsigned_field(val, ID_AA64ISAR1_GPI_SHIFT) >= \
+	 ID_AA64ISAR1_GPI_IMP_DEF)
+
 struct id_reg_info {
 	u32	sys_reg;	/* Register ID */
 
@@ -397,6 +415,36 @@ static int validate_id_aa64isar0_el1(struct kvm_vcpu *vcpu,
 	return 0;
 }
 
+static int validate_id_aa64isar1_el1(struct kvm_vcpu *vcpu,
+				     const struct id_reg_info *id_reg, u64 val)
+{
+	bool has_gpi, has_gpa, has_api, has_apa;
+	bool generic, address;
+
+	has_gpi = aa64isar1_has_gpi(val);
+	has_gpa = aa64isar1_has_gpa(val);
+	has_api = aa64isar1_has_api(val);
+	has_apa = aa64isar1_has_apa(val);
+	if ((has_gpi && has_gpa) || (has_api && has_apa))
+		return -EINVAL;
+
+	generic = has_gpi || has_gpa;
+	address = has_api || has_apa;
+	/*
+	 * Since the current KVM guest implementation works by enabling
+	 * both address/generic pointer authentication features,
+	 * return an error if they conflict.
+	 */
+	if (generic ^ address)
+		return -EPERM;
+
+	/* Check if there is a conflict with a request via KVM_ARM_VCPU_INIT */
+	if (vcpu_has_ptrauth(vcpu) ^ (generic && address))
+		return -EPERM;
+
+	return 0;
+}
+
 static void init_id_aa64pfr0_el1_info(struct id_reg_info *id_reg)
 {
 	u64 limit = id_reg->vcpu_limit_val;
@@ -434,8 +482,14 @@ static void init_id_aa64pfr1_el1_info(struct id_reg_info *id_reg)
 		id_reg->vcpu_limit_val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_MTE);
 }
 
+static void init_id_aa64isar1_el1_info(struct id_reg_info *id_reg)
+{
+	if (!system_has_full_ptr_auth())
+		id_reg->vcpu_limit_val &= ~PTRAUTH_MASK;
+}
+
 static u64 vcpu_mask_id_aa64pfr0_el1(const struct kvm_vcpu *vcpu,
-					 const struct id_reg_info *idr)
+				     const struct id_reg_info *idr)
 {
 	return vcpu_has_sve(vcpu) ? 0 : ARM64_FEATURE_MASK(ID_AA64PFR0_SVE);
 }
@@ -446,6 +500,12 @@ static u64 vcpu_mask_id_aa64pfr1_el1(const struct kvm_vcpu *vcpu,
 	return kvm_has_mte(vcpu->kvm) ? 0 : (ARM64_FEATURE_MASK(ID_AA64PFR1_MTE));
 }
 
+static u64 vcpu_mask_id_aa64isar1_el1(const struct kvm_vcpu *vcpu,
+					  const struct id_reg_info *idr)
+{
+	return vcpu_has_ptrauth(vcpu) ? 0 : PTRAUTH_MASK;
+}
+
 static struct id_reg_info id_aa64pfr0_el1_info = {
 	.sys_reg = SYS_ID_AA64PFR0_EL1,
 	.ignore_mask = ARM64_FEATURE_MASK(ID_AA64PFR0_GIC),
@@ -469,6 +529,13 @@ static struct id_reg_info id_aa64isar0_el1_info = {
 	.validate = validate_id_aa64isar0_el1,
 };
 
+static struct id_reg_info id_aa64isar1_el1_info = {
+	.sys_reg = SYS_ID_AA64ISAR1_EL1,
+	.init = init_id_aa64isar1_el1_info,
+	.validate = validate_id_aa64isar1_el1,
+	.vcpu_mask = vcpu_mask_id_aa64isar1_el1,
+};
+
 /*
  * An ID register that needs special handling to control the value for the
  * guest must have its own id_reg_info in id_reg_info_table.
@@ -481,6 +548,7 @@ static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {
 	[IDREG_IDX(SYS_ID_AA64PFR0_EL1)] = &id_aa64pfr0_el1_info,
 	[IDREG_IDX(SYS_ID_AA64PFR1_EL1)] = &id_aa64pfr1_el1_info,
 	[IDREG_IDX(SYS_ID_AA64ISAR0_EL1)] = &id_aa64isar0_el1_info,
+	[IDREG_IDX(SYS_ID_AA64ISAR1_EL1)] = &id_aa64isar1_el1_info,
 };
 
 static int validate_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
@@ -1398,13 +1466,6 @@ static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
 		val &= ~(id_reg->vcpu_mask(vcpu, id_reg));
 
 	switch (id) {
-	case SYS_ID_AA64ISAR1_EL1:
-		if (!vcpu_has_ptrauth(vcpu))
-			val &= ~(ARM64_FEATURE_MASK(ID_AA64ISAR1_APA) |
-				 ARM64_FEATURE_MASK(ID_AA64ISAR1_API) |
-				 ARM64_FEATURE_MASK(ID_AA64ISAR1_GPA) |
-				 ARM64_FEATURE_MASK(ID_AA64ISAR1_GPI));
-		break;
 	case SYS_ID_AA64DFR0_EL1:
 		/* Limit debug to ARMv8.0 */
 		val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_DEBUGVER);
-- 
2.34.1.448.ga2b2bfdf31-goog

_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 07/26] KVM: arm64: Make ID_AA64ISAR1_EL1 writable
@ 2022-01-06  4:26   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

This patch adds id_reg_info for ID_AA64ISAR1_EL1 to make it
writable by userspace.

Return an error if userspace tries to set PTRAUTH related fields
of the register to values that conflict with PTRAUTH configuration,
which was configured by KVM_ARM_VCPU_INIT, for the guest.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/kvm/sys_regs.c | 77 +++++++++++++++++++++++++++++++++++----
 1 file changed, 69 insertions(+), 8 deletions(-)

diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 3f1313875be5..2f79997016a4 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -265,6 +265,24 @@ static bool trap_raz_wi(struct kvm_vcpu *vcpu,
 		return read_zero(vcpu, p);
 }
 
+#define PTRAUTH_MASK	(ARM64_FEATURE_MASK(ID_AA64ISAR1_APA) |	\
+			 ARM64_FEATURE_MASK(ID_AA64ISAR1_API) | \
+			 ARM64_FEATURE_MASK(ID_AA64ISAR1_GPA) |	\
+			 ARM64_FEATURE_MASK(ID_AA64ISAR1_GPI))
+
+#define aa64isar1_has_apa(val)	\
+	(cpuid_feature_extract_unsigned_field(val, ID_AA64ISAR1_APA_SHIFT) >= \
+	 ID_AA64ISAR1_APA_ARCHITECTED)
+#define aa64isar1_has_api(val)	\
+	(cpuid_feature_extract_unsigned_field(val, ID_AA64ISAR1_API_SHIFT) >= \
+	 ID_AA64ISAR1_API_IMP_DEF)
+#define aa64isar1_has_gpa(val)	\
+	(cpuid_feature_extract_unsigned_field(val, ID_AA64ISAR1_GPA_SHIFT) >= \
+	 ID_AA64ISAR1_GPA_ARCHITECTED)
+#define aa64isar1_has_gpi(val)	\
+	(cpuid_feature_extract_unsigned_field(val, ID_AA64ISAR1_GPI_SHIFT) >= \
+	 ID_AA64ISAR1_GPI_IMP_DEF)
+
 struct id_reg_info {
 	u32	sys_reg;	/* Register ID */
 
@@ -397,6 +415,36 @@ static int validate_id_aa64isar0_el1(struct kvm_vcpu *vcpu,
 	return 0;
 }
 
+static int validate_id_aa64isar1_el1(struct kvm_vcpu *vcpu,
+				     const struct id_reg_info *id_reg, u64 val)
+{
+	bool has_gpi, has_gpa, has_api, has_apa;
+	bool generic, address;
+
+	has_gpi = aa64isar1_has_gpi(val);
+	has_gpa = aa64isar1_has_gpa(val);
+	has_api = aa64isar1_has_api(val);
+	has_apa = aa64isar1_has_apa(val);
+	if ((has_gpi && has_gpa) || (has_api && has_apa))
+		return -EINVAL;
+
+	generic = has_gpi || has_gpa;
+	address = has_api || has_apa;
+	/*
+	 * Since the current KVM guest implementation works by enabling
+	 * both address/generic pointer authentication features,
+	 * return an error if they conflict.
+	 */
+	if (generic ^ address)
+		return -EPERM;
+
+	/* Check if there is a conflict with a request via KVM_ARM_VCPU_INIT */
+	if (vcpu_has_ptrauth(vcpu) ^ (generic && address))
+		return -EPERM;
+
+	return 0;
+}
+
 static void init_id_aa64pfr0_el1_info(struct id_reg_info *id_reg)
 {
 	u64 limit = id_reg->vcpu_limit_val;
@@ -434,8 +482,14 @@ static void init_id_aa64pfr1_el1_info(struct id_reg_info *id_reg)
 		id_reg->vcpu_limit_val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_MTE);
 }
 
+static void init_id_aa64isar1_el1_info(struct id_reg_info *id_reg)
+{
+	if (!system_has_full_ptr_auth())
+		id_reg->vcpu_limit_val &= ~PTRAUTH_MASK;
+}
+
 static u64 vcpu_mask_id_aa64pfr0_el1(const struct kvm_vcpu *vcpu,
-					 const struct id_reg_info *idr)
+				     const struct id_reg_info *idr)
 {
 	return vcpu_has_sve(vcpu) ? 0 : ARM64_FEATURE_MASK(ID_AA64PFR0_SVE);
 }
@@ -446,6 +500,12 @@ static u64 vcpu_mask_id_aa64pfr1_el1(const struct kvm_vcpu *vcpu,
 	return kvm_has_mte(vcpu->kvm) ? 0 : (ARM64_FEATURE_MASK(ID_AA64PFR1_MTE));
 }
 
+static u64 vcpu_mask_id_aa64isar1_el1(const struct kvm_vcpu *vcpu,
+					  const struct id_reg_info *idr)
+{
+	return vcpu_has_ptrauth(vcpu) ? 0 : PTRAUTH_MASK;
+}
+
 static struct id_reg_info id_aa64pfr0_el1_info = {
 	.sys_reg = SYS_ID_AA64PFR0_EL1,
 	.ignore_mask = ARM64_FEATURE_MASK(ID_AA64PFR0_GIC),
@@ -469,6 +529,13 @@ static struct id_reg_info id_aa64isar0_el1_info = {
 	.validate = validate_id_aa64isar0_el1,
 };
 
+static struct id_reg_info id_aa64isar1_el1_info = {
+	.sys_reg = SYS_ID_AA64ISAR1_EL1,
+	.init = init_id_aa64isar1_el1_info,
+	.validate = validate_id_aa64isar1_el1,
+	.vcpu_mask = vcpu_mask_id_aa64isar1_el1,
+};
+
 /*
  * An ID register that needs special handling to control the value for the
  * guest must have its own id_reg_info in id_reg_info_table.
@@ -481,6 +548,7 @@ static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {
 	[IDREG_IDX(SYS_ID_AA64PFR0_EL1)] = &id_aa64pfr0_el1_info,
 	[IDREG_IDX(SYS_ID_AA64PFR1_EL1)] = &id_aa64pfr1_el1_info,
 	[IDREG_IDX(SYS_ID_AA64ISAR0_EL1)] = &id_aa64isar0_el1_info,
+	[IDREG_IDX(SYS_ID_AA64ISAR1_EL1)] = &id_aa64isar1_el1_info,
 };
 
 static int validate_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
@@ -1398,13 +1466,6 @@ static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
 		val &= ~(id_reg->vcpu_mask(vcpu, id_reg));
 
 	switch (id) {
-	case SYS_ID_AA64ISAR1_EL1:
-		if (!vcpu_has_ptrauth(vcpu))
-			val &= ~(ARM64_FEATURE_MASK(ID_AA64ISAR1_APA) |
-				 ARM64_FEATURE_MASK(ID_AA64ISAR1_API) |
-				 ARM64_FEATURE_MASK(ID_AA64ISAR1_GPA) |
-				 ARM64_FEATURE_MASK(ID_AA64ISAR1_GPI));
-		break;
 	case SYS_ID_AA64DFR0_EL1:
 		/* Limit debug to ARMv8.0 */
 		val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_DEBUGVER);
-- 
2.34.1.448.ga2b2bfdf31-goog


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 08/26] KVM: arm64: Make ID_AA64MMFR0_EL1 writable
  2022-01-06  4:26 ` Reiji Watanabe
  (?)
@ 2022-01-06  4:26   ` Reiji Watanabe
  -1 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

This patch adds id_reg_info for ID_AA64MMFR0_EL1 to make it
writable by userspace.

Since ID_AA64MMFR0_EL1 stage 2 granule size fields don't follow the
standard ID scheme, we need a special handling to validate those fields.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/kvm/sys_regs.c | 127 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 127 insertions(+)

diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 2f79997016a4..723910267966 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -445,6 +445,118 @@ static int validate_id_aa64isar1_el1(struct kvm_vcpu *vcpu,
 	return 0;
 }
 
+/*
+ * Check if the requested stage2 translation granule size indicated in
+ * @mmfr0 is also indicated in @mmfr0_lim.
+ * If TGranX_2 field is zero, the value must be validated based on TGranX
+ * field because that indicates the feature support is identified in
+ * TGranX field.
+ * This function relies on the fact TGranX fields are validated before
+ * through the arm64_check_features.
+ */
+static int aa64mmfr0_tgran2_check(int field, u64 mmfr0, u64 mmfr0_lim)
+{
+	s64 tgran2, lim_tgran2, rtgran1;
+	int f1;
+	bool is_signed;
+
+	tgran2 = cpuid_feature_extract_unsigned_field(mmfr0, field);
+	lim_tgran2 = cpuid_feature_extract_unsigned_field(mmfr0_lim, field);
+	if (tgran2 && lim_tgran2)
+		/*
+		 * We don't need to check TGranX field. We can simply
+		 * compare tgran2 and lim_tgran2.
+		 */
+		return (tgran2 > lim_tgran2) ? -E2BIG : 0;
+
+	if (tgran2 == lim_tgran2)
+		/*
+		 * Both of them are zero.  Since TGranX in @mmfr0 is already
+		 * validated by arm64_check_features, tgran2 must be fine.
+		 */
+		return 0;
+
+	/*
+	 * Either tgran2 or lim_tgran2 is zero.
+	 * Need stage1 granule size to validate tgran2.
+	 */
+
+	/*
+	 * Get TGranX's bit position by subtracting 12 from TGranX_2's bit
+	 * position.
+	 */
+	f1 = field - 12;
+
+	/* TGran4/TGran64 is signed and TGran16 is unsigned field. */
+	is_signed = (f1 == ID_AA64MMFR0_TGRAN16_SHIFT) ? false : true;
+
+	/*
+	 * If tgran2 == 0 (&& lim_tgran2 != 0), the requested stage2 granule
+	 * size is indicated in the stage1 granule size field of @mmfr0.
+	 * So, validate the stage1 granule size against the stage2 limit
+	 * granule size.
+	 * If lim_tgran2 == 0 (&& tgran2 != 0), the stage2 limit granule size
+	 * is indicated in the stage1 granule size field of @mmfr0_lim.
+	 * So, validate the requested stage2 granule size against the stage1
+	 * limit granule size.
+	 */
+
+	 /* Get the relevant stage1 granule size to validate tgran2 */
+	if (tgran2 == 0)
+		/* The requested stage1 granule size */
+		rtgran1 = cpuid_feature_extract_field(mmfr0, f1, is_signed);
+	else /* lim_tgran2 == 0 */
+		/* The stage1 limit granule size */
+		rtgran1 = cpuid_feature_extract_field(mmfr0_lim, f1, is_signed);
+
+	/*
+	 * Adjust the value of rtgran1 to compare with stage2 granule size,
+	 * which indicates: 1: Not supported, 2: Supported, etc.
+	 */
+	if (is_signed)
+		/* For signed, -1: Not supported, 0: Supported, etc. */
+		rtgran1 += 0x2;
+	else
+		/* For unsigned, 0: Not supported, 1: Supported, etc. */
+		rtgran1 += 0x1;
+
+	if ((tgran2 == 0) && (rtgran1 > lim_tgran2))
+		/*
+		 * The requested stage1 granule size (== the requested stage2
+		 * granule size) is larger than the stage2 limit granule size.
+		 */
+		return -E2BIG;
+	else if ((lim_tgran2 == 0) && (tgran2 > rtgran1))
+		/*
+		 * The requested stage2 granule size is larger than the stage1
+		 * limit granulze size (== the stage2 limit granule size).
+		 */
+		return -E2BIG;
+
+	return 0;
+}
+
+static int validate_id_aa64mmfr0_el1(struct kvm_vcpu *vcpu,
+				     const struct id_reg_info *id_reg, u64 val)
+{
+	u64 limit = id_reg->vcpu_limit_val;
+	int ret;
+
+	ret = aa64mmfr0_tgran2_check(ID_AA64MMFR0_TGRAN4_2_SHIFT, val, limit);
+	if (ret)
+		return ret;
+
+	ret = aa64mmfr0_tgran2_check(ID_AA64MMFR0_TGRAN64_2_SHIFT, val, limit);
+	if (ret)
+		return ret;
+
+	ret = aa64mmfr0_tgran2_check(ID_AA64MMFR0_TGRAN16_2_SHIFT, val, limit);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
 static void init_id_aa64pfr0_el1_info(struct id_reg_info *id_reg)
 {
 	u64 limit = id_reg->vcpu_limit_val;
@@ -536,6 +648,20 @@ static struct id_reg_info id_aa64isar1_el1_info = {
 	.vcpu_mask = vcpu_mask_id_aa64isar1_el1,
 };
 
+static struct id_reg_info id_aa64mmfr0_el1_info = {
+	.sys_reg = SYS_ID_AA64MMFR0_EL1,
+	/*
+	 * When TGranX_2 value is 0, validity of the value depend on TGranX
+	 * value, and TGranX_2 value must be validated against TGranX value,
+	 * which is done by validate_id_aa64mmfr0_el1.
+	 * So, skip the regular validity checking for TGranX_2 fields.
+	 */
+	.ignore_mask = ARM64_FEATURE_MASK(ID_AA64MMFR0_TGRAN4_2) |
+		       ARM64_FEATURE_MASK(ID_AA64MMFR0_TGRAN64_2) |
+		       ARM64_FEATURE_MASK(ID_AA64MMFR0_TGRAN16_2),
+	.validate = validate_id_aa64mmfr0_el1,
+};
+
 /*
  * An ID register that needs special handling to control the value for the
  * guest must have its own id_reg_info in id_reg_info_table.
@@ -549,6 +675,7 @@ static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {
 	[IDREG_IDX(SYS_ID_AA64PFR1_EL1)] = &id_aa64pfr1_el1_info,
 	[IDREG_IDX(SYS_ID_AA64ISAR0_EL1)] = &id_aa64isar0_el1_info,
 	[IDREG_IDX(SYS_ID_AA64ISAR1_EL1)] = &id_aa64isar1_el1_info,
+	[IDREG_IDX(SYS_ID_AA64MMFR0_EL1)] = &id_aa64mmfr0_el1_info,
 };
 
 static int validate_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
-- 
2.34.1.448.ga2b2bfdf31-goog


^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 08/26] KVM: arm64: Make ID_AA64MMFR0_EL1 writable
@ 2022-01-06  4:26   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, Will Deacon, Peter Shier, Paolo Bonzini, linux-arm-kernel

This patch adds id_reg_info for ID_AA64MMFR0_EL1 to make it
writable by userspace.

Since ID_AA64MMFR0_EL1 stage 2 granule size fields don't follow the
standard ID scheme, we need a special handling to validate those fields.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/kvm/sys_regs.c | 127 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 127 insertions(+)

diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 2f79997016a4..723910267966 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -445,6 +445,118 @@ static int validate_id_aa64isar1_el1(struct kvm_vcpu *vcpu,
 	return 0;
 }
 
+/*
+ * Check if the requested stage2 translation granule size indicated in
+ * @mmfr0 is also indicated in @mmfr0_lim.
+ * If TGranX_2 field is zero, the value must be validated based on TGranX
+ * field because that indicates the feature support is identified in
+ * TGranX field.
+ * This function relies on the fact TGranX fields are validated before
+ * through the arm64_check_features.
+ */
+static int aa64mmfr0_tgran2_check(int field, u64 mmfr0, u64 mmfr0_lim)
+{
+	s64 tgran2, lim_tgran2, rtgran1;
+	int f1;
+	bool is_signed;
+
+	tgran2 = cpuid_feature_extract_unsigned_field(mmfr0, field);
+	lim_tgran2 = cpuid_feature_extract_unsigned_field(mmfr0_lim, field);
+	if (tgran2 && lim_tgran2)
+		/*
+		 * We don't need to check TGranX field. We can simply
+		 * compare tgran2 and lim_tgran2.
+		 */
+		return (tgran2 > lim_tgran2) ? -E2BIG : 0;
+
+	if (tgran2 == lim_tgran2)
+		/*
+		 * Both of them are zero.  Since TGranX in @mmfr0 is already
+		 * validated by arm64_check_features, tgran2 must be fine.
+		 */
+		return 0;
+
+	/*
+	 * Either tgran2 or lim_tgran2 is zero.
+	 * Need stage1 granule size to validate tgran2.
+	 */
+
+	/*
+	 * Get TGranX's bit position by subtracting 12 from TGranX_2's bit
+	 * position.
+	 */
+	f1 = field - 12;
+
+	/* TGran4/TGran64 is signed and TGran16 is unsigned field. */
+	is_signed = (f1 == ID_AA64MMFR0_TGRAN16_SHIFT) ? false : true;
+
+	/*
+	 * If tgran2 == 0 (&& lim_tgran2 != 0), the requested stage2 granule
+	 * size is indicated in the stage1 granule size field of @mmfr0.
+	 * So, validate the stage1 granule size against the stage2 limit
+	 * granule size.
+	 * If lim_tgran2 == 0 (&& tgran2 != 0), the stage2 limit granule size
+	 * is indicated in the stage1 granule size field of @mmfr0_lim.
+	 * So, validate the requested stage2 granule size against the stage1
+	 * limit granule size.
+	 */
+
+	 /* Get the relevant stage1 granule size to validate tgran2 */
+	if (tgran2 == 0)
+		/* The requested stage1 granule size */
+		rtgran1 = cpuid_feature_extract_field(mmfr0, f1, is_signed);
+	else /* lim_tgran2 == 0 */
+		/* The stage1 limit granule size */
+		rtgran1 = cpuid_feature_extract_field(mmfr0_lim, f1, is_signed);
+
+	/*
+	 * Adjust the value of rtgran1 to compare with stage2 granule size,
+	 * which indicates: 1: Not supported, 2: Supported, etc.
+	 */
+	if (is_signed)
+		/* For signed, -1: Not supported, 0: Supported, etc. */
+		rtgran1 += 0x2;
+	else
+		/* For unsigned, 0: Not supported, 1: Supported, etc. */
+		rtgran1 += 0x1;
+
+	if ((tgran2 == 0) && (rtgran1 > lim_tgran2))
+		/*
+		 * The requested stage1 granule size (== the requested stage2
+		 * granule size) is larger than the stage2 limit granule size.
+		 */
+		return -E2BIG;
+	else if ((lim_tgran2 == 0) && (tgran2 > rtgran1))
+		/*
+		 * The requested stage2 granule size is larger than the stage1
+		 * limit granulze size (== the stage2 limit granule size).
+		 */
+		return -E2BIG;
+
+	return 0;
+}
+
+static int validate_id_aa64mmfr0_el1(struct kvm_vcpu *vcpu,
+				     const struct id_reg_info *id_reg, u64 val)
+{
+	u64 limit = id_reg->vcpu_limit_val;
+	int ret;
+
+	ret = aa64mmfr0_tgran2_check(ID_AA64MMFR0_TGRAN4_2_SHIFT, val, limit);
+	if (ret)
+		return ret;
+
+	ret = aa64mmfr0_tgran2_check(ID_AA64MMFR0_TGRAN64_2_SHIFT, val, limit);
+	if (ret)
+		return ret;
+
+	ret = aa64mmfr0_tgran2_check(ID_AA64MMFR0_TGRAN16_2_SHIFT, val, limit);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
 static void init_id_aa64pfr0_el1_info(struct id_reg_info *id_reg)
 {
 	u64 limit = id_reg->vcpu_limit_val;
@@ -536,6 +648,20 @@ static struct id_reg_info id_aa64isar1_el1_info = {
 	.vcpu_mask = vcpu_mask_id_aa64isar1_el1,
 };
 
+static struct id_reg_info id_aa64mmfr0_el1_info = {
+	.sys_reg = SYS_ID_AA64MMFR0_EL1,
+	/*
+	 * When TGranX_2 value is 0, validity of the value depend on TGranX
+	 * value, and TGranX_2 value must be validated against TGranX value,
+	 * which is done by validate_id_aa64mmfr0_el1.
+	 * So, skip the regular validity checking for TGranX_2 fields.
+	 */
+	.ignore_mask = ARM64_FEATURE_MASK(ID_AA64MMFR0_TGRAN4_2) |
+		       ARM64_FEATURE_MASK(ID_AA64MMFR0_TGRAN64_2) |
+		       ARM64_FEATURE_MASK(ID_AA64MMFR0_TGRAN16_2),
+	.validate = validate_id_aa64mmfr0_el1,
+};
+
 /*
  * An ID register that needs special handling to control the value for the
  * guest must have its own id_reg_info in id_reg_info_table.
@@ -549,6 +675,7 @@ static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {
 	[IDREG_IDX(SYS_ID_AA64PFR1_EL1)] = &id_aa64pfr1_el1_info,
 	[IDREG_IDX(SYS_ID_AA64ISAR0_EL1)] = &id_aa64isar0_el1_info,
 	[IDREG_IDX(SYS_ID_AA64ISAR1_EL1)] = &id_aa64isar1_el1_info,
+	[IDREG_IDX(SYS_ID_AA64MMFR0_EL1)] = &id_aa64mmfr0_el1_info,
 };
 
 static int validate_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
-- 
2.34.1.448.ga2b2bfdf31-goog

_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 08/26] KVM: arm64: Make ID_AA64MMFR0_EL1 writable
@ 2022-01-06  4:26   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

This patch adds id_reg_info for ID_AA64MMFR0_EL1 to make it
writable by userspace.

Since ID_AA64MMFR0_EL1 stage 2 granule size fields don't follow the
standard ID scheme, we need a special handling to validate those fields.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/kvm/sys_regs.c | 127 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 127 insertions(+)

diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 2f79997016a4..723910267966 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -445,6 +445,118 @@ static int validate_id_aa64isar1_el1(struct kvm_vcpu *vcpu,
 	return 0;
 }
 
+/*
+ * Check if the requested stage2 translation granule size indicated in
+ * @mmfr0 is also indicated in @mmfr0_lim.
+ * If TGranX_2 field is zero, the value must be validated based on TGranX
+ * field because that indicates the feature support is identified in
+ * TGranX field.
+ * This function relies on the fact TGranX fields are validated before
+ * through the arm64_check_features.
+ */
+static int aa64mmfr0_tgran2_check(int field, u64 mmfr0, u64 mmfr0_lim)
+{
+	s64 tgran2, lim_tgran2, rtgran1;
+	int f1;
+	bool is_signed;
+
+	tgran2 = cpuid_feature_extract_unsigned_field(mmfr0, field);
+	lim_tgran2 = cpuid_feature_extract_unsigned_field(mmfr0_lim, field);
+	if (tgran2 && lim_tgran2)
+		/*
+		 * We don't need to check TGranX field. We can simply
+		 * compare tgran2 and lim_tgran2.
+		 */
+		return (tgran2 > lim_tgran2) ? -E2BIG : 0;
+
+	if (tgran2 == lim_tgran2)
+		/*
+		 * Both of them are zero.  Since TGranX in @mmfr0 is already
+		 * validated by arm64_check_features, tgran2 must be fine.
+		 */
+		return 0;
+
+	/*
+	 * Either tgran2 or lim_tgran2 is zero.
+	 * Need stage1 granule size to validate tgran2.
+	 */
+
+	/*
+	 * Get TGranX's bit position by subtracting 12 from TGranX_2's bit
+	 * position.
+	 */
+	f1 = field - 12;
+
+	/* TGran4/TGran64 is signed and TGran16 is unsigned field. */
+	is_signed = (f1 == ID_AA64MMFR0_TGRAN16_SHIFT) ? false : true;
+
+	/*
+	 * If tgran2 == 0 (&& lim_tgran2 != 0), the requested stage2 granule
+	 * size is indicated in the stage1 granule size field of @mmfr0.
+	 * So, validate the stage1 granule size against the stage2 limit
+	 * granule size.
+	 * If lim_tgran2 == 0 (&& tgran2 != 0), the stage2 limit granule size
+	 * is indicated in the stage1 granule size field of @mmfr0_lim.
+	 * So, validate the requested stage2 granule size against the stage1
+	 * limit granule size.
+	 */
+
+	 /* Get the relevant stage1 granule size to validate tgran2 */
+	if (tgran2 == 0)
+		/* The requested stage1 granule size */
+		rtgran1 = cpuid_feature_extract_field(mmfr0, f1, is_signed);
+	else /* lim_tgran2 == 0 */
+		/* The stage1 limit granule size */
+		rtgran1 = cpuid_feature_extract_field(mmfr0_lim, f1, is_signed);
+
+	/*
+	 * Adjust the value of rtgran1 to compare with stage2 granule size,
+	 * which indicates: 1: Not supported, 2: Supported, etc.
+	 */
+	if (is_signed)
+		/* For signed, -1: Not supported, 0: Supported, etc. */
+		rtgran1 += 0x2;
+	else
+		/* For unsigned, 0: Not supported, 1: Supported, etc. */
+		rtgran1 += 0x1;
+
+	if ((tgran2 == 0) && (rtgran1 > lim_tgran2))
+		/*
+		 * The requested stage1 granule size (== the requested stage2
+		 * granule size) is larger than the stage2 limit granule size.
+		 */
+		return -E2BIG;
+	else if ((lim_tgran2 == 0) && (tgran2 > rtgran1))
+		/*
+		 * The requested stage2 granule size is larger than the stage1
+		 * limit granulze size (== the stage2 limit granule size).
+		 */
+		return -E2BIG;
+
+	return 0;
+}
+
+static int validate_id_aa64mmfr0_el1(struct kvm_vcpu *vcpu,
+				     const struct id_reg_info *id_reg, u64 val)
+{
+	u64 limit = id_reg->vcpu_limit_val;
+	int ret;
+
+	ret = aa64mmfr0_tgran2_check(ID_AA64MMFR0_TGRAN4_2_SHIFT, val, limit);
+	if (ret)
+		return ret;
+
+	ret = aa64mmfr0_tgran2_check(ID_AA64MMFR0_TGRAN64_2_SHIFT, val, limit);
+	if (ret)
+		return ret;
+
+	ret = aa64mmfr0_tgran2_check(ID_AA64MMFR0_TGRAN16_2_SHIFT, val, limit);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
 static void init_id_aa64pfr0_el1_info(struct id_reg_info *id_reg)
 {
 	u64 limit = id_reg->vcpu_limit_val;
@@ -536,6 +648,20 @@ static struct id_reg_info id_aa64isar1_el1_info = {
 	.vcpu_mask = vcpu_mask_id_aa64isar1_el1,
 };
 
+static struct id_reg_info id_aa64mmfr0_el1_info = {
+	.sys_reg = SYS_ID_AA64MMFR0_EL1,
+	/*
+	 * When TGranX_2 value is 0, validity of the value depend on TGranX
+	 * value, and TGranX_2 value must be validated against TGranX value,
+	 * which is done by validate_id_aa64mmfr0_el1.
+	 * So, skip the regular validity checking for TGranX_2 fields.
+	 */
+	.ignore_mask = ARM64_FEATURE_MASK(ID_AA64MMFR0_TGRAN4_2) |
+		       ARM64_FEATURE_MASK(ID_AA64MMFR0_TGRAN64_2) |
+		       ARM64_FEATURE_MASK(ID_AA64MMFR0_TGRAN16_2),
+	.validate = validate_id_aa64mmfr0_el1,
+};
+
 /*
  * An ID register that needs special handling to control the value for the
  * guest must have its own id_reg_info in id_reg_info_table.
@@ -549,6 +675,7 @@ static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {
 	[IDREG_IDX(SYS_ID_AA64PFR1_EL1)] = &id_aa64pfr1_el1_info,
 	[IDREG_IDX(SYS_ID_AA64ISAR0_EL1)] = &id_aa64isar0_el1_info,
 	[IDREG_IDX(SYS_ID_AA64ISAR1_EL1)] = &id_aa64isar1_el1_info,
+	[IDREG_IDX(SYS_ID_AA64MMFR0_EL1)] = &id_aa64mmfr0_el1_info,
 };
 
 static int validate_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
-- 
2.34.1.448.ga2b2bfdf31-goog


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 09/26] KVM: arm64: Hide IMPLEMENTATION DEFINED PMU support for the guest
  2022-01-06  4:26 ` Reiji Watanabe
  (?)
@ 2022-01-06  4:26   ` Reiji Watanabe
  -1 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

When ID_AA64DFR0_EL1.PMUVER or ID_DFR0_EL1.PERFMON is 0xf, which
means IMPLEMENTATION DEFINED PMU supported, KVM unconditionally
expose the value for the guest as it is.  Since KVM doesn't support
IMPLEMENTATION DEFINED PMU for the guest, in that case KVM should
expose 0x0 (PMU is not implemented) instead.

Change cpuid_feature_cap_perfmon_field() to update the field value
to 0x0 when it is 0xf.

Fixes: 8e35aa642ee4 ("arm64: cpufeature: Extract capped perfmon fields")
Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/include/asm/cpufeature.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
index eda7ddbed8cf..487ca7555c18 100644
--- a/arch/arm64/include/asm/cpufeature.h
+++ b/arch/arm64/include/asm/cpufeature.h
@@ -553,7 +553,7 @@ cpuid_feature_cap_perfmon_field(u64 features, int field, u64 cap)
 
 	/* Treat IMPLEMENTATION DEFINED functionality as unimplemented */
 	if (val == ID_AA64DFR0_PMUVER_IMP_DEF)
-		val = 0;
+		return (features & ~mask);
 
 	if (val > cap) {
 		features &= ~mask;
-- 
2.34.1.448.ga2b2bfdf31-goog


^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 09/26] KVM: arm64: Hide IMPLEMENTATION DEFINED PMU support for the guest
@ 2022-01-06  4:26   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, Will Deacon, Peter Shier, Paolo Bonzini, linux-arm-kernel

When ID_AA64DFR0_EL1.PMUVER or ID_DFR0_EL1.PERFMON is 0xf, which
means IMPLEMENTATION DEFINED PMU supported, KVM unconditionally
expose the value for the guest as it is.  Since KVM doesn't support
IMPLEMENTATION DEFINED PMU for the guest, in that case KVM should
expose 0x0 (PMU is not implemented) instead.

Change cpuid_feature_cap_perfmon_field() to update the field value
to 0x0 when it is 0xf.

Fixes: 8e35aa642ee4 ("arm64: cpufeature: Extract capped perfmon fields")
Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/include/asm/cpufeature.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
index eda7ddbed8cf..487ca7555c18 100644
--- a/arch/arm64/include/asm/cpufeature.h
+++ b/arch/arm64/include/asm/cpufeature.h
@@ -553,7 +553,7 @@ cpuid_feature_cap_perfmon_field(u64 features, int field, u64 cap)
 
 	/* Treat IMPLEMENTATION DEFINED functionality as unimplemented */
 	if (val == ID_AA64DFR0_PMUVER_IMP_DEF)
-		val = 0;
+		return (features & ~mask);
 
 	if (val > cap) {
 		features &= ~mask;
-- 
2.34.1.448.ga2b2bfdf31-goog

_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 09/26] KVM: arm64: Hide IMPLEMENTATION DEFINED PMU support for the guest
@ 2022-01-06  4:26   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

When ID_AA64DFR0_EL1.PMUVER or ID_DFR0_EL1.PERFMON is 0xf, which
means IMPLEMENTATION DEFINED PMU supported, KVM unconditionally
expose the value for the guest as it is.  Since KVM doesn't support
IMPLEMENTATION DEFINED PMU for the guest, in that case KVM should
expose 0x0 (PMU is not implemented) instead.

Change cpuid_feature_cap_perfmon_field() to update the field value
to 0x0 when it is 0xf.

Fixes: 8e35aa642ee4 ("arm64: cpufeature: Extract capped perfmon fields")
Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/include/asm/cpufeature.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
index eda7ddbed8cf..487ca7555c18 100644
--- a/arch/arm64/include/asm/cpufeature.h
+++ b/arch/arm64/include/asm/cpufeature.h
@@ -553,7 +553,7 @@ cpuid_feature_cap_perfmon_field(u64 features, int field, u64 cap)
 
 	/* Treat IMPLEMENTATION DEFINED functionality as unimplemented */
 	if (val == ID_AA64DFR0_PMUVER_IMP_DEF)
-		val = 0;
+		return (features & ~mask);
 
 	if (val > cap) {
 		features &= ~mask;
-- 
2.34.1.448.ga2b2bfdf31-goog


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 10/26] KVM: arm64: Make ID_AA64DFR0_EL1 writable
  2022-01-06  4:26 ` Reiji Watanabe
  (?)
@ 2022-01-06  4:26   ` Reiji Watanabe
  -1 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

This patch adds id_reg_info for ID_AA64DFR0_EL1 to make it writable
by userspace.

Return an error if userspace tries to set PMUVER field of the
register to a value that conflicts with the PMU configuration.

Since number of context-aware breakpoints must be no more than number
of supported breakpoints according to Arm ARM, return an error
if userspace tries to set CTX_CMPS field to such value.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/kvm/sys_regs.c | 83 +++++++++++++++++++++++++++++++++------
 1 file changed, 71 insertions(+), 12 deletions(-)

diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 723910267966..9a9055d60223 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -557,6 +557,45 @@ static int validate_id_aa64mmfr0_el1(struct kvm_vcpu *vcpu,
 	return 0;
 }
 
+static bool id_reg_has_pmu(u64 val, u64 shift, unsigned int min)
+{
+	unsigned int pmu = cpuid_feature_extract_unsigned_field(val, shift);
+
+	/*
+	 * Treat IMPLEMENTATION DEFINED functionality as unimplemented for
+	 * ID_AA64DFR0_EL1.PMUVer/ID_DFR0_EL1.PerfMon.
+	 */
+	if (pmu == 0xf)
+		pmu = 0;
+
+	return (pmu >= min);
+}
+
+static int validate_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
+				    const struct id_reg_info *id_reg, u64 val)
+{
+	unsigned int brps, ctx_cmps;
+	bool vcpu_pmu, dfr0_pmu;
+
+	brps = cpuid_feature_extract_unsigned_field(val, ID_AA64DFR0_BRPS_SHIFT);
+	ctx_cmps = cpuid_feature_extract_unsigned_field(val, ID_AA64DFR0_CTX_CMPS_SHIFT);
+
+	/*
+	 * Number of context-aware breakpoints can be no more than number of
+	 * supported breakpoints.
+	 */
+	if (ctx_cmps > brps)
+		return -EINVAL;
+
+	vcpu_pmu = kvm_vcpu_has_pmu(vcpu);
+	dfr0_pmu = id_reg_has_pmu(val, ID_AA64DFR0_PMUVER_SHIFT, ID_AA64DFR0_PMUVER_8_0);
+	/* Check if there is a conflict with a request via KVM_ARM_VCPU_INIT */
+	if (vcpu_pmu ^ dfr0_pmu)
+		return -EPERM;
+
+	return 0;
+}
+
 static void init_id_aa64pfr0_el1_info(struct id_reg_info *id_reg)
 {
 	u64 limit = id_reg->vcpu_limit_val;
@@ -600,8 +639,25 @@ static void init_id_aa64isar1_el1_info(struct id_reg_info *id_reg)
 		id_reg->vcpu_limit_val &= ~PTRAUTH_MASK;
 }
 
+static void init_id_aa64dfr0_el1_info(struct id_reg_info *id_reg)
+{
+	u64 limit = id_reg->vcpu_limit_val;
+
+	/* Limit guests to PMUv3 for ARMv8.4 */
+	limit = cpuid_feature_cap_perfmon_field(limit, ID_AA64DFR0_PMUVER_SHIFT,
+						ID_AA64DFR0_PMUVER_8_4);
+	/* Limit debug to ARMv8.0 */
+	limit &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_DEBUGVER);
+	limit |= (FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_DEBUGVER), 6));
+
+	/* Hide SPE from guests */
+	limit &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_PMSVER);
+
+	id_reg->vcpu_limit_val = limit;
+}
+
 static u64 vcpu_mask_id_aa64pfr0_el1(const struct kvm_vcpu *vcpu,
-				     const struct id_reg_info *idr)
+					 const struct id_reg_info *idr)
 {
 	return vcpu_has_sve(vcpu) ? 0 : ARM64_FEATURE_MASK(ID_AA64PFR0_SVE);
 }
@@ -618,6 +674,12 @@ static u64 vcpu_mask_id_aa64isar1_el1(const struct kvm_vcpu *vcpu,
 	return vcpu_has_ptrauth(vcpu) ? 0 : PTRAUTH_MASK;
 }
 
+static u64 vcpu_mask_id_aa64dfr0_el1(const struct kvm_vcpu *vcpu,
+					 const struct id_reg_info *idr)
+{
+	return kvm_vcpu_has_pmu(vcpu) ? 0 : ARM64_FEATURE_MASK(ID_AA64DFR0_PMUVER);
+}
+
 static struct id_reg_info id_aa64pfr0_el1_info = {
 	.sys_reg = SYS_ID_AA64PFR0_EL1,
 	.ignore_mask = ARM64_FEATURE_MASK(ID_AA64PFR0_GIC),
@@ -662,6 +724,13 @@ static struct id_reg_info id_aa64mmfr0_el1_info = {
 	.validate = validate_id_aa64mmfr0_el1,
 };
 
+static struct id_reg_info id_aa64dfr0_el1_info = {
+	.sys_reg = SYS_ID_AA64DFR0_EL1,
+	.init = init_id_aa64dfr0_el1_info,
+	.validate = validate_id_aa64dfr0_el1,
+	.vcpu_mask = vcpu_mask_id_aa64dfr0_el1,
+};
+
 /*
  * An ID register that needs special handling to control the value for the
  * guest must have its own id_reg_info in id_reg_info_table.
@@ -673,6 +742,7 @@ static struct id_reg_info id_aa64mmfr0_el1_info = {
 static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {
 	[IDREG_IDX(SYS_ID_AA64PFR0_EL1)] = &id_aa64pfr0_el1_info,
 	[IDREG_IDX(SYS_ID_AA64PFR1_EL1)] = &id_aa64pfr1_el1_info,
+	[IDREG_IDX(SYS_ID_AA64DFR0_EL1)] = &id_aa64dfr0_el1_info,
 	[IDREG_IDX(SYS_ID_AA64ISAR0_EL1)] = &id_aa64isar0_el1_info,
 	[IDREG_IDX(SYS_ID_AA64ISAR1_EL1)] = &id_aa64isar1_el1_info,
 	[IDREG_IDX(SYS_ID_AA64MMFR0_EL1)] = &id_aa64mmfr0_el1_info,
@@ -1593,17 +1663,6 @@ static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
 		val &= ~(id_reg->vcpu_mask(vcpu, id_reg));
 
 	switch (id) {
-	case SYS_ID_AA64DFR0_EL1:
-		/* Limit debug to ARMv8.0 */
-		val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_DEBUGVER);
-		val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_DEBUGVER), 6);
-		/* Limit guests to PMUv3 for ARMv8.4 */
-		val = cpuid_feature_cap_perfmon_field(val,
-						      ID_AA64DFR0_PMUVER_SHIFT,
-						      kvm_vcpu_has_pmu(vcpu) ? ID_AA64DFR0_PMUVER_8_4 : 0);
-		/* Hide SPE from guests */
-		val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_PMSVER);
-		break;
 	case SYS_ID_DFR0_EL1:
 		/* Limit guests to PMUv3 for ARMv8.4 */
 		val = cpuid_feature_cap_perfmon_field(val,
-- 
2.34.1.448.ga2b2bfdf31-goog


^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 10/26] KVM: arm64: Make ID_AA64DFR0_EL1 writable
@ 2022-01-06  4:26   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, Will Deacon, Peter Shier, Paolo Bonzini, linux-arm-kernel

This patch adds id_reg_info for ID_AA64DFR0_EL1 to make it writable
by userspace.

Return an error if userspace tries to set PMUVER field of the
register to a value that conflicts with the PMU configuration.

Since number of context-aware breakpoints must be no more than number
of supported breakpoints according to Arm ARM, return an error
if userspace tries to set CTX_CMPS field to such value.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/kvm/sys_regs.c | 83 +++++++++++++++++++++++++++++++++------
 1 file changed, 71 insertions(+), 12 deletions(-)

diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 723910267966..9a9055d60223 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -557,6 +557,45 @@ static int validate_id_aa64mmfr0_el1(struct kvm_vcpu *vcpu,
 	return 0;
 }
 
+static bool id_reg_has_pmu(u64 val, u64 shift, unsigned int min)
+{
+	unsigned int pmu = cpuid_feature_extract_unsigned_field(val, shift);
+
+	/*
+	 * Treat IMPLEMENTATION DEFINED functionality as unimplemented for
+	 * ID_AA64DFR0_EL1.PMUVer/ID_DFR0_EL1.PerfMon.
+	 */
+	if (pmu == 0xf)
+		pmu = 0;
+
+	return (pmu >= min);
+}
+
+static int validate_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
+				    const struct id_reg_info *id_reg, u64 val)
+{
+	unsigned int brps, ctx_cmps;
+	bool vcpu_pmu, dfr0_pmu;
+
+	brps = cpuid_feature_extract_unsigned_field(val, ID_AA64DFR0_BRPS_SHIFT);
+	ctx_cmps = cpuid_feature_extract_unsigned_field(val, ID_AA64DFR0_CTX_CMPS_SHIFT);
+
+	/*
+	 * Number of context-aware breakpoints can be no more than number of
+	 * supported breakpoints.
+	 */
+	if (ctx_cmps > brps)
+		return -EINVAL;
+
+	vcpu_pmu = kvm_vcpu_has_pmu(vcpu);
+	dfr0_pmu = id_reg_has_pmu(val, ID_AA64DFR0_PMUVER_SHIFT, ID_AA64DFR0_PMUVER_8_0);
+	/* Check if there is a conflict with a request via KVM_ARM_VCPU_INIT */
+	if (vcpu_pmu ^ dfr0_pmu)
+		return -EPERM;
+
+	return 0;
+}
+
 static void init_id_aa64pfr0_el1_info(struct id_reg_info *id_reg)
 {
 	u64 limit = id_reg->vcpu_limit_val;
@@ -600,8 +639,25 @@ static void init_id_aa64isar1_el1_info(struct id_reg_info *id_reg)
 		id_reg->vcpu_limit_val &= ~PTRAUTH_MASK;
 }
 
+static void init_id_aa64dfr0_el1_info(struct id_reg_info *id_reg)
+{
+	u64 limit = id_reg->vcpu_limit_val;
+
+	/* Limit guests to PMUv3 for ARMv8.4 */
+	limit = cpuid_feature_cap_perfmon_field(limit, ID_AA64DFR0_PMUVER_SHIFT,
+						ID_AA64DFR0_PMUVER_8_4);
+	/* Limit debug to ARMv8.0 */
+	limit &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_DEBUGVER);
+	limit |= (FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_DEBUGVER), 6));
+
+	/* Hide SPE from guests */
+	limit &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_PMSVER);
+
+	id_reg->vcpu_limit_val = limit;
+}
+
 static u64 vcpu_mask_id_aa64pfr0_el1(const struct kvm_vcpu *vcpu,
-				     const struct id_reg_info *idr)
+					 const struct id_reg_info *idr)
 {
 	return vcpu_has_sve(vcpu) ? 0 : ARM64_FEATURE_MASK(ID_AA64PFR0_SVE);
 }
@@ -618,6 +674,12 @@ static u64 vcpu_mask_id_aa64isar1_el1(const struct kvm_vcpu *vcpu,
 	return vcpu_has_ptrauth(vcpu) ? 0 : PTRAUTH_MASK;
 }
 
+static u64 vcpu_mask_id_aa64dfr0_el1(const struct kvm_vcpu *vcpu,
+					 const struct id_reg_info *idr)
+{
+	return kvm_vcpu_has_pmu(vcpu) ? 0 : ARM64_FEATURE_MASK(ID_AA64DFR0_PMUVER);
+}
+
 static struct id_reg_info id_aa64pfr0_el1_info = {
 	.sys_reg = SYS_ID_AA64PFR0_EL1,
 	.ignore_mask = ARM64_FEATURE_MASK(ID_AA64PFR0_GIC),
@@ -662,6 +724,13 @@ static struct id_reg_info id_aa64mmfr0_el1_info = {
 	.validate = validate_id_aa64mmfr0_el1,
 };
 
+static struct id_reg_info id_aa64dfr0_el1_info = {
+	.sys_reg = SYS_ID_AA64DFR0_EL1,
+	.init = init_id_aa64dfr0_el1_info,
+	.validate = validate_id_aa64dfr0_el1,
+	.vcpu_mask = vcpu_mask_id_aa64dfr0_el1,
+};
+
 /*
  * An ID register that needs special handling to control the value for the
  * guest must have its own id_reg_info in id_reg_info_table.
@@ -673,6 +742,7 @@ static struct id_reg_info id_aa64mmfr0_el1_info = {
 static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {
 	[IDREG_IDX(SYS_ID_AA64PFR0_EL1)] = &id_aa64pfr0_el1_info,
 	[IDREG_IDX(SYS_ID_AA64PFR1_EL1)] = &id_aa64pfr1_el1_info,
+	[IDREG_IDX(SYS_ID_AA64DFR0_EL1)] = &id_aa64dfr0_el1_info,
 	[IDREG_IDX(SYS_ID_AA64ISAR0_EL1)] = &id_aa64isar0_el1_info,
 	[IDREG_IDX(SYS_ID_AA64ISAR1_EL1)] = &id_aa64isar1_el1_info,
 	[IDREG_IDX(SYS_ID_AA64MMFR0_EL1)] = &id_aa64mmfr0_el1_info,
@@ -1593,17 +1663,6 @@ static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
 		val &= ~(id_reg->vcpu_mask(vcpu, id_reg));
 
 	switch (id) {
-	case SYS_ID_AA64DFR0_EL1:
-		/* Limit debug to ARMv8.0 */
-		val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_DEBUGVER);
-		val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_DEBUGVER), 6);
-		/* Limit guests to PMUv3 for ARMv8.4 */
-		val = cpuid_feature_cap_perfmon_field(val,
-						      ID_AA64DFR0_PMUVER_SHIFT,
-						      kvm_vcpu_has_pmu(vcpu) ? ID_AA64DFR0_PMUVER_8_4 : 0);
-		/* Hide SPE from guests */
-		val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_PMSVER);
-		break;
 	case SYS_ID_DFR0_EL1:
 		/* Limit guests to PMUv3 for ARMv8.4 */
 		val = cpuid_feature_cap_perfmon_field(val,
-- 
2.34.1.448.ga2b2bfdf31-goog

_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 10/26] KVM: arm64: Make ID_AA64DFR0_EL1 writable
@ 2022-01-06  4:26   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

This patch adds id_reg_info for ID_AA64DFR0_EL1 to make it writable
by userspace.

Return an error if userspace tries to set PMUVER field of the
register to a value that conflicts with the PMU configuration.

Since number of context-aware breakpoints must be no more than number
of supported breakpoints according to Arm ARM, return an error
if userspace tries to set CTX_CMPS field to such value.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/kvm/sys_regs.c | 83 +++++++++++++++++++++++++++++++++------
 1 file changed, 71 insertions(+), 12 deletions(-)

diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 723910267966..9a9055d60223 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -557,6 +557,45 @@ static int validate_id_aa64mmfr0_el1(struct kvm_vcpu *vcpu,
 	return 0;
 }
 
+static bool id_reg_has_pmu(u64 val, u64 shift, unsigned int min)
+{
+	unsigned int pmu = cpuid_feature_extract_unsigned_field(val, shift);
+
+	/*
+	 * Treat IMPLEMENTATION DEFINED functionality as unimplemented for
+	 * ID_AA64DFR0_EL1.PMUVer/ID_DFR0_EL1.PerfMon.
+	 */
+	if (pmu == 0xf)
+		pmu = 0;
+
+	return (pmu >= min);
+}
+
+static int validate_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
+				    const struct id_reg_info *id_reg, u64 val)
+{
+	unsigned int brps, ctx_cmps;
+	bool vcpu_pmu, dfr0_pmu;
+
+	brps = cpuid_feature_extract_unsigned_field(val, ID_AA64DFR0_BRPS_SHIFT);
+	ctx_cmps = cpuid_feature_extract_unsigned_field(val, ID_AA64DFR0_CTX_CMPS_SHIFT);
+
+	/*
+	 * Number of context-aware breakpoints can be no more than number of
+	 * supported breakpoints.
+	 */
+	if (ctx_cmps > brps)
+		return -EINVAL;
+
+	vcpu_pmu = kvm_vcpu_has_pmu(vcpu);
+	dfr0_pmu = id_reg_has_pmu(val, ID_AA64DFR0_PMUVER_SHIFT, ID_AA64DFR0_PMUVER_8_0);
+	/* Check if there is a conflict with a request via KVM_ARM_VCPU_INIT */
+	if (vcpu_pmu ^ dfr0_pmu)
+		return -EPERM;
+
+	return 0;
+}
+
 static void init_id_aa64pfr0_el1_info(struct id_reg_info *id_reg)
 {
 	u64 limit = id_reg->vcpu_limit_val;
@@ -600,8 +639,25 @@ static void init_id_aa64isar1_el1_info(struct id_reg_info *id_reg)
 		id_reg->vcpu_limit_val &= ~PTRAUTH_MASK;
 }
 
+static void init_id_aa64dfr0_el1_info(struct id_reg_info *id_reg)
+{
+	u64 limit = id_reg->vcpu_limit_val;
+
+	/* Limit guests to PMUv3 for ARMv8.4 */
+	limit = cpuid_feature_cap_perfmon_field(limit, ID_AA64DFR0_PMUVER_SHIFT,
+						ID_AA64DFR0_PMUVER_8_4);
+	/* Limit debug to ARMv8.0 */
+	limit &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_DEBUGVER);
+	limit |= (FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_DEBUGVER), 6));
+
+	/* Hide SPE from guests */
+	limit &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_PMSVER);
+
+	id_reg->vcpu_limit_val = limit;
+}
+
 static u64 vcpu_mask_id_aa64pfr0_el1(const struct kvm_vcpu *vcpu,
-				     const struct id_reg_info *idr)
+					 const struct id_reg_info *idr)
 {
 	return vcpu_has_sve(vcpu) ? 0 : ARM64_FEATURE_MASK(ID_AA64PFR0_SVE);
 }
@@ -618,6 +674,12 @@ static u64 vcpu_mask_id_aa64isar1_el1(const struct kvm_vcpu *vcpu,
 	return vcpu_has_ptrauth(vcpu) ? 0 : PTRAUTH_MASK;
 }
 
+static u64 vcpu_mask_id_aa64dfr0_el1(const struct kvm_vcpu *vcpu,
+					 const struct id_reg_info *idr)
+{
+	return kvm_vcpu_has_pmu(vcpu) ? 0 : ARM64_FEATURE_MASK(ID_AA64DFR0_PMUVER);
+}
+
 static struct id_reg_info id_aa64pfr0_el1_info = {
 	.sys_reg = SYS_ID_AA64PFR0_EL1,
 	.ignore_mask = ARM64_FEATURE_MASK(ID_AA64PFR0_GIC),
@@ -662,6 +724,13 @@ static struct id_reg_info id_aa64mmfr0_el1_info = {
 	.validate = validate_id_aa64mmfr0_el1,
 };
 
+static struct id_reg_info id_aa64dfr0_el1_info = {
+	.sys_reg = SYS_ID_AA64DFR0_EL1,
+	.init = init_id_aa64dfr0_el1_info,
+	.validate = validate_id_aa64dfr0_el1,
+	.vcpu_mask = vcpu_mask_id_aa64dfr0_el1,
+};
+
 /*
  * An ID register that needs special handling to control the value for the
  * guest must have its own id_reg_info in id_reg_info_table.
@@ -673,6 +742,7 @@ static struct id_reg_info id_aa64mmfr0_el1_info = {
 static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {
 	[IDREG_IDX(SYS_ID_AA64PFR0_EL1)] = &id_aa64pfr0_el1_info,
 	[IDREG_IDX(SYS_ID_AA64PFR1_EL1)] = &id_aa64pfr1_el1_info,
+	[IDREG_IDX(SYS_ID_AA64DFR0_EL1)] = &id_aa64dfr0_el1_info,
 	[IDREG_IDX(SYS_ID_AA64ISAR0_EL1)] = &id_aa64isar0_el1_info,
 	[IDREG_IDX(SYS_ID_AA64ISAR1_EL1)] = &id_aa64isar1_el1_info,
 	[IDREG_IDX(SYS_ID_AA64MMFR0_EL1)] = &id_aa64mmfr0_el1_info,
@@ -1593,17 +1663,6 @@ static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
 		val &= ~(id_reg->vcpu_mask(vcpu, id_reg));
 
 	switch (id) {
-	case SYS_ID_AA64DFR0_EL1:
-		/* Limit debug to ARMv8.0 */
-		val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_DEBUGVER);
-		val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_DEBUGVER), 6);
-		/* Limit guests to PMUv3 for ARMv8.4 */
-		val = cpuid_feature_cap_perfmon_field(val,
-						      ID_AA64DFR0_PMUVER_SHIFT,
-						      kvm_vcpu_has_pmu(vcpu) ? ID_AA64DFR0_PMUVER_8_4 : 0);
-		/* Hide SPE from guests */
-		val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_PMSVER);
-		break;
 	case SYS_ID_DFR0_EL1:
 		/* Limit guests to PMUv3 for ARMv8.4 */
 		val = cpuid_feature_cap_perfmon_field(val,
-- 
2.34.1.448.ga2b2bfdf31-goog


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 11/26] KVM: arm64: Make ID_DFR0_EL1 writable
  2022-01-06  4:26 ` Reiji Watanabe
  (?)
@ 2022-01-06  4:26   ` Reiji Watanabe
  -1 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

This patch adds id_reg_info for ID_DFR0_EL1 to make it writable
by userspace.

Return an error if userspace tries to set PerfMon field of the
register to a value that conflicts with the PMU configuration.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/kvm/sys_regs.c | 55 ++++++++++++++++++++++++++++++++-------
 1 file changed, 45 insertions(+), 10 deletions(-)

diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 9a9055d60223..1707c7832593 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -596,6 +596,27 @@ static int validate_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
 	return 0;
 }
 
+static int validate_id_dfr0_el1(struct kvm_vcpu *vcpu,
+				const struct id_reg_info *id_reg, u64 val)
+{
+	bool vcpu_pmu, dfr0_pmu;
+	unsigned int perfmon;
+
+	perfmon = cpuid_feature_extract_unsigned_field(val, ID_DFR0_PERFMON_SHIFT);
+	if (perfmon == 1 || perfmon == 2)
+		/* PMUv1 or PMUv2 is not allowed on ARMv8. */
+		return -EINVAL;
+
+	vcpu_pmu = kvm_vcpu_has_pmu(vcpu);
+	dfr0_pmu = id_reg_has_pmu(val, ID_DFR0_PERFMON_SHIFT, ID_DFR0_PERFMON_8_0);
+
+	/* Check if there is a conflict with a request via KVM_ARM_VCPU_INIT */
+	if (vcpu_pmu ^ dfr0_pmu)
+		return -EPERM;
+
+	return 0;
+}
+
 static void init_id_aa64pfr0_el1_info(struct id_reg_info *id_reg)
 {
 	u64 limit = id_reg->vcpu_limit_val;
@@ -656,8 +677,17 @@ static void init_id_aa64dfr0_el1_info(struct id_reg_info *id_reg)
 	id_reg->vcpu_limit_val = limit;
 }
 
+static void init_id_dfr0_el1_info(struct id_reg_info *id_reg)
+{
+	/* Limit guests to PMUv3 for ARMv8.4 */
+	id_reg->vcpu_limit_val =
+		cpuid_feature_cap_perfmon_field(id_reg->vcpu_limit_val,
+						ID_DFR0_PERFMON_SHIFT,
+						ID_DFR0_PERFMON_8_4);
+}
+
 static u64 vcpu_mask_id_aa64pfr0_el1(const struct kvm_vcpu *vcpu,
-					 const struct id_reg_info *idr)
+				     const struct id_reg_info *idr)
 {
 	return vcpu_has_sve(vcpu) ? 0 : ARM64_FEATURE_MASK(ID_AA64PFR0_SVE);
 }
@@ -680,6 +710,12 @@ static u64 vcpu_mask_id_aa64dfr0_el1(const struct kvm_vcpu *vcpu,
 	return kvm_vcpu_has_pmu(vcpu) ? 0 : ARM64_FEATURE_MASK(ID_AA64DFR0_PMUVER);
 }
 
+static u64 vcpu_mask_id_dfr0_el1(const struct kvm_vcpu *vcpu,
+				     const struct id_reg_info *idr)
+{
+	return kvm_vcpu_has_pmu(vcpu) ? 0 : ARM64_FEATURE_MASK(ID_DFR0_PERFMON);
+}
+
 static struct id_reg_info id_aa64pfr0_el1_info = {
 	.sys_reg = SYS_ID_AA64PFR0_EL1,
 	.ignore_mask = ARM64_FEATURE_MASK(ID_AA64PFR0_GIC),
@@ -731,6 +767,13 @@ static struct id_reg_info id_aa64dfr0_el1_info = {
 	.vcpu_mask = vcpu_mask_id_aa64dfr0_el1,
 };
 
+static struct id_reg_info id_dfr0_el1_info = {
+	.sys_reg = SYS_ID_DFR0_EL1,
+	.init = init_id_dfr0_el1_info,
+	.validate = validate_id_dfr0_el1,
+	.vcpu_mask = vcpu_mask_id_dfr0_el1,
+};
+
 /*
  * An ID register that needs special handling to control the value for the
  * guest must have its own id_reg_info in id_reg_info_table.
@@ -740,6 +783,7 @@ static struct id_reg_info id_aa64dfr0_el1_info = {
  */
 #define	GET_ID_REG_INFO(id)	(id_reg_info_table[IDREG_IDX(id)])
 static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {
+	[IDREG_IDX(SYS_ID_DFR0_EL1)] = &id_dfr0_el1_info,
 	[IDREG_IDX(SYS_ID_AA64PFR0_EL1)] = &id_aa64pfr0_el1_info,
 	[IDREG_IDX(SYS_ID_AA64PFR1_EL1)] = &id_aa64pfr1_el1_info,
 	[IDREG_IDX(SYS_ID_AA64DFR0_EL1)] = &id_aa64dfr0_el1_info,
@@ -1662,15 +1706,6 @@ static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
 		/* Clear fields for opt-in features that are not configured. */
 		val &= ~(id_reg->vcpu_mask(vcpu, id_reg));
 
-	switch (id) {
-	case SYS_ID_DFR0_EL1:
-		/* Limit guests to PMUv3 for ARMv8.4 */
-		val = cpuid_feature_cap_perfmon_field(val,
-						      ID_DFR0_PERFMON_SHIFT,
-						      kvm_vcpu_has_pmu(vcpu) ? ID_DFR0_PERFMON_8_4 : 0);
-		break;
-	}
-
 	return val;
 }
 
-- 
2.34.1.448.ga2b2bfdf31-goog


^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 11/26] KVM: arm64: Make ID_DFR0_EL1 writable
@ 2022-01-06  4:26   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, Will Deacon, Peter Shier, Paolo Bonzini, linux-arm-kernel

This patch adds id_reg_info for ID_DFR0_EL1 to make it writable
by userspace.

Return an error if userspace tries to set PerfMon field of the
register to a value that conflicts with the PMU configuration.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/kvm/sys_regs.c | 55 ++++++++++++++++++++++++++++++++-------
 1 file changed, 45 insertions(+), 10 deletions(-)

diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 9a9055d60223..1707c7832593 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -596,6 +596,27 @@ static int validate_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
 	return 0;
 }
 
+static int validate_id_dfr0_el1(struct kvm_vcpu *vcpu,
+				const struct id_reg_info *id_reg, u64 val)
+{
+	bool vcpu_pmu, dfr0_pmu;
+	unsigned int perfmon;
+
+	perfmon = cpuid_feature_extract_unsigned_field(val, ID_DFR0_PERFMON_SHIFT);
+	if (perfmon == 1 || perfmon == 2)
+		/* PMUv1 or PMUv2 is not allowed on ARMv8. */
+		return -EINVAL;
+
+	vcpu_pmu = kvm_vcpu_has_pmu(vcpu);
+	dfr0_pmu = id_reg_has_pmu(val, ID_DFR0_PERFMON_SHIFT, ID_DFR0_PERFMON_8_0);
+
+	/* Check if there is a conflict with a request via KVM_ARM_VCPU_INIT */
+	if (vcpu_pmu ^ dfr0_pmu)
+		return -EPERM;
+
+	return 0;
+}
+
 static void init_id_aa64pfr0_el1_info(struct id_reg_info *id_reg)
 {
 	u64 limit = id_reg->vcpu_limit_val;
@@ -656,8 +677,17 @@ static void init_id_aa64dfr0_el1_info(struct id_reg_info *id_reg)
 	id_reg->vcpu_limit_val = limit;
 }
 
+static void init_id_dfr0_el1_info(struct id_reg_info *id_reg)
+{
+	/* Limit guests to PMUv3 for ARMv8.4 */
+	id_reg->vcpu_limit_val =
+		cpuid_feature_cap_perfmon_field(id_reg->vcpu_limit_val,
+						ID_DFR0_PERFMON_SHIFT,
+						ID_DFR0_PERFMON_8_4);
+}
+
 static u64 vcpu_mask_id_aa64pfr0_el1(const struct kvm_vcpu *vcpu,
-					 const struct id_reg_info *idr)
+				     const struct id_reg_info *idr)
 {
 	return vcpu_has_sve(vcpu) ? 0 : ARM64_FEATURE_MASK(ID_AA64PFR0_SVE);
 }
@@ -680,6 +710,12 @@ static u64 vcpu_mask_id_aa64dfr0_el1(const struct kvm_vcpu *vcpu,
 	return kvm_vcpu_has_pmu(vcpu) ? 0 : ARM64_FEATURE_MASK(ID_AA64DFR0_PMUVER);
 }
 
+static u64 vcpu_mask_id_dfr0_el1(const struct kvm_vcpu *vcpu,
+				     const struct id_reg_info *idr)
+{
+	return kvm_vcpu_has_pmu(vcpu) ? 0 : ARM64_FEATURE_MASK(ID_DFR0_PERFMON);
+}
+
 static struct id_reg_info id_aa64pfr0_el1_info = {
 	.sys_reg = SYS_ID_AA64PFR0_EL1,
 	.ignore_mask = ARM64_FEATURE_MASK(ID_AA64PFR0_GIC),
@@ -731,6 +767,13 @@ static struct id_reg_info id_aa64dfr0_el1_info = {
 	.vcpu_mask = vcpu_mask_id_aa64dfr0_el1,
 };
 
+static struct id_reg_info id_dfr0_el1_info = {
+	.sys_reg = SYS_ID_DFR0_EL1,
+	.init = init_id_dfr0_el1_info,
+	.validate = validate_id_dfr0_el1,
+	.vcpu_mask = vcpu_mask_id_dfr0_el1,
+};
+
 /*
  * An ID register that needs special handling to control the value for the
  * guest must have its own id_reg_info in id_reg_info_table.
@@ -740,6 +783,7 @@ static struct id_reg_info id_aa64dfr0_el1_info = {
  */
 #define	GET_ID_REG_INFO(id)	(id_reg_info_table[IDREG_IDX(id)])
 static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {
+	[IDREG_IDX(SYS_ID_DFR0_EL1)] = &id_dfr0_el1_info,
 	[IDREG_IDX(SYS_ID_AA64PFR0_EL1)] = &id_aa64pfr0_el1_info,
 	[IDREG_IDX(SYS_ID_AA64PFR1_EL1)] = &id_aa64pfr1_el1_info,
 	[IDREG_IDX(SYS_ID_AA64DFR0_EL1)] = &id_aa64dfr0_el1_info,
@@ -1662,15 +1706,6 @@ static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
 		/* Clear fields for opt-in features that are not configured. */
 		val &= ~(id_reg->vcpu_mask(vcpu, id_reg));
 
-	switch (id) {
-	case SYS_ID_DFR0_EL1:
-		/* Limit guests to PMUv3 for ARMv8.4 */
-		val = cpuid_feature_cap_perfmon_field(val,
-						      ID_DFR0_PERFMON_SHIFT,
-						      kvm_vcpu_has_pmu(vcpu) ? ID_DFR0_PERFMON_8_4 : 0);
-		break;
-	}
-
 	return val;
 }
 
-- 
2.34.1.448.ga2b2bfdf31-goog

_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 11/26] KVM: arm64: Make ID_DFR0_EL1 writable
@ 2022-01-06  4:26   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

This patch adds id_reg_info for ID_DFR0_EL1 to make it writable
by userspace.

Return an error if userspace tries to set PerfMon field of the
register to a value that conflicts with the PMU configuration.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/kvm/sys_regs.c | 55 ++++++++++++++++++++++++++++++++-------
 1 file changed, 45 insertions(+), 10 deletions(-)

diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 9a9055d60223..1707c7832593 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -596,6 +596,27 @@ static int validate_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
 	return 0;
 }
 
+static int validate_id_dfr0_el1(struct kvm_vcpu *vcpu,
+				const struct id_reg_info *id_reg, u64 val)
+{
+	bool vcpu_pmu, dfr0_pmu;
+	unsigned int perfmon;
+
+	perfmon = cpuid_feature_extract_unsigned_field(val, ID_DFR0_PERFMON_SHIFT);
+	if (perfmon == 1 || perfmon == 2)
+		/* PMUv1 or PMUv2 is not allowed on ARMv8. */
+		return -EINVAL;
+
+	vcpu_pmu = kvm_vcpu_has_pmu(vcpu);
+	dfr0_pmu = id_reg_has_pmu(val, ID_DFR0_PERFMON_SHIFT, ID_DFR0_PERFMON_8_0);
+
+	/* Check if there is a conflict with a request via KVM_ARM_VCPU_INIT */
+	if (vcpu_pmu ^ dfr0_pmu)
+		return -EPERM;
+
+	return 0;
+}
+
 static void init_id_aa64pfr0_el1_info(struct id_reg_info *id_reg)
 {
 	u64 limit = id_reg->vcpu_limit_val;
@@ -656,8 +677,17 @@ static void init_id_aa64dfr0_el1_info(struct id_reg_info *id_reg)
 	id_reg->vcpu_limit_val = limit;
 }
 
+static void init_id_dfr0_el1_info(struct id_reg_info *id_reg)
+{
+	/* Limit guests to PMUv3 for ARMv8.4 */
+	id_reg->vcpu_limit_val =
+		cpuid_feature_cap_perfmon_field(id_reg->vcpu_limit_val,
+						ID_DFR0_PERFMON_SHIFT,
+						ID_DFR0_PERFMON_8_4);
+}
+
 static u64 vcpu_mask_id_aa64pfr0_el1(const struct kvm_vcpu *vcpu,
-					 const struct id_reg_info *idr)
+				     const struct id_reg_info *idr)
 {
 	return vcpu_has_sve(vcpu) ? 0 : ARM64_FEATURE_MASK(ID_AA64PFR0_SVE);
 }
@@ -680,6 +710,12 @@ static u64 vcpu_mask_id_aa64dfr0_el1(const struct kvm_vcpu *vcpu,
 	return kvm_vcpu_has_pmu(vcpu) ? 0 : ARM64_FEATURE_MASK(ID_AA64DFR0_PMUVER);
 }
 
+static u64 vcpu_mask_id_dfr0_el1(const struct kvm_vcpu *vcpu,
+				     const struct id_reg_info *idr)
+{
+	return kvm_vcpu_has_pmu(vcpu) ? 0 : ARM64_FEATURE_MASK(ID_DFR0_PERFMON);
+}
+
 static struct id_reg_info id_aa64pfr0_el1_info = {
 	.sys_reg = SYS_ID_AA64PFR0_EL1,
 	.ignore_mask = ARM64_FEATURE_MASK(ID_AA64PFR0_GIC),
@@ -731,6 +767,13 @@ static struct id_reg_info id_aa64dfr0_el1_info = {
 	.vcpu_mask = vcpu_mask_id_aa64dfr0_el1,
 };
 
+static struct id_reg_info id_dfr0_el1_info = {
+	.sys_reg = SYS_ID_DFR0_EL1,
+	.init = init_id_dfr0_el1_info,
+	.validate = validate_id_dfr0_el1,
+	.vcpu_mask = vcpu_mask_id_dfr0_el1,
+};
+
 /*
  * An ID register that needs special handling to control the value for the
  * guest must have its own id_reg_info in id_reg_info_table.
@@ -740,6 +783,7 @@ static struct id_reg_info id_aa64dfr0_el1_info = {
  */
 #define	GET_ID_REG_INFO(id)	(id_reg_info_table[IDREG_IDX(id)])
 static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {
+	[IDREG_IDX(SYS_ID_DFR0_EL1)] = &id_dfr0_el1_info,
 	[IDREG_IDX(SYS_ID_AA64PFR0_EL1)] = &id_aa64pfr0_el1_info,
 	[IDREG_IDX(SYS_ID_AA64PFR1_EL1)] = &id_aa64pfr1_el1_info,
 	[IDREG_IDX(SYS_ID_AA64DFR0_EL1)] = &id_aa64dfr0_el1_info,
@@ -1662,15 +1706,6 @@ static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
 		/* Clear fields for opt-in features that are not configured. */
 		val &= ~(id_reg->vcpu_mask(vcpu, id_reg));
 
-	switch (id) {
-	case SYS_ID_DFR0_EL1:
-		/* Limit guests to PMUv3 for ARMv8.4 */
-		val = cpuid_feature_cap_perfmon_field(val,
-						      ID_DFR0_PERFMON_SHIFT,
-						      kvm_vcpu_has_pmu(vcpu) ? ID_DFR0_PERFMON_8_4 : 0);
-		break;
-	}
-
 	return val;
 }
 
-- 
2.34.1.448.ga2b2bfdf31-goog


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 12/26] KVM: arm64: Make MVFR1_EL1 writable
  2022-01-06  4:26 ` Reiji Watanabe
  (?)
@ 2022-01-06  4:26   ` Reiji Watanabe
  -1 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

This patch adds id_reg_info for MVFR1_EL1 to make it writable
by userspace.

There are only a few valid combinations of values that can be set
for FPHP and SIMDHP fields according to Arm ARM.  Return an error
when userspace tries to set those fields to values that don't match
any of the valid combinations.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/kvm/sys_regs.c | 36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 1707c7832593..1c18a19c5785 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -617,6 +617,36 @@ static int validate_id_dfr0_el1(struct kvm_vcpu *vcpu,
 	return 0;
 }
 
+static int validate_mvfr1_el1(struct kvm_vcpu *vcpu,
+			      const struct id_reg_info *id_reg, u64 val)
+{
+	unsigned int fphp, simdhp;
+	struct fphp_simdhp {
+		unsigned int fphp;
+		unsigned int simdhp;
+	};
+	/* Permitted fphp/simdhp value combinations according to Arm ARM */
+	struct fphp_simdhp valid_fphp_simdhp[3] = {{0, 0}, {2, 1}, {3, 2}};
+	int i;
+	bool is_valid_fphp_simdhp = false;
+
+	fphp = cpuid_feature_extract_unsigned_field(val, MVFR1_FPHP_SHIFT);
+	simdhp = cpuid_feature_extract_unsigned_field(val, MVFR1_SIMDHP_SHIFT);
+
+	for (i = 0; i < ARRAY_SIZE(valid_fphp_simdhp); i++) {
+		if (valid_fphp_simdhp[i].fphp == fphp &&
+		    valid_fphp_simdhp[i].simdhp == simdhp) {
+			is_valid_fphp_simdhp = true;
+			break;
+		}
+	}
+
+	if (!is_valid_fphp_simdhp)
+		return -EINVAL;
+
+	return 0;
+}
+
 static void init_id_aa64pfr0_el1_info(struct id_reg_info *id_reg)
 {
 	u64 limit = id_reg->vcpu_limit_val;
@@ -774,6 +804,11 @@ static struct id_reg_info id_dfr0_el1_info = {
 	.vcpu_mask = vcpu_mask_id_dfr0_el1,
 };
 
+static struct id_reg_info mvfr1_el1_info = {
+	.sys_reg = SYS_MVFR1_EL1,
+	.validate = validate_mvfr1_el1,
+};
+
 /*
  * An ID register that needs special handling to control the value for the
  * guest must have its own id_reg_info in id_reg_info_table.
@@ -784,6 +819,7 @@ static struct id_reg_info id_dfr0_el1_info = {
 #define	GET_ID_REG_INFO(id)	(id_reg_info_table[IDREG_IDX(id)])
 static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {
 	[IDREG_IDX(SYS_ID_DFR0_EL1)] = &id_dfr0_el1_info,
+	[IDREG_IDX(SYS_MVFR1_EL1)] = &mvfr1_el1_info,
 	[IDREG_IDX(SYS_ID_AA64PFR0_EL1)] = &id_aa64pfr0_el1_info,
 	[IDREG_IDX(SYS_ID_AA64PFR1_EL1)] = &id_aa64pfr1_el1_info,
 	[IDREG_IDX(SYS_ID_AA64DFR0_EL1)] = &id_aa64dfr0_el1_info,
-- 
2.34.1.448.ga2b2bfdf31-goog


^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 12/26] KVM: arm64: Make MVFR1_EL1 writable
@ 2022-01-06  4:26   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, Will Deacon, Peter Shier, Paolo Bonzini, linux-arm-kernel

This patch adds id_reg_info for MVFR1_EL1 to make it writable
by userspace.

There are only a few valid combinations of values that can be set
for FPHP and SIMDHP fields according to Arm ARM.  Return an error
when userspace tries to set those fields to values that don't match
any of the valid combinations.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/kvm/sys_regs.c | 36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 1707c7832593..1c18a19c5785 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -617,6 +617,36 @@ static int validate_id_dfr0_el1(struct kvm_vcpu *vcpu,
 	return 0;
 }
 
+static int validate_mvfr1_el1(struct kvm_vcpu *vcpu,
+			      const struct id_reg_info *id_reg, u64 val)
+{
+	unsigned int fphp, simdhp;
+	struct fphp_simdhp {
+		unsigned int fphp;
+		unsigned int simdhp;
+	};
+	/* Permitted fphp/simdhp value combinations according to Arm ARM */
+	struct fphp_simdhp valid_fphp_simdhp[3] = {{0, 0}, {2, 1}, {3, 2}};
+	int i;
+	bool is_valid_fphp_simdhp = false;
+
+	fphp = cpuid_feature_extract_unsigned_field(val, MVFR1_FPHP_SHIFT);
+	simdhp = cpuid_feature_extract_unsigned_field(val, MVFR1_SIMDHP_SHIFT);
+
+	for (i = 0; i < ARRAY_SIZE(valid_fphp_simdhp); i++) {
+		if (valid_fphp_simdhp[i].fphp == fphp &&
+		    valid_fphp_simdhp[i].simdhp == simdhp) {
+			is_valid_fphp_simdhp = true;
+			break;
+		}
+	}
+
+	if (!is_valid_fphp_simdhp)
+		return -EINVAL;
+
+	return 0;
+}
+
 static void init_id_aa64pfr0_el1_info(struct id_reg_info *id_reg)
 {
 	u64 limit = id_reg->vcpu_limit_val;
@@ -774,6 +804,11 @@ static struct id_reg_info id_dfr0_el1_info = {
 	.vcpu_mask = vcpu_mask_id_dfr0_el1,
 };
 
+static struct id_reg_info mvfr1_el1_info = {
+	.sys_reg = SYS_MVFR1_EL1,
+	.validate = validate_mvfr1_el1,
+};
+
 /*
  * An ID register that needs special handling to control the value for the
  * guest must have its own id_reg_info in id_reg_info_table.
@@ -784,6 +819,7 @@ static struct id_reg_info id_dfr0_el1_info = {
 #define	GET_ID_REG_INFO(id)	(id_reg_info_table[IDREG_IDX(id)])
 static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {
 	[IDREG_IDX(SYS_ID_DFR0_EL1)] = &id_dfr0_el1_info,
+	[IDREG_IDX(SYS_MVFR1_EL1)] = &mvfr1_el1_info,
 	[IDREG_IDX(SYS_ID_AA64PFR0_EL1)] = &id_aa64pfr0_el1_info,
 	[IDREG_IDX(SYS_ID_AA64PFR1_EL1)] = &id_aa64pfr1_el1_info,
 	[IDREG_IDX(SYS_ID_AA64DFR0_EL1)] = &id_aa64dfr0_el1_info,
-- 
2.34.1.448.ga2b2bfdf31-goog

_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 12/26] KVM: arm64: Make MVFR1_EL1 writable
@ 2022-01-06  4:26   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

This patch adds id_reg_info for MVFR1_EL1 to make it writable
by userspace.

There are only a few valid combinations of values that can be set
for FPHP and SIMDHP fields according to Arm ARM.  Return an error
when userspace tries to set those fields to values that don't match
any of the valid combinations.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/kvm/sys_regs.c | 36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 1707c7832593..1c18a19c5785 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -617,6 +617,36 @@ static int validate_id_dfr0_el1(struct kvm_vcpu *vcpu,
 	return 0;
 }
 
+static int validate_mvfr1_el1(struct kvm_vcpu *vcpu,
+			      const struct id_reg_info *id_reg, u64 val)
+{
+	unsigned int fphp, simdhp;
+	struct fphp_simdhp {
+		unsigned int fphp;
+		unsigned int simdhp;
+	};
+	/* Permitted fphp/simdhp value combinations according to Arm ARM */
+	struct fphp_simdhp valid_fphp_simdhp[3] = {{0, 0}, {2, 1}, {3, 2}};
+	int i;
+	bool is_valid_fphp_simdhp = false;
+
+	fphp = cpuid_feature_extract_unsigned_field(val, MVFR1_FPHP_SHIFT);
+	simdhp = cpuid_feature_extract_unsigned_field(val, MVFR1_SIMDHP_SHIFT);
+
+	for (i = 0; i < ARRAY_SIZE(valid_fphp_simdhp); i++) {
+		if (valid_fphp_simdhp[i].fphp == fphp &&
+		    valid_fphp_simdhp[i].simdhp == simdhp) {
+			is_valid_fphp_simdhp = true;
+			break;
+		}
+	}
+
+	if (!is_valid_fphp_simdhp)
+		return -EINVAL;
+
+	return 0;
+}
+
 static void init_id_aa64pfr0_el1_info(struct id_reg_info *id_reg)
 {
 	u64 limit = id_reg->vcpu_limit_val;
@@ -774,6 +804,11 @@ static struct id_reg_info id_dfr0_el1_info = {
 	.vcpu_mask = vcpu_mask_id_dfr0_el1,
 };
 
+static struct id_reg_info mvfr1_el1_info = {
+	.sys_reg = SYS_MVFR1_EL1,
+	.validate = validate_mvfr1_el1,
+};
+
 /*
  * An ID register that needs special handling to control the value for the
  * guest must have its own id_reg_info in id_reg_info_table.
@@ -784,6 +819,7 @@ static struct id_reg_info id_dfr0_el1_info = {
 #define	GET_ID_REG_INFO(id)	(id_reg_info_table[IDREG_IDX(id)])
 static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {
 	[IDREG_IDX(SYS_ID_DFR0_EL1)] = &id_dfr0_el1_info,
+	[IDREG_IDX(SYS_MVFR1_EL1)] = &mvfr1_el1_info,
 	[IDREG_IDX(SYS_ID_AA64PFR0_EL1)] = &id_aa64pfr0_el1_info,
 	[IDREG_IDX(SYS_ID_AA64PFR1_EL1)] = &id_aa64pfr1_el1_info,
 	[IDREG_IDX(SYS_ID_AA64DFR0_EL1)] = &id_aa64dfr0_el1_info,
-- 
2.34.1.448.ga2b2bfdf31-goog


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 13/26] KVM: arm64: Make ID registers without id_reg_info writable
  2022-01-06  4:26 ` Reiji Watanabe
  (?)
@ 2022-01-06  4:26   ` Reiji Watanabe
  -1 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

Make ID registers that don't have id_reg_info writable.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/kvm/sys_regs.c | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 1c18a19c5785..ddbeefc3881c 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -1859,11 +1859,8 @@ static int __set_id_reg(struct kvm_vcpu *vcpu,
 		/* The value is same as the current value. Nothing to do. */
 		return 0;
 
-	/*
-	 * Don't allow to modify the register's value if the register is raz,
-	 * or the reg doesn't have the id_reg_info.
-	 */
-	if (raz || !GET_ID_REG_INFO(encoding))
+	/* Don't allow to modify the register's value if the register is raz. */
+	if (raz)
 		return -EINVAL;
 
 	/*
-- 
2.34.1.448.ga2b2bfdf31-goog


^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 13/26] KVM: arm64: Make ID registers without id_reg_info writable
@ 2022-01-06  4:26   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, Will Deacon, Peter Shier, Paolo Bonzini, linux-arm-kernel

Make ID registers that don't have id_reg_info writable.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/kvm/sys_regs.c | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 1c18a19c5785..ddbeefc3881c 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -1859,11 +1859,8 @@ static int __set_id_reg(struct kvm_vcpu *vcpu,
 		/* The value is same as the current value. Nothing to do. */
 		return 0;
 
-	/*
-	 * Don't allow to modify the register's value if the register is raz,
-	 * or the reg doesn't have the id_reg_info.
-	 */
-	if (raz || !GET_ID_REG_INFO(encoding))
+	/* Don't allow to modify the register's value if the register is raz. */
+	if (raz)
 		return -EINVAL;
 
 	/*
-- 
2.34.1.448.ga2b2bfdf31-goog

_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 13/26] KVM: arm64: Make ID registers without id_reg_info writable
@ 2022-01-06  4:26   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

Make ID registers that don't have id_reg_info writable.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/kvm/sys_regs.c | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 1c18a19c5785..ddbeefc3881c 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -1859,11 +1859,8 @@ static int __set_id_reg(struct kvm_vcpu *vcpu,
 		/* The value is same as the current value. Nothing to do. */
 		return 0;
 
-	/*
-	 * Don't allow to modify the register's value if the register is raz,
-	 * or the reg doesn't have the id_reg_info.
-	 */
-	if (raz || !GET_ID_REG_INFO(encoding))
+	/* Don't allow to modify the register's value if the register is raz. */
+	if (raz)
 		return -EINVAL;
 
 	/*
-- 
2.34.1.448.ga2b2bfdf31-goog


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 14/26] KVM: arm64: Add consistency checking for frac fields of ID registers
  2022-01-06  4:26 ` Reiji Watanabe
  (?)
@ 2022-01-06  4:26   ` Reiji Watanabe
  -1 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

Feature fractional field of an ID register cannot be simply validated
at KVM_SET_ONE_REG because its validity depends on its (main) feature
field value, which could be in a different ID register (and might be
set later).
Validate fractional fields at the first KVM_RUN instead.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/include/asm/kvm_host.h |   1 +
 arch/arm64/kvm/arm.c              |   3 +
 arch/arm64/kvm/sys_regs.c         | 116 +++++++++++++++++++++++++++++-
 3 files changed, 117 insertions(+), 3 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 4509f9e7472d..7b3f86bd6a6b 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -750,6 +750,7 @@ long kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
 
 void set_default_id_regs(struct kvm *kvm);
 int kvm_set_id_reg_feature(struct kvm *kvm, u32 id, u8 field_shift, u8 fval);
+int kvm_id_regs_consistency_check(const struct kvm_vcpu *vcpu);
 
 /* Guest/host FPSIMD coordination helpers */
 int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu);
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 5f497a0af254..16fc2ce32069 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -596,6 +596,9 @@ static int kvm_vcpu_first_run_init(struct kvm_vcpu *vcpu)
 	if (!kvm_arm_vcpu_is_finalized(vcpu))
 		return -EPERM;
 
+	if (!kvm_vm_is_protected(kvm) && kvm_id_regs_consistency_check(vcpu))
+		return -EPERM;
+
 	vcpu->arch.has_run_once = true;
 
 	kvm_arm_vcpu_init_debug(vcpu);
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index ddbeefc3881c..6adb7b04620c 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -756,9 +756,6 @@ static struct id_reg_info id_aa64pfr0_el1_info = {
 
 static struct id_reg_info id_aa64pfr1_el1_info = {
 	.sys_reg = SYS_ID_AA64PFR1_EL1,
-	.ignore_mask = ARM64_FEATURE_MASK(ID_AA64PFR1_RASFRAC) |
-		       ARM64_FEATURE_MASK(ID_AA64PFR1_MPAMFRAC) |
-		       ARM64_FEATURE_MASK(ID_AA64PFR1_CSV2FRAC),
 	.init = init_id_aa64pfr1_el1_info,
 	.validate = validate_id_aa64pfr1_el1,
 	.vcpu_mask = vcpu_mask_id_aa64pfr1_el1,
@@ -3434,10 +3431,109 @@ int kvm_arm_copy_sys_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
 	return write_demux_regids(uindices);
 }
 
+/* ID register's fractional field information with its feature field. */
+struct feature_frac {
+	u32	id;
+	u32	shift;
+	u32	frac_id;
+	u32	frac_shift;
+	u8	frac_ftr_check;
+};
+
+static struct feature_frac feature_frac_table[] = {
+	{
+		.frac_id = SYS_ID_AA64PFR1_EL1,
+		.frac_shift = ID_AA64PFR1_RASFRAC_SHIFT,
+		.id = SYS_ID_AA64PFR0_EL1,
+		.shift = ID_AA64PFR0_RAS_SHIFT,
+	},
+	{
+		.frac_id = SYS_ID_AA64PFR1_EL1,
+		.frac_shift = ID_AA64PFR1_MPAMFRAC_SHIFT,
+		.id = SYS_ID_AA64PFR0_EL1,
+		.shift = ID_AA64PFR0_MPAM_SHIFT,
+	},
+	{
+		.frac_id = SYS_ID_AA64PFR1_EL1,
+		.frac_shift = ID_AA64PFR1_CSV2FRAC_SHIFT,
+		.id = SYS_ID_AA64PFR0_EL1,
+		.shift = ID_AA64PFR0_CSV2_SHIFT,
+	},
+};
+
+/*
+ * Return non-zero if the feature/fractional fields pair are not
+ * supported. Return zero otherwise.
+ * This function validates only the fractional feature field,
+ * and relies on the fact the feature field is validated before
+ * through arm64_check_features.
+ */
+static int vcpu_id_reg_feature_frac_check(const struct kvm_vcpu *vcpu,
+					  const struct feature_frac *ftr_frac)
+{
+	const struct id_reg_info *id_reg;
+	u32 id;
+	u64 val, lim, mask;
+
+	/* Check if the feature field value is same as the limit */
+	id = ftr_frac->id;
+	id_reg = GET_ID_REG_INFO(id);
+
+	mask = (u64)ARM64_FEATURE_FIELD_MASK << ftr_frac->shift;
+	val = __read_id_reg(vcpu, id) & mask;
+	lim = id_reg ? id_reg->vcpu_limit_val : read_sanitised_ftr_reg(id);
+	lim &= mask;
+
+	if (val != lim)
+		/*
+		 * The feature level is lower than the limit.
+		 * Any fractional version should be fine.
+		 */
+		return 0;
+
+	/* Check the fractional feature field */
+	id = ftr_frac->frac_id;
+	id_reg = GET_ID_REG_INFO(id);
+
+	mask = (u64)ARM64_FEATURE_FIELD_MASK << ftr_frac->frac_shift;
+	val = __read_id_reg(vcpu, id) & mask;
+	lim = id_reg ? id_reg->vcpu_limit_val : read_sanitised_ftr_reg(id);
+	lim &= mask;
+
+	if (val == lim)
+		/*
+		 * Both the feature and fractional fields are the same
+		 * as limit.
+		 */
+		return 0;
+
+	return arm64_check_features(id, val, lim);
+}
+
+int kvm_id_regs_consistency_check(const struct kvm_vcpu *vcpu)
+{
+	int i, err;
+	const struct feature_frac *frac;
+
+	/*
+	 * Check ID registers' fractional fields, which aren't checked
+	 * at KVM_SET_ONE_REG.
+	 */
+	for (i = 0; i < ARRAY_SIZE(feature_frac_table); i++) {
+		frac = &feature_frac_table[i];
+		err = vcpu_id_reg_feature_frac_check(vcpu, frac);
+		if (err)
+			return err;
+	}
+	return 0;
+}
+
 static void id_reg_info_init_all(void)
 {
 	int i;
 	struct id_reg_info *id_reg;
+	struct feature_frac *frac;
+	u64 ftr_mask = ARM64_FEATURE_FIELD_MASK;
 
 	for (i = 0; i < ARRAY_SIZE(id_reg_info_table); i++) {
 		id_reg = (struct id_reg_info *)id_reg_info_table[i];
@@ -3446,6 +3542,20 @@ static void id_reg_info_init_all(void)
 
 		id_reg_info_init(id_reg);
 	}
+
+	/*
+	 * Update ignore_mask of ID registers based on fractional fields
+	 * information.  Any ID register that have fractional fields
+	 * is expected to have its own id_reg_info.
+	 */
+	for (i = 0; i < ARRAY_SIZE(feature_frac_table); i++) {
+		frac = &feature_frac_table[i];
+		id_reg = GET_ID_REG_INFO(frac->frac_id);
+		if (WARN_ON_ONCE(!id_reg))
+			continue;
+
+		id_reg->ignore_mask |= ftr_mask << frac->frac_shift;
+	}
 }
 
 void kvm_sys_reg_table_init(void)
-- 
2.34.1.448.ga2b2bfdf31-goog


^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 14/26] KVM: arm64: Add consistency checking for frac fields of ID registers
@ 2022-01-06  4:26   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, Will Deacon, Peter Shier, Paolo Bonzini, linux-arm-kernel

Feature fractional field of an ID register cannot be simply validated
at KVM_SET_ONE_REG because its validity depends on its (main) feature
field value, which could be in a different ID register (and might be
set later).
Validate fractional fields at the first KVM_RUN instead.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/include/asm/kvm_host.h |   1 +
 arch/arm64/kvm/arm.c              |   3 +
 arch/arm64/kvm/sys_regs.c         | 116 +++++++++++++++++++++++++++++-
 3 files changed, 117 insertions(+), 3 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 4509f9e7472d..7b3f86bd6a6b 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -750,6 +750,7 @@ long kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
 
 void set_default_id_regs(struct kvm *kvm);
 int kvm_set_id_reg_feature(struct kvm *kvm, u32 id, u8 field_shift, u8 fval);
+int kvm_id_regs_consistency_check(const struct kvm_vcpu *vcpu);
 
 /* Guest/host FPSIMD coordination helpers */
 int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu);
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 5f497a0af254..16fc2ce32069 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -596,6 +596,9 @@ static int kvm_vcpu_first_run_init(struct kvm_vcpu *vcpu)
 	if (!kvm_arm_vcpu_is_finalized(vcpu))
 		return -EPERM;
 
+	if (!kvm_vm_is_protected(kvm) && kvm_id_regs_consistency_check(vcpu))
+		return -EPERM;
+
 	vcpu->arch.has_run_once = true;
 
 	kvm_arm_vcpu_init_debug(vcpu);
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index ddbeefc3881c..6adb7b04620c 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -756,9 +756,6 @@ static struct id_reg_info id_aa64pfr0_el1_info = {
 
 static struct id_reg_info id_aa64pfr1_el1_info = {
 	.sys_reg = SYS_ID_AA64PFR1_EL1,
-	.ignore_mask = ARM64_FEATURE_MASK(ID_AA64PFR1_RASFRAC) |
-		       ARM64_FEATURE_MASK(ID_AA64PFR1_MPAMFRAC) |
-		       ARM64_FEATURE_MASK(ID_AA64PFR1_CSV2FRAC),
 	.init = init_id_aa64pfr1_el1_info,
 	.validate = validate_id_aa64pfr1_el1,
 	.vcpu_mask = vcpu_mask_id_aa64pfr1_el1,
@@ -3434,10 +3431,109 @@ int kvm_arm_copy_sys_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
 	return write_demux_regids(uindices);
 }
 
+/* ID register's fractional field information with its feature field. */
+struct feature_frac {
+	u32	id;
+	u32	shift;
+	u32	frac_id;
+	u32	frac_shift;
+	u8	frac_ftr_check;
+};
+
+static struct feature_frac feature_frac_table[] = {
+	{
+		.frac_id = SYS_ID_AA64PFR1_EL1,
+		.frac_shift = ID_AA64PFR1_RASFRAC_SHIFT,
+		.id = SYS_ID_AA64PFR0_EL1,
+		.shift = ID_AA64PFR0_RAS_SHIFT,
+	},
+	{
+		.frac_id = SYS_ID_AA64PFR1_EL1,
+		.frac_shift = ID_AA64PFR1_MPAMFRAC_SHIFT,
+		.id = SYS_ID_AA64PFR0_EL1,
+		.shift = ID_AA64PFR0_MPAM_SHIFT,
+	},
+	{
+		.frac_id = SYS_ID_AA64PFR1_EL1,
+		.frac_shift = ID_AA64PFR1_CSV2FRAC_SHIFT,
+		.id = SYS_ID_AA64PFR0_EL1,
+		.shift = ID_AA64PFR0_CSV2_SHIFT,
+	},
+};
+
+/*
+ * Return non-zero if the feature/fractional fields pair are not
+ * supported. Return zero otherwise.
+ * This function validates only the fractional feature field,
+ * and relies on the fact the feature field is validated before
+ * through arm64_check_features.
+ */
+static int vcpu_id_reg_feature_frac_check(const struct kvm_vcpu *vcpu,
+					  const struct feature_frac *ftr_frac)
+{
+	const struct id_reg_info *id_reg;
+	u32 id;
+	u64 val, lim, mask;
+
+	/* Check if the feature field value is same as the limit */
+	id = ftr_frac->id;
+	id_reg = GET_ID_REG_INFO(id);
+
+	mask = (u64)ARM64_FEATURE_FIELD_MASK << ftr_frac->shift;
+	val = __read_id_reg(vcpu, id) & mask;
+	lim = id_reg ? id_reg->vcpu_limit_val : read_sanitised_ftr_reg(id);
+	lim &= mask;
+
+	if (val != lim)
+		/*
+		 * The feature level is lower than the limit.
+		 * Any fractional version should be fine.
+		 */
+		return 0;
+
+	/* Check the fractional feature field */
+	id = ftr_frac->frac_id;
+	id_reg = GET_ID_REG_INFO(id);
+
+	mask = (u64)ARM64_FEATURE_FIELD_MASK << ftr_frac->frac_shift;
+	val = __read_id_reg(vcpu, id) & mask;
+	lim = id_reg ? id_reg->vcpu_limit_val : read_sanitised_ftr_reg(id);
+	lim &= mask;
+
+	if (val == lim)
+		/*
+		 * Both the feature and fractional fields are the same
+		 * as limit.
+		 */
+		return 0;
+
+	return arm64_check_features(id, val, lim);
+}
+
+int kvm_id_regs_consistency_check(const struct kvm_vcpu *vcpu)
+{
+	int i, err;
+	const struct feature_frac *frac;
+
+	/*
+	 * Check ID registers' fractional fields, which aren't checked
+	 * at KVM_SET_ONE_REG.
+	 */
+	for (i = 0; i < ARRAY_SIZE(feature_frac_table); i++) {
+		frac = &feature_frac_table[i];
+		err = vcpu_id_reg_feature_frac_check(vcpu, frac);
+		if (err)
+			return err;
+	}
+	return 0;
+}
+
 static void id_reg_info_init_all(void)
 {
 	int i;
 	struct id_reg_info *id_reg;
+	struct feature_frac *frac;
+	u64 ftr_mask = ARM64_FEATURE_FIELD_MASK;
 
 	for (i = 0; i < ARRAY_SIZE(id_reg_info_table); i++) {
 		id_reg = (struct id_reg_info *)id_reg_info_table[i];
@@ -3446,6 +3542,20 @@ static void id_reg_info_init_all(void)
 
 		id_reg_info_init(id_reg);
 	}
+
+	/*
+	 * Update ignore_mask of ID registers based on fractional fields
+	 * information.  Any ID register that have fractional fields
+	 * is expected to have its own id_reg_info.
+	 */
+	for (i = 0; i < ARRAY_SIZE(feature_frac_table); i++) {
+		frac = &feature_frac_table[i];
+		id_reg = GET_ID_REG_INFO(frac->frac_id);
+		if (WARN_ON_ONCE(!id_reg))
+			continue;
+
+		id_reg->ignore_mask |= ftr_mask << frac->frac_shift;
+	}
 }
 
 void kvm_sys_reg_table_init(void)
-- 
2.34.1.448.ga2b2bfdf31-goog

_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 14/26] KVM: arm64: Add consistency checking for frac fields of ID registers
@ 2022-01-06  4:26   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

Feature fractional field of an ID register cannot be simply validated
at KVM_SET_ONE_REG because its validity depends on its (main) feature
field value, which could be in a different ID register (and might be
set later).
Validate fractional fields at the first KVM_RUN instead.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/include/asm/kvm_host.h |   1 +
 arch/arm64/kvm/arm.c              |   3 +
 arch/arm64/kvm/sys_regs.c         | 116 +++++++++++++++++++++++++++++-
 3 files changed, 117 insertions(+), 3 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 4509f9e7472d..7b3f86bd6a6b 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -750,6 +750,7 @@ long kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
 
 void set_default_id_regs(struct kvm *kvm);
 int kvm_set_id_reg_feature(struct kvm *kvm, u32 id, u8 field_shift, u8 fval);
+int kvm_id_regs_consistency_check(const struct kvm_vcpu *vcpu);
 
 /* Guest/host FPSIMD coordination helpers */
 int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu);
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 5f497a0af254..16fc2ce32069 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -596,6 +596,9 @@ static int kvm_vcpu_first_run_init(struct kvm_vcpu *vcpu)
 	if (!kvm_arm_vcpu_is_finalized(vcpu))
 		return -EPERM;
 
+	if (!kvm_vm_is_protected(kvm) && kvm_id_regs_consistency_check(vcpu))
+		return -EPERM;
+
 	vcpu->arch.has_run_once = true;
 
 	kvm_arm_vcpu_init_debug(vcpu);
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index ddbeefc3881c..6adb7b04620c 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -756,9 +756,6 @@ static struct id_reg_info id_aa64pfr0_el1_info = {
 
 static struct id_reg_info id_aa64pfr1_el1_info = {
 	.sys_reg = SYS_ID_AA64PFR1_EL1,
-	.ignore_mask = ARM64_FEATURE_MASK(ID_AA64PFR1_RASFRAC) |
-		       ARM64_FEATURE_MASK(ID_AA64PFR1_MPAMFRAC) |
-		       ARM64_FEATURE_MASK(ID_AA64PFR1_CSV2FRAC),
 	.init = init_id_aa64pfr1_el1_info,
 	.validate = validate_id_aa64pfr1_el1,
 	.vcpu_mask = vcpu_mask_id_aa64pfr1_el1,
@@ -3434,10 +3431,109 @@ int kvm_arm_copy_sys_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
 	return write_demux_regids(uindices);
 }
 
+/* ID register's fractional field information with its feature field. */
+struct feature_frac {
+	u32	id;
+	u32	shift;
+	u32	frac_id;
+	u32	frac_shift;
+	u8	frac_ftr_check;
+};
+
+static struct feature_frac feature_frac_table[] = {
+	{
+		.frac_id = SYS_ID_AA64PFR1_EL1,
+		.frac_shift = ID_AA64PFR1_RASFRAC_SHIFT,
+		.id = SYS_ID_AA64PFR0_EL1,
+		.shift = ID_AA64PFR0_RAS_SHIFT,
+	},
+	{
+		.frac_id = SYS_ID_AA64PFR1_EL1,
+		.frac_shift = ID_AA64PFR1_MPAMFRAC_SHIFT,
+		.id = SYS_ID_AA64PFR0_EL1,
+		.shift = ID_AA64PFR0_MPAM_SHIFT,
+	},
+	{
+		.frac_id = SYS_ID_AA64PFR1_EL1,
+		.frac_shift = ID_AA64PFR1_CSV2FRAC_SHIFT,
+		.id = SYS_ID_AA64PFR0_EL1,
+		.shift = ID_AA64PFR0_CSV2_SHIFT,
+	},
+};
+
+/*
+ * Return non-zero if the feature/fractional fields pair are not
+ * supported. Return zero otherwise.
+ * This function validates only the fractional feature field,
+ * and relies on the fact the feature field is validated before
+ * through arm64_check_features.
+ */
+static int vcpu_id_reg_feature_frac_check(const struct kvm_vcpu *vcpu,
+					  const struct feature_frac *ftr_frac)
+{
+	const struct id_reg_info *id_reg;
+	u32 id;
+	u64 val, lim, mask;
+
+	/* Check if the feature field value is same as the limit */
+	id = ftr_frac->id;
+	id_reg = GET_ID_REG_INFO(id);
+
+	mask = (u64)ARM64_FEATURE_FIELD_MASK << ftr_frac->shift;
+	val = __read_id_reg(vcpu, id) & mask;
+	lim = id_reg ? id_reg->vcpu_limit_val : read_sanitised_ftr_reg(id);
+	lim &= mask;
+
+	if (val != lim)
+		/*
+		 * The feature level is lower than the limit.
+		 * Any fractional version should be fine.
+		 */
+		return 0;
+
+	/* Check the fractional feature field */
+	id = ftr_frac->frac_id;
+	id_reg = GET_ID_REG_INFO(id);
+
+	mask = (u64)ARM64_FEATURE_FIELD_MASK << ftr_frac->frac_shift;
+	val = __read_id_reg(vcpu, id) & mask;
+	lim = id_reg ? id_reg->vcpu_limit_val : read_sanitised_ftr_reg(id);
+	lim &= mask;
+
+	if (val == lim)
+		/*
+		 * Both the feature and fractional fields are the same
+		 * as limit.
+		 */
+		return 0;
+
+	return arm64_check_features(id, val, lim);
+}
+
+int kvm_id_regs_consistency_check(const struct kvm_vcpu *vcpu)
+{
+	int i, err;
+	const struct feature_frac *frac;
+
+	/*
+	 * Check ID registers' fractional fields, which aren't checked
+	 * at KVM_SET_ONE_REG.
+	 */
+	for (i = 0; i < ARRAY_SIZE(feature_frac_table); i++) {
+		frac = &feature_frac_table[i];
+		err = vcpu_id_reg_feature_frac_check(vcpu, frac);
+		if (err)
+			return err;
+	}
+	return 0;
+}
+
 static void id_reg_info_init_all(void)
 {
 	int i;
 	struct id_reg_info *id_reg;
+	struct feature_frac *frac;
+	u64 ftr_mask = ARM64_FEATURE_FIELD_MASK;
 
 	for (i = 0; i < ARRAY_SIZE(id_reg_info_table); i++) {
 		id_reg = (struct id_reg_info *)id_reg_info_table[i];
@@ -3446,6 +3542,20 @@ static void id_reg_info_init_all(void)
 
 		id_reg_info_init(id_reg);
 	}
+
+	/*
+	 * Update ignore_mask of ID registers based on fractional fields
+	 * information.  Any ID register that have fractional fields
+	 * is expected to have its own id_reg_info.
+	 */
+	for (i = 0; i < ARRAY_SIZE(feature_frac_table); i++) {
+		frac = &feature_frac_table[i];
+		id_reg = GET_ID_REG_INFO(frac->frac_id);
+		if (WARN_ON_ONCE(!id_reg))
+			continue;
+
+		id_reg->ignore_mask |= ftr_mask << frac->frac_shift;
+	}
 }
 
 void kvm_sys_reg_table_init(void)
-- 
2.34.1.448.ga2b2bfdf31-goog


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 15/26] KVM: arm64: Introduce KVM_CAP_ARM_ID_REG_CONFIGURABLE capability
  2022-01-06  4:26 ` Reiji Watanabe
  (?)
@ 2022-01-06  4:26   ` Reiji Watanabe
  -1 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

Introduce a new capability KVM_CAP_ARM_ID_REG_CONFIGURABLE to indicate
that ID registers are writable by userspace.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 Documentation/virt/kvm/api.rst | 12 ++++++++++++
 arch/arm64/kvm/arm.c           |  1 +
 include/uapi/linux/kvm.h       |  1 +
 3 files changed, 14 insertions(+)

diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
index aeeb071c7688..86099a9b0bae 100644
--- a/Documentation/virt/kvm/api.rst
+++ b/Documentation/virt/kvm/api.rst
@@ -2574,6 +2574,10 @@ EINVAL.
 After the vcpu's SVE configuration is finalized, further attempts to
 write this register will fail with EPERM.
 
+The arm64 ID registers (where Op0=3, Op1=0, CRn=0, 0<=CRm<8, 0<=Op2<8)
+are allowed to set by userspace if KVM_CAP_ARM_ID_REG_CONFIGURABLE is
+available.  They become immutable after calling KVM_RUN on any of the
+vcpus in the guest (modifying values of those registers will fail).
 
 MIPS registers are mapped using the lower 32 bits.  The upper 16 of that is
 the register group type:
@@ -7484,3 +7488,11 @@ The argument to KVM_ENABLE_CAP is also a bitmask, and must be a subset
 of the result of KVM_CHECK_EXTENSION.  KVM will forward to userspace
 the hypercalls whose corresponding bit is in the argument, and return
 ENOSYS for the others.
+
+8.35 KVM_CAP_ARM_ID_REG_CONFIGURABLE
+------------------------------------
+
+:Architectures: arm64
+
+This capability indicates that userspace can modify the ID registers
+via KVM_SET_ONE_REG ioctl.
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 16fc2ce32069..876f2777acf2 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -216,6 +216,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
 	case KVM_CAP_SET_GUEST_DEBUG:
 	case KVM_CAP_VCPU_ATTRIBUTES:
 	case KVM_CAP_PTP_KVM:
+	case KVM_CAP_ARM_ID_REG_CONFIGURABLE:
 		r = 1;
 		break;
 	case KVM_CAP_SET_GUEST_DEBUG2:
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 1daa45268de2..9697c06a7f5b 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -1131,6 +1131,7 @@ struct kvm_ppc_resize_hpt {
 #define KVM_CAP_EXIT_ON_EMULATION_FAILURE 204
 #define KVM_CAP_ARM_MTE 205
 #define KVM_CAP_VM_MOVE_ENC_CONTEXT_FROM 206
+#define KVM_CAP_ARM_ID_REG_CONFIGURABLE 207
 
 #ifdef KVM_CAP_IRQ_ROUTING
 
-- 
2.34.1.448.ga2b2bfdf31-goog


^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 15/26] KVM: arm64: Introduce KVM_CAP_ARM_ID_REG_CONFIGURABLE capability
@ 2022-01-06  4:26   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, Will Deacon, Peter Shier, Paolo Bonzini, linux-arm-kernel

Introduce a new capability KVM_CAP_ARM_ID_REG_CONFIGURABLE to indicate
that ID registers are writable by userspace.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 Documentation/virt/kvm/api.rst | 12 ++++++++++++
 arch/arm64/kvm/arm.c           |  1 +
 include/uapi/linux/kvm.h       |  1 +
 3 files changed, 14 insertions(+)

diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
index aeeb071c7688..86099a9b0bae 100644
--- a/Documentation/virt/kvm/api.rst
+++ b/Documentation/virt/kvm/api.rst
@@ -2574,6 +2574,10 @@ EINVAL.
 After the vcpu's SVE configuration is finalized, further attempts to
 write this register will fail with EPERM.
 
+The arm64 ID registers (where Op0=3, Op1=0, CRn=0, 0<=CRm<8, 0<=Op2<8)
+are allowed to set by userspace if KVM_CAP_ARM_ID_REG_CONFIGURABLE is
+available.  They become immutable after calling KVM_RUN on any of the
+vcpus in the guest (modifying values of those registers will fail).
 
 MIPS registers are mapped using the lower 32 bits.  The upper 16 of that is
 the register group type:
@@ -7484,3 +7488,11 @@ The argument to KVM_ENABLE_CAP is also a bitmask, and must be a subset
 of the result of KVM_CHECK_EXTENSION.  KVM will forward to userspace
 the hypercalls whose corresponding bit is in the argument, and return
 ENOSYS for the others.
+
+8.35 KVM_CAP_ARM_ID_REG_CONFIGURABLE
+------------------------------------
+
+:Architectures: arm64
+
+This capability indicates that userspace can modify the ID registers
+via KVM_SET_ONE_REG ioctl.
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 16fc2ce32069..876f2777acf2 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -216,6 +216,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
 	case KVM_CAP_SET_GUEST_DEBUG:
 	case KVM_CAP_VCPU_ATTRIBUTES:
 	case KVM_CAP_PTP_KVM:
+	case KVM_CAP_ARM_ID_REG_CONFIGURABLE:
 		r = 1;
 		break;
 	case KVM_CAP_SET_GUEST_DEBUG2:
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 1daa45268de2..9697c06a7f5b 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -1131,6 +1131,7 @@ struct kvm_ppc_resize_hpt {
 #define KVM_CAP_EXIT_ON_EMULATION_FAILURE 204
 #define KVM_CAP_ARM_MTE 205
 #define KVM_CAP_VM_MOVE_ENC_CONTEXT_FROM 206
+#define KVM_CAP_ARM_ID_REG_CONFIGURABLE 207
 
 #ifdef KVM_CAP_IRQ_ROUTING
 
-- 
2.34.1.448.ga2b2bfdf31-goog

_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 15/26] KVM: arm64: Introduce KVM_CAP_ARM_ID_REG_CONFIGURABLE capability
@ 2022-01-06  4:26   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

Introduce a new capability KVM_CAP_ARM_ID_REG_CONFIGURABLE to indicate
that ID registers are writable by userspace.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 Documentation/virt/kvm/api.rst | 12 ++++++++++++
 arch/arm64/kvm/arm.c           |  1 +
 include/uapi/linux/kvm.h       |  1 +
 3 files changed, 14 insertions(+)

diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
index aeeb071c7688..86099a9b0bae 100644
--- a/Documentation/virt/kvm/api.rst
+++ b/Documentation/virt/kvm/api.rst
@@ -2574,6 +2574,10 @@ EINVAL.
 After the vcpu's SVE configuration is finalized, further attempts to
 write this register will fail with EPERM.
 
+The arm64 ID registers (where Op0=3, Op1=0, CRn=0, 0<=CRm<8, 0<=Op2<8)
+are allowed to set by userspace if KVM_CAP_ARM_ID_REG_CONFIGURABLE is
+available.  They become immutable after calling KVM_RUN on any of the
+vcpus in the guest (modifying values of those registers will fail).
 
 MIPS registers are mapped using the lower 32 bits.  The upper 16 of that is
 the register group type:
@@ -7484,3 +7488,11 @@ The argument to KVM_ENABLE_CAP is also a bitmask, and must be a subset
 of the result of KVM_CHECK_EXTENSION.  KVM will forward to userspace
 the hypercalls whose corresponding bit is in the argument, and return
 ENOSYS for the others.
+
+8.35 KVM_CAP_ARM_ID_REG_CONFIGURABLE
+------------------------------------
+
+:Architectures: arm64
+
+This capability indicates that userspace can modify the ID registers
+via KVM_SET_ONE_REG ioctl.
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 16fc2ce32069..876f2777acf2 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -216,6 +216,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
 	case KVM_CAP_SET_GUEST_DEBUG:
 	case KVM_CAP_VCPU_ATTRIBUTES:
 	case KVM_CAP_PTP_KVM:
+	case KVM_CAP_ARM_ID_REG_CONFIGURABLE:
 		r = 1;
 		break;
 	case KVM_CAP_SET_GUEST_DEBUG2:
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 1daa45268de2..9697c06a7f5b 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -1131,6 +1131,7 @@ struct kvm_ppc_resize_hpt {
 #define KVM_CAP_EXIT_ON_EMULATION_FAILURE 204
 #define KVM_CAP_ARM_MTE 205
 #define KVM_CAP_VM_MOVE_ENC_CONTEXT_FROM 206
+#define KVM_CAP_ARM_ID_REG_CONFIGURABLE 207
 
 #ifdef KVM_CAP_IRQ_ROUTING
 
-- 
2.34.1.448.ga2b2bfdf31-goog


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 16/26] KVM: arm64: Add kunit test for ID register validation
  2022-01-06  4:26 ` Reiji Watanabe
  (?)
@ 2022-01-06  4:26   ` Reiji Watanabe
  -1 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

Add kunit tests for functions that are used for validation of ID
registers, CONFIG_KVM_KUNIT_TEST option to enable the tests, and
.kunitconfig to run the kunit tests.

One line change below is needed in the default arm64.py to fully
run all of those kunit tests.
-----------------------------------------------------------------------
$ diff tools/testing/kunit/qemu_configs/arm64.py arm64_kvm_min.py
12c12
< 			   extra_qemu_params=['-machine virt', '-cpu cortex-a57'])
---
> 			   extra_qemu_params=['-M virt,virtualization=on,mte=on', '-cpu max,sve=on'])
-----------------------------------------------------------------------

The outputs from the tests are:
-----------------------------------------------------------------------
$ tools/testing/kunit/kunit.py run --timeout=60 --jobs=`nproc --all` \
          --arch=arm64 --cross_compile=aarch64-linux-gnu- \
          --qemu_config arm64_kvm_min.py \
          --kunitconfig=arch/arm64/kvm/.kunitconfig
[12:14:24] Configuring KUnit Kernel ...
[12:14:24] Building KUnit Kernel ...
Populating config with:
$ make ARCH=arm64 olddefconfig CROSS_COMPILE=aarch64-linux-gnu- O=.kunit
Building with:
$ make ARCH=arm64 --jobs=96 CROSS_COMPILE=aarch64-linux-gnu- O=.kunit
[12:14:31] Starting KUnit Kernel (1/1)...
[12:14:31] ============================================================
Running tests with:
$ qemu-system-aarch64 -nodefaults -m 1024 -kernel .kunit/arch/arm64/boot/Image.gz -append 'mem=1G console=tty kunit_shutdown=halt console=ttyAMA0 kunit_shutdown=reboot' -no-reboot -nographic -serial stdio -M virt,virtualization=on,mte=on -cpu max,sve=on
[12:14:32] ========== kvm-sys-regs-test-suite (13 subtests) ===========
[12:14:32] [PASSED] vcpu_id_reg_feature_frac_check_test
[12:14:32] [PASSED] validate_id_aa64mmfr0_tgran2_test
[12:14:32] [PASSED] validate_id_aa64mmfr0_tgran2_test
[12:14:32] [PASSED] validate_id_aa64mmfr0_tgran2_test
[12:14:32] [PASSED] validate_id_aa64pfr0_el1_test
[12:14:32] [PASSED] validate_id_aa64pfr1_el1_test
[12:14:32] [PASSED] validate_id_aa64isar0_el1_test
[12:14:32] [PASSED] validate_id_aa64isar1_el1_test
[12:14:32] [PASSED] validate_id_aa64mmfr0_el1_test
[12:14:32] [PASSED] validate_id_aa64dfr0_el1_test
[12:14:32] [PASSED] validate_id_dfr0_el1_test
[12:14:32] [PASSED] validate_mvfr1_el1_test
[12:14:32] [PASSED] validate_id_reg_test
[12:14:32] ============= [PASSED] kvm-sys-regs-test-suite =============
[12:14:32] ============================================================
[12:14:32] Testing complete. Passed: 13, Failed: 0, Crashed: 0, Skipped: 0, Errors: 0
[12:14:32] Elapsed time: 8.223s total, 0.002s configuring, 6.655s building, 1.565s running
-----------------------------------------------------------------------

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/kvm/.kunitconfig    |   4 +
 arch/arm64/kvm/Kconfig         |  11 +
 arch/arm64/kvm/sys_regs.c      |   4 +
 arch/arm64/kvm/sys_regs_test.c | 985 +++++++++++++++++++++++++++++++++
 4 files changed, 1004 insertions(+)
 create mode 100644 arch/arm64/kvm/.kunitconfig
 create mode 100644 arch/arm64/kvm/sys_regs_test.c

diff --git a/arch/arm64/kvm/.kunitconfig b/arch/arm64/kvm/.kunitconfig
new file mode 100644
index 000000000000..c564c98fc319
--- /dev/null
+++ b/arch/arm64/kvm/.kunitconfig
@@ -0,0 +1,4 @@
+CONFIG_KUNIT=y
+CONFIG_VIRTUALIZATION=y
+CONFIG_KVM=y
+CONFIG_KVM_KUNIT_TEST=y
diff --git a/arch/arm64/kvm/Kconfig b/arch/arm64/kvm/Kconfig
index 8ffcbe29395e..48fbdd17b2fd 100644
--- a/arch/arm64/kvm/Kconfig
+++ b/arch/arm64/kvm/Kconfig
@@ -54,4 +54,15 @@ config NVHE_EL2_DEBUG
 
 	  If unsure, say N.
 
+config KVM_KUNIT_TEST
+	bool "KUnit tests for KVM on ARM64 processors" if !KUNIT_ALL_TESTS
+	depends on KVM && KUNIT
+	default KUNIT_ALL_TESTS
+	help
+	  Say Y here to enable KUnit tests for the KVM on ARM64.
+	  Only useful for KVM/ARM development and are not for inclusion into
+	  a production build.
+
+	  If unsure, say N.
+
 endif # VIRTUALIZATION
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 6adb7b04620c..d5bd04c68cd4 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -3636,3 +3636,7 @@ int kvm_set_id_reg_feature(struct kvm *kvm, u32 id, u8 field_shift, u8 fval)
 
 	return __modify_kvm_id_reg(kvm, id, val, preserve_mask);
 }
+
+#if IS_ENABLED(CONFIG_KVM_KUNIT_TEST)
+#include "sys_regs_test.c"
+#endif
diff --git a/arch/arm64/kvm/sys_regs_test.c b/arch/arm64/kvm/sys_regs_test.c
new file mode 100644
index 000000000000..5b1b95bd9ec4
--- /dev/null
+++ b/arch/arm64/kvm/sys_regs_test.c
@@ -0,0 +1,985 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * KUnit tests for arch/arm64/kvm/sys_regs.c.
+ */
+
+#include <linux/module.h>
+#include <kunit/test.h>
+#include <kunit/test.h>
+#include <linux/kvm_host.h>
+#include <asm/cpufeature.h>
+#include "asm/sysreg.h"
+
+/*
+ * Create a vcpu with the minimum fields required for testing in this file
+ * including the struct kvm.  Any resources that are allocated by this
+ * function must be allocated by kunit_* so that we don't need to explicitly
+ * free them.
+ */
+static struct kvm_vcpu *test_kvm_vcpu_init(struct kunit *test)
+{
+	struct kvm_vcpu *vcpu;
+	struct kvm *kvm;
+
+	kvm = kunit_kzalloc(test, sizeof(struct kvm), GFP_KERNEL);
+	if (!kvm)
+		return NULL;
+
+	vcpu = kunit_kzalloc(test, sizeof(struct kvm_vcpu), GFP_KERNEL);
+	if (!vcpu) {
+		kunit_kfree(test, kvm);
+		return NULL;
+	}
+
+	vcpu->cpu = -1;
+	vcpu->kvm = kvm;
+	vcpu->vcpu_id = 0;
+
+	return vcpu;
+}
+
+static void test_kvm_vcpu_fini(struct kunit *test, struct kvm_vcpu *vcpu)
+{
+	if (vcpu->kvm)
+		kunit_kfree(test, vcpu->kvm);
+
+	kunit_kfree(test, vcpu);
+}
+
+/* Test parameter information to test arm64_check_features */
+struct check_features_test {
+	u64	check_types;
+	u64	value;
+	u64	limit;
+	int	expected;
+};
+
+
+/* Used to define test parameters of vcpu_id_reg_feature_frac_check_test() */
+struct feat_info {
+	u32	id;
+	u32	shift;
+	u32	value;
+	u32	limit;
+};
+
+struct frac_check_test {
+	struct feat_info feat;
+	struct feat_info frac_feat;
+	int ret;
+};
+
+#define	FRAC_FEAT(id, shift, value, limit)	{id, shift, value, limit}
+
+/* Tests parameters of vcpu_id_reg_feature_frac_check_test() */
+struct frac_check_test frac_params[] = {
+	{
+		/*
+		 * The feature value is smaller than its limit.
+		 * Expect no error regardless of the frac value.
+		 */
+		FRAC_FEAT(SYS_ID_AA64PFR0_EL1, ID_AA64PFR0_RAS_SHIFT, 1, 2),
+		FRAC_FEAT(SYS_ID_AA64PFR1_EL1, ID_AA64PFR1_RASFRAC_SHIFT, 1, 1),
+		0,
+	},
+	{
+		/*
+		 * The feature value is smaller than its limit.
+		 * Expect no error regardless of the frac value.
+		 */
+		FRAC_FEAT(SYS_ID_AA64PFR0_EL1, ID_AA64PFR0_RAS_SHIFT, 1, 2),
+		FRAC_FEAT(SYS_ID_AA64PFR1_EL1, ID_AA64PFR1_RASFRAC_SHIFT, 1, 2),
+		0,
+	},
+	{
+		/*
+		 * The feature value is smaller than its limit.
+		 * Expect no error regardless of the frac value.
+		 */
+		FRAC_FEAT(SYS_ID_AA64PFR0_EL1, ID_AA64PFR0_RAS_SHIFT, 1, 2),
+		FRAC_FEAT(SYS_ID_AA64PFR1_EL1, ID_AA64PFR1_RASFRAC_SHIFT, 2, 1),
+		0,
+	},
+	{
+		/*
+		 * Both the feature and frac values are same as their limits.
+		 * Expect no error.
+		 */
+		FRAC_FEAT(SYS_ID_AA64PFR0_EL1, ID_AA64PFR0_RAS_SHIFT, 1, 1),
+		FRAC_FEAT(SYS_ID_AA64PFR1_EL1, ID_AA64PFR1_RASFRAC_SHIFT, 1, 1),
+		0,
+	},
+	{
+		/*
+		 * The feature value is same as its limit, and the frac value
+		 * is smaller than its limit. Expect no error.
+		 */
+		FRAC_FEAT(SYS_ID_AA64PFR0_EL1, ID_AA64PFR0_RAS_SHIFT, 1, 1),
+		FRAC_FEAT(SYS_ID_AA64PFR1_EL1, ID_AA64PFR1_RASFRAC_SHIFT, 1, 2),
+		0,
+	},
+	{
+		/*
+		 * The feature value is same as its limit, and the frac value
+		 * is larger than its limit. Expect an error.
+		 */
+		FRAC_FEAT(SYS_ID_AA64PFR0_EL1, ID_AA64PFR0_RAS_SHIFT, 1, 1),
+		FRAC_FEAT(SYS_ID_AA64PFR1_EL1, ID_AA64PFR1_RASFRAC_SHIFT, 2, 1),
+		-E2BIG,
+	},
+
+};
+
+static void frac_case_to_desc(struct frac_check_test *t, char *desc)
+{
+	struct feat_info *feat = &t->feat;
+	struct feat_info *frac = &t->frac_feat;
+
+	snprintf(desc, KUNIT_PARAM_DESC_SIZE,
+		 "feat - shift:%d, val:%d, lim:%d, frac - shift:%d, val:%d, lim:%d\n",
+		 feat->shift, feat->value, feat->limit,
+		 frac->shift, frac->value, frac->limit);
+}
+
+KUNIT_ARRAY_PARAM(frac, frac_params, frac_case_to_desc);
+
+/* Tests for vcpu_id_reg_feature_frac_check(). */
+static void vcpu_id_reg_feature_frac_check_test(struct kunit *test)
+{
+	struct kvm_vcpu *vcpu;
+	u32 id, frac_id;
+	struct id_reg_info id_data, frac_id_data;
+	struct id_reg_info *idr, *frac_idr;
+	struct feature_frac frac_data, *frac = &frac_data;
+	const struct frac_check_test *frct = test->param_value;
+
+	vcpu = test_kvm_vcpu_init(test);
+	KUNIT_ASSERT_TRUE(test, vcpu);
+
+	id = frct->feat.id;
+	frac_id = frct->frac_feat.id;
+
+	frac->id = id;
+	frac->shift = frct->feat.shift;
+	frac->frac_id = frac_id;
+	frac->frac_shift = frct->frac_feat.shift;
+
+	idr = GET_ID_REG_INFO(id);
+	frac_idr = GET_ID_REG_INFO(frac_id);
+
+	/* Save the original id_reg_info (and restore later) */
+	memcpy(&id_data, idr, sizeof(id_data));
+	memcpy(&frac_id_data, frac_idr, sizeof(frac_id_data));
+
+	/* The id could be same as the frac_id */
+	idr->vcpu_limit_val = (u64)frct->feat.limit << frac->shift;
+	frac_idr->vcpu_limit_val |=
+			(u64)frct->frac_feat.limit << frac->frac_shift;
+
+	write_kvm_id_reg(vcpu->kvm, id, (u64)frct->feat.value << frac->shift);
+	write_kvm_id_reg(vcpu->kvm, frac_id,
+			  (u64)frct->frac_feat.value << frac->frac_shift);
+
+	KUNIT_EXPECT_EQ(test,
+			vcpu_id_reg_feature_frac_check(vcpu, frac),
+			frct->ret);
+
+	/* Restore id_reg_info */
+	memcpy(idr, &id_data, sizeof(id_data));
+	memcpy(frac_idr, &frac_id_data, sizeof(frac_id_data));
+}
+
+/*
+ * Test parameter information to test validate_id_aa64mmfr0_tgran2
+ * and validate_id_aa64mmfr0_el1_test.
+ */
+struct tgran_test {
+	int gran2_field;
+	int gran2;
+	int gran2_lim;
+	int gran1;
+	int gran1_lim;
+	int ret;
+};
+
+/*
+ * Test parameters of validate_id_aa64mmfr0_tgran2_test() for TGran4_2.
+ * Defined values for the field are:
+ *  0x0: Support for 4KB granule at stage 2 is identified in TGran4.
+ *  0x1: 4KB granule not supported at stage 2.
+ *  0x2: 4KB granule supported at stage 2.
+ *  0x3: 4KB granule at stage 2 supports 52-bit input and output addresses.
+ *
+ * Defined values for the TGran4 are:
+ *  0x0: 4KB granule supported.
+ *  0x1: 4KB granule supports 52-bit input and output addresses.
+ *  0xf: 4KB granule not supported.
+ */
+struct tgran_test tgran4_2_test_params[] = {
+	/* Enable 4KB granule on the host that supports the granule */
+	{ID_AA64MMFR0_TGRAN4_2_SHIFT, 2, 2,  0,   0, 0},
+	/* Enable 4KB granule on the host that doesn't support the granule */
+	{ID_AA64MMFR0_TGRAN4_2_SHIFT, 2, 1,  0,   0, -E2BIG},
+	/* Disable 4KB granule on the host that supports the granule */
+	{ID_AA64MMFR0_TGRAN4_2_SHIFT, 1, 2,  0,   0, 0},
+	/* Enable 4KB granule on the host that supports the granule */
+	{ID_AA64MMFR0_TGRAN4_2_SHIFT, 0, 0,  0,   0, 0},
+	/* Disable 4KB granule */
+	{ID_AA64MMFR0_TGRAN4_2_SHIFT, 0, 1, 0xf,   0, 0},
+	/* Enable 4KB granule on the host that doesn't support the granule */
+	{ID_AA64MMFR0_TGRAN4_2_SHIFT, 0, 1,   0,   0, -E2BIG},
+	/* Disable 4KB granule */
+	{ID_AA64MMFR0_TGRAN4_2_SHIFT, 0, 2, 0xf,   0, 0},
+	/*
+	 * Enable 4KB granule with 52 bit address on the host that doesn't
+	 * support it.
+	 */
+	{ID_AA64MMFR0_TGRAN4_2_SHIFT, 0, 2,   1,   0, -E2BIG},
+	/* Disable 4KB granule */
+	{ID_AA64MMFR0_TGRAN4_2_SHIFT, 1, 0,   0, 0xf,  0},
+	/* Disable 4KB granule */
+	{ID_AA64MMFR0_TGRAN4_2_SHIFT, 1, 0,   0,   0,  0},
+	/* Enable 4KB granule on the host that doesn't support the granule */
+	{ID_AA64MMFR0_TGRAN4_2_SHIFT, 2, 0, 0xf, 0xf,  -E2BIG},
+	/* Enable 4KB granule on the host that supports the granule */
+	{ID_AA64MMFR0_TGRAN4_2_SHIFT, 2, 0,   0,   0,  0},
+};
+
+/*
+ * Test parameters of validate_id_aa64mmfr0_tgran2_test() for TGran64_2.
+ * Defined values for the field are:
+ *  0x0: Support for 64KB granule at stage 2 is identified in TGran64.
+ *  0x1: 64KB granule not supported at stage 2.
+ *  0x2: 64KB granule supported at stage 2.
+ *  0x3: 64KB granule at stage 2 supports 52-bit input and output addresses.
+ *
+ * Defined values for the TGran64 are:
+ *  0x0: 64KB granule supported.
+ *  0xf: 64KB granule not supported.
+ */
+struct tgran_test tgran64_2_test_params[] = {
+	/* Enable 64KB granule on the host that supports the granule */
+	{ID_AA64MMFR0_TGRAN64_2_SHIFT, 2, 2,   0,   0, 0},
+	/* Enable 64KB granule on the host that doesn't support the granule */
+	{ID_AA64MMFR0_TGRAN64_2_SHIFT, 2, 1,   0,   0, -E2BIG},
+	/* Enable 64KB granule on the host that supports the granule */
+	{ID_AA64MMFR0_TGRAN64_2_SHIFT, 1, 2,   0,   0, 0},
+	/* Enable 64KB granule on the host that supports the granule */
+	{ID_AA64MMFR0_TGRAN64_2_SHIFT, 0, 0,   0,   0, 0},
+	/* Disable 64KB granule */
+	{ID_AA64MMFR0_TGRAN64_2_SHIFT, 0, 1, 0xf,   0, 0},
+	/* Enable 64KB granule on the host that doesn't support the granule */
+	{ID_AA64MMFR0_TGRAN64_2_SHIFT, 0, 1,   0,   0, -E2BIG},
+	/* Disable 64KB granule */
+	{ID_AA64MMFR0_TGRAN64_2_SHIFT, 0, 2, 0xf,   0, 0},
+	/* Disable 64KB granule */
+	{ID_AA64MMFR0_TGRAN64_2_SHIFT, 1, 0,   0, 0xf, 0},
+	/* Disable 64KB granule */
+	{ID_AA64MMFR0_TGRAN64_2_SHIFT, 1, 0,   0,   0, 0},
+	/* Enable 64KB granule on the host that doesn't support the granule */
+	{ID_AA64MMFR0_TGRAN64_2_SHIFT, 2, 0, 0xf, 0xf, -E2BIG},
+	/* Enable 64KB granule on the host that supports the granule */
+	{ID_AA64MMFR0_TGRAN64_2_SHIFT, 2, 0,   0,   0, 0},
+};
+
+/*
+ * Test parameters of validate_id_aa64mmfr0_tgran2_test() for TGran16_2
+ * Defined values for the field are:
+ *  0x0: Support for 16KB granule at stage 2 is identified in TGran16.
+ *  0x1: 16KB granule not supported at stage 2.
+ *  0x2: 16KB granule supported at stage 2.
+ *  0x3: 16KB granule at stage 2 supports 52-bit input and output addresses.
+ *
+ * Defined values for the TGran16 are:
+ *  0x0: 16KB granule not supported.
+ *  0x1: 16KB granule supported.
+ *  0x2: 16KB granule supports 52-bit input and output addresses.
+ */
+struct tgran_test tgran16_2_test_params[] = {
+	/* Enable 16KB granule on the host that supports the granule */
+	{ID_AA64MMFR0_TGRAN16_2_SHIFT, 2, 2,  0,  0, 0},
+	/* Enable 16KB granule on the host that doesn't support the granule */
+	{ID_AA64MMFR0_TGRAN16_2_SHIFT, 2, 1,  0,  0, -E2BIG},
+	/* Disable 16KB granule on the host that supports the granule */
+	{ID_AA64MMFR0_TGRAN16_2_SHIFT, 1, 2,  0,  0, 0},
+	/* Disable 16KB granule */
+	{ID_AA64MMFR0_TGRAN16_2_SHIFT, 0, 0,  0,  0, 0},
+	/* Disable 16KB granule */
+	{ID_AA64MMFR0_TGRAN16_2_SHIFT, 0, 1,  0,  0, 0},
+	/* Enable 16KB granule on the host that doesn't support the granule */
+	{ID_AA64MMFR0_TGRAN16_2_SHIFT, 0, 1,  1,  0, -E2BIG},
+	/* Disable 16KB granule */
+	{ID_AA64MMFR0_TGRAN16_2_SHIFT, 0, 2,  0,  0, 0},
+	/*
+	 * Enable 16KB granule with 52 bit address on the host that doesn't
+	 * support it.
+	 */
+	{ID_AA64MMFR0_TGRAN16_2_SHIFT, 0, 2,  2,  2, -E2BIG},
+	/* Disable 16KB granule */
+	{ID_AA64MMFR0_TGRAN16_2_SHIFT, 1, 0,  0,  0, 0},
+	/* Disable 16KB granule */
+	{ID_AA64MMFR0_TGRAN16_2_SHIFT, 1, 0,  0,  1, 0},
+	/* Enable 16KB granule on the host that doesn't support the granule */
+	{ID_AA64MMFR0_TGRAN16_2_SHIFT, 2, 0,  0,  0, -E2BIG},
+	/* Enable 16KB granule on the host that supports the granule */
+	{ID_AA64MMFR0_TGRAN16_2_SHIFT, 2, 0,  0,  1, 0},
+	/* Enable 16KB granule on the host that supports the granule */
+	{ID_AA64MMFR0_TGRAN16_2_SHIFT, 2, 0,  0,  2, 0},
+};
+
+static void tgran2_case_to_desc(struct tgran_test *t, char *desc)
+{
+	snprintf(desc, KUNIT_PARAM_DESC_SIZE,
+		 "gran2(field=%d): val=%d, lim=%d gran1: val=%d limit=%d\n",
+		 t->gran2_field, t->gran2, t->gran2_lim,
+		 t->gran1, t->gran1_lim);
+}
+
+KUNIT_ARRAY_PARAM(tgran4_2, tgran4_2_test_params, tgran2_case_to_desc);
+KUNIT_ARRAY_PARAM(tgran64_2, tgran64_2_test_params, tgran2_case_to_desc);
+KUNIT_ARRAY_PARAM(tgran16_2, tgran16_2_test_params, tgran2_case_to_desc);
+
+#define	MAKE_MMFR0_TGRAN(shift1, gran1, shift2, gran2)		\
+	(((u64)((gran1) & 0xf) << (shift1)) |			\
+	 ((u64)((gran2) & 0xf) << (shift2)))
+
+/* Return the bit position of TGranX field for the given TGranX_2 field. */
+static int tgran2_to_tgran1_shift(int tgran2_shift)
+{
+	int tgran1_shift = -1;
+
+	switch (tgran2_shift) {
+	case ID_AA64MMFR0_TGRAN4_2_SHIFT:
+		tgran1_shift = ID_AA64MMFR0_TGRAN4_SHIFT;
+		break;
+	case ID_AA64MMFR0_TGRAN64_2_SHIFT:
+		tgran1_shift = ID_AA64MMFR0_TGRAN64_SHIFT;
+		break;
+	case ID_AA64MMFR0_TGRAN16_2_SHIFT:
+		tgran1_shift = ID_AA64MMFR0_TGRAN16_SHIFT;
+		break;
+	default:
+		break;
+	}
+
+	return tgran1_shift;
+}
+
+/* Tests for validate_id_aa64mmfr0_el1(). */
+static void validate_id_aa64mmfr0_tgran2_test(struct kunit *test)
+{
+	const struct tgran_test *t = test->param_value;
+	int shift1, shift2;
+	u64 v, lim;
+
+	shift2 = t->gran2_field;
+	shift1 = tgran2_to_tgran1_shift(shift2);
+	v = MAKE_MMFR0_TGRAN(shift1, t->gran1, shift2, t->gran2);
+	lim = MAKE_MMFR0_TGRAN(shift1, t->gran1_lim, shift2, t->gran2_lim);
+
+	KUNIT_EXPECT_EQ(test, aa64mmfr0_tgran2_check(shift2, v, lim), t->ret);
+}
+
+/* Tests for validate_id_aa64pfr0_el1(). */
+static void validate_id_aa64pfr0_el1_test(struct kunit *test)
+{
+	struct id_reg_info *id_reg;
+	struct kvm_vcpu *vcpu;
+	u64 v;
+
+	vcpu = test_kvm_vcpu_init(test);
+	KUNIT_ASSERT_TRUE(test, vcpu);
+
+	id_reg = GET_ID_REG_INFO(SYS_ID_AA64PFR0_EL1);
+
+	v = 0;
+	KUNIT_EXPECT_EQ(test, validate_id_aa64pfr0_el1(vcpu, id_reg, v), 0);
+
+	/*
+	 * Tests for GIC.
+	 * GIC must be 1 when vGIC3 is configured.
+	 */
+	v = 0x0000000;	/* GIC = 0 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64pfr0_el1(vcpu, id_reg, v), 0);
+
+	/* Test with VGIC_V2 */
+	vcpu->kvm->arch.vgic.in_kernel = true;
+	vcpu->kvm->arch.vgic.vgic_model = KVM_DEV_TYPE_ARM_VGIC_V2;
+
+	v = 0x0000000;	/* GIC = 0 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64pfr0_el1(vcpu, id_reg, v), 0);
+
+	/* Test with VGIC_V3 */
+	vcpu->kvm->arch.vgic.vgic_model = KVM_DEV_TYPE_ARM_VGIC_V3;
+
+	v = 0x0000000;	/* GIC = 0 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64pfr0_el1(vcpu, id_reg, v), 0);
+	v = 0x1000000;	/* GIC = 1 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64pfr0_el1(vcpu, id_reg, v), 0);
+
+	/* Restore the original VGIC state */
+	vcpu->kvm->arch.vgic.in_kernel = false;
+	vcpu->kvm->arch.vgic.vgic_model = 0;
+
+	/*
+	 * Tests for AdvSIMD/FP.
+	 * AdvSIMD must have the same value as FP.
+	 */
+
+	/* Tests with SVE disabled */
+	v = 0x000010000;	/* AdvSIMD = 0, FP = 1 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64pfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x000100000;	/* AdvSIMD = 1, FP = 0 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64pfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x000ff0000;	/* AdvSIMD = 0xf, FP = 0xf */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64pfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x100000000;	/* SVE =1, AdvSIMD = 0, FP = 0 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64pfr0_el1(vcpu, id_reg, v), 0);
+	if (!system_supports_sve()) {
+		kunit_skip(test, "No SVE support. Partial skip)");
+		/* Not reached */
+	}
+
+	/* Tests with SVE enabled */
+	vcpu->arch.flags |= KVM_ARM64_GUEST_HAS_SVE;
+
+	v = 0x100000000;	/* SVE =1, AdvSIMD = 0, FP = 0 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64pfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x100ff0000;	/* SVE =1, AdvSIMD = 0, FP = 0 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64pfr0_el1(vcpu, id_reg, v), 0);
+
+	vcpu->arch.flags &= ~KVM_ARM64_GUEST_HAS_SVE;
+}
+
+/* Tests for validate_id_aa64pfr1_el1() */
+static void validate_id_aa64pfr1_el1_test(struct kunit *test)
+{
+	struct id_reg_info *id_reg;
+	struct kvm_vcpu *vcpu;
+	u64 v;
+
+	vcpu = test_kvm_vcpu_init(test);
+	KUNIT_ASSERT_TRUE(test, vcpu);
+
+	id_reg = GET_ID_REG_INFO(SYS_ID_AA64PFR1_EL1);
+	v = 0;
+	KUNIT_EXPECT_EQ(test, validate_id_aa64pfr1_el1(vcpu, id_reg, v), 0);
+
+	/* Tests for MTE */
+
+	/* Tests with MTE disabled */
+	KUNIT_EXPECT_FALSE(test, vcpu->kvm->arch.mte_enabled);
+
+	v = 0x000;	/* MTE = 0 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64pfr1_el1(vcpu, id_reg, v), 0);
+
+	v = 0x100;	/* MTE = 1*/
+	KUNIT_EXPECT_NE(test, validate_id_aa64pfr1_el1(vcpu, id_reg, v), 0);
+
+	if (!system_supports_mte()) {
+		kunit_skip(test, "(No MTE support. Partial skip)");
+		/* Not reached */
+	}
+
+	/* Tests with MTE enabled */
+	vcpu->kvm->arch.mte_enabled = true;
+
+	v = 0x100;	/* MTE = 1*/
+	KUNIT_EXPECT_EQ(test, validate_id_aa64pfr1_el1(vcpu, id_reg, v), 0);
+
+	v = 0x0;	/* MTE = 0 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64pfr1_el1(vcpu, id_reg, v), 0);
+}
+
+/* Tests for validate_id_aa64isar0_el1(). */
+static void validate_id_aa64isar0_el1_test(struct kunit *test)
+{
+	struct id_reg_info *id_reg;
+	struct kvm_vcpu *vcpu;
+	u64 v;
+
+	vcpu = test_kvm_vcpu_init(test);
+	KUNIT_ASSERT_TRUE(test, vcpu);
+
+	id_reg = GET_ID_REG_INFO(SYS_ID_AA64ISAR0_EL1);
+
+	v = 0;
+	KUNIT_EXPECT_EQ(test, validate_id_aa64isar0_el1(vcpu, id_reg, v), 0);
+
+	/*
+	 * Tests for SM3/SM4.
+	 * Arm ARM says SM3 must have the same value as SM4.
+	 */
+
+	v = 0x01000000000;	/* SM4 = 0, SM3 = 1 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64isar0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x10000000000;	/* SM4 = 1, SM3 = 0 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64isar0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x11000000000;	/* SM3 = SM4 = 1 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64isar0_el1(vcpu, id_reg, v), 0);
+
+
+	/*
+	 * Tests for SHA1/SHA2/SHA3.  Arm ARM says:
+	 * If SHA1 is 0x0, both SHA2 and SHA3 must be 0x0.
+	 * If SHA2 is 0x0, SHA1 must be 0x0.
+	 * If SHA2 is 0x2, SHA3 must be 0x1.
+	 * If SHA3 is 0x1, SHA2 msut be 0x2.
+	 */
+
+	v = 0x000000100;	/* SHA2 = 0, SHA1 = 1 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64isar0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x000001000;	/* SHA2 = 1, SHA1 = 0 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64isar0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x000001100;	/* SHA2 = 1, SHA1 = 1 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64isar0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x100002000;	/* SHA3 = 1, SHA2 = 2 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64isar0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x000002000;	/* SHA3 = 0, SHA2 = 2 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64isar0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x100001000;	/* SHA3 = 1, SHA2 = 1 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64isar0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x200000000;	/* SHA3 = 2, SHA1 = 0 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64isar0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x200001100;	/* SHA3 = 2, SHA2= 1, SHA1 = 1 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64isar0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x300003300;	/* SHA3 = 3, SHA2 = 3, SHA1 = 3 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64isar0_el1(vcpu, id_reg, v), 0);
+}
+
+/* Tests for validate_id_aa64isar1_el1() */
+static void validate_id_aa64isar1_el1_test(struct kunit *test)
+{
+	struct id_reg_info *id_reg;
+	struct kvm_vcpu *vcpu;
+	u64 v;
+
+	vcpu = test_kvm_vcpu_init(test);
+	KUNIT_ASSERT_TRUE(test, vcpu);
+
+	id_reg = GET_ID_REG_INFO(SYS_ID_AA64ISAR1_EL1);
+
+	v = 0;
+	KUNIT_EXPECT_EQ(test, validate_id_aa64isar1_el1(vcpu, id_reg, v), 0);
+
+	/*
+	 * Tests for GPI/GPA/API/APA.
+	 * Arm ARM says:
+	 * If GPA is non-zero, GPI must be zero.
+	 * If GPI is non-zero, GPA must be zero.
+	 * If APA is non-zero, API must be zero.
+	 * If API is non-zero, APA must be zero.
+	 */
+
+	/* Tests with PTRAUTH disabled */
+	v = 0x11000110;	/* GPI = 1, GPA = 1, API = 1, APA = 1 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64isar1_el1(vcpu, id_reg, v), 0);
+
+	v = 0x11000100;	/* GPI = 1, GPA = 1, API = 1, APA = 0 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64isar1_el1(vcpu, id_reg, v), 0);
+
+	v = 0x11000010;	/* GPI = 1, GPA = 1, API = 0, APA = 1 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64isar1_el1(vcpu, id_reg, v), 0);
+
+	v = 0x10000110;	/* GPI = 1, GPA = 0, API = 1, APA = 1 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64isar1_el1(vcpu, id_reg, v), 0);
+
+	v = 0x01000110;	/* GPI = 0, GPA = 1, API = 1, APA = 1 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64isar1_el1(vcpu, id_reg, v), 0);
+
+	if (!system_has_full_ptr_auth()) {
+		kunit_skip(test, "(No PTRAUTH support. Partial skip)");
+		/* Not reached */
+	}
+
+	/* Tests with PTRAUTH enabled */
+	vcpu->arch.flags |= KVM_ARM64_GUEST_HAS_PTRAUTH;
+
+	v = 0x10000100;	/* GPI = 1, GPA = 0, API = 1, APA = 0 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64isar1_el1(vcpu, id_reg, v), 0);
+
+	v = 0x10000010;	/* GPI = 1, GPA = 0, API = 0, APA = 1 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64isar1_el1(vcpu, id_reg, v), 0);
+
+	v = 0x01000100;	/* GPI = 0, GPA = 1, API = 1, APA = 0 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64isar1_el1(vcpu, id_reg, v), 0);
+
+	v = 0x01000010;	/* GPI = 0, GPA = 1, API = 0, APA = 1 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64isar1_el1(vcpu, id_reg, v), 0);
+
+	v = 0;
+	KUNIT_EXPECT_NE(test, validate_id_aa64isar1_el1(vcpu, id_reg, v), 0);
+}
+
+/* Tests for validate_id_aa64mmfr0_el1() */
+static void validate_id_aa64mmfr0_el1_test(struct kunit *test)
+{
+	struct id_reg_info id_data, *id_reg;
+	const struct tgran_test *t4, *t64, *t16;
+	struct kvm_vcpu *vcpu;
+	int field4, field4_2, field64, field64_2, field16, field16_2;
+	u64 v, v4, lim4, v64, lim64, v16, lim16;
+	int i, j, ret;
+
+	id_reg = GET_ID_REG_INFO(SYS_ID_AA64MMFR0_EL1);
+
+	/* Save the original id_reg_info (and restore later) */
+	memcpy(&id_data, id_reg, sizeof(id_data));
+
+	vcpu = test_kvm_vcpu_init(test);
+
+	t4 = test->param_value;
+	field4_2 = t4->gran2_field;
+	field4 = tgran2_to_tgran1_shift(field4_2);
+	v4 = MAKE_MMFR0_TGRAN(field4, t4->gran1, field4_2, t4->gran2);
+	lim4 = MAKE_MMFR0_TGRAN(field4, t4->gran1_lim, field4_2, t4->gran2_lim);
+
+	/*
+	 * For each given gran4_2 params, test validate_id_aa64mmfr0_el1
+	 * with each of tgran64_2 and tgran16_2 params.
+	 */
+	for (i = 0; i < ARRAY_SIZE(tgran64_2_test_params); i++) {
+		t64 = &tgran64_2_test_params[i];
+		field64_2 = t64->gran2_field;
+		field64 = tgran2_to_tgran1_shift(field64_2);
+		v64 = MAKE_MMFR0_TGRAN(field64, t64->gran1,
+				       field64_2, t64->gran2);
+		lim64 = MAKE_MMFR0_TGRAN(field64, t64->gran1_lim,
+					 field64_2, t64->gran2_lim);
+
+		for (j = 0; j < ARRAY_SIZE(tgran16_2_test_params); j++) {
+			t16 = &tgran16_2_test_params[j];
+
+			field16_2 = t16->gran2_field;
+			field16 = tgran2_to_tgran1_shift(field16_2);
+			v16 = MAKE_MMFR0_TGRAN(field16, t16->gran1,
+					       field16_2, t16->gran2);
+			lim16 = MAKE_MMFR0_TGRAN(field16, t16->gran1_lim,
+						 field16_2, t16->gran2_lim);
+
+			/* Build id_aa64mmfr0_el1 from tgran16/64/4 values */
+			v = v16 | v64 | v4;
+			id_reg->vcpu_limit_val = lim16 | lim64 | lim4;
+
+			ret = t4->ret ? t4->ret : t64->ret;
+			ret = ret ? ret : t16->ret;
+			KUNIT_EXPECT_EQ(test,
+				validate_id_aa64mmfr0_el1(vcpu, id_reg, v),
+				ret);
+		}
+	}
+
+	/* Restore id_reg_info */
+	memcpy(id_reg, &id_data, sizeof(id_data));
+}
+
+/* Tests for validate_id_aa64dfr0_el1() */
+static void validate_id_aa64dfr0_el1_test(struct kunit *test)
+{
+	struct id_reg_info *id_reg;
+	struct kvm_vcpu *vcpu;
+	u64 v;
+
+	id_reg = GET_ID_REG_INFO(SYS_ID_AA64DFR0_EL1);
+	vcpu = test_kvm_vcpu_init(test);
+	KUNIT_ASSERT_TRUE(test, vcpu);
+
+	v = 0;
+	KUNIT_EXPECT_EQ(test, validate_id_aa64dfr0_el1(vcpu, id_reg, v), 0);
+
+	/*
+	 * Tests for CTX_CMPS/BRPS.
+	 * Number of context-aware breakpoints can be no more than number
+	 * of supported breakpoints.
+	 */
+	v = 0x10001000;	/* CTX_CMPS = 1, BRPS = 1 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64dfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x20001000;	/* CTX_CMPS = 2, BRPS = 1 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64dfr0_el1(vcpu, id_reg, v), 0);
+
+	/* Tests for PMUVer */
+
+	/* Tests with PMUv3 disabled. */
+
+	v = 0x000;	/* PMUVER = 0x0 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64dfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0xf00;	/* PMUVER = 0xf */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64dfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x100;	/* PMUVER = 1 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64dfr0_el1(vcpu, id_reg, v), 0);
+
+	/* Tests with PMUv3 enabled */
+	set_bit(KVM_ARM_VCPU_PMU_V3, vcpu->arch.features);
+
+	v = 0x000;	/* PMUVER = 0x0 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64dfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x000;	/* PMUVER = 0xf */
+	KUNIT_EXPECT_NE(test, validate_id_aa64dfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x100;	/* PMUVER = 1 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64dfr0_el1(vcpu, id_reg, v), 0);
+}
+
+/* Tests for validate_id_dfr0_el1() */
+static void validate_id_dfr0_el1_test(struct kunit *test)
+{
+	struct id_reg_info *id_reg;
+	struct kvm_vcpu *vcpu;
+	u64 v;
+
+	id_reg = GET_ID_REG_INFO(SYS_ID_DFR0_EL1);
+	vcpu = test_kvm_vcpu_init(test);
+	KUNIT_ASSERT_TRUE(test, vcpu);
+
+	v = 0;
+	KUNIT_EXPECT_EQ(test, validate_id_dfr0_el1(vcpu, id_reg, v), 0);
+
+	/* Tests for PERFMON */
+
+	/* Tests with PMUv3 disabled */
+
+	v = 0x0000000;	/* PERFMON = 0x0 */
+	KUNIT_EXPECT_EQ(test, validate_id_dfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0xf000000;	/* PERFMON = 0xf */
+	KUNIT_EXPECT_EQ(test, validate_id_dfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x1000000;	/* PERFMON = 1 */
+	KUNIT_EXPECT_NE(test, validate_id_dfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x2000000;	/* PERFMON = 2 */
+	KUNIT_EXPECT_NE(test, validate_id_dfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x3000000;	/* PERFMON = 3 */
+	KUNIT_EXPECT_NE(test, validate_id_dfr0_el1(vcpu, id_reg, v), 0);
+
+
+	/* Tests with PMUv3 enabled */
+	set_bit(KVM_ARM_VCPU_PMU_V3, vcpu->arch.features);
+
+	v = 0x0000000;	/* PERFMON = 0x0 */
+	KUNIT_EXPECT_NE(test, validate_id_dfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0xf000000;	/* PERFMON = 0xf */
+	KUNIT_EXPECT_NE(test, validate_id_dfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x1000000;	/* PERFMON = 1 */
+	KUNIT_EXPECT_NE(test, validate_id_dfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x2000000;	/* PERFMON = 2 */
+	KUNIT_EXPECT_NE(test, validate_id_dfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x3000000;	/* PERFMON = 3 */
+	KUNIT_EXPECT_EQ(test, validate_id_dfr0_el1(vcpu, id_reg, v), 0);
+}
+
+/* Tests for validate_mvfr1_el1(). */
+static void validate_mvfr1_el1_test(struct kunit *test)
+{
+	struct id_reg_info *id_reg;
+	struct kvm_vcpu *vcpu;
+	u64 v;
+
+	id_reg = GET_ID_REG_INFO(SYS_MVFR1_EL1);
+	vcpu = test_kvm_vcpu_init(test);
+	KUNIT_ASSERT_TRUE(test, vcpu);
+
+	v = 0;
+	KUNIT_EXPECT_EQ(test, validate_mvfr1_el1(vcpu, id_reg, v), 0);
+
+	/*
+	 * Tests for FPHP/SIMDHP.
+	 * Arm ARM says the level of support indicated by FPHP must be
+	 * equivalent to the level of support indicated by the SIMDHP,
+	 * meaning the permitted values are:
+	 * FPHP = 0x0, SIMDHP = 0x0
+	 * FPHP = 0x2, SIMDHP = 0x1
+	 * FPHP = 0x3, SIMDHP = 0x2
+	 */
+	v = 0x0000000;	/* FPHP = 0, SIMDHP = 0 */
+	KUNIT_EXPECT_EQ(test, validate_mvfr1_el1(vcpu, id_reg, v), 0);
+
+	v = 0x2100000;	/* FPHP = 2, SIMDHP = 1 */
+	KUNIT_EXPECT_EQ(test, validate_mvfr1_el1(vcpu, id_reg, v), 0);
+
+	v = 0x3200000;	/* FPHP = 3, SIMDHP = 2 */
+	KUNIT_EXPECT_EQ(test, validate_mvfr1_el1(vcpu, id_reg, v), 0);
+
+	v = 0x1100000;	/* FPHP = 1, SIMDHP = 1 */
+	KUNIT_EXPECT_NE(test, validate_mvfr1_el1(vcpu, id_reg, v), 0);
+
+	v = 0x2200000;	/* FPHP = 2, SIMDHP = 2 */
+	KUNIT_EXPECT_NE(test, validate_mvfr1_el1(vcpu, id_reg, v), 0);
+
+	v = 0x3300000;	/* FPHP = 3, SIMDHP = 3 */
+	KUNIT_EXPECT_NE(test, validate_mvfr1_el1(vcpu, id_reg, v), 0);
+
+	v = (u64)-1;
+	KUNIT_EXPECT_NE(test, validate_mvfr1_el1(vcpu, id_reg, v), 0);
+}
+
+/*
+ * Helper function for validate_id_reg_test().
+ * We don't use KUNIT_ASSERT or kunit_skip because this is a helper test
+ * function and we are not sure if it's safe to exist from the test case.
+ */
+static void validate_id_reg_test_one_field(struct kunit *test,
+		u32 id, int pos, int fval, int flimit,
+		bool is_signed, struct id_reg_info *idr)
+{
+	struct kvm_vcpu *vcpu;
+	int fmin = is_signed ? -1 : 0;
+	int fmax = is_signed ? 7 : 15;
+	u64 fmask = ARM64_FEATURE_FIELD_MASK;
+	u64 val;
+
+	vcpu = test_kvm_vcpu_init(test);
+	KUNIT_ASSERT_TRUE(test, vcpu);
+
+	if (flimit > fmax) {
+		/* Shouldn't happen. Make the test failure. */
+		KUNIT_EXPECT_FALSE(test, flimit > fmax);
+		kunit_err(test, "%s: flimit(%d) > fmax(%d). Must be test bug",
+			  __func__, flimit, fmax);
+		return;
+	}
+
+	if (fval > fmin) {
+		/* Set the field to a smaller value */
+		val = ((u64)(fval - 1) & fmask) << pos;
+		KUNIT_EXPECT_EQ(test, validate_id_reg(vcpu, id, val), 0);
+	}
+
+	if (fval < flimit) {
+		/* Set the field to a larger value, but smaller than flimit */
+		val = ((u64)(fval + 1) & fmask) << pos;
+		KUNIT_EXPECT_EQ(test, validate_id_reg(vcpu, id, val), 0);
+		/* Set the field to the flimit */
+		val = ((u64)flimit & fmask) << pos;
+		KUNIT_EXPECT_EQ(test, validate_id_reg(vcpu, id, val), 0);
+	}
+
+	if (flimit < fmax) {
+		/* Set the field to a larger value than flimit */
+		val = ((u64)(flimit + 1) & fmask) << pos;
+		KUNIT_EXPECT_NE(test, validate_id_reg(vcpu, id, val), 0);
+
+		/* Test with ignore_mask */
+		if (idr) {
+			idr->ignore_mask = fmask << pos;
+			KUNIT_EXPECT_EQ(test,
+					validate_id_reg(vcpu, id, val),
+					0);
+		}
+	}
+	test_kvm_vcpu_fini(test, vcpu);
+}
+
+/*
+ * Test for validate_id_reg().
+ */
+static void validate_id_reg_test(struct kunit *test)
+{
+	struct id_reg_info idr_data, *idr, *original_idr;
+	u32 id;
+	int fval, flim, pos;
+	u64 val;
+	bool sign;
+
+	/* Use AA64PFR0_EL1 because it includes both sign/unsigned fields */
+	id = SYS_ID_AA64PFR0_EL1;
+
+	/* Save the original id_reg_info */
+	original_idr = GET_ID_REG_INFO(id);
+
+	/* Test with a temporary id_reg_info for testing */
+	idr = &idr_data;
+	GET_ID_REG_INFO(id) = idr;
+
+	fval = 0x1;
+	flim = 0x2;
+
+	/* Test with unsigned field */
+	pos = ID_AA64PFR0_RAS_SHIFT;
+
+	/* Set up id_reg_info for testing */
+	memset(idr, 0, sizeof(*idr));
+	idr->sys_reg = id;
+	idr->vcpu_limit_val = (u64)flim << pos;
+	validate_id_reg_test_one_field(test, id, pos, fval, flim, false, idr);
+
+	/* Test with signed field */
+	pos = ID_AA64PFR0_FP_SHIFT;
+
+	/* Set up id_reg_info for testing */
+	memset(idr, 0, sizeof(*idr));
+	idr->sys_reg = id;
+	idr->vcpu_limit_val = (u64)flim << pos;
+	validate_id_reg_test_one_field(test, id, pos, fval, flim, true, idr);
+
+
+	/* Test without id_reg_info */
+	GET_ID_REG_INFO(id) = NULL;
+	if (original_idr)
+		val = original_idr->vcpu_limit_val;
+	else
+		val = read_sanitised_ftr_reg(id);
+
+	for (pos = 0; pos < 64; pos += 4) {
+		if (pos == ID_AA64PFR0_FP_SHIFT ||
+		    pos == ID_AA64PFR0_ASIMD_SHIFT)
+			sign = true;
+		else
+			sign = false;
+
+		fval = cpuid_feature_extract_field(val, pos, sign);
+		validate_id_reg_test_one_field(test, id, pos, fval, fval,
+					       sign, NULL);
+	}
+
+	/* Restore the original id_reg_info */
+	GET_ID_REG_INFO(id) = original_idr;
+}
+
+static struct kunit_case kvm_sys_regs_test_cases[] = {
+	KUNIT_CASE_PARAM(vcpu_id_reg_feature_frac_check_test, frac_gen_params),
+	KUNIT_CASE_PARAM(validate_id_aa64mmfr0_tgran2_test, tgran4_2_gen_params),
+	KUNIT_CASE_PARAM(validate_id_aa64mmfr0_tgran2_test, tgran64_2_gen_params),
+	KUNIT_CASE_PARAM(validate_id_aa64mmfr0_tgran2_test, tgran16_2_gen_params),
+	KUNIT_CASE(validate_id_aa64pfr0_el1_test),
+	KUNIT_CASE(validate_id_aa64pfr1_el1_test),
+	KUNIT_CASE(validate_id_aa64isar0_el1_test),
+	KUNIT_CASE(validate_id_aa64isar1_el1_test),
+	KUNIT_CASE_PARAM(validate_id_aa64mmfr0_el1_test, tgran4_2_gen_params),
+	KUNIT_CASE(validate_id_aa64dfr0_el1_test),
+	KUNIT_CASE(validate_id_dfr0_el1_test),
+	KUNIT_CASE(validate_mvfr1_el1_test),
+	KUNIT_CASE(validate_id_reg_test),
+	{}
+};
+
+static struct kunit_suite kvm_sys_regs_test_suite = {
+	.name = "kvm-sys-regs-test-suite",
+	.test_cases = kvm_sys_regs_test_cases,
+};
+
+kunit_test_suites(&kvm_sys_regs_test_suite);
+MODULE_LICENSE("GPL");
-- 
2.34.1.448.ga2b2bfdf31-goog


^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 16/26] KVM: arm64: Add kunit test for ID register validation
@ 2022-01-06  4:26   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, Will Deacon, Peter Shier, Paolo Bonzini, linux-arm-kernel

Add kunit tests for functions that are used for validation of ID
registers, CONFIG_KVM_KUNIT_TEST option to enable the tests, and
.kunitconfig to run the kunit tests.

One line change below is needed in the default arm64.py to fully
run all of those kunit tests.
-----------------------------------------------------------------------
$ diff tools/testing/kunit/qemu_configs/arm64.py arm64_kvm_min.py
12c12
< 			   extra_qemu_params=['-machine virt', '-cpu cortex-a57'])
---
> 			   extra_qemu_params=['-M virt,virtualization=on,mte=on', '-cpu max,sve=on'])
-----------------------------------------------------------------------

The outputs from the tests are:
-----------------------------------------------------------------------
$ tools/testing/kunit/kunit.py run --timeout=60 --jobs=`nproc --all` \
          --arch=arm64 --cross_compile=aarch64-linux-gnu- \
          --qemu_config arm64_kvm_min.py \
          --kunitconfig=arch/arm64/kvm/.kunitconfig
[12:14:24] Configuring KUnit Kernel ...
[12:14:24] Building KUnit Kernel ...
Populating config with:
$ make ARCH=arm64 olddefconfig CROSS_COMPILE=aarch64-linux-gnu- O=.kunit
Building with:
$ make ARCH=arm64 --jobs=96 CROSS_COMPILE=aarch64-linux-gnu- O=.kunit
[12:14:31] Starting KUnit Kernel (1/1)...
[12:14:31] ============================================================
Running tests with:
$ qemu-system-aarch64 -nodefaults -m 1024 -kernel .kunit/arch/arm64/boot/Image.gz -append 'mem=1G console=tty kunit_shutdown=halt console=ttyAMA0 kunit_shutdown=reboot' -no-reboot -nographic -serial stdio -M virt,virtualization=on,mte=on -cpu max,sve=on
[12:14:32] ========== kvm-sys-regs-test-suite (13 subtests) ===========
[12:14:32] [PASSED] vcpu_id_reg_feature_frac_check_test
[12:14:32] [PASSED] validate_id_aa64mmfr0_tgran2_test
[12:14:32] [PASSED] validate_id_aa64mmfr0_tgran2_test
[12:14:32] [PASSED] validate_id_aa64mmfr0_tgran2_test
[12:14:32] [PASSED] validate_id_aa64pfr0_el1_test
[12:14:32] [PASSED] validate_id_aa64pfr1_el1_test
[12:14:32] [PASSED] validate_id_aa64isar0_el1_test
[12:14:32] [PASSED] validate_id_aa64isar1_el1_test
[12:14:32] [PASSED] validate_id_aa64mmfr0_el1_test
[12:14:32] [PASSED] validate_id_aa64dfr0_el1_test
[12:14:32] [PASSED] validate_id_dfr0_el1_test
[12:14:32] [PASSED] validate_mvfr1_el1_test
[12:14:32] [PASSED] validate_id_reg_test
[12:14:32] ============= [PASSED] kvm-sys-regs-test-suite =============
[12:14:32] ============================================================
[12:14:32] Testing complete. Passed: 13, Failed: 0, Crashed: 0, Skipped: 0, Errors: 0
[12:14:32] Elapsed time: 8.223s total, 0.002s configuring, 6.655s building, 1.565s running
-----------------------------------------------------------------------

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/kvm/.kunitconfig    |   4 +
 arch/arm64/kvm/Kconfig         |  11 +
 arch/arm64/kvm/sys_regs.c      |   4 +
 arch/arm64/kvm/sys_regs_test.c | 985 +++++++++++++++++++++++++++++++++
 4 files changed, 1004 insertions(+)
 create mode 100644 arch/arm64/kvm/.kunitconfig
 create mode 100644 arch/arm64/kvm/sys_regs_test.c

diff --git a/arch/arm64/kvm/.kunitconfig b/arch/arm64/kvm/.kunitconfig
new file mode 100644
index 000000000000..c564c98fc319
--- /dev/null
+++ b/arch/arm64/kvm/.kunitconfig
@@ -0,0 +1,4 @@
+CONFIG_KUNIT=y
+CONFIG_VIRTUALIZATION=y
+CONFIG_KVM=y
+CONFIG_KVM_KUNIT_TEST=y
diff --git a/arch/arm64/kvm/Kconfig b/arch/arm64/kvm/Kconfig
index 8ffcbe29395e..48fbdd17b2fd 100644
--- a/arch/arm64/kvm/Kconfig
+++ b/arch/arm64/kvm/Kconfig
@@ -54,4 +54,15 @@ config NVHE_EL2_DEBUG
 
 	  If unsure, say N.
 
+config KVM_KUNIT_TEST
+	bool "KUnit tests for KVM on ARM64 processors" if !KUNIT_ALL_TESTS
+	depends on KVM && KUNIT
+	default KUNIT_ALL_TESTS
+	help
+	  Say Y here to enable KUnit tests for the KVM on ARM64.
+	  Only useful for KVM/ARM development and are not for inclusion into
+	  a production build.
+
+	  If unsure, say N.
+
 endif # VIRTUALIZATION
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 6adb7b04620c..d5bd04c68cd4 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -3636,3 +3636,7 @@ int kvm_set_id_reg_feature(struct kvm *kvm, u32 id, u8 field_shift, u8 fval)
 
 	return __modify_kvm_id_reg(kvm, id, val, preserve_mask);
 }
+
+#if IS_ENABLED(CONFIG_KVM_KUNIT_TEST)
+#include "sys_regs_test.c"
+#endif
diff --git a/arch/arm64/kvm/sys_regs_test.c b/arch/arm64/kvm/sys_regs_test.c
new file mode 100644
index 000000000000..5b1b95bd9ec4
--- /dev/null
+++ b/arch/arm64/kvm/sys_regs_test.c
@@ -0,0 +1,985 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * KUnit tests for arch/arm64/kvm/sys_regs.c.
+ */
+
+#include <linux/module.h>
+#include <kunit/test.h>
+#include <kunit/test.h>
+#include <linux/kvm_host.h>
+#include <asm/cpufeature.h>
+#include "asm/sysreg.h"
+
+/*
+ * Create a vcpu with the minimum fields required for testing in this file
+ * including the struct kvm.  Any resources that are allocated by this
+ * function must be allocated by kunit_* so that we don't need to explicitly
+ * free them.
+ */
+static struct kvm_vcpu *test_kvm_vcpu_init(struct kunit *test)
+{
+	struct kvm_vcpu *vcpu;
+	struct kvm *kvm;
+
+	kvm = kunit_kzalloc(test, sizeof(struct kvm), GFP_KERNEL);
+	if (!kvm)
+		return NULL;
+
+	vcpu = kunit_kzalloc(test, sizeof(struct kvm_vcpu), GFP_KERNEL);
+	if (!vcpu) {
+		kunit_kfree(test, kvm);
+		return NULL;
+	}
+
+	vcpu->cpu = -1;
+	vcpu->kvm = kvm;
+	vcpu->vcpu_id = 0;
+
+	return vcpu;
+}
+
+static void test_kvm_vcpu_fini(struct kunit *test, struct kvm_vcpu *vcpu)
+{
+	if (vcpu->kvm)
+		kunit_kfree(test, vcpu->kvm);
+
+	kunit_kfree(test, vcpu);
+}
+
+/* Test parameter information to test arm64_check_features */
+struct check_features_test {
+	u64	check_types;
+	u64	value;
+	u64	limit;
+	int	expected;
+};
+
+
+/* Used to define test parameters of vcpu_id_reg_feature_frac_check_test() */
+struct feat_info {
+	u32	id;
+	u32	shift;
+	u32	value;
+	u32	limit;
+};
+
+struct frac_check_test {
+	struct feat_info feat;
+	struct feat_info frac_feat;
+	int ret;
+};
+
+#define	FRAC_FEAT(id, shift, value, limit)	{id, shift, value, limit}
+
+/* Tests parameters of vcpu_id_reg_feature_frac_check_test() */
+struct frac_check_test frac_params[] = {
+	{
+		/*
+		 * The feature value is smaller than its limit.
+		 * Expect no error regardless of the frac value.
+		 */
+		FRAC_FEAT(SYS_ID_AA64PFR0_EL1, ID_AA64PFR0_RAS_SHIFT, 1, 2),
+		FRAC_FEAT(SYS_ID_AA64PFR1_EL1, ID_AA64PFR1_RASFRAC_SHIFT, 1, 1),
+		0,
+	},
+	{
+		/*
+		 * The feature value is smaller than its limit.
+		 * Expect no error regardless of the frac value.
+		 */
+		FRAC_FEAT(SYS_ID_AA64PFR0_EL1, ID_AA64PFR0_RAS_SHIFT, 1, 2),
+		FRAC_FEAT(SYS_ID_AA64PFR1_EL1, ID_AA64PFR1_RASFRAC_SHIFT, 1, 2),
+		0,
+	},
+	{
+		/*
+		 * The feature value is smaller than its limit.
+		 * Expect no error regardless of the frac value.
+		 */
+		FRAC_FEAT(SYS_ID_AA64PFR0_EL1, ID_AA64PFR0_RAS_SHIFT, 1, 2),
+		FRAC_FEAT(SYS_ID_AA64PFR1_EL1, ID_AA64PFR1_RASFRAC_SHIFT, 2, 1),
+		0,
+	},
+	{
+		/*
+		 * Both the feature and frac values are same as their limits.
+		 * Expect no error.
+		 */
+		FRAC_FEAT(SYS_ID_AA64PFR0_EL1, ID_AA64PFR0_RAS_SHIFT, 1, 1),
+		FRAC_FEAT(SYS_ID_AA64PFR1_EL1, ID_AA64PFR1_RASFRAC_SHIFT, 1, 1),
+		0,
+	},
+	{
+		/*
+		 * The feature value is same as its limit, and the frac value
+		 * is smaller than its limit. Expect no error.
+		 */
+		FRAC_FEAT(SYS_ID_AA64PFR0_EL1, ID_AA64PFR0_RAS_SHIFT, 1, 1),
+		FRAC_FEAT(SYS_ID_AA64PFR1_EL1, ID_AA64PFR1_RASFRAC_SHIFT, 1, 2),
+		0,
+	},
+	{
+		/*
+		 * The feature value is same as its limit, and the frac value
+		 * is larger than its limit. Expect an error.
+		 */
+		FRAC_FEAT(SYS_ID_AA64PFR0_EL1, ID_AA64PFR0_RAS_SHIFT, 1, 1),
+		FRAC_FEAT(SYS_ID_AA64PFR1_EL1, ID_AA64PFR1_RASFRAC_SHIFT, 2, 1),
+		-E2BIG,
+	},
+
+};
+
+static void frac_case_to_desc(struct frac_check_test *t, char *desc)
+{
+	struct feat_info *feat = &t->feat;
+	struct feat_info *frac = &t->frac_feat;
+
+	snprintf(desc, KUNIT_PARAM_DESC_SIZE,
+		 "feat - shift:%d, val:%d, lim:%d, frac - shift:%d, val:%d, lim:%d\n",
+		 feat->shift, feat->value, feat->limit,
+		 frac->shift, frac->value, frac->limit);
+}
+
+KUNIT_ARRAY_PARAM(frac, frac_params, frac_case_to_desc);
+
+/* Tests for vcpu_id_reg_feature_frac_check(). */
+static void vcpu_id_reg_feature_frac_check_test(struct kunit *test)
+{
+	struct kvm_vcpu *vcpu;
+	u32 id, frac_id;
+	struct id_reg_info id_data, frac_id_data;
+	struct id_reg_info *idr, *frac_idr;
+	struct feature_frac frac_data, *frac = &frac_data;
+	const struct frac_check_test *frct = test->param_value;
+
+	vcpu = test_kvm_vcpu_init(test);
+	KUNIT_ASSERT_TRUE(test, vcpu);
+
+	id = frct->feat.id;
+	frac_id = frct->frac_feat.id;
+
+	frac->id = id;
+	frac->shift = frct->feat.shift;
+	frac->frac_id = frac_id;
+	frac->frac_shift = frct->frac_feat.shift;
+
+	idr = GET_ID_REG_INFO(id);
+	frac_idr = GET_ID_REG_INFO(frac_id);
+
+	/* Save the original id_reg_info (and restore later) */
+	memcpy(&id_data, idr, sizeof(id_data));
+	memcpy(&frac_id_data, frac_idr, sizeof(frac_id_data));
+
+	/* The id could be same as the frac_id */
+	idr->vcpu_limit_val = (u64)frct->feat.limit << frac->shift;
+	frac_idr->vcpu_limit_val |=
+			(u64)frct->frac_feat.limit << frac->frac_shift;
+
+	write_kvm_id_reg(vcpu->kvm, id, (u64)frct->feat.value << frac->shift);
+	write_kvm_id_reg(vcpu->kvm, frac_id,
+			  (u64)frct->frac_feat.value << frac->frac_shift);
+
+	KUNIT_EXPECT_EQ(test,
+			vcpu_id_reg_feature_frac_check(vcpu, frac),
+			frct->ret);
+
+	/* Restore id_reg_info */
+	memcpy(idr, &id_data, sizeof(id_data));
+	memcpy(frac_idr, &frac_id_data, sizeof(frac_id_data));
+}
+
+/*
+ * Test parameter information to test validate_id_aa64mmfr0_tgran2
+ * and validate_id_aa64mmfr0_el1_test.
+ */
+struct tgran_test {
+	int gran2_field;
+	int gran2;
+	int gran2_lim;
+	int gran1;
+	int gran1_lim;
+	int ret;
+};
+
+/*
+ * Test parameters of validate_id_aa64mmfr0_tgran2_test() for TGran4_2.
+ * Defined values for the field are:
+ *  0x0: Support for 4KB granule at stage 2 is identified in TGran4.
+ *  0x1: 4KB granule not supported at stage 2.
+ *  0x2: 4KB granule supported at stage 2.
+ *  0x3: 4KB granule at stage 2 supports 52-bit input and output addresses.
+ *
+ * Defined values for the TGran4 are:
+ *  0x0: 4KB granule supported.
+ *  0x1: 4KB granule supports 52-bit input and output addresses.
+ *  0xf: 4KB granule not supported.
+ */
+struct tgran_test tgran4_2_test_params[] = {
+	/* Enable 4KB granule on the host that supports the granule */
+	{ID_AA64MMFR0_TGRAN4_2_SHIFT, 2, 2,  0,   0, 0},
+	/* Enable 4KB granule on the host that doesn't support the granule */
+	{ID_AA64MMFR0_TGRAN4_2_SHIFT, 2, 1,  0,   0, -E2BIG},
+	/* Disable 4KB granule on the host that supports the granule */
+	{ID_AA64MMFR0_TGRAN4_2_SHIFT, 1, 2,  0,   0, 0},
+	/* Enable 4KB granule on the host that supports the granule */
+	{ID_AA64MMFR0_TGRAN4_2_SHIFT, 0, 0,  0,   0, 0},
+	/* Disable 4KB granule */
+	{ID_AA64MMFR0_TGRAN4_2_SHIFT, 0, 1, 0xf,   0, 0},
+	/* Enable 4KB granule on the host that doesn't support the granule */
+	{ID_AA64MMFR0_TGRAN4_2_SHIFT, 0, 1,   0,   0, -E2BIG},
+	/* Disable 4KB granule */
+	{ID_AA64MMFR0_TGRAN4_2_SHIFT, 0, 2, 0xf,   0, 0},
+	/*
+	 * Enable 4KB granule with 52 bit address on the host that doesn't
+	 * support it.
+	 */
+	{ID_AA64MMFR0_TGRAN4_2_SHIFT, 0, 2,   1,   0, -E2BIG},
+	/* Disable 4KB granule */
+	{ID_AA64MMFR0_TGRAN4_2_SHIFT, 1, 0,   0, 0xf,  0},
+	/* Disable 4KB granule */
+	{ID_AA64MMFR0_TGRAN4_2_SHIFT, 1, 0,   0,   0,  0},
+	/* Enable 4KB granule on the host that doesn't support the granule */
+	{ID_AA64MMFR0_TGRAN4_2_SHIFT, 2, 0, 0xf, 0xf,  -E2BIG},
+	/* Enable 4KB granule on the host that supports the granule */
+	{ID_AA64MMFR0_TGRAN4_2_SHIFT, 2, 0,   0,   0,  0},
+};
+
+/*
+ * Test parameters of validate_id_aa64mmfr0_tgran2_test() for TGran64_2.
+ * Defined values for the field are:
+ *  0x0: Support for 64KB granule at stage 2 is identified in TGran64.
+ *  0x1: 64KB granule not supported at stage 2.
+ *  0x2: 64KB granule supported at stage 2.
+ *  0x3: 64KB granule at stage 2 supports 52-bit input and output addresses.
+ *
+ * Defined values for the TGran64 are:
+ *  0x0: 64KB granule supported.
+ *  0xf: 64KB granule not supported.
+ */
+struct tgran_test tgran64_2_test_params[] = {
+	/* Enable 64KB granule on the host that supports the granule */
+	{ID_AA64MMFR0_TGRAN64_2_SHIFT, 2, 2,   0,   0, 0},
+	/* Enable 64KB granule on the host that doesn't support the granule */
+	{ID_AA64MMFR0_TGRAN64_2_SHIFT, 2, 1,   0,   0, -E2BIG},
+	/* Enable 64KB granule on the host that supports the granule */
+	{ID_AA64MMFR0_TGRAN64_2_SHIFT, 1, 2,   0,   0, 0},
+	/* Enable 64KB granule on the host that supports the granule */
+	{ID_AA64MMFR0_TGRAN64_2_SHIFT, 0, 0,   0,   0, 0},
+	/* Disable 64KB granule */
+	{ID_AA64MMFR0_TGRAN64_2_SHIFT, 0, 1, 0xf,   0, 0},
+	/* Enable 64KB granule on the host that doesn't support the granule */
+	{ID_AA64MMFR0_TGRAN64_2_SHIFT, 0, 1,   0,   0, -E2BIG},
+	/* Disable 64KB granule */
+	{ID_AA64MMFR0_TGRAN64_2_SHIFT, 0, 2, 0xf,   0, 0},
+	/* Disable 64KB granule */
+	{ID_AA64MMFR0_TGRAN64_2_SHIFT, 1, 0,   0, 0xf, 0},
+	/* Disable 64KB granule */
+	{ID_AA64MMFR0_TGRAN64_2_SHIFT, 1, 0,   0,   0, 0},
+	/* Enable 64KB granule on the host that doesn't support the granule */
+	{ID_AA64MMFR0_TGRAN64_2_SHIFT, 2, 0, 0xf, 0xf, -E2BIG},
+	/* Enable 64KB granule on the host that supports the granule */
+	{ID_AA64MMFR0_TGRAN64_2_SHIFT, 2, 0,   0,   0, 0},
+};
+
+/*
+ * Test parameters of validate_id_aa64mmfr0_tgran2_test() for TGran16_2
+ * Defined values for the field are:
+ *  0x0: Support for 16KB granule at stage 2 is identified in TGran16.
+ *  0x1: 16KB granule not supported at stage 2.
+ *  0x2: 16KB granule supported at stage 2.
+ *  0x3: 16KB granule at stage 2 supports 52-bit input and output addresses.
+ *
+ * Defined values for the TGran16 are:
+ *  0x0: 16KB granule not supported.
+ *  0x1: 16KB granule supported.
+ *  0x2: 16KB granule supports 52-bit input and output addresses.
+ */
+struct tgran_test tgran16_2_test_params[] = {
+	/* Enable 16KB granule on the host that supports the granule */
+	{ID_AA64MMFR0_TGRAN16_2_SHIFT, 2, 2,  0,  0, 0},
+	/* Enable 16KB granule on the host that doesn't support the granule */
+	{ID_AA64MMFR0_TGRAN16_2_SHIFT, 2, 1,  0,  0, -E2BIG},
+	/* Disable 16KB granule on the host that supports the granule */
+	{ID_AA64MMFR0_TGRAN16_2_SHIFT, 1, 2,  0,  0, 0},
+	/* Disable 16KB granule */
+	{ID_AA64MMFR0_TGRAN16_2_SHIFT, 0, 0,  0,  0, 0},
+	/* Disable 16KB granule */
+	{ID_AA64MMFR0_TGRAN16_2_SHIFT, 0, 1,  0,  0, 0},
+	/* Enable 16KB granule on the host that doesn't support the granule */
+	{ID_AA64MMFR0_TGRAN16_2_SHIFT, 0, 1,  1,  0, -E2BIG},
+	/* Disable 16KB granule */
+	{ID_AA64MMFR0_TGRAN16_2_SHIFT, 0, 2,  0,  0, 0},
+	/*
+	 * Enable 16KB granule with 52 bit address on the host that doesn't
+	 * support it.
+	 */
+	{ID_AA64MMFR0_TGRAN16_2_SHIFT, 0, 2,  2,  2, -E2BIG},
+	/* Disable 16KB granule */
+	{ID_AA64MMFR0_TGRAN16_2_SHIFT, 1, 0,  0,  0, 0},
+	/* Disable 16KB granule */
+	{ID_AA64MMFR0_TGRAN16_2_SHIFT, 1, 0,  0,  1, 0},
+	/* Enable 16KB granule on the host that doesn't support the granule */
+	{ID_AA64MMFR0_TGRAN16_2_SHIFT, 2, 0,  0,  0, -E2BIG},
+	/* Enable 16KB granule on the host that supports the granule */
+	{ID_AA64MMFR0_TGRAN16_2_SHIFT, 2, 0,  0,  1, 0},
+	/* Enable 16KB granule on the host that supports the granule */
+	{ID_AA64MMFR0_TGRAN16_2_SHIFT, 2, 0,  0,  2, 0},
+};
+
+static void tgran2_case_to_desc(struct tgran_test *t, char *desc)
+{
+	snprintf(desc, KUNIT_PARAM_DESC_SIZE,
+		 "gran2(field=%d): val=%d, lim=%d gran1: val=%d limit=%d\n",
+		 t->gran2_field, t->gran2, t->gran2_lim,
+		 t->gran1, t->gran1_lim);
+}
+
+KUNIT_ARRAY_PARAM(tgran4_2, tgran4_2_test_params, tgran2_case_to_desc);
+KUNIT_ARRAY_PARAM(tgran64_2, tgran64_2_test_params, tgran2_case_to_desc);
+KUNIT_ARRAY_PARAM(tgran16_2, tgran16_2_test_params, tgran2_case_to_desc);
+
+#define	MAKE_MMFR0_TGRAN(shift1, gran1, shift2, gran2)		\
+	(((u64)((gran1) & 0xf) << (shift1)) |			\
+	 ((u64)((gran2) & 0xf) << (shift2)))
+
+/* Return the bit position of TGranX field for the given TGranX_2 field. */
+static int tgran2_to_tgran1_shift(int tgran2_shift)
+{
+	int tgran1_shift = -1;
+
+	switch (tgran2_shift) {
+	case ID_AA64MMFR0_TGRAN4_2_SHIFT:
+		tgran1_shift = ID_AA64MMFR0_TGRAN4_SHIFT;
+		break;
+	case ID_AA64MMFR0_TGRAN64_2_SHIFT:
+		tgran1_shift = ID_AA64MMFR0_TGRAN64_SHIFT;
+		break;
+	case ID_AA64MMFR0_TGRAN16_2_SHIFT:
+		tgran1_shift = ID_AA64MMFR0_TGRAN16_SHIFT;
+		break;
+	default:
+		break;
+	}
+
+	return tgran1_shift;
+}
+
+/* Tests for validate_id_aa64mmfr0_el1(). */
+static void validate_id_aa64mmfr0_tgran2_test(struct kunit *test)
+{
+	const struct tgran_test *t = test->param_value;
+	int shift1, shift2;
+	u64 v, lim;
+
+	shift2 = t->gran2_field;
+	shift1 = tgran2_to_tgran1_shift(shift2);
+	v = MAKE_MMFR0_TGRAN(shift1, t->gran1, shift2, t->gran2);
+	lim = MAKE_MMFR0_TGRAN(shift1, t->gran1_lim, shift2, t->gran2_lim);
+
+	KUNIT_EXPECT_EQ(test, aa64mmfr0_tgran2_check(shift2, v, lim), t->ret);
+}
+
+/* Tests for validate_id_aa64pfr0_el1(). */
+static void validate_id_aa64pfr0_el1_test(struct kunit *test)
+{
+	struct id_reg_info *id_reg;
+	struct kvm_vcpu *vcpu;
+	u64 v;
+
+	vcpu = test_kvm_vcpu_init(test);
+	KUNIT_ASSERT_TRUE(test, vcpu);
+
+	id_reg = GET_ID_REG_INFO(SYS_ID_AA64PFR0_EL1);
+
+	v = 0;
+	KUNIT_EXPECT_EQ(test, validate_id_aa64pfr0_el1(vcpu, id_reg, v), 0);
+
+	/*
+	 * Tests for GIC.
+	 * GIC must be 1 when vGIC3 is configured.
+	 */
+	v = 0x0000000;	/* GIC = 0 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64pfr0_el1(vcpu, id_reg, v), 0);
+
+	/* Test with VGIC_V2 */
+	vcpu->kvm->arch.vgic.in_kernel = true;
+	vcpu->kvm->arch.vgic.vgic_model = KVM_DEV_TYPE_ARM_VGIC_V2;
+
+	v = 0x0000000;	/* GIC = 0 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64pfr0_el1(vcpu, id_reg, v), 0);
+
+	/* Test with VGIC_V3 */
+	vcpu->kvm->arch.vgic.vgic_model = KVM_DEV_TYPE_ARM_VGIC_V3;
+
+	v = 0x0000000;	/* GIC = 0 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64pfr0_el1(vcpu, id_reg, v), 0);
+	v = 0x1000000;	/* GIC = 1 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64pfr0_el1(vcpu, id_reg, v), 0);
+
+	/* Restore the original VGIC state */
+	vcpu->kvm->arch.vgic.in_kernel = false;
+	vcpu->kvm->arch.vgic.vgic_model = 0;
+
+	/*
+	 * Tests for AdvSIMD/FP.
+	 * AdvSIMD must have the same value as FP.
+	 */
+
+	/* Tests with SVE disabled */
+	v = 0x000010000;	/* AdvSIMD = 0, FP = 1 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64pfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x000100000;	/* AdvSIMD = 1, FP = 0 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64pfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x000ff0000;	/* AdvSIMD = 0xf, FP = 0xf */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64pfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x100000000;	/* SVE =1, AdvSIMD = 0, FP = 0 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64pfr0_el1(vcpu, id_reg, v), 0);
+	if (!system_supports_sve()) {
+		kunit_skip(test, "No SVE support. Partial skip)");
+		/* Not reached */
+	}
+
+	/* Tests with SVE enabled */
+	vcpu->arch.flags |= KVM_ARM64_GUEST_HAS_SVE;
+
+	v = 0x100000000;	/* SVE =1, AdvSIMD = 0, FP = 0 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64pfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x100ff0000;	/* SVE =1, AdvSIMD = 0, FP = 0 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64pfr0_el1(vcpu, id_reg, v), 0);
+
+	vcpu->arch.flags &= ~KVM_ARM64_GUEST_HAS_SVE;
+}
+
+/* Tests for validate_id_aa64pfr1_el1() */
+static void validate_id_aa64pfr1_el1_test(struct kunit *test)
+{
+	struct id_reg_info *id_reg;
+	struct kvm_vcpu *vcpu;
+	u64 v;
+
+	vcpu = test_kvm_vcpu_init(test);
+	KUNIT_ASSERT_TRUE(test, vcpu);
+
+	id_reg = GET_ID_REG_INFO(SYS_ID_AA64PFR1_EL1);
+	v = 0;
+	KUNIT_EXPECT_EQ(test, validate_id_aa64pfr1_el1(vcpu, id_reg, v), 0);
+
+	/* Tests for MTE */
+
+	/* Tests with MTE disabled */
+	KUNIT_EXPECT_FALSE(test, vcpu->kvm->arch.mte_enabled);
+
+	v = 0x000;	/* MTE = 0 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64pfr1_el1(vcpu, id_reg, v), 0);
+
+	v = 0x100;	/* MTE = 1*/
+	KUNIT_EXPECT_NE(test, validate_id_aa64pfr1_el1(vcpu, id_reg, v), 0);
+
+	if (!system_supports_mte()) {
+		kunit_skip(test, "(No MTE support. Partial skip)");
+		/* Not reached */
+	}
+
+	/* Tests with MTE enabled */
+	vcpu->kvm->arch.mte_enabled = true;
+
+	v = 0x100;	/* MTE = 1*/
+	KUNIT_EXPECT_EQ(test, validate_id_aa64pfr1_el1(vcpu, id_reg, v), 0);
+
+	v = 0x0;	/* MTE = 0 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64pfr1_el1(vcpu, id_reg, v), 0);
+}
+
+/* Tests for validate_id_aa64isar0_el1(). */
+static void validate_id_aa64isar0_el1_test(struct kunit *test)
+{
+	struct id_reg_info *id_reg;
+	struct kvm_vcpu *vcpu;
+	u64 v;
+
+	vcpu = test_kvm_vcpu_init(test);
+	KUNIT_ASSERT_TRUE(test, vcpu);
+
+	id_reg = GET_ID_REG_INFO(SYS_ID_AA64ISAR0_EL1);
+
+	v = 0;
+	KUNIT_EXPECT_EQ(test, validate_id_aa64isar0_el1(vcpu, id_reg, v), 0);
+
+	/*
+	 * Tests for SM3/SM4.
+	 * Arm ARM says SM3 must have the same value as SM4.
+	 */
+
+	v = 0x01000000000;	/* SM4 = 0, SM3 = 1 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64isar0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x10000000000;	/* SM4 = 1, SM3 = 0 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64isar0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x11000000000;	/* SM3 = SM4 = 1 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64isar0_el1(vcpu, id_reg, v), 0);
+
+
+	/*
+	 * Tests for SHA1/SHA2/SHA3.  Arm ARM says:
+	 * If SHA1 is 0x0, both SHA2 and SHA3 must be 0x0.
+	 * If SHA2 is 0x0, SHA1 must be 0x0.
+	 * If SHA2 is 0x2, SHA3 must be 0x1.
+	 * If SHA3 is 0x1, SHA2 msut be 0x2.
+	 */
+
+	v = 0x000000100;	/* SHA2 = 0, SHA1 = 1 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64isar0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x000001000;	/* SHA2 = 1, SHA1 = 0 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64isar0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x000001100;	/* SHA2 = 1, SHA1 = 1 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64isar0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x100002000;	/* SHA3 = 1, SHA2 = 2 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64isar0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x000002000;	/* SHA3 = 0, SHA2 = 2 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64isar0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x100001000;	/* SHA3 = 1, SHA2 = 1 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64isar0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x200000000;	/* SHA3 = 2, SHA1 = 0 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64isar0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x200001100;	/* SHA3 = 2, SHA2= 1, SHA1 = 1 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64isar0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x300003300;	/* SHA3 = 3, SHA2 = 3, SHA1 = 3 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64isar0_el1(vcpu, id_reg, v), 0);
+}
+
+/* Tests for validate_id_aa64isar1_el1() */
+static void validate_id_aa64isar1_el1_test(struct kunit *test)
+{
+	struct id_reg_info *id_reg;
+	struct kvm_vcpu *vcpu;
+	u64 v;
+
+	vcpu = test_kvm_vcpu_init(test);
+	KUNIT_ASSERT_TRUE(test, vcpu);
+
+	id_reg = GET_ID_REG_INFO(SYS_ID_AA64ISAR1_EL1);
+
+	v = 0;
+	KUNIT_EXPECT_EQ(test, validate_id_aa64isar1_el1(vcpu, id_reg, v), 0);
+
+	/*
+	 * Tests for GPI/GPA/API/APA.
+	 * Arm ARM says:
+	 * If GPA is non-zero, GPI must be zero.
+	 * If GPI is non-zero, GPA must be zero.
+	 * If APA is non-zero, API must be zero.
+	 * If API is non-zero, APA must be zero.
+	 */
+
+	/* Tests with PTRAUTH disabled */
+	v = 0x11000110;	/* GPI = 1, GPA = 1, API = 1, APA = 1 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64isar1_el1(vcpu, id_reg, v), 0);
+
+	v = 0x11000100;	/* GPI = 1, GPA = 1, API = 1, APA = 0 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64isar1_el1(vcpu, id_reg, v), 0);
+
+	v = 0x11000010;	/* GPI = 1, GPA = 1, API = 0, APA = 1 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64isar1_el1(vcpu, id_reg, v), 0);
+
+	v = 0x10000110;	/* GPI = 1, GPA = 0, API = 1, APA = 1 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64isar1_el1(vcpu, id_reg, v), 0);
+
+	v = 0x01000110;	/* GPI = 0, GPA = 1, API = 1, APA = 1 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64isar1_el1(vcpu, id_reg, v), 0);
+
+	if (!system_has_full_ptr_auth()) {
+		kunit_skip(test, "(No PTRAUTH support. Partial skip)");
+		/* Not reached */
+	}
+
+	/* Tests with PTRAUTH enabled */
+	vcpu->arch.flags |= KVM_ARM64_GUEST_HAS_PTRAUTH;
+
+	v = 0x10000100;	/* GPI = 1, GPA = 0, API = 1, APA = 0 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64isar1_el1(vcpu, id_reg, v), 0);
+
+	v = 0x10000010;	/* GPI = 1, GPA = 0, API = 0, APA = 1 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64isar1_el1(vcpu, id_reg, v), 0);
+
+	v = 0x01000100;	/* GPI = 0, GPA = 1, API = 1, APA = 0 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64isar1_el1(vcpu, id_reg, v), 0);
+
+	v = 0x01000010;	/* GPI = 0, GPA = 1, API = 0, APA = 1 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64isar1_el1(vcpu, id_reg, v), 0);
+
+	v = 0;
+	KUNIT_EXPECT_NE(test, validate_id_aa64isar1_el1(vcpu, id_reg, v), 0);
+}
+
+/* Tests for validate_id_aa64mmfr0_el1() */
+static void validate_id_aa64mmfr0_el1_test(struct kunit *test)
+{
+	struct id_reg_info id_data, *id_reg;
+	const struct tgran_test *t4, *t64, *t16;
+	struct kvm_vcpu *vcpu;
+	int field4, field4_2, field64, field64_2, field16, field16_2;
+	u64 v, v4, lim4, v64, lim64, v16, lim16;
+	int i, j, ret;
+
+	id_reg = GET_ID_REG_INFO(SYS_ID_AA64MMFR0_EL1);
+
+	/* Save the original id_reg_info (and restore later) */
+	memcpy(&id_data, id_reg, sizeof(id_data));
+
+	vcpu = test_kvm_vcpu_init(test);
+
+	t4 = test->param_value;
+	field4_2 = t4->gran2_field;
+	field4 = tgran2_to_tgran1_shift(field4_2);
+	v4 = MAKE_MMFR0_TGRAN(field4, t4->gran1, field4_2, t4->gran2);
+	lim4 = MAKE_MMFR0_TGRAN(field4, t4->gran1_lim, field4_2, t4->gran2_lim);
+
+	/*
+	 * For each given gran4_2 params, test validate_id_aa64mmfr0_el1
+	 * with each of tgran64_2 and tgran16_2 params.
+	 */
+	for (i = 0; i < ARRAY_SIZE(tgran64_2_test_params); i++) {
+		t64 = &tgran64_2_test_params[i];
+		field64_2 = t64->gran2_field;
+		field64 = tgran2_to_tgran1_shift(field64_2);
+		v64 = MAKE_MMFR0_TGRAN(field64, t64->gran1,
+				       field64_2, t64->gran2);
+		lim64 = MAKE_MMFR0_TGRAN(field64, t64->gran1_lim,
+					 field64_2, t64->gran2_lim);
+
+		for (j = 0; j < ARRAY_SIZE(tgran16_2_test_params); j++) {
+			t16 = &tgran16_2_test_params[j];
+
+			field16_2 = t16->gran2_field;
+			field16 = tgran2_to_tgran1_shift(field16_2);
+			v16 = MAKE_MMFR0_TGRAN(field16, t16->gran1,
+					       field16_2, t16->gran2);
+			lim16 = MAKE_MMFR0_TGRAN(field16, t16->gran1_lim,
+						 field16_2, t16->gran2_lim);
+
+			/* Build id_aa64mmfr0_el1 from tgran16/64/4 values */
+			v = v16 | v64 | v4;
+			id_reg->vcpu_limit_val = lim16 | lim64 | lim4;
+
+			ret = t4->ret ? t4->ret : t64->ret;
+			ret = ret ? ret : t16->ret;
+			KUNIT_EXPECT_EQ(test,
+				validate_id_aa64mmfr0_el1(vcpu, id_reg, v),
+				ret);
+		}
+	}
+
+	/* Restore id_reg_info */
+	memcpy(id_reg, &id_data, sizeof(id_data));
+}
+
+/* Tests for validate_id_aa64dfr0_el1() */
+static void validate_id_aa64dfr0_el1_test(struct kunit *test)
+{
+	struct id_reg_info *id_reg;
+	struct kvm_vcpu *vcpu;
+	u64 v;
+
+	id_reg = GET_ID_REG_INFO(SYS_ID_AA64DFR0_EL1);
+	vcpu = test_kvm_vcpu_init(test);
+	KUNIT_ASSERT_TRUE(test, vcpu);
+
+	v = 0;
+	KUNIT_EXPECT_EQ(test, validate_id_aa64dfr0_el1(vcpu, id_reg, v), 0);
+
+	/*
+	 * Tests for CTX_CMPS/BRPS.
+	 * Number of context-aware breakpoints can be no more than number
+	 * of supported breakpoints.
+	 */
+	v = 0x10001000;	/* CTX_CMPS = 1, BRPS = 1 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64dfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x20001000;	/* CTX_CMPS = 2, BRPS = 1 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64dfr0_el1(vcpu, id_reg, v), 0);
+
+	/* Tests for PMUVer */
+
+	/* Tests with PMUv3 disabled. */
+
+	v = 0x000;	/* PMUVER = 0x0 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64dfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0xf00;	/* PMUVER = 0xf */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64dfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x100;	/* PMUVER = 1 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64dfr0_el1(vcpu, id_reg, v), 0);
+
+	/* Tests with PMUv3 enabled */
+	set_bit(KVM_ARM_VCPU_PMU_V3, vcpu->arch.features);
+
+	v = 0x000;	/* PMUVER = 0x0 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64dfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x000;	/* PMUVER = 0xf */
+	KUNIT_EXPECT_NE(test, validate_id_aa64dfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x100;	/* PMUVER = 1 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64dfr0_el1(vcpu, id_reg, v), 0);
+}
+
+/* Tests for validate_id_dfr0_el1() */
+static void validate_id_dfr0_el1_test(struct kunit *test)
+{
+	struct id_reg_info *id_reg;
+	struct kvm_vcpu *vcpu;
+	u64 v;
+
+	id_reg = GET_ID_REG_INFO(SYS_ID_DFR0_EL1);
+	vcpu = test_kvm_vcpu_init(test);
+	KUNIT_ASSERT_TRUE(test, vcpu);
+
+	v = 0;
+	KUNIT_EXPECT_EQ(test, validate_id_dfr0_el1(vcpu, id_reg, v), 0);
+
+	/* Tests for PERFMON */
+
+	/* Tests with PMUv3 disabled */
+
+	v = 0x0000000;	/* PERFMON = 0x0 */
+	KUNIT_EXPECT_EQ(test, validate_id_dfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0xf000000;	/* PERFMON = 0xf */
+	KUNIT_EXPECT_EQ(test, validate_id_dfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x1000000;	/* PERFMON = 1 */
+	KUNIT_EXPECT_NE(test, validate_id_dfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x2000000;	/* PERFMON = 2 */
+	KUNIT_EXPECT_NE(test, validate_id_dfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x3000000;	/* PERFMON = 3 */
+	KUNIT_EXPECT_NE(test, validate_id_dfr0_el1(vcpu, id_reg, v), 0);
+
+
+	/* Tests with PMUv3 enabled */
+	set_bit(KVM_ARM_VCPU_PMU_V3, vcpu->arch.features);
+
+	v = 0x0000000;	/* PERFMON = 0x0 */
+	KUNIT_EXPECT_NE(test, validate_id_dfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0xf000000;	/* PERFMON = 0xf */
+	KUNIT_EXPECT_NE(test, validate_id_dfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x1000000;	/* PERFMON = 1 */
+	KUNIT_EXPECT_NE(test, validate_id_dfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x2000000;	/* PERFMON = 2 */
+	KUNIT_EXPECT_NE(test, validate_id_dfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x3000000;	/* PERFMON = 3 */
+	KUNIT_EXPECT_EQ(test, validate_id_dfr0_el1(vcpu, id_reg, v), 0);
+}
+
+/* Tests for validate_mvfr1_el1(). */
+static void validate_mvfr1_el1_test(struct kunit *test)
+{
+	struct id_reg_info *id_reg;
+	struct kvm_vcpu *vcpu;
+	u64 v;
+
+	id_reg = GET_ID_REG_INFO(SYS_MVFR1_EL1);
+	vcpu = test_kvm_vcpu_init(test);
+	KUNIT_ASSERT_TRUE(test, vcpu);
+
+	v = 0;
+	KUNIT_EXPECT_EQ(test, validate_mvfr1_el1(vcpu, id_reg, v), 0);
+
+	/*
+	 * Tests for FPHP/SIMDHP.
+	 * Arm ARM says the level of support indicated by FPHP must be
+	 * equivalent to the level of support indicated by the SIMDHP,
+	 * meaning the permitted values are:
+	 * FPHP = 0x0, SIMDHP = 0x0
+	 * FPHP = 0x2, SIMDHP = 0x1
+	 * FPHP = 0x3, SIMDHP = 0x2
+	 */
+	v = 0x0000000;	/* FPHP = 0, SIMDHP = 0 */
+	KUNIT_EXPECT_EQ(test, validate_mvfr1_el1(vcpu, id_reg, v), 0);
+
+	v = 0x2100000;	/* FPHP = 2, SIMDHP = 1 */
+	KUNIT_EXPECT_EQ(test, validate_mvfr1_el1(vcpu, id_reg, v), 0);
+
+	v = 0x3200000;	/* FPHP = 3, SIMDHP = 2 */
+	KUNIT_EXPECT_EQ(test, validate_mvfr1_el1(vcpu, id_reg, v), 0);
+
+	v = 0x1100000;	/* FPHP = 1, SIMDHP = 1 */
+	KUNIT_EXPECT_NE(test, validate_mvfr1_el1(vcpu, id_reg, v), 0);
+
+	v = 0x2200000;	/* FPHP = 2, SIMDHP = 2 */
+	KUNIT_EXPECT_NE(test, validate_mvfr1_el1(vcpu, id_reg, v), 0);
+
+	v = 0x3300000;	/* FPHP = 3, SIMDHP = 3 */
+	KUNIT_EXPECT_NE(test, validate_mvfr1_el1(vcpu, id_reg, v), 0);
+
+	v = (u64)-1;
+	KUNIT_EXPECT_NE(test, validate_mvfr1_el1(vcpu, id_reg, v), 0);
+}
+
+/*
+ * Helper function for validate_id_reg_test().
+ * We don't use KUNIT_ASSERT or kunit_skip because this is a helper test
+ * function and we are not sure if it's safe to exist from the test case.
+ */
+static void validate_id_reg_test_one_field(struct kunit *test,
+		u32 id, int pos, int fval, int flimit,
+		bool is_signed, struct id_reg_info *idr)
+{
+	struct kvm_vcpu *vcpu;
+	int fmin = is_signed ? -1 : 0;
+	int fmax = is_signed ? 7 : 15;
+	u64 fmask = ARM64_FEATURE_FIELD_MASK;
+	u64 val;
+
+	vcpu = test_kvm_vcpu_init(test);
+	KUNIT_ASSERT_TRUE(test, vcpu);
+
+	if (flimit > fmax) {
+		/* Shouldn't happen. Make the test failure. */
+		KUNIT_EXPECT_FALSE(test, flimit > fmax);
+		kunit_err(test, "%s: flimit(%d) > fmax(%d). Must be test bug",
+			  __func__, flimit, fmax);
+		return;
+	}
+
+	if (fval > fmin) {
+		/* Set the field to a smaller value */
+		val = ((u64)(fval - 1) & fmask) << pos;
+		KUNIT_EXPECT_EQ(test, validate_id_reg(vcpu, id, val), 0);
+	}
+
+	if (fval < flimit) {
+		/* Set the field to a larger value, but smaller than flimit */
+		val = ((u64)(fval + 1) & fmask) << pos;
+		KUNIT_EXPECT_EQ(test, validate_id_reg(vcpu, id, val), 0);
+		/* Set the field to the flimit */
+		val = ((u64)flimit & fmask) << pos;
+		KUNIT_EXPECT_EQ(test, validate_id_reg(vcpu, id, val), 0);
+	}
+
+	if (flimit < fmax) {
+		/* Set the field to a larger value than flimit */
+		val = ((u64)(flimit + 1) & fmask) << pos;
+		KUNIT_EXPECT_NE(test, validate_id_reg(vcpu, id, val), 0);
+
+		/* Test with ignore_mask */
+		if (idr) {
+			idr->ignore_mask = fmask << pos;
+			KUNIT_EXPECT_EQ(test,
+					validate_id_reg(vcpu, id, val),
+					0);
+		}
+	}
+	test_kvm_vcpu_fini(test, vcpu);
+}
+
+/*
+ * Test for validate_id_reg().
+ */
+static void validate_id_reg_test(struct kunit *test)
+{
+	struct id_reg_info idr_data, *idr, *original_idr;
+	u32 id;
+	int fval, flim, pos;
+	u64 val;
+	bool sign;
+
+	/* Use AA64PFR0_EL1 because it includes both sign/unsigned fields */
+	id = SYS_ID_AA64PFR0_EL1;
+
+	/* Save the original id_reg_info */
+	original_idr = GET_ID_REG_INFO(id);
+
+	/* Test with a temporary id_reg_info for testing */
+	idr = &idr_data;
+	GET_ID_REG_INFO(id) = idr;
+
+	fval = 0x1;
+	flim = 0x2;
+
+	/* Test with unsigned field */
+	pos = ID_AA64PFR0_RAS_SHIFT;
+
+	/* Set up id_reg_info for testing */
+	memset(idr, 0, sizeof(*idr));
+	idr->sys_reg = id;
+	idr->vcpu_limit_val = (u64)flim << pos;
+	validate_id_reg_test_one_field(test, id, pos, fval, flim, false, idr);
+
+	/* Test with signed field */
+	pos = ID_AA64PFR0_FP_SHIFT;
+
+	/* Set up id_reg_info for testing */
+	memset(idr, 0, sizeof(*idr));
+	idr->sys_reg = id;
+	idr->vcpu_limit_val = (u64)flim << pos;
+	validate_id_reg_test_one_field(test, id, pos, fval, flim, true, idr);
+
+
+	/* Test without id_reg_info */
+	GET_ID_REG_INFO(id) = NULL;
+	if (original_idr)
+		val = original_idr->vcpu_limit_val;
+	else
+		val = read_sanitised_ftr_reg(id);
+
+	for (pos = 0; pos < 64; pos += 4) {
+		if (pos == ID_AA64PFR0_FP_SHIFT ||
+		    pos == ID_AA64PFR0_ASIMD_SHIFT)
+			sign = true;
+		else
+			sign = false;
+
+		fval = cpuid_feature_extract_field(val, pos, sign);
+		validate_id_reg_test_one_field(test, id, pos, fval, fval,
+					       sign, NULL);
+	}
+
+	/* Restore the original id_reg_info */
+	GET_ID_REG_INFO(id) = original_idr;
+}
+
+static struct kunit_case kvm_sys_regs_test_cases[] = {
+	KUNIT_CASE_PARAM(vcpu_id_reg_feature_frac_check_test, frac_gen_params),
+	KUNIT_CASE_PARAM(validate_id_aa64mmfr0_tgran2_test, tgran4_2_gen_params),
+	KUNIT_CASE_PARAM(validate_id_aa64mmfr0_tgran2_test, tgran64_2_gen_params),
+	KUNIT_CASE_PARAM(validate_id_aa64mmfr0_tgran2_test, tgran16_2_gen_params),
+	KUNIT_CASE(validate_id_aa64pfr0_el1_test),
+	KUNIT_CASE(validate_id_aa64pfr1_el1_test),
+	KUNIT_CASE(validate_id_aa64isar0_el1_test),
+	KUNIT_CASE(validate_id_aa64isar1_el1_test),
+	KUNIT_CASE_PARAM(validate_id_aa64mmfr0_el1_test, tgran4_2_gen_params),
+	KUNIT_CASE(validate_id_aa64dfr0_el1_test),
+	KUNIT_CASE(validate_id_dfr0_el1_test),
+	KUNIT_CASE(validate_mvfr1_el1_test),
+	KUNIT_CASE(validate_id_reg_test),
+	{}
+};
+
+static struct kunit_suite kvm_sys_regs_test_suite = {
+	.name = "kvm-sys-regs-test-suite",
+	.test_cases = kvm_sys_regs_test_cases,
+};
+
+kunit_test_suites(&kvm_sys_regs_test_suite);
+MODULE_LICENSE("GPL");
-- 
2.34.1.448.ga2b2bfdf31-goog

_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 16/26] KVM: arm64: Add kunit test for ID register validation
@ 2022-01-06  4:26   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

Add kunit tests for functions that are used for validation of ID
registers, CONFIG_KVM_KUNIT_TEST option to enable the tests, and
.kunitconfig to run the kunit tests.

One line change below is needed in the default arm64.py to fully
run all of those kunit tests.
-----------------------------------------------------------------------
$ diff tools/testing/kunit/qemu_configs/arm64.py arm64_kvm_min.py
12c12
< 			   extra_qemu_params=['-machine virt', '-cpu cortex-a57'])
---
> 			   extra_qemu_params=['-M virt,virtualization=on,mte=on', '-cpu max,sve=on'])
-----------------------------------------------------------------------

The outputs from the tests are:
-----------------------------------------------------------------------
$ tools/testing/kunit/kunit.py run --timeout=60 --jobs=`nproc --all` \
          --arch=arm64 --cross_compile=aarch64-linux-gnu- \
          --qemu_config arm64_kvm_min.py \
          --kunitconfig=arch/arm64/kvm/.kunitconfig
[12:14:24] Configuring KUnit Kernel ...
[12:14:24] Building KUnit Kernel ...
Populating config with:
$ make ARCH=arm64 olddefconfig CROSS_COMPILE=aarch64-linux-gnu- O=.kunit
Building with:
$ make ARCH=arm64 --jobs=96 CROSS_COMPILE=aarch64-linux-gnu- O=.kunit
[12:14:31] Starting KUnit Kernel (1/1)...
[12:14:31] ============================================================
Running tests with:
$ qemu-system-aarch64 -nodefaults -m 1024 -kernel .kunit/arch/arm64/boot/Image.gz -append 'mem=1G console=tty kunit_shutdown=halt console=ttyAMA0 kunit_shutdown=reboot' -no-reboot -nographic -serial stdio -M virt,virtualization=on,mte=on -cpu max,sve=on
[12:14:32] ========== kvm-sys-regs-test-suite (13 subtests) ===========
[12:14:32] [PASSED] vcpu_id_reg_feature_frac_check_test
[12:14:32] [PASSED] validate_id_aa64mmfr0_tgran2_test
[12:14:32] [PASSED] validate_id_aa64mmfr0_tgran2_test
[12:14:32] [PASSED] validate_id_aa64mmfr0_tgran2_test
[12:14:32] [PASSED] validate_id_aa64pfr0_el1_test
[12:14:32] [PASSED] validate_id_aa64pfr1_el1_test
[12:14:32] [PASSED] validate_id_aa64isar0_el1_test
[12:14:32] [PASSED] validate_id_aa64isar1_el1_test
[12:14:32] [PASSED] validate_id_aa64mmfr0_el1_test
[12:14:32] [PASSED] validate_id_aa64dfr0_el1_test
[12:14:32] [PASSED] validate_id_dfr0_el1_test
[12:14:32] [PASSED] validate_mvfr1_el1_test
[12:14:32] [PASSED] validate_id_reg_test
[12:14:32] ============= [PASSED] kvm-sys-regs-test-suite =============
[12:14:32] ============================================================
[12:14:32] Testing complete. Passed: 13, Failed: 0, Crashed: 0, Skipped: 0, Errors: 0
[12:14:32] Elapsed time: 8.223s total, 0.002s configuring, 6.655s building, 1.565s running
-----------------------------------------------------------------------

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/kvm/.kunitconfig    |   4 +
 arch/arm64/kvm/Kconfig         |  11 +
 arch/arm64/kvm/sys_regs.c      |   4 +
 arch/arm64/kvm/sys_regs_test.c | 985 +++++++++++++++++++++++++++++++++
 4 files changed, 1004 insertions(+)
 create mode 100644 arch/arm64/kvm/.kunitconfig
 create mode 100644 arch/arm64/kvm/sys_regs_test.c

diff --git a/arch/arm64/kvm/.kunitconfig b/arch/arm64/kvm/.kunitconfig
new file mode 100644
index 000000000000..c564c98fc319
--- /dev/null
+++ b/arch/arm64/kvm/.kunitconfig
@@ -0,0 +1,4 @@
+CONFIG_KUNIT=y
+CONFIG_VIRTUALIZATION=y
+CONFIG_KVM=y
+CONFIG_KVM_KUNIT_TEST=y
diff --git a/arch/arm64/kvm/Kconfig b/arch/arm64/kvm/Kconfig
index 8ffcbe29395e..48fbdd17b2fd 100644
--- a/arch/arm64/kvm/Kconfig
+++ b/arch/arm64/kvm/Kconfig
@@ -54,4 +54,15 @@ config NVHE_EL2_DEBUG
 
 	  If unsure, say N.
 
+config KVM_KUNIT_TEST
+	bool "KUnit tests for KVM on ARM64 processors" if !KUNIT_ALL_TESTS
+	depends on KVM && KUNIT
+	default KUNIT_ALL_TESTS
+	help
+	  Say Y here to enable KUnit tests for the KVM on ARM64.
+	  Only useful for KVM/ARM development and are not for inclusion into
+	  a production build.
+
+	  If unsure, say N.
+
 endif # VIRTUALIZATION
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 6adb7b04620c..d5bd04c68cd4 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -3636,3 +3636,7 @@ int kvm_set_id_reg_feature(struct kvm *kvm, u32 id, u8 field_shift, u8 fval)
 
 	return __modify_kvm_id_reg(kvm, id, val, preserve_mask);
 }
+
+#if IS_ENABLED(CONFIG_KVM_KUNIT_TEST)
+#include "sys_regs_test.c"
+#endif
diff --git a/arch/arm64/kvm/sys_regs_test.c b/arch/arm64/kvm/sys_regs_test.c
new file mode 100644
index 000000000000..5b1b95bd9ec4
--- /dev/null
+++ b/arch/arm64/kvm/sys_regs_test.c
@@ -0,0 +1,985 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * KUnit tests for arch/arm64/kvm/sys_regs.c.
+ */
+
+#include <linux/module.h>
+#include <kunit/test.h>
+#include <kunit/test.h>
+#include <linux/kvm_host.h>
+#include <asm/cpufeature.h>
+#include "asm/sysreg.h"
+
+/*
+ * Create a vcpu with the minimum fields required for testing in this file
+ * including the struct kvm.  Any resources that are allocated by this
+ * function must be allocated by kunit_* so that we don't need to explicitly
+ * free them.
+ */
+static struct kvm_vcpu *test_kvm_vcpu_init(struct kunit *test)
+{
+	struct kvm_vcpu *vcpu;
+	struct kvm *kvm;
+
+	kvm = kunit_kzalloc(test, sizeof(struct kvm), GFP_KERNEL);
+	if (!kvm)
+		return NULL;
+
+	vcpu = kunit_kzalloc(test, sizeof(struct kvm_vcpu), GFP_KERNEL);
+	if (!vcpu) {
+		kunit_kfree(test, kvm);
+		return NULL;
+	}
+
+	vcpu->cpu = -1;
+	vcpu->kvm = kvm;
+	vcpu->vcpu_id = 0;
+
+	return vcpu;
+}
+
+static void test_kvm_vcpu_fini(struct kunit *test, struct kvm_vcpu *vcpu)
+{
+	if (vcpu->kvm)
+		kunit_kfree(test, vcpu->kvm);
+
+	kunit_kfree(test, vcpu);
+}
+
+/* Test parameter information to test arm64_check_features */
+struct check_features_test {
+	u64	check_types;
+	u64	value;
+	u64	limit;
+	int	expected;
+};
+
+
+/* Used to define test parameters of vcpu_id_reg_feature_frac_check_test() */
+struct feat_info {
+	u32	id;
+	u32	shift;
+	u32	value;
+	u32	limit;
+};
+
+struct frac_check_test {
+	struct feat_info feat;
+	struct feat_info frac_feat;
+	int ret;
+};
+
+#define	FRAC_FEAT(id, shift, value, limit)	{id, shift, value, limit}
+
+/* Tests parameters of vcpu_id_reg_feature_frac_check_test() */
+struct frac_check_test frac_params[] = {
+	{
+		/*
+		 * The feature value is smaller than its limit.
+		 * Expect no error regardless of the frac value.
+		 */
+		FRAC_FEAT(SYS_ID_AA64PFR0_EL1, ID_AA64PFR0_RAS_SHIFT, 1, 2),
+		FRAC_FEAT(SYS_ID_AA64PFR1_EL1, ID_AA64PFR1_RASFRAC_SHIFT, 1, 1),
+		0,
+	},
+	{
+		/*
+		 * The feature value is smaller than its limit.
+		 * Expect no error regardless of the frac value.
+		 */
+		FRAC_FEAT(SYS_ID_AA64PFR0_EL1, ID_AA64PFR0_RAS_SHIFT, 1, 2),
+		FRAC_FEAT(SYS_ID_AA64PFR1_EL1, ID_AA64PFR1_RASFRAC_SHIFT, 1, 2),
+		0,
+	},
+	{
+		/*
+		 * The feature value is smaller than its limit.
+		 * Expect no error regardless of the frac value.
+		 */
+		FRAC_FEAT(SYS_ID_AA64PFR0_EL1, ID_AA64PFR0_RAS_SHIFT, 1, 2),
+		FRAC_FEAT(SYS_ID_AA64PFR1_EL1, ID_AA64PFR1_RASFRAC_SHIFT, 2, 1),
+		0,
+	},
+	{
+		/*
+		 * Both the feature and frac values are same as their limits.
+		 * Expect no error.
+		 */
+		FRAC_FEAT(SYS_ID_AA64PFR0_EL1, ID_AA64PFR0_RAS_SHIFT, 1, 1),
+		FRAC_FEAT(SYS_ID_AA64PFR1_EL1, ID_AA64PFR1_RASFRAC_SHIFT, 1, 1),
+		0,
+	},
+	{
+		/*
+		 * The feature value is same as its limit, and the frac value
+		 * is smaller than its limit. Expect no error.
+		 */
+		FRAC_FEAT(SYS_ID_AA64PFR0_EL1, ID_AA64PFR0_RAS_SHIFT, 1, 1),
+		FRAC_FEAT(SYS_ID_AA64PFR1_EL1, ID_AA64PFR1_RASFRAC_SHIFT, 1, 2),
+		0,
+	},
+	{
+		/*
+		 * The feature value is same as its limit, and the frac value
+		 * is larger than its limit. Expect an error.
+		 */
+		FRAC_FEAT(SYS_ID_AA64PFR0_EL1, ID_AA64PFR0_RAS_SHIFT, 1, 1),
+		FRAC_FEAT(SYS_ID_AA64PFR1_EL1, ID_AA64PFR1_RASFRAC_SHIFT, 2, 1),
+		-E2BIG,
+	},
+
+};
+
+static void frac_case_to_desc(struct frac_check_test *t, char *desc)
+{
+	struct feat_info *feat = &t->feat;
+	struct feat_info *frac = &t->frac_feat;
+
+	snprintf(desc, KUNIT_PARAM_DESC_SIZE,
+		 "feat - shift:%d, val:%d, lim:%d, frac - shift:%d, val:%d, lim:%d\n",
+		 feat->shift, feat->value, feat->limit,
+		 frac->shift, frac->value, frac->limit);
+}
+
+KUNIT_ARRAY_PARAM(frac, frac_params, frac_case_to_desc);
+
+/* Tests for vcpu_id_reg_feature_frac_check(). */
+static void vcpu_id_reg_feature_frac_check_test(struct kunit *test)
+{
+	struct kvm_vcpu *vcpu;
+	u32 id, frac_id;
+	struct id_reg_info id_data, frac_id_data;
+	struct id_reg_info *idr, *frac_idr;
+	struct feature_frac frac_data, *frac = &frac_data;
+	const struct frac_check_test *frct = test->param_value;
+
+	vcpu = test_kvm_vcpu_init(test);
+	KUNIT_ASSERT_TRUE(test, vcpu);
+
+	id = frct->feat.id;
+	frac_id = frct->frac_feat.id;
+
+	frac->id = id;
+	frac->shift = frct->feat.shift;
+	frac->frac_id = frac_id;
+	frac->frac_shift = frct->frac_feat.shift;
+
+	idr = GET_ID_REG_INFO(id);
+	frac_idr = GET_ID_REG_INFO(frac_id);
+
+	/* Save the original id_reg_info (and restore later) */
+	memcpy(&id_data, idr, sizeof(id_data));
+	memcpy(&frac_id_data, frac_idr, sizeof(frac_id_data));
+
+	/* The id could be same as the frac_id */
+	idr->vcpu_limit_val = (u64)frct->feat.limit << frac->shift;
+	frac_idr->vcpu_limit_val |=
+			(u64)frct->frac_feat.limit << frac->frac_shift;
+
+	write_kvm_id_reg(vcpu->kvm, id, (u64)frct->feat.value << frac->shift);
+	write_kvm_id_reg(vcpu->kvm, frac_id,
+			  (u64)frct->frac_feat.value << frac->frac_shift);
+
+	KUNIT_EXPECT_EQ(test,
+			vcpu_id_reg_feature_frac_check(vcpu, frac),
+			frct->ret);
+
+	/* Restore id_reg_info */
+	memcpy(idr, &id_data, sizeof(id_data));
+	memcpy(frac_idr, &frac_id_data, sizeof(frac_id_data));
+}
+
+/*
+ * Test parameter information to test validate_id_aa64mmfr0_tgran2
+ * and validate_id_aa64mmfr0_el1_test.
+ */
+struct tgran_test {
+	int gran2_field;
+	int gran2;
+	int gran2_lim;
+	int gran1;
+	int gran1_lim;
+	int ret;
+};
+
+/*
+ * Test parameters of validate_id_aa64mmfr0_tgran2_test() for TGran4_2.
+ * Defined values for the field are:
+ *  0x0: Support for 4KB granule at stage 2 is identified in TGran4.
+ *  0x1: 4KB granule not supported at stage 2.
+ *  0x2: 4KB granule supported at stage 2.
+ *  0x3: 4KB granule at stage 2 supports 52-bit input and output addresses.
+ *
+ * Defined values for the TGran4 are:
+ *  0x0: 4KB granule supported.
+ *  0x1: 4KB granule supports 52-bit input and output addresses.
+ *  0xf: 4KB granule not supported.
+ */
+struct tgran_test tgran4_2_test_params[] = {
+	/* Enable 4KB granule on the host that supports the granule */
+	{ID_AA64MMFR0_TGRAN4_2_SHIFT, 2, 2,  0,   0, 0},
+	/* Enable 4KB granule on the host that doesn't support the granule */
+	{ID_AA64MMFR0_TGRAN4_2_SHIFT, 2, 1,  0,   0, -E2BIG},
+	/* Disable 4KB granule on the host that supports the granule */
+	{ID_AA64MMFR0_TGRAN4_2_SHIFT, 1, 2,  0,   0, 0},
+	/* Enable 4KB granule on the host that supports the granule */
+	{ID_AA64MMFR0_TGRAN4_2_SHIFT, 0, 0,  0,   0, 0},
+	/* Disable 4KB granule */
+	{ID_AA64MMFR0_TGRAN4_2_SHIFT, 0, 1, 0xf,   0, 0},
+	/* Enable 4KB granule on the host that doesn't support the granule */
+	{ID_AA64MMFR0_TGRAN4_2_SHIFT, 0, 1,   0,   0, -E2BIG},
+	/* Disable 4KB granule */
+	{ID_AA64MMFR0_TGRAN4_2_SHIFT, 0, 2, 0xf,   0, 0},
+	/*
+	 * Enable 4KB granule with 52 bit address on the host that doesn't
+	 * support it.
+	 */
+	{ID_AA64MMFR0_TGRAN4_2_SHIFT, 0, 2,   1,   0, -E2BIG},
+	/* Disable 4KB granule */
+	{ID_AA64MMFR0_TGRAN4_2_SHIFT, 1, 0,   0, 0xf,  0},
+	/* Disable 4KB granule */
+	{ID_AA64MMFR0_TGRAN4_2_SHIFT, 1, 0,   0,   0,  0},
+	/* Enable 4KB granule on the host that doesn't support the granule */
+	{ID_AA64MMFR0_TGRAN4_2_SHIFT, 2, 0, 0xf, 0xf,  -E2BIG},
+	/* Enable 4KB granule on the host that supports the granule */
+	{ID_AA64MMFR0_TGRAN4_2_SHIFT, 2, 0,   0,   0,  0},
+};
+
+/*
+ * Test parameters of validate_id_aa64mmfr0_tgran2_test() for TGran64_2.
+ * Defined values for the field are:
+ *  0x0: Support for 64KB granule at stage 2 is identified in TGran64.
+ *  0x1: 64KB granule not supported at stage 2.
+ *  0x2: 64KB granule supported at stage 2.
+ *  0x3: 64KB granule at stage 2 supports 52-bit input and output addresses.
+ *
+ * Defined values for the TGran64 are:
+ *  0x0: 64KB granule supported.
+ *  0xf: 64KB granule not supported.
+ */
+struct tgran_test tgran64_2_test_params[] = {
+	/* Enable 64KB granule on the host that supports the granule */
+	{ID_AA64MMFR0_TGRAN64_2_SHIFT, 2, 2,   0,   0, 0},
+	/* Enable 64KB granule on the host that doesn't support the granule */
+	{ID_AA64MMFR0_TGRAN64_2_SHIFT, 2, 1,   0,   0, -E2BIG},
+	/* Enable 64KB granule on the host that supports the granule */
+	{ID_AA64MMFR0_TGRAN64_2_SHIFT, 1, 2,   0,   0, 0},
+	/* Enable 64KB granule on the host that supports the granule */
+	{ID_AA64MMFR0_TGRAN64_2_SHIFT, 0, 0,   0,   0, 0},
+	/* Disable 64KB granule */
+	{ID_AA64MMFR0_TGRAN64_2_SHIFT, 0, 1, 0xf,   0, 0},
+	/* Enable 64KB granule on the host that doesn't support the granule */
+	{ID_AA64MMFR0_TGRAN64_2_SHIFT, 0, 1,   0,   0, -E2BIG},
+	/* Disable 64KB granule */
+	{ID_AA64MMFR0_TGRAN64_2_SHIFT, 0, 2, 0xf,   0, 0},
+	/* Disable 64KB granule */
+	{ID_AA64MMFR0_TGRAN64_2_SHIFT, 1, 0,   0, 0xf, 0},
+	/* Disable 64KB granule */
+	{ID_AA64MMFR0_TGRAN64_2_SHIFT, 1, 0,   0,   0, 0},
+	/* Enable 64KB granule on the host that doesn't support the granule */
+	{ID_AA64MMFR0_TGRAN64_2_SHIFT, 2, 0, 0xf, 0xf, -E2BIG},
+	/* Enable 64KB granule on the host that supports the granule */
+	{ID_AA64MMFR0_TGRAN64_2_SHIFT, 2, 0,   0,   0, 0},
+};
+
+/*
+ * Test parameters of validate_id_aa64mmfr0_tgran2_test() for TGran16_2
+ * Defined values for the field are:
+ *  0x0: Support for 16KB granule at stage 2 is identified in TGran16.
+ *  0x1: 16KB granule not supported at stage 2.
+ *  0x2: 16KB granule supported at stage 2.
+ *  0x3: 16KB granule at stage 2 supports 52-bit input and output addresses.
+ *
+ * Defined values for the TGran16 are:
+ *  0x0: 16KB granule not supported.
+ *  0x1: 16KB granule supported.
+ *  0x2: 16KB granule supports 52-bit input and output addresses.
+ */
+struct tgran_test tgran16_2_test_params[] = {
+	/* Enable 16KB granule on the host that supports the granule */
+	{ID_AA64MMFR0_TGRAN16_2_SHIFT, 2, 2,  0,  0, 0},
+	/* Enable 16KB granule on the host that doesn't support the granule */
+	{ID_AA64MMFR0_TGRAN16_2_SHIFT, 2, 1,  0,  0, -E2BIG},
+	/* Disable 16KB granule on the host that supports the granule */
+	{ID_AA64MMFR0_TGRAN16_2_SHIFT, 1, 2,  0,  0, 0},
+	/* Disable 16KB granule */
+	{ID_AA64MMFR0_TGRAN16_2_SHIFT, 0, 0,  0,  0, 0},
+	/* Disable 16KB granule */
+	{ID_AA64MMFR0_TGRAN16_2_SHIFT, 0, 1,  0,  0, 0},
+	/* Enable 16KB granule on the host that doesn't support the granule */
+	{ID_AA64MMFR0_TGRAN16_2_SHIFT, 0, 1,  1,  0, -E2BIG},
+	/* Disable 16KB granule */
+	{ID_AA64MMFR0_TGRAN16_2_SHIFT, 0, 2,  0,  0, 0},
+	/*
+	 * Enable 16KB granule with 52 bit address on the host that doesn't
+	 * support it.
+	 */
+	{ID_AA64MMFR0_TGRAN16_2_SHIFT, 0, 2,  2,  2, -E2BIG},
+	/* Disable 16KB granule */
+	{ID_AA64MMFR0_TGRAN16_2_SHIFT, 1, 0,  0,  0, 0},
+	/* Disable 16KB granule */
+	{ID_AA64MMFR0_TGRAN16_2_SHIFT, 1, 0,  0,  1, 0},
+	/* Enable 16KB granule on the host that doesn't support the granule */
+	{ID_AA64MMFR0_TGRAN16_2_SHIFT, 2, 0,  0,  0, -E2BIG},
+	/* Enable 16KB granule on the host that supports the granule */
+	{ID_AA64MMFR0_TGRAN16_2_SHIFT, 2, 0,  0,  1, 0},
+	/* Enable 16KB granule on the host that supports the granule */
+	{ID_AA64MMFR0_TGRAN16_2_SHIFT, 2, 0,  0,  2, 0},
+};
+
+static void tgran2_case_to_desc(struct tgran_test *t, char *desc)
+{
+	snprintf(desc, KUNIT_PARAM_DESC_SIZE,
+		 "gran2(field=%d): val=%d, lim=%d gran1: val=%d limit=%d\n",
+		 t->gran2_field, t->gran2, t->gran2_lim,
+		 t->gran1, t->gran1_lim);
+}
+
+KUNIT_ARRAY_PARAM(tgran4_2, tgran4_2_test_params, tgran2_case_to_desc);
+KUNIT_ARRAY_PARAM(tgran64_2, tgran64_2_test_params, tgran2_case_to_desc);
+KUNIT_ARRAY_PARAM(tgran16_2, tgran16_2_test_params, tgran2_case_to_desc);
+
+#define	MAKE_MMFR0_TGRAN(shift1, gran1, shift2, gran2)		\
+	(((u64)((gran1) & 0xf) << (shift1)) |			\
+	 ((u64)((gran2) & 0xf) << (shift2)))
+
+/* Return the bit position of TGranX field for the given TGranX_2 field. */
+static int tgran2_to_tgran1_shift(int tgran2_shift)
+{
+	int tgran1_shift = -1;
+
+	switch (tgran2_shift) {
+	case ID_AA64MMFR0_TGRAN4_2_SHIFT:
+		tgran1_shift = ID_AA64MMFR0_TGRAN4_SHIFT;
+		break;
+	case ID_AA64MMFR0_TGRAN64_2_SHIFT:
+		tgran1_shift = ID_AA64MMFR0_TGRAN64_SHIFT;
+		break;
+	case ID_AA64MMFR0_TGRAN16_2_SHIFT:
+		tgran1_shift = ID_AA64MMFR0_TGRAN16_SHIFT;
+		break;
+	default:
+		break;
+	}
+
+	return tgran1_shift;
+}
+
+/* Tests for validate_id_aa64mmfr0_el1(). */
+static void validate_id_aa64mmfr0_tgran2_test(struct kunit *test)
+{
+	const struct tgran_test *t = test->param_value;
+	int shift1, shift2;
+	u64 v, lim;
+
+	shift2 = t->gran2_field;
+	shift1 = tgran2_to_tgran1_shift(shift2);
+	v = MAKE_MMFR0_TGRAN(shift1, t->gran1, shift2, t->gran2);
+	lim = MAKE_MMFR0_TGRAN(shift1, t->gran1_lim, shift2, t->gran2_lim);
+
+	KUNIT_EXPECT_EQ(test, aa64mmfr0_tgran2_check(shift2, v, lim), t->ret);
+}
+
+/* Tests for validate_id_aa64pfr0_el1(). */
+static void validate_id_aa64pfr0_el1_test(struct kunit *test)
+{
+	struct id_reg_info *id_reg;
+	struct kvm_vcpu *vcpu;
+	u64 v;
+
+	vcpu = test_kvm_vcpu_init(test);
+	KUNIT_ASSERT_TRUE(test, vcpu);
+
+	id_reg = GET_ID_REG_INFO(SYS_ID_AA64PFR0_EL1);
+
+	v = 0;
+	KUNIT_EXPECT_EQ(test, validate_id_aa64pfr0_el1(vcpu, id_reg, v), 0);
+
+	/*
+	 * Tests for GIC.
+	 * GIC must be 1 when vGIC3 is configured.
+	 */
+	v = 0x0000000;	/* GIC = 0 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64pfr0_el1(vcpu, id_reg, v), 0);
+
+	/* Test with VGIC_V2 */
+	vcpu->kvm->arch.vgic.in_kernel = true;
+	vcpu->kvm->arch.vgic.vgic_model = KVM_DEV_TYPE_ARM_VGIC_V2;
+
+	v = 0x0000000;	/* GIC = 0 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64pfr0_el1(vcpu, id_reg, v), 0);
+
+	/* Test with VGIC_V3 */
+	vcpu->kvm->arch.vgic.vgic_model = KVM_DEV_TYPE_ARM_VGIC_V3;
+
+	v = 0x0000000;	/* GIC = 0 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64pfr0_el1(vcpu, id_reg, v), 0);
+	v = 0x1000000;	/* GIC = 1 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64pfr0_el1(vcpu, id_reg, v), 0);
+
+	/* Restore the original VGIC state */
+	vcpu->kvm->arch.vgic.in_kernel = false;
+	vcpu->kvm->arch.vgic.vgic_model = 0;
+
+	/*
+	 * Tests for AdvSIMD/FP.
+	 * AdvSIMD must have the same value as FP.
+	 */
+
+	/* Tests with SVE disabled */
+	v = 0x000010000;	/* AdvSIMD = 0, FP = 1 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64pfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x000100000;	/* AdvSIMD = 1, FP = 0 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64pfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x000ff0000;	/* AdvSIMD = 0xf, FP = 0xf */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64pfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x100000000;	/* SVE =1, AdvSIMD = 0, FP = 0 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64pfr0_el1(vcpu, id_reg, v), 0);
+	if (!system_supports_sve()) {
+		kunit_skip(test, "No SVE support. Partial skip)");
+		/* Not reached */
+	}
+
+	/* Tests with SVE enabled */
+	vcpu->arch.flags |= KVM_ARM64_GUEST_HAS_SVE;
+
+	v = 0x100000000;	/* SVE =1, AdvSIMD = 0, FP = 0 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64pfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x100ff0000;	/* SVE =1, AdvSIMD = 0, FP = 0 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64pfr0_el1(vcpu, id_reg, v), 0);
+
+	vcpu->arch.flags &= ~KVM_ARM64_GUEST_HAS_SVE;
+}
+
+/* Tests for validate_id_aa64pfr1_el1() */
+static void validate_id_aa64pfr1_el1_test(struct kunit *test)
+{
+	struct id_reg_info *id_reg;
+	struct kvm_vcpu *vcpu;
+	u64 v;
+
+	vcpu = test_kvm_vcpu_init(test);
+	KUNIT_ASSERT_TRUE(test, vcpu);
+
+	id_reg = GET_ID_REG_INFO(SYS_ID_AA64PFR1_EL1);
+	v = 0;
+	KUNIT_EXPECT_EQ(test, validate_id_aa64pfr1_el1(vcpu, id_reg, v), 0);
+
+	/* Tests for MTE */
+
+	/* Tests with MTE disabled */
+	KUNIT_EXPECT_FALSE(test, vcpu->kvm->arch.mte_enabled);
+
+	v = 0x000;	/* MTE = 0 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64pfr1_el1(vcpu, id_reg, v), 0);
+
+	v = 0x100;	/* MTE = 1*/
+	KUNIT_EXPECT_NE(test, validate_id_aa64pfr1_el1(vcpu, id_reg, v), 0);
+
+	if (!system_supports_mte()) {
+		kunit_skip(test, "(No MTE support. Partial skip)");
+		/* Not reached */
+	}
+
+	/* Tests with MTE enabled */
+	vcpu->kvm->arch.mte_enabled = true;
+
+	v = 0x100;	/* MTE = 1*/
+	KUNIT_EXPECT_EQ(test, validate_id_aa64pfr1_el1(vcpu, id_reg, v), 0);
+
+	v = 0x0;	/* MTE = 0 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64pfr1_el1(vcpu, id_reg, v), 0);
+}
+
+/* Tests for validate_id_aa64isar0_el1(). */
+static void validate_id_aa64isar0_el1_test(struct kunit *test)
+{
+	struct id_reg_info *id_reg;
+	struct kvm_vcpu *vcpu;
+	u64 v;
+
+	vcpu = test_kvm_vcpu_init(test);
+	KUNIT_ASSERT_TRUE(test, vcpu);
+
+	id_reg = GET_ID_REG_INFO(SYS_ID_AA64ISAR0_EL1);
+
+	v = 0;
+	KUNIT_EXPECT_EQ(test, validate_id_aa64isar0_el1(vcpu, id_reg, v), 0);
+
+	/*
+	 * Tests for SM3/SM4.
+	 * Arm ARM says SM3 must have the same value as SM4.
+	 */
+
+	v = 0x01000000000;	/* SM4 = 0, SM3 = 1 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64isar0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x10000000000;	/* SM4 = 1, SM3 = 0 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64isar0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x11000000000;	/* SM3 = SM4 = 1 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64isar0_el1(vcpu, id_reg, v), 0);
+
+
+	/*
+	 * Tests for SHA1/SHA2/SHA3.  Arm ARM says:
+	 * If SHA1 is 0x0, both SHA2 and SHA3 must be 0x0.
+	 * If SHA2 is 0x0, SHA1 must be 0x0.
+	 * If SHA2 is 0x2, SHA3 must be 0x1.
+	 * If SHA3 is 0x1, SHA2 msut be 0x2.
+	 */
+
+	v = 0x000000100;	/* SHA2 = 0, SHA1 = 1 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64isar0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x000001000;	/* SHA2 = 1, SHA1 = 0 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64isar0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x000001100;	/* SHA2 = 1, SHA1 = 1 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64isar0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x100002000;	/* SHA3 = 1, SHA2 = 2 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64isar0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x000002000;	/* SHA3 = 0, SHA2 = 2 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64isar0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x100001000;	/* SHA3 = 1, SHA2 = 1 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64isar0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x200000000;	/* SHA3 = 2, SHA1 = 0 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64isar0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x200001100;	/* SHA3 = 2, SHA2= 1, SHA1 = 1 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64isar0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x300003300;	/* SHA3 = 3, SHA2 = 3, SHA1 = 3 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64isar0_el1(vcpu, id_reg, v), 0);
+}
+
+/* Tests for validate_id_aa64isar1_el1() */
+static void validate_id_aa64isar1_el1_test(struct kunit *test)
+{
+	struct id_reg_info *id_reg;
+	struct kvm_vcpu *vcpu;
+	u64 v;
+
+	vcpu = test_kvm_vcpu_init(test);
+	KUNIT_ASSERT_TRUE(test, vcpu);
+
+	id_reg = GET_ID_REG_INFO(SYS_ID_AA64ISAR1_EL1);
+
+	v = 0;
+	KUNIT_EXPECT_EQ(test, validate_id_aa64isar1_el1(vcpu, id_reg, v), 0);
+
+	/*
+	 * Tests for GPI/GPA/API/APA.
+	 * Arm ARM says:
+	 * If GPA is non-zero, GPI must be zero.
+	 * If GPI is non-zero, GPA must be zero.
+	 * If APA is non-zero, API must be zero.
+	 * If API is non-zero, APA must be zero.
+	 */
+
+	/* Tests with PTRAUTH disabled */
+	v = 0x11000110;	/* GPI = 1, GPA = 1, API = 1, APA = 1 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64isar1_el1(vcpu, id_reg, v), 0);
+
+	v = 0x11000100;	/* GPI = 1, GPA = 1, API = 1, APA = 0 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64isar1_el1(vcpu, id_reg, v), 0);
+
+	v = 0x11000010;	/* GPI = 1, GPA = 1, API = 0, APA = 1 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64isar1_el1(vcpu, id_reg, v), 0);
+
+	v = 0x10000110;	/* GPI = 1, GPA = 0, API = 1, APA = 1 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64isar1_el1(vcpu, id_reg, v), 0);
+
+	v = 0x01000110;	/* GPI = 0, GPA = 1, API = 1, APA = 1 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64isar1_el1(vcpu, id_reg, v), 0);
+
+	if (!system_has_full_ptr_auth()) {
+		kunit_skip(test, "(No PTRAUTH support. Partial skip)");
+		/* Not reached */
+	}
+
+	/* Tests with PTRAUTH enabled */
+	vcpu->arch.flags |= KVM_ARM64_GUEST_HAS_PTRAUTH;
+
+	v = 0x10000100;	/* GPI = 1, GPA = 0, API = 1, APA = 0 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64isar1_el1(vcpu, id_reg, v), 0);
+
+	v = 0x10000010;	/* GPI = 1, GPA = 0, API = 0, APA = 1 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64isar1_el1(vcpu, id_reg, v), 0);
+
+	v = 0x01000100;	/* GPI = 0, GPA = 1, API = 1, APA = 0 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64isar1_el1(vcpu, id_reg, v), 0);
+
+	v = 0x01000010;	/* GPI = 0, GPA = 1, API = 0, APA = 1 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64isar1_el1(vcpu, id_reg, v), 0);
+
+	v = 0;
+	KUNIT_EXPECT_NE(test, validate_id_aa64isar1_el1(vcpu, id_reg, v), 0);
+}
+
+/* Tests for validate_id_aa64mmfr0_el1() */
+static void validate_id_aa64mmfr0_el1_test(struct kunit *test)
+{
+	struct id_reg_info id_data, *id_reg;
+	const struct tgran_test *t4, *t64, *t16;
+	struct kvm_vcpu *vcpu;
+	int field4, field4_2, field64, field64_2, field16, field16_2;
+	u64 v, v4, lim4, v64, lim64, v16, lim16;
+	int i, j, ret;
+
+	id_reg = GET_ID_REG_INFO(SYS_ID_AA64MMFR0_EL1);
+
+	/* Save the original id_reg_info (and restore later) */
+	memcpy(&id_data, id_reg, sizeof(id_data));
+
+	vcpu = test_kvm_vcpu_init(test);
+
+	t4 = test->param_value;
+	field4_2 = t4->gran2_field;
+	field4 = tgran2_to_tgran1_shift(field4_2);
+	v4 = MAKE_MMFR0_TGRAN(field4, t4->gran1, field4_2, t4->gran2);
+	lim4 = MAKE_MMFR0_TGRAN(field4, t4->gran1_lim, field4_2, t4->gran2_lim);
+
+	/*
+	 * For each given gran4_2 params, test validate_id_aa64mmfr0_el1
+	 * with each of tgran64_2 and tgran16_2 params.
+	 */
+	for (i = 0; i < ARRAY_SIZE(tgran64_2_test_params); i++) {
+		t64 = &tgran64_2_test_params[i];
+		field64_2 = t64->gran2_field;
+		field64 = tgran2_to_tgran1_shift(field64_2);
+		v64 = MAKE_MMFR0_TGRAN(field64, t64->gran1,
+				       field64_2, t64->gran2);
+		lim64 = MAKE_MMFR0_TGRAN(field64, t64->gran1_lim,
+					 field64_2, t64->gran2_lim);
+
+		for (j = 0; j < ARRAY_SIZE(tgran16_2_test_params); j++) {
+			t16 = &tgran16_2_test_params[j];
+
+			field16_2 = t16->gran2_field;
+			field16 = tgran2_to_tgran1_shift(field16_2);
+			v16 = MAKE_MMFR0_TGRAN(field16, t16->gran1,
+					       field16_2, t16->gran2);
+			lim16 = MAKE_MMFR0_TGRAN(field16, t16->gran1_lim,
+						 field16_2, t16->gran2_lim);
+
+			/* Build id_aa64mmfr0_el1 from tgran16/64/4 values */
+			v = v16 | v64 | v4;
+			id_reg->vcpu_limit_val = lim16 | lim64 | lim4;
+
+			ret = t4->ret ? t4->ret : t64->ret;
+			ret = ret ? ret : t16->ret;
+			KUNIT_EXPECT_EQ(test,
+				validate_id_aa64mmfr0_el1(vcpu, id_reg, v),
+				ret);
+		}
+	}
+
+	/* Restore id_reg_info */
+	memcpy(id_reg, &id_data, sizeof(id_data));
+}
+
+/* Tests for validate_id_aa64dfr0_el1() */
+static void validate_id_aa64dfr0_el1_test(struct kunit *test)
+{
+	struct id_reg_info *id_reg;
+	struct kvm_vcpu *vcpu;
+	u64 v;
+
+	id_reg = GET_ID_REG_INFO(SYS_ID_AA64DFR0_EL1);
+	vcpu = test_kvm_vcpu_init(test);
+	KUNIT_ASSERT_TRUE(test, vcpu);
+
+	v = 0;
+	KUNIT_EXPECT_EQ(test, validate_id_aa64dfr0_el1(vcpu, id_reg, v), 0);
+
+	/*
+	 * Tests for CTX_CMPS/BRPS.
+	 * Number of context-aware breakpoints can be no more than number
+	 * of supported breakpoints.
+	 */
+	v = 0x10001000;	/* CTX_CMPS = 1, BRPS = 1 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64dfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x20001000;	/* CTX_CMPS = 2, BRPS = 1 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64dfr0_el1(vcpu, id_reg, v), 0);
+
+	/* Tests for PMUVer */
+
+	/* Tests with PMUv3 disabled. */
+
+	v = 0x000;	/* PMUVER = 0x0 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64dfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0xf00;	/* PMUVER = 0xf */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64dfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x100;	/* PMUVER = 1 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64dfr0_el1(vcpu, id_reg, v), 0);
+
+	/* Tests with PMUv3 enabled */
+	set_bit(KVM_ARM_VCPU_PMU_V3, vcpu->arch.features);
+
+	v = 0x000;	/* PMUVER = 0x0 */
+	KUNIT_EXPECT_NE(test, validate_id_aa64dfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x000;	/* PMUVER = 0xf */
+	KUNIT_EXPECT_NE(test, validate_id_aa64dfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x100;	/* PMUVER = 1 */
+	KUNIT_EXPECT_EQ(test, validate_id_aa64dfr0_el1(vcpu, id_reg, v), 0);
+}
+
+/* Tests for validate_id_dfr0_el1() */
+static void validate_id_dfr0_el1_test(struct kunit *test)
+{
+	struct id_reg_info *id_reg;
+	struct kvm_vcpu *vcpu;
+	u64 v;
+
+	id_reg = GET_ID_REG_INFO(SYS_ID_DFR0_EL1);
+	vcpu = test_kvm_vcpu_init(test);
+	KUNIT_ASSERT_TRUE(test, vcpu);
+
+	v = 0;
+	KUNIT_EXPECT_EQ(test, validate_id_dfr0_el1(vcpu, id_reg, v), 0);
+
+	/* Tests for PERFMON */
+
+	/* Tests with PMUv3 disabled */
+
+	v = 0x0000000;	/* PERFMON = 0x0 */
+	KUNIT_EXPECT_EQ(test, validate_id_dfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0xf000000;	/* PERFMON = 0xf */
+	KUNIT_EXPECT_EQ(test, validate_id_dfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x1000000;	/* PERFMON = 1 */
+	KUNIT_EXPECT_NE(test, validate_id_dfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x2000000;	/* PERFMON = 2 */
+	KUNIT_EXPECT_NE(test, validate_id_dfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x3000000;	/* PERFMON = 3 */
+	KUNIT_EXPECT_NE(test, validate_id_dfr0_el1(vcpu, id_reg, v), 0);
+
+
+	/* Tests with PMUv3 enabled */
+	set_bit(KVM_ARM_VCPU_PMU_V3, vcpu->arch.features);
+
+	v = 0x0000000;	/* PERFMON = 0x0 */
+	KUNIT_EXPECT_NE(test, validate_id_dfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0xf000000;	/* PERFMON = 0xf */
+	KUNIT_EXPECT_NE(test, validate_id_dfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x1000000;	/* PERFMON = 1 */
+	KUNIT_EXPECT_NE(test, validate_id_dfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x2000000;	/* PERFMON = 2 */
+	KUNIT_EXPECT_NE(test, validate_id_dfr0_el1(vcpu, id_reg, v), 0);
+
+	v = 0x3000000;	/* PERFMON = 3 */
+	KUNIT_EXPECT_EQ(test, validate_id_dfr0_el1(vcpu, id_reg, v), 0);
+}
+
+/* Tests for validate_mvfr1_el1(). */
+static void validate_mvfr1_el1_test(struct kunit *test)
+{
+	struct id_reg_info *id_reg;
+	struct kvm_vcpu *vcpu;
+	u64 v;
+
+	id_reg = GET_ID_REG_INFO(SYS_MVFR1_EL1);
+	vcpu = test_kvm_vcpu_init(test);
+	KUNIT_ASSERT_TRUE(test, vcpu);
+
+	v = 0;
+	KUNIT_EXPECT_EQ(test, validate_mvfr1_el1(vcpu, id_reg, v), 0);
+
+	/*
+	 * Tests for FPHP/SIMDHP.
+	 * Arm ARM says the level of support indicated by FPHP must be
+	 * equivalent to the level of support indicated by the SIMDHP,
+	 * meaning the permitted values are:
+	 * FPHP = 0x0, SIMDHP = 0x0
+	 * FPHP = 0x2, SIMDHP = 0x1
+	 * FPHP = 0x3, SIMDHP = 0x2
+	 */
+	v = 0x0000000;	/* FPHP = 0, SIMDHP = 0 */
+	KUNIT_EXPECT_EQ(test, validate_mvfr1_el1(vcpu, id_reg, v), 0);
+
+	v = 0x2100000;	/* FPHP = 2, SIMDHP = 1 */
+	KUNIT_EXPECT_EQ(test, validate_mvfr1_el1(vcpu, id_reg, v), 0);
+
+	v = 0x3200000;	/* FPHP = 3, SIMDHP = 2 */
+	KUNIT_EXPECT_EQ(test, validate_mvfr1_el1(vcpu, id_reg, v), 0);
+
+	v = 0x1100000;	/* FPHP = 1, SIMDHP = 1 */
+	KUNIT_EXPECT_NE(test, validate_mvfr1_el1(vcpu, id_reg, v), 0);
+
+	v = 0x2200000;	/* FPHP = 2, SIMDHP = 2 */
+	KUNIT_EXPECT_NE(test, validate_mvfr1_el1(vcpu, id_reg, v), 0);
+
+	v = 0x3300000;	/* FPHP = 3, SIMDHP = 3 */
+	KUNIT_EXPECT_NE(test, validate_mvfr1_el1(vcpu, id_reg, v), 0);
+
+	v = (u64)-1;
+	KUNIT_EXPECT_NE(test, validate_mvfr1_el1(vcpu, id_reg, v), 0);
+}
+
+/*
+ * Helper function for validate_id_reg_test().
+ * We don't use KUNIT_ASSERT or kunit_skip because this is a helper test
+ * function and we are not sure if it's safe to exist from the test case.
+ */
+static void validate_id_reg_test_one_field(struct kunit *test,
+		u32 id, int pos, int fval, int flimit,
+		bool is_signed, struct id_reg_info *idr)
+{
+	struct kvm_vcpu *vcpu;
+	int fmin = is_signed ? -1 : 0;
+	int fmax = is_signed ? 7 : 15;
+	u64 fmask = ARM64_FEATURE_FIELD_MASK;
+	u64 val;
+
+	vcpu = test_kvm_vcpu_init(test);
+	KUNIT_ASSERT_TRUE(test, vcpu);
+
+	if (flimit > fmax) {
+		/* Shouldn't happen. Make the test failure. */
+		KUNIT_EXPECT_FALSE(test, flimit > fmax);
+		kunit_err(test, "%s: flimit(%d) > fmax(%d). Must be test bug",
+			  __func__, flimit, fmax);
+		return;
+	}
+
+	if (fval > fmin) {
+		/* Set the field to a smaller value */
+		val = ((u64)(fval - 1) & fmask) << pos;
+		KUNIT_EXPECT_EQ(test, validate_id_reg(vcpu, id, val), 0);
+	}
+
+	if (fval < flimit) {
+		/* Set the field to a larger value, but smaller than flimit */
+		val = ((u64)(fval + 1) & fmask) << pos;
+		KUNIT_EXPECT_EQ(test, validate_id_reg(vcpu, id, val), 0);
+		/* Set the field to the flimit */
+		val = ((u64)flimit & fmask) << pos;
+		KUNIT_EXPECT_EQ(test, validate_id_reg(vcpu, id, val), 0);
+	}
+
+	if (flimit < fmax) {
+		/* Set the field to a larger value than flimit */
+		val = ((u64)(flimit + 1) & fmask) << pos;
+		KUNIT_EXPECT_NE(test, validate_id_reg(vcpu, id, val), 0);
+
+		/* Test with ignore_mask */
+		if (idr) {
+			idr->ignore_mask = fmask << pos;
+			KUNIT_EXPECT_EQ(test,
+					validate_id_reg(vcpu, id, val),
+					0);
+		}
+	}
+	test_kvm_vcpu_fini(test, vcpu);
+}
+
+/*
+ * Test for validate_id_reg().
+ */
+static void validate_id_reg_test(struct kunit *test)
+{
+	struct id_reg_info idr_data, *idr, *original_idr;
+	u32 id;
+	int fval, flim, pos;
+	u64 val;
+	bool sign;
+
+	/* Use AA64PFR0_EL1 because it includes both sign/unsigned fields */
+	id = SYS_ID_AA64PFR0_EL1;
+
+	/* Save the original id_reg_info */
+	original_idr = GET_ID_REG_INFO(id);
+
+	/* Test with a temporary id_reg_info for testing */
+	idr = &idr_data;
+	GET_ID_REG_INFO(id) = idr;
+
+	fval = 0x1;
+	flim = 0x2;
+
+	/* Test with unsigned field */
+	pos = ID_AA64PFR0_RAS_SHIFT;
+
+	/* Set up id_reg_info for testing */
+	memset(idr, 0, sizeof(*idr));
+	idr->sys_reg = id;
+	idr->vcpu_limit_val = (u64)flim << pos;
+	validate_id_reg_test_one_field(test, id, pos, fval, flim, false, idr);
+
+	/* Test with signed field */
+	pos = ID_AA64PFR0_FP_SHIFT;
+
+	/* Set up id_reg_info for testing */
+	memset(idr, 0, sizeof(*idr));
+	idr->sys_reg = id;
+	idr->vcpu_limit_val = (u64)flim << pos;
+	validate_id_reg_test_one_field(test, id, pos, fval, flim, true, idr);
+
+
+	/* Test without id_reg_info */
+	GET_ID_REG_INFO(id) = NULL;
+	if (original_idr)
+		val = original_idr->vcpu_limit_val;
+	else
+		val = read_sanitised_ftr_reg(id);
+
+	for (pos = 0; pos < 64; pos += 4) {
+		if (pos == ID_AA64PFR0_FP_SHIFT ||
+		    pos == ID_AA64PFR0_ASIMD_SHIFT)
+			sign = true;
+		else
+			sign = false;
+
+		fval = cpuid_feature_extract_field(val, pos, sign);
+		validate_id_reg_test_one_field(test, id, pos, fval, fval,
+					       sign, NULL);
+	}
+
+	/* Restore the original id_reg_info */
+	GET_ID_REG_INFO(id) = original_idr;
+}
+
+static struct kunit_case kvm_sys_regs_test_cases[] = {
+	KUNIT_CASE_PARAM(vcpu_id_reg_feature_frac_check_test, frac_gen_params),
+	KUNIT_CASE_PARAM(validate_id_aa64mmfr0_tgran2_test, tgran4_2_gen_params),
+	KUNIT_CASE_PARAM(validate_id_aa64mmfr0_tgran2_test, tgran64_2_gen_params),
+	KUNIT_CASE_PARAM(validate_id_aa64mmfr0_tgran2_test, tgran16_2_gen_params),
+	KUNIT_CASE(validate_id_aa64pfr0_el1_test),
+	KUNIT_CASE(validate_id_aa64pfr1_el1_test),
+	KUNIT_CASE(validate_id_aa64isar0_el1_test),
+	KUNIT_CASE(validate_id_aa64isar1_el1_test),
+	KUNIT_CASE_PARAM(validate_id_aa64mmfr0_el1_test, tgran4_2_gen_params),
+	KUNIT_CASE(validate_id_aa64dfr0_el1_test),
+	KUNIT_CASE(validate_id_dfr0_el1_test),
+	KUNIT_CASE(validate_mvfr1_el1_test),
+	KUNIT_CASE(validate_id_reg_test),
+	{}
+};
+
+static struct kunit_suite kvm_sys_regs_test_suite = {
+	.name = "kvm-sys-regs-test-suite",
+	.test_cases = kvm_sys_regs_test_cases,
+};
+
+kunit_test_suites(&kvm_sys_regs_test_suite);
+MODULE_LICENSE("GPL");
-- 
2.34.1.448.ga2b2bfdf31-goog


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 17/26] KVM: arm64: Use vcpu->arch cptr_el2 to track value of cptr_el2 for VHE
  2022-01-06  4:26 ` Reiji Watanabe
  (?)
@ 2022-01-06  4:26   ` Reiji Watanabe
  -1 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

Track the baseline guest value for cptr_el2 in struct kvm_vcpu_arch
for VHE.  Use this value when setting cptr_el2 for the guest.

Currently this value is unchanged, but the following patches will set
trapping bits based on features supported for the guest.

No functional change intended.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/include/asm/kvm_arm.h | 16 ++++++++++++++++
 arch/arm64/kvm/arm.c             |  5 ++++-
 arch/arm64/kvm/hyp/vhe/switch.c  | 14 ++------------
 3 files changed, 22 insertions(+), 13 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_arm.h b/arch/arm64/include/asm/kvm_arm.h
index 01d47c5886dc..8ab6ea038721 100644
--- a/arch/arm64/include/asm/kvm_arm.h
+++ b/arch/arm64/include/asm/kvm_arm.h
@@ -288,6 +288,22 @@
 				 GENMASK(19, 14) |	\
 				 BIT(11))
 
+/*
+ * With VHE (HCR.E2H == 1), accesses to CPACR_EL1 are routed to
+ * CPTR_EL2. In general, CPACR_EL1 has the same layout as CPTR_EL2,
+ * except for some missing controls, such as TAM.
+ * In this case, CPTR_EL2.TAM has the same position with or without
+ * VHE (HCR.E2H == 1) which allows us to use here the CPTR_EL2.TAM
+ * shift value for trapping the AMU accesses.
+ */
+#define CPTR_EL2_VHE_GUEST_DEFAULT	(CPACR_EL1_TTA | CPTR_EL2_TAM)
+
+/*
+ * Bits that are copied from vcpu->arch.cptr_el2 to set cptr_el2 for
+ * guest with VHE.
+ */
+#define CPTR_EL2_VHE_GUEST_TRACKED_MASK	(CPACR_EL1_TTA | CPTR_EL2_TAM)
+
 /* Hyp Debug Configuration Register bits */
 #define MDCR_EL2_E2TB_MASK	(UL(0x3))
 #define MDCR_EL2_E2TB_SHIFT	(UL(24))
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 876f2777acf2..3700144f6271 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -1136,7 +1136,10 @@ static int kvm_arch_vcpu_ioctl_vcpu_init(struct kvm_vcpu *vcpu,
 	}
 
 	vcpu_reset_hcr(vcpu);
-	vcpu->arch.cptr_el2 = CPTR_EL2_DEFAULT;
+	if (has_vhe())
+		vcpu->arch.cptr_el2 = CPTR_EL2_VHE_GUEST_DEFAULT;
+	else
+		vcpu->arch.cptr_el2 = CPTR_EL2_DEFAULT;
 
 	/*
 	 * Handle the "start in power-off" case.
diff --git a/arch/arm64/kvm/hyp/vhe/switch.c b/arch/arm64/kvm/hyp/vhe/switch.c
index fbb26b93c347..572b2669ae84 100644
--- a/arch/arm64/kvm/hyp/vhe/switch.c
+++ b/arch/arm64/kvm/hyp/vhe/switch.c
@@ -38,20 +38,10 @@ static void __activate_traps(struct kvm_vcpu *vcpu)
 	___activate_traps(vcpu);
 
 	val = read_sysreg(cpacr_el1);
-	val |= CPACR_EL1_TTA;
+	val &= ~CPTR_EL2_VHE_GUEST_TRACKED_MASK;
+	val |= (vcpu->arch.cptr_el2 & CPTR_EL2_VHE_GUEST_TRACKED_MASK);
 	val &= ~CPACR_EL1_ZEN;
 
-	/*
-	 * With VHE (HCR.E2H == 1), accesses to CPACR_EL1 are routed to
-	 * CPTR_EL2. In general, CPACR_EL1 has the same layout as CPTR_EL2,
-	 * except for some missing controls, such as TAM.
-	 * In this case, CPTR_EL2.TAM has the same position with or without
-	 * VHE (HCR.E2H == 1) which allows us to use here the CPTR_EL2.TAM
-	 * shift value for trapping the AMU accesses.
-	 */
-
-	val |= CPTR_EL2_TAM;
-
 	if (update_fp_enabled(vcpu)) {
 		if (vcpu_has_sve(vcpu))
 			val |= CPACR_EL1_ZEN;
-- 
2.34.1.448.ga2b2bfdf31-goog


^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 17/26] KVM: arm64: Use vcpu->arch cptr_el2 to track value of cptr_el2 for VHE
@ 2022-01-06  4:26   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, Will Deacon, Peter Shier, Paolo Bonzini, linux-arm-kernel

Track the baseline guest value for cptr_el2 in struct kvm_vcpu_arch
for VHE.  Use this value when setting cptr_el2 for the guest.

Currently this value is unchanged, but the following patches will set
trapping bits based on features supported for the guest.

No functional change intended.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/include/asm/kvm_arm.h | 16 ++++++++++++++++
 arch/arm64/kvm/arm.c             |  5 ++++-
 arch/arm64/kvm/hyp/vhe/switch.c  | 14 ++------------
 3 files changed, 22 insertions(+), 13 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_arm.h b/arch/arm64/include/asm/kvm_arm.h
index 01d47c5886dc..8ab6ea038721 100644
--- a/arch/arm64/include/asm/kvm_arm.h
+++ b/arch/arm64/include/asm/kvm_arm.h
@@ -288,6 +288,22 @@
 				 GENMASK(19, 14) |	\
 				 BIT(11))
 
+/*
+ * With VHE (HCR.E2H == 1), accesses to CPACR_EL1 are routed to
+ * CPTR_EL2. In general, CPACR_EL1 has the same layout as CPTR_EL2,
+ * except for some missing controls, such as TAM.
+ * In this case, CPTR_EL2.TAM has the same position with or without
+ * VHE (HCR.E2H == 1) which allows us to use here the CPTR_EL2.TAM
+ * shift value for trapping the AMU accesses.
+ */
+#define CPTR_EL2_VHE_GUEST_DEFAULT	(CPACR_EL1_TTA | CPTR_EL2_TAM)
+
+/*
+ * Bits that are copied from vcpu->arch.cptr_el2 to set cptr_el2 for
+ * guest with VHE.
+ */
+#define CPTR_EL2_VHE_GUEST_TRACKED_MASK	(CPACR_EL1_TTA | CPTR_EL2_TAM)
+
 /* Hyp Debug Configuration Register bits */
 #define MDCR_EL2_E2TB_MASK	(UL(0x3))
 #define MDCR_EL2_E2TB_SHIFT	(UL(24))
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 876f2777acf2..3700144f6271 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -1136,7 +1136,10 @@ static int kvm_arch_vcpu_ioctl_vcpu_init(struct kvm_vcpu *vcpu,
 	}
 
 	vcpu_reset_hcr(vcpu);
-	vcpu->arch.cptr_el2 = CPTR_EL2_DEFAULT;
+	if (has_vhe())
+		vcpu->arch.cptr_el2 = CPTR_EL2_VHE_GUEST_DEFAULT;
+	else
+		vcpu->arch.cptr_el2 = CPTR_EL2_DEFAULT;
 
 	/*
 	 * Handle the "start in power-off" case.
diff --git a/arch/arm64/kvm/hyp/vhe/switch.c b/arch/arm64/kvm/hyp/vhe/switch.c
index fbb26b93c347..572b2669ae84 100644
--- a/arch/arm64/kvm/hyp/vhe/switch.c
+++ b/arch/arm64/kvm/hyp/vhe/switch.c
@@ -38,20 +38,10 @@ static void __activate_traps(struct kvm_vcpu *vcpu)
 	___activate_traps(vcpu);
 
 	val = read_sysreg(cpacr_el1);
-	val |= CPACR_EL1_TTA;
+	val &= ~CPTR_EL2_VHE_GUEST_TRACKED_MASK;
+	val |= (vcpu->arch.cptr_el2 & CPTR_EL2_VHE_GUEST_TRACKED_MASK);
 	val &= ~CPACR_EL1_ZEN;
 
-	/*
-	 * With VHE (HCR.E2H == 1), accesses to CPACR_EL1 are routed to
-	 * CPTR_EL2. In general, CPACR_EL1 has the same layout as CPTR_EL2,
-	 * except for some missing controls, such as TAM.
-	 * In this case, CPTR_EL2.TAM has the same position with or without
-	 * VHE (HCR.E2H == 1) which allows us to use here the CPTR_EL2.TAM
-	 * shift value for trapping the AMU accesses.
-	 */
-
-	val |= CPTR_EL2_TAM;
-
 	if (update_fp_enabled(vcpu)) {
 		if (vcpu_has_sve(vcpu))
 			val |= CPACR_EL1_ZEN;
-- 
2.34.1.448.ga2b2bfdf31-goog

_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 17/26] KVM: arm64: Use vcpu->arch cptr_el2 to track value of cptr_el2 for VHE
@ 2022-01-06  4:26   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:26 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

Track the baseline guest value for cptr_el2 in struct kvm_vcpu_arch
for VHE.  Use this value when setting cptr_el2 for the guest.

Currently this value is unchanged, but the following patches will set
trapping bits based on features supported for the guest.

No functional change intended.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/include/asm/kvm_arm.h | 16 ++++++++++++++++
 arch/arm64/kvm/arm.c             |  5 ++++-
 arch/arm64/kvm/hyp/vhe/switch.c  | 14 ++------------
 3 files changed, 22 insertions(+), 13 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_arm.h b/arch/arm64/include/asm/kvm_arm.h
index 01d47c5886dc..8ab6ea038721 100644
--- a/arch/arm64/include/asm/kvm_arm.h
+++ b/arch/arm64/include/asm/kvm_arm.h
@@ -288,6 +288,22 @@
 				 GENMASK(19, 14) |	\
 				 BIT(11))
 
+/*
+ * With VHE (HCR.E2H == 1), accesses to CPACR_EL1 are routed to
+ * CPTR_EL2. In general, CPACR_EL1 has the same layout as CPTR_EL2,
+ * except for some missing controls, such as TAM.
+ * In this case, CPTR_EL2.TAM has the same position with or without
+ * VHE (HCR.E2H == 1) which allows us to use here the CPTR_EL2.TAM
+ * shift value for trapping the AMU accesses.
+ */
+#define CPTR_EL2_VHE_GUEST_DEFAULT	(CPACR_EL1_TTA | CPTR_EL2_TAM)
+
+/*
+ * Bits that are copied from vcpu->arch.cptr_el2 to set cptr_el2 for
+ * guest with VHE.
+ */
+#define CPTR_EL2_VHE_GUEST_TRACKED_MASK	(CPACR_EL1_TTA | CPTR_EL2_TAM)
+
 /* Hyp Debug Configuration Register bits */
 #define MDCR_EL2_E2TB_MASK	(UL(0x3))
 #define MDCR_EL2_E2TB_SHIFT	(UL(24))
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 876f2777acf2..3700144f6271 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -1136,7 +1136,10 @@ static int kvm_arch_vcpu_ioctl_vcpu_init(struct kvm_vcpu *vcpu,
 	}
 
 	vcpu_reset_hcr(vcpu);
-	vcpu->arch.cptr_el2 = CPTR_EL2_DEFAULT;
+	if (has_vhe())
+		vcpu->arch.cptr_el2 = CPTR_EL2_VHE_GUEST_DEFAULT;
+	else
+		vcpu->arch.cptr_el2 = CPTR_EL2_DEFAULT;
 
 	/*
 	 * Handle the "start in power-off" case.
diff --git a/arch/arm64/kvm/hyp/vhe/switch.c b/arch/arm64/kvm/hyp/vhe/switch.c
index fbb26b93c347..572b2669ae84 100644
--- a/arch/arm64/kvm/hyp/vhe/switch.c
+++ b/arch/arm64/kvm/hyp/vhe/switch.c
@@ -38,20 +38,10 @@ static void __activate_traps(struct kvm_vcpu *vcpu)
 	___activate_traps(vcpu);
 
 	val = read_sysreg(cpacr_el1);
-	val |= CPACR_EL1_TTA;
+	val &= ~CPTR_EL2_VHE_GUEST_TRACKED_MASK;
+	val |= (vcpu->arch.cptr_el2 & CPTR_EL2_VHE_GUEST_TRACKED_MASK);
 	val &= ~CPACR_EL1_ZEN;
 
-	/*
-	 * With VHE (HCR.E2H == 1), accesses to CPACR_EL1 are routed to
-	 * CPTR_EL2. In general, CPACR_EL1 has the same layout as CPTR_EL2,
-	 * except for some missing controls, such as TAM.
-	 * In this case, CPTR_EL2.TAM has the same position with or without
-	 * VHE (HCR.E2H == 1) which allows us to use here the CPTR_EL2.TAM
-	 * shift value for trapping the AMU accesses.
-	 */
-
-	val |= CPTR_EL2_TAM;
-
 	if (update_fp_enabled(vcpu)) {
 		if (vcpu_has_sve(vcpu))
 			val |= CPACR_EL1_ZEN;
-- 
2.34.1.448.ga2b2bfdf31-goog


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 18/26] KVM: arm64: Use vcpu->arch.mdcr_el2 to track value of mdcr_el2
  2022-01-06  4:26 ` Reiji Watanabe
  (?)
@ 2022-01-06  4:27   ` Reiji Watanabe
  -1 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:27 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

Track the baseline guest value for mdcr_el2 in struct kvm_vcpu_arch.
Use this value when setting mdcr_el2 for the guest.

Currently this value is unchanged, but the following patches will set
trapping bits based on features supported for the guest.

No functional change intended.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/include/asm/kvm_arm.h | 16 ++++++++++++++++
 arch/arm64/kvm/arm.c             |  1 +
 arch/arm64/kvm/debug.c           | 13 ++++---------
 3 files changed, 21 insertions(+), 9 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_arm.h b/arch/arm64/include/asm/kvm_arm.h
index 8ab6ea038721..4b2ac9e32a36 100644
--- a/arch/arm64/include/asm/kvm_arm.h
+++ b/arch/arm64/include/asm/kvm_arm.h
@@ -333,6 +333,22 @@
 				 BIT(18) |		\
 				 GENMASK(16, 15))
 
+/*
+ * The default value for the guest below also clears MDCR_EL2_E2PB_MASK
+ * and MDCR_EL2_E2TB_MASK to disable guest access to the profiling and
+ * trace buffers.
+ */
+#define MDCR_GUEST_FLAGS_DEFAULT				\
+	(MDCR_EL2_TPM  | MDCR_EL2_TPMS | MDCR_EL2_TTRF |	\
+	 MDCR_EL2_TPMCR | MDCR_EL2_TDRA | MDCR_EL2_TDOSA)
+
+/* Bits that are copied from vcpu->arch.mdcr_el2 to set mdcr_el2 for guest. */
+#define MDCR_GUEST_FLAGS_TRACKED_MASK				\
+	(MDCR_EL2_TPM  | MDCR_EL2_TPMS | MDCR_EL2_TTRF |	\
+	 MDCR_EL2_TPMCR | MDCR_EL2_TDRA | MDCR_EL2_TDOSA |	\
+	 (MDCR_EL2_E2PB_MASK << MDCR_EL2_E2PB_SHIFT))
+
+
 /* For compatibility with fault code shared with 32-bit */
 #define FSC_FAULT	ESR_ELx_FSC_FAULT
 #define FSC_ACCESS	ESR_ELx_FSC_ACCESS
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 3700144f6271..33acbae7a7ed 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -1136,6 +1136,7 @@ static int kvm_arch_vcpu_ioctl_vcpu_init(struct kvm_vcpu *vcpu,
 	}
 
 	vcpu_reset_hcr(vcpu);
+	vcpu->arch.mdcr_el2 = MDCR_GUEST_FLAGS_DEFAULT;
 	if (has_vhe())
 		vcpu->arch.cptr_el2 = CPTR_EL2_VHE_GUEST_DEFAULT;
 	else
diff --git a/arch/arm64/kvm/debug.c b/arch/arm64/kvm/debug.c
index db9361338b2a..83330968a411 100644
--- a/arch/arm64/kvm/debug.c
+++ b/arch/arm64/kvm/debug.c
@@ -84,16 +84,11 @@ void kvm_arm_init_debug(void)
 static void kvm_arm_setup_mdcr_el2(struct kvm_vcpu *vcpu)
 {
 	/*
-	 * This also clears MDCR_EL2_E2PB_MASK and MDCR_EL2_E2TB_MASK
-	 * to disable guest access to the profiling and trace buffers
+	 * Keep the vcpu->arch.mdcr_el2 bits that are specified by
+	 * MDCR_GUEST_FLAGS_TRACKED_MASK.
 	 */
-	vcpu->arch.mdcr_el2 = __this_cpu_read(mdcr_el2) & MDCR_EL2_HPMN_MASK;
-	vcpu->arch.mdcr_el2 |= (MDCR_EL2_TPM |
-				MDCR_EL2_TPMS |
-				MDCR_EL2_TTRF |
-				MDCR_EL2_TPMCR |
-				MDCR_EL2_TDRA |
-				MDCR_EL2_TDOSA);
+	vcpu->arch.mdcr_el2 &= MDCR_GUEST_FLAGS_TRACKED_MASK;
+	vcpu->arch.mdcr_el2 |= __this_cpu_read(mdcr_el2) & MDCR_EL2_HPMN_MASK;
 
 	/* Is the VM being debugged by userspace? */
 	if (vcpu->guest_debug)
-- 
2.34.1.448.ga2b2bfdf31-goog


^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 18/26] KVM: arm64: Use vcpu->arch.mdcr_el2 to track value of mdcr_el2
@ 2022-01-06  4:27   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:27 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, Will Deacon, Peter Shier, Paolo Bonzini, linux-arm-kernel

Track the baseline guest value for mdcr_el2 in struct kvm_vcpu_arch.
Use this value when setting mdcr_el2 for the guest.

Currently this value is unchanged, but the following patches will set
trapping bits based on features supported for the guest.

No functional change intended.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/include/asm/kvm_arm.h | 16 ++++++++++++++++
 arch/arm64/kvm/arm.c             |  1 +
 arch/arm64/kvm/debug.c           | 13 ++++---------
 3 files changed, 21 insertions(+), 9 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_arm.h b/arch/arm64/include/asm/kvm_arm.h
index 8ab6ea038721..4b2ac9e32a36 100644
--- a/arch/arm64/include/asm/kvm_arm.h
+++ b/arch/arm64/include/asm/kvm_arm.h
@@ -333,6 +333,22 @@
 				 BIT(18) |		\
 				 GENMASK(16, 15))
 
+/*
+ * The default value for the guest below also clears MDCR_EL2_E2PB_MASK
+ * and MDCR_EL2_E2TB_MASK to disable guest access to the profiling and
+ * trace buffers.
+ */
+#define MDCR_GUEST_FLAGS_DEFAULT				\
+	(MDCR_EL2_TPM  | MDCR_EL2_TPMS | MDCR_EL2_TTRF |	\
+	 MDCR_EL2_TPMCR | MDCR_EL2_TDRA | MDCR_EL2_TDOSA)
+
+/* Bits that are copied from vcpu->arch.mdcr_el2 to set mdcr_el2 for guest. */
+#define MDCR_GUEST_FLAGS_TRACKED_MASK				\
+	(MDCR_EL2_TPM  | MDCR_EL2_TPMS | MDCR_EL2_TTRF |	\
+	 MDCR_EL2_TPMCR | MDCR_EL2_TDRA | MDCR_EL2_TDOSA |	\
+	 (MDCR_EL2_E2PB_MASK << MDCR_EL2_E2PB_SHIFT))
+
+
 /* For compatibility with fault code shared with 32-bit */
 #define FSC_FAULT	ESR_ELx_FSC_FAULT
 #define FSC_ACCESS	ESR_ELx_FSC_ACCESS
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 3700144f6271..33acbae7a7ed 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -1136,6 +1136,7 @@ static int kvm_arch_vcpu_ioctl_vcpu_init(struct kvm_vcpu *vcpu,
 	}
 
 	vcpu_reset_hcr(vcpu);
+	vcpu->arch.mdcr_el2 = MDCR_GUEST_FLAGS_DEFAULT;
 	if (has_vhe())
 		vcpu->arch.cptr_el2 = CPTR_EL2_VHE_GUEST_DEFAULT;
 	else
diff --git a/arch/arm64/kvm/debug.c b/arch/arm64/kvm/debug.c
index db9361338b2a..83330968a411 100644
--- a/arch/arm64/kvm/debug.c
+++ b/arch/arm64/kvm/debug.c
@@ -84,16 +84,11 @@ void kvm_arm_init_debug(void)
 static void kvm_arm_setup_mdcr_el2(struct kvm_vcpu *vcpu)
 {
 	/*
-	 * This also clears MDCR_EL2_E2PB_MASK and MDCR_EL2_E2TB_MASK
-	 * to disable guest access to the profiling and trace buffers
+	 * Keep the vcpu->arch.mdcr_el2 bits that are specified by
+	 * MDCR_GUEST_FLAGS_TRACKED_MASK.
 	 */
-	vcpu->arch.mdcr_el2 = __this_cpu_read(mdcr_el2) & MDCR_EL2_HPMN_MASK;
-	vcpu->arch.mdcr_el2 |= (MDCR_EL2_TPM |
-				MDCR_EL2_TPMS |
-				MDCR_EL2_TTRF |
-				MDCR_EL2_TPMCR |
-				MDCR_EL2_TDRA |
-				MDCR_EL2_TDOSA);
+	vcpu->arch.mdcr_el2 &= MDCR_GUEST_FLAGS_TRACKED_MASK;
+	vcpu->arch.mdcr_el2 |= __this_cpu_read(mdcr_el2) & MDCR_EL2_HPMN_MASK;
 
 	/* Is the VM being debugged by userspace? */
 	if (vcpu->guest_debug)
-- 
2.34.1.448.ga2b2bfdf31-goog

_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 18/26] KVM: arm64: Use vcpu->arch.mdcr_el2 to track value of mdcr_el2
@ 2022-01-06  4:27   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:27 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

Track the baseline guest value for mdcr_el2 in struct kvm_vcpu_arch.
Use this value when setting mdcr_el2 for the guest.

Currently this value is unchanged, but the following patches will set
trapping bits based on features supported for the guest.

No functional change intended.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/include/asm/kvm_arm.h | 16 ++++++++++++++++
 arch/arm64/kvm/arm.c             |  1 +
 arch/arm64/kvm/debug.c           | 13 ++++---------
 3 files changed, 21 insertions(+), 9 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_arm.h b/arch/arm64/include/asm/kvm_arm.h
index 8ab6ea038721..4b2ac9e32a36 100644
--- a/arch/arm64/include/asm/kvm_arm.h
+++ b/arch/arm64/include/asm/kvm_arm.h
@@ -333,6 +333,22 @@
 				 BIT(18) |		\
 				 GENMASK(16, 15))
 
+/*
+ * The default value for the guest below also clears MDCR_EL2_E2PB_MASK
+ * and MDCR_EL2_E2TB_MASK to disable guest access to the profiling and
+ * trace buffers.
+ */
+#define MDCR_GUEST_FLAGS_DEFAULT				\
+	(MDCR_EL2_TPM  | MDCR_EL2_TPMS | MDCR_EL2_TTRF |	\
+	 MDCR_EL2_TPMCR | MDCR_EL2_TDRA | MDCR_EL2_TDOSA)
+
+/* Bits that are copied from vcpu->arch.mdcr_el2 to set mdcr_el2 for guest. */
+#define MDCR_GUEST_FLAGS_TRACKED_MASK				\
+	(MDCR_EL2_TPM  | MDCR_EL2_TPMS | MDCR_EL2_TTRF |	\
+	 MDCR_EL2_TPMCR | MDCR_EL2_TDRA | MDCR_EL2_TDOSA |	\
+	 (MDCR_EL2_E2PB_MASK << MDCR_EL2_E2PB_SHIFT))
+
+
 /* For compatibility with fault code shared with 32-bit */
 #define FSC_FAULT	ESR_ELx_FSC_FAULT
 #define FSC_ACCESS	ESR_ELx_FSC_ACCESS
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 3700144f6271..33acbae7a7ed 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -1136,6 +1136,7 @@ static int kvm_arch_vcpu_ioctl_vcpu_init(struct kvm_vcpu *vcpu,
 	}
 
 	vcpu_reset_hcr(vcpu);
+	vcpu->arch.mdcr_el2 = MDCR_GUEST_FLAGS_DEFAULT;
 	if (has_vhe())
 		vcpu->arch.cptr_el2 = CPTR_EL2_VHE_GUEST_DEFAULT;
 	else
diff --git a/arch/arm64/kvm/debug.c b/arch/arm64/kvm/debug.c
index db9361338b2a..83330968a411 100644
--- a/arch/arm64/kvm/debug.c
+++ b/arch/arm64/kvm/debug.c
@@ -84,16 +84,11 @@ void kvm_arm_init_debug(void)
 static void kvm_arm_setup_mdcr_el2(struct kvm_vcpu *vcpu)
 {
 	/*
-	 * This also clears MDCR_EL2_E2PB_MASK and MDCR_EL2_E2TB_MASK
-	 * to disable guest access to the profiling and trace buffers
+	 * Keep the vcpu->arch.mdcr_el2 bits that are specified by
+	 * MDCR_GUEST_FLAGS_TRACKED_MASK.
 	 */
-	vcpu->arch.mdcr_el2 = __this_cpu_read(mdcr_el2) & MDCR_EL2_HPMN_MASK;
-	vcpu->arch.mdcr_el2 |= (MDCR_EL2_TPM |
-				MDCR_EL2_TPMS |
-				MDCR_EL2_TTRF |
-				MDCR_EL2_TPMCR |
-				MDCR_EL2_TDRA |
-				MDCR_EL2_TDOSA);
+	vcpu->arch.mdcr_el2 &= MDCR_GUEST_FLAGS_TRACKED_MASK;
+	vcpu->arch.mdcr_el2 |= __this_cpu_read(mdcr_el2) & MDCR_EL2_HPMN_MASK;
 
 	/* Is the VM being debugged by userspace? */
 	if (vcpu->guest_debug)
-- 
2.34.1.448.ga2b2bfdf31-goog


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 19/26] KVM: arm64: Introduce framework to trap disabled features
  2022-01-06  4:26 ` Reiji Watanabe
  (?)
@ 2022-01-06  4:27   ` Reiji Watanabe
  -1 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:27 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

When a CPU feature that is supported on the host is not exposed to
its guest, emulating a real CPU's behavior (by trapping or disabling
guest's using the feature) is generally a desirable behavior (when
it's possible without any or little side effect).

Introduce feature_config_ctrl structure, which manages feature
information to program configuration register to trap or disable
the feature when the feature is not exposed to the guest, and
functions that uses the structure to activate the vcpu's trapping the
feature.  Those codes don't update trap configuration registers
themselves (HCR_EL2, etc) but values for the registers in
kvm_vcpu_arch at the first KVM_RUN.

At present, no feature has feature_config_ctrl yet and the following
patches will add the feature_config_ctrl for some features.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/include/asm/kvm_host.h |   1 +
 arch/arm64/kvm/arm.c              |  13 ++--
 arch/arm64/kvm/sys_regs.c         | 105 +++++++++++++++++++++++++++++-
 3 files changed, 113 insertions(+), 6 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 7b3f86bd6a6b..0274009cb05d 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -751,6 +751,7 @@ long kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
 void set_default_id_regs(struct kvm *kvm);
 int kvm_set_id_reg_feature(struct kvm *kvm, u32 id, u8 field_shift, u8 fval);
 int kvm_id_regs_consistency_check(const struct kvm_vcpu *vcpu);
+void kvm_vcpu_init_traps(struct kvm_vcpu *vcpu);
 
 /* Guest/host FPSIMD coordination helpers */
 int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu);
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 33acbae7a7ed..e2f52eb84618 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -626,13 +626,16 @@ static int kvm_vcpu_first_run_init(struct kvm_vcpu *vcpu)
 
 	ret = kvm_arm_pmu_v3_enable(vcpu);
 
-	/*
-	 * Initialize traps for protected VMs.
-	 * NOTE: Move to run in EL2 directly, rather than via a hypercall, once
-	 * the code is in place for first run initialization at EL2.
-	 */
+	/* Initialize traps for the guest. */
 	if (kvm_vm_is_protected(kvm))
+		/*
+		 * NOTE: Move to run in EL2 directly, rather than via a
+		 * hypercall, once the code is in place for first run
+		 * initialization at EL2.
+		 */
 		kvm_call_hyp_nvhe(__pkvm_vcpu_init_traps, vcpu);
+	else
+		kvm_vcpu_init_traps(vcpu);
 
 	return ret;
 }
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index d5bd04c68cd4..33893a501475 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -283,8 +283,30 @@ static bool trap_raz_wi(struct kvm_vcpu *vcpu,
 	(cpuid_feature_extract_unsigned_field(val, ID_AA64ISAR1_GPI_SHIFT) >= \
 	 ID_AA64ISAR1_GPI_IMP_DEF)
 
+/*
+ * Feature information to program configuration register to trap or disable
+ * guest's using a feature when the feature is not exposed to the guest.
+ */
+struct feature_config_ctrl {
+	/* ID register/field for the feature */
+	u32	ftr_reg;	/* ID register */
+	bool	ftr_signed;	/* Is the feature field signed ? */
+	u8	ftr_shift;	/* Field of ID register for the feature */
+	s8	ftr_min;	/* Min value that indicate the feature */
+
+	/*
+	 * Function to check trapping is needed. This is used when the above
+	 * fields are not enough to determine if trapping is needed.
+	 */
+	bool	(*ftr_need_trap)(struct kvm_vcpu *vcpu);
+
+	/* Function to activate trapping the feature. */
+	void	(*trap_activate)(struct kvm_vcpu *vcpu);
+};
+
 struct id_reg_info {
 	u32	sys_reg;	/* Register ID */
+	u64	sys_val;	/* Sanitized system value */
 
 	/*
 	 * Limit value of the register for a vcpu. The value is the sanitized
@@ -327,11 +349,15 @@ struct id_reg_info {
 	 */
 	u64 (*vcpu_mask)(const struct kvm_vcpu *vcpu,
 			 const struct id_reg_info *id_reg);
+
+	/* Information to trap features that are disabled for the guest */
+	const struct feature_config_ctrl *(*trap_features)[];
 };
 
 static void id_reg_info_init(struct id_reg_info *id_reg)
 {
-	id_reg->vcpu_limit_val = read_sanitised_ftr_reg(id_reg->sys_reg);
+	id_reg->sys_val = read_sanitised_ftr_reg(id_reg->sys_reg);
+	id_reg->vcpu_limit_val = id_reg->sys_val;
 	if (id_reg->init)
 		id_reg->init(id_reg);
 }
@@ -857,6 +883,24 @@ static int validate_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
 	return err;
 }
 
+static inline bool feature_avail(const struct feature_config_ctrl *ctrl,
+				 u64 id_val)
+{
+	int field_val = cpuid_feature_extract_field(id_val,
+				ctrl->ftr_shift, ctrl->ftr_signed);
+
+	return (field_val >= ctrl->ftr_min);
+}
+
+static inline bool vcpu_feature_is_available(struct kvm_vcpu *vcpu,
+					const struct feature_config_ctrl *ctrl)
+{
+	u64 val;
+
+	val = __read_id_reg(vcpu, ctrl->ftr_reg);
+	return feature_avail(ctrl, val);
+}
+
 /*
  * ARMv8.1 mandates at least a trivial LORegion implementation, where all the
  * RW registers are RES0 (which we can implement as RAZ/WI). On an ARMv8.0
@@ -1799,6 +1843,46 @@ static int reg_from_user(u64 *val, const void __user *uaddr, u64 id);
 static int reg_to_user(void __user *uaddr, const u64 *val, u64 id);
 static u64 sys_reg_to_index(const struct sys_reg_desc *reg);
 
+static void id_reg_features_trap_activate(struct kvm_vcpu *vcpu,
+					  const struct id_reg_info *id_reg)
+{
+	u64 val;
+	int i = 0;
+	const struct feature_config_ctrl **ctrlp_array, *ctrl;
+
+	if (!id_reg || !id_reg->trap_features)
+		/* No information to trap a feature */
+		return;
+
+	val = __read_id_reg(vcpu, id_reg->sys_reg);
+	if (val == id_reg->sys_val)
+		/* No feature needs to be trapped (no feature is disabled). */
+		return;
+
+	ctrlp_array = *id_reg->trap_features;
+	while ((ctrl = ctrlp_array[i++]) != NULL) {
+		if (WARN_ON_ONCE(!ctrl->trap_activate))
+			/* Shouldn't happen */
+			continue;
+
+		if (ctrl->ftr_need_trap && ctrl->ftr_need_trap(vcpu)) {
+			ctrl->trap_activate(vcpu);
+			continue;
+		}
+
+		if (!feature_avail(ctrl, id_reg->sys_val))
+			/* The feature is not supported on the host. */
+			continue;
+
+		if (feature_avail(ctrl, val))
+			/* The feature is enabled for the guest. */
+			continue;
+
+		/* The feature is supported but disabled. */
+		ctrl->trap_activate(vcpu);
+	}
+}
+
 /* Visibility overrides for SVE-specific control registers */
 static unsigned int sve_visibility(const struct kvm_vcpu *vcpu,
 				   const struct sys_reg_desc *rd)
@@ -3431,6 +3515,25 @@ int kvm_arm_copy_sys_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
 	return write_demux_regids(uindices);
 }
 
+/*
+ * This function activates vcpu's trapping of features that are included in
+ * trap_features[] of id_reg_info if the features are supported on the
+ * host, but are hidden from the guest (i.e. values of ID registers for
+ * the guest are modified to not show the features' availability).
+ * This function just updates values for trap configuration registers (e.g.
+ * HCR_EL2, etc) in kvm_vcpu_arch, which will be restored before switching
+ * to the guest, but doesn't update the registers themselves.
+ * This function should be called once at the first KVM_RUN (ID registers
+ * are immutable after the first KVM_RUN).
+ */
+void kvm_vcpu_init_traps(struct kvm_vcpu *vcpu)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(id_reg_info_table); i++)
+		id_reg_features_trap_activate(vcpu, id_reg_info_table[i]);
+}
+
 /* ID register's fractional field information with its feature field. */
 struct feature_frac {
 	u32	id;
-- 
2.34.1.448.ga2b2bfdf31-goog


^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 19/26] KVM: arm64: Introduce framework to trap disabled features
@ 2022-01-06  4:27   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:27 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, Will Deacon, Peter Shier, Paolo Bonzini, linux-arm-kernel

When a CPU feature that is supported on the host is not exposed to
its guest, emulating a real CPU's behavior (by trapping or disabling
guest's using the feature) is generally a desirable behavior (when
it's possible without any or little side effect).

Introduce feature_config_ctrl structure, which manages feature
information to program configuration register to trap or disable
the feature when the feature is not exposed to the guest, and
functions that uses the structure to activate the vcpu's trapping the
feature.  Those codes don't update trap configuration registers
themselves (HCR_EL2, etc) but values for the registers in
kvm_vcpu_arch at the first KVM_RUN.

At present, no feature has feature_config_ctrl yet and the following
patches will add the feature_config_ctrl for some features.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/include/asm/kvm_host.h |   1 +
 arch/arm64/kvm/arm.c              |  13 ++--
 arch/arm64/kvm/sys_regs.c         | 105 +++++++++++++++++++++++++++++-
 3 files changed, 113 insertions(+), 6 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 7b3f86bd6a6b..0274009cb05d 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -751,6 +751,7 @@ long kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
 void set_default_id_regs(struct kvm *kvm);
 int kvm_set_id_reg_feature(struct kvm *kvm, u32 id, u8 field_shift, u8 fval);
 int kvm_id_regs_consistency_check(const struct kvm_vcpu *vcpu);
+void kvm_vcpu_init_traps(struct kvm_vcpu *vcpu);
 
 /* Guest/host FPSIMD coordination helpers */
 int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu);
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 33acbae7a7ed..e2f52eb84618 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -626,13 +626,16 @@ static int kvm_vcpu_first_run_init(struct kvm_vcpu *vcpu)
 
 	ret = kvm_arm_pmu_v3_enable(vcpu);
 
-	/*
-	 * Initialize traps for protected VMs.
-	 * NOTE: Move to run in EL2 directly, rather than via a hypercall, once
-	 * the code is in place for first run initialization at EL2.
-	 */
+	/* Initialize traps for the guest. */
 	if (kvm_vm_is_protected(kvm))
+		/*
+		 * NOTE: Move to run in EL2 directly, rather than via a
+		 * hypercall, once the code is in place for first run
+		 * initialization at EL2.
+		 */
 		kvm_call_hyp_nvhe(__pkvm_vcpu_init_traps, vcpu);
+	else
+		kvm_vcpu_init_traps(vcpu);
 
 	return ret;
 }
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index d5bd04c68cd4..33893a501475 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -283,8 +283,30 @@ static bool trap_raz_wi(struct kvm_vcpu *vcpu,
 	(cpuid_feature_extract_unsigned_field(val, ID_AA64ISAR1_GPI_SHIFT) >= \
 	 ID_AA64ISAR1_GPI_IMP_DEF)
 
+/*
+ * Feature information to program configuration register to trap or disable
+ * guest's using a feature when the feature is not exposed to the guest.
+ */
+struct feature_config_ctrl {
+	/* ID register/field for the feature */
+	u32	ftr_reg;	/* ID register */
+	bool	ftr_signed;	/* Is the feature field signed ? */
+	u8	ftr_shift;	/* Field of ID register for the feature */
+	s8	ftr_min;	/* Min value that indicate the feature */
+
+	/*
+	 * Function to check trapping is needed. This is used when the above
+	 * fields are not enough to determine if trapping is needed.
+	 */
+	bool	(*ftr_need_trap)(struct kvm_vcpu *vcpu);
+
+	/* Function to activate trapping the feature. */
+	void	(*trap_activate)(struct kvm_vcpu *vcpu);
+};
+
 struct id_reg_info {
 	u32	sys_reg;	/* Register ID */
+	u64	sys_val;	/* Sanitized system value */
 
 	/*
 	 * Limit value of the register for a vcpu. The value is the sanitized
@@ -327,11 +349,15 @@ struct id_reg_info {
 	 */
 	u64 (*vcpu_mask)(const struct kvm_vcpu *vcpu,
 			 const struct id_reg_info *id_reg);
+
+	/* Information to trap features that are disabled for the guest */
+	const struct feature_config_ctrl *(*trap_features)[];
 };
 
 static void id_reg_info_init(struct id_reg_info *id_reg)
 {
-	id_reg->vcpu_limit_val = read_sanitised_ftr_reg(id_reg->sys_reg);
+	id_reg->sys_val = read_sanitised_ftr_reg(id_reg->sys_reg);
+	id_reg->vcpu_limit_val = id_reg->sys_val;
 	if (id_reg->init)
 		id_reg->init(id_reg);
 }
@@ -857,6 +883,24 @@ static int validate_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
 	return err;
 }
 
+static inline bool feature_avail(const struct feature_config_ctrl *ctrl,
+				 u64 id_val)
+{
+	int field_val = cpuid_feature_extract_field(id_val,
+				ctrl->ftr_shift, ctrl->ftr_signed);
+
+	return (field_val >= ctrl->ftr_min);
+}
+
+static inline bool vcpu_feature_is_available(struct kvm_vcpu *vcpu,
+					const struct feature_config_ctrl *ctrl)
+{
+	u64 val;
+
+	val = __read_id_reg(vcpu, ctrl->ftr_reg);
+	return feature_avail(ctrl, val);
+}
+
 /*
  * ARMv8.1 mandates at least a trivial LORegion implementation, where all the
  * RW registers are RES0 (which we can implement as RAZ/WI). On an ARMv8.0
@@ -1799,6 +1843,46 @@ static int reg_from_user(u64 *val, const void __user *uaddr, u64 id);
 static int reg_to_user(void __user *uaddr, const u64 *val, u64 id);
 static u64 sys_reg_to_index(const struct sys_reg_desc *reg);
 
+static void id_reg_features_trap_activate(struct kvm_vcpu *vcpu,
+					  const struct id_reg_info *id_reg)
+{
+	u64 val;
+	int i = 0;
+	const struct feature_config_ctrl **ctrlp_array, *ctrl;
+
+	if (!id_reg || !id_reg->trap_features)
+		/* No information to trap a feature */
+		return;
+
+	val = __read_id_reg(vcpu, id_reg->sys_reg);
+	if (val == id_reg->sys_val)
+		/* No feature needs to be trapped (no feature is disabled). */
+		return;
+
+	ctrlp_array = *id_reg->trap_features;
+	while ((ctrl = ctrlp_array[i++]) != NULL) {
+		if (WARN_ON_ONCE(!ctrl->trap_activate))
+			/* Shouldn't happen */
+			continue;
+
+		if (ctrl->ftr_need_trap && ctrl->ftr_need_trap(vcpu)) {
+			ctrl->trap_activate(vcpu);
+			continue;
+		}
+
+		if (!feature_avail(ctrl, id_reg->sys_val))
+			/* The feature is not supported on the host. */
+			continue;
+
+		if (feature_avail(ctrl, val))
+			/* The feature is enabled for the guest. */
+			continue;
+
+		/* The feature is supported but disabled. */
+		ctrl->trap_activate(vcpu);
+	}
+}
+
 /* Visibility overrides for SVE-specific control registers */
 static unsigned int sve_visibility(const struct kvm_vcpu *vcpu,
 				   const struct sys_reg_desc *rd)
@@ -3431,6 +3515,25 @@ int kvm_arm_copy_sys_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
 	return write_demux_regids(uindices);
 }
 
+/*
+ * This function activates vcpu's trapping of features that are included in
+ * trap_features[] of id_reg_info if the features are supported on the
+ * host, but are hidden from the guest (i.e. values of ID registers for
+ * the guest are modified to not show the features' availability).
+ * This function just updates values for trap configuration registers (e.g.
+ * HCR_EL2, etc) in kvm_vcpu_arch, which will be restored before switching
+ * to the guest, but doesn't update the registers themselves.
+ * This function should be called once at the first KVM_RUN (ID registers
+ * are immutable after the first KVM_RUN).
+ */
+void kvm_vcpu_init_traps(struct kvm_vcpu *vcpu)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(id_reg_info_table); i++)
+		id_reg_features_trap_activate(vcpu, id_reg_info_table[i]);
+}
+
 /* ID register's fractional field information with its feature field. */
 struct feature_frac {
 	u32	id;
-- 
2.34.1.448.ga2b2bfdf31-goog

_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 19/26] KVM: arm64: Introduce framework to trap disabled features
@ 2022-01-06  4:27   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:27 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

When a CPU feature that is supported on the host is not exposed to
its guest, emulating a real CPU's behavior (by trapping or disabling
guest's using the feature) is generally a desirable behavior (when
it's possible without any or little side effect).

Introduce feature_config_ctrl structure, which manages feature
information to program configuration register to trap or disable
the feature when the feature is not exposed to the guest, and
functions that uses the structure to activate the vcpu's trapping the
feature.  Those codes don't update trap configuration registers
themselves (HCR_EL2, etc) but values for the registers in
kvm_vcpu_arch at the first KVM_RUN.

At present, no feature has feature_config_ctrl yet and the following
patches will add the feature_config_ctrl for some features.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/include/asm/kvm_host.h |   1 +
 arch/arm64/kvm/arm.c              |  13 ++--
 arch/arm64/kvm/sys_regs.c         | 105 +++++++++++++++++++++++++++++-
 3 files changed, 113 insertions(+), 6 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 7b3f86bd6a6b..0274009cb05d 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -751,6 +751,7 @@ long kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
 void set_default_id_regs(struct kvm *kvm);
 int kvm_set_id_reg_feature(struct kvm *kvm, u32 id, u8 field_shift, u8 fval);
 int kvm_id_regs_consistency_check(const struct kvm_vcpu *vcpu);
+void kvm_vcpu_init_traps(struct kvm_vcpu *vcpu);
 
 /* Guest/host FPSIMD coordination helpers */
 int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu);
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 33acbae7a7ed..e2f52eb84618 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -626,13 +626,16 @@ static int kvm_vcpu_first_run_init(struct kvm_vcpu *vcpu)
 
 	ret = kvm_arm_pmu_v3_enable(vcpu);
 
-	/*
-	 * Initialize traps for protected VMs.
-	 * NOTE: Move to run in EL2 directly, rather than via a hypercall, once
-	 * the code is in place for first run initialization at EL2.
-	 */
+	/* Initialize traps for the guest. */
 	if (kvm_vm_is_protected(kvm))
+		/*
+		 * NOTE: Move to run in EL2 directly, rather than via a
+		 * hypercall, once the code is in place for first run
+		 * initialization at EL2.
+		 */
 		kvm_call_hyp_nvhe(__pkvm_vcpu_init_traps, vcpu);
+	else
+		kvm_vcpu_init_traps(vcpu);
 
 	return ret;
 }
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index d5bd04c68cd4..33893a501475 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -283,8 +283,30 @@ static bool trap_raz_wi(struct kvm_vcpu *vcpu,
 	(cpuid_feature_extract_unsigned_field(val, ID_AA64ISAR1_GPI_SHIFT) >= \
 	 ID_AA64ISAR1_GPI_IMP_DEF)
 
+/*
+ * Feature information to program configuration register to trap or disable
+ * guest's using a feature when the feature is not exposed to the guest.
+ */
+struct feature_config_ctrl {
+	/* ID register/field for the feature */
+	u32	ftr_reg;	/* ID register */
+	bool	ftr_signed;	/* Is the feature field signed ? */
+	u8	ftr_shift;	/* Field of ID register for the feature */
+	s8	ftr_min;	/* Min value that indicate the feature */
+
+	/*
+	 * Function to check trapping is needed. This is used when the above
+	 * fields are not enough to determine if trapping is needed.
+	 */
+	bool	(*ftr_need_trap)(struct kvm_vcpu *vcpu);
+
+	/* Function to activate trapping the feature. */
+	void	(*trap_activate)(struct kvm_vcpu *vcpu);
+};
+
 struct id_reg_info {
 	u32	sys_reg;	/* Register ID */
+	u64	sys_val;	/* Sanitized system value */
 
 	/*
 	 * Limit value of the register for a vcpu. The value is the sanitized
@@ -327,11 +349,15 @@ struct id_reg_info {
 	 */
 	u64 (*vcpu_mask)(const struct kvm_vcpu *vcpu,
 			 const struct id_reg_info *id_reg);
+
+	/* Information to trap features that are disabled for the guest */
+	const struct feature_config_ctrl *(*trap_features)[];
 };
 
 static void id_reg_info_init(struct id_reg_info *id_reg)
 {
-	id_reg->vcpu_limit_val = read_sanitised_ftr_reg(id_reg->sys_reg);
+	id_reg->sys_val = read_sanitised_ftr_reg(id_reg->sys_reg);
+	id_reg->vcpu_limit_val = id_reg->sys_val;
 	if (id_reg->init)
 		id_reg->init(id_reg);
 }
@@ -857,6 +883,24 @@ static int validate_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
 	return err;
 }
 
+static inline bool feature_avail(const struct feature_config_ctrl *ctrl,
+				 u64 id_val)
+{
+	int field_val = cpuid_feature_extract_field(id_val,
+				ctrl->ftr_shift, ctrl->ftr_signed);
+
+	return (field_val >= ctrl->ftr_min);
+}
+
+static inline bool vcpu_feature_is_available(struct kvm_vcpu *vcpu,
+					const struct feature_config_ctrl *ctrl)
+{
+	u64 val;
+
+	val = __read_id_reg(vcpu, ctrl->ftr_reg);
+	return feature_avail(ctrl, val);
+}
+
 /*
  * ARMv8.1 mandates at least a trivial LORegion implementation, where all the
  * RW registers are RES0 (which we can implement as RAZ/WI). On an ARMv8.0
@@ -1799,6 +1843,46 @@ static int reg_from_user(u64 *val, const void __user *uaddr, u64 id);
 static int reg_to_user(void __user *uaddr, const u64 *val, u64 id);
 static u64 sys_reg_to_index(const struct sys_reg_desc *reg);
 
+static void id_reg_features_trap_activate(struct kvm_vcpu *vcpu,
+					  const struct id_reg_info *id_reg)
+{
+	u64 val;
+	int i = 0;
+	const struct feature_config_ctrl **ctrlp_array, *ctrl;
+
+	if (!id_reg || !id_reg->trap_features)
+		/* No information to trap a feature */
+		return;
+
+	val = __read_id_reg(vcpu, id_reg->sys_reg);
+	if (val == id_reg->sys_val)
+		/* No feature needs to be trapped (no feature is disabled). */
+		return;
+
+	ctrlp_array = *id_reg->trap_features;
+	while ((ctrl = ctrlp_array[i++]) != NULL) {
+		if (WARN_ON_ONCE(!ctrl->trap_activate))
+			/* Shouldn't happen */
+			continue;
+
+		if (ctrl->ftr_need_trap && ctrl->ftr_need_trap(vcpu)) {
+			ctrl->trap_activate(vcpu);
+			continue;
+		}
+
+		if (!feature_avail(ctrl, id_reg->sys_val))
+			/* The feature is not supported on the host. */
+			continue;
+
+		if (feature_avail(ctrl, val))
+			/* The feature is enabled for the guest. */
+			continue;
+
+		/* The feature is supported but disabled. */
+		ctrl->trap_activate(vcpu);
+	}
+}
+
 /* Visibility overrides for SVE-specific control registers */
 static unsigned int sve_visibility(const struct kvm_vcpu *vcpu,
 				   const struct sys_reg_desc *rd)
@@ -3431,6 +3515,25 @@ int kvm_arm_copy_sys_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
 	return write_demux_regids(uindices);
 }
 
+/*
+ * This function activates vcpu's trapping of features that are included in
+ * trap_features[] of id_reg_info if the features are supported on the
+ * host, but are hidden from the guest (i.e. values of ID registers for
+ * the guest are modified to not show the features' availability).
+ * This function just updates values for trap configuration registers (e.g.
+ * HCR_EL2, etc) in kvm_vcpu_arch, which will be restored before switching
+ * to the guest, but doesn't update the registers themselves.
+ * This function should be called once at the first KVM_RUN (ID registers
+ * are immutable after the first KVM_RUN).
+ */
+void kvm_vcpu_init_traps(struct kvm_vcpu *vcpu)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(id_reg_info_table); i++)
+		id_reg_features_trap_activate(vcpu, id_reg_info_table[i]);
+}
+
 /* ID register's fractional field information with its feature field. */
 struct feature_frac {
 	u32	id;
-- 
2.34.1.448.ga2b2bfdf31-goog


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 20/26] KVM: arm64: Trap disabled features of ID_AA64PFR0_EL1
  2022-01-06  4:26 ` Reiji Watanabe
  (?)
@ 2022-01-06  4:27   ` Reiji Watanabe
  -1 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:27 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

Add feature_config_ctrl for RAS and AMU, which are indicated in
ID_AA64PFR0_EL1, to program configuration registers to trap
guest's using those features when they are not exposed to the guest.

Introduce trap_ras_regs() to change a behavior of guest's access to
the registers, which is currently raz/wi, depending on the feature's
availability for the guest (and inject undefined instruction
exception when guest's RAS register access are trapped and RAS is
not exposed to the guest).  In order to keep the current visibility
of the RAS registers from userspace (always visible), a visibility
function for RAS registers is not added.

No code is added for AMU's access/visibility handler because the
current code already injects the exception for Guest's AMU register
access unconditionally because AMU is never exposed to the guest.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/kvm/sys_regs.c | 90 +++++++++++++++++++++++++++++++++++----
 1 file changed, 82 insertions(+), 8 deletions(-)

diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 33893a501475..015d67092d5e 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -304,6 +304,63 @@ struct feature_config_ctrl {
 	void	(*trap_activate)(struct kvm_vcpu *vcpu);
 };
 
+enum vcpu_config_reg {
+	VCPU_HCR_EL2 = 1,
+	VCPU_MDCR_EL2,
+	VCPU_CPTR_EL2,
+};
+
+static void feature_trap_activate(struct kvm_vcpu *vcpu,
+				  enum vcpu_config_reg cfg_reg,
+				  u64 cfg_set, u64 cfg_clear)
+{
+	u64 *reg_ptr, reg_val;
+
+	switch (cfg_reg) {
+	case VCPU_HCR_EL2:
+		reg_ptr = &vcpu->arch.hcr_el2;
+		break;
+	case VCPU_MDCR_EL2:
+		reg_ptr = &vcpu->arch.mdcr_el2;
+		break;
+	case VCPU_CPTR_EL2:
+		reg_ptr = &vcpu->arch.cptr_el2;
+		break;
+	}
+
+	/* Clear/Set fields that are indicated by cfg_clear/cfg_set. */
+	reg_val = (*reg_ptr & ~cfg_clear);
+	reg_val |= cfg_set;
+	*reg_ptr = reg_val;
+}
+
+static void feature_ras_trap_activate(struct kvm_vcpu *vcpu)
+{
+	feature_trap_activate(vcpu, VCPU_HCR_EL2, HCR_TERR | HCR_TEA, HCR_FIEN);
+}
+
+static void feature_amu_trap_activate(struct kvm_vcpu *vcpu)
+{
+	feature_trap_activate(vcpu, VCPU_CPTR_EL2, CPTR_EL2_TAM, 0);
+}
+
+/* For ID_AA64PFR0_EL1 */
+static struct feature_config_ctrl ftr_ctrl_ras = {
+	.ftr_reg = SYS_ID_AA64PFR0_EL1,
+	.ftr_shift = ID_AA64PFR0_RAS_SHIFT,
+	.ftr_min = ID_AA64PFR0_RAS_V1,
+	.ftr_signed = FTR_UNSIGNED,
+	.trap_activate = feature_ras_trap_activate,
+};
+
+static struct feature_config_ctrl ftr_ctrl_amu = {
+	.ftr_reg = SYS_ID_AA64PFR0_EL1,
+	.ftr_shift = ID_AA64PFR0_AMU_SHIFT,
+	.ftr_min = ID_AA64PFR0_AMU,
+	.ftr_signed = FTR_UNSIGNED,
+	.trap_activate = feature_amu_trap_activate,
+};
+
 struct id_reg_info {
 	u32	sys_reg;	/* Register ID */
 	u64	sys_val;	/* Sanitized system value */
@@ -778,6 +835,11 @@ static struct id_reg_info id_aa64pfr0_el1_info = {
 	.init = init_id_aa64pfr0_el1_info,
 	.validate = validate_id_aa64pfr0_el1,
 	.vcpu_mask = vcpu_mask_id_aa64pfr0_el1,
+	.trap_features = &(const struct feature_config_ctrl *[]) {
+		&ftr_ctrl_ras,
+		&ftr_ctrl_amu,
+		NULL,
+	},
 };
 
 static struct id_reg_info id_aa64pfr1_el1_info = {
@@ -901,6 +963,18 @@ static inline bool vcpu_feature_is_available(struct kvm_vcpu *vcpu,
 	return feature_avail(ctrl, val);
 }
 
+static bool trap_ras_regs(struct kvm_vcpu *vcpu,
+			  struct sys_reg_params *p,
+			  const struct sys_reg_desc *r)
+{
+	if (!vcpu_feature_is_available(vcpu, &ftr_ctrl_ras)) {
+		kvm_inject_undefined(vcpu);
+		return false;
+	}
+
+	return trap_raz_wi(vcpu, p, r);
+}
+
 /*
  * ARMv8.1 mandates at least a trivial LORegion implementation, where all the
  * RW registers are RES0 (which we can implement as RAZ/WI). On an ARMv8.0
@@ -2265,14 +2339,14 @@ static const struct sys_reg_desc sys_reg_descs[] = {
 	{ SYS_DESC(SYS_AFSR1_EL1), access_vm_reg, reset_unknown, AFSR1_EL1 },
 	{ SYS_DESC(SYS_ESR_EL1), access_vm_reg, reset_unknown, ESR_EL1 },
 
-	{ SYS_DESC(SYS_ERRIDR_EL1), trap_raz_wi },
-	{ SYS_DESC(SYS_ERRSELR_EL1), trap_raz_wi },
-	{ SYS_DESC(SYS_ERXFR_EL1), trap_raz_wi },
-	{ SYS_DESC(SYS_ERXCTLR_EL1), trap_raz_wi },
-	{ SYS_DESC(SYS_ERXSTATUS_EL1), trap_raz_wi },
-	{ SYS_DESC(SYS_ERXADDR_EL1), trap_raz_wi },
-	{ SYS_DESC(SYS_ERXMISC0_EL1), trap_raz_wi },
-	{ SYS_DESC(SYS_ERXMISC1_EL1), trap_raz_wi },
+	{ SYS_DESC(SYS_ERRIDR_EL1), trap_ras_regs },
+	{ SYS_DESC(SYS_ERRSELR_EL1), trap_ras_regs },
+	{ SYS_DESC(SYS_ERXFR_EL1), trap_ras_regs },
+	{ SYS_DESC(SYS_ERXCTLR_EL1), trap_ras_regs },
+	{ SYS_DESC(SYS_ERXSTATUS_EL1), trap_ras_regs },
+	{ SYS_DESC(SYS_ERXADDR_EL1), trap_ras_regs },
+	{ SYS_DESC(SYS_ERXMISC0_EL1), trap_ras_regs },
+	{ SYS_DESC(SYS_ERXMISC1_EL1), trap_ras_regs },
 
 	MTE_REG(TFSR_EL1),
 	MTE_REG(TFSRE0_EL1),
-- 
2.34.1.448.ga2b2bfdf31-goog


^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 20/26] KVM: arm64: Trap disabled features of ID_AA64PFR0_EL1
@ 2022-01-06  4:27   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:27 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, Will Deacon, Peter Shier, Paolo Bonzini, linux-arm-kernel

Add feature_config_ctrl for RAS and AMU, which are indicated in
ID_AA64PFR0_EL1, to program configuration registers to trap
guest's using those features when they are not exposed to the guest.

Introduce trap_ras_regs() to change a behavior of guest's access to
the registers, which is currently raz/wi, depending on the feature's
availability for the guest (and inject undefined instruction
exception when guest's RAS register access are trapped and RAS is
not exposed to the guest).  In order to keep the current visibility
of the RAS registers from userspace (always visible), a visibility
function for RAS registers is not added.

No code is added for AMU's access/visibility handler because the
current code already injects the exception for Guest's AMU register
access unconditionally because AMU is never exposed to the guest.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/kvm/sys_regs.c | 90 +++++++++++++++++++++++++++++++++++----
 1 file changed, 82 insertions(+), 8 deletions(-)

diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 33893a501475..015d67092d5e 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -304,6 +304,63 @@ struct feature_config_ctrl {
 	void	(*trap_activate)(struct kvm_vcpu *vcpu);
 };
 
+enum vcpu_config_reg {
+	VCPU_HCR_EL2 = 1,
+	VCPU_MDCR_EL2,
+	VCPU_CPTR_EL2,
+};
+
+static void feature_trap_activate(struct kvm_vcpu *vcpu,
+				  enum vcpu_config_reg cfg_reg,
+				  u64 cfg_set, u64 cfg_clear)
+{
+	u64 *reg_ptr, reg_val;
+
+	switch (cfg_reg) {
+	case VCPU_HCR_EL2:
+		reg_ptr = &vcpu->arch.hcr_el2;
+		break;
+	case VCPU_MDCR_EL2:
+		reg_ptr = &vcpu->arch.mdcr_el2;
+		break;
+	case VCPU_CPTR_EL2:
+		reg_ptr = &vcpu->arch.cptr_el2;
+		break;
+	}
+
+	/* Clear/Set fields that are indicated by cfg_clear/cfg_set. */
+	reg_val = (*reg_ptr & ~cfg_clear);
+	reg_val |= cfg_set;
+	*reg_ptr = reg_val;
+}
+
+static void feature_ras_trap_activate(struct kvm_vcpu *vcpu)
+{
+	feature_trap_activate(vcpu, VCPU_HCR_EL2, HCR_TERR | HCR_TEA, HCR_FIEN);
+}
+
+static void feature_amu_trap_activate(struct kvm_vcpu *vcpu)
+{
+	feature_trap_activate(vcpu, VCPU_CPTR_EL2, CPTR_EL2_TAM, 0);
+}
+
+/* For ID_AA64PFR0_EL1 */
+static struct feature_config_ctrl ftr_ctrl_ras = {
+	.ftr_reg = SYS_ID_AA64PFR0_EL1,
+	.ftr_shift = ID_AA64PFR0_RAS_SHIFT,
+	.ftr_min = ID_AA64PFR0_RAS_V1,
+	.ftr_signed = FTR_UNSIGNED,
+	.trap_activate = feature_ras_trap_activate,
+};
+
+static struct feature_config_ctrl ftr_ctrl_amu = {
+	.ftr_reg = SYS_ID_AA64PFR0_EL1,
+	.ftr_shift = ID_AA64PFR0_AMU_SHIFT,
+	.ftr_min = ID_AA64PFR0_AMU,
+	.ftr_signed = FTR_UNSIGNED,
+	.trap_activate = feature_amu_trap_activate,
+};
+
 struct id_reg_info {
 	u32	sys_reg;	/* Register ID */
 	u64	sys_val;	/* Sanitized system value */
@@ -778,6 +835,11 @@ static struct id_reg_info id_aa64pfr0_el1_info = {
 	.init = init_id_aa64pfr0_el1_info,
 	.validate = validate_id_aa64pfr0_el1,
 	.vcpu_mask = vcpu_mask_id_aa64pfr0_el1,
+	.trap_features = &(const struct feature_config_ctrl *[]) {
+		&ftr_ctrl_ras,
+		&ftr_ctrl_amu,
+		NULL,
+	},
 };
 
 static struct id_reg_info id_aa64pfr1_el1_info = {
@@ -901,6 +963,18 @@ static inline bool vcpu_feature_is_available(struct kvm_vcpu *vcpu,
 	return feature_avail(ctrl, val);
 }
 
+static bool trap_ras_regs(struct kvm_vcpu *vcpu,
+			  struct sys_reg_params *p,
+			  const struct sys_reg_desc *r)
+{
+	if (!vcpu_feature_is_available(vcpu, &ftr_ctrl_ras)) {
+		kvm_inject_undefined(vcpu);
+		return false;
+	}
+
+	return trap_raz_wi(vcpu, p, r);
+}
+
 /*
  * ARMv8.1 mandates at least a trivial LORegion implementation, where all the
  * RW registers are RES0 (which we can implement as RAZ/WI). On an ARMv8.0
@@ -2265,14 +2339,14 @@ static const struct sys_reg_desc sys_reg_descs[] = {
 	{ SYS_DESC(SYS_AFSR1_EL1), access_vm_reg, reset_unknown, AFSR1_EL1 },
 	{ SYS_DESC(SYS_ESR_EL1), access_vm_reg, reset_unknown, ESR_EL1 },
 
-	{ SYS_DESC(SYS_ERRIDR_EL1), trap_raz_wi },
-	{ SYS_DESC(SYS_ERRSELR_EL1), trap_raz_wi },
-	{ SYS_DESC(SYS_ERXFR_EL1), trap_raz_wi },
-	{ SYS_DESC(SYS_ERXCTLR_EL1), trap_raz_wi },
-	{ SYS_DESC(SYS_ERXSTATUS_EL1), trap_raz_wi },
-	{ SYS_DESC(SYS_ERXADDR_EL1), trap_raz_wi },
-	{ SYS_DESC(SYS_ERXMISC0_EL1), trap_raz_wi },
-	{ SYS_DESC(SYS_ERXMISC1_EL1), trap_raz_wi },
+	{ SYS_DESC(SYS_ERRIDR_EL1), trap_ras_regs },
+	{ SYS_DESC(SYS_ERRSELR_EL1), trap_ras_regs },
+	{ SYS_DESC(SYS_ERXFR_EL1), trap_ras_regs },
+	{ SYS_DESC(SYS_ERXCTLR_EL1), trap_ras_regs },
+	{ SYS_DESC(SYS_ERXSTATUS_EL1), trap_ras_regs },
+	{ SYS_DESC(SYS_ERXADDR_EL1), trap_ras_regs },
+	{ SYS_DESC(SYS_ERXMISC0_EL1), trap_ras_regs },
+	{ SYS_DESC(SYS_ERXMISC1_EL1), trap_ras_regs },
 
 	MTE_REG(TFSR_EL1),
 	MTE_REG(TFSRE0_EL1),
-- 
2.34.1.448.ga2b2bfdf31-goog

_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 20/26] KVM: arm64: Trap disabled features of ID_AA64PFR0_EL1
@ 2022-01-06  4:27   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:27 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

Add feature_config_ctrl for RAS and AMU, which are indicated in
ID_AA64PFR0_EL1, to program configuration registers to trap
guest's using those features when they are not exposed to the guest.

Introduce trap_ras_regs() to change a behavior of guest's access to
the registers, which is currently raz/wi, depending on the feature's
availability for the guest (and inject undefined instruction
exception when guest's RAS register access are trapped and RAS is
not exposed to the guest).  In order to keep the current visibility
of the RAS registers from userspace (always visible), a visibility
function for RAS registers is not added.

No code is added for AMU's access/visibility handler because the
current code already injects the exception for Guest's AMU register
access unconditionally because AMU is never exposed to the guest.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/kvm/sys_regs.c | 90 +++++++++++++++++++++++++++++++++++----
 1 file changed, 82 insertions(+), 8 deletions(-)

diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 33893a501475..015d67092d5e 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -304,6 +304,63 @@ struct feature_config_ctrl {
 	void	(*trap_activate)(struct kvm_vcpu *vcpu);
 };
 
+enum vcpu_config_reg {
+	VCPU_HCR_EL2 = 1,
+	VCPU_MDCR_EL2,
+	VCPU_CPTR_EL2,
+};
+
+static void feature_trap_activate(struct kvm_vcpu *vcpu,
+				  enum vcpu_config_reg cfg_reg,
+				  u64 cfg_set, u64 cfg_clear)
+{
+	u64 *reg_ptr, reg_val;
+
+	switch (cfg_reg) {
+	case VCPU_HCR_EL2:
+		reg_ptr = &vcpu->arch.hcr_el2;
+		break;
+	case VCPU_MDCR_EL2:
+		reg_ptr = &vcpu->arch.mdcr_el2;
+		break;
+	case VCPU_CPTR_EL2:
+		reg_ptr = &vcpu->arch.cptr_el2;
+		break;
+	}
+
+	/* Clear/Set fields that are indicated by cfg_clear/cfg_set. */
+	reg_val = (*reg_ptr & ~cfg_clear);
+	reg_val |= cfg_set;
+	*reg_ptr = reg_val;
+}
+
+static void feature_ras_trap_activate(struct kvm_vcpu *vcpu)
+{
+	feature_trap_activate(vcpu, VCPU_HCR_EL2, HCR_TERR | HCR_TEA, HCR_FIEN);
+}
+
+static void feature_amu_trap_activate(struct kvm_vcpu *vcpu)
+{
+	feature_trap_activate(vcpu, VCPU_CPTR_EL2, CPTR_EL2_TAM, 0);
+}
+
+/* For ID_AA64PFR0_EL1 */
+static struct feature_config_ctrl ftr_ctrl_ras = {
+	.ftr_reg = SYS_ID_AA64PFR0_EL1,
+	.ftr_shift = ID_AA64PFR0_RAS_SHIFT,
+	.ftr_min = ID_AA64PFR0_RAS_V1,
+	.ftr_signed = FTR_UNSIGNED,
+	.trap_activate = feature_ras_trap_activate,
+};
+
+static struct feature_config_ctrl ftr_ctrl_amu = {
+	.ftr_reg = SYS_ID_AA64PFR0_EL1,
+	.ftr_shift = ID_AA64PFR0_AMU_SHIFT,
+	.ftr_min = ID_AA64PFR0_AMU,
+	.ftr_signed = FTR_UNSIGNED,
+	.trap_activate = feature_amu_trap_activate,
+};
+
 struct id_reg_info {
 	u32	sys_reg;	/* Register ID */
 	u64	sys_val;	/* Sanitized system value */
@@ -778,6 +835,11 @@ static struct id_reg_info id_aa64pfr0_el1_info = {
 	.init = init_id_aa64pfr0_el1_info,
 	.validate = validate_id_aa64pfr0_el1,
 	.vcpu_mask = vcpu_mask_id_aa64pfr0_el1,
+	.trap_features = &(const struct feature_config_ctrl *[]) {
+		&ftr_ctrl_ras,
+		&ftr_ctrl_amu,
+		NULL,
+	},
 };
 
 static struct id_reg_info id_aa64pfr1_el1_info = {
@@ -901,6 +963,18 @@ static inline bool vcpu_feature_is_available(struct kvm_vcpu *vcpu,
 	return feature_avail(ctrl, val);
 }
 
+static bool trap_ras_regs(struct kvm_vcpu *vcpu,
+			  struct sys_reg_params *p,
+			  const struct sys_reg_desc *r)
+{
+	if (!vcpu_feature_is_available(vcpu, &ftr_ctrl_ras)) {
+		kvm_inject_undefined(vcpu);
+		return false;
+	}
+
+	return trap_raz_wi(vcpu, p, r);
+}
+
 /*
  * ARMv8.1 mandates at least a trivial LORegion implementation, where all the
  * RW registers are RES0 (which we can implement as RAZ/WI). On an ARMv8.0
@@ -2265,14 +2339,14 @@ static const struct sys_reg_desc sys_reg_descs[] = {
 	{ SYS_DESC(SYS_AFSR1_EL1), access_vm_reg, reset_unknown, AFSR1_EL1 },
 	{ SYS_DESC(SYS_ESR_EL1), access_vm_reg, reset_unknown, ESR_EL1 },
 
-	{ SYS_DESC(SYS_ERRIDR_EL1), trap_raz_wi },
-	{ SYS_DESC(SYS_ERRSELR_EL1), trap_raz_wi },
-	{ SYS_DESC(SYS_ERXFR_EL1), trap_raz_wi },
-	{ SYS_DESC(SYS_ERXCTLR_EL1), trap_raz_wi },
-	{ SYS_DESC(SYS_ERXSTATUS_EL1), trap_raz_wi },
-	{ SYS_DESC(SYS_ERXADDR_EL1), trap_raz_wi },
-	{ SYS_DESC(SYS_ERXMISC0_EL1), trap_raz_wi },
-	{ SYS_DESC(SYS_ERXMISC1_EL1), trap_raz_wi },
+	{ SYS_DESC(SYS_ERRIDR_EL1), trap_ras_regs },
+	{ SYS_DESC(SYS_ERRSELR_EL1), trap_ras_regs },
+	{ SYS_DESC(SYS_ERXFR_EL1), trap_ras_regs },
+	{ SYS_DESC(SYS_ERXCTLR_EL1), trap_ras_regs },
+	{ SYS_DESC(SYS_ERXSTATUS_EL1), trap_ras_regs },
+	{ SYS_DESC(SYS_ERXADDR_EL1), trap_ras_regs },
+	{ SYS_DESC(SYS_ERXMISC0_EL1), trap_ras_regs },
+	{ SYS_DESC(SYS_ERXMISC1_EL1), trap_ras_regs },
 
 	MTE_REG(TFSR_EL1),
 	MTE_REG(TFSRE0_EL1),
-- 
2.34.1.448.ga2b2bfdf31-goog


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 21/26] KVM: arm64: Trap disabled features of ID_AA64PFR1_EL1
  2022-01-06  4:26 ` Reiji Watanabe
  (?)
@ 2022-01-06  4:27   ` Reiji Watanabe
  -1 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:27 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

Add feature_config_ctrl for MTE, which is indicated in
ID_AA64PFR1_EL1, to program configuration register to trap the
guest's using the feature when it is not exposed to the guest.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/kvm/sys_regs.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 015d67092d5e..72e745c5a9c2 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -344,6 +344,11 @@ static void feature_amu_trap_activate(struct kvm_vcpu *vcpu)
 	feature_trap_activate(vcpu, VCPU_CPTR_EL2, CPTR_EL2_TAM, 0);
 }
 
+static void feature_mte_trap_activate(struct kvm_vcpu *vcpu)
+{
+	feature_trap_activate(vcpu, VCPU_HCR_EL2, HCR_TID5, HCR_DCT | HCR_ATA);
+}
+
 /* For ID_AA64PFR0_EL1 */
 static struct feature_config_ctrl ftr_ctrl_ras = {
 	.ftr_reg = SYS_ID_AA64PFR0_EL1,
@@ -361,6 +366,15 @@ static struct feature_config_ctrl ftr_ctrl_amu = {
 	.trap_activate = feature_amu_trap_activate,
 };
 
+/* For ID_AA64PFR1_EL1 */
+static struct feature_config_ctrl ftr_ctrl_mte = {
+	.ftr_reg = SYS_ID_AA64PFR1_EL1,
+	.ftr_shift = ID_AA64PFR1_MTE_SHIFT,
+	.ftr_min = ID_AA64PFR1_MTE_EL0,
+	.ftr_signed = FTR_UNSIGNED,
+	.trap_activate = feature_mte_trap_activate,
+};
+
 struct id_reg_info {
 	u32	sys_reg;	/* Register ID */
 	u64	sys_val;	/* Sanitized system value */
@@ -847,6 +861,10 @@ static struct id_reg_info id_aa64pfr1_el1_info = {
 	.init = init_id_aa64pfr1_el1_info,
 	.validate = validate_id_aa64pfr1_el1,
 	.vcpu_mask = vcpu_mask_id_aa64pfr1_el1,
+	.trap_features = &(const struct feature_config_ctrl *[]) {
+		&ftr_ctrl_mte,
+		NULL,
+	},
 };
 
 static struct id_reg_info id_aa64isar0_el1_info = {
-- 
2.34.1.448.ga2b2bfdf31-goog


^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 21/26] KVM: arm64: Trap disabled features of ID_AA64PFR1_EL1
@ 2022-01-06  4:27   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:27 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, Will Deacon, Peter Shier, Paolo Bonzini, linux-arm-kernel

Add feature_config_ctrl for MTE, which is indicated in
ID_AA64PFR1_EL1, to program configuration register to trap the
guest's using the feature when it is not exposed to the guest.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/kvm/sys_regs.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 015d67092d5e..72e745c5a9c2 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -344,6 +344,11 @@ static void feature_amu_trap_activate(struct kvm_vcpu *vcpu)
 	feature_trap_activate(vcpu, VCPU_CPTR_EL2, CPTR_EL2_TAM, 0);
 }
 
+static void feature_mte_trap_activate(struct kvm_vcpu *vcpu)
+{
+	feature_trap_activate(vcpu, VCPU_HCR_EL2, HCR_TID5, HCR_DCT | HCR_ATA);
+}
+
 /* For ID_AA64PFR0_EL1 */
 static struct feature_config_ctrl ftr_ctrl_ras = {
 	.ftr_reg = SYS_ID_AA64PFR0_EL1,
@@ -361,6 +366,15 @@ static struct feature_config_ctrl ftr_ctrl_amu = {
 	.trap_activate = feature_amu_trap_activate,
 };
 
+/* For ID_AA64PFR1_EL1 */
+static struct feature_config_ctrl ftr_ctrl_mte = {
+	.ftr_reg = SYS_ID_AA64PFR1_EL1,
+	.ftr_shift = ID_AA64PFR1_MTE_SHIFT,
+	.ftr_min = ID_AA64PFR1_MTE_EL0,
+	.ftr_signed = FTR_UNSIGNED,
+	.trap_activate = feature_mte_trap_activate,
+};
+
 struct id_reg_info {
 	u32	sys_reg;	/* Register ID */
 	u64	sys_val;	/* Sanitized system value */
@@ -847,6 +861,10 @@ static struct id_reg_info id_aa64pfr1_el1_info = {
 	.init = init_id_aa64pfr1_el1_info,
 	.validate = validate_id_aa64pfr1_el1,
 	.vcpu_mask = vcpu_mask_id_aa64pfr1_el1,
+	.trap_features = &(const struct feature_config_ctrl *[]) {
+		&ftr_ctrl_mte,
+		NULL,
+	},
 };
 
 static struct id_reg_info id_aa64isar0_el1_info = {
-- 
2.34.1.448.ga2b2bfdf31-goog

_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 21/26] KVM: arm64: Trap disabled features of ID_AA64PFR1_EL1
@ 2022-01-06  4:27   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:27 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

Add feature_config_ctrl for MTE, which is indicated in
ID_AA64PFR1_EL1, to program configuration register to trap the
guest's using the feature when it is not exposed to the guest.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/kvm/sys_regs.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 015d67092d5e..72e745c5a9c2 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -344,6 +344,11 @@ static void feature_amu_trap_activate(struct kvm_vcpu *vcpu)
 	feature_trap_activate(vcpu, VCPU_CPTR_EL2, CPTR_EL2_TAM, 0);
 }
 
+static void feature_mte_trap_activate(struct kvm_vcpu *vcpu)
+{
+	feature_trap_activate(vcpu, VCPU_HCR_EL2, HCR_TID5, HCR_DCT | HCR_ATA);
+}
+
 /* For ID_AA64PFR0_EL1 */
 static struct feature_config_ctrl ftr_ctrl_ras = {
 	.ftr_reg = SYS_ID_AA64PFR0_EL1,
@@ -361,6 +366,15 @@ static struct feature_config_ctrl ftr_ctrl_amu = {
 	.trap_activate = feature_amu_trap_activate,
 };
 
+/* For ID_AA64PFR1_EL1 */
+static struct feature_config_ctrl ftr_ctrl_mte = {
+	.ftr_reg = SYS_ID_AA64PFR1_EL1,
+	.ftr_shift = ID_AA64PFR1_MTE_SHIFT,
+	.ftr_min = ID_AA64PFR1_MTE_EL0,
+	.ftr_signed = FTR_UNSIGNED,
+	.trap_activate = feature_mte_trap_activate,
+};
+
 struct id_reg_info {
 	u32	sys_reg;	/* Register ID */
 	u64	sys_val;	/* Sanitized system value */
@@ -847,6 +861,10 @@ static struct id_reg_info id_aa64pfr1_el1_info = {
 	.init = init_id_aa64pfr1_el1_info,
 	.validate = validate_id_aa64pfr1_el1,
 	.vcpu_mask = vcpu_mask_id_aa64pfr1_el1,
+	.trap_features = &(const struct feature_config_ctrl *[]) {
+		&ftr_ctrl_mte,
+		NULL,
+	},
 };
 
 static struct id_reg_info id_aa64isar0_el1_info = {
-- 
2.34.1.448.ga2b2bfdf31-goog


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 22/26] KVM: arm64: Trap disabled features of ID_AA64DFR0_EL1
  2022-01-06  4:26 ` Reiji Watanabe
  (?)
@ 2022-01-06  4:27   ` Reiji Watanabe
  -1 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:27 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

Add feature_config_ctrl for PMUv3, PMS and TraceFilt, which are
indicated in ID_AA64DFR0_EL1, to program configuration registers
to trap guest's using those features when they are not exposed to
the guest.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/kvm/sys_regs.c | 47 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 47 insertions(+)

diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 72e745c5a9c2..229671ec3abd 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -349,6 +349,22 @@ static void feature_mte_trap_activate(struct kvm_vcpu *vcpu)
 	feature_trap_activate(vcpu, VCPU_HCR_EL2, HCR_TID5, HCR_DCT | HCR_ATA);
 }
 
+static void feature_pmuv3_trap_activate(struct kvm_vcpu *vcpu)
+{
+	feature_trap_activate(vcpu, VCPU_MDCR_EL2, MDCR_EL2_TPM, 0);
+}
+
+static void feature_pms_trap_activate(struct kvm_vcpu *vcpu)
+{
+	feature_trap_activate(vcpu, VCPU_MDCR_EL2, MDCR_EL2_TPMS,
+			      MDCR_EL2_E2PB_MASK << MDCR_EL2_E2PB_SHIFT);
+}
+
+static void feature_tracefilt_trap_activate(struct kvm_vcpu *vcpu)
+{
+	feature_trap_activate(vcpu, VCPU_MDCR_EL2, MDCR_EL2_TTRF, 0);
+}
+
 /* For ID_AA64PFR0_EL1 */
 static struct feature_config_ctrl ftr_ctrl_ras = {
 	.ftr_reg = SYS_ID_AA64PFR0_EL1,
@@ -375,6 +391,31 @@ static struct feature_config_ctrl ftr_ctrl_mte = {
 	.trap_activate = feature_mte_trap_activate,
 };
 
+/* For ID_AA64DFR0_EL1 */
+static struct feature_config_ctrl ftr_ctrl_pmuv3 = {
+	.ftr_reg = SYS_ID_AA64DFR0_EL1,
+	.ftr_shift = ID_AA64DFR0_PMUVER_SHIFT,
+	.ftr_min = ID_AA64DFR0_PMUVER_8_0,
+	.ftr_signed = FTR_UNSIGNED,
+	.trap_activate = feature_pmuv3_trap_activate,
+};
+
+static struct feature_config_ctrl ftr_ctrl_pms = {
+	.ftr_reg = SYS_ID_AA64DFR0_EL1,
+	.ftr_shift = ID_AA64DFR0_PMSVER_SHIFT,
+	.ftr_min = ID_AA64DFR0_PMSVER_8_2,
+	.ftr_signed = FTR_UNSIGNED,
+	.trap_activate = feature_pms_trap_activate,
+};
+
+static struct feature_config_ctrl ftr_ctrl_tracefilt = {
+	.ftr_reg = SYS_ID_AA64DFR0_EL1,
+	.ftr_shift = ID_AA64DFR0_TRACE_FILT_SHIFT,
+	.ftr_min = 1,
+	.ftr_signed = FTR_UNSIGNED,
+	.trap_activate = feature_tracefilt_trap_activate,
+};
+
 struct id_reg_info {
 	u32	sys_reg;	/* Register ID */
 	u64	sys_val;	/* Sanitized system value */
@@ -898,6 +939,12 @@ static struct id_reg_info id_aa64dfr0_el1_info = {
 	.init = init_id_aa64dfr0_el1_info,
 	.validate = validate_id_aa64dfr0_el1,
 	.vcpu_mask = vcpu_mask_id_aa64dfr0_el1,
+	.trap_features = &(const struct feature_config_ctrl *[]) {
+		&ftr_ctrl_pmuv3,
+		&ftr_ctrl_pms,
+		&ftr_ctrl_tracefilt,
+		NULL,
+	},
 };
 
 static struct id_reg_info id_dfr0_el1_info = {
-- 
2.34.1.448.ga2b2bfdf31-goog


^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 22/26] KVM: arm64: Trap disabled features of ID_AA64DFR0_EL1
@ 2022-01-06  4:27   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:27 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, Will Deacon, Peter Shier, Paolo Bonzini, linux-arm-kernel

Add feature_config_ctrl for PMUv3, PMS and TraceFilt, which are
indicated in ID_AA64DFR0_EL1, to program configuration registers
to trap guest's using those features when they are not exposed to
the guest.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/kvm/sys_regs.c | 47 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 47 insertions(+)

diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 72e745c5a9c2..229671ec3abd 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -349,6 +349,22 @@ static void feature_mte_trap_activate(struct kvm_vcpu *vcpu)
 	feature_trap_activate(vcpu, VCPU_HCR_EL2, HCR_TID5, HCR_DCT | HCR_ATA);
 }
 
+static void feature_pmuv3_trap_activate(struct kvm_vcpu *vcpu)
+{
+	feature_trap_activate(vcpu, VCPU_MDCR_EL2, MDCR_EL2_TPM, 0);
+}
+
+static void feature_pms_trap_activate(struct kvm_vcpu *vcpu)
+{
+	feature_trap_activate(vcpu, VCPU_MDCR_EL2, MDCR_EL2_TPMS,
+			      MDCR_EL2_E2PB_MASK << MDCR_EL2_E2PB_SHIFT);
+}
+
+static void feature_tracefilt_trap_activate(struct kvm_vcpu *vcpu)
+{
+	feature_trap_activate(vcpu, VCPU_MDCR_EL2, MDCR_EL2_TTRF, 0);
+}
+
 /* For ID_AA64PFR0_EL1 */
 static struct feature_config_ctrl ftr_ctrl_ras = {
 	.ftr_reg = SYS_ID_AA64PFR0_EL1,
@@ -375,6 +391,31 @@ static struct feature_config_ctrl ftr_ctrl_mte = {
 	.trap_activate = feature_mte_trap_activate,
 };
 
+/* For ID_AA64DFR0_EL1 */
+static struct feature_config_ctrl ftr_ctrl_pmuv3 = {
+	.ftr_reg = SYS_ID_AA64DFR0_EL1,
+	.ftr_shift = ID_AA64DFR0_PMUVER_SHIFT,
+	.ftr_min = ID_AA64DFR0_PMUVER_8_0,
+	.ftr_signed = FTR_UNSIGNED,
+	.trap_activate = feature_pmuv3_trap_activate,
+};
+
+static struct feature_config_ctrl ftr_ctrl_pms = {
+	.ftr_reg = SYS_ID_AA64DFR0_EL1,
+	.ftr_shift = ID_AA64DFR0_PMSVER_SHIFT,
+	.ftr_min = ID_AA64DFR0_PMSVER_8_2,
+	.ftr_signed = FTR_UNSIGNED,
+	.trap_activate = feature_pms_trap_activate,
+};
+
+static struct feature_config_ctrl ftr_ctrl_tracefilt = {
+	.ftr_reg = SYS_ID_AA64DFR0_EL1,
+	.ftr_shift = ID_AA64DFR0_TRACE_FILT_SHIFT,
+	.ftr_min = 1,
+	.ftr_signed = FTR_UNSIGNED,
+	.trap_activate = feature_tracefilt_trap_activate,
+};
+
 struct id_reg_info {
 	u32	sys_reg;	/* Register ID */
 	u64	sys_val;	/* Sanitized system value */
@@ -898,6 +939,12 @@ static struct id_reg_info id_aa64dfr0_el1_info = {
 	.init = init_id_aa64dfr0_el1_info,
 	.validate = validate_id_aa64dfr0_el1,
 	.vcpu_mask = vcpu_mask_id_aa64dfr0_el1,
+	.trap_features = &(const struct feature_config_ctrl *[]) {
+		&ftr_ctrl_pmuv3,
+		&ftr_ctrl_pms,
+		&ftr_ctrl_tracefilt,
+		NULL,
+	},
 };
 
 static struct id_reg_info id_dfr0_el1_info = {
-- 
2.34.1.448.ga2b2bfdf31-goog

_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 22/26] KVM: arm64: Trap disabled features of ID_AA64DFR0_EL1
@ 2022-01-06  4:27   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:27 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

Add feature_config_ctrl for PMUv3, PMS and TraceFilt, which are
indicated in ID_AA64DFR0_EL1, to program configuration registers
to trap guest's using those features when they are not exposed to
the guest.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/kvm/sys_regs.c | 47 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 47 insertions(+)

diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 72e745c5a9c2..229671ec3abd 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -349,6 +349,22 @@ static void feature_mte_trap_activate(struct kvm_vcpu *vcpu)
 	feature_trap_activate(vcpu, VCPU_HCR_EL2, HCR_TID5, HCR_DCT | HCR_ATA);
 }
 
+static void feature_pmuv3_trap_activate(struct kvm_vcpu *vcpu)
+{
+	feature_trap_activate(vcpu, VCPU_MDCR_EL2, MDCR_EL2_TPM, 0);
+}
+
+static void feature_pms_trap_activate(struct kvm_vcpu *vcpu)
+{
+	feature_trap_activate(vcpu, VCPU_MDCR_EL2, MDCR_EL2_TPMS,
+			      MDCR_EL2_E2PB_MASK << MDCR_EL2_E2PB_SHIFT);
+}
+
+static void feature_tracefilt_trap_activate(struct kvm_vcpu *vcpu)
+{
+	feature_trap_activate(vcpu, VCPU_MDCR_EL2, MDCR_EL2_TTRF, 0);
+}
+
 /* For ID_AA64PFR0_EL1 */
 static struct feature_config_ctrl ftr_ctrl_ras = {
 	.ftr_reg = SYS_ID_AA64PFR0_EL1,
@@ -375,6 +391,31 @@ static struct feature_config_ctrl ftr_ctrl_mte = {
 	.trap_activate = feature_mte_trap_activate,
 };
 
+/* For ID_AA64DFR0_EL1 */
+static struct feature_config_ctrl ftr_ctrl_pmuv3 = {
+	.ftr_reg = SYS_ID_AA64DFR0_EL1,
+	.ftr_shift = ID_AA64DFR0_PMUVER_SHIFT,
+	.ftr_min = ID_AA64DFR0_PMUVER_8_0,
+	.ftr_signed = FTR_UNSIGNED,
+	.trap_activate = feature_pmuv3_trap_activate,
+};
+
+static struct feature_config_ctrl ftr_ctrl_pms = {
+	.ftr_reg = SYS_ID_AA64DFR0_EL1,
+	.ftr_shift = ID_AA64DFR0_PMSVER_SHIFT,
+	.ftr_min = ID_AA64DFR0_PMSVER_8_2,
+	.ftr_signed = FTR_UNSIGNED,
+	.trap_activate = feature_pms_trap_activate,
+};
+
+static struct feature_config_ctrl ftr_ctrl_tracefilt = {
+	.ftr_reg = SYS_ID_AA64DFR0_EL1,
+	.ftr_shift = ID_AA64DFR0_TRACE_FILT_SHIFT,
+	.ftr_min = 1,
+	.ftr_signed = FTR_UNSIGNED,
+	.trap_activate = feature_tracefilt_trap_activate,
+};
+
 struct id_reg_info {
 	u32	sys_reg;	/* Register ID */
 	u64	sys_val;	/* Sanitized system value */
@@ -898,6 +939,12 @@ static struct id_reg_info id_aa64dfr0_el1_info = {
 	.init = init_id_aa64dfr0_el1_info,
 	.validate = validate_id_aa64dfr0_el1,
 	.vcpu_mask = vcpu_mask_id_aa64dfr0_el1,
+	.trap_features = &(const struct feature_config_ctrl *[]) {
+		&ftr_ctrl_pmuv3,
+		&ftr_ctrl_pms,
+		&ftr_ctrl_tracefilt,
+		NULL,
+	},
 };
 
 static struct id_reg_info id_dfr0_el1_info = {
-- 
2.34.1.448.ga2b2bfdf31-goog


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 23/26] KVM: arm64: Trap disabled features of ID_AA64MMFR1_EL1
  2022-01-06  4:26 ` Reiji Watanabe
  (?)
@ 2022-01-06  4:27   ` Reiji Watanabe
  -1 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:27 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

Add feature_config_ctrl for LORegions, which is indicated in
ID_AA64MMFR1_EL1, to program configuration register to trap
guest's using the feature when it is not exposed to the guest.

Change trap_loregion() to use vcpu_feature_is_available()
to simplify checking of the feature's availability.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/kvm/sys_regs.c | 26 ++++++++++++++++++++++++--
 1 file changed, 24 insertions(+), 2 deletions(-)

diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 229671ec3abd..f8a5ee927ecf 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -365,6 +365,11 @@ static void feature_tracefilt_trap_activate(struct kvm_vcpu *vcpu)
 	feature_trap_activate(vcpu, VCPU_MDCR_EL2, MDCR_EL2_TTRF, 0);
 }
 
+static void feature_lor_trap_activate(struct kvm_vcpu *vcpu)
+{
+	feature_trap_activate(vcpu, VCPU_HCR_EL2, HCR_TLOR, 0);
+}
+
 /* For ID_AA64PFR0_EL1 */
 static struct feature_config_ctrl ftr_ctrl_ras = {
 	.ftr_reg = SYS_ID_AA64PFR0_EL1,
@@ -416,6 +421,15 @@ static struct feature_config_ctrl ftr_ctrl_tracefilt = {
 	.trap_activate = feature_tracefilt_trap_activate,
 };
 
+/* For ID_AA64MMFR1_EL1 */
+static struct feature_config_ctrl ftr_ctrl_lor = {
+	.ftr_reg = SYS_ID_AA64MMFR1_EL1,
+	.ftr_shift = ID_AA64MMFR1_LOR_SHIFT,
+	.ftr_min = 1,
+	.ftr_signed = FTR_UNSIGNED,
+	.trap_activate = feature_lor_trap_activate,
+};
+
 struct id_reg_info {
 	u32	sys_reg;	/* Register ID */
 	u64	sys_val;	/* Sanitized system value */
@@ -947,6 +961,14 @@ static struct id_reg_info id_aa64dfr0_el1_info = {
 	},
 };
 
+static struct id_reg_info id_aa64mmfr1_el1_info = {
+	.sys_reg = SYS_ID_AA64MMFR1_EL1,
+	.trap_features = &(const struct feature_config_ctrl *[]) {
+		&ftr_ctrl_lor,
+		NULL,
+	},
+};
+
 static struct id_reg_info id_dfr0_el1_info = {
 	.sys_reg = SYS_ID_DFR0_EL1,
 	.init = init_id_dfr0_el1_info,
@@ -976,6 +998,7 @@ static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {
 	[IDREG_IDX(SYS_ID_AA64ISAR0_EL1)] = &id_aa64isar0_el1_info,
 	[IDREG_IDX(SYS_ID_AA64ISAR1_EL1)] = &id_aa64isar1_el1_info,
 	[IDREG_IDX(SYS_ID_AA64MMFR0_EL1)] = &id_aa64mmfr0_el1_info,
+	[IDREG_IDX(SYS_ID_AA64MMFR1_EL1)] = &id_aa64mmfr1_el1_info,
 };
 
 static int validate_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
@@ -1050,10 +1073,9 @@ static bool trap_loregion(struct kvm_vcpu *vcpu,
 			  struct sys_reg_params *p,
 			  const struct sys_reg_desc *r)
 {
-	u64 val = __read_id_reg(vcpu, SYS_ID_AA64MMFR1_EL1);
 	u32 sr = reg_to_encoding(r);
 
-	if (!(val & (0xfUL << ID_AA64MMFR1_LOR_SHIFT))) {
+	if (!vcpu_feature_is_available(vcpu, &ftr_ctrl_lor)) {
 		kvm_inject_undefined(vcpu);
 		return false;
 	}
-- 
2.34.1.448.ga2b2bfdf31-goog


^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 23/26] KVM: arm64: Trap disabled features of ID_AA64MMFR1_EL1
@ 2022-01-06  4:27   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:27 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, Will Deacon, Peter Shier, Paolo Bonzini, linux-arm-kernel

Add feature_config_ctrl for LORegions, which is indicated in
ID_AA64MMFR1_EL1, to program configuration register to trap
guest's using the feature when it is not exposed to the guest.

Change trap_loregion() to use vcpu_feature_is_available()
to simplify checking of the feature's availability.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/kvm/sys_regs.c | 26 ++++++++++++++++++++++++--
 1 file changed, 24 insertions(+), 2 deletions(-)

diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 229671ec3abd..f8a5ee927ecf 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -365,6 +365,11 @@ static void feature_tracefilt_trap_activate(struct kvm_vcpu *vcpu)
 	feature_trap_activate(vcpu, VCPU_MDCR_EL2, MDCR_EL2_TTRF, 0);
 }
 
+static void feature_lor_trap_activate(struct kvm_vcpu *vcpu)
+{
+	feature_trap_activate(vcpu, VCPU_HCR_EL2, HCR_TLOR, 0);
+}
+
 /* For ID_AA64PFR0_EL1 */
 static struct feature_config_ctrl ftr_ctrl_ras = {
 	.ftr_reg = SYS_ID_AA64PFR0_EL1,
@@ -416,6 +421,15 @@ static struct feature_config_ctrl ftr_ctrl_tracefilt = {
 	.trap_activate = feature_tracefilt_trap_activate,
 };
 
+/* For ID_AA64MMFR1_EL1 */
+static struct feature_config_ctrl ftr_ctrl_lor = {
+	.ftr_reg = SYS_ID_AA64MMFR1_EL1,
+	.ftr_shift = ID_AA64MMFR1_LOR_SHIFT,
+	.ftr_min = 1,
+	.ftr_signed = FTR_UNSIGNED,
+	.trap_activate = feature_lor_trap_activate,
+};
+
 struct id_reg_info {
 	u32	sys_reg;	/* Register ID */
 	u64	sys_val;	/* Sanitized system value */
@@ -947,6 +961,14 @@ static struct id_reg_info id_aa64dfr0_el1_info = {
 	},
 };
 
+static struct id_reg_info id_aa64mmfr1_el1_info = {
+	.sys_reg = SYS_ID_AA64MMFR1_EL1,
+	.trap_features = &(const struct feature_config_ctrl *[]) {
+		&ftr_ctrl_lor,
+		NULL,
+	},
+};
+
 static struct id_reg_info id_dfr0_el1_info = {
 	.sys_reg = SYS_ID_DFR0_EL1,
 	.init = init_id_dfr0_el1_info,
@@ -976,6 +998,7 @@ static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {
 	[IDREG_IDX(SYS_ID_AA64ISAR0_EL1)] = &id_aa64isar0_el1_info,
 	[IDREG_IDX(SYS_ID_AA64ISAR1_EL1)] = &id_aa64isar1_el1_info,
 	[IDREG_IDX(SYS_ID_AA64MMFR0_EL1)] = &id_aa64mmfr0_el1_info,
+	[IDREG_IDX(SYS_ID_AA64MMFR1_EL1)] = &id_aa64mmfr1_el1_info,
 };
 
 static int validate_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
@@ -1050,10 +1073,9 @@ static bool trap_loregion(struct kvm_vcpu *vcpu,
 			  struct sys_reg_params *p,
 			  const struct sys_reg_desc *r)
 {
-	u64 val = __read_id_reg(vcpu, SYS_ID_AA64MMFR1_EL1);
 	u32 sr = reg_to_encoding(r);
 
-	if (!(val & (0xfUL << ID_AA64MMFR1_LOR_SHIFT))) {
+	if (!vcpu_feature_is_available(vcpu, &ftr_ctrl_lor)) {
 		kvm_inject_undefined(vcpu);
 		return false;
 	}
-- 
2.34.1.448.ga2b2bfdf31-goog

_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 23/26] KVM: arm64: Trap disabled features of ID_AA64MMFR1_EL1
@ 2022-01-06  4:27   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:27 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

Add feature_config_ctrl for LORegions, which is indicated in
ID_AA64MMFR1_EL1, to program configuration register to trap
guest's using the feature when it is not exposed to the guest.

Change trap_loregion() to use vcpu_feature_is_available()
to simplify checking of the feature's availability.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/kvm/sys_regs.c | 26 ++++++++++++++++++++++++--
 1 file changed, 24 insertions(+), 2 deletions(-)

diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 229671ec3abd..f8a5ee927ecf 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -365,6 +365,11 @@ static void feature_tracefilt_trap_activate(struct kvm_vcpu *vcpu)
 	feature_trap_activate(vcpu, VCPU_MDCR_EL2, MDCR_EL2_TTRF, 0);
 }
 
+static void feature_lor_trap_activate(struct kvm_vcpu *vcpu)
+{
+	feature_trap_activate(vcpu, VCPU_HCR_EL2, HCR_TLOR, 0);
+}
+
 /* For ID_AA64PFR0_EL1 */
 static struct feature_config_ctrl ftr_ctrl_ras = {
 	.ftr_reg = SYS_ID_AA64PFR0_EL1,
@@ -416,6 +421,15 @@ static struct feature_config_ctrl ftr_ctrl_tracefilt = {
 	.trap_activate = feature_tracefilt_trap_activate,
 };
 
+/* For ID_AA64MMFR1_EL1 */
+static struct feature_config_ctrl ftr_ctrl_lor = {
+	.ftr_reg = SYS_ID_AA64MMFR1_EL1,
+	.ftr_shift = ID_AA64MMFR1_LOR_SHIFT,
+	.ftr_min = 1,
+	.ftr_signed = FTR_UNSIGNED,
+	.trap_activate = feature_lor_trap_activate,
+};
+
 struct id_reg_info {
 	u32	sys_reg;	/* Register ID */
 	u64	sys_val;	/* Sanitized system value */
@@ -947,6 +961,14 @@ static struct id_reg_info id_aa64dfr0_el1_info = {
 	},
 };
 
+static struct id_reg_info id_aa64mmfr1_el1_info = {
+	.sys_reg = SYS_ID_AA64MMFR1_EL1,
+	.trap_features = &(const struct feature_config_ctrl *[]) {
+		&ftr_ctrl_lor,
+		NULL,
+	},
+};
+
 static struct id_reg_info id_dfr0_el1_info = {
 	.sys_reg = SYS_ID_DFR0_EL1,
 	.init = init_id_dfr0_el1_info,
@@ -976,6 +998,7 @@ static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {
 	[IDREG_IDX(SYS_ID_AA64ISAR0_EL1)] = &id_aa64isar0_el1_info,
 	[IDREG_IDX(SYS_ID_AA64ISAR1_EL1)] = &id_aa64isar1_el1_info,
 	[IDREG_IDX(SYS_ID_AA64MMFR0_EL1)] = &id_aa64mmfr0_el1_info,
+	[IDREG_IDX(SYS_ID_AA64MMFR1_EL1)] = &id_aa64mmfr1_el1_info,
 };
 
 static int validate_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
@@ -1050,10 +1073,9 @@ static bool trap_loregion(struct kvm_vcpu *vcpu,
 			  struct sys_reg_params *p,
 			  const struct sys_reg_desc *r)
 {
-	u64 val = __read_id_reg(vcpu, SYS_ID_AA64MMFR1_EL1);
 	u32 sr = reg_to_encoding(r);
 
-	if (!(val & (0xfUL << ID_AA64MMFR1_LOR_SHIFT))) {
+	if (!vcpu_feature_is_available(vcpu, &ftr_ctrl_lor)) {
 		kvm_inject_undefined(vcpu);
 		return false;
 	}
-- 
2.34.1.448.ga2b2bfdf31-goog


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 24/26] KVM: arm64: Trap disabled features of ID_AA64ISAR1_EL1
  2022-01-06  4:26 ` Reiji Watanabe
  (?)
@ 2022-01-06  4:27   ` Reiji Watanabe
  -1 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:27 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

Add feature_config_ctrl for PTRAUTH, which is indicated in
ID_AA64ISAR1_EL1, to program configuration register to trap
guest's using the feature when it is not exposed to the guest.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/kvm/sys_regs.c | 39 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 39 insertions(+)

diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index f8a5ee927ecf..e6ef76f9b563 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -283,6 +283,30 @@ static bool trap_raz_wi(struct kvm_vcpu *vcpu,
 	(cpuid_feature_extract_unsigned_field(val, ID_AA64ISAR1_GPI_SHIFT) >= \
 	 ID_AA64ISAR1_GPI_IMP_DEF)
 
+/*
+ * Return true if ptrauth needs to be trapped.
+ * (i.e. if ptrauth is supported on the host but not exposed to the guest)
+ */
+static bool vcpu_need_trap_ptrauth(struct kvm_vcpu *vcpu)
+{
+	u64 val;
+	bool generic, address;
+
+	if (!system_has_full_ptr_auth())
+		/* The feature is not supported. */
+		return false;
+
+	val = __read_id_reg(vcpu, SYS_ID_AA64ISAR1_EL1);
+	generic = aa64isar1_has_gpi(val) || aa64isar1_has_gpa(val);
+	address = aa64isar1_has_api(val) || aa64isar1_has_apa(val);
+	if (generic && address)
+		/* The feature is available. */
+		return false;
+
+	/* The feature is supported but hidden. */
+	return true;
+}
+
 /*
  * Feature information to program configuration register to trap or disable
  * guest's using a feature when the feature is not exposed to the guest.
@@ -370,6 +394,11 @@ static void feature_lor_trap_activate(struct kvm_vcpu *vcpu)
 	feature_trap_activate(vcpu, VCPU_HCR_EL2, HCR_TLOR, 0);
 }
 
+static void feature_ptrauth_trap_activate(struct kvm_vcpu *vcpu)
+{
+	feature_trap_activate(vcpu, VCPU_HCR_EL2, 0, HCR_API | HCR_APK);
+}
+
 /* For ID_AA64PFR0_EL1 */
 static struct feature_config_ctrl ftr_ctrl_ras = {
 	.ftr_reg = SYS_ID_AA64PFR0_EL1,
@@ -430,6 +459,12 @@ static struct feature_config_ctrl ftr_ctrl_lor = {
 	.trap_activate = feature_lor_trap_activate,
 };
 
+/* For SYS_ID_AA64ISAR1_EL1 */
+static struct feature_config_ctrl ftr_ctrl_ptrauth = {
+	.ftr_need_trap = vcpu_need_trap_ptrauth,
+	.trap_activate = feature_ptrauth_trap_activate,
+};
+
 struct id_reg_info {
 	u32	sys_reg;	/* Register ID */
 	u64	sys_val;	/* Sanitized system value */
@@ -932,6 +967,10 @@ static struct id_reg_info id_aa64isar1_el1_info = {
 	.init = init_id_aa64isar1_el1_info,
 	.validate = validate_id_aa64isar1_el1,
 	.vcpu_mask = vcpu_mask_id_aa64isar1_el1,
+	.trap_features = &(const struct feature_config_ctrl *[]) {
+		&ftr_ctrl_ptrauth,
+		NULL,
+	},
 };
 
 static struct id_reg_info id_aa64mmfr0_el1_info = {
-- 
2.34.1.448.ga2b2bfdf31-goog


^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 24/26] KVM: arm64: Trap disabled features of ID_AA64ISAR1_EL1
@ 2022-01-06  4:27   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:27 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, Will Deacon, Peter Shier, Paolo Bonzini, linux-arm-kernel

Add feature_config_ctrl for PTRAUTH, which is indicated in
ID_AA64ISAR1_EL1, to program configuration register to trap
guest's using the feature when it is not exposed to the guest.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/kvm/sys_regs.c | 39 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 39 insertions(+)

diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index f8a5ee927ecf..e6ef76f9b563 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -283,6 +283,30 @@ static bool trap_raz_wi(struct kvm_vcpu *vcpu,
 	(cpuid_feature_extract_unsigned_field(val, ID_AA64ISAR1_GPI_SHIFT) >= \
 	 ID_AA64ISAR1_GPI_IMP_DEF)
 
+/*
+ * Return true if ptrauth needs to be trapped.
+ * (i.e. if ptrauth is supported on the host but not exposed to the guest)
+ */
+static bool vcpu_need_trap_ptrauth(struct kvm_vcpu *vcpu)
+{
+	u64 val;
+	bool generic, address;
+
+	if (!system_has_full_ptr_auth())
+		/* The feature is not supported. */
+		return false;
+
+	val = __read_id_reg(vcpu, SYS_ID_AA64ISAR1_EL1);
+	generic = aa64isar1_has_gpi(val) || aa64isar1_has_gpa(val);
+	address = aa64isar1_has_api(val) || aa64isar1_has_apa(val);
+	if (generic && address)
+		/* The feature is available. */
+		return false;
+
+	/* The feature is supported but hidden. */
+	return true;
+}
+
 /*
  * Feature information to program configuration register to trap or disable
  * guest's using a feature when the feature is not exposed to the guest.
@@ -370,6 +394,11 @@ static void feature_lor_trap_activate(struct kvm_vcpu *vcpu)
 	feature_trap_activate(vcpu, VCPU_HCR_EL2, HCR_TLOR, 0);
 }
 
+static void feature_ptrauth_trap_activate(struct kvm_vcpu *vcpu)
+{
+	feature_trap_activate(vcpu, VCPU_HCR_EL2, 0, HCR_API | HCR_APK);
+}
+
 /* For ID_AA64PFR0_EL1 */
 static struct feature_config_ctrl ftr_ctrl_ras = {
 	.ftr_reg = SYS_ID_AA64PFR0_EL1,
@@ -430,6 +459,12 @@ static struct feature_config_ctrl ftr_ctrl_lor = {
 	.trap_activate = feature_lor_trap_activate,
 };
 
+/* For SYS_ID_AA64ISAR1_EL1 */
+static struct feature_config_ctrl ftr_ctrl_ptrauth = {
+	.ftr_need_trap = vcpu_need_trap_ptrauth,
+	.trap_activate = feature_ptrauth_trap_activate,
+};
+
 struct id_reg_info {
 	u32	sys_reg;	/* Register ID */
 	u64	sys_val;	/* Sanitized system value */
@@ -932,6 +967,10 @@ static struct id_reg_info id_aa64isar1_el1_info = {
 	.init = init_id_aa64isar1_el1_info,
 	.validate = validate_id_aa64isar1_el1,
 	.vcpu_mask = vcpu_mask_id_aa64isar1_el1,
+	.trap_features = &(const struct feature_config_ctrl *[]) {
+		&ftr_ctrl_ptrauth,
+		NULL,
+	},
 };
 
 static struct id_reg_info id_aa64mmfr0_el1_info = {
-- 
2.34.1.448.ga2b2bfdf31-goog

_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 24/26] KVM: arm64: Trap disabled features of ID_AA64ISAR1_EL1
@ 2022-01-06  4:27   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:27 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

Add feature_config_ctrl for PTRAUTH, which is indicated in
ID_AA64ISAR1_EL1, to program configuration register to trap
guest's using the feature when it is not exposed to the guest.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/kvm/sys_regs.c | 39 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 39 insertions(+)

diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index f8a5ee927ecf..e6ef76f9b563 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -283,6 +283,30 @@ static bool trap_raz_wi(struct kvm_vcpu *vcpu,
 	(cpuid_feature_extract_unsigned_field(val, ID_AA64ISAR1_GPI_SHIFT) >= \
 	 ID_AA64ISAR1_GPI_IMP_DEF)
 
+/*
+ * Return true if ptrauth needs to be trapped.
+ * (i.e. if ptrauth is supported on the host but not exposed to the guest)
+ */
+static bool vcpu_need_trap_ptrauth(struct kvm_vcpu *vcpu)
+{
+	u64 val;
+	bool generic, address;
+
+	if (!system_has_full_ptr_auth())
+		/* The feature is not supported. */
+		return false;
+
+	val = __read_id_reg(vcpu, SYS_ID_AA64ISAR1_EL1);
+	generic = aa64isar1_has_gpi(val) || aa64isar1_has_gpa(val);
+	address = aa64isar1_has_api(val) || aa64isar1_has_apa(val);
+	if (generic && address)
+		/* The feature is available. */
+		return false;
+
+	/* The feature is supported but hidden. */
+	return true;
+}
+
 /*
  * Feature information to program configuration register to trap or disable
  * guest's using a feature when the feature is not exposed to the guest.
@@ -370,6 +394,11 @@ static void feature_lor_trap_activate(struct kvm_vcpu *vcpu)
 	feature_trap_activate(vcpu, VCPU_HCR_EL2, HCR_TLOR, 0);
 }
 
+static void feature_ptrauth_trap_activate(struct kvm_vcpu *vcpu)
+{
+	feature_trap_activate(vcpu, VCPU_HCR_EL2, 0, HCR_API | HCR_APK);
+}
+
 /* For ID_AA64PFR0_EL1 */
 static struct feature_config_ctrl ftr_ctrl_ras = {
 	.ftr_reg = SYS_ID_AA64PFR0_EL1,
@@ -430,6 +459,12 @@ static struct feature_config_ctrl ftr_ctrl_lor = {
 	.trap_activate = feature_lor_trap_activate,
 };
 
+/* For SYS_ID_AA64ISAR1_EL1 */
+static struct feature_config_ctrl ftr_ctrl_ptrauth = {
+	.ftr_need_trap = vcpu_need_trap_ptrauth,
+	.trap_activate = feature_ptrauth_trap_activate,
+};
+
 struct id_reg_info {
 	u32	sys_reg;	/* Register ID */
 	u64	sys_val;	/* Sanitized system value */
@@ -932,6 +967,10 @@ static struct id_reg_info id_aa64isar1_el1_info = {
 	.init = init_id_aa64isar1_el1_info,
 	.validate = validate_id_aa64isar1_el1,
 	.vcpu_mask = vcpu_mask_id_aa64isar1_el1,
+	.trap_features = &(const struct feature_config_ctrl *[]) {
+		&ftr_ctrl_ptrauth,
+		NULL,
+	},
 };
 
 static struct id_reg_info id_aa64mmfr0_el1_info = {
-- 
2.34.1.448.ga2b2bfdf31-goog


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 25/26] KVM: arm64: Add kunit test for trap initialization
  2022-01-06  4:26 ` Reiji Watanabe
  (?)
@ 2022-01-06  4:27   ` Reiji Watanabe
  -1 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:27 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

Add KUnit tests for functions in arch/arm64/kvm/sys_regs_test.c
that activates traps for disabled features.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/kvm/sys_regs_test.c | 262 +++++++++++++++++++++++++++++++++
 1 file changed, 262 insertions(+)

diff --git a/arch/arm64/kvm/sys_regs_test.c b/arch/arm64/kvm/sys_regs_test.c
index 5b1b95bd9ec4..4a705ffef93b 100644
--- a/arch/arm64/kvm/sys_regs_test.c
+++ b/arch/arm64/kvm/sys_regs_test.c
@@ -959,6 +959,265 @@ static void validate_id_reg_test(struct kunit *test)
 	GET_ID_REG_INFO(id) = original_idr;
 }
 
+struct trap_config_test {
+	u64 set;
+	u64 clear;
+	u64 prev_val;
+	u64 expect_val;
+};
+
+struct trap_config_test trap_params[] = {
+	{0x30000800000, 0, 0, 0x30000800000},
+	{0, 0x30000800000, 0, 0},
+	{0x30000800000, 0, (u64)-1, (u64)-1},
+	{0, 0x30000800000, (u64)-1, (u64)0xfffffcffff7fffff},
+};
+
+static void trap_case_to_desc(struct trap_config_test *t, char *desc)
+{
+	snprintf(desc, KUNIT_PARAM_DESC_SIZE,
+		 "trap - set:0x%llx, clear:0x%llx, prev_val:0x%llx\n",
+		 t->set, t->clear, t->prev_val);
+}
+
+KUNIT_ARRAY_PARAM(trap, trap_params, trap_case_to_desc);
+
+/* Tests for feature_trap_activate(). */
+static void feature_trap_activate_test(struct kunit *test)
+{
+	struct kvm_vcpu *vcpu;
+	const struct trap_config_test *trap = test->param_value;
+
+	vcpu = test_kvm_vcpu_init(test);
+	KUNIT_ASSERT_TRUE(test, vcpu);
+
+	/* Test for HCR_EL2 */
+	vcpu->arch.hcr_el2 = trap->prev_val;
+	feature_trap_activate(vcpu, VCPU_HCR_EL2, trap->set, trap->clear);
+	KUNIT_EXPECT_EQ(test, vcpu->arch.hcr_el2, trap->expect_val);
+
+	/* Test for MDCR_EL2 */
+	vcpu->arch.mdcr_el2 = trap->prev_val;
+	feature_trap_activate(vcpu, VCPU_MDCR_EL2, trap->set, trap->clear);
+	KUNIT_EXPECT_EQ(test, vcpu->arch.mdcr_el2, trap->expect_val);
+
+	/* Test for CPTR_EL2 */
+	vcpu->arch.cptr_el2 = trap->prev_val;
+	feature_trap_activate(vcpu, VCPU_CPTR_EL2, trap->set, trap->clear);
+	KUNIT_EXPECT_EQ(test, vcpu->arch.cptr_el2, trap->expect_val);
+}
+
+static u64 test_trap_set0;
+static u64 test_trap_clear0;
+static void test_trap_activate0(struct kvm_vcpu *vcpu)
+{
+	feature_trap_activate(vcpu, VCPU_HCR_EL2,
+			      test_trap_set0, test_trap_clear0);
+}
+
+static u64 test_trap_set1;
+static u64 test_trap_clear1;
+static void test_trap_activate1(struct kvm_vcpu *vcpu)
+{
+	feature_trap_activate(vcpu, VCPU_HCR_EL2,
+			      test_trap_set1, test_trap_clear1);
+}
+
+static u64 test_trap_set2;
+static u64 test_trap_clear2;
+static void test_trap_activate2(struct kvm_vcpu *vcpu)
+{
+	feature_trap_activate(vcpu, VCPU_HCR_EL2,
+			      test_trap_set2, test_trap_clear2);
+}
+
+
+static void setup_feature_config_ctrl(struct feature_config_ctrl *config,
+				      u32 id, int shift, int min, bool sign,
+				      void *fn)
+{
+	memset(config, 0, sizeof(*config));
+	config->ftr_reg = id;
+	config->ftr_shift = shift;
+	config->ftr_min = min;
+	config->ftr_signed = sign;
+	config->trap_activate = fn;
+}
+
+/*
+ * Tests for id_reg_features_trap_activate.
+ * Setup a id_reg_info with three entries in id_reg_info->trap_features[].
+ * Check if the config register is updated to enable trap for the disabled
+ * features.
+ */
+static void id_reg_features_trap_activate_test(struct kunit *test)
+{
+	struct kvm_vcpu *vcpu;
+	u32 id;
+	u64 cfg_set, cfg_clear, id_reg_sys_val, id_reg_val;
+	struct id_reg_info id_reg_data;
+	struct feature_config_ctrl config0, config1, config2;
+	struct feature_config_ctrl *trap_features[] = {
+		&config0, &config1, &config2, NULL,
+	};
+
+	vcpu = test_kvm_vcpu_init(test);
+	KUNIT_EXPECT_TRUE(test, vcpu);
+	if (!vcpu)
+		return;
+
+	/* Setup id_reg_info */
+	id_reg_sys_val = 0x7777777777777777;
+	id = SYS_ID_AA64DFR0_EL1;
+	id_reg_data.sys_reg = id;
+	id_reg_data.sys_val = id_reg_sys_val;
+	id_reg_data.vcpu_limit_val  = (u64)-1;
+	id_reg_data.trap_features =
+			(const struct feature_config_ctrl *(*)[])trap_features;
+
+	/* Setup the 1st feature_config_ctrl */
+	test_trap_set0 = 0x3;
+	test_trap_clear0 = 0x0;
+	setup_feature_config_ctrl(&config0, id, 60, 2, FTR_UNSIGNED,
+				  &test_trap_activate0);
+
+	/* Setup the 2nd feature_config_ctrl */
+	test_trap_set1 = 0x30000040;
+	test_trap_clear1 = 0x40000000;
+	setup_feature_config_ctrl(&config1, id, 0, 1, FTR_UNSIGNED,
+				  &test_trap_activate1);
+
+	/* Setup the 3rd feature_config_ctrl */
+	test_trap_set2 = 0x30000000800;
+	test_trap_clear2 = 0x40000000000;
+	setup_feature_config_ctrl(&config2, id, 4, 0, FTR_SIGNED,
+				  &test_trap_activate2);
+
+#define	ftr_dis(cfg)	\
+	((u64)(((cfg)->ftr_min - 1) & 0xf) << (cfg)->ftr_shift)
+
+#define	ftr_en(cfg)	\
+	((u64)(cfg)->ftr_min << (cfg)->ftr_shift)
+
+	/* Test with features enabled for config0, 1 and 2 */
+	id_reg_val = ftr_en(&config0) | ftr_en(&config1) | ftr_en(&config2);
+	write_kvm_id_reg(vcpu->kvm, id, id_reg_val);
+	vcpu->arch.hcr_el2 = 0;
+	id_reg_features_trap_activate(vcpu, &id_reg_data);
+	KUNIT_EXPECT_EQ(test, vcpu->arch.hcr_el2, 0);
+
+
+	/* Test with features disabled for config0 only */
+	id_reg_val = ftr_dis(&config0) | ftr_en(&config1) | ftr_en(&config2);
+	write_kvm_id_reg(vcpu->kvm, id, id_reg_val);
+	vcpu->arch.hcr_el2 = 0;
+	cfg_set = test_trap_set0;
+	cfg_clear = test_trap_clear0;
+
+	id_reg_features_trap_activate(vcpu, &id_reg_data);
+	KUNIT_EXPECT_EQ(test, vcpu->arch.hcr_el2 & cfg_set, cfg_set);
+	KUNIT_EXPECT_EQ(test, vcpu->arch.hcr_el2 & cfg_clear, 0);
+
+
+	/* Test with features disabled for config0 and config1  */
+	id_reg_val = ftr_dis(&config0) | ftr_dis(&config1) | ftr_en(&config2);
+	write_kvm_id_reg(vcpu->kvm, id, id_reg_val);
+	vcpu->arch.hcr_el2 = 0;
+
+	cfg_set = test_trap_set0 | test_trap_set1;
+	cfg_clear = test_trap_clear0 | test_trap_clear1;
+
+	id_reg_features_trap_activate(vcpu, &id_reg_data);
+	KUNIT_EXPECT_EQ(test, vcpu->arch.hcr_el2 & cfg_set, cfg_set);
+	KUNIT_EXPECT_EQ(test, vcpu->arch.hcr_el2 & cfg_clear, 0);
+
+
+	/* Test with features disabled for config0, config1, and config2 */
+	id_reg_val = ftr_dis(&config0) | ftr_dis(&config1) | ftr_dis(&config2);
+	write_kvm_id_reg(vcpu->kvm, id, id_reg_val);
+	vcpu->arch.hcr_el2 = 0;
+
+	cfg_set = test_trap_set0 | test_trap_set1 | test_trap_set2;
+	cfg_clear = test_trap_clear0 | test_trap_clear1 | test_trap_clear2;
+
+	id_reg_features_trap_activate(vcpu, &id_reg_data);
+	KUNIT_EXPECT_EQ(test, vcpu->arch.hcr_el2 & cfg_set, cfg_set);
+	KUNIT_EXPECT_EQ(test, vcpu->arch.hcr_el2 & cfg_clear, 0);
+
+
+	/* Test with id_reg_info == NULL */
+	vcpu->arch.hcr_el2 = 0;
+	id_reg_features_trap_activate(vcpu, NULL);
+	KUNIT_EXPECT_EQ(test, vcpu->arch.hcr_el2, 0);
+
+
+	/* Test with id_reg_data.trap_features = NULL */
+	id_reg_data.trap_features = NULL;
+	vcpu->arch.hcr_el2 = 0;
+	id_reg_features_trap_activate(vcpu, &id_reg_data);
+	KUNIT_EXPECT_EQ(test, vcpu->arch.hcr_el2, 0);
+
+}
+
+/* Tests for vcpu_need_trap_ptrauth(). */
+static void vcpu_need_trap_ptrauth_test(struct kunit *test)
+{
+	struct kvm_vcpu *vcpu;
+	u32 id = SYS_ID_AA64ISAR1_EL1;
+
+	vcpu = test_kvm_vcpu_init(test);
+	KUNIT_EXPECT_TRUE(test, vcpu);
+	if (!vcpu)
+		return;
+
+	if (system_has_full_ptr_auth()) {
+		/* Tests with PTRAUTH disabled vCPU */
+		write_kvm_id_reg(vcpu->kvm, id, 0x0);
+		KUNIT_EXPECT_TRUE(test, vcpu_need_trap_ptrauth(vcpu));
+
+		/* GPI = 1, API = 1 */
+		write_kvm_id_reg(vcpu->kvm, id, 0x10000100);
+		KUNIT_EXPECT_TRUE(test, vcpu_need_trap_ptrauth(vcpu));
+
+		/* GPI = 1, APA = 1 */
+		write_kvm_id_reg(vcpu->kvm, id, 0x10000010);
+		KUNIT_EXPECT_TRUE(test, vcpu_need_trap_ptrauth(vcpu));
+
+		/* GPA = 1, API = 1 */
+		write_kvm_id_reg(vcpu->kvm, id, 0x01000100);
+		KUNIT_EXPECT_TRUE(test, vcpu_need_trap_ptrauth(vcpu));
+
+		/* GPA = 1, APA = 1 */
+		write_kvm_id_reg(vcpu->kvm, id, 0x01000010);
+		KUNIT_EXPECT_TRUE(test, vcpu_need_trap_ptrauth(vcpu));
+
+
+		/* Tests with PTRAUTH enabled vCPU */
+		vcpu->arch.flags |= KVM_ARM64_GUEST_HAS_PTRAUTH;
+
+		write_kvm_id_reg(vcpu->kvm, id, 0x0);
+		KUNIT_EXPECT_TRUE(test, vcpu_need_trap_ptrauth(vcpu));
+
+		/* GPI = 1, API = 1 */
+		write_kvm_id_reg(vcpu->kvm, id, 0x10000100);
+		KUNIT_EXPECT_FALSE(test, vcpu_need_trap_ptrauth(vcpu));
+
+		/* GPI = 1, APA = 1 */
+		write_kvm_id_reg(vcpu->kvm, id, 0x10000010);
+		KUNIT_EXPECT_FALSE(test, vcpu_need_trap_ptrauth(vcpu));
+
+		/* GPA = 1, API = 1 */
+		write_kvm_id_reg(vcpu->kvm, id, 0x01000100);
+		KUNIT_EXPECT_FALSE(test, vcpu_need_trap_ptrauth(vcpu));
+
+		/* GPA = 1, APA = 1 */
+		write_kvm_id_reg(vcpu->kvm, id, 0x01000010);
+		KUNIT_EXPECT_FALSE(test, vcpu_need_trap_ptrauth(vcpu));
+	} else {
+		KUNIT_EXPECT_FALSE(test, vcpu_need_trap_ptrauth(vcpu));
+	}
+}
+
 static struct kunit_case kvm_sys_regs_test_cases[] = {
 	KUNIT_CASE_PARAM(vcpu_id_reg_feature_frac_check_test, frac_gen_params),
 	KUNIT_CASE_PARAM(validate_id_aa64mmfr0_tgran2_test, tgran4_2_gen_params),
@@ -973,6 +1232,9 @@ static struct kunit_case kvm_sys_regs_test_cases[] = {
 	KUNIT_CASE(validate_id_dfr0_el1_test),
 	KUNIT_CASE(validate_mvfr1_el1_test),
 	KUNIT_CASE(validate_id_reg_test),
+	KUNIT_CASE(vcpu_need_trap_ptrauth_test),
+	KUNIT_CASE_PARAM(feature_trap_activate_test, trap_gen_params),
+	KUNIT_CASE(id_reg_features_trap_activate_test),
 	{}
 };
 
-- 
2.34.1.448.ga2b2bfdf31-goog


^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 25/26] KVM: arm64: Add kunit test for trap initialization
@ 2022-01-06  4:27   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:27 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, Will Deacon, Peter Shier, Paolo Bonzini, linux-arm-kernel

Add KUnit tests for functions in arch/arm64/kvm/sys_regs_test.c
that activates traps for disabled features.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/kvm/sys_regs_test.c | 262 +++++++++++++++++++++++++++++++++
 1 file changed, 262 insertions(+)

diff --git a/arch/arm64/kvm/sys_regs_test.c b/arch/arm64/kvm/sys_regs_test.c
index 5b1b95bd9ec4..4a705ffef93b 100644
--- a/arch/arm64/kvm/sys_regs_test.c
+++ b/arch/arm64/kvm/sys_regs_test.c
@@ -959,6 +959,265 @@ static void validate_id_reg_test(struct kunit *test)
 	GET_ID_REG_INFO(id) = original_idr;
 }
 
+struct trap_config_test {
+	u64 set;
+	u64 clear;
+	u64 prev_val;
+	u64 expect_val;
+};
+
+struct trap_config_test trap_params[] = {
+	{0x30000800000, 0, 0, 0x30000800000},
+	{0, 0x30000800000, 0, 0},
+	{0x30000800000, 0, (u64)-1, (u64)-1},
+	{0, 0x30000800000, (u64)-1, (u64)0xfffffcffff7fffff},
+};
+
+static void trap_case_to_desc(struct trap_config_test *t, char *desc)
+{
+	snprintf(desc, KUNIT_PARAM_DESC_SIZE,
+		 "trap - set:0x%llx, clear:0x%llx, prev_val:0x%llx\n",
+		 t->set, t->clear, t->prev_val);
+}
+
+KUNIT_ARRAY_PARAM(trap, trap_params, trap_case_to_desc);
+
+/* Tests for feature_trap_activate(). */
+static void feature_trap_activate_test(struct kunit *test)
+{
+	struct kvm_vcpu *vcpu;
+	const struct trap_config_test *trap = test->param_value;
+
+	vcpu = test_kvm_vcpu_init(test);
+	KUNIT_ASSERT_TRUE(test, vcpu);
+
+	/* Test for HCR_EL2 */
+	vcpu->arch.hcr_el2 = trap->prev_val;
+	feature_trap_activate(vcpu, VCPU_HCR_EL2, trap->set, trap->clear);
+	KUNIT_EXPECT_EQ(test, vcpu->arch.hcr_el2, trap->expect_val);
+
+	/* Test for MDCR_EL2 */
+	vcpu->arch.mdcr_el2 = trap->prev_val;
+	feature_trap_activate(vcpu, VCPU_MDCR_EL2, trap->set, trap->clear);
+	KUNIT_EXPECT_EQ(test, vcpu->arch.mdcr_el2, trap->expect_val);
+
+	/* Test for CPTR_EL2 */
+	vcpu->arch.cptr_el2 = trap->prev_val;
+	feature_trap_activate(vcpu, VCPU_CPTR_EL2, trap->set, trap->clear);
+	KUNIT_EXPECT_EQ(test, vcpu->arch.cptr_el2, trap->expect_val);
+}
+
+static u64 test_trap_set0;
+static u64 test_trap_clear0;
+static void test_trap_activate0(struct kvm_vcpu *vcpu)
+{
+	feature_trap_activate(vcpu, VCPU_HCR_EL2,
+			      test_trap_set0, test_trap_clear0);
+}
+
+static u64 test_trap_set1;
+static u64 test_trap_clear1;
+static void test_trap_activate1(struct kvm_vcpu *vcpu)
+{
+	feature_trap_activate(vcpu, VCPU_HCR_EL2,
+			      test_trap_set1, test_trap_clear1);
+}
+
+static u64 test_trap_set2;
+static u64 test_trap_clear2;
+static void test_trap_activate2(struct kvm_vcpu *vcpu)
+{
+	feature_trap_activate(vcpu, VCPU_HCR_EL2,
+			      test_trap_set2, test_trap_clear2);
+}
+
+
+static void setup_feature_config_ctrl(struct feature_config_ctrl *config,
+				      u32 id, int shift, int min, bool sign,
+				      void *fn)
+{
+	memset(config, 0, sizeof(*config));
+	config->ftr_reg = id;
+	config->ftr_shift = shift;
+	config->ftr_min = min;
+	config->ftr_signed = sign;
+	config->trap_activate = fn;
+}
+
+/*
+ * Tests for id_reg_features_trap_activate.
+ * Setup a id_reg_info with three entries in id_reg_info->trap_features[].
+ * Check if the config register is updated to enable trap for the disabled
+ * features.
+ */
+static void id_reg_features_trap_activate_test(struct kunit *test)
+{
+	struct kvm_vcpu *vcpu;
+	u32 id;
+	u64 cfg_set, cfg_clear, id_reg_sys_val, id_reg_val;
+	struct id_reg_info id_reg_data;
+	struct feature_config_ctrl config0, config1, config2;
+	struct feature_config_ctrl *trap_features[] = {
+		&config0, &config1, &config2, NULL,
+	};
+
+	vcpu = test_kvm_vcpu_init(test);
+	KUNIT_EXPECT_TRUE(test, vcpu);
+	if (!vcpu)
+		return;
+
+	/* Setup id_reg_info */
+	id_reg_sys_val = 0x7777777777777777;
+	id = SYS_ID_AA64DFR0_EL1;
+	id_reg_data.sys_reg = id;
+	id_reg_data.sys_val = id_reg_sys_val;
+	id_reg_data.vcpu_limit_val  = (u64)-1;
+	id_reg_data.trap_features =
+			(const struct feature_config_ctrl *(*)[])trap_features;
+
+	/* Setup the 1st feature_config_ctrl */
+	test_trap_set0 = 0x3;
+	test_trap_clear0 = 0x0;
+	setup_feature_config_ctrl(&config0, id, 60, 2, FTR_UNSIGNED,
+				  &test_trap_activate0);
+
+	/* Setup the 2nd feature_config_ctrl */
+	test_trap_set1 = 0x30000040;
+	test_trap_clear1 = 0x40000000;
+	setup_feature_config_ctrl(&config1, id, 0, 1, FTR_UNSIGNED,
+				  &test_trap_activate1);
+
+	/* Setup the 3rd feature_config_ctrl */
+	test_trap_set2 = 0x30000000800;
+	test_trap_clear2 = 0x40000000000;
+	setup_feature_config_ctrl(&config2, id, 4, 0, FTR_SIGNED,
+				  &test_trap_activate2);
+
+#define	ftr_dis(cfg)	\
+	((u64)(((cfg)->ftr_min - 1) & 0xf) << (cfg)->ftr_shift)
+
+#define	ftr_en(cfg)	\
+	((u64)(cfg)->ftr_min << (cfg)->ftr_shift)
+
+	/* Test with features enabled for config0, 1 and 2 */
+	id_reg_val = ftr_en(&config0) | ftr_en(&config1) | ftr_en(&config2);
+	write_kvm_id_reg(vcpu->kvm, id, id_reg_val);
+	vcpu->arch.hcr_el2 = 0;
+	id_reg_features_trap_activate(vcpu, &id_reg_data);
+	KUNIT_EXPECT_EQ(test, vcpu->arch.hcr_el2, 0);
+
+
+	/* Test with features disabled for config0 only */
+	id_reg_val = ftr_dis(&config0) | ftr_en(&config1) | ftr_en(&config2);
+	write_kvm_id_reg(vcpu->kvm, id, id_reg_val);
+	vcpu->arch.hcr_el2 = 0;
+	cfg_set = test_trap_set0;
+	cfg_clear = test_trap_clear0;
+
+	id_reg_features_trap_activate(vcpu, &id_reg_data);
+	KUNIT_EXPECT_EQ(test, vcpu->arch.hcr_el2 & cfg_set, cfg_set);
+	KUNIT_EXPECT_EQ(test, vcpu->arch.hcr_el2 & cfg_clear, 0);
+
+
+	/* Test with features disabled for config0 and config1  */
+	id_reg_val = ftr_dis(&config0) | ftr_dis(&config1) | ftr_en(&config2);
+	write_kvm_id_reg(vcpu->kvm, id, id_reg_val);
+	vcpu->arch.hcr_el2 = 0;
+
+	cfg_set = test_trap_set0 | test_trap_set1;
+	cfg_clear = test_trap_clear0 | test_trap_clear1;
+
+	id_reg_features_trap_activate(vcpu, &id_reg_data);
+	KUNIT_EXPECT_EQ(test, vcpu->arch.hcr_el2 & cfg_set, cfg_set);
+	KUNIT_EXPECT_EQ(test, vcpu->arch.hcr_el2 & cfg_clear, 0);
+
+
+	/* Test with features disabled for config0, config1, and config2 */
+	id_reg_val = ftr_dis(&config0) | ftr_dis(&config1) | ftr_dis(&config2);
+	write_kvm_id_reg(vcpu->kvm, id, id_reg_val);
+	vcpu->arch.hcr_el2 = 0;
+
+	cfg_set = test_trap_set0 | test_trap_set1 | test_trap_set2;
+	cfg_clear = test_trap_clear0 | test_trap_clear1 | test_trap_clear2;
+
+	id_reg_features_trap_activate(vcpu, &id_reg_data);
+	KUNIT_EXPECT_EQ(test, vcpu->arch.hcr_el2 & cfg_set, cfg_set);
+	KUNIT_EXPECT_EQ(test, vcpu->arch.hcr_el2 & cfg_clear, 0);
+
+
+	/* Test with id_reg_info == NULL */
+	vcpu->arch.hcr_el2 = 0;
+	id_reg_features_trap_activate(vcpu, NULL);
+	KUNIT_EXPECT_EQ(test, vcpu->arch.hcr_el2, 0);
+
+
+	/* Test with id_reg_data.trap_features = NULL */
+	id_reg_data.trap_features = NULL;
+	vcpu->arch.hcr_el2 = 0;
+	id_reg_features_trap_activate(vcpu, &id_reg_data);
+	KUNIT_EXPECT_EQ(test, vcpu->arch.hcr_el2, 0);
+
+}
+
+/* Tests for vcpu_need_trap_ptrauth(). */
+static void vcpu_need_trap_ptrauth_test(struct kunit *test)
+{
+	struct kvm_vcpu *vcpu;
+	u32 id = SYS_ID_AA64ISAR1_EL1;
+
+	vcpu = test_kvm_vcpu_init(test);
+	KUNIT_EXPECT_TRUE(test, vcpu);
+	if (!vcpu)
+		return;
+
+	if (system_has_full_ptr_auth()) {
+		/* Tests with PTRAUTH disabled vCPU */
+		write_kvm_id_reg(vcpu->kvm, id, 0x0);
+		KUNIT_EXPECT_TRUE(test, vcpu_need_trap_ptrauth(vcpu));
+
+		/* GPI = 1, API = 1 */
+		write_kvm_id_reg(vcpu->kvm, id, 0x10000100);
+		KUNIT_EXPECT_TRUE(test, vcpu_need_trap_ptrauth(vcpu));
+
+		/* GPI = 1, APA = 1 */
+		write_kvm_id_reg(vcpu->kvm, id, 0x10000010);
+		KUNIT_EXPECT_TRUE(test, vcpu_need_trap_ptrauth(vcpu));
+
+		/* GPA = 1, API = 1 */
+		write_kvm_id_reg(vcpu->kvm, id, 0x01000100);
+		KUNIT_EXPECT_TRUE(test, vcpu_need_trap_ptrauth(vcpu));
+
+		/* GPA = 1, APA = 1 */
+		write_kvm_id_reg(vcpu->kvm, id, 0x01000010);
+		KUNIT_EXPECT_TRUE(test, vcpu_need_trap_ptrauth(vcpu));
+
+
+		/* Tests with PTRAUTH enabled vCPU */
+		vcpu->arch.flags |= KVM_ARM64_GUEST_HAS_PTRAUTH;
+
+		write_kvm_id_reg(vcpu->kvm, id, 0x0);
+		KUNIT_EXPECT_TRUE(test, vcpu_need_trap_ptrauth(vcpu));
+
+		/* GPI = 1, API = 1 */
+		write_kvm_id_reg(vcpu->kvm, id, 0x10000100);
+		KUNIT_EXPECT_FALSE(test, vcpu_need_trap_ptrauth(vcpu));
+
+		/* GPI = 1, APA = 1 */
+		write_kvm_id_reg(vcpu->kvm, id, 0x10000010);
+		KUNIT_EXPECT_FALSE(test, vcpu_need_trap_ptrauth(vcpu));
+
+		/* GPA = 1, API = 1 */
+		write_kvm_id_reg(vcpu->kvm, id, 0x01000100);
+		KUNIT_EXPECT_FALSE(test, vcpu_need_trap_ptrauth(vcpu));
+
+		/* GPA = 1, APA = 1 */
+		write_kvm_id_reg(vcpu->kvm, id, 0x01000010);
+		KUNIT_EXPECT_FALSE(test, vcpu_need_trap_ptrauth(vcpu));
+	} else {
+		KUNIT_EXPECT_FALSE(test, vcpu_need_trap_ptrauth(vcpu));
+	}
+}
+
 static struct kunit_case kvm_sys_regs_test_cases[] = {
 	KUNIT_CASE_PARAM(vcpu_id_reg_feature_frac_check_test, frac_gen_params),
 	KUNIT_CASE_PARAM(validate_id_aa64mmfr0_tgran2_test, tgran4_2_gen_params),
@@ -973,6 +1232,9 @@ static struct kunit_case kvm_sys_regs_test_cases[] = {
 	KUNIT_CASE(validate_id_dfr0_el1_test),
 	KUNIT_CASE(validate_mvfr1_el1_test),
 	KUNIT_CASE(validate_id_reg_test),
+	KUNIT_CASE(vcpu_need_trap_ptrauth_test),
+	KUNIT_CASE_PARAM(feature_trap_activate_test, trap_gen_params),
+	KUNIT_CASE(id_reg_features_trap_activate_test),
 	{}
 };
 
-- 
2.34.1.448.ga2b2bfdf31-goog

_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 25/26] KVM: arm64: Add kunit test for trap initialization
@ 2022-01-06  4:27   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:27 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

Add KUnit tests for functions in arch/arm64/kvm/sys_regs_test.c
that activates traps for disabled features.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/kvm/sys_regs_test.c | 262 +++++++++++++++++++++++++++++++++
 1 file changed, 262 insertions(+)

diff --git a/arch/arm64/kvm/sys_regs_test.c b/arch/arm64/kvm/sys_regs_test.c
index 5b1b95bd9ec4..4a705ffef93b 100644
--- a/arch/arm64/kvm/sys_regs_test.c
+++ b/arch/arm64/kvm/sys_regs_test.c
@@ -959,6 +959,265 @@ static void validate_id_reg_test(struct kunit *test)
 	GET_ID_REG_INFO(id) = original_idr;
 }
 
+struct trap_config_test {
+	u64 set;
+	u64 clear;
+	u64 prev_val;
+	u64 expect_val;
+};
+
+struct trap_config_test trap_params[] = {
+	{0x30000800000, 0, 0, 0x30000800000},
+	{0, 0x30000800000, 0, 0},
+	{0x30000800000, 0, (u64)-1, (u64)-1},
+	{0, 0x30000800000, (u64)-1, (u64)0xfffffcffff7fffff},
+};
+
+static void trap_case_to_desc(struct trap_config_test *t, char *desc)
+{
+	snprintf(desc, KUNIT_PARAM_DESC_SIZE,
+		 "trap - set:0x%llx, clear:0x%llx, prev_val:0x%llx\n",
+		 t->set, t->clear, t->prev_val);
+}
+
+KUNIT_ARRAY_PARAM(trap, trap_params, trap_case_to_desc);
+
+/* Tests for feature_trap_activate(). */
+static void feature_trap_activate_test(struct kunit *test)
+{
+	struct kvm_vcpu *vcpu;
+	const struct trap_config_test *trap = test->param_value;
+
+	vcpu = test_kvm_vcpu_init(test);
+	KUNIT_ASSERT_TRUE(test, vcpu);
+
+	/* Test for HCR_EL2 */
+	vcpu->arch.hcr_el2 = trap->prev_val;
+	feature_trap_activate(vcpu, VCPU_HCR_EL2, trap->set, trap->clear);
+	KUNIT_EXPECT_EQ(test, vcpu->arch.hcr_el2, trap->expect_val);
+
+	/* Test for MDCR_EL2 */
+	vcpu->arch.mdcr_el2 = trap->prev_val;
+	feature_trap_activate(vcpu, VCPU_MDCR_EL2, trap->set, trap->clear);
+	KUNIT_EXPECT_EQ(test, vcpu->arch.mdcr_el2, trap->expect_val);
+
+	/* Test for CPTR_EL2 */
+	vcpu->arch.cptr_el2 = trap->prev_val;
+	feature_trap_activate(vcpu, VCPU_CPTR_EL2, trap->set, trap->clear);
+	KUNIT_EXPECT_EQ(test, vcpu->arch.cptr_el2, trap->expect_val);
+}
+
+static u64 test_trap_set0;
+static u64 test_trap_clear0;
+static void test_trap_activate0(struct kvm_vcpu *vcpu)
+{
+	feature_trap_activate(vcpu, VCPU_HCR_EL2,
+			      test_trap_set0, test_trap_clear0);
+}
+
+static u64 test_trap_set1;
+static u64 test_trap_clear1;
+static void test_trap_activate1(struct kvm_vcpu *vcpu)
+{
+	feature_trap_activate(vcpu, VCPU_HCR_EL2,
+			      test_trap_set1, test_trap_clear1);
+}
+
+static u64 test_trap_set2;
+static u64 test_trap_clear2;
+static void test_trap_activate2(struct kvm_vcpu *vcpu)
+{
+	feature_trap_activate(vcpu, VCPU_HCR_EL2,
+			      test_trap_set2, test_trap_clear2);
+}
+
+
+static void setup_feature_config_ctrl(struct feature_config_ctrl *config,
+				      u32 id, int shift, int min, bool sign,
+				      void *fn)
+{
+	memset(config, 0, sizeof(*config));
+	config->ftr_reg = id;
+	config->ftr_shift = shift;
+	config->ftr_min = min;
+	config->ftr_signed = sign;
+	config->trap_activate = fn;
+}
+
+/*
+ * Tests for id_reg_features_trap_activate.
+ * Setup a id_reg_info with three entries in id_reg_info->trap_features[].
+ * Check if the config register is updated to enable trap for the disabled
+ * features.
+ */
+static void id_reg_features_trap_activate_test(struct kunit *test)
+{
+	struct kvm_vcpu *vcpu;
+	u32 id;
+	u64 cfg_set, cfg_clear, id_reg_sys_val, id_reg_val;
+	struct id_reg_info id_reg_data;
+	struct feature_config_ctrl config0, config1, config2;
+	struct feature_config_ctrl *trap_features[] = {
+		&config0, &config1, &config2, NULL,
+	};
+
+	vcpu = test_kvm_vcpu_init(test);
+	KUNIT_EXPECT_TRUE(test, vcpu);
+	if (!vcpu)
+		return;
+
+	/* Setup id_reg_info */
+	id_reg_sys_val = 0x7777777777777777;
+	id = SYS_ID_AA64DFR0_EL1;
+	id_reg_data.sys_reg = id;
+	id_reg_data.sys_val = id_reg_sys_val;
+	id_reg_data.vcpu_limit_val  = (u64)-1;
+	id_reg_data.trap_features =
+			(const struct feature_config_ctrl *(*)[])trap_features;
+
+	/* Setup the 1st feature_config_ctrl */
+	test_trap_set0 = 0x3;
+	test_trap_clear0 = 0x0;
+	setup_feature_config_ctrl(&config0, id, 60, 2, FTR_UNSIGNED,
+				  &test_trap_activate0);
+
+	/* Setup the 2nd feature_config_ctrl */
+	test_trap_set1 = 0x30000040;
+	test_trap_clear1 = 0x40000000;
+	setup_feature_config_ctrl(&config1, id, 0, 1, FTR_UNSIGNED,
+				  &test_trap_activate1);
+
+	/* Setup the 3rd feature_config_ctrl */
+	test_trap_set2 = 0x30000000800;
+	test_trap_clear2 = 0x40000000000;
+	setup_feature_config_ctrl(&config2, id, 4, 0, FTR_SIGNED,
+				  &test_trap_activate2);
+
+#define	ftr_dis(cfg)	\
+	((u64)(((cfg)->ftr_min - 1) & 0xf) << (cfg)->ftr_shift)
+
+#define	ftr_en(cfg)	\
+	((u64)(cfg)->ftr_min << (cfg)->ftr_shift)
+
+	/* Test with features enabled for config0, 1 and 2 */
+	id_reg_val = ftr_en(&config0) | ftr_en(&config1) | ftr_en(&config2);
+	write_kvm_id_reg(vcpu->kvm, id, id_reg_val);
+	vcpu->arch.hcr_el2 = 0;
+	id_reg_features_trap_activate(vcpu, &id_reg_data);
+	KUNIT_EXPECT_EQ(test, vcpu->arch.hcr_el2, 0);
+
+
+	/* Test with features disabled for config0 only */
+	id_reg_val = ftr_dis(&config0) | ftr_en(&config1) | ftr_en(&config2);
+	write_kvm_id_reg(vcpu->kvm, id, id_reg_val);
+	vcpu->arch.hcr_el2 = 0;
+	cfg_set = test_trap_set0;
+	cfg_clear = test_trap_clear0;
+
+	id_reg_features_trap_activate(vcpu, &id_reg_data);
+	KUNIT_EXPECT_EQ(test, vcpu->arch.hcr_el2 & cfg_set, cfg_set);
+	KUNIT_EXPECT_EQ(test, vcpu->arch.hcr_el2 & cfg_clear, 0);
+
+
+	/* Test with features disabled for config0 and config1  */
+	id_reg_val = ftr_dis(&config0) | ftr_dis(&config1) | ftr_en(&config2);
+	write_kvm_id_reg(vcpu->kvm, id, id_reg_val);
+	vcpu->arch.hcr_el2 = 0;
+
+	cfg_set = test_trap_set0 | test_trap_set1;
+	cfg_clear = test_trap_clear0 | test_trap_clear1;
+
+	id_reg_features_trap_activate(vcpu, &id_reg_data);
+	KUNIT_EXPECT_EQ(test, vcpu->arch.hcr_el2 & cfg_set, cfg_set);
+	KUNIT_EXPECT_EQ(test, vcpu->arch.hcr_el2 & cfg_clear, 0);
+
+
+	/* Test with features disabled for config0, config1, and config2 */
+	id_reg_val = ftr_dis(&config0) | ftr_dis(&config1) | ftr_dis(&config2);
+	write_kvm_id_reg(vcpu->kvm, id, id_reg_val);
+	vcpu->arch.hcr_el2 = 0;
+
+	cfg_set = test_trap_set0 | test_trap_set1 | test_trap_set2;
+	cfg_clear = test_trap_clear0 | test_trap_clear1 | test_trap_clear2;
+
+	id_reg_features_trap_activate(vcpu, &id_reg_data);
+	KUNIT_EXPECT_EQ(test, vcpu->arch.hcr_el2 & cfg_set, cfg_set);
+	KUNIT_EXPECT_EQ(test, vcpu->arch.hcr_el2 & cfg_clear, 0);
+
+
+	/* Test with id_reg_info == NULL */
+	vcpu->arch.hcr_el2 = 0;
+	id_reg_features_trap_activate(vcpu, NULL);
+	KUNIT_EXPECT_EQ(test, vcpu->arch.hcr_el2, 0);
+
+
+	/* Test with id_reg_data.trap_features = NULL */
+	id_reg_data.trap_features = NULL;
+	vcpu->arch.hcr_el2 = 0;
+	id_reg_features_trap_activate(vcpu, &id_reg_data);
+	KUNIT_EXPECT_EQ(test, vcpu->arch.hcr_el2, 0);
+
+}
+
+/* Tests for vcpu_need_trap_ptrauth(). */
+static void vcpu_need_trap_ptrauth_test(struct kunit *test)
+{
+	struct kvm_vcpu *vcpu;
+	u32 id = SYS_ID_AA64ISAR1_EL1;
+
+	vcpu = test_kvm_vcpu_init(test);
+	KUNIT_EXPECT_TRUE(test, vcpu);
+	if (!vcpu)
+		return;
+
+	if (system_has_full_ptr_auth()) {
+		/* Tests with PTRAUTH disabled vCPU */
+		write_kvm_id_reg(vcpu->kvm, id, 0x0);
+		KUNIT_EXPECT_TRUE(test, vcpu_need_trap_ptrauth(vcpu));
+
+		/* GPI = 1, API = 1 */
+		write_kvm_id_reg(vcpu->kvm, id, 0x10000100);
+		KUNIT_EXPECT_TRUE(test, vcpu_need_trap_ptrauth(vcpu));
+
+		/* GPI = 1, APA = 1 */
+		write_kvm_id_reg(vcpu->kvm, id, 0x10000010);
+		KUNIT_EXPECT_TRUE(test, vcpu_need_trap_ptrauth(vcpu));
+
+		/* GPA = 1, API = 1 */
+		write_kvm_id_reg(vcpu->kvm, id, 0x01000100);
+		KUNIT_EXPECT_TRUE(test, vcpu_need_trap_ptrauth(vcpu));
+
+		/* GPA = 1, APA = 1 */
+		write_kvm_id_reg(vcpu->kvm, id, 0x01000010);
+		KUNIT_EXPECT_TRUE(test, vcpu_need_trap_ptrauth(vcpu));
+
+
+		/* Tests with PTRAUTH enabled vCPU */
+		vcpu->arch.flags |= KVM_ARM64_GUEST_HAS_PTRAUTH;
+
+		write_kvm_id_reg(vcpu->kvm, id, 0x0);
+		KUNIT_EXPECT_TRUE(test, vcpu_need_trap_ptrauth(vcpu));
+
+		/* GPI = 1, API = 1 */
+		write_kvm_id_reg(vcpu->kvm, id, 0x10000100);
+		KUNIT_EXPECT_FALSE(test, vcpu_need_trap_ptrauth(vcpu));
+
+		/* GPI = 1, APA = 1 */
+		write_kvm_id_reg(vcpu->kvm, id, 0x10000010);
+		KUNIT_EXPECT_FALSE(test, vcpu_need_trap_ptrauth(vcpu));
+
+		/* GPA = 1, API = 1 */
+		write_kvm_id_reg(vcpu->kvm, id, 0x01000100);
+		KUNIT_EXPECT_FALSE(test, vcpu_need_trap_ptrauth(vcpu));
+
+		/* GPA = 1, APA = 1 */
+		write_kvm_id_reg(vcpu->kvm, id, 0x01000010);
+		KUNIT_EXPECT_FALSE(test, vcpu_need_trap_ptrauth(vcpu));
+	} else {
+		KUNIT_EXPECT_FALSE(test, vcpu_need_trap_ptrauth(vcpu));
+	}
+}
+
 static struct kunit_case kvm_sys_regs_test_cases[] = {
 	KUNIT_CASE_PARAM(vcpu_id_reg_feature_frac_check_test, frac_gen_params),
 	KUNIT_CASE_PARAM(validate_id_aa64mmfr0_tgran2_test, tgran4_2_gen_params),
@@ -973,6 +1232,9 @@ static struct kunit_case kvm_sys_regs_test_cases[] = {
 	KUNIT_CASE(validate_id_dfr0_el1_test),
 	KUNIT_CASE(validate_mvfr1_el1_test),
 	KUNIT_CASE(validate_id_reg_test),
+	KUNIT_CASE(vcpu_need_trap_ptrauth_test),
+	KUNIT_CASE_PARAM(feature_trap_activate_test, trap_gen_params),
+	KUNIT_CASE(id_reg_features_trap_activate_test),
 	{}
 };
 
-- 
2.34.1.448.ga2b2bfdf31-goog


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 26/26] KVM: arm64: selftests: Introduce id_reg_test
  2022-01-06  4:26 ` Reiji Watanabe
  (?)
@ 2022-01-06  4:27   ` Reiji Watanabe
  -1 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:27 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

Introduce a test for aarch64 to validate basic behavior of
KVM_GET_ONE_REG and KVM_SET_ONE_REG for ID registers.

This test runs only when KVM_CAP_ARM_ID_REG_CONFIGURABLE is supported.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 tools/arch/arm64/include/asm/sysreg.h         |    1 +
 tools/testing/selftests/kvm/.gitignore        |    1 +
 tools/testing/selftests/kvm/Makefile          |    1 +
 .../selftests/kvm/aarch64/id_reg_test.c       | 1239 +++++++++++++++++
 4 files changed, 1242 insertions(+)
 create mode 100644 tools/testing/selftests/kvm/aarch64/id_reg_test.c

diff --git a/tools/arch/arm64/include/asm/sysreg.h b/tools/arch/arm64/include/asm/sysreg.h
index 7640fa27be94..be3947c125f1 100644
--- a/tools/arch/arm64/include/asm/sysreg.h
+++ b/tools/arch/arm64/include/asm/sysreg.h
@@ -793,6 +793,7 @@
 #define ID_AA64PFR0_ELx_32BIT_64BIT	0x2
 
 /* id_aa64pfr1 */
+#define ID_AA64PFR1_CSV2FRAC_SHIFT	32
 #define ID_AA64PFR1_MPAMFRAC_SHIFT	16
 #define ID_AA64PFR1_RASFRAC_SHIFT	12
 #define ID_AA64PFR1_MTE_SHIFT		8
diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore
index 3cb5ac5da087..e0a5f304375d 100644
--- a/tools/testing/selftests/kvm/.gitignore
+++ b/tools/testing/selftests/kvm/.gitignore
@@ -2,6 +2,7 @@
 /aarch64/arch_timer
 /aarch64/debug-exceptions
 /aarch64/get-reg-list
+/aarch64/id_reg_test
 /aarch64/psci_cpu_on_test
 /aarch64/vgic_init
 /s390x/memop
diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
index 17342b575e85..0162a7d98889 100644
--- a/tools/testing/selftests/kvm/Makefile
+++ b/tools/testing/selftests/kvm/Makefile
@@ -94,6 +94,7 @@ TEST_GEN_PROGS_x86_64 += system_counter_offset_test
 TEST_GEN_PROGS_aarch64 += aarch64/arch_timer
 TEST_GEN_PROGS_aarch64 += aarch64/debug-exceptions
 TEST_GEN_PROGS_aarch64 += aarch64/get-reg-list
+TEST_GEN_PROGS_aarch64 += aarch64/id_reg_test
 TEST_GEN_PROGS_aarch64 += aarch64/psci_cpu_on_test
 TEST_GEN_PROGS_aarch64 += aarch64/vgic_init
 TEST_GEN_PROGS_aarch64 += demand_paging_test
diff --git a/tools/testing/selftests/kvm/aarch64/id_reg_test.c b/tools/testing/selftests/kvm/aarch64/id_reg_test.c
new file mode 100644
index 000000000000..e2cac415ad57
--- /dev/null
+++ b/tools/testing/selftests/kvm/aarch64/id_reg_test.c
@@ -0,0 +1,1239 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#define _GNU_SOURCE
+
+#include <stdlib.h>
+#include <time.h>
+#include <pthread.h>
+#include <linux/kvm.h>
+#include <linux/sizes.h>
+
+#include "kvm_util.h"
+#include "processor.h"
+#include "vgic.h"
+
+/*
+ * id_reg_test.c - Tests reading/writing the aarch64's ID registers
+ *
+ * The test validates KVM_SET_ONE_REG/KVM_GET_ONE_REG ioctl for ID
+ * registers as well as reading ID register from the guest works fine.
+ */
+
+/* Reserved ID registers */
+#define	SYS_ID_REG_3_3_EL1		sys_reg(3, 0, 0, 3, 3)
+#define	SYS_ID_REG_3_7_EL1		sys_reg(3, 0, 0, 3, 7)
+
+#define	SYS_ID_REG_4_2_EL1		sys_reg(3, 0, 0, 4, 2)
+#define	SYS_ID_REG_4_3_EL1		sys_reg(3, 0, 0, 4, 3)
+#define	SYS_ID_REG_4_5_EL1		sys_reg(3, 0, 0, 4, 5)
+#define	SYS_ID_REG_4_6_EL1		sys_reg(3, 0, 0, 4, 6)
+#define	SYS_ID_REG_4_7_EL1		sys_reg(3, 0, 0, 4, 7)
+
+#define	SYS_ID_REG_5_2_EL1		sys_reg(3, 0, 0, 5, 2)
+#define	SYS_ID_REG_5_3_EL1		sys_reg(3, 0, 0, 5, 3)
+#define	SYS_ID_REG_5_6_EL1		sys_reg(3, 0, 0, 5, 6)
+#define	SYS_ID_REG_5_7_EL1		sys_reg(3, 0, 0, 5, 7)
+
+#define	SYS_ID_REG_6_2_EL1		sys_reg(3, 0, 0, 6, 2)
+#define	SYS_ID_REG_6_3_EL1		sys_reg(3, 0, 0, 6, 3)
+#define	SYS_ID_REG_6_4_EL1		sys_reg(3, 0, 0, 6, 4)
+#define	SYS_ID_REG_6_5_EL1		sys_reg(3, 0, 0, 6, 5)
+#define	SYS_ID_REG_6_6_EL1		sys_reg(3, 0, 0, 6, 6)
+#define	SYS_ID_REG_6_7_EL1		sys_reg(3, 0, 0, 6, 7)
+
+#define	SYS_ID_REG_7_3_EL1		sys_reg(3, 0, 0, 7, 3)
+#define	SYS_ID_REG_7_4_EL1		sys_reg(3, 0, 0, 7, 4)
+#define	SYS_ID_REG_7_5_EL1		sys_reg(3, 0, 0, 7, 5)
+#define	SYS_ID_REG_7_6_EL1		sys_reg(3, 0, 0, 7, 6)
+#define	SYS_ID_REG_7_7_EL1		sys_reg(3, 0, 0, 7, 7)
+
+#define	READ_ID_REG_FN(name)	read_## name ## _EL1
+
+#define	DEFINE_READ_SYS_REG(reg_name)			\
+uint64_t read_##reg_name(void)				\
+{							\
+	return read_sysreg_s(SYS_##reg_name);		\
+}
+
+#define DEFINE_READ_ID_REG(name)	\
+	DEFINE_READ_SYS_REG(name ## _EL1)
+
+#define	__ID_REG(reg_name)		\
+	.name = #reg_name,		\
+	.id = SYS_## reg_name ##_EL1,	\
+	.read_reg = READ_ID_REG_FN(reg_name),
+
+#define	ID_REG_ENT(reg_name)	\
+	[ID_IDX(reg_name)] = { __ID_REG(reg_name) }
+
+/* Functions to read each ID register */
+/* CRm=1 */
+DEFINE_READ_ID_REG(ID_PFR0)
+DEFINE_READ_ID_REG(ID_PFR1)
+DEFINE_READ_ID_REG(ID_DFR0)
+DEFINE_READ_ID_REG(ID_AFR0)
+DEFINE_READ_ID_REG(ID_MMFR0)
+DEFINE_READ_ID_REG(ID_MMFR1)
+DEFINE_READ_ID_REG(ID_MMFR2)
+DEFINE_READ_ID_REG(ID_MMFR3)
+
+/* CRm=2 */
+DEFINE_READ_ID_REG(ID_ISAR0)
+DEFINE_READ_ID_REG(ID_ISAR1)
+DEFINE_READ_ID_REG(ID_ISAR2)
+DEFINE_READ_ID_REG(ID_ISAR3)
+DEFINE_READ_ID_REG(ID_ISAR4)
+DEFINE_READ_ID_REG(ID_ISAR5)
+DEFINE_READ_ID_REG(ID_MMFR4)
+DEFINE_READ_ID_REG(ID_ISAR6)
+
+/* CRm=3 */
+DEFINE_READ_ID_REG(MVFR0)
+DEFINE_READ_ID_REG(MVFR1)
+DEFINE_READ_ID_REG(MVFR2)
+DEFINE_READ_ID_REG(ID_REG_3_3)
+DEFINE_READ_ID_REG(ID_PFR2)
+DEFINE_READ_ID_REG(ID_DFR1)
+DEFINE_READ_ID_REG(ID_MMFR5)
+DEFINE_READ_ID_REG(ID_REG_3_7)
+
+/* CRm=4 */
+DEFINE_READ_ID_REG(ID_AA64PFR0)
+DEFINE_READ_ID_REG(ID_AA64PFR1)
+DEFINE_READ_ID_REG(ID_REG_4_2)
+DEFINE_READ_ID_REG(ID_REG_4_3)
+DEFINE_READ_ID_REG(ID_AA64ZFR0)
+DEFINE_READ_ID_REG(ID_REG_4_5)
+DEFINE_READ_ID_REG(ID_REG_4_6)
+DEFINE_READ_ID_REG(ID_REG_4_7)
+
+/* CRm=5 */
+DEFINE_READ_ID_REG(ID_AA64DFR0)
+DEFINE_READ_ID_REG(ID_AA64DFR1)
+DEFINE_READ_ID_REG(ID_REG_5_2)
+DEFINE_READ_ID_REG(ID_REG_5_3)
+DEFINE_READ_ID_REG(ID_AA64AFR0)
+DEFINE_READ_ID_REG(ID_AA64AFR1)
+DEFINE_READ_ID_REG(ID_REG_5_6)
+DEFINE_READ_ID_REG(ID_REG_5_7)
+
+/* CRm=6 */
+DEFINE_READ_ID_REG(ID_AA64ISAR0)
+DEFINE_READ_ID_REG(ID_AA64ISAR1)
+DEFINE_READ_ID_REG(ID_REG_6_2)
+DEFINE_READ_ID_REG(ID_REG_6_3)
+DEFINE_READ_ID_REG(ID_REG_6_4)
+DEFINE_READ_ID_REG(ID_REG_6_5)
+DEFINE_READ_ID_REG(ID_REG_6_6)
+DEFINE_READ_ID_REG(ID_REG_6_7)
+
+/* CRm=7 */
+DEFINE_READ_ID_REG(ID_AA64MMFR0)
+DEFINE_READ_ID_REG(ID_AA64MMFR1)
+DEFINE_READ_ID_REG(ID_AA64MMFR2)
+DEFINE_READ_ID_REG(ID_REG_7_3)
+DEFINE_READ_ID_REG(ID_REG_7_4)
+DEFINE_READ_ID_REG(ID_REG_7_5)
+DEFINE_READ_ID_REG(ID_REG_7_6)
+DEFINE_READ_ID_REG(ID_REG_7_7)
+
+#define	ID_IDX(name)	REG_IDX_## name
+
+enum id_reg_idx {
+	/* CRm=1 */
+	ID_IDX(ID_PFR0) = 0,
+	ID_IDX(ID_PFR1),
+	ID_IDX(ID_DFR0),
+	ID_IDX(ID_AFR0),
+	ID_IDX(ID_MMFR0),
+	ID_IDX(ID_MMFR1),
+	ID_IDX(ID_MMFR2),
+	ID_IDX(ID_MMFR3),
+
+	/* CRm=2 */
+	ID_IDX(ID_ISAR0),
+	ID_IDX(ID_ISAR1),
+	ID_IDX(ID_ISAR2),
+	ID_IDX(ID_ISAR3),
+	ID_IDX(ID_ISAR4),
+	ID_IDX(ID_ISAR5),
+	ID_IDX(ID_MMFR4),
+	ID_IDX(ID_ISAR6),
+
+	/* CRm=3 */
+	ID_IDX(MVFR0),
+	ID_IDX(MVFR1),
+	ID_IDX(MVFR2),
+	ID_IDX(ID_REG_3_3),
+	ID_IDX(ID_PFR2),
+	ID_IDX(ID_DFR1),
+	ID_IDX(ID_MMFR5),
+	ID_IDX(ID_REG_3_7),
+
+	/* CRm=4 */
+	ID_IDX(ID_AA64PFR0),
+	ID_IDX(ID_AA64PFR1),
+	ID_IDX(ID_REG_4_2),
+	ID_IDX(ID_REG_4_3),
+	ID_IDX(ID_AA64ZFR0),
+	ID_IDX(ID_REG_4_5),
+	ID_IDX(ID_REG_4_6),
+	ID_IDX(ID_REG_4_7),
+
+	/* CRm=5 */
+	ID_IDX(ID_AA64DFR0),
+	ID_IDX(ID_AA64DFR1),
+	ID_IDX(ID_REG_5_2),
+	ID_IDX(ID_REG_5_3),
+	ID_IDX(ID_AA64AFR0),
+	ID_IDX(ID_AA64AFR1),
+	ID_IDX(ID_REG_5_6),
+	ID_IDX(ID_REG_5_7),
+
+	/* CRm=6 */
+	ID_IDX(ID_AA64ISAR0),
+	ID_IDX(ID_AA64ISAR1),
+	ID_IDX(ID_REG_6_2),
+	ID_IDX(ID_REG_6_3),
+	ID_IDX(ID_REG_6_4),
+	ID_IDX(ID_REG_6_5),
+	ID_IDX(ID_REG_6_6),
+	ID_IDX(ID_REG_6_7),
+
+	/* CRm=7 */
+	ID_IDX(ID_AA64MMFR0),
+	ID_IDX(ID_AA64MMFR1),
+	ID_IDX(ID_AA64MMFR2),
+	ID_IDX(ID_REG_7_3),
+	ID_IDX(ID_REG_7_4),
+	ID_IDX(ID_REG_7_5),
+	ID_IDX(ID_REG_7_6),
+	ID_IDX(ID_REG_7_7),
+};
+
+struct id_reg_test_info {
+	char		*name;
+	uint32_t	id;
+	/* Indicates the register can be set to 0 */
+	bool		can_clear;
+	uint64_t	initial_value;
+	uint64_t	current_value;
+	uint64_t	(*read_reg)(void);
+};
+
+#define	ID_REG_INFO(name)	(&id_reg_list[ID_IDX(name)])
+static struct id_reg_test_info id_reg_list[] = {
+	/* CRm=1 */
+	ID_REG_ENT(ID_PFR0),
+	ID_REG_ENT(ID_PFR1),
+	ID_REG_ENT(ID_DFR0),
+	ID_REG_ENT(ID_AFR0),
+	ID_REG_ENT(ID_MMFR0),
+	ID_REG_ENT(ID_MMFR1),
+	ID_REG_ENT(ID_MMFR2),
+	ID_REG_ENT(ID_MMFR3),
+
+	/* CRm=2 */
+	ID_REG_ENT(ID_ISAR0),
+	ID_REG_ENT(ID_ISAR1),
+	ID_REG_ENT(ID_ISAR2),
+	ID_REG_ENT(ID_ISAR3),
+	ID_REG_ENT(ID_ISAR4),
+	ID_REG_ENT(ID_ISAR5),
+	ID_REG_ENT(ID_MMFR4),
+	ID_REG_ENT(ID_ISAR6),
+
+	/* CRm=3 */
+	ID_REG_ENT(MVFR0),
+	ID_REG_ENT(MVFR1),
+	ID_REG_ENT(MVFR2),
+	ID_REG_ENT(ID_REG_3_3),
+	ID_REG_ENT(ID_PFR2),
+	ID_REG_ENT(ID_DFR1),
+	ID_REG_ENT(ID_MMFR5),
+	ID_REG_ENT(ID_REG_3_7),
+
+	/* CRm=4 */
+	ID_REG_ENT(ID_AA64PFR0),
+	ID_REG_ENT(ID_AA64PFR1),
+	ID_REG_ENT(ID_REG_4_2),
+	ID_REG_ENT(ID_REG_4_3),
+	ID_REG_ENT(ID_AA64ZFR0),
+	ID_REG_ENT(ID_REG_4_5),
+	ID_REG_ENT(ID_REG_4_6),
+	ID_REG_ENT(ID_REG_4_7),
+
+	/* CRm=5 */
+	ID_REG_ENT(ID_AA64DFR0),
+	ID_REG_ENT(ID_AA64DFR1),
+	ID_REG_ENT(ID_REG_5_2),
+	ID_REG_ENT(ID_REG_5_3),
+	ID_REG_ENT(ID_AA64AFR0),
+	ID_REG_ENT(ID_AA64AFR1),
+	ID_REG_ENT(ID_REG_5_6),
+	ID_REG_ENT(ID_REG_5_7),
+
+	/* CRm=6 */
+	ID_REG_ENT(ID_AA64ISAR0),
+	ID_REG_ENT(ID_AA64ISAR1),
+	ID_REG_ENT(ID_REG_6_2),
+	ID_REG_ENT(ID_REG_6_3),
+	ID_REG_ENT(ID_REG_6_4),
+	ID_REG_ENT(ID_REG_6_5),
+	ID_REG_ENT(ID_REG_6_6),
+	ID_REG_ENT(ID_REG_6_7),
+
+	/* CRm=7 */
+	ID_REG_ENT(ID_AA64MMFR0),
+	ID_REG_ENT(ID_AA64MMFR1),
+	ID_REG_ENT(ID_AA64MMFR2),
+	ID_REG_ENT(ID_REG_7_3),
+	ID_REG_ENT(ID_REG_7_4),
+	ID_REG_ENT(ID_REG_7_5),
+	ID_REG_ENT(ID_REG_7_6),
+	ID_REG_ENT(ID_REG_7_7),
+};
+
+static bool aarch32_support = true;
+
+/* Utilities to get a feature field from ID register value */
+static inline int
+cpuid_signed_field_width(uint64_t id_val, int field, int width)
+{
+	return (s64)(id_val << (64 - width - field)) >> (64 - width);
+}
+
+static unsigned int
+cpuid_unsigned_field_width(uint64_t id_val, int field, int width)
+{
+	return (uint64_t)(id_val << (64 - width - field)) >> (64 - width);
+}
+
+static inline int __attribute_const__
+cpuid_extract_field_width(uint64_t id_val, int field, int width, bool sign)
+{
+	return (sign) ? cpuid_signed_field_width(id_val, field, width) :
+			cpuid_unsigned_field_width(id_val, field, width);
+}
+
+#define is_id_reg(id)	\
+	(sys_reg_Op0(id) == 3 && sys_reg_Op1(id) == 0 &&	\
+	 sys_reg_CRn(id) == 0 && sys_reg_CRm(id) >= 0 &&	\
+	 sys_reg_CRm(id) < 8)
+
+#define	GET_ID_FIELD(regval, shift, is_signed)	\
+	cpuid_extract_field_width(regval, shift, 4, is_signed)
+
+#define	GET_ID_UFIELD(regval, shift)	\
+	cpuid_unsigned_field_width(regval, shift, 4)
+
+#define	UPDATE_ID_UFIELD(regval, shift, fval)	\
+	(((regval) & ~(0xfULL << (shift))) |	\
+	 (((uint64_t)((fval) & 0xf)) << (shift)))
+
+void pmu_init(struct kvm_vm *vm, uint32_t vcpu)
+{
+	struct kvm_device_attr attr = {
+		.group = KVM_ARM_VCPU_PMU_V3_CTRL,
+		.attr = KVM_ARM_VCPU_PMU_V3_INIT,
+	};
+	vcpu_ioctl(vm, vcpu, KVM_SET_DEVICE_ATTR, &attr);
+}
+
+void sve_init(struct kvm_vm *vm, uint32_t vcpu)
+{
+	int feature = KVM_ARM_VCPU_SVE;
+
+	vcpu_ioctl(vm, vcpu, KVM_ARM_VCPU_FINALIZE, &feature);
+}
+
+#define GICD_BASE_GPA			0x8000000ULL
+#define GICR_BASE_GPA			0x80A0000ULL
+
+void test_vgic_init(struct kvm_vm *vm, uint32_t vcpu)
+{
+	/* We jsut need to configure gic v3 (we don't use it though) */
+	vgic_v3_setup(vm, 1, GICD_BASE_GPA, GICR_BASE_GPA);
+}
+
+static bool is_aarch32_id_reg(uint32_t id)
+{
+	uint32_t crm, op2;
+
+	if (!is_id_reg(id))
+		return false;
+
+	crm = sys_reg_CRm(id);
+	op2 = sys_reg_Op2(id);
+	if (crm == 1 || crm == 2 || (crm == 3 && (op2 != 3 && op2 != 7)))
+		/* AArch32 ID register */
+		return true;
+
+	return false;
+}
+
+#define	MAX_CAPS	2
+struct feature_test_info {
+	char	*name;	/* Feature Name (Debug information) */
+
+	/* ID register that identifies the presence of the feature */
+	struct id_reg_test_info	*sreg;
+
+	/*
+	 * Bit position of the ID register field that identifies
+	 * the presence of the feature.
+	 */
+	int	shift;
+
+	/* Min value of the field that indicates the presence of the feature. */
+	int	min;
+	bool	is_sign;	/* Is the field signed or unsigned ? */
+	int	ncaps;		/* Number of valid Capabilities in caps[] */
+
+	/* KVM_CAP_* Capabilities to indicates that KVM supports this feature */
+	long	caps[MAX_CAPS];
+
+	/* struct kvm_enable_cap to use the capability if needed */
+	struct kvm_enable_cap	*opt_in_cap;
+
+	/* Should the guest check the ID register for this feature ? */
+	bool	run_test;
+
+	/*
+	 * Extra initialization function to enable the feature if needed.
+	 * (e.g. KVM_ARM_VCPU_FINALIZE for SVE)
+	 */
+	void	(*init_feature)(struct kvm_vm *vm, uint32_t vcpuid);
+
+	/* struct kvm_vcpu_init to opt-in the feature if needed */
+	struct kvm_vcpu_init	*vcpu_init;
+};
+
+/* Information for opt-in CPU features */
+static struct feature_test_info feature_test_info_table[] = {
+	{
+		.name = "SVE",
+		.sreg = ID_REG_INFO(ID_AA64PFR0),
+		.shift = ID_AA64PFR0_SVE_SHIFT,
+		.min = 1,
+		.caps = {KVM_CAP_ARM_SVE},
+		.ncaps = 1,
+		.init_feature = sve_init,
+		.vcpu_init = &(struct kvm_vcpu_init) {
+			.features = {1ULL << KVM_ARM_VCPU_SVE},
+		},
+	},
+	{
+		.name = "GIC",
+		.sreg = ID_REG_INFO(ID_AA64PFR0),
+		.shift = ID_AA64PFR0_GIC_SHIFT,
+		.min = 1,
+		.caps = {KVM_CAP_IRQCHIP},
+		.ncaps = 1,
+		.init_feature = test_vgic_init,
+	},
+	{
+		.name = "MTE",
+		.sreg = ID_REG_INFO(ID_AA64PFR1),
+		.shift = ID_AA64PFR1_MTE_SHIFT,
+		.min = 2,
+		.caps = {KVM_CAP_ARM_MTE},
+		.ncaps = 1,
+		.opt_in_cap = &(struct kvm_enable_cap) {
+				.cap = KVM_CAP_ARM_MTE,
+		},
+	},
+	{
+		.name = "PMUV3",
+		.sreg = ID_REG_INFO(ID_AA64DFR0),
+		.shift = ID_AA64DFR0_PMUVER_SHIFT,
+		.min = 1,
+		.init_feature = pmu_init,
+		.caps = {KVM_CAP_ARM_PMU_V3},
+		.ncaps = 1,
+		.vcpu_init = &(struct kvm_vcpu_init) {
+			.features = {1ULL << KVM_ARM_VCPU_PMU_V3},
+		},
+	},
+	{
+		.name = "PERFMON",
+		.sreg = ID_REG_INFO(ID_DFR0),
+		.shift = ID_DFR0_PERFMON_SHIFT,
+		.min = 3,
+		.init_feature = pmu_init,
+		.caps = {KVM_CAP_ARM_PMU_V3},
+		.ncaps = 1,
+		.vcpu_init = &(struct kvm_vcpu_init) {
+			.features = {1ULL << KVM_ARM_VCPU_PMU_V3},
+		},
+	},
+};
+
+static void walk_id_reg_list(void (*fn)(struct id_reg_test_info *r, void *arg),
+			     void *arg)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(id_reg_list); i++)
+		fn(&id_reg_list[i], arg);
+}
+
+static void guest_code_id_reg_check_one(struct id_reg_test_info *idr, void *arg)
+{
+	uint64_t v = idr->read_reg();
+
+	GUEST_ASSERT_2(v == idr->current_value, idr->name, idr->current_value);
+}
+
+static void guest_code_id_reg_check_all(uint32_t cpu)
+{
+	walk_id_reg_list(guest_code_id_reg_check_one, NULL);
+	GUEST_DONE();
+}
+
+static void guest_code_do_nothing(uint32_t cpu)
+{
+	GUEST_DONE();
+}
+
+static void guest_code_feature_check(uint32_t cpu)
+{
+	int i;
+	struct feature_test_info *finfo;
+
+	for (i = 0; i < ARRAY_SIZE(feature_test_info_table); i++) {
+		finfo = &feature_test_info_table[i];
+		if (finfo->run_test)
+			guest_code_id_reg_check_one(finfo->sreg, NULL);
+	}
+
+	GUEST_DONE();
+}
+
+static void guest_code_ptrauth_check(uint32_t cpuid)
+{
+	struct id_reg_test_info *sreg = ID_REG_INFO(ID_AA64ISAR1);
+	uint64_t val = sreg->read_reg();
+
+	GUEST_ASSERT_2(val == sreg->current_value, "PTRAUTH", val);
+	GUEST_DONE();
+}
+
+static void reset_id_reg_info_current_value(struct id_reg_test_info *info,
+					    void *arg)
+{
+	info->current_value = info->initial_value;
+}
+
+/* Reset current_value field of each id_reg_test_info */
+static void reset_id_reg_info(void)
+{
+	walk_id_reg_list(reset_id_reg_info_current_value, NULL);
+}
+
+static struct kvm_vm *test_vm_create(uint32_t nvcpus,
+		void (*guest_code)(uint32_t), struct kvm_vcpu_init *init,
+		struct kvm_enable_cap *cap)
+{
+	struct kvm_vm *vm;
+	uint32_t cpuid;
+	uint64_t mem_pages;
+
+	mem_pages = DEFAULT_GUEST_PHY_PAGES + DEFAULT_STACK_PGS * nvcpus;
+	mem_pages += mem_pages / (PTES_PER_MIN_PAGE * 2);
+	mem_pages = vm_adjust_num_guest_pages(VM_MODE_DEFAULT, mem_pages);
+
+	vm = vm_create(VM_MODE_DEFAULT, mem_pages, O_RDWR);
+	if (cap)
+		vm_enable_cap(vm, cap);
+
+	kvm_vm_elf_load(vm, program_invocation_name);
+
+	if (init && init->target == -1) {
+		struct kvm_vcpu_init preferred;
+
+		vm_ioctl(vm, KVM_ARM_PREFERRED_TARGET, &preferred);
+		init->target = preferred.target;
+	}
+
+	vm_init_descriptor_tables(vm);
+	for (cpuid = 0; cpuid < nvcpus; cpuid++) {
+		aarch64_vcpu_add_default(vm, cpuid, init, guest_code);
+		vcpu_init_descriptor_tables(vm, cpuid);
+	}
+
+	ucall_init(vm, NULL);
+	return vm;
+}
+
+static void test_vm_free(struct kvm_vm *vm)
+{
+	ucall_uninit(vm);
+	kvm_vm_free(vm);
+}
+
+#define	TEST_RUN(vm, cpu)	\
+	(test_vcpu_run(__func__, __LINE__, vm, cpu, true))
+
+#define	TEST_RUN_NO_SYNC_DATA(vm, cpu)	\
+	(test_vcpu_run(__func__, __LINE__, vm, cpu, false))
+
+static int test_vcpu_run(const char *test_name, int line,
+			 struct kvm_vm *vm, uint32_t vcpuid, bool sync_data)
+{
+	struct ucall uc;
+	int ret;
+
+	if (sync_data) {
+		sync_global_to_guest(vm, id_reg_list);
+		sync_global_to_guest(vm, feature_test_info_table);
+	}
+
+	vcpu_args_set(vm, vcpuid, 1, vcpuid);
+
+	ret = _vcpu_run(vm, vcpuid);
+	if (ret) {
+		ret = errno;
+		goto sync_exit;
+	}
+
+	switch (get_ucall(vm, vcpuid, &uc)) {
+	case UCALL_SYNC:
+	case UCALL_DONE:
+		ret = 0;
+		break;
+	case UCALL_ABORT:
+		TEST_FAIL(
+		    "%s (%s) at line %d (user %s at line %d), args[3]=0x%lx",
+		    (char *)uc.args[0], (char *)uc.args[2], (int)uc.args[1],
+		    test_name, line, uc.args[3]);
+		break;
+	default:
+		TEST_FAIL("Unexpected guest exit\n");
+	}
+
+sync_exit:
+	if (sync_data) {
+		sync_global_from_guest(vm, id_reg_list);
+		sync_global_from_guest(vm, feature_test_info_table);
+	}
+	return ret;
+}
+
+struct vm_vcpu_arg {
+	struct kvm_vm	*vm;
+	uint32_t	vcpuid;
+	bool		after_run;
+};
+
+/*
+ * Test if KVM_SET_ONE_REG can work with the value KVM_GET_ONE_REG returns,
+ * KVM_SET_ONE_REG with zero works before KVM_RUN (and fails after KVM_RUN),
+ * and KVM_GET_ONE_REG returns the value KVM_SET_ONE_REG sets.
+ */
+static void test_get_set_id_reg(struct id_reg_test_info *sreg, void *arg)
+{
+	struct kvm_vm *vm = ((struct vm_vcpu_arg *)arg)->vm;
+	uint32_t vcpuid = ((struct vm_vcpu_arg *)arg)->vcpuid;
+	bool after_run = ((struct vm_vcpu_arg *)arg)->after_run;
+	struct kvm_one_reg one_reg;
+	uint64_t reg_val, tval;
+	int ret;
+
+	one_reg.addr = (uint64_t)&reg_val;
+	one_reg.id = KVM_ARM64_SYS_REG(sreg->id);
+
+	/* Check the current register value */
+	vcpu_ioctl(vm, vcpuid, KVM_GET_ONE_REG, &one_reg);
+	TEST_ASSERT(reg_val == sreg->current_value,
+		    "GET(%s) didn't return 0x%lx but 0x%lx",
+		    sreg->name, sreg->current_value, reg_val);
+	tval = reg_val;
+
+	/* Try to clear the register that should be able to be cleared. */
+	if ((reg_val != 0) && (sreg->can_clear)) {
+		reg_val = 0;
+		ret = _vcpu_ioctl(vm, vcpuid, KVM_SET_ONE_REG, &one_reg);
+		if (after_run) {
+			/* Expect an error after KVM_RUN */
+			TEST_ASSERT(ret,
+				    "Clearing %s unexpectedly worked\n",
+				    sreg->name);
+		} else {
+			TEST_ASSERT(!ret,
+				    "Clearing %s didn't work\n", sreg->name);
+			/*
+			 * Make sure that KVM_GET_ONE_REG provides the value
+			 * we set.
+			 */
+			vcpu_ioctl(vm, vcpuid, KVM_GET_ONE_REG, &one_reg);
+			TEST_ASSERT(reg_val == 0,
+				    "GET(%s) didn't return 0x%lx but 0x%lx",
+				    sreg->name, (uint64_t)0, reg_val);
+		}
+	}
+
+	/* Check if KVM_SET_ONE_REG works with the original value. */
+	reg_val = tval;
+	ret = _vcpu_ioctl(vm, vcpuid, KVM_SET_ONE_REG, &one_reg);
+	TEST_ASSERT(ret == 0, "Setting the same ID reg value should work\n");
+
+	/* Make sure that KVM_GET_ONE_REG provides the value we set. */
+	vcpu_ioctl(vm, vcpuid, KVM_GET_ONE_REG, &one_reg);
+	TEST_ASSERT(reg_val == tval,
+		    "GET(%s) didn't return 0x%lx but 0x%lx",
+		    sreg->name, sreg->current_value, reg_val);
+}
+
+/*
+ * Test if KVM_SET_ONE_REG with the current value works before KVM_RUN,
+ * values of ID registers the guest sees are consistent with the ones
+ * userspace sees, and KVM_SET_ONE_REG after KVM_RUN works when the
+ * specified value is the same as the current one (fails otherwise).
+ */
+static void test_id_regs_basic(void)
+{
+	struct kvm_vm *vm;
+	struct vm_vcpu_arg arg = { .vcpuid = 0 };
+	int ret;
+
+	reset_id_reg_info();
+
+	vm = test_vm_create(1, guest_code_id_reg_check_all, NULL, NULL);
+
+	arg.vm = vm;
+	walk_id_reg_list(test_get_set_id_reg, &arg);
+
+	ret = TEST_RUN(vm, 0);
+	assert(!ret);
+
+	arg.after_run = true;
+	walk_id_reg_list(test_get_set_id_reg, &arg);
+
+	test_vm_free(vm);
+}
+
+static bool caps_are_supported(long *caps, int ncaps)
+{
+	int i;
+
+	for (i = 0; i < ncaps; i++) {
+		if (kvm_check_cap(caps[i]) <= 0)
+			return false;
+	}
+	return true;
+}
+
+#define	NCAPS_PTRAUTH	2
+
+/*
+ * Test if the ID register value reflects the ptrauth feature configuration.
+ * KVM_SET_ONE_REG should work as long as the requested value is consistent
+ * with the ptrauth feature configuration.
+ */
+static void test_feature_ptrauth(void)
+{
+	struct kvm_one_reg one_reg;
+	struct kvm_vcpu_init init;
+	struct kvm_vm *vm = NULL;
+	struct id_reg_test_info *sreg = ID_REG_INFO(ID_AA64ISAR1);
+	uint32_t vcpu = 0;
+	int64_t rval;
+	int ret;
+	int apa, api, gpa, gpi;
+	char *name = "PTRAUTH";
+	long caps[NCAPS_PTRAUTH] = {KVM_CAP_ARM_PTRAUTH_ADDRESS,
+				    KVM_CAP_ARM_PTRAUTH_GENERIC};
+
+	reset_id_reg_info();
+	one_reg.addr = (uint64_t)&rval;
+	one_reg.id = KVM_ARM64_SYS_REG(sreg->id);
+
+	if (caps_are_supported(caps, NCAPS_PTRAUTH)) {
+
+		/* Test with feature enabled */
+		memset(&init, 0, sizeof(init));
+		init.target = -1;
+		init.features[0] = (1ULL << KVM_ARM_VCPU_PTRAUTH_ADDRESS |
+				    1ULL << KVM_ARM_VCPU_PTRAUTH_GENERIC);
+		vm = test_vm_create(1, guest_code_ptrauth_check, &init, NULL);
+		vcpu_ioctl(vm, vcpu, KVM_GET_ONE_REG, &one_reg);
+
+		/* Make sure values of apa/api/gpa/gpi fields are expected */
+		apa = GET_ID_UFIELD(rval, ID_AA64ISAR1_APA_SHIFT);
+		api = GET_ID_UFIELD(rval, ID_AA64ISAR1_API_SHIFT);
+		gpa = GET_ID_UFIELD(rval, ID_AA64ISAR1_GPA_SHIFT);
+		gpi = GET_ID_UFIELD(rval, ID_AA64ISAR1_GPI_SHIFT);
+
+		TEST_ASSERT((apa > 0) || (api > 0),
+			    "Either apa(0x%x) or api(0x%x) must be available",
+			    apa, gpa);
+		TEST_ASSERT((gpa > 0) || (gpi > 0),
+			    "Either gpa(0x%x) or gpi(0x%x) must be available",
+			    gpa, gpi);
+
+		TEST_ASSERT((apa > 0) ^ (api > 0),
+			    "Both apa(0x%x) and api(0x%x) must not be available",
+			    apa, api);
+		TEST_ASSERT((gpa > 0) ^ (gpi > 0),
+			    "Both gpa(0x%x) and gpi(0x%x) must not be available",
+			    gpa, gpi);
+
+		sreg->current_value = rval;
+
+		pr_debug("%s: Test with %s enabled (%s: 0x%lx)\n",
+			 __func__, name, sreg->name, sreg->current_value);
+
+		/* Make sure that the guest sees the same ID register value. */
+		ret = TEST_RUN(vm, vcpu);
+
+		TEST_ASSERT(!ret, "%s:KVM_RUN failed with %s enabled",
+			    __func__, name);
+		test_vm_free(vm);
+	}
+
+	reset_id_reg_info();
+
+	/* Test with feature disabled */
+	vm = test_vm_create(1, guest_code_feature_check, NULL, NULL);
+	vcpu_ioctl(vm, vcpu, KVM_GET_ONE_REG, &one_reg);
+
+	apa = GET_ID_UFIELD(rval, ID_AA64ISAR1_APA_SHIFT);
+	api = GET_ID_UFIELD(rval, ID_AA64ISAR1_API_SHIFT);
+	gpa = GET_ID_UFIELD(rval, ID_AA64ISAR1_GPA_SHIFT);
+	gpi = GET_ID_UFIELD(rval, ID_AA64ISAR1_GPI_SHIFT);
+	TEST_ASSERT(!apa && !api && !gpa && !gpi,
+	    "apa(0x%x), api(0x%x), gpa(0x%x), gpi(0x%x) must be zero",
+	    apa, api, gpa, gpi);
+
+	pr_debug("%s: Test with %s disabled (%s: 0x%lx)\n",
+		 __func__, name, sreg->name, sreg->current_value);
+
+	/* Make sure that the guest sees the same ID register value. */
+	ret = TEST_RUN(vm, vcpu);
+	TEST_ASSERT(!ret, "%s TEST_RUN failed with %s enabled, ret=0x%x",
+		    __func__, name, ret);
+
+	test_vm_free(vm);
+}
+
+static bool feature_caps_are_available(struct feature_test_info *finfo)
+{
+	return ((finfo->ncaps > 0) &&
+		caps_are_supported(finfo->caps, finfo->ncaps));
+}
+
+/*
+ * Test if the ID register value reflects the feature configuration.
+ * KVM_SET_ONE_REG should work as long as the requested value is
+ * consistent with the feature configuration.
+ */
+static void test_feature(struct feature_test_info *finfo)
+{
+	struct id_reg_test_info *sreg = finfo->sreg;
+	struct kvm_one_reg one_reg;
+	struct kvm_vcpu_init init, *initp = NULL;
+	struct kvm_vm *vm = NULL;
+	int64_t fval, reg_val;
+	uint32_t vcpu = 0;
+	bool is_sign = finfo->is_sign;
+	int min = finfo->min;
+	int shift = finfo->shift;
+	int ret;
+
+	pr_debug("%s: %s (reg %s)\n", __func__, finfo->name, sreg->name);
+
+	reset_id_reg_info();
+
+	if (is_aarch32_id_reg(sreg->id) && !aarch32_support)
+		/*
+		 * AArch32 is not supported. Skip testing with the AArch32
+		 * ID register.
+		 */
+		return;
+
+	/* Indicate that guest runs the test for the feature */
+	finfo->run_test = 1;
+	one_reg.addr = (uint64_t)&reg_val;
+	one_reg.id = KVM_ARM64_SYS_REG(sreg->id);
+
+	/*
+	 * Test with feature enabled if the feature is exposed in the default
+	 * ID register value or the capabilities are supported at KVM level.
+	 */
+	if ((GET_ID_FIELD(sreg->initial_value, shift, is_sign) >= min) ||
+	    feature_caps_are_available(finfo)) {
+		if (finfo->vcpu_init) {
+			/* Need to enable the feature via KVM_ARM_VCPU_INIT. */
+			memset(&init, 0, sizeof(init));
+			init = *finfo->vcpu_init;
+			init.target = -1;
+			initp = &init;
+		}
+
+		vm = test_vm_create(1, guest_code_feature_check, initp,
+				    finfo->opt_in_cap);
+		if (finfo->init_feature)
+			/* Run any required extra process to use the feature */
+			finfo->init_feature(vm, vcpu);
+
+		/* Check if the ID register value indicates the feature */
+		vcpu_ioctl(vm, vcpu, KVM_GET_ONE_REG, &one_reg);
+		fval = GET_ID_FIELD(reg_val, shift, is_sign);
+		TEST_ASSERT(fval >= min, "%s field of %s is too small (%ld)",
+			    finfo->name, sreg->name, fval);
+		sreg->current_value = reg_val;
+
+		pr_debug("%s: Test with %s enabled (%s: 0x%lx)\n", __func__,
+			 finfo->name, sreg->name, sreg->current_value);
+
+		/* Make sure that the guest sees the same ID register value. */
+		ret = TEST_RUN(vm, vcpu);
+		TEST_ASSERT(!ret, "%s:TEST_RUN failed with %s enabled",
+			    __func__, finfo->name);
+
+		test_vm_free(vm);
+	}
+
+	reset_id_reg_info();
+
+	/* Test with feature disabled */
+	vm = test_vm_create(1, guest_code_feature_check, NULL, NULL);
+	vcpu_ioctl(vm, vcpu, KVM_GET_ONE_REG, &one_reg);
+	fval = GET_ID_FIELD(reg_val, shift, is_sign);
+	if (finfo->vcpu_init || finfo->opt_in_cap) {
+		/*
+		 * If the feature needs to be enabled with KVM_ARM_VCPU_INIT
+		 * or opt-in capabilities, the default value of the ID register
+		 * shouldn't indicate the feature.
+		 */
+		TEST_ASSERT(fval < min, "%s field of %s is too big (%ld)",
+		    finfo->name, sreg->name, fval);
+	} else {
+		/* Update the relevant field to hide the feature. */
+		fval = is_sign ? 0xf : 0x0;
+		reg_val = UPDATE_ID_UFIELD(reg_val, shift, fval);
+		ret = _vcpu_ioctl(vm, vcpu, KVM_SET_ONE_REG, &one_reg);
+		TEST_ASSERT(ret == 0, "Disabling %s failed %d\n",
+			    finfo->name, ret);
+		sreg->current_value = reg_val;
+	}
+
+	pr_debug("%s: Test with %s disabled (%s: 0x%lx)\n",
+		 __func__, finfo->name, sreg->name, sreg->current_value);
+
+	/* Make sure that the guest sees the same ID register value. */
+	ret = TEST_RUN(vm, vcpu);
+	finfo->run_test = 0;
+	test_vm_free(vm);
+}
+
+/*
+ * For each opt-in feature in feature_test_info_table[],
+ * test if KVM_GET_ONE_REG/KVM_SET_ONE_REG works appropriately according
+ * to the feature configuration.  See test_feature's comment for more detail.
+ */
+static void test_feature_all(void)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(feature_test_info_table); i++)
+		test_feature(&feature_test_info_table[i]);
+}
+
+int set_id_reg(struct kvm_vm *vm, uint32_t vcpu, struct id_reg_test_info *sreg,
+	       uint64_t new_val)
+{
+	int ret;
+	uint64_t reg_val;
+	struct kvm_one_reg one_reg;
+
+	one_reg.id = KVM_ARM64_SYS_REG(sreg->id);
+	one_reg.addr = (uint64_t)&reg_val;
+
+	reg_val = new_val;
+	ret = _vcpu_ioctl(vm, vcpu, KVM_SET_ONE_REG, &one_reg);
+	if (!ret)
+		sreg->current_value = new_val;
+
+	return ret;
+}
+
+
+/*
+ * Create a new VM with one vCPU, set the ID register to @new_val.
+ */
+int set_id_reg_vm(struct id_reg_test_info *sreg, uint64_t new_val)
+{
+	struct kvm_vm *vm;
+	int ret;
+	uint32_t vcpu = 0;
+
+	reset_id_reg_info();
+
+	vm = test_vm_create(1, guest_code_id_reg_check_all, NULL, NULL);
+	ret = set_id_reg(vm, vcpu, sreg, new_val);
+	test_vm_free(vm);
+
+	return ret;
+}
+
+struct frac_info {
+	char	*name;
+	struct id_reg_test_info *sreg;
+	struct id_reg_test_info *frac_sreg;
+	int	shift;
+	int	frac_shift;
+};
+
+struct frac_info frac_info_table[] = {
+	{
+		.name = "RAS",
+		.sreg = ID_REG_INFO(ID_AA64PFR0),
+		.shift = ID_AA64PFR0_RAS_SHIFT,
+		.frac_sreg = ID_REG_INFO(ID_AA64PFR1),
+		.frac_shift = ID_AA64PFR1_RASFRAC_SHIFT,
+	},
+	{
+		.name = "MPAM",
+		.sreg = ID_REG_INFO(ID_AA64PFR0),
+		.shift = ID_AA64PFR0_MPAM_SHIFT,
+		.frac_sreg = ID_REG_INFO(ID_AA64PFR1),
+		.frac_shift = ID_AA64PFR1_MPAMFRAC_SHIFT,
+	},
+	{
+		.name = "CSV2",
+		.sreg = ID_REG_INFO(ID_AA64PFR0),
+		.shift = ID_AA64PFR0_CSV2_SHIFT,
+		.frac_sreg = ID_REG_INFO(ID_AA64PFR1),
+		.frac_shift = ID_AA64PFR1_CSV2FRAC_SHIFT,
+	},
+};
+
+
+/*
+ * Make sure that we can set the fractional reg field even before setting
+ * the feature reg field.
+ */
+int test_feature_frac_vm(struct frac_info *frac, uint64_t new_val,
+			 uint64_t frac_new_val)
+{
+	struct kvm_vm *vm;
+	uint32_t vcpu = 0;
+	struct id_reg_test_info *sreg, *frac_sreg;
+	int ret;
+
+	sreg = frac->sreg;
+	frac_sreg = frac->frac_sreg;
+	reset_id_reg_info();
+
+	reset_id_reg_info();
+
+	vm = test_vm_create(1, guest_code_id_reg_check_all, NULL, NULL);
+
+	/* Set fractional reg field */
+	ret = set_id_reg(vm, vcpu, frac_sreg, frac_new_val);
+	TEST_ASSERT(!ret, "SET_REG(%s=0x%lx) failed, ret=0x%x",
+		    frac_sreg->name, frac_new_val, ret);
+
+	/* Set feature reg field */
+	ret = set_id_reg(vm, vcpu, sreg, new_val);
+	TEST_ASSERT(!ret, "SET_REG(%s=0x%lx) failed, ret=0x%x",
+		    sreg->name, new_val, ret);
+
+	ret = TEST_RUN(vm, vcpu);
+	test_vm_free(vm);
+
+	return ret;
+}
+
+/*
+ * Test for setting the feature fractional field of the ID register.
+ * When the (main) feature field of the ID register is the same as the host's,
+ * the fractional field value cannot be larger than the host's.
+ * (KVM_SET_ONE_REG should work but KVM_RUN with the larger value will fail)
+ * When the (main) feature field of the ID register is smaler than the host's,
+ * the fractional field can be any values.
+ * The function tests those behaviors.
+ */
+void test_feature_frac_one(struct frac_info *frac)
+{
+	uint64_t ftr_val, ftr_fval, frac_val, frac_fval;
+	int ret, shift, frac_shift;
+	struct id_reg_test_info *sreg, *frac_sreg;
+
+	reset_id_reg_info();
+
+	sreg = frac->sreg;
+	shift = frac->shift;
+	frac_sreg = frac->frac_sreg;
+	frac_shift = frac->frac_shift;
+
+	pr_debug("%s(%s Frac) reg:%s(shift:%d) frac reg:%s(shift:%d)\n",
+		 __func__, frac->name, sreg->name, shift, frac_sreg->name,
+		 frac_shift);
+
+	/*
+	 * Use the host's feature value for the guest.
+	 * KVM_RUN with a larger frac value than the host's should fail.
+	 * Otherwise, it should work.
+	 */
+
+	frac_fval = GET_ID_UFIELD(frac_sreg->initial_value, frac_shift);
+	if (frac_fval > 0) {
+		/* Test with smaller frac value */
+		frac_val = UPDATE_ID_UFIELD(frac_sreg->initial_value,
+					    frac_shift, frac_fval - 1);
+		ret = test_feature_frac_vm(frac, sreg->initial_value, frac_val);
+		TEST_ASSERT(!ret, "Test smaller %s frac (val:%lx) failed(%d)",
+			    frac->name, frac_val, ret);
+	}
+
+	reset_id_reg_info();
+
+	if (frac_fval != 0xf) {
+		/* Test with larger frac value */
+		frac_val = UPDATE_ID_UFIELD(frac_sreg->initial_value,
+						frac_shift, frac_fval + 1);
+
+		/* Setting larger frac shouldn't fail at ioctl */
+		ret = set_id_reg_vm(frac_sreg, frac_val);
+		TEST_ASSERT(!ret,
+			"SET larger %s frac (%s org:%lx, val:%lx) failed(%d)",
+			frac->name, frac_sreg->name, frac_sreg->initial_value,
+			frac_val, ret);
+
+		/* KVM_RUN with larger frac should fail */
+		ret = test_feature_frac_vm(frac, sreg->initial_value, frac_val);
+		TEST_ASSERT(ret,
+			"Test with larger %s frac (%s org:%lx, val:%lx) worked",
+			frac->name, frac_sreg->name, frac_sreg->initial_value,
+			frac_val);
+	}
+
+	reset_id_reg_info();
+
+	/*
+	 * Test with a smaller (main) feature value than the host's.
+	 */
+	ftr_fval = GET_ID_UFIELD(sreg->initial_value, shift);
+	if (ftr_fval == 0)
+		/* Cannot set it to the smaller value */
+		return;
+
+	ftr_val = UPDATE_ID_UFIELD(sreg->initial_value, shift, ftr_fval - 1);
+	ret = test_feature_frac_vm(frac, ftr_val, frac_sreg->initial_value);
+	TEST_ASSERT(!ret, "Test with smaller %s (val:%lx) failed(%d)",
+		    frac->name, ftr_val, ret);
+
+	if (frac_fval > 0) {
+		/* Test with smaller frac value */
+		frac_val = UPDATE_ID_UFIELD(frac_sreg->initial_value,
+					    frac_shift, frac_fval - 1);
+		ret = test_feature_frac_vm(frac, ftr_val, frac_val);
+		TEST_ASSERT(!ret,
+		    "Test with smaller %s and frac (val:%lx) failed(%d)",
+		    frac->name, ftr_val, ret);
+	}
+
+	if (frac_fval != 0xf) {
+		/* Test with larger frac value */
+		frac_val = UPDATE_ID_UFIELD(frac_sreg->initial_value,
+					    frac_shift, frac_fval + 1);
+		ret = test_feature_frac_vm(frac, ftr_val, frac_val);
+		TEST_ASSERT(!ret,
+		    "Test with smaller %s and larger frac (val:%lx) failed(%d)",
+		    frac->name, ftr_val, ret);
+	}
+}
+
+/*
+ * Test for setting feature fractional fields of ID registers.
+ * See test_feature_frac_one's comments for more detail.
+ */
+void test_feature_frac_all(void)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(frac_info_table); i++)
+		test_feature_frac_one(&frac_info_table[i]);
+}
+
+void run_test(void)
+{
+	test_id_regs_basic();
+	test_feature_all();
+	test_feature_ptrauth();
+	test_feature_frac_all();
+}
+
+static void init_id_reg_info_one(struct id_reg_test_info *sreg, void *arg)
+{
+	struct kvm_one_reg one_reg;
+	uint64_t reg_val;
+	struct kvm_vm *vm = ((struct vm_vcpu_arg *)arg)->vm;
+	uint32_t vcpuid = ((struct vm_vcpu_arg *)arg)->vcpuid;
+	int ret;
+
+	one_reg.addr = (uint64_t)&reg_val;
+	one_reg.id = KVM_ARM64_SYS_REG(sreg->id);
+	vcpu_ioctl(vm, vcpuid, KVM_GET_ONE_REG, &one_reg);
+	sreg->current_value = reg_val;
+
+	/* Keep the initial value to reset the register value later */
+	sreg->initial_value = reg_val;
+
+	/* Check if the register can be set to 0 */
+	reg_val = 0;
+	ret = _vcpu_ioctl(vm, vcpuid, KVM_SET_ONE_REG, &one_reg);
+	if (!ret)
+		sreg->can_clear = true;
+
+	pr_debug("%s (0x%x): 0x%lx%s\n", sreg->name, sreg->id,
+		 sreg->initial_value, sreg->can_clear ? ", can clear" : "");
+}
+
+/*
+ * Check if aarch32 is supported, and initialize id_reg_test_info for all
+ * the ID registers.  Loop over the idreg list and populates each id_reg
+ * info with the initial value, current value, and can_clear value.
+ */
+static void init_test_info(void)
+{
+	uint64_t reg_val;
+	int fval;
+	struct kvm_vm *vm;
+	struct kvm_one_reg one_reg;
+	struct vm_vcpu_arg arg = { .vcpuid = 0 };
+
+	vm = test_vm_create(1, guest_code_do_nothing, NULL, NULL);
+
+	/* Get ID_AA64PFR0_EL1 to check if AArch32 is supported */
+	one_reg.addr = (uint64_t)&reg_val;
+	one_reg.id = KVM_ARM64_SYS_REG(SYS_ID_AA64PFR0_EL1);
+	vcpu_ioctl(vm, 0, KVM_GET_ONE_REG, &one_reg);
+	fval = GET_ID_UFIELD(reg_val, ID_AA64PFR0_EL0_SHIFT);
+	if (fval == 0x1)
+		/* No AArch32 support */
+		aarch32_support = false;
+
+	/* Initialize id_reg_test_info */
+	arg.vm = vm;
+	walk_id_reg_list(init_id_reg_info_one, &arg);
+	test_vm_free(vm);
+}
+
+int main(void)
+{
+
+	setbuf(stdout, NULL);
+
+	if (kvm_check_cap(KVM_CAP_ARM_ID_REG_CONFIGURABLE) <= 0) {
+		print_skip("KVM_CAP_ARM_ID_REG_CONFIGURABLE is not supported\n");
+		exit(KSFT_SKIP);
+	}
+
+	init_test_info();
+	run_test();
+	return 0;
+}
-- 
2.34.1.448.ga2b2bfdf31-goog


^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 26/26] KVM: arm64: selftests: Introduce id_reg_test
@ 2022-01-06  4:27   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:27 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, Will Deacon, Peter Shier, Paolo Bonzini, linux-arm-kernel

Introduce a test for aarch64 to validate basic behavior of
KVM_GET_ONE_REG and KVM_SET_ONE_REG for ID registers.

This test runs only when KVM_CAP_ARM_ID_REG_CONFIGURABLE is supported.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 tools/arch/arm64/include/asm/sysreg.h         |    1 +
 tools/testing/selftests/kvm/.gitignore        |    1 +
 tools/testing/selftests/kvm/Makefile          |    1 +
 .../selftests/kvm/aarch64/id_reg_test.c       | 1239 +++++++++++++++++
 4 files changed, 1242 insertions(+)
 create mode 100644 tools/testing/selftests/kvm/aarch64/id_reg_test.c

diff --git a/tools/arch/arm64/include/asm/sysreg.h b/tools/arch/arm64/include/asm/sysreg.h
index 7640fa27be94..be3947c125f1 100644
--- a/tools/arch/arm64/include/asm/sysreg.h
+++ b/tools/arch/arm64/include/asm/sysreg.h
@@ -793,6 +793,7 @@
 #define ID_AA64PFR0_ELx_32BIT_64BIT	0x2
 
 /* id_aa64pfr1 */
+#define ID_AA64PFR1_CSV2FRAC_SHIFT	32
 #define ID_AA64PFR1_MPAMFRAC_SHIFT	16
 #define ID_AA64PFR1_RASFRAC_SHIFT	12
 #define ID_AA64PFR1_MTE_SHIFT		8
diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore
index 3cb5ac5da087..e0a5f304375d 100644
--- a/tools/testing/selftests/kvm/.gitignore
+++ b/tools/testing/selftests/kvm/.gitignore
@@ -2,6 +2,7 @@
 /aarch64/arch_timer
 /aarch64/debug-exceptions
 /aarch64/get-reg-list
+/aarch64/id_reg_test
 /aarch64/psci_cpu_on_test
 /aarch64/vgic_init
 /s390x/memop
diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
index 17342b575e85..0162a7d98889 100644
--- a/tools/testing/selftests/kvm/Makefile
+++ b/tools/testing/selftests/kvm/Makefile
@@ -94,6 +94,7 @@ TEST_GEN_PROGS_x86_64 += system_counter_offset_test
 TEST_GEN_PROGS_aarch64 += aarch64/arch_timer
 TEST_GEN_PROGS_aarch64 += aarch64/debug-exceptions
 TEST_GEN_PROGS_aarch64 += aarch64/get-reg-list
+TEST_GEN_PROGS_aarch64 += aarch64/id_reg_test
 TEST_GEN_PROGS_aarch64 += aarch64/psci_cpu_on_test
 TEST_GEN_PROGS_aarch64 += aarch64/vgic_init
 TEST_GEN_PROGS_aarch64 += demand_paging_test
diff --git a/tools/testing/selftests/kvm/aarch64/id_reg_test.c b/tools/testing/selftests/kvm/aarch64/id_reg_test.c
new file mode 100644
index 000000000000..e2cac415ad57
--- /dev/null
+++ b/tools/testing/selftests/kvm/aarch64/id_reg_test.c
@@ -0,0 +1,1239 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#define _GNU_SOURCE
+
+#include <stdlib.h>
+#include <time.h>
+#include <pthread.h>
+#include <linux/kvm.h>
+#include <linux/sizes.h>
+
+#include "kvm_util.h"
+#include "processor.h"
+#include "vgic.h"
+
+/*
+ * id_reg_test.c - Tests reading/writing the aarch64's ID registers
+ *
+ * The test validates KVM_SET_ONE_REG/KVM_GET_ONE_REG ioctl for ID
+ * registers as well as reading ID register from the guest works fine.
+ */
+
+/* Reserved ID registers */
+#define	SYS_ID_REG_3_3_EL1		sys_reg(3, 0, 0, 3, 3)
+#define	SYS_ID_REG_3_7_EL1		sys_reg(3, 0, 0, 3, 7)
+
+#define	SYS_ID_REG_4_2_EL1		sys_reg(3, 0, 0, 4, 2)
+#define	SYS_ID_REG_4_3_EL1		sys_reg(3, 0, 0, 4, 3)
+#define	SYS_ID_REG_4_5_EL1		sys_reg(3, 0, 0, 4, 5)
+#define	SYS_ID_REG_4_6_EL1		sys_reg(3, 0, 0, 4, 6)
+#define	SYS_ID_REG_4_7_EL1		sys_reg(3, 0, 0, 4, 7)
+
+#define	SYS_ID_REG_5_2_EL1		sys_reg(3, 0, 0, 5, 2)
+#define	SYS_ID_REG_5_3_EL1		sys_reg(3, 0, 0, 5, 3)
+#define	SYS_ID_REG_5_6_EL1		sys_reg(3, 0, 0, 5, 6)
+#define	SYS_ID_REG_5_7_EL1		sys_reg(3, 0, 0, 5, 7)
+
+#define	SYS_ID_REG_6_2_EL1		sys_reg(3, 0, 0, 6, 2)
+#define	SYS_ID_REG_6_3_EL1		sys_reg(3, 0, 0, 6, 3)
+#define	SYS_ID_REG_6_4_EL1		sys_reg(3, 0, 0, 6, 4)
+#define	SYS_ID_REG_6_5_EL1		sys_reg(3, 0, 0, 6, 5)
+#define	SYS_ID_REG_6_6_EL1		sys_reg(3, 0, 0, 6, 6)
+#define	SYS_ID_REG_6_7_EL1		sys_reg(3, 0, 0, 6, 7)
+
+#define	SYS_ID_REG_7_3_EL1		sys_reg(3, 0, 0, 7, 3)
+#define	SYS_ID_REG_7_4_EL1		sys_reg(3, 0, 0, 7, 4)
+#define	SYS_ID_REG_7_5_EL1		sys_reg(3, 0, 0, 7, 5)
+#define	SYS_ID_REG_7_6_EL1		sys_reg(3, 0, 0, 7, 6)
+#define	SYS_ID_REG_7_7_EL1		sys_reg(3, 0, 0, 7, 7)
+
+#define	READ_ID_REG_FN(name)	read_## name ## _EL1
+
+#define	DEFINE_READ_SYS_REG(reg_name)			\
+uint64_t read_##reg_name(void)				\
+{							\
+	return read_sysreg_s(SYS_##reg_name);		\
+}
+
+#define DEFINE_READ_ID_REG(name)	\
+	DEFINE_READ_SYS_REG(name ## _EL1)
+
+#define	__ID_REG(reg_name)		\
+	.name = #reg_name,		\
+	.id = SYS_## reg_name ##_EL1,	\
+	.read_reg = READ_ID_REG_FN(reg_name),
+
+#define	ID_REG_ENT(reg_name)	\
+	[ID_IDX(reg_name)] = { __ID_REG(reg_name) }
+
+/* Functions to read each ID register */
+/* CRm=1 */
+DEFINE_READ_ID_REG(ID_PFR0)
+DEFINE_READ_ID_REG(ID_PFR1)
+DEFINE_READ_ID_REG(ID_DFR0)
+DEFINE_READ_ID_REG(ID_AFR0)
+DEFINE_READ_ID_REG(ID_MMFR0)
+DEFINE_READ_ID_REG(ID_MMFR1)
+DEFINE_READ_ID_REG(ID_MMFR2)
+DEFINE_READ_ID_REG(ID_MMFR3)
+
+/* CRm=2 */
+DEFINE_READ_ID_REG(ID_ISAR0)
+DEFINE_READ_ID_REG(ID_ISAR1)
+DEFINE_READ_ID_REG(ID_ISAR2)
+DEFINE_READ_ID_REG(ID_ISAR3)
+DEFINE_READ_ID_REG(ID_ISAR4)
+DEFINE_READ_ID_REG(ID_ISAR5)
+DEFINE_READ_ID_REG(ID_MMFR4)
+DEFINE_READ_ID_REG(ID_ISAR6)
+
+/* CRm=3 */
+DEFINE_READ_ID_REG(MVFR0)
+DEFINE_READ_ID_REG(MVFR1)
+DEFINE_READ_ID_REG(MVFR2)
+DEFINE_READ_ID_REG(ID_REG_3_3)
+DEFINE_READ_ID_REG(ID_PFR2)
+DEFINE_READ_ID_REG(ID_DFR1)
+DEFINE_READ_ID_REG(ID_MMFR5)
+DEFINE_READ_ID_REG(ID_REG_3_7)
+
+/* CRm=4 */
+DEFINE_READ_ID_REG(ID_AA64PFR0)
+DEFINE_READ_ID_REG(ID_AA64PFR1)
+DEFINE_READ_ID_REG(ID_REG_4_2)
+DEFINE_READ_ID_REG(ID_REG_4_3)
+DEFINE_READ_ID_REG(ID_AA64ZFR0)
+DEFINE_READ_ID_REG(ID_REG_4_5)
+DEFINE_READ_ID_REG(ID_REG_4_6)
+DEFINE_READ_ID_REG(ID_REG_4_7)
+
+/* CRm=5 */
+DEFINE_READ_ID_REG(ID_AA64DFR0)
+DEFINE_READ_ID_REG(ID_AA64DFR1)
+DEFINE_READ_ID_REG(ID_REG_5_2)
+DEFINE_READ_ID_REG(ID_REG_5_3)
+DEFINE_READ_ID_REG(ID_AA64AFR0)
+DEFINE_READ_ID_REG(ID_AA64AFR1)
+DEFINE_READ_ID_REG(ID_REG_5_6)
+DEFINE_READ_ID_REG(ID_REG_5_7)
+
+/* CRm=6 */
+DEFINE_READ_ID_REG(ID_AA64ISAR0)
+DEFINE_READ_ID_REG(ID_AA64ISAR1)
+DEFINE_READ_ID_REG(ID_REG_6_2)
+DEFINE_READ_ID_REG(ID_REG_6_3)
+DEFINE_READ_ID_REG(ID_REG_6_4)
+DEFINE_READ_ID_REG(ID_REG_6_5)
+DEFINE_READ_ID_REG(ID_REG_6_6)
+DEFINE_READ_ID_REG(ID_REG_6_7)
+
+/* CRm=7 */
+DEFINE_READ_ID_REG(ID_AA64MMFR0)
+DEFINE_READ_ID_REG(ID_AA64MMFR1)
+DEFINE_READ_ID_REG(ID_AA64MMFR2)
+DEFINE_READ_ID_REG(ID_REG_7_3)
+DEFINE_READ_ID_REG(ID_REG_7_4)
+DEFINE_READ_ID_REG(ID_REG_7_5)
+DEFINE_READ_ID_REG(ID_REG_7_6)
+DEFINE_READ_ID_REG(ID_REG_7_7)
+
+#define	ID_IDX(name)	REG_IDX_## name
+
+enum id_reg_idx {
+	/* CRm=1 */
+	ID_IDX(ID_PFR0) = 0,
+	ID_IDX(ID_PFR1),
+	ID_IDX(ID_DFR0),
+	ID_IDX(ID_AFR0),
+	ID_IDX(ID_MMFR0),
+	ID_IDX(ID_MMFR1),
+	ID_IDX(ID_MMFR2),
+	ID_IDX(ID_MMFR3),
+
+	/* CRm=2 */
+	ID_IDX(ID_ISAR0),
+	ID_IDX(ID_ISAR1),
+	ID_IDX(ID_ISAR2),
+	ID_IDX(ID_ISAR3),
+	ID_IDX(ID_ISAR4),
+	ID_IDX(ID_ISAR5),
+	ID_IDX(ID_MMFR4),
+	ID_IDX(ID_ISAR6),
+
+	/* CRm=3 */
+	ID_IDX(MVFR0),
+	ID_IDX(MVFR1),
+	ID_IDX(MVFR2),
+	ID_IDX(ID_REG_3_3),
+	ID_IDX(ID_PFR2),
+	ID_IDX(ID_DFR1),
+	ID_IDX(ID_MMFR5),
+	ID_IDX(ID_REG_3_7),
+
+	/* CRm=4 */
+	ID_IDX(ID_AA64PFR0),
+	ID_IDX(ID_AA64PFR1),
+	ID_IDX(ID_REG_4_2),
+	ID_IDX(ID_REG_4_3),
+	ID_IDX(ID_AA64ZFR0),
+	ID_IDX(ID_REG_4_5),
+	ID_IDX(ID_REG_4_6),
+	ID_IDX(ID_REG_4_7),
+
+	/* CRm=5 */
+	ID_IDX(ID_AA64DFR0),
+	ID_IDX(ID_AA64DFR1),
+	ID_IDX(ID_REG_5_2),
+	ID_IDX(ID_REG_5_3),
+	ID_IDX(ID_AA64AFR0),
+	ID_IDX(ID_AA64AFR1),
+	ID_IDX(ID_REG_5_6),
+	ID_IDX(ID_REG_5_7),
+
+	/* CRm=6 */
+	ID_IDX(ID_AA64ISAR0),
+	ID_IDX(ID_AA64ISAR1),
+	ID_IDX(ID_REG_6_2),
+	ID_IDX(ID_REG_6_3),
+	ID_IDX(ID_REG_6_4),
+	ID_IDX(ID_REG_6_5),
+	ID_IDX(ID_REG_6_6),
+	ID_IDX(ID_REG_6_7),
+
+	/* CRm=7 */
+	ID_IDX(ID_AA64MMFR0),
+	ID_IDX(ID_AA64MMFR1),
+	ID_IDX(ID_AA64MMFR2),
+	ID_IDX(ID_REG_7_3),
+	ID_IDX(ID_REG_7_4),
+	ID_IDX(ID_REG_7_5),
+	ID_IDX(ID_REG_7_6),
+	ID_IDX(ID_REG_7_7),
+};
+
+struct id_reg_test_info {
+	char		*name;
+	uint32_t	id;
+	/* Indicates the register can be set to 0 */
+	bool		can_clear;
+	uint64_t	initial_value;
+	uint64_t	current_value;
+	uint64_t	(*read_reg)(void);
+};
+
+#define	ID_REG_INFO(name)	(&id_reg_list[ID_IDX(name)])
+static struct id_reg_test_info id_reg_list[] = {
+	/* CRm=1 */
+	ID_REG_ENT(ID_PFR0),
+	ID_REG_ENT(ID_PFR1),
+	ID_REG_ENT(ID_DFR0),
+	ID_REG_ENT(ID_AFR0),
+	ID_REG_ENT(ID_MMFR0),
+	ID_REG_ENT(ID_MMFR1),
+	ID_REG_ENT(ID_MMFR2),
+	ID_REG_ENT(ID_MMFR3),
+
+	/* CRm=2 */
+	ID_REG_ENT(ID_ISAR0),
+	ID_REG_ENT(ID_ISAR1),
+	ID_REG_ENT(ID_ISAR2),
+	ID_REG_ENT(ID_ISAR3),
+	ID_REG_ENT(ID_ISAR4),
+	ID_REG_ENT(ID_ISAR5),
+	ID_REG_ENT(ID_MMFR4),
+	ID_REG_ENT(ID_ISAR6),
+
+	/* CRm=3 */
+	ID_REG_ENT(MVFR0),
+	ID_REG_ENT(MVFR1),
+	ID_REG_ENT(MVFR2),
+	ID_REG_ENT(ID_REG_3_3),
+	ID_REG_ENT(ID_PFR2),
+	ID_REG_ENT(ID_DFR1),
+	ID_REG_ENT(ID_MMFR5),
+	ID_REG_ENT(ID_REG_3_7),
+
+	/* CRm=4 */
+	ID_REG_ENT(ID_AA64PFR0),
+	ID_REG_ENT(ID_AA64PFR1),
+	ID_REG_ENT(ID_REG_4_2),
+	ID_REG_ENT(ID_REG_4_3),
+	ID_REG_ENT(ID_AA64ZFR0),
+	ID_REG_ENT(ID_REG_4_5),
+	ID_REG_ENT(ID_REG_4_6),
+	ID_REG_ENT(ID_REG_4_7),
+
+	/* CRm=5 */
+	ID_REG_ENT(ID_AA64DFR0),
+	ID_REG_ENT(ID_AA64DFR1),
+	ID_REG_ENT(ID_REG_5_2),
+	ID_REG_ENT(ID_REG_5_3),
+	ID_REG_ENT(ID_AA64AFR0),
+	ID_REG_ENT(ID_AA64AFR1),
+	ID_REG_ENT(ID_REG_5_6),
+	ID_REG_ENT(ID_REG_5_7),
+
+	/* CRm=6 */
+	ID_REG_ENT(ID_AA64ISAR0),
+	ID_REG_ENT(ID_AA64ISAR1),
+	ID_REG_ENT(ID_REG_6_2),
+	ID_REG_ENT(ID_REG_6_3),
+	ID_REG_ENT(ID_REG_6_4),
+	ID_REG_ENT(ID_REG_6_5),
+	ID_REG_ENT(ID_REG_6_6),
+	ID_REG_ENT(ID_REG_6_7),
+
+	/* CRm=7 */
+	ID_REG_ENT(ID_AA64MMFR0),
+	ID_REG_ENT(ID_AA64MMFR1),
+	ID_REG_ENT(ID_AA64MMFR2),
+	ID_REG_ENT(ID_REG_7_3),
+	ID_REG_ENT(ID_REG_7_4),
+	ID_REG_ENT(ID_REG_7_5),
+	ID_REG_ENT(ID_REG_7_6),
+	ID_REG_ENT(ID_REG_7_7),
+};
+
+static bool aarch32_support = true;
+
+/* Utilities to get a feature field from ID register value */
+static inline int
+cpuid_signed_field_width(uint64_t id_val, int field, int width)
+{
+	return (s64)(id_val << (64 - width - field)) >> (64 - width);
+}
+
+static unsigned int
+cpuid_unsigned_field_width(uint64_t id_val, int field, int width)
+{
+	return (uint64_t)(id_val << (64 - width - field)) >> (64 - width);
+}
+
+static inline int __attribute_const__
+cpuid_extract_field_width(uint64_t id_val, int field, int width, bool sign)
+{
+	return (sign) ? cpuid_signed_field_width(id_val, field, width) :
+			cpuid_unsigned_field_width(id_val, field, width);
+}
+
+#define is_id_reg(id)	\
+	(sys_reg_Op0(id) == 3 && sys_reg_Op1(id) == 0 &&	\
+	 sys_reg_CRn(id) == 0 && sys_reg_CRm(id) >= 0 &&	\
+	 sys_reg_CRm(id) < 8)
+
+#define	GET_ID_FIELD(regval, shift, is_signed)	\
+	cpuid_extract_field_width(regval, shift, 4, is_signed)
+
+#define	GET_ID_UFIELD(regval, shift)	\
+	cpuid_unsigned_field_width(regval, shift, 4)
+
+#define	UPDATE_ID_UFIELD(regval, shift, fval)	\
+	(((regval) & ~(0xfULL << (shift))) |	\
+	 (((uint64_t)((fval) & 0xf)) << (shift)))
+
+void pmu_init(struct kvm_vm *vm, uint32_t vcpu)
+{
+	struct kvm_device_attr attr = {
+		.group = KVM_ARM_VCPU_PMU_V3_CTRL,
+		.attr = KVM_ARM_VCPU_PMU_V3_INIT,
+	};
+	vcpu_ioctl(vm, vcpu, KVM_SET_DEVICE_ATTR, &attr);
+}
+
+void sve_init(struct kvm_vm *vm, uint32_t vcpu)
+{
+	int feature = KVM_ARM_VCPU_SVE;
+
+	vcpu_ioctl(vm, vcpu, KVM_ARM_VCPU_FINALIZE, &feature);
+}
+
+#define GICD_BASE_GPA			0x8000000ULL
+#define GICR_BASE_GPA			0x80A0000ULL
+
+void test_vgic_init(struct kvm_vm *vm, uint32_t vcpu)
+{
+	/* We jsut need to configure gic v3 (we don't use it though) */
+	vgic_v3_setup(vm, 1, GICD_BASE_GPA, GICR_BASE_GPA);
+}
+
+static bool is_aarch32_id_reg(uint32_t id)
+{
+	uint32_t crm, op2;
+
+	if (!is_id_reg(id))
+		return false;
+
+	crm = sys_reg_CRm(id);
+	op2 = sys_reg_Op2(id);
+	if (crm == 1 || crm == 2 || (crm == 3 && (op2 != 3 && op2 != 7)))
+		/* AArch32 ID register */
+		return true;
+
+	return false;
+}
+
+#define	MAX_CAPS	2
+struct feature_test_info {
+	char	*name;	/* Feature Name (Debug information) */
+
+	/* ID register that identifies the presence of the feature */
+	struct id_reg_test_info	*sreg;
+
+	/*
+	 * Bit position of the ID register field that identifies
+	 * the presence of the feature.
+	 */
+	int	shift;
+
+	/* Min value of the field that indicates the presence of the feature. */
+	int	min;
+	bool	is_sign;	/* Is the field signed or unsigned ? */
+	int	ncaps;		/* Number of valid Capabilities in caps[] */
+
+	/* KVM_CAP_* Capabilities to indicates that KVM supports this feature */
+	long	caps[MAX_CAPS];
+
+	/* struct kvm_enable_cap to use the capability if needed */
+	struct kvm_enable_cap	*opt_in_cap;
+
+	/* Should the guest check the ID register for this feature ? */
+	bool	run_test;
+
+	/*
+	 * Extra initialization function to enable the feature if needed.
+	 * (e.g. KVM_ARM_VCPU_FINALIZE for SVE)
+	 */
+	void	(*init_feature)(struct kvm_vm *vm, uint32_t vcpuid);
+
+	/* struct kvm_vcpu_init to opt-in the feature if needed */
+	struct kvm_vcpu_init	*vcpu_init;
+};
+
+/* Information for opt-in CPU features */
+static struct feature_test_info feature_test_info_table[] = {
+	{
+		.name = "SVE",
+		.sreg = ID_REG_INFO(ID_AA64PFR0),
+		.shift = ID_AA64PFR0_SVE_SHIFT,
+		.min = 1,
+		.caps = {KVM_CAP_ARM_SVE},
+		.ncaps = 1,
+		.init_feature = sve_init,
+		.vcpu_init = &(struct kvm_vcpu_init) {
+			.features = {1ULL << KVM_ARM_VCPU_SVE},
+		},
+	},
+	{
+		.name = "GIC",
+		.sreg = ID_REG_INFO(ID_AA64PFR0),
+		.shift = ID_AA64PFR0_GIC_SHIFT,
+		.min = 1,
+		.caps = {KVM_CAP_IRQCHIP},
+		.ncaps = 1,
+		.init_feature = test_vgic_init,
+	},
+	{
+		.name = "MTE",
+		.sreg = ID_REG_INFO(ID_AA64PFR1),
+		.shift = ID_AA64PFR1_MTE_SHIFT,
+		.min = 2,
+		.caps = {KVM_CAP_ARM_MTE},
+		.ncaps = 1,
+		.opt_in_cap = &(struct kvm_enable_cap) {
+				.cap = KVM_CAP_ARM_MTE,
+		},
+	},
+	{
+		.name = "PMUV3",
+		.sreg = ID_REG_INFO(ID_AA64DFR0),
+		.shift = ID_AA64DFR0_PMUVER_SHIFT,
+		.min = 1,
+		.init_feature = pmu_init,
+		.caps = {KVM_CAP_ARM_PMU_V3},
+		.ncaps = 1,
+		.vcpu_init = &(struct kvm_vcpu_init) {
+			.features = {1ULL << KVM_ARM_VCPU_PMU_V3},
+		},
+	},
+	{
+		.name = "PERFMON",
+		.sreg = ID_REG_INFO(ID_DFR0),
+		.shift = ID_DFR0_PERFMON_SHIFT,
+		.min = 3,
+		.init_feature = pmu_init,
+		.caps = {KVM_CAP_ARM_PMU_V3},
+		.ncaps = 1,
+		.vcpu_init = &(struct kvm_vcpu_init) {
+			.features = {1ULL << KVM_ARM_VCPU_PMU_V3},
+		},
+	},
+};
+
+static void walk_id_reg_list(void (*fn)(struct id_reg_test_info *r, void *arg),
+			     void *arg)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(id_reg_list); i++)
+		fn(&id_reg_list[i], arg);
+}
+
+static void guest_code_id_reg_check_one(struct id_reg_test_info *idr, void *arg)
+{
+	uint64_t v = idr->read_reg();
+
+	GUEST_ASSERT_2(v == idr->current_value, idr->name, idr->current_value);
+}
+
+static void guest_code_id_reg_check_all(uint32_t cpu)
+{
+	walk_id_reg_list(guest_code_id_reg_check_one, NULL);
+	GUEST_DONE();
+}
+
+static void guest_code_do_nothing(uint32_t cpu)
+{
+	GUEST_DONE();
+}
+
+static void guest_code_feature_check(uint32_t cpu)
+{
+	int i;
+	struct feature_test_info *finfo;
+
+	for (i = 0; i < ARRAY_SIZE(feature_test_info_table); i++) {
+		finfo = &feature_test_info_table[i];
+		if (finfo->run_test)
+			guest_code_id_reg_check_one(finfo->sreg, NULL);
+	}
+
+	GUEST_DONE();
+}
+
+static void guest_code_ptrauth_check(uint32_t cpuid)
+{
+	struct id_reg_test_info *sreg = ID_REG_INFO(ID_AA64ISAR1);
+	uint64_t val = sreg->read_reg();
+
+	GUEST_ASSERT_2(val == sreg->current_value, "PTRAUTH", val);
+	GUEST_DONE();
+}
+
+static void reset_id_reg_info_current_value(struct id_reg_test_info *info,
+					    void *arg)
+{
+	info->current_value = info->initial_value;
+}
+
+/* Reset current_value field of each id_reg_test_info */
+static void reset_id_reg_info(void)
+{
+	walk_id_reg_list(reset_id_reg_info_current_value, NULL);
+}
+
+static struct kvm_vm *test_vm_create(uint32_t nvcpus,
+		void (*guest_code)(uint32_t), struct kvm_vcpu_init *init,
+		struct kvm_enable_cap *cap)
+{
+	struct kvm_vm *vm;
+	uint32_t cpuid;
+	uint64_t mem_pages;
+
+	mem_pages = DEFAULT_GUEST_PHY_PAGES + DEFAULT_STACK_PGS * nvcpus;
+	mem_pages += mem_pages / (PTES_PER_MIN_PAGE * 2);
+	mem_pages = vm_adjust_num_guest_pages(VM_MODE_DEFAULT, mem_pages);
+
+	vm = vm_create(VM_MODE_DEFAULT, mem_pages, O_RDWR);
+	if (cap)
+		vm_enable_cap(vm, cap);
+
+	kvm_vm_elf_load(vm, program_invocation_name);
+
+	if (init && init->target == -1) {
+		struct kvm_vcpu_init preferred;
+
+		vm_ioctl(vm, KVM_ARM_PREFERRED_TARGET, &preferred);
+		init->target = preferred.target;
+	}
+
+	vm_init_descriptor_tables(vm);
+	for (cpuid = 0; cpuid < nvcpus; cpuid++) {
+		aarch64_vcpu_add_default(vm, cpuid, init, guest_code);
+		vcpu_init_descriptor_tables(vm, cpuid);
+	}
+
+	ucall_init(vm, NULL);
+	return vm;
+}
+
+static void test_vm_free(struct kvm_vm *vm)
+{
+	ucall_uninit(vm);
+	kvm_vm_free(vm);
+}
+
+#define	TEST_RUN(vm, cpu)	\
+	(test_vcpu_run(__func__, __LINE__, vm, cpu, true))
+
+#define	TEST_RUN_NO_SYNC_DATA(vm, cpu)	\
+	(test_vcpu_run(__func__, __LINE__, vm, cpu, false))
+
+static int test_vcpu_run(const char *test_name, int line,
+			 struct kvm_vm *vm, uint32_t vcpuid, bool sync_data)
+{
+	struct ucall uc;
+	int ret;
+
+	if (sync_data) {
+		sync_global_to_guest(vm, id_reg_list);
+		sync_global_to_guest(vm, feature_test_info_table);
+	}
+
+	vcpu_args_set(vm, vcpuid, 1, vcpuid);
+
+	ret = _vcpu_run(vm, vcpuid);
+	if (ret) {
+		ret = errno;
+		goto sync_exit;
+	}
+
+	switch (get_ucall(vm, vcpuid, &uc)) {
+	case UCALL_SYNC:
+	case UCALL_DONE:
+		ret = 0;
+		break;
+	case UCALL_ABORT:
+		TEST_FAIL(
+		    "%s (%s) at line %d (user %s at line %d), args[3]=0x%lx",
+		    (char *)uc.args[0], (char *)uc.args[2], (int)uc.args[1],
+		    test_name, line, uc.args[3]);
+		break;
+	default:
+		TEST_FAIL("Unexpected guest exit\n");
+	}
+
+sync_exit:
+	if (sync_data) {
+		sync_global_from_guest(vm, id_reg_list);
+		sync_global_from_guest(vm, feature_test_info_table);
+	}
+	return ret;
+}
+
+struct vm_vcpu_arg {
+	struct kvm_vm	*vm;
+	uint32_t	vcpuid;
+	bool		after_run;
+};
+
+/*
+ * Test if KVM_SET_ONE_REG can work with the value KVM_GET_ONE_REG returns,
+ * KVM_SET_ONE_REG with zero works before KVM_RUN (and fails after KVM_RUN),
+ * and KVM_GET_ONE_REG returns the value KVM_SET_ONE_REG sets.
+ */
+static void test_get_set_id_reg(struct id_reg_test_info *sreg, void *arg)
+{
+	struct kvm_vm *vm = ((struct vm_vcpu_arg *)arg)->vm;
+	uint32_t vcpuid = ((struct vm_vcpu_arg *)arg)->vcpuid;
+	bool after_run = ((struct vm_vcpu_arg *)arg)->after_run;
+	struct kvm_one_reg one_reg;
+	uint64_t reg_val, tval;
+	int ret;
+
+	one_reg.addr = (uint64_t)&reg_val;
+	one_reg.id = KVM_ARM64_SYS_REG(sreg->id);
+
+	/* Check the current register value */
+	vcpu_ioctl(vm, vcpuid, KVM_GET_ONE_REG, &one_reg);
+	TEST_ASSERT(reg_val == sreg->current_value,
+		    "GET(%s) didn't return 0x%lx but 0x%lx",
+		    sreg->name, sreg->current_value, reg_val);
+	tval = reg_val;
+
+	/* Try to clear the register that should be able to be cleared. */
+	if ((reg_val != 0) && (sreg->can_clear)) {
+		reg_val = 0;
+		ret = _vcpu_ioctl(vm, vcpuid, KVM_SET_ONE_REG, &one_reg);
+		if (after_run) {
+			/* Expect an error after KVM_RUN */
+			TEST_ASSERT(ret,
+				    "Clearing %s unexpectedly worked\n",
+				    sreg->name);
+		} else {
+			TEST_ASSERT(!ret,
+				    "Clearing %s didn't work\n", sreg->name);
+			/*
+			 * Make sure that KVM_GET_ONE_REG provides the value
+			 * we set.
+			 */
+			vcpu_ioctl(vm, vcpuid, KVM_GET_ONE_REG, &one_reg);
+			TEST_ASSERT(reg_val == 0,
+				    "GET(%s) didn't return 0x%lx but 0x%lx",
+				    sreg->name, (uint64_t)0, reg_val);
+		}
+	}
+
+	/* Check if KVM_SET_ONE_REG works with the original value. */
+	reg_val = tval;
+	ret = _vcpu_ioctl(vm, vcpuid, KVM_SET_ONE_REG, &one_reg);
+	TEST_ASSERT(ret == 0, "Setting the same ID reg value should work\n");
+
+	/* Make sure that KVM_GET_ONE_REG provides the value we set. */
+	vcpu_ioctl(vm, vcpuid, KVM_GET_ONE_REG, &one_reg);
+	TEST_ASSERT(reg_val == tval,
+		    "GET(%s) didn't return 0x%lx but 0x%lx",
+		    sreg->name, sreg->current_value, reg_val);
+}
+
+/*
+ * Test if KVM_SET_ONE_REG with the current value works before KVM_RUN,
+ * values of ID registers the guest sees are consistent with the ones
+ * userspace sees, and KVM_SET_ONE_REG after KVM_RUN works when the
+ * specified value is the same as the current one (fails otherwise).
+ */
+static void test_id_regs_basic(void)
+{
+	struct kvm_vm *vm;
+	struct vm_vcpu_arg arg = { .vcpuid = 0 };
+	int ret;
+
+	reset_id_reg_info();
+
+	vm = test_vm_create(1, guest_code_id_reg_check_all, NULL, NULL);
+
+	arg.vm = vm;
+	walk_id_reg_list(test_get_set_id_reg, &arg);
+
+	ret = TEST_RUN(vm, 0);
+	assert(!ret);
+
+	arg.after_run = true;
+	walk_id_reg_list(test_get_set_id_reg, &arg);
+
+	test_vm_free(vm);
+}
+
+static bool caps_are_supported(long *caps, int ncaps)
+{
+	int i;
+
+	for (i = 0; i < ncaps; i++) {
+		if (kvm_check_cap(caps[i]) <= 0)
+			return false;
+	}
+	return true;
+}
+
+#define	NCAPS_PTRAUTH	2
+
+/*
+ * Test if the ID register value reflects the ptrauth feature configuration.
+ * KVM_SET_ONE_REG should work as long as the requested value is consistent
+ * with the ptrauth feature configuration.
+ */
+static void test_feature_ptrauth(void)
+{
+	struct kvm_one_reg one_reg;
+	struct kvm_vcpu_init init;
+	struct kvm_vm *vm = NULL;
+	struct id_reg_test_info *sreg = ID_REG_INFO(ID_AA64ISAR1);
+	uint32_t vcpu = 0;
+	int64_t rval;
+	int ret;
+	int apa, api, gpa, gpi;
+	char *name = "PTRAUTH";
+	long caps[NCAPS_PTRAUTH] = {KVM_CAP_ARM_PTRAUTH_ADDRESS,
+				    KVM_CAP_ARM_PTRAUTH_GENERIC};
+
+	reset_id_reg_info();
+	one_reg.addr = (uint64_t)&rval;
+	one_reg.id = KVM_ARM64_SYS_REG(sreg->id);
+
+	if (caps_are_supported(caps, NCAPS_PTRAUTH)) {
+
+		/* Test with feature enabled */
+		memset(&init, 0, sizeof(init));
+		init.target = -1;
+		init.features[0] = (1ULL << KVM_ARM_VCPU_PTRAUTH_ADDRESS |
+				    1ULL << KVM_ARM_VCPU_PTRAUTH_GENERIC);
+		vm = test_vm_create(1, guest_code_ptrauth_check, &init, NULL);
+		vcpu_ioctl(vm, vcpu, KVM_GET_ONE_REG, &one_reg);
+
+		/* Make sure values of apa/api/gpa/gpi fields are expected */
+		apa = GET_ID_UFIELD(rval, ID_AA64ISAR1_APA_SHIFT);
+		api = GET_ID_UFIELD(rval, ID_AA64ISAR1_API_SHIFT);
+		gpa = GET_ID_UFIELD(rval, ID_AA64ISAR1_GPA_SHIFT);
+		gpi = GET_ID_UFIELD(rval, ID_AA64ISAR1_GPI_SHIFT);
+
+		TEST_ASSERT((apa > 0) || (api > 0),
+			    "Either apa(0x%x) or api(0x%x) must be available",
+			    apa, gpa);
+		TEST_ASSERT((gpa > 0) || (gpi > 0),
+			    "Either gpa(0x%x) or gpi(0x%x) must be available",
+			    gpa, gpi);
+
+		TEST_ASSERT((apa > 0) ^ (api > 0),
+			    "Both apa(0x%x) and api(0x%x) must not be available",
+			    apa, api);
+		TEST_ASSERT((gpa > 0) ^ (gpi > 0),
+			    "Both gpa(0x%x) and gpi(0x%x) must not be available",
+			    gpa, gpi);
+
+		sreg->current_value = rval;
+
+		pr_debug("%s: Test with %s enabled (%s: 0x%lx)\n",
+			 __func__, name, sreg->name, sreg->current_value);
+
+		/* Make sure that the guest sees the same ID register value. */
+		ret = TEST_RUN(vm, vcpu);
+
+		TEST_ASSERT(!ret, "%s:KVM_RUN failed with %s enabled",
+			    __func__, name);
+		test_vm_free(vm);
+	}
+
+	reset_id_reg_info();
+
+	/* Test with feature disabled */
+	vm = test_vm_create(1, guest_code_feature_check, NULL, NULL);
+	vcpu_ioctl(vm, vcpu, KVM_GET_ONE_REG, &one_reg);
+
+	apa = GET_ID_UFIELD(rval, ID_AA64ISAR1_APA_SHIFT);
+	api = GET_ID_UFIELD(rval, ID_AA64ISAR1_API_SHIFT);
+	gpa = GET_ID_UFIELD(rval, ID_AA64ISAR1_GPA_SHIFT);
+	gpi = GET_ID_UFIELD(rval, ID_AA64ISAR1_GPI_SHIFT);
+	TEST_ASSERT(!apa && !api && !gpa && !gpi,
+	    "apa(0x%x), api(0x%x), gpa(0x%x), gpi(0x%x) must be zero",
+	    apa, api, gpa, gpi);
+
+	pr_debug("%s: Test with %s disabled (%s: 0x%lx)\n",
+		 __func__, name, sreg->name, sreg->current_value);
+
+	/* Make sure that the guest sees the same ID register value. */
+	ret = TEST_RUN(vm, vcpu);
+	TEST_ASSERT(!ret, "%s TEST_RUN failed with %s enabled, ret=0x%x",
+		    __func__, name, ret);
+
+	test_vm_free(vm);
+}
+
+static bool feature_caps_are_available(struct feature_test_info *finfo)
+{
+	return ((finfo->ncaps > 0) &&
+		caps_are_supported(finfo->caps, finfo->ncaps));
+}
+
+/*
+ * Test if the ID register value reflects the feature configuration.
+ * KVM_SET_ONE_REG should work as long as the requested value is
+ * consistent with the feature configuration.
+ */
+static void test_feature(struct feature_test_info *finfo)
+{
+	struct id_reg_test_info *sreg = finfo->sreg;
+	struct kvm_one_reg one_reg;
+	struct kvm_vcpu_init init, *initp = NULL;
+	struct kvm_vm *vm = NULL;
+	int64_t fval, reg_val;
+	uint32_t vcpu = 0;
+	bool is_sign = finfo->is_sign;
+	int min = finfo->min;
+	int shift = finfo->shift;
+	int ret;
+
+	pr_debug("%s: %s (reg %s)\n", __func__, finfo->name, sreg->name);
+
+	reset_id_reg_info();
+
+	if (is_aarch32_id_reg(sreg->id) && !aarch32_support)
+		/*
+		 * AArch32 is not supported. Skip testing with the AArch32
+		 * ID register.
+		 */
+		return;
+
+	/* Indicate that guest runs the test for the feature */
+	finfo->run_test = 1;
+	one_reg.addr = (uint64_t)&reg_val;
+	one_reg.id = KVM_ARM64_SYS_REG(sreg->id);
+
+	/*
+	 * Test with feature enabled if the feature is exposed in the default
+	 * ID register value or the capabilities are supported at KVM level.
+	 */
+	if ((GET_ID_FIELD(sreg->initial_value, shift, is_sign) >= min) ||
+	    feature_caps_are_available(finfo)) {
+		if (finfo->vcpu_init) {
+			/* Need to enable the feature via KVM_ARM_VCPU_INIT. */
+			memset(&init, 0, sizeof(init));
+			init = *finfo->vcpu_init;
+			init.target = -1;
+			initp = &init;
+		}
+
+		vm = test_vm_create(1, guest_code_feature_check, initp,
+				    finfo->opt_in_cap);
+		if (finfo->init_feature)
+			/* Run any required extra process to use the feature */
+			finfo->init_feature(vm, vcpu);
+
+		/* Check if the ID register value indicates the feature */
+		vcpu_ioctl(vm, vcpu, KVM_GET_ONE_REG, &one_reg);
+		fval = GET_ID_FIELD(reg_val, shift, is_sign);
+		TEST_ASSERT(fval >= min, "%s field of %s is too small (%ld)",
+			    finfo->name, sreg->name, fval);
+		sreg->current_value = reg_val;
+
+		pr_debug("%s: Test with %s enabled (%s: 0x%lx)\n", __func__,
+			 finfo->name, sreg->name, sreg->current_value);
+
+		/* Make sure that the guest sees the same ID register value. */
+		ret = TEST_RUN(vm, vcpu);
+		TEST_ASSERT(!ret, "%s:TEST_RUN failed with %s enabled",
+			    __func__, finfo->name);
+
+		test_vm_free(vm);
+	}
+
+	reset_id_reg_info();
+
+	/* Test with feature disabled */
+	vm = test_vm_create(1, guest_code_feature_check, NULL, NULL);
+	vcpu_ioctl(vm, vcpu, KVM_GET_ONE_REG, &one_reg);
+	fval = GET_ID_FIELD(reg_val, shift, is_sign);
+	if (finfo->vcpu_init || finfo->opt_in_cap) {
+		/*
+		 * If the feature needs to be enabled with KVM_ARM_VCPU_INIT
+		 * or opt-in capabilities, the default value of the ID register
+		 * shouldn't indicate the feature.
+		 */
+		TEST_ASSERT(fval < min, "%s field of %s is too big (%ld)",
+		    finfo->name, sreg->name, fval);
+	} else {
+		/* Update the relevant field to hide the feature. */
+		fval = is_sign ? 0xf : 0x0;
+		reg_val = UPDATE_ID_UFIELD(reg_val, shift, fval);
+		ret = _vcpu_ioctl(vm, vcpu, KVM_SET_ONE_REG, &one_reg);
+		TEST_ASSERT(ret == 0, "Disabling %s failed %d\n",
+			    finfo->name, ret);
+		sreg->current_value = reg_val;
+	}
+
+	pr_debug("%s: Test with %s disabled (%s: 0x%lx)\n",
+		 __func__, finfo->name, sreg->name, sreg->current_value);
+
+	/* Make sure that the guest sees the same ID register value. */
+	ret = TEST_RUN(vm, vcpu);
+	finfo->run_test = 0;
+	test_vm_free(vm);
+}
+
+/*
+ * For each opt-in feature in feature_test_info_table[],
+ * test if KVM_GET_ONE_REG/KVM_SET_ONE_REG works appropriately according
+ * to the feature configuration.  See test_feature's comment for more detail.
+ */
+static void test_feature_all(void)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(feature_test_info_table); i++)
+		test_feature(&feature_test_info_table[i]);
+}
+
+int set_id_reg(struct kvm_vm *vm, uint32_t vcpu, struct id_reg_test_info *sreg,
+	       uint64_t new_val)
+{
+	int ret;
+	uint64_t reg_val;
+	struct kvm_one_reg one_reg;
+
+	one_reg.id = KVM_ARM64_SYS_REG(sreg->id);
+	one_reg.addr = (uint64_t)&reg_val;
+
+	reg_val = new_val;
+	ret = _vcpu_ioctl(vm, vcpu, KVM_SET_ONE_REG, &one_reg);
+	if (!ret)
+		sreg->current_value = new_val;
+
+	return ret;
+}
+
+
+/*
+ * Create a new VM with one vCPU, set the ID register to @new_val.
+ */
+int set_id_reg_vm(struct id_reg_test_info *sreg, uint64_t new_val)
+{
+	struct kvm_vm *vm;
+	int ret;
+	uint32_t vcpu = 0;
+
+	reset_id_reg_info();
+
+	vm = test_vm_create(1, guest_code_id_reg_check_all, NULL, NULL);
+	ret = set_id_reg(vm, vcpu, sreg, new_val);
+	test_vm_free(vm);
+
+	return ret;
+}
+
+struct frac_info {
+	char	*name;
+	struct id_reg_test_info *sreg;
+	struct id_reg_test_info *frac_sreg;
+	int	shift;
+	int	frac_shift;
+};
+
+struct frac_info frac_info_table[] = {
+	{
+		.name = "RAS",
+		.sreg = ID_REG_INFO(ID_AA64PFR0),
+		.shift = ID_AA64PFR0_RAS_SHIFT,
+		.frac_sreg = ID_REG_INFO(ID_AA64PFR1),
+		.frac_shift = ID_AA64PFR1_RASFRAC_SHIFT,
+	},
+	{
+		.name = "MPAM",
+		.sreg = ID_REG_INFO(ID_AA64PFR0),
+		.shift = ID_AA64PFR0_MPAM_SHIFT,
+		.frac_sreg = ID_REG_INFO(ID_AA64PFR1),
+		.frac_shift = ID_AA64PFR1_MPAMFRAC_SHIFT,
+	},
+	{
+		.name = "CSV2",
+		.sreg = ID_REG_INFO(ID_AA64PFR0),
+		.shift = ID_AA64PFR0_CSV2_SHIFT,
+		.frac_sreg = ID_REG_INFO(ID_AA64PFR1),
+		.frac_shift = ID_AA64PFR1_CSV2FRAC_SHIFT,
+	},
+};
+
+
+/*
+ * Make sure that we can set the fractional reg field even before setting
+ * the feature reg field.
+ */
+int test_feature_frac_vm(struct frac_info *frac, uint64_t new_val,
+			 uint64_t frac_new_val)
+{
+	struct kvm_vm *vm;
+	uint32_t vcpu = 0;
+	struct id_reg_test_info *sreg, *frac_sreg;
+	int ret;
+
+	sreg = frac->sreg;
+	frac_sreg = frac->frac_sreg;
+	reset_id_reg_info();
+
+	reset_id_reg_info();
+
+	vm = test_vm_create(1, guest_code_id_reg_check_all, NULL, NULL);
+
+	/* Set fractional reg field */
+	ret = set_id_reg(vm, vcpu, frac_sreg, frac_new_val);
+	TEST_ASSERT(!ret, "SET_REG(%s=0x%lx) failed, ret=0x%x",
+		    frac_sreg->name, frac_new_val, ret);
+
+	/* Set feature reg field */
+	ret = set_id_reg(vm, vcpu, sreg, new_val);
+	TEST_ASSERT(!ret, "SET_REG(%s=0x%lx) failed, ret=0x%x",
+		    sreg->name, new_val, ret);
+
+	ret = TEST_RUN(vm, vcpu);
+	test_vm_free(vm);
+
+	return ret;
+}
+
+/*
+ * Test for setting the feature fractional field of the ID register.
+ * When the (main) feature field of the ID register is the same as the host's,
+ * the fractional field value cannot be larger than the host's.
+ * (KVM_SET_ONE_REG should work but KVM_RUN with the larger value will fail)
+ * When the (main) feature field of the ID register is smaler than the host's,
+ * the fractional field can be any values.
+ * The function tests those behaviors.
+ */
+void test_feature_frac_one(struct frac_info *frac)
+{
+	uint64_t ftr_val, ftr_fval, frac_val, frac_fval;
+	int ret, shift, frac_shift;
+	struct id_reg_test_info *sreg, *frac_sreg;
+
+	reset_id_reg_info();
+
+	sreg = frac->sreg;
+	shift = frac->shift;
+	frac_sreg = frac->frac_sreg;
+	frac_shift = frac->frac_shift;
+
+	pr_debug("%s(%s Frac) reg:%s(shift:%d) frac reg:%s(shift:%d)\n",
+		 __func__, frac->name, sreg->name, shift, frac_sreg->name,
+		 frac_shift);
+
+	/*
+	 * Use the host's feature value for the guest.
+	 * KVM_RUN with a larger frac value than the host's should fail.
+	 * Otherwise, it should work.
+	 */
+
+	frac_fval = GET_ID_UFIELD(frac_sreg->initial_value, frac_shift);
+	if (frac_fval > 0) {
+		/* Test with smaller frac value */
+		frac_val = UPDATE_ID_UFIELD(frac_sreg->initial_value,
+					    frac_shift, frac_fval - 1);
+		ret = test_feature_frac_vm(frac, sreg->initial_value, frac_val);
+		TEST_ASSERT(!ret, "Test smaller %s frac (val:%lx) failed(%d)",
+			    frac->name, frac_val, ret);
+	}
+
+	reset_id_reg_info();
+
+	if (frac_fval != 0xf) {
+		/* Test with larger frac value */
+		frac_val = UPDATE_ID_UFIELD(frac_sreg->initial_value,
+						frac_shift, frac_fval + 1);
+
+		/* Setting larger frac shouldn't fail at ioctl */
+		ret = set_id_reg_vm(frac_sreg, frac_val);
+		TEST_ASSERT(!ret,
+			"SET larger %s frac (%s org:%lx, val:%lx) failed(%d)",
+			frac->name, frac_sreg->name, frac_sreg->initial_value,
+			frac_val, ret);
+
+		/* KVM_RUN with larger frac should fail */
+		ret = test_feature_frac_vm(frac, sreg->initial_value, frac_val);
+		TEST_ASSERT(ret,
+			"Test with larger %s frac (%s org:%lx, val:%lx) worked",
+			frac->name, frac_sreg->name, frac_sreg->initial_value,
+			frac_val);
+	}
+
+	reset_id_reg_info();
+
+	/*
+	 * Test with a smaller (main) feature value than the host's.
+	 */
+	ftr_fval = GET_ID_UFIELD(sreg->initial_value, shift);
+	if (ftr_fval == 0)
+		/* Cannot set it to the smaller value */
+		return;
+
+	ftr_val = UPDATE_ID_UFIELD(sreg->initial_value, shift, ftr_fval - 1);
+	ret = test_feature_frac_vm(frac, ftr_val, frac_sreg->initial_value);
+	TEST_ASSERT(!ret, "Test with smaller %s (val:%lx) failed(%d)",
+		    frac->name, ftr_val, ret);
+
+	if (frac_fval > 0) {
+		/* Test with smaller frac value */
+		frac_val = UPDATE_ID_UFIELD(frac_sreg->initial_value,
+					    frac_shift, frac_fval - 1);
+		ret = test_feature_frac_vm(frac, ftr_val, frac_val);
+		TEST_ASSERT(!ret,
+		    "Test with smaller %s and frac (val:%lx) failed(%d)",
+		    frac->name, ftr_val, ret);
+	}
+
+	if (frac_fval != 0xf) {
+		/* Test with larger frac value */
+		frac_val = UPDATE_ID_UFIELD(frac_sreg->initial_value,
+					    frac_shift, frac_fval + 1);
+		ret = test_feature_frac_vm(frac, ftr_val, frac_val);
+		TEST_ASSERT(!ret,
+		    "Test with smaller %s and larger frac (val:%lx) failed(%d)",
+		    frac->name, ftr_val, ret);
+	}
+}
+
+/*
+ * Test for setting feature fractional fields of ID registers.
+ * See test_feature_frac_one's comments for more detail.
+ */
+void test_feature_frac_all(void)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(frac_info_table); i++)
+		test_feature_frac_one(&frac_info_table[i]);
+}
+
+void run_test(void)
+{
+	test_id_regs_basic();
+	test_feature_all();
+	test_feature_ptrauth();
+	test_feature_frac_all();
+}
+
+static void init_id_reg_info_one(struct id_reg_test_info *sreg, void *arg)
+{
+	struct kvm_one_reg one_reg;
+	uint64_t reg_val;
+	struct kvm_vm *vm = ((struct vm_vcpu_arg *)arg)->vm;
+	uint32_t vcpuid = ((struct vm_vcpu_arg *)arg)->vcpuid;
+	int ret;
+
+	one_reg.addr = (uint64_t)&reg_val;
+	one_reg.id = KVM_ARM64_SYS_REG(sreg->id);
+	vcpu_ioctl(vm, vcpuid, KVM_GET_ONE_REG, &one_reg);
+	sreg->current_value = reg_val;
+
+	/* Keep the initial value to reset the register value later */
+	sreg->initial_value = reg_val;
+
+	/* Check if the register can be set to 0 */
+	reg_val = 0;
+	ret = _vcpu_ioctl(vm, vcpuid, KVM_SET_ONE_REG, &one_reg);
+	if (!ret)
+		sreg->can_clear = true;
+
+	pr_debug("%s (0x%x): 0x%lx%s\n", sreg->name, sreg->id,
+		 sreg->initial_value, sreg->can_clear ? ", can clear" : "");
+}
+
+/*
+ * Check if aarch32 is supported, and initialize id_reg_test_info for all
+ * the ID registers.  Loop over the idreg list and populates each id_reg
+ * info with the initial value, current value, and can_clear value.
+ */
+static void init_test_info(void)
+{
+	uint64_t reg_val;
+	int fval;
+	struct kvm_vm *vm;
+	struct kvm_one_reg one_reg;
+	struct vm_vcpu_arg arg = { .vcpuid = 0 };
+
+	vm = test_vm_create(1, guest_code_do_nothing, NULL, NULL);
+
+	/* Get ID_AA64PFR0_EL1 to check if AArch32 is supported */
+	one_reg.addr = (uint64_t)&reg_val;
+	one_reg.id = KVM_ARM64_SYS_REG(SYS_ID_AA64PFR0_EL1);
+	vcpu_ioctl(vm, 0, KVM_GET_ONE_REG, &one_reg);
+	fval = GET_ID_UFIELD(reg_val, ID_AA64PFR0_EL0_SHIFT);
+	if (fval == 0x1)
+		/* No AArch32 support */
+		aarch32_support = false;
+
+	/* Initialize id_reg_test_info */
+	arg.vm = vm;
+	walk_id_reg_list(init_id_reg_info_one, &arg);
+	test_vm_free(vm);
+}
+
+int main(void)
+{
+
+	setbuf(stdout, NULL);
+
+	if (kvm_check_cap(KVM_CAP_ARM_ID_REG_CONFIGURABLE) <= 0) {
+		print_skip("KVM_CAP_ARM_ID_REG_CONFIGURABLE is not supported\n");
+		exit(KSFT_SKIP);
+	}
+
+	init_test_info();
+	run_test();
+	return 0;
+}
-- 
2.34.1.448.ga2b2bfdf31-goog

_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* [RFC PATCH v4 26/26] KVM: arm64: selftests: Introduce id_reg_test
@ 2022-01-06  4:27   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-06  4:27 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, linux-arm-kernel, James Morse, Alexandru Elisei,
	Suzuki K Poulose, Paolo Bonzini, Will Deacon, Andrew Jones,
	Peng Liang, Peter Shier, Ricardo Koller, Oliver Upton,
	Jing Zhang, Raghavendra Rao Anata, Reiji Watanabe

Introduce a test for aarch64 to validate basic behavior of
KVM_GET_ONE_REG and KVM_SET_ONE_REG for ID registers.

This test runs only when KVM_CAP_ARM_ID_REG_CONFIGURABLE is supported.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 tools/arch/arm64/include/asm/sysreg.h         |    1 +
 tools/testing/selftests/kvm/.gitignore        |    1 +
 tools/testing/selftests/kvm/Makefile          |    1 +
 .../selftests/kvm/aarch64/id_reg_test.c       | 1239 +++++++++++++++++
 4 files changed, 1242 insertions(+)
 create mode 100644 tools/testing/selftests/kvm/aarch64/id_reg_test.c

diff --git a/tools/arch/arm64/include/asm/sysreg.h b/tools/arch/arm64/include/asm/sysreg.h
index 7640fa27be94..be3947c125f1 100644
--- a/tools/arch/arm64/include/asm/sysreg.h
+++ b/tools/arch/arm64/include/asm/sysreg.h
@@ -793,6 +793,7 @@
 #define ID_AA64PFR0_ELx_32BIT_64BIT	0x2
 
 /* id_aa64pfr1 */
+#define ID_AA64PFR1_CSV2FRAC_SHIFT	32
 #define ID_AA64PFR1_MPAMFRAC_SHIFT	16
 #define ID_AA64PFR1_RASFRAC_SHIFT	12
 #define ID_AA64PFR1_MTE_SHIFT		8
diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore
index 3cb5ac5da087..e0a5f304375d 100644
--- a/tools/testing/selftests/kvm/.gitignore
+++ b/tools/testing/selftests/kvm/.gitignore
@@ -2,6 +2,7 @@
 /aarch64/arch_timer
 /aarch64/debug-exceptions
 /aarch64/get-reg-list
+/aarch64/id_reg_test
 /aarch64/psci_cpu_on_test
 /aarch64/vgic_init
 /s390x/memop
diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
index 17342b575e85..0162a7d98889 100644
--- a/tools/testing/selftests/kvm/Makefile
+++ b/tools/testing/selftests/kvm/Makefile
@@ -94,6 +94,7 @@ TEST_GEN_PROGS_x86_64 += system_counter_offset_test
 TEST_GEN_PROGS_aarch64 += aarch64/arch_timer
 TEST_GEN_PROGS_aarch64 += aarch64/debug-exceptions
 TEST_GEN_PROGS_aarch64 += aarch64/get-reg-list
+TEST_GEN_PROGS_aarch64 += aarch64/id_reg_test
 TEST_GEN_PROGS_aarch64 += aarch64/psci_cpu_on_test
 TEST_GEN_PROGS_aarch64 += aarch64/vgic_init
 TEST_GEN_PROGS_aarch64 += demand_paging_test
diff --git a/tools/testing/selftests/kvm/aarch64/id_reg_test.c b/tools/testing/selftests/kvm/aarch64/id_reg_test.c
new file mode 100644
index 000000000000..e2cac415ad57
--- /dev/null
+++ b/tools/testing/selftests/kvm/aarch64/id_reg_test.c
@@ -0,0 +1,1239 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#define _GNU_SOURCE
+
+#include <stdlib.h>
+#include <time.h>
+#include <pthread.h>
+#include <linux/kvm.h>
+#include <linux/sizes.h>
+
+#include "kvm_util.h"
+#include "processor.h"
+#include "vgic.h"
+
+/*
+ * id_reg_test.c - Tests reading/writing the aarch64's ID registers
+ *
+ * The test validates KVM_SET_ONE_REG/KVM_GET_ONE_REG ioctl for ID
+ * registers as well as reading ID register from the guest works fine.
+ */
+
+/* Reserved ID registers */
+#define	SYS_ID_REG_3_3_EL1		sys_reg(3, 0, 0, 3, 3)
+#define	SYS_ID_REG_3_7_EL1		sys_reg(3, 0, 0, 3, 7)
+
+#define	SYS_ID_REG_4_2_EL1		sys_reg(3, 0, 0, 4, 2)
+#define	SYS_ID_REG_4_3_EL1		sys_reg(3, 0, 0, 4, 3)
+#define	SYS_ID_REG_4_5_EL1		sys_reg(3, 0, 0, 4, 5)
+#define	SYS_ID_REG_4_6_EL1		sys_reg(3, 0, 0, 4, 6)
+#define	SYS_ID_REG_4_7_EL1		sys_reg(3, 0, 0, 4, 7)
+
+#define	SYS_ID_REG_5_2_EL1		sys_reg(3, 0, 0, 5, 2)
+#define	SYS_ID_REG_5_3_EL1		sys_reg(3, 0, 0, 5, 3)
+#define	SYS_ID_REG_5_6_EL1		sys_reg(3, 0, 0, 5, 6)
+#define	SYS_ID_REG_5_7_EL1		sys_reg(3, 0, 0, 5, 7)
+
+#define	SYS_ID_REG_6_2_EL1		sys_reg(3, 0, 0, 6, 2)
+#define	SYS_ID_REG_6_3_EL1		sys_reg(3, 0, 0, 6, 3)
+#define	SYS_ID_REG_6_4_EL1		sys_reg(3, 0, 0, 6, 4)
+#define	SYS_ID_REG_6_5_EL1		sys_reg(3, 0, 0, 6, 5)
+#define	SYS_ID_REG_6_6_EL1		sys_reg(3, 0, 0, 6, 6)
+#define	SYS_ID_REG_6_7_EL1		sys_reg(3, 0, 0, 6, 7)
+
+#define	SYS_ID_REG_7_3_EL1		sys_reg(3, 0, 0, 7, 3)
+#define	SYS_ID_REG_7_4_EL1		sys_reg(3, 0, 0, 7, 4)
+#define	SYS_ID_REG_7_5_EL1		sys_reg(3, 0, 0, 7, 5)
+#define	SYS_ID_REG_7_6_EL1		sys_reg(3, 0, 0, 7, 6)
+#define	SYS_ID_REG_7_7_EL1		sys_reg(3, 0, 0, 7, 7)
+
+#define	READ_ID_REG_FN(name)	read_## name ## _EL1
+
+#define	DEFINE_READ_SYS_REG(reg_name)			\
+uint64_t read_##reg_name(void)				\
+{							\
+	return read_sysreg_s(SYS_##reg_name);		\
+}
+
+#define DEFINE_READ_ID_REG(name)	\
+	DEFINE_READ_SYS_REG(name ## _EL1)
+
+#define	__ID_REG(reg_name)		\
+	.name = #reg_name,		\
+	.id = SYS_## reg_name ##_EL1,	\
+	.read_reg = READ_ID_REG_FN(reg_name),
+
+#define	ID_REG_ENT(reg_name)	\
+	[ID_IDX(reg_name)] = { __ID_REG(reg_name) }
+
+/* Functions to read each ID register */
+/* CRm=1 */
+DEFINE_READ_ID_REG(ID_PFR0)
+DEFINE_READ_ID_REG(ID_PFR1)
+DEFINE_READ_ID_REG(ID_DFR0)
+DEFINE_READ_ID_REG(ID_AFR0)
+DEFINE_READ_ID_REG(ID_MMFR0)
+DEFINE_READ_ID_REG(ID_MMFR1)
+DEFINE_READ_ID_REG(ID_MMFR2)
+DEFINE_READ_ID_REG(ID_MMFR3)
+
+/* CRm=2 */
+DEFINE_READ_ID_REG(ID_ISAR0)
+DEFINE_READ_ID_REG(ID_ISAR1)
+DEFINE_READ_ID_REG(ID_ISAR2)
+DEFINE_READ_ID_REG(ID_ISAR3)
+DEFINE_READ_ID_REG(ID_ISAR4)
+DEFINE_READ_ID_REG(ID_ISAR5)
+DEFINE_READ_ID_REG(ID_MMFR4)
+DEFINE_READ_ID_REG(ID_ISAR6)
+
+/* CRm=3 */
+DEFINE_READ_ID_REG(MVFR0)
+DEFINE_READ_ID_REG(MVFR1)
+DEFINE_READ_ID_REG(MVFR2)
+DEFINE_READ_ID_REG(ID_REG_3_3)
+DEFINE_READ_ID_REG(ID_PFR2)
+DEFINE_READ_ID_REG(ID_DFR1)
+DEFINE_READ_ID_REG(ID_MMFR5)
+DEFINE_READ_ID_REG(ID_REG_3_7)
+
+/* CRm=4 */
+DEFINE_READ_ID_REG(ID_AA64PFR0)
+DEFINE_READ_ID_REG(ID_AA64PFR1)
+DEFINE_READ_ID_REG(ID_REG_4_2)
+DEFINE_READ_ID_REG(ID_REG_4_3)
+DEFINE_READ_ID_REG(ID_AA64ZFR0)
+DEFINE_READ_ID_REG(ID_REG_4_5)
+DEFINE_READ_ID_REG(ID_REG_4_6)
+DEFINE_READ_ID_REG(ID_REG_4_7)
+
+/* CRm=5 */
+DEFINE_READ_ID_REG(ID_AA64DFR0)
+DEFINE_READ_ID_REG(ID_AA64DFR1)
+DEFINE_READ_ID_REG(ID_REG_5_2)
+DEFINE_READ_ID_REG(ID_REG_5_3)
+DEFINE_READ_ID_REG(ID_AA64AFR0)
+DEFINE_READ_ID_REG(ID_AA64AFR1)
+DEFINE_READ_ID_REG(ID_REG_5_6)
+DEFINE_READ_ID_REG(ID_REG_5_7)
+
+/* CRm=6 */
+DEFINE_READ_ID_REG(ID_AA64ISAR0)
+DEFINE_READ_ID_REG(ID_AA64ISAR1)
+DEFINE_READ_ID_REG(ID_REG_6_2)
+DEFINE_READ_ID_REG(ID_REG_6_3)
+DEFINE_READ_ID_REG(ID_REG_6_4)
+DEFINE_READ_ID_REG(ID_REG_6_5)
+DEFINE_READ_ID_REG(ID_REG_6_6)
+DEFINE_READ_ID_REG(ID_REG_6_7)
+
+/* CRm=7 */
+DEFINE_READ_ID_REG(ID_AA64MMFR0)
+DEFINE_READ_ID_REG(ID_AA64MMFR1)
+DEFINE_READ_ID_REG(ID_AA64MMFR2)
+DEFINE_READ_ID_REG(ID_REG_7_3)
+DEFINE_READ_ID_REG(ID_REG_7_4)
+DEFINE_READ_ID_REG(ID_REG_7_5)
+DEFINE_READ_ID_REG(ID_REG_7_6)
+DEFINE_READ_ID_REG(ID_REG_7_7)
+
+#define	ID_IDX(name)	REG_IDX_## name
+
+enum id_reg_idx {
+	/* CRm=1 */
+	ID_IDX(ID_PFR0) = 0,
+	ID_IDX(ID_PFR1),
+	ID_IDX(ID_DFR0),
+	ID_IDX(ID_AFR0),
+	ID_IDX(ID_MMFR0),
+	ID_IDX(ID_MMFR1),
+	ID_IDX(ID_MMFR2),
+	ID_IDX(ID_MMFR3),
+
+	/* CRm=2 */
+	ID_IDX(ID_ISAR0),
+	ID_IDX(ID_ISAR1),
+	ID_IDX(ID_ISAR2),
+	ID_IDX(ID_ISAR3),
+	ID_IDX(ID_ISAR4),
+	ID_IDX(ID_ISAR5),
+	ID_IDX(ID_MMFR4),
+	ID_IDX(ID_ISAR6),
+
+	/* CRm=3 */
+	ID_IDX(MVFR0),
+	ID_IDX(MVFR1),
+	ID_IDX(MVFR2),
+	ID_IDX(ID_REG_3_3),
+	ID_IDX(ID_PFR2),
+	ID_IDX(ID_DFR1),
+	ID_IDX(ID_MMFR5),
+	ID_IDX(ID_REG_3_7),
+
+	/* CRm=4 */
+	ID_IDX(ID_AA64PFR0),
+	ID_IDX(ID_AA64PFR1),
+	ID_IDX(ID_REG_4_2),
+	ID_IDX(ID_REG_4_3),
+	ID_IDX(ID_AA64ZFR0),
+	ID_IDX(ID_REG_4_5),
+	ID_IDX(ID_REG_4_6),
+	ID_IDX(ID_REG_4_7),
+
+	/* CRm=5 */
+	ID_IDX(ID_AA64DFR0),
+	ID_IDX(ID_AA64DFR1),
+	ID_IDX(ID_REG_5_2),
+	ID_IDX(ID_REG_5_3),
+	ID_IDX(ID_AA64AFR0),
+	ID_IDX(ID_AA64AFR1),
+	ID_IDX(ID_REG_5_6),
+	ID_IDX(ID_REG_5_7),
+
+	/* CRm=6 */
+	ID_IDX(ID_AA64ISAR0),
+	ID_IDX(ID_AA64ISAR1),
+	ID_IDX(ID_REG_6_2),
+	ID_IDX(ID_REG_6_3),
+	ID_IDX(ID_REG_6_4),
+	ID_IDX(ID_REG_6_5),
+	ID_IDX(ID_REG_6_6),
+	ID_IDX(ID_REG_6_7),
+
+	/* CRm=7 */
+	ID_IDX(ID_AA64MMFR0),
+	ID_IDX(ID_AA64MMFR1),
+	ID_IDX(ID_AA64MMFR2),
+	ID_IDX(ID_REG_7_3),
+	ID_IDX(ID_REG_7_4),
+	ID_IDX(ID_REG_7_5),
+	ID_IDX(ID_REG_7_6),
+	ID_IDX(ID_REG_7_7),
+};
+
+struct id_reg_test_info {
+	char		*name;
+	uint32_t	id;
+	/* Indicates the register can be set to 0 */
+	bool		can_clear;
+	uint64_t	initial_value;
+	uint64_t	current_value;
+	uint64_t	(*read_reg)(void);
+};
+
+#define	ID_REG_INFO(name)	(&id_reg_list[ID_IDX(name)])
+static struct id_reg_test_info id_reg_list[] = {
+	/* CRm=1 */
+	ID_REG_ENT(ID_PFR0),
+	ID_REG_ENT(ID_PFR1),
+	ID_REG_ENT(ID_DFR0),
+	ID_REG_ENT(ID_AFR0),
+	ID_REG_ENT(ID_MMFR0),
+	ID_REG_ENT(ID_MMFR1),
+	ID_REG_ENT(ID_MMFR2),
+	ID_REG_ENT(ID_MMFR3),
+
+	/* CRm=2 */
+	ID_REG_ENT(ID_ISAR0),
+	ID_REG_ENT(ID_ISAR1),
+	ID_REG_ENT(ID_ISAR2),
+	ID_REG_ENT(ID_ISAR3),
+	ID_REG_ENT(ID_ISAR4),
+	ID_REG_ENT(ID_ISAR5),
+	ID_REG_ENT(ID_MMFR4),
+	ID_REG_ENT(ID_ISAR6),
+
+	/* CRm=3 */
+	ID_REG_ENT(MVFR0),
+	ID_REG_ENT(MVFR1),
+	ID_REG_ENT(MVFR2),
+	ID_REG_ENT(ID_REG_3_3),
+	ID_REG_ENT(ID_PFR2),
+	ID_REG_ENT(ID_DFR1),
+	ID_REG_ENT(ID_MMFR5),
+	ID_REG_ENT(ID_REG_3_7),
+
+	/* CRm=4 */
+	ID_REG_ENT(ID_AA64PFR0),
+	ID_REG_ENT(ID_AA64PFR1),
+	ID_REG_ENT(ID_REG_4_2),
+	ID_REG_ENT(ID_REG_4_3),
+	ID_REG_ENT(ID_AA64ZFR0),
+	ID_REG_ENT(ID_REG_4_5),
+	ID_REG_ENT(ID_REG_4_6),
+	ID_REG_ENT(ID_REG_4_7),
+
+	/* CRm=5 */
+	ID_REG_ENT(ID_AA64DFR0),
+	ID_REG_ENT(ID_AA64DFR1),
+	ID_REG_ENT(ID_REG_5_2),
+	ID_REG_ENT(ID_REG_5_3),
+	ID_REG_ENT(ID_AA64AFR0),
+	ID_REG_ENT(ID_AA64AFR1),
+	ID_REG_ENT(ID_REG_5_6),
+	ID_REG_ENT(ID_REG_5_7),
+
+	/* CRm=6 */
+	ID_REG_ENT(ID_AA64ISAR0),
+	ID_REG_ENT(ID_AA64ISAR1),
+	ID_REG_ENT(ID_REG_6_2),
+	ID_REG_ENT(ID_REG_6_3),
+	ID_REG_ENT(ID_REG_6_4),
+	ID_REG_ENT(ID_REG_6_5),
+	ID_REG_ENT(ID_REG_6_6),
+	ID_REG_ENT(ID_REG_6_7),
+
+	/* CRm=7 */
+	ID_REG_ENT(ID_AA64MMFR0),
+	ID_REG_ENT(ID_AA64MMFR1),
+	ID_REG_ENT(ID_AA64MMFR2),
+	ID_REG_ENT(ID_REG_7_3),
+	ID_REG_ENT(ID_REG_7_4),
+	ID_REG_ENT(ID_REG_7_5),
+	ID_REG_ENT(ID_REG_7_6),
+	ID_REG_ENT(ID_REG_7_7),
+};
+
+static bool aarch32_support = true;
+
+/* Utilities to get a feature field from ID register value */
+static inline int
+cpuid_signed_field_width(uint64_t id_val, int field, int width)
+{
+	return (s64)(id_val << (64 - width - field)) >> (64 - width);
+}
+
+static unsigned int
+cpuid_unsigned_field_width(uint64_t id_val, int field, int width)
+{
+	return (uint64_t)(id_val << (64 - width - field)) >> (64 - width);
+}
+
+static inline int __attribute_const__
+cpuid_extract_field_width(uint64_t id_val, int field, int width, bool sign)
+{
+	return (sign) ? cpuid_signed_field_width(id_val, field, width) :
+			cpuid_unsigned_field_width(id_val, field, width);
+}
+
+#define is_id_reg(id)	\
+	(sys_reg_Op0(id) == 3 && sys_reg_Op1(id) == 0 &&	\
+	 sys_reg_CRn(id) == 0 && sys_reg_CRm(id) >= 0 &&	\
+	 sys_reg_CRm(id) < 8)
+
+#define	GET_ID_FIELD(regval, shift, is_signed)	\
+	cpuid_extract_field_width(regval, shift, 4, is_signed)
+
+#define	GET_ID_UFIELD(regval, shift)	\
+	cpuid_unsigned_field_width(regval, shift, 4)
+
+#define	UPDATE_ID_UFIELD(regval, shift, fval)	\
+	(((regval) & ~(0xfULL << (shift))) |	\
+	 (((uint64_t)((fval) & 0xf)) << (shift)))
+
+void pmu_init(struct kvm_vm *vm, uint32_t vcpu)
+{
+	struct kvm_device_attr attr = {
+		.group = KVM_ARM_VCPU_PMU_V3_CTRL,
+		.attr = KVM_ARM_VCPU_PMU_V3_INIT,
+	};
+	vcpu_ioctl(vm, vcpu, KVM_SET_DEVICE_ATTR, &attr);
+}
+
+void sve_init(struct kvm_vm *vm, uint32_t vcpu)
+{
+	int feature = KVM_ARM_VCPU_SVE;
+
+	vcpu_ioctl(vm, vcpu, KVM_ARM_VCPU_FINALIZE, &feature);
+}
+
+#define GICD_BASE_GPA			0x8000000ULL
+#define GICR_BASE_GPA			0x80A0000ULL
+
+void test_vgic_init(struct kvm_vm *vm, uint32_t vcpu)
+{
+	/* We jsut need to configure gic v3 (we don't use it though) */
+	vgic_v3_setup(vm, 1, GICD_BASE_GPA, GICR_BASE_GPA);
+}
+
+static bool is_aarch32_id_reg(uint32_t id)
+{
+	uint32_t crm, op2;
+
+	if (!is_id_reg(id))
+		return false;
+
+	crm = sys_reg_CRm(id);
+	op2 = sys_reg_Op2(id);
+	if (crm == 1 || crm == 2 || (crm == 3 && (op2 != 3 && op2 != 7)))
+		/* AArch32 ID register */
+		return true;
+
+	return false;
+}
+
+#define	MAX_CAPS	2
+struct feature_test_info {
+	char	*name;	/* Feature Name (Debug information) */
+
+	/* ID register that identifies the presence of the feature */
+	struct id_reg_test_info	*sreg;
+
+	/*
+	 * Bit position of the ID register field that identifies
+	 * the presence of the feature.
+	 */
+	int	shift;
+
+	/* Min value of the field that indicates the presence of the feature. */
+	int	min;
+	bool	is_sign;	/* Is the field signed or unsigned ? */
+	int	ncaps;		/* Number of valid Capabilities in caps[] */
+
+	/* KVM_CAP_* Capabilities to indicates that KVM supports this feature */
+	long	caps[MAX_CAPS];
+
+	/* struct kvm_enable_cap to use the capability if needed */
+	struct kvm_enable_cap	*opt_in_cap;
+
+	/* Should the guest check the ID register for this feature ? */
+	bool	run_test;
+
+	/*
+	 * Extra initialization function to enable the feature if needed.
+	 * (e.g. KVM_ARM_VCPU_FINALIZE for SVE)
+	 */
+	void	(*init_feature)(struct kvm_vm *vm, uint32_t vcpuid);
+
+	/* struct kvm_vcpu_init to opt-in the feature if needed */
+	struct kvm_vcpu_init	*vcpu_init;
+};
+
+/* Information for opt-in CPU features */
+static struct feature_test_info feature_test_info_table[] = {
+	{
+		.name = "SVE",
+		.sreg = ID_REG_INFO(ID_AA64PFR0),
+		.shift = ID_AA64PFR0_SVE_SHIFT,
+		.min = 1,
+		.caps = {KVM_CAP_ARM_SVE},
+		.ncaps = 1,
+		.init_feature = sve_init,
+		.vcpu_init = &(struct kvm_vcpu_init) {
+			.features = {1ULL << KVM_ARM_VCPU_SVE},
+		},
+	},
+	{
+		.name = "GIC",
+		.sreg = ID_REG_INFO(ID_AA64PFR0),
+		.shift = ID_AA64PFR0_GIC_SHIFT,
+		.min = 1,
+		.caps = {KVM_CAP_IRQCHIP},
+		.ncaps = 1,
+		.init_feature = test_vgic_init,
+	},
+	{
+		.name = "MTE",
+		.sreg = ID_REG_INFO(ID_AA64PFR1),
+		.shift = ID_AA64PFR1_MTE_SHIFT,
+		.min = 2,
+		.caps = {KVM_CAP_ARM_MTE},
+		.ncaps = 1,
+		.opt_in_cap = &(struct kvm_enable_cap) {
+				.cap = KVM_CAP_ARM_MTE,
+		},
+	},
+	{
+		.name = "PMUV3",
+		.sreg = ID_REG_INFO(ID_AA64DFR0),
+		.shift = ID_AA64DFR0_PMUVER_SHIFT,
+		.min = 1,
+		.init_feature = pmu_init,
+		.caps = {KVM_CAP_ARM_PMU_V3},
+		.ncaps = 1,
+		.vcpu_init = &(struct kvm_vcpu_init) {
+			.features = {1ULL << KVM_ARM_VCPU_PMU_V3},
+		},
+	},
+	{
+		.name = "PERFMON",
+		.sreg = ID_REG_INFO(ID_DFR0),
+		.shift = ID_DFR0_PERFMON_SHIFT,
+		.min = 3,
+		.init_feature = pmu_init,
+		.caps = {KVM_CAP_ARM_PMU_V3},
+		.ncaps = 1,
+		.vcpu_init = &(struct kvm_vcpu_init) {
+			.features = {1ULL << KVM_ARM_VCPU_PMU_V3},
+		},
+	},
+};
+
+static void walk_id_reg_list(void (*fn)(struct id_reg_test_info *r, void *arg),
+			     void *arg)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(id_reg_list); i++)
+		fn(&id_reg_list[i], arg);
+}
+
+static void guest_code_id_reg_check_one(struct id_reg_test_info *idr, void *arg)
+{
+	uint64_t v = idr->read_reg();
+
+	GUEST_ASSERT_2(v == idr->current_value, idr->name, idr->current_value);
+}
+
+static void guest_code_id_reg_check_all(uint32_t cpu)
+{
+	walk_id_reg_list(guest_code_id_reg_check_one, NULL);
+	GUEST_DONE();
+}
+
+static void guest_code_do_nothing(uint32_t cpu)
+{
+	GUEST_DONE();
+}
+
+static void guest_code_feature_check(uint32_t cpu)
+{
+	int i;
+	struct feature_test_info *finfo;
+
+	for (i = 0; i < ARRAY_SIZE(feature_test_info_table); i++) {
+		finfo = &feature_test_info_table[i];
+		if (finfo->run_test)
+			guest_code_id_reg_check_one(finfo->sreg, NULL);
+	}
+
+	GUEST_DONE();
+}
+
+static void guest_code_ptrauth_check(uint32_t cpuid)
+{
+	struct id_reg_test_info *sreg = ID_REG_INFO(ID_AA64ISAR1);
+	uint64_t val = sreg->read_reg();
+
+	GUEST_ASSERT_2(val == sreg->current_value, "PTRAUTH", val);
+	GUEST_DONE();
+}
+
+static void reset_id_reg_info_current_value(struct id_reg_test_info *info,
+					    void *arg)
+{
+	info->current_value = info->initial_value;
+}
+
+/* Reset current_value field of each id_reg_test_info */
+static void reset_id_reg_info(void)
+{
+	walk_id_reg_list(reset_id_reg_info_current_value, NULL);
+}
+
+static struct kvm_vm *test_vm_create(uint32_t nvcpus,
+		void (*guest_code)(uint32_t), struct kvm_vcpu_init *init,
+		struct kvm_enable_cap *cap)
+{
+	struct kvm_vm *vm;
+	uint32_t cpuid;
+	uint64_t mem_pages;
+
+	mem_pages = DEFAULT_GUEST_PHY_PAGES + DEFAULT_STACK_PGS * nvcpus;
+	mem_pages += mem_pages / (PTES_PER_MIN_PAGE * 2);
+	mem_pages = vm_adjust_num_guest_pages(VM_MODE_DEFAULT, mem_pages);
+
+	vm = vm_create(VM_MODE_DEFAULT, mem_pages, O_RDWR);
+	if (cap)
+		vm_enable_cap(vm, cap);
+
+	kvm_vm_elf_load(vm, program_invocation_name);
+
+	if (init && init->target == -1) {
+		struct kvm_vcpu_init preferred;
+
+		vm_ioctl(vm, KVM_ARM_PREFERRED_TARGET, &preferred);
+		init->target = preferred.target;
+	}
+
+	vm_init_descriptor_tables(vm);
+	for (cpuid = 0; cpuid < nvcpus; cpuid++) {
+		aarch64_vcpu_add_default(vm, cpuid, init, guest_code);
+		vcpu_init_descriptor_tables(vm, cpuid);
+	}
+
+	ucall_init(vm, NULL);
+	return vm;
+}
+
+static void test_vm_free(struct kvm_vm *vm)
+{
+	ucall_uninit(vm);
+	kvm_vm_free(vm);
+}
+
+#define	TEST_RUN(vm, cpu)	\
+	(test_vcpu_run(__func__, __LINE__, vm, cpu, true))
+
+#define	TEST_RUN_NO_SYNC_DATA(vm, cpu)	\
+	(test_vcpu_run(__func__, __LINE__, vm, cpu, false))
+
+static int test_vcpu_run(const char *test_name, int line,
+			 struct kvm_vm *vm, uint32_t vcpuid, bool sync_data)
+{
+	struct ucall uc;
+	int ret;
+
+	if (sync_data) {
+		sync_global_to_guest(vm, id_reg_list);
+		sync_global_to_guest(vm, feature_test_info_table);
+	}
+
+	vcpu_args_set(vm, vcpuid, 1, vcpuid);
+
+	ret = _vcpu_run(vm, vcpuid);
+	if (ret) {
+		ret = errno;
+		goto sync_exit;
+	}
+
+	switch (get_ucall(vm, vcpuid, &uc)) {
+	case UCALL_SYNC:
+	case UCALL_DONE:
+		ret = 0;
+		break;
+	case UCALL_ABORT:
+		TEST_FAIL(
+		    "%s (%s) at line %d (user %s at line %d), args[3]=0x%lx",
+		    (char *)uc.args[0], (char *)uc.args[2], (int)uc.args[1],
+		    test_name, line, uc.args[3]);
+		break;
+	default:
+		TEST_FAIL("Unexpected guest exit\n");
+	}
+
+sync_exit:
+	if (sync_data) {
+		sync_global_from_guest(vm, id_reg_list);
+		sync_global_from_guest(vm, feature_test_info_table);
+	}
+	return ret;
+}
+
+struct vm_vcpu_arg {
+	struct kvm_vm	*vm;
+	uint32_t	vcpuid;
+	bool		after_run;
+};
+
+/*
+ * Test if KVM_SET_ONE_REG can work with the value KVM_GET_ONE_REG returns,
+ * KVM_SET_ONE_REG with zero works before KVM_RUN (and fails after KVM_RUN),
+ * and KVM_GET_ONE_REG returns the value KVM_SET_ONE_REG sets.
+ */
+static void test_get_set_id_reg(struct id_reg_test_info *sreg, void *arg)
+{
+	struct kvm_vm *vm = ((struct vm_vcpu_arg *)arg)->vm;
+	uint32_t vcpuid = ((struct vm_vcpu_arg *)arg)->vcpuid;
+	bool after_run = ((struct vm_vcpu_arg *)arg)->after_run;
+	struct kvm_one_reg one_reg;
+	uint64_t reg_val, tval;
+	int ret;
+
+	one_reg.addr = (uint64_t)&reg_val;
+	one_reg.id = KVM_ARM64_SYS_REG(sreg->id);
+
+	/* Check the current register value */
+	vcpu_ioctl(vm, vcpuid, KVM_GET_ONE_REG, &one_reg);
+	TEST_ASSERT(reg_val == sreg->current_value,
+		    "GET(%s) didn't return 0x%lx but 0x%lx",
+		    sreg->name, sreg->current_value, reg_val);
+	tval = reg_val;
+
+	/* Try to clear the register that should be able to be cleared. */
+	if ((reg_val != 0) && (sreg->can_clear)) {
+		reg_val = 0;
+		ret = _vcpu_ioctl(vm, vcpuid, KVM_SET_ONE_REG, &one_reg);
+		if (after_run) {
+			/* Expect an error after KVM_RUN */
+			TEST_ASSERT(ret,
+				    "Clearing %s unexpectedly worked\n",
+				    sreg->name);
+		} else {
+			TEST_ASSERT(!ret,
+				    "Clearing %s didn't work\n", sreg->name);
+			/*
+			 * Make sure that KVM_GET_ONE_REG provides the value
+			 * we set.
+			 */
+			vcpu_ioctl(vm, vcpuid, KVM_GET_ONE_REG, &one_reg);
+			TEST_ASSERT(reg_val == 0,
+				    "GET(%s) didn't return 0x%lx but 0x%lx",
+				    sreg->name, (uint64_t)0, reg_val);
+		}
+	}
+
+	/* Check if KVM_SET_ONE_REG works with the original value. */
+	reg_val = tval;
+	ret = _vcpu_ioctl(vm, vcpuid, KVM_SET_ONE_REG, &one_reg);
+	TEST_ASSERT(ret == 0, "Setting the same ID reg value should work\n");
+
+	/* Make sure that KVM_GET_ONE_REG provides the value we set. */
+	vcpu_ioctl(vm, vcpuid, KVM_GET_ONE_REG, &one_reg);
+	TEST_ASSERT(reg_val == tval,
+		    "GET(%s) didn't return 0x%lx but 0x%lx",
+		    sreg->name, sreg->current_value, reg_val);
+}
+
+/*
+ * Test if KVM_SET_ONE_REG with the current value works before KVM_RUN,
+ * values of ID registers the guest sees are consistent with the ones
+ * userspace sees, and KVM_SET_ONE_REG after KVM_RUN works when the
+ * specified value is the same as the current one (fails otherwise).
+ */
+static void test_id_regs_basic(void)
+{
+	struct kvm_vm *vm;
+	struct vm_vcpu_arg arg = { .vcpuid = 0 };
+	int ret;
+
+	reset_id_reg_info();
+
+	vm = test_vm_create(1, guest_code_id_reg_check_all, NULL, NULL);
+
+	arg.vm = vm;
+	walk_id_reg_list(test_get_set_id_reg, &arg);
+
+	ret = TEST_RUN(vm, 0);
+	assert(!ret);
+
+	arg.after_run = true;
+	walk_id_reg_list(test_get_set_id_reg, &arg);
+
+	test_vm_free(vm);
+}
+
+static bool caps_are_supported(long *caps, int ncaps)
+{
+	int i;
+
+	for (i = 0; i < ncaps; i++) {
+		if (kvm_check_cap(caps[i]) <= 0)
+			return false;
+	}
+	return true;
+}
+
+#define	NCAPS_PTRAUTH	2
+
+/*
+ * Test if the ID register value reflects the ptrauth feature configuration.
+ * KVM_SET_ONE_REG should work as long as the requested value is consistent
+ * with the ptrauth feature configuration.
+ */
+static void test_feature_ptrauth(void)
+{
+	struct kvm_one_reg one_reg;
+	struct kvm_vcpu_init init;
+	struct kvm_vm *vm = NULL;
+	struct id_reg_test_info *sreg = ID_REG_INFO(ID_AA64ISAR1);
+	uint32_t vcpu = 0;
+	int64_t rval;
+	int ret;
+	int apa, api, gpa, gpi;
+	char *name = "PTRAUTH";
+	long caps[NCAPS_PTRAUTH] = {KVM_CAP_ARM_PTRAUTH_ADDRESS,
+				    KVM_CAP_ARM_PTRAUTH_GENERIC};
+
+	reset_id_reg_info();
+	one_reg.addr = (uint64_t)&rval;
+	one_reg.id = KVM_ARM64_SYS_REG(sreg->id);
+
+	if (caps_are_supported(caps, NCAPS_PTRAUTH)) {
+
+		/* Test with feature enabled */
+		memset(&init, 0, sizeof(init));
+		init.target = -1;
+		init.features[0] = (1ULL << KVM_ARM_VCPU_PTRAUTH_ADDRESS |
+				    1ULL << KVM_ARM_VCPU_PTRAUTH_GENERIC);
+		vm = test_vm_create(1, guest_code_ptrauth_check, &init, NULL);
+		vcpu_ioctl(vm, vcpu, KVM_GET_ONE_REG, &one_reg);
+
+		/* Make sure values of apa/api/gpa/gpi fields are expected */
+		apa = GET_ID_UFIELD(rval, ID_AA64ISAR1_APA_SHIFT);
+		api = GET_ID_UFIELD(rval, ID_AA64ISAR1_API_SHIFT);
+		gpa = GET_ID_UFIELD(rval, ID_AA64ISAR1_GPA_SHIFT);
+		gpi = GET_ID_UFIELD(rval, ID_AA64ISAR1_GPI_SHIFT);
+
+		TEST_ASSERT((apa > 0) || (api > 0),
+			    "Either apa(0x%x) or api(0x%x) must be available",
+			    apa, gpa);
+		TEST_ASSERT((gpa > 0) || (gpi > 0),
+			    "Either gpa(0x%x) or gpi(0x%x) must be available",
+			    gpa, gpi);
+
+		TEST_ASSERT((apa > 0) ^ (api > 0),
+			    "Both apa(0x%x) and api(0x%x) must not be available",
+			    apa, api);
+		TEST_ASSERT((gpa > 0) ^ (gpi > 0),
+			    "Both gpa(0x%x) and gpi(0x%x) must not be available",
+			    gpa, gpi);
+
+		sreg->current_value = rval;
+
+		pr_debug("%s: Test with %s enabled (%s: 0x%lx)\n",
+			 __func__, name, sreg->name, sreg->current_value);
+
+		/* Make sure that the guest sees the same ID register value. */
+		ret = TEST_RUN(vm, vcpu);
+
+		TEST_ASSERT(!ret, "%s:KVM_RUN failed with %s enabled",
+			    __func__, name);
+		test_vm_free(vm);
+	}
+
+	reset_id_reg_info();
+
+	/* Test with feature disabled */
+	vm = test_vm_create(1, guest_code_feature_check, NULL, NULL);
+	vcpu_ioctl(vm, vcpu, KVM_GET_ONE_REG, &one_reg);
+
+	apa = GET_ID_UFIELD(rval, ID_AA64ISAR1_APA_SHIFT);
+	api = GET_ID_UFIELD(rval, ID_AA64ISAR1_API_SHIFT);
+	gpa = GET_ID_UFIELD(rval, ID_AA64ISAR1_GPA_SHIFT);
+	gpi = GET_ID_UFIELD(rval, ID_AA64ISAR1_GPI_SHIFT);
+	TEST_ASSERT(!apa && !api && !gpa && !gpi,
+	    "apa(0x%x), api(0x%x), gpa(0x%x), gpi(0x%x) must be zero",
+	    apa, api, gpa, gpi);
+
+	pr_debug("%s: Test with %s disabled (%s: 0x%lx)\n",
+		 __func__, name, sreg->name, sreg->current_value);
+
+	/* Make sure that the guest sees the same ID register value. */
+	ret = TEST_RUN(vm, vcpu);
+	TEST_ASSERT(!ret, "%s TEST_RUN failed with %s enabled, ret=0x%x",
+		    __func__, name, ret);
+
+	test_vm_free(vm);
+}
+
+static bool feature_caps_are_available(struct feature_test_info *finfo)
+{
+	return ((finfo->ncaps > 0) &&
+		caps_are_supported(finfo->caps, finfo->ncaps));
+}
+
+/*
+ * Test if the ID register value reflects the feature configuration.
+ * KVM_SET_ONE_REG should work as long as the requested value is
+ * consistent with the feature configuration.
+ */
+static void test_feature(struct feature_test_info *finfo)
+{
+	struct id_reg_test_info *sreg = finfo->sreg;
+	struct kvm_one_reg one_reg;
+	struct kvm_vcpu_init init, *initp = NULL;
+	struct kvm_vm *vm = NULL;
+	int64_t fval, reg_val;
+	uint32_t vcpu = 0;
+	bool is_sign = finfo->is_sign;
+	int min = finfo->min;
+	int shift = finfo->shift;
+	int ret;
+
+	pr_debug("%s: %s (reg %s)\n", __func__, finfo->name, sreg->name);
+
+	reset_id_reg_info();
+
+	if (is_aarch32_id_reg(sreg->id) && !aarch32_support)
+		/*
+		 * AArch32 is not supported. Skip testing with the AArch32
+		 * ID register.
+		 */
+		return;
+
+	/* Indicate that guest runs the test for the feature */
+	finfo->run_test = 1;
+	one_reg.addr = (uint64_t)&reg_val;
+	one_reg.id = KVM_ARM64_SYS_REG(sreg->id);
+
+	/*
+	 * Test with feature enabled if the feature is exposed in the default
+	 * ID register value or the capabilities are supported at KVM level.
+	 */
+	if ((GET_ID_FIELD(sreg->initial_value, shift, is_sign) >= min) ||
+	    feature_caps_are_available(finfo)) {
+		if (finfo->vcpu_init) {
+			/* Need to enable the feature via KVM_ARM_VCPU_INIT. */
+			memset(&init, 0, sizeof(init));
+			init = *finfo->vcpu_init;
+			init.target = -1;
+			initp = &init;
+		}
+
+		vm = test_vm_create(1, guest_code_feature_check, initp,
+				    finfo->opt_in_cap);
+		if (finfo->init_feature)
+			/* Run any required extra process to use the feature */
+			finfo->init_feature(vm, vcpu);
+
+		/* Check if the ID register value indicates the feature */
+		vcpu_ioctl(vm, vcpu, KVM_GET_ONE_REG, &one_reg);
+		fval = GET_ID_FIELD(reg_val, shift, is_sign);
+		TEST_ASSERT(fval >= min, "%s field of %s is too small (%ld)",
+			    finfo->name, sreg->name, fval);
+		sreg->current_value = reg_val;
+
+		pr_debug("%s: Test with %s enabled (%s: 0x%lx)\n", __func__,
+			 finfo->name, sreg->name, sreg->current_value);
+
+		/* Make sure that the guest sees the same ID register value. */
+		ret = TEST_RUN(vm, vcpu);
+		TEST_ASSERT(!ret, "%s:TEST_RUN failed with %s enabled",
+			    __func__, finfo->name);
+
+		test_vm_free(vm);
+	}
+
+	reset_id_reg_info();
+
+	/* Test with feature disabled */
+	vm = test_vm_create(1, guest_code_feature_check, NULL, NULL);
+	vcpu_ioctl(vm, vcpu, KVM_GET_ONE_REG, &one_reg);
+	fval = GET_ID_FIELD(reg_val, shift, is_sign);
+	if (finfo->vcpu_init || finfo->opt_in_cap) {
+		/*
+		 * If the feature needs to be enabled with KVM_ARM_VCPU_INIT
+		 * or opt-in capabilities, the default value of the ID register
+		 * shouldn't indicate the feature.
+		 */
+		TEST_ASSERT(fval < min, "%s field of %s is too big (%ld)",
+		    finfo->name, sreg->name, fval);
+	} else {
+		/* Update the relevant field to hide the feature. */
+		fval = is_sign ? 0xf : 0x0;
+		reg_val = UPDATE_ID_UFIELD(reg_val, shift, fval);
+		ret = _vcpu_ioctl(vm, vcpu, KVM_SET_ONE_REG, &one_reg);
+		TEST_ASSERT(ret == 0, "Disabling %s failed %d\n",
+			    finfo->name, ret);
+		sreg->current_value = reg_val;
+	}
+
+	pr_debug("%s: Test with %s disabled (%s: 0x%lx)\n",
+		 __func__, finfo->name, sreg->name, sreg->current_value);
+
+	/* Make sure that the guest sees the same ID register value. */
+	ret = TEST_RUN(vm, vcpu);
+	finfo->run_test = 0;
+	test_vm_free(vm);
+}
+
+/*
+ * For each opt-in feature in feature_test_info_table[],
+ * test if KVM_GET_ONE_REG/KVM_SET_ONE_REG works appropriately according
+ * to the feature configuration.  See test_feature's comment for more detail.
+ */
+static void test_feature_all(void)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(feature_test_info_table); i++)
+		test_feature(&feature_test_info_table[i]);
+}
+
+int set_id_reg(struct kvm_vm *vm, uint32_t vcpu, struct id_reg_test_info *sreg,
+	       uint64_t new_val)
+{
+	int ret;
+	uint64_t reg_val;
+	struct kvm_one_reg one_reg;
+
+	one_reg.id = KVM_ARM64_SYS_REG(sreg->id);
+	one_reg.addr = (uint64_t)&reg_val;
+
+	reg_val = new_val;
+	ret = _vcpu_ioctl(vm, vcpu, KVM_SET_ONE_REG, &one_reg);
+	if (!ret)
+		sreg->current_value = new_val;
+
+	return ret;
+}
+
+
+/*
+ * Create a new VM with one vCPU, set the ID register to @new_val.
+ */
+int set_id_reg_vm(struct id_reg_test_info *sreg, uint64_t new_val)
+{
+	struct kvm_vm *vm;
+	int ret;
+	uint32_t vcpu = 0;
+
+	reset_id_reg_info();
+
+	vm = test_vm_create(1, guest_code_id_reg_check_all, NULL, NULL);
+	ret = set_id_reg(vm, vcpu, sreg, new_val);
+	test_vm_free(vm);
+
+	return ret;
+}
+
+struct frac_info {
+	char	*name;
+	struct id_reg_test_info *sreg;
+	struct id_reg_test_info *frac_sreg;
+	int	shift;
+	int	frac_shift;
+};
+
+struct frac_info frac_info_table[] = {
+	{
+		.name = "RAS",
+		.sreg = ID_REG_INFO(ID_AA64PFR0),
+		.shift = ID_AA64PFR0_RAS_SHIFT,
+		.frac_sreg = ID_REG_INFO(ID_AA64PFR1),
+		.frac_shift = ID_AA64PFR1_RASFRAC_SHIFT,
+	},
+	{
+		.name = "MPAM",
+		.sreg = ID_REG_INFO(ID_AA64PFR0),
+		.shift = ID_AA64PFR0_MPAM_SHIFT,
+		.frac_sreg = ID_REG_INFO(ID_AA64PFR1),
+		.frac_shift = ID_AA64PFR1_MPAMFRAC_SHIFT,
+	},
+	{
+		.name = "CSV2",
+		.sreg = ID_REG_INFO(ID_AA64PFR0),
+		.shift = ID_AA64PFR0_CSV2_SHIFT,
+		.frac_sreg = ID_REG_INFO(ID_AA64PFR1),
+		.frac_shift = ID_AA64PFR1_CSV2FRAC_SHIFT,
+	},
+};
+
+
+/*
+ * Make sure that we can set the fractional reg field even before setting
+ * the feature reg field.
+ */
+int test_feature_frac_vm(struct frac_info *frac, uint64_t new_val,
+			 uint64_t frac_new_val)
+{
+	struct kvm_vm *vm;
+	uint32_t vcpu = 0;
+	struct id_reg_test_info *sreg, *frac_sreg;
+	int ret;
+
+	sreg = frac->sreg;
+	frac_sreg = frac->frac_sreg;
+	reset_id_reg_info();
+
+	reset_id_reg_info();
+
+	vm = test_vm_create(1, guest_code_id_reg_check_all, NULL, NULL);
+
+	/* Set fractional reg field */
+	ret = set_id_reg(vm, vcpu, frac_sreg, frac_new_val);
+	TEST_ASSERT(!ret, "SET_REG(%s=0x%lx) failed, ret=0x%x",
+		    frac_sreg->name, frac_new_val, ret);
+
+	/* Set feature reg field */
+	ret = set_id_reg(vm, vcpu, sreg, new_val);
+	TEST_ASSERT(!ret, "SET_REG(%s=0x%lx) failed, ret=0x%x",
+		    sreg->name, new_val, ret);
+
+	ret = TEST_RUN(vm, vcpu);
+	test_vm_free(vm);
+
+	return ret;
+}
+
+/*
+ * Test for setting the feature fractional field of the ID register.
+ * When the (main) feature field of the ID register is the same as the host's,
+ * the fractional field value cannot be larger than the host's.
+ * (KVM_SET_ONE_REG should work but KVM_RUN with the larger value will fail)
+ * When the (main) feature field of the ID register is smaler than the host's,
+ * the fractional field can be any values.
+ * The function tests those behaviors.
+ */
+void test_feature_frac_one(struct frac_info *frac)
+{
+	uint64_t ftr_val, ftr_fval, frac_val, frac_fval;
+	int ret, shift, frac_shift;
+	struct id_reg_test_info *sreg, *frac_sreg;
+
+	reset_id_reg_info();
+
+	sreg = frac->sreg;
+	shift = frac->shift;
+	frac_sreg = frac->frac_sreg;
+	frac_shift = frac->frac_shift;
+
+	pr_debug("%s(%s Frac) reg:%s(shift:%d) frac reg:%s(shift:%d)\n",
+		 __func__, frac->name, sreg->name, shift, frac_sreg->name,
+		 frac_shift);
+
+	/*
+	 * Use the host's feature value for the guest.
+	 * KVM_RUN with a larger frac value than the host's should fail.
+	 * Otherwise, it should work.
+	 */
+
+	frac_fval = GET_ID_UFIELD(frac_sreg->initial_value, frac_shift);
+	if (frac_fval > 0) {
+		/* Test with smaller frac value */
+		frac_val = UPDATE_ID_UFIELD(frac_sreg->initial_value,
+					    frac_shift, frac_fval - 1);
+		ret = test_feature_frac_vm(frac, sreg->initial_value, frac_val);
+		TEST_ASSERT(!ret, "Test smaller %s frac (val:%lx) failed(%d)",
+			    frac->name, frac_val, ret);
+	}
+
+	reset_id_reg_info();
+
+	if (frac_fval != 0xf) {
+		/* Test with larger frac value */
+		frac_val = UPDATE_ID_UFIELD(frac_sreg->initial_value,
+						frac_shift, frac_fval + 1);
+
+		/* Setting larger frac shouldn't fail at ioctl */
+		ret = set_id_reg_vm(frac_sreg, frac_val);
+		TEST_ASSERT(!ret,
+			"SET larger %s frac (%s org:%lx, val:%lx) failed(%d)",
+			frac->name, frac_sreg->name, frac_sreg->initial_value,
+			frac_val, ret);
+
+		/* KVM_RUN with larger frac should fail */
+		ret = test_feature_frac_vm(frac, sreg->initial_value, frac_val);
+		TEST_ASSERT(ret,
+			"Test with larger %s frac (%s org:%lx, val:%lx) worked",
+			frac->name, frac_sreg->name, frac_sreg->initial_value,
+			frac_val);
+	}
+
+	reset_id_reg_info();
+
+	/*
+	 * Test with a smaller (main) feature value than the host's.
+	 */
+	ftr_fval = GET_ID_UFIELD(sreg->initial_value, shift);
+	if (ftr_fval == 0)
+		/* Cannot set it to the smaller value */
+		return;
+
+	ftr_val = UPDATE_ID_UFIELD(sreg->initial_value, shift, ftr_fval - 1);
+	ret = test_feature_frac_vm(frac, ftr_val, frac_sreg->initial_value);
+	TEST_ASSERT(!ret, "Test with smaller %s (val:%lx) failed(%d)",
+		    frac->name, ftr_val, ret);
+
+	if (frac_fval > 0) {
+		/* Test with smaller frac value */
+		frac_val = UPDATE_ID_UFIELD(frac_sreg->initial_value,
+					    frac_shift, frac_fval - 1);
+		ret = test_feature_frac_vm(frac, ftr_val, frac_val);
+		TEST_ASSERT(!ret,
+		    "Test with smaller %s and frac (val:%lx) failed(%d)",
+		    frac->name, ftr_val, ret);
+	}
+
+	if (frac_fval != 0xf) {
+		/* Test with larger frac value */
+		frac_val = UPDATE_ID_UFIELD(frac_sreg->initial_value,
+					    frac_shift, frac_fval + 1);
+		ret = test_feature_frac_vm(frac, ftr_val, frac_val);
+		TEST_ASSERT(!ret,
+		    "Test with smaller %s and larger frac (val:%lx) failed(%d)",
+		    frac->name, ftr_val, ret);
+	}
+}
+
+/*
+ * Test for setting feature fractional fields of ID registers.
+ * See test_feature_frac_one's comments for more detail.
+ */
+void test_feature_frac_all(void)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(frac_info_table); i++)
+		test_feature_frac_one(&frac_info_table[i]);
+}
+
+void run_test(void)
+{
+	test_id_regs_basic();
+	test_feature_all();
+	test_feature_ptrauth();
+	test_feature_frac_all();
+}
+
+static void init_id_reg_info_one(struct id_reg_test_info *sreg, void *arg)
+{
+	struct kvm_one_reg one_reg;
+	uint64_t reg_val;
+	struct kvm_vm *vm = ((struct vm_vcpu_arg *)arg)->vm;
+	uint32_t vcpuid = ((struct vm_vcpu_arg *)arg)->vcpuid;
+	int ret;
+
+	one_reg.addr = (uint64_t)&reg_val;
+	one_reg.id = KVM_ARM64_SYS_REG(sreg->id);
+	vcpu_ioctl(vm, vcpuid, KVM_GET_ONE_REG, &one_reg);
+	sreg->current_value = reg_val;
+
+	/* Keep the initial value to reset the register value later */
+	sreg->initial_value = reg_val;
+
+	/* Check if the register can be set to 0 */
+	reg_val = 0;
+	ret = _vcpu_ioctl(vm, vcpuid, KVM_SET_ONE_REG, &one_reg);
+	if (!ret)
+		sreg->can_clear = true;
+
+	pr_debug("%s (0x%x): 0x%lx%s\n", sreg->name, sreg->id,
+		 sreg->initial_value, sreg->can_clear ? ", can clear" : "");
+}
+
+/*
+ * Check if aarch32 is supported, and initialize id_reg_test_info for all
+ * the ID registers.  Loop over the idreg list and populates each id_reg
+ * info with the initial value, current value, and can_clear value.
+ */
+static void init_test_info(void)
+{
+	uint64_t reg_val;
+	int fval;
+	struct kvm_vm *vm;
+	struct kvm_one_reg one_reg;
+	struct vm_vcpu_arg arg = { .vcpuid = 0 };
+
+	vm = test_vm_create(1, guest_code_do_nothing, NULL, NULL);
+
+	/* Get ID_AA64PFR0_EL1 to check if AArch32 is supported */
+	one_reg.addr = (uint64_t)&reg_val;
+	one_reg.id = KVM_ARM64_SYS_REG(SYS_ID_AA64PFR0_EL1);
+	vcpu_ioctl(vm, 0, KVM_GET_ONE_REG, &one_reg);
+	fval = GET_ID_UFIELD(reg_val, ID_AA64PFR0_EL0_SHIFT);
+	if (fval == 0x1)
+		/* No AArch32 support */
+		aarch32_support = false;
+
+	/* Initialize id_reg_test_info */
+	arg.vm = vm;
+	walk_id_reg_list(init_id_reg_info_one, &arg);
+	test_vm_free(vm);
+}
+
+int main(void)
+{
+
+	setbuf(stdout, NULL);
+
+	if (kvm_check_cap(KVM_CAP_ARM_ID_REG_CONFIGURABLE) <= 0) {
+		print_skip("KVM_CAP_ARM_ID_REG_CONFIGURABLE is not supported\n");
+		exit(KSFT_SKIP);
+	}
+
+	init_test_info();
+	run_test();
+	return 0;
+}
-- 
2.34.1.448.ga2b2bfdf31-goog


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 01/26] KVM: arm64: Introduce a validation function for an ID register
  2022-01-06  4:26   ` Reiji Watanabe
  (?)
@ 2022-01-07  7:12     ` Reiji Watanabe
  -1 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-07  7:12 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, Linux ARM, James Morse, Alexandru Elisei, Suzuki K Poulose,
	Paolo Bonzini, Will Deacon, Andrew Jones, Peng Liang,
	Peter Shier, Ricardo Koller, Oliver Upton, Jing Zhang,
	Raghavendra Rao Anata

On Wed, Jan 5, 2022 at 8:28 PM Reiji Watanabe <reijiw@google.com> wrote:
>
> Introduce arm64_check_features(), which does a basic validity checking
> of an ID register value against the register's limit value, which is
> generally the host's sanitized value.
>
> This function will be used by the following patches to check if an ID
> register value that userspace tries to set for a guest can be supported
> on the host.
>
> The validation is done using arm64_ftr_bits_kvm, which is created from
> arm64_ftr_regs, with some entries overwritten by entries from
> arm64_ftr_bits_kvm_override.
>
> Signed-off-by: Reiji Watanabe <reijiw@google.com>
> ---
>  arch/arm64/include/asm/cpufeature.h |   1 +
>  arch/arm64/kernel/cpufeature.c      | 228 ++++++++++++++++++++++++++++
>  2 files changed, 229 insertions(+)
>
> diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
> index ef6be92b1921..eda7ddbed8cf 100644
> --- a/arch/arm64/include/asm/cpufeature.h
> +++ b/arch/arm64/include/asm/cpufeature.h
> @@ -631,6 +631,7 @@ void check_local_cpu_capabilities(void);
>
>  u64 read_sanitised_ftr_reg(u32 id);
>  u64 __read_sysreg_by_encoding(u32 sys_id);
> +int arm64_check_features(u32 sys_reg, u64 val, u64 limit);
>
>  static inline bool cpu_supports_mixed_endian_el0(void)
>  {
> diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
> index 6f3e677d88f1..48dff8b101d9 100644
> --- a/arch/arm64/kernel/cpufeature.c
> +++ b/arch/arm64/kernel/cpufeature.c
> @@ -3140,3 +3140,231 @@ ssize_t cpu_show_meltdown(struct device *dev, struct device_attribute *attr,
>                 return sprintf(buf, "Vulnerable\n");
>         }
>  }
> +
> +#ifdef CONFIG_KVM
> +/*
> + * arm64_ftr_bits_kvm[] is used for KVM to check if features that are
> + * indicated in an ID register value for the guest are available on the host.
> + * arm64_ftr_bits_kvm[] is created based on arm64_ftr_regs[].  But, for
> + * registers for which arm64_ftr_bits_kvm_override[] has a corresponding
> + * entry, replace arm64_ftr_bits entries in arm64_ftr_bits_kvm[] with the
> + * ones in arm64_ftr_bits_kvm_override[].
> + */
> +static struct __ftr_reg_bits_entry *arm64_ftr_bits_kvm;
> +static size_t arm64_ftr_bits_kvm_nentries;
> +static DEFINE_MUTEX(arm64_ftr_bits_kvm_lock);
> +
> +/*
> + * Number of arm64_ftr_bits entries for each register.
> + * (Number of 4 bits fields in 64 bit register + 1 entry for ARM64_FTR_END)
> + */
> +#define        MAX_FTR_BITS_LEN        17
> +
> +/* Use FTR_LOWER_SAFE for AA64DFR0_EL1.PMUVER and AA64DFR0_EL1.DEBUGVER. */
> +static struct arm64_ftr_bits ftr_id_aa64dfr0_kvm[MAX_FTR_BITS_LEN] = {
> +       S_ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64DFR0_PMUVER_SHIFT, 4, 0),
> +       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x6),
> +       ARM64_FTR_END,
> +};
> +
> +#define        ARM64_FTR_REG_BITS(id, table)   {       \
> +       .sys_id = id,                           \
> +       .ftr_bits = &((table)[0]),              \
> +}
> +
> +struct __ftr_reg_bits_entry {
> +       u32     sys_id;
> +       struct arm64_ftr_bits   *ftr_bits;
> +};
> +
> +/*
> + * All entries in arm64_ftr_bits_kvm_override[] are used to override
> + * the corresponding entries in arm64_ftr_bits_kvm[].
> + */
> +static struct __ftr_reg_bits_entry arm64_ftr_bits_kvm_override[] = {
> +       ARM64_FTR_REG_BITS(SYS_ID_AA64DFR0_EL1, ftr_id_aa64dfr0_kvm),
> +};
> +
> +/*
> + * Override entries in @orig_ftrp with the ones in @new_ftrp when their shift
> + * fields match.  The last entry of @orig_ftrp and @new_ftrp must be
> + * ARM64_FTR_END (.width == 0).
> + */
> +static void arm64_ftr_reg_bits_overrite(struct arm64_ftr_bits *orig_ftrp,
> +                                       struct arm64_ftr_bits *new_ftrp)
> +{
> +       struct arm64_ftr_bits *o_ftrp, *n_ftrp;
> +
> +       for (n_ftrp = new_ftrp; n_ftrp->width; n_ftrp++) {
> +               for (o_ftrp = orig_ftrp; o_ftrp->width; o_ftrp++) {
> +                       if (o_ftrp->shift == n_ftrp->shift) {
> +                               *o_ftrp = *n_ftrp;
> +                               break;
> +                       }
> +               }
> +       }
> +}
> +
> +/*
> + * Copy arm64_ftr_bits entries from @src_ftrp to @dst_ftrp.  The last entries
> + * of @dst_ftrp and @src_ftrp must be ARM64_FTR_END (.width == 0).
> + */
> +static void copy_arm64_ftr_bits(struct arm64_ftr_bits *dst_ftrp,
> +                               const struct arm64_ftr_bits *src_ftrp)
> +{
> +       int i = 0;
> +
> +       for (; src_ftrp[i].width; i++) {
> +               if (WARN_ON_ONCE(i >= (MAX_FTR_BITS_LEN - 1)))
> +                       break;
> +
> +               dst_ftrp[i] = src_ftrp[i];
> +       }
> +
> +       dst_ftrp[i].width = 0;
> +}
> +
> +/*
> + * Initialize arm64_ftr_bits_kvm.  Copy arm64_ftr_bits for each ID register
> + * from arm64_ftr_regs to arm64_ftr_bits_kvm, and then override entries in
> + * arm64_ftr_bits_kvm with ones in arm64_ftr_bits_kvm_override.
> + */
> +static int init_arm64_ftr_bits_kvm(void)
> +{
> +       struct arm64_ftr_bits ftr_temp[MAX_FTR_BITS_LEN];
> +       static struct __ftr_reg_bits_entry *reg_bits_array, *bits, *o_bits;
> +       int i, j, nent, ret;
> +
> +       mutex_lock(&arm64_ftr_bits_kvm_lock);
> +       if (arm64_ftr_bits_kvm) {
> +               /* Already initialized */
> +               ret = 0;
> +               goto unlock_exit;
> +       }
> +
> +       nent = ARRAY_SIZE(arm64_ftr_regs);
> +       reg_bits_array = kcalloc(nent, sizeof(struct __ftr_reg_bits_entry),
> +                                GFP_KERNEL);
> +       if (!reg_bits_array) {
> +               ret = ENOMEM;
> +               goto unlock_exit;
> +       }
> +
> +       /* Copy entries from arm64_ftr_regs to reg_bits_array */
> +       for (i = 0; i < nent; i++) {
> +               bits = &reg_bits_array[i];
> +               bits->sys_id = arm64_ftr_regs[i].sys_id;
> +               bits->ftr_bits = (struct arm64_ftr_bits *)arm64_ftr_regs[i].reg->ftr_bits;
> +       };
> +
> +       /*
> +        * Override the entries in reg_bits_array with the ones in
> +        * arm64_ftr_bits_kvm_override.
> +        */
> +       for (i = 0; i < ARRAY_SIZE(arm64_ftr_bits_kvm_override); i++) {
> +               o_bits = &arm64_ftr_bits_kvm_override[i];
> +               for (j = 0; j < nent; j++) {
> +                       bits = &reg_bits_array[j];
> +                       if (bits->sys_id != o_bits->sys_id)
> +                               continue;
> +
> +                       memset(ftr_temp, 0, sizeof(ftr_temp));
> +
> +                       /*
> +                        * Temporary save all entries in o_bits->ftr_bits
> +                        * to ftr_temp.
> +                        */
> +                       copy_arm64_ftr_bits(ftr_temp, o_bits->ftr_bits);
> +
> +                       /*
> +                        * Copy entries from bits->ftr_bits to o_bits->ftr_bits.
> +                        */
> +                       copy_arm64_ftr_bits(o_bits->ftr_bits, bits->ftr_bits);
> +
> +                       /*
> +                        * Override entries in o_bits->ftr_bits with the
> +                        * saved ones, and update bits->ftr_bits with
> +                        * o_bits->ftr_bits.
> +                        */
> +                       arm64_ftr_reg_bits_overrite(o_bits->ftr_bits, ftr_temp);
> +                       bits->ftr_bits = o_bits->ftr_bits;
> +                       break;
> +               }
> +       }
> +
> +       arm64_ftr_bits_kvm_nentries = nent;
> +       arm64_ftr_bits_kvm = reg_bits_array;

I've just noticed that the patch has a problem in terms of memory ordering.
I'm thinking of fixing the code above as follows in the v5 patch.
---
        <...>
        arm64_ftr_bits_kvm_nentries = nent;

        /*
         * Make sure any data written earlier in this function are visible
         * from other CPUs before setting arm64_ftr_bits_kvm.
         */
        smp_wmb();

        WRITE_ONCE(arm64_ftr_bits_kvm, reg_bits_array);
        <...>
---

Also, I will fix the reader side code of those data in
get_arm64_ftr_bits_kvm().

Thanks,
Reiji


> +       ret = 0;
> +
> +unlock_exit:
> +       mutex_unlock(&arm64_ftr_bits_kvm_lock);
> +       return ret;
> +}
> +
> +static int search_cmp_ftr_reg_bits(const void *id, const void *regp)
> +{
> +       return ((int)(unsigned long)id -
> +               (int)((const struct __ftr_reg_bits_entry *)regp)->sys_id);
> +}
> +
> +static const struct arm64_ftr_bits *get_arm64_ftr_bits_kvm(u32 sys_id)
> +{
> +       const struct __ftr_reg_bits_entry *ret;
> +       int err;
> +
> +       if (!arm64_ftr_bits_kvm) {
> +               /* arm64_ftr_bits_kvm is not initialized yet. */
> +               err = init_arm64_ftr_bits_kvm();
> +               if (err)
> +                       return NULL;
> +       }
> +
> +       ret = bsearch((const void *)(unsigned long)sys_id,
> +                     arm64_ftr_bits_kvm,
> +                     arm64_ftr_bits_kvm_nentries,
> +                     sizeof(arm64_ftr_bits_kvm[0]),
> +                     search_cmp_ftr_reg_bits);
> +       if (ret)
> +               return ret->ftr_bits;
> +
> +       return NULL;
> +}
> +
> +/*
> + * Check if features (or levels of features) that are indicated in the ID
> + * register value @val are also indicated in @limit.
> + * This function is for KVM to check if features that are indicated in @val,
> + * which will be used as the ID register value for its guest, are supported
> + * on the host.
> + * For AA64MMFR0_EL1.TGranX_2 fields, which don't follow the standard ID
> + * scheme, the function checks if values of the fields in @val are the same
> + * as the ones in @limit.
> + */
> +int arm64_check_features(u32 sys_reg, u64 val, u64 limit)
> +{
> +       const struct arm64_ftr_bits *ftrp = get_arm64_ftr_bits_kvm(sys_reg);
> +       u64 exposed_mask = 0;
> +
> +       if (!ftrp)
> +               return -ENOENT;
> +
> +       for (; ftrp->width; ftrp++) {
> +               s64 ftr_val = arm64_ftr_value(ftrp, val);
> +               s64 ftr_lim = arm64_ftr_value(ftrp, limit);
> +
> +               exposed_mask |= arm64_ftr_mask(ftrp);
> +
> +               if (ftr_val == ftr_lim)
> +                       continue;
> +
> +               if (ftr_val != arm64_ftr_safe_value(ftrp, ftr_val, ftr_lim))
> +                       return -E2BIG;
> +       }
> +
> +       /* Make sure that no unrecognized fields are set in @val. */
> +       if (val & ~exposed_mask)
> +               return -E2BIG;
> +
> +       return 0;
> +}
> +#endif /* CONFIG_KVM */
> --
> 2.34.1.448.ga2b2bfdf31-goog
>

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 01/26] KVM: arm64: Introduce a validation function for an ID register
@ 2022-01-07  7:12     ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-07  7:12 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, Will Deacon, Peter Shier, Paolo Bonzini, Linux ARM

On Wed, Jan 5, 2022 at 8:28 PM Reiji Watanabe <reijiw@google.com> wrote:
>
> Introduce arm64_check_features(), which does a basic validity checking
> of an ID register value against the register's limit value, which is
> generally the host's sanitized value.
>
> This function will be used by the following patches to check if an ID
> register value that userspace tries to set for a guest can be supported
> on the host.
>
> The validation is done using arm64_ftr_bits_kvm, which is created from
> arm64_ftr_regs, with some entries overwritten by entries from
> arm64_ftr_bits_kvm_override.
>
> Signed-off-by: Reiji Watanabe <reijiw@google.com>
> ---
>  arch/arm64/include/asm/cpufeature.h |   1 +
>  arch/arm64/kernel/cpufeature.c      | 228 ++++++++++++++++++++++++++++
>  2 files changed, 229 insertions(+)
>
> diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
> index ef6be92b1921..eda7ddbed8cf 100644
> --- a/arch/arm64/include/asm/cpufeature.h
> +++ b/arch/arm64/include/asm/cpufeature.h
> @@ -631,6 +631,7 @@ void check_local_cpu_capabilities(void);
>
>  u64 read_sanitised_ftr_reg(u32 id);
>  u64 __read_sysreg_by_encoding(u32 sys_id);
> +int arm64_check_features(u32 sys_reg, u64 val, u64 limit);
>
>  static inline bool cpu_supports_mixed_endian_el0(void)
>  {
> diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
> index 6f3e677d88f1..48dff8b101d9 100644
> --- a/arch/arm64/kernel/cpufeature.c
> +++ b/arch/arm64/kernel/cpufeature.c
> @@ -3140,3 +3140,231 @@ ssize_t cpu_show_meltdown(struct device *dev, struct device_attribute *attr,
>                 return sprintf(buf, "Vulnerable\n");
>         }
>  }
> +
> +#ifdef CONFIG_KVM
> +/*
> + * arm64_ftr_bits_kvm[] is used for KVM to check if features that are
> + * indicated in an ID register value for the guest are available on the host.
> + * arm64_ftr_bits_kvm[] is created based on arm64_ftr_regs[].  But, for
> + * registers for which arm64_ftr_bits_kvm_override[] has a corresponding
> + * entry, replace arm64_ftr_bits entries in arm64_ftr_bits_kvm[] with the
> + * ones in arm64_ftr_bits_kvm_override[].
> + */
> +static struct __ftr_reg_bits_entry *arm64_ftr_bits_kvm;
> +static size_t arm64_ftr_bits_kvm_nentries;
> +static DEFINE_MUTEX(arm64_ftr_bits_kvm_lock);
> +
> +/*
> + * Number of arm64_ftr_bits entries for each register.
> + * (Number of 4 bits fields in 64 bit register + 1 entry for ARM64_FTR_END)
> + */
> +#define        MAX_FTR_BITS_LEN        17
> +
> +/* Use FTR_LOWER_SAFE for AA64DFR0_EL1.PMUVER and AA64DFR0_EL1.DEBUGVER. */
> +static struct arm64_ftr_bits ftr_id_aa64dfr0_kvm[MAX_FTR_BITS_LEN] = {
> +       S_ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64DFR0_PMUVER_SHIFT, 4, 0),
> +       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x6),
> +       ARM64_FTR_END,
> +};
> +
> +#define        ARM64_FTR_REG_BITS(id, table)   {       \
> +       .sys_id = id,                           \
> +       .ftr_bits = &((table)[0]),              \
> +}
> +
> +struct __ftr_reg_bits_entry {
> +       u32     sys_id;
> +       struct arm64_ftr_bits   *ftr_bits;
> +};
> +
> +/*
> + * All entries in arm64_ftr_bits_kvm_override[] are used to override
> + * the corresponding entries in arm64_ftr_bits_kvm[].
> + */
> +static struct __ftr_reg_bits_entry arm64_ftr_bits_kvm_override[] = {
> +       ARM64_FTR_REG_BITS(SYS_ID_AA64DFR0_EL1, ftr_id_aa64dfr0_kvm),
> +};
> +
> +/*
> + * Override entries in @orig_ftrp with the ones in @new_ftrp when their shift
> + * fields match.  The last entry of @orig_ftrp and @new_ftrp must be
> + * ARM64_FTR_END (.width == 0).
> + */
> +static void arm64_ftr_reg_bits_overrite(struct arm64_ftr_bits *orig_ftrp,
> +                                       struct arm64_ftr_bits *new_ftrp)
> +{
> +       struct arm64_ftr_bits *o_ftrp, *n_ftrp;
> +
> +       for (n_ftrp = new_ftrp; n_ftrp->width; n_ftrp++) {
> +               for (o_ftrp = orig_ftrp; o_ftrp->width; o_ftrp++) {
> +                       if (o_ftrp->shift == n_ftrp->shift) {
> +                               *o_ftrp = *n_ftrp;
> +                               break;
> +                       }
> +               }
> +       }
> +}
> +
> +/*
> + * Copy arm64_ftr_bits entries from @src_ftrp to @dst_ftrp.  The last entries
> + * of @dst_ftrp and @src_ftrp must be ARM64_FTR_END (.width == 0).
> + */
> +static void copy_arm64_ftr_bits(struct arm64_ftr_bits *dst_ftrp,
> +                               const struct arm64_ftr_bits *src_ftrp)
> +{
> +       int i = 0;
> +
> +       for (; src_ftrp[i].width; i++) {
> +               if (WARN_ON_ONCE(i >= (MAX_FTR_BITS_LEN - 1)))
> +                       break;
> +
> +               dst_ftrp[i] = src_ftrp[i];
> +       }
> +
> +       dst_ftrp[i].width = 0;
> +}
> +
> +/*
> + * Initialize arm64_ftr_bits_kvm.  Copy arm64_ftr_bits for each ID register
> + * from arm64_ftr_regs to arm64_ftr_bits_kvm, and then override entries in
> + * arm64_ftr_bits_kvm with ones in arm64_ftr_bits_kvm_override.
> + */
> +static int init_arm64_ftr_bits_kvm(void)
> +{
> +       struct arm64_ftr_bits ftr_temp[MAX_FTR_BITS_LEN];
> +       static struct __ftr_reg_bits_entry *reg_bits_array, *bits, *o_bits;
> +       int i, j, nent, ret;
> +
> +       mutex_lock(&arm64_ftr_bits_kvm_lock);
> +       if (arm64_ftr_bits_kvm) {
> +               /* Already initialized */
> +               ret = 0;
> +               goto unlock_exit;
> +       }
> +
> +       nent = ARRAY_SIZE(arm64_ftr_regs);
> +       reg_bits_array = kcalloc(nent, sizeof(struct __ftr_reg_bits_entry),
> +                                GFP_KERNEL);
> +       if (!reg_bits_array) {
> +               ret = ENOMEM;
> +               goto unlock_exit;
> +       }
> +
> +       /* Copy entries from arm64_ftr_regs to reg_bits_array */
> +       for (i = 0; i < nent; i++) {
> +               bits = &reg_bits_array[i];
> +               bits->sys_id = arm64_ftr_regs[i].sys_id;
> +               bits->ftr_bits = (struct arm64_ftr_bits *)arm64_ftr_regs[i].reg->ftr_bits;
> +       };
> +
> +       /*
> +        * Override the entries in reg_bits_array with the ones in
> +        * arm64_ftr_bits_kvm_override.
> +        */
> +       for (i = 0; i < ARRAY_SIZE(arm64_ftr_bits_kvm_override); i++) {
> +               o_bits = &arm64_ftr_bits_kvm_override[i];
> +               for (j = 0; j < nent; j++) {
> +                       bits = &reg_bits_array[j];
> +                       if (bits->sys_id != o_bits->sys_id)
> +                               continue;
> +
> +                       memset(ftr_temp, 0, sizeof(ftr_temp));
> +
> +                       /*
> +                        * Temporary save all entries in o_bits->ftr_bits
> +                        * to ftr_temp.
> +                        */
> +                       copy_arm64_ftr_bits(ftr_temp, o_bits->ftr_bits);
> +
> +                       /*
> +                        * Copy entries from bits->ftr_bits to o_bits->ftr_bits.
> +                        */
> +                       copy_arm64_ftr_bits(o_bits->ftr_bits, bits->ftr_bits);
> +
> +                       /*
> +                        * Override entries in o_bits->ftr_bits with the
> +                        * saved ones, and update bits->ftr_bits with
> +                        * o_bits->ftr_bits.
> +                        */
> +                       arm64_ftr_reg_bits_overrite(o_bits->ftr_bits, ftr_temp);
> +                       bits->ftr_bits = o_bits->ftr_bits;
> +                       break;
> +               }
> +       }
> +
> +       arm64_ftr_bits_kvm_nentries = nent;
> +       arm64_ftr_bits_kvm = reg_bits_array;

I've just noticed that the patch has a problem in terms of memory ordering.
I'm thinking of fixing the code above as follows in the v5 patch.
---
        <...>
        arm64_ftr_bits_kvm_nentries = nent;

        /*
         * Make sure any data written earlier in this function are visible
         * from other CPUs before setting arm64_ftr_bits_kvm.
         */
        smp_wmb();

        WRITE_ONCE(arm64_ftr_bits_kvm, reg_bits_array);
        <...>
---

Also, I will fix the reader side code of those data in
get_arm64_ftr_bits_kvm().

Thanks,
Reiji


> +       ret = 0;
> +
> +unlock_exit:
> +       mutex_unlock(&arm64_ftr_bits_kvm_lock);
> +       return ret;
> +}
> +
> +static int search_cmp_ftr_reg_bits(const void *id, const void *regp)
> +{
> +       return ((int)(unsigned long)id -
> +               (int)((const struct __ftr_reg_bits_entry *)regp)->sys_id);
> +}
> +
> +static const struct arm64_ftr_bits *get_arm64_ftr_bits_kvm(u32 sys_id)
> +{
> +       const struct __ftr_reg_bits_entry *ret;
> +       int err;
> +
> +       if (!arm64_ftr_bits_kvm) {
> +               /* arm64_ftr_bits_kvm is not initialized yet. */
> +               err = init_arm64_ftr_bits_kvm();
> +               if (err)
> +                       return NULL;
> +       }
> +
> +       ret = bsearch((const void *)(unsigned long)sys_id,
> +                     arm64_ftr_bits_kvm,
> +                     arm64_ftr_bits_kvm_nentries,
> +                     sizeof(arm64_ftr_bits_kvm[0]),
> +                     search_cmp_ftr_reg_bits);
> +       if (ret)
> +               return ret->ftr_bits;
> +
> +       return NULL;
> +}
> +
> +/*
> + * Check if features (or levels of features) that are indicated in the ID
> + * register value @val are also indicated in @limit.
> + * This function is for KVM to check if features that are indicated in @val,
> + * which will be used as the ID register value for its guest, are supported
> + * on the host.
> + * For AA64MMFR0_EL1.TGranX_2 fields, which don't follow the standard ID
> + * scheme, the function checks if values of the fields in @val are the same
> + * as the ones in @limit.
> + */
> +int arm64_check_features(u32 sys_reg, u64 val, u64 limit)
> +{
> +       const struct arm64_ftr_bits *ftrp = get_arm64_ftr_bits_kvm(sys_reg);
> +       u64 exposed_mask = 0;
> +
> +       if (!ftrp)
> +               return -ENOENT;
> +
> +       for (; ftrp->width; ftrp++) {
> +               s64 ftr_val = arm64_ftr_value(ftrp, val);
> +               s64 ftr_lim = arm64_ftr_value(ftrp, limit);
> +
> +               exposed_mask |= arm64_ftr_mask(ftrp);
> +
> +               if (ftr_val == ftr_lim)
> +                       continue;
> +
> +               if (ftr_val != arm64_ftr_safe_value(ftrp, ftr_val, ftr_lim))
> +                       return -E2BIG;
> +       }
> +
> +       /* Make sure that no unrecognized fields are set in @val. */
> +       if (val & ~exposed_mask)
> +               return -E2BIG;
> +
> +       return 0;
> +}
> +#endif /* CONFIG_KVM */
> --
> 2.34.1.448.ga2b2bfdf31-goog
>
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 01/26] KVM: arm64: Introduce a validation function for an ID register
@ 2022-01-07  7:12     ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-07  7:12 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, Linux ARM, James Morse, Alexandru Elisei, Suzuki K Poulose,
	Paolo Bonzini, Will Deacon, Andrew Jones, Peng Liang,
	Peter Shier, Ricardo Koller, Oliver Upton, Jing Zhang,
	Raghavendra Rao Anata

On Wed, Jan 5, 2022 at 8:28 PM Reiji Watanabe <reijiw@google.com> wrote:
>
> Introduce arm64_check_features(), which does a basic validity checking
> of an ID register value against the register's limit value, which is
> generally the host's sanitized value.
>
> This function will be used by the following patches to check if an ID
> register value that userspace tries to set for a guest can be supported
> on the host.
>
> The validation is done using arm64_ftr_bits_kvm, which is created from
> arm64_ftr_regs, with some entries overwritten by entries from
> arm64_ftr_bits_kvm_override.
>
> Signed-off-by: Reiji Watanabe <reijiw@google.com>
> ---
>  arch/arm64/include/asm/cpufeature.h |   1 +
>  arch/arm64/kernel/cpufeature.c      | 228 ++++++++++++++++++++++++++++
>  2 files changed, 229 insertions(+)
>
> diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
> index ef6be92b1921..eda7ddbed8cf 100644
> --- a/arch/arm64/include/asm/cpufeature.h
> +++ b/arch/arm64/include/asm/cpufeature.h
> @@ -631,6 +631,7 @@ void check_local_cpu_capabilities(void);
>
>  u64 read_sanitised_ftr_reg(u32 id);
>  u64 __read_sysreg_by_encoding(u32 sys_id);
> +int arm64_check_features(u32 sys_reg, u64 val, u64 limit);
>
>  static inline bool cpu_supports_mixed_endian_el0(void)
>  {
> diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
> index 6f3e677d88f1..48dff8b101d9 100644
> --- a/arch/arm64/kernel/cpufeature.c
> +++ b/arch/arm64/kernel/cpufeature.c
> @@ -3140,3 +3140,231 @@ ssize_t cpu_show_meltdown(struct device *dev, struct device_attribute *attr,
>                 return sprintf(buf, "Vulnerable\n");
>         }
>  }
> +
> +#ifdef CONFIG_KVM
> +/*
> + * arm64_ftr_bits_kvm[] is used for KVM to check if features that are
> + * indicated in an ID register value for the guest are available on the host.
> + * arm64_ftr_bits_kvm[] is created based on arm64_ftr_regs[].  But, for
> + * registers for which arm64_ftr_bits_kvm_override[] has a corresponding
> + * entry, replace arm64_ftr_bits entries in arm64_ftr_bits_kvm[] with the
> + * ones in arm64_ftr_bits_kvm_override[].
> + */
> +static struct __ftr_reg_bits_entry *arm64_ftr_bits_kvm;
> +static size_t arm64_ftr_bits_kvm_nentries;
> +static DEFINE_MUTEX(arm64_ftr_bits_kvm_lock);
> +
> +/*
> + * Number of arm64_ftr_bits entries for each register.
> + * (Number of 4 bits fields in 64 bit register + 1 entry for ARM64_FTR_END)
> + */
> +#define        MAX_FTR_BITS_LEN        17
> +
> +/* Use FTR_LOWER_SAFE for AA64DFR0_EL1.PMUVER and AA64DFR0_EL1.DEBUGVER. */
> +static struct arm64_ftr_bits ftr_id_aa64dfr0_kvm[MAX_FTR_BITS_LEN] = {
> +       S_ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64DFR0_PMUVER_SHIFT, 4, 0),
> +       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x6),
> +       ARM64_FTR_END,
> +};
> +
> +#define        ARM64_FTR_REG_BITS(id, table)   {       \
> +       .sys_id = id,                           \
> +       .ftr_bits = &((table)[0]),              \
> +}
> +
> +struct __ftr_reg_bits_entry {
> +       u32     sys_id;
> +       struct arm64_ftr_bits   *ftr_bits;
> +};
> +
> +/*
> + * All entries in arm64_ftr_bits_kvm_override[] are used to override
> + * the corresponding entries in arm64_ftr_bits_kvm[].
> + */
> +static struct __ftr_reg_bits_entry arm64_ftr_bits_kvm_override[] = {
> +       ARM64_FTR_REG_BITS(SYS_ID_AA64DFR0_EL1, ftr_id_aa64dfr0_kvm),
> +};
> +
> +/*
> + * Override entries in @orig_ftrp with the ones in @new_ftrp when their shift
> + * fields match.  The last entry of @orig_ftrp and @new_ftrp must be
> + * ARM64_FTR_END (.width == 0).
> + */
> +static void arm64_ftr_reg_bits_overrite(struct arm64_ftr_bits *orig_ftrp,
> +                                       struct arm64_ftr_bits *new_ftrp)
> +{
> +       struct arm64_ftr_bits *o_ftrp, *n_ftrp;
> +
> +       for (n_ftrp = new_ftrp; n_ftrp->width; n_ftrp++) {
> +               for (o_ftrp = orig_ftrp; o_ftrp->width; o_ftrp++) {
> +                       if (o_ftrp->shift == n_ftrp->shift) {
> +                               *o_ftrp = *n_ftrp;
> +                               break;
> +                       }
> +               }
> +       }
> +}
> +
> +/*
> + * Copy arm64_ftr_bits entries from @src_ftrp to @dst_ftrp.  The last entries
> + * of @dst_ftrp and @src_ftrp must be ARM64_FTR_END (.width == 0).
> + */
> +static void copy_arm64_ftr_bits(struct arm64_ftr_bits *dst_ftrp,
> +                               const struct arm64_ftr_bits *src_ftrp)
> +{
> +       int i = 0;
> +
> +       for (; src_ftrp[i].width; i++) {
> +               if (WARN_ON_ONCE(i >= (MAX_FTR_BITS_LEN - 1)))
> +                       break;
> +
> +               dst_ftrp[i] = src_ftrp[i];
> +       }
> +
> +       dst_ftrp[i].width = 0;
> +}
> +
> +/*
> + * Initialize arm64_ftr_bits_kvm.  Copy arm64_ftr_bits for each ID register
> + * from arm64_ftr_regs to arm64_ftr_bits_kvm, and then override entries in
> + * arm64_ftr_bits_kvm with ones in arm64_ftr_bits_kvm_override.
> + */
> +static int init_arm64_ftr_bits_kvm(void)
> +{
> +       struct arm64_ftr_bits ftr_temp[MAX_FTR_BITS_LEN];
> +       static struct __ftr_reg_bits_entry *reg_bits_array, *bits, *o_bits;
> +       int i, j, nent, ret;
> +
> +       mutex_lock(&arm64_ftr_bits_kvm_lock);
> +       if (arm64_ftr_bits_kvm) {
> +               /* Already initialized */
> +               ret = 0;
> +               goto unlock_exit;
> +       }
> +
> +       nent = ARRAY_SIZE(arm64_ftr_regs);
> +       reg_bits_array = kcalloc(nent, sizeof(struct __ftr_reg_bits_entry),
> +                                GFP_KERNEL);
> +       if (!reg_bits_array) {
> +               ret = ENOMEM;
> +               goto unlock_exit;
> +       }
> +
> +       /* Copy entries from arm64_ftr_regs to reg_bits_array */
> +       for (i = 0; i < nent; i++) {
> +               bits = &reg_bits_array[i];
> +               bits->sys_id = arm64_ftr_regs[i].sys_id;
> +               bits->ftr_bits = (struct arm64_ftr_bits *)arm64_ftr_regs[i].reg->ftr_bits;
> +       };
> +
> +       /*
> +        * Override the entries in reg_bits_array with the ones in
> +        * arm64_ftr_bits_kvm_override.
> +        */
> +       for (i = 0; i < ARRAY_SIZE(arm64_ftr_bits_kvm_override); i++) {
> +               o_bits = &arm64_ftr_bits_kvm_override[i];
> +               for (j = 0; j < nent; j++) {
> +                       bits = &reg_bits_array[j];
> +                       if (bits->sys_id != o_bits->sys_id)
> +                               continue;
> +
> +                       memset(ftr_temp, 0, sizeof(ftr_temp));
> +
> +                       /*
> +                        * Temporary save all entries in o_bits->ftr_bits
> +                        * to ftr_temp.
> +                        */
> +                       copy_arm64_ftr_bits(ftr_temp, o_bits->ftr_bits);
> +
> +                       /*
> +                        * Copy entries from bits->ftr_bits to o_bits->ftr_bits.
> +                        */
> +                       copy_arm64_ftr_bits(o_bits->ftr_bits, bits->ftr_bits);
> +
> +                       /*
> +                        * Override entries in o_bits->ftr_bits with the
> +                        * saved ones, and update bits->ftr_bits with
> +                        * o_bits->ftr_bits.
> +                        */
> +                       arm64_ftr_reg_bits_overrite(o_bits->ftr_bits, ftr_temp);
> +                       bits->ftr_bits = o_bits->ftr_bits;
> +                       break;
> +               }
> +       }
> +
> +       arm64_ftr_bits_kvm_nentries = nent;
> +       arm64_ftr_bits_kvm = reg_bits_array;

I've just noticed that the patch has a problem in terms of memory ordering.
I'm thinking of fixing the code above as follows in the v5 patch.
---
        <...>
        arm64_ftr_bits_kvm_nentries = nent;

        /*
         * Make sure any data written earlier in this function are visible
         * from other CPUs before setting arm64_ftr_bits_kvm.
         */
        smp_wmb();

        WRITE_ONCE(arm64_ftr_bits_kvm, reg_bits_array);
        <...>
---

Also, I will fix the reader side code of those data in
get_arm64_ftr_bits_kvm().

Thanks,
Reiji


> +       ret = 0;
> +
> +unlock_exit:
> +       mutex_unlock(&arm64_ftr_bits_kvm_lock);
> +       return ret;
> +}
> +
> +static int search_cmp_ftr_reg_bits(const void *id, const void *regp)
> +{
> +       return ((int)(unsigned long)id -
> +               (int)((const struct __ftr_reg_bits_entry *)regp)->sys_id);
> +}
> +
> +static const struct arm64_ftr_bits *get_arm64_ftr_bits_kvm(u32 sys_id)
> +{
> +       const struct __ftr_reg_bits_entry *ret;
> +       int err;
> +
> +       if (!arm64_ftr_bits_kvm) {
> +               /* arm64_ftr_bits_kvm is not initialized yet. */
> +               err = init_arm64_ftr_bits_kvm();
> +               if (err)
> +                       return NULL;
> +       }
> +
> +       ret = bsearch((const void *)(unsigned long)sys_id,
> +                     arm64_ftr_bits_kvm,
> +                     arm64_ftr_bits_kvm_nentries,
> +                     sizeof(arm64_ftr_bits_kvm[0]),
> +                     search_cmp_ftr_reg_bits);
> +       if (ret)
> +               return ret->ftr_bits;
> +
> +       return NULL;
> +}
> +
> +/*
> + * Check if features (or levels of features) that are indicated in the ID
> + * register value @val are also indicated in @limit.
> + * This function is for KVM to check if features that are indicated in @val,
> + * which will be used as the ID register value for its guest, are supported
> + * on the host.
> + * For AA64MMFR0_EL1.TGranX_2 fields, which don't follow the standard ID
> + * scheme, the function checks if values of the fields in @val are the same
> + * as the ones in @limit.
> + */
> +int arm64_check_features(u32 sys_reg, u64 val, u64 limit)
> +{
> +       const struct arm64_ftr_bits *ftrp = get_arm64_ftr_bits_kvm(sys_reg);
> +       u64 exposed_mask = 0;
> +
> +       if (!ftrp)
> +               return -ENOENT;
> +
> +       for (; ftrp->width; ftrp++) {
> +               s64 ftr_val = arm64_ftr_value(ftrp, val);
> +               s64 ftr_lim = arm64_ftr_value(ftrp, limit);
> +
> +               exposed_mask |= arm64_ftr_mask(ftrp);
> +
> +               if (ftr_val == ftr_lim)
> +                       continue;
> +
> +               if (ftr_val != arm64_ftr_safe_value(ftrp, ftr_val, ftr_lim))
> +                       return -E2BIG;
> +       }
> +
> +       /* Make sure that no unrecognized fields are set in @val. */
> +       if (val & ~exposed_mask)
> +               return -E2BIG;
> +
> +       return 0;
> +}
> +#endif /* CONFIG_KVM */
> --
> 2.34.1.448.ga2b2bfdf31-goog
>

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 00/26] KVM: arm64: Make CPU ID registers writable by userspace
  2022-01-06  4:26 ` Reiji Watanabe
  (?)
@ 2022-01-18  4:24   ` Reiji Watanabe
  -1 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-18  4:24 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, Linux ARM, James Morse, Alexandru Elisei, Suzuki K Poulose,
	Paolo Bonzini, Will Deacon, Andrew Jones, Peng Liang,
	Peter Shier, Ricardo Koller, Oliver Upton, Jing Zhang,
	Raghavendra Rao Anata

On Wed, Jan 5, 2022 at 8:27 PM Reiji Watanabe <reijiw@google.com> wrote:
>
> In KVM/arm64, values of ID registers for a guest are mostly same as
> its host's values except for bits for feature that KVM doesn't support
> and for opt-in features that userspace didn't configure.  Userspace
> can use KVM_SET_ONE_REG to a set ID register value, but it fails
> if userspace attempts to modify the register value.
>
> This patch series adds support to allow userspace to modify a value of
> ID registers (as long as KVM can support features that are indicated
> in the registers) so userspace can have more control of configuring
> and unconfiguring features for guests.  We need this because we would
> like to expose a uniform set/level of features for a group of guests on
> systems with different ARM CPUs.  Since some features are not binary
> in nature (e.g. AA64DFR0_EL1.BRPs fields indicate number of
> breakpoints minus 1), using KVM_ARM_VCPU_INIT to control such features
> is inconvenient.
>
> The patch series is for both VHE and non-VHE, except for protected VMs,
> which have a different way of configuring ID registers based on its
> different requirements [1].
> There was a patch series that tried to achieve the same thing [2].
> A few snippets of codes in this series were inspired by or came from [2].
>
> The initial value of ID registers for a vCPU will be the host's value
> with bits cleared for unsupported features and for opt-in features that
> were not configured. So, the initial value userspace can see (via
> KVM_GET_ONE_REG) is the upper limit that can be set for the register.
> Any requests to change the value that conflicts with opt-in features'
> configuration will fail (e.g. if KVM_ARM_VCPU_PMU_V3 is configured by
> KVM_ARM_VCPU_INIT, ID_AA64DFR0_EL1.PMUVER cannot be set to zero.
> Otherwise, the initial value of ID_AA64DFR0_EL1.PMUVER will be zero,
> and cannot be changed from zero).
>
> When a guest tries to use a CPU feature that is not exposed to the guest,
> trapping it (to emulate a real CPU's behavior) would generally be a
> desirable behavior (when it is possible with no or little side effects).
> The later patches in the series add codes for this.  Only features that
> can be trapped independently will be trapped by this series though.
>
> This series adds kunit tests for new functions in sys_regs.c (except for
> trivial ones), and these tests are enabled with a new configuration
> option 'CONFIG_KVM_KUNIT_TEST'.

A gentle ping on this series.

Since KVM/ARM doesn't have any kunit tests yet, the series (patch-16)
introduces .kunitconfig for KVM/ARM in addition to the new configuration
option (The .kunitconfig in the patch includes only the minimum
configuration options needed for the current tests in the patch).

Any feedback on the patch-16 would be appreciated as with other patches.

Thanks,
Reiji








>
> The series is based on v5.16-rc8 with the patch [3] applied.
>
> v4:
>   - Make ID registers storage per VM instead of per vCPU. [Marc]
>   - Implement arm64_check_features() in arch/arm64/kernel/cpufeature.c
>     by using existing codes in the file. [Marc]
>   - Use a configuration function to enable traps for disabled
>     features. [Marc]
>   - Document ID registers become immutable after the first KVM_RUN [Eric]
>   - Update ID_AA64PFR0.GIC at the point where a GICv3 is created. [Marc]
>   - Get TGranX's bit position by substracting 12 from TGranX_2's bit
>     position. [Eric]
>   - Don't validate AArch32 ID registers when the system doesn't support
>     32bit EL0. [Eric]
>   - Add/fixes comments for patches. [Eric]
>   - Made bug fixes/improvements of the selftest. [Eric]
>   - Added .kunitconfig for arm64 KUnit tests
>
> v3: https://lore.kernel.org/all/20211117064359.2362060-1-reijiw@google.com/
>   - Remove ID register consistency checking across vCPUs. [Oliver]
>   - Change KVM_CAP_ARM_ID_REG_WRITABLE to
>     KVM_CAP_ARM_ID_REG_CONFIGURABLE. [Oliver]
>   - Add KUnit testing for ID register validation and trap initialization.
>   - Change read_id_reg() to take care of ID_AA64PFR0_EL1.GIC.
>   - Add a helper of read_id_reg() (__read_id_reg()) and use the helper
>     instead of directly using __vcpu_sys_reg().
>   - Change not to run kvm_id_regs_consistency_check() and
>     kvm_vcpu_init_traps() for protected VMs.
>   - Update selftest to remove test cases for ID register consistency.
>     checking across vCPUs and to add test cases for ID_AA64PFR0_EL1.GIC.
>
> v2: https://lore.kernel.org/all/20211103062520.1445832-1-reijiw@google.com/
>   - Remove unnecessary line breaks. [Andrew]
>   - Use @params for comments. [Andrew]
>   - Move arm64_check_features to arch/arm64/kvm/sys_regs.c and
>     change that KVM specific feature check function.  [Andrew]
>   - Remove unnecessary raz handling from __set_id_reg. [Andrew]
>   - Remove sys_val field from the initial id_reg_info and add it
>     in the later patch. [Andrew]
>   - Call id_reg->init() from id_reg_info_init(). [Andrew]
>   - Fix cpuid_feature_cap_perfmon_field() to convert 0xf to 0x0
>     (and use it in the following patches).
>   - Change kvm_vcpu_first_run_init to set has_run_once to false
>     when kvm_id_regs_consistency_check() fails.
>   - Add a patch to introduce id_reg_info for ID_AA64MMFR0_EL1,
>     which requires special validity checking for TGran*_2 fields.
>   - Add patches to introduce id_reg_info for ID_DFR1_EL1 and
>     ID_MMFR0_EL1, which are required due to arm64_check_features
>     implementation change.
>   - Add a new argument, which is a pointer to id_reg_info, for
>     id_reg_info's validate().
>
> v1: https://lore.kernel.org/all/20211012043535.500493-1-reijiw@google.com/
>
> [1] https://lore.kernel.org/kvmarm/20211010145636.1950948-1-tabba@google.com/
> [2] https://lore.kernel.org/kvm/20201102033422.657391-1-liangpeng10@huawei.com/
> [3] https://lore.kernel.org/all/20220104194918.373612-2-rananta@google.com/
>
> Reiji Watanabe (26):
>   KVM: arm64: Introduce a validation function for an ID register
>   KVM: arm64: Save ID registers' sanitized value per guest
>   KVM: arm64: Introduce struct id_reg_info
>   KVM: arm64: Make ID_AA64PFR0_EL1 writable
>   KVM: arm64: Make ID_AA64PFR1_EL1 writable
>   KVM: arm64: Make ID_AA64ISAR0_EL1 writable
>   KVM: arm64: Make ID_AA64ISAR1_EL1 writable
>   KVM: arm64: Make ID_AA64MMFR0_EL1 writable
>   KVM: arm64: Hide IMPLEMENTATION DEFINED PMU support for the guest
>   KVM: arm64: Make ID_AA64DFR0_EL1 writable
>   KVM: arm64: Make ID_DFR0_EL1 writable
>   KVM: arm64: Make MVFR1_EL1 writable
>   KVM: arm64: Make ID registers without id_reg_info writable
>   KVM: arm64: Add consistency checking for frac fields of ID registers
>   KVM: arm64: Introduce KVM_CAP_ARM_ID_REG_CONFIGURABLE capability
>   KVM: arm64: Add kunit test for ID register validation
>   KVM: arm64: Use vcpu->arch cptr_el2 to track value of cptr_el2 for VHE
>   KVM: arm64: Use vcpu->arch.mdcr_el2 to track value of mdcr_el2
>   KVM: arm64: Introduce framework to trap disabled features
>   KVM: arm64: Trap disabled features of ID_AA64PFR0_EL1
>   KVM: arm64: Trap disabled features of ID_AA64PFR1_EL1
>   KVM: arm64: Trap disabled features of ID_AA64DFR0_EL1
>   KVM: arm64: Trap disabled features of ID_AA64MMFR1_EL1
>   KVM: arm64: Trap disabled features of ID_AA64ISAR1_EL1
>   KVM: arm64: Add kunit test for trap initialization
>   KVM: arm64: selftests: Introduce id_reg_test
>
>  Documentation/virt/kvm/api.rst                |   12 +
>  arch/arm64/include/asm/cpufeature.h           |    3 +-
>  arch/arm64/include/asm/kvm_arm.h              |   32 +
>  arch/arm64/include/asm/kvm_host.h             |   19 +
>  arch/arm64/include/asm/sysreg.h               |    3 +
>  arch/arm64/kernel/cpufeature.c                |  228 +++
>  arch/arm64/kvm/.kunitconfig                   |    4 +
>  arch/arm64/kvm/Kconfig                        |   11 +
>  arch/arm64/kvm/arm.c                          |   24 +-
>  arch/arm64/kvm/debug.c                        |   13 +-
>  arch/arm64/kvm/hyp/vhe/switch.c               |   14 +-
>  arch/arm64/kvm/sys_regs.c                     | 1329 +++++++++++++++--
>  arch/arm64/kvm/sys_regs_test.c                | 1247 ++++++++++++++++
>  arch/arm64/kvm/vgic/vgic-init.c               |    5 +
>  include/uapi/linux/kvm.h                      |    1 +
>  tools/arch/arm64/include/asm/sysreg.h         |    1 +
>  tools/testing/selftests/kvm/.gitignore        |    1 +
>  tools/testing/selftests/kvm/Makefile          |    1 +
>  .../selftests/kvm/aarch64/id_reg_test.c       | 1239 +++++++++++++++
>  19 files changed, 4041 insertions(+), 146 deletions(-)
>  create mode 100644 arch/arm64/kvm/.kunitconfig
>  create mode 100644 arch/arm64/kvm/sys_regs_test.c
>  create mode 100644 tools/testing/selftests/kvm/aarch64/id_reg_test.c
>
>
> base-commit: d399b107ee49bf5ea0391bd7614d512809e927b0
> --
> 2.34.1.448.ga2b2bfdf31-goog
>

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 00/26] KVM: arm64: Make CPU ID registers writable by userspace
@ 2022-01-18  4:24   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-18  4:24 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, Will Deacon, Peter Shier, Paolo Bonzini, Linux ARM

On Wed, Jan 5, 2022 at 8:27 PM Reiji Watanabe <reijiw@google.com> wrote:
>
> In KVM/arm64, values of ID registers for a guest are mostly same as
> its host's values except for bits for feature that KVM doesn't support
> and for opt-in features that userspace didn't configure.  Userspace
> can use KVM_SET_ONE_REG to a set ID register value, but it fails
> if userspace attempts to modify the register value.
>
> This patch series adds support to allow userspace to modify a value of
> ID registers (as long as KVM can support features that are indicated
> in the registers) so userspace can have more control of configuring
> and unconfiguring features for guests.  We need this because we would
> like to expose a uniform set/level of features for a group of guests on
> systems with different ARM CPUs.  Since some features are not binary
> in nature (e.g. AA64DFR0_EL1.BRPs fields indicate number of
> breakpoints minus 1), using KVM_ARM_VCPU_INIT to control such features
> is inconvenient.
>
> The patch series is for both VHE and non-VHE, except for protected VMs,
> which have a different way of configuring ID registers based on its
> different requirements [1].
> There was a patch series that tried to achieve the same thing [2].
> A few snippets of codes in this series were inspired by or came from [2].
>
> The initial value of ID registers for a vCPU will be the host's value
> with bits cleared for unsupported features and for opt-in features that
> were not configured. So, the initial value userspace can see (via
> KVM_GET_ONE_REG) is the upper limit that can be set for the register.
> Any requests to change the value that conflicts with opt-in features'
> configuration will fail (e.g. if KVM_ARM_VCPU_PMU_V3 is configured by
> KVM_ARM_VCPU_INIT, ID_AA64DFR0_EL1.PMUVER cannot be set to zero.
> Otherwise, the initial value of ID_AA64DFR0_EL1.PMUVER will be zero,
> and cannot be changed from zero).
>
> When a guest tries to use a CPU feature that is not exposed to the guest,
> trapping it (to emulate a real CPU's behavior) would generally be a
> desirable behavior (when it is possible with no or little side effects).
> The later patches in the series add codes for this.  Only features that
> can be trapped independently will be trapped by this series though.
>
> This series adds kunit tests for new functions in sys_regs.c (except for
> trivial ones), and these tests are enabled with a new configuration
> option 'CONFIG_KVM_KUNIT_TEST'.

A gentle ping on this series.

Since KVM/ARM doesn't have any kunit tests yet, the series (patch-16)
introduces .kunitconfig for KVM/ARM in addition to the new configuration
option (The .kunitconfig in the patch includes only the minimum
configuration options needed for the current tests in the patch).

Any feedback on the patch-16 would be appreciated as with other patches.

Thanks,
Reiji








>
> The series is based on v5.16-rc8 with the patch [3] applied.
>
> v4:
>   - Make ID registers storage per VM instead of per vCPU. [Marc]
>   - Implement arm64_check_features() in arch/arm64/kernel/cpufeature.c
>     by using existing codes in the file. [Marc]
>   - Use a configuration function to enable traps for disabled
>     features. [Marc]
>   - Document ID registers become immutable after the first KVM_RUN [Eric]
>   - Update ID_AA64PFR0.GIC at the point where a GICv3 is created. [Marc]
>   - Get TGranX's bit position by substracting 12 from TGranX_2's bit
>     position. [Eric]
>   - Don't validate AArch32 ID registers when the system doesn't support
>     32bit EL0. [Eric]
>   - Add/fixes comments for patches. [Eric]
>   - Made bug fixes/improvements of the selftest. [Eric]
>   - Added .kunitconfig for arm64 KUnit tests
>
> v3: https://lore.kernel.org/all/20211117064359.2362060-1-reijiw@google.com/
>   - Remove ID register consistency checking across vCPUs. [Oliver]
>   - Change KVM_CAP_ARM_ID_REG_WRITABLE to
>     KVM_CAP_ARM_ID_REG_CONFIGURABLE. [Oliver]
>   - Add KUnit testing for ID register validation and trap initialization.
>   - Change read_id_reg() to take care of ID_AA64PFR0_EL1.GIC.
>   - Add a helper of read_id_reg() (__read_id_reg()) and use the helper
>     instead of directly using __vcpu_sys_reg().
>   - Change not to run kvm_id_regs_consistency_check() and
>     kvm_vcpu_init_traps() for protected VMs.
>   - Update selftest to remove test cases for ID register consistency.
>     checking across vCPUs and to add test cases for ID_AA64PFR0_EL1.GIC.
>
> v2: https://lore.kernel.org/all/20211103062520.1445832-1-reijiw@google.com/
>   - Remove unnecessary line breaks. [Andrew]
>   - Use @params for comments. [Andrew]
>   - Move arm64_check_features to arch/arm64/kvm/sys_regs.c and
>     change that KVM specific feature check function.  [Andrew]
>   - Remove unnecessary raz handling from __set_id_reg. [Andrew]
>   - Remove sys_val field from the initial id_reg_info and add it
>     in the later patch. [Andrew]
>   - Call id_reg->init() from id_reg_info_init(). [Andrew]
>   - Fix cpuid_feature_cap_perfmon_field() to convert 0xf to 0x0
>     (and use it in the following patches).
>   - Change kvm_vcpu_first_run_init to set has_run_once to false
>     when kvm_id_regs_consistency_check() fails.
>   - Add a patch to introduce id_reg_info for ID_AA64MMFR0_EL1,
>     which requires special validity checking for TGran*_2 fields.
>   - Add patches to introduce id_reg_info for ID_DFR1_EL1 and
>     ID_MMFR0_EL1, which are required due to arm64_check_features
>     implementation change.
>   - Add a new argument, which is a pointer to id_reg_info, for
>     id_reg_info's validate().
>
> v1: https://lore.kernel.org/all/20211012043535.500493-1-reijiw@google.com/
>
> [1] https://lore.kernel.org/kvmarm/20211010145636.1950948-1-tabba@google.com/
> [2] https://lore.kernel.org/kvm/20201102033422.657391-1-liangpeng10@huawei.com/
> [3] https://lore.kernel.org/all/20220104194918.373612-2-rananta@google.com/
>
> Reiji Watanabe (26):
>   KVM: arm64: Introduce a validation function for an ID register
>   KVM: arm64: Save ID registers' sanitized value per guest
>   KVM: arm64: Introduce struct id_reg_info
>   KVM: arm64: Make ID_AA64PFR0_EL1 writable
>   KVM: arm64: Make ID_AA64PFR1_EL1 writable
>   KVM: arm64: Make ID_AA64ISAR0_EL1 writable
>   KVM: arm64: Make ID_AA64ISAR1_EL1 writable
>   KVM: arm64: Make ID_AA64MMFR0_EL1 writable
>   KVM: arm64: Hide IMPLEMENTATION DEFINED PMU support for the guest
>   KVM: arm64: Make ID_AA64DFR0_EL1 writable
>   KVM: arm64: Make ID_DFR0_EL1 writable
>   KVM: arm64: Make MVFR1_EL1 writable
>   KVM: arm64: Make ID registers without id_reg_info writable
>   KVM: arm64: Add consistency checking for frac fields of ID registers
>   KVM: arm64: Introduce KVM_CAP_ARM_ID_REG_CONFIGURABLE capability
>   KVM: arm64: Add kunit test for ID register validation
>   KVM: arm64: Use vcpu->arch cptr_el2 to track value of cptr_el2 for VHE
>   KVM: arm64: Use vcpu->arch.mdcr_el2 to track value of mdcr_el2
>   KVM: arm64: Introduce framework to trap disabled features
>   KVM: arm64: Trap disabled features of ID_AA64PFR0_EL1
>   KVM: arm64: Trap disabled features of ID_AA64PFR1_EL1
>   KVM: arm64: Trap disabled features of ID_AA64DFR0_EL1
>   KVM: arm64: Trap disabled features of ID_AA64MMFR1_EL1
>   KVM: arm64: Trap disabled features of ID_AA64ISAR1_EL1
>   KVM: arm64: Add kunit test for trap initialization
>   KVM: arm64: selftests: Introduce id_reg_test
>
>  Documentation/virt/kvm/api.rst                |   12 +
>  arch/arm64/include/asm/cpufeature.h           |    3 +-
>  arch/arm64/include/asm/kvm_arm.h              |   32 +
>  arch/arm64/include/asm/kvm_host.h             |   19 +
>  arch/arm64/include/asm/sysreg.h               |    3 +
>  arch/arm64/kernel/cpufeature.c                |  228 +++
>  arch/arm64/kvm/.kunitconfig                   |    4 +
>  arch/arm64/kvm/Kconfig                        |   11 +
>  arch/arm64/kvm/arm.c                          |   24 +-
>  arch/arm64/kvm/debug.c                        |   13 +-
>  arch/arm64/kvm/hyp/vhe/switch.c               |   14 +-
>  arch/arm64/kvm/sys_regs.c                     | 1329 +++++++++++++++--
>  arch/arm64/kvm/sys_regs_test.c                | 1247 ++++++++++++++++
>  arch/arm64/kvm/vgic/vgic-init.c               |    5 +
>  include/uapi/linux/kvm.h                      |    1 +
>  tools/arch/arm64/include/asm/sysreg.h         |    1 +
>  tools/testing/selftests/kvm/.gitignore        |    1 +
>  tools/testing/selftests/kvm/Makefile          |    1 +
>  .../selftests/kvm/aarch64/id_reg_test.c       | 1239 +++++++++++++++
>  19 files changed, 4041 insertions(+), 146 deletions(-)
>  create mode 100644 arch/arm64/kvm/.kunitconfig
>  create mode 100644 arch/arm64/kvm/sys_regs_test.c
>  create mode 100644 tools/testing/selftests/kvm/aarch64/id_reg_test.c
>
>
> base-commit: d399b107ee49bf5ea0391bd7614d512809e927b0
> --
> 2.34.1.448.ga2b2bfdf31-goog
>
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 00/26] KVM: arm64: Make CPU ID registers writable by userspace
@ 2022-01-18  4:24   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-18  4:24 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm
  Cc: kvm, Linux ARM, James Morse, Alexandru Elisei, Suzuki K Poulose,
	Paolo Bonzini, Will Deacon, Andrew Jones, Peng Liang,
	Peter Shier, Ricardo Koller, Oliver Upton, Jing Zhang,
	Raghavendra Rao Anata

On Wed, Jan 5, 2022 at 8:27 PM Reiji Watanabe <reijiw@google.com> wrote:
>
> In KVM/arm64, values of ID registers for a guest are mostly same as
> its host's values except for bits for feature that KVM doesn't support
> and for opt-in features that userspace didn't configure.  Userspace
> can use KVM_SET_ONE_REG to a set ID register value, but it fails
> if userspace attempts to modify the register value.
>
> This patch series adds support to allow userspace to modify a value of
> ID registers (as long as KVM can support features that are indicated
> in the registers) so userspace can have more control of configuring
> and unconfiguring features for guests.  We need this because we would
> like to expose a uniform set/level of features for a group of guests on
> systems with different ARM CPUs.  Since some features are not binary
> in nature (e.g. AA64DFR0_EL1.BRPs fields indicate number of
> breakpoints minus 1), using KVM_ARM_VCPU_INIT to control such features
> is inconvenient.
>
> The patch series is for both VHE and non-VHE, except for protected VMs,
> which have a different way of configuring ID registers based on its
> different requirements [1].
> There was a patch series that tried to achieve the same thing [2].
> A few snippets of codes in this series were inspired by or came from [2].
>
> The initial value of ID registers for a vCPU will be the host's value
> with bits cleared for unsupported features and for opt-in features that
> were not configured. So, the initial value userspace can see (via
> KVM_GET_ONE_REG) is the upper limit that can be set for the register.
> Any requests to change the value that conflicts with opt-in features'
> configuration will fail (e.g. if KVM_ARM_VCPU_PMU_V3 is configured by
> KVM_ARM_VCPU_INIT, ID_AA64DFR0_EL1.PMUVER cannot be set to zero.
> Otherwise, the initial value of ID_AA64DFR0_EL1.PMUVER will be zero,
> and cannot be changed from zero).
>
> When a guest tries to use a CPU feature that is not exposed to the guest,
> trapping it (to emulate a real CPU's behavior) would generally be a
> desirable behavior (when it is possible with no or little side effects).
> The later patches in the series add codes for this.  Only features that
> can be trapped independently will be trapped by this series though.
>
> This series adds kunit tests for new functions in sys_regs.c (except for
> trivial ones), and these tests are enabled with a new configuration
> option 'CONFIG_KVM_KUNIT_TEST'.

A gentle ping on this series.

Since KVM/ARM doesn't have any kunit tests yet, the series (patch-16)
introduces .kunitconfig for KVM/ARM in addition to the new configuration
option (The .kunitconfig in the patch includes only the minimum
configuration options needed for the current tests in the patch).

Any feedback on the patch-16 would be appreciated as with other patches.

Thanks,
Reiji








>
> The series is based on v5.16-rc8 with the patch [3] applied.
>
> v4:
>   - Make ID registers storage per VM instead of per vCPU. [Marc]
>   - Implement arm64_check_features() in arch/arm64/kernel/cpufeature.c
>     by using existing codes in the file. [Marc]
>   - Use a configuration function to enable traps for disabled
>     features. [Marc]
>   - Document ID registers become immutable after the first KVM_RUN [Eric]
>   - Update ID_AA64PFR0.GIC at the point where a GICv3 is created. [Marc]
>   - Get TGranX's bit position by substracting 12 from TGranX_2's bit
>     position. [Eric]
>   - Don't validate AArch32 ID registers when the system doesn't support
>     32bit EL0. [Eric]
>   - Add/fixes comments for patches. [Eric]
>   - Made bug fixes/improvements of the selftest. [Eric]
>   - Added .kunitconfig for arm64 KUnit tests
>
> v3: https://lore.kernel.org/all/20211117064359.2362060-1-reijiw@google.com/
>   - Remove ID register consistency checking across vCPUs. [Oliver]
>   - Change KVM_CAP_ARM_ID_REG_WRITABLE to
>     KVM_CAP_ARM_ID_REG_CONFIGURABLE. [Oliver]
>   - Add KUnit testing for ID register validation and trap initialization.
>   - Change read_id_reg() to take care of ID_AA64PFR0_EL1.GIC.
>   - Add a helper of read_id_reg() (__read_id_reg()) and use the helper
>     instead of directly using __vcpu_sys_reg().
>   - Change not to run kvm_id_regs_consistency_check() and
>     kvm_vcpu_init_traps() for protected VMs.
>   - Update selftest to remove test cases for ID register consistency.
>     checking across vCPUs and to add test cases for ID_AA64PFR0_EL1.GIC.
>
> v2: https://lore.kernel.org/all/20211103062520.1445832-1-reijiw@google.com/
>   - Remove unnecessary line breaks. [Andrew]
>   - Use @params for comments. [Andrew]
>   - Move arm64_check_features to arch/arm64/kvm/sys_regs.c and
>     change that KVM specific feature check function.  [Andrew]
>   - Remove unnecessary raz handling from __set_id_reg. [Andrew]
>   - Remove sys_val field from the initial id_reg_info and add it
>     in the later patch. [Andrew]
>   - Call id_reg->init() from id_reg_info_init(). [Andrew]
>   - Fix cpuid_feature_cap_perfmon_field() to convert 0xf to 0x0
>     (and use it in the following patches).
>   - Change kvm_vcpu_first_run_init to set has_run_once to false
>     when kvm_id_regs_consistency_check() fails.
>   - Add a patch to introduce id_reg_info for ID_AA64MMFR0_EL1,
>     which requires special validity checking for TGran*_2 fields.
>   - Add patches to introduce id_reg_info for ID_DFR1_EL1 and
>     ID_MMFR0_EL1, which are required due to arm64_check_features
>     implementation change.
>   - Add a new argument, which is a pointer to id_reg_info, for
>     id_reg_info's validate().
>
> v1: https://lore.kernel.org/all/20211012043535.500493-1-reijiw@google.com/
>
> [1] https://lore.kernel.org/kvmarm/20211010145636.1950948-1-tabba@google.com/
> [2] https://lore.kernel.org/kvm/20201102033422.657391-1-liangpeng10@huawei.com/
> [3] https://lore.kernel.org/all/20220104194918.373612-2-rananta@google.com/
>
> Reiji Watanabe (26):
>   KVM: arm64: Introduce a validation function for an ID register
>   KVM: arm64: Save ID registers' sanitized value per guest
>   KVM: arm64: Introduce struct id_reg_info
>   KVM: arm64: Make ID_AA64PFR0_EL1 writable
>   KVM: arm64: Make ID_AA64PFR1_EL1 writable
>   KVM: arm64: Make ID_AA64ISAR0_EL1 writable
>   KVM: arm64: Make ID_AA64ISAR1_EL1 writable
>   KVM: arm64: Make ID_AA64MMFR0_EL1 writable
>   KVM: arm64: Hide IMPLEMENTATION DEFINED PMU support for the guest
>   KVM: arm64: Make ID_AA64DFR0_EL1 writable
>   KVM: arm64: Make ID_DFR0_EL1 writable
>   KVM: arm64: Make MVFR1_EL1 writable
>   KVM: arm64: Make ID registers without id_reg_info writable
>   KVM: arm64: Add consistency checking for frac fields of ID registers
>   KVM: arm64: Introduce KVM_CAP_ARM_ID_REG_CONFIGURABLE capability
>   KVM: arm64: Add kunit test for ID register validation
>   KVM: arm64: Use vcpu->arch cptr_el2 to track value of cptr_el2 for VHE
>   KVM: arm64: Use vcpu->arch.mdcr_el2 to track value of mdcr_el2
>   KVM: arm64: Introduce framework to trap disabled features
>   KVM: arm64: Trap disabled features of ID_AA64PFR0_EL1
>   KVM: arm64: Trap disabled features of ID_AA64PFR1_EL1
>   KVM: arm64: Trap disabled features of ID_AA64DFR0_EL1
>   KVM: arm64: Trap disabled features of ID_AA64MMFR1_EL1
>   KVM: arm64: Trap disabled features of ID_AA64ISAR1_EL1
>   KVM: arm64: Add kunit test for trap initialization
>   KVM: arm64: selftests: Introduce id_reg_test
>
>  Documentation/virt/kvm/api.rst                |   12 +
>  arch/arm64/include/asm/cpufeature.h           |    3 +-
>  arch/arm64/include/asm/kvm_arm.h              |   32 +
>  arch/arm64/include/asm/kvm_host.h             |   19 +
>  arch/arm64/include/asm/sysreg.h               |    3 +
>  arch/arm64/kernel/cpufeature.c                |  228 +++
>  arch/arm64/kvm/.kunitconfig                   |    4 +
>  arch/arm64/kvm/Kconfig                        |   11 +
>  arch/arm64/kvm/arm.c                          |   24 +-
>  arch/arm64/kvm/debug.c                        |   13 +-
>  arch/arm64/kvm/hyp/vhe/switch.c               |   14 +-
>  arch/arm64/kvm/sys_regs.c                     | 1329 +++++++++++++++--
>  arch/arm64/kvm/sys_regs_test.c                | 1247 ++++++++++++++++
>  arch/arm64/kvm/vgic/vgic-init.c               |    5 +
>  include/uapi/linux/kvm.h                      |    1 +
>  tools/arch/arm64/include/asm/sysreg.h         |    1 +
>  tools/testing/selftests/kvm/.gitignore        |    1 +
>  tools/testing/selftests/kvm/Makefile          |    1 +
>  .../selftests/kvm/aarch64/id_reg_test.c       | 1239 +++++++++++++++
>  19 files changed, 4041 insertions(+), 146 deletions(-)
>  create mode 100644 arch/arm64/kvm/.kunitconfig
>  create mode 100644 arch/arm64/kvm/sys_regs_test.c
>  create mode 100644 tools/testing/selftests/kvm/aarch64/id_reg_test.c
>
>
> base-commit: d399b107ee49bf5ea0391bd7614d512809e927b0
> --
> 2.34.1.448.ga2b2bfdf31-goog
>

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 00/26] KVM: arm64: Make CPU ID registers writable by userspace
  2022-01-06  4:26 ` Reiji Watanabe
  (?)
@ 2022-01-24 16:18   ` Fuad Tabba
  -1 siblings, 0 replies; 201+ messages in thread
From: Fuad Tabba @ 2022-01-24 16:18 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, linux-arm-kernel

Hi Reiji,

On Thu, Jan 6, 2022 at 4:27 AM Reiji Watanabe <reijiw@google.com> wrote:

> The patch series is for both VHE and non-VHE, except for protected VMs,
> which have a different way of configuring ID registers based on its
> different requirements [1].

I am reviewing this patch series and I really like your approach. I
think that once it's through we might be able to generalize it for
protected VMs as well (I did some of the work for the ID registers for
those).

> The series is based on v5.16-rc8 with the patch [3] applied.

I tried to apply this series on v5.16-rc8 after applying
20220104194918.373612-2-rananta@google.com, and it didn't apply
cleanly. If you could point me to a working branch that I could
checkout I would like to test it and experiment with it a bit more.

Thanks,
/fuad




> v4:
>   - Make ID registers storage per VM instead of per vCPU. [Marc]
>   - Implement arm64_check_features() in arch/arm64/kernel/cpufeature.c
>     by using existing codes in the file. [Marc]
>   - Use a configuration function to enable traps for disabled
>     features. [Marc]
>   - Document ID registers become immutable after the first KVM_RUN [Eric]
>   - Update ID_AA64PFR0.GIC at the point where a GICv3 is created. [Marc]
>   - Get TGranX's bit position by substracting 12 from TGranX_2's bit
>     position. [Eric]
>   - Don't validate AArch32 ID registers when the system doesn't support
>     32bit EL0. [Eric]
>   - Add/fixes comments for patches. [Eric]
>   - Made bug fixes/improvements of the selftest. [Eric]
>   - Added .kunitconfig for arm64 KUnit tests
>
> v3: https://lore.kernel.org/all/20211117064359.2362060-1-reijiw@google.com/
>   - Remove ID register consistency checking across vCPUs. [Oliver]
>   - Change KVM_CAP_ARM_ID_REG_WRITABLE to
>     KVM_CAP_ARM_ID_REG_CONFIGURABLE. [Oliver]
>   - Add KUnit testing for ID register validation and trap initialization.
>   - Change read_id_reg() to take care of ID_AA64PFR0_EL1.GIC.
>   - Add a helper of read_id_reg() (__read_id_reg()) and use the helper
>     instead of directly using __vcpu_sys_reg().
>   - Change not to run kvm_id_regs_consistency_check() and
>     kvm_vcpu_init_traps() for protected VMs.
>   - Update selftest to remove test cases for ID register consistency.
>     checking across vCPUs and to add test cases for ID_AA64PFR0_EL1.GIC.
>
> v2: https://lore.kernel.org/all/20211103062520.1445832-1-reijiw@google.com/
>   - Remove unnecessary line breaks. [Andrew]
>   - Use @params for comments. [Andrew]
>   - Move arm64_check_features to arch/arm64/kvm/sys_regs.c and
>     change that KVM specific feature check function.  [Andrew]
>   - Remove unnecessary raz handling from __set_id_reg. [Andrew]
>   - Remove sys_val field from the initial id_reg_info and add it
>     in the later patch. [Andrew]
>   - Call id_reg->init() from id_reg_info_init(). [Andrew]
>   - Fix cpuid_feature_cap_perfmon_field() to convert 0xf to 0x0
>     (and use it in the following patches).
>   - Change kvm_vcpu_first_run_init to set has_run_once to false
>     when kvm_id_regs_consistency_check() fails.
>   - Add a patch to introduce id_reg_info for ID_AA64MMFR0_EL1,
>     which requires special validity checking for TGran*_2 fields.
>   - Add patches to introduce id_reg_info for ID_DFR1_EL1 and
>     ID_MMFR0_EL1, which are required due to arm64_check_features
>     implementation change.
>   - Add a new argument, which is a pointer to id_reg_info, for
>     id_reg_info's validate().
>
> v1: https://lore.kernel.org/all/20211012043535.500493-1-reijiw@google.com/
>
> [1] https://lore.kernel.org/kvmarm/20211010145636.1950948-1-tabba@google.com/
> [2] https://lore.kernel.org/kvm/20201102033422.657391-1-liangpeng10@huawei.com/
> [3] https://lore.kernel.org/all/20220104194918.373612-2-rananta@google.com/
>
> Reiji Watanabe (26):
>   KVM: arm64: Introduce a validation function for an ID register
>   KVM: arm64: Save ID registers' sanitized value per guest
>   KVM: arm64: Introduce struct id_reg_info
>   KVM: arm64: Make ID_AA64PFR0_EL1 writable
>   KVM: arm64: Make ID_AA64PFR1_EL1 writable
>   KVM: arm64: Make ID_AA64ISAR0_EL1 writable
>   KVM: arm64: Make ID_AA64ISAR1_EL1 writable
>   KVM: arm64: Make ID_AA64MMFR0_EL1 writable
>   KVM: arm64: Hide IMPLEMENTATION DEFINED PMU support for the guest
>   KVM: arm64: Make ID_AA64DFR0_EL1 writable
>   KVM: arm64: Make ID_DFR0_EL1 writable
>   KVM: arm64: Make MVFR1_EL1 writable
>   KVM: arm64: Make ID registers without id_reg_info writable
>   KVM: arm64: Add consistency checking for frac fields of ID registers
>   KVM: arm64: Introduce KVM_CAP_ARM_ID_REG_CONFIGURABLE capability
>   KVM: arm64: Add kunit test for ID register validation
>   KVM: arm64: Use vcpu->arch cptr_el2 to track value of cptr_el2 for VHE
>   KVM: arm64: Use vcpu->arch.mdcr_el2 to track value of mdcr_el2
>   KVM: arm64: Introduce framework to trap disabled features
>   KVM: arm64: Trap disabled features of ID_AA64PFR0_EL1
>   KVM: arm64: Trap disabled features of ID_AA64PFR1_EL1
>   KVM: arm64: Trap disabled features of ID_AA64DFR0_EL1
>   KVM: arm64: Trap disabled features of ID_AA64MMFR1_EL1
>   KVM: arm64: Trap disabled features of ID_AA64ISAR1_EL1
>   KVM: arm64: Add kunit test for trap initialization
>   KVM: arm64: selftests: Introduce id_reg_test
>
>  Documentation/virt/kvm/api.rst                |   12 +
>  arch/arm64/include/asm/cpufeature.h           |    3 +-
>  arch/arm64/include/asm/kvm_arm.h              |   32 +
>  arch/arm64/include/asm/kvm_host.h             |   19 +
>  arch/arm64/include/asm/sysreg.h               |    3 +
>  arch/arm64/kernel/cpufeature.c                |  228 +++
>  arch/arm64/kvm/.kunitconfig                   |    4 +
>  arch/arm64/kvm/Kconfig                        |   11 +
>  arch/arm64/kvm/arm.c                          |   24 +-
>  arch/arm64/kvm/debug.c                        |   13 +-
>  arch/arm64/kvm/hyp/vhe/switch.c               |   14 +-
>  arch/arm64/kvm/sys_regs.c                     | 1329 +++++++++++++++--
>  arch/arm64/kvm/sys_regs_test.c                | 1247 ++++++++++++++++
>  arch/arm64/kvm/vgic/vgic-init.c               |    5 +
>  include/uapi/linux/kvm.h                      |    1 +
>  tools/arch/arm64/include/asm/sysreg.h         |    1 +
>  tools/testing/selftests/kvm/.gitignore        |    1 +
>  tools/testing/selftests/kvm/Makefile          |    1 +
>  .../selftests/kvm/aarch64/id_reg_test.c       | 1239 +++++++++++++++
>  19 files changed, 4041 insertions(+), 146 deletions(-)
>  create mode 100644 arch/arm64/kvm/.kunitconfig
>  create mode 100644 arch/arm64/kvm/sys_regs_test.c
>  create mode 100644 tools/testing/selftests/kvm/aarch64/id_reg_test.c
>
>
> base-commit: d399b107ee49bf5ea0391bd7614d512809e927b0
> --
> 2.34.1.448.ga2b2bfdf31-goog
>
> _______________________________________________
> kvmarm mailing list
> kvmarm@lists.cs.columbia.edu
> https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 00/26] KVM: arm64: Make CPU ID registers writable by userspace
@ 2022-01-24 16:18   ` Fuad Tabba
  0 siblings, 0 replies; 201+ messages in thread
From: Fuad Tabba @ 2022-01-24 16:18 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: kvm, Marc Zyngier, Peter Shier, Paolo Bonzini, Will Deacon,
	kvmarm, linux-arm-kernel

Hi Reiji,

On Thu, Jan 6, 2022 at 4:27 AM Reiji Watanabe <reijiw@google.com> wrote:

> The patch series is for both VHE and non-VHE, except for protected VMs,
> which have a different way of configuring ID registers based on its
> different requirements [1].

I am reviewing this patch series and I really like your approach. I
think that once it's through we might be able to generalize it for
protected VMs as well (I did some of the work for the ID registers for
those).

> The series is based on v5.16-rc8 with the patch [3] applied.

I tried to apply this series on v5.16-rc8 after applying
20220104194918.373612-2-rananta@google.com, and it didn't apply
cleanly. If you could point me to a working branch that I could
checkout I would like to test it and experiment with it a bit more.

Thanks,
/fuad




> v4:
>   - Make ID registers storage per VM instead of per vCPU. [Marc]
>   - Implement arm64_check_features() in arch/arm64/kernel/cpufeature.c
>     by using existing codes in the file. [Marc]
>   - Use a configuration function to enable traps for disabled
>     features. [Marc]
>   - Document ID registers become immutable after the first KVM_RUN [Eric]
>   - Update ID_AA64PFR0.GIC at the point where a GICv3 is created. [Marc]
>   - Get TGranX's bit position by substracting 12 from TGranX_2's bit
>     position. [Eric]
>   - Don't validate AArch32 ID registers when the system doesn't support
>     32bit EL0. [Eric]
>   - Add/fixes comments for patches. [Eric]
>   - Made bug fixes/improvements of the selftest. [Eric]
>   - Added .kunitconfig for arm64 KUnit tests
>
> v3: https://lore.kernel.org/all/20211117064359.2362060-1-reijiw@google.com/
>   - Remove ID register consistency checking across vCPUs. [Oliver]
>   - Change KVM_CAP_ARM_ID_REG_WRITABLE to
>     KVM_CAP_ARM_ID_REG_CONFIGURABLE. [Oliver]
>   - Add KUnit testing for ID register validation and trap initialization.
>   - Change read_id_reg() to take care of ID_AA64PFR0_EL1.GIC.
>   - Add a helper of read_id_reg() (__read_id_reg()) and use the helper
>     instead of directly using __vcpu_sys_reg().
>   - Change not to run kvm_id_regs_consistency_check() and
>     kvm_vcpu_init_traps() for protected VMs.
>   - Update selftest to remove test cases for ID register consistency.
>     checking across vCPUs and to add test cases for ID_AA64PFR0_EL1.GIC.
>
> v2: https://lore.kernel.org/all/20211103062520.1445832-1-reijiw@google.com/
>   - Remove unnecessary line breaks. [Andrew]
>   - Use @params for comments. [Andrew]
>   - Move arm64_check_features to arch/arm64/kvm/sys_regs.c and
>     change that KVM specific feature check function.  [Andrew]
>   - Remove unnecessary raz handling from __set_id_reg. [Andrew]
>   - Remove sys_val field from the initial id_reg_info and add it
>     in the later patch. [Andrew]
>   - Call id_reg->init() from id_reg_info_init(). [Andrew]
>   - Fix cpuid_feature_cap_perfmon_field() to convert 0xf to 0x0
>     (and use it in the following patches).
>   - Change kvm_vcpu_first_run_init to set has_run_once to false
>     when kvm_id_regs_consistency_check() fails.
>   - Add a patch to introduce id_reg_info for ID_AA64MMFR0_EL1,
>     which requires special validity checking for TGran*_2 fields.
>   - Add patches to introduce id_reg_info for ID_DFR1_EL1 and
>     ID_MMFR0_EL1, which are required due to arm64_check_features
>     implementation change.
>   - Add a new argument, which is a pointer to id_reg_info, for
>     id_reg_info's validate().
>
> v1: https://lore.kernel.org/all/20211012043535.500493-1-reijiw@google.com/
>
> [1] https://lore.kernel.org/kvmarm/20211010145636.1950948-1-tabba@google.com/
> [2] https://lore.kernel.org/kvm/20201102033422.657391-1-liangpeng10@huawei.com/
> [3] https://lore.kernel.org/all/20220104194918.373612-2-rananta@google.com/
>
> Reiji Watanabe (26):
>   KVM: arm64: Introduce a validation function for an ID register
>   KVM: arm64: Save ID registers' sanitized value per guest
>   KVM: arm64: Introduce struct id_reg_info
>   KVM: arm64: Make ID_AA64PFR0_EL1 writable
>   KVM: arm64: Make ID_AA64PFR1_EL1 writable
>   KVM: arm64: Make ID_AA64ISAR0_EL1 writable
>   KVM: arm64: Make ID_AA64ISAR1_EL1 writable
>   KVM: arm64: Make ID_AA64MMFR0_EL1 writable
>   KVM: arm64: Hide IMPLEMENTATION DEFINED PMU support for the guest
>   KVM: arm64: Make ID_AA64DFR0_EL1 writable
>   KVM: arm64: Make ID_DFR0_EL1 writable
>   KVM: arm64: Make MVFR1_EL1 writable
>   KVM: arm64: Make ID registers without id_reg_info writable
>   KVM: arm64: Add consistency checking for frac fields of ID registers
>   KVM: arm64: Introduce KVM_CAP_ARM_ID_REG_CONFIGURABLE capability
>   KVM: arm64: Add kunit test for ID register validation
>   KVM: arm64: Use vcpu->arch cptr_el2 to track value of cptr_el2 for VHE
>   KVM: arm64: Use vcpu->arch.mdcr_el2 to track value of mdcr_el2
>   KVM: arm64: Introduce framework to trap disabled features
>   KVM: arm64: Trap disabled features of ID_AA64PFR0_EL1
>   KVM: arm64: Trap disabled features of ID_AA64PFR1_EL1
>   KVM: arm64: Trap disabled features of ID_AA64DFR0_EL1
>   KVM: arm64: Trap disabled features of ID_AA64MMFR1_EL1
>   KVM: arm64: Trap disabled features of ID_AA64ISAR1_EL1
>   KVM: arm64: Add kunit test for trap initialization
>   KVM: arm64: selftests: Introduce id_reg_test
>
>  Documentation/virt/kvm/api.rst                |   12 +
>  arch/arm64/include/asm/cpufeature.h           |    3 +-
>  arch/arm64/include/asm/kvm_arm.h              |   32 +
>  arch/arm64/include/asm/kvm_host.h             |   19 +
>  arch/arm64/include/asm/sysreg.h               |    3 +
>  arch/arm64/kernel/cpufeature.c                |  228 +++
>  arch/arm64/kvm/.kunitconfig                   |    4 +
>  arch/arm64/kvm/Kconfig                        |   11 +
>  arch/arm64/kvm/arm.c                          |   24 +-
>  arch/arm64/kvm/debug.c                        |   13 +-
>  arch/arm64/kvm/hyp/vhe/switch.c               |   14 +-
>  arch/arm64/kvm/sys_regs.c                     | 1329 +++++++++++++++--
>  arch/arm64/kvm/sys_regs_test.c                | 1247 ++++++++++++++++
>  arch/arm64/kvm/vgic/vgic-init.c               |    5 +
>  include/uapi/linux/kvm.h                      |    1 +
>  tools/arch/arm64/include/asm/sysreg.h         |    1 +
>  tools/testing/selftests/kvm/.gitignore        |    1 +
>  tools/testing/selftests/kvm/Makefile          |    1 +
>  .../selftests/kvm/aarch64/id_reg_test.c       | 1239 +++++++++++++++
>  19 files changed, 4041 insertions(+), 146 deletions(-)
>  create mode 100644 arch/arm64/kvm/.kunitconfig
>  create mode 100644 arch/arm64/kvm/sys_regs_test.c
>  create mode 100644 tools/testing/selftests/kvm/aarch64/id_reg_test.c
>
>
> base-commit: d399b107ee49bf5ea0391bd7614d512809e927b0
> --
> 2.34.1.448.ga2b2bfdf31-goog
>
> _______________________________________________
> kvmarm mailing list
> kvmarm@lists.cs.columbia.edu
> https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 00/26] KVM: arm64: Make CPU ID registers writable by userspace
@ 2022-01-24 16:18   ` Fuad Tabba
  0 siblings, 0 replies; 201+ messages in thread
From: Fuad Tabba @ 2022-01-24 16:18 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, linux-arm-kernel

Hi Reiji,

On Thu, Jan 6, 2022 at 4:27 AM Reiji Watanabe <reijiw@google.com> wrote:

> The patch series is for both VHE and non-VHE, except for protected VMs,
> which have a different way of configuring ID registers based on its
> different requirements [1].

I am reviewing this patch series and I really like your approach. I
think that once it's through we might be able to generalize it for
protected VMs as well (I did some of the work for the ID registers for
those).

> The series is based on v5.16-rc8 with the patch [3] applied.

I tried to apply this series on v5.16-rc8 after applying
20220104194918.373612-2-rananta@google.com, and it didn't apply
cleanly. If you could point me to a working branch that I could
checkout I would like to test it and experiment with it a bit more.

Thanks,
/fuad




> v4:
>   - Make ID registers storage per VM instead of per vCPU. [Marc]
>   - Implement arm64_check_features() in arch/arm64/kernel/cpufeature.c
>     by using existing codes in the file. [Marc]
>   - Use a configuration function to enable traps for disabled
>     features. [Marc]
>   - Document ID registers become immutable after the first KVM_RUN [Eric]
>   - Update ID_AA64PFR0.GIC at the point where a GICv3 is created. [Marc]
>   - Get TGranX's bit position by substracting 12 from TGranX_2's bit
>     position. [Eric]
>   - Don't validate AArch32 ID registers when the system doesn't support
>     32bit EL0. [Eric]
>   - Add/fixes comments for patches. [Eric]
>   - Made bug fixes/improvements of the selftest. [Eric]
>   - Added .kunitconfig for arm64 KUnit tests
>
> v3: https://lore.kernel.org/all/20211117064359.2362060-1-reijiw@google.com/
>   - Remove ID register consistency checking across vCPUs. [Oliver]
>   - Change KVM_CAP_ARM_ID_REG_WRITABLE to
>     KVM_CAP_ARM_ID_REG_CONFIGURABLE. [Oliver]
>   - Add KUnit testing for ID register validation and trap initialization.
>   - Change read_id_reg() to take care of ID_AA64PFR0_EL1.GIC.
>   - Add a helper of read_id_reg() (__read_id_reg()) and use the helper
>     instead of directly using __vcpu_sys_reg().
>   - Change not to run kvm_id_regs_consistency_check() and
>     kvm_vcpu_init_traps() for protected VMs.
>   - Update selftest to remove test cases for ID register consistency.
>     checking across vCPUs and to add test cases for ID_AA64PFR0_EL1.GIC.
>
> v2: https://lore.kernel.org/all/20211103062520.1445832-1-reijiw@google.com/
>   - Remove unnecessary line breaks. [Andrew]
>   - Use @params for comments. [Andrew]
>   - Move arm64_check_features to arch/arm64/kvm/sys_regs.c and
>     change that KVM specific feature check function.  [Andrew]
>   - Remove unnecessary raz handling from __set_id_reg. [Andrew]
>   - Remove sys_val field from the initial id_reg_info and add it
>     in the later patch. [Andrew]
>   - Call id_reg->init() from id_reg_info_init(). [Andrew]
>   - Fix cpuid_feature_cap_perfmon_field() to convert 0xf to 0x0
>     (and use it in the following patches).
>   - Change kvm_vcpu_first_run_init to set has_run_once to false
>     when kvm_id_regs_consistency_check() fails.
>   - Add a patch to introduce id_reg_info for ID_AA64MMFR0_EL1,
>     which requires special validity checking for TGran*_2 fields.
>   - Add patches to introduce id_reg_info for ID_DFR1_EL1 and
>     ID_MMFR0_EL1, which are required due to arm64_check_features
>     implementation change.
>   - Add a new argument, which is a pointer to id_reg_info, for
>     id_reg_info's validate().
>
> v1: https://lore.kernel.org/all/20211012043535.500493-1-reijiw@google.com/
>
> [1] https://lore.kernel.org/kvmarm/20211010145636.1950948-1-tabba@google.com/
> [2] https://lore.kernel.org/kvm/20201102033422.657391-1-liangpeng10@huawei.com/
> [3] https://lore.kernel.org/all/20220104194918.373612-2-rananta@google.com/
>
> Reiji Watanabe (26):
>   KVM: arm64: Introduce a validation function for an ID register
>   KVM: arm64: Save ID registers' sanitized value per guest
>   KVM: arm64: Introduce struct id_reg_info
>   KVM: arm64: Make ID_AA64PFR0_EL1 writable
>   KVM: arm64: Make ID_AA64PFR1_EL1 writable
>   KVM: arm64: Make ID_AA64ISAR0_EL1 writable
>   KVM: arm64: Make ID_AA64ISAR1_EL1 writable
>   KVM: arm64: Make ID_AA64MMFR0_EL1 writable
>   KVM: arm64: Hide IMPLEMENTATION DEFINED PMU support for the guest
>   KVM: arm64: Make ID_AA64DFR0_EL1 writable
>   KVM: arm64: Make ID_DFR0_EL1 writable
>   KVM: arm64: Make MVFR1_EL1 writable
>   KVM: arm64: Make ID registers without id_reg_info writable
>   KVM: arm64: Add consistency checking for frac fields of ID registers
>   KVM: arm64: Introduce KVM_CAP_ARM_ID_REG_CONFIGURABLE capability
>   KVM: arm64: Add kunit test for ID register validation
>   KVM: arm64: Use vcpu->arch cptr_el2 to track value of cptr_el2 for VHE
>   KVM: arm64: Use vcpu->arch.mdcr_el2 to track value of mdcr_el2
>   KVM: arm64: Introduce framework to trap disabled features
>   KVM: arm64: Trap disabled features of ID_AA64PFR0_EL1
>   KVM: arm64: Trap disabled features of ID_AA64PFR1_EL1
>   KVM: arm64: Trap disabled features of ID_AA64DFR0_EL1
>   KVM: arm64: Trap disabled features of ID_AA64MMFR1_EL1
>   KVM: arm64: Trap disabled features of ID_AA64ISAR1_EL1
>   KVM: arm64: Add kunit test for trap initialization
>   KVM: arm64: selftests: Introduce id_reg_test
>
>  Documentation/virt/kvm/api.rst                |   12 +
>  arch/arm64/include/asm/cpufeature.h           |    3 +-
>  arch/arm64/include/asm/kvm_arm.h              |   32 +
>  arch/arm64/include/asm/kvm_host.h             |   19 +
>  arch/arm64/include/asm/sysreg.h               |    3 +
>  arch/arm64/kernel/cpufeature.c                |  228 +++
>  arch/arm64/kvm/.kunitconfig                   |    4 +
>  arch/arm64/kvm/Kconfig                        |   11 +
>  arch/arm64/kvm/arm.c                          |   24 +-
>  arch/arm64/kvm/debug.c                        |   13 +-
>  arch/arm64/kvm/hyp/vhe/switch.c               |   14 +-
>  arch/arm64/kvm/sys_regs.c                     | 1329 +++++++++++++++--
>  arch/arm64/kvm/sys_regs_test.c                | 1247 ++++++++++++++++
>  arch/arm64/kvm/vgic/vgic-init.c               |    5 +
>  include/uapi/linux/kvm.h                      |    1 +
>  tools/arch/arm64/include/asm/sysreg.h         |    1 +
>  tools/testing/selftests/kvm/.gitignore        |    1 +
>  tools/testing/selftests/kvm/Makefile          |    1 +
>  .../selftests/kvm/aarch64/id_reg_test.c       | 1239 +++++++++++++++
>  19 files changed, 4041 insertions(+), 146 deletions(-)
>  create mode 100644 arch/arm64/kvm/.kunitconfig
>  create mode 100644 arch/arm64/kvm/sys_regs_test.c
>  create mode 100644 tools/testing/selftests/kvm/aarch64/id_reg_test.c
>
>
> base-commit: d399b107ee49bf5ea0391bd7614d512809e927b0
> --
> 2.34.1.448.ga2b2bfdf31-goog
>
> _______________________________________________
> kvmarm mailing list
> kvmarm@lists.cs.columbia.edu
> https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 01/26] KVM: arm64: Introduce a validation function for an ID register
  2022-01-06  4:26   ` Reiji Watanabe
  (?)
@ 2022-01-24 16:20     ` Fuad Tabba
  -1 siblings, 0 replies; 201+ messages in thread
From: Fuad Tabba @ 2022-01-24 16:20 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, linux-arm-kernel

Hi Reiji,

...

> +
> +/*
> + * Override entries in @orig_ftrp with the ones in @new_ftrp when their shift
> + * fields match.  The last entry of @orig_ftrp and @new_ftrp must be
> + * ARM64_FTR_END (.width == 0).
> + */
> +static void arm64_ftr_reg_bits_overrite(struct arm64_ftr_bits *orig_ftrp,

s/overrite/override

> +                                       struct arm64_ftr_bits *new_ftrp)

Should this be const struct arm64_ftr_bits *new_ftrp, which would also
make it consistent with copy_arm64_ftr_bits()?

> +{
> +       struct arm64_ftr_bits *o_ftrp, *n_ftrp;
> +
> +       for (n_ftrp = new_ftrp; n_ftrp->width; n_ftrp++) {
> +               for (o_ftrp = orig_ftrp; o_ftrp->width; o_ftrp++) {
> +                       if (o_ftrp->shift == n_ftrp->shift) {
> +                               *o_ftrp = *n_ftrp;
> +                               break;
> +                       }
> +               }
> +       }
> +}
> +

...

> +/*
> + * Initialize arm64_ftr_bits_kvm.  Copy arm64_ftr_bits for each ID register
> + * from arm64_ftr_regs to arm64_ftr_bits_kvm, and then override entries in
> + * arm64_ftr_bits_kvm with ones in arm64_ftr_bits_kvm_override.
> + */
> +static int init_arm64_ftr_bits_kvm(void)
> +{
> +       struct arm64_ftr_bits ftr_temp[MAX_FTR_BITS_LEN];
> +       static struct __ftr_reg_bits_entry *reg_bits_array, *bits, *o_bits;
> +       int i, j, nent, ret;
> +
> +       mutex_lock(&arm64_ftr_bits_kvm_lock);
> +       if (arm64_ftr_bits_kvm) {
> +               /* Already initialized */
> +               ret = 0;
> +               goto unlock_exit;
> +       }
> +
> +       nent = ARRAY_SIZE(arm64_ftr_regs);
> +       reg_bits_array = kcalloc(nent, sizeof(struct __ftr_reg_bits_entry),
> +                                GFP_KERNEL);
> +       if (!reg_bits_array) {
> +               ret = ENOMEM;

Should this be -ENOMEM?


> +               goto unlock_exit;
> +       }
> +
> +       /* Copy entries from arm64_ftr_regs to reg_bits_array */
> +       for (i = 0; i < nent; i++) {
> +               bits = &reg_bits_array[i];
> +               bits->sys_id = arm64_ftr_regs[i].sys_id;
> +               bits->ftr_bits = (struct arm64_ftr_bits *)arm64_ftr_regs[i].reg->ftr_bits;
> +       };
> +
> +       /*
> +        * Override the entries in reg_bits_array with the ones in
> +        * arm64_ftr_bits_kvm_override.
> +        */
> +       for (i = 0; i < ARRAY_SIZE(arm64_ftr_bits_kvm_override); i++) {
> +               o_bits = &arm64_ftr_bits_kvm_override[i];
> +               for (j = 0; j < nent; j++) {
> +                       bits = &reg_bits_array[j];
> +                       if (bits->sys_id != o_bits->sys_id)
> +                               continue;
> +
> +                       memset(ftr_temp, 0, sizeof(ftr_temp));
> +
> +                       /*
> +                        * Temporary save all entries in o_bits->ftr_bits
> +                        * to ftr_temp.
> +                        */
> +                       copy_arm64_ftr_bits(ftr_temp, o_bits->ftr_bits);
> +
> +                       /*
> +                        * Copy entries from bits->ftr_bits to o_bits->ftr_bits.
> +                        */
> +                       copy_arm64_ftr_bits(o_bits->ftr_bits, bits->ftr_bits);
> +
> +                       /*
> +                        * Override entries in o_bits->ftr_bits with the
> +                        * saved ones, and update bits->ftr_bits with
> +                        * o_bits->ftr_bits.
> +                        */
> +                       arm64_ftr_reg_bits_overrite(o_bits->ftr_bits, ftr_temp);
> +                       bits->ftr_bits = o_bits->ftr_bits;
> +                       break;
> +               }
> +       }

Could you please explain using ftr_temp[] and changing the value in
arm64_ftr_bits_kvm_override, rather than just
arm64_ftr_reg_bits_overrite(bits->ftr_bits, o_bits->ftr_bits)?


> +static const struct arm64_ftr_bits *get_arm64_ftr_bits_kvm(u32 sys_id)
> +{
> +       const struct __ftr_reg_bits_entry *ret;
> +       int err;
> +
> +       if (!arm64_ftr_bits_kvm) {
> +               /* arm64_ftr_bits_kvm is not initialized yet. */
> +               err = init_arm64_ftr_bits_kvm();

Rather than doing this check, can we just initialize it earlier, maybe
(indirectly) via kvm_arch_init_vm() or around the same time?


> +               if (err)
> +                       return NULL;
> +       }
> +
> +       ret = bsearch((const void *)(unsigned long)sys_id,
> +                     arm64_ftr_bits_kvm,
> +                     arm64_ftr_bits_kvm_nentries,
> +                     sizeof(arm64_ftr_bits_kvm[0]),
> +                     search_cmp_ftr_reg_bits);
> +       if (ret)
> +               return ret->ftr_bits;
> +
> +       return NULL;
> +}
> +
> +/*
> + * Check if features (or levels of features) that are indicated in the ID
> + * register value @val are also indicated in @limit.
> + * This function is for KVM to check if features that are indicated in @val,
> + * which will be used as the ID register value for its guest, are supported
> + * on the host.
> + * For AA64MMFR0_EL1.TGranX_2 fields, which don't follow the standard ID
> + * scheme, the function checks if values of the fields in @val are the same
> + * as the ones in @limit.
> + */
> +int arm64_check_features(u32 sys_reg, u64 val, u64 limit)
> +{
> +       const struct arm64_ftr_bits *ftrp = get_arm64_ftr_bits_kvm(sys_reg);
> +       u64 exposed_mask = 0;
> +
> +       if (!ftrp)
> +               return -ENOENT;
> +
> +       for (; ftrp->width; ftrp++) {
> +               s64 ftr_val = arm64_ftr_value(ftrp, val);
> +               s64 ftr_lim = arm64_ftr_value(ftrp, limit);
> +
> +               exposed_mask |= arm64_ftr_mask(ftrp);
> +
> +               if (ftr_val == ftr_lim)
> +                       continue;

At first I thought that this check isn't necessary, it should be
covered by the check below (arm64_ftr_safe_value. However, I think
that it's needed for the FTR_HIGHER_OR_ZERO_SAFE case. If my
understanding is correct, it might be worth adding a comment, or even
encapsulating this logic in a arm64_is_safe_value() function for
clarity.

> +
> +               if (ftr_val != arm64_ftr_safe_value(ftrp, ftr_val, ftr_lim))
> +                       return -E2BIG;
> +       }
> +
> +       /* Make sure that no unrecognized fields are set in @val. */
> +       if (val & ~exposed_mask)
> +               return -E2BIG;
> +
> +       return 0;
> +}

Thanks,
/fuad








> +#endif /* CONFIG_KVM */
> --
> 2.34.1.448.ga2b2bfdf31-goog
>
> _______________________________________________
> kvmarm mailing list
> kvmarm@lists.cs.columbia.edu
> https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 01/26] KVM: arm64: Introduce a validation function for an ID register
@ 2022-01-24 16:20     ` Fuad Tabba
  0 siblings, 0 replies; 201+ messages in thread
From: Fuad Tabba @ 2022-01-24 16:20 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: kvm, Marc Zyngier, Peter Shier, Paolo Bonzini, Will Deacon,
	kvmarm, linux-arm-kernel

Hi Reiji,

...

> +
> +/*
> + * Override entries in @orig_ftrp with the ones in @new_ftrp when their shift
> + * fields match.  The last entry of @orig_ftrp and @new_ftrp must be
> + * ARM64_FTR_END (.width == 0).
> + */
> +static void arm64_ftr_reg_bits_overrite(struct arm64_ftr_bits *orig_ftrp,

s/overrite/override

> +                                       struct arm64_ftr_bits *new_ftrp)

Should this be const struct arm64_ftr_bits *new_ftrp, which would also
make it consistent with copy_arm64_ftr_bits()?

> +{
> +       struct arm64_ftr_bits *o_ftrp, *n_ftrp;
> +
> +       for (n_ftrp = new_ftrp; n_ftrp->width; n_ftrp++) {
> +               for (o_ftrp = orig_ftrp; o_ftrp->width; o_ftrp++) {
> +                       if (o_ftrp->shift == n_ftrp->shift) {
> +                               *o_ftrp = *n_ftrp;
> +                               break;
> +                       }
> +               }
> +       }
> +}
> +

...

> +/*
> + * Initialize arm64_ftr_bits_kvm.  Copy arm64_ftr_bits for each ID register
> + * from arm64_ftr_regs to arm64_ftr_bits_kvm, and then override entries in
> + * arm64_ftr_bits_kvm with ones in arm64_ftr_bits_kvm_override.
> + */
> +static int init_arm64_ftr_bits_kvm(void)
> +{
> +       struct arm64_ftr_bits ftr_temp[MAX_FTR_BITS_LEN];
> +       static struct __ftr_reg_bits_entry *reg_bits_array, *bits, *o_bits;
> +       int i, j, nent, ret;
> +
> +       mutex_lock(&arm64_ftr_bits_kvm_lock);
> +       if (arm64_ftr_bits_kvm) {
> +               /* Already initialized */
> +               ret = 0;
> +               goto unlock_exit;
> +       }
> +
> +       nent = ARRAY_SIZE(arm64_ftr_regs);
> +       reg_bits_array = kcalloc(nent, sizeof(struct __ftr_reg_bits_entry),
> +                                GFP_KERNEL);
> +       if (!reg_bits_array) {
> +               ret = ENOMEM;

Should this be -ENOMEM?


> +               goto unlock_exit;
> +       }
> +
> +       /* Copy entries from arm64_ftr_regs to reg_bits_array */
> +       for (i = 0; i < nent; i++) {
> +               bits = &reg_bits_array[i];
> +               bits->sys_id = arm64_ftr_regs[i].sys_id;
> +               bits->ftr_bits = (struct arm64_ftr_bits *)arm64_ftr_regs[i].reg->ftr_bits;
> +       };
> +
> +       /*
> +        * Override the entries in reg_bits_array with the ones in
> +        * arm64_ftr_bits_kvm_override.
> +        */
> +       for (i = 0; i < ARRAY_SIZE(arm64_ftr_bits_kvm_override); i++) {
> +               o_bits = &arm64_ftr_bits_kvm_override[i];
> +               for (j = 0; j < nent; j++) {
> +                       bits = &reg_bits_array[j];
> +                       if (bits->sys_id != o_bits->sys_id)
> +                               continue;
> +
> +                       memset(ftr_temp, 0, sizeof(ftr_temp));
> +
> +                       /*
> +                        * Temporary save all entries in o_bits->ftr_bits
> +                        * to ftr_temp.
> +                        */
> +                       copy_arm64_ftr_bits(ftr_temp, o_bits->ftr_bits);
> +
> +                       /*
> +                        * Copy entries from bits->ftr_bits to o_bits->ftr_bits.
> +                        */
> +                       copy_arm64_ftr_bits(o_bits->ftr_bits, bits->ftr_bits);
> +
> +                       /*
> +                        * Override entries in o_bits->ftr_bits with the
> +                        * saved ones, and update bits->ftr_bits with
> +                        * o_bits->ftr_bits.
> +                        */
> +                       arm64_ftr_reg_bits_overrite(o_bits->ftr_bits, ftr_temp);
> +                       bits->ftr_bits = o_bits->ftr_bits;
> +                       break;
> +               }
> +       }

Could you please explain using ftr_temp[] and changing the value in
arm64_ftr_bits_kvm_override, rather than just
arm64_ftr_reg_bits_overrite(bits->ftr_bits, o_bits->ftr_bits)?


> +static const struct arm64_ftr_bits *get_arm64_ftr_bits_kvm(u32 sys_id)
> +{
> +       const struct __ftr_reg_bits_entry *ret;
> +       int err;
> +
> +       if (!arm64_ftr_bits_kvm) {
> +               /* arm64_ftr_bits_kvm is not initialized yet. */
> +               err = init_arm64_ftr_bits_kvm();

Rather than doing this check, can we just initialize it earlier, maybe
(indirectly) via kvm_arch_init_vm() or around the same time?


> +               if (err)
> +                       return NULL;
> +       }
> +
> +       ret = bsearch((const void *)(unsigned long)sys_id,
> +                     arm64_ftr_bits_kvm,
> +                     arm64_ftr_bits_kvm_nentries,
> +                     sizeof(arm64_ftr_bits_kvm[0]),
> +                     search_cmp_ftr_reg_bits);
> +       if (ret)
> +               return ret->ftr_bits;
> +
> +       return NULL;
> +}
> +
> +/*
> + * Check if features (or levels of features) that are indicated in the ID
> + * register value @val are also indicated in @limit.
> + * This function is for KVM to check if features that are indicated in @val,
> + * which will be used as the ID register value for its guest, are supported
> + * on the host.
> + * For AA64MMFR0_EL1.TGranX_2 fields, which don't follow the standard ID
> + * scheme, the function checks if values of the fields in @val are the same
> + * as the ones in @limit.
> + */
> +int arm64_check_features(u32 sys_reg, u64 val, u64 limit)
> +{
> +       const struct arm64_ftr_bits *ftrp = get_arm64_ftr_bits_kvm(sys_reg);
> +       u64 exposed_mask = 0;
> +
> +       if (!ftrp)
> +               return -ENOENT;
> +
> +       for (; ftrp->width; ftrp++) {
> +               s64 ftr_val = arm64_ftr_value(ftrp, val);
> +               s64 ftr_lim = arm64_ftr_value(ftrp, limit);
> +
> +               exposed_mask |= arm64_ftr_mask(ftrp);
> +
> +               if (ftr_val == ftr_lim)
> +                       continue;

At first I thought that this check isn't necessary, it should be
covered by the check below (arm64_ftr_safe_value. However, I think
that it's needed for the FTR_HIGHER_OR_ZERO_SAFE case. If my
understanding is correct, it might be worth adding a comment, or even
encapsulating this logic in a arm64_is_safe_value() function for
clarity.

> +
> +               if (ftr_val != arm64_ftr_safe_value(ftrp, ftr_val, ftr_lim))
> +                       return -E2BIG;
> +       }
> +
> +       /* Make sure that no unrecognized fields are set in @val. */
> +       if (val & ~exposed_mask)
> +               return -E2BIG;
> +
> +       return 0;
> +}

Thanks,
/fuad








> +#endif /* CONFIG_KVM */
> --
> 2.34.1.448.ga2b2bfdf31-goog
>
> _______________________________________________
> kvmarm mailing list
> kvmarm@lists.cs.columbia.edu
> https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 01/26] KVM: arm64: Introduce a validation function for an ID register
@ 2022-01-24 16:20     ` Fuad Tabba
  0 siblings, 0 replies; 201+ messages in thread
From: Fuad Tabba @ 2022-01-24 16:20 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, linux-arm-kernel

Hi Reiji,

...

> +
> +/*
> + * Override entries in @orig_ftrp with the ones in @new_ftrp when their shift
> + * fields match.  The last entry of @orig_ftrp and @new_ftrp must be
> + * ARM64_FTR_END (.width == 0).
> + */
> +static void arm64_ftr_reg_bits_overrite(struct arm64_ftr_bits *orig_ftrp,

s/overrite/override

> +                                       struct arm64_ftr_bits *new_ftrp)

Should this be const struct arm64_ftr_bits *new_ftrp, which would also
make it consistent with copy_arm64_ftr_bits()?

> +{
> +       struct arm64_ftr_bits *o_ftrp, *n_ftrp;
> +
> +       for (n_ftrp = new_ftrp; n_ftrp->width; n_ftrp++) {
> +               for (o_ftrp = orig_ftrp; o_ftrp->width; o_ftrp++) {
> +                       if (o_ftrp->shift == n_ftrp->shift) {
> +                               *o_ftrp = *n_ftrp;
> +                               break;
> +                       }
> +               }
> +       }
> +}
> +

...

> +/*
> + * Initialize arm64_ftr_bits_kvm.  Copy arm64_ftr_bits for each ID register
> + * from arm64_ftr_regs to arm64_ftr_bits_kvm, and then override entries in
> + * arm64_ftr_bits_kvm with ones in arm64_ftr_bits_kvm_override.
> + */
> +static int init_arm64_ftr_bits_kvm(void)
> +{
> +       struct arm64_ftr_bits ftr_temp[MAX_FTR_BITS_LEN];
> +       static struct __ftr_reg_bits_entry *reg_bits_array, *bits, *o_bits;
> +       int i, j, nent, ret;
> +
> +       mutex_lock(&arm64_ftr_bits_kvm_lock);
> +       if (arm64_ftr_bits_kvm) {
> +               /* Already initialized */
> +               ret = 0;
> +               goto unlock_exit;
> +       }
> +
> +       nent = ARRAY_SIZE(arm64_ftr_regs);
> +       reg_bits_array = kcalloc(nent, sizeof(struct __ftr_reg_bits_entry),
> +                                GFP_KERNEL);
> +       if (!reg_bits_array) {
> +               ret = ENOMEM;

Should this be -ENOMEM?


> +               goto unlock_exit;
> +       }
> +
> +       /* Copy entries from arm64_ftr_regs to reg_bits_array */
> +       for (i = 0; i < nent; i++) {
> +               bits = &reg_bits_array[i];
> +               bits->sys_id = arm64_ftr_regs[i].sys_id;
> +               bits->ftr_bits = (struct arm64_ftr_bits *)arm64_ftr_regs[i].reg->ftr_bits;
> +       };
> +
> +       /*
> +        * Override the entries in reg_bits_array with the ones in
> +        * arm64_ftr_bits_kvm_override.
> +        */
> +       for (i = 0; i < ARRAY_SIZE(arm64_ftr_bits_kvm_override); i++) {
> +               o_bits = &arm64_ftr_bits_kvm_override[i];
> +               for (j = 0; j < nent; j++) {
> +                       bits = &reg_bits_array[j];
> +                       if (bits->sys_id != o_bits->sys_id)
> +                               continue;
> +
> +                       memset(ftr_temp, 0, sizeof(ftr_temp));
> +
> +                       /*
> +                        * Temporary save all entries in o_bits->ftr_bits
> +                        * to ftr_temp.
> +                        */
> +                       copy_arm64_ftr_bits(ftr_temp, o_bits->ftr_bits);
> +
> +                       /*
> +                        * Copy entries from bits->ftr_bits to o_bits->ftr_bits.
> +                        */
> +                       copy_arm64_ftr_bits(o_bits->ftr_bits, bits->ftr_bits);
> +
> +                       /*
> +                        * Override entries in o_bits->ftr_bits with the
> +                        * saved ones, and update bits->ftr_bits with
> +                        * o_bits->ftr_bits.
> +                        */
> +                       arm64_ftr_reg_bits_overrite(o_bits->ftr_bits, ftr_temp);
> +                       bits->ftr_bits = o_bits->ftr_bits;
> +                       break;
> +               }
> +       }

Could you please explain using ftr_temp[] and changing the value in
arm64_ftr_bits_kvm_override, rather than just
arm64_ftr_reg_bits_overrite(bits->ftr_bits, o_bits->ftr_bits)?


> +static const struct arm64_ftr_bits *get_arm64_ftr_bits_kvm(u32 sys_id)
> +{
> +       const struct __ftr_reg_bits_entry *ret;
> +       int err;
> +
> +       if (!arm64_ftr_bits_kvm) {
> +               /* arm64_ftr_bits_kvm is not initialized yet. */
> +               err = init_arm64_ftr_bits_kvm();

Rather than doing this check, can we just initialize it earlier, maybe
(indirectly) via kvm_arch_init_vm() or around the same time?


> +               if (err)
> +                       return NULL;
> +       }
> +
> +       ret = bsearch((const void *)(unsigned long)sys_id,
> +                     arm64_ftr_bits_kvm,
> +                     arm64_ftr_bits_kvm_nentries,
> +                     sizeof(arm64_ftr_bits_kvm[0]),
> +                     search_cmp_ftr_reg_bits);
> +       if (ret)
> +               return ret->ftr_bits;
> +
> +       return NULL;
> +}
> +
> +/*
> + * Check if features (or levels of features) that are indicated in the ID
> + * register value @val are also indicated in @limit.
> + * This function is for KVM to check if features that are indicated in @val,
> + * which will be used as the ID register value for its guest, are supported
> + * on the host.
> + * For AA64MMFR0_EL1.TGranX_2 fields, which don't follow the standard ID
> + * scheme, the function checks if values of the fields in @val are the same
> + * as the ones in @limit.
> + */
> +int arm64_check_features(u32 sys_reg, u64 val, u64 limit)
> +{
> +       const struct arm64_ftr_bits *ftrp = get_arm64_ftr_bits_kvm(sys_reg);
> +       u64 exposed_mask = 0;
> +
> +       if (!ftrp)
> +               return -ENOENT;
> +
> +       for (; ftrp->width; ftrp++) {
> +               s64 ftr_val = arm64_ftr_value(ftrp, val);
> +               s64 ftr_lim = arm64_ftr_value(ftrp, limit);
> +
> +               exposed_mask |= arm64_ftr_mask(ftrp);
> +
> +               if (ftr_val == ftr_lim)
> +                       continue;

At first I thought that this check isn't necessary, it should be
covered by the check below (arm64_ftr_safe_value. However, I think
that it's needed for the FTR_HIGHER_OR_ZERO_SAFE case. If my
understanding is correct, it might be worth adding a comment, or even
encapsulating this logic in a arm64_is_safe_value() function for
clarity.

> +
> +               if (ftr_val != arm64_ftr_safe_value(ftrp, ftr_val, ftr_lim))
> +                       return -E2BIG;
> +       }
> +
> +       /* Make sure that no unrecognized fields are set in @val. */
> +       if (val & ~exposed_mask)
> +               return -E2BIG;
> +
> +       return 0;
> +}

Thanks,
/fuad








> +#endif /* CONFIG_KVM */
> --
> 2.34.1.448.ga2b2bfdf31-goog
>
> _______________________________________________
> kvmarm mailing list
> kvmarm@lists.cs.columbia.edu
> https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 02/26] KVM: arm64: Save ID registers' sanitized value per guest
  2022-01-06  4:26   ` Reiji Watanabe
  (?)
@ 2022-01-24 16:21     ` Fuad Tabba
  -1 siblings, 0 replies; 201+ messages in thread
From: Fuad Tabba @ 2022-01-24 16:21 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, linux-arm-kernel

Hi Reiji,

On Thu, Jan 6, 2022 at 4:28 AM Reiji Watanabe <reijiw@google.com> wrote:
>
> Introduce id_regs[] in kvm_arch as a storage of guest's ID registers,
> and save ID registers' sanitized value in the array at KVM_CREATE_VM.
> Use the saved ones when ID registers are read by the guest or
> userspace (via KVM_GET_ONE_REG).
>
> Signed-off-by: Reiji Watanabe <reijiw@google.com>
> ---
>  arch/arm64/include/asm/kvm_host.h | 16 ++++++++
>  arch/arm64/kvm/arm.c              |  1 +
>  arch/arm64/kvm/sys_regs.c         | 62 ++++++++++++++++++++++---------
>  3 files changed, 62 insertions(+), 17 deletions(-)
>
> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index 2a5f7f38006f..c789a0137f58 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -102,6 +102,17 @@ struct kvm_s2_mmu {
>  struct kvm_arch_memory_slot {
>  };
>
> +/*
> + * (Op0, Op1, CRn, CRm, Op2) of ID registers is (3, 0, 0, crm, op2),
> + * where 0<=crm<8, 0<=op2<8.
> + */
> +#define KVM_ARM_ID_REG_MAX_NUM 64
> +#define IDREG_IDX(id)          ((sys_reg_CRm(id) << 3) | sys_reg_Op2(id))
> +#define is_id_reg(id)  \
> +       (sys_reg_Op0(id) == 3 && sys_reg_Op1(id) == 0 &&        \
> +        sys_reg_CRn(id) == 0 && sys_reg_CRm(id) >= 0 &&        \
> +        sys_reg_CRm(id) < 8)
> +

This is consistent with the Arm ARM "Table D12-2 System instruction
encodings for non-Debug System register accesses".

Minor nit, would it be better to have IDREG_IDX and is_id_reg in
arch/arm64/kvm/sys_regs.h, since other similar and related ones are
there?

>  struct kvm_arch {
>         struct kvm_s2_mmu mmu;
>
> @@ -137,6 +148,9 @@ struct kvm_arch {
>
>         /* Memory Tagging Extension enabled for the guest */
>         bool mte_enabled;
> +
> +       /* ID registers for the guest. */
> +       u64 id_regs[KVM_ARM_ID_REG_MAX_NUM];
>  };
>
>  struct kvm_vcpu_fault_info {
> @@ -734,6 +748,8 @@ int kvm_arm_vcpu_arch_has_attr(struct kvm_vcpu *vcpu,
>  long kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
>                                 struct kvm_arm_copy_mte_tags *copy_tags);
>
> +void set_default_id_regs(struct kvm *kvm);
> +
>  /* Guest/host FPSIMD coordination helpers */
>  int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu);
>  void kvm_arch_vcpu_load_fp(struct kvm_vcpu *vcpu);
> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> index e4727dc771bf..5f497a0af254 100644
> --- a/arch/arm64/kvm/arm.c
> +++ b/arch/arm64/kvm/arm.c
> @@ -156,6 +156,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
>         kvm->arch.max_vcpus = kvm_arm_default_max_vcpus();
>
>         set_default_spectre(kvm);
> +       set_default_id_regs(kvm);
>
>         return ret;
>  out_free_stage2_pgd:
> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> index e3ec1a44f94d..80dc62f98ef0 100644
> --- a/arch/arm64/kvm/sys_regs.c
> +++ b/arch/arm64/kvm/sys_regs.c
> @@ -33,6 +33,8 @@
>
>  #include "trace.h"
>
> +static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id);
> +
>  /*
>   * All of this file is extremely similar to the ARM coproc.c, but the
>   * types are different. My gut feeling is that it should be pretty
> @@ -273,7 +275,7 @@ static bool trap_loregion(struct kvm_vcpu *vcpu,
>                           struct sys_reg_params *p,
>                           const struct sys_reg_desc *r)
>  {
> -       u64 val = read_sanitised_ftr_reg(SYS_ID_AA64MMFR1_EL1);
> +       u64 val = __read_id_reg(vcpu, SYS_ID_AA64MMFR1_EL1);
>         u32 sr = reg_to_encoding(r);
>
>         if (!(val & (0xfUL << ID_AA64MMFR1_LOR_SHIFT))) {
> @@ -1059,17 +1061,9 @@ static bool access_arch_timer(struct kvm_vcpu *vcpu,
>         return true;
>  }
>
> -/* Read a sanitised cpufeature ID register by sys_reg_desc */
> -static u64 read_id_reg(const struct kvm_vcpu *vcpu,
> -               struct sys_reg_desc const *r, bool raz)
> +static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
>  {
> -       u32 id = reg_to_encoding(r);
> -       u64 val;
> -
> -       if (raz)
> -               return 0;
> -
> -       val = read_sanitised_ftr_reg(id);
> +       u64 val = vcpu->kvm->arch.id_regs[IDREG_IDX(id)];
>
>         switch (id) {
>         case SYS_ID_AA64PFR0_EL1:
> @@ -1119,6 +1113,14 @@ static u64 read_id_reg(const struct kvm_vcpu *vcpu,
>         return val;
>  }
>
> +static u64 read_id_reg(const struct kvm_vcpu *vcpu,
> +                      struct sys_reg_desc const *r, bool raz)
> +{
> +       u32 id = reg_to_encoding(r);
> +
> +       return raz ? 0 : __read_id_reg(vcpu, id);
> +}
> +
>  static unsigned int id_visibility(const struct kvm_vcpu *vcpu,
>                                   const struct sys_reg_desc *r)
>  {
> @@ -1223,9 +1225,8 @@ static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
>  /*
>   * cpufeature ID register user accessors
>   *
> - * For now, these registers are immutable for userspace, so no values
> - * are stored, and for set_id_reg() we don't allow the effective value
> - * to be changed.
> + * For now, these registers are immutable for userspace, so for set_id_reg()
> + * we don't allow the effective value to be changed.
>   */
>  static int __get_id_reg(const struct kvm_vcpu *vcpu,
>                         const struct sys_reg_desc *rd, void __user *uaddr,
> @@ -1237,7 +1238,7 @@ static int __get_id_reg(const struct kvm_vcpu *vcpu,
>         return reg_to_user(uaddr, &val, id);
>  }
>
> -static int __set_id_reg(const struct kvm_vcpu *vcpu,
> +static int __set_id_reg(struct kvm_vcpu *vcpu,
>                         const struct sys_reg_desc *rd, void __user *uaddr,
>                         bool raz)

Minor nit: why remove the const in this patch? This is required for a
future patch but not for this one.

Thanks,
/fuad


>  {
> @@ -1837,8 +1838,8 @@ static bool trap_dbgdidr(struct kvm_vcpu *vcpu,
>         if (p->is_write) {
>                 return ignore_write(vcpu, p);
>         } else {
> -               u64 dfr = read_sanitised_ftr_reg(SYS_ID_AA64DFR0_EL1);
> -               u64 pfr = read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1);
> +               u64 dfr = __read_id_reg(vcpu, SYS_ID_AA64DFR0_EL1);
> +               u64 pfr = __read_id_reg(vcpu, SYS_ID_AA64PFR0_EL1);
>                 u32 el3 = !!cpuid_feature_extract_unsigned_field(pfr, ID_AA64PFR0_EL3_SHIFT);
>
>                 p->regval = ((((dfr >> ID_AA64DFR0_WRPS_SHIFT) & 0xf) << 28) |
> @@ -2850,3 +2851,30 @@ void kvm_sys_reg_table_init(void)
>         /* Clear all higher bits. */
>         cache_levels &= (1 << (i*3))-1;
>  }
> +
> +/*
> + * Set the guest's ID registers that are defined in sys_reg_descs[]
> + * with ID_SANITISED() to the host's sanitized value.
> + */
> +void set_default_id_regs(struct kvm *kvm)
> +{
> +       int i;
> +       u32 id;
> +       const struct sys_reg_desc *rd;
> +       u64 val;
> +
> +       for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
> +               rd = &sys_reg_descs[i];
> +               if (rd->access != access_id_reg)
> +                       /* Not ID register, or hidden/reserved ID register */
> +                       continue;
> +
> +               id = reg_to_encoding(rd);
> +               if (WARN_ON_ONCE(!is_id_reg(id)))
> +                       /* Shouldn't happen */
> +                       continue;
> +
> +               val = read_sanitised_ftr_reg(id);
> +               kvm->arch.id_regs[IDREG_IDX(id)] = val;
> +       }
> +}
> --
> 2.34.1.448.ga2b2bfdf31-goog
>
> _______________________________________________
> kvmarm mailing list
> kvmarm@lists.cs.columbia.edu
> https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 02/26] KVM: arm64: Save ID registers' sanitized value per guest
@ 2022-01-24 16:21     ` Fuad Tabba
  0 siblings, 0 replies; 201+ messages in thread
From: Fuad Tabba @ 2022-01-24 16:21 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: kvm, Marc Zyngier, Peter Shier, Paolo Bonzini, Will Deacon,
	kvmarm, linux-arm-kernel

Hi Reiji,

On Thu, Jan 6, 2022 at 4:28 AM Reiji Watanabe <reijiw@google.com> wrote:
>
> Introduce id_regs[] in kvm_arch as a storage of guest's ID registers,
> and save ID registers' sanitized value in the array at KVM_CREATE_VM.
> Use the saved ones when ID registers are read by the guest or
> userspace (via KVM_GET_ONE_REG).
>
> Signed-off-by: Reiji Watanabe <reijiw@google.com>
> ---
>  arch/arm64/include/asm/kvm_host.h | 16 ++++++++
>  arch/arm64/kvm/arm.c              |  1 +
>  arch/arm64/kvm/sys_regs.c         | 62 ++++++++++++++++++++++---------
>  3 files changed, 62 insertions(+), 17 deletions(-)
>
> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index 2a5f7f38006f..c789a0137f58 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -102,6 +102,17 @@ struct kvm_s2_mmu {
>  struct kvm_arch_memory_slot {
>  };
>
> +/*
> + * (Op0, Op1, CRn, CRm, Op2) of ID registers is (3, 0, 0, crm, op2),
> + * where 0<=crm<8, 0<=op2<8.
> + */
> +#define KVM_ARM_ID_REG_MAX_NUM 64
> +#define IDREG_IDX(id)          ((sys_reg_CRm(id) << 3) | sys_reg_Op2(id))
> +#define is_id_reg(id)  \
> +       (sys_reg_Op0(id) == 3 && sys_reg_Op1(id) == 0 &&        \
> +        sys_reg_CRn(id) == 0 && sys_reg_CRm(id) >= 0 &&        \
> +        sys_reg_CRm(id) < 8)
> +

This is consistent with the Arm ARM "Table D12-2 System instruction
encodings for non-Debug System register accesses".

Minor nit, would it be better to have IDREG_IDX and is_id_reg in
arch/arm64/kvm/sys_regs.h, since other similar and related ones are
there?

>  struct kvm_arch {
>         struct kvm_s2_mmu mmu;
>
> @@ -137,6 +148,9 @@ struct kvm_arch {
>
>         /* Memory Tagging Extension enabled for the guest */
>         bool mte_enabled;
> +
> +       /* ID registers for the guest. */
> +       u64 id_regs[KVM_ARM_ID_REG_MAX_NUM];
>  };
>
>  struct kvm_vcpu_fault_info {
> @@ -734,6 +748,8 @@ int kvm_arm_vcpu_arch_has_attr(struct kvm_vcpu *vcpu,
>  long kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
>                                 struct kvm_arm_copy_mte_tags *copy_tags);
>
> +void set_default_id_regs(struct kvm *kvm);
> +
>  /* Guest/host FPSIMD coordination helpers */
>  int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu);
>  void kvm_arch_vcpu_load_fp(struct kvm_vcpu *vcpu);
> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> index e4727dc771bf..5f497a0af254 100644
> --- a/arch/arm64/kvm/arm.c
> +++ b/arch/arm64/kvm/arm.c
> @@ -156,6 +156,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
>         kvm->arch.max_vcpus = kvm_arm_default_max_vcpus();
>
>         set_default_spectre(kvm);
> +       set_default_id_regs(kvm);
>
>         return ret;
>  out_free_stage2_pgd:
> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> index e3ec1a44f94d..80dc62f98ef0 100644
> --- a/arch/arm64/kvm/sys_regs.c
> +++ b/arch/arm64/kvm/sys_regs.c
> @@ -33,6 +33,8 @@
>
>  #include "trace.h"
>
> +static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id);
> +
>  /*
>   * All of this file is extremely similar to the ARM coproc.c, but the
>   * types are different. My gut feeling is that it should be pretty
> @@ -273,7 +275,7 @@ static bool trap_loregion(struct kvm_vcpu *vcpu,
>                           struct sys_reg_params *p,
>                           const struct sys_reg_desc *r)
>  {
> -       u64 val = read_sanitised_ftr_reg(SYS_ID_AA64MMFR1_EL1);
> +       u64 val = __read_id_reg(vcpu, SYS_ID_AA64MMFR1_EL1);
>         u32 sr = reg_to_encoding(r);
>
>         if (!(val & (0xfUL << ID_AA64MMFR1_LOR_SHIFT))) {
> @@ -1059,17 +1061,9 @@ static bool access_arch_timer(struct kvm_vcpu *vcpu,
>         return true;
>  }
>
> -/* Read a sanitised cpufeature ID register by sys_reg_desc */
> -static u64 read_id_reg(const struct kvm_vcpu *vcpu,
> -               struct sys_reg_desc const *r, bool raz)
> +static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
>  {
> -       u32 id = reg_to_encoding(r);
> -       u64 val;
> -
> -       if (raz)
> -               return 0;
> -
> -       val = read_sanitised_ftr_reg(id);
> +       u64 val = vcpu->kvm->arch.id_regs[IDREG_IDX(id)];
>
>         switch (id) {
>         case SYS_ID_AA64PFR0_EL1:
> @@ -1119,6 +1113,14 @@ static u64 read_id_reg(const struct kvm_vcpu *vcpu,
>         return val;
>  }
>
> +static u64 read_id_reg(const struct kvm_vcpu *vcpu,
> +                      struct sys_reg_desc const *r, bool raz)
> +{
> +       u32 id = reg_to_encoding(r);
> +
> +       return raz ? 0 : __read_id_reg(vcpu, id);
> +}
> +
>  static unsigned int id_visibility(const struct kvm_vcpu *vcpu,
>                                   const struct sys_reg_desc *r)
>  {
> @@ -1223,9 +1225,8 @@ static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
>  /*
>   * cpufeature ID register user accessors
>   *
> - * For now, these registers are immutable for userspace, so no values
> - * are stored, and for set_id_reg() we don't allow the effective value
> - * to be changed.
> + * For now, these registers are immutable for userspace, so for set_id_reg()
> + * we don't allow the effective value to be changed.
>   */
>  static int __get_id_reg(const struct kvm_vcpu *vcpu,
>                         const struct sys_reg_desc *rd, void __user *uaddr,
> @@ -1237,7 +1238,7 @@ static int __get_id_reg(const struct kvm_vcpu *vcpu,
>         return reg_to_user(uaddr, &val, id);
>  }
>
> -static int __set_id_reg(const struct kvm_vcpu *vcpu,
> +static int __set_id_reg(struct kvm_vcpu *vcpu,
>                         const struct sys_reg_desc *rd, void __user *uaddr,
>                         bool raz)

Minor nit: why remove the const in this patch? This is required for a
future patch but not for this one.

Thanks,
/fuad


>  {
> @@ -1837,8 +1838,8 @@ static bool trap_dbgdidr(struct kvm_vcpu *vcpu,
>         if (p->is_write) {
>                 return ignore_write(vcpu, p);
>         } else {
> -               u64 dfr = read_sanitised_ftr_reg(SYS_ID_AA64DFR0_EL1);
> -               u64 pfr = read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1);
> +               u64 dfr = __read_id_reg(vcpu, SYS_ID_AA64DFR0_EL1);
> +               u64 pfr = __read_id_reg(vcpu, SYS_ID_AA64PFR0_EL1);
>                 u32 el3 = !!cpuid_feature_extract_unsigned_field(pfr, ID_AA64PFR0_EL3_SHIFT);
>
>                 p->regval = ((((dfr >> ID_AA64DFR0_WRPS_SHIFT) & 0xf) << 28) |
> @@ -2850,3 +2851,30 @@ void kvm_sys_reg_table_init(void)
>         /* Clear all higher bits. */
>         cache_levels &= (1 << (i*3))-1;
>  }
> +
> +/*
> + * Set the guest's ID registers that are defined in sys_reg_descs[]
> + * with ID_SANITISED() to the host's sanitized value.
> + */
> +void set_default_id_regs(struct kvm *kvm)
> +{
> +       int i;
> +       u32 id;
> +       const struct sys_reg_desc *rd;
> +       u64 val;
> +
> +       for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
> +               rd = &sys_reg_descs[i];
> +               if (rd->access != access_id_reg)
> +                       /* Not ID register, or hidden/reserved ID register */
> +                       continue;
> +
> +               id = reg_to_encoding(rd);
> +               if (WARN_ON_ONCE(!is_id_reg(id)))
> +                       /* Shouldn't happen */
> +                       continue;
> +
> +               val = read_sanitised_ftr_reg(id);
> +               kvm->arch.id_regs[IDREG_IDX(id)] = val;
> +       }
> +}
> --
> 2.34.1.448.ga2b2bfdf31-goog
>
> _______________________________________________
> kvmarm mailing list
> kvmarm@lists.cs.columbia.edu
> https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 02/26] KVM: arm64: Save ID registers' sanitized value per guest
@ 2022-01-24 16:21     ` Fuad Tabba
  0 siblings, 0 replies; 201+ messages in thread
From: Fuad Tabba @ 2022-01-24 16:21 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, linux-arm-kernel

Hi Reiji,

On Thu, Jan 6, 2022 at 4:28 AM Reiji Watanabe <reijiw@google.com> wrote:
>
> Introduce id_regs[] in kvm_arch as a storage of guest's ID registers,
> and save ID registers' sanitized value in the array at KVM_CREATE_VM.
> Use the saved ones when ID registers are read by the guest or
> userspace (via KVM_GET_ONE_REG).
>
> Signed-off-by: Reiji Watanabe <reijiw@google.com>
> ---
>  arch/arm64/include/asm/kvm_host.h | 16 ++++++++
>  arch/arm64/kvm/arm.c              |  1 +
>  arch/arm64/kvm/sys_regs.c         | 62 ++++++++++++++++++++++---------
>  3 files changed, 62 insertions(+), 17 deletions(-)
>
> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index 2a5f7f38006f..c789a0137f58 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -102,6 +102,17 @@ struct kvm_s2_mmu {
>  struct kvm_arch_memory_slot {
>  };
>
> +/*
> + * (Op0, Op1, CRn, CRm, Op2) of ID registers is (3, 0, 0, crm, op2),
> + * where 0<=crm<8, 0<=op2<8.
> + */
> +#define KVM_ARM_ID_REG_MAX_NUM 64
> +#define IDREG_IDX(id)          ((sys_reg_CRm(id) << 3) | sys_reg_Op2(id))
> +#define is_id_reg(id)  \
> +       (sys_reg_Op0(id) == 3 && sys_reg_Op1(id) == 0 &&        \
> +        sys_reg_CRn(id) == 0 && sys_reg_CRm(id) >= 0 &&        \
> +        sys_reg_CRm(id) < 8)
> +

This is consistent with the Arm ARM "Table D12-2 System instruction
encodings for non-Debug System register accesses".

Minor nit, would it be better to have IDREG_IDX and is_id_reg in
arch/arm64/kvm/sys_regs.h, since other similar and related ones are
there?

>  struct kvm_arch {
>         struct kvm_s2_mmu mmu;
>
> @@ -137,6 +148,9 @@ struct kvm_arch {
>
>         /* Memory Tagging Extension enabled for the guest */
>         bool mte_enabled;
> +
> +       /* ID registers for the guest. */
> +       u64 id_regs[KVM_ARM_ID_REG_MAX_NUM];
>  };
>
>  struct kvm_vcpu_fault_info {
> @@ -734,6 +748,8 @@ int kvm_arm_vcpu_arch_has_attr(struct kvm_vcpu *vcpu,
>  long kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
>                                 struct kvm_arm_copy_mte_tags *copy_tags);
>
> +void set_default_id_regs(struct kvm *kvm);
> +
>  /* Guest/host FPSIMD coordination helpers */
>  int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu);
>  void kvm_arch_vcpu_load_fp(struct kvm_vcpu *vcpu);
> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> index e4727dc771bf..5f497a0af254 100644
> --- a/arch/arm64/kvm/arm.c
> +++ b/arch/arm64/kvm/arm.c
> @@ -156,6 +156,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
>         kvm->arch.max_vcpus = kvm_arm_default_max_vcpus();
>
>         set_default_spectre(kvm);
> +       set_default_id_regs(kvm);
>
>         return ret;
>  out_free_stage2_pgd:
> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> index e3ec1a44f94d..80dc62f98ef0 100644
> --- a/arch/arm64/kvm/sys_regs.c
> +++ b/arch/arm64/kvm/sys_regs.c
> @@ -33,6 +33,8 @@
>
>  #include "trace.h"
>
> +static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id);
> +
>  /*
>   * All of this file is extremely similar to the ARM coproc.c, but the
>   * types are different. My gut feeling is that it should be pretty
> @@ -273,7 +275,7 @@ static bool trap_loregion(struct kvm_vcpu *vcpu,
>                           struct sys_reg_params *p,
>                           const struct sys_reg_desc *r)
>  {
> -       u64 val = read_sanitised_ftr_reg(SYS_ID_AA64MMFR1_EL1);
> +       u64 val = __read_id_reg(vcpu, SYS_ID_AA64MMFR1_EL1);
>         u32 sr = reg_to_encoding(r);
>
>         if (!(val & (0xfUL << ID_AA64MMFR1_LOR_SHIFT))) {
> @@ -1059,17 +1061,9 @@ static bool access_arch_timer(struct kvm_vcpu *vcpu,
>         return true;
>  }
>
> -/* Read a sanitised cpufeature ID register by sys_reg_desc */
> -static u64 read_id_reg(const struct kvm_vcpu *vcpu,
> -               struct sys_reg_desc const *r, bool raz)
> +static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
>  {
> -       u32 id = reg_to_encoding(r);
> -       u64 val;
> -
> -       if (raz)
> -               return 0;
> -
> -       val = read_sanitised_ftr_reg(id);
> +       u64 val = vcpu->kvm->arch.id_regs[IDREG_IDX(id)];
>
>         switch (id) {
>         case SYS_ID_AA64PFR0_EL1:
> @@ -1119,6 +1113,14 @@ static u64 read_id_reg(const struct kvm_vcpu *vcpu,
>         return val;
>  }
>
> +static u64 read_id_reg(const struct kvm_vcpu *vcpu,
> +                      struct sys_reg_desc const *r, bool raz)
> +{
> +       u32 id = reg_to_encoding(r);
> +
> +       return raz ? 0 : __read_id_reg(vcpu, id);
> +}
> +
>  static unsigned int id_visibility(const struct kvm_vcpu *vcpu,
>                                   const struct sys_reg_desc *r)
>  {
> @@ -1223,9 +1225,8 @@ static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
>  /*
>   * cpufeature ID register user accessors
>   *
> - * For now, these registers are immutable for userspace, so no values
> - * are stored, and for set_id_reg() we don't allow the effective value
> - * to be changed.
> + * For now, these registers are immutable for userspace, so for set_id_reg()
> + * we don't allow the effective value to be changed.
>   */
>  static int __get_id_reg(const struct kvm_vcpu *vcpu,
>                         const struct sys_reg_desc *rd, void __user *uaddr,
> @@ -1237,7 +1238,7 @@ static int __get_id_reg(const struct kvm_vcpu *vcpu,
>         return reg_to_user(uaddr, &val, id);
>  }
>
> -static int __set_id_reg(const struct kvm_vcpu *vcpu,
> +static int __set_id_reg(struct kvm_vcpu *vcpu,
>                         const struct sys_reg_desc *rd, void __user *uaddr,
>                         bool raz)

Minor nit: why remove the const in this patch? This is required for a
future patch but not for this one.

Thanks,
/fuad


>  {
> @@ -1837,8 +1838,8 @@ static bool trap_dbgdidr(struct kvm_vcpu *vcpu,
>         if (p->is_write) {
>                 return ignore_write(vcpu, p);
>         } else {
> -               u64 dfr = read_sanitised_ftr_reg(SYS_ID_AA64DFR0_EL1);
> -               u64 pfr = read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1);
> +               u64 dfr = __read_id_reg(vcpu, SYS_ID_AA64DFR0_EL1);
> +               u64 pfr = __read_id_reg(vcpu, SYS_ID_AA64PFR0_EL1);
>                 u32 el3 = !!cpuid_feature_extract_unsigned_field(pfr, ID_AA64PFR0_EL3_SHIFT);
>
>                 p->regval = ((((dfr >> ID_AA64DFR0_WRPS_SHIFT) & 0xf) << 28) |
> @@ -2850,3 +2851,30 @@ void kvm_sys_reg_table_init(void)
>         /* Clear all higher bits. */
>         cache_levels &= (1 << (i*3))-1;
>  }
> +
> +/*
> + * Set the guest's ID registers that are defined in sys_reg_descs[]
> + * with ID_SANITISED() to the host's sanitized value.
> + */
> +void set_default_id_regs(struct kvm *kvm)
> +{
> +       int i;
> +       u32 id;
> +       const struct sys_reg_desc *rd;
> +       u64 val;
> +
> +       for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
> +               rd = &sys_reg_descs[i];
> +               if (rd->access != access_id_reg)
> +                       /* Not ID register, or hidden/reserved ID register */
> +                       continue;
> +
> +               id = reg_to_encoding(rd);
> +               if (WARN_ON_ONCE(!is_id_reg(id)))
> +                       /* Shouldn't happen */
> +                       continue;
> +
> +               val = read_sanitised_ftr_reg(id);
> +               kvm->arch.id_regs[IDREG_IDX(id)] = val;
> +       }
> +}
> --
> 2.34.1.448.ga2b2bfdf31-goog
>
> _______________________________________________
> kvmarm mailing list
> kvmarm@lists.cs.columbia.edu
> https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 03/26] KVM: arm64: Introduce struct id_reg_info
  2022-01-06  4:26   ` Reiji Watanabe
  (?)
@ 2022-01-24 16:28     ` Fuad Tabba
  -1 siblings, 0 replies; 201+ messages in thread
From: Fuad Tabba @ 2022-01-24 16:28 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, linux-arm-kernel

Hi Reiji,

On Thu, Jan 6, 2022 at 4:28 AM Reiji Watanabe <reijiw@google.com> wrote:
>
> This patch lays the groundwork to make ID registers writable.
>
> Introduce struct id_reg_info for an ID register to manage the
> register specific control of its value for the guest, and provide set
> of functions commonly used for ID registers to make them writable.
>
> The id_reg_info is used to do register specific initialization,
> validation of the ID register and etc.  Not all ID registers must
> have the id_reg_info. ID registers that don't have the id_reg_info
> are handled in a common way that is applied to all ID registers.
>
> At present, changing an ID register from userspace is allowed only
> if the ID register has the id_reg_info, but that will be changed
> by the following patches.
>
> No ID register has the structure yet and the following patches
> will add the id_reg_info for some ID registers.
>
> Signed-off-by: Reiji Watanabe <reijiw@google.com>
> ---
>  arch/arm64/kvm/sys_regs.c | 243 ++++++++++++++++++++++++++++++++++++--
>  1 file changed, 230 insertions(+), 13 deletions(-)
>
> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> index 80dc62f98ef0..971018288bee 100644
> --- a/arch/arm64/kvm/sys_regs.c
> +++ b/arch/arm64/kvm/sys_regs.c
> @@ -265,6 +265,101 @@ static bool trap_raz_wi(struct kvm_vcpu *vcpu,
>                 return read_zero(vcpu, p);
>  }
>
> +struct id_reg_info {
> +       u32     sys_reg;        /* Register ID */

Nit: Why not have the comment above, as for the other fields of this struct?

> +
> +       /*
> +        * Limit value of the register for a vcpu. The value is the sanitized
> +        * system value with bits cleared for unsupported features for the
> +        * guest.
> +        */

I think that rather than saying "with bits cleared for unsupported
features", it might be better to rephrase along the lines of "with
bits indicating" or "set/cleared to indicate", so that it applies to
signed fields as well.

> +       u64     vcpu_limit_val;
> +
> +       /* Fields that we don't to validate by arm64_check_features. */

Nit: Remove "to".

> +       u64     ignore_mask;
> +
> +       /* Initialization function of the id_reg_info */
> +       void (*init)(struct id_reg_info *id_reg);
> +       /*
> +        * This is an optional ID register specific validation function.
> +        * When userspace tries to set the ID register, arm64_check_features()
> +        * will check if the requested value indicates any features that cannot
> +        * be supported by KVM on the host.  But, some ID register fields need
> +        * a special checking, and this function can be used for such fields.
> +        * e.g. When SVE is configured for a vCPU by KVM_ARM_VCPU_INIT,
> +        * ID_AA64PFR0_EL1.SVE shouldn't be set to 0 for the vCPU.
> +        * The validation function for ID_AA64PFR0_EL1 could be used to check
> +        * the field is consistent with SVE configuration.
> +        */
> +       int (*validate)(struct kvm_vcpu *vcpu, const struct id_reg_info *id_reg,
> +                       u64 val);
> +
> +       /*
> +        * Return a bitmask of the vCPU's ID register fields that are not
> +        * synced with saved (per VM) ID register value, which usually
> +        * indicates opt-in CPU features that is not configured for the vCPU.

Nit: s/is/are


> +        * ID registers are saved per VM, but some opt-in CPU features can
> +        * be configured per vCPU.  The saved (per VM) values for such
> +        * features are for vCPUs with the features (and zero for
> +        * vCPUs without the features).
> +        * Return value of this function is used to handle such fields
> +        * for per vCPU ID register read/write request with saved per VM
> +        * ID register.  See the __write_id_reg's comment for more detail.
> +        */
> +       u64 (*vcpu_mask)(const struct kvm_vcpu *vcpu,
> +                        const struct id_reg_info *id_reg);
> +};
> +
> +static void id_reg_info_init(struct id_reg_info *id_reg)
> +{
> +       id_reg->vcpu_limit_val = read_sanitised_ftr_reg(id_reg->sys_reg);
> +       if (id_reg->init)

If there is an id_reg then the init function is necessary, isn't it?
Otherwise it doesn't seem to add more than the default handling. If
that's right, should we instead ensure that init is always set?

> +               id_reg->init(id_reg);
> +}
> +
> +/*
> + * An ID register that needs special handling to control the value for the
> + * guest must have its own id_reg_info in id_reg_info_table.
> + * (i.e. the reset value is different from the host's sanitized value,
> + * the value is affected by opt-in features, some fields need specific
> + * validation, etc.)
> + */
> +#define        GET_ID_REG_INFO(id)     (id_reg_info_table[IDREG_IDX(id)])
> +static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {};
> +
> +static int validate_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
> +{
> +       const struct id_reg_info *id_reg = GET_ID_REG_INFO(id);
> +       u64 limit, tmp_val;
> +       int err;
> +
> +       if (id_reg) {
> +               limit = id_reg->vcpu_limit_val;
> +               /*
> +                * Replace the fields that are indicated in ignore_mask with
> +                * the value in the limit to not have arm64_check_features()
> +                * check the field in @val.
> +                */
> +               tmp_val = val & ~id_reg->ignore_mask;
> +               tmp_val |= (limit & id_reg->ignore_mask);
> +       } else {
> +               limit = read_sanitised_ftr_reg(id);
> +               tmp_val = val;
> +       }
> +
> +       /* Check if the value indicates any feature that is not in the limit. */
> +       err = arm64_check_features(id, tmp_val, limit);
> +       if (err)
> +               return err;
> +
> +       if (id_reg && id_reg->validate)
> +               /* Run the ID register specific validity check. */
> +               err = id_reg->validate(vcpu, id_reg, val);
> +
> +       return err;
> +}
> +
>  /*
>   * ARMv8.1 mandates at least a trivial LORegion implementation, where all the
>   * RW registers are RES0 (which we can implement as RAZ/WI). On an ARMv8.0
> @@ -1061,9 +1156,81 @@ static bool access_arch_timer(struct kvm_vcpu *vcpu,
>         return true;
>  }
>
> +static u64 read_kvm_id_reg(struct kvm *kvm, u32 id)
> +{
> +       return kvm->arch.id_regs[IDREG_IDX(id)];
> +}
> +
> +static int modify_kvm_id_reg(struct kvm *kvm, u32 id, u64 val,
> +                            u64 preserve_mask)
> +{
> +       u64 old, new;
> +
> +       mutex_lock(&kvm->lock);
> +
> +       old = kvm->arch.id_regs[IDREG_IDX(id)];
> +
> +       /* Preserve the value at the bit position set in preserve_mask */
> +       new = old & preserve_mask;
> +       new |= (val & ~preserve_mask);
> +
> +       /* Don't allow to modify ID register value after KVM_RUN on any vCPUs */
> +       if (kvm_vm_has_started(kvm) && new != old)
> +               return -EBUSY;

This path doesn't release the lock. I see that it gets fixed in a
future patch, but it might be good to fix it here...

> +
> +       WRITE_ONCE(kvm->arch.id_regs[IDREG_IDX(id)], new);
> +       mutex_unlock(&kvm->lock);
> +
> +       return 0;
> +}
> +
> +static int write_kvm_id_reg(struct kvm *kvm, u32 id, u64 val)
> +{
> +       return modify_kvm_id_reg(kvm, id, val, 0);
> +}
> +
> +
> +/*
> + * KVM basically forces all vCPUs of the guest to have a uniform value for
> + * each ID register (, which means KVM_SET_ONE_REG for a vCPU affects all
> + * the vCPUs of the guest), and the id_regs[] of kvm_arch holds values
> + * of ID registers for the guest.  However, there is an exception for
> + * ID register fields corresponding to CPU features that can be
> + * configured per vCPU by KVM_ARM_VCPU_INIT, or etc (e.g. PMUv3, SVE, etc).
> + * For such fields, all vCPUs that have the feature will have a non-zero
> + * uniform value (, which can be updated by userspace), but the vCPUs that

Nit: uneven nesting of parentheses and commas :)


> + * don't have the feature will have zero for the fields.
> + * Values that @id_regs holds are for vCPUs that have such features.  So,
> + * to get the ID register value for a vCPU that doesn't have those features,
> + * the corresponding fields in id_regs[] needs to be cleared.
> + * A bitmask of the fields are provided by id_reg_info's vcpu_mask(), and
> + * __write_id_reg() and __read_id_reg() take care of those fields using
> + * the bitmask.
> + */
> +static int __write_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
> +{
> +       const struct id_reg_info *id_reg = GET_ID_REG_INFO(id);
> +       u64 mask = 0;
> +
> +       if (id_reg && id_reg->vcpu_mask)
> +               mask = id_reg->vcpu_mask(vcpu, id_reg);
> +
> +       /*
> +        * Update the ID register for the guest with @val, except for fields
> +        * that are set in the mask, which indicates fields for opt-in
> +        * features that are not configured for the vCPU.
> +        */
> +       return modify_kvm_id_reg(vcpu->kvm, id, val, mask);
> +}
> +
>  static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
>  {
> -       u64 val = vcpu->kvm->arch.id_regs[IDREG_IDX(id)];
> +       const struct id_reg_info *id_reg = GET_ID_REG_INFO(id);
> +       u64 val = read_kvm_id_reg(vcpu->kvm, id);
> +
> +       if (id_reg && id_reg->vcpu_mask)
> +               /* Clear fields for opt-in features that are not configured. */
> +               val &= ~(id_reg->vcpu_mask(vcpu, id_reg));
>         switch (id) {
>         case SYS_ID_AA64PFR0_EL1:
> @@ -1222,12 +1389,7 @@ static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
>         return 0;
>  }
>
> -/*
> - * cpufeature ID register user accessors
> - *
> - * For now, these registers are immutable for userspace, so for set_id_reg()
> - * we don't allow the effective value to be changed.
> - */
> +/* cpufeature ID register user accessors */
>  static int __get_id_reg(const struct kvm_vcpu *vcpu,
>                         const struct sys_reg_desc *rd, void __user *uaddr,
>                         bool raz)
> @@ -1238,11 +1400,31 @@ static int __get_id_reg(const struct kvm_vcpu *vcpu,
>         return reg_to_user(uaddr, &val, id);
>  }
>
> +/*
> + * Check if the given id indicates AArch32 ID register encoding.
> + */
> +static bool is_aarch32_id_reg(u32 id)
> +{
> +       u32 crm, op2;
> +
> +       if (!is_id_reg(id))
> +               return false;
> +
> +       crm = sys_reg_CRm(id);
> +       op2 = sys_reg_Op2(id);
> +       if (crm == 1 || crm == 2 || (crm == 3 && (op2 != 3 && op2 != 7)))

Consistent with the Arm ARM "Table D12-2 System instruction encodings
for non-Debug System register accesses"

> +               /* AArch32 ID register */
> +               return true;
> +
> +       return false;
> +}
> +
>  static int __set_id_reg(struct kvm_vcpu *vcpu,
>                         const struct sys_reg_desc *rd, void __user *uaddr,
>                         bool raz)
>  {
>         const u64 id = sys_reg_to_index(rd);
> +       u32 encoding = reg_to_encoding(rd);
>         int err;
>         u64 val;
>
> @@ -1250,11 +1432,28 @@ static int __set_id_reg(struct kvm_vcpu *vcpu,
>         if (err)
>                 return err;
>
> -       /* This is what we mean by invariant: you can't change it. */
> -       if (val != read_id_reg(vcpu, rd, raz))
> +       if (val == read_id_reg(vcpu, rd, raz))
> +               /* The value is same as the current value. Nothing to do. */
> +               return 0;
> +
> +       /*
> +        * Don't allow to modify the register's value if the register is raz,
> +        * or the reg doesn't have the id_reg_info.
> +        */
> +       if (raz || !GET_ID_REG_INFO(encoding))
>                 return -EINVAL;
>
> -       return 0;
> +       /*
> +        * Skip the validation of AArch32 ID registers if the system doesn't
> +        * 32bit EL0 (their value are UNKNOWN).
> +        */
> +       if (system_supports_32bit_el0() || !is_aarch32_id_reg(encoding)) {
> +               err = validate_id_reg(vcpu, encoding, val);
> +               if (err)
> +                       return err;
> +       }
> +
> +       return __write_id_reg(vcpu, encoding, val);
>  }
>
>  static int get_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
> @@ -2816,6 +3015,20 @@ int kvm_arm_copy_sys_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
>         return write_demux_regids(uindices);
>  }
>
> +static void id_reg_info_init_all(void)
> +{
> +       int i;
> +       struct id_reg_info *id_reg;
> +
> +       for (i = 0; i < ARRAY_SIZE(id_reg_info_table); i++) {
> +               id_reg = (struct id_reg_info *)id_reg_info_table[i];
> +               if (!id_reg)
> +                       continue;
> +
> +               id_reg_info_init(id_reg);
> +       }
> +}
> +
>  void kvm_sys_reg_table_init(void)
>  {
>         unsigned int i;
> @@ -2850,6 +3063,8 @@ void kvm_sys_reg_table_init(void)
>                         break;
>         /* Clear all higher bits. */
>         cache_levels &= (1 << (i*3))-1;
> +
> +       id_reg_info_init_all();
>  }
>
>  /*
> @@ -2862,11 +3077,12 @@ void set_default_id_regs(struct kvm *kvm)
>         u32 id;
>         const struct sys_reg_desc *rd;
>         u64 val;
> +       struct id_reg_info *idr;
>
>         for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
>                 rd = &sys_reg_descs[i];
>                 if (rd->access != access_id_reg)
> -                       /* Not ID register, or hidden/reserved ID register */
> +                       /* Not ID register or hidden/reserved ID register */
>                         continue;
>
>                 id = reg_to_encoding(rd);
> @@ -2874,7 +3090,8 @@ void set_default_id_regs(struct kvm *kvm)
>                         /* Shouldn't happen */
>                         continue;
>
> -               val = read_sanitised_ftr_reg(id);
> -               kvm->arch.id_regs[IDREG_IDX(id)] = val;
> +               idr = GET_ID_REG_INFO(id);
> +               val = idr ? idr->vcpu_limit_val : read_sanitised_ftr_reg(id);
> +               (void)write_kvm_id_reg(kvm, id, val);

Rather than ignoring the return value of write_kvm_id_reg(), wouldn't
it be better if set_default_id_regs were to propagate it back to
kvm_arch_init_vm in case there's a problem?

Thanks,
/fuad





>         }
>  }
> --
> 2.34.1.448.ga2b2bfdf31-goog
>
> _______________________________________________
> kvmarm mailing list
> kvmarm@lists.cs.columbia.edu
> https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 03/26] KVM: arm64: Introduce struct id_reg_info
@ 2022-01-24 16:28     ` Fuad Tabba
  0 siblings, 0 replies; 201+ messages in thread
From: Fuad Tabba @ 2022-01-24 16:28 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: kvm, Marc Zyngier, Peter Shier, Paolo Bonzini, Will Deacon,
	kvmarm, linux-arm-kernel

Hi Reiji,

On Thu, Jan 6, 2022 at 4:28 AM Reiji Watanabe <reijiw@google.com> wrote:
>
> This patch lays the groundwork to make ID registers writable.
>
> Introduce struct id_reg_info for an ID register to manage the
> register specific control of its value for the guest, and provide set
> of functions commonly used for ID registers to make them writable.
>
> The id_reg_info is used to do register specific initialization,
> validation of the ID register and etc.  Not all ID registers must
> have the id_reg_info. ID registers that don't have the id_reg_info
> are handled in a common way that is applied to all ID registers.
>
> At present, changing an ID register from userspace is allowed only
> if the ID register has the id_reg_info, but that will be changed
> by the following patches.
>
> No ID register has the structure yet and the following patches
> will add the id_reg_info for some ID registers.
>
> Signed-off-by: Reiji Watanabe <reijiw@google.com>
> ---
>  arch/arm64/kvm/sys_regs.c | 243 ++++++++++++++++++++++++++++++++++++--
>  1 file changed, 230 insertions(+), 13 deletions(-)
>
> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> index 80dc62f98ef0..971018288bee 100644
> --- a/arch/arm64/kvm/sys_regs.c
> +++ b/arch/arm64/kvm/sys_regs.c
> @@ -265,6 +265,101 @@ static bool trap_raz_wi(struct kvm_vcpu *vcpu,
>                 return read_zero(vcpu, p);
>  }
>
> +struct id_reg_info {
> +       u32     sys_reg;        /* Register ID */

Nit: Why not have the comment above, as for the other fields of this struct?

> +
> +       /*
> +        * Limit value of the register for a vcpu. The value is the sanitized
> +        * system value with bits cleared for unsupported features for the
> +        * guest.
> +        */

I think that rather than saying "with bits cleared for unsupported
features", it might be better to rephrase along the lines of "with
bits indicating" or "set/cleared to indicate", so that it applies to
signed fields as well.

> +       u64     vcpu_limit_val;
> +
> +       /* Fields that we don't to validate by arm64_check_features. */

Nit: Remove "to".

> +       u64     ignore_mask;
> +
> +       /* Initialization function of the id_reg_info */
> +       void (*init)(struct id_reg_info *id_reg);
> +       /*
> +        * This is an optional ID register specific validation function.
> +        * When userspace tries to set the ID register, arm64_check_features()
> +        * will check if the requested value indicates any features that cannot
> +        * be supported by KVM on the host.  But, some ID register fields need
> +        * a special checking, and this function can be used for such fields.
> +        * e.g. When SVE is configured for a vCPU by KVM_ARM_VCPU_INIT,
> +        * ID_AA64PFR0_EL1.SVE shouldn't be set to 0 for the vCPU.
> +        * The validation function for ID_AA64PFR0_EL1 could be used to check
> +        * the field is consistent with SVE configuration.
> +        */
> +       int (*validate)(struct kvm_vcpu *vcpu, const struct id_reg_info *id_reg,
> +                       u64 val);
> +
> +       /*
> +        * Return a bitmask of the vCPU's ID register fields that are not
> +        * synced with saved (per VM) ID register value, which usually
> +        * indicates opt-in CPU features that is not configured for the vCPU.

Nit: s/is/are


> +        * ID registers are saved per VM, but some opt-in CPU features can
> +        * be configured per vCPU.  The saved (per VM) values for such
> +        * features are for vCPUs with the features (and zero for
> +        * vCPUs without the features).
> +        * Return value of this function is used to handle such fields
> +        * for per vCPU ID register read/write request with saved per VM
> +        * ID register.  See the __write_id_reg's comment for more detail.
> +        */
> +       u64 (*vcpu_mask)(const struct kvm_vcpu *vcpu,
> +                        const struct id_reg_info *id_reg);
> +};
> +
> +static void id_reg_info_init(struct id_reg_info *id_reg)
> +{
> +       id_reg->vcpu_limit_val = read_sanitised_ftr_reg(id_reg->sys_reg);
> +       if (id_reg->init)

If there is an id_reg then the init function is necessary, isn't it?
Otherwise it doesn't seem to add more than the default handling. If
that's right, should we instead ensure that init is always set?

> +               id_reg->init(id_reg);
> +}
> +
> +/*
> + * An ID register that needs special handling to control the value for the
> + * guest must have its own id_reg_info in id_reg_info_table.
> + * (i.e. the reset value is different from the host's sanitized value,
> + * the value is affected by opt-in features, some fields need specific
> + * validation, etc.)
> + */
> +#define        GET_ID_REG_INFO(id)     (id_reg_info_table[IDREG_IDX(id)])
> +static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {};
> +
> +static int validate_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
> +{
> +       const struct id_reg_info *id_reg = GET_ID_REG_INFO(id);
> +       u64 limit, tmp_val;
> +       int err;
> +
> +       if (id_reg) {
> +               limit = id_reg->vcpu_limit_val;
> +               /*
> +                * Replace the fields that are indicated in ignore_mask with
> +                * the value in the limit to not have arm64_check_features()
> +                * check the field in @val.
> +                */
> +               tmp_val = val & ~id_reg->ignore_mask;
> +               tmp_val |= (limit & id_reg->ignore_mask);
> +       } else {
> +               limit = read_sanitised_ftr_reg(id);
> +               tmp_val = val;
> +       }
> +
> +       /* Check if the value indicates any feature that is not in the limit. */
> +       err = arm64_check_features(id, tmp_val, limit);
> +       if (err)
> +               return err;
> +
> +       if (id_reg && id_reg->validate)
> +               /* Run the ID register specific validity check. */
> +               err = id_reg->validate(vcpu, id_reg, val);
> +
> +       return err;
> +}
> +
>  /*
>   * ARMv8.1 mandates at least a trivial LORegion implementation, where all the
>   * RW registers are RES0 (which we can implement as RAZ/WI). On an ARMv8.0
> @@ -1061,9 +1156,81 @@ static bool access_arch_timer(struct kvm_vcpu *vcpu,
>         return true;
>  }
>
> +static u64 read_kvm_id_reg(struct kvm *kvm, u32 id)
> +{
> +       return kvm->arch.id_regs[IDREG_IDX(id)];
> +}
> +
> +static int modify_kvm_id_reg(struct kvm *kvm, u32 id, u64 val,
> +                            u64 preserve_mask)
> +{
> +       u64 old, new;
> +
> +       mutex_lock(&kvm->lock);
> +
> +       old = kvm->arch.id_regs[IDREG_IDX(id)];
> +
> +       /* Preserve the value at the bit position set in preserve_mask */
> +       new = old & preserve_mask;
> +       new |= (val & ~preserve_mask);
> +
> +       /* Don't allow to modify ID register value after KVM_RUN on any vCPUs */
> +       if (kvm_vm_has_started(kvm) && new != old)
> +               return -EBUSY;

This path doesn't release the lock. I see that it gets fixed in a
future patch, but it might be good to fix it here...

> +
> +       WRITE_ONCE(kvm->arch.id_regs[IDREG_IDX(id)], new);
> +       mutex_unlock(&kvm->lock);
> +
> +       return 0;
> +}
> +
> +static int write_kvm_id_reg(struct kvm *kvm, u32 id, u64 val)
> +{
> +       return modify_kvm_id_reg(kvm, id, val, 0);
> +}
> +
> +
> +/*
> + * KVM basically forces all vCPUs of the guest to have a uniform value for
> + * each ID register (, which means KVM_SET_ONE_REG for a vCPU affects all
> + * the vCPUs of the guest), and the id_regs[] of kvm_arch holds values
> + * of ID registers for the guest.  However, there is an exception for
> + * ID register fields corresponding to CPU features that can be
> + * configured per vCPU by KVM_ARM_VCPU_INIT, or etc (e.g. PMUv3, SVE, etc).
> + * For such fields, all vCPUs that have the feature will have a non-zero
> + * uniform value (, which can be updated by userspace), but the vCPUs that

Nit: uneven nesting of parentheses and commas :)


> + * don't have the feature will have zero for the fields.
> + * Values that @id_regs holds are for vCPUs that have such features.  So,
> + * to get the ID register value for a vCPU that doesn't have those features,
> + * the corresponding fields in id_regs[] needs to be cleared.
> + * A bitmask of the fields are provided by id_reg_info's vcpu_mask(), and
> + * __write_id_reg() and __read_id_reg() take care of those fields using
> + * the bitmask.
> + */
> +static int __write_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
> +{
> +       const struct id_reg_info *id_reg = GET_ID_REG_INFO(id);
> +       u64 mask = 0;
> +
> +       if (id_reg && id_reg->vcpu_mask)
> +               mask = id_reg->vcpu_mask(vcpu, id_reg);
> +
> +       /*
> +        * Update the ID register for the guest with @val, except for fields
> +        * that are set in the mask, which indicates fields for opt-in
> +        * features that are not configured for the vCPU.
> +        */
> +       return modify_kvm_id_reg(vcpu->kvm, id, val, mask);
> +}
> +
>  static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
>  {
> -       u64 val = vcpu->kvm->arch.id_regs[IDREG_IDX(id)];
> +       const struct id_reg_info *id_reg = GET_ID_REG_INFO(id);
> +       u64 val = read_kvm_id_reg(vcpu->kvm, id);
> +
> +       if (id_reg && id_reg->vcpu_mask)
> +               /* Clear fields for opt-in features that are not configured. */
> +               val &= ~(id_reg->vcpu_mask(vcpu, id_reg));
>         switch (id) {
>         case SYS_ID_AA64PFR0_EL1:
> @@ -1222,12 +1389,7 @@ static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
>         return 0;
>  }
>
> -/*
> - * cpufeature ID register user accessors
> - *
> - * For now, these registers are immutable for userspace, so for set_id_reg()
> - * we don't allow the effective value to be changed.
> - */
> +/* cpufeature ID register user accessors */
>  static int __get_id_reg(const struct kvm_vcpu *vcpu,
>                         const struct sys_reg_desc *rd, void __user *uaddr,
>                         bool raz)
> @@ -1238,11 +1400,31 @@ static int __get_id_reg(const struct kvm_vcpu *vcpu,
>         return reg_to_user(uaddr, &val, id);
>  }
>
> +/*
> + * Check if the given id indicates AArch32 ID register encoding.
> + */
> +static bool is_aarch32_id_reg(u32 id)
> +{
> +       u32 crm, op2;
> +
> +       if (!is_id_reg(id))
> +               return false;
> +
> +       crm = sys_reg_CRm(id);
> +       op2 = sys_reg_Op2(id);
> +       if (crm == 1 || crm == 2 || (crm == 3 && (op2 != 3 && op2 != 7)))

Consistent with the Arm ARM "Table D12-2 System instruction encodings
for non-Debug System register accesses"

> +               /* AArch32 ID register */
> +               return true;
> +
> +       return false;
> +}
> +
>  static int __set_id_reg(struct kvm_vcpu *vcpu,
>                         const struct sys_reg_desc *rd, void __user *uaddr,
>                         bool raz)
>  {
>         const u64 id = sys_reg_to_index(rd);
> +       u32 encoding = reg_to_encoding(rd);
>         int err;
>         u64 val;
>
> @@ -1250,11 +1432,28 @@ static int __set_id_reg(struct kvm_vcpu *vcpu,
>         if (err)
>                 return err;
>
> -       /* This is what we mean by invariant: you can't change it. */
> -       if (val != read_id_reg(vcpu, rd, raz))
> +       if (val == read_id_reg(vcpu, rd, raz))
> +               /* The value is same as the current value. Nothing to do. */
> +               return 0;
> +
> +       /*
> +        * Don't allow to modify the register's value if the register is raz,
> +        * or the reg doesn't have the id_reg_info.
> +        */
> +       if (raz || !GET_ID_REG_INFO(encoding))
>                 return -EINVAL;
>
> -       return 0;
> +       /*
> +        * Skip the validation of AArch32 ID registers if the system doesn't
> +        * 32bit EL0 (their value are UNKNOWN).
> +        */
> +       if (system_supports_32bit_el0() || !is_aarch32_id_reg(encoding)) {
> +               err = validate_id_reg(vcpu, encoding, val);
> +               if (err)
> +                       return err;
> +       }
> +
> +       return __write_id_reg(vcpu, encoding, val);
>  }
>
>  static int get_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
> @@ -2816,6 +3015,20 @@ int kvm_arm_copy_sys_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
>         return write_demux_regids(uindices);
>  }
>
> +static void id_reg_info_init_all(void)
> +{
> +       int i;
> +       struct id_reg_info *id_reg;
> +
> +       for (i = 0; i < ARRAY_SIZE(id_reg_info_table); i++) {
> +               id_reg = (struct id_reg_info *)id_reg_info_table[i];
> +               if (!id_reg)
> +                       continue;
> +
> +               id_reg_info_init(id_reg);
> +       }
> +}
> +
>  void kvm_sys_reg_table_init(void)
>  {
>         unsigned int i;
> @@ -2850,6 +3063,8 @@ void kvm_sys_reg_table_init(void)
>                         break;
>         /* Clear all higher bits. */
>         cache_levels &= (1 << (i*3))-1;
> +
> +       id_reg_info_init_all();
>  }
>
>  /*
> @@ -2862,11 +3077,12 @@ void set_default_id_regs(struct kvm *kvm)
>         u32 id;
>         const struct sys_reg_desc *rd;
>         u64 val;
> +       struct id_reg_info *idr;
>
>         for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
>                 rd = &sys_reg_descs[i];
>                 if (rd->access != access_id_reg)
> -                       /* Not ID register, or hidden/reserved ID register */
> +                       /* Not ID register or hidden/reserved ID register */
>                         continue;
>
>                 id = reg_to_encoding(rd);
> @@ -2874,7 +3090,8 @@ void set_default_id_regs(struct kvm *kvm)
>                         /* Shouldn't happen */
>                         continue;
>
> -               val = read_sanitised_ftr_reg(id);
> -               kvm->arch.id_regs[IDREG_IDX(id)] = val;
> +               idr = GET_ID_REG_INFO(id);
> +               val = idr ? idr->vcpu_limit_val : read_sanitised_ftr_reg(id);
> +               (void)write_kvm_id_reg(kvm, id, val);

Rather than ignoring the return value of write_kvm_id_reg(), wouldn't
it be better if set_default_id_regs were to propagate it back to
kvm_arch_init_vm in case there's a problem?

Thanks,
/fuad





>         }
>  }
> --
> 2.34.1.448.ga2b2bfdf31-goog
>
> _______________________________________________
> kvmarm mailing list
> kvmarm@lists.cs.columbia.edu
> https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 03/26] KVM: arm64: Introduce struct id_reg_info
@ 2022-01-24 16:28     ` Fuad Tabba
  0 siblings, 0 replies; 201+ messages in thread
From: Fuad Tabba @ 2022-01-24 16:28 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, linux-arm-kernel

Hi Reiji,

On Thu, Jan 6, 2022 at 4:28 AM Reiji Watanabe <reijiw@google.com> wrote:
>
> This patch lays the groundwork to make ID registers writable.
>
> Introduce struct id_reg_info for an ID register to manage the
> register specific control of its value for the guest, and provide set
> of functions commonly used for ID registers to make them writable.
>
> The id_reg_info is used to do register specific initialization,
> validation of the ID register and etc.  Not all ID registers must
> have the id_reg_info. ID registers that don't have the id_reg_info
> are handled in a common way that is applied to all ID registers.
>
> At present, changing an ID register from userspace is allowed only
> if the ID register has the id_reg_info, but that will be changed
> by the following patches.
>
> No ID register has the structure yet and the following patches
> will add the id_reg_info for some ID registers.
>
> Signed-off-by: Reiji Watanabe <reijiw@google.com>
> ---
>  arch/arm64/kvm/sys_regs.c | 243 ++++++++++++++++++++++++++++++++++++--
>  1 file changed, 230 insertions(+), 13 deletions(-)
>
> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> index 80dc62f98ef0..971018288bee 100644
> --- a/arch/arm64/kvm/sys_regs.c
> +++ b/arch/arm64/kvm/sys_regs.c
> @@ -265,6 +265,101 @@ static bool trap_raz_wi(struct kvm_vcpu *vcpu,
>                 return read_zero(vcpu, p);
>  }
>
> +struct id_reg_info {
> +       u32     sys_reg;        /* Register ID */

Nit: Why not have the comment above, as for the other fields of this struct?

> +
> +       /*
> +        * Limit value of the register for a vcpu. The value is the sanitized
> +        * system value with bits cleared for unsupported features for the
> +        * guest.
> +        */

I think that rather than saying "with bits cleared for unsupported
features", it might be better to rephrase along the lines of "with
bits indicating" or "set/cleared to indicate", so that it applies to
signed fields as well.

> +       u64     vcpu_limit_val;
> +
> +       /* Fields that we don't to validate by arm64_check_features. */

Nit: Remove "to".

> +       u64     ignore_mask;
> +
> +       /* Initialization function of the id_reg_info */
> +       void (*init)(struct id_reg_info *id_reg);
> +       /*
> +        * This is an optional ID register specific validation function.
> +        * When userspace tries to set the ID register, arm64_check_features()
> +        * will check if the requested value indicates any features that cannot
> +        * be supported by KVM on the host.  But, some ID register fields need
> +        * a special checking, and this function can be used for such fields.
> +        * e.g. When SVE is configured for a vCPU by KVM_ARM_VCPU_INIT,
> +        * ID_AA64PFR0_EL1.SVE shouldn't be set to 0 for the vCPU.
> +        * The validation function for ID_AA64PFR0_EL1 could be used to check
> +        * the field is consistent with SVE configuration.
> +        */
> +       int (*validate)(struct kvm_vcpu *vcpu, const struct id_reg_info *id_reg,
> +                       u64 val);
> +
> +       /*
> +        * Return a bitmask of the vCPU's ID register fields that are not
> +        * synced with saved (per VM) ID register value, which usually
> +        * indicates opt-in CPU features that is not configured for the vCPU.

Nit: s/is/are


> +        * ID registers are saved per VM, but some opt-in CPU features can
> +        * be configured per vCPU.  The saved (per VM) values for such
> +        * features are for vCPUs with the features (and zero for
> +        * vCPUs without the features).
> +        * Return value of this function is used to handle such fields
> +        * for per vCPU ID register read/write request with saved per VM
> +        * ID register.  See the __write_id_reg's comment for more detail.
> +        */
> +       u64 (*vcpu_mask)(const struct kvm_vcpu *vcpu,
> +                        const struct id_reg_info *id_reg);
> +};
> +
> +static void id_reg_info_init(struct id_reg_info *id_reg)
> +{
> +       id_reg->vcpu_limit_val = read_sanitised_ftr_reg(id_reg->sys_reg);
> +       if (id_reg->init)

If there is an id_reg then the init function is necessary, isn't it?
Otherwise it doesn't seem to add more than the default handling. If
that's right, should we instead ensure that init is always set?

> +               id_reg->init(id_reg);
> +}
> +
> +/*
> + * An ID register that needs special handling to control the value for the
> + * guest must have its own id_reg_info in id_reg_info_table.
> + * (i.e. the reset value is different from the host's sanitized value,
> + * the value is affected by opt-in features, some fields need specific
> + * validation, etc.)
> + */
> +#define        GET_ID_REG_INFO(id)     (id_reg_info_table[IDREG_IDX(id)])
> +static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {};
> +
> +static int validate_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
> +{
> +       const struct id_reg_info *id_reg = GET_ID_REG_INFO(id);
> +       u64 limit, tmp_val;
> +       int err;
> +
> +       if (id_reg) {
> +               limit = id_reg->vcpu_limit_val;
> +               /*
> +                * Replace the fields that are indicated in ignore_mask with
> +                * the value in the limit to not have arm64_check_features()
> +                * check the field in @val.
> +                */
> +               tmp_val = val & ~id_reg->ignore_mask;
> +               tmp_val |= (limit & id_reg->ignore_mask);
> +       } else {
> +               limit = read_sanitised_ftr_reg(id);
> +               tmp_val = val;
> +       }
> +
> +       /* Check if the value indicates any feature that is not in the limit. */
> +       err = arm64_check_features(id, tmp_val, limit);
> +       if (err)
> +               return err;
> +
> +       if (id_reg && id_reg->validate)
> +               /* Run the ID register specific validity check. */
> +               err = id_reg->validate(vcpu, id_reg, val);
> +
> +       return err;
> +}
> +
>  /*
>   * ARMv8.1 mandates at least a trivial LORegion implementation, where all the
>   * RW registers are RES0 (which we can implement as RAZ/WI). On an ARMv8.0
> @@ -1061,9 +1156,81 @@ static bool access_arch_timer(struct kvm_vcpu *vcpu,
>         return true;
>  }
>
> +static u64 read_kvm_id_reg(struct kvm *kvm, u32 id)
> +{
> +       return kvm->arch.id_regs[IDREG_IDX(id)];
> +}
> +
> +static int modify_kvm_id_reg(struct kvm *kvm, u32 id, u64 val,
> +                            u64 preserve_mask)
> +{
> +       u64 old, new;
> +
> +       mutex_lock(&kvm->lock);
> +
> +       old = kvm->arch.id_regs[IDREG_IDX(id)];
> +
> +       /* Preserve the value at the bit position set in preserve_mask */
> +       new = old & preserve_mask;
> +       new |= (val & ~preserve_mask);
> +
> +       /* Don't allow to modify ID register value after KVM_RUN on any vCPUs */
> +       if (kvm_vm_has_started(kvm) && new != old)
> +               return -EBUSY;

This path doesn't release the lock. I see that it gets fixed in a
future patch, but it might be good to fix it here...

> +
> +       WRITE_ONCE(kvm->arch.id_regs[IDREG_IDX(id)], new);
> +       mutex_unlock(&kvm->lock);
> +
> +       return 0;
> +}
> +
> +static int write_kvm_id_reg(struct kvm *kvm, u32 id, u64 val)
> +{
> +       return modify_kvm_id_reg(kvm, id, val, 0);
> +}
> +
> +
> +/*
> + * KVM basically forces all vCPUs of the guest to have a uniform value for
> + * each ID register (, which means KVM_SET_ONE_REG for a vCPU affects all
> + * the vCPUs of the guest), and the id_regs[] of kvm_arch holds values
> + * of ID registers for the guest.  However, there is an exception for
> + * ID register fields corresponding to CPU features that can be
> + * configured per vCPU by KVM_ARM_VCPU_INIT, or etc (e.g. PMUv3, SVE, etc).
> + * For such fields, all vCPUs that have the feature will have a non-zero
> + * uniform value (, which can be updated by userspace), but the vCPUs that

Nit: uneven nesting of parentheses and commas :)


> + * don't have the feature will have zero for the fields.
> + * Values that @id_regs holds are for vCPUs that have such features.  So,
> + * to get the ID register value for a vCPU that doesn't have those features,
> + * the corresponding fields in id_regs[] needs to be cleared.
> + * A bitmask of the fields are provided by id_reg_info's vcpu_mask(), and
> + * __write_id_reg() and __read_id_reg() take care of those fields using
> + * the bitmask.
> + */
> +static int __write_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
> +{
> +       const struct id_reg_info *id_reg = GET_ID_REG_INFO(id);
> +       u64 mask = 0;
> +
> +       if (id_reg && id_reg->vcpu_mask)
> +               mask = id_reg->vcpu_mask(vcpu, id_reg);
> +
> +       /*
> +        * Update the ID register for the guest with @val, except for fields
> +        * that are set in the mask, which indicates fields for opt-in
> +        * features that are not configured for the vCPU.
> +        */
> +       return modify_kvm_id_reg(vcpu->kvm, id, val, mask);
> +}
> +
>  static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
>  {
> -       u64 val = vcpu->kvm->arch.id_regs[IDREG_IDX(id)];
> +       const struct id_reg_info *id_reg = GET_ID_REG_INFO(id);
> +       u64 val = read_kvm_id_reg(vcpu->kvm, id);
> +
> +       if (id_reg && id_reg->vcpu_mask)
> +               /* Clear fields for opt-in features that are not configured. */
> +               val &= ~(id_reg->vcpu_mask(vcpu, id_reg));
>         switch (id) {
>         case SYS_ID_AA64PFR0_EL1:
> @@ -1222,12 +1389,7 @@ static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
>         return 0;
>  }
>
> -/*
> - * cpufeature ID register user accessors
> - *
> - * For now, these registers are immutable for userspace, so for set_id_reg()
> - * we don't allow the effective value to be changed.
> - */
> +/* cpufeature ID register user accessors */
>  static int __get_id_reg(const struct kvm_vcpu *vcpu,
>                         const struct sys_reg_desc *rd, void __user *uaddr,
>                         bool raz)
> @@ -1238,11 +1400,31 @@ static int __get_id_reg(const struct kvm_vcpu *vcpu,
>         return reg_to_user(uaddr, &val, id);
>  }
>
> +/*
> + * Check if the given id indicates AArch32 ID register encoding.
> + */
> +static bool is_aarch32_id_reg(u32 id)
> +{
> +       u32 crm, op2;
> +
> +       if (!is_id_reg(id))
> +               return false;
> +
> +       crm = sys_reg_CRm(id);
> +       op2 = sys_reg_Op2(id);
> +       if (crm == 1 || crm == 2 || (crm == 3 && (op2 != 3 && op2 != 7)))

Consistent with the Arm ARM "Table D12-2 System instruction encodings
for non-Debug System register accesses"

> +               /* AArch32 ID register */
> +               return true;
> +
> +       return false;
> +}
> +
>  static int __set_id_reg(struct kvm_vcpu *vcpu,
>                         const struct sys_reg_desc *rd, void __user *uaddr,
>                         bool raz)
>  {
>         const u64 id = sys_reg_to_index(rd);
> +       u32 encoding = reg_to_encoding(rd);
>         int err;
>         u64 val;
>
> @@ -1250,11 +1432,28 @@ static int __set_id_reg(struct kvm_vcpu *vcpu,
>         if (err)
>                 return err;
>
> -       /* This is what we mean by invariant: you can't change it. */
> -       if (val != read_id_reg(vcpu, rd, raz))
> +       if (val == read_id_reg(vcpu, rd, raz))
> +               /* The value is same as the current value. Nothing to do. */
> +               return 0;
> +
> +       /*
> +        * Don't allow to modify the register's value if the register is raz,
> +        * or the reg doesn't have the id_reg_info.
> +        */
> +       if (raz || !GET_ID_REG_INFO(encoding))
>                 return -EINVAL;
>
> -       return 0;
> +       /*
> +        * Skip the validation of AArch32 ID registers if the system doesn't
> +        * 32bit EL0 (their value are UNKNOWN).
> +        */
> +       if (system_supports_32bit_el0() || !is_aarch32_id_reg(encoding)) {
> +               err = validate_id_reg(vcpu, encoding, val);
> +               if (err)
> +                       return err;
> +       }
> +
> +       return __write_id_reg(vcpu, encoding, val);
>  }
>
>  static int get_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
> @@ -2816,6 +3015,20 @@ int kvm_arm_copy_sys_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
>         return write_demux_regids(uindices);
>  }
>
> +static void id_reg_info_init_all(void)
> +{
> +       int i;
> +       struct id_reg_info *id_reg;
> +
> +       for (i = 0; i < ARRAY_SIZE(id_reg_info_table); i++) {
> +               id_reg = (struct id_reg_info *)id_reg_info_table[i];
> +               if (!id_reg)
> +                       continue;
> +
> +               id_reg_info_init(id_reg);
> +       }
> +}
> +
>  void kvm_sys_reg_table_init(void)
>  {
>         unsigned int i;
> @@ -2850,6 +3063,8 @@ void kvm_sys_reg_table_init(void)
>                         break;
>         /* Clear all higher bits. */
>         cache_levels &= (1 << (i*3))-1;
> +
> +       id_reg_info_init_all();
>  }
>
>  /*
> @@ -2862,11 +3077,12 @@ void set_default_id_regs(struct kvm *kvm)
>         u32 id;
>         const struct sys_reg_desc *rd;
>         u64 val;
> +       struct id_reg_info *idr;
>
>         for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
>                 rd = &sys_reg_descs[i];
>                 if (rd->access != access_id_reg)
> -                       /* Not ID register, or hidden/reserved ID register */
> +                       /* Not ID register or hidden/reserved ID register */
>                         continue;
>
>                 id = reg_to_encoding(rd);
> @@ -2874,7 +3090,8 @@ void set_default_id_regs(struct kvm *kvm)
>                         /* Shouldn't happen */
>                         continue;
>
> -               val = read_sanitised_ftr_reg(id);
> -               kvm->arch.id_regs[IDREG_IDX(id)] = val;
> +               idr = GET_ID_REG_INFO(id);
> +               val = idr ? idr->vcpu_limit_val : read_sanitised_ftr_reg(id);
> +               (void)write_kvm_id_reg(kvm, id, val);

Rather than ignoring the return value of write_kvm_id_reg(), wouldn't
it be better if set_default_id_regs were to propagate it back to
kvm_arch_init_vm in case there's a problem?

Thanks,
/fuad





>         }
>  }
> --
> 2.34.1.448.ga2b2bfdf31-goog
>
> _______________________________________________
> kvmarm mailing list
> kvmarm@lists.cs.columbia.edu
> https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 04/26] KVM: arm64: Make ID_AA64PFR0_EL1 writable
  2022-01-06  4:26   ` Reiji Watanabe
  (?)
@ 2022-01-24 16:51     ` Fuad Tabba
  -1 siblings, 0 replies; 201+ messages in thread
From: Fuad Tabba @ 2022-01-24 16:51 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, linux-arm-kernel

Hi Reiji,

On Thu, Jan 6, 2022 at 4:28 AM Reiji Watanabe <reijiw@google.com> wrote:
>
> This patch adds id_reg_info for ID_AA64PFR0_EL1 to make it writable by
> userspace.
>
> Return an error if userspace tries to set SVE/GIC field of the register
> to a value that conflicts with SVE/GIC configuration for the guest.
> SIMD/FP/SVE fields of the requested value are validated according to
> Arm ARM.
>
> Signed-off-by: Reiji Watanabe <reijiw@google.com>
> ---
>  arch/arm64/include/asm/kvm_host.h |   1 +
>  arch/arm64/include/asm/sysreg.h   |   2 +
>  arch/arm64/kvm/sys_regs.c         | 177 +++++++++++++++++++-----------
>  arch/arm64/kvm/vgic/vgic-init.c   |   5 +
>  4 files changed, 123 insertions(+), 62 deletions(-)
>
> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index c789a0137f58..4509f9e7472d 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -749,6 +749,7 @@ long kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
>                                 struct kvm_arm_copy_mte_tags *copy_tags);
>
>  void set_default_id_regs(struct kvm *kvm);
> +int kvm_set_id_reg_feature(struct kvm *kvm, u32 id, u8 field_shift, u8 fval);
>
>  /* Guest/host FPSIMD coordination helpers */
>  int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu);
> diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h
> index 16b3f1a1d468..e26027817171 100644
> --- a/arch/arm64/include/asm/sysreg.h
> +++ b/arch/arm64/include/asm/sysreg.h
> @@ -798,6 +798,7 @@
>  #define ID_AA64PFR0_ASIMD_SUPPORTED    0x0
>  #define ID_AA64PFR0_ELx_64BIT_ONLY     0x1
>  #define ID_AA64PFR0_ELx_32BIT_64BIT    0x2
> +#define ID_AA64PFR0_GIC3               0x1
>
>  /* id_aa64pfr1 */
>  #define ID_AA64PFR1_MPAMFRAC_SHIFT     16
> @@ -1197,6 +1198,7 @@
>  #define ICH_VTR_TDS_MASK       (1 << ICH_VTR_TDS_SHIFT)
>
>  #define ARM64_FEATURE_FIELD_BITS       4
> +#define ARM64_FEATURE_FIELD_MASK       ((1ull << ARM64_FEATURE_FIELD_BITS) - 1)
>
>  /* Create a mask for the feature bits of the specified feature. */
>  #define ARM64_FEATURE_MASK(x)  (GENMASK_ULL(x##_SHIFT + ARM64_FEATURE_FIELD_BITS - 1, x##_SHIFT))
> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> index 971018288bee..1eb5c5fb614f 100644
> --- a/arch/arm64/kvm/sys_regs.c
> +++ b/arch/arm64/kvm/sys_regs.c
> @@ -318,6 +318,92 @@ static void id_reg_info_init(struct id_reg_info *id_reg)
>                 id_reg->init(id_reg);
>  }
>
> +static int validate_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
> +                                   const struct id_reg_info *id_reg, u64 val)
> +{
> +       int fp, simd;
> +       unsigned int gic;
> +       bool vcpu_has_sve = vcpu_has_sve(vcpu);
> +       bool pfr0_has_sve = id_aa64pfr0_sve(val);
> +
> +       simd = cpuid_feature_extract_signed_field(val, ID_AA64PFR0_ASIMD_SHIFT);
> +       fp = cpuid_feature_extract_signed_field(val, ID_AA64PFR0_FP_SHIFT);
> +       if (simd != fp)

Why is this the case? Could you add a comment?

> +               return -EINVAL;
> +
> +       /* fp must be supported when sve is supported */
> +       if (pfr0_has_sve && (fp < 0))
> +               return -EINVAL;
> +
> +       /* Check if there is a conflict with a request via KVM_ARM_VCPU_INIT */
> +       if (vcpu_has_sve ^ pfr0_has_sve)
> +               return -EPERM;
> +
> +       if ((irqchip_in_kernel(vcpu->kvm) &&
> +            vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3)) {
> +               gic = cpuid_feature_extract_unsigned_field(val,
> +                                                       ID_AA64PFR0_GIC_SHIFT);
> +               if (gic == 0)
> +                       return -EPERM;
> +
> +               if (gic > ID_AA64PFR0_GIC3)
> +                       return -E2BIG;
> +       } else {
> +               u64 mask = ARM64_FEATURE_MASK(ID_AA64PFR0_GIC);
> +               int err = arm64_check_features(id_reg->sys_reg, val & mask,
> +                                              id_reg->vcpu_limit_val & mask);
> +               if (err)
> +                       return err;
> +       }
> +
> +       return 0;
> +}
> +
> +static void init_id_aa64pfr0_el1_info(struct id_reg_info *id_reg)
> +{
> +       u64 limit = id_reg->vcpu_limit_val;
> +       unsigned int gic;
> +
> +       limit &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_AMU);
> +       if (!system_supports_sve())
> +               limit &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_SVE);
> +
> +       /*
> +        * The default is to expose CSV2 == 1 and CSV3 == 1 if the HW
> +        * isn't affected.  Userspace can override this as long as it
> +        * doesn't promise the impossible.
> +        */
> +       limit &= ~(ARM64_FEATURE_MASK(ID_AA64PFR0_CSV2) |
> +                  ARM64_FEATURE_MASK(ID_AA64PFR0_CSV3));
> +
> +       if (arm64_get_spectre_v2_state() == SPECTRE_UNAFFECTED)
> +               limit |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_CSV2), 1);
> +       if (arm64_get_meltdown_state() == SPECTRE_UNAFFECTED)
> +               limit |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_CSV3), 1);
> +
> +       gic = cpuid_feature_extract_unsigned_field(limit, ID_AA64PFR0_GIC_SHIFT);
> +       if (gic > 1) {
> +               /* Limit to GICv3.0/4.0 */
> +               limit &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_GIC);
> +               limit |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_GIC), ID_AA64PFR0_GIC3);
> +       }
> +       id_reg->vcpu_limit_val = limit;
> +}
> +
> +static u64 vcpu_mask_id_aa64pfr0_el1(const struct kvm_vcpu *vcpu,
> +                                        const struct id_reg_info *idr)
> +{
> +       return vcpu_has_sve(vcpu) ? 0 : ARM64_FEATURE_MASK(ID_AA64PFR0_SVE);
> +}
> +
> +static struct id_reg_info id_aa64pfr0_el1_info = {
> +       .sys_reg = SYS_ID_AA64PFR0_EL1,
> +       .ignore_mask = ARM64_FEATURE_MASK(ID_AA64PFR0_GIC),
> +       .init = init_id_aa64pfr0_el1_info,
> +       .validate = validate_id_aa64pfr0_el1,
> +       .vcpu_mask = vcpu_mask_id_aa64pfr0_el1,
> +};
> +
>  /*
>   * An ID register that needs special handling to control the value for the
>   * guest must have its own id_reg_info in id_reg_info_table.
> @@ -326,7 +412,9 @@ static void id_reg_info_init(struct id_reg_info *id_reg)
>   * validation, etc.)
>   */
>  #define        GET_ID_REG_INFO(id)     (id_reg_info_table[IDREG_IDX(id)])
> -static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {};
> +static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {
> +       [IDREG_IDX(SYS_ID_AA64PFR0_EL1)] = &id_aa64pfr0_el1_info,
> +};
>
>  static int validate_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
>  {
> @@ -1161,12 +1249,12 @@ static u64 read_kvm_id_reg(struct kvm *kvm, u32 id)
>         return kvm->arch.id_regs[IDREG_IDX(id)];
>  }
>
> -static int modify_kvm_id_reg(struct kvm *kvm, u32 id, u64 val,
> +static int __modify_kvm_id_reg(struct kvm *kvm, u32 id, u64 val,
>                              u64 preserve_mask)
>  {
>         u64 old, new;
>
> -       mutex_lock(&kvm->lock);
> +       lockdep_assert_held(&kvm->lock);
>
>         old = kvm->arch.id_regs[IDREG_IDX(id)];
>
> @@ -1179,11 +1267,21 @@ static int modify_kvm_id_reg(struct kvm *kvm, u32 id, u64 val,
>                 return -EBUSY;
>
>         WRITE_ONCE(kvm->arch.id_regs[IDREG_IDX(id)], new);
> -       mutex_unlock(&kvm->lock);
>
>         return 0;
>  }
>
> +static int modify_kvm_id_reg(struct kvm *kvm, u32 id, u64 val,
> +                            u64 preserve_mask)
> +{
> +       int ret;
> +
> +       mutex_lock(&kvm->lock);
> +       ret = __modify_kvm_id_reg(kvm, id, val, preserve_mask);
> +       mutex_unlock(&kvm->lock);
> +
> +       return ret;
> +}

I think you probably wanted these changes to modify_kvm_id_reg() to go
into the previous patch rather than in this one.


>  static int write_kvm_id_reg(struct kvm *kvm, u32 id, u64 val)
>  {
>         return modify_kvm_id_reg(kvm, id, val, 0);
> @@ -1233,20 +1331,6 @@ static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
>                 val &= ~(id_reg->vcpu_mask(vcpu, id_reg));
>
>         switch (id) {
> -       case SYS_ID_AA64PFR0_EL1:
> -               if (!vcpu_has_sve(vcpu))
> -                       val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_SVE);
> -               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_AMU);
> -               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_CSV2);
> -               val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_CSV2), (u64)vcpu->kvm->arch.pfr0_csv2);
> -               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_CSV3);
> -               val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_CSV3), (u64)vcpu->kvm->arch.pfr0_csv3);
> -               if (irqchip_in_kernel(vcpu->kvm) &&
> -                   vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3) {
> -                       val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_GIC);
> -                       val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_GIC), 1);
> -               }
> -               break;
>         case SYS_ID_AA64PFR1_EL1:
>                 if (!kvm_has_mte(vcpu->kvm))
>                         val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_MTE);
> @@ -1347,48 +1431,6 @@ static unsigned int sve_visibility(const struct kvm_vcpu *vcpu,
>         return REG_HIDDEN;
>  }
>
> -static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
> -                              const struct sys_reg_desc *rd,
> -                              const struct kvm_one_reg *reg, void __user *uaddr)
> -{
> -       const u64 id = sys_reg_to_index(rd);
> -       u8 csv2, csv3;
> -       int err;
> -       u64 val;
> -
> -       err = reg_from_user(&val, uaddr, id);
> -       if (err)
> -               return err;
> -
> -       /*
> -        * Allow AA64PFR0_EL1.CSV2 to be set from userspace as long as
> -        * it doesn't promise more than what is actually provided (the
> -        * guest could otherwise be covered in ectoplasmic residue).
> -        */
> -       csv2 = cpuid_feature_extract_unsigned_field(val, ID_AA64PFR0_CSV2_SHIFT);
> -       if (csv2 > 1 ||
> -           (csv2 && arm64_get_spectre_v2_state() != SPECTRE_UNAFFECTED))
> -               return -EINVAL;
> -
> -       /* Same thing for CSV3 */
> -       csv3 = cpuid_feature_extract_unsigned_field(val, ID_AA64PFR0_CSV3_SHIFT);
> -       if (csv3 > 1 ||
> -           (csv3 && arm64_get_meltdown_state() != SPECTRE_UNAFFECTED))
> -               return -EINVAL;
> -
> -       /* We can only differ with CSV[23], and anything else is an error */
> -       val ^= read_id_reg(vcpu, rd, false);
> -       val &= ~((0xFUL << ID_AA64PFR0_CSV2_SHIFT) |
> -                (0xFUL << ID_AA64PFR0_CSV3_SHIFT));
> -       if (val)
> -               return -EINVAL;
> -
> -       vcpu->kvm->arch.pfr0_csv2 = csv2;
> -       vcpu->kvm->arch.pfr0_csv3 = csv3 ;
> -
> -       return 0;
> -}
> -
>  /* cpufeature ID register user accessors */
>  static int __get_id_reg(const struct kvm_vcpu *vcpu,
>                         const struct sys_reg_desc *rd, void __user *uaddr,
> @@ -1702,8 +1744,7 @@ static const struct sys_reg_desc sys_reg_descs[] = {
>
>         /* AArch64 ID registers */
>         /* CRm=4 */
> -       { SYS_DESC(SYS_ID_AA64PFR0_EL1), .access = access_id_reg,
> -         .get_user = get_id_reg, .set_user = set_id_aa64pfr0_el1, },
> +       ID_SANITISED(ID_AA64PFR0_EL1),
>         ID_SANITISED(ID_AA64PFR1_EL1),
>         ID_UNALLOCATED(4,2),
>         ID_UNALLOCATED(4,3),
> @@ -3095,3 +3136,15 @@ void set_default_id_regs(struct kvm *kvm)
>                 (void)write_kvm_id_reg(kvm, id, val);
>         }
>  }
> +
> +/*
> + * Update the ID register's field with @fval for the guest.
> + * The caller is expected to hold the kvm->lock.
> + */
> +int kvm_set_id_reg_feature(struct kvm *kvm, u32 id, u8 field_shift, u8 fval)
> +{
> +       u64 val = ((u64)fval & ARM64_FEATURE_FIELD_MASK) << field_shift;
> +       u64 preserve_mask = ~(ARM64_FEATURE_FIELD_MASK << field_shift);
> +
> +       return __modify_kvm_id_reg(kvm, id, val, preserve_mask);
> +}

This seems to me like it should also be in the previous patch or a
separate patch.

> diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c
> index 0a06d0648970..28d9bf0e178c 100644
> --- a/arch/arm64/kvm/vgic/vgic-init.c
> +++ b/arch/arm64/kvm/vgic/vgic-init.c
> @@ -116,6 +116,11 @@ int kvm_vgic_create(struct kvm *kvm, u32 type)
>         else
>                 INIT_LIST_HEAD(&kvm->arch.vgic.rd_regions);
>
> +       if (type == KVM_DEV_TYPE_ARM_VGIC_V3)
> +               /* Set ID_AA64PFR0_EL1.GIC to 1 */
> +               (void)kvm_set_id_reg_feature(kvm, SYS_ID_AA64PFR0_EL1,
> +                                    ID_AA64PFR0_GIC3, ID_AA64PFR0_GIC_SHIFT);
> +

If this fails wouldn't it be better to return the error?

Thanks,
/fuad


>  out_unlock:
>         unlock_all_vcpus(kvm);
>         return ret;
> --
> 2.34.1.448.ga2b2bfdf31-goog
>
> _______________________________________________
> kvmarm mailing list
> kvmarm@lists.cs.columbia.edu
> https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 04/26] KVM: arm64: Make ID_AA64PFR0_EL1 writable
@ 2022-01-24 16:51     ` Fuad Tabba
  0 siblings, 0 replies; 201+ messages in thread
From: Fuad Tabba @ 2022-01-24 16:51 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: kvm, Marc Zyngier, Peter Shier, Paolo Bonzini, Will Deacon,
	kvmarm, linux-arm-kernel

Hi Reiji,

On Thu, Jan 6, 2022 at 4:28 AM Reiji Watanabe <reijiw@google.com> wrote:
>
> This patch adds id_reg_info for ID_AA64PFR0_EL1 to make it writable by
> userspace.
>
> Return an error if userspace tries to set SVE/GIC field of the register
> to a value that conflicts with SVE/GIC configuration for the guest.
> SIMD/FP/SVE fields of the requested value are validated according to
> Arm ARM.
>
> Signed-off-by: Reiji Watanabe <reijiw@google.com>
> ---
>  arch/arm64/include/asm/kvm_host.h |   1 +
>  arch/arm64/include/asm/sysreg.h   |   2 +
>  arch/arm64/kvm/sys_regs.c         | 177 +++++++++++++++++++-----------
>  arch/arm64/kvm/vgic/vgic-init.c   |   5 +
>  4 files changed, 123 insertions(+), 62 deletions(-)
>
> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index c789a0137f58..4509f9e7472d 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -749,6 +749,7 @@ long kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
>                                 struct kvm_arm_copy_mte_tags *copy_tags);
>
>  void set_default_id_regs(struct kvm *kvm);
> +int kvm_set_id_reg_feature(struct kvm *kvm, u32 id, u8 field_shift, u8 fval);
>
>  /* Guest/host FPSIMD coordination helpers */
>  int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu);
> diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h
> index 16b3f1a1d468..e26027817171 100644
> --- a/arch/arm64/include/asm/sysreg.h
> +++ b/arch/arm64/include/asm/sysreg.h
> @@ -798,6 +798,7 @@
>  #define ID_AA64PFR0_ASIMD_SUPPORTED    0x0
>  #define ID_AA64PFR0_ELx_64BIT_ONLY     0x1
>  #define ID_AA64PFR0_ELx_32BIT_64BIT    0x2
> +#define ID_AA64PFR0_GIC3               0x1
>
>  /* id_aa64pfr1 */
>  #define ID_AA64PFR1_MPAMFRAC_SHIFT     16
> @@ -1197,6 +1198,7 @@
>  #define ICH_VTR_TDS_MASK       (1 << ICH_VTR_TDS_SHIFT)
>
>  #define ARM64_FEATURE_FIELD_BITS       4
> +#define ARM64_FEATURE_FIELD_MASK       ((1ull << ARM64_FEATURE_FIELD_BITS) - 1)
>
>  /* Create a mask for the feature bits of the specified feature. */
>  #define ARM64_FEATURE_MASK(x)  (GENMASK_ULL(x##_SHIFT + ARM64_FEATURE_FIELD_BITS - 1, x##_SHIFT))
> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> index 971018288bee..1eb5c5fb614f 100644
> --- a/arch/arm64/kvm/sys_regs.c
> +++ b/arch/arm64/kvm/sys_regs.c
> @@ -318,6 +318,92 @@ static void id_reg_info_init(struct id_reg_info *id_reg)
>                 id_reg->init(id_reg);
>  }
>
> +static int validate_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
> +                                   const struct id_reg_info *id_reg, u64 val)
> +{
> +       int fp, simd;
> +       unsigned int gic;
> +       bool vcpu_has_sve = vcpu_has_sve(vcpu);
> +       bool pfr0_has_sve = id_aa64pfr0_sve(val);
> +
> +       simd = cpuid_feature_extract_signed_field(val, ID_AA64PFR0_ASIMD_SHIFT);
> +       fp = cpuid_feature_extract_signed_field(val, ID_AA64PFR0_FP_SHIFT);
> +       if (simd != fp)

Why is this the case? Could you add a comment?

> +               return -EINVAL;
> +
> +       /* fp must be supported when sve is supported */
> +       if (pfr0_has_sve && (fp < 0))
> +               return -EINVAL;
> +
> +       /* Check if there is a conflict with a request via KVM_ARM_VCPU_INIT */
> +       if (vcpu_has_sve ^ pfr0_has_sve)
> +               return -EPERM;
> +
> +       if ((irqchip_in_kernel(vcpu->kvm) &&
> +            vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3)) {
> +               gic = cpuid_feature_extract_unsigned_field(val,
> +                                                       ID_AA64PFR0_GIC_SHIFT);
> +               if (gic == 0)
> +                       return -EPERM;
> +
> +               if (gic > ID_AA64PFR0_GIC3)
> +                       return -E2BIG;
> +       } else {
> +               u64 mask = ARM64_FEATURE_MASK(ID_AA64PFR0_GIC);
> +               int err = arm64_check_features(id_reg->sys_reg, val & mask,
> +                                              id_reg->vcpu_limit_val & mask);
> +               if (err)
> +                       return err;
> +       }
> +
> +       return 0;
> +}
> +
> +static void init_id_aa64pfr0_el1_info(struct id_reg_info *id_reg)
> +{
> +       u64 limit = id_reg->vcpu_limit_val;
> +       unsigned int gic;
> +
> +       limit &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_AMU);
> +       if (!system_supports_sve())
> +               limit &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_SVE);
> +
> +       /*
> +        * The default is to expose CSV2 == 1 and CSV3 == 1 if the HW
> +        * isn't affected.  Userspace can override this as long as it
> +        * doesn't promise the impossible.
> +        */
> +       limit &= ~(ARM64_FEATURE_MASK(ID_AA64PFR0_CSV2) |
> +                  ARM64_FEATURE_MASK(ID_AA64PFR0_CSV3));
> +
> +       if (arm64_get_spectre_v2_state() == SPECTRE_UNAFFECTED)
> +               limit |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_CSV2), 1);
> +       if (arm64_get_meltdown_state() == SPECTRE_UNAFFECTED)
> +               limit |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_CSV3), 1);
> +
> +       gic = cpuid_feature_extract_unsigned_field(limit, ID_AA64PFR0_GIC_SHIFT);
> +       if (gic > 1) {
> +               /* Limit to GICv3.0/4.0 */
> +               limit &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_GIC);
> +               limit |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_GIC), ID_AA64PFR0_GIC3);
> +       }
> +       id_reg->vcpu_limit_val = limit;
> +}
> +
> +static u64 vcpu_mask_id_aa64pfr0_el1(const struct kvm_vcpu *vcpu,
> +                                        const struct id_reg_info *idr)
> +{
> +       return vcpu_has_sve(vcpu) ? 0 : ARM64_FEATURE_MASK(ID_AA64PFR0_SVE);
> +}
> +
> +static struct id_reg_info id_aa64pfr0_el1_info = {
> +       .sys_reg = SYS_ID_AA64PFR0_EL1,
> +       .ignore_mask = ARM64_FEATURE_MASK(ID_AA64PFR0_GIC),
> +       .init = init_id_aa64pfr0_el1_info,
> +       .validate = validate_id_aa64pfr0_el1,
> +       .vcpu_mask = vcpu_mask_id_aa64pfr0_el1,
> +};
> +
>  /*
>   * An ID register that needs special handling to control the value for the
>   * guest must have its own id_reg_info in id_reg_info_table.
> @@ -326,7 +412,9 @@ static void id_reg_info_init(struct id_reg_info *id_reg)
>   * validation, etc.)
>   */
>  #define        GET_ID_REG_INFO(id)     (id_reg_info_table[IDREG_IDX(id)])
> -static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {};
> +static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {
> +       [IDREG_IDX(SYS_ID_AA64PFR0_EL1)] = &id_aa64pfr0_el1_info,
> +};
>
>  static int validate_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
>  {
> @@ -1161,12 +1249,12 @@ static u64 read_kvm_id_reg(struct kvm *kvm, u32 id)
>         return kvm->arch.id_regs[IDREG_IDX(id)];
>  }
>
> -static int modify_kvm_id_reg(struct kvm *kvm, u32 id, u64 val,
> +static int __modify_kvm_id_reg(struct kvm *kvm, u32 id, u64 val,
>                              u64 preserve_mask)
>  {
>         u64 old, new;
>
> -       mutex_lock(&kvm->lock);
> +       lockdep_assert_held(&kvm->lock);
>
>         old = kvm->arch.id_regs[IDREG_IDX(id)];
>
> @@ -1179,11 +1267,21 @@ static int modify_kvm_id_reg(struct kvm *kvm, u32 id, u64 val,
>                 return -EBUSY;
>
>         WRITE_ONCE(kvm->arch.id_regs[IDREG_IDX(id)], new);
> -       mutex_unlock(&kvm->lock);
>
>         return 0;
>  }
>
> +static int modify_kvm_id_reg(struct kvm *kvm, u32 id, u64 val,
> +                            u64 preserve_mask)
> +{
> +       int ret;
> +
> +       mutex_lock(&kvm->lock);
> +       ret = __modify_kvm_id_reg(kvm, id, val, preserve_mask);
> +       mutex_unlock(&kvm->lock);
> +
> +       return ret;
> +}

I think you probably wanted these changes to modify_kvm_id_reg() to go
into the previous patch rather than in this one.


>  static int write_kvm_id_reg(struct kvm *kvm, u32 id, u64 val)
>  {
>         return modify_kvm_id_reg(kvm, id, val, 0);
> @@ -1233,20 +1331,6 @@ static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
>                 val &= ~(id_reg->vcpu_mask(vcpu, id_reg));
>
>         switch (id) {
> -       case SYS_ID_AA64PFR0_EL1:
> -               if (!vcpu_has_sve(vcpu))
> -                       val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_SVE);
> -               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_AMU);
> -               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_CSV2);
> -               val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_CSV2), (u64)vcpu->kvm->arch.pfr0_csv2);
> -               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_CSV3);
> -               val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_CSV3), (u64)vcpu->kvm->arch.pfr0_csv3);
> -               if (irqchip_in_kernel(vcpu->kvm) &&
> -                   vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3) {
> -                       val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_GIC);
> -                       val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_GIC), 1);
> -               }
> -               break;
>         case SYS_ID_AA64PFR1_EL1:
>                 if (!kvm_has_mte(vcpu->kvm))
>                         val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_MTE);
> @@ -1347,48 +1431,6 @@ static unsigned int sve_visibility(const struct kvm_vcpu *vcpu,
>         return REG_HIDDEN;
>  }
>
> -static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
> -                              const struct sys_reg_desc *rd,
> -                              const struct kvm_one_reg *reg, void __user *uaddr)
> -{
> -       const u64 id = sys_reg_to_index(rd);
> -       u8 csv2, csv3;
> -       int err;
> -       u64 val;
> -
> -       err = reg_from_user(&val, uaddr, id);
> -       if (err)
> -               return err;
> -
> -       /*
> -        * Allow AA64PFR0_EL1.CSV2 to be set from userspace as long as
> -        * it doesn't promise more than what is actually provided (the
> -        * guest could otherwise be covered in ectoplasmic residue).
> -        */
> -       csv2 = cpuid_feature_extract_unsigned_field(val, ID_AA64PFR0_CSV2_SHIFT);
> -       if (csv2 > 1 ||
> -           (csv2 && arm64_get_spectre_v2_state() != SPECTRE_UNAFFECTED))
> -               return -EINVAL;
> -
> -       /* Same thing for CSV3 */
> -       csv3 = cpuid_feature_extract_unsigned_field(val, ID_AA64PFR0_CSV3_SHIFT);
> -       if (csv3 > 1 ||
> -           (csv3 && arm64_get_meltdown_state() != SPECTRE_UNAFFECTED))
> -               return -EINVAL;
> -
> -       /* We can only differ with CSV[23], and anything else is an error */
> -       val ^= read_id_reg(vcpu, rd, false);
> -       val &= ~((0xFUL << ID_AA64PFR0_CSV2_SHIFT) |
> -                (0xFUL << ID_AA64PFR0_CSV3_SHIFT));
> -       if (val)
> -               return -EINVAL;
> -
> -       vcpu->kvm->arch.pfr0_csv2 = csv2;
> -       vcpu->kvm->arch.pfr0_csv3 = csv3 ;
> -
> -       return 0;
> -}
> -
>  /* cpufeature ID register user accessors */
>  static int __get_id_reg(const struct kvm_vcpu *vcpu,
>                         const struct sys_reg_desc *rd, void __user *uaddr,
> @@ -1702,8 +1744,7 @@ static const struct sys_reg_desc sys_reg_descs[] = {
>
>         /* AArch64 ID registers */
>         /* CRm=4 */
> -       { SYS_DESC(SYS_ID_AA64PFR0_EL1), .access = access_id_reg,
> -         .get_user = get_id_reg, .set_user = set_id_aa64pfr0_el1, },
> +       ID_SANITISED(ID_AA64PFR0_EL1),
>         ID_SANITISED(ID_AA64PFR1_EL1),
>         ID_UNALLOCATED(4,2),
>         ID_UNALLOCATED(4,3),
> @@ -3095,3 +3136,15 @@ void set_default_id_regs(struct kvm *kvm)
>                 (void)write_kvm_id_reg(kvm, id, val);
>         }
>  }
> +
> +/*
> + * Update the ID register's field with @fval for the guest.
> + * The caller is expected to hold the kvm->lock.
> + */
> +int kvm_set_id_reg_feature(struct kvm *kvm, u32 id, u8 field_shift, u8 fval)
> +{
> +       u64 val = ((u64)fval & ARM64_FEATURE_FIELD_MASK) << field_shift;
> +       u64 preserve_mask = ~(ARM64_FEATURE_FIELD_MASK << field_shift);
> +
> +       return __modify_kvm_id_reg(kvm, id, val, preserve_mask);
> +}

This seems to me like it should also be in the previous patch or a
separate patch.

> diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c
> index 0a06d0648970..28d9bf0e178c 100644
> --- a/arch/arm64/kvm/vgic/vgic-init.c
> +++ b/arch/arm64/kvm/vgic/vgic-init.c
> @@ -116,6 +116,11 @@ int kvm_vgic_create(struct kvm *kvm, u32 type)
>         else
>                 INIT_LIST_HEAD(&kvm->arch.vgic.rd_regions);
>
> +       if (type == KVM_DEV_TYPE_ARM_VGIC_V3)
> +               /* Set ID_AA64PFR0_EL1.GIC to 1 */
> +               (void)kvm_set_id_reg_feature(kvm, SYS_ID_AA64PFR0_EL1,
> +                                    ID_AA64PFR0_GIC3, ID_AA64PFR0_GIC_SHIFT);
> +

If this fails wouldn't it be better to return the error?

Thanks,
/fuad


>  out_unlock:
>         unlock_all_vcpus(kvm);
>         return ret;
> --
> 2.34.1.448.ga2b2bfdf31-goog
>
> _______________________________________________
> kvmarm mailing list
> kvmarm@lists.cs.columbia.edu
> https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 04/26] KVM: arm64: Make ID_AA64PFR0_EL1 writable
@ 2022-01-24 16:51     ` Fuad Tabba
  0 siblings, 0 replies; 201+ messages in thread
From: Fuad Tabba @ 2022-01-24 16:51 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, linux-arm-kernel

Hi Reiji,

On Thu, Jan 6, 2022 at 4:28 AM Reiji Watanabe <reijiw@google.com> wrote:
>
> This patch adds id_reg_info for ID_AA64PFR0_EL1 to make it writable by
> userspace.
>
> Return an error if userspace tries to set SVE/GIC field of the register
> to a value that conflicts with SVE/GIC configuration for the guest.
> SIMD/FP/SVE fields of the requested value are validated according to
> Arm ARM.
>
> Signed-off-by: Reiji Watanabe <reijiw@google.com>
> ---
>  arch/arm64/include/asm/kvm_host.h |   1 +
>  arch/arm64/include/asm/sysreg.h   |   2 +
>  arch/arm64/kvm/sys_regs.c         | 177 +++++++++++++++++++-----------
>  arch/arm64/kvm/vgic/vgic-init.c   |   5 +
>  4 files changed, 123 insertions(+), 62 deletions(-)
>
> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index c789a0137f58..4509f9e7472d 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -749,6 +749,7 @@ long kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
>                                 struct kvm_arm_copy_mte_tags *copy_tags);
>
>  void set_default_id_regs(struct kvm *kvm);
> +int kvm_set_id_reg_feature(struct kvm *kvm, u32 id, u8 field_shift, u8 fval);
>
>  /* Guest/host FPSIMD coordination helpers */
>  int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu);
> diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h
> index 16b3f1a1d468..e26027817171 100644
> --- a/arch/arm64/include/asm/sysreg.h
> +++ b/arch/arm64/include/asm/sysreg.h
> @@ -798,6 +798,7 @@
>  #define ID_AA64PFR0_ASIMD_SUPPORTED    0x0
>  #define ID_AA64PFR0_ELx_64BIT_ONLY     0x1
>  #define ID_AA64PFR0_ELx_32BIT_64BIT    0x2
> +#define ID_AA64PFR0_GIC3               0x1
>
>  /* id_aa64pfr1 */
>  #define ID_AA64PFR1_MPAMFRAC_SHIFT     16
> @@ -1197,6 +1198,7 @@
>  #define ICH_VTR_TDS_MASK       (1 << ICH_VTR_TDS_SHIFT)
>
>  #define ARM64_FEATURE_FIELD_BITS       4
> +#define ARM64_FEATURE_FIELD_MASK       ((1ull << ARM64_FEATURE_FIELD_BITS) - 1)
>
>  /* Create a mask for the feature bits of the specified feature. */
>  #define ARM64_FEATURE_MASK(x)  (GENMASK_ULL(x##_SHIFT + ARM64_FEATURE_FIELD_BITS - 1, x##_SHIFT))
> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> index 971018288bee..1eb5c5fb614f 100644
> --- a/arch/arm64/kvm/sys_regs.c
> +++ b/arch/arm64/kvm/sys_regs.c
> @@ -318,6 +318,92 @@ static void id_reg_info_init(struct id_reg_info *id_reg)
>                 id_reg->init(id_reg);
>  }
>
> +static int validate_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
> +                                   const struct id_reg_info *id_reg, u64 val)
> +{
> +       int fp, simd;
> +       unsigned int gic;
> +       bool vcpu_has_sve = vcpu_has_sve(vcpu);
> +       bool pfr0_has_sve = id_aa64pfr0_sve(val);
> +
> +       simd = cpuid_feature_extract_signed_field(val, ID_AA64PFR0_ASIMD_SHIFT);
> +       fp = cpuid_feature_extract_signed_field(val, ID_AA64PFR0_FP_SHIFT);
> +       if (simd != fp)

Why is this the case? Could you add a comment?

> +               return -EINVAL;
> +
> +       /* fp must be supported when sve is supported */
> +       if (pfr0_has_sve && (fp < 0))
> +               return -EINVAL;
> +
> +       /* Check if there is a conflict with a request via KVM_ARM_VCPU_INIT */
> +       if (vcpu_has_sve ^ pfr0_has_sve)
> +               return -EPERM;
> +
> +       if ((irqchip_in_kernel(vcpu->kvm) &&
> +            vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3)) {
> +               gic = cpuid_feature_extract_unsigned_field(val,
> +                                                       ID_AA64PFR0_GIC_SHIFT);
> +               if (gic == 0)
> +                       return -EPERM;
> +
> +               if (gic > ID_AA64PFR0_GIC3)
> +                       return -E2BIG;
> +       } else {
> +               u64 mask = ARM64_FEATURE_MASK(ID_AA64PFR0_GIC);
> +               int err = arm64_check_features(id_reg->sys_reg, val & mask,
> +                                              id_reg->vcpu_limit_val & mask);
> +               if (err)
> +                       return err;
> +       }
> +
> +       return 0;
> +}
> +
> +static void init_id_aa64pfr0_el1_info(struct id_reg_info *id_reg)
> +{
> +       u64 limit = id_reg->vcpu_limit_val;
> +       unsigned int gic;
> +
> +       limit &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_AMU);
> +       if (!system_supports_sve())
> +               limit &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_SVE);
> +
> +       /*
> +        * The default is to expose CSV2 == 1 and CSV3 == 1 if the HW
> +        * isn't affected.  Userspace can override this as long as it
> +        * doesn't promise the impossible.
> +        */
> +       limit &= ~(ARM64_FEATURE_MASK(ID_AA64PFR0_CSV2) |
> +                  ARM64_FEATURE_MASK(ID_AA64PFR0_CSV3));
> +
> +       if (arm64_get_spectre_v2_state() == SPECTRE_UNAFFECTED)
> +               limit |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_CSV2), 1);
> +       if (arm64_get_meltdown_state() == SPECTRE_UNAFFECTED)
> +               limit |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_CSV3), 1);
> +
> +       gic = cpuid_feature_extract_unsigned_field(limit, ID_AA64PFR0_GIC_SHIFT);
> +       if (gic > 1) {
> +               /* Limit to GICv3.0/4.0 */
> +               limit &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_GIC);
> +               limit |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_GIC), ID_AA64PFR0_GIC3);
> +       }
> +       id_reg->vcpu_limit_val = limit;
> +}
> +
> +static u64 vcpu_mask_id_aa64pfr0_el1(const struct kvm_vcpu *vcpu,
> +                                        const struct id_reg_info *idr)
> +{
> +       return vcpu_has_sve(vcpu) ? 0 : ARM64_FEATURE_MASK(ID_AA64PFR0_SVE);
> +}
> +
> +static struct id_reg_info id_aa64pfr0_el1_info = {
> +       .sys_reg = SYS_ID_AA64PFR0_EL1,
> +       .ignore_mask = ARM64_FEATURE_MASK(ID_AA64PFR0_GIC),
> +       .init = init_id_aa64pfr0_el1_info,
> +       .validate = validate_id_aa64pfr0_el1,
> +       .vcpu_mask = vcpu_mask_id_aa64pfr0_el1,
> +};
> +
>  /*
>   * An ID register that needs special handling to control the value for the
>   * guest must have its own id_reg_info in id_reg_info_table.
> @@ -326,7 +412,9 @@ static void id_reg_info_init(struct id_reg_info *id_reg)
>   * validation, etc.)
>   */
>  #define        GET_ID_REG_INFO(id)     (id_reg_info_table[IDREG_IDX(id)])
> -static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {};
> +static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {
> +       [IDREG_IDX(SYS_ID_AA64PFR0_EL1)] = &id_aa64pfr0_el1_info,
> +};
>
>  static int validate_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
>  {
> @@ -1161,12 +1249,12 @@ static u64 read_kvm_id_reg(struct kvm *kvm, u32 id)
>         return kvm->arch.id_regs[IDREG_IDX(id)];
>  }
>
> -static int modify_kvm_id_reg(struct kvm *kvm, u32 id, u64 val,
> +static int __modify_kvm_id_reg(struct kvm *kvm, u32 id, u64 val,
>                              u64 preserve_mask)
>  {
>         u64 old, new;
>
> -       mutex_lock(&kvm->lock);
> +       lockdep_assert_held(&kvm->lock);
>
>         old = kvm->arch.id_regs[IDREG_IDX(id)];
>
> @@ -1179,11 +1267,21 @@ static int modify_kvm_id_reg(struct kvm *kvm, u32 id, u64 val,
>                 return -EBUSY;
>
>         WRITE_ONCE(kvm->arch.id_regs[IDREG_IDX(id)], new);
> -       mutex_unlock(&kvm->lock);
>
>         return 0;
>  }
>
> +static int modify_kvm_id_reg(struct kvm *kvm, u32 id, u64 val,
> +                            u64 preserve_mask)
> +{
> +       int ret;
> +
> +       mutex_lock(&kvm->lock);
> +       ret = __modify_kvm_id_reg(kvm, id, val, preserve_mask);
> +       mutex_unlock(&kvm->lock);
> +
> +       return ret;
> +}

I think you probably wanted these changes to modify_kvm_id_reg() to go
into the previous patch rather than in this one.


>  static int write_kvm_id_reg(struct kvm *kvm, u32 id, u64 val)
>  {
>         return modify_kvm_id_reg(kvm, id, val, 0);
> @@ -1233,20 +1331,6 @@ static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
>                 val &= ~(id_reg->vcpu_mask(vcpu, id_reg));
>
>         switch (id) {
> -       case SYS_ID_AA64PFR0_EL1:
> -               if (!vcpu_has_sve(vcpu))
> -                       val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_SVE);
> -               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_AMU);
> -               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_CSV2);
> -               val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_CSV2), (u64)vcpu->kvm->arch.pfr0_csv2);
> -               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_CSV3);
> -               val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_CSV3), (u64)vcpu->kvm->arch.pfr0_csv3);
> -               if (irqchip_in_kernel(vcpu->kvm) &&
> -                   vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3) {
> -                       val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_GIC);
> -                       val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_GIC), 1);
> -               }
> -               break;
>         case SYS_ID_AA64PFR1_EL1:
>                 if (!kvm_has_mte(vcpu->kvm))
>                         val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_MTE);
> @@ -1347,48 +1431,6 @@ static unsigned int sve_visibility(const struct kvm_vcpu *vcpu,
>         return REG_HIDDEN;
>  }
>
> -static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
> -                              const struct sys_reg_desc *rd,
> -                              const struct kvm_one_reg *reg, void __user *uaddr)
> -{
> -       const u64 id = sys_reg_to_index(rd);
> -       u8 csv2, csv3;
> -       int err;
> -       u64 val;
> -
> -       err = reg_from_user(&val, uaddr, id);
> -       if (err)
> -               return err;
> -
> -       /*
> -        * Allow AA64PFR0_EL1.CSV2 to be set from userspace as long as
> -        * it doesn't promise more than what is actually provided (the
> -        * guest could otherwise be covered in ectoplasmic residue).
> -        */
> -       csv2 = cpuid_feature_extract_unsigned_field(val, ID_AA64PFR0_CSV2_SHIFT);
> -       if (csv2 > 1 ||
> -           (csv2 && arm64_get_spectre_v2_state() != SPECTRE_UNAFFECTED))
> -               return -EINVAL;
> -
> -       /* Same thing for CSV3 */
> -       csv3 = cpuid_feature_extract_unsigned_field(val, ID_AA64PFR0_CSV3_SHIFT);
> -       if (csv3 > 1 ||
> -           (csv3 && arm64_get_meltdown_state() != SPECTRE_UNAFFECTED))
> -               return -EINVAL;
> -
> -       /* We can only differ with CSV[23], and anything else is an error */
> -       val ^= read_id_reg(vcpu, rd, false);
> -       val &= ~((0xFUL << ID_AA64PFR0_CSV2_SHIFT) |
> -                (0xFUL << ID_AA64PFR0_CSV3_SHIFT));
> -       if (val)
> -               return -EINVAL;
> -
> -       vcpu->kvm->arch.pfr0_csv2 = csv2;
> -       vcpu->kvm->arch.pfr0_csv3 = csv3 ;
> -
> -       return 0;
> -}
> -
>  /* cpufeature ID register user accessors */
>  static int __get_id_reg(const struct kvm_vcpu *vcpu,
>                         const struct sys_reg_desc *rd, void __user *uaddr,
> @@ -1702,8 +1744,7 @@ static const struct sys_reg_desc sys_reg_descs[] = {
>
>         /* AArch64 ID registers */
>         /* CRm=4 */
> -       { SYS_DESC(SYS_ID_AA64PFR0_EL1), .access = access_id_reg,
> -         .get_user = get_id_reg, .set_user = set_id_aa64pfr0_el1, },
> +       ID_SANITISED(ID_AA64PFR0_EL1),
>         ID_SANITISED(ID_AA64PFR1_EL1),
>         ID_UNALLOCATED(4,2),
>         ID_UNALLOCATED(4,3),
> @@ -3095,3 +3136,15 @@ void set_default_id_regs(struct kvm *kvm)
>                 (void)write_kvm_id_reg(kvm, id, val);
>         }
>  }
> +
> +/*
> + * Update the ID register's field with @fval for the guest.
> + * The caller is expected to hold the kvm->lock.
> + */
> +int kvm_set_id_reg_feature(struct kvm *kvm, u32 id, u8 field_shift, u8 fval)
> +{
> +       u64 val = ((u64)fval & ARM64_FEATURE_FIELD_MASK) << field_shift;
> +       u64 preserve_mask = ~(ARM64_FEATURE_FIELD_MASK << field_shift);
> +
> +       return __modify_kvm_id_reg(kvm, id, val, preserve_mask);
> +}

This seems to me like it should also be in the previous patch or a
separate patch.

> diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c
> index 0a06d0648970..28d9bf0e178c 100644
> --- a/arch/arm64/kvm/vgic/vgic-init.c
> +++ b/arch/arm64/kvm/vgic/vgic-init.c
> @@ -116,6 +116,11 @@ int kvm_vgic_create(struct kvm *kvm, u32 type)
>         else
>                 INIT_LIST_HEAD(&kvm->arch.vgic.rd_regions);
>
> +       if (type == KVM_DEV_TYPE_ARM_VGIC_V3)
> +               /* Set ID_AA64PFR0_EL1.GIC to 1 */
> +               (void)kvm_set_id_reg_feature(kvm, SYS_ID_AA64PFR0_EL1,
> +                                    ID_AA64PFR0_GIC3, ID_AA64PFR0_GIC_SHIFT);
> +

If this fails wouldn't it be better to return the error?

Thanks,
/fuad


>  out_unlock:
>         unlock_all_vcpus(kvm);
>         return ret;
> --
> 2.34.1.448.ga2b2bfdf31-goog
>
> _______________________________________________
> kvmarm mailing list
> kvmarm@lists.cs.columbia.edu
> https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 14/26] KVM: arm64: Add consistency checking for frac fields of ID registers
  2022-01-06  4:26   ` Reiji Watanabe
  (?)
@ 2022-01-24 17:00     ` Fuad Tabba
  -1 siblings, 0 replies; 201+ messages in thread
From: Fuad Tabba @ 2022-01-24 17:00 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, linux-arm-kernel

Hi Reiji,

On Thu, Jan 6, 2022 at 4:29 AM Reiji Watanabe <reijiw@google.com> wrote:
>
> Feature fractional field of an ID register cannot be simply validated
> at KVM_SET_ONE_REG because its validity depends on its (main) feature
> field value, which could be in a different ID register (and might be
> set later).
> Validate fractional fields at the first KVM_RUN instead.
>
> Signed-off-by: Reiji Watanabe <reijiw@google.com>
> ---
>  arch/arm64/include/asm/kvm_host.h |   1 +
>  arch/arm64/kvm/arm.c              |   3 +
>  arch/arm64/kvm/sys_regs.c         | 116 +++++++++++++++++++++++++++++-
>  3 files changed, 117 insertions(+), 3 deletions(-)
>
> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index 4509f9e7472d..7b3f86bd6a6b 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -750,6 +750,7 @@ long kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
>
>  void set_default_id_regs(struct kvm *kvm);
>  int kvm_set_id_reg_feature(struct kvm *kvm, u32 id, u8 field_shift, u8 fval);
> +int kvm_id_regs_consistency_check(const struct kvm_vcpu *vcpu);
>
>  /* Guest/host FPSIMD coordination helpers */
>  int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu);
> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> index 5f497a0af254..16fc2ce32069 100644
> --- a/arch/arm64/kvm/arm.c
> +++ b/arch/arm64/kvm/arm.c
> @@ -596,6 +596,9 @@ static int kvm_vcpu_first_run_init(struct kvm_vcpu *vcpu)
>         if (!kvm_arm_vcpu_is_finalized(vcpu))
>                 return -EPERM;
>
> +       if (!kvm_vm_is_protected(kvm) && kvm_id_regs_consistency_check(vcpu))
> +               return -EPERM;
> +
>         vcpu->arch.has_run_once = true;
>
>         kvm_arm_vcpu_init_debug(vcpu);
> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> index ddbeefc3881c..6adb7b04620c 100644
> --- a/arch/arm64/kvm/sys_regs.c
> +++ b/arch/arm64/kvm/sys_regs.c
> @@ -756,9 +756,6 @@ static struct id_reg_info id_aa64pfr0_el1_info = {
>
>  static struct id_reg_info id_aa64pfr1_el1_info = {
>         .sys_reg = SYS_ID_AA64PFR1_EL1,
> -       .ignore_mask = ARM64_FEATURE_MASK(ID_AA64PFR1_RASFRAC) |
> -                      ARM64_FEATURE_MASK(ID_AA64PFR1_MPAMFRAC) |
> -                      ARM64_FEATURE_MASK(ID_AA64PFR1_CSV2FRAC),
>         .init = init_id_aa64pfr1_el1_info,
>         .validate = validate_id_aa64pfr1_el1,
>         .vcpu_mask = vcpu_mask_id_aa64pfr1_el1,
> @@ -3434,10 +3431,109 @@ int kvm_arm_copy_sys_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
>         return write_demux_regids(uindices);
>  }
>
> +/* ID register's fractional field information with its feature field. */
> +struct feature_frac {
> +       u32     id;
> +       u32     shift;
> +       u32     frac_id;
> +       u32     frac_shift;
> +       u8      frac_ftr_check;
> +};

frac_ftr_check doesn't seem to be used. Also, it would be easier to
read if the ordering of the fields match the ordering you initialize
them below.

> +
> +static struct feature_frac feature_frac_table[] = {
> +       {
> +               .frac_id = SYS_ID_AA64PFR1_EL1,
> +               .frac_shift = ID_AA64PFR1_RASFRAC_SHIFT,
> +               .id = SYS_ID_AA64PFR0_EL1,
> +               .shift = ID_AA64PFR0_RAS_SHIFT,
> +       },
> +       {
> +               .frac_id = SYS_ID_AA64PFR1_EL1,
> +               .frac_shift = ID_AA64PFR1_MPAMFRAC_SHIFT,
> +               .id = SYS_ID_AA64PFR0_EL1,
> +               .shift = ID_AA64PFR0_MPAM_SHIFT,
> +       },
> +       {
> +               .frac_id = SYS_ID_AA64PFR1_EL1,
> +               .frac_shift = ID_AA64PFR1_CSV2FRAC_SHIFT,
> +               .id = SYS_ID_AA64PFR0_EL1,
> +               .shift = ID_AA64PFR0_CSV2_SHIFT,
> +       },
> +};
> +
> +/*
> + * Return non-zero if the feature/fractional fields pair are not
> + * supported. Return zero otherwise.
> + * This function validates only the fractional feature field,
> + * and relies on the fact the feature field is validated before
> + * through arm64_check_features.
> + */
> +static int vcpu_id_reg_feature_frac_check(const struct kvm_vcpu *vcpu,
> +                                         const struct feature_frac *ftr_frac)
> +{
> +       const struct id_reg_info *id_reg;
> +       u32 id;
> +       u64 val, lim, mask;
> +
> +       /* Check if the feature field value is same as the limit */
> +       id = ftr_frac->id;
> +       id_reg = GET_ID_REG_INFO(id);
> +
> +       mask = (u64)ARM64_FEATURE_FIELD_MASK << ftr_frac->shift;
> +       val = __read_id_reg(vcpu, id) & mask;
> +       lim = id_reg ? id_reg->vcpu_limit_val : read_sanitised_ftr_reg(id);
> +       lim &= mask;
> +
> +       if (val != lim)
> +               /*
> +                * The feature level is lower than the limit.
> +                * Any fractional version should be fine.
> +                */
> +               return 0;
> +
> +       /* Check the fractional feature field */
> +       id = ftr_frac->frac_id;
> +       id_reg = GET_ID_REG_INFO(id);
> +
> +       mask = (u64)ARM64_FEATURE_FIELD_MASK << ftr_frac->frac_shift;
> +       val = __read_id_reg(vcpu, id) & mask;
> +       lim = id_reg ? id_reg->vcpu_limit_val : read_sanitised_ftr_reg(id);
> +       lim &= mask;
> +
> +       if (val == lim)
> +               /*
> +                * Both the feature and fractional fields are the same
> +                * as limit.
> +                */
> +               return 0;
> +
> +       return arm64_check_features(id, val, lim);
> +}
> +
> +int kvm_id_regs_consistency_check(const struct kvm_vcpu *vcpu)

Nit: considering that this is only checking the fractional fields,
should the function name reflect that?

> +{
> +       int i, err;
> +       const struct feature_frac *frac;
> +
> +       /*
> +        * Check ID registers' fractional fields, which aren't checked
> +        * at KVM_SET_ONE_REG.
> +        */
> +       for (i = 0; i < ARRAY_SIZE(feature_frac_table); i++) {
> +               frac = &feature_frac_table[i];
> +               err = vcpu_id_reg_feature_frac_check(vcpu, frac);
> +               if (err)
> +                       return err;
> +       }
> +       return 0;
> +}
> +
>  static void id_reg_info_init_all(void)
>  {
>         int i;
>         struct id_reg_info *id_reg;
> +       struct feature_frac *frac;
> +       u64 ftr_mask = ARM64_FEATURE_FIELD_MASK;
>
>         for (i = 0; i < ARRAY_SIZE(id_reg_info_table); i++) {
>                 id_reg = (struct id_reg_info *)id_reg_info_table[i];
> @@ -3446,6 +3542,20 @@ static void id_reg_info_init_all(void)
>
>                 id_reg_info_init(id_reg);
>         }
> +
> +       /*
> +        * Update ignore_mask of ID registers based on fractional fields
> +        * information.  Any ID register that have fractional fields
> +        * is expected to have its own id_reg_info.
> +        */
> +       for (i = 0; i < ARRAY_SIZE(feature_frac_table); i++) {
> +               frac = &feature_frac_table[i];
> +               id_reg = GET_ID_REG_INFO(frac->frac_id);
> +               if (WARN_ON_ONCE(!id_reg))
> +                       continue;
> +
> +               id_reg->ignore_mask |= ftr_mask << frac->frac_shift;
> +       }
>  }

Thanks,
/fuad


>
>  void kvm_sys_reg_table_init(void)
> --
> 2.34.1.448.ga2b2bfdf31-goog
>
> _______________________________________________
> kvmarm mailing list
> kvmarm@lists.cs.columbia.edu
> https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 14/26] KVM: arm64: Add consistency checking for frac fields of ID registers
@ 2022-01-24 17:00     ` Fuad Tabba
  0 siblings, 0 replies; 201+ messages in thread
From: Fuad Tabba @ 2022-01-24 17:00 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: kvm, Marc Zyngier, Peter Shier, Paolo Bonzini, Will Deacon,
	kvmarm, linux-arm-kernel

Hi Reiji,

On Thu, Jan 6, 2022 at 4:29 AM Reiji Watanabe <reijiw@google.com> wrote:
>
> Feature fractional field of an ID register cannot be simply validated
> at KVM_SET_ONE_REG because its validity depends on its (main) feature
> field value, which could be in a different ID register (and might be
> set later).
> Validate fractional fields at the first KVM_RUN instead.
>
> Signed-off-by: Reiji Watanabe <reijiw@google.com>
> ---
>  arch/arm64/include/asm/kvm_host.h |   1 +
>  arch/arm64/kvm/arm.c              |   3 +
>  arch/arm64/kvm/sys_regs.c         | 116 +++++++++++++++++++++++++++++-
>  3 files changed, 117 insertions(+), 3 deletions(-)
>
> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index 4509f9e7472d..7b3f86bd6a6b 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -750,6 +750,7 @@ long kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
>
>  void set_default_id_regs(struct kvm *kvm);
>  int kvm_set_id_reg_feature(struct kvm *kvm, u32 id, u8 field_shift, u8 fval);
> +int kvm_id_regs_consistency_check(const struct kvm_vcpu *vcpu);
>
>  /* Guest/host FPSIMD coordination helpers */
>  int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu);
> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> index 5f497a0af254..16fc2ce32069 100644
> --- a/arch/arm64/kvm/arm.c
> +++ b/arch/arm64/kvm/arm.c
> @@ -596,6 +596,9 @@ static int kvm_vcpu_first_run_init(struct kvm_vcpu *vcpu)
>         if (!kvm_arm_vcpu_is_finalized(vcpu))
>                 return -EPERM;
>
> +       if (!kvm_vm_is_protected(kvm) && kvm_id_regs_consistency_check(vcpu))
> +               return -EPERM;
> +
>         vcpu->arch.has_run_once = true;
>
>         kvm_arm_vcpu_init_debug(vcpu);
> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> index ddbeefc3881c..6adb7b04620c 100644
> --- a/arch/arm64/kvm/sys_regs.c
> +++ b/arch/arm64/kvm/sys_regs.c
> @@ -756,9 +756,6 @@ static struct id_reg_info id_aa64pfr0_el1_info = {
>
>  static struct id_reg_info id_aa64pfr1_el1_info = {
>         .sys_reg = SYS_ID_AA64PFR1_EL1,
> -       .ignore_mask = ARM64_FEATURE_MASK(ID_AA64PFR1_RASFRAC) |
> -                      ARM64_FEATURE_MASK(ID_AA64PFR1_MPAMFRAC) |
> -                      ARM64_FEATURE_MASK(ID_AA64PFR1_CSV2FRAC),
>         .init = init_id_aa64pfr1_el1_info,
>         .validate = validate_id_aa64pfr1_el1,
>         .vcpu_mask = vcpu_mask_id_aa64pfr1_el1,
> @@ -3434,10 +3431,109 @@ int kvm_arm_copy_sys_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
>         return write_demux_regids(uindices);
>  }
>
> +/* ID register's fractional field information with its feature field. */
> +struct feature_frac {
> +       u32     id;
> +       u32     shift;
> +       u32     frac_id;
> +       u32     frac_shift;
> +       u8      frac_ftr_check;
> +};

frac_ftr_check doesn't seem to be used. Also, it would be easier to
read if the ordering of the fields match the ordering you initialize
them below.

> +
> +static struct feature_frac feature_frac_table[] = {
> +       {
> +               .frac_id = SYS_ID_AA64PFR1_EL1,
> +               .frac_shift = ID_AA64PFR1_RASFRAC_SHIFT,
> +               .id = SYS_ID_AA64PFR0_EL1,
> +               .shift = ID_AA64PFR0_RAS_SHIFT,
> +       },
> +       {
> +               .frac_id = SYS_ID_AA64PFR1_EL1,
> +               .frac_shift = ID_AA64PFR1_MPAMFRAC_SHIFT,
> +               .id = SYS_ID_AA64PFR0_EL1,
> +               .shift = ID_AA64PFR0_MPAM_SHIFT,
> +       },
> +       {
> +               .frac_id = SYS_ID_AA64PFR1_EL1,
> +               .frac_shift = ID_AA64PFR1_CSV2FRAC_SHIFT,
> +               .id = SYS_ID_AA64PFR0_EL1,
> +               .shift = ID_AA64PFR0_CSV2_SHIFT,
> +       },
> +};
> +
> +/*
> + * Return non-zero if the feature/fractional fields pair are not
> + * supported. Return zero otherwise.
> + * This function validates only the fractional feature field,
> + * and relies on the fact the feature field is validated before
> + * through arm64_check_features.
> + */
> +static int vcpu_id_reg_feature_frac_check(const struct kvm_vcpu *vcpu,
> +                                         const struct feature_frac *ftr_frac)
> +{
> +       const struct id_reg_info *id_reg;
> +       u32 id;
> +       u64 val, lim, mask;
> +
> +       /* Check if the feature field value is same as the limit */
> +       id = ftr_frac->id;
> +       id_reg = GET_ID_REG_INFO(id);
> +
> +       mask = (u64)ARM64_FEATURE_FIELD_MASK << ftr_frac->shift;
> +       val = __read_id_reg(vcpu, id) & mask;
> +       lim = id_reg ? id_reg->vcpu_limit_val : read_sanitised_ftr_reg(id);
> +       lim &= mask;
> +
> +       if (val != lim)
> +               /*
> +                * The feature level is lower than the limit.
> +                * Any fractional version should be fine.
> +                */
> +               return 0;
> +
> +       /* Check the fractional feature field */
> +       id = ftr_frac->frac_id;
> +       id_reg = GET_ID_REG_INFO(id);
> +
> +       mask = (u64)ARM64_FEATURE_FIELD_MASK << ftr_frac->frac_shift;
> +       val = __read_id_reg(vcpu, id) & mask;
> +       lim = id_reg ? id_reg->vcpu_limit_val : read_sanitised_ftr_reg(id);
> +       lim &= mask;
> +
> +       if (val == lim)
> +               /*
> +                * Both the feature and fractional fields are the same
> +                * as limit.
> +                */
> +               return 0;
> +
> +       return arm64_check_features(id, val, lim);
> +}
> +
> +int kvm_id_regs_consistency_check(const struct kvm_vcpu *vcpu)

Nit: considering that this is only checking the fractional fields,
should the function name reflect that?

> +{
> +       int i, err;
> +       const struct feature_frac *frac;
> +
> +       /*
> +        * Check ID registers' fractional fields, which aren't checked
> +        * at KVM_SET_ONE_REG.
> +        */
> +       for (i = 0; i < ARRAY_SIZE(feature_frac_table); i++) {
> +               frac = &feature_frac_table[i];
> +               err = vcpu_id_reg_feature_frac_check(vcpu, frac);
> +               if (err)
> +                       return err;
> +       }
> +       return 0;
> +}
> +
>  static void id_reg_info_init_all(void)
>  {
>         int i;
>         struct id_reg_info *id_reg;
> +       struct feature_frac *frac;
> +       u64 ftr_mask = ARM64_FEATURE_FIELD_MASK;
>
>         for (i = 0; i < ARRAY_SIZE(id_reg_info_table); i++) {
>                 id_reg = (struct id_reg_info *)id_reg_info_table[i];
> @@ -3446,6 +3542,20 @@ static void id_reg_info_init_all(void)
>
>                 id_reg_info_init(id_reg);
>         }
> +
> +       /*
> +        * Update ignore_mask of ID registers based on fractional fields
> +        * information.  Any ID register that have fractional fields
> +        * is expected to have its own id_reg_info.
> +        */
> +       for (i = 0; i < ARRAY_SIZE(feature_frac_table); i++) {
> +               frac = &feature_frac_table[i];
> +               id_reg = GET_ID_REG_INFO(frac->frac_id);
> +               if (WARN_ON_ONCE(!id_reg))
> +                       continue;
> +
> +               id_reg->ignore_mask |= ftr_mask << frac->frac_shift;
> +       }
>  }

Thanks,
/fuad


>
>  void kvm_sys_reg_table_init(void)
> --
> 2.34.1.448.ga2b2bfdf31-goog
>
> _______________________________________________
> kvmarm mailing list
> kvmarm@lists.cs.columbia.edu
> https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 14/26] KVM: arm64: Add consistency checking for frac fields of ID registers
@ 2022-01-24 17:00     ` Fuad Tabba
  0 siblings, 0 replies; 201+ messages in thread
From: Fuad Tabba @ 2022-01-24 17:00 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, linux-arm-kernel

Hi Reiji,

On Thu, Jan 6, 2022 at 4:29 AM Reiji Watanabe <reijiw@google.com> wrote:
>
> Feature fractional field of an ID register cannot be simply validated
> at KVM_SET_ONE_REG because its validity depends on its (main) feature
> field value, which could be in a different ID register (and might be
> set later).
> Validate fractional fields at the first KVM_RUN instead.
>
> Signed-off-by: Reiji Watanabe <reijiw@google.com>
> ---
>  arch/arm64/include/asm/kvm_host.h |   1 +
>  arch/arm64/kvm/arm.c              |   3 +
>  arch/arm64/kvm/sys_regs.c         | 116 +++++++++++++++++++++++++++++-
>  3 files changed, 117 insertions(+), 3 deletions(-)
>
> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index 4509f9e7472d..7b3f86bd6a6b 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -750,6 +750,7 @@ long kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
>
>  void set_default_id_regs(struct kvm *kvm);
>  int kvm_set_id_reg_feature(struct kvm *kvm, u32 id, u8 field_shift, u8 fval);
> +int kvm_id_regs_consistency_check(const struct kvm_vcpu *vcpu);
>
>  /* Guest/host FPSIMD coordination helpers */
>  int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu);
> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> index 5f497a0af254..16fc2ce32069 100644
> --- a/arch/arm64/kvm/arm.c
> +++ b/arch/arm64/kvm/arm.c
> @@ -596,6 +596,9 @@ static int kvm_vcpu_first_run_init(struct kvm_vcpu *vcpu)
>         if (!kvm_arm_vcpu_is_finalized(vcpu))
>                 return -EPERM;
>
> +       if (!kvm_vm_is_protected(kvm) && kvm_id_regs_consistency_check(vcpu))
> +               return -EPERM;
> +
>         vcpu->arch.has_run_once = true;
>
>         kvm_arm_vcpu_init_debug(vcpu);
> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> index ddbeefc3881c..6adb7b04620c 100644
> --- a/arch/arm64/kvm/sys_regs.c
> +++ b/arch/arm64/kvm/sys_regs.c
> @@ -756,9 +756,6 @@ static struct id_reg_info id_aa64pfr0_el1_info = {
>
>  static struct id_reg_info id_aa64pfr1_el1_info = {
>         .sys_reg = SYS_ID_AA64PFR1_EL1,
> -       .ignore_mask = ARM64_FEATURE_MASK(ID_AA64PFR1_RASFRAC) |
> -                      ARM64_FEATURE_MASK(ID_AA64PFR1_MPAMFRAC) |
> -                      ARM64_FEATURE_MASK(ID_AA64PFR1_CSV2FRAC),
>         .init = init_id_aa64pfr1_el1_info,
>         .validate = validate_id_aa64pfr1_el1,
>         .vcpu_mask = vcpu_mask_id_aa64pfr1_el1,
> @@ -3434,10 +3431,109 @@ int kvm_arm_copy_sys_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
>         return write_demux_regids(uindices);
>  }
>
> +/* ID register's fractional field information with its feature field. */
> +struct feature_frac {
> +       u32     id;
> +       u32     shift;
> +       u32     frac_id;
> +       u32     frac_shift;
> +       u8      frac_ftr_check;
> +};

frac_ftr_check doesn't seem to be used. Also, it would be easier to
read if the ordering of the fields match the ordering you initialize
them below.

> +
> +static struct feature_frac feature_frac_table[] = {
> +       {
> +               .frac_id = SYS_ID_AA64PFR1_EL1,
> +               .frac_shift = ID_AA64PFR1_RASFRAC_SHIFT,
> +               .id = SYS_ID_AA64PFR0_EL1,
> +               .shift = ID_AA64PFR0_RAS_SHIFT,
> +       },
> +       {
> +               .frac_id = SYS_ID_AA64PFR1_EL1,
> +               .frac_shift = ID_AA64PFR1_MPAMFRAC_SHIFT,
> +               .id = SYS_ID_AA64PFR0_EL1,
> +               .shift = ID_AA64PFR0_MPAM_SHIFT,
> +       },
> +       {
> +               .frac_id = SYS_ID_AA64PFR1_EL1,
> +               .frac_shift = ID_AA64PFR1_CSV2FRAC_SHIFT,
> +               .id = SYS_ID_AA64PFR0_EL1,
> +               .shift = ID_AA64PFR0_CSV2_SHIFT,
> +       },
> +};
> +
> +/*
> + * Return non-zero if the feature/fractional fields pair are not
> + * supported. Return zero otherwise.
> + * This function validates only the fractional feature field,
> + * and relies on the fact the feature field is validated before
> + * through arm64_check_features.
> + */
> +static int vcpu_id_reg_feature_frac_check(const struct kvm_vcpu *vcpu,
> +                                         const struct feature_frac *ftr_frac)
> +{
> +       const struct id_reg_info *id_reg;
> +       u32 id;
> +       u64 val, lim, mask;
> +
> +       /* Check if the feature field value is same as the limit */
> +       id = ftr_frac->id;
> +       id_reg = GET_ID_REG_INFO(id);
> +
> +       mask = (u64)ARM64_FEATURE_FIELD_MASK << ftr_frac->shift;
> +       val = __read_id_reg(vcpu, id) & mask;
> +       lim = id_reg ? id_reg->vcpu_limit_val : read_sanitised_ftr_reg(id);
> +       lim &= mask;
> +
> +       if (val != lim)
> +               /*
> +                * The feature level is lower than the limit.
> +                * Any fractional version should be fine.
> +                */
> +               return 0;
> +
> +       /* Check the fractional feature field */
> +       id = ftr_frac->frac_id;
> +       id_reg = GET_ID_REG_INFO(id);
> +
> +       mask = (u64)ARM64_FEATURE_FIELD_MASK << ftr_frac->frac_shift;
> +       val = __read_id_reg(vcpu, id) & mask;
> +       lim = id_reg ? id_reg->vcpu_limit_val : read_sanitised_ftr_reg(id);
> +       lim &= mask;
> +
> +       if (val == lim)
> +               /*
> +                * Both the feature and fractional fields are the same
> +                * as limit.
> +                */
> +               return 0;
> +
> +       return arm64_check_features(id, val, lim);
> +}
> +
> +int kvm_id_regs_consistency_check(const struct kvm_vcpu *vcpu)

Nit: considering that this is only checking the fractional fields,
should the function name reflect that?

> +{
> +       int i, err;
> +       const struct feature_frac *frac;
> +
> +       /*
> +        * Check ID registers' fractional fields, which aren't checked
> +        * at KVM_SET_ONE_REG.
> +        */
> +       for (i = 0; i < ARRAY_SIZE(feature_frac_table); i++) {
> +               frac = &feature_frac_table[i];
> +               err = vcpu_id_reg_feature_frac_check(vcpu, frac);
> +               if (err)
> +                       return err;
> +       }
> +       return 0;
> +}
> +
>  static void id_reg_info_init_all(void)
>  {
>         int i;
>         struct id_reg_info *id_reg;
> +       struct feature_frac *frac;
> +       u64 ftr_mask = ARM64_FEATURE_FIELD_MASK;
>
>         for (i = 0; i < ARRAY_SIZE(id_reg_info_table); i++) {
>                 id_reg = (struct id_reg_info *)id_reg_info_table[i];
> @@ -3446,6 +3542,20 @@ static void id_reg_info_init_all(void)
>
>                 id_reg_info_init(id_reg);
>         }
> +
> +       /*
> +        * Update ignore_mask of ID registers based on fractional fields
> +        * information.  Any ID register that have fractional fields
> +        * is expected to have its own id_reg_info.
> +        */
> +       for (i = 0; i < ARRAY_SIZE(feature_frac_table); i++) {
> +               frac = &feature_frac_table[i];
> +               id_reg = GET_ID_REG_INFO(frac->frac_id);
> +               if (WARN_ON_ONCE(!id_reg))
> +                       continue;
> +
> +               id_reg->ignore_mask |= ftr_mask << frac->frac_shift;
> +       }
>  }

Thanks,
/fuad


>
>  void kvm_sys_reg_table_init(void)
> --
> 2.34.1.448.ga2b2bfdf31-goog
>
> _______________________________________________
> kvmarm mailing list
> kvmarm@lists.cs.columbia.edu
> https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 20/26] KVM: arm64: Trap disabled features of ID_AA64PFR0_EL1
  2022-01-06  4:27   ` Reiji Watanabe
  (?)
@ 2022-01-24 17:16     ` Fuad Tabba
  -1 siblings, 0 replies; 201+ messages in thread
From: Fuad Tabba @ 2022-01-24 17:16 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, linux-arm-kernel

Hi Reiji,

On Thu, Jan 6, 2022 at 4:29 AM Reiji Watanabe <reijiw@google.com> wrote:
>
> Add feature_config_ctrl for RAS and AMU, which are indicated in
> ID_AA64PFR0_EL1, to program configuration registers to trap
> guest's using those features when they are not exposed to the guest.
>
> Introduce trap_ras_regs() to change a behavior of guest's access to
> the registers, which is currently raz/wi, depending on the feature's
> availability for the guest (and inject undefined instruction
> exception when guest's RAS register access are trapped and RAS is
> not exposed to the guest).  In order to keep the current visibility
> of the RAS registers from userspace (always visible), a visibility
> function for RAS registers is not added.
>
> No code is added for AMU's access/visibility handler because the
> current code already injects the exception for Guest's AMU register
> access unconditionally because AMU is never exposed to the guest.

I think it might be code to trap it anyway, in case AMU guest support
is added in the future.

>
> Signed-off-by: Reiji Watanabe <reijiw@google.com>
> ---
>  arch/arm64/kvm/sys_regs.c | 90 +++++++++++++++++++++++++++++++++++----
>  1 file changed, 82 insertions(+), 8 deletions(-)
>
> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> index 33893a501475..015d67092d5e 100644
> --- a/arch/arm64/kvm/sys_regs.c
> +++ b/arch/arm64/kvm/sys_regs.c
> @@ -304,6 +304,63 @@ struct feature_config_ctrl {
>         void    (*trap_activate)(struct kvm_vcpu *vcpu);
>  };
>
> +enum vcpu_config_reg {
> +       VCPU_HCR_EL2 = 1,
> +       VCPU_MDCR_EL2,
> +       VCPU_CPTR_EL2,
> +};
> +
> +static void feature_trap_activate(struct kvm_vcpu *vcpu,
> +                                 enum vcpu_config_reg cfg_reg,
> +                                 u64 cfg_set, u64 cfg_clear)
> +{
> +       u64 *reg_ptr, reg_val;
> +
> +       switch (cfg_reg) {
> +       case VCPU_HCR_EL2:
> +               reg_ptr = &vcpu->arch.hcr_el2;
> +               break;
> +       case VCPU_MDCR_EL2:
> +               reg_ptr = &vcpu->arch.mdcr_el2;
> +               break;
> +       case VCPU_CPTR_EL2:
> +               reg_ptr = &vcpu->arch.cptr_el2;
> +               break;
> +       }
> +
> +       /* Clear/Set fields that are indicated by cfg_clear/cfg_set. */
> +       reg_val = (*reg_ptr & ~cfg_clear);
> +       reg_val |= cfg_set;
> +       *reg_ptr = reg_val;
> +}
> +
> +static void feature_ras_trap_activate(struct kvm_vcpu *vcpu)
> +{
> +       feature_trap_activate(vcpu, VCPU_HCR_EL2, HCR_TERR | HCR_TEA, HCR_FIEN);

Covers all the flags for ras.

> +}
> +
> +static void feature_amu_trap_activate(struct kvm_vcpu *vcpu)
> +{
> +       feature_trap_activate(vcpu, VCPU_CPTR_EL2, CPTR_EL2_TAM, 0);

Covers the CPTR flags for AMU, but as you mentioned, does not
explicitly clear HCR_AMVOFFEN.

Cheers,
/fuad


> +}
> +
> +/* For ID_AA64PFR0_EL1 */
> +static struct feature_config_ctrl ftr_ctrl_ras = {
> +       .ftr_reg = SYS_ID_AA64PFR0_EL1,
> +       .ftr_shift = ID_AA64PFR0_RAS_SHIFT,
> +       .ftr_min = ID_AA64PFR0_RAS_V1,
> +       .ftr_signed = FTR_UNSIGNED,
> +       .trap_activate = feature_ras_trap_activate,
> +};
> +
> +static struct feature_config_ctrl ftr_ctrl_amu = {
> +       .ftr_reg = SYS_ID_AA64PFR0_EL1,
> +       .ftr_shift = ID_AA64PFR0_AMU_SHIFT,
> +       .ftr_min = ID_AA64PFR0_AMU,
> +       .ftr_signed = FTR_UNSIGNED,
> +       .trap_activate = feature_amu_trap_activate,
> +};
> +
>  struct id_reg_info {
>         u32     sys_reg;        /* Register ID */
>         u64     sys_val;        /* Sanitized system value */
> @@ -778,6 +835,11 @@ static struct id_reg_info id_aa64pfr0_el1_info = {
>         .init = init_id_aa64pfr0_el1_info,
>         .validate = validate_id_aa64pfr0_el1,
>         .vcpu_mask = vcpu_mask_id_aa64pfr0_el1,
> +       .trap_features = &(const struct feature_config_ctrl *[]) {
> +               &ftr_ctrl_ras,
> +               &ftr_ctrl_amu,
> +               NULL,
> +       },
>  };
>
>  static struct id_reg_info id_aa64pfr1_el1_info = {
> @@ -901,6 +963,18 @@ static inline bool vcpu_feature_is_available(struct kvm_vcpu *vcpu,
>         return feature_avail(ctrl, val);
>  }
>
> +static bool trap_ras_regs(struct kvm_vcpu *vcpu,
> +                         struct sys_reg_params *p,
> +                         const struct sys_reg_desc *r)
> +{
> +       if (!vcpu_feature_is_available(vcpu, &ftr_ctrl_ras)) {
> +               kvm_inject_undefined(vcpu);
> +               return false;
> +       }
> +
> +       return trap_raz_wi(vcpu, p, r);
> +}
> +
>  /*
>   * ARMv8.1 mandates at least a trivial LORegion implementation, where all the
>   * RW registers are RES0 (which we can implement as RAZ/WI). On an ARMv8.0
> @@ -2265,14 +2339,14 @@ static const struct sys_reg_desc sys_reg_descs[] = {
>         { SYS_DESC(SYS_AFSR1_EL1), access_vm_reg, reset_unknown, AFSR1_EL1 },
>         { SYS_DESC(SYS_ESR_EL1), access_vm_reg, reset_unknown, ESR_EL1 },
>
> -       { SYS_DESC(SYS_ERRIDR_EL1), trap_raz_wi },
> -       { SYS_DESC(SYS_ERRSELR_EL1), trap_raz_wi },
> -       { SYS_DESC(SYS_ERXFR_EL1), trap_raz_wi },
> -       { SYS_DESC(SYS_ERXCTLR_EL1), trap_raz_wi },
> -       { SYS_DESC(SYS_ERXSTATUS_EL1), trap_raz_wi },
> -       { SYS_DESC(SYS_ERXADDR_EL1), trap_raz_wi },
> -       { SYS_DESC(SYS_ERXMISC0_EL1), trap_raz_wi },
> -       { SYS_DESC(SYS_ERXMISC1_EL1), trap_raz_wi },
> +       { SYS_DESC(SYS_ERRIDR_EL1), trap_ras_regs },
> +       { SYS_DESC(SYS_ERRSELR_EL1), trap_ras_regs },
> +       { SYS_DESC(SYS_ERXFR_EL1), trap_ras_regs },
> +       { SYS_DESC(SYS_ERXCTLR_EL1), trap_ras_regs },
> +       { SYS_DESC(SYS_ERXSTATUS_EL1), trap_ras_regs },
> +       { SYS_DESC(SYS_ERXADDR_EL1), trap_ras_regs },
> +       { SYS_DESC(SYS_ERXMISC0_EL1), trap_ras_regs },
> +       { SYS_DESC(SYS_ERXMISC1_EL1), trap_ras_regs },
>
>         MTE_REG(TFSR_EL1),
>         MTE_REG(TFSRE0_EL1),
> --
> 2.34.1.448.ga2b2bfdf31-goog
>
> _______________________________________________
> kvmarm mailing list
> kvmarm@lists.cs.columbia.edu
> https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 20/26] KVM: arm64: Trap disabled features of ID_AA64PFR0_EL1
@ 2022-01-24 17:16     ` Fuad Tabba
  0 siblings, 0 replies; 201+ messages in thread
From: Fuad Tabba @ 2022-01-24 17:16 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: kvm, Marc Zyngier, Peter Shier, Paolo Bonzini, Will Deacon,
	kvmarm, linux-arm-kernel

Hi Reiji,

On Thu, Jan 6, 2022 at 4:29 AM Reiji Watanabe <reijiw@google.com> wrote:
>
> Add feature_config_ctrl for RAS and AMU, which are indicated in
> ID_AA64PFR0_EL1, to program configuration registers to trap
> guest's using those features when they are not exposed to the guest.
>
> Introduce trap_ras_regs() to change a behavior of guest's access to
> the registers, which is currently raz/wi, depending on the feature's
> availability for the guest (and inject undefined instruction
> exception when guest's RAS register access are trapped and RAS is
> not exposed to the guest).  In order to keep the current visibility
> of the RAS registers from userspace (always visible), a visibility
> function for RAS registers is not added.
>
> No code is added for AMU's access/visibility handler because the
> current code already injects the exception for Guest's AMU register
> access unconditionally because AMU is never exposed to the guest.

I think it might be code to trap it anyway, in case AMU guest support
is added in the future.

>
> Signed-off-by: Reiji Watanabe <reijiw@google.com>
> ---
>  arch/arm64/kvm/sys_regs.c | 90 +++++++++++++++++++++++++++++++++++----
>  1 file changed, 82 insertions(+), 8 deletions(-)
>
> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> index 33893a501475..015d67092d5e 100644
> --- a/arch/arm64/kvm/sys_regs.c
> +++ b/arch/arm64/kvm/sys_regs.c
> @@ -304,6 +304,63 @@ struct feature_config_ctrl {
>         void    (*trap_activate)(struct kvm_vcpu *vcpu);
>  };
>
> +enum vcpu_config_reg {
> +       VCPU_HCR_EL2 = 1,
> +       VCPU_MDCR_EL2,
> +       VCPU_CPTR_EL2,
> +};
> +
> +static void feature_trap_activate(struct kvm_vcpu *vcpu,
> +                                 enum vcpu_config_reg cfg_reg,
> +                                 u64 cfg_set, u64 cfg_clear)
> +{
> +       u64 *reg_ptr, reg_val;
> +
> +       switch (cfg_reg) {
> +       case VCPU_HCR_EL2:
> +               reg_ptr = &vcpu->arch.hcr_el2;
> +               break;
> +       case VCPU_MDCR_EL2:
> +               reg_ptr = &vcpu->arch.mdcr_el2;
> +               break;
> +       case VCPU_CPTR_EL2:
> +               reg_ptr = &vcpu->arch.cptr_el2;
> +               break;
> +       }
> +
> +       /* Clear/Set fields that are indicated by cfg_clear/cfg_set. */
> +       reg_val = (*reg_ptr & ~cfg_clear);
> +       reg_val |= cfg_set;
> +       *reg_ptr = reg_val;
> +}
> +
> +static void feature_ras_trap_activate(struct kvm_vcpu *vcpu)
> +{
> +       feature_trap_activate(vcpu, VCPU_HCR_EL2, HCR_TERR | HCR_TEA, HCR_FIEN);

Covers all the flags for ras.

> +}
> +
> +static void feature_amu_trap_activate(struct kvm_vcpu *vcpu)
> +{
> +       feature_trap_activate(vcpu, VCPU_CPTR_EL2, CPTR_EL2_TAM, 0);

Covers the CPTR flags for AMU, but as you mentioned, does not
explicitly clear HCR_AMVOFFEN.

Cheers,
/fuad


> +}
> +
> +/* For ID_AA64PFR0_EL1 */
> +static struct feature_config_ctrl ftr_ctrl_ras = {
> +       .ftr_reg = SYS_ID_AA64PFR0_EL1,
> +       .ftr_shift = ID_AA64PFR0_RAS_SHIFT,
> +       .ftr_min = ID_AA64PFR0_RAS_V1,
> +       .ftr_signed = FTR_UNSIGNED,
> +       .trap_activate = feature_ras_trap_activate,
> +};
> +
> +static struct feature_config_ctrl ftr_ctrl_amu = {
> +       .ftr_reg = SYS_ID_AA64PFR0_EL1,
> +       .ftr_shift = ID_AA64PFR0_AMU_SHIFT,
> +       .ftr_min = ID_AA64PFR0_AMU,
> +       .ftr_signed = FTR_UNSIGNED,
> +       .trap_activate = feature_amu_trap_activate,
> +};
> +
>  struct id_reg_info {
>         u32     sys_reg;        /* Register ID */
>         u64     sys_val;        /* Sanitized system value */
> @@ -778,6 +835,11 @@ static struct id_reg_info id_aa64pfr0_el1_info = {
>         .init = init_id_aa64pfr0_el1_info,
>         .validate = validate_id_aa64pfr0_el1,
>         .vcpu_mask = vcpu_mask_id_aa64pfr0_el1,
> +       .trap_features = &(const struct feature_config_ctrl *[]) {
> +               &ftr_ctrl_ras,
> +               &ftr_ctrl_amu,
> +               NULL,
> +       },
>  };
>
>  static struct id_reg_info id_aa64pfr1_el1_info = {
> @@ -901,6 +963,18 @@ static inline bool vcpu_feature_is_available(struct kvm_vcpu *vcpu,
>         return feature_avail(ctrl, val);
>  }
>
> +static bool trap_ras_regs(struct kvm_vcpu *vcpu,
> +                         struct sys_reg_params *p,
> +                         const struct sys_reg_desc *r)
> +{
> +       if (!vcpu_feature_is_available(vcpu, &ftr_ctrl_ras)) {
> +               kvm_inject_undefined(vcpu);
> +               return false;
> +       }
> +
> +       return trap_raz_wi(vcpu, p, r);
> +}
> +
>  /*
>   * ARMv8.1 mandates at least a trivial LORegion implementation, where all the
>   * RW registers are RES0 (which we can implement as RAZ/WI). On an ARMv8.0
> @@ -2265,14 +2339,14 @@ static const struct sys_reg_desc sys_reg_descs[] = {
>         { SYS_DESC(SYS_AFSR1_EL1), access_vm_reg, reset_unknown, AFSR1_EL1 },
>         { SYS_DESC(SYS_ESR_EL1), access_vm_reg, reset_unknown, ESR_EL1 },
>
> -       { SYS_DESC(SYS_ERRIDR_EL1), trap_raz_wi },
> -       { SYS_DESC(SYS_ERRSELR_EL1), trap_raz_wi },
> -       { SYS_DESC(SYS_ERXFR_EL1), trap_raz_wi },
> -       { SYS_DESC(SYS_ERXCTLR_EL1), trap_raz_wi },
> -       { SYS_DESC(SYS_ERXSTATUS_EL1), trap_raz_wi },
> -       { SYS_DESC(SYS_ERXADDR_EL1), trap_raz_wi },
> -       { SYS_DESC(SYS_ERXMISC0_EL1), trap_raz_wi },
> -       { SYS_DESC(SYS_ERXMISC1_EL1), trap_raz_wi },
> +       { SYS_DESC(SYS_ERRIDR_EL1), trap_ras_regs },
> +       { SYS_DESC(SYS_ERRSELR_EL1), trap_ras_regs },
> +       { SYS_DESC(SYS_ERXFR_EL1), trap_ras_regs },
> +       { SYS_DESC(SYS_ERXCTLR_EL1), trap_ras_regs },
> +       { SYS_DESC(SYS_ERXSTATUS_EL1), trap_ras_regs },
> +       { SYS_DESC(SYS_ERXADDR_EL1), trap_ras_regs },
> +       { SYS_DESC(SYS_ERXMISC0_EL1), trap_ras_regs },
> +       { SYS_DESC(SYS_ERXMISC1_EL1), trap_ras_regs },
>
>         MTE_REG(TFSR_EL1),
>         MTE_REG(TFSRE0_EL1),
> --
> 2.34.1.448.ga2b2bfdf31-goog
>
> _______________________________________________
> kvmarm mailing list
> kvmarm@lists.cs.columbia.edu
> https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 20/26] KVM: arm64: Trap disabled features of ID_AA64PFR0_EL1
@ 2022-01-24 17:16     ` Fuad Tabba
  0 siblings, 0 replies; 201+ messages in thread
From: Fuad Tabba @ 2022-01-24 17:16 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, linux-arm-kernel

Hi Reiji,

On Thu, Jan 6, 2022 at 4:29 AM Reiji Watanabe <reijiw@google.com> wrote:
>
> Add feature_config_ctrl for RAS and AMU, which are indicated in
> ID_AA64PFR0_EL1, to program configuration registers to trap
> guest's using those features when they are not exposed to the guest.
>
> Introduce trap_ras_regs() to change a behavior of guest's access to
> the registers, which is currently raz/wi, depending on the feature's
> availability for the guest (and inject undefined instruction
> exception when guest's RAS register access are trapped and RAS is
> not exposed to the guest).  In order to keep the current visibility
> of the RAS registers from userspace (always visible), a visibility
> function for RAS registers is not added.
>
> No code is added for AMU's access/visibility handler because the
> current code already injects the exception for Guest's AMU register
> access unconditionally because AMU is never exposed to the guest.

I think it might be code to trap it anyway, in case AMU guest support
is added in the future.

>
> Signed-off-by: Reiji Watanabe <reijiw@google.com>
> ---
>  arch/arm64/kvm/sys_regs.c | 90 +++++++++++++++++++++++++++++++++++----
>  1 file changed, 82 insertions(+), 8 deletions(-)
>
> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> index 33893a501475..015d67092d5e 100644
> --- a/arch/arm64/kvm/sys_regs.c
> +++ b/arch/arm64/kvm/sys_regs.c
> @@ -304,6 +304,63 @@ struct feature_config_ctrl {
>         void    (*trap_activate)(struct kvm_vcpu *vcpu);
>  };
>
> +enum vcpu_config_reg {
> +       VCPU_HCR_EL2 = 1,
> +       VCPU_MDCR_EL2,
> +       VCPU_CPTR_EL2,
> +};
> +
> +static void feature_trap_activate(struct kvm_vcpu *vcpu,
> +                                 enum vcpu_config_reg cfg_reg,
> +                                 u64 cfg_set, u64 cfg_clear)
> +{
> +       u64 *reg_ptr, reg_val;
> +
> +       switch (cfg_reg) {
> +       case VCPU_HCR_EL2:
> +               reg_ptr = &vcpu->arch.hcr_el2;
> +               break;
> +       case VCPU_MDCR_EL2:
> +               reg_ptr = &vcpu->arch.mdcr_el2;
> +               break;
> +       case VCPU_CPTR_EL2:
> +               reg_ptr = &vcpu->arch.cptr_el2;
> +               break;
> +       }
> +
> +       /* Clear/Set fields that are indicated by cfg_clear/cfg_set. */
> +       reg_val = (*reg_ptr & ~cfg_clear);
> +       reg_val |= cfg_set;
> +       *reg_ptr = reg_val;
> +}
> +
> +static void feature_ras_trap_activate(struct kvm_vcpu *vcpu)
> +{
> +       feature_trap_activate(vcpu, VCPU_HCR_EL2, HCR_TERR | HCR_TEA, HCR_FIEN);

Covers all the flags for ras.

> +}
> +
> +static void feature_amu_trap_activate(struct kvm_vcpu *vcpu)
> +{
> +       feature_trap_activate(vcpu, VCPU_CPTR_EL2, CPTR_EL2_TAM, 0);

Covers the CPTR flags for AMU, but as you mentioned, does not
explicitly clear HCR_AMVOFFEN.

Cheers,
/fuad


> +}
> +
> +/* For ID_AA64PFR0_EL1 */
> +static struct feature_config_ctrl ftr_ctrl_ras = {
> +       .ftr_reg = SYS_ID_AA64PFR0_EL1,
> +       .ftr_shift = ID_AA64PFR0_RAS_SHIFT,
> +       .ftr_min = ID_AA64PFR0_RAS_V1,
> +       .ftr_signed = FTR_UNSIGNED,
> +       .trap_activate = feature_ras_trap_activate,
> +};
> +
> +static struct feature_config_ctrl ftr_ctrl_amu = {
> +       .ftr_reg = SYS_ID_AA64PFR0_EL1,
> +       .ftr_shift = ID_AA64PFR0_AMU_SHIFT,
> +       .ftr_min = ID_AA64PFR0_AMU,
> +       .ftr_signed = FTR_UNSIGNED,
> +       .trap_activate = feature_amu_trap_activate,
> +};
> +
>  struct id_reg_info {
>         u32     sys_reg;        /* Register ID */
>         u64     sys_val;        /* Sanitized system value */
> @@ -778,6 +835,11 @@ static struct id_reg_info id_aa64pfr0_el1_info = {
>         .init = init_id_aa64pfr0_el1_info,
>         .validate = validate_id_aa64pfr0_el1,
>         .vcpu_mask = vcpu_mask_id_aa64pfr0_el1,
> +       .trap_features = &(const struct feature_config_ctrl *[]) {
> +               &ftr_ctrl_ras,
> +               &ftr_ctrl_amu,
> +               NULL,
> +       },
>  };
>
>  static struct id_reg_info id_aa64pfr1_el1_info = {
> @@ -901,6 +963,18 @@ static inline bool vcpu_feature_is_available(struct kvm_vcpu *vcpu,
>         return feature_avail(ctrl, val);
>  }
>
> +static bool trap_ras_regs(struct kvm_vcpu *vcpu,
> +                         struct sys_reg_params *p,
> +                         const struct sys_reg_desc *r)
> +{
> +       if (!vcpu_feature_is_available(vcpu, &ftr_ctrl_ras)) {
> +               kvm_inject_undefined(vcpu);
> +               return false;
> +       }
> +
> +       return trap_raz_wi(vcpu, p, r);
> +}
> +
>  /*
>   * ARMv8.1 mandates at least a trivial LORegion implementation, where all the
>   * RW registers are RES0 (which we can implement as RAZ/WI). On an ARMv8.0
> @@ -2265,14 +2339,14 @@ static const struct sys_reg_desc sys_reg_descs[] = {
>         { SYS_DESC(SYS_AFSR1_EL1), access_vm_reg, reset_unknown, AFSR1_EL1 },
>         { SYS_DESC(SYS_ESR_EL1), access_vm_reg, reset_unknown, ESR_EL1 },
>
> -       { SYS_DESC(SYS_ERRIDR_EL1), trap_raz_wi },
> -       { SYS_DESC(SYS_ERRSELR_EL1), trap_raz_wi },
> -       { SYS_DESC(SYS_ERXFR_EL1), trap_raz_wi },
> -       { SYS_DESC(SYS_ERXCTLR_EL1), trap_raz_wi },
> -       { SYS_DESC(SYS_ERXSTATUS_EL1), trap_raz_wi },
> -       { SYS_DESC(SYS_ERXADDR_EL1), trap_raz_wi },
> -       { SYS_DESC(SYS_ERXMISC0_EL1), trap_raz_wi },
> -       { SYS_DESC(SYS_ERXMISC1_EL1), trap_raz_wi },
> +       { SYS_DESC(SYS_ERRIDR_EL1), trap_ras_regs },
> +       { SYS_DESC(SYS_ERRSELR_EL1), trap_ras_regs },
> +       { SYS_DESC(SYS_ERXFR_EL1), trap_ras_regs },
> +       { SYS_DESC(SYS_ERXCTLR_EL1), trap_ras_regs },
> +       { SYS_DESC(SYS_ERXSTATUS_EL1), trap_ras_regs },
> +       { SYS_DESC(SYS_ERXADDR_EL1), trap_ras_regs },
> +       { SYS_DESC(SYS_ERXMISC0_EL1), trap_ras_regs },
> +       { SYS_DESC(SYS_ERXMISC1_EL1), trap_ras_regs },
>
>         MTE_REG(TFSR_EL1),
>         MTE_REG(TFSRE0_EL1),
> --
> 2.34.1.448.ga2b2bfdf31-goog
>
> _______________________________________________
> kvmarm mailing list
> kvmarm@lists.cs.columbia.edu
> https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 22/26] KVM: arm64: Trap disabled features of ID_AA64DFR0_EL1
  2022-01-06  4:27   ` Reiji Watanabe
  (?)
@ 2022-01-24 17:19     ` Fuad Tabba
  -1 siblings, 0 replies; 201+ messages in thread
From: Fuad Tabba @ 2022-01-24 17:19 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, linux-arm-kernel

.Hi Reiji,

On Thu, Jan 6, 2022 at 4:29 AM Reiji Watanabe <reijiw@google.com> wrote:
>
> Add feature_config_ctrl for PMUv3, PMS and TraceFilt, which are
> indicated in ID_AA64DFR0_EL1, to program configuration registers
> to trap guest's using those features when they are not exposed to
> the guest.
>
> Signed-off-by: Reiji Watanabe <reijiw@google.com>
> ---
>  arch/arm64/kvm/sys_regs.c | 47 +++++++++++++++++++++++++++++++++++++++
>  1 file changed, 47 insertions(+)
>
> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> index 72e745c5a9c2..229671ec3abd 100644
> --- a/arch/arm64/kvm/sys_regs.c
> +++ b/arch/arm64/kvm/sys_regs.c
> @@ -349,6 +349,22 @@ static void feature_mte_trap_activate(struct kvm_vcpu *vcpu)
>         feature_trap_activate(vcpu, VCPU_HCR_EL2, HCR_TID5, HCR_DCT | HCR_ATA);
>  }
>
> +static void feature_pmuv3_trap_activate(struct kvm_vcpu *vcpu)
> +{
> +       feature_trap_activate(vcpu, VCPU_MDCR_EL2, MDCR_EL2_TPM, 0);

I think that for full coverage it might be good to include setting
MDCR_EL2_TPMCR, and clearing MDCR_EL2_HPME | MDCR_EL2_MTPME |
MDCR_EL2_HPMN_MASK, even if redundant at this point.

> +}
> +
> +static void feature_pms_trap_activate(struct kvm_vcpu *vcpu)
> +{
> +       feature_trap_activate(vcpu, VCPU_MDCR_EL2, MDCR_EL2_TPMS,
> +                             MDCR_EL2_E2PB_MASK << MDCR_EL2_E2PB_SHIFT);
> +}
> +
> +static void feature_tracefilt_trap_activate(struct kvm_vcpu *vcpu)
> +{
> +       feature_trap_activate(vcpu, VCPU_MDCR_EL2, MDCR_EL2_TTRF, 0);
> +}
> +
>  /* For ID_AA64PFR0_EL1 */
>  static struct feature_config_ctrl ftr_ctrl_ras = {
>         .ftr_reg = SYS_ID_AA64PFR0_EL1,
> @@ -375,6 +391,31 @@ static struct feature_config_ctrl ftr_ctrl_mte = {
>         .trap_activate = feature_mte_trap_activate,
>  };
>
> +/* For ID_AA64DFR0_EL1 */
> +static struct feature_config_ctrl ftr_ctrl_pmuv3 = {
> +       .ftr_reg = SYS_ID_AA64DFR0_EL1,
> +       .ftr_shift = ID_AA64DFR0_PMUVER_SHIFT,
> +       .ftr_min = ID_AA64DFR0_PMUVER_8_0,
> +       .ftr_signed = FTR_UNSIGNED,
> +       .trap_activate = feature_pmuv3_trap_activate,
> +};
> +
> +static struct feature_config_ctrl ftr_ctrl_pms = {
> +       .ftr_reg = SYS_ID_AA64DFR0_EL1,
> +       .ftr_shift = ID_AA64DFR0_PMSVER_SHIFT,
> +       .ftr_min = ID_AA64DFR0_PMSVER_8_2,
> +       .ftr_signed = FTR_UNSIGNED,
> +       .trap_activate = feature_pms_trap_activate,
> +};
> +
> +static struct feature_config_ctrl ftr_ctrl_tracefilt = {
> +       .ftr_reg = SYS_ID_AA64DFR0_EL1,
> +       .ftr_shift = ID_AA64DFR0_TRACE_FILT_SHIFT,
> +       .ftr_min = 1,
> +       .ftr_signed = FTR_UNSIGNED,
> +       .trap_activate = feature_tracefilt_trap_activate,
> +};

I think you might be missing trace, ID_AA64DFR0_TRACEVER -> CPTR_EL2_TTA.

Cheers,
/fuad


> +
>  struct id_reg_info {
>         u32     sys_reg;        /* Register ID */
>         u64     sys_val;        /* Sanitized system value */
> @@ -898,6 +939,12 @@ static struct id_reg_info id_aa64dfr0_el1_info = {
>         .init = init_id_aa64dfr0_el1_info,
>         .validate = validate_id_aa64dfr0_el1,
>         .vcpu_mask = vcpu_mask_id_aa64dfr0_el1,
> +       .trap_features = &(const struct feature_config_ctrl *[]) {
> +               &ftr_ctrl_pmuv3,
> +               &ftr_ctrl_pms,
> +               &ftr_ctrl_tracefilt,
> +               NULL,
> +       },
>  };
>
>  static struct id_reg_info id_dfr0_el1_info = {
> --
> 2.34.1.448.ga2b2bfdf31-goog
>
> _______________________________________________
> kvmarm mailing list
> kvmarm@lists.cs.columbia.edu
> https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 22/26] KVM: arm64: Trap disabled features of ID_AA64DFR0_EL1
@ 2022-01-24 17:19     ` Fuad Tabba
  0 siblings, 0 replies; 201+ messages in thread
From: Fuad Tabba @ 2022-01-24 17:19 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: kvm, Marc Zyngier, Peter Shier, Paolo Bonzini, Will Deacon,
	kvmarm, linux-arm-kernel

.Hi Reiji,

On Thu, Jan 6, 2022 at 4:29 AM Reiji Watanabe <reijiw@google.com> wrote:
>
> Add feature_config_ctrl for PMUv3, PMS and TraceFilt, which are
> indicated in ID_AA64DFR0_EL1, to program configuration registers
> to trap guest's using those features when they are not exposed to
> the guest.
>
> Signed-off-by: Reiji Watanabe <reijiw@google.com>
> ---
>  arch/arm64/kvm/sys_regs.c | 47 +++++++++++++++++++++++++++++++++++++++
>  1 file changed, 47 insertions(+)
>
> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> index 72e745c5a9c2..229671ec3abd 100644
> --- a/arch/arm64/kvm/sys_regs.c
> +++ b/arch/arm64/kvm/sys_regs.c
> @@ -349,6 +349,22 @@ static void feature_mte_trap_activate(struct kvm_vcpu *vcpu)
>         feature_trap_activate(vcpu, VCPU_HCR_EL2, HCR_TID5, HCR_DCT | HCR_ATA);
>  }
>
> +static void feature_pmuv3_trap_activate(struct kvm_vcpu *vcpu)
> +{
> +       feature_trap_activate(vcpu, VCPU_MDCR_EL2, MDCR_EL2_TPM, 0);

I think that for full coverage it might be good to include setting
MDCR_EL2_TPMCR, and clearing MDCR_EL2_HPME | MDCR_EL2_MTPME |
MDCR_EL2_HPMN_MASK, even if redundant at this point.

> +}
> +
> +static void feature_pms_trap_activate(struct kvm_vcpu *vcpu)
> +{
> +       feature_trap_activate(vcpu, VCPU_MDCR_EL2, MDCR_EL2_TPMS,
> +                             MDCR_EL2_E2PB_MASK << MDCR_EL2_E2PB_SHIFT);
> +}
> +
> +static void feature_tracefilt_trap_activate(struct kvm_vcpu *vcpu)
> +{
> +       feature_trap_activate(vcpu, VCPU_MDCR_EL2, MDCR_EL2_TTRF, 0);
> +}
> +
>  /* For ID_AA64PFR0_EL1 */
>  static struct feature_config_ctrl ftr_ctrl_ras = {
>         .ftr_reg = SYS_ID_AA64PFR0_EL1,
> @@ -375,6 +391,31 @@ static struct feature_config_ctrl ftr_ctrl_mte = {
>         .trap_activate = feature_mte_trap_activate,
>  };
>
> +/* For ID_AA64DFR0_EL1 */
> +static struct feature_config_ctrl ftr_ctrl_pmuv3 = {
> +       .ftr_reg = SYS_ID_AA64DFR0_EL1,
> +       .ftr_shift = ID_AA64DFR0_PMUVER_SHIFT,
> +       .ftr_min = ID_AA64DFR0_PMUVER_8_0,
> +       .ftr_signed = FTR_UNSIGNED,
> +       .trap_activate = feature_pmuv3_trap_activate,
> +};
> +
> +static struct feature_config_ctrl ftr_ctrl_pms = {
> +       .ftr_reg = SYS_ID_AA64DFR0_EL1,
> +       .ftr_shift = ID_AA64DFR0_PMSVER_SHIFT,
> +       .ftr_min = ID_AA64DFR0_PMSVER_8_2,
> +       .ftr_signed = FTR_UNSIGNED,
> +       .trap_activate = feature_pms_trap_activate,
> +};
> +
> +static struct feature_config_ctrl ftr_ctrl_tracefilt = {
> +       .ftr_reg = SYS_ID_AA64DFR0_EL1,
> +       .ftr_shift = ID_AA64DFR0_TRACE_FILT_SHIFT,
> +       .ftr_min = 1,
> +       .ftr_signed = FTR_UNSIGNED,
> +       .trap_activate = feature_tracefilt_trap_activate,
> +};

I think you might be missing trace, ID_AA64DFR0_TRACEVER -> CPTR_EL2_TTA.

Cheers,
/fuad


> +
>  struct id_reg_info {
>         u32     sys_reg;        /* Register ID */
>         u64     sys_val;        /* Sanitized system value */
> @@ -898,6 +939,12 @@ static struct id_reg_info id_aa64dfr0_el1_info = {
>         .init = init_id_aa64dfr0_el1_info,
>         .validate = validate_id_aa64dfr0_el1,
>         .vcpu_mask = vcpu_mask_id_aa64dfr0_el1,
> +       .trap_features = &(const struct feature_config_ctrl *[]) {
> +               &ftr_ctrl_pmuv3,
> +               &ftr_ctrl_pms,
> +               &ftr_ctrl_tracefilt,
> +               NULL,
> +       },
>  };
>
>  static struct id_reg_info id_dfr0_el1_info = {
> --
> 2.34.1.448.ga2b2bfdf31-goog
>
> _______________________________________________
> kvmarm mailing list
> kvmarm@lists.cs.columbia.edu
> https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 22/26] KVM: arm64: Trap disabled features of ID_AA64DFR0_EL1
@ 2022-01-24 17:19     ` Fuad Tabba
  0 siblings, 0 replies; 201+ messages in thread
From: Fuad Tabba @ 2022-01-24 17:19 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, linux-arm-kernel

.Hi Reiji,

On Thu, Jan 6, 2022 at 4:29 AM Reiji Watanabe <reijiw@google.com> wrote:
>
> Add feature_config_ctrl for PMUv3, PMS and TraceFilt, which are
> indicated in ID_AA64DFR0_EL1, to program configuration registers
> to trap guest's using those features when they are not exposed to
> the guest.
>
> Signed-off-by: Reiji Watanabe <reijiw@google.com>
> ---
>  arch/arm64/kvm/sys_regs.c | 47 +++++++++++++++++++++++++++++++++++++++
>  1 file changed, 47 insertions(+)
>
> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> index 72e745c5a9c2..229671ec3abd 100644
> --- a/arch/arm64/kvm/sys_regs.c
> +++ b/arch/arm64/kvm/sys_regs.c
> @@ -349,6 +349,22 @@ static void feature_mte_trap_activate(struct kvm_vcpu *vcpu)
>         feature_trap_activate(vcpu, VCPU_HCR_EL2, HCR_TID5, HCR_DCT | HCR_ATA);
>  }
>
> +static void feature_pmuv3_trap_activate(struct kvm_vcpu *vcpu)
> +{
> +       feature_trap_activate(vcpu, VCPU_MDCR_EL2, MDCR_EL2_TPM, 0);

I think that for full coverage it might be good to include setting
MDCR_EL2_TPMCR, and clearing MDCR_EL2_HPME | MDCR_EL2_MTPME |
MDCR_EL2_HPMN_MASK, even if redundant at this point.

> +}
> +
> +static void feature_pms_trap_activate(struct kvm_vcpu *vcpu)
> +{
> +       feature_trap_activate(vcpu, VCPU_MDCR_EL2, MDCR_EL2_TPMS,
> +                             MDCR_EL2_E2PB_MASK << MDCR_EL2_E2PB_SHIFT);
> +}
> +
> +static void feature_tracefilt_trap_activate(struct kvm_vcpu *vcpu)
> +{
> +       feature_trap_activate(vcpu, VCPU_MDCR_EL2, MDCR_EL2_TTRF, 0);
> +}
> +
>  /* For ID_AA64PFR0_EL1 */
>  static struct feature_config_ctrl ftr_ctrl_ras = {
>         .ftr_reg = SYS_ID_AA64PFR0_EL1,
> @@ -375,6 +391,31 @@ static struct feature_config_ctrl ftr_ctrl_mte = {
>         .trap_activate = feature_mte_trap_activate,
>  };
>
> +/* For ID_AA64DFR0_EL1 */
> +static struct feature_config_ctrl ftr_ctrl_pmuv3 = {
> +       .ftr_reg = SYS_ID_AA64DFR0_EL1,
> +       .ftr_shift = ID_AA64DFR0_PMUVER_SHIFT,
> +       .ftr_min = ID_AA64DFR0_PMUVER_8_0,
> +       .ftr_signed = FTR_UNSIGNED,
> +       .trap_activate = feature_pmuv3_trap_activate,
> +};
> +
> +static struct feature_config_ctrl ftr_ctrl_pms = {
> +       .ftr_reg = SYS_ID_AA64DFR0_EL1,
> +       .ftr_shift = ID_AA64DFR0_PMSVER_SHIFT,
> +       .ftr_min = ID_AA64DFR0_PMSVER_8_2,
> +       .ftr_signed = FTR_UNSIGNED,
> +       .trap_activate = feature_pms_trap_activate,
> +};
> +
> +static struct feature_config_ctrl ftr_ctrl_tracefilt = {
> +       .ftr_reg = SYS_ID_AA64DFR0_EL1,
> +       .ftr_shift = ID_AA64DFR0_TRACE_FILT_SHIFT,
> +       .ftr_min = 1,
> +       .ftr_signed = FTR_UNSIGNED,
> +       .trap_activate = feature_tracefilt_trap_activate,
> +};

I think you might be missing trace, ID_AA64DFR0_TRACEVER -> CPTR_EL2_TTA.

Cheers,
/fuad


> +
>  struct id_reg_info {
>         u32     sys_reg;        /* Register ID */
>         u64     sys_val;        /* Sanitized system value */
> @@ -898,6 +939,12 @@ static struct id_reg_info id_aa64dfr0_el1_info = {
>         .init = init_id_aa64dfr0_el1_info,
>         .validate = validate_id_aa64dfr0_el1,
>         .vcpu_mask = vcpu_mask_id_aa64dfr0_el1,
> +       .trap_features = &(const struct feature_config_ctrl *[]) {
> +               &ftr_ctrl_pmuv3,
> +               &ftr_ctrl_pms,
> +               &ftr_ctrl_tracefilt,
> +               NULL,
> +       },
>  };
>
>  static struct id_reg_info id_dfr0_el1_info = {
> --
> 2.34.1.448.ga2b2bfdf31-goog
>
> _______________________________________________
> kvmarm mailing list
> kvmarm@lists.cs.columbia.edu
> https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 23/26] KVM: arm64: Trap disabled features of ID_AA64MMFR1_EL1
  2022-01-06  4:27   ` Reiji Watanabe
  (?)
@ 2022-01-24 17:37     ` Fuad Tabba
  -1 siblings, 0 replies; 201+ messages in thread
From: Fuad Tabba @ 2022-01-24 17:37 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, linux-arm-kernel

Hi Reiji,

The series might be missing an entry for ID_AA64MMFR0_EL1, Debug
Communications Channel registers, ID_AA64MMFR0_FGT -> MDCR_EL2_TDCC.

Cheers,
/fuad


On Thu, Jan 6, 2022 at 4:29 AM Reiji Watanabe <reijiw@google.com> wrote:
>
> Add feature_config_ctrl for LORegions, which is indicated in
> ID_AA64MMFR1_EL1, to program configuration register to trap
> guest's using the feature when it is not exposed to the guest.
>
> Change trap_loregion() to use vcpu_feature_is_available()
> to simplify checking of the feature's availability.
>
> Signed-off-by: Reiji Watanabe <reijiw@google.com>
> ---
>  arch/arm64/kvm/sys_regs.c | 26 ++++++++++++++++++++++++--
>  1 file changed, 24 insertions(+), 2 deletions(-)
>
> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> index 229671ec3abd..f8a5ee927ecf 100644
> --- a/arch/arm64/kvm/sys_regs.c
> +++ b/arch/arm64/kvm/sys_regs.c
> @@ -365,6 +365,11 @@ static void feature_tracefilt_trap_activate(struct kvm_vcpu *vcpu)
>         feature_trap_activate(vcpu, VCPU_MDCR_EL2, MDCR_EL2_TTRF, 0);
>  }
>
> +static void feature_lor_trap_activate(struct kvm_vcpu *vcpu)
> +{
> +       feature_trap_activate(vcpu, VCPU_HCR_EL2, HCR_TLOR, 0);
> +}
> +
>  /* For ID_AA64PFR0_EL1 */
>  static struct feature_config_ctrl ftr_ctrl_ras = {
>         .ftr_reg = SYS_ID_AA64PFR0_EL1,
> @@ -416,6 +421,15 @@ static struct feature_config_ctrl ftr_ctrl_tracefilt = {
>         .trap_activate = feature_tracefilt_trap_activate,
>  };
>
> +/* For ID_AA64MMFR1_EL1 */
> +static struct feature_config_ctrl ftr_ctrl_lor = {
> +       .ftr_reg = SYS_ID_AA64MMFR1_EL1,
> +       .ftr_shift = ID_AA64MMFR1_LOR_SHIFT,
> +       .ftr_min = 1,
> +       .ftr_signed = FTR_UNSIGNED,
> +       .trap_activate = feature_lor_trap_activate,
> +};
> +
>  struct id_reg_info {
>         u32     sys_reg;        /* Register ID */
>         u64     sys_val;        /* Sanitized system value */
> @@ -947,6 +961,14 @@ static struct id_reg_info id_aa64dfr0_el1_info = {
>         },
>  };
>
> +static struct id_reg_info id_aa64mmfr1_el1_info = {
> +       .sys_reg = SYS_ID_AA64MMFR1_EL1,
> +       .trap_features = &(const struct feature_config_ctrl *[]) {
> +               &ftr_ctrl_lor,
> +               NULL,
> +       },
> +};
> +
>  static struct id_reg_info id_dfr0_el1_info = {
>         .sys_reg = SYS_ID_DFR0_EL1,
>         .init = init_id_dfr0_el1_info,
> @@ -976,6 +998,7 @@ static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {
>         [IDREG_IDX(SYS_ID_AA64ISAR0_EL1)] = &id_aa64isar0_el1_info,
>         [IDREG_IDX(SYS_ID_AA64ISAR1_EL1)] = &id_aa64isar1_el1_info,
>         [IDREG_IDX(SYS_ID_AA64MMFR0_EL1)] = &id_aa64mmfr0_el1_info,
> +       [IDREG_IDX(SYS_ID_AA64MMFR1_EL1)] = &id_aa64mmfr1_el1_info,
>  };
>
>  static int validate_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
> @@ -1050,10 +1073,9 @@ static bool trap_loregion(struct kvm_vcpu *vcpu,
>                           struct sys_reg_params *p,
>                           const struct sys_reg_desc *r)
>  {
> -       u64 val = __read_id_reg(vcpu, SYS_ID_AA64MMFR1_EL1);
>         u32 sr = reg_to_encoding(r);
>
> -       if (!(val & (0xfUL << ID_AA64MMFR1_LOR_SHIFT))) {
> +       if (!vcpu_feature_is_available(vcpu, &ftr_ctrl_lor)) {
>                 kvm_inject_undefined(vcpu);
>                 return false;
>         }
> --
> 2.34.1.448.ga2b2bfdf31-goog
>
> _______________________________________________
> kvmarm mailing list
> kvmarm@lists.cs.columbia.edu
> https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 23/26] KVM: arm64: Trap disabled features of ID_AA64MMFR1_EL1
@ 2022-01-24 17:37     ` Fuad Tabba
  0 siblings, 0 replies; 201+ messages in thread
From: Fuad Tabba @ 2022-01-24 17:37 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: kvm, Marc Zyngier, Peter Shier, Paolo Bonzini, Will Deacon,
	kvmarm, linux-arm-kernel

Hi Reiji,

The series might be missing an entry for ID_AA64MMFR0_EL1, Debug
Communications Channel registers, ID_AA64MMFR0_FGT -> MDCR_EL2_TDCC.

Cheers,
/fuad


On Thu, Jan 6, 2022 at 4:29 AM Reiji Watanabe <reijiw@google.com> wrote:
>
> Add feature_config_ctrl for LORegions, which is indicated in
> ID_AA64MMFR1_EL1, to program configuration register to trap
> guest's using the feature when it is not exposed to the guest.
>
> Change trap_loregion() to use vcpu_feature_is_available()
> to simplify checking of the feature's availability.
>
> Signed-off-by: Reiji Watanabe <reijiw@google.com>
> ---
>  arch/arm64/kvm/sys_regs.c | 26 ++++++++++++++++++++++++--
>  1 file changed, 24 insertions(+), 2 deletions(-)
>
> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> index 229671ec3abd..f8a5ee927ecf 100644
> --- a/arch/arm64/kvm/sys_regs.c
> +++ b/arch/arm64/kvm/sys_regs.c
> @@ -365,6 +365,11 @@ static void feature_tracefilt_trap_activate(struct kvm_vcpu *vcpu)
>         feature_trap_activate(vcpu, VCPU_MDCR_EL2, MDCR_EL2_TTRF, 0);
>  }
>
> +static void feature_lor_trap_activate(struct kvm_vcpu *vcpu)
> +{
> +       feature_trap_activate(vcpu, VCPU_HCR_EL2, HCR_TLOR, 0);
> +}
> +
>  /* For ID_AA64PFR0_EL1 */
>  static struct feature_config_ctrl ftr_ctrl_ras = {
>         .ftr_reg = SYS_ID_AA64PFR0_EL1,
> @@ -416,6 +421,15 @@ static struct feature_config_ctrl ftr_ctrl_tracefilt = {
>         .trap_activate = feature_tracefilt_trap_activate,
>  };
>
> +/* For ID_AA64MMFR1_EL1 */
> +static struct feature_config_ctrl ftr_ctrl_lor = {
> +       .ftr_reg = SYS_ID_AA64MMFR1_EL1,
> +       .ftr_shift = ID_AA64MMFR1_LOR_SHIFT,
> +       .ftr_min = 1,
> +       .ftr_signed = FTR_UNSIGNED,
> +       .trap_activate = feature_lor_trap_activate,
> +};
> +
>  struct id_reg_info {
>         u32     sys_reg;        /* Register ID */
>         u64     sys_val;        /* Sanitized system value */
> @@ -947,6 +961,14 @@ static struct id_reg_info id_aa64dfr0_el1_info = {
>         },
>  };
>
> +static struct id_reg_info id_aa64mmfr1_el1_info = {
> +       .sys_reg = SYS_ID_AA64MMFR1_EL1,
> +       .trap_features = &(const struct feature_config_ctrl *[]) {
> +               &ftr_ctrl_lor,
> +               NULL,
> +       },
> +};
> +
>  static struct id_reg_info id_dfr0_el1_info = {
>         .sys_reg = SYS_ID_DFR0_EL1,
>         .init = init_id_dfr0_el1_info,
> @@ -976,6 +998,7 @@ static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {
>         [IDREG_IDX(SYS_ID_AA64ISAR0_EL1)] = &id_aa64isar0_el1_info,
>         [IDREG_IDX(SYS_ID_AA64ISAR1_EL1)] = &id_aa64isar1_el1_info,
>         [IDREG_IDX(SYS_ID_AA64MMFR0_EL1)] = &id_aa64mmfr0_el1_info,
> +       [IDREG_IDX(SYS_ID_AA64MMFR1_EL1)] = &id_aa64mmfr1_el1_info,
>  };
>
>  static int validate_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
> @@ -1050,10 +1073,9 @@ static bool trap_loregion(struct kvm_vcpu *vcpu,
>                           struct sys_reg_params *p,
>                           const struct sys_reg_desc *r)
>  {
> -       u64 val = __read_id_reg(vcpu, SYS_ID_AA64MMFR1_EL1);
>         u32 sr = reg_to_encoding(r);
>
> -       if (!(val & (0xfUL << ID_AA64MMFR1_LOR_SHIFT))) {
> +       if (!vcpu_feature_is_available(vcpu, &ftr_ctrl_lor)) {
>                 kvm_inject_undefined(vcpu);
>                 return false;
>         }
> --
> 2.34.1.448.ga2b2bfdf31-goog
>
> _______________________________________________
> kvmarm mailing list
> kvmarm@lists.cs.columbia.edu
> https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 23/26] KVM: arm64: Trap disabled features of ID_AA64MMFR1_EL1
@ 2022-01-24 17:37     ` Fuad Tabba
  0 siblings, 0 replies; 201+ messages in thread
From: Fuad Tabba @ 2022-01-24 17:37 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, linux-arm-kernel

Hi Reiji,

The series might be missing an entry for ID_AA64MMFR0_EL1, Debug
Communications Channel registers, ID_AA64MMFR0_FGT -> MDCR_EL2_TDCC.

Cheers,
/fuad


On Thu, Jan 6, 2022 at 4:29 AM Reiji Watanabe <reijiw@google.com> wrote:
>
> Add feature_config_ctrl for LORegions, which is indicated in
> ID_AA64MMFR1_EL1, to program configuration register to trap
> guest's using the feature when it is not exposed to the guest.
>
> Change trap_loregion() to use vcpu_feature_is_available()
> to simplify checking of the feature's availability.
>
> Signed-off-by: Reiji Watanabe <reijiw@google.com>
> ---
>  arch/arm64/kvm/sys_regs.c | 26 ++++++++++++++++++++++++--
>  1 file changed, 24 insertions(+), 2 deletions(-)
>
> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> index 229671ec3abd..f8a5ee927ecf 100644
> --- a/arch/arm64/kvm/sys_regs.c
> +++ b/arch/arm64/kvm/sys_regs.c
> @@ -365,6 +365,11 @@ static void feature_tracefilt_trap_activate(struct kvm_vcpu *vcpu)
>         feature_trap_activate(vcpu, VCPU_MDCR_EL2, MDCR_EL2_TTRF, 0);
>  }
>
> +static void feature_lor_trap_activate(struct kvm_vcpu *vcpu)
> +{
> +       feature_trap_activate(vcpu, VCPU_HCR_EL2, HCR_TLOR, 0);
> +}
> +
>  /* For ID_AA64PFR0_EL1 */
>  static struct feature_config_ctrl ftr_ctrl_ras = {
>         .ftr_reg = SYS_ID_AA64PFR0_EL1,
> @@ -416,6 +421,15 @@ static struct feature_config_ctrl ftr_ctrl_tracefilt = {
>         .trap_activate = feature_tracefilt_trap_activate,
>  };
>
> +/* For ID_AA64MMFR1_EL1 */
> +static struct feature_config_ctrl ftr_ctrl_lor = {
> +       .ftr_reg = SYS_ID_AA64MMFR1_EL1,
> +       .ftr_shift = ID_AA64MMFR1_LOR_SHIFT,
> +       .ftr_min = 1,
> +       .ftr_signed = FTR_UNSIGNED,
> +       .trap_activate = feature_lor_trap_activate,
> +};
> +
>  struct id_reg_info {
>         u32     sys_reg;        /* Register ID */
>         u64     sys_val;        /* Sanitized system value */
> @@ -947,6 +961,14 @@ static struct id_reg_info id_aa64dfr0_el1_info = {
>         },
>  };
>
> +static struct id_reg_info id_aa64mmfr1_el1_info = {
> +       .sys_reg = SYS_ID_AA64MMFR1_EL1,
> +       .trap_features = &(const struct feature_config_ctrl *[]) {
> +               &ftr_ctrl_lor,
> +               NULL,
> +       },
> +};
> +
>  static struct id_reg_info id_dfr0_el1_info = {
>         .sys_reg = SYS_ID_DFR0_EL1,
>         .init = init_id_dfr0_el1_info,
> @@ -976,6 +998,7 @@ static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {
>         [IDREG_IDX(SYS_ID_AA64ISAR0_EL1)] = &id_aa64isar0_el1_info,
>         [IDREG_IDX(SYS_ID_AA64ISAR1_EL1)] = &id_aa64isar1_el1_info,
>         [IDREG_IDX(SYS_ID_AA64MMFR0_EL1)] = &id_aa64mmfr0_el1_info,
> +       [IDREG_IDX(SYS_ID_AA64MMFR1_EL1)] = &id_aa64mmfr1_el1_info,
>  };
>
>  static int validate_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
> @@ -1050,10 +1073,9 @@ static bool trap_loregion(struct kvm_vcpu *vcpu,
>                           struct sys_reg_params *p,
>                           const struct sys_reg_desc *r)
>  {
> -       u64 val = __read_id_reg(vcpu, SYS_ID_AA64MMFR1_EL1);
>         u32 sr = reg_to_encoding(r);
>
> -       if (!(val & (0xfUL << ID_AA64MMFR1_LOR_SHIFT))) {
> +       if (!vcpu_feature_is_available(vcpu, &ftr_ctrl_lor)) {
>                 kvm_inject_undefined(vcpu);
>                 return false;
>         }
> --
> 2.34.1.448.ga2b2bfdf31-goog
>
> _______________________________________________
> kvmarm mailing list
> kvmarm@lists.cs.columbia.edu
> https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 00/26] KVM: arm64: Make CPU ID registers writable by userspace
  2022-01-24 16:18   ` Fuad Tabba
  (?)
@ 2022-01-25  6:31     ` Reiji Watanabe
  -1 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-25  6:31 UTC (permalink / raw)
  To: Fuad Tabba
  Cc: kvm, Marc Zyngier, Peter Shier, Paolo Bonzini, Will Deacon,
	kvmarm, Linux ARM

Hi Fuad,

On Mon, Jan 24, 2022 at 8:19 AM Fuad Tabba <tabba@google.com> wrote:
>
> Hi Reiji,
>
> On Thu, Jan 6, 2022 at 4:27 AM Reiji Watanabe <reijiw@google.com> wrote:
>
> > The patch series is for both VHE and non-VHE, except for protected VMs,
> > which have a different way of configuring ID registers based on its
> > different requirements [1].
>
> I am reviewing this patch series and I really like your approach. I
> think that once it's through we might be able to generalize it for
> protected VMs as well (I did some of the work for the ID registers for
> those).

Thank you for the review and comments for the series.
I will be looking at your comments.

> > The series is based on v5.16-rc8 with the patch [3] applied.
>
> I tried to apply this series on v5.16-rc8 after applying
> 20220104194918.373612-2-rananta@google.com, and it didn't apply
> cleanly. If you could point me to a working branch that I could
> checkout I would like to test it and experiment with it a bit more.

I'm sorry for the inconvenience.  I'm not sure why though...
I tried that again (applied the series on v5.16-rc8 after applying
20220104194918.373612-2-rananta@google.com), and it worked fine.

Anyway, you can find the branch at:
 https://github.com/reijiw-kvm/kvmarm-idreg/tree/id-regs-v4-5.16-rc8

Thanks,
Reiji


>
>
>
>
> > v4:
> >   - Make ID registers storage per VM instead of per vCPU. [Marc]
> >   - Implement arm64_check_features() in arch/arm64/kernel/cpufeature.c
> >     by using existing codes in the file. [Marc]
> >   - Use a configuration function to enable traps for disabled
> >     features. [Marc]
> >   - Document ID registers become immutable after the first KVM_RUN [Eric]
> >   - Update ID_AA64PFR0.GIC at the point where a GICv3 is created. [Marc]
> >   - Get TGranX's bit position by substracting 12 from TGranX_2's bit
> >     position. [Eric]
> >   - Don't validate AArch32 ID registers when the system doesn't support
> >     32bit EL0. [Eric]
> >   - Add/fixes comments for patches. [Eric]
> >   - Made bug fixes/improvements of the selftest. [Eric]
> >   - Added .kunitconfig for arm64 KUnit tests
> >
> > v3: https://lore.kernel.org/all/20211117064359.2362060-1-reijiw@google.com/
> >   - Remove ID register consistency checking across vCPUs. [Oliver]
> >   - Change KVM_CAP_ARM_ID_REG_WRITABLE to
> >     KVM_CAP_ARM_ID_REG_CONFIGURABLE. [Oliver]
> >   - Add KUnit testing for ID register validation and trap initialization.
> >   - Change read_id_reg() to take care of ID_AA64PFR0_EL1.GIC.
> >   - Add a helper of read_id_reg() (__read_id_reg()) and use the helper
> >     instead of directly using __vcpu_sys_reg().
> >   - Change not to run kvm_id_regs_consistency_check() and
> >     kvm_vcpu_init_traps() for protected VMs.
> >   - Update selftest to remove test cases for ID register consistency.
> >     checking across vCPUs and to add test cases for ID_AA64PFR0_EL1.GIC.
> >
> > v2: https://lore.kernel.org/all/20211103062520.1445832-1-reijiw@google.com/
> >   - Remove unnecessary line breaks. [Andrew]
> >   - Use @params for comments. [Andrew]
> >   - Move arm64_check_features to arch/arm64/kvm/sys_regs.c and
> >     change that KVM specific feature check function.  [Andrew]
> >   - Remove unnecessary raz handling from __set_id_reg. [Andrew]
> >   - Remove sys_val field from the initial id_reg_info and add it
> >     in the later patch. [Andrew]
> >   - Call id_reg->init() from id_reg_info_init(). [Andrew]
> >   - Fix cpuid_feature_cap_perfmon_field() to convert 0xf to 0x0
> >     (and use it in the following patches).
> >   - Change kvm_vcpu_first_run_init to set has_run_once to false
> >     when kvm_id_regs_consistency_check() fails.
> >   - Add a patch to introduce id_reg_info for ID_AA64MMFR0_EL1,
> >     which requires special validity checking for TGran*_2 fields.
> >   - Add patches to introduce id_reg_info for ID_DFR1_EL1 and
> >     ID_MMFR0_EL1, which are required due to arm64_check_features
> >     implementation change.
> >   - Add a new argument, which is a pointer to id_reg_info, for
> >     id_reg_info's validate().
> >
> > v1: https://lore.kernel.org/all/20211012043535.500493-1-reijiw@google.com/
> >
> > [1] https://lore.kernel.org/kvmarm/20211010145636.1950948-1-tabba@google.com/
> > [2] https://lore.kernel.org/kvm/20201102033422.657391-1-liangpeng10@huawei.com/
> > [3] https://lore.kernel.org/all/20220104194918.373612-2-rananta@google.com/
> >
> > Reiji Watanabe (26):
> >   KVM: arm64: Introduce a validation function for an ID register
> >   KVM: arm64: Save ID registers' sanitized value per guest
> >   KVM: arm64: Introduce struct id_reg_info
> >   KVM: arm64: Make ID_AA64PFR0_EL1 writable
> >   KVM: arm64: Make ID_AA64PFR1_EL1 writable
> >   KVM: arm64: Make ID_AA64ISAR0_EL1 writable
> >   KVM: arm64: Make ID_AA64ISAR1_EL1 writable
> >   KVM: arm64: Make ID_AA64MMFR0_EL1 writable
> >   KVM: arm64: Hide IMPLEMENTATION DEFINED PMU support for the guest
> >   KVM: arm64: Make ID_AA64DFR0_EL1 writable
> >   KVM: arm64: Make ID_DFR0_EL1 writable
> >   KVM: arm64: Make MVFR1_EL1 writable
> >   KVM: arm64: Make ID registers without id_reg_info writable
> >   KVM: arm64: Add consistency checking for frac fields of ID registers
> >   KVM: arm64: Introduce KVM_CAP_ARM_ID_REG_CONFIGURABLE capability
> >   KVM: arm64: Add kunit test for ID register validation
> >   KVM: arm64: Use vcpu->arch cptr_el2 to track value of cptr_el2 for VHE
> >   KVM: arm64: Use vcpu->arch.mdcr_el2 to track value of mdcr_el2
> >   KVM: arm64: Introduce framework to trap disabled features
> >   KVM: arm64: Trap disabled features of ID_AA64PFR0_EL1
> >   KVM: arm64: Trap disabled features of ID_AA64PFR1_EL1
> >   KVM: arm64: Trap disabled features of ID_AA64DFR0_EL1
> >   KVM: arm64: Trap disabled features of ID_AA64MMFR1_EL1
> >   KVM: arm64: Trap disabled features of ID_AA64ISAR1_EL1
> >   KVM: arm64: Add kunit test for trap initialization
> >   KVM: arm64: selftests: Introduce id_reg_test
> >
> >  Documentation/virt/kvm/api.rst                |   12 +
> >  arch/arm64/include/asm/cpufeature.h           |    3 +-
> >  arch/arm64/include/asm/kvm_arm.h              |   32 +
> >  arch/arm64/include/asm/kvm_host.h             |   19 +
> >  arch/arm64/include/asm/sysreg.h               |    3 +
> >  arch/arm64/kernel/cpufeature.c                |  228 +++
> >  arch/arm64/kvm/.kunitconfig                   |    4 +
> >  arch/arm64/kvm/Kconfig                        |   11 +
> >  arch/arm64/kvm/arm.c                          |   24 +-
> >  arch/arm64/kvm/debug.c                        |   13 +-
> >  arch/arm64/kvm/hyp/vhe/switch.c               |   14 +-
> >  arch/arm64/kvm/sys_regs.c                     | 1329 +++++++++++++++--
> >  arch/arm64/kvm/sys_regs_test.c                | 1247 ++++++++++++++++
> >  arch/arm64/kvm/vgic/vgic-init.c               |    5 +
> >  include/uapi/linux/kvm.h                      |    1 +
> >  tools/arch/arm64/include/asm/sysreg.h         |    1 +
> >  tools/testing/selftests/kvm/.gitignore        |    1 +
> >  tools/testing/selftests/kvm/Makefile          |    1 +
> >  .../selftests/kvm/aarch64/id_reg_test.c       | 1239 +++++++++++++++
> >  19 files changed, 4041 insertions(+), 146 deletions(-)
> >  create mode 100644 arch/arm64/kvm/.kunitconfig
> >  create mode 100644 arch/arm64/kvm/sys_regs_test.c
> >  create mode 100644 tools/testing/selftests/kvm/aarch64/id_reg_test.c
> >
> >
> > base-commit: d399b107ee49bf5ea0391bd7614d512809e927b0
> > --
> > 2.34.1.448.ga2b2bfdf31-goog
> >
> > _______________________________________________
> > kvmarm mailing list
> > kvmarm@lists.cs.columbia.edu
> > https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 00/26] KVM: arm64: Make CPU ID registers writable by userspace
@ 2022-01-25  6:31     ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-25  6:31 UTC (permalink / raw)
  To: Fuad Tabba
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, Linux ARM

Hi Fuad,

On Mon, Jan 24, 2022 at 8:19 AM Fuad Tabba <tabba@google.com> wrote:
>
> Hi Reiji,
>
> On Thu, Jan 6, 2022 at 4:27 AM Reiji Watanabe <reijiw@google.com> wrote:
>
> > The patch series is for both VHE and non-VHE, except for protected VMs,
> > which have a different way of configuring ID registers based on its
> > different requirements [1].
>
> I am reviewing this patch series and I really like your approach. I
> think that once it's through we might be able to generalize it for
> protected VMs as well (I did some of the work for the ID registers for
> those).

Thank you for the review and comments for the series.
I will be looking at your comments.

> > The series is based on v5.16-rc8 with the patch [3] applied.
>
> I tried to apply this series on v5.16-rc8 after applying
> 20220104194918.373612-2-rananta@google.com, and it didn't apply
> cleanly. If you could point me to a working branch that I could
> checkout I would like to test it and experiment with it a bit more.

I'm sorry for the inconvenience.  I'm not sure why though...
I tried that again (applied the series on v5.16-rc8 after applying
20220104194918.373612-2-rananta@google.com), and it worked fine.

Anyway, you can find the branch at:
 https://github.com/reijiw-kvm/kvmarm-idreg/tree/id-regs-v4-5.16-rc8

Thanks,
Reiji


>
>
>
>
> > v4:
> >   - Make ID registers storage per VM instead of per vCPU. [Marc]
> >   - Implement arm64_check_features() in arch/arm64/kernel/cpufeature.c
> >     by using existing codes in the file. [Marc]
> >   - Use a configuration function to enable traps for disabled
> >     features. [Marc]
> >   - Document ID registers become immutable after the first KVM_RUN [Eric]
> >   - Update ID_AA64PFR0.GIC at the point where a GICv3 is created. [Marc]
> >   - Get TGranX's bit position by substracting 12 from TGranX_2's bit
> >     position. [Eric]
> >   - Don't validate AArch32 ID registers when the system doesn't support
> >     32bit EL0. [Eric]
> >   - Add/fixes comments for patches. [Eric]
> >   - Made bug fixes/improvements of the selftest. [Eric]
> >   - Added .kunitconfig for arm64 KUnit tests
> >
> > v3: https://lore.kernel.org/all/20211117064359.2362060-1-reijiw@google.com/
> >   - Remove ID register consistency checking across vCPUs. [Oliver]
> >   - Change KVM_CAP_ARM_ID_REG_WRITABLE to
> >     KVM_CAP_ARM_ID_REG_CONFIGURABLE. [Oliver]
> >   - Add KUnit testing for ID register validation and trap initialization.
> >   - Change read_id_reg() to take care of ID_AA64PFR0_EL1.GIC.
> >   - Add a helper of read_id_reg() (__read_id_reg()) and use the helper
> >     instead of directly using __vcpu_sys_reg().
> >   - Change not to run kvm_id_regs_consistency_check() and
> >     kvm_vcpu_init_traps() for protected VMs.
> >   - Update selftest to remove test cases for ID register consistency.
> >     checking across vCPUs and to add test cases for ID_AA64PFR0_EL1.GIC.
> >
> > v2: https://lore.kernel.org/all/20211103062520.1445832-1-reijiw@google.com/
> >   - Remove unnecessary line breaks. [Andrew]
> >   - Use @params for comments. [Andrew]
> >   - Move arm64_check_features to arch/arm64/kvm/sys_regs.c and
> >     change that KVM specific feature check function.  [Andrew]
> >   - Remove unnecessary raz handling from __set_id_reg. [Andrew]
> >   - Remove sys_val field from the initial id_reg_info and add it
> >     in the later patch. [Andrew]
> >   - Call id_reg->init() from id_reg_info_init(). [Andrew]
> >   - Fix cpuid_feature_cap_perfmon_field() to convert 0xf to 0x0
> >     (and use it in the following patches).
> >   - Change kvm_vcpu_first_run_init to set has_run_once to false
> >     when kvm_id_regs_consistency_check() fails.
> >   - Add a patch to introduce id_reg_info for ID_AA64MMFR0_EL1,
> >     which requires special validity checking for TGran*_2 fields.
> >   - Add patches to introduce id_reg_info for ID_DFR1_EL1 and
> >     ID_MMFR0_EL1, which are required due to arm64_check_features
> >     implementation change.
> >   - Add a new argument, which is a pointer to id_reg_info, for
> >     id_reg_info's validate().
> >
> > v1: https://lore.kernel.org/all/20211012043535.500493-1-reijiw@google.com/
> >
> > [1] https://lore.kernel.org/kvmarm/20211010145636.1950948-1-tabba@google.com/
> > [2] https://lore.kernel.org/kvm/20201102033422.657391-1-liangpeng10@huawei.com/
> > [3] https://lore.kernel.org/all/20220104194918.373612-2-rananta@google.com/
> >
> > Reiji Watanabe (26):
> >   KVM: arm64: Introduce a validation function for an ID register
> >   KVM: arm64: Save ID registers' sanitized value per guest
> >   KVM: arm64: Introduce struct id_reg_info
> >   KVM: arm64: Make ID_AA64PFR0_EL1 writable
> >   KVM: arm64: Make ID_AA64PFR1_EL1 writable
> >   KVM: arm64: Make ID_AA64ISAR0_EL1 writable
> >   KVM: arm64: Make ID_AA64ISAR1_EL1 writable
> >   KVM: arm64: Make ID_AA64MMFR0_EL1 writable
> >   KVM: arm64: Hide IMPLEMENTATION DEFINED PMU support for the guest
> >   KVM: arm64: Make ID_AA64DFR0_EL1 writable
> >   KVM: arm64: Make ID_DFR0_EL1 writable
> >   KVM: arm64: Make MVFR1_EL1 writable
> >   KVM: arm64: Make ID registers without id_reg_info writable
> >   KVM: arm64: Add consistency checking for frac fields of ID registers
> >   KVM: arm64: Introduce KVM_CAP_ARM_ID_REG_CONFIGURABLE capability
> >   KVM: arm64: Add kunit test for ID register validation
> >   KVM: arm64: Use vcpu->arch cptr_el2 to track value of cptr_el2 for VHE
> >   KVM: arm64: Use vcpu->arch.mdcr_el2 to track value of mdcr_el2
> >   KVM: arm64: Introduce framework to trap disabled features
> >   KVM: arm64: Trap disabled features of ID_AA64PFR0_EL1
> >   KVM: arm64: Trap disabled features of ID_AA64PFR1_EL1
> >   KVM: arm64: Trap disabled features of ID_AA64DFR0_EL1
> >   KVM: arm64: Trap disabled features of ID_AA64MMFR1_EL1
> >   KVM: arm64: Trap disabled features of ID_AA64ISAR1_EL1
> >   KVM: arm64: Add kunit test for trap initialization
> >   KVM: arm64: selftests: Introduce id_reg_test
> >
> >  Documentation/virt/kvm/api.rst                |   12 +
> >  arch/arm64/include/asm/cpufeature.h           |    3 +-
> >  arch/arm64/include/asm/kvm_arm.h              |   32 +
> >  arch/arm64/include/asm/kvm_host.h             |   19 +
> >  arch/arm64/include/asm/sysreg.h               |    3 +
> >  arch/arm64/kernel/cpufeature.c                |  228 +++
> >  arch/arm64/kvm/.kunitconfig                   |    4 +
> >  arch/arm64/kvm/Kconfig                        |   11 +
> >  arch/arm64/kvm/arm.c                          |   24 +-
> >  arch/arm64/kvm/debug.c                        |   13 +-
> >  arch/arm64/kvm/hyp/vhe/switch.c               |   14 +-
> >  arch/arm64/kvm/sys_regs.c                     | 1329 +++++++++++++++--
> >  arch/arm64/kvm/sys_regs_test.c                | 1247 ++++++++++++++++
> >  arch/arm64/kvm/vgic/vgic-init.c               |    5 +
> >  include/uapi/linux/kvm.h                      |    1 +
> >  tools/arch/arm64/include/asm/sysreg.h         |    1 +
> >  tools/testing/selftests/kvm/.gitignore        |    1 +
> >  tools/testing/selftests/kvm/Makefile          |    1 +
> >  .../selftests/kvm/aarch64/id_reg_test.c       | 1239 +++++++++++++++
> >  19 files changed, 4041 insertions(+), 146 deletions(-)
> >  create mode 100644 arch/arm64/kvm/.kunitconfig
> >  create mode 100644 arch/arm64/kvm/sys_regs_test.c
> >  create mode 100644 tools/testing/selftests/kvm/aarch64/id_reg_test.c
> >
> >
> > base-commit: d399b107ee49bf5ea0391bd7614d512809e927b0
> > --
> > 2.34.1.448.ga2b2bfdf31-goog
> >
> > _______________________________________________
> > kvmarm mailing list
> > kvmarm@lists.cs.columbia.edu
> > https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 00/26] KVM: arm64: Make CPU ID registers writable by userspace
@ 2022-01-25  6:31     ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-25  6:31 UTC (permalink / raw)
  To: Fuad Tabba
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, Linux ARM

Hi Fuad,

On Mon, Jan 24, 2022 at 8:19 AM Fuad Tabba <tabba@google.com> wrote:
>
> Hi Reiji,
>
> On Thu, Jan 6, 2022 at 4:27 AM Reiji Watanabe <reijiw@google.com> wrote:
>
> > The patch series is for both VHE and non-VHE, except for protected VMs,
> > which have a different way of configuring ID registers based on its
> > different requirements [1].
>
> I am reviewing this patch series and I really like your approach. I
> think that once it's through we might be able to generalize it for
> protected VMs as well (I did some of the work for the ID registers for
> those).

Thank you for the review and comments for the series.
I will be looking at your comments.

> > The series is based on v5.16-rc8 with the patch [3] applied.
>
> I tried to apply this series on v5.16-rc8 after applying
> 20220104194918.373612-2-rananta@google.com, and it didn't apply
> cleanly. If you could point me to a working branch that I could
> checkout I would like to test it and experiment with it a bit more.

I'm sorry for the inconvenience.  I'm not sure why though...
I tried that again (applied the series on v5.16-rc8 after applying
20220104194918.373612-2-rananta@google.com), and it worked fine.

Anyway, you can find the branch at:
 https://github.com/reijiw-kvm/kvmarm-idreg/tree/id-regs-v4-5.16-rc8

Thanks,
Reiji


>
>
>
>
> > v4:
> >   - Make ID registers storage per VM instead of per vCPU. [Marc]
> >   - Implement arm64_check_features() in arch/arm64/kernel/cpufeature.c
> >     by using existing codes in the file. [Marc]
> >   - Use a configuration function to enable traps for disabled
> >     features. [Marc]
> >   - Document ID registers become immutable after the first KVM_RUN [Eric]
> >   - Update ID_AA64PFR0.GIC at the point where a GICv3 is created. [Marc]
> >   - Get TGranX's bit position by substracting 12 from TGranX_2's bit
> >     position. [Eric]
> >   - Don't validate AArch32 ID registers when the system doesn't support
> >     32bit EL0. [Eric]
> >   - Add/fixes comments for patches. [Eric]
> >   - Made bug fixes/improvements of the selftest. [Eric]
> >   - Added .kunitconfig for arm64 KUnit tests
> >
> > v3: https://lore.kernel.org/all/20211117064359.2362060-1-reijiw@google.com/
> >   - Remove ID register consistency checking across vCPUs. [Oliver]
> >   - Change KVM_CAP_ARM_ID_REG_WRITABLE to
> >     KVM_CAP_ARM_ID_REG_CONFIGURABLE. [Oliver]
> >   - Add KUnit testing for ID register validation and trap initialization.
> >   - Change read_id_reg() to take care of ID_AA64PFR0_EL1.GIC.
> >   - Add a helper of read_id_reg() (__read_id_reg()) and use the helper
> >     instead of directly using __vcpu_sys_reg().
> >   - Change not to run kvm_id_regs_consistency_check() and
> >     kvm_vcpu_init_traps() for protected VMs.
> >   - Update selftest to remove test cases for ID register consistency.
> >     checking across vCPUs and to add test cases for ID_AA64PFR0_EL1.GIC.
> >
> > v2: https://lore.kernel.org/all/20211103062520.1445832-1-reijiw@google.com/
> >   - Remove unnecessary line breaks. [Andrew]
> >   - Use @params for comments. [Andrew]
> >   - Move arm64_check_features to arch/arm64/kvm/sys_regs.c and
> >     change that KVM specific feature check function.  [Andrew]
> >   - Remove unnecessary raz handling from __set_id_reg. [Andrew]
> >   - Remove sys_val field from the initial id_reg_info and add it
> >     in the later patch. [Andrew]
> >   - Call id_reg->init() from id_reg_info_init(). [Andrew]
> >   - Fix cpuid_feature_cap_perfmon_field() to convert 0xf to 0x0
> >     (and use it in the following patches).
> >   - Change kvm_vcpu_first_run_init to set has_run_once to false
> >     when kvm_id_regs_consistency_check() fails.
> >   - Add a patch to introduce id_reg_info for ID_AA64MMFR0_EL1,
> >     which requires special validity checking for TGran*_2 fields.
> >   - Add patches to introduce id_reg_info for ID_DFR1_EL1 and
> >     ID_MMFR0_EL1, which are required due to arm64_check_features
> >     implementation change.
> >   - Add a new argument, which is a pointer to id_reg_info, for
> >     id_reg_info's validate().
> >
> > v1: https://lore.kernel.org/all/20211012043535.500493-1-reijiw@google.com/
> >
> > [1] https://lore.kernel.org/kvmarm/20211010145636.1950948-1-tabba@google.com/
> > [2] https://lore.kernel.org/kvm/20201102033422.657391-1-liangpeng10@huawei.com/
> > [3] https://lore.kernel.org/all/20220104194918.373612-2-rananta@google.com/
> >
> > Reiji Watanabe (26):
> >   KVM: arm64: Introduce a validation function for an ID register
> >   KVM: arm64: Save ID registers' sanitized value per guest
> >   KVM: arm64: Introduce struct id_reg_info
> >   KVM: arm64: Make ID_AA64PFR0_EL1 writable
> >   KVM: arm64: Make ID_AA64PFR1_EL1 writable
> >   KVM: arm64: Make ID_AA64ISAR0_EL1 writable
> >   KVM: arm64: Make ID_AA64ISAR1_EL1 writable
> >   KVM: arm64: Make ID_AA64MMFR0_EL1 writable
> >   KVM: arm64: Hide IMPLEMENTATION DEFINED PMU support for the guest
> >   KVM: arm64: Make ID_AA64DFR0_EL1 writable
> >   KVM: arm64: Make ID_DFR0_EL1 writable
> >   KVM: arm64: Make MVFR1_EL1 writable
> >   KVM: arm64: Make ID registers without id_reg_info writable
> >   KVM: arm64: Add consistency checking for frac fields of ID registers
> >   KVM: arm64: Introduce KVM_CAP_ARM_ID_REG_CONFIGURABLE capability
> >   KVM: arm64: Add kunit test for ID register validation
> >   KVM: arm64: Use vcpu->arch cptr_el2 to track value of cptr_el2 for VHE
> >   KVM: arm64: Use vcpu->arch.mdcr_el2 to track value of mdcr_el2
> >   KVM: arm64: Introduce framework to trap disabled features
> >   KVM: arm64: Trap disabled features of ID_AA64PFR0_EL1
> >   KVM: arm64: Trap disabled features of ID_AA64PFR1_EL1
> >   KVM: arm64: Trap disabled features of ID_AA64DFR0_EL1
> >   KVM: arm64: Trap disabled features of ID_AA64MMFR1_EL1
> >   KVM: arm64: Trap disabled features of ID_AA64ISAR1_EL1
> >   KVM: arm64: Add kunit test for trap initialization
> >   KVM: arm64: selftests: Introduce id_reg_test
> >
> >  Documentation/virt/kvm/api.rst                |   12 +
> >  arch/arm64/include/asm/cpufeature.h           |    3 +-
> >  arch/arm64/include/asm/kvm_arm.h              |   32 +
> >  arch/arm64/include/asm/kvm_host.h             |   19 +
> >  arch/arm64/include/asm/sysreg.h               |    3 +
> >  arch/arm64/kernel/cpufeature.c                |  228 +++
> >  arch/arm64/kvm/.kunitconfig                   |    4 +
> >  arch/arm64/kvm/Kconfig                        |   11 +
> >  arch/arm64/kvm/arm.c                          |   24 +-
> >  arch/arm64/kvm/debug.c                        |   13 +-
> >  arch/arm64/kvm/hyp/vhe/switch.c               |   14 +-
> >  arch/arm64/kvm/sys_regs.c                     | 1329 +++++++++++++++--
> >  arch/arm64/kvm/sys_regs_test.c                | 1247 ++++++++++++++++
> >  arch/arm64/kvm/vgic/vgic-init.c               |    5 +
> >  include/uapi/linux/kvm.h                      |    1 +
> >  tools/arch/arm64/include/asm/sysreg.h         |    1 +
> >  tools/testing/selftests/kvm/.gitignore        |    1 +
> >  tools/testing/selftests/kvm/Makefile          |    1 +
> >  .../selftests/kvm/aarch64/id_reg_test.c       | 1239 +++++++++++++++
> >  19 files changed, 4041 insertions(+), 146 deletions(-)
> >  create mode 100644 arch/arm64/kvm/.kunitconfig
> >  create mode 100644 arch/arm64/kvm/sys_regs_test.c
> >  create mode 100644 tools/testing/selftests/kvm/aarch64/id_reg_test.c
> >
> >
> > base-commit: d399b107ee49bf5ea0391bd7614d512809e927b0
> > --
> > 2.34.1.448.ga2b2bfdf31-goog
> >
> > _______________________________________________
> > kvmarm mailing list
> > kvmarm@lists.cs.columbia.edu
> > https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 01/26] KVM: arm64: Introduce a validation function for an ID register
  2022-01-06  4:26   ` Reiji Watanabe
  (?)
@ 2022-01-26  4:30     ` Ricardo Koller
  -1 siblings, 0 replies; 201+ messages in thread
From: Ricardo Koller @ 2022-01-26  4:30 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: Marc Zyngier, kvmarm, kvm, linux-arm-kernel, James Morse,
	Alexandru Elisei, Suzuki K Poulose, Paolo Bonzini, Will Deacon,
	Andrew Jones, Peng Liang, Peter Shier, Oliver Upton, Jing Zhang,
	Raghavendra Rao Anata

Hey Reiji,

On Wed, Jan 05, 2022 at 08:26:43PM -0800, Reiji Watanabe wrote:
> Introduce arm64_check_features(), which does a basic validity checking
> of an ID register value against the register's limit value, which is
> generally the host's sanitized value.
> 
> This function will be used by the following patches to check if an ID
> register value that userspace tries to set for a guest can be supported
> on the host.
> 
> The validation is done using arm64_ftr_bits_kvm, which is created from
> arm64_ftr_regs, with some entries overwritten by entries from
> arm64_ftr_bits_kvm_override.
> 
> Signed-off-by: Reiji Watanabe <reijiw@google.com>
> ---
>  arch/arm64/include/asm/cpufeature.h |   1 +
>  arch/arm64/kernel/cpufeature.c      | 228 ++++++++++++++++++++++++++++
>  2 files changed, 229 insertions(+)
> 
> diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
> index ef6be92b1921..eda7ddbed8cf 100644
> --- a/arch/arm64/include/asm/cpufeature.h
> +++ b/arch/arm64/include/asm/cpufeature.h
> @@ -631,6 +631,7 @@ void check_local_cpu_capabilities(void);
>  
>  u64 read_sanitised_ftr_reg(u32 id);
>  u64 __read_sysreg_by_encoding(u32 sys_id);
> +int arm64_check_features(u32 sys_reg, u64 val, u64 limit);
>  
>  static inline bool cpu_supports_mixed_endian_el0(void)
>  {
> diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
> index 6f3e677d88f1..48dff8b101d9 100644
> --- a/arch/arm64/kernel/cpufeature.c
> +++ b/arch/arm64/kernel/cpufeature.c
> @@ -3140,3 +3140,231 @@ ssize_t cpu_show_meltdown(struct device *dev, struct device_attribute *attr,
>  		return sprintf(buf, "Vulnerable\n");
>  	}
>  }
> +
> +#ifdef CONFIG_KVM
> +/*
> + * arm64_ftr_bits_kvm[] is used for KVM to check if features that are
> + * indicated in an ID register value for the guest are available on the host.
> + * arm64_ftr_bits_kvm[] is created based on arm64_ftr_regs[].  But, for
> + * registers for which arm64_ftr_bits_kvm_override[] has a corresponding
> + * entry, replace arm64_ftr_bits entries in arm64_ftr_bits_kvm[] with the
> + * ones in arm64_ftr_bits_kvm_override[].
> + */
> +static struct __ftr_reg_bits_entry *arm64_ftr_bits_kvm;
> +static size_t arm64_ftr_bits_kvm_nentries;

I don't think this is really needed, as arm64_ftr_bits_kvm_override has
to have the same size as arm64_ftr_bits_kvm. You could use
ARRAY_SIZE(arm64_ftr_regs) like in get_arm64_ftr_reg_nowarn().

> +static DEFINE_MUTEX(arm64_ftr_bits_kvm_lock);
> +
> +/*
> + * Number of arm64_ftr_bits entries for each register.
> + * (Number of 4 bits fields in 64 bit register + 1 entry for ARM64_FTR_END)
> + */
> +#define	MAX_FTR_BITS_LEN	17
> +
> +/* Use FTR_LOWER_SAFE for AA64DFR0_EL1.PMUVER and AA64DFR0_EL1.DEBUGVER. */
> +static struct arm64_ftr_bits ftr_id_aa64dfr0_kvm[MAX_FTR_BITS_LEN] = {
> +	S_ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64DFR0_PMUVER_SHIFT, 4, 0),
> +	ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x6),
> +	ARM64_FTR_END,
> +};
> +
> +#define	ARM64_FTR_REG_BITS(id, table)	{	\
> +	.sys_id = id,				\
> +	.ftr_bits = &((table)[0]),		\
> +}
> +
> +struct __ftr_reg_bits_entry {
> +	u32	sys_id;
> +	struct arm64_ftr_bits	*ftr_bits;
> +};
> +
> +/*
> + * All entries in arm64_ftr_bits_kvm_override[] are used to override
> + * the corresponding entries in arm64_ftr_bits_kvm[].
> + */
> +static struct __ftr_reg_bits_entry arm64_ftr_bits_kvm_override[] = {
> +	ARM64_FTR_REG_BITS(SYS_ID_AA64DFR0_EL1, ftr_id_aa64dfr0_kvm),
> +};
> +
> +/*
> + * Override entries in @orig_ftrp with the ones in @new_ftrp when their shift
> + * fields match.  The last entry of @orig_ftrp and @new_ftrp must be
> + * ARM64_FTR_END (.width == 0).
> + */
> +static void arm64_ftr_reg_bits_overrite(struct arm64_ftr_bits *orig_ftrp,
> +					struct arm64_ftr_bits *new_ftrp)
> +{
> +	struct arm64_ftr_bits *o_ftrp, *n_ftrp;
> +
> +	for (n_ftrp = new_ftrp; n_ftrp->width; n_ftrp++) {
> +		for (o_ftrp = orig_ftrp; o_ftrp->width; o_ftrp++) {
> +			if (o_ftrp->shift == n_ftrp->shift) {
> +				*o_ftrp = *n_ftrp;
> +				break;
> +			}
> +		}
> +	}
> +}
> +
> +/*
> + * Copy arm64_ftr_bits entries from @src_ftrp to @dst_ftrp.  The last entries
> + * of @dst_ftrp and @src_ftrp must be ARM64_FTR_END (.width == 0).
> + */
> +static void copy_arm64_ftr_bits(struct arm64_ftr_bits *dst_ftrp,
> +				const struct arm64_ftr_bits *src_ftrp)
> +{
> +	int i = 0;
> +
> +	for (; src_ftrp[i].width; i++) {
> +		if (WARN_ON_ONCE(i >= (MAX_FTR_BITS_LEN - 1)))
> +			break;
> +
> +		dst_ftrp[i] = src_ftrp[i];
> +	}
> +
> +	dst_ftrp[i].width = 0;
> +}
> +
> +/*
> + * Initialize arm64_ftr_bits_kvm.  Copy arm64_ftr_bits for each ID register
> + * from arm64_ftr_regs to arm64_ftr_bits_kvm, and then override entries in
> + * arm64_ftr_bits_kvm with ones in arm64_ftr_bits_kvm_override.
> + */
> +static int init_arm64_ftr_bits_kvm(void)
> +{
> +	struct arm64_ftr_bits ftr_temp[MAX_FTR_BITS_LEN];
> +	static struct __ftr_reg_bits_entry *reg_bits_array, *bits, *o_bits;
> +	int i, j, nent, ret;
> +
> +	mutex_lock(&arm64_ftr_bits_kvm_lock);

This is initialized lazily, whenever KVM calls arm64_check_features(). I
guess that's why it needs the lock (and possibly a barrier as you
mentoin in your reply). Would it be possible to simplify things by
initializing arm64_ftr_bits_kvm somewhere at boot time (in
init_cpu_features maybe?)?

> +	if (arm64_ftr_bits_kvm) {
> +		/* Already initialized */
> +		ret = 0;
> +		goto unlock_exit;
> +	}
> +
> +	nent = ARRAY_SIZE(arm64_ftr_regs);
> +	reg_bits_array = kcalloc(nent, sizeof(struct __ftr_reg_bits_entry),
> +				 GFP_KERNEL);
> +	if (!reg_bits_array) {
> +		ret = ENOMEM;
> +		goto unlock_exit;
> +	}
> +
> +	/* Copy entries from arm64_ftr_regs to reg_bits_array */
> +	for (i = 0; i < nent; i++) {
> +		bits = &reg_bits_array[i];
> +		bits->sys_id = arm64_ftr_regs[i].sys_id;
> +		bits->ftr_bits = (struct arm64_ftr_bits *)arm64_ftr_regs[i].reg->ftr_bits;
> +	};
> +
> +	/*
> +	 * Override the entries in reg_bits_array with the ones in
> +	 * arm64_ftr_bits_kvm_override.
> +	 */
> +	for (i = 0; i < ARRAY_SIZE(arm64_ftr_bits_kvm_override); i++) {
> +		o_bits = &arm64_ftr_bits_kvm_override[i];
> +		for (j = 0; j < nent; j++) {
> +			bits = &reg_bits_array[j];
> +			if (bits->sys_id != o_bits->sys_id)
> +				continue;
> +
> +			memset(ftr_temp, 0, sizeof(ftr_temp));
> +
> +			/*
> +			 * Temporary save all entries in o_bits->ftr_bits
> +			 * to ftr_temp.
> +			 */
> +			copy_arm64_ftr_bits(ftr_temp, o_bits->ftr_bits);
> +
> +			/*
> +			 * Copy entries from bits->ftr_bits to o_bits->ftr_bits.
> +			 */
> +			copy_arm64_ftr_bits(o_bits->ftr_bits, bits->ftr_bits);
> +
> +			/*
> +			 * Override entries in o_bits->ftr_bits with the
> +			 * saved ones, and update bits->ftr_bits with
> +			 * o_bits->ftr_bits.
> +			 */
> +			arm64_ftr_reg_bits_overrite(o_bits->ftr_bits, ftr_temp);
> +			bits->ftr_bits = o_bits->ftr_bits;
> +			break;
> +		}
> +	}
> +
> +	arm64_ftr_bits_kvm_nentries = nent;
> +	arm64_ftr_bits_kvm = reg_bits_array;
> +	ret = 0;
> +
> +unlock_exit:
> +	mutex_unlock(&arm64_ftr_bits_kvm_lock);
> +	return ret;
> +}
> +
> +static int search_cmp_ftr_reg_bits(const void *id, const void *regp)
> +{
> +	return ((int)(unsigned long)id -
> +		(int)((const struct __ftr_reg_bits_entry *)regp)->sys_id);
> +}
> +
> +static const struct arm64_ftr_bits *get_arm64_ftr_bits_kvm(u32 sys_id)
> +{
> +	const struct __ftr_reg_bits_entry *ret;
> +	int err;
> +
> +	if (!arm64_ftr_bits_kvm) {
> +		/* arm64_ftr_bits_kvm is not initialized yet. */
> +		err = init_arm64_ftr_bits_kvm();
> +		if (err)
> +			return NULL;
> +	}
> +
> +	ret = bsearch((const void *)(unsigned long)sys_id,
> +		      arm64_ftr_bits_kvm,
> +		      arm64_ftr_bits_kvm_nentries,
> +		      sizeof(arm64_ftr_bits_kvm[0]),
> +		      search_cmp_ftr_reg_bits);
> +	if (ret)
> +		return ret->ftr_bits;
> +
> +	return NULL;
> +}
> +
> +/*
> + * Check if features (or levels of features) that are indicated in the ID
> + * register value @val are also indicated in @limit.
> + * This function is for KVM to check if features that are indicated in @val,
> + * which will be used as the ID register value for its guest, are supported
> + * on the host.
> + * For AA64MMFR0_EL1.TGranX_2 fields, which don't follow the standard ID
> + * scheme, the function checks if values of the fields in @val are the same
> + * as the ones in @limit.
> + */
> +int arm64_check_features(u32 sys_reg, u64 val, u64 limit)
> +{
> +	const struct arm64_ftr_bits *ftrp = get_arm64_ftr_bits_kvm(sys_reg);

Given that this is to be used only by KVM (and it's inside CONFIG_KVM),
it might be better to have "kvm" somewhere in its name.

> +	u64 exposed_mask = 0;
> +
> +	if (!ftrp)
> +		return -ENOENT;
> +
> +	for (; ftrp->width; ftrp++) {
> +		s64 ftr_val = arm64_ftr_value(ftrp, val);
> +		s64 ftr_lim = arm64_ftr_value(ftrp, limit);
> +
> +		exposed_mask |= arm64_ftr_mask(ftrp);
> +
> +		if (ftr_val == ftr_lim)
> +			continue;
> +
> +		if (ftr_val != arm64_ftr_safe_value(ftrp, ftr_val, ftr_lim))
> +			return -E2BIG;
> +	}
> +
> +	/* Make sure that no unrecognized fields are set in @val. */
> +	if (val & ~exposed_mask)
> +		return -E2BIG;
> +
> +	return 0;
> +}
> +#endif /* CONFIG_KVM */
> -- 
> 2.34.1.448.ga2b2bfdf31-goog
> 

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 01/26] KVM: arm64: Introduce a validation function for an ID register
@ 2022-01-26  4:30     ` Ricardo Koller
  0 siblings, 0 replies; 201+ messages in thread
From: Ricardo Koller @ 2022-01-26  4:30 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: kvm, Marc Zyngier, Peter Shier, Will Deacon, Paolo Bonzini,
	kvmarm, linux-arm-kernel

Hey Reiji,

On Wed, Jan 05, 2022 at 08:26:43PM -0800, Reiji Watanabe wrote:
> Introduce arm64_check_features(), which does a basic validity checking
> of an ID register value against the register's limit value, which is
> generally the host's sanitized value.
> 
> This function will be used by the following patches to check if an ID
> register value that userspace tries to set for a guest can be supported
> on the host.
> 
> The validation is done using arm64_ftr_bits_kvm, which is created from
> arm64_ftr_regs, with some entries overwritten by entries from
> arm64_ftr_bits_kvm_override.
> 
> Signed-off-by: Reiji Watanabe <reijiw@google.com>
> ---
>  arch/arm64/include/asm/cpufeature.h |   1 +
>  arch/arm64/kernel/cpufeature.c      | 228 ++++++++++++++++++++++++++++
>  2 files changed, 229 insertions(+)
> 
> diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
> index ef6be92b1921..eda7ddbed8cf 100644
> --- a/arch/arm64/include/asm/cpufeature.h
> +++ b/arch/arm64/include/asm/cpufeature.h
> @@ -631,6 +631,7 @@ void check_local_cpu_capabilities(void);
>  
>  u64 read_sanitised_ftr_reg(u32 id);
>  u64 __read_sysreg_by_encoding(u32 sys_id);
> +int arm64_check_features(u32 sys_reg, u64 val, u64 limit);
>  
>  static inline bool cpu_supports_mixed_endian_el0(void)
>  {
> diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
> index 6f3e677d88f1..48dff8b101d9 100644
> --- a/arch/arm64/kernel/cpufeature.c
> +++ b/arch/arm64/kernel/cpufeature.c
> @@ -3140,3 +3140,231 @@ ssize_t cpu_show_meltdown(struct device *dev, struct device_attribute *attr,
>  		return sprintf(buf, "Vulnerable\n");
>  	}
>  }
> +
> +#ifdef CONFIG_KVM
> +/*
> + * arm64_ftr_bits_kvm[] is used for KVM to check if features that are
> + * indicated in an ID register value for the guest are available on the host.
> + * arm64_ftr_bits_kvm[] is created based on arm64_ftr_regs[].  But, for
> + * registers for which arm64_ftr_bits_kvm_override[] has a corresponding
> + * entry, replace arm64_ftr_bits entries in arm64_ftr_bits_kvm[] with the
> + * ones in arm64_ftr_bits_kvm_override[].
> + */
> +static struct __ftr_reg_bits_entry *arm64_ftr_bits_kvm;
> +static size_t arm64_ftr_bits_kvm_nentries;

I don't think this is really needed, as arm64_ftr_bits_kvm_override has
to have the same size as arm64_ftr_bits_kvm. You could use
ARRAY_SIZE(arm64_ftr_regs) like in get_arm64_ftr_reg_nowarn().

> +static DEFINE_MUTEX(arm64_ftr_bits_kvm_lock);
> +
> +/*
> + * Number of arm64_ftr_bits entries for each register.
> + * (Number of 4 bits fields in 64 bit register + 1 entry for ARM64_FTR_END)
> + */
> +#define	MAX_FTR_BITS_LEN	17
> +
> +/* Use FTR_LOWER_SAFE for AA64DFR0_EL1.PMUVER and AA64DFR0_EL1.DEBUGVER. */
> +static struct arm64_ftr_bits ftr_id_aa64dfr0_kvm[MAX_FTR_BITS_LEN] = {
> +	S_ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64DFR0_PMUVER_SHIFT, 4, 0),
> +	ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x6),
> +	ARM64_FTR_END,
> +};
> +
> +#define	ARM64_FTR_REG_BITS(id, table)	{	\
> +	.sys_id = id,				\
> +	.ftr_bits = &((table)[0]),		\
> +}
> +
> +struct __ftr_reg_bits_entry {
> +	u32	sys_id;
> +	struct arm64_ftr_bits	*ftr_bits;
> +};
> +
> +/*
> + * All entries in arm64_ftr_bits_kvm_override[] are used to override
> + * the corresponding entries in arm64_ftr_bits_kvm[].
> + */
> +static struct __ftr_reg_bits_entry arm64_ftr_bits_kvm_override[] = {
> +	ARM64_FTR_REG_BITS(SYS_ID_AA64DFR0_EL1, ftr_id_aa64dfr0_kvm),
> +};
> +
> +/*
> + * Override entries in @orig_ftrp with the ones in @new_ftrp when their shift
> + * fields match.  The last entry of @orig_ftrp and @new_ftrp must be
> + * ARM64_FTR_END (.width == 0).
> + */
> +static void arm64_ftr_reg_bits_overrite(struct arm64_ftr_bits *orig_ftrp,
> +					struct arm64_ftr_bits *new_ftrp)
> +{
> +	struct arm64_ftr_bits *o_ftrp, *n_ftrp;
> +
> +	for (n_ftrp = new_ftrp; n_ftrp->width; n_ftrp++) {
> +		for (o_ftrp = orig_ftrp; o_ftrp->width; o_ftrp++) {
> +			if (o_ftrp->shift == n_ftrp->shift) {
> +				*o_ftrp = *n_ftrp;
> +				break;
> +			}
> +		}
> +	}
> +}
> +
> +/*
> + * Copy arm64_ftr_bits entries from @src_ftrp to @dst_ftrp.  The last entries
> + * of @dst_ftrp and @src_ftrp must be ARM64_FTR_END (.width == 0).
> + */
> +static void copy_arm64_ftr_bits(struct arm64_ftr_bits *dst_ftrp,
> +				const struct arm64_ftr_bits *src_ftrp)
> +{
> +	int i = 0;
> +
> +	for (; src_ftrp[i].width; i++) {
> +		if (WARN_ON_ONCE(i >= (MAX_FTR_BITS_LEN - 1)))
> +			break;
> +
> +		dst_ftrp[i] = src_ftrp[i];
> +	}
> +
> +	dst_ftrp[i].width = 0;
> +}
> +
> +/*
> + * Initialize arm64_ftr_bits_kvm.  Copy arm64_ftr_bits for each ID register
> + * from arm64_ftr_regs to arm64_ftr_bits_kvm, and then override entries in
> + * arm64_ftr_bits_kvm with ones in arm64_ftr_bits_kvm_override.
> + */
> +static int init_arm64_ftr_bits_kvm(void)
> +{
> +	struct arm64_ftr_bits ftr_temp[MAX_FTR_BITS_LEN];
> +	static struct __ftr_reg_bits_entry *reg_bits_array, *bits, *o_bits;
> +	int i, j, nent, ret;
> +
> +	mutex_lock(&arm64_ftr_bits_kvm_lock);

This is initialized lazily, whenever KVM calls arm64_check_features(). I
guess that's why it needs the lock (and possibly a barrier as you
mentoin in your reply). Would it be possible to simplify things by
initializing arm64_ftr_bits_kvm somewhere at boot time (in
init_cpu_features maybe?)?

> +	if (arm64_ftr_bits_kvm) {
> +		/* Already initialized */
> +		ret = 0;
> +		goto unlock_exit;
> +	}
> +
> +	nent = ARRAY_SIZE(arm64_ftr_regs);
> +	reg_bits_array = kcalloc(nent, sizeof(struct __ftr_reg_bits_entry),
> +				 GFP_KERNEL);
> +	if (!reg_bits_array) {
> +		ret = ENOMEM;
> +		goto unlock_exit;
> +	}
> +
> +	/* Copy entries from arm64_ftr_regs to reg_bits_array */
> +	for (i = 0; i < nent; i++) {
> +		bits = &reg_bits_array[i];
> +		bits->sys_id = arm64_ftr_regs[i].sys_id;
> +		bits->ftr_bits = (struct arm64_ftr_bits *)arm64_ftr_regs[i].reg->ftr_bits;
> +	};
> +
> +	/*
> +	 * Override the entries in reg_bits_array with the ones in
> +	 * arm64_ftr_bits_kvm_override.
> +	 */
> +	for (i = 0; i < ARRAY_SIZE(arm64_ftr_bits_kvm_override); i++) {
> +		o_bits = &arm64_ftr_bits_kvm_override[i];
> +		for (j = 0; j < nent; j++) {
> +			bits = &reg_bits_array[j];
> +			if (bits->sys_id != o_bits->sys_id)
> +				continue;
> +
> +			memset(ftr_temp, 0, sizeof(ftr_temp));
> +
> +			/*
> +			 * Temporary save all entries in o_bits->ftr_bits
> +			 * to ftr_temp.
> +			 */
> +			copy_arm64_ftr_bits(ftr_temp, o_bits->ftr_bits);
> +
> +			/*
> +			 * Copy entries from bits->ftr_bits to o_bits->ftr_bits.
> +			 */
> +			copy_arm64_ftr_bits(o_bits->ftr_bits, bits->ftr_bits);
> +
> +			/*
> +			 * Override entries in o_bits->ftr_bits with the
> +			 * saved ones, and update bits->ftr_bits with
> +			 * o_bits->ftr_bits.
> +			 */
> +			arm64_ftr_reg_bits_overrite(o_bits->ftr_bits, ftr_temp);
> +			bits->ftr_bits = o_bits->ftr_bits;
> +			break;
> +		}
> +	}
> +
> +	arm64_ftr_bits_kvm_nentries = nent;
> +	arm64_ftr_bits_kvm = reg_bits_array;
> +	ret = 0;
> +
> +unlock_exit:
> +	mutex_unlock(&arm64_ftr_bits_kvm_lock);
> +	return ret;
> +}
> +
> +static int search_cmp_ftr_reg_bits(const void *id, const void *regp)
> +{
> +	return ((int)(unsigned long)id -
> +		(int)((const struct __ftr_reg_bits_entry *)regp)->sys_id);
> +}
> +
> +static const struct arm64_ftr_bits *get_arm64_ftr_bits_kvm(u32 sys_id)
> +{
> +	const struct __ftr_reg_bits_entry *ret;
> +	int err;
> +
> +	if (!arm64_ftr_bits_kvm) {
> +		/* arm64_ftr_bits_kvm is not initialized yet. */
> +		err = init_arm64_ftr_bits_kvm();
> +		if (err)
> +			return NULL;
> +	}
> +
> +	ret = bsearch((const void *)(unsigned long)sys_id,
> +		      arm64_ftr_bits_kvm,
> +		      arm64_ftr_bits_kvm_nentries,
> +		      sizeof(arm64_ftr_bits_kvm[0]),
> +		      search_cmp_ftr_reg_bits);
> +	if (ret)
> +		return ret->ftr_bits;
> +
> +	return NULL;
> +}
> +
> +/*
> + * Check if features (or levels of features) that are indicated in the ID
> + * register value @val are also indicated in @limit.
> + * This function is for KVM to check if features that are indicated in @val,
> + * which will be used as the ID register value for its guest, are supported
> + * on the host.
> + * For AA64MMFR0_EL1.TGranX_2 fields, which don't follow the standard ID
> + * scheme, the function checks if values of the fields in @val are the same
> + * as the ones in @limit.
> + */
> +int arm64_check_features(u32 sys_reg, u64 val, u64 limit)
> +{
> +	const struct arm64_ftr_bits *ftrp = get_arm64_ftr_bits_kvm(sys_reg);

Given that this is to be used only by KVM (and it's inside CONFIG_KVM),
it might be better to have "kvm" somewhere in its name.

> +	u64 exposed_mask = 0;
> +
> +	if (!ftrp)
> +		return -ENOENT;
> +
> +	for (; ftrp->width; ftrp++) {
> +		s64 ftr_val = arm64_ftr_value(ftrp, val);
> +		s64 ftr_lim = arm64_ftr_value(ftrp, limit);
> +
> +		exposed_mask |= arm64_ftr_mask(ftrp);
> +
> +		if (ftr_val == ftr_lim)
> +			continue;
> +
> +		if (ftr_val != arm64_ftr_safe_value(ftrp, ftr_val, ftr_lim))
> +			return -E2BIG;
> +	}
> +
> +	/* Make sure that no unrecognized fields are set in @val. */
> +	if (val & ~exposed_mask)
> +		return -E2BIG;
> +
> +	return 0;
> +}
> +#endif /* CONFIG_KVM */
> -- 
> 2.34.1.448.ga2b2bfdf31-goog
> 
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 01/26] KVM: arm64: Introduce a validation function for an ID register
@ 2022-01-26  4:30     ` Ricardo Koller
  0 siblings, 0 replies; 201+ messages in thread
From: Ricardo Koller @ 2022-01-26  4:30 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: Marc Zyngier, kvmarm, kvm, linux-arm-kernel, James Morse,
	Alexandru Elisei, Suzuki K Poulose, Paolo Bonzini, Will Deacon,
	Andrew Jones, Peng Liang, Peter Shier, Oliver Upton, Jing Zhang,
	Raghavendra Rao Anata

Hey Reiji,

On Wed, Jan 05, 2022 at 08:26:43PM -0800, Reiji Watanabe wrote:
> Introduce arm64_check_features(), which does a basic validity checking
> of an ID register value against the register's limit value, which is
> generally the host's sanitized value.
> 
> This function will be used by the following patches to check if an ID
> register value that userspace tries to set for a guest can be supported
> on the host.
> 
> The validation is done using arm64_ftr_bits_kvm, which is created from
> arm64_ftr_regs, with some entries overwritten by entries from
> arm64_ftr_bits_kvm_override.
> 
> Signed-off-by: Reiji Watanabe <reijiw@google.com>
> ---
>  arch/arm64/include/asm/cpufeature.h |   1 +
>  arch/arm64/kernel/cpufeature.c      | 228 ++++++++++++++++++++++++++++
>  2 files changed, 229 insertions(+)
> 
> diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
> index ef6be92b1921..eda7ddbed8cf 100644
> --- a/arch/arm64/include/asm/cpufeature.h
> +++ b/arch/arm64/include/asm/cpufeature.h
> @@ -631,6 +631,7 @@ void check_local_cpu_capabilities(void);
>  
>  u64 read_sanitised_ftr_reg(u32 id);
>  u64 __read_sysreg_by_encoding(u32 sys_id);
> +int arm64_check_features(u32 sys_reg, u64 val, u64 limit);
>  
>  static inline bool cpu_supports_mixed_endian_el0(void)
>  {
> diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
> index 6f3e677d88f1..48dff8b101d9 100644
> --- a/arch/arm64/kernel/cpufeature.c
> +++ b/arch/arm64/kernel/cpufeature.c
> @@ -3140,3 +3140,231 @@ ssize_t cpu_show_meltdown(struct device *dev, struct device_attribute *attr,
>  		return sprintf(buf, "Vulnerable\n");
>  	}
>  }
> +
> +#ifdef CONFIG_KVM
> +/*
> + * arm64_ftr_bits_kvm[] is used for KVM to check if features that are
> + * indicated in an ID register value for the guest are available on the host.
> + * arm64_ftr_bits_kvm[] is created based on arm64_ftr_regs[].  But, for
> + * registers for which arm64_ftr_bits_kvm_override[] has a corresponding
> + * entry, replace arm64_ftr_bits entries in arm64_ftr_bits_kvm[] with the
> + * ones in arm64_ftr_bits_kvm_override[].
> + */
> +static struct __ftr_reg_bits_entry *arm64_ftr_bits_kvm;
> +static size_t arm64_ftr_bits_kvm_nentries;

I don't think this is really needed, as arm64_ftr_bits_kvm_override has
to have the same size as arm64_ftr_bits_kvm. You could use
ARRAY_SIZE(arm64_ftr_regs) like in get_arm64_ftr_reg_nowarn().

> +static DEFINE_MUTEX(arm64_ftr_bits_kvm_lock);
> +
> +/*
> + * Number of arm64_ftr_bits entries for each register.
> + * (Number of 4 bits fields in 64 bit register + 1 entry for ARM64_FTR_END)
> + */
> +#define	MAX_FTR_BITS_LEN	17
> +
> +/* Use FTR_LOWER_SAFE for AA64DFR0_EL1.PMUVER and AA64DFR0_EL1.DEBUGVER. */
> +static struct arm64_ftr_bits ftr_id_aa64dfr0_kvm[MAX_FTR_BITS_LEN] = {
> +	S_ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64DFR0_PMUVER_SHIFT, 4, 0),
> +	ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x6),
> +	ARM64_FTR_END,
> +};
> +
> +#define	ARM64_FTR_REG_BITS(id, table)	{	\
> +	.sys_id = id,				\
> +	.ftr_bits = &((table)[0]),		\
> +}
> +
> +struct __ftr_reg_bits_entry {
> +	u32	sys_id;
> +	struct arm64_ftr_bits	*ftr_bits;
> +};
> +
> +/*
> + * All entries in arm64_ftr_bits_kvm_override[] are used to override
> + * the corresponding entries in arm64_ftr_bits_kvm[].
> + */
> +static struct __ftr_reg_bits_entry arm64_ftr_bits_kvm_override[] = {
> +	ARM64_FTR_REG_BITS(SYS_ID_AA64DFR0_EL1, ftr_id_aa64dfr0_kvm),
> +};
> +
> +/*
> + * Override entries in @orig_ftrp with the ones in @new_ftrp when their shift
> + * fields match.  The last entry of @orig_ftrp and @new_ftrp must be
> + * ARM64_FTR_END (.width == 0).
> + */
> +static void arm64_ftr_reg_bits_overrite(struct arm64_ftr_bits *orig_ftrp,
> +					struct arm64_ftr_bits *new_ftrp)
> +{
> +	struct arm64_ftr_bits *o_ftrp, *n_ftrp;
> +
> +	for (n_ftrp = new_ftrp; n_ftrp->width; n_ftrp++) {
> +		for (o_ftrp = orig_ftrp; o_ftrp->width; o_ftrp++) {
> +			if (o_ftrp->shift == n_ftrp->shift) {
> +				*o_ftrp = *n_ftrp;
> +				break;
> +			}
> +		}
> +	}
> +}
> +
> +/*
> + * Copy arm64_ftr_bits entries from @src_ftrp to @dst_ftrp.  The last entries
> + * of @dst_ftrp and @src_ftrp must be ARM64_FTR_END (.width == 0).
> + */
> +static void copy_arm64_ftr_bits(struct arm64_ftr_bits *dst_ftrp,
> +				const struct arm64_ftr_bits *src_ftrp)
> +{
> +	int i = 0;
> +
> +	for (; src_ftrp[i].width; i++) {
> +		if (WARN_ON_ONCE(i >= (MAX_FTR_BITS_LEN - 1)))
> +			break;
> +
> +		dst_ftrp[i] = src_ftrp[i];
> +	}
> +
> +	dst_ftrp[i].width = 0;
> +}
> +
> +/*
> + * Initialize arm64_ftr_bits_kvm.  Copy arm64_ftr_bits for each ID register
> + * from arm64_ftr_regs to arm64_ftr_bits_kvm, and then override entries in
> + * arm64_ftr_bits_kvm with ones in arm64_ftr_bits_kvm_override.
> + */
> +static int init_arm64_ftr_bits_kvm(void)
> +{
> +	struct arm64_ftr_bits ftr_temp[MAX_FTR_BITS_LEN];
> +	static struct __ftr_reg_bits_entry *reg_bits_array, *bits, *o_bits;
> +	int i, j, nent, ret;
> +
> +	mutex_lock(&arm64_ftr_bits_kvm_lock);

This is initialized lazily, whenever KVM calls arm64_check_features(). I
guess that's why it needs the lock (and possibly a barrier as you
mentoin in your reply). Would it be possible to simplify things by
initializing arm64_ftr_bits_kvm somewhere at boot time (in
init_cpu_features maybe?)?

> +	if (arm64_ftr_bits_kvm) {
> +		/* Already initialized */
> +		ret = 0;
> +		goto unlock_exit;
> +	}
> +
> +	nent = ARRAY_SIZE(arm64_ftr_regs);
> +	reg_bits_array = kcalloc(nent, sizeof(struct __ftr_reg_bits_entry),
> +				 GFP_KERNEL);
> +	if (!reg_bits_array) {
> +		ret = ENOMEM;
> +		goto unlock_exit;
> +	}
> +
> +	/* Copy entries from arm64_ftr_regs to reg_bits_array */
> +	for (i = 0; i < nent; i++) {
> +		bits = &reg_bits_array[i];
> +		bits->sys_id = arm64_ftr_regs[i].sys_id;
> +		bits->ftr_bits = (struct arm64_ftr_bits *)arm64_ftr_regs[i].reg->ftr_bits;
> +	};
> +
> +	/*
> +	 * Override the entries in reg_bits_array with the ones in
> +	 * arm64_ftr_bits_kvm_override.
> +	 */
> +	for (i = 0; i < ARRAY_SIZE(arm64_ftr_bits_kvm_override); i++) {
> +		o_bits = &arm64_ftr_bits_kvm_override[i];
> +		for (j = 0; j < nent; j++) {
> +			bits = &reg_bits_array[j];
> +			if (bits->sys_id != o_bits->sys_id)
> +				continue;
> +
> +			memset(ftr_temp, 0, sizeof(ftr_temp));
> +
> +			/*
> +			 * Temporary save all entries in o_bits->ftr_bits
> +			 * to ftr_temp.
> +			 */
> +			copy_arm64_ftr_bits(ftr_temp, o_bits->ftr_bits);
> +
> +			/*
> +			 * Copy entries from bits->ftr_bits to o_bits->ftr_bits.
> +			 */
> +			copy_arm64_ftr_bits(o_bits->ftr_bits, bits->ftr_bits);
> +
> +			/*
> +			 * Override entries in o_bits->ftr_bits with the
> +			 * saved ones, and update bits->ftr_bits with
> +			 * o_bits->ftr_bits.
> +			 */
> +			arm64_ftr_reg_bits_overrite(o_bits->ftr_bits, ftr_temp);
> +			bits->ftr_bits = o_bits->ftr_bits;
> +			break;
> +		}
> +	}
> +
> +	arm64_ftr_bits_kvm_nentries = nent;
> +	arm64_ftr_bits_kvm = reg_bits_array;
> +	ret = 0;
> +
> +unlock_exit:
> +	mutex_unlock(&arm64_ftr_bits_kvm_lock);
> +	return ret;
> +}
> +
> +static int search_cmp_ftr_reg_bits(const void *id, const void *regp)
> +{
> +	return ((int)(unsigned long)id -
> +		(int)((const struct __ftr_reg_bits_entry *)regp)->sys_id);
> +}
> +
> +static const struct arm64_ftr_bits *get_arm64_ftr_bits_kvm(u32 sys_id)
> +{
> +	const struct __ftr_reg_bits_entry *ret;
> +	int err;
> +
> +	if (!arm64_ftr_bits_kvm) {
> +		/* arm64_ftr_bits_kvm is not initialized yet. */
> +		err = init_arm64_ftr_bits_kvm();
> +		if (err)
> +			return NULL;
> +	}
> +
> +	ret = bsearch((const void *)(unsigned long)sys_id,
> +		      arm64_ftr_bits_kvm,
> +		      arm64_ftr_bits_kvm_nentries,
> +		      sizeof(arm64_ftr_bits_kvm[0]),
> +		      search_cmp_ftr_reg_bits);
> +	if (ret)
> +		return ret->ftr_bits;
> +
> +	return NULL;
> +}
> +
> +/*
> + * Check if features (or levels of features) that are indicated in the ID
> + * register value @val are also indicated in @limit.
> + * This function is for KVM to check if features that are indicated in @val,
> + * which will be used as the ID register value for its guest, are supported
> + * on the host.
> + * For AA64MMFR0_EL1.TGranX_2 fields, which don't follow the standard ID
> + * scheme, the function checks if values of the fields in @val are the same
> + * as the ones in @limit.
> + */
> +int arm64_check_features(u32 sys_reg, u64 val, u64 limit)
> +{
> +	const struct arm64_ftr_bits *ftrp = get_arm64_ftr_bits_kvm(sys_reg);

Given that this is to be used only by KVM (and it's inside CONFIG_KVM),
it might be better to have "kvm" somewhere in its name.

> +	u64 exposed_mask = 0;
> +
> +	if (!ftrp)
> +		return -ENOENT;
> +
> +	for (; ftrp->width; ftrp++) {
> +		s64 ftr_val = arm64_ftr_value(ftrp, val);
> +		s64 ftr_lim = arm64_ftr_value(ftrp, limit);
> +
> +		exposed_mask |= arm64_ftr_mask(ftrp);
> +
> +		if (ftr_val == ftr_lim)
> +			continue;
> +
> +		if (ftr_val != arm64_ftr_safe_value(ftrp, ftr_val, ftr_lim))
> +			return -E2BIG;
> +	}
> +
> +	/* Make sure that no unrecognized fields are set in @val. */
> +	if (val & ~exposed_mask)
> +		return -E2BIG;
> +
> +	return 0;
> +}
> +#endif /* CONFIG_KVM */
> -- 
> 2.34.1.448.ga2b2bfdf31-goog
> 

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 02/26] KVM: arm64: Save ID registers' sanitized value per guest
  2022-01-06  4:26   ` Reiji Watanabe
  (?)
@ 2022-01-26  5:22     ` Ricardo Koller
  -1 siblings, 0 replies; 201+ messages in thread
From: Ricardo Koller @ 2022-01-26  5:22 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: Marc Zyngier, kvmarm, kvm, linux-arm-kernel, James Morse,
	Alexandru Elisei, Suzuki K Poulose, Paolo Bonzini, Will Deacon,
	Andrew Jones, Peng Liang, Peter Shier, Oliver Upton, Jing Zhang,
	Raghavendra Rao Anata

On Wed, Jan 05, 2022 at 08:26:44PM -0800, Reiji Watanabe wrote:
> Introduce id_regs[] in kvm_arch as a storage of guest's ID registers,
> and save ID registers' sanitized value in the array at KVM_CREATE_VM.
> Use the saved ones when ID registers are read by the guest or
> userspace (via KVM_GET_ONE_REG).
> 
> Signed-off-by: Reiji Watanabe <reijiw@google.com>
> ---
>  arch/arm64/include/asm/kvm_host.h | 16 ++++++++
>  arch/arm64/kvm/arm.c              |  1 +
>  arch/arm64/kvm/sys_regs.c         | 62 ++++++++++++++++++++++---------
>  3 files changed, 62 insertions(+), 17 deletions(-)
> 
> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index 2a5f7f38006f..c789a0137f58 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -102,6 +102,17 @@ struct kvm_s2_mmu {
>  struct kvm_arch_memory_slot {
>  };
>  
> +/*
> + * (Op0, Op1, CRn, CRm, Op2) of ID registers is (3, 0, 0, crm, op2),
> + * where 0<=crm<8, 0<=op2<8.

Is this observation based on this table?

Table D12-2 System instruction encodings for non-Debug System register accesses

in that case, it seems that the ID registers list might grow after
crm=7, and as CRm has 4 bits, why not 16*8=128?

> + */
> +#define KVM_ARM_ID_REG_MAX_NUM	64
> +#define IDREG_IDX(id)		((sys_reg_CRm(id) << 3) | sys_reg_Op2(id))
> +#define is_id_reg(id)	\
> +	(sys_reg_Op0(id) == 3 && sys_reg_Op1(id) == 0 &&	\
> +	 sys_reg_CRn(id) == 0 && sys_reg_CRm(id) >= 0 &&	\
> +	 sys_reg_CRm(id) < 8)
> +
>  struct kvm_arch {
>  	struct kvm_s2_mmu mmu;
>  
> @@ -137,6 +148,9 @@ struct kvm_arch {
>  
>  	/* Memory Tagging Extension enabled for the guest */
>  	bool mte_enabled;
> +
> +	/* ID registers for the guest. */
> +	u64 id_regs[KVM_ARM_ID_REG_MAX_NUM];
>  };
>  
>  struct kvm_vcpu_fault_info {
> @@ -734,6 +748,8 @@ int kvm_arm_vcpu_arch_has_attr(struct kvm_vcpu *vcpu,
>  long kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
>  				struct kvm_arm_copy_mte_tags *copy_tags);
>  
> +void set_default_id_regs(struct kvm *kvm);
> +
>  /* Guest/host FPSIMD coordination helpers */
>  int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu);
>  void kvm_arch_vcpu_load_fp(struct kvm_vcpu *vcpu);
> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> index e4727dc771bf..5f497a0af254 100644
> --- a/arch/arm64/kvm/arm.c
> +++ b/arch/arm64/kvm/arm.c
> @@ -156,6 +156,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
>  	kvm->arch.max_vcpus = kvm_arm_default_max_vcpus();
>  
>  	set_default_spectre(kvm);
> +	set_default_id_regs(kvm);
>  
>  	return ret;
>  out_free_stage2_pgd:
> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> index e3ec1a44f94d..80dc62f98ef0 100644
> --- a/arch/arm64/kvm/sys_regs.c
> +++ b/arch/arm64/kvm/sys_regs.c
> @@ -33,6 +33,8 @@
>  
>  #include "trace.h"
>  
> +static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id);
> +
>  /*
>   * All of this file is extremely similar to the ARM coproc.c, but the
>   * types are different. My gut feeling is that it should be pretty
> @@ -273,7 +275,7 @@ static bool trap_loregion(struct kvm_vcpu *vcpu,
>  			  struct sys_reg_params *p,
>  			  const struct sys_reg_desc *r)
>  {
> -	u64 val = read_sanitised_ftr_reg(SYS_ID_AA64MMFR1_EL1);
> +	u64 val = __read_id_reg(vcpu, SYS_ID_AA64MMFR1_EL1);
>  	u32 sr = reg_to_encoding(r);
>  
>  	if (!(val & (0xfUL << ID_AA64MMFR1_LOR_SHIFT))) {
> @@ -1059,17 +1061,9 @@ static bool access_arch_timer(struct kvm_vcpu *vcpu,
>  	return true;
>  }
>  
> -/* Read a sanitised cpufeature ID register by sys_reg_desc */
> -static u64 read_id_reg(const struct kvm_vcpu *vcpu,
> -		struct sys_reg_desc const *r, bool raz)
> +static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
>  {
> -	u32 id = reg_to_encoding(r);
> -	u64 val;
> -
> -	if (raz)
> -		return 0;
> -
> -	val = read_sanitised_ftr_reg(id);
> +	u64 val = vcpu->kvm->arch.id_regs[IDREG_IDX(id)];
>  
>  	switch (id) {
>  	case SYS_ID_AA64PFR0_EL1:
> @@ -1119,6 +1113,14 @@ static u64 read_id_reg(const struct kvm_vcpu *vcpu,
>  	return val;
>  }
>  
> +static u64 read_id_reg(const struct kvm_vcpu *vcpu,
> +		       struct sys_reg_desc const *r, bool raz)
> +{
> +	u32 id = reg_to_encoding(r);
> +
> +	return raz ? 0 : __read_id_reg(vcpu, id);
> +}
> +
>  static unsigned int id_visibility(const struct kvm_vcpu *vcpu,
>  				  const struct sys_reg_desc *r)
>  {
> @@ -1223,9 +1225,8 @@ static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
>  /*
>   * cpufeature ID register user accessors
>   *
> - * For now, these registers are immutable for userspace, so no values
> - * are stored, and for set_id_reg() we don't allow the effective value
> - * to be changed.
> + * For now, these registers are immutable for userspace, so for set_id_reg()
> + * we don't allow the effective value to be changed.
>   */
>  static int __get_id_reg(const struct kvm_vcpu *vcpu,
>  			const struct sys_reg_desc *rd, void __user *uaddr,
> @@ -1237,7 +1238,7 @@ static int __get_id_reg(const struct kvm_vcpu *vcpu,
>  	return reg_to_user(uaddr, &val, id);
>  }
>  
> -static int __set_id_reg(const struct kvm_vcpu *vcpu,
> +static int __set_id_reg(struct kvm_vcpu *vcpu,
>  			const struct sys_reg_desc *rd, void __user *uaddr,
>  			bool raz)
>  {
> @@ -1837,8 +1838,8 @@ static bool trap_dbgdidr(struct kvm_vcpu *vcpu,
>  	if (p->is_write) {
>  		return ignore_write(vcpu, p);
>  	} else {
> -		u64 dfr = read_sanitised_ftr_reg(SYS_ID_AA64DFR0_EL1);
> -		u64 pfr = read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1);
> +		u64 dfr = __read_id_reg(vcpu, SYS_ID_AA64DFR0_EL1);
> +		u64 pfr = __read_id_reg(vcpu, SYS_ID_AA64PFR0_EL1);
>  		u32 el3 = !!cpuid_feature_extract_unsigned_field(pfr, ID_AA64PFR0_EL3_SHIFT);
>  
>  		p->regval = ((((dfr >> ID_AA64DFR0_WRPS_SHIFT) & 0xf) << 28) |
> @@ -2850,3 +2851,30 @@ void kvm_sys_reg_table_init(void)
>  	/* Clear all higher bits. */
>  	cache_levels &= (1 << (i*3))-1;
>  }
> +
> +/*
> + * Set the guest's ID registers that are defined in sys_reg_descs[]
> + * with ID_SANITISED() to the host's sanitized value.
> + */
> +void set_default_id_regs(struct kvm *kvm)
> +{
> +	int i;
> +	u32 id;
> +	const struct sys_reg_desc *rd;
> +	u64 val;
> +
> +	for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
> +		rd = &sys_reg_descs[i];
> +		if (rd->access != access_id_reg)
> +			/* Not ID register, or hidden/reserved ID register */
> +			continue;
> +
> +		id = reg_to_encoding(rd);
> +		if (WARN_ON_ONCE(!is_id_reg(id)))
> +			/* Shouldn't happen */
> +			continue;
> +
> +		val = read_sanitised_ftr_reg(id);

I'm a bit confused. Shouldn't the default+sanitized values already use
arm64_ftr_bits_kvm (instead of arm64_ftr_regs)?

> +		kvm->arch.id_regs[IDREG_IDX(id)] = val;
> +	}
> +}
> -- 
> 2.34.1.448.ga2b2bfdf31-goog
> 

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 02/26] KVM: arm64: Save ID registers' sanitized value per guest
@ 2022-01-26  5:22     ` Ricardo Koller
  0 siblings, 0 replies; 201+ messages in thread
From: Ricardo Koller @ 2022-01-26  5:22 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: kvm, Marc Zyngier, Peter Shier, Will Deacon, Paolo Bonzini,
	kvmarm, linux-arm-kernel

On Wed, Jan 05, 2022 at 08:26:44PM -0800, Reiji Watanabe wrote:
> Introduce id_regs[] in kvm_arch as a storage of guest's ID registers,
> and save ID registers' sanitized value in the array at KVM_CREATE_VM.
> Use the saved ones when ID registers are read by the guest or
> userspace (via KVM_GET_ONE_REG).
> 
> Signed-off-by: Reiji Watanabe <reijiw@google.com>
> ---
>  arch/arm64/include/asm/kvm_host.h | 16 ++++++++
>  arch/arm64/kvm/arm.c              |  1 +
>  arch/arm64/kvm/sys_regs.c         | 62 ++++++++++++++++++++++---------
>  3 files changed, 62 insertions(+), 17 deletions(-)
> 
> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index 2a5f7f38006f..c789a0137f58 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -102,6 +102,17 @@ struct kvm_s2_mmu {
>  struct kvm_arch_memory_slot {
>  };
>  
> +/*
> + * (Op0, Op1, CRn, CRm, Op2) of ID registers is (3, 0, 0, crm, op2),
> + * where 0<=crm<8, 0<=op2<8.

Is this observation based on this table?

Table D12-2 System instruction encodings for non-Debug System register accesses

in that case, it seems that the ID registers list might grow after
crm=7, and as CRm has 4 bits, why not 16*8=128?

> + */
> +#define KVM_ARM_ID_REG_MAX_NUM	64
> +#define IDREG_IDX(id)		((sys_reg_CRm(id) << 3) | sys_reg_Op2(id))
> +#define is_id_reg(id)	\
> +	(sys_reg_Op0(id) == 3 && sys_reg_Op1(id) == 0 &&	\
> +	 sys_reg_CRn(id) == 0 && sys_reg_CRm(id) >= 0 &&	\
> +	 sys_reg_CRm(id) < 8)
> +
>  struct kvm_arch {
>  	struct kvm_s2_mmu mmu;
>  
> @@ -137,6 +148,9 @@ struct kvm_arch {
>  
>  	/* Memory Tagging Extension enabled for the guest */
>  	bool mte_enabled;
> +
> +	/* ID registers for the guest. */
> +	u64 id_regs[KVM_ARM_ID_REG_MAX_NUM];
>  };
>  
>  struct kvm_vcpu_fault_info {
> @@ -734,6 +748,8 @@ int kvm_arm_vcpu_arch_has_attr(struct kvm_vcpu *vcpu,
>  long kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
>  				struct kvm_arm_copy_mte_tags *copy_tags);
>  
> +void set_default_id_regs(struct kvm *kvm);
> +
>  /* Guest/host FPSIMD coordination helpers */
>  int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu);
>  void kvm_arch_vcpu_load_fp(struct kvm_vcpu *vcpu);
> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> index e4727dc771bf..5f497a0af254 100644
> --- a/arch/arm64/kvm/arm.c
> +++ b/arch/arm64/kvm/arm.c
> @@ -156,6 +156,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
>  	kvm->arch.max_vcpus = kvm_arm_default_max_vcpus();
>  
>  	set_default_spectre(kvm);
> +	set_default_id_regs(kvm);
>  
>  	return ret;
>  out_free_stage2_pgd:
> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> index e3ec1a44f94d..80dc62f98ef0 100644
> --- a/arch/arm64/kvm/sys_regs.c
> +++ b/arch/arm64/kvm/sys_regs.c
> @@ -33,6 +33,8 @@
>  
>  #include "trace.h"
>  
> +static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id);
> +
>  /*
>   * All of this file is extremely similar to the ARM coproc.c, but the
>   * types are different. My gut feeling is that it should be pretty
> @@ -273,7 +275,7 @@ static bool trap_loregion(struct kvm_vcpu *vcpu,
>  			  struct sys_reg_params *p,
>  			  const struct sys_reg_desc *r)
>  {
> -	u64 val = read_sanitised_ftr_reg(SYS_ID_AA64MMFR1_EL1);
> +	u64 val = __read_id_reg(vcpu, SYS_ID_AA64MMFR1_EL1);
>  	u32 sr = reg_to_encoding(r);
>  
>  	if (!(val & (0xfUL << ID_AA64MMFR1_LOR_SHIFT))) {
> @@ -1059,17 +1061,9 @@ static bool access_arch_timer(struct kvm_vcpu *vcpu,
>  	return true;
>  }
>  
> -/* Read a sanitised cpufeature ID register by sys_reg_desc */
> -static u64 read_id_reg(const struct kvm_vcpu *vcpu,
> -		struct sys_reg_desc const *r, bool raz)
> +static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
>  {
> -	u32 id = reg_to_encoding(r);
> -	u64 val;
> -
> -	if (raz)
> -		return 0;
> -
> -	val = read_sanitised_ftr_reg(id);
> +	u64 val = vcpu->kvm->arch.id_regs[IDREG_IDX(id)];
>  
>  	switch (id) {
>  	case SYS_ID_AA64PFR0_EL1:
> @@ -1119,6 +1113,14 @@ static u64 read_id_reg(const struct kvm_vcpu *vcpu,
>  	return val;
>  }
>  
> +static u64 read_id_reg(const struct kvm_vcpu *vcpu,
> +		       struct sys_reg_desc const *r, bool raz)
> +{
> +	u32 id = reg_to_encoding(r);
> +
> +	return raz ? 0 : __read_id_reg(vcpu, id);
> +}
> +
>  static unsigned int id_visibility(const struct kvm_vcpu *vcpu,
>  				  const struct sys_reg_desc *r)
>  {
> @@ -1223,9 +1225,8 @@ static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
>  /*
>   * cpufeature ID register user accessors
>   *
> - * For now, these registers are immutable for userspace, so no values
> - * are stored, and for set_id_reg() we don't allow the effective value
> - * to be changed.
> + * For now, these registers are immutable for userspace, so for set_id_reg()
> + * we don't allow the effective value to be changed.
>   */
>  static int __get_id_reg(const struct kvm_vcpu *vcpu,
>  			const struct sys_reg_desc *rd, void __user *uaddr,
> @@ -1237,7 +1238,7 @@ static int __get_id_reg(const struct kvm_vcpu *vcpu,
>  	return reg_to_user(uaddr, &val, id);
>  }
>  
> -static int __set_id_reg(const struct kvm_vcpu *vcpu,
> +static int __set_id_reg(struct kvm_vcpu *vcpu,
>  			const struct sys_reg_desc *rd, void __user *uaddr,
>  			bool raz)
>  {
> @@ -1837,8 +1838,8 @@ static bool trap_dbgdidr(struct kvm_vcpu *vcpu,
>  	if (p->is_write) {
>  		return ignore_write(vcpu, p);
>  	} else {
> -		u64 dfr = read_sanitised_ftr_reg(SYS_ID_AA64DFR0_EL1);
> -		u64 pfr = read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1);
> +		u64 dfr = __read_id_reg(vcpu, SYS_ID_AA64DFR0_EL1);
> +		u64 pfr = __read_id_reg(vcpu, SYS_ID_AA64PFR0_EL1);
>  		u32 el3 = !!cpuid_feature_extract_unsigned_field(pfr, ID_AA64PFR0_EL3_SHIFT);
>  
>  		p->regval = ((((dfr >> ID_AA64DFR0_WRPS_SHIFT) & 0xf) << 28) |
> @@ -2850,3 +2851,30 @@ void kvm_sys_reg_table_init(void)
>  	/* Clear all higher bits. */
>  	cache_levels &= (1 << (i*3))-1;
>  }
> +
> +/*
> + * Set the guest's ID registers that are defined in sys_reg_descs[]
> + * with ID_SANITISED() to the host's sanitized value.
> + */
> +void set_default_id_regs(struct kvm *kvm)
> +{
> +	int i;
> +	u32 id;
> +	const struct sys_reg_desc *rd;
> +	u64 val;
> +
> +	for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
> +		rd = &sys_reg_descs[i];
> +		if (rd->access != access_id_reg)
> +			/* Not ID register, or hidden/reserved ID register */
> +			continue;
> +
> +		id = reg_to_encoding(rd);
> +		if (WARN_ON_ONCE(!is_id_reg(id)))
> +			/* Shouldn't happen */
> +			continue;
> +
> +		val = read_sanitised_ftr_reg(id);

I'm a bit confused. Shouldn't the default+sanitized values already use
arm64_ftr_bits_kvm (instead of arm64_ftr_regs)?

> +		kvm->arch.id_regs[IDREG_IDX(id)] = val;
> +	}
> +}
> -- 
> 2.34.1.448.ga2b2bfdf31-goog
> 
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 02/26] KVM: arm64: Save ID registers' sanitized value per guest
@ 2022-01-26  5:22     ` Ricardo Koller
  0 siblings, 0 replies; 201+ messages in thread
From: Ricardo Koller @ 2022-01-26  5:22 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: Marc Zyngier, kvmarm, kvm, linux-arm-kernel, James Morse,
	Alexandru Elisei, Suzuki K Poulose, Paolo Bonzini, Will Deacon,
	Andrew Jones, Peng Liang, Peter Shier, Oliver Upton, Jing Zhang,
	Raghavendra Rao Anata

On Wed, Jan 05, 2022 at 08:26:44PM -0800, Reiji Watanabe wrote:
> Introduce id_regs[] in kvm_arch as a storage of guest's ID registers,
> and save ID registers' sanitized value in the array at KVM_CREATE_VM.
> Use the saved ones when ID registers are read by the guest or
> userspace (via KVM_GET_ONE_REG).
> 
> Signed-off-by: Reiji Watanabe <reijiw@google.com>
> ---
>  arch/arm64/include/asm/kvm_host.h | 16 ++++++++
>  arch/arm64/kvm/arm.c              |  1 +
>  arch/arm64/kvm/sys_regs.c         | 62 ++++++++++++++++++++++---------
>  3 files changed, 62 insertions(+), 17 deletions(-)
> 
> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index 2a5f7f38006f..c789a0137f58 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -102,6 +102,17 @@ struct kvm_s2_mmu {
>  struct kvm_arch_memory_slot {
>  };
>  
> +/*
> + * (Op0, Op1, CRn, CRm, Op2) of ID registers is (3, 0, 0, crm, op2),
> + * where 0<=crm<8, 0<=op2<8.

Is this observation based on this table?

Table D12-2 System instruction encodings for non-Debug System register accesses

in that case, it seems that the ID registers list might grow after
crm=7, and as CRm has 4 bits, why not 16*8=128?

> + */
> +#define KVM_ARM_ID_REG_MAX_NUM	64
> +#define IDREG_IDX(id)		((sys_reg_CRm(id) << 3) | sys_reg_Op2(id))
> +#define is_id_reg(id)	\
> +	(sys_reg_Op0(id) == 3 && sys_reg_Op1(id) == 0 &&	\
> +	 sys_reg_CRn(id) == 0 && sys_reg_CRm(id) >= 0 &&	\
> +	 sys_reg_CRm(id) < 8)
> +
>  struct kvm_arch {
>  	struct kvm_s2_mmu mmu;
>  
> @@ -137,6 +148,9 @@ struct kvm_arch {
>  
>  	/* Memory Tagging Extension enabled for the guest */
>  	bool mte_enabled;
> +
> +	/* ID registers for the guest. */
> +	u64 id_regs[KVM_ARM_ID_REG_MAX_NUM];
>  };
>  
>  struct kvm_vcpu_fault_info {
> @@ -734,6 +748,8 @@ int kvm_arm_vcpu_arch_has_attr(struct kvm_vcpu *vcpu,
>  long kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
>  				struct kvm_arm_copy_mte_tags *copy_tags);
>  
> +void set_default_id_regs(struct kvm *kvm);
> +
>  /* Guest/host FPSIMD coordination helpers */
>  int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu);
>  void kvm_arch_vcpu_load_fp(struct kvm_vcpu *vcpu);
> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> index e4727dc771bf..5f497a0af254 100644
> --- a/arch/arm64/kvm/arm.c
> +++ b/arch/arm64/kvm/arm.c
> @@ -156,6 +156,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
>  	kvm->arch.max_vcpus = kvm_arm_default_max_vcpus();
>  
>  	set_default_spectre(kvm);
> +	set_default_id_regs(kvm);
>  
>  	return ret;
>  out_free_stage2_pgd:
> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> index e3ec1a44f94d..80dc62f98ef0 100644
> --- a/arch/arm64/kvm/sys_regs.c
> +++ b/arch/arm64/kvm/sys_regs.c
> @@ -33,6 +33,8 @@
>  
>  #include "trace.h"
>  
> +static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id);
> +
>  /*
>   * All of this file is extremely similar to the ARM coproc.c, but the
>   * types are different. My gut feeling is that it should be pretty
> @@ -273,7 +275,7 @@ static bool trap_loregion(struct kvm_vcpu *vcpu,
>  			  struct sys_reg_params *p,
>  			  const struct sys_reg_desc *r)
>  {
> -	u64 val = read_sanitised_ftr_reg(SYS_ID_AA64MMFR1_EL1);
> +	u64 val = __read_id_reg(vcpu, SYS_ID_AA64MMFR1_EL1);
>  	u32 sr = reg_to_encoding(r);
>  
>  	if (!(val & (0xfUL << ID_AA64MMFR1_LOR_SHIFT))) {
> @@ -1059,17 +1061,9 @@ static bool access_arch_timer(struct kvm_vcpu *vcpu,
>  	return true;
>  }
>  
> -/* Read a sanitised cpufeature ID register by sys_reg_desc */
> -static u64 read_id_reg(const struct kvm_vcpu *vcpu,
> -		struct sys_reg_desc const *r, bool raz)
> +static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
>  {
> -	u32 id = reg_to_encoding(r);
> -	u64 val;
> -
> -	if (raz)
> -		return 0;
> -
> -	val = read_sanitised_ftr_reg(id);
> +	u64 val = vcpu->kvm->arch.id_regs[IDREG_IDX(id)];
>  
>  	switch (id) {
>  	case SYS_ID_AA64PFR0_EL1:
> @@ -1119,6 +1113,14 @@ static u64 read_id_reg(const struct kvm_vcpu *vcpu,
>  	return val;
>  }
>  
> +static u64 read_id_reg(const struct kvm_vcpu *vcpu,
> +		       struct sys_reg_desc const *r, bool raz)
> +{
> +	u32 id = reg_to_encoding(r);
> +
> +	return raz ? 0 : __read_id_reg(vcpu, id);
> +}
> +
>  static unsigned int id_visibility(const struct kvm_vcpu *vcpu,
>  				  const struct sys_reg_desc *r)
>  {
> @@ -1223,9 +1225,8 @@ static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
>  /*
>   * cpufeature ID register user accessors
>   *
> - * For now, these registers are immutable for userspace, so no values
> - * are stored, and for set_id_reg() we don't allow the effective value
> - * to be changed.
> + * For now, these registers are immutable for userspace, so for set_id_reg()
> + * we don't allow the effective value to be changed.
>   */
>  static int __get_id_reg(const struct kvm_vcpu *vcpu,
>  			const struct sys_reg_desc *rd, void __user *uaddr,
> @@ -1237,7 +1238,7 @@ static int __get_id_reg(const struct kvm_vcpu *vcpu,
>  	return reg_to_user(uaddr, &val, id);
>  }
>  
> -static int __set_id_reg(const struct kvm_vcpu *vcpu,
> +static int __set_id_reg(struct kvm_vcpu *vcpu,
>  			const struct sys_reg_desc *rd, void __user *uaddr,
>  			bool raz)
>  {
> @@ -1837,8 +1838,8 @@ static bool trap_dbgdidr(struct kvm_vcpu *vcpu,
>  	if (p->is_write) {
>  		return ignore_write(vcpu, p);
>  	} else {
> -		u64 dfr = read_sanitised_ftr_reg(SYS_ID_AA64DFR0_EL1);
> -		u64 pfr = read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1);
> +		u64 dfr = __read_id_reg(vcpu, SYS_ID_AA64DFR0_EL1);
> +		u64 pfr = __read_id_reg(vcpu, SYS_ID_AA64PFR0_EL1);
>  		u32 el3 = !!cpuid_feature_extract_unsigned_field(pfr, ID_AA64PFR0_EL3_SHIFT);
>  
>  		p->regval = ((((dfr >> ID_AA64DFR0_WRPS_SHIFT) & 0xf) << 28) |
> @@ -2850,3 +2851,30 @@ void kvm_sys_reg_table_init(void)
>  	/* Clear all higher bits. */
>  	cache_levels &= (1 << (i*3))-1;
>  }
> +
> +/*
> + * Set the guest's ID registers that are defined in sys_reg_descs[]
> + * with ID_SANITISED() to the host's sanitized value.
> + */
> +void set_default_id_regs(struct kvm *kvm)
> +{
> +	int i;
> +	u32 id;
> +	const struct sys_reg_desc *rd;
> +	u64 val;
> +
> +	for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
> +		rd = &sys_reg_descs[i];
> +		if (rd->access != access_id_reg)
> +			/* Not ID register, or hidden/reserved ID register */
> +			continue;
> +
> +		id = reg_to_encoding(rd);
> +		if (WARN_ON_ONCE(!is_id_reg(id)))
> +			/* Shouldn't happen */
> +			continue;
> +
> +		val = read_sanitised_ftr_reg(id);

I'm a bit confused. Shouldn't the default+sanitized values already use
arm64_ftr_bits_kvm (instead of arm64_ftr_regs)?

> +		kvm->arch.id_regs[IDREG_IDX(id)] = val;
> +	}
> +}
> -- 
> 2.34.1.448.ga2b2bfdf31-goog
> 

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 01/26] KVM: arm64: Introduce a validation function for an ID register
  2022-01-24 16:20     ` Fuad Tabba
  (?)
@ 2022-01-26  6:04       ` Reiji Watanabe
  -1 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-26  6:04 UTC (permalink / raw)
  To: Fuad Tabba
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, Linux ARM

Hi Fuad,

> > +/*
> > + * Override entries in @orig_ftrp with the ones in @new_ftrp when their shift
> > + * fields match.  The last entry of @orig_ftrp and @new_ftrp must be
> > + * ARM64_FTR_END (.width == 0).
> > + */
> > +static void arm64_ftr_reg_bits_overrite(struct arm64_ftr_bits *orig_ftrp,
>
> s/overrite/override

Thank you for catching this. I will fix it.


> > +                                       struct arm64_ftr_bits *new_ftrp)
>
> Should this be const struct arm64_ftr_bits *new_ftrp, which would also
> make it consistent with copy_arm64_ftr_bits()?

Yes, I will make new_ftrp const.

>
> > +{
> > +       struct arm64_ftr_bits *o_ftrp, *n_ftrp;
> > +
> > +       for (n_ftrp = new_ftrp; n_ftrp->width; n_ftrp++) {
> > +               for (o_ftrp = orig_ftrp; o_ftrp->width; o_ftrp++) {
> > +                       if (o_ftrp->shift == n_ftrp->shift) {
> > +                               *o_ftrp = *n_ftrp;
> > +                               break;
> > +                       }
> > +               }
> > +       }
> > +}
> > +
>
> ...
>
> > +/*
> > + * Initialize arm64_ftr_bits_kvm.  Copy arm64_ftr_bits for each ID register
> > + * from arm64_ftr_regs to arm64_ftr_bits_kvm, and then override entries in
> > + * arm64_ftr_bits_kvm with ones in arm64_ftr_bits_kvm_override.
> > + */
> > +static int init_arm64_ftr_bits_kvm(void)
> > +{
> > +       struct arm64_ftr_bits ftr_temp[MAX_FTR_BITS_LEN];
> > +       static struct __ftr_reg_bits_entry *reg_bits_array, *bits, *o_bits;
> > +       int i, j, nent, ret;
> > +
> > +       mutex_lock(&arm64_ftr_bits_kvm_lock);
> > +       if (arm64_ftr_bits_kvm) {
> > +               /* Already initialized */
> > +               ret = 0;
> > +               goto unlock_exit;
> > +       }
> > +
> > +       nent = ARRAY_SIZE(arm64_ftr_regs);
> > +       reg_bits_array = kcalloc(nent, sizeof(struct __ftr_reg_bits_entry),
> > +                                GFP_KERNEL);
> > +       if (!reg_bits_array) {
> > +               ret = ENOMEM;
>
> Should this be -ENOMEM?

Yes, I will fix it.


> > +               goto unlock_exit;
> > +       }
> > +
> > +       /* Copy entries from arm64_ftr_regs to reg_bits_array */
> > +       for (i = 0; i < nent; i++) {
> > +               bits = &reg_bits_array[i];
> > +               bits->sys_id = arm64_ftr_regs[i].sys_id;
> > +               bits->ftr_bits = (struct arm64_ftr_bits *)arm64_ftr_regs[i].reg->ftr_bits;
> > +       };
> > +
> > +       /*
> > +        * Override the entries in reg_bits_array with the ones in
> > +        * arm64_ftr_bits_kvm_override.
> > +        */
> > +       for (i = 0; i < ARRAY_SIZE(arm64_ftr_bits_kvm_override); i++) {
> > +               o_bits = &arm64_ftr_bits_kvm_override[i];
> > +               for (j = 0; j < nent; j++) {
> > +                       bits = &reg_bits_array[j];
> > +                       if (bits->sys_id != o_bits->sys_id)
> > +                               continue;
> > +
> > +                       memset(ftr_temp, 0, sizeof(ftr_temp));
> > +
> > +                       /*
> > +                        * Temporary save all entries in o_bits->ftr_bits
> > +                        * to ftr_temp.
> > +                        */
> > +                       copy_arm64_ftr_bits(ftr_temp, o_bits->ftr_bits);
> > +
> > +                       /*
> > +                        * Copy entries from bits->ftr_bits to o_bits->ftr_bits.
> > +                        */
> > +                       copy_arm64_ftr_bits(o_bits->ftr_bits, bits->ftr_bits);
> > +
> > +                       /*
> > +                        * Override entries in o_bits->ftr_bits with the
> > +                        * saved ones, and update bits->ftr_bits with
> > +                        * o_bits->ftr_bits.
> > +                        */
> > +                       arm64_ftr_reg_bits_overrite(o_bits->ftr_bits, ftr_temp);
> > +                       bits->ftr_bits = o_bits->ftr_bits;
> > +                       break;
> > +               }
> > +       }
>
> Could you please explain using ftr_temp[] and changing the value in
> arm64_ftr_bits_kvm_override, rather than just
> arm64_ftr_reg_bits_overrite(bits->ftr_bits, o_bits->ftr_bits)?

I would like to maintain the order of the entries in the original
ftr_bits so that (future) functions that work for the original ones
also work for the KVM's.
The copy and override is an easy way to do that.  The same thing can
be done without ftr_temp[], but it would look a bit tricky.

If we assume the order shouldn't matter or entries in ftr_bits
are always in descending order, just changing the value in
arm64_ftr_bits_kvm_override would be a much simpler way though.


>
>
> > +static const struct arm64_ftr_bits *get_arm64_ftr_bits_kvm(u32 sys_id)
> > +{
> > +       const struct __ftr_reg_bits_entry *ret;
> > +       int err;
> > +
> > +       if (!arm64_ftr_bits_kvm) {
> > +               /* arm64_ftr_bits_kvm is not initialized yet. */
> > +               err = init_arm64_ftr_bits_kvm();
>
> Rather than doing this check, can we just initialize it earlier, maybe
> (indirectly) via kvm_arch_init_vm() or around the same time?

Thank you for the comment.
I will consider when it should be initialized.
( perhaps even earlier than kvm_arch_init_vm())

>
>
> > +               if (err)
> > +                       return NULL;
> > +       }
> > +
> > +       ret = bsearch((const void *)(unsigned long)sys_id,
> > +                     arm64_ftr_bits_kvm,
> > +                     arm64_ftr_bits_kvm_nentries,
> > +                     sizeof(arm64_ftr_bits_kvm[0]),
> > +                     search_cmp_ftr_reg_bits);
> > +       if (ret)
> > +               return ret->ftr_bits;
> > +
> > +       return NULL;
> > +}
> > +
> > +/*
> > + * Check if features (or levels of features) that are indicated in the ID
> > + * register value @val are also indicated in @limit.
> > + * This function is for KVM to check if features that are indicated in @val,
> > + * which will be used as the ID register value for its guest, are supported
> > + * on the host.
> > + * For AA64MMFR0_EL1.TGranX_2 fields, which don't follow the standard ID
> > + * scheme, the function checks if values of the fields in @val are the same
> > + * as the ones in @limit.
> > + */
> > +int arm64_check_features(u32 sys_reg, u64 val, u64 limit)
> > +{
> > +       const struct arm64_ftr_bits *ftrp = get_arm64_ftr_bits_kvm(sys_reg);
> > +       u64 exposed_mask = 0;
> > +
> > +       if (!ftrp)
> > +               return -ENOENT;
> > +
> > +       for (; ftrp->width; ftrp++) {
> > +               s64 ftr_val = arm64_ftr_value(ftrp, val);
> > +               s64 ftr_lim = arm64_ftr_value(ftrp, limit);
> > +
> > +               exposed_mask |= arm64_ftr_mask(ftrp);
> > +
> > +               if (ftr_val == ftr_lim)
> > +                       continue;
>
> At first I thought that this check isn't necessary, it should be
> covered by the check below (arm64_ftr_safe_value. However, I think
> that it's needed for the FTR_HIGHER_OR_ZERO_SAFE case. If my
> understanding is correct, it might be worth adding a comment, or even
> encapsulating this logic in a arm64_is_safe_value() function for
> clarity.

In my understanding, arm64_ftr_safe_value() provides a safe value
when two values are different, and I think there is nothing special
about the usage of this function (This is actually how the function
is used by update_cpu_ftr_reg()).
Without the check, it won't work for FTR_EXACT, but there might be
more in the future.

Perhaps it might be more straightforward to add the equality check
into arm64_ftr_safe_value() ?

>
> > +
> > +               if (ftr_val != arm64_ftr_safe_value(ftrp, ftr_val, ftr_lim))
> > +                       return -E2BIG;
> > +       }
> > +
> > +       /* Make sure that no unrecognized fields are set in @val. */
> > +       if (val & ~exposed_mask)
> > +               return -E2BIG;
> > +
> > +       return 0;
> > +}

Thanks,
Reiji

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 01/26] KVM: arm64: Introduce a validation function for an ID register
@ 2022-01-26  6:04       ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-26  6:04 UTC (permalink / raw)
  To: Fuad Tabba
  Cc: kvm, Marc Zyngier, Peter Shier, Paolo Bonzini, Will Deacon,
	kvmarm, Linux ARM

Hi Fuad,

> > +/*
> > + * Override entries in @orig_ftrp with the ones in @new_ftrp when their shift
> > + * fields match.  The last entry of @orig_ftrp and @new_ftrp must be
> > + * ARM64_FTR_END (.width == 0).
> > + */
> > +static void arm64_ftr_reg_bits_overrite(struct arm64_ftr_bits *orig_ftrp,
>
> s/overrite/override

Thank you for catching this. I will fix it.


> > +                                       struct arm64_ftr_bits *new_ftrp)
>
> Should this be const struct arm64_ftr_bits *new_ftrp, which would also
> make it consistent with copy_arm64_ftr_bits()?

Yes, I will make new_ftrp const.

>
> > +{
> > +       struct arm64_ftr_bits *o_ftrp, *n_ftrp;
> > +
> > +       for (n_ftrp = new_ftrp; n_ftrp->width; n_ftrp++) {
> > +               for (o_ftrp = orig_ftrp; o_ftrp->width; o_ftrp++) {
> > +                       if (o_ftrp->shift == n_ftrp->shift) {
> > +                               *o_ftrp = *n_ftrp;
> > +                               break;
> > +                       }
> > +               }
> > +       }
> > +}
> > +
>
> ...
>
> > +/*
> > + * Initialize arm64_ftr_bits_kvm.  Copy arm64_ftr_bits for each ID register
> > + * from arm64_ftr_regs to arm64_ftr_bits_kvm, and then override entries in
> > + * arm64_ftr_bits_kvm with ones in arm64_ftr_bits_kvm_override.
> > + */
> > +static int init_arm64_ftr_bits_kvm(void)
> > +{
> > +       struct arm64_ftr_bits ftr_temp[MAX_FTR_BITS_LEN];
> > +       static struct __ftr_reg_bits_entry *reg_bits_array, *bits, *o_bits;
> > +       int i, j, nent, ret;
> > +
> > +       mutex_lock(&arm64_ftr_bits_kvm_lock);
> > +       if (arm64_ftr_bits_kvm) {
> > +               /* Already initialized */
> > +               ret = 0;
> > +               goto unlock_exit;
> > +       }
> > +
> > +       nent = ARRAY_SIZE(arm64_ftr_regs);
> > +       reg_bits_array = kcalloc(nent, sizeof(struct __ftr_reg_bits_entry),
> > +                                GFP_KERNEL);
> > +       if (!reg_bits_array) {
> > +               ret = ENOMEM;
>
> Should this be -ENOMEM?

Yes, I will fix it.


> > +               goto unlock_exit;
> > +       }
> > +
> > +       /* Copy entries from arm64_ftr_regs to reg_bits_array */
> > +       for (i = 0; i < nent; i++) {
> > +               bits = &reg_bits_array[i];
> > +               bits->sys_id = arm64_ftr_regs[i].sys_id;
> > +               bits->ftr_bits = (struct arm64_ftr_bits *)arm64_ftr_regs[i].reg->ftr_bits;
> > +       };
> > +
> > +       /*
> > +        * Override the entries in reg_bits_array with the ones in
> > +        * arm64_ftr_bits_kvm_override.
> > +        */
> > +       for (i = 0; i < ARRAY_SIZE(arm64_ftr_bits_kvm_override); i++) {
> > +               o_bits = &arm64_ftr_bits_kvm_override[i];
> > +               for (j = 0; j < nent; j++) {
> > +                       bits = &reg_bits_array[j];
> > +                       if (bits->sys_id != o_bits->sys_id)
> > +                               continue;
> > +
> > +                       memset(ftr_temp, 0, sizeof(ftr_temp));
> > +
> > +                       /*
> > +                        * Temporary save all entries in o_bits->ftr_bits
> > +                        * to ftr_temp.
> > +                        */
> > +                       copy_arm64_ftr_bits(ftr_temp, o_bits->ftr_bits);
> > +
> > +                       /*
> > +                        * Copy entries from bits->ftr_bits to o_bits->ftr_bits.
> > +                        */
> > +                       copy_arm64_ftr_bits(o_bits->ftr_bits, bits->ftr_bits);
> > +
> > +                       /*
> > +                        * Override entries in o_bits->ftr_bits with the
> > +                        * saved ones, and update bits->ftr_bits with
> > +                        * o_bits->ftr_bits.
> > +                        */
> > +                       arm64_ftr_reg_bits_overrite(o_bits->ftr_bits, ftr_temp);
> > +                       bits->ftr_bits = o_bits->ftr_bits;
> > +                       break;
> > +               }
> > +       }
>
> Could you please explain using ftr_temp[] and changing the value in
> arm64_ftr_bits_kvm_override, rather than just
> arm64_ftr_reg_bits_overrite(bits->ftr_bits, o_bits->ftr_bits)?

I would like to maintain the order of the entries in the original
ftr_bits so that (future) functions that work for the original ones
also work for the KVM's.
The copy and override is an easy way to do that.  The same thing can
be done without ftr_temp[], but it would look a bit tricky.

If we assume the order shouldn't matter or entries in ftr_bits
are always in descending order, just changing the value in
arm64_ftr_bits_kvm_override would be a much simpler way though.


>
>
> > +static const struct arm64_ftr_bits *get_arm64_ftr_bits_kvm(u32 sys_id)
> > +{
> > +       const struct __ftr_reg_bits_entry *ret;
> > +       int err;
> > +
> > +       if (!arm64_ftr_bits_kvm) {
> > +               /* arm64_ftr_bits_kvm is not initialized yet. */
> > +               err = init_arm64_ftr_bits_kvm();
>
> Rather than doing this check, can we just initialize it earlier, maybe
> (indirectly) via kvm_arch_init_vm() or around the same time?

Thank you for the comment.
I will consider when it should be initialized.
( perhaps even earlier than kvm_arch_init_vm())

>
>
> > +               if (err)
> > +                       return NULL;
> > +       }
> > +
> > +       ret = bsearch((const void *)(unsigned long)sys_id,
> > +                     arm64_ftr_bits_kvm,
> > +                     arm64_ftr_bits_kvm_nentries,
> > +                     sizeof(arm64_ftr_bits_kvm[0]),
> > +                     search_cmp_ftr_reg_bits);
> > +       if (ret)
> > +               return ret->ftr_bits;
> > +
> > +       return NULL;
> > +}
> > +
> > +/*
> > + * Check if features (or levels of features) that are indicated in the ID
> > + * register value @val are also indicated in @limit.
> > + * This function is for KVM to check if features that are indicated in @val,
> > + * which will be used as the ID register value for its guest, are supported
> > + * on the host.
> > + * For AA64MMFR0_EL1.TGranX_2 fields, which don't follow the standard ID
> > + * scheme, the function checks if values of the fields in @val are the same
> > + * as the ones in @limit.
> > + */
> > +int arm64_check_features(u32 sys_reg, u64 val, u64 limit)
> > +{
> > +       const struct arm64_ftr_bits *ftrp = get_arm64_ftr_bits_kvm(sys_reg);
> > +       u64 exposed_mask = 0;
> > +
> > +       if (!ftrp)
> > +               return -ENOENT;
> > +
> > +       for (; ftrp->width; ftrp++) {
> > +               s64 ftr_val = arm64_ftr_value(ftrp, val);
> > +               s64 ftr_lim = arm64_ftr_value(ftrp, limit);
> > +
> > +               exposed_mask |= arm64_ftr_mask(ftrp);
> > +
> > +               if (ftr_val == ftr_lim)
> > +                       continue;
>
> At first I thought that this check isn't necessary, it should be
> covered by the check below (arm64_ftr_safe_value. However, I think
> that it's needed for the FTR_HIGHER_OR_ZERO_SAFE case. If my
> understanding is correct, it might be worth adding a comment, or even
> encapsulating this logic in a arm64_is_safe_value() function for
> clarity.

In my understanding, arm64_ftr_safe_value() provides a safe value
when two values are different, and I think there is nothing special
about the usage of this function (This is actually how the function
is used by update_cpu_ftr_reg()).
Without the check, it won't work for FTR_EXACT, but there might be
more in the future.

Perhaps it might be more straightforward to add the equality check
into arm64_ftr_safe_value() ?

>
> > +
> > +               if (ftr_val != arm64_ftr_safe_value(ftrp, ftr_val, ftr_lim))
> > +                       return -E2BIG;
> > +       }
> > +
> > +       /* Make sure that no unrecognized fields are set in @val. */
> > +       if (val & ~exposed_mask)
> > +               return -E2BIG;
> > +
> > +       return 0;
> > +}

Thanks,
Reiji
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 01/26] KVM: arm64: Introduce a validation function for an ID register
@ 2022-01-26  6:04       ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-26  6:04 UTC (permalink / raw)
  To: Fuad Tabba
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, Linux ARM

Hi Fuad,

> > +/*
> > + * Override entries in @orig_ftrp with the ones in @new_ftrp when their shift
> > + * fields match.  The last entry of @orig_ftrp and @new_ftrp must be
> > + * ARM64_FTR_END (.width == 0).
> > + */
> > +static void arm64_ftr_reg_bits_overrite(struct arm64_ftr_bits *orig_ftrp,
>
> s/overrite/override

Thank you for catching this. I will fix it.


> > +                                       struct arm64_ftr_bits *new_ftrp)
>
> Should this be const struct arm64_ftr_bits *new_ftrp, which would also
> make it consistent with copy_arm64_ftr_bits()?

Yes, I will make new_ftrp const.

>
> > +{
> > +       struct arm64_ftr_bits *o_ftrp, *n_ftrp;
> > +
> > +       for (n_ftrp = new_ftrp; n_ftrp->width; n_ftrp++) {
> > +               for (o_ftrp = orig_ftrp; o_ftrp->width; o_ftrp++) {
> > +                       if (o_ftrp->shift == n_ftrp->shift) {
> > +                               *o_ftrp = *n_ftrp;
> > +                               break;
> > +                       }
> > +               }
> > +       }
> > +}
> > +
>
> ...
>
> > +/*
> > + * Initialize arm64_ftr_bits_kvm.  Copy arm64_ftr_bits for each ID register
> > + * from arm64_ftr_regs to arm64_ftr_bits_kvm, and then override entries in
> > + * arm64_ftr_bits_kvm with ones in arm64_ftr_bits_kvm_override.
> > + */
> > +static int init_arm64_ftr_bits_kvm(void)
> > +{
> > +       struct arm64_ftr_bits ftr_temp[MAX_FTR_BITS_LEN];
> > +       static struct __ftr_reg_bits_entry *reg_bits_array, *bits, *o_bits;
> > +       int i, j, nent, ret;
> > +
> > +       mutex_lock(&arm64_ftr_bits_kvm_lock);
> > +       if (arm64_ftr_bits_kvm) {
> > +               /* Already initialized */
> > +               ret = 0;
> > +               goto unlock_exit;
> > +       }
> > +
> > +       nent = ARRAY_SIZE(arm64_ftr_regs);
> > +       reg_bits_array = kcalloc(nent, sizeof(struct __ftr_reg_bits_entry),
> > +                                GFP_KERNEL);
> > +       if (!reg_bits_array) {
> > +               ret = ENOMEM;
>
> Should this be -ENOMEM?

Yes, I will fix it.


> > +               goto unlock_exit;
> > +       }
> > +
> > +       /* Copy entries from arm64_ftr_regs to reg_bits_array */
> > +       for (i = 0; i < nent; i++) {
> > +               bits = &reg_bits_array[i];
> > +               bits->sys_id = arm64_ftr_regs[i].sys_id;
> > +               bits->ftr_bits = (struct arm64_ftr_bits *)arm64_ftr_regs[i].reg->ftr_bits;
> > +       };
> > +
> > +       /*
> > +        * Override the entries in reg_bits_array with the ones in
> > +        * arm64_ftr_bits_kvm_override.
> > +        */
> > +       for (i = 0; i < ARRAY_SIZE(arm64_ftr_bits_kvm_override); i++) {
> > +               o_bits = &arm64_ftr_bits_kvm_override[i];
> > +               for (j = 0; j < nent; j++) {
> > +                       bits = &reg_bits_array[j];
> > +                       if (bits->sys_id != o_bits->sys_id)
> > +                               continue;
> > +
> > +                       memset(ftr_temp, 0, sizeof(ftr_temp));
> > +
> > +                       /*
> > +                        * Temporary save all entries in o_bits->ftr_bits
> > +                        * to ftr_temp.
> > +                        */
> > +                       copy_arm64_ftr_bits(ftr_temp, o_bits->ftr_bits);
> > +
> > +                       /*
> > +                        * Copy entries from bits->ftr_bits to o_bits->ftr_bits.
> > +                        */
> > +                       copy_arm64_ftr_bits(o_bits->ftr_bits, bits->ftr_bits);
> > +
> > +                       /*
> > +                        * Override entries in o_bits->ftr_bits with the
> > +                        * saved ones, and update bits->ftr_bits with
> > +                        * o_bits->ftr_bits.
> > +                        */
> > +                       arm64_ftr_reg_bits_overrite(o_bits->ftr_bits, ftr_temp);
> > +                       bits->ftr_bits = o_bits->ftr_bits;
> > +                       break;
> > +               }
> > +       }
>
> Could you please explain using ftr_temp[] and changing the value in
> arm64_ftr_bits_kvm_override, rather than just
> arm64_ftr_reg_bits_overrite(bits->ftr_bits, o_bits->ftr_bits)?

I would like to maintain the order of the entries in the original
ftr_bits so that (future) functions that work for the original ones
also work for the KVM's.
The copy and override is an easy way to do that.  The same thing can
be done without ftr_temp[], but it would look a bit tricky.

If we assume the order shouldn't matter or entries in ftr_bits
are always in descending order, just changing the value in
arm64_ftr_bits_kvm_override would be a much simpler way though.


>
>
> > +static const struct arm64_ftr_bits *get_arm64_ftr_bits_kvm(u32 sys_id)
> > +{
> > +       const struct __ftr_reg_bits_entry *ret;
> > +       int err;
> > +
> > +       if (!arm64_ftr_bits_kvm) {
> > +               /* arm64_ftr_bits_kvm is not initialized yet. */
> > +               err = init_arm64_ftr_bits_kvm();
>
> Rather than doing this check, can we just initialize it earlier, maybe
> (indirectly) via kvm_arch_init_vm() or around the same time?

Thank you for the comment.
I will consider when it should be initialized.
( perhaps even earlier than kvm_arch_init_vm())

>
>
> > +               if (err)
> > +                       return NULL;
> > +       }
> > +
> > +       ret = bsearch((const void *)(unsigned long)sys_id,
> > +                     arm64_ftr_bits_kvm,
> > +                     arm64_ftr_bits_kvm_nentries,
> > +                     sizeof(arm64_ftr_bits_kvm[0]),
> > +                     search_cmp_ftr_reg_bits);
> > +       if (ret)
> > +               return ret->ftr_bits;
> > +
> > +       return NULL;
> > +}
> > +
> > +/*
> > + * Check if features (or levels of features) that are indicated in the ID
> > + * register value @val are also indicated in @limit.
> > + * This function is for KVM to check if features that are indicated in @val,
> > + * which will be used as the ID register value for its guest, are supported
> > + * on the host.
> > + * For AA64MMFR0_EL1.TGranX_2 fields, which don't follow the standard ID
> > + * scheme, the function checks if values of the fields in @val are the same
> > + * as the ones in @limit.
> > + */
> > +int arm64_check_features(u32 sys_reg, u64 val, u64 limit)
> > +{
> > +       const struct arm64_ftr_bits *ftrp = get_arm64_ftr_bits_kvm(sys_reg);
> > +       u64 exposed_mask = 0;
> > +
> > +       if (!ftrp)
> > +               return -ENOENT;
> > +
> > +       for (; ftrp->width; ftrp++) {
> > +               s64 ftr_val = arm64_ftr_value(ftrp, val);
> > +               s64 ftr_lim = arm64_ftr_value(ftrp, limit);
> > +
> > +               exposed_mask |= arm64_ftr_mask(ftrp);
> > +
> > +               if (ftr_val == ftr_lim)
> > +                       continue;
>
> At first I thought that this check isn't necessary, it should be
> covered by the check below (arm64_ftr_safe_value. However, I think
> that it's needed for the FTR_HIGHER_OR_ZERO_SAFE case. If my
> understanding is correct, it might be worth adding a comment, or even
> encapsulating this logic in a arm64_is_safe_value() function for
> clarity.

In my understanding, arm64_ftr_safe_value() provides a safe value
when two values are different, and I think there is nothing special
about the usage of this function (This is actually how the function
is used by update_cpu_ftr_reg()).
Without the check, it won't work for FTR_EXACT, but there might be
more in the future.

Perhaps it might be more straightforward to add the equality check
into arm64_ftr_safe_value() ?

>
> > +
> > +               if (ftr_val != arm64_ftr_safe_value(ftrp, ftr_val, ftr_lim))
> > +                       return -E2BIG;
> > +       }
> > +
> > +       /* Make sure that no unrecognized fields are set in @val. */
> > +       if (val & ~exposed_mask)
> > +               return -E2BIG;
> > +
> > +       return 0;
> > +}

Thanks,
Reiji

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 03/26] KVM: arm64: Introduce struct id_reg_info
  2022-01-24 16:28     ` Fuad Tabba
  (?)
@ 2022-01-26  6:46       ` Reiji Watanabe
  -1 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-26  6:46 UTC (permalink / raw)
  To: Fuad Tabba
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, Linux ARM

Hi Fuad,

On Mon, Jan 24, 2022 at 8:29 AM Fuad Tabba <tabba@google.com> wrote:
>
> Hi Reiji,
>
> On Thu, Jan 6, 2022 at 4:28 AM Reiji Watanabe <reijiw@google.com> wrote:
> >
> > This patch lays the groundwork to make ID registers writable.
> >
> > Introduce struct id_reg_info for an ID register to manage the
> > register specific control of its value for the guest, and provide set
> > of functions commonly used for ID registers to make them writable.
> >
> > The id_reg_info is used to do register specific initialization,
> > validation of the ID register and etc.  Not all ID registers must
> > have the id_reg_info. ID registers that don't have the id_reg_info
> > are handled in a common way that is applied to all ID registers.
> >
> > At present, changing an ID register from userspace is allowed only
> > if the ID register has the id_reg_info, but that will be changed
> > by the following patches.
> >
> > No ID register has the structure yet and the following patches
> > will add the id_reg_info for some ID registers.
> >
> > Signed-off-by: Reiji Watanabe <reijiw@google.com>
> > ---
> >  arch/arm64/kvm/sys_regs.c | 243 ++++++++++++++++++++++++++++++++++++--
> >  1 file changed, 230 insertions(+), 13 deletions(-)
> >
> > diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> > index 80dc62f98ef0..971018288bee 100644
> > --- a/arch/arm64/kvm/sys_regs.c
> > +++ b/arch/arm64/kvm/sys_regs.c
> > @@ -265,6 +265,101 @@ static bool trap_raz_wi(struct kvm_vcpu *vcpu,
> >                 return read_zero(vcpu, p);
> >  }
> >
> > +struct id_reg_info {
> > +       u32     sys_reg;        /* Register ID */
>
> Nit: Why not have the comment above, as for the other fields of this struct?

Yes, I will fix that.
(I placed the comment there just because the comment was short
 enough to place it on the right side)

>
> > +
> > +       /*
> > +        * Limit value of the register for a vcpu. The value is the sanitized
> > +        * system value with bits cleared for unsupported features for the
> > +        * guest.
> > +        */
>
> I think that rather than saying "with bits cleared for unsupported
> features", it might be better to rephrase along the lines of "with
> bits indicating" or "set/cleared to indicate", so that it applies to
> signed fields as well.

Thank you for the suggestion ! I will fix the comment.

>
> > +       u64     vcpu_limit_val;
> > +
> > +       /* Fields that we don't to validate by arm64_check_features. */
>
> Nit: Remove "to".

I will fix it.

>
> > +       u64     ignore_mask;
> > +
> > +       /* Initialization function of the id_reg_info */
> > +       void (*init)(struct id_reg_info *id_reg);
> > +       /*
> > +        * This is an optional ID register specific validation function.
> > +        * When userspace tries to set the ID register, arm64_check_features()
> > +        * will check if the requested value indicates any features that cannot
> > +        * be supported by KVM on the host.  But, some ID register fields need
> > +        * a special checking, and this function can be used for such fields.
> > +        * e.g. When SVE is configured for a vCPU by KVM_ARM_VCPU_INIT,
> > +        * ID_AA64PFR0_EL1.SVE shouldn't be set to 0 for the vCPU.
> > +        * The validation function for ID_AA64PFR0_EL1 could be used to check
> > +        * the field is consistent with SVE configuration.
> > +        */
> > +       int (*validate)(struct kvm_vcpu *vcpu, const struct id_reg_info *id_reg,
> > +                       u64 val);
> > +
> > +       /*
> > +        * Return a bitmask of the vCPU's ID register fields that are not
> > +        * synced with saved (per VM) ID register value, which usually
> > +        * indicates opt-in CPU features that is not configured for the vCPU.
>
> Nit: s/is/are

I will fix it.

>
>
> > +        * ID registers are saved per VM, but some opt-in CPU features can
> > +        * be configured per vCPU.  The saved (per VM) values for such
> > +        * features are for vCPUs with the features (and zero for
> > +        * vCPUs without the features).
> > +        * Return value of this function is used to handle such fields
> > +        * for per vCPU ID register read/write request with saved per VM
> > +        * ID register.  See the __write_id_reg's comment for more detail.
> > +        */
> > +       u64 (*vcpu_mask)(const struct kvm_vcpu *vcpu,
> > +                        const struct id_reg_info *id_reg);
> > +};
> > +
> > +static void id_reg_info_init(struct id_reg_info *id_reg)
> > +{
> > +       id_reg->vcpu_limit_val = read_sanitised_ftr_reg(id_reg->sys_reg);
> > +       if (id_reg->init)
>
> If there is an id_reg then the init function is necessary, isn't it?
> Otherwise it doesn't seem to add more than the default handling. If
> that's right, should we instead ensure that init is always set?

The init function is optional (maybe I should comment that).

>
> > +               id_reg->init(id_reg);
> > +}
> > +
> > +/*
> > + * An ID register that needs special handling to control the value for the
> > + * guest must have its own id_reg_info in id_reg_info_table.
> > + * (i.e. the reset value is different from the host's sanitized value,
> > + * the value is affected by opt-in features, some fields need specific
> > + * validation, etc.)
> > + */
> > +#define        GET_ID_REG_INFO(id)     (id_reg_info_table[IDREG_IDX(id)])
> > +static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {};
> > +
> > +static int validate_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
> > +{
> > +       const struct id_reg_info *id_reg = GET_ID_REG_INFO(id);
> > +       u64 limit, tmp_val;
> > +       int err;
> > +
> > +       if (id_reg) {
> > +               limit = id_reg->vcpu_limit_val;
> > +               /*
> > +                * Replace the fields that are indicated in ignore_mask with
> > +                * the value in the limit to not have arm64_check_features()
> > +                * check the field in @val.
> > +                */
> > +               tmp_val = val & ~id_reg->ignore_mask;
> > +               tmp_val |= (limit & id_reg->ignore_mask);
> > +       } else {
> > +               limit = read_sanitised_ftr_reg(id);
> > +               tmp_val = val;
> > +       }
> > +
> > +       /* Check if the value indicates any feature that is not in the limit. */
> > +       err = arm64_check_features(id, tmp_val, limit);
> > +       if (err)
> > +               return err;
> > +
> > +       if (id_reg && id_reg->validate)
> > +               /* Run the ID register specific validity check. */
> > +               err = id_reg->validate(vcpu, id_reg, val);
> > +
> > +       return err;
> > +}
> > +
> >  /*
> >   * ARMv8.1 mandates at least a trivial LORegion implementation, where all the
> >   * RW registers are RES0 (which we can implement as RAZ/WI). On an ARMv8.0
> > @@ -1061,9 +1156,81 @@ static bool access_arch_timer(struct kvm_vcpu *vcpu,
> >         return true;
> >  }
> >
> > +static u64 read_kvm_id_reg(struct kvm *kvm, u32 id)
> > +{
> > +       return kvm->arch.id_regs[IDREG_IDX(id)];
> > +}
> > +
> > +static int modify_kvm_id_reg(struct kvm *kvm, u32 id, u64 val,
> > +                            u64 preserve_mask)
> > +{
> > +       u64 old, new;
> > +
> > +       mutex_lock(&kvm->lock);
> > +
> > +       old = kvm->arch.id_regs[IDREG_IDX(id)];
> > +
> > +       /* Preserve the value at the bit position set in preserve_mask */
> > +       new = old & preserve_mask;
> > +       new |= (val & ~preserve_mask);
> > +
> > +       /* Don't allow to modify ID register value after KVM_RUN on any vCPUs */
> > +       if (kvm_vm_has_started(kvm) && new != old)
> > +               return -EBUSY;
>
> This path doesn't release the lock. I see that it gets fixed in a
> future patch, but it might be good to fix it here...

Thank you for catching it ! Yes, I will fix this.

>
> > +
> > +       WRITE_ONCE(kvm->arch.id_regs[IDREG_IDX(id)], new);
> > +       mutex_unlock(&kvm->lock);
> > +
> > +       return 0;
> > +}
> > +
> > +static int write_kvm_id_reg(struct kvm *kvm, u32 id, u64 val)
> > +{
> > +       return modify_kvm_id_reg(kvm, id, val, 0);
> > +}
> > +
> > +
> > +/*
> > + * KVM basically forces all vCPUs of the guest to have a uniform value for
> > + * each ID register (, which means KVM_SET_ONE_REG for a vCPU affects all
> > + * the vCPUs of the guest), and the id_regs[] of kvm_arch holds values
> > + * of ID registers for the guest.  However, there is an exception for
> > + * ID register fields corresponding to CPU features that can be
> > + * configured per vCPU by KVM_ARM_VCPU_INIT, or etc (e.g. PMUv3, SVE, etc).
> > + * For such fields, all vCPUs that have the feature will have a non-zero
> > + * uniform value (, which can be updated by userspace), but the vCPUs that
>
> Nit: uneven nesting of parentheses and commas :)

I will fix it.


> > + * don't have the feature will have zero for the fields.
> > + * Values that @id_regs holds are for vCPUs that have such features.  So,
> > + * to get the ID register value for a vCPU that doesn't have those features,
> > + * the corresponding fields in id_regs[] needs to be cleared.
> > + * A bitmask of the fields are provided by id_reg_info's vcpu_mask(), and
> > + * __write_id_reg() and __read_id_reg() take care of those fields using
> > + * the bitmask.
> > + */
> > +static int __write_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
> > +{
> > +       const struct id_reg_info *id_reg = GET_ID_REG_INFO(id);
> > +       u64 mask = 0;
> > +
> > +       if (id_reg && id_reg->vcpu_mask)
> > +               mask = id_reg->vcpu_mask(vcpu, id_reg);
> > +
> > +       /*
> > +        * Update the ID register for the guest with @val, except for fields
> > +        * that are set in the mask, which indicates fields for opt-in
> > +        * features that are not configured for the vCPU.
> > +        */
> > +       return modify_kvm_id_reg(vcpu->kvm, id, val, mask);
> > +}
> > +
> >  static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
> >  {
> > -       u64 val = vcpu->kvm->arch.id_regs[IDREG_IDX(id)];
> > +       const struct id_reg_info *id_reg = GET_ID_REG_INFO(id);
> > +       u64 val = read_kvm_id_reg(vcpu->kvm, id);
> > +
> > +       if (id_reg && id_reg->vcpu_mask)
> > +               /* Clear fields for opt-in features that are not configured. */
> > +               val &= ~(id_reg->vcpu_mask(vcpu, id_reg));
> >         switch (id) {
> >         case SYS_ID_AA64PFR0_EL1:
> > @@ -1222,12 +1389,7 @@ static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
> >         return 0;
> >  }
> >
> > -/*
> > - * cpufeature ID register user accessors
> > - *
> > - * For now, these registers are immutable for userspace, so for set_id_reg()
> > - * we don't allow the effective value to be changed.
> > - */
> > +/* cpufeature ID register user accessors */
> >  static int __get_id_reg(const struct kvm_vcpu *vcpu,
> >                         const struct sys_reg_desc *rd, void __user *uaddr,
> >                         bool raz)
> > @@ -1238,11 +1400,31 @@ static int __get_id_reg(const struct kvm_vcpu *vcpu,
> >         return reg_to_user(uaddr, &val, id);
> >  }
> >
> > +/*
> > + * Check if the given id indicates AArch32 ID register encoding.
> > + */
> > +static bool is_aarch32_id_reg(u32 id)
> > +{
> > +       u32 crm, op2;
> > +
> > +       if (!is_id_reg(id))
> > +               return false;
> > +
> > +       crm = sys_reg_CRm(id);
> > +       op2 = sys_reg_Op2(id);
> > +       if (crm == 1 || crm == 2 || (crm == 3 && (op2 != 3 && op2 != 7)))
>
> Consistent with the Arm ARM "Table D12-2 System instruction encodings
> for non-Debug System register accesses"
>
> > +               /* AArch32 ID register */
> > +               return true;
> > +
> > +       return false;
> > +}
> > +
> >  static int __set_id_reg(struct kvm_vcpu *vcpu,
> >                         const struct sys_reg_desc *rd, void __user *uaddr,
> >                         bool raz)
> >  {
> >         const u64 id = sys_reg_to_index(rd);
> > +       u32 encoding = reg_to_encoding(rd);
> >         int err;
> >         u64 val;
> >
> > @@ -1250,11 +1432,28 @@ static int __set_id_reg(struct kvm_vcpu *vcpu,
> >         if (err)
> >                 return err;
> >
> > -       /* This is what we mean by invariant: you can't change it. */
> > -       if (val != read_id_reg(vcpu, rd, raz))
> > +       if (val == read_id_reg(vcpu, rd, raz))
> > +               /* The value is same as the current value. Nothing to do. */
> > +               return 0;
> > +
> > +       /*
> > +        * Don't allow to modify the register's value if the register is raz,
> > +        * or the reg doesn't have the id_reg_info.
> > +        */
> > +       if (raz || !GET_ID_REG_INFO(encoding))
> >                 return -EINVAL;
> >
> > -       return 0;
> > +       /*
> > +        * Skip the validation of AArch32 ID registers if the system doesn't
> > +        * 32bit EL0 (their value are UNKNOWN).
> > +        */
> > +       if (system_supports_32bit_el0() || !is_aarch32_id_reg(encoding)) {
> > +               err = validate_id_reg(vcpu, encoding, val);
> > +               if (err)
> > +                       return err;
> > +       }
> > +
> > +       return __write_id_reg(vcpu, encoding, val);
> >  }
> >
> >  static int get_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
> > @@ -2816,6 +3015,20 @@ int kvm_arm_copy_sys_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
> >         return write_demux_regids(uindices);
> >  }
> >
> > +static void id_reg_info_init_all(void)
> > +{
> > +       int i;
> > +       struct id_reg_info *id_reg;
> > +
> > +       for (i = 0; i < ARRAY_SIZE(id_reg_info_table); i++) {
> > +               id_reg = (struct id_reg_info *)id_reg_info_table[i];
> > +               if (!id_reg)
> > +                       continue;
> > +
> > +               id_reg_info_init(id_reg);
> > +       }
> > +}
> > +
> >  void kvm_sys_reg_table_init(void)
> >  {
> >         unsigned int i;
> > @@ -2850,6 +3063,8 @@ void kvm_sys_reg_table_init(void)
> >                         break;
> >         /* Clear all higher bits. */
> >         cache_levels &= (1 << (i*3))-1;
> > +
> > +       id_reg_info_init_all();
> >  }
> >
> >  /*
> > @@ -2862,11 +3077,12 @@ void set_default_id_regs(struct kvm *kvm)
> >         u32 id;
> >         const struct sys_reg_desc *rd;
> >         u64 val;
> > +       struct id_reg_info *idr;
> >
> >         for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
> >                 rd = &sys_reg_descs[i];
> >                 if (rd->access != access_id_reg)
> > -                       /* Not ID register, or hidden/reserved ID register */
> > +                       /* Not ID register or hidden/reserved ID register */
> >                         continue;
> >
> >                 id = reg_to_encoding(rd);
> > @@ -2874,7 +3090,8 @@ void set_default_id_regs(struct kvm *kvm)
> >                         /* Shouldn't happen */
> >                         continue;
> >
> > -               val = read_sanitised_ftr_reg(id);
> > -               kvm->arch.id_regs[IDREG_IDX(id)] = val;
> > +               idr = GET_ID_REG_INFO(id);
> > +               val = idr ? idr->vcpu_limit_val : read_sanitised_ftr_reg(id);
> > +               (void)write_kvm_id_reg(kvm, id, val);
>
> Rather than ignoring the return value of write_kvm_id_reg(), wouldn't
> it be better if set_default_id_regs were to propagate it back to
> kvm_arch_init_vm in case there's a problem?

Since write_kvm_id_reg() should never return an error for this
case, returning an error to kvm_arch_init_vm() adds a practically
unnecessary error handling, which I would like to avoid.
So, how about putting WARN_ON_ONCE on its return value ?

Thanks,
Reiji

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 03/26] KVM: arm64: Introduce struct id_reg_info
@ 2022-01-26  6:46       ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-26  6:46 UTC (permalink / raw)
  To: Fuad Tabba
  Cc: kvm, Marc Zyngier, Peter Shier, Paolo Bonzini, Will Deacon,
	kvmarm, Linux ARM

Hi Fuad,

On Mon, Jan 24, 2022 at 8:29 AM Fuad Tabba <tabba@google.com> wrote:
>
> Hi Reiji,
>
> On Thu, Jan 6, 2022 at 4:28 AM Reiji Watanabe <reijiw@google.com> wrote:
> >
> > This patch lays the groundwork to make ID registers writable.
> >
> > Introduce struct id_reg_info for an ID register to manage the
> > register specific control of its value for the guest, and provide set
> > of functions commonly used for ID registers to make them writable.
> >
> > The id_reg_info is used to do register specific initialization,
> > validation of the ID register and etc.  Not all ID registers must
> > have the id_reg_info. ID registers that don't have the id_reg_info
> > are handled in a common way that is applied to all ID registers.
> >
> > At present, changing an ID register from userspace is allowed only
> > if the ID register has the id_reg_info, but that will be changed
> > by the following patches.
> >
> > No ID register has the structure yet and the following patches
> > will add the id_reg_info for some ID registers.
> >
> > Signed-off-by: Reiji Watanabe <reijiw@google.com>
> > ---
> >  arch/arm64/kvm/sys_regs.c | 243 ++++++++++++++++++++++++++++++++++++--
> >  1 file changed, 230 insertions(+), 13 deletions(-)
> >
> > diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> > index 80dc62f98ef0..971018288bee 100644
> > --- a/arch/arm64/kvm/sys_regs.c
> > +++ b/arch/arm64/kvm/sys_regs.c
> > @@ -265,6 +265,101 @@ static bool trap_raz_wi(struct kvm_vcpu *vcpu,
> >                 return read_zero(vcpu, p);
> >  }
> >
> > +struct id_reg_info {
> > +       u32     sys_reg;        /* Register ID */
>
> Nit: Why not have the comment above, as for the other fields of this struct?

Yes, I will fix that.
(I placed the comment there just because the comment was short
 enough to place it on the right side)

>
> > +
> > +       /*
> > +        * Limit value of the register for a vcpu. The value is the sanitized
> > +        * system value with bits cleared for unsupported features for the
> > +        * guest.
> > +        */
>
> I think that rather than saying "with bits cleared for unsupported
> features", it might be better to rephrase along the lines of "with
> bits indicating" or "set/cleared to indicate", so that it applies to
> signed fields as well.

Thank you for the suggestion ! I will fix the comment.

>
> > +       u64     vcpu_limit_val;
> > +
> > +       /* Fields that we don't to validate by arm64_check_features. */
>
> Nit: Remove "to".

I will fix it.

>
> > +       u64     ignore_mask;
> > +
> > +       /* Initialization function of the id_reg_info */
> > +       void (*init)(struct id_reg_info *id_reg);
> > +       /*
> > +        * This is an optional ID register specific validation function.
> > +        * When userspace tries to set the ID register, arm64_check_features()
> > +        * will check if the requested value indicates any features that cannot
> > +        * be supported by KVM on the host.  But, some ID register fields need
> > +        * a special checking, and this function can be used for such fields.
> > +        * e.g. When SVE is configured for a vCPU by KVM_ARM_VCPU_INIT,
> > +        * ID_AA64PFR0_EL1.SVE shouldn't be set to 0 for the vCPU.
> > +        * The validation function for ID_AA64PFR0_EL1 could be used to check
> > +        * the field is consistent with SVE configuration.
> > +        */
> > +       int (*validate)(struct kvm_vcpu *vcpu, const struct id_reg_info *id_reg,
> > +                       u64 val);
> > +
> > +       /*
> > +        * Return a bitmask of the vCPU's ID register fields that are not
> > +        * synced with saved (per VM) ID register value, which usually
> > +        * indicates opt-in CPU features that is not configured for the vCPU.
>
> Nit: s/is/are

I will fix it.

>
>
> > +        * ID registers are saved per VM, but some opt-in CPU features can
> > +        * be configured per vCPU.  The saved (per VM) values for such
> > +        * features are for vCPUs with the features (and zero for
> > +        * vCPUs without the features).
> > +        * Return value of this function is used to handle such fields
> > +        * for per vCPU ID register read/write request with saved per VM
> > +        * ID register.  See the __write_id_reg's comment for more detail.
> > +        */
> > +       u64 (*vcpu_mask)(const struct kvm_vcpu *vcpu,
> > +                        const struct id_reg_info *id_reg);
> > +};
> > +
> > +static void id_reg_info_init(struct id_reg_info *id_reg)
> > +{
> > +       id_reg->vcpu_limit_val = read_sanitised_ftr_reg(id_reg->sys_reg);
> > +       if (id_reg->init)
>
> If there is an id_reg then the init function is necessary, isn't it?
> Otherwise it doesn't seem to add more than the default handling. If
> that's right, should we instead ensure that init is always set?

The init function is optional (maybe I should comment that).

>
> > +               id_reg->init(id_reg);
> > +}
> > +
> > +/*
> > + * An ID register that needs special handling to control the value for the
> > + * guest must have its own id_reg_info in id_reg_info_table.
> > + * (i.e. the reset value is different from the host's sanitized value,
> > + * the value is affected by opt-in features, some fields need specific
> > + * validation, etc.)
> > + */
> > +#define        GET_ID_REG_INFO(id)     (id_reg_info_table[IDREG_IDX(id)])
> > +static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {};
> > +
> > +static int validate_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
> > +{
> > +       const struct id_reg_info *id_reg = GET_ID_REG_INFO(id);
> > +       u64 limit, tmp_val;
> > +       int err;
> > +
> > +       if (id_reg) {
> > +               limit = id_reg->vcpu_limit_val;
> > +               /*
> > +                * Replace the fields that are indicated in ignore_mask with
> > +                * the value in the limit to not have arm64_check_features()
> > +                * check the field in @val.
> > +                */
> > +               tmp_val = val & ~id_reg->ignore_mask;
> > +               tmp_val |= (limit & id_reg->ignore_mask);
> > +       } else {
> > +               limit = read_sanitised_ftr_reg(id);
> > +               tmp_val = val;
> > +       }
> > +
> > +       /* Check if the value indicates any feature that is not in the limit. */
> > +       err = arm64_check_features(id, tmp_val, limit);
> > +       if (err)
> > +               return err;
> > +
> > +       if (id_reg && id_reg->validate)
> > +               /* Run the ID register specific validity check. */
> > +               err = id_reg->validate(vcpu, id_reg, val);
> > +
> > +       return err;
> > +}
> > +
> >  /*
> >   * ARMv8.1 mandates at least a trivial LORegion implementation, where all the
> >   * RW registers are RES0 (which we can implement as RAZ/WI). On an ARMv8.0
> > @@ -1061,9 +1156,81 @@ static bool access_arch_timer(struct kvm_vcpu *vcpu,
> >         return true;
> >  }
> >
> > +static u64 read_kvm_id_reg(struct kvm *kvm, u32 id)
> > +{
> > +       return kvm->arch.id_regs[IDREG_IDX(id)];
> > +}
> > +
> > +static int modify_kvm_id_reg(struct kvm *kvm, u32 id, u64 val,
> > +                            u64 preserve_mask)
> > +{
> > +       u64 old, new;
> > +
> > +       mutex_lock(&kvm->lock);
> > +
> > +       old = kvm->arch.id_regs[IDREG_IDX(id)];
> > +
> > +       /* Preserve the value at the bit position set in preserve_mask */
> > +       new = old & preserve_mask;
> > +       new |= (val & ~preserve_mask);
> > +
> > +       /* Don't allow to modify ID register value after KVM_RUN on any vCPUs */
> > +       if (kvm_vm_has_started(kvm) && new != old)
> > +               return -EBUSY;
>
> This path doesn't release the lock. I see that it gets fixed in a
> future patch, but it might be good to fix it here...

Thank you for catching it ! Yes, I will fix this.

>
> > +
> > +       WRITE_ONCE(kvm->arch.id_regs[IDREG_IDX(id)], new);
> > +       mutex_unlock(&kvm->lock);
> > +
> > +       return 0;
> > +}
> > +
> > +static int write_kvm_id_reg(struct kvm *kvm, u32 id, u64 val)
> > +{
> > +       return modify_kvm_id_reg(kvm, id, val, 0);
> > +}
> > +
> > +
> > +/*
> > + * KVM basically forces all vCPUs of the guest to have a uniform value for
> > + * each ID register (, which means KVM_SET_ONE_REG for a vCPU affects all
> > + * the vCPUs of the guest), and the id_regs[] of kvm_arch holds values
> > + * of ID registers for the guest.  However, there is an exception for
> > + * ID register fields corresponding to CPU features that can be
> > + * configured per vCPU by KVM_ARM_VCPU_INIT, or etc (e.g. PMUv3, SVE, etc).
> > + * For such fields, all vCPUs that have the feature will have a non-zero
> > + * uniform value (, which can be updated by userspace), but the vCPUs that
>
> Nit: uneven nesting of parentheses and commas :)

I will fix it.


> > + * don't have the feature will have zero for the fields.
> > + * Values that @id_regs holds are for vCPUs that have such features.  So,
> > + * to get the ID register value for a vCPU that doesn't have those features,
> > + * the corresponding fields in id_regs[] needs to be cleared.
> > + * A bitmask of the fields are provided by id_reg_info's vcpu_mask(), and
> > + * __write_id_reg() and __read_id_reg() take care of those fields using
> > + * the bitmask.
> > + */
> > +static int __write_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
> > +{
> > +       const struct id_reg_info *id_reg = GET_ID_REG_INFO(id);
> > +       u64 mask = 0;
> > +
> > +       if (id_reg && id_reg->vcpu_mask)
> > +               mask = id_reg->vcpu_mask(vcpu, id_reg);
> > +
> > +       /*
> > +        * Update the ID register for the guest with @val, except for fields
> > +        * that are set in the mask, which indicates fields for opt-in
> > +        * features that are not configured for the vCPU.
> > +        */
> > +       return modify_kvm_id_reg(vcpu->kvm, id, val, mask);
> > +}
> > +
> >  static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
> >  {
> > -       u64 val = vcpu->kvm->arch.id_regs[IDREG_IDX(id)];
> > +       const struct id_reg_info *id_reg = GET_ID_REG_INFO(id);
> > +       u64 val = read_kvm_id_reg(vcpu->kvm, id);
> > +
> > +       if (id_reg && id_reg->vcpu_mask)
> > +               /* Clear fields for opt-in features that are not configured. */
> > +               val &= ~(id_reg->vcpu_mask(vcpu, id_reg));
> >         switch (id) {
> >         case SYS_ID_AA64PFR0_EL1:
> > @@ -1222,12 +1389,7 @@ static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
> >         return 0;
> >  }
> >
> > -/*
> > - * cpufeature ID register user accessors
> > - *
> > - * For now, these registers are immutable for userspace, so for set_id_reg()
> > - * we don't allow the effective value to be changed.
> > - */
> > +/* cpufeature ID register user accessors */
> >  static int __get_id_reg(const struct kvm_vcpu *vcpu,
> >                         const struct sys_reg_desc *rd, void __user *uaddr,
> >                         bool raz)
> > @@ -1238,11 +1400,31 @@ static int __get_id_reg(const struct kvm_vcpu *vcpu,
> >         return reg_to_user(uaddr, &val, id);
> >  }
> >
> > +/*
> > + * Check if the given id indicates AArch32 ID register encoding.
> > + */
> > +static bool is_aarch32_id_reg(u32 id)
> > +{
> > +       u32 crm, op2;
> > +
> > +       if (!is_id_reg(id))
> > +               return false;
> > +
> > +       crm = sys_reg_CRm(id);
> > +       op2 = sys_reg_Op2(id);
> > +       if (crm == 1 || crm == 2 || (crm == 3 && (op2 != 3 && op2 != 7)))
>
> Consistent with the Arm ARM "Table D12-2 System instruction encodings
> for non-Debug System register accesses"
>
> > +               /* AArch32 ID register */
> > +               return true;
> > +
> > +       return false;
> > +}
> > +
> >  static int __set_id_reg(struct kvm_vcpu *vcpu,
> >                         const struct sys_reg_desc *rd, void __user *uaddr,
> >                         bool raz)
> >  {
> >         const u64 id = sys_reg_to_index(rd);
> > +       u32 encoding = reg_to_encoding(rd);
> >         int err;
> >         u64 val;
> >
> > @@ -1250,11 +1432,28 @@ static int __set_id_reg(struct kvm_vcpu *vcpu,
> >         if (err)
> >                 return err;
> >
> > -       /* This is what we mean by invariant: you can't change it. */
> > -       if (val != read_id_reg(vcpu, rd, raz))
> > +       if (val == read_id_reg(vcpu, rd, raz))
> > +               /* The value is same as the current value. Nothing to do. */
> > +               return 0;
> > +
> > +       /*
> > +        * Don't allow to modify the register's value if the register is raz,
> > +        * or the reg doesn't have the id_reg_info.
> > +        */
> > +       if (raz || !GET_ID_REG_INFO(encoding))
> >                 return -EINVAL;
> >
> > -       return 0;
> > +       /*
> > +        * Skip the validation of AArch32 ID registers if the system doesn't
> > +        * 32bit EL0 (their value are UNKNOWN).
> > +        */
> > +       if (system_supports_32bit_el0() || !is_aarch32_id_reg(encoding)) {
> > +               err = validate_id_reg(vcpu, encoding, val);
> > +               if (err)
> > +                       return err;
> > +       }
> > +
> > +       return __write_id_reg(vcpu, encoding, val);
> >  }
> >
> >  static int get_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
> > @@ -2816,6 +3015,20 @@ int kvm_arm_copy_sys_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
> >         return write_demux_regids(uindices);
> >  }
> >
> > +static void id_reg_info_init_all(void)
> > +{
> > +       int i;
> > +       struct id_reg_info *id_reg;
> > +
> > +       for (i = 0; i < ARRAY_SIZE(id_reg_info_table); i++) {
> > +               id_reg = (struct id_reg_info *)id_reg_info_table[i];
> > +               if (!id_reg)
> > +                       continue;
> > +
> > +               id_reg_info_init(id_reg);
> > +       }
> > +}
> > +
> >  void kvm_sys_reg_table_init(void)
> >  {
> >         unsigned int i;
> > @@ -2850,6 +3063,8 @@ void kvm_sys_reg_table_init(void)
> >                         break;
> >         /* Clear all higher bits. */
> >         cache_levels &= (1 << (i*3))-1;
> > +
> > +       id_reg_info_init_all();
> >  }
> >
> >  /*
> > @@ -2862,11 +3077,12 @@ void set_default_id_regs(struct kvm *kvm)
> >         u32 id;
> >         const struct sys_reg_desc *rd;
> >         u64 val;
> > +       struct id_reg_info *idr;
> >
> >         for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
> >                 rd = &sys_reg_descs[i];
> >                 if (rd->access != access_id_reg)
> > -                       /* Not ID register, or hidden/reserved ID register */
> > +                       /* Not ID register or hidden/reserved ID register */
> >                         continue;
> >
> >                 id = reg_to_encoding(rd);
> > @@ -2874,7 +3090,8 @@ void set_default_id_regs(struct kvm *kvm)
> >                         /* Shouldn't happen */
> >                         continue;
> >
> > -               val = read_sanitised_ftr_reg(id);
> > -               kvm->arch.id_regs[IDREG_IDX(id)] = val;
> > +               idr = GET_ID_REG_INFO(id);
> > +               val = idr ? idr->vcpu_limit_val : read_sanitised_ftr_reg(id);
> > +               (void)write_kvm_id_reg(kvm, id, val);
>
> Rather than ignoring the return value of write_kvm_id_reg(), wouldn't
> it be better if set_default_id_regs were to propagate it back to
> kvm_arch_init_vm in case there's a problem?

Since write_kvm_id_reg() should never return an error for this
case, returning an error to kvm_arch_init_vm() adds a practically
unnecessary error handling, which I would like to avoid.
So, how about putting WARN_ON_ONCE on its return value ?

Thanks,
Reiji
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 03/26] KVM: arm64: Introduce struct id_reg_info
@ 2022-01-26  6:46       ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-26  6:46 UTC (permalink / raw)
  To: Fuad Tabba
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, Linux ARM

Hi Fuad,

On Mon, Jan 24, 2022 at 8:29 AM Fuad Tabba <tabba@google.com> wrote:
>
> Hi Reiji,
>
> On Thu, Jan 6, 2022 at 4:28 AM Reiji Watanabe <reijiw@google.com> wrote:
> >
> > This patch lays the groundwork to make ID registers writable.
> >
> > Introduce struct id_reg_info for an ID register to manage the
> > register specific control of its value for the guest, and provide set
> > of functions commonly used for ID registers to make them writable.
> >
> > The id_reg_info is used to do register specific initialization,
> > validation of the ID register and etc.  Not all ID registers must
> > have the id_reg_info. ID registers that don't have the id_reg_info
> > are handled in a common way that is applied to all ID registers.
> >
> > At present, changing an ID register from userspace is allowed only
> > if the ID register has the id_reg_info, but that will be changed
> > by the following patches.
> >
> > No ID register has the structure yet and the following patches
> > will add the id_reg_info for some ID registers.
> >
> > Signed-off-by: Reiji Watanabe <reijiw@google.com>
> > ---
> >  arch/arm64/kvm/sys_regs.c | 243 ++++++++++++++++++++++++++++++++++++--
> >  1 file changed, 230 insertions(+), 13 deletions(-)
> >
> > diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> > index 80dc62f98ef0..971018288bee 100644
> > --- a/arch/arm64/kvm/sys_regs.c
> > +++ b/arch/arm64/kvm/sys_regs.c
> > @@ -265,6 +265,101 @@ static bool trap_raz_wi(struct kvm_vcpu *vcpu,
> >                 return read_zero(vcpu, p);
> >  }
> >
> > +struct id_reg_info {
> > +       u32     sys_reg;        /* Register ID */
>
> Nit: Why not have the comment above, as for the other fields of this struct?

Yes, I will fix that.
(I placed the comment there just because the comment was short
 enough to place it on the right side)

>
> > +
> > +       /*
> > +        * Limit value of the register for a vcpu. The value is the sanitized
> > +        * system value with bits cleared for unsupported features for the
> > +        * guest.
> > +        */
>
> I think that rather than saying "with bits cleared for unsupported
> features", it might be better to rephrase along the lines of "with
> bits indicating" or "set/cleared to indicate", so that it applies to
> signed fields as well.

Thank you for the suggestion ! I will fix the comment.

>
> > +       u64     vcpu_limit_val;
> > +
> > +       /* Fields that we don't to validate by arm64_check_features. */
>
> Nit: Remove "to".

I will fix it.

>
> > +       u64     ignore_mask;
> > +
> > +       /* Initialization function of the id_reg_info */
> > +       void (*init)(struct id_reg_info *id_reg);
> > +       /*
> > +        * This is an optional ID register specific validation function.
> > +        * When userspace tries to set the ID register, arm64_check_features()
> > +        * will check if the requested value indicates any features that cannot
> > +        * be supported by KVM on the host.  But, some ID register fields need
> > +        * a special checking, and this function can be used for such fields.
> > +        * e.g. When SVE is configured for a vCPU by KVM_ARM_VCPU_INIT,
> > +        * ID_AA64PFR0_EL1.SVE shouldn't be set to 0 for the vCPU.
> > +        * The validation function for ID_AA64PFR0_EL1 could be used to check
> > +        * the field is consistent with SVE configuration.
> > +        */
> > +       int (*validate)(struct kvm_vcpu *vcpu, const struct id_reg_info *id_reg,
> > +                       u64 val);
> > +
> > +       /*
> > +        * Return a bitmask of the vCPU's ID register fields that are not
> > +        * synced with saved (per VM) ID register value, which usually
> > +        * indicates opt-in CPU features that is not configured for the vCPU.
>
> Nit: s/is/are

I will fix it.

>
>
> > +        * ID registers are saved per VM, but some opt-in CPU features can
> > +        * be configured per vCPU.  The saved (per VM) values for such
> > +        * features are for vCPUs with the features (and zero for
> > +        * vCPUs without the features).
> > +        * Return value of this function is used to handle such fields
> > +        * for per vCPU ID register read/write request with saved per VM
> > +        * ID register.  See the __write_id_reg's comment for more detail.
> > +        */
> > +       u64 (*vcpu_mask)(const struct kvm_vcpu *vcpu,
> > +                        const struct id_reg_info *id_reg);
> > +};
> > +
> > +static void id_reg_info_init(struct id_reg_info *id_reg)
> > +{
> > +       id_reg->vcpu_limit_val = read_sanitised_ftr_reg(id_reg->sys_reg);
> > +       if (id_reg->init)
>
> If there is an id_reg then the init function is necessary, isn't it?
> Otherwise it doesn't seem to add more than the default handling. If
> that's right, should we instead ensure that init is always set?

The init function is optional (maybe I should comment that).

>
> > +               id_reg->init(id_reg);
> > +}
> > +
> > +/*
> > + * An ID register that needs special handling to control the value for the
> > + * guest must have its own id_reg_info in id_reg_info_table.
> > + * (i.e. the reset value is different from the host's sanitized value,
> > + * the value is affected by opt-in features, some fields need specific
> > + * validation, etc.)
> > + */
> > +#define        GET_ID_REG_INFO(id)     (id_reg_info_table[IDREG_IDX(id)])
> > +static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {};
> > +
> > +static int validate_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
> > +{
> > +       const struct id_reg_info *id_reg = GET_ID_REG_INFO(id);
> > +       u64 limit, tmp_val;
> > +       int err;
> > +
> > +       if (id_reg) {
> > +               limit = id_reg->vcpu_limit_val;
> > +               /*
> > +                * Replace the fields that are indicated in ignore_mask with
> > +                * the value in the limit to not have arm64_check_features()
> > +                * check the field in @val.
> > +                */
> > +               tmp_val = val & ~id_reg->ignore_mask;
> > +               tmp_val |= (limit & id_reg->ignore_mask);
> > +       } else {
> > +               limit = read_sanitised_ftr_reg(id);
> > +               tmp_val = val;
> > +       }
> > +
> > +       /* Check if the value indicates any feature that is not in the limit. */
> > +       err = arm64_check_features(id, tmp_val, limit);
> > +       if (err)
> > +               return err;
> > +
> > +       if (id_reg && id_reg->validate)
> > +               /* Run the ID register specific validity check. */
> > +               err = id_reg->validate(vcpu, id_reg, val);
> > +
> > +       return err;
> > +}
> > +
> >  /*
> >   * ARMv8.1 mandates at least a trivial LORegion implementation, where all the
> >   * RW registers are RES0 (which we can implement as RAZ/WI). On an ARMv8.0
> > @@ -1061,9 +1156,81 @@ static bool access_arch_timer(struct kvm_vcpu *vcpu,
> >         return true;
> >  }
> >
> > +static u64 read_kvm_id_reg(struct kvm *kvm, u32 id)
> > +{
> > +       return kvm->arch.id_regs[IDREG_IDX(id)];
> > +}
> > +
> > +static int modify_kvm_id_reg(struct kvm *kvm, u32 id, u64 val,
> > +                            u64 preserve_mask)
> > +{
> > +       u64 old, new;
> > +
> > +       mutex_lock(&kvm->lock);
> > +
> > +       old = kvm->arch.id_regs[IDREG_IDX(id)];
> > +
> > +       /* Preserve the value at the bit position set in preserve_mask */
> > +       new = old & preserve_mask;
> > +       new |= (val & ~preserve_mask);
> > +
> > +       /* Don't allow to modify ID register value after KVM_RUN on any vCPUs */
> > +       if (kvm_vm_has_started(kvm) && new != old)
> > +               return -EBUSY;
>
> This path doesn't release the lock. I see that it gets fixed in a
> future patch, but it might be good to fix it here...

Thank you for catching it ! Yes, I will fix this.

>
> > +
> > +       WRITE_ONCE(kvm->arch.id_regs[IDREG_IDX(id)], new);
> > +       mutex_unlock(&kvm->lock);
> > +
> > +       return 0;
> > +}
> > +
> > +static int write_kvm_id_reg(struct kvm *kvm, u32 id, u64 val)
> > +{
> > +       return modify_kvm_id_reg(kvm, id, val, 0);
> > +}
> > +
> > +
> > +/*
> > + * KVM basically forces all vCPUs of the guest to have a uniform value for
> > + * each ID register (, which means KVM_SET_ONE_REG for a vCPU affects all
> > + * the vCPUs of the guest), and the id_regs[] of kvm_arch holds values
> > + * of ID registers for the guest.  However, there is an exception for
> > + * ID register fields corresponding to CPU features that can be
> > + * configured per vCPU by KVM_ARM_VCPU_INIT, or etc (e.g. PMUv3, SVE, etc).
> > + * For such fields, all vCPUs that have the feature will have a non-zero
> > + * uniform value (, which can be updated by userspace), but the vCPUs that
>
> Nit: uneven nesting of parentheses and commas :)

I will fix it.


> > + * don't have the feature will have zero for the fields.
> > + * Values that @id_regs holds are for vCPUs that have such features.  So,
> > + * to get the ID register value for a vCPU that doesn't have those features,
> > + * the corresponding fields in id_regs[] needs to be cleared.
> > + * A bitmask of the fields are provided by id_reg_info's vcpu_mask(), and
> > + * __write_id_reg() and __read_id_reg() take care of those fields using
> > + * the bitmask.
> > + */
> > +static int __write_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
> > +{
> > +       const struct id_reg_info *id_reg = GET_ID_REG_INFO(id);
> > +       u64 mask = 0;
> > +
> > +       if (id_reg && id_reg->vcpu_mask)
> > +               mask = id_reg->vcpu_mask(vcpu, id_reg);
> > +
> > +       /*
> > +        * Update the ID register for the guest with @val, except for fields
> > +        * that are set in the mask, which indicates fields for opt-in
> > +        * features that are not configured for the vCPU.
> > +        */
> > +       return modify_kvm_id_reg(vcpu->kvm, id, val, mask);
> > +}
> > +
> >  static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
> >  {
> > -       u64 val = vcpu->kvm->arch.id_regs[IDREG_IDX(id)];
> > +       const struct id_reg_info *id_reg = GET_ID_REG_INFO(id);
> > +       u64 val = read_kvm_id_reg(vcpu->kvm, id);
> > +
> > +       if (id_reg && id_reg->vcpu_mask)
> > +               /* Clear fields for opt-in features that are not configured. */
> > +               val &= ~(id_reg->vcpu_mask(vcpu, id_reg));
> >         switch (id) {
> >         case SYS_ID_AA64PFR0_EL1:
> > @@ -1222,12 +1389,7 @@ static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
> >         return 0;
> >  }
> >
> > -/*
> > - * cpufeature ID register user accessors
> > - *
> > - * For now, these registers are immutable for userspace, so for set_id_reg()
> > - * we don't allow the effective value to be changed.
> > - */
> > +/* cpufeature ID register user accessors */
> >  static int __get_id_reg(const struct kvm_vcpu *vcpu,
> >                         const struct sys_reg_desc *rd, void __user *uaddr,
> >                         bool raz)
> > @@ -1238,11 +1400,31 @@ static int __get_id_reg(const struct kvm_vcpu *vcpu,
> >         return reg_to_user(uaddr, &val, id);
> >  }
> >
> > +/*
> > + * Check if the given id indicates AArch32 ID register encoding.
> > + */
> > +static bool is_aarch32_id_reg(u32 id)
> > +{
> > +       u32 crm, op2;
> > +
> > +       if (!is_id_reg(id))
> > +               return false;
> > +
> > +       crm = sys_reg_CRm(id);
> > +       op2 = sys_reg_Op2(id);
> > +       if (crm == 1 || crm == 2 || (crm == 3 && (op2 != 3 && op2 != 7)))
>
> Consistent with the Arm ARM "Table D12-2 System instruction encodings
> for non-Debug System register accesses"
>
> > +               /* AArch32 ID register */
> > +               return true;
> > +
> > +       return false;
> > +}
> > +
> >  static int __set_id_reg(struct kvm_vcpu *vcpu,
> >                         const struct sys_reg_desc *rd, void __user *uaddr,
> >                         bool raz)
> >  {
> >         const u64 id = sys_reg_to_index(rd);
> > +       u32 encoding = reg_to_encoding(rd);
> >         int err;
> >         u64 val;
> >
> > @@ -1250,11 +1432,28 @@ static int __set_id_reg(struct kvm_vcpu *vcpu,
> >         if (err)
> >                 return err;
> >
> > -       /* This is what we mean by invariant: you can't change it. */
> > -       if (val != read_id_reg(vcpu, rd, raz))
> > +       if (val == read_id_reg(vcpu, rd, raz))
> > +               /* The value is same as the current value. Nothing to do. */
> > +               return 0;
> > +
> > +       /*
> > +        * Don't allow to modify the register's value if the register is raz,
> > +        * or the reg doesn't have the id_reg_info.
> > +        */
> > +       if (raz || !GET_ID_REG_INFO(encoding))
> >                 return -EINVAL;
> >
> > -       return 0;
> > +       /*
> > +        * Skip the validation of AArch32 ID registers if the system doesn't
> > +        * 32bit EL0 (their value are UNKNOWN).
> > +        */
> > +       if (system_supports_32bit_el0() || !is_aarch32_id_reg(encoding)) {
> > +               err = validate_id_reg(vcpu, encoding, val);
> > +               if (err)
> > +                       return err;
> > +       }
> > +
> > +       return __write_id_reg(vcpu, encoding, val);
> >  }
> >
> >  static int get_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
> > @@ -2816,6 +3015,20 @@ int kvm_arm_copy_sys_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
> >         return write_demux_regids(uindices);
> >  }
> >
> > +static void id_reg_info_init_all(void)
> > +{
> > +       int i;
> > +       struct id_reg_info *id_reg;
> > +
> > +       for (i = 0; i < ARRAY_SIZE(id_reg_info_table); i++) {
> > +               id_reg = (struct id_reg_info *)id_reg_info_table[i];
> > +               if (!id_reg)
> > +                       continue;
> > +
> > +               id_reg_info_init(id_reg);
> > +       }
> > +}
> > +
> >  void kvm_sys_reg_table_init(void)
> >  {
> >         unsigned int i;
> > @@ -2850,6 +3063,8 @@ void kvm_sys_reg_table_init(void)
> >                         break;
> >         /* Clear all higher bits. */
> >         cache_levels &= (1 << (i*3))-1;
> > +
> > +       id_reg_info_init_all();
> >  }
> >
> >  /*
> > @@ -2862,11 +3077,12 @@ void set_default_id_regs(struct kvm *kvm)
> >         u32 id;
> >         const struct sys_reg_desc *rd;
> >         u64 val;
> > +       struct id_reg_info *idr;
> >
> >         for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
> >                 rd = &sys_reg_descs[i];
> >                 if (rd->access != access_id_reg)
> > -                       /* Not ID register, or hidden/reserved ID register */
> > +                       /* Not ID register or hidden/reserved ID register */
> >                         continue;
> >
> >                 id = reg_to_encoding(rd);
> > @@ -2874,7 +3090,8 @@ void set_default_id_regs(struct kvm *kvm)
> >                         /* Shouldn't happen */
> >                         continue;
> >
> > -               val = read_sanitised_ftr_reg(id);
> > -               kvm->arch.id_regs[IDREG_IDX(id)] = val;
> > +               idr = GET_ID_REG_INFO(id);
> > +               val = idr ? idr->vcpu_limit_val : read_sanitised_ftr_reg(id);
> > +               (void)write_kvm_id_reg(kvm, id, val);
>
> Rather than ignoring the return value of write_kvm_id_reg(), wouldn't
> it be better if set_default_id_regs were to propagate it back to
> kvm_arch_init_vm in case there's a problem?

Since write_kvm_id_reg() should never return an error for this
case, returning an error to kvm_arch_init_vm() adds a practically
unnecessary error handling, which I would like to avoid.
So, how about putting WARN_ON_ONCE on its return value ?

Thanks,
Reiji

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 04/26] KVM: arm64: Make ID_AA64PFR0_EL1 writable
  2022-01-24 16:51     ` Fuad Tabba
  (?)
@ 2022-01-27  4:01       ` Reiji Watanabe
  -1 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-27  4:01 UTC (permalink / raw)
  To: Fuad Tabba
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, Linux ARM

 Hi Fuad,

On Mon, Jan 24, 2022 at 8:51 AM Fuad Tabba <tabba@google.com> wrote:
>
> Hi Reiji,
>
> On Thu, Jan 6, 2022 at 4:28 AM Reiji Watanabe <reijiw@google.com> wrote:
> >
> > This patch adds id_reg_info for ID_AA64PFR0_EL1 to make it writable by
> > userspace.
> >
> > Return an error if userspace tries to set SVE/GIC field of the register
> > to a value that conflicts with SVE/GIC configuration for the guest.
> > SIMD/FP/SVE fields of the requested value are validated according to
> > Arm ARM.
> >
> > Signed-off-by: Reiji Watanabe <reijiw@google.com>
> > ---
> >  arch/arm64/include/asm/kvm_host.h |   1 +
> >  arch/arm64/include/asm/sysreg.h   |   2 +
> >  arch/arm64/kvm/sys_regs.c         | 177 +++++++++++++++++++-----------
> >  arch/arm64/kvm/vgic/vgic-init.c   |   5 +
> >  4 files changed, 123 insertions(+), 62 deletions(-)
> >
> > diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> > index c789a0137f58..4509f9e7472d 100644
> > --- a/arch/arm64/include/asm/kvm_host.h
> > +++ b/arch/arm64/include/asm/kvm_host.h
> > @@ -749,6 +749,7 @@ long kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
> >                                 struct kvm_arm_copy_mte_tags *copy_tags);
> >
> >  void set_default_id_regs(struct kvm *kvm);
> > +int kvm_set_id_reg_feature(struct kvm *kvm, u32 id, u8 field_shift, u8 fval);
> >
> >  /* Guest/host FPSIMD coordination helpers */
> >  int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu);
> > diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h
> > index 16b3f1a1d468..e26027817171 100644
> > --- a/arch/arm64/include/asm/sysreg.h
> > +++ b/arch/arm64/include/asm/sysreg.h
> > @@ -798,6 +798,7 @@
> >  #define ID_AA64PFR0_ASIMD_SUPPORTED    0x0
> >  #define ID_AA64PFR0_ELx_64BIT_ONLY     0x1
> >  #define ID_AA64PFR0_ELx_32BIT_64BIT    0x2
> > +#define ID_AA64PFR0_GIC3               0x1
> >
> >  /* id_aa64pfr1 */
> >  #define ID_AA64PFR1_MPAMFRAC_SHIFT     16
> > @@ -1197,6 +1198,7 @@
> >  #define ICH_VTR_TDS_MASK       (1 << ICH_VTR_TDS_SHIFT)
> >
> >  #define ARM64_FEATURE_FIELD_BITS       4
> > +#define ARM64_FEATURE_FIELD_MASK       ((1ull << ARM64_FEATURE_FIELD_BITS) - 1)
> >
> >  /* Create a mask for the feature bits of the specified feature. */
> >  #define ARM64_FEATURE_MASK(x)  (GENMASK_ULL(x##_SHIFT + ARM64_FEATURE_FIELD_BITS - 1, x##_SHIFT))
> > diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> > index 971018288bee..1eb5c5fb614f 100644
> > --- a/arch/arm64/kvm/sys_regs.c
> > +++ b/arch/arm64/kvm/sys_regs.c
> > @@ -318,6 +318,92 @@ static void id_reg_info_init(struct id_reg_info *id_reg)
> >                 id_reg->init(id_reg);
> >  }
> >
> > +static int validate_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
> > +                                   const struct id_reg_info *id_reg, u64 val)
> > +{
> > +       int fp, simd;
> > +       unsigned int gic;
> > +       bool vcpu_has_sve = vcpu_has_sve(vcpu);
> > +       bool pfr0_has_sve = id_aa64pfr0_sve(val);
> > +
> > +       simd = cpuid_feature_extract_signed_field(val, ID_AA64PFR0_ASIMD_SHIFT);
> > +       fp = cpuid_feature_extract_signed_field(val, ID_AA64PFR0_FP_SHIFT);
> > +       if (simd != fp)
>
> Why is this the case? Could you add a comment?

Arm ARM says AdvSIMD field must have the same value as the FP field.
I will add the comment.


> > +               return -EINVAL;
> > +
> > +       /* fp must be supported when sve is supported */
> > +       if (pfr0_has_sve && (fp < 0))
> > +               return -EINVAL;
> > +
> > +       /* Check if there is a conflict with a request via KVM_ARM_VCPU_INIT */
> > +       if (vcpu_has_sve ^ pfr0_has_sve)
> > +               return -EPERM;
> > +
> > +       if ((irqchip_in_kernel(vcpu->kvm) &&
> > +            vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3)) {
> > +               gic = cpuid_feature_extract_unsigned_field(val,
> > +                                                       ID_AA64PFR0_GIC_SHIFT);
> > +               if (gic == 0)
> > +                       return -EPERM;
> > +
> > +               if (gic > ID_AA64PFR0_GIC3)
> > +                       return -E2BIG;
> > +       } else {
> > +               u64 mask = ARM64_FEATURE_MASK(ID_AA64PFR0_GIC);
> > +               int err = arm64_check_features(id_reg->sys_reg, val & mask,
> > +                                              id_reg->vcpu_limit_val & mask);
> > +               if (err)
> > +                       return err;
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +static void init_id_aa64pfr0_el1_info(struct id_reg_info *id_reg)
> > +{
> > +       u64 limit = id_reg->vcpu_limit_val;
> > +       unsigned int gic;
> > +
> > +       limit &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_AMU);
> > +       if (!system_supports_sve())
> > +               limit &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_SVE);
> > +
> > +       /*
> > +        * The default is to expose CSV2 == 1 and CSV3 == 1 if the HW
> > +        * isn't affected.  Userspace can override this as long as it
> > +        * doesn't promise the impossible.
> > +        */
> > +       limit &= ~(ARM64_FEATURE_MASK(ID_AA64PFR0_CSV2) |
> > +                  ARM64_FEATURE_MASK(ID_AA64PFR0_CSV3));
> > +
> > +       if (arm64_get_spectre_v2_state() == SPECTRE_UNAFFECTED)
> > +               limit |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_CSV2), 1);
> > +       if (arm64_get_meltdown_state() == SPECTRE_UNAFFECTED)
> > +               limit |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_CSV3), 1);
> > +
> > +       gic = cpuid_feature_extract_unsigned_field(limit, ID_AA64PFR0_GIC_SHIFT);
> > +       if (gic > 1) {
> > +               /* Limit to GICv3.0/4.0 */
> > +               limit &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_GIC);
> > +               limit |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_GIC), ID_AA64PFR0_GIC3);
> > +       }
> > +       id_reg->vcpu_limit_val = limit;
> > +}
> > +
> > +static u64 vcpu_mask_id_aa64pfr0_el1(const struct kvm_vcpu *vcpu,
> > +                                        const struct id_reg_info *idr)
> > +{
> > +       return vcpu_has_sve(vcpu) ? 0 : ARM64_FEATURE_MASK(ID_AA64PFR0_SVE);
> > +}
> > +
> > +static struct id_reg_info id_aa64pfr0_el1_info = {
> > +       .sys_reg = SYS_ID_AA64PFR0_EL1,
> > +       .ignore_mask = ARM64_FEATURE_MASK(ID_AA64PFR0_GIC),
> > +       .init = init_id_aa64pfr0_el1_info,
> > +       .validate = validate_id_aa64pfr0_el1,
> > +       .vcpu_mask = vcpu_mask_id_aa64pfr0_el1,
> > +};
> > +
> >  /*
> >   * An ID register that needs special handling to control the value for the
> >   * guest must have its own id_reg_info in id_reg_info_table.
> > @@ -326,7 +412,9 @@ static void id_reg_info_init(struct id_reg_info *id_reg)
> >   * validation, etc.)
> >   */
> >  #define        GET_ID_REG_INFO(id)     (id_reg_info_table[IDREG_IDX(id)])
> > -static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {};
> > +static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {
> > +       [IDREG_IDX(SYS_ID_AA64PFR0_EL1)] = &id_aa64pfr0_el1_info,
> > +};
> >
> >  static int validate_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
> >  {
> > @@ -1161,12 +1249,12 @@ static u64 read_kvm_id_reg(struct kvm *kvm, u32 id)
> >         return kvm->arch.id_regs[IDREG_IDX(id)];
> >  }
> >
> > -static int modify_kvm_id_reg(struct kvm *kvm, u32 id, u64 val,
> > +static int __modify_kvm_id_reg(struct kvm *kvm, u32 id, u64 val,
> >                              u64 preserve_mask)
> >  {
> >         u64 old, new;
> >
> > -       mutex_lock(&kvm->lock);
> > +       lockdep_assert_held(&kvm->lock);
> >
> >         old = kvm->arch.id_regs[IDREG_IDX(id)];
> >
> > @@ -1179,11 +1267,21 @@ static int modify_kvm_id_reg(struct kvm *kvm, u32 id, u64 val,
> >                 return -EBUSY;
> >
> >         WRITE_ONCE(kvm->arch.id_regs[IDREG_IDX(id)], new);
> > -       mutex_unlock(&kvm->lock);
> >
> >         return 0;
> >  }
> >
> > +static int modify_kvm_id_reg(struct kvm *kvm, u32 id, u64 val,
> > +                            u64 preserve_mask)
> > +{
> > +       int ret;
> > +
> > +       mutex_lock(&kvm->lock);
> > +       ret = __modify_kvm_id_reg(kvm, id, val, preserve_mask);
> > +       mutex_unlock(&kvm->lock);
> > +
> > +       return ret;
> > +}
>
> I think you probably wanted these changes to modify_kvm_id_reg() to go
> into the previous patch rather than in this one.

I will move them into the previous patch.
(I delayed this change until the code actually needed it)

>
>
> >  static int write_kvm_id_reg(struct kvm *kvm, u32 id, u64 val)
> >  {
> >         return modify_kvm_id_reg(kvm, id, val, 0);
> > @@ -1233,20 +1331,6 @@ static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
> >                 val &= ~(id_reg->vcpu_mask(vcpu, id_reg));
> >
> >         switch (id) {
> > -       case SYS_ID_AA64PFR0_EL1:
> > -               if (!vcpu_has_sve(vcpu))
> > -                       val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_SVE);
> > -               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_AMU);
> > -               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_CSV2);
> > -               val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_CSV2), (u64)vcpu->kvm->arch.pfr0_csv2);
> > -               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_CSV3);
> > -               val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_CSV3), (u64)vcpu->kvm->arch.pfr0_csv3);
> > -               if (irqchip_in_kernel(vcpu->kvm) &&
> > -                   vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3) {
> > -                       val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_GIC);
> > -                       val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_GIC), 1);
> > -               }
> > -               break;
> >         case SYS_ID_AA64PFR1_EL1:
> >                 if (!kvm_has_mte(vcpu->kvm))
> >                         val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_MTE);
> > @@ -1347,48 +1431,6 @@ static unsigned int sve_visibility(const struct kvm_vcpu *vcpu,
> >         return REG_HIDDEN;
> >  }
> >
> > -static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
> > -                              const struct sys_reg_desc *rd,
> > -                              const struct kvm_one_reg *reg, void __user *uaddr)
> > -{
> > -       const u64 id = sys_reg_to_index(rd);
> > -       u8 csv2, csv3;
> > -       int err;
> > -       u64 val;
> > -
> > -       err = reg_from_user(&val, uaddr, id);
> > -       if (err)
> > -               return err;
> > -
> > -       /*
> > -        * Allow AA64PFR0_EL1.CSV2 to be set from userspace as long as
> > -        * it doesn't promise more than what is actually provided (the
> > -        * guest could otherwise be covered in ectoplasmic residue).
> > -        */
> > -       csv2 = cpuid_feature_extract_unsigned_field(val, ID_AA64PFR0_CSV2_SHIFT);
> > -       if (csv2 > 1 ||
> > -           (csv2 && arm64_get_spectre_v2_state() != SPECTRE_UNAFFECTED))
> > -               return -EINVAL;
> > -
> > -       /* Same thing for CSV3 */
> > -       csv3 = cpuid_feature_extract_unsigned_field(val, ID_AA64PFR0_CSV3_SHIFT);
> > -       if (csv3 > 1 ||
> > -           (csv3 && arm64_get_meltdown_state() != SPECTRE_UNAFFECTED))
> > -               return -EINVAL;
> > -
> > -       /* We can only differ with CSV[23], and anything else is an error */
> > -       val ^= read_id_reg(vcpu, rd, false);
> > -       val &= ~((0xFUL << ID_AA64PFR0_CSV2_SHIFT) |
> > -                (0xFUL << ID_AA64PFR0_CSV3_SHIFT));
> > -       if (val)
> > -               return -EINVAL;
> > -
> > -       vcpu->kvm->arch.pfr0_csv2 = csv2;
> > -       vcpu->kvm->arch.pfr0_csv3 = csv3 ;
> > -
> > -       return 0;
> > -}
> > -
> >  /* cpufeature ID register user accessors */
> >  static int __get_id_reg(const struct kvm_vcpu *vcpu,
> >                         const struct sys_reg_desc *rd, void __user *uaddr,
> > @@ -1702,8 +1744,7 @@ static const struct sys_reg_desc sys_reg_descs[] = {
> >
> >         /* AArch64 ID registers */
> >         /* CRm=4 */
> > -       { SYS_DESC(SYS_ID_AA64PFR0_EL1), .access = access_id_reg,
> > -         .get_user = get_id_reg, .set_user = set_id_aa64pfr0_el1, },
> > +       ID_SANITISED(ID_AA64PFR0_EL1),
> >         ID_SANITISED(ID_AA64PFR1_EL1),
> >         ID_UNALLOCATED(4,2),
> >         ID_UNALLOCATED(4,3),
> > @@ -3095,3 +3136,15 @@ void set_default_id_regs(struct kvm *kvm)
> >                 (void)write_kvm_id_reg(kvm, id, val);
> >         }
> >  }
> > +
> > +/*
> > + * Update the ID register's field with @fval for the guest.
> > + * The caller is expected to hold the kvm->lock.
> > + */
> > +int kvm_set_id_reg_feature(struct kvm *kvm, u32 id, u8 field_shift, u8 fval)
> > +{
> > +       u64 val = ((u64)fval & ARM64_FEATURE_FIELD_MASK) << field_shift;
> > +       u64 preserve_mask = ~(ARM64_FEATURE_FIELD_MASK << field_shift);
> > +
> > +       return __modify_kvm_id_reg(kvm, id, val, preserve_mask);
> > +}
>
> This seems to me like it should also be in the previous patch or a
> separate patch.

This is also the same as the previous comment.
I will move them into the previous patch.

>
> > diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c
> > index 0a06d0648970..28d9bf0e178c 100644
> > --- a/arch/arm64/kvm/vgic/vgic-init.c
> > +++ b/arch/arm64/kvm/vgic/vgic-init.c
> > @@ -116,6 +116,11 @@ int kvm_vgic_create(struct kvm *kvm, u32 type)
> >         else
> >                 INIT_LIST_HEAD(&kvm->arch.vgic.rd_regions);
> >
> > +       if (type == KVM_DEV_TYPE_ARM_VGIC_V3)
> > +               /* Set ID_AA64PFR0_EL1.GIC to 1 */
> > +               (void)kvm_set_id_reg_feature(kvm, SYS_ID_AA64PFR0_EL1,
> > +                                    ID_AA64PFR0_GIC3, ID_AA64PFR0_GIC_SHIFT);
> > +
>
> If this fails wouldn't it be better to return the error?

This should never fail because kvm_vgic_create() prevents
userspace from running the first KVM_RUN for any vCPUs
while it calls kvm_set_id_reg_feature().
So, I am thinking of adding WARN_ON_ONCE() for the return value
rather than adding an unnecessary error handling.

Thanks,
Reiji

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 04/26] KVM: arm64: Make ID_AA64PFR0_EL1 writable
@ 2022-01-27  4:01       ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-27  4:01 UTC (permalink / raw)
  To: Fuad Tabba
  Cc: kvm, Marc Zyngier, Peter Shier, Paolo Bonzini, Will Deacon,
	kvmarm, Linux ARM

 Hi Fuad,

On Mon, Jan 24, 2022 at 8:51 AM Fuad Tabba <tabba@google.com> wrote:
>
> Hi Reiji,
>
> On Thu, Jan 6, 2022 at 4:28 AM Reiji Watanabe <reijiw@google.com> wrote:
> >
> > This patch adds id_reg_info for ID_AA64PFR0_EL1 to make it writable by
> > userspace.
> >
> > Return an error if userspace tries to set SVE/GIC field of the register
> > to a value that conflicts with SVE/GIC configuration for the guest.
> > SIMD/FP/SVE fields of the requested value are validated according to
> > Arm ARM.
> >
> > Signed-off-by: Reiji Watanabe <reijiw@google.com>
> > ---
> >  arch/arm64/include/asm/kvm_host.h |   1 +
> >  arch/arm64/include/asm/sysreg.h   |   2 +
> >  arch/arm64/kvm/sys_regs.c         | 177 +++++++++++++++++++-----------
> >  arch/arm64/kvm/vgic/vgic-init.c   |   5 +
> >  4 files changed, 123 insertions(+), 62 deletions(-)
> >
> > diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> > index c789a0137f58..4509f9e7472d 100644
> > --- a/arch/arm64/include/asm/kvm_host.h
> > +++ b/arch/arm64/include/asm/kvm_host.h
> > @@ -749,6 +749,7 @@ long kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
> >                                 struct kvm_arm_copy_mte_tags *copy_tags);
> >
> >  void set_default_id_regs(struct kvm *kvm);
> > +int kvm_set_id_reg_feature(struct kvm *kvm, u32 id, u8 field_shift, u8 fval);
> >
> >  /* Guest/host FPSIMD coordination helpers */
> >  int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu);
> > diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h
> > index 16b3f1a1d468..e26027817171 100644
> > --- a/arch/arm64/include/asm/sysreg.h
> > +++ b/arch/arm64/include/asm/sysreg.h
> > @@ -798,6 +798,7 @@
> >  #define ID_AA64PFR0_ASIMD_SUPPORTED    0x0
> >  #define ID_AA64PFR0_ELx_64BIT_ONLY     0x1
> >  #define ID_AA64PFR0_ELx_32BIT_64BIT    0x2
> > +#define ID_AA64PFR0_GIC3               0x1
> >
> >  /* id_aa64pfr1 */
> >  #define ID_AA64PFR1_MPAMFRAC_SHIFT     16
> > @@ -1197,6 +1198,7 @@
> >  #define ICH_VTR_TDS_MASK       (1 << ICH_VTR_TDS_SHIFT)
> >
> >  #define ARM64_FEATURE_FIELD_BITS       4
> > +#define ARM64_FEATURE_FIELD_MASK       ((1ull << ARM64_FEATURE_FIELD_BITS) - 1)
> >
> >  /* Create a mask for the feature bits of the specified feature. */
> >  #define ARM64_FEATURE_MASK(x)  (GENMASK_ULL(x##_SHIFT + ARM64_FEATURE_FIELD_BITS - 1, x##_SHIFT))
> > diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> > index 971018288bee..1eb5c5fb614f 100644
> > --- a/arch/arm64/kvm/sys_regs.c
> > +++ b/arch/arm64/kvm/sys_regs.c
> > @@ -318,6 +318,92 @@ static void id_reg_info_init(struct id_reg_info *id_reg)
> >                 id_reg->init(id_reg);
> >  }
> >
> > +static int validate_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
> > +                                   const struct id_reg_info *id_reg, u64 val)
> > +{
> > +       int fp, simd;
> > +       unsigned int gic;
> > +       bool vcpu_has_sve = vcpu_has_sve(vcpu);
> > +       bool pfr0_has_sve = id_aa64pfr0_sve(val);
> > +
> > +       simd = cpuid_feature_extract_signed_field(val, ID_AA64PFR0_ASIMD_SHIFT);
> > +       fp = cpuid_feature_extract_signed_field(val, ID_AA64PFR0_FP_SHIFT);
> > +       if (simd != fp)
>
> Why is this the case? Could you add a comment?

Arm ARM says AdvSIMD field must have the same value as the FP field.
I will add the comment.


> > +               return -EINVAL;
> > +
> > +       /* fp must be supported when sve is supported */
> > +       if (pfr0_has_sve && (fp < 0))
> > +               return -EINVAL;
> > +
> > +       /* Check if there is a conflict with a request via KVM_ARM_VCPU_INIT */
> > +       if (vcpu_has_sve ^ pfr0_has_sve)
> > +               return -EPERM;
> > +
> > +       if ((irqchip_in_kernel(vcpu->kvm) &&
> > +            vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3)) {
> > +               gic = cpuid_feature_extract_unsigned_field(val,
> > +                                                       ID_AA64PFR0_GIC_SHIFT);
> > +               if (gic == 0)
> > +                       return -EPERM;
> > +
> > +               if (gic > ID_AA64PFR0_GIC3)
> > +                       return -E2BIG;
> > +       } else {
> > +               u64 mask = ARM64_FEATURE_MASK(ID_AA64PFR0_GIC);
> > +               int err = arm64_check_features(id_reg->sys_reg, val & mask,
> > +                                              id_reg->vcpu_limit_val & mask);
> > +               if (err)
> > +                       return err;
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +static void init_id_aa64pfr0_el1_info(struct id_reg_info *id_reg)
> > +{
> > +       u64 limit = id_reg->vcpu_limit_val;
> > +       unsigned int gic;
> > +
> > +       limit &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_AMU);
> > +       if (!system_supports_sve())
> > +               limit &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_SVE);
> > +
> > +       /*
> > +        * The default is to expose CSV2 == 1 and CSV3 == 1 if the HW
> > +        * isn't affected.  Userspace can override this as long as it
> > +        * doesn't promise the impossible.
> > +        */
> > +       limit &= ~(ARM64_FEATURE_MASK(ID_AA64PFR0_CSV2) |
> > +                  ARM64_FEATURE_MASK(ID_AA64PFR0_CSV3));
> > +
> > +       if (arm64_get_spectre_v2_state() == SPECTRE_UNAFFECTED)
> > +               limit |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_CSV2), 1);
> > +       if (arm64_get_meltdown_state() == SPECTRE_UNAFFECTED)
> > +               limit |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_CSV3), 1);
> > +
> > +       gic = cpuid_feature_extract_unsigned_field(limit, ID_AA64PFR0_GIC_SHIFT);
> > +       if (gic > 1) {
> > +               /* Limit to GICv3.0/4.0 */
> > +               limit &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_GIC);
> > +               limit |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_GIC), ID_AA64PFR0_GIC3);
> > +       }
> > +       id_reg->vcpu_limit_val = limit;
> > +}
> > +
> > +static u64 vcpu_mask_id_aa64pfr0_el1(const struct kvm_vcpu *vcpu,
> > +                                        const struct id_reg_info *idr)
> > +{
> > +       return vcpu_has_sve(vcpu) ? 0 : ARM64_FEATURE_MASK(ID_AA64PFR0_SVE);
> > +}
> > +
> > +static struct id_reg_info id_aa64pfr0_el1_info = {
> > +       .sys_reg = SYS_ID_AA64PFR0_EL1,
> > +       .ignore_mask = ARM64_FEATURE_MASK(ID_AA64PFR0_GIC),
> > +       .init = init_id_aa64pfr0_el1_info,
> > +       .validate = validate_id_aa64pfr0_el1,
> > +       .vcpu_mask = vcpu_mask_id_aa64pfr0_el1,
> > +};
> > +
> >  /*
> >   * An ID register that needs special handling to control the value for the
> >   * guest must have its own id_reg_info in id_reg_info_table.
> > @@ -326,7 +412,9 @@ static void id_reg_info_init(struct id_reg_info *id_reg)
> >   * validation, etc.)
> >   */
> >  #define        GET_ID_REG_INFO(id)     (id_reg_info_table[IDREG_IDX(id)])
> > -static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {};
> > +static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {
> > +       [IDREG_IDX(SYS_ID_AA64PFR0_EL1)] = &id_aa64pfr0_el1_info,
> > +};
> >
> >  static int validate_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
> >  {
> > @@ -1161,12 +1249,12 @@ static u64 read_kvm_id_reg(struct kvm *kvm, u32 id)
> >         return kvm->arch.id_regs[IDREG_IDX(id)];
> >  }
> >
> > -static int modify_kvm_id_reg(struct kvm *kvm, u32 id, u64 val,
> > +static int __modify_kvm_id_reg(struct kvm *kvm, u32 id, u64 val,
> >                              u64 preserve_mask)
> >  {
> >         u64 old, new;
> >
> > -       mutex_lock(&kvm->lock);
> > +       lockdep_assert_held(&kvm->lock);
> >
> >         old = kvm->arch.id_regs[IDREG_IDX(id)];
> >
> > @@ -1179,11 +1267,21 @@ static int modify_kvm_id_reg(struct kvm *kvm, u32 id, u64 val,
> >                 return -EBUSY;
> >
> >         WRITE_ONCE(kvm->arch.id_regs[IDREG_IDX(id)], new);
> > -       mutex_unlock(&kvm->lock);
> >
> >         return 0;
> >  }
> >
> > +static int modify_kvm_id_reg(struct kvm *kvm, u32 id, u64 val,
> > +                            u64 preserve_mask)
> > +{
> > +       int ret;
> > +
> > +       mutex_lock(&kvm->lock);
> > +       ret = __modify_kvm_id_reg(kvm, id, val, preserve_mask);
> > +       mutex_unlock(&kvm->lock);
> > +
> > +       return ret;
> > +}
>
> I think you probably wanted these changes to modify_kvm_id_reg() to go
> into the previous patch rather than in this one.

I will move them into the previous patch.
(I delayed this change until the code actually needed it)

>
>
> >  static int write_kvm_id_reg(struct kvm *kvm, u32 id, u64 val)
> >  {
> >         return modify_kvm_id_reg(kvm, id, val, 0);
> > @@ -1233,20 +1331,6 @@ static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
> >                 val &= ~(id_reg->vcpu_mask(vcpu, id_reg));
> >
> >         switch (id) {
> > -       case SYS_ID_AA64PFR0_EL1:
> > -               if (!vcpu_has_sve(vcpu))
> > -                       val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_SVE);
> > -               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_AMU);
> > -               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_CSV2);
> > -               val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_CSV2), (u64)vcpu->kvm->arch.pfr0_csv2);
> > -               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_CSV3);
> > -               val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_CSV3), (u64)vcpu->kvm->arch.pfr0_csv3);
> > -               if (irqchip_in_kernel(vcpu->kvm) &&
> > -                   vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3) {
> > -                       val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_GIC);
> > -                       val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_GIC), 1);
> > -               }
> > -               break;
> >         case SYS_ID_AA64PFR1_EL1:
> >                 if (!kvm_has_mte(vcpu->kvm))
> >                         val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_MTE);
> > @@ -1347,48 +1431,6 @@ static unsigned int sve_visibility(const struct kvm_vcpu *vcpu,
> >         return REG_HIDDEN;
> >  }
> >
> > -static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
> > -                              const struct sys_reg_desc *rd,
> > -                              const struct kvm_one_reg *reg, void __user *uaddr)
> > -{
> > -       const u64 id = sys_reg_to_index(rd);
> > -       u8 csv2, csv3;
> > -       int err;
> > -       u64 val;
> > -
> > -       err = reg_from_user(&val, uaddr, id);
> > -       if (err)
> > -               return err;
> > -
> > -       /*
> > -        * Allow AA64PFR0_EL1.CSV2 to be set from userspace as long as
> > -        * it doesn't promise more than what is actually provided (the
> > -        * guest could otherwise be covered in ectoplasmic residue).
> > -        */
> > -       csv2 = cpuid_feature_extract_unsigned_field(val, ID_AA64PFR0_CSV2_SHIFT);
> > -       if (csv2 > 1 ||
> > -           (csv2 && arm64_get_spectre_v2_state() != SPECTRE_UNAFFECTED))
> > -               return -EINVAL;
> > -
> > -       /* Same thing for CSV3 */
> > -       csv3 = cpuid_feature_extract_unsigned_field(val, ID_AA64PFR0_CSV3_SHIFT);
> > -       if (csv3 > 1 ||
> > -           (csv3 && arm64_get_meltdown_state() != SPECTRE_UNAFFECTED))
> > -               return -EINVAL;
> > -
> > -       /* We can only differ with CSV[23], and anything else is an error */
> > -       val ^= read_id_reg(vcpu, rd, false);
> > -       val &= ~((0xFUL << ID_AA64PFR0_CSV2_SHIFT) |
> > -                (0xFUL << ID_AA64PFR0_CSV3_SHIFT));
> > -       if (val)
> > -               return -EINVAL;
> > -
> > -       vcpu->kvm->arch.pfr0_csv2 = csv2;
> > -       vcpu->kvm->arch.pfr0_csv3 = csv3 ;
> > -
> > -       return 0;
> > -}
> > -
> >  /* cpufeature ID register user accessors */
> >  static int __get_id_reg(const struct kvm_vcpu *vcpu,
> >                         const struct sys_reg_desc *rd, void __user *uaddr,
> > @@ -1702,8 +1744,7 @@ static const struct sys_reg_desc sys_reg_descs[] = {
> >
> >         /* AArch64 ID registers */
> >         /* CRm=4 */
> > -       { SYS_DESC(SYS_ID_AA64PFR0_EL1), .access = access_id_reg,
> > -         .get_user = get_id_reg, .set_user = set_id_aa64pfr0_el1, },
> > +       ID_SANITISED(ID_AA64PFR0_EL1),
> >         ID_SANITISED(ID_AA64PFR1_EL1),
> >         ID_UNALLOCATED(4,2),
> >         ID_UNALLOCATED(4,3),
> > @@ -3095,3 +3136,15 @@ void set_default_id_regs(struct kvm *kvm)
> >                 (void)write_kvm_id_reg(kvm, id, val);
> >         }
> >  }
> > +
> > +/*
> > + * Update the ID register's field with @fval for the guest.
> > + * The caller is expected to hold the kvm->lock.
> > + */
> > +int kvm_set_id_reg_feature(struct kvm *kvm, u32 id, u8 field_shift, u8 fval)
> > +{
> > +       u64 val = ((u64)fval & ARM64_FEATURE_FIELD_MASK) << field_shift;
> > +       u64 preserve_mask = ~(ARM64_FEATURE_FIELD_MASK << field_shift);
> > +
> > +       return __modify_kvm_id_reg(kvm, id, val, preserve_mask);
> > +}
>
> This seems to me like it should also be in the previous patch or a
> separate patch.

This is also the same as the previous comment.
I will move them into the previous patch.

>
> > diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c
> > index 0a06d0648970..28d9bf0e178c 100644
> > --- a/arch/arm64/kvm/vgic/vgic-init.c
> > +++ b/arch/arm64/kvm/vgic/vgic-init.c
> > @@ -116,6 +116,11 @@ int kvm_vgic_create(struct kvm *kvm, u32 type)
> >         else
> >                 INIT_LIST_HEAD(&kvm->arch.vgic.rd_regions);
> >
> > +       if (type == KVM_DEV_TYPE_ARM_VGIC_V3)
> > +               /* Set ID_AA64PFR0_EL1.GIC to 1 */
> > +               (void)kvm_set_id_reg_feature(kvm, SYS_ID_AA64PFR0_EL1,
> > +                                    ID_AA64PFR0_GIC3, ID_AA64PFR0_GIC_SHIFT);
> > +
>
> If this fails wouldn't it be better to return the error?

This should never fail because kvm_vgic_create() prevents
userspace from running the first KVM_RUN for any vCPUs
while it calls kvm_set_id_reg_feature().
So, I am thinking of adding WARN_ON_ONCE() for the return value
rather than adding an unnecessary error handling.

Thanks,
Reiji
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 04/26] KVM: arm64: Make ID_AA64PFR0_EL1 writable
@ 2022-01-27  4:01       ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-27  4:01 UTC (permalink / raw)
  To: Fuad Tabba
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, Linux ARM

 Hi Fuad,

On Mon, Jan 24, 2022 at 8:51 AM Fuad Tabba <tabba@google.com> wrote:
>
> Hi Reiji,
>
> On Thu, Jan 6, 2022 at 4:28 AM Reiji Watanabe <reijiw@google.com> wrote:
> >
> > This patch adds id_reg_info for ID_AA64PFR0_EL1 to make it writable by
> > userspace.
> >
> > Return an error if userspace tries to set SVE/GIC field of the register
> > to a value that conflicts with SVE/GIC configuration for the guest.
> > SIMD/FP/SVE fields of the requested value are validated according to
> > Arm ARM.
> >
> > Signed-off-by: Reiji Watanabe <reijiw@google.com>
> > ---
> >  arch/arm64/include/asm/kvm_host.h |   1 +
> >  arch/arm64/include/asm/sysreg.h   |   2 +
> >  arch/arm64/kvm/sys_regs.c         | 177 +++++++++++++++++++-----------
> >  arch/arm64/kvm/vgic/vgic-init.c   |   5 +
> >  4 files changed, 123 insertions(+), 62 deletions(-)
> >
> > diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> > index c789a0137f58..4509f9e7472d 100644
> > --- a/arch/arm64/include/asm/kvm_host.h
> > +++ b/arch/arm64/include/asm/kvm_host.h
> > @@ -749,6 +749,7 @@ long kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
> >                                 struct kvm_arm_copy_mte_tags *copy_tags);
> >
> >  void set_default_id_regs(struct kvm *kvm);
> > +int kvm_set_id_reg_feature(struct kvm *kvm, u32 id, u8 field_shift, u8 fval);
> >
> >  /* Guest/host FPSIMD coordination helpers */
> >  int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu);
> > diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h
> > index 16b3f1a1d468..e26027817171 100644
> > --- a/arch/arm64/include/asm/sysreg.h
> > +++ b/arch/arm64/include/asm/sysreg.h
> > @@ -798,6 +798,7 @@
> >  #define ID_AA64PFR0_ASIMD_SUPPORTED    0x0
> >  #define ID_AA64PFR0_ELx_64BIT_ONLY     0x1
> >  #define ID_AA64PFR0_ELx_32BIT_64BIT    0x2
> > +#define ID_AA64PFR0_GIC3               0x1
> >
> >  /* id_aa64pfr1 */
> >  #define ID_AA64PFR1_MPAMFRAC_SHIFT     16
> > @@ -1197,6 +1198,7 @@
> >  #define ICH_VTR_TDS_MASK       (1 << ICH_VTR_TDS_SHIFT)
> >
> >  #define ARM64_FEATURE_FIELD_BITS       4
> > +#define ARM64_FEATURE_FIELD_MASK       ((1ull << ARM64_FEATURE_FIELD_BITS) - 1)
> >
> >  /* Create a mask for the feature bits of the specified feature. */
> >  #define ARM64_FEATURE_MASK(x)  (GENMASK_ULL(x##_SHIFT + ARM64_FEATURE_FIELD_BITS - 1, x##_SHIFT))
> > diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> > index 971018288bee..1eb5c5fb614f 100644
> > --- a/arch/arm64/kvm/sys_regs.c
> > +++ b/arch/arm64/kvm/sys_regs.c
> > @@ -318,6 +318,92 @@ static void id_reg_info_init(struct id_reg_info *id_reg)
> >                 id_reg->init(id_reg);
> >  }
> >
> > +static int validate_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
> > +                                   const struct id_reg_info *id_reg, u64 val)
> > +{
> > +       int fp, simd;
> > +       unsigned int gic;
> > +       bool vcpu_has_sve = vcpu_has_sve(vcpu);
> > +       bool pfr0_has_sve = id_aa64pfr0_sve(val);
> > +
> > +       simd = cpuid_feature_extract_signed_field(val, ID_AA64PFR0_ASIMD_SHIFT);
> > +       fp = cpuid_feature_extract_signed_field(val, ID_AA64PFR0_FP_SHIFT);
> > +       if (simd != fp)
>
> Why is this the case? Could you add a comment?

Arm ARM says AdvSIMD field must have the same value as the FP field.
I will add the comment.


> > +               return -EINVAL;
> > +
> > +       /* fp must be supported when sve is supported */
> > +       if (pfr0_has_sve && (fp < 0))
> > +               return -EINVAL;
> > +
> > +       /* Check if there is a conflict with a request via KVM_ARM_VCPU_INIT */
> > +       if (vcpu_has_sve ^ pfr0_has_sve)
> > +               return -EPERM;
> > +
> > +       if ((irqchip_in_kernel(vcpu->kvm) &&
> > +            vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3)) {
> > +               gic = cpuid_feature_extract_unsigned_field(val,
> > +                                                       ID_AA64PFR0_GIC_SHIFT);
> > +               if (gic == 0)
> > +                       return -EPERM;
> > +
> > +               if (gic > ID_AA64PFR0_GIC3)
> > +                       return -E2BIG;
> > +       } else {
> > +               u64 mask = ARM64_FEATURE_MASK(ID_AA64PFR0_GIC);
> > +               int err = arm64_check_features(id_reg->sys_reg, val & mask,
> > +                                              id_reg->vcpu_limit_val & mask);
> > +               if (err)
> > +                       return err;
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +static void init_id_aa64pfr0_el1_info(struct id_reg_info *id_reg)
> > +{
> > +       u64 limit = id_reg->vcpu_limit_val;
> > +       unsigned int gic;
> > +
> > +       limit &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_AMU);
> > +       if (!system_supports_sve())
> > +               limit &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_SVE);
> > +
> > +       /*
> > +        * The default is to expose CSV2 == 1 and CSV3 == 1 if the HW
> > +        * isn't affected.  Userspace can override this as long as it
> > +        * doesn't promise the impossible.
> > +        */
> > +       limit &= ~(ARM64_FEATURE_MASK(ID_AA64PFR0_CSV2) |
> > +                  ARM64_FEATURE_MASK(ID_AA64PFR0_CSV3));
> > +
> > +       if (arm64_get_spectre_v2_state() == SPECTRE_UNAFFECTED)
> > +               limit |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_CSV2), 1);
> > +       if (arm64_get_meltdown_state() == SPECTRE_UNAFFECTED)
> > +               limit |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_CSV3), 1);
> > +
> > +       gic = cpuid_feature_extract_unsigned_field(limit, ID_AA64PFR0_GIC_SHIFT);
> > +       if (gic > 1) {
> > +               /* Limit to GICv3.0/4.0 */
> > +               limit &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_GIC);
> > +               limit |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_GIC), ID_AA64PFR0_GIC3);
> > +       }
> > +       id_reg->vcpu_limit_val = limit;
> > +}
> > +
> > +static u64 vcpu_mask_id_aa64pfr0_el1(const struct kvm_vcpu *vcpu,
> > +                                        const struct id_reg_info *idr)
> > +{
> > +       return vcpu_has_sve(vcpu) ? 0 : ARM64_FEATURE_MASK(ID_AA64PFR0_SVE);
> > +}
> > +
> > +static struct id_reg_info id_aa64pfr0_el1_info = {
> > +       .sys_reg = SYS_ID_AA64PFR0_EL1,
> > +       .ignore_mask = ARM64_FEATURE_MASK(ID_AA64PFR0_GIC),
> > +       .init = init_id_aa64pfr0_el1_info,
> > +       .validate = validate_id_aa64pfr0_el1,
> > +       .vcpu_mask = vcpu_mask_id_aa64pfr0_el1,
> > +};
> > +
> >  /*
> >   * An ID register that needs special handling to control the value for the
> >   * guest must have its own id_reg_info in id_reg_info_table.
> > @@ -326,7 +412,9 @@ static void id_reg_info_init(struct id_reg_info *id_reg)
> >   * validation, etc.)
> >   */
> >  #define        GET_ID_REG_INFO(id)     (id_reg_info_table[IDREG_IDX(id)])
> > -static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {};
> > +static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {
> > +       [IDREG_IDX(SYS_ID_AA64PFR0_EL1)] = &id_aa64pfr0_el1_info,
> > +};
> >
> >  static int validate_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
> >  {
> > @@ -1161,12 +1249,12 @@ static u64 read_kvm_id_reg(struct kvm *kvm, u32 id)
> >         return kvm->arch.id_regs[IDREG_IDX(id)];
> >  }
> >
> > -static int modify_kvm_id_reg(struct kvm *kvm, u32 id, u64 val,
> > +static int __modify_kvm_id_reg(struct kvm *kvm, u32 id, u64 val,
> >                              u64 preserve_mask)
> >  {
> >         u64 old, new;
> >
> > -       mutex_lock(&kvm->lock);
> > +       lockdep_assert_held(&kvm->lock);
> >
> >         old = kvm->arch.id_regs[IDREG_IDX(id)];
> >
> > @@ -1179,11 +1267,21 @@ static int modify_kvm_id_reg(struct kvm *kvm, u32 id, u64 val,
> >                 return -EBUSY;
> >
> >         WRITE_ONCE(kvm->arch.id_regs[IDREG_IDX(id)], new);
> > -       mutex_unlock(&kvm->lock);
> >
> >         return 0;
> >  }
> >
> > +static int modify_kvm_id_reg(struct kvm *kvm, u32 id, u64 val,
> > +                            u64 preserve_mask)
> > +{
> > +       int ret;
> > +
> > +       mutex_lock(&kvm->lock);
> > +       ret = __modify_kvm_id_reg(kvm, id, val, preserve_mask);
> > +       mutex_unlock(&kvm->lock);
> > +
> > +       return ret;
> > +}
>
> I think you probably wanted these changes to modify_kvm_id_reg() to go
> into the previous patch rather than in this one.

I will move them into the previous patch.
(I delayed this change until the code actually needed it)

>
>
> >  static int write_kvm_id_reg(struct kvm *kvm, u32 id, u64 val)
> >  {
> >         return modify_kvm_id_reg(kvm, id, val, 0);
> > @@ -1233,20 +1331,6 @@ static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
> >                 val &= ~(id_reg->vcpu_mask(vcpu, id_reg));
> >
> >         switch (id) {
> > -       case SYS_ID_AA64PFR0_EL1:
> > -               if (!vcpu_has_sve(vcpu))
> > -                       val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_SVE);
> > -               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_AMU);
> > -               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_CSV2);
> > -               val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_CSV2), (u64)vcpu->kvm->arch.pfr0_csv2);
> > -               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_CSV3);
> > -               val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_CSV3), (u64)vcpu->kvm->arch.pfr0_csv3);
> > -               if (irqchip_in_kernel(vcpu->kvm) &&
> > -                   vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3) {
> > -                       val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_GIC);
> > -                       val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_GIC), 1);
> > -               }
> > -               break;
> >         case SYS_ID_AA64PFR1_EL1:
> >                 if (!kvm_has_mte(vcpu->kvm))
> >                         val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_MTE);
> > @@ -1347,48 +1431,6 @@ static unsigned int sve_visibility(const struct kvm_vcpu *vcpu,
> >         return REG_HIDDEN;
> >  }
> >
> > -static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
> > -                              const struct sys_reg_desc *rd,
> > -                              const struct kvm_one_reg *reg, void __user *uaddr)
> > -{
> > -       const u64 id = sys_reg_to_index(rd);
> > -       u8 csv2, csv3;
> > -       int err;
> > -       u64 val;
> > -
> > -       err = reg_from_user(&val, uaddr, id);
> > -       if (err)
> > -               return err;
> > -
> > -       /*
> > -        * Allow AA64PFR0_EL1.CSV2 to be set from userspace as long as
> > -        * it doesn't promise more than what is actually provided (the
> > -        * guest could otherwise be covered in ectoplasmic residue).
> > -        */
> > -       csv2 = cpuid_feature_extract_unsigned_field(val, ID_AA64PFR0_CSV2_SHIFT);
> > -       if (csv2 > 1 ||
> > -           (csv2 && arm64_get_spectre_v2_state() != SPECTRE_UNAFFECTED))
> > -               return -EINVAL;
> > -
> > -       /* Same thing for CSV3 */
> > -       csv3 = cpuid_feature_extract_unsigned_field(val, ID_AA64PFR0_CSV3_SHIFT);
> > -       if (csv3 > 1 ||
> > -           (csv3 && arm64_get_meltdown_state() != SPECTRE_UNAFFECTED))
> > -               return -EINVAL;
> > -
> > -       /* We can only differ with CSV[23], and anything else is an error */
> > -       val ^= read_id_reg(vcpu, rd, false);
> > -       val &= ~((0xFUL << ID_AA64PFR0_CSV2_SHIFT) |
> > -                (0xFUL << ID_AA64PFR0_CSV3_SHIFT));
> > -       if (val)
> > -               return -EINVAL;
> > -
> > -       vcpu->kvm->arch.pfr0_csv2 = csv2;
> > -       vcpu->kvm->arch.pfr0_csv3 = csv3 ;
> > -
> > -       return 0;
> > -}
> > -
> >  /* cpufeature ID register user accessors */
> >  static int __get_id_reg(const struct kvm_vcpu *vcpu,
> >                         const struct sys_reg_desc *rd, void __user *uaddr,
> > @@ -1702,8 +1744,7 @@ static const struct sys_reg_desc sys_reg_descs[] = {
> >
> >         /* AArch64 ID registers */
> >         /* CRm=4 */
> > -       { SYS_DESC(SYS_ID_AA64PFR0_EL1), .access = access_id_reg,
> > -         .get_user = get_id_reg, .set_user = set_id_aa64pfr0_el1, },
> > +       ID_SANITISED(ID_AA64PFR0_EL1),
> >         ID_SANITISED(ID_AA64PFR1_EL1),
> >         ID_UNALLOCATED(4,2),
> >         ID_UNALLOCATED(4,3),
> > @@ -3095,3 +3136,15 @@ void set_default_id_regs(struct kvm *kvm)
> >                 (void)write_kvm_id_reg(kvm, id, val);
> >         }
> >  }
> > +
> > +/*
> > + * Update the ID register's field with @fval for the guest.
> > + * The caller is expected to hold the kvm->lock.
> > + */
> > +int kvm_set_id_reg_feature(struct kvm *kvm, u32 id, u8 field_shift, u8 fval)
> > +{
> > +       u64 val = ((u64)fval & ARM64_FEATURE_FIELD_MASK) << field_shift;
> > +       u64 preserve_mask = ~(ARM64_FEATURE_FIELD_MASK << field_shift);
> > +
> > +       return __modify_kvm_id_reg(kvm, id, val, preserve_mask);
> > +}
>
> This seems to me like it should also be in the previous patch or a
> separate patch.

This is also the same as the previous comment.
I will move them into the previous patch.

>
> > diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c
> > index 0a06d0648970..28d9bf0e178c 100644
> > --- a/arch/arm64/kvm/vgic/vgic-init.c
> > +++ b/arch/arm64/kvm/vgic/vgic-init.c
> > @@ -116,6 +116,11 @@ int kvm_vgic_create(struct kvm *kvm, u32 type)
> >         else
> >                 INIT_LIST_HEAD(&kvm->arch.vgic.rd_regions);
> >
> > +       if (type == KVM_DEV_TYPE_ARM_VGIC_V3)
> > +               /* Set ID_AA64PFR0_EL1.GIC to 1 */
> > +               (void)kvm_set_id_reg_feature(kvm, SYS_ID_AA64PFR0_EL1,
> > +                                    ID_AA64PFR0_GIC3, ID_AA64PFR0_GIC_SHIFT);
> > +
>
> If this fails wouldn't it be better to return the error?

This should never fail because kvm_vgic_create() prevents
userspace from running the first KVM_RUN for any vCPUs
while it calls kvm_set_id_reg_feature().
So, I am thinking of adding WARN_ON_ONCE() for the return value
rather than adding an unnecessary error handling.

Thanks,
Reiji

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 14/26] KVM: arm64: Add consistency checking for frac fields of ID registers
  2022-01-24 17:00     ` Fuad Tabba
  (?)
@ 2022-01-27  5:03       ` Reiji Watanabe
  -1 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-27  5:03 UTC (permalink / raw)
  To: Fuad Tabba
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, Linux ARM

Hi Fuad,

On Mon, Jan 24, 2022 at 9:01 AM Fuad Tabba <tabba@google.com> wrote:
>
> Hi Reiji,
>
> On Thu, Jan 6, 2022 at 4:29 AM Reiji Watanabe <reijiw@google.com> wrote:
> >
> > Feature fractional field of an ID register cannot be simply validated
> > at KVM_SET_ONE_REG because its validity depends on its (main) feature
> > field value, which could be in a different ID register (and might be
> > set later).
> > Validate fractional fields at the first KVM_RUN instead.
> >
> > Signed-off-by: Reiji Watanabe <reijiw@google.com>
> > ---
> >  arch/arm64/include/asm/kvm_host.h |   1 +
> >  arch/arm64/kvm/arm.c              |   3 +
> >  arch/arm64/kvm/sys_regs.c         | 116 +++++++++++++++++++++++++++++-
> >  3 files changed, 117 insertions(+), 3 deletions(-)
> >
> > diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> > index 4509f9e7472d..7b3f86bd6a6b 100644
> > --- a/arch/arm64/include/asm/kvm_host.h
> > +++ b/arch/arm64/include/asm/kvm_host.h
> > @@ -750,6 +750,7 @@ long kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
> >
> >  void set_default_id_regs(struct kvm *kvm);
> >  int kvm_set_id_reg_feature(struct kvm *kvm, u32 id, u8 field_shift, u8 fval);
> > +int kvm_id_regs_consistency_check(const struct kvm_vcpu *vcpu);
> >
> >  /* Guest/host FPSIMD coordination helpers */
> >  int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu);
> > diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> > index 5f497a0af254..16fc2ce32069 100644
> > --- a/arch/arm64/kvm/arm.c
> > +++ b/arch/arm64/kvm/arm.c
> > @@ -596,6 +596,9 @@ static int kvm_vcpu_first_run_init(struct kvm_vcpu *vcpu)
> >         if (!kvm_arm_vcpu_is_finalized(vcpu))
> >                 return -EPERM;
> >
> > +       if (!kvm_vm_is_protected(kvm) && kvm_id_regs_consistency_check(vcpu))
> > +               return -EPERM;
> > +
> >         vcpu->arch.has_run_once = true;
> >
> >         kvm_arm_vcpu_init_debug(vcpu);
> > diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> > index ddbeefc3881c..6adb7b04620c 100644
> > --- a/arch/arm64/kvm/sys_regs.c
> > +++ b/arch/arm64/kvm/sys_regs.c
> > @@ -756,9 +756,6 @@ static struct id_reg_info id_aa64pfr0_el1_info = {
> >
> >  static struct id_reg_info id_aa64pfr1_el1_info = {
> >         .sys_reg = SYS_ID_AA64PFR1_EL1,
> > -       .ignore_mask = ARM64_FEATURE_MASK(ID_AA64PFR1_RASFRAC) |
> > -                      ARM64_FEATURE_MASK(ID_AA64PFR1_MPAMFRAC) |
> > -                      ARM64_FEATURE_MASK(ID_AA64PFR1_CSV2FRAC),
> >         .init = init_id_aa64pfr1_el1_info,
> >         .validate = validate_id_aa64pfr1_el1,
> >         .vcpu_mask = vcpu_mask_id_aa64pfr1_el1,
> > @@ -3434,10 +3431,109 @@ int kvm_arm_copy_sys_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
> >         return write_demux_regids(uindices);
> >  }
> >
> > +/* ID register's fractional field information with its feature field. */
> > +struct feature_frac {
> > +       u32     id;
> > +       u32     shift;
> > +       u32     frac_id;
> > +       u32     frac_shift;
> > +       u8      frac_ftr_check;
> > +};
>
> frac_ftr_check doesn't seem to be used. Also, it would be easier to
> read if the ordering of the fields match the ordering you initialize
> them below.

Thank you for catching this.
I will remove frac_ftr_check and change the ordering.

>
> > +
> > +static struct feature_frac feature_frac_table[] = {
> > +       {
> > +               .frac_id = SYS_ID_AA64PFR1_EL1,
> > +               .frac_shift = ID_AA64PFR1_RASFRAC_SHIFT,
> > +               .id = SYS_ID_AA64PFR0_EL1,
> > +               .shift = ID_AA64PFR0_RAS_SHIFT,
> > +       },
> > +       {
> > +               .frac_id = SYS_ID_AA64PFR1_EL1,
> > +               .frac_shift = ID_AA64PFR1_MPAMFRAC_SHIFT,
> > +               .id = SYS_ID_AA64PFR0_EL1,
> > +               .shift = ID_AA64PFR0_MPAM_SHIFT,
> > +       },
> > +       {
> > +               .frac_id = SYS_ID_AA64PFR1_EL1,
> > +               .frac_shift = ID_AA64PFR1_CSV2FRAC_SHIFT,
> > +               .id = SYS_ID_AA64PFR0_EL1,
> > +               .shift = ID_AA64PFR0_CSV2_SHIFT,
> > +       },
> > +};
> > +
> > +/*
> > + * Return non-zero if the feature/fractional fields pair are not
> > + * supported. Return zero otherwise.
> > + * This function validates only the fractional feature field,
> > + * and relies on the fact the feature field is validated before
> > + * through arm64_check_features.
> > + */
> > +static int vcpu_id_reg_feature_frac_check(const struct kvm_vcpu *vcpu,
> > +                                         const struct feature_frac *ftr_frac)
> > +{
> > +       const struct id_reg_info *id_reg;
> > +       u32 id;
> > +       u64 val, lim, mask;
> > +
> > +       /* Check if the feature field value is same as the limit */
> > +       id = ftr_frac->id;
> > +       id_reg = GET_ID_REG_INFO(id);
> > +
> > +       mask = (u64)ARM64_FEATURE_FIELD_MASK << ftr_frac->shift;
> > +       val = __read_id_reg(vcpu, id) & mask;
> > +       lim = id_reg ? id_reg->vcpu_limit_val : read_sanitised_ftr_reg(id);
> > +       lim &= mask;
> > +
> > +       if (val != lim)
> > +               /*
> > +                * The feature level is lower than the limit.
> > +                * Any fractional version should be fine.
> > +                */
> > +               return 0;
> > +
> > +       /* Check the fractional feature field */
> > +       id = ftr_frac->frac_id;
> > +       id_reg = GET_ID_REG_INFO(id);
> > +
> > +       mask = (u64)ARM64_FEATURE_FIELD_MASK << ftr_frac->frac_shift;
> > +       val = __read_id_reg(vcpu, id) & mask;
> > +       lim = id_reg ? id_reg->vcpu_limit_val : read_sanitised_ftr_reg(id);
> > +       lim &= mask;
> > +
> > +       if (val == lim)
> > +               /*
> > +                * Both the feature and fractional fields are the same
> > +                * as limit.
> > +                */
> > +               return 0;
> > +
> > +       return arm64_check_features(id, val, lim);
> > +}
> > +
> > +int kvm_id_regs_consistency_check(const struct kvm_vcpu *vcpu)
>
> Nit: considering that this is only checking the fractional fields,
> should the function name reflect that?

Thank you for the suggestion.
I will change the function name to reflect that.
(There were more checks in older version and I forgot to
change the name...)

Thanks,
Reiji

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 14/26] KVM: arm64: Add consistency checking for frac fields of ID registers
@ 2022-01-27  5:03       ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-27  5:03 UTC (permalink / raw)
  To: Fuad Tabba
  Cc: kvm, Marc Zyngier, Peter Shier, Paolo Bonzini, Will Deacon,
	kvmarm, Linux ARM

Hi Fuad,

On Mon, Jan 24, 2022 at 9:01 AM Fuad Tabba <tabba@google.com> wrote:
>
> Hi Reiji,
>
> On Thu, Jan 6, 2022 at 4:29 AM Reiji Watanabe <reijiw@google.com> wrote:
> >
> > Feature fractional field of an ID register cannot be simply validated
> > at KVM_SET_ONE_REG because its validity depends on its (main) feature
> > field value, which could be in a different ID register (and might be
> > set later).
> > Validate fractional fields at the first KVM_RUN instead.
> >
> > Signed-off-by: Reiji Watanabe <reijiw@google.com>
> > ---
> >  arch/arm64/include/asm/kvm_host.h |   1 +
> >  arch/arm64/kvm/arm.c              |   3 +
> >  arch/arm64/kvm/sys_regs.c         | 116 +++++++++++++++++++++++++++++-
> >  3 files changed, 117 insertions(+), 3 deletions(-)
> >
> > diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> > index 4509f9e7472d..7b3f86bd6a6b 100644
> > --- a/arch/arm64/include/asm/kvm_host.h
> > +++ b/arch/arm64/include/asm/kvm_host.h
> > @@ -750,6 +750,7 @@ long kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
> >
> >  void set_default_id_regs(struct kvm *kvm);
> >  int kvm_set_id_reg_feature(struct kvm *kvm, u32 id, u8 field_shift, u8 fval);
> > +int kvm_id_regs_consistency_check(const struct kvm_vcpu *vcpu);
> >
> >  /* Guest/host FPSIMD coordination helpers */
> >  int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu);
> > diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> > index 5f497a0af254..16fc2ce32069 100644
> > --- a/arch/arm64/kvm/arm.c
> > +++ b/arch/arm64/kvm/arm.c
> > @@ -596,6 +596,9 @@ static int kvm_vcpu_first_run_init(struct kvm_vcpu *vcpu)
> >         if (!kvm_arm_vcpu_is_finalized(vcpu))
> >                 return -EPERM;
> >
> > +       if (!kvm_vm_is_protected(kvm) && kvm_id_regs_consistency_check(vcpu))
> > +               return -EPERM;
> > +
> >         vcpu->arch.has_run_once = true;
> >
> >         kvm_arm_vcpu_init_debug(vcpu);
> > diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> > index ddbeefc3881c..6adb7b04620c 100644
> > --- a/arch/arm64/kvm/sys_regs.c
> > +++ b/arch/arm64/kvm/sys_regs.c
> > @@ -756,9 +756,6 @@ static struct id_reg_info id_aa64pfr0_el1_info = {
> >
> >  static struct id_reg_info id_aa64pfr1_el1_info = {
> >         .sys_reg = SYS_ID_AA64PFR1_EL1,
> > -       .ignore_mask = ARM64_FEATURE_MASK(ID_AA64PFR1_RASFRAC) |
> > -                      ARM64_FEATURE_MASK(ID_AA64PFR1_MPAMFRAC) |
> > -                      ARM64_FEATURE_MASK(ID_AA64PFR1_CSV2FRAC),
> >         .init = init_id_aa64pfr1_el1_info,
> >         .validate = validate_id_aa64pfr1_el1,
> >         .vcpu_mask = vcpu_mask_id_aa64pfr1_el1,
> > @@ -3434,10 +3431,109 @@ int kvm_arm_copy_sys_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
> >         return write_demux_regids(uindices);
> >  }
> >
> > +/* ID register's fractional field information with its feature field. */
> > +struct feature_frac {
> > +       u32     id;
> > +       u32     shift;
> > +       u32     frac_id;
> > +       u32     frac_shift;
> > +       u8      frac_ftr_check;
> > +};
>
> frac_ftr_check doesn't seem to be used. Also, it would be easier to
> read if the ordering of the fields match the ordering you initialize
> them below.

Thank you for catching this.
I will remove frac_ftr_check and change the ordering.

>
> > +
> > +static struct feature_frac feature_frac_table[] = {
> > +       {
> > +               .frac_id = SYS_ID_AA64PFR1_EL1,
> > +               .frac_shift = ID_AA64PFR1_RASFRAC_SHIFT,
> > +               .id = SYS_ID_AA64PFR0_EL1,
> > +               .shift = ID_AA64PFR0_RAS_SHIFT,
> > +       },
> > +       {
> > +               .frac_id = SYS_ID_AA64PFR1_EL1,
> > +               .frac_shift = ID_AA64PFR1_MPAMFRAC_SHIFT,
> > +               .id = SYS_ID_AA64PFR0_EL1,
> > +               .shift = ID_AA64PFR0_MPAM_SHIFT,
> > +       },
> > +       {
> > +               .frac_id = SYS_ID_AA64PFR1_EL1,
> > +               .frac_shift = ID_AA64PFR1_CSV2FRAC_SHIFT,
> > +               .id = SYS_ID_AA64PFR0_EL1,
> > +               .shift = ID_AA64PFR0_CSV2_SHIFT,
> > +       },
> > +};
> > +
> > +/*
> > + * Return non-zero if the feature/fractional fields pair are not
> > + * supported. Return zero otherwise.
> > + * This function validates only the fractional feature field,
> > + * and relies on the fact the feature field is validated before
> > + * through arm64_check_features.
> > + */
> > +static int vcpu_id_reg_feature_frac_check(const struct kvm_vcpu *vcpu,
> > +                                         const struct feature_frac *ftr_frac)
> > +{
> > +       const struct id_reg_info *id_reg;
> > +       u32 id;
> > +       u64 val, lim, mask;
> > +
> > +       /* Check if the feature field value is same as the limit */
> > +       id = ftr_frac->id;
> > +       id_reg = GET_ID_REG_INFO(id);
> > +
> > +       mask = (u64)ARM64_FEATURE_FIELD_MASK << ftr_frac->shift;
> > +       val = __read_id_reg(vcpu, id) & mask;
> > +       lim = id_reg ? id_reg->vcpu_limit_val : read_sanitised_ftr_reg(id);
> > +       lim &= mask;
> > +
> > +       if (val != lim)
> > +               /*
> > +                * The feature level is lower than the limit.
> > +                * Any fractional version should be fine.
> > +                */
> > +               return 0;
> > +
> > +       /* Check the fractional feature field */
> > +       id = ftr_frac->frac_id;
> > +       id_reg = GET_ID_REG_INFO(id);
> > +
> > +       mask = (u64)ARM64_FEATURE_FIELD_MASK << ftr_frac->frac_shift;
> > +       val = __read_id_reg(vcpu, id) & mask;
> > +       lim = id_reg ? id_reg->vcpu_limit_val : read_sanitised_ftr_reg(id);
> > +       lim &= mask;
> > +
> > +       if (val == lim)
> > +               /*
> > +                * Both the feature and fractional fields are the same
> > +                * as limit.
> > +                */
> > +               return 0;
> > +
> > +       return arm64_check_features(id, val, lim);
> > +}
> > +
> > +int kvm_id_regs_consistency_check(const struct kvm_vcpu *vcpu)
>
> Nit: considering that this is only checking the fractional fields,
> should the function name reflect that?

Thank you for the suggestion.
I will change the function name to reflect that.
(There were more checks in older version and I forgot to
change the name...)

Thanks,
Reiji
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 14/26] KVM: arm64: Add consistency checking for frac fields of ID registers
@ 2022-01-27  5:03       ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-27  5:03 UTC (permalink / raw)
  To: Fuad Tabba
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, Linux ARM

Hi Fuad,

On Mon, Jan 24, 2022 at 9:01 AM Fuad Tabba <tabba@google.com> wrote:
>
> Hi Reiji,
>
> On Thu, Jan 6, 2022 at 4:29 AM Reiji Watanabe <reijiw@google.com> wrote:
> >
> > Feature fractional field of an ID register cannot be simply validated
> > at KVM_SET_ONE_REG because its validity depends on its (main) feature
> > field value, which could be in a different ID register (and might be
> > set later).
> > Validate fractional fields at the first KVM_RUN instead.
> >
> > Signed-off-by: Reiji Watanabe <reijiw@google.com>
> > ---
> >  arch/arm64/include/asm/kvm_host.h |   1 +
> >  arch/arm64/kvm/arm.c              |   3 +
> >  arch/arm64/kvm/sys_regs.c         | 116 +++++++++++++++++++++++++++++-
> >  3 files changed, 117 insertions(+), 3 deletions(-)
> >
> > diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> > index 4509f9e7472d..7b3f86bd6a6b 100644
> > --- a/arch/arm64/include/asm/kvm_host.h
> > +++ b/arch/arm64/include/asm/kvm_host.h
> > @@ -750,6 +750,7 @@ long kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
> >
> >  void set_default_id_regs(struct kvm *kvm);
> >  int kvm_set_id_reg_feature(struct kvm *kvm, u32 id, u8 field_shift, u8 fval);
> > +int kvm_id_regs_consistency_check(const struct kvm_vcpu *vcpu);
> >
> >  /* Guest/host FPSIMD coordination helpers */
> >  int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu);
> > diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> > index 5f497a0af254..16fc2ce32069 100644
> > --- a/arch/arm64/kvm/arm.c
> > +++ b/arch/arm64/kvm/arm.c
> > @@ -596,6 +596,9 @@ static int kvm_vcpu_first_run_init(struct kvm_vcpu *vcpu)
> >         if (!kvm_arm_vcpu_is_finalized(vcpu))
> >                 return -EPERM;
> >
> > +       if (!kvm_vm_is_protected(kvm) && kvm_id_regs_consistency_check(vcpu))
> > +               return -EPERM;
> > +
> >         vcpu->arch.has_run_once = true;
> >
> >         kvm_arm_vcpu_init_debug(vcpu);
> > diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> > index ddbeefc3881c..6adb7b04620c 100644
> > --- a/arch/arm64/kvm/sys_regs.c
> > +++ b/arch/arm64/kvm/sys_regs.c
> > @@ -756,9 +756,6 @@ static struct id_reg_info id_aa64pfr0_el1_info = {
> >
> >  static struct id_reg_info id_aa64pfr1_el1_info = {
> >         .sys_reg = SYS_ID_AA64PFR1_EL1,
> > -       .ignore_mask = ARM64_FEATURE_MASK(ID_AA64PFR1_RASFRAC) |
> > -                      ARM64_FEATURE_MASK(ID_AA64PFR1_MPAMFRAC) |
> > -                      ARM64_FEATURE_MASK(ID_AA64PFR1_CSV2FRAC),
> >         .init = init_id_aa64pfr1_el1_info,
> >         .validate = validate_id_aa64pfr1_el1,
> >         .vcpu_mask = vcpu_mask_id_aa64pfr1_el1,
> > @@ -3434,10 +3431,109 @@ int kvm_arm_copy_sys_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
> >         return write_demux_regids(uindices);
> >  }
> >
> > +/* ID register's fractional field information with its feature field. */
> > +struct feature_frac {
> > +       u32     id;
> > +       u32     shift;
> > +       u32     frac_id;
> > +       u32     frac_shift;
> > +       u8      frac_ftr_check;
> > +};
>
> frac_ftr_check doesn't seem to be used. Also, it would be easier to
> read if the ordering of the fields match the ordering you initialize
> them below.

Thank you for catching this.
I will remove frac_ftr_check and change the ordering.

>
> > +
> > +static struct feature_frac feature_frac_table[] = {
> > +       {
> > +               .frac_id = SYS_ID_AA64PFR1_EL1,
> > +               .frac_shift = ID_AA64PFR1_RASFRAC_SHIFT,
> > +               .id = SYS_ID_AA64PFR0_EL1,
> > +               .shift = ID_AA64PFR0_RAS_SHIFT,
> > +       },
> > +       {
> > +               .frac_id = SYS_ID_AA64PFR1_EL1,
> > +               .frac_shift = ID_AA64PFR1_MPAMFRAC_SHIFT,
> > +               .id = SYS_ID_AA64PFR0_EL1,
> > +               .shift = ID_AA64PFR0_MPAM_SHIFT,
> > +       },
> > +       {
> > +               .frac_id = SYS_ID_AA64PFR1_EL1,
> > +               .frac_shift = ID_AA64PFR1_CSV2FRAC_SHIFT,
> > +               .id = SYS_ID_AA64PFR0_EL1,
> > +               .shift = ID_AA64PFR0_CSV2_SHIFT,
> > +       },
> > +};
> > +
> > +/*
> > + * Return non-zero if the feature/fractional fields pair are not
> > + * supported. Return zero otherwise.
> > + * This function validates only the fractional feature field,
> > + * and relies on the fact the feature field is validated before
> > + * through arm64_check_features.
> > + */
> > +static int vcpu_id_reg_feature_frac_check(const struct kvm_vcpu *vcpu,
> > +                                         const struct feature_frac *ftr_frac)
> > +{
> > +       const struct id_reg_info *id_reg;
> > +       u32 id;
> > +       u64 val, lim, mask;
> > +
> > +       /* Check if the feature field value is same as the limit */
> > +       id = ftr_frac->id;
> > +       id_reg = GET_ID_REG_INFO(id);
> > +
> > +       mask = (u64)ARM64_FEATURE_FIELD_MASK << ftr_frac->shift;
> > +       val = __read_id_reg(vcpu, id) & mask;
> > +       lim = id_reg ? id_reg->vcpu_limit_val : read_sanitised_ftr_reg(id);
> > +       lim &= mask;
> > +
> > +       if (val != lim)
> > +               /*
> > +                * The feature level is lower than the limit.
> > +                * Any fractional version should be fine.
> > +                */
> > +               return 0;
> > +
> > +       /* Check the fractional feature field */
> > +       id = ftr_frac->frac_id;
> > +       id_reg = GET_ID_REG_INFO(id);
> > +
> > +       mask = (u64)ARM64_FEATURE_FIELD_MASK << ftr_frac->frac_shift;
> > +       val = __read_id_reg(vcpu, id) & mask;
> > +       lim = id_reg ? id_reg->vcpu_limit_val : read_sanitised_ftr_reg(id);
> > +       lim &= mask;
> > +
> > +       if (val == lim)
> > +               /*
> > +                * Both the feature and fractional fields are the same
> > +                * as limit.
> > +                */
> > +               return 0;
> > +
> > +       return arm64_check_features(id, val, lim);
> > +}
> > +
> > +int kvm_id_regs_consistency_check(const struct kvm_vcpu *vcpu)
>
> Nit: considering that this is only checking the fractional fields,
> should the function name reflect that?

Thank you for the suggestion.
I will change the function name to reflect that.
(There were more checks in older version and I forgot to
change the name...)

Thanks,
Reiji

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 20/26] KVM: arm64: Trap disabled features of ID_AA64PFR0_EL1
  2022-01-24 17:16     ` Fuad Tabba
  (?)
@ 2022-01-27  7:19       ` Reiji Watanabe
  -1 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-27  7:19 UTC (permalink / raw)
  To: Fuad Tabba
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, Linux ARM

Hi Fuad,

On Mon, Jan 24, 2022 at 9:17 AM Fuad Tabba <tabba@google.com> wrote:
>
> Hi Reiji,
>
> On Thu, Jan 6, 2022 at 4:29 AM Reiji Watanabe <reijiw@google.com> wrote:
> >
> > Add feature_config_ctrl for RAS and AMU, which are indicated in
> > ID_AA64PFR0_EL1, to program configuration registers to trap
> > guest's using those features when they are not exposed to the guest.
> >
> > Introduce trap_ras_regs() to change a behavior of guest's access to
> > the registers, which is currently raz/wi, depending on the feature's
> > availability for the guest (and inject undefined instruction
> > exception when guest's RAS register access are trapped and RAS is
> > not exposed to the guest).  In order to keep the current visibility
> > of the RAS registers from userspace (always visible), a visibility
> > function for RAS registers is not added.
> >
> > No code is added for AMU's access/visibility handler because the
> > current code already injects the exception for Guest's AMU register
> > access unconditionally because AMU is never exposed to the guest.
>
> I think it might be code to trap it anyway, in case AMU guest support
> is added in the future.

Yes, I will fix it.
(I forgot to update the comment above...)


> >
> > Signed-off-by: Reiji Watanabe <reijiw@google.com>
> > ---
> >  arch/arm64/kvm/sys_regs.c | 90 +++++++++++++++++++++++++++++++++++----
> >  1 file changed, 82 insertions(+), 8 deletions(-)
> >
> > diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> > index 33893a501475..015d67092d5e 100644
> > --- a/arch/arm64/kvm/sys_regs.c
> > +++ b/arch/arm64/kvm/sys_regs.c
> > @@ -304,6 +304,63 @@ struct feature_config_ctrl {
> >         void    (*trap_activate)(struct kvm_vcpu *vcpu);
> >  };
> >
> > +enum vcpu_config_reg {
> > +       VCPU_HCR_EL2 = 1,
> > +       VCPU_MDCR_EL2,
> > +       VCPU_CPTR_EL2,
> > +};
> > +
> > +static void feature_trap_activate(struct kvm_vcpu *vcpu,
> > +                                 enum vcpu_config_reg cfg_reg,
> > +                                 u64 cfg_set, u64 cfg_clear)
> > +{
> > +       u64 *reg_ptr, reg_val;
> > +
> > +       switch (cfg_reg) {
> > +       case VCPU_HCR_EL2:
> > +               reg_ptr = &vcpu->arch.hcr_el2;
> > +               break;
> > +       case VCPU_MDCR_EL2:
> > +               reg_ptr = &vcpu->arch.mdcr_el2;
> > +               break;
> > +       case VCPU_CPTR_EL2:
> > +               reg_ptr = &vcpu->arch.cptr_el2;
> > +               break;
> > +       }
> > +
> > +       /* Clear/Set fields that are indicated by cfg_clear/cfg_set. */
> > +       reg_val = (*reg_ptr & ~cfg_clear);
> > +       reg_val |= cfg_set;
> > +       *reg_ptr = reg_val;
> > +}
> > +
> > +static void feature_ras_trap_activate(struct kvm_vcpu *vcpu)
> > +{
> > +       feature_trap_activate(vcpu, VCPU_HCR_EL2, HCR_TERR | HCR_TEA, HCR_FIEN);
>
> Covers all the flags for ras.
>
> > +}
> > +
> > +static void feature_amu_trap_activate(struct kvm_vcpu *vcpu)
> > +{
> > +       feature_trap_activate(vcpu, VCPU_CPTR_EL2, CPTR_EL2_TAM, 0);
>
> Covers the CPTR flags for AMU, but as you mentioned, does not
> explicitly clear HCR_AMVOFFEN.

In my understanding, clearing HCR_EL2.AMVOFFEN is not necessary as
CPTR_EL2.TAM == 1 traps the guest's accessing AMEVCNTR0<n>_EL0 and
AMEVCNTR1<n>_EL0 anyway (HCR_EL2.AMVOFFEN doesn't matter).
(Or is my understanding wrong ??)

Thanks,
Reiji

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 20/26] KVM: arm64: Trap disabled features of ID_AA64PFR0_EL1
@ 2022-01-27  7:19       ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-27  7:19 UTC (permalink / raw)
  To: Fuad Tabba
  Cc: kvm, Marc Zyngier, Peter Shier, Paolo Bonzini, Will Deacon,
	kvmarm, Linux ARM

Hi Fuad,

On Mon, Jan 24, 2022 at 9:17 AM Fuad Tabba <tabba@google.com> wrote:
>
> Hi Reiji,
>
> On Thu, Jan 6, 2022 at 4:29 AM Reiji Watanabe <reijiw@google.com> wrote:
> >
> > Add feature_config_ctrl for RAS and AMU, which are indicated in
> > ID_AA64PFR0_EL1, to program configuration registers to trap
> > guest's using those features when they are not exposed to the guest.
> >
> > Introduce trap_ras_regs() to change a behavior of guest's access to
> > the registers, which is currently raz/wi, depending on the feature's
> > availability for the guest (and inject undefined instruction
> > exception when guest's RAS register access are trapped and RAS is
> > not exposed to the guest).  In order to keep the current visibility
> > of the RAS registers from userspace (always visible), a visibility
> > function for RAS registers is not added.
> >
> > No code is added for AMU's access/visibility handler because the
> > current code already injects the exception for Guest's AMU register
> > access unconditionally because AMU is never exposed to the guest.
>
> I think it might be code to trap it anyway, in case AMU guest support
> is added in the future.

Yes, I will fix it.
(I forgot to update the comment above...)


> >
> > Signed-off-by: Reiji Watanabe <reijiw@google.com>
> > ---
> >  arch/arm64/kvm/sys_regs.c | 90 +++++++++++++++++++++++++++++++++++----
> >  1 file changed, 82 insertions(+), 8 deletions(-)
> >
> > diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> > index 33893a501475..015d67092d5e 100644
> > --- a/arch/arm64/kvm/sys_regs.c
> > +++ b/arch/arm64/kvm/sys_regs.c
> > @@ -304,6 +304,63 @@ struct feature_config_ctrl {
> >         void    (*trap_activate)(struct kvm_vcpu *vcpu);
> >  };
> >
> > +enum vcpu_config_reg {
> > +       VCPU_HCR_EL2 = 1,
> > +       VCPU_MDCR_EL2,
> > +       VCPU_CPTR_EL2,
> > +};
> > +
> > +static void feature_trap_activate(struct kvm_vcpu *vcpu,
> > +                                 enum vcpu_config_reg cfg_reg,
> > +                                 u64 cfg_set, u64 cfg_clear)
> > +{
> > +       u64 *reg_ptr, reg_val;
> > +
> > +       switch (cfg_reg) {
> > +       case VCPU_HCR_EL2:
> > +               reg_ptr = &vcpu->arch.hcr_el2;
> > +               break;
> > +       case VCPU_MDCR_EL2:
> > +               reg_ptr = &vcpu->arch.mdcr_el2;
> > +               break;
> > +       case VCPU_CPTR_EL2:
> > +               reg_ptr = &vcpu->arch.cptr_el2;
> > +               break;
> > +       }
> > +
> > +       /* Clear/Set fields that are indicated by cfg_clear/cfg_set. */
> > +       reg_val = (*reg_ptr & ~cfg_clear);
> > +       reg_val |= cfg_set;
> > +       *reg_ptr = reg_val;
> > +}
> > +
> > +static void feature_ras_trap_activate(struct kvm_vcpu *vcpu)
> > +{
> > +       feature_trap_activate(vcpu, VCPU_HCR_EL2, HCR_TERR | HCR_TEA, HCR_FIEN);
>
> Covers all the flags for ras.
>
> > +}
> > +
> > +static void feature_amu_trap_activate(struct kvm_vcpu *vcpu)
> > +{
> > +       feature_trap_activate(vcpu, VCPU_CPTR_EL2, CPTR_EL2_TAM, 0);
>
> Covers the CPTR flags for AMU, but as you mentioned, does not
> explicitly clear HCR_AMVOFFEN.

In my understanding, clearing HCR_EL2.AMVOFFEN is not necessary as
CPTR_EL2.TAM == 1 traps the guest's accessing AMEVCNTR0<n>_EL0 and
AMEVCNTR1<n>_EL0 anyway (HCR_EL2.AMVOFFEN doesn't matter).
(Or is my understanding wrong ??)

Thanks,
Reiji
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 20/26] KVM: arm64: Trap disabled features of ID_AA64PFR0_EL1
@ 2022-01-27  7:19       ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-27  7:19 UTC (permalink / raw)
  To: Fuad Tabba
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, Linux ARM

Hi Fuad,

On Mon, Jan 24, 2022 at 9:17 AM Fuad Tabba <tabba@google.com> wrote:
>
> Hi Reiji,
>
> On Thu, Jan 6, 2022 at 4:29 AM Reiji Watanabe <reijiw@google.com> wrote:
> >
> > Add feature_config_ctrl for RAS and AMU, which are indicated in
> > ID_AA64PFR0_EL1, to program configuration registers to trap
> > guest's using those features when they are not exposed to the guest.
> >
> > Introduce trap_ras_regs() to change a behavior of guest's access to
> > the registers, which is currently raz/wi, depending on the feature's
> > availability for the guest (and inject undefined instruction
> > exception when guest's RAS register access are trapped and RAS is
> > not exposed to the guest).  In order to keep the current visibility
> > of the RAS registers from userspace (always visible), a visibility
> > function for RAS registers is not added.
> >
> > No code is added for AMU's access/visibility handler because the
> > current code already injects the exception for Guest's AMU register
> > access unconditionally because AMU is never exposed to the guest.
>
> I think it might be code to trap it anyway, in case AMU guest support
> is added in the future.

Yes, I will fix it.
(I forgot to update the comment above...)


> >
> > Signed-off-by: Reiji Watanabe <reijiw@google.com>
> > ---
> >  arch/arm64/kvm/sys_regs.c | 90 +++++++++++++++++++++++++++++++++++----
> >  1 file changed, 82 insertions(+), 8 deletions(-)
> >
> > diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> > index 33893a501475..015d67092d5e 100644
> > --- a/arch/arm64/kvm/sys_regs.c
> > +++ b/arch/arm64/kvm/sys_regs.c
> > @@ -304,6 +304,63 @@ struct feature_config_ctrl {
> >         void    (*trap_activate)(struct kvm_vcpu *vcpu);
> >  };
> >
> > +enum vcpu_config_reg {
> > +       VCPU_HCR_EL2 = 1,
> > +       VCPU_MDCR_EL2,
> > +       VCPU_CPTR_EL2,
> > +};
> > +
> > +static void feature_trap_activate(struct kvm_vcpu *vcpu,
> > +                                 enum vcpu_config_reg cfg_reg,
> > +                                 u64 cfg_set, u64 cfg_clear)
> > +{
> > +       u64 *reg_ptr, reg_val;
> > +
> > +       switch (cfg_reg) {
> > +       case VCPU_HCR_EL2:
> > +               reg_ptr = &vcpu->arch.hcr_el2;
> > +               break;
> > +       case VCPU_MDCR_EL2:
> > +               reg_ptr = &vcpu->arch.mdcr_el2;
> > +               break;
> > +       case VCPU_CPTR_EL2:
> > +               reg_ptr = &vcpu->arch.cptr_el2;
> > +               break;
> > +       }
> > +
> > +       /* Clear/Set fields that are indicated by cfg_clear/cfg_set. */
> > +       reg_val = (*reg_ptr & ~cfg_clear);
> > +       reg_val |= cfg_set;
> > +       *reg_ptr = reg_val;
> > +}
> > +
> > +static void feature_ras_trap_activate(struct kvm_vcpu *vcpu)
> > +{
> > +       feature_trap_activate(vcpu, VCPU_HCR_EL2, HCR_TERR | HCR_TEA, HCR_FIEN);
>
> Covers all the flags for ras.
>
> > +}
> > +
> > +static void feature_amu_trap_activate(struct kvm_vcpu *vcpu)
> > +{
> > +       feature_trap_activate(vcpu, VCPU_CPTR_EL2, CPTR_EL2_TAM, 0);
>
> Covers the CPTR flags for AMU, but as you mentioned, does not
> explicitly clear HCR_AMVOFFEN.

In my understanding, clearing HCR_EL2.AMVOFFEN is not necessary as
CPTR_EL2.TAM == 1 traps the guest's accessing AMEVCNTR0<n>_EL0 and
AMEVCNTR1<n>_EL0 anyway (HCR_EL2.AMVOFFEN doesn't matter).
(Or is my understanding wrong ??)

Thanks,
Reiji

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 22/26] KVM: arm64: Trap disabled features of ID_AA64DFR0_EL1
  2022-01-24 17:19     ` Fuad Tabba
  (?)
@ 2022-01-28  5:40       ` Reiji Watanabe
  -1 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-28  5:40 UTC (permalink / raw)
  To: Fuad Tabba
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, Linux ARM

Hi Fuad,

On Mon, Jan 24, 2022 at 9:20 AM Fuad Tabba <tabba@google.com> wrote:
>
> .Hi Reiji,
>
> On Thu, Jan 6, 2022 at 4:29 AM Reiji Watanabe <reijiw@google.com> wrote:
> >
> > Add feature_config_ctrl for PMUv3, PMS and TraceFilt, which are
> > indicated in ID_AA64DFR0_EL1, to program configuration registers
> > to trap guest's using those features when they are not exposed to
> > the guest.
> >
> > Signed-off-by: Reiji Watanabe <reijiw@google.com>
> > ---
> >  arch/arm64/kvm/sys_regs.c | 47 +++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 47 insertions(+)
> >
> > diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> > index 72e745c5a9c2..229671ec3abd 100644
> > --- a/arch/arm64/kvm/sys_regs.c
> > +++ b/arch/arm64/kvm/sys_regs.c
> > @@ -349,6 +349,22 @@ static void feature_mte_trap_activate(struct kvm_vcpu *vcpu)
> >         feature_trap_activate(vcpu, VCPU_HCR_EL2, HCR_TID5, HCR_DCT | HCR_ATA);
> >  }
> >
> > +static void feature_pmuv3_trap_activate(struct kvm_vcpu *vcpu)
> > +{
> > +       feature_trap_activate(vcpu, VCPU_MDCR_EL2, MDCR_EL2_TPM, 0);
>
> I think that for full coverage it might be good to include setting
> MDCR_EL2_TPMCR, and clearing MDCR_EL2_HPME | MDCR_EL2_MTPME |
> MDCR_EL2_HPMN_MASK, even if redundant at this point.

I included what is needed only, and I would prefer not to let KVM
do things that are not needed to trap guest's using the feature.
Please let me know if you have a specific reason why you think it
would be better to include them.

>
> > +}
> > +
> > +static void feature_pms_trap_activate(struct kvm_vcpu *vcpu)
> > +{
> > +       feature_trap_activate(vcpu, VCPU_MDCR_EL2, MDCR_EL2_TPMS,
> > +                             MDCR_EL2_E2PB_MASK << MDCR_EL2_E2PB_SHIFT);
> > +}
> > +
> > +static void feature_tracefilt_trap_activate(struct kvm_vcpu *vcpu)
> > +{
> > +       feature_trap_activate(vcpu, VCPU_MDCR_EL2, MDCR_EL2_TTRF, 0);
> > +}
> > +
> >  /* For ID_AA64PFR0_EL1 */
> >  static struct feature_config_ctrl ftr_ctrl_ras = {
> >         .ftr_reg = SYS_ID_AA64PFR0_EL1,
> > @@ -375,6 +391,31 @@ static struct feature_config_ctrl ftr_ctrl_mte = {
> >         .trap_activate = feature_mte_trap_activate,
> >  };
> >
> > +/* For ID_AA64DFR0_EL1 */
> > +static struct feature_config_ctrl ftr_ctrl_pmuv3 = {
> > +       .ftr_reg = SYS_ID_AA64DFR0_EL1,
> > +       .ftr_shift = ID_AA64DFR0_PMUVER_SHIFT,
> > +       .ftr_min = ID_AA64DFR0_PMUVER_8_0,
> > +       .ftr_signed = FTR_UNSIGNED,
> > +       .trap_activate = feature_pmuv3_trap_activate,
> > +};
> > +
> > +static struct feature_config_ctrl ftr_ctrl_pms = {
> > +       .ftr_reg = SYS_ID_AA64DFR0_EL1,
> > +       .ftr_shift = ID_AA64DFR0_PMSVER_SHIFT,
> > +       .ftr_min = ID_AA64DFR0_PMSVER_8_2,
> > +       .ftr_signed = FTR_UNSIGNED,
> > +       .trap_activate = feature_pms_trap_activate,
> > +};
> > +
> > +static struct feature_config_ctrl ftr_ctrl_tracefilt = {
> > +       .ftr_reg = SYS_ID_AA64DFR0_EL1,
> > +       .ftr_shift = ID_AA64DFR0_TRACE_FILT_SHIFT,
> > +       .ftr_min = 1,
> > +       .ftr_signed = FTR_UNSIGNED,
> > +       .trap_activate = feature_tracefilt_trap_activate,
> > +};
>
> I think you might be missing trace, ID_AA64DFR0_TRACEVER -> CPTR_EL2_TTA.

Thank you for catching this. I will add the trace.

Thanks,
Reiji

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 22/26] KVM: arm64: Trap disabled features of ID_AA64DFR0_EL1
@ 2022-01-28  5:40       ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-28  5:40 UTC (permalink / raw)
  To: Fuad Tabba
  Cc: kvm, Marc Zyngier, Peter Shier, Paolo Bonzini, Will Deacon,
	kvmarm, Linux ARM

Hi Fuad,

On Mon, Jan 24, 2022 at 9:20 AM Fuad Tabba <tabba@google.com> wrote:
>
> .Hi Reiji,
>
> On Thu, Jan 6, 2022 at 4:29 AM Reiji Watanabe <reijiw@google.com> wrote:
> >
> > Add feature_config_ctrl for PMUv3, PMS and TraceFilt, which are
> > indicated in ID_AA64DFR0_EL1, to program configuration registers
> > to trap guest's using those features when they are not exposed to
> > the guest.
> >
> > Signed-off-by: Reiji Watanabe <reijiw@google.com>
> > ---
> >  arch/arm64/kvm/sys_regs.c | 47 +++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 47 insertions(+)
> >
> > diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> > index 72e745c5a9c2..229671ec3abd 100644
> > --- a/arch/arm64/kvm/sys_regs.c
> > +++ b/arch/arm64/kvm/sys_regs.c
> > @@ -349,6 +349,22 @@ static void feature_mte_trap_activate(struct kvm_vcpu *vcpu)
> >         feature_trap_activate(vcpu, VCPU_HCR_EL2, HCR_TID5, HCR_DCT | HCR_ATA);
> >  }
> >
> > +static void feature_pmuv3_trap_activate(struct kvm_vcpu *vcpu)
> > +{
> > +       feature_trap_activate(vcpu, VCPU_MDCR_EL2, MDCR_EL2_TPM, 0);
>
> I think that for full coverage it might be good to include setting
> MDCR_EL2_TPMCR, and clearing MDCR_EL2_HPME | MDCR_EL2_MTPME |
> MDCR_EL2_HPMN_MASK, even if redundant at this point.

I included what is needed only, and I would prefer not to let KVM
do things that are not needed to trap guest's using the feature.
Please let me know if you have a specific reason why you think it
would be better to include them.

>
> > +}
> > +
> > +static void feature_pms_trap_activate(struct kvm_vcpu *vcpu)
> > +{
> > +       feature_trap_activate(vcpu, VCPU_MDCR_EL2, MDCR_EL2_TPMS,
> > +                             MDCR_EL2_E2PB_MASK << MDCR_EL2_E2PB_SHIFT);
> > +}
> > +
> > +static void feature_tracefilt_trap_activate(struct kvm_vcpu *vcpu)
> > +{
> > +       feature_trap_activate(vcpu, VCPU_MDCR_EL2, MDCR_EL2_TTRF, 0);
> > +}
> > +
> >  /* For ID_AA64PFR0_EL1 */
> >  static struct feature_config_ctrl ftr_ctrl_ras = {
> >         .ftr_reg = SYS_ID_AA64PFR0_EL1,
> > @@ -375,6 +391,31 @@ static struct feature_config_ctrl ftr_ctrl_mte = {
> >         .trap_activate = feature_mte_trap_activate,
> >  };
> >
> > +/* For ID_AA64DFR0_EL1 */
> > +static struct feature_config_ctrl ftr_ctrl_pmuv3 = {
> > +       .ftr_reg = SYS_ID_AA64DFR0_EL1,
> > +       .ftr_shift = ID_AA64DFR0_PMUVER_SHIFT,
> > +       .ftr_min = ID_AA64DFR0_PMUVER_8_0,
> > +       .ftr_signed = FTR_UNSIGNED,
> > +       .trap_activate = feature_pmuv3_trap_activate,
> > +};
> > +
> > +static struct feature_config_ctrl ftr_ctrl_pms = {
> > +       .ftr_reg = SYS_ID_AA64DFR0_EL1,
> > +       .ftr_shift = ID_AA64DFR0_PMSVER_SHIFT,
> > +       .ftr_min = ID_AA64DFR0_PMSVER_8_2,
> > +       .ftr_signed = FTR_UNSIGNED,
> > +       .trap_activate = feature_pms_trap_activate,
> > +};
> > +
> > +static struct feature_config_ctrl ftr_ctrl_tracefilt = {
> > +       .ftr_reg = SYS_ID_AA64DFR0_EL1,
> > +       .ftr_shift = ID_AA64DFR0_TRACE_FILT_SHIFT,
> > +       .ftr_min = 1,
> > +       .ftr_signed = FTR_UNSIGNED,
> > +       .trap_activate = feature_tracefilt_trap_activate,
> > +};
>
> I think you might be missing trace, ID_AA64DFR0_TRACEVER -> CPTR_EL2_TTA.

Thank you for catching this. I will add the trace.

Thanks,
Reiji
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 22/26] KVM: arm64: Trap disabled features of ID_AA64DFR0_EL1
@ 2022-01-28  5:40       ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-28  5:40 UTC (permalink / raw)
  To: Fuad Tabba
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, Linux ARM

Hi Fuad,

On Mon, Jan 24, 2022 at 9:20 AM Fuad Tabba <tabba@google.com> wrote:
>
> .Hi Reiji,
>
> On Thu, Jan 6, 2022 at 4:29 AM Reiji Watanabe <reijiw@google.com> wrote:
> >
> > Add feature_config_ctrl for PMUv3, PMS and TraceFilt, which are
> > indicated in ID_AA64DFR0_EL1, to program configuration registers
> > to trap guest's using those features when they are not exposed to
> > the guest.
> >
> > Signed-off-by: Reiji Watanabe <reijiw@google.com>
> > ---
> >  arch/arm64/kvm/sys_regs.c | 47 +++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 47 insertions(+)
> >
> > diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> > index 72e745c5a9c2..229671ec3abd 100644
> > --- a/arch/arm64/kvm/sys_regs.c
> > +++ b/arch/arm64/kvm/sys_regs.c
> > @@ -349,6 +349,22 @@ static void feature_mte_trap_activate(struct kvm_vcpu *vcpu)
> >         feature_trap_activate(vcpu, VCPU_HCR_EL2, HCR_TID5, HCR_DCT | HCR_ATA);
> >  }
> >
> > +static void feature_pmuv3_trap_activate(struct kvm_vcpu *vcpu)
> > +{
> > +       feature_trap_activate(vcpu, VCPU_MDCR_EL2, MDCR_EL2_TPM, 0);
>
> I think that for full coverage it might be good to include setting
> MDCR_EL2_TPMCR, and clearing MDCR_EL2_HPME | MDCR_EL2_MTPME |
> MDCR_EL2_HPMN_MASK, even if redundant at this point.

I included what is needed only, and I would prefer not to let KVM
do things that are not needed to trap guest's using the feature.
Please let me know if you have a specific reason why you think it
would be better to include them.

>
> > +}
> > +
> > +static void feature_pms_trap_activate(struct kvm_vcpu *vcpu)
> > +{
> > +       feature_trap_activate(vcpu, VCPU_MDCR_EL2, MDCR_EL2_TPMS,
> > +                             MDCR_EL2_E2PB_MASK << MDCR_EL2_E2PB_SHIFT);
> > +}
> > +
> > +static void feature_tracefilt_trap_activate(struct kvm_vcpu *vcpu)
> > +{
> > +       feature_trap_activate(vcpu, VCPU_MDCR_EL2, MDCR_EL2_TTRF, 0);
> > +}
> > +
> >  /* For ID_AA64PFR0_EL1 */
> >  static struct feature_config_ctrl ftr_ctrl_ras = {
> >         .ftr_reg = SYS_ID_AA64PFR0_EL1,
> > @@ -375,6 +391,31 @@ static struct feature_config_ctrl ftr_ctrl_mte = {
> >         .trap_activate = feature_mte_trap_activate,
> >  };
> >
> > +/* For ID_AA64DFR0_EL1 */
> > +static struct feature_config_ctrl ftr_ctrl_pmuv3 = {
> > +       .ftr_reg = SYS_ID_AA64DFR0_EL1,
> > +       .ftr_shift = ID_AA64DFR0_PMUVER_SHIFT,
> > +       .ftr_min = ID_AA64DFR0_PMUVER_8_0,
> > +       .ftr_signed = FTR_UNSIGNED,
> > +       .trap_activate = feature_pmuv3_trap_activate,
> > +};
> > +
> > +static struct feature_config_ctrl ftr_ctrl_pms = {
> > +       .ftr_reg = SYS_ID_AA64DFR0_EL1,
> > +       .ftr_shift = ID_AA64DFR0_PMSVER_SHIFT,
> > +       .ftr_min = ID_AA64DFR0_PMSVER_8_2,
> > +       .ftr_signed = FTR_UNSIGNED,
> > +       .trap_activate = feature_pms_trap_activate,
> > +};
> > +
> > +static struct feature_config_ctrl ftr_ctrl_tracefilt = {
> > +       .ftr_reg = SYS_ID_AA64DFR0_EL1,
> > +       .ftr_shift = ID_AA64DFR0_TRACE_FILT_SHIFT,
> > +       .ftr_min = 1,
> > +       .ftr_signed = FTR_UNSIGNED,
> > +       .trap_activate = feature_tracefilt_trap_activate,
> > +};
>
> I think you might be missing trace, ID_AA64DFR0_TRACEVER -> CPTR_EL2_TTA.

Thank you for catching this. I will add the trace.

Thanks,
Reiji

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 23/26] KVM: arm64: Trap disabled features of ID_AA64MMFR1_EL1
  2022-01-24 17:37     ` Fuad Tabba
  (?)
@ 2022-01-28  5:43       ` Reiji Watanabe
  -1 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-28  5:43 UTC (permalink / raw)
  To: Fuad Tabba
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, Linux ARM

Hi Fuad,

On Mon, Jan 24, 2022 at 9:38 AM Fuad Tabba <tabba@google.com> wrote:
>
> Hi Reiji,
>
> The series might be missing an entry for ID_AA64MMFR0_EL1, Debug
> Communications Channel registers, ID_AA64MMFR0_FGT -> MDCR_EL2_TDCC.

I will add them in v5 series.
Thank you so much for all the review comments!

Thanks,
Reiji


>
> Cheers,
> /fuad
>
>
> On Thu, Jan 6, 2022 at 4:29 AM Reiji Watanabe <reijiw@google.com> wrote:
> >
> > Add feature_config_ctrl for LORegions, which is indicated in
> > ID_AA64MMFR1_EL1, to program configuration register to trap
> > guest's using the feature when it is not exposed to the guest.
> >
> > Change trap_loregion() to use vcpu_feature_is_available()
> > to simplify checking of the feature's availability.
> >
> > Signed-off-by: Reiji Watanabe <reijiw@google.com>
> > ---
> >  arch/arm64/kvm/sys_regs.c | 26 ++++++++++++++++++++++++--
> >  1 file changed, 24 insertions(+), 2 deletions(-)
> >
> > diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> > index 229671ec3abd..f8a5ee927ecf 100644
> > --- a/arch/arm64/kvm/sys_regs.c
> > +++ b/arch/arm64/kvm/sys_regs.c
> > @@ -365,6 +365,11 @@ static void feature_tracefilt_trap_activate(struct kvm_vcpu *vcpu)
> >         feature_trap_activate(vcpu, VCPU_MDCR_EL2, MDCR_EL2_TTRF, 0);
> >  }
> >
> > +static void feature_lor_trap_activate(struct kvm_vcpu *vcpu)
> > +{
> > +       feature_trap_activate(vcpu, VCPU_HCR_EL2, HCR_TLOR, 0);
> > +}
> > +
> >  /* For ID_AA64PFR0_EL1 */
> >  static struct feature_config_ctrl ftr_ctrl_ras = {
> >         .ftr_reg = SYS_ID_AA64PFR0_EL1,
> > @@ -416,6 +421,15 @@ static struct feature_config_ctrl ftr_ctrl_tracefilt = {
> >         .trap_activate = feature_tracefilt_trap_activate,
> >  };
> >
> > +/* For ID_AA64MMFR1_EL1 */
> > +static struct feature_config_ctrl ftr_ctrl_lor = {
> > +       .ftr_reg = SYS_ID_AA64MMFR1_EL1,
> > +       .ftr_shift = ID_AA64MMFR1_LOR_SHIFT,
> > +       .ftr_min = 1,
> > +       .ftr_signed = FTR_UNSIGNED,
> > +       .trap_activate = feature_lor_trap_activate,
> > +};
> > +
> >  struct id_reg_info {
> >         u32     sys_reg;        /* Register ID */
> >         u64     sys_val;        /* Sanitized system value */
> > @@ -947,6 +961,14 @@ static struct id_reg_info id_aa64dfr0_el1_info = {
> >         },
> >  };
> >
> > +static struct id_reg_info id_aa64mmfr1_el1_info = {
> > +       .sys_reg = SYS_ID_AA64MMFR1_EL1,
> > +       .trap_features = &(const struct feature_config_ctrl *[]) {
> > +               &ftr_ctrl_lor,
> > +               NULL,
> > +       },
> > +};
> > +
> >  static struct id_reg_info id_dfr0_el1_info = {
> >         .sys_reg = SYS_ID_DFR0_EL1,
> >         .init = init_id_dfr0_el1_info,
> > @@ -976,6 +998,7 @@ static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {
> >         [IDREG_IDX(SYS_ID_AA64ISAR0_EL1)] = &id_aa64isar0_el1_info,
> >         [IDREG_IDX(SYS_ID_AA64ISAR1_EL1)] = &id_aa64isar1_el1_info,
> >         [IDREG_IDX(SYS_ID_AA64MMFR0_EL1)] = &id_aa64mmfr0_el1_info,
> > +       [IDREG_IDX(SYS_ID_AA64MMFR1_EL1)] = &id_aa64mmfr1_el1_info,
> >  };
> >
> >  static int validate_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
> > @@ -1050,10 +1073,9 @@ static bool trap_loregion(struct kvm_vcpu *vcpu,
> >                           struct sys_reg_params *p,
> >                           const struct sys_reg_desc *r)
> >  {
> > -       u64 val = __read_id_reg(vcpu, SYS_ID_AA64MMFR1_EL1);
> >         u32 sr = reg_to_encoding(r);
> >
> > -       if (!(val & (0xfUL << ID_AA64MMFR1_LOR_SHIFT))) {
> > +       if (!vcpu_feature_is_available(vcpu, &ftr_ctrl_lor)) {
> >                 kvm_inject_undefined(vcpu);
> >                 return false;
> >         }
> > --
> > 2.34.1.448.ga2b2bfdf31-goog
> >
> > _______________________________________________
> > kvmarm mailing list
> > kvmarm@lists.cs.columbia.edu
> > https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 23/26] KVM: arm64: Trap disabled features of ID_AA64MMFR1_EL1
@ 2022-01-28  5:43       ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-28  5:43 UTC (permalink / raw)
  To: Fuad Tabba
  Cc: kvm, Marc Zyngier, Peter Shier, Paolo Bonzini, Will Deacon,
	kvmarm, Linux ARM

Hi Fuad,

On Mon, Jan 24, 2022 at 9:38 AM Fuad Tabba <tabba@google.com> wrote:
>
> Hi Reiji,
>
> The series might be missing an entry for ID_AA64MMFR0_EL1, Debug
> Communications Channel registers, ID_AA64MMFR0_FGT -> MDCR_EL2_TDCC.

I will add them in v5 series.
Thank you so much for all the review comments!

Thanks,
Reiji


>
> Cheers,
> /fuad
>
>
> On Thu, Jan 6, 2022 at 4:29 AM Reiji Watanabe <reijiw@google.com> wrote:
> >
> > Add feature_config_ctrl for LORegions, which is indicated in
> > ID_AA64MMFR1_EL1, to program configuration register to trap
> > guest's using the feature when it is not exposed to the guest.
> >
> > Change trap_loregion() to use vcpu_feature_is_available()
> > to simplify checking of the feature's availability.
> >
> > Signed-off-by: Reiji Watanabe <reijiw@google.com>
> > ---
> >  arch/arm64/kvm/sys_regs.c | 26 ++++++++++++++++++++++++--
> >  1 file changed, 24 insertions(+), 2 deletions(-)
> >
> > diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> > index 229671ec3abd..f8a5ee927ecf 100644
> > --- a/arch/arm64/kvm/sys_regs.c
> > +++ b/arch/arm64/kvm/sys_regs.c
> > @@ -365,6 +365,11 @@ static void feature_tracefilt_trap_activate(struct kvm_vcpu *vcpu)
> >         feature_trap_activate(vcpu, VCPU_MDCR_EL2, MDCR_EL2_TTRF, 0);
> >  }
> >
> > +static void feature_lor_trap_activate(struct kvm_vcpu *vcpu)
> > +{
> > +       feature_trap_activate(vcpu, VCPU_HCR_EL2, HCR_TLOR, 0);
> > +}
> > +
> >  /* For ID_AA64PFR0_EL1 */
> >  static struct feature_config_ctrl ftr_ctrl_ras = {
> >         .ftr_reg = SYS_ID_AA64PFR0_EL1,
> > @@ -416,6 +421,15 @@ static struct feature_config_ctrl ftr_ctrl_tracefilt = {
> >         .trap_activate = feature_tracefilt_trap_activate,
> >  };
> >
> > +/* For ID_AA64MMFR1_EL1 */
> > +static struct feature_config_ctrl ftr_ctrl_lor = {
> > +       .ftr_reg = SYS_ID_AA64MMFR1_EL1,
> > +       .ftr_shift = ID_AA64MMFR1_LOR_SHIFT,
> > +       .ftr_min = 1,
> > +       .ftr_signed = FTR_UNSIGNED,
> > +       .trap_activate = feature_lor_trap_activate,
> > +};
> > +
> >  struct id_reg_info {
> >         u32     sys_reg;        /* Register ID */
> >         u64     sys_val;        /* Sanitized system value */
> > @@ -947,6 +961,14 @@ static struct id_reg_info id_aa64dfr0_el1_info = {
> >         },
> >  };
> >
> > +static struct id_reg_info id_aa64mmfr1_el1_info = {
> > +       .sys_reg = SYS_ID_AA64MMFR1_EL1,
> > +       .trap_features = &(const struct feature_config_ctrl *[]) {
> > +               &ftr_ctrl_lor,
> > +               NULL,
> > +       },
> > +};
> > +
> >  static struct id_reg_info id_dfr0_el1_info = {
> >         .sys_reg = SYS_ID_DFR0_EL1,
> >         .init = init_id_dfr0_el1_info,
> > @@ -976,6 +998,7 @@ static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {
> >         [IDREG_IDX(SYS_ID_AA64ISAR0_EL1)] = &id_aa64isar0_el1_info,
> >         [IDREG_IDX(SYS_ID_AA64ISAR1_EL1)] = &id_aa64isar1_el1_info,
> >         [IDREG_IDX(SYS_ID_AA64MMFR0_EL1)] = &id_aa64mmfr0_el1_info,
> > +       [IDREG_IDX(SYS_ID_AA64MMFR1_EL1)] = &id_aa64mmfr1_el1_info,
> >  };
> >
> >  static int validate_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
> > @@ -1050,10 +1073,9 @@ static bool trap_loregion(struct kvm_vcpu *vcpu,
> >                           struct sys_reg_params *p,
> >                           const struct sys_reg_desc *r)
> >  {
> > -       u64 val = __read_id_reg(vcpu, SYS_ID_AA64MMFR1_EL1);
> >         u32 sr = reg_to_encoding(r);
> >
> > -       if (!(val & (0xfUL << ID_AA64MMFR1_LOR_SHIFT))) {
> > +       if (!vcpu_feature_is_available(vcpu, &ftr_ctrl_lor)) {
> >                 kvm_inject_undefined(vcpu);
> >                 return false;
> >         }
> > --
> > 2.34.1.448.ga2b2bfdf31-goog
> >
> > _______________________________________________
> > kvmarm mailing list
> > kvmarm@lists.cs.columbia.edu
> > https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 23/26] KVM: arm64: Trap disabled features of ID_AA64MMFR1_EL1
@ 2022-01-28  5:43       ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-28  5:43 UTC (permalink / raw)
  To: Fuad Tabba
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, Linux ARM

Hi Fuad,

On Mon, Jan 24, 2022 at 9:38 AM Fuad Tabba <tabba@google.com> wrote:
>
> Hi Reiji,
>
> The series might be missing an entry for ID_AA64MMFR0_EL1, Debug
> Communications Channel registers, ID_AA64MMFR0_FGT -> MDCR_EL2_TDCC.

I will add them in v5 series.
Thank you so much for all the review comments!

Thanks,
Reiji


>
> Cheers,
> /fuad
>
>
> On Thu, Jan 6, 2022 at 4:29 AM Reiji Watanabe <reijiw@google.com> wrote:
> >
> > Add feature_config_ctrl for LORegions, which is indicated in
> > ID_AA64MMFR1_EL1, to program configuration register to trap
> > guest's using the feature when it is not exposed to the guest.
> >
> > Change trap_loregion() to use vcpu_feature_is_available()
> > to simplify checking of the feature's availability.
> >
> > Signed-off-by: Reiji Watanabe <reijiw@google.com>
> > ---
> >  arch/arm64/kvm/sys_regs.c | 26 ++++++++++++++++++++++++--
> >  1 file changed, 24 insertions(+), 2 deletions(-)
> >
> > diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> > index 229671ec3abd..f8a5ee927ecf 100644
> > --- a/arch/arm64/kvm/sys_regs.c
> > +++ b/arch/arm64/kvm/sys_regs.c
> > @@ -365,6 +365,11 @@ static void feature_tracefilt_trap_activate(struct kvm_vcpu *vcpu)
> >         feature_trap_activate(vcpu, VCPU_MDCR_EL2, MDCR_EL2_TTRF, 0);
> >  }
> >
> > +static void feature_lor_trap_activate(struct kvm_vcpu *vcpu)
> > +{
> > +       feature_trap_activate(vcpu, VCPU_HCR_EL2, HCR_TLOR, 0);
> > +}
> > +
> >  /* For ID_AA64PFR0_EL1 */
> >  static struct feature_config_ctrl ftr_ctrl_ras = {
> >         .ftr_reg = SYS_ID_AA64PFR0_EL1,
> > @@ -416,6 +421,15 @@ static struct feature_config_ctrl ftr_ctrl_tracefilt = {
> >         .trap_activate = feature_tracefilt_trap_activate,
> >  };
> >
> > +/* For ID_AA64MMFR1_EL1 */
> > +static struct feature_config_ctrl ftr_ctrl_lor = {
> > +       .ftr_reg = SYS_ID_AA64MMFR1_EL1,
> > +       .ftr_shift = ID_AA64MMFR1_LOR_SHIFT,
> > +       .ftr_min = 1,
> > +       .ftr_signed = FTR_UNSIGNED,
> > +       .trap_activate = feature_lor_trap_activate,
> > +};
> > +
> >  struct id_reg_info {
> >         u32     sys_reg;        /* Register ID */
> >         u64     sys_val;        /* Sanitized system value */
> > @@ -947,6 +961,14 @@ static struct id_reg_info id_aa64dfr0_el1_info = {
> >         },
> >  };
> >
> > +static struct id_reg_info id_aa64mmfr1_el1_info = {
> > +       .sys_reg = SYS_ID_AA64MMFR1_EL1,
> > +       .trap_features = &(const struct feature_config_ctrl *[]) {
> > +               &ftr_ctrl_lor,
> > +               NULL,
> > +       },
> > +};
> > +
> >  static struct id_reg_info id_dfr0_el1_info = {
> >         .sys_reg = SYS_ID_DFR0_EL1,
> >         .init = init_id_dfr0_el1_info,
> > @@ -976,6 +998,7 @@ static struct id_reg_info *id_reg_info_table[KVM_ARM_ID_REG_MAX_NUM] = {
> >         [IDREG_IDX(SYS_ID_AA64ISAR0_EL1)] = &id_aa64isar0_el1_info,
> >         [IDREG_IDX(SYS_ID_AA64ISAR1_EL1)] = &id_aa64isar1_el1_info,
> >         [IDREG_IDX(SYS_ID_AA64MMFR0_EL1)] = &id_aa64mmfr0_el1_info,
> > +       [IDREG_IDX(SYS_ID_AA64MMFR1_EL1)] = &id_aa64mmfr1_el1_info,
> >  };
> >
> >  static int validate_id_reg(struct kvm_vcpu *vcpu, u32 id, u64 val)
> > @@ -1050,10 +1073,9 @@ static bool trap_loregion(struct kvm_vcpu *vcpu,
> >                           struct sys_reg_params *p,
> >                           const struct sys_reg_desc *r)
> >  {
> > -       u64 val = __read_id_reg(vcpu, SYS_ID_AA64MMFR1_EL1);
> >         u32 sr = reg_to_encoding(r);
> >
> > -       if (!(val & (0xfUL << ID_AA64MMFR1_LOR_SHIFT))) {
> > +       if (!vcpu_feature_is_available(vcpu, &ftr_ctrl_lor)) {
> >                 kvm_inject_undefined(vcpu);
> >                 return false;
> >         }
> > --
> > 2.34.1.448.ga2b2bfdf31-goog
> >
> > _______________________________________________
> > kvmarm mailing list
> > kvmarm@lists.cs.columbia.edu
> > https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 01/26] KVM: arm64: Introduce a validation function for an ID register
  2022-01-26  4:30     ` Ricardo Koller
  (?)
@ 2022-01-28  6:01       ` Reiji Watanabe
  -1 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-28  6:01 UTC (permalink / raw)
  To: Ricardo Koller
  Cc: Marc Zyngier, kvmarm, kvm, Linux ARM, James Morse,
	Alexandru Elisei, Suzuki K Poulose, Paolo Bonzini, Will Deacon,
	Andrew Jones, Peng Liang, Peter Shier, Oliver Upton, Jing Zhang,
	Raghavendra Rao Anata

Hi Ricardo,

On Tue, Jan 25, 2022 at 8:30 PM Ricardo Koller <ricarkol@google.com> wrote:
>
> Hey Reiji,
>
> On Wed, Jan 05, 2022 at 08:26:43PM -0800, Reiji Watanabe wrote:
> > Introduce arm64_check_features(), which does a basic validity checking
> > of an ID register value against the register's limit value, which is
> > generally the host's sanitized value.
> >
> > This function will be used by the following patches to check if an ID
> > register value that userspace tries to set for a guest can be supported
> > on the host.
> >
> > The validation is done using arm64_ftr_bits_kvm, which is created from
> > arm64_ftr_regs, with some entries overwritten by entries from
> > arm64_ftr_bits_kvm_override.
> >
> > Signed-off-by: Reiji Watanabe <reijiw@google.com>
> > ---
> >  arch/arm64/include/asm/cpufeature.h |   1 +
> >  arch/arm64/kernel/cpufeature.c      | 228 ++++++++++++++++++++++++++++
> >  2 files changed, 229 insertions(+)
> >
> > diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
> > index ef6be92b1921..eda7ddbed8cf 100644
> > --- a/arch/arm64/include/asm/cpufeature.h
> > +++ b/arch/arm64/include/asm/cpufeature.h
> > @@ -631,6 +631,7 @@ void check_local_cpu_capabilities(void);
> >
> >  u64 read_sanitised_ftr_reg(u32 id);
> >  u64 __read_sysreg_by_encoding(u32 sys_id);
> > +int arm64_check_features(u32 sys_reg, u64 val, u64 limit);
> >
> >  static inline bool cpu_supports_mixed_endian_el0(void)
> >  {
> > diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
> > index 6f3e677d88f1..48dff8b101d9 100644
> > --- a/arch/arm64/kernel/cpufeature.c
> > +++ b/arch/arm64/kernel/cpufeature.c
> > @@ -3140,3 +3140,231 @@ ssize_t cpu_show_meltdown(struct device *dev, struct device_attribute *attr,
> >               return sprintf(buf, "Vulnerable\n");
> >       }
> >  }
> > +
> > +#ifdef CONFIG_KVM
> > +/*
> > + * arm64_ftr_bits_kvm[] is used for KVM to check if features that are
> > + * indicated in an ID register value for the guest are available on the host.
> > + * arm64_ftr_bits_kvm[] is created based on arm64_ftr_regs[].  But, for
> > + * registers for which arm64_ftr_bits_kvm_override[] has a corresponding
> > + * entry, replace arm64_ftr_bits entries in arm64_ftr_bits_kvm[] with the
> > + * ones in arm64_ftr_bits_kvm_override[].
> > + */
> > +static struct __ftr_reg_bits_entry *arm64_ftr_bits_kvm;
> > +static size_t arm64_ftr_bits_kvm_nentries;
>
> I don't think this is really needed, as arm64_ftr_bits_kvm_override has
> to have the same size as arm64_ftr_bits_kvm. You could use
> ARRAY_SIZE(arm64_ftr_regs) like in get_arm64_ftr_reg_nowarn().

Thanks for the review!
Yes, you are right. I will remove arm64_ftr_bits_kvm_nentries,
and use ARRAY_SIZE(arm64_ftr_regs) instead.

> > +static DEFINE_MUTEX(arm64_ftr_bits_kvm_lock);
> > +
> > +/*
> > + * Number of arm64_ftr_bits entries for each register.
> > + * (Number of 4 bits fields in 64 bit register + 1 entry for ARM64_FTR_END)
> > + */
> > +#define      MAX_FTR_BITS_LEN        17
> > +
> > +/* Use FTR_LOWER_SAFE for AA64DFR0_EL1.PMUVER and AA64DFR0_EL1.DEBUGVER. */
> > +static struct arm64_ftr_bits ftr_id_aa64dfr0_kvm[MAX_FTR_BITS_LEN] = {
> > +     S_ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64DFR0_PMUVER_SHIFT, 4, 0),
> > +     ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x6),
> > +     ARM64_FTR_END,
> > +};
> > +
> > +#define      ARM64_FTR_REG_BITS(id, table)   {       \
> > +     .sys_id = id,                           \
> > +     .ftr_bits = &((table)[0]),              \
> > +}
> > +
> > +struct __ftr_reg_bits_entry {
> > +     u32     sys_id;
> > +     struct arm64_ftr_bits   *ftr_bits;
> > +};
> > +
> > +/*
> > + * All entries in arm64_ftr_bits_kvm_override[] are used to override
> > + * the corresponding entries in arm64_ftr_bits_kvm[].
> > + */
> > +static struct __ftr_reg_bits_entry arm64_ftr_bits_kvm_override[] = {
> > +     ARM64_FTR_REG_BITS(SYS_ID_AA64DFR0_EL1, ftr_id_aa64dfr0_kvm),
> > +};
> > +
> > +/*
> > + * Override entries in @orig_ftrp with the ones in @new_ftrp when their shift
> > + * fields match.  The last entry of @orig_ftrp and @new_ftrp must be
> > + * ARM64_FTR_END (.width == 0).
> > + */
> > +static void arm64_ftr_reg_bits_overrite(struct arm64_ftr_bits *orig_ftrp,
> > +                                     struct arm64_ftr_bits *new_ftrp)
> > +{
> > +     struct arm64_ftr_bits *o_ftrp, *n_ftrp;
> > +
> > +     for (n_ftrp = new_ftrp; n_ftrp->width; n_ftrp++) {
> > +             for (o_ftrp = orig_ftrp; o_ftrp->width; o_ftrp++) {
> > +                     if (o_ftrp->shift == n_ftrp->shift) {
> > +                             *o_ftrp = *n_ftrp;
> > +                             break;
> > +                     }
> > +             }
> > +     }
> > +}
> > +
> > +/*
> > + * Copy arm64_ftr_bits entries from @src_ftrp to @dst_ftrp.  The last entries
> > + * of @dst_ftrp and @src_ftrp must be ARM64_FTR_END (.width == 0).
> > + */
> > +static void copy_arm64_ftr_bits(struct arm64_ftr_bits *dst_ftrp,
> > +                             const struct arm64_ftr_bits *src_ftrp)
> > +{
> > +     int i = 0;
> > +
> > +     for (; src_ftrp[i].width; i++) {
> > +             if (WARN_ON_ONCE(i >= (MAX_FTR_BITS_LEN - 1)))
> > +                     break;
> > +
> > +             dst_ftrp[i] = src_ftrp[i];
> > +     }
> > +
> > +     dst_ftrp[i].width = 0;
> > +}
> > +
> > +/*
> > + * Initialize arm64_ftr_bits_kvm.  Copy arm64_ftr_bits for each ID register
> > + * from arm64_ftr_regs to arm64_ftr_bits_kvm, and then override entries in
> > + * arm64_ftr_bits_kvm with ones in arm64_ftr_bits_kvm_override.
> > + */
> > +static int init_arm64_ftr_bits_kvm(void)
> > +{
> > +     struct arm64_ftr_bits ftr_temp[MAX_FTR_BITS_LEN];
> > +     static struct __ftr_reg_bits_entry *reg_bits_array, *bits, *o_bits;
> > +     int i, j, nent, ret;
> > +
> > +     mutex_lock(&arm64_ftr_bits_kvm_lock);
>
> This is initialized lazily, whenever KVM calls arm64_check_features(). I
> guess that's why it needs the lock (and possibly a barrier as you
> mentoin in your reply). Would it be possible to simplify things by
> initializing arm64_ftr_bits_kvm somewhere at boot time (in
> init_cpu_features maybe?)?

I agree that it could simplify the code.
I will look into initializing that earlier.


>
> > +     if (arm64_ftr_bits_kvm) {
> > +             /* Already initialized */
> > +             ret = 0;
> > +             goto unlock_exit;
> > +     }
> > +
> > +     nent = ARRAY_SIZE(arm64_ftr_regs);
> > +     reg_bits_array = kcalloc(nent, sizeof(struct __ftr_reg_bits_entry),
> > +                              GFP_KERNEL);
> > +     if (!reg_bits_array) {
> > +             ret = ENOMEM;
> > +             goto unlock_exit;
> > +     }
> > +
> > +     /* Copy entries from arm64_ftr_regs to reg_bits_array */
> > +     for (i = 0; i < nent; i++) {
> > +             bits = &reg_bits_array[i];
> > +             bits->sys_id = arm64_ftr_regs[i].sys_id;
> > +             bits->ftr_bits = (struct arm64_ftr_bits *)arm64_ftr_regs[i].reg->ftr_bits;
> > +     };
> > +
> > +     /*
> > +      * Override the entries in reg_bits_array with the ones in
> > +      * arm64_ftr_bits_kvm_override.
> > +      */
> > +     for (i = 0; i < ARRAY_SIZE(arm64_ftr_bits_kvm_override); i++) {
> > +             o_bits = &arm64_ftr_bits_kvm_override[i];
> > +             for (j = 0; j < nent; j++) {
> > +                     bits = &reg_bits_array[j];
> > +                     if (bits->sys_id != o_bits->sys_id)
> > +                             continue;
> > +
> > +                     memset(ftr_temp, 0, sizeof(ftr_temp));
> > +
> > +                     /*
> > +                      * Temporary save all entries in o_bits->ftr_bits
> > +                      * to ftr_temp.
> > +                      */
> > +                     copy_arm64_ftr_bits(ftr_temp, o_bits->ftr_bits);
> > +
> > +                     /*
> > +                      * Copy entries from bits->ftr_bits to o_bits->ftr_bits.
> > +                      */
> > +                     copy_arm64_ftr_bits(o_bits->ftr_bits, bits->ftr_bits);
> > +
> > +                     /*
> > +                      * Override entries in o_bits->ftr_bits with the
> > +                      * saved ones, and update bits->ftr_bits with
> > +                      * o_bits->ftr_bits.
> > +                      */
> > +                     arm64_ftr_reg_bits_overrite(o_bits->ftr_bits, ftr_temp);
> > +                     bits->ftr_bits = o_bits->ftr_bits;
> > +                     break;
> > +             }
> > +     }
> > +
> > +     arm64_ftr_bits_kvm_nentries = nent;
> > +     arm64_ftr_bits_kvm = reg_bits_array;
> > +     ret = 0;
> > +
> > +unlock_exit:
> > +     mutex_unlock(&arm64_ftr_bits_kvm_lock);
> > +     return ret;
> > +}
> > +
> > +static int search_cmp_ftr_reg_bits(const void *id, const void *regp)
> > +{
> > +     return ((int)(unsigned long)id -
> > +             (int)((const struct __ftr_reg_bits_entry *)regp)->sys_id);
> > +}
> > +
> > +static const struct arm64_ftr_bits *get_arm64_ftr_bits_kvm(u32 sys_id)
> > +{
> > +     const struct __ftr_reg_bits_entry *ret;
> > +     int err;
> > +
> > +     if (!arm64_ftr_bits_kvm) {
> > +             /* arm64_ftr_bits_kvm is not initialized yet. */
> > +             err = init_arm64_ftr_bits_kvm();
> > +             if (err)
> > +                     return NULL;
> > +     }
> > +
> > +     ret = bsearch((const void *)(unsigned long)sys_id,
> > +                   arm64_ftr_bits_kvm,
> > +                   arm64_ftr_bits_kvm_nentries,
> > +                   sizeof(arm64_ftr_bits_kvm[0]),
> > +                   search_cmp_ftr_reg_bits);
> > +     if (ret)
> > +             return ret->ftr_bits;
> > +
> > +     return NULL;
> > +}
> > +
> > +/*
> > + * Check if features (or levels of features) that are indicated in the ID
> > + * register value @val are also indicated in @limit.
> > + * This function is for KVM to check if features that are indicated in @val,
> > + * which will be used as the ID register value for its guest, are supported
> > + * on the host.
> > + * For AA64MMFR0_EL1.TGranX_2 fields, which don't follow the standard ID
> > + * scheme, the function checks if values of the fields in @val are the same
> > + * as the ones in @limit.
> > + */
> > +int arm64_check_features(u32 sys_reg, u64 val, u64 limit)
> > +{
> > +     const struct arm64_ftr_bits *ftrp = get_arm64_ftr_bits_kvm(sys_reg);
>
> Given that this is to be used only by KVM (and it's inside CONFIG_KVM),
> it might be better to have "kvm" somewhere in its name.

Yes, that might be better. I will change the name.

Thanks,
Reiji

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 01/26] KVM: arm64: Introduce a validation function for an ID register
@ 2022-01-28  6:01       ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-28  6:01 UTC (permalink / raw)
  To: Ricardo Koller
  Cc: kvm, Marc Zyngier, Peter Shier, Will Deacon, Paolo Bonzini,
	kvmarm, Linux ARM

Hi Ricardo,

On Tue, Jan 25, 2022 at 8:30 PM Ricardo Koller <ricarkol@google.com> wrote:
>
> Hey Reiji,
>
> On Wed, Jan 05, 2022 at 08:26:43PM -0800, Reiji Watanabe wrote:
> > Introduce arm64_check_features(), which does a basic validity checking
> > of an ID register value against the register's limit value, which is
> > generally the host's sanitized value.
> >
> > This function will be used by the following patches to check if an ID
> > register value that userspace tries to set for a guest can be supported
> > on the host.
> >
> > The validation is done using arm64_ftr_bits_kvm, which is created from
> > arm64_ftr_regs, with some entries overwritten by entries from
> > arm64_ftr_bits_kvm_override.
> >
> > Signed-off-by: Reiji Watanabe <reijiw@google.com>
> > ---
> >  arch/arm64/include/asm/cpufeature.h |   1 +
> >  arch/arm64/kernel/cpufeature.c      | 228 ++++++++++++++++++++++++++++
> >  2 files changed, 229 insertions(+)
> >
> > diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
> > index ef6be92b1921..eda7ddbed8cf 100644
> > --- a/arch/arm64/include/asm/cpufeature.h
> > +++ b/arch/arm64/include/asm/cpufeature.h
> > @@ -631,6 +631,7 @@ void check_local_cpu_capabilities(void);
> >
> >  u64 read_sanitised_ftr_reg(u32 id);
> >  u64 __read_sysreg_by_encoding(u32 sys_id);
> > +int arm64_check_features(u32 sys_reg, u64 val, u64 limit);
> >
> >  static inline bool cpu_supports_mixed_endian_el0(void)
> >  {
> > diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
> > index 6f3e677d88f1..48dff8b101d9 100644
> > --- a/arch/arm64/kernel/cpufeature.c
> > +++ b/arch/arm64/kernel/cpufeature.c
> > @@ -3140,3 +3140,231 @@ ssize_t cpu_show_meltdown(struct device *dev, struct device_attribute *attr,
> >               return sprintf(buf, "Vulnerable\n");
> >       }
> >  }
> > +
> > +#ifdef CONFIG_KVM
> > +/*
> > + * arm64_ftr_bits_kvm[] is used for KVM to check if features that are
> > + * indicated in an ID register value for the guest are available on the host.
> > + * arm64_ftr_bits_kvm[] is created based on arm64_ftr_regs[].  But, for
> > + * registers for which arm64_ftr_bits_kvm_override[] has a corresponding
> > + * entry, replace arm64_ftr_bits entries in arm64_ftr_bits_kvm[] with the
> > + * ones in arm64_ftr_bits_kvm_override[].
> > + */
> > +static struct __ftr_reg_bits_entry *arm64_ftr_bits_kvm;
> > +static size_t arm64_ftr_bits_kvm_nentries;
>
> I don't think this is really needed, as arm64_ftr_bits_kvm_override has
> to have the same size as arm64_ftr_bits_kvm. You could use
> ARRAY_SIZE(arm64_ftr_regs) like in get_arm64_ftr_reg_nowarn().

Thanks for the review!
Yes, you are right. I will remove arm64_ftr_bits_kvm_nentries,
and use ARRAY_SIZE(arm64_ftr_regs) instead.

> > +static DEFINE_MUTEX(arm64_ftr_bits_kvm_lock);
> > +
> > +/*
> > + * Number of arm64_ftr_bits entries for each register.
> > + * (Number of 4 bits fields in 64 bit register + 1 entry for ARM64_FTR_END)
> > + */
> > +#define      MAX_FTR_BITS_LEN        17
> > +
> > +/* Use FTR_LOWER_SAFE for AA64DFR0_EL1.PMUVER and AA64DFR0_EL1.DEBUGVER. */
> > +static struct arm64_ftr_bits ftr_id_aa64dfr0_kvm[MAX_FTR_BITS_LEN] = {
> > +     S_ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64DFR0_PMUVER_SHIFT, 4, 0),
> > +     ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x6),
> > +     ARM64_FTR_END,
> > +};
> > +
> > +#define      ARM64_FTR_REG_BITS(id, table)   {       \
> > +     .sys_id = id,                           \
> > +     .ftr_bits = &((table)[0]),              \
> > +}
> > +
> > +struct __ftr_reg_bits_entry {
> > +     u32     sys_id;
> > +     struct arm64_ftr_bits   *ftr_bits;
> > +};
> > +
> > +/*
> > + * All entries in arm64_ftr_bits_kvm_override[] are used to override
> > + * the corresponding entries in arm64_ftr_bits_kvm[].
> > + */
> > +static struct __ftr_reg_bits_entry arm64_ftr_bits_kvm_override[] = {
> > +     ARM64_FTR_REG_BITS(SYS_ID_AA64DFR0_EL1, ftr_id_aa64dfr0_kvm),
> > +};
> > +
> > +/*
> > + * Override entries in @orig_ftrp with the ones in @new_ftrp when their shift
> > + * fields match.  The last entry of @orig_ftrp and @new_ftrp must be
> > + * ARM64_FTR_END (.width == 0).
> > + */
> > +static void arm64_ftr_reg_bits_overrite(struct arm64_ftr_bits *orig_ftrp,
> > +                                     struct arm64_ftr_bits *new_ftrp)
> > +{
> > +     struct arm64_ftr_bits *o_ftrp, *n_ftrp;
> > +
> > +     for (n_ftrp = new_ftrp; n_ftrp->width; n_ftrp++) {
> > +             for (o_ftrp = orig_ftrp; o_ftrp->width; o_ftrp++) {
> > +                     if (o_ftrp->shift == n_ftrp->shift) {
> > +                             *o_ftrp = *n_ftrp;
> > +                             break;
> > +                     }
> > +             }
> > +     }
> > +}
> > +
> > +/*
> > + * Copy arm64_ftr_bits entries from @src_ftrp to @dst_ftrp.  The last entries
> > + * of @dst_ftrp and @src_ftrp must be ARM64_FTR_END (.width == 0).
> > + */
> > +static void copy_arm64_ftr_bits(struct arm64_ftr_bits *dst_ftrp,
> > +                             const struct arm64_ftr_bits *src_ftrp)
> > +{
> > +     int i = 0;
> > +
> > +     for (; src_ftrp[i].width; i++) {
> > +             if (WARN_ON_ONCE(i >= (MAX_FTR_BITS_LEN - 1)))
> > +                     break;
> > +
> > +             dst_ftrp[i] = src_ftrp[i];
> > +     }
> > +
> > +     dst_ftrp[i].width = 0;
> > +}
> > +
> > +/*
> > + * Initialize arm64_ftr_bits_kvm.  Copy arm64_ftr_bits for each ID register
> > + * from arm64_ftr_regs to arm64_ftr_bits_kvm, and then override entries in
> > + * arm64_ftr_bits_kvm with ones in arm64_ftr_bits_kvm_override.
> > + */
> > +static int init_arm64_ftr_bits_kvm(void)
> > +{
> > +     struct arm64_ftr_bits ftr_temp[MAX_FTR_BITS_LEN];
> > +     static struct __ftr_reg_bits_entry *reg_bits_array, *bits, *o_bits;
> > +     int i, j, nent, ret;
> > +
> > +     mutex_lock(&arm64_ftr_bits_kvm_lock);
>
> This is initialized lazily, whenever KVM calls arm64_check_features(). I
> guess that's why it needs the lock (and possibly a barrier as you
> mentoin in your reply). Would it be possible to simplify things by
> initializing arm64_ftr_bits_kvm somewhere at boot time (in
> init_cpu_features maybe?)?

I agree that it could simplify the code.
I will look into initializing that earlier.


>
> > +     if (arm64_ftr_bits_kvm) {
> > +             /* Already initialized */
> > +             ret = 0;
> > +             goto unlock_exit;
> > +     }
> > +
> > +     nent = ARRAY_SIZE(arm64_ftr_regs);
> > +     reg_bits_array = kcalloc(nent, sizeof(struct __ftr_reg_bits_entry),
> > +                              GFP_KERNEL);
> > +     if (!reg_bits_array) {
> > +             ret = ENOMEM;
> > +             goto unlock_exit;
> > +     }
> > +
> > +     /* Copy entries from arm64_ftr_regs to reg_bits_array */
> > +     for (i = 0; i < nent; i++) {
> > +             bits = &reg_bits_array[i];
> > +             bits->sys_id = arm64_ftr_regs[i].sys_id;
> > +             bits->ftr_bits = (struct arm64_ftr_bits *)arm64_ftr_regs[i].reg->ftr_bits;
> > +     };
> > +
> > +     /*
> > +      * Override the entries in reg_bits_array with the ones in
> > +      * arm64_ftr_bits_kvm_override.
> > +      */
> > +     for (i = 0; i < ARRAY_SIZE(arm64_ftr_bits_kvm_override); i++) {
> > +             o_bits = &arm64_ftr_bits_kvm_override[i];
> > +             for (j = 0; j < nent; j++) {
> > +                     bits = &reg_bits_array[j];
> > +                     if (bits->sys_id != o_bits->sys_id)
> > +                             continue;
> > +
> > +                     memset(ftr_temp, 0, sizeof(ftr_temp));
> > +
> > +                     /*
> > +                      * Temporary save all entries in o_bits->ftr_bits
> > +                      * to ftr_temp.
> > +                      */
> > +                     copy_arm64_ftr_bits(ftr_temp, o_bits->ftr_bits);
> > +
> > +                     /*
> > +                      * Copy entries from bits->ftr_bits to o_bits->ftr_bits.
> > +                      */
> > +                     copy_arm64_ftr_bits(o_bits->ftr_bits, bits->ftr_bits);
> > +
> > +                     /*
> > +                      * Override entries in o_bits->ftr_bits with the
> > +                      * saved ones, and update bits->ftr_bits with
> > +                      * o_bits->ftr_bits.
> > +                      */
> > +                     arm64_ftr_reg_bits_overrite(o_bits->ftr_bits, ftr_temp);
> > +                     bits->ftr_bits = o_bits->ftr_bits;
> > +                     break;
> > +             }
> > +     }
> > +
> > +     arm64_ftr_bits_kvm_nentries = nent;
> > +     arm64_ftr_bits_kvm = reg_bits_array;
> > +     ret = 0;
> > +
> > +unlock_exit:
> > +     mutex_unlock(&arm64_ftr_bits_kvm_lock);
> > +     return ret;
> > +}
> > +
> > +static int search_cmp_ftr_reg_bits(const void *id, const void *regp)
> > +{
> > +     return ((int)(unsigned long)id -
> > +             (int)((const struct __ftr_reg_bits_entry *)regp)->sys_id);
> > +}
> > +
> > +static const struct arm64_ftr_bits *get_arm64_ftr_bits_kvm(u32 sys_id)
> > +{
> > +     const struct __ftr_reg_bits_entry *ret;
> > +     int err;
> > +
> > +     if (!arm64_ftr_bits_kvm) {
> > +             /* arm64_ftr_bits_kvm is not initialized yet. */
> > +             err = init_arm64_ftr_bits_kvm();
> > +             if (err)
> > +                     return NULL;
> > +     }
> > +
> > +     ret = bsearch((const void *)(unsigned long)sys_id,
> > +                   arm64_ftr_bits_kvm,
> > +                   arm64_ftr_bits_kvm_nentries,
> > +                   sizeof(arm64_ftr_bits_kvm[0]),
> > +                   search_cmp_ftr_reg_bits);
> > +     if (ret)
> > +             return ret->ftr_bits;
> > +
> > +     return NULL;
> > +}
> > +
> > +/*
> > + * Check if features (or levels of features) that are indicated in the ID
> > + * register value @val are also indicated in @limit.
> > + * This function is for KVM to check if features that are indicated in @val,
> > + * which will be used as the ID register value for its guest, are supported
> > + * on the host.
> > + * For AA64MMFR0_EL1.TGranX_2 fields, which don't follow the standard ID
> > + * scheme, the function checks if values of the fields in @val are the same
> > + * as the ones in @limit.
> > + */
> > +int arm64_check_features(u32 sys_reg, u64 val, u64 limit)
> > +{
> > +     const struct arm64_ftr_bits *ftrp = get_arm64_ftr_bits_kvm(sys_reg);
>
> Given that this is to be used only by KVM (and it's inside CONFIG_KVM),
> it might be better to have "kvm" somewhere in its name.

Yes, that might be better. I will change the name.

Thanks,
Reiji
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 01/26] KVM: arm64: Introduce a validation function for an ID register
@ 2022-01-28  6:01       ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-28  6:01 UTC (permalink / raw)
  To: Ricardo Koller
  Cc: Marc Zyngier, kvmarm, kvm, Linux ARM, James Morse,
	Alexandru Elisei, Suzuki K Poulose, Paolo Bonzini, Will Deacon,
	Andrew Jones, Peng Liang, Peter Shier, Oliver Upton, Jing Zhang,
	Raghavendra Rao Anata

Hi Ricardo,

On Tue, Jan 25, 2022 at 8:30 PM Ricardo Koller <ricarkol@google.com> wrote:
>
> Hey Reiji,
>
> On Wed, Jan 05, 2022 at 08:26:43PM -0800, Reiji Watanabe wrote:
> > Introduce arm64_check_features(), which does a basic validity checking
> > of an ID register value against the register's limit value, which is
> > generally the host's sanitized value.
> >
> > This function will be used by the following patches to check if an ID
> > register value that userspace tries to set for a guest can be supported
> > on the host.
> >
> > The validation is done using arm64_ftr_bits_kvm, which is created from
> > arm64_ftr_regs, with some entries overwritten by entries from
> > arm64_ftr_bits_kvm_override.
> >
> > Signed-off-by: Reiji Watanabe <reijiw@google.com>
> > ---
> >  arch/arm64/include/asm/cpufeature.h |   1 +
> >  arch/arm64/kernel/cpufeature.c      | 228 ++++++++++++++++++++++++++++
> >  2 files changed, 229 insertions(+)
> >
> > diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
> > index ef6be92b1921..eda7ddbed8cf 100644
> > --- a/arch/arm64/include/asm/cpufeature.h
> > +++ b/arch/arm64/include/asm/cpufeature.h
> > @@ -631,6 +631,7 @@ void check_local_cpu_capabilities(void);
> >
> >  u64 read_sanitised_ftr_reg(u32 id);
> >  u64 __read_sysreg_by_encoding(u32 sys_id);
> > +int arm64_check_features(u32 sys_reg, u64 val, u64 limit);
> >
> >  static inline bool cpu_supports_mixed_endian_el0(void)
> >  {
> > diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
> > index 6f3e677d88f1..48dff8b101d9 100644
> > --- a/arch/arm64/kernel/cpufeature.c
> > +++ b/arch/arm64/kernel/cpufeature.c
> > @@ -3140,3 +3140,231 @@ ssize_t cpu_show_meltdown(struct device *dev, struct device_attribute *attr,
> >               return sprintf(buf, "Vulnerable\n");
> >       }
> >  }
> > +
> > +#ifdef CONFIG_KVM
> > +/*
> > + * arm64_ftr_bits_kvm[] is used for KVM to check if features that are
> > + * indicated in an ID register value for the guest are available on the host.
> > + * arm64_ftr_bits_kvm[] is created based on arm64_ftr_regs[].  But, for
> > + * registers for which arm64_ftr_bits_kvm_override[] has a corresponding
> > + * entry, replace arm64_ftr_bits entries in arm64_ftr_bits_kvm[] with the
> > + * ones in arm64_ftr_bits_kvm_override[].
> > + */
> > +static struct __ftr_reg_bits_entry *arm64_ftr_bits_kvm;
> > +static size_t arm64_ftr_bits_kvm_nentries;
>
> I don't think this is really needed, as arm64_ftr_bits_kvm_override has
> to have the same size as arm64_ftr_bits_kvm. You could use
> ARRAY_SIZE(arm64_ftr_regs) like in get_arm64_ftr_reg_nowarn().

Thanks for the review!
Yes, you are right. I will remove arm64_ftr_bits_kvm_nentries,
and use ARRAY_SIZE(arm64_ftr_regs) instead.

> > +static DEFINE_MUTEX(arm64_ftr_bits_kvm_lock);
> > +
> > +/*
> > + * Number of arm64_ftr_bits entries for each register.
> > + * (Number of 4 bits fields in 64 bit register + 1 entry for ARM64_FTR_END)
> > + */
> > +#define      MAX_FTR_BITS_LEN        17
> > +
> > +/* Use FTR_LOWER_SAFE for AA64DFR0_EL1.PMUVER and AA64DFR0_EL1.DEBUGVER. */
> > +static struct arm64_ftr_bits ftr_id_aa64dfr0_kvm[MAX_FTR_BITS_LEN] = {
> > +     S_ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64DFR0_PMUVER_SHIFT, 4, 0),
> > +     ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x6),
> > +     ARM64_FTR_END,
> > +};
> > +
> > +#define      ARM64_FTR_REG_BITS(id, table)   {       \
> > +     .sys_id = id,                           \
> > +     .ftr_bits = &((table)[0]),              \
> > +}
> > +
> > +struct __ftr_reg_bits_entry {
> > +     u32     sys_id;
> > +     struct arm64_ftr_bits   *ftr_bits;
> > +};
> > +
> > +/*
> > + * All entries in arm64_ftr_bits_kvm_override[] are used to override
> > + * the corresponding entries in arm64_ftr_bits_kvm[].
> > + */
> > +static struct __ftr_reg_bits_entry arm64_ftr_bits_kvm_override[] = {
> > +     ARM64_FTR_REG_BITS(SYS_ID_AA64DFR0_EL1, ftr_id_aa64dfr0_kvm),
> > +};
> > +
> > +/*
> > + * Override entries in @orig_ftrp with the ones in @new_ftrp when their shift
> > + * fields match.  The last entry of @orig_ftrp and @new_ftrp must be
> > + * ARM64_FTR_END (.width == 0).
> > + */
> > +static void arm64_ftr_reg_bits_overrite(struct arm64_ftr_bits *orig_ftrp,
> > +                                     struct arm64_ftr_bits *new_ftrp)
> > +{
> > +     struct arm64_ftr_bits *o_ftrp, *n_ftrp;
> > +
> > +     for (n_ftrp = new_ftrp; n_ftrp->width; n_ftrp++) {
> > +             for (o_ftrp = orig_ftrp; o_ftrp->width; o_ftrp++) {
> > +                     if (o_ftrp->shift == n_ftrp->shift) {
> > +                             *o_ftrp = *n_ftrp;
> > +                             break;
> > +                     }
> > +             }
> > +     }
> > +}
> > +
> > +/*
> > + * Copy arm64_ftr_bits entries from @src_ftrp to @dst_ftrp.  The last entries
> > + * of @dst_ftrp and @src_ftrp must be ARM64_FTR_END (.width == 0).
> > + */
> > +static void copy_arm64_ftr_bits(struct arm64_ftr_bits *dst_ftrp,
> > +                             const struct arm64_ftr_bits *src_ftrp)
> > +{
> > +     int i = 0;
> > +
> > +     for (; src_ftrp[i].width; i++) {
> > +             if (WARN_ON_ONCE(i >= (MAX_FTR_BITS_LEN - 1)))
> > +                     break;
> > +
> > +             dst_ftrp[i] = src_ftrp[i];
> > +     }
> > +
> > +     dst_ftrp[i].width = 0;
> > +}
> > +
> > +/*
> > + * Initialize arm64_ftr_bits_kvm.  Copy arm64_ftr_bits for each ID register
> > + * from arm64_ftr_regs to arm64_ftr_bits_kvm, and then override entries in
> > + * arm64_ftr_bits_kvm with ones in arm64_ftr_bits_kvm_override.
> > + */
> > +static int init_arm64_ftr_bits_kvm(void)
> > +{
> > +     struct arm64_ftr_bits ftr_temp[MAX_FTR_BITS_LEN];
> > +     static struct __ftr_reg_bits_entry *reg_bits_array, *bits, *o_bits;
> > +     int i, j, nent, ret;
> > +
> > +     mutex_lock(&arm64_ftr_bits_kvm_lock);
>
> This is initialized lazily, whenever KVM calls arm64_check_features(). I
> guess that's why it needs the lock (and possibly a barrier as you
> mentoin in your reply). Would it be possible to simplify things by
> initializing arm64_ftr_bits_kvm somewhere at boot time (in
> init_cpu_features maybe?)?

I agree that it could simplify the code.
I will look into initializing that earlier.


>
> > +     if (arm64_ftr_bits_kvm) {
> > +             /* Already initialized */
> > +             ret = 0;
> > +             goto unlock_exit;
> > +     }
> > +
> > +     nent = ARRAY_SIZE(arm64_ftr_regs);
> > +     reg_bits_array = kcalloc(nent, sizeof(struct __ftr_reg_bits_entry),
> > +                              GFP_KERNEL);
> > +     if (!reg_bits_array) {
> > +             ret = ENOMEM;
> > +             goto unlock_exit;
> > +     }
> > +
> > +     /* Copy entries from arm64_ftr_regs to reg_bits_array */
> > +     for (i = 0; i < nent; i++) {
> > +             bits = &reg_bits_array[i];
> > +             bits->sys_id = arm64_ftr_regs[i].sys_id;
> > +             bits->ftr_bits = (struct arm64_ftr_bits *)arm64_ftr_regs[i].reg->ftr_bits;
> > +     };
> > +
> > +     /*
> > +      * Override the entries in reg_bits_array with the ones in
> > +      * arm64_ftr_bits_kvm_override.
> > +      */
> > +     for (i = 0; i < ARRAY_SIZE(arm64_ftr_bits_kvm_override); i++) {
> > +             o_bits = &arm64_ftr_bits_kvm_override[i];
> > +             for (j = 0; j < nent; j++) {
> > +                     bits = &reg_bits_array[j];
> > +                     if (bits->sys_id != o_bits->sys_id)
> > +                             continue;
> > +
> > +                     memset(ftr_temp, 0, sizeof(ftr_temp));
> > +
> > +                     /*
> > +                      * Temporary save all entries in o_bits->ftr_bits
> > +                      * to ftr_temp.
> > +                      */
> > +                     copy_arm64_ftr_bits(ftr_temp, o_bits->ftr_bits);
> > +
> > +                     /*
> > +                      * Copy entries from bits->ftr_bits to o_bits->ftr_bits.
> > +                      */
> > +                     copy_arm64_ftr_bits(o_bits->ftr_bits, bits->ftr_bits);
> > +
> > +                     /*
> > +                      * Override entries in o_bits->ftr_bits with the
> > +                      * saved ones, and update bits->ftr_bits with
> > +                      * o_bits->ftr_bits.
> > +                      */
> > +                     arm64_ftr_reg_bits_overrite(o_bits->ftr_bits, ftr_temp);
> > +                     bits->ftr_bits = o_bits->ftr_bits;
> > +                     break;
> > +             }
> > +     }
> > +
> > +     arm64_ftr_bits_kvm_nentries = nent;
> > +     arm64_ftr_bits_kvm = reg_bits_array;
> > +     ret = 0;
> > +
> > +unlock_exit:
> > +     mutex_unlock(&arm64_ftr_bits_kvm_lock);
> > +     return ret;
> > +}
> > +
> > +static int search_cmp_ftr_reg_bits(const void *id, const void *regp)
> > +{
> > +     return ((int)(unsigned long)id -
> > +             (int)((const struct __ftr_reg_bits_entry *)regp)->sys_id);
> > +}
> > +
> > +static const struct arm64_ftr_bits *get_arm64_ftr_bits_kvm(u32 sys_id)
> > +{
> > +     const struct __ftr_reg_bits_entry *ret;
> > +     int err;
> > +
> > +     if (!arm64_ftr_bits_kvm) {
> > +             /* arm64_ftr_bits_kvm is not initialized yet. */
> > +             err = init_arm64_ftr_bits_kvm();
> > +             if (err)
> > +                     return NULL;
> > +     }
> > +
> > +     ret = bsearch((const void *)(unsigned long)sys_id,
> > +                   arm64_ftr_bits_kvm,
> > +                   arm64_ftr_bits_kvm_nentries,
> > +                   sizeof(arm64_ftr_bits_kvm[0]),
> > +                   search_cmp_ftr_reg_bits);
> > +     if (ret)
> > +             return ret->ftr_bits;
> > +
> > +     return NULL;
> > +}
> > +
> > +/*
> > + * Check if features (or levels of features) that are indicated in the ID
> > + * register value @val are also indicated in @limit.
> > + * This function is for KVM to check if features that are indicated in @val,
> > + * which will be used as the ID register value for its guest, are supported
> > + * on the host.
> > + * For AA64MMFR0_EL1.TGranX_2 fields, which don't follow the standard ID
> > + * scheme, the function checks if values of the fields in @val are the same
> > + * as the ones in @limit.
> > + */
> > +int arm64_check_features(u32 sys_reg, u64 val, u64 limit)
> > +{
> > +     const struct arm64_ftr_bits *ftrp = get_arm64_ftr_bits_kvm(sys_reg);
>
> Given that this is to be used only by KVM (and it's inside CONFIG_KVM),
> it might be better to have "kvm" somewhere in its name.

Yes, that might be better. I will change the name.

Thanks,
Reiji

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 02/26] KVM: arm64: Save ID registers' sanitized value per guest
  2022-01-26  5:22     ` Ricardo Koller
  (?)
@ 2022-01-28  6:24       ` Reiji Watanabe
  -1 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-28  6:24 UTC (permalink / raw)
  To: Ricardo Koller
  Cc: Marc Zyngier, kvmarm, kvm, Linux ARM, James Morse,
	Alexandru Elisei, Suzuki K Poulose, Paolo Bonzini, Will Deacon,
	Andrew Jones, Peng Liang, Peter Shier, Oliver Upton, Jing Zhang,
	Raghavendra Rao Anata

Hi Ricardo,

On Tue, Jan 25, 2022 at 9:22 PM Ricardo Koller <ricarkol@google.com> wrote:
>
> On Wed, Jan 05, 2022 at 08:26:44PM -0800, Reiji Watanabe wrote:
> > Introduce id_regs[] in kvm_arch as a storage of guest's ID registers,
> > and save ID registers' sanitized value in the array at KVM_CREATE_VM.
> > Use the saved ones when ID registers are read by the guest or
> > userspace (via KVM_GET_ONE_REG).
> >
> > Signed-off-by: Reiji Watanabe <reijiw@google.com>
> > ---
> >  arch/arm64/include/asm/kvm_host.h | 16 ++++++++
> >  arch/arm64/kvm/arm.c              |  1 +
> >  arch/arm64/kvm/sys_regs.c         | 62 ++++++++++++++++++++++---------
> >  3 files changed, 62 insertions(+), 17 deletions(-)
> >
> > diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> > index 2a5f7f38006f..c789a0137f58 100644
> > --- a/arch/arm64/include/asm/kvm_host.h
> > +++ b/arch/arm64/include/asm/kvm_host.h
> > @@ -102,6 +102,17 @@ struct kvm_s2_mmu {
> >  struct kvm_arch_memory_slot {
> >  };
> >
> > +/*
> > + * (Op0, Op1, CRn, CRm, Op2) of ID registers is (3, 0, 0, crm, op2),
> > + * where 0<=crm<8, 0<=op2<8.
>
> Is this observation based on this table?
>
> Table D12-2 System instruction encodings for non-Debug System register accesses
> in that case, it seems that the ID registers list might grow after
> crm=7, and as CRm has 4 bits, why not 16*8=128?

This is basically for registers that are already reserved as RAZ in the
table (sys_reg_descs[] has entries for the reserved ones as well).
Registers with crm > 7 are not reserved yet, and that will be expanded
later as needed later.


>
> > + */
> > +#define KVM_ARM_ID_REG_MAX_NUM       64
> > +#define IDREG_IDX(id)                ((sys_reg_CRm(id) << 3) | sys_reg_Op2(id))
> > +#define is_id_reg(id)        \
> > +     (sys_reg_Op0(id) == 3 && sys_reg_Op1(id) == 0 &&        \
> > +      sys_reg_CRn(id) == 0 && sys_reg_CRm(id) >= 0 &&        \
> > +      sys_reg_CRm(id) < 8)
> > +
> >  struct kvm_arch {
> >       struct kvm_s2_mmu mmu;
> >
> > @@ -137,6 +148,9 @@ struct kvm_arch {
> >
> >       /* Memory Tagging Extension enabled for the guest */
> >       bool mte_enabled;
> > +
> > +     /* ID registers for the guest. */
> > +     u64 id_regs[KVM_ARM_ID_REG_MAX_NUM];
> >  };
> >
> >  struct kvm_vcpu_fault_info {
> > @@ -734,6 +748,8 @@ int kvm_arm_vcpu_arch_has_attr(struct kvm_vcpu *vcpu,
> >  long kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
> >                               struct kvm_arm_copy_mte_tags *copy_tags);
> >
> > +void set_default_id_regs(struct kvm *kvm);
> > +
> >  /* Guest/host FPSIMD coordination helpers */
> >  int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu);
> >  void kvm_arch_vcpu_load_fp(struct kvm_vcpu *vcpu);
> > diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> > index e4727dc771bf..5f497a0af254 100644
> > --- a/arch/arm64/kvm/arm.c
> > +++ b/arch/arm64/kvm/arm.c
> > @@ -156,6 +156,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
> >       kvm->arch.max_vcpus = kvm_arm_default_max_vcpus();
> >
> >       set_default_spectre(kvm);
> > +     set_default_id_regs(kvm);
> >
> >       return ret;
> >  out_free_stage2_pgd:
> > diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> > index e3ec1a44f94d..80dc62f98ef0 100644
> > --- a/arch/arm64/kvm/sys_regs.c
> > +++ b/arch/arm64/kvm/sys_regs.c
> > @@ -33,6 +33,8 @@
> >
> >  #include "trace.h"
> >
> > +static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id);
> > +
> >  /*
> >   * All of this file is extremely similar to the ARM coproc.c, but the
> >   * types are different. My gut feeling is that it should be pretty
> > @@ -273,7 +275,7 @@ static bool trap_loregion(struct kvm_vcpu *vcpu,
> >                         struct sys_reg_params *p,
> >                         const struct sys_reg_desc *r)
> >  {
> > -     u64 val = read_sanitised_ftr_reg(SYS_ID_AA64MMFR1_EL1);
> > +     u64 val = __read_id_reg(vcpu, SYS_ID_AA64MMFR1_EL1);
> >       u32 sr = reg_to_encoding(r);
> >
> >       if (!(val & (0xfUL << ID_AA64MMFR1_LOR_SHIFT))) {
> > @@ -1059,17 +1061,9 @@ static bool access_arch_timer(struct kvm_vcpu *vcpu,
> >       return true;
> >  }
> >
> > -/* Read a sanitised cpufeature ID register by sys_reg_desc */
> > -static u64 read_id_reg(const struct kvm_vcpu *vcpu,
> > -             struct sys_reg_desc const *r, bool raz)
> > +static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
> >  {
> > -     u32 id = reg_to_encoding(r);
> > -     u64 val;
> > -
> > -     if (raz)
> > -             return 0;
> > -
> > -     val = read_sanitised_ftr_reg(id);
> > +     u64 val = vcpu->kvm->arch.id_regs[IDREG_IDX(id)];
> >
> >       switch (id) {
> >       case SYS_ID_AA64PFR0_EL1:
> > @@ -1119,6 +1113,14 @@ static u64 read_id_reg(const struct kvm_vcpu *vcpu,
> >       return val;
> >  }
> >
> > +static u64 read_id_reg(const struct kvm_vcpu *vcpu,
> > +                    struct sys_reg_desc const *r, bool raz)
> > +{
> > +     u32 id = reg_to_encoding(r);
> > +
> > +     return raz ? 0 : __read_id_reg(vcpu, id);
> > +}
> > +
> >  static unsigned int id_visibility(const struct kvm_vcpu *vcpu,
> >                                 const struct sys_reg_desc *r)
> >  {
> > @@ -1223,9 +1225,8 @@ static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
> >  /*
> >   * cpufeature ID register user accessors
> >   *
> > - * For now, these registers are immutable for userspace, so no values
> > - * are stored, and for set_id_reg() we don't allow the effective value
> > - * to be changed.
> > + * For now, these registers are immutable for userspace, so for set_id_reg()
> > + * we don't allow the effective value to be changed.
> >   */
> >  static int __get_id_reg(const struct kvm_vcpu *vcpu,
> >                       const struct sys_reg_desc *rd, void __user *uaddr,
> > @@ -1237,7 +1238,7 @@ static int __get_id_reg(const struct kvm_vcpu *vcpu,
> >       return reg_to_user(uaddr, &val, id);
> >  }
> >
> > -static int __set_id_reg(const struct kvm_vcpu *vcpu,
> > +static int __set_id_reg(struct kvm_vcpu *vcpu,
> >                       const struct sys_reg_desc *rd, void __user *uaddr,
> >                       bool raz)
> >  {
> > @@ -1837,8 +1838,8 @@ static bool trap_dbgdidr(struct kvm_vcpu *vcpu,
> >       if (p->is_write) {
> >               return ignore_write(vcpu, p);
> >       } else {
> > -             u64 dfr = read_sanitised_ftr_reg(SYS_ID_AA64DFR0_EL1);
> > -             u64 pfr = read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1);
> > +             u64 dfr = __read_id_reg(vcpu, SYS_ID_AA64DFR0_EL1);
> > +             u64 pfr = __read_id_reg(vcpu, SYS_ID_AA64PFR0_EL1);
> >               u32 el3 = !!cpuid_feature_extract_unsigned_field(pfr, ID_AA64PFR0_EL3_SHIFT);
> >
> >               p->regval = ((((dfr >> ID_AA64DFR0_WRPS_SHIFT) & 0xf) << 28) |
> > @@ -2850,3 +2851,30 @@ void kvm_sys_reg_table_init(void)
> >       /* Clear all higher bits. */
> >       cache_levels &= (1 << (i*3))-1;
> >  }
> > +
> > +/*
> > + * Set the guest's ID registers that are defined in sys_reg_descs[]
> > + * with ID_SANITISED() to the host's sanitized value.
> > + */
> > +void set_default_id_regs(struct kvm *kvm)
> > +{
> > +     int i;
> > +     u32 id;
> > +     const struct sys_reg_desc *rd;
> > +     u64 val;
> > +
> > +     for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
> > +             rd = &sys_reg_descs[i];
> > +             if (rd->access != access_id_reg)
> > +                     /* Not ID register, or hidden/reserved ID register */
> > +                     continue;
> > +
> > +             id = reg_to_encoding(rd);
> > +             if (WARN_ON_ONCE(!is_id_reg(id)))
> > +                     /* Shouldn't happen */
> > +                     continue;
> > +
> > +             val = read_sanitised_ftr_reg(id);
>
> I'm a bit confused. Shouldn't the default+sanitized values already use
> arm64_ftr_bits_kvm (instead of arm64_ftr_regs)?

I'm not sure if I understand your question.
arm64_ftr_bits_kvm is used for feature support checkings when
userspace tries to modify a value of ID registers.
With this patch, KVM just saves the sanitized values in the kvm's
buffer, but userspace is still not allowed to modify values of ID
registers yet.
I hope it answers your question.

Thanks,
Reiji

>
> > +             kvm->arch.id_regs[IDREG_IDX(id)] = val;
> > +     }
> > +}
> > --
> > 2.34.1.448.ga2b2bfdf31-goog
> >

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 02/26] KVM: arm64: Save ID registers' sanitized value per guest
@ 2022-01-28  6:24       ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-28  6:24 UTC (permalink / raw)
  To: Ricardo Koller
  Cc: kvm, Marc Zyngier, Peter Shier, Will Deacon, Paolo Bonzini,
	kvmarm, Linux ARM

Hi Ricardo,

On Tue, Jan 25, 2022 at 9:22 PM Ricardo Koller <ricarkol@google.com> wrote:
>
> On Wed, Jan 05, 2022 at 08:26:44PM -0800, Reiji Watanabe wrote:
> > Introduce id_regs[] in kvm_arch as a storage of guest's ID registers,
> > and save ID registers' sanitized value in the array at KVM_CREATE_VM.
> > Use the saved ones when ID registers are read by the guest or
> > userspace (via KVM_GET_ONE_REG).
> >
> > Signed-off-by: Reiji Watanabe <reijiw@google.com>
> > ---
> >  arch/arm64/include/asm/kvm_host.h | 16 ++++++++
> >  arch/arm64/kvm/arm.c              |  1 +
> >  arch/arm64/kvm/sys_regs.c         | 62 ++++++++++++++++++++++---------
> >  3 files changed, 62 insertions(+), 17 deletions(-)
> >
> > diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> > index 2a5f7f38006f..c789a0137f58 100644
> > --- a/arch/arm64/include/asm/kvm_host.h
> > +++ b/arch/arm64/include/asm/kvm_host.h
> > @@ -102,6 +102,17 @@ struct kvm_s2_mmu {
> >  struct kvm_arch_memory_slot {
> >  };
> >
> > +/*
> > + * (Op0, Op1, CRn, CRm, Op2) of ID registers is (3, 0, 0, crm, op2),
> > + * where 0<=crm<8, 0<=op2<8.
>
> Is this observation based on this table?
>
> Table D12-2 System instruction encodings for non-Debug System register accesses
> in that case, it seems that the ID registers list might grow after
> crm=7, and as CRm has 4 bits, why not 16*8=128?

This is basically for registers that are already reserved as RAZ in the
table (sys_reg_descs[] has entries for the reserved ones as well).
Registers with crm > 7 are not reserved yet, and that will be expanded
later as needed later.


>
> > + */
> > +#define KVM_ARM_ID_REG_MAX_NUM       64
> > +#define IDREG_IDX(id)                ((sys_reg_CRm(id) << 3) | sys_reg_Op2(id))
> > +#define is_id_reg(id)        \
> > +     (sys_reg_Op0(id) == 3 && sys_reg_Op1(id) == 0 &&        \
> > +      sys_reg_CRn(id) == 0 && sys_reg_CRm(id) >= 0 &&        \
> > +      sys_reg_CRm(id) < 8)
> > +
> >  struct kvm_arch {
> >       struct kvm_s2_mmu mmu;
> >
> > @@ -137,6 +148,9 @@ struct kvm_arch {
> >
> >       /* Memory Tagging Extension enabled for the guest */
> >       bool mte_enabled;
> > +
> > +     /* ID registers for the guest. */
> > +     u64 id_regs[KVM_ARM_ID_REG_MAX_NUM];
> >  };
> >
> >  struct kvm_vcpu_fault_info {
> > @@ -734,6 +748,8 @@ int kvm_arm_vcpu_arch_has_attr(struct kvm_vcpu *vcpu,
> >  long kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
> >                               struct kvm_arm_copy_mte_tags *copy_tags);
> >
> > +void set_default_id_regs(struct kvm *kvm);
> > +
> >  /* Guest/host FPSIMD coordination helpers */
> >  int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu);
> >  void kvm_arch_vcpu_load_fp(struct kvm_vcpu *vcpu);
> > diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> > index e4727dc771bf..5f497a0af254 100644
> > --- a/arch/arm64/kvm/arm.c
> > +++ b/arch/arm64/kvm/arm.c
> > @@ -156,6 +156,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
> >       kvm->arch.max_vcpus = kvm_arm_default_max_vcpus();
> >
> >       set_default_spectre(kvm);
> > +     set_default_id_regs(kvm);
> >
> >       return ret;
> >  out_free_stage2_pgd:
> > diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> > index e3ec1a44f94d..80dc62f98ef0 100644
> > --- a/arch/arm64/kvm/sys_regs.c
> > +++ b/arch/arm64/kvm/sys_regs.c
> > @@ -33,6 +33,8 @@
> >
> >  #include "trace.h"
> >
> > +static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id);
> > +
> >  /*
> >   * All of this file is extremely similar to the ARM coproc.c, but the
> >   * types are different. My gut feeling is that it should be pretty
> > @@ -273,7 +275,7 @@ static bool trap_loregion(struct kvm_vcpu *vcpu,
> >                         struct sys_reg_params *p,
> >                         const struct sys_reg_desc *r)
> >  {
> > -     u64 val = read_sanitised_ftr_reg(SYS_ID_AA64MMFR1_EL1);
> > +     u64 val = __read_id_reg(vcpu, SYS_ID_AA64MMFR1_EL1);
> >       u32 sr = reg_to_encoding(r);
> >
> >       if (!(val & (0xfUL << ID_AA64MMFR1_LOR_SHIFT))) {
> > @@ -1059,17 +1061,9 @@ static bool access_arch_timer(struct kvm_vcpu *vcpu,
> >       return true;
> >  }
> >
> > -/* Read a sanitised cpufeature ID register by sys_reg_desc */
> > -static u64 read_id_reg(const struct kvm_vcpu *vcpu,
> > -             struct sys_reg_desc const *r, bool raz)
> > +static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
> >  {
> > -     u32 id = reg_to_encoding(r);
> > -     u64 val;
> > -
> > -     if (raz)
> > -             return 0;
> > -
> > -     val = read_sanitised_ftr_reg(id);
> > +     u64 val = vcpu->kvm->arch.id_regs[IDREG_IDX(id)];
> >
> >       switch (id) {
> >       case SYS_ID_AA64PFR0_EL1:
> > @@ -1119,6 +1113,14 @@ static u64 read_id_reg(const struct kvm_vcpu *vcpu,
> >       return val;
> >  }
> >
> > +static u64 read_id_reg(const struct kvm_vcpu *vcpu,
> > +                    struct sys_reg_desc const *r, bool raz)
> > +{
> > +     u32 id = reg_to_encoding(r);
> > +
> > +     return raz ? 0 : __read_id_reg(vcpu, id);
> > +}
> > +
> >  static unsigned int id_visibility(const struct kvm_vcpu *vcpu,
> >                                 const struct sys_reg_desc *r)
> >  {
> > @@ -1223,9 +1225,8 @@ static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
> >  /*
> >   * cpufeature ID register user accessors
> >   *
> > - * For now, these registers are immutable for userspace, so no values
> > - * are stored, and for set_id_reg() we don't allow the effective value
> > - * to be changed.
> > + * For now, these registers are immutable for userspace, so for set_id_reg()
> > + * we don't allow the effective value to be changed.
> >   */
> >  static int __get_id_reg(const struct kvm_vcpu *vcpu,
> >                       const struct sys_reg_desc *rd, void __user *uaddr,
> > @@ -1237,7 +1238,7 @@ static int __get_id_reg(const struct kvm_vcpu *vcpu,
> >       return reg_to_user(uaddr, &val, id);
> >  }
> >
> > -static int __set_id_reg(const struct kvm_vcpu *vcpu,
> > +static int __set_id_reg(struct kvm_vcpu *vcpu,
> >                       const struct sys_reg_desc *rd, void __user *uaddr,
> >                       bool raz)
> >  {
> > @@ -1837,8 +1838,8 @@ static bool trap_dbgdidr(struct kvm_vcpu *vcpu,
> >       if (p->is_write) {
> >               return ignore_write(vcpu, p);
> >       } else {
> > -             u64 dfr = read_sanitised_ftr_reg(SYS_ID_AA64DFR0_EL1);
> > -             u64 pfr = read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1);
> > +             u64 dfr = __read_id_reg(vcpu, SYS_ID_AA64DFR0_EL1);
> > +             u64 pfr = __read_id_reg(vcpu, SYS_ID_AA64PFR0_EL1);
> >               u32 el3 = !!cpuid_feature_extract_unsigned_field(pfr, ID_AA64PFR0_EL3_SHIFT);
> >
> >               p->regval = ((((dfr >> ID_AA64DFR0_WRPS_SHIFT) & 0xf) << 28) |
> > @@ -2850,3 +2851,30 @@ void kvm_sys_reg_table_init(void)
> >       /* Clear all higher bits. */
> >       cache_levels &= (1 << (i*3))-1;
> >  }
> > +
> > +/*
> > + * Set the guest's ID registers that are defined in sys_reg_descs[]
> > + * with ID_SANITISED() to the host's sanitized value.
> > + */
> > +void set_default_id_regs(struct kvm *kvm)
> > +{
> > +     int i;
> > +     u32 id;
> > +     const struct sys_reg_desc *rd;
> > +     u64 val;
> > +
> > +     for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
> > +             rd = &sys_reg_descs[i];
> > +             if (rd->access != access_id_reg)
> > +                     /* Not ID register, or hidden/reserved ID register */
> > +                     continue;
> > +
> > +             id = reg_to_encoding(rd);
> > +             if (WARN_ON_ONCE(!is_id_reg(id)))
> > +                     /* Shouldn't happen */
> > +                     continue;
> > +
> > +             val = read_sanitised_ftr_reg(id);
>
> I'm a bit confused. Shouldn't the default+sanitized values already use
> arm64_ftr_bits_kvm (instead of arm64_ftr_regs)?

I'm not sure if I understand your question.
arm64_ftr_bits_kvm is used for feature support checkings when
userspace tries to modify a value of ID registers.
With this patch, KVM just saves the sanitized values in the kvm's
buffer, but userspace is still not allowed to modify values of ID
registers yet.
I hope it answers your question.

Thanks,
Reiji

>
> > +             kvm->arch.id_regs[IDREG_IDX(id)] = val;
> > +     }
> > +}
> > --
> > 2.34.1.448.ga2b2bfdf31-goog
> >
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 02/26] KVM: arm64: Save ID registers' sanitized value per guest
@ 2022-01-28  6:24       ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-28  6:24 UTC (permalink / raw)
  To: Ricardo Koller
  Cc: Marc Zyngier, kvmarm, kvm, Linux ARM, James Morse,
	Alexandru Elisei, Suzuki K Poulose, Paolo Bonzini, Will Deacon,
	Andrew Jones, Peng Liang, Peter Shier, Oliver Upton, Jing Zhang,
	Raghavendra Rao Anata

Hi Ricardo,

On Tue, Jan 25, 2022 at 9:22 PM Ricardo Koller <ricarkol@google.com> wrote:
>
> On Wed, Jan 05, 2022 at 08:26:44PM -0800, Reiji Watanabe wrote:
> > Introduce id_regs[] in kvm_arch as a storage of guest's ID registers,
> > and save ID registers' sanitized value in the array at KVM_CREATE_VM.
> > Use the saved ones when ID registers are read by the guest or
> > userspace (via KVM_GET_ONE_REG).
> >
> > Signed-off-by: Reiji Watanabe <reijiw@google.com>
> > ---
> >  arch/arm64/include/asm/kvm_host.h | 16 ++++++++
> >  arch/arm64/kvm/arm.c              |  1 +
> >  arch/arm64/kvm/sys_regs.c         | 62 ++++++++++++++++++++++---------
> >  3 files changed, 62 insertions(+), 17 deletions(-)
> >
> > diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> > index 2a5f7f38006f..c789a0137f58 100644
> > --- a/arch/arm64/include/asm/kvm_host.h
> > +++ b/arch/arm64/include/asm/kvm_host.h
> > @@ -102,6 +102,17 @@ struct kvm_s2_mmu {
> >  struct kvm_arch_memory_slot {
> >  };
> >
> > +/*
> > + * (Op0, Op1, CRn, CRm, Op2) of ID registers is (3, 0, 0, crm, op2),
> > + * where 0<=crm<8, 0<=op2<8.
>
> Is this observation based on this table?
>
> Table D12-2 System instruction encodings for non-Debug System register accesses
> in that case, it seems that the ID registers list might grow after
> crm=7, and as CRm has 4 bits, why not 16*8=128?

This is basically for registers that are already reserved as RAZ in the
table (sys_reg_descs[] has entries for the reserved ones as well).
Registers with crm > 7 are not reserved yet, and that will be expanded
later as needed later.


>
> > + */
> > +#define KVM_ARM_ID_REG_MAX_NUM       64
> > +#define IDREG_IDX(id)                ((sys_reg_CRm(id) << 3) | sys_reg_Op2(id))
> > +#define is_id_reg(id)        \
> > +     (sys_reg_Op0(id) == 3 && sys_reg_Op1(id) == 0 &&        \
> > +      sys_reg_CRn(id) == 0 && sys_reg_CRm(id) >= 0 &&        \
> > +      sys_reg_CRm(id) < 8)
> > +
> >  struct kvm_arch {
> >       struct kvm_s2_mmu mmu;
> >
> > @@ -137,6 +148,9 @@ struct kvm_arch {
> >
> >       /* Memory Tagging Extension enabled for the guest */
> >       bool mte_enabled;
> > +
> > +     /* ID registers for the guest. */
> > +     u64 id_regs[KVM_ARM_ID_REG_MAX_NUM];
> >  };
> >
> >  struct kvm_vcpu_fault_info {
> > @@ -734,6 +748,8 @@ int kvm_arm_vcpu_arch_has_attr(struct kvm_vcpu *vcpu,
> >  long kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
> >                               struct kvm_arm_copy_mte_tags *copy_tags);
> >
> > +void set_default_id_regs(struct kvm *kvm);
> > +
> >  /* Guest/host FPSIMD coordination helpers */
> >  int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu);
> >  void kvm_arch_vcpu_load_fp(struct kvm_vcpu *vcpu);
> > diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> > index e4727dc771bf..5f497a0af254 100644
> > --- a/arch/arm64/kvm/arm.c
> > +++ b/arch/arm64/kvm/arm.c
> > @@ -156,6 +156,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
> >       kvm->arch.max_vcpus = kvm_arm_default_max_vcpus();
> >
> >       set_default_spectre(kvm);
> > +     set_default_id_regs(kvm);
> >
> >       return ret;
> >  out_free_stage2_pgd:
> > diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> > index e3ec1a44f94d..80dc62f98ef0 100644
> > --- a/arch/arm64/kvm/sys_regs.c
> > +++ b/arch/arm64/kvm/sys_regs.c
> > @@ -33,6 +33,8 @@
> >
> >  #include "trace.h"
> >
> > +static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id);
> > +
> >  /*
> >   * All of this file is extremely similar to the ARM coproc.c, but the
> >   * types are different. My gut feeling is that it should be pretty
> > @@ -273,7 +275,7 @@ static bool trap_loregion(struct kvm_vcpu *vcpu,
> >                         struct sys_reg_params *p,
> >                         const struct sys_reg_desc *r)
> >  {
> > -     u64 val = read_sanitised_ftr_reg(SYS_ID_AA64MMFR1_EL1);
> > +     u64 val = __read_id_reg(vcpu, SYS_ID_AA64MMFR1_EL1);
> >       u32 sr = reg_to_encoding(r);
> >
> >       if (!(val & (0xfUL << ID_AA64MMFR1_LOR_SHIFT))) {
> > @@ -1059,17 +1061,9 @@ static bool access_arch_timer(struct kvm_vcpu *vcpu,
> >       return true;
> >  }
> >
> > -/* Read a sanitised cpufeature ID register by sys_reg_desc */
> > -static u64 read_id_reg(const struct kvm_vcpu *vcpu,
> > -             struct sys_reg_desc const *r, bool raz)
> > +static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
> >  {
> > -     u32 id = reg_to_encoding(r);
> > -     u64 val;
> > -
> > -     if (raz)
> > -             return 0;
> > -
> > -     val = read_sanitised_ftr_reg(id);
> > +     u64 val = vcpu->kvm->arch.id_regs[IDREG_IDX(id)];
> >
> >       switch (id) {
> >       case SYS_ID_AA64PFR0_EL1:
> > @@ -1119,6 +1113,14 @@ static u64 read_id_reg(const struct kvm_vcpu *vcpu,
> >       return val;
> >  }
> >
> > +static u64 read_id_reg(const struct kvm_vcpu *vcpu,
> > +                    struct sys_reg_desc const *r, bool raz)
> > +{
> > +     u32 id = reg_to_encoding(r);
> > +
> > +     return raz ? 0 : __read_id_reg(vcpu, id);
> > +}
> > +
> >  static unsigned int id_visibility(const struct kvm_vcpu *vcpu,
> >                                 const struct sys_reg_desc *r)
> >  {
> > @@ -1223,9 +1225,8 @@ static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
> >  /*
> >   * cpufeature ID register user accessors
> >   *
> > - * For now, these registers are immutable for userspace, so no values
> > - * are stored, and for set_id_reg() we don't allow the effective value
> > - * to be changed.
> > + * For now, these registers are immutable for userspace, so for set_id_reg()
> > + * we don't allow the effective value to be changed.
> >   */
> >  static int __get_id_reg(const struct kvm_vcpu *vcpu,
> >                       const struct sys_reg_desc *rd, void __user *uaddr,
> > @@ -1237,7 +1238,7 @@ static int __get_id_reg(const struct kvm_vcpu *vcpu,
> >       return reg_to_user(uaddr, &val, id);
> >  }
> >
> > -static int __set_id_reg(const struct kvm_vcpu *vcpu,
> > +static int __set_id_reg(struct kvm_vcpu *vcpu,
> >                       const struct sys_reg_desc *rd, void __user *uaddr,
> >                       bool raz)
> >  {
> > @@ -1837,8 +1838,8 @@ static bool trap_dbgdidr(struct kvm_vcpu *vcpu,
> >       if (p->is_write) {
> >               return ignore_write(vcpu, p);
> >       } else {
> > -             u64 dfr = read_sanitised_ftr_reg(SYS_ID_AA64DFR0_EL1);
> > -             u64 pfr = read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1);
> > +             u64 dfr = __read_id_reg(vcpu, SYS_ID_AA64DFR0_EL1);
> > +             u64 pfr = __read_id_reg(vcpu, SYS_ID_AA64PFR0_EL1);
> >               u32 el3 = !!cpuid_feature_extract_unsigned_field(pfr, ID_AA64PFR0_EL3_SHIFT);
> >
> >               p->regval = ((((dfr >> ID_AA64DFR0_WRPS_SHIFT) & 0xf) << 28) |
> > @@ -2850,3 +2851,30 @@ void kvm_sys_reg_table_init(void)
> >       /* Clear all higher bits. */
> >       cache_levels &= (1 << (i*3))-1;
> >  }
> > +
> > +/*
> > + * Set the guest's ID registers that are defined in sys_reg_descs[]
> > + * with ID_SANITISED() to the host's sanitized value.
> > + */
> > +void set_default_id_regs(struct kvm *kvm)
> > +{
> > +     int i;
> > +     u32 id;
> > +     const struct sys_reg_desc *rd;
> > +     u64 val;
> > +
> > +     for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
> > +             rd = &sys_reg_descs[i];
> > +             if (rd->access != access_id_reg)
> > +                     /* Not ID register, or hidden/reserved ID register */
> > +                     continue;
> > +
> > +             id = reg_to_encoding(rd);
> > +             if (WARN_ON_ONCE(!is_id_reg(id)))
> > +                     /* Shouldn't happen */
> > +                     continue;
> > +
> > +             val = read_sanitised_ftr_reg(id);
>
> I'm a bit confused. Shouldn't the default+sanitized values already use
> arm64_ftr_bits_kvm (instead of arm64_ftr_regs)?

I'm not sure if I understand your question.
arm64_ftr_bits_kvm is used for feature support checkings when
userspace tries to modify a value of ID registers.
With this patch, KVM just saves the sanitized values in the kvm's
buffer, but userspace is still not allowed to modify values of ID
registers yet.
I hope it answers your question.

Thanks,
Reiji

>
> > +             kvm->arch.id_regs[IDREG_IDX(id)] = val;
> > +     }
> > +}
> > --
> > 2.34.1.448.ga2b2bfdf31-goog
> >

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 02/26] KVM: arm64: Save ID registers' sanitized value per guest
  2022-01-28  6:24       ` Reiji Watanabe
  (?)
@ 2022-01-28 19:27         ` Ricardo Koller
  -1 siblings, 0 replies; 201+ messages in thread
From: Ricardo Koller @ 2022-01-28 19:27 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: Marc Zyngier, kvmarm, kvm, Linux ARM, James Morse,
	Alexandru Elisei, Suzuki K Poulose, Paolo Bonzini, Will Deacon,
	Andrew Jones, Peng Liang, Peter Shier, Oliver Upton, Jing Zhang,
	Raghavendra Rao Anata

On Thu, Jan 27, 2022 at 10:24 PM Reiji Watanabe <reijiw@google.com> wrote:
>
> Hi Ricardo,
>
> On Tue, Jan 25, 2022 at 9:22 PM Ricardo Koller <ricarkol@google.com> wrote:
> >
> > On Wed, Jan 05, 2022 at 08:26:44PM -0800, Reiji Watanabe wrote:
> > > Introduce id_regs[] in kvm_arch as a storage of guest's ID registers,
> > > and save ID registers' sanitized value in the array at KVM_CREATE_VM.
> > > Use the saved ones when ID registers are read by the guest or
> > > userspace (via KVM_GET_ONE_REG).
> > >
> > > Signed-off-by: Reiji Watanabe <reijiw@google.com>
> > > ---
> > >  arch/arm64/include/asm/kvm_host.h | 16 ++++++++
> > >  arch/arm64/kvm/arm.c              |  1 +
> > >  arch/arm64/kvm/sys_regs.c         | 62 ++++++++++++++++++++++---------
> > >  3 files changed, 62 insertions(+), 17 deletions(-)
> > >
> > > diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> > > index 2a5f7f38006f..c789a0137f58 100644
> > > --- a/arch/arm64/include/asm/kvm_host.h
> > > +++ b/arch/arm64/include/asm/kvm_host.h
> > > @@ -102,6 +102,17 @@ struct kvm_s2_mmu {
> > >  struct kvm_arch_memory_slot {
> > >  };
> > >
> > > +/*
> > > + * (Op0, Op1, CRn, CRm, Op2) of ID registers is (3, 0, 0, crm, op2),
> > > + * where 0<=crm<8, 0<=op2<8.
> >
> > Is this observation based on this table?
> >
> > Table D12-2 System instruction encodings for non-Debug System register accesses
> > in that case, it seems that the ID registers list might grow after
> > crm=7, and as CRm has 4 bits, why not 16*8=128?
>
> This is basically for registers that are already reserved as RAZ in the
> table (sys_reg_descs[] has entries for the reserved ones as well).
> Registers with crm > 7 are not reserved yet, and that will be expanded
> later as needed later.
>
>
> >
> > > + */
> > > +#define KVM_ARM_ID_REG_MAX_NUM       64
> > > +#define IDREG_IDX(id)                ((sys_reg_CRm(id) << 3) | sys_reg_Op2(id))
> > > +#define is_id_reg(id)        \
> > > +     (sys_reg_Op0(id) == 3 && sys_reg_Op1(id) == 0 &&        \
> > > +      sys_reg_CRn(id) == 0 && sys_reg_CRm(id) >= 0 &&        \
> > > +      sys_reg_CRm(id) < 8)
> > > +
> > >  struct kvm_arch {
> > >       struct kvm_s2_mmu mmu;
> > >
> > > @@ -137,6 +148,9 @@ struct kvm_arch {
> > >
> > >       /* Memory Tagging Extension enabled for the guest */
> > >       bool mte_enabled;
> > > +
> > > +     /* ID registers for the guest. */
> > > +     u64 id_regs[KVM_ARM_ID_REG_MAX_NUM];
> > >  };
> > >
> > >  struct kvm_vcpu_fault_info {
> > > @@ -734,6 +748,8 @@ int kvm_arm_vcpu_arch_has_attr(struct kvm_vcpu *vcpu,
> > >  long kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
> > >                               struct kvm_arm_copy_mte_tags *copy_tags);
> > >
> > > +void set_default_id_regs(struct kvm *kvm);
> > > +
> > >  /* Guest/host FPSIMD coordination helpers */
> > >  int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu);
> > >  void kvm_arch_vcpu_load_fp(struct kvm_vcpu *vcpu);
> > > diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> > > index e4727dc771bf..5f497a0af254 100644
> > > --- a/arch/arm64/kvm/arm.c
> > > +++ b/arch/arm64/kvm/arm.c
> > > @@ -156,6 +156,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
> > >       kvm->arch.max_vcpus = kvm_arm_default_max_vcpus();
> > >
> > >       set_default_spectre(kvm);
> > > +     set_default_id_regs(kvm);
> > >
> > >       return ret;
> > >  out_free_stage2_pgd:
> > > diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> > > index e3ec1a44f94d..80dc62f98ef0 100644
> > > --- a/arch/arm64/kvm/sys_regs.c
> > > +++ b/arch/arm64/kvm/sys_regs.c
> > > @@ -33,6 +33,8 @@
> > >
> > >  #include "trace.h"
> > >
> > > +static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id);
> > > +
> > >  /*
> > >   * All of this file is extremely similar to the ARM coproc.c, but the
> > >   * types are different. My gut feeling is that it should be pretty
> > > @@ -273,7 +275,7 @@ static bool trap_loregion(struct kvm_vcpu *vcpu,
> > >                         struct sys_reg_params *p,
> > >                         const struct sys_reg_desc *r)
> > >  {
> > > -     u64 val = read_sanitised_ftr_reg(SYS_ID_AA64MMFR1_EL1);
> > > +     u64 val = __read_id_reg(vcpu, SYS_ID_AA64MMFR1_EL1);
> > >       u32 sr = reg_to_encoding(r);
> > >
> > >       if (!(val & (0xfUL << ID_AA64MMFR1_LOR_SHIFT))) {
> > > @@ -1059,17 +1061,9 @@ static bool access_arch_timer(struct kvm_vcpu *vcpu,
> > >       return true;
> > >  }
> > >
> > > -/* Read a sanitised cpufeature ID register by sys_reg_desc */
> > > -static u64 read_id_reg(const struct kvm_vcpu *vcpu,
> > > -             struct sys_reg_desc const *r, bool raz)
> > > +static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
> > >  {
> > > -     u32 id = reg_to_encoding(r);
> > > -     u64 val;
> > > -
> > > -     if (raz)
> > > -             return 0;
> > > -
> > > -     val = read_sanitised_ftr_reg(id);
> > > +     u64 val = vcpu->kvm->arch.id_regs[IDREG_IDX(id)];
> > >
> > >       switch (id) {
> > >       case SYS_ID_AA64PFR0_EL1:
> > > @@ -1119,6 +1113,14 @@ static u64 read_id_reg(const struct kvm_vcpu *vcpu,
> > >       return val;
> > >  }
> > >
> > > +static u64 read_id_reg(const struct kvm_vcpu *vcpu,
> > > +                    struct sys_reg_desc const *r, bool raz)
> > > +{
> > > +     u32 id = reg_to_encoding(r);
> > > +
> > > +     return raz ? 0 : __read_id_reg(vcpu, id);
> > > +}
> > > +
> > >  static unsigned int id_visibility(const struct kvm_vcpu *vcpu,
> > >                                 const struct sys_reg_desc *r)
> > >  {
> > > @@ -1223,9 +1225,8 @@ static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
> > >  /*
> > >   * cpufeature ID register user accessors
> > >   *
> > > - * For now, these registers are immutable for userspace, so no values
> > > - * are stored, and for set_id_reg() we don't allow the effective value
> > > - * to be changed.
> > > + * For now, these registers are immutable for userspace, so for set_id_reg()
> > > + * we don't allow the effective value to be changed.
> > >   */
> > >  static int __get_id_reg(const struct kvm_vcpu *vcpu,
> > >                       const struct sys_reg_desc *rd, void __user *uaddr,
> > > @@ -1237,7 +1238,7 @@ static int __get_id_reg(const struct kvm_vcpu *vcpu,
> > >       return reg_to_user(uaddr, &val, id);
> > >  }
> > >
> > > -static int __set_id_reg(const struct kvm_vcpu *vcpu,
> > > +static int __set_id_reg(struct kvm_vcpu *vcpu,
> > >                       const struct sys_reg_desc *rd, void __user *uaddr,
> > >                       bool raz)
> > >  {
> > > @@ -1837,8 +1838,8 @@ static bool trap_dbgdidr(struct kvm_vcpu *vcpu,
> > >       if (p->is_write) {
> > >               return ignore_write(vcpu, p);
> > >       } else {
> > > -             u64 dfr = read_sanitised_ftr_reg(SYS_ID_AA64DFR0_EL1);
> > > -             u64 pfr = read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1);
> > > +             u64 dfr = __read_id_reg(vcpu, SYS_ID_AA64DFR0_EL1);
> > > +             u64 pfr = __read_id_reg(vcpu, SYS_ID_AA64PFR0_EL1);
> > >               u32 el3 = !!cpuid_feature_extract_unsigned_field(pfr, ID_AA64PFR0_EL3_SHIFT);
> > >
> > >               p->regval = ((((dfr >> ID_AA64DFR0_WRPS_SHIFT) & 0xf) << 28) |
> > > @@ -2850,3 +2851,30 @@ void kvm_sys_reg_table_init(void)
> > >       /* Clear all higher bits. */
> > >       cache_levels &= (1 << (i*3))-1;
> > >  }
> > > +
> > > +/*
> > > + * Set the guest's ID registers that are defined in sys_reg_descs[]
> > > + * with ID_SANITISED() to the host's sanitized value.
> > > + */
> > > +void set_default_id_regs(struct kvm *kvm)
> > > +{
> > > +     int i;
> > > +     u32 id;
> > > +     const struct sys_reg_desc *rd;
> > > +     u64 val;
> > > +
> > > +     for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
> > > +             rd = &sys_reg_descs[i];
> > > +             if (rd->access != access_id_reg)
> > > +                     /* Not ID register, or hidden/reserved ID register */
> > > +                     continue;
> > > +
> > > +             id = reg_to_encoding(rd);
> > > +             if (WARN_ON_ONCE(!is_id_reg(id)))
> > > +                     /* Shouldn't happen */
> > > +                     continue;
> > > +
> > > +             val = read_sanitised_ftr_reg(id);
> >
> > I'm a bit confused. Shouldn't the default+sanitized values already use
> > arm64_ftr_bits_kvm (instead of arm64_ftr_regs)?
>
> I'm not sure if I understand your question.
> arm64_ftr_bits_kvm is used for feature support checkings when
> userspace tries to modify a value of ID registers.
> With this patch, KVM just saves the sanitized values in the kvm's
> buffer, but userspace is still not allowed to modify values of ID
> registers yet.
> I hope it answers your question.

Based on the previous commit I was assuming that some registers, like
id_aa64dfr0,
would default to the overwritten values as the sanitized values. More
specifically: if
userspace doesn't modify any ID reg, shouldn't the defaults have the
KVM overwritten
values (arm64_ftr_bits_kvm)?

>
> Thanks,
> Reiji
>
> >
> > > +             kvm->arch.id_regs[IDREG_IDX(id)] = val;
> > > +     }
> > > +}
> > > --
> > > 2.34.1.448.ga2b2bfdf31-goog
> > >

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 02/26] KVM: arm64: Save ID registers' sanitized value per guest
@ 2022-01-28 19:27         ` Ricardo Koller
  0 siblings, 0 replies; 201+ messages in thread
From: Ricardo Koller @ 2022-01-28 19:27 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: kvm, Marc Zyngier, Peter Shier, Will Deacon, Paolo Bonzini,
	kvmarm, Linux ARM

On Thu, Jan 27, 2022 at 10:24 PM Reiji Watanabe <reijiw@google.com> wrote:
>
> Hi Ricardo,
>
> On Tue, Jan 25, 2022 at 9:22 PM Ricardo Koller <ricarkol@google.com> wrote:
> >
> > On Wed, Jan 05, 2022 at 08:26:44PM -0800, Reiji Watanabe wrote:
> > > Introduce id_regs[] in kvm_arch as a storage of guest's ID registers,
> > > and save ID registers' sanitized value in the array at KVM_CREATE_VM.
> > > Use the saved ones when ID registers are read by the guest or
> > > userspace (via KVM_GET_ONE_REG).
> > >
> > > Signed-off-by: Reiji Watanabe <reijiw@google.com>
> > > ---
> > >  arch/arm64/include/asm/kvm_host.h | 16 ++++++++
> > >  arch/arm64/kvm/arm.c              |  1 +
> > >  arch/arm64/kvm/sys_regs.c         | 62 ++++++++++++++++++++++---------
> > >  3 files changed, 62 insertions(+), 17 deletions(-)
> > >
> > > diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> > > index 2a5f7f38006f..c789a0137f58 100644
> > > --- a/arch/arm64/include/asm/kvm_host.h
> > > +++ b/arch/arm64/include/asm/kvm_host.h
> > > @@ -102,6 +102,17 @@ struct kvm_s2_mmu {
> > >  struct kvm_arch_memory_slot {
> > >  };
> > >
> > > +/*
> > > + * (Op0, Op1, CRn, CRm, Op2) of ID registers is (3, 0, 0, crm, op2),
> > > + * where 0<=crm<8, 0<=op2<8.
> >
> > Is this observation based on this table?
> >
> > Table D12-2 System instruction encodings for non-Debug System register accesses
> > in that case, it seems that the ID registers list might grow after
> > crm=7, and as CRm has 4 bits, why not 16*8=128?
>
> This is basically for registers that are already reserved as RAZ in the
> table (sys_reg_descs[] has entries for the reserved ones as well).
> Registers with crm > 7 are not reserved yet, and that will be expanded
> later as needed later.
>
>
> >
> > > + */
> > > +#define KVM_ARM_ID_REG_MAX_NUM       64
> > > +#define IDREG_IDX(id)                ((sys_reg_CRm(id) << 3) | sys_reg_Op2(id))
> > > +#define is_id_reg(id)        \
> > > +     (sys_reg_Op0(id) == 3 && sys_reg_Op1(id) == 0 &&        \
> > > +      sys_reg_CRn(id) == 0 && sys_reg_CRm(id) >= 0 &&        \
> > > +      sys_reg_CRm(id) < 8)
> > > +
> > >  struct kvm_arch {
> > >       struct kvm_s2_mmu mmu;
> > >
> > > @@ -137,6 +148,9 @@ struct kvm_arch {
> > >
> > >       /* Memory Tagging Extension enabled for the guest */
> > >       bool mte_enabled;
> > > +
> > > +     /* ID registers for the guest. */
> > > +     u64 id_regs[KVM_ARM_ID_REG_MAX_NUM];
> > >  };
> > >
> > >  struct kvm_vcpu_fault_info {
> > > @@ -734,6 +748,8 @@ int kvm_arm_vcpu_arch_has_attr(struct kvm_vcpu *vcpu,
> > >  long kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
> > >                               struct kvm_arm_copy_mte_tags *copy_tags);
> > >
> > > +void set_default_id_regs(struct kvm *kvm);
> > > +
> > >  /* Guest/host FPSIMD coordination helpers */
> > >  int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu);
> > >  void kvm_arch_vcpu_load_fp(struct kvm_vcpu *vcpu);
> > > diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> > > index e4727dc771bf..5f497a0af254 100644
> > > --- a/arch/arm64/kvm/arm.c
> > > +++ b/arch/arm64/kvm/arm.c
> > > @@ -156,6 +156,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
> > >       kvm->arch.max_vcpus = kvm_arm_default_max_vcpus();
> > >
> > >       set_default_spectre(kvm);
> > > +     set_default_id_regs(kvm);
> > >
> > >       return ret;
> > >  out_free_stage2_pgd:
> > > diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> > > index e3ec1a44f94d..80dc62f98ef0 100644
> > > --- a/arch/arm64/kvm/sys_regs.c
> > > +++ b/arch/arm64/kvm/sys_regs.c
> > > @@ -33,6 +33,8 @@
> > >
> > >  #include "trace.h"
> > >
> > > +static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id);
> > > +
> > >  /*
> > >   * All of this file is extremely similar to the ARM coproc.c, but the
> > >   * types are different. My gut feeling is that it should be pretty
> > > @@ -273,7 +275,7 @@ static bool trap_loregion(struct kvm_vcpu *vcpu,
> > >                         struct sys_reg_params *p,
> > >                         const struct sys_reg_desc *r)
> > >  {
> > > -     u64 val = read_sanitised_ftr_reg(SYS_ID_AA64MMFR1_EL1);
> > > +     u64 val = __read_id_reg(vcpu, SYS_ID_AA64MMFR1_EL1);
> > >       u32 sr = reg_to_encoding(r);
> > >
> > >       if (!(val & (0xfUL << ID_AA64MMFR1_LOR_SHIFT))) {
> > > @@ -1059,17 +1061,9 @@ static bool access_arch_timer(struct kvm_vcpu *vcpu,
> > >       return true;
> > >  }
> > >
> > > -/* Read a sanitised cpufeature ID register by sys_reg_desc */
> > > -static u64 read_id_reg(const struct kvm_vcpu *vcpu,
> > > -             struct sys_reg_desc const *r, bool raz)
> > > +static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
> > >  {
> > > -     u32 id = reg_to_encoding(r);
> > > -     u64 val;
> > > -
> > > -     if (raz)
> > > -             return 0;
> > > -
> > > -     val = read_sanitised_ftr_reg(id);
> > > +     u64 val = vcpu->kvm->arch.id_regs[IDREG_IDX(id)];
> > >
> > >       switch (id) {
> > >       case SYS_ID_AA64PFR0_EL1:
> > > @@ -1119,6 +1113,14 @@ static u64 read_id_reg(const struct kvm_vcpu *vcpu,
> > >       return val;
> > >  }
> > >
> > > +static u64 read_id_reg(const struct kvm_vcpu *vcpu,
> > > +                    struct sys_reg_desc const *r, bool raz)
> > > +{
> > > +     u32 id = reg_to_encoding(r);
> > > +
> > > +     return raz ? 0 : __read_id_reg(vcpu, id);
> > > +}
> > > +
> > >  static unsigned int id_visibility(const struct kvm_vcpu *vcpu,
> > >                                 const struct sys_reg_desc *r)
> > >  {
> > > @@ -1223,9 +1225,8 @@ static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
> > >  /*
> > >   * cpufeature ID register user accessors
> > >   *
> > > - * For now, these registers are immutable for userspace, so no values
> > > - * are stored, and for set_id_reg() we don't allow the effective value
> > > - * to be changed.
> > > + * For now, these registers are immutable for userspace, so for set_id_reg()
> > > + * we don't allow the effective value to be changed.
> > >   */
> > >  static int __get_id_reg(const struct kvm_vcpu *vcpu,
> > >                       const struct sys_reg_desc *rd, void __user *uaddr,
> > > @@ -1237,7 +1238,7 @@ static int __get_id_reg(const struct kvm_vcpu *vcpu,
> > >       return reg_to_user(uaddr, &val, id);
> > >  }
> > >
> > > -static int __set_id_reg(const struct kvm_vcpu *vcpu,
> > > +static int __set_id_reg(struct kvm_vcpu *vcpu,
> > >                       const struct sys_reg_desc *rd, void __user *uaddr,
> > >                       bool raz)
> > >  {
> > > @@ -1837,8 +1838,8 @@ static bool trap_dbgdidr(struct kvm_vcpu *vcpu,
> > >       if (p->is_write) {
> > >               return ignore_write(vcpu, p);
> > >       } else {
> > > -             u64 dfr = read_sanitised_ftr_reg(SYS_ID_AA64DFR0_EL1);
> > > -             u64 pfr = read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1);
> > > +             u64 dfr = __read_id_reg(vcpu, SYS_ID_AA64DFR0_EL1);
> > > +             u64 pfr = __read_id_reg(vcpu, SYS_ID_AA64PFR0_EL1);
> > >               u32 el3 = !!cpuid_feature_extract_unsigned_field(pfr, ID_AA64PFR0_EL3_SHIFT);
> > >
> > >               p->regval = ((((dfr >> ID_AA64DFR0_WRPS_SHIFT) & 0xf) << 28) |
> > > @@ -2850,3 +2851,30 @@ void kvm_sys_reg_table_init(void)
> > >       /* Clear all higher bits. */
> > >       cache_levels &= (1 << (i*3))-1;
> > >  }
> > > +
> > > +/*
> > > + * Set the guest's ID registers that are defined in sys_reg_descs[]
> > > + * with ID_SANITISED() to the host's sanitized value.
> > > + */
> > > +void set_default_id_regs(struct kvm *kvm)
> > > +{
> > > +     int i;
> > > +     u32 id;
> > > +     const struct sys_reg_desc *rd;
> > > +     u64 val;
> > > +
> > > +     for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
> > > +             rd = &sys_reg_descs[i];
> > > +             if (rd->access != access_id_reg)
> > > +                     /* Not ID register, or hidden/reserved ID register */
> > > +                     continue;
> > > +
> > > +             id = reg_to_encoding(rd);
> > > +             if (WARN_ON_ONCE(!is_id_reg(id)))
> > > +                     /* Shouldn't happen */
> > > +                     continue;
> > > +
> > > +             val = read_sanitised_ftr_reg(id);
> >
> > I'm a bit confused. Shouldn't the default+sanitized values already use
> > arm64_ftr_bits_kvm (instead of arm64_ftr_regs)?
>
> I'm not sure if I understand your question.
> arm64_ftr_bits_kvm is used for feature support checkings when
> userspace tries to modify a value of ID registers.
> With this patch, KVM just saves the sanitized values in the kvm's
> buffer, but userspace is still not allowed to modify values of ID
> registers yet.
> I hope it answers your question.

Based on the previous commit I was assuming that some registers, like
id_aa64dfr0,
would default to the overwritten values as the sanitized values. More
specifically: if
userspace doesn't modify any ID reg, shouldn't the defaults have the
KVM overwritten
values (arm64_ftr_bits_kvm)?

>
> Thanks,
> Reiji
>
> >
> > > +             kvm->arch.id_regs[IDREG_IDX(id)] = val;
> > > +     }
> > > +}
> > > --
> > > 2.34.1.448.ga2b2bfdf31-goog
> > >
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 02/26] KVM: arm64: Save ID registers' sanitized value per guest
@ 2022-01-28 19:27         ` Ricardo Koller
  0 siblings, 0 replies; 201+ messages in thread
From: Ricardo Koller @ 2022-01-28 19:27 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: Marc Zyngier, kvmarm, kvm, Linux ARM, James Morse,
	Alexandru Elisei, Suzuki K Poulose, Paolo Bonzini, Will Deacon,
	Andrew Jones, Peng Liang, Peter Shier, Oliver Upton, Jing Zhang,
	Raghavendra Rao Anata

On Thu, Jan 27, 2022 at 10:24 PM Reiji Watanabe <reijiw@google.com> wrote:
>
> Hi Ricardo,
>
> On Tue, Jan 25, 2022 at 9:22 PM Ricardo Koller <ricarkol@google.com> wrote:
> >
> > On Wed, Jan 05, 2022 at 08:26:44PM -0800, Reiji Watanabe wrote:
> > > Introduce id_regs[] in kvm_arch as a storage of guest's ID registers,
> > > and save ID registers' sanitized value in the array at KVM_CREATE_VM.
> > > Use the saved ones when ID registers are read by the guest or
> > > userspace (via KVM_GET_ONE_REG).
> > >
> > > Signed-off-by: Reiji Watanabe <reijiw@google.com>
> > > ---
> > >  arch/arm64/include/asm/kvm_host.h | 16 ++++++++
> > >  arch/arm64/kvm/arm.c              |  1 +
> > >  arch/arm64/kvm/sys_regs.c         | 62 ++++++++++++++++++++++---------
> > >  3 files changed, 62 insertions(+), 17 deletions(-)
> > >
> > > diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> > > index 2a5f7f38006f..c789a0137f58 100644
> > > --- a/arch/arm64/include/asm/kvm_host.h
> > > +++ b/arch/arm64/include/asm/kvm_host.h
> > > @@ -102,6 +102,17 @@ struct kvm_s2_mmu {
> > >  struct kvm_arch_memory_slot {
> > >  };
> > >
> > > +/*
> > > + * (Op0, Op1, CRn, CRm, Op2) of ID registers is (3, 0, 0, crm, op2),
> > > + * where 0<=crm<8, 0<=op2<8.
> >
> > Is this observation based on this table?
> >
> > Table D12-2 System instruction encodings for non-Debug System register accesses
> > in that case, it seems that the ID registers list might grow after
> > crm=7, and as CRm has 4 bits, why not 16*8=128?
>
> This is basically for registers that are already reserved as RAZ in the
> table (sys_reg_descs[] has entries for the reserved ones as well).
> Registers with crm > 7 are not reserved yet, and that will be expanded
> later as needed later.
>
>
> >
> > > + */
> > > +#define KVM_ARM_ID_REG_MAX_NUM       64
> > > +#define IDREG_IDX(id)                ((sys_reg_CRm(id) << 3) | sys_reg_Op2(id))
> > > +#define is_id_reg(id)        \
> > > +     (sys_reg_Op0(id) == 3 && sys_reg_Op1(id) == 0 &&        \
> > > +      sys_reg_CRn(id) == 0 && sys_reg_CRm(id) >= 0 &&        \
> > > +      sys_reg_CRm(id) < 8)
> > > +
> > >  struct kvm_arch {
> > >       struct kvm_s2_mmu mmu;
> > >
> > > @@ -137,6 +148,9 @@ struct kvm_arch {
> > >
> > >       /* Memory Tagging Extension enabled for the guest */
> > >       bool mte_enabled;
> > > +
> > > +     /* ID registers for the guest. */
> > > +     u64 id_regs[KVM_ARM_ID_REG_MAX_NUM];
> > >  };
> > >
> > >  struct kvm_vcpu_fault_info {
> > > @@ -734,6 +748,8 @@ int kvm_arm_vcpu_arch_has_attr(struct kvm_vcpu *vcpu,
> > >  long kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
> > >                               struct kvm_arm_copy_mte_tags *copy_tags);
> > >
> > > +void set_default_id_regs(struct kvm *kvm);
> > > +
> > >  /* Guest/host FPSIMD coordination helpers */
> > >  int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu);
> > >  void kvm_arch_vcpu_load_fp(struct kvm_vcpu *vcpu);
> > > diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> > > index e4727dc771bf..5f497a0af254 100644
> > > --- a/arch/arm64/kvm/arm.c
> > > +++ b/arch/arm64/kvm/arm.c
> > > @@ -156,6 +156,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
> > >       kvm->arch.max_vcpus = kvm_arm_default_max_vcpus();
> > >
> > >       set_default_spectre(kvm);
> > > +     set_default_id_regs(kvm);
> > >
> > >       return ret;
> > >  out_free_stage2_pgd:
> > > diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> > > index e3ec1a44f94d..80dc62f98ef0 100644
> > > --- a/arch/arm64/kvm/sys_regs.c
> > > +++ b/arch/arm64/kvm/sys_regs.c
> > > @@ -33,6 +33,8 @@
> > >
> > >  #include "trace.h"
> > >
> > > +static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id);
> > > +
> > >  /*
> > >   * All of this file is extremely similar to the ARM coproc.c, but the
> > >   * types are different. My gut feeling is that it should be pretty
> > > @@ -273,7 +275,7 @@ static bool trap_loregion(struct kvm_vcpu *vcpu,
> > >                         struct sys_reg_params *p,
> > >                         const struct sys_reg_desc *r)
> > >  {
> > > -     u64 val = read_sanitised_ftr_reg(SYS_ID_AA64MMFR1_EL1);
> > > +     u64 val = __read_id_reg(vcpu, SYS_ID_AA64MMFR1_EL1);
> > >       u32 sr = reg_to_encoding(r);
> > >
> > >       if (!(val & (0xfUL << ID_AA64MMFR1_LOR_SHIFT))) {
> > > @@ -1059,17 +1061,9 @@ static bool access_arch_timer(struct kvm_vcpu *vcpu,
> > >       return true;
> > >  }
> > >
> > > -/* Read a sanitised cpufeature ID register by sys_reg_desc */
> > > -static u64 read_id_reg(const struct kvm_vcpu *vcpu,
> > > -             struct sys_reg_desc const *r, bool raz)
> > > +static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
> > >  {
> > > -     u32 id = reg_to_encoding(r);
> > > -     u64 val;
> > > -
> > > -     if (raz)
> > > -             return 0;
> > > -
> > > -     val = read_sanitised_ftr_reg(id);
> > > +     u64 val = vcpu->kvm->arch.id_regs[IDREG_IDX(id)];
> > >
> > >       switch (id) {
> > >       case SYS_ID_AA64PFR0_EL1:
> > > @@ -1119,6 +1113,14 @@ static u64 read_id_reg(const struct kvm_vcpu *vcpu,
> > >       return val;
> > >  }
> > >
> > > +static u64 read_id_reg(const struct kvm_vcpu *vcpu,
> > > +                    struct sys_reg_desc const *r, bool raz)
> > > +{
> > > +     u32 id = reg_to_encoding(r);
> > > +
> > > +     return raz ? 0 : __read_id_reg(vcpu, id);
> > > +}
> > > +
> > >  static unsigned int id_visibility(const struct kvm_vcpu *vcpu,
> > >                                 const struct sys_reg_desc *r)
> > >  {
> > > @@ -1223,9 +1225,8 @@ static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
> > >  /*
> > >   * cpufeature ID register user accessors
> > >   *
> > > - * For now, these registers are immutable for userspace, so no values
> > > - * are stored, and for set_id_reg() we don't allow the effective value
> > > - * to be changed.
> > > + * For now, these registers are immutable for userspace, so for set_id_reg()
> > > + * we don't allow the effective value to be changed.
> > >   */
> > >  static int __get_id_reg(const struct kvm_vcpu *vcpu,
> > >                       const struct sys_reg_desc *rd, void __user *uaddr,
> > > @@ -1237,7 +1238,7 @@ static int __get_id_reg(const struct kvm_vcpu *vcpu,
> > >       return reg_to_user(uaddr, &val, id);
> > >  }
> > >
> > > -static int __set_id_reg(const struct kvm_vcpu *vcpu,
> > > +static int __set_id_reg(struct kvm_vcpu *vcpu,
> > >                       const struct sys_reg_desc *rd, void __user *uaddr,
> > >                       bool raz)
> > >  {
> > > @@ -1837,8 +1838,8 @@ static bool trap_dbgdidr(struct kvm_vcpu *vcpu,
> > >       if (p->is_write) {
> > >               return ignore_write(vcpu, p);
> > >       } else {
> > > -             u64 dfr = read_sanitised_ftr_reg(SYS_ID_AA64DFR0_EL1);
> > > -             u64 pfr = read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1);
> > > +             u64 dfr = __read_id_reg(vcpu, SYS_ID_AA64DFR0_EL1);
> > > +             u64 pfr = __read_id_reg(vcpu, SYS_ID_AA64PFR0_EL1);
> > >               u32 el3 = !!cpuid_feature_extract_unsigned_field(pfr, ID_AA64PFR0_EL3_SHIFT);
> > >
> > >               p->regval = ((((dfr >> ID_AA64DFR0_WRPS_SHIFT) & 0xf) << 28) |
> > > @@ -2850,3 +2851,30 @@ void kvm_sys_reg_table_init(void)
> > >       /* Clear all higher bits. */
> > >       cache_levels &= (1 << (i*3))-1;
> > >  }
> > > +
> > > +/*
> > > + * Set the guest's ID registers that are defined in sys_reg_descs[]
> > > + * with ID_SANITISED() to the host's sanitized value.
> > > + */
> > > +void set_default_id_regs(struct kvm *kvm)
> > > +{
> > > +     int i;
> > > +     u32 id;
> > > +     const struct sys_reg_desc *rd;
> > > +     u64 val;
> > > +
> > > +     for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
> > > +             rd = &sys_reg_descs[i];
> > > +             if (rd->access != access_id_reg)
> > > +                     /* Not ID register, or hidden/reserved ID register */
> > > +                     continue;
> > > +
> > > +             id = reg_to_encoding(rd);
> > > +             if (WARN_ON_ONCE(!is_id_reg(id)))
> > > +                     /* Shouldn't happen */
> > > +                     continue;
> > > +
> > > +             val = read_sanitised_ftr_reg(id);
> >
> > I'm a bit confused. Shouldn't the default+sanitized values already use
> > arm64_ftr_bits_kvm (instead of arm64_ftr_regs)?
>
> I'm not sure if I understand your question.
> arm64_ftr_bits_kvm is used for feature support checkings when
> userspace tries to modify a value of ID registers.
> With this patch, KVM just saves the sanitized values in the kvm's
> buffer, but userspace is still not allowed to modify values of ID
> registers yet.
> I hope it answers your question.

Based on the previous commit I was assuming that some registers, like
id_aa64dfr0,
would default to the overwritten values as the sanitized values. More
specifically: if
userspace doesn't modify any ID reg, shouldn't the defaults have the
KVM overwritten
values (arm64_ftr_bits_kvm)?

>
> Thanks,
> Reiji
>
> >
> > > +             kvm->arch.id_regs[IDREG_IDX(id)] = val;
> > > +     }
> > > +}
> > > --
> > > 2.34.1.448.ga2b2bfdf31-goog
> > >

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 02/26] KVM: arm64: Save ID registers' sanitized value per guest
  2022-01-28 19:27         ` Ricardo Koller
  (?)
@ 2022-01-29  5:52           ` Reiji Watanabe
  -1 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-29  5:52 UTC (permalink / raw)
  To: Ricardo Koller
  Cc: Marc Zyngier, kvmarm, kvm, Linux ARM, James Morse,
	Alexandru Elisei, Suzuki K Poulose, Paolo Bonzini, Will Deacon,
	Andrew Jones, Peng Liang, Peter Shier, Oliver Upton, Jing Zhang,
	Raghavendra Rao Anata

Hi Ricardo,

> > > > +
> > > > +/*
> > > > + * Set the guest's ID registers that are defined in sys_reg_descs[]
> > > > + * with ID_SANITISED() to the host's sanitized value.
> > > > + */
> > > > +void set_default_id_regs(struct kvm *kvm)
> > > > +{
> > > > +     int i;
> > > > +     u32 id;
> > > > +     const struct sys_reg_desc *rd;
> > > > +     u64 val;
> > > > +
> > > > +     for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
> > > > +             rd = &sys_reg_descs[i];
> > > > +             if (rd->access != access_id_reg)
> > > > +                     /* Not ID register, or hidden/reserved ID register */
> > > > +                     continue;
> > > > +
> > > > +             id = reg_to_encoding(rd);
> > > > +             if (WARN_ON_ONCE(!is_id_reg(id)))
> > > > +                     /* Shouldn't happen */
> > > > +                     continue;
> > > > +
> > > > +             val = read_sanitised_ftr_reg(id);
> > >
> > > I'm a bit confused. Shouldn't the default+sanitized values already use
> > > arm64_ftr_bits_kvm (instead of arm64_ftr_regs)?
> >
> > I'm not sure if I understand your question.
> > arm64_ftr_bits_kvm is used for feature support checkings when
> > userspace tries to modify a value of ID registers.
> > With this patch, KVM just saves the sanitized values in the kvm's
> > buffer, but userspace is still not allowed to modify values of ID
> > registers yet.
> > I hope it answers your question.
>
> Based on the previous commit I was assuming that some registers, like
> id_aa64dfr0,
> would default to the overwritten values as the sanitized values. More
> specifically: if
> userspace doesn't modify any ID reg, shouldn't the defaults have the
> KVM overwritten
> values (arm64_ftr_bits_kvm)?

arm64_ftr_bits_kvm doesn't have arm64_ftr_reg but arm64_ftr_bits,
and arm64_ftr_bits_kvm doesn't have the sanitized values.

Thanks,
Reiji

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 02/26] KVM: arm64: Save ID registers' sanitized value per guest
@ 2022-01-29  5:52           ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-29  5:52 UTC (permalink / raw)
  To: Ricardo Koller
  Cc: kvm, Marc Zyngier, Peter Shier, Will Deacon, Paolo Bonzini,
	kvmarm, Linux ARM

Hi Ricardo,

> > > > +
> > > > +/*
> > > > + * Set the guest's ID registers that are defined in sys_reg_descs[]
> > > > + * with ID_SANITISED() to the host's sanitized value.
> > > > + */
> > > > +void set_default_id_regs(struct kvm *kvm)
> > > > +{
> > > > +     int i;
> > > > +     u32 id;
> > > > +     const struct sys_reg_desc *rd;
> > > > +     u64 val;
> > > > +
> > > > +     for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
> > > > +             rd = &sys_reg_descs[i];
> > > > +             if (rd->access != access_id_reg)
> > > > +                     /* Not ID register, or hidden/reserved ID register */
> > > > +                     continue;
> > > > +
> > > > +             id = reg_to_encoding(rd);
> > > > +             if (WARN_ON_ONCE(!is_id_reg(id)))
> > > > +                     /* Shouldn't happen */
> > > > +                     continue;
> > > > +
> > > > +             val = read_sanitised_ftr_reg(id);
> > >
> > > I'm a bit confused. Shouldn't the default+sanitized values already use
> > > arm64_ftr_bits_kvm (instead of arm64_ftr_regs)?
> >
> > I'm not sure if I understand your question.
> > arm64_ftr_bits_kvm is used for feature support checkings when
> > userspace tries to modify a value of ID registers.
> > With this patch, KVM just saves the sanitized values in the kvm's
> > buffer, but userspace is still not allowed to modify values of ID
> > registers yet.
> > I hope it answers your question.
>
> Based on the previous commit I was assuming that some registers, like
> id_aa64dfr0,
> would default to the overwritten values as the sanitized values. More
> specifically: if
> userspace doesn't modify any ID reg, shouldn't the defaults have the
> KVM overwritten
> values (arm64_ftr_bits_kvm)?

arm64_ftr_bits_kvm doesn't have arm64_ftr_reg but arm64_ftr_bits,
and arm64_ftr_bits_kvm doesn't have the sanitized values.

Thanks,
Reiji
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 02/26] KVM: arm64: Save ID registers' sanitized value per guest
@ 2022-01-29  5:52           ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-01-29  5:52 UTC (permalink / raw)
  To: Ricardo Koller
  Cc: Marc Zyngier, kvmarm, kvm, Linux ARM, James Morse,
	Alexandru Elisei, Suzuki K Poulose, Paolo Bonzini, Will Deacon,
	Andrew Jones, Peng Liang, Peter Shier, Oliver Upton, Jing Zhang,
	Raghavendra Rao Anata

Hi Ricardo,

> > > > +
> > > > +/*
> > > > + * Set the guest's ID registers that are defined in sys_reg_descs[]
> > > > + * with ID_SANITISED() to the host's sanitized value.
> > > > + */
> > > > +void set_default_id_regs(struct kvm *kvm)
> > > > +{
> > > > +     int i;
> > > > +     u32 id;
> > > > +     const struct sys_reg_desc *rd;
> > > > +     u64 val;
> > > > +
> > > > +     for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
> > > > +             rd = &sys_reg_descs[i];
> > > > +             if (rd->access != access_id_reg)
> > > > +                     /* Not ID register, or hidden/reserved ID register */
> > > > +                     continue;
> > > > +
> > > > +             id = reg_to_encoding(rd);
> > > > +             if (WARN_ON_ONCE(!is_id_reg(id)))
> > > > +                     /* Shouldn't happen */
> > > > +                     continue;
> > > > +
> > > > +             val = read_sanitised_ftr_reg(id);
> > >
> > > I'm a bit confused. Shouldn't the default+sanitized values already use
> > > arm64_ftr_bits_kvm (instead of arm64_ftr_regs)?
> >
> > I'm not sure if I understand your question.
> > arm64_ftr_bits_kvm is used for feature support checkings when
> > userspace tries to modify a value of ID registers.
> > With this patch, KVM just saves the sanitized values in the kvm's
> > buffer, but userspace is still not allowed to modify values of ID
> > registers yet.
> > I hope it answers your question.
>
> Based on the previous commit I was assuming that some registers, like
> id_aa64dfr0,
> would default to the overwritten values as the sanitized values. More
> specifically: if
> userspace doesn't modify any ID reg, shouldn't the defaults have the
> KVM overwritten
> values (arm64_ftr_bits_kvm)?

arm64_ftr_bits_kvm doesn't have arm64_ftr_reg but arm64_ftr_bits,
and arm64_ftr_bits_kvm doesn't have the sanitized values.

Thanks,
Reiji

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 02/26] KVM: arm64: Save ID registers' sanitized value per guest
  2022-01-29  5:52           ` Reiji Watanabe
  (?)
@ 2022-01-31  3:40             ` Ricardo Koller
  -1 siblings, 0 replies; 201+ messages in thread
From: Ricardo Koller @ 2022-01-31  3:40 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: Marc Zyngier, kvmarm, kvm, Linux ARM, James Morse,
	Alexandru Elisei, Suzuki K Poulose, Paolo Bonzini, Will Deacon,
	Andrew Jones, Peng Liang, Peter Shier, Oliver Upton, Jing Zhang,
	Raghavendra Rao Anata

On Fri, Jan 28, 2022 at 09:52:21PM -0800, Reiji Watanabe wrote:
> Hi Ricardo,
> 
> > > > > +
> > > > > +/*
> > > > > + * Set the guest's ID registers that are defined in sys_reg_descs[]
> > > > > + * with ID_SANITISED() to the host's sanitized value.
> > > > > + */
> > > > > +void set_default_id_regs(struct kvm *kvm)
> > > > > +{
> > > > > +     int i;
> > > > > +     u32 id;
> > > > > +     const struct sys_reg_desc *rd;
> > > > > +     u64 val;
> > > > > +
> > > > > +     for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
> > > > > +             rd = &sys_reg_descs[i];
> > > > > +             if (rd->access != access_id_reg)
> > > > > +                     /* Not ID register, or hidden/reserved ID register */
> > > > > +                     continue;
> > > > > +
> > > > > +             id = reg_to_encoding(rd);
> > > > > +             if (WARN_ON_ONCE(!is_id_reg(id)))
> > > > > +                     /* Shouldn't happen */
> > > > > +                     continue;
> > > > > +
> > > > > +             val = read_sanitised_ftr_reg(id);
> > > >
> > > > I'm a bit confused. Shouldn't the default+sanitized values already use
> > > > arm64_ftr_bits_kvm (instead of arm64_ftr_regs)?
> > >
> > > I'm not sure if I understand your question.
> > > arm64_ftr_bits_kvm is used for feature support checkings when
> > > userspace tries to modify a value of ID registers.
> > > With this patch, KVM just saves the sanitized values in the kvm's
> > > buffer, but userspace is still not allowed to modify values of ID
> > > registers yet.
> > > I hope it answers your question.
> >
> > Based on the previous commit I was assuming that some registers, like
> > id_aa64dfr0,
> > would default to the overwritten values as the sanitized values. More
> > specifically: if
> > userspace doesn't modify any ID reg, shouldn't the defaults have the
> > KVM overwritten
> > values (arm64_ftr_bits_kvm)?
> 
> arm64_ftr_bits_kvm doesn't have arm64_ftr_reg but arm64_ftr_bits,
> and arm64_ftr_bits_kvm doesn't have the sanitized values.
> 
> Thanks,

Hey Reiji,

Sorry, I wasn't very clear. This is what I meant.

If I set DEBUGVER to 0x5 (w/ FTR_EXACT) using this patch on top of the
series:

 static struct arm64_ftr_bits ftr_id_aa64dfr0_kvm[MAX_FTR_BITS_LEN] = {
        S_ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64DFR0_PMUVER_SHIFT, 4, 0),
-       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x6),
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x5),

it means that userspace would not be able to set DEBUGVER to anything
but 0x5. But I'm not sure what it should mean for the default KVM value
of DEBUGVER, specifically the value calculated in set_default_id_regs().
As it is, KVM is still setting the guest-visible value to 0x6, and my
"desire" to only allow booting VMs with DEBUGVER=0x5 is being ignored: I
booted a VM and the DEBUGVER value from inside is still 0x6. I was
expecting it to not boot, or to show a warning.

I think this has some implications for migrations. It would not be
possible to migrate the example VM on the patched kernel from above: you
can boot a VM with DEBUGVER=0x5 but you can't migrate it.

Thanks,
Ricardo

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 02/26] KVM: arm64: Save ID registers' sanitized value per guest
@ 2022-01-31  3:40             ` Ricardo Koller
  0 siblings, 0 replies; 201+ messages in thread
From: Ricardo Koller @ 2022-01-31  3:40 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: kvm, Marc Zyngier, Peter Shier, Will Deacon, Paolo Bonzini,
	kvmarm, Linux ARM

On Fri, Jan 28, 2022 at 09:52:21PM -0800, Reiji Watanabe wrote:
> Hi Ricardo,
> 
> > > > > +
> > > > > +/*
> > > > > + * Set the guest's ID registers that are defined in sys_reg_descs[]
> > > > > + * with ID_SANITISED() to the host's sanitized value.
> > > > > + */
> > > > > +void set_default_id_regs(struct kvm *kvm)
> > > > > +{
> > > > > +     int i;
> > > > > +     u32 id;
> > > > > +     const struct sys_reg_desc *rd;
> > > > > +     u64 val;
> > > > > +
> > > > > +     for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
> > > > > +             rd = &sys_reg_descs[i];
> > > > > +             if (rd->access != access_id_reg)
> > > > > +                     /* Not ID register, or hidden/reserved ID register */
> > > > > +                     continue;
> > > > > +
> > > > > +             id = reg_to_encoding(rd);
> > > > > +             if (WARN_ON_ONCE(!is_id_reg(id)))
> > > > > +                     /* Shouldn't happen */
> > > > > +                     continue;
> > > > > +
> > > > > +             val = read_sanitised_ftr_reg(id);
> > > >
> > > > I'm a bit confused. Shouldn't the default+sanitized values already use
> > > > arm64_ftr_bits_kvm (instead of arm64_ftr_regs)?
> > >
> > > I'm not sure if I understand your question.
> > > arm64_ftr_bits_kvm is used for feature support checkings when
> > > userspace tries to modify a value of ID registers.
> > > With this patch, KVM just saves the sanitized values in the kvm's
> > > buffer, but userspace is still not allowed to modify values of ID
> > > registers yet.
> > > I hope it answers your question.
> >
> > Based on the previous commit I was assuming that some registers, like
> > id_aa64dfr0,
> > would default to the overwritten values as the sanitized values. More
> > specifically: if
> > userspace doesn't modify any ID reg, shouldn't the defaults have the
> > KVM overwritten
> > values (arm64_ftr_bits_kvm)?
> 
> arm64_ftr_bits_kvm doesn't have arm64_ftr_reg but arm64_ftr_bits,
> and arm64_ftr_bits_kvm doesn't have the sanitized values.
> 
> Thanks,

Hey Reiji,

Sorry, I wasn't very clear. This is what I meant.

If I set DEBUGVER to 0x5 (w/ FTR_EXACT) using this patch on top of the
series:

 static struct arm64_ftr_bits ftr_id_aa64dfr0_kvm[MAX_FTR_BITS_LEN] = {
        S_ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64DFR0_PMUVER_SHIFT, 4, 0),
-       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x6),
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x5),

it means that userspace would not be able to set DEBUGVER to anything
but 0x5. But I'm not sure what it should mean for the default KVM value
of DEBUGVER, specifically the value calculated in set_default_id_regs().
As it is, KVM is still setting the guest-visible value to 0x6, and my
"desire" to only allow booting VMs with DEBUGVER=0x5 is being ignored: I
booted a VM and the DEBUGVER value from inside is still 0x6. I was
expecting it to not boot, or to show a warning.

I think this has some implications for migrations. It would not be
possible to migrate the example VM on the patched kernel from above: you
can boot a VM with DEBUGVER=0x5 but you can't migrate it.

Thanks,
Ricardo
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 02/26] KVM: arm64: Save ID registers' sanitized value per guest
@ 2022-01-31  3:40             ` Ricardo Koller
  0 siblings, 0 replies; 201+ messages in thread
From: Ricardo Koller @ 2022-01-31  3:40 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: Marc Zyngier, kvmarm, kvm, Linux ARM, James Morse,
	Alexandru Elisei, Suzuki K Poulose, Paolo Bonzini, Will Deacon,
	Andrew Jones, Peng Liang, Peter Shier, Oliver Upton, Jing Zhang,
	Raghavendra Rao Anata

On Fri, Jan 28, 2022 at 09:52:21PM -0800, Reiji Watanabe wrote:
> Hi Ricardo,
> 
> > > > > +
> > > > > +/*
> > > > > + * Set the guest's ID registers that are defined in sys_reg_descs[]
> > > > > + * with ID_SANITISED() to the host's sanitized value.
> > > > > + */
> > > > > +void set_default_id_regs(struct kvm *kvm)
> > > > > +{
> > > > > +     int i;
> > > > > +     u32 id;
> > > > > +     const struct sys_reg_desc *rd;
> > > > > +     u64 val;
> > > > > +
> > > > > +     for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
> > > > > +             rd = &sys_reg_descs[i];
> > > > > +             if (rd->access != access_id_reg)
> > > > > +                     /* Not ID register, or hidden/reserved ID register */
> > > > > +                     continue;
> > > > > +
> > > > > +             id = reg_to_encoding(rd);
> > > > > +             if (WARN_ON_ONCE(!is_id_reg(id)))
> > > > > +                     /* Shouldn't happen */
> > > > > +                     continue;
> > > > > +
> > > > > +             val = read_sanitised_ftr_reg(id);
> > > >
> > > > I'm a bit confused. Shouldn't the default+sanitized values already use
> > > > arm64_ftr_bits_kvm (instead of arm64_ftr_regs)?
> > >
> > > I'm not sure if I understand your question.
> > > arm64_ftr_bits_kvm is used for feature support checkings when
> > > userspace tries to modify a value of ID registers.
> > > With this patch, KVM just saves the sanitized values in the kvm's
> > > buffer, but userspace is still not allowed to modify values of ID
> > > registers yet.
> > > I hope it answers your question.
> >
> > Based on the previous commit I was assuming that some registers, like
> > id_aa64dfr0,
> > would default to the overwritten values as the sanitized values. More
> > specifically: if
> > userspace doesn't modify any ID reg, shouldn't the defaults have the
> > KVM overwritten
> > values (arm64_ftr_bits_kvm)?
> 
> arm64_ftr_bits_kvm doesn't have arm64_ftr_reg but arm64_ftr_bits,
> and arm64_ftr_bits_kvm doesn't have the sanitized values.
> 
> Thanks,

Hey Reiji,

Sorry, I wasn't very clear. This is what I meant.

If I set DEBUGVER to 0x5 (w/ FTR_EXACT) using this patch on top of the
series:

 static struct arm64_ftr_bits ftr_id_aa64dfr0_kvm[MAX_FTR_BITS_LEN] = {
        S_ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64DFR0_PMUVER_SHIFT, 4, 0),
-       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x6),
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x5),

it means that userspace would not be able to set DEBUGVER to anything
but 0x5. But I'm not sure what it should mean for the default KVM value
of DEBUGVER, specifically the value calculated in set_default_id_regs().
As it is, KVM is still setting the guest-visible value to 0x6, and my
"desire" to only allow booting VMs with DEBUGVER=0x5 is being ignored: I
booted a VM and the DEBUGVER value from inside is still 0x6. I was
expecting it to not boot, or to show a warning.

I think this has some implications for migrations. It would not be
possible to migrate the example VM on the patched kernel from above: you
can boot a VM with DEBUGVER=0x5 but you can't migrate it.

Thanks,
Ricardo

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 02/26] KVM: arm64: Save ID registers' sanitized value per guest
  2022-01-31  3:40             ` Ricardo Koller
  (?)
@ 2022-02-01  6:00               ` Reiji Watanabe
  -1 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-02-01  6:00 UTC (permalink / raw)
  To: Ricardo Koller
  Cc: Marc Zyngier, kvmarm, kvm, Linux ARM, James Morse,
	Alexandru Elisei, Suzuki K Poulose, Paolo Bonzini, Will Deacon,
	Andrew Jones, Peng Liang, Peter Shier, Oliver Upton, Jing Zhang,
	Raghavendra Rao Anata

Hi Ricardo,

On Sun, Jan 30, 2022 at 7:40 PM Ricardo Koller <ricarkol@google.com> wrote:
>
> On Fri, Jan 28, 2022 at 09:52:21PM -0800, Reiji Watanabe wrote:
> > Hi Ricardo,
> >
> > > > > > +
> > > > > > +/*
> > > > > > + * Set the guest's ID registers that are defined in sys_reg_descs[]
> > > > > > + * with ID_SANITISED() to the host's sanitized value.
> > > > > > + */
> > > > > > +void set_default_id_regs(struct kvm *kvm)
> > > > > > +{
> > > > > > +     int i;
> > > > > > +     u32 id;
> > > > > > +     const struct sys_reg_desc *rd;
> > > > > > +     u64 val;
> > > > > > +
> > > > > > +     for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
> > > > > > +             rd = &sys_reg_descs[i];
> > > > > > +             if (rd->access != access_id_reg)
> > > > > > +                     /* Not ID register, or hidden/reserved ID register */
> > > > > > +                     continue;
> > > > > > +
> > > > > > +             id = reg_to_encoding(rd);
> > > > > > +             if (WARN_ON_ONCE(!is_id_reg(id)))
> > > > > > +                     /* Shouldn't happen */
> > > > > > +                     continue;
> > > > > > +
> > > > > > +             val = read_sanitised_ftr_reg(id);
> > > > >
> > > > > I'm a bit confused. Shouldn't the default+sanitized values already use
> > > > > arm64_ftr_bits_kvm (instead of arm64_ftr_regs)?
> > > >
> > > > I'm not sure if I understand your question.
> > > > arm64_ftr_bits_kvm is used for feature support checkings when
> > > > userspace tries to modify a value of ID registers.
> > > > With this patch, KVM just saves the sanitized values in the kvm's
> > > > buffer, but userspace is still not allowed to modify values of ID
> > > > registers yet.
> > > > I hope it answers your question.
> > >
> > > Based on the previous commit I was assuming that some registers, like
> > > id_aa64dfr0,
> > > would default to the overwritten values as the sanitized values. More
> > > specifically: if
> > > userspace doesn't modify any ID reg, shouldn't the defaults have the
> > > KVM overwritten
> > > values (arm64_ftr_bits_kvm)?
> >
> > arm64_ftr_bits_kvm doesn't have arm64_ftr_reg but arm64_ftr_bits,
> > and arm64_ftr_bits_kvm doesn't have the sanitized values.
> >
> > Thanks,
>
> Hey Reiji,
>
> Sorry, I wasn't very clear. This is what I meant.
>
> If I set DEBUGVER to 0x5 (w/ FTR_EXACT) using this patch on top of the
> series:
>
>  static struct arm64_ftr_bits ftr_id_aa64dfr0_kvm[MAX_FTR_BITS_LEN] = {
>         S_ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64DFR0_PMUVER_SHIFT, 4, 0),
> -       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x6),
> +       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x5),
>
> it means that userspace would not be able to set DEBUGVER to anything
> but 0x5. But I'm not sure what it should mean for the default KVM value
> of DEBUGVER, specifically the value calculated in set_default_id_regs().
> As it is, KVM is still setting the guest-visible value to 0x6, and my
> "desire" to only allow booting VMs with DEBUGVER=0x5 is being ignored: I
> booted a VM and the DEBUGVER value from inside is still 0x6. I was
> expecting it to not boot, or to show a warning.

Thank you for the explanation!

FTR_EXACT (in the existing code) means that the safe_val should be
used if values of the field are not identical between CPUs (see how
update_cpu_ftr_reg() uses arm64_ftr_safe_value()). For KVM usage,
it means that if the field value for a vCPU is different from the one
for the host's sanitized value, only the safe_val can be used safely
for the guest (purely in terms of CPU feature).

If KVM wants to restrict some features due to some reasons (e.g.
a feature for guests is not supported by the KVM yet), it should
be done by KVM (not by cpufeature.c), and  'validate' function in
"struct id_reg_info", which is introduced in patch-3, will be used
for such cases (the following patches actually use).

Thanks,
Reiji

>
> I think this has some implications for migrations. It would not be
> possible to migrate the example VM on the patched kernel from above: you
> can boot a VM with DEBUGVER=0x5 but you can't migrate it.
>
> Thanks,
> Ricardo

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 02/26] KVM: arm64: Save ID registers' sanitized value per guest
@ 2022-02-01  6:00               ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-02-01  6:00 UTC (permalink / raw)
  To: Ricardo Koller
  Cc: kvm, Marc Zyngier, Peter Shier, Will Deacon, Paolo Bonzini,
	kvmarm, Linux ARM

Hi Ricardo,

On Sun, Jan 30, 2022 at 7:40 PM Ricardo Koller <ricarkol@google.com> wrote:
>
> On Fri, Jan 28, 2022 at 09:52:21PM -0800, Reiji Watanabe wrote:
> > Hi Ricardo,
> >
> > > > > > +
> > > > > > +/*
> > > > > > + * Set the guest's ID registers that are defined in sys_reg_descs[]
> > > > > > + * with ID_SANITISED() to the host's sanitized value.
> > > > > > + */
> > > > > > +void set_default_id_regs(struct kvm *kvm)
> > > > > > +{
> > > > > > +     int i;
> > > > > > +     u32 id;
> > > > > > +     const struct sys_reg_desc *rd;
> > > > > > +     u64 val;
> > > > > > +
> > > > > > +     for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
> > > > > > +             rd = &sys_reg_descs[i];
> > > > > > +             if (rd->access != access_id_reg)
> > > > > > +                     /* Not ID register, or hidden/reserved ID register */
> > > > > > +                     continue;
> > > > > > +
> > > > > > +             id = reg_to_encoding(rd);
> > > > > > +             if (WARN_ON_ONCE(!is_id_reg(id)))
> > > > > > +                     /* Shouldn't happen */
> > > > > > +                     continue;
> > > > > > +
> > > > > > +             val = read_sanitised_ftr_reg(id);
> > > > >
> > > > > I'm a bit confused. Shouldn't the default+sanitized values already use
> > > > > arm64_ftr_bits_kvm (instead of arm64_ftr_regs)?
> > > >
> > > > I'm not sure if I understand your question.
> > > > arm64_ftr_bits_kvm is used for feature support checkings when
> > > > userspace tries to modify a value of ID registers.
> > > > With this patch, KVM just saves the sanitized values in the kvm's
> > > > buffer, but userspace is still not allowed to modify values of ID
> > > > registers yet.
> > > > I hope it answers your question.
> > >
> > > Based on the previous commit I was assuming that some registers, like
> > > id_aa64dfr0,
> > > would default to the overwritten values as the sanitized values. More
> > > specifically: if
> > > userspace doesn't modify any ID reg, shouldn't the defaults have the
> > > KVM overwritten
> > > values (arm64_ftr_bits_kvm)?
> >
> > arm64_ftr_bits_kvm doesn't have arm64_ftr_reg but arm64_ftr_bits,
> > and arm64_ftr_bits_kvm doesn't have the sanitized values.
> >
> > Thanks,
>
> Hey Reiji,
>
> Sorry, I wasn't very clear. This is what I meant.
>
> If I set DEBUGVER to 0x5 (w/ FTR_EXACT) using this patch on top of the
> series:
>
>  static struct arm64_ftr_bits ftr_id_aa64dfr0_kvm[MAX_FTR_BITS_LEN] = {
>         S_ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64DFR0_PMUVER_SHIFT, 4, 0),
> -       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x6),
> +       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x5),
>
> it means that userspace would not be able to set DEBUGVER to anything
> but 0x5. But I'm not sure what it should mean for the default KVM value
> of DEBUGVER, specifically the value calculated in set_default_id_regs().
> As it is, KVM is still setting the guest-visible value to 0x6, and my
> "desire" to only allow booting VMs with DEBUGVER=0x5 is being ignored: I
> booted a VM and the DEBUGVER value from inside is still 0x6. I was
> expecting it to not boot, or to show a warning.

Thank you for the explanation!

FTR_EXACT (in the existing code) means that the safe_val should be
used if values of the field are not identical between CPUs (see how
update_cpu_ftr_reg() uses arm64_ftr_safe_value()). For KVM usage,
it means that if the field value for a vCPU is different from the one
for the host's sanitized value, only the safe_val can be used safely
for the guest (purely in terms of CPU feature).

If KVM wants to restrict some features due to some reasons (e.g.
a feature for guests is not supported by the KVM yet), it should
be done by KVM (not by cpufeature.c), and  'validate' function in
"struct id_reg_info", which is introduced in patch-3, will be used
for such cases (the following patches actually use).

Thanks,
Reiji

>
> I think this has some implications for migrations. It would not be
> possible to migrate the example VM on the patched kernel from above: you
> can boot a VM with DEBUGVER=0x5 but you can't migrate it.
>
> Thanks,
> Ricardo
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 02/26] KVM: arm64: Save ID registers' sanitized value per guest
@ 2022-02-01  6:00               ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-02-01  6:00 UTC (permalink / raw)
  To: Ricardo Koller
  Cc: Marc Zyngier, kvmarm, kvm, Linux ARM, James Morse,
	Alexandru Elisei, Suzuki K Poulose, Paolo Bonzini, Will Deacon,
	Andrew Jones, Peng Liang, Peter Shier, Oliver Upton, Jing Zhang,
	Raghavendra Rao Anata

Hi Ricardo,

On Sun, Jan 30, 2022 at 7:40 PM Ricardo Koller <ricarkol@google.com> wrote:
>
> On Fri, Jan 28, 2022 at 09:52:21PM -0800, Reiji Watanabe wrote:
> > Hi Ricardo,
> >
> > > > > > +
> > > > > > +/*
> > > > > > + * Set the guest's ID registers that are defined in sys_reg_descs[]
> > > > > > + * with ID_SANITISED() to the host's sanitized value.
> > > > > > + */
> > > > > > +void set_default_id_regs(struct kvm *kvm)
> > > > > > +{
> > > > > > +     int i;
> > > > > > +     u32 id;
> > > > > > +     const struct sys_reg_desc *rd;
> > > > > > +     u64 val;
> > > > > > +
> > > > > > +     for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
> > > > > > +             rd = &sys_reg_descs[i];
> > > > > > +             if (rd->access != access_id_reg)
> > > > > > +                     /* Not ID register, or hidden/reserved ID register */
> > > > > > +                     continue;
> > > > > > +
> > > > > > +             id = reg_to_encoding(rd);
> > > > > > +             if (WARN_ON_ONCE(!is_id_reg(id)))
> > > > > > +                     /* Shouldn't happen */
> > > > > > +                     continue;
> > > > > > +
> > > > > > +             val = read_sanitised_ftr_reg(id);
> > > > >
> > > > > I'm a bit confused. Shouldn't the default+sanitized values already use
> > > > > arm64_ftr_bits_kvm (instead of arm64_ftr_regs)?
> > > >
> > > > I'm not sure if I understand your question.
> > > > arm64_ftr_bits_kvm is used for feature support checkings when
> > > > userspace tries to modify a value of ID registers.
> > > > With this patch, KVM just saves the sanitized values in the kvm's
> > > > buffer, but userspace is still not allowed to modify values of ID
> > > > registers yet.
> > > > I hope it answers your question.
> > >
> > > Based on the previous commit I was assuming that some registers, like
> > > id_aa64dfr0,
> > > would default to the overwritten values as the sanitized values. More
> > > specifically: if
> > > userspace doesn't modify any ID reg, shouldn't the defaults have the
> > > KVM overwritten
> > > values (arm64_ftr_bits_kvm)?
> >
> > arm64_ftr_bits_kvm doesn't have arm64_ftr_reg but arm64_ftr_bits,
> > and arm64_ftr_bits_kvm doesn't have the sanitized values.
> >
> > Thanks,
>
> Hey Reiji,
>
> Sorry, I wasn't very clear. This is what I meant.
>
> If I set DEBUGVER to 0x5 (w/ FTR_EXACT) using this patch on top of the
> series:
>
>  static struct arm64_ftr_bits ftr_id_aa64dfr0_kvm[MAX_FTR_BITS_LEN] = {
>         S_ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64DFR0_PMUVER_SHIFT, 4, 0),
> -       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x6),
> +       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x5),
>
> it means that userspace would not be able to set DEBUGVER to anything
> but 0x5. But I'm not sure what it should mean for the default KVM value
> of DEBUGVER, specifically the value calculated in set_default_id_regs().
> As it is, KVM is still setting the guest-visible value to 0x6, and my
> "desire" to only allow booting VMs with DEBUGVER=0x5 is being ignored: I
> booted a VM and the DEBUGVER value from inside is still 0x6. I was
> expecting it to not boot, or to show a warning.

Thank you for the explanation!

FTR_EXACT (in the existing code) means that the safe_val should be
used if values of the field are not identical between CPUs (see how
update_cpu_ftr_reg() uses arm64_ftr_safe_value()). For KVM usage,
it means that if the field value for a vCPU is different from the one
for the host's sanitized value, only the safe_val can be used safely
for the guest (purely in terms of CPU feature).

If KVM wants to restrict some features due to some reasons (e.g.
a feature for guests is not supported by the KVM yet), it should
be done by KVM (not by cpufeature.c), and  'validate' function in
"struct id_reg_info", which is introduced in patch-3, will be used
for such cases (the following patches actually use).

Thanks,
Reiji

>
> I think this has some implications for migrations. It would not be
> possible to migrate the example VM on the patched kernel from above: you
> can boot a VM with DEBUGVER=0x5 but you can't migrate it.
>
> Thanks,
> Ricardo

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 00/26] KVM: arm64: Make CPU ID registers writable by userspace
  2022-01-25  6:31     ` Reiji Watanabe
  (?)
@ 2022-02-01 14:12       ` Fuad Tabba
  -1 siblings, 0 replies; 201+ messages in thread
From: Fuad Tabba @ 2022-02-01 14:12 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, Linux ARM

Hi Reiji,

On Tue, Jan 25, 2022 at 6:31 AM Reiji Watanabe <reijiw@google.com> wrote:
>
> Hi Fuad,
>
> On Mon, Jan 24, 2022 at 8:19 AM Fuad Tabba <tabba@google.com> wrote:
> >
> > Hi Reiji,
> >
> > On Thu, Jan 6, 2022 at 4:27 AM Reiji Watanabe <reijiw@google.com> wrote:
> >
> > > The patch series is for both VHE and non-VHE, except for protected VMs,
> > > which have a different way of configuring ID registers based on its
> > > different requirements [1].
> >
> > I am reviewing this patch series and I really like your approach. I
> > think that once it's through we might be able to generalize it for
> > protected VMs as well (I did some of the work for the ID registers for
> > those).
>
> Thank you for the review and comments for the series.
> I will be looking at your comments.
>
> > > The series is based on v5.16-rc8 with the patch [3] applied.
> >
> > I tried to apply this series on v5.16-rc8 after applying
> > 20220104194918.373612-2-rananta@google.com, and it didn't apply
> > cleanly. If you could point me to a working branch that I could
> > checkout I would like to test it and experiment with it a bit more.
>
> I'm sorry for the inconvenience.  I'm not sure why though...
> I tried that again (applied the series on v5.16-rc8 after applying
> 20220104194918.373612-2-rananta@google.com), and it worked fine.
>
> Anyway, you can find the branch at:
>  https://github.com/reijiw-kvm/kvmarm-idreg/tree/id-regs-v4-5.16-rc8

This works, thanks. I guess the problem I had earlier was that I got
the whole series in 20220104194918.373612-2-rananta@google.com and not
just the first patch.

Cheers,
/fuad


> Thanks,
> Reiji
>
>
> >
> >
> >
> >
> > > v4:
> > >   - Make ID registers storage per VM instead of per vCPU. [Marc]
> > >   - Implement arm64_check_features() in arch/arm64/kernel/cpufeature.c
> > >     by using existing codes in the file. [Marc]
> > >   - Use a configuration function to enable traps for disabled
> > >     features. [Marc]
> > >   - Document ID registers become immutable after the first KVM_RUN [Eric]
> > >   - Update ID_AA64PFR0.GIC at the point where a GICv3 is created. [Marc]
> > >   - Get TGranX's bit position by substracting 12 from TGranX_2's bit
> > >     position. [Eric]
> > >   - Don't validate AArch32 ID registers when the system doesn't support
> > >     32bit EL0. [Eric]
> > >   - Add/fixes comments for patches. [Eric]
> > >   - Made bug fixes/improvements of the selftest. [Eric]
> > >   - Added .kunitconfig for arm64 KUnit tests
> > >
> > > v3: https://lore.kernel.org/all/20211117064359.2362060-1-reijiw@google.com/
> > >   - Remove ID register consistency checking across vCPUs. [Oliver]
> > >   - Change KVM_CAP_ARM_ID_REG_WRITABLE to
> > >     KVM_CAP_ARM_ID_REG_CONFIGURABLE. [Oliver]
> > >   - Add KUnit testing for ID register validation and trap initialization.
> > >   - Change read_id_reg() to take care of ID_AA64PFR0_EL1.GIC.
> > >   - Add a helper of read_id_reg() (__read_id_reg()) and use the helper
> > >     instead of directly using __vcpu_sys_reg().
> > >   - Change not to run kvm_id_regs_consistency_check() and
> > >     kvm_vcpu_init_traps() for protected VMs.
> > >   - Update selftest to remove test cases for ID register consistency.
> > >     checking across vCPUs and to add test cases for ID_AA64PFR0_EL1.GIC.
> > >
> > > v2: https://lore.kernel.org/all/20211103062520.1445832-1-reijiw@google.com/
> > >   - Remove unnecessary line breaks. [Andrew]
> > >   - Use @params for comments. [Andrew]
> > >   - Move arm64_check_features to arch/arm64/kvm/sys_regs.c and
> > >     change that KVM specific feature check function.  [Andrew]
> > >   - Remove unnecessary raz handling from __set_id_reg. [Andrew]
> > >   - Remove sys_val field from the initial id_reg_info and add it
> > >     in the later patch. [Andrew]
> > >   - Call id_reg->init() from id_reg_info_init(). [Andrew]
> > >   - Fix cpuid_feature_cap_perfmon_field() to convert 0xf to 0x0
> > >     (and use it in the following patches).
> > >   - Change kvm_vcpu_first_run_init to set has_run_once to false
> > >     when kvm_id_regs_consistency_check() fails.
> > >   - Add a patch to introduce id_reg_info for ID_AA64MMFR0_EL1,
> > >     which requires special validity checking for TGran*_2 fields.
> > >   - Add patches to introduce id_reg_info for ID_DFR1_EL1 and
> > >     ID_MMFR0_EL1, which are required due to arm64_check_features
> > >     implementation change.
> > >   - Add a new argument, which is a pointer to id_reg_info, for
> > >     id_reg_info's validate().
> > >
> > > v1: https://lore.kernel.org/all/20211012043535.500493-1-reijiw@google.com/
> > >
> > > [1] https://lore.kernel.org/kvmarm/20211010145636.1950948-1-tabba@google.com/
> > > [2] https://lore.kernel.org/kvm/20201102033422.657391-1-liangpeng10@huawei.com/
> > > [3] https://lore.kernel.org/all/20220104194918.373612-2-rananta@google.com/
> > >
> > > Reiji Watanabe (26):
> > >   KVM: arm64: Introduce a validation function for an ID register
> > >   KVM: arm64: Save ID registers' sanitized value per guest
> > >   KVM: arm64: Introduce struct id_reg_info
> > >   KVM: arm64: Make ID_AA64PFR0_EL1 writable
> > >   KVM: arm64: Make ID_AA64PFR1_EL1 writable
> > >   KVM: arm64: Make ID_AA64ISAR0_EL1 writable
> > >   KVM: arm64: Make ID_AA64ISAR1_EL1 writable
> > >   KVM: arm64: Make ID_AA64MMFR0_EL1 writable
> > >   KVM: arm64: Hide IMPLEMENTATION DEFINED PMU support for the guest
> > >   KVM: arm64: Make ID_AA64DFR0_EL1 writable
> > >   KVM: arm64: Make ID_DFR0_EL1 writable
> > >   KVM: arm64: Make MVFR1_EL1 writable
> > >   KVM: arm64: Make ID registers without id_reg_info writable
> > >   KVM: arm64: Add consistency checking for frac fields of ID registers
> > >   KVM: arm64: Introduce KVM_CAP_ARM_ID_REG_CONFIGURABLE capability
> > >   KVM: arm64: Add kunit test for ID register validation
> > >   KVM: arm64: Use vcpu->arch cptr_el2 to track value of cptr_el2 for VHE
> > >   KVM: arm64: Use vcpu->arch.mdcr_el2 to track value of mdcr_el2
> > >   KVM: arm64: Introduce framework to trap disabled features
> > >   KVM: arm64: Trap disabled features of ID_AA64PFR0_EL1
> > >   KVM: arm64: Trap disabled features of ID_AA64PFR1_EL1
> > >   KVM: arm64: Trap disabled features of ID_AA64DFR0_EL1
> > >   KVM: arm64: Trap disabled features of ID_AA64MMFR1_EL1
> > >   KVM: arm64: Trap disabled features of ID_AA64ISAR1_EL1
> > >   KVM: arm64: Add kunit test for trap initialization
> > >   KVM: arm64: selftests: Introduce id_reg_test
> > >
> > >  Documentation/virt/kvm/api.rst                |   12 +
> > >  arch/arm64/include/asm/cpufeature.h           |    3 +-
> > >  arch/arm64/include/asm/kvm_arm.h              |   32 +
> > >  arch/arm64/include/asm/kvm_host.h             |   19 +
> > >  arch/arm64/include/asm/sysreg.h               |    3 +
> > >  arch/arm64/kernel/cpufeature.c                |  228 +++
> > >  arch/arm64/kvm/.kunitconfig                   |    4 +
> > >  arch/arm64/kvm/Kconfig                        |   11 +
> > >  arch/arm64/kvm/arm.c                          |   24 +-
> > >  arch/arm64/kvm/debug.c                        |   13 +-
> > >  arch/arm64/kvm/hyp/vhe/switch.c               |   14 +-
> > >  arch/arm64/kvm/sys_regs.c                     | 1329 +++++++++++++++--
> > >  arch/arm64/kvm/sys_regs_test.c                | 1247 ++++++++++++++++
> > >  arch/arm64/kvm/vgic/vgic-init.c               |    5 +
> > >  include/uapi/linux/kvm.h                      |    1 +
> > >  tools/arch/arm64/include/asm/sysreg.h         |    1 +
> > >  tools/testing/selftests/kvm/.gitignore        |    1 +
> > >  tools/testing/selftests/kvm/Makefile          |    1 +
> > >  .../selftests/kvm/aarch64/id_reg_test.c       | 1239 +++++++++++++++
> > >  19 files changed, 4041 insertions(+), 146 deletions(-)
> > >  create mode 100644 arch/arm64/kvm/.kunitconfig
> > >  create mode 100644 arch/arm64/kvm/sys_regs_test.c
> > >  create mode 100644 tools/testing/selftests/kvm/aarch64/id_reg_test.c
> > >
> > >
> > > base-commit: d399b107ee49bf5ea0391bd7614d512809e927b0
> > > --
> > > 2.34.1.448.ga2b2bfdf31-goog
> > >
> > > _______________________________________________
> > > kvmarm mailing list
> > > kvmarm@lists.cs.columbia.edu
> > > https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 00/26] KVM: arm64: Make CPU ID registers writable by userspace
@ 2022-02-01 14:12       ` Fuad Tabba
  0 siblings, 0 replies; 201+ messages in thread
From: Fuad Tabba @ 2022-02-01 14:12 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: kvm, Marc Zyngier, Peter Shier, Paolo Bonzini, Will Deacon,
	kvmarm, Linux ARM

Hi Reiji,

On Tue, Jan 25, 2022 at 6:31 AM Reiji Watanabe <reijiw@google.com> wrote:
>
> Hi Fuad,
>
> On Mon, Jan 24, 2022 at 8:19 AM Fuad Tabba <tabba@google.com> wrote:
> >
> > Hi Reiji,
> >
> > On Thu, Jan 6, 2022 at 4:27 AM Reiji Watanabe <reijiw@google.com> wrote:
> >
> > > The patch series is for both VHE and non-VHE, except for protected VMs,
> > > which have a different way of configuring ID registers based on its
> > > different requirements [1].
> >
> > I am reviewing this patch series and I really like your approach. I
> > think that once it's through we might be able to generalize it for
> > protected VMs as well (I did some of the work for the ID registers for
> > those).
>
> Thank you for the review and comments for the series.
> I will be looking at your comments.
>
> > > The series is based on v5.16-rc8 with the patch [3] applied.
> >
> > I tried to apply this series on v5.16-rc8 after applying
> > 20220104194918.373612-2-rananta@google.com, and it didn't apply
> > cleanly. If you could point me to a working branch that I could
> > checkout I would like to test it and experiment with it a bit more.
>
> I'm sorry for the inconvenience.  I'm not sure why though...
> I tried that again (applied the series on v5.16-rc8 after applying
> 20220104194918.373612-2-rananta@google.com), and it worked fine.
>
> Anyway, you can find the branch at:
>  https://github.com/reijiw-kvm/kvmarm-idreg/tree/id-regs-v4-5.16-rc8

This works, thanks. I guess the problem I had earlier was that I got
the whole series in 20220104194918.373612-2-rananta@google.com and not
just the first patch.

Cheers,
/fuad


> Thanks,
> Reiji
>
>
> >
> >
> >
> >
> > > v4:
> > >   - Make ID registers storage per VM instead of per vCPU. [Marc]
> > >   - Implement arm64_check_features() in arch/arm64/kernel/cpufeature.c
> > >     by using existing codes in the file. [Marc]
> > >   - Use a configuration function to enable traps for disabled
> > >     features. [Marc]
> > >   - Document ID registers become immutable after the first KVM_RUN [Eric]
> > >   - Update ID_AA64PFR0.GIC at the point where a GICv3 is created. [Marc]
> > >   - Get TGranX's bit position by substracting 12 from TGranX_2's bit
> > >     position. [Eric]
> > >   - Don't validate AArch32 ID registers when the system doesn't support
> > >     32bit EL0. [Eric]
> > >   - Add/fixes comments for patches. [Eric]
> > >   - Made bug fixes/improvements of the selftest. [Eric]
> > >   - Added .kunitconfig for arm64 KUnit tests
> > >
> > > v3: https://lore.kernel.org/all/20211117064359.2362060-1-reijiw@google.com/
> > >   - Remove ID register consistency checking across vCPUs. [Oliver]
> > >   - Change KVM_CAP_ARM_ID_REG_WRITABLE to
> > >     KVM_CAP_ARM_ID_REG_CONFIGURABLE. [Oliver]
> > >   - Add KUnit testing for ID register validation and trap initialization.
> > >   - Change read_id_reg() to take care of ID_AA64PFR0_EL1.GIC.
> > >   - Add a helper of read_id_reg() (__read_id_reg()) and use the helper
> > >     instead of directly using __vcpu_sys_reg().
> > >   - Change not to run kvm_id_regs_consistency_check() and
> > >     kvm_vcpu_init_traps() for protected VMs.
> > >   - Update selftest to remove test cases for ID register consistency.
> > >     checking across vCPUs and to add test cases for ID_AA64PFR0_EL1.GIC.
> > >
> > > v2: https://lore.kernel.org/all/20211103062520.1445832-1-reijiw@google.com/
> > >   - Remove unnecessary line breaks. [Andrew]
> > >   - Use @params for comments. [Andrew]
> > >   - Move arm64_check_features to arch/arm64/kvm/sys_regs.c and
> > >     change that KVM specific feature check function.  [Andrew]
> > >   - Remove unnecessary raz handling from __set_id_reg. [Andrew]
> > >   - Remove sys_val field from the initial id_reg_info and add it
> > >     in the later patch. [Andrew]
> > >   - Call id_reg->init() from id_reg_info_init(). [Andrew]
> > >   - Fix cpuid_feature_cap_perfmon_field() to convert 0xf to 0x0
> > >     (and use it in the following patches).
> > >   - Change kvm_vcpu_first_run_init to set has_run_once to false
> > >     when kvm_id_regs_consistency_check() fails.
> > >   - Add a patch to introduce id_reg_info for ID_AA64MMFR0_EL1,
> > >     which requires special validity checking for TGran*_2 fields.
> > >   - Add patches to introduce id_reg_info for ID_DFR1_EL1 and
> > >     ID_MMFR0_EL1, which are required due to arm64_check_features
> > >     implementation change.
> > >   - Add a new argument, which is a pointer to id_reg_info, for
> > >     id_reg_info's validate().
> > >
> > > v1: https://lore.kernel.org/all/20211012043535.500493-1-reijiw@google.com/
> > >
> > > [1] https://lore.kernel.org/kvmarm/20211010145636.1950948-1-tabba@google.com/
> > > [2] https://lore.kernel.org/kvm/20201102033422.657391-1-liangpeng10@huawei.com/
> > > [3] https://lore.kernel.org/all/20220104194918.373612-2-rananta@google.com/
> > >
> > > Reiji Watanabe (26):
> > >   KVM: arm64: Introduce a validation function for an ID register
> > >   KVM: arm64: Save ID registers' sanitized value per guest
> > >   KVM: arm64: Introduce struct id_reg_info
> > >   KVM: arm64: Make ID_AA64PFR0_EL1 writable
> > >   KVM: arm64: Make ID_AA64PFR1_EL1 writable
> > >   KVM: arm64: Make ID_AA64ISAR0_EL1 writable
> > >   KVM: arm64: Make ID_AA64ISAR1_EL1 writable
> > >   KVM: arm64: Make ID_AA64MMFR0_EL1 writable
> > >   KVM: arm64: Hide IMPLEMENTATION DEFINED PMU support for the guest
> > >   KVM: arm64: Make ID_AA64DFR0_EL1 writable
> > >   KVM: arm64: Make ID_DFR0_EL1 writable
> > >   KVM: arm64: Make MVFR1_EL1 writable
> > >   KVM: arm64: Make ID registers without id_reg_info writable
> > >   KVM: arm64: Add consistency checking for frac fields of ID registers
> > >   KVM: arm64: Introduce KVM_CAP_ARM_ID_REG_CONFIGURABLE capability
> > >   KVM: arm64: Add kunit test for ID register validation
> > >   KVM: arm64: Use vcpu->arch cptr_el2 to track value of cptr_el2 for VHE
> > >   KVM: arm64: Use vcpu->arch.mdcr_el2 to track value of mdcr_el2
> > >   KVM: arm64: Introduce framework to trap disabled features
> > >   KVM: arm64: Trap disabled features of ID_AA64PFR0_EL1
> > >   KVM: arm64: Trap disabled features of ID_AA64PFR1_EL1
> > >   KVM: arm64: Trap disabled features of ID_AA64DFR0_EL1
> > >   KVM: arm64: Trap disabled features of ID_AA64MMFR1_EL1
> > >   KVM: arm64: Trap disabled features of ID_AA64ISAR1_EL1
> > >   KVM: arm64: Add kunit test for trap initialization
> > >   KVM: arm64: selftests: Introduce id_reg_test
> > >
> > >  Documentation/virt/kvm/api.rst                |   12 +
> > >  arch/arm64/include/asm/cpufeature.h           |    3 +-
> > >  arch/arm64/include/asm/kvm_arm.h              |   32 +
> > >  arch/arm64/include/asm/kvm_host.h             |   19 +
> > >  arch/arm64/include/asm/sysreg.h               |    3 +
> > >  arch/arm64/kernel/cpufeature.c                |  228 +++
> > >  arch/arm64/kvm/.kunitconfig                   |    4 +
> > >  arch/arm64/kvm/Kconfig                        |   11 +
> > >  arch/arm64/kvm/arm.c                          |   24 +-
> > >  arch/arm64/kvm/debug.c                        |   13 +-
> > >  arch/arm64/kvm/hyp/vhe/switch.c               |   14 +-
> > >  arch/arm64/kvm/sys_regs.c                     | 1329 +++++++++++++++--
> > >  arch/arm64/kvm/sys_regs_test.c                | 1247 ++++++++++++++++
> > >  arch/arm64/kvm/vgic/vgic-init.c               |    5 +
> > >  include/uapi/linux/kvm.h                      |    1 +
> > >  tools/arch/arm64/include/asm/sysreg.h         |    1 +
> > >  tools/testing/selftests/kvm/.gitignore        |    1 +
> > >  tools/testing/selftests/kvm/Makefile          |    1 +
> > >  .../selftests/kvm/aarch64/id_reg_test.c       | 1239 +++++++++++++++
> > >  19 files changed, 4041 insertions(+), 146 deletions(-)
> > >  create mode 100644 arch/arm64/kvm/.kunitconfig
> > >  create mode 100644 arch/arm64/kvm/sys_regs_test.c
> > >  create mode 100644 tools/testing/selftests/kvm/aarch64/id_reg_test.c
> > >
> > >
> > > base-commit: d399b107ee49bf5ea0391bd7614d512809e927b0
> > > --
> > > 2.34.1.448.ga2b2bfdf31-goog
> > >
> > > _______________________________________________
> > > kvmarm mailing list
> > > kvmarm@lists.cs.columbia.edu
> > > https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 00/26] KVM: arm64: Make CPU ID registers writable by userspace
@ 2022-02-01 14:12       ` Fuad Tabba
  0 siblings, 0 replies; 201+ messages in thread
From: Fuad Tabba @ 2022-02-01 14:12 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, Linux ARM

Hi Reiji,

On Tue, Jan 25, 2022 at 6:31 AM Reiji Watanabe <reijiw@google.com> wrote:
>
> Hi Fuad,
>
> On Mon, Jan 24, 2022 at 8:19 AM Fuad Tabba <tabba@google.com> wrote:
> >
> > Hi Reiji,
> >
> > On Thu, Jan 6, 2022 at 4:27 AM Reiji Watanabe <reijiw@google.com> wrote:
> >
> > > The patch series is for both VHE and non-VHE, except for protected VMs,
> > > which have a different way of configuring ID registers based on its
> > > different requirements [1].
> >
> > I am reviewing this patch series and I really like your approach. I
> > think that once it's through we might be able to generalize it for
> > protected VMs as well (I did some of the work for the ID registers for
> > those).
>
> Thank you for the review and comments for the series.
> I will be looking at your comments.
>
> > > The series is based on v5.16-rc8 with the patch [3] applied.
> >
> > I tried to apply this series on v5.16-rc8 after applying
> > 20220104194918.373612-2-rananta@google.com, and it didn't apply
> > cleanly. If you could point me to a working branch that I could
> > checkout I would like to test it and experiment with it a bit more.
>
> I'm sorry for the inconvenience.  I'm not sure why though...
> I tried that again (applied the series on v5.16-rc8 after applying
> 20220104194918.373612-2-rananta@google.com), and it worked fine.
>
> Anyway, you can find the branch at:
>  https://github.com/reijiw-kvm/kvmarm-idreg/tree/id-regs-v4-5.16-rc8

This works, thanks. I guess the problem I had earlier was that I got
the whole series in 20220104194918.373612-2-rananta@google.com and not
just the first patch.

Cheers,
/fuad


> Thanks,
> Reiji
>
>
> >
> >
> >
> >
> > > v4:
> > >   - Make ID registers storage per VM instead of per vCPU. [Marc]
> > >   - Implement arm64_check_features() in arch/arm64/kernel/cpufeature.c
> > >     by using existing codes in the file. [Marc]
> > >   - Use a configuration function to enable traps for disabled
> > >     features. [Marc]
> > >   - Document ID registers become immutable after the first KVM_RUN [Eric]
> > >   - Update ID_AA64PFR0.GIC at the point where a GICv3 is created. [Marc]
> > >   - Get TGranX's bit position by substracting 12 from TGranX_2's bit
> > >     position. [Eric]
> > >   - Don't validate AArch32 ID registers when the system doesn't support
> > >     32bit EL0. [Eric]
> > >   - Add/fixes comments for patches. [Eric]
> > >   - Made bug fixes/improvements of the selftest. [Eric]
> > >   - Added .kunitconfig for arm64 KUnit tests
> > >
> > > v3: https://lore.kernel.org/all/20211117064359.2362060-1-reijiw@google.com/
> > >   - Remove ID register consistency checking across vCPUs. [Oliver]
> > >   - Change KVM_CAP_ARM_ID_REG_WRITABLE to
> > >     KVM_CAP_ARM_ID_REG_CONFIGURABLE. [Oliver]
> > >   - Add KUnit testing for ID register validation and trap initialization.
> > >   - Change read_id_reg() to take care of ID_AA64PFR0_EL1.GIC.
> > >   - Add a helper of read_id_reg() (__read_id_reg()) and use the helper
> > >     instead of directly using __vcpu_sys_reg().
> > >   - Change not to run kvm_id_regs_consistency_check() and
> > >     kvm_vcpu_init_traps() for protected VMs.
> > >   - Update selftest to remove test cases for ID register consistency.
> > >     checking across vCPUs and to add test cases for ID_AA64PFR0_EL1.GIC.
> > >
> > > v2: https://lore.kernel.org/all/20211103062520.1445832-1-reijiw@google.com/
> > >   - Remove unnecessary line breaks. [Andrew]
> > >   - Use @params for comments. [Andrew]
> > >   - Move arm64_check_features to arch/arm64/kvm/sys_regs.c and
> > >     change that KVM specific feature check function.  [Andrew]
> > >   - Remove unnecessary raz handling from __set_id_reg. [Andrew]
> > >   - Remove sys_val field from the initial id_reg_info and add it
> > >     in the later patch. [Andrew]
> > >   - Call id_reg->init() from id_reg_info_init(). [Andrew]
> > >   - Fix cpuid_feature_cap_perfmon_field() to convert 0xf to 0x0
> > >     (and use it in the following patches).
> > >   - Change kvm_vcpu_first_run_init to set has_run_once to false
> > >     when kvm_id_regs_consistency_check() fails.
> > >   - Add a patch to introduce id_reg_info for ID_AA64MMFR0_EL1,
> > >     which requires special validity checking for TGran*_2 fields.
> > >   - Add patches to introduce id_reg_info for ID_DFR1_EL1 and
> > >     ID_MMFR0_EL1, which are required due to arm64_check_features
> > >     implementation change.
> > >   - Add a new argument, which is a pointer to id_reg_info, for
> > >     id_reg_info's validate().
> > >
> > > v1: https://lore.kernel.org/all/20211012043535.500493-1-reijiw@google.com/
> > >
> > > [1] https://lore.kernel.org/kvmarm/20211010145636.1950948-1-tabba@google.com/
> > > [2] https://lore.kernel.org/kvm/20201102033422.657391-1-liangpeng10@huawei.com/
> > > [3] https://lore.kernel.org/all/20220104194918.373612-2-rananta@google.com/
> > >
> > > Reiji Watanabe (26):
> > >   KVM: arm64: Introduce a validation function for an ID register
> > >   KVM: arm64: Save ID registers' sanitized value per guest
> > >   KVM: arm64: Introduce struct id_reg_info
> > >   KVM: arm64: Make ID_AA64PFR0_EL1 writable
> > >   KVM: arm64: Make ID_AA64PFR1_EL1 writable
> > >   KVM: arm64: Make ID_AA64ISAR0_EL1 writable
> > >   KVM: arm64: Make ID_AA64ISAR1_EL1 writable
> > >   KVM: arm64: Make ID_AA64MMFR0_EL1 writable
> > >   KVM: arm64: Hide IMPLEMENTATION DEFINED PMU support for the guest
> > >   KVM: arm64: Make ID_AA64DFR0_EL1 writable
> > >   KVM: arm64: Make ID_DFR0_EL1 writable
> > >   KVM: arm64: Make MVFR1_EL1 writable
> > >   KVM: arm64: Make ID registers without id_reg_info writable
> > >   KVM: arm64: Add consistency checking for frac fields of ID registers
> > >   KVM: arm64: Introduce KVM_CAP_ARM_ID_REG_CONFIGURABLE capability
> > >   KVM: arm64: Add kunit test for ID register validation
> > >   KVM: arm64: Use vcpu->arch cptr_el2 to track value of cptr_el2 for VHE
> > >   KVM: arm64: Use vcpu->arch.mdcr_el2 to track value of mdcr_el2
> > >   KVM: arm64: Introduce framework to trap disabled features
> > >   KVM: arm64: Trap disabled features of ID_AA64PFR0_EL1
> > >   KVM: arm64: Trap disabled features of ID_AA64PFR1_EL1
> > >   KVM: arm64: Trap disabled features of ID_AA64DFR0_EL1
> > >   KVM: arm64: Trap disabled features of ID_AA64MMFR1_EL1
> > >   KVM: arm64: Trap disabled features of ID_AA64ISAR1_EL1
> > >   KVM: arm64: Add kunit test for trap initialization
> > >   KVM: arm64: selftests: Introduce id_reg_test
> > >
> > >  Documentation/virt/kvm/api.rst                |   12 +
> > >  arch/arm64/include/asm/cpufeature.h           |    3 +-
> > >  arch/arm64/include/asm/kvm_arm.h              |   32 +
> > >  arch/arm64/include/asm/kvm_host.h             |   19 +
> > >  arch/arm64/include/asm/sysreg.h               |    3 +
> > >  arch/arm64/kernel/cpufeature.c                |  228 +++
> > >  arch/arm64/kvm/.kunitconfig                   |    4 +
> > >  arch/arm64/kvm/Kconfig                        |   11 +
> > >  arch/arm64/kvm/arm.c                          |   24 +-
> > >  arch/arm64/kvm/debug.c                        |   13 +-
> > >  arch/arm64/kvm/hyp/vhe/switch.c               |   14 +-
> > >  arch/arm64/kvm/sys_regs.c                     | 1329 +++++++++++++++--
> > >  arch/arm64/kvm/sys_regs_test.c                | 1247 ++++++++++++++++
> > >  arch/arm64/kvm/vgic/vgic-init.c               |    5 +
> > >  include/uapi/linux/kvm.h                      |    1 +
> > >  tools/arch/arm64/include/asm/sysreg.h         |    1 +
> > >  tools/testing/selftests/kvm/.gitignore        |    1 +
> > >  tools/testing/selftests/kvm/Makefile          |    1 +
> > >  .../selftests/kvm/aarch64/id_reg_test.c       | 1239 +++++++++++++++
> > >  19 files changed, 4041 insertions(+), 146 deletions(-)
> > >  create mode 100644 arch/arm64/kvm/.kunitconfig
> > >  create mode 100644 arch/arm64/kvm/sys_regs_test.c
> > >  create mode 100644 tools/testing/selftests/kvm/aarch64/id_reg_test.c
> > >
> > >
> > > base-commit: d399b107ee49bf5ea0391bd7614d512809e927b0
> > > --
> > > 2.34.1.448.ga2b2bfdf31-goog
> > >
> > > _______________________________________________
> > > kvmarm mailing list
> > > kvmarm@lists.cs.columbia.edu
> > > https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 01/26] KVM: arm64: Introduce a validation function for an ID register
  2022-01-26  6:04       ` Reiji Watanabe
  (?)
@ 2022-02-01 14:13         ` Fuad Tabba
  -1 siblings, 0 replies; 201+ messages in thread
From: Fuad Tabba @ 2022-02-01 14:13 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, Linux ARM

Hi Reiji,

...

> > Could you please explain using ftr_temp[] and changing the value in
> > arm64_ftr_bits_kvm_override, rather than just
> > arm64_ftr_reg_bits_overrite(bits->ftr_bits, o_bits->ftr_bits)?
>
> I would like to maintain the order of the entries in the original
> ftr_bits so that (future) functions that work for the original ones
> also work for the KVM's.
> The copy and override is an easy way to do that.  The same thing can
> be done without ftr_temp[], but it would look a bit tricky.
>
> If we assume the order shouldn't matter or entries in ftr_bits
> are always in descending order, just changing the value in
> arm64_ftr_bits_kvm_override would be a much simpler way though.

Could you please add a comment in that case? I did find it to be
confusing until I read your explanation here.

>
> >
> >
> > > +static const struct arm64_ftr_bits *get_arm64_ftr_bits_kvm(u32 sys_id)
> > > +{
> > > +       const struct __ftr_reg_bits_entry *ret;
> > > +       int err;
> > > +
> > > +       if (!arm64_ftr_bits_kvm) {
> > > +               /* arm64_ftr_bits_kvm is not initialized yet. */
> > > +               err = init_arm64_ftr_bits_kvm();
> >
> > Rather than doing this check, can we just initialize it earlier, maybe
> > (indirectly) via kvm_arch_init_vm() or around the same time?
>
> Thank you for the comment.
> I will consider when it should be initialized.
> ( perhaps even earlier than kvm_arch_init_vm())
>
> >
> >
> > > +               if (err)
> > > +                       return NULL;
> > > +       }
> > > +
> > > +       ret = bsearch((const void *)(unsigned long)sys_id,
> > > +                     arm64_ftr_bits_kvm,
> > > +                     arm64_ftr_bits_kvm_nentries,
> > > +                     sizeof(arm64_ftr_bits_kvm[0]),
> > > +                     search_cmp_ftr_reg_bits);
> > > +       if (ret)
> > > +               return ret->ftr_bits;
> > > +
> > > +       return NULL;
> > > +}
> > > +
> > > +/*
> > > + * Check if features (or levels of features) that are indicated in the ID
> > > + * register value @val are also indicated in @limit.
> > > + * This function is for KVM to check if features that are indicated in @val,
> > > + * which will be used as the ID register value for its guest, are supported
> > > + * on the host.
> > > + * For AA64MMFR0_EL1.TGranX_2 fields, which don't follow the standard ID
> > > + * scheme, the function checks if values of the fields in @val are the same
> > > + * as the ones in @limit.
> > > + */
> > > +int arm64_check_features(u32 sys_reg, u64 val, u64 limit)
> > > +{
> > > +       const struct arm64_ftr_bits *ftrp = get_arm64_ftr_bits_kvm(sys_reg);
> > > +       u64 exposed_mask = 0;
> > > +
> > > +       if (!ftrp)
> > > +               return -ENOENT;
> > > +
> > > +       for (; ftrp->width; ftrp++) {
> > > +               s64 ftr_val = arm64_ftr_value(ftrp, val);
> > > +               s64 ftr_lim = arm64_ftr_value(ftrp, limit);
> > > +
> > > +               exposed_mask |= arm64_ftr_mask(ftrp);
> > > +
> > > +               if (ftr_val == ftr_lim)
> > > +                       continue;
> >
> > At first I thought that this check isn't necessary, it should be
> > covered by the check below (arm64_ftr_safe_value. However, I think
> > that it's needed for the FTR_HIGHER_OR_ZERO_SAFE case. If my
> > understanding is correct, it might be worth adding a comment, or even
> > encapsulating this logic in a arm64_is_safe_value() function for
> > clarity.
>
> In my understanding, arm64_ftr_safe_value() provides a safe value
> when two values are different, and I think there is nothing special
> about the usage of this function (This is actually how the function
> is used by update_cpu_ftr_reg()).
> Without the check, it won't work for FTR_EXACT, but there might be
> more in the future.
>
> Perhaps it might be more straightforward to add the equality check
> into arm64_ftr_safe_value() ?

I don't think this would work for all callers of
arm64_ftr_safe_value(). The thing is arm64_ftr_safe_value() doesn't
check whether the value is safe, but it returns the safe value that
supports the highest feature. Whereas arm64_check_features() on the
other hand is trying to determine whether a value is safe.

If you move the equality check there it would work for
arm64_check_features(), but I am not convinced it wouldn't change the
behavior for init_cpu_ftr_reg() in the case of FTR_EXACT, unless this
never applies to override->val. What do you think?

Thanks,
/fuad


> >
> > > +
> > > +               if (ftr_val != arm64_ftr_safe_value(ftrp, ftr_val, ftr_lim))
> > > +                       return -E2BIG;
> > > +       }
> > > +
> > > +       /* Make sure that no unrecognized fields are set in @val. */
> > > +       if (val & ~exposed_mask)
> > > +               return -E2BIG;
> > > +
> > > +       return 0;
> > > +}
>
> Thanks,
> Reiji

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 01/26] KVM: arm64: Introduce a validation function for an ID register
@ 2022-02-01 14:13         ` Fuad Tabba
  0 siblings, 0 replies; 201+ messages in thread
From: Fuad Tabba @ 2022-02-01 14:13 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: kvm, Marc Zyngier, Peter Shier, Paolo Bonzini, Will Deacon,
	kvmarm, Linux ARM

Hi Reiji,

...

> > Could you please explain using ftr_temp[] and changing the value in
> > arm64_ftr_bits_kvm_override, rather than just
> > arm64_ftr_reg_bits_overrite(bits->ftr_bits, o_bits->ftr_bits)?
>
> I would like to maintain the order of the entries in the original
> ftr_bits so that (future) functions that work for the original ones
> also work for the KVM's.
> The copy and override is an easy way to do that.  The same thing can
> be done without ftr_temp[], but it would look a bit tricky.
>
> If we assume the order shouldn't matter or entries in ftr_bits
> are always in descending order, just changing the value in
> arm64_ftr_bits_kvm_override would be a much simpler way though.

Could you please add a comment in that case? I did find it to be
confusing until I read your explanation here.

>
> >
> >
> > > +static const struct arm64_ftr_bits *get_arm64_ftr_bits_kvm(u32 sys_id)
> > > +{
> > > +       const struct __ftr_reg_bits_entry *ret;
> > > +       int err;
> > > +
> > > +       if (!arm64_ftr_bits_kvm) {
> > > +               /* arm64_ftr_bits_kvm is not initialized yet. */
> > > +               err = init_arm64_ftr_bits_kvm();
> >
> > Rather than doing this check, can we just initialize it earlier, maybe
> > (indirectly) via kvm_arch_init_vm() or around the same time?
>
> Thank you for the comment.
> I will consider when it should be initialized.
> ( perhaps even earlier than kvm_arch_init_vm())
>
> >
> >
> > > +               if (err)
> > > +                       return NULL;
> > > +       }
> > > +
> > > +       ret = bsearch((const void *)(unsigned long)sys_id,
> > > +                     arm64_ftr_bits_kvm,
> > > +                     arm64_ftr_bits_kvm_nentries,
> > > +                     sizeof(arm64_ftr_bits_kvm[0]),
> > > +                     search_cmp_ftr_reg_bits);
> > > +       if (ret)
> > > +               return ret->ftr_bits;
> > > +
> > > +       return NULL;
> > > +}
> > > +
> > > +/*
> > > + * Check if features (or levels of features) that are indicated in the ID
> > > + * register value @val are also indicated in @limit.
> > > + * This function is for KVM to check if features that are indicated in @val,
> > > + * which will be used as the ID register value for its guest, are supported
> > > + * on the host.
> > > + * For AA64MMFR0_EL1.TGranX_2 fields, which don't follow the standard ID
> > > + * scheme, the function checks if values of the fields in @val are the same
> > > + * as the ones in @limit.
> > > + */
> > > +int arm64_check_features(u32 sys_reg, u64 val, u64 limit)
> > > +{
> > > +       const struct arm64_ftr_bits *ftrp = get_arm64_ftr_bits_kvm(sys_reg);
> > > +       u64 exposed_mask = 0;
> > > +
> > > +       if (!ftrp)
> > > +               return -ENOENT;
> > > +
> > > +       for (; ftrp->width; ftrp++) {
> > > +               s64 ftr_val = arm64_ftr_value(ftrp, val);
> > > +               s64 ftr_lim = arm64_ftr_value(ftrp, limit);
> > > +
> > > +               exposed_mask |= arm64_ftr_mask(ftrp);
> > > +
> > > +               if (ftr_val == ftr_lim)
> > > +                       continue;
> >
> > At first I thought that this check isn't necessary, it should be
> > covered by the check below (arm64_ftr_safe_value. However, I think
> > that it's needed for the FTR_HIGHER_OR_ZERO_SAFE case. If my
> > understanding is correct, it might be worth adding a comment, or even
> > encapsulating this logic in a arm64_is_safe_value() function for
> > clarity.
>
> In my understanding, arm64_ftr_safe_value() provides a safe value
> when two values are different, and I think there is nothing special
> about the usage of this function (This is actually how the function
> is used by update_cpu_ftr_reg()).
> Without the check, it won't work for FTR_EXACT, but there might be
> more in the future.
>
> Perhaps it might be more straightforward to add the equality check
> into arm64_ftr_safe_value() ?

I don't think this would work for all callers of
arm64_ftr_safe_value(). The thing is arm64_ftr_safe_value() doesn't
check whether the value is safe, but it returns the safe value that
supports the highest feature. Whereas arm64_check_features() on the
other hand is trying to determine whether a value is safe.

If you move the equality check there it would work for
arm64_check_features(), but I am not convinced it wouldn't change the
behavior for init_cpu_ftr_reg() in the case of FTR_EXACT, unless this
never applies to override->val. What do you think?

Thanks,
/fuad


> >
> > > +
> > > +               if (ftr_val != arm64_ftr_safe_value(ftrp, ftr_val, ftr_lim))
> > > +                       return -E2BIG;
> > > +       }
> > > +
> > > +       /* Make sure that no unrecognized fields are set in @val. */
> > > +       if (val & ~exposed_mask)
> > > +               return -E2BIG;
> > > +
> > > +       return 0;
> > > +}
>
> Thanks,
> Reiji
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 01/26] KVM: arm64: Introduce a validation function for an ID register
@ 2022-02-01 14:13         ` Fuad Tabba
  0 siblings, 0 replies; 201+ messages in thread
From: Fuad Tabba @ 2022-02-01 14:13 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, Linux ARM

Hi Reiji,

...

> > Could you please explain using ftr_temp[] and changing the value in
> > arm64_ftr_bits_kvm_override, rather than just
> > arm64_ftr_reg_bits_overrite(bits->ftr_bits, o_bits->ftr_bits)?
>
> I would like to maintain the order of the entries in the original
> ftr_bits so that (future) functions that work for the original ones
> also work for the KVM's.
> The copy and override is an easy way to do that.  The same thing can
> be done without ftr_temp[], but it would look a bit tricky.
>
> If we assume the order shouldn't matter or entries in ftr_bits
> are always in descending order, just changing the value in
> arm64_ftr_bits_kvm_override would be a much simpler way though.

Could you please add a comment in that case? I did find it to be
confusing until I read your explanation here.

>
> >
> >
> > > +static const struct arm64_ftr_bits *get_arm64_ftr_bits_kvm(u32 sys_id)
> > > +{
> > > +       const struct __ftr_reg_bits_entry *ret;
> > > +       int err;
> > > +
> > > +       if (!arm64_ftr_bits_kvm) {
> > > +               /* arm64_ftr_bits_kvm is not initialized yet. */
> > > +               err = init_arm64_ftr_bits_kvm();
> >
> > Rather than doing this check, can we just initialize it earlier, maybe
> > (indirectly) via kvm_arch_init_vm() or around the same time?
>
> Thank you for the comment.
> I will consider when it should be initialized.
> ( perhaps even earlier than kvm_arch_init_vm())
>
> >
> >
> > > +               if (err)
> > > +                       return NULL;
> > > +       }
> > > +
> > > +       ret = bsearch((const void *)(unsigned long)sys_id,
> > > +                     arm64_ftr_bits_kvm,
> > > +                     arm64_ftr_bits_kvm_nentries,
> > > +                     sizeof(arm64_ftr_bits_kvm[0]),
> > > +                     search_cmp_ftr_reg_bits);
> > > +       if (ret)
> > > +               return ret->ftr_bits;
> > > +
> > > +       return NULL;
> > > +}
> > > +
> > > +/*
> > > + * Check if features (or levels of features) that are indicated in the ID
> > > + * register value @val are also indicated in @limit.
> > > + * This function is for KVM to check if features that are indicated in @val,
> > > + * which will be used as the ID register value for its guest, are supported
> > > + * on the host.
> > > + * For AA64MMFR0_EL1.TGranX_2 fields, which don't follow the standard ID
> > > + * scheme, the function checks if values of the fields in @val are the same
> > > + * as the ones in @limit.
> > > + */
> > > +int arm64_check_features(u32 sys_reg, u64 val, u64 limit)
> > > +{
> > > +       const struct arm64_ftr_bits *ftrp = get_arm64_ftr_bits_kvm(sys_reg);
> > > +       u64 exposed_mask = 0;
> > > +
> > > +       if (!ftrp)
> > > +               return -ENOENT;
> > > +
> > > +       for (; ftrp->width; ftrp++) {
> > > +               s64 ftr_val = arm64_ftr_value(ftrp, val);
> > > +               s64 ftr_lim = arm64_ftr_value(ftrp, limit);
> > > +
> > > +               exposed_mask |= arm64_ftr_mask(ftrp);
> > > +
> > > +               if (ftr_val == ftr_lim)
> > > +                       continue;
> >
> > At first I thought that this check isn't necessary, it should be
> > covered by the check below (arm64_ftr_safe_value. However, I think
> > that it's needed for the FTR_HIGHER_OR_ZERO_SAFE case. If my
> > understanding is correct, it might be worth adding a comment, or even
> > encapsulating this logic in a arm64_is_safe_value() function for
> > clarity.
>
> In my understanding, arm64_ftr_safe_value() provides a safe value
> when two values are different, and I think there is nothing special
> about the usage of this function (This is actually how the function
> is used by update_cpu_ftr_reg()).
> Without the check, it won't work for FTR_EXACT, but there might be
> more in the future.
>
> Perhaps it might be more straightforward to add the equality check
> into arm64_ftr_safe_value() ?

I don't think this would work for all callers of
arm64_ftr_safe_value(). The thing is arm64_ftr_safe_value() doesn't
check whether the value is safe, but it returns the safe value that
supports the highest feature. Whereas arm64_check_features() on the
other hand is trying to determine whether a value is safe.

If you move the equality check there it would work for
arm64_check_features(), but I am not convinced it wouldn't change the
behavior for init_cpu_ftr_reg() in the case of FTR_EXACT, unless this
never applies to override->val. What do you think?

Thanks,
/fuad


> >
> > > +
> > > +               if (ftr_val != arm64_ftr_safe_value(ftrp, ftr_val, ftr_lim))
> > > +                       return -E2BIG;
> > > +       }
> > > +
> > > +       /* Make sure that no unrecognized fields are set in @val. */
> > > +       if (val & ~exposed_mask)
> > > +               return -E2BIG;
> > > +
> > > +       return 0;
> > > +}
>
> Thanks,
> Reiji

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 03/26] KVM: arm64: Introduce struct id_reg_info
  2022-01-26  6:46       ` Reiji Watanabe
  (?)
@ 2022-02-01 14:13         ` Fuad Tabba
  -1 siblings, 0 replies; 201+ messages in thread
From: Fuad Tabba @ 2022-02-01 14:13 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, Linux ARM

Hi Reiji,

...


> > > @@ -2862,11 +3077,12 @@ void set_default_id_regs(struct kvm *kvm)
> > >         u32 id;
> > >         const struct sys_reg_desc *rd;
> > >         u64 val;
> > > +       struct id_reg_info *idr;
> > >
> > >         for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
> > >                 rd = &sys_reg_descs[i];
> > >                 if (rd->access != access_id_reg)
> > > -                       /* Not ID register, or hidden/reserved ID register */
> > > +                       /* Not ID register or hidden/reserved ID register */
> > >                         continue;
> > >
> > >                 id = reg_to_encoding(rd);
> > > @@ -2874,7 +3090,8 @@ void set_default_id_regs(struct kvm *kvm)
> > >                         /* Shouldn't happen */
> > >                         continue;
> > >
> > > -               val = read_sanitised_ftr_reg(id);
> > > -               kvm->arch.id_regs[IDREG_IDX(id)] = val;
> > > +               idr = GET_ID_REG_INFO(id);
> > > +               val = idr ? idr->vcpu_limit_val : read_sanitised_ftr_reg(id);
> > > +               (void)write_kvm_id_reg(kvm, id, val);
> >
> > Rather than ignoring the return value of write_kvm_id_reg(), wouldn't
> > it be better if set_default_id_regs were to propagate it back to
> > kvm_arch_init_vm in case there's a problem?
>
> Since write_kvm_id_reg() should never return an error for this
> case, returning an error to kvm_arch_init_vm() adds a practically
> unnecessary error handling, which I would like to avoid.
> So, how about putting WARN_ON_ONCE on its return value ?

I think this makes sense in this case.

Thanks,
/fuad

> Thanks,
> Reiji

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 03/26] KVM: arm64: Introduce struct id_reg_info
@ 2022-02-01 14:13         ` Fuad Tabba
  0 siblings, 0 replies; 201+ messages in thread
From: Fuad Tabba @ 2022-02-01 14:13 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: kvm, Marc Zyngier, Peter Shier, Paolo Bonzini, Will Deacon,
	kvmarm, Linux ARM

Hi Reiji,

...


> > > @@ -2862,11 +3077,12 @@ void set_default_id_regs(struct kvm *kvm)
> > >         u32 id;
> > >         const struct sys_reg_desc *rd;
> > >         u64 val;
> > > +       struct id_reg_info *idr;
> > >
> > >         for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
> > >                 rd = &sys_reg_descs[i];
> > >                 if (rd->access != access_id_reg)
> > > -                       /* Not ID register, or hidden/reserved ID register */
> > > +                       /* Not ID register or hidden/reserved ID register */
> > >                         continue;
> > >
> > >                 id = reg_to_encoding(rd);
> > > @@ -2874,7 +3090,8 @@ void set_default_id_regs(struct kvm *kvm)
> > >                         /* Shouldn't happen */
> > >                         continue;
> > >
> > > -               val = read_sanitised_ftr_reg(id);
> > > -               kvm->arch.id_regs[IDREG_IDX(id)] = val;
> > > +               idr = GET_ID_REG_INFO(id);
> > > +               val = idr ? idr->vcpu_limit_val : read_sanitised_ftr_reg(id);
> > > +               (void)write_kvm_id_reg(kvm, id, val);
> >
> > Rather than ignoring the return value of write_kvm_id_reg(), wouldn't
> > it be better if set_default_id_regs were to propagate it back to
> > kvm_arch_init_vm in case there's a problem?
>
> Since write_kvm_id_reg() should never return an error for this
> case, returning an error to kvm_arch_init_vm() adds a practically
> unnecessary error handling, which I would like to avoid.
> So, how about putting WARN_ON_ONCE on its return value ?

I think this makes sense in this case.

Thanks,
/fuad

> Thanks,
> Reiji
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 03/26] KVM: arm64: Introduce struct id_reg_info
@ 2022-02-01 14:13         ` Fuad Tabba
  0 siblings, 0 replies; 201+ messages in thread
From: Fuad Tabba @ 2022-02-01 14:13 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, Linux ARM

Hi Reiji,

...


> > > @@ -2862,11 +3077,12 @@ void set_default_id_regs(struct kvm *kvm)
> > >         u32 id;
> > >         const struct sys_reg_desc *rd;
> > >         u64 val;
> > > +       struct id_reg_info *idr;
> > >
> > >         for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
> > >                 rd = &sys_reg_descs[i];
> > >                 if (rd->access != access_id_reg)
> > > -                       /* Not ID register, or hidden/reserved ID register */
> > > +                       /* Not ID register or hidden/reserved ID register */
> > >                         continue;
> > >
> > >                 id = reg_to_encoding(rd);
> > > @@ -2874,7 +3090,8 @@ void set_default_id_regs(struct kvm *kvm)
> > >                         /* Shouldn't happen */
> > >                         continue;
> > >
> > > -               val = read_sanitised_ftr_reg(id);
> > > -               kvm->arch.id_regs[IDREG_IDX(id)] = val;
> > > +               idr = GET_ID_REG_INFO(id);
> > > +               val = idr ? idr->vcpu_limit_val : read_sanitised_ftr_reg(id);
> > > +               (void)write_kvm_id_reg(kvm, id, val);
> >
> > Rather than ignoring the return value of write_kvm_id_reg(), wouldn't
> > it be better if set_default_id_regs were to propagate it back to
> > kvm_arch_init_vm in case there's a problem?
>
> Since write_kvm_id_reg() should never return an error for this
> case, returning an error to kvm_arch_init_vm() adds a practically
> unnecessary error handling, which I would like to avoid.
> So, how about putting WARN_ON_ONCE on its return value ?

I think this makes sense in this case.

Thanks,
/fuad

> Thanks,
> Reiji

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 04/26] KVM: arm64: Make ID_AA64PFR0_EL1 writable
  2022-01-27  4:01       ` Reiji Watanabe
  (?)
@ 2022-02-01 14:14         ` Fuad Tabba
  -1 siblings, 0 replies; 201+ messages in thread
From: Fuad Tabba @ 2022-02-01 14:14 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, Linux ARM

Hi Reiji,

...

> > > diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c
> > > index 0a06d0648970..28d9bf0e178c 100644
> > > --- a/arch/arm64/kvm/vgic/vgic-init.c
> > > +++ b/arch/arm64/kvm/vgic/vgic-init.c
> > > @@ -116,6 +116,11 @@ int kvm_vgic_create(struct kvm *kvm, u32 type)
> > >         else
> > >                 INIT_LIST_HEAD(&kvm->arch.vgic.rd_regions);
> > >
> > > +       if (type == KVM_DEV_TYPE_ARM_VGIC_V3)
> > > +               /* Set ID_AA64PFR0_EL1.GIC to 1 */
> > > +               (void)kvm_set_id_reg_feature(kvm, SYS_ID_AA64PFR0_EL1,
> > > +                                    ID_AA64PFR0_GIC3, ID_AA64PFR0_GIC_SHIFT);
> > > +
> >
> > If this fails wouldn't it be better to return the error?
>
> This should never fail because kvm_vgic_create() prevents
> userspace from running the first KVM_RUN for any vCPUs
> while it calls kvm_set_id_reg_feature().
> So, I am thinking of adding WARN_ON_ONCE() for the return value
> rather than adding an unnecessary error handling.

Consider this to be a nit from my part, as I don't have any strong
feelings about this, but kvm_vgic_create() already returns an error if
there's a problem. So I don't think that that would be imposing any
additional error handling.

Thanks,
/fuad

> Thanks,
> Reiji

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 04/26] KVM: arm64: Make ID_AA64PFR0_EL1 writable
@ 2022-02-01 14:14         ` Fuad Tabba
  0 siblings, 0 replies; 201+ messages in thread
From: Fuad Tabba @ 2022-02-01 14:14 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: kvm, Marc Zyngier, Peter Shier, Paolo Bonzini, Will Deacon,
	kvmarm, Linux ARM

Hi Reiji,

...

> > > diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c
> > > index 0a06d0648970..28d9bf0e178c 100644
> > > --- a/arch/arm64/kvm/vgic/vgic-init.c
> > > +++ b/arch/arm64/kvm/vgic/vgic-init.c
> > > @@ -116,6 +116,11 @@ int kvm_vgic_create(struct kvm *kvm, u32 type)
> > >         else
> > >                 INIT_LIST_HEAD(&kvm->arch.vgic.rd_regions);
> > >
> > > +       if (type == KVM_DEV_TYPE_ARM_VGIC_V3)
> > > +               /* Set ID_AA64PFR0_EL1.GIC to 1 */
> > > +               (void)kvm_set_id_reg_feature(kvm, SYS_ID_AA64PFR0_EL1,
> > > +                                    ID_AA64PFR0_GIC3, ID_AA64PFR0_GIC_SHIFT);
> > > +
> >
> > If this fails wouldn't it be better to return the error?
>
> This should never fail because kvm_vgic_create() prevents
> userspace from running the first KVM_RUN for any vCPUs
> while it calls kvm_set_id_reg_feature().
> So, I am thinking of adding WARN_ON_ONCE() for the return value
> rather than adding an unnecessary error handling.

Consider this to be a nit from my part, as I don't have any strong
feelings about this, but kvm_vgic_create() already returns an error if
there's a problem. So I don't think that that would be imposing any
additional error handling.

Thanks,
/fuad

> Thanks,
> Reiji
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 04/26] KVM: arm64: Make ID_AA64PFR0_EL1 writable
@ 2022-02-01 14:14         ` Fuad Tabba
  0 siblings, 0 replies; 201+ messages in thread
From: Fuad Tabba @ 2022-02-01 14:14 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, Linux ARM

Hi Reiji,

...

> > > diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c
> > > index 0a06d0648970..28d9bf0e178c 100644
> > > --- a/arch/arm64/kvm/vgic/vgic-init.c
> > > +++ b/arch/arm64/kvm/vgic/vgic-init.c
> > > @@ -116,6 +116,11 @@ int kvm_vgic_create(struct kvm *kvm, u32 type)
> > >         else
> > >                 INIT_LIST_HEAD(&kvm->arch.vgic.rd_regions);
> > >
> > > +       if (type == KVM_DEV_TYPE_ARM_VGIC_V3)
> > > +               /* Set ID_AA64PFR0_EL1.GIC to 1 */
> > > +               (void)kvm_set_id_reg_feature(kvm, SYS_ID_AA64PFR0_EL1,
> > > +                                    ID_AA64PFR0_GIC3, ID_AA64PFR0_GIC_SHIFT);
> > > +
> >
> > If this fails wouldn't it be better to return the error?
>
> This should never fail because kvm_vgic_create() prevents
> userspace from running the first KVM_RUN for any vCPUs
> while it calls kvm_set_id_reg_feature().
> So, I am thinking of adding WARN_ON_ONCE() for the return value
> rather than adding an unnecessary error handling.

Consider this to be a nit from my part, as I don't have any strong
feelings about this, but kvm_vgic_create() already returns an error if
there's a problem. So I don't think that that would be imposing any
additional error handling.

Thanks,
/fuad

> Thanks,
> Reiji

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 20/26] KVM: arm64: Trap disabled features of ID_AA64PFR0_EL1
  2022-01-27  7:19       ` Reiji Watanabe
  (?)
@ 2022-02-01 14:14         ` Fuad Tabba
  -1 siblings, 0 replies; 201+ messages in thread
From: Fuad Tabba @ 2022-02-01 14:14 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, Linux ARM

Hi Reiji,

...

> > > +static void feature_amu_trap_activate(struct kvm_vcpu *vcpu)
> > > +{
> > > +       feature_trap_activate(vcpu, VCPU_CPTR_EL2, CPTR_EL2_TAM, 0);
> >
> > Covers the CPTR flags for AMU, but as you mentioned, does not
> > explicitly clear HCR_AMVOFFEN.
>
> In my understanding, clearing HCR_EL2.AMVOFFEN is not necessary as
> CPTR_EL2.TAM == 1 traps the guest's accessing AMEVCNTR0<n>_EL0 and
> AMEVCNTR1<n>_EL0 anyway (HCR_EL2.AMVOFFEN doesn't matter).
> (Or is my understanding wrong ??)

You're right. However, I think they should be cleared first for
completeness. Also, if I understand correctly, AMVOFFEN is about
enabling and disabling virtualization of the registers, making
indirect reads of the virtual offset registers as zero, so it's not
just about trapping.

Thanks,
/fuad

> Thanks,
> Reiji

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 20/26] KVM: arm64: Trap disabled features of ID_AA64PFR0_EL1
@ 2022-02-01 14:14         ` Fuad Tabba
  0 siblings, 0 replies; 201+ messages in thread
From: Fuad Tabba @ 2022-02-01 14:14 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: kvm, Marc Zyngier, Peter Shier, Paolo Bonzini, Will Deacon,
	kvmarm, Linux ARM

Hi Reiji,

...

> > > +static void feature_amu_trap_activate(struct kvm_vcpu *vcpu)
> > > +{
> > > +       feature_trap_activate(vcpu, VCPU_CPTR_EL2, CPTR_EL2_TAM, 0);
> >
> > Covers the CPTR flags for AMU, but as you mentioned, does not
> > explicitly clear HCR_AMVOFFEN.
>
> In my understanding, clearing HCR_EL2.AMVOFFEN is not necessary as
> CPTR_EL2.TAM == 1 traps the guest's accessing AMEVCNTR0<n>_EL0 and
> AMEVCNTR1<n>_EL0 anyway (HCR_EL2.AMVOFFEN doesn't matter).
> (Or is my understanding wrong ??)

You're right. However, I think they should be cleared first for
completeness. Also, if I understand correctly, AMVOFFEN is about
enabling and disabling virtualization of the registers, making
indirect reads of the virtual offset registers as zero, so it's not
just about trapping.

Thanks,
/fuad

> Thanks,
> Reiji
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 20/26] KVM: arm64: Trap disabled features of ID_AA64PFR0_EL1
@ 2022-02-01 14:14         ` Fuad Tabba
  0 siblings, 0 replies; 201+ messages in thread
From: Fuad Tabba @ 2022-02-01 14:14 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, Linux ARM

Hi Reiji,

...

> > > +static void feature_amu_trap_activate(struct kvm_vcpu *vcpu)
> > > +{
> > > +       feature_trap_activate(vcpu, VCPU_CPTR_EL2, CPTR_EL2_TAM, 0);
> >
> > Covers the CPTR flags for AMU, but as you mentioned, does not
> > explicitly clear HCR_AMVOFFEN.
>
> In my understanding, clearing HCR_EL2.AMVOFFEN is not necessary as
> CPTR_EL2.TAM == 1 traps the guest's accessing AMEVCNTR0<n>_EL0 and
> AMEVCNTR1<n>_EL0 anyway (HCR_EL2.AMVOFFEN doesn't matter).
> (Or is my understanding wrong ??)

You're right. However, I think they should be cleared first for
completeness. Also, if I understand correctly, AMVOFFEN is about
enabling and disabling virtualization of the registers, making
indirect reads of the virtual offset registers as zero, so it's not
just about trapping.

Thanks,
/fuad

> Thanks,
> Reiji

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 02/26] KVM: arm64: Save ID registers' sanitized value per guest
  2022-02-01  6:00               ` Reiji Watanabe
  (?)
@ 2022-02-01 18:38                 ` Ricardo Koller
  -1 siblings, 0 replies; 201+ messages in thread
From: Ricardo Koller @ 2022-02-01 18:38 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: Marc Zyngier, kvmarm, kvm, Linux ARM, James Morse,
	Alexandru Elisei, Suzuki K Poulose, Paolo Bonzini, Will Deacon,
	Andrew Jones, Peng Liang, Peter Shier, Oliver Upton, Jing Zhang,
	Raghavendra Rao Anata

Hey Reiji,

On Mon, Jan 31, 2022 at 10:00:40PM -0800, Reiji Watanabe wrote:
> Hi Ricardo,
> 
> On Sun, Jan 30, 2022 at 7:40 PM Ricardo Koller <ricarkol@google.com> wrote:
> >
> > On Fri, Jan 28, 2022 at 09:52:21PM -0800, Reiji Watanabe wrote:
> > > Hi Ricardo,
> > >
> > > > > > > +
> > > > > > > +/*
> > > > > > > + * Set the guest's ID registers that are defined in sys_reg_descs[]
> > > > > > > + * with ID_SANITISED() to the host's sanitized value.
> > > > > > > + */
> > > > > > > +void set_default_id_regs(struct kvm *kvm)
> > > > > > > +{
> > > > > > > +     int i;
> > > > > > > +     u32 id;
> > > > > > > +     const struct sys_reg_desc *rd;
> > > > > > > +     u64 val;
> > > > > > > +
> > > > > > > +     for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
> > > > > > > +             rd = &sys_reg_descs[i];
> > > > > > > +             if (rd->access != access_id_reg)
> > > > > > > +                     /* Not ID register, or hidden/reserved ID register */
> > > > > > > +                     continue;
> > > > > > > +
> > > > > > > +             id = reg_to_encoding(rd);
> > > > > > > +             if (WARN_ON_ONCE(!is_id_reg(id)))
> > > > > > > +                     /* Shouldn't happen */
> > > > > > > +                     continue;
> > > > > > > +
> > > > > > > +             val = read_sanitised_ftr_reg(id);
> > > > > >
> > > > > > I'm a bit confused. Shouldn't the default+sanitized values already use
> > > > > > arm64_ftr_bits_kvm (instead of arm64_ftr_regs)?
> > > > >
> > > > > I'm not sure if I understand your question.
> > > > > arm64_ftr_bits_kvm is used for feature support checkings when
> > > > > userspace tries to modify a value of ID registers.
> > > > > With this patch, KVM just saves the sanitized values in the kvm's
> > > > > buffer, but userspace is still not allowed to modify values of ID
> > > > > registers yet.
> > > > > I hope it answers your question.
> > > >
> > > > Based on the previous commit I was assuming that some registers, like
> > > > id_aa64dfr0,
> > > > would default to the overwritten values as the sanitized values. More
> > > > specifically: if
> > > > userspace doesn't modify any ID reg, shouldn't the defaults have the
> > > > KVM overwritten
> > > > values (arm64_ftr_bits_kvm)?
> > >
> > > arm64_ftr_bits_kvm doesn't have arm64_ftr_reg but arm64_ftr_bits,
> > > and arm64_ftr_bits_kvm doesn't have the sanitized values.
> > >
> > > Thanks,
> >
> > Hey Reiji,
> >
> > Sorry, I wasn't very clear. This is what I meant.
> >
> > If I set DEBUGVER to 0x5 (w/ FTR_EXACT) using this patch on top of the
> > series:
> >
> >  static struct arm64_ftr_bits ftr_id_aa64dfr0_kvm[MAX_FTR_BITS_LEN] = {
> >         S_ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64DFR0_PMUVER_SHIFT, 4, 0),
> > -       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x6),
> > +       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x5),
> >
> > it means that userspace would not be able to set DEBUGVER to anything
> > but 0x5. But I'm not sure what it should mean for the default KVM value
> > of DEBUGVER, specifically the value calculated in set_default_id_regs().
> > As it is, KVM is still setting the guest-visible value to 0x6, and my
> > "desire" to only allow booting VMs with DEBUGVER=0x5 is being ignored: I
> > booted a VM and the DEBUGVER value from inside is still 0x6. I was
> > expecting it to not boot, or to show a warning.
>
> Thank you for the explanation!
> 
> FTR_EXACT (in the existing code) means that the safe_val should be
> used if values of the field are not identical between CPUs (see how
> update_cpu_ftr_reg() uses arm64_ftr_safe_value()). For KVM usage,
> it means that if the field value for a vCPU is different from the one
> for the host's sanitized value, only the safe_val can be used safely
> for the guest (purely in terms of CPU feature).

Let me double check my understanding using the DEBUGVER example, please.
The safe_value would be DEBUGVER=5, and it contradicts the initial VM
value calculated on the KVM side. Q1: Can a contradiction like this
occur in practice? Q2: If the user saves and restores this id-reg on the
same kernel, the AA64DFR0 userspace write would fail (ftr_val !=
arm64_ftr_safe_value), right?

> 
> If KVM wants to restrict some features due to some reasons (e.g.
> a feature for guests is not supported by the KVM yet), it should
> be done by KVM (not by cpufeature.c), and  'validate' function in
> "struct id_reg_info", which is introduced in patch-3, will be used
> for such cases (the following patches actually use).
> 

Got it, thanks.

> Thanks,
> Reiji
> 

Thanks,
Ricardo

> >
> > I think this has some implications for migrations. It would not be
> > possible to migrate the example VM on the patched kernel from above: you
> > can boot a VM with DEBUGVER=0x5 but you can't migrate it.
> >
> > Thanks,
> > Ricardo

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 02/26] KVM: arm64: Save ID registers' sanitized value per guest
@ 2022-02-01 18:38                 ` Ricardo Koller
  0 siblings, 0 replies; 201+ messages in thread
From: Ricardo Koller @ 2022-02-01 18:38 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: kvm, Marc Zyngier, Peter Shier, Will Deacon, Paolo Bonzini,
	kvmarm, Linux ARM

Hey Reiji,

On Mon, Jan 31, 2022 at 10:00:40PM -0800, Reiji Watanabe wrote:
> Hi Ricardo,
> 
> On Sun, Jan 30, 2022 at 7:40 PM Ricardo Koller <ricarkol@google.com> wrote:
> >
> > On Fri, Jan 28, 2022 at 09:52:21PM -0800, Reiji Watanabe wrote:
> > > Hi Ricardo,
> > >
> > > > > > > +
> > > > > > > +/*
> > > > > > > + * Set the guest's ID registers that are defined in sys_reg_descs[]
> > > > > > > + * with ID_SANITISED() to the host's sanitized value.
> > > > > > > + */
> > > > > > > +void set_default_id_regs(struct kvm *kvm)
> > > > > > > +{
> > > > > > > +     int i;
> > > > > > > +     u32 id;
> > > > > > > +     const struct sys_reg_desc *rd;
> > > > > > > +     u64 val;
> > > > > > > +
> > > > > > > +     for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
> > > > > > > +             rd = &sys_reg_descs[i];
> > > > > > > +             if (rd->access != access_id_reg)
> > > > > > > +                     /* Not ID register, or hidden/reserved ID register */
> > > > > > > +                     continue;
> > > > > > > +
> > > > > > > +             id = reg_to_encoding(rd);
> > > > > > > +             if (WARN_ON_ONCE(!is_id_reg(id)))
> > > > > > > +                     /* Shouldn't happen */
> > > > > > > +                     continue;
> > > > > > > +
> > > > > > > +             val = read_sanitised_ftr_reg(id);
> > > > > >
> > > > > > I'm a bit confused. Shouldn't the default+sanitized values already use
> > > > > > arm64_ftr_bits_kvm (instead of arm64_ftr_regs)?
> > > > >
> > > > > I'm not sure if I understand your question.
> > > > > arm64_ftr_bits_kvm is used for feature support checkings when
> > > > > userspace tries to modify a value of ID registers.
> > > > > With this patch, KVM just saves the sanitized values in the kvm's
> > > > > buffer, but userspace is still not allowed to modify values of ID
> > > > > registers yet.
> > > > > I hope it answers your question.
> > > >
> > > > Based on the previous commit I was assuming that some registers, like
> > > > id_aa64dfr0,
> > > > would default to the overwritten values as the sanitized values. More
> > > > specifically: if
> > > > userspace doesn't modify any ID reg, shouldn't the defaults have the
> > > > KVM overwritten
> > > > values (arm64_ftr_bits_kvm)?
> > >
> > > arm64_ftr_bits_kvm doesn't have arm64_ftr_reg but arm64_ftr_bits,
> > > and arm64_ftr_bits_kvm doesn't have the sanitized values.
> > >
> > > Thanks,
> >
> > Hey Reiji,
> >
> > Sorry, I wasn't very clear. This is what I meant.
> >
> > If I set DEBUGVER to 0x5 (w/ FTR_EXACT) using this patch on top of the
> > series:
> >
> >  static struct arm64_ftr_bits ftr_id_aa64dfr0_kvm[MAX_FTR_BITS_LEN] = {
> >         S_ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64DFR0_PMUVER_SHIFT, 4, 0),
> > -       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x6),
> > +       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x5),
> >
> > it means that userspace would not be able to set DEBUGVER to anything
> > but 0x5. But I'm not sure what it should mean for the default KVM value
> > of DEBUGVER, specifically the value calculated in set_default_id_regs().
> > As it is, KVM is still setting the guest-visible value to 0x6, and my
> > "desire" to only allow booting VMs with DEBUGVER=0x5 is being ignored: I
> > booted a VM and the DEBUGVER value from inside is still 0x6. I was
> > expecting it to not boot, or to show a warning.
>
> Thank you for the explanation!
> 
> FTR_EXACT (in the existing code) means that the safe_val should be
> used if values of the field are not identical between CPUs (see how
> update_cpu_ftr_reg() uses arm64_ftr_safe_value()). For KVM usage,
> it means that if the field value for a vCPU is different from the one
> for the host's sanitized value, only the safe_val can be used safely
> for the guest (purely in terms of CPU feature).

Let me double check my understanding using the DEBUGVER example, please.
The safe_value would be DEBUGVER=5, and it contradicts the initial VM
value calculated on the KVM side. Q1: Can a contradiction like this
occur in practice? Q2: If the user saves and restores this id-reg on the
same kernel, the AA64DFR0 userspace write would fail (ftr_val !=
arm64_ftr_safe_value), right?

> 
> If KVM wants to restrict some features due to some reasons (e.g.
> a feature for guests is not supported by the KVM yet), it should
> be done by KVM (not by cpufeature.c), and  'validate' function in
> "struct id_reg_info", which is introduced in patch-3, will be used
> for such cases (the following patches actually use).
> 

Got it, thanks.

> Thanks,
> Reiji
> 

Thanks,
Ricardo

> >
> > I think this has some implications for migrations. It would not be
> > possible to migrate the example VM on the patched kernel from above: you
> > can boot a VM with DEBUGVER=0x5 but you can't migrate it.
> >
> > Thanks,
> > Ricardo
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 02/26] KVM: arm64: Save ID registers' sanitized value per guest
@ 2022-02-01 18:38                 ` Ricardo Koller
  0 siblings, 0 replies; 201+ messages in thread
From: Ricardo Koller @ 2022-02-01 18:38 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: Marc Zyngier, kvmarm, kvm, Linux ARM, James Morse,
	Alexandru Elisei, Suzuki K Poulose, Paolo Bonzini, Will Deacon,
	Andrew Jones, Peng Liang, Peter Shier, Oliver Upton, Jing Zhang,
	Raghavendra Rao Anata

Hey Reiji,

On Mon, Jan 31, 2022 at 10:00:40PM -0800, Reiji Watanabe wrote:
> Hi Ricardo,
> 
> On Sun, Jan 30, 2022 at 7:40 PM Ricardo Koller <ricarkol@google.com> wrote:
> >
> > On Fri, Jan 28, 2022 at 09:52:21PM -0800, Reiji Watanabe wrote:
> > > Hi Ricardo,
> > >
> > > > > > > +
> > > > > > > +/*
> > > > > > > + * Set the guest's ID registers that are defined in sys_reg_descs[]
> > > > > > > + * with ID_SANITISED() to the host's sanitized value.
> > > > > > > + */
> > > > > > > +void set_default_id_regs(struct kvm *kvm)
> > > > > > > +{
> > > > > > > +     int i;
> > > > > > > +     u32 id;
> > > > > > > +     const struct sys_reg_desc *rd;
> > > > > > > +     u64 val;
> > > > > > > +
> > > > > > > +     for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
> > > > > > > +             rd = &sys_reg_descs[i];
> > > > > > > +             if (rd->access != access_id_reg)
> > > > > > > +                     /* Not ID register, or hidden/reserved ID register */
> > > > > > > +                     continue;
> > > > > > > +
> > > > > > > +             id = reg_to_encoding(rd);
> > > > > > > +             if (WARN_ON_ONCE(!is_id_reg(id)))
> > > > > > > +                     /* Shouldn't happen */
> > > > > > > +                     continue;
> > > > > > > +
> > > > > > > +             val = read_sanitised_ftr_reg(id);
> > > > > >
> > > > > > I'm a bit confused. Shouldn't the default+sanitized values already use
> > > > > > arm64_ftr_bits_kvm (instead of arm64_ftr_regs)?
> > > > >
> > > > > I'm not sure if I understand your question.
> > > > > arm64_ftr_bits_kvm is used for feature support checkings when
> > > > > userspace tries to modify a value of ID registers.
> > > > > With this patch, KVM just saves the sanitized values in the kvm's
> > > > > buffer, but userspace is still not allowed to modify values of ID
> > > > > registers yet.
> > > > > I hope it answers your question.
> > > >
> > > > Based on the previous commit I was assuming that some registers, like
> > > > id_aa64dfr0,
> > > > would default to the overwritten values as the sanitized values. More
> > > > specifically: if
> > > > userspace doesn't modify any ID reg, shouldn't the defaults have the
> > > > KVM overwritten
> > > > values (arm64_ftr_bits_kvm)?
> > >
> > > arm64_ftr_bits_kvm doesn't have arm64_ftr_reg but arm64_ftr_bits,
> > > and arm64_ftr_bits_kvm doesn't have the sanitized values.
> > >
> > > Thanks,
> >
> > Hey Reiji,
> >
> > Sorry, I wasn't very clear. This is what I meant.
> >
> > If I set DEBUGVER to 0x5 (w/ FTR_EXACT) using this patch on top of the
> > series:
> >
> >  static struct arm64_ftr_bits ftr_id_aa64dfr0_kvm[MAX_FTR_BITS_LEN] = {
> >         S_ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64DFR0_PMUVER_SHIFT, 4, 0),
> > -       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x6),
> > +       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x5),
> >
> > it means that userspace would not be able to set DEBUGVER to anything
> > but 0x5. But I'm not sure what it should mean for the default KVM value
> > of DEBUGVER, specifically the value calculated in set_default_id_regs().
> > As it is, KVM is still setting the guest-visible value to 0x6, and my
> > "desire" to only allow booting VMs with DEBUGVER=0x5 is being ignored: I
> > booted a VM and the DEBUGVER value from inside is still 0x6. I was
> > expecting it to not boot, or to show a warning.
>
> Thank you for the explanation!
> 
> FTR_EXACT (in the existing code) means that the safe_val should be
> used if values of the field are not identical between CPUs (see how
> update_cpu_ftr_reg() uses arm64_ftr_safe_value()). For KVM usage,
> it means that if the field value for a vCPU is different from the one
> for the host's sanitized value, only the safe_val can be used safely
> for the guest (purely in terms of CPU feature).

Let me double check my understanding using the DEBUGVER example, please.
The safe_value would be DEBUGVER=5, and it contradicts the initial VM
value calculated on the KVM side. Q1: Can a contradiction like this
occur in practice? Q2: If the user saves and restores this id-reg on the
same kernel, the AA64DFR0 userspace write would fail (ftr_val !=
arm64_ftr_safe_value), right?

> 
> If KVM wants to restrict some features due to some reasons (e.g.
> a feature for guests is not supported by the KVM yet), it should
> be done by KVM (not by cpufeature.c), and  'validate' function in
> "struct id_reg_info", which is introduced in patch-3, will be used
> for such cases (the following patches actually use).
> 

Got it, thanks.

> Thanks,
> Reiji
> 

Thanks,
Ricardo

> >
> > I think this has some implications for migrations. It would not be
> > possible to migrate the example VM on the patched kernel from above: you
> > can boot a VM with DEBUGVER=0x5 but you can't migrate it.
> >
> > Thanks,
> > Ricardo

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 01/26] KVM: arm64: Introduce a validation function for an ID register
  2022-02-01 14:13         ` Fuad Tabba
  (?)
@ 2022-02-02  6:46           ` Reiji Watanabe
  -1 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-02-02  6:46 UTC (permalink / raw)
  To: Fuad Tabba
  Cc: kvm, Marc Zyngier, Peter Shier, Paolo Bonzini, Will Deacon,
	kvmarm, Linux ARM

Hi Fuad,

On Tue, Feb 1, 2022 at 6:14 AM Fuad Tabba <tabba@google.com> wrote:
>
> Hi Reiji,
>
> ...
>
> > > Could you please explain using ftr_temp[] and changing the value in
> > > arm64_ftr_bits_kvm_override, rather than just
> > > arm64_ftr_reg_bits_overrite(bits->ftr_bits, o_bits->ftr_bits)?
> >
> > I would like to maintain the order of the entries in the original
> > ftr_bits so that (future) functions that work for the original ones
> > also work for the KVM's.
> > The copy and override is an easy way to do that.  The same thing can
> > be done without ftr_temp[], but it would look a bit tricky.
> >
> > If we assume the order shouldn't matter or entries in ftr_bits
> > are always in descending order, just changing the value in
> > arm64_ftr_bits_kvm_override would be a much simpler way though.
>
> Could you please add a comment in that case? I did find it to be
> confusing until I read your explanation here.

Yes, I will add a comment for it.

>
> >
> > >
> > >
> > > > +static const struct arm64_ftr_bits *get_arm64_ftr_bits_kvm(u32 sys_id)
> > > > +{
> > > > +       const struct __ftr_reg_bits_entry *ret;
> > > > +       int err;
> > > > +
> > > > +       if (!arm64_ftr_bits_kvm) {
> > > > +               /* arm64_ftr_bits_kvm is not initialized yet. */
> > > > +               err = init_arm64_ftr_bits_kvm();
> > >
> > > Rather than doing this check, can we just initialize it earlier, maybe
> > > (indirectly) via kvm_arch_init_vm() or around the same time?
> >
> > Thank you for the comment.
> > I will consider when it should be initialized.
> > ( perhaps even earlier than kvm_arch_init_vm())
> >
> > >
> > >
> > > > +               if (err)
> > > > +                       return NULL;
> > > > +       }
> > > > +
> > > > +       ret = bsearch((const void *)(unsigned long)sys_id,
> > > > +                     arm64_ftr_bits_kvm,
> > > > +                     arm64_ftr_bits_kvm_nentries,
> > > > +                     sizeof(arm64_ftr_bits_kvm[0]),
> > > > +                     search_cmp_ftr_reg_bits);
> > > > +       if (ret)
> > > > +               return ret->ftr_bits;
> > > > +
> > > > +       return NULL;
> > > > +}
> > > > +
> > > > +/*
> > > > + * Check if features (or levels of features) that are indicated in the ID
> > > > + * register value @val are also indicated in @limit.
> > > > + * This function is for KVM to check if features that are indicated in @val,
> > > > + * which will be used as the ID register value for its guest, are supported
> > > > + * on the host.
> > > > + * For AA64MMFR0_EL1.TGranX_2 fields, which don't follow the standard ID
> > > > + * scheme, the function checks if values of the fields in @val are the same
> > > > + * as the ones in @limit.
> > > > + */
> > > > +int arm64_check_features(u32 sys_reg, u64 val, u64 limit)
> > > > +{
> > > > +       const struct arm64_ftr_bits *ftrp = get_arm64_ftr_bits_kvm(sys_reg);
> > > > +       u64 exposed_mask = 0;
> > > > +
> > > > +       if (!ftrp)
> > > > +               return -ENOENT;
> > > > +
> > > > +       for (; ftrp->width; ftrp++) {
> > > > +               s64 ftr_val = arm64_ftr_value(ftrp, val);
> > > > +               s64 ftr_lim = arm64_ftr_value(ftrp, limit);
> > > > +
> > > > +               exposed_mask |= arm64_ftr_mask(ftrp);
> > > > +
> > > > +               if (ftr_val == ftr_lim)
> > > > +                       continue;
> > >
> > > At first I thought that this check isn't necessary, it should be
> > > covered by the check below (arm64_ftr_safe_value. However, I think
> > > that it's needed for the FTR_HIGHER_OR_ZERO_SAFE case. If my
> > > understanding is correct, it might be worth adding a comment, or even
> > > encapsulating this logic in a arm64_is_safe_value() function for
> > > clarity.
> >
> > In my understanding, arm64_ftr_safe_value() provides a safe value
> > when two values are different, and I think there is nothing special
> > about the usage of this function (This is actually how the function
> > is used by update_cpu_ftr_reg()).
> > Without the check, it won't work for FTR_EXACT, but there might be
> > more in the future.
> >
> > Perhaps it might be more straightforward to add the equality check
> > into arm64_ftr_safe_value() ?
>
> I don't think this would work for all callers of
> arm64_ftr_safe_value(). The thing is arm64_ftr_safe_value() doesn't
> check whether the value is safe, but it returns the safe value that
> supports the highest feature. Whereas arm64_check_features() on the
> other hand is trying to determine whether a value is safe.
>
> If you move the equality check there it would work for
> arm64_check_features(), but I am not convinced it wouldn't change the
> behavior for init_cpu_ftr_reg() in the case of FTR_EXACT, unless this
> never applies to override->val. What do you think?

The equality check (simply returns the new value if new == cur) could
change a return value of arm64_ftr_safe_value only if ftr_ovr == ftr_new
for FTR_EXACT case.  For init_cpu_ftr_reg, since ftr_ovr value doesn't
matter if ftr_ovr == ftr_new, I would think the override behavior itself
stays the same although the message that will be printed by
init_cpu_ftr_reg() will change ("ignoring override" => "already set").

Having said that, since the change (having arm64_ftr_safe_value does
the equality check) isn't necessary, either way is fine, and
I can keep the current implementation of arm64_ftr_safe_value().

Thanks,
Reiji


>
> Thanks,
> /fuad
>
>
> > >
> > > > +
> > > > +               if (ftr_val != arm64_ftr_safe_value(ftrp, ftr_val, ftr_lim))
> > > > +                       return -E2BIG;
> > > > +       }
> > > > +
> > > > +       /* Make sure that no unrecognized fields are set in @val. */
> > > > +       if (val & ~exposed_mask)
> > > > +               return -E2BIG;
> > > > +
> > > > +       return 0;
> > > > +}
> >
> > Thanks,
> > Reiji
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 01/26] KVM: arm64: Introduce a validation function for an ID register
@ 2022-02-02  6:46           ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-02-02  6:46 UTC (permalink / raw)
  To: Fuad Tabba
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, Linux ARM

Hi Fuad,

On Tue, Feb 1, 2022 at 6:14 AM Fuad Tabba <tabba@google.com> wrote:
>
> Hi Reiji,
>
> ...
>
> > > Could you please explain using ftr_temp[] and changing the value in
> > > arm64_ftr_bits_kvm_override, rather than just
> > > arm64_ftr_reg_bits_overrite(bits->ftr_bits, o_bits->ftr_bits)?
> >
> > I would like to maintain the order of the entries in the original
> > ftr_bits so that (future) functions that work for the original ones
> > also work for the KVM's.
> > The copy and override is an easy way to do that.  The same thing can
> > be done without ftr_temp[], but it would look a bit tricky.
> >
> > If we assume the order shouldn't matter or entries in ftr_bits
> > are always in descending order, just changing the value in
> > arm64_ftr_bits_kvm_override would be a much simpler way though.
>
> Could you please add a comment in that case? I did find it to be
> confusing until I read your explanation here.

Yes, I will add a comment for it.

>
> >
> > >
> > >
> > > > +static const struct arm64_ftr_bits *get_arm64_ftr_bits_kvm(u32 sys_id)
> > > > +{
> > > > +       const struct __ftr_reg_bits_entry *ret;
> > > > +       int err;
> > > > +
> > > > +       if (!arm64_ftr_bits_kvm) {
> > > > +               /* arm64_ftr_bits_kvm is not initialized yet. */
> > > > +               err = init_arm64_ftr_bits_kvm();
> > >
> > > Rather than doing this check, can we just initialize it earlier, maybe
> > > (indirectly) via kvm_arch_init_vm() or around the same time?
> >
> > Thank you for the comment.
> > I will consider when it should be initialized.
> > ( perhaps even earlier than kvm_arch_init_vm())
> >
> > >
> > >
> > > > +               if (err)
> > > > +                       return NULL;
> > > > +       }
> > > > +
> > > > +       ret = bsearch((const void *)(unsigned long)sys_id,
> > > > +                     arm64_ftr_bits_kvm,
> > > > +                     arm64_ftr_bits_kvm_nentries,
> > > > +                     sizeof(arm64_ftr_bits_kvm[0]),
> > > > +                     search_cmp_ftr_reg_bits);
> > > > +       if (ret)
> > > > +               return ret->ftr_bits;
> > > > +
> > > > +       return NULL;
> > > > +}
> > > > +
> > > > +/*
> > > > + * Check if features (or levels of features) that are indicated in the ID
> > > > + * register value @val are also indicated in @limit.
> > > > + * This function is for KVM to check if features that are indicated in @val,
> > > > + * which will be used as the ID register value for its guest, are supported
> > > > + * on the host.
> > > > + * For AA64MMFR0_EL1.TGranX_2 fields, which don't follow the standard ID
> > > > + * scheme, the function checks if values of the fields in @val are the same
> > > > + * as the ones in @limit.
> > > > + */
> > > > +int arm64_check_features(u32 sys_reg, u64 val, u64 limit)
> > > > +{
> > > > +       const struct arm64_ftr_bits *ftrp = get_arm64_ftr_bits_kvm(sys_reg);
> > > > +       u64 exposed_mask = 0;
> > > > +
> > > > +       if (!ftrp)
> > > > +               return -ENOENT;
> > > > +
> > > > +       for (; ftrp->width; ftrp++) {
> > > > +               s64 ftr_val = arm64_ftr_value(ftrp, val);
> > > > +               s64 ftr_lim = arm64_ftr_value(ftrp, limit);
> > > > +
> > > > +               exposed_mask |= arm64_ftr_mask(ftrp);
> > > > +
> > > > +               if (ftr_val == ftr_lim)
> > > > +                       continue;
> > >
> > > At first I thought that this check isn't necessary, it should be
> > > covered by the check below (arm64_ftr_safe_value. However, I think
> > > that it's needed for the FTR_HIGHER_OR_ZERO_SAFE case. If my
> > > understanding is correct, it might be worth adding a comment, or even
> > > encapsulating this logic in a arm64_is_safe_value() function for
> > > clarity.
> >
> > In my understanding, arm64_ftr_safe_value() provides a safe value
> > when two values are different, and I think there is nothing special
> > about the usage of this function (This is actually how the function
> > is used by update_cpu_ftr_reg()).
> > Without the check, it won't work for FTR_EXACT, but there might be
> > more in the future.
> >
> > Perhaps it might be more straightforward to add the equality check
> > into arm64_ftr_safe_value() ?
>
> I don't think this would work for all callers of
> arm64_ftr_safe_value(). The thing is arm64_ftr_safe_value() doesn't
> check whether the value is safe, but it returns the safe value that
> supports the highest feature. Whereas arm64_check_features() on the
> other hand is trying to determine whether a value is safe.
>
> If you move the equality check there it would work for
> arm64_check_features(), but I am not convinced it wouldn't change the
> behavior for init_cpu_ftr_reg() in the case of FTR_EXACT, unless this
> never applies to override->val. What do you think?

The equality check (simply returns the new value if new == cur) could
change a return value of arm64_ftr_safe_value only if ftr_ovr == ftr_new
for FTR_EXACT case.  For init_cpu_ftr_reg, since ftr_ovr value doesn't
matter if ftr_ovr == ftr_new, I would think the override behavior itself
stays the same although the message that will be printed by
init_cpu_ftr_reg() will change ("ignoring override" => "already set").

Having said that, since the change (having arm64_ftr_safe_value does
the equality check) isn't necessary, either way is fine, and
I can keep the current implementation of arm64_ftr_safe_value().

Thanks,
Reiji


>
> Thanks,
> /fuad
>
>
> > >
> > > > +
> > > > +               if (ftr_val != arm64_ftr_safe_value(ftrp, ftr_val, ftr_lim))
> > > > +                       return -E2BIG;
> > > > +       }
> > > > +
> > > > +       /* Make sure that no unrecognized fields are set in @val. */
> > > > +       if (val & ~exposed_mask)
> > > > +               return -E2BIG;
> > > > +
> > > > +       return 0;
> > > > +}
> >
> > Thanks,
> > Reiji

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 01/26] KVM: arm64: Introduce a validation function for an ID register
@ 2022-02-02  6:46           ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-02-02  6:46 UTC (permalink / raw)
  To: Fuad Tabba
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, Linux ARM

Hi Fuad,

On Tue, Feb 1, 2022 at 6:14 AM Fuad Tabba <tabba@google.com> wrote:
>
> Hi Reiji,
>
> ...
>
> > > Could you please explain using ftr_temp[] and changing the value in
> > > arm64_ftr_bits_kvm_override, rather than just
> > > arm64_ftr_reg_bits_overrite(bits->ftr_bits, o_bits->ftr_bits)?
> >
> > I would like to maintain the order of the entries in the original
> > ftr_bits so that (future) functions that work for the original ones
> > also work for the KVM's.
> > The copy and override is an easy way to do that.  The same thing can
> > be done without ftr_temp[], but it would look a bit tricky.
> >
> > If we assume the order shouldn't matter or entries in ftr_bits
> > are always in descending order, just changing the value in
> > arm64_ftr_bits_kvm_override would be a much simpler way though.
>
> Could you please add a comment in that case? I did find it to be
> confusing until I read your explanation here.

Yes, I will add a comment for it.

>
> >
> > >
> > >
> > > > +static const struct arm64_ftr_bits *get_arm64_ftr_bits_kvm(u32 sys_id)
> > > > +{
> > > > +       const struct __ftr_reg_bits_entry *ret;
> > > > +       int err;
> > > > +
> > > > +       if (!arm64_ftr_bits_kvm) {
> > > > +               /* arm64_ftr_bits_kvm is not initialized yet. */
> > > > +               err = init_arm64_ftr_bits_kvm();
> > >
> > > Rather than doing this check, can we just initialize it earlier, maybe
> > > (indirectly) via kvm_arch_init_vm() or around the same time?
> >
> > Thank you for the comment.
> > I will consider when it should be initialized.
> > ( perhaps even earlier than kvm_arch_init_vm())
> >
> > >
> > >
> > > > +               if (err)
> > > > +                       return NULL;
> > > > +       }
> > > > +
> > > > +       ret = bsearch((const void *)(unsigned long)sys_id,
> > > > +                     arm64_ftr_bits_kvm,
> > > > +                     arm64_ftr_bits_kvm_nentries,
> > > > +                     sizeof(arm64_ftr_bits_kvm[0]),
> > > > +                     search_cmp_ftr_reg_bits);
> > > > +       if (ret)
> > > > +               return ret->ftr_bits;
> > > > +
> > > > +       return NULL;
> > > > +}
> > > > +
> > > > +/*
> > > > + * Check if features (or levels of features) that are indicated in the ID
> > > > + * register value @val are also indicated in @limit.
> > > > + * This function is for KVM to check if features that are indicated in @val,
> > > > + * which will be used as the ID register value for its guest, are supported
> > > > + * on the host.
> > > > + * For AA64MMFR0_EL1.TGranX_2 fields, which don't follow the standard ID
> > > > + * scheme, the function checks if values of the fields in @val are the same
> > > > + * as the ones in @limit.
> > > > + */
> > > > +int arm64_check_features(u32 sys_reg, u64 val, u64 limit)
> > > > +{
> > > > +       const struct arm64_ftr_bits *ftrp = get_arm64_ftr_bits_kvm(sys_reg);
> > > > +       u64 exposed_mask = 0;
> > > > +
> > > > +       if (!ftrp)
> > > > +               return -ENOENT;
> > > > +
> > > > +       for (; ftrp->width; ftrp++) {
> > > > +               s64 ftr_val = arm64_ftr_value(ftrp, val);
> > > > +               s64 ftr_lim = arm64_ftr_value(ftrp, limit);
> > > > +
> > > > +               exposed_mask |= arm64_ftr_mask(ftrp);
> > > > +
> > > > +               if (ftr_val == ftr_lim)
> > > > +                       continue;
> > >
> > > At first I thought that this check isn't necessary, it should be
> > > covered by the check below (arm64_ftr_safe_value. However, I think
> > > that it's needed for the FTR_HIGHER_OR_ZERO_SAFE case. If my
> > > understanding is correct, it might be worth adding a comment, or even
> > > encapsulating this logic in a arm64_is_safe_value() function for
> > > clarity.
> >
> > In my understanding, arm64_ftr_safe_value() provides a safe value
> > when two values are different, and I think there is nothing special
> > about the usage of this function (This is actually how the function
> > is used by update_cpu_ftr_reg()).
> > Without the check, it won't work for FTR_EXACT, but there might be
> > more in the future.
> >
> > Perhaps it might be more straightforward to add the equality check
> > into arm64_ftr_safe_value() ?
>
> I don't think this would work for all callers of
> arm64_ftr_safe_value(). The thing is arm64_ftr_safe_value() doesn't
> check whether the value is safe, but it returns the safe value that
> supports the highest feature. Whereas arm64_check_features() on the
> other hand is trying to determine whether a value is safe.
>
> If you move the equality check there it would work for
> arm64_check_features(), but I am not convinced it wouldn't change the
> behavior for init_cpu_ftr_reg() in the case of FTR_EXACT, unless this
> never applies to override->val. What do you think?

The equality check (simply returns the new value if new == cur) could
change a return value of arm64_ftr_safe_value only if ftr_ovr == ftr_new
for FTR_EXACT case.  For init_cpu_ftr_reg, since ftr_ovr value doesn't
matter if ftr_ovr == ftr_new, I would think the override behavior itself
stays the same although the message that will be printed by
init_cpu_ftr_reg() will change ("ignoring override" => "already set").

Having said that, since the change (having arm64_ftr_safe_value does
the equality check) isn't necessary, either way is fine, and
I can keep the current implementation of arm64_ftr_safe_value().

Thanks,
Reiji


>
> Thanks,
> /fuad
>
>
> > >
> > > > +
> > > > +               if (ftr_val != arm64_ftr_safe_value(ftrp, ftr_val, ftr_lim))
> > > > +                       return -E2BIG;
> > > > +       }
> > > > +
> > > > +       /* Make sure that no unrecognized fields are set in @val. */
> > > > +       if (val & ~exposed_mask)
> > > > +               return -E2BIG;
> > > > +
> > > > +       return 0;
> > > > +}
> >
> > Thanks,
> > Reiji

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 02/26] KVM: arm64: Save ID registers' sanitized value per guest
  2022-02-01 18:38                 ` Ricardo Koller
  (?)
@ 2022-02-03  6:31                   ` Reiji Watanabe
  -1 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-02-03  6:31 UTC (permalink / raw)
  To: Ricardo Koller
  Cc: Marc Zyngier, kvmarm, kvm, Linux ARM, James Morse,
	Alexandru Elisei, Suzuki K Poulose, Paolo Bonzini, Will Deacon,
	Andrew Jones, Peng Liang, Peter Shier, Oliver Upton, Jing Zhang,
	Raghavendra Rao Anata

Hi Ricardo,

On Tue, Feb 1, 2022 at 10:39 AM Ricardo Koller <ricarkol@google.com> wrote:
>
> Hey Reiji,
>
> On Mon, Jan 31, 2022 at 10:00:40PM -0800, Reiji Watanabe wrote:
> > Hi Ricardo,
> >
> > On Sun, Jan 30, 2022 at 7:40 PM Ricardo Koller <ricarkol@google.com> wrote:
> > >
> > > On Fri, Jan 28, 2022 at 09:52:21PM -0800, Reiji Watanabe wrote:
> > > > Hi Ricardo,
> > > >
> > > > > > > > +
> > > > > > > > +/*
> > > > > > > > + * Set the guest's ID registers that are defined in sys_reg_descs[]
> > > > > > > > + * with ID_SANITISED() to the host's sanitized value.
> > > > > > > > + */
> > > > > > > > +void set_default_id_regs(struct kvm *kvm)
> > > > > > > > +{
> > > > > > > > +     int i;
> > > > > > > > +     u32 id;
> > > > > > > > +     const struct sys_reg_desc *rd;
> > > > > > > > +     u64 val;
> > > > > > > > +
> > > > > > > > +     for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
> > > > > > > > +             rd = &sys_reg_descs[i];
> > > > > > > > +             if (rd->access != access_id_reg)
> > > > > > > > +                     /* Not ID register, or hidden/reserved ID register */
> > > > > > > > +                     continue;
> > > > > > > > +
> > > > > > > > +             id = reg_to_encoding(rd);
> > > > > > > > +             if (WARN_ON_ONCE(!is_id_reg(id)))
> > > > > > > > +                     /* Shouldn't happen */
> > > > > > > > +                     continue;
> > > > > > > > +
> > > > > > > > +             val = read_sanitised_ftr_reg(id);
> > > > > > >
> > > > > > > I'm a bit confused. Shouldn't the default+sanitized values already use
> > > > > > > arm64_ftr_bits_kvm (instead of arm64_ftr_regs)?
> > > > > >
> > > > > > I'm not sure if I understand your question.
> > > > > > arm64_ftr_bits_kvm is used for feature support checkings when
> > > > > > userspace tries to modify a value of ID registers.
> > > > > > With this patch, KVM just saves the sanitized values in the kvm's
> > > > > > buffer, but userspace is still not allowed to modify values of ID
> > > > > > registers yet.
> > > > > > I hope it answers your question.
> > > > >
> > > > > Based on the previous commit I was assuming that some registers, like
> > > > > id_aa64dfr0,
> > > > > would default to the overwritten values as the sanitized values. More
> > > > > specifically: if
> > > > > userspace doesn't modify any ID reg, shouldn't the defaults have the
> > > > > KVM overwritten
> > > > > values (arm64_ftr_bits_kvm)?
> > > >
> > > > arm64_ftr_bits_kvm doesn't have arm64_ftr_reg but arm64_ftr_bits,
> > > > and arm64_ftr_bits_kvm doesn't have the sanitized values.
> > > >
> > > > Thanks,
> > >
> > > Hey Reiji,
> > >
> > > Sorry, I wasn't very clear. This is what I meant.
> > >
> > > If I set DEBUGVER to 0x5 (w/ FTR_EXACT) using this patch on top of the
> > > series:
> > >
> > >  static struct arm64_ftr_bits ftr_id_aa64dfr0_kvm[MAX_FTR_BITS_LEN] = {
> > >         S_ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64DFR0_PMUVER_SHIFT, 4, 0),
> > > -       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x6),
> > > +       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x5),
> > >
> > > it means that userspace would not be able to set DEBUGVER to anything
> > > but 0x5. But I'm not sure what it should mean for the default KVM value
> > > of DEBUGVER, specifically the value calculated in set_default_id_regs().
> > > As it is, KVM is still setting the guest-visible value to 0x6, and my
> > > "desire" to only allow booting VMs with DEBUGVER=0x5 is being ignored: I
> > > booted a VM and the DEBUGVER value from inside is still 0x6. I was
> > > expecting it to not boot, or to show a warning.
> >
> > Thank you for the explanation!
> >
> > FTR_EXACT (in the existing code) means that the safe_val should be
> > used if values of the field are not identical between CPUs (see how
> > update_cpu_ftr_reg() uses arm64_ftr_safe_value()). For KVM usage,
> > it means that if the field value for a vCPU is different from the one
> > for the host's sanitized value, only the safe_val can be used safely
> > for the guest (purely in terms of CPU feature).
>
> Let me double check my understanding using the DEBUGVER example, please.
> The safe_value would be DEBUGVER=5, and it contradicts the initial VM
> value calculated on the KVM side. Q1: Can a contradiction like this
> occur in practice? Q2: If the user saves and restores this id-reg on the
> same kernel, the AA64DFR0 userspace write would fail (ftr_val !=
> arm64_ftr_safe_value), right?

Thank you for the comment!

For Q1, yes, we might possibly create a bug that makes a contradiction
between KVM and cpufeature.c.
For Q2, even with such a contradiction, userspace will still be able to
save and restore the id reg on the same kernel on the same system in most
cases because @limit that KVM will specify for arm64_check_features()
will mostly be the same as the initial value for the guest (except for
fields corresponding to opt-in CPU features, which are configured with
KVM_ARM_VCPU_INIT or etc) and arm64_check_features does an equality check
per field.  Having said that, as you suggested, it might be better to run
arm64_check_features for the initial value against the host value so we
can catch such a bug. I'll look into doing that in v5.

Thanks,
Reiji

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 02/26] KVM: arm64: Save ID registers' sanitized value per guest
@ 2022-02-03  6:31                   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-02-03  6:31 UTC (permalink / raw)
  To: Ricardo Koller
  Cc: kvm, Marc Zyngier, Peter Shier, Will Deacon, Paolo Bonzini,
	kvmarm, Linux ARM

Hi Ricardo,

On Tue, Feb 1, 2022 at 10:39 AM Ricardo Koller <ricarkol@google.com> wrote:
>
> Hey Reiji,
>
> On Mon, Jan 31, 2022 at 10:00:40PM -0800, Reiji Watanabe wrote:
> > Hi Ricardo,
> >
> > On Sun, Jan 30, 2022 at 7:40 PM Ricardo Koller <ricarkol@google.com> wrote:
> > >
> > > On Fri, Jan 28, 2022 at 09:52:21PM -0800, Reiji Watanabe wrote:
> > > > Hi Ricardo,
> > > >
> > > > > > > > +
> > > > > > > > +/*
> > > > > > > > + * Set the guest's ID registers that are defined in sys_reg_descs[]
> > > > > > > > + * with ID_SANITISED() to the host's sanitized value.
> > > > > > > > + */
> > > > > > > > +void set_default_id_regs(struct kvm *kvm)
> > > > > > > > +{
> > > > > > > > +     int i;
> > > > > > > > +     u32 id;
> > > > > > > > +     const struct sys_reg_desc *rd;
> > > > > > > > +     u64 val;
> > > > > > > > +
> > > > > > > > +     for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
> > > > > > > > +             rd = &sys_reg_descs[i];
> > > > > > > > +             if (rd->access != access_id_reg)
> > > > > > > > +                     /* Not ID register, or hidden/reserved ID register */
> > > > > > > > +                     continue;
> > > > > > > > +
> > > > > > > > +             id = reg_to_encoding(rd);
> > > > > > > > +             if (WARN_ON_ONCE(!is_id_reg(id)))
> > > > > > > > +                     /* Shouldn't happen */
> > > > > > > > +                     continue;
> > > > > > > > +
> > > > > > > > +             val = read_sanitised_ftr_reg(id);
> > > > > > >
> > > > > > > I'm a bit confused. Shouldn't the default+sanitized values already use
> > > > > > > arm64_ftr_bits_kvm (instead of arm64_ftr_regs)?
> > > > > >
> > > > > > I'm not sure if I understand your question.
> > > > > > arm64_ftr_bits_kvm is used for feature support checkings when
> > > > > > userspace tries to modify a value of ID registers.
> > > > > > With this patch, KVM just saves the sanitized values in the kvm's
> > > > > > buffer, but userspace is still not allowed to modify values of ID
> > > > > > registers yet.
> > > > > > I hope it answers your question.
> > > > >
> > > > > Based on the previous commit I was assuming that some registers, like
> > > > > id_aa64dfr0,
> > > > > would default to the overwritten values as the sanitized values. More
> > > > > specifically: if
> > > > > userspace doesn't modify any ID reg, shouldn't the defaults have the
> > > > > KVM overwritten
> > > > > values (arm64_ftr_bits_kvm)?
> > > >
> > > > arm64_ftr_bits_kvm doesn't have arm64_ftr_reg but arm64_ftr_bits,
> > > > and arm64_ftr_bits_kvm doesn't have the sanitized values.
> > > >
> > > > Thanks,
> > >
> > > Hey Reiji,
> > >
> > > Sorry, I wasn't very clear. This is what I meant.
> > >
> > > If I set DEBUGVER to 0x5 (w/ FTR_EXACT) using this patch on top of the
> > > series:
> > >
> > >  static struct arm64_ftr_bits ftr_id_aa64dfr0_kvm[MAX_FTR_BITS_LEN] = {
> > >         S_ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64DFR0_PMUVER_SHIFT, 4, 0),
> > > -       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x6),
> > > +       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x5),
> > >
> > > it means that userspace would not be able to set DEBUGVER to anything
> > > but 0x5. But I'm not sure what it should mean for the default KVM value
> > > of DEBUGVER, specifically the value calculated in set_default_id_regs().
> > > As it is, KVM is still setting the guest-visible value to 0x6, and my
> > > "desire" to only allow booting VMs with DEBUGVER=0x5 is being ignored: I
> > > booted a VM and the DEBUGVER value from inside is still 0x6. I was
> > > expecting it to not boot, or to show a warning.
> >
> > Thank you for the explanation!
> >
> > FTR_EXACT (in the existing code) means that the safe_val should be
> > used if values of the field are not identical between CPUs (see how
> > update_cpu_ftr_reg() uses arm64_ftr_safe_value()). For KVM usage,
> > it means that if the field value for a vCPU is different from the one
> > for the host's sanitized value, only the safe_val can be used safely
> > for the guest (purely in terms of CPU feature).
>
> Let me double check my understanding using the DEBUGVER example, please.
> The safe_value would be DEBUGVER=5, and it contradicts the initial VM
> value calculated on the KVM side. Q1: Can a contradiction like this
> occur in practice? Q2: If the user saves and restores this id-reg on the
> same kernel, the AA64DFR0 userspace write would fail (ftr_val !=
> arm64_ftr_safe_value), right?

Thank you for the comment!

For Q1, yes, we might possibly create a bug that makes a contradiction
between KVM and cpufeature.c.
For Q2, even with such a contradiction, userspace will still be able to
save and restore the id reg on the same kernel on the same system in most
cases because @limit that KVM will specify for arm64_check_features()
will mostly be the same as the initial value for the guest (except for
fields corresponding to opt-in CPU features, which are configured with
KVM_ARM_VCPU_INIT or etc) and arm64_check_features does an equality check
per field.  Having said that, as you suggested, it might be better to run
arm64_check_features for the initial value against the host value so we
can catch such a bug. I'll look into doing that in v5.

Thanks,
Reiji
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 02/26] KVM: arm64: Save ID registers' sanitized value per guest
@ 2022-02-03  6:31                   ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-02-03  6:31 UTC (permalink / raw)
  To: Ricardo Koller
  Cc: Marc Zyngier, kvmarm, kvm, Linux ARM, James Morse,
	Alexandru Elisei, Suzuki K Poulose, Paolo Bonzini, Will Deacon,
	Andrew Jones, Peng Liang, Peter Shier, Oliver Upton, Jing Zhang,
	Raghavendra Rao Anata

Hi Ricardo,

On Tue, Feb 1, 2022 at 10:39 AM Ricardo Koller <ricarkol@google.com> wrote:
>
> Hey Reiji,
>
> On Mon, Jan 31, 2022 at 10:00:40PM -0800, Reiji Watanabe wrote:
> > Hi Ricardo,
> >
> > On Sun, Jan 30, 2022 at 7:40 PM Ricardo Koller <ricarkol@google.com> wrote:
> > >
> > > On Fri, Jan 28, 2022 at 09:52:21PM -0800, Reiji Watanabe wrote:
> > > > Hi Ricardo,
> > > >
> > > > > > > > +
> > > > > > > > +/*
> > > > > > > > + * Set the guest's ID registers that are defined in sys_reg_descs[]
> > > > > > > > + * with ID_SANITISED() to the host's sanitized value.
> > > > > > > > + */
> > > > > > > > +void set_default_id_regs(struct kvm *kvm)
> > > > > > > > +{
> > > > > > > > +     int i;
> > > > > > > > +     u32 id;
> > > > > > > > +     const struct sys_reg_desc *rd;
> > > > > > > > +     u64 val;
> > > > > > > > +
> > > > > > > > +     for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
> > > > > > > > +             rd = &sys_reg_descs[i];
> > > > > > > > +             if (rd->access != access_id_reg)
> > > > > > > > +                     /* Not ID register, or hidden/reserved ID register */
> > > > > > > > +                     continue;
> > > > > > > > +
> > > > > > > > +             id = reg_to_encoding(rd);
> > > > > > > > +             if (WARN_ON_ONCE(!is_id_reg(id)))
> > > > > > > > +                     /* Shouldn't happen */
> > > > > > > > +                     continue;
> > > > > > > > +
> > > > > > > > +             val = read_sanitised_ftr_reg(id);
> > > > > > >
> > > > > > > I'm a bit confused. Shouldn't the default+sanitized values already use
> > > > > > > arm64_ftr_bits_kvm (instead of arm64_ftr_regs)?
> > > > > >
> > > > > > I'm not sure if I understand your question.
> > > > > > arm64_ftr_bits_kvm is used for feature support checkings when
> > > > > > userspace tries to modify a value of ID registers.
> > > > > > With this patch, KVM just saves the sanitized values in the kvm's
> > > > > > buffer, but userspace is still not allowed to modify values of ID
> > > > > > registers yet.
> > > > > > I hope it answers your question.
> > > > >
> > > > > Based on the previous commit I was assuming that some registers, like
> > > > > id_aa64dfr0,
> > > > > would default to the overwritten values as the sanitized values. More
> > > > > specifically: if
> > > > > userspace doesn't modify any ID reg, shouldn't the defaults have the
> > > > > KVM overwritten
> > > > > values (arm64_ftr_bits_kvm)?
> > > >
> > > > arm64_ftr_bits_kvm doesn't have arm64_ftr_reg but arm64_ftr_bits,
> > > > and arm64_ftr_bits_kvm doesn't have the sanitized values.
> > > >
> > > > Thanks,
> > >
> > > Hey Reiji,
> > >
> > > Sorry, I wasn't very clear. This is what I meant.
> > >
> > > If I set DEBUGVER to 0x5 (w/ FTR_EXACT) using this patch on top of the
> > > series:
> > >
> > >  static struct arm64_ftr_bits ftr_id_aa64dfr0_kvm[MAX_FTR_BITS_LEN] = {
> > >         S_ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64DFR0_PMUVER_SHIFT, 4, 0),
> > > -       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x6),
> > > +       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x5),
> > >
> > > it means that userspace would not be able to set DEBUGVER to anything
> > > but 0x5. But I'm not sure what it should mean for the default KVM value
> > > of DEBUGVER, specifically the value calculated in set_default_id_regs().
> > > As it is, KVM is still setting the guest-visible value to 0x6, and my
> > > "desire" to only allow booting VMs with DEBUGVER=0x5 is being ignored: I
> > > booted a VM and the DEBUGVER value from inside is still 0x6. I was
> > > expecting it to not boot, or to show a warning.
> >
> > Thank you for the explanation!
> >
> > FTR_EXACT (in the existing code) means that the safe_val should be
> > used if values of the field are not identical between CPUs (see how
> > update_cpu_ftr_reg() uses arm64_ftr_safe_value()). For KVM usage,
> > it means that if the field value for a vCPU is different from the one
> > for the host's sanitized value, only the safe_val can be used safely
> > for the guest (purely in terms of CPU feature).
>
> Let me double check my understanding using the DEBUGVER example, please.
> The safe_value would be DEBUGVER=5, and it contradicts the initial VM
> value calculated on the KVM side. Q1: Can a contradiction like this
> occur in practice? Q2: If the user saves and restores this id-reg on the
> same kernel, the AA64DFR0 userspace write would fail (ftr_val !=
> arm64_ftr_safe_value), right?

Thank you for the comment!

For Q1, yes, we might possibly create a bug that makes a contradiction
between KVM and cpufeature.c.
For Q2, even with such a contradiction, userspace will still be able to
save and restore the id reg on the same kernel on the same system in most
cases because @limit that KVM will specify for arm64_check_features()
will mostly be the same as the initial value for the guest (except for
fields corresponding to opt-in CPU features, which are configured with
KVM_ARM_VCPU_INIT or etc) and arm64_check_features does an equality check
per field.  Having said that, as you suggested, it might be better to run
arm64_check_features for the initial value against the host value so we
can catch such a bug. I'll look into doing that in v5.

Thanks,
Reiji

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 02/26] KVM: arm64: Save ID registers' sanitized value per guest
  2022-02-03  6:31                   ` Reiji Watanabe
  (?)
@ 2022-02-04 14:41                     ` Ricardo Koller
  -1 siblings, 0 replies; 201+ messages in thread
From: Ricardo Koller @ 2022-02-04 14:41 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: Marc Zyngier, kvmarm, kvm, Linux ARM, James Morse,
	Alexandru Elisei, Suzuki K Poulose, Paolo Bonzini, Will Deacon,
	Andrew Jones, Peng Liang, Peter Shier, Oliver Upton, Jing Zhang,
	Raghavendra Rao Anata

On Wed, Feb 02, 2022 at 10:31:26PM -0800, Reiji Watanabe wrote:
> Hi Ricardo,
> 
> On Tue, Feb 1, 2022 at 10:39 AM Ricardo Koller <ricarkol@google.com> wrote:
> >
> > Hey Reiji,
> >
> > On Mon, Jan 31, 2022 at 10:00:40PM -0800, Reiji Watanabe wrote:
> > > Hi Ricardo,
> > >
> > > On Sun, Jan 30, 2022 at 7:40 PM Ricardo Koller <ricarkol@google.com> wrote:
> > > >
> > > > On Fri, Jan 28, 2022 at 09:52:21PM -0800, Reiji Watanabe wrote:
> > > > > Hi Ricardo,
> > > > >
> > > > > > > > > +
> > > > > > > > > +/*
> > > > > > > > > + * Set the guest's ID registers that are defined in sys_reg_descs[]
> > > > > > > > > + * with ID_SANITISED() to the host's sanitized value.
> > > > > > > > > + */
> > > > > > > > > +void set_default_id_regs(struct kvm *kvm)
> > > > > > > > > +{
> > > > > > > > > +     int i;
> > > > > > > > > +     u32 id;
> > > > > > > > > +     const struct sys_reg_desc *rd;
> > > > > > > > > +     u64 val;
> > > > > > > > > +
> > > > > > > > > +     for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
> > > > > > > > > +             rd = &sys_reg_descs[i];
> > > > > > > > > +             if (rd->access != access_id_reg)
> > > > > > > > > +                     /* Not ID register, or hidden/reserved ID register */
> > > > > > > > > +                     continue;
> > > > > > > > > +
> > > > > > > > > +             id = reg_to_encoding(rd);
> > > > > > > > > +             if (WARN_ON_ONCE(!is_id_reg(id)))
> > > > > > > > > +                     /* Shouldn't happen */
> > > > > > > > > +                     continue;
> > > > > > > > > +
> > > > > > > > > +             val = read_sanitised_ftr_reg(id);
> > > > > > > >
> > > > > > > > I'm a bit confused. Shouldn't the default+sanitized values already use
> > > > > > > > arm64_ftr_bits_kvm (instead of arm64_ftr_regs)?
> > > > > > >
> > > > > > > I'm not sure if I understand your question.
> > > > > > > arm64_ftr_bits_kvm is used for feature support checkings when
> > > > > > > userspace tries to modify a value of ID registers.
> > > > > > > With this patch, KVM just saves the sanitized values in the kvm's
> > > > > > > buffer, but userspace is still not allowed to modify values of ID
> > > > > > > registers yet.
> > > > > > > I hope it answers your question.
> > > > > >
> > > > > > Based on the previous commit I was assuming that some registers, like
> > > > > > id_aa64dfr0,
> > > > > > would default to the overwritten values as the sanitized values. More
> > > > > > specifically: if
> > > > > > userspace doesn't modify any ID reg, shouldn't the defaults have the
> > > > > > KVM overwritten
> > > > > > values (arm64_ftr_bits_kvm)?
> > > > >
> > > > > arm64_ftr_bits_kvm doesn't have arm64_ftr_reg but arm64_ftr_bits,
> > > > > and arm64_ftr_bits_kvm doesn't have the sanitized values.
> > > > >
> > > > > Thanks,
> > > >
> > > > Hey Reiji,
> > > >
> > > > Sorry, I wasn't very clear. This is what I meant.
> > > >
> > > > If I set DEBUGVER to 0x5 (w/ FTR_EXACT) using this patch on top of the
> > > > series:
> > > >
> > > >  static struct arm64_ftr_bits ftr_id_aa64dfr0_kvm[MAX_FTR_BITS_LEN] = {
> > > >         S_ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64DFR0_PMUVER_SHIFT, 4, 0),
> > > > -       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x6),
> > > > +       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x5),
> > > >
> > > > it means that userspace would not be able to set DEBUGVER to anything
> > > > but 0x5. But I'm not sure what it should mean for the default KVM value
> > > > of DEBUGVER, specifically the value calculated in set_default_id_regs().
> > > > As it is, KVM is still setting the guest-visible value to 0x6, and my
> > > > "desire" to only allow booting VMs with DEBUGVER=0x5 is being ignored: I
> > > > booted a VM and the DEBUGVER value from inside is still 0x6. I was
> > > > expecting it to not boot, or to show a warning.
> > >
> > > Thank you for the explanation!
> > >
> > > FTR_EXACT (in the existing code) means that the safe_val should be
> > > used if values of the field are not identical between CPUs (see how
> > > update_cpu_ftr_reg() uses arm64_ftr_safe_value()). For KVM usage,
> > > it means that if the field value for a vCPU is different from the one
> > > for the host's sanitized value, only the safe_val can be used safely
> > > for the guest (purely in terms of CPU feature).
> >
> > Let me double check my understanding using the DEBUGVER example, please.
> > The safe_value would be DEBUGVER=5, and it contradicts the initial VM
> > value calculated on the KVM side. Q1: Can a contradiction like this
> > occur in practice? Q2: If the user saves and restores this id-reg on the
> > same kernel, the AA64DFR0 userspace write would fail (ftr_val !=
> > arm64_ftr_safe_value), right?
> 
> Thank you for the comment!
> 
> For Q1, yes, we might possibly create a bug that makes a contradiction
> between KVM and cpufeature.c.
> For Q2, even with such a contradiction, userspace will still be able to
> save and restore the id reg on the same kernel on the same system in most
> cases because @limit that KVM will specify for arm64_check_features()
> will mostly be the same as the initial value for the guest (except for
> fields corresponding to opt-in CPU features, which are configured with
> KVM_ARM_VCPU_INIT or etc) and arm64_check_features does an equality check
> per field.  Having said that, as you suggested, it might be better to run
> arm64_check_features for the initial value against the host value so we
> can catch such a bug. I'll look into doing that in v5.
> 

Thanks Reiji. Looking forward to v5.

> Thanks,
> Reiji

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 02/26] KVM: arm64: Save ID registers' sanitized value per guest
@ 2022-02-04 14:41                     ` Ricardo Koller
  0 siblings, 0 replies; 201+ messages in thread
From: Ricardo Koller @ 2022-02-04 14:41 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: kvm, Marc Zyngier, Peter Shier, Will Deacon, Paolo Bonzini,
	kvmarm, Linux ARM

On Wed, Feb 02, 2022 at 10:31:26PM -0800, Reiji Watanabe wrote:
> Hi Ricardo,
> 
> On Tue, Feb 1, 2022 at 10:39 AM Ricardo Koller <ricarkol@google.com> wrote:
> >
> > Hey Reiji,
> >
> > On Mon, Jan 31, 2022 at 10:00:40PM -0800, Reiji Watanabe wrote:
> > > Hi Ricardo,
> > >
> > > On Sun, Jan 30, 2022 at 7:40 PM Ricardo Koller <ricarkol@google.com> wrote:
> > > >
> > > > On Fri, Jan 28, 2022 at 09:52:21PM -0800, Reiji Watanabe wrote:
> > > > > Hi Ricardo,
> > > > >
> > > > > > > > > +
> > > > > > > > > +/*
> > > > > > > > > + * Set the guest's ID registers that are defined in sys_reg_descs[]
> > > > > > > > > + * with ID_SANITISED() to the host's sanitized value.
> > > > > > > > > + */
> > > > > > > > > +void set_default_id_regs(struct kvm *kvm)
> > > > > > > > > +{
> > > > > > > > > +     int i;
> > > > > > > > > +     u32 id;
> > > > > > > > > +     const struct sys_reg_desc *rd;
> > > > > > > > > +     u64 val;
> > > > > > > > > +
> > > > > > > > > +     for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
> > > > > > > > > +             rd = &sys_reg_descs[i];
> > > > > > > > > +             if (rd->access != access_id_reg)
> > > > > > > > > +                     /* Not ID register, or hidden/reserved ID register */
> > > > > > > > > +                     continue;
> > > > > > > > > +
> > > > > > > > > +             id = reg_to_encoding(rd);
> > > > > > > > > +             if (WARN_ON_ONCE(!is_id_reg(id)))
> > > > > > > > > +                     /* Shouldn't happen */
> > > > > > > > > +                     continue;
> > > > > > > > > +
> > > > > > > > > +             val = read_sanitised_ftr_reg(id);
> > > > > > > >
> > > > > > > > I'm a bit confused. Shouldn't the default+sanitized values already use
> > > > > > > > arm64_ftr_bits_kvm (instead of arm64_ftr_regs)?
> > > > > > >
> > > > > > > I'm not sure if I understand your question.
> > > > > > > arm64_ftr_bits_kvm is used for feature support checkings when
> > > > > > > userspace tries to modify a value of ID registers.
> > > > > > > With this patch, KVM just saves the sanitized values in the kvm's
> > > > > > > buffer, but userspace is still not allowed to modify values of ID
> > > > > > > registers yet.
> > > > > > > I hope it answers your question.
> > > > > >
> > > > > > Based on the previous commit I was assuming that some registers, like
> > > > > > id_aa64dfr0,
> > > > > > would default to the overwritten values as the sanitized values. More
> > > > > > specifically: if
> > > > > > userspace doesn't modify any ID reg, shouldn't the defaults have the
> > > > > > KVM overwritten
> > > > > > values (arm64_ftr_bits_kvm)?
> > > > >
> > > > > arm64_ftr_bits_kvm doesn't have arm64_ftr_reg but arm64_ftr_bits,
> > > > > and arm64_ftr_bits_kvm doesn't have the sanitized values.
> > > > >
> > > > > Thanks,
> > > >
> > > > Hey Reiji,
> > > >
> > > > Sorry, I wasn't very clear. This is what I meant.
> > > >
> > > > If I set DEBUGVER to 0x5 (w/ FTR_EXACT) using this patch on top of the
> > > > series:
> > > >
> > > >  static struct arm64_ftr_bits ftr_id_aa64dfr0_kvm[MAX_FTR_BITS_LEN] = {
> > > >         S_ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64DFR0_PMUVER_SHIFT, 4, 0),
> > > > -       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x6),
> > > > +       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x5),
> > > >
> > > > it means that userspace would not be able to set DEBUGVER to anything
> > > > but 0x5. But I'm not sure what it should mean for the default KVM value
> > > > of DEBUGVER, specifically the value calculated in set_default_id_regs().
> > > > As it is, KVM is still setting the guest-visible value to 0x6, and my
> > > > "desire" to only allow booting VMs with DEBUGVER=0x5 is being ignored: I
> > > > booted a VM and the DEBUGVER value from inside is still 0x6. I was
> > > > expecting it to not boot, or to show a warning.
> > >
> > > Thank you for the explanation!
> > >
> > > FTR_EXACT (in the existing code) means that the safe_val should be
> > > used if values of the field are not identical between CPUs (see how
> > > update_cpu_ftr_reg() uses arm64_ftr_safe_value()). For KVM usage,
> > > it means that if the field value for a vCPU is different from the one
> > > for the host's sanitized value, only the safe_val can be used safely
> > > for the guest (purely in terms of CPU feature).
> >
> > Let me double check my understanding using the DEBUGVER example, please.
> > The safe_value would be DEBUGVER=5, and it contradicts the initial VM
> > value calculated on the KVM side. Q1: Can a contradiction like this
> > occur in practice? Q2: If the user saves and restores this id-reg on the
> > same kernel, the AA64DFR0 userspace write would fail (ftr_val !=
> > arm64_ftr_safe_value), right?
> 
> Thank you for the comment!
> 
> For Q1, yes, we might possibly create a bug that makes a contradiction
> between KVM and cpufeature.c.
> For Q2, even with such a contradiction, userspace will still be able to
> save and restore the id reg on the same kernel on the same system in most
> cases because @limit that KVM will specify for arm64_check_features()
> will mostly be the same as the initial value for the guest (except for
> fields corresponding to opt-in CPU features, which are configured with
> KVM_ARM_VCPU_INIT or etc) and arm64_check_features does an equality check
> per field.  Having said that, as you suggested, it might be better to run
> arm64_check_features for the initial value against the host value so we
> can catch such a bug. I'll look into doing that in v5.
> 

Thanks Reiji. Looking forward to v5.

> Thanks,
> Reiji
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 02/26] KVM: arm64: Save ID registers' sanitized value per guest
@ 2022-02-04 14:41                     ` Ricardo Koller
  0 siblings, 0 replies; 201+ messages in thread
From: Ricardo Koller @ 2022-02-04 14:41 UTC (permalink / raw)
  To: Reiji Watanabe
  Cc: Marc Zyngier, kvmarm, kvm, Linux ARM, James Morse,
	Alexandru Elisei, Suzuki K Poulose, Paolo Bonzini, Will Deacon,
	Andrew Jones, Peng Liang, Peter Shier, Oliver Upton, Jing Zhang,
	Raghavendra Rao Anata

On Wed, Feb 02, 2022 at 10:31:26PM -0800, Reiji Watanabe wrote:
> Hi Ricardo,
> 
> On Tue, Feb 1, 2022 at 10:39 AM Ricardo Koller <ricarkol@google.com> wrote:
> >
> > Hey Reiji,
> >
> > On Mon, Jan 31, 2022 at 10:00:40PM -0800, Reiji Watanabe wrote:
> > > Hi Ricardo,
> > >
> > > On Sun, Jan 30, 2022 at 7:40 PM Ricardo Koller <ricarkol@google.com> wrote:
> > > >
> > > > On Fri, Jan 28, 2022 at 09:52:21PM -0800, Reiji Watanabe wrote:
> > > > > Hi Ricardo,
> > > > >
> > > > > > > > > +
> > > > > > > > > +/*
> > > > > > > > > + * Set the guest's ID registers that are defined in sys_reg_descs[]
> > > > > > > > > + * with ID_SANITISED() to the host's sanitized value.
> > > > > > > > > + */
> > > > > > > > > +void set_default_id_regs(struct kvm *kvm)
> > > > > > > > > +{
> > > > > > > > > +     int i;
> > > > > > > > > +     u32 id;
> > > > > > > > > +     const struct sys_reg_desc *rd;
> > > > > > > > > +     u64 val;
> > > > > > > > > +
> > > > > > > > > +     for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
> > > > > > > > > +             rd = &sys_reg_descs[i];
> > > > > > > > > +             if (rd->access != access_id_reg)
> > > > > > > > > +                     /* Not ID register, or hidden/reserved ID register */
> > > > > > > > > +                     continue;
> > > > > > > > > +
> > > > > > > > > +             id = reg_to_encoding(rd);
> > > > > > > > > +             if (WARN_ON_ONCE(!is_id_reg(id)))
> > > > > > > > > +                     /* Shouldn't happen */
> > > > > > > > > +                     continue;
> > > > > > > > > +
> > > > > > > > > +             val = read_sanitised_ftr_reg(id);
> > > > > > > >
> > > > > > > > I'm a bit confused. Shouldn't the default+sanitized values already use
> > > > > > > > arm64_ftr_bits_kvm (instead of arm64_ftr_regs)?
> > > > > > >
> > > > > > > I'm not sure if I understand your question.
> > > > > > > arm64_ftr_bits_kvm is used for feature support checkings when
> > > > > > > userspace tries to modify a value of ID registers.
> > > > > > > With this patch, KVM just saves the sanitized values in the kvm's
> > > > > > > buffer, but userspace is still not allowed to modify values of ID
> > > > > > > registers yet.
> > > > > > > I hope it answers your question.
> > > > > >
> > > > > > Based on the previous commit I was assuming that some registers, like
> > > > > > id_aa64dfr0,
> > > > > > would default to the overwritten values as the sanitized values. More
> > > > > > specifically: if
> > > > > > userspace doesn't modify any ID reg, shouldn't the defaults have the
> > > > > > KVM overwritten
> > > > > > values (arm64_ftr_bits_kvm)?
> > > > >
> > > > > arm64_ftr_bits_kvm doesn't have arm64_ftr_reg but arm64_ftr_bits,
> > > > > and arm64_ftr_bits_kvm doesn't have the sanitized values.
> > > > >
> > > > > Thanks,
> > > >
> > > > Hey Reiji,
> > > >
> > > > Sorry, I wasn't very clear. This is what I meant.
> > > >
> > > > If I set DEBUGVER to 0x5 (w/ FTR_EXACT) using this patch on top of the
> > > > series:
> > > >
> > > >  static struct arm64_ftr_bits ftr_id_aa64dfr0_kvm[MAX_FTR_BITS_LEN] = {
> > > >         S_ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64DFR0_PMUVER_SHIFT, 4, 0),
> > > > -       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x6),
> > > > +       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x5),
> > > >
> > > > it means that userspace would not be able to set DEBUGVER to anything
> > > > but 0x5. But I'm not sure what it should mean for the default KVM value
> > > > of DEBUGVER, specifically the value calculated in set_default_id_regs().
> > > > As it is, KVM is still setting the guest-visible value to 0x6, and my
> > > > "desire" to only allow booting VMs with DEBUGVER=0x5 is being ignored: I
> > > > booted a VM and the DEBUGVER value from inside is still 0x6. I was
> > > > expecting it to not boot, or to show a warning.
> > >
> > > Thank you for the explanation!
> > >
> > > FTR_EXACT (in the existing code) means that the safe_val should be
> > > used if values of the field are not identical between CPUs (see how
> > > update_cpu_ftr_reg() uses arm64_ftr_safe_value()). For KVM usage,
> > > it means that if the field value for a vCPU is different from the one
> > > for the host's sanitized value, only the safe_val can be used safely
> > > for the guest (purely in terms of CPU feature).
> >
> > Let me double check my understanding using the DEBUGVER example, please.
> > The safe_value would be DEBUGVER=5, and it contradicts the initial VM
> > value calculated on the KVM side. Q1: Can a contradiction like this
> > occur in practice? Q2: If the user saves and restores this id-reg on the
> > same kernel, the AA64DFR0 userspace write would fail (ftr_val !=
> > arm64_ftr_safe_value), right?
> 
> Thank you for the comment!
> 
> For Q1, yes, we might possibly create a bug that makes a contradiction
> between KVM and cpufeature.c.
> For Q2, even with such a contradiction, userspace will still be able to
> save and restore the id reg on the same kernel on the same system in most
> cases because @limit that KVM will specify for arm64_check_features()
> will mostly be the same as the initial value for the guest (except for
> fields corresponding to opt-in CPU features, which are configured with
> KVM_ARM_VCPU_INIT or etc) and arm64_check_features does an equality check
> per field.  Having said that, as you suggested, it might be better to run
> arm64_check_features for the initial value against the host value so we
> can catch such a bug. I'll look into doing that in v5.
> 

Thanks Reiji. Looking forward to v5.

> Thanks,
> Reiji

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 02/26] KVM: arm64: Save ID registers' sanitized value per guest
  2022-01-24 16:21     ` Fuad Tabba
  (?)
@ 2022-02-09  2:26       ` Reiji Watanabe
  -1 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-02-09  2:26 UTC (permalink / raw)
  To: Fuad Tabba
  Cc: kvm, Marc Zyngier, Peter Shier, Paolo Bonzini, Will Deacon,
	kvmarm, Linux ARM

Hi Fuad,

Sorry for the late reply.
I've noticed that I didn't reply to the comments in this mail.

On Mon, Jan 24, 2022 at 8:22 AM Fuad Tabba <tabba@google.com> wrote:
>
> Hi Reiji,
>
> On Thu, Jan 6, 2022 at 4:28 AM Reiji Watanabe <reijiw@google.com> wrote:
> >
> > Introduce id_regs[] in kvm_arch as a storage of guest's ID registers,
> > and save ID registers' sanitized value in the array at KVM_CREATE_VM.
> > Use the saved ones when ID registers are read by the guest or
> > userspace (via KVM_GET_ONE_REG).
> >
> > Signed-off-by: Reiji Watanabe <reijiw@google.com>
> > ---
> >  arch/arm64/include/asm/kvm_host.h | 16 ++++++++
> >  arch/arm64/kvm/arm.c              |  1 +
> >  arch/arm64/kvm/sys_regs.c         | 62 ++++++++++++++++++++++---------
> >  3 files changed, 62 insertions(+), 17 deletions(-)
> >
> > diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> > index 2a5f7f38006f..c789a0137f58 100644
> > --- a/arch/arm64/include/asm/kvm_host.h
> > +++ b/arch/arm64/include/asm/kvm_host.h
> > @@ -102,6 +102,17 @@ struct kvm_s2_mmu {
> >  struct kvm_arch_memory_slot {
> >  };
> >
> > +/*
> > + * (Op0, Op1, CRn, CRm, Op2) of ID registers is (3, 0, 0, crm, op2),
> > + * where 0<=crm<8, 0<=op2<8.
> > + */
> > +#define KVM_ARM_ID_REG_MAX_NUM 64
> > +#define IDREG_IDX(id)          ((sys_reg_CRm(id) << 3) | sys_reg_Op2(id))
> > +#define is_id_reg(id)  \
> > +       (sys_reg_Op0(id) == 3 && sys_reg_Op1(id) == 0 &&        \
> > +        sys_reg_CRn(id) == 0 && sys_reg_CRm(id) >= 0 &&        \
> > +        sys_reg_CRm(id) < 8)
> > +
>
> This is consistent with the Arm ARM "Table D12-2 System instruction
> encodings for non-Debug System register accesses".
>
> Minor nit, would it be better to have IDREG_IDX and is_id_reg in
> arch/arm64/kvm/sys_regs.h, since other similar and related ones are
> there?

I will move is_id_reg in sys_regs.c as it is used only in the file.
I will keep IDREG_IDX in arch/arm64/kvm/sys_regs.h as IDREG_IDX is
used to get an index of kvm_arch.id_regs[], which is defined in
kvm_host.h.

>
> >  struct kvm_arch {
> >         struct kvm_s2_mmu mmu;
> >
> > @@ -137,6 +148,9 @@ struct kvm_arch {
> >
> >         /* Memory Tagging Extension enabled for the guest */
> >         bool mte_enabled;
> > +
> > +       /* ID registers for the guest. */
> > +       u64 id_regs[KVM_ARM_ID_REG_MAX_NUM];
> >  };
> >
> >  struct kvm_vcpu_fault_info {
> > @@ -734,6 +748,8 @@ int kvm_arm_vcpu_arch_has_attr(struct kvm_vcpu *vcpu,
> >  long kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
> >                                 struct kvm_arm_copy_mte_tags *copy_tags);
> >
> > +void set_default_id_regs(struct kvm *kvm);
> > +
> >  /* Guest/host FPSIMD coordination helpers */
> >  int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu);
> >  void kvm_arch_vcpu_load_fp(struct kvm_vcpu *vcpu);
> > diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> > index e4727dc771bf..5f497a0af254 100644
> > --- a/arch/arm64/kvm/arm.c
> > +++ b/arch/arm64/kvm/arm.c
> > @@ -156,6 +156,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
> >         kvm->arch.max_vcpus = kvm_arm_default_max_vcpus();
> >
> >         set_default_spectre(kvm);
> > +       set_default_id_regs(kvm);
> >
> >         return ret;
> >  out_free_stage2_pgd:
> > diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> > index e3ec1a44f94d..80dc62f98ef0 100644
> > --- a/arch/arm64/kvm/sys_regs.c
> > +++ b/arch/arm64/kvm/sys_regs.c
> > @@ -33,6 +33,8 @@
> >
> >  #include "trace.h"
> >
> > +static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id);
> > +
> >  /*
> >   * All of this file is extremely similar to the ARM coproc.c, but the
> >   * types are different. My gut feeling is that it should be pretty
> > @@ -273,7 +275,7 @@ static bool trap_loregion(struct kvm_vcpu *vcpu,
> >                           struct sys_reg_params *p,
> >                           const struct sys_reg_desc *r)
> >  {
> > -       u64 val = read_sanitised_ftr_reg(SYS_ID_AA64MMFR1_EL1);
> > +       u64 val = __read_id_reg(vcpu, SYS_ID_AA64MMFR1_EL1);
> >         u32 sr = reg_to_encoding(r);
> >
> >         if (!(val & (0xfUL << ID_AA64MMFR1_LOR_SHIFT))) {
> > @@ -1059,17 +1061,9 @@ static bool access_arch_timer(struct kvm_vcpu *vcpu,
> >         return true;
> >  }
> >
> > -/* Read a sanitised cpufeature ID register by sys_reg_desc */
> > -static u64 read_id_reg(const struct kvm_vcpu *vcpu,
> > -               struct sys_reg_desc const *r, bool raz)
> > +static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
> >  {
> > -       u32 id = reg_to_encoding(r);
> > -       u64 val;
> > -
> > -       if (raz)
> > -               return 0;
> > -
> > -       val = read_sanitised_ftr_reg(id);
> > +       u64 val = vcpu->kvm->arch.id_regs[IDREG_IDX(id)];
> >
> >         switch (id) {
> >         case SYS_ID_AA64PFR0_EL1:
> > @@ -1119,6 +1113,14 @@ static u64 read_id_reg(const struct kvm_vcpu *vcpu,
> >         return val;
> >  }
> >
> > +static u64 read_id_reg(const struct kvm_vcpu *vcpu,
> > +                      struct sys_reg_desc const *r, bool raz)
> > +{
> > +       u32 id = reg_to_encoding(r);
> > +
> > +       return raz ? 0 : __read_id_reg(vcpu, id);
> > +}
> > +
> >  static unsigned int id_visibility(const struct kvm_vcpu *vcpu,
> >                                   const struct sys_reg_desc *r)
> >  {
> > @@ -1223,9 +1225,8 @@ static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
> >  /*
> >   * cpufeature ID register user accessors
> >   *
> > - * For now, these registers are immutable for userspace, so no values
> > - * are stored, and for set_id_reg() we don't allow the effective value
> > - * to be changed.
> > + * For now, these registers are immutable for userspace, so for set_id_reg()
> > + * we don't allow the effective value to be changed.
> >   */
> >  static int __get_id_reg(const struct kvm_vcpu *vcpu,
> >                         const struct sys_reg_desc *rd, void __user *uaddr,
> > @@ -1237,7 +1238,7 @@ static int __get_id_reg(const struct kvm_vcpu *vcpu,
> >         return reg_to_user(uaddr, &val, id);
> >  }
> >
> > -static int __set_id_reg(const struct kvm_vcpu *vcpu,
> > +static int __set_id_reg(struct kvm_vcpu *vcpu,
> >                         const struct sys_reg_desc *rd, void __user *uaddr,
> >                         bool raz)
>
> Minor nit: why remove the const in this patch? This is required for a
> future patch but not for this one.

Thank you for catching this. I will fix this.

Regards,
Reiji

>
>
> >  {
> > @@ -1837,8 +1838,8 @@ static bool trap_dbgdidr(struct kvm_vcpu *vcpu,
> >         if (p->is_write) {
> >                 return ignore_write(vcpu, p);
> >         } else {
> > -               u64 dfr = read_sanitised_ftr_reg(SYS_ID_AA64DFR0_EL1);
> > -               u64 pfr = read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1);
> > +               u64 dfr = __read_id_reg(vcpu, SYS_ID_AA64DFR0_EL1);
> > +               u64 pfr = __read_id_reg(vcpu, SYS_ID_AA64PFR0_EL1);
> >                 u32 el3 = !!cpuid_feature_extract_unsigned_field(pfr, ID_AA64PFR0_EL3_SHIFT);
> >
> >                 p->regval = ((((dfr >> ID_AA64DFR0_WRPS_SHIFT) & 0xf) << 28) |
> > @@ -2850,3 +2851,30 @@ void kvm_sys_reg_table_init(void)
> >         /* Clear all higher bits. */
> >         cache_levels &= (1 << (i*3))-1;
> >  }
> > +
> > +/*
> > + * Set the guest's ID registers that are defined in sys_reg_descs[]
> > + * with ID_SANITISED() to the host's sanitized value.
> > + */
> > +void set_default_id_regs(struct kvm *kvm)
> > +{
> > +       int i;
> > +       u32 id;
> > +       const struct sys_reg_desc *rd;
> > +       u64 val;
> > +
> > +       for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
> > +               rd = &sys_reg_descs[i];
> > +               if (rd->access != access_id_reg)
> > +                       /* Not ID register, or hidden/reserved ID register */
> > +                       continue;
> > +
> > +               id = reg_to_encoding(rd);
> > +               if (WARN_ON_ONCE(!is_id_reg(id)))
> > +                       /* Shouldn't happen */
> > +                       continue;
> > +
> > +               val = read_sanitised_ftr_reg(id);
> > +               kvm->arch.id_regs[IDREG_IDX(id)] = val;
> > +       }
> > +}
> > --
> > 2.34.1.448.ga2b2bfdf31-goog
> >
> > _______________________________________________
> > kvmarm mailing list
> > kvmarm@lists.cs.columbia.edu
> > https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 02/26] KVM: arm64: Save ID registers' sanitized value per guest
@ 2022-02-09  2:26       ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-02-09  2:26 UTC (permalink / raw)
  To: Fuad Tabba
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, Linux ARM

Hi Fuad,

Sorry for the late reply.
I've noticed that I didn't reply to the comments in this mail.

On Mon, Jan 24, 2022 at 8:22 AM Fuad Tabba <tabba@google.com> wrote:
>
> Hi Reiji,
>
> On Thu, Jan 6, 2022 at 4:28 AM Reiji Watanabe <reijiw@google.com> wrote:
> >
> > Introduce id_regs[] in kvm_arch as a storage of guest's ID registers,
> > and save ID registers' sanitized value in the array at KVM_CREATE_VM.
> > Use the saved ones when ID registers are read by the guest or
> > userspace (via KVM_GET_ONE_REG).
> >
> > Signed-off-by: Reiji Watanabe <reijiw@google.com>
> > ---
> >  arch/arm64/include/asm/kvm_host.h | 16 ++++++++
> >  arch/arm64/kvm/arm.c              |  1 +
> >  arch/arm64/kvm/sys_regs.c         | 62 ++++++++++++++++++++++---------
> >  3 files changed, 62 insertions(+), 17 deletions(-)
> >
> > diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> > index 2a5f7f38006f..c789a0137f58 100644
> > --- a/arch/arm64/include/asm/kvm_host.h
> > +++ b/arch/arm64/include/asm/kvm_host.h
> > @@ -102,6 +102,17 @@ struct kvm_s2_mmu {
> >  struct kvm_arch_memory_slot {
> >  };
> >
> > +/*
> > + * (Op0, Op1, CRn, CRm, Op2) of ID registers is (3, 0, 0, crm, op2),
> > + * where 0<=crm<8, 0<=op2<8.
> > + */
> > +#define KVM_ARM_ID_REG_MAX_NUM 64
> > +#define IDREG_IDX(id)          ((sys_reg_CRm(id) << 3) | sys_reg_Op2(id))
> > +#define is_id_reg(id)  \
> > +       (sys_reg_Op0(id) == 3 && sys_reg_Op1(id) == 0 &&        \
> > +        sys_reg_CRn(id) == 0 && sys_reg_CRm(id) >= 0 &&        \
> > +        sys_reg_CRm(id) < 8)
> > +
>
> This is consistent with the Arm ARM "Table D12-2 System instruction
> encodings for non-Debug System register accesses".
>
> Minor nit, would it be better to have IDREG_IDX and is_id_reg in
> arch/arm64/kvm/sys_regs.h, since other similar and related ones are
> there?

I will move is_id_reg in sys_regs.c as it is used only in the file.
I will keep IDREG_IDX in arch/arm64/kvm/sys_regs.h as IDREG_IDX is
used to get an index of kvm_arch.id_regs[], which is defined in
kvm_host.h.

>
> >  struct kvm_arch {
> >         struct kvm_s2_mmu mmu;
> >
> > @@ -137,6 +148,9 @@ struct kvm_arch {
> >
> >         /* Memory Tagging Extension enabled for the guest */
> >         bool mte_enabled;
> > +
> > +       /* ID registers for the guest. */
> > +       u64 id_regs[KVM_ARM_ID_REG_MAX_NUM];
> >  };
> >
> >  struct kvm_vcpu_fault_info {
> > @@ -734,6 +748,8 @@ int kvm_arm_vcpu_arch_has_attr(struct kvm_vcpu *vcpu,
> >  long kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
> >                                 struct kvm_arm_copy_mte_tags *copy_tags);
> >
> > +void set_default_id_regs(struct kvm *kvm);
> > +
> >  /* Guest/host FPSIMD coordination helpers */
> >  int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu);
> >  void kvm_arch_vcpu_load_fp(struct kvm_vcpu *vcpu);
> > diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> > index e4727dc771bf..5f497a0af254 100644
> > --- a/arch/arm64/kvm/arm.c
> > +++ b/arch/arm64/kvm/arm.c
> > @@ -156,6 +156,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
> >         kvm->arch.max_vcpus = kvm_arm_default_max_vcpus();
> >
> >         set_default_spectre(kvm);
> > +       set_default_id_regs(kvm);
> >
> >         return ret;
> >  out_free_stage2_pgd:
> > diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> > index e3ec1a44f94d..80dc62f98ef0 100644
> > --- a/arch/arm64/kvm/sys_regs.c
> > +++ b/arch/arm64/kvm/sys_regs.c
> > @@ -33,6 +33,8 @@
> >
> >  #include "trace.h"
> >
> > +static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id);
> > +
> >  /*
> >   * All of this file is extremely similar to the ARM coproc.c, but the
> >   * types are different. My gut feeling is that it should be pretty
> > @@ -273,7 +275,7 @@ static bool trap_loregion(struct kvm_vcpu *vcpu,
> >                           struct sys_reg_params *p,
> >                           const struct sys_reg_desc *r)
> >  {
> > -       u64 val = read_sanitised_ftr_reg(SYS_ID_AA64MMFR1_EL1);
> > +       u64 val = __read_id_reg(vcpu, SYS_ID_AA64MMFR1_EL1);
> >         u32 sr = reg_to_encoding(r);
> >
> >         if (!(val & (0xfUL << ID_AA64MMFR1_LOR_SHIFT))) {
> > @@ -1059,17 +1061,9 @@ static bool access_arch_timer(struct kvm_vcpu *vcpu,
> >         return true;
> >  }
> >
> > -/* Read a sanitised cpufeature ID register by sys_reg_desc */
> > -static u64 read_id_reg(const struct kvm_vcpu *vcpu,
> > -               struct sys_reg_desc const *r, bool raz)
> > +static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
> >  {
> > -       u32 id = reg_to_encoding(r);
> > -       u64 val;
> > -
> > -       if (raz)
> > -               return 0;
> > -
> > -       val = read_sanitised_ftr_reg(id);
> > +       u64 val = vcpu->kvm->arch.id_regs[IDREG_IDX(id)];
> >
> >         switch (id) {
> >         case SYS_ID_AA64PFR0_EL1:
> > @@ -1119,6 +1113,14 @@ static u64 read_id_reg(const struct kvm_vcpu *vcpu,
> >         return val;
> >  }
> >
> > +static u64 read_id_reg(const struct kvm_vcpu *vcpu,
> > +                      struct sys_reg_desc const *r, bool raz)
> > +{
> > +       u32 id = reg_to_encoding(r);
> > +
> > +       return raz ? 0 : __read_id_reg(vcpu, id);
> > +}
> > +
> >  static unsigned int id_visibility(const struct kvm_vcpu *vcpu,
> >                                   const struct sys_reg_desc *r)
> >  {
> > @@ -1223,9 +1225,8 @@ static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
> >  /*
> >   * cpufeature ID register user accessors
> >   *
> > - * For now, these registers are immutable for userspace, so no values
> > - * are stored, and for set_id_reg() we don't allow the effective value
> > - * to be changed.
> > + * For now, these registers are immutable for userspace, so for set_id_reg()
> > + * we don't allow the effective value to be changed.
> >   */
> >  static int __get_id_reg(const struct kvm_vcpu *vcpu,
> >                         const struct sys_reg_desc *rd, void __user *uaddr,
> > @@ -1237,7 +1238,7 @@ static int __get_id_reg(const struct kvm_vcpu *vcpu,
> >         return reg_to_user(uaddr, &val, id);
> >  }
> >
> > -static int __set_id_reg(const struct kvm_vcpu *vcpu,
> > +static int __set_id_reg(struct kvm_vcpu *vcpu,
> >                         const struct sys_reg_desc *rd, void __user *uaddr,
> >                         bool raz)
>
> Minor nit: why remove the const in this patch? This is required for a
> future patch but not for this one.

Thank you for catching this. I will fix this.

Regards,
Reiji

>
>
> >  {
> > @@ -1837,8 +1838,8 @@ static bool trap_dbgdidr(struct kvm_vcpu *vcpu,
> >         if (p->is_write) {
> >                 return ignore_write(vcpu, p);
> >         } else {
> > -               u64 dfr = read_sanitised_ftr_reg(SYS_ID_AA64DFR0_EL1);
> > -               u64 pfr = read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1);
> > +               u64 dfr = __read_id_reg(vcpu, SYS_ID_AA64DFR0_EL1);
> > +               u64 pfr = __read_id_reg(vcpu, SYS_ID_AA64PFR0_EL1);
> >                 u32 el3 = !!cpuid_feature_extract_unsigned_field(pfr, ID_AA64PFR0_EL3_SHIFT);
> >
> >                 p->regval = ((((dfr >> ID_AA64DFR0_WRPS_SHIFT) & 0xf) << 28) |
> > @@ -2850,3 +2851,30 @@ void kvm_sys_reg_table_init(void)
> >         /* Clear all higher bits. */
> >         cache_levels &= (1 << (i*3))-1;
> >  }
> > +
> > +/*
> > + * Set the guest's ID registers that are defined in sys_reg_descs[]
> > + * with ID_SANITISED() to the host's sanitized value.
> > + */
> > +void set_default_id_regs(struct kvm *kvm)
> > +{
> > +       int i;
> > +       u32 id;
> > +       const struct sys_reg_desc *rd;
> > +       u64 val;
> > +
> > +       for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
> > +               rd = &sys_reg_descs[i];
> > +               if (rd->access != access_id_reg)
> > +                       /* Not ID register, or hidden/reserved ID register */
> > +                       continue;
> > +
> > +               id = reg_to_encoding(rd);
> > +               if (WARN_ON_ONCE(!is_id_reg(id)))
> > +                       /* Shouldn't happen */
> > +                       continue;
> > +
> > +               val = read_sanitised_ftr_reg(id);
> > +               kvm->arch.id_regs[IDREG_IDX(id)] = val;
> > +       }
> > +}
> > --
> > 2.34.1.448.ga2b2bfdf31-goog
> >
> > _______________________________________________
> > kvmarm mailing list
> > kvmarm@lists.cs.columbia.edu
> > https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 02/26] KVM: arm64: Save ID registers' sanitized value per guest
@ 2022-02-09  2:26       ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-02-09  2:26 UTC (permalink / raw)
  To: Fuad Tabba
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, Linux ARM

Hi Fuad,

Sorry for the late reply.
I've noticed that I didn't reply to the comments in this mail.

On Mon, Jan 24, 2022 at 8:22 AM Fuad Tabba <tabba@google.com> wrote:
>
> Hi Reiji,
>
> On Thu, Jan 6, 2022 at 4:28 AM Reiji Watanabe <reijiw@google.com> wrote:
> >
> > Introduce id_regs[] in kvm_arch as a storage of guest's ID registers,
> > and save ID registers' sanitized value in the array at KVM_CREATE_VM.
> > Use the saved ones when ID registers are read by the guest or
> > userspace (via KVM_GET_ONE_REG).
> >
> > Signed-off-by: Reiji Watanabe <reijiw@google.com>
> > ---
> >  arch/arm64/include/asm/kvm_host.h | 16 ++++++++
> >  arch/arm64/kvm/arm.c              |  1 +
> >  arch/arm64/kvm/sys_regs.c         | 62 ++++++++++++++++++++++---------
> >  3 files changed, 62 insertions(+), 17 deletions(-)
> >
> > diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> > index 2a5f7f38006f..c789a0137f58 100644
> > --- a/arch/arm64/include/asm/kvm_host.h
> > +++ b/arch/arm64/include/asm/kvm_host.h
> > @@ -102,6 +102,17 @@ struct kvm_s2_mmu {
> >  struct kvm_arch_memory_slot {
> >  };
> >
> > +/*
> > + * (Op0, Op1, CRn, CRm, Op2) of ID registers is (3, 0, 0, crm, op2),
> > + * where 0<=crm<8, 0<=op2<8.
> > + */
> > +#define KVM_ARM_ID_REG_MAX_NUM 64
> > +#define IDREG_IDX(id)          ((sys_reg_CRm(id) << 3) | sys_reg_Op2(id))
> > +#define is_id_reg(id)  \
> > +       (sys_reg_Op0(id) == 3 && sys_reg_Op1(id) == 0 &&        \
> > +        sys_reg_CRn(id) == 0 && sys_reg_CRm(id) >= 0 &&        \
> > +        sys_reg_CRm(id) < 8)
> > +
>
> This is consistent with the Arm ARM "Table D12-2 System instruction
> encodings for non-Debug System register accesses".
>
> Minor nit, would it be better to have IDREG_IDX and is_id_reg in
> arch/arm64/kvm/sys_regs.h, since other similar and related ones are
> there?

I will move is_id_reg in sys_regs.c as it is used only in the file.
I will keep IDREG_IDX in arch/arm64/kvm/sys_regs.h as IDREG_IDX is
used to get an index of kvm_arch.id_regs[], which is defined in
kvm_host.h.

>
> >  struct kvm_arch {
> >         struct kvm_s2_mmu mmu;
> >
> > @@ -137,6 +148,9 @@ struct kvm_arch {
> >
> >         /* Memory Tagging Extension enabled for the guest */
> >         bool mte_enabled;
> > +
> > +       /* ID registers for the guest. */
> > +       u64 id_regs[KVM_ARM_ID_REG_MAX_NUM];
> >  };
> >
> >  struct kvm_vcpu_fault_info {
> > @@ -734,6 +748,8 @@ int kvm_arm_vcpu_arch_has_attr(struct kvm_vcpu *vcpu,
> >  long kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
> >                                 struct kvm_arm_copy_mte_tags *copy_tags);
> >
> > +void set_default_id_regs(struct kvm *kvm);
> > +
> >  /* Guest/host FPSIMD coordination helpers */
> >  int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu);
> >  void kvm_arch_vcpu_load_fp(struct kvm_vcpu *vcpu);
> > diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> > index e4727dc771bf..5f497a0af254 100644
> > --- a/arch/arm64/kvm/arm.c
> > +++ b/arch/arm64/kvm/arm.c
> > @@ -156,6 +156,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
> >         kvm->arch.max_vcpus = kvm_arm_default_max_vcpus();
> >
> >         set_default_spectre(kvm);
> > +       set_default_id_regs(kvm);
> >
> >         return ret;
> >  out_free_stage2_pgd:
> > diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> > index e3ec1a44f94d..80dc62f98ef0 100644
> > --- a/arch/arm64/kvm/sys_regs.c
> > +++ b/arch/arm64/kvm/sys_regs.c
> > @@ -33,6 +33,8 @@
> >
> >  #include "trace.h"
> >
> > +static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id);
> > +
> >  /*
> >   * All of this file is extremely similar to the ARM coproc.c, but the
> >   * types are different. My gut feeling is that it should be pretty
> > @@ -273,7 +275,7 @@ static bool trap_loregion(struct kvm_vcpu *vcpu,
> >                           struct sys_reg_params *p,
> >                           const struct sys_reg_desc *r)
> >  {
> > -       u64 val = read_sanitised_ftr_reg(SYS_ID_AA64MMFR1_EL1);
> > +       u64 val = __read_id_reg(vcpu, SYS_ID_AA64MMFR1_EL1);
> >         u32 sr = reg_to_encoding(r);
> >
> >         if (!(val & (0xfUL << ID_AA64MMFR1_LOR_SHIFT))) {
> > @@ -1059,17 +1061,9 @@ static bool access_arch_timer(struct kvm_vcpu *vcpu,
> >         return true;
> >  }
> >
> > -/* Read a sanitised cpufeature ID register by sys_reg_desc */
> > -static u64 read_id_reg(const struct kvm_vcpu *vcpu,
> > -               struct sys_reg_desc const *r, bool raz)
> > +static u64 __read_id_reg(const struct kvm_vcpu *vcpu, u32 id)
> >  {
> > -       u32 id = reg_to_encoding(r);
> > -       u64 val;
> > -
> > -       if (raz)
> > -               return 0;
> > -
> > -       val = read_sanitised_ftr_reg(id);
> > +       u64 val = vcpu->kvm->arch.id_regs[IDREG_IDX(id)];
> >
> >         switch (id) {
> >         case SYS_ID_AA64PFR0_EL1:
> > @@ -1119,6 +1113,14 @@ static u64 read_id_reg(const struct kvm_vcpu *vcpu,
> >         return val;
> >  }
> >
> > +static u64 read_id_reg(const struct kvm_vcpu *vcpu,
> > +                      struct sys_reg_desc const *r, bool raz)
> > +{
> > +       u32 id = reg_to_encoding(r);
> > +
> > +       return raz ? 0 : __read_id_reg(vcpu, id);
> > +}
> > +
> >  static unsigned int id_visibility(const struct kvm_vcpu *vcpu,
> >                                   const struct sys_reg_desc *r)
> >  {
> > @@ -1223,9 +1225,8 @@ static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
> >  /*
> >   * cpufeature ID register user accessors
> >   *
> > - * For now, these registers are immutable for userspace, so no values
> > - * are stored, and for set_id_reg() we don't allow the effective value
> > - * to be changed.
> > + * For now, these registers are immutable for userspace, so for set_id_reg()
> > + * we don't allow the effective value to be changed.
> >   */
> >  static int __get_id_reg(const struct kvm_vcpu *vcpu,
> >                         const struct sys_reg_desc *rd, void __user *uaddr,
> > @@ -1237,7 +1238,7 @@ static int __get_id_reg(const struct kvm_vcpu *vcpu,
> >         return reg_to_user(uaddr, &val, id);
> >  }
> >
> > -static int __set_id_reg(const struct kvm_vcpu *vcpu,
> > +static int __set_id_reg(struct kvm_vcpu *vcpu,
> >                         const struct sys_reg_desc *rd, void __user *uaddr,
> >                         bool raz)
>
> Minor nit: why remove the const in this patch? This is required for a
> future patch but not for this one.

Thank you for catching this. I will fix this.

Regards,
Reiji

>
>
> >  {
> > @@ -1837,8 +1838,8 @@ static bool trap_dbgdidr(struct kvm_vcpu *vcpu,
> >         if (p->is_write) {
> >                 return ignore_write(vcpu, p);
> >         } else {
> > -               u64 dfr = read_sanitised_ftr_reg(SYS_ID_AA64DFR0_EL1);
> > -               u64 pfr = read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1);
> > +               u64 dfr = __read_id_reg(vcpu, SYS_ID_AA64DFR0_EL1);
> > +               u64 pfr = __read_id_reg(vcpu, SYS_ID_AA64PFR0_EL1);
> >                 u32 el3 = !!cpuid_feature_extract_unsigned_field(pfr, ID_AA64PFR0_EL3_SHIFT);
> >
> >                 p->regval = ((((dfr >> ID_AA64DFR0_WRPS_SHIFT) & 0xf) << 28) |
> > @@ -2850,3 +2851,30 @@ void kvm_sys_reg_table_init(void)
> >         /* Clear all higher bits. */
> >         cache_levels &= (1 << (i*3))-1;
> >  }
> > +
> > +/*
> > + * Set the guest's ID registers that are defined in sys_reg_descs[]
> > + * with ID_SANITISED() to the host's sanitized value.
> > + */
> > +void set_default_id_regs(struct kvm *kvm)
> > +{
> > +       int i;
> > +       u32 id;
> > +       const struct sys_reg_desc *rd;
> > +       u64 val;
> > +
> > +       for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
> > +               rd = &sys_reg_descs[i];
> > +               if (rd->access != access_id_reg)
> > +                       /* Not ID register, or hidden/reserved ID register */
> > +                       continue;
> > +
> > +               id = reg_to_encoding(rd);
> > +               if (WARN_ON_ONCE(!is_id_reg(id)))
> > +                       /* Shouldn't happen */
> > +                       continue;
> > +
> > +               val = read_sanitised_ftr_reg(id);
> > +               kvm->arch.id_regs[IDREG_IDX(id)] = val;
> > +       }
> > +}
> > --
> > 2.34.1.448.ga2b2bfdf31-goog
> >
> > _______________________________________________
> > kvmarm mailing list
> > kvmarm@lists.cs.columbia.edu
> > https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 23/26] KVM: arm64: Trap disabled features of ID_AA64MMFR1_EL1
  2022-01-28  5:43       ` Reiji Watanabe
  (?)
@ 2022-02-09  4:51         ` Reiji Watanabe
  -1 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-02-09  4:51 UTC (permalink / raw)
  To: Fuad Tabba
  Cc: kvm, Marc Zyngier, Peter Shier, Paolo Bonzini, Will Deacon,
	kvmarm, Linux ARM

Hi Fuad,

> > The series might be missing an entry for ID_AA64MMFR0_EL1, Debug
> > Communications Channel registers, ID_AA64MMFR0_FGT -> MDCR_EL2_TDCC.

Looking at Arm ARM, it appears any of the registers that can be
trapped by MDCR_EL2_TDCC are present even when FEAT_FGT is not
implemented (I understand MDCR_EL2_TDCC is available when FEAT_FGT
is implemented though).  So, this is not something that the
framework is trying to address.

Thanks,
Reiji

>
> I will add them in v5 series.
> Thank you so much for all the review comments!
>
> Thanks,
> Reiji
>
>
> >
> > Cheers,
> > /fuad
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 23/26] KVM: arm64: Trap disabled features of ID_AA64MMFR1_EL1
@ 2022-02-09  4:51         ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-02-09  4:51 UTC (permalink / raw)
  To: Fuad Tabba
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, Linux ARM

Hi Fuad,

> > The series might be missing an entry for ID_AA64MMFR0_EL1, Debug
> > Communications Channel registers, ID_AA64MMFR0_FGT -> MDCR_EL2_TDCC.

Looking at Arm ARM, it appears any of the registers that can be
trapped by MDCR_EL2_TDCC are present even when FEAT_FGT is not
implemented (I understand MDCR_EL2_TDCC is available when FEAT_FGT
is implemented though).  So, this is not something that the
framework is trying to address.

Thanks,
Reiji

>
> I will add them in v5 series.
> Thank you so much for all the review comments!
>
> Thanks,
> Reiji
>
>
> >
> > Cheers,
> > /fuad

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 23/26] KVM: arm64: Trap disabled features of ID_AA64MMFR1_EL1
@ 2022-02-09  4:51         ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-02-09  4:51 UTC (permalink / raw)
  To: Fuad Tabba
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, Linux ARM

Hi Fuad,

> > The series might be missing an entry for ID_AA64MMFR0_EL1, Debug
> > Communications Channel registers, ID_AA64MMFR0_FGT -> MDCR_EL2_TDCC.

Looking at Arm ARM, it appears any of the registers that can be
trapped by MDCR_EL2_TDCC are present even when FEAT_FGT is not
implemented (I understand MDCR_EL2_TDCC is available when FEAT_FGT
is implemented though).  So, this is not something that the
framework is trying to address.

Thanks,
Reiji

>
> I will add them in v5 series.
> Thank you so much for all the review comments!
>
> Thanks,
> Reiji
>
>
> >
> > Cheers,
> > /fuad

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 20/26] KVM: arm64: Trap disabled features of ID_AA64PFR0_EL1
  2022-02-01 14:14         ` Fuad Tabba
  (?)
@ 2022-02-10  4:15           ` Reiji Watanabe
  -1 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-02-10  4:15 UTC (permalink / raw)
  To: Fuad Tabba
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, Linux ARM

Hi Fuad,

On Tue, Feb 1, 2022 at 6:14 AM Fuad Tabba <tabba@google.com> wrote:
>
> Hi Reiji,
>
> ...
>
> > > > +static void feature_amu_trap_activate(struct kvm_vcpu *vcpu)
> > > > +{
> > > > +       feature_trap_activate(vcpu, VCPU_CPTR_EL2, CPTR_EL2_TAM, 0);
> > >
> > > Covers the CPTR flags for AMU, but as you mentioned, does not
> > > explicitly clear HCR_AMVOFFEN.
> >
> > In my understanding, clearing HCR_EL2.AMVOFFEN is not necessary as
> > CPTR_EL2.TAM == 1 traps the guest's accessing AMEVCNTR0<n>_EL0 and
> > AMEVCNTR1<n>_EL0 anyway (HCR_EL2.AMVOFFEN doesn't matter).
> > (Or is my understanding wrong ??)
>
> You're right. However, I think they should be cleared first for
> completeness. Also, if I understand correctly, AMVOFFEN is about
> enabling and disabling virtualization of the registers, making
> indirect reads of the virtual offset registers as zero, so it's not
> just about trapping.

I understand that AMVOFFEN is making indirect reads of the
virtual offset registers as zero.  But, in my understanding,
enabling and disabling virtualization of the registers doesn't
matter as long as CPTR_EL2.TAM == 1 (a value of HCR_EL2.AMVOFFEN
doesn't change any behavior because the virtual offset registers
won't be used). So, I'm not too keen on adding that so far.

Thanks,
Reiji

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 20/26] KVM: arm64: Trap disabled features of ID_AA64PFR0_EL1
@ 2022-02-10  4:15           ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-02-10  4:15 UTC (permalink / raw)
  To: Fuad Tabba
  Cc: kvm, Marc Zyngier, Peter Shier, Paolo Bonzini, Will Deacon,
	kvmarm, Linux ARM

Hi Fuad,

On Tue, Feb 1, 2022 at 6:14 AM Fuad Tabba <tabba@google.com> wrote:
>
> Hi Reiji,
>
> ...
>
> > > > +static void feature_amu_trap_activate(struct kvm_vcpu *vcpu)
> > > > +{
> > > > +       feature_trap_activate(vcpu, VCPU_CPTR_EL2, CPTR_EL2_TAM, 0);
> > >
> > > Covers the CPTR flags for AMU, but as you mentioned, does not
> > > explicitly clear HCR_AMVOFFEN.
> >
> > In my understanding, clearing HCR_EL2.AMVOFFEN is not necessary as
> > CPTR_EL2.TAM == 1 traps the guest's accessing AMEVCNTR0<n>_EL0 and
> > AMEVCNTR1<n>_EL0 anyway (HCR_EL2.AMVOFFEN doesn't matter).
> > (Or is my understanding wrong ??)
>
> You're right. However, I think they should be cleared first for
> completeness. Also, if I understand correctly, AMVOFFEN is about
> enabling and disabling virtualization of the registers, making
> indirect reads of the virtual offset registers as zero, so it's not
> just about trapping.

I understand that AMVOFFEN is making indirect reads of the
virtual offset registers as zero.  But, in my understanding,
enabling and disabling virtualization of the registers doesn't
matter as long as CPTR_EL2.TAM == 1 (a value of HCR_EL2.AMVOFFEN
doesn't change any behavior because the virtual offset registers
won't be used). So, I'm not too keen on adding that so far.

Thanks,
Reiji
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 20/26] KVM: arm64: Trap disabled features of ID_AA64PFR0_EL1
@ 2022-02-10  4:15           ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-02-10  4:15 UTC (permalink / raw)
  To: Fuad Tabba
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, Linux ARM

Hi Fuad,

On Tue, Feb 1, 2022 at 6:14 AM Fuad Tabba <tabba@google.com> wrote:
>
> Hi Reiji,
>
> ...
>
> > > > +static void feature_amu_trap_activate(struct kvm_vcpu *vcpu)
> > > > +{
> > > > +       feature_trap_activate(vcpu, VCPU_CPTR_EL2, CPTR_EL2_TAM, 0);
> > >
> > > Covers the CPTR flags for AMU, but as you mentioned, does not
> > > explicitly clear HCR_AMVOFFEN.
> >
> > In my understanding, clearing HCR_EL2.AMVOFFEN is not necessary as
> > CPTR_EL2.TAM == 1 traps the guest's accessing AMEVCNTR0<n>_EL0 and
> > AMEVCNTR1<n>_EL0 anyway (HCR_EL2.AMVOFFEN doesn't matter).
> > (Or is my understanding wrong ??)
>
> You're right. However, I think they should be cleared first for
> completeness. Also, if I understand correctly, AMVOFFEN is about
> enabling and disabling virtualization of the registers, making
> indirect reads of the virtual offset registers as zero, so it's not
> just about trapping.

I understand that AMVOFFEN is making indirect reads of the
virtual offset registers as zero.  But, in my understanding,
enabling and disabling virtualization of the registers doesn't
matter as long as CPTR_EL2.TAM == 1 (a value of HCR_EL2.AMVOFFEN
doesn't change any behavior because the virtual offset registers
won't be used). So, I'm not too keen on adding that so far.

Thanks,
Reiji

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 04/26] KVM: arm64: Make ID_AA64PFR0_EL1 writable
  2022-02-01 14:14         ` Fuad Tabba
  (?)
@ 2022-02-10  5:33           ` Reiji Watanabe
  -1 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-02-10  5:33 UTC (permalink / raw)
  To: Fuad Tabba
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, Linux ARM

Hi Fuad,

On Tue, Feb 1, 2022 at 6:14 AM Fuad Tabba <tabba@google.com> wrote:
>
> Hi Reiji,
>
> ...
>
> > > > diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c
> > > > index 0a06d0648970..28d9bf0e178c 100644
> > > > --- a/arch/arm64/kvm/vgic/vgic-init.c
> > > > +++ b/arch/arm64/kvm/vgic/vgic-init.c
> > > > @@ -116,6 +116,11 @@ int kvm_vgic_create(struct kvm *kvm, u32 type)
> > > >         else
> > > >                 INIT_LIST_HEAD(&kvm->arch.vgic.rd_regions);
> > > >
> > > > +       if (type == KVM_DEV_TYPE_ARM_VGIC_V3)
> > > > +               /* Set ID_AA64PFR0_EL1.GIC to 1 */
> > > > +               (void)kvm_set_id_reg_feature(kvm, SYS_ID_AA64PFR0_EL1,
> > > > +                                    ID_AA64PFR0_GIC3, ID_AA64PFR0_GIC_SHIFT);
> > > > +
> > >
> > > If this fails wouldn't it be better to return the error?
> >
> > This should never fail because kvm_vgic_create() prevents
> > userspace from running the first KVM_RUN for any vCPUs
> > while it calls kvm_set_id_reg_feature().
> > So, I am thinking of adding WARN_ON_ONCE() for the return value
> > rather than adding an unnecessary error handling.
>
> Consider this to be a nit from my part, as I don't have any strong
> feelings about this, but kvm_vgic_create() already returns an error if
> there's a problem. So I don't think that that would be imposing any
> additional error handling.

That's true.  But, since this failure case cannot be caused by userspace
(but KVM's bug), I would still prefer using WARN_ON_ONCE.

Thanks,
Reiji

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 04/26] KVM: arm64: Make ID_AA64PFR0_EL1 writable
@ 2022-02-10  5:33           ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-02-10  5:33 UTC (permalink / raw)
  To: Fuad Tabba
  Cc: kvm, Marc Zyngier, Peter Shier, Paolo Bonzini, Will Deacon,
	kvmarm, Linux ARM

Hi Fuad,

On Tue, Feb 1, 2022 at 6:14 AM Fuad Tabba <tabba@google.com> wrote:
>
> Hi Reiji,
>
> ...
>
> > > > diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c
> > > > index 0a06d0648970..28d9bf0e178c 100644
> > > > --- a/arch/arm64/kvm/vgic/vgic-init.c
> > > > +++ b/arch/arm64/kvm/vgic/vgic-init.c
> > > > @@ -116,6 +116,11 @@ int kvm_vgic_create(struct kvm *kvm, u32 type)
> > > >         else
> > > >                 INIT_LIST_HEAD(&kvm->arch.vgic.rd_regions);
> > > >
> > > > +       if (type == KVM_DEV_TYPE_ARM_VGIC_V3)
> > > > +               /* Set ID_AA64PFR0_EL1.GIC to 1 */
> > > > +               (void)kvm_set_id_reg_feature(kvm, SYS_ID_AA64PFR0_EL1,
> > > > +                                    ID_AA64PFR0_GIC3, ID_AA64PFR0_GIC_SHIFT);
> > > > +
> > >
> > > If this fails wouldn't it be better to return the error?
> >
> > This should never fail because kvm_vgic_create() prevents
> > userspace from running the first KVM_RUN for any vCPUs
> > while it calls kvm_set_id_reg_feature().
> > So, I am thinking of adding WARN_ON_ONCE() for the return value
> > rather than adding an unnecessary error handling.
>
> Consider this to be a nit from my part, as I don't have any strong
> feelings about this, but kvm_vgic_create() already returns an error if
> there's a problem. So I don't think that that would be imposing any
> additional error handling.

That's true.  But, since this failure case cannot be caused by userspace
(but KVM's bug), I would still prefer using WARN_ON_ONCE.

Thanks,
Reiji
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

^ permalink raw reply	[flat|nested] 201+ messages in thread

* Re: [RFC PATCH v4 04/26] KVM: arm64: Make ID_AA64PFR0_EL1 writable
@ 2022-02-10  5:33           ` Reiji Watanabe
  0 siblings, 0 replies; 201+ messages in thread
From: Reiji Watanabe @ 2022-02-10  5:33 UTC (permalink / raw)
  To: Fuad Tabba
  Cc: Marc Zyngier, kvmarm, kvm, Will Deacon, Peter Shier,
	Paolo Bonzini, Linux ARM

Hi Fuad,

On Tue, Feb 1, 2022 at 6:14 AM Fuad Tabba <tabba@google.com> wrote:
>
> Hi Reiji,
>
> ...
>
> > > > diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c
> > > > index 0a06d0648970..28d9bf0e178c 100644
> > > > --- a/arch/arm64/kvm/vgic/vgic-init.c
> > > > +++ b/arch/arm64/kvm/vgic/vgic-init.c
> > > > @@ -116,6 +116,11 @@ int kvm_vgic_create(struct kvm *kvm, u32 type)
> > > >         else
> > > >                 INIT_LIST_HEAD(&kvm->arch.vgic.rd_regions);
> > > >
> > > > +       if (type == KVM_DEV_TYPE_ARM_VGIC_V3)
> > > > +               /* Set ID_AA64PFR0_EL1.GIC to 1 */
> > > > +               (void)kvm_set_id_reg_feature(kvm, SYS_ID_AA64PFR0_EL1,
> > > > +                                    ID_AA64PFR0_GIC3, ID_AA64PFR0_GIC_SHIFT);
> > > > +
> > >
> > > If this fails wouldn't it be better to return the error?
> >
> > This should never fail because kvm_vgic_create() prevents
> > userspace from running the first KVM_RUN for any vCPUs
> > while it calls kvm_set_id_reg_feature().
> > So, I am thinking of adding WARN_ON_ONCE() for the return value
> > rather than adding an unnecessary error handling.
>
> Consider this to be a nit from my part, as I don't have any strong
> feelings about this, but kvm_vgic_create() already returns an error if
> there's a problem. So I don't think that that would be imposing any
> additional error handling.

That's true.  But, since this failure case cannot be caused by userspace
(but KVM's bug), I would still prefer using WARN_ON_ONCE.

Thanks,
Reiji

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 201+ messages in thread

end of thread, other threads:[~2022-02-10  5:35 UTC | newest]

Thread overview: 201+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-01-06  4:26 [RFC PATCH v4 00/26] KVM: arm64: Make CPU ID registers writable by userspace Reiji Watanabe
2022-01-06  4:26 ` Reiji Watanabe
2022-01-06  4:26 ` Reiji Watanabe
2022-01-06  4:26 ` [RFC PATCH v4 01/26] KVM: arm64: Introduce a validation function for an ID register Reiji Watanabe
2022-01-06  4:26   ` Reiji Watanabe
2022-01-06  4:26   ` Reiji Watanabe
2022-01-07  7:12   ` Reiji Watanabe
2022-01-07  7:12     ` Reiji Watanabe
2022-01-07  7:12     ` Reiji Watanabe
2022-01-24 16:20   ` Fuad Tabba
2022-01-24 16:20     ` Fuad Tabba
2022-01-24 16:20     ` Fuad Tabba
2022-01-26  6:04     ` Reiji Watanabe
2022-01-26  6:04       ` Reiji Watanabe
2022-01-26  6:04       ` Reiji Watanabe
2022-02-01 14:13       ` Fuad Tabba
2022-02-01 14:13         ` Fuad Tabba
2022-02-01 14:13         ` Fuad Tabba
2022-02-02  6:46         ` Reiji Watanabe
2022-02-02  6:46           ` Reiji Watanabe
2022-02-02  6:46           ` Reiji Watanabe
2022-01-26  4:30   ` Ricardo Koller
2022-01-26  4:30     ` Ricardo Koller
2022-01-26  4:30     ` Ricardo Koller
2022-01-28  6:01     ` Reiji Watanabe
2022-01-28  6:01       ` Reiji Watanabe
2022-01-28  6:01       ` Reiji Watanabe
2022-01-06  4:26 ` [RFC PATCH v4 02/26] KVM: arm64: Save ID registers' sanitized value per guest Reiji Watanabe
2022-01-06  4:26   ` Reiji Watanabe
2022-01-06  4:26   ` Reiji Watanabe
2022-01-24 16:21   ` Fuad Tabba
2022-01-24 16:21     ` Fuad Tabba
2022-01-24 16:21     ` Fuad Tabba
2022-02-09  2:26     ` Reiji Watanabe
2022-02-09  2:26       ` Reiji Watanabe
2022-02-09  2:26       ` Reiji Watanabe
2022-01-26  5:22   ` Ricardo Koller
2022-01-26  5:22     ` Ricardo Koller
2022-01-26  5:22     ` Ricardo Koller
2022-01-28  6:24     ` Reiji Watanabe
2022-01-28  6:24       ` Reiji Watanabe
2022-01-28  6:24       ` Reiji Watanabe
2022-01-28 19:27       ` Ricardo Koller
2022-01-28 19:27         ` Ricardo Koller
2022-01-28 19:27         ` Ricardo Koller
2022-01-29  5:52         ` Reiji Watanabe
2022-01-29  5:52           ` Reiji Watanabe
2022-01-29  5:52           ` Reiji Watanabe
2022-01-31  3:40           ` Ricardo Koller
2022-01-31  3:40             ` Ricardo Koller
2022-01-31  3:40             ` Ricardo Koller
2022-02-01  6:00             ` Reiji Watanabe
2022-02-01  6:00               ` Reiji Watanabe
2022-02-01  6:00               ` Reiji Watanabe
2022-02-01 18:38               ` Ricardo Koller
2022-02-01 18:38                 ` Ricardo Koller
2022-02-01 18:38                 ` Ricardo Koller
2022-02-03  6:31                 ` Reiji Watanabe
2022-02-03  6:31                   ` Reiji Watanabe
2022-02-03  6:31                   ` Reiji Watanabe
2022-02-04 14:41                   ` Ricardo Koller
2022-02-04 14:41                     ` Ricardo Koller
2022-02-04 14:41                     ` Ricardo Koller
2022-01-06  4:26 ` [RFC PATCH v4 03/26] KVM: arm64: Introduce struct id_reg_info Reiji Watanabe
2022-01-06  4:26   ` Reiji Watanabe
2022-01-06  4:26   ` Reiji Watanabe
2022-01-24 16:28   ` Fuad Tabba
2022-01-24 16:28     ` Fuad Tabba
2022-01-24 16:28     ` Fuad Tabba
2022-01-26  6:46     ` Reiji Watanabe
2022-01-26  6:46       ` Reiji Watanabe
2022-01-26  6:46       ` Reiji Watanabe
2022-02-01 14:13       ` Fuad Tabba
2022-02-01 14:13         ` Fuad Tabba
2022-02-01 14:13         ` Fuad Tabba
2022-01-06  4:26 ` [RFC PATCH v4 04/26] KVM: arm64: Make ID_AA64PFR0_EL1 writable Reiji Watanabe
2022-01-06  4:26   ` Reiji Watanabe
2022-01-06  4:26   ` Reiji Watanabe
2022-01-24 16:51   ` Fuad Tabba
2022-01-24 16:51     ` Fuad Tabba
2022-01-24 16:51     ` Fuad Tabba
2022-01-27  4:01     ` Reiji Watanabe
2022-01-27  4:01       ` Reiji Watanabe
2022-01-27  4:01       ` Reiji Watanabe
2022-02-01 14:14       ` Fuad Tabba
2022-02-01 14:14         ` Fuad Tabba
2022-02-01 14:14         ` Fuad Tabba
2022-02-10  5:33         ` Reiji Watanabe
2022-02-10  5:33           ` Reiji Watanabe
2022-02-10  5:33           ` Reiji Watanabe
2022-01-06  4:26 ` [RFC PATCH v4 05/26] KVM: arm64: Make ID_AA64PFR1_EL1 writable Reiji Watanabe
2022-01-06  4:26   ` Reiji Watanabe
2022-01-06  4:26   ` Reiji Watanabe
2022-01-06  4:26 ` [RFC PATCH v4 06/26] KVM: arm64: Make ID_AA64ISAR0_EL1 writable Reiji Watanabe
2022-01-06  4:26   ` Reiji Watanabe
2022-01-06  4:26   ` Reiji Watanabe
2022-01-06  4:26 ` [RFC PATCH v4 07/26] KVM: arm64: Make ID_AA64ISAR1_EL1 writable Reiji Watanabe
2022-01-06  4:26   ` Reiji Watanabe
2022-01-06  4:26   ` Reiji Watanabe
2022-01-06  4:26 ` [RFC PATCH v4 08/26] KVM: arm64: Make ID_AA64MMFR0_EL1 writable Reiji Watanabe
2022-01-06  4:26   ` Reiji Watanabe
2022-01-06  4:26   ` Reiji Watanabe
2022-01-06  4:26 ` [RFC PATCH v4 09/26] KVM: arm64: Hide IMPLEMENTATION DEFINED PMU support for the guest Reiji Watanabe
2022-01-06  4:26   ` Reiji Watanabe
2022-01-06  4:26   ` Reiji Watanabe
2022-01-06  4:26 ` [RFC PATCH v4 10/26] KVM: arm64: Make ID_AA64DFR0_EL1 writable Reiji Watanabe
2022-01-06  4:26   ` Reiji Watanabe
2022-01-06  4:26   ` Reiji Watanabe
2022-01-06  4:26 ` [RFC PATCH v4 11/26] KVM: arm64: Make ID_DFR0_EL1 writable Reiji Watanabe
2022-01-06  4:26   ` Reiji Watanabe
2022-01-06  4:26   ` Reiji Watanabe
2022-01-06  4:26 ` [RFC PATCH v4 12/26] KVM: arm64: Make MVFR1_EL1 writable Reiji Watanabe
2022-01-06  4:26   ` Reiji Watanabe
2022-01-06  4:26   ` Reiji Watanabe
2022-01-06  4:26 ` [RFC PATCH v4 13/26] KVM: arm64: Make ID registers without id_reg_info writable Reiji Watanabe
2022-01-06  4:26   ` Reiji Watanabe
2022-01-06  4:26   ` Reiji Watanabe
2022-01-06  4:26 ` [RFC PATCH v4 14/26] KVM: arm64: Add consistency checking for frac fields of ID registers Reiji Watanabe
2022-01-06  4:26   ` Reiji Watanabe
2022-01-06  4:26   ` Reiji Watanabe
2022-01-24 17:00   ` Fuad Tabba
2022-01-24 17:00     ` Fuad Tabba
2022-01-24 17:00     ` Fuad Tabba
2022-01-27  5:03     ` Reiji Watanabe
2022-01-27  5:03       ` Reiji Watanabe
2022-01-27  5:03       ` Reiji Watanabe
2022-01-06  4:26 ` [RFC PATCH v4 15/26] KVM: arm64: Introduce KVM_CAP_ARM_ID_REG_CONFIGURABLE capability Reiji Watanabe
2022-01-06  4:26   ` Reiji Watanabe
2022-01-06  4:26   ` Reiji Watanabe
2022-01-06  4:26 ` [RFC PATCH v4 16/26] KVM: arm64: Add kunit test for ID register validation Reiji Watanabe
2022-01-06  4:26   ` Reiji Watanabe
2022-01-06  4:26   ` Reiji Watanabe
2022-01-06  4:26 ` [RFC PATCH v4 17/26] KVM: arm64: Use vcpu->arch cptr_el2 to track value of cptr_el2 for VHE Reiji Watanabe
2022-01-06  4:26   ` Reiji Watanabe
2022-01-06  4:26   ` Reiji Watanabe
2022-01-06  4:27 ` [RFC PATCH v4 18/26] KVM: arm64: Use vcpu->arch.mdcr_el2 to track value of mdcr_el2 Reiji Watanabe
2022-01-06  4:27   ` Reiji Watanabe
2022-01-06  4:27   ` Reiji Watanabe
2022-01-06  4:27 ` [RFC PATCH v4 19/26] KVM: arm64: Introduce framework to trap disabled features Reiji Watanabe
2022-01-06  4:27   ` Reiji Watanabe
2022-01-06  4:27   ` Reiji Watanabe
2022-01-06  4:27 ` [RFC PATCH v4 20/26] KVM: arm64: Trap disabled features of ID_AA64PFR0_EL1 Reiji Watanabe
2022-01-06  4:27   ` Reiji Watanabe
2022-01-06  4:27   ` Reiji Watanabe
2022-01-24 17:16   ` Fuad Tabba
2022-01-24 17:16     ` Fuad Tabba
2022-01-24 17:16     ` Fuad Tabba
2022-01-27  7:19     ` Reiji Watanabe
2022-01-27  7:19       ` Reiji Watanabe
2022-01-27  7:19       ` Reiji Watanabe
2022-02-01 14:14       ` Fuad Tabba
2022-02-01 14:14         ` Fuad Tabba
2022-02-01 14:14         ` Fuad Tabba
2022-02-10  4:15         ` Reiji Watanabe
2022-02-10  4:15           ` Reiji Watanabe
2022-02-10  4:15           ` Reiji Watanabe
2022-01-06  4:27 ` [RFC PATCH v4 21/26] KVM: arm64: Trap disabled features of ID_AA64PFR1_EL1 Reiji Watanabe
2022-01-06  4:27   ` Reiji Watanabe
2022-01-06  4:27   ` Reiji Watanabe
2022-01-06  4:27 ` [RFC PATCH v4 22/26] KVM: arm64: Trap disabled features of ID_AA64DFR0_EL1 Reiji Watanabe
2022-01-06  4:27   ` Reiji Watanabe
2022-01-06  4:27   ` Reiji Watanabe
2022-01-24 17:19   ` Fuad Tabba
2022-01-24 17:19     ` Fuad Tabba
2022-01-24 17:19     ` Fuad Tabba
2022-01-28  5:40     ` Reiji Watanabe
2022-01-28  5:40       ` Reiji Watanabe
2022-01-28  5:40       ` Reiji Watanabe
2022-01-06  4:27 ` [RFC PATCH v4 23/26] KVM: arm64: Trap disabled features of ID_AA64MMFR1_EL1 Reiji Watanabe
2022-01-06  4:27   ` Reiji Watanabe
2022-01-06  4:27   ` Reiji Watanabe
2022-01-24 17:37   ` Fuad Tabba
2022-01-24 17:37     ` Fuad Tabba
2022-01-24 17:37     ` Fuad Tabba
2022-01-28  5:43     ` Reiji Watanabe
2022-01-28  5:43       ` Reiji Watanabe
2022-01-28  5:43       ` Reiji Watanabe
2022-02-09  4:51       ` Reiji Watanabe
2022-02-09  4:51         ` Reiji Watanabe
2022-02-09  4:51         ` Reiji Watanabe
2022-01-06  4:27 ` [RFC PATCH v4 24/26] KVM: arm64: Trap disabled features of ID_AA64ISAR1_EL1 Reiji Watanabe
2022-01-06  4:27   ` Reiji Watanabe
2022-01-06  4:27   ` Reiji Watanabe
2022-01-06  4:27 ` [RFC PATCH v4 25/26] KVM: arm64: Add kunit test for trap initialization Reiji Watanabe
2022-01-06  4:27   ` Reiji Watanabe
2022-01-06  4:27   ` Reiji Watanabe
2022-01-06  4:27 ` [RFC PATCH v4 26/26] KVM: arm64: selftests: Introduce id_reg_test Reiji Watanabe
2022-01-06  4:27   ` Reiji Watanabe
2022-01-06  4:27   ` Reiji Watanabe
2022-01-18  4:24 ` [RFC PATCH v4 00/26] KVM: arm64: Make CPU ID registers writable by userspace Reiji Watanabe
2022-01-18  4:24   ` Reiji Watanabe
2022-01-18  4:24   ` Reiji Watanabe
2022-01-24 16:18 ` Fuad Tabba
2022-01-24 16:18   ` Fuad Tabba
2022-01-24 16:18   ` Fuad Tabba
2022-01-25  6:31   ` Reiji Watanabe
2022-01-25  6:31     ` Reiji Watanabe
2022-01-25  6:31     ` Reiji Watanabe
2022-02-01 14:12     ` Fuad Tabba
2022-02-01 14:12       ` Fuad Tabba
2022-02-01 14:12       ` Fuad Tabba

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.