All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v5 00/10] KVM: arm64: Add support for hypercall services selection
@ 2022-04-07  1:15 ` Raghavendra Rao Ananta
  0 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-07  1:15 UTC (permalink / raw)
  To: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose
  Cc: kvm, Catalin Marinas, Peter Shier, linux-kernel, Paolo Bonzini,
	Will Deacon, kvmarm, linux-arm-kernel

Hello,

Continuing the discussion from [1], the series tries to add support
for the userspace to elect the hypercall services that it wishes
to expose to the guest, rather than the guest discovering them
unconditionally. The idea employed by the series was taken from
[1] as suggested by Marc Z.

In a broad sense, the concept is similar to the current implementation
of PSCI interface- create a 'firmware psuedo-register' to handle the
firmware revisions. The series extends this idea to all the other
hypercalls such as TRNG (True Random Number Generator), PV_TIME
(Paravirtualized Time), and PTP (Precision Time protocol).

For better categorization and future scaling, these firmware registers
are categorized based on the service call owners. Also, unlike the
existing firmware psuedo-registers, they hold the features supported
in the form of a bitmap.

During the VM initialization, the registers holds an upper-limit of
the features supported by each one of them. It's expected that the
userspace discover the features provided by each register via GET_ONE_REG,
and writeback the desired values using SET_ONE_REG. KVM allows this
modification only until the VM has started.

Some of the standard function-ids, such as ARM_SMCCC_VERSION_FUNC_ID,
need not be associated with a feature bit. For such ids, the series
introduced an allowed-list, hvc_func_default_allowed_list[], that holds
all such ids. As a result, the functions that are not elected by userspace,
or if they are not a part of this allowed-list, will be denied for when
the guests invoke them.

Older VMMs can simply ignore this interface and the hypercall services
will be exposed unconditionally to the guests, thus ensuring backward
compatibility.

The patches are based off of mainline kernel 5.18-rc1, with the selftest
patches from [2] applied.

Patch-1 factors out the non-PSCI related interface from psci.c to
hypercalls.c, as the series would extend the list in the upcoming
patches.

Patch-2 sets up the framework for the bitmap firmware psuedo-registers.
It includes read/write support for the registers, and a helper to check
if a particular hypercall service is supported for the guest.
It also adds the register KVM_REG_ARM_STD_HYP_BMAP to support ARM's
standard secure services.

Patch-3 introduces the firmware register, KVM_REG_ARM_STD_HYP_BMAP,
which holds the standard hypervisor services (such as PV_TIME).

Patch-4 introduces the firmware register, KVM_REG_ARM_VENDOR_HYP_BMAP,
which holds the vendor specific hypercall services.

Patch-5,6 Add the necessary documentation for the newly added firmware
registers.

Patch-7 imports the SMCCC definitions from linux/arm-smccc.h into tools/
for further use in selftests.

Patch-8 adds the selftest to test the guest (using 'hvc') and userspace
interfaces (SET/GET_ONE_REG).

Patch-9 adds these firmware registers into the get-reg-list selftest.

Patch-10 is unrelated to the series, but adds KVM_REG_ARM_FW_REG(3)
to base_regs[] of get-regs-list selftest for the sake of completion.

[1]: https://lore.kernel.org/kvmarm/874kbcpmlq.wl-maz@kernel.org/T/
[2]: https://lore.kernel.org/kvmarm/YUzgdbYk8BeCnHyW@google.com/

Regards,
Raghavendra

v4 -> v5:

Addressed comments by Oliver (thank you!):

- Rebased the series to accommodate ARM_SMCCC_ARCH_WORKAROUND_3
  and PSCI 1.1 changes, and capturing VM's first run.
- Removed the patches related to register scoping (v4 02/13 and
  03/13). I plan to re-introduce them in its own series.
- Dropped the patch that captures VM's first run.
- Moved the bitmap feature firmware registers to its own CORPOC
  space (0x0016).
- Move the KVM_REG_ARM_*_BIT_MAX definitions from uapi header
  to internal header (arm_hypercalls.h).
- Renamed the hypercall descriptor to 'struct kvm_smccc_features',
  and kvm_hvc_call_supported() to kvm_hvc_call_allowed().
- Introduced an allowed-list to hold the function-ids that aren't
  represented by feature-bits.
- Introduced kvm_psci_func_id_is_valid() to check if a given
  function-id is a valid PSCI id, which is used in
  kvm_hvc_call_allowed().
- Introduced KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT as bit-0 of
  KVM_REG_ARM_VENDOR_HYP_BMAP register and
  KVM_REG_ARM_VENDOR_HYP_BIT_PTP is moved to bit-1.
- Updated the arm-smccc.h import to include the definition of
  ARM_SMCCC_ARCH_WORKAROUND_3.
- Introduced the KVM_REG_ARM_FW_FEAT_BMAP COPROC definition to
  get-reg-list selftest.
- Created a new patch to include KVM_REG_ARM_FW_REG(3) in
  get-reg-list.


v3 -> v4

Addressed comments and took suggestions by Reiji, Oliver, Marc,
Sean and Jim:

- Renamed and moved the VM has run once check to arm64.
- Introduced the capability to dynamically modify the register
  encodings to include the scope information.
- Replaced mutex_lock with READ_ONCE and WRITE_ONCE when the
  bitmaps are accessed.
- The hypercalls selftest re-runs with KVM_CAP_ARM_REG_SCOPE
  enabled.

v2 -> v3

Addressed comments by Marc and Andrew:

- Dropped kvm_vcpu_has_run_once() implementation.
- Redifined kvm_vm_has_run_once() as kvm_vm_has_started() in the core
  KVM code that introduces a new field, 'vm_started', to track this.
- KVM_CAP_ARM_HVC_FW_REG_BMAP returns the number of psuedo-firmware
  bitmap registers upon a 'read'. Support for 'write' removed.
- Removed redundant spinlock, 'fw_reg_bmap_enabled' fields from the
  hypercall descriptor structure.
- A separate sub-struct to hold the bitmap info is removed. The bitmap
  info is directly stored in the hypercall descriptor structure
  (struct kvm_hvc_desc).

v1 -> v2

Addressed comments by Oliver (thanks!):

- Introduced kvm_vcpu_has_run_once() and kvm_vm_has_run_once() in the
  core kvm code, rather than relying on ARM specific
  vcpu->arch.has_run_once.
- Writing to KVM_REG_ARM_PSCI_VERSION is done in hypercalls.c itself,
  rather than separating out to psci.c.
- Introduced KVM_CAP_ARM_HVC_FW_REG_BMAP to enable the extension.
- Tracks the register accesses from VMM to decide whether to sanitize
  a register or not, as opposed to sanitizing upon the first 'write'
  in v1.
- kvm_hvc_call_supported() is implemented using a direct switch-case
  statement, instead of looping over all the registers to pick the
  register for the function-id.
- Replaced the register bit definitions with #defines, instead of enums.
- Removed the patch v1-06/08 that imports the firmware register
  definitions as it's not needed.
- Separated out the documentations in its own patch, and the renaming
  of hypercalls.rst to psci.rst into another patch.
- Add the new firmware registers to get-reg-list KVM selftest.

v1: https://lore.kernel.org/kvmarm/20211102002203.1046069-1-rananta@google.com/
v2: https://lore.kernel.org/kvmarm/20211113012234.1443009-1-rananta@google.com/
v3: https://lore.kernel.org/linux-arm-kernel/20220104194918.373612-1-rananta@google.com/
v4: https://lore.kernel.org/lkml/20220224172559.4170192-1-rananta@google.com/

Raghavendra Rao Ananta (10):
  KVM: arm64: Factor out firmware register handling from psci.c
  KVM: arm64: Setup a framework for hypercall bitmap firmware registers
  KVM: arm64: Add standard hypervisor firmware register
  KVM: arm64: Add vendor hypervisor firmware register
  Docs: KVM: Rename psci.rst to hypercalls.rst
  Docs: KVM: Add doc for the bitmap firmware registers
  tools: Import ARM SMCCC definitions
  selftests: KVM: aarch64: Introduce hypercall ABI test
  selftests: KVM: aarch64: Add the bitmap firmware registers to
    get-reg-list
  selftests: KVM: aarch64: Add KVM_REG_ARM_FW_REG(3) to get-reg-list

 Documentation/virt/kvm/api.rst                |  17 +
 Documentation/virt/kvm/arm/hypercalls.rst     | 136 +++++++
 Documentation/virt/kvm/arm/psci.rst           |  77 ----
 arch/arm64/include/asm/kvm_host.h             |  16 +
 arch/arm64/include/uapi/asm/kvm.h             |  16 +
 arch/arm64/kvm/arm.c                          |   1 +
 arch/arm64/kvm/guest.c                        |  10 +-
 arch/arm64/kvm/hypercalls.c                   | 321 +++++++++++++++-
 arch/arm64/kvm/psci.c                         | 183 ----------
 include/kvm/arm_hypercalls.h                  |  22 ++
 include/kvm/arm_psci.h                        |  17 +-
 tools/include/linux/arm-smccc.h               | 193 ++++++++++
 tools/testing/selftests/kvm/.gitignore        |   1 +
 tools/testing/selftests/kvm/Makefile          |   1 +
 .../selftests/kvm/aarch64/get-reg-list.c      |   9 +
 .../selftests/kvm/aarch64/hypercalls.c        | 344 ++++++++++++++++++
 16 files changed, 1092 insertions(+), 272 deletions(-)
 create mode 100644 Documentation/virt/kvm/arm/hypercalls.rst
 delete mode 100644 Documentation/virt/kvm/arm/psci.rst
 create mode 100644 tools/include/linux/arm-smccc.h
 create mode 100644 tools/testing/selftests/kvm/aarch64/hypercalls.c

-- 
2.35.1.1094.g7c7d902a7c-goog

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

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

* [PATCH v5 00/10] KVM: arm64: Add support for hypercall services selection
@ 2022-04-07  1:15 ` Raghavendra Rao Ananta
  0 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-07  1:15 UTC (permalink / raw)
  To: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose
  Cc: Paolo Bonzini, Catalin Marinas, Will Deacon, Peter Shier,
	Ricardo Koller, Oliver Upton, Reiji Watanabe, Jing Zhang,
	Raghavendra Rao Anata, linux-arm-kernel, kvmarm, linux-kernel,
	kvm

Hello,

Continuing the discussion from [1], the series tries to add support
for the userspace to elect the hypercall services that it wishes
to expose to the guest, rather than the guest discovering them
unconditionally. The idea employed by the series was taken from
[1] as suggested by Marc Z.

In a broad sense, the concept is similar to the current implementation
of PSCI interface- create a 'firmware psuedo-register' to handle the
firmware revisions. The series extends this idea to all the other
hypercalls such as TRNG (True Random Number Generator), PV_TIME
(Paravirtualized Time), and PTP (Precision Time protocol).

For better categorization and future scaling, these firmware registers
are categorized based on the service call owners. Also, unlike the
existing firmware psuedo-registers, they hold the features supported
in the form of a bitmap.

During the VM initialization, the registers holds an upper-limit of
the features supported by each one of them. It's expected that the
userspace discover the features provided by each register via GET_ONE_REG,
and writeback the desired values using SET_ONE_REG. KVM allows this
modification only until the VM has started.

Some of the standard function-ids, such as ARM_SMCCC_VERSION_FUNC_ID,
need not be associated with a feature bit. For such ids, the series
introduced an allowed-list, hvc_func_default_allowed_list[], that holds
all such ids. As a result, the functions that are not elected by userspace,
or if they are not a part of this allowed-list, will be denied for when
the guests invoke them.

Older VMMs can simply ignore this interface and the hypercall services
will be exposed unconditionally to the guests, thus ensuring backward
compatibility.

The patches are based off of mainline kernel 5.18-rc1, with the selftest
patches from [2] applied.

Patch-1 factors out the non-PSCI related interface from psci.c to
hypercalls.c, as the series would extend the list in the upcoming
patches.

Patch-2 sets up the framework for the bitmap firmware psuedo-registers.
It includes read/write support for the registers, and a helper to check
if a particular hypercall service is supported for the guest.
It also adds the register KVM_REG_ARM_STD_HYP_BMAP to support ARM's
standard secure services.

Patch-3 introduces the firmware register, KVM_REG_ARM_STD_HYP_BMAP,
which holds the standard hypervisor services (such as PV_TIME).

Patch-4 introduces the firmware register, KVM_REG_ARM_VENDOR_HYP_BMAP,
which holds the vendor specific hypercall services.

Patch-5,6 Add the necessary documentation for the newly added firmware
registers.

Patch-7 imports the SMCCC definitions from linux/arm-smccc.h into tools/
for further use in selftests.

Patch-8 adds the selftest to test the guest (using 'hvc') and userspace
interfaces (SET/GET_ONE_REG).

Patch-9 adds these firmware registers into the get-reg-list selftest.

Patch-10 is unrelated to the series, but adds KVM_REG_ARM_FW_REG(3)
to base_regs[] of get-regs-list selftest for the sake of completion.

[1]: https://lore.kernel.org/kvmarm/874kbcpmlq.wl-maz@kernel.org/T/
[2]: https://lore.kernel.org/kvmarm/YUzgdbYk8BeCnHyW@google.com/

Regards,
Raghavendra

v4 -> v5:

Addressed comments by Oliver (thank you!):

- Rebased the series to accommodate ARM_SMCCC_ARCH_WORKAROUND_3
  and PSCI 1.1 changes, and capturing VM's first run.
- Removed the patches related to register scoping (v4 02/13 and
  03/13). I plan to re-introduce them in its own series.
- Dropped the patch that captures VM's first run.
- Moved the bitmap feature firmware registers to its own CORPOC
  space (0x0016).
- Move the KVM_REG_ARM_*_BIT_MAX definitions from uapi header
  to internal header (arm_hypercalls.h).
- Renamed the hypercall descriptor to 'struct kvm_smccc_features',
  and kvm_hvc_call_supported() to kvm_hvc_call_allowed().
- Introduced an allowed-list to hold the function-ids that aren't
  represented by feature-bits.
- Introduced kvm_psci_func_id_is_valid() to check if a given
  function-id is a valid PSCI id, which is used in
  kvm_hvc_call_allowed().
- Introduced KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT as bit-0 of
  KVM_REG_ARM_VENDOR_HYP_BMAP register and
  KVM_REG_ARM_VENDOR_HYP_BIT_PTP is moved to bit-1.
- Updated the arm-smccc.h import to include the definition of
  ARM_SMCCC_ARCH_WORKAROUND_3.
- Introduced the KVM_REG_ARM_FW_FEAT_BMAP COPROC definition to
  get-reg-list selftest.
- Created a new patch to include KVM_REG_ARM_FW_REG(3) in
  get-reg-list.


v3 -> v4

Addressed comments and took suggestions by Reiji, Oliver, Marc,
Sean and Jim:

- Renamed and moved the VM has run once check to arm64.
- Introduced the capability to dynamically modify the register
  encodings to include the scope information.
- Replaced mutex_lock with READ_ONCE and WRITE_ONCE when the
  bitmaps are accessed.
- The hypercalls selftest re-runs with KVM_CAP_ARM_REG_SCOPE
  enabled.

v2 -> v3

Addressed comments by Marc and Andrew:

- Dropped kvm_vcpu_has_run_once() implementation.
- Redifined kvm_vm_has_run_once() as kvm_vm_has_started() in the core
  KVM code that introduces a new field, 'vm_started', to track this.
- KVM_CAP_ARM_HVC_FW_REG_BMAP returns the number of psuedo-firmware
  bitmap registers upon a 'read'. Support for 'write' removed.
- Removed redundant spinlock, 'fw_reg_bmap_enabled' fields from the
  hypercall descriptor structure.
- A separate sub-struct to hold the bitmap info is removed. The bitmap
  info is directly stored in the hypercall descriptor structure
  (struct kvm_hvc_desc).

v1 -> v2

Addressed comments by Oliver (thanks!):

- Introduced kvm_vcpu_has_run_once() and kvm_vm_has_run_once() in the
  core kvm code, rather than relying on ARM specific
  vcpu->arch.has_run_once.
- Writing to KVM_REG_ARM_PSCI_VERSION is done in hypercalls.c itself,
  rather than separating out to psci.c.
- Introduced KVM_CAP_ARM_HVC_FW_REG_BMAP to enable the extension.
- Tracks the register accesses from VMM to decide whether to sanitize
  a register or not, as opposed to sanitizing upon the first 'write'
  in v1.
- kvm_hvc_call_supported() is implemented using a direct switch-case
  statement, instead of looping over all the registers to pick the
  register for the function-id.
- Replaced the register bit definitions with #defines, instead of enums.
- Removed the patch v1-06/08 that imports the firmware register
  definitions as it's not needed.
- Separated out the documentations in its own patch, and the renaming
  of hypercalls.rst to psci.rst into another patch.
- Add the new firmware registers to get-reg-list KVM selftest.

v1: https://lore.kernel.org/kvmarm/20211102002203.1046069-1-rananta@google.com/
v2: https://lore.kernel.org/kvmarm/20211113012234.1443009-1-rananta@google.com/
v3: https://lore.kernel.org/linux-arm-kernel/20220104194918.373612-1-rananta@google.com/
v4: https://lore.kernel.org/lkml/20220224172559.4170192-1-rananta@google.com/

Raghavendra Rao Ananta (10):
  KVM: arm64: Factor out firmware register handling from psci.c
  KVM: arm64: Setup a framework for hypercall bitmap firmware registers
  KVM: arm64: Add standard hypervisor firmware register
  KVM: arm64: Add vendor hypervisor firmware register
  Docs: KVM: Rename psci.rst to hypercalls.rst
  Docs: KVM: Add doc for the bitmap firmware registers
  tools: Import ARM SMCCC definitions
  selftests: KVM: aarch64: Introduce hypercall ABI test
  selftests: KVM: aarch64: Add the bitmap firmware registers to
    get-reg-list
  selftests: KVM: aarch64: Add KVM_REG_ARM_FW_REG(3) to get-reg-list

 Documentation/virt/kvm/api.rst                |  17 +
 Documentation/virt/kvm/arm/hypercalls.rst     | 136 +++++++
 Documentation/virt/kvm/arm/psci.rst           |  77 ----
 arch/arm64/include/asm/kvm_host.h             |  16 +
 arch/arm64/include/uapi/asm/kvm.h             |  16 +
 arch/arm64/kvm/arm.c                          |   1 +
 arch/arm64/kvm/guest.c                        |  10 +-
 arch/arm64/kvm/hypercalls.c                   | 321 +++++++++++++++-
 arch/arm64/kvm/psci.c                         | 183 ----------
 include/kvm/arm_hypercalls.h                  |  22 ++
 include/kvm/arm_psci.h                        |  17 +-
 tools/include/linux/arm-smccc.h               | 193 ++++++++++
 tools/testing/selftests/kvm/.gitignore        |   1 +
 tools/testing/selftests/kvm/Makefile          |   1 +
 .../selftests/kvm/aarch64/get-reg-list.c      |   9 +
 .../selftests/kvm/aarch64/hypercalls.c        | 344 ++++++++++++++++++
 16 files changed, 1092 insertions(+), 272 deletions(-)
 create mode 100644 Documentation/virt/kvm/arm/hypercalls.rst
 delete mode 100644 Documentation/virt/kvm/arm/psci.rst
 create mode 100644 tools/include/linux/arm-smccc.h
 create mode 100644 tools/testing/selftests/kvm/aarch64/hypercalls.c

-- 
2.35.1.1094.g7c7d902a7c-goog


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

* [PATCH v5 00/10] KVM: arm64: Add support for hypercall services selection
@ 2022-04-07  1:15 ` Raghavendra Rao Ananta
  0 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-07  1:15 UTC (permalink / raw)
  To: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose
  Cc: Paolo Bonzini, Catalin Marinas, Will Deacon, Peter Shier,
	Ricardo Koller, Oliver Upton, Reiji Watanabe, Jing Zhang,
	Raghavendra Rao Anata, linux-arm-kernel, kvmarm, linux-kernel,
	kvm

Hello,

Continuing the discussion from [1], the series tries to add support
for the userspace to elect the hypercall services that it wishes
to expose to the guest, rather than the guest discovering them
unconditionally. The idea employed by the series was taken from
[1] as suggested by Marc Z.

In a broad sense, the concept is similar to the current implementation
of PSCI interface- create a 'firmware psuedo-register' to handle the
firmware revisions. The series extends this idea to all the other
hypercalls such as TRNG (True Random Number Generator), PV_TIME
(Paravirtualized Time), and PTP (Precision Time protocol).

For better categorization and future scaling, these firmware registers
are categorized based on the service call owners. Also, unlike the
existing firmware psuedo-registers, they hold the features supported
in the form of a bitmap.

During the VM initialization, the registers holds an upper-limit of
the features supported by each one of them. It's expected that the
userspace discover the features provided by each register via GET_ONE_REG,
and writeback the desired values using SET_ONE_REG. KVM allows this
modification only until the VM has started.

Some of the standard function-ids, such as ARM_SMCCC_VERSION_FUNC_ID,
need not be associated with a feature bit. For such ids, the series
introduced an allowed-list, hvc_func_default_allowed_list[], that holds
all such ids. As a result, the functions that are not elected by userspace,
or if they are not a part of this allowed-list, will be denied for when
the guests invoke them.

Older VMMs can simply ignore this interface and the hypercall services
will be exposed unconditionally to the guests, thus ensuring backward
compatibility.

The patches are based off of mainline kernel 5.18-rc1, with the selftest
patches from [2] applied.

Patch-1 factors out the non-PSCI related interface from psci.c to
hypercalls.c, as the series would extend the list in the upcoming
patches.

Patch-2 sets up the framework for the bitmap firmware psuedo-registers.
It includes read/write support for the registers, and a helper to check
if a particular hypercall service is supported for the guest.
It also adds the register KVM_REG_ARM_STD_HYP_BMAP to support ARM's
standard secure services.

Patch-3 introduces the firmware register, KVM_REG_ARM_STD_HYP_BMAP,
which holds the standard hypervisor services (such as PV_TIME).

Patch-4 introduces the firmware register, KVM_REG_ARM_VENDOR_HYP_BMAP,
which holds the vendor specific hypercall services.

Patch-5,6 Add the necessary documentation for the newly added firmware
registers.

Patch-7 imports the SMCCC definitions from linux/arm-smccc.h into tools/
for further use in selftests.

Patch-8 adds the selftest to test the guest (using 'hvc') and userspace
interfaces (SET/GET_ONE_REG).

Patch-9 adds these firmware registers into the get-reg-list selftest.

Patch-10 is unrelated to the series, but adds KVM_REG_ARM_FW_REG(3)
to base_regs[] of get-regs-list selftest for the sake of completion.

[1]: https://lore.kernel.org/kvmarm/874kbcpmlq.wl-maz@kernel.org/T/
[2]: https://lore.kernel.org/kvmarm/YUzgdbYk8BeCnHyW@google.com/

Regards,
Raghavendra

v4 -> v5:

Addressed comments by Oliver (thank you!):

- Rebased the series to accommodate ARM_SMCCC_ARCH_WORKAROUND_3
  and PSCI 1.1 changes, and capturing VM's first run.
- Removed the patches related to register scoping (v4 02/13 and
  03/13). I plan to re-introduce them in its own series.
- Dropped the patch that captures VM's first run.
- Moved the bitmap feature firmware registers to its own CORPOC
  space (0x0016).
- Move the KVM_REG_ARM_*_BIT_MAX definitions from uapi header
  to internal header (arm_hypercalls.h).
- Renamed the hypercall descriptor to 'struct kvm_smccc_features',
  and kvm_hvc_call_supported() to kvm_hvc_call_allowed().
- Introduced an allowed-list to hold the function-ids that aren't
  represented by feature-bits.
- Introduced kvm_psci_func_id_is_valid() to check if a given
  function-id is a valid PSCI id, which is used in
  kvm_hvc_call_allowed().
- Introduced KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT as bit-0 of
  KVM_REG_ARM_VENDOR_HYP_BMAP register and
  KVM_REG_ARM_VENDOR_HYP_BIT_PTP is moved to bit-1.
- Updated the arm-smccc.h import to include the definition of
  ARM_SMCCC_ARCH_WORKAROUND_3.
- Introduced the KVM_REG_ARM_FW_FEAT_BMAP COPROC definition to
  get-reg-list selftest.
- Created a new patch to include KVM_REG_ARM_FW_REG(3) in
  get-reg-list.


v3 -> v4

Addressed comments and took suggestions by Reiji, Oliver, Marc,
Sean and Jim:

- Renamed and moved the VM has run once check to arm64.
- Introduced the capability to dynamically modify the register
  encodings to include the scope information.
- Replaced mutex_lock with READ_ONCE and WRITE_ONCE when the
  bitmaps are accessed.
- The hypercalls selftest re-runs with KVM_CAP_ARM_REG_SCOPE
  enabled.

v2 -> v3

Addressed comments by Marc and Andrew:

- Dropped kvm_vcpu_has_run_once() implementation.
- Redifined kvm_vm_has_run_once() as kvm_vm_has_started() in the core
  KVM code that introduces a new field, 'vm_started', to track this.
- KVM_CAP_ARM_HVC_FW_REG_BMAP returns the number of psuedo-firmware
  bitmap registers upon a 'read'. Support for 'write' removed.
- Removed redundant spinlock, 'fw_reg_bmap_enabled' fields from the
  hypercall descriptor structure.
- A separate sub-struct to hold the bitmap info is removed. The bitmap
  info is directly stored in the hypercall descriptor structure
  (struct kvm_hvc_desc).

v1 -> v2

Addressed comments by Oliver (thanks!):

- Introduced kvm_vcpu_has_run_once() and kvm_vm_has_run_once() in the
  core kvm code, rather than relying on ARM specific
  vcpu->arch.has_run_once.
- Writing to KVM_REG_ARM_PSCI_VERSION is done in hypercalls.c itself,
  rather than separating out to psci.c.
- Introduced KVM_CAP_ARM_HVC_FW_REG_BMAP to enable the extension.
- Tracks the register accesses from VMM to decide whether to sanitize
  a register or not, as opposed to sanitizing upon the first 'write'
  in v1.
- kvm_hvc_call_supported() is implemented using a direct switch-case
  statement, instead of looping over all the registers to pick the
  register for the function-id.
- Replaced the register bit definitions with #defines, instead of enums.
- Removed the patch v1-06/08 that imports the firmware register
  definitions as it's not needed.
- Separated out the documentations in its own patch, and the renaming
  of hypercalls.rst to psci.rst into another patch.
- Add the new firmware registers to get-reg-list KVM selftest.

v1: https://lore.kernel.org/kvmarm/20211102002203.1046069-1-rananta@google.com/
v2: https://lore.kernel.org/kvmarm/20211113012234.1443009-1-rananta@google.com/
v3: https://lore.kernel.org/linux-arm-kernel/20220104194918.373612-1-rananta@google.com/
v4: https://lore.kernel.org/lkml/20220224172559.4170192-1-rananta@google.com/

Raghavendra Rao Ananta (10):
  KVM: arm64: Factor out firmware register handling from psci.c
  KVM: arm64: Setup a framework for hypercall bitmap firmware registers
  KVM: arm64: Add standard hypervisor firmware register
  KVM: arm64: Add vendor hypervisor firmware register
  Docs: KVM: Rename psci.rst to hypercalls.rst
  Docs: KVM: Add doc for the bitmap firmware registers
  tools: Import ARM SMCCC definitions
  selftests: KVM: aarch64: Introduce hypercall ABI test
  selftests: KVM: aarch64: Add the bitmap firmware registers to
    get-reg-list
  selftests: KVM: aarch64: Add KVM_REG_ARM_FW_REG(3) to get-reg-list

 Documentation/virt/kvm/api.rst                |  17 +
 Documentation/virt/kvm/arm/hypercalls.rst     | 136 +++++++
 Documentation/virt/kvm/arm/psci.rst           |  77 ----
 arch/arm64/include/asm/kvm_host.h             |  16 +
 arch/arm64/include/uapi/asm/kvm.h             |  16 +
 arch/arm64/kvm/arm.c                          |   1 +
 arch/arm64/kvm/guest.c                        |  10 +-
 arch/arm64/kvm/hypercalls.c                   | 321 +++++++++++++++-
 arch/arm64/kvm/psci.c                         | 183 ----------
 include/kvm/arm_hypercalls.h                  |  22 ++
 include/kvm/arm_psci.h                        |  17 +-
 tools/include/linux/arm-smccc.h               | 193 ++++++++++
 tools/testing/selftests/kvm/.gitignore        |   1 +
 tools/testing/selftests/kvm/Makefile          |   1 +
 .../selftests/kvm/aarch64/get-reg-list.c      |   9 +
 .../selftests/kvm/aarch64/hypercalls.c        | 344 ++++++++++++++++++
 16 files changed, 1092 insertions(+), 272 deletions(-)
 create mode 100644 Documentation/virt/kvm/arm/hypercalls.rst
 delete mode 100644 Documentation/virt/kvm/arm/psci.rst
 create mode 100644 tools/include/linux/arm-smccc.h
 create mode 100644 tools/testing/selftests/kvm/aarch64/hypercalls.c

-- 
2.35.1.1094.g7c7d902a7c-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] 96+ messages in thread

* [PATCH v5 01/10] KVM: arm64: Factor out firmware register handling from psci.c
  2022-04-07  1:15 ` Raghavendra Rao Ananta
  (?)
@ 2022-04-07  1:15   ` Raghavendra Rao Ananta
  -1 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-07  1:15 UTC (permalink / raw)
  To: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose
  Cc: kvm, Catalin Marinas, Peter Shier, linux-kernel, Paolo Bonzini,
	Will Deacon, kvmarm, linux-arm-kernel

Common hypercall firmware register handing is currently employed
by psci.c. Since the upcoming patches add more of these registers,
it's better to move the generic handling to hypercall.c for a
cleaner presentation.

While we are at it, collect all the firmware registers under
fw_reg_ids[] to help implement kvm_arm_get_fw_num_regs() and
kvm_arm_copy_fw_reg_indices() in a generic way. Also, define
KVM_REG_FEATURE_LEVEL_MASK using a GENMASK instead.

No functional change intended.

Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
Reviewed-by: Oliver Upton <oupton@google.com>
---
 arch/arm64/kvm/guest.c       |   2 +-
 arch/arm64/kvm/hypercalls.c  | 185 +++++++++++++++++++++++++++++++++++
 arch/arm64/kvm/psci.c        | 183 ----------------------------------
 include/kvm/arm_hypercalls.h |   7 ++
 include/kvm/arm_psci.h       |   7 --
 5 files changed, 193 insertions(+), 191 deletions(-)

diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
index 7e15b03fbdf8..0d5cca56cbda 100644
--- a/arch/arm64/kvm/guest.c
+++ b/arch/arm64/kvm/guest.c
@@ -18,7 +18,7 @@
 #include <linux/string.h>
 #include <linux/vmalloc.h>
 #include <linux/fs.h>
-#include <kvm/arm_psci.h>
+#include <kvm/arm_hypercalls.h>
 #include <asm/cputype.h>
 #include <linux/uaccess.h>
 #include <asm/fpsimd.h>
diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c
index 202b8c455724..fa6d9378d8e7 100644
--- a/arch/arm64/kvm/hypercalls.c
+++ b/arch/arm64/kvm/hypercalls.c
@@ -158,3 +158,188 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
 	smccc_set_retval(vcpu, val[0], val[1], val[2], val[3]);
 	return 1;
 }
+
+static const u64 kvm_arm_fw_reg_ids[] = {
+	KVM_REG_ARM_PSCI_VERSION,
+	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1,
+	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2,
+	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3,
+};
+
+int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
+{
+	return ARRAY_SIZE(kvm_arm_fw_reg_ids);
+}
+
+int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(kvm_arm_fw_reg_ids); i++) {
+		if (put_user(kvm_arm_fw_reg_ids[i], uindices++))
+			return -EFAULT;
+	}
+
+	return 0;
+}
+
+#define KVM_REG_FEATURE_LEVEL_WIDTH	4
+#define KVM_REG_FEATURE_LEVEL_MASK	GENMASK(KVM_REG_FEATURE_LEVEL_WIDTH, 0)
+
+/*
+ * Convert the workaround level into an easy-to-compare number, where higher
+ * values mean better protection.
+ */
+static int get_kernel_wa_level(u64 regid)
+{
+	switch (regid) {
+	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
+		switch (arm64_get_spectre_v2_state()) {
+		case SPECTRE_VULNERABLE:
+			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
+		case SPECTRE_MITIGATED:
+			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_AVAIL;
+		case SPECTRE_UNAFFECTED:
+			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_REQUIRED;
+		}
+		return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
+	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
+		switch (arm64_get_spectre_v4_state()) {
+		case SPECTRE_MITIGATED:
+			/*
+			 * As for the hypercall discovery, we pretend we
+			 * don't have any FW mitigation if SSBS is there at
+			 * all times.
+			 */
+			if (cpus_have_final_cap(ARM64_SSBS))
+				return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
+			fallthrough;
+		case SPECTRE_UNAFFECTED:
+			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
+		case SPECTRE_VULNERABLE:
+			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
+		}
+		break;
+	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
+		switch (arm64_get_spectre_bhb_state()) {
+		case SPECTRE_VULNERABLE:
+			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
+		case SPECTRE_MITIGATED:
+			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_AVAIL;
+		case SPECTRE_UNAFFECTED:
+			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_REQUIRED;
+		}
+		return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
+	}
+
+	return -EINVAL;
+}
+
+int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
+{
+	void __user *uaddr = (void __user *)(long)reg->addr;
+	u64 val;
+
+	switch (reg->id) {
+	case KVM_REG_ARM_PSCI_VERSION:
+		val = kvm_psci_version(vcpu);
+		break;
+	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
+	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
+	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
+		val = get_kernel_wa_level(reg->id) & KVM_REG_FEATURE_LEVEL_MASK;
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	if (copy_to_user(uaddr, &val, KVM_REG_SIZE(reg->id)))
+		return -EFAULT;
+
+	return 0;
+}
+
+int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
+{
+	void __user *uaddr = (void __user *)(long)reg->addr;
+	u64 val;
+	int wa_level;
+
+	if (copy_from_user(&val, uaddr, KVM_REG_SIZE(reg->id)))
+		return -EFAULT;
+
+	switch (reg->id) {
+	case KVM_REG_ARM_PSCI_VERSION:
+	{
+		bool wants_02;
+
+		wants_02 = test_bit(KVM_ARM_VCPU_PSCI_0_2, vcpu->arch.features);
+
+		switch (val) {
+		case KVM_ARM_PSCI_0_1:
+			if (wants_02)
+				return -EINVAL;
+			vcpu->kvm->arch.psci_version = val;
+			return 0;
+		case KVM_ARM_PSCI_0_2:
+		case KVM_ARM_PSCI_1_0:
+		case KVM_ARM_PSCI_1_1:
+			if (!wants_02)
+				return -EINVAL;
+			vcpu->kvm->arch.psci_version = val;
+			return 0;
+		}
+		break;
+	}
+
+	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
+	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
+		if (val & ~KVM_REG_FEATURE_LEVEL_MASK)
+			return -EINVAL;
+
+		if (get_kernel_wa_level(reg->id) < val)
+			return -EINVAL;
+
+		return 0;
+
+	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
+		if (val & ~(KVM_REG_FEATURE_LEVEL_MASK |
+			    KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED))
+			return -EINVAL;
+
+		/* The enabled bit must not be set unless the level is AVAIL. */
+		if ((val & KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED) &&
+		    (val & KVM_REG_FEATURE_LEVEL_MASK) != KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL)
+			return -EINVAL;
+
+		/*
+		 * Map all the possible incoming states to the only two we
+		 * really want to deal with.
+		 */
+		switch (val & KVM_REG_FEATURE_LEVEL_MASK) {
+		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL:
+		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_UNKNOWN:
+			wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
+			break;
+		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL:
+		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED:
+			wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		/*
+		 * We can deal with NOT_AVAIL on NOT_REQUIRED, but not the
+		 * other way around.
+		 */
+		if (get_kernel_wa_level(reg->id) < wa_level)
+			return -EINVAL;
+
+		return 0;
+	default:
+		return -ENOENT;
+	}
+
+	return -EINVAL;
+}
diff --git a/arch/arm64/kvm/psci.c b/arch/arm64/kvm/psci.c
index 372da09a2fab..bdfa93ca57d1 100644
--- a/arch/arm64/kvm/psci.c
+++ b/arch/arm64/kvm/psci.c
@@ -439,186 +439,3 @@ int kvm_psci_call(struct kvm_vcpu *vcpu)
 		return -EINVAL;
 	}
 }
-
-int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
-{
-	return 4;		/* PSCI version and three workaround registers */
-}
-
-int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
-{
-	if (put_user(KVM_REG_ARM_PSCI_VERSION, uindices++))
-		return -EFAULT;
-
-	if (put_user(KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1, uindices++))
-		return -EFAULT;
-
-	if (put_user(KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2, uindices++))
-		return -EFAULT;
-
-	if (put_user(KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3, uindices++))
-		return -EFAULT;
-
-	return 0;
-}
-
-#define KVM_REG_FEATURE_LEVEL_WIDTH	4
-#define KVM_REG_FEATURE_LEVEL_MASK	(BIT(KVM_REG_FEATURE_LEVEL_WIDTH) - 1)
-
-/*
- * Convert the workaround level into an easy-to-compare number, where higher
- * values mean better protection.
- */
-static int get_kernel_wa_level(u64 regid)
-{
-	switch (regid) {
-	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
-		switch (arm64_get_spectre_v2_state()) {
-		case SPECTRE_VULNERABLE:
-			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
-		case SPECTRE_MITIGATED:
-			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_AVAIL;
-		case SPECTRE_UNAFFECTED:
-			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_REQUIRED;
-		}
-		return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
-	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
-		switch (arm64_get_spectre_v4_state()) {
-		case SPECTRE_MITIGATED:
-			/*
-			 * As for the hypercall discovery, we pretend we
-			 * don't have any FW mitigation if SSBS is there at
-			 * all times.
-			 */
-			if (cpus_have_final_cap(ARM64_SSBS))
-				return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
-			fallthrough;
-		case SPECTRE_UNAFFECTED:
-			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
-		case SPECTRE_VULNERABLE:
-			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
-		}
-		break;
-	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
-		switch (arm64_get_spectre_bhb_state()) {
-		case SPECTRE_VULNERABLE:
-			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
-		case SPECTRE_MITIGATED:
-			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_AVAIL;
-		case SPECTRE_UNAFFECTED:
-			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_REQUIRED;
-		}
-		return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
-	}
-
-	return -EINVAL;
-}
-
-int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
-{
-	void __user *uaddr = (void __user *)(long)reg->addr;
-	u64 val;
-
-	switch (reg->id) {
-	case KVM_REG_ARM_PSCI_VERSION:
-		val = kvm_psci_version(vcpu);
-		break;
-	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
-	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
-	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
-		val = get_kernel_wa_level(reg->id) & KVM_REG_FEATURE_LEVEL_MASK;
-		break;
-	default:
-		return -ENOENT;
-	}
-
-	if (copy_to_user(uaddr, &val, KVM_REG_SIZE(reg->id)))
-		return -EFAULT;
-
-	return 0;
-}
-
-int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
-{
-	void __user *uaddr = (void __user *)(long)reg->addr;
-	u64 val;
-	int wa_level;
-
-	if (copy_from_user(&val, uaddr, KVM_REG_SIZE(reg->id)))
-		return -EFAULT;
-
-	switch (reg->id) {
-	case KVM_REG_ARM_PSCI_VERSION:
-	{
-		bool wants_02;
-
-		wants_02 = test_bit(KVM_ARM_VCPU_PSCI_0_2, vcpu->arch.features);
-
-		switch (val) {
-		case KVM_ARM_PSCI_0_1:
-			if (wants_02)
-				return -EINVAL;
-			vcpu->kvm->arch.psci_version = val;
-			return 0;
-		case KVM_ARM_PSCI_0_2:
-		case KVM_ARM_PSCI_1_0:
-		case KVM_ARM_PSCI_1_1:
-			if (!wants_02)
-				return -EINVAL;
-			vcpu->kvm->arch.psci_version = val;
-			return 0;
-		}
-		break;
-	}
-
-	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
-	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
-		if (val & ~KVM_REG_FEATURE_LEVEL_MASK)
-			return -EINVAL;
-
-		if (get_kernel_wa_level(reg->id) < val)
-			return -EINVAL;
-
-		return 0;
-
-	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
-		if (val & ~(KVM_REG_FEATURE_LEVEL_MASK |
-			    KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED))
-			return -EINVAL;
-
-		/* The enabled bit must not be set unless the level is AVAIL. */
-		if ((val & KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED) &&
-		    (val & KVM_REG_FEATURE_LEVEL_MASK) != KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL)
-			return -EINVAL;
-
-		/*
-		 * Map all the possible incoming states to the only two we
-		 * really want to deal with.
-		 */
-		switch (val & KVM_REG_FEATURE_LEVEL_MASK) {
-		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL:
-		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_UNKNOWN:
-			wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
-			break;
-		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL:
-		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED:
-			wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
-			break;
-		default:
-			return -EINVAL;
-		}
-
-		/*
-		 * We can deal with NOT_AVAIL on NOT_REQUIRED, but not the
-		 * other way around.
-		 */
-		if (get_kernel_wa_level(reg->id) < wa_level)
-			return -EINVAL;
-
-		return 0;
-	default:
-		return -ENOENT;
-	}
-
-	return -EINVAL;
-}
diff --git a/include/kvm/arm_hypercalls.h b/include/kvm/arm_hypercalls.h
index 0e2509d27910..5d38628a8d04 100644
--- a/include/kvm/arm_hypercalls.h
+++ b/include/kvm/arm_hypercalls.h
@@ -40,4 +40,11 @@ static inline void smccc_set_retval(struct kvm_vcpu *vcpu,
 	vcpu_set_reg(vcpu, 3, a3);
 }
 
+struct kvm_one_reg;
+
+int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu);
+int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices);
+int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
+int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
+
 #endif
diff --git a/include/kvm/arm_psci.h b/include/kvm/arm_psci.h
index 68b96c3826c3..6e55b9283789 100644
--- a/include/kvm/arm_psci.h
+++ b/include/kvm/arm_psci.h
@@ -39,11 +39,4 @@ static inline int kvm_psci_version(struct kvm_vcpu *vcpu)
 
 int kvm_psci_call(struct kvm_vcpu *vcpu);
 
-struct kvm_one_reg;
-
-int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu);
-int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices);
-int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
-int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
-
 #endif /* __KVM_ARM_PSCI_H__ */
-- 
2.35.1.1094.g7c7d902a7c-goog

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

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

* [PATCH v5 01/10] KVM: arm64: Factor out firmware register handling from psci.c
@ 2022-04-07  1:15   ` Raghavendra Rao Ananta
  0 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-07  1:15 UTC (permalink / raw)
  To: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose
  Cc: Paolo Bonzini, Catalin Marinas, Will Deacon, Peter Shier,
	Ricardo Koller, Oliver Upton, Reiji Watanabe, Jing Zhang,
	Raghavendra Rao Anata, linux-arm-kernel, kvmarm, linux-kernel,
	kvm

Common hypercall firmware register handing is currently employed
by psci.c. Since the upcoming patches add more of these registers,
it's better to move the generic handling to hypercall.c for a
cleaner presentation.

While we are at it, collect all the firmware registers under
fw_reg_ids[] to help implement kvm_arm_get_fw_num_regs() and
kvm_arm_copy_fw_reg_indices() in a generic way. Also, define
KVM_REG_FEATURE_LEVEL_MASK using a GENMASK instead.

No functional change intended.

Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
Reviewed-by: Oliver Upton <oupton@google.com>
---
 arch/arm64/kvm/guest.c       |   2 +-
 arch/arm64/kvm/hypercalls.c  | 185 +++++++++++++++++++++++++++++++++++
 arch/arm64/kvm/psci.c        | 183 ----------------------------------
 include/kvm/arm_hypercalls.h |   7 ++
 include/kvm/arm_psci.h       |   7 --
 5 files changed, 193 insertions(+), 191 deletions(-)

diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
index 7e15b03fbdf8..0d5cca56cbda 100644
--- a/arch/arm64/kvm/guest.c
+++ b/arch/arm64/kvm/guest.c
@@ -18,7 +18,7 @@
 #include <linux/string.h>
 #include <linux/vmalloc.h>
 #include <linux/fs.h>
-#include <kvm/arm_psci.h>
+#include <kvm/arm_hypercalls.h>
 #include <asm/cputype.h>
 #include <linux/uaccess.h>
 #include <asm/fpsimd.h>
diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c
index 202b8c455724..fa6d9378d8e7 100644
--- a/arch/arm64/kvm/hypercalls.c
+++ b/arch/arm64/kvm/hypercalls.c
@@ -158,3 +158,188 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
 	smccc_set_retval(vcpu, val[0], val[1], val[2], val[3]);
 	return 1;
 }
+
+static const u64 kvm_arm_fw_reg_ids[] = {
+	KVM_REG_ARM_PSCI_VERSION,
+	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1,
+	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2,
+	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3,
+};
+
+int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
+{
+	return ARRAY_SIZE(kvm_arm_fw_reg_ids);
+}
+
+int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(kvm_arm_fw_reg_ids); i++) {
+		if (put_user(kvm_arm_fw_reg_ids[i], uindices++))
+			return -EFAULT;
+	}
+
+	return 0;
+}
+
+#define KVM_REG_FEATURE_LEVEL_WIDTH	4
+#define KVM_REG_FEATURE_LEVEL_MASK	GENMASK(KVM_REG_FEATURE_LEVEL_WIDTH, 0)
+
+/*
+ * Convert the workaround level into an easy-to-compare number, where higher
+ * values mean better protection.
+ */
+static int get_kernel_wa_level(u64 regid)
+{
+	switch (regid) {
+	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
+		switch (arm64_get_spectre_v2_state()) {
+		case SPECTRE_VULNERABLE:
+			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
+		case SPECTRE_MITIGATED:
+			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_AVAIL;
+		case SPECTRE_UNAFFECTED:
+			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_REQUIRED;
+		}
+		return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
+	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
+		switch (arm64_get_spectre_v4_state()) {
+		case SPECTRE_MITIGATED:
+			/*
+			 * As for the hypercall discovery, we pretend we
+			 * don't have any FW mitigation if SSBS is there at
+			 * all times.
+			 */
+			if (cpus_have_final_cap(ARM64_SSBS))
+				return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
+			fallthrough;
+		case SPECTRE_UNAFFECTED:
+			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
+		case SPECTRE_VULNERABLE:
+			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
+		}
+		break;
+	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
+		switch (arm64_get_spectre_bhb_state()) {
+		case SPECTRE_VULNERABLE:
+			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
+		case SPECTRE_MITIGATED:
+			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_AVAIL;
+		case SPECTRE_UNAFFECTED:
+			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_REQUIRED;
+		}
+		return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
+	}
+
+	return -EINVAL;
+}
+
+int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
+{
+	void __user *uaddr = (void __user *)(long)reg->addr;
+	u64 val;
+
+	switch (reg->id) {
+	case KVM_REG_ARM_PSCI_VERSION:
+		val = kvm_psci_version(vcpu);
+		break;
+	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
+	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
+	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
+		val = get_kernel_wa_level(reg->id) & KVM_REG_FEATURE_LEVEL_MASK;
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	if (copy_to_user(uaddr, &val, KVM_REG_SIZE(reg->id)))
+		return -EFAULT;
+
+	return 0;
+}
+
+int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
+{
+	void __user *uaddr = (void __user *)(long)reg->addr;
+	u64 val;
+	int wa_level;
+
+	if (copy_from_user(&val, uaddr, KVM_REG_SIZE(reg->id)))
+		return -EFAULT;
+
+	switch (reg->id) {
+	case KVM_REG_ARM_PSCI_VERSION:
+	{
+		bool wants_02;
+
+		wants_02 = test_bit(KVM_ARM_VCPU_PSCI_0_2, vcpu->arch.features);
+
+		switch (val) {
+		case KVM_ARM_PSCI_0_1:
+			if (wants_02)
+				return -EINVAL;
+			vcpu->kvm->arch.psci_version = val;
+			return 0;
+		case KVM_ARM_PSCI_0_2:
+		case KVM_ARM_PSCI_1_0:
+		case KVM_ARM_PSCI_1_1:
+			if (!wants_02)
+				return -EINVAL;
+			vcpu->kvm->arch.psci_version = val;
+			return 0;
+		}
+		break;
+	}
+
+	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
+	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
+		if (val & ~KVM_REG_FEATURE_LEVEL_MASK)
+			return -EINVAL;
+
+		if (get_kernel_wa_level(reg->id) < val)
+			return -EINVAL;
+
+		return 0;
+
+	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
+		if (val & ~(KVM_REG_FEATURE_LEVEL_MASK |
+			    KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED))
+			return -EINVAL;
+
+		/* The enabled bit must not be set unless the level is AVAIL. */
+		if ((val & KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED) &&
+		    (val & KVM_REG_FEATURE_LEVEL_MASK) != KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL)
+			return -EINVAL;
+
+		/*
+		 * Map all the possible incoming states to the only two we
+		 * really want to deal with.
+		 */
+		switch (val & KVM_REG_FEATURE_LEVEL_MASK) {
+		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL:
+		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_UNKNOWN:
+			wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
+			break;
+		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL:
+		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED:
+			wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		/*
+		 * We can deal with NOT_AVAIL on NOT_REQUIRED, but not the
+		 * other way around.
+		 */
+		if (get_kernel_wa_level(reg->id) < wa_level)
+			return -EINVAL;
+
+		return 0;
+	default:
+		return -ENOENT;
+	}
+
+	return -EINVAL;
+}
diff --git a/arch/arm64/kvm/psci.c b/arch/arm64/kvm/psci.c
index 372da09a2fab..bdfa93ca57d1 100644
--- a/arch/arm64/kvm/psci.c
+++ b/arch/arm64/kvm/psci.c
@@ -439,186 +439,3 @@ int kvm_psci_call(struct kvm_vcpu *vcpu)
 		return -EINVAL;
 	}
 }
-
-int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
-{
-	return 4;		/* PSCI version and three workaround registers */
-}
-
-int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
-{
-	if (put_user(KVM_REG_ARM_PSCI_VERSION, uindices++))
-		return -EFAULT;
-
-	if (put_user(KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1, uindices++))
-		return -EFAULT;
-
-	if (put_user(KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2, uindices++))
-		return -EFAULT;
-
-	if (put_user(KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3, uindices++))
-		return -EFAULT;
-
-	return 0;
-}
-
-#define KVM_REG_FEATURE_LEVEL_WIDTH	4
-#define KVM_REG_FEATURE_LEVEL_MASK	(BIT(KVM_REG_FEATURE_LEVEL_WIDTH) - 1)
-
-/*
- * Convert the workaround level into an easy-to-compare number, where higher
- * values mean better protection.
- */
-static int get_kernel_wa_level(u64 regid)
-{
-	switch (regid) {
-	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
-		switch (arm64_get_spectre_v2_state()) {
-		case SPECTRE_VULNERABLE:
-			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
-		case SPECTRE_MITIGATED:
-			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_AVAIL;
-		case SPECTRE_UNAFFECTED:
-			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_REQUIRED;
-		}
-		return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
-	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
-		switch (arm64_get_spectre_v4_state()) {
-		case SPECTRE_MITIGATED:
-			/*
-			 * As for the hypercall discovery, we pretend we
-			 * don't have any FW mitigation if SSBS is there at
-			 * all times.
-			 */
-			if (cpus_have_final_cap(ARM64_SSBS))
-				return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
-			fallthrough;
-		case SPECTRE_UNAFFECTED:
-			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
-		case SPECTRE_VULNERABLE:
-			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
-		}
-		break;
-	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
-		switch (arm64_get_spectre_bhb_state()) {
-		case SPECTRE_VULNERABLE:
-			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
-		case SPECTRE_MITIGATED:
-			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_AVAIL;
-		case SPECTRE_UNAFFECTED:
-			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_REQUIRED;
-		}
-		return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
-	}
-
-	return -EINVAL;
-}
-
-int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
-{
-	void __user *uaddr = (void __user *)(long)reg->addr;
-	u64 val;
-
-	switch (reg->id) {
-	case KVM_REG_ARM_PSCI_VERSION:
-		val = kvm_psci_version(vcpu);
-		break;
-	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
-	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
-	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
-		val = get_kernel_wa_level(reg->id) & KVM_REG_FEATURE_LEVEL_MASK;
-		break;
-	default:
-		return -ENOENT;
-	}
-
-	if (copy_to_user(uaddr, &val, KVM_REG_SIZE(reg->id)))
-		return -EFAULT;
-
-	return 0;
-}
-
-int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
-{
-	void __user *uaddr = (void __user *)(long)reg->addr;
-	u64 val;
-	int wa_level;
-
-	if (copy_from_user(&val, uaddr, KVM_REG_SIZE(reg->id)))
-		return -EFAULT;
-
-	switch (reg->id) {
-	case KVM_REG_ARM_PSCI_VERSION:
-	{
-		bool wants_02;
-
-		wants_02 = test_bit(KVM_ARM_VCPU_PSCI_0_2, vcpu->arch.features);
-
-		switch (val) {
-		case KVM_ARM_PSCI_0_1:
-			if (wants_02)
-				return -EINVAL;
-			vcpu->kvm->arch.psci_version = val;
-			return 0;
-		case KVM_ARM_PSCI_0_2:
-		case KVM_ARM_PSCI_1_0:
-		case KVM_ARM_PSCI_1_1:
-			if (!wants_02)
-				return -EINVAL;
-			vcpu->kvm->arch.psci_version = val;
-			return 0;
-		}
-		break;
-	}
-
-	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
-	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
-		if (val & ~KVM_REG_FEATURE_LEVEL_MASK)
-			return -EINVAL;
-
-		if (get_kernel_wa_level(reg->id) < val)
-			return -EINVAL;
-
-		return 0;
-
-	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
-		if (val & ~(KVM_REG_FEATURE_LEVEL_MASK |
-			    KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED))
-			return -EINVAL;
-
-		/* The enabled bit must not be set unless the level is AVAIL. */
-		if ((val & KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED) &&
-		    (val & KVM_REG_FEATURE_LEVEL_MASK) != KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL)
-			return -EINVAL;
-
-		/*
-		 * Map all the possible incoming states to the only two we
-		 * really want to deal with.
-		 */
-		switch (val & KVM_REG_FEATURE_LEVEL_MASK) {
-		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL:
-		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_UNKNOWN:
-			wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
-			break;
-		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL:
-		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED:
-			wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
-			break;
-		default:
-			return -EINVAL;
-		}
-
-		/*
-		 * We can deal with NOT_AVAIL on NOT_REQUIRED, but not the
-		 * other way around.
-		 */
-		if (get_kernel_wa_level(reg->id) < wa_level)
-			return -EINVAL;
-
-		return 0;
-	default:
-		return -ENOENT;
-	}
-
-	return -EINVAL;
-}
diff --git a/include/kvm/arm_hypercalls.h b/include/kvm/arm_hypercalls.h
index 0e2509d27910..5d38628a8d04 100644
--- a/include/kvm/arm_hypercalls.h
+++ b/include/kvm/arm_hypercalls.h
@@ -40,4 +40,11 @@ static inline void smccc_set_retval(struct kvm_vcpu *vcpu,
 	vcpu_set_reg(vcpu, 3, a3);
 }
 
+struct kvm_one_reg;
+
+int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu);
+int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices);
+int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
+int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
+
 #endif
diff --git a/include/kvm/arm_psci.h b/include/kvm/arm_psci.h
index 68b96c3826c3..6e55b9283789 100644
--- a/include/kvm/arm_psci.h
+++ b/include/kvm/arm_psci.h
@@ -39,11 +39,4 @@ static inline int kvm_psci_version(struct kvm_vcpu *vcpu)
 
 int kvm_psci_call(struct kvm_vcpu *vcpu);
 
-struct kvm_one_reg;
-
-int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu);
-int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices);
-int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
-int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
-
 #endif /* __KVM_ARM_PSCI_H__ */
-- 
2.35.1.1094.g7c7d902a7c-goog


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

* [PATCH v5 01/10] KVM: arm64: Factor out firmware register handling from psci.c
@ 2022-04-07  1:15   ` Raghavendra Rao Ananta
  0 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-07  1:15 UTC (permalink / raw)
  To: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose
  Cc: Paolo Bonzini, Catalin Marinas, Will Deacon, Peter Shier,
	Ricardo Koller, Oliver Upton, Reiji Watanabe, Jing Zhang,
	Raghavendra Rao Anata, linux-arm-kernel, kvmarm, linux-kernel,
	kvm

Common hypercall firmware register handing is currently employed
by psci.c. Since the upcoming patches add more of these registers,
it's better to move the generic handling to hypercall.c for a
cleaner presentation.

While we are at it, collect all the firmware registers under
fw_reg_ids[] to help implement kvm_arm_get_fw_num_regs() and
kvm_arm_copy_fw_reg_indices() in a generic way. Also, define
KVM_REG_FEATURE_LEVEL_MASK using a GENMASK instead.

No functional change intended.

Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
Reviewed-by: Oliver Upton <oupton@google.com>
---
 arch/arm64/kvm/guest.c       |   2 +-
 arch/arm64/kvm/hypercalls.c  | 185 +++++++++++++++++++++++++++++++++++
 arch/arm64/kvm/psci.c        | 183 ----------------------------------
 include/kvm/arm_hypercalls.h |   7 ++
 include/kvm/arm_psci.h       |   7 --
 5 files changed, 193 insertions(+), 191 deletions(-)

diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
index 7e15b03fbdf8..0d5cca56cbda 100644
--- a/arch/arm64/kvm/guest.c
+++ b/arch/arm64/kvm/guest.c
@@ -18,7 +18,7 @@
 #include <linux/string.h>
 #include <linux/vmalloc.h>
 #include <linux/fs.h>
-#include <kvm/arm_psci.h>
+#include <kvm/arm_hypercalls.h>
 #include <asm/cputype.h>
 #include <linux/uaccess.h>
 #include <asm/fpsimd.h>
diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c
index 202b8c455724..fa6d9378d8e7 100644
--- a/arch/arm64/kvm/hypercalls.c
+++ b/arch/arm64/kvm/hypercalls.c
@@ -158,3 +158,188 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
 	smccc_set_retval(vcpu, val[0], val[1], val[2], val[3]);
 	return 1;
 }
+
+static const u64 kvm_arm_fw_reg_ids[] = {
+	KVM_REG_ARM_PSCI_VERSION,
+	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1,
+	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2,
+	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3,
+};
+
+int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
+{
+	return ARRAY_SIZE(kvm_arm_fw_reg_ids);
+}
+
+int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(kvm_arm_fw_reg_ids); i++) {
+		if (put_user(kvm_arm_fw_reg_ids[i], uindices++))
+			return -EFAULT;
+	}
+
+	return 0;
+}
+
+#define KVM_REG_FEATURE_LEVEL_WIDTH	4
+#define KVM_REG_FEATURE_LEVEL_MASK	GENMASK(KVM_REG_FEATURE_LEVEL_WIDTH, 0)
+
+/*
+ * Convert the workaround level into an easy-to-compare number, where higher
+ * values mean better protection.
+ */
+static int get_kernel_wa_level(u64 regid)
+{
+	switch (regid) {
+	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
+		switch (arm64_get_spectre_v2_state()) {
+		case SPECTRE_VULNERABLE:
+			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
+		case SPECTRE_MITIGATED:
+			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_AVAIL;
+		case SPECTRE_UNAFFECTED:
+			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_REQUIRED;
+		}
+		return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
+	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
+		switch (arm64_get_spectre_v4_state()) {
+		case SPECTRE_MITIGATED:
+			/*
+			 * As for the hypercall discovery, we pretend we
+			 * don't have any FW mitigation if SSBS is there at
+			 * all times.
+			 */
+			if (cpus_have_final_cap(ARM64_SSBS))
+				return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
+			fallthrough;
+		case SPECTRE_UNAFFECTED:
+			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
+		case SPECTRE_VULNERABLE:
+			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
+		}
+		break;
+	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
+		switch (arm64_get_spectre_bhb_state()) {
+		case SPECTRE_VULNERABLE:
+			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
+		case SPECTRE_MITIGATED:
+			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_AVAIL;
+		case SPECTRE_UNAFFECTED:
+			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_REQUIRED;
+		}
+		return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
+	}
+
+	return -EINVAL;
+}
+
+int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
+{
+	void __user *uaddr = (void __user *)(long)reg->addr;
+	u64 val;
+
+	switch (reg->id) {
+	case KVM_REG_ARM_PSCI_VERSION:
+		val = kvm_psci_version(vcpu);
+		break;
+	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
+	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
+	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
+		val = get_kernel_wa_level(reg->id) & KVM_REG_FEATURE_LEVEL_MASK;
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	if (copy_to_user(uaddr, &val, KVM_REG_SIZE(reg->id)))
+		return -EFAULT;
+
+	return 0;
+}
+
+int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
+{
+	void __user *uaddr = (void __user *)(long)reg->addr;
+	u64 val;
+	int wa_level;
+
+	if (copy_from_user(&val, uaddr, KVM_REG_SIZE(reg->id)))
+		return -EFAULT;
+
+	switch (reg->id) {
+	case KVM_REG_ARM_PSCI_VERSION:
+	{
+		bool wants_02;
+
+		wants_02 = test_bit(KVM_ARM_VCPU_PSCI_0_2, vcpu->arch.features);
+
+		switch (val) {
+		case KVM_ARM_PSCI_0_1:
+			if (wants_02)
+				return -EINVAL;
+			vcpu->kvm->arch.psci_version = val;
+			return 0;
+		case KVM_ARM_PSCI_0_2:
+		case KVM_ARM_PSCI_1_0:
+		case KVM_ARM_PSCI_1_1:
+			if (!wants_02)
+				return -EINVAL;
+			vcpu->kvm->arch.psci_version = val;
+			return 0;
+		}
+		break;
+	}
+
+	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
+	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
+		if (val & ~KVM_REG_FEATURE_LEVEL_MASK)
+			return -EINVAL;
+
+		if (get_kernel_wa_level(reg->id) < val)
+			return -EINVAL;
+
+		return 0;
+
+	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
+		if (val & ~(KVM_REG_FEATURE_LEVEL_MASK |
+			    KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED))
+			return -EINVAL;
+
+		/* The enabled bit must not be set unless the level is AVAIL. */
+		if ((val & KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED) &&
+		    (val & KVM_REG_FEATURE_LEVEL_MASK) != KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL)
+			return -EINVAL;
+
+		/*
+		 * Map all the possible incoming states to the only two we
+		 * really want to deal with.
+		 */
+		switch (val & KVM_REG_FEATURE_LEVEL_MASK) {
+		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL:
+		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_UNKNOWN:
+			wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
+			break;
+		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL:
+		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED:
+			wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		/*
+		 * We can deal with NOT_AVAIL on NOT_REQUIRED, but not the
+		 * other way around.
+		 */
+		if (get_kernel_wa_level(reg->id) < wa_level)
+			return -EINVAL;
+
+		return 0;
+	default:
+		return -ENOENT;
+	}
+
+	return -EINVAL;
+}
diff --git a/arch/arm64/kvm/psci.c b/arch/arm64/kvm/psci.c
index 372da09a2fab..bdfa93ca57d1 100644
--- a/arch/arm64/kvm/psci.c
+++ b/arch/arm64/kvm/psci.c
@@ -439,186 +439,3 @@ int kvm_psci_call(struct kvm_vcpu *vcpu)
 		return -EINVAL;
 	}
 }
-
-int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
-{
-	return 4;		/* PSCI version and three workaround registers */
-}
-
-int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
-{
-	if (put_user(KVM_REG_ARM_PSCI_VERSION, uindices++))
-		return -EFAULT;
-
-	if (put_user(KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1, uindices++))
-		return -EFAULT;
-
-	if (put_user(KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2, uindices++))
-		return -EFAULT;
-
-	if (put_user(KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3, uindices++))
-		return -EFAULT;
-
-	return 0;
-}
-
-#define KVM_REG_FEATURE_LEVEL_WIDTH	4
-#define KVM_REG_FEATURE_LEVEL_MASK	(BIT(KVM_REG_FEATURE_LEVEL_WIDTH) - 1)
-
-/*
- * Convert the workaround level into an easy-to-compare number, where higher
- * values mean better protection.
- */
-static int get_kernel_wa_level(u64 regid)
-{
-	switch (regid) {
-	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
-		switch (arm64_get_spectre_v2_state()) {
-		case SPECTRE_VULNERABLE:
-			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
-		case SPECTRE_MITIGATED:
-			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_AVAIL;
-		case SPECTRE_UNAFFECTED:
-			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_REQUIRED;
-		}
-		return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
-	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
-		switch (arm64_get_spectre_v4_state()) {
-		case SPECTRE_MITIGATED:
-			/*
-			 * As for the hypercall discovery, we pretend we
-			 * don't have any FW mitigation if SSBS is there at
-			 * all times.
-			 */
-			if (cpus_have_final_cap(ARM64_SSBS))
-				return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
-			fallthrough;
-		case SPECTRE_UNAFFECTED:
-			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
-		case SPECTRE_VULNERABLE:
-			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
-		}
-		break;
-	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
-		switch (arm64_get_spectre_bhb_state()) {
-		case SPECTRE_VULNERABLE:
-			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
-		case SPECTRE_MITIGATED:
-			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_AVAIL;
-		case SPECTRE_UNAFFECTED:
-			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_REQUIRED;
-		}
-		return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
-	}
-
-	return -EINVAL;
-}
-
-int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
-{
-	void __user *uaddr = (void __user *)(long)reg->addr;
-	u64 val;
-
-	switch (reg->id) {
-	case KVM_REG_ARM_PSCI_VERSION:
-		val = kvm_psci_version(vcpu);
-		break;
-	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
-	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
-	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
-		val = get_kernel_wa_level(reg->id) & KVM_REG_FEATURE_LEVEL_MASK;
-		break;
-	default:
-		return -ENOENT;
-	}
-
-	if (copy_to_user(uaddr, &val, KVM_REG_SIZE(reg->id)))
-		return -EFAULT;
-
-	return 0;
-}
-
-int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
-{
-	void __user *uaddr = (void __user *)(long)reg->addr;
-	u64 val;
-	int wa_level;
-
-	if (copy_from_user(&val, uaddr, KVM_REG_SIZE(reg->id)))
-		return -EFAULT;
-
-	switch (reg->id) {
-	case KVM_REG_ARM_PSCI_VERSION:
-	{
-		bool wants_02;
-
-		wants_02 = test_bit(KVM_ARM_VCPU_PSCI_0_2, vcpu->arch.features);
-
-		switch (val) {
-		case KVM_ARM_PSCI_0_1:
-			if (wants_02)
-				return -EINVAL;
-			vcpu->kvm->arch.psci_version = val;
-			return 0;
-		case KVM_ARM_PSCI_0_2:
-		case KVM_ARM_PSCI_1_0:
-		case KVM_ARM_PSCI_1_1:
-			if (!wants_02)
-				return -EINVAL;
-			vcpu->kvm->arch.psci_version = val;
-			return 0;
-		}
-		break;
-	}
-
-	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
-	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
-		if (val & ~KVM_REG_FEATURE_LEVEL_MASK)
-			return -EINVAL;
-
-		if (get_kernel_wa_level(reg->id) < val)
-			return -EINVAL;
-
-		return 0;
-
-	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
-		if (val & ~(KVM_REG_FEATURE_LEVEL_MASK |
-			    KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED))
-			return -EINVAL;
-
-		/* The enabled bit must not be set unless the level is AVAIL. */
-		if ((val & KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED) &&
-		    (val & KVM_REG_FEATURE_LEVEL_MASK) != KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL)
-			return -EINVAL;
-
-		/*
-		 * Map all the possible incoming states to the only two we
-		 * really want to deal with.
-		 */
-		switch (val & KVM_REG_FEATURE_LEVEL_MASK) {
-		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL:
-		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_UNKNOWN:
-			wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
-			break;
-		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL:
-		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED:
-			wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
-			break;
-		default:
-			return -EINVAL;
-		}
-
-		/*
-		 * We can deal with NOT_AVAIL on NOT_REQUIRED, but not the
-		 * other way around.
-		 */
-		if (get_kernel_wa_level(reg->id) < wa_level)
-			return -EINVAL;
-
-		return 0;
-	default:
-		return -ENOENT;
-	}
-
-	return -EINVAL;
-}
diff --git a/include/kvm/arm_hypercalls.h b/include/kvm/arm_hypercalls.h
index 0e2509d27910..5d38628a8d04 100644
--- a/include/kvm/arm_hypercalls.h
+++ b/include/kvm/arm_hypercalls.h
@@ -40,4 +40,11 @@ static inline void smccc_set_retval(struct kvm_vcpu *vcpu,
 	vcpu_set_reg(vcpu, 3, a3);
 }
 
+struct kvm_one_reg;
+
+int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu);
+int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices);
+int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
+int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
+
 #endif
diff --git a/include/kvm/arm_psci.h b/include/kvm/arm_psci.h
index 68b96c3826c3..6e55b9283789 100644
--- a/include/kvm/arm_psci.h
+++ b/include/kvm/arm_psci.h
@@ -39,11 +39,4 @@ static inline int kvm_psci_version(struct kvm_vcpu *vcpu)
 
 int kvm_psci_call(struct kvm_vcpu *vcpu);
 
-struct kvm_one_reg;
-
-int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu);
-int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices);
-int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
-int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
-
 #endif /* __KVM_ARM_PSCI_H__ */
-- 
2.35.1.1094.g7c7d902a7c-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] 96+ messages in thread

* [PATCH v5 02/10] KVM: arm64: Setup a framework for hypercall bitmap firmware registers
  2022-04-07  1:15 ` Raghavendra Rao Ananta
  (?)
@ 2022-04-07  1:15   ` Raghavendra Rao Ananta
  -1 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-07  1:15 UTC (permalink / raw)
  To: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose
  Cc: kvm, Catalin Marinas, Peter Shier, linux-kernel, Paolo Bonzini,
	Will Deacon, kvmarm, linux-arm-kernel

KVM regularly introduces new hypercall services to the guests without
any consent from the userspace. This means, the guests can observe
hypercall services in and out as they migrate across various host
kernel versions. This could be a major problem if the guest
discovered a hypercall, started using it, and after getting migrated
to an older kernel realizes that it's no longer available. Depending
on how the guest handles the change, there's a potential chance that
the guest would just panic.

As a result, there's a need for the userspace to elect the services
that it wishes the guest to discover. It can elect these services
based on the kernels spread across its (migration) fleet. To remedy
this, extend the existing firmware psuedo-registers, such as
KVM_REG_ARM_PSCI_VERSION, but by creating a new COPROC register space
for all the hypercall services available.

These firmware registers are categorized based on the service call
owners, but unlike the existing firmware psuedo-registers, they hold
the features supported in the form of a bitmap.

During the VM initialization, the registers are set to upper-limit of
the features supported by the corresponding registers. It's expected
that the VMMs discover the features provided by each register via
GET_ONE_REG, and writeback the desired values using SET_ONE_REG.
KVM allows this modification only until the VM has started.

Some of the standard features are not mapped to any bits of the
registers. But since they can recreate the original problem of
making it available without userspace's consent, they need to
be explicitly added to the hvc_func_default_allowed_list[]. Any
function-id that's not enabled via the bitmap, or not listed in
hvc_func_default_allowed_list[], will be returned as
SMCCC_RET_NOT_SUPPORTED to the guest.

Older userspace code can simply ignore the feature and the
hypercall services will be exposed unconditionally to the guests,
thus ensuring backward compatibility.

In this patch, the framework adds the register only for ARM's standard
secure services (owner value 4). Currently, this includes support only
for ARM True Random Number Generator (TRNG) service, with bit-0 of the
register representing mandatory features of v1.0. Other services are
momentarily added in the upcoming patches.

Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
---
 arch/arm64/include/asm/kvm_host.h |  12 ++++
 arch/arm64/include/uapi/asm/kvm.h |   9 +++
 arch/arm64/kvm/arm.c              |   1 +
 arch/arm64/kvm/guest.c            |   8 ++-
 arch/arm64/kvm/hypercalls.c       | 102 ++++++++++++++++++++++++++++++
 include/kvm/arm_hypercalls.h      |   7 ++
 include/kvm/arm_psci.h            |  12 ++++
 7 files changed, 149 insertions(+), 2 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index e3b25dc6c367..6e663383d7b4 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -101,6 +101,15 @@ struct kvm_s2_mmu {
 struct kvm_arch_memory_slot {
 };
 
+/**
+ * struct kvm_smccc_features: Descriptor the hypercall services exposed to the guests
+ *
+ * @std_bmap: Bitmap of standard secure service calls
+ */
+struct kvm_smccc_features {
+	u64 std_bmap;
+};
+
 struct kvm_arch {
 	struct kvm_s2_mmu mmu;
 
@@ -140,6 +149,9 @@ struct kvm_arch {
 
 	u8 pfr0_csv2;
 	u8 pfr0_csv3;
+
+	/* Hypercall features firmware registers' descriptor */
+	struct kvm_smccc_features smccc_feat;
 };
 
 struct kvm_vcpu_fault_info {
diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h
index c1b6ddc02d2f..56e4bc58a355 100644
--- a/arch/arm64/include/uapi/asm/kvm.h
+++ b/arch/arm64/include/uapi/asm/kvm.h
@@ -332,6 +332,15 @@ struct kvm_arm_copy_mte_tags {
 #define KVM_ARM64_SVE_VLS_WORDS	\
 	((KVM_ARM64_SVE_VQ_MAX - KVM_ARM64_SVE_VQ_MIN) / 64 + 1)
 
+/* Bitmap feature firmware registers */
+#define KVM_REG_ARM_FW_FEAT_BMAP		(0x0016 << KVM_REG_ARM_COPROC_SHIFT)
+#define KVM_REG_ARM_FW_FEAT_BMAP_REG(r)		(KVM_REG_ARM64 | KVM_REG_SIZE_U64 | \
+						KVM_REG_ARM_FW_FEAT_BMAP |	\
+						((r) & 0xffff))
+
+#define KVM_REG_ARM_STD_BMAP			KVM_REG_ARM_FW_FEAT_BMAP_REG(0)
+#define KVM_REG_ARM_STD_BIT_TRNG_V1_0		BIT(0)
+
 /* Device Control API: ARM VGIC */
 #define KVM_DEV_ARM_VGIC_GRP_ADDR	0
 #define KVM_DEV_ARM_VGIC_GRP_DIST_REGS	1
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 523bc934fe2f..a37fadbd617e 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);
+	kvm_arm_init_hypercalls(kvm);
 
 	return ret;
 out_free_stage2_pgd:
diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
index 0d5cca56cbda..8c607199cad1 100644
--- a/arch/arm64/kvm/guest.c
+++ b/arch/arm64/kvm/guest.c
@@ -756,7 +756,9 @@ int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 
 	switch (reg->id & KVM_REG_ARM_COPROC_MASK) {
 	case KVM_REG_ARM_CORE:	return get_core_reg(vcpu, reg);
-	case KVM_REG_ARM_FW:	return kvm_arm_get_fw_reg(vcpu, reg);
+	case KVM_REG_ARM_FW:
+	case KVM_REG_ARM_FW_FEAT_BMAP:
+		return kvm_arm_get_fw_reg(vcpu, reg);
 	case KVM_REG_ARM64_SVE:	return get_sve_reg(vcpu, reg);
 	}
 
@@ -774,7 +776,9 @@ int kvm_arm_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 
 	switch (reg->id & KVM_REG_ARM_COPROC_MASK) {
 	case KVM_REG_ARM_CORE:	return set_core_reg(vcpu, reg);
-	case KVM_REG_ARM_FW:	return kvm_arm_set_fw_reg(vcpu, reg);
+	case KVM_REG_ARM_FW:
+	case KVM_REG_ARM_FW_FEAT_BMAP:
+		return kvm_arm_set_fw_reg(vcpu, reg);
 	case KVM_REG_ARM64_SVE:	return set_sve_reg(vcpu, reg);
 	}
 
diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c
index fa6d9378d8e7..cf04b5ee5f56 100644
--- a/arch/arm64/kvm/hypercalls.c
+++ b/arch/arm64/kvm/hypercalls.c
@@ -58,6 +58,53 @@ static void kvm_ptp_get_time(struct kvm_vcpu *vcpu, u64 *val)
 	val[3] = lower_32_bits(cycles);
 }
 
+/*
+ * List of function-ids that are not gated with the bitmapped feature
+ * firmware registers, and are to be allowed for servicing the call by default.
+ */
+static const u32 hvc_func_default_allowed_list[] = {
+	ARM_SMCCC_VERSION_FUNC_ID,
+	ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
+	ARM_SMCCC_HV_PV_TIME_FEATURES,
+	ARM_SMCCC_HV_PV_TIME_ST,
+	ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID,
+	ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID,
+	ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID,
+};
+
+static bool kvm_hvc_call_default_allowed(struct kvm_vcpu *vcpu, u32 func_id)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(hvc_func_default_allowed_list); i++)
+		if (func_id == hvc_func_default_allowed_list[i])
+			return true;
+
+	return kvm_psci_func_id_is_valid(vcpu, func_id);
+}
+
+static bool kvm_arm_fw_reg_feat_enabled(u64 reg_bmap, u64 feat_bit)
+{
+	return reg_bmap & feat_bit;
+}
+
+static bool kvm_hvc_call_allowed(struct kvm_vcpu *vcpu, u32 func_id)
+{
+	struct kvm_smccc_features *smccc_feat = &vcpu->kvm->arch.smccc_feat;
+
+	switch (func_id) {
+	case ARM_SMCCC_TRNG_VERSION:
+	case ARM_SMCCC_TRNG_FEATURES:
+	case ARM_SMCCC_TRNG_GET_UUID:
+	case ARM_SMCCC_TRNG_RND32:
+	case ARM_SMCCC_TRNG_RND64:
+		return kvm_arm_fw_reg_feat_enabled(smccc_feat->std_bmap,
+						KVM_REG_ARM_STD_BIT_TRNG_V1_0);
+	default:
+		return kvm_hvc_call_default_allowed(vcpu, func_id);
+	}
+}
+
 int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
 {
 	u32 func_id = smccc_get_function(vcpu);
@@ -65,6 +112,9 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
 	u32 feature;
 	gpa_t gpa;
 
+	if (!kvm_hvc_call_allowed(vcpu, func_id))
+		goto out;
+
 	switch (func_id) {
 	case ARM_SMCCC_VERSION_FUNC_ID:
 		val[0] = ARM_SMCCC_VERSION_1_1;
@@ -155,6 +205,7 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
 		return kvm_psci_call(vcpu);
 	}
 
+out:
 	smccc_set_retval(vcpu, val[0], val[1], val[2], val[3]);
 	return 1;
 }
@@ -164,8 +215,16 @@ static const u64 kvm_arm_fw_reg_ids[] = {
 	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1,
 	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2,
 	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3,
+	KVM_REG_ARM_STD_BMAP,
 };
 
+void kvm_arm_init_hypercalls(struct kvm *kvm)
+{
+	struct kvm_smccc_features *smccc_feat = &kvm->arch.smccc_feat;
+
+	smccc_feat->std_bmap = KVM_ARM_SMCCC_STD_FEATURES;
+}
+
 int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
 {
 	return ARRAY_SIZE(kvm_arm_fw_reg_ids);
@@ -237,6 +296,7 @@ static int get_kernel_wa_level(u64 regid)
 
 int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 {
+	struct kvm_smccc_features *smccc_feat = &vcpu->kvm->arch.smccc_feat;
 	void __user *uaddr = (void __user *)(long)reg->addr;
 	u64 val;
 
@@ -249,6 +309,9 @@ int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
 		val = get_kernel_wa_level(reg->id) & KVM_REG_FEATURE_LEVEL_MASK;
 		break;
+	case KVM_REG_ARM_STD_BMAP:
+		val = READ_ONCE(smccc_feat->std_bmap);
+		break;
 	default:
 		return -ENOENT;
 	}
@@ -259,6 +322,43 @@ int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 	return 0;
 }
 
+static int kvm_arm_set_fw_reg_bmap(struct kvm_vcpu *vcpu, u64 reg_id, u64 val)
+{
+	int ret = 0;
+	struct kvm *kvm = vcpu->kvm;
+	struct kvm_smccc_features *smccc_feat = &kvm->arch.smccc_feat;
+	u64 *fw_reg_bmap, fw_reg_features;
+
+	switch (reg_id) {
+	case KVM_REG_ARM_STD_BMAP:
+		fw_reg_bmap = &smccc_feat->std_bmap;
+		fw_reg_features = KVM_ARM_SMCCC_STD_FEATURES;
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	/* Check for unsupported bit */
+	if (val & ~fw_reg_features)
+		return -EINVAL;
+
+	mutex_lock(&kvm->lock);
+
+	/*
+	 * If the VM (any vCPU) has already started running, return success
+	 * if there's no change in the value. Else, return -EBUSY.
+	 */
+	if (test_bit(KVM_ARCH_FLAG_HAS_RAN_ONCE, &kvm->arch.flags)) {
+		ret = *fw_reg_bmap != val ? -EBUSY : 0;
+		goto out;
+	}
+
+	WRITE_ONCE(*fw_reg_bmap, val);
+out:
+	mutex_unlock(&kvm->lock);
+	return ret;
+}
+
 int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 {
 	void __user *uaddr = (void __user *)(long)reg->addr;
@@ -337,6 +437,8 @@ int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 			return -EINVAL;
 
 		return 0;
+	case KVM_REG_ARM_STD_BMAP:
+		return kvm_arm_set_fw_reg_bmap(vcpu, reg->id, val);
 	default:
 		return -ENOENT;
 	}
diff --git a/include/kvm/arm_hypercalls.h b/include/kvm/arm_hypercalls.h
index 5d38628a8d04..fd3ff350ee9d 100644
--- a/include/kvm/arm_hypercalls.h
+++ b/include/kvm/arm_hypercalls.h
@@ -6,6 +6,12 @@
 
 #include <asm/kvm_emulate.h>
 
+/* Last valid bits of the bitmapped firmware registers */
+#define KVM_REG_ARM_STD_BMAP_BIT_MAX		0
+
+#define KVM_ARM_SMCCC_STD_FEATURES \
+	GENMASK_ULL(KVM_REG_ARM_STD_BMAP_BIT_MAX, 0)
+
 int kvm_hvc_call_handler(struct kvm_vcpu *vcpu);
 
 static inline u32 smccc_get_function(struct kvm_vcpu *vcpu)
@@ -42,6 +48,7 @@ static inline void smccc_set_retval(struct kvm_vcpu *vcpu,
 
 struct kvm_one_reg;
 
+void kvm_arm_init_hypercalls(struct kvm *kvm);
 int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu);
 int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices);
 int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
diff --git a/include/kvm/arm_psci.h b/include/kvm/arm_psci.h
index 6e55b9283789..d7a87367de56 100644
--- a/include/kvm/arm_psci.h
+++ b/include/kvm/arm_psci.h
@@ -36,6 +36,18 @@ static inline int kvm_psci_version(struct kvm_vcpu *vcpu)
 	return KVM_ARM_PSCI_0_1;
 }
 
+static inline bool kvm_psci_func_id_is_valid(struct kvm_vcpu *vcpu, u32 func_id)
+{
+	/* PSCI 0.1 doesn't comply with the standard SMCCC */
+	if (kvm_psci_version(vcpu) == KVM_ARM_PSCI_0_1)
+		return (func_id == KVM_PSCI_FN_CPU_OFF || func_id == KVM_PSCI_FN_CPU_ON);
+
+	if (ARM_SMCCC_OWNER_NUM(func_id) == ARM_SMCCC_OWNER_STANDARD &&
+		ARM_SMCCC_FUNC_NUM(func_id) >= 0 && ARM_SMCCC_FUNC_NUM(func_id) <= 0x1f)
+		return true;
+
+	return false;
+}
 
 int kvm_psci_call(struct kvm_vcpu *vcpu);
 
-- 
2.35.1.1094.g7c7d902a7c-goog

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

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

* [PATCH v5 02/10] KVM: arm64: Setup a framework for hypercall bitmap firmware registers
@ 2022-04-07  1:15   ` Raghavendra Rao Ananta
  0 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-07  1:15 UTC (permalink / raw)
  To: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose
  Cc: Paolo Bonzini, Catalin Marinas, Will Deacon, Peter Shier,
	Ricardo Koller, Oliver Upton, Reiji Watanabe, Jing Zhang,
	Raghavendra Rao Anata, linux-arm-kernel, kvmarm, linux-kernel,
	kvm

KVM regularly introduces new hypercall services to the guests without
any consent from the userspace. This means, the guests can observe
hypercall services in and out as they migrate across various host
kernel versions. This could be a major problem if the guest
discovered a hypercall, started using it, and after getting migrated
to an older kernel realizes that it's no longer available. Depending
on how the guest handles the change, there's a potential chance that
the guest would just panic.

As a result, there's a need for the userspace to elect the services
that it wishes the guest to discover. It can elect these services
based on the kernels spread across its (migration) fleet. To remedy
this, extend the existing firmware psuedo-registers, such as
KVM_REG_ARM_PSCI_VERSION, but by creating a new COPROC register space
for all the hypercall services available.

These firmware registers are categorized based on the service call
owners, but unlike the existing firmware psuedo-registers, they hold
the features supported in the form of a bitmap.

During the VM initialization, the registers are set to upper-limit of
the features supported by the corresponding registers. It's expected
that the VMMs discover the features provided by each register via
GET_ONE_REG, and writeback the desired values using SET_ONE_REG.
KVM allows this modification only until the VM has started.

Some of the standard features are not mapped to any bits of the
registers. But since they can recreate the original problem of
making it available without userspace's consent, they need to
be explicitly added to the hvc_func_default_allowed_list[]. Any
function-id that's not enabled via the bitmap, or not listed in
hvc_func_default_allowed_list[], will be returned as
SMCCC_RET_NOT_SUPPORTED to the guest.

Older userspace code can simply ignore the feature and the
hypercall services will be exposed unconditionally to the guests,
thus ensuring backward compatibility.

In this patch, the framework adds the register only for ARM's standard
secure services (owner value 4). Currently, this includes support only
for ARM True Random Number Generator (TRNG) service, with bit-0 of the
register representing mandatory features of v1.0. Other services are
momentarily added in the upcoming patches.

Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
---
 arch/arm64/include/asm/kvm_host.h |  12 ++++
 arch/arm64/include/uapi/asm/kvm.h |   9 +++
 arch/arm64/kvm/arm.c              |   1 +
 arch/arm64/kvm/guest.c            |   8 ++-
 arch/arm64/kvm/hypercalls.c       | 102 ++++++++++++++++++++++++++++++
 include/kvm/arm_hypercalls.h      |   7 ++
 include/kvm/arm_psci.h            |  12 ++++
 7 files changed, 149 insertions(+), 2 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index e3b25dc6c367..6e663383d7b4 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -101,6 +101,15 @@ struct kvm_s2_mmu {
 struct kvm_arch_memory_slot {
 };
 
+/**
+ * struct kvm_smccc_features: Descriptor the hypercall services exposed to the guests
+ *
+ * @std_bmap: Bitmap of standard secure service calls
+ */
+struct kvm_smccc_features {
+	u64 std_bmap;
+};
+
 struct kvm_arch {
 	struct kvm_s2_mmu mmu;
 
@@ -140,6 +149,9 @@ struct kvm_arch {
 
 	u8 pfr0_csv2;
 	u8 pfr0_csv3;
+
+	/* Hypercall features firmware registers' descriptor */
+	struct kvm_smccc_features smccc_feat;
 };
 
 struct kvm_vcpu_fault_info {
diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h
index c1b6ddc02d2f..56e4bc58a355 100644
--- a/arch/arm64/include/uapi/asm/kvm.h
+++ b/arch/arm64/include/uapi/asm/kvm.h
@@ -332,6 +332,15 @@ struct kvm_arm_copy_mte_tags {
 #define KVM_ARM64_SVE_VLS_WORDS	\
 	((KVM_ARM64_SVE_VQ_MAX - KVM_ARM64_SVE_VQ_MIN) / 64 + 1)
 
+/* Bitmap feature firmware registers */
+#define KVM_REG_ARM_FW_FEAT_BMAP		(0x0016 << KVM_REG_ARM_COPROC_SHIFT)
+#define KVM_REG_ARM_FW_FEAT_BMAP_REG(r)		(KVM_REG_ARM64 | KVM_REG_SIZE_U64 | \
+						KVM_REG_ARM_FW_FEAT_BMAP |	\
+						((r) & 0xffff))
+
+#define KVM_REG_ARM_STD_BMAP			KVM_REG_ARM_FW_FEAT_BMAP_REG(0)
+#define KVM_REG_ARM_STD_BIT_TRNG_V1_0		BIT(0)
+
 /* Device Control API: ARM VGIC */
 #define KVM_DEV_ARM_VGIC_GRP_ADDR	0
 #define KVM_DEV_ARM_VGIC_GRP_DIST_REGS	1
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 523bc934fe2f..a37fadbd617e 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);
+	kvm_arm_init_hypercalls(kvm);
 
 	return ret;
 out_free_stage2_pgd:
diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
index 0d5cca56cbda..8c607199cad1 100644
--- a/arch/arm64/kvm/guest.c
+++ b/arch/arm64/kvm/guest.c
@@ -756,7 +756,9 @@ int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 
 	switch (reg->id & KVM_REG_ARM_COPROC_MASK) {
 	case KVM_REG_ARM_CORE:	return get_core_reg(vcpu, reg);
-	case KVM_REG_ARM_FW:	return kvm_arm_get_fw_reg(vcpu, reg);
+	case KVM_REG_ARM_FW:
+	case KVM_REG_ARM_FW_FEAT_BMAP:
+		return kvm_arm_get_fw_reg(vcpu, reg);
 	case KVM_REG_ARM64_SVE:	return get_sve_reg(vcpu, reg);
 	}
 
@@ -774,7 +776,9 @@ int kvm_arm_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 
 	switch (reg->id & KVM_REG_ARM_COPROC_MASK) {
 	case KVM_REG_ARM_CORE:	return set_core_reg(vcpu, reg);
-	case KVM_REG_ARM_FW:	return kvm_arm_set_fw_reg(vcpu, reg);
+	case KVM_REG_ARM_FW:
+	case KVM_REG_ARM_FW_FEAT_BMAP:
+		return kvm_arm_set_fw_reg(vcpu, reg);
 	case KVM_REG_ARM64_SVE:	return set_sve_reg(vcpu, reg);
 	}
 
diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c
index fa6d9378d8e7..cf04b5ee5f56 100644
--- a/arch/arm64/kvm/hypercalls.c
+++ b/arch/arm64/kvm/hypercalls.c
@@ -58,6 +58,53 @@ static void kvm_ptp_get_time(struct kvm_vcpu *vcpu, u64 *val)
 	val[3] = lower_32_bits(cycles);
 }
 
+/*
+ * List of function-ids that are not gated with the bitmapped feature
+ * firmware registers, and are to be allowed for servicing the call by default.
+ */
+static const u32 hvc_func_default_allowed_list[] = {
+	ARM_SMCCC_VERSION_FUNC_ID,
+	ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
+	ARM_SMCCC_HV_PV_TIME_FEATURES,
+	ARM_SMCCC_HV_PV_TIME_ST,
+	ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID,
+	ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID,
+	ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID,
+};
+
+static bool kvm_hvc_call_default_allowed(struct kvm_vcpu *vcpu, u32 func_id)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(hvc_func_default_allowed_list); i++)
+		if (func_id == hvc_func_default_allowed_list[i])
+			return true;
+
+	return kvm_psci_func_id_is_valid(vcpu, func_id);
+}
+
+static bool kvm_arm_fw_reg_feat_enabled(u64 reg_bmap, u64 feat_bit)
+{
+	return reg_bmap & feat_bit;
+}
+
+static bool kvm_hvc_call_allowed(struct kvm_vcpu *vcpu, u32 func_id)
+{
+	struct kvm_smccc_features *smccc_feat = &vcpu->kvm->arch.smccc_feat;
+
+	switch (func_id) {
+	case ARM_SMCCC_TRNG_VERSION:
+	case ARM_SMCCC_TRNG_FEATURES:
+	case ARM_SMCCC_TRNG_GET_UUID:
+	case ARM_SMCCC_TRNG_RND32:
+	case ARM_SMCCC_TRNG_RND64:
+		return kvm_arm_fw_reg_feat_enabled(smccc_feat->std_bmap,
+						KVM_REG_ARM_STD_BIT_TRNG_V1_0);
+	default:
+		return kvm_hvc_call_default_allowed(vcpu, func_id);
+	}
+}
+
 int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
 {
 	u32 func_id = smccc_get_function(vcpu);
@@ -65,6 +112,9 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
 	u32 feature;
 	gpa_t gpa;
 
+	if (!kvm_hvc_call_allowed(vcpu, func_id))
+		goto out;
+
 	switch (func_id) {
 	case ARM_SMCCC_VERSION_FUNC_ID:
 		val[0] = ARM_SMCCC_VERSION_1_1;
@@ -155,6 +205,7 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
 		return kvm_psci_call(vcpu);
 	}
 
+out:
 	smccc_set_retval(vcpu, val[0], val[1], val[2], val[3]);
 	return 1;
 }
@@ -164,8 +215,16 @@ static const u64 kvm_arm_fw_reg_ids[] = {
 	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1,
 	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2,
 	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3,
+	KVM_REG_ARM_STD_BMAP,
 };
 
+void kvm_arm_init_hypercalls(struct kvm *kvm)
+{
+	struct kvm_smccc_features *smccc_feat = &kvm->arch.smccc_feat;
+
+	smccc_feat->std_bmap = KVM_ARM_SMCCC_STD_FEATURES;
+}
+
 int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
 {
 	return ARRAY_SIZE(kvm_arm_fw_reg_ids);
@@ -237,6 +296,7 @@ static int get_kernel_wa_level(u64 regid)
 
 int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 {
+	struct kvm_smccc_features *smccc_feat = &vcpu->kvm->arch.smccc_feat;
 	void __user *uaddr = (void __user *)(long)reg->addr;
 	u64 val;
 
@@ -249,6 +309,9 @@ int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
 		val = get_kernel_wa_level(reg->id) & KVM_REG_FEATURE_LEVEL_MASK;
 		break;
+	case KVM_REG_ARM_STD_BMAP:
+		val = READ_ONCE(smccc_feat->std_bmap);
+		break;
 	default:
 		return -ENOENT;
 	}
@@ -259,6 +322,43 @@ int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 	return 0;
 }
 
+static int kvm_arm_set_fw_reg_bmap(struct kvm_vcpu *vcpu, u64 reg_id, u64 val)
+{
+	int ret = 0;
+	struct kvm *kvm = vcpu->kvm;
+	struct kvm_smccc_features *smccc_feat = &kvm->arch.smccc_feat;
+	u64 *fw_reg_bmap, fw_reg_features;
+
+	switch (reg_id) {
+	case KVM_REG_ARM_STD_BMAP:
+		fw_reg_bmap = &smccc_feat->std_bmap;
+		fw_reg_features = KVM_ARM_SMCCC_STD_FEATURES;
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	/* Check for unsupported bit */
+	if (val & ~fw_reg_features)
+		return -EINVAL;
+
+	mutex_lock(&kvm->lock);
+
+	/*
+	 * If the VM (any vCPU) has already started running, return success
+	 * if there's no change in the value. Else, return -EBUSY.
+	 */
+	if (test_bit(KVM_ARCH_FLAG_HAS_RAN_ONCE, &kvm->arch.flags)) {
+		ret = *fw_reg_bmap != val ? -EBUSY : 0;
+		goto out;
+	}
+
+	WRITE_ONCE(*fw_reg_bmap, val);
+out:
+	mutex_unlock(&kvm->lock);
+	return ret;
+}
+
 int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 {
 	void __user *uaddr = (void __user *)(long)reg->addr;
@@ -337,6 +437,8 @@ int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 			return -EINVAL;
 
 		return 0;
+	case KVM_REG_ARM_STD_BMAP:
+		return kvm_arm_set_fw_reg_bmap(vcpu, reg->id, val);
 	default:
 		return -ENOENT;
 	}
diff --git a/include/kvm/arm_hypercalls.h b/include/kvm/arm_hypercalls.h
index 5d38628a8d04..fd3ff350ee9d 100644
--- a/include/kvm/arm_hypercalls.h
+++ b/include/kvm/arm_hypercalls.h
@@ -6,6 +6,12 @@
 
 #include <asm/kvm_emulate.h>
 
+/* Last valid bits of the bitmapped firmware registers */
+#define KVM_REG_ARM_STD_BMAP_BIT_MAX		0
+
+#define KVM_ARM_SMCCC_STD_FEATURES \
+	GENMASK_ULL(KVM_REG_ARM_STD_BMAP_BIT_MAX, 0)
+
 int kvm_hvc_call_handler(struct kvm_vcpu *vcpu);
 
 static inline u32 smccc_get_function(struct kvm_vcpu *vcpu)
@@ -42,6 +48,7 @@ static inline void smccc_set_retval(struct kvm_vcpu *vcpu,
 
 struct kvm_one_reg;
 
+void kvm_arm_init_hypercalls(struct kvm *kvm);
 int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu);
 int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices);
 int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
diff --git a/include/kvm/arm_psci.h b/include/kvm/arm_psci.h
index 6e55b9283789..d7a87367de56 100644
--- a/include/kvm/arm_psci.h
+++ b/include/kvm/arm_psci.h
@@ -36,6 +36,18 @@ static inline int kvm_psci_version(struct kvm_vcpu *vcpu)
 	return KVM_ARM_PSCI_0_1;
 }
 
+static inline bool kvm_psci_func_id_is_valid(struct kvm_vcpu *vcpu, u32 func_id)
+{
+	/* PSCI 0.1 doesn't comply with the standard SMCCC */
+	if (kvm_psci_version(vcpu) == KVM_ARM_PSCI_0_1)
+		return (func_id == KVM_PSCI_FN_CPU_OFF || func_id == KVM_PSCI_FN_CPU_ON);
+
+	if (ARM_SMCCC_OWNER_NUM(func_id) == ARM_SMCCC_OWNER_STANDARD &&
+		ARM_SMCCC_FUNC_NUM(func_id) >= 0 && ARM_SMCCC_FUNC_NUM(func_id) <= 0x1f)
+		return true;
+
+	return false;
+}
 
 int kvm_psci_call(struct kvm_vcpu *vcpu);
 
-- 
2.35.1.1094.g7c7d902a7c-goog


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

* [PATCH v5 02/10] KVM: arm64: Setup a framework for hypercall bitmap firmware registers
@ 2022-04-07  1:15   ` Raghavendra Rao Ananta
  0 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-07  1:15 UTC (permalink / raw)
  To: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose
  Cc: Paolo Bonzini, Catalin Marinas, Will Deacon, Peter Shier,
	Ricardo Koller, Oliver Upton, Reiji Watanabe, Jing Zhang,
	Raghavendra Rao Anata, linux-arm-kernel, kvmarm, linux-kernel,
	kvm

KVM regularly introduces new hypercall services to the guests without
any consent from the userspace. This means, the guests can observe
hypercall services in and out as they migrate across various host
kernel versions. This could be a major problem if the guest
discovered a hypercall, started using it, and after getting migrated
to an older kernel realizes that it's no longer available. Depending
on how the guest handles the change, there's a potential chance that
the guest would just panic.

As a result, there's a need for the userspace to elect the services
that it wishes the guest to discover. It can elect these services
based on the kernels spread across its (migration) fleet. To remedy
this, extend the existing firmware psuedo-registers, such as
KVM_REG_ARM_PSCI_VERSION, but by creating a new COPROC register space
for all the hypercall services available.

These firmware registers are categorized based on the service call
owners, but unlike the existing firmware psuedo-registers, they hold
the features supported in the form of a bitmap.

During the VM initialization, the registers are set to upper-limit of
the features supported by the corresponding registers. It's expected
that the VMMs discover the features provided by each register via
GET_ONE_REG, and writeback the desired values using SET_ONE_REG.
KVM allows this modification only until the VM has started.

Some of the standard features are not mapped to any bits of the
registers. But since they can recreate the original problem of
making it available without userspace's consent, they need to
be explicitly added to the hvc_func_default_allowed_list[]. Any
function-id that's not enabled via the bitmap, or not listed in
hvc_func_default_allowed_list[], will be returned as
SMCCC_RET_NOT_SUPPORTED to the guest.

Older userspace code can simply ignore the feature and the
hypercall services will be exposed unconditionally to the guests,
thus ensuring backward compatibility.

In this patch, the framework adds the register only for ARM's standard
secure services (owner value 4). Currently, this includes support only
for ARM True Random Number Generator (TRNG) service, with bit-0 of the
register representing mandatory features of v1.0. Other services are
momentarily added in the upcoming patches.

Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
---
 arch/arm64/include/asm/kvm_host.h |  12 ++++
 arch/arm64/include/uapi/asm/kvm.h |   9 +++
 arch/arm64/kvm/arm.c              |   1 +
 arch/arm64/kvm/guest.c            |   8 ++-
 arch/arm64/kvm/hypercalls.c       | 102 ++++++++++++++++++++++++++++++
 include/kvm/arm_hypercalls.h      |   7 ++
 include/kvm/arm_psci.h            |  12 ++++
 7 files changed, 149 insertions(+), 2 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index e3b25dc6c367..6e663383d7b4 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -101,6 +101,15 @@ struct kvm_s2_mmu {
 struct kvm_arch_memory_slot {
 };
 
+/**
+ * struct kvm_smccc_features: Descriptor the hypercall services exposed to the guests
+ *
+ * @std_bmap: Bitmap of standard secure service calls
+ */
+struct kvm_smccc_features {
+	u64 std_bmap;
+};
+
 struct kvm_arch {
 	struct kvm_s2_mmu mmu;
 
@@ -140,6 +149,9 @@ struct kvm_arch {
 
 	u8 pfr0_csv2;
 	u8 pfr0_csv3;
+
+	/* Hypercall features firmware registers' descriptor */
+	struct kvm_smccc_features smccc_feat;
 };
 
 struct kvm_vcpu_fault_info {
diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h
index c1b6ddc02d2f..56e4bc58a355 100644
--- a/arch/arm64/include/uapi/asm/kvm.h
+++ b/arch/arm64/include/uapi/asm/kvm.h
@@ -332,6 +332,15 @@ struct kvm_arm_copy_mte_tags {
 #define KVM_ARM64_SVE_VLS_WORDS	\
 	((KVM_ARM64_SVE_VQ_MAX - KVM_ARM64_SVE_VQ_MIN) / 64 + 1)
 
+/* Bitmap feature firmware registers */
+#define KVM_REG_ARM_FW_FEAT_BMAP		(0x0016 << KVM_REG_ARM_COPROC_SHIFT)
+#define KVM_REG_ARM_FW_FEAT_BMAP_REG(r)		(KVM_REG_ARM64 | KVM_REG_SIZE_U64 | \
+						KVM_REG_ARM_FW_FEAT_BMAP |	\
+						((r) & 0xffff))
+
+#define KVM_REG_ARM_STD_BMAP			KVM_REG_ARM_FW_FEAT_BMAP_REG(0)
+#define KVM_REG_ARM_STD_BIT_TRNG_V1_0		BIT(0)
+
 /* Device Control API: ARM VGIC */
 #define KVM_DEV_ARM_VGIC_GRP_ADDR	0
 #define KVM_DEV_ARM_VGIC_GRP_DIST_REGS	1
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 523bc934fe2f..a37fadbd617e 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);
+	kvm_arm_init_hypercalls(kvm);
 
 	return ret;
 out_free_stage2_pgd:
diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
index 0d5cca56cbda..8c607199cad1 100644
--- a/arch/arm64/kvm/guest.c
+++ b/arch/arm64/kvm/guest.c
@@ -756,7 +756,9 @@ int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 
 	switch (reg->id & KVM_REG_ARM_COPROC_MASK) {
 	case KVM_REG_ARM_CORE:	return get_core_reg(vcpu, reg);
-	case KVM_REG_ARM_FW:	return kvm_arm_get_fw_reg(vcpu, reg);
+	case KVM_REG_ARM_FW:
+	case KVM_REG_ARM_FW_FEAT_BMAP:
+		return kvm_arm_get_fw_reg(vcpu, reg);
 	case KVM_REG_ARM64_SVE:	return get_sve_reg(vcpu, reg);
 	}
 
@@ -774,7 +776,9 @@ int kvm_arm_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 
 	switch (reg->id & KVM_REG_ARM_COPROC_MASK) {
 	case KVM_REG_ARM_CORE:	return set_core_reg(vcpu, reg);
-	case KVM_REG_ARM_FW:	return kvm_arm_set_fw_reg(vcpu, reg);
+	case KVM_REG_ARM_FW:
+	case KVM_REG_ARM_FW_FEAT_BMAP:
+		return kvm_arm_set_fw_reg(vcpu, reg);
 	case KVM_REG_ARM64_SVE:	return set_sve_reg(vcpu, reg);
 	}
 
diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c
index fa6d9378d8e7..cf04b5ee5f56 100644
--- a/arch/arm64/kvm/hypercalls.c
+++ b/arch/arm64/kvm/hypercalls.c
@@ -58,6 +58,53 @@ static void kvm_ptp_get_time(struct kvm_vcpu *vcpu, u64 *val)
 	val[3] = lower_32_bits(cycles);
 }
 
+/*
+ * List of function-ids that are not gated with the bitmapped feature
+ * firmware registers, and are to be allowed for servicing the call by default.
+ */
+static const u32 hvc_func_default_allowed_list[] = {
+	ARM_SMCCC_VERSION_FUNC_ID,
+	ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
+	ARM_SMCCC_HV_PV_TIME_FEATURES,
+	ARM_SMCCC_HV_PV_TIME_ST,
+	ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID,
+	ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID,
+	ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID,
+};
+
+static bool kvm_hvc_call_default_allowed(struct kvm_vcpu *vcpu, u32 func_id)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(hvc_func_default_allowed_list); i++)
+		if (func_id == hvc_func_default_allowed_list[i])
+			return true;
+
+	return kvm_psci_func_id_is_valid(vcpu, func_id);
+}
+
+static bool kvm_arm_fw_reg_feat_enabled(u64 reg_bmap, u64 feat_bit)
+{
+	return reg_bmap & feat_bit;
+}
+
+static bool kvm_hvc_call_allowed(struct kvm_vcpu *vcpu, u32 func_id)
+{
+	struct kvm_smccc_features *smccc_feat = &vcpu->kvm->arch.smccc_feat;
+
+	switch (func_id) {
+	case ARM_SMCCC_TRNG_VERSION:
+	case ARM_SMCCC_TRNG_FEATURES:
+	case ARM_SMCCC_TRNG_GET_UUID:
+	case ARM_SMCCC_TRNG_RND32:
+	case ARM_SMCCC_TRNG_RND64:
+		return kvm_arm_fw_reg_feat_enabled(smccc_feat->std_bmap,
+						KVM_REG_ARM_STD_BIT_TRNG_V1_0);
+	default:
+		return kvm_hvc_call_default_allowed(vcpu, func_id);
+	}
+}
+
 int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
 {
 	u32 func_id = smccc_get_function(vcpu);
@@ -65,6 +112,9 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
 	u32 feature;
 	gpa_t gpa;
 
+	if (!kvm_hvc_call_allowed(vcpu, func_id))
+		goto out;
+
 	switch (func_id) {
 	case ARM_SMCCC_VERSION_FUNC_ID:
 		val[0] = ARM_SMCCC_VERSION_1_1;
@@ -155,6 +205,7 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
 		return kvm_psci_call(vcpu);
 	}
 
+out:
 	smccc_set_retval(vcpu, val[0], val[1], val[2], val[3]);
 	return 1;
 }
@@ -164,8 +215,16 @@ static const u64 kvm_arm_fw_reg_ids[] = {
 	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1,
 	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2,
 	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3,
+	KVM_REG_ARM_STD_BMAP,
 };
 
+void kvm_arm_init_hypercalls(struct kvm *kvm)
+{
+	struct kvm_smccc_features *smccc_feat = &kvm->arch.smccc_feat;
+
+	smccc_feat->std_bmap = KVM_ARM_SMCCC_STD_FEATURES;
+}
+
 int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
 {
 	return ARRAY_SIZE(kvm_arm_fw_reg_ids);
@@ -237,6 +296,7 @@ static int get_kernel_wa_level(u64 regid)
 
 int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 {
+	struct kvm_smccc_features *smccc_feat = &vcpu->kvm->arch.smccc_feat;
 	void __user *uaddr = (void __user *)(long)reg->addr;
 	u64 val;
 
@@ -249,6 +309,9 @@ int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
 		val = get_kernel_wa_level(reg->id) & KVM_REG_FEATURE_LEVEL_MASK;
 		break;
+	case KVM_REG_ARM_STD_BMAP:
+		val = READ_ONCE(smccc_feat->std_bmap);
+		break;
 	default:
 		return -ENOENT;
 	}
@@ -259,6 +322,43 @@ int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 	return 0;
 }
 
+static int kvm_arm_set_fw_reg_bmap(struct kvm_vcpu *vcpu, u64 reg_id, u64 val)
+{
+	int ret = 0;
+	struct kvm *kvm = vcpu->kvm;
+	struct kvm_smccc_features *smccc_feat = &kvm->arch.smccc_feat;
+	u64 *fw_reg_bmap, fw_reg_features;
+
+	switch (reg_id) {
+	case KVM_REG_ARM_STD_BMAP:
+		fw_reg_bmap = &smccc_feat->std_bmap;
+		fw_reg_features = KVM_ARM_SMCCC_STD_FEATURES;
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	/* Check for unsupported bit */
+	if (val & ~fw_reg_features)
+		return -EINVAL;
+
+	mutex_lock(&kvm->lock);
+
+	/*
+	 * If the VM (any vCPU) has already started running, return success
+	 * if there's no change in the value. Else, return -EBUSY.
+	 */
+	if (test_bit(KVM_ARCH_FLAG_HAS_RAN_ONCE, &kvm->arch.flags)) {
+		ret = *fw_reg_bmap != val ? -EBUSY : 0;
+		goto out;
+	}
+
+	WRITE_ONCE(*fw_reg_bmap, val);
+out:
+	mutex_unlock(&kvm->lock);
+	return ret;
+}
+
 int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 {
 	void __user *uaddr = (void __user *)(long)reg->addr;
@@ -337,6 +437,8 @@ int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 			return -EINVAL;
 
 		return 0;
+	case KVM_REG_ARM_STD_BMAP:
+		return kvm_arm_set_fw_reg_bmap(vcpu, reg->id, val);
 	default:
 		return -ENOENT;
 	}
diff --git a/include/kvm/arm_hypercalls.h b/include/kvm/arm_hypercalls.h
index 5d38628a8d04..fd3ff350ee9d 100644
--- a/include/kvm/arm_hypercalls.h
+++ b/include/kvm/arm_hypercalls.h
@@ -6,6 +6,12 @@
 
 #include <asm/kvm_emulate.h>
 
+/* Last valid bits of the bitmapped firmware registers */
+#define KVM_REG_ARM_STD_BMAP_BIT_MAX		0
+
+#define KVM_ARM_SMCCC_STD_FEATURES \
+	GENMASK_ULL(KVM_REG_ARM_STD_BMAP_BIT_MAX, 0)
+
 int kvm_hvc_call_handler(struct kvm_vcpu *vcpu);
 
 static inline u32 smccc_get_function(struct kvm_vcpu *vcpu)
@@ -42,6 +48,7 @@ static inline void smccc_set_retval(struct kvm_vcpu *vcpu,
 
 struct kvm_one_reg;
 
+void kvm_arm_init_hypercalls(struct kvm *kvm);
 int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu);
 int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices);
 int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
diff --git a/include/kvm/arm_psci.h b/include/kvm/arm_psci.h
index 6e55b9283789..d7a87367de56 100644
--- a/include/kvm/arm_psci.h
+++ b/include/kvm/arm_psci.h
@@ -36,6 +36,18 @@ static inline int kvm_psci_version(struct kvm_vcpu *vcpu)
 	return KVM_ARM_PSCI_0_1;
 }
 
+static inline bool kvm_psci_func_id_is_valid(struct kvm_vcpu *vcpu, u32 func_id)
+{
+	/* PSCI 0.1 doesn't comply with the standard SMCCC */
+	if (kvm_psci_version(vcpu) == KVM_ARM_PSCI_0_1)
+		return (func_id == KVM_PSCI_FN_CPU_OFF || func_id == KVM_PSCI_FN_CPU_ON);
+
+	if (ARM_SMCCC_OWNER_NUM(func_id) == ARM_SMCCC_OWNER_STANDARD &&
+		ARM_SMCCC_FUNC_NUM(func_id) >= 0 && ARM_SMCCC_FUNC_NUM(func_id) <= 0x1f)
+		return true;
+
+	return false;
+}
 
 int kvm_psci_call(struct kvm_vcpu *vcpu);
 
-- 
2.35.1.1094.g7c7d902a7c-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] 96+ messages in thread

* [PATCH v5 03/10] KVM: arm64: Add standard hypervisor firmware register
  2022-04-07  1:15 ` Raghavendra Rao Ananta
  (?)
@ 2022-04-07  1:15   ` Raghavendra Rao Ananta
  -1 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-07  1:15 UTC (permalink / raw)
  To: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose
  Cc: kvm, Catalin Marinas, Peter Shier, linux-kernel, Paolo Bonzini,
	Will Deacon, kvmarm, linux-arm-kernel

Introduce the firmware register to hold the standard hypervisor
service calls (owner value 5) as a bitmap. The bitmap represents
the features that'll be enabled for the guest, as configured by
the user-space. Currently, this includes support only for
Paravirtualized time, represented by bit-0.

Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
---
 arch/arm64/include/asm/kvm_host.h |  2 ++
 arch/arm64/include/uapi/asm/kvm.h |  3 +++
 arch/arm64/kvm/hypercalls.c       | 21 ++++++++++++++++++---
 include/kvm/arm_hypercalls.h      |  4 ++++
 4 files changed, 27 insertions(+), 3 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 6e663383d7b4..20165242ebd9 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -105,9 +105,11 @@ struct kvm_arch_memory_slot {
  * struct kvm_smccc_features: Descriptor the hypercall services exposed to the guests
  *
  * @std_bmap: Bitmap of standard secure service calls
+ * @std_hyp_bmap: Bitmap of standard hypervisor service calls
  */
 struct kvm_smccc_features {
 	u64 std_bmap;
+	u64 std_hyp_bmap;
 };
 
 struct kvm_arch {
diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h
index 56e4bc58a355..67353bf4e69d 100644
--- a/arch/arm64/include/uapi/asm/kvm.h
+++ b/arch/arm64/include/uapi/asm/kvm.h
@@ -341,6 +341,9 @@ struct kvm_arm_copy_mte_tags {
 #define KVM_REG_ARM_STD_BMAP			KVM_REG_ARM_FW_FEAT_BMAP_REG(0)
 #define KVM_REG_ARM_STD_BIT_TRNG_V1_0		BIT(0)
 
+#define KVM_REG_ARM_STD_HYP_BMAP		KVM_REG_ARM_FW_FEAT_BMAP_REG(1)
+#define KVM_REG_ARM_STD_HYP_BIT_PV_TIME		BIT(0)
+
 /* Device Control API: ARM VGIC */
 #define KVM_DEV_ARM_VGIC_GRP_ADDR	0
 #define KVM_DEV_ARM_VGIC_GRP_DIST_REGS	1
diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c
index cf04b5ee5f56..64ae6c7e7145 100644
--- a/arch/arm64/kvm/hypercalls.c
+++ b/arch/arm64/kvm/hypercalls.c
@@ -65,8 +65,6 @@ static void kvm_ptp_get_time(struct kvm_vcpu *vcpu, u64 *val)
 static const u32 hvc_func_default_allowed_list[] = {
 	ARM_SMCCC_VERSION_FUNC_ID,
 	ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
-	ARM_SMCCC_HV_PV_TIME_FEATURES,
-	ARM_SMCCC_HV_PV_TIME_ST,
 	ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID,
 	ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID,
 	ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID,
@@ -100,6 +98,10 @@ static bool kvm_hvc_call_allowed(struct kvm_vcpu *vcpu, u32 func_id)
 	case ARM_SMCCC_TRNG_RND64:
 		return kvm_arm_fw_reg_feat_enabled(smccc_feat->std_bmap,
 						KVM_REG_ARM_STD_BIT_TRNG_V1_0);
+	case ARM_SMCCC_HV_PV_TIME_FEATURES:
+	case ARM_SMCCC_HV_PV_TIME_ST:
+		return kvm_arm_fw_reg_feat_enabled(smccc_feat->std_hyp_bmap,
+					KVM_REG_ARM_STD_HYP_BIT_PV_TIME);
 	default:
 		return kvm_hvc_call_default_allowed(vcpu, func_id);
 	}
@@ -107,6 +109,7 @@ static bool kvm_hvc_call_allowed(struct kvm_vcpu *vcpu, u32 func_id)
 
 int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
 {
+	struct kvm_smccc_features *smccc_feat = &vcpu->kvm->arch.smccc_feat;
 	u32 func_id = smccc_get_function(vcpu);
 	u64 val[4] = {SMCCC_RET_NOT_SUPPORTED};
 	u32 feature;
@@ -170,7 +173,9 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
 			}
 			break;
 		case ARM_SMCCC_HV_PV_TIME_FEATURES:
-			val[0] = SMCCC_RET_SUCCESS;
+			if (kvm_arm_fw_reg_feat_enabled(smccc_feat->std_hyp_bmap,
+					KVM_REG_ARM_STD_HYP_BIT_PV_TIME))
+				val[0] = SMCCC_RET_SUCCESS;
 			break;
 		}
 		break;
@@ -216,6 +221,7 @@ static const u64 kvm_arm_fw_reg_ids[] = {
 	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2,
 	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3,
 	KVM_REG_ARM_STD_BMAP,
+	KVM_REG_ARM_STD_HYP_BMAP,
 };
 
 void kvm_arm_init_hypercalls(struct kvm *kvm)
@@ -223,6 +229,7 @@ void kvm_arm_init_hypercalls(struct kvm *kvm)
 	struct kvm_smccc_features *smccc_feat = &kvm->arch.smccc_feat;
 
 	smccc_feat->std_bmap = KVM_ARM_SMCCC_STD_FEATURES;
+	smccc_feat->std_hyp_bmap = KVM_ARM_SMCCC_STD_HYP_FEATURES;
 }
 
 int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
@@ -312,6 +319,9 @@ int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 	case KVM_REG_ARM_STD_BMAP:
 		val = READ_ONCE(smccc_feat->std_bmap);
 		break;
+	case KVM_REG_ARM_STD_HYP_BMAP:
+		val = READ_ONCE(smccc_feat->std_hyp_bmap);
+		break;
 	default:
 		return -ENOENT;
 	}
@@ -334,6 +344,10 @@ static int kvm_arm_set_fw_reg_bmap(struct kvm_vcpu *vcpu, u64 reg_id, u64 val)
 		fw_reg_bmap = &smccc_feat->std_bmap;
 		fw_reg_features = KVM_ARM_SMCCC_STD_FEATURES;
 		break;
+	case KVM_REG_ARM_STD_HYP_BMAP:
+		fw_reg_bmap = &smccc_feat->std_hyp_bmap;
+		fw_reg_features = KVM_ARM_SMCCC_STD_HYP_FEATURES;
+		break;
 	default:
 		return -ENOENT;
 	}
@@ -438,6 +452,7 @@ int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 
 		return 0;
 	case KVM_REG_ARM_STD_BMAP:
+	case KVM_REG_ARM_STD_HYP_BMAP:
 		return kvm_arm_set_fw_reg_bmap(vcpu, reg->id, val);
 	default:
 		return -ENOENT;
diff --git a/include/kvm/arm_hypercalls.h b/include/kvm/arm_hypercalls.h
index fd3ff350ee9d..b0915d8c5b81 100644
--- a/include/kvm/arm_hypercalls.h
+++ b/include/kvm/arm_hypercalls.h
@@ -8,10 +8,14 @@
 
 /* Last valid bits of the bitmapped firmware registers */
 #define KVM_REG_ARM_STD_BMAP_BIT_MAX		0
+#define KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX	0
 
 #define KVM_ARM_SMCCC_STD_FEATURES \
 	GENMASK_ULL(KVM_REG_ARM_STD_BMAP_BIT_MAX, 0)
 
+#define KVM_ARM_SMCCC_STD_HYP_FEATURES \
+	GENMASK_ULL(KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX, 0)
+
 int kvm_hvc_call_handler(struct kvm_vcpu *vcpu);
 
 static inline u32 smccc_get_function(struct kvm_vcpu *vcpu)
-- 
2.35.1.1094.g7c7d902a7c-goog

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

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

* [PATCH v5 03/10] KVM: arm64: Add standard hypervisor firmware register
@ 2022-04-07  1:15   ` Raghavendra Rao Ananta
  0 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-07  1:15 UTC (permalink / raw)
  To: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose
  Cc: Paolo Bonzini, Catalin Marinas, Will Deacon, Peter Shier,
	Ricardo Koller, Oliver Upton, Reiji Watanabe, Jing Zhang,
	Raghavendra Rao Anata, linux-arm-kernel, kvmarm, linux-kernel,
	kvm

Introduce the firmware register to hold the standard hypervisor
service calls (owner value 5) as a bitmap. The bitmap represents
the features that'll be enabled for the guest, as configured by
the user-space. Currently, this includes support only for
Paravirtualized time, represented by bit-0.

Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
---
 arch/arm64/include/asm/kvm_host.h |  2 ++
 arch/arm64/include/uapi/asm/kvm.h |  3 +++
 arch/arm64/kvm/hypercalls.c       | 21 ++++++++++++++++++---
 include/kvm/arm_hypercalls.h      |  4 ++++
 4 files changed, 27 insertions(+), 3 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 6e663383d7b4..20165242ebd9 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -105,9 +105,11 @@ struct kvm_arch_memory_slot {
  * struct kvm_smccc_features: Descriptor the hypercall services exposed to the guests
  *
  * @std_bmap: Bitmap of standard secure service calls
+ * @std_hyp_bmap: Bitmap of standard hypervisor service calls
  */
 struct kvm_smccc_features {
 	u64 std_bmap;
+	u64 std_hyp_bmap;
 };
 
 struct kvm_arch {
diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h
index 56e4bc58a355..67353bf4e69d 100644
--- a/arch/arm64/include/uapi/asm/kvm.h
+++ b/arch/arm64/include/uapi/asm/kvm.h
@@ -341,6 +341,9 @@ struct kvm_arm_copy_mte_tags {
 #define KVM_REG_ARM_STD_BMAP			KVM_REG_ARM_FW_FEAT_BMAP_REG(0)
 #define KVM_REG_ARM_STD_BIT_TRNG_V1_0		BIT(0)
 
+#define KVM_REG_ARM_STD_HYP_BMAP		KVM_REG_ARM_FW_FEAT_BMAP_REG(1)
+#define KVM_REG_ARM_STD_HYP_BIT_PV_TIME		BIT(0)
+
 /* Device Control API: ARM VGIC */
 #define KVM_DEV_ARM_VGIC_GRP_ADDR	0
 #define KVM_DEV_ARM_VGIC_GRP_DIST_REGS	1
diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c
index cf04b5ee5f56..64ae6c7e7145 100644
--- a/arch/arm64/kvm/hypercalls.c
+++ b/arch/arm64/kvm/hypercalls.c
@@ -65,8 +65,6 @@ static void kvm_ptp_get_time(struct kvm_vcpu *vcpu, u64 *val)
 static const u32 hvc_func_default_allowed_list[] = {
 	ARM_SMCCC_VERSION_FUNC_ID,
 	ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
-	ARM_SMCCC_HV_PV_TIME_FEATURES,
-	ARM_SMCCC_HV_PV_TIME_ST,
 	ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID,
 	ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID,
 	ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID,
@@ -100,6 +98,10 @@ static bool kvm_hvc_call_allowed(struct kvm_vcpu *vcpu, u32 func_id)
 	case ARM_SMCCC_TRNG_RND64:
 		return kvm_arm_fw_reg_feat_enabled(smccc_feat->std_bmap,
 						KVM_REG_ARM_STD_BIT_TRNG_V1_0);
+	case ARM_SMCCC_HV_PV_TIME_FEATURES:
+	case ARM_SMCCC_HV_PV_TIME_ST:
+		return kvm_arm_fw_reg_feat_enabled(smccc_feat->std_hyp_bmap,
+					KVM_REG_ARM_STD_HYP_BIT_PV_TIME);
 	default:
 		return kvm_hvc_call_default_allowed(vcpu, func_id);
 	}
@@ -107,6 +109,7 @@ static bool kvm_hvc_call_allowed(struct kvm_vcpu *vcpu, u32 func_id)
 
 int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
 {
+	struct kvm_smccc_features *smccc_feat = &vcpu->kvm->arch.smccc_feat;
 	u32 func_id = smccc_get_function(vcpu);
 	u64 val[4] = {SMCCC_RET_NOT_SUPPORTED};
 	u32 feature;
@@ -170,7 +173,9 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
 			}
 			break;
 		case ARM_SMCCC_HV_PV_TIME_FEATURES:
-			val[0] = SMCCC_RET_SUCCESS;
+			if (kvm_arm_fw_reg_feat_enabled(smccc_feat->std_hyp_bmap,
+					KVM_REG_ARM_STD_HYP_BIT_PV_TIME))
+				val[0] = SMCCC_RET_SUCCESS;
 			break;
 		}
 		break;
@@ -216,6 +221,7 @@ static const u64 kvm_arm_fw_reg_ids[] = {
 	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2,
 	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3,
 	KVM_REG_ARM_STD_BMAP,
+	KVM_REG_ARM_STD_HYP_BMAP,
 };
 
 void kvm_arm_init_hypercalls(struct kvm *kvm)
@@ -223,6 +229,7 @@ void kvm_arm_init_hypercalls(struct kvm *kvm)
 	struct kvm_smccc_features *smccc_feat = &kvm->arch.smccc_feat;
 
 	smccc_feat->std_bmap = KVM_ARM_SMCCC_STD_FEATURES;
+	smccc_feat->std_hyp_bmap = KVM_ARM_SMCCC_STD_HYP_FEATURES;
 }
 
 int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
@@ -312,6 +319,9 @@ int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 	case KVM_REG_ARM_STD_BMAP:
 		val = READ_ONCE(smccc_feat->std_bmap);
 		break;
+	case KVM_REG_ARM_STD_HYP_BMAP:
+		val = READ_ONCE(smccc_feat->std_hyp_bmap);
+		break;
 	default:
 		return -ENOENT;
 	}
@@ -334,6 +344,10 @@ static int kvm_arm_set_fw_reg_bmap(struct kvm_vcpu *vcpu, u64 reg_id, u64 val)
 		fw_reg_bmap = &smccc_feat->std_bmap;
 		fw_reg_features = KVM_ARM_SMCCC_STD_FEATURES;
 		break;
+	case KVM_REG_ARM_STD_HYP_BMAP:
+		fw_reg_bmap = &smccc_feat->std_hyp_bmap;
+		fw_reg_features = KVM_ARM_SMCCC_STD_HYP_FEATURES;
+		break;
 	default:
 		return -ENOENT;
 	}
@@ -438,6 +452,7 @@ int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 
 		return 0;
 	case KVM_REG_ARM_STD_BMAP:
+	case KVM_REG_ARM_STD_HYP_BMAP:
 		return kvm_arm_set_fw_reg_bmap(vcpu, reg->id, val);
 	default:
 		return -ENOENT;
diff --git a/include/kvm/arm_hypercalls.h b/include/kvm/arm_hypercalls.h
index fd3ff350ee9d..b0915d8c5b81 100644
--- a/include/kvm/arm_hypercalls.h
+++ b/include/kvm/arm_hypercalls.h
@@ -8,10 +8,14 @@
 
 /* Last valid bits of the bitmapped firmware registers */
 #define KVM_REG_ARM_STD_BMAP_BIT_MAX		0
+#define KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX	0
 
 #define KVM_ARM_SMCCC_STD_FEATURES \
 	GENMASK_ULL(KVM_REG_ARM_STD_BMAP_BIT_MAX, 0)
 
+#define KVM_ARM_SMCCC_STD_HYP_FEATURES \
+	GENMASK_ULL(KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX, 0)
+
 int kvm_hvc_call_handler(struct kvm_vcpu *vcpu);
 
 static inline u32 smccc_get_function(struct kvm_vcpu *vcpu)
-- 
2.35.1.1094.g7c7d902a7c-goog


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

* [PATCH v5 03/10] KVM: arm64: Add standard hypervisor firmware register
@ 2022-04-07  1:15   ` Raghavendra Rao Ananta
  0 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-07  1:15 UTC (permalink / raw)
  To: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose
  Cc: Paolo Bonzini, Catalin Marinas, Will Deacon, Peter Shier,
	Ricardo Koller, Oliver Upton, Reiji Watanabe, Jing Zhang,
	Raghavendra Rao Anata, linux-arm-kernel, kvmarm, linux-kernel,
	kvm

Introduce the firmware register to hold the standard hypervisor
service calls (owner value 5) as a bitmap. The bitmap represents
the features that'll be enabled for the guest, as configured by
the user-space. Currently, this includes support only for
Paravirtualized time, represented by bit-0.

Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
---
 arch/arm64/include/asm/kvm_host.h |  2 ++
 arch/arm64/include/uapi/asm/kvm.h |  3 +++
 arch/arm64/kvm/hypercalls.c       | 21 ++++++++++++++++++---
 include/kvm/arm_hypercalls.h      |  4 ++++
 4 files changed, 27 insertions(+), 3 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 6e663383d7b4..20165242ebd9 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -105,9 +105,11 @@ struct kvm_arch_memory_slot {
  * struct kvm_smccc_features: Descriptor the hypercall services exposed to the guests
  *
  * @std_bmap: Bitmap of standard secure service calls
+ * @std_hyp_bmap: Bitmap of standard hypervisor service calls
  */
 struct kvm_smccc_features {
 	u64 std_bmap;
+	u64 std_hyp_bmap;
 };
 
 struct kvm_arch {
diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h
index 56e4bc58a355..67353bf4e69d 100644
--- a/arch/arm64/include/uapi/asm/kvm.h
+++ b/arch/arm64/include/uapi/asm/kvm.h
@@ -341,6 +341,9 @@ struct kvm_arm_copy_mte_tags {
 #define KVM_REG_ARM_STD_BMAP			KVM_REG_ARM_FW_FEAT_BMAP_REG(0)
 #define KVM_REG_ARM_STD_BIT_TRNG_V1_0		BIT(0)
 
+#define KVM_REG_ARM_STD_HYP_BMAP		KVM_REG_ARM_FW_FEAT_BMAP_REG(1)
+#define KVM_REG_ARM_STD_HYP_BIT_PV_TIME		BIT(0)
+
 /* Device Control API: ARM VGIC */
 #define KVM_DEV_ARM_VGIC_GRP_ADDR	0
 #define KVM_DEV_ARM_VGIC_GRP_DIST_REGS	1
diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c
index cf04b5ee5f56..64ae6c7e7145 100644
--- a/arch/arm64/kvm/hypercalls.c
+++ b/arch/arm64/kvm/hypercalls.c
@@ -65,8 +65,6 @@ static void kvm_ptp_get_time(struct kvm_vcpu *vcpu, u64 *val)
 static const u32 hvc_func_default_allowed_list[] = {
 	ARM_SMCCC_VERSION_FUNC_ID,
 	ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
-	ARM_SMCCC_HV_PV_TIME_FEATURES,
-	ARM_SMCCC_HV_PV_TIME_ST,
 	ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID,
 	ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID,
 	ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID,
@@ -100,6 +98,10 @@ static bool kvm_hvc_call_allowed(struct kvm_vcpu *vcpu, u32 func_id)
 	case ARM_SMCCC_TRNG_RND64:
 		return kvm_arm_fw_reg_feat_enabled(smccc_feat->std_bmap,
 						KVM_REG_ARM_STD_BIT_TRNG_V1_0);
+	case ARM_SMCCC_HV_PV_TIME_FEATURES:
+	case ARM_SMCCC_HV_PV_TIME_ST:
+		return kvm_arm_fw_reg_feat_enabled(smccc_feat->std_hyp_bmap,
+					KVM_REG_ARM_STD_HYP_BIT_PV_TIME);
 	default:
 		return kvm_hvc_call_default_allowed(vcpu, func_id);
 	}
@@ -107,6 +109,7 @@ static bool kvm_hvc_call_allowed(struct kvm_vcpu *vcpu, u32 func_id)
 
 int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
 {
+	struct kvm_smccc_features *smccc_feat = &vcpu->kvm->arch.smccc_feat;
 	u32 func_id = smccc_get_function(vcpu);
 	u64 val[4] = {SMCCC_RET_NOT_SUPPORTED};
 	u32 feature;
@@ -170,7 +173,9 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
 			}
 			break;
 		case ARM_SMCCC_HV_PV_TIME_FEATURES:
-			val[0] = SMCCC_RET_SUCCESS;
+			if (kvm_arm_fw_reg_feat_enabled(smccc_feat->std_hyp_bmap,
+					KVM_REG_ARM_STD_HYP_BIT_PV_TIME))
+				val[0] = SMCCC_RET_SUCCESS;
 			break;
 		}
 		break;
@@ -216,6 +221,7 @@ static const u64 kvm_arm_fw_reg_ids[] = {
 	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2,
 	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3,
 	KVM_REG_ARM_STD_BMAP,
+	KVM_REG_ARM_STD_HYP_BMAP,
 };
 
 void kvm_arm_init_hypercalls(struct kvm *kvm)
@@ -223,6 +229,7 @@ void kvm_arm_init_hypercalls(struct kvm *kvm)
 	struct kvm_smccc_features *smccc_feat = &kvm->arch.smccc_feat;
 
 	smccc_feat->std_bmap = KVM_ARM_SMCCC_STD_FEATURES;
+	smccc_feat->std_hyp_bmap = KVM_ARM_SMCCC_STD_HYP_FEATURES;
 }
 
 int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
@@ -312,6 +319,9 @@ int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 	case KVM_REG_ARM_STD_BMAP:
 		val = READ_ONCE(smccc_feat->std_bmap);
 		break;
+	case KVM_REG_ARM_STD_HYP_BMAP:
+		val = READ_ONCE(smccc_feat->std_hyp_bmap);
+		break;
 	default:
 		return -ENOENT;
 	}
@@ -334,6 +344,10 @@ static int kvm_arm_set_fw_reg_bmap(struct kvm_vcpu *vcpu, u64 reg_id, u64 val)
 		fw_reg_bmap = &smccc_feat->std_bmap;
 		fw_reg_features = KVM_ARM_SMCCC_STD_FEATURES;
 		break;
+	case KVM_REG_ARM_STD_HYP_BMAP:
+		fw_reg_bmap = &smccc_feat->std_hyp_bmap;
+		fw_reg_features = KVM_ARM_SMCCC_STD_HYP_FEATURES;
+		break;
 	default:
 		return -ENOENT;
 	}
@@ -438,6 +452,7 @@ int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 
 		return 0;
 	case KVM_REG_ARM_STD_BMAP:
+	case KVM_REG_ARM_STD_HYP_BMAP:
 		return kvm_arm_set_fw_reg_bmap(vcpu, reg->id, val);
 	default:
 		return -ENOENT;
diff --git a/include/kvm/arm_hypercalls.h b/include/kvm/arm_hypercalls.h
index fd3ff350ee9d..b0915d8c5b81 100644
--- a/include/kvm/arm_hypercalls.h
+++ b/include/kvm/arm_hypercalls.h
@@ -8,10 +8,14 @@
 
 /* Last valid bits of the bitmapped firmware registers */
 #define KVM_REG_ARM_STD_BMAP_BIT_MAX		0
+#define KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX	0
 
 #define KVM_ARM_SMCCC_STD_FEATURES \
 	GENMASK_ULL(KVM_REG_ARM_STD_BMAP_BIT_MAX, 0)
 
+#define KVM_ARM_SMCCC_STD_HYP_FEATURES \
+	GENMASK_ULL(KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX, 0)
+
 int kvm_hvc_call_handler(struct kvm_vcpu *vcpu);
 
 static inline u32 smccc_get_function(struct kvm_vcpu *vcpu)
-- 
2.35.1.1094.g7c7d902a7c-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] 96+ messages in thread

* [PATCH v5 04/10] KVM: arm64: Add vendor hypervisor firmware register
  2022-04-07  1:15 ` Raghavendra Rao Ananta
  (?)
@ 2022-04-07  1:15   ` Raghavendra Rao Ananta
  -1 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-07  1:15 UTC (permalink / raw)
  To: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose
  Cc: kvm, Catalin Marinas, Peter Shier, linux-kernel, Paolo Bonzini,
	Will Deacon, kvmarm, linux-arm-kernel

Introduce the firmware register to hold the vendor specific
hypervisor service calls (owner value 6) as a bitmap. The
bitmap represents the features that'll be enabled for the
guest, as configured by the user-space. Currently, this
includes support for KVM-vendor features, and Precision Time
Protocol (PTP), represented by bit-0 and bit-1 respectively.

Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
---
 arch/arm64/include/asm/kvm_host.h |  2 ++
 arch/arm64/include/uapi/asm/kvm.h |  4 ++++
 arch/arm64/kvm/hypercalls.c       | 21 +++++++++++++++++----
 include/kvm/arm_hypercalls.h      |  4 ++++
 4 files changed, 27 insertions(+), 4 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 20165242ebd9..b79161bad69a 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -106,10 +106,12 @@ struct kvm_arch_memory_slot {
  *
  * @std_bmap: Bitmap of standard secure service calls
  * @std_hyp_bmap: Bitmap of standard hypervisor service calls
+ * @vendor_hyp_bmap: Bitmap of vendor specific hypervisor service calls
  */
 struct kvm_smccc_features {
 	u64 std_bmap;
 	u64 std_hyp_bmap;
+	u64 vendor_hyp_bmap;
 };
 
 struct kvm_arch {
diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h
index 67353bf4e69d..9a5ac0ed4113 100644
--- a/arch/arm64/include/uapi/asm/kvm.h
+++ b/arch/arm64/include/uapi/asm/kvm.h
@@ -344,6 +344,10 @@ struct kvm_arm_copy_mte_tags {
 #define KVM_REG_ARM_STD_HYP_BMAP		KVM_REG_ARM_FW_FEAT_BMAP_REG(1)
 #define KVM_REG_ARM_STD_HYP_BIT_PV_TIME		BIT(0)
 
+#define KVM_REG_ARM_VENDOR_HYP_BMAP		KVM_REG_ARM_FW_FEAT_BMAP_REG(2)
+#define KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT	BIT(0)
+#define KVM_REG_ARM_VENDOR_HYP_BIT_PTP		BIT(1)
+
 /* Device Control API: ARM VGIC */
 #define KVM_DEV_ARM_VGIC_GRP_ADDR	0
 #define KVM_DEV_ARM_VGIC_GRP_DIST_REGS	1
diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c
index 64ae6c7e7145..80836c341fd3 100644
--- a/arch/arm64/kvm/hypercalls.c
+++ b/arch/arm64/kvm/hypercalls.c
@@ -66,8 +66,6 @@ static const u32 hvc_func_default_allowed_list[] = {
 	ARM_SMCCC_VERSION_FUNC_ID,
 	ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
 	ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID,
-	ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID,
-	ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID,
 };
 
 static bool kvm_hvc_call_default_allowed(struct kvm_vcpu *vcpu, u32 func_id)
@@ -102,6 +100,12 @@ static bool kvm_hvc_call_allowed(struct kvm_vcpu *vcpu, u32 func_id)
 	case ARM_SMCCC_HV_PV_TIME_ST:
 		return kvm_arm_fw_reg_feat_enabled(smccc_feat->std_hyp_bmap,
 					KVM_REG_ARM_STD_HYP_BIT_PV_TIME);
+	case ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID:
+		return kvm_arm_fw_reg_feat_enabled(smccc_feat->vendor_hyp_bmap,
+					KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT);
+	case ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID:
+		return kvm_arm_fw_reg_feat_enabled(smccc_feat->vendor_hyp_bmap,
+					KVM_REG_ARM_VENDOR_HYP_BIT_PTP);
 	default:
 		return kvm_hvc_call_default_allowed(vcpu, func_id);
 	}
@@ -194,8 +198,7 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
 		val[3] = ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_3;
 		break;
 	case ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID:
-		val[0] = BIT(ARM_SMCCC_KVM_FUNC_FEATURES);
-		val[0] |= BIT(ARM_SMCCC_KVM_FUNC_PTP);
+		val[0] = smccc_feat->vendor_hyp_bmap;
 		break;
 	case ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID:
 		kvm_ptp_get_time(vcpu, val);
@@ -222,6 +225,7 @@ static const u64 kvm_arm_fw_reg_ids[] = {
 	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3,
 	KVM_REG_ARM_STD_BMAP,
 	KVM_REG_ARM_STD_HYP_BMAP,
+	KVM_REG_ARM_VENDOR_HYP_BMAP,
 };
 
 void kvm_arm_init_hypercalls(struct kvm *kvm)
@@ -230,6 +234,7 @@ void kvm_arm_init_hypercalls(struct kvm *kvm)
 
 	smccc_feat->std_bmap = KVM_ARM_SMCCC_STD_FEATURES;
 	smccc_feat->std_hyp_bmap = KVM_ARM_SMCCC_STD_HYP_FEATURES;
+	smccc_feat->vendor_hyp_bmap = KVM_ARM_SMCCC_VENDOR_HYP_FEATURES;
 }
 
 int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
@@ -322,6 +327,9 @@ int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 	case KVM_REG_ARM_STD_HYP_BMAP:
 		val = READ_ONCE(smccc_feat->std_hyp_bmap);
 		break;
+	case KVM_REG_ARM_VENDOR_HYP_BMAP:
+		val = READ_ONCE(smccc_feat->vendor_hyp_bmap);
+		break;
 	default:
 		return -ENOENT;
 	}
@@ -348,6 +356,10 @@ static int kvm_arm_set_fw_reg_bmap(struct kvm_vcpu *vcpu, u64 reg_id, u64 val)
 		fw_reg_bmap = &smccc_feat->std_hyp_bmap;
 		fw_reg_features = KVM_ARM_SMCCC_STD_HYP_FEATURES;
 		break;
+	case KVM_REG_ARM_VENDOR_HYP_BMAP:
+		fw_reg_bmap = &smccc_feat->vendor_hyp_bmap;
+		fw_reg_features = KVM_ARM_SMCCC_VENDOR_HYP_FEATURES;
+		break;
 	default:
 		return -ENOENT;
 	}
@@ -453,6 +465,7 @@ int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 		return 0;
 	case KVM_REG_ARM_STD_BMAP:
 	case KVM_REG_ARM_STD_HYP_BMAP:
+	case KVM_REG_ARM_VENDOR_HYP_BMAP:
 		return kvm_arm_set_fw_reg_bmap(vcpu, reg->id, val);
 	default:
 		return -ENOENT;
diff --git a/include/kvm/arm_hypercalls.h b/include/kvm/arm_hypercalls.h
index b0915d8c5b81..eaf4f6b318a8 100644
--- a/include/kvm/arm_hypercalls.h
+++ b/include/kvm/arm_hypercalls.h
@@ -9,6 +9,7 @@
 /* Last valid bits of the bitmapped firmware registers */
 #define KVM_REG_ARM_STD_BMAP_BIT_MAX		0
 #define KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX	0
+#define KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX	1
 
 #define KVM_ARM_SMCCC_STD_FEATURES \
 	GENMASK_ULL(KVM_REG_ARM_STD_BMAP_BIT_MAX, 0)
@@ -16,6 +17,9 @@
 #define KVM_ARM_SMCCC_STD_HYP_FEATURES \
 	GENMASK_ULL(KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX, 0)
 
+#define KVM_ARM_SMCCC_VENDOR_HYP_FEATURES \
+	GENMASK_ULL(KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX, 0)
+
 int kvm_hvc_call_handler(struct kvm_vcpu *vcpu);
 
 static inline u32 smccc_get_function(struct kvm_vcpu *vcpu)
-- 
2.35.1.1094.g7c7d902a7c-goog

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

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

* [PATCH v5 04/10] KVM: arm64: Add vendor hypervisor firmware register
@ 2022-04-07  1:15   ` Raghavendra Rao Ananta
  0 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-07  1:15 UTC (permalink / raw)
  To: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose
  Cc: Paolo Bonzini, Catalin Marinas, Will Deacon, Peter Shier,
	Ricardo Koller, Oliver Upton, Reiji Watanabe, Jing Zhang,
	Raghavendra Rao Anata, linux-arm-kernel, kvmarm, linux-kernel,
	kvm

Introduce the firmware register to hold the vendor specific
hypervisor service calls (owner value 6) as a bitmap. The
bitmap represents the features that'll be enabled for the
guest, as configured by the user-space. Currently, this
includes support for KVM-vendor features, and Precision Time
Protocol (PTP), represented by bit-0 and bit-1 respectively.

Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
---
 arch/arm64/include/asm/kvm_host.h |  2 ++
 arch/arm64/include/uapi/asm/kvm.h |  4 ++++
 arch/arm64/kvm/hypercalls.c       | 21 +++++++++++++++++----
 include/kvm/arm_hypercalls.h      |  4 ++++
 4 files changed, 27 insertions(+), 4 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 20165242ebd9..b79161bad69a 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -106,10 +106,12 @@ struct kvm_arch_memory_slot {
  *
  * @std_bmap: Bitmap of standard secure service calls
  * @std_hyp_bmap: Bitmap of standard hypervisor service calls
+ * @vendor_hyp_bmap: Bitmap of vendor specific hypervisor service calls
  */
 struct kvm_smccc_features {
 	u64 std_bmap;
 	u64 std_hyp_bmap;
+	u64 vendor_hyp_bmap;
 };
 
 struct kvm_arch {
diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h
index 67353bf4e69d..9a5ac0ed4113 100644
--- a/arch/arm64/include/uapi/asm/kvm.h
+++ b/arch/arm64/include/uapi/asm/kvm.h
@@ -344,6 +344,10 @@ struct kvm_arm_copy_mte_tags {
 #define KVM_REG_ARM_STD_HYP_BMAP		KVM_REG_ARM_FW_FEAT_BMAP_REG(1)
 #define KVM_REG_ARM_STD_HYP_BIT_PV_TIME		BIT(0)
 
+#define KVM_REG_ARM_VENDOR_HYP_BMAP		KVM_REG_ARM_FW_FEAT_BMAP_REG(2)
+#define KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT	BIT(0)
+#define KVM_REG_ARM_VENDOR_HYP_BIT_PTP		BIT(1)
+
 /* Device Control API: ARM VGIC */
 #define KVM_DEV_ARM_VGIC_GRP_ADDR	0
 #define KVM_DEV_ARM_VGIC_GRP_DIST_REGS	1
diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c
index 64ae6c7e7145..80836c341fd3 100644
--- a/arch/arm64/kvm/hypercalls.c
+++ b/arch/arm64/kvm/hypercalls.c
@@ -66,8 +66,6 @@ static const u32 hvc_func_default_allowed_list[] = {
 	ARM_SMCCC_VERSION_FUNC_ID,
 	ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
 	ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID,
-	ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID,
-	ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID,
 };
 
 static bool kvm_hvc_call_default_allowed(struct kvm_vcpu *vcpu, u32 func_id)
@@ -102,6 +100,12 @@ static bool kvm_hvc_call_allowed(struct kvm_vcpu *vcpu, u32 func_id)
 	case ARM_SMCCC_HV_PV_TIME_ST:
 		return kvm_arm_fw_reg_feat_enabled(smccc_feat->std_hyp_bmap,
 					KVM_REG_ARM_STD_HYP_BIT_PV_TIME);
+	case ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID:
+		return kvm_arm_fw_reg_feat_enabled(smccc_feat->vendor_hyp_bmap,
+					KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT);
+	case ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID:
+		return kvm_arm_fw_reg_feat_enabled(smccc_feat->vendor_hyp_bmap,
+					KVM_REG_ARM_VENDOR_HYP_BIT_PTP);
 	default:
 		return kvm_hvc_call_default_allowed(vcpu, func_id);
 	}
@@ -194,8 +198,7 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
 		val[3] = ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_3;
 		break;
 	case ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID:
-		val[0] = BIT(ARM_SMCCC_KVM_FUNC_FEATURES);
-		val[0] |= BIT(ARM_SMCCC_KVM_FUNC_PTP);
+		val[0] = smccc_feat->vendor_hyp_bmap;
 		break;
 	case ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID:
 		kvm_ptp_get_time(vcpu, val);
@@ -222,6 +225,7 @@ static const u64 kvm_arm_fw_reg_ids[] = {
 	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3,
 	KVM_REG_ARM_STD_BMAP,
 	KVM_REG_ARM_STD_HYP_BMAP,
+	KVM_REG_ARM_VENDOR_HYP_BMAP,
 };
 
 void kvm_arm_init_hypercalls(struct kvm *kvm)
@@ -230,6 +234,7 @@ void kvm_arm_init_hypercalls(struct kvm *kvm)
 
 	smccc_feat->std_bmap = KVM_ARM_SMCCC_STD_FEATURES;
 	smccc_feat->std_hyp_bmap = KVM_ARM_SMCCC_STD_HYP_FEATURES;
+	smccc_feat->vendor_hyp_bmap = KVM_ARM_SMCCC_VENDOR_HYP_FEATURES;
 }
 
 int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
@@ -322,6 +327,9 @@ int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 	case KVM_REG_ARM_STD_HYP_BMAP:
 		val = READ_ONCE(smccc_feat->std_hyp_bmap);
 		break;
+	case KVM_REG_ARM_VENDOR_HYP_BMAP:
+		val = READ_ONCE(smccc_feat->vendor_hyp_bmap);
+		break;
 	default:
 		return -ENOENT;
 	}
@@ -348,6 +356,10 @@ static int kvm_arm_set_fw_reg_bmap(struct kvm_vcpu *vcpu, u64 reg_id, u64 val)
 		fw_reg_bmap = &smccc_feat->std_hyp_bmap;
 		fw_reg_features = KVM_ARM_SMCCC_STD_HYP_FEATURES;
 		break;
+	case KVM_REG_ARM_VENDOR_HYP_BMAP:
+		fw_reg_bmap = &smccc_feat->vendor_hyp_bmap;
+		fw_reg_features = KVM_ARM_SMCCC_VENDOR_HYP_FEATURES;
+		break;
 	default:
 		return -ENOENT;
 	}
@@ -453,6 +465,7 @@ int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 		return 0;
 	case KVM_REG_ARM_STD_BMAP:
 	case KVM_REG_ARM_STD_HYP_BMAP:
+	case KVM_REG_ARM_VENDOR_HYP_BMAP:
 		return kvm_arm_set_fw_reg_bmap(vcpu, reg->id, val);
 	default:
 		return -ENOENT;
diff --git a/include/kvm/arm_hypercalls.h b/include/kvm/arm_hypercalls.h
index b0915d8c5b81..eaf4f6b318a8 100644
--- a/include/kvm/arm_hypercalls.h
+++ b/include/kvm/arm_hypercalls.h
@@ -9,6 +9,7 @@
 /* Last valid bits of the bitmapped firmware registers */
 #define KVM_REG_ARM_STD_BMAP_BIT_MAX		0
 #define KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX	0
+#define KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX	1
 
 #define KVM_ARM_SMCCC_STD_FEATURES \
 	GENMASK_ULL(KVM_REG_ARM_STD_BMAP_BIT_MAX, 0)
@@ -16,6 +17,9 @@
 #define KVM_ARM_SMCCC_STD_HYP_FEATURES \
 	GENMASK_ULL(KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX, 0)
 
+#define KVM_ARM_SMCCC_VENDOR_HYP_FEATURES \
+	GENMASK_ULL(KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX, 0)
+
 int kvm_hvc_call_handler(struct kvm_vcpu *vcpu);
 
 static inline u32 smccc_get_function(struct kvm_vcpu *vcpu)
-- 
2.35.1.1094.g7c7d902a7c-goog


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

* [PATCH v5 04/10] KVM: arm64: Add vendor hypervisor firmware register
@ 2022-04-07  1:15   ` Raghavendra Rao Ananta
  0 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-07  1:15 UTC (permalink / raw)
  To: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose
  Cc: Paolo Bonzini, Catalin Marinas, Will Deacon, Peter Shier,
	Ricardo Koller, Oliver Upton, Reiji Watanabe, Jing Zhang,
	Raghavendra Rao Anata, linux-arm-kernel, kvmarm, linux-kernel,
	kvm

Introduce the firmware register to hold the vendor specific
hypervisor service calls (owner value 6) as a bitmap. The
bitmap represents the features that'll be enabled for the
guest, as configured by the user-space. Currently, this
includes support for KVM-vendor features, and Precision Time
Protocol (PTP), represented by bit-0 and bit-1 respectively.

Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
---
 arch/arm64/include/asm/kvm_host.h |  2 ++
 arch/arm64/include/uapi/asm/kvm.h |  4 ++++
 arch/arm64/kvm/hypercalls.c       | 21 +++++++++++++++++----
 include/kvm/arm_hypercalls.h      |  4 ++++
 4 files changed, 27 insertions(+), 4 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 20165242ebd9..b79161bad69a 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -106,10 +106,12 @@ struct kvm_arch_memory_slot {
  *
  * @std_bmap: Bitmap of standard secure service calls
  * @std_hyp_bmap: Bitmap of standard hypervisor service calls
+ * @vendor_hyp_bmap: Bitmap of vendor specific hypervisor service calls
  */
 struct kvm_smccc_features {
 	u64 std_bmap;
 	u64 std_hyp_bmap;
+	u64 vendor_hyp_bmap;
 };
 
 struct kvm_arch {
diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h
index 67353bf4e69d..9a5ac0ed4113 100644
--- a/arch/arm64/include/uapi/asm/kvm.h
+++ b/arch/arm64/include/uapi/asm/kvm.h
@@ -344,6 +344,10 @@ struct kvm_arm_copy_mte_tags {
 #define KVM_REG_ARM_STD_HYP_BMAP		KVM_REG_ARM_FW_FEAT_BMAP_REG(1)
 #define KVM_REG_ARM_STD_HYP_BIT_PV_TIME		BIT(0)
 
+#define KVM_REG_ARM_VENDOR_HYP_BMAP		KVM_REG_ARM_FW_FEAT_BMAP_REG(2)
+#define KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT	BIT(0)
+#define KVM_REG_ARM_VENDOR_HYP_BIT_PTP		BIT(1)
+
 /* Device Control API: ARM VGIC */
 #define KVM_DEV_ARM_VGIC_GRP_ADDR	0
 #define KVM_DEV_ARM_VGIC_GRP_DIST_REGS	1
diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c
index 64ae6c7e7145..80836c341fd3 100644
--- a/arch/arm64/kvm/hypercalls.c
+++ b/arch/arm64/kvm/hypercalls.c
@@ -66,8 +66,6 @@ static const u32 hvc_func_default_allowed_list[] = {
 	ARM_SMCCC_VERSION_FUNC_ID,
 	ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
 	ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID,
-	ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID,
-	ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID,
 };
 
 static bool kvm_hvc_call_default_allowed(struct kvm_vcpu *vcpu, u32 func_id)
@@ -102,6 +100,12 @@ static bool kvm_hvc_call_allowed(struct kvm_vcpu *vcpu, u32 func_id)
 	case ARM_SMCCC_HV_PV_TIME_ST:
 		return kvm_arm_fw_reg_feat_enabled(smccc_feat->std_hyp_bmap,
 					KVM_REG_ARM_STD_HYP_BIT_PV_TIME);
+	case ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID:
+		return kvm_arm_fw_reg_feat_enabled(smccc_feat->vendor_hyp_bmap,
+					KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT);
+	case ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID:
+		return kvm_arm_fw_reg_feat_enabled(smccc_feat->vendor_hyp_bmap,
+					KVM_REG_ARM_VENDOR_HYP_BIT_PTP);
 	default:
 		return kvm_hvc_call_default_allowed(vcpu, func_id);
 	}
@@ -194,8 +198,7 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
 		val[3] = ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_3;
 		break;
 	case ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID:
-		val[0] = BIT(ARM_SMCCC_KVM_FUNC_FEATURES);
-		val[0] |= BIT(ARM_SMCCC_KVM_FUNC_PTP);
+		val[0] = smccc_feat->vendor_hyp_bmap;
 		break;
 	case ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID:
 		kvm_ptp_get_time(vcpu, val);
@@ -222,6 +225,7 @@ static const u64 kvm_arm_fw_reg_ids[] = {
 	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3,
 	KVM_REG_ARM_STD_BMAP,
 	KVM_REG_ARM_STD_HYP_BMAP,
+	KVM_REG_ARM_VENDOR_HYP_BMAP,
 };
 
 void kvm_arm_init_hypercalls(struct kvm *kvm)
@@ -230,6 +234,7 @@ void kvm_arm_init_hypercalls(struct kvm *kvm)
 
 	smccc_feat->std_bmap = KVM_ARM_SMCCC_STD_FEATURES;
 	smccc_feat->std_hyp_bmap = KVM_ARM_SMCCC_STD_HYP_FEATURES;
+	smccc_feat->vendor_hyp_bmap = KVM_ARM_SMCCC_VENDOR_HYP_FEATURES;
 }
 
 int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
@@ -322,6 +327,9 @@ int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 	case KVM_REG_ARM_STD_HYP_BMAP:
 		val = READ_ONCE(smccc_feat->std_hyp_bmap);
 		break;
+	case KVM_REG_ARM_VENDOR_HYP_BMAP:
+		val = READ_ONCE(smccc_feat->vendor_hyp_bmap);
+		break;
 	default:
 		return -ENOENT;
 	}
@@ -348,6 +356,10 @@ static int kvm_arm_set_fw_reg_bmap(struct kvm_vcpu *vcpu, u64 reg_id, u64 val)
 		fw_reg_bmap = &smccc_feat->std_hyp_bmap;
 		fw_reg_features = KVM_ARM_SMCCC_STD_HYP_FEATURES;
 		break;
+	case KVM_REG_ARM_VENDOR_HYP_BMAP:
+		fw_reg_bmap = &smccc_feat->vendor_hyp_bmap;
+		fw_reg_features = KVM_ARM_SMCCC_VENDOR_HYP_FEATURES;
+		break;
 	default:
 		return -ENOENT;
 	}
@@ -453,6 +465,7 @@ int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 		return 0;
 	case KVM_REG_ARM_STD_BMAP:
 	case KVM_REG_ARM_STD_HYP_BMAP:
+	case KVM_REG_ARM_VENDOR_HYP_BMAP:
 		return kvm_arm_set_fw_reg_bmap(vcpu, reg->id, val);
 	default:
 		return -ENOENT;
diff --git a/include/kvm/arm_hypercalls.h b/include/kvm/arm_hypercalls.h
index b0915d8c5b81..eaf4f6b318a8 100644
--- a/include/kvm/arm_hypercalls.h
+++ b/include/kvm/arm_hypercalls.h
@@ -9,6 +9,7 @@
 /* Last valid bits of the bitmapped firmware registers */
 #define KVM_REG_ARM_STD_BMAP_BIT_MAX		0
 #define KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX	0
+#define KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX	1
 
 #define KVM_ARM_SMCCC_STD_FEATURES \
 	GENMASK_ULL(KVM_REG_ARM_STD_BMAP_BIT_MAX, 0)
@@ -16,6 +17,9 @@
 #define KVM_ARM_SMCCC_STD_HYP_FEATURES \
 	GENMASK_ULL(KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX, 0)
 
+#define KVM_ARM_SMCCC_VENDOR_HYP_FEATURES \
+	GENMASK_ULL(KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX, 0)
+
 int kvm_hvc_call_handler(struct kvm_vcpu *vcpu);
 
 static inline u32 smccc_get_function(struct kvm_vcpu *vcpu)
-- 
2.35.1.1094.g7c7d902a7c-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] 96+ messages in thread

* [PATCH v5 05/10] Docs: KVM: Rename psci.rst to hypercalls.rst
  2022-04-07  1:15 ` Raghavendra Rao Ananta
  (?)
@ 2022-04-07  1:16   ` Raghavendra Rao Ananta
  -1 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-07  1:16 UTC (permalink / raw)
  To: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose
  Cc: kvm, Catalin Marinas, Peter Shier, linux-kernel, Paolo Bonzini,
	Will Deacon, kvmarm, linux-arm-kernel

Since the doc also covers general hypercalls' details,
rather than just PSCI, and the fact that the bitmap firmware
registers' details will be added to this doc, rename the file
to a more appropriate name- hypercalls.rst.

Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
Reviewed-by: Oliver Upton <oupton@google.com>
---
 Documentation/virt/kvm/arm/{psci.rst => hypercalls.rst} | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename Documentation/virt/kvm/arm/{psci.rst => hypercalls.rst} (100%)

diff --git a/Documentation/virt/kvm/arm/psci.rst b/Documentation/virt/kvm/arm/hypercalls.rst
similarity index 100%
rename from Documentation/virt/kvm/arm/psci.rst
rename to Documentation/virt/kvm/arm/hypercalls.rst
-- 
2.35.1.1094.g7c7d902a7c-goog

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

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

* [PATCH v5 05/10] Docs: KVM: Rename psci.rst to hypercalls.rst
@ 2022-04-07  1:16   ` Raghavendra Rao Ananta
  0 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-07  1:16 UTC (permalink / raw)
  To: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose
  Cc: Paolo Bonzini, Catalin Marinas, Will Deacon, Peter Shier,
	Ricardo Koller, Oliver Upton, Reiji Watanabe, Jing Zhang,
	Raghavendra Rao Anata, linux-arm-kernel, kvmarm, linux-kernel,
	kvm

Since the doc also covers general hypercalls' details,
rather than just PSCI, and the fact that the bitmap firmware
registers' details will be added to this doc, rename the file
to a more appropriate name- hypercalls.rst.

Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
Reviewed-by: Oliver Upton <oupton@google.com>
---
 Documentation/virt/kvm/arm/{psci.rst => hypercalls.rst} | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename Documentation/virt/kvm/arm/{psci.rst => hypercalls.rst} (100%)

diff --git a/Documentation/virt/kvm/arm/psci.rst b/Documentation/virt/kvm/arm/hypercalls.rst
similarity index 100%
rename from Documentation/virt/kvm/arm/psci.rst
rename to Documentation/virt/kvm/arm/hypercalls.rst
-- 
2.35.1.1094.g7c7d902a7c-goog


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

* [PATCH v5 05/10] Docs: KVM: Rename psci.rst to hypercalls.rst
@ 2022-04-07  1:16   ` Raghavendra Rao Ananta
  0 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-07  1:16 UTC (permalink / raw)
  To: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose
  Cc: Paolo Bonzini, Catalin Marinas, Will Deacon, Peter Shier,
	Ricardo Koller, Oliver Upton, Reiji Watanabe, Jing Zhang,
	Raghavendra Rao Anata, linux-arm-kernel, kvmarm, linux-kernel,
	kvm

Since the doc also covers general hypercalls' details,
rather than just PSCI, and the fact that the bitmap firmware
registers' details will be added to this doc, rename the file
to a more appropriate name- hypercalls.rst.

Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
Reviewed-by: Oliver Upton <oupton@google.com>
---
 Documentation/virt/kvm/arm/{psci.rst => hypercalls.rst} | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename Documentation/virt/kvm/arm/{psci.rst => hypercalls.rst} (100%)

diff --git a/Documentation/virt/kvm/arm/psci.rst b/Documentation/virt/kvm/arm/hypercalls.rst
similarity index 100%
rename from Documentation/virt/kvm/arm/psci.rst
rename to Documentation/virt/kvm/arm/hypercalls.rst
-- 
2.35.1.1094.g7c7d902a7c-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] 96+ messages in thread

* [PATCH v5 06/10] Docs: KVM: Add doc for the bitmap firmware registers
  2022-04-07  1:15 ` Raghavendra Rao Ananta
  (?)
@ 2022-04-07  1:16   ` Raghavendra Rao Ananta
  -1 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-07  1:16 UTC (permalink / raw)
  To: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose
  Cc: kvm, Catalin Marinas, Peter Shier, linux-kernel, Paolo Bonzini,
	Will Deacon, kvmarm, linux-arm-kernel

Add the documentation for the bitmap firmware registers in
hypercalls.rst and api.rst. This includes the details for
KVM_REG_ARM_STD_BMAP, KVM_REG_ARM_STD_HYP_BMAP, and
KVM_REG_ARM_VENDOR_HYP_BMAP registers.

Since the document is growing to carry other hypercall related
information, make necessary adjustments to present the document
in a generic sense, rather than being PSCI focused.

Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
---
 Documentation/virt/kvm/api.rst            | 17 ++++
 Documentation/virt/kvm/arm/hypercalls.rst | 95 ++++++++++++++++++-----
 2 files changed, 94 insertions(+), 18 deletions(-)

diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
index d13fa6600467..e0107b157965 100644
--- a/Documentation/virt/kvm/api.rst
+++ b/Documentation/virt/kvm/api.rst
@@ -2542,6 +2542,23 @@ arm64 firmware pseudo-registers have the following bit pattern::
 
   0x6030 0000 0014 <regno:16>
 
+arm64 bitmap feature firmware pseudo-registers have the following bit pattern::
+
+  0x6030 0000 0016 <regno:16>
+
+The bitmap feature firmware registers exposes the hypercall services that are
+available for userspace to configure. The set bits corresponds to the services
+that are available for the guests to access. By default, KVM sets all the
+supported bits during VM initialization. The userspace can discover the
+available services via KVM_GET_ONE_REG, and write-back the bitmap corresponding
+to the features that it wishes guests to see via KVM_SET_ONE_REG.
+
+Note: These registers are immutable once any of the vCPUs of the VM has run at
+least once. A KVM_SET_ONE_REG in such a scenario will return a -EBUSY to userspace.
+If there's no change in the value that's being written, 0 (success) is returned.
+
+(See Documentation/virt/kvm/arm/hypercalls.rst for more details.)
+
 arm64 SVE registers have the following bit patterns::
 
   0x6080 0000 0015 00 <n:5> <slice:5>   Zn bits[2048*slice + 2047 : 2048*slice]
diff --git a/Documentation/virt/kvm/arm/hypercalls.rst b/Documentation/virt/kvm/arm/hypercalls.rst
index d52c2e83b5b8..ccda9fc2d253 100644
--- a/Documentation/virt/kvm/arm/hypercalls.rst
+++ b/Documentation/virt/kvm/arm/hypercalls.rst
@@ -1,32 +1,32 @@
 .. SPDX-License-Identifier: GPL-2.0
 
-=========================================
-Power State Coordination Interface (PSCI)
-=========================================
+=======================
+ARM Hypercall Interface
+=======================
 
-KVM implements the PSCI (Power State Coordination Interface)
-specification in order to provide services such as CPU on/off, reset
-and power-off to the guest.
+KVM handles the hypercall services as requested by the guests. New hypercall
+services are regularly made available by the ARM specification or by KVM (as
+vendor services) if they make sense from a virtualization point of view.
 
-The PSCI specification is regularly updated to provide new features,
-and KVM implements these updates if they make sense from a virtualization
-point of view.
-
-This means that a guest booted on two different versions of KVM can
-observe two different "firmware" revisions. This could cause issues if
-a given guest is tied to a particular PSCI revision (unlikely), or if
-a migration causes a different PSCI version to be exposed out of the
-blue to an unsuspecting guest.
+This means that a guest booted on two different versions of KVM can observe
+two different "firmware" revisions. This could cause issues if a given guest
+is tied to a particular version of a hypercall service, or if a migration
+causes a different version to be exposed out of the blue to an unsuspecting
+guest.
 
 In order to remedy this situation, KVM exposes a set of "firmware
 pseudo-registers" that can be manipulated using the GET/SET_ONE_REG
 interface. These registers can be saved/restored by userspace, and set
-to a convenient value if required.
+to a convenient value as required.
 
-The following register is defined:
+The following registers are defined:
 
 * KVM_REG_ARM_PSCI_VERSION:
 
+  KVM implements the PSCI (Power State Coordination Interface)
+  specification in order to provide services such as CPU on/off, reset
+  and power-off to the guest.
+
   - Only valid if the vcpu has the KVM_ARM_VCPU_PSCI_0_2 feature set
     (and thus has already been initialized)
   - Returns the current PSCI version on GET_ONE_REG (defaulting to the
@@ -74,4 +74,63 @@ The following register is defined:
     KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED:
       The workaround is always active on this vCPU or it is not needed.
 
-.. [1] https://developer.arm.com/-/media/developer/pdf/ARM_DEN_0070A_Firmware_interfaces_for_mitigating_CVE-2017-5715.pdf
+
+Bitmap Feature Firmware Registers
+---------------------------------
+
+Contrary to the above registers, the following registers exposes the hypercall
+services in the form of a feature-bitmap to the userspace. This bitmap is
+translated to the services that are available to the guest. There is a register
+defined per service call owner and can be accessed via GET/SET_ONE_REG interface.
+
+By default, these registers are set with the upper limit of the features that
+are supported. This way userspace can discover all the electable hypercall services
+via GET_ONE_REG. The user-space can write-back the desired bitmap back via
+SET_ONE_REG. The features for the registers that are untouched, probably because
+userspace isn't aware of them, will be exposed as is to the guest.
+
+Note that KVM would't allow the userspace to configure the registers anymore once
+any of the vCPUs has run at least once. Instead, it will return a -EBUSY. However,
+if there's no change in the incoming value, it simply returns a success.
+
+The psuedo-firmware bitmap register are as follows:
+
+* KVM_REG_ARM_STD_BMAP:
+    Controls the bitmap of the ARM Standard Secure Service Calls.
+
+  The following bits are accepted:
+
+    Bit-0: KVM_REG_ARM_STD_BIT_TRNG_V1_0:
+      The bit represents the services offered under v1.0 of ARM True Random
+      Number Generator (TRNG) specification, ARM DEN0098.
+
+* KVM_REG_ARM_STD_HYP_BMAP:
+    Controls the bitmap of the ARM Standard Hypervisor Service Calls.
+
+  The following bits are accepted:
+
+    Bit-0: KVM_REG_ARM_STD_HYP_BIT_PV_TIME:
+      The bit represents the Paravirtualized Time service as represented by
+      ARM DEN0057A.
+
+* KVM_REG_ARM_VENDOR_HYP_BMAP:
+    Controls the bitmap of the Vendor specific Hypervisor Service Calls.
+
+  The following bits are accepted:
+
+    Bit-0: KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT
+      The bit represents the ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID
+      function-id
+
+    Bit-1: KVM_REG_ARM_VENDOR_HYP_BIT_PTP:
+      The bit represents the Precision Time Protocol KVM service.
+
+Errors:
+
+    =======  =============================================================
+    -ENOENT   Unknown register accessed.
+    -EBUSY    Attempt a 'write' to the register after the VM has started.
+    -EINVAL   Invalid bitmap written to the register.
+    =======  =============================================================
+
+.. [1] https://developer.arm.com/-/media/developer/pdf/ARM_DEN_0070A_Firmware_interfaces_for_mitigating_CVE-2017-5715.pdf
\ No newline at end of file
-- 
2.35.1.1094.g7c7d902a7c-goog

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

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

* [PATCH v5 06/10] Docs: KVM: Add doc for the bitmap firmware registers
@ 2022-04-07  1:16   ` Raghavendra Rao Ananta
  0 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-07  1:16 UTC (permalink / raw)
  To: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose
  Cc: Paolo Bonzini, Catalin Marinas, Will Deacon, Peter Shier,
	Ricardo Koller, Oliver Upton, Reiji Watanabe, Jing Zhang,
	Raghavendra Rao Anata, linux-arm-kernel, kvmarm, linux-kernel,
	kvm

Add the documentation for the bitmap firmware registers in
hypercalls.rst and api.rst. This includes the details for
KVM_REG_ARM_STD_BMAP, KVM_REG_ARM_STD_HYP_BMAP, and
KVM_REG_ARM_VENDOR_HYP_BMAP registers.

Since the document is growing to carry other hypercall related
information, make necessary adjustments to present the document
in a generic sense, rather than being PSCI focused.

Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
---
 Documentation/virt/kvm/api.rst            | 17 ++++
 Documentation/virt/kvm/arm/hypercalls.rst | 95 ++++++++++++++++++-----
 2 files changed, 94 insertions(+), 18 deletions(-)

diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
index d13fa6600467..e0107b157965 100644
--- a/Documentation/virt/kvm/api.rst
+++ b/Documentation/virt/kvm/api.rst
@@ -2542,6 +2542,23 @@ arm64 firmware pseudo-registers have the following bit pattern::
 
   0x6030 0000 0014 <regno:16>
 
+arm64 bitmap feature firmware pseudo-registers have the following bit pattern::
+
+  0x6030 0000 0016 <regno:16>
+
+The bitmap feature firmware registers exposes the hypercall services that are
+available for userspace to configure. The set bits corresponds to the services
+that are available for the guests to access. By default, KVM sets all the
+supported bits during VM initialization. The userspace can discover the
+available services via KVM_GET_ONE_REG, and write-back the bitmap corresponding
+to the features that it wishes guests to see via KVM_SET_ONE_REG.
+
+Note: These registers are immutable once any of the vCPUs of the VM has run at
+least once. A KVM_SET_ONE_REG in such a scenario will return a -EBUSY to userspace.
+If there's no change in the value that's being written, 0 (success) is returned.
+
+(See Documentation/virt/kvm/arm/hypercalls.rst for more details.)
+
 arm64 SVE registers have the following bit patterns::
 
   0x6080 0000 0015 00 <n:5> <slice:5>   Zn bits[2048*slice + 2047 : 2048*slice]
diff --git a/Documentation/virt/kvm/arm/hypercalls.rst b/Documentation/virt/kvm/arm/hypercalls.rst
index d52c2e83b5b8..ccda9fc2d253 100644
--- a/Documentation/virt/kvm/arm/hypercalls.rst
+++ b/Documentation/virt/kvm/arm/hypercalls.rst
@@ -1,32 +1,32 @@
 .. SPDX-License-Identifier: GPL-2.0
 
-=========================================
-Power State Coordination Interface (PSCI)
-=========================================
+=======================
+ARM Hypercall Interface
+=======================
 
-KVM implements the PSCI (Power State Coordination Interface)
-specification in order to provide services such as CPU on/off, reset
-and power-off to the guest.
+KVM handles the hypercall services as requested by the guests. New hypercall
+services are regularly made available by the ARM specification or by KVM (as
+vendor services) if they make sense from a virtualization point of view.
 
-The PSCI specification is regularly updated to provide new features,
-and KVM implements these updates if they make sense from a virtualization
-point of view.
-
-This means that a guest booted on two different versions of KVM can
-observe two different "firmware" revisions. This could cause issues if
-a given guest is tied to a particular PSCI revision (unlikely), or if
-a migration causes a different PSCI version to be exposed out of the
-blue to an unsuspecting guest.
+This means that a guest booted on two different versions of KVM can observe
+two different "firmware" revisions. This could cause issues if a given guest
+is tied to a particular version of a hypercall service, or if a migration
+causes a different version to be exposed out of the blue to an unsuspecting
+guest.
 
 In order to remedy this situation, KVM exposes a set of "firmware
 pseudo-registers" that can be manipulated using the GET/SET_ONE_REG
 interface. These registers can be saved/restored by userspace, and set
-to a convenient value if required.
+to a convenient value as required.
 
-The following register is defined:
+The following registers are defined:
 
 * KVM_REG_ARM_PSCI_VERSION:
 
+  KVM implements the PSCI (Power State Coordination Interface)
+  specification in order to provide services such as CPU on/off, reset
+  and power-off to the guest.
+
   - Only valid if the vcpu has the KVM_ARM_VCPU_PSCI_0_2 feature set
     (and thus has already been initialized)
   - Returns the current PSCI version on GET_ONE_REG (defaulting to the
@@ -74,4 +74,63 @@ The following register is defined:
     KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED:
       The workaround is always active on this vCPU or it is not needed.
 
-.. [1] https://developer.arm.com/-/media/developer/pdf/ARM_DEN_0070A_Firmware_interfaces_for_mitigating_CVE-2017-5715.pdf
+
+Bitmap Feature Firmware Registers
+---------------------------------
+
+Contrary to the above registers, the following registers exposes the hypercall
+services in the form of a feature-bitmap to the userspace. This bitmap is
+translated to the services that are available to the guest. There is a register
+defined per service call owner and can be accessed via GET/SET_ONE_REG interface.
+
+By default, these registers are set with the upper limit of the features that
+are supported. This way userspace can discover all the electable hypercall services
+via GET_ONE_REG. The user-space can write-back the desired bitmap back via
+SET_ONE_REG. The features for the registers that are untouched, probably because
+userspace isn't aware of them, will be exposed as is to the guest.
+
+Note that KVM would't allow the userspace to configure the registers anymore once
+any of the vCPUs has run at least once. Instead, it will return a -EBUSY. However,
+if there's no change in the incoming value, it simply returns a success.
+
+The psuedo-firmware bitmap register are as follows:
+
+* KVM_REG_ARM_STD_BMAP:
+    Controls the bitmap of the ARM Standard Secure Service Calls.
+
+  The following bits are accepted:
+
+    Bit-0: KVM_REG_ARM_STD_BIT_TRNG_V1_0:
+      The bit represents the services offered under v1.0 of ARM True Random
+      Number Generator (TRNG) specification, ARM DEN0098.
+
+* KVM_REG_ARM_STD_HYP_BMAP:
+    Controls the bitmap of the ARM Standard Hypervisor Service Calls.
+
+  The following bits are accepted:
+
+    Bit-0: KVM_REG_ARM_STD_HYP_BIT_PV_TIME:
+      The bit represents the Paravirtualized Time service as represented by
+      ARM DEN0057A.
+
+* KVM_REG_ARM_VENDOR_HYP_BMAP:
+    Controls the bitmap of the Vendor specific Hypervisor Service Calls.
+
+  The following bits are accepted:
+
+    Bit-0: KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT
+      The bit represents the ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID
+      function-id
+
+    Bit-1: KVM_REG_ARM_VENDOR_HYP_BIT_PTP:
+      The bit represents the Precision Time Protocol KVM service.
+
+Errors:
+
+    =======  =============================================================
+    -ENOENT   Unknown register accessed.
+    -EBUSY    Attempt a 'write' to the register after the VM has started.
+    -EINVAL   Invalid bitmap written to the register.
+    =======  =============================================================
+
+.. [1] https://developer.arm.com/-/media/developer/pdf/ARM_DEN_0070A_Firmware_interfaces_for_mitigating_CVE-2017-5715.pdf
\ No newline at end of file
-- 
2.35.1.1094.g7c7d902a7c-goog


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

* [PATCH v5 06/10] Docs: KVM: Add doc for the bitmap firmware registers
@ 2022-04-07  1:16   ` Raghavendra Rao Ananta
  0 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-07  1:16 UTC (permalink / raw)
  To: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose
  Cc: Paolo Bonzini, Catalin Marinas, Will Deacon, Peter Shier,
	Ricardo Koller, Oliver Upton, Reiji Watanabe, Jing Zhang,
	Raghavendra Rao Anata, linux-arm-kernel, kvmarm, linux-kernel,
	kvm

Add the documentation for the bitmap firmware registers in
hypercalls.rst and api.rst. This includes the details for
KVM_REG_ARM_STD_BMAP, KVM_REG_ARM_STD_HYP_BMAP, and
KVM_REG_ARM_VENDOR_HYP_BMAP registers.

Since the document is growing to carry other hypercall related
information, make necessary adjustments to present the document
in a generic sense, rather than being PSCI focused.

Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
---
 Documentation/virt/kvm/api.rst            | 17 ++++
 Documentation/virt/kvm/arm/hypercalls.rst | 95 ++++++++++++++++++-----
 2 files changed, 94 insertions(+), 18 deletions(-)

diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
index d13fa6600467..e0107b157965 100644
--- a/Documentation/virt/kvm/api.rst
+++ b/Documentation/virt/kvm/api.rst
@@ -2542,6 +2542,23 @@ arm64 firmware pseudo-registers have the following bit pattern::
 
   0x6030 0000 0014 <regno:16>
 
+arm64 bitmap feature firmware pseudo-registers have the following bit pattern::
+
+  0x6030 0000 0016 <regno:16>
+
+The bitmap feature firmware registers exposes the hypercall services that are
+available for userspace to configure. The set bits corresponds to the services
+that are available for the guests to access. By default, KVM sets all the
+supported bits during VM initialization. The userspace can discover the
+available services via KVM_GET_ONE_REG, and write-back the bitmap corresponding
+to the features that it wishes guests to see via KVM_SET_ONE_REG.
+
+Note: These registers are immutable once any of the vCPUs of the VM has run at
+least once. A KVM_SET_ONE_REG in such a scenario will return a -EBUSY to userspace.
+If there's no change in the value that's being written, 0 (success) is returned.
+
+(See Documentation/virt/kvm/arm/hypercalls.rst for more details.)
+
 arm64 SVE registers have the following bit patterns::
 
   0x6080 0000 0015 00 <n:5> <slice:5>   Zn bits[2048*slice + 2047 : 2048*slice]
diff --git a/Documentation/virt/kvm/arm/hypercalls.rst b/Documentation/virt/kvm/arm/hypercalls.rst
index d52c2e83b5b8..ccda9fc2d253 100644
--- a/Documentation/virt/kvm/arm/hypercalls.rst
+++ b/Documentation/virt/kvm/arm/hypercalls.rst
@@ -1,32 +1,32 @@
 .. SPDX-License-Identifier: GPL-2.0
 
-=========================================
-Power State Coordination Interface (PSCI)
-=========================================
+=======================
+ARM Hypercall Interface
+=======================
 
-KVM implements the PSCI (Power State Coordination Interface)
-specification in order to provide services such as CPU on/off, reset
-and power-off to the guest.
+KVM handles the hypercall services as requested by the guests. New hypercall
+services are regularly made available by the ARM specification or by KVM (as
+vendor services) if they make sense from a virtualization point of view.
 
-The PSCI specification is regularly updated to provide new features,
-and KVM implements these updates if they make sense from a virtualization
-point of view.
-
-This means that a guest booted on two different versions of KVM can
-observe two different "firmware" revisions. This could cause issues if
-a given guest is tied to a particular PSCI revision (unlikely), or if
-a migration causes a different PSCI version to be exposed out of the
-blue to an unsuspecting guest.
+This means that a guest booted on two different versions of KVM can observe
+two different "firmware" revisions. This could cause issues if a given guest
+is tied to a particular version of a hypercall service, or if a migration
+causes a different version to be exposed out of the blue to an unsuspecting
+guest.
 
 In order to remedy this situation, KVM exposes a set of "firmware
 pseudo-registers" that can be manipulated using the GET/SET_ONE_REG
 interface. These registers can be saved/restored by userspace, and set
-to a convenient value if required.
+to a convenient value as required.
 
-The following register is defined:
+The following registers are defined:
 
 * KVM_REG_ARM_PSCI_VERSION:
 
+  KVM implements the PSCI (Power State Coordination Interface)
+  specification in order to provide services such as CPU on/off, reset
+  and power-off to the guest.
+
   - Only valid if the vcpu has the KVM_ARM_VCPU_PSCI_0_2 feature set
     (and thus has already been initialized)
   - Returns the current PSCI version on GET_ONE_REG (defaulting to the
@@ -74,4 +74,63 @@ The following register is defined:
     KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED:
       The workaround is always active on this vCPU or it is not needed.
 
-.. [1] https://developer.arm.com/-/media/developer/pdf/ARM_DEN_0070A_Firmware_interfaces_for_mitigating_CVE-2017-5715.pdf
+
+Bitmap Feature Firmware Registers
+---------------------------------
+
+Contrary to the above registers, the following registers exposes the hypercall
+services in the form of a feature-bitmap to the userspace. This bitmap is
+translated to the services that are available to the guest. There is a register
+defined per service call owner and can be accessed via GET/SET_ONE_REG interface.
+
+By default, these registers are set with the upper limit of the features that
+are supported. This way userspace can discover all the electable hypercall services
+via GET_ONE_REG. The user-space can write-back the desired bitmap back via
+SET_ONE_REG. The features for the registers that are untouched, probably because
+userspace isn't aware of them, will be exposed as is to the guest.
+
+Note that KVM would't allow the userspace to configure the registers anymore once
+any of the vCPUs has run at least once. Instead, it will return a -EBUSY. However,
+if there's no change in the incoming value, it simply returns a success.
+
+The psuedo-firmware bitmap register are as follows:
+
+* KVM_REG_ARM_STD_BMAP:
+    Controls the bitmap of the ARM Standard Secure Service Calls.
+
+  The following bits are accepted:
+
+    Bit-0: KVM_REG_ARM_STD_BIT_TRNG_V1_0:
+      The bit represents the services offered under v1.0 of ARM True Random
+      Number Generator (TRNG) specification, ARM DEN0098.
+
+* KVM_REG_ARM_STD_HYP_BMAP:
+    Controls the bitmap of the ARM Standard Hypervisor Service Calls.
+
+  The following bits are accepted:
+
+    Bit-0: KVM_REG_ARM_STD_HYP_BIT_PV_TIME:
+      The bit represents the Paravirtualized Time service as represented by
+      ARM DEN0057A.
+
+* KVM_REG_ARM_VENDOR_HYP_BMAP:
+    Controls the bitmap of the Vendor specific Hypervisor Service Calls.
+
+  The following bits are accepted:
+
+    Bit-0: KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT
+      The bit represents the ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID
+      function-id
+
+    Bit-1: KVM_REG_ARM_VENDOR_HYP_BIT_PTP:
+      The bit represents the Precision Time Protocol KVM service.
+
+Errors:
+
+    =======  =============================================================
+    -ENOENT   Unknown register accessed.
+    -EBUSY    Attempt a 'write' to the register after the VM has started.
+    -EINVAL   Invalid bitmap written to the register.
+    =======  =============================================================
+
+.. [1] https://developer.arm.com/-/media/developer/pdf/ARM_DEN_0070A_Firmware_interfaces_for_mitigating_CVE-2017-5715.pdf
\ No newline at end of file
-- 
2.35.1.1094.g7c7d902a7c-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] 96+ messages in thread

* [PATCH v5 07/10] tools: Import ARM SMCCC definitions
  2022-04-07  1:15 ` Raghavendra Rao Ananta
  (?)
@ 2022-04-07  1:16   ` Raghavendra Rao Ananta
  -1 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-07  1:16 UTC (permalink / raw)
  To: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose
  Cc: kvm, Catalin Marinas, Peter Shier, linux-kernel, Paolo Bonzini,
	Will Deacon, kvmarm, linux-arm-kernel

Import the standard SMCCC definitions from include/linux/arm-smccc.h.

Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
---
 tools/include/linux/arm-smccc.h | 193 ++++++++++++++++++++++++++++++++
 1 file changed, 193 insertions(+)
 create mode 100644 tools/include/linux/arm-smccc.h

diff --git a/tools/include/linux/arm-smccc.h b/tools/include/linux/arm-smccc.h
new file mode 100644
index 000000000000..63ce9bebccd3
--- /dev/null
+++ b/tools/include/linux/arm-smccc.h
@@ -0,0 +1,193 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2015, Linaro Limited
+ */
+#ifndef __LINUX_ARM_SMCCC_H
+#define __LINUX_ARM_SMCCC_H
+
+#include <linux/const.h>
+
+/*
+ * This file provides common defines for ARM SMC Calling Convention as
+ * specified in
+ * https://developer.arm.com/docs/den0028/latest
+ *
+ * This code is up-to-date with version DEN 0028 C
+ */
+
+#define ARM_SMCCC_STD_CALL	        _AC(0,U)
+#define ARM_SMCCC_FAST_CALL	        _AC(1,U)
+#define ARM_SMCCC_TYPE_SHIFT		31
+
+#define ARM_SMCCC_SMC_32		0
+#define ARM_SMCCC_SMC_64		1
+#define ARM_SMCCC_CALL_CONV_SHIFT	30
+
+#define ARM_SMCCC_OWNER_MASK		0x3F
+#define ARM_SMCCC_OWNER_SHIFT		24
+
+#define ARM_SMCCC_FUNC_MASK		0xFFFF
+
+#define ARM_SMCCC_IS_FAST_CALL(smc_val)	\
+	((smc_val) & (ARM_SMCCC_FAST_CALL << ARM_SMCCC_TYPE_SHIFT))
+#define ARM_SMCCC_IS_64(smc_val) \
+	((smc_val) & (ARM_SMCCC_SMC_64 << ARM_SMCCC_CALL_CONV_SHIFT))
+#define ARM_SMCCC_FUNC_NUM(smc_val)	((smc_val) & ARM_SMCCC_FUNC_MASK)
+#define ARM_SMCCC_OWNER_NUM(smc_val) \
+	(((smc_val) >> ARM_SMCCC_OWNER_SHIFT) & ARM_SMCCC_OWNER_MASK)
+
+#define ARM_SMCCC_CALL_VAL(type, calling_convention, owner, func_num) \
+	(((type) << ARM_SMCCC_TYPE_SHIFT) | \
+	((calling_convention) << ARM_SMCCC_CALL_CONV_SHIFT) | \
+	(((owner) & ARM_SMCCC_OWNER_MASK) << ARM_SMCCC_OWNER_SHIFT) | \
+	((func_num) & ARM_SMCCC_FUNC_MASK))
+
+#define ARM_SMCCC_OWNER_ARCH		0
+#define ARM_SMCCC_OWNER_CPU		1
+#define ARM_SMCCC_OWNER_SIP		2
+#define ARM_SMCCC_OWNER_OEM		3
+#define ARM_SMCCC_OWNER_STANDARD	4
+#define ARM_SMCCC_OWNER_STANDARD_HYP	5
+#define ARM_SMCCC_OWNER_VENDOR_HYP	6
+#define ARM_SMCCC_OWNER_TRUSTED_APP	48
+#define ARM_SMCCC_OWNER_TRUSTED_APP_END	49
+#define ARM_SMCCC_OWNER_TRUSTED_OS	50
+#define ARM_SMCCC_OWNER_TRUSTED_OS_END	63
+
+#define ARM_SMCCC_FUNC_QUERY_CALL_UID  0xff01
+
+#define ARM_SMCCC_QUIRK_NONE		0
+#define ARM_SMCCC_QUIRK_QCOM_A6		1 /* Save/restore register a6 */
+
+#define ARM_SMCCC_VERSION_1_0		0x10000
+#define ARM_SMCCC_VERSION_1_1		0x10001
+#define ARM_SMCCC_VERSION_1_2		0x10002
+#define ARM_SMCCC_VERSION_1_3		0x10003
+
+#define ARM_SMCCC_1_3_SVE_HINT		0x10000
+
+#define ARM_SMCCC_VERSION_FUNC_ID					\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,				\
+			   ARM_SMCCC_SMC_32,				\
+			   0, 0)
+
+#define ARM_SMCCC_ARCH_FEATURES_FUNC_ID					\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,				\
+			   ARM_SMCCC_SMC_32,				\
+			   0, 1)
+
+#define ARM_SMCCC_ARCH_SOC_ID						\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,				\
+			   ARM_SMCCC_SMC_32,				\
+			   0, 2)
+
+#define ARM_SMCCC_ARCH_WORKAROUND_1					\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,				\
+			   ARM_SMCCC_SMC_32,				\
+			   0, 0x8000)
+
+#define ARM_SMCCC_ARCH_WORKAROUND_2					\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,				\
+			   ARM_SMCCC_SMC_32,				\
+			   0, 0x7fff)
+
+#define ARM_SMCCC_ARCH_WORKAROUND_3					\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,				\
+			   ARM_SMCCC_SMC_32,				\
+			   0, 0x3fff)
+
+#define ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID				\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,				\
+			   ARM_SMCCC_SMC_32,				\
+			   ARM_SMCCC_OWNER_VENDOR_HYP,			\
+			   ARM_SMCCC_FUNC_QUERY_CALL_UID)
+
+/* KVM UID value: 28b46fb6-2ec5-11e9-a9ca-4b564d003a74 */
+#define ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_0	0xb66fb428U
+#define ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_1	0xe911c52eU
+#define ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_2	0x564bcaa9U
+#define ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_3	0x743a004dU
+
+/* KVM "vendor specific" services */
+#define ARM_SMCCC_KVM_FUNC_FEATURES		0
+#define ARM_SMCCC_KVM_FUNC_PTP			1
+#define ARM_SMCCC_KVM_FUNC_FEATURES_2		127
+#define ARM_SMCCC_KVM_NUM_FUNCS			128
+
+#define ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID			\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,				\
+			   ARM_SMCCC_SMC_32,				\
+			   ARM_SMCCC_OWNER_VENDOR_HYP,			\
+			   ARM_SMCCC_KVM_FUNC_FEATURES)
+
+#define SMCCC_ARCH_WORKAROUND_RET_UNAFFECTED	1
+
+/*
+ * ptp_kvm is a feature used for time sync between vm and host.
+ * ptp_kvm module in guest kernel will get service from host using
+ * this hypercall ID.
+ */
+#define ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID				\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,				\
+			   ARM_SMCCC_SMC_32,				\
+			   ARM_SMCCC_OWNER_VENDOR_HYP,			\
+			   ARM_SMCCC_KVM_FUNC_PTP)
+
+/* ptp_kvm counter type ID */
+#define KVM_PTP_VIRT_COUNTER			0
+#define KVM_PTP_PHYS_COUNTER			1
+
+/* Paravirtualised time calls (defined by ARM DEN0057A) */
+#define ARM_SMCCC_HV_PV_TIME_FEATURES				\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,			\
+			   ARM_SMCCC_SMC_64,			\
+			   ARM_SMCCC_OWNER_STANDARD_HYP,	\
+			   0x20)
+
+#define ARM_SMCCC_HV_PV_TIME_ST					\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,			\
+			   ARM_SMCCC_SMC_64,			\
+			   ARM_SMCCC_OWNER_STANDARD_HYP,	\
+			   0x21)
+
+/* TRNG entropy source calls (defined by ARM DEN0098) */
+#define ARM_SMCCC_TRNG_VERSION					\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,			\
+			   ARM_SMCCC_SMC_32,			\
+			   ARM_SMCCC_OWNER_STANDARD,		\
+			   0x50)
+
+#define ARM_SMCCC_TRNG_FEATURES					\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,			\
+			   ARM_SMCCC_SMC_32,			\
+			   ARM_SMCCC_OWNER_STANDARD,		\
+			   0x51)
+
+#define ARM_SMCCC_TRNG_GET_UUID					\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,			\
+			   ARM_SMCCC_SMC_32,			\
+			   ARM_SMCCC_OWNER_STANDARD,		\
+			   0x52)
+
+#define ARM_SMCCC_TRNG_RND32					\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,			\
+			   ARM_SMCCC_SMC_32,			\
+			   ARM_SMCCC_OWNER_STANDARD,		\
+			   0x53)
+
+#define ARM_SMCCC_TRNG_RND64					\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,			\
+			   ARM_SMCCC_SMC_64,			\
+			   ARM_SMCCC_OWNER_STANDARD,		\
+			   0x53)
+
+/*
+ * Return codes defined in ARM DEN 0070A
+ * ARM DEN 0070A is now merged/consolidated into ARM DEN 0028 C
+ */
+#define SMCCC_RET_SUCCESS			0
+#define SMCCC_RET_NOT_SUPPORTED			-1
+#define SMCCC_RET_NOT_REQUIRED			-2
+#define SMCCC_RET_INVALID_PARAMETER		-3
+
+#endif /*__LINUX_ARM_SMCCC_H*/
-- 
2.35.1.1094.g7c7d902a7c-goog

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

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

* [PATCH v5 07/10] tools: Import ARM SMCCC definitions
@ 2022-04-07  1:16   ` Raghavendra Rao Ananta
  0 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-07  1:16 UTC (permalink / raw)
  To: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose
  Cc: Paolo Bonzini, Catalin Marinas, Will Deacon, Peter Shier,
	Ricardo Koller, Oliver Upton, Reiji Watanabe, Jing Zhang,
	Raghavendra Rao Anata, linux-arm-kernel, kvmarm, linux-kernel,
	kvm

Import the standard SMCCC definitions from include/linux/arm-smccc.h.

Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
---
 tools/include/linux/arm-smccc.h | 193 ++++++++++++++++++++++++++++++++
 1 file changed, 193 insertions(+)
 create mode 100644 tools/include/linux/arm-smccc.h

diff --git a/tools/include/linux/arm-smccc.h b/tools/include/linux/arm-smccc.h
new file mode 100644
index 000000000000..63ce9bebccd3
--- /dev/null
+++ b/tools/include/linux/arm-smccc.h
@@ -0,0 +1,193 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2015, Linaro Limited
+ */
+#ifndef __LINUX_ARM_SMCCC_H
+#define __LINUX_ARM_SMCCC_H
+
+#include <linux/const.h>
+
+/*
+ * This file provides common defines for ARM SMC Calling Convention as
+ * specified in
+ * https://developer.arm.com/docs/den0028/latest
+ *
+ * This code is up-to-date with version DEN 0028 C
+ */
+
+#define ARM_SMCCC_STD_CALL	        _AC(0,U)
+#define ARM_SMCCC_FAST_CALL	        _AC(1,U)
+#define ARM_SMCCC_TYPE_SHIFT		31
+
+#define ARM_SMCCC_SMC_32		0
+#define ARM_SMCCC_SMC_64		1
+#define ARM_SMCCC_CALL_CONV_SHIFT	30
+
+#define ARM_SMCCC_OWNER_MASK		0x3F
+#define ARM_SMCCC_OWNER_SHIFT		24
+
+#define ARM_SMCCC_FUNC_MASK		0xFFFF
+
+#define ARM_SMCCC_IS_FAST_CALL(smc_val)	\
+	((smc_val) & (ARM_SMCCC_FAST_CALL << ARM_SMCCC_TYPE_SHIFT))
+#define ARM_SMCCC_IS_64(smc_val) \
+	((smc_val) & (ARM_SMCCC_SMC_64 << ARM_SMCCC_CALL_CONV_SHIFT))
+#define ARM_SMCCC_FUNC_NUM(smc_val)	((smc_val) & ARM_SMCCC_FUNC_MASK)
+#define ARM_SMCCC_OWNER_NUM(smc_val) \
+	(((smc_val) >> ARM_SMCCC_OWNER_SHIFT) & ARM_SMCCC_OWNER_MASK)
+
+#define ARM_SMCCC_CALL_VAL(type, calling_convention, owner, func_num) \
+	(((type) << ARM_SMCCC_TYPE_SHIFT) | \
+	((calling_convention) << ARM_SMCCC_CALL_CONV_SHIFT) | \
+	(((owner) & ARM_SMCCC_OWNER_MASK) << ARM_SMCCC_OWNER_SHIFT) | \
+	((func_num) & ARM_SMCCC_FUNC_MASK))
+
+#define ARM_SMCCC_OWNER_ARCH		0
+#define ARM_SMCCC_OWNER_CPU		1
+#define ARM_SMCCC_OWNER_SIP		2
+#define ARM_SMCCC_OWNER_OEM		3
+#define ARM_SMCCC_OWNER_STANDARD	4
+#define ARM_SMCCC_OWNER_STANDARD_HYP	5
+#define ARM_SMCCC_OWNER_VENDOR_HYP	6
+#define ARM_SMCCC_OWNER_TRUSTED_APP	48
+#define ARM_SMCCC_OWNER_TRUSTED_APP_END	49
+#define ARM_SMCCC_OWNER_TRUSTED_OS	50
+#define ARM_SMCCC_OWNER_TRUSTED_OS_END	63
+
+#define ARM_SMCCC_FUNC_QUERY_CALL_UID  0xff01
+
+#define ARM_SMCCC_QUIRK_NONE		0
+#define ARM_SMCCC_QUIRK_QCOM_A6		1 /* Save/restore register a6 */
+
+#define ARM_SMCCC_VERSION_1_0		0x10000
+#define ARM_SMCCC_VERSION_1_1		0x10001
+#define ARM_SMCCC_VERSION_1_2		0x10002
+#define ARM_SMCCC_VERSION_1_3		0x10003
+
+#define ARM_SMCCC_1_3_SVE_HINT		0x10000
+
+#define ARM_SMCCC_VERSION_FUNC_ID					\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,				\
+			   ARM_SMCCC_SMC_32,				\
+			   0, 0)
+
+#define ARM_SMCCC_ARCH_FEATURES_FUNC_ID					\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,				\
+			   ARM_SMCCC_SMC_32,				\
+			   0, 1)
+
+#define ARM_SMCCC_ARCH_SOC_ID						\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,				\
+			   ARM_SMCCC_SMC_32,				\
+			   0, 2)
+
+#define ARM_SMCCC_ARCH_WORKAROUND_1					\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,				\
+			   ARM_SMCCC_SMC_32,				\
+			   0, 0x8000)
+
+#define ARM_SMCCC_ARCH_WORKAROUND_2					\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,				\
+			   ARM_SMCCC_SMC_32,				\
+			   0, 0x7fff)
+
+#define ARM_SMCCC_ARCH_WORKAROUND_3					\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,				\
+			   ARM_SMCCC_SMC_32,				\
+			   0, 0x3fff)
+
+#define ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID				\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,				\
+			   ARM_SMCCC_SMC_32,				\
+			   ARM_SMCCC_OWNER_VENDOR_HYP,			\
+			   ARM_SMCCC_FUNC_QUERY_CALL_UID)
+
+/* KVM UID value: 28b46fb6-2ec5-11e9-a9ca-4b564d003a74 */
+#define ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_0	0xb66fb428U
+#define ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_1	0xe911c52eU
+#define ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_2	0x564bcaa9U
+#define ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_3	0x743a004dU
+
+/* KVM "vendor specific" services */
+#define ARM_SMCCC_KVM_FUNC_FEATURES		0
+#define ARM_SMCCC_KVM_FUNC_PTP			1
+#define ARM_SMCCC_KVM_FUNC_FEATURES_2		127
+#define ARM_SMCCC_KVM_NUM_FUNCS			128
+
+#define ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID			\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,				\
+			   ARM_SMCCC_SMC_32,				\
+			   ARM_SMCCC_OWNER_VENDOR_HYP,			\
+			   ARM_SMCCC_KVM_FUNC_FEATURES)
+
+#define SMCCC_ARCH_WORKAROUND_RET_UNAFFECTED	1
+
+/*
+ * ptp_kvm is a feature used for time sync between vm and host.
+ * ptp_kvm module in guest kernel will get service from host using
+ * this hypercall ID.
+ */
+#define ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID				\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,				\
+			   ARM_SMCCC_SMC_32,				\
+			   ARM_SMCCC_OWNER_VENDOR_HYP,			\
+			   ARM_SMCCC_KVM_FUNC_PTP)
+
+/* ptp_kvm counter type ID */
+#define KVM_PTP_VIRT_COUNTER			0
+#define KVM_PTP_PHYS_COUNTER			1
+
+/* Paravirtualised time calls (defined by ARM DEN0057A) */
+#define ARM_SMCCC_HV_PV_TIME_FEATURES				\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,			\
+			   ARM_SMCCC_SMC_64,			\
+			   ARM_SMCCC_OWNER_STANDARD_HYP,	\
+			   0x20)
+
+#define ARM_SMCCC_HV_PV_TIME_ST					\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,			\
+			   ARM_SMCCC_SMC_64,			\
+			   ARM_SMCCC_OWNER_STANDARD_HYP,	\
+			   0x21)
+
+/* TRNG entropy source calls (defined by ARM DEN0098) */
+#define ARM_SMCCC_TRNG_VERSION					\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,			\
+			   ARM_SMCCC_SMC_32,			\
+			   ARM_SMCCC_OWNER_STANDARD,		\
+			   0x50)
+
+#define ARM_SMCCC_TRNG_FEATURES					\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,			\
+			   ARM_SMCCC_SMC_32,			\
+			   ARM_SMCCC_OWNER_STANDARD,		\
+			   0x51)
+
+#define ARM_SMCCC_TRNG_GET_UUID					\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,			\
+			   ARM_SMCCC_SMC_32,			\
+			   ARM_SMCCC_OWNER_STANDARD,		\
+			   0x52)
+
+#define ARM_SMCCC_TRNG_RND32					\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,			\
+			   ARM_SMCCC_SMC_32,			\
+			   ARM_SMCCC_OWNER_STANDARD,		\
+			   0x53)
+
+#define ARM_SMCCC_TRNG_RND64					\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,			\
+			   ARM_SMCCC_SMC_64,			\
+			   ARM_SMCCC_OWNER_STANDARD,		\
+			   0x53)
+
+/*
+ * Return codes defined in ARM DEN 0070A
+ * ARM DEN 0070A is now merged/consolidated into ARM DEN 0028 C
+ */
+#define SMCCC_RET_SUCCESS			0
+#define SMCCC_RET_NOT_SUPPORTED			-1
+#define SMCCC_RET_NOT_REQUIRED			-2
+#define SMCCC_RET_INVALID_PARAMETER		-3
+
+#endif /*__LINUX_ARM_SMCCC_H*/
-- 
2.35.1.1094.g7c7d902a7c-goog


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

* [PATCH v5 07/10] tools: Import ARM SMCCC definitions
@ 2022-04-07  1:16   ` Raghavendra Rao Ananta
  0 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-07  1:16 UTC (permalink / raw)
  To: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose
  Cc: Paolo Bonzini, Catalin Marinas, Will Deacon, Peter Shier,
	Ricardo Koller, Oliver Upton, Reiji Watanabe, Jing Zhang,
	Raghavendra Rao Anata, linux-arm-kernel, kvmarm, linux-kernel,
	kvm

Import the standard SMCCC definitions from include/linux/arm-smccc.h.

Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
---
 tools/include/linux/arm-smccc.h | 193 ++++++++++++++++++++++++++++++++
 1 file changed, 193 insertions(+)
 create mode 100644 tools/include/linux/arm-smccc.h

diff --git a/tools/include/linux/arm-smccc.h b/tools/include/linux/arm-smccc.h
new file mode 100644
index 000000000000..63ce9bebccd3
--- /dev/null
+++ b/tools/include/linux/arm-smccc.h
@@ -0,0 +1,193 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2015, Linaro Limited
+ */
+#ifndef __LINUX_ARM_SMCCC_H
+#define __LINUX_ARM_SMCCC_H
+
+#include <linux/const.h>
+
+/*
+ * This file provides common defines for ARM SMC Calling Convention as
+ * specified in
+ * https://developer.arm.com/docs/den0028/latest
+ *
+ * This code is up-to-date with version DEN 0028 C
+ */
+
+#define ARM_SMCCC_STD_CALL	        _AC(0,U)
+#define ARM_SMCCC_FAST_CALL	        _AC(1,U)
+#define ARM_SMCCC_TYPE_SHIFT		31
+
+#define ARM_SMCCC_SMC_32		0
+#define ARM_SMCCC_SMC_64		1
+#define ARM_SMCCC_CALL_CONV_SHIFT	30
+
+#define ARM_SMCCC_OWNER_MASK		0x3F
+#define ARM_SMCCC_OWNER_SHIFT		24
+
+#define ARM_SMCCC_FUNC_MASK		0xFFFF
+
+#define ARM_SMCCC_IS_FAST_CALL(smc_val)	\
+	((smc_val) & (ARM_SMCCC_FAST_CALL << ARM_SMCCC_TYPE_SHIFT))
+#define ARM_SMCCC_IS_64(smc_val) \
+	((smc_val) & (ARM_SMCCC_SMC_64 << ARM_SMCCC_CALL_CONV_SHIFT))
+#define ARM_SMCCC_FUNC_NUM(smc_val)	((smc_val) & ARM_SMCCC_FUNC_MASK)
+#define ARM_SMCCC_OWNER_NUM(smc_val) \
+	(((smc_val) >> ARM_SMCCC_OWNER_SHIFT) & ARM_SMCCC_OWNER_MASK)
+
+#define ARM_SMCCC_CALL_VAL(type, calling_convention, owner, func_num) \
+	(((type) << ARM_SMCCC_TYPE_SHIFT) | \
+	((calling_convention) << ARM_SMCCC_CALL_CONV_SHIFT) | \
+	(((owner) & ARM_SMCCC_OWNER_MASK) << ARM_SMCCC_OWNER_SHIFT) | \
+	((func_num) & ARM_SMCCC_FUNC_MASK))
+
+#define ARM_SMCCC_OWNER_ARCH		0
+#define ARM_SMCCC_OWNER_CPU		1
+#define ARM_SMCCC_OWNER_SIP		2
+#define ARM_SMCCC_OWNER_OEM		3
+#define ARM_SMCCC_OWNER_STANDARD	4
+#define ARM_SMCCC_OWNER_STANDARD_HYP	5
+#define ARM_SMCCC_OWNER_VENDOR_HYP	6
+#define ARM_SMCCC_OWNER_TRUSTED_APP	48
+#define ARM_SMCCC_OWNER_TRUSTED_APP_END	49
+#define ARM_SMCCC_OWNER_TRUSTED_OS	50
+#define ARM_SMCCC_OWNER_TRUSTED_OS_END	63
+
+#define ARM_SMCCC_FUNC_QUERY_CALL_UID  0xff01
+
+#define ARM_SMCCC_QUIRK_NONE		0
+#define ARM_SMCCC_QUIRK_QCOM_A6		1 /* Save/restore register a6 */
+
+#define ARM_SMCCC_VERSION_1_0		0x10000
+#define ARM_SMCCC_VERSION_1_1		0x10001
+#define ARM_SMCCC_VERSION_1_2		0x10002
+#define ARM_SMCCC_VERSION_1_3		0x10003
+
+#define ARM_SMCCC_1_3_SVE_HINT		0x10000
+
+#define ARM_SMCCC_VERSION_FUNC_ID					\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,				\
+			   ARM_SMCCC_SMC_32,				\
+			   0, 0)
+
+#define ARM_SMCCC_ARCH_FEATURES_FUNC_ID					\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,				\
+			   ARM_SMCCC_SMC_32,				\
+			   0, 1)
+
+#define ARM_SMCCC_ARCH_SOC_ID						\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,				\
+			   ARM_SMCCC_SMC_32,				\
+			   0, 2)
+
+#define ARM_SMCCC_ARCH_WORKAROUND_1					\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,				\
+			   ARM_SMCCC_SMC_32,				\
+			   0, 0x8000)
+
+#define ARM_SMCCC_ARCH_WORKAROUND_2					\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,				\
+			   ARM_SMCCC_SMC_32,				\
+			   0, 0x7fff)
+
+#define ARM_SMCCC_ARCH_WORKAROUND_3					\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,				\
+			   ARM_SMCCC_SMC_32,				\
+			   0, 0x3fff)
+
+#define ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID				\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,				\
+			   ARM_SMCCC_SMC_32,				\
+			   ARM_SMCCC_OWNER_VENDOR_HYP,			\
+			   ARM_SMCCC_FUNC_QUERY_CALL_UID)
+
+/* KVM UID value: 28b46fb6-2ec5-11e9-a9ca-4b564d003a74 */
+#define ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_0	0xb66fb428U
+#define ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_1	0xe911c52eU
+#define ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_2	0x564bcaa9U
+#define ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_3	0x743a004dU
+
+/* KVM "vendor specific" services */
+#define ARM_SMCCC_KVM_FUNC_FEATURES		0
+#define ARM_SMCCC_KVM_FUNC_PTP			1
+#define ARM_SMCCC_KVM_FUNC_FEATURES_2		127
+#define ARM_SMCCC_KVM_NUM_FUNCS			128
+
+#define ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID			\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,				\
+			   ARM_SMCCC_SMC_32,				\
+			   ARM_SMCCC_OWNER_VENDOR_HYP,			\
+			   ARM_SMCCC_KVM_FUNC_FEATURES)
+
+#define SMCCC_ARCH_WORKAROUND_RET_UNAFFECTED	1
+
+/*
+ * ptp_kvm is a feature used for time sync between vm and host.
+ * ptp_kvm module in guest kernel will get service from host using
+ * this hypercall ID.
+ */
+#define ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID				\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,				\
+			   ARM_SMCCC_SMC_32,				\
+			   ARM_SMCCC_OWNER_VENDOR_HYP,			\
+			   ARM_SMCCC_KVM_FUNC_PTP)
+
+/* ptp_kvm counter type ID */
+#define KVM_PTP_VIRT_COUNTER			0
+#define KVM_PTP_PHYS_COUNTER			1
+
+/* Paravirtualised time calls (defined by ARM DEN0057A) */
+#define ARM_SMCCC_HV_PV_TIME_FEATURES				\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,			\
+			   ARM_SMCCC_SMC_64,			\
+			   ARM_SMCCC_OWNER_STANDARD_HYP,	\
+			   0x20)
+
+#define ARM_SMCCC_HV_PV_TIME_ST					\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,			\
+			   ARM_SMCCC_SMC_64,			\
+			   ARM_SMCCC_OWNER_STANDARD_HYP,	\
+			   0x21)
+
+/* TRNG entropy source calls (defined by ARM DEN0098) */
+#define ARM_SMCCC_TRNG_VERSION					\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,			\
+			   ARM_SMCCC_SMC_32,			\
+			   ARM_SMCCC_OWNER_STANDARD,		\
+			   0x50)
+
+#define ARM_SMCCC_TRNG_FEATURES					\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,			\
+			   ARM_SMCCC_SMC_32,			\
+			   ARM_SMCCC_OWNER_STANDARD,		\
+			   0x51)
+
+#define ARM_SMCCC_TRNG_GET_UUID					\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,			\
+			   ARM_SMCCC_SMC_32,			\
+			   ARM_SMCCC_OWNER_STANDARD,		\
+			   0x52)
+
+#define ARM_SMCCC_TRNG_RND32					\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,			\
+			   ARM_SMCCC_SMC_32,			\
+			   ARM_SMCCC_OWNER_STANDARD,		\
+			   0x53)
+
+#define ARM_SMCCC_TRNG_RND64					\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,			\
+			   ARM_SMCCC_SMC_64,			\
+			   ARM_SMCCC_OWNER_STANDARD,		\
+			   0x53)
+
+/*
+ * Return codes defined in ARM DEN 0070A
+ * ARM DEN 0070A is now merged/consolidated into ARM DEN 0028 C
+ */
+#define SMCCC_RET_SUCCESS			0
+#define SMCCC_RET_NOT_SUPPORTED			-1
+#define SMCCC_RET_NOT_REQUIRED			-2
+#define SMCCC_RET_INVALID_PARAMETER		-3
+
+#endif /*__LINUX_ARM_SMCCC_H*/
-- 
2.35.1.1094.g7c7d902a7c-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] 96+ messages in thread

* [PATCH v5 08/10] selftests: KVM: aarch64: Introduce hypercall ABI test
  2022-04-07  1:15 ` Raghavendra Rao Ananta
  (?)
@ 2022-04-07  1:16   ` Raghavendra Rao Ananta
  -1 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-07  1:16 UTC (permalink / raw)
  To: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose
  Cc: kvm, Catalin Marinas, Peter Shier, linux-kernel, Paolo Bonzini,
	Will Deacon, kvmarm, linux-arm-kernel

Introduce a KVM selftest to check the hypercall interface
for arm64 platforms. The test validates the user-space's
IOCTL interface to read/write the psuedo-firmware registers
as well as its effects on the guest upon certain configurations.

Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
---
 tools/testing/selftests/kvm/.gitignore        |   1 +
 tools/testing/selftests/kvm/Makefile          |   1 +
 .../selftests/kvm/aarch64/hypercalls.c        | 344 ++++++++++++++++++
 3 files changed, 346 insertions(+)
 create mode 100644 tools/testing/selftests/kvm/aarch64/hypercalls.c

diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore
index e82b816a6608..7ef52b3b1560 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/hypercalls
 /aarch64/psci_test
 /aarch64/vgic_init
 /aarch64/vgic_irq
diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
index 2f74f502de65..af4cb88bcf83 100644
--- a/tools/testing/selftests/kvm/Makefile
+++ b/tools/testing/selftests/kvm/Makefile
@@ -105,6 +105,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/hypercalls
 TEST_GEN_PROGS_aarch64 += aarch64/psci_test
 TEST_GEN_PROGS_aarch64 += aarch64/vgic_init
 TEST_GEN_PROGS_aarch64 += aarch64/vgic_irq
diff --git a/tools/testing/selftests/kvm/aarch64/hypercalls.c b/tools/testing/selftests/kvm/aarch64/hypercalls.c
new file mode 100644
index 000000000000..9941fb75772a
--- /dev/null
+++ b/tools/testing/selftests/kvm/aarch64/hypercalls.c
@@ -0,0 +1,344 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/* hypercalls: Check the ARM64's psuedo-firmware bitmap register interface.
+ *
+ * The test validates the basic hypercall functionalities that are exposed
+ * via the psuedo-firmware bitmap register. This includes the registers'
+ * read/write behavior before and after the VM has started, and if the
+ * hypercalls are properly masked or unmasked to the guest when disabled or
+ * enabled from the KVM userspace, respectively.
+ */
+
+#include <errno.h>
+#include <linux/arm-smccc.h>
+#include <asm/kvm.h>
+#include <kvm_util.h>
+
+#include "processor.h"
+
+#define FW_REG_ULIMIT_VAL(max_feat_bit) (GENMASK_ULL(max_feat_bit, 0))
+
+/* Last valid bits of the bitmapped firmware registers */
+#define KVM_REG_ARM_STD_BMAP_BIT_MAX		0
+#define KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX	0
+#define KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX	1
+
+struct kvm_fw_reg_info {
+	uint64_t reg;		/* Register definition */
+	uint64_t max_feat_bit;	/* Bit that represents the upper limit of the feature-map */
+};
+
+#define FW_REG_INFO(r, bit_max)			\
+	{					\
+		.reg = r,			\
+		.max_feat_bit = bit_max,	\
+	}
+
+static const struct kvm_fw_reg_info fw_reg_info[] = {
+	FW_REG_INFO(KVM_REG_ARM_STD_BMAP, KVM_REG_ARM_STD_BMAP_BIT_MAX),
+	FW_REG_INFO(KVM_REG_ARM_STD_HYP_BMAP, KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX),
+	FW_REG_INFO(KVM_REG_ARM_VENDOR_HYP_BMAP, KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX),
+};
+
+enum test_stage {
+	TEST_STAGE_REG_IFACE,
+	TEST_STAGE_HVC_IFACE_FEAT_DISABLED,
+	TEST_STAGE_HVC_IFACE_FEAT_ENABLED,
+	TEST_STAGE_HVC_IFACE_FALSE_INFO,
+	TEST_STAGE_END,
+};
+
+static int stage;
+
+struct test_hvc_info {
+	uint32_t func_id;
+	int64_t arg0;
+};
+
+#define TEST_HVC_INFO(f, a0)	\
+	{			\
+		.func_id = f,	\
+		.arg0 = a0,	\
+	}
+
+static const struct test_hvc_info hvc_info[] = {
+	/* KVM_REG_ARM_STD_BMAP */
+	TEST_HVC_INFO(ARM_SMCCC_TRNG_VERSION, 0),
+	TEST_HVC_INFO(ARM_SMCCC_TRNG_FEATURES, ARM_SMCCC_TRNG_RND64),
+	TEST_HVC_INFO(ARM_SMCCC_TRNG_GET_UUID, 0),
+	TEST_HVC_INFO(ARM_SMCCC_TRNG_RND32, 0),
+	TEST_HVC_INFO(ARM_SMCCC_TRNG_RND64, 0),
+
+	/* KVM_REG_ARM_STD_HYP_BMAP */
+	TEST_HVC_INFO(ARM_SMCCC_ARCH_FEATURES_FUNC_ID, ARM_SMCCC_HV_PV_TIME_FEATURES),
+	TEST_HVC_INFO(ARM_SMCCC_HV_PV_TIME_FEATURES, ARM_SMCCC_HV_PV_TIME_ST),
+	TEST_HVC_INFO(ARM_SMCCC_HV_PV_TIME_ST, 0),
+
+	/* KVM_REG_ARM_VENDOR_HYP_BMAP */
+	TEST_HVC_INFO(ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID,
+			ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID),
+	TEST_HVC_INFO(ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID, KVM_PTP_VIRT_COUNTER),
+};
+
+/* Feed false hypercall info to test the KVM behavior */
+static const struct test_hvc_info false_hvc_info[] = {
+	/* Feature support check against a different family of hypercalls */
+	TEST_HVC_INFO(ARM_SMCCC_TRNG_FEATURES, ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID),
+	TEST_HVC_INFO(ARM_SMCCC_ARCH_FEATURES_FUNC_ID, ARM_SMCCC_TRNG_RND64),
+	TEST_HVC_INFO(ARM_SMCCC_HV_PV_TIME_FEATURES, ARM_SMCCC_TRNG_RND64),
+};
+
+static void guest_test_hvc(const struct test_hvc_info *hc_info)
+{
+	unsigned int i;
+	struct arm_smccc_res res;
+	unsigned int hvc_info_arr_sz;
+
+	hvc_info_arr_sz =
+	hc_info == hvc_info ? ARRAY_SIZE(hvc_info) : ARRAY_SIZE(false_hvc_info);
+
+	for (i = 0; i < hvc_info_arr_sz; i++, hc_info++) {
+
+		memset(&res, 0, sizeof(res));
+		smccc_hvc(hc_info->func_id, hc_info->arg0, 0, 0, 0, 0, 0, 0, &res);
+
+		switch (stage) {
+		case TEST_STAGE_HVC_IFACE_FEAT_DISABLED:
+		case TEST_STAGE_HVC_IFACE_FALSE_INFO:
+			GUEST_ASSERT_3(res.a0 == SMCCC_RET_NOT_SUPPORTED,
+					res.a0, hc_info->func_id, hc_info->arg0);
+			break;
+		case TEST_STAGE_HVC_IFACE_FEAT_ENABLED:
+			GUEST_ASSERT_3(res.a0 != SMCCC_RET_NOT_SUPPORTED,
+					res.a0, hc_info->func_id, hc_info->arg0);
+			break;
+		default:
+			GUEST_ASSERT_1(0, stage);
+		}
+	}
+}
+
+static void guest_code(void)
+{
+	while (stage != TEST_STAGE_END) {
+		switch (stage) {
+		case TEST_STAGE_REG_IFACE:
+			break;
+		case TEST_STAGE_HVC_IFACE_FEAT_DISABLED:
+		case TEST_STAGE_HVC_IFACE_FEAT_ENABLED:
+			guest_test_hvc(hvc_info);
+			break;
+		case TEST_STAGE_HVC_IFACE_FALSE_INFO:
+			guest_test_hvc(false_hvc_info);
+			break;
+		default:
+			GUEST_ASSERT_1(0, stage);
+		}
+
+		GUEST_SYNC(stage);
+	}
+
+	GUEST_DONE();
+}
+
+static int set_fw_reg(struct kvm_vm *vm, uint64_t id, uint64_t val)
+{
+	struct kvm_one_reg reg = {
+		.id = id,
+		.addr = (uint64_t)&val,
+	};
+
+	return _vcpu_ioctl(vm, 0, KVM_SET_ONE_REG, &reg);
+}
+
+static void get_fw_reg(struct kvm_vm *vm, uint64_t id, uint64_t *addr)
+{
+	struct kvm_one_reg reg = {
+		.id = id,
+		.addr = (uint64_t)addr,
+	};
+
+	vcpu_ioctl(vm, 0, KVM_GET_ONE_REG, &reg);
+}
+
+struct st_time {
+	uint32_t rev;
+	uint32_t attr;
+	uint64_t st_time;
+};
+
+#define STEAL_TIME_SIZE		((sizeof(struct st_time) + 63) & ~63)
+#define ST_GPA_BASE		(1 << 30)
+
+static void steal_time_init(struct kvm_vm *vm)
+{
+	uint64_t st_ipa = (ulong)ST_GPA_BASE;
+	unsigned int gpages;
+	struct kvm_device_attr dev = {
+		.group = KVM_ARM_VCPU_PVTIME_CTRL,
+		.attr = KVM_ARM_VCPU_PVTIME_IPA,
+		.addr = (uint64_t)&st_ipa,
+	};
+
+	gpages = vm_calc_num_guest_pages(VM_MODE_DEFAULT, STEAL_TIME_SIZE);
+	vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, ST_GPA_BASE, 1, gpages, 0);
+
+	vcpu_ioctl(vm, 0, KVM_SET_DEVICE_ATTR, &dev);
+}
+
+static void test_fw_regs_before_vm_start(struct kvm_vm *vm)
+{
+	uint64_t val;
+	unsigned int i;
+	int ret;
+
+	for (i = 0; i < ARRAY_SIZE(fw_reg_info); i++) {
+		const struct kvm_fw_reg_info *reg_info = &fw_reg_info[i];
+
+		/* First 'read' should be an upper limit of the features supported */
+		get_fw_reg(vm, reg_info->reg, &val);
+		TEST_ASSERT(val == FW_REG_ULIMIT_VAL(reg_info->max_feat_bit),
+			"Expected all the features to be set for reg: 0x%lx; expected: 0x%llx; read: 0x%lx\n",
+			reg_info->reg, GENMASK_ULL(reg_info->max_feat_bit, 0), val);
+
+		/* Test a 'write' by disabling all the features of the register map */
+		ret = set_fw_reg(vm, reg_info->reg, 0);
+		TEST_ASSERT(ret == 0,
+			"Failed to clear all the features of reg: 0x%lx; ret: %d\n",
+			reg_info->reg, errno);
+
+		get_fw_reg(vm, reg_info->reg, &val);
+		TEST_ASSERT(val == 0,
+			"Expected all the features to be cleared for reg: 0x%lx\n", reg_info->reg);
+
+		/*
+		 * Test enabling a feature that's not supported.
+		 * Avoid this check if all the bits are occupied.
+		 */
+		if (reg_info->max_feat_bit < 63) {
+			ret = set_fw_reg(vm, reg_info->reg, BIT(reg_info->max_feat_bit + 1));
+			TEST_ASSERT(ret != 0 && errno == EINVAL,
+			"Unexpected behavior or return value (%d) while setting an unsupported feature for reg: 0x%lx\n",
+			errno, reg_info->reg);
+		}
+	}
+}
+
+static void test_fw_regs_after_vm_start(struct kvm_vm *vm)
+{
+	uint64_t val;
+	unsigned int i;
+	int ret;
+
+	for (i = 0; i < ARRAY_SIZE(fw_reg_info); i++) {
+		const struct kvm_fw_reg_info *reg_info = &fw_reg_info[i];
+
+		/*
+		 * Before starting the VM, the test clears all the bits.
+		 * Check if that's still the case.
+		 */
+		get_fw_reg(vm, reg_info->reg, &val);
+		TEST_ASSERT(val == 0,
+			"Expected all the features to be cleared for reg: 0x%lx\n",
+			reg_info->reg);
+
+		/*
+		 * Test setting the last read value. KVM should allow this
+		 * even if VM has started running.
+		 */
+		ret = set_fw_reg(vm, reg_info->reg, val);
+		TEST_ASSERT(ret == 0,
+			"Failed to set the register with previously read value after Vm start for reg: 0x%lx; ret: %d\n",
+			reg_info->reg, errno);
+
+		/*
+		 * Set all the features for this register again. KVM shouldn't
+		 * allow this as the VM is running.
+		 */
+		ret = set_fw_reg(vm, reg_info->reg, FW_REG_ULIMIT_VAL(reg_info->max_feat_bit));
+		TEST_ASSERT(ret != 0 && errno == EBUSY,
+		"Unexpected behavior or return value (%d) while setting a feature while VM is running for reg: 0x%lx\n",
+		errno, reg_info->reg);
+	}
+}
+
+static struct kvm_vm *test_vm_create(void)
+{
+	struct kvm_vm *vm;
+
+	vm = vm_create_default(0, 0, guest_code);
+
+	ucall_init(vm, NULL);
+	steal_time_init(vm);
+
+	return vm;
+}
+
+static struct kvm_vm *test_guest_stage(struct kvm_vm *vm)
+{
+	struct kvm_vm *ret_vm = vm;
+
+	pr_debug("Stage: %d\n", stage);
+
+	switch (stage) {
+	case TEST_STAGE_REG_IFACE:
+		test_fw_regs_after_vm_start(vm);
+		break;
+	case TEST_STAGE_HVC_IFACE_FEAT_DISABLED:
+		/* Start a new VM so that all the features are now enabled by default */
+		kvm_vm_free(vm);
+		ret_vm = test_vm_create();
+		break;
+	case TEST_STAGE_HVC_IFACE_FEAT_ENABLED:
+	case TEST_STAGE_HVC_IFACE_FALSE_INFO:
+		break;
+	default:
+		TEST_FAIL("Unknown test stage: %d\n", stage);
+	}
+
+	stage++;
+	sync_global_to_guest(vm, stage);
+
+	return ret_vm;
+}
+
+static void test_run(void)
+{
+	struct kvm_vm *vm;
+	struct ucall uc;
+	bool guest_done = false;
+
+	vm = test_vm_create();
+
+	test_fw_regs_before_vm_start(vm);
+
+	while (!guest_done) {
+		vcpu_run(vm, 0);
+
+		switch (get_ucall(vm, 0, &uc)) {
+		case UCALL_SYNC:
+			vm = test_guest_stage(vm);
+			break;
+		case UCALL_DONE:
+			guest_done = true;
+			break;
+		case UCALL_ABORT:
+			TEST_FAIL("%s at %s:%ld\n\tvalues: 0x%lx, 0x%lx; 0x%lx, stage: %u",
+			(const char *)uc.args[0], __FILE__, uc.args[1],
+			uc.args[2], uc.args[3], uc.args[4], stage);
+			break;
+		default:
+			TEST_FAIL("Unexpected guest exit\n");
+		}
+	}
+
+	kvm_vm_free(vm);
+}
+
+int main(void)
+{
+	setbuf(stdout, NULL);
+
+	test_run();
+	return 0;
+}
-- 
2.35.1.1094.g7c7d902a7c-goog

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

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

* [PATCH v5 08/10] selftests: KVM: aarch64: Introduce hypercall ABI test
@ 2022-04-07  1:16   ` Raghavendra Rao Ananta
  0 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-07  1:16 UTC (permalink / raw)
  To: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose
  Cc: Paolo Bonzini, Catalin Marinas, Will Deacon, Peter Shier,
	Ricardo Koller, Oliver Upton, Reiji Watanabe, Jing Zhang,
	Raghavendra Rao Anata, linux-arm-kernel, kvmarm, linux-kernel,
	kvm

Introduce a KVM selftest to check the hypercall interface
for arm64 platforms. The test validates the user-space's
IOCTL interface to read/write the psuedo-firmware registers
as well as its effects on the guest upon certain configurations.

Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
---
 tools/testing/selftests/kvm/.gitignore        |   1 +
 tools/testing/selftests/kvm/Makefile          |   1 +
 .../selftests/kvm/aarch64/hypercalls.c        | 344 ++++++++++++++++++
 3 files changed, 346 insertions(+)
 create mode 100644 tools/testing/selftests/kvm/aarch64/hypercalls.c

diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore
index e82b816a6608..7ef52b3b1560 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/hypercalls
 /aarch64/psci_test
 /aarch64/vgic_init
 /aarch64/vgic_irq
diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
index 2f74f502de65..af4cb88bcf83 100644
--- a/tools/testing/selftests/kvm/Makefile
+++ b/tools/testing/selftests/kvm/Makefile
@@ -105,6 +105,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/hypercalls
 TEST_GEN_PROGS_aarch64 += aarch64/psci_test
 TEST_GEN_PROGS_aarch64 += aarch64/vgic_init
 TEST_GEN_PROGS_aarch64 += aarch64/vgic_irq
diff --git a/tools/testing/selftests/kvm/aarch64/hypercalls.c b/tools/testing/selftests/kvm/aarch64/hypercalls.c
new file mode 100644
index 000000000000..9941fb75772a
--- /dev/null
+++ b/tools/testing/selftests/kvm/aarch64/hypercalls.c
@@ -0,0 +1,344 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/* hypercalls: Check the ARM64's psuedo-firmware bitmap register interface.
+ *
+ * The test validates the basic hypercall functionalities that are exposed
+ * via the psuedo-firmware bitmap register. This includes the registers'
+ * read/write behavior before and after the VM has started, and if the
+ * hypercalls are properly masked or unmasked to the guest when disabled or
+ * enabled from the KVM userspace, respectively.
+ */
+
+#include <errno.h>
+#include <linux/arm-smccc.h>
+#include <asm/kvm.h>
+#include <kvm_util.h>
+
+#include "processor.h"
+
+#define FW_REG_ULIMIT_VAL(max_feat_bit) (GENMASK_ULL(max_feat_bit, 0))
+
+/* Last valid bits of the bitmapped firmware registers */
+#define KVM_REG_ARM_STD_BMAP_BIT_MAX		0
+#define KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX	0
+#define KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX	1
+
+struct kvm_fw_reg_info {
+	uint64_t reg;		/* Register definition */
+	uint64_t max_feat_bit;	/* Bit that represents the upper limit of the feature-map */
+};
+
+#define FW_REG_INFO(r, bit_max)			\
+	{					\
+		.reg = r,			\
+		.max_feat_bit = bit_max,	\
+	}
+
+static const struct kvm_fw_reg_info fw_reg_info[] = {
+	FW_REG_INFO(KVM_REG_ARM_STD_BMAP, KVM_REG_ARM_STD_BMAP_BIT_MAX),
+	FW_REG_INFO(KVM_REG_ARM_STD_HYP_BMAP, KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX),
+	FW_REG_INFO(KVM_REG_ARM_VENDOR_HYP_BMAP, KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX),
+};
+
+enum test_stage {
+	TEST_STAGE_REG_IFACE,
+	TEST_STAGE_HVC_IFACE_FEAT_DISABLED,
+	TEST_STAGE_HVC_IFACE_FEAT_ENABLED,
+	TEST_STAGE_HVC_IFACE_FALSE_INFO,
+	TEST_STAGE_END,
+};
+
+static int stage;
+
+struct test_hvc_info {
+	uint32_t func_id;
+	int64_t arg0;
+};
+
+#define TEST_HVC_INFO(f, a0)	\
+	{			\
+		.func_id = f,	\
+		.arg0 = a0,	\
+	}
+
+static const struct test_hvc_info hvc_info[] = {
+	/* KVM_REG_ARM_STD_BMAP */
+	TEST_HVC_INFO(ARM_SMCCC_TRNG_VERSION, 0),
+	TEST_HVC_INFO(ARM_SMCCC_TRNG_FEATURES, ARM_SMCCC_TRNG_RND64),
+	TEST_HVC_INFO(ARM_SMCCC_TRNG_GET_UUID, 0),
+	TEST_HVC_INFO(ARM_SMCCC_TRNG_RND32, 0),
+	TEST_HVC_INFO(ARM_SMCCC_TRNG_RND64, 0),
+
+	/* KVM_REG_ARM_STD_HYP_BMAP */
+	TEST_HVC_INFO(ARM_SMCCC_ARCH_FEATURES_FUNC_ID, ARM_SMCCC_HV_PV_TIME_FEATURES),
+	TEST_HVC_INFO(ARM_SMCCC_HV_PV_TIME_FEATURES, ARM_SMCCC_HV_PV_TIME_ST),
+	TEST_HVC_INFO(ARM_SMCCC_HV_PV_TIME_ST, 0),
+
+	/* KVM_REG_ARM_VENDOR_HYP_BMAP */
+	TEST_HVC_INFO(ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID,
+			ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID),
+	TEST_HVC_INFO(ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID, KVM_PTP_VIRT_COUNTER),
+};
+
+/* Feed false hypercall info to test the KVM behavior */
+static const struct test_hvc_info false_hvc_info[] = {
+	/* Feature support check against a different family of hypercalls */
+	TEST_HVC_INFO(ARM_SMCCC_TRNG_FEATURES, ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID),
+	TEST_HVC_INFO(ARM_SMCCC_ARCH_FEATURES_FUNC_ID, ARM_SMCCC_TRNG_RND64),
+	TEST_HVC_INFO(ARM_SMCCC_HV_PV_TIME_FEATURES, ARM_SMCCC_TRNG_RND64),
+};
+
+static void guest_test_hvc(const struct test_hvc_info *hc_info)
+{
+	unsigned int i;
+	struct arm_smccc_res res;
+	unsigned int hvc_info_arr_sz;
+
+	hvc_info_arr_sz =
+	hc_info == hvc_info ? ARRAY_SIZE(hvc_info) : ARRAY_SIZE(false_hvc_info);
+
+	for (i = 0; i < hvc_info_arr_sz; i++, hc_info++) {
+
+		memset(&res, 0, sizeof(res));
+		smccc_hvc(hc_info->func_id, hc_info->arg0, 0, 0, 0, 0, 0, 0, &res);
+
+		switch (stage) {
+		case TEST_STAGE_HVC_IFACE_FEAT_DISABLED:
+		case TEST_STAGE_HVC_IFACE_FALSE_INFO:
+			GUEST_ASSERT_3(res.a0 == SMCCC_RET_NOT_SUPPORTED,
+					res.a0, hc_info->func_id, hc_info->arg0);
+			break;
+		case TEST_STAGE_HVC_IFACE_FEAT_ENABLED:
+			GUEST_ASSERT_3(res.a0 != SMCCC_RET_NOT_SUPPORTED,
+					res.a0, hc_info->func_id, hc_info->arg0);
+			break;
+		default:
+			GUEST_ASSERT_1(0, stage);
+		}
+	}
+}
+
+static void guest_code(void)
+{
+	while (stage != TEST_STAGE_END) {
+		switch (stage) {
+		case TEST_STAGE_REG_IFACE:
+			break;
+		case TEST_STAGE_HVC_IFACE_FEAT_DISABLED:
+		case TEST_STAGE_HVC_IFACE_FEAT_ENABLED:
+			guest_test_hvc(hvc_info);
+			break;
+		case TEST_STAGE_HVC_IFACE_FALSE_INFO:
+			guest_test_hvc(false_hvc_info);
+			break;
+		default:
+			GUEST_ASSERT_1(0, stage);
+		}
+
+		GUEST_SYNC(stage);
+	}
+
+	GUEST_DONE();
+}
+
+static int set_fw_reg(struct kvm_vm *vm, uint64_t id, uint64_t val)
+{
+	struct kvm_one_reg reg = {
+		.id = id,
+		.addr = (uint64_t)&val,
+	};
+
+	return _vcpu_ioctl(vm, 0, KVM_SET_ONE_REG, &reg);
+}
+
+static void get_fw_reg(struct kvm_vm *vm, uint64_t id, uint64_t *addr)
+{
+	struct kvm_one_reg reg = {
+		.id = id,
+		.addr = (uint64_t)addr,
+	};
+
+	vcpu_ioctl(vm, 0, KVM_GET_ONE_REG, &reg);
+}
+
+struct st_time {
+	uint32_t rev;
+	uint32_t attr;
+	uint64_t st_time;
+};
+
+#define STEAL_TIME_SIZE		((sizeof(struct st_time) + 63) & ~63)
+#define ST_GPA_BASE		(1 << 30)
+
+static void steal_time_init(struct kvm_vm *vm)
+{
+	uint64_t st_ipa = (ulong)ST_GPA_BASE;
+	unsigned int gpages;
+	struct kvm_device_attr dev = {
+		.group = KVM_ARM_VCPU_PVTIME_CTRL,
+		.attr = KVM_ARM_VCPU_PVTIME_IPA,
+		.addr = (uint64_t)&st_ipa,
+	};
+
+	gpages = vm_calc_num_guest_pages(VM_MODE_DEFAULT, STEAL_TIME_SIZE);
+	vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, ST_GPA_BASE, 1, gpages, 0);
+
+	vcpu_ioctl(vm, 0, KVM_SET_DEVICE_ATTR, &dev);
+}
+
+static void test_fw_regs_before_vm_start(struct kvm_vm *vm)
+{
+	uint64_t val;
+	unsigned int i;
+	int ret;
+
+	for (i = 0; i < ARRAY_SIZE(fw_reg_info); i++) {
+		const struct kvm_fw_reg_info *reg_info = &fw_reg_info[i];
+
+		/* First 'read' should be an upper limit of the features supported */
+		get_fw_reg(vm, reg_info->reg, &val);
+		TEST_ASSERT(val == FW_REG_ULIMIT_VAL(reg_info->max_feat_bit),
+			"Expected all the features to be set for reg: 0x%lx; expected: 0x%llx; read: 0x%lx\n",
+			reg_info->reg, GENMASK_ULL(reg_info->max_feat_bit, 0), val);
+
+		/* Test a 'write' by disabling all the features of the register map */
+		ret = set_fw_reg(vm, reg_info->reg, 0);
+		TEST_ASSERT(ret == 0,
+			"Failed to clear all the features of reg: 0x%lx; ret: %d\n",
+			reg_info->reg, errno);
+
+		get_fw_reg(vm, reg_info->reg, &val);
+		TEST_ASSERT(val == 0,
+			"Expected all the features to be cleared for reg: 0x%lx\n", reg_info->reg);
+
+		/*
+		 * Test enabling a feature that's not supported.
+		 * Avoid this check if all the bits are occupied.
+		 */
+		if (reg_info->max_feat_bit < 63) {
+			ret = set_fw_reg(vm, reg_info->reg, BIT(reg_info->max_feat_bit + 1));
+			TEST_ASSERT(ret != 0 && errno == EINVAL,
+			"Unexpected behavior or return value (%d) while setting an unsupported feature for reg: 0x%lx\n",
+			errno, reg_info->reg);
+		}
+	}
+}
+
+static void test_fw_regs_after_vm_start(struct kvm_vm *vm)
+{
+	uint64_t val;
+	unsigned int i;
+	int ret;
+
+	for (i = 0; i < ARRAY_SIZE(fw_reg_info); i++) {
+		const struct kvm_fw_reg_info *reg_info = &fw_reg_info[i];
+
+		/*
+		 * Before starting the VM, the test clears all the bits.
+		 * Check if that's still the case.
+		 */
+		get_fw_reg(vm, reg_info->reg, &val);
+		TEST_ASSERT(val == 0,
+			"Expected all the features to be cleared for reg: 0x%lx\n",
+			reg_info->reg);
+
+		/*
+		 * Test setting the last read value. KVM should allow this
+		 * even if VM has started running.
+		 */
+		ret = set_fw_reg(vm, reg_info->reg, val);
+		TEST_ASSERT(ret == 0,
+			"Failed to set the register with previously read value after Vm start for reg: 0x%lx; ret: %d\n",
+			reg_info->reg, errno);
+
+		/*
+		 * Set all the features for this register again. KVM shouldn't
+		 * allow this as the VM is running.
+		 */
+		ret = set_fw_reg(vm, reg_info->reg, FW_REG_ULIMIT_VAL(reg_info->max_feat_bit));
+		TEST_ASSERT(ret != 0 && errno == EBUSY,
+		"Unexpected behavior or return value (%d) while setting a feature while VM is running for reg: 0x%lx\n",
+		errno, reg_info->reg);
+	}
+}
+
+static struct kvm_vm *test_vm_create(void)
+{
+	struct kvm_vm *vm;
+
+	vm = vm_create_default(0, 0, guest_code);
+
+	ucall_init(vm, NULL);
+	steal_time_init(vm);
+
+	return vm;
+}
+
+static struct kvm_vm *test_guest_stage(struct kvm_vm *vm)
+{
+	struct kvm_vm *ret_vm = vm;
+
+	pr_debug("Stage: %d\n", stage);
+
+	switch (stage) {
+	case TEST_STAGE_REG_IFACE:
+		test_fw_regs_after_vm_start(vm);
+		break;
+	case TEST_STAGE_HVC_IFACE_FEAT_DISABLED:
+		/* Start a new VM so that all the features are now enabled by default */
+		kvm_vm_free(vm);
+		ret_vm = test_vm_create();
+		break;
+	case TEST_STAGE_HVC_IFACE_FEAT_ENABLED:
+	case TEST_STAGE_HVC_IFACE_FALSE_INFO:
+		break;
+	default:
+		TEST_FAIL("Unknown test stage: %d\n", stage);
+	}
+
+	stage++;
+	sync_global_to_guest(vm, stage);
+
+	return ret_vm;
+}
+
+static void test_run(void)
+{
+	struct kvm_vm *vm;
+	struct ucall uc;
+	bool guest_done = false;
+
+	vm = test_vm_create();
+
+	test_fw_regs_before_vm_start(vm);
+
+	while (!guest_done) {
+		vcpu_run(vm, 0);
+
+		switch (get_ucall(vm, 0, &uc)) {
+		case UCALL_SYNC:
+			vm = test_guest_stage(vm);
+			break;
+		case UCALL_DONE:
+			guest_done = true;
+			break;
+		case UCALL_ABORT:
+			TEST_FAIL("%s at %s:%ld\n\tvalues: 0x%lx, 0x%lx; 0x%lx, stage: %u",
+			(const char *)uc.args[0], __FILE__, uc.args[1],
+			uc.args[2], uc.args[3], uc.args[4], stage);
+			break;
+		default:
+			TEST_FAIL("Unexpected guest exit\n");
+		}
+	}
+
+	kvm_vm_free(vm);
+}
+
+int main(void)
+{
+	setbuf(stdout, NULL);
+
+	test_run();
+	return 0;
+}
-- 
2.35.1.1094.g7c7d902a7c-goog


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

* [PATCH v5 08/10] selftests: KVM: aarch64: Introduce hypercall ABI test
@ 2022-04-07  1:16   ` Raghavendra Rao Ananta
  0 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-07  1:16 UTC (permalink / raw)
  To: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose
  Cc: Paolo Bonzini, Catalin Marinas, Will Deacon, Peter Shier,
	Ricardo Koller, Oliver Upton, Reiji Watanabe, Jing Zhang,
	Raghavendra Rao Anata, linux-arm-kernel, kvmarm, linux-kernel,
	kvm

Introduce a KVM selftest to check the hypercall interface
for arm64 platforms. The test validates the user-space's
IOCTL interface to read/write the psuedo-firmware registers
as well as its effects on the guest upon certain configurations.

Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
---
 tools/testing/selftests/kvm/.gitignore        |   1 +
 tools/testing/selftests/kvm/Makefile          |   1 +
 .../selftests/kvm/aarch64/hypercalls.c        | 344 ++++++++++++++++++
 3 files changed, 346 insertions(+)
 create mode 100644 tools/testing/selftests/kvm/aarch64/hypercalls.c

diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore
index e82b816a6608..7ef52b3b1560 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/hypercalls
 /aarch64/psci_test
 /aarch64/vgic_init
 /aarch64/vgic_irq
diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
index 2f74f502de65..af4cb88bcf83 100644
--- a/tools/testing/selftests/kvm/Makefile
+++ b/tools/testing/selftests/kvm/Makefile
@@ -105,6 +105,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/hypercalls
 TEST_GEN_PROGS_aarch64 += aarch64/psci_test
 TEST_GEN_PROGS_aarch64 += aarch64/vgic_init
 TEST_GEN_PROGS_aarch64 += aarch64/vgic_irq
diff --git a/tools/testing/selftests/kvm/aarch64/hypercalls.c b/tools/testing/selftests/kvm/aarch64/hypercalls.c
new file mode 100644
index 000000000000..9941fb75772a
--- /dev/null
+++ b/tools/testing/selftests/kvm/aarch64/hypercalls.c
@@ -0,0 +1,344 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/* hypercalls: Check the ARM64's psuedo-firmware bitmap register interface.
+ *
+ * The test validates the basic hypercall functionalities that are exposed
+ * via the psuedo-firmware bitmap register. This includes the registers'
+ * read/write behavior before and after the VM has started, and if the
+ * hypercalls are properly masked or unmasked to the guest when disabled or
+ * enabled from the KVM userspace, respectively.
+ */
+
+#include <errno.h>
+#include <linux/arm-smccc.h>
+#include <asm/kvm.h>
+#include <kvm_util.h>
+
+#include "processor.h"
+
+#define FW_REG_ULIMIT_VAL(max_feat_bit) (GENMASK_ULL(max_feat_bit, 0))
+
+/* Last valid bits of the bitmapped firmware registers */
+#define KVM_REG_ARM_STD_BMAP_BIT_MAX		0
+#define KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX	0
+#define KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX	1
+
+struct kvm_fw_reg_info {
+	uint64_t reg;		/* Register definition */
+	uint64_t max_feat_bit;	/* Bit that represents the upper limit of the feature-map */
+};
+
+#define FW_REG_INFO(r, bit_max)			\
+	{					\
+		.reg = r,			\
+		.max_feat_bit = bit_max,	\
+	}
+
+static const struct kvm_fw_reg_info fw_reg_info[] = {
+	FW_REG_INFO(KVM_REG_ARM_STD_BMAP, KVM_REG_ARM_STD_BMAP_BIT_MAX),
+	FW_REG_INFO(KVM_REG_ARM_STD_HYP_BMAP, KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX),
+	FW_REG_INFO(KVM_REG_ARM_VENDOR_HYP_BMAP, KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX),
+};
+
+enum test_stage {
+	TEST_STAGE_REG_IFACE,
+	TEST_STAGE_HVC_IFACE_FEAT_DISABLED,
+	TEST_STAGE_HVC_IFACE_FEAT_ENABLED,
+	TEST_STAGE_HVC_IFACE_FALSE_INFO,
+	TEST_STAGE_END,
+};
+
+static int stage;
+
+struct test_hvc_info {
+	uint32_t func_id;
+	int64_t arg0;
+};
+
+#define TEST_HVC_INFO(f, a0)	\
+	{			\
+		.func_id = f,	\
+		.arg0 = a0,	\
+	}
+
+static const struct test_hvc_info hvc_info[] = {
+	/* KVM_REG_ARM_STD_BMAP */
+	TEST_HVC_INFO(ARM_SMCCC_TRNG_VERSION, 0),
+	TEST_HVC_INFO(ARM_SMCCC_TRNG_FEATURES, ARM_SMCCC_TRNG_RND64),
+	TEST_HVC_INFO(ARM_SMCCC_TRNG_GET_UUID, 0),
+	TEST_HVC_INFO(ARM_SMCCC_TRNG_RND32, 0),
+	TEST_HVC_INFO(ARM_SMCCC_TRNG_RND64, 0),
+
+	/* KVM_REG_ARM_STD_HYP_BMAP */
+	TEST_HVC_INFO(ARM_SMCCC_ARCH_FEATURES_FUNC_ID, ARM_SMCCC_HV_PV_TIME_FEATURES),
+	TEST_HVC_INFO(ARM_SMCCC_HV_PV_TIME_FEATURES, ARM_SMCCC_HV_PV_TIME_ST),
+	TEST_HVC_INFO(ARM_SMCCC_HV_PV_TIME_ST, 0),
+
+	/* KVM_REG_ARM_VENDOR_HYP_BMAP */
+	TEST_HVC_INFO(ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID,
+			ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID),
+	TEST_HVC_INFO(ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID, KVM_PTP_VIRT_COUNTER),
+};
+
+/* Feed false hypercall info to test the KVM behavior */
+static const struct test_hvc_info false_hvc_info[] = {
+	/* Feature support check against a different family of hypercalls */
+	TEST_HVC_INFO(ARM_SMCCC_TRNG_FEATURES, ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID),
+	TEST_HVC_INFO(ARM_SMCCC_ARCH_FEATURES_FUNC_ID, ARM_SMCCC_TRNG_RND64),
+	TEST_HVC_INFO(ARM_SMCCC_HV_PV_TIME_FEATURES, ARM_SMCCC_TRNG_RND64),
+};
+
+static void guest_test_hvc(const struct test_hvc_info *hc_info)
+{
+	unsigned int i;
+	struct arm_smccc_res res;
+	unsigned int hvc_info_arr_sz;
+
+	hvc_info_arr_sz =
+	hc_info == hvc_info ? ARRAY_SIZE(hvc_info) : ARRAY_SIZE(false_hvc_info);
+
+	for (i = 0; i < hvc_info_arr_sz; i++, hc_info++) {
+
+		memset(&res, 0, sizeof(res));
+		smccc_hvc(hc_info->func_id, hc_info->arg0, 0, 0, 0, 0, 0, 0, &res);
+
+		switch (stage) {
+		case TEST_STAGE_HVC_IFACE_FEAT_DISABLED:
+		case TEST_STAGE_HVC_IFACE_FALSE_INFO:
+			GUEST_ASSERT_3(res.a0 == SMCCC_RET_NOT_SUPPORTED,
+					res.a0, hc_info->func_id, hc_info->arg0);
+			break;
+		case TEST_STAGE_HVC_IFACE_FEAT_ENABLED:
+			GUEST_ASSERT_3(res.a0 != SMCCC_RET_NOT_SUPPORTED,
+					res.a0, hc_info->func_id, hc_info->arg0);
+			break;
+		default:
+			GUEST_ASSERT_1(0, stage);
+		}
+	}
+}
+
+static void guest_code(void)
+{
+	while (stage != TEST_STAGE_END) {
+		switch (stage) {
+		case TEST_STAGE_REG_IFACE:
+			break;
+		case TEST_STAGE_HVC_IFACE_FEAT_DISABLED:
+		case TEST_STAGE_HVC_IFACE_FEAT_ENABLED:
+			guest_test_hvc(hvc_info);
+			break;
+		case TEST_STAGE_HVC_IFACE_FALSE_INFO:
+			guest_test_hvc(false_hvc_info);
+			break;
+		default:
+			GUEST_ASSERT_1(0, stage);
+		}
+
+		GUEST_SYNC(stage);
+	}
+
+	GUEST_DONE();
+}
+
+static int set_fw_reg(struct kvm_vm *vm, uint64_t id, uint64_t val)
+{
+	struct kvm_one_reg reg = {
+		.id = id,
+		.addr = (uint64_t)&val,
+	};
+
+	return _vcpu_ioctl(vm, 0, KVM_SET_ONE_REG, &reg);
+}
+
+static void get_fw_reg(struct kvm_vm *vm, uint64_t id, uint64_t *addr)
+{
+	struct kvm_one_reg reg = {
+		.id = id,
+		.addr = (uint64_t)addr,
+	};
+
+	vcpu_ioctl(vm, 0, KVM_GET_ONE_REG, &reg);
+}
+
+struct st_time {
+	uint32_t rev;
+	uint32_t attr;
+	uint64_t st_time;
+};
+
+#define STEAL_TIME_SIZE		((sizeof(struct st_time) + 63) & ~63)
+#define ST_GPA_BASE		(1 << 30)
+
+static void steal_time_init(struct kvm_vm *vm)
+{
+	uint64_t st_ipa = (ulong)ST_GPA_BASE;
+	unsigned int gpages;
+	struct kvm_device_attr dev = {
+		.group = KVM_ARM_VCPU_PVTIME_CTRL,
+		.attr = KVM_ARM_VCPU_PVTIME_IPA,
+		.addr = (uint64_t)&st_ipa,
+	};
+
+	gpages = vm_calc_num_guest_pages(VM_MODE_DEFAULT, STEAL_TIME_SIZE);
+	vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, ST_GPA_BASE, 1, gpages, 0);
+
+	vcpu_ioctl(vm, 0, KVM_SET_DEVICE_ATTR, &dev);
+}
+
+static void test_fw_regs_before_vm_start(struct kvm_vm *vm)
+{
+	uint64_t val;
+	unsigned int i;
+	int ret;
+
+	for (i = 0; i < ARRAY_SIZE(fw_reg_info); i++) {
+		const struct kvm_fw_reg_info *reg_info = &fw_reg_info[i];
+
+		/* First 'read' should be an upper limit of the features supported */
+		get_fw_reg(vm, reg_info->reg, &val);
+		TEST_ASSERT(val == FW_REG_ULIMIT_VAL(reg_info->max_feat_bit),
+			"Expected all the features to be set for reg: 0x%lx; expected: 0x%llx; read: 0x%lx\n",
+			reg_info->reg, GENMASK_ULL(reg_info->max_feat_bit, 0), val);
+
+		/* Test a 'write' by disabling all the features of the register map */
+		ret = set_fw_reg(vm, reg_info->reg, 0);
+		TEST_ASSERT(ret == 0,
+			"Failed to clear all the features of reg: 0x%lx; ret: %d\n",
+			reg_info->reg, errno);
+
+		get_fw_reg(vm, reg_info->reg, &val);
+		TEST_ASSERT(val == 0,
+			"Expected all the features to be cleared for reg: 0x%lx\n", reg_info->reg);
+
+		/*
+		 * Test enabling a feature that's not supported.
+		 * Avoid this check if all the bits are occupied.
+		 */
+		if (reg_info->max_feat_bit < 63) {
+			ret = set_fw_reg(vm, reg_info->reg, BIT(reg_info->max_feat_bit + 1));
+			TEST_ASSERT(ret != 0 && errno == EINVAL,
+			"Unexpected behavior or return value (%d) while setting an unsupported feature for reg: 0x%lx\n",
+			errno, reg_info->reg);
+		}
+	}
+}
+
+static void test_fw_regs_after_vm_start(struct kvm_vm *vm)
+{
+	uint64_t val;
+	unsigned int i;
+	int ret;
+
+	for (i = 0; i < ARRAY_SIZE(fw_reg_info); i++) {
+		const struct kvm_fw_reg_info *reg_info = &fw_reg_info[i];
+
+		/*
+		 * Before starting the VM, the test clears all the bits.
+		 * Check if that's still the case.
+		 */
+		get_fw_reg(vm, reg_info->reg, &val);
+		TEST_ASSERT(val == 0,
+			"Expected all the features to be cleared for reg: 0x%lx\n",
+			reg_info->reg);
+
+		/*
+		 * Test setting the last read value. KVM should allow this
+		 * even if VM has started running.
+		 */
+		ret = set_fw_reg(vm, reg_info->reg, val);
+		TEST_ASSERT(ret == 0,
+			"Failed to set the register with previously read value after Vm start for reg: 0x%lx; ret: %d\n",
+			reg_info->reg, errno);
+
+		/*
+		 * Set all the features for this register again. KVM shouldn't
+		 * allow this as the VM is running.
+		 */
+		ret = set_fw_reg(vm, reg_info->reg, FW_REG_ULIMIT_VAL(reg_info->max_feat_bit));
+		TEST_ASSERT(ret != 0 && errno == EBUSY,
+		"Unexpected behavior or return value (%d) while setting a feature while VM is running for reg: 0x%lx\n",
+		errno, reg_info->reg);
+	}
+}
+
+static struct kvm_vm *test_vm_create(void)
+{
+	struct kvm_vm *vm;
+
+	vm = vm_create_default(0, 0, guest_code);
+
+	ucall_init(vm, NULL);
+	steal_time_init(vm);
+
+	return vm;
+}
+
+static struct kvm_vm *test_guest_stage(struct kvm_vm *vm)
+{
+	struct kvm_vm *ret_vm = vm;
+
+	pr_debug("Stage: %d\n", stage);
+
+	switch (stage) {
+	case TEST_STAGE_REG_IFACE:
+		test_fw_regs_after_vm_start(vm);
+		break;
+	case TEST_STAGE_HVC_IFACE_FEAT_DISABLED:
+		/* Start a new VM so that all the features are now enabled by default */
+		kvm_vm_free(vm);
+		ret_vm = test_vm_create();
+		break;
+	case TEST_STAGE_HVC_IFACE_FEAT_ENABLED:
+	case TEST_STAGE_HVC_IFACE_FALSE_INFO:
+		break;
+	default:
+		TEST_FAIL("Unknown test stage: %d\n", stage);
+	}
+
+	stage++;
+	sync_global_to_guest(vm, stage);
+
+	return ret_vm;
+}
+
+static void test_run(void)
+{
+	struct kvm_vm *vm;
+	struct ucall uc;
+	bool guest_done = false;
+
+	vm = test_vm_create();
+
+	test_fw_regs_before_vm_start(vm);
+
+	while (!guest_done) {
+		vcpu_run(vm, 0);
+
+		switch (get_ucall(vm, 0, &uc)) {
+		case UCALL_SYNC:
+			vm = test_guest_stage(vm);
+			break;
+		case UCALL_DONE:
+			guest_done = true;
+			break;
+		case UCALL_ABORT:
+			TEST_FAIL("%s at %s:%ld\n\tvalues: 0x%lx, 0x%lx; 0x%lx, stage: %u",
+			(const char *)uc.args[0], __FILE__, uc.args[1],
+			uc.args[2], uc.args[3], uc.args[4], stage);
+			break;
+		default:
+			TEST_FAIL("Unexpected guest exit\n");
+		}
+	}
+
+	kvm_vm_free(vm);
+}
+
+int main(void)
+{
+	setbuf(stdout, NULL);
+
+	test_run();
+	return 0;
+}
-- 
2.35.1.1094.g7c7d902a7c-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] 96+ messages in thread

* [PATCH v5 09/10] selftests: KVM: aarch64: Add the bitmap firmware registers to get-reg-list
  2022-04-07  1:15 ` Raghavendra Rao Ananta
  (?)
@ 2022-04-07  1:16   ` Raghavendra Rao Ananta
  -1 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-07  1:16 UTC (permalink / raw)
  To: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose
  Cc: kvm, Catalin Marinas, Peter Shier, linux-kernel, Paolo Bonzini,
	Will Deacon, kvmarm, linux-arm-kernel

Add the psuedo-firmware registers KVM_REG_ARM_STD_BMAP,
KVM_REG_ARM_STD_HYP_BMAP, and KVM_REG_ARM_VENDOR_HYP_BMAP to
the base_regs[] list.

Also, add the COPROC support for KVM_REG_ARM_FW_FEAT_BMAP.

Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
---
 tools/testing/selftests/kvm/aarch64/get-reg-list.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/tools/testing/selftests/kvm/aarch64/get-reg-list.c b/tools/testing/selftests/kvm/aarch64/get-reg-list.c
index f12147c43464..281c08b3fdd2 100644
--- a/tools/testing/selftests/kvm/aarch64/get-reg-list.c
+++ b/tools/testing/selftests/kvm/aarch64/get-reg-list.c
@@ -294,6 +294,11 @@ static void print_reg(struct vcpu_config *c, __u64 id)
 			    "%s: Unexpected bits set in FW reg id: 0x%llx", config_name(c), id);
 		printf("\tKVM_REG_ARM_FW_REG(%lld),\n", id & 0xffff);
 		break;
+	case KVM_REG_ARM_FW_FEAT_BMAP:
+		TEST_ASSERT(id == KVM_REG_ARM_FW_FEAT_BMAP_REG(id & 0xffff),
+			    "%s: Unexpected bits set in the bitmap feature FW reg id: 0x%llx", config_name(c), id);
+		printf("\tKVM_REG_ARM_FW_FEAT_BMAP_REG(%lld),\n", id & 0xffff);
+		break;
 	case KVM_REG_ARM64_SVE:
 		if (has_cap(c, KVM_CAP_ARM_SVE))
 			printf("\t%s,\n", sve_id_to_str(c, id));
@@ -686,6 +691,9 @@ static __u64 base_regs[] = {
 	KVM_REG_ARM_FW_REG(0),
 	KVM_REG_ARM_FW_REG(1),
 	KVM_REG_ARM_FW_REG(2),
+	KVM_REG_ARM_FW_FEAT_BMAP_REG(0),	/* KVM_REG_ARM_STD_BMAP */
+	KVM_REG_ARM_FW_FEAT_BMAP_REG(1),	/* KVM_REG_ARM_STD_HYP_BMAP */
+	KVM_REG_ARM_FW_FEAT_BMAP_REG(2),	/* KVM_REG_ARM_VENDOR_HYP_BMAP */
 	ARM64_SYS_REG(3, 3, 14, 3, 1),	/* CNTV_CTL_EL0 */
 	ARM64_SYS_REG(3, 3, 14, 3, 2),	/* CNTV_CVAL_EL0 */
 	ARM64_SYS_REG(3, 3, 14, 0, 2),
-- 
2.35.1.1094.g7c7d902a7c-goog

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

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

* [PATCH v5 09/10] selftests: KVM: aarch64: Add the bitmap firmware registers to get-reg-list
@ 2022-04-07  1:16   ` Raghavendra Rao Ananta
  0 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-07  1:16 UTC (permalink / raw)
  To: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose
  Cc: Paolo Bonzini, Catalin Marinas, Will Deacon, Peter Shier,
	Ricardo Koller, Oliver Upton, Reiji Watanabe, Jing Zhang,
	Raghavendra Rao Anata, linux-arm-kernel, kvmarm, linux-kernel,
	kvm

Add the psuedo-firmware registers KVM_REG_ARM_STD_BMAP,
KVM_REG_ARM_STD_HYP_BMAP, and KVM_REG_ARM_VENDOR_HYP_BMAP to
the base_regs[] list.

Also, add the COPROC support for KVM_REG_ARM_FW_FEAT_BMAP.

Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
---
 tools/testing/selftests/kvm/aarch64/get-reg-list.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/tools/testing/selftests/kvm/aarch64/get-reg-list.c b/tools/testing/selftests/kvm/aarch64/get-reg-list.c
index f12147c43464..281c08b3fdd2 100644
--- a/tools/testing/selftests/kvm/aarch64/get-reg-list.c
+++ b/tools/testing/selftests/kvm/aarch64/get-reg-list.c
@@ -294,6 +294,11 @@ static void print_reg(struct vcpu_config *c, __u64 id)
 			    "%s: Unexpected bits set in FW reg id: 0x%llx", config_name(c), id);
 		printf("\tKVM_REG_ARM_FW_REG(%lld),\n", id & 0xffff);
 		break;
+	case KVM_REG_ARM_FW_FEAT_BMAP:
+		TEST_ASSERT(id == KVM_REG_ARM_FW_FEAT_BMAP_REG(id & 0xffff),
+			    "%s: Unexpected bits set in the bitmap feature FW reg id: 0x%llx", config_name(c), id);
+		printf("\tKVM_REG_ARM_FW_FEAT_BMAP_REG(%lld),\n", id & 0xffff);
+		break;
 	case KVM_REG_ARM64_SVE:
 		if (has_cap(c, KVM_CAP_ARM_SVE))
 			printf("\t%s,\n", sve_id_to_str(c, id));
@@ -686,6 +691,9 @@ static __u64 base_regs[] = {
 	KVM_REG_ARM_FW_REG(0),
 	KVM_REG_ARM_FW_REG(1),
 	KVM_REG_ARM_FW_REG(2),
+	KVM_REG_ARM_FW_FEAT_BMAP_REG(0),	/* KVM_REG_ARM_STD_BMAP */
+	KVM_REG_ARM_FW_FEAT_BMAP_REG(1),	/* KVM_REG_ARM_STD_HYP_BMAP */
+	KVM_REG_ARM_FW_FEAT_BMAP_REG(2),	/* KVM_REG_ARM_VENDOR_HYP_BMAP */
 	ARM64_SYS_REG(3, 3, 14, 3, 1),	/* CNTV_CTL_EL0 */
 	ARM64_SYS_REG(3, 3, 14, 3, 2),	/* CNTV_CVAL_EL0 */
 	ARM64_SYS_REG(3, 3, 14, 0, 2),
-- 
2.35.1.1094.g7c7d902a7c-goog


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

* [PATCH v5 09/10] selftests: KVM: aarch64: Add the bitmap firmware registers to get-reg-list
@ 2022-04-07  1:16   ` Raghavendra Rao Ananta
  0 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-07  1:16 UTC (permalink / raw)
  To: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose
  Cc: Paolo Bonzini, Catalin Marinas, Will Deacon, Peter Shier,
	Ricardo Koller, Oliver Upton, Reiji Watanabe, Jing Zhang,
	Raghavendra Rao Anata, linux-arm-kernel, kvmarm, linux-kernel,
	kvm

Add the psuedo-firmware registers KVM_REG_ARM_STD_BMAP,
KVM_REG_ARM_STD_HYP_BMAP, and KVM_REG_ARM_VENDOR_HYP_BMAP to
the base_regs[] list.

Also, add the COPROC support for KVM_REG_ARM_FW_FEAT_BMAP.

Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
---
 tools/testing/selftests/kvm/aarch64/get-reg-list.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/tools/testing/selftests/kvm/aarch64/get-reg-list.c b/tools/testing/selftests/kvm/aarch64/get-reg-list.c
index f12147c43464..281c08b3fdd2 100644
--- a/tools/testing/selftests/kvm/aarch64/get-reg-list.c
+++ b/tools/testing/selftests/kvm/aarch64/get-reg-list.c
@@ -294,6 +294,11 @@ static void print_reg(struct vcpu_config *c, __u64 id)
 			    "%s: Unexpected bits set in FW reg id: 0x%llx", config_name(c), id);
 		printf("\tKVM_REG_ARM_FW_REG(%lld),\n", id & 0xffff);
 		break;
+	case KVM_REG_ARM_FW_FEAT_BMAP:
+		TEST_ASSERT(id == KVM_REG_ARM_FW_FEAT_BMAP_REG(id & 0xffff),
+			    "%s: Unexpected bits set in the bitmap feature FW reg id: 0x%llx", config_name(c), id);
+		printf("\tKVM_REG_ARM_FW_FEAT_BMAP_REG(%lld),\n", id & 0xffff);
+		break;
 	case KVM_REG_ARM64_SVE:
 		if (has_cap(c, KVM_CAP_ARM_SVE))
 			printf("\t%s,\n", sve_id_to_str(c, id));
@@ -686,6 +691,9 @@ static __u64 base_regs[] = {
 	KVM_REG_ARM_FW_REG(0),
 	KVM_REG_ARM_FW_REG(1),
 	KVM_REG_ARM_FW_REG(2),
+	KVM_REG_ARM_FW_FEAT_BMAP_REG(0),	/* KVM_REG_ARM_STD_BMAP */
+	KVM_REG_ARM_FW_FEAT_BMAP_REG(1),	/* KVM_REG_ARM_STD_HYP_BMAP */
+	KVM_REG_ARM_FW_FEAT_BMAP_REG(2),	/* KVM_REG_ARM_VENDOR_HYP_BMAP */
 	ARM64_SYS_REG(3, 3, 14, 3, 1),	/* CNTV_CTL_EL0 */
 	ARM64_SYS_REG(3, 3, 14, 3, 2),	/* CNTV_CVAL_EL0 */
 	ARM64_SYS_REG(3, 3, 14, 0, 2),
-- 
2.35.1.1094.g7c7d902a7c-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] 96+ messages in thread

* [PATCH v5 10/10] selftests: KVM: aarch64: Add KVM_REG_ARM_FW_REG(3) to get-reg-list
  2022-04-07  1:15 ` Raghavendra Rao Ananta
  (?)
@ 2022-04-07  1:16   ` Raghavendra Rao Ananta
  -1 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-07  1:16 UTC (permalink / raw)
  To: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose
  Cc: kvm, Catalin Marinas, Peter Shier, linux-kernel, Paolo Bonzini,
	Will Deacon, kvmarm, linux-arm-kernel

Add the register KVM_REG_ARM_FW_REG(3)
(KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3) to the base_regs[] of
get-reg-list.

Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
---
 tools/testing/selftests/kvm/aarch64/get-reg-list.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/tools/testing/selftests/kvm/aarch64/get-reg-list.c b/tools/testing/selftests/kvm/aarch64/get-reg-list.c
index 281c08b3fdd2..7049c31aa443 100644
--- a/tools/testing/selftests/kvm/aarch64/get-reg-list.c
+++ b/tools/testing/selftests/kvm/aarch64/get-reg-list.c
@@ -691,6 +691,7 @@ static __u64 base_regs[] = {
 	KVM_REG_ARM_FW_REG(0),
 	KVM_REG_ARM_FW_REG(1),
 	KVM_REG_ARM_FW_REG(2),
+	KVM_REG_ARM_FW_REG(3),
 	KVM_REG_ARM_FW_FEAT_BMAP_REG(0),	/* KVM_REG_ARM_STD_BMAP */
 	KVM_REG_ARM_FW_FEAT_BMAP_REG(1),	/* KVM_REG_ARM_STD_HYP_BMAP */
 	KVM_REG_ARM_FW_FEAT_BMAP_REG(2),	/* KVM_REG_ARM_VENDOR_HYP_BMAP */
-- 
2.35.1.1094.g7c7d902a7c-goog

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

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

* [PATCH v5 10/10] selftests: KVM: aarch64: Add KVM_REG_ARM_FW_REG(3) to get-reg-list
@ 2022-04-07  1:16   ` Raghavendra Rao Ananta
  0 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-07  1:16 UTC (permalink / raw)
  To: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose
  Cc: Paolo Bonzini, Catalin Marinas, Will Deacon, Peter Shier,
	Ricardo Koller, Oliver Upton, Reiji Watanabe, Jing Zhang,
	Raghavendra Rao Anata, linux-arm-kernel, kvmarm, linux-kernel,
	kvm

Add the register KVM_REG_ARM_FW_REG(3)
(KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3) to the base_regs[] of
get-reg-list.

Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
---
 tools/testing/selftests/kvm/aarch64/get-reg-list.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/tools/testing/selftests/kvm/aarch64/get-reg-list.c b/tools/testing/selftests/kvm/aarch64/get-reg-list.c
index 281c08b3fdd2..7049c31aa443 100644
--- a/tools/testing/selftests/kvm/aarch64/get-reg-list.c
+++ b/tools/testing/selftests/kvm/aarch64/get-reg-list.c
@@ -691,6 +691,7 @@ static __u64 base_regs[] = {
 	KVM_REG_ARM_FW_REG(0),
 	KVM_REG_ARM_FW_REG(1),
 	KVM_REG_ARM_FW_REG(2),
+	KVM_REG_ARM_FW_REG(3),
 	KVM_REG_ARM_FW_FEAT_BMAP_REG(0),	/* KVM_REG_ARM_STD_BMAP */
 	KVM_REG_ARM_FW_FEAT_BMAP_REG(1),	/* KVM_REG_ARM_STD_HYP_BMAP */
 	KVM_REG_ARM_FW_FEAT_BMAP_REG(2),	/* KVM_REG_ARM_VENDOR_HYP_BMAP */
-- 
2.35.1.1094.g7c7d902a7c-goog


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

* [PATCH v5 10/10] selftests: KVM: aarch64: Add KVM_REG_ARM_FW_REG(3) to get-reg-list
@ 2022-04-07  1:16   ` Raghavendra Rao Ananta
  0 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-07  1:16 UTC (permalink / raw)
  To: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose
  Cc: Paolo Bonzini, Catalin Marinas, Will Deacon, Peter Shier,
	Ricardo Koller, Oliver Upton, Reiji Watanabe, Jing Zhang,
	Raghavendra Rao Anata, linux-arm-kernel, kvmarm, linux-kernel,
	kvm

Add the register KVM_REG_ARM_FW_REG(3)
(KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3) to the base_regs[] of
get-reg-list.

Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
---
 tools/testing/selftests/kvm/aarch64/get-reg-list.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/tools/testing/selftests/kvm/aarch64/get-reg-list.c b/tools/testing/selftests/kvm/aarch64/get-reg-list.c
index 281c08b3fdd2..7049c31aa443 100644
--- a/tools/testing/selftests/kvm/aarch64/get-reg-list.c
+++ b/tools/testing/selftests/kvm/aarch64/get-reg-list.c
@@ -691,6 +691,7 @@ static __u64 base_regs[] = {
 	KVM_REG_ARM_FW_REG(0),
 	KVM_REG_ARM_FW_REG(1),
 	KVM_REG_ARM_FW_REG(2),
+	KVM_REG_ARM_FW_REG(3),
 	KVM_REG_ARM_FW_FEAT_BMAP_REG(0),	/* KVM_REG_ARM_STD_BMAP */
 	KVM_REG_ARM_FW_FEAT_BMAP_REG(1),	/* KVM_REG_ARM_STD_HYP_BMAP */
 	KVM_REG_ARM_FW_FEAT_BMAP_REG(2),	/* KVM_REG_ARM_VENDOR_HYP_BMAP */
-- 
2.35.1.1094.g7c7d902a7c-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] 96+ messages in thread

* Re: [PATCH v5 02/10] KVM: arm64: Setup a framework for hypercall bitmap firmware registers
  2022-04-07  1:15   ` Raghavendra Rao Ananta
  (?)
@ 2022-04-07  9:06     ` Marc Zyngier
  -1 siblings, 0 replies; 96+ messages in thread
From: Marc Zyngier @ 2022-04-07  9:06 UTC (permalink / raw)
  To: Raghavendra Rao Ananta
  Cc: Andrew Jones, James Morse, Alexandru Elisei, Suzuki K Poulose,
	Paolo Bonzini, Catalin Marinas, Will Deacon, Peter Shier,
	Ricardo Koller, Oliver Upton, Reiji Watanabe, Jing Zhang,
	linux-arm-kernel, kvmarm, linux-kernel, kvm

Hi Raghavendra,

On Thu, 07 Apr 2022 02:15:57 +0100,
Raghavendra Rao Ananta <rananta@google.com> wrote:
> 
> KVM regularly introduces new hypercall services to the guests without
> any consent from the userspace. This means, the guests can observe
> hypercall services in and out as they migrate across various host
> kernel versions. This could be a major problem if the guest
> discovered a hypercall, started using it, and after getting migrated
> to an older kernel realizes that it's no longer available. Depending
> on how the guest handles the change, there's a potential chance that
> the guest would just panic.
> 
> As a result, there's a need for the userspace to elect the services
> that it wishes the guest to discover. It can elect these services
> based on the kernels spread across its (migration) fleet. To remedy
> this, extend the existing firmware psuedo-registers, such as

nit: pseudo

> KVM_REG_ARM_PSCI_VERSION, but by creating a new COPROC register space
> for all the hypercall services available.
> 
> These firmware registers are categorized based on the service call
> owners, but unlike the existing firmware psuedo-registers, they hold

nit: pseudo again

> the features supported in the form of a bitmap.
> 
> During the VM initialization, the registers are set to upper-limit of
> the features supported by the corresponding registers. It's expected
> that the VMMs discover the features provided by each register via
> GET_ONE_REG, and writeback the desired values using SET_ONE_REG.

nit: write back

> KVM allows this modification only until the VM has started.
> 
> Some of the standard features are not mapped to any bits of the
> registers. But since they can recreate the original problem of
> making it available without userspace's consent, they need to
> be explicitly added to the hvc_func_default_allowed_list[]. Any
> function-id that's not enabled via the bitmap, or not listed in
> hvc_func_default_allowed_list[], will be returned as
> SMCCC_RET_NOT_SUPPORTED to the guest.
> 
> Older userspace code can simply ignore the feature and the
> hypercall services will be exposed unconditionally to the guests,
> thus ensuring backward compatibility.
> 
> In this patch, the framework adds the register only for ARM's standard
> secure services (owner value 4). Currently, this includes support only
> for ARM True Random Number Generator (TRNG) service, with bit-0 of the
> register representing mandatory features of v1.0. Other services are
> momentarily added in the upcoming patches.
> 
> Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
> ---
>  arch/arm64/include/asm/kvm_host.h |  12 ++++
>  arch/arm64/include/uapi/asm/kvm.h |   9 +++
>  arch/arm64/kvm/arm.c              |   1 +
>  arch/arm64/kvm/guest.c            |   8 ++-
>  arch/arm64/kvm/hypercalls.c       | 102 ++++++++++++++++++++++++++++++
>  include/kvm/arm_hypercalls.h      |   7 ++
>  include/kvm/arm_psci.h            |  12 ++++
>  7 files changed, 149 insertions(+), 2 deletions(-)
> 
> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index e3b25dc6c367..6e663383d7b4 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -101,6 +101,15 @@ struct kvm_s2_mmu {
>  struct kvm_arch_memory_slot {
>  };
>  
> +/**
> + * struct kvm_smccc_features: Descriptor the hypercall services exposed to the guests
> + *
> + * @std_bmap: Bitmap of standard secure service calls
> + */
> +struct kvm_smccc_features {
> +	u64 std_bmap;

Consider using 'unsigned long' for bitmaps.

> +};
> +
>  struct kvm_arch {
>  	struct kvm_s2_mmu mmu;
>  
> @@ -140,6 +149,9 @@ struct kvm_arch {
>  
>  	u8 pfr0_csv2;
>  	u8 pfr0_csv3;
> +
> +	/* Hypercall features firmware registers' descriptor */
> +	struct kvm_smccc_features smccc_feat;
>  };
>  
>  struct kvm_vcpu_fault_info {
> diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h
> index c1b6ddc02d2f..56e4bc58a355 100644
> --- a/arch/arm64/include/uapi/asm/kvm.h
> +++ b/arch/arm64/include/uapi/asm/kvm.h
> @@ -332,6 +332,15 @@ struct kvm_arm_copy_mte_tags {
>  #define KVM_ARM64_SVE_VLS_WORDS	\
>  	((KVM_ARM64_SVE_VQ_MAX - KVM_ARM64_SVE_VQ_MIN) / 64 + 1)
>  
> +/* Bitmap feature firmware registers */
> +#define KVM_REG_ARM_FW_FEAT_BMAP		(0x0016 << KVM_REG_ARM_COPROC_SHIFT)
> +#define KVM_REG_ARM_FW_FEAT_BMAP_REG(r)		(KVM_REG_ARM64 | KVM_REG_SIZE_U64 | \
> +						KVM_REG_ARM_FW_FEAT_BMAP |	\
> +						((r) & 0xffff))
> +
> +#define KVM_REG_ARM_STD_BMAP			KVM_REG_ARM_FW_FEAT_BMAP_REG(0)
> +#define KVM_REG_ARM_STD_BIT_TRNG_V1_0		BIT(0)

I'm really in two minds about this. Having one bit per service is easy
from an implementation perspective, but is also means that this
disallow fine grained control over which hypercalls are actually
available. If tomorrow TRNG 1.1 adds a new hypercall and that KVM
implements both, how does the selection mechanism works? You will
need a version selector (a la PSCI), which defeats this API somehow
(and renders the name of the #define invalid).

I wonder if a more correct way to look at this is to enumerate the
hypercalls themselves (all 5 of them), though coming up with an
encoding is tricky (RNG32 and RNG64 would clash, for example).

Thoughts?

> +
>  /* Device Control API: ARM VGIC */
>  #define KVM_DEV_ARM_VGIC_GRP_ADDR	0
>  #define KVM_DEV_ARM_VGIC_GRP_DIST_REGS	1
> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> index 523bc934fe2f..a37fadbd617e 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);
> +	kvm_arm_init_hypercalls(kvm);
>  
>  	return ret;
>  out_free_stage2_pgd:
> diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
> index 0d5cca56cbda..8c607199cad1 100644
> --- a/arch/arm64/kvm/guest.c
> +++ b/arch/arm64/kvm/guest.c
> @@ -756,7 +756,9 @@ int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>  
>  	switch (reg->id & KVM_REG_ARM_COPROC_MASK) {
>  	case KVM_REG_ARM_CORE:	return get_core_reg(vcpu, reg);
> -	case KVM_REG_ARM_FW:	return kvm_arm_get_fw_reg(vcpu, reg);
> +	case KVM_REG_ARM_FW:
> +	case KVM_REG_ARM_FW_FEAT_BMAP:
> +		return kvm_arm_get_fw_reg(vcpu, reg);
>  	case KVM_REG_ARM64_SVE:	return get_sve_reg(vcpu, reg);
>  	}
>  
> @@ -774,7 +776,9 @@ int kvm_arm_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>  
>  	switch (reg->id & KVM_REG_ARM_COPROC_MASK) {
>  	case KVM_REG_ARM_CORE:	return set_core_reg(vcpu, reg);
> -	case KVM_REG_ARM_FW:	return kvm_arm_set_fw_reg(vcpu, reg);
> +	case KVM_REG_ARM_FW:
> +	case KVM_REG_ARM_FW_FEAT_BMAP:
> +		return kvm_arm_set_fw_reg(vcpu, reg);
>  	case KVM_REG_ARM64_SVE:	return set_sve_reg(vcpu, reg);
>  	}
>  
> diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c
> index fa6d9378d8e7..cf04b5ee5f56 100644
> --- a/arch/arm64/kvm/hypercalls.c
> +++ b/arch/arm64/kvm/hypercalls.c
> @@ -58,6 +58,53 @@ static void kvm_ptp_get_time(struct kvm_vcpu *vcpu, u64 *val)
>  	val[3] = lower_32_bits(cycles);
>  }
>  
> +/*
> + * List of function-ids that are not gated with the bitmapped feature
> + * firmware registers, and are to be allowed for servicing the call by default.
> + */
> +static const u32 hvc_func_default_allowed_list[] = {
> +	ARM_SMCCC_VERSION_FUNC_ID,
> +	ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
> +	ARM_SMCCC_HV_PV_TIME_FEATURES,
> +	ARM_SMCCC_HV_PV_TIME_ST,
> +	ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID,
> +	ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID,
> +	ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID,
> +};
> +
> +static bool kvm_hvc_call_default_allowed(struct kvm_vcpu *vcpu, u32 func_id)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(hvc_func_default_allowed_list); i++)
> +		if (func_id == hvc_func_default_allowed_list[i])
> +			return true;

Huh, this really is ugly. This array is bound to become bigger over
time, meaning that the average hypercall time is going to increase. At
the very least, this should be turned into a switch/case statement, as
the compile is pretty good at building a search tree (better than this
naive loop, for a start), and we have those everywhere else.

> +
> +	return kvm_psci_func_id_is_valid(vcpu, func_id);
> +}
> +
> +static bool kvm_arm_fw_reg_feat_enabled(u64 reg_bmap, u64 feat_bit)
> +{
> +	return reg_bmap & feat_bit;
> +}

We really don't need to reimplement test_bit().

> +
> +static bool kvm_hvc_call_allowed(struct kvm_vcpu *vcpu, u32 func_id)
> +{
> +	struct kvm_smccc_features *smccc_feat = &vcpu->kvm->arch.smccc_feat;
> +
> +	switch (func_id) {
> +	case ARM_SMCCC_TRNG_VERSION:
> +	case ARM_SMCCC_TRNG_FEATURES:
> +	case ARM_SMCCC_TRNG_GET_UUID:
> +	case ARM_SMCCC_TRNG_RND32:
> +	case ARM_SMCCC_TRNG_RND64:
> +		return kvm_arm_fw_reg_feat_enabled(smccc_feat->std_bmap,
> +						KVM_REG_ARM_STD_BIT_TRNG_V1_0);
> +	default:
> +		return kvm_hvc_call_default_allowed(vcpu, func_id);
> +	}
> +}
> +
>  int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
>  {
>  	u32 func_id = smccc_get_function(vcpu);
> @@ -65,6 +112,9 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
>  	u32 feature;
>  	gpa_t gpa;
>  
> +	if (!kvm_hvc_call_allowed(vcpu, func_id))
> +		goto out;
> +
>  	switch (func_id) {
>  	case ARM_SMCCC_VERSION_FUNC_ID:
>  		val[0] = ARM_SMCCC_VERSION_1_1;
> @@ -155,6 +205,7 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
>  		return kvm_psci_call(vcpu);
>  	}
>  
> +out:
>  	smccc_set_retval(vcpu, val[0], val[1], val[2], val[3]);
>  	return 1;
>  }
> @@ -164,8 +215,16 @@ static const u64 kvm_arm_fw_reg_ids[] = {
>  	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1,
>  	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2,
>  	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3,
> +	KVM_REG_ARM_STD_BMAP,
>  };
>  
> +void kvm_arm_init_hypercalls(struct kvm *kvm)
> +{
> +	struct kvm_smccc_features *smccc_feat = &kvm->arch.smccc_feat;
> +
> +	smccc_feat->std_bmap = KVM_ARM_SMCCC_STD_FEATURES;
> +}
> +
>  int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
>  {
>  	return ARRAY_SIZE(kvm_arm_fw_reg_ids);
> @@ -237,6 +296,7 @@ static int get_kernel_wa_level(u64 regid)
>  
>  int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>  {
> +	struct kvm_smccc_features *smccc_feat = &vcpu->kvm->arch.smccc_feat;
>  	void __user *uaddr = (void __user *)(long)reg->addr;
>  	u64 val;
>  
> @@ -249,6 +309,9 @@ int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>  	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
>  		val = get_kernel_wa_level(reg->id) & KVM_REG_FEATURE_LEVEL_MASK;
>  		break;
> +	case KVM_REG_ARM_STD_BMAP:
> +		val = READ_ONCE(smccc_feat->std_bmap);
> +		break;
>  	default:
>  		return -ENOENT;
>  	}
> @@ -259,6 +322,43 @@ int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>  	return 0;
>  }
>  
> +static int kvm_arm_set_fw_reg_bmap(struct kvm_vcpu *vcpu, u64 reg_id, u64 val)
> +{
> +	int ret = 0;
> +	struct kvm *kvm = vcpu->kvm;
> +	struct kvm_smccc_features *smccc_feat = &kvm->arch.smccc_feat;
> +	u64 *fw_reg_bmap, fw_reg_features;
> +
> +	switch (reg_id) {
> +	case KVM_REG_ARM_STD_BMAP:
> +		fw_reg_bmap = &smccc_feat->std_bmap;
> +		fw_reg_features = KVM_ARM_SMCCC_STD_FEATURES;
> +		break;
> +	default:
> +		return -ENOENT;
> +	}
> +
> +	/* Check for unsupported bit */
> +	if (val & ~fw_reg_features)
> +		return -EINVAL;
> +
> +	mutex_lock(&kvm->lock);
> +
> +	/*
> +	 * If the VM (any vCPU) has already started running, return success
> +	 * if there's no change in the value. Else, return -EBUSY.

No, this should *always* fail if a vcpu has started. Otherwise, you
start allowing hard to spot races.

> +	 */
> +	if (test_bit(KVM_ARCH_FLAG_HAS_RAN_ONCE, &kvm->arch.flags)) {
> +		ret = *fw_reg_bmap != val ? -EBUSY : 0;
> +		goto out;
> +	}
> +
> +	WRITE_ONCE(*fw_reg_bmap, val);

I'm not sure what this WRITE_ONCE guards against. Do you expect
concurrent reads at this stage?

> +out:
> +	mutex_unlock(&kvm->lock);
> +	return ret;
> +}
> +
>  int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>  {
>  	void __user *uaddr = (void __user *)(long)reg->addr;
> @@ -337,6 +437,8 @@ int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>  			return -EINVAL;
>  
>  		return 0;
> +	case KVM_REG_ARM_STD_BMAP:
> +		return kvm_arm_set_fw_reg_bmap(vcpu, reg->id, val);
>  	default:
>  		return -ENOENT;
>  	}
> diff --git a/include/kvm/arm_hypercalls.h b/include/kvm/arm_hypercalls.h
> index 5d38628a8d04..fd3ff350ee9d 100644
> --- a/include/kvm/arm_hypercalls.h
> +++ b/include/kvm/arm_hypercalls.h
> @@ -6,6 +6,12 @@
>  
>  #include <asm/kvm_emulate.h>
>  
> +/* Last valid bits of the bitmapped firmware registers */
> +#define KVM_REG_ARM_STD_BMAP_BIT_MAX		0
> +
> +#define KVM_ARM_SMCCC_STD_FEATURES \
> +	GENMASK_ULL(KVM_REG_ARM_STD_BMAP_BIT_MAX, 0)
> +
>  int kvm_hvc_call_handler(struct kvm_vcpu *vcpu);
>  
>  static inline u32 smccc_get_function(struct kvm_vcpu *vcpu)
> @@ -42,6 +48,7 @@ static inline void smccc_set_retval(struct kvm_vcpu *vcpu,
>  
>  struct kvm_one_reg;
>  
> +void kvm_arm_init_hypercalls(struct kvm *kvm);
>  int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu);
>  int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices);
>  int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
> diff --git a/include/kvm/arm_psci.h b/include/kvm/arm_psci.h
> index 6e55b9283789..d7a87367de56 100644
> --- a/include/kvm/arm_psci.h
> +++ b/include/kvm/arm_psci.h
> @@ -36,6 +36,18 @@ static inline int kvm_psci_version(struct kvm_vcpu *vcpu)
>  	return KVM_ARM_PSCI_0_1;
>  }
>  
> +static inline bool kvm_psci_func_id_is_valid(struct kvm_vcpu *vcpu, u32 func_id)
> +{
> +	/* PSCI 0.1 doesn't comply with the standard SMCCC */
> +	if (kvm_psci_version(vcpu) == KVM_ARM_PSCI_0_1)
> +		return (func_id == KVM_PSCI_FN_CPU_OFF || func_id == KVM_PSCI_FN_CPU_ON);
> +
> +	if (ARM_SMCCC_OWNER_NUM(func_id) == ARM_SMCCC_OWNER_STANDARD &&
> +		ARM_SMCCC_FUNC_NUM(func_id) >= 0 && ARM_SMCCC_FUNC_NUM(func_id) <= 0x1f)
> +		return true;
> +
> +	return false;
> +}

Why the inline function? Do you expect this to be shared with
something else? If not, I'd rather you move it into the caller.

>  
>  int kvm_psci_call(struct kvm_vcpu *vcpu);
>  

Thanks,

	M.

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

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

* Re: [PATCH v5 02/10] KVM: arm64: Setup a framework for hypercall bitmap firmware registers
@ 2022-04-07  9:06     ` Marc Zyngier
  0 siblings, 0 replies; 96+ messages in thread
From: Marc Zyngier @ 2022-04-07  9:06 UTC (permalink / raw)
  To: Raghavendra Rao Ananta
  Cc: kvm, Will Deacon, Catalin Marinas, Peter Shier, linux-kernel,
	Paolo Bonzini, kvmarm, linux-arm-kernel

Hi Raghavendra,

On Thu, 07 Apr 2022 02:15:57 +0100,
Raghavendra Rao Ananta <rananta@google.com> wrote:
> 
> KVM regularly introduces new hypercall services to the guests without
> any consent from the userspace. This means, the guests can observe
> hypercall services in and out as they migrate across various host
> kernel versions. This could be a major problem if the guest
> discovered a hypercall, started using it, and after getting migrated
> to an older kernel realizes that it's no longer available. Depending
> on how the guest handles the change, there's a potential chance that
> the guest would just panic.
> 
> As a result, there's a need for the userspace to elect the services
> that it wishes the guest to discover. It can elect these services
> based on the kernels spread across its (migration) fleet. To remedy
> this, extend the existing firmware psuedo-registers, such as

nit: pseudo

> KVM_REG_ARM_PSCI_VERSION, but by creating a new COPROC register space
> for all the hypercall services available.
> 
> These firmware registers are categorized based on the service call
> owners, but unlike the existing firmware psuedo-registers, they hold

nit: pseudo again

> the features supported in the form of a bitmap.
> 
> During the VM initialization, the registers are set to upper-limit of
> the features supported by the corresponding registers. It's expected
> that the VMMs discover the features provided by each register via
> GET_ONE_REG, and writeback the desired values using SET_ONE_REG.

nit: write back

> KVM allows this modification only until the VM has started.
> 
> Some of the standard features are not mapped to any bits of the
> registers. But since they can recreate the original problem of
> making it available without userspace's consent, they need to
> be explicitly added to the hvc_func_default_allowed_list[]. Any
> function-id that's not enabled via the bitmap, or not listed in
> hvc_func_default_allowed_list[], will be returned as
> SMCCC_RET_NOT_SUPPORTED to the guest.
> 
> Older userspace code can simply ignore the feature and the
> hypercall services will be exposed unconditionally to the guests,
> thus ensuring backward compatibility.
> 
> In this patch, the framework adds the register only for ARM's standard
> secure services (owner value 4). Currently, this includes support only
> for ARM True Random Number Generator (TRNG) service, with bit-0 of the
> register representing mandatory features of v1.0. Other services are
> momentarily added in the upcoming patches.
> 
> Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
> ---
>  arch/arm64/include/asm/kvm_host.h |  12 ++++
>  arch/arm64/include/uapi/asm/kvm.h |   9 +++
>  arch/arm64/kvm/arm.c              |   1 +
>  arch/arm64/kvm/guest.c            |   8 ++-
>  arch/arm64/kvm/hypercalls.c       | 102 ++++++++++++++++++++++++++++++
>  include/kvm/arm_hypercalls.h      |   7 ++
>  include/kvm/arm_psci.h            |  12 ++++
>  7 files changed, 149 insertions(+), 2 deletions(-)
> 
> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index e3b25dc6c367..6e663383d7b4 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -101,6 +101,15 @@ struct kvm_s2_mmu {
>  struct kvm_arch_memory_slot {
>  };
>  
> +/**
> + * struct kvm_smccc_features: Descriptor the hypercall services exposed to the guests
> + *
> + * @std_bmap: Bitmap of standard secure service calls
> + */
> +struct kvm_smccc_features {
> +	u64 std_bmap;

Consider using 'unsigned long' for bitmaps.

> +};
> +
>  struct kvm_arch {
>  	struct kvm_s2_mmu mmu;
>  
> @@ -140,6 +149,9 @@ struct kvm_arch {
>  
>  	u8 pfr0_csv2;
>  	u8 pfr0_csv3;
> +
> +	/* Hypercall features firmware registers' descriptor */
> +	struct kvm_smccc_features smccc_feat;
>  };
>  
>  struct kvm_vcpu_fault_info {
> diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h
> index c1b6ddc02d2f..56e4bc58a355 100644
> --- a/arch/arm64/include/uapi/asm/kvm.h
> +++ b/arch/arm64/include/uapi/asm/kvm.h
> @@ -332,6 +332,15 @@ struct kvm_arm_copy_mte_tags {
>  #define KVM_ARM64_SVE_VLS_WORDS	\
>  	((KVM_ARM64_SVE_VQ_MAX - KVM_ARM64_SVE_VQ_MIN) / 64 + 1)
>  
> +/* Bitmap feature firmware registers */
> +#define KVM_REG_ARM_FW_FEAT_BMAP		(0x0016 << KVM_REG_ARM_COPROC_SHIFT)
> +#define KVM_REG_ARM_FW_FEAT_BMAP_REG(r)		(KVM_REG_ARM64 | KVM_REG_SIZE_U64 | \
> +						KVM_REG_ARM_FW_FEAT_BMAP |	\
> +						((r) & 0xffff))
> +
> +#define KVM_REG_ARM_STD_BMAP			KVM_REG_ARM_FW_FEAT_BMAP_REG(0)
> +#define KVM_REG_ARM_STD_BIT_TRNG_V1_0		BIT(0)

I'm really in two minds about this. Having one bit per service is easy
from an implementation perspective, but is also means that this
disallow fine grained control over which hypercalls are actually
available. If tomorrow TRNG 1.1 adds a new hypercall and that KVM
implements both, how does the selection mechanism works? You will
need a version selector (a la PSCI), which defeats this API somehow
(and renders the name of the #define invalid).

I wonder if a more correct way to look at this is to enumerate the
hypercalls themselves (all 5 of them), though coming up with an
encoding is tricky (RNG32 and RNG64 would clash, for example).

Thoughts?

> +
>  /* Device Control API: ARM VGIC */
>  #define KVM_DEV_ARM_VGIC_GRP_ADDR	0
>  #define KVM_DEV_ARM_VGIC_GRP_DIST_REGS	1
> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> index 523bc934fe2f..a37fadbd617e 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);
> +	kvm_arm_init_hypercalls(kvm);
>  
>  	return ret;
>  out_free_stage2_pgd:
> diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
> index 0d5cca56cbda..8c607199cad1 100644
> --- a/arch/arm64/kvm/guest.c
> +++ b/arch/arm64/kvm/guest.c
> @@ -756,7 +756,9 @@ int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>  
>  	switch (reg->id & KVM_REG_ARM_COPROC_MASK) {
>  	case KVM_REG_ARM_CORE:	return get_core_reg(vcpu, reg);
> -	case KVM_REG_ARM_FW:	return kvm_arm_get_fw_reg(vcpu, reg);
> +	case KVM_REG_ARM_FW:
> +	case KVM_REG_ARM_FW_FEAT_BMAP:
> +		return kvm_arm_get_fw_reg(vcpu, reg);
>  	case KVM_REG_ARM64_SVE:	return get_sve_reg(vcpu, reg);
>  	}
>  
> @@ -774,7 +776,9 @@ int kvm_arm_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>  
>  	switch (reg->id & KVM_REG_ARM_COPROC_MASK) {
>  	case KVM_REG_ARM_CORE:	return set_core_reg(vcpu, reg);
> -	case KVM_REG_ARM_FW:	return kvm_arm_set_fw_reg(vcpu, reg);
> +	case KVM_REG_ARM_FW:
> +	case KVM_REG_ARM_FW_FEAT_BMAP:
> +		return kvm_arm_set_fw_reg(vcpu, reg);
>  	case KVM_REG_ARM64_SVE:	return set_sve_reg(vcpu, reg);
>  	}
>  
> diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c
> index fa6d9378d8e7..cf04b5ee5f56 100644
> --- a/arch/arm64/kvm/hypercalls.c
> +++ b/arch/arm64/kvm/hypercalls.c
> @@ -58,6 +58,53 @@ static void kvm_ptp_get_time(struct kvm_vcpu *vcpu, u64 *val)
>  	val[3] = lower_32_bits(cycles);
>  }
>  
> +/*
> + * List of function-ids that are not gated with the bitmapped feature
> + * firmware registers, and are to be allowed for servicing the call by default.
> + */
> +static const u32 hvc_func_default_allowed_list[] = {
> +	ARM_SMCCC_VERSION_FUNC_ID,
> +	ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
> +	ARM_SMCCC_HV_PV_TIME_FEATURES,
> +	ARM_SMCCC_HV_PV_TIME_ST,
> +	ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID,
> +	ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID,
> +	ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID,
> +};
> +
> +static bool kvm_hvc_call_default_allowed(struct kvm_vcpu *vcpu, u32 func_id)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(hvc_func_default_allowed_list); i++)
> +		if (func_id == hvc_func_default_allowed_list[i])
> +			return true;

Huh, this really is ugly. This array is bound to become bigger over
time, meaning that the average hypercall time is going to increase. At
the very least, this should be turned into a switch/case statement, as
the compile is pretty good at building a search tree (better than this
naive loop, for a start), and we have those everywhere else.

> +
> +	return kvm_psci_func_id_is_valid(vcpu, func_id);
> +}
> +
> +static bool kvm_arm_fw_reg_feat_enabled(u64 reg_bmap, u64 feat_bit)
> +{
> +	return reg_bmap & feat_bit;
> +}

We really don't need to reimplement test_bit().

> +
> +static bool kvm_hvc_call_allowed(struct kvm_vcpu *vcpu, u32 func_id)
> +{
> +	struct kvm_smccc_features *smccc_feat = &vcpu->kvm->arch.smccc_feat;
> +
> +	switch (func_id) {
> +	case ARM_SMCCC_TRNG_VERSION:
> +	case ARM_SMCCC_TRNG_FEATURES:
> +	case ARM_SMCCC_TRNG_GET_UUID:
> +	case ARM_SMCCC_TRNG_RND32:
> +	case ARM_SMCCC_TRNG_RND64:
> +		return kvm_arm_fw_reg_feat_enabled(smccc_feat->std_bmap,
> +						KVM_REG_ARM_STD_BIT_TRNG_V1_0);
> +	default:
> +		return kvm_hvc_call_default_allowed(vcpu, func_id);
> +	}
> +}
> +
>  int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
>  {
>  	u32 func_id = smccc_get_function(vcpu);
> @@ -65,6 +112,9 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
>  	u32 feature;
>  	gpa_t gpa;
>  
> +	if (!kvm_hvc_call_allowed(vcpu, func_id))
> +		goto out;
> +
>  	switch (func_id) {
>  	case ARM_SMCCC_VERSION_FUNC_ID:
>  		val[0] = ARM_SMCCC_VERSION_1_1;
> @@ -155,6 +205,7 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
>  		return kvm_psci_call(vcpu);
>  	}
>  
> +out:
>  	smccc_set_retval(vcpu, val[0], val[1], val[2], val[3]);
>  	return 1;
>  }
> @@ -164,8 +215,16 @@ static const u64 kvm_arm_fw_reg_ids[] = {
>  	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1,
>  	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2,
>  	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3,
> +	KVM_REG_ARM_STD_BMAP,
>  };
>  
> +void kvm_arm_init_hypercalls(struct kvm *kvm)
> +{
> +	struct kvm_smccc_features *smccc_feat = &kvm->arch.smccc_feat;
> +
> +	smccc_feat->std_bmap = KVM_ARM_SMCCC_STD_FEATURES;
> +}
> +
>  int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
>  {
>  	return ARRAY_SIZE(kvm_arm_fw_reg_ids);
> @@ -237,6 +296,7 @@ static int get_kernel_wa_level(u64 regid)
>  
>  int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>  {
> +	struct kvm_smccc_features *smccc_feat = &vcpu->kvm->arch.smccc_feat;
>  	void __user *uaddr = (void __user *)(long)reg->addr;
>  	u64 val;
>  
> @@ -249,6 +309,9 @@ int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>  	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
>  		val = get_kernel_wa_level(reg->id) & KVM_REG_FEATURE_LEVEL_MASK;
>  		break;
> +	case KVM_REG_ARM_STD_BMAP:
> +		val = READ_ONCE(smccc_feat->std_bmap);
> +		break;
>  	default:
>  		return -ENOENT;
>  	}
> @@ -259,6 +322,43 @@ int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>  	return 0;
>  }
>  
> +static int kvm_arm_set_fw_reg_bmap(struct kvm_vcpu *vcpu, u64 reg_id, u64 val)
> +{
> +	int ret = 0;
> +	struct kvm *kvm = vcpu->kvm;
> +	struct kvm_smccc_features *smccc_feat = &kvm->arch.smccc_feat;
> +	u64 *fw_reg_bmap, fw_reg_features;
> +
> +	switch (reg_id) {
> +	case KVM_REG_ARM_STD_BMAP:
> +		fw_reg_bmap = &smccc_feat->std_bmap;
> +		fw_reg_features = KVM_ARM_SMCCC_STD_FEATURES;
> +		break;
> +	default:
> +		return -ENOENT;
> +	}
> +
> +	/* Check for unsupported bit */
> +	if (val & ~fw_reg_features)
> +		return -EINVAL;
> +
> +	mutex_lock(&kvm->lock);
> +
> +	/*
> +	 * If the VM (any vCPU) has already started running, return success
> +	 * if there's no change in the value. Else, return -EBUSY.

No, this should *always* fail if a vcpu has started. Otherwise, you
start allowing hard to spot races.

> +	 */
> +	if (test_bit(KVM_ARCH_FLAG_HAS_RAN_ONCE, &kvm->arch.flags)) {
> +		ret = *fw_reg_bmap != val ? -EBUSY : 0;
> +		goto out;
> +	}
> +
> +	WRITE_ONCE(*fw_reg_bmap, val);

I'm not sure what this WRITE_ONCE guards against. Do you expect
concurrent reads at this stage?

> +out:
> +	mutex_unlock(&kvm->lock);
> +	return ret;
> +}
> +
>  int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>  {
>  	void __user *uaddr = (void __user *)(long)reg->addr;
> @@ -337,6 +437,8 @@ int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>  			return -EINVAL;
>  
>  		return 0;
> +	case KVM_REG_ARM_STD_BMAP:
> +		return kvm_arm_set_fw_reg_bmap(vcpu, reg->id, val);
>  	default:
>  		return -ENOENT;
>  	}
> diff --git a/include/kvm/arm_hypercalls.h b/include/kvm/arm_hypercalls.h
> index 5d38628a8d04..fd3ff350ee9d 100644
> --- a/include/kvm/arm_hypercalls.h
> +++ b/include/kvm/arm_hypercalls.h
> @@ -6,6 +6,12 @@
>  
>  #include <asm/kvm_emulate.h>
>  
> +/* Last valid bits of the bitmapped firmware registers */
> +#define KVM_REG_ARM_STD_BMAP_BIT_MAX		0
> +
> +#define KVM_ARM_SMCCC_STD_FEATURES \
> +	GENMASK_ULL(KVM_REG_ARM_STD_BMAP_BIT_MAX, 0)
> +
>  int kvm_hvc_call_handler(struct kvm_vcpu *vcpu);
>  
>  static inline u32 smccc_get_function(struct kvm_vcpu *vcpu)
> @@ -42,6 +48,7 @@ static inline void smccc_set_retval(struct kvm_vcpu *vcpu,
>  
>  struct kvm_one_reg;
>  
> +void kvm_arm_init_hypercalls(struct kvm *kvm);
>  int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu);
>  int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices);
>  int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
> diff --git a/include/kvm/arm_psci.h b/include/kvm/arm_psci.h
> index 6e55b9283789..d7a87367de56 100644
> --- a/include/kvm/arm_psci.h
> +++ b/include/kvm/arm_psci.h
> @@ -36,6 +36,18 @@ static inline int kvm_psci_version(struct kvm_vcpu *vcpu)
>  	return KVM_ARM_PSCI_0_1;
>  }
>  
> +static inline bool kvm_psci_func_id_is_valid(struct kvm_vcpu *vcpu, u32 func_id)
> +{
> +	/* PSCI 0.1 doesn't comply with the standard SMCCC */
> +	if (kvm_psci_version(vcpu) == KVM_ARM_PSCI_0_1)
> +		return (func_id == KVM_PSCI_FN_CPU_OFF || func_id == KVM_PSCI_FN_CPU_ON);
> +
> +	if (ARM_SMCCC_OWNER_NUM(func_id) == ARM_SMCCC_OWNER_STANDARD &&
> +		ARM_SMCCC_FUNC_NUM(func_id) >= 0 && ARM_SMCCC_FUNC_NUM(func_id) <= 0x1f)
> +		return true;
> +
> +	return false;
> +}

Why the inline function? Do you expect this to be shared with
something else? If not, I'd rather you move it into the caller.

>  
>  int kvm_psci_call(struct kvm_vcpu *vcpu);
>  

Thanks,

	M.

-- 
Without deviation from the norm, progress is not possible.
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

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

* Re: [PATCH v5 02/10] KVM: arm64: Setup a framework for hypercall bitmap firmware registers
@ 2022-04-07  9:06     ` Marc Zyngier
  0 siblings, 0 replies; 96+ messages in thread
From: Marc Zyngier @ 2022-04-07  9:06 UTC (permalink / raw)
  To: Raghavendra Rao Ananta
  Cc: Andrew Jones, James Morse, Alexandru Elisei, Suzuki K Poulose,
	Paolo Bonzini, Catalin Marinas, Will Deacon, Peter Shier,
	Ricardo Koller, Oliver Upton, Reiji Watanabe, Jing Zhang,
	linux-arm-kernel, kvmarm, linux-kernel, kvm

Hi Raghavendra,

On Thu, 07 Apr 2022 02:15:57 +0100,
Raghavendra Rao Ananta <rananta@google.com> wrote:
> 
> KVM regularly introduces new hypercall services to the guests without
> any consent from the userspace. This means, the guests can observe
> hypercall services in and out as they migrate across various host
> kernel versions. This could be a major problem if the guest
> discovered a hypercall, started using it, and after getting migrated
> to an older kernel realizes that it's no longer available. Depending
> on how the guest handles the change, there's a potential chance that
> the guest would just panic.
> 
> As a result, there's a need for the userspace to elect the services
> that it wishes the guest to discover. It can elect these services
> based on the kernels spread across its (migration) fleet. To remedy
> this, extend the existing firmware psuedo-registers, such as

nit: pseudo

> KVM_REG_ARM_PSCI_VERSION, but by creating a new COPROC register space
> for all the hypercall services available.
> 
> These firmware registers are categorized based on the service call
> owners, but unlike the existing firmware psuedo-registers, they hold

nit: pseudo again

> the features supported in the form of a bitmap.
> 
> During the VM initialization, the registers are set to upper-limit of
> the features supported by the corresponding registers. It's expected
> that the VMMs discover the features provided by each register via
> GET_ONE_REG, and writeback the desired values using SET_ONE_REG.

nit: write back

> KVM allows this modification only until the VM has started.
> 
> Some of the standard features are not mapped to any bits of the
> registers. But since they can recreate the original problem of
> making it available without userspace's consent, they need to
> be explicitly added to the hvc_func_default_allowed_list[]. Any
> function-id that's not enabled via the bitmap, or not listed in
> hvc_func_default_allowed_list[], will be returned as
> SMCCC_RET_NOT_SUPPORTED to the guest.
> 
> Older userspace code can simply ignore the feature and the
> hypercall services will be exposed unconditionally to the guests,
> thus ensuring backward compatibility.
> 
> In this patch, the framework adds the register only for ARM's standard
> secure services (owner value 4). Currently, this includes support only
> for ARM True Random Number Generator (TRNG) service, with bit-0 of the
> register representing mandatory features of v1.0. Other services are
> momentarily added in the upcoming patches.
> 
> Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
> ---
>  arch/arm64/include/asm/kvm_host.h |  12 ++++
>  arch/arm64/include/uapi/asm/kvm.h |   9 +++
>  arch/arm64/kvm/arm.c              |   1 +
>  arch/arm64/kvm/guest.c            |   8 ++-
>  arch/arm64/kvm/hypercalls.c       | 102 ++++++++++++++++++++++++++++++
>  include/kvm/arm_hypercalls.h      |   7 ++
>  include/kvm/arm_psci.h            |  12 ++++
>  7 files changed, 149 insertions(+), 2 deletions(-)
> 
> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index e3b25dc6c367..6e663383d7b4 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -101,6 +101,15 @@ struct kvm_s2_mmu {
>  struct kvm_arch_memory_slot {
>  };
>  
> +/**
> + * struct kvm_smccc_features: Descriptor the hypercall services exposed to the guests
> + *
> + * @std_bmap: Bitmap of standard secure service calls
> + */
> +struct kvm_smccc_features {
> +	u64 std_bmap;

Consider using 'unsigned long' for bitmaps.

> +};
> +
>  struct kvm_arch {
>  	struct kvm_s2_mmu mmu;
>  
> @@ -140,6 +149,9 @@ struct kvm_arch {
>  
>  	u8 pfr0_csv2;
>  	u8 pfr0_csv3;
> +
> +	/* Hypercall features firmware registers' descriptor */
> +	struct kvm_smccc_features smccc_feat;
>  };
>  
>  struct kvm_vcpu_fault_info {
> diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h
> index c1b6ddc02d2f..56e4bc58a355 100644
> --- a/arch/arm64/include/uapi/asm/kvm.h
> +++ b/arch/arm64/include/uapi/asm/kvm.h
> @@ -332,6 +332,15 @@ struct kvm_arm_copy_mte_tags {
>  #define KVM_ARM64_SVE_VLS_WORDS	\
>  	((KVM_ARM64_SVE_VQ_MAX - KVM_ARM64_SVE_VQ_MIN) / 64 + 1)
>  
> +/* Bitmap feature firmware registers */
> +#define KVM_REG_ARM_FW_FEAT_BMAP		(0x0016 << KVM_REG_ARM_COPROC_SHIFT)
> +#define KVM_REG_ARM_FW_FEAT_BMAP_REG(r)		(KVM_REG_ARM64 | KVM_REG_SIZE_U64 | \
> +						KVM_REG_ARM_FW_FEAT_BMAP |	\
> +						((r) & 0xffff))
> +
> +#define KVM_REG_ARM_STD_BMAP			KVM_REG_ARM_FW_FEAT_BMAP_REG(0)
> +#define KVM_REG_ARM_STD_BIT_TRNG_V1_0		BIT(0)

I'm really in two minds about this. Having one bit per service is easy
from an implementation perspective, but is also means that this
disallow fine grained control over which hypercalls are actually
available. If tomorrow TRNG 1.1 adds a new hypercall and that KVM
implements both, how does the selection mechanism works? You will
need a version selector (a la PSCI), which defeats this API somehow
(and renders the name of the #define invalid).

I wonder if a more correct way to look at this is to enumerate the
hypercalls themselves (all 5 of them), though coming up with an
encoding is tricky (RNG32 and RNG64 would clash, for example).

Thoughts?

> +
>  /* Device Control API: ARM VGIC */
>  #define KVM_DEV_ARM_VGIC_GRP_ADDR	0
>  #define KVM_DEV_ARM_VGIC_GRP_DIST_REGS	1
> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> index 523bc934fe2f..a37fadbd617e 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);
> +	kvm_arm_init_hypercalls(kvm);
>  
>  	return ret;
>  out_free_stage2_pgd:
> diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
> index 0d5cca56cbda..8c607199cad1 100644
> --- a/arch/arm64/kvm/guest.c
> +++ b/arch/arm64/kvm/guest.c
> @@ -756,7 +756,9 @@ int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>  
>  	switch (reg->id & KVM_REG_ARM_COPROC_MASK) {
>  	case KVM_REG_ARM_CORE:	return get_core_reg(vcpu, reg);
> -	case KVM_REG_ARM_FW:	return kvm_arm_get_fw_reg(vcpu, reg);
> +	case KVM_REG_ARM_FW:
> +	case KVM_REG_ARM_FW_FEAT_BMAP:
> +		return kvm_arm_get_fw_reg(vcpu, reg);
>  	case KVM_REG_ARM64_SVE:	return get_sve_reg(vcpu, reg);
>  	}
>  
> @@ -774,7 +776,9 @@ int kvm_arm_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>  
>  	switch (reg->id & KVM_REG_ARM_COPROC_MASK) {
>  	case KVM_REG_ARM_CORE:	return set_core_reg(vcpu, reg);
> -	case KVM_REG_ARM_FW:	return kvm_arm_set_fw_reg(vcpu, reg);
> +	case KVM_REG_ARM_FW:
> +	case KVM_REG_ARM_FW_FEAT_BMAP:
> +		return kvm_arm_set_fw_reg(vcpu, reg);
>  	case KVM_REG_ARM64_SVE:	return set_sve_reg(vcpu, reg);
>  	}
>  
> diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c
> index fa6d9378d8e7..cf04b5ee5f56 100644
> --- a/arch/arm64/kvm/hypercalls.c
> +++ b/arch/arm64/kvm/hypercalls.c
> @@ -58,6 +58,53 @@ static void kvm_ptp_get_time(struct kvm_vcpu *vcpu, u64 *val)
>  	val[3] = lower_32_bits(cycles);
>  }
>  
> +/*
> + * List of function-ids that are not gated with the bitmapped feature
> + * firmware registers, and are to be allowed for servicing the call by default.
> + */
> +static const u32 hvc_func_default_allowed_list[] = {
> +	ARM_SMCCC_VERSION_FUNC_ID,
> +	ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
> +	ARM_SMCCC_HV_PV_TIME_FEATURES,
> +	ARM_SMCCC_HV_PV_TIME_ST,
> +	ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID,
> +	ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID,
> +	ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID,
> +};
> +
> +static bool kvm_hvc_call_default_allowed(struct kvm_vcpu *vcpu, u32 func_id)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(hvc_func_default_allowed_list); i++)
> +		if (func_id == hvc_func_default_allowed_list[i])
> +			return true;

Huh, this really is ugly. This array is bound to become bigger over
time, meaning that the average hypercall time is going to increase. At
the very least, this should be turned into a switch/case statement, as
the compile is pretty good at building a search tree (better than this
naive loop, for a start), and we have those everywhere else.

> +
> +	return kvm_psci_func_id_is_valid(vcpu, func_id);
> +}
> +
> +static bool kvm_arm_fw_reg_feat_enabled(u64 reg_bmap, u64 feat_bit)
> +{
> +	return reg_bmap & feat_bit;
> +}

We really don't need to reimplement test_bit().

> +
> +static bool kvm_hvc_call_allowed(struct kvm_vcpu *vcpu, u32 func_id)
> +{
> +	struct kvm_smccc_features *smccc_feat = &vcpu->kvm->arch.smccc_feat;
> +
> +	switch (func_id) {
> +	case ARM_SMCCC_TRNG_VERSION:
> +	case ARM_SMCCC_TRNG_FEATURES:
> +	case ARM_SMCCC_TRNG_GET_UUID:
> +	case ARM_SMCCC_TRNG_RND32:
> +	case ARM_SMCCC_TRNG_RND64:
> +		return kvm_arm_fw_reg_feat_enabled(smccc_feat->std_bmap,
> +						KVM_REG_ARM_STD_BIT_TRNG_V1_0);
> +	default:
> +		return kvm_hvc_call_default_allowed(vcpu, func_id);
> +	}
> +}
> +
>  int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
>  {
>  	u32 func_id = smccc_get_function(vcpu);
> @@ -65,6 +112,9 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
>  	u32 feature;
>  	gpa_t gpa;
>  
> +	if (!kvm_hvc_call_allowed(vcpu, func_id))
> +		goto out;
> +
>  	switch (func_id) {
>  	case ARM_SMCCC_VERSION_FUNC_ID:
>  		val[0] = ARM_SMCCC_VERSION_1_1;
> @@ -155,6 +205,7 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
>  		return kvm_psci_call(vcpu);
>  	}
>  
> +out:
>  	smccc_set_retval(vcpu, val[0], val[1], val[2], val[3]);
>  	return 1;
>  }
> @@ -164,8 +215,16 @@ static const u64 kvm_arm_fw_reg_ids[] = {
>  	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1,
>  	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2,
>  	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3,
> +	KVM_REG_ARM_STD_BMAP,
>  };
>  
> +void kvm_arm_init_hypercalls(struct kvm *kvm)
> +{
> +	struct kvm_smccc_features *smccc_feat = &kvm->arch.smccc_feat;
> +
> +	smccc_feat->std_bmap = KVM_ARM_SMCCC_STD_FEATURES;
> +}
> +
>  int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
>  {
>  	return ARRAY_SIZE(kvm_arm_fw_reg_ids);
> @@ -237,6 +296,7 @@ static int get_kernel_wa_level(u64 regid)
>  
>  int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>  {
> +	struct kvm_smccc_features *smccc_feat = &vcpu->kvm->arch.smccc_feat;
>  	void __user *uaddr = (void __user *)(long)reg->addr;
>  	u64 val;
>  
> @@ -249,6 +309,9 @@ int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>  	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
>  		val = get_kernel_wa_level(reg->id) & KVM_REG_FEATURE_LEVEL_MASK;
>  		break;
> +	case KVM_REG_ARM_STD_BMAP:
> +		val = READ_ONCE(smccc_feat->std_bmap);
> +		break;
>  	default:
>  		return -ENOENT;
>  	}
> @@ -259,6 +322,43 @@ int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>  	return 0;
>  }
>  
> +static int kvm_arm_set_fw_reg_bmap(struct kvm_vcpu *vcpu, u64 reg_id, u64 val)
> +{
> +	int ret = 0;
> +	struct kvm *kvm = vcpu->kvm;
> +	struct kvm_smccc_features *smccc_feat = &kvm->arch.smccc_feat;
> +	u64 *fw_reg_bmap, fw_reg_features;
> +
> +	switch (reg_id) {
> +	case KVM_REG_ARM_STD_BMAP:
> +		fw_reg_bmap = &smccc_feat->std_bmap;
> +		fw_reg_features = KVM_ARM_SMCCC_STD_FEATURES;
> +		break;
> +	default:
> +		return -ENOENT;
> +	}
> +
> +	/* Check for unsupported bit */
> +	if (val & ~fw_reg_features)
> +		return -EINVAL;
> +
> +	mutex_lock(&kvm->lock);
> +
> +	/*
> +	 * If the VM (any vCPU) has already started running, return success
> +	 * if there's no change in the value. Else, return -EBUSY.

No, this should *always* fail if a vcpu has started. Otherwise, you
start allowing hard to spot races.

> +	 */
> +	if (test_bit(KVM_ARCH_FLAG_HAS_RAN_ONCE, &kvm->arch.flags)) {
> +		ret = *fw_reg_bmap != val ? -EBUSY : 0;
> +		goto out;
> +	}
> +
> +	WRITE_ONCE(*fw_reg_bmap, val);

I'm not sure what this WRITE_ONCE guards against. Do you expect
concurrent reads at this stage?

> +out:
> +	mutex_unlock(&kvm->lock);
> +	return ret;
> +}
> +
>  int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>  {
>  	void __user *uaddr = (void __user *)(long)reg->addr;
> @@ -337,6 +437,8 @@ int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>  			return -EINVAL;
>  
>  		return 0;
> +	case KVM_REG_ARM_STD_BMAP:
> +		return kvm_arm_set_fw_reg_bmap(vcpu, reg->id, val);
>  	default:
>  		return -ENOENT;
>  	}
> diff --git a/include/kvm/arm_hypercalls.h b/include/kvm/arm_hypercalls.h
> index 5d38628a8d04..fd3ff350ee9d 100644
> --- a/include/kvm/arm_hypercalls.h
> +++ b/include/kvm/arm_hypercalls.h
> @@ -6,6 +6,12 @@
>  
>  #include <asm/kvm_emulate.h>
>  
> +/* Last valid bits of the bitmapped firmware registers */
> +#define KVM_REG_ARM_STD_BMAP_BIT_MAX		0
> +
> +#define KVM_ARM_SMCCC_STD_FEATURES \
> +	GENMASK_ULL(KVM_REG_ARM_STD_BMAP_BIT_MAX, 0)
> +
>  int kvm_hvc_call_handler(struct kvm_vcpu *vcpu);
>  
>  static inline u32 smccc_get_function(struct kvm_vcpu *vcpu)
> @@ -42,6 +48,7 @@ static inline void smccc_set_retval(struct kvm_vcpu *vcpu,
>  
>  struct kvm_one_reg;
>  
> +void kvm_arm_init_hypercalls(struct kvm *kvm);
>  int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu);
>  int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices);
>  int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
> diff --git a/include/kvm/arm_psci.h b/include/kvm/arm_psci.h
> index 6e55b9283789..d7a87367de56 100644
> --- a/include/kvm/arm_psci.h
> +++ b/include/kvm/arm_psci.h
> @@ -36,6 +36,18 @@ static inline int kvm_psci_version(struct kvm_vcpu *vcpu)
>  	return KVM_ARM_PSCI_0_1;
>  }
>  
> +static inline bool kvm_psci_func_id_is_valid(struct kvm_vcpu *vcpu, u32 func_id)
> +{
> +	/* PSCI 0.1 doesn't comply with the standard SMCCC */
> +	if (kvm_psci_version(vcpu) == KVM_ARM_PSCI_0_1)
> +		return (func_id == KVM_PSCI_FN_CPU_OFF || func_id == KVM_PSCI_FN_CPU_ON);
> +
> +	if (ARM_SMCCC_OWNER_NUM(func_id) == ARM_SMCCC_OWNER_STANDARD &&
> +		ARM_SMCCC_FUNC_NUM(func_id) >= 0 && ARM_SMCCC_FUNC_NUM(func_id) <= 0x1f)
> +		return true;
> +
> +	return false;
> +}

Why the inline function? Do you expect this to be shared with
something else? If not, I'd rather you move it into the caller.

>  
>  int kvm_psci_call(struct kvm_vcpu *vcpu);
>  

Thanks,

	M.

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

_______________________________________________
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] 96+ messages in thread

* Re: [PATCH v5 02/10] KVM: arm64: Setup a framework for hypercall bitmap firmware registers
  2022-04-07  9:06     ` Marc Zyngier
  (?)
@ 2022-04-07 17:24       ` Raghavendra Rao Ananta
  -1 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-07 17:24 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: kvm, Will Deacon, Catalin Marinas, Peter Shier, linux-kernel,
	Paolo Bonzini, kvmarm, linux-arm-kernel

Hi Marc,

On Thu, Apr 7, 2022 at 2:07 AM Marc Zyngier <maz@kernel.org> wrote:
>
> Hi Raghavendra,
>
> On Thu, 07 Apr 2022 02:15:57 +0100,
> Raghavendra Rao Ananta <rananta@google.com> wrote:
> >
> > KVM regularly introduces new hypercall services to the guests without
> > any consent from the userspace. This means, the guests can observe
> > hypercall services in and out as they migrate across various host
> > kernel versions. This could be a major problem if the guest
> > discovered a hypercall, started using it, and after getting migrated
> > to an older kernel realizes that it's no longer available. Depending
> > on how the guest handles the change, there's a potential chance that
> > the guest would just panic.
> >
> > As a result, there's a need for the userspace to elect the services
> > that it wishes the guest to discover. It can elect these services
> > based on the kernels spread across its (migration) fleet. To remedy
> > this, extend the existing firmware psuedo-registers, such as
>
> nit: pseudo
>
> > KVM_REG_ARM_PSCI_VERSION, but by creating a new COPROC register space
> > for all the hypercall services available.
> >
> > These firmware registers are categorized based on the service call
> > owners, but unlike the existing firmware psuedo-registers, they hold
>
> nit: pseudo again
>
> > the features supported in the form of a bitmap.
> >
> > During the VM initialization, the registers are set to upper-limit of
> > the features supported by the corresponding registers. It's expected
> > that the VMMs discover the features provided by each register via
> > GET_ONE_REG, and writeback the desired values using SET_ONE_REG.
>
> nit: write back
>
> > KVM allows this modification only until the VM has started.
> >
> > Some of the standard features are not mapped to any bits of the
> > registers. But since they can recreate the original problem of
> > making it available without userspace's consent, they need to
> > be explicitly added to the hvc_func_default_allowed_list[]. Any
> > function-id that's not enabled via the bitmap, or not listed in
> > hvc_func_default_allowed_list[], will be returned as
> > SMCCC_RET_NOT_SUPPORTED to the guest.
> >
> > Older userspace code can simply ignore the feature and the
> > hypercall services will be exposed unconditionally to the guests,
> > thus ensuring backward compatibility.
> >
> > In this patch, the framework adds the register only for ARM's standard
> > secure services (owner value 4). Currently, this includes support only
> > for ARM True Random Number Generator (TRNG) service, with bit-0 of the
> > register representing mandatory features of v1.0. Other services are
> > momentarily added in the upcoming patches.
> >
> > Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
> > ---
> >  arch/arm64/include/asm/kvm_host.h |  12 ++++
> >  arch/arm64/include/uapi/asm/kvm.h |   9 +++
> >  arch/arm64/kvm/arm.c              |   1 +
> >  arch/arm64/kvm/guest.c            |   8 ++-
> >  arch/arm64/kvm/hypercalls.c       | 102 ++++++++++++++++++++++++++++++
> >  include/kvm/arm_hypercalls.h      |   7 ++
> >  include/kvm/arm_psci.h            |  12 ++++
> >  7 files changed, 149 insertions(+), 2 deletions(-)
> >
> > diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> > index e3b25dc6c367..6e663383d7b4 100644
> > --- a/arch/arm64/include/asm/kvm_host.h
> > +++ b/arch/arm64/include/asm/kvm_host.h
> > @@ -101,6 +101,15 @@ struct kvm_s2_mmu {
> >  struct kvm_arch_memory_slot {
> >  };
> >
> > +/**
> > + * struct kvm_smccc_features: Descriptor the hypercall services exposed to the guests
> > + *
> > + * @std_bmap: Bitmap of standard secure service calls
> > + */
> > +struct kvm_smccc_features {
> > +     u64 std_bmap;
>
> Consider using 'unsigned long' for bitmaps.
>
Sure.

> > +};
> > +
> >  struct kvm_arch {
> >       struct kvm_s2_mmu mmu;
> >
> > @@ -140,6 +149,9 @@ struct kvm_arch {
> >
> >       u8 pfr0_csv2;
> >       u8 pfr0_csv3;
> > +
> > +     /* Hypercall features firmware registers' descriptor */
> > +     struct kvm_smccc_features smccc_feat;
> >  };
> >
> >  struct kvm_vcpu_fault_info {
> > diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h
> > index c1b6ddc02d2f..56e4bc58a355 100644
> > --- a/arch/arm64/include/uapi/asm/kvm.h
> > +++ b/arch/arm64/include/uapi/asm/kvm.h
> > @@ -332,6 +332,15 @@ struct kvm_arm_copy_mte_tags {
> >  #define KVM_ARM64_SVE_VLS_WORDS      \
> >       ((KVM_ARM64_SVE_VQ_MAX - KVM_ARM64_SVE_VQ_MIN) / 64 + 1)
> >
> > +/* Bitmap feature firmware registers */
> > +#define KVM_REG_ARM_FW_FEAT_BMAP             (0x0016 << KVM_REG_ARM_COPROC_SHIFT)
> > +#define KVM_REG_ARM_FW_FEAT_BMAP_REG(r)              (KVM_REG_ARM64 | KVM_REG_SIZE_U64 | \
> > +                                             KVM_REG_ARM_FW_FEAT_BMAP |      \
> > +                                             ((r) & 0xffff))
> > +
> > +#define KVM_REG_ARM_STD_BMAP                 KVM_REG_ARM_FW_FEAT_BMAP_REG(0)
> > +#define KVM_REG_ARM_STD_BIT_TRNG_V1_0                BIT(0)
>
> I'm really in two minds about this. Having one bit per service is easy
> from an implementation perspective, but is also means that this
> disallow fine grained control over which hypercalls are actually
> available. If tomorrow TRNG 1.1 adds a new hypercall and that KVM
> implements both, how does the selection mechanism works? You will
> need a version selector (a la PSCI), which defeats this API somehow
> (and renders the name of the #define invalid).
>
> I wonder if a more correct way to look at this is to enumerate the
> hypercalls themselves (all 5 of them), though coming up with an
> encoding is tricky (RNG32 and RNG64 would clash, for example).
>
> Thoughts?
>
I was on the fence about this too. The TRNG spec (ARM DEN 0098,
Table-4) mentions that v1.0 should have VERSION, FEATURES, GET_UUID,
and RND as mandatory features. Hence, if KVM advertised that it
supports TRNG v1.0, I thought it would be best to expose all or
nothing of v1.0 by guarding them with a single bit.
Broadly, the idea is to have a bit per version. If v1.1 comes along,
we can have another bit for that. If it's not too ugly to implement,
we can be a little more aggressive and ensure that userspace doesn't
enable v1.1 without enabling v1.0.

> > +
> >  /* Device Control API: ARM VGIC */
> >  #define KVM_DEV_ARM_VGIC_GRP_ADDR    0
> >  #define KVM_DEV_ARM_VGIC_GRP_DIST_REGS       1
> > diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> > index 523bc934fe2f..a37fadbd617e 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);
> > +     kvm_arm_init_hypercalls(kvm);
> >
> >       return ret;
> >  out_free_stage2_pgd:
> > diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
> > index 0d5cca56cbda..8c607199cad1 100644
> > --- a/arch/arm64/kvm/guest.c
> > +++ b/arch/arm64/kvm/guest.c
> > @@ -756,7 +756,9 @@ int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> >
> >       switch (reg->id & KVM_REG_ARM_COPROC_MASK) {
> >       case KVM_REG_ARM_CORE:  return get_core_reg(vcpu, reg);
> > -     case KVM_REG_ARM_FW:    return kvm_arm_get_fw_reg(vcpu, reg);
> > +     case KVM_REG_ARM_FW:
> > +     case KVM_REG_ARM_FW_FEAT_BMAP:
> > +             return kvm_arm_get_fw_reg(vcpu, reg);
> >       case KVM_REG_ARM64_SVE: return get_sve_reg(vcpu, reg);
> >       }
> >
> > @@ -774,7 +776,9 @@ int kvm_arm_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> >
> >       switch (reg->id & KVM_REG_ARM_COPROC_MASK) {
> >       case KVM_REG_ARM_CORE:  return set_core_reg(vcpu, reg);
> > -     case KVM_REG_ARM_FW:    return kvm_arm_set_fw_reg(vcpu, reg);
> > +     case KVM_REG_ARM_FW:
> > +     case KVM_REG_ARM_FW_FEAT_BMAP:
> > +             return kvm_arm_set_fw_reg(vcpu, reg);
> >       case KVM_REG_ARM64_SVE: return set_sve_reg(vcpu, reg);
> >       }
> >
> > diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c
> > index fa6d9378d8e7..cf04b5ee5f56 100644
> > --- a/arch/arm64/kvm/hypercalls.c
> > +++ b/arch/arm64/kvm/hypercalls.c
> > @@ -58,6 +58,53 @@ static void kvm_ptp_get_time(struct kvm_vcpu *vcpu, u64 *val)
> >       val[3] = lower_32_bits(cycles);
> >  }
> >
> > +/*
> > + * List of function-ids that are not gated with the bitmapped feature
> > + * firmware registers, and are to be allowed for servicing the call by default.
> > + */
> > +static const u32 hvc_func_default_allowed_list[] = {
> > +     ARM_SMCCC_VERSION_FUNC_ID,
> > +     ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
> > +     ARM_SMCCC_HV_PV_TIME_FEATURES,
> > +     ARM_SMCCC_HV_PV_TIME_ST,
> > +     ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID,
> > +     ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID,
> > +     ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID,
> > +};
> > +
> > +static bool kvm_hvc_call_default_allowed(struct kvm_vcpu *vcpu, u32 func_id)
> > +{
> > +     unsigned int i;
> > +
> > +     for (i = 0; i < ARRAY_SIZE(hvc_func_default_allowed_list); i++)
> > +             if (func_id == hvc_func_default_allowed_list[i])
> > +                     return true;
>
> Huh, this really is ugly. This array is bound to become bigger over
> time, meaning that the average hypercall time is going to increase. At
> the very least, this should be turned into a switch/case statement, as
> the compile is pretty good at building a search tree (better than this
> naive loop, for a start), and we have those everywhere else.
>
Makes sense. I'll make it a switch-case.

> > +
> > +     return kvm_psci_func_id_is_valid(vcpu, func_id);
> > +}
> > +
> > +static bool kvm_arm_fw_reg_feat_enabled(u64 reg_bmap, u64 feat_bit)
> > +{
> > +     return reg_bmap & feat_bit;
> > +}
>
> We really don't need to reimplement test_bit().
>
Right, I forgot about test_bit() :)

> > +
> > +static bool kvm_hvc_call_allowed(struct kvm_vcpu *vcpu, u32 func_id)
> > +{
> > +     struct kvm_smccc_features *smccc_feat = &vcpu->kvm->arch.smccc_feat;
> > +
> > +     switch (func_id) {
> > +     case ARM_SMCCC_TRNG_VERSION:
> > +     case ARM_SMCCC_TRNG_FEATURES:
> > +     case ARM_SMCCC_TRNG_GET_UUID:
> > +     case ARM_SMCCC_TRNG_RND32:
> > +     case ARM_SMCCC_TRNG_RND64:
> > +             return kvm_arm_fw_reg_feat_enabled(smccc_feat->std_bmap,
> > +                                             KVM_REG_ARM_STD_BIT_TRNG_V1_0);
> > +     default:
> > +             return kvm_hvc_call_default_allowed(vcpu, func_id);
> > +     }
> > +}
> > +
> >  int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
> >  {
> >       u32 func_id = smccc_get_function(vcpu);
> > @@ -65,6 +112,9 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
> >       u32 feature;
> >       gpa_t gpa;
> >
> > +     if (!kvm_hvc_call_allowed(vcpu, func_id))
> > +             goto out;
> > +
> >       switch (func_id) {
> >       case ARM_SMCCC_VERSION_FUNC_ID:
> >               val[0] = ARM_SMCCC_VERSION_1_1;
> > @@ -155,6 +205,7 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
> >               return kvm_psci_call(vcpu);
> >       }
> >
> > +out:
> >       smccc_set_retval(vcpu, val[0], val[1], val[2], val[3]);
> >       return 1;
> >  }
> > @@ -164,8 +215,16 @@ static const u64 kvm_arm_fw_reg_ids[] = {
> >       KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1,
> >       KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2,
> >       KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3,
> > +     KVM_REG_ARM_STD_BMAP,
> >  };
> >
> > +void kvm_arm_init_hypercalls(struct kvm *kvm)
> > +{
> > +     struct kvm_smccc_features *smccc_feat = &kvm->arch.smccc_feat;
> > +
> > +     smccc_feat->std_bmap = KVM_ARM_SMCCC_STD_FEATURES;
> > +}
> > +
> >  int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
> >  {
> >       return ARRAY_SIZE(kvm_arm_fw_reg_ids);
> > @@ -237,6 +296,7 @@ static int get_kernel_wa_level(u64 regid)
> >
> >  int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> >  {
> > +     struct kvm_smccc_features *smccc_feat = &vcpu->kvm->arch.smccc_feat;
> >       void __user *uaddr = (void __user *)(long)reg->addr;
> >       u64 val;
> >
> > @@ -249,6 +309,9 @@ int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> >       case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
> >               val = get_kernel_wa_level(reg->id) & KVM_REG_FEATURE_LEVEL_MASK;
> >               break;
> > +     case KVM_REG_ARM_STD_BMAP:
> > +             val = READ_ONCE(smccc_feat->std_bmap);
> > +             break;
> >       default:
> >               return -ENOENT;
> >       }
> > @@ -259,6 +322,43 @@ int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> >       return 0;
> >  }
> >
> > +static int kvm_arm_set_fw_reg_bmap(struct kvm_vcpu *vcpu, u64 reg_id, u64 val)
> > +{
> > +     int ret = 0;
> > +     struct kvm *kvm = vcpu->kvm;
> > +     struct kvm_smccc_features *smccc_feat = &kvm->arch.smccc_feat;
> > +     u64 *fw_reg_bmap, fw_reg_features;
> > +
> > +     switch (reg_id) {
> > +     case KVM_REG_ARM_STD_BMAP:
> > +             fw_reg_bmap = &smccc_feat->std_bmap;
> > +             fw_reg_features = KVM_ARM_SMCCC_STD_FEATURES;
> > +             break;
> > +     default:
> > +             return -ENOENT;
> > +     }
> > +
> > +     /* Check for unsupported bit */
> > +     if (val & ~fw_reg_features)
> > +             return -EINVAL;
> > +
> > +     mutex_lock(&kvm->lock);
> > +
> > +     /*
> > +      * If the VM (any vCPU) has already started running, return success
> > +      * if there's no change in the value. Else, return -EBUSY.
>
> No, this should *always* fail if a vcpu has started. Otherwise, you
> start allowing hard to spot races.
>
The idea came from the fact that userspace could spawn multiple
threads to configure the vCPU registers. Since we don't have the
VM-scoped registers yet, it may be possible that userspace has issued
a KVM_RUN on one of the vCPU, while the others are lagging behind and
still configuring the registers. The slower threads may see -EBUSY and
could panic. But if you feel that it's an overkill and the userspace
should deal with it, we can return EBUSY for all writes after KVM_RUN.

> > +      */
> > +     if (test_bit(KVM_ARCH_FLAG_HAS_RAN_ONCE, &kvm->arch.flags)) {
> > +             ret = *fw_reg_bmap != val ? -EBUSY : 0;
> > +             goto out;
> > +     }
> > +
> > +     WRITE_ONCE(*fw_reg_bmap, val);
>
> I'm not sure what this WRITE_ONCE guards against. Do you expect
> concurrent reads at this stage?
>
Again, the assumption here is that userspace could have multiple
threads reading and writing to these registers. Without the VM scoped
registers in place, we may end up with a read/write to the same memory
location for all the vCPUs.

> > +out:
> > +     mutex_unlock(&kvm->lock);
> > +     return ret;
> > +}
> > +
> >  int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> >  {
> >       void __user *uaddr = (void __user *)(long)reg->addr;
> > @@ -337,6 +437,8 @@ int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> >                       return -EINVAL;
> >
> >               return 0;
> > +     case KVM_REG_ARM_STD_BMAP:
> > +             return kvm_arm_set_fw_reg_bmap(vcpu, reg->id, val);
> >       default:
> >               return -ENOENT;
> >       }
> > diff --git a/include/kvm/arm_hypercalls.h b/include/kvm/arm_hypercalls.h
> > index 5d38628a8d04..fd3ff350ee9d 100644
> > --- a/include/kvm/arm_hypercalls.h
> > +++ b/include/kvm/arm_hypercalls.h
> > @@ -6,6 +6,12 @@
> >
> >  #include <asm/kvm_emulate.h>
> >
> > +/* Last valid bits of the bitmapped firmware registers */
> > +#define KVM_REG_ARM_STD_BMAP_BIT_MAX         0
> > +
> > +#define KVM_ARM_SMCCC_STD_FEATURES \
> > +     GENMASK_ULL(KVM_REG_ARM_STD_BMAP_BIT_MAX, 0)
> > +
> >  int kvm_hvc_call_handler(struct kvm_vcpu *vcpu);
> >
> >  static inline u32 smccc_get_function(struct kvm_vcpu *vcpu)
> > @@ -42,6 +48,7 @@ static inline void smccc_set_retval(struct kvm_vcpu *vcpu,
> >
> >  struct kvm_one_reg;
> >
> > +void kvm_arm_init_hypercalls(struct kvm *kvm);
> >  int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu);
> >  int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices);
> >  int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
> > diff --git a/include/kvm/arm_psci.h b/include/kvm/arm_psci.h
> > index 6e55b9283789..d7a87367de56 100644
> > --- a/include/kvm/arm_psci.h
> > +++ b/include/kvm/arm_psci.h
> > @@ -36,6 +36,18 @@ static inline int kvm_psci_version(struct kvm_vcpu *vcpu)
> >       return KVM_ARM_PSCI_0_1;
> >  }
> >
> > +static inline bool kvm_psci_func_id_is_valid(struct kvm_vcpu *vcpu, u32 func_id)
> > +{
> > +     /* PSCI 0.1 doesn't comply with the standard SMCCC */
> > +     if (kvm_psci_version(vcpu) == KVM_ARM_PSCI_0_1)
> > +             return (func_id == KVM_PSCI_FN_CPU_OFF || func_id == KVM_PSCI_FN_CPU_ON);
> > +
> > +     if (ARM_SMCCC_OWNER_NUM(func_id) == ARM_SMCCC_OWNER_STANDARD &&
> > +             ARM_SMCCC_FUNC_NUM(func_id) >= 0 && ARM_SMCCC_FUNC_NUM(func_id) <= 0x1f)
> > +             return true;
> > +
> > +     return false;
> > +}
>
> Why the inline function? Do you expect this to be shared with
> something else? If not, I'd rather you move it into the caller.
>
Well, no plans to share as of yet. Will move it to psci.c

> >
> >  int kvm_psci_call(struct kvm_vcpu *vcpu);
> >
>

Thanks for the review, Marc. I'll fix all the other nits.

Regards,
Raghavendra

> Thanks,
>
>         M.
>
> --
> Without deviation from the norm, progress is not possible.
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

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

* Re: [PATCH v5 02/10] KVM: arm64: Setup a framework for hypercall bitmap firmware registers
@ 2022-04-07 17:24       ` Raghavendra Rao Ananta
  0 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-07 17:24 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: Andrew Jones, James Morse, Alexandru Elisei, Suzuki K Poulose,
	Paolo Bonzini, Catalin Marinas, Will Deacon, Peter Shier,
	Ricardo Koller, Oliver Upton, Reiji Watanabe, Jing Zhang,
	linux-arm-kernel, kvmarm, linux-kernel, kvm

Hi Marc,

On Thu, Apr 7, 2022 at 2:07 AM Marc Zyngier <maz@kernel.org> wrote:
>
> Hi Raghavendra,
>
> On Thu, 07 Apr 2022 02:15:57 +0100,
> Raghavendra Rao Ananta <rananta@google.com> wrote:
> >
> > KVM regularly introduces new hypercall services to the guests without
> > any consent from the userspace. This means, the guests can observe
> > hypercall services in and out as they migrate across various host
> > kernel versions. This could be a major problem if the guest
> > discovered a hypercall, started using it, and after getting migrated
> > to an older kernel realizes that it's no longer available. Depending
> > on how the guest handles the change, there's a potential chance that
> > the guest would just panic.
> >
> > As a result, there's a need for the userspace to elect the services
> > that it wishes the guest to discover. It can elect these services
> > based on the kernels spread across its (migration) fleet. To remedy
> > this, extend the existing firmware psuedo-registers, such as
>
> nit: pseudo
>
> > KVM_REG_ARM_PSCI_VERSION, but by creating a new COPROC register space
> > for all the hypercall services available.
> >
> > These firmware registers are categorized based on the service call
> > owners, but unlike the existing firmware psuedo-registers, they hold
>
> nit: pseudo again
>
> > the features supported in the form of a bitmap.
> >
> > During the VM initialization, the registers are set to upper-limit of
> > the features supported by the corresponding registers. It's expected
> > that the VMMs discover the features provided by each register via
> > GET_ONE_REG, and writeback the desired values using SET_ONE_REG.
>
> nit: write back
>
> > KVM allows this modification only until the VM has started.
> >
> > Some of the standard features are not mapped to any bits of the
> > registers. But since they can recreate the original problem of
> > making it available without userspace's consent, they need to
> > be explicitly added to the hvc_func_default_allowed_list[]. Any
> > function-id that's not enabled via the bitmap, or not listed in
> > hvc_func_default_allowed_list[], will be returned as
> > SMCCC_RET_NOT_SUPPORTED to the guest.
> >
> > Older userspace code can simply ignore the feature and the
> > hypercall services will be exposed unconditionally to the guests,
> > thus ensuring backward compatibility.
> >
> > In this patch, the framework adds the register only for ARM's standard
> > secure services (owner value 4). Currently, this includes support only
> > for ARM True Random Number Generator (TRNG) service, with bit-0 of the
> > register representing mandatory features of v1.0. Other services are
> > momentarily added in the upcoming patches.
> >
> > Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
> > ---
> >  arch/arm64/include/asm/kvm_host.h |  12 ++++
> >  arch/arm64/include/uapi/asm/kvm.h |   9 +++
> >  arch/arm64/kvm/arm.c              |   1 +
> >  arch/arm64/kvm/guest.c            |   8 ++-
> >  arch/arm64/kvm/hypercalls.c       | 102 ++++++++++++++++++++++++++++++
> >  include/kvm/arm_hypercalls.h      |   7 ++
> >  include/kvm/arm_psci.h            |  12 ++++
> >  7 files changed, 149 insertions(+), 2 deletions(-)
> >
> > diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> > index e3b25dc6c367..6e663383d7b4 100644
> > --- a/arch/arm64/include/asm/kvm_host.h
> > +++ b/arch/arm64/include/asm/kvm_host.h
> > @@ -101,6 +101,15 @@ struct kvm_s2_mmu {
> >  struct kvm_arch_memory_slot {
> >  };
> >
> > +/**
> > + * struct kvm_smccc_features: Descriptor the hypercall services exposed to the guests
> > + *
> > + * @std_bmap: Bitmap of standard secure service calls
> > + */
> > +struct kvm_smccc_features {
> > +     u64 std_bmap;
>
> Consider using 'unsigned long' for bitmaps.
>
Sure.

> > +};
> > +
> >  struct kvm_arch {
> >       struct kvm_s2_mmu mmu;
> >
> > @@ -140,6 +149,9 @@ struct kvm_arch {
> >
> >       u8 pfr0_csv2;
> >       u8 pfr0_csv3;
> > +
> > +     /* Hypercall features firmware registers' descriptor */
> > +     struct kvm_smccc_features smccc_feat;
> >  };
> >
> >  struct kvm_vcpu_fault_info {
> > diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h
> > index c1b6ddc02d2f..56e4bc58a355 100644
> > --- a/arch/arm64/include/uapi/asm/kvm.h
> > +++ b/arch/arm64/include/uapi/asm/kvm.h
> > @@ -332,6 +332,15 @@ struct kvm_arm_copy_mte_tags {
> >  #define KVM_ARM64_SVE_VLS_WORDS      \
> >       ((KVM_ARM64_SVE_VQ_MAX - KVM_ARM64_SVE_VQ_MIN) / 64 + 1)
> >
> > +/* Bitmap feature firmware registers */
> > +#define KVM_REG_ARM_FW_FEAT_BMAP             (0x0016 << KVM_REG_ARM_COPROC_SHIFT)
> > +#define KVM_REG_ARM_FW_FEAT_BMAP_REG(r)              (KVM_REG_ARM64 | KVM_REG_SIZE_U64 | \
> > +                                             KVM_REG_ARM_FW_FEAT_BMAP |      \
> > +                                             ((r) & 0xffff))
> > +
> > +#define KVM_REG_ARM_STD_BMAP                 KVM_REG_ARM_FW_FEAT_BMAP_REG(0)
> > +#define KVM_REG_ARM_STD_BIT_TRNG_V1_0                BIT(0)
>
> I'm really in two minds about this. Having one bit per service is easy
> from an implementation perspective, but is also means that this
> disallow fine grained control over which hypercalls are actually
> available. If tomorrow TRNG 1.1 adds a new hypercall and that KVM
> implements both, how does the selection mechanism works? You will
> need a version selector (a la PSCI), which defeats this API somehow
> (and renders the name of the #define invalid).
>
> I wonder if a more correct way to look at this is to enumerate the
> hypercalls themselves (all 5 of them), though coming up with an
> encoding is tricky (RNG32 and RNG64 would clash, for example).
>
> Thoughts?
>
I was on the fence about this too. The TRNG spec (ARM DEN 0098,
Table-4) mentions that v1.0 should have VERSION, FEATURES, GET_UUID,
and RND as mandatory features. Hence, if KVM advertised that it
supports TRNG v1.0, I thought it would be best to expose all or
nothing of v1.0 by guarding them with a single bit.
Broadly, the idea is to have a bit per version. If v1.1 comes along,
we can have another bit for that. If it's not too ugly to implement,
we can be a little more aggressive and ensure that userspace doesn't
enable v1.1 without enabling v1.0.

> > +
> >  /* Device Control API: ARM VGIC */
> >  #define KVM_DEV_ARM_VGIC_GRP_ADDR    0
> >  #define KVM_DEV_ARM_VGIC_GRP_DIST_REGS       1
> > diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> > index 523bc934fe2f..a37fadbd617e 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);
> > +     kvm_arm_init_hypercalls(kvm);
> >
> >       return ret;
> >  out_free_stage2_pgd:
> > diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
> > index 0d5cca56cbda..8c607199cad1 100644
> > --- a/arch/arm64/kvm/guest.c
> > +++ b/arch/arm64/kvm/guest.c
> > @@ -756,7 +756,9 @@ int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> >
> >       switch (reg->id & KVM_REG_ARM_COPROC_MASK) {
> >       case KVM_REG_ARM_CORE:  return get_core_reg(vcpu, reg);
> > -     case KVM_REG_ARM_FW:    return kvm_arm_get_fw_reg(vcpu, reg);
> > +     case KVM_REG_ARM_FW:
> > +     case KVM_REG_ARM_FW_FEAT_BMAP:
> > +             return kvm_arm_get_fw_reg(vcpu, reg);
> >       case KVM_REG_ARM64_SVE: return get_sve_reg(vcpu, reg);
> >       }
> >
> > @@ -774,7 +776,9 @@ int kvm_arm_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> >
> >       switch (reg->id & KVM_REG_ARM_COPROC_MASK) {
> >       case KVM_REG_ARM_CORE:  return set_core_reg(vcpu, reg);
> > -     case KVM_REG_ARM_FW:    return kvm_arm_set_fw_reg(vcpu, reg);
> > +     case KVM_REG_ARM_FW:
> > +     case KVM_REG_ARM_FW_FEAT_BMAP:
> > +             return kvm_arm_set_fw_reg(vcpu, reg);
> >       case KVM_REG_ARM64_SVE: return set_sve_reg(vcpu, reg);
> >       }
> >
> > diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c
> > index fa6d9378d8e7..cf04b5ee5f56 100644
> > --- a/arch/arm64/kvm/hypercalls.c
> > +++ b/arch/arm64/kvm/hypercalls.c
> > @@ -58,6 +58,53 @@ static void kvm_ptp_get_time(struct kvm_vcpu *vcpu, u64 *val)
> >       val[3] = lower_32_bits(cycles);
> >  }
> >
> > +/*
> > + * List of function-ids that are not gated with the bitmapped feature
> > + * firmware registers, and are to be allowed for servicing the call by default.
> > + */
> > +static const u32 hvc_func_default_allowed_list[] = {
> > +     ARM_SMCCC_VERSION_FUNC_ID,
> > +     ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
> > +     ARM_SMCCC_HV_PV_TIME_FEATURES,
> > +     ARM_SMCCC_HV_PV_TIME_ST,
> > +     ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID,
> > +     ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID,
> > +     ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID,
> > +};
> > +
> > +static bool kvm_hvc_call_default_allowed(struct kvm_vcpu *vcpu, u32 func_id)
> > +{
> > +     unsigned int i;
> > +
> > +     for (i = 0; i < ARRAY_SIZE(hvc_func_default_allowed_list); i++)
> > +             if (func_id == hvc_func_default_allowed_list[i])
> > +                     return true;
>
> Huh, this really is ugly. This array is bound to become bigger over
> time, meaning that the average hypercall time is going to increase. At
> the very least, this should be turned into a switch/case statement, as
> the compile is pretty good at building a search tree (better than this
> naive loop, for a start), and we have those everywhere else.
>
Makes sense. I'll make it a switch-case.

> > +
> > +     return kvm_psci_func_id_is_valid(vcpu, func_id);
> > +}
> > +
> > +static bool kvm_arm_fw_reg_feat_enabled(u64 reg_bmap, u64 feat_bit)
> > +{
> > +     return reg_bmap & feat_bit;
> > +}
>
> We really don't need to reimplement test_bit().
>
Right, I forgot about test_bit() :)

> > +
> > +static bool kvm_hvc_call_allowed(struct kvm_vcpu *vcpu, u32 func_id)
> > +{
> > +     struct kvm_smccc_features *smccc_feat = &vcpu->kvm->arch.smccc_feat;
> > +
> > +     switch (func_id) {
> > +     case ARM_SMCCC_TRNG_VERSION:
> > +     case ARM_SMCCC_TRNG_FEATURES:
> > +     case ARM_SMCCC_TRNG_GET_UUID:
> > +     case ARM_SMCCC_TRNG_RND32:
> > +     case ARM_SMCCC_TRNG_RND64:
> > +             return kvm_arm_fw_reg_feat_enabled(smccc_feat->std_bmap,
> > +                                             KVM_REG_ARM_STD_BIT_TRNG_V1_0);
> > +     default:
> > +             return kvm_hvc_call_default_allowed(vcpu, func_id);
> > +     }
> > +}
> > +
> >  int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
> >  {
> >       u32 func_id = smccc_get_function(vcpu);
> > @@ -65,6 +112,9 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
> >       u32 feature;
> >       gpa_t gpa;
> >
> > +     if (!kvm_hvc_call_allowed(vcpu, func_id))
> > +             goto out;
> > +
> >       switch (func_id) {
> >       case ARM_SMCCC_VERSION_FUNC_ID:
> >               val[0] = ARM_SMCCC_VERSION_1_1;
> > @@ -155,6 +205,7 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
> >               return kvm_psci_call(vcpu);
> >       }
> >
> > +out:
> >       smccc_set_retval(vcpu, val[0], val[1], val[2], val[3]);
> >       return 1;
> >  }
> > @@ -164,8 +215,16 @@ static const u64 kvm_arm_fw_reg_ids[] = {
> >       KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1,
> >       KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2,
> >       KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3,
> > +     KVM_REG_ARM_STD_BMAP,
> >  };
> >
> > +void kvm_arm_init_hypercalls(struct kvm *kvm)
> > +{
> > +     struct kvm_smccc_features *smccc_feat = &kvm->arch.smccc_feat;
> > +
> > +     smccc_feat->std_bmap = KVM_ARM_SMCCC_STD_FEATURES;
> > +}
> > +
> >  int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
> >  {
> >       return ARRAY_SIZE(kvm_arm_fw_reg_ids);
> > @@ -237,6 +296,7 @@ static int get_kernel_wa_level(u64 regid)
> >
> >  int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> >  {
> > +     struct kvm_smccc_features *smccc_feat = &vcpu->kvm->arch.smccc_feat;
> >       void __user *uaddr = (void __user *)(long)reg->addr;
> >       u64 val;
> >
> > @@ -249,6 +309,9 @@ int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> >       case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
> >               val = get_kernel_wa_level(reg->id) & KVM_REG_FEATURE_LEVEL_MASK;
> >               break;
> > +     case KVM_REG_ARM_STD_BMAP:
> > +             val = READ_ONCE(smccc_feat->std_bmap);
> > +             break;
> >       default:
> >               return -ENOENT;
> >       }
> > @@ -259,6 +322,43 @@ int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> >       return 0;
> >  }
> >
> > +static int kvm_arm_set_fw_reg_bmap(struct kvm_vcpu *vcpu, u64 reg_id, u64 val)
> > +{
> > +     int ret = 0;
> > +     struct kvm *kvm = vcpu->kvm;
> > +     struct kvm_smccc_features *smccc_feat = &kvm->arch.smccc_feat;
> > +     u64 *fw_reg_bmap, fw_reg_features;
> > +
> > +     switch (reg_id) {
> > +     case KVM_REG_ARM_STD_BMAP:
> > +             fw_reg_bmap = &smccc_feat->std_bmap;
> > +             fw_reg_features = KVM_ARM_SMCCC_STD_FEATURES;
> > +             break;
> > +     default:
> > +             return -ENOENT;
> > +     }
> > +
> > +     /* Check for unsupported bit */
> > +     if (val & ~fw_reg_features)
> > +             return -EINVAL;
> > +
> > +     mutex_lock(&kvm->lock);
> > +
> > +     /*
> > +      * If the VM (any vCPU) has already started running, return success
> > +      * if there's no change in the value. Else, return -EBUSY.
>
> No, this should *always* fail if a vcpu has started. Otherwise, you
> start allowing hard to spot races.
>
The idea came from the fact that userspace could spawn multiple
threads to configure the vCPU registers. Since we don't have the
VM-scoped registers yet, it may be possible that userspace has issued
a KVM_RUN on one of the vCPU, while the others are lagging behind and
still configuring the registers. The slower threads may see -EBUSY and
could panic. But if you feel that it's an overkill and the userspace
should deal with it, we can return EBUSY for all writes after KVM_RUN.

> > +      */
> > +     if (test_bit(KVM_ARCH_FLAG_HAS_RAN_ONCE, &kvm->arch.flags)) {
> > +             ret = *fw_reg_bmap != val ? -EBUSY : 0;
> > +             goto out;
> > +     }
> > +
> > +     WRITE_ONCE(*fw_reg_bmap, val);
>
> I'm not sure what this WRITE_ONCE guards against. Do you expect
> concurrent reads at this stage?
>
Again, the assumption here is that userspace could have multiple
threads reading and writing to these registers. Without the VM scoped
registers in place, we may end up with a read/write to the same memory
location for all the vCPUs.

> > +out:
> > +     mutex_unlock(&kvm->lock);
> > +     return ret;
> > +}
> > +
> >  int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> >  {
> >       void __user *uaddr = (void __user *)(long)reg->addr;
> > @@ -337,6 +437,8 @@ int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> >                       return -EINVAL;
> >
> >               return 0;
> > +     case KVM_REG_ARM_STD_BMAP:
> > +             return kvm_arm_set_fw_reg_bmap(vcpu, reg->id, val);
> >       default:
> >               return -ENOENT;
> >       }
> > diff --git a/include/kvm/arm_hypercalls.h b/include/kvm/arm_hypercalls.h
> > index 5d38628a8d04..fd3ff350ee9d 100644
> > --- a/include/kvm/arm_hypercalls.h
> > +++ b/include/kvm/arm_hypercalls.h
> > @@ -6,6 +6,12 @@
> >
> >  #include <asm/kvm_emulate.h>
> >
> > +/* Last valid bits of the bitmapped firmware registers */
> > +#define KVM_REG_ARM_STD_BMAP_BIT_MAX         0
> > +
> > +#define KVM_ARM_SMCCC_STD_FEATURES \
> > +     GENMASK_ULL(KVM_REG_ARM_STD_BMAP_BIT_MAX, 0)
> > +
> >  int kvm_hvc_call_handler(struct kvm_vcpu *vcpu);
> >
> >  static inline u32 smccc_get_function(struct kvm_vcpu *vcpu)
> > @@ -42,6 +48,7 @@ static inline void smccc_set_retval(struct kvm_vcpu *vcpu,
> >
> >  struct kvm_one_reg;
> >
> > +void kvm_arm_init_hypercalls(struct kvm *kvm);
> >  int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu);
> >  int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices);
> >  int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
> > diff --git a/include/kvm/arm_psci.h b/include/kvm/arm_psci.h
> > index 6e55b9283789..d7a87367de56 100644
> > --- a/include/kvm/arm_psci.h
> > +++ b/include/kvm/arm_psci.h
> > @@ -36,6 +36,18 @@ static inline int kvm_psci_version(struct kvm_vcpu *vcpu)
> >       return KVM_ARM_PSCI_0_1;
> >  }
> >
> > +static inline bool kvm_psci_func_id_is_valid(struct kvm_vcpu *vcpu, u32 func_id)
> > +{
> > +     /* PSCI 0.1 doesn't comply with the standard SMCCC */
> > +     if (kvm_psci_version(vcpu) == KVM_ARM_PSCI_0_1)
> > +             return (func_id == KVM_PSCI_FN_CPU_OFF || func_id == KVM_PSCI_FN_CPU_ON);
> > +
> > +     if (ARM_SMCCC_OWNER_NUM(func_id) == ARM_SMCCC_OWNER_STANDARD &&
> > +             ARM_SMCCC_FUNC_NUM(func_id) >= 0 && ARM_SMCCC_FUNC_NUM(func_id) <= 0x1f)
> > +             return true;
> > +
> > +     return false;
> > +}
>
> Why the inline function? Do you expect this to be shared with
> something else? If not, I'd rather you move it into the caller.
>
Well, no plans to share as of yet. Will move it to psci.c

> >
> >  int kvm_psci_call(struct kvm_vcpu *vcpu);
> >
>

Thanks for the review, Marc. I'll fix all the other nits.

Regards,
Raghavendra

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

_______________________________________________
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] 96+ messages in thread

* Re: [PATCH v5 02/10] KVM: arm64: Setup a framework for hypercall bitmap firmware registers
@ 2022-04-07 17:24       ` Raghavendra Rao Ananta
  0 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-07 17:24 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: Andrew Jones, James Morse, Alexandru Elisei, Suzuki K Poulose,
	Paolo Bonzini, Catalin Marinas, Will Deacon, Peter Shier,
	Ricardo Koller, Oliver Upton, Reiji Watanabe, Jing Zhang,
	linux-arm-kernel, kvmarm, linux-kernel, kvm

Hi Marc,

On Thu, Apr 7, 2022 at 2:07 AM Marc Zyngier <maz@kernel.org> wrote:
>
> Hi Raghavendra,
>
> On Thu, 07 Apr 2022 02:15:57 +0100,
> Raghavendra Rao Ananta <rananta@google.com> wrote:
> >
> > KVM regularly introduces new hypercall services to the guests without
> > any consent from the userspace. This means, the guests can observe
> > hypercall services in and out as they migrate across various host
> > kernel versions. This could be a major problem if the guest
> > discovered a hypercall, started using it, and after getting migrated
> > to an older kernel realizes that it's no longer available. Depending
> > on how the guest handles the change, there's a potential chance that
> > the guest would just panic.
> >
> > As a result, there's a need for the userspace to elect the services
> > that it wishes the guest to discover. It can elect these services
> > based on the kernels spread across its (migration) fleet. To remedy
> > this, extend the existing firmware psuedo-registers, such as
>
> nit: pseudo
>
> > KVM_REG_ARM_PSCI_VERSION, but by creating a new COPROC register space
> > for all the hypercall services available.
> >
> > These firmware registers are categorized based on the service call
> > owners, but unlike the existing firmware psuedo-registers, they hold
>
> nit: pseudo again
>
> > the features supported in the form of a bitmap.
> >
> > During the VM initialization, the registers are set to upper-limit of
> > the features supported by the corresponding registers. It's expected
> > that the VMMs discover the features provided by each register via
> > GET_ONE_REG, and writeback the desired values using SET_ONE_REG.
>
> nit: write back
>
> > KVM allows this modification only until the VM has started.
> >
> > Some of the standard features are not mapped to any bits of the
> > registers. But since they can recreate the original problem of
> > making it available without userspace's consent, they need to
> > be explicitly added to the hvc_func_default_allowed_list[]. Any
> > function-id that's not enabled via the bitmap, or not listed in
> > hvc_func_default_allowed_list[], will be returned as
> > SMCCC_RET_NOT_SUPPORTED to the guest.
> >
> > Older userspace code can simply ignore the feature and the
> > hypercall services will be exposed unconditionally to the guests,
> > thus ensuring backward compatibility.
> >
> > In this patch, the framework adds the register only for ARM's standard
> > secure services (owner value 4). Currently, this includes support only
> > for ARM True Random Number Generator (TRNG) service, with bit-0 of the
> > register representing mandatory features of v1.0. Other services are
> > momentarily added in the upcoming patches.
> >
> > Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
> > ---
> >  arch/arm64/include/asm/kvm_host.h |  12 ++++
> >  arch/arm64/include/uapi/asm/kvm.h |   9 +++
> >  arch/arm64/kvm/arm.c              |   1 +
> >  arch/arm64/kvm/guest.c            |   8 ++-
> >  arch/arm64/kvm/hypercalls.c       | 102 ++++++++++++++++++++++++++++++
> >  include/kvm/arm_hypercalls.h      |   7 ++
> >  include/kvm/arm_psci.h            |  12 ++++
> >  7 files changed, 149 insertions(+), 2 deletions(-)
> >
> > diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> > index e3b25dc6c367..6e663383d7b4 100644
> > --- a/arch/arm64/include/asm/kvm_host.h
> > +++ b/arch/arm64/include/asm/kvm_host.h
> > @@ -101,6 +101,15 @@ struct kvm_s2_mmu {
> >  struct kvm_arch_memory_slot {
> >  };
> >
> > +/**
> > + * struct kvm_smccc_features: Descriptor the hypercall services exposed to the guests
> > + *
> > + * @std_bmap: Bitmap of standard secure service calls
> > + */
> > +struct kvm_smccc_features {
> > +     u64 std_bmap;
>
> Consider using 'unsigned long' for bitmaps.
>
Sure.

> > +};
> > +
> >  struct kvm_arch {
> >       struct kvm_s2_mmu mmu;
> >
> > @@ -140,6 +149,9 @@ struct kvm_arch {
> >
> >       u8 pfr0_csv2;
> >       u8 pfr0_csv3;
> > +
> > +     /* Hypercall features firmware registers' descriptor */
> > +     struct kvm_smccc_features smccc_feat;
> >  };
> >
> >  struct kvm_vcpu_fault_info {
> > diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h
> > index c1b6ddc02d2f..56e4bc58a355 100644
> > --- a/arch/arm64/include/uapi/asm/kvm.h
> > +++ b/arch/arm64/include/uapi/asm/kvm.h
> > @@ -332,6 +332,15 @@ struct kvm_arm_copy_mte_tags {
> >  #define KVM_ARM64_SVE_VLS_WORDS      \
> >       ((KVM_ARM64_SVE_VQ_MAX - KVM_ARM64_SVE_VQ_MIN) / 64 + 1)
> >
> > +/* Bitmap feature firmware registers */
> > +#define KVM_REG_ARM_FW_FEAT_BMAP             (0x0016 << KVM_REG_ARM_COPROC_SHIFT)
> > +#define KVM_REG_ARM_FW_FEAT_BMAP_REG(r)              (KVM_REG_ARM64 | KVM_REG_SIZE_U64 | \
> > +                                             KVM_REG_ARM_FW_FEAT_BMAP |      \
> > +                                             ((r) & 0xffff))
> > +
> > +#define KVM_REG_ARM_STD_BMAP                 KVM_REG_ARM_FW_FEAT_BMAP_REG(0)
> > +#define KVM_REG_ARM_STD_BIT_TRNG_V1_0                BIT(0)
>
> I'm really in two minds about this. Having one bit per service is easy
> from an implementation perspective, but is also means that this
> disallow fine grained control over which hypercalls are actually
> available. If tomorrow TRNG 1.1 adds a new hypercall and that KVM
> implements both, how does the selection mechanism works? You will
> need a version selector (a la PSCI), which defeats this API somehow
> (and renders the name of the #define invalid).
>
> I wonder if a more correct way to look at this is to enumerate the
> hypercalls themselves (all 5 of them), though coming up with an
> encoding is tricky (RNG32 and RNG64 would clash, for example).
>
> Thoughts?
>
I was on the fence about this too. The TRNG spec (ARM DEN 0098,
Table-4) mentions that v1.0 should have VERSION, FEATURES, GET_UUID,
and RND as mandatory features. Hence, if KVM advertised that it
supports TRNG v1.0, I thought it would be best to expose all or
nothing of v1.0 by guarding them with a single bit.
Broadly, the idea is to have a bit per version. If v1.1 comes along,
we can have another bit for that. If it's not too ugly to implement,
we can be a little more aggressive and ensure that userspace doesn't
enable v1.1 without enabling v1.0.

> > +
> >  /* Device Control API: ARM VGIC */
> >  #define KVM_DEV_ARM_VGIC_GRP_ADDR    0
> >  #define KVM_DEV_ARM_VGIC_GRP_DIST_REGS       1
> > diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> > index 523bc934fe2f..a37fadbd617e 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);
> > +     kvm_arm_init_hypercalls(kvm);
> >
> >       return ret;
> >  out_free_stage2_pgd:
> > diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
> > index 0d5cca56cbda..8c607199cad1 100644
> > --- a/arch/arm64/kvm/guest.c
> > +++ b/arch/arm64/kvm/guest.c
> > @@ -756,7 +756,9 @@ int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> >
> >       switch (reg->id & KVM_REG_ARM_COPROC_MASK) {
> >       case KVM_REG_ARM_CORE:  return get_core_reg(vcpu, reg);
> > -     case KVM_REG_ARM_FW:    return kvm_arm_get_fw_reg(vcpu, reg);
> > +     case KVM_REG_ARM_FW:
> > +     case KVM_REG_ARM_FW_FEAT_BMAP:
> > +             return kvm_arm_get_fw_reg(vcpu, reg);
> >       case KVM_REG_ARM64_SVE: return get_sve_reg(vcpu, reg);
> >       }
> >
> > @@ -774,7 +776,9 @@ int kvm_arm_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> >
> >       switch (reg->id & KVM_REG_ARM_COPROC_MASK) {
> >       case KVM_REG_ARM_CORE:  return set_core_reg(vcpu, reg);
> > -     case KVM_REG_ARM_FW:    return kvm_arm_set_fw_reg(vcpu, reg);
> > +     case KVM_REG_ARM_FW:
> > +     case KVM_REG_ARM_FW_FEAT_BMAP:
> > +             return kvm_arm_set_fw_reg(vcpu, reg);
> >       case KVM_REG_ARM64_SVE: return set_sve_reg(vcpu, reg);
> >       }
> >
> > diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c
> > index fa6d9378d8e7..cf04b5ee5f56 100644
> > --- a/arch/arm64/kvm/hypercalls.c
> > +++ b/arch/arm64/kvm/hypercalls.c
> > @@ -58,6 +58,53 @@ static void kvm_ptp_get_time(struct kvm_vcpu *vcpu, u64 *val)
> >       val[3] = lower_32_bits(cycles);
> >  }
> >
> > +/*
> > + * List of function-ids that are not gated with the bitmapped feature
> > + * firmware registers, and are to be allowed for servicing the call by default.
> > + */
> > +static const u32 hvc_func_default_allowed_list[] = {
> > +     ARM_SMCCC_VERSION_FUNC_ID,
> > +     ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
> > +     ARM_SMCCC_HV_PV_TIME_FEATURES,
> > +     ARM_SMCCC_HV_PV_TIME_ST,
> > +     ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID,
> > +     ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID,
> > +     ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID,
> > +};
> > +
> > +static bool kvm_hvc_call_default_allowed(struct kvm_vcpu *vcpu, u32 func_id)
> > +{
> > +     unsigned int i;
> > +
> > +     for (i = 0; i < ARRAY_SIZE(hvc_func_default_allowed_list); i++)
> > +             if (func_id == hvc_func_default_allowed_list[i])
> > +                     return true;
>
> Huh, this really is ugly. This array is bound to become bigger over
> time, meaning that the average hypercall time is going to increase. At
> the very least, this should be turned into a switch/case statement, as
> the compile is pretty good at building a search tree (better than this
> naive loop, for a start), and we have those everywhere else.
>
Makes sense. I'll make it a switch-case.

> > +
> > +     return kvm_psci_func_id_is_valid(vcpu, func_id);
> > +}
> > +
> > +static bool kvm_arm_fw_reg_feat_enabled(u64 reg_bmap, u64 feat_bit)
> > +{
> > +     return reg_bmap & feat_bit;
> > +}
>
> We really don't need to reimplement test_bit().
>
Right, I forgot about test_bit() :)

> > +
> > +static bool kvm_hvc_call_allowed(struct kvm_vcpu *vcpu, u32 func_id)
> > +{
> > +     struct kvm_smccc_features *smccc_feat = &vcpu->kvm->arch.smccc_feat;
> > +
> > +     switch (func_id) {
> > +     case ARM_SMCCC_TRNG_VERSION:
> > +     case ARM_SMCCC_TRNG_FEATURES:
> > +     case ARM_SMCCC_TRNG_GET_UUID:
> > +     case ARM_SMCCC_TRNG_RND32:
> > +     case ARM_SMCCC_TRNG_RND64:
> > +             return kvm_arm_fw_reg_feat_enabled(smccc_feat->std_bmap,
> > +                                             KVM_REG_ARM_STD_BIT_TRNG_V1_0);
> > +     default:
> > +             return kvm_hvc_call_default_allowed(vcpu, func_id);
> > +     }
> > +}
> > +
> >  int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
> >  {
> >       u32 func_id = smccc_get_function(vcpu);
> > @@ -65,6 +112,9 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
> >       u32 feature;
> >       gpa_t gpa;
> >
> > +     if (!kvm_hvc_call_allowed(vcpu, func_id))
> > +             goto out;
> > +
> >       switch (func_id) {
> >       case ARM_SMCCC_VERSION_FUNC_ID:
> >               val[0] = ARM_SMCCC_VERSION_1_1;
> > @@ -155,6 +205,7 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
> >               return kvm_psci_call(vcpu);
> >       }
> >
> > +out:
> >       smccc_set_retval(vcpu, val[0], val[1], val[2], val[3]);
> >       return 1;
> >  }
> > @@ -164,8 +215,16 @@ static const u64 kvm_arm_fw_reg_ids[] = {
> >       KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1,
> >       KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2,
> >       KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3,
> > +     KVM_REG_ARM_STD_BMAP,
> >  };
> >
> > +void kvm_arm_init_hypercalls(struct kvm *kvm)
> > +{
> > +     struct kvm_smccc_features *smccc_feat = &kvm->arch.smccc_feat;
> > +
> > +     smccc_feat->std_bmap = KVM_ARM_SMCCC_STD_FEATURES;
> > +}
> > +
> >  int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
> >  {
> >       return ARRAY_SIZE(kvm_arm_fw_reg_ids);
> > @@ -237,6 +296,7 @@ static int get_kernel_wa_level(u64 regid)
> >
> >  int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> >  {
> > +     struct kvm_smccc_features *smccc_feat = &vcpu->kvm->arch.smccc_feat;
> >       void __user *uaddr = (void __user *)(long)reg->addr;
> >       u64 val;
> >
> > @@ -249,6 +309,9 @@ int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> >       case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
> >               val = get_kernel_wa_level(reg->id) & KVM_REG_FEATURE_LEVEL_MASK;
> >               break;
> > +     case KVM_REG_ARM_STD_BMAP:
> > +             val = READ_ONCE(smccc_feat->std_bmap);
> > +             break;
> >       default:
> >               return -ENOENT;
> >       }
> > @@ -259,6 +322,43 @@ int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> >       return 0;
> >  }
> >
> > +static int kvm_arm_set_fw_reg_bmap(struct kvm_vcpu *vcpu, u64 reg_id, u64 val)
> > +{
> > +     int ret = 0;
> > +     struct kvm *kvm = vcpu->kvm;
> > +     struct kvm_smccc_features *smccc_feat = &kvm->arch.smccc_feat;
> > +     u64 *fw_reg_bmap, fw_reg_features;
> > +
> > +     switch (reg_id) {
> > +     case KVM_REG_ARM_STD_BMAP:
> > +             fw_reg_bmap = &smccc_feat->std_bmap;
> > +             fw_reg_features = KVM_ARM_SMCCC_STD_FEATURES;
> > +             break;
> > +     default:
> > +             return -ENOENT;
> > +     }
> > +
> > +     /* Check for unsupported bit */
> > +     if (val & ~fw_reg_features)
> > +             return -EINVAL;
> > +
> > +     mutex_lock(&kvm->lock);
> > +
> > +     /*
> > +      * If the VM (any vCPU) has already started running, return success
> > +      * if there's no change in the value. Else, return -EBUSY.
>
> No, this should *always* fail if a vcpu has started. Otherwise, you
> start allowing hard to spot races.
>
The idea came from the fact that userspace could spawn multiple
threads to configure the vCPU registers. Since we don't have the
VM-scoped registers yet, it may be possible that userspace has issued
a KVM_RUN on one of the vCPU, while the others are lagging behind and
still configuring the registers. The slower threads may see -EBUSY and
could panic. But if you feel that it's an overkill and the userspace
should deal with it, we can return EBUSY for all writes after KVM_RUN.

> > +      */
> > +     if (test_bit(KVM_ARCH_FLAG_HAS_RAN_ONCE, &kvm->arch.flags)) {
> > +             ret = *fw_reg_bmap != val ? -EBUSY : 0;
> > +             goto out;
> > +     }
> > +
> > +     WRITE_ONCE(*fw_reg_bmap, val);
>
> I'm not sure what this WRITE_ONCE guards against. Do you expect
> concurrent reads at this stage?
>
Again, the assumption here is that userspace could have multiple
threads reading and writing to these registers. Without the VM scoped
registers in place, we may end up with a read/write to the same memory
location for all the vCPUs.

> > +out:
> > +     mutex_unlock(&kvm->lock);
> > +     return ret;
> > +}
> > +
> >  int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> >  {
> >       void __user *uaddr = (void __user *)(long)reg->addr;
> > @@ -337,6 +437,8 @@ int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> >                       return -EINVAL;
> >
> >               return 0;
> > +     case KVM_REG_ARM_STD_BMAP:
> > +             return kvm_arm_set_fw_reg_bmap(vcpu, reg->id, val);
> >       default:
> >               return -ENOENT;
> >       }
> > diff --git a/include/kvm/arm_hypercalls.h b/include/kvm/arm_hypercalls.h
> > index 5d38628a8d04..fd3ff350ee9d 100644
> > --- a/include/kvm/arm_hypercalls.h
> > +++ b/include/kvm/arm_hypercalls.h
> > @@ -6,6 +6,12 @@
> >
> >  #include <asm/kvm_emulate.h>
> >
> > +/* Last valid bits of the bitmapped firmware registers */
> > +#define KVM_REG_ARM_STD_BMAP_BIT_MAX         0
> > +
> > +#define KVM_ARM_SMCCC_STD_FEATURES \
> > +     GENMASK_ULL(KVM_REG_ARM_STD_BMAP_BIT_MAX, 0)
> > +
> >  int kvm_hvc_call_handler(struct kvm_vcpu *vcpu);
> >
> >  static inline u32 smccc_get_function(struct kvm_vcpu *vcpu)
> > @@ -42,6 +48,7 @@ static inline void smccc_set_retval(struct kvm_vcpu *vcpu,
> >
> >  struct kvm_one_reg;
> >
> > +void kvm_arm_init_hypercalls(struct kvm *kvm);
> >  int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu);
> >  int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices);
> >  int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
> > diff --git a/include/kvm/arm_psci.h b/include/kvm/arm_psci.h
> > index 6e55b9283789..d7a87367de56 100644
> > --- a/include/kvm/arm_psci.h
> > +++ b/include/kvm/arm_psci.h
> > @@ -36,6 +36,18 @@ static inline int kvm_psci_version(struct kvm_vcpu *vcpu)
> >       return KVM_ARM_PSCI_0_1;
> >  }
> >
> > +static inline bool kvm_psci_func_id_is_valid(struct kvm_vcpu *vcpu, u32 func_id)
> > +{
> > +     /* PSCI 0.1 doesn't comply with the standard SMCCC */
> > +     if (kvm_psci_version(vcpu) == KVM_ARM_PSCI_0_1)
> > +             return (func_id == KVM_PSCI_FN_CPU_OFF || func_id == KVM_PSCI_FN_CPU_ON);
> > +
> > +     if (ARM_SMCCC_OWNER_NUM(func_id) == ARM_SMCCC_OWNER_STANDARD &&
> > +             ARM_SMCCC_FUNC_NUM(func_id) >= 0 && ARM_SMCCC_FUNC_NUM(func_id) <= 0x1f)
> > +             return true;
> > +
> > +     return false;
> > +}
>
> Why the inline function? Do you expect this to be shared with
> something else? If not, I'd rather you move it into the caller.
>
Well, no plans to share as of yet. Will move it to psci.c

> >
> >  int kvm_psci_call(struct kvm_vcpu *vcpu);
> >
>

Thanks for the review, Marc. I'll fix all the other nits.

Regards,
Raghavendra

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

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

* Re: [PATCH v5 02/10] KVM: arm64: Setup a framework for hypercall bitmap firmware registers
  2022-04-07 17:24       ` Raghavendra Rao Ananta
  (?)
@ 2022-04-08 16:59         ` Marc Zyngier
  -1 siblings, 0 replies; 96+ messages in thread
From: Marc Zyngier @ 2022-04-08 16:59 UTC (permalink / raw)
  To: Raghavendra Rao Ananta
  Cc: Andrew Jones, James Morse, Alexandru Elisei, Suzuki K Poulose,
	Paolo Bonzini, Catalin Marinas, Will Deacon, Peter Shier,
	Ricardo Koller, Oliver Upton, Reiji Watanabe, Jing Zhang,
	linux-arm-kernel, kvmarm, linux-kernel, kvm

On Thu, 07 Apr 2022 18:24:14 +0100,
Raghavendra Rao Ananta <rananta@google.com> wrote:
> 
> Hi Marc,
> 
> > > +#define KVM_REG_ARM_STD_BIT_TRNG_V1_0                BIT(0)
> >
> > I'm really in two minds about this. Having one bit per service is easy
> > from an implementation perspective, but is also means that this
> > disallow fine grained control over which hypercalls are actually
> > available. If tomorrow TRNG 1.1 adds a new hypercall and that KVM
> > implements both, how does the selection mechanism works? You will
> > need a version selector (a la PSCI), which defeats this API somehow
> > (and renders the name of the #define invalid).
> >
> > I wonder if a more correct way to look at this is to enumerate the
> > hypercalls themselves (all 5 of them), though coming up with an
> > encoding is tricky (RNG32 and RNG64 would clash, for example).
> >
> > Thoughts?
> >
> I was on the fence about this too. The TRNG spec (ARM DEN 0098,
> Table-4) mentions that v1.0 should have VERSION, FEATURES, GET_UUID,
> and RND as mandatory features. Hence, if KVM advertised that it
> supports TRNG v1.0, I thought it would be best to expose all or
> nothing of v1.0 by guarding them with a single bit.
> Broadly, the idea is to have a bit per version. If v1.1 comes along,
> we can have another bit for that. If it's not too ugly to implement,
> we can be a little more aggressive and ensure that userspace doesn't
> enable v1.1 without enabling v1.0.

OK, that'd be assuming that we'll never see a service where version A
is incompatible with version B and that we have to exclude one or the
other. Meh. Let's cross that bridge once it is actually built.

[...]

> > > +     mutex_lock(&kvm->lock);
> > > +
> > > +     /*
> > > +      * If the VM (any vCPU) has already started running, return success
> > > +      * if there's no change in the value. Else, return -EBUSY.
> >
> > No, this should *always* fail if a vcpu has started. Otherwise, you
> > start allowing hard to spot races.
> >
> The idea came from the fact that userspace could spawn multiple
> threads to configure the vCPU registers. Since we don't have the
> VM-scoped registers yet, it may be possible that userspace has issued
> a KVM_RUN on one of the vCPU, while the others are lagging behind and
> still configuring the registers. The slower threads may see -EBUSY and
> could panic. But if you feel that it's an overkill and the userspace
> should deal with it, we can return EBUSY for all writes after KVM_RUN.

I'd rather have that. There already is stuff that rely on things not
changing once a vcpu has run, so I'd rather be consistent.

>
> > > +      */
> > > +     if (test_bit(KVM_ARCH_FLAG_HAS_RAN_ONCE, &kvm->arch.flags)) {
> > > +             ret = *fw_reg_bmap != val ? -EBUSY : 0;
> > > +             goto out;
> > > +     }
> > > +
> > > +     WRITE_ONCE(*fw_reg_bmap, val);
> >
> > I'm not sure what this WRITE_ONCE guards against. Do you expect
> > concurrent reads at this stage?
> >
> Again, the assumption here is that userspace could have multiple
> threads reading and writing to these registers. Without the VM scoped
> registers in place, we may end up with a read/write to the same memory
> location for all the vCPUs.

We only have one vcpu updating this at any given time (that's what the
lock ensures). A simple write should be OK, as far as I can tell.

Thanks,

	M.

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

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

* Re: [PATCH v5 02/10] KVM: arm64: Setup a framework for hypercall bitmap firmware registers
@ 2022-04-08 16:59         ` Marc Zyngier
  0 siblings, 0 replies; 96+ messages in thread
From: Marc Zyngier @ 2022-04-08 16:59 UTC (permalink / raw)
  To: Raghavendra Rao Ananta
  Cc: kvm, Will Deacon, Catalin Marinas, Peter Shier, linux-kernel,
	Paolo Bonzini, kvmarm, linux-arm-kernel

On Thu, 07 Apr 2022 18:24:14 +0100,
Raghavendra Rao Ananta <rananta@google.com> wrote:
> 
> Hi Marc,
> 
> > > +#define KVM_REG_ARM_STD_BIT_TRNG_V1_0                BIT(0)
> >
> > I'm really in two minds about this. Having one bit per service is easy
> > from an implementation perspective, but is also means that this
> > disallow fine grained control over which hypercalls are actually
> > available. If tomorrow TRNG 1.1 adds a new hypercall and that KVM
> > implements both, how does the selection mechanism works? You will
> > need a version selector (a la PSCI), which defeats this API somehow
> > (and renders the name of the #define invalid).
> >
> > I wonder if a more correct way to look at this is to enumerate the
> > hypercalls themselves (all 5 of them), though coming up with an
> > encoding is tricky (RNG32 and RNG64 would clash, for example).
> >
> > Thoughts?
> >
> I was on the fence about this too. The TRNG spec (ARM DEN 0098,
> Table-4) mentions that v1.0 should have VERSION, FEATURES, GET_UUID,
> and RND as mandatory features. Hence, if KVM advertised that it
> supports TRNG v1.0, I thought it would be best to expose all or
> nothing of v1.0 by guarding them with a single bit.
> Broadly, the idea is to have a bit per version. If v1.1 comes along,
> we can have another bit for that. If it's not too ugly to implement,
> we can be a little more aggressive and ensure that userspace doesn't
> enable v1.1 without enabling v1.0.

OK, that'd be assuming that we'll never see a service where version A
is incompatible with version B and that we have to exclude one or the
other. Meh. Let's cross that bridge once it is actually built.

[...]

> > > +     mutex_lock(&kvm->lock);
> > > +
> > > +     /*
> > > +      * If the VM (any vCPU) has already started running, return success
> > > +      * if there's no change in the value. Else, return -EBUSY.
> >
> > No, this should *always* fail if a vcpu has started. Otherwise, you
> > start allowing hard to spot races.
> >
> The idea came from the fact that userspace could spawn multiple
> threads to configure the vCPU registers. Since we don't have the
> VM-scoped registers yet, it may be possible that userspace has issued
> a KVM_RUN on one of the vCPU, while the others are lagging behind and
> still configuring the registers. The slower threads may see -EBUSY and
> could panic. But if you feel that it's an overkill and the userspace
> should deal with it, we can return EBUSY for all writes after KVM_RUN.

I'd rather have that. There already is stuff that rely on things not
changing once a vcpu has run, so I'd rather be consistent.

>
> > > +      */
> > > +     if (test_bit(KVM_ARCH_FLAG_HAS_RAN_ONCE, &kvm->arch.flags)) {
> > > +             ret = *fw_reg_bmap != val ? -EBUSY : 0;
> > > +             goto out;
> > > +     }
> > > +
> > > +     WRITE_ONCE(*fw_reg_bmap, val);
> >
> > I'm not sure what this WRITE_ONCE guards against. Do you expect
> > concurrent reads at this stage?
> >
> Again, the assumption here is that userspace could have multiple
> threads reading and writing to these registers. Without the VM scoped
> registers in place, we may end up with a read/write to the same memory
> location for all the vCPUs.

We only have one vcpu updating this at any given time (that's what the
lock ensures). A simple write should be OK, as far as I can tell.

Thanks,

	M.

-- 
Without deviation from the norm, progress is not possible.
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

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

* Re: [PATCH v5 02/10] KVM: arm64: Setup a framework for hypercall bitmap firmware registers
@ 2022-04-08 16:59         ` Marc Zyngier
  0 siblings, 0 replies; 96+ messages in thread
From: Marc Zyngier @ 2022-04-08 16:59 UTC (permalink / raw)
  To: Raghavendra Rao Ananta
  Cc: Andrew Jones, James Morse, Alexandru Elisei, Suzuki K Poulose,
	Paolo Bonzini, Catalin Marinas, Will Deacon, Peter Shier,
	Ricardo Koller, Oliver Upton, Reiji Watanabe, Jing Zhang,
	linux-arm-kernel, kvmarm, linux-kernel, kvm

On Thu, 07 Apr 2022 18:24:14 +0100,
Raghavendra Rao Ananta <rananta@google.com> wrote:
> 
> Hi Marc,
> 
> > > +#define KVM_REG_ARM_STD_BIT_TRNG_V1_0                BIT(0)
> >
> > I'm really in two minds about this. Having one bit per service is easy
> > from an implementation perspective, but is also means that this
> > disallow fine grained control over which hypercalls are actually
> > available. If tomorrow TRNG 1.1 adds a new hypercall and that KVM
> > implements both, how does the selection mechanism works? You will
> > need a version selector (a la PSCI), which defeats this API somehow
> > (and renders the name of the #define invalid).
> >
> > I wonder if a more correct way to look at this is to enumerate the
> > hypercalls themselves (all 5 of them), though coming up with an
> > encoding is tricky (RNG32 and RNG64 would clash, for example).
> >
> > Thoughts?
> >
> I was on the fence about this too. The TRNG spec (ARM DEN 0098,
> Table-4) mentions that v1.0 should have VERSION, FEATURES, GET_UUID,
> and RND as mandatory features. Hence, if KVM advertised that it
> supports TRNG v1.0, I thought it would be best to expose all or
> nothing of v1.0 by guarding them with a single bit.
> Broadly, the idea is to have a bit per version. If v1.1 comes along,
> we can have another bit for that. If it's not too ugly to implement,
> we can be a little more aggressive and ensure that userspace doesn't
> enable v1.1 without enabling v1.0.

OK, that'd be assuming that we'll never see a service where version A
is incompatible with version B and that we have to exclude one or the
other. Meh. Let's cross that bridge once it is actually built.

[...]

> > > +     mutex_lock(&kvm->lock);
> > > +
> > > +     /*
> > > +      * If the VM (any vCPU) has already started running, return success
> > > +      * if there's no change in the value. Else, return -EBUSY.
> >
> > No, this should *always* fail if a vcpu has started. Otherwise, you
> > start allowing hard to spot races.
> >
> The idea came from the fact that userspace could spawn multiple
> threads to configure the vCPU registers. Since we don't have the
> VM-scoped registers yet, it may be possible that userspace has issued
> a KVM_RUN on one of the vCPU, while the others are lagging behind and
> still configuring the registers. The slower threads may see -EBUSY and
> could panic. But if you feel that it's an overkill and the userspace
> should deal with it, we can return EBUSY for all writes after KVM_RUN.

I'd rather have that. There already is stuff that rely on things not
changing once a vcpu has run, so I'd rather be consistent.

>
> > > +      */
> > > +     if (test_bit(KVM_ARCH_FLAG_HAS_RAN_ONCE, &kvm->arch.flags)) {
> > > +             ret = *fw_reg_bmap != val ? -EBUSY : 0;
> > > +             goto out;
> > > +     }
> > > +
> > > +     WRITE_ONCE(*fw_reg_bmap, val);
> >
> > I'm not sure what this WRITE_ONCE guards against. Do you expect
> > concurrent reads at this stage?
> >
> Again, the assumption here is that userspace could have multiple
> threads reading and writing to these registers. Without the VM scoped
> registers in place, we may end up with a read/write to the same memory
> location for all the vCPUs.

We only have one vcpu updating this at any given time (that's what the
lock ensures). A simple write should be OK, as far as I can tell.

Thanks,

	M.

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

_______________________________________________
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] 96+ messages in thread

* Re: [PATCH v5 02/10] KVM: arm64: Setup a framework for hypercall bitmap firmware registers
  2022-04-08 16:59         ` Marc Zyngier
  (?)
@ 2022-04-08 17:34           ` Raghavendra Rao Ananta
  -1 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-08 17:34 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: Andrew Jones, James Morse, Alexandru Elisei, Suzuki K Poulose,
	Paolo Bonzini, Catalin Marinas, Will Deacon, Peter Shier,
	Ricardo Koller, Oliver Upton, Reiji Watanabe, Jing Zhang,
	linux-arm-kernel, kvmarm, linux-kernel, kvm

On Fri, Apr 8, 2022 at 9:59 AM Marc Zyngier <maz@kernel.org> wrote:
>
> On Thu, 07 Apr 2022 18:24:14 +0100,
> Raghavendra Rao Ananta <rananta@google.com> wrote:
> >
> > Hi Marc,
> >
> > > > +#define KVM_REG_ARM_STD_BIT_TRNG_V1_0                BIT(0)
> > >
> > > I'm really in two minds about this. Having one bit per service is easy
> > > from an implementation perspective, but is also means that this
> > > disallow fine grained control over which hypercalls are actually
> > > available. If tomorrow TRNG 1.1 adds a new hypercall and that KVM
> > > implements both, how does the selection mechanism works? You will
> > > need a version selector (a la PSCI), which defeats this API somehow
> > > (and renders the name of the #define invalid).
> > >
> > > I wonder if a more correct way to look at this is to enumerate the
> > > hypercalls themselves (all 5 of them), though coming up with an
> > > encoding is tricky (RNG32 and RNG64 would clash, for example).
> > >
> > > Thoughts?
> > >
> > I was on the fence about this too. The TRNG spec (ARM DEN 0098,
> > Table-4) mentions that v1.0 should have VERSION, FEATURES, GET_UUID,
> > and RND as mandatory features. Hence, if KVM advertised that it
> > supports TRNG v1.0, I thought it would be best to expose all or
> > nothing of v1.0 by guarding them with a single bit.
> > Broadly, the idea is to have a bit per version. If v1.1 comes along,
> > we can have another bit for that. If it's not too ugly to implement,
> > we can be a little more aggressive and ensure that userspace doesn't
> > enable v1.1 without enabling v1.0.
>
> OK, that'd be assuming that we'll never see a service where version A
> is incompatible with version B and that we have to exclude one or the
> other. Meh. Let's cross that bridge once it is actually built.
>
> [...]
>
> > > > +     mutex_lock(&kvm->lock);
> > > > +
> > > > +     /*
> > > > +      * If the VM (any vCPU) has already started running, return success
> > > > +      * if there's no change in the value. Else, return -EBUSY.
> > >
> > > No, this should *always* fail if a vcpu has started. Otherwise, you
> > > start allowing hard to spot races.
> > >
> > The idea came from the fact that userspace could spawn multiple
> > threads to configure the vCPU registers. Since we don't have the
> > VM-scoped registers yet, it may be possible that userspace has issued
> > a KVM_RUN on one of the vCPU, while the others are lagging behind and
> > still configuring the registers. The slower threads may see -EBUSY and
> > could panic. But if you feel that it's an overkill and the userspace
> > should deal with it, we can return EBUSY for all writes after KVM_RUN.
>
> I'd rather have that. There already is stuff that rely on things not
> changing once a vcpu has run, so I'd rather be consistent.
>
Sure, I'll return EBUSY if the VM has started regardless of the incoming value.
> >
> > > > +      */
> > > > +     if (test_bit(KVM_ARCH_FLAG_HAS_RAN_ONCE, &kvm->arch.flags)) {
> > > > +             ret = *fw_reg_bmap != val ? -EBUSY : 0;
> > > > +             goto out;
> > > > +     }
> > > > +
> > > > +     WRITE_ONCE(*fw_reg_bmap, val);
> > >
> > > I'm not sure what this WRITE_ONCE guards against. Do you expect
> > > concurrent reads at this stage?
> > >
> > Again, the assumption here is that userspace could have multiple
> > threads reading and writing to these registers. Without the VM scoped
> > registers in place, we may end up with a read/write to the same memory
> > location for all the vCPUs.
>
> We only have one vcpu updating this at any given time (that's what the
> lock ensures). A simple write should be OK, as far as I can tell.
>
I agree that a write against another write should be fine without the
WRITE_ONCE. But my little concern was this write against a read
(unsure how userspace accesses these registers). I'm guessing it
shouldn't hurt to keep them in place, no? :)

Regards,
Raghavendra

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

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

* Re: [PATCH v5 02/10] KVM: arm64: Setup a framework for hypercall bitmap firmware registers
@ 2022-04-08 17:34           ` Raghavendra Rao Ananta
  0 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-08 17:34 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: kvm, Will Deacon, Catalin Marinas, Peter Shier, linux-kernel,
	Paolo Bonzini, kvmarm, linux-arm-kernel

On Fri, Apr 8, 2022 at 9:59 AM Marc Zyngier <maz@kernel.org> wrote:
>
> On Thu, 07 Apr 2022 18:24:14 +0100,
> Raghavendra Rao Ananta <rananta@google.com> wrote:
> >
> > Hi Marc,
> >
> > > > +#define KVM_REG_ARM_STD_BIT_TRNG_V1_0                BIT(0)
> > >
> > > I'm really in two minds about this. Having one bit per service is easy
> > > from an implementation perspective, but is also means that this
> > > disallow fine grained control over which hypercalls are actually
> > > available. If tomorrow TRNG 1.1 adds a new hypercall and that KVM
> > > implements both, how does the selection mechanism works? You will
> > > need a version selector (a la PSCI), which defeats this API somehow
> > > (and renders the name of the #define invalid).
> > >
> > > I wonder if a more correct way to look at this is to enumerate the
> > > hypercalls themselves (all 5 of them), though coming up with an
> > > encoding is tricky (RNG32 and RNG64 would clash, for example).
> > >
> > > Thoughts?
> > >
> > I was on the fence about this too. The TRNG spec (ARM DEN 0098,
> > Table-4) mentions that v1.0 should have VERSION, FEATURES, GET_UUID,
> > and RND as mandatory features. Hence, if KVM advertised that it
> > supports TRNG v1.0, I thought it would be best to expose all or
> > nothing of v1.0 by guarding them with a single bit.
> > Broadly, the idea is to have a bit per version. If v1.1 comes along,
> > we can have another bit for that. If it's not too ugly to implement,
> > we can be a little more aggressive and ensure that userspace doesn't
> > enable v1.1 without enabling v1.0.
>
> OK, that'd be assuming that we'll never see a service where version A
> is incompatible with version B and that we have to exclude one or the
> other. Meh. Let's cross that bridge once it is actually built.
>
> [...]
>
> > > > +     mutex_lock(&kvm->lock);
> > > > +
> > > > +     /*
> > > > +      * If the VM (any vCPU) has already started running, return success
> > > > +      * if there's no change in the value. Else, return -EBUSY.
> > >
> > > No, this should *always* fail if a vcpu has started. Otherwise, you
> > > start allowing hard to spot races.
> > >
> > The idea came from the fact that userspace could spawn multiple
> > threads to configure the vCPU registers. Since we don't have the
> > VM-scoped registers yet, it may be possible that userspace has issued
> > a KVM_RUN on one of the vCPU, while the others are lagging behind and
> > still configuring the registers. The slower threads may see -EBUSY and
> > could panic. But if you feel that it's an overkill and the userspace
> > should deal with it, we can return EBUSY for all writes after KVM_RUN.
>
> I'd rather have that. There already is stuff that rely on things not
> changing once a vcpu has run, so I'd rather be consistent.
>
Sure, I'll return EBUSY if the VM has started regardless of the incoming value.
> >
> > > > +      */
> > > > +     if (test_bit(KVM_ARCH_FLAG_HAS_RAN_ONCE, &kvm->arch.flags)) {
> > > > +             ret = *fw_reg_bmap != val ? -EBUSY : 0;
> > > > +             goto out;
> > > > +     }
> > > > +
> > > > +     WRITE_ONCE(*fw_reg_bmap, val);
> > >
> > > I'm not sure what this WRITE_ONCE guards against. Do you expect
> > > concurrent reads at this stage?
> > >
> > Again, the assumption here is that userspace could have multiple
> > threads reading and writing to these registers. Without the VM scoped
> > registers in place, we may end up with a read/write to the same memory
> > location for all the vCPUs.
>
> We only have one vcpu updating this at any given time (that's what the
> lock ensures). A simple write should be OK, as far as I can tell.
>
I agree that a write against another write should be fine without the
WRITE_ONCE. But my little concern was this write against a read
(unsure how userspace accesses these registers). I'm guessing it
shouldn't hurt to keep them in place, no? :)

Regards,
Raghavendra

> Thanks,
>
>         M.
>
> --
> Without deviation from the norm, progress is not possible.
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

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

* Re: [PATCH v5 02/10] KVM: arm64: Setup a framework for hypercall bitmap firmware registers
@ 2022-04-08 17:34           ` Raghavendra Rao Ananta
  0 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-08 17:34 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: Andrew Jones, James Morse, Alexandru Elisei, Suzuki K Poulose,
	Paolo Bonzini, Catalin Marinas, Will Deacon, Peter Shier,
	Ricardo Koller, Oliver Upton, Reiji Watanabe, Jing Zhang,
	linux-arm-kernel, kvmarm, linux-kernel, kvm

On Fri, Apr 8, 2022 at 9:59 AM Marc Zyngier <maz@kernel.org> wrote:
>
> On Thu, 07 Apr 2022 18:24:14 +0100,
> Raghavendra Rao Ananta <rananta@google.com> wrote:
> >
> > Hi Marc,
> >
> > > > +#define KVM_REG_ARM_STD_BIT_TRNG_V1_0                BIT(0)
> > >
> > > I'm really in two minds about this. Having one bit per service is easy
> > > from an implementation perspective, but is also means that this
> > > disallow fine grained control over which hypercalls are actually
> > > available. If tomorrow TRNG 1.1 adds a new hypercall and that KVM
> > > implements both, how does the selection mechanism works? You will
> > > need a version selector (a la PSCI), which defeats this API somehow
> > > (and renders the name of the #define invalid).
> > >
> > > I wonder if a more correct way to look at this is to enumerate the
> > > hypercalls themselves (all 5 of them), though coming up with an
> > > encoding is tricky (RNG32 and RNG64 would clash, for example).
> > >
> > > Thoughts?
> > >
> > I was on the fence about this too. The TRNG spec (ARM DEN 0098,
> > Table-4) mentions that v1.0 should have VERSION, FEATURES, GET_UUID,
> > and RND as mandatory features. Hence, if KVM advertised that it
> > supports TRNG v1.0, I thought it would be best to expose all or
> > nothing of v1.0 by guarding them with a single bit.
> > Broadly, the idea is to have a bit per version. If v1.1 comes along,
> > we can have another bit for that. If it's not too ugly to implement,
> > we can be a little more aggressive and ensure that userspace doesn't
> > enable v1.1 without enabling v1.0.
>
> OK, that'd be assuming that we'll never see a service where version A
> is incompatible with version B and that we have to exclude one or the
> other. Meh. Let's cross that bridge once it is actually built.
>
> [...]
>
> > > > +     mutex_lock(&kvm->lock);
> > > > +
> > > > +     /*
> > > > +      * If the VM (any vCPU) has already started running, return success
> > > > +      * if there's no change in the value. Else, return -EBUSY.
> > >
> > > No, this should *always* fail if a vcpu has started. Otherwise, you
> > > start allowing hard to spot races.
> > >
> > The idea came from the fact that userspace could spawn multiple
> > threads to configure the vCPU registers. Since we don't have the
> > VM-scoped registers yet, it may be possible that userspace has issued
> > a KVM_RUN on one of the vCPU, while the others are lagging behind and
> > still configuring the registers. The slower threads may see -EBUSY and
> > could panic. But if you feel that it's an overkill and the userspace
> > should deal with it, we can return EBUSY for all writes after KVM_RUN.
>
> I'd rather have that. There already is stuff that rely on things not
> changing once a vcpu has run, so I'd rather be consistent.
>
Sure, I'll return EBUSY if the VM has started regardless of the incoming value.
> >
> > > > +      */
> > > > +     if (test_bit(KVM_ARCH_FLAG_HAS_RAN_ONCE, &kvm->arch.flags)) {
> > > > +             ret = *fw_reg_bmap != val ? -EBUSY : 0;
> > > > +             goto out;
> > > > +     }
> > > > +
> > > > +     WRITE_ONCE(*fw_reg_bmap, val);
> > >
> > > I'm not sure what this WRITE_ONCE guards against. Do you expect
> > > concurrent reads at this stage?
> > >
> > Again, the assumption here is that userspace could have multiple
> > threads reading and writing to these registers. Without the VM scoped
> > registers in place, we may end up with a read/write to the same memory
> > location for all the vCPUs.
>
> We only have one vcpu updating this at any given time (that's what the
> lock ensures). A simple write should be OK, as far as I can tell.
>
I agree that a write against another write should be fine without the
WRITE_ONCE. But my little concern was this write against a read
(unsure how userspace accesses these registers). I'm guessing it
shouldn't hurt to keep them in place, no? :)

Regards,
Raghavendra

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

_______________________________________________
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] 96+ messages in thread

* Re: [PATCH v5 01/10] KVM: arm64: Factor out firmware register handling from psci.c
  2022-04-07  1:15   ` Raghavendra Rao Ananta
  (?)
@ 2022-04-12  7:06     ` Gavin Shan
  -1 siblings, 0 replies; 96+ messages in thread
From: Gavin Shan @ 2022-04-12  7:06 UTC (permalink / raw)
  To: Raghavendra Rao Ananta, Marc Zyngier, Andrew Jones, James Morse,
	Alexandru Elisei, Suzuki K Poulose
  Cc: kvm, Catalin Marinas, Peter Shier, linux-kernel, Paolo Bonzini,
	Will Deacon, kvmarm, linux-arm-kernel

Hi Raghavendra,

On 4/7/22 9:15 AM, Raghavendra Rao Ananta wrote:
> Common hypercall firmware register handing is currently employed
> by psci.c. Since the upcoming patches add more of these registers,
> it's better to move the generic handling to hypercall.c for a
> cleaner presentation.
> 
> While we are at it, collect all the firmware registers under
> fw_reg_ids[] to help implement kvm_arm_get_fw_num_regs() and
> kvm_arm_copy_fw_reg_indices() in a generic way. Also, define
> KVM_REG_FEATURE_LEVEL_MASK using a GENMASK instead.
> 
> No functional change intended.
> 
> Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
> Reviewed-by: Oliver Upton <oupton@google.com>
> ---
>   arch/arm64/kvm/guest.c       |   2 +-
>   arch/arm64/kvm/hypercalls.c  | 185 +++++++++++++++++++++++++++++++++++
>   arch/arm64/kvm/psci.c        | 183 ----------------------------------
>   include/kvm/arm_hypercalls.h |   7 ++
>   include/kvm/arm_psci.h       |   7 --
>   5 files changed, 193 insertions(+), 191 deletions(-)
> 

Apart from the below nits:

Reviewed-by: Gavin Shan <gshan@redhat.com>

> diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
> index 7e15b03fbdf8..0d5cca56cbda 100644
> --- a/arch/arm64/kvm/guest.c
> +++ b/arch/arm64/kvm/guest.c
> @@ -18,7 +18,7 @@
>   #include <linux/string.h>
>   #include <linux/vmalloc.h>
>   #include <linux/fs.h>
> -#include <kvm/arm_psci.h>
> +#include <kvm/arm_hypercalls.h>
>   #include <asm/cputype.h>
>   #include <linux/uaccess.h>
>   #include <asm/fpsimd.h>
> diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c
> index 202b8c455724..fa6d9378d8e7 100644
> --- a/arch/arm64/kvm/hypercalls.c
> +++ b/arch/arm64/kvm/hypercalls.c
> @@ -158,3 +158,188 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
>   	smccc_set_retval(vcpu, val[0], val[1], val[2], val[3]);
>   	return 1;
>   }
> +
> +static const u64 kvm_arm_fw_reg_ids[] = {
> +	KVM_REG_ARM_PSCI_VERSION,
> +	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1,
> +	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2,
> +	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3,
> +};
> +
> +int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
> +{
> +	return ARRAY_SIZE(kvm_arm_fw_reg_ids);
> +}
> +
> +int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(kvm_arm_fw_reg_ids); i++) {
> +		if (put_user(kvm_arm_fw_reg_ids[i], uindices++))
> +			return -EFAULT;
> +	}
> +
> +	return 0;
> +}
> +

Since we're here, I think we can make this function to return 'ARRAY_SIZE(kvm_arm_fw_reg_ids)',
to be consistent with copy_{core, sve}_reg_indices(). With the return value fixed, additional
patch can use @ret in kvm_arm_copy_reg_indices().

> +#define KVM_REG_FEATURE_LEVEL_WIDTH	4
> +#define KVM_REG_FEATURE_LEVEL_MASK	GENMASK(KVM_REG_FEATURE_LEVEL_WIDTH, 0)
> +

It seems 'BIT()' is replaced with 'GENMASK' in the movement, but it's not mentioned
in the commit log. I guess it'd better to mention it if you agree.

> +/*
> + * Convert the workaround level into an easy-to-compare number, where higher
> + * values mean better protection.
> + */
> +static int get_kernel_wa_level(u64 regid)
> +{
> +	switch (regid) {
> +	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
> +		switch (arm64_get_spectre_v2_state()) {
> +		case SPECTRE_VULNERABLE:
> +			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
> +		case SPECTRE_MITIGATED:
> +			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_AVAIL;
> +		case SPECTRE_UNAFFECTED:
> +			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_REQUIRED;
> +		}
> +		return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
> +	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
> +		switch (arm64_get_spectre_v4_state()) {
> +		case SPECTRE_MITIGATED:
> +			/*
> +			 * As for the hypercall discovery, we pretend we
> +			 * don't have any FW mitigation if SSBS is there at
> +			 * all times.
> +			 */
> +			if (cpus_have_final_cap(ARM64_SSBS))
> +				return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
> +			fallthrough;
> +		case SPECTRE_UNAFFECTED:
> +			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
> +		case SPECTRE_VULNERABLE:
> +			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
> +		}
> +		break;
> +	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
> +		switch (arm64_get_spectre_bhb_state()) {
> +		case SPECTRE_VULNERABLE:
> +			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
> +		case SPECTRE_MITIGATED:
> +			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_AVAIL;
> +		case SPECTRE_UNAFFECTED:
> +			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_REQUIRED;
> +		}
> +		return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> +{
> +	void __user *uaddr = (void __user *)(long)reg->addr;
> +	u64 val;
> +
> +	switch (reg->id) {
> +	case KVM_REG_ARM_PSCI_VERSION:
> +		val = kvm_psci_version(vcpu);
> +		break;
> +	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
> +	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
> +	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
> +		val = get_kernel_wa_level(reg->id) & KVM_REG_FEATURE_LEVEL_MASK;
> +		break;
> +	default:
> +		return -ENOENT;
> +	}
> +
> +	if (copy_to_user(uaddr, &val, KVM_REG_SIZE(reg->id)))
> +		return -EFAULT;
> +
> +	return 0;
> +}
> +
> +int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> +{
> +	void __user *uaddr = (void __user *)(long)reg->addr;
> +	u64 val;
> +	int wa_level;
> +
> +	if (copy_from_user(&val, uaddr, KVM_REG_SIZE(reg->id)))
> +		return -EFAULT;
> +
> +	switch (reg->id) {
> +	case KVM_REG_ARM_PSCI_VERSION:
> +	{
> +		bool wants_02;
> +
> +		wants_02 = test_bit(KVM_ARM_VCPU_PSCI_0_2, vcpu->arch.features);
> +
> +		switch (val) {
> +		case KVM_ARM_PSCI_0_1:
> +			if (wants_02)
> +				return -EINVAL;
> +			vcpu->kvm->arch.psci_version = val;
> +			return 0;
> +		case KVM_ARM_PSCI_0_2:
> +		case KVM_ARM_PSCI_1_0:
> +		case KVM_ARM_PSCI_1_1:
> +			if (!wants_02)
> +				return -EINVAL;
> +			vcpu->kvm->arch.psci_version = val;
> +			return 0;
> +		}
> +		break;
> +	}
> +
> +	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
> +	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
> +		if (val & ~KVM_REG_FEATURE_LEVEL_MASK)
> +			return -EINVAL;
> +
> +		if (get_kernel_wa_level(reg->id) < val)
> +			return -EINVAL;
> +
> +		return 0;
> +
> +	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
> +		if (val & ~(KVM_REG_FEATURE_LEVEL_MASK |
> +			    KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED))
> +			return -EINVAL;
> +
> +		/* The enabled bit must not be set unless the level is AVAIL. */
> +		if ((val & KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED) &&
> +		    (val & KVM_REG_FEATURE_LEVEL_MASK) != KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL)
> +			return -EINVAL;
> +
> +		/*
> +		 * Map all the possible incoming states to the only two we
> +		 * really want to deal with.
> +		 */
> +		switch (val & KVM_REG_FEATURE_LEVEL_MASK) {
> +		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL:
> +		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_UNKNOWN:
> +			wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
> +			break;
> +		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL:
> +		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED:
> +			wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
> +			break;
> +		default:
> +			return -EINVAL;
> +		}
> +
> +		/*
> +		 * We can deal with NOT_AVAIL on NOT_REQUIRED, but not the
> +		 * other way around.
> +		 */
> +		if (get_kernel_wa_level(reg->id) < wa_level)
> +			return -EINVAL;
> +
> +		return 0;
> +	default:
> +		return -ENOENT;
> +	}
> +
> +	return -EINVAL;
> +}
> diff --git a/arch/arm64/kvm/psci.c b/arch/arm64/kvm/psci.c
> index 372da09a2fab..bdfa93ca57d1 100644
> --- a/arch/arm64/kvm/psci.c
> +++ b/arch/arm64/kvm/psci.c
> @@ -439,186 +439,3 @@ int kvm_psci_call(struct kvm_vcpu *vcpu)
>   		return -EINVAL;
>   	}
>   }
> -
> -int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
> -{
> -	return 4;		/* PSCI version and three workaround registers */
> -}
> -
> -int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
> -{
> -	if (put_user(KVM_REG_ARM_PSCI_VERSION, uindices++))
> -		return -EFAULT;
> -
> -	if (put_user(KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1, uindices++))
> -		return -EFAULT;
> -
> -	if (put_user(KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2, uindices++))
> -		return -EFAULT;
> -
> -	if (put_user(KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3, uindices++))
> -		return -EFAULT;
> -
> -	return 0;
> -}
> -
> -#define KVM_REG_FEATURE_LEVEL_WIDTH	4
> -#define KVM_REG_FEATURE_LEVEL_MASK	(BIT(KVM_REG_FEATURE_LEVEL_WIDTH) - 1)
> -
> -/*
> - * Convert the workaround level into an easy-to-compare number, where higher
> - * values mean better protection.
> - */
> -static int get_kernel_wa_level(u64 regid)
> -{
> -	switch (regid) {
> -	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
> -		switch (arm64_get_spectre_v2_state()) {
> -		case SPECTRE_VULNERABLE:
> -			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
> -		case SPECTRE_MITIGATED:
> -			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_AVAIL;
> -		case SPECTRE_UNAFFECTED:
> -			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_REQUIRED;
> -		}
> -		return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
> -	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
> -		switch (arm64_get_spectre_v4_state()) {
> -		case SPECTRE_MITIGATED:
> -			/*
> -			 * As for the hypercall discovery, we pretend we
> -			 * don't have any FW mitigation if SSBS is there at
> -			 * all times.
> -			 */
> -			if (cpus_have_final_cap(ARM64_SSBS))
> -				return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
> -			fallthrough;
> -		case SPECTRE_UNAFFECTED:
> -			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
> -		case SPECTRE_VULNERABLE:
> -			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
> -		}
> -		break;
> -	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
> -		switch (arm64_get_spectre_bhb_state()) {
> -		case SPECTRE_VULNERABLE:
> -			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
> -		case SPECTRE_MITIGATED:
> -			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_AVAIL;
> -		case SPECTRE_UNAFFECTED:
> -			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_REQUIRED;
> -		}
> -		return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
> -	}
> -
> -	return -EINVAL;
> -}
> -
> -int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> -{
> -	void __user *uaddr = (void __user *)(long)reg->addr;
> -	u64 val;
> -
> -	switch (reg->id) {
> -	case KVM_REG_ARM_PSCI_VERSION:
> -		val = kvm_psci_version(vcpu);
> -		break;
> -	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
> -	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
> -	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
> -		val = get_kernel_wa_level(reg->id) & KVM_REG_FEATURE_LEVEL_MASK;
> -		break;
> -	default:
> -		return -ENOENT;
> -	}
> -
> -	if (copy_to_user(uaddr, &val, KVM_REG_SIZE(reg->id)))
> -		return -EFAULT;
> -
> -	return 0;
> -}
> -
> -int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> -{
> -	void __user *uaddr = (void __user *)(long)reg->addr;
> -	u64 val;
> -	int wa_level;
> -
> -	if (copy_from_user(&val, uaddr, KVM_REG_SIZE(reg->id)))
> -		return -EFAULT;
> -
> -	switch (reg->id) {
> -	case KVM_REG_ARM_PSCI_VERSION:
> -	{
> -		bool wants_02;
> -
> -		wants_02 = test_bit(KVM_ARM_VCPU_PSCI_0_2, vcpu->arch.features);
> -
> -		switch (val) {
> -		case KVM_ARM_PSCI_0_1:
> -			if (wants_02)
> -				return -EINVAL;
> -			vcpu->kvm->arch.psci_version = val;
> -			return 0;
> -		case KVM_ARM_PSCI_0_2:
> -		case KVM_ARM_PSCI_1_0:
> -		case KVM_ARM_PSCI_1_1:
> -			if (!wants_02)
> -				return -EINVAL;
> -			vcpu->kvm->arch.psci_version = val;
> -			return 0;
> -		}
> -		break;
> -	}
> -
> -	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
> -	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
> -		if (val & ~KVM_REG_FEATURE_LEVEL_MASK)
> -			return -EINVAL;
> -
> -		if (get_kernel_wa_level(reg->id) < val)
> -			return -EINVAL;
> -
> -		return 0;
> -
> -	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
> -		if (val & ~(KVM_REG_FEATURE_LEVEL_MASK |
> -			    KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED))
> -			return -EINVAL;
> -
> -		/* The enabled bit must not be set unless the level is AVAIL. */
> -		if ((val & KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED) &&
> -		    (val & KVM_REG_FEATURE_LEVEL_MASK) != KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL)
> -			return -EINVAL;
> -
> -		/*
> -		 * Map all the possible incoming states to the only two we
> -		 * really want to deal with.
> -		 */
> -		switch (val & KVM_REG_FEATURE_LEVEL_MASK) {
> -		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL:
> -		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_UNKNOWN:
> -			wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
> -			break;
> -		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL:
> -		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED:
> -			wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
> -			break;
> -		default:
> -			return -EINVAL;
> -		}
> -
> -		/*
> -		 * We can deal with NOT_AVAIL on NOT_REQUIRED, but not the
> -		 * other way around.
> -		 */
> -		if (get_kernel_wa_level(reg->id) < wa_level)
> -			return -EINVAL;
> -
> -		return 0;
> -	default:
> -		return -ENOENT;
> -	}
> -
> -	return -EINVAL;
> -}
> diff --git a/include/kvm/arm_hypercalls.h b/include/kvm/arm_hypercalls.h
> index 0e2509d27910..5d38628a8d04 100644
> --- a/include/kvm/arm_hypercalls.h
> +++ b/include/kvm/arm_hypercalls.h
> @@ -40,4 +40,11 @@ static inline void smccc_set_retval(struct kvm_vcpu *vcpu,
>   	vcpu_set_reg(vcpu, 3, a3);
>   }
>   
> +struct kvm_one_reg;
> +
> +int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu);
> +int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices);
> +int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
> +int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
> +
>   #endif
> diff --git a/include/kvm/arm_psci.h b/include/kvm/arm_psci.h
> index 68b96c3826c3..6e55b9283789 100644
> --- a/include/kvm/arm_psci.h
> +++ b/include/kvm/arm_psci.h
> @@ -39,11 +39,4 @@ static inline int kvm_psci_version(struct kvm_vcpu *vcpu)
>   
>   int kvm_psci_call(struct kvm_vcpu *vcpu);
>   
> -struct kvm_one_reg;
> -
> -int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu);
> -int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices);
> -int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
> -int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
> -
>   #endif /* __KVM_ARM_PSCI_H__ */
> 

Thanks,
Gavin

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

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

* Re: [PATCH v5 01/10] KVM: arm64: Factor out firmware register handling from psci.c
@ 2022-04-12  7:06     ` Gavin Shan
  0 siblings, 0 replies; 96+ messages in thread
From: Gavin Shan @ 2022-04-12  7:06 UTC (permalink / raw)
  To: Raghavendra Rao Ananta, Marc Zyngier, Andrew Jones, James Morse,
	Alexandru Elisei, Suzuki K Poulose
  Cc: kvm, Catalin Marinas, Peter Shier, linux-kernel, Paolo Bonzini,
	Will Deacon, kvmarm, linux-arm-kernel

Hi Raghavendra,

On 4/7/22 9:15 AM, Raghavendra Rao Ananta wrote:
> Common hypercall firmware register handing is currently employed
> by psci.c. Since the upcoming patches add more of these registers,
> it's better to move the generic handling to hypercall.c for a
> cleaner presentation.
> 
> While we are at it, collect all the firmware registers under
> fw_reg_ids[] to help implement kvm_arm_get_fw_num_regs() and
> kvm_arm_copy_fw_reg_indices() in a generic way. Also, define
> KVM_REG_FEATURE_LEVEL_MASK using a GENMASK instead.
> 
> No functional change intended.
> 
> Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
> Reviewed-by: Oliver Upton <oupton@google.com>
> ---
>   arch/arm64/kvm/guest.c       |   2 +-
>   arch/arm64/kvm/hypercalls.c  | 185 +++++++++++++++++++++++++++++++++++
>   arch/arm64/kvm/psci.c        | 183 ----------------------------------
>   include/kvm/arm_hypercalls.h |   7 ++
>   include/kvm/arm_psci.h       |   7 --
>   5 files changed, 193 insertions(+), 191 deletions(-)
> 

Apart from the below nits:

Reviewed-by: Gavin Shan <gshan@redhat.com>

> diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
> index 7e15b03fbdf8..0d5cca56cbda 100644
> --- a/arch/arm64/kvm/guest.c
> +++ b/arch/arm64/kvm/guest.c
> @@ -18,7 +18,7 @@
>   #include <linux/string.h>
>   #include <linux/vmalloc.h>
>   #include <linux/fs.h>
> -#include <kvm/arm_psci.h>
> +#include <kvm/arm_hypercalls.h>
>   #include <asm/cputype.h>
>   #include <linux/uaccess.h>
>   #include <asm/fpsimd.h>
> diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c
> index 202b8c455724..fa6d9378d8e7 100644
> --- a/arch/arm64/kvm/hypercalls.c
> +++ b/arch/arm64/kvm/hypercalls.c
> @@ -158,3 +158,188 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
>   	smccc_set_retval(vcpu, val[0], val[1], val[2], val[3]);
>   	return 1;
>   }
> +
> +static const u64 kvm_arm_fw_reg_ids[] = {
> +	KVM_REG_ARM_PSCI_VERSION,
> +	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1,
> +	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2,
> +	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3,
> +};
> +
> +int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
> +{
> +	return ARRAY_SIZE(kvm_arm_fw_reg_ids);
> +}
> +
> +int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(kvm_arm_fw_reg_ids); i++) {
> +		if (put_user(kvm_arm_fw_reg_ids[i], uindices++))
> +			return -EFAULT;
> +	}
> +
> +	return 0;
> +}
> +

Since we're here, I think we can make this function to return 'ARRAY_SIZE(kvm_arm_fw_reg_ids)',
to be consistent with copy_{core, sve}_reg_indices(). With the return value fixed, additional
patch can use @ret in kvm_arm_copy_reg_indices().

> +#define KVM_REG_FEATURE_LEVEL_WIDTH	4
> +#define KVM_REG_FEATURE_LEVEL_MASK	GENMASK(KVM_REG_FEATURE_LEVEL_WIDTH, 0)
> +

It seems 'BIT()' is replaced with 'GENMASK' in the movement, but it's not mentioned
in the commit log. I guess it'd better to mention it if you agree.

> +/*
> + * Convert the workaround level into an easy-to-compare number, where higher
> + * values mean better protection.
> + */
> +static int get_kernel_wa_level(u64 regid)
> +{
> +	switch (regid) {
> +	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
> +		switch (arm64_get_spectre_v2_state()) {
> +		case SPECTRE_VULNERABLE:
> +			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
> +		case SPECTRE_MITIGATED:
> +			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_AVAIL;
> +		case SPECTRE_UNAFFECTED:
> +			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_REQUIRED;
> +		}
> +		return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
> +	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
> +		switch (arm64_get_spectre_v4_state()) {
> +		case SPECTRE_MITIGATED:
> +			/*
> +			 * As for the hypercall discovery, we pretend we
> +			 * don't have any FW mitigation if SSBS is there at
> +			 * all times.
> +			 */
> +			if (cpus_have_final_cap(ARM64_SSBS))
> +				return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
> +			fallthrough;
> +		case SPECTRE_UNAFFECTED:
> +			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
> +		case SPECTRE_VULNERABLE:
> +			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
> +		}
> +		break;
> +	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
> +		switch (arm64_get_spectre_bhb_state()) {
> +		case SPECTRE_VULNERABLE:
> +			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
> +		case SPECTRE_MITIGATED:
> +			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_AVAIL;
> +		case SPECTRE_UNAFFECTED:
> +			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_REQUIRED;
> +		}
> +		return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> +{
> +	void __user *uaddr = (void __user *)(long)reg->addr;
> +	u64 val;
> +
> +	switch (reg->id) {
> +	case KVM_REG_ARM_PSCI_VERSION:
> +		val = kvm_psci_version(vcpu);
> +		break;
> +	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
> +	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
> +	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
> +		val = get_kernel_wa_level(reg->id) & KVM_REG_FEATURE_LEVEL_MASK;
> +		break;
> +	default:
> +		return -ENOENT;
> +	}
> +
> +	if (copy_to_user(uaddr, &val, KVM_REG_SIZE(reg->id)))
> +		return -EFAULT;
> +
> +	return 0;
> +}
> +
> +int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> +{
> +	void __user *uaddr = (void __user *)(long)reg->addr;
> +	u64 val;
> +	int wa_level;
> +
> +	if (copy_from_user(&val, uaddr, KVM_REG_SIZE(reg->id)))
> +		return -EFAULT;
> +
> +	switch (reg->id) {
> +	case KVM_REG_ARM_PSCI_VERSION:
> +	{
> +		bool wants_02;
> +
> +		wants_02 = test_bit(KVM_ARM_VCPU_PSCI_0_2, vcpu->arch.features);
> +
> +		switch (val) {
> +		case KVM_ARM_PSCI_0_1:
> +			if (wants_02)
> +				return -EINVAL;
> +			vcpu->kvm->arch.psci_version = val;
> +			return 0;
> +		case KVM_ARM_PSCI_0_2:
> +		case KVM_ARM_PSCI_1_0:
> +		case KVM_ARM_PSCI_1_1:
> +			if (!wants_02)
> +				return -EINVAL;
> +			vcpu->kvm->arch.psci_version = val;
> +			return 0;
> +		}
> +		break;
> +	}
> +
> +	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
> +	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
> +		if (val & ~KVM_REG_FEATURE_LEVEL_MASK)
> +			return -EINVAL;
> +
> +		if (get_kernel_wa_level(reg->id) < val)
> +			return -EINVAL;
> +
> +		return 0;
> +
> +	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
> +		if (val & ~(KVM_REG_FEATURE_LEVEL_MASK |
> +			    KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED))
> +			return -EINVAL;
> +
> +		/* The enabled bit must not be set unless the level is AVAIL. */
> +		if ((val & KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED) &&
> +		    (val & KVM_REG_FEATURE_LEVEL_MASK) != KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL)
> +			return -EINVAL;
> +
> +		/*
> +		 * Map all the possible incoming states to the only two we
> +		 * really want to deal with.
> +		 */
> +		switch (val & KVM_REG_FEATURE_LEVEL_MASK) {
> +		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL:
> +		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_UNKNOWN:
> +			wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
> +			break;
> +		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL:
> +		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED:
> +			wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
> +			break;
> +		default:
> +			return -EINVAL;
> +		}
> +
> +		/*
> +		 * We can deal with NOT_AVAIL on NOT_REQUIRED, but not the
> +		 * other way around.
> +		 */
> +		if (get_kernel_wa_level(reg->id) < wa_level)
> +			return -EINVAL;
> +
> +		return 0;
> +	default:
> +		return -ENOENT;
> +	}
> +
> +	return -EINVAL;
> +}
> diff --git a/arch/arm64/kvm/psci.c b/arch/arm64/kvm/psci.c
> index 372da09a2fab..bdfa93ca57d1 100644
> --- a/arch/arm64/kvm/psci.c
> +++ b/arch/arm64/kvm/psci.c
> @@ -439,186 +439,3 @@ int kvm_psci_call(struct kvm_vcpu *vcpu)
>   		return -EINVAL;
>   	}
>   }
> -
> -int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
> -{
> -	return 4;		/* PSCI version and three workaround registers */
> -}
> -
> -int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
> -{
> -	if (put_user(KVM_REG_ARM_PSCI_VERSION, uindices++))
> -		return -EFAULT;
> -
> -	if (put_user(KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1, uindices++))
> -		return -EFAULT;
> -
> -	if (put_user(KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2, uindices++))
> -		return -EFAULT;
> -
> -	if (put_user(KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3, uindices++))
> -		return -EFAULT;
> -
> -	return 0;
> -}
> -
> -#define KVM_REG_FEATURE_LEVEL_WIDTH	4
> -#define KVM_REG_FEATURE_LEVEL_MASK	(BIT(KVM_REG_FEATURE_LEVEL_WIDTH) - 1)
> -
> -/*
> - * Convert the workaround level into an easy-to-compare number, where higher
> - * values mean better protection.
> - */
> -static int get_kernel_wa_level(u64 regid)
> -{
> -	switch (regid) {
> -	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
> -		switch (arm64_get_spectre_v2_state()) {
> -		case SPECTRE_VULNERABLE:
> -			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
> -		case SPECTRE_MITIGATED:
> -			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_AVAIL;
> -		case SPECTRE_UNAFFECTED:
> -			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_REQUIRED;
> -		}
> -		return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
> -	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
> -		switch (arm64_get_spectre_v4_state()) {
> -		case SPECTRE_MITIGATED:
> -			/*
> -			 * As for the hypercall discovery, we pretend we
> -			 * don't have any FW mitigation if SSBS is there at
> -			 * all times.
> -			 */
> -			if (cpus_have_final_cap(ARM64_SSBS))
> -				return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
> -			fallthrough;
> -		case SPECTRE_UNAFFECTED:
> -			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
> -		case SPECTRE_VULNERABLE:
> -			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
> -		}
> -		break;
> -	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
> -		switch (arm64_get_spectre_bhb_state()) {
> -		case SPECTRE_VULNERABLE:
> -			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
> -		case SPECTRE_MITIGATED:
> -			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_AVAIL;
> -		case SPECTRE_UNAFFECTED:
> -			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_REQUIRED;
> -		}
> -		return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
> -	}
> -
> -	return -EINVAL;
> -}
> -
> -int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> -{
> -	void __user *uaddr = (void __user *)(long)reg->addr;
> -	u64 val;
> -
> -	switch (reg->id) {
> -	case KVM_REG_ARM_PSCI_VERSION:
> -		val = kvm_psci_version(vcpu);
> -		break;
> -	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
> -	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
> -	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
> -		val = get_kernel_wa_level(reg->id) & KVM_REG_FEATURE_LEVEL_MASK;
> -		break;
> -	default:
> -		return -ENOENT;
> -	}
> -
> -	if (copy_to_user(uaddr, &val, KVM_REG_SIZE(reg->id)))
> -		return -EFAULT;
> -
> -	return 0;
> -}
> -
> -int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> -{
> -	void __user *uaddr = (void __user *)(long)reg->addr;
> -	u64 val;
> -	int wa_level;
> -
> -	if (copy_from_user(&val, uaddr, KVM_REG_SIZE(reg->id)))
> -		return -EFAULT;
> -
> -	switch (reg->id) {
> -	case KVM_REG_ARM_PSCI_VERSION:
> -	{
> -		bool wants_02;
> -
> -		wants_02 = test_bit(KVM_ARM_VCPU_PSCI_0_2, vcpu->arch.features);
> -
> -		switch (val) {
> -		case KVM_ARM_PSCI_0_1:
> -			if (wants_02)
> -				return -EINVAL;
> -			vcpu->kvm->arch.psci_version = val;
> -			return 0;
> -		case KVM_ARM_PSCI_0_2:
> -		case KVM_ARM_PSCI_1_0:
> -		case KVM_ARM_PSCI_1_1:
> -			if (!wants_02)
> -				return -EINVAL;
> -			vcpu->kvm->arch.psci_version = val;
> -			return 0;
> -		}
> -		break;
> -	}
> -
> -	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
> -	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
> -		if (val & ~KVM_REG_FEATURE_LEVEL_MASK)
> -			return -EINVAL;
> -
> -		if (get_kernel_wa_level(reg->id) < val)
> -			return -EINVAL;
> -
> -		return 0;
> -
> -	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
> -		if (val & ~(KVM_REG_FEATURE_LEVEL_MASK |
> -			    KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED))
> -			return -EINVAL;
> -
> -		/* The enabled bit must not be set unless the level is AVAIL. */
> -		if ((val & KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED) &&
> -		    (val & KVM_REG_FEATURE_LEVEL_MASK) != KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL)
> -			return -EINVAL;
> -
> -		/*
> -		 * Map all the possible incoming states to the only two we
> -		 * really want to deal with.
> -		 */
> -		switch (val & KVM_REG_FEATURE_LEVEL_MASK) {
> -		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL:
> -		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_UNKNOWN:
> -			wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
> -			break;
> -		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL:
> -		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED:
> -			wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
> -			break;
> -		default:
> -			return -EINVAL;
> -		}
> -
> -		/*
> -		 * We can deal with NOT_AVAIL on NOT_REQUIRED, but not the
> -		 * other way around.
> -		 */
> -		if (get_kernel_wa_level(reg->id) < wa_level)
> -			return -EINVAL;
> -
> -		return 0;
> -	default:
> -		return -ENOENT;
> -	}
> -
> -	return -EINVAL;
> -}
> diff --git a/include/kvm/arm_hypercalls.h b/include/kvm/arm_hypercalls.h
> index 0e2509d27910..5d38628a8d04 100644
> --- a/include/kvm/arm_hypercalls.h
> +++ b/include/kvm/arm_hypercalls.h
> @@ -40,4 +40,11 @@ static inline void smccc_set_retval(struct kvm_vcpu *vcpu,
>   	vcpu_set_reg(vcpu, 3, a3);
>   }
>   
> +struct kvm_one_reg;
> +
> +int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu);
> +int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices);
> +int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
> +int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
> +
>   #endif
> diff --git a/include/kvm/arm_psci.h b/include/kvm/arm_psci.h
> index 68b96c3826c3..6e55b9283789 100644
> --- a/include/kvm/arm_psci.h
> +++ b/include/kvm/arm_psci.h
> @@ -39,11 +39,4 @@ static inline int kvm_psci_version(struct kvm_vcpu *vcpu)
>   
>   int kvm_psci_call(struct kvm_vcpu *vcpu);
>   
> -struct kvm_one_reg;
> -
> -int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu);
> -int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices);
> -int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
> -int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
> -
>   #endif /* __KVM_ARM_PSCI_H__ */
> 

Thanks,
Gavin


_______________________________________________
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] 96+ messages in thread

* Re: [PATCH v5 01/10] KVM: arm64: Factor out firmware register handling from psci.c
@ 2022-04-12  7:06     ` Gavin Shan
  0 siblings, 0 replies; 96+ messages in thread
From: Gavin Shan @ 2022-04-12  7:06 UTC (permalink / raw)
  To: Raghavendra Rao Ananta, Marc Zyngier, Andrew Jones, James Morse,
	Alexandru Elisei, Suzuki K Poulose
  Cc: kvm, Catalin Marinas, Peter Shier, linux-kernel, Paolo Bonzini,
	Will Deacon, kvmarm, linux-arm-kernel

Hi Raghavendra,

On 4/7/22 9:15 AM, Raghavendra Rao Ananta wrote:
> Common hypercall firmware register handing is currently employed
> by psci.c. Since the upcoming patches add more of these registers,
> it's better to move the generic handling to hypercall.c for a
> cleaner presentation.
> 
> While we are at it, collect all the firmware registers under
> fw_reg_ids[] to help implement kvm_arm_get_fw_num_regs() and
> kvm_arm_copy_fw_reg_indices() in a generic way. Also, define
> KVM_REG_FEATURE_LEVEL_MASK using a GENMASK instead.
> 
> No functional change intended.
> 
> Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
> Reviewed-by: Oliver Upton <oupton@google.com>
> ---
>   arch/arm64/kvm/guest.c       |   2 +-
>   arch/arm64/kvm/hypercalls.c  | 185 +++++++++++++++++++++++++++++++++++
>   arch/arm64/kvm/psci.c        | 183 ----------------------------------
>   include/kvm/arm_hypercalls.h |   7 ++
>   include/kvm/arm_psci.h       |   7 --
>   5 files changed, 193 insertions(+), 191 deletions(-)
> 

Apart from the below nits:

Reviewed-by: Gavin Shan <gshan@redhat.com>

> diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
> index 7e15b03fbdf8..0d5cca56cbda 100644
> --- a/arch/arm64/kvm/guest.c
> +++ b/arch/arm64/kvm/guest.c
> @@ -18,7 +18,7 @@
>   #include <linux/string.h>
>   #include <linux/vmalloc.h>
>   #include <linux/fs.h>
> -#include <kvm/arm_psci.h>
> +#include <kvm/arm_hypercalls.h>
>   #include <asm/cputype.h>
>   #include <linux/uaccess.h>
>   #include <asm/fpsimd.h>
> diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c
> index 202b8c455724..fa6d9378d8e7 100644
> --- a/arch/arm64/kvm/hypercalls.c
> +++ b/arch/arm64/kvm/hypercalls.c
> @@ -158,3 +158,188 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
>   	smccc_set_retval(vcpu, val[0], val[1], val[2], val[3]);
>   	return 1;
>   }
> +
> +static const u64 kvm_arm_fw_reg_ids[] = {
> +	KVM_REG_ARM_PSCI_VERSION,
> +	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1,
> +	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2,
> +	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3,
> +};
> +
> +int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
> +{
> +	return ARRAY_SIZE(kvm_arm_fw_reg_ids);
> +}
> +
> +int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(kvm_arm_fw_reg_ids); i++) {
> +		if (put_user(kvm_arm_fw_reg_ids[i], uindices++))
> +			return -EFAULT;
> +	}
> +
> +	return 0;
> +}
> +

Since we're here, I think we can make this function to return 'ARRAY_SIZE(kvm_arm_fw_reg_ids)',
to be consistent with copy_{core, sve}_reg_indices(). With the return value fixed, additional
patch can use @ret in kvm_arm_copy_reg_indices().

> +#define KVM_REG_FEATURE_LEVEL_WIDTH	4
> +#define KVM_REG_FEATURE_LEVEL_MASK	GENMASK(KVM_REG_FEATURE_LEVEL_WIDTH, 0)
> +

It seems 'BIT()' is replaced with 'GENMASK' in the movement, but it's not mentioned
in the commit log. I guess it'd better to mention it if you agree.

> +/*
> + * Convert the workaround level into an easy-to-compare number, where higher
> + * values mean better protection.
> + */
> +static int get_kernel_wa_level(u64 regid)
> +{
> +	switch (regid) {
> +	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
> +		switch (arm64_get_spectre_v2_state()) {
> +		case SPECTRE_VULNERABLE:
> +			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
> +		case SPECTRE_MITIGATED:
> +			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_AVAIL;
> +		case SPECTRE_UNAFFECTED:
> +			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_REQUIRED;
> +		}
> +		return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
> +	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
> +		switch (arm64_get_spectre_v4_state()) {
> +		case SPECTRE_MITIGATED:
> +			/*
> +			 * As for the hypercall discovery, we pretend we
> +			 * don't have any FW mitigation if SSBS is there at
> +			 * all times.
> +			 */
> +			if (cpus_have_final_cap(ARM64_SSBS))
> +				return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
> +			fallthrough;
> +		case SPECTRE_UNAFFECTED:
> +			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
> +		case SPECTRE_VULNERABLE:
> +			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
> +		}
> +		break;
> +	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
> +		switch (arm64_get_spectre_bhb_state()) {
> +		case SPECTRE_VULNERABLE:
> +			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
> +		case SPECTRE_MITIGATED:
> +			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_AVAIL;
> +		case SPECTRE_UNAFFECTED:
> +			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_REQUIRED;
> +		}
> +		return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> +{
> +	void __user *uaddr = (void __user *)(long)reg->addr;
> +	u64 val;
> +
> +	switch (reg->id) {
> +	case KVM_REG_ARM_PSCI_VERSION:
> +		val = kvm_psci_version(vcpu);
> +		break;
> +	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
> +	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
> +	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
> +		val = get_kernel_wa_level(reg->id) & KVM_REG_FEATURE_LEVEL_MASK;
> +		break;
> +	default:
> +		return -ENOENT;
> +	}
> +
> +	if (copy_to_user(uaddr, &val, KVM_REG_SIZE(reg->id)))
> +		return -EFAULT;
> +
> +	return 0;
> +}
> +
> +int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> +{
> +	void __user *uaddr = (void __user *)(long)reg->addr;
> +	u64 val;
> +	int wa_level;
> +
> +	if (copy_from_user(&val, uaddr, KVM_REG_SIZE(reg->id)))
> +		return -EFAULT;
> +
> +	switch (reg->id) {
> +	case KVM_REG_ARM_PSCI_VERSION:
> +	{
> +		bool wants_02;
> +
> +		wants_02 = test_bit(KVM_ARM_VCPU_PSCI_0_2, vcpu->arch.features);
> +
> +		switch (val) {
> +		case KVM_ARM_PSCI_0_1:
> +			if (wants_02)
> +				return -EINVAL;
> +			vcpu->kvm->arch.psci_version = val;
> +			return 0;
> +		case KVM_ARM_PSCI_0_2:
> +		case KVM_ARM_PSCI_1_0:
> +		case KVM_ARM_PSCI_1_1:
> +			if (!wants_02)
> +				return -EINVAL;
> +			vcpu->kvm->arch.psci_version = val;
> +			return 0;
> +		}
> +		break;
> +	}
> +
> +	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
> +	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
> +		if (val & ~KVM_REG_FEATURE_LEVEL_MASK)
> +			return -EINVAL;
> +
> +		if (get_kernel_wa_level(reg->id) < val)
> +			return -EINVAL;
> +
> +		return 0;
> +
> +	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
> +		if (val & ~(KVM_REG_FEATURE_LEVEL_MASK |
> +			    KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED))
> +			return -EINVAL;
> +
> +		/* The enabled bit must not be set unless the level is AVAIL. */
> +		if ((val & KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED) &&
> +		    (val & KVM_REG_FEATURE_LEVEL_MASK) != KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL)
> +			return -EINVAL;
> +
> +		/*
> +		 * Map all the possible incoming states to the only two we
> +		 * really want to deal with.
> +		 */
> +		switch (val & KVM_REG_FEATURE_LEVEL_MASK) {
> +		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL:
> +		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_UNKNOWN:
> +			wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
> +			break;
> +		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL:
> +		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED:
> +			wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
> +			break;
> +		default:
> +			return -EINVAL;
> +		}
> +
> +		/*
> +		 * We can deal with NOT_AVAIL on NOT_REQUIRED, but not the
> +		 * other way around.
> +		 */
> +		if (get_kernel_wa_level(reg->id) < wa_level)
> +			return -EINVAL;
> +
> +		return 0;
> +	default:
> +		return -ENOENT;
> +	}
> +
> +	return -EINVAL;
> +}
> diff --git a/arch/arm64/kvm/psci.c b/arch/arm64/kvm/psci.c
> index 372da09a2fab..bdfa93ca57d1 100644
> --- a/arch/arm64/kvm/psci.c
> +++ b/arch/arm64/kvm/psci.c
> @@ -439,186 +439,3 @@ int kvm_psci_call(struct kvm_vcpu *vcpu)
>   		return -EINVAL;
>   	}
>   }
> -
> -int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
> -{
> -	return 4;		/* PSCI version and three workaround registers */
> -}
> -
> -int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
> -{
> -	if (put_user(KVM_REG_ARM_PSCI_VERSION, uindices++))
> -		return -EFAULT;
> -
> -	if (put_user(KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1, uindices++))
> -		return -EFAULT;
> -
> -	if (put_user(KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2, uindices++))
> -		return -EFAULT;
> -
> -	if (put_user(KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3, uindices++))
> -		return -EFAULT;
> -
> -	return 0;
> -}
> -
> -#define KVM_REG_FEATURE_LEVEL_WIDTH	4
> -#define KVM_REG_FEATURE_LEVEL_MASK	(BIT(KVM_REG_FEATURE_LEVEL_WIDTH) - 1)
> -
> -/*
> - * Convert the workaround level into an easy-to-compare number, where higher
> - * values mean better protection.
> - */
> -static int get_kernel_wa_level(u64 regid)
> -{
> -	switch (regid) {
> -	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
> -		switch (arm64_get_spectre_v2_state()) {
> -		case SPECTRE_VULNERABLE:
> -			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
> -		case SPECTRE_MITIGATED:
> -			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_AVAIL;
> -		case SPECTRE_UNAFFECTED:
> -			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_REQUIRED;
> -		}
> -		return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
> -	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
> -		switch (arm64_get_spectre_v4_state()) {
> -		case SPECTRE_MITIGATED:
> -			/*
> -			 * As for the hypercall discovery, we pretend we
> -			 * don't have any FW mitigation if SSBS is there at
> -			 * all times.
> -			 */
> -			if (cpus_have_final_cap(ARM64_SSBS))
> -				return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
> -			fallthrough;
> -		case SPECTRE_UNAFFECTED:
> -			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
> -		case SPECTRE_VULNERABLE:
> -			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
> -		}
> -		break;
> -	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
> -		switch (arm64_get_spectre_bhb_state()) {
> -		case SPECTRE_VULNERABLE:
> -			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
> -		case SPECTRE_MITIGATED:
> -			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_AVAIL;
> -		case SPECTRE_UNAFFECTED:
> -			return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_REQUIRED;
> -		}
> -		return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
> -	}
> -
> -	return -EINVAL;
> -}
> -
> -int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> -{
> -	void __user *uaddr = (void __user *)(long)reg->addr;
> -	u64 val;
> -
> -	switch (reg->id) {
> -	case KVM_REG_ARM_PSCI_VERSION:
> -		val = kvm_psci_version(vcpu);
> -		break;
> -	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
> -	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
> -	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
> -		val = get_kernel_wa_level(reg->id) & KVM_REG_FEATURE_LEVEL_MASK;
> -		break;
> -	default:
> -		return -ENOENT;
> -	}
> -
> -	if (copy_to_user(uaddr, &val, KVM_REG_SIZE(reg->id)))
> -		return -EFAULT;
> -
> -	return 0;
> -}
> -
> -int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> -{
> -	void __user *uaddr = (void __user *)(long)reg->addr;
> -	u64 val;
> -	int wa_level;
> -
> -	if (copy_from_user(&val, uaddr, KVM_REG_SIZE(reg->id)))
> -		return -EFAULT;
> -
> -	switch (reg->id) {
> -	case KVM_REG_ARM_PSCI_VERSION:
> -	{
> -		bool wants_02;
> -
> -		wants_02 = test_bit(KVM_ARM_VCPU_PSCI_0_2, vcpu->arch.features);
> -
> -		switch (val) {
> -		case KVM_ARM_PSCI_0_1:
> -			if (wants_02)
> -				return -EINVAL;
> -			vcpu->kvm->arch.psci_version = val;
> -			return 0;
> -		case KVM_ARM_PSCI_0_2:
> -		case KVM_ARM_PSCI_1_0:
> -		case KVM_ARM_PSCI_1_1:
> -			if (!wants_02)
> -				return -EINVAL;
> -			vcpu->kvm->arch.psci_version = val;
> -			return 0;
> -		}
> -		break;
> -	}
> -
> -	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
> -	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
> -		if (val & ~KVM_REG_FEATURE_LEVEL_MASK)
> -			return -EINVAL;
> -
> -		if (get_kernel_wa_level(reg->id) < val)
> -			return -EINVAL;
> -
> -		return 0;
> -
> -	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
> -		if (val & ~(KVM_REG_FEATURE_LEVEL_MASK |
> -			    KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED))
> -			return -EINVAL;
> -
> -		/* The enabled bit must not be set unless the level is AVAIL. */
> -		if ((val & KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED) &&
> -		    (val & KVM_REG_FEATURE_LEVEL_MASK) != KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL)
> -			return -EINVAL;
> -
> -		/*
> -		 * Map all the possible incoming states to the only two we
> -		 * really want to deal with.
> -		 */
> -		switch (val & KVM_REG_FEATURE_LEVEL_MASK) {
> -		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL:
> -		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_UNKNOWN:
> -			wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
> -			break;
> -		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL:
> -		case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED:
> -			wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
> -			break;
> -		default:
> -			return -EINVAL;
> -		}
> -
> -		/*
> -		 * We can deal with NOT_AVAIL on NOT_REQUIRED, but not the
> -		 * other way around.
> -		 */
> -		if (get_kernel_wa_level(reg->id) < wa_level)
> -			return -EINVAL;
> -
> -		return 0;
> -	default:
> -		return -ENOENT;
> -	}
> -
> -	return -EINVAL;
> -}
> diff --git a/include/kvm/arm_hypercalls.h b/include/kvm/arm_hypercalls.h
> index 0e2509d27910..5d38628a8d04 100644
> --- a/include/kvm/arm_hypercalls.h
> +++ b/include/kvm/arm_hypercalls.h
> @@ -40,4 +40,11 @@ static inline void smccc_set_retval(struct kvm_vcpu *vcpu,
>   	vcpu_set_reg(vcpu, 3, a3);
>   }
>   
> +struct kvm_one_reg;
> +
> +int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu);
> +int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices);
> +int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
> +int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
> +
>   #endif
> diff --git a/include/kvm/arm_psci.h b/include/kvm/arm_psci.h
> index 68b96c3826c3..6e55b9283789 100644
> --- a/include/kvm/arm_psci.h
> +++ b/include/kvm/arm_psci.h
> @@ -39,11 +39,4 @@ static inline int kvm_psci_version(struct kvm_vcpu *vcpu)
>   
>   int kvm_psci_call(struct kvm_vcpu *vcpu);
>   
> -struct kvm_one_reg;
> -
> -int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu);
> -int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices);
> -int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
> -int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
> -
>   #endif /* __KVM_ARM_PSCI_H__ */
> 

Thanks,
Gavin


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

* Re: [PATCH v5 01/10] KVM: arm64: Factor out firmware register handling from psci.c
  2022-04-12  7:06     ` Gavin Shan
  (?)
@ 2022-04-12 16:41       ` Raghavendra Rao Ananta
  -1 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-12 16:41 UTC (permalink / raw)
  To: Gavin Shan
  Cc: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose, kvm, Catalin Marinas, Peter Shier,
	linux-kernel, Paolo Bonzini, Will Deacon, kvmarm,
	linux-arm-kernel

Hi Gavin,

On Tue, Apr 12, 2022 at 12:07 AM Gavin Shan <gshan@redhat.com> wrote:
>
> Hi Raghavendra,
>
> On 4/7/22 9:15 AM, Raghavendra Rao Ananta wrote:
> > Common hypercall firmware register handing is currently employed
> > by psci.c. Since the upcoming patches add more of these registers,
> > it's better to move the generic handling to hypercall.c for a
> > cleaner presentation.
> >
> > While we are at it, collect all the firmware registers under
> > fw_reg_ids[] to help implement kvm_arm_get_fw_num_regs() and
> > kvm_arm_copy_fw_reg_indices() in a generic way. Also, define
> > KVM_REG_FEATURE_LEVEL_MASK using a GENMASK instead.
> >
> > No functional change intended.
> >
> > Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
> > Reviewed-by: Oliver Upton <oupton@google.com>
> > ---
> >   arch/arm64/kvm/guest.c       |   2 +-
> >   arch/arm64/kvm/hypercalls.c  | 185 +++++++++++++++++++++++++++++++++++
> >   arch/arm64/kvm/psci.c        | 183 ----------------------------------
> >   include/kvm/arm_hypercalls.h |   7 ++
> >   include/kvm/arm_psci.h       |   7 --
> >   5 files changed, 193 insertions(+), 191 deletions(-)
> >
>
> Apart from the below nits:
>
> Reviewed-by: Gavin Shan <gshan@redhat.com>
>
> > diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
> > index 7e15b03fbdf8..0d5cca56cbda 100644
> > --- a/arch/arm64/kvm/guest.c
> > +++ b/arch/arm64/kvm/guest.c
> > @@ -18,7 +18,7 @@
> >   #include <linux/string.h>
> >   #include <linux/vmalloc.h>
> >   #include <linux/fs.h>
> > -#include <kvm/arm_psci.h>
> > +#include <kvm/arm_hypercalls.h>
> >   #include <asm/cputype.h>
> >   #include <linux/uaccess.h>
> >   #include <asm/fpsimd.h>
> > diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c
> > index 202b8c455724..fa6d9378d8e7 100644
> > --- a/arch/arm64/kvm/hypercalls.c
> > +++ b/arch/arm64/kvm/hypercalls.c
> > @@ -158,3 +158,188 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
> >       smccc_set_retval(vcpu, val[0], val[1], val[2], val[3]);
> >       return 1;
> >   }
> > +
> > +static const u64 kvm_arm_fw_reg_ids[] = {
> > +     KVM_REG_ARM_PSCI_VERSION,
> > +     KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1,
> > +     KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2,
> > +     KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3,
> > +};
> > +
> > +int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
> > +{
> > +     return ARRAY_SIZE(kvm_arm_fw_reg_ids);
> > +}
> > +
> > +int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
> > +{
> > +     int i;
> > +
> > +     for (i = 0; i < ARRAY_SIZE(kvm_arm_fw_reg_ids); i++) {
> > +             if (put_user(kvm_arm_fw_reg_ids[i], uindices++))
> > +                     return -EFAULT;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
>
> Since we're here, I think we can make this function to return 'ARRAY_SIZE(kvm_arm_fw_reg_ids)',
> to be consistent with copy_{core, sve}_reg_indices(). With the return value fixed, additional
> patch can use @ret in kvm_arm_copy_reg_indices().
>
Well we can, however, since this is mostly refactoring, I didn't want
to change the original functionality of the code.
The only caller of this is kvm_arm_copy_reg_indices()
(arch/arm64/kvm/guest.c), which only checks for 'ret < 0'.
Also, do you have a need for it? If yes, I can change it in the next revision.

> > +#define KVM_REG_FEATURE_LEVEL_WIDTH  4
> > +#define KVM_REG_FEATURE_LEVEL_MASK   GENMASK(KVM_REG_FEATURE_LEVEL_WIDTH, 0)
> > +
>
> It seems 'BIT()' is replaced with 'GENMASK' in the movement, but it's not mentioned
> in the commit log. I guess it'd better to mention it if you agree.
>
The last sentence of the commit text mentions this :)
Please let me know if it's not clear.

> > +/*
> > + * Convert the workaround level into an easy-to-compare number, where higher
> > + * values mean better protection.
> > + */
> > +static int get_kernel_wa_level(u64 regid)
> > +{
> > +     switch (regid) {
> > +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
> > +             switch (arm64_get_spectre_v2_state()) {
> > +             case SPECTRE_VULNERABLE:
> > +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
> > +             case SPECTRE_MITIGATED:
> > +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_AVAIL;
> > +             case SPECTRE_UNAFFECTED:
> > +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_REQUIRED;
> > +             }
> > +             return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
> > +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
> > +             switch (arm64_get_spectre_v4_state()) {
> > +             case SPECTRE_MITIGATED:
> > +                     /*
> > +                      * As for the hypercall discovery, we pretend we
> > +                      * don't have any FW mitigation if SSBS is there at
> > +                      * all times.
> > +                      */
> > +                     if (cpus_have_final_cap(ARM64_SSBS))
> > +                             return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
> > +                     fallthrough;
> > +             case SPECTRE_UNAFFECTED:
> > +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
> > +             case SPECTRE_VULNERABLE:
> > +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
> > +             }
> > +             break;
> > +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
> > +             switch (arm64_get_spectre_bhb_state()) {
> > +             case SPECTRE_VULNERABLE:
> > +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
> > +             case SPECTRE_MITIGATED:
> > +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_AVAIL;
> > +             case SPECTRE_UNAFFECTED:
> > +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_REQUIRED;
> > +             }
> > +             return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
> > +     }
> > +
> > +     return -EINVAL;
> > +}
> > +
> > +int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> > +{
> > +     void __user *uaddr = (void __user *)(long)reg->addr;
> > +     u64 val;
> > +
> > +     switch (reg->id) {
> > +     case KVM_REG_ARM_PSCI_VERSION:
> > +             val = kvm_psci_version(vcpu);
> > +             break;
> > +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
> > +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
> > +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
> > +             val = get_kernel_wa_level(reg->id) & KVM_REG_FEATURE_LEVEL_MASK;
> > +             break;
> > +     default:
> > +             return -ENOENT;
> > +     }
> > +
> > +     if (copy_to_user(uaddr, &val, KVM_REG_SIZE(reg->id)))
> > +             return -EFAULT;
> > +
> > +     return 0;
> > +}
> > +
> > +int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> > +{
> > +     void __user *uaddr = (void __user *)(long)reg->addr;
> > +     u64 val;
> > +     int wa_level;
> > +
> > +     if (copy_from_user(&val, uaddr, KVM_REG_SIZE(reg->id)))
> > +             return -EFAULT;
> > +
> > +     switch (reg->id) {
> > +     case KVM_REG_ARM_PSCI_VERSION:
> > +     {
> > +             bool wants_02;
> > +
> > +             wants_02 = test_bit(KVM_ARM_VCPU_PSCI_0_2, vcpu->arch.features);
> > +
> > +             switch (val) {
> > +             case KVM_ARM_PSCI_0_1:
> > +                     if (wants_02)
> > +                             return -EINVAL;
> > +                     vcpu->kvm->arch.psci_version = val;
> > +                     return 0;
> > +             case KVM_ARM_PSCI_0_2:
> > +             case KVM_ARM_PSCI_1_0:
> > +             case KVM_ARM_PSCI_1_1:
> > +                     if (!wants_02)
> > +                             return -EINVAL;
> > +                     vcpu->kvm->arch.psci_version = val;
> > +                     return 0;
> > +             }
> > +             break;
> > +     }
> > +
> > +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
> > +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
> > +             if (val & ~KVM_REG_FEATURE_LEVEL_MASK)
> > +                     return -EINVAL;
> > +
> > +             if (get_kernel_wa_level(reg->id) < val)
> > +                     return -EINVAL;
> > +
> > +             return 0;
> > +
> > +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
> > +             if (val & ~(KVM_REG_FEATURE_LEVEL_MASK |
> > +                         KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED))
> > +                     return -EINVAL;
> > +
> > +             /* The enabled bit must not be set unless the level is AVAIL. */
> > +             if ((val & KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED) &&
> > +                 (val & KVM_REG_FEATURE_LEVEL_MASK) != KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL)
> > +                     return -EINVAL;
> > +
> > +             /*
> > +              * Map all the possible incoming states to the only two we
> > +              * really want to deal with.
> > +              */
> > +             switch (val & KVM_REG_FEATURE_LEVEL_MASK) {
> > +             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL:
> > +             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_UNKNOWN:
> > +                     wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
> > +                     break;
> > +             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL:
> > +             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED:
> > +                     wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
> > +                     break;
> > +             default:
> > +                     return -EINVAL;
> > +             }
> > +
> > +             /*
> > +              * We can deal with NOT_AVAIL on NOT_REQUIRED, but not the
> > +              * other way around.
> > +              */
> > +             if (get_kernel_wa_level(reg->id) < wa_level)
> > +                     return -EINVAL;
> > +
> > +             return 0;
> > +     default:
> > +             return -ENOENT;
> > +     }
> > +
> > +     return -EINVAL;
> > +}
> > diff --git a/arch/arm64/kvm/psci.c b/arch/arm64/kvm/psci.c
> > index 372da09a2fab..bdfa93ca57d1 100644
> > --- a/arch/arm64/kvm/psci.c
> > +++ b/arch/arm64/kvm/psci.c
> > @@ -439,186 +439,3 @@ int kvm_psci_call(struct kvm_vcpu *vcpu)
> >               return -EINVAL;
> >       }
> >   }
> > -
> > -int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
> > -{
> > -     return 4;               /* PSCI version and three workaround registers */
> > -}
> > -
> > -int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
> > -{
> > -     if (put_user(KVM_REG_ARM_PSCI_VERSION, uindices++))
> > -             return -EFAULT;
> > -
> > -     if (put_user(KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1, uindices++))
> > -             return -EFAULT;
> > -
> > -     if (put_user(KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2, uindices++))
> > -             return -EFAULT;
> > -
> > -     if (put_user(KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3, uindices++))
> > -             return -EFAULT;
> > -
> > -     return 0;
> > -}
> > -
> > -#define KVM_REG_FEATURE_LEVEL_WIDTH  4
> > -#define KVM_REG_FEATURE_LEVEL_MASK   (BIT(KVM_REG_FEATURE_LEVEL_WIDTH) - 1)
> > -
> > -/*
> > - * Convert the workaround level into an easy-to-compare number, where higher
> > - * values mean better protection.
> > - */
> > -static int get_kernel_wa_level(u64 regid)
> > -{
> > -     switch (regid) {
> > -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
> > -             switch (arm64_get_spectre_v2_state()) {
> > -             case SPECTRE_VULNERABLE:
> > -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
> > -             case SPECTRE_MITIGATED:
> > -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_AVAIL;
> > -             case SPECTRE_UNAFFECTED:
> > -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_REQUIRED;
> > -             }
> > -             return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
> > -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
> > -             switch (arm64_get_spectre_v4_state()) {
> > -             case SPECTRE_MITIGATED:
> > -                     /*
> > -                      * As for the hypercall discovery, we pretend we
> > -                      * don't have any FW mitigation if SSBS is there at
> > -                      * all times.
> > -                      */
> > -                     if (cpus_have_final_cap(ARM64_SSBS))
> > -                             return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
> > -                     fallthrough;
> > -             case SPECTRE_UNAFFECTED:
> > -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
> > -             case SPECTRE_VULNERABLE:
> > -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
> > -             }
> > -             break;
> > -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
> > -             switch (arm64_get_spectre_bhb_state()) {
> > -             case SPECTRE_VULNERABLE:
> > -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
> > -             case SPECTRE_MITIGATED:
> > -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_AVAIL;
> > -             case SPECTRE_UNAFFECTED:
> > -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_REQUIRED;
> > -             }
> > -             return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
> > -     }
> > -
> > -     return -EINVAL;
> > -}
> > -
> > -int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> > -{
> > -     void __user *uaddr = (void __user *)(long)reg->addr;
> > -     u64 val;
> > -
> > -     switch (reg->id) {
> > -     case KVM_REG_ARM_PSCI_VERSION:
> > -             val = kvm_psci_version(vcpu);
> > -             break;
> > -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
> > -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
> > -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
> > -             val = get_kernel_wa_level(reg->id) & KVM_REG_FEATURE_LEVEL_MASK;
> > -             break;
> > -     default:
> > -             return -ENOENT;
> > -     }
> > -
> > -     if (copy_to_user(uaddr, &val, KVM_REG_SIZE(reg->id)))
> > -             return -EFAULT;
> > -
> > -     return 0;
> > -}
> > -
> > -int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> > -{
> > -     void __user *uaddr = (void __user *)(long)reg->addr;
> > -     u64 val;
> > -     int wa_level;
> > -
> > -     if (copy_from_user(&val, uaddr, KVM_REG_SIZE(reg->id)))
> > -             return -EFAULT;
> > -
> > -     switch (reg->id) {
> > -     case KVM_REG_ARM_PSCI_VERSION:
> > -     {
> > -             bool wants_02;
> > -
> > -             wants_02 = test_bit(KVM_ARM_VCPU_PSCI_0_2, vcpu->arch.features);
> > -
> > -             switch (val) {
> > -             case KVM_ARM_PSCI_0_1:
> > -                     if (wants_02)
> > -                             return -EINVAL;
> > -                     vcpu->kvm->arch.psci_version = val;
> > -                     return 0;
> > -             case KVM_ARM_PSCI_0_2:
> > -             case KVM_ARM_PSCI_1_0:
> > -             case KVM_ARM_PSCI_1_1:
> > -                     if (!wants_02)
> > -                             return -EINVAL;
> > -                     vcpu->kvm->arch.psci_version = val;
> > -                     return 0;
> > -             }
> > -             break;
> > -     }
> > -
> > -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
> > -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
> > -             if (val & ~KVM_REG_FEATURE_LEVEL_MASK)
> > -                     return -EINVAL;
> > -
> > -             if (get_kernel_wa_level(reg->id) < val)
> > -                     return -EINVAL;
> > -
> > -             return 0;
> > -
> > -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
> > -             if (val & ~(KVM_REG_FEATURE_LEVEL_MASK |
> > -                         KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED))
> > -                     return -EINVAL;
> > -
> > -             /* The enabled bit must not be set unless the level is AVAIL. */
> > -             if ((val & KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED) &&
> > -                 (val & KVM_REG_FEATURE_LEVEL_MASK) != KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL)
> > -                     return -EINVAL;
> > -
> > -             /*
> > -              * Map all the possible incoming states to the only two we
> > -              * really want to deal with.
> > -              */
> > -             switch (val & KVM_REG_FEATURE_LEVEL_MASK) {
> > -             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL:
> > -             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_UNKNOWN:
> > -                     wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
> > -                     break;
> > -             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL:
> > -             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED:
> > -                     wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
> > -                     break;
> > -             default:
> > -                     return -EINVAL;
> > -             }
> > -
> > -             /*
> > -              * We can deal with NOT_AVAIL on NOT_REQUIRED, but not the
> > -              * other way around.
> > -              */
> > -             if (get_kernel_wa_level(reg->id) < wa_level)
> > -                     return -EINVAL;
> > -
> > -             return 0;
> > -     default:
> > -             return -ENOENT;
> > -     }
> > -
> > -     return -EINVAL;
> > -}
> > diff --git a/include/kvm/arm_hypercalls.h b/include/kvm/arm_hypercalls.h
> > index 0e2509d27910..5d38628a8d04 100644
> > --- a/include/kvm/arm_hypercalls.h
> > +++ b/include/kvm/arm_hypercalls.h
> > @@ -40,4 +40,11 @@ static inline void smccc_set_retval(struct kvm_vcpu *vcpu,
> >       vcpu_set_reg(vcpu, 3, a3);
> >   }
> >
> > +struct kvm_one_reg;
> > +
> > +int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu);
> > +int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices);
> > +int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
> > +int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
> > +
> >   #endif
> > diff --git a/include/kvm/arm_psci.h b/include/kvm/arm_psci.h
> > index 68b96c3826c3..6e55b9283789 100644
> > --- a/include/kvm/arm_psci.h
> > +++ b/include/kvm/arm_psci.h
> > @@ -39,11 +39,4 @@ static inline int kvm_psci_version(struct kvm_vcpu *vcpu)
> >
> >   int kvm_psci_call(struct kvm_vcpu *vcpu);
> >
> > -struct kvm_one_reg;
> > -
> > -int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu);
> > -int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices);
> > -int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
> > -int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
> > -
> >   #endif /* __KVM_ARM_PSCI_H__ */
> >
>
> Thanks,
> Gavin
Thank you for the review, Gavin.

Regards,
Raghavendra
>

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

* Re: [PATCH v5 01/10] KVM: arm64: Factor out firmware register handling from psci.c
@ 2022-04-12 16:41       ` Raghavendra Rao Ananta
  0 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-12 16:41 UTC (permalink / raw)
  To: Gavin Shan
  Cc: kvm, Marc Zyngier, Peter Shier, linux-kernel, Will Deacon,
	Catalin Marinas, Paolo Bonzini, kvmarm, linux-arm-kernel

Hi Gavin,

On Tue, Apr 12, 2022 at 12:07 AM Gavin Shan <gshan@redhat.com> wrote:
>
> Hi Raghavendra,
>
> On 4/7/22 9:15 AM, Raghavendra Rao Ananta wrote:
> > Common hypercall firmware register handing is currently employed
> > by psci.c. Since the upcoming patches add more of these registers,
> > it's better to move the generic handling to hypercall.c for a
> > cleaner presentation.
> >
> > While we are at it, collect all the firmware registers under
> > fw_reg_ids[] to help implement kvm_arm_get_fw_num_regs() and
> > kvm_arm_copy_fw_reg_indices() in a generic way. Also, define
> > KVM_REG_FEATURE_LEVEL_MASK using a GENMASK instead.
> >
> > No functional change intended.
> >
> > Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
> > Reviewed-by: Oliver Upton <oupton@google.com>
> > ---
> >   arch/arm64/kvm/guest.c       |   2 +-
> >   arch/arm64/kvm/hypercalls.c  | 185 +++++++++++++++++++++++++++++++++++
> >   arch/arm64/kvm/psci.c        | 183 ----------------------------------
> >   include/kvm/arm_hypercalls.h |   7 ++
> >   include/kvm/arm_psci.h       |   7 --
> >   5 files changed, 193 insertions(+), 191 deletions(-)
> >
>
> Apart from the below nits:
>
> Reviewed-by: Gavin Shan <gshan@redhat.com>
>
> > diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
> > index 7e15b03fbdf8..0d5cca56cbda 100644
> > --- a/arch/arm64/kvm/guest.c
> > +++ b/arch/arm64/kvm/guest.c
> > @@ -18,7 +18,7 @@
> >   #include <linux/string.h>
> >   #include <linux/vmalloc.h>
> >   #include <linux/fs.h>
> > -#include <kvm/arm_psci.h>
> > +#include <kvm/arm_hypercalls.h>
> >   #include <asm/cputype.h>
> >   #include <linux/uaccess.h>
> >   #include <asm/fpsimd.h>
> > diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c
> > index 202b8c455724..fa6d9378d8e7 100644
> > --- a/arch/arm64/kvm/hypercalls.c
> > +++ b/arch/arm64/kvm/hypercalls.c
> > @@ -158,3 +158,188 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
> >       smccc_set_retval(vcpu, val[0], val[1], val[2], val[3]);
> >       return 1;
> >   }
> > +
> > +static const u64 kvm_arm_fw_reg_ids[] = {
> > +     KVM_REG_ARM_PSCI_VERSION,
> > +     KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1,
> > +     KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2,
> > +     KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3,
> > +};
> > +
> > +int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
> > +{
> > +     return ARRAY_SIZE(kvm_arm_fw_reg_ids);
> > +}
> > +
> > +int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
> > +{
> > +     int i;
> > +
> > +     for (i = 0; i < ARRAY_SIZE(kvm_arm_fw_reg_ids); i++) {
> > +             if (put_user(kvm_arm_fw_reg_ids[i], uindices++))
> > +                     return -EFAULT;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
>
> Since we're here, I think we can make this function to return 'ARRAY_SIZE(kvm_arm_fw_reg_ids)',
> to be consistent with copy_{core, sve}_reg_indices(). With the return value fixed, additional
> patch can use @ret in kvm_arm_copy_reg_indices().
>
Well we can, however, since this is mostly refactoring, I didn't want
to change the original functionality of the code.
The only caller of this is kvm_arm_copy_reg_indices()
(arch/arm64/kvm/guest.c), which only checks for 'ret < 0'.
Also, do you have a need for it? If yes, I can change it in the next revision.

> > +#define KVM_REG_FEATURE_LEVEL_WIDTH  4
> > +#define KVM_REG_FEATURE_LEVEL_MASK   GENMASK(KVM_REG_FEATURE_LEVEL_WIDTH, 0)
> > +
>
> It seems 'BIT()' is replaced with 'GENMASK' in the movement, but it's not mentioned
> in the commit log. I guess it'd better to mention it if you agree.
>
The last sentence of the commit text mentions this :)
Please let me know if it's not clear.

> > +/*
> > + * Convert the workaround level into an easy-to-compare number, where higher
> > + * values mean better protection.
> > + */
> > +static int get_kernel_wa_level(u64 regid)
> > +{
> > +     switch (regid) {
> > +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
> > +             switch (arm64_get_spectre_v2_state()) {
> > +             case SPECTRE_VULNERABLE:
> > +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
> > +             case SPECTRE_MITIGATED:
> > +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_AVAIL;
> > +             case SPECTRE_UNAFFECTED:
> > +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_REQUIRED;
> > +             }
> > +             return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
> > +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
> > +             switch (arm64_get_spectre_v4_state()) {
> > +             case SPECTRE_MITIGATED:
> > +                     /*
> > +                      * As for the hypercall discovery, we pretend we
> > +                      * don't have any FW mitigation if SSBS is there at
> > +                      * all times.
> > +                      */
> > +                     if (cpus_have_final_cap(ARM64_SSBS))
> > +                             return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
> > +                     fallthrough;
> > +             case SPECTRE_UNAFFECTED:
> > +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
> > +             case SPECTRE_VULNERABLE:
> > +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
> > +             }
> > +             break;
> > +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
> > +             switch (arm64_get_spectre_bhb_state()) {
> > +             case SPECTRE_VULNERABLE:
> > +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
> > +             case SPECTRE_MITIGATED:
> > +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_AVAIL;
> > +             case SPECTRE_UNAFFECTED:
> > +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_REQUIRED;
> > +             }
> > +             return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
> > +     }
> > +
> > +     return -EINVAL;
> > +}
> > +
> > +int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> > +{
> > +     void __user *uaddr = (void __user *)(long)reg->addr;
> > +     u64 val;
> > +
> > +     switch (reg->id) {
> > +     case KVM_REG_ARM_PSCI_VERSION:
> > +             val = kvm_psci_version(vcpu);
> > +             break;
> > +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
> > +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
> > +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
> > +             val = get_kernel_wa_level(reg->id) & KVM_REG_FEATURE_LEVEL_MASK;
> > +             break;
> > +     default:
> > +             return -ENOENT;
> > +     }
> > +
> > +     if (copy_to_user(uaddr, &val, KVM_REG_SIZE(reg->id)))
> > +             return -EFAULT;
> > +
> > +     return 0;
> > +}
> > +
> > +int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> > +{
> > +     void __user *uaddr = (void __user *)(long)reg->addr;
> > +     u64 val;
> > +     int wa_level;
> > +
> > +     if (copy_from_user(&val, uaddr, KVM_REG_SIZE(reg->id)))
> > +             return -EFAULT;
> > +
> > +     switch (reg->id) {
> > +     case KVM_REG_ARM_PSCI_VERSION:
> > +     {
> > +             bool wants_02;
> > +
> > +             wants_02 = test_bit(KVM_ARM_VCPU_PSCI_0_2, vcpu->arch.features);
> > +
> > +             switch (val) {
> > +             case KVM_ARM_PSCI_0_1:
> > +                     if (wants_02)
> > +                             return -EINVAL;
> > +                     vcpu->kvm->arch.psci_version = val;
> > +                     return 0;
> > +             case KVM_ARM_PSCI_0_2:
> > +             case KVM_ARM_PSCI_1_0:
> > +             case KVM_ARM_PSCI_1_1:
> > +                     if (!wants_02)
> > +                             return -EINVAL;
> > +                     vcpu->kvm->arch.psci_version = val;
> > +                     return 0;
> > +             }
> > +             break;
> > +     }
> > +
> > +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
> > +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
> > +             if (val & ~KVM_REG_FEATURE_LEVEL_MASK)
> > +                     return -EINVAL;
> > +
> > +             if (get_kernel_wa_level(reg->id) < val)
> > +                     return -EINVAL;
> > +
> > +             return 0;
> > +
> > +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
> > +             if (val & ~(KVM_REG_FEATURE_LEVEL_MASK |
> > +                         KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED))
> > +                     return -EINVAL;
> > +
> > +             /* The enabled bit must not be set unless the level is AVAIL. */
> > +             if ((val & KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED) &&
> > +                 (val & KVM_REG_FEATURE_LEVEL_MASK) != KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL)
> > +                     return -EINVAL;
> > +
> > +             /*
> > +              * Map all the possible incoming states to the only two we
> > +              * really want to deal with.
> > +              */
> > +             switch (val & KVM_REG_FEATURE_LEVEL_MASK) {
> > +             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL:
> > +             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_UNKNOWN:
> > +                     wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
> > +                     break;
> > +             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL:
> > +             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED:
> > +                     wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
> > +                     break;
> > +             default:
> > +                     return -EINVAL;
> > +             }
> > +
> > +             /*
> > +              * We can deal with NOT_AVAIL on NOT_REQUIRED, but not the
> > +              * other way around.
> > +              */
> > +             if (get_kernel_wa_level(reg->id) < wa_level)
> > +                     return -EINVAL;
> > +
> > +             return 0;
> > +     default:
> > +             return -ENOENT;
> > +     }
> > +
> > +     return -EINVAL;
> > +}
> > diff --git a/arch/arm64/kvm/psci.c b/arch/arm64/kvm/psci.c
> > index 372da09a2fab..bdfa93ca57d1 100644
> > --- a/arch/arm64/kvm/psci.c
> > +++ b/arch/arm64/kvm/psci.c
> > @@ -439,186 +439,3 @@ int kvm_psci_call(struct kvm_vcpu *vcpu)
> >               return -EINVAL;
> >       }
> >   }
> > -
> > -int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
> > -{
> > -     return 4;               /* PSCI version and three workaround registers */
> > -}
> > -
> > -int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
> > -{
> > -     if (put_user(KVM_REG_ARM_PSCI_VERSION, uindices++))
> > -             return -EFAULT;
> > -
> > -     if (put_user(KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1, uindices++))
> > -             return -EFAULT;
> > -
> > -     if (put_user(KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2, uindices++))
> > -             return -EFAULT;
> > -
> > -     if (put_user(KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3, uindices++))
> > -             return -EFAULT;
> > -
> > -     return 0;
> > -}
> > -
> > -#define KVM_REG_FEATURE_LEVEL_WIDTH  4
> > -#define KVM_REG_FEATURE_LEVEL_MASK   (BIT(KVM_REG_FEATURE_LEVEL_WIDTH) - 1)
> > -
> > -/*
> > - * Convert the workaround level into an easy-to-compare number, where higher
> > - * values mean better protection.
> > - */
> > -static int get_kernel_wa_level(u64 regid)
> > -{
> > -     switch (regid) {
> > -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
> > -             switch (arm64_get_spectre_v2_state()) {
> > -             case SPECTRE_VULNERABLE:
> > -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
> > -             case SPECTRE_MITIGATED:
> > -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_AVAIL;
> > -             case SPECTRE_UNAFFECTED:
> > -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_REQUIRED;
> > -             }
> > -             return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
> > -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
> > -             switch (arm64_get_spectre_v4_state()) {
> > -             case SPECTRE_MITIGATED:
> > -                     /*
> > -                      * As for the hypercall discovery, we pretend we
> > -                      * don't have any FW mitigation if SSBS is there at
> > -                      * all times.
> > -                      */
> > -                     if (cpus_have_final_cap(ARM64_SSBS))
> > -                             return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
> > -                     fallthrough;
> > -             case SPECTRE_UNAFFECTED:
> > -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
> > -             case SPECTRE_VULNERABLE:
> > -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
> > -             }
> > -             break;
> > -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
> > -             switch (arm64_get_spectre_bhb_state()) {
> > -             case SPECTRE_VULNERABLE:
> > -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
> > -             case SPECTRE_MITIGATED:
> > -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_AVAIL;
> > -             case SPECTRE_UNAFFECTED:
> > -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_REQUIRED;
> > -             }
> > -             return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
> > -     }
> > -
> > -     return -EINVAL;
> > -}
> > -
> > -int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> > -{
> > -     void __user *uaddr = (void __user *)(long)reg->addr;
> > -     u64 val;
> > -
> > -     switch (reg->id) {
> > -     case KVM_REG_ARM_PSCI_VERSION:
> > -             val = kvm_psci_version(vcpu);
> > -             break;
> > -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
> > -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
> > -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
> > -             val = get_kernel_wa_level(reg->id) & KVM_REG_FEATURE_LEVEL_MASK;
> > -             break;
> > -     default:
> > -             return -ENOENT;
> > -     }
> > -
> > -     if (copy_to_user(uaddr, &val, KVM_REG_SIZE(reg->id)))
> > -             return -EFAULT;
> > -
> > -     return 0;
> > -}
> > -
> > -int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> > -{
> > -     void __user *uaddr = (void __user *)(long)reg->addr;
> > -     u64 val;
> > -     int wa_level;
> > -
> > -     if (copy_from_user(&val, uaddr, KVM_REG_SIZE(reg->id)))
> > -             return -EFAULT;
> > -
> > -     switch (reg->id) {
> > -     case KVM_REG_ARM_PSCI_VERSION:
> > -     {
> > -             bool wants_02;
> > -
> > -             wants_02 = test_bit(KVM_ARM_VCPU_PSCI_0_2, vcpu->arch.features);
> > -
> > -             switch (val) {
> > -             case KVM_ARM_PSCI_0_1:
> > -                     if (wants_02)
> > -                             return -EINVAL;
> > -                     vcpu->kvm->arch.psci_version = val;
> > -                     return 0;
> > -             case KVM_ARM_PSCI_0_2:
> > -             case KVM_ARM_PSCI_1_0:
> > -             case KVM_ARM_PSCI_1_1:
> > -                     if (!wants_02)
> > -                             return -EINVAL;
> > -                     vcpu->kvm->arch.psci_version = val;
> > -                     return 0;
> > -             }
> > -             break;
> > -     }
> > -
> > -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
> > -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
> > -             if (val & ~KVM_REG_FEATURE_LEVEL_MASK)
> > -                     return -EINVAL;
> > -
> > -             if (get_kernel_wa_level(reg->id) < val)
> > -                     return -EINVAL;
> > -
> > -             return 0;
> > -
> > -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
> > -             if (val & ~(KVM_REG_FEATURE_LEVEL_MASK |
> > -                         KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED))
> > -                     return -EINVAL;
> > -
> > -             /* The enabled bit must not be set unless the level is AVAIL. */
> > -             if ((val & KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED) &&
> > -                 (val & KVM_REG_FEATURE_LEVEL_MASK) != KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL)
> > -                     return -EINVAL;
> > -
> > -             /*
> > -              * Map all the possible incoming states to the only two we
> > -              * really want to deal with.
> > -              */
> > -             switch (val & KVM_REG_FEATURE_LEVEL_MASK) {
> > -             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL:
> > -             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_UNKNOWN:
> > -                     wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
> > -                     break;
> > -             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL:
> > -             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED:
> > -                     wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
> > -                     break;
> > -             default:
> > -                     return -EINVAL;
> > -             }
> > -
> > -             /*
> > -              * We can deal with NOT_AVAIL on NOT_REQUIRED, but not the
> > -              * other way around.
> > -              */
> > -             if (get_kernel_wa_level(reg->id) < wa_level)
> > -                     return -EINVAL;
> > -
> > -             return 0;
> > -     default:
> > -             return -ENOENT;
> > -     }
> > -
> > -     return -EINVAL;
> > -}
> > diff --git a/include/kvm/arm_hypercalls.h b/include/kvm/arm_hypercalls.h
> > index 0e2509d27910..5d38628a8d04 100644
> > --- a/include/kvm/arm_hypercalls.h
> > +++ b/include/kvm/arm_hypercalls.h
> > @@ -40,4 +40,11 @@ static inline void smccc_set_retval(struct kvm_vcpu *vcpu,
> >       vcpu_set_reg(vcpu, 3, a3);
> >   }
> >
> > +struct kvm_one_reg;
> > +
> > +int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu);
> > +int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices);
> > +int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
> > +int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
> > +
> >   #endif
> > diff --git a/include/kvm/arm_psci.h b/include/kvm/arm_psci.h
> > index 68b96c3826c3..6e55b9283789 100644
> > --- a/include/kvm/arm_psci.h
> > +++ b/include/kvm/arm_psci.h
> > @@ -39,11 +39,4 @@ static inline int kvm_psci_version(struct kvm_vcpu *vcpu)
> >
> >   int kvm_psci_call(struct kvm_vcpu *vcpu);
> >
> > -struct kvm_one_reg;
> > -
> > -int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu);
> > -int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices);
> > -int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
> > -int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
> > -
> >   #endif /* __KVM_ARM_PSCI_H__ */
> >
>
> Thanks,
> Gavin
Thank you for the review, Gavin.

Regards,
Raghavendra
>
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

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

* Re: [PATCH v5 01/10] KVM: arm64: Factor out firmware register handling from psci.c
@ 2022-04-12 16:41       ` Raghavendra Rao Ananta
  0 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-12 16:41 UTC (permalink / raw)
  To: Gavin Shan
  Cc: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose, kvm, Catalin Marinas, Peter Shier,
	linux-kernel, Paolo Bonzini, Will Deacon, kvmarm,
	linux-arm-kernel

Hi Gavin,

On Tue, Apr 12, 2022 at 12:07 AM Gavin Shan <gshan@redhat.com> wrote:
>
> Hi Raghavendra,
>
> On 4/7/22 9:15 AM, Raghavendra Rao Ananta wrote:
> > Common hypercall firmware register handing is currently employed
> > by psci.c. Since the upcoming patches add more of these registers,
> > it's better to move the generic handling to hypercall.c for a
> > cleaner presentation.
> >
> > While we are at it, collect all the firmware registers under
> > fw_reg_ids[] to help implement kvm_arm_get_fw_num_regs() and
> > kvm_arm_copy_fw_reg_indices() in a generic way. Also, define
> > KVM_REG_FEATURE_LEVEL_MASK using a GENMASK instead.
> >
> > No functional change intended.
> >
> > Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
> > Reviewed-by: Oliver Upton <oupton@google.com>
> > ---
> >   arch/arm64/kvm/guest.c       |   2 +-
> >   arch/arm64/kvm/hypercalls.c  | 185 +++++++++++++++++++++++++++++++++++
> >   arch/arm64/kvm/psci.c        | 183 ----------------------------------
> >   include/kvm/arm_hypercalls.h |   7 ++
> >   include/kvm/arm_psci.h       |   7 --
> >   5 files changed, 193 insertions(+), 191 deletions(-)
> >
>
> Apart from the below nits:
>
> Reviewed-by: Gavin Shan <gshan@redhat.com>
>
> > diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
> > index 7e15b03fbdf8..0d5cca56cbda 100644
> > --- a/arch/arm64/kvm/guest.c
> > +++ b/arch/arm64/kvm/guest.c
> > @@ -18,7 +18,7 @@
> >   #include <linux/string.h>
> >   #include <linux/vmalloc.h>
> >   #include <linux/fs.h>
> > -#include <kvm/arm_psci.h>
> > +#include <kvm/arm_hypercalls.h>
> >   #include <asm/cputype.h>
> >   #include <linux/uaccess.h>
> >   #include <asm/fpsimd.h>
> > diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c
> > index 202b8c455724..fa6d9378d8e7 100644
> > --- a/arch/arm64/kvm/hypercalls.c
> > +++ b/arch/arm64/kvm/hypercalls.c
> > @@ -158,3 +158,188 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
> >       smccc_set_retval(vcpu, val[0], val[1], val[2], val[3]);
> >       return 1;
> >   }
> > +
> > +static const u64 kvm_arm_fw_reg_ids[] = {
> > +     KVM_REG_ARM_PSCI_VERSION,
> > +     KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1,
> > +     KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2,
> > +     KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3,
> > +};
> > +
> > +int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
> > +{
> > +     return ARRAY_SIZE(kvm_arm_fw_reg_ids);
> > +}
> > +
> > +int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
> > +{
> > +     int i;
> > +
> > +     for (i = 0; i < ARRAY_SIZE(kvm_arm_fw_reg_ids); i++) {
> > +             if (put_user(kvm_arm_fw_reg_ids[i], uindices++))
> > +                     return -EFAULT;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
>
> Since we're here, I think we can make this function to return 'ARRAY_SIZE(kvm_arm_fw_reg_ids)',
> to be consistent with copy_{core, sve}_reg_indices(). With the return value fixed, additional
> patch can use @ret in kvm_arm_copy_reg_indices().
>
Well we can, however, since this is mostly refactoring, I didn't want
to change the original functionality of the code.
The only caller of this is kvm_arm_copy_reg_indices()
(arch/arm64/kvm/guest.c), which only checks for 'ret < 0'.
Also, do you have a need for it? If yes, I can change it in the next revision.

> > +#define KVM_REG_FEATURE_LEVEL_WIDTH  4
> > +#define KVM_REG_FEATURE_LEVEL_MASK   GENMASK(KVM_REG_FEATURE_LEVEL_WIDTH, 0)
> > +
>
> It seems 'BIT()' is replaced with 'GENMASK' in the movement, but it's not mentioned
> in the commit log. I guess it'd better to mention it if you agree.
>
The last sentence of the commit text mentions this :)
Please let me know if it's not clear.

> > +/*
> > + * Convert the workaround level into an easy-to-compare number, where higher
> > + * values mean better protection.
> > + */
> > +static int get_kernel_wa_level(u64 regid)
> > +{
> > +     switch (regid) {
> > +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
> > +             switch (arm64_get_spectre_v2_state()) {
> > +             case SPECTRE_VULNERABLE:
> > +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
> > +             case SPECTRE_MITIGATED:
> > +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_AVAIL;
> > +             case SPECTRE_UNAFFECTED:
> > +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_REQUIRED;
> > +             }
> > +             return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
> > +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
> > +             switch (arm64_get_spectre_v4_state()) {
> > +             case SPECTRE_MITIGATED:
> > +                     /*
> > +                      * As for the hypercall discovery, we pretend we
> > +                      * don't have any FW mitigation if SSBS is there at
> > +                      * all times.
> > +                      */
> > +                     if (cpus_have_final_cap(ARM64_SSBS))
> > +                             return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
> > +                     fallthrough;
> > +             case SPECTRE_UNAFFECTED:
> > +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
> > +             case SPECTRE_VULNERABLE:
> > +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
> > +             }
> > +             break;
> > +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
> > +             switch (arm64_get_spectre_bhb_state()) {
> > +             case SPECTRE_VULNERABLE:
> > +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
> > +             case SPECTRE_MITIGATED:
> > +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_AVAIL;
> > +             case SPECTRE_UNAFFECTED:
> > +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_REQUIRED;
> > +             }
> > +             return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
> > +     }
> > +
> > +     return -EINVAL;
> > +}
> > +
> > +int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> > +{
> > +     void __user *uaddr = (void __user *)(long)reg->addr;
> > +     u64 val;
> > +
> > +     switch (reg->id) {
> > +     case KVM_REG_ARM_PSCI_VERSION:
> > +             val = kvm_psci_version(vcpu);
> > +             break;
> > +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
> > +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
> > +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
> > +             val = get_kernel_wa_level(reg->id) & KVM_REG_FEATURE_LEVEL_MASK;
> > +             break;
> > +     default:
> > +             return -ENOENT;
> > +     }
> > +
> > +     if (copy_to_user(uaddr, &val, KVM_REG_SIZE(reg->id)))
> > +             return -EFAULT;
> > +
> > +     return 0;
> > +}
> > +
> > +int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> > +{
> > +     void __user *uaddr = (void __user *)(long)reg->addr;
> > +     u64 val;
> > +     int wa_level;
> > +
> > +     if (copy_from_user(&val, uaddr, KVM_REG_SIZE(reg->id)))
> > +             return -EFAULT;
> > +
> > +     switch (reg->id) {
> > +     case KVM_REG_ARM_PSCI_VERSION:
> > +     {
> > +             bool wants_02;
> > +
> > +             wants_02 = test_bit(KVM_ARM_VCPU_PSCI_0_2, vcpu->arch.features);
> > +
> > +             switch (val) {
> > +             case KVM_ARM_PSCI_0_1:
> > +                     if (wants_02)
> > +                             return -EINVAL;
> > +                     vcpu->kvm->arch.psci_version = val;
> > +                     return 0;
> > +             case KVM_ARM_PSCI_0_2:
> > +             case KVM_ARM_PSCI_1_0:
> > +             case KVM_ARM_PSCI_1_1:
> > +                     if (!wants_02)
> > +                             return -EINVAL;
> > +                     vcpu->kvm->arch.psci_version = val;
> > +                     return 0;
> > +             }
> > +             break;
> > +     }
> > +
> > +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
> > +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
> > +             if (val & ~KVM_REG_FEATURE_LEVEL_MASK)
> > +                     return -EINVAL;
> > +
> > +             if (get_kernel_wa_level(reg->id) < val)
> > +                     return -EINVAL;
> > +
> > +             return 0;
> > +
> > +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
> > +             if (val & ~(KVM_REG_FEATURE_LEVEL_MASK |
> > +                         KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED))
> > +                     return -EINVAL;
> > +
> > +             /* The enabled bit must not be set unless the level is AVAIL. */
> > +             if ((val & KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED) &&
> > +                 (val & KVM_REG_FEATURE_LEVEL_MASK) != KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL)
> > +                     return -EINVAL;
> > +
> > +             /*
> > +              * Map all the possible incoming states to the only two we
> > +              * really want to deal with.
> > +              */
> > +             switch (val & KVM_REG_FEATURE_LEVEL_MASK) {
> > +             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL:
> > +             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_UNKNOWN:
> > +                     wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
> > +                     break;
> > +             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL:
> > +             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED:
> > +                     wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
> > +                     break;
> > +             default:
> > +                     return -EINVAL;
> > +             }
> > +
> > +             /*
> > +              * We can deal with NOT_AVAIL on NOT_REQUIRED, but not the
> > +              * other way around.
> > +              */
> > +             if (get_kernel_wa_level(reg->id) < wa_level)
> > +                     return -EINVAL;
> > +
> > +             return 0;
> > +     default:
> > +             return -ENOENT;
> > +     }
> > +
> > +     return -EINVAL;
> > +}
> > diff --git a/arch/arm64/kvm/psci.c b/arch/arm64/kvm/psci.c
> > index 372da09a2fab..bdfa93ca57d1 100644
> > --- a/arch/arm64/kvm/psci.c
> > +++ b/arch/arm64/kvm/psci.c
> > @@ -439,186 +439,3 @@ int kvm_psci_call(struct kvm_vcpu *vcpu)
> >               return -EINVAL;
> >       }
> >   }
> > -
> > -int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
> > -{
> > -     return 4;               /* PSCI version and three workaround registers */
> > -}
> > -
> > -int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
> > -{
> > -     if (put_user(KVM_REG_ARM_PSCI_VERSION, uindices++))
> > -             return -EFAULT;
> > -
> > -     if (put_user(KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1, uindices++))
> > -             return -EFAULT;
> > -
> > -     if (put_user(KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2, uindices++))
> > -             return -EFAULT;
> > -
> > -     if (put_user(KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3, uindices++))
> > -             return -EFAULT;
> > -
> > -     return 0;
> > -}
> > -
> > -#define KVM_REG_FEATURE_LEVEL_WIDTH  4
> > -#define KVM_REG_FEATURE_LEVEL_MASK   (BIT(KVM_REG_FEATURE_LEVEL_WIDTH) - 1)
> > -
> > -/*
> > - * Convert the workaround level into an easy-to-compare number, where higher
> > - * values mean better protection.
> > - */
> > -static int get_kernel_wa_level(u64 regid)
> > -{
> > -     switch (regid) {
> > -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
> > -             switch (arm64_get_spectre_v2_state()) {
> > -             case SPECTRE_VULNERABLE:
> > -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
> > -             case SPECTRE_MITIGATED:
> > -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_AVAIL;
> > -             case SPECTRE_UNAFFECTED:
> > -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_REQUIRED;
> > -             }
> > -             return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
> > -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
> > -             switch (arm64_get_spectre_v4_state()) {
> > -             case SPECTRE_MITIGATED:
> > -                     /*
> > -                      * As for the hypercall discovery, we pretend we
> > -                      * don't have any FW mitigation if SSBS is there at
> > -                      * all times.
> > -                      */
> > -                     if (cpus_have_final_cap(ARM64_SSBS))
> > -                             return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
> > -                     fallthrough;
> > -             case SPECTRE_UNAFFECTED:
> > -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
> > -             case SPECTRE_VULNERABLE:
> > -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
> > -             }
> > -             break;
> > -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
> > -             switch (arm64_get_spectre_bhb_state()) {
> > -             case SPECTRE_VULNERABLE:
> > -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
> > -             case SPECTRE_MITIGATED:
> > -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_AVAIL;
> > -             case SPECTRE_UNAFFECTED:
> > -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_REQUIRED;
> > -             }
> > -             return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
> > -     }
> > -
> > -     return -EINVAL;
> > -}
> > -
> > -int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> > -{
> > -     void __user *uaddr = (void __user *)(long)reg->addr;
> > -     u64 val;
> > -
> > -     switch (reg->id) {
> > -     case KVM_REG_ARM_PSCI_VERSION:
> > -             val = kvm_psci_version(vcpu);
> > -             break;
> > -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
> > -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
> > -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
> > -             val = get_kernel_wa_level(reg->id) & KVM_REG_FEATURE_LEVEL_MASK;
> > -             break;
> > -     default:
> > -             return -ENOENT;
> > -     }
> > -
> > -     if (copy_to_user(uaddr, &val, KVM_REG_SIZE(reg->id)))
> > -             return -EFAULT;
> > -
> > -     return 0;
> > -}
> > -
> > -int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> > -{
> > -     void __user *uaddr = (void __user *)(long)reg->addr;
> > -     u64 val;
> > -     int wa_level;
> > -
> > -     if (copy_from_user(&val, uaddr, KVM_REG_SIZE(reg->id)))
> > -             return -EFAULT;
> > -
> > -     switch (reg->id) {
> > -     case KVM_REG_ARM_PSCI_VERSION:
> > -     {
> > -             bool wants_02;
> > -
> > -             wants_02 = test_bit(KVM_ARM_VCPU_PSCI_0_2, vcpu->arch.features);
> > -
> > -             switch (val) {
> > -             case KVM_ARM_PSCI_0_1:
> > -                     if (wants_02)
> > -                             return -EINVAL;
> > -                     vcpu->kvm->arch.psci_version = val;
> > -                     return 0;
> > -             case KVM_ARM_PSCI_0_2:
> > -             case KVM_ARM_PSCI_1_0:
> > -             case KVM_ARM_PSCI_1_1:
> > -                     if (!wants_02)
> > -                             return -EINVAL;
> > -                     vcpu->kvm->arch.psci_version = val;
> > -                     return 0;
> > -             }
> > -             break;
> > -     }
> > -
> > -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
> > -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
> > -             if (val & ~KVM_REG_FEATURE_LEVEL_MASK)
> > -                     return -EINVAL;
> > -
> > -             if (get_kernel_wa_level(reg->id) < val)
> > -                     return -EINVAL;
> > -
> > -             return 0;
> > -
> > -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
> > -             if (val & ~(KVM_REG_FEATURE_LEVEL_MASK |
> > -                         KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED))
> > -                     return -EINVAL;
> > -
> > -             /* The enabled bit must not be set unless the level is AVAIL. */
> > -             if ((val & KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED) &&
> > -                 (val & KVM_REG_FEATURE_LEVEL_MASK) != KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL)
> > -                     return -EINVAL;
> > -
> > -             /*
> > -              * Map all the possible incoming states to the only two we
> > -              * really want to deal with.
> > -              */
> > -             switch (val & KVM_REG_FEATURE_LEVEL_MASK) {
> > -             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL:
> > -             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_UNKNOWN:
> > -                     wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
> > -                     break;
> > -             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL:
> > -             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED:
> > -                     wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
> > -                     break;
> > -             default:
> > -                     return -EINVAL;
> > -             }
> > -
> > -             /*
> > -              * We can deal with NOT_AVAIL on NOT_REQUIRED, but not the
> > -              * other way around.
> > -              */
> > -             if (get_kernel_wa_level(reg->id) < wa_level)
> > -                     return -EINVAL;
> > -
> > -             return 0;
> > -     default:
> > -             return -ENOENT;
> > -     }
> > -
> > -     return -EINVAL;
> > -}
> > diff --git a/include/kvm/arm_hypercalls.h b/include/kvm/arm_hypercalls.h
> > index 0e2509d27910..5d38628a8d04 100644
> > --- a/include/kvm/arm_hypercalls.h
> > +++ b/include/kvm/arm_hypercalls.h
> > @@ -40,4 +40,11 @@ static inline void smccc_set_retval(struct kvm_vcpu *vcpu,
> >       vcpu_set_reg(vcpu, 3, a3);
> >   }
> >
> > +struct kvm_one_reg;
> > +
> > +int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu);
> > +int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices);
> > +int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
> > +int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
> > +
> >   #endif
> > diff --git a/include/kvm/arm_psci.h b/include/kvm/arm_psci.h
> > index 68b96c3826c3..6e55b9283789 100644
> > --- a/include/kvm/arm_psci.h
> > +++ b/include/kvm/arm_psci.h
> > @@ -39,11 +39,4 @@ static inline int kvm_psci_version(struct kvm_vcpu *vcpu)
> >
> >   int kvm_psci_call(struct kvm_vcpu *vcpu);
> >
> > -struct kvm_one_reg;
> > -
> > -int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu);
> > -int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices);
> > -int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
> > -int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
> > -
> >   #endif /* __KVM_ARM_PSCI_H__ */
> >
>
> Thanks,
> Gavin
Thank you for the review, Gavin.

Regards,
Raghavendra
>

_______________________________________________
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] 96+ messages in thread

* Re: [PATCH v5 01/10] KVM: arm64: Factor out firmware register handling from psci.c
  2022-04-12 16:41       ` Raghavendra Rao Ananta
  (?)
@ 2022-04-13  3:10         ` Gavin Shan
  -1 siblings, 0 replies; 96+ messages in thread
From: Gavin Shan @ 2022-04-13  3:10 UTC (permalink / raw)
  To: Raghavendra Rao Ananta
  Cc: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose, kvm, Catalin Marinas, Peter Shier,
	linux-kernel, Paolo Bonzini, Will Deacon, kvmarm,
	linux-arm-kernel

Hi Raghavendra,

On 4/13/22 12:41 AM, Raghavendra Rao Ananta wrote:
> On Tue, Apr 12, 2022 at 12:07 AM Gavin Shan <gshan@redhat.com> wrote:
>> On 4/7/22 9:15 AM, Raghavendra Rao Ananta wrote:
>>> Common hypercall firmware register handing is currently employed
>>> by psci.c. Since the upcoming patches add more of these registers,
>>> it's better to move the generic handling to hypercall.c for a
>>> cleaner presentation.
>>>
>>> While we are at it, collect all the firmware registers under
>>> fw_reg_ids[] to help implement kvm_arm_get_fw_num_regs() and
>>> kvm_arm_copy_fw_reg_indices() in a generic way. Also, define
>>> KVM_REG_FEATURE_LEVEL_MASK using a GENMASK instead.
>>>
>>> No functional change intended.
>>>
>>> Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
>>> Reviewed-by: Oliver Upton <oupton@google.com>
>>> ---
>>>    arch/arm64/kvm/guest.c       |   2 +-
>>>    arch/arm64/kvm/hypercalls.c  | 185 +++++++++++++++++++++++++++++++++++
>>>    arch/arm64/kvm/psci.c        | 183 ----------------------------------
>>>    include/kvm/arm_hypercalls.h |   7 ++
>>>    include/kvm/arm_psci.h       |   7 --
>>>    5 files changed, 193 insertions(+), 191 deletions(-)
>>>
>>
>> Apart from the below nits:
>>
>> Reviewed-by: Gavin Shan <gshan@redhat.com>
>>
>>> diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
>>> index 7e15b03fbdf8..0d5cca56cbda 100644
>>> --- a/arch/arm64/kvm/guest.c
>>> +++ b/arch/arm64/kvm/guest.c
>>> @@ -18,7 +18,7 @@
>>>    #include <linux/string.h>
>>>    #include <linux/vmalloc.h>
>>>    #include <linux/fs.h>
>>> -#include <kvm/arm_psci.h>
>>> +#include <kvm/arm_hypercalls.h>
>>>    #include <asm/cputype.h>
>>>    #include <linux/uaccess.h>
>>>    #include <asm/fpsimd.h>
>>> diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c
>>> index 202b8c455724..fa6d9378d8e7 100644
>>> --- a/arch/arm64/kvm/hypercalls.c
>>> +++ b/arch/arm64/kvm/hypercalls.c
>>> @@ -158,3 +158,188 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
>>>        smccc_set_retval(vcpu, val[0], val[1], val[2], val[3]);
>>>        return 1;
>>>    }
>>> +
>>> +static const u64 kvm_arm_fw_reg_ids[] = {
>>> +     KVM_REG_ARM_PSCI_VERSION,
>>> +     KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1,
>>> +     KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2,
>>> +     KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3,
>>> +};
>>> +
>>> +int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
>>> +{
>>> +     return ARRAY_SIZE(kvm_arm_fw_reg_ids);
>>> +}
>>> +
>>> +int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
>>> +{
>>> +     int i;
>>> +
>>> +     for (i = 0; i < ARRAY_SIZE(kvm_arm_fw_reg_ids); i++) {
>>> +             if (put_user(kvm_arm_fw_reg_ids[i], uindices++))
>>> +                     return -EFAULT;
>>> +     }
>>> +
>>> +     return 0;
>>> +}
>>> +
>>
>> Since we're here, I think we can make this function to return 'ARRAY_SIZE(kvm_arm_fw_reg_ids)',
>> to be consistent with copy_{core, sve}_reg_indices(). With the return value fixed, additional
>> patch can use @ret in kvm_arm_copy_reg_indices().
>>
> Well we can, however, since this is mostly refactoring, I didn't want
> to change the original functionality of the code.
> The only caller of this is kvm_arm_copy_reg_indices()
> (arch/arm64/kvm/guest.c), which only checks for 'ret < 0'.
> Also, do you have a need for it? If yes, I can change it in the next revision.
> 

The current implementation isn't wrong. With the return value fixed,
The individual snippets in kvm_arm_copy_reg_indices() looks similar.
It's not a big deal. If you plan to fix the return value for
kvm_arm_copy_fw_reg_indices() and copy_timer_indices(), a separate
patch can make the changes before this patch [v5 01/10]. Or we
can ignore this and fix it after this series is merged :)

>>> +#define KVM_REG_FEATURE_LEVEL_WIDTH  4
>>> +#define KVM_REG_FEATURE_LEVEL_MASK   GENMASK(KVM_REG_FEATURE_LEVEL_WIDTH, 0)
>>> +
>>
>> It seems 'BIT()' is replaced with 'GENMASK' in the movement, but it's not mentioned
>> in the commit log. I guess it'd better to mention it if you agree.
>>
> The last sentence of the commit text mentions this :)
> Please let me know if it's not clear.
> 

Exactly and it's clear. Sorry that I missed it :)

>>> +/*
>>> + * Convert the workaround level into an easy-to-compare number, where higher
>>> + * values mean better protection.
>>> + */
>>> +static int get_kernel_wa_level(u64 regid)
>>> +{
>>> +     switch (regid) {
>>> +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
>>> +             switch (arm64_get_spectre_v2_state()) {
>>> +             case SPECTRE_VULNERABLE:
>>> +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
>>> +             case SPECTRE_MITIGATED:
>>> +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_AVAIL;
>>> +             case SPECTRE_UNAFFECTED:
>>> +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_REQUIRED;
>>> +             }
>>> +             return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
>>> +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
>>> +             switch (arm64_get_spectre_v4_state()) {
>>> +             case SPECTRE_MITIGATED:
>>> +                     /*
>>> +                      * As for the hypercall discovery, we pretend we
>>> +                      * don't have any FW mitigation if SSBS is there at
>>> +                      * all times.
>>> +                      */
>>> +                     if (cpus_have_final_cap(ARM64_SSBS))
>>> +                             return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
>>> +                     fallthrough;
>>> +             case SPECTRE_UNAFFECTED:
>>> +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
>>> +             case SPECTRE_VULNERABLE:
>>> +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
>>> +             }
>>> +             break;
>>> +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
>>> +             switch (arm64_get_spectre_bhb_state()) {
>>> +             case SPECTRE_VULNERABLE:
>>> +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
>>> +             case SPECTRE_MITIGATED:
>>> +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_AVAIL;
>>> +             case SPECTRE_UNAFFECTED:
>>> +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_REQUIRED;
>>> +             }
>>> +             return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
>>> +     }
>>> +
>>> +     return -EINVAL;
>>> +}
>>> +
>>> +int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>>> +{
>>> +     void __user *uaddr = (void __user *)(long)reg->addr;
>>> +     u64 val;
>>> +
>>> +     switch (reg->id) {
>>> +     case KVM_REG_ARM_PSCI_VERSION:
>>> +             val = kvm_psci_version(vcpu);
>>> +             break;
>>> +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
>>> +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
>>> +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
>>> +             val = get_kernel_wa_level(reg->id) & KVM_REG_FEATURE_LEVEL_MASK;
>>> +             break;
>>> +     default:
>>> +             return -ENOENT;
>>> +     }
>>> +
>>> +     if (copy_to_user(uaddr, &val, KVM_REG_SIZE(reg->id)))
>>> +             return -EFAULT;
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>>> +{
>>> +     void __user *uaddr = (void __user *)(long)reg->addr;
>>> +     u64 val;
>>> +     int wa_level;
>>> +
>>> +     if (copy_from_user(&val, uaddr, KVM_REG_SIZE(reg->id)))
>>> +             return -EFAULT;
>>> +
>>> +     switch (reg->id) {
>>> +     case KVM_REG_ARM_PSCI_VERSION:
>>> +     {
>>> +             bool wants_02;
>>> +
>>> +             wants_02 = test_bit(KVM_ARM_VCPU_PSCI_0_2, vcpu->arch.features);
>>> +
>>> +             switch (val) {
>>> +             case KVM_ARM_PSCI_0_1:
>>> +                     if (wants_02)
>>> +                             return -EINVAL;
>>> +                     vcpu->kvm->arch.psci_version = val;
>>> +                     return 0;
>>> +             case KVM_ARM_PSCI_0_2:
>>> +             case KVM_ARM_PSCI_1_0:
>>> +             case KVM_ARM_PSCI_1_1:
>>> +                     if (!wants_02)
>>> +                             return -EINVAL;
>>> +                     vcpu->kvm->arch.psci_version = val;
>>> +                     return 0;
>>> +             }
>>> +             break;
>>> +     }
>>> +
>>> +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
>>> +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
>>> +             if (val & ~KVM_REG_FEATURE_LEVEL_MASK)
>>> +                     return -EINVAL;
>>> +
>>> +             if (get_kernel_wa_level(reg->id) < val)
>>> +                     return -EINVAL;
>>> +
>>> +             return 0;
>>> +
>>> +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
>>> +             if (val & ~(KVM_REG_FEATURE_LEVEL_MASK |
>>> +                         KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED))
>>> +                     return -EINVAL;
>>> +
>>> +             /* The enabled bit must not be set unless the level is AVAIL. */
>>> +             if ((val & KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED) &&
>>> +                 (val & KVM_REG_FEATURE_LEVEL_MASK) != KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL)
>>> +                     return -EINVAL;
>>> +
>>> +             /*
>>> +              * Map all the possible incoming states to the only two we
>>> +              * really want to deal with.
>>> +              */
>>> +             switch (val & KVM_REG_FEATURE_LEVEL_MASK) {
>>> +             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL:
>>> +             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_UNKNOWN:
>>> +                     wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
>>> +                     break;
>>> +             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL:
>>> +             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED:
>>> +                     wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
>>> +                     break;
>>> +             default:
>>> +                     return -EINVAL;
>>> +             }
>>> +
>>> +             /*
>>> +              * We can deal with NOT_AVAIL on NOT_REQUIRED, but not the
>>> +              * other way around.
>>> +              */
>>> +             if (get_kernel_wa_level(reg->id) < wa_level)
>>> +                     return -EINVAL;
>>> +
>>> +             return 0;
>>> +     default:
>>> +             return -ENOENT;
>>> +     }
>>> +
>>> +     return -EINVAL;
>>> +}
>>> diff --git a/arch/arm64/kvm/psci.c b/arch/arm64/kvm/psci.c
>>> index 372da09a2fab..bdfa93ca57d1 100644
>>> --- a/arch/arm64/kvm/psci.c
>>> +++ b/arch/arm64/kvm/psci.c
>>> @@ -439,186 +439,3 @@ int kvm_psci_call(struct kvm_vcpu *vcpu)
>>>                return -EINVAL;
>>>        }
>>>    }
>>> -
>>> -int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
>>> -{
>>> -     return 4;               /* PSCI version and three workaround registers */
>>> -}
>>> -
>>> -int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
>>> -{
>>> -     if (put_user(KVM_REG_ARM_PSCI_VERSION, uindices++))
>>> -             return -EFAULT;
>>> -
>>> -     if (put_user(KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1, uindices++))
>>> -             return -EFAULT;
>>> -
>>> -     if (put_user(KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2, uindices++))
>>> -             return -EFAULT;
>>> -
>>> -     if (put_user(KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3, uindices++))
>>> -             return -EFAULT;
>>> -
>>> -     return 0;
>>> -}
>>> -
>>> -#define KVM_REG_FEATURE_LEVEL_WIDTH  4
>>> -#define KVM_REG_FEATURE_LEVEL_MASK   (BIT(KVM_REG_FEATURE_LEVEL_WIDTH) - 1)
>>> -
>>> -/*
>>> - * Convert the workaround level into an easy-to-compare number, where higher
>>> - * values mean better protection.
>>> - */
>>> -static int get_kernel_wa_level(u64 regid)
>>> -{
>>> -     switch (regid) {
>>> -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
>>> -             switch (arm64_get_spectre_v2_state()) {
>>> -             case SPECTRE_VULNERABLE:
>>> -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
>>> -             case SPECTRE_MITIGATED:
>>> -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_AVAIL;
>>> -             case SPECTRE_UNAFFECTED:
>>> -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_REQUIRED;
>>> -             }
>>> -             return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
>>> -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
>>> -             switch (arm64_get_spectre_v4_state()) {
>>> -             case SPECTRE_MITIGATED:
>>> -                     /*
>>> -                      * As for the hypercall discovery, we pretend we
>>> -                      * don't have any FW mitigation if SSBS is there at
>>> -                      * all times.
>>> -                      */
>>> -                     if (cpus_have_final_cap(ARM64_SSBS))
>>> -                             return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
>>> -                     fallthrough;
>>> -             case SPECTRE_UNAFFECTED:
>>> -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
>>> -             case SPECTRE_VULNERABLE:
>>> -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
>>> -             }
>>> -             break;
>>> -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
>>> -             switch (arm64_get_spectre_bhb_state()) {
>>> -             case SPECTRE_VULNERABLE:
>>> -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
>>> -             case SPECTRE_MITIGATED:
>>> -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_AVAIL;
>>> -             case SPECTRE_UNAFFECTED:
>>> -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_REQUIRED;
>>> -             }
>>> -             return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
>>> -     }
>>> -
>>> -     return -EINVAL;
>>> -}
>>> -
>>> -int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>>> -{
>>> -     void __user *uaddr = (void __user *)(long)reg->addr;
>>> -     u64 val;
>>> -
>>> -     switch (reg->id) {
>>> -     case KVM_REG_ARM_PSCI_VERSION:
>>> -             val = kvm_psci_version(vcpu);
>>> -             break;
>>> -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
>>> -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
>>> -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
>>> -             val = get_kernel_wa_level(reg->id) & KVM_REG_FEATURE_LEVEL_MASK;
>>> -             break;
>>> -     default:
>>> -             return -ENOENT;
>>> -     }
>>> -
>>> -     if (copy_to_user(uaddr, &val, KVM_REG_SIZE(reg->id)))
>>> -             return -EFAULT;
>>> -
>>> -     return 0;
>>> -}
>>> -
>>> -int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>>> -{
>>> -     void __user *uaddr = (void __user *)(long)reg->addr;
>>> -     u64 val;
>>> -     int wa_level;
>>> -
>>> -     if (copy_from_user(&val, uaddr, KVM_REG_SIZE(reg->id)))
>>> -             return -EFAULT;
>>> -
>>> -     switch (reg->id) {
>>> -     case KVM_REG_ARM_PSCI_VERSION:
>>> -     {
>>> -             bool wants_02;
>>> -
>>> -             wants_02 = test_bit(KVM_ARM_VCPU_PSCI_0_2, vcpu->arch.features);
>>> -
>>> -             switch (val) {
>>> -             case KVM_ARM_PSCI_0_1:
>>> -                     if (wants_02)
>>> -                             return -EINVAL;
>>> -                     vcpu->kvm->arch.psci_version = val;
>>> -                     return 0;
>>> -             case KVM_ARM_PSCI_0_2:
>>> -             case KVM_ARM_PSCI_1_0:
>>> -             case KVM_ARM_PSCI_1_1:
>>> -                     if (!wants_02)
>>> -                             return -EINVAL;
>>> -                     vcpu->kvm->arch.psci_version = val;
>>> -                     return 0;
>>> -             }
>>> -             break;
>>> -     }
>>> -
>>> -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
>>> -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
>>> -             if (val & ~KVM_REG_FEATURE_LEVEL_MASK)
>>> -                     return -EINVAL;
>>> -
>>> -             if (get_kernel_wa_level(reg->id) < val)
>>> -                     return -EINVAL;
>>> -
>>> -             return 0;
>>> -
>>> -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
>>> -             if (val & ~(KVM_REG_FEATURE_LEVEL_MASK |
>>> -                         KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED))
>>> -                     return -EINVAL;
>>> -
>>> -             /* The enabled bit must not be set unless the level is AVAIL. */
>>> -             if ((val & KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED) &&
>>> -                 (val & KVM_REG_FEATURE_LEVEL_MASK) != KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL)
>>> -                     return -EINVAL;
>>> -
>>> -             /*
>>> -              * Map all the possible incoming states to the only two we
>>> -              * really want to deal with.
>>> -              */
>>> -             switch (val & KVM_REG_FEATURE_LEVEL_MASK) {
>>> -             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL:
>>> -             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_UNKNOWN:
>>> -                     wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
>>> -                     break;
>>> -             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL:
>>> -             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED:
>>> -                     wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
>>> -                     break;
>>> -             default:
>>> -                     return -EINVAL;
>>> -             }
>>> -
>>> -             /*
>>> -              * We can deal with NOT_AVAIL on NOT_REQUIRED, but not the
>>> -              * other way around.
>>> -              */
>>> -             if (get_kernel_wa_level(reg->id) < wa_level)
>>> -                     return -EINVAL;
>>> -
>>> -             return 0;
>>> -     default:
>>> -             return -ENOENT;
>>> -     }
>>> -
>>> -     return -EINVAL;
>>> -}
>>> diff --git a/include/kvm/arm_hypercalls.h b/include/kvm/arm_hypercalls.h
>>> index 0e2509d27910..5d38628a8d04 100644
>>> --- a/include/kvm/arm_hypercalls.h
>>> +++ b/include/kvm/arm_hypercalls.h
>>> @@ -40,4 +40,11 @@ static inline void smccc_set_retval(struct kvm_vcpu *vcpu,
>>>        vcpu_set_reg(vcpu, 3, a3);
>>>    }
>>>
>>> +struct kvm_one_reg;
>>> +
>>> +int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu);
>>> +int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices);
>>> +int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
>>> +int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
>>> +
>>>    #endif
>>> diff --git a/include/kvm/arm_psci.h b/include/kvm/arm_psci.h
>>> index 68b96c3826c3..6e55b9283789 100644
>>> --- a/include/kvm/arm_psci.h
>>> +++ b/include/kvm/arm_psci.h
>>> @@ -39,11 +39,4 @@ static inline int kvm_psci_version(struct kvm_vcpu *vcpu)
>>>
>>>    int kvm_psci_call(struct kvm_vcpu *vcpu);
>>>
>>> -struct kvm_one_reg;
>>> -
>>> -int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu);
>>> -int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices);
>>> -int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
>>> -int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
>>> -
>>>    #endif /* __KVM_ARM_PSCI_H__ */
>>>
>>

[...]

> Thank you for the review, Gavin.
> 

No worries. The SDEI virtualization series depends on this to
some extent :)

Thanks,
Gavin



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

* Re: [PATCH v5 01/10] KVM: arm64: Factor out firmware register handling from psci.c
@ 2022-04-13  3:10         ` Gavin Shan
  0 siblings, 0 replies; 96+ messages in thread
From: Gavin Shan @ 2022-04-13  3:10 UTC (permalink / raw)
  To: Raghavendra Rao Ananta
  Cc: kvm, Marc Zyngier, Peter Shier, linux-kernel, Will Deacon,
	Catalin Marinas, Paolo Bonzini, kvmarm, linux-arm-kernel

Hi Raghavendra,

On 4/13/22 12:41 AM, Raghavendra Rao Ananta wrote:
> On Tue, Apr 12, 2022 at 12:07 AM Gavin Shan <gshan@redhat.com> wrote:
>> On 4/7/22 9:15 AM, Raghavendra Rao Ananta wrote:
>>> Common hypercall firmware register handing is currently employed
>>> by psci.c. Since the upcoming patches add more of these registers,
>>> it's better to move the generic handling to hypercall.c for a
>>> cleaner presentation.
>>>
>>> While we are at it, collect all the firmware registers under
>>> fw_reg_ids[] to help implement kvm_arm_get_fw_num_regs() and
>>> kvm_arm_copy_fw_reg_indices() in a generic way. Also, define
>>> KVM_REG_FEATURE_LEVEL_MASK using a GENMASK instead.
>>>
>>> No functional change intended.
>>>
>>> Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
>>> Reviewed-by: Oliver Upton <oupton@google.com>
>>> ---
>>>    arch/arm64/kvm/guest.c       |   2 +-
>>>    arch/arm64/kvm/hypercalls.c  | 185 +++++++++++++++++++++++++++++++++++
>>>    arch/arm64/kvm/psci.c        | 183 ----------------------------------
>>>    include/kvm/arm_hypercalls.h |   7 ++
>>>    include/kvm/arm_psci.h       |   7 --
>>>    5 files changed, 193 insertions(+), 191 deletions(-)
>>>
>>
>> Apart from the below nits:
>>
>> Reviewed-by: Gavin Shan <gshan@redhat.com>
>>
>>> diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
>>> index 7e15b03fbdf8..0d5cca56cbda 100644
>>> --- a/arch/arm64/kvm/guest.c
>>> +++ b/arch/arm64/kvm/guest.c
>>> @@ -18,7 +18,7 @@
>>>    #include <linux/string.h>
>>>    #include <linux/vmalloc.h>
>>>    #include <linux/fs.h>
>>> -#include <kvm/arm_psci.h>
>>> +#include <kvm/arm_hypercalls.h>
>>>    #include <asm/cputype.h>
>>>    #include <linux/uaccess.h>
>>>    #include <asm/fpsimd.h>
>>> diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c
>>> index 202b8c455724..fa6d9378d8e7 100644
>>> --- a/arch/arm64/kvm/hypercalls.c
>>> +++ b/arch/arm64/kvm/hypercalls.c
>>> @@ -158,3 +158,188 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
>>>        smccc_set_retval(vcpu, val[0], val[1], val[2], val[3]);
>>>        return 1;
>>>    }
>>> +
>>> +static const u64 kvm_arm_fw_reg_ids[] = {
>>> +     KVM_REG_ARM_PSCI_VERSION,
>>> +     KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1,
>>> +     KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2,
>>> +     KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3,
>>> +};
>>> +
>>> +int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
>>> +{
>>> +     return ARRAY_SIZE(kvm_arm_fw_reg_ids);
>>> +}
>>> +
>>> +int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
>>> +{
>>> +     int i;
>>> +
>>> +     for (i = 0; i < ARRAY_SIZE(kvm_arm_fw_reg_ids); i++) {
>>> +             if (put_user(kvm_arm_fw_reg_ids[i], uindices++))
>>> +                     return -EFAULT;
>>> +     }
>>> +
>>> +     return 0;
>>> +}
>>> +
>>
>> Since we're here, I think we can make this function to return 'ARRAY_SIZE(kvm_arm_fw_reg_ids)',
>> to be consistent with copy_{core, sve}_reg_indices(). With the return value fixed, additional
>> patch can use @ret in kvm_arm_copy_reg_indices().
>>
> Well we can, however, since this is mostly refactoring, I didn't want
> to change the original functionality of the code.
> The only caller of this is kvm_arm_copy_reg_indices()
> (arch/arm64/kvm/guest.c), which only checks for 'ret < 0'.
> Also, do you have a need for it? If yes, I can change it in the next revision.
> 

The current implementation isn't wrong. With the return value fixed,
The individual snippets in kvm_arm_copy_reg_indices() looks similar.
It's not a big deal. If you plan to fix the return value for
kvm_arm_copy_fw_reg_indices() and copy_timer_indices(), a separate
patch can make the changes before this patch [v5 01/10]. Or we
can ignore this and fix it after this series is merged :)

>>> +#define KVM_REG_FEATURE_LEVEL_WIDTH  4
>>> +#define KVM_REG_FEATURE_LEVEL_MASK   GENMASK(KVM_REG_FEATURE_LEVEL_WIDTH, 0)
>>> +
>>
>> It seems 'BIT()' is replaced with 'GENMASK' in the movement, but it's not mentioned
>> in the commit log. I guess it'd better to mention it if you agree.
>>
> The last sentence of the commit text mentions this :)
> Please let me know if it's not clear.
> 

Exactly and it's clear. Sorry that I missed it :)

>>> +/*
>>> + * Convert the workaround level into an easy-to-compare number, where higher
>>> + * values mean better protection.
>>> + */
>>> +static int get_kernel_wa_level(u64 regid)
>>> +{
>>> +     switch (regid) {
>>> +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
>>> +             switch (arm64_get_spectre_v2_state()) {
>>> +             case SPECTRE_VULNERABLE:
>>> +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
>>> +             case SPECTRE_MITIGATED:
>>> +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_AVAIL;
>>> +             case SPECTRE_UNAFFECTED:
>>> +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_REQUIRED;
>>> +             }
>>> +             return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
>>> +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
>>> +             switch (arm64_get_spectre_v4_state()) {
>>> +             case SPECTRE_MITIGATED:
>>> +                     /*
>>> +                      * As for the hypercall discovery, we pretend we
>>> +                      * don't have any FW mitigation if SSBS is there at
>>> +                      * all times.
>>> +                      */
>>> +                     if (cpus_have_final_cap(ARM64_SSBS))
>>> +                             return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
>>> +                     fallthrough;
>>> +             case SPECTRE_UNAFFECTED:
>>> +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
>>> +             case SPECTRE_VULNERABLE:
>>> +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
>>> +             }
>>> +             break;
>>> +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
>>> +             switch (arm64_get_spectre_bhb_state()) {
>>> +             case SPECTRE_VULNERABLE:
>>> +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
>>> +             case SPECTRE_MITIGATED:
>>> +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_AVAIL;
>>> +             case SPECTRE_UNAFFECTED:
>>> +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_REQUIRED;
>>> +             }
>>> +             return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
>>> +     }
>>> +
>>> +     return -EINVAL;
>>> +}
>>> +
>>> +int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>>> +{
>>> +     void __user *uaddr = (void __user *)(long)reg->addr;
>>> +     u64 val;
>>> +
>>> +     switch (reg->id) {
>>> +     case KVM_REG_ARM_PSCI_VERSION:
>>> +             val = kvm_psci_version(vcpu);
>>> +             break;
>>> +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
>>> +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
>>> +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
>>> +             val = get_kernel_wa_level(reg->id) & KVM_REG_FEATURE_LEVEL_MASK;
>>> +             break;
>>> +     default:
>>> +             return -ENOENT;
>>> +     }
>>> +
>>> +     if (copy_to_user(uaddr, &val, KVM_REG_SIZE(reg->id)))
>>> +             return -EFAULT;
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>>> +{
>>> +     void __user *uaddr = (void __user *)(long)reg->addr;
>>> +     u64 val;
>>> +     int wa_level;
>>> +
>>> +     if (copy_from_user(&val, uaddr, KVM_REG_SIZE(reg->id)))
>>> +             return -EFAULT;
>>> +
>>> +     switch (reg->id) {
>>> +     case KVM_REG_ARM_PSCI_VERSION:
>>> +     {
>>> +             bool wants_02;
>>> +
>>> +             wants_02 = test_bit(KVM_ARM_VCPU_PSCI_0_2, vcpu->arch.features);
>>> +
>>> +             switch (val) {
>>> +             case KVM_ARM_PSCI_0_1:
>>> +                     if (wants_02)
>>> +                             return -EINVAL;
>>> +                     vcpu->kvm->arch.psci_version = val;
>>> +                     return 0;
>>> +             case KVM_ARM_PSCI_0_2:
>>> +             case KVM_ARM_PSCI_1_0:
>>> +             case KVM_ARM_PSCI_1_1:
>>> +                     if (!wants_02)
>>> +                             return -EINVAL;
>>> +                     vcpu->kvm->arch.psci_version = val;
>>> +                     return 0;
>>> +             }
>>> +             break;
>>> +     }
>>> +
>>> +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
>>> +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
>>> +             if (val & ~KVM_REG_FEATURE_LEVEL_MASK)
>>> +                     return -EINVAL;
>>> +
>>> +             if (get_kernel_wa_level(reg->id) < val)
>>> +                     return -EINVAL;
>>> +
>>> +             return 0;
>>> +
>>> +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
>>> +             if (val & ~(KVM_REG_FEATURE_LEVEL_MASK |
>>> +                         KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED))
>>> +                     return -EINVAL;
>>> +
>>> +             /* The enabled bit must not be set unless the level is AVAIL. */
>>> +             if ((val & KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED) &&
>>> +                 (val & KVM_REG_FEATURE_LEVEL_MASK) != KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL)
>>> +                     return -EINVAL;
>>> +
>>> +             /*
>>> +              * Map all the possible incoming states to the only two we
>>> +              * really want to deal with.
>>> +              */
>>> +             switch (val & KVM_REG_FEATURE_LEVEL_MASK) {
>>> +             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL:
>>> +             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_UNKNOWN:
>>> +                     wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
>>> +                     break;
>>> +             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL:
>>> +             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED:
>>> +                     wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
>>> +                     break;
>>> +             default:
>>> +                     return -EINVAL;
>>> +             }
>>> +
>>> +             /*
>>> +              * We can deal with NOT_AVAIL on NOT_REQUIRED, but not the
>>> +              * other way around.
>>> +              */
>>> +             if (get_kernel_wa_level(reg->id) < wa_level)
>>> +                     return -EINVAL;
>>> +
>>> +             return 0;
>>> +     default:
>>> +             return -ENOENT;
>>> +     }
>>> +
>>> +     return -EINVAL;
>>> +}
>>> diff --git a/arch/arm64/kvm/psci.c b/arch/arm64/kvm/psci.c
>>> index 372da09a2fab..bdfa93ca57d1 100644
>>> --- a/arch/arm64/kvm/psci.c
>>> +++ b/arch/arm64/kvm/psci.c
>>> @@ -439,186 +439,3 @@ int kvm_psci_call(struct kvm_vcpu *vcpu)
>>>                return -EINVAL;
>>>        }
>>>    }
>>> -
>>> -int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
>>> -{
>>> -     return 4;               /* PSCI version and three workaround registers */
>>> -}
>>> -
>>> -int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
>>> -{
>>> -     if (put_user(KVM_REG_ARM_PSCI_VERSION, uindices++))
>>> -             return -EFAULT;
>>> -
>>> -     if (put_user(KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1, uindices++))
>>> -             return -EFAULT;
>>> -
>>> -     if (put_user(KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2, uindices++))
>>> -             return -EFAULT;
>>> -
>>> -     if (put_user(KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3, uindices++))
>>> -             return -EFAULT;
>>> -
>>> -     return 0;
>>> -}
>>> -
>>> -#define KVM_REG_FEATURE_LEVEL_WIDTH  4
>>> -#define KVM_REG_FEATURE_LEVEL_MASK   (BIT(KVM_REG_FEATURE_LEVEL_WIDTH) - 1)
>>> -
>>> -/*
>>> - * Convert the workaround level into an easy-to-compare number, where higher
>>> - * values mean better protection.
>>> - */
>>> -static int get_kernel_wa_level(u64 regid)
>>> -{
>>> -     switch (regid) {
>>> -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
>>> -             switch (arm64_get_spectre_v2_state()) {
>>> -             case SPECTRE_VULNERABLE:
>>> -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
>>> -             case SPECTRE_MITIGATED:
>>> -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_AVAIL;
>>> -             case SPECTRE_UNAFFECTED:
>>> -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_REQUIRED;
>>> -             }
>>> -             return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
>>> -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
>>> -             switch (arm64_get_spectre_v4_state()) {
>>> -             case SPECTRE_MITIGATED:
>>> -                     /*
>>> -                      * As for the hypercall discovery, we pretend we
>>> -                      * don't have any FW mitigation if SSBS is there at
>>> -                      * all times.
>>> -                      */
>>> -                     if (cpus_have_final_cap(ARM64_SSBS))
>>> -                             return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
>>> -                     fallthrough;
>>> -             case SPECTRE_UNAFFECTED:
>>> -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
>>> -             case SPECTRE_VULNERABLE:
>>> -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
>>> -             }
>>> -             break;
>>> -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
>>> -             switch (arm64_get_spectre_bhb_state()) {
>>> -             case SPECTRE_VULNERABLE:
>>> -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
>>> -             case SPECTRE_MITIGATED:
>>> -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_AVAIL;
>>> -             case SPECTRE_UNAFFECTED:
>>> -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_REQUIRED;
>>> -             }
>>> -             return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
>>> -     }
>>> -
>>> -     return -EINVAL;
>>> -}
>>> -
>>> -int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>>> -{
>>> -     void __user *uaddr = (void __user *)(long)reg->addr;
>>> -     u64 val;
>>> -
>>> -     switch (reg->id) {
>>> -     case KVM_REG_ARM_PSCI_VERSION:
>>> -             val = kvm_psci_version(vcpu);
>>> -             break;
>>> -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
>>> -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
>>> -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
>>> -             val = get_kernel_wa_level(reg->id) & KVM_REG_FEATURE_LEVEL_MASK;
>>> -             break;
>>> -     default:
>>> -             return -ENOENT;
>>> -     }
>>> -
>>> -     if (copy_to_user(uaddr, &val, KVM_REG_SIZE(reg->id)))
>>> -             return -EFAULT;
>>> -
>>> -     return 0;
>>> -}
>>> -
>>> -int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>>> -{
>>> -     void __user *uaddr = (void __user *)(long)reg->addr;
>>> -     u64 val;
>>> -     int wa_level;
>>> -
>>> -     if (copy_from_user(&val, uaddr, KVM_REG_SIZE(reg->id)))
>>> -             return -EFAULT;
>>> -
>>> -     switch (reg->id) {
>>> -     case KVM_REG_ARM_PSCI_VERSION:
>>> -     {
>>> -             bool wants_02;
>>> -
>>> -             wants_02 = test_bit(KVM_ARM_VCPU_PSCI_0_2, vcpu->arch.features);
>>> -
>>> -             switch (val) {
>>> -             case KVM_ARM_PSCI_0_1:
>>> -                     if (wants_02)
>>> -                             return -EINVAL;
>>> -                     vcpu->kvm->arch.psci_version = val;
>>> -                     return 0;
>>> -             case KVM_ARM_PSCI_0_2:
>>> -             case KVM_ARM_PSCI_1_0:
>>> -             case KVM_ARM_PSCI_1_1:
>>> -                     if (!wants_02)
>>> -                             return -EINVAL;
>>> -                     vcpu->kvm->arch.psci_version = val;
>>> -                     return 0;
>>> -             }
>>> -             break;
>>> -     }
>>> -
>>> -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
>>> -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
>>> -             if (val & ~KVM_REG_FEATURE_LEVEL_MASK)
>>> -                     return -EINVAL;
>>> -
>>> -             if (get_kernel_wa_level(reg->id) < val)
>>> -                     return -EINVAL;
>>> -
>>> -             return 0;
>>> -
>>> -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
>>> -             if (val & ~(KVM_REG_FEATURE_LEVEL_MASK |
>>> -                         KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED))
>>> -                     return -EINVAL;
>>> -
>>> -             /* The enabled bit must not be set unless the level is AVAIL. */
>>> -             if ((val & KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED) &&
>>> -                 (val & KVM_REG_FEATURE_LEVEL_MASK) != KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL)
>>> -                     return -EINVAL;
>>> -
>>> -             /*
>>> -              * Map all the possible incoming states to the only two we
>>> -              * really want to deal with.
>>> -              */
>>> -             switch (val & KVM_REG_FEATURE_LEVEL_MASK) {
>>> -             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL:
>>> -             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_UNKNOWN:
>>> -                     wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
>>> -                     break;
>>> -             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL:
>>> -             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED:
>>> -                     wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
>>> -                     break;
>>> -             default:
>>> -                     return -EINVAL;
>>> -             }
>>> -
>>> -             /*
>>> -              * We can deal with NOT_AVAIL on NOT_REQUIRED, but not the
>>> -              * other way around.
>>> -              */
>>> -             if (get_kernel_wa_level(reg->id) < wa_level)
>>> -                     return -EINVAL;
>>> -
>>> -             return 0;
>>> -     default:
>>> -             return -ENOENT;
>>> -     }
>>> -
>>> -     return -EINVAL;
>>> -}
>>> diff --git a/include/kvm/arm_hypercalls.h b/include/kvm/arm_hypercalls.h
>>> index 0e2509d27910..5d38628a8d04 100644
>>> --- a/include/kvm/arm_hypercalls.h
>>> +++ b/include/kvm/arm_hypercalls.h
>>> @@ -40,4 +40,11 @@ static inline void smccc_set_retval(struct kvm_vcpu *vcpu,
>>>        vcpu_set_reg(vcpu, 3, a3);
>>>    }
>>>
>>> +struct kvm_one_reg;
>>> +
>>> +int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu);
>>> +int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices);
>>> +int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
>>> +int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
>>> +
>>>    #endif
>>> diff --git a/include/kvm/arm_psci.h b/include/kvm/arm_psci.h
>>> index 68b96c3826c3..6e55b9283789 100644
>>> --- a/include/kvm/arm_psci.h
>>> +++ b/include/kvm/arm_psci.h
>>> @@ -39,11 +39,4 @@ static inline int kvm_psci_version(struct kvm_vcpu *vcpu)
>>>
>>>    int kvm_psci_call(struct kvm_vcpu *vcpu);
>>>
>>> -struct kvm_one_reg;
>>> -
>>> -int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu);
>>> -int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices);
>>> -int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
>>> -int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
>>> -
>>>    #endif /* __KVM_ARM_PSCI_H__ */
>>>
>>

[...]

> Thank you for the review, Gavin.
> 

No worries. The SDEI virtualization series depends on this to
some extent :)

Thanks,
Gavin


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

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

* Re: [PATCH v5 01/10] KVM: arm64: Factor out firmware register handling from psci.c
@ 2022-04-13  3:10         ` Gavin Shan
  0 siblings, 0 replies; 96+ messages in thread
From: Gavin Shan @ 2022-04-13  3:10 UTC (permalink / raw)
  To: Raghavendra Rao Ananta
  Cc: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose, kvm, Catalin Marinas, Peter Shier,
	linux-kernel, Paolo Bonzini, Will Deacon, kvmarm,
	linux-arm-kernel

Hi Raghavendra,

On 4/13/22 12:41 AM, Raghavendra Rao Ananta wrote:
> On Tue, Apr 12, 2022 at 12:07 AM Gavin Shan <gshan@redhat.com> wrote:
>> On 4/7/22 9:15 AM, Raghavendra Rao Ananta wrote:
>>> Common hypercall firmware register handing is currently employed
>>> by psci.c. Since the upcoming patches add more of these registers,
>>> it's better to move the generic handling to hypercall.c for a
>>> cleaner presentation.
>>>
>>> While we are at it, collect all the firmware registers under
>>> fw_reg_ids[] to help implement kvm_arm_get_fw_num_regs() and
>>> kvm_arm_copy_fw_reg_indices() in a generic way. Also, define
>>> KVM_REG_FEATURE_LEVEL_MASK using a GENMASK instead.
>>>
>>> No functional change intended.
>>>
>>> Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
>>> Reviewed-by: Oliver Upton <oupton@google.com>
>>> ---
>>>    arch/arm64/kvm/guest.c       |   2 +-
>>>    arch/arm64/kvm/hypercalls.c  | 185 +++++++++++++++++++++++++++++++++++
>>>    arch/arm64/kvm/psci.c        | 183 ----------------------------------
>>>    include/kvm/arm_hypercalls.h |   7 ++
>>>    include/kvm/arm_psci.h       |   7 --
>>>    5 files changed, 193 insertions(+), 191 deletions(-)
>>>
>>
>> Apart from the below nits:
>>
>> Reviewed-by: Gavin Shan <gshan@redhat.com>
>>
>>> diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
>>> index 7e15b03fbdf8..0d5cca56cbda 100644
>>> --- a/arch/arm64/kvm/guest.c
>>> +++ b/arch/arm64/kvm/guest.c
>>> @@ -18,7 +18,7 @@
>>>    #include <linux/string.h>
>>>    #include <linux/vmalloc.h>
>>>    #include <linux/fs.h>
>>> -#include <kvm/arm_psci.h>
>>> +#include <kvm/arm_hypercalls.h>
>>>    #include <asm/cputype.h>
>>>    #include <linux/uaccess.h>
>>>    #include <asm/fpsimd.h>
>>> diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c
>>> index 202b8c455724..fa6d9378d8e7 100644
>>> --- a/arch/arm64/kvm/hypercalls.c
>>> +++ b/arch/arm64/kvm/hypercalls.c
>>> @@ -158,3 +158,188 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
>>>        smccc_set_retval(vcpu, val[0], val[1], val[2], val[3]);
>>>        return 1;
>>>    }
>>> +
>>> +static const u64 kvm_arm_fw_reg_ids[] = {
>>> +     KVM_REG_ARM_PSCI_VERSION,
>>> +     KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1,
>>> +     KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2,
>>> +     KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3,
>>> +};
>>> +
>>> +int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
>>> +{
>>> +     return ARRAY_SIZE(kvm_arm_fw_reg_ids);
>>> +}
>>> +
>>> +int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
>>> +{
>>> +     int i;
>>> +
>>> +     for (i = 0; i < ARRAY_SIZE(kvm_arm_fw_reg_ids); i++) {
>>> +             if (put_user(kvm_arm_fw_reg_ids[i], uindices++))
>>> +                     return -EFAULT;
>>> +     }
>>> +
>>> +     return 0;
>>> +}
>>> +
>>
>> Since we're here, I think we can make this function to return 'ARRAY_SIZE(kvm_arm_fw_reg_ids)',
>> to be consistent with copy_{core, sve}_reg_indices(). With the return value fixed, additional
>> patch can use @ret in kvm_arm_copy_reg_indices().
>>
> Well we can, however, since this is mostly refactoring, I didn't want
> to change the original functionality of the code.
> The only caller of this is kvm_arm_copy_reg_indices()
> (arch/arm64/kvm/guest.c), which only checks for 'ret < 0'.
> Also, do you have a need for it? If yes, I can change it in the next revision.
> 

The current implementation isn't wrong. With the return value fixed,
The individual snippets in kvm_arm_copy_reg_indices() looks similar.
It's not a big deal. If you plan to fix the return value for
kvm_arm_copy_fw_reg_indices() and copy_timer_indices(), a separate
patch can make the changes before this patch [v5 01/10]. Or we
can ignore this and fix it after this series is merged :)

>>> +#define KVM_REG_FEATURE_LEVEL_WIDTH  4
>>> +#define KVM_REG_FEATURE_LEVEL_MASK   GENMASK(KVM_REG_FEATURE_LEVEL_WIDTH, 0)
>>> +
>>
>> It seems 'BIT()' is replaced with 'GENMASK' in the movement, but it's not mentioned
>> in the commit log. I guess it'd better to mention it if you agree.
>>
> The last sentence of the commit text mentions this :)
> Please let me know if it's not clear.
> 

Exactly and it's clear. Sorry that I missed it :)

>>> +/*
>>> + * Convert the workaround level into an easy-to-compare number, where higher
>>> + * values mean better protection.
>>> + */
>>> +static int get_kernel_wa_level(u64 regid)
>>> +{
>>> +     switch (regid) {
>>> +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
>>> +             switch (arm64_get_spectre_v2_state()) {
>>> +             case SPECTRE_VULNERABLE:
>>> +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
>>> +             case SPECTRE_MITIGATED:
>>> +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_AVAIL;
>>> +             case SPECTRE_UNAFFECTED:
>>> +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_REQUIRED;
>>> +             }
>>> +             return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
>>> +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
>>> +             switch (arm64_get_spectre_v4_state()) {
>>> +             case SPECTRE_MITIGATED:
>>> +                     /*
>>> +                      * As for the hypercall discovery, we pretend we
>>> +                      * don't have any FW mitigation if SSBS is there at
>>> +                      * all times.
>>> +                      */
>>> +                     if (cpus_have_final_cap(ARM64_SSBS))
>>> +                             return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
>>> +                     fallthrough;
>>> +             case SPECTRE_UNAFFECTED:
>>> +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
>>> +             case SPECTRE_VULNERABLE:
>>> +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
>>> +             }
>>> +             break;
>>> +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
>>> +             switch (arm64_get_spectre_bhb_state()) {
>>> +             case SPECTRE_VULNERABLE:
>>> +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
>>> +             case SPECTRE_MITIGATED:
>>> +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_AVAIL;
>>> +             case SPECTRE_UNAFFECTED:
>>> +                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_REQUIRED;
>>> +             }
>>> +             return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
>>> +     }
>>> +
>>> +     return -EINVAL;
>>> +}
>>> +
>>> +int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>>> +{
>>> +     void __user *uaddr = (void __user *)(long)reg->addr;
>>> +     u64 val;
>>> +
>>> +     switch (reg->id) {
>>> +     case KVM_REG_ARM_PSCI_VERSION:
>>> +             val = kvm_psci_version(vcpu);
>>> +             break;
>>> +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
>>> +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
>>> +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
>>> +             val = get_kernel_wa_level(reg->id) & KVM_REG_FEATURE_LEVEL_MASK;
>>> +             break;
>>> +     default:
>>> +             return -ENOENT;
>>> +     }
>>> +
>>> +     if (copy_to_user(uaddr, &val, KVM_REG_SIZE(reg->id)))
>>> +             return -EFAULT;
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>>> +{
>>> +     void __user *uaddr = (void __user *)(long)reg->addr;
>>> +     u64 val;
>>> +     int wa_level;
>>> +
>>> +     if (copy_from_user(&val, uaddr, KVM_REG_SIZE(reg->id)))
>>> +             return -EFAULT;
>>> +
>>> +     switch (reg->id) {
>>> +     case KVM_REG_ARM_PSCI_VERSION:
>>> +     {
>>> +             bool wants_02;
>>> +
>>> +             wants_02 = test_bit(KVM_ARM_VCPU_PSCI_0_2, vcpu->arch.features);
>>> +
>>> +             switch (val) {
>>> +             case KVM_ARM_PSCI_0_1:
>>> +                     if (wants_02)
>>> +                             return -EINVAL;
>>> +                     vcpu->kvm->arch.psci_version = val;
>>> +                     return 0;
>>> +             case KVM_ARM_PSCI_0_2:
>>> +             case KVM_ARM_PSCI_1_0:
>>> +             case KVM_ARM_PSCI_1_1:
>>> +                     if (!wants_02)
>>> +                             return -EINVAL;
>>> +                     vcpu->kvm->arch.psci_version = val;
>>> +                     return 0;
>>> +             }
>>> +             break;
>>> +     }
>>> +
>>> +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
>>> +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
>>> +             if (val & ~KVM_REG_FEATURE_LEVEL_MASK)
>>> +                     return -EINVAL;
>>> +
>>> +             if (get_kernel_wa_level(reg->id) < val)
>>> +                     return -EINVAL;
>>> +
>>> +             return 0;
>>> +
>>> +     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
>>> +             if (val & ~(KVM_REG_FEATURE_LEVEL_MASK |
>>> +                         KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED))
>>> +                     return -EINVAL;
>>> +
>>> +             /* The enabled bit must not be set unless the level is AVAIL. */
>>> +             if ((val & KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED) &&
>>> +                 (val & KVM_REG_FEATURE_LEVEL_MASK) != KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL)
>>> +                     return -EINVAL;
>>> +
>>> +             /*
>>> +              * Map all the possible incoming states to the only two we
>>> +              * really want to deal with.
>>> +              */
>>> +             switch (val & KVM_REG_FEATURE_LEVEL_MASK) {
>>> +             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL:
>>> +             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_UNKNOWN:
>>> +                     wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
>>> +                     break;
>>> +             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL:
>>> +             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED:
>>> +                     wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
>>> +                     break;
>>> +             default:
>>> +                     return -EINVAL;
>>> +             }
>>> +
>>> +             /*
>>> +              * We can deal with NOT_AVAIL on NOT_REQUIRED, but not the
>>> +              * other way around.
>>> +              */
>>> +             if (get_kernel_wa_level(reg->id) < wa_level)
>>> +                     return -EINVAL;
>>> +
>>> +             return 0;
>>> +     default:
>>> +             return -ENOENT;
>>> +     }
>>> +
>>> +     return -EINVAL;
>>> +}
>>> diff --git a/arch/arm64/kvm/psci.c b/arch/arm64/kvm/psci.c
>>> index 372da09a2fab..bdfa93ca57d1 100644
>>> --- a/arch/arm64/kvm/psci.c
>>> +++ b/arch/arm64/kvm/psci.c
>>> @@ -439,186 +439,3 @@ int kvm_psci_call(struct kvm_vcpu *vcpu)
>>>                return -EINVAL;
>>>        }
>>>    }
>>> -
>>> -int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
>>> -{
>>> -     return 4;               /* PSCI version and three workaround registers */
>>> -}
>>> -
>>> -int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
>>> -{
>>> -     if (put_user(KVM_REG_ARM_PSCI_VERSION, uindices++))
>>> -             return -EFAULT;
>>> -
>>> -     if (put_user(KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1, uindices++))
>>> -             return -EFAULT;
>>> -
>>> -     if (put_user(KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2, uindices++))
>>> -             return -EFAULT;
>>> -
>>> -     if (put_user(KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3, uindices++))
>>> -             return -EFAULT;
>>> -
>>> -     return 0;
>>> -}
>>> -
>>> -#define KVM_REG_FEATURE_LEVEL_WIDTH  4
>>> -#define KVM_REG_FEATURE_LEVEL_MASK   (BIT(KVM_REG_FEATURE_LEVEL_WIDTH) - 1)
>>> -
>>> -/*
>>> - * Convert the workaround level into an easy-to-compare number, where higher
>>> - * values mean better protection.
>>> - */
>>> -static int get_kernel_wa_level(u64 regid)
>>> -{
>>> -     switch (regid) {
>>> -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
>>> -             switch (arm64_get_spectre_v2_state()) {
>>> -             case SPECTRE_VULNERABLE:
>>> -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
>>> -             case SPECTRE_MITIGATED:
>>> -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_AVAIL;
>>> -             case SPECTRE_UNAFFECTED:
>>> -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_REQUIRED;
>>> -             }
>>> -             return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
>>> -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
>>> -             switch (arm64_get_spectre_v4_state()) {
>>> -             case SPECTRE_MITIGATED:
>>> -                     /*
>>> -                      * As for the hypercall discovery, we pretend we
>>> -                      * don't have any FW mitigation if SSBS is there at
>>> -                      * all times.
>>> -                      */
>>> -                     if (cpus_have_final_cap(ARM64_SSBS))
>>> -                             return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
>>> -                     fallthrough;
>>> -             case SPECTRE_UNAFFECTED:
>>> -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
>>> -             case SPECTRE_VULNERABLE:
>>> -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
>>> -             }
>>> -             break;
>>> -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
>>> -             switch (arm64_get_spectre_bhb_state()) {
>>> -             case SPECTRE_VULNERABLE:
>>> -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
>>> -             case SPECTRE_MITIGATED:
>>> -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_AVAIL;
>>> -             case SPECTRE_UNAFFECTED:
>>> -                     return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_REQUIRED;
>>> -             }
>>> -             return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
>>> -     }
>>> -
>>> -     return -EINVAL;
>>> -}
>>> -
>>> -int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>>> -{
>>> -     void __user *uaddr = (void __user *)(long)reg->addr;
>>> -     u64 val;
>>> -
>>> -     switch (reg->id) {
>>> -     case KVM_REG_ARM_PSCI_VERSION:
>>> -             val = kvm_psci_version(vcpu);
>>> -             break;
>>> -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
>>> -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
>>> -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
>>> -             val = get_kernel_wa_level(reg->id) & KVM_REG_FEATURE_LEVEL_MASK;
>>> -             break;
>>> -     default:
>>> -             return -ENOENT;
>>> -     }
>>> -
>>> -     if (copy_to_user(uaddr, &val, KVM_REG_SIZE(reg->id)))
>>> -             return -EFAULT;
>>> -
>>> -     return 0;
>>> -}
>>> -
>>> -int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>>> -{
>>> -     void __user *uaddr = (void __user *)(long)reg->addr;
>>> -     u64 val;
>>> -     int wa_level;
>>> -
>>> -     if (copy_from_user(&val, uaddr, KVM_REG_SIZE(reg->id)))
>>> -             return -EFAULT;
>>> -
>>> -     switch (reg->id) {
>>> -     case KVM_REG_ARM_PSCI_VERSION:
>>> -     {
>>> -             bool wants_02;
>>> -
>>> -             wants_02 = test_bit(KVM_ARM_VCPU_PSCI_0_2, vcpu->arch.features);
>>> -
>>> -             switch (val) {
>>> -             case KVM_ARM_PSCI_0_1:
>>> -                     if (wants_02)
>>> -                             return -EINVAL;
>>> -                     vcpu->kvm->arch.psci_version = val;
>>> -                     return 0;
>>> -             case KVM_ARM_PSCI_0_2:
>>> -             case KVM_ARM_PSCI_1_0:
>>> -             case KVM_ARM_PSCI_1_1:
>>> -                     if (!wants_02)
>>> -                             return -EINVAL;
>>> -                     vcpu->kvm->arch.psci_version = val;
>>> -                     return 0;
>>> -             }
>>> -             break;
>>> -     }
>>> -
>>> -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
>>> -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
>>> -             if (val & ~KVM_REG_FEATURE_LEVEL_MASK)
>>> -                     return -EINVAL;
>>> -
>>> -             if (get_kernel_wa_level(reg->id) < val)
>>> -                     return -EINVAL;
>>> -
>>> -             return 0;
>>> -
>>> -     case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
>>> -             if (val & ~(KVM_REG_FEATURE_LEVEL_MASK |
>>> -                         KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED))
>>> -                     return -EINVAL;
>>> -
>>> -             /* The enabled bit must not be set unless the level is AVAIL. */
>>> -             if ((val & KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED) &&
>>> -                 (val & KVM_REG_FEATURE_LEVEL_MASK) != KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL)
>>> -                     return -EINVAL;
>>> -
>>> -             /*
>>> -              * Map all the possible incoming states to the only two we
>>> -              * really want to deal with.
>>> -              */
>>> -             switch (val & KVM_REG_FEATURE_LEVEL_MASK) {
>>> -             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL:
>>> -             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_UNKNOWN:
>>> -                     wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
>>> -                     break;
>>> -             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL:
>>> -             case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED:
>>> -                     wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
>>> -                     break;
>>> -             default:
>>> -                     return -EINVAL;
>>> -             }
>>> -
>>> -             /*
>>> -              * We can deal with NOT_AVAIL on NOT_REQUIRED, but not the
>>> -              * other way around.
>>> -              */
>>> -             if (get_kernel_wa_level(reg->id) < wa_level)
>>> -                     return -EINVAL;
>>> -
>>> -             return 0;
>>> -     default:
>>> -             return -ENOENT;
>>> -     }
>>> -
>>> -     return -EINVAL;
>>> -}
>>> diff --git a/include/kvm/arm_hypercalls.h b/include/kvm/arm_hypercalls.h
>>> index 0e2509d27910..5d38628a8d04 100644
>>> --- a/include/kvm/arm_hypercalls.h
>>> +++ b/include/kvm/arm_hypercalls.h
>>> @@ -40,4 +40,11 @@ static inline void smccc_set_retval(struct kvm_vcpu *vcpu,
>>>        vcpu_set_reg(vcpu, 3, a3);
>>>    }
>>>
>>> +struct kvm_one_reg;
>>> +
>>> +int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu);
>>> +int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices);
>>> +int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
>>> +int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
>>> +
>>>    #endif
>>> diff --git a/include/kvm/arm_psci.h b/include/kvm/arm_psci.h
>>> index 68b96c3826c3..6e55b9283789 100644
>>> --- a/include/kvm/arm_psci.h
>>> +++ b/include/kvm/arm_psci.h
>>> @@ -39,11 +39,4 @@ static inline int kvm_psci_version(struct kvm_vcpu *vcpu)
>>>
>>>    int kvm_psci_call(struct kvm_vcpu *vcpu);
>>>
>>> -struct kvm_one_reg;
>>> -
>>> -int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu);
>>> -int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices);
>>> -int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
>>> -int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
>>> -
>>>    #endif /* __KVM_ARM_PSCI_H__ */
>>>
>>

[...]

> Thank you for the review, Gavin.
> 

No worries. The SDEI virtualization series depends on this to
some extent :)

Thanks,
Gavin



_______________________________________________
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] 96+ messages in thread

* Re: [PATCH v5 04/10] KVM: arm64: Add vendor hypervisor firmware register
  2022-04-07  1:15   ` Raghavendra Rao Ananta
  (?)
@ 2022-04-13  3:59     ` Gavin Shan
  -1 siblings, 0 replies; 96+ messages in thread
From: Gavin Shan @ 2022-04-13  3:59 UTC (permalink / raw)
  To: Raghavendra Rao Ananta, Marc Zyngier, Andrew Jones, James Morse,
	Alexandru Elisei, Suzuki K Poulose
  Cc: kvm, Catalin Marinas, Peter Shier, linux-kernel, Paolo Bonzini,
	Will Deacon, kvmarm, linux-arm-kernel

Hi Raghavendra,

On 4/7/22 9:15 AM, Raghavendra Rao Ananta wrote:
> Introduce the firmware register to hold the vendor specific
> hypervisor service calls (owner value 6) as a bitmap. The
> bitmap represents the features that'll be enabled for the
> guest, as configured by the user-space. Currently, this
> includes support for KVM-vendor features, and Precision Time
> Protocol (PTP), represented by bit-0 and bit-1 respectively.
> 
> Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
> ---
>   arch/arm64/include/asm/kvm_host.h |  2 ++
>   arch/arm64/include/uapi/asm/kvm.h |  4 ++++
>   arch/arm64/kvm/hypercalls.c       | 21 +++++++++++++++++----
>   include/kvm/arm_hypercalls.h      |  4 ++++
>   4 files changed, 27 insertions(+), 4 deletions(-)
> 
> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index 20165242ebd9..b79161bad69a 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -106,10 +106,12 @@ struct kvm_arch_memory_slot {
>    *
>    * @std_bmap: Bitmap of standard secure service calls
>    * @std_hyp_bmap: Bitmap of standard hypervisor service calls
> + * @vendor_hyp_bmap: Bitmap of vendor specific hypervisor service calls
>    */
>   struct kvm_smccc_features {
>   	u64 std_bmap;
>   	u64 std_hyp_bmap;
> +	u64 vendor_hyp_bmap;
>   };
>   
>   struct kvm_arch {
> diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h
> index 67353bf4e69d..9a5ac0ed4113 100644
> --- a/arch/arm64/include/uapi/asm/kvm.h
> +++ b/arch/arm64/include/uapi/asm/kvm.h
> @@ -344,6 +344,10 @@ struct kvm_arm_copy_mte_tags {
>   #define KVM_REG_ARM_STD_HYP_BMAP		KVM_REG_ARM_FW_FEAT_BMAP_REG(1)
>   #define KVM_REG_ARM_STD_HYP_BIT_PV_TIME		BIT(0)
>   
> +#define KVM_REG_ARM_VENDOR_HYP_BMAP		KVM_REG_ARM_FW_FEAT_BMAP_REG(2)
> +#define KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT	BIT(0)
> +#define KVM_REG_ARM_VENDOR_HYP_BIT_PTP		BIT(1)
> +
>   /* Device Control API: ARM VGIC */
>   #define KVM_DEV_ARM_VGIC_GRP_ADDR	0
>   #define KVM_DEV_ARM_VGIC_GRP_DIST_REGS	1
> diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c
> index 64ae6c7e7145..80836c341fd3 100644
> --- a/arch/arm64/kvm/hypercalls.c
> +++ b/arch/arm64/kvm/hypercalls.c
> @@ -66,8 +66,6 @@ static const u32 hvc_func_default_allowed_list[] = {
>   	ARM_SMCCC_VERSION_FUNC_ID,
>   	ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
>   	ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID,
> -	ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID,
> -	ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID,
>   };
>   
>   static bool kvm_hvc_call_default_allowed(struct kvm_vcpu *vcpu, u32 func_id)
> @@ -102,6 +100,12 @@ static bool kvm_hvc_call_allowed(struct kvm_vcpu *vcpu, u32 func_id)
>   	case ARM_SMCCC_HV_PV_TIME_ST:
>   		return kvm_arm_fw_reg_feat_enabled(smccc_feat->std_hyp_bmap,
>   					KVM_REG_ARM_STD_HYP_BIT_PV_TIME);
> +	case ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID:
> +		return kvm_arm_fw_reg_feat_enabled(smccc_feat->vendor_hyp_bmap,
> +					KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT);
> +	case ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID:
> +		return kvm_arm_fw_reg_feat_enabled(smccc_feat->vendor_hyp_bmap,
> +					KVM_REG_ARM_VENDOR_HYP_BIT_PTP);
>   	default:
>   		return kvm_hvc_call_default_allowed(vcpu, func_id);
>   	}

I guess we may return SMCCC_RET_NOT_SUPPORTED for ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID
if KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT isn't set? Otherwise, we need explain it
in the commit log.

KVM_REG_ARM_VENDOR_HYP_BIT_{FUNC_FEAT, PTP} aren't parallel to each other.
I think PTP can't be on if KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT is off.

> @@ -194,8 +198,7 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
>   		val[3] = ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_3;
>   		break;
>   	case ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID:
> -		val[0] = BIT(ARM_SMCCC_KVM_FUNC_FEATURES);
> -		val[0] |= BIT(ARM_SMCCC_KVM_FUNC_PTP);
> +		val[0] = smccc_feat->vendor_hyp_bmap;
>   		break;
>   	case ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID:
>   		kvm_ptp_get_time(vcpu, val);
> @@ -222,6 +225,7 @@ static const u64 kvm_arm_fw_reg_ids[] = {
>   	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3,
>   	KVM_REG_ARM_STD_BMAP,
>   	KVM_REG_ARM_STD_HYP_BMAP,
> +	KVM_REG_ARM_VENDOR_HYP_BMAP,
>   };
>   
>   void kvm_arm_init_hypercalls(struct kvm *kvm)
> @@ -230,6 +234,7 @@ void kvm_arm_init_hypercalls(struct kvm *kvm)
>   
>   	smccc_feat->std_bmap = KVM_ARM_SMCCC_STD_FEATURES;
>   	smccc_feat->std_hyp_bmap = KVM_ARM_SMCCC_STD_HYP_FEATURES;
> +	smccc_feat->vendor_hyp_bmap = KVM_ARM_SMCCC_VENDOR_HYP_FEATURES;
>   }
>   
>   int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
> @@ -322,6 +327,9 @@ int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>   	case KVM_REG_ARM_STD_HYP_BMAP:
>   		val = READ_ONCE(smccc_feat->std_hyp_bmap);
>   		break;
> +	case KVM_REG_ARM_VENDOR_HYP_BMAP:
> +		val = READ_ONCE(smccc_feat->vendor_hyp_bmap);
> +		break;
>   	default:
>   		return -ENOENT;
>   	}
> @@ -348,6 +356,10 @@ static int kvm_arm_set_fw_reg_bmap(struct kvm_vcpu *vcpu, u64 reg_id, u64 val)
>   		fw_reg_bmap = &smccc_feat->std_hyp_bmap;
>   		fw_reg_features = KVM_ARM_SMCCC_STD_HYP_FEATURES;
>   		break;
> +	case KVM_REG_ARM_VENDOR_HYP_BMAP:
> +		fw_reg_bmap = &smccc_feat->vendor_hyp_bmap;
> +		fw_reg_features = KVM_ARM_SMCCC_VENDOR_HYP_FEATURES;
> +		break;
>   	default:
>   		return -ENOENT;
>   	}

If KVM_REG_ARM_VENDOR_HYP_BIT_{FUNC_FEAT, PTP} aren't parallel to each other,
special code is needed to gurantee PTP is cleared if VENDOR_HYP is disabled.

> @@ -453,6 +465,7 @@ int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>   		return 0;
>   	case KVM_REG_ARM_STD_BMAP:
>   	case KVM_REG_ARM_STD_HYP_BMAP:
> +	case KVM_REG_ARM_VENDOR_HYP_BMAP:
>   		return kvm_arm_set_fw_reg_bmap(vcpu, reg->id, val);
>   	default:
>   		return -ENOENT;
> diff --git a/include/kvm/arm_hypercalls.h b/include/kvm/arm_hypercalls.h
> index b0915d8c5b81..eaf4f6b318a8 100644
> --- a/include/kvm/arm_hypercalls.h
> +++ b/include/kvm/arm_hypercalls.h
> @@ -9,6 +9,7 @@
>   /* Last valid bits of the bitmapped firmware registers */
>   #define KVM_REG_ARM_STD_BMAP_BIT_MAX		0
>   #define KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX	0
> +#define KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX	1
>   
>   #define KVM_ARM_SMCCC_STD_FEATURES \
>   	GENMASK_ULL(KVM_REG_ARM_STD_BMAP_BIT_MAX, 0)
> @@ -16,6 +17,9 @@
>   #define KVM_ARM_SMCCC_STD_HYP_FEATURES \
>   	GENMASK_ULL(KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX, 0)
>   
> +#define KVM_ARM_SMCCC_VENDOR_HYP_FEATURES \
> +	GENMASK_ULL(KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX, 0)
> +
>   int kvm_hvc_call_handler(struct kvm_vcpu *vcpu);
>   
>   static inline u32 smccc_get_function(struct kvm_vcpu *vcpu)
> 

Thanks,
Gavin


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

* Re: [PATCH v5 04/10] KVM: arm64: Add vendor hypervisor firmware register
@ 2022-04-13  3:59     ` Gavin Shan
  0 siblings, 0 replies; 96+ messages in thread
From: Gavin Shan @ 2022-04-13  3:59 UTC (permalink / raw)
  To: Raghavendra Rao Ananta, Marc Zyngier, Andrew Jones, James Morse,
	Alexandru Elisei, Suzuki K Poulose
  Cc: kvm, Catalin Marinas, Peter Shier, linux-kernel, Paolo Bonzini,
	Will Deacon, kvmarm, linux-arm-kernel

Hi Raghavendra,

On 4/7/22 9:15 AM, Raghavendra Rao Ananta wrote:
> Introduce the firmware register to hold the vendor specific
> hypervisor service calls (owner value 6) as a bitmap. The
> bitmap represents the features that'll be enabled for the
> guest, as configured by the user-space. Currently, this
> includes support for KVM-vendor features, and Precision Time
> Protocol (PTP), represented by bit-0 and bit-1 respectively.
> 
> Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
> ---
>   arch/arm64/include/asm/kvm_host.h |  2 ++
>   arch/arm64/include/uapi/asm/kvm.h |  4 ++++
>   arch/arm64/kvm/hypercalls.c       | 21 +++++++++++++++++----
>   include/kvm/arm_hypercalls.h      |  4 ++++
>   4 files changed, 27 insertions(+), 4 deletions(-)
> 
> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index 20165242ebd9..b79161bad69a 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -106,10 +106,12 @@ struct kvm_arch_memory_slot {
>    *
>    * @std_bmap: Bitmap of standard secure service calls
>    * @std_hyp_bmap: Bitmap of standard hypervisor service calls
> + * @vendor_hyp_bmap: Bitmap of vendor specific hypervisor service calls
>    */
>   struct kvm_smccc_features {
>   	u64 std_bmap;
>   	u64 std_hyp_bmap;
> +	u64 vendor_hyp_bmap;
>   };
>   
>   struct kvm_arch {
> diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h
> index 67353bf4e69d..9a5ac0ed4113 100644
> --- a/arch/arm64/include/uapi/asm/kvm.h
> +++ b/arch/arm64/include/uapi/asm/kvm.h
> @@ -344,6 +344,10 @@ struct kvm_arm_copy_mte_tags {
>   #define KVM_REG_ARM_STD_HYP_BMAP		KVM_REG_ARM_FW_FEAT_BMAP_REG(1)
>   #define KVM_REG_ARM_STD_HYP_BIT_PV_TIME		BIT(0)
>   
> +#define KVM_REG_ARM_VENDOR_HYP_BMAP		KVM_REG_ARM_FW_FEAT_BMAP_REG(2)
> +#define KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT	BIT(0)
> +#define KVM_REG_ARM_VENDOR_HYP_BIT_PTP		BIT(1)
> +
>   /* Device Control API: ARM VGIC */
>   #define KVM_DEV_ARM_VGIC_GRP_ADDR	0
>   #define KVM_DEV_ARM_VGIC_GRP_DIST_REGS	1
> diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c
> index 64ae6c7e7145..80836c341fd3 100644
> --- a/arch/arm64/kvm/hypercalls.c
> +++ b/arch/arm64/kvm/hypercalls.c
> @@ -66,8 +66,6 @@ static const u32 hvc_func_default_allowed_list[] = {
>   	ARM_SMCCC_VERSION_FUNC_ID,
>   	ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
>   	ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID,
> -	ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID,
> -	ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID,
>   };
>   
>   static bool kvm_hvc_call_default_allowed(struct kvm_vcpu *vcpu, u32 func_id)
> @@ -102,6 +100,12 @@ static bool kvm_hvc_call_allowed(struct kvm_vcpu *vcpu, u32 func_id)
>   	case ARM_SMCCC_HV_PV_TIME_ST:
>   		return kvm_arm_fw_reg_feat_enabled(smccc_feat->std_hyp_bmap,
>   					KVM_REG_ARM_STD_HYP_BIT_PV_TIME);
> +	case ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID:
> +		return kvm_arm_fw_reg_feat_enabled(smccc_feat->vendor_hyp_bmap,
> +					KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT);
> +	case ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID:
> +		return kvm_arm_fw_reg_feat_enabled(smccc_feat->vendor_hyp_bmap,
> +					KVM_REG_ARM_VENDOR_HYP_BIT_PTP);
>   	default:
>   		return kvm_hvc_call_default_allowed(vcpu, func_id);
>   	}

I guess we may return SMCCC_RET_NOT_SUPPORTED for ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID
if KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT isn't set? Otherwise, we need explain it
in the commit log.

KVM_REG_ARM_VENDOR_HYP_BIT_{FUNC_FEAT, PTP} aren't parallel to each other.
I think PTP can't be on if KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT is off.

> @@ -194,8 +198,7 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
>   		val[3] = ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_3;
>   		break;
>   	case ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID:
> -		val[0] = BIT(ARM_SMCCC_KVM_FUNC_FEATURES);
> -		val[0] |= BIT(ARM_SMCCC_KVM_FUNC_PTP);
> +		val[0] = smccc_feat->vendor_hyp_bmap;
>   		break;
>   	case ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID:
>   		kvm_ptp_get_time(vcpu, val);
> @@ -222,6 +225,7 @@ static const u64 kvm_arm_fw_reg_ids[] = {
>   	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3,
>   	KVM_REG_ARM_STD_BMAP,
>   	KVM_REG_ARM_STD_HYP_BMAP,
> +	KVM_REG_ARM_VENDOR_HYP_BMAP,
>   };
>   
>   void kvm_arm_init_hypercalls(struct kvm *kvm)
> @@ -230,6 +234,7 @@ void kvm_arm_init_hypercalls(struct kvm *kvm)
>   
>   	smccc_feat->std_bmap = KVM_ARM_SMCCC_STD_FEATURES;
>   	smccc_feat->std_hyp_bmap = KVM_ARM_SMCCC_STD_HYP_FEATURES;
> +	smccc_feat->vendor_hyp_bmap = KVM_ARM_SMCCC_VENDOR_HYP_FEATURES;
>   }
>   
>   int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
> @@ -322,6 +327,9 @@ int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>   	case KVM_REG_ARM_STD_HYP_BMAP:
>   		val = READ_ONCE(smccc_feat->std_hyp_bmap);
>   		break;
> +	case KVM_REG_ARM_VENDOR_HYP_BMAP:
> +		val = READ_ONCE(smccc_feat->vendor_hyp_bmap);
> +		break;
>   	default:
>   		return -ENOENT;
>   	}
> @@ -348,6 +356,10 @@ static int kvm_arm_set_fw_reg_bmap(struct kvm_vcpu *vcpu, u64 reg_id, u64 val)
>   		fw_reg_bmap = &smccc_feat->std_hyp_bmap;
>   		fw_reg_features = KVM_ARM_SMCCC_STD_HYP_FEATURES;
>   		break;
> +	case KVM_REG_ARM_VENDOR_HYP_BMAP:
> +		fw_reg_bmap = &smccc_feat->vendor_hyp_bmap;
> +		fw_reg_features = KVM_ARM_SMCCC_VENDOR_HYP_FEATURES;
> +		break;
>   	default:
>   		return -ENOENT;
>   	}

If KVM_REG_ARM_VENDOR_HYP_BIT_{FUNC_FEAT, PTP} aren't parallel to each other,
special code is needed to gurantee PTP is cleared if VENDOR_HYP is disabled.

> @@ -453,6 +465,7 @@ int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>   		return 0;
>   	case KVM_REG_ARM_STD_BMAP:
>   	case KVM_REG_ARM_STD_HYP_BMAP:
> +	case KVM_REG_ARM_VENDOR_HYP_BMAP:
>   		return kvm_arm_set_fw_reg_bmap(vcpu, reg->id, val);
>   	default:
>   		return -ENOENT;
> diff --git a/include/kvm/arm_hypercalls.h b/include/kvm/arm_hypercalls.h
> index b0915d8c5b81..eaf4f6b318a8 100644
> --- a/include/kvm/arm_hypercalls.h
> +++ b/include/kvm/arm_hypercalls.h
> @@ -9,6 +9,7 @@
>   /* Last valid bits of the bitmapped firmware registers */
>   #define KVM_REG_ARM_STD_BMAP_BIT_MAX		0
>   #define KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX	0
> +#define KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX	1
>   
>   #define KVM_ARM_SMCCC_STD_FEATURES \
>   	GENMASK_ULL(KVM_REG_ARM_STD_BMAP_BIT_MAX, 0)
> @@ -16,6 +17,9 @@
>   #define KVM_ARM_SMCCC_STD_HYP_FEATURES \
>   	GENMASK_ULL(KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX, 0)
>   
> +#define KVM_ARM_SMCCC_VENDOR_HYP_FEATURES \
> +	GENMASK_ULL(KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX, 0)
> +
>   int kvm_hvc_call_handler(struct kvm_vcpu *vcpu);
>   
>   static inline u32 smccc_get_function(struct kvm_vcpu *vcpu)
> 

Thanks,
Gavin

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

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

* Re: [PATCH v5 04/10] KVM: arm64: Add vendor hypervisor firmware register
@ 2022-04-13  3:59     ` Gavin Shan
  0 siblings, 0 replies; 96+ messages in thread
From: Gavin Shan @ 2022-04-13  3:59 UTC (permalink / raw)
  To: Raghavendra Rao Ananta, Marc Zyngier, Andrew Jones, James Morse,
	Alexandru Elisei, Suzuki K Poulose
  Cc: kvm, Catalin Marinas, Peter Shier, linux-kernel, Paolo Bonzini,
	Will Deacon, kvmarm, linux-arm-kernel

Hi Raghavendra,

On 4/7/22 9:15 AM, Raghavendra Rao Ananta wrote:
> Introduce the firmware register to hold the vendor specific
> hypervisor service calls (owner value 6) as a bitmap. The
> bitmap represents the features that'll be enabled for the
> guest, as configured by the user-space. Currently, this
> includes support for KVM-vendor features, and Precision Time
> Protocol (PTP), represented by bit-0 and bit-1 respectively.
> 
> Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
> ---
>   arch/arm64/include/asm/kvm_host.h |  2 ++
>   arch/arm64/include/uapi/asm/kvm.h |  4 ++++
>   arch/arm64/kvm/hypercalls.c       | 21 +++++++++++++++++----
>   include/kvm/arm_hypercalls.h      |  4 ++++
>   4 files changed, 27 insertions(+), 4 deletions(-)
> 
> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index 20165242ebd9..b79161bad69a 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -106,10 +106,12 @@ struct kvm_arch_memory_slot {
>    *
>    * @std_bmap: Bitmap of standard secure service calls
>    * @std_hyp_bmap: Bitmap of standard hypervisor service calls
> + * @vendor_hyp_bmap: Bitmap of vendor specific hypervisor service calls
>    */
>   struct kvm_smccc_features {
>   	u64 std_bmap;
>   	u64 std_hyp_bmap;
> +	u64 vendor_hyp_bmap;
>   };
>   
>   struct kvm_arch {
> diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h
> index 67353bf4e69d..9a5ac0ed4113 100644
> --- a/arch/arm64/include/uapi/asm/kvm.h
> +++ b/arch/arm64/include/uapi/asm/kvm.h
> @@ -344,6 +344,10 @@ struct kvm_arm_copy_mte_tags {
>   #define KVM_REG_ARM_STD_HYP_BMAP		KVM_REG_ARM_FW_FEAT_BMAP_REG(1)
>   #define KVM_REG_ARM_STD_HYP_BIT_PV_TIME		BIT(0)
>   
> +#define KVM_REG_ARM_VENDOR_HYP_BMAP		KVM_REG_ARM_FW_FEAT_BMAP_REG(2)
> +#define KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT	BIT(0)
> +#define KVM_REG_ARM_VENDOR_HYP_BIT_PTP		BIT(1)
> +
>   /* Device Control API: ARM VGIC */
>   #define KVM_DEV_ARM_VGIC_GRP_ADDR	0
>   #define KVM_DEV_ARM_VGIC_GRP_DIST_REGS	1
> diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c
> index 64ae6c7e7145..80836c341fd3 100644
> --- a/arch/arm64/kvm/hypercalls.c
> +++ b/arch/arm64/kvm/hypercalls.c
> @@ -66,8 +66,6 @@ static const u32 hvc_func_default_allowed_list[] = {
>   	ARM_SMCCC_VERSION_FUNC_ID,
>   	ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
>   	ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID,
> -	ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID,
> -	ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID,
>   };
>   
>   static bool kvm_hvc_call_default_allowed(struct kvm_vcpu *vcpu, u32 func_id)
> @@ -102,6 +100,12 @@ static bool kvm_hvc_call_allowed(struct kvm_vcpu *vcpu, u32 func_id)
>   	case ARM_SMCCC_HV_PV_TIME_ST:
>   		return kvm_arm_fw_reg_feat_enabled(smccc_feat->std_hyp_bmap,
>   					KVM_REG_ARM_STD_HYP_BIT_PV_TIME);
> +	case ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID:
> +		return kvm_arm_fw_reg_feat_enabled(smccc_feat->vendor_hyp_bmap,
> +					KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT);
> +	case ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID:
> +		return kvm_arm_fw_reg_feat_enabled(smccc_feat->vendor_hyp_bmap,
> +					KVM_REG_ARM_VENDOR_HYP_BIT_PTP);
>   	default:
>   		return kvm_hvc_call_default_allowed(vcpu, func_id);
>   	}

I guess we may return SMCCC_RET_NOT_SUPPORTED for ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID
if KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT isn't set? Otherwise, we need explain it
in the commit log.

KVM_REG_ARM_VENDOR_HYP_BIT_{FUNC_FEAT, PTP} aren't parallel to each other.
I think PTP can't be on if KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT is off.

> @@ -194,8 +198,7 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
>   		val[3] = ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_3;
>   		break;
>   	case ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID:
> -		val[0] = BIT(ARM_SMCCC_KVM_FUNC_FEATURES);
> -		val[0] |= BIT(ARM_SMCCC_KVM_FUNC_PTP);
> +		val[0] = smccc_feat->vendor_hyp_bmap;
>   		break;
>   	case ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID:
>   		kvm_ptp_get_time(vcpu, val);
> @@ -222,6 +225,7 @@ static const u64 kvm_arm_fw_reg_ids[] = {
>   	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3,
>   	KVM_REG_ARM_STD_BMAP,
>   	KVM_REG_ARM_STD_HYP_BMAP,
> +	KVM_REG_ARM_VENDOR_HYP_BMAP,
>   };
>   
>   void kvm_arm_init_hypercalls(struct kvm *kvm)
> @@ -230,6 +234,7 @@ void kvm_arm_init_hypercalls(struct kvm *kvm)
>   
>   	smccc_feat->std_bmap = KVM_ARM_SMCCC_STD_FEATURES;
>   	smccc_feat->std_hyp_bmap = KVM_ARM_SMCCC_STD_HYP_FEATURES;
> +	smccc_feat->vendor_hyp_bmap = KVM_ARM_SMCCC_VENDOR_HYP_FEATURES;
>   }
>   
>   int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
> @@ -322,6 +327,9 @@ int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>   	case KVM_REG_ARM_STD_HYP_BMAP:
>   		val = READ_ONCE(smccc_feat->std_hyp_bmap);
>   		break;
> +	case KVM_REG_ARM_VENDOR_HYP_BMAP:
> +		val = READ_ONCE(smccc_feat->vendor_hyp_bmap);
> +		break;
>   	default:
>   		return -ENOENT;
>   	}
> @@ -348,6 +356,10 @@ static int kvm_arm_set_fw_reg_bmap(struct kvm_vcpu *vcpu, u64 reg_id, u64 val)
>   		fw_reg_bmap = &smccc_feat->std_hyp_bmap;
>   		fw_reg_features = KVM_ARM_SMCCC_STD_HYP_FEATURES;
>   		break;
> +	case KVM_REG_ARM_VENDOR_HYP_BMAP:
> +		fw_reg_bmap = &smccc_feat->vendor_hyp_bmap;
> +		fw_reg_features = KVM_ARM_SMCCC_VENDOR_HYP_FEATURES;
> +		break;
>   	default:
>   		return -ENOENT;
>   	}

If KVM_REG_ARM_VENDOR_HYP_BIT_{FUNC_FEAT, PTP} aren't parallel to each other,
special code is needed to gurantee PTP is cleared if VENDOR_HYP is disabled.

> @@ -453,6 +465,7 @@ int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>   		return 0;
>   	case KVM_REG_ARM_STD_BMAP:
>   	case KVM_REG_ARM_STD_HYP_BMAP:
> +	case KVM_REG_ARM_VENDOR_HYP_BMAP:
>   		return kvm_arm_set_fw_reg_bmap(vcpu, reg->id, val);
>   	default:
>   		return -ENOENT;
> diff --git a/include/kvm/arm_hypercalls.h b/include/kvm/arm_hypercalls.h
> index b0915d8c5b81..eaf4f6b318a8 100644
> --- a/include/kvm/arm_hypercalls.h
> +++ b/include/kvm/arm_hypercalls.h
> @@ -9,6 +9,7 @@
>   /* Last valid bits of the bitmapped firmware registers */
>   #define KVM_REG_ARM_STD_BMAP_BIT_MAX		0
>   #define KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX	0
> +#define KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX	1
>   
>   #define KVM_ARM_SMCCC_STD_FEATURES \
>   	GENMASK_ULL(KVM_REG_ARM_STD_BMAP_BIT_MAX, 0)
> @@ -16,6 +17,9 @@
>   #define KVM_ARM_SMCCC_STD_HYP_FEATURES \
>   	GENMASK_ULL(KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX, 0)
>   
> +#define KVM_ARM_SMCCC_VENDOR_HYP_FEATURES \
> +	GENMASK_ULL(KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX, 0)
> +
>   int kvm_hvc_call_handler(struct kvm_vcpu *vcpu);
>   
>   static inline u32 smccc_get_function(struct kvm_vcpu *vcpu)
> 

Thanks,
Gavin


_______________________________________________
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] 96+ messages in thread

* Re: [PATCH v5 06/10] Docs: KVM: Add doc for the bitmap firmware registers
  2022-04-07  1:16   ` Raghavendra Rao Ananta
  (?)
@ 2022-04-13  6:39     ` Gavin Shan
  -1 siblings, 0 replies; 96+ messages in thread
From: Gavin Shan @ 2022-04-13  6:39 UTC (permalink / raw)
  To: Raghavendra Rao Ananta, Marc Zyngier, Andrew Jones, James Morse,
	Alexandru Elisei, Suzuki K Poulose
  Cc: kvm, Catalin Marinas, Peter Shier, linux-kernel, Paolo Bonzini,
	Will Deacon, kvmarm, linux-arm-kernel

Hi Raghavendra,

On 4/7/22 9:16 AM, Raghavendra Rao Ananta wrote:
> Add the documentation for the bitmap firmware registers in
> hypercalls.rst and api.rst. This includes the details for
> KVM_REG_ARM_STD_BMAP, KVM_REG_ARM_STD_HYP_BMAP, and
> KVM_REG_ARM_VENDOR_HYP_BMAP registers.
> 
> Since the document is growing to carry other hypercall related
> information, make necessary adjustments to present the document
> in a generic sense, rather than being PSCI focused.
> 
> Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
> ---
>   Documentation/virt/kvm/api.rst            | 17 ++++
>   Documentation/virt/kvm/arm/hypercalls.rst | 95 ++++++++++++++++++-----
>   2 files changed, 94 insertions(+), 18 deletions(-)
> 
> diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
> index d13fa6600467..e0107b157965 100644
> --- a/Documentation/virt/kvm/api.rst
> +++ b/Documentation/virt/kvm/api.rst
> @@ -2542,6 +2542,23 @@ arm64 firmware pseudo-registers have the following bit pattern::
>   
>     0x6030 0000 0014 <regno:16>
>   
> +arm64 bitmap feature firmware pseudo-registers have the following bit pattern::
> +
> +  0x6030 0000 0016 <regno:16>
> +
> +The bitmap feature firmware registers exposes the hypercall services that are
> +available for userspace to configure. The set bits corresponds to the services
> +that are available for the guests to access. By default, KVM sets all the
> +supported bits during VM initialization. The userspace can discover the
> +available services via KVM_GET_ONE_REG, and write-back the bitmap corresponding
> +to the features that it wishes guests to see via KVM_SET_ONE_REG.
> +
> +Note: These registers are immutable once any of the vCPUs of the VM has run at
> +least once. A KVM_SET_ONE_REG in such a scenario will return a -EBUSY to userspace.
> +If there's no change in the value that's being written, 0 (success) is returned.
> +
> +(See Documentation/virt/kvm/arm/hypercalls.rst for more details.)
> +
>   arm64 SVE registers have the following bit patterns::
>   
>     0x6080 0000 0015 00 <n:5> <slice:5>   Zn bits[2048*slice + 2047 : 2048*slice]
> diff --git a/Documentation/virt/kvm/arm/hypercalls.rst b/Documentation/virt/kvm/arm/hypercalls.rst
> index d52c2e83b5b8..ccda9fc2d253 100644
> --- a/Documentation/virt/kvm/arm/hypercalls.rst
> +++ b/Documentation/virt/kvm/arm/hypercalls.rst
> @@ -1,32 +1,32 @@
>   .. SPDX-License-Identifier: GPL-2.0
>   
> -=========================================
> -Power State Coordination Interface (PSCI)
> -=========================================
> +=======================
> +ARM Hypercall Interface
> +=======================
>   
> -KVM implements the PSCI (Power State Coordination Interface)
> -specification in order to provide services such as CPU on/off, reset
> -and power-off to the guest.
> +KVM handles the hypercall services as requested by the guests. New hypercall
> +services are regularly made available by the ARM specification or by KVM (as
> +vendor services) if they make sense from a virtualization point of view.
>   
> -The PSCI specification is regularly updated to provide new features,
> -and KVM implements these updates if they make sense from a virtualization
> -point of view.
> -
> -This means that a guest booted on two different versions of KVM can
> -observe two different "firmware" revisions. This could cause issues if
> -a given guest is tied to a particular PSCI revision (unlikely), or if
> -a migration causes a different PSCI version to be exposed out of the
> -blue to an unsuspecting guest.
> +This means that a guest booted on two different versions of KVM can observe
> +two different "firmware" revisions. This could cause issues if a given guest
> +is tied to a particular version of a hypercall service, or if a migration
> +causes a different version to be exposed out of the blue to an unsuspecting
> +guest.
>   
>   In order to remedy this situation, KVM exposes a set of "firmware
>   pseudo-registers" that can be manipulated using the GET/SET_ONE_REG
>   interface. These registers can be saved/restored by userspace, and set
> -to a convenient value if required.
> +to a convenient value as required.
>   
> -The following register is defined:
> +The following registers are defined:
>   
>   * KVM_REG_ARM_PSCI_VERSION:
>   
> +  KVM implements the PSCI (Power State Coordination Interface)
> +  specification in order to provide services such as CPU on/off, reset
> +  and power-off to the guest.
> +
>     - Only valid if the vcpu has the KVM_ARM_VCPU_PSCI_0_2 feature set
>       (and thus has already been initialized)
>     - Returns the current PSCI version on GET_ONE_REG (defaulting to the
> @@ -74,4 +74,63 @@ The following register is defined:
>       KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED:
>         The workaround is always active on this vCPU or it is not needed.
>   
> -.. [1] https://developer.arm.com/-/media/developer/pdf/ARM_DEN_0070A_Firmware_interfaces_for_mitigating_CVE-2017-5715.pdf
> +
> +Bitmap Feature Firmware Registers
> +---------------------------------
> +
> +Contrary to the above registers, the following registers exposes the hypercall
> +services in the form of a feature-bitmap to the userspace. This bitmap is
> +translated to the services that are available to the guest. There is a register
> +defined per service call owner and can be accessed via GET/SET_ONE_REG interface.
> +
> +By default, these registers are set with the upper limit of the features that
> +are supported. This way userspace can discover all the electable hypercall services
> +via GET_ONE_REG. The user-space can write-back the desired bitmap back via
> +SET_ONE_REG. The features for the registers that are untouched, probably because
> +userspace isn't aware of them, will be exposed as is to the guest.
> +
> +Note that KVM would't allow the userspace to configure the registers anymore once
> +any of the vCPUs has run at least once. Instead, it will return a -EBUSY. However,
> +if there's no change in the incoming value, it simply returns a success.
> +

It would be better to replace "a success" with "zero", to be consistent
with "-EBUSY". The suggestion may be invalid if the code needs changes
based on Marc's suggestions.

> +The psuedo-firmware bitmap register are as follows:
> +
> +* KVM_REG_ARM_STD_BMAP:
> +    Controls the bitmap of the ARM Standard Secure Service Calls.
> +
> +  The following bits are accepted:
> +
> +    Bit-0: KVM_REG_ARM_STD_BIT_TRNG_V1_0:
> +      The bit represents the services offered under v1.0 of ARM True Random
> +      Number Generator (TRNG) specification, ARM DEN0098.
> +
> +* KVM_REG_ARM_STD_HYP_BMAP:
> +    Controls the bitmap of the ARM Standard Hypervisor Service Calls.
> +
> +  The following bits are accepted:
> +
> +    Bit-0: KVM_REG_ARM_STD_HYP_BIT_PV_TIME:
> +      The bit represents the Paravirtualized Time service as represented by
> +      ARM DEN0057A.
> +
> +* KVM_REG_ARM_VENDOR_HYP_BMAP:
> +    Controls the bitmap of the Vendor specific Hypervisor Service Calls.
> +
> +  The following bits are accepted:
> +
> +    Bit-0: KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT
> +      The bit represents the ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID
> +      function-id
> +
> +    Bit-1: KVM_REG_ARM_VENDOR_HYP_BIT_PTP:
> +      The bit represents the Precision Time Protocol KVM service.
> +
> +Errors:
> +
> +    =======  =============================================================
> +    -ENOENT   Unknown register accessed.
> +    -EBUSY    Attempt a 'write' to the register after the VM has started.
> +    -EINVAL   Invalid bitmap written to the register.
> +    =======  =============================================================
> +
> +.. [1] https://developer.arm.com/-/media/developer/pdf/ARM_DEN_0070A_Firmware_interfaces_for_mitigating_CVE-2017-5715.pdf
> \ No newline at end of file
> 

Thanks,
Gavin


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

* Re: [PATCH v5 06/10] Docs: KVM: Add doc for the bitmap firmware registers
@ 2022-04-13  6:39     ` Gavin Shan
  0 siblings, 0 replies; 96+ messages in thread
From: Gavin Shan @ 2022-04-13  6:39 UTC (permalink / raw)
  To: Raghavendra Rao Ananta, Marc Zyngier, Andrew Jones, James Morse,
	Alexandru Elisei, Suzuki K Poulose
  Cc: kvm, Catalin Marinas, Peter Shier, linux-kernel, Paolo Bonzini,
	Will Deacon, kvmarm, linux-arm-kernel

Hi Raghavendra,

On 4/7/22 9:16 AM, Raghavendra Rao Ananta wrote:
> Add the documentation for the bitmap firmware registers in
> hypercalls.rst and api.rst. This includes the details for
> KVM_REG_ARM_STD_BMAP, KVM_REG_ARM_STD_HYP_BMAP, and
> KVM_REG_ARM_VENDOR_HYP_BMAP registers.
> 
> Since the document is growing to carry other hypercall related
> information, make necessary adjustments to present the document
> in a generic sense, rather than being PSCI focused.
> 
> Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
> ---
>   Documentation/virt/kvm/api.rst            | 17 ++++
>   Documentation/virt/kvm/arm/hypercalls.rst | 95 ++++++++++++++++++-----
>   2 files changed, 94 insertions(+), 18 deletions(-)
> 
> diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
> index d13fa6600467..e0107b157965 100644
> --- a/Documentation/virt/kvm/api.rst
> +++ b/Documentation/virt/kvm/api.rst
> @@ -2542,6 +2542,23 @@ arm64 firmware pseudo-registers have the following bit pattern::
>   
>     0x6030 0000 0014 <regno:16>
>   
> +arm64 bitmap feature firmware pseudo-registers have the following bit pattern::
> +
> +  0x6030 0000 0016 <regno:16>
> +
> +The bitmap feature firmware registers exposes the hypercall services that are
> +available for userspace to configure. The set bits corresponds to the services
> +that are available for the guests to access. By default, KVM sets all the
> +supported bits during VM initialization. The userspace can discover the
> +available services via KVM_GET_ONE_REG, and write-back the bitmap corresponding
> +to the features that it wishes guests to see via KVM_SET_ONE_REG.
> +
> +Note: These registers are immutable once any of the vCPUs of the VM has run at
> +least once. A KVM_SET_ONE_REG in such a scenario will return a -EBUSY to userspace.
> +If there's no change in the value that's being written, 0 (success) is returned.
> +
> +(See Documentation/virt/kvm/arm/hypercalls.rst for more details.)
> +
>   arm64 SVE registers have the following bit patterns::
>   
>     0x6080 0000 0015 00 <n:5> <slice:5>   Zn bits[2048*slice + 2047 : 2048*slice]
> diff --git a/Documentation/virt/kvm/arm/hypercalls.rst b/Documentation/virt/kvm/arm/hypercalls.rst
> index d52c2e83b5b8..ccda9fc2d253 100644
> --- a/Documentation/virt/kvm/arm/hypercalls.rst
> +++ b/Documentation/virt/kvm/arm/hypercalls.rst
> @@ -1,32 +1,32 @@
>   .. SPDX-License-Identifier: GPL-2.0
>   
> -=========================================
> -Power State Coordination Interface (PSCI)
> -=========================================
> +=======================
> +ARM Hypercall Interface
> +=======================
>   
> -KVM implements the PSCI (Power State Coordination Interface)
> -specification in order to provide services such as CPU on/off, reset
> -and power-off to the guest.
> +KVM handles the hypercall services as requested by the guests. New hypercall
> +services are regularly made available by the ARM specification or by KVM (as
> +vendor services) if they make sense from a virtualization point of view.
>   
> -The PSCI specification is regularly updated to provide new features,
> -and KVM implements these updates if they make sense from a virtualization
> -point of view.
> -
> -This means that a guest booted on two different versions of KVM can
> -observe two different "firmware" revisions. This could cause issues if
> -a given guest is tied to a particular PSCI revision (unlikely), or if
> -a migration causes a different PSCI version to be exposed out of the
> -blue to an unsuspecting guest.
> +This means that a guest booted on two different versions of KVM can observe
> +two different "firmware" revisions. This could cause issues if a given guest
> +is tied to a particular version of a hypercall service, or if a migration
> +causes a different version to be exposed out of the blue to an unsuspecting
> +guest.
>   
>   In order to remedy this situation, KVM exposes a set of "firmware
>   pseudo-registers" that can be manipulated using the GET/SET_ONE_REG
>   interface. These registers can be saved/restored by userspace, and set
> -to a convenient value if required.
> +to a convenient value as required.
>   
> -The following register is defined:
> +The following registers are defined:
>   
>   * KVM_REG_ARM_PSCI_VERSION:
>   
> +  KVM implements the PSCI (Power State Coordination Interface)
> +  specification in order to provide services such as CPU on/off, reset
> +  and power-off to the guest.
> +
>     - Only valid if the vcpu has the KVM_ARM_VCPU_PSCI_0_2 feature set
>       (and thus has already been initialized)
>     - Returns the current PSCI version on GET_ONE_REG (defaulting to the
> @@ -74,4 +74,63 @@ The following register is defined:
>       KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED:
>         The workaround is always active on this vCPU or it is not needed.
>   
> -.. [1] https://developer.arm.com/-/media/developer/pdf/ARM_DEN_0070A_Firmware_interfaces_for_mitigating_CVE-2017-5715.pdf
> +
> +Bitmap Feature Firmware Registers
> +---------------------------------
> +
> +Contrary to the above registers, the following registers exposes the hypercall
> +services in the form of a feature-bitmap to the userspace. This bitmap is
> +translated to the services that are available to the guest. There is a register
> +defined per service call owner and can be accessed via GET/SET_ONE_REG interface.
> +
> +By default, these registers are set with the upper limit of the features that
> +are supported. This way userspace can discover all the electable hypercall services
> +via GET_ONE_REG. The user-space can write-back the desired bitmap back via
> +SET_ONE_REG. The features for the registers that are untouched, probably because
> +userspace isn't aware of them, will be exposed as is to the guest.
> +
> +Note that KVM would't allow the userspace to configure the registers anymore once
> +any of the vCPUs has run at least once. Instead, it will return a -EBUSY. However,
> +if there's no change in the incoming value, it simply returns a success.
> +

It would be better to replace "a success" with "zero", to be consistent
with "-EBUSY". The suggestion may be invalid if the code needs changes
based on Marc's suggestions.

> +The psuedo-firmware bitmap register are as follows:
> +
> +* KVM_REG_ARM_STD_BMAP:
> +    Controls the bitmap of the ARM Standard Secure Service Calls.
> +
> +  The following bits are accepted:
> +
> +    Bit-0: KVM_REG_ARM_STD_BIT_TRNG_V1_0:
> +      The bit represents the services offered under v1.0 of ARM True Random
> +      Number Generator (TRNG) specification, ARM DEN0098.
> +
> +* KVM_REG_ARM_STD_HYP_BMAP:
> +    Controls the bitmap of the ARM Standard Hypervisor Service Calls.
> +
> +  The following bits are accepted:
> +
> +    Bit-0: KVM_REG_ARM_STD_HYP_BIT_PV_TIME:
> +      The bit represents the Paravirtualized Time service as represented by
> +      ARM DEN0057A.
> +
> +* KVM_REG_ARM_VENDOR_HYP_BMAP:
> +    Controls the bitmap of the Vendor specific Hypervisor Service Calls.
> +
> +  The following bits are accepted:
> +
> +    Bit-0: KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT
> +      The bit represents the ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID
> +      function-id
> +
> +    Bit-1: KVM_REG_ARM_VENDOR_HYP_BIT_PTP:
> +      The bit represents the Precision Time Protocol KVM service.
> +
> +Errors:
> +
> +    =======  =============================================================
> +    -ENOENT   Unknown register accessed.
> +    -EBUSY    Attempt a 'write' to the register after the VM has started.
> +    -EINVAL   Invalid bitmap written to the register.
> +    =======  =============================================================
> +
> +.. [1] https://developer.arm.com/-/media/developer/pdf/ARM_DEN_0070A_Firmware_interfaces_for_mitigating_CVE-2017-5715.pdf
> \ No newline at end of file
> 

Thanks,
Gavin

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

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

* Re: [PATCH v5 06/10] Docs: KVM: Add doc for the bitmap firmware registers
@ 2022-04-13  6:39     ` Gavin Shan
  0 siblings, 0 replies; 96+ messages in thread
From: Gavin Shan @ 2022-04-13  6:39 UTC (permalink / raw)
  To: Raghavendra Rao Ananta, Marc Zyngier, Andrew Jones, James Morse,
	Alexandru Elisei, Suzuki K Poulose
  Cc: kvm, Catalin Marinas, Peter Shier, linux-kernel, Paolo Bonzini,
	Will Deacon, kvmarm, linux-arm-kernel

Hi Raghavendra,

On 4/7/22 9:16 AM, Raghavendra Rao Ananta wrote:
> Add the documentation for the bitmap firmware registers in
> hypercalls.rst and api.rst. This includes the details for
> KVM_REG_ARM_STD_BMAP, KVM_REG_ARM_STD_HYP_BMAP, and
> KVM_REG_ARM_VENDOR_HYP_BMAP registers.
> 
> Since the document is growing to carry other hypercall related
> information, make necessary adjustments to present the document
> in a generic sense, rather than being PSCI focused.
> 
> Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
> ---
>   Documentation/virt/kvm/api.rst            | 17 ++++
>   Documentation/virt/kvm/arm/hypercalls.rst | 95 ++++++++++++++++++-----
>   2 files changed, 94 insertions(+), 18 deletions(-)
> 
> diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
> index d13fa6600467..e0107b157965 100644
> --- a/Documentation/virt/kvm/api.rst
> +++ b/Documentation/virt/kvm/api.rst
> @@ -2542,6 +2542,23 @@ arm64 firmware pseudo-registers have the following bit pattern::
>   
>     0x6030 0000 0014 <regno:16>
>   
> +arm64 bitmap feature firmware pseudo-registers have the following bit pattern::
> +
> +  0x6030 0000 0016 <regno:16>
> +
> +The bitmap feature firmware registers exposes the hypercall services that are
> +available for userspace to configure. The set bits corresponds to the services
> +that are available for the guests to access. By default, KVM sets all the
> +supported bits during VM initialization. The userspace can discover the
> +available services via KVM_GET_ONE_REG, and write-back the bitmap corresponding
> +to the features that it wishes guests to see via KVM_SET_ONE_REG.
> +
> +Note: These registers are immutable once any of the vCPUs of the VM has run at
> +least once. A KVM_SET_ONE_REG in such a scenario will return a -EBUSY to userspace.
> +If there's no change in the value that's being written, 0 (success) is returned.
> +
> +(See Documentation/virt/kvm/arm/hypercalls.rst for more details.)
> +
>   arm64 SVE registers have the following bit patterns::
>   
>     0x6080 0000 0015 00 <n:5> <slice:5>   Zn bits[2048*slice + 2047 : 2048*slice]
> diff --git a/Documentation/virt/kvm/arm/hypercalls.rst b/Documentation/virt/kvm/arm/hypercalls.rst
> index d52c2e83b5b8..ccda9fc2d253 100644
> --- a/Documentation/virt/kvm/arm/hypercalls.rst
> +++ b/Documentation/virt/kvm/arm/hypercalls.rst
> @@ -1,32 +1,32 @@
>   .. SPDX-License-Identifier: GPL-2.0
>   
> -=========================================
> -Power State Coordination Interface (PSCI)
> -=========================================
> +=======================
> +ARM Hypercall Interface
> +=======================
>   
> -KVM implements the PSCI (Power State Coordination Interface)
> -specification in order to provide services such as CPU on/off, reset
> -and power-off to the guest.
> +KVM handles the hypercall services as requested by the guests. New hypercall
> +services are regularly made available by the ARM specification or by KVM (as
> +vendor services) if they make sense from a virtualization point of view.
>   
> -The PSCI specification is regularly updated to provide new features,
> -and KVM implements these updates if they make sense from a virtualization
> -point of view.
> -
> -This means that a guest booted on two different versions of KVM can
> -observe two different "firmware" revisions. This could cause issues if
> -a given guest is tied to a particular PSCI revision (unlikely), or if
> -a migration causes a different PSCI version to be exposed out of the
> -blue to an unsuspecting guest.
> +This means that a guest booted on two different versions of KVM can observe
> +two different "firmware" revisions. This could cause issues if a given guest
> +is tied to a particular version of a hypercall service, or if a migration
> +causes a different version to be exposed out of the blue to an unsuspecting
> +guest.
>   
>   In order to remedy this situation, KVM exposes a set of "firmware
>   pseudo-registers" that can be manipulated using the GET/SET_ONE_REG
>   interface. These registers can be saved/restored by userspace, and set
> -to a convenient value if required.
> +to a convenient value as required.
>   
> -The following register is defined:
> +The following registers are defined:
>   
>   * KVM_REG_ARM_PSCI_VERSION:
>   
> +  KVM implements the PSCI (Power State Coordination Interface)
> +  specification in order to provide services such as CPU on/off, reset
> +  and power-off to the guest.
> +
>     - Only valid if the vcpu has the KVM_ARM_VCPU_PSCI_0_2 feature set
>       (and thus has already been initialized)
>     - Returns the current PSCI version on GET_ONE_REG (defaulting to the
> @@ -74,4 +74,63 @@ The following register is defined:
>       KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED:
>         The workaround is always active on this vCPU or it is not needed.
>   
> -.. [1] https://developer.arm.com/-/media/developer/pdf/ARM_DEN_0070A_Firmware_interfaces_for_mitigating_CVE-2017-5715.pdf
> +
> +Bitmap Feature Firmware Registers
> +---------------------------------
> +
> +Contrary to the above registers, the following registers exposes the hypercall
> +services in the form of a feature-bitmap to the userspace. This bitmap is
> +translated to the services that are available to the guest. There is a register
> +defined per service call owner and can be accessed via GET/SET_ONE_REG interface.
> +
> +By default, these registers are set with the upper limit of the features that
> +are supported. This way userspace can discover all the electable hypercall services
> +via GET_ONE_REG. The user-space can write-back the desired bitmap back via
> +SET_ONE_REG. The features for the registers that are untouched, probably because
> +userspace isn't aware of them, will be exposed as is to the guest.
> +
> +Note that KVM would't allow the userspace to configure the registers anymore once
> +any of the vCPUs has run at least once. Instead, it will return a -EBUSY. However,
> +if there's no change in the incoming value, it simply returns a success.
> +

It would be better to replace "a success" with "zero", to be consistent
with "-EBUSY". The suggestion may be invalid if the code needs changes
based on Marc's suggestions.

> +The psuedo-firmware bitmap register are as follows:
> +
> +* KVM_REG_ARM_STD_BMAP:
> +    Controls the bitmap of the ARM Standard Secure Service Calls.
> +
> +  The following bits are accepted:
> +
> +    Bit-0: KVM_REG_ARM_STD_BIT_TRNG_V1_0:
> +      The bit represents the services offered under v1.0 of ARM True Random
> +      Number Generator (TRNG) specification, ARM DEN0098.
> +
> +* KVM_REG_ARM_STD_HYP_BMAP:
> +    Controls the bitmap of the ARM Standard Hypervisor Service Calls.
> +
> +  The following bits are accepted:
> +
> +    Bit-0: KVM_REG_ARM_STD_HYP_BIT_PV_TIME:
> +      The bit represents the Paravirtualized Time service as represented by
> +      ARM DEN0057A.
> +
> +* KVM_REG_ARM_VENDOR_HYP_BMAP:
> +    Controls the bitmap of the Vendor specific Hypervisor Service Calls.
> +
> +  The following bits are accepted:
> +
> +    Bit-0: KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT
> +      The bit represents the ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID
> +      function-id
> +
> +    Bit-1: KVM_REG_ARM_VENDOR_HYP_BIT_PTP:
> +      The bit represents the Precision Time Protocol KVM service.
> +
> +Errors:
> +
> +    =======  =============================================================
> +    -ENOENT   Unknown register accessed.
> +    -EBUSY    Attempt a 'write' to the register after the VM has started.
> +    -EINVAL   Invalid bitmap written to the register.
> +    =======  =============================================================
> +
> +.. [1] https://developer.arm.com/-/media/developer/pdf/ARM_DEN_0070A_Firmware_interfaces_for_mitigating_CVE-2017-5715.pdf
> \ No newline at end of file
> 

Thanks,
Gavin


_______________________________________________
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] 96+ messages in thread

* Re: [PATCH v5 08/10] selftests: KVM: aarch64: Introduce hypercall ABI test
  2022-04-07  1:16   ` Raghavendra Rao Ananta
  (?)
@ 2022-04-13  9:07     ` Gavin Shan
  -1 siblings, 0 replies; 96+ messages in thread
From: Gavin Shan @ 2022-04-13  9:07 UTC (permalink / raw)
  To: Raghavendra Rao Ananta, Marc Zyngier, Andrew Jones, James Morse,
	Alexandru Elisei, Suzuki K Poulose
  Cc: kvm, Catalin Marinas, Peter Shier, linux-kernel, Paolo Bonzini,
	Will Deacon, kvmarm, linux-arm-kernel

Hi Raghavendra,

On 4/7/22 9:16 AM, Raghavendra Rao Ananta wrote:
> Introduce a KVM selftest to check the hypercall interface
> for arm64 platforms. The test validates the user-space's
> IOCTL interface to read/write the psuedo-firmware registers
> as well as its effects on the guest upon certain configurations.
> 
> Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
> ---
>   tools/testing/selftests/kvm/.gitignore        |   1 +
>   tools/testing/selftests/kvm/Makefile          |   1 +
>   .../selftests/kvm/aarch64/hypercalls.c        | 344 ++++++++++++++++++
>   3 files changed, 346 insertions(+)
>   create mode 100644 tools/testing/selftests/kvm/aarch64/hypercalls.c
> 

To be more precise, s/IOCTL/{GET,SET}_ONE_REG ?

> diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore
> index e82b816a6608..7ef52b3b1560 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/hypercalls
>   /aarch64/psci_test
>   /aarch64/vgic_init
>   /aarch64/vgic_irq
> diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
> index 2f74f502de65..af4cb88bcf83 100644
> --- a/tools/testing/selftests/kvm/Makefile
> +++ b/tools/testing/selftests/kvm/Makefile
> @@ -105,6 +105,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/hypercalls
>   TEST_GEN_PROGS_aarch64 += aarch64/psci_test
>   TEST_GEN_PROGS_aarch64 += aarch64/vgic_init
>   TEST_GEN_PROGS_aarch64 += aarch64/vgic_irq
> diff --git a/tools/testing/selftests/kvm/aarch64/hypercalls.c b/tools/testing/selftests/kvm/aarch64/hypercalls.c
> new file mode 100644
> index 000000000000..9941fb75772a
> --- /dev/null
> +++ b/tools/testing/selftests/kvm/aarch64/hypercalls.c
> @@ -0,0 +1,344 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +
> +/* hypercalls: Check the ARM64's psuedo-firmware bitmap register interface.
> + *
> + * The test validates the basic hypercall functionalities that are exposed
> + * via the psuedo-firmware bitmap register. This includes the registers'
> + * read/write behavior before and after the VM has started, and if the
> + * hypercalls are properly masked or unmasked to the guest when disabled or
> + * enabled from the KVM userspace, respectively.
> + */
> +
> +#include <errno.h>
> +#include <linux/arm-smccc.h>
> +#include <asm/kvm.h>
> +#include <kvm_util.h>
> +
> +#include "processor.h"
> +
> +#define FW_REG_ULIMIT_VAL(max_feat_bit) (GENMASK_ULL(max_feat_bit, 0))
> +
> +/* Last valid bits of the bitmapped firmware registers */
> +#define KVM_REG_ARM_STD_BMAP_BIT_MAX		0
> +#define KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX	0
> +#define KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX	1
> +
> +struct kvm_fw_reg_info {
> +	uint64_t reg;		/* Register definition */
> +	uint64_t max_feat_bit;	/* Bit that represents the upper limit of the feature-map */
> +};
> +
> +#define FW_REG_INFO(r, bit_max)			\
> +	{					\
> +		.reg = r,			\
> +		.max_feat_bit = bit_max,	\
> +	}
> +
> +static const struct kvm_fw_reg_info fw_reg_info[] = {
> +	FW_REG_INFO(KVM_REG_ARM_STD_BMAP, KVM_REG_ARM_STD_BMAP_BIT_MAX),
> +	FW_REG_INFO(KVM_REG_ARM_STD_HYP_BMAP, KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX),
> +	FW_REG_INFO(KVM_REG_ARM_VENDOR_HYP_BMAP, KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX),
> +};
> +

This can be simplifed by:

#define FW_REG_INFO(r)			\
	{ .reg = r,			\
	  .max_feat_bit = r_##BIT_MAX,	\
	}

static const struct kvm_fw_reg_info fw_reg_info[] = {
	FW_REG_INFO(KVM_REG_ARM_STD_BMAP),
	FW_REG_INFO(KVM_REG_ARM_STD_HYP_BMAP),
	FW_REG_INFO(KVM_REG_ARM_VENDOR_HYP_BMAP),
};

> +enum test_stage {
> +	TEST_STAGE_REG_IFACE,
> +	TEST_STAGE_HVC_IFACE_FEAT_DISABLED,
> +	TEST_STAGE_HVC_IFACE_FEAT_ENABLED,
> +	TEST_STAGE_HVC_IFACE_FALSE_INFO,
> +	TEST_STAGE_END,
> +};
> +
> +static int stage;
> +

I think it'd better to initialize @stage to TEST_STAGE_REG_IFACE.

> +struct test_hvc_info {
> +	uint32_t func_id;
> +	int64_t arg0;
> +};
> +
> +#define TEST_HVC_INFO(f, a0)	\
> +	{			\
> +		.func_id = f,	\
> +		.arg0 = a0,	\
> +	}
> +

According to those functions (smccc_get_{function, argx}()) defined
in include/kvm/arm_hypercalls.h, @arg0 would have type of "uint64_t"
if I'm correct. Besides, @func_id is arg0 and arg0 should be arg1?
So if I'm correct, this would be:

struct test_hvc_info {
	uint32_t func_id;
	uint64_t arg1
};

> +static const struct test_hvc_info hvc_info[] = {
> +	/* KVM_REG_ARM_STD_BMAP */
> +	TEST_HVC_INFO(ARM_SMCCC_TRNG_VERSION, 0),
> +	TEST_HVC_INFO(ARM_SMCCC_TRNG_FEATURES, ARM_SMCCC_TRNG_RND64),
> +	TEST_HVC_INFO(ARM_SMCCC_TRNG_GET_UUID, 0),
> +	TEST_HVC_INFO(ARM_SMCCC_TRNG_RND32, 0),
> +	TEST_HVC_INFO(ARM_SMCCC_TRNG_RND64, 0),
> +
> +	/* KVM_REG_ARM_STD_HYP_BMAP */
> +	TEST_HVC_INFO(ARM_SMCCC_ARCH_FEATURES_FUNC_ID, ARM_SMCCC_HV_PV_TIME_FEATURES),
> +	TEST_HVC_INFO(ARM_SMCCC_HV_PV_TIME_FEATURES, ARM_SMCCC_HV_PV_TIME_ST),
> +	TEST_HVC_INFO(ARM_SMCCC_HV_PV_TIME_ST, 0),
> +
> +	/* KVM_REG_ARM_VENDOR_HYP_BMAP */
> +	TEST_HVC_INFO(ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID,
> +			ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID),
> +	TEST_HVC_INFO(ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID, KVM_PTP_VIRT_COUNTER),
> +};
> +
> +/* Feed false hypercall info to test the KVM behavior */
> +static const struct test_hvc_info false_hvc_info[] = {
> +	/* Feature support check against a different family of hypercalls */
> +	TEST_HVC_INFO(ARM_SMCCC_TRNG_FEATURES, ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID),
> +	TEST_HVC_INFO(ARM_SMCCC_ARCH_FEATURES_FUNC_ID, ARM_SMCCC_TRNG_RND64),
> +	TEST_HVC_INFO(ARM_SMCCC_HV_PV_TIME_FEATURES, ARM_SMCCC_TRNG_RND64),
> +};
> +
> +static void guest_test_hvc(const struct test_hvc_info *hc_info)
> +{
> +	unsigned int i;
> +	struct arm_smccc_res res;
> +	unsigned int hvc_info_arr_sz;
> +
> +	hvc_info_arr_sz =
> +	hc_info == hvc_info ? ARRAY_SIZE(hvc_info) : ARRAY_SIZE(false_hvc_info);
> +
> +	for (i = 0; i < hvc_info_arr_sz; i++, hc_info++) {
> +
> +		memset(&res, 0, sizeof(res));
> +		smccc_hvc(hc_info->func_id, hc_info->arg0, 0, 0, 0, 0, 0, 0, &res);
> +

Unnecessary empty line before memset(). I don't find where smccc_hvc()
is defined.

> +		switch (stage) {
> +		case TEST_STAGE_HVC_IFACE_FEAT_DISABLED:
> +		case TEST_STAGE_HVC_IFACE_FALSE_INFO:
> +			GUEST_ASSERT_3(res.a0 == SMCCC_RET_NOT_SUPPORTED,
> +					res.a0, hc_info->func_id, hc_info->arg0);
                                        ^^

It seems the code here isn't properly aligned. Maybe it's your
preference :)

> +			break;
> +		case TEST_STAGE_HVC_IFACE_FEAT_ENABLED:
> +			GUEST_ASSERT_3(res.a0 != SMCCC_RET_NOT_SUPPORTED,
> +					res.a0, hc_info->func_id, hc_info->arg0);
> +			break;
> +		default:
> +			GUEST_ASSERT_1(0, stage);
> +		}
> +	}
> +}
> +
> +static void guest_code(void)
> +{
> +	while (stage != TEST_STAGE_END) {
> +		switch (stage) {
> +		case TEST_STAGE_REG_IFACE:
> +			break;
> +		case TEST_STAGE_HVC_IFACE_FEAT_DISABLED:
> +		case TEST_STAGE_HVC_IFACE_FEAT_ENABLED:
> +			guest_test_hvc(hvc_info);
> +			break;
> +		case TEST_STAGE_HVC_IFACE_FALSE_INFO:
> +			guest_test_hvc(false_hvc_info);
> +			break;
> +		default:
> +			GUEST_ASSERT_1(0, stage);
> +		}
> +
> +		GUEST_SYNC(stage);
> +	}
> +
> +	GUEST_DONE();
> +}
> +
> +static int set_fw_reg(struct kvm_vm *vm, uint64_t id, uint64_t val)
> +{
> +	struct kvm_one_reg reg = {
> +		.id = id,
> +		.addr = (uint64_t)&val,
> +	};
> +
> +	return _vcpu_ioctl(vm, 0, KVM_SET_ONE_REG, &reg);
> +}
> +
> +static void get_fw_reg(struct kvm_vm *vm, uint64_t id, uint64_t *addr)
> +{
> +	struct kvm_one_reg reg = {
> +		.id = id,
> +		.addr = (uint64_t)addr,
> +	};
> +
> +	vcpu_ioctl(vm, 0, KVM_GET_ONE_REG, &reg);
> +}
> +
> +struct st_time {
> +	uint32_t rev;
> +	uint32_t attr;
> +	uint64_t st_time;
> +};
> +
> +#define STEAL_TIME_SIZE		((sizeof(struct st_time) + 63) & ~63)
> +#define ST_GPA_BASE		(1 << 30)
> +
> +static void steal_time_init(struct kvm_vm *vm)
> +{
> +	uint64_t st_ipa = (ulong)ST_GPA_BASE;
> +	unsigned int gpages;
> +	struct kvm_device_attr dev = {
> +		.group = KVM_ARM_VCPU_PVTIME_CTRL,
> +		.attr = KVM_ARM_VCPU_PVTIME_IPA,
> +		.addr = (uint64_t)&st_ipa,
> +	};
> +
> +	gpages = vm_calc_num_guest_pages(VM_MODE_DEFAULT, STEAL_TIME_SIZE);
> +	vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, ST_GPA_BASE, 1, gpages, 0);
> +
> +	vcpu_ioctl(vm, 0, KVM_SET_DEVICE_ATTR, &dev);
> +}
> +

It might be helpful to do TEST_FAIL() on error returned from
this vcpu_ioctl(), or skip those PVTIME SMCCC calls accordingly
if the attribute isn't set successfully.

> +static void test_fw_regs_before_vm_start(struct kvm_vm *vm)
> +{
> +	uint64_t val;
> +	unsigned int i;
> +	int ret;
> +
> +	for (i = 0; i < ARRAY_SIZE(fw_reg_info); i++) {
> +		const struct kvm_fw_reg_info *reg_info = &fw_reg_info[i];
> +
> +		/* First 'read' should be an upper limit of the features supported */
> +		get_fw_reg(vm, reg_info->reg, &val);
> +		TEST_ASSERT(val == FW_REG_ULIMIT_VAL(reg_info->max_feat_bit),
> +			"Expected all the features to be set for reg: 0x%lx; expected: 0x%llx; read: 0x%lx\n",
> +			reg_info->reg, GENMASK_ULL(reg_info->max_feat_bit, 0), val);
> +

s/GENMASK_ULL(...)/FW_REG_ULIMIT_VAL(...)

> +		/* Test a 'write' by disabling all the features of the register map */
> +		ret = set_fw_reg(vm, reg_info->reg, 0);
> +		TEST_ASSERT(ret == 0,
> +			"Failed to clear all the features of reg: 0x%lx; ret: %d\n",
> +			reg_info->reg, errno);
> +
> +		get_fw_reg(vm, reg_info->reg, &val);
> +		TEST_ASSERT(val == 0,
> +			"Expected all the features to be cleared for reg: 0x%lx\n", reg_info->reg);
> +
> +		/*
> +		 * Test enabling a feature that's not supported.
> +		 * Avoid this check if all the bits are occupied.
> +		 */
> +		if (reg_info->max_feat_bit < 63) {
> +			ret = set_fw_reg(vm, reg_info->reg, BIT(reg_info->max_feat_bit + 1));
> +			TEST_ASSERT(ret != 0 && errno == EINVAL,
> +			"Unexpected behavior or return value (%d) while setting an unsupported feature for reg: 0x%lx\n",
> +			errno, reg_info->reg);
> +		}
> +	}
> +}
> +
> +static void test_fw_regs_after_vm_start(struct kvm_vm *vm)
> +{
> +	uint64_t val;
> +	unsigned int i;
> +	int ret;
> +
> +	for (i = 0; i < ARRAY_SIZE(fw_reg_info); i++) {
> +		const struct kvm_fw_reg_info *reg_info = &fw_reg_info[i];
> +
> +		/*
> +		 * Before starting the VM, the test clears all the bits.
> +		 * Check if that's still the case.
> +		 */
> +		get_fw_reg(vm, reg_info->reg, &val);
> +		TEST_ASSERT(val == 0,
> +			"Expected all the features to be cleared for reg: 0x%lx\n",
> +			reg_info->reg);
> +
> +		/*
> +		 * Test setting the last read value. KVM should allow this
> +		 * even if VM has started running.
> +		 */
> +		ret = set_fw_reg(vm, reg_info->reg, val);
> +		TEST_ASSERT(ret == 0,
> +			"Failed to set the register with previously read value after Vm start for reg: 0x%lx; ret: %d\n",
> +			reg_info->reg, errno);
> +
> +		/*
> +		 * Set all the features for this register again. KVM shouldn't
> +		 * allow this as the VM is running.
> +		 */
> +		ret = set_fw_reg(vm, reg_info->reg, FW_REG_ULIMIT_VAL(reg_info->max_feat_bit));
> +		TEST_ASSERT(ret != 0 && errno == EBUSY,
> +		"Unexpected behavior or return value (%d) while setting a feature while VM is running for reg: 0x%lx\n",
> +		errno, reg_info->reg);
> +	}
> +}
> +
> +static struct kvm_vm *test_vm_create(void)
> +{
> +	struct kvm_vm *vm;
> +
> +	vm = vm_create_default(0, 0, guest_code);
> +
> +	ucall_init(vm, NULL);
> +	steal_time_init(vm);
> +
> +	return vm;
> +}
> +
> +static struct kvm_vm *test_guest_stage(struct kvm_vm *vm)
> +{
> +	struct kvm_vm *ret_vm = vm;
> +
> +	pr_debug("Stage: %d\n", stage);
> +
> +	switch (stage) {
> +	case TEST_STAGE_REG_IFACE:
> +		test_fw_regs_after_vm_start(vm);
> +		break;
> +	case TEST_STAGE_HVC_IFACE_FEAT_DISABLED:
> +		/* Start a new VM so that all the features are now enabled by default */
> +		kvm_vm_free(vm);
> +		ret_vm = test_vm_create();
> +		break;
> +	case TEST_STAGE_HVC_IFACE_FEAT_ENABLED:
> +	case TEST_STAGE_HVC_IFACE_FALSE_INFO:
> +		break;
> +	default:
> +		TEST_FAIL("Unknown test stage: %d\n", stage);
> +	}
> +
> +	stage++;
> +	sync_global_to_guest(vm, stage);
> +
> +	return ret_vm;
> +}
> +
> +static void test_run(void)
> +{
> +	struct kvm_vm *vm;
> +	struct ucall uc;
> +	bool guest_done = false;
> +
> +	vm = test_vm_create();
> +
> +	test_fw_regs_before_vm_start(vm);
> +
> +	while (!guest_done) {
> +		vcpu_run(vm, 0);
> +
> +		switch (get_ucall(vm, 0, &uc)) {
> +		case UCALL_SYNC:
> +			vm = test_guest_stage(vm);
> +			break;
> +		case UCALL_DONE:
> +			guest_done = true;
> +			break;
> +		case UCALL_ABORT:
> +			TEST_FAIL("%s at %s:%ld\n\tvalues: 0x%lx, 0x%lx; 0x%lx, stage: %u",
> +			(const char *)uc.args[0], __FILE__, uc.args[1],
> +			uc.args[2], uc.args[3], uc.args[4], stage);
> +			break;
> +		default:
> +			TEST_FAIL("Unexpected guest exit\n");
> +		}
> +	}
> +
> +	kvm_vm_free(vm);
> +}
> +
> +int main(void)
> +{
> +	setbuf(stdout, NULL);
> +
> +	test_run();
> +	return 0;
> +}
> 

Thanks,
Gavin


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

* Re: [PATCH v5 08/10] selftests: KVM: aarch64: Introduce hypercall ABI test
@ 2022-04-13  9:07     ` Gavin Shan
  0 siblings, 0 replies; 96+ messages in thread
From: Gavin Shan @ 2022-04-13  9:07 UTC (permalink / raw)
  To: Raghavendra Rao Ananta, Marc Zyngier, Andrew Jones, James Morse,
	Alexandru Elisei, Suzuki K Poulose
  Cc: kvm, Catalin Marinas, Peter Shier, linux-kernel, Paolo Bonzini,
	Will Deacon, kvmarm, linux-arm-kernel

Hi Raghavendra,

On 4/7/22 9:16 AM, Raghavendra Rao Ananta wrote:
> Introduce a KVM selftest to check the hypercall interface
> for arm64 platforms. The test validates the user-space's
> IOCTL interface to read/write the psuedo-firmware registers
> as well as its effects on the guest upon certain configurations.
> 
> Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
> ---
>   tools/testing/selftests/kvm/.gitignore        |   1 +
>   tools/testing/selftests/kvm/Makefile          |   1 +
>   .../selftests/kvm/aarch64/hypercalls.c        | 344 ++++++++++++++++++
>   3 files changed, 346 insertions(+)
>   create mode 100644 tools/testing/selftests/kvm/aarch64/hypercalls.c
> 

To be more precise, s/IOCTL/{GET,SET}_ONE_REG ?

> diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore
> index e82b816a6608..7ef52b3b1560 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/hypercalls
>   /aarch64/psci_test
>   /aarch64/vgic_init
>   /aarch64/vgic_irq
> diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
> index 2f74f502de65..af4cb88bcf83 100644
> --- a/tools/testing/selftests/kvm/Makefile
> +++ b/tools/testing/selftests/kvm/Makefile
> @@ -105,6 +105,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/hypercalls
>   TEST_GEN_PROGS_aarch64 += aarch64/psci_test
>   TEST_GEN_PROGS_aarch64 += aarch64/vgic_init
>   TEST_GEN_PROGS_aarch64 += aarch64/vgic_irq
> diff --git a/tools/testing/selftests/kvm/aarch64/hypercalls.c b/tools/testing/selftests/kvm/aarch64/hypercalls.c
> new file mode 100644
> index 000000000000..9941fb75772a
> --- /dev/null
> +++ b/tools/testing/selftests/kvm/aarch64/hypercalls.c
> @@ -0,0 +1,344 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +
> +/* hypercalls: Check the ARM64's psuedo-firmware bitmap register interface.
> + *
> + * The test validates the basic hypercall functionalities that are exposed
> + * via the psuedo-firmware bitmap register. This includes the registers'
> + * read/write behavior before and after the VM has started, and if the
> + * hypercalls are properly masked or unmasked to the guest when disabled or
> + * enabled from the KVM userspace, respectively.
> + */
> +
> +#include <errno.h>
> +#include <linux/arm-smccc.h>
> +#include <asm/kvm.h>
> +#include <kvm_util.h>
> +
> +#include "processor.h"
> +
> +#define FW_REG_ULIMIT_VAL(max_feat_bit) (GENMASK_ULL(max_feat_bit, 0))
> +
> +/* Last valid bits of the bitmapped firmware registers */
> +#define KVM_REG_ARM_STD_BMAP_BIT_MAX		0
> +#define KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX	0
> +#define KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX	1
> +
> +struct kvm_fw_reg_info {
> +	uint64_t reg;		/* Register definition */
> +	uint64_t max_feat_bit;	/* Bit that represents the upper limit of the feature-map */
> +};
> +
> +#define FW_REG_INFO(r, bit_max)			\
> +	{					\
> +		.reg = r,			\
> +		.max_feat_bit = bit_max,	\
> +	}
> +
> +static const struct kvm_fw_reg_info fw_reg_info[] = {
> +	FW_REG_INFO(KVM_REG_ARM_STD_BMAP, KVM_REG_ARM_STD_BMAP_BIT_MAX),
> +	FW_REG_INFO(KVM_REG_ARM_STD_HYP_BMAP, KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX),
> +	FW_REG_INFO(KVM_REG_ARM_VENDOR_HYP_BMAP, KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX),
> +};
> +

This can be simplifed by:

#define FW_REG_INFO(r)			\
	{ .reg = r,			\
	  .max_feat_bit = r_##BIT_MAX,	\
	}

static const struct kvm_fw_reg_info fw_reg_info[] = {
	FW_REG_INFO(KVM_REG_ARM_STD_BMAP),
	FW_REG_INFO(KVM_REG_ARM_STD_HYP_BMAP),
	FW_REG_INFO(KVM_REG_ARM_VENDOR_HYP_BMAP),
};

> +enum test_stage {
> +	TEST_STAGE_REG_IFACE,
> +	TEST_STAGE_HVC_IFACE_FEAT_DISABLED,
> +	TEST_STAGE_HVC_IFACE_FEAT_ENABLED,
> +	TEST_STAGE_HVC_IFACE_FALSE_INFO,
> +	TEST_STAGE_END,
> +};
> +
> +static int stage;
> +

I think it'd better to initialize @stage to TEST_STAGE_REG_IFACE.

> +struct test_hvc_info {
> +	uint32_t func_id;
> +	int64_t arg0;
> +};
> +
> +#define TEST_HVC_INFO(f, a0)	\
> +	{			\
> +		.func_id = f,	\
> +		.arg0 = a0,	\
> +	}
> +

According to those functions (smccc_get_{function, argx}()) defined
in include/kvm/arm_hypercalls.h, @arg0 would have type of "uint64_t"
if I'm correct. Besides, @func_id is arg0 and arg0 should be arg1?
So if I'm correct, this would be:

struct test_hvc_info {
	uint32_t func_id;
	uint64_t arg1
};

> +static const struct test_hvc_info hvc_info[] = {
> +	/* KVM_REG_ARM_STD_BMAP */
> +	TEST_HVC_INFO(ARM_SMCCC_TRNG_VERSION, 0),
> +	TEST_HVC_INFO(ARM_SMCCC_TRNG_FEATURES, ARM_SMCCC_TRNG_RND64),
> +	TEST_HVC_INFO(ARM_SMCCC_TRNG_GET_UUID, 0),
> +	TEST_HVC_INFO(ARM_SMCCC_TRNG_RND32, 0),
> +	TEST_HVC_INFO(ARM_SMCCC_TRNG_RND64, 0),
> +
> +	/* KVM_REG_ARM_STD_HYP_BMAP */
> +	TEST_HVC_INFO(ARM_SMCCC_ARCH_FEATURES_FUNC_ID, ARM_SMCCC_HV_PV_TIME_FEATURES),
> +	TEST_HVC_INFO(ARM_SMCCC_HV_PV_TIME_FEATURES, ARM_SMCCC_HV_PV_TIME_ST),
> +	TEST_HVC_INFO(ARM_SMCCC_HV_PV_TIME_ST, 0),
> +
> +	/* KVM_REG_ARM_VENDOR_HYP_BMAP */
> +	TEST_HVC_INFO(ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID,
> +			ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID),
> +	TEST_HVC_INFO(ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID, KVM_PTP_VIRT_COUNTER),
> +};
> +
> +/* Feed false hypercall info to test the KVM behavior */
> +static const struct test_hvc_info false_hvc_info[] = {
> +	/* Feature support check against a different family of hypercalls */
> +	TEST_HVC_INFO(ARM_SMCCC_TRNG_FEATURES, ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID),
> +	TEST_HVC_INFO(ARM_SMCCC_ARCH_FEATURES_FUNC_ID, ARM_SMCCC_TRNG_RND64),
> +	TEST_HVC_INFO(ARM_SMCCC_HV_PV_TIME_FEATURES, ARM_SMCCC_TRNG_RND64),
> +};
> +
> +static void guest_test_hvc(const struct test_hvc_info *hc_info)
> +{
> +	unsigned int i;
> +	struct arm_smccc_res res;
> +	unsigned int hvc_info_arr_sz;
> +
> +	hvc_info_arr_sz =
> +	hc_info == hvc_info ? ARRAY_SIZE(hvc_info) : ARRAY_SIZE(false_hvc_info);
> +
> +	for (i = 0; i < hvc_info_arr_sz; i++, hc_info++) {
> +
> +		memset(&res, 0, sizeof(res));
> +		smccc_hvc(hc_info->func_id, hc_info->arg0, 0, 0, 0, 0, 0, 0, &res);
> +

Unnecessary empty line before memset(). I don't find where smccc_hvc()
is defined.

> +		switch (stage) {
> +		case TEST_STAGE_HVC_IFACE_FEAT_DISABLED:
> +		case TEST_STAGE_HVC_IFACE_FALSE_INFO:
> +			GUEST_ASSERT_3(res.a0 == SMCCC_RET_NOT_SUPPORTED,
> +					res.a0, hc_info->func_id, hc_info->arg0);
                                        ^^

It seems the code here isn't properly aligned. Maybe it's your
preference :)

> +			break;
> +		case TEST_STAGE_HVC_IFACE_FEAT_ENABLED:
> +			GUEST_ASSERT_3(res.a0 != SMCCC_RET_NOT_SUPPORTED,
> +					res.a0, hc_info->func_id, hc_info->arg0);
> +			break;
> +		default:
> +			GUEST_ASSERT_1(0, stage);
> +		}
> +	}
> +}
> +
> +static void guest_code(void)
> +{
> +	while (stage != TEST_STAGE_END) {
> +		switch (stage) {
> +		case TEST_STAGE_REG_IFACE:
> +			break;
> +		case TEST_STAGE_HVC_IFACE_FEAT_DISABLED:
> +		case TEST_STAGE_HVC_IFACE_FEAT_ENABLED:
> +			guest_test_hvc(hvc_info);
> +			break;
> +		case TEST_STAGE_HVC_IFACE_FALSE_INFO:
> +			guest_test_hvc(false_hvc_info);
> +			break;
> +		default:
> +			GUEST_ASSERT_1(0, stage);
> +		}
> +
> +		GUEST_SYNC(stage);
> +	}
> +
> +	GUEST_DONE();
> +}
> +
> +static int set_fw_reg(struct kvm_vm *vm, uint64_t id, uint64_t val)
> +{
> +	struct kvm_one_reg reg = {
> +		.id = id,
> +		.addr = (uint64_t)&val,
> +	};
> +
> +	return _vcpu_ioctl(vm, 0, KVM_SET_ONE_REG, &reg);
> +}
> +
> +static void get_fw_reg(struct kvm_vm *vm, uint64_t id, uint64_t *addr)
> +{
> +	struct kvm_one_reg reg = {
> +		.id = id,
> +		.addr = (uint64_t)addr,
> +	};
> +
> +	vcpu_ioctl(vm, 0, KVM_GET_ONE_REG, &reg);
> +}
> +
> +struct st_time {
> +	uint32_t rev;
> +	uint32_t attr;
> +	uint64_t st_time;
> +};
> +
> +#define STEAL_TIME_SIZE		((sizeof(struct st_time) + 63) & ~63)
> +#define ST_GPA_BASE		(1 << 30)
> +
> +static void steal_time_init(struct kvm_vm *vm)
> +{
> +	uint64_t st_ipa = (ulong)ST_GPA_BASE;
> +	unsigned int gpages;
> +	struct kvm_device_attr dev = {
> +		.group = KVM_ARM_VCPU_PVTIME_CTRL,
> +		.attr = KVM_ARM_VCPU_PVTIME_IPA,
> +		.addr = (uint64_t)&st_ipa,
> +	};
> +
> +	gpages = vm_calc_num_guest_pages(VM_MODE_DEFAULT, STEAL_TIME_SIZE);
> +	vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, ST_GPA_BASE, 1, gpages, 0);
> +
> +	vcpu_ioctl(vm, 0, KVM_SET_DEVICE_ATTR, &dev);
> +}
> +

It might be helpful to do TEST_FAIL() on error returned from
this vcpu_ioctl(), or skip those PVTIME SMCCC calls accordingly
if the attribute isn't set successfully.

> +static void test_fw_regs_before_vm_start(struct kvm_vm *vm)
> +{
> +	uint64_t val;
> +	unsigned int i;
> +	int ret;
> +
> +	for (i = 0; i < ARRAY_SIZE(fw_reg_info); i++) {
> +		const struct kvm_fw_reg_info *reg_info = &fw_reg_info[i];
> +
> +		/* First 'read' should be an upper limit of the features supported */
> +		get_fw_reg(vm, reg_info->reg, &val);
> +		TEST_ASSERT(val == FW_REG_ULIMIT_VAL(reg_info->max_feat_bit),
> +			"Expected all the features to be set for reg: 0x%lx; expected: 0x%llx; read: 0x%lx\n",
> +			reg_info->reg, GENMASK_ULL(reg_info->max_feat_bit, 0), val);
> +

s/GENMASK_ULL(...)/FW_REG_ULIMIT_VAL(...)

> +		/* Test a 'write' by disabling all the features of the register map */
> +		ret = set_fw_reg(vm, reg_info->reg, 0);
> +		TEST_ASSERT(ret == 0,
> +			"Failed to clear all the features of reg: 0x%lx; ret: %d\n",
> +			reg_info->reg, errno);
> +
> +		get_fw_reg(vm, reg_info->reg, &val);
> +		TEST_ASSERT(val == 0,
> +			"Expected all the features to be cleared for reg: 0x%lx\n", reg_info->reg);
> +
> +		/*
> +		 * Test enabling a feature that's not supported.
> +		 * Avoid this check if all the bits are occupied.
> +		 */
> +		if (reg_info->max_feat_bit < 63) {
> +			ret = set_fw_reg(vm, reg_info->reg, BIT(reg_info->max_feat_bit + 1));
> +			TEST_ASSERT(ret != 0 && errno == EINVAL,
> +			"Unexpected behavior or return value (%d) while setting an unsupported feature for reg: 0x%lx\n",
> +			errno, reg_info->reg);
> +		}
> +	}
> +}
> +
> +static void test_fw_regs_after_vm_start(struct kvm_vm *vm)
> +{
> +	uint64_t val;
> +	unsigned int i;
> +	int ret;
> +
> +	for (i = 0; i < ARRAY_SIZE(fw_reg_info); i++) {
> +		const struct kvm_fw_reg_info *reg_info = &fw_reg_info[i];
> +
> +		/*
> +		 * Before starting the VM, the test clears all the bits.
> +		 * Check if that's still the case.
> +		 */
> +		get_fw_reg(vm, reg_info->reg, &val);
> +		TEST_ASSERT(val == 0,
> +			"Expected all the features to be cleared for reg: 0x%lx\n",
> +			reg_info->reg);
> +
> +		/*
> +		 * Test setting the last read value. KVM should allow this
> +		 * even if VM has started running.
> +		 */
> +		ret = set_fw_reg(vm, reg_info->reg, val);
> +		TEST_ASSERT(ret == 0,
> +			"Failed to set the register with previously read value after Vm start for reg: 0x%lx; ret: %d\n",
> +			reg_info->reg, errno);
> +
> +		/*
> +		 * Set all the features for this register again. KVM shouldn't
> +		 * allow this as the VM is running.
> +		 */
> +		ret = set_fw_reg(vm, reg_info->reg, FW_REG_ULIMIT_VAL(reg_info->max_feat_bit));
> +		TEST_ASSERT(ret != 0 && errno == EBUSY,
> +		"Unexpected behavior or return value (%d) while setting a feature while VM is running for reg: 0x%lx\n",
> +		errno, reg_info->reg);
> +	}
> +}
> +
> +static struct kvm_vm *test_vm_create(void)
> +{
> +	struct kvm_vm *vm;
> +
> +	vm = vm_create_default(0, 0, guest_code);
> +
> +	ucall_init(vm, NULL);
> +	steal_time_init(vm);
> +
> +	return vm;
> +}
> +
> +static struct kvm_vm *test_guest_stage(struct kvm_vm *vm)
> +{
> +	struct kvm_vm *ret_vm = vm;
> +
> +	pr_debug("Stage: %d\n", stage);
> +
> +	switch (stage) {
> +	case TEST_STAGE_REG_IFACE:
> +		test_fw_regs_after_vm_start(vm);
> +		break;
> +	case TEST_STAGE_HVC_IFACE_FEAT_DISABLED:
> +		/* Start a new VM so that all the features are now enabled by default */
> +		kvm_vm_free(vm);
> +		ret_vm = test_vm_create();
> +		break;
> +	case TEST_STAGE_HVC_IFACE_FEAT_ENABLED:
> +	case TEST_STAGE_HVC_IFACE_FALSE_INFO:
> +		break;
> +	default:
> +		TEST_FAIL("Unknown test stage: %d\n", stage);
> +	}
> +
> +	stage++;
> +	sync_global_to_guest(vm, stage);
> +
> +	return ret_vm;
> +}
> +
> +static void test_run(void)
> +{
> +	struct kvm_vm *vm;
> +	struct ucall uc;
> +	bool guest_done = false;
> +
> +	vm = test_vm_create();
> +
> +	test_fw_regs_before_vm_start(vm);
> +
> +	while (!guest_done) {
> +		vcpu_run(vm, 0);
> +
> +		switch (get_ucall(vm, 0, &uc)) {
> +		case UCALL_SYNC:
> +			vm = test_guest_stage(vm);
> +			break;
> +		case UCALL_DONE:
> +			guest_done = true;
> +			break;
> +		case UCALL_ABORT:
> +			TEST_FAIL("%s at %s:%ld\n\tvalues: 0x%lx, 0x%lx; 0x%lx, stage: %u",
> +			(const char *)uc.args[0], __FILE__, uc.args[1],
> +			uc.args[2], uc.args[3], uc.args[4], stage);
> +			break;
> +		default:
> +			TEST_FAIL("Unexpected guest exit\n");
> +		}
> +	}
> +
> +	kvm_vm_free(vm);
> +}
> +
> +int main(void)
> +{
> +	setbuf(stdout, NULL);
> +
> +	test_run();
> +	return 0;
> +}
> 

Thanks,
Gavin

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

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

* Re: [PATCH v5 08/10] selftests: KVM: aarch64: Introduce hypercall ABI test
@ 2022-04-13  9:07     ` Gavin Shan
  0 siblings, 0 replies; 96+ messages in thread
From: Gavin Shan @ 2022-04-13  9:07 UTC (permalink / raw)
  To: Raghavendra Rao Ananta, Marc Zyngier, Andrew Jones, James Morse,
	Alexandru Elisei, Suzuki K Poulose
  Cc: kvm, Catalin Marinas, Peter Shier, linux-kernel, Paolo Bonzini,
	Will Deacon, kvmarm, linux-arm-kernel

Hi Raghavendra,

On 4/7/22 9:16 AM, Raghavendra Rao Ananta wrote:
> Introduce a KVM selftest to check the hypercall interface
> for arm64 platforms. The test validates the user-space's
> IOCTL interface to read/write the psuedo-firmware registers
> as well as its effects on the guest upon certain configurations.
> 
> Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
> ---
>   tools/testing/selftests/kvm/.gitignore        |   1 +
>   tools/testing/selftests/kvm/Makefile          |   1 +
>   .../selftests/kvm/aarch64/hypercalls.c        | 344 ++++++++++++++++++
>   3 files changed, 346 insertions(+)
>   create mode 100644 tools/testing/selftests/kvm/aarch64/hypercalls.c
> 

To be more precise, s/IOCTL/{GET,SET}_ONE_REG ?

> diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore
> index e82b816a6608..7ef52b3b1560 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/hypercalls
>   /aarch64/psci_test
>   /aarch64/vgic_init
>   /aarch64/vgic_irq
> diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
> index 2f74f502de65..af4cb88bcf83 100644
> --- a/tools/testing/selftests/kvm/Makefile
> +++ b/tools/testing/selftests/kvm/Makefile
> @@ -105,6 +105,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/hypercalls
>   TEST_GEN_PROGS_aarch64 += aarch64/psci_test
>   TEST_GEN_PROGS_aarch64 += aarch64/vgic_init
>   TEST_GEN_PROGS_aarch64 += aarch64/vgic_irq
> diff --git a/tools/testing/selftests/kvm/aarch64/hypercalls.c b/tools/testing/selftests/kvm/aarch64/hypercalls.c
> new file mode 100644
> index 000000000000..9941fb75772a
> --- /dev/null
> +++ b/tools/testing/selftests/kvm/aarch64/hypercalls.c
> @@ -0,0 +1,344 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +
> +/* hypercalls: Check the ARM64's psuedo-firmware bitmap register interface.
> + *
> + * The test validates the basic hypercall functionalities that are exposed
> + * via the psuedo-firmware bitmap register. This includes the registers'
> + * read/write behavior before and after the VM has started, and if the
> + * hypercalls are properly masked or unmasked to the guest when disabled or
> + * enabled from the KVM userspace, respectively.
> + */
> +
> +#include <errno.h>
> +#include <linux/arm-smccc.h>
> +#include <asm/kvm.h>
> +#include <kvm_util.h>
> +
> +#include "processor.h"
> +
> +#define FW_REG_ULIMIT_VAL(max_feat_bit) (GENMASK_ULL(max_feat_bit, 0))
> +
> +/* Last valid bits of the bitmapped firmware registers */
> +#define KVM_REG_ARM_STD_BMAP_BIT_MAX		0
> +#define KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX	0
> +#define KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX	1
> +
> +struct kvm_fw_reg_info {
> +	uint64_t reg;		/* Register definition */
> +	uint64_t max_feat_bit;	/* Bit that represents the upper limit of the feature-map */
> +};
> +
> +#define FW_REG_INFO(r, bit_max)			\
> +	{					\
> +		.reg = r,			\
> +		.max_feat_bit = bit_max,	\
> +	}
> +
> +static const struct kvm_fw_reg_info fw_reg_info[] = {
> +	FW_REG_INFO(KVM_REG_ARM_STD_BMAP, KVM_REG_ARM_STD_BMAP_BIT_MAX),
> +	FW_REG_INFO(KVM_REG_ARM_STD_HYP_BMAP, KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX),
> +	FW_REG_INFO(KVM_REG_ARM_VENDOR_HYP_BMAP, KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX),
> +};
> +

This can be simplifed by:

#define FW_REG_INFO(r)			\
	{ .reg = r,			\
	  .max_feat_bit = r_##BIT_MAX,	\
	}

static const struct kvm_fw_reg_info fw_reg_info[] = {
	FW_REG_INFO(KVM_REG_ARM_STD_BMAP),
	FW_REG_INFO(KVM_REG_ARM_STD_HYP_BMAP),
	FW_REG_INFO(KVM_REG_ARM_VENDOR_HYP_BMAP),
};

> +enum test_stage {
> +	TEST_STAGE_REG_IFACE,
> +	TEST_STAGE_HVC_IFACE_FEAT_DISABLED,
> +	TEST_STAGE_HVC_IFACE_FEAT_ENABLED,
> +	TEST_STAGE_HVC_IFACE_FALSE_INFO,
> +	TEST_STAGE_END,
> +};
> +
> +static int stage;
> +

I think it'd better to initialize @stage to TEST_STAGE_REG_IFACE.

> +struct test_hvc_info {
> +	uint32_t func_id;
> +	int64_t arg0;
> +};
> +
> +#define TEST_HVC_INFO(f, a0)	\
> +	{			\
> +		.func_id = f,	\
> +		.arg0 = a0,	\
> +	}
> +

According to those functions (smccc_get_{function, argx}()) defined
in include/kvm/arm_hypercalls.h, @arg0 would have type of "uint64_t"
if I'm correct. Besides, @func_id is arg0 and arg0 should be arg1?
So if I'm correct, this would be:

struct test_hvc_info {
	uint32_t func_id;
	uint64_t arg1
};

> +static const struct test_hvc_info hvc_info[] = {
> +	/* KVM_REG_ARM_STD_BMAP */
> +	TEST_HVC_INFO(ARM_SMCCC_TRNG_VERSION, 0),
> +	TEST_HVC_INFO(ARM_SMCCC_TRNG_FEATURES, ARM_SMCCC_TRNG_RND64),
> +	TEST_HVC_INFO(ARM_SMCCC_TRNG_GET_UUID, 0),
> +	TEST_HVC_INFO(ARM_SMCCC_TRNG_RND32, 0),
> +	TEST_HVC_INFO(ARM_SMCCC_TRNG_RND64, 0),
> +
> +	/* KVM_REG_ARM_STD_HYP_BMAP */
> +	TEST_HVC_INFO(ARM_SMCCC_ARCH_FEATURES_FUNC_ID, ARM_SMCCC_HV_PV_TIME_FEATURES),
> +	TEST_HVC_INFO(ARM_SMCCC_HV_PV_TIME_FEATURES, ARM_SMCCC_HV_PV_TIME_ST),
> +	TEST_HVC_INFO(ARM_SMCCC_HV_PV_TIME_ST, 0),
> +
> +	/* KVM_REG_ARM_VENDOR_HYP_BMAP */
> +	TEST_HVC_INFO(ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID,
> +			ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID),
> +	TEST_HVC_INFO(ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID, KVM_PTP_VIRT_COUNTER),
> +};
> +
> +/* Feed false hypercall info to test the KVM behavior */
> +static const struct test_hvc_info false_hvc_info[] = {
> +	/* Feature support check against a different family of hypercalls */
> +	TEST_HVC_INFO(ARM_SMCCC_TRNG_FEATURES, ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID),
> +	TEST_HVC_INFO(ARM_SMCCC_ARCH_FEATURES_FUNC_ID, ARM_SMCCC_TRNG_RND64),
> +	TEST_HVC_INFO(ARM_SMCCC_HV_PV_TIME_FEATURES, ARM_SMCCC_TRNG_RND64),
> +};
> +
> +static void guest_test_hvc(const struct test_hvc_info *hc_info)
> +{
> +	unsigned int i;
> +	struct arm_smccc_res res;
> +	unsigned int hvc_info_arr_sz;
> +
> +	hvc_info_arr_sz =
> +	hc_info == hvc_info ? ARRAY_SIZE(hvc_info) : ARRAY_SIZE(false_hvc_info);
> +
> +	for (i = 0; i < hvc_info_arr_sz; i++, hc_info++) {
> +
> +		memset(&res, 0, sizeof(res));
> +		smccc_hvc(hc_info->func_id, hc_info->arg0, 0, 0, 0, 0, 0, 0, &res);
> +

Unnecessary empty line before memset(). I don't find where smccc_hvc()
is defined.

> +		switch (stage) {
> +		case TEST_STAGE_HVC_IFACE_FEAT_DISABLED:
> +		case TEST_STAGE_HVC_IFACE_FALSE_INFO:
> +			GUEST_ASSERT_3(res.a0 == SMCCC_RET_NOT_SUPPORTED,
> +					res.a0, hc_info->func_id, hc_info->arg0);
                                        ^^

It seems the code here isn't properly aligned. Maybe it's your
preference :)

> +			break;
> +		case TEST_STAGE_HVC_IFACE_FEAT_ENABLED:
> +			GUEST_ASSERT_3(res.a0 != SMCCC_RET_NOT_SUPPORTED,
> +					res.a0, hc_info->func_id, hc_info->arg0);
> +			break;
> +		default:
> +			GUEST_ASSERT_1(0, stage);
> +		}
> +	}
> +}
> +
> +static void guest_code(void)
> +{
> +	while (stage != TEST_STAGE_END) {
> +		switch (stage) {
> +		case TEST_STAGE_REG_IFACE:
> +			break;
> +		case TEST_STAGE_HVC_IFACE_FEAT_DISABLED:
> +		case TEST_STAGE_HVC_IFACE_FEAT_ENABLED:
> +			guest_test_hvc(hvc_info);
> +			break;
> +		case TEST_STAGE_HVC_IFACE_FALSE_INFO:
> +			guest_test_hvc(false_hvc_info);
> +			break;
> +		default:
> +			GUEST_ASSERT_1(0, stage);
> +		}
> +
> +		GUEST_SYNC(stage);
> +	}
> +
> +	GUEST_DONE();
> +}
> +
> +static int set_fw_reg(struct kvm_vm *vm, uint64_t id, uint64_t val)
> +{
> +	struct kvm_one_reg reg = {
> +		.id = id,
> +		.addr = (uint64_t)&val,
> +	};
> +
> +	return _vcpu_ioctl(vm, 0, KVM_SET_ONE_REG, &reg);
> +}
> +
> +static void get_fw_reg(struct kvm_vm *vm, uint64_t id, uint64_t *addr)
> +{
> +	struct kvm_one_reg reg = {
> +		.id = id,
> +		.addr = (uint64_t)addr,
> +	};
> +
> +	vcpu_ioctl(vm, 0, KVM_GET_ONE_REG, &reg);
> +}
> +
> +struct st_time {
> +	uint32_t rev;
> +	uint32_t attr;
> +	uint64_t st_time;
> +};
> +
> +#define STEAL_TIME_SIZE		((sizeof(struct st_time) + 63) & ~63)
> +#define ST_GPA_BASE		(1 << 30)
> +
> +static void steal_time_init(struct kvm_vm *vm)
> +{
> +	uint64_t st_ipa = (ulong)ST_GPA_BASE;
> +	unsigned int gpages;
> +	struct kvm_device_attr dev = {
> +		.group = KVM_ARM_VCPU_PVTIME_CTRL,
> +		.attr = KVM_ARM_VCPU_PVTIME_IPA,
> +		.addr = (uint64_t)&st_ipa,
> +	};
> +
> +	gpages = vm_calc_num_guest_pages(VM_MODE_DEFAULT, STEAL_TIME_SIZE);
> +	vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, ST_GPA_BASE, 1, gpages, 0);
> +
> +	vcpu_ioctl(vm, 0, KVM_SET_DEVICE_ATTR, &dev);
> +}
> +

It might be helpful to do TEST_FAIL() on error returned from
this vcpu_ioctl(), or skip those PVTIME SMCCC calls accordingly
if the attribute isn't set successfully.

> +static void test_fw_regs_before_vm_start(struct kvm_vm *vm)
> +{
> +	uint64_t val;
> +	unsigned int i;
> +	int ret;
> +
> +	for (i = 0; i < ARRAY_SIZE(fw_reg_info); i++) {
> +		const struct kvm_fw_reg_info *reg_info = &fw_reg_info[i];
> +
> +		/* First 'read' should be an upper limit of the features supported */
> +		get_fw_reg(vm, reg_info->reg, &val);
> +		TEST_ASSERT(val == FW_REG_ULIMIT_VAL(reg_info->max_feat_bit),
> +			"Expected all the features to be set for reg: 0x%lx; expected: 0x%llx; read: 0x%lx\n",
> +			reg_info->reg, GENMASK_ULL(reg_info->max_feat_bit, 0), val);
> +

s/GENMASK_ULL(...)/FW_REG_ULIMIT_VAL(...)

> +		/* Test a 'write' by disabling all the features of the register map */
> +		ret = set_fw_reg(vm, reg_info->reg, 0);
> +		TEST_ASSERT(ret == 0,
> +			"Failed to clear all the features of reg: 0x%lx; ret: %d\n",
> +			reg_info->reg, errno);
> +
> +		get_fw_reg(vm, reg_info->reg, &val);
> +		TEST_ASSERT(val == 0,
> +			"Expected all the features to be cleared for reg: 0x%lx\n", reg_info->reg);
> +
> +		/*
> +		 * Test enabling a feature that's not supported.
> +		 * Avoid this check if all the bits are occupied.
> +		 */
> +		if (reg_info->max_feat_bit < 63) {
> +			ret = set_fw_reg(vm, reg_info->reg, BIT(reg_info->max_feat_bit + 1));
> +			TEST_ASSERT(ret != 0 && errno == EINVAL,
> +			"Unexpected behavior or return value (%d) while setting an unsupported feature for reg: 0x%lx\n",
> +			errno, reg_info->reg);
> +		}
> +	}
> +}
> +
> +static void test_fw_regs_after_vm_start(struct kvm_vm *vm)
> +{
> +	uint64_t val;
> +	unsigned int i;
> +	int ret;
> +
> +	for (i = 0; i < ARRAY_SIZE(fw_reg_info); i++) {
> +		const struct kvm_fw_reg_info *reg_info = &fw_reg_info[i];
> +
> +		/*
> +		 * Before starting the VM, the test clears all the bits.
> +		 * Check if that's still the case.
> +		 */
> +		get_fw_reg(vm, reg_info->reg, &val);
> +		TEST_ASSERT(val == 0,
> +			"Expected all the features to be cleared for reg: 0x%lx\n",
> +			reg_info->reg);
> +
> +		/*
> +		 * Test setting the last read value. KVM should allow this
> +		 * even if VM has started running.
> +		 */
> +		ret = set_fw_reg(vm, reg_info->reg, val);
> +		TEST_ASSERT(ret == 0,
> +			"Failed to set the register with previously read value after Vm start for reg: 0x%lx; ret: %d\n",
> +			reg_info->reg, errno);
> +
> +		/*
> +		 * Set all the features for this register again. KVM shouldn't
> +		 * allow this as the VM is running.
> +		 */
> +		ret = set_fw_reg(vm, reg_info->reg, FW_REG_ULIMIT_VAL(reg_info->max_feat_bit));
> +		TEST_ASSERT(ret != 0 && errno == EBUSY,
> +		"Unexpected behavior or return value (%d) while setting a feature while VM is running for reg: 0x%lx\n",
> +		errno, reg_info->reg);
> +	}
> +}
> +
> +static struct kvm_vm *test_vm_create(void)
> +{
> +	struct kvm_vm *vm;
> +
> +	vm = vm_create_default(0, 0, guest_code);
> +
> +	ucall_init(vm, NULL);
> +	steal_time_init(vm);
> +
> +	return vm;
> +}
> +
> +static struct kvm_vm *test_guest_stage(struct kvm_vm *vm)
> +{
> +	struct kvm_vm *ret_vm = vm;
> +
> +	pr_debug("Stage: %d\n", stage);
> +
> +	switch (stage) {
> +	case TEST_STAGE_REG_IFACE:
> +		test_fw_regs_after_vm_start(vm);
> +		break;
> +	case TEST_STAGE_HVC_IFACE_FEAT_DISABLED:
> +		/* Start a new VM so that all the features are now enabled by default */
> +		kvm_vm_free(vm);
> +		ret_vm = test_vm_create();
> +		break;
> +	case TEST_STAGE_HVC_IFACE_FEAT_ENABLED:
> +	case TEST_STAGE_HVC_IFACE_FALSE_INFO:
> +		break;
> +	default:
> +		TEST_FAIL("Unknown test stage: %d\n", stage);
> +	}
> +
> +	stage++;
> +	sync_global_to_guest(vm, stage);
> +
> +	return ret_vm;
> +}
> +
> +static void test_run(void)
> +{
> +	struct kvm_vm *vm;
> +	struct ucall uc;
> +	bool guest_done = false;
> +
> +	vm = test_vm_create();
> +
> +	test_fw_regs_before_vm_start(vm);
> +
> +	while (!guest_done) {
> +		vcpu_run(vm, 0);
> +
> +		switch (get_ucall(vm, 0, &uc)) {
> +		case UCALL_SYNC:
> +			vm = test_guest_stage(vm);
> +			break;
> +		case UCALL_DONE:
> +			guest_done = true;
> +			break;
> +		case UCALL_ABORT:
> +			TEST_FAIL("%s at %s:%ld\n\tvalues: 0x%lx, 0x%lx; 0x%lx, stage: %u",
> +			(const char *)uc.args[0], __FILE__, uc.args[1],
> +			uc.args[2], uc.args[3], uc.args[4], stage);
> +			break;
> +		default:
> +			TEST_FAIL("Unexpected guest exit\n");
> +		}
> +	}
> +
> +	kvm_vm_free(vm);
> +}
> +
> +int main(void)
> +{
> +	setbuf(stdout, NULL);
> +
> +	test_run();
> +	return 0;
> +}
> 

Thanks,
Gavin


_______________________________________________
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] 96+ messages in thread

* Re: [PATCH v5 10/10] selftests: KVM: aarch64: Add KVM_REG_ARM_FW_REG(3) to get-reg-list
  2022-04-07  1:16   ` Raghavendra Rao Ananta
  (?)
@ 2022-04-13  9:22     ` Gavin Shan
  -1 siblings, 0 replies; 96+ messages in thread
From: Gavin Shan @ 2022-04-13  9:22 UTC (permalink / raw)
  To: Raghavendra Rao Ananta, Marc Zyngier, Andrew Jones, James Morse,
	Alexandru Elisei, Suzuki K Poulose
  Cc: kvm, Catalin Marinas, Peter Shier, linux-kernel, Paolo Bonzini,
	Will Deacon, kvmarm, linux-arm-kernel

Hi Raghavendra,

On 4/7/22 9:16 AM, Raghavendra Rao Ananta wrote:
> Add the register KVM_REG_ARM_FW_REG(3)
> (KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3) to the base_regs[] of
> get-reg-list.
> 
> Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
> ---
>   tools/testing/selftests/kvm/aarch64/get-reg-list.c | 1 +
>   1 file changed, 1 insertion(+)
> 
> diff --git a/tools/testing/selftests/kvm/aarch64/get-reg-list.c b/tools/testing/selftests/kvm/aarch64/get-reg-list.c
> index 281c08b3fdd2..7049c31aa443 100644
> --- a/tools/testing/selftests/kvm/aarch64/get-reg-list.c
> +++ b/tools/testing/selftests/kvm/aarch64/get-reg-list.c
> @@ -691,6 +691,7 @@ static __u64 base_regs[] = {
>   	KVM_REG_ARM_FW_REG(0),
>   	KVM_REG_ARM_FW_REG(1),
>   	KVM_REG_ARM_FW_REG(2),
> +	KVM_REG_ARM_FW_REG(3),
>   	KVM_REG_ARM_FW_FEAT_BMAP_REG(0),	/* KVM_REG_ARM_STD_BMAP */
>   	KVM_REG_ARM_FW_FEAT_BMAP_REG(1),	/* KVM_REG_ARM_STD_HYP_BMAP */
>   	KVM_REG_ARM_FW_FEAT_BMAP_REG(2),	/* KVM_REG_ARM_VENDOR_HYP_BMAP */
> 

It seems the same fixup has been done in another patch.

https://www.mail-archive.com/kvmarm@lists.cs.columbia.edu/msg38027.html

Thanks,
Gavin


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

* Re: [PATCH v5 10/10] selftests: KVM: aarch64: Add KVM_REG_ARM_FW_REG(3) to get-reg-list
@ 2022-04-13  9:22     ` Gavin Shan
  0 siblings, 0 replies; 96+ messages in thread
From: Gavin Shan @ 2022-04-13  9:22 UTC (permalink / raw)
  To: Raghavendra Rao Ananta, Marc Zyngier, Andrew Jones, James Morse,
	Alexandru Elisei, Suzuki K Poulose
  Cc: kvm, Catalin Marinas, Peter Shier, linux-kernel, Paolo Bonzini,
	Will Deacon, kvmarm, linux-arm-kernel

Hi Raghavendra,

On 4/7/22 9:16 AM, Raghavendra Rao Ananta wrote:
> Add the register KVM_REG_ARM_FW_REG(3)
> (KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3) to the base_regs[] of
> get-reg-list.
> 
> Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
> ---
>   tools/testing/selftests/kvm/aarch64/get-reg-list.c | 1 +
>   1 file changed, 1 insertion(+)
> 
> diff --git a/tools/testing/selftests/kvm/aarch64/get-reg-list.c b/tools/testing/selftests/kvm/aarch64/get-reg-list.c
> index 281c08b3fdd2..7049c31aa443 100644
> --- a/tools/testing/selftests/kvm/aarch64/get-reg-list.c
> +++ b/tools/testing/selftests/kvm/aarch64/get-reg-list.c
> @@ -691,6 +691,7 @@ static __u64 base_regs[] = {
>   	KVM_REG_ARM_FW_REG(0),
>   	KVM_REG_ARM_FW_REG(1),
>   	KVM_REG_ARM_FW_REG(2),
> +	KVM_REG_ARM_FW_REG(3),
>   	KVM_REG_ARM_FW_FEAT_BMAP_REG(0),	/* KVM_REG_ARM_STD_BMAP */
>   	KVM_REG_ARM_FW_FEAT_BMAP_REG(1),	/* KVM_REG_ARM_STD_HYP_BMAP */
>   	KVM_REG_ARM_FW_FEAT_BMAP_REG(2),	/* KVM_REG_ARM_VENDOR_HYP_BMAP */
> 

It seems the same fixup has been done in another patch.

https://www.mail-archive.com/kvmarm@lists.cs.columbia.edu/msg38027.html

Thanks,
Gavin

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

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

* Re: [PATCH v5 10/10] selftests: KVM: aarch64: Add KVM_REG_ARM_FW_REG(3) to get-reg-list
@ 2022-04-13  9:22     ` Gavin Shan
  0 siblings, 0 replies; 96+ messages in thread
From: Gavin Shan @ 2022-04-13  9:22 UTC (permalink / raw)
  To: Raghavendra Rao Ananta, Marc Zyngier, Andrew Jones, James Morse,
	Alexandru Elisei, Suzuki K Poulose
  Cc: kvm, Catalin Marinas, Peter Shier, linux-kernel, Paolo Bonzini,
	Will Deacon, kvmarm, linux-arm-kernel

Hi Raghavendra,

On 4/7/22 9:16 AM, Raghavendra Rao Ananta wrote:
> Add the register KVM_REG_ARM_FW_REG(3)
> (KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3) to the base_regs[] of
> get-reg-list.
> 
> Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
> ---
>   tools/testing/selftests/kvm/aarch64/get-reg-list.c | 1 +
>   1 file changed, 1 insertion(+)
> 
> diff --git a/tools/testing/selftests/kvm/aarch64/get-reg-list.c b/tools/testing/selftests/kvm/aarch64/get-reg-list.c
> index 281c08b3fdd2..7049c31aa443 100644
> --- a/tools/testing/selftests/kvm/aarch64/get-reg-list.c
> +++ b/tools/testing/selftests/kvm/aarch64/get-reg-list.c
> @@ -691,6 +691,7 @@ static __u64 base_regs[] = {
>   	KVM_REG_ARM_FW_REG(0),
>   	KVM_REG_ARM_FW_REG(1),
>   	KVM_REG_ARM_FW_REG(2),
> +	KVM_REG_ARM_FW_REG(3),
>   	KVM_REG_ARM_FW_FEAT_BMAP_REG(0),	/* KVM_REG_ARM_STD_BMAP */
>   	KVM_REG_ARM_FW_FEAT_BMAP_REG(1),	/* KVM_REG_ARM_STD_HYP_BMAP */
>   	KVM_REG_ARM_FW_FEAT_BMAP_REG(2),	/* KVM_REG_ARM_VENDOR_HYP_BMAP */
> 

It seems the same fixup has been done in another patch.

https://www.mail-archive.com/kvmarm@lists.cs.columbia.edu/msg38027.html

Thanks,
Gavin


_______________________________________________
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] 96+ messages in thread

* Re: [PATCH v5 04/10] KVM: arm64: Add vendor hypervisor firmware register
  2022-04-13  3:59     ` Gavin Shan
  (?)
@ 2022-04-13 16:59       ` Raghavendra Rao Ananta
  -1 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-13 16:59 UTC (permalink / raw)
  To: Gavin Shan
  Cc: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose, kvm, Catalin Marinas, Peter Shier,
	linux-kernel, Paolo Bonzini, Will Deacon, kvmarm,
	linux-arm-kernel

On Tue, Apr 12, 2022 at 8:59 PM Gavin Shan <gshan@redhat.com> wrote:
>
> Hi Raghavendra,
>
> On 4/7/22 9:15 AM, Raghavendra Rao Ananta wrote:
> > Introduce the firmware register to hold the vendor specific
> > hypervisor service calls (owner value 6) as a bitmap. The
> > bitmap represents the features that'll be enabled for the
> > guest, as configured by the user-space. Currently, this
> > includes support for KVM-vendor features, and Precision Time
> > Protocol (PTP), represented by bit-0 and bit-1 respectively.
> >
> > Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
> > ---
> >   arch/arm64/include/asm/kvm_host.h |  2 ++
> >   arch/arm64/include/uapi/asm/kvm.h |  4 ++++
> >   arch/arm64/kvm/hypercalls.c       | 21 +++++++++++++++++----
> >   include/kvm/arm_hypercalls.h      |  4 ++++
> >   4 files changed, 27 insertions(+), 4 deletions(-)
> >
> > diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> > index 20165242ebd9..b79161bad69a 100644
> > --- a/arch/arm64/include/asm/kvm_host.h
> > +++ b/arch/arm64/include/asm/kvm_host.h
> > @@ -106,10 +106,12 @@ struct kvm_arch_memory_slot {
> >    *
> >    * @std_bmap: Bitmap of standard secure service calls
> >    * @std_hyp_bmap: Bitmap of standard hypervisor service calls
> > + * @vendor_hyp_bmap: Bitmap of vendor specific hypervisor service calls
> >    */
> >   struct kvm_smccc_features {
> >       u64 std_bmap;
> >       u64 std_hyp_bmap;
> > +     u64 vendor_hyp_bmap;
> >   };
> >
> >   struct kvm_arch {
> > diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h
> > index 67353bf4e69d..9a5ac0ed4113 100644
> > --- a/arch/arm64/include/uapi/asm/kvm.h
> > +++ b/arch/arm64/include/uapi/asm/kvm.h
> > @@ -344,6 +344,10 @@ struct kvm_arm_copy_mte_tags {
> >   #define KVM_REG_ARM_STD_HYP_BMAP            KVM_REG_ARM_FW_FEAT_BMAP_REG(1)
> >   #define KVM_REG_ARM_STD_HYP_BIT_PV_TIME             BIT(0)
> >
> > +#define KVM_REG_ARM_VENDOR_HYP_BMAP          KVM_REG_ARM_FW_FEAT_BMAP_REG(2)
> > +#define KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT BIT(0)
> > +#define KVM_REG_ARM_VENDOR_HYP_BIT_PTP               BIT(1)
> > +
> >   /* Device Control API: ARM VGIC */
> >   #define KVM_DEV_ARM_VGIC_GRP_ADDR   0
> >   #define KVM_DEV_ARM_VGIC_GRP_DIST_REGS      1
> > diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c
> > index 64ae6c7e7145..80836c341fd3 100644
> > --- a/arch/arm64/kvm/hypercalls.c
> > +++ b/arch/arm64/kvm/hypercalls.c
> > @@ -66,8 +66,6 @@ static const u32 hvc_func_default_allowed_list[] = {
> >       ARM_SMCCC_VERSION_FUNC_ID,
> >       ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
> >       ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID,
> > -     ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID,
> > -     ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID,
> >   };
> >
> >   static bool kvm_hvc_call_default_allowed(struct kvm_vcpu *vcpu, u32 func_id)
> > @@ -102,6 +100,12 @@ static bool kvm_hvc_call_allowed(struct kvm_vcpu *vcpu, u32 func_id)
> >       case ARM_SMCCC_HV_PV_TIME_ST:
> >               return kvm_arm_fw_reg_feat_enabled(smccc_feat->std_hyp_bmap,
> >                                       KVM_REG_ARM_STD_HYP_BIT_PV_TIME);
> > +     case ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID:
> > +             return kvm_arm_fw_reg_feat_enabled(smccc_feat->vendor_hyp_bmap,
> > +                                     KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT);
> > +     case ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID:
> > +             return kvm_arm_fw_reg_feat_enabled(smccc_feat->vendor_hyp_bmap,
> > +                                     KVM_REG_ARM_VENDOR_HYP_BIT_PTP);
> >       default:
> >               return kvm_hvc_call_default_allowed(vcpu, func_id);
> >       }
>
> I guess we may return SMCCC_RET_NOT_SUPPORTED for ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID
> if KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT isn't set? Otherwise, we need explain it
> in the commit log.
>
ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID is a part of the hvc
allowed-list (hvc_func_default_allowed_list[]), which means it's not
associated with any feature bit and is always enabled. If the guest
were to issue ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID, we'd end up in
the 'default' case and the kvm_hvc_call_default_allowed() would return
'true'. This is documented in patch 2/10.

> KVM_REG_ARM_VENDOR_HYP_BIT_{FUNC_FEAT, PTP} aren't parallel to each other.
> I think PTP can't be on if KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT is off.
>
Actually we went through this scenario [1]. Of course, we can build
some logic around it to make sure that the userspace does the right
thing, but at this point the consensus is that, unless it's an issue
for KVM, it's treated as a userspace bug.

> > @@ -194,8 +198,7 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
> >               val[3] = ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_3;
> >               break;
> >       case ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID:
> > -             val[0] = BIT(ARM_SMCCC_KVM_FUNC_FEATURES);
> > -             val[0] |= BIT(ARM_SMCCC_KVM_FUNC_PTP);
> > +             val[0] = smccc_feat->vendor_hyp_bmap;
> >               break;
> >       case ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID:
> >               kvm_ptp_get_time(vcpu, val);
> > @@ -222,6 +225,7 @@ static const u64 kvm_arm_fw_reg_ids[] = {
> >       KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3,
> >       KVM_REG_ARM_STD_BMAP,
> >       KVM_REG_ARM_STD_HYP_BMAP,
> > +     KVM_REG_ARM_VENDOR_HYP_BMAP,
> >   };
> >
> >   void kvm_arm_init_hypercalls(struct kvm *kvm)
> > @@ -230,6 +234,7 @@ void kvm_arm_init_hypercalls(struct kvm *kvm)
> >
> >       smccc_feat->std_bmap = KVM_ARM_SMCCC_STD_FEATURES;
> >       smccc_feat->std_hyp_bmap = KVM_ARM_SMCCC_STD_HYP_FEATURES;
> > +     smccc_feat->vendor_hyp_bmap = KVM_ARM_SMCCC_VENDOR_HYP_FEATURES;
> >   }
> >
> >   int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
> > @@ -322,6 +327,9 @@ int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> >       case KVM_REG_ARM_STD_HYP_BMAP:
> >               val = READ_ONCE(smccc_feat->std_hyp_bmap);
> >               break;
> > +     case KVM_REG_ARM_VENDOR_HYP_BMAP:
> > +             val = READ_ONCE(smccc_feat->vendor_hyp_bmap);
> > +             break;
> >       default:
> >               return -ENOENT;
> >       }
> > @@ -348,6 +356,10 @@ static int kvm_arm_set_fw_reg_bmap(struct kvm_vcpu *vcpu, u64 reg_id, u64 val)
> >               fw_reg_bmap = &smccc_feat->std_hyp_bmap;
> >               fw_reg_features = KVM_ARM_SMCCC_STD_HYP_FEATURES;
> >               break;
> > +     case KVM_REG_ARM_VENDOR_HYP_BMAP:
> > +             fw_reg_bmap = &smccc_feat->vendor_hyp_bmap;
> > +             fw_reg_features = KVM_ARM_SMCCC_VENDOR_HYP_FEATURES;
> > +             break;
> >       default:
> >               return -ENOENT;
> >       }
>
> If KVM_REG_ARM_VENDOR_HYP_BIT_{FUNC_FEAT, PTP} aren't parallel to each other,
> special code is needed to gurantee PTP is cleared if VENDOR_HYP is disabled.
>
Please see the above comment :)

> > @@ -453,6 +465,7 @@ int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> >               return 0;
> >       case KVM_REG_ARM_STD_BMAP:
> >       case KVM_REG_ARM_STD_HYP_BMAP:
> > +     case KVM_REG_ARM_VENDOR_HYP_BMAP:
> >               return kvm_arm_set_fw_reg_bmap(vcpu, reg->id, val);
> >       default:
> >               return -ENOENT;
> > diff --git a/include/kvm/arm_hypercalls.h b/include/kvm/arm_hypercalls.h
> > index b0915d8c5b81..eaf4f6b318a8 100644
> > --- a/include/kvm/arm_hypercalls.h
> > +++ b/include/kvm/arm_hypercalls.h
> > @@ -9,6 +9,7 @@
> >   /* Last valid bits of the bitmapped firmware registers */
> >   #define KVM_REG_ARM_STD_BMAP_BIT_MAX                0
> >   #define KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX    0
> > +#define KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX  1
> >
> >   #define KVM_ARM_SMCCC_STD_FEATURES \
> >       GENMASK_ULL(KVM_REG_ARM_STD_BMAP_BIT_MAX, 0)
> > @@ -16,6 +17,9 @@
> >   #define KVM_ARM_SMCCC_STD_HYP_FEATURES \
> >       GENMASK_ULL(KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX, 0)
> >
> > +#define KVM_ARM_SMCCC_VENDOR_HYP_FEATURES \
> > +     GENMASK_ULL(KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX, 0)
> > +
> >   int kvm_hvc_call_handler(struct kvm_vcpu *vcpu);
> >
> >   static inline u32 smccc_get_function(struct kvm_vcpu *vcpu)
> >
>
> Thanks,
> Gavin
>

Thanks for the review.

Regards,
Raghavendra

[1]: https://lore.kernel.org/lkml/YjA1AzZPlPV20kMj@google.com/

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

* Re: [PATCH v5 04/10] KVM: arm64: Add vendor hypervisor firmware register
@ 2022-04-13 16:59       ` Raghavendra Rao Ananta
  0 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-13 16:59 UTC (permalink / raw)
  To: Gavin Shan
  Cc: kvm, Marc Zyngier, Peter Shier, linux-kernel, Will Deacon,
	Catalin Marinas, Paolo Bonzini, kvmarm, linux-arm-kernel

On Tue, Apr 12, 2022 at 8:59 PM Gavin Shan <gshan@redhat.com> wrote:
>
> Hi Raghavendra,
>
> On 4/7/22 9:15 AM, Raghavendra Rao Ananta wrote:
> > Introduce the firmware register to hold the vendor specific
> > hypervisor service calls (owner value 6) as a bitmap. The
> > bitmap represents the features that'll be enabled for the
> > guest, as configured by the user-space. Currently, this
> > includes support for KVM-vendor features, and Precision Time
> > Protocol (PTP), represented by bit-0 and bit-1 respectively.
> >
> > Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
> > ---
> >   arch/arm64/include/asm/kvm_host.h |  2 ++
> >   arch/arm64/include/uapi/asm/kvm.h |  4 ++++
> >   arch/arm64/kvm/hypercalls.c       | 21 +++++++++++++++++----
> >   include/kvm/arm_hypercalls.h      |  4 ++++
> >   4 files changed, 27 insertions(+), 4 deletions(-)
> >
> > diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> > index 20165242ebd9..b79161bad69a 100644
> > --- a/arch/arm64/include/asm/kvm_host.h
> > +++ b/arch/arm64/include/asm/kvm_host.h
> > @@ -106,10 +106,12 @@ struct kvm_arch_memory_slot {
> >    *
> >    * @std_bmap: Bitmap of standard secure service calls
> >    * @std_hyp_bmap: Bitmap of standard hypervisor service calls
> > + * @vendor_hyp_bmap: Bitmap of vendor specific hypervisor service calls
> >    */
> >   struct kvm_smccc_features {
> >       u64 std_bmap;
> >       u64 std_hyp_bmap;
> > +     u64 vendor_hyp_bmap;
> >   };
> >
> >   struct kvm_arch {
> > diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h
> > index 67353bf4e69d..9a5ac0ed4113 100644
> > --- a/arch/arm64/include/uapi/asm/kvm.h
> > +++ b/arch/arm64/include/uapi/asm/kvm.h
> > @@ -344,6 +344,10 @@ struct kvm_arm_copy_mte_tags {
> >   #define KVM_REG_ARM_STD_HYP_BMAP            KVM_REG_ARM_FW_FEAT_BMAP_REG(1)
> >   #define KVM_REG_ARM_STD_HYP_BIT_PV_TIME             BIT(0)
> >
> > +#define KVM_REG_ARM_VENDOR_HYP_BMAP          KVM_REG_ARM_FW_FEAT_BMAP_REG(2)
> > +#define KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT BIT(0)
> > +#define KVM_REG_ARM_VENDOR_HYP_BIT_PTP               BIT(1)
> > +
> >   /* Device Control API: ARM VGIC */
> >   #define KVM_DEV_ARM_VGIC_GRP_ADDR   0
> >   #define KVM_DEV_ARM_VGIC_GRP_DIST_REGS      1
> > diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c
> > index 64ae6c7e7145..80836c341fd3 100644
> > --- a/arch/arm64/kvm/hypercalls.c
> > +++ b/arch/arm64/kvm/hypercalls.c
> > @@ -66,8 +66,6 @@ static const u32 hvc_func_default_allowed_list[] = {
> >       ARM_SMCCC_VERSION_FUNC_ID,
> >       ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
> >       ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID,
> > -     ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID,
> > -     ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID,
> >   };
> >
> >   static bool kvm_hvc_call_default_allowed(struct kvm_vcpu *vcpu, u32 func_id)
> > @@ -102,6 +100,12 @@ static bool kvm_hvc_call_allowed(struct kvm_vcpu *vcpu, u32 func_id)
> >       case ARM_SMCCC_HV_PV_TIME_ST:
> >               return kvm_arm_fw_reg_feat_enabled(smccc_feat->std_hyp_bmap,
> >                                       KVM_REG_ARM_STD_HYP_BIT_PV_TIME);
> > +     case ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID:
> > +             return kvm_arm_fw_reg_feat_enabled(smccc_feat->vendor_hyp_bmap,
> > +                                     KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT);
> > +     case ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID:
> > +             return kvm_arm_fw_reg_feat_enabled(smccc_feat->vendor_hyp_bmap,
> > +                                     KVM_REG_ARM_VENDOR_HYP_BIT_PTP);
> >       default:
> >               return kvm_hvc_call_default_allowed(vcpu, func_id);
> >       }
>
> I guess we may return SMCCC_RET_NOT_SUPPORTED for ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID
> if KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT isn't set? Otherwise, we need explain it
> in the commit log.
>
ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID is a part of the hvc
allowed-list (hvc_func_default_allowed_list[]), which means it's not
associated with any feature bit and is always enabled. If the guest
were to issue ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID, we'd end up in
the 'default' case and the kvm_hvc_call_default_allowed() would return
'true'. This is documented in patch 2/10.

> KVM_REG_ARM_VENDOR_HYP_BIT_{FUNC_FEAT, PTP} aren't parallel to each other.
> I think PTP can't be on if KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT is off.
>
Actually we went through this scenario [1]. Of course, we can build
some logic around it to make sure that the userspace does the right
thing, but at this point the consensus is that, unless it's an issue
for KVM, it's treated as a userspace bug.

> > @@ -194,8 +198,7 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
> >               val[3] = ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_3;
> >               break;
> >       case ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID:
> > -             val[0] = BIT(ARM_SMCCC_KVM_FUNC_FEATURES);
> > -             val[0] |= BIT(ARM_SMCCC_KVM_FUNC_PTP);
> > +             val[0] = smccc_feat->vendor_hyp_bmap;
> >               break;
> >       case ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID:
> >               kvm_ptp_get_time(vcpu, val);
> > @@ -222,6 +225,7 @@ static const u64 kvm_arm_fw_reg_ids[] = {
> >       KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3,
> >       KVM_REG_ARM_STD_BMAP,
> >       KVM_REG_ARM_STD_HYP_BMAP,
> > +     KVM_REG_ARM_VENDOR_HYP_BMAP,
> >   };
> >
> >   void kvm_arm_init_hypercalls(struct kvm *kvm)
> > @@ -230,6 +234,7 @@ void kvm_arm_init_hypercalls(struct kvm *kvm)
> >
> >       smccc_feat->std_bmap = KVM_ARM_SMCCC_STD_FEATURES;
> >       smccc_feat->std_hyp_bmap = KVM_ARM_SMCCC_STD_HYP_FEATURES;
> > +     smccc_feat->vendor_hyp_bmap = KVM_ARM_SMCCC_VENDOR_HYP_FEATURES;
> >   }
> >
> >   int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
> > @@ -322,6 +327,9 @@ int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> >       case KVM_REG_ARM_STD_HYP_BMAP:
> >               val = READ_ONCE(smccc_feat->std_hyp_bmap);
> >               break;
> > +     case KVM_REG_ARM_VENDOR_HYP_BMAP:
> > +             val = READ_ONCE(smccc_feat->vendor_hyp_bmap);
> > +             break;
> >       default:
> >               return -ENOENT;
> >       }
> > @@ -348,6 +356,10 @@ static int kvm_arm_set_fw_reg_bmap(struct kvm_vcpu *vcpu, u64 reg_id, u64 val)
> >               fw_reg_bmap = &smccc_feat->std_hyp_bmap;
> >               fw_reg_features = KVM_ARM_SMCCC_STD_HYP_FEATURES;
> >               break;
> > +     case KVM_REG_ARM_VENDOR_HYP_BMAP:
> > +             fw_reg_bmap = &smccc_feat->vendor_hyp_bmap;
> > +             fw_reg_features = KVM_ARM_SMCCC_VENDOR_HYP_FEATURES;
> > +             break;
> >       default:
> >               return -ENOENT;
> >       }
>
> If KVM_REG_ARM_VENDOR_HYP_BIT_{FUNC_FEAT, PTP} aren't parallel to each other,
> special code is needed to gurantee PTP is cleared if VENDOR_HYP is disabled.
>
Please see the above comment :)

> > @@ -453,6 +465,7 @@ int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> >               return 0;
> >       case KVM_REG_ARM_STD_BMAP:
> >       case KVM_REG_ARM_STD_HYP_BMAP:
> > +     case KVM_REG_ARM_VENDOR_HYP_BMAP:
> >               return kvm_arm_set_fw_reg_bmap(vcpu, reg->id, val);
> >       default:
> >               return -ENOENT;
> > diff --git a/include/kvm/arm_hypercalls.h b/include/kvm/arm_hypercalls.h
> > index b0915d8c5b81..eaf4f6b318a8 100644
> > --- a/include/kvm/arm_hypercalls.h
> > +++ b/include/kvm/arm_hypercalls.h
> > @@ -9,6 +9,7 @@
> >   /* Last valid bits of the bitmapped firmware registers */
> >   #define KVM_REG_ARM_STD_BMAP_BIT_MAX                0
> >   #define KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX    0
> > +#define KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX  1
> >
> >   #define KVM_ARM_SMCCC_STD_FEATURES \
> >       GENMASK_ULL(KVM_REG_ARM_STD_BMAP_BIT_MAX, 0)
> > @@ -16,6 +17,9 @@
> >   #define KVM_ARM_SMCCC_STD_HYP_FEATURES \
> >       GENMASK_ULL(KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX, 0)
> >
> > +#define KVM_ARM_SMCCC_VENDOR_HYP_FEATURES \
> > +     GENMASK_ULL(KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX, 0)
> > +
> >   int kvm_hvc_call_handler(struct kvm_vcpu *vcpu);
> >
> >   static inline u32 smccc_get_function(struct kvm_vcpu *vcpu)
> >
>
> Thanks,
> Gavin
>

Thanks for the review.

Regards,
Raghavendra

[1]: https://lore.kernel.org/lkml/YjA1AzZPlPV20kMj@google.com/
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

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

* Re: [PATCH v5 04/10] KVM: arm64: Add vendor hypervisor firmware register
@ 2022-04-13 16:59       ` Raghavendra Rao Ananta
  0 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-13 16:59 UTC (permalink / raw)
  To: Gavin Shan
  Cc: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose, kvm, Catalin Marinas, Peter Shier,
	linux-kernel, Paolo Bonzini, Will Deacon, kvmarm,
	linux-arm-kernel

On Tue, Apr 12, 2022 at 8:59 PM Gavin Shan <gshan@redhat.com> wrote:
>
> Hi Raghavendra,
>
> On 4/7/22 9:15 AM, Raghavendra Rao Ananta wrote:
> > Introduce the firmware register to hold the vendor specific
> > hypervisor service calls (owner value 6) as a bitmap. The
> > bitmap represents the features that'll be enabled for the
> > guest, as configured by the user-space. Currently, this
> > includes support for KVM-vendor features, and Precision Time
> > Protocol (PTP), represented by bit-0 and bit-1 respectively.
> >
> > Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
> > ---
> >   arch/arm64/include/asm/kvm_host.h |  2 ++
> >   arch/arm64/include/uapi/asm/kvm.h |  4 ++++
> >   arch/arm64/kvm/hypercalls.c       | 21 +++++++++++++++++----
> >   include/kvm/arm_hypercalls.h      |  4 ++++
> >   4 files changed, 27 insertions(+), 4 deletions(-)
> >
> > diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> > index 20165242ebd9..b79161bad69a 100644
> > --- a/arch/arm64/include/asm/kvm_host.h
> > +++ b/arch/arm64/include/asm/kvm_host.h
> > @@ -106,10 +106,12 @@ struct kvm_arch_memory_slot {
> >    *
> >    * @std_bmap: Bitmap of standard secure service calls
> >    * @std_hyp_bmap: Bitmap of standard hypervisor service calls
> > + * @vendor_hyp_bmap: Bitmap of vendor specific hypervisor service calls
> >    */
> >   struct kvm_smccc_features {
> >       u64 std_bmap;
> >       u64 std_hyp_bmap;
> > +     u64 vendor_hyp_bmap;
> >   };
> >
> >   struct kvm_arch {
> > diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h
> > index 67353bf4e69d..9a5ac0ed4113 100644
> > --- a/arch/arm64/include/uapi/asm/kvm.h
> > +++ b/arch/arm64/include/uapi/asm/kvm.h
> > @@ -344,6 +344,10 @@ struct kvm_arm_copy_mte_tags {
> >   #define KVM_REG_ARM_STD_HYP_BMAP            KVM_REG_ARM_FW_FEAT_BMAP_REG(1)
> >   #define KVM_REG_ARM_STD_HYP_BIT_PV_TIME             BIT(0)
> >
> > +#define KVM_REG_ARM_VENDOR_HYP_BMAP          KVM_REG_ARM_FW_FEAT_BMAP_REG(2)
> > +#define KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT BIT(0)
> > +#define KVM_REG_ARM_VENDOR_HYP_BIT_PTP               BIT(1)
> > +
> >   /* Device Control API: ARM VGIC */
> >   #define KVM_DEV_ARM_VGIC_GRP_ADDR   0
> >   #define KVM_DEV_ARM_VGIC_GRP_DIST_REGS      1
> > diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c
> > index 64ae6c7e7145..80836c341fd3 100644
> > --- a/arch/arm64/kvm/hypercalls.c
> > +++ b/arch/arm64/kvm/hypercalls.c
> > @@ -66,8 +66,6 @@ static const u32 hvc_func_default_allowed_list[] = {
> >       ARM_SMCCC_VERSION_FUNC_ID,
> >       ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
> >       ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID,
> > -     ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID,
> > -     ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID,
> >   };
> >
> >   static bool kvm_hvc_call_default_allowed(struct kvm_vcpu *vcpu, u32 func_id)
> > @@ -102,6 +100,12 @@ static bool kvm_hvc_call_allowed(struct kvm_vcpu *vcpu, u32 func_id)
> >       case ARM_SMCCC_HV_PV_TIME_ST:
> >               return kvm_arm_fw_reg_feat_enabled(smccc_feat->std_hyp_bmap,
> >                                       KVM_REG_ARM_STD_HYP_BIT_PV_TIME);
> > +     case ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID:
> > +             return kvm_arm_fw_reg_feat_enabled(smccc_feat->vendor_hyp_bmap,
> > +                                     KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT);
> > +     case ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID:
> > +             return kvm_arm_fw_reg_feat_enabled(smccc_feat->vendor_hyp_bmap,
> > +                                     KVM_REG_ARM_VENDOR_HYP_BIT_PTP);
> >       default:
> >               return kvm_hvc_call_default_allowed(vcpu, func_id);
> >       }
>
> I guess we may return SMCCC_RET_NOT_SUPPORTED for ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID
> if KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT isn't set? Otherwise, we need explain it
> in the commit log.
>
ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID is a part of the hvc
allowed-list (hvc_func_default_allowed_list[]), which means it's not
associated with any feature bit and is always enabled. If the guest
were to issue ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID, we'd end up in
the 'default' case and the kvm_hvc_call_default_allowed() would return
'true'. This is documented in patch 2/10.

> KVM_REG_ARM_VENDOR_HYP_BIT_{FUNC_FEAT, PTP} aren't parallel to each other.
> I think PTP can't be on if KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT is off.
>
Actually we went through this scenario [1]. Of course, we can build
some logic around it to make sure that the userspace does the right
thing, but at this point the consensus is that, unless it's an issue
for KVM, it's treated as a userspace bug.

> > @@ -194,8 +198,7 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
> >               val[3] = ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_3;
> >               break;
> >       case ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID:
> > -             val[0] = BIT(ARM_SMCCC_KVM_FUNC_FEATURES);
> > -             val[0] |= BIT(ARM_SMCCC_KVM_FUNC_PTP);
> > +             val[0] = smccc_feat->vendor_hyp_bmap;
> >               break;
> >       case ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID:
> >               kvm_ptp_get_time(vcpu, val);
> > @@ -222,6 +225,7 @@ static const u64 kvm_arm_fw_reg_ids[] = {
> >       KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3,
> >       KVM_REG_ARM_STD_BMAP,
> >       KVM_REG_ARM_STD_HYP_BMAP,
> > +     KVM_REG_ARM_VENDOR_HYP_BMAP,
> >   };
> >
> >   void kvm_arm_init_hypercalls(struct kvm *kvm)
> > @@ -230,6 +234,7 @@ void kvm_arm_init_hypercalls(struct kvm *kvm)
> >
> >       smccc_feat->std_bmap = KVM_ARM_SMCCC_STD_FEATURES;
> >       smccc_feat->std_hyp_bmap = KVM_ARM_SMCCC_STD_HYP_FEATURES;
> > +     smccc_feat->vendor_hyp_bmap = KVM_ARM_SMCCC_VENDOR_HYP_FEATURES;
> >   }
> >
> >   int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
> > @@ -322,6 +327,9 @@ int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> >       case KVM_REG_ARM_STD_HYP_BMAP:
> >               val = READ_ONCE(smccc_feat->std_hyp_bmap);
> >               break;
> > +     case KVM_REG_ARM_VENDOR_HYP_BMAP:
> > +             val = READ_ONCE(smccc_feat->vendor_hyp_bmap);
> > +             break;
> >       default:
> >               return -ENOENT;
> >       }
> > @@ -348,6 +356,10 @@ static int kvm_arm_set_fw_reg_bmap(struct kvm_vcpu *vcpu, u64 reg_id, u64 val)
> >               fw_reg_bmap = &smccc_feat->std_hyp_bmap;
> >               fw_reg_features = KVM_ARM_SMCCC_STD_HYP_FEATURES;
> >               break;
> > +     case KVM_REG_ARM_VENDOR_HYP_BMAP:
> > +             fw_reg_bmap = &smccc_feat->vendor_hyp_bmap;
> > +             fw_reg_features = KVM_ARM_SMCCC_VENDOR_HYP_FEATURES;
> > +             break;
> >       default:
> >               return -ENOENT;
> >       }
>
> If KVM_REG_ARM_VENDOR_HYP_BIT_{FUNC_FEAT, PTP} aren't parallel to each other,
> special code is needed to gurantee PTP is cleared if VENDOR_HYP is disabled.
>
Please see the above comment :)

> > @@ -453,6 +465,7 @@ int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> >               return 0;
> >       case KVM_REG_ARM_STD_BMAP:
> >       case KVM_REG_ARM_STD_HYP_BMAP:
> > +     case KVM_REG_ARM_VENDOR_HYP_BMAP:
> >               return kvm_arm_set_fw_reg_bmap(vcpu, reg->id, val);
> >       default:
> >               return -ENOENT;
> > diff --git a/include/kvm/arm_hypercalls.h b/include/kvm/arm_hypercalls.h
> > index b0915d8c5b81..eaf4f6b318a8 100644
> > --- a/include/kvm/arm_hypercalls.h
> > +++ b/include/kvm/arm_hypercalls.h
> > @@ -9,6 +9,7 @@
> >   /* Last valid bits of the bitmapped firmware registers */
> >   #define KVM_REG_ARM_STD_BMAP_BIT_MAX                0
> >   #define KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX    0
> > +#define KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX  1
> >
> >   #define KVM_ARM_SMCCC_STD_FEATURES \
> >       GENMASK_ULL(KVM_REG_ARM_STD_BMAP_BIT_MAX, 0)
> > @@ -16,6 +17,9 @@
> >   #define KVM_ARM_SMCCC_STD_HYP_FEATURES \
> >       GENMASK_ULL(KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX, 0)
> >
> > +#define KVM_ARM_SMCCC_VENDOR_HYP_FEATURES \
> > +     GENMASK_ULL(KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX, 0)
> > +
> >   int kvm_hvc_call_handler(struct kvm_vcpu *vcpu);
> >
> >   static inline u32 smccc_get_function(struct kvm_vcpu *vcpu)
> >
>
> Thanks,
> Gavin
>

Thanks for the review.

Regards,
Raghavendra

[1]: https://lore.kernel.org/lkml/YjA1AzZPlPV20kMj@google.com/

_______________________________________________
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] 96+ messages in thread

* Re: [PATCH v5 06/10] Docs: KVM: Add doc for the bitmap firmware registers
  2022-04-13  6:39     ` Gavin Shan
  (?)
@ 2022-04-13 17:01       ` Raghavendra Rao Ananta
  -1 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-13 17:01 UTC (permalink / raw)
  To: Gavin Shan
  Cc: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose, kvm, Catalin Marinas, Peter Shier,
	linux-kernel, Paolo Bonzini, Will Deacon, kvmarm,
	linux-arm-kernel

On Tue, Apr 12, 2022 at 11:40 PM Gavin Shan <gshan@redhat.com> wrote:
>
> Hi Raghavendra,
>
> On 4/7/22 9:16 AM, Raghavendra Rao Ananta wrote:
> > Add the documentation for the bitmap firmware registers in
> > hypercalls.rst and api.rst. This includes the details for
> > KVM_REG_ARM_STD_BMAP, KVM_REG_ARM_STD_HYP_BMAP, and
> > KVM_REG_ARM_VENDOR_HYP_BMAP registers.
> >
> > Since the document is growing to carry other hypercall related
> > information, make necessary adjustments to present the document
> > in a generic sense, rather than being PSCI focused.
> >
> > Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
> > ---
> >   Documentation/virt/kvm/api.rst            | 17 ++++
> >   Documentation/virt/kvm/arm/hypercalls.rst | 95 ++++++++++++++++++-----
> >   2 files changed, 94 insertions(+), 18 deletions(-)
> >
> > diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
> > index d13fa6600467..e0107b157965 100644
> > --- a/Documentation/virt/kvm/api.rst
> > +++ b/Documentation/virt/kvm/api.rst
> > @@ -2542,6 +2542,23 @@ arm64 firmware pseudo-registers have the following bit pattern::
> >
> >     0x6030 0000 0014 <regno:16>
> >
> > +arm64 bitmap feature firmware pseudo-registers have the following bit pattern::
> > +
> > +  0x6030 0000 0016 <regno:16>
> > +
> > +The bitmap feature firmware registers exposes the hypercall services that are
> > +available for userspace to configure. The set bits corresponds to the services
> > +that are available for the guests to access. By default, KVM sets all the
> > +supported bits during VM initialization. The userspace can discover the
> > +available services via KVM_GET_ONE_REG, and write-back the bitmap corresponding
> > +to the features that it wishes guests to see via KVM_SET_ONE_REG.
> > +
> > +Note: These registers are immutable once any of the vCPUs of the VM has run at
> > +least once. A KVM_SET_ONE_REG in such a scenario will return a -EBUSY to userspace.
> > +If there's no change in the value that's being written, 0 (success) is returned.
> > +
> > +(See Documentation/virt/kvm/arm/hypercalls.rst for more details.)
> > +
> >   arm64 SVE registers have the following bit patterns::
> >
> >     0x6080 0000 0015 00 <n:5> <slice:5>   Zn bits[2048*slice + 2047 : 2048*slice]
> > diff --git a/Documentation/virt/kvm/arm/hypercalls.rst b/Documentation/virt/kvm/arm/hypercalls.rst
> > index d52c2e83b5b8..ccda9fc2d253 100644
> > --- a/Documentation/virt/kvm/arm/hypercalls.rst
> > +++ b/Documentation/virt/kvm/arm/hypercalls.rst
> > @@ -1,32 +1,32 @@
> >   .. SPDX-License-Identifier: GPL-2.0
> >
> > -=========================================
> > -Power State Coordination Interface (PSCI)
> > -=========================================
> > +=======================
> > +ARM Hypercall Interface
> > +=======================
> >
> > -KVM implements the PSCI (Power State Coordination Interface)
> > -specification in order to provide services such as CPU on/off, reset
> > -and power-off to the guest.
> > +KVM handles the hypercall services as requested by the guests. New hypercall
> > +services are regularly made available by the ARM specification or by KVM (as
> > +vendor services) if they make sense from a virtualization point of view.
> >
> > -The PSCI specification is regularly updated to provide new features,
> > -and KVM implements these updates if they make sense from a virtualization
> > -point of view.
> > -
> > -This means that a guest booted on two different versions of KVM can
> > -observe two different "firmware" revisions. This could cause issues if
> > -a given guest is tied to a particular PSCI revision (unlikely), or if
> > -a migration causes a different PSCI version to be exposed out of the
> > -blue to an unsuspecting guest.
> > +This means that a guest booted on two different versions of KVM can observe
> > +two different "firmware" revisions. This could cause issues if a given guest
> > +is tied to a particular version of a hypercall service, or if a migration
> > +causes a different version to be exposed out of the blue to an unsuspecting
> > +guest.
> >
> >   In order to remedy this situation, KVM exposes a set of "firmware
> >   pseudo-registers" that can be manipulated using the GET/SET_ONE_REG
> >   interface. These registers can be saved/restored by userspace, and set
> > -to a convenient value if required.
> > +to a convenient value as required.
> >
> > -The following register is defined:
> > +The following registers are defined:
> >
> >   * KVM_REG_ARM_PSCI_VERSION:
> >
> > +  KVM implements the PSCI (Power State Coordination Interface)
> > +  specification in order to provide services such as CPU on/off, reset
> > +  and power-off to the guest.
> > +
> >     - Only valid if the vcpu has the KVM_ARM_VCPU_PSCI_0_2 feature set
> >       (and thus has already been initialized)
> >     - Returns the current PSCI version on GET_ONE_REG (defaulting to the
> > @@ -74,4 +74,63 @@ The following register is defined:
> >       KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED:
> >         The workaround is always active on this vCPU or it is not needed.
> >
> > -.. [1] https://developer.arm.com/-/media/developer/pdf/ARM_DEN_0070A_Firmware_interfaces_for_mitigating_CVE-2017-5715.pdf
> > +
> > +Bitmap Feature Firmware Registers
> > +---------------------------------
> > +
> > +Contrary to the above registers, the following registers exposes the hypercall
> > +services in the form of a feature-bitmap to the userspace. This bitmap is
> > +translated to the services that are available to the guest. There is a register
> > +defined per service call owner and can be accessed via GET/SET_ONE_REG interface.
> > +
> > +By default, these registers are set with the upper limit of the features that
> > +are supported. This way userspace can discover all the electable hypercall services
> > +via GET_ONE_REG. The user-space can write-back the desired bitmap back via
> > +SET_ONE_REG. The features for the registers that are untouched, probably because
> > +userspace isn't aware of them, will be exposed as is to the guest.
> > +
> > +Note that KVM would't allow the userspace to configure the registers anymore once
> > +any of the vCPUs has run at least once. Instead, it will return a -EBUSY. However,
> > +if there's no change in the incoming value, it simply returns a success.
> > +
>
> It would be better to replace "a success" with "zero", to be consistent
> with "-EBUSY". The suggestion may be invalid if the code needs changes
> based on Marc's suggestions.
>
Yes, I agree. Mentioning 'zero' makes more sense. However, I would be
scraping off this logic and return -EBUSY for all the writes after the
VM has started.

> > +The psuedo-firmware bitmap register are as follows:
> > +
> > +* KVM_REG_ARM_STD_BMAP:
> > +    Controls the bitmap of the ARM Standard Secure Service Calls.
> > +
> > +  The following bits are accepted:
> > +
> > +    Bit-0: KVM_REG_ARM_STD_BIT_TRNG_V1_0:
> > +      The bit represents the services offered under v1.0 of ARM True Random
> > +      Number Generator (TRNG) specification, ARM DEN0098.
> > +
> > +* KVM_REG_ARM_STD_HYP_BMAP:
> > +    Controls the bitmap of the ARM Standard Hypervisor Service Calls.
> > +
> > +  The following bits are accepted:
> > +
> > +    Bit-0: KVM_REG_ARM_STD_HYP_BIT_PV_TIME:
> > +      The bit represents the Paravirtualized Time service as represented by
> > +      ARM DEN0057A.
> > +
> > +* KVM_REG_ARM_VENDOR_HYP_BMAP:
> > +    Controls the bitmap of the Vendor specific Hypervisor Service Calls.
> > +
> > +  The following bits are accepted:
> > +
> > +    Bit-0: KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT
> > +      The bit represents the ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID
> > +      function-id
> > +
> > +    Bit-1: KVM_REG_ARM_VENDOR_HYP_BIT_PTP:
> > +      The bit represents the Precision Time Protocol KVM service.
> > +
> > +Errors:
> > +
> > +    =======  =============================================================
> > +    -ENOENT   Unknown register accessed.
> > +    -EBUSY    Attempt a 'write' to the register after the VM has started.
> > +    -EINVAL   Invalid bitmap written to the register.
> > +    =======  =============================================================
> > +
> > +.. [1] https://developer.arm.com/-/media/developer/pdf/ARM_DEN_0070A_Firmware_interfaces_for_mitigating_CVE-2017-5715.pdf
> > \ No newline at end of file
> >
>
> Thanks,
> Gavin
>
Regards,
Raghavendra

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

* Re: [PATCH v5 06/10] Docs: KVM: Add doc for the bitmap firmware registers
@ 2022-04-13 17:01       ` Raghavendra Rao Ananta
  0 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-13 17:01 UTC (permalink / raw)
  To: Gavin Shan
  Cc: kvm, Marc Zyngier, Peter Shier, linux-kernel, Will Deacon,
	Catalin Marinas, Paolo Bonzini, kvmarm, linux-arm-kernel

On Tue, Apr 12, 2022 at 11:40 PM Gavin Shan <gshan@redhat.com> wrote:
>
> Hi Raghavendra,
>
> On 4/7/22 9:16 AM, Raghavendra Rao Ananta wrote:
> > Add the documentation for the bitmap firmware registers in
> > hypercalls.rst and api.rst. This includes the details for
> > KVM_REG_ARM_STD_BMAP, KVM_REG_ARM_STD_HYP_BMAP, and
> > KVM_REG_ARM_VENDOR_HYP_BMAP registers.
> >
> > Since the document is growing to carry other hypercall related
> > information, make necessary adjustments to present the document
> > in a generic sense, rather than being PSCI focused.
> >
> > Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
> > ---
> >   Documentation/virt/kvm/api.rst            | 17 ++++
> >   Documentation/virt/kvm/arm/hypercalls.rst | 95 ++++++++++++++++++-----
> >   2 files changed, 94 insertions(+), 18 deletions(-)
> >
> > diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
> > index d13fa6600467..e0107b157965 100644
> > --- a/Documentation/virt/kvm/api.rst
> > +++ b/Documentation/virt/kvm/api.rst
> > @@ -2542,6 +2542,23 @@ arm64 firmware pseudo-registers have the following bit pattern::
> >
> >     0x6030 0000 0014 <regno:16>
> >
> > +arm64 bitmap feature firmware pseudo-registers have the following bit pattern::
> > +
> > +  0x6030 0000 0016 <regno:16>
> > +
> > +The bitmap feature firmware registers exposes the hypercall services that are
> > +available for userspace to configure. The set bits corresponds to the services
> > +that are available for the guests to access. By default, KVM sets all the
> > +supported bits during VM initialization. The userspace can discover the
> > +available services via KVM_GET_ONE_REG, and write-back the bitmap corresponding
> > +to the features that it wishes guests to see via KVM_SET_ONE_REG.
> > +
> > +Note: These registers are immutable once any of the vCPUs of the VM has run at
> > +least once. A KVM_SET_ONE_REG in such a scenario will return a -EBUSY to userspace.
> > +If there's no change in the value that's being written, 0 (success) is returned.
> > +
> > +(See Documentation/virt/kvm/arm/hypercalls.rst for more details.)
> > +
> >   arm64 SVE registers have the following bit patterns::
> >
> >     0x6080 0000 0015 00 <n:5> <slice:5>   Zn bits[2048*slice + 2047 : 2048*slice]
> > diff --git a/Documentation/virt/kvm/arm/hypercalls.rst b/Documentation/virt/kvm/arm/hypercalls.rst
> > index d52c2e83b5b8..ccda9fc2d253 100644
> > --- a/Documentation/virt/kvm/arm/hypercalls.rst
> > +++ b/Documentation/virt/kvm/arm/hypercalls.rst
> > @@ -1,32 +1,32 @@
> >   .. SPDX-License-Identifier: GPL-2.0
> >
> > -=========================================
> > -Power State Coordination Interface (PSCI)
> > -=========================================
> > +=======================
> > +ARM Hypercall Interface
> > +=======================
> >
> > -KVM implements the PSCI (Power State Coordination Interface)
> > -specification in order to provide services such as CPU on/off, reset
> > -and power-off to the guest.
> > +KVM handles the hypercall services as requested by the guests. New hypercall
> > +services are regularly made available by the ARM specification or by KVM (as
> > +vendor services) if they make sense from a virtualization point of view.
> >
> > -The PSCI specification is regularly updated to provide new features,
> > -and KVM implements these updates if they make sense from a virtualization
> > -point of view.
> > -
> > -This means that a guest booted on two different versions of KVM can
> > -observe two different "firmware" revisions. This could cause issues if
> > -a given guest is tied to a particular PSCI revision (unlikely), or if
> > -a migration causes a different PSCI version to be exposed out of the
> > -blue to an unsuspecting guest.
> > +This means that a guest booted on two different versions of KVM can observe
> > +two different "firmware" revisions. This could cause issues if a given guest
> > +is tied to a particular version of a hypercall service, or if a migration
> > +causes a different version to be exposed out of the blue to an unsuspecting
> > +guest.
> >
> >   In order to remedy this situation, KVM exposes a set of "firmware
> >   pseudo-registers" that can be manipulated using the GET/SET_ONE_REG
> >   interface. These registers can be saved/restored by userspace, and set
> > -to a convenient value if required.
> > +to a convenient value as required.
> >
> > -The following register is defined:
> > +The following registers are defined:
> >
> >   * KVM_REG_ARM_PSCI_VERSION:
> >
> > +  KVM implements the PSCI (Power State Coordination Interface)
> > +  specification in order to provide services such as CPU on/off, reset
> > +  and power-off to the guest.
> > +
> >     - Only valid if the vcpu has the KVM_ARM_VCPU_PSCI_0_2 feature set
> >       (and thus has already been initialized)
> >     - Returns the current PSCI version on GET_ONE_REG (defaulting to the
> > @@ -74,4 +74,63 @@ The following register is defined:
> >       KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED:
> >         The workaround is always active on this vCPU or it is not needed.
> >
> > -.. [1] https://developer.arm.com/-/media/developer/pdf/ARM_DEN_0070A_Firmware_interfaces_for_mitigating_CVE-2017-5715.pdf
> > +
> > +Bitmap Feature Firmware Registers
> > +---------------------------------
> > +
> > +Contrary to the above registers, the following registers exposes the hypercall
> > +services in the form of a feature-bitmap to the userspace. This bitmap is
> > +translated to the services that are available to the guest. There is a register
> > +defined per service call owner and can be accessed via GET/SET_ONE_REG interface.
> > +
> > +By default, these registers are set with the upper limit of the features that
> > +are supported. This way userspace can discover all the electable hypercall services
> > +via GET_ONE_REG. The user-space can write-back the desired bitmap back via
> > +SET_ONE_REG. The features for the registers that are untouched, probably because
> > +userspace isn't aware of them, will be exposed as is to the guest.
> > +
> > +Note that KVM would't allow the userspace to configure the registers anymore once
> > +any of the vCPUs has run at least once. Instead, it will return a -EBUSY. However,
> > +if there's no change in the incoming value, it simply returns a success.
> > +
>
> It would be better to replace "a success" with "zero", to be consistent
> with "-EBUSY". The suggestion may be invalid if the code needs changes
> based on Marc's suggestions.
>
Yes, I agree. Mentioning 'zero' makes more sense. However, I would be
scraping off this logic and return -EBUSY for all the writes after the
VM has started.

> > +The psuedo-firmware bitmap register are as follows:
> > +
> > +* KVM_REG_ARM_STD_BMAP:
> > +    Controls the bitmap of the ARM Standard Secure Service Calls.
> > +
> > +  The following bits are accepted:
> > +
> > +    Bit-0: KVM_REG_ARM_STD_BIT_TRNG_V1_0:
> > +      The bit represents the services offered under v1.0 of ARM True Random
> > +      Number Generator (TRNG) specification, ARM DEN0098.
> > +
> > +* KVM_REG_ARM_STD_HYP_BMAP:
> > +    Controls the bitmap of the ARM Standard Hypervisor Service Calls.
> > +
> > +  The following bits are accepted:
> > +
> > +    Bit-0: KVM_REG_ARM_STD_HYP_BIT_PV_TIME:
> > +      The bit represents the Paravirtualized Time service as represented by
> > +      ARM DEN0057A.
> > +
> > +* KVM_REG_ARM_VENDOR_HYP_BMAP:
> > +    Controls the bitmap of the Vendor specific Hypervisor Service Calls.
> > +
> > +  The following bits are accepted:
> > +
> > +    Bit-0: KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT
> > +      The bit represents the ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID
> > +      function-id
> > +
> > +    Bit-1: KVM_REG_ARM_VENDOR_HYP_BIT_PTP:
> > +      The bit represents the Precision Time Protocol KVM service.
> > +
> > +Errors:
> > +
> > +    =======  =============================================================
> > +    -ENOENT   Unknown register accessed.
> > +    -EBUSY    Attempt a 'write' to the register after the VM has started.
> > +    -EINVAL   Invalid bitmap written to the register.
> > +    =======  =============================================================
> > +
> > +.. [1] https://developer.arm.com/-/media/developer/pdf/ARM_DEN_0070A_Firmware_interfaces_for_mitigating_CVE-2017-5715.pdf
> > \ No newline at end of file
> >
>
> Thanks,
> Gavin
>
Regards,
Raghavendra
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

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

* Re: [PATCH v5 06/10] Docs: KVM: Add doc for the bitmap firmware registers
@ 2022-04-13 17:01       ` Raghavendra Rao Ananta
  0 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-13 17:01 UTC (permalink / raw)
  To: Gavin Shan
  Cc: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose, kvm, Catalin Marinas, Peter Shier,
	linux-kernel, Paolo Bonzini, Will Deacon, kvmarm,
	linux-arm-kernel

On Tue, Apr 12, 2022 at 11:40 PM Gavin Shan <gshan@redhat.com> wrote:
>
> Hi Raghavendra,
>
> On 4/7/22 9:16 AM, Raghavendra Rao Ananta wrote:
> > Add the documentation for the bitmap firmware registers in
> > hypercalls.rst and api.rst. This includes the details for
> > KVM_REG_ARM_STD_BMAP, KVM_REG_ARM_STD_HYP_BMAP, and
> > KVM_REG_ARM_VENDOR_HYP_BMAP registers.
> >
> > Since the document is growing to carry other hypercall related
> > information, make necessary adjustments to present the document
> > in a generic sense, rather than being PSCI focused.
> >
> > Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
> > ---
> >   Documentation/virt/kvm/api.rst            | 17 ++++
> >   Documentation/virt/kvm/arm/hypercalls.rst | 95 ++++++++++++++++++-----
> >   2 files changed, 94 insertions(+), 18 deletions(-)
> >
> > diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
> > index d13fa6600467..e0107b157965 100644
> > --- a/Documentation/virt/kvm/api.rst
> > +++ b/Documentation/virt/kvm/api.rst
> > @@ -2542,6 +2542,23 @@ arm64 firmware pseudo-registers have the following bit pattern::
> >
> >     0x6030 0000 0014 <regno:16>
> >
> > +arm64 bitmap feature firmware pseudo-registers have the following bit pattern::
> > +
> > +  0x6030 0000 0016 <regno:16>
> > +
> > +The bitmap feature firmware registers exposes the hypercall services that are
> > +available for userspace to configure. The set bits corresponds to the services
> > +that are available for the guests to access. By default, KVM sets all the
> > +supported bits during VM initialization. The userspace can discover the
> > +available services via KVM_GET_ONE_REG, and write-back the bitmap corresponding
> > +to the features that it wishes guests to see via KVM_SET_ONE_REG.
> > +
> > +Note: These registers are immutable once any of the vCPUs of the VM has run at
> > +least once. A KVM_SET_ONE_REG in such a scenario will return a -EBUSY to userspace.
> > +If there's no change in the value that's being written, 0 (success) is returned.
> > +
> > +(See Documentation/virt/kvm/arm/hypercalls.rst for more details.)
> > +
> >   arm64 SVE registers have the following bit patterns::
> >
> >     0x6080 0000 0015 00 <n:5> <slice:5>   Zn bits[2048*slice + 2047 : 2048*slice]
> > diff --git a/Documentation/virt/kvm/arm/hypercalls.rst b/Documentation/virt/kvm/arm/hypercalls.rst
> > index d52c2e83b5b8..ccda9fc2d253 100644
> > --- a/Documentation/virt/kvm/arm/hypercalls.rst
> > +++ b/Documentation/virt/kvm/arm/hypercalls.rst
> > @@ -1,32 +1,32 @@
> >   .. SPDX-License-Identifier: GPL-2.0
> >
> > -=========================================
> > -Power State Coordination Interface (PSCI)
> > -=========================================
> > +=======================
> > +ARM Hypercall Interface
> > +=======================
> >
> > -KVM implements the PSCI (Power State Coordination Interface)
> > -specification in order to provide services such as CPU on/off, reset
> > -and power-off to the guest.
> > +KVM handles the hypercall services as requested by the guests. New hypercall
> > +services are regularly made available by the ARM specification or by KVM (as
> > +vendor services) if they make sense from a virtualization point of view.
> >
> > -The PSCI specification is regularly updated to provide new features,
> > -and KVM implements these updates if they make sense from a virtualization
> > -point of view.
> > -
> > -This means that a guest booted on two different versions of KVM can
> > -observe two different "firmware" revisions. This could cause issues if
> > -a given guest is tied to a particular PSCI revision (unlikely), or if
> > -a migration causes a different PSCI version to be exposed out of the
> > -blue to an unsuspecting guest.
> > +This means that a guest booted on two different versions of KVM can observe
> > +two different "firmware" revisions. This could cause issues if a given guest
> > +is tied to a particular version of a hypercall service, or if a migration
> > +causes a different version to be exposed out of the blue to an unsuspecting
> > +guest.
> >
> >   In order to remedy this situation, KVM exposes a set of "firmware
> >   pseudo-registers" that can be manipulated using the GET/SET_ONE_REG
> >   interface. These registers can be saved/restored by userspace, and set
> > -to a convenient value if required.
> > +to a convenient value as required.
> >
> > -The following register is defined:
> > +The following registers are defined:
> >
> >   * KVM_REG_ARM_PSCI_VERSION:
> >
> > +  KVM implements the PSCI (Power State Coordination Interface)
> > +  specification in order to provide services such as CPU on/off, reset
> > +  and power-off to the guest.
> > +
> >     - Only valid if the vcpu has the KVM_ARM_VCPU_PSCI_0_2 feature set
> >       (and thus has already been initialized)
> >     - Returns the current PSCI version on GET_ONE_REG (defaulting to the
> > @@ -74,4 +74,63 @@ The following register is defined:
> >       KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED:
> >         The workaround is always active on this vCPU or it is not needed.
> >
> > -.. [1] https://developer.arm.com/-/media/developer/pdf/ARM_DEN_0070A_Firmware_interfaces_for_mitigating_CVE-2017-5715.pdf
> > +
> > +Bitmap Feature Firmware Registers
> > +---------------------------------
> > +
> > +Contrary to the above registers, the following registers exposes the hypercall
> > +services in the form of a feature-bitmap to the userspace. This bitmap is
> > +translated to the services that are available to the guest. There is a register
> > +defined per service call owner and can be accessed via GET/SET_ONE_REG interface.
> > +
> > +By default, these registers are set with the upper limit of the features that
> > +are supported. This way userspace can discover all the electable hypercall services
> > +via GET_ONE_REG. The user-space can write-back the desired bitmap back via
> > +SET_ONE_REG. The features for the registers that are untouched, probably because
> > +userspace isn't aware of them, will be exposed as is to the guest.
> > +
> > +Note that KVM would't allow the userspace to configure the registers anymore once
> > +any of the vCPUs has run at least once. Instead, it will return a -EBUSY. However,
> > +if there's no change in the incoming value, it simply returns a success.
> > +
>
> It would be better to replace "a success" with "zero", to be consistent
> with "-EBUSY". The suggestion may be invalid if the code needs changes
> based on Marc's suggestions.
>
Yes, I agree. Mentioning 'zero' makes more sense. However, I would be
scraping off this logic and return -EBUSY for all the writes after the
VM has started.

> > +The psuedo-firmware bitmap register are as follows:
> > +
> > +* KVM_REG_ARM_STD_BMAP:
> > +    Controls the bitmap of the ARM Standard Secure Service Calls.
> > +
> > +  The following bits are accepted:
> > +
> > +    Bit-0: KVM_REG_ARM_STD_BIT_TRNG_V1_0:
> > +      The bit represents the services offered under v1.0 of ARM True Random
> > +      Number Generator (TRNG) specification, ARM DEN0098.
> > +
> > +* KVM_REG_ARM_STD_HYP_BMAP:
> > +    Controls the bitmap of the ARM Standard Hypervisor Service Calls.
> > +
> > +  The following bits are accepted:
> > +
> > +    Bit-0: KVM_REG_ARM_STD_HYP_BIT_PV_TIME:
> > +      The bit represents the Paravirtualized Time service as represented by
> > +      ARM DEN0057A.
> > +
> > +* KVM_REG_ARM_VENDOR_HYP_BMAP:
> > +    Controls the bitmap of the Vendor specific Hypervisor Service Calls.
> > +
> > +  The following bits are accepted:
> > +
> > +    Bit-0: KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT
> > +      The bit represents the ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID
> > +      function-id
> > +
> > +    Bit-1: KVM_REG_ARM_VENDOR_HYP_BIT_PTP:
> > +      The bit represents the Precision Time Protocol KVM service.
> > +
> > +Errors:
> > +
> > +    =======  =============================================================
> > +    -ENOENT   Unknown register accessed.
> > +    -EBUSY    Attempt a 'write' to the register after the VM has started.
> > +    -EINVAL   Invalid bitmap written to the register.
> > +    =======  =============================================================
> > +
> > +.. [1] https://developer.arm.com/-/media/developer/pdf/ARM_DEN_0070A_Firmware_interfaces_for_mitigating_CVE-2017-5715.pdf
> > \ No newline at end of file
> >
>
> Thanks,
> Gavin
>
Regards,
Raghavendra

_______________________________________________
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] 96+ messages in thread

* Re: [PATCH v5 08/10] selftests: KVM: aarch64: Introduce hypercall ABI test
  2022-04-13  9:07     ` Gavin Shan
  (?)
@ 2022-04-13 17:32       ` Raghavendra Rao Ananta
  -1 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-13 17:32 UTC (permalink / raw)
  To: Gavin Shan, Oliver Upton
  Cc: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose, kvm, Catalin Marinas, Peter Shier,
	linux-kernel, Paolo Bonzini, Will Deacon, kvmarm,
	linux-arm-kernel

On Wed, Apr 13, 2022 at 2:07 AM Gavin Shan <gshan@redhat.com> wrote:
>
> Hi Raghavendra,
>
> On 4/7/22 9:16 AM, Raghavendra Rao Ananta wrote:
> > Introduce a KVM selftest to check the hypercall interface
> > for arm64 platforms. The test validates the user-space's
> > IOCTL interface to read/write the psuedo-firmware registers
> > as well as its effects on the guest upon certain configurations.
> >
> > Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
> > ---
> >   tools/testing/selftests/kvm/.gitignore        |   1 +
> >   tools/testing/selftests/kvm/Makefile          |   1 +
> >   .../selftests/kvm/aarch64/hypercalls.c        | 344 ++++++++++++++++++
> >   3 files changed, 346 insertions(+)
> >   create mode 100644 tools/testing/selftests/kvm/aarch64/hypercalls.c
> >
>
> To be more precise, s/IOCTL/{GET,SET}_ONE_REG ?
>
Sure, I think that'll be better.

> > diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore
> > index e82b816a6608..7ef52b3b1560 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/hypercalls
> >   /aarch64/psci_test
> >   /aarch64/vgic_init
> >   /aarch64/vgic_irq
> > diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
> > index 2f74f502de65..af4cb88bcf83 100644
> > --- a/tools/testing/selftests/kvm/Makefile
> > +++ b/tools/testing/selftests/kvm/Makefile
> > @@ -105,6 +105,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/hypercalls
> >   TEST_GEN_PROGS_aarch64 += aarch64/psci_test
> >   TEST_GEN_PROGS_aarch64 += aarch64/vgic_init
> >   TEST_GEN_PROGS_aarch64 += aarch64/vgic_irq
> > diff --git a/tools/testing/selftests/kvm/aarch64/hypercalls.c b/tools/testing/selftests/kvm/aarch64/hypercalls.c
> > new file mode 100644
> > index 000000000000..9941fb75772a
> > --- /dev/null
> > +++ b/tools/testing/selftests/kvm/aarch64/hypercalls.c
> > @@ -0,0 +1,344 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +
> > +/* hypercalls: Check the ARM64's psuedo-firmware bitmap register interface.
> > + *
> > + * The test validates the basic hypercall functionalities that are exposed
> > + * via the psuedo-firmware bitmap register. This includes the registers'
> > + * read/write behavior before and after the VM has started, and if the
> > + * hypercalls are properly masked or unmasked to the guest when disabled or
> > + * enabled from the KVM userspace, respectively.
> > + */
> > +
> > +#include <errno.h>
> > +#include <linux/arm-smccc.h>
> > +#include <asm/kvm.h>
> > +#include <kvm_util.h>
> > +
> > +#include "processor.h"
> > +
> > +#define FW_REG_ULIMIT_VAL(max_feat_bit) (GENMASK_ULL(max_feat_bit, 0))
> > +
> > +/* Last valid bits of the bitmapped firmware registers */
> > +#define KVM_REG_ARM_STD_BMAP_BIT_MAX         0
> > +#define KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX     0
> > +#define KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX  1
> > +
> > +struct kvm_fw_reg_info {
> > +     uint64_t reg;           /* Register definition */
> > +     uint64_t max_feat_bit;  /* Bit that represents the upper limit of the feature-map */
> > +};
> > +
> > +#define FW_REG_INFO(r, bit_max)                      \
> > +     {                                       \
> > +             .reg = r,                       \
> > +             .max_feat_bit = bit_max,        \
> > +     }
> > +
> > +static const struct kvm_fw_reg_info fw_reg_info[] = {
> > +     FW_REG_INFO(KVM_REG_ARM_STD_BMAP, KVM_REG_ARM_STD_BMAP_BIT_MAX),
> > +     FW_REG_INFO(KVM_REG_ARM_STD_HYP_BMAP, KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX),
> > +     FW_REG_INFO(KVM_REG_ARM_VENDOR_HYP_BMAP, KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX),
> > +};
> > +
>
> This can be simplifed by:
>
> #define FW_REG_INFO(r)                  \
>         { .reg = r,                     \
>           .max_feat_bit = r_##BIT_MAX,  \
>         }
>
> static const struct kvm_fw_reg_info fw_reg_info[] = {
>         FW_REG_INFO(KVM_REG_ARM_STD_BMAP),
>         FW_REG_INFO(KVM_REG_ARM_STD_HYP_BMAP),
>         FW_REG_INFO(KVM_REG_ARM_VENDOR_HYP_BMAP),
> };
>
Yes, probably that looks better. Thanks for the suggestion.

> > +enum test_stage {
> > +     TEST_STAGE_REG_IFACE,
> > +     TEST_STAGE_HVC_IFACE_FEAT_DISABLED,
> > +     TEST_STAGE_HVC_IFACE_FEAT_ENABLED,
> > +     TEST_STAGE_HVC_IFACE_FALSE_INFO,
> > +     TEST_STAGE_END,
> > +};
> > +
> > +static int stage;
> > +
>
> I think it'd better to initialize @stage to TEST_STAGE_REG_IFACE.
>
Will do.
> > +struct test_hvc_info {
> > +     uint32_t func_id;
> > +     int64_t arg0;
> > +};
> > +
> > +#define TEST_HVC_INFO(f, a0) \
> > +     {                       \
> > +             .func_id = f,   \
> > +             .arg0 = a0,     \
> > +     }
> > +
>
> According to those functions (smccc_get_{function, argx}()) defined
> in include/kvm/arm_hypercalls.h, @arg0 would have type of "uint64_t"
> if I'm correct. Besides, @func_id is arg0 and arg0 should be arg1?
> So if I'm correct, this would be:
>
> struct test_hvc_info {
>         uint32_t func_id;
>         uint64_t arg1
> };
>
Thanks for noticing this! I'll change it to 'unit64'. Regarding the
argument naming, I understand that it's a little confusing. I went
with 'arg0' to align with the selftest library's convention. But, I
agree that it's not aligned with what the kernel is used to.

Oliver, do you think we can start the argument naming from a1/arg1 in [1]?

> > +static const struct test_hvc_info hvc_info[] = {
> > +     /* KVM_REG_ARM_STD_BMAP */
> > +     TEST_HVC_INFO(ARM_SMCCC_TRNG_VERSION, 0),
> > +     TEST_HVC_INFO(ARM_SMCCC_TRNG_FEATURES, ARM_SMCCC_TRNG_RND64),
> > +     TEST_HVC_INFO(ARM_SMCCC_TRNG_GET_UUID, 0),
> > +     TEST_HVC_INFO(ARM_SMCCC_TRNG_RND32, 0),
> > +     TEST_HVC_INFO(ARM_SMCCC_TRNG_RND64, 0),
> > +
> > +     /* KVM_REG_ARM_STD_HYP_BMAP */
> > +     TEST_HVC_INFO(ARM_SMCCC_ARCH_FEATURES_FUNC_ID, ARM_SMCCC_HV_PV_TIME_FEATURES),
> > +     TEST_HVC_INFO(ARM_SMCCC_HV_PV_TIME_FEATURES, ARM_SMCCC_HV_PV_TIME_ST),
> > +     TEST_HVC_INFO(ARM_SMCCC_HV_PV_TIME_ST, 0),
> > +
> > +     /* KVM_REG_ARM_VENDOR_HYP_BMAP */
> > +     TEST_HVC_INFO(ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID,
> > +                     ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID),
> > +     TEST_HVC_INFO(ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID, KVM_PTP_VIRT_COUNTER),
> > +};
> > +
> > +/* Feed false hypercall info to test the KVM behavior */
> > +static const struct test_hvc_info false_hvc_info[] = {
> > +     /* Feature support check against a different family of hypercalls */
> > +     TEST_HVC_INFO(ARM_SMCCC_TRNG_FEATURES, ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID),
> > +     TEST_HVC_INFO(ARM_SMCCC_ARCH_FEATURES_FUNC_ID, ARM_SMCCC_TRNG_RND64),
> > +     TEST_HVC_INFO(ARM_SMCCC_HV_PV_TIME_FEATURES, ARM_SMCCC_TRNG_RND64),
> > +};
> > +
> > +static void guest_test_hvc(const struct test_hvc_info *hc_info)
> > +{
> > +     unsigned int i;
> > +     struct arm_smccc_res res;
> > +     unsigned int hvc_info_arr_sz;
> > +
> > +     hvc_info_arr_sz =
> > +     hc_info == hvc_info ? ARRAY_SIZE(hvc_info) : ARRAY_SIZE(false_hvc_info);
> > +
> > +     for (i = 0; i < hvc_info_arr_sz; i++, hc_info++) {
> > +
> > +             memset(&res, 0, sizeof(res));
> > +             smccc_hvc(hc_info->func_id, hc_info->arg0, 0, 0, 0, 0, 0, 0, &res);
> > +
>
> Unnecessary empty line before memset(). I don't find where smccc_hvc()
> is defined.
>
I can clear the line and for the definition of smccc_hvc(), I applied
Oliver's patch [1].

> > +             switch (stage) {
> > +             case TEST_STAGE_HVC_IFACE_FEAT_DISABLED:
> > +             case TEST_STAGE_HVC_IFACE_FALSE_INFO:
> > +                     GUEST_ASSERT_3(res.a0 == SMCCC_RET_NOT_SUPPORTED,
> > +                                     res.a0, hc_info->func_id, hc_info->arg0);
>                                         ^^
>
> It seems the code here isn't properly aligned. Maybe it's your
> preference :)
>
I think my editor is acting weird. I'll check again. Thanks for catching this!

> > +                     break;
> > +             case TEST_STAGE_HVC_IFACE_FEAT_ENABLED:
> > +                     GUEST_ASSERT_3(res.a0 != SMCCC_RET_NOT_SUPPORTED,
> > +                                     res.a0, hc_info->func_id, hc_info->arg0);
> > +                     break;
> > +             default:
> > +                     GUEST_ASSERT_1(0, stage);
> > +             }
> > +     }
> > +}
> > +
> > +static void guest_code(void)
> > +{
> > +     while (stage != TEST_STAGE_END) {
> > +             switch (stage) {
> > +             case TEST_STAGE_REG_IFACE:
> > +                     break;
> > +             case TEST_STAGE_HVC_IFACE_FEAT_DISABLED:
> > +             case TEST_STAGE_HVC_IFACE_FEAT_ENABLED:
> > +                     guest_test_hvc(hvc_info);
> > +                     break;
> > +             case TEST_STAGE_HVC_IFACE_FALSE_INFO:
> > +                     guest_test_hvc(false_hvc_info);
> > +                     break;
> > +             default:
> > +                     GUEST_ASSERT_1(0, stage);
> > +             }
> > +
> > +             GUEST_SYNC(stage);
> > +     }
> > +
> > +     GUEST_DONE();
> > +}
> > +
> > +static int set_fw_reg(struct kvm_vm *vm, uint64_t id, uint64_t val)
> > +{
> > +     struct kvm_one_reg reg = {
> > +             .id = id,
> > +             .addr = (uint64_t)&val,
> > +     };
> > +
> > +     return _vcpu_ioctl(vm, 0, KVM_SET_ONE_REG, &reg);
> > +}
> > +
> > +static void get_fw_reg(struct kvm_vm *vm, uint64_t id, uint64_t *addr)
> > +{
> > +     struct kvm_one_reg reg = {
> > +             .id = id,
> > +             .addr = (uint64_t)addr,
> > +     };
> > +
> > +     vcpu_ioctl(vm, 0, KVM_GET_ONE_REG, &reg);
> > +}
> > +
> > +struct st_time {
> > +     uint32_t rev;
> > +     uint32_t attr;
> > +     uint64_t st_time;
> > +};
> > +
> > +#define STEAL_TIME_SIZE              ((sizeof(struct st_time) + 63) & ~63)
> > +#define ST_GPA_BASE          (1 << 30)
> > +
> > +static void steal_time_init(struct kvm_vm *vm)
> > +{
> > +     uint64_t st_ipa = (ulong)ST_GPA_BASE;
> > +     unsigned int gpages;
> > +     struct kvm_device_attr dev = {
> > +             .group = KVM_ARM_VCPU_PVTIME_CTRL,
> > +             .attr = KVM_ARM_VCPU_PVTIME_IPA,
> > +             .addr = (uint64_t)&st_ipa,
> > +     };
> > +
> > +     gpages = vm_calc_num_guest_pages(VM_MODE_DEFAULT, STEAL_TIME_SIZE);
> > +     vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, ST_GPA_BASE, 1, gpages, 0);
> > +
> > +     vcpu_ioctl(vm, 0, KVM_SET_DEVICE_ATTR, &dev);
> > +}
> > +
>
> It might be helpful to do TEST_FAIL() on error returned from
> this vcpu_ioctl(), or skip those PVTIME SMCCC calls accordingly
> if the attribute isn't set successfully.
>
vcpu_ioctl() does a TEST_ASSERT() for us. Of course we can check it
ourselves and skip if needed, but don't you think that may go
unnoticed should any future changes tries to mess with
steal_time_init() incorrectly and we'd end up skipping the test
forever until we really notice skipped test?

> > +static void test_fw_regs_before_vm_start(struct kvm_vm *vm)
> > +{
> > +     uint64_t val;
> > +     unsigned int i;
> > +     int ret;
> > +
> > +     for (i = 0; i < ARRAY_SIZE(fw_reg_info); i++) {
> > +             const struct kvm_fw_reg_info *reg_info = &fw_reg_info[i];
> > +
> > +             /* First 'read' should be an upper limit of the features supported */
> > +             get_fw_reg(vm, reg_info->reg, &val);
> > +             TEST_ASSERT(val == FW_REG_ULIMIT_VAL(reg_info->max_feat_bit),
> > +                     "Expected all the features to be set for reg: 0x%lx; expected: 0x%llx; read: 0x%lx\n",
> > +                     reg_info->reg, GENMASK_ULL(reg_info->max_feat_bit, 0), val);
> > +
>
> s/GENMASK_ULL(...)/FW_REG_ULIMIT_VAL(...)
>
Right, that's better.

> > +             /* Test a 'write' by disabling all the features of the register map */
> > +             ret = set_fw_reg(vm, reg_info->reg, 0);
> > +             TEST_ASSERT(ret == 0,
> > +                     "Failed to clear all the features of reg: 0x%lx; ret: %d\n",
> > +                     reg_info->reg, errno);
> > +
> > +             get_fw_reg(vm, reg_info->reg, &val);
> > +             TEST_ASSERT(val == 0,
> > +                     "Expected all the features to be cleared for reg: 0x%lx\n", reg_info->reg);
> > +
> > +             /*
> > +              * Test enabling a feature that's not supported.
> > +              * Avoid this check if all the bits are occupied.
> > +              */
> > +             if (reg_info->max_feat_bit < 63) {
> > +                     ret = set_fw_reg(vm, reg_info->reg, BIT(reg_info->max_feat_bit + 1));
> > +                     TEST_ASSERT(ret != 0 && errno == EINVAL,
> > +                     "Unexpected behavior or return value (%d) while setting an unsupported feature for reg: 0x%lx\n",
> > +                     errno, reg_info->reg);
> > +             }
> > +     }
> > +}
> > +
> > +static void test_fw_regs_after_vm_start(struct kvm_vm *vm)
> > +{
> > +     uint64_t val;
> > +     unsigned int i;
> > +     int ret;
> > +
> > +     for (i = 0; i < ARRAY_SIZE(fw_reg_info); i++) {
> > +             const struct kvm_fw_reg_info *reg_info = &fw_reg_info[i];
> > +
> > +             /*
> > +              * Before starting the VM, the test clears all the bits.
> > +              * Check if that's still the case.
> > +              */
> > +             get_fw_reg(vm, reg_info->reg, &val);
> > +             TEST_ASSERT(val == 0,
> > +                     "Expected all the features to be cleared for reg: 0x%lx\n",
> > +                     reg_info->reg);
> > +
> > +             /*
> > +              * Test setting the last read value. KVM should allow this
> > +              * even if VM has started running.
> > +              */
> > +             ret = set_fw_reg(vm, reg_info->reg, val);
> > +             TEST_ASSERT(ret == 0,
> > +                     "Failed to set the register with previously read value after Vm start for reg: 0x%lx; ret: %d\n",
> > +                     reg_info->reg, errno);
> > +
> > +             /*
> > +              * Set all the features for this register again. KVM shouldn't
> > +              * allow this as the VM is running.
> > +              */
> > +             ret = set_fw_reg(vm, reg_info->reg, FW_REG_ULIMIT_VAL(reg_info->max_feat_bit));
> > +             TEST_ASSERT(ret != 0 && errno == EBUSY,
> > +             "Unexpected behavior or return value (%d) while setting a feature while VM is running for reg: 0x%lx\n",
> > +             errno, reg_info->reg);
> > +     }
> > +}
> > +
> > +static struct kvm_vm *test_vm_create(void)
> > +{
> > +     struct kvm_vm *vm;
> > +
> > +     vm = vm_create_default(0, 0, guest_code);
> > +
> > +     ucall_init(vm, NULL);
> > +     steal_time_init(vm);
> > +
> > +     return vm;
> > +}
> > +
> > +static struct kvm_vm *test_guest_stage(struct kvm_vm *vm)
> > +{
> > +     struct kvm_vm *ret_vm = vm;
> > +
> > +     pr_debug("Stage: %d\n", stage);
> > +
> > +     switch (stage) {
> > +     case TEST_STAGE_REG_IFACE:
> > +             test_fw_regs_after_vm_start(vm);
> > +             break;
> > +     case TEST_STAGE_HVC_IFACE_FEAT_DISABLED:
> > +             /* Start a new VM so that all the features are now enabled by default */
> > +             kvm_vm_free(vm);
> > +             ret_vm = test_vm_create();
> > +             break;
> > +     case TEST_STAGE_HVC_IFACE_FEAT_ENABLED:
> > +     case TEST_STAGE_HVC_IFACE_FALSE_INFO:
> > +             break;
> > +     default:
> > +             TEST_FAIL("Unknown test stage: %d\n", stage);
> > +     }
> > +
> > +     stage++;
> > +     sync_global_to_guest(vm, stage);
> > +
> > +     return ret_vm;
> > +}
> > +
> > +static void test_run(void)
> > +{
> > +     struct kvm_vm *vm;
> > +     struct ucall uc;
> > +     bool guest_done = false;
> > +
> > +     vm = test_vm_create();
> > +
> > +     test_fw_regs_before_vm_start(vm);
> > +
> > +     while (!guest_done) {
> > +             vcpu_run(vm, 0);
> > +
> > +             switch (get_ucall(vm, 0, &uc)) {
> > +             case UCALL_SYNC:
> > +                     vm = test_guest_stage(vm);
> > +                     break;
> > +             case UCALL_DONE:
> > +                     guest_done = true;
> > +                     break;
> > +             case UCALL_ABORT:
> > +                     TEST_FAIL("%s at %s:%ld\n\tvalues: 0x%lx, 0x%lx; 0x%lx, stage: %u",
> > +                     (const char *)uc.args[0], __FILE__, uc.args[1],
> > +                     uc.args[2], uc.args[3], uc.args[4], stage);
> > +                     break;
> > +             default:
> > +                     TEST_FAIL("Unexpected guest exit\n");
> > +             }
> > +     }
> > +
> > +     kvm_vm_free(vm);
> > +}
> > +
> > +int main(void)
> > +{
> > +     setbuf(stdout, NULL);
> > +
> > +     test_run();
> > +     return 0;
> > +}
> >
>
> Thanks,
> Gavin
>

Thanks for taking the time to review.

Regards,
Raghavendra

[1]: https://lore.kernel.org/kvmarm/20220409184549.1681189-11-oupton@google.com/T/#u

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

* Re: [PATCH v5 08/10] selftests: KVM: aarch64: Introduce hypercall ABI test
@ 2022-04-13 17:32       ` Raghavendra Rao Ananta
  0 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-13 17:32 UTC (permalink / raw)
  To: Gavin Shan, Oliver Upton
  Cc: kvm, Marc Zyngier, Peter Shier, linux-kernel, Will Deacon,
	Catalin Marinas, Paolo Bonzini, kvmarm, linux-arm-kernel

On Wed, Apr 13, 2022 at 2:07 AM Gavin Shan <gshan@redhat.com> wrote:
>
> Hi Raghavendra,
>
> On 4/7/22 9:16 AM, Raghavendra Rao Ananta wrote:
> > Introduce a KVM selftest to check the hypercall interface
> > for arm64 platforms. The test validates the user-space's
> > IOCTL interface to read/write the psuedo-firmware registers
> > as well as its effects on the guest upon certain configurations.
> >
> > Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
> > ---
> >   tools/testing/selftests/kvm/.gitignore        |   1 +
> >   tools/testing/selftests/kvm/Makefile          |   1 +
> >   .../selftests/kvm/aarch64/hypercalls.c        | 344 ++++++++++++++++++
> >   3 files changed, 346 insertions(+)
> >   create mode 100644 tools/testing/selftests/kvm/aarch64/hypercalls.c
> >
>
> To be more precise, s/IOCTL/{GET,SET}_ONE_REG ?
>
Sure, I think that'll be better.

> > diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore
> > index e82b816a6608..7ef52b3b1560 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/hypercalls
> >   /aarch64/psci_test
> >   /aarch64/vgic_init
> >   /aarch64/vgic_irq
> > diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
> > index 2f74f502de65..af4cb88bcf83 100644
> > --- a/tools/testing/selftests/kvm/Makefile
> > +++ b/tools/testing/selftests/kvm/Makefile
> > @@ -105,6 +105,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/hypercalls
> >   TEST_GEN_PROGS_aarch64 += aarch64/psci_test
> >   TEST_GEN_PROGS_aarch64 += aarch64/vgic_init
> >   TEST_GEN_PROGS_aarch64 += aarch64/vgic_irq
> > diff --git a/tools/testing/selftests/kvm/aarch64/hypercalls.c b/tools/testing/selftests/kvm/aarch64/hypercalls.c
> > new file mode 100644
> > index 000000000000..9941fb75772a
> > --- /dev/null
> > +++ b/tools/testing/selftests/kvm/aarch64/hypercalls.c
> > @@ -0,0 +1,344 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +
> > +/* hypercalls: Check the ARM64's psuedo-firmware bitmap register interface.
> > + *
> > + * The test validates the basic hypercall functionalities that are exposed
> > + * via the psuedo-firmware bitmap register. This includes the registers'
> > + * read/write behavior before and after the VM has started, and if the
> > + * hypercalls are properly masked or unmasked to the guest when disabled or
> > + * enabled from the KVM userspace, respectively.
> > + */
> > +
> > +#include <errno.h>
> > +#include <linux/arm-smccc.h>
> > +#include <asm/kvm.h>
> > +#include <kvm_util.h>
> > +
> > +#include "processor.h"
> > +
> > +#define FW_REG_ULIMIT_VAL(max_feat_bit) (GENMASK_ULL(max_feat_bit, 0))
> > +
> > +/* Last valid bits of the bitmapped firmware registers */
> > +#define KVM_REG_ARM_STD_BMAP_BIT_MAX         0
> > +#define KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX     0
> > +#define KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX  1
> > +
> > +struct kvm_fw_reg_info {
> > +     uint64_t reg;           /* Register definition */
> > +     uint64_t max_feat_bit;  /* Bit that represents the upper limit of the feature-map */
> > +};
> > +
> > +#define FW_REG_INFO(r, bit_max)                      \
> > +     {                                       \
> > +             .reg = r,                       \
> > +             .max_feat_bit = bit_max,        \
> > +     }
> > +
> > +static const struct kvm_fw_reg_info fw_reg_info[] = {
> > +     FW_REG_INFO(KVM_REG_ARM_STD_BMAP, KVM_REG_ARM_STD_BMAP_BIT_MAX),
> > +     FW_REG_INFO(KVM_REG_ARM_STD_HYP_BMAP, KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX),
> > +     FW_REG_INFO(KVM_REG_ARM_VENDOR_HYP_BMAP, KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX),
> > +};
> > +
>
> This can be simplifed by:
>
> #define FW_REG_INFO(r)                  \
>         { .reg = r,                     \
>           .max_feat_bit = r_##BIT_MAX,  \
>         }
>
> static const struct kvm_fw_reg_info fw_reg_info[] = {
>         FW_REG_INFO(KVM_REG_ARM_STD_BMAP),
>         FW_REG_INFO(KVM_REG_ARM_STD_HYP_BMAP),
>         FW_REG_INFO(KVM_REG_ARM_VENDOR_HYP_BMAP),
> };
>
Yes, probably that looks better. Thanks for the suggestion.

> > +enum test_stage {
> > +     TEST_STAGE_REG_IFACE,
> > +     TEST_STAGE_HVC_IFACE_FEAT_DISABLED,
> > +     TEST_STAGE_HVC_IFACE_FEAT_ENABLED,
> > +     TEST_STAGE_HVC_IFACE_FALSE_INFO,
> > +     TEST_STAGE_END,
> > +};
> > +
> > +static int stage;
> > +
>
> I think it'd better to initialize @stage to TEST_STAGE_REG_IFACE.
>
Will do.
> > +struct test_hvc_info {
> > +     uint32_t func_id;
> > +     int64_t arg0;
> > +};
> > +
> > +#define TEST_HVC_INFO(f, a0) \
> > +     {                       \
> > +             .func_id = f,   \
> > +             .arg0 = a0,     \
> > +     }
> > +
>
> According to those functions (smccc_get_{function, argx}()) defined
> in include/kvm/arm_hypercalls.h, @arg0 would have type of "uint64_t"
> if I'm correct. Besides, @func_id is arg0 and arg0 should be arg1?
> So if I'm correct, this would be:
>
> struct test_hvc_info {
>         uint32_t func_id;
>         uint64_t arg1
> };
>
Thanks for noticing this! I'll change it to 'unit64'. Regarding the
argument naming, I understand that it's a little confusing. I went
with 'arg0' to align with the selftest library's convention. But, I
agree that it's not aligned with what the kernel is used to.

Oliver, do you think we can start the argument naming from a1/arg1 in [1]?

> > +static const struct test_hvc_info hvc_info[] = {
> > +     /* KVM_REG_ARM_STD_BMAP */
> > +     TEST_HVC_INFO(ARM_SMCCC_TRNG_VERSION, 0),
> > +     TEST_HVC_INFO(ARM_SMCCC_TRNG_FEATURES, ARM_SMCCC_TRNG_RND64),
> > +     TEST_HVC_INFO(ARM_SMCCC_TRNG_GET_UUID, 0),
> > +     TEST_HVC_INFO(ARM_SMCCC_TRNG_RND32, 0),
> > +     TEST_HVC_INFO(ARM_SMCCC_TRNG_RND64, 0),
> > +
> > +     /* KVM_REG_ARM_STD_HYP_BMAP */
> > +     TEST_HVC_INFO(ARM_SMCCC_ARCH_FEATURES_FUNC_ID, ARM_SMCCC_HV_PV_TIME_FEATURES),
> > +     TEST_HVC_INFO(ARM_SMCCC_HV_PV_TIME_FEATURES, ARM_SMCCC_HV_PV_TIME_ST),
> > +     TEST_HVC_INFO(ARM_SMCCC_HV_PV_TIME_ST, 0),
> > +
> > +     /* KVM_REG_ARM_VENDOR_HYP_BMAP */
> > +     TEST_HVC_INFO(ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID,
> > +                     ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID),
> > +     TEST_HVC_INFO(ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID, KVM_PTP_VIRT_COUNTER),
> > +};
> > +
> > +/* Feed false hypercall info to test the KVM behavior */
> > +static const struct test_hvc_info false_hvc_info[] = {
> > +     /* Feature support check against a different family of hypercalls */
> > +     TEST_HVC_INFO(ARM_SMCCC_TRNG_FEATURES, ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID),
> > +     TEST_HVC_INFO(ARM_SMCCC_ARCH_FEATURES_FUNC_ID, ARM_SMCCC_TRNG_RND64),
> > +     TEST_HVC_INFO(ARM_SMCCC_HV_PV_TIME_FEATURES, ARM_SMCCC_TRNG_RND64),
> > +};
> > +
> > +static void guest_test_hvc(const struct test_hvc_info *hc_info)
> > +{
> > +     unsigned int i;
> > +     struct arm_smccc_res res;
> > +     unsigned int hvc_info_arr_sz;
> > +
> > +     hvc_info_arr_sz =
> > +     hc_info == hvc_info ? ARRAY_SIZE(hvc_info) : ARRAY_SIZE(false_hvc_info);
> > +
> > +     for (i = 0; i < hvc_info_arr_sz; i++, hc_info++) {
> > +
> > +             memset(&res, 0, sizeof(res));
> > +             smccc_hvc(hc_info->func_id, hc_info->arg0, 0, 0, 0, 0, 0, 0, &res);
> > +
>
> Unnecessary empty line before memset(). I don't find where smccc_hvc()
> is defined.
>
I can clear the line and for the definition of smccc_hvc(), I applied
Oliver's patch [1].

> > +             switch (stage) {
> > +             case TEST_STAGE_HVC_IFACE_FEAT_DISABLED:
> > +             case TEST_STAGE_HVC_IFACE_FALSE_INFO:
> > +                     GUEST_ASSERT_3(res.a0 == SMCCC_RET_NOT_SUPPORTED,
> > +                                     res.a0, hc_info->func_id, hc_info->arg0);
>                                         ^^
>
> It seems the code here isn't properly aligned. Maybe it's your
> preference :)
>
I think my editor is acting weird. I'll check again. Thanks for catching this!

> > +                     break;
> > +             case TEST_STAGE_HVC_IFACE_FEAT_ENABLED:
> > +                     GUEST_ASSERT_3(res.a0 != SMCCC_RET_NOT_SUPPORTED,
> > +                                     res.a0, hc_info->func_id, hc_info->arg0);
> > +                     break;
> > +             default:
> > +                     GUEST_ASSERT_1(0, stage);
> > +             }
> > +     }
> > +}
> > +
> > +static void guest_code(void)
> > +{
> > +     while (stage != TEST_STAGE_END) {
> > +             switch (stage) {
> > +             case TEST_STAGE_REG_IFACE:
> > +                     break;
> > +             case TEST_STAGE_HVC_IFACE_FEAT_DISABLED:
> > +             case TEST_STAGE_HVC_IFACE_FEAT_ENABLED:
> > +                     guest_test_hvc(hvc_info);
> > +                     break;
> > +             case TEST_STAGE_HVC_IFACE_FALSE_INFO:
> > +                     guest_test_hvc(false_hvc_info);
> > +                     break;
> > +             default:
> > +                     GUEST_ASSERT_1(0, stage);
> > +             }
> > +
> > +             GUEST_SYNC(stage);
> > +     }
> > +
> > +     GUEST_DONE();
> > +}
> > +
> > +static int set_fw_reg(struct kvm_vm *vm, uint64_t id, uint64_t val)
> > +{
> > +     struct kvm_one_reg reg = {
> > +             .id = id,
> > +             .addr = (uint64_t)&val,
> > +     };
> > +
> > +     return _vcpu_ioctl(vm, 0, KVM_SET_ONE_REG, &reg);
> > +}
> > +
> > +static void get_fw_reg(struct kvm_vm *vm, uint64_t id, uint64_t *addr)
> > +{
> > +     struct kvm_one_reg reg = {
> > +             .id = id,
> > +             .addr = (uint64_t)addr,
> > +     };
> > +
> > +     vcpu_ioctl(vm, 0, KVM_GET_ONE_REG, &reg);
> > +}
> > +
> > +struct st_time {
> > +     uint32_t rev;
> > +     uint32_t attr;
> > +     uint64_t st_time;
> > +};
> > +
> > +#define STEAL_TIME_SIZE              ((sizeof(struct st_time) + 63) & ~63)
> > +#define ST_GPA_BASE          (1 << 30)
> > +
> > +static void steal_time_init(struct kvm_vm *vm)
> > +{
> > +     uint64_t st_ipa = (ulong)ST_GPA_BASE;
> > +     unsigned int gpages;
> > +     struct kvm_device_attr dev = {
> > +             .group = KVM_ARM_VCPU_PVTIME_CTRL,
> > +             .attr = KVM_ARM_VCPU_PVTIME_IPA,
> > +             .addr = (uint64_t)&st_ipa,
> > +     };
> > +
> > +     gpages = vm_calc_num_guest_pages(VM_MODE_DEFAULT, STEAL_TIME_SIZE);
> > +     vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, ST_GPA_BASE, 1, gpages, 0);
> > +
> > +     vcpu_ioctl(vm, 0, KVM_SET_DEVICE_ATTR, &dev);
> > +}
> > +
>
> It might be helpful to do TEST_FAIL() on error returned from
> this vcpu_ioctl(), or skip those PVTIME SMCCC calls accordingly
> if the attribute isn't set successfully.
>
vcpu_ioctl() does a TEST_ASSERT() for us. Of course we can check it
ourselves and skip if needed, but don't you think that may go
unnoticed should any future changes tries to mess with
steal_time_init() incorrectly and we'd end up skipping the test
forever until we really notice skipped test?

> > +static void test_fw_regs_before_vm_start(struct kvm_vm *vm)
> > +{
> > +     uint64_t val;
> > +     unsigned int i;
> > +     int ret;
> > +
> > +     for (i = 0; i < ARRAY_SIZE(fw_reg_info); i++) {
> > +             const struct kvm_fw_reg_info *reg_info = &fw_reg_info[i];
> > +
> > +             /* First 'read' should be an upper limit of the features supported */
> > +             get_fw_reg(vm, reg_info->reg, &val);
> > +             TEST_ASSERT(val == FW_REG_ULIMIT_VAL(reg_info->max_feat_bit),
> > +                     "Expected all the features to be set for reg: 0x%lx; expected: 0x%llx; read: 0x%lx\n",
> > +                     reg_info->reg, GENMASK_ULL(reg_info->max_feat_bit, 0), val);
> > +
>
> s/GENMASK_ULL(...)/FW_REG_ULIMIT_VAL(...)
>
Right, that's better.

> > +             /* Test a 'write' by disabling all the features of the register map */
> > +             ret = set_fw_reg(vm, reg_info->reg, 0);
> > +             TEST_ASSERT(ret == 0,
> > +                     "Failed to clear all the features of reg: 0x%lx; ret: %d\n",
> > +                     reg_info->reg, errno);
> > +
> > +             get_fw_reg(vm, reg_info->reg, &val);
> > +             TEST_ASSERT(val == 0,
> > +                     "Expected all the features to be cleared for reg: 0x%lx\n", reg_info->reg);
> > +
> > +             /*
> > +              * Test enabling a feature that's not supported.
> > +              * Avoid this check if all the bits are occupied.
> > +              */
> > +             if (reg_info->max_feat_bit < 63) {
> > +                     ret = set_fw_reg(vm, reg_info->reg, BIT(reg_info->max_feat_bit + 1));
> > +                     TEST_ASSERT(ret != 0 && errno == EINVAL,
> > +                     "Unexpected behavior or return value (%d) while setting an unsupported feature for reg: 0x%lx\n",
> > +                     errno, reg_info->reg);
> > +             }
> > +     }
> > +}
> > +
> > +static void test_fw_regs_after_vm_start(struct kvm_vm *vm)
> > +{
> > +     uint64_t val;
> > +     unsigned int i;
> > +     int ret;
> > +
> > +     for (i = 0; i < ARRAY_SIZE(fw_reg_info); i++) {
> > +             const struct kvm_fw_reg_info *reg_info = &fw_reg_info[i];
> > +
> > +             /*
> > +              * Before starting the VM, the test clears all the bits.
> > +              * Check if that's still the case.
> > +              */
> > +             get_fw_reg(vm, reg_info->reg, &val);
> > +             TEST_ASSERT(val == 0,
> > +                     "Expected all the features to be cleared for reg: 0x%lx\n",
> > +                     reg_info->reg);
> > +
> > +             /*
> > +              * Test setting the last read value. KVM should allow this
> > +              * even if VM has started running.
> > +              */
> > +             ret = set_fw_reg(vm, reg_info->reg, val);
> > +             TEST_ASSERT(ret == 0,
> > +                     "Failed to set the register with previously read value after Vm start for reg: 0x%lx; ret: %d\n",
> > +                     reg_info->reg, errno);
> > +
> > +             /*
> > +              * Set all the features for this register again. KVM shouldn't
> > +              * allow this as the VM is running.
> > +              */
> > +             ret = set_fw_reg(vm, reg_info->reg, FW_REG_ULIMIT_VAL(reg_info->max_feat_bit));
> > +             TEST_ASSERT(ret != 0 && errno == EBUSY,
> > +             "Unexpected behavior or return value (%d) while setting a feature while VM is running for reg: 0x%lx\n",
> > +             errno, reg_info->reg);
> > +     }
> > +}
> > +
> > +static struct kvm_vm *test_vm_create(void)
> > +{
> > +     struct kvm_vm *vm;
> > +
> > +     vm = vm_create_default(0, 0, guest_code);
> > +
> > +     ucall_init(vm, NULL);
> > +     steal_time_init(vm);
> > +
> > +     return vm;
> > +}
> > +
> > +static struct kvm_vm *test_guest_stage(struct kvm_vm *vm)
> > +{
> > +     struct kvm_vm *ret_vm = vm;
> > +
> > +     pr_debug("Stage: %d\n", stage);
> > +
> > +     switch (stage) {
> > +     case TEST_STAGE_REG_IFACE:
> > +             test_fw_regs_after_vm_start(vm);
> > +             break;
> > +     case TEST_STAGE_HVC_IFACE_FEAT_DISABLED:
> > +             /* Start a new VM so that all the features are now enabled by default */
> > +             kvm_vm_free(vm);
> > +             ret_vm = test_vm_create();
> > +             break;
> > +     case TEST_STAGE_HVC_IFACE_FEAT_ENABLED:
> > +     case TEST_STAGE_HVC_IFACE_FALSE_INFO:
> > +             break;
> > +     default:
> > +             TEST_FAIL("Unknown test stage: %d\n", stage);
> > +     }
> > +
> > +     stage++;
> > +     sync_global_to_guest(vm, stage);
> > +
> > +     return ret_vm;
> > +}
> > +
> > +static void test_run(void)
> > +{
> > +     struct kvm_vm *vm;
> > +     struct ucall uc;
> > +     bool guest_done = false;
> > +
> > +     vm = test_vm_create();
> > +
> > +     test_fw_regs_before_vm_start(vm);
> > +
> > +     while (!guest_done) {
> > +             vcpu_run(vm, 0);
> > +
> > +             switch (get_ucall(vm, 0, &uc)) {
> > +             case UCALL_SYNC:
> > +                     vm = test_guest_stage(vm);
> > +                     break;
> > +             case UCALL_DONE:
> > +                     guest_done = true;
> > +                     break;
> > +             case UCALL_ABORT:
> > +                     TEST_FAIL("%s at %s:%ld\n\tvalues: 0x%lx, 0x%lx; 0x%lx, stage: %u",
> > +                     (const char *)uc.args[0], __FILE__, uc.args[1],
> > +                     uc.args[2], uc.args[3], uc.args[4], stage);
> > +                     break;
> > +             default:
> > +                     TEST_FAIL("Unexpected guest exit\n");
> > +             }
> > +     }
> > +
> > +     kvm_vm_free(vm);
> > +}
> > +
> > +int main(void)
> > +{
> > +     setbuf(stdout, NULL);
> > +
> > +     test_run();
> > +     return 0;
> > +}
> >
>
> Thanks,
> Gavin
>

Thanks for taking the time to review.

Regards,
Raghavendra

[1]: https://lore.kernel.org/kvmarm/20220409184549.1681189-11-oupton@google.com/T/#u
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

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

* Re: [PATCH v5 08/10] selftests: KVM: aarch64: Introduce hypercall ABI test
@ 2022-04-13 17:32       ` Raghavendra Rao Ananta
  0 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-13 17:32 UTC (permalink / raw)
  To: Gavin Shan, Oliver Upton
  Cc: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose, kvm, Catalin Marinas, Peter Shier,
	linux-kernel, Paolo Bonzini, Will Deacon, kvmarm,
	linux-arm-kernel

On Wed, Apr 13, 2022 at 2:07 AM Gavin Shan <gshan@redhat.com> wrote:
>
> Hi Raghavendra,
>
> On 4/7/22 9:16 AM, Raghavendra Rao Ananta wrote:
> > Introduce a KVM selftest to check the hypercall interface
> > for arm64 platforms. The test validates the user-space's
> > IOCTL interface to read/write the psuedo-firmware registers
> > as well as its effects on the guest upon certain configurations.
> >
> > Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
> > ---
> >   tools/testing/selftests/kvm/.gitignore        |   1 +
> >   tools/testing/selftests/kvm/Makefile          |   1 +
> >   .../selftests/kvm/aarch64/hypercalls.c        | 344 ++++++++++++++++++
> >   3 files changed, 346 insertions(+)
> >   create mode 100644 tools/testing/selftests/kvm/aarch64/hypercalls.c
> >
>
> To be more precise, s/IOCTL/{GET,SET}_ONE_REG ?
>
Sure, I think that'll be better.

> > diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore
> > index e82b816a6608..7ef52b3b1560 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/hypercalls
> >   /aarch64/psci_test
> >   /aarch64/vgic_init
> >   /aarch64/vgic_irq
> > diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
> > index 2f74f502de65..af4cb88bcf83 100644
> > --- a/tools/testing/selftests/kvm/Makefile
> > +++ b/tools/testing/selftests/kvm/Makefile
> > @@ -105,6 +105,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/hypercalls
> >   TEST_GEN_PROGS_aarch64 += aarch64/psci_test
> >   TEST_GEN_PROGS_aarch64 += aarch64/vgic_init
> >   TEST_GEN_PROGS_aarch64 += aarch64/vgic_irq
> > diff --git a/tools/testing/selftests/kvm/aarch64/hypercalls.c b/tools/testing/selftests/kvm/aarch64/hypercalls.c
> > new file mode 100644
> > index 000000000000..9941fb75772a
> > --- /dev/null
> > +++ b/tools/testing/selftests/kvm/aarch64/hypercalls.c
> > @@ -0,0 +1,344 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +
> > +/* hypercalls: Check the ARM64's psuedo-firmware bitmap register interface.
> > + *
> > + * The test validates the basic hypercall functionalities that are exposed
> > + * via the psuedo-firmware bitmap register. This includes the registers'
> > + * read/write behavior before and after the VM has started, and if the
> > + * hypercalls are properly masked or unmasked to the guest when disabled or
> > + * enabled from the KVM userspace, respectively.
> > + */
> > +
> > +#include <errno.h>
> > +#include <linux/arm-smccc.h>
> > +#include <asm/kvm.h>
> > +#include <kvm_util.h>
> > +
> > +#include "processor.h"
> > +
> > +#define FW_REG_ULIMIT_VAL(max_feat_bit) (GENMASK_ULL(max_feat_bit, 0))
> > +
> > +/* Last valid bits of the bitmapped firmware registers */
> > +#define KVM_REG_ARM_STD_BMAP_BIT_MAX         0
> > +#define KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX     0
> > +#define KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX  1
> > +
> > +struct kvm_fw_reg_info {
> > +     uint64_t reg;           /* Register definition */
> > +     uint64_t max_feat_bit;  /* Bit that represents the upper limit of the feature-map */
> > +};
> > +
> > +#define FW_REG_INFO(r, bit_max)                      \
> > +     {                                       \
> > +             .reg = r,                       \
> > +             .max_feat_bit = bit_max,        \
> > +     }
> > +
> > +static const struct kvm_fw_reg_info fw_reg_info[] = {
> > +     FW_REG_INFO(KVM_REG_ARM_STD_BMAP, KVM_REG_ARM_STD_BMAP_BIT_MAX),
> > +     FW_REG_INFO(KVM_REG_ARM_STD_HYP_BMAP, KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX),
> > +     FW_REG_INFO(KVM_REG_ARM_VENDOR_HYP_BMAP, KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX),
> > +};
> > +
>
> This can be simplifed by:
>
> #define FW_REG_INFO(r)                  \
>         { .reg = r,                     \
>           .max_feat_bit = r_##BIT_MAX,  \
>         }
>
> static const struct kvm_fw_reg_info fw_reg_info[] = {
>         FW_REG_INFO(KVM_REG_ARM_STD_BMAP),
>         FW_REG_INFO(KVM_REG_ARM_STD_HYP_BMAP),
>         FW_REG_INFO(KVM_REG_ARM_VENDOR_HYP_BMAP),
> };
>
Yes, probably that looks better. Thanks for the suggestion.

> > +enum test_stage {
> > +     TEST_STAGE_REG_IFACE,
> > +     TEST_STAGE_HVC_IFACE_FEAT_DISABLED,
> > +     TEST_STAGE_HVC_IFACE_FEAT_ENABLED,
> > +     TEST_STAGE_HVC_IFACE_FALSE_INFO,
> > +     TEST_STAGE_END,
> > +};
> > +
> > +static int stage;
> > +
>
> I think it'd better to initialize @stage to TEST_STAGE_REG_IFACE.
>
Will do.
> > +struct test_hvc_info {
> > +     uint32_t func_id;
> > +     int64_t arg0;
> > +};
> > +
> > +#define TEST_HVC_INFO(f, a0) \
> > +     {                       \
> > +             .func_id = f,   \
> > +             .arg0 = a0,     \
> > +     }
> > +
>
> According to those functions (smccc_get_{function, argx}()) defined
> in include/kvm/arm_hypercalls.h, @arg0 would have type of "uint64_t"
> if I'm correct. Besides, @func_id is arg0 and arg0 should be arg1?
> So if I'm correct, this would be:
>
> struct test_hvc_info {
>         uint32_t func_id;
>         uint64_t arg1
> };
>
Thanks for noticing this! I'll change it to 'unit64'. Regarding the
argument naming, I understand that it's a little confusing. I went
with 'arg0' to align with the selftest library's convention. But, I
agree that it's not aligned with what the kernel is used to.

Oliver, do you think we can start the argument naming from a1/arg1 in [1]?

> > +static const struct test_hvc_info hvc_info[] = {
> > +     /* KVM_REG_ARM_STD_BMAP */
> > +     TEST_HVC_INFO(ARM_SMCCC_TRNG_VERSION, 0),
> > +     TEST_HVC_INFO(ARM_SMCCC_TRNG_FEATURES, ARM_SMCCC_TRNG_RND64),
> > +     TEST_HVC_INFO(ARM_SMCCC_TRNG_GET_UUID, 0),
> > +     TEST_HVC_INFO(ARM_SMCCC_TRNG_RND32, 0),
> > +     TEST_HVC_INFO(ARM_SMCCC_TRNG_RND64, 0),
> > +
> > +     /* KVM_REG_ARM_STD_HYP_BMAP */
> > +     TEST_HVC_INFO(ARM_SMCCC_ARCH_FEATURES_FUNC_ID, ARM_SMCCC_HV_PV_TIME_FEATURES),
> > +     TEST_HVC_INFO(ARM_SMCCC_HV_PV_TIME_FEATURES, ARM_SMCCC_HV_PV_TIME_ST),
> > +     TEST_HVC_INFO(ARM_SMCCC_HV_PV_TIME_ST, 0),
> > +
> > +     /* KVM_REG_ARM_VENDOR_HYP_BMAP */
> > +     TEST_HVC_INFO(ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID,
> > +                     ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID),
> > +     TEST_HVC_INFO(ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID, KVM_PTP_VIRT_COUNTER),
> > +};
> > +
> > +/* Feed false hypercall info to test the KVM behavior */
> > +static const struct test_hvc_info false_hvc_info[] = {
> > +     /* Feature support check against a different family of hypercalls */
> > +     TEST_HVC_INFO(ARM_SMCCC_TRNG_FEATURES, ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID),
> > +     TEST_HVC_INFO(ARM_SMCCC_ARCH_FEATURES_FUNC_ID, ARM_SMCCC_TRNG_RND64),
> > +     TEST_HVC_INFO(ARM_SMCCC_HV_PV_TIME_FEATURES, ARM_SMCCC_TRNG_RND64),
> > +};
> > +
> > +static void guest_test_hvc(const struct test_hvc_info *hc_info)
> > +{
> > +     unsigned int i;
> > +     struct arm_smccc_res res;
> > +     unsigned int hvc_info_arr_sz;
> > +
> > +     hvc_info_arr_sz =
> > +     hc_info == hvc_info ? ARRAY_SIZE(hvc_info) : ARRAY_SIZE(false_hvc_info);
> > +
> > +     for (i = 0; i < hvc_info_arr_sz; i++, hc_info++) {
> > +
> > +             memset(&res, 0, sizeof(res));
> > +             smccc_hvc(hc_info->func_id, hc_info->arg0, 0, 0, 0, 0, 0, 0, &res);
> > +
>
> Unnecessary empty line before memset(). I don't find where smccc_hvc()
> is defined.
>
I can clear the line and for the definition of smccc_hvc(), I applied
Oliver's patch [1].

> > +             switch (stage) {
> > +             case TEST_STAGE_HVC_IFACE_FEAT_DISABLED:
> > +             case TEST_STAGE_HVC_IFACE_FALSE_INFO:
> > +                     GUEST_ASSERT_3(res.a0 == SMCCC_RET_NOT_SUPPORTED,
> > +                                     res.a0, hc_info->func_id, hc_info->arg0);
>                                         ^^
>
> It seems the code here isn't properly aligned. Maybe it's your
> preference :)
>
I think my editor is acting weird. I'll check again. Thanks for catching this!

> > +                     break;
> > +             case TEST_STAGE_HVC_IFACE_FEAT_ENABLED:
> > +                     GUEST_ASSERT_3(res.a0 != SMCCC_RET_NOT_SUPPORTED,
> > +                                     res.a0, hc_info->func_id, hc_info->arg0);
> > +                     break;
> > +             default:
> > +                     GUEST_ASSERT_1(0, stage);
> > +             }
> > +     }
> > +}
> > +
> > +static void guest_code(void)
> > +{
> > +     while (stage != TEST_STAGE_END) {
> > +             switch (stage) {
> > +             case TEST_STAGE_REG_IFACE:
> > +                     break;
> > +             case TEST_STAGE_HVC_IFACE_FEAT_DISABLED:
> > +             case TEST_STAGE_HVC_IFACE_FEAT_ENABLED:
> > +                     guest_test_hvc(hvc_info);
> > +                     break;
> > +             case TEST_STAGE_HVC_IFACE_FALSE_INFO:
> > +                     guest_test_hvc(false_hvc_info);
> > +                     break;
> > +             default:
> > +                     GUEST_ASSERT_1(0, stage);
> > +             }
> > +
> > +             GUEST_SYNC(stage);
> > +     }
> > +
> > +     GUEST_DONE();
> > +}
> > +
> > +static int set_fw_reg(struct kvm_vm *vm, uint64_t id, uint64_t val)
> > +{
> > +     struct kvm_one_reg reg = {
> > +             .id = id,
> > +             .addr = (uint64_t)&val,
> > +     };
> > +
> > +     return _vcpu_ioctl(vm, 0, KVM_SET_ONE_REG, &reg);
> > +}
> > +
> > +static void get_fw_reg(struct kvm_vm *vm, uint64_t id, uint64_t *addr)
> > +{
> > +     struct kvm_one_reg reg = {
> > +             .id = id,
> > +             .addr = (uint64_t)addr,
> > +     };
> > +
> > +     vcpu_ioctl(vm, 0, KVM_GET_ONE_REG, &reg);
> > +}
> > +
> > +struct st_time {
> > +     uint32_t rev;
> > +     uint32_t attr;
> > +     uint64_t st_time;
> > +};
> > +
> > +#define STEAL_TIME_SIZE              ((sizeof(struct st_time) + 63) & ~63)
> > +#define ST_GPA_BASE          (1 << 30)
> > +
> > +static void steal_time_init(struct kvm_vm *vm)
> > +{
> > +     uint64_t st_ipa = (ulong)ST_GPA_BASE;
> > +     unsigned int gpages;
> > +     struct kvm_device_attr dev = {
> > +             .group = KVM_ARM_VCPU_PVTIME_CTRL,
> > +             .attr = KVM_ARM_VCPU_PVTIME_IPA,
> > +             .addr = (uint64_t)&st_ipa,
> > +     };
> > +
> > +     gpages = vm_calc_num_guest_pages(VM_MODE_DEFAULT, STEAL_TIME_SIZE);
> > +     vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, ST_GPA_BASE, 1, gpages, 0);
> > +
> > +     vcpu_ioctl(vm, 0, KVM_SET_DEVICE_ATTR, &dev);
> > +}
> > +
>
> It might be helpful to do TEST_FAIL() on error returned from
> this vcpu_ioctl(), or skip those PVTIME SMCCC calls accordingly
> if the attribute isn't set successfully.
>
vcpu_ioctl() does a TEST_ASSERT() for us. Of course we can check it
ourselves and skip if needed, but don't you think that may go
unnoticed should any future changes tries to mess with
steal_time_init() incorrectly and we'd end up skipping the test
forever until we really notice skipped test?

> > +static void test_fw_regs_before_vm_start(struct kvm_vm *vm)
> > +{
> > +     uint64_t val;
> > +     unsigned int i;
> > +     int ret;
> > +
> > +     for (i = 0; i < ARRAY_SIZE(fw_reg_info); i++) {
> > +             const struct kvm_fw_reg_info *reg_info = &fw_reg_info[i];
> > +
> > +             /* First 'read' should be an upper limit of the features supported */
> > +             get_fw_reg(vm, reg_info->reg, &val);
> > +             TEST_ASSERT(val == FW_REG_ULIMIT_VAL(reg_info->max_feat_bit),
> > +                     "Expected all the features to be set for reg: 0x%lx; expected: 0x%llx; read: 0x%lx\n",
> > +                     reg_info->reg, GENMASK_ULL(reg_info->max_feat_bit, 0), val);
> > +
>
> s/GENMASK_ULL(...)/FW_REG_ULIMIT_VAL(...)
>
Right, that's better.

> > +             /* Test a 'write' by disabling all the features of the register map */
> > +             ret = set_fw_reg(vm, reg_info->reg, 0);
> > +             TEST_ASSERT(ret == 0,
> > +                     "Failed to clear all the features of reg: 0x%lx; ret: %d\n",
> > +                     reg_info->reg, errno);
> > +
> > +             get_fw_reg(vm, reg_info->reg, &val);
> > +             TEST_ASSERT(val == 0,
> > +                     "Expected all the features to be cleared for reg: 0x%lx\n", reg_info->reg);
> > +
> > +             /*
> > +              * Test enabling a feature that's not supported.
> > +              * Avoid this check if all the bits are occupied.
> > +              */
> > +             if (reg_info->max_feat_bit < 63) {
> > +                     ret = set_fw_reg(vm, reg_info->reg, BIT(reg_info->max_feat_bit + 1));
> > +                     TEST_ASSERT(ret != 0 && errno == EINVAL,
> > +                     "Unexpected behavior or return value (%d) while setting an unsupported feature for reg: 0x%lx\n",
> > +                     errno, reg_info->reg);
> > +             }
> > +     }
> > +}
> > +
> > +static void test_fw_regs_after_vm_start(struct kvm_vm *vm)
> > +{
> > +     uint64_t val;
> > +     unsigned int i;
> > +     int ret;
> > +
> > +     for (i = 0; i < ARRAY_SIZE(fw_reg_info); i++) {
> > +             const struct kvm_fw_reg_info *reg_info = &fw_reg_info[i];
> > +
> > +             /*
> > +              * Before starting the VM, the test clears all the bits.
> > +              * Check if that's still the case.
> > +              */
> > +             get_fw_reg(vm, reg_info->reg, &val);
> > +             TEST_ASSERT(val == 0,
> > +                     "Expected all the features to be cleared for reg: 0x%lx\n",
> > +                     reg_info->reg);
> > +
> > +             /*
> > +              * Test setting the last read value. KVM should allow this
> > +              * even if VM has started running.
> > +              */
> > +             ret = set_fw_reg(vm, reg_info->reg, val);
> > +             TEST_ASSERT(ret == 0,
> > +                     "Failed to set the register with previously read value after Vm start for reg: 0x%lx; ret: %d\n",
> > +                     reg_info->reg, errno);
> > +
> > +             /*
> > +              * Set all the features for this register again. KVM shouldn't
> > +              * allow this as the VM is running.
> > +              */
> > +             ret = set_fw_reg(vm, reg_info->reg, FW_REG_ULIMIT_VAL(reg_info->max_feat_bit));
> > +             TEST_ASSERT(ret != 0 && errno == EBUSY,
> > +             "Unexpected behavior or return value (%d) while setting a feature while VM is running for reg: 0x%lx\n",
> > +             errno, reg_info->reg);
> > +     }
> > +}
> > +
> > +static struct kvm_vm *test_vm_create(void)
> > +{
> > +     struct kvm_vm *vm;
> > +
> > +     vm = vm_create_default(0, 0, guest_code);
> > +
> > +     ucall_init(vm, NULL);
> > +     steal_time_init(vm);
> > +
> > +     return vm;
> > +}
> > +
> > +static struct kvm_vm *test_guest_stage(struct kvm_vm *vm)
> > +{
> > +     struct kvm_vm *ret_vm = vm;
> > +
> > +     pr_debug("Stage: %d\n", stage);
> > +
> > +     switch (stage) {
> > +     case TEST_STAGE_REG_IFACE:
> > +             test_fw_regs_after_vm_start(vm);
> > +             break;
> > +     case TEST_STAGE_HVC_IFACE_FEAT_DISABLED:
> > +             /* Start a new VM so that all the features are now enabled by default */
> > +             kvm_vm_free(vm);
> > +             ret_vm = test_vm_create();
> > +             break;
> > +     case TEST_STAGE_HVC_IFACE_FEAT_ENABLED:
> > +     case TEST_STAGE_HVC_IFACE_FALSE_INFO:
> > +             break;
> > +     default:
> > +             TEST_FAIL("Unknown test stage: %d\n", stage);
> > +     }
> > +
> > +     stage++;
> > +     sync_global_to_guest(vm, stage);
> > +
> > +     return ret_vm;
> > +}
> > +
> > +static void test_run(void)
> > +{
> > +     struct kvm_vm *vm;
> > +     struct ucall uc;
> > +     bool guest_done = false;
> > +
> > +     vm = test_vm_create();
> > +
> > +     test_fw_regs_before_vm_start(vm);
> > +
> > +     while (!guest_done) {
> > +             vcpu_run(vm, 0);
> > +
> > +             switch (get_ucall(vm, 0, &uc)) {
> > +             case UCALL_SYNC:
> > +                     vm = test_guest_stage(vm);
> > +                     break;
> > +             case UCALL_DONE:
> > +                     guest_done = true;
> > +                     break;
> > +             case UCALL_ABORT:
> > +                     TEST_FAIL("%s at %s:%ld\n\tvalues: 0x%lx, 0x%lx; 0x%lx, stage: %u",
> > +                     (const char *)uc.args[0], __FILE__, uc.args[1],
> > +                     uc.args[2], uc.args[3], uc.args[4], stage);
> > +                     break;
> > +             default:
> > +                     TEST_FAIL("Unexpected guest exit\n");
> > +             }
> > +     }
> > +
> > +     kvm_vm_free(vm);
> > +}
> > +
> > +int main(void)
> > +{
> > +     setbuf(stdout, NULL);
> > +
> > +     test_run();
> > +     return 0;
> > +}
> >
>
> Thanks,
> Gavin
>

Thanks for taking the time to review.

Regards,
Raghavendra

[1]: https://lore.kernel.org/kvmarm/20220409184549.1681189-11-oupton@google.com/T/#u

_______________________________________________
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] 96+ messages in thread

* Re: [PATCH v5 10/10] selftests: KVM: aarch64: Add KVM_REG_ARM_FW_REG(3) to get-reg-list
  2022-04-13  9:22     ` Gavin Shan
  (?)
@ 2022-04-13 17:33       ` Raghavendra Rao Ananta
  -1 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-13 17:33 UTC (permalink / raw)
  To: Gavin Shan
  Cc: kvm, Marc Zyngier, Peter Shier, linux-kernel, Will Deacon,
	Catalin Marinas, Paolo Bonzini, kvmarm, linux-arm-kernel

On Wed, Apr 13, 2022 at 2:22 AM Gavin Shan <gshan@redhat.com> wrote:
>
> Hi Raghavendra,
>
> On 4/7/22 9:16 AM, Raghavendra Rao Ananta wrote:
> > Add the register KVM_REG_ARM_FW_REG(3)
> > (KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3) to the base_regs[] of
> > get-reg-list.
> >
> > Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
> > ---
> >   tools/testing/selftests/kvm/aarch64/get-reg-list.c | 1 +
> >   1 file changed, 1 insertion(+)
> >
> > diff --git a/tools/testing/selftests/kvm/aarch64/get-reg-list.c b/tools/testing/selftests/kvm/aarch64/get-reg-list.c
> > index 281c08b3fdd2..7049c31aa443 100644
> > --- a/tools/testing/selftests/kvm/aarch64/get-reg-list.c
> > +++ b/tools/testing/selftests/kvm/aarch64/get-reg-list.c
> > @@ -691,6 +691,7 @@ static __u64 base_regs[] = {
> >       KVM_REG_ARM_FW_REG(0),
> >       KVM_REG_ARM_FW_REG(1),
> >       KVM_REG_ARM_FW_REG(2),
> > +     KVM_REG_ARM_FW_REG(3),
> >       KVM_REG_ARM_FW_FEAT_BMAP_REG(0),        /* KVM_REG_ARM_STD_BMAP */
> >       KVM_REG_ARM_FW_FEAT_BMAP_REG(1),        /* KVM_REG_ARM_STD_HYP_BMAP */
> >       KVM_REG_ARM_FW_FEAT_BMAP_REG(2),        /* KVM_REG_ARM_VENDOR_HYP_BMAP */
> >
>
> It seems the same fixup has been done in another patch.
>
> https://www.mail-archive.com/kvmarm@lists.cs.columbia.edu/msg38027.html
>
Yes, Andrew won the race :(
I'll drop this patch.

> Thanks,
> Gavin
>

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

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

* Re: [PATCH v5 10/10] selftests: KVM: aarch64: Add KVM_REG_ARM_FW_REG(3) to get-reg-list
@ 2022-04-13 17:33       ` Raghavendra Rao Ananta
  0 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-13 17:33 UTC (permalink / raw)
  To: Gavin Shan
  Cc: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose, kvm, Catalin Marinas, Peter Shier,
	linux-kernel, Paolo Bonzini, Will Deacon, kvmarm,
	linux-arm-kernel

On Wed, Apr 13, 2022 at 2:22 AM Gavin Shan <gshan@redhat.com> wrote:
>
> Hi Raghavendra,
>
> On 4/7/22 9:16 AM, Raghavendra Rao Ananta wrote:
> > Add the register KVM_REG_ARM_FW_REG(3)
> > (KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3) to the base_regs[] of
> > get-reg-list.
> >
> > Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
> > ---
> >   tools/testing/selftests/kvm/aarch64/get-reg-list.c | 1 +
> >   1 file changed, 1 insertion(+)
> >
> > diff --git a/tools/testing/selftests/kvm/aarch64/get-reg-list.c b/tools/testing/selftests/kvm/aarch64/get-reg-list.c
> > index 281c08b3fdd2..7049c31aa443 100644
> > --- a/tools/testing/selftests/kvm/aarch64/get-reg-list.c
> > +++ b/tools/testing/selftests/kvm/aarch64/get-reg-list.c
> > @@ -691,6 +691,7 @@ static __u64 base_regs[] = {
> >       KVM_REG_ARM_FW_REG(0),
> >       KVM_REG_ARM_FW_REG(1),
> >       KVM_REG_ARM_FW_REG(2),
> > +     KVM_REG_ARM_FW_REG(3),
> >       KVM_REG_ARM_FW_FEAT_BMAP_REG(0),        /* KVM_REG_ARM_STD_BMAP */
> >       KVM_REG_ARM_FW_FEAT_BMAP_REG(1),        /* KVM_REG_ARM_STD_HYP_BMAP */
> >       KVM_REG_ARM_FW_FEAT_BMAP_REG(2),        /* KVM_REG_ARM_VENDOR_HYP_BMAP */
> >
>
> It seems the same fixup has been done in another patch.
>
> https://www.mail-archive.com/kvmarm@lists.cs.columbia.edu/msg38027.html
>
Yes, Andrew won the race :(
I'll drop this patch.

> Thanks,
> Gavin
>

Regards,
Raghavendra

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

* Re: [PATCH v5 10/10] selftests: KVM: aarch64: Add KVM_REG_ARM_FW_REG(3) to get-reg-list
@ 2022-04-13 17:33       ` Raghavendra Rao Ananta
  0 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-13 17:33 UTC (permalink / raw)
  To: Gavin Shan
  Cc: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose, kvm, Catalin Marinas, Peter Shier,
	linux-kernel, Paolo Bonzini, Will Deacon, kvmarm,
	linux-arm-kernel

On Wed, Apr 13, 2022 at 2:22 AM Gavin Shan <gshan@redhat.com> wrote:
>
> Hi Raghavendra,
>
> On 4/7/22 9:16 AM, Raghavendra Rao Ananta wrote:
> > Add the register KVM_REG_ARM_FW_REG(3)
> > (KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3) to the base_regs[] of
> > get-reg-list.
> >
> > Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
> > ---
> >   tools/testing/selftests/kvm/aarch64/get-reg-list.c | 1 +
> >   1 file changed, 1 insertion(+)
> >
> > diff --git a/tools/testing/selftests/kvm/aarch64/get-reg-list.c b/tools/testing/selftests/kvm/aarch64/get-reg-list.c
> > index 281c08b3fdd2..7049c31aa443 100644
> > --- a/tools/testing/selftests/kvm/aarch64/get-reg-list.c
> > +++ b/tools/testing/selftests/kvm/aarch64/get-reg-list.c
> > @@ -691,6 +691,7 @@ static __u64 base_regs[] = {
> >       KVM_REG_ARM_FW_REG(0),
> >       KVM_REG_ARM_FW_REG(1),
> >       KVM_REG_ARM_FW_REG(2),
> > +     KVM_REG_ARM_FW_REG(3),
> >       KVM_REG_ARM_FW_FEAT_BMAP_REG(0),        /* KVM_REG_ARM_STD_BMAP */
> >       KVM_REG_ARM_FW_FEAT_BMAP_REG(1),        /* KVM_REG_ARM_STD_HYP_BMAP */
> >       KVM_REG_ARM_FW_FEAT_BMAP_REG(2),        /* KVM_REG_ARM_VENDOR_HYP_BMAP */
> >
>
> It seems the same fixup has been done in another patch.
>
> https://www.mail-archive.com/kvmarm@lists.cs.columbia.edu/msg38027.html
>
Yes, Andrew won the race :(
I'll drop this patch.

> Thanks,
> Gavin
>

Regards,
Raghavendra

_______________________________________________
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] 96+ messages in thread

* Re: [PATCH v5 04/10] KVM: arm64: Add vendor hypervisor firmware register
  2022-04-13 16:59       ` Raghavendra Rao Ananta
  (?)
@ 2022-04-14  1:04         ` Gavin Shan
  -1 siblings, 0 replies; 96+ messages in thread
From: Gavin Shan @ 2022-04-14  1:04 UTC (permalink / raw)
  To: Raghavendra Rao Ananta
  Cc: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose, kvm, Catalin Marinas, Peter Shier,
	linux-kernel, Paolo Bonzini, Will Deacon, kvmarm,
	linux-arm-kernel

Hi Raghavendra,

On 4/14/22 12:59 AM, Raghavendra Rao Ananta wrote:
> On Tue, Apr 12, 2022 at 8:59 PM Gavin Shan <gshan@redhat.com> wrote:
>> On 4/7/22 9:15 AM, Raghavendra Rao Ananta wrote:
>>> Introduce the firmware register to hold the vendor specific
>>> hypervisor service calls (owner value 6) as a bitmap. The
>>> bitmap represents the features that'll be enabled for the
>>> guest, as configured by the user-space. Currently, this
>>> includes support for KVM-vendor features, and Precision Time
>>> Protocol (PTP), represented by bit-0 and bit-1 respectively.
>>>
>>> Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
>>> ---
>>>    arch/arm64/include/asm/kvm_host.h |  2 ++
>>>    arch/arm64/include/uapi/asm/kvm.h |  4 ++++
>>>    arch/arm64/kvm/hypercalls.c       | 21 +++++++++++++++++----
>>>    include/kvm/arm_hypercalls.h      |  4 ++++
>>>    4 files changed, 27 insertions(+), 4 deletions(-)
>>>
>>> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
>>> index 20165242ebd9..b79161bad69a 100644
>>> --- a/arch/arm64/include/asm/kvm_host.h
>>> +++ b/arch/arm64/include/asm/kvm_host.h
>>> @@ -106,10 +106,12 @@ struct kvm_arch_memory_slot {
>>>     *
>>>     * @std_bmap: Bitmap of standard secure service calls
>>>     * @std_hyp_bmap: Bitmap of standard hypervisor service calls
>>> + * @vendor_hyp_bmap: Bitmap of vendor specific hypervisor service calls
>>>     */
>>>    struct kvm_smccc_features {
>>>        u64 std_bmap;
>>>        u64 std_hyp_bmap;
>>> +     u64 vendor_hyp_bmap;
>>>    };
>>>
>>>    struct kvm_arch {
>>> diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h
>>> index 67353bf4e69d..9a5ac0ed4113 100644
>>> --- a/arch/arm64/include/uapi/asm/kvm.h
>>> +++ b/arch/arm64/include/uapi/asm/kvm.h
>>> @@ -344,6 +344,10 @@ struct kvm_arm_copy_mte_tags {
>>>    #define KVM_REG_ARM_STD_HYP_BMAP            KVM_REG_ARM_FW_FEAT_BMAP_REG(1)
>>>    #define KVM_REG_ARM_STD_HYP_BIT_PV_TIME             BIT(0)
>>>
>>> +#define KVM_REG_ARM_VENDOR_HYP_BMAP          KVM_REG_ARM_FW_FEAT_BMAP_REG(2)
>>> +#define KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT BIT(0)
>>> +#define KVM_REG_ARM_VENDOR_HYP_BIT_PTP               BIT(1)
>>> +
>>>    /* Device Control API: ARM VGIC */
>>>    #define KVM_DEV_ARM_VGIC_GRP_ADDR   0
>>>    #define KVM_DEV_ARM_VGIC_GRP_DIST_REGS      1
>>> diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c
>>> index 64ae6c7e7145..80836c341fd3 100644
>>> --- a/arch/arm64/kvm/hypercalls.c
>>> +++ b/arch/arm64/kvm/hypercalls.c
>>> @@ -66,8 +66,6 @@ static const u32 hvc_func_default_allowed_list[] = {
>>>        ARM_SMCCC_VERSION_FUNC_ID,
>>>        ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
>>>        ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID,
>>> -     ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID,
>>> -     ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID,
>>>    };
>>>
>>>    static bool kvm_hvc_call_default_allowed(struct kvm_vcpu *vcpu, u32 func_id)
>>> @@ -102,6 +100,12 @@ static bool kvm_hvc_call_allowed(struct kvm_vcpu *vcpu, u32 func_id)
>>>        case ARM_SMCCC_HV_PV_TIME_ST:
>>>                return kvm_arm_fw_reg_feat_enabled(smccc_feat->std_hyp_bmap,
>>>                                        KVM_REG_ARM_STD_HYP_BIT_PV_TIME);
>>> +     case ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID:
>>> +             return kvm_arm_fw_reg_feat_enabled(smccc_feat->vendor_hyp_bmap,
>>> +                                     KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT);
>>> +     case ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID:
>>> +             return kvm_arm_fw_reg_feat_enabled(smccc_feat->vendor_hyp_bmap,
>>> +                                     KVM_REG_ARM_VENDOR_HYP_BIT_PTP);
>>>        default:
>>>                return kvm_hvc_call_default_allowed(vcpu, func_id);
>>>        }
>>
>> I guess we may return SMCCC_RET_NOT_SUPPORTED for ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID
>> if KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT isn't set? Otherwise, we need explain it
>> in the commit log.
>>
> ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID is a part of the hvc
> allowed-list (hvc_func_default_allowed_list[]), which means it's not
> associated with any feature bit and is always enabled. If the guest
> were to issue ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID, we'd end up in
> the 'default' case and the kvm_hvc_call_default_allowed() would return
> 'true'. This is documented in patch 2/10.
> 

I think I might not make myself clear and sorry for that. The point is
the following hvc calls should be belonging to 'Vendor Specific Hypervisor
Service', or I'm wrong. If I'm correct, VENDOR_HYP_CALL_UID_FUNC_ID
should be disallowed if bit#0 isn't set in @vendor_hyp_bmap.

     ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID
     ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID
     ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID

ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID was introduced by commit 6e085e0ac9cf
("arm/arm64: Probe for the presence of KVM hypervisor"). According to the
commit log, the identifier and supported (vendor specific) feature list
is returned by this call and ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID.
So the users depend on both calls to probe the supported features or
services. So it seems incorrect to allow ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID
even the 'Vendor Specific Hypervisor Service' is disabled and bit#0
is cleared in @vendor_hyp_bmap by users.

>> KVM_REG_ARM_VENDOR_HYP_BIT_{FUNC_FEAT, PTP} aren't parallel to each other.
>> I think PTP can't be on if KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT is off.
>>
> Actually we went through this scenario [1]. Of course, we can build
> some logic around it to make sure that the userspace does the right
> thing, but at this point the consensus is that, unless it's an issue
> for KVM, it's treated as a userspace bug.
> 

Thanks for the pointer. I chime in late and I didn't check the reviewing
history on this series. Hopefully I didn't bring too much confusing comments
to you.

I think it's fine by treating it as a userspace bug, but it would be nice
to add comments somewhere if you agree.

>>> @@ -194,8 +198,7 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
>>>                val[3] = ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_3;
>>>                break;
>>>        case ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID:
>>> -             val[0] = BIT(ARM_SMCCC_KVM_FUNC_FEATURES);
>>> -             val[0] |= BIT(ARM_SMCCC_KVM_FUNC_PTP);
>>> +             val[0] = smccc_feat->vendor_hyp_bmap;
>>>                break;
>>>        case ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID:
>>>                kvm_ptp_get_time(vcpu, val);
>>> @@ -222,6 +225,7 @@ static const u64 kvm_arm_fw_reg_ids[] = {
>>>        KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3,
>>>        KVM_REG_ARM_STD_BMAP,
>>>        KVM_REG_ARM_STD_HYP_BMAP,
>>> +     KVM_REG_ARM_VENDOR_HYP_BMAP,
>>>    };
>>>
>>>    void kvm_arm_init_hypercalls(struct kvm *kvm)
>>> @@ -230,6 +234,7 @@ void kvm_arm_init_hypercalls(struct kvm *kvm)
>>>
>>>        smccc_feat->std_bmap = KVM_ARM_SMCCC_STD_FEATURES;
>>>        smccc_feat->std_hyp_bmap = KVM_ARM_SMCCC_STD_HYP_FEATURES;
>>> +     smccc_feat->vendor_hyp_bmap = KVM_ARM_SMCCC_VENDOR_HYP_FEATURES;
>>>    }
>>>
>>>    int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
>>> @@ -322,6 +327,9 @@ int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>>>        case KVM_REG_ARM_STD_HYP_BMAP:
>>>                val = READ_ONCE(smccc_feat->std_hyp_bmap);
>>>                break;
>>> +     case KVM_REG_ARM_VENDOR_HYP_BMAP:
>>> +             val = READ_ONCE(smccc_feat->vendor_hyp_bmap);
>>> +             break;
>>>        default:
>>>                return -ENOENT;
>>>        }
>>> @@ -348,6 +356,10 @@ static int kvm_arm_set_fw_reg_bmap(struct kvm_vcpu *vcpu, u64 reg_id, u64 val)
>>>                fw_reg_bmap = &smccc_feat->std_hyp_bmap;
>>>                fw_reg_features = KVM_ARM_SMCCC_STD_HYP_FEATURES;
>>>                break;
>>> +     case KVM_REG_ARM_VENDOR_HYP_BMAP:
>>> +             fw_reg_bmap = &smccc_feat->vendor_hyp_bmap;
>>> +             fw_reg_features = KVM_ARM_SMCCC_VENDOR_HYP_FEATURES;
>>> +             break;
>>>        default:
>>>                return -ENOENT;
>>>        }
>>
>> If KVM_REG_ARM_VENDOR_HYP_BIT_{FUNC_FEAT, PTP} aren't parallel to each other,
>> special code is needed to gurantee PTP is cleared if VENDOR_HYP is disabled.
>>
> Please see the above comment :)
> 

Thanks for the pointer and explanation :)

>>> @@ -453,6 +465,7 @@ int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>>>                return 0;
>>>        case KVM_REG_ARM_STD_BMAP:
>>>        case KVM_REG_ARM_STD_HYP_BMAP:
>>> +     case KVM_REG_ARM_VENDOR_HYP_BMAP:
>>>                return kvm_arm_set_fw_reg_bmap(vcpu, reg->id, val);
>>>        default:
>>>                return -ENOENT;
>>> diff --git a/include/kvm/arm_hypercalls.h b/include/kvm/arm_hypercalls.h
>>> index b0915d8c5b81..eaf4f6b318a8 100644
>>> --- a/include/kvm/arm_hypercalls.h
>>> +++ b/include/kvm/arm_hypercalls.h
>>> @@ -9,6 +9,7 @@
>>>    /* Last valid bits of the bitmapped firmware registers */
>>>    #define KVM_REG_ARM_STD_BMAP_BIT_MAX                0
>>>    #define KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX    0
>>> +#define KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX  1
>>>
>>>    #define KVM_ARM_SMCCC_STD_FEATURES \
>>>        GENMASK_ULL(KVM_REG_ARM_STD_BMAP_BIT_MAX, 0)
>>> @@ -16,6 +17,9 @@
>>>    #define KVM_ARM_SMCCC_STD_HYP_FEATURES \
>>>        GENMASK_ULL(KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX, 0)
>>>
>>> +#define KVM_ARM_SMCCC_VENDOR_HYP_FEATURES \
>>> +     GENMASK_ULL(KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX, 0)
>>> +
>>>    int kvm_hvc_call_handler(struct kvm_vcpu *vcpu);
>>>
>>>    static inline u32 smccc_get_function(struct kvm_vcpu *vcpu)
>>>
>>
> 
> Thanks for the review.
> 

No worries and sorry for the late chime-in :)

> 
> [1]: https://lore.kernel.org/lkml/YjA1AzZPlPV20kMj@google.com/
> 

Thanks,
Gavin


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

* Re: [PATCH v5 04/10] KVM: arm64: Add vendor hypervisor firmware register
@ 2022-04-14  1:04         ` Gavin Shan
  0 siblings, 0 replies; 96+ messages in thread
From: Gavin Shan @ 2022-04-14  1:04 UTC (permalink / raw)
  To: Raghavendra Rao Ananta
  Cc: kvm, Marc Zyngier, Peter Shier, linux-kernel, Will Deacon,
	Catalin Marinas, Paolo Bonzini, kvmarm, linux-arm-kernel

Hi Raghavendra,

On 4/14/22 12:59 AM, Raghavendra Rao Ananta wrote:
> On Tue, Apr 12, 2022 at 8:59 PM Gavin Shan <gshan@redhat.com> wrote:
>> On 4/7/22 9:15 AM, Raghavendra Rao Ananta wrote:
>>> Introduce the firmware register to hold the vendor specific
>>> hypervisor service calls (owner value 6) as a bitmap. The
>>> bitmap represents the features that'll be enabled for the
>>> guest, as configured by the user-space. Currently, this
>>> includes support for KVM-vendor features, and Precision Time
>>> Protocol (PTP), represented by bit-0 and bit-1 respectively.
>>>
>>> Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
>>> ---
>>>    arch/arm64/include/asm/kvm_host.h |  2 ++
>>>    arch/arm64/include/uapi/asm/kvm.h |  4 ++++
>>>    arch/arm64/kvm/hypercalls.c       | 21 +++++++++++++++++----
>>>    include/kvm/arm_hypercalls.h      |  4 ++++
>>>    4 files changed, 27 insertions(+), 4 deletions(-)
>>>
>>> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
>>> index 20165242ebd9..b79161bad69a 100644
>>> --- a/arch/arm64/include/asm/kvm_host.h
>>> +++ b/arch/arm64/include/asm/kvm_host.h
>>> @@ -106,10 +106,12 @@ struct kvm_arch_memory_slot {
>>>     *
>>>     * @std_bmap: Bitmap of standard secure service calls
>>>     * @std_hyp_bmap: Bitmap of standard hypervisor service calls
>>> + * @vendor_hyp_bmap: Bitmap of vendor specific hypervisor service calls
>>>     */
>>>    struct kvm_smccc_features {
>>>        u64 std_bmap;
>>>        u64 std_hyp_bmap;
>>> +     u64 vendor_hyp_bmap;
>>>    };
>>>
>>>    struct kvm_arch {
>>> diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h
>>> index 67353bf4e69d..9a5ac0ed4113 100644
>>> --- a/arch/arm64/include/uapi/asm/kvm.h
>>> +++ b/arch/arm64/include/uapi/asm/kvm.h
>>> @@ -344,6 +344,10 @@ struct kvm_arm_copy_mte_tags {
>>>    #define KVM_REG_ARM_STD_HYP_BMAP            KVM_REG_ARM_FW_FEAT_BMAP_REG(1)
>>>    #define KVM_REG_ARM_STD_HYP_BIT_PV_TIME             BIT(0)
>>>
>>> +#define KVM_REG_ARM_VENDOR_HYP_BMAP          KVM_REG_ARM_FW_FEAT_BMAP_REG(2)
>>> +#define KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT BIT(0)
>>> +#define KVM_REG_ARM_VENDOR_HYP_BIT_PTP               BIT(1)
>>> +
>>>    /* Device Control API: ARM VGIC */
>>>    #define KVM_DEV_ARM_VGIC_GRP_ADDR   0
>>>    #define KVM_DEV_ARM_VGIC_GRP_DIST_REGS      1
>>> diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c
>>> index 64ae6c7e7145..80836c341fd3 100644
>>> --- a/arch/arm64/kvm/hypercalls.c
>>> +++ b/arch/arm64/kvm/hypercalls.c
>>> @@ -66,8 +66,6 @@ static const u32 hvc_func_default_allowed_list[] = {
>>>        ARM_SMCCC_VERSION_FUNC_ID,
>>>        ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
>>>        ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID,
>>> -     ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID,
>>> -     ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID,
>>>    };
>>>
>>>    static bool kvm_hvc_call_default_allowed(struct kvm_vcpu *vcpu, u32 func_id)
>>> @@ -102,6 +100,12 @@ static bool kvm_hvc_call_allowed(struct kvm_vcpu *vcpu, u32 func_id)
>>>        case ARM_SMCCC_HV_PV_TIME_ST:
>>>                return kvm_arm_fw_reg_feat_enabled(smccc_feat->std_hyp_bmap,
>>>                                        KVM_REG_ARM_STD_HYP_BIT_PV_TIME);
>>> +     case ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID:
>>> +             return kvm_arm_fw_reg_feat_enabled(smccc_feat->vendor_hyp_bmap,
>>> +                                     KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT);
>>> +     case ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID:
>>> +             return kvm_arm_fw_reg_feat_enabled(smccc_feat->vendor_hyp_bmap,
>>> +                                     KVM_REG_ARM_VENDOR_HYP_BIT_PTP);
>>>        default:
>>>                return kvm_hvc_call_default_allowed(vcpu, func_id);
>>>        }
>>
>> I guess we may return SMCCC_RET_NOT_SUPPORTED for ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID
>> if KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT isn't set? Otherwise, we need explain it
>> in the commit log.
>>
> ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID is a part of the hvc
> allowed-list (hvc_func_default_allowed_list[]), which means it's not
> associated with any feature bit and is always enabled. If the guest
> were to issue ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID, we'd end up in
> the 'default' case and the kvm_hvc_call_default_allowed() would return
> 'true'. This is documented in patch 2/10.
> 

I think I might not make myself clear and sorry for that. The point is
the following hvc calls should be belonging to 'Vendor Specific Hypervisor
Service', or I'm wrong. If I'm correct, VENDOR_HYP_CALL_UID_FUNC_ID
should be disallowed if bit#0 isn't set in @vendor_hyp_bmap.

     ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID
     ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID
     ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID

ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID was introduced by commit 6e085e0ac9cf
("arm/arm64: Probe for the presence of KVM hypervisor"). According to the
commit log, the identifier and supported (vendor specific) feature list
is returned by this call and ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID.
So the users depend on both calls to probe the supported features or
services. So it seems incorrect to allow ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID
even the 'Vendor Specific Hypervisor Service' is disabled and bit#0
is cleared in @vendor_hyp_bmap by users.

>> KVM_REG_ARM_VENDOR_HYP_BIT_{FUNC_FEAT, PTP} aren't parallel to each other.
>> I think PTP can't be on if KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT is off.
>>
> Actually we went through this scenario [1]. Of course, we can build
> some logic around it to make sure that the userspace does the right
> thing, but at this point the consensus is that, unless it's an issue
> for KVM, it's treated as a userspace bug.
> 

Thanks for the pointer. I chime in late and I didn't check the reviewing
history on this series. Hopefully I didn't bring too much confusing comments
to you.

I think it's fine by treating it as a userspace bug, but it would be nice
to add comments somewhere if you agree.

>>> @@ -194,8 +198,7 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
>>>                val[3] = ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_3;
>>>                break;
>>>        case ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID:
>>> -             val[0] = BIT(ARM_SMCCC_KVM_FUNC_FEATURES);
>>> -             val[0] |= BIT(ARM_SMCCC_KVM_FUNC_PTP);
>>> +             val[0] = smccc_feat->vendor_hyp_bmap;
>>>                break;
>>>        case ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID:
>>>                kvm_ptp_get_time(vcpu, val);
>>> @@ -222,6 +225,7 @@ static const u64 kvm_arm_fw_reg_ids[] = {
>>>        KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3,
>>>        KVM_REG_ARM_STD_BMAP,
>>>        KVM_REG_ARM_STD_HYP_BMAP,
>>> +     KVM_REG_ARM_VENDOR_HYP_BMAP,
>>>    };
>>>
>>>    void kvm_arm_init_hypercalls(struct kvm *kvm)
>>> @@ -230,6 +234,7 @@ void kvm_arm_init_hypercalls(struct kvm *kvm)
>>>
>>>        smccc_feat->std_bmap = KVM_ARM_SMCCC_STD_FEATURES;
>>>        smccc_feat->std_hyp_bmap = KVM_ARM_SMCCC_STD_HYP_FEATURES;
>>> +     smccc_feat->vendor_hyp_bmap = KVM_ARM_SMCCC_VENDOR_HYP_FEATURES;
>>>    }
>>>
>>>    int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
>>> @@ -322,6 +327,9 @@ int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>>>        case KVM_REG_ARM_STD_HYP_BMAP:
>>>                val = READ_ONCE(smccc_feat->std_hyp_bmap);
>>>                break;
>>> +     case KVM_REG_ARM_VENDOR_HYP_BMAP:
>>> +             val = READ_ONCE(smccc_feat->vendor_hyp_bmap);
>>> +             break;
>>>        default:
>>>                return -ENOENT;
>>>        }
>>> @@ -348,6 +356,10 @@ static int kvm_arm_set_fw_reg_bmap(struct kvm_vcpu *vcpu, u64 reg_id, u64 val)
>>>                fw_reg_bmap = &smccc_feat->std_hyp_bmap;
>>>                fw_reg_features = KVM_ARM_SMCCC_STD_HYP_FEATURES;
>>>                break;
>>> +     case KVM_REG_ARM_VENDOR_HYP_BMAP:
>>> +             fw_reg_bmap = &smccc_feat->vendor_hyp_bmap;
>>> +             fw_reg_features = KVM_ARM_SMCCC_VENDOR_HYP_FEATURES;
>>> +             break;
>>>        default:
>>>                return -ENOENT;
>>>        }
>>
>> If KVM_REG_ARM_VENDOR_HYP_BIT_{FUNC_FEAT, PTP} aren't parallel to each other,
>> special code is needed to gurantee PTP is cleared if VENDOR_HYP is disabled.
>>
> Please see the above comment :)
> 

Thanks for the pointer and explanation :)

>>> @@ -453,6 +465,7 @@ int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>>>                return 0;
>>>        case KVM_REG_ARM_STD_BMAP:
>>>        case KVM_REG_ARM_STD_HYP_BMAP:
>>> +     case KVM_REG_ARM_VENDOR_HYP_BMAP:
>>>                return kvm_arm_set_fw_reg_bmap(vcpu, reg->id, val);
>>>        default:
>>>                return -ENOENT;
>>> diff --git a/include/kvm/arm_hypercalls.h b/include/kvm/arm_hypercalls.h
>>> index b0915d8c5b81..eaf4f6b318a8 100644
>>> --- a/include/kvm/arm_hypercalls.h
>>> +++ b/include/kvm/arm_hypercalls.h
>>> @@ -9,6 +9,7 @@
>>>    /* Last valid bits of the bitmapped firmware registers */
>>>    #define KVM_REG_ARM_STD_BMAP_BIT_MAX                0
>>>    #define KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX    0
>>> +#define KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX  1
>>>
>>>    #define KVM_ARM_SMCCC_STD_FEATURES \
>>>        GENMASK_ULL(KVM_REG_ARM_STD_BMAP_BIT_MAX, 0)
>>> @@ -16,6 +17,9 @@
>>>    #define KVM_ARM_SMCCC_STD_HYP_FEATURES \
>>>        GENMASK_ULL(KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX, 0)
>>>
>>> +#define KVM_ARM_SMCCC_VENDOR_HYP_FEATURES \
>>> +     GENMASK_ULL(KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX, 0)
>>> +
>>>    int kvm_hvc_call_handler(struct kvm_vcpu *vcpu);
>>>
>>>    static inline u32 smccc_get_function(struct kvm_vcpu *vcpu)
>>>
>>
> 
> Thanks for the review.
> 

No worries and sorry for the late chime-in :)

> 
> [1]: https://lore.kernel.org/lkml/YjA1AzZPlPV20kMj@google.com/
> 

Thanks,
Gavin

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

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

* Re: [PATCH v5 04/10] KVM: arm64: Add vendor hypervisor firmware register
@ 2022-04-14  1:04         ` Gavin Shan
  0 siblings, 0 replies; 96+ messages in thread
From: Gavin Shan @ 2022-04-14  1:04 UTC (permalink / raw)
  To: Raghavendra Rao Ananta
  Cc: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose, kvm, Catalin Marinas, Peter Shier,
	linux-kernel, Paolo Bonzini, Will Deacon, kvmarm,
	linux-arm-kernel

Hi Raghavendra,

On 4/14/22 12:59 AM, Raghavendra Rao Ananta wrote:
> On Tue, Apr 12, 2022 at 8:59 PM Gavin Shan <gshan@redhat.com> wrote:
>> On 4/7/22 9:15 AM, Raghavendra Rao Ananta wrote:
>>> Introduce the firmware register to hold the vendor specific
>>> hypervisor service calls (owner value 6) as a bitmap. The
>>> bitmap represents the features that'll be enabled for the
>>> guest, as configured by the user-space. Currently, this
>>> includes support for KVM-vendor features, and Precision Time
>>> Protocol (PTP), represented by bit-0 and bit-1 respectively.
>>>
>>> Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
>>> ---
>>>    arch/arm64/include/asm/kvm_host.h |  2 ++
>>>    arch/arm64/include/uapi/asm/kvm.h |  4 ++++
>>>    arch/arm64/kvm/hypercalls.c       | 21 +++++++++++++++++----
>>>    include/kvm/arm_hypercalls.h      |  4 ++++
>>>    4 files changed, 27 insertions(+), 4 deletions(-)
>>>
>>> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
>>> index 20165242ebd9..b79161bad69a 100644
>>> --- a/arch/arm64/include/asm/kvm_host.h
>>> +++ b/arch/arm64/include/asm/kvm_host.h
>>> @@ -106,10 +106,12 @@ struct kvm_arch_memory_slot {
>>>     *
>>>     * @std_bmap: Bitmap of standard secure service calls
>>>     * @std_hyp_bmap: Bitmap of standard hypervisor service calls
>>> + * @vendor_hyp_bmap: Bitmap of vendor specific hypervisor service calls
>>>     */
>>>    struct kvm_smccc_features {
>>>        u64 std_bmap;
>>>        u64 std_hyp_bmap;
>>> +     u64 vendor_hyp_bmap;
>>>    };
>>>
>>>    struct kvm_arch {
>>> diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h
>>> index 67353bf4e69d..9a5ac0ed4113 100644
>>> --- a/arch/arm64/include/uapi/asm/kvm.h
>>> +++ b/arch/arm64/include/uapi/asm/kvm.h
>>> @@ -344,6 +344,10 @@ struct kvm_arm_copy_mte_tags {
>>>    #define KVM_REG_ARM_STD_HYP_BMAP            KVM_REG_ARM_FW_FEAT_BMAP_REG(1)
>>>    #define KVM_REG_ARM_STD_HYP_BIT_PV_TIME             BIT(0)
>>>
>>> +#define KVM_REG_ARM_VENDOR_HYP_BMAP          KVM_REG_ARM_FW_FEAT_BMAP_REG(2)
>>> +#define KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT BIT(0)
>>> +#define KVM_REG_ARM_VENDOR_HYP_BIT_PTP               BIT(1)
>>> +
>>>    /* Device Control API: ARM VGIC */
>>>    #define KVM_DEV_ARM_VGIC_GRP_ADDR   0
>>>    #define KVM_DEV_ARM_VGIC_GRP_DIST_REGS      1
>>> diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c
>>> index 64ae6c7e7145..80836c341fd3 100644
>>> --- a/arch/arm64/kvm/hypercalls.c
>>> +++ b/arch/arm64/kvm/hypercalls.c
>>> @@ -66,8 +66,6 @@ static const u32 hvc_func_default_allowed_list[] = {
>>>        ARM_SMCCC_VERSION_FUNC_ID,
>>>        ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
>>>        ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID,
>>> -     ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID,
>>> -     ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID,
>>>    };
>>>
>>>    static bool kvm_hvc_call_default_allowed(struct kvm_vcpu *vcpu, u32 func_id)
>>> @@ -102,6 +100,12 @@ static bool kvm_hvc_call_allowed(struct kvm_vcpu *vcpu, u32 func_id)
>>>        case ARM_SMCCC_HV_PV_TIME_ST:
>>>                return kvm_arm_fw_reg_feat_enabled(smccc_feat->std_hyp_bmap,
>>>                                        KVM_REG_ARM_STD_HYP_BIT_PV_TIME);
>>> +     case ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID:
>>> +             return kvm_arm_fw_reg_feat_enabled(smccc_feat->vendor_hyp_bmap,
>>> +                                     KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT);
>>> +     case ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID:
>>> +             return kvm_arm_fw_reg_feat_enabled(smccc_feat->vendor_hyp_bmap,
>>> +                                     KVM_REG_ARM_VENDOR_HYP_BIT_PTP);
>>>        default:
>>>                return kvm_hvc_call_default_allowed(vcpu, func_id);
>>>        }
>>
>> I guess we may return SMCCC_RET_NOT_SUPPORTED for ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID
>> if KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT isn't set? Otherwise, we need explain it
>> in the commit log.
>>
> ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID is a part of the hvc
> allowed-list (hvc_func_default_allowed_list[]), which means it's not
> associated with any feature bit and is always enabled. If the guest
> were to issue ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID, we'd end up in
> the 'default' case and the kvm_hvc_call_default_allowed() would return
> 'true'. This is documented in patch 2/10.
> 

I think I might not make myself clear and sorry for that. The point is
the following hvc calls should be belonging to 'Vendor Specific Hypervisor
Service', or I'm wrong. If I'm correct, VENDOR_HYP_CALL_UID_FUNC_ID
should be disallowed if bit#0 isn't set in @vendor_hyp_bmap.

     ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID
     ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID
     ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID

ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID was introduced by commit 6e085e0ac9cf
("arm/arm64: Probe for the presence of KVM hypervisor"). According to the
commit log, the identifier and supported (vendor specific) feature list
is returned by this call and ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID.
So the users depend on both calls to probe the supported features or
services. So it seems incorrect to allow ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID
even the 'Vendor Specific Hypervisor Service' is disabled and bit#0
is cleared in @vendor_hyp_bmap by users.

>> KVM_REG_ARM_VENDOR_HYP_BIT_{FUNC_FEAT, PTP} aren't parallel to each other.
>> I think PTP can't be on if KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT is off.
>>
> Actually we went through this scenario [1]. Of course, we can build
> some logic around it to make sure that the userspace does the right
> thing, but at this point the consensus is that, unless it's an issue
> for KVM, it's treated as a userspace bug.
> 

Thanks for the pointer. I chime in late and I didn't check the reviewing
history on this series. Hopefully I didn't bring too much confusing comments
to you.

I think it's fine by treating it as a userspace bug, but it would be nice
to add comments somewhere if you agree.

>>> @@ -194,8 +198,7 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
>>>                val[3] = ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_3;
>>>                break;
>>>        case ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID:
>>> -             val[0] = BIT(ARM_SMCCC_KVM_FUNC_FEATURES);
>>> -             val[0] |= BIT(ARM_SMCCC_KVM_FUNC_PTP);
>>> +             val[0] = smccc_feat->vendor_hyp_bmap;
>>>                break;
>>>        case ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID:
>>>                kvm_ptp_get_time(vcpu, val);
>>> @@ -222,6 +225,7 @@ static const u64 kvm_arm_fw_reg_ids[] = {
>>>        KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3,
>>>        KVM_REG_ARM_STD_BMAP,
>>>        KVM_REG_ARM_STD_HYP_BMAP,
>>> +     KVM_REG_ARM_VENDOR_HYP_BMAP,
>>>    };
>>>
>>>    void kvm_arm_init_hypercalls(struct kvm *kvm)
>>> @@ -230,6 +234,7 @@ void kvm_arm_init_hypercalls(struct kvm *kvm)
>>>
>>>        smccc_feat->std_bmap = KVM_ARM_SMCCC_STD_FEATURES;
>>>        smccc_feat->std_hyp_bmap = KVM_ARM_SMCCC_STD_HYP_FEATURES;
>>> +     smccc_feat->vendor_hyp_bmap = KVM_ARM_SMCCC_VENDOR_HYP_FEATURES;
>>>    }
>>>
>>>    int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
>>> @@ -322,6 +327,9 @@ int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>>>        case KVM_REG_ARM_STD_HYP_BMAP:
>>>                val = READ_ONCE(smccc_feat->std_hyp_bmap);
>>>                break;
>>> +     case KVM_REG_ARM_VENDOR_HYP_BMAP:
>>> +             val = READ_ONCE(smccc_feat->vendor_hyp_bmap);
>>> +             break;
>>>        default:
>>>                return -ENOENT;
>>>        }
>>> @@ -348,6 +356,10 @@ static int kvm_arm_set_fw_reg_bmap(struct kvm_vcpu *vcpu, u64 reg_id, u64 val)
>>>                fw_reg_bmap = &smccc_feat->std_hyp_bmap;
>>>                fw_reg_features = KVM_ARM_SMCCC_STD_HYP_FEATURES;
>>>                break;
>>> +     case KVM_REG_ARM_VENDOR_HYP_BMAP:
>>> +             fw_reg_bmap = &smccc_feat->vendor_hyp_bmap;
>>> +             fw_reg_features = KVM_ARM_SMCCC_VENDOR_HYP_FEATURES;
>>> +             break;
>>>        default:
>>>                return -ENOENT;
>>>        }
>>
>> If KVM_REG_ARM_VENDOR_HYP_BIT_{FUNC_FEAT, PTP} aren't parallel to each other,
>> special code is needed to gurantee PTP is cleared if VENDOR_HYP is disabled.
>>
> Please see the above comment :)
> 

Thanks for the pointer and explanation :)

>>> @@ -453,6 +465,7 @@ int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>>>                return 0;
>>>        case KVM_REG_ARM_STD_BMAP:
>>>        case KVM_REG_ARM_STD_HYP_BMAP:
>>> +     case KVM_REG_ARM_VENDOR_HYP_BMAP:
>>>                return kvm_arm_set_fw_reg_bmap(vcpu, reg->id, val);
>>>        default:
>>>                return -ENOENT;
>>> diff --git a/include/kvm/arm_hypercalls.h b/include/kvm/arm_hypercalls.h
>>> index b0915d8c5b81..eaf4f6b318a8 100644
>>> --- a/include/kvm/arm_hypercalls.h
>>> +++ b/include/kvm/arm_hypercalls.h
>>> @@ -9,6 +9,7 @@
>>>    /* Last valid bits of the bitmapped firmware registers */
>>>    #define KVM_REG_ARM_STD_BMAP_BIT_MAX                0
>>>    #define KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX    0
>>> +#define KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX  1
>>>
>>>    #define KVM_ARM_SMCCC_STD_FEATURES \
>>>        GENMASK_ULL(KVM_REG_ARM_STD_BMAP_BIT_MAX, 0)
>>> @@ -16,6 +17,9 @@
>>>    #define KVM_ARM_SMCCC_STD_HYP_FEATURES \
>>>        GENMASK_ULL(KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX, 0)
>>>
>>> +#define KVM_ARM_SMCCC_VENDOR_HYP_FEATURES \
>>> +     GENMASK_ULL(KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX, 0)
>>> +
>>>    int kvm_hvc_call_handler(struct kvm_vcpu *vcpu);
>>>
>>>    static inline u32 smccc_get_function(struct kvm_vcpu *vcpu)
>>>
>>
> 
> Thanks for the review.
> 

No worries and sorry for the late chime-in :)

> 
> [1]: https://lore.kernel.org/lkml/YjA1AzZPlPV20kMj@google.com/
> 

Thanks,
Gavin


_______________________________________________
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] 96+ messages in thread

* Re: [PATCH v5 08/10] selftests: KVM: aarch64: Introduce hypercall ABI test
  2022-04-13 17:32       ` Raghavendra Rao Ananta
  (?)
@ 2022-04-14  1:09         ` Gavin Shan
  -1 siblings, 0 replies; 96+ messages in thread
From: Gavin Shan @ 2022-04-14  1:09 UTC (permalink / raw)
  To: Raghavendra Rao Ananta, Oliver Upton
  Cc: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose, kvm, Catalin Marinas, Peter Shier,
	linux-kernel, Paolo Bonzini, Will Deacon, kvmarm,
	linux-arm-kernel

Hi Raghavendra,

On 4/14/22 1:32 AM, Raghavendra Rao Ananta wrote:
> On Wed, Apr 13, 2022 at 2:07 AM Gavin Shan <gshan@redhat.com> wrote:
>> On 4/7/22 9:16 AM, Raghavendra Rao Ananta wrote:
>>> Introduce a KVM selftest to check the hypercall interface
>>> for arm64 platforms. The test validates the user-space's
>>> IOCTL interface to read/write the psuedo-firmware registers
>>> as well as its effects on the guest upon certain configurations.
>>>
>>> Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
>>> ---
>>>    tools/testing/selftests/kvm/.gitignore        |   1 +
>>>    tools/testing/selftests/kvm/Makefile          |   1 +
>>>    .../selftests/kvm/aarch64/hypercalls.c        | 344 ++++++++++++++++++
>>>    3 files changed, 346 insertions(+)
>>>    create mode 100644 tools/testing/selftests/kvm/aarch64/hypercalls.c
>>>
>>
>> To be more precise, s/IOCTL/{GET,SET}_ONE_REG ?
>>
> Sure, I think that'll be better.
> 
>>> diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore
>>> index e82b816a6608..7ef52b3b1560 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/hypercalls
>>>    /aarch64/psci_test
>>>    /aarch64/vgic_init
>>>    /aarch64/vgic_irq
>>> diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
>>> index 2f74f502de65..af4cb88bcf83 100644
>>> --- a/tools/testing/selftests/kvm/Makefile
>>> +++ b/tools/testing/selftests/kvm/Makefile
>>> @@ -105,6 +105,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/hypercalls
>>>    TEST_GEN_PROGS_aarch64 += aarch64/psci_test
>>>    TEST_GEN_PROGS_aarch64 += aarch64/vgic_init
>>>    TEST_GEN_PROGS_aarch64 += aarch64/vgic_irq
>>> diff --git a/tools/testing/selftests/kvm/aarch64/hypercalls.c b/tools/testing/selftests/kvm/aarch64/hypercalls.c
>>> new file mode 100644
>>> index 000000000000..9941fb75772a
>>> --- /dev/null
>>> +++ b/tools/testing/selftests/kvm/aarch64/hypercalls.c
>>> @@ -0,0 +1,344 @@
>>> +// SPDX-License-Identifier: GPL-2.0-only
>>> +
>>> +/* hypercalls: Check the ARM64's psuedo-firmware bitmap register interface.
>>> + *
>>> + * The test validates the basic hypercall functionalities that are exposed
>>> + * via the psuedo-firmware bitmap register. This includes the registers'
>>> + * read/write behavior before and after the VM has started, and if the
>>> + * hypercalls are properly masked or unmasked to the guest when disabled or
>>> + * enabled from the KVM userspace, respectively.
>>> + */
>>> +
>>> +#include <errno.h>
>>> +#include <linux/arm-smccc.h>
>>> +#include <asm/kvm.h>
>>> +#include <kvm_util.h>
>>> +
>>> +#include "processor.h"
>>> +
>>> +#define FW_REG_ULIMIT_VAL(max_feat_bit) (GENMASK_ULL(max_feat_bit, 0))
>>> +
>>> +/* Last valid bits of the bitmapped firmware registers */
>>> +#define KVM_REG_ARM_STD_BMAP_BIT_MAX         0
>>> +#define KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX     0
>>> +#define KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX  1
>>> +
>>> +struct kvm_fw_reg_info {
>>> +     uint64_t reg;           /* Register definition */
>>> +     uint64_t max_feat_bit;  /* Bit that represents the upper limit of the feature-map */
>>> +};
>>> +
>>> +#define FW_REG_INFO(r, bit_max)                      \
>>> +     {                                       \
>>> +             .reg = r,                       \
>>> +             .max_feat_bit = bit_max,        \
>>> +     }
>>> +
>>> +static const struct kvm_fw_reg_info fw_reg_info[] = {
>>> +     FW_REG_INFO(KVM_REG_ARM_STD_BMAP, KVM_REG_ARM_STD_BMAP_BIT_MAX),
>>> +     FW_REG_INFO(KVM_REG_ARM_STD_HYP_BMAP, KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX),
>>> +     FW_REG_INFO(KVM_REG_ARM_VENDOR_HYP_BMAP, KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX),
>>> +};
>>> +
>>
>> This can be simplifed by:
>>
>> #define FW_REG_INFO(r)                  \
>>          { .reg = r,                     \
>>            .max_feat_bit = r_##BIT_MAX,  \
>>          }
>>
>> static const struct kvm_fw_reg_info fw_reg_info[] = {
>>          FW_REG_INFO(KVM_REG_ARM_STD_BMAP),
>>          FW_REG_INFO(KVM_REG_ARM_STD_HYP_BMAP),
>>          FW_REG_INFO(KVM_REG_ARM_VENDOR_HYP_BMAP),
>> };
>>
> Yes, probably that looks better. Thanks for the suggestion.
> 
>>> +enum test_stage {
>>> +     TEST_STAGE_REG_IFACE,
>>> +     TEST_STAGE_HVC_IFACE_FEAT_DISABLED,
>>> +     TEST_STAGE_HVC_IFACE_FEAT_ENABLED,
>>> +     TEST_STAGE_HVC_IFACE_FALSE_INFO,
>>> +     TEST_STAGE_END,
>>> +};
>>> +
>>> +static int stage;
>>> +
>>
>> I think it'd better to initialize @stage to TEST_STAGE_REG_IFACE.
>>
> Will do.
>>> +struct test_hvc_info {
>>> +     uint32_t func_id;
>>> +     int64_t arg0;
>>> +};
>>> +
>>> +#define TEST_HVC_INFO(f, a0) \
>>> +     {                       \
>>> +             .func_id = f,   \
>>> +             .arg0 = a0,     \
>>> +     }
>>> +
>>
>> According to those functions (smccc_get_{function, argx}()) defined
>> in include/kvm/arm_hypercalls.h, @arg0 would have type of "uint64_t"
>> if I'm correct. Besides, @func_id is arg0 and arg0 should be arg1?
>> So if I'm correct, this would be:
>>
>> struct test_hvc_info {
>>          uint32_t func_id;
>>          uint64_t arg1
>> };
>>
> Thanks for noticing this! I'll change it to 'unit64'. Regarding the
> argument naming, I understand that it's a little confusing. I went
> with 'arg0' to align with the selftest library's convention. But, I
> agree that it's not aligned with what the kernel is used to.
> 
> Oliver, do you think we can start the argument naming from a1/arg1 in [1]?
> 
>>> +static const struct test_hvc_info hvc_info[] = {
>>> +     /* KVM_REG_ARM_STD_BMAP */
>>> +     TEST_HVC_INFO(ARM_SMCCC_TRNG_VERSION, 0),
>>> +     TEST_HVC_INFO(ARM_SMCCC_TRNG_FEATURES, ARM_SMCCC_TRNG_RND64),
>>> +     TEST_HVC_INFO(ARM_SMCCC_TRNG_GET_UUID, 0),
>>> +     TEST_HVC_INFO(ARM_SMCCC_TRNG_RND32, 0),
>>> +     TEST_HVC_INFO(ARM_SMCCC_TRNG_RND64, 0),
>>> +
>>> +     /* KVM_REG_ARM_STD_HYP_BMAP */
>>> +     TEST_HVC_INFO(ARM_SMCCC_ARCH_FEATURES_FUNC_ID, ARM_SMCCC_HV_PV_TIME_FEATURES),
>>> +     TEST_HVC_INFO(ARM_SMCCC_HV_PV_TIME_FEATURES, ARM_SMCCC_HV_PV_TIME_ST),
>>> +     TEST_HVC_INFO(ARM_SMCCC_HV_PV_TIME_ST, 0),
>>> +
>>> +     /* KVM_REG_ARM_VENDOR_HYP_BMAP */
>>> +     TEST_HVC_INFO(ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID,
>>> +                     ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID),
>>> +     TEST_HVC_INFO(ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID, KVM_PTP_VIRT_COUNTER),
>>> +};
>>> +
>>> +/* Feed false hypercall info to test the KVM behavior */
>>> +static const struct test_hvc_info false_hvc_info[] = {
>>> +     /* Feature support check against a different family of hypercalls */
>>> +     TEST_HVC_INFO(ARM_SMCCC_TRNG_FEATURES, ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID),
>>> +     TEST_HVC_INFO(ARM_SMCCC_ARCH_FEATURES_FUNC_ID, ARM_SMCCC_TRNG_RND64),
>>> +     TEST_HVC_INFO(ARM_SMCCC_HV_PV_TIME_FEATURES, ARM_SMCCC_TRNG_RND64),
>>> +};
>>> +
>>> +static void guest_test_hvc(const struct test_hvc_info *hc_info)
>>> +{
>>> +     unsigned int i;
>>> +     struct arm_smccc_res res;
>>> +     unsigned int hvc_info_arr_sz;
>>> +
>>> +     hvc_info_arr_sz =
>>> +     hc_info == hvc_info ? ARRAY_SIZE(hvc_info) : ARRAY_SIZE(false_hvc_info);
>>> +
>>> +     for (i = 0; i < hvc_info_arr_sz; i++, hc_info++) {
>>> +
>>> +             memset(&res, 0, sizeof(res));
>>> +             smccc_hvc(hc_info->func_id, hc_info->arg0, 0, 0, 0, 0, 0, 0, &res);
>>> +
>>
>> Unnecessary empty line before memset(). I don't find where smccc_hvc()
>> is defined.
>>
> I can clear the line and for the definition of smccc_hvc(), I applied
> Oliver's patch [1].
> 
>>> +             switch (stage) {
>>> +             case TEST_STAGE_HVC_IFACE_FEAT_DISABLED:
>>> +             case TEST_STAGE_HVC_IFACE_FALSE_INFO:
>>> +                     GUEST_ASSERT_3(res.a0 == SMCCC_RET_NOT_SUPPORTED,
>>> +                                     res.a0, hc_info->func_id, hc_info->arg0);
>>                                          ^^
>>
>> It seems the code here isn't properly aligned. Maybe it's your
>> preference :)
>>
> I think my editor is acting weird. I'll check again. Thanks for catching this!
> 
>>> +                     break;
>>> +             case TEST_STAGE_HVC_IFACE_FEAT_ENABLED:
>>> +                     GUEST_ASSERT_3(res.a0 != SMCCC_RET_NOT_SUPPORTED,
>>> +                                     res.a0, hc_info->func_id, hc_info->arg0);
>>> +                     break;
>>> +             default:
>>> +                     GUEST_ASSERT_1(0, stage);
>>> +             }
>>> +     }
>>> +}
>>> +
>>> +static void guest_code(void)
>>> +{
>>> +     while (stage != TEST_STAGE_END) {
>>> +             switch (stage) {
>>> +             case TEST_STAGE_REG_IFACE:
>>> +                     break;
>>> +             case TEST_STAGE_HVC_IFACE_FEAT_DISABLED:
>>> +             case TEST_STAGE_HVC_IFACE_FEAT_ENABLED:
>>> +                     guest_test_hvc(hvc_info);
>>> +                     break;
>>> +             case TEST_STAGE_HVC_IFACE_FALSE_INFO:
>>> +                     guest_test_hvc(false_hvc_info);
>>> +                     break;
>>> +             default:
>>> +                     GUEST_ASSERT_1(0, stage);
>>> +             }
>>> +
>>> +             GUEST_SYNC(stage);
>>> +     }
>>> +
>>> +     GUEST_DONE();
>>> +}
>>> +
>>> +static int set_fw_reg(struct kvm_vm *vm, uint64_t id, uint64_t val)
>>> +{
>>> +     struct kvm_one_reg reg = {
>>> +             .id = id,
>>> +             .addr = (uint64_t)&val,
>>> +     };
>>> +
>>> +     return _vcpu_ioctl(vm, 0, KVM_SET_ONE_REG, &reg);
>>> +}
>>> +
>>> +static void get_fw_reg(struct kvm_vm *vm, uint64_t id, uint64_t *addr)
>>> +{
>>> +     struct kvm_one_reg reg = {
>>> +             .id = id,
>>> +             .addr = (uint64_t)addr,
>>> +     };
>>> +
>>> +     vcpu_ioctl(vm, 0, KVM_GET_ONE_REG, &reg);
>>> +}
>>> +
>>> +struct st_time {
>>> +     uint32_t rev;
>>> +     uint32_t attr;
>>> +     uint64_t st_time;
>>> +};
>>> +
>>> +#define STEAL_TIME_SIZE              ((sizeof(struct st_time) + 63) & ~63)
>>> +#define ST_GPA_BASE          (1 << 30)
>>> +
>>> +static void steal_time_init(struct kvm_vm *vm)
>>> +{
>>> +     uint64_t st_ipa = (ulong)ST_GPA_BASE;
>>> +     unsigned int gpages;
>>> +     struct kvm_device_attr dev = {
>>> +             .group = KVM_ARM_VCPU_PVTIME_CTRL,
>>> +             .attr = KVM_ARM_VCPU_PVTIME_IPA,
>>> +             .addr = (uint64_t)&st_ipa,
>>> +     };
>>> +
>>> +     gpages = vm_calc_num_guest_pages(VM_MODE_DEFAULT, STEAL_TIME_SIZE);
>>> +     vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, ST_GPA_BASE, 1, gpages, 0);
>>> +
>>> +     vcpu_ioctl(vm, 0, KVM_SET_DEVICE_ATTR, &dev);
>>> +}
>>> +
>>
>> It might be helpful to do TEST_FAIL() on error returned from
>> this vcpu_ioctl(), or skip those PVTIME SMCCC calls accordingly
>> if the attribute isn't set successfully.
>>
> vcpu_ioctl() does a TEST_ASSERT() for us. Of course we can check it
> ourselves and skip if needed, but don't you think that may go
> unnoticed should any future changes tries to mess with
> steal_time_init() incorrectly and we'd end up skipping the test
> forever until we really notice skipped test?
> 

Yes, I missed the TEST_ASSERT() in vcpu_ioctl(). In this case,
we're safe and please ignore this comment :)

>>> +static void test_fw_regs_before_vm_start(struct kvm_vm *vm)
>>> +{
>>> +     uint64_t val;
>>> +     unsigned int i;
>>> +     int ret;
>>> +
>>> +     for (i = 0; i < ARRAY_SIZE(fw_reg_info); i++) {
>>> +             const struct kvm_fw_reg_info *reg_info = &fw_reg_info[i];
>>> +
>>> +             /* First 'read' should be an upper limit of the features supported */
>>> +             get_fw_reg(vm, reg_info->reg, &val);
>>> +             TEST_ASSERT(val == FW_REG_ULIMIT_VAL(reg_info->max_feat_bit),
>>> +                     "Expected all the features to be set for reg: 0x%lx; expected: 0x%llx; read: 0x%lx\n",
>>> +                     reg_info->reg, GENMASK_ULL(reg_info->max_feat_bit, 0), val);
>>> +
>>
>> s/GENMASK_ULL(...)/FW_REG_ULIMIT_VAL(...)
>>
> Right, that's better.
> 
>>> +             /* Test a 'write' by disabling all the features of the register map */
>>> +             ret = set_fw_reg(vm, reg_info->reg, 0);
>>> +             TEST_ASSERT(ret == 0,
>>> +                     "Failed to clear all the features of reg: 0x%lx; ret: %d\n",
>>> +                     reg_info->reg, errno);
>>> +
>>> +             get_fw_reg(vm, reg_info->reg, &val);
>>> +             TEST_ASSERT(val == 0,
>>> +                     "Expected all the features to be cleared for reg: 0x%lx\n", reg_info->reg);
>>> +
>>> +             /*
>>> +              * Test enabling a feature that's not supported.
>>> +              * Avoid this check if all the bits are occupied.
>>> +              */
>>> +             if (reg_info->max_feat_bit < 63) {
>>> +                     ret = set_fw_reg(vm, reg_info->reg, BIT(reg_info->max_feat_bit + 1));
>>> +                     TEST_ASSERT(ret != 0 && errno == EINVAL,
>>> +                     "Unexpected behavior or return value (%d) while setting an unsupported feature for reg: 0x%lx\n",
>>> +                     errno, reg_info->reg);
>>> +             }
>>> +     }
>>> +}
>>> +
>>> +static void test_fw_regs_after_vm_start(struct kvm_vm *vm)
>>> +{
>>> +     uint64_t val;
>>> +     unsigned int i;
>>> +     int ret;
>>> +
>>> +     for (i = 0; i < ARRAY_SIZE(fw_reg_info); i++) {
>>> +             const struct kvm_fw_reg_info *reg_info = &fw_reg_info[i];
>>> +
>>> +             /*
>>> +              * Before starting the VM, the test clears all the bits.
>>> +              * Check if that's still the case.
>>> +              */
>>> +             get_fw_reg(vm, reg_info->reg, &val);
>>> +             TEST_ASSERT(val == 0,
>>> +                     "Expected all the features to be cleared for reg: 0x%lx\n",
>>> +                     reg_info->reg);
>>> +
>>> +             /*
>>> +              * Test setting the last read value. KVM should allow this
>>> +              * even if VM has started running.
>>> +              */
>>> +             ret = set_fw_reg(vm, reg_info->reg, val);
>>> +             TEST_ASSERT(ret == 0,
>>> +                     "Failed to set the register with previously read value after Vm start for reg: 0x%lx; ret: %d\n",
>>> +                     reg_info->reg, errno);
>>> +
>>> +             /*
>>> +              * Set all the features for this register again. KVM shouldn't
>>> +              * allow this as the VM is running.
>>> +              */
>>> +             ret = set_fw_reg(vm, reg_info->reg, FW_REG_ULIMIT_VAL(reg_info->max_feat_bit));
>>> +             TEST_ASSERT(ret != 0 && errno == EBUSY,
>>> +             "Unexpected behavior or return value (%d) while setting a feature while VM is running for reg: 0x%lx\n",
>>> +             errno, reg_info->reg);
>>> +     }
>>> +}
>>> +
>>> +static struct kvm_vm *test_vm_create(void)
>>> +{
>>> +     struct kvm_vm *vm;
>>> +
>>> +     vm = vm_create_default(0, 0, guest_code);
>>> +
>>> +     ucall_init(vm, NULL);
>>> +     steal_time_init(vm);
>>> +
>>> +     return vm;
>>> +}
>>> +
>>> +static struct kvm_vm *test_guest_stage(struct kvm_vm *vm)
>>> +{
>>> +     struct kvm_vm *ret_vm = vm;
>>> +
>>> +     pr_debug("Stage: %d\n", stage);
>>> +
>>> +     switch (stage) {
>>> +     case TEST_STAGE_REG_IFACE:
>>> +             test_fw_regs_after_vm_start(vm);
>>> +             break;
>>> +     case TEST_STAGE_HVC_IFACE_FEAT_DISABLED:
>>> +             /* Start a new VM so that all the features are now enabled by default */
>>> +             kvm_vm_free(vm);
>>> +             ret_vm = test_vm_create();
>>> +             break;
>>> +     case TEST_STAGE_HVC_IFACE_FEAT_ENABLED:
>>> +     case TEST_STAGE_HVC_IFACE_FALSE_INFO:
>>> +             break;
>>> +     default:
>>> +             TEST_FAIL("Unknown test stage: %d\n", stage);
>>> +     }
>>> +
>>> +     stage++;
>>> +     sync_global_to_guest(vm, stage);
>>> +
>>> +     return ret_vm;
>>> +}
>>> +
>>> +static void test_run(void)
>>> +{
>>> +     struct kvm_vm *vm;
>>> +     struct ucall uc;
>>> +     bool guest_done = false;
>>> +
>>> +     vm = test_vm_create();
>>> +
>>> +     test_fw_regs_before_vm_start(vm);
>>> +
>>> +     while (!guest_done) {
>>> +             vcpu_run(vm, 0);
>>> +
>>> +             switch (get_ucall(vm, 0, &uc)) {
>>> +             case UCALL_SYNC:
>>> +                     vm = test_guest_stage(vm);
>>> +                     break;
>>> +             case UCALL_DONE:
>>> +                     guest_done = true;
>>> +                     break;
>>> +             case UCALL_ABORT:
>>> +                     TEST_FAIL("%s at %s:%ld\n\tvalues: 0x%lx, 0x%lx; 0x%lx, stage: %u",
>>> +                     (const char *)uc.args[0], __FILE__, uc.args[1],
>>> +                     uc.args[2], uc.args[3], uc.args[4], stage);
>>> +                     break;
>>> +             default:
>>> +                     TEST_FAIL("Unexpected guest exit\n");
>>> +             }
>>> +     }
>>> +
>>> +     kvm_vm_free(vm);
>>> +}
>>> +
>>> +int main(void)
>>> +{
>>> +     setbuf(stdout, NULL);
>>> +
>>> +     test_run();
>>> +     return 0;
>>> +}
> 
> Thanks for taking the time to review.
> 

No worries and sorry for late chime-in.> 
> [1]: https://lore.kernel.org/kvmarm/20220409184549.1681189-11-oupton@google.com/T/#u
> 

Thanks,
Gavin


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

* Re: [PATCH v5 08/10] selftests: KVM: aarch64: Introduce hypercall ABI test
@ 2022-04-14  1:09         ` Gavin Shan
  0 siblings, 0 replies; 96+ messages in thread
From: Gavin Shan @ 2022-04-14  1:09 UTC (permalink / raw)
  To: Raghavendra Rao Ananta, Oliver Upton
  Cc: kvm, Marc Zyngier, Peter Shier, linux-kernel, Will Deacon,
	Catalin Marinas, Paolo Bonzini, kvmarm, linux-arm-kernel

Hi Raghavendra,

On 4/14/22 1:32 AM, Raghavendra Rao Ananta wrote:
> On Wed, Apr 13, 2022 at 2:07 AM Gavin Shan <gshan@redhat.com> wrote:
>> On 4/7/22 9:16 AM, Raghavendra Rao Ananta wrote:
>>> Introduce a KVM selftest to check the hypercall interface
>>> for arm64 platforms. The test validates the user-space's
>>> IOCTL interface to read/write the psuedo-firmware registers
>>> as well as its effects on the guest upon certain configurations.
>>>
>>> Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
>>> ---
>>>    tools/testing/selftests/kvm/.gitignore        |   1 +
>>>    tools/testing/selftests/kvm/Makefile          |   1 +
>>>    .../selftests/kvm/aarch64/hypercalls.c        | 344 ++++++++++++++++++
>>>    3 files changed, 346 insertions(+)
>>>    create mode 100644 tools/testing/selftests/kvm/aarch64/hypercalls.c
>>>
>>
>> To be more precise, s/IOCTL/{GET,SET}_ONE_REG ?
>>
> Sure, I think that'll be better.
> 
>>> diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore
>>> index e82b816a6608..7ef52b3b1560 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/hypercalls
>>>    /aarch64/psci_test
>>>    /aarch64/vgic_init
>>>    /aarch64/vgic_irq
>>> diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
>>> index 2f74f502de65..af4cb88bcf83 100644
>>> --- a/tools/testing/selftests/kvm/Makefile
>>> +++ b/tools/testing/selftests/kvm/Makefile
>>> @@ -105,6 +105,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/hypercalls
>>>    TEST_GEN_PROGS_aarch64 += aarch64/psci_test
>>>    TEST_GEN_PROGS_aarch64 += aarch64/vgic_init
>>>    TEST_GEN_PROGS_aarch64 += aarch64/vgic_irq
>>> diff --git a/tools/testing/selftests/kvm/aarch64/hypercalls.c b/tools/testing/selftests/kvm/aarch64/hypercalls.c
>>> new file mode 100644
>>> index 000000000000..9941fb75772a
>>> --- /dev/null
>>> +++ b/tools/testing/selftests/kvm/aarch64/hypercalls.c
>>> @@ -0,0 +1,344 @@
>>> +// SPDX-License-Identifier: GPL-2.0-only
>>> +
>>> +/* hypercalls: Check the ARM64's psuedo-firmware bitmap register interface.
>>> + *
>>> + * The test validates the basic hypercall functionalities that are exposed
>>> + * via the psuedo-firmware bitmap register. This includes the registers'
>>> + * read/write behavior before and after the VM has started, and if the
>>> + * hypercalls are properly masked or unmasked to the guest when disabled or
>>> + * enabled from the KVM userspace, respectively.
>>> + */
>>> +
>>> +#include <errno.h>
>>> +#include <linux/arm-smccc.h>
>>> +#include <asm/kvm.h>
>>> +#include <kvm_util.h>
>>> +
>>> +#include "processor.h"
>>> +
>>> +#define FW_REG_ULIMIT_VAL(max_feat_bit) (GENMASK_ULL(max_feat_bit, 0))
>>> +
>>> +/* Last valid bits of the bitmapped firmware registers */
>>> +#define KVM_REG_ARM_STD_BMAP_BIT_MAX         0
>>> +#define KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX     0
>>> +#define KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX  1
>>> +
>>> +struct kvm_fw_reg_info {
>>> +     uint64_t reg;           /* Register definition */
>>> +     uint64_t max_feat_bit;  /* Bit that represents the upper limit of the feature-map */
>>> +};
>>> +
>>> +#define FW_REG_INFO(r, bit_max)                      \
>>> +     {                                       \
>>> +             .reg = r,                       \
>>> +             .max_feat_bit = bit_max,        \
>>> +     }
>>> +
>>> +static const struct kvm_fw_reg_info fw_reg_info[] = {
>>> +     FW_REG_INFO(KVM_REG_ARM_STD_BMAP, KVM_REG_ARM_STD_BMAP_BIT_MAX),
>>> +     FW_REG_INFO(KVM_REG_ARM_STD_HYP_BMAP, KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX),
>>> +     FW_REG_INFO(KVM_REG_ARM_VENDOR_HYP_BMAP, KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX),
>>> +};
>>> +
>>
>> This can be simplifed by:
>>
>> #define FW_REG_INFO(r)                  \
>>          { .reg = r,                     \
>>            .max_feat_bit = r_##BIT_MAX,  \
>>          }
>>
>> static const struct kvm_fw_reg_info fw_reg_info[] = {
>>          FW_REG_INFO(KVM_REG_ARM_STD_BMAP),
>>          FW_REG_INFO(KVM_REG_ARM_STD_HYP_BMAP),
>>          FW_REG_INFO(KVM_REG_ARM_VENDOR_HYP_BMAP),
>> };
>>
> Yes, probably that looks better. Thanks for the suggestion.
> 
>>> +enum test_stage {
>>> +     TEST_STAGE_REG_IFACE,
>>> +     TEST_STAGE_HVC_IFACE_FEAT_DISABLED,
>>> +     TEST_STAGE_HVC_IFACE_FEAT_ENABLED,
>>> +     TEST_STAGE_HVC_IFACE_FALSE_INFO,
>>> +     TEST_STAGE_END,
>>> +};
>>> +
>>> +static int stage;
>>> +
>>
>> I think it'd better to initialize @stage to TEST_STAGE_REG_IFACE.
>>
> Will do.
>>> +struct test_hvc_info {
>>> +     uint32_t func_id;
>>> +     int64_t arg0;
>>> +};
>>> +
>>> +#define TEST_HVC_INFO(f, a0) \
>>> +     {                       \
>>> +             .func_id = f,   \
>>> +             .arg0 = a0,     \
>>> +     }
>>> +
>>
>> According to those functions (smccc_get_{function, argx}()) defined
>> in include/kvm/arm_hypercalls.h, @arg0 would have type of "uint64_t"
>> if I'm correct. Besides, @func_id is arg0 and arg0 should be arg1?
>> So if I'm correct, this would be:
>>
>> struct test_hvc_info {
>>          uint32_t func_id;
>>          uint64_t arg1
>> };
>>
> Thanks for noticing this! I'll change it to 'unit64'. Regarding the
> argument naming, I understand that it's a little confusing. I went
> with 'arg0' to align with the selftest library's convention. But, I
> agree that it's not aligned with what the kernel is used to.
> 
> Oliver, do you think we can start the argument naming from a1/arg1 in [1]?
> 
>>> +static const struct test_hvc_info hvc_info[] = {
>>> +     /* KVM_REG_ARM_STD_BMAP */
>>> +     TEST_HVC_INFO(ARM_SMCCC_TRNG_VERSION, 0),
>>> +     TEST_HVC_INFO(ARM_SMCCC_TRNG_FEATURES, ARM_SMCCC_TRNG_RND64),
>>> +     TEST_HVC_INFO(ARM_SMCCC_TRNG_GET_UUID, 0),
>>> +     TEST_HVC_INFO(ARM_SMCCC_TRNG_RND32, 0),
>>> +     TEST_HVC_INFO(ARM_SMCCC_TRNG_RND64, 0),
>>> +
>>> +     /* KVM_REG_ARM_STD_HYP_BMAP */
>>> +     TEST_HVC_INFO(ARM_SMCCC_ARCH_FEATURES_FUNC_ID, ARM_SMCCC_HV_PV_TIME_FEATURES),
>>> +     TEST_HVC_INFO(ARM_SMCCC_HV_PV_TIME_FEATURES, ARM_SMCCC_HV_PV_TIME_ST),
>>> +     TEST_HVC_INFO(ARM_SMCCC_HV_PV_TIME_ST, 0),
>>> +
>>> +     /* KVM_REG_ARM_VENDOR_HYP_BMAP */
>>> +     TEST_HVC_INFO(ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID,
>>> +                     ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID),
>>> +     TEST_HVC_INFO(ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID, KVM_PTP_VIRT_COUNTER),
>>> +};
>>> +
>>> +/* Feed false hypercall info to test the KVM behavior */
>>> +static const struct test_hvc_info false_hvc_info[] = {
>>> +     /* Feature support check against a different family of hypercalls */
>>> +     TEST_HVC_INFO(ARM_SMCCC_TRNG_FEATURES, ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID),
>>> +     TEST_HVC_INFO(ARM_SMCCC_ARCH_FEATURES_FUNC_ID, ARM_SMCCC_TRNG_RND64),
>>> +     TEST_HVC_INFO(ARM_SMCCC_HV_PV_TIME_FEATURES, ARM_SMCCC_TRNG_RND64),
>>> +};
>>> +
>>> +static void guest_test_hvc(const struct test_hvc_info *hc_info)
>>> +{
>>> +     unsigned int i;
>>> +     struct arm_smccc_res res;
>>> +     unsigned int hvc_info_arr_sz;
>>> +
>>> +     hvc_info_arr_sz =
>>> +     hc_info == hvc_info ? ARRAY_SIZE(hvc_info) : ARRAY_SIZE(false_hvc_info);
>>> +
>>> +     for (i = 0; i < hvc_info_arr_sz; i++, hc_info++) {
>>> +
>>> +             memset(&res, 0, sizeof(res));
>>> +             smccc_hvc(hc_info->func_id, hc_info->arg0, 0, 0, 0, 0, 0, 0, &res);
>>> +
>>
>> Unnecessary empty line before memset(). I don't find where smccc_hvc()
>> is defined.
>>
> I can clear the line and for the definition of smccc_hvc(), I applied
> Oliver's patch [1].
> 
>>> +             switch (stage) {
>>> +             case TEST_STAGE_HVC_IFACE_FEAT_DISABLED:
>>> +             case TEST_STAGE_HVC_IFACE_FALSE_INFO:
>>> +                     GUEST_ASSERT_3(res.a0 == SMCCC_RET_NOT_SUPPORTED,
>>> +                                     res.a0, hc_info->func_id, hc_info->arg0);
>>                                          ^^
>>
>> It seems the code here isn't properly aligned. Maybe it's your
>> preference :)
>>
> I think my editor is acting weird. I'll check again. Thanks for catching this!
> 
>>> +                     break;
>>> +             case TEST_STAGE_HVC_IFACE_FEAT_ENABLED:
>>> +                     GUEST_ASSERT_3(res.a0 != SMCCC_RET_NOT_SUPPORTED,
>>> +                                     res.a0, hc_info->func_id, hc_info->arg0);
>>> +                     break;
>>> +             default:
>>> +                     GUEST_ASSERT_1(0, stage);
>>> +             }
>>> +     }
>>> +}
>>> +
>>> +static void guest_code(void)
>>> +{
>>> +     while (stage != TEST_STAGE_END) {
>>> +             switch (stage) {
>>> +             case TEST_STAGE_REG_IFACE:
>>> +                     break;
>>> +             case TEST_STAGE_HVC_IFACE_FEAT_DISABLED:
>>> +             case TEST_STAGE_HVC_IFACE_FEAT_ENABLED:
>>> +                     guest_test_hvc(hvc_info);
>>> +                     break;
>>> +             case TEST_STAGE_HVC_IFACE_FALSE_INFO:
>>> +                     guest_test_hvc(false_hvc_info);
>>> +                     break;
>>> +             default:
>>> +                     GUEST_ASSERT_1(0, stage);
>>> +             }
>>> +
>>> +             GUEST_SYNC(stage);
>>> +     }
>>> +
>>> +     GUEST_DONE();
>>> +}
>>> +
>>> +static int set_fw_reg(struct kvm_vm *vm, uint64_t id, uint64_t val)
>>> +{
>>> +     struct kvm_one_reg reg = {
>>> +             .id = id,
>>> +             .addr = (uint64_t)&val,
>>> +     };
>>> +
>>> +     return _vcpu_ioctl(vm, 0, KVM_SET_ONE_REG, &reg);
>>> +}
>>> +
>>> +static void get_fw_reg(struct kvm_vm *vm, uint64_t id, uint64_t *addr)
>>> +{
>>> +     struct kvm_one_reg reg = {
>>> +             .id = id,
>>> +             .addr = (uint64_t)addr,
>>> +     };
>>> +
>>> +     vcpu_ioctl(vm, 0, KVM_GET_ONE_REG, &reg);
>>> +}
>>> +
>>> +struct st_time {
>>> +     uint32_t rev;
>>> +     uint32_t attr;
>>> +     uint64_t st_time;
>>> +};
>>> +
>>> +#define STEAL_TIME_SIZE              ((sizeof(struct st_time) + 63) & ~63)
>>> +#define ST_GPA_BASE          (1 << 30)
>>> +
>>> +static void steal_time_init(struct kvm_vm *vm)
>>> +{
>>> +     uint64_t st_ipa = (ulong)ST_GPA_BASE;
>>> +     unsigned int gpages;
>>> +     struct kvm_device_attr dev = {
>>> +             .group = KVM_ARM_VCPU_PVTIME_CTRL,
>>> +             .attr = KVM_ARM_VCPU_PVTIME_IPA,
>>> +             .addr = (uint64_t)&st_ipa,
>>> +     };
>>> +
>>> +     gpages = vm_calc_num_guest_pages(VM_MODE_DEFAULT, STEAL_TIME_SIZE);
>>> +     vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, ST_GPA_BASE, 1, gpages, 0);
>>> +
>>> +     vcpu_ioctl(vm, 0, KVM_SET_DEVICE_ATTR, &dev);
>>> +}
>>> +
>>
>> It might be helpful to do TEST_FAIL() on error returned from
>> this vcpu_ioctl(), or skip those PVTIME SMCCC calls accordingly
>> if the attribute isn't set successfully.
>>
> vcpu_ioctl() does a TEST_ASSERT() for us. Of course we can check it
> ourselves and skip if needed, but don't you think that may go
> unnoticed should any future changes tries to mess with
> steal_time_init() incorrectly and we'd end up skipping the test
> forever until we really notice skipped test?
> 

Yes, I missed the TEST_ASSERT() in vcpu_ioctl(). In this case,
we're safe and please ignore this comment :)

>>> +static void test_fw_regs_before_vm_start(struct kvm_vm *vm)
>>> +{
>>> +     uint64_t val;
>>> +     unsigned int i;
>>> +     int ret;
>>> +
>>> +     for (i = 0; i < ARRAY_SIZE(fw_reg_info); i++) {
>>> +             const struct kvm_fw_reg_info *reg_info = &fw_reg_info[i];
>>> +
>>> +             /* First 'read' should be an upper limit of the features supported */
>>> +             get_fw_reg(vm, reg_info->reg, &val);
>>> +             TEST_ASSERT(val == FW_REG_ULIMIT_VAL(reg_info->max_feat_bit),
>>> +                     "Expected all the features to be set for reg: 0x%lx; expected: 0x%llx; read: 0x%lx\n",
>>> +                     reg_info->reg, GENMASK_ULL(reg_info->max_feat_bit, 0), val);
>>> +
>>
>> s/GENMASK_ULL(...)/FW_REG_ULIMIT_VAL(...)
>>
> Right, that's better.
> 
>>> +             /* Test a 'write' by disabling all the features of the register map */
>>> +             ret = set_fw_reg(vm, reg_info->reg, 0);
>>> +             TEST_ASSERT(ret == 0,
>>> +                     "Failed to clear all the features of reg: 0x%lx; ret: %d\n",
>>> +                     reg_info->reg, errno);
>>> +
>>> +             get_fw_reg(vm, reg_info->reg, &val);
>>> +             TEST_ASSERT(val == 0,
>>> +                     "Expected all the features to be cleared for reg: 0x%lx\n", reg_info->reg);
>>> +
>>> +             /*
>>> +              * Test enabling a feature that's not supported.
>>> +              * Avoid this check if all the bits are occupied.
>>> +              */
>>> +             if (reg_info->max_feat_bit < 63) {
>>> +                     ret = set_fw_reg(vm, reg_info->reg, BIT(reg_info->max_feat_bit + 1));
>>> +                     TEST_ASSERT(ret != 0 && errno == EINVAL,
>>> +                     "Unexpected behavior or return value (%d) while setting an unsupported feature for reg: 0x%lx\n",
>>> +                     errno, reg_info->reg);
>>> +             }
>>> +     }
>>> +}
>>> +
>>> +static void test_fw_regs_after_vm_start(struct kvm_vm *vm)
>>> +{
>>> +     uint64_t val;
>>> +     unsigned int i;
>>> +     int ret;
>>> +
>>> +     for (i = 0; i < ARRAY_SIZE(fw_reg_info); i++) {
>>> +             const struct kvm_fw_reg_info *reg_info = &fw_reg_info[i];
>>> +
>>> +             /*
>>> +              * Before starting the VM, the test clears all the bits.
>>> +              * Check if that's still the case.
>>> +              */
>>> +             get_fw_reg(vm, reg_info->reg, &val);
>>> +             TEST_ASSERT(val == 0,
>>> +                     "Expected all the features to be cleared for reg: 0x%lx\n",
>>> +                     reg_info->reg);
>>> +
>>> +             /*
>>> +              * Test setting the last read value. KVM should allow this
>>> +              * even if VM has started running.
>>> +              */
>>> +             ret = set_fw_reg(vm, reg_info->reg, val);
>>> +             TEST_ASSERT(ret == 0,
>>> +                     "Failed to set the register with previously read value after Vm start for reg: 0x%lx; ret: %d\n",
>>> +                     reg_info->reg, errno);
>>> +
>>> +             /*
>>> +              * Set all the features for this register again. KVM shouldn't
>>> +              * allow this as the VM is running.
>>> +              */
>>> +             ret = set_fw_reg(vm, reg_info->reg, FW_REG_ULIMIT_VAL(reg_info->max_feat_bit));
>>> +             TEST_ASSERT(ret != 0 && errno == EBUSY,
>>> +             "Unexpected behavior or return value (%d) while setting a feature while VM is running for reg: 0x%lx\n",
>>> +             errno, reg_info->reg);
>>> +     }
>>> +}
>>> +
>>> +static struct kvm_vm *test_vm_create(void)
>>> +{
>>> +     struct kvm_vm *vm;
>>> +
>>> +     vm = vm_create_default(0, 0, guest_code);
>>> +
>>> +     ucall_init(vm, NULL);
>>> +     steal_time_init(vm);
>>> +
>>> +     return vm;
>>> +}
>>> +
>>> +static struct kvm_vm *test_guest_stage(struct kvm_vm *vm)
>>> +{
>>> +     struct kvm_vm *ret_vm = vm;
>>> +
>>> +     pr_debug("Stage: %d\n", stage);
>>> +
>>> +     switch (stage) {
>>> +     case TEST_STAGE_REG_IFACE:
>>> +             test_fw_regs_after_vm_start(vm);
>>> +             break;
>>> +     case TEST_STAGE_HVC_IFACE_FEAT_DISABLED:
>>> +             /* Start a new VM so that all the features are now enabled by default */
>>> +             kvm_vm_free(vm);
>>> +             ret_vm = test_vm_create();
>>> +             break;
>>> +     case TEST_STAGE_HVC_IFACE_FEAT_ENABLED:
>>> +     case TEST_STAGE_HVC_IFACE_FALSE_INFO:
>>> +             break;
>>> +     default:
>>> +             TEST_FAIL("Unknown test stage: %d\n", stage);
>>> +     }
>>> +
>>> +     stage++;
>>> +     sync_global_to_guest(vm, stage);
>>> +
>>> +     return ret_vm;
>>> +}
>>> +
>>> +static void test_run(void)
>>> +{
>>> +     struct kvm_vm *vm;
>>> +     struct ucall uc;
>>> +     bool guest_done = false;
>>> +
>>> +     vm = test_vm_create();
>>> +
>>> +     test_fw_regs_before_vm_start(vm);
>>> +
>>> +     while (!guest_done) {
>>> +             vcpu_run(vm, 0);
>>> +
>>> +             switch (get_ucall(vm, 0, &uc)) {
>>> +             case UCALL_SYNC:
>>> +                     vm = test_guest_stage(vm);
>>> +                     break;
>>> +             case UCALL_DONE:
>>> +                     guest_done = true;
>>> +                     break;
>>> +             case UCALL_ABORT:
>>> +                     TEST_FAIL("%s at %s:%ld\n\tvalues: 0x%lx, 0x%lx; 0x%lx, stage: %u",
>>> +                     (const char *)uc.args[0], __FILE__, uc.args[1],
>>> +                     uc.args[2], uc.args[3], uc.args[4], stage);
>>> +                     break;
>>> +             default:
>>> +                     TEST_FAIL("Unexpected guest exit\n");
>>> +             }
>>> +     }
>>> +
>>> +     kvm_vm_free(vm);
>>> +}
>>> +
>>> +int main(void)
>>> +{
>>> +     setbuf(stdout, NULL);
>>> +
>>> +     test_run();
>>> +     return 0;
>>> +}
> 
> Thanks for taking the time to review.
> 

No worries and sorry for late chime-in.> 
> [1]: https://lore.kernel.org/kvmarm/20220409184549.1681189-11-oupton@google.com/T/#u
> 

Thanks,
Gavin

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

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

* Re: [PATCH v5 08/10] selftests: KVM: aarch64: Introduce hypercall ABI test
@ 2022-04-14  1:09         ` Gavin Shan
  0 siblings, 0 replies; 96+ messages in thread
From: Gavin Shan @ 2022-04-14  1:09 UTC (permalink / raw)
  To: Raghavendra Rao Ananta, Oliver Upton
  Cc: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose, kvm, Catalin Marinas, Peter Shier,
	linux-kernel, Paolo Bonzini, Will Deacon, kvmarm,
	linux-arm-kernel

Hi Raghavendra,

On 4/14/22 1:32 AM, Raghavendra Rao Ananta wrote:
> On Wed, Apr 13, 2022 at 2:07 AM Gavin Shan <gshan@redhat.com> wrote:
>> On 4/7/22 9:16 AM, Raghavendra Rao Ananta wrote:
>>> Introduce a KVM selftest to check the hypercall interface
>>> for arm64 platforms. The test validates the user-space's
>>> IOCTL interface to read/write the psuedo-firmware registers
>>> as well as its effects on the guest upon certain configurations.
>>>
>>> Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
>>> ---
>>>    tools/testing/selftests/kvm/.gitignore        |   1 +
>>>    tools/testing/selftests/kvm/Makefile          |   1 +
>>>    .../selftests/kvm/aarch64/hypercalls.c        | 344 ++++++++++++++++++
>>>    3 files changed, 346 insertions(+)
>>>    create mode 100644 tools/testing/selftests/kvm/aarch64/hypercalls.c
>>>
>>
>> To be more precise, s/IOCTL/{GET,SET}_ONE_REG ?
>>
> Sure, I think that'll be better.
> 
>>> diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore
>>> index e82b816a6608..7ef52b3b1560 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/hypercalls
>>>    /aarch64/psci_test
>>>    /aarch64/vgic_init
>>>    /aarch64/vgic_irq
>>> diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
>>> index 2f74f502de65..af4cb88bcf83 100644
>>> --- a/tools/testing/selftests/kvm/Makefile
>>> +++ b/tools/testing/selftests/kvm/Makefile
>>> @@ -105,6 +105,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/hypercalls
>>>    TEST_GEN_PROGS_aarch64 += aarch64/psci_test
>>>    TEST_GEN_PROGS_aarch64 += aarch64/vgic_init
>>>    TEST_GEN_PROGS_aarch64 += aarch64/vgic_irq
>>> diff --git a/tools/testing/selftests/kvm/aarch64/hypercalls.c b/tools/testing/selftests/kvm/aarch64/hypercalls.c
>>> new file mode 100644
>>> index 000000000000..9941fb75772a
>>> --- /dev/null
>>> +++ b/tools/testing/selftests/kvm/aarch64/hypercalls.c
>>> @@ -0,0 +1,344 @@
>>> +// SPDX-License-Identifier: GPL-2.0-only
>>> +
>>> +/* hypercalls: Check the ARM64's psuedo-firmware bitmap register interface.
>>> + *
>>> + * The test validates the basic hypercall functionalities that are exposed
>>> + * via the psuedo-firmware bitmap register. This includes the registers'
>>> + * read/write behavior before and after the VM has started, and if the
>>> + * hypercalls are properly masked or unmasked to the guest when disabled or
>>> + * enabled from the KVM userspace, respectively.
>>> + */
>>> +
>>> +#include <errno.h>
>>> +#include <linux/arm-smccc.h>
>>> +#include <asm/kvm.h>
>>> +#include <kvm_util.h>
>>> +
>>> +#include "processor.h"
>>> +
>>> +#define FW_REG_ULIMIT_VAL(max_feat_bit) (GENMASK_ULL(max_feat_bit, 0))
>>> +
>>> +/* Last valid bits of the bitmapped firmware registers */
>>> +#define KVM_REG_ARM_STD_BMAP_BIT_MAX         0
>>> +#define KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX     0
>>> +#define KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX  1
>>> +
>>> +struct kvm_fw_reg_info {
>>> +     uint64_t reg;           /* Register definition */
>>> +     uint64_t max_feat_bit;  /* Bit that represents the upper limit of the feature-map */
>>> +};
>>> +
>>> +#define FW_REG_INFO(r, bit_max)                      \
>>> +     {                                       \
>>> +             .reg = r,                       \
>>> +             .max_feat_bit = bit_max,        \
>>> +     }
>>> +
>>> +static const struct kvm_fw_reg_info fw_reg_info[] = {
>>> +     FW_REG_INFO(KVM_REG_ARM_STD_BMAP, KVM_REG_ARM_STD_BMAP_BIT_MAX),
>>> +     FW_REG_INFO(KVM_REG_ARM_STD_HYP_BMAP, KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX),
>>> +     FW_REG_INFO(KVM_REG_ARM_VENDOR_HYP_BMAP, KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX),
>>> +};
>>> +
>>
>> This can be simplifed by:
>>
>> #define FW_REG_INFO(r)                  \
>>          { .reg = r,                     \
>>            .max_feat_bit = r_##BIT_MAX,  \
>>          }
>>
>> static const struct kvm_fw_reg_info fw_reg_info[] = {
>>          FW_REG_INFO(KVM_REG_ARM_STD_BMAP),
>>          FW_REG_INFO(KVM_REG_ARM_STD_HYP_BMAP),
>>          FW_REG_INFO(KVM_REG_ARM_VENDOR_HYP_BMAP),
>> };
>>
> Yes, probably that looks better. Thanks for the suggestion.
> 
>>> +enum test_stage {
>>> +     TEST_STAGE_REG_IFACE,
>>> +     TEST_STAGE_HVC_IFACE_FEAT_DISABLED,
>>> +     TEST_STAGE_HVC_IFACE_FEAT_ENABLED,
>>> +     TEST_STAGE_HVC_IFACE_FALSE_INFO,
>>> +     TEST_STAGE_END,
>>> +};
>>> +
>>> +static int stage;
>>> +
>>
>> I think it'd better to initialize @stage to TEST_STAGE_REG_IFACE.
>>
> Will do.
>>> +struct test_hvc_info {
>>> +     uint32_t func_id;
>>> +     int64_t arg0;
>>> +};
>>> +
>>> +#define TEST_HVC_INFO(f, a0) \
>>> +     {                       \
>>> +             .func_id = f,   \
>>> +             .arg0 = a0,     \
>>> +     }
>>> +
>>
>> According to those functions (smccc_get_{function, argx}()) defined
>> in include/kvm/arm_hypercalls.h, @arg0 would have type of "uint64_t"
>> if I'm correct. Besides, @func_id is arg0 and arg0 should be arg1?
>> So if I'm correct, this would be:
>>
>> struct test_hvc_info {
>>          uint32_t func_id;
>>          uint64_t arg1
>> };
>>
> Thanks for noticing this! I'll change it to 'unit64'. Regarding the
> argument naming, I understand that it's a little confusing. I went
> with 'arg0' to align with the selftest library's convention. But, I
> agree that it's not aligned with what the kernel is used to.
> 
> Oliver, do you think we can start the argument naming from a1/arg1 in [1]?
> 
>>> +static const struct test_hvc_info hvc_info[] = {
>>> +     /* KVM_REG_ARM_STD_BMAP */
>>> +     TEST_HVC_INFO(ARM_SMCCC_TRNG_VERSION, 0),
>>> +     TEST_HVC_INFO(ARM_SMCCC_TRNG_FEATURES, ARM_SMCCC_TRNG_RND64),
>>> +     TEST_HVC_INFO(ARM_SMCCC_TRNG_GET_UUID, 0),
>>> +     TEST_HVC_INFO(ARM_SMCCC_TRNG_RND32, 0),
>>> +     TEST_HVC_INFO(ARM_SMCCC_TRNG_RND64, 0),
>>> +
>>> +     /* KVM_REG_ARM_STD_HYP_BMAP */
>>> +     TEST_HVC_INFO(ARM_SMCCC_ARCH_FEATURES_FUNC_ID, ARM_SMCCC_HV_PV_TIME_FEATURES),
>>> +     TEST_HVC_INFO(ARM_SMCCC_HV_PV_TIME_FEATURES, ARM_SMCCC_HV_PV_TIME_ST),
>>> +     TEST_HVC_INFO(ARM_SMCCC_HV_PV_TIME_ST, 0),
>>> +
>>> +     /* KVM_REG_ARM_VENDOR_HYP_BMAP */
>>> +     TEST_HVC_INFO(ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID,
>>> +                     ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID),
>>> +     TEST_HVC_INFO(ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID, KVM_PTP_VIRT_COUNTER),
>>> +};
>>> +
>>> +/* Feed false hypercall info to test the KVM behavior */
>>> +static const struct test_hvc_info false_hvc_info[] = {
>>> +     /* Feature support check against a different family of hypercalls */
>>> +     TEST_HVC_INFO(ARM_SMCCC_TRNG_FEATURES, ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID),
>>> +     TEST_HVC_INFO(ARM_SMCCC_ARCH_FEATURES_FUNC_ID, ARM_SMCCC_TRNG_RND64),
>>> +     TEST_HVC_INFO(ARM_SMCCC_HV_PV_TIME_FEATURES, ARM_SMCCC_TRNG_RND64),
>>> +};
>>> +
>>> +static void guest_test_hvc(const struct test_hvc_info *hc_info)
>>> +{
>>> +     unsigned int i;
>>> +     struct arm_smccc_res res;
>>> +     unsigned int hvc_info_arr_sz;
>>> +
>>> +     hvc_info_arr_sz =
>>> +     hc_info == hvc_info ? ARRAY_SIZE(hvc_info) : ARRAY_SIZE(false_hvc_info);
>>> +
>>> +     for (i = 0; i < hvc_info_arr_sz; i++, hc_info++) {
>>> +
>>> +             memset(&res, 0, sizeof(res));
>>> +             smccc_hvc(hc_info->func_id, hc_info->arg0, 0, 0, 0, 0, 0, 0, &res);
>>> +
>>
>> Unnecessary empty line before memset(). I don't find where smccc_hvc()
>> is defined.
>>
> I can clear the line and for the definition of smccc_hvc(), I applied
> Oliver's patch [1].
> 
>>> +             switch (stage) {
>>> +             case TEST_STAGE_HVC_IFACE_FEAT_DISABLED:
>>> +             case TEST_STAGE_HVC_IFACE_FALSE_INFO:
>>> +                     GUEST_ASSERT_3(res.a0 == SMCCC_RET_NOT_SUPPORTED,
>>> +                                     res.a0, hc_info->func_id, hc_info->arg0);
>>                                          ^^
>>
>> It seems the code here isn't properly aligned. Maybe it's your
>> preference :)
>>
> I think my editor is acting weird. I'll check again. Thanks for catching this!
> 
>>> +                     break;
>>> +             case TEST_STAGE_HVC_IFACE_FEAT_ENABLED:
>>> +                     GUEST_ASSERT_3(res.a0 != SMCCC_RET_NOT_SUPPORTED,
>>> +                                     res.a0, hc_info->func_id, hc_info->arg0);
>>> +                     break;
>>> +             default:
>>> +                     GUEST_ASSERT_1(0, stage);
>>> +             }
>>> +     }
>>> +}
>>> +
>>> +static void guest_code(void)
>>> +{
>>> +     while (stage != TEST_STAGE_END) {
>>> +             switch (stage) {
>>> +             case TEST_STAGE_REG_IFACE:
>>> +                     break;
>>> +             case TEST_STAGE_HVC_IFACE_FEAT_DISABLED:
>>> +             case TEST_STAGE_HVC_IFACE_FEAT_ENABLED:
>>> +                     guest_test_hvc(hvc_info);
>>> +                     break;
>>> +             case TEST_STAGE_HVC_IFACE_FALSE_INFO:
>>> +                     guest_test_hvc(false_hvc_info);
>>> +                     break;
>>> +             default:
>>> +                     GUEST_ASSERT_1(0, stage);
>>> +             }
>>> +
>>> +             GUEST_SYNC(stage);
>>> +     }
>>> +
>>> +     GUEST_DONE();
>>> +}
>>> +
>>> +static int set_fw_reg(struct kvm_vm *vm, uint64_t id, uint64_t val)
>>> +{
>>> +     struct kvm_one_reg reg = {
>>> +             .id = id,
>>> +             .addr = (uint64_t)&val,
>>> +     };
>>> +
>>> +     return _vcpu_ioctl(vm, 0, KVM_SET_ONE_REG, &reg);
>>> +}
>>> +
>>> +static void get_fw_reg(struct kvm_vm *vm, uint64_t id, uint64_t *addr)
>>> +{
>>> +     struct kvm_one_reg reg = {
>>> +             .id = id,
>>> +             .addr = (uint64_t)addr,
>>> +     };
>>> +
>>> +     vcpu_ioctl(vm, 0, KVM_GET_ONE_REG, &reg);
>>> +}
>>> +
>>> +struct st_time {
>>> +     uint32_t rev;
>>> +     uint32_t attr;
>>> +     uint64_t st_time;
>>> +};
>>> +
>>> +#define STEAL_TIME_SIZE              ((sizeof(struct st_time) + 63) & ~63)
>>> +#define ST_GPA_BASE          (1 << 30)
>>> +
>>> +static void steal_time_init(struct kvm_vm *vm)
>>> +{
>>> +     uint64_t st_ipa = (ulong)ST_GPA_BASE;
>>> +     unsigned int gpages;
>>> +     struct kvm_device_attr dev = {
>>> +             .group = KVM_ARM_VCPU_PVTIME_CTRL,
>>> +             .attr = KVM_ARM_VCPU_PVTIME_IPA,
>>> +             .addr = (uint64_t)&st_ipa,
>>> +     };
>>> +
>>> +     gpages = vm_calc_num_guest_pages(VM_MODE_DEFAULT, STEAL_TIME_SIZE);
>>> +     vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, ST_GPA_BASE, 1, gpages, 0);
>>> +
>>> +     vcpu_ioctl(vm, 0, KVM_SET_DEVICE_ATTR, &dev);
>>> +}
>>> +
>>
>> It might be helpful to do TEST_FAIL() on error returned from
>> this vcpu_ioctl(), or skip those PVTIME SMCCC calls accordingly
>> if the attribute isn't set successfully.
>>
> vcpu_ioctl() does a TEST_ASSERT() for us. Of course we can check it
> ourselves and skip if needed, but don't you think that may go
> unnoticed should any future changes tries to mess with
> steal_time_init() incorrectly and we'd end up skipping the test
> forever until we really notice skipped test?
> 

Yes, I missed the TEST_ASSERT() in vcpu_ioctl(). In this case,
we're safe and please ignore this comment :)

>>> +static void test_fw_regs_before_vm_start(struct kvm_vm *vm)
>>> +{
>>> +     uint64_t val;
>>> +     unsigned int i;
>>> +     int ret;
>>> +
>>> +     for (i = 0; i < ARRAY_SIZE(fw_reg_info); i++) {
>>> +             const struct kvm_fw_reg_info *reg_info = &fw_reg_info[i];
>>> +
>>> +             /* First 'read' should be an upper limit of the features supported */
>>> +             get_fw_reg(vm, reg_info->reg, &val);
>>> +             TEST_ASSERT(val == FW_REG_ULIMIT_VAL(reg_info->max_feat_bit),
>>> +                     "Expected all the features to be set for reg: 0x%lx; expected: 0x%llx; read: 0x%lx\n",
>>> +                     reg_info->reg, GENMASK_ULL(reg_info->max_feat_bit, 0), val);
>>> +
>>
>> s/GENMASK_ULL(...)/FW_REG_ULIMIT_VAL(...)
>>
> Right, that's better.
> 
>>> +             /* Test a 'write' by disabling all the features of the register map */
>>> +             ret = set_fw_reg(vm, reg_info->reg, 0);
>>> +             TEST_ASSERT(ret == 0,
>>> +                     "Failed to clear all the features of reg: 0x%lx; ret: %d\n",
>>> +                     reg_info->reg, errno);
>>> +
>>> +             get_fw_reg(vm, reg_info->reg, &val);
>>> +             TEST_ASSERT(val == 0,
>>> +                     "Expected all the features to be cleared for reg: 0x%lx\n", reg_info->reg);
>>> +
>>> +             /*
>>> +              * Test enabling a feature that's not supported.
>>> +              * Avoid this check if all the bits are occupied.
>>> +              */
>>> +             if (reg_info->max_feat_bit < 63) {
>>> +                     ret = set_fw_reg(vm, reg_info->reg, BIT(reg_info->max_feat_bit + 1));
>>> +                     TEST_ASSERT(ret != 0 && errno == EINVAL,
>>> +                     "Unexpected behavior or return value (%d) while setting an unsupported feature for reg: 0x%lx\n",
>>> +                     errno, reg_info->reg);
>>> +             }
>>> +     }
>>> +}
>>> +
>>> +static void test_fw_regs_after_vm_start(struct kvm_vm *vm)
>>> +{
>>> +     uint64_t val;
>>> +     unsigned int i;
>>> +     int ret;
>>> +
>>> +     for (i = 0; i < ARRAY_SIZE(fw_reg_info); i++) {
>>> +             const struct kvm_fw_reg_info *reg_info = &fw_reg_info[i];
>>> +
>>> +             /*
>>> +              * Before starting the VM, the test clears all the bits.
>>> +              * Check if that's still the case.
>>> +              */
>>> +             get_fw_reg(vm, reg_info->reg, &val);
>>> +             TEST_ASSERT(val == 0,
>>> +                     "Expected all the features to be cleared for reg: 0x%lx\n",
>>> +                     reg_info->reg);
>>> +
>>> +             /*
>>> +              * Test setting the last read value. KVM should allow this
>>> +              * even if VM has started running.
>>> +              */
>>> +             ret = set_fw_reg(vm, reg_info->reg, val);
>>> +             TEST_ASSERT(ret == 0,
>>> +                     "Failed to set the register with previously read value after Vm start for reg: 0x%lx; ret: %d\n",
>>> +                     reg_info->reg, errno);
>>> +
>>> +             /*
>>> +              * Set all the features for this register again. KVM shouldn't
>>> +              * allow this as the VM is running.
>>> +              */
>>> +             ret = set_fw_reg(vm, reg_info->reg, FW_REG_ULIMIT_VAL(reg_info->max_feat_bit));
>>> +             TEST_ASSERT(ret != 0 && errno == EBUSY,
>>> +             "Unexpected behavior or return value (%d) while setting a feature while VM is running for reg: 0x%lx\n",
>>> +             errno, reg_info->reg);
>>> +     }
>>> +}
>>> +
>>> +static struct kvm_vm *test_vm_create(void)
>>> +{
>>> +     struct kvm_vm *vm;
>>> +
>>> +     vm = vm_create_default(0, 0, guest_code);
>>> +
>>> +     ucall_init(vm, NULL);
>>> +     steal_time_init(vm);
>>> +
>>> +     return vm;
>>> +}
>>> +
>>> +static struct kvm_vm *test_guest_stage(struct kvm_vm *vm)
>>> +{
>>> +     struct kvm_vm *ret_vm = vm;
>>> +
>>> +     pr_debug("Stage: %d\n", stage);
>>> +
>>> +     switch (stage) {
>>> +     case TEST_STAGE_REG_IFACE:
>>> +             test_fw_regs_after_vm_start(vm);
>>> +             break;
>>> +     case TEST_STAGE_HVC_IFACE_FEAT_DISABLED:
>>> +             /* Start a new VM so that all the features are now enabled by default */
>>> +             kvm_vm_free(vm);
>>> +             ret_vm = test_vm_create();
>>> +             break;
>>> +     case TEST_STAGE_HVC_IFACE_FEAT_ENABLED:
>>> +     case TEST_STAGE_HVC_IFACE_FALSE_INFO:
>>> +             break;
>>> +     default:
>>> +             TEST_FAIL("Unknown test stage: %d\n", stage);
>>> +     }
>>> +
>>> +     stage++;
>>> +     sync_global_to_guest(vm, stage);
>>> +
>>> +     return ret_vm;
>>> +}
>>> +
>>> +static void test_run(void)
>>> +{
>>> +     struct kvm_vm *vm;
>>> +     struct ucall uc;
>>> +     bool guest_done = false;
>>> +
>>> +     vm = test_vm_create();
>>> +
>>> +     test_fw_regs_before_vm_start(vm);
>>> +
>>> +     while (!guest_done) {
>>> +             vcpu_run(vm, 0);
>>> +
>>> +             switch (get_ucall(vm, 0, &uc)) {
>>> +             case UCALL_SYNC:
>>> +                     vm = test_guest_stage(vm);
>>> +                     break;
>>> +             case UCALL_DONE:
>>> +                     guest_done = true;
>>> +                     break;
>>> +             case UCALL_ABORT:
>>> +                     TEST_FAIL("%s at %s:%ld\n\tvalues: 0x%lx, 0x%lx; 0x%lx, stage: %u",
>>> +                     (const char *)uc.args[0], __FILE__, uc.args[1],
>>> +                     uc.args[2], uc.args[3], uc.args[4], stage);
>>> +                     break;
>>> +             default:
>>> +                     TEST_FAIL("Unexpected guest exit\n");
>>> +             }
>>> +     }
>>> +
>>> +     kvm_vm_free(vm);
>>> +}
>>> +
>>> +int main(void)
>>> +{
>>> +     setbuf(stdout, NULL);
>>> +
>>> +     test_run();
>>> +     return 0;
>>> +}
> 
> Thanks for taking the time to review.
> 

No worries and sorry for late chime-in.> 
> [1]: https://lore.kernel.org/kvmarm/20220409184549.1681189-11-oupton@google.com/T/#u
> 

Thanks,
Gavin


_______________________________________________
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] 96+ messages in thread

* Re: [PATCH v5 04/10] KVM: arm64: Add vendor hypervisor firmware register
  2022-04-14  1:04         ` Gavin Shan
  (?)
@ 2022-04-14 17:05           ` Raghavendra Rao Ananta
  -1 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-14 17:05 UTC (permalink / raw)
  To: Gavin Shan
  Cc: kvm, Marc Zyngier, Peter Shier, linux-kernel, Will Deacon,
	Catalin Marinas, Paolo Bonzini, kvmarm, linux-arm-kernel

On Wed, Apr 13, 2022 at 6:04 PM Gavin Shan <gshan@redhat.com> wrote:
>
> Hi Raghavendra,
>
> On 4/14/22 12:59 AM, Raghavendra Rao Ananta wrote:
> > On Tue, Apr 12, 2022 at 8:59 PM Gavin Shan <gshan@redhat.com> wrote:
> >> On 4/7/22 9:15 AM, Raghavendra Rao Ananta wrote:
> >>> Introduce the firmware register to hold the vendor specific
> >>> hypervisor service calls (owner value 6) as a bitmap. The
> >>> bitmap represents the features that'll be enabled for the
> >>> guest, as configured by the user-space. Currently, this
> >>> includes support for KVM-vendor features, and Precision Time
> >>> Protocol (PTP), represented by bit-0 and bit-1 respectively.
> >>>
> >>> Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
> >>> ---
> >>>    arch/arm64/include/asm/kvm_host.h |  2 ++
> >>>    arch/arm64/include/uapi/asm/kvm.h |  4 ++++
> >>>    arch/arm64/kvm/hypercalls.c       | 21 +++++++++++++++++----
> >>>    include/kvm/arm_hypercalls.h      |  4 ++++
> >>>    4 files changed, 27 insertions(+), 4 deletions(-)
> >>>
> >>> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> >>> index 20165242ebd9..b79161bad69a 100644
> >>> --- a/arch/arm64/include/asm/kvm_host.h
> >>> +++ b/arch/arm64/include/asm/kvm_host.h
> >>> @@ -106,10 +106,12 @@ struct kvm_arch_memory_slot {
> >>>     *
> >>>     * @std_bmap: Bitmap of standard secure service calls
> >>>     * @std_hyp_bmap: Bitmap of standard hypervisor service calls
> >>> + * @vendor_hyp_bmap: Bitmap of vendor specific hypervisor service calls
> >>>     */
> >>>    struct kvm_smccc_features {
> >>>        u64 std_bmap;
> >>>        u64 std_hyp_bmap;
> >>> +     u64 vendor_hyp_bmap;
> >>>    };
> >>>
> >>>    struct kvm_arch {
> >>> diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h
> >>> index 67353bf4e69d..9a5ac0ed4113 100644
> >>> --- a/arch/arm64/include/uapi/asm/kvm.h
> >>> +++ b/arch/arm64/include/uapi/asm/kvm.h
> >>> @@ -344,6 +344,10 @@ struct kvm_arm_copy_mte_tags {
> >>>    #define KVM_REG_ARM_STD_HYP_BMAP            KVM_REG_ARM_FW_FEAT_BMAP_REG(1)
> >>>    #define KVM_REG_ARM_STD_HYP_BIT_PV_TIME             BIT(0)
> >>>
> >>> +#define KVM_REG_ARM_VENDOR_HYP_BMAP          KVM_REG_ARM_FW_FEAT_BMAP_REG(2)
> >>> +#define KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT BIT(0)
> >>> +#define KVM_REG_ARM_VENDOR_HYP_BIT_PTP               BIT(1)
> >>> +
> >>>    /* Device Control API: ARM VGIC */
> >>>    #define KVM_DEV_ARM_VGIC_GRP_ADDR   0
> >>>    #define KVM_DEV_ARM_VGIC_GRP_DIST_REGS      1
> >>> diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c
> >>> index 64ae6c7e7145..80836c341fd3 100644
> >>> --- a/arch/arm64/kvm/hypercalls.c
> >>> +++ b/arch/arm64/kvm/hypercalls.c
> >>> @@ -66,8 +66,6 @@ static const u32 hvc_func_default_allowed_list[] = {
> >>>        ARM_SMCCC_VERSION_FUNC_ID,
> >>>        ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
> >>>        ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID,
> >>> -     ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID,
> >>> -     ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID,
> >>>    };
> >>>
> >>>    static bool kvm_hvc_call_default_allowed(struct kvm_vcpu *vcpu, u32 func_id)
> >>> @@ -102,6 +100,12 @@ static bool kvm_hvc_call_allowed(struct kvm_vcpu *vcpu, u32 func_id)
> >>>        case ARM_SMCCC_HV_PV_TIME_ST:
> >>>                return kvm_arm_fw_reg_feat_enabled(smccc_feat->std_hyp_bmap,
> >>>                                        KVM_REG_ARM_STD_HYP_BIT_PV_TIME);
> >>> +     case ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID:
> >>> +             return kvm_arm_fw_reg_feat_enabled(smccc_feat->vendor_hyp_bmap,
> >>> +                                     KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT);
> >>> +     case ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID:
> >>> +             return kvm_arm_fw_reg_feat_enabled(smccc_feat->vendor_hyp_bmap,
> >>> +                                     KVM_REG_ARM_VENDOR_HYP_BIT_PTP);
> >>>        default:
> >>>                return kvm_hvc_call_default_allowed(vcpu, func_id);
> >>>        }
> >>
> >> I guess we may return SMCCC_RET_NOT_SUPPORTED for ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID
> >> if KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT isn't set? Otherwise, we need explain it
> >> in the commit log.
> >>
> > ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID is a part of the hvc
> > allowed-list (hvc_func_default_allowed_list[]), which means it's not
> > associated with any feature bit and is always enabled. If the guest
> > were to issue ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID, we'd end up in
> > the 'default' case and the kvm_hvc_call_default_allowed() would return
> > 'true'. This is documented in patch 2/10.
> >
>
> I think I might not make myself clear and sorry for that. The point is
> the following hvc calls should be belonging to 'Vendor Specific Hypervisor
> Service', or I'm wrong. If I'm correct, VENDOR_HYP_CALL_UID_FUNC_ID
> should be disallowed if bit#0 isn't set in @vendor_hyp_bmap.
>
>      ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID
>      ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID
>      ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID
>
> ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID was introduced by commit 6e085e0ac9cf
> ("arm/arm64: Probe for the presence of KVM hypervisor"). According to the
> commit log, the identifier and supported (vendor specific) feature list
> is returned by this call and ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID.
> So the users depend on both calls to probe the supported features or
> services. So it seems incorrect to allow ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID
> even the 'Vendor Specific Hypervisor Service' is disabled and bit#0
> is cleared in @vendor_hyp_bmap by users.
>
Hm, it was a grey area for me since the FEATURES_FUNC_ID didn't
broadcast the presence of UID_FUNC_ID. But what you said makes sense.
UID_FUNC_ID should tag along with FEATURES_FUNC_ID. I can merge both
of them under bit-0.
Thanks for sharing the background.

Regards,
Raghavendra

> >> KVM_REG_ARM_VENDOR_HYP_BIT_{FUNC_FEAT, PTP} aren't parallel to each other.
> >> I think PTP can't be on if KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT is off.
> >>
> > Actually we went through this scenario [1]. Of course, we can build
> > some logic around it to make sure that the userspace does the right
> > thing, but at this point the consensus is that, unless it's an issue
> > for KVM, it's treated as a userspace bug.
> >
>
> Thanks for the pointer. I chime in late and I didn't check the reviewing
> history on this series. Hopefully I didn't bring too much confusing comments
> to you.
>
> I think it's fine by treating it as a userspace bug, but it would be nice
> to add comments somewhere if you agree.
>
> >>> @@ -194,8 +198,7 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
> >>>                val[3] = ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_3;
> >>>                break;
> >>>        case ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID:
> >>> -             val[0] = BIT(ARM_SMCCC_KVM_FUNC_FEATURES);
> >>> -             val[0] |= BIT(ARM_SMCCC_KVM_FUNC_PTP);
> >>> +             val[0] = smccc_feat->vendor_hyp_bmap;
> >>>                break;
> >>>        case ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID:
> >>>                kvm_ptp_get_time(vcpu, val);
> >>> @@ -222,6 +225,7 @@ static const u64 kvm_arm_fw_reg_ids[] = {
> >>>        KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3,
> >>>        KVM_REG_ARM_STD_BMAP,
> >>>        KVM_REG_ARM_STD_HYP_BMAP,
> >>> +     KVM_REG_ARM_VENDOR_HYP_BMAP,
> >>>    };
> >>>
> >>>    void kvm_arm_init_hypercalls(struct kvm *kvm)
> >>> @@ -230,6 +234,7 @@ void kvm_arm_init_hypercalls(struct kvm *kvm)
> >>>
> >>>        smccc_feat->std_bmap = KVM_ARM_SMCCC_STD_FEATURES;
> >>>        smccc_feat->std_hyp_bmap = KVM_ARM_SMCCC_STD_HYP_FEATURES;
> >>> +     smccc_feat->vendor_hyp_bmap = KVM_ARM_SMCCC_VENDOR_HYP_FEATURES;
> >>>    }
> >>>
> >>>    int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
> >>> @@ -322,6 +327,9 @@ int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> >>>        case KVM_REG_ARM_STD_HYP_BMAP:
> >>>                val = READ_ONCE(smccc_feat->std_hyp_bmap);
> >>>                break;
> >>> +     case KVM_REG_ARM_VENDOR_HYP_BMAP:
> >>> +             val = READ_ONCE(smccc_feat->vendor_hyp_bmap);
> >>> +             break;
> >>>        default:
> >>>                return -ENOENT;
> >>>        }
> >>> @@ -348,6 +356,10 @@ static int kvm_arm_set_fw_reg_bmap(struct kvm_vcpu *vcpu, u64 reg_id, u64 val)
> >>>                fw_reg_bmap = &smccc_feat->std_hyp_bmap;
> >>>                fw_reg_features = KVM_ARM_SMCCC_STD_HYP_FEATURES;
> >>>                break;
> >>> +     case KVM_REG_ARM_VENDOR_HYP_BMAP:
> >>> +             fw_reg_bmap = &smccc_feat->vendor_hyp_bmap;
> >>> +             fw_reg_features = KVM_ARM_SMCCC_VENDOR_HYP_FEATURES;
> >>> +             break;
> >>>        default:
> >>>                return -ENOENT;
> >>>        }
> >>
> >> If KVM_REG_ARM_VENDOR_HYP_BIT_{FUNC_FEAT, PTP} aren't parallel to each other,
> >> special code is needed to gurantee PTP is cleared if VENDOR_HYP is disabled.
> >>
> > Please see the above comment :)
> >
>
> Thanks for the pointer and explanation :)
>
> >>> @@ -453,6 +465,7 @@ int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> >>>                return 0;
> >>>        case KVM_REG_ARM_STD_BMAP:
> >>>        case KVM_REG_ARM_STD_HYP_BMAP:
> >>> +     case KVM_REG_ARM_VENDOR_HYP_BMAP:
> >>>                return kvm_arm_set_fw_reg_bmap(vcpu, reg->id, val);
> >>>        default:
> >>>                return -ENOENT;
> >>> diff --git a/include/kvm/arm_hypercalls.h b/include/kvm/arm_hypercalls.h
> >>> index b0915d8c5b81..eaf4f6b318a8 100644
> >>> --- a/include/kvm/arm_hypercalls.h
> >>> +++ b/include/kvm/arm_hypercalls.h
> >>> @@ -9,6 +9,7 @@
> >>>    /* Last valid bits of the bitmapped firmware registers */
> >>>    #define KVM_REG_ARM_STD_BMAP_BIT_MAX                0
> >>>    #define KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX    0
> >>> +#define KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX  1
> >>>
> >>>    #define KVM_ARM_SMCCC_STD_FEATURES \
> >>>        GENMASK_ULL(KVM_REG_ARM_STD_BMAP_BIT_MAX, 0)
> >>> @@ -16,6 +17,9 @@
> >>>    #define KVM_ARM_SMCCC_STD_HYP_FEATURES \
> >>>        GENMASK_ULL(KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX, 0)
> >>>
> >>> +#define KVM_ARM_SMCCC_VENDOR_HYP_FEATURES \
> >>> +     GENMASK_ULL(KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX, 0)
> >>> +
> >>>    int kvm_hvc_call_handler(struct kvm_vcpu *vcpu);
> >>>
> >>>    static inline u32 smccc_get_function(struct kvm_vcpu *vcpu)
> >>>
> >>
> >
> > Thanks for the review.
> >
>
> No worries and sorry for the late chime-in :)
>
> >
> > [1]: https://lore.kernel.org/lkml/YjA1AzZPlPV20kMj@google.com/
> >
>
> Thanks,
> Gavin
>
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

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

* Re: [PATCH v5 04/10] KVM: arm64: Add vendor hypervisor firmware register
@ 2022-04-14 17:05           ` Raghavendra Rao Ananta
  0 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-14 17:05 UTC (permalink / raw)
  To: Gavin Shan
  Cc: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose, kvm, Catalin Marinas, Peter Shier,
	linux-kernel, Paolo Bonzini, Will Deacon, kvmarm,
	linux-arm-kernel

On Wed, Apr 13, 2022 at 6:04 PM Gavin Shan <gshan@redhat.com> wrote:
>
> Hi Raghavendra,
>
> On 4/14/22 12:59 AM, Raghavendra Rao Ananta wrote:
> > On Tue, Apr 12, 2022 at 8:59 PM Gavin Shan <gshan@redhat.com> wrote:
> >> On 4/7/22 9:15 AM, Raghavendra Rao Ananta wrote:
> >>> Introduce the firmware register to hold the vendor specific
> >>> hypervisor service calls (owner value 6) as a bitmap. The
> >>> bitmap represents the features that'll be enabled for the
> >>> guest, as configured by the user-space. Currently, this
> >>> includes support for KVM-vendor features, and Precision Time
> >>> Protocol (PTP), represented by bit-0 and bit-1 respectively.
> >>>
> >>> Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
> >>> ---
> >>>    arch/arm64/include/asm/kvm_host.h |  2 ++
> >>>    arch/arm64/include/uapi/asm/kvm.h |  4 ++++
> >>>    arch/arm64/kvm/hypercalls.c       | 21 +++++++++++++++++----
> >>>    include/kvm/arm_hypercalls.h      |  4 ++++
> >>>    4 files changed, 27 insertions(+), 4 deletions(-)
> >>>
> >>> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> >>> index 20165242ebd9..b79161bad69a 100644
> >>> --- a/arch/arm64/include/asm/kvm_host.h
> >>> +++ b/arch/arm64/include/asm/kvm_host.h
> >>> @@ -106,10 +106,12 @@ struct kvm_arch_memory_slot {
> >>>     *
> >>>     * @std_bmap: Bitmap of standard secure service calls
> >>>     * @std_hyp_bmap: Bitmap of standard hypervisor service calls
> >>> + * @vendor_hyp_bmap: Bitmap of vendor specific hypervisor service calls
> >>>     */
> >>>    struct kvm_smccc_features {
> >>>        u64 std_bmap;
> >>>        u64 std_hyp_bmap;
> >>> +     u64 vendor_hyp_bmap;
> >>>    };
> >>>
> >>>    struct kvm_arch {
> >>> diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h
> >>> index 67353bf4e69d..9a5ac0ed4113 100644
> >>> --- a/arch/arm64/include/uapi/asm/kvm.h
> >>> +++ b/arch/arm64/include/uapi/asm/kvm.h
> >>> @@ -344,6 +344,10 @@ struct kvm_arm_copy_mte_tags {
> >>>    #define KVM_REG_ARM_STD_HYP_BMAP            KVM_REG_ARM_FW_FEAT_BMAP_REG(1)
> >>>    #define KVM_REG_ARM_STD_HYP_BIT_PV_TIME             BIT(0)
> >>>
> >>> +#define KVM_REG_ARM_VENDOR_HYP_BMAP          KVM_REG_ARM_FW_FEAT_BMAP_REG(2)
> >>> +#define KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT BIT(0)
> >>> +#define KVM_REG_ARM_VENDOR_HYP_BIT_PTP               BIT(1)
> >>> +
> >>>    /* Device Control API: ARM VGIC */
> >>>    #define KVM_DEV_ARM_VGIC_GRP_ADDR   0
> >>>    #define KVM_DEV_ARM_VGIC_GRP_DIST_REGS      1
> >>> diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c
> >>> index 64ae6c7e7145..80836c341fd3 100644
> >>> --- a/arch/arm64/kvm/hypercalls.c
> >>> +++ b/arch/arm64/kvm/hypercalls.c
> >>> @@ -66,8 +66,6 @@ static const u32 hvc_func_default_allowed_list[] = {
> >>>        ARM_SMCCC_VERSION_FUNC_ID,
> >>>        ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
> >>>        ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID,
> >>> -     ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID,
> >>> -     ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID,
> >>>    };
> >>>
> >>>    static bool kvm_hvc_call_default_allowed(struct kvm_vcpu *vcpu, u32 func_id)
> >>> @@ -102,6 +100,12 @@ static bool kvm_hvc_call_allowed(struct kvm_vcpu *vcpu, u32 func_id)
> >>>        case ARM_SMCCC_HV_PV_TIME_ST:
> >>>                return kvm_arm_fw_reg_feat_enabled(smccc_feat->std_hyp_bmap,
> >>>                                        KVM_REG_ARM_STD_HYP_BIT_PV_TIME);
> >>> +     case ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID:
> >>> +             return kvm_arm_fw_reg_feat_enabled(smccc_feat->vendor_hyp_bmap,
> >>> +                                     KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT);
> >>> +     case ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID:
> >>> +             return kvm_arm_fw_reg_feat_enabled(smccc_feat->vendor_hyp_bmap,
> >>> +                                     KVM_REG_ARM_VENDOR_HYP_BIT_PTP);
> >>>        default:
> >>>                return kvm_hvc_call_default_allowed(vcpu, func_id);
> >>>        }
> >>
> >> I guess we may return SMCCC_RET_NOT_SUPPORTED for ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID
> >> if KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT isn't set? Otherwise, we need explain it
> >> in the commit log.
> >>
> > ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID is a part of the hvc
> > allowed-list (hvc_func_default_allowed_list[]), which means it's not
> > associated with any feature bit and is always enabled. If the guest
> > were to issue ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID, we'd end up in
> > the 'default' case and the kvm_hvc_call_default_allowed() would return
> > 'true'. This is documented in patch 2/10.
> >
>
> I think I might not make myself clear and sorry for that. The point is
> the following hvc calls should be belonging to 'Vendor Specific Hypervisor
> Service', or I'm wrong. If I'm correct, VENDOR_HYP_CALL_UID_FUNC_ID
> should be disallowed if bit#0 isn't set in @vendor_hyp_bmap.
>
>      ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID
>      ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID
>      ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID
>
> ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID was introduced by commit 6e085e0ac9cf
> ("arm/arm64: Probe for the presence of KVM hypervisor"). According to the
> commit log, the identifier and supported (vendor specific) feature list
> is returned by this call and ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID.
> So the users depend on both calls to probe the supported features or
> services. So it seems incorrect to allow ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID
> even the 'Vendor Specific Hypervisor Service' is disabled and bit#0
> is cleared in @vendor_hyp_bmap by users.
>
Hm, it was a grey area for me since the FEATURES_FUNC_ID didn't
broadcast the presence of UID_FUNC_ID. But what you said makes sense.
UID_FUNC_ID should tag along with FEATURES_FUNC_ID. I can merge both
of them under bit-0.
Thanks for sharing the background.

Regards,
Raghavendra

> >> KVM_REG_ARM_VENDOR_HYP_BIT_{FUNC_FEAT, PTP} aren't parallel to each other.
> >> I think PTP can't be on if KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT is off.
> >>
> > Actually we went through this scenario [1]. Of course, we can build
> > some logic around it to make sure that the userspace does the right
> > thing, but at this point the consensus is that, unless it's an issue
> > for KVM, it's treated as a userspace bug.
> >
>
> Thanks for the pointer. I chime in late and I didn't check the reviewing
> history on this series. Hopefully I didn't bring too much confusing comments
> to you.
>
> I think it's fine by treating it as a userspace bug, but it would be nice
> to add comments somewhere if you agree.
>
> >>> @@ -194,8 +198,7 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
> >>>                val[3] = ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_3;
> >>>                break;
> >>>        case ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID:
> >>> -             val[0] = BIT(ARM_SMCCC_KVM_FUNC_FEATURES);
> >>> -             val[0] |= BIT(ARM_SMCCC_KVM_FUNC_PTP);
> >>> +             val[0] = smccc_feat->vendor_hyp_bmap;
> >>>                break;
> >>>        case ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID:
> >>>                kvm_ptp_get_time(vcpu, val);
> >>> @@ -222,6 +225,7 @@ static const u64 kvm_arm_fw_reg_ids[] = {
> >>>        KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3,
> >>>        KVM_REG_ARM_STD_BMAP,
> >>>        KVM_REG_ARM_STD_HYP_BMAP,
> >>> +     KVM_REG_ARM_VENDOR_HYP_BMAP,
> >>>    };
> >>>
> >>>    void kvm_arm_init_hypercalls(struct kvm *kvm)
> >>> @@ -230,6 +234,7 @@ void kvm_arm_init_hypercalls(struct kvm *kvm)
> >>>
> >>>        smccc_feat->std_bmap = KVM_ARM_SMCCC_STD_FEATURES;
> >>>        smccc_feat->std_hyp_bmap = KVM_ARM_SMCCC_STD_HYP_FEATURES;
> >>> +     smccc_feat->vendor_hyp_bmap = KVM_ARM_SMCCC_VENDOR_HYP_FEATURES;
> >>>    }
> >>>
> >>>    int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
> >>> @@ -322,6 +327,9 @@ int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> >>>        case KVM_REG_ARM_STD_HYP_BMAP:
> >>>                val = READ_ONCE(smccc_feat->std_hyp_bmap);
> >>>                break;
> >>> +     case KVM_REG_ARM_VENDOR_HYP_BMAP:
> >>> +             val = READ_ONCE(smccc_feat->vendor_hyp_bmap);
> >>> +             break;
> >>>        default:
> >>>                return -ENOENT;
> >>>        }
> >>> @@ -348,6 +356,10 @@ static int kvm_arm_set_fw_reg_bmap(struct kvm_vcpu *vcpu, u64 reg_id, u64 val)
> >>>                fw_reg_bmap = &smccc_feat->std_hyp_bmap;
> >>>                fw_reg_features = KVM_ARM_SMCCC_STD_HYP_FEATURES;
> >>>                break;
> >>> +     case KVM_REG_ARM_VENDOR_HYP_BMAP:
> >>> +             fw_reg_bmap = &smccc_feat->vendor_hyp_bmap;
> >>> +             fw_reg_features = KVM_ARM_SMCCC_VENDOR_HYP_FEATURES;
> >>> +             break;
> >>>        default:
> >>>                return -ENOENT;
> >>>        }
> >>
> >> If KVM_REG_ARM_VENDOR_HYP_BIT_{FUNC_FEAT, PTP} aren't parallel to each other,
> >> special code is needed to gurantee PTP is cleared if VENDOR_HYP is disabled.
> >>
> > Please see the above comment :)
> >
>
> Thanks for the pointer and explanation :)
>
> >>> @@ -453,6 +465,7 @@ int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> >>>                return 0;
> >>>        case KVM_REG_ARM_STD_BMAP:
> >>>        case KVM_REG_ARM_STD_HYP_BMAP:
> >>> +     case KVM_REG_ARM_VENDOR_HYP_BMAP:
> >>>                return kvm_arm_set_fw_reg_bmap(vcpu, reg->id, val);
> >>>        default:
> >>>                return -ENOENT;
> >>> diff --git a/include/kvm/arm_hypercalls.h b/include/kvm/arm_hypercalls.h
> >>> index b0915d8c5b81..eaf4f6b318a8 100644
> >>> --- a/include/kvm/arm_hypercalls.h
> >>> +++ b/include/kvm/arm_hypercalls.h
> >>> @@ -9,6 +9,7 @@
> >>>    /* Last valid bits of the bitmapped firmware registers */
> >>>    #define KVM_REG_ARM_STD_BMAP_BIT_MAX                0
> >>>    #define KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX    0
> >>> +#define KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX  1
> >>>
> >>>    #define KVM_ARM_SMCCC_STD_FEATURES \
> >>>        GENMASK_ULL(KVM_REG_ARM_STD_BMAP_BIT_MAX, 0)
> >>> @@ -16,6 +17,9 @@
> >>>    #define KVM_ARM_SMCCC_STD_HYP_FEATURES \
> >>>        GENMASK_ULL(KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX, 0)
> >>>
> >>> +#define KVM_ARM_SMCCC_VENDOR_HYP_FEATURES \
> >>> +     GENMASK_ULL(KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX, 0)
> >>> +
> >>>    int kvm_hvc_call_handler(struct kvm_vcpu *vcpu);
> >>>
> >>>    static inline u32 smccc_get_function(struct kvm_vcpu *vcpu)
> >>>
> >>
> >
> > Thanks for the review.
> >
>
> No worries and sorry for the late chime-in :)
>
> >
> > [1]: https://lore.kernel.org/lkml/YjA1AzZPlPV20kMj@google.com/
> >
>
> Thanks,
> Gavin
>

_______________________________________________
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] 96+ messages in thread

* Re: [PATCH v5 04/10] KVM: arm64: Add vendor hypervisor firmware register
@ 2022-04-14 17:05           ` Raghavendra Rao Ananta
  0 siblings, 0 replies; 96+ messages in thread
From: Raghavendra Rao Ananta @ 2022-04-14 17:05 UTC (permalink / raw)
  To: Gavin Shan
  Cc: Marc Zyngier, Andrew Jones, James Morse, Alexandru Elisei,
	Suzuki K Poulose, kvm, Catalin Marinas, Peter Shier,
	linux-kernel, Paolo Bonzini, Will Deacon, kvmarm,
	linux-arm-kernel

On Wed, Apr 13, 2022 at 6:04 PM Gavin Shan <gshan@redhat.com> wrote:
>
> Hi Raghavendra,
>
> On 4/14/22 12:59 AM, Raghavendra Rao Ananta wrote:
> > On Tue, Apr 12, 2022 at 8:59 PM Gavin Shan <gshan@redhat.com> wrote:
> >> On 4/7/22 9:15 AM, Raghavendra Rao Ananta wrote:
> >>> Introduce the firmware register to hold the vendor specific
> >>> hypervisor service calls (owner value 6) as a bitmap. The
> >>> bitmap represents the features that'll be enabled for the
> >>> guest, as configured by the user-space. Currently, this
> >>> includes support for KVM-vendor features, and Precision Time
> >>> Protocol (PTP), represented by bit-0 and bit-1 respectively.
> >>>
> >>> Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
> >>> ---
> >>>    arch/arm64/include/asm/kvm_host.h |  2 ++
> >>>    arch/arm64/include/uapi/asm/kvm.h |  4 ++++
> >>>    arch/arm64/kvm/hypercalls.c       | 21 +++++++++++++++++----
> >>>    include/kvm/arm_hypercalls.h      |  4 ++++
> >>>    4 files changed, 27 insertions(+), 4 deletions(-)
> >>>
> >>> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> >>> index 20165242ebd9..b79161bad69a 100644
> >>> --- a/arch/arm64/include/asm/kvm_host.h
> >>> +++ b/arch/arm64/include/asm/kvm_host.h
> >>> @@ -106,10 +106,12 @@ struct kvm_arch_memory_slot {
> >>>     *
> >>>     * @std_bmap: Bitmap of standard secure service calls
> >>>     * @std_hyp_bmap: Bitmap of standard hypervisor service calls
> >>> + * @vendor_hyp_bmap: Bitmap of vendor specific hypervisor service calls
> >>>     */
> >>>    struct kvm_smccc_features {
> >>>        u64 std_bmap;
> >>>        u64 std_hyp_bmap;
> >>> +     u64 vendor_hyp_bmap;
> >>>    };
> >>>
> >>>    struct kvm_arch {
> >>> diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h
> >>> index 67353bf4e69d..9a5ac0ed4113 100644
> >>> --- a/arch/arm64/include/uapi/asm/kvm.h
> >>> +++ b/arch/arm64/include/uapi/asm/kvm.h
> >>> @@ -344,6 +344,10 @@ struct kvm_arm_copy_mte_tags {
> >>>    #define KVM_REG_ARM_STD_HYP_BMAP            KVM_REG_ARM_FW_FEAT_BMAP_REG(1)
> >>>    #define KVM_REG_ARM_STD_HYP_BIT_PV_TIME             BIT(0)
> >>>
> >>> +#define KVM_REG_ARM_VENDOR_HYP_BMAP          KVM_REG_ARM_FW_FEAT_BMAP_REG(2)
> >>> +#define KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT BIT(0)
> >>> +#define KVM_REG_ARM_VENDOR_HYP_BIT_PTP               BIT(1)
> >>> +
> >>>    /* Device Control API: ARM VGIC */
> >>>    #define KVM_DEV_ARM_VGIC_GRP_ADDR   0
> >>>    #define KVM_DEV_ARM_VGIC_GRP_DIST_REGS      1
> >>> diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c
> >>> index 64ae6c7e7145..80836c341fd3 100644
> >>> --- a/arch/arm64/kvm/hypercalls.c
> >>> +++ b/arch/arm64/kvm/hypercalls.c
> >>> @@ -66,8 +66,6 @@ static const u32 hvc_func_default_allowed_list[] = {
> >>>        ARM_SMCCC_VERSION_FUNC_ID,
> >>>        ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
> >>>        ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID,
> >>> -     ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID,
> >>> -     ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID,
> >>>    };
> >>>
> >>>    static bool kvm_hvc_call_default_allowed(struct kvm_vcpu *vcpu, u32 func_id)
> >>> @@ -102,6 +100,12 @@ static bool kvm_hvc_call_allowed(struct kvm_vcpu *vcpu, u32 func_id)
> >>>        case ARM_SMCCC_HV_PV_TIME_ST:
> >>>                return kvm_arm_fw_reg_feat_enabled(smccc_feat->std_hyp_bmap,
> >>>                                        KVM_REG_ARM_STD_HYP_BIT_PV_TIME);
> >>> +     case ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID:
> >>> +             return kvm_arm_fw_reg_feat_enabled(smccc_feat->vendor_hyp_bmap,
> >>> +                                     KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT);
> >>> +     case ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID:
> >>> +             return kvm_arm_fw_reg_feat_enabled(smccc_feat->vendor_hyp_bmap,
> >>> +                                     KVM_REG_ARM_VENDOR_HYP_BIT_PTP);
> >>>        default:
> >>>                return kvm_hvc_call_default_allowed(vcpu, func_id);
> >>>        }
> >>
> >> I guess we may return SMCCC_RET_NOT_SUPPORTED for ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID
> >> if KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT isn't set? Otherwise, we need explain it
> >> in the commit log.
> >>
> > ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID is a part of the hvc
> > allowed-list (hvc_func_default_allowed_list[]), which means it's not
> > associated with any feature bit and is always enabled. If the guest
> > were to issue ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID, we'd end up in
> > the 'default' case and the kvm_hvc_call_default_allowed() would return
> > 'true'. This is documented in patch 2/10.
> >
>
> I think I might not make myself clear and sorry for that. The point is
> the following hvc calls should be belonging to 'Vendor Specific Hypervisor
> Service', or I'm wrong. If I'm correct, VENDOR_HYP_CALL_UID_FUNC_ID
> should be disallowed if bit#0 isn't set in @vendor_hyp_bmap.
>
>      ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID
>      ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID
>      ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID
>
> ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID was introduced by commit 6e085e0ac9cf
> ("arm/arm64: Probe for the presence of KVM hypervisor"). According to the
> commit log, the identifier and supported (vendor specific) feature list
> is returned by this call and ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID.
> So the users depend on both calls to probe the supported features or
> services. So it seems incorrect to allow ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID
> even the 'Vendor Specific Hypervisor Service' is disabled and bit#0
> is cleared in @vendor_hyp_bmap by users.
>
Hm, it was a grey area for me since the FEATURES_FUNC_ID didn't
broadcast the presence of UID_FUNC_ID. But what you said makes sense.
UID_FUNC_ID should tag along with FEATURES_FUNC_ID. I can merge both
of them under bit-0.
Thanks for sharing the background.

Regards,
Raghavendra

> >> KVM_REG_ARM_VENDOR_HYP_BIT_{FUNC_FEAT, PTP} aren't parallel to each other.
> >> I think PTP can't be on if KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT is off.
> >>
> > Actually we went through this scenario [1]. Of course, we can build
> > some logic around it to make sure that the userspace does the right
> > thing, but at this point the consensus is that, unless it's an issue
> > for KVM, it's treated as a userspace bug.
> >
>
> Thanks for the pointer. I chime in late and I didn't check the reviewing
> history on this series. Hopefully I didn't bring too much confusing comments
> to you.
>
> I think it's fine by treating it as a userspace bug, but it would be nice
> to add comments somewhere if you agree.
>
> >>> @@ -194,8 +198,7 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
> >>>                val[3] = ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_3;
> >>>                break;
> >>>        case ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID:
> >>> -             val[0] = BIT(ARM_SMCCC_KVM_FUNC_FEATURES);
> >>> -             val[0] |= BIT(ARM_SMCCC_KVM_FUNC_PTP);
> >>> +             val[0] = smccc_feat->vendor_hyp_bmap;
> >>>                break;
> >>>        case ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID:
> >>>                kvm_ptp_get_time(vcpu, val);
> >>> @@ -222,6 +225,7 @@ static const u64 kvm_arm_fw_reg_ids[] = {
> >>>        KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3,
> >>>        KVM_REG_ARM_STD_BMAP,
> >>>        KVM_REG_ARM_STD_HYP_BMAP,
> >>> +     KVM_REG_ARM_VENDOR_HYP_BMAP,
> >>>    };
> >>>
> >>>    void kvm_arm_init_hypercalls(struct kvm *kvm)
> >>> @@ -230,6 +234,7 @@ void kvm_arm_init_hypercalls(struct kvm *kvm)
> >>>
> >>>        smccc_feat->std_bmap = KVM_ARM_SMCCC_STD_FEATURES;
> >>>        smccc_feat->std_hyp_bmap = KVM_ARM_SMCCC_STD_HYP_FEATURES;
> >>> +     smccc_feat->vendor_hyp_bmap = KVM_ARM_SMCCC_VENDOR_HYP_FEATURES;
> >>>    }
> >>>
> >>>    int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
> >>> @@ -322,6 +327,9 @@ int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> >>>        case KVM_REG_ARM_STD_HYP_BMAP:
> >>>                val = READ_ONCE(smccc_feat->std_hyp_bmap);
> >>>                break;
> >>> +     case KVM_REG_ARM_VENDOR_HYP_BMAP:
> >>> +             val = READ_ONCE(smccc_feat->vendor_hyp_bmap);
> >>> +             break;
> >>>        default:
> >>>                return -ENOENT;
> >>>        }
> >>> @@ -348,6 +356,10 @@ static int kvm_arm_set_fw_reg_bmap(struct kvm_vcpu *vcpu, u64 reg_id, u64 val)
> >>>                fw_reg_bmap = &smccc_feat->std_hyp_bmap;
> >>>                fw_reg_features = KVM_ARM_SMCCC_STD_HYP_FEATURES;
> >>>                break;
> >>> +     case KVM_REG_ARM_VENDOR_HYP_BMAP:
> >>> +             fw_reg_bmap = &smccc_feat->vendor_hyp_bmap;
> >>> +             fw_reg_features = KVM_ARM_SMCCC_VENDOR_HYP_FEATURES;
> >>> +             break;
> >>>        default:
> >>>                return -ENOENT;
> >>>        }
> >>
> >> If KVM_REG_ARM_VENDOR_HYP_BIT_{FUNC_FEAT, PTP} aren't parallel to each other,
> >> special code is needed to gurantee PTP is cleared if VENDOR_HYP is disabled.
> >>
> > Please see the above comment :)
> >
>
> Thanks for the pointer and explanation :)
>
> >>> @@ -453,6 +465,7 @@ int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> >>>                return 0;
> >>>        case KVM_REG_ARM_STD_BMAP:
> >>>        case KVM_REG_ARM_STD_HYP_BMAP:
> >>> +     case KVM_REG_ARM_VENDOR_HYP_BMAP:
> >>>                return kvm_arm_set_fw_reg_bmap(vcpu, reg->id, val);
> >>>        default:
> >>>                return -ENOENT;
> >>> diff --git a/include/kvm/arm_hypercalls.h b/include/kvm/arm_hypercalls.h
> >>> index b0915d8c5b81..eaf4f6b318a8 100644
> >>> --- a/include/kvm/arm_hypercalls.h
> >>> +++ b/include/kvm/arm_hypercalls.h
> >>> @@ -9,6 +9,7 @@
> >>>    /* Last valid bits of the bitmapped firmware registers */
> >>>    #define KVM_REG_ARM_STD_BMAP_BIT_MAX                0
> >>>    #define KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX    0
> >>> +#define KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX  1
> >>>
> >>>    #define KVM_ARM_SMCCC_STD_FEATURES \
> >>>        GENMASK_ULL(KVM_REG_ARM_STD_BMAP_BIT_MAX, 0)
> >>> @@ -16,6 +17,9 @@
> >>>    #define KVM_ARM_SMCCC_STD_HYP_FEATURES \
> >>>        GENMASK_ULL(KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX, 0)
> >>>
> >>> +#define KVM_ARM_SMCCC_VENDOR_HYP_FEATURES \
> >>> +     GENMASK_ULL(KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX, 0)
> >>> +
> >>>    int kvm_hvc_call_handler(struct kvm_vcpu *vcpu);
> >>>
> >>>    static inline u32 smccc_get_function(struct kvm_vcpu *vcpu)
> >>>
> >>
> >
> > Thanks for the review.
> >
>
> No worries and sorry for the late chime-in :)
>
> >
> > [1]: https://lore.kernel.org/lkml/YjA1AzZPlPV20kMj@google.com/
> >
>
> Thanks,
> Gavin
>

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

* Re: [PATCH v5 00/10] KVM: arm64: Add support for hypercall services selection
  2022-04-07  1:15 ` Raghavendra Rao Ananta
  (?)
@ 2022-04-15  6:44   ` Gavin Shan
  -1 siblings, 0 replies; 96+ messages in thread
From: Gavin Shan @ 2022-04-15  6:44 UTC (permalink / raw)
  To: Raghavendra Rao Ananta, Marc Zyngier, Andrew Jones, James Morse,
	Alexandru Elisei, Suzuki K Poulose
  Cc: kvm, Catalin Marinas, Peter Shier, linux-kernel, Paolo Bonzini,
	Will Deacon, kvmarm, linux-arm-kernel

Hi Raghavendra,

On 4/7/22 9:15 AM, Raghavendra Rao Ananta wrote:
> Continuing the discussion from [1], the series tries to add support
> for the userspace to elect the hypercall services that it wishes
> to expose to the guest, rather than the guest discovering them
> unconditionally. The idea employed by the series was taken from
> [1] as suggested by Marc Z.
> 
> In a broad sense, the concept is similar to the current implementation
> of PSCI interface- create a 'firmware psuedo-register' to handle the
> firmware revisions. The series extends this idea to all the other
> hypercalls such as TRNG (True Random Number Generator), PV_TIME
> (Paravirtualized Time), and PTP (Precision Time protocol).
> 
> For better categorization and future scaling, these firmware registers
> are categorized based on the service call owners. Also, unlike the
> existing firmware psuedo-registers, they hold the features supported
> in the form of a bitmap.
> 
> During the VM initialization, the registers holds an upper-limit of
> the features supported by each one of them. It's expected that the
> userspace discover the features provided by each register via GET_ONE_REG,
> and writeback the desired values using SET_ONE_REG. KVM allows this
> modification only until the VM has started.
> 
> Some of the standard function-ids, such as ARM_SMCCC_VERSION_FUNC_ID,
> need not be associated with a feature bit. For such ids, the series
> introduced an allowed-list, hvc_func_default_allowed_list[], that holds
> all such ids. As a result, the functions that are not elected by userspace,
> or if they are not a part of this allowed-list, will be denied for when
> the guests invoke them.
> 
> Older VMMs can simply ignore this interface and the hypercall services
> will be exposed unconditionally to the guests, thus ensuring backward
> compatibility.
> 

[...]

I rethinking about the design again and just get one question. Hopefully,
someone have the answer for us. The newly added 3 pseudo registers and
the existing ones like KVM_REG_ARM_PSCI_VERSION are all tied up with
vcpu, instead of VM. I don't think it's correct. I'm not sure if VM-scoped
pseudo registers aren't allowed by ARM architecture or the effort isn't
worthy to support it.

These pseudo registers are introduced to present the available hypercalls,
and then they can be disabled from userspace. In the implementation, these 3
registers are vcpu scoped. It means that multiple vcpus can be asymmetric
in terms of usable hypercalls. For example, ARM_SMCCC_TRNG hypercalls
can be enabled on vcpu0, but disabled on vcpu1. I don't think it's expected.

On the other hand, the information stored in these 3 registers needs to
be migrated through {GET,SET}_ONE_REG by VMM (QEMU). all the information
stored in these 3 registers are all same on all vcpus, which is exactly
as we expect. In migration circumstance, we're transporting identical
information for all vcpus and it's unnecessary.

Thanks,
Gavin




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

* Re: [PATCH v5 00/10] KVM: arm64: Add support for hypercall services selection
@ 2022-04-15  6:44   ` Gavin Shan
  0 siblings, 0 replies; 96+ messages in thread
From: Gavin Shan @ 2022-04-15  6:44 UTC (permalink / raw)
  To: Raghavendra Rao Ananta, Marc Zyngier, Andrew Jones, James Morse,
	Alexandru Elisei, Suzuki K Poulose
  Cc: kvm, Catalin Marinas, Peter Shier, linux-kernel, Paolo Bonzini,
	Will Deacon, kvmarm, linux-arm-kernel

Hi Raghavendra,

On 4/7/22 9:15 AM, Raghavendra Rao Ananta wrote:
> Continuing the discussion from [1], the series tries to add support
> for the userspace to elect the hypercall services that it wishes
> to expose to the guest, rather than the guest discovering them
> unconditionally. The idea employed by the series was taken from
> [1] as suggested by Marc Z.
> 
> In a broad sense, the concept is similar to the current implementation
> of PSCI interface- create a 'firmware psuedo-register' to handle the
> firmware revisions. The series extends this idea to all the other
> hypercalls such as TRNG (True Random Number Generator), PV_TIME
> (Paravirtualized Time), and PTP (Precision Time protocol).
> 
> For better categorization and future scaling, these firmware registers
> are categorized based on the service call owners. Also, unlike the
> existing firmware psuedo-registers, they hold the features supported
> in the form of a bitmap.
> 
> During the VM initialization, the registers holds an upper-limit of
> the features supported by each one of them. It's expected that the
> userspace discover the features provided by each register via GET_ONE_REG,
> and writeback the desired values using SET_ONE_REG. KVM allows this
> modification only until the VM has started.
> 
> Some of the standard function-ids, such as ARM_SMCCC_VERSION_FUNC_ID,
> need not be associated with a feature bit. For such ids, the series
> introduced an allowed-list, hvc_func_default_allowed_list[], that holds
> all such ids. As a result, the functions that are not elected by userspace,
> or if they are not a part of this allowed-list, will be denied for when
> the guests invoke them.
> 
> Older VMMs can simply ignore this interface and the hypercall services
> will be exposed unconditionally to the guests, thus ensuring backward
> compatibility.
> 

[...]

I rethinking about the design again and just get one question. Hopefully,
someone have the answer for us. The newly added 3 pseudo registers and
the existing ones like KVM_REG_ARM_PSCI_VERSION are all tied up with
vcpu, instead of VM. I don't think it's correct. I'm not sure if VM-scoped
pseudo registers aren't allowed by ARM architecture or the effort isn't
worthy to support it.

These pseudo registers are introduced to present the available hypercalls,
and then they can be disabled from userspace. In the implementation, these 3
registers are vcpu scoped. It means that multiple vcpus can be asymmetric
in terms of usable hypercalls. For example, ARM_SMCCC_TRNG hypercalls
can be enabled on vcpu0, but disabled on vcpu1. I don't think it's expected.

On the other hand, the information stored in these 3 registers needs to
be migrated through {GET,SET}_ONE_REG by VMM (QEMU). all the information
stored in these 3 registers are all same on all vcpus, which is exactly
as we expect. In migration circumstance, we're transporting identical
information for all vcpus and it's unnecessary.

Thanks,
Gavin



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

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

* Re: [PATCH v5 00/10] KVM: arm64: Add support for hypercall services selection
@ 2022-04-15  6:44   ` Gavin Shan
  0 siblings, 0 replies; 96+ messages in thread
From: Gavin Shan @ 2022-04-15  6:44 UTC (permalink / raw)
  To: Raghavendra Rao Ananta, Marc Zyngier, Andrew Jones, James Morse,
	Alexandru Elisei, Suzuki K Poulose
  Cc: kvm, Catalin Marinas, Peter Shier, linux-kernel, Paolo Bonzini,
	Will Deacon, kvmarm, linux-arm-kernel

Hi Raghavendra,

On 4/7/22 9:15 AM, Raghavendra Rao Ananta wrote:
> Continuing the discussion from [1], the series tries to add support
> for the userspace to elect the hypercall services that it wishes
> to expose to the guest, rather than the guest discovering them
> unconditionally. The idea employed by the series was taken from
> [1] as suggested by Marc Z.
> 
> In a broad sense, the concept is similar to the current implementation
> of PSCI interface- create a 'firmware psuedo-register' to handle the
> firmware revisions. The series extends this idea to all the other
> hypercalls such as TRNG (True Random Number Generator), PV_TIME
> (Paravirtualized Time), and PTP (Precision Time protocol).
> 
> For better categorization and future scaling, these firmware registers
> are categorized based on the service call owners. Also, unlike the
> existing firmware psuedo-registers, they hold the features supported
> in the form of a bitmap.
> 
> During the VM initialization, the registers holds an upper-limit of
> the features supported by each one of them. It's expected that the
> userspace discover the features provided by each register via GET_ONE_REG,
> and writeback the desired values using SET_ONE_REG. KVM allows this
> modification only until the VM has started.
> 
> Some of the standard function-ids, such as ARM_SMCCC_VERSION_FUNC_ID,
> need not be associated with a feature bit. For such ids, the series
> introduced an allowed-list, hvc_func_default_allowed_list[], that holds
> all such ids. As a result, the functions that are not elected by userspace,
> or if they are not a part of this allowed-list, will be denied for when
> the guests invoke them.
> 
> Older VMMs can simply ignore this interface and the hypercall services
> will be exposed unconditionally to the guests, thus ensuring backward
> compatibility.
> 

[...]

I rethinking about the design again and just get one question. Hopefully,
someone have the answer for us. The newly added 3 pseudo registers and
the existing ones like KVM_REG_ARM_PSCI_VERSION are all tied up with
vcpu, instead of VM. I don't think it's correct. I'm not sure if VM-scoped
pseudo registers aren't allowed by ARM architecture or the effort isn't
worthy to support it.

These pseudo registers are introduced to present the available hypercalls,
and then they can be disabled from userspace. In the implementation, these 3
registers are vcpu scoped. It means that multiple vcpus can be asymmetric
in terms of usable hypercalls. For example, ARM_SMCCC_TRNG hypercalls
can be enabled on vcpu0, but disabled on vcpu1. I don't think it's expected.

On the other hand, the information stored in these 3 registers needs to
be migrated through {GET,SET}_ONE_REG by VMM (QEMU). all the information
stored in these 3 registers are all same on all vcpus, which is exactly
as we expect. In migration circumstance, we're transporting identical
information for all vcpus and it's unnecessary.

Thanks,
Gavin




_______________________________________________
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] 96+ messages in thread

* Re: [PATCH v5 00/10] KVM: arm64: Add support for hypercall services selection
  2022-04-15  6:44   ` Gavin Shan
  (?)
@ 2022-04-15  8:58     ` Marc Zyngier
  -1 siblings, 0 replies; 96+ messages in thread
From: Marc Zyngier @ 2022-04-15  8:58 UTC (permalink / raw)
  To: Gavin Shan
  Cc: Raghavendra Rao Ananta, Andrew Jones, James Morse,
	Alexandru Elisei, Suzuki K Poulose, kvm, Catalin Marinas,
	Peter Shier, linux-kernel, Paolo Bonzini, Will Deacon, kvmarm,
	linux-arm-kernel

On Fri, 15 Apr 2022 07:44:55 +0100,
Gavin Shan <gshan@redhat.com> wrote:
> 
> Hi Raghavendra,
> 
> On 4/7/22 9:15 AM, Raghavendra Rao Ananta wrote:
> > Continuing the discussion from [1], the series tries to add support
> > for the userspace to elect the hypercall services that it wishes
> > to expose to the guest, rather than the guest discovering them
> > unconditionally. The idea employed by the series was taken from
> > [1] as suggested by Marc Z.
> > 
> > In a broad sense, the concept is similar to the current implementation
> > of PSCI interface- create a 'firmware psuedo-register' to handle the
> > firmware revisions. The series extends this idea to all the other
> > hypercalls such as TRNG (True Random Number Generator), PV_TIME
> > (Paravirtualized Time), and PTP (Precision Time protocol).
> > 
> > For better categorization and future scaling, these firmware registers
> > are categorized based on the service call owners. Also, unlike the
> > existing firmware psuedo-registers, they hold the features supported
> > in the form of a bitmap.
> > 
> > During the VM initialization, the registers holds an upper-limit of
> > the features supported by each one of them. It's expected that the
> > userspace discover the features provided by each register via GET_ONE_REG,
> > and writeback the desired values using SET_ONE_REG. KVM allows this
> > modification only until the VM has started.
> > 
> > Some of the standard function-ids, such as ARM_SMCCC_VERSION_FUNC_ID,
> > need not be associated with a feature bit. For such ids, the series
> > introduced an allowed-list, hvc_func_default_allowed_list[], that holds
> > all such ids. As a result, the functions that are not elected by userspace,
> > or if they are not a part of this allowed-list, will be denied for when
> > the guests invoke them.
> > 
> > Older VMMs can simply ignore this interface and the hypercall services
> > will be exposed unconditionally to the guests, thus ensuring backward
> > compatibility.
> > 
> 
> [...]
> 
> I rethinking about the design again and just get one question. Hopefully,
> someone have the answer for us. The newly added 3 pseudo registers and
> the existing ones like KVM_REG_ARM_PSCI_VERSION are all tied up with
> vcpu, instead of VM. I don't think it's correct. I'm not sure if VM-scoped
> pseudo registers aren't allowed by ARM architecture or the effort isn't
> worthy to support it.

We have had that discussion before (around version 2 of this series,
if I remember well).

> 
> These pseudo registers are introduced to present the available hypercalls,
> and then they can be disabled from userspace. In the implementation, these 3
> registers are vcpu scoped. It means that multiple vcpus can be asymmetric
> in terms of usable hypercalls. For example, ARM_SMCCC_TRNG hypercalls
> can be enabled on vcpu0, but disabled on vcpu1. I don't think it's expected.

No, that's not the way this is supposed to work. These hypercalls are
of course global, even if the accessor is per-vcpu. This is similar to
tons of other things, such as some of the PMU data, the timer virtual
offset... the list goes on. If that's not what this code does, then it
is a bug and it needs to be fixed.

> On the other hand, the information stored in these 3 registers needs to
> be migrated through {GET,SET}_ONE_REG by VMM (QEMU). all the information
> stored in these 3 registers are all same on all vcpus, which is exactly
> as we expect. In migration circumstance, we're transporting identical
> information for all vcpus and it's unnecessary.

Yes, we all understand that. My response to that was (and still is):

- There is no need to invent a new userspace interface. The one we
  have is terrible enough, and we don't need another square wheel that
  would need to be maintained beside the existing one.

- Let's say we have 1024 new pseudo-registers, 1024 vcpus, 64bit regs:
  that's 8MB worth of extra data. This is not insignificant, but also
  not really a problem given that such a large VM is probably attached
  to a proportionally large amount of memory. In practice, we're
  talking of less than 10 registers, and less than 100 vcpus. A crazy
  8kB at most. Who cares?

- If this is eventually deemed to be a *real* scalability problem, we
  can always expose a map of registers that are global, and let
  userspace know that it can elide the rest. Problem solved, backward
  compatibility preserved. And I'm willing to bet that we won't need
  it in my lifetime.

Thanks,

	M.

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

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

* Re: [PATCH v5 00/10] KVM: arm64: Add support for hypercall services selection
@ 2022-04-15  8:58     ` Marc Zyngier
  0 siblings, 0 replies; 96+ messages in thread
From: Marc Zyngier @ 2022-04-15  8:58 UTC (permalink / raw)
  To: Gavin Shan
  Cc: kvm, Catalin Marinas, Peter Shier, linux-kernel, Will Deacon,
	Paolo Bonzini, kvmarm, linux-arm-kernel

On Fri, 15 Apr 2022 07:44:55 +0100,
Gavin Shan <gshan@redhat.com> wrote:
> 
> Hi Raghavendra,
> 
> On 4/7/22 9:15 AM, Raghavendra Rao Ananta wrote:
> > Continuing the discussion from [1], the series tries to add support
> > for the userspace to elect the hypercall services that it wishes
> > to expose to the guest, rather than the guest discovering them
> > unconditionally. The idea employed by the series was taken from
> > [1] as suggested by Marc Z.
> > 
> > In a broad sense, the concept is similar to the current implementation
> > of PSCI interface- create a 'firmware psuedo-register' to handle the
> > firmware revisions. The series extends this idea to all the other
> > hypercalls such as TRNG (True Random Number Generator), PV_TIME
> > (Paravirtualized Time), and PTP (Precision Time protocol).
> > 
> > For better categorization and future scaling, these firmware registers
> > are categorized based on the service call owners. Also, unlike the
> > existing firmware psuedo-registers, they hold the features supported
> > in the form of a bitmap.
> > 
> > During the VM initialization, the registers holds an upper-limit of
> > the features supported by each one of them. It's expected that the
> > userspace discover the features provided by each register via GET_ONE_REG,
> > and writeback the desired values using SET_ONE_REG. KVM allows this
> > modification only until the VM has started.
> > 
> > Some of the standard function-ids, such as ARM_SMCCC_VERSION_FUNC_ID,
> > need not be associated with a feature bit. For such ids, the series
> > introduced an allowed-list, hvc_func_default_allowed_list[], that holds
> > all such ids. As a result, the functions that are not elected by userspace,
> > or if they are not a part of this allowed-list, will be denied for when
> > the guests invoke them.
> > 
> > Older VMMs can simply ignore this interface and the hypercall services
> > will be exposed unconditionally to the guests, thus ensuring backward
> > compatibility.
> > 
> 
> [...]
> 
> I rethinking about the design again and just get one question. Hopefully,
> someone have the answer for us. The newly added 3 pseudo registers and
> the existing ones like KVM_REG_ARM_PSCI_VERSION are all tied up with
> vcpu, instead of VM. I don't think it's correct. I'm not sure if VM-scoped
> pseudo registers aren't allowed by ARM architecture or the effort isn't
> worthy to support it.

We have had that discussion before (around version 2 of this series,
if I remember well).

> 
> These pseudo registers are introduced to present the available hypercalls,
> and then they can be disabled from userspace. In the implementation, these 3
> registers are vcpu scoped. It means that multiple vcpus can be asymmetric
> in terms of usable hypercalls. For example, ARM_SMCCC_TRNG hypercalls
> can be enabled on vcpu0, but disabled on vcpu1. I don't think it's expected.

No, that's not the way this is supposed to work. These hypercalls are
of course global, even if the accessor is per-vcpu. This is similar to
tons of other things, such as some of the PMU data, the timer virtual
offset... the list goes on. If that's not what this code does, then it
is a bug and it needs to be fixed.

> On the other hand, the information stored in these 3 registers needs to
> be migrated through {GET,SET}_ONE_REG by VMM (QEMU). all the information
> stored in these 3 registers are all same on all vcpus, which is exactly
> as we expect. In migration circumstance, we're transporting identical
> information for all vcpus and it's unnecessary.

Yes, we all understand that. My response to that was (and still is):

- There is no need to invent a new userspace interface. The one we
  have is terrible enough, and we don't need another square wheel that
  would need to be maintained beside the existing one.

- Let's say we have 1024 new pseudo-registers, 1024 vcpus, 64bit regs:
  that's 8MB worth of extra data. This is not insignificant, but also
  not really a problem given that such a large VM is probably attached
  to a proportionally large amount of memory. In practice, we're
  talking of less than 10 registers, and less than 100 vcpus. A crazy
  8kB at most. Who cares?

- If this is eventually deemed to be a *real* scalability problem, we
  can always expose a map of registers that are global, and let
  userspace know that it can elide the rest. Problem solved, backward
  compatibility preserved. And I'm willing to bet that we won't need
  it in my lifetime.

Thanks,

	M.

-- 
Without deviation from the norm, progress is not possible.
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

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

* Re: [PATCH v5 00/10] KVM: arm64: Add support for hypercall services selection
@ 2022-04-15  8:58     ` Marc Zyngier
  0 siblings, 0 replies; 96+ messages in thread
From: Marc Zyngier @ 2022-04-15  8:58 UTC (permalink / raw)
  To: Gavin Shan
  Cc: Raghavendra Rao Ananta, Andrew Jones, James Morse,
	Alexandru Elisei, Suzuki K Poulose, kvm, Catalin Marinas,
	Peter Shier, linux-kernel, Paolo Bonzini, Will Deacon, kvmarm,
	linux-arm-kernel

On Fri, 15 Apr 2022 07:44:55 +0100,
Gavin Shan <gshan@redhat.com> wrote:
> 
> Hi Raghavendra,
> 
> On 4/7/22 9:15 AM, Raghavendra Rao Ananta wrote:
> > Continuing the discussion from [1], the series tries to add support
> > for the userspace to elect the hypercall services that it wishes
> > to expose to the guest, rather than the guest discovering them
> > unconditionally. The idea employed by the series was taken from
> > [1] as suggested by Marc Z.
> > 
> > In a broad sense, the concept is similar to the current implementation
> > of PSCI interface- create a 'firmware psuedo-register' to handle the
> > firmware revisions. The series extends this idea to all the other
> > hypercalls such as TRNG (True Random Number Generator), PV_TIME
> > (Paravirtualized Time), and PTP (Precision Time protocol).
> > 
> > For better categorization and future scaling, these firmware registers
> > are categorized based on the service call owners. Also, unlike the
> > existing firmware psuedo-registers, they hold the features supported
> > in the form of a bitmap.
> > 
> > During the VM initialization, the registers holds an upper-limit of
> > the features supported by each one of them. It's expected that the
> > userspace discover the features provided by each register via GET_ONE_REG,
> > and writeback the desired values using SET_ONE_REG. KVM allows this
> > modification only until the VM has started.
> > 
> > Some of the standard function-ids, such as ARM_SMCCC_VERSION_FUNC_ID,
> > need not be associated with a feature bit. For such ids, the series
> > introduced an allowed-list, hvc_func_default_allowed_list[], that holds
> > all such ids. As a result, the functions that are not elected by userspace,
> > or if they are not a part of this allowed-list, will be denied for when
> > the guests invoke them.
> > 
> > Older VMMs can simply ignore this interface and the hypercall services
> > will be exposed unconditionally to the guests, thus ensuring backward
> > compatibility.
> > 
> 
> [...]
> 
> I rethinking about the design again and just get one question. Hopefully,
> someone have the answer for us. The newly added 3 pseudo registers and
> the existing ones like KVM_REG_ARM_PSCI_VERSION are all tied up with
> vcpu, instead of VM. I don't think it's correct. I'm not sure if VM-scoped
> pseudo registers aren't allowed by ARM architecture or the effort isn't
> worthy to support it.

We have had that discussion before (around version 2 of this series,
if I remember well).

> 
> These pseudo registers are introduced to present the available hypercalls,
> and then they can be disabled from userspace. In the implementation, these 3
> registers are vcpu scoped. It means that multiple vcpus can be asymmetric
> in terms of usable hypercalls. For example, ARM_SMCCC_TRNG hypercalls
> can be enabled on vcpu0, but disabled on vcpu1. I don't think it's expected.

No, that's not the way this is supposed to work. These hypercalls are
of course global, even if the accessor is per-vcpu. This is similar to
tons of other things, such as some of the PMU data, the timer virtual
offset... the list goes on. If that's not what this code does, then it
is a bug and it needs to be fixed.

> On the other hand, the information stored in these 3 registers needs to
> be migrated through {GET,SET}_ONE_REG by VMM (QEMU). all the information
> stored in these 3 registers are all same on all vcpus, which is exactly
> as we expect. In migration circumstance, we're transporting identical
> information for all vcpus and it's unnecessary.

Yes, we all understand that. My response to that was (and still is):

- There is no need to invent a new userspace interface. The one we
  have is terrible enough, and we don't need another square wheel that
  would need to be maintained beside the existing one.

- Let's say we have 1024 new pseudo-registers, 1024 vcpus, 64bit regs:
  that's 8MB worth of extra data. This is not insignificant, but also
  not really a problem given that such a large VM is probably attached
  to a proportionally large amount of memory. In practice, we're
  talking of less than 10 registers, and less than 100 vcpus. A crazy
  8kB at most. Who cares?

- If this is eventually deemed to be a *real* scalability problem, we
  can always expose a map of registers that are global, and let
  userspace know that it can elide the rest. Problem solved, backward
  compatibility preserved. And I'm willing to bet that we won't need
  it in my lifetime.

Thanks,

	M.

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

_______________________________________________
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] 96+ messages in thread

* Re: [PATCH v5 00/10] KVM: arm64: Add support for hypercall services selection
  2022-04-15  8:58     ` Marc Zyngier
  (?)
@ 2022-04-18  2:53       ` Gavin Shan
  -1 siblings, 0 replies; 96+ messages in thread
From: Gavin Shan @ 2022-04-18  2:53 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: Raghavendra Rao Ananta, Andrew Jones, James Morse,
	Alexandru Elisei, Suzuki K Poulose, kvm, Catalin Marinas,
	Peter Shier, linux-kernel, Paolo Bonzini, Will Deacon, kvmarm,
	linux-arm-kernel

Hi Marc,

On 4/15/22 4:58 PM, Marc Zyngier wrote:
> On Fri, 15 Apr 2022 07:44:55 +0100,
> Gavin Shan <gshan@redhat.com> wrote:
>> On 4/7/22 9:15 AM, Raghavendra Rao Ananta wrote:
>>> Continuing the discussion from [1], the series tries to add support
>>> for the userspace to elect the hypercall services that it wishes
>>> to expose to the guest, rather than the guest discovering them
>>> unconditionally. The idea employed by the series was taken from
>>> [1] as suggested by Marc Z.
>>>
>>> In a broad sense, the concept is similar to the current implementation
>>> of PSCI interface- create a 'firmware psuedo-register' to handle the
>>> firmware revisions. The series extends this idea to all the other
>>> hypercalls such as TRNG (True Random Number Generator), PV_TIME
>>> (Paravirtualized Time), and PTP (Precision Time protocol).
>>>
>>> For better categorization and future scaling, these firmware registers
>>> are categorized based on the service call owners. Also, unlike the
>>> existing firmware psuedo-registers, they hold the features supported
>>> in the form of a bitmap.
>>>
>>> During the VM initialization, the registers holds an upper-limit of
>>> the features supported by each one of them. It's expected that the
>>> userspace discover the features provided by each register via GET_ONE_REG,
>>> and writeback the desired values using SET_ONE_REG. KVM allows this
>>> modification only until the VM has started.
>>>
>>> Some of the standard function-ids, such as ARM_SMCCC_VERSION_FUNC_ID,
>>> need not be associated with a feature bit. For such ids, the series
>>> introduced an allowed-list, hvc_func_default_allowed_list[], that holds
>>> all such ids. As a result, the functions that are not elected by userspace,
>>> or if they are not a part of this allowed-list, will be denied for when
>>> the guests invoke them.
>>>
>>> Older VMMs can simply ignore this interface and the hypercall services
>>> will be exposed unconditionally to the guests, thus ensuring backward
>>> compatibility.
>>>
>>
>> [...]
>>
>> I rethinking about the design again and just get one question. Hopefully,
>> someone have the answer for us. The newly added 3 pseudo registers and
>> the existing ones like KVM_REG_ARM_PSCI_VERSION are all tied up with
>> vcpu, instead of VM. I don't think it's correct. I'm not sure if VM-scoped
>> pseudo registers aren't allowed by ARM architecture or the effort isn't
>> worthy to support it.
> 
> We have had that discussion before (around version 2 of this series,
> if I remember well).
> 

Yeah, I'm chime-in this series lately. There must be some discussions,
including this topic, I missed :)

>>
>> These pseudo registers are introduced to present the available hypercalls,
>> and then they can be disabled from userspace. In the implementation, these 3
>> registers are vcpu scoped. It means that multiple vcpus can be asymmetric
>> in terms of usable hypercalls. For example, ARM_SMCCC_TRNG hypercalls
>> can be enabled on vcpu0, but disabled on vcpu1. I don't think it's expected.
> 
> No, that's not the way this is supposed to work. These hypercalls are
> of course global, even if the accessor is per-vcpu. This is similar to
> tons of other things, such as some of the PMU data, the timer virtual
> offset... the list goes on. If that's not what this code does, then it
> is a bug and it needs to be fixed.
> 

Ok.

>> On the other hand, the information stored in these 3 registers needs to
>> be migrated through {GET,SET}_ONE_REG by VMM (QEMU). all the information
>> stored in these 3 registers are all same on all vcpus, which is exactly
>> as we expect. In migration circumstance, we're transporting identical
>> information for all vcpus and it's unnecessary.
> 
> Yes, we all understand that. My response to that was (and still is):
> 
> - There is no need to invent a new userspace interface. The one we
>    have is terrible enough, and we don't need another square wheel that
>    would need to be maintained beside the existing one.
> 
> - Let's say we have 1024 new pseudo-registers, 1024 vcpus, 64bit regs:
>    that's 8MB worth of extra data. This is not insignificant, but also
>    not really a problem given that such a large VM is probably attached
>    to a proportionally large amount of memory. In practice, we're
>    talking of less than 10 registers, and less than 100 vcpus. A crazy
>    8kB at most. Who cares?
> 
> - If this is eventually deemed to be a *real* scalability problem, we
>    can always expose a map of registers that are global, and let
>    userspace know that it can elide the rest. Problem solved, backward
>    compatibility preserved. And I'm willing to bet that we won't need
>    it in my lifetime.
> 

The reason why I raised question is just to check if it's a missed
point in the design. As I said, I obviously missed the previous
discussions and glad that this has been discussed through.

Thanks for the details. Yes, it's totally fine to migrate 8KB data.
Besides, VMM (QEMU) can choose to do migration on one single vcpu,
instead of all of them, as you said.

Thanks,
Gavin


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

* Re: [PATCH v5 00/10] KVM: arm64: Add support for hypercall services selection
@ 2022-04-18  2:53       ` Gavin Shan
  0 siblings, 0 replies; 96+ messages in thread
From: Gavin Shan @ 2022-04-18  2:53 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: kvm, Catalin Marinas, Peter Shier, linux-kernel, Will Deacon,
	Paolo Bonzini, kvmarm, linux-arm-kernel

Hi Marc,

On 4/15/22 4:58 PM, Marc Zyngier wrote:
> On Fri, 15 Apr 2022 07:44:55 +0100,
> Gavin Shan <gshan@redhat.com> wrote:
>> On 4/7/22 9:15 AM, Raghavendra Rao Ananta wrote:
>>> Continuing the discussion from [1], the series tries to add support
>>> for the userspace to elect the hypercall services that it wishes
>>> to expose to the guest, rather than the guest discovering them
>>> unconditionally. The idea employed by the series was taken from
>>> [1] as suggested by Marc Z.
>>>
>>> In a broad sense, the concept is similar to the current implementation
>>> of PSCI interface- create a 'firmware psuedo-register' to handle the
>>> firmware revisions. The series extends this idea to all the other
>>> hypercalls such as TRNG (True Random Number Generator), PV_TIME
>>> (Paravirtualized Time), and PTP (Precision Time protocol).
>>>
>>> For better categorization and future scaling, these firmware registers
>>> are categorized based on the service call owners. Also, unlike the
>>> existing firmware psuedo-registers, they hold the features supported
>>> in the form of a bitmap.
>>>
>>> During the VM initialization, the registers holds an upper-limit of
>>> the features supported by each one of them. It's expected that the
>>> userspace discover the features provided by each register via GET_ONE_REG,
>>> and writeback the desired values using SET_ONE_REG. KVM allows this
>>> modification only until the VM has started.
>>>
>>> Some of the standard function-ids, such as ARM_SMCCC_VERSION_FUNC_ID,
>>> need not be associated with a feature bit. For such ids, the series
>>> introduced an allowed-list, hvc_func_default_allowed_list[], that holds
>>> all such ids. As a result, the functions that are not elected by userspace,
>>> or if they are not a part of this allowed-list, will be denied for when
>>> the guests invoke them.
>>>
>>> Older VMMs can simply ignore this interface and the hypercall services
>>> will be exposed unconditionally to the guests, thus ensuring backward
>>> compatibility.
>>>
>>
>> [...]
>>
>> I rethinking about the design again and just get one question. Hopefully,
>> someone have the answer for us. The newly added 3 pseudo registers and
>> the existing ones like KVM_REG_ARM_PSCI_VERSION are all tied up with
>> vcpu, instead of VM. I don't think it's correct. I'm not sure if VM-scoped
>> pseudo registers aren't allowed by ARM architecture or the effort isn't
>> worthy to support it.
> 
> We have had that discussion before (around version 2 of this series,
> if I remember well).
> 

Yeah, I'm chime-in this series lately. There must be some discussions,
including this topic, I missed :)

>>
>> These pseudo registers are introduced to present the available hypercalls,
>> and then they can be disabled from userspace. In the implementation, these 3
>> registers are vcpu scoped. It means that multiple vcpus can be asymmetric
>> in terms of usable hypercalls. For example, ARM_SMCCC_TRNG hypercalls
>> can be enabled on vcpu0, but disabled on vcpu1. I don't think it's expected.
> 
> No, that's not the way this is supposed to work. These hypercalls are
> of course global, even if the accessor is per-vcpu. This is similar to
> tons of other things, such as some of the PMU data, the timer virtual
> offset... the list goes on. If that's not what this code does, then it
> is a bug and it needs to be fixed.
> 

Ok.

>> On the other hand, the information stored in these 3 registers needs to
>> be migrated through {GET,SET}_ONE_REG by VMM (QEMU). all the information
>> stored in these 3 registers are all same on all vcpus, which is exactly
>> as we expect. In migration circumstance, we're transporting identical
>> information for all vcpus and it's unnecessary.
> 
> Yes, we all understand that. My response to that was (and still is):
> 
> - There is no need to invent a new userspace interface. The one we
>    have is terrible enough, and we don't need another square wheel that
>    would need to be maintained beside the existing one.
> 
> - Let's say we have 1024 new pseudo-registers, 1024 vcpus, 64bit regs:
>    that's 8MB worth of extra data. This is not insignificant, but also
>    not really a problem given that such a large VM is probably attached
>    to a proportionally large amount of memory. In practice, we're
>    talking of less than 10 registers, and less than 100 vcpus. A crazy
>    8kB at most. Who cares?
> 
> - If this is eventually deemed to be a *real* scalability problem, we
>    can always expose a map of registers that are global, and let
>    userspace know that it can elide the rest. Problem solved, backward
>    compatibility preserved. And I'm willing to bet that we won't need
>    it in my lifetime.
> 

The reason why I raised question is just to check if it's a missed
point in the design. As I said, I obviously missed the previous
discussions and glad that this has been discussed through.

Thanks for the details. Yes, it's totally fine to migrate 8KB data.
Besides, VMM (QEMU) can choose to do migration on one single vcpu,
instead of all of them, as you said.

Thanks,
Gavin

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

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

* Re: [PATCH v5 00/10] KVM: arm64: Add support for hypercall services selection
@ 2022-04-18  2:53       ` Gavin Shan
  0 siblings, 0 replies; 96+ messages in thread
From: Gavin Shan @ 2022-04-18  2:53 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: Raghavendra Rao Ananta, Andrew Jones, James Morse,
	Alexandru Elisei, Suzuki K Poulose, kvm, Catalin Marinas,
	Peter Shier, linux-kernel, Paolo Bonzini, Will Deacon, kvmarm,
	linux-arm-kernel

Hi Marc,

On 4/15/22 4:58 PM, Marc Zyngier wrote:
> On Fri, 15 Apr 2022 07:44:55 +0100,
> Gavin Shan <gshan@redhat.com> wrote:
>> On 4/7/22 9:15 AM, Raghavendra Rao Ananta wrote:
>>> Continuing the discussion from [1], the series tries to add support
>>> for the userspace to elect the hypercall services that it wishes
>>> to expose to the guest, rather than the guest discovering them
>>> unconditionally. The idea employed by the series was taken from
>>> [1] as suggested by Marc Z.
>>>
>>> In a broad sense, the concept is similar to the current implementation
>>> of PSCI interface- create a 'firmware psuedo-register' to handle the
>>> firmware revisions. The series extends this idea to all the other
>>> hypercalls such as TRNG (True Random Number Generator), PV_TIME
>>> (Paravirtualized Time), and PTP (Precision Time protocol).
>>>
>>> For better categorization and future scaling, these firmware registers
>>> are categorized based on the service call owners. Also, unlike the
>>> existing firmware psuedo-registers, they hold the features supported
>>> in the form of a bitmap.
>>>
>>> During the VM initialization, the registers holds an upper-limit of
>>> the features supported by each one of them. It's expected that the
>>> userspace discover the features provided by each register via GET_ONE_REG,
>>> and writeback the desired values using SET_ONE_REG. KVM allows this
>>> modification only until the VM has started.
>>>
>>> Some of the standard function-ids, such as ARM_SMCCC_VERSION_FUNC_ID,
>>> need not be associated with a feature bit. For such ids, the series
>>> introduced an allowed-list, hvc_func_default_allowed_list[], that holds
>>> all such ids. As a result, the functions that are not elected by userspace,
>>> or if they are not a part of this allowed-list, will be denied for when
>>> the guests invoke them.
>>>
>>> Older VMMs can simply ignore this interface and the hypercall services
>>> will be exposed unconditionally to the guests, thus ensuring backward
>>> compatibility.
>>>
>>
>> [...]
>>
>> I rethinking about the design again and just get one question. Hopefully,
>> someone have the answer for us. The newly added 3 pseudo registers and
>> the existing ones like KVM_REG_ARM_PSCI_VERSION are all tied up with
>> vcpu, instead of VM. I don't think it's correct. I'm not sure if VM-scoped
>> pseudo registers aren't allowed by ARM architecture or the effort isn't
>> worthy to support it.
> 
> We have had that discussion before (around version 2 of this series,
> if I remember well).
> 

Yeah, I'm chime-in this series lately. There must be some discussions,
including this topic, I missed :)

>>
>> These pseudo registers are introduced to present the available hypercalls,
>> and then they can be disabled from userspace. In the implementation, these 3
>> registers are vcpu scoped. It means that multiple vcpus can be asymmetric
>> in terms of usable hypercalls. For example, ARM_SMCCC_TRNG hypercalls
>> can be enabled on vcpu0, but disabled on vcpu1. I don't think it's expected.
> 
> No, that's not the way this is supposed to work. These hypercalls are
> of course global, even if the accessor is per-vcpu. This is similar to
> tons of other things, such as some of the PMU data, the timer virtual
> offset... the list goes on. If that's not what this code does, then it
> is a bug and it needs to be fixed.
> 

Ok.

>> On the other hand, the information stored in these 3 registers needs to
>> be migrated through {GET,SET}_ONE_REG by VMM (QEMU). all the information
>> stored in these 3 registers are all same on all vcpus, which is exactly
>> as we expect. In migration circumstance, we're transporting identical
>> information for all vcpus and it's unnecessary.
> 
> Yes, we all understand that. My response to that was (and still is):
> 
> - There is no need to invent a new userspace interface. The one we
>    have is terrible enough, and we don't need another square wheel that
>    would need to be maintained beside the existing one.
> 
> - Let's say we have 1024 new pseudo-registers, 1024 vcpus, 64bit regs:
>    that's 8MB worth of extra data. This is not insignificant, but also
>    not really a problem given that such a large VM is probably attached
>    to a proportionally large amount of memory. In practice, we're
>    talking of less than 10 registers, and less than 100 vcpus. A crazy
>    8kB at most. Who cares?
> 
> - If this is eventually deemed to be a *real* scalability problem, we
>    can always expose a map of registers that are global, and let
>    userspace know that it can elide the rest. Problem solved, backward
>    compatibility preserved. And I'm willing to bet that we won't need
>    it in my lifetime.
> 

The reason why I raised question is just to check if it's a missed
point in the design. As I said, I obviously missed the previous
discussions and glad that this has been discussed through.

Thanks for the details. Yes, it's totally fine to migrate 8KB data.
Besides, VMM (QEMU) can choose to do migration on one single vcpu,
instead of all of them, as you said.

Thanks,
Gavin


_______________________________________________
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] 96+ messages in thread

end of thread, other threads:[~2022-04-18  2:55 UTC | newest]

Thread overview: 96+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-04-07  1:15 [PATCH v5 00/10] KVM: arm64: Add support for hypercall services selection Raghavendra Rao Ananta
2022-04-07  1:15 ` Raghavendra Rao Ananta
2022-04-07  1:15 ` Raghavendra Rao Ananta
2022-04-07  1:15 ` [PATCH v5 01/10] KVM: arm64: Factor out firmware register handling from psci.c Raghavendra Rao Ananta
2022-04-07  1:15   ` Raghavendra Rao Ananta
2022-04-07  1:15   ` Raghavendra Rao Ananta
2022-04-12  7:06   ` Gavin Shan
2022-04-12  7:06     ` Gavin Shan
2022-04-12  7:06     ` Gavin Shan
2022-04-12 16:41     ` Raghavendra Rao Ananta
2022-04-12 16:41       ` Raghavendra Rao Ananta
2022-04-12 16:41       ` Raghavendra Rao Ananta
2022-04-13  3:10       ` Gavin Shan
2022-04-13  3:10         ` Gavin Shan
2022-04-13  3:10         ` Gavin Shan
2022-04-07  1:15 ` [PATCH v5 02/10] KVM: arm64: Setup a framework for hypercall bitmap firmware registers Raghavendra Rao Ananta
2022-04-07  1:15   ` Raghavendra Rao Ananta
2022-04-07  1:15   ` Raghavendra Rao Ananta
2022-04-07  9:06   ` Marc Zyngier
2022-04-07  9:06     ` Marc Zyngier
2022-04-07  9:06     ` Marc Zyngier
2022-04-07 17:24     ` Raghavendra Rao Ananta
2022-04-07 17:24       ` Raghavendra Rao Ananta
2022-04-07 17:24       ` Raghavendra Rao Ananta
2022-04-08 16:59       ` Marc Zyngier
2022-04-08 16:59         ` Marc Zyngier
2022-04-08 16:59         ` Marc Zyngier
2022-04-08 17:34         ` Raghavendra Rao Ananta
2022-04-08 17:34           ` Raghavendra Rao Ananta
2022-04-08 17:34           ` Raghavendra Rao Ananta
2022-04-07  1:15 ` [PATCH v5 03/10] KVM: arm64: Add standard hypervisor firmware register Raghavendra Rao Ananta
2022-04-07  1:15   ` Raghavendra Rao Ananta
2022-04-07  1:15   ` Raghavendra Rao Ananta
2022-04-07  1:15 ` [PATCH v5 04/10] KVM: arm64: Add vendor " Raghavendra Rao Ananta
2022-04-07  1:15   ` Raghavendra Rao Ananta
2022-04-07  1:15   ` Raghavendra Rao Ananta
2022-04-13  3:59   ` Gavin Shan
2022-04-13  3:59     ` Gavin Shan
2022-04-13  3:59     ` Gavin Shan
2022-04-13 16:59     ` Raghavendra Rao Ananta
2022-04-13 16:59       ` Raghavendra Rao Ananta
2022-04-13 16:59       ` Raghavendra Rao Ananta
2022-04-14  1:04       ` Gavin Shan
2022-04-14  1:04         ` Gavin Shan
2022-04-14  1:04         ` Gavin Shan
2022-04-14 17:05         ` Raghavendra Rao Ananta
2022-04-14 17:05           ` Raghavendra Rao Ananta
2022-04-14 17:05           ` Raghavendra Rao Ananta
2022-04-07  1:16 ` [PATCH v5 05/10] Docs: KVM: Rename psci.rst to hypercalls.rst Raghavendra Rao Ananta
2022-04-07  1:16   ` Raghavendra Rao Ananta
2022-04-07  1:16   ` Raghavendra Rao Ananta
2022-04-07  1:16 ` [PATCH v5 06/10] Docs: KVM: Add doc for the bitmap firmware registers Raghavendra Rao Ananta
2022-04-07  1:16   ` Raghavendra Rao Ananta
2022-04-07  1:16   ` Raghavendra Rao Ananta
2022-04-13  6:39   ` Gavin Shan
2022-04-13  6:39     ` Gavin Shan
2022-04-13  6:39     ` Gavin Shan
2022-04-13 17:01     ` Raghavendra Rao Ananta
2022-04-13 17:01       ` Raghavendra Rao Ananta
2022-04-13 17:01       ` Raghavendra Rao Ananta
2022-04-07  1:16 ` [PATCH v5 07/10] tools: Import ARM SMCCC definitions Raghavendra Rao Ananta
2022-04-07  1:16   ` Raghavendra Rao Ananta
2022-04-07  1:16   ` Raghavendra Rao Ananta
2022-04-07  1:16 ` [PATCH v5 08/10] selftests: KVM: aarch64: Introduce hypercall ABI test Raghavendra Rao Ananta
2022-04-07  1:16   ` Raghavendra Rao Ananta
2022-04-07  1:16   ` Raghavendra Rao Ananta
2022-04-13  9:07   ` Gavin Shan
2022-04-13  9:07     ` Gavin Shan
2022-04-13  9:07     ` Gavin Shan
2022-04-13 17:32     ` Raghavendra Rao Ananta
2022-04-13 17:32       ` Raghavendra Rao Ananta
2022-04-13 17:32       ` Raghavendra Rao Ananta
2022-04-14  1:09       ` Gavin Shan
2022-04-14  1:09         ` Gavin Shan
2022-04-14  1:09         ` Gavin Shan
2022-04-07  1:16 ` [PATCH v5 09/10] selftests: KVM: aarch64: Add the bitmap firmware registers to get-reg-list Raghavendra Rao Ananta
2022-04-07  1:16   ` Raghavendra Rao Ananta
2022-04-07  1:16   ` Raghavendra Rao Ananta
2022-04-07  1:16 ` [PATCH v5 10/10] selftests: KVM: aarch64: Add KVM_REG_ARM_FW_REG(3) " Raghavendra Rao Ananta
2022-04-07  1:16   ` Raghavendra Rao Ananta
2022-04-07  1:16   ` Raghavendra Rao Ananta
2022-04-13  9:22   ` Gavin Shan
2022-04-13  9:22     ` Gavin Shan
2022-04-13  9:22     ` Gavin Shan
2022-04-13 17:33     ` Raghavendra Rao Ananta
2022-04-13 17:33       ` Raghavendra Rao Ananta
2022-04-13 17:33       ` Raghavendra Rao Ananta
2022-04-15  6:44 ` [PATCH v5 00/10] KVM: arm64: Add support for hypercall services selection Gavin Shan
2022-04-15  6:44   ` Gavin Shan
2022-04-15  6:44   ` Gavin Shan
2022-04-15  8:58   ` Marc Zyngier
2022-04-15  8:58     ` Marc Zyngier
2022-04-15  8:58     ` Marc Zyngier
2022-04-18  2:53     ` Gavin Shan
2022-04-18  2:53       ` Gavin Shan
2022-04-18  2:53       ` Gavin Shan

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.