99#include <asm/kvm_mmu.h>
1010#include <kvm/arm_hypercalls.h>
1111#include <linux/arm-smccc.h>
12+ #include <linux/kvm_host.h>
1213#include <linux/psci.h>
1314#include <kvm/arm_psci.h>
1415#include <uapi/linux/psci.h>
1516
1617#include <nvhe/trap_handler.h>
1718
19+ void kvm_hyp_cpu_entry (unsigned long r0 );
20+
21+ void __noreturn __host_enter (struct kvm_cpu_context * host_ctxt );
22+
1823/* Config options set by the host. */
1924__ro_after_init u32 kvm_host_psci_version ;
2025__ro_after_init struct psci_0_1_function_ids kvm_host_psci_0_1_function_ids ;
2126__ro_after_init s64 hyp_physvirt_offset ;
2227
2328#define __hyp_pa (x ) ((phys_addr_t)((x)) + hyp_physvirt_offset)
2429
30+ #define INVALID_CPU_ID UINT_MAX
31+
32+ struct psci_boot_args {
33+ atomic_t lock ;
34+ unsigned long pc ;
35+ unsigned long r0 ;
36+ };
37+
38+ #define PSCI_BOOT_ARGS_UNLOCKED 0
39+ #define PSCI_BOOT_ARGS_LOCKED 1
40+
41+ #define PSCI_BOOT_ARGS_INIT \
42+ ((struct psci_boot_args){ \
43+ .lock = ATOMIC_INIT(PSCI_BOOT_ARGS_UNLOCKED), \
44+ })
45+
46+ static DEFINE_PER_CPU (struct psci_boot_args , cpu_on_args ) = PSCI_BOOT_ARGS_INIT ;
47+
2548static u64 get_psci_func_id (struct kvm_cpu_context * host_ctxt )
2649{
2750 DECLARE_REG (u64 , func_id , host_ctxt , 0 );
@@ -75,11 +98,101 @@ static __noreturn unsigned long psci_forward_noreturn(struct kvm_cpu_context *ho
7598 hyp_panic (); /* unreachable */
7699}
77100
101+ static unsigned int find_cpu_id (u64 mpidr )
102+ {
103+ unsigned int i ;
104+
105+ /* Reject invalid MPIDRs */
106+ if (mpidr & ~MPIDR_HWID_BITMASK )
107+ return INVALID_CPU_ID ;
108+
109+ for (i = 0 ; i < NR_CPUS ; i ++ ) {
110+ if (cpu_logical_map (i ) == mpidr )
111+ return i ;
112+ }
113+
114+ return INVALID_CPU_ID ;
115+ }
116+
117+ static __always_inline bool try_acquire_boot_args (struct psci_boot_args * args )
118+ {
119+ return atomic_cmpxchg_acquire (& args -> lock ,
120+ PSCI_BOOT_ARGS_UNLOCKED ,
121+ PSCI_BOOT_ARGS_LOCKED ) ==
122+ PSCI_BOOT_ARGS_UNLOCKED ;
123+ }
124+
125+ static __always_inline void release_boot_args (struct psci_boot_args * args )
126+ {
127+ atomic_set_release (& args -> lock , PSCI_BOOT_ARGS_UNLOCKED );
128+ }
129+
130+ static int psci_cpu_on (u64 func_id , struct kvm_cpu_context * host_ctxt )
131+ {
132+ DECLARE_REG (u64 , mpidr , host_ctxt , 1 );
133+ DECLARE_REG (unsigned long, pc , host_ctxt , 2 );
134+ DECLARE_REG (unsigned long, r0 , host_ctxt , 3 );
135+
136+ unsigned int cpu_id ;
137+ struct psci_boot_args * boot_args ;
138+ struct kvm_nvhe_init_params * init_params ;
139+ int ret ;
140+
141+ /*
142+ * Find the logical CPU ID for the given MPIDR. The search set is
143+ * the set of CPUs that were online at the point of KVM initialization.
144+ * Booting other CPUs is rejected because their cpufeatures were not
145+ * checked against the finalized capabilities. This could be relaxed
146+ * by doing the feature checks in hyp.
147+ */
148+ cpu_id = find_cpu_id (mpidr );
149+ if (cpu_id == INVALID_CPU_ID )
150+ return PSCI_RET_INVALID_PARAMS ;
151+
152+ boot_args = per_cpu_ptr (hyp_symbol_addr (cpu_on_args ), cpu_id );
153+ init_params = per_cpu_ptr (hyp_symbol_addr (kvm_init_params ), cpu_id );
154+
155+ /* Check if the target CPU is already being booted. */
156+ if (!try_acquire_boot_args (boot_args ))
157+ return PSCI_RET_ALREADY_ON ;
158+
159+ boot_args -> pc = pc ;
160+ boot_args -> r0 = r0 ;
161+ wmb ();
162+
163+ ret = psci_call (func_id , mpidr ,
164+ __hyp_pa (hyp_symbol_addr (kvm_hyp_cpu_entry )),
165+ __hyp_pa (init_params ));
166+
167+ /* If successful, the lock will be released by the target CPU. */
168+ if (ret != PSCI_RET_SUCCESS )
169+ release_boot_args (boot_args );
170+
171+ return ret ;
172+ }
173+
174+ asmlinkage void __noreturn kvm_host_psci_cpu_entry (bool is_cpu_on )
175+ {
176+ struct psci_boot_args * boot_args ;
177+ struct kvm_cpu_context * host_ctxt ;
178+
179+ host_ctxt = & this_cpu_ptr (hyp_symbol_addr (kvm_host_data ))-> host_ctxt ;
180+ boot_args = this_cpu_ptr (hyp_symbol_addr (cpu_on_args ));
181+
182+ cpu_reg (host_ctxt , 0 ) = boot_args -> r0 ;
183+ write_sysreg_el2 (boot_args -> pc , SYS_ELR );
184+ release_boot_args (boot_args );
185+
186+ __host_enter (host_ctxt );
187+ }
188+
78189static unsigned long psci_0_1_handler (u64 func_id , struct kvm_cpu_context * host_ctxt )
79190{
80191 if ((func_id == kvm_host_psci_0_1_function_ids .cpu_off ) ||
81192 (func_id == kvm_host_psci_0_1_function_ids .migrate ))
82193 return psci_forward (host_ctxt );
194+ else if (func_id == kvm_host_psci_0_1_function_ids .cpu_on )
195+ return psci_cpu_on (func_id , host_ctxt );
83196 else
84197 return PSCI_RET_NOT_SUPPORTED ;
85198}
@@ -98,6 +211,8 @@ static unsigned long psci_0_2_handler(u64 func_id, struct kvm_cpu_context *host_
98211 case PSCI_0_2_FN_SYSTEM_RESET :
99212 psci_forward_noreturn (host_ctxt );
100213 unreachable ();
214+ case PSCI_0_2_FN64_CPU_ON :
215+ return psci_cpu_on (func_id , host_ctxt );
101216 default :
102217 return PSCI_RET_NOT_SUPPORTED ;
103218 }
0 commit comments