1 /*
2  * Copyright 2019 The Hafnium Authors.
3  *
4  * Use of this source code is governed by a BSD-style
5  * license that can be found in the LICENSE file or at
6  * https://opensource.org/licenses/BSD-3-Clause.
7  */
8 
9 #include "hf/arch/vm/power_mgmt.h"
10 
11 #include "hf/arch/mm.h"
12 
13 #include "hf/spinlock.h"
14 
15 #include "test/hftest.h"
16 
17 struct cpu_start_state {
18 	void (*entry)(uintptr_t arg);
19 	uintreg_t arg;
20 	struct spinlock lock;
21 };
22 
cpu_entry(uintptr_t arg)23 static noreturn void cpu_entry(uintptr_t arg)
24 {
25 	/*
26 	 * The function prototype must match the entry function so we permit the
27 	 * int to pointer conversion.
28 	 */
29 	// NOLINTNEXTLINE(performance-no-int-to-ptr)
30 	struct cpu_start_state *s = (struct cpu_start_state *)arg;
31 	struct cpu_start_state s_copy;
32 
33 	/*
34 	 * Initialize memory and enable caching. Must be the first thing we do.
35 	 */
36 	hftest_mm_vcpu_init();
37 
38 	/* Make a copy of the cpu_start_state struct. */
39 	s_copy = *s;
40 
41 	/* Inform cpu_start() that the state struct memory can now be freed. */
42 	sl_unlock(&s->lock);
43 
44 	/* Call the given entry function with the given argument. */
45 	s_copy.entry(s_copy.arg);
46 
47 	/* If the entry function returns, turn off the CPU. */
48 	arch_cpu_stop();
49 }
50 
hftest_cpu_start(uintptr_t id,void * stack,size_t stack_size,void (* entry)(uintptr_t arg),uintptr_t arg)51 bool hftest_cpu_start(uintptr_t id, void *stack, size_t stack_size,
52 		      void (*entry)(uintptr_t arg), uintptr_t arg)
53 {
54 	struct cpu_start_state s;
55 	struct arch_cpu_start_state s_arch;
56 
57 	/*
58 	 * Config for arch_cpu_start() which will start a new CPU and
59 	 * immediately jump to cpu_entry(). This function must guarantee that
60 	 * the state struct is not be freed until cpu_entry() is called.
61 	 */
62 	s_arch.initial_sp = (uintptr_t)stack + stack_size;
63 	s_arch.entry = cpu_entry;
64 	s_arch.arg = (uintptr_t)&s;
65 
66 	/*
67 	 * Flush the `cpu_start_state` struct because the new CPU will be
68 	 * started without caching enabled and will need the data early on.
69 	 * Write back is all that is really needed so flushing will definitely
70 	 * get the job done.
71 	 */
72 	arch_mm_flush_dcache(&s_arch, sizeof(s_arch));
73 
74 	if ((s_arch.initial_sp % STACK_ALIGN) != 0) {
75 		HFTEST_FAIL(true,
76 			    "Stack pointer of new vCPU not properly aligned.");
77 	}
78 
79 	/*
80 	 * Config for cpu_entry(). Its job is to initialize memory and call the
81 	 * provided entry point with the provided argument.
82 	 */
83 	s.entry = entry;
84 	s.arg = arg;
85 	sl_init(&s.lock);
86 
87 	/*
88 	 * Lock the cpu_start_state struct which will be unlocked once
89 	 * cpu_entry() does not need its content anymore. This simultaneously
90 	 * protects the arch_cpu_start_state struct which must not be freed
91 	 * before cpu_entry() is called.
92 	 */
93 	sl_lock(&s.lock);
94 
95 	/* Try to start the given CPU. */
96 	if (!arch_cpu_start(id, &s_arch)) {
97 		return false;
98 	}
99 
100 	/*
101 	 * Wait until cpu_entry() unlocks the cpu_start_state lock before
102 	 * freeing stack memory.
103 	 */
104 	sl_lock(&s.lock);
105 	return true;
106 }
107