1 /* 2 * Copyright (c) 2025, Arm Limited. All rights reserved. 3 * 4 * SPDX-License-Identifier: BSD-3-Clause 5 */ 6 7 #include <string.h> 8 9 #include <common/debug.h> 10 #include <lib/psci/psci_lib.h> 11 #include <lib/spinlock.h> 12 #include <lib/utils_def.h> 13 #include <plat/common/platform.h> 14 #include <services/lfa_holding_pen.h> 15 16 #include <platform_def.h> 17 18 static spinlock_t holding_lock; 19 static spinlock_t activation_lock; 20 static uint32_t activation_count; 21 static enum lfa_retc activation_status; 22 23 /** 24 * lfa_holding_start - Called by each active CPU to coordinate live activation. 25 * 26 * Note that only CPUs that are active at the time of activation will 27 * participate in CPU rendezvous. 28 * 29 * This function is invoked by each CPU participating in the LFA Activate 30 * process. It increments the shared activation count under `activation_lock` 31 * to track how many CPUs have entered the activation phase. 32 * 33 * The first CPU to enter acquires the `holding_lock`, which ensures 34 * serialization during the wait and activation phases. This lock is 35 * released only after the last CPU completes the activation. 36 * 37 * The function returns `true` only for the last CPU to enter, allowing it 38 * to proceed with performing the live firmware activation. All other CPUs 39 * receive `false` and will wait in `lfa_holding_wait()` until activation 40 * is complete. 41 * 42 * @return `true` for the last CPU, `false` for all others. 43 */ 44 bool lfa_holding_start(void) 45 { 46 bool status; 47 unsigned int no_of_cpus; 48 49 spin_lock(&activation_lock); 50 51 if (activation_count == 0U) { 52 /* First CPU locks holding lock */ 53 spin_lock(&holding_lock); 54 } 55 56 activation_count += 1U; 57 58 no_of_cpus = psci_num_cpus_running_on_safe(plat_my_core_pos()); 59 status = (activation_count == no_of_cpus); 60 if (!status) { 61 VERBOSE("Hold, %d CPU left\n", 62 PLATFORM_CORE_COUNT - activation_count); 63 } 64 65 spin_unlock(&activation_lock); 66 67 return status; 68 } 69 70 /** 71 * lfa_holding_wait - CPUs wait until activation is completed by the last CPU. 72 * 73 * All CPUs are serialized using `holding_lock`, which is initially acquired 74 * by the first CPU in `lfa_holding_start()` and only released by the last 75 * CPU through `lfa_holding_release()`. This ensures that no two CPUs enter 76 * the critical section at the same time during the wait phase. Once the 77 * last CPU completes activation, each CPU decrements the activation count 78 * and returns the final activation status, which was set by the last CPU 79 * to complete the activation process. 80 * 81 * @return Activation status set by the last CPU. 82 */ 83 enum lfa_retc lfa_holding_wait(void) 84 { 85 spin_lock(&holding_lock); 86 activation_count -= 1U; 87 spin_unlock(&holding_lock); 88 return activation_status; 89 } 90 91 /** 92 * lfa_holding_release - Called by the last CPU to complete activation. 93 * 94 * This function is used by the last participating CPU after it completes 95 * live firmware activation. It updates the shared activation status and 96 * resets the activation count. Finally, it releases the `holding_lock` to 97 * allow other CPUs that were waiting in `lfa_holding_wait()` to proceed. 98 * 99 * @param status Activation status to be shared with other CPUs. 100 */ 101 void lfa_holding_release(enum lfa_retc status) 102 { 103 activation_count = 0U; 104 activation_status = status; 105 spin_unlock(&holding_lock); 106 } 107