blob: 576c6bd2e766ec98e3845ee2e78929d0519c6437 [file] [log] [blame]
David Brazdil3ad6e542019-09-13 17:17:09 +01001/*
2 * Copyright 2019 The Hafnium Authors.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "hf/arch/vm/power_mgmt.h"
18
19#include "hf/arch/mm.h"
20
21#include "hf/spinlock.h"
22
23#include "hftest.h"
24
25struct cpu_start_state {
26 void (*entry)(uintptr_t arg);
27 uintreg_t arg;
28 struct spinlock lock;
29};
30
31static noreturn void cpu_entry(uintptr_t arg)
32{
33 struct cpu_start_state *s = (struct cpu_start_state *)arg;
34 struct cpu_start_state s_copy;
35
36 /*
37 * Initialize memory and enable caching. Must be the first thing we do.
38 */
39 hftest_mm_vcpu_init();
40
41 /* Make a copy of the cpu_start_state struct. */
42 s_copy = *s;
43
44 /* Inform cpu_start() that the state struct memory can now be freed. */
45 sl_unlock(&s->lock);
46
47 /* Call the given entry function with the given argument. */
48 s_copy.entry(s_copy.arg);
49
50 /* If the entry function returns, turn off the CPU. */
51 arch_cpu_stop();
52}
53
54bool hftest_cpu_start(uintptr_t id, void *stack, size_t stack_size,
55 void (*entry)(uintptr_t arg), uintptr_t arg)
56{
57 struct cpu_start_state s;
58 struct arch_cpu_start_state s_arch;
59
60 /*
61 * Config for arch_cpu_start() which will start a new CPU and
62 * immediately jump to cpu_entry(). This function must guarantee that
63 * the state struct is not be freed until cpu_entry() is called.
64 */
65 s_arch.initial_sp = (uintptr_t)stack + stack_size;
66 s_arch.entry = cpu_entry;
67 s_arch.arg = (uintptr_t)&s;
68
69 /*
70 * Write back the `cpu_start_state` struct because the new CPU will be
71 * started without caching enabled and will need the data early on.
72 */
73 arch_mm_write_back_dcache(&s_arch, sizeof(s_arch));
74
75 if ((s_arch.initial_sp % STACK_ALIGN) != 0) {
76 HFTEST_FAIL(true,
77 "Stack pointer of new vCPU not properly aligned.");
78 }
79
80 /*
81 * Config for cpu_entry(). Its job is to initialize memory and call the
82 * provided entry point with the provided argument.
83 */
84 s.entry = entry;
85 s.arg = arg;
86 sl_init(&s.lock);
87
88 /*
89 * Lock the cpu_start_state struct which will be unlocked once
90 * cpu_entry() does not need its content anymore. This simultaneously
91 * protects the arch_cpu_start_state struct which must not be freed
92 * before cpu_entry() is called.
93 */
94 sl_lock(&s.lock);
95
96 /* Try to start the given CPU. */
97 if (!arch_cpu_start(id, &s_arch)) {
98 return false;
99 }
100
101 /*
102 * Wait until cpu_entry() unlocks the cpu_start_state lock before
103 * freeing stack memory.
104 */
105 sl_lock(&s.lock);
106 return true;
107}