xref: /kvm-unit-tests/lib/arm/smp.c (revision 6f447945e4285eea5f13f6ff6affab06da8604f8)
168ea0e0bSAndrew Jones /*
268ea0e0bSAndrew Jones  * Secondary cpu support
368ea0e0bSAndrew Jones  *
468ea0e0bSAndrew Jones  * Copyright (C) 2015, Red Hat Inc, Andrew Jones <drjones@redhat.com>
568ea0e0bSAndrew Jones  *
668ea0e0bSAndrew Jones  * This work is licensed under the terms of the GNU LGPL, version 2.
768ea0e0bSAndrew Jones  */
868ea0e0bSAndrew Jones #include <libcflat.h>
968ea0e0bSAndrew Jones #include <asm/thread_info.h>
10ff942175SAndrew Jones #include <asm/spinlock.h>
1168ea0e0bSAndrew Jones #include <asm/cpumask.h>
121742c67aSAndrew Jones #include <asm/barrier.h>
1368ea0e0bSAndrew Jones #include <asm/mmu.h>
1468ea0e0bSAndrew Jones #include <asm/psci.h>
1568ea0e0bSAndrew Jones #include <asm/smp.h>
1668ea0e0bSAndrew Jones 
1768ea0e0bSAndrew Jones cpumask_t cpu_present_mask;
1868ea0e0bSAndrew Jones cpumask_t cpu_online_mask;
199246de4cSAndrew Jones cpumask_t cpu_idle_mask;
20ff942175SAndrew Jones 
21ff942175SAndrew Jones struct secondary_data {
22ff942175SAndrew Jones 	void *stack;            /* must be first member of struct */
23ff942175SAndrew Jones 	secondary_entry_fn entry;
24ff942175SAndrew Jones };
2568ea0e0bSAndrew Jones struct secondary_data secondary_data;
26ff942175SAndrew Jones static struct spinlock lock;
2768ea0e0bSAndrew Jones 
2868ea0e0bSAndrew Jones secondary_entry_fn secondary_cinit(void)
2968ea0e0bSAndrew Jones {
3068ea0e0bSAndrew Jones 	struct thread_info *ti = current_thread_info();
3168ea0e0bSAndrew Jones 	secondary_entry_fn entry;
3268ea0e0bSAndrew Jones 
3368ea0e0bSAndrew Jones 	thread_info_init(ti, 0);
341742c67aSAndrew Jones 	mmu_mark_enabled(ti->cpu);
3568ea0e0bSAndrew Jones 
3668ea0e0bSAndrew Jones 	/*
3768ea0e0bSAndrew Jones 	 * Save secondary_data.entry locally to avoid opening a race
3868ea0e0bSAndrew Jones 	 * window between marking ourselves online and calling it.
3968ea0e0bSAndrew Jones 	 */
4068ea0e0bSAndrew Jones 	entry = secondary_data.entry;
4168ea0e0bSAndrew Jones 	set_cpu_online(ti->cpu, true);
4268ea0e0bSAndrew Jones 	sev();
4368ea0e0bSAndrew Jones 
4468ea0e0bSAndrew Jones 	/*
4568ea0e0bSAndrew Jones 	 * Return to the assembly stub, allowing entry to be called
4668ea0e0bSAndrew Jones 	 * from there with an empty stack.
4768ea0e0bSAndrew Jones 	 */
4868ea0e0bSAndrew Jones 	return entry;
4968ea0e0bSAndrew Jones }
5068ea0e0bSAndrew Jones 
51e713fd55SAndrew Jones static void __smp_boot_secondary(int cpu, secondary_entry_fn entry)
5268ea0e0bSAndrew Jones {
5318ab6cadSAndrew Jones 	int ret;
5468ea0e0bSAndrew Jones 
55632f935cSAndrew Jones 	secondary_data.stack = thread_stack_alloc();
5668ea0e0bSAndrew Jones 	secondary_data.entry = entry;
571742c67aSAndrew Jones 	mmu_mark_disabled(cpu);
5818ab6cadSAndrew Jones 	ret = cpu_psci_cpu_boot(cpu);
5918ab6cadSAndrew Jones 	assert(ret == 0);
6068ea0e0bSAndrew Jones 
6168ea0e0bSAndrew Jones 	while (!cpu_online(cpu))
6268ea0e0bSAndrew Jones 		wfe();
63e713fd55SAndrew Jones }
64ff942175SAndrew Jones 
65e713fd55SAndrew Jones void smp_boot_secondary(int cpu, secondary_entry_fn entry)
66e713fd55SAndrew Jones {
67e713fd55SAndrew Jones 	spin_lock(&lock);
68e713fd55SAndrew Jones 	assert_msg(!cpu_online(cpu), "CPU%d already boot once", cpu);
69e713fd55SAndrew Jones 	__smp_boot_secondary(cpu, entry);
70ff942175SAndrew Jones 	spin_unlock(&lock);
7168ea0e0bSAndrew Jones }
72543ce33cSAndrew Jones 
73e713fd55SAndrew Jones typedef void (*on_cpu_func)(void *);
74e713fd55SAndrew Jones struct on_cpu_info {
75e713fd55SAndrew Jones 	on_cpu_func func;
76e713fd55SAndrew Jones 	void *data;
77e713fd55SAndrew Jones };
78e713fd55SAndrew Jones static struct on_cpu_info on_cpu_info[NR_CPUS];
79e713fd55SAndrew Jones 
809246de4cSAndrew Jones void do_idle(void)
81543ce33cSAndrew Jones {
829246de4cSAndrew Jones 	int cpu = smp_processor_id();
83543ce33cSAndrew Jones 
849246de4cSAndrew Jones 	set_cpu_idle(cpu, true);
859246de4cSAndrew Jones 	sev();
869246de4cSAndrew Jones 
879246de4cSAndrew Jones 	for (;;) {
889246de4cSAndrew Jones 		while (cpu_idle(cpu))
899246de4cSAndrew Jones 			wfe();
90e713fd55SAndrew Jones 		smp_rmb();
91e713fd55SAndrew Jones 		on_cpu_info[cpu].func(on_cpu_info[cpu].data);
92e713fd55SAndrew Jones 		on_cpu_info[cpu].func = NULL;
93e713fd55SAndrew Jones 		smp_wmb();
949246de4cSAndrew Jones 		set_cpu_idle(cpu, true);
959246de4cSAndrew Jones 		sev();
969246de4cSAndrew Jones 	}
97543ce33cSAndrew Jones }
98543ce33cSAndrew Jones 
99e713fd55SAndrew Jones void on_cpu_async(int cpu, void (*func)(void *data), void *data)
100e713fd55SAndrew Jones {
101e713fd55SAndrew Jones 	if (cpu == smp_processor_id()) {
102e713fd55SAndrew Jones 		func(data);
103e713fd55SAndrew Jones 		return;
104e713fd55SAndrew Jones 	}
105e713fd55SAndrew Jones 
106e713fd55SAndrew Jones 	spin_lock(&lock);
107e713fd55SAndrew Jones 	if (!cpu_online(cpu))
108e713fd55SAndrew Jones 		__smp_boot_secondary(cpu, do_idle);
109e713fd55SAndrew Jones 	spin_unlock(&lock);
110e713fd55SAndrew Jones 
111e713fd55SAndrew Jones 	for (;;) {
112e713fd55SAndrew Jones 		while (!cpu_idle(cpu))
113e713fd55SAndrew Jones 			wfe();
114e713fd55SAndrew Jones 		spin_lock(&lock);
115e713fd55SAndrew Jones 		if ((volatile void *)on_cpu_info[cpu].func == NULL)
116e713fd55SAndrew Jones 			break;
117e713fd55SAndrew Jones 		spin_unlock(&lock);
118e713fd55SAndrew Jones 	}
119e713fd55SAndrew Jones 	on_cpu_info[cpu].func = func;
120e713fd55SAndrew Jones 	on_cpu_info[cpu].data = data;
121e713fd55SAndrew Jones 	spin_unlock(&lock);
122e713fd55SAndrew Jones 	set_cpu_idle(cpu, false);
123e713fd55SAndrew Jones 	sev();
124e713fd55SAndrew Jones }
125e713fd55SAndrew Jones 
126e713fd55SAndrew Jones void on_cpu(int cpu, void (*func)(void *data), void *data)
127e713fd55SAndrew Jones {
128e713fd55SAndrew Jones 	on_cpu_async(cpu, func, data);
129e713fd55SAndrew Jones 
130e713fd55SAndrew Jones 	while (!cpu_idle(cpu))
131e713fd55SAndrew Jones 		wfe();
132e713fd55SAndrew Jones }
133e713fd55SAndrew Jones 
134*6f447945SAndrew Jones void on_cpus(void (*func)(void))
135543ce33cSAndrew Jones {
136543ce33cSAndrew Jones 	int cpu;
137543ce33cSAndrew Jones 
138543ce33cSAndrew Jones 	for_each_present_cpu(cpu) {
139543ce33cSAndrew Jones 		if (cpu == 0)
140543ce33cSAndrew Jones 			continue;
141*6f447945SAndrew Jones 		on_cpu_async(cpu, (on_cpu_func)func, NULL);
142543ce33cSAndrew Jones 	}
143543ce33cSAndrew Jones 	func();
144543ce33cSAndrew Jones 
1459246de4cSAndrew Jones 	set_cpu_idle(0, true);
1469246de4cSAndrew Jones 	while (!cpumask_full(&cpu_idle_mask))
1479246de4cSAndrew Jones 		wfe();
1489246de4cSAndrew Jones 	set_cpu_idle(0, false);
149543ce33cSAndrew Jones }
150