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