qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH v8 16/19] hvf: arm: Implement PSCI handling


From: Peter Maydell
Subject: Re: [PATCH v8 16/19] hvf: arm: Implement PSCI handling
Date: Tue, 15 Jun 2021 13:54:25 +0100

On Wed, 19 May 2021 at 21:23, Alexander Graf <agraf@csgraf.de> wrote:
>
> We need to handle PSCI calls. Most of the TCG code works for us,
> but we can simplify it to only handle aa64 mode and we need to
> handle SUSPEND differently.
>
> This patch takes the TCG code as template and duplicates it in HVF.
>
> To tell the guest that we support PSCI 0.2 now, update the check in
> arm_cpu_initfn() as well.
>
> Signed-off-by: Alexander Graf <agraf@csgraf.de>
>
> ---
>
> v6 -> v7:
>
>   - This patch integrates "arm: Set PSCI to 0.2 for HVF"
>
> v7 -> v8:
>
>   - Do not advance for HVC, PC is already updated by hvf
>   - Fix checkpatch error


> +static int hvf_psci_cpu_off(ARMCPU *arm_cpu)
> +{
> +    int32_t ret = 0;
> +    ret = arm_set_cpu_off(arm_cpu->mp_affinity);
> +    assert(ret == QEMU_ARM_POWERCTL_RET_SUCCESS);
> +
> +    return 0;

If you're always returning 0 you might as well just make
it return void.

> +}
> +
> +static int hvf_handle_psci_call(CPUState *cpu)

I think the callsites would be clearer if you made the function
return true for "PSCI call handled", false for "not recognised,
give the guest an UNDEF". Code like
         if (hvf_handle_psci_call(cpu)) {
             stuff;
         }

looks like the 'stuff' is for the "psci call handled" case,
which at the moment it isn't.

Either way, a comment for this function describing what its
return value semantics are would be useful.

> +    ARMCPU *arm_cpu = ARM_CPU(cpu);
> +    CPUARMState *env = &arm_cpu->env;
> +    uint64_t param[4] = {
> +        env->xregs[0],
> +        env->xregs[1],
> +        env->xregs[2],
> +        env->xregs[3]
> +    };
> +    uint64_t context_id, mpidr;
> +    bool target_aarch64 = true;
> +    CPUState *target_cpu_state;
> +    ARMCPU *target_cpu;
> +    target_ulong entry;
> +    int target_el = 1;
> +    int32_t ret = 0;
> +
> +    trace_hvf_psci_call(param[0], param[1], param[2], param[3],
> +                        arm_cpu->mp_affinity);
> +
> +    switch (param[0]) {
> +    case QEMU_PSCI_0_2_FN_PSCI_VERSION:
> +        ret = QEMU_PSCI_0_2_RET_VERSION_0_2;
> +        break;
> +    case QEMU_PSCI_0_2_FN_MIGRATE_INFO_TYPE:
> +        ret = QEMU_PSCI_0_2_RET_TOS_MIGRATION_NOT_REQUIRED; /* No trusted OS 
> */
> +        break;
> +    case QEMU_PSCI_0_2_FN_AFFINITY_INFO:
> +    case QEMU_PSCI_0_2_FN64_AFFINITY_INFO:
> +        mpidr = param[1];
> +
> +        switch (param[2]) {
> +        case 0:
> +            target_cpu_state = arm_get_cpu_by_id(mpidr);
> +            if (!target_cpu_state) {
> +                ret = QEMU_PSCI_RET_INVALID_PARAMS;
> +                break;
> +            }
> +            target_cpu = ARM_CPU(target_cpu_state);
> +
> +            ret = target_cpu->power_state;
> +            break;
> +        default:
> +            /* Everything above affinity level 0 is always on. */
> +            ret = 0;
> +        }
> +        break;
> +    case QEMU_PSCI_0_2_FN_SYSTEM_RESET:
> +        qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
> +        /* QEMU reset and shutdown are async requests, but PSCI
> +         * mandates that we never return from the reset/shutdown
> +         * call, so power the CPU off now so it doesn't execute
> +         * anything further.
> +         */
> +        return hvf_psci_cpu_off(arm_cpu);
> +    case QEMU_PSCI_0_2_FN_SYSTEM_OFF:
> +        qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
> +        return hvf_psci_cpu_off(arm_cpu);
> +    case QEMU_PSCI_0_1_FN_CPU_ON:
> +    case QEMU_PSCI_0_2_FN_CPU_ON:
> +    case QEMU_PSCI_0_2_FN64_CPU_ON:
> +        mpidr = param[1];
> +        entry = param[2];
> +        context_id = param[3];
> +        ret = arm_set_cpu_on(mpidr, entry, context_id,
> +                             target_el, target_aarch64);
> +        break;
> +    case QEMU_PSCI_0_1_FN_CPU_OFF:
> +    case QEMU_PSCI_0_2_FN_CPU_OFF:
> +        return hvf_psci_cpu_off(arm_cpu);
> +    case QEMU_PSCI_0_1_FN_CPU_SUSPEND:
> +    case QEMU_PSCI_0_2_FN_CPU_SUSPEND:
> +    case QEMU_PSCI_0_2_FN64_CPU_SUSPEND:
> +        /* Affinity levels are not supported in QEMU */
> +        if (param[1] & 0xfffe0000) {
> +            ret = QEMU_PSCI_RET_INVALID_PARAMS;
> +            break;
> +        }
> +        /* Powerdown is not supported, we always go into WFI */
> +        env->xregs[0] = 0;
> +        hvf_wfi(cpu);
> +        break;
> +    case QEMU_PSCI_0_1_FN_MIGRATE:
> +    case QEMU_PSCI_0_2_FN_MIGRATE:
> +        ret = QEMU_PSCI_RET_NOT_SUPPORTED;
> +        break;
> +    default:
> +        return 1;
> +    }
> +
> +    env->xregs[0] = ret;
> +    return 0;
> +}
> +
>  static uint64_t hvf_sysreg_read(CPUState *cpu, uint32_t reg)
>  {
>      ARMCPU *arm_cpu = ARM_CPU(cpu);
> @@ -716,6 +823,8 @@ int hvf_vcpu_exec(CPUState *cpu)
>      }
>
>      if (cpu->halted) {
> +        /* On unhalt, we usually have CPU state changes. Prepare for them. */
> +        cpu_synchronize_state(cpu);
>          return EXCP_HLT;
>      }

This seems odd. If I understand the control flow correctly, this
is neither:
 (a) just before we're about to emulate a PSCI call so we need
the current values of the registers from hvf
 (b) when we've just changed the CPU registers because we made
a PSCI call and we want to push them back to hvf
 (c) when we're about to resume the CPU because it's gone from
halted to not-halted, which might also be a good time to push
register state back to hvf

So what's it for ?

My expectation would be that we would ensure the state is in sync
(ie that hvf has any local changes) before we call hv_cpu_run(),
and that we would go the other way before we access any local
register CPU struct state.

thanks
-- PMM



reply via email to

[Prev in Thread] Current Thread [Next in Thread]