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 */
lfa_holding_start(void)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 */
lfa_holding_wait(void)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 */
lfa_holding_release(enum lfa_retc status)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