[RFC PATCH 23/26] kvm: arm64: Intercept host's CPU_SUSPEND PSCI SMCs

Andrew Walbran qwandor at google.com
Thu Nov 5 05:34:15 EST 2020


On Wed, 4 Nov 2020 at 19:37, 'David Brazdil' via kernel-team
<kernel-team at android.com> wrote:
>
> Add a handler of CPU_SUSPEND host PSCI SMCs. When invoked, it determines
> whether the requested power state loses context, ie. whether it is
> indistinguishable from a WHI or whether it is a deeper sleep state that
Do you mean WFI?

> behaves like a CPU_OFF+CPU_ON.
>
> If it's the former, it forwards the call to EL3 and returns to the host
> after waking up.
>
> If it's the latter, it saves r0,pc of the host into and makes the same
> call to EL3 with the hyp CPU entry point. When the core wakes up, EL2
> state is initialized before dropping back to EL1.
>
> Signed-off-by: David Brazdil <dbrazdil at google.com>
> ---
>  arch/arm64/kvm/arm.c           |  2 ++
>  arch/arm64/kvm/hyp/nvhe/psci.c | 49 +++++++++++++++++++++++++++++++++-
>  drivers/firmware/psci/psci.c   |  9 -------
>  include/uapi/linux/psci.h      |  7 +++++
>  4 files changed, 57 insertions(+), 10 deletions(-)
>
> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> index 166975999ead..6fbda652200b 100644
> --- a/arch/arm64/kvm/arm.c
> +++ b/arch/arm64/kvm/arm.c
> @@ -1521,9 +1521,11 @@ static void init_psci(void)
>  {
>         extern u32 kvm_nvhe_sym(kvm_host_psci_version);
>         extern u32 kvm_nvhe_sym(kvm_host_psci_function_id)[PSCI_FN_MAX];
> +       extern u32 kvm_nvhe_sym(kvm_host_psci_cpu_suspend_feature);
>         int cpu;
>
>         kvm_nvhe_sym(kvm_host_psci_version) = psci_driver_version;
> +       kvm_nvhe_sym(kvm_host_psci_cpu_suspend_feature) = psci_cpu_suspend_feature;
>         memcpy(kvm_nvhe_sym(kvm_host_psci_function_id),
>                 psci_function_id, sizeof(psci_function_id));
>
> diff --git a/arch/arm64/kvm/hyp/nvhe/psci.c b/arch/arm64/kvm/hyp/nvhe/psci.c
> index 42ee5effa827..4899c8319bb4 100644
> --- a/arch/arm64/kvm/hyp/nvhe/psci.c
> +++ b/arch/arm64/kvm/hyp/nvhe/psci.c
> @@ -21,6 +21,7 @@
>  /* Config options set by the host. */
>  u32 kvm_host_psci_version = PSCI_VERSION(0, 0);
>  u32 kvm_host_psci_function_id[PSCI_FN_MAX];
> +u32 kvm_host_psci_cpu_suspend_feature;
>  s64 hyp_physvirt_offset;
>
>  #define __hyp_pa(x) ((phys_addr_t)(x) + hyp_physvirt_offset)
> @@ -83,6 +84,20 @@ static __noreturn unsigned long psci_forward_noreturn(struct kvm_cpu_context *ho
>         hyp_panic(); /* unreachable */
>  }
>
> +static bool psci_has_ext_power_state(void)
> +{
> +       return kvm_host_psci_cpu_suspend_feature & PSCI_1_0_FEATURES_CPU_SUSPEND_PF_MASK;
> +}
> +
> +static bool psci_power_state_loses_context(u32 state)
> +{
> +       const u32 mask = psci_has_ext_power_state() ?
> +                                       PSCI_1_0_EXT_POWER_STATE_TYPE_MASK :
> +                                       PSCI_0_2_POWER_STATE_TYPE_MASK;
> +
> +       return state & mask;
> +}
> +
>  static unsigned int find_cpu_id(u64 mpidr)
>  {
>         int i;
> @@ -106,6 +121,34 @@ static phys_addr_t cpu_entry_pa(void)
>         return kern_va - kimage_voffset;
>  }
>
> +static int psci_cpu_suspend(u64 func_id, struct kvm_cpu_context *host_ctxt)
> +{
> +       u64 power_state = host_ctxt->regs.regs[1];
> +       unsigned long pc = host_ctxt->regs.regs[2];
> +       unsigned long r0 = host_ctxt->regs.regs[3];
> +       hyp_spinlock_t *cpu_lock;
> +       struct vcpu_reset_state *cpu_reset;
> +       struct kvm_nvhe_init_params *cpu_params;
> +
> +       if (!psci_power_state_loses_context(power_state)) {
> +               /* This power state has the same semantics as WFI. */
> +               return psci_call(PSCI_0_2_FN64_CPU_SUSPEND, 0, 0, 0);
> +       }
> +
> +       cpu_lock = this_cpu_ptr(&psci_cpu_lock);
> +       cpu_reset = this_cpu_ptr(&psci_cpu_reset);
> +       cpu_params = this_cpu_ptr(&kvm_init_params);
> +
> +       /* Resuming from this state has the same semantics as CPU_ON. */
> +       hyp_spin_lock(cpu_lock);
> +       *cpu_reset = (struct vcpu_reset_state){
> +               .pc = pc,
> +               .r0 = r0,
> +       };
> +       hyp_spin_unlock(cpu_lock);
> +       return psci_call(func_id, power_state, cpu_entry_pa(), __hyp_pa(cpu_params));
> +}
> +
>  static int psci_cpu_off(u64 func_id, struct kvm_cpu_context *host_ctxt)
>  {
>         hyp_spinlock_t *cpu_lock = this_cpu_ptr(&psci_cpu_lock);
> @@ -193,7 +236,9 @@ static int psci_cpu_on(u64 func_id, struct kvm_cpu_context *host_ctxt)
>
>  static unsigned long psci_0_1_handler(u64 func_id, struct kvm_cpu_context *host_ctxt)
>  {
> -       if (func_id == kvm_host_psci_function_id[PSCI_FN_CPU_OFF])
> +       if (func_id == kvm_host_psci_function_id[PSCI_FN_CPU_SUSPEND])
> +               return psci_cpu_suspend(func_id, host_ctxt);
> +       else if (func_id == kvm_host_psci_function_id[PSCI_FN_CPU_OFF])
>                 return psci_cpu_off(func_id, host_ctxt);
>         else if (func_id == kvm_host_psci_function_id[PSCI_FN_CPU_ON])
>                 return psci_cpu_on(func_id, host_ctxt);
> @@ -216,6 +261,8 @@ static unsigned long psci_0_2_handler(u64 func_id, struct kvm_cpu_context *host_
>         case PSCI_0_2_FN_SYSTEM_RESET:
>                 psci_forward_noreturn(host_ctxt);
>                 unreachable();
> +       case PSCI_0_2_FN64_CPU_SUSPEND:
> +               return psci_cpu_suspend(func_id, host_ctxt);
>         case PSCI_0_2_FN_CPU_OFF:
>                 return psci_cpu_off(func_id, host_ctxt);
>         case PSCI_0_2_FN64_CPU_ON:
> diff --git a/drivers/firmware/psci/psci.c b/drivers/firmware/psci/psci.c
> index b6ad237b1518..387e24409da7 100644
> --- a/drivers/firmware/psci/psci.c
> +++ b/drivers/firmware/psci/psci.c
> @@ -62,15 +62,6 @@ static psci_fn *invoke_psci_fn;
>
>  u32 psci_function_id[PSCI_FN_MAX];
>
> -#define PSCI_0_2_POWER_STATE_MASK              \
> -                               (PSCI_0_2_POWER_STATE_ID_MASK | \
> -                               PSCI_0_2_POWER_STATE_TYPE_MASK | \
> -                               PSCI_0_2_POWER_STATE_AFFL_MASK)
> -
> -#define PSCI_1_0_EXT_POWER_STATE_MASK          \
> -                               (PSCI_1_0_EXT_POWER_STATE_ID_MASK | \
> -                               PSCI_1_0_EXT_POWER_STATE_TYPE_MASK)
> -
>  u32 psci_cpu_suspend_feature;
>  static bool psci_system_reset2_supported;
>
> diff --git a/include/uapi/linux/psci.h b/include/uapi/linux/psci.h
> index 0d52b8dbe8c2..df3d85ce86f7 100644
> --- a/include/uapi/linux/psci.h
> +++ b/include/uapi/linux/psci.h
> @@ -65,6 +65,10 @@
>  #define PSCI_0_2_POWER_STATE_AFFL_SHIFT                24
>  #define PSCI_0_2_POWER_STATE_AFFL_MASK         \
>                                 (0x3 << PSCI_0_2_POWER_STATE_AFFL_SHIFT)
> +#define PSCI_0_2_POWER_STATE_MASK                                      \
> +                               (PSCI_0_2_POWER_STATE_ID_MASK |         \
> +                                PSCI_0_2_POWER_STATE_TYPE_MASK |       \
> +                                PSCI_0_2_POWER_STATE_AFFL_MASK)
>
>  /* PSCI extended power state encoding for CPU_SUSPEND function */
>  #define PSCI_1_0_EXT_POWER_STATE_ID_MASK       0xfffffff
> @@ -72,6 +76,9 @@
>  #define PSCI_1_0_EXT_POWER_STATE_TYPE_SHIFT    30
>  #define PSCI_1_0_EXT_POWER_STATE_TYPE_MASK     \
>                                 (0x1 << PSCI_1_0_EXT_POWER_STATE_TYPE_SHIFT)
> +#define PSCI_1_0_EXT_POWER_STATE_MASK                                  \
> +                               (PSCI_1_0_EXT_POWER_STATE_ID_MASK |     \
> +                                PSCI_1_0_EXT_POWER_STATE_TYPE_MASK)
>
>  /* PSCI v0.2 affinity level state returned by AFFINITY_INFO */
>  #define PSCI_0_2_AFFINITY_LEVEL_ON             0
> --
> 2.29.1.341.ge80a0c044ae-goog
>
> --
> To unsubscribe from this group and stop receiving emails from it, send an email to kernel-team+unsubscribe at android.com.
>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: smime.p7s
Type: application/pkcs7-signature
Size: 3998 bytes
Desc: S/MIME Cryptographic Signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20201105/cd4d71e8/attachment.p7s>


More information about the linux-arm-kernel mailing list