| /* |
| * Copyright 2021 The Hafnium Authors. |
| * |
| * Use of this source code is governed by a BSD-style |
| * license that can be found in the LICENSE file or at |
| * https://opensource.org/licenses/BSD-3-Clause. |
| */ |
| |
| #include "hf/arch/plat/psci.h" |
| |
| #include "hf/api.h" |
| #include "hf/check.h" |
| #include "hf/cpu.h" |
| #include "hf/ffa/notifications.h" |
| #include "hf/vm.h" |
| |
| #include "psci.h" |
| |
| void cpu_entry(struct cpu *c); |
| |
| /** |
| * Returns zero in context of the SPMC as it does not rely |
| * on the EL3 PSCI framework. |
| */ |
| uint32_t plat_psci_version_get(void) |
| { |
| return 0; |
| } |
| |
| /** |
| * Initialize the platform power managment module in context of |
| * running the SPMC. |
| */ |
| void plat_psci_init(void) |
| { |
| struct ffa_value res; |
| |
| /* |
| * DEN0077A FF-A v1.1 Beta0 section 18.3.2.1.1 |
| * Register the SPMC secondary cold boot entry point at the secure |
| * physical FF-A instance (to the SPMD). |
| */ |
| res = smc_ffa_call_ext( |
| (struct ffa_value){.func = FFA_SECONDARY_EP_REGISTER_64, |
| .arg1 = (uintreg_t)&cpu_entry}); |
| |
| if (res.func != FFA_SUCCESS_32 && res.func != FFA_SUCCESS_64) { |
| panic("FFA_SECONDARY_EP_REGISTER_64 failed"); |
| } |
| } |
| |
| void plat_psci_cpu_suspend(uint32_t power_state) |
| { |
| (void)power_state; |
| } |
| |
| /** Switch to the normal world vCPU pinned on this physical CPU now. */ |
| static struct vcpu *plat_psci_switch_to_other_world(struct cpu *c) |
| { |
| struct vcpu_locked other_world_vcpu_locked; |
| struct vm *other_world_vm = vm_find(HF_OTHER_WORLD_ID); |
| struct vcpu *other_world_vcpu; |
| |
| CHECK(other_world_vm != NULL); |
| |
| other_world_vcpu = vm_get_vcpu(other_world_vm, cpu_index(c)); |
| |
| CHECK(other_world_vcpu != NULL); |
| |
| other_world_vcpu_locked = vcpu_lock(other_world_vcpu); |
| |
| /* |
| * Return FFA_MSG_WAIT_32 to indicate to SPMD that SPMC |
| * has successfully finished initialization on this |
| * CPU. |
| */ |
| arch_regs_set_retval(&other_world_vcpu->regs, |
| (struct ffa_value){.func = FFA_MSG_WAIT_32}); |
| |
| other_world_vcpu->state = VCPU_STATE_WAITING; |
| vcpu_unlock(&other_world_vcpu_locked); |
| |
| return other_world_vcpu; |
| } |
| |
| /** |
| * Check if there is at least one SP whose execution context needs to be |
| * bootstrapped on this physical CPU. |
| */ |
| static struct vm *plat_psci_get_boot_vm(struct cpu *c) |
| { |
| struct vm *boot_vm; |
| |
| if (cpu_index(c) == PRIMARY_CPU_IDX) { |
| boot_vm = vm_get_boot_vm(); |
| |
| /* |
| * On the primary CPU, at least one SP will exist whose |
| * execution context shall be bootstrapped. |
| */ |
| CHECK(boot_vm != NULL); |
| } else { |
| boot_vm = vm_get_boot_vm_secondary_core(); |
| |
| /* |
| * It is possible that no SP might exist that needs its |
| * execution context to be bootstrapped on secondary CPU. This |
| * can happen if all the SPs in the system are UP partitions and |
| * hence, have no vCPUs pinned to secondary CPUs. |
| */ |
| if (boot_vm != NULL) { |
| assert(boot_vm->vcpu_count > 1); |
| } |
| } |
| |
| return boot_vm; |
| } |
| |
| struct vcpu *plat_psci_cpu_resume(struct cpu *c) |
| { |
| struct vcpu_locked vcpu_locked; |
| struct vm *boot_vm; |
| struct vcpu *boot_vcpu; |
| |
| cpu_on(c); |
| |
| arch_cpu_init(c); |
| |
| /* Initialize SRI for running core. */ |
| ffa_notifications_sri_init(c); |
| |
| boot_vm = plat_psci_get_boot_vm(c); |
| |
| if (boot_vm == NULL) { |
| return plat_psci_switch_to_other_world(c); |
| } |
| |
| /* Obtain the vCPU for the boot SP on this CPU. */ |
| boot_vcpu = vm_get_vcpu(boot_vm, cpu_index(c)); |
| |
| /* Lock the vCPU to update its fields. */ |
| vcpu_locked = vcpu_lock(boot_vcpu); |
| |
| /* Pin the vCPU to this CPU. */ |
| boot_vcpu->cpu = c; |
| |
| vcpu_secondary_reset_and_start(vcpu_locked, boot_vcpu->vm->secondary_ep, |
| 0ULL); |
| |
| /* Set the vCPU's state to STARTING. */ |
| boot_vcpu->state = VCPU_STATE_STARTING; |
| boot_vcpu->regs_available = false; |
| |
| /* vCPU restarts in runtime model for SP initialization. */ |
| boot_vcpu->rt_model = RTM_SP_INIT; |
| |
| /* Set the designated GP register with the core linear id. */ |
| vcpu_set_phys_core_idx(boot_vcpu); |
| |
| if (cpu_index(c) == PRIMARY_CPU_IDX) { |
| /* |
| * Boot information is passed by the SPMC to the SP's execution |
| * context only on the primary CPU. |
| */ |
| vcpu_set_boot_info_gp_reg(boot_vcpu); |
| } |
| |
| vcpu_unlock(&vcpu_locked); |
| |
| return boot_vcpu; |
| } |