blob: 5326ab098697e5e43e6b2dafc44039db3e7bf8c6 [file] [log] [blame]
David Brazdil3ad6e542019-09-13 17:17:09 +01001/*
2 * Copyright 2019 The Hafnium Authors.
3 *
Andrew Walbrane959ec12020-06-17 15:01:09 +01004 * 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.
David Brazdil3ad6e542019-09-13 17:17:09 +01007 */
8
9#include "hf/arch/vm/power_mgmt.h"
10
11#include "hf/arch/mm.h"
12
13#include "hf/spinlock.h"
14
Andrew Walbran1e7c7742019-12-13 17:10:02 +000015#include "test/hftest.h"
David Brazdil3ad6e542019-09-13 17:17:09 +010016
17struct cpu_start_state {
18 void (*entry)(uintptr_t arg);
19 uintreg_t arg;
20 struct spinlock lock;
21};
22
23static noreturn void cpu_entry(uintptr_t arg)
24{
25 struct cpu_start_state *s = (struct cpu_start_state *)arg;
26 struct cpu_start_state s_copy;
27
28 /*
29 * Initialize memory and enable caching. Must be the first thing we do.
30 */
31 hftest_mm_vcpu_init();
32
33 /* Make a copy of the cpu_start_state struct. */
34 s_copy = *s;
35
36 /* Inform cpu_start() that the state struct memory can now be freed. */
37 sl_unlock(&s->lock);
38
39 /* Call the given entry function with the given argument. */
40 s_copy.entry(s_copy.arg);
41
42 /* If the entry function returns, turn off the CPU. */
43 arch_cpu_stop();
44}
45
46bool hftest_cpu_start(uintptr_t id, void *stack, size_t stack_size,
47 void (*entry)(uintptr_t arg), uintptr_t arg)
48{
49 struct cpu_start_state s;
50 struct arch_cpu_start_state s_arch;
51
52 /*
53 * Config for arch_cpu_start() which will start a new CPU and
54 * immediately jump to cpu_entry(). This function must guarantee that
55 * the state struct is not be freed until cpu_entry() is called.
56 */
57 s_arch.initial_sp = (uintptr_t)stack + stack_size;
58 s_arch.entry = cpu_entry;
59 s_arch.arg = (uintptr_t)&s;
60
61 /*
Andrew Scullc059fbe2019-09-12 12:58:40 +010062 * Flush the `cpu_start_state` struct because the new CPU will be
David Brazdil3ad6e542019-09-13 17:17:09 +010063 * started without caching enabled and will need the data early on.
Andrew Scullc059fbe2019-09-12 12:58:40 +010064 * Write back is all that is really needed so flushing will definitely
65 * get the job done.
David Brazdil3ad6e542019-09-13 17:17:09 +010066 */
Andrew Scullc059fbe2019-09-12 12:58:40 +010067 arch_mm_flush_dcache(&s_arch, sizeof(s_arch));
David Brazdil3ad6e542019-09-13 17:17:09 +010068
69 if ((s_arch.initial_sp % STACK_ALIGN) != 0) {
70 HFTEST_FAIL(true,
71 "Stack pointer of new vCPU not properly aligned.");
72 }
73
74 /*
75 * Config for cpu_entry(). Its job is to initialize memory and call the
76 * provided entry point with the provided argument.
77 */
78 s.entry = entry;
79 s.arg = arg;
80 sl_init(&s.lock);
81
82 /*
83 * Lock the cpu_start_state struct which will be unlocked once
84 * cpu_entry() does not need its content anymore. This simultaneously
85 * protects the arch_cpu_start_state struct which must not be freed
86 * before cpu_entry() is called.
87 */
88 sl_lock(&s.lock);
89
90 /* Try to start the given CPU. */
91 if (!arch_cpu_start(id, &s_arch)) {
92 return false;
93 }
94
95 /*
96 * Wait until cpu_entry() unlocks the cpu_start_state lock before
97 * freeing stack memory.
98 */
99 sl_lock(&s.lock);
100 return true;
101}