xref: /kvm-unit-tests/lib/x86/smp.c (revision c604fa931a1cb70c3649ac1b7223178fc79eab6a)
1 
2 #include <libcflat.h>
3 #include "processor.h"
4 #include "atomic.h"
5 #include "smp.h"
6 #include "apic.h"
7 #include "fwcfg.h"
8 #include "desc.h"
9 
10 #define IPI_VECTOR 0x20
11 
12 typedef void (*ipi_function_type)(void *data);
13 
14 static struct spinlock ipi_lock;
15 static volatile ipi_function_type ipi_function;
16 static void *volatile ipi_data;
17 static volatile int ipi_done;
18 static volatile bool ipi_wait;
19 static int _cpu_count;
20 static atomic_t active_cpus;
21 
22 static __attribute__((used)) void ipi(void)
23 {
24 	void (*function)(void *data) = ipi_function;
25 	void *data = ipi_data;
26 	bool wait = ipi_wait;
27 
28 	if (!wait) {
29 		ipi_done = 1;
30 		apic_write(APIC_EOI, 0);
31 	}
32 	function(data);
33 	atomic_dec(&active_cpus);
34 	if (wait) {
35 		ipi_done = 1;
36 		apic_write(APIC_EOI, 0);
37 	}
38 }
39 
40 asm (
41 	 "ipi_entry: \n"
42 	 "   call ipi \n"
43 #ifndef __x86_64__
44 	 "   iret"
45 #else
46 	 "   iretq"
47 #endif
48 	 );
49 
50 int cpu_count(void)
51 {
52 	return _cpu_count;
53 }
54 
55 int smp_id(void)
56 {
57 	return this_cpu_read_smp_id();
58 }
59 
60 static void setup_smp_id(void *data)
61 {
62 	this_cpu_write_smp_id(apic_id());
63 }
64 
65 static void __on_cpu(int cpu, void (*function)(void *data), void *data, int wait)
66 {
67 	const u32 ipi_icr = APIC_INT_ASSERT | APIC_DEST_PHYSICAL | APIC_DM_FIXED | IPI_VECTOR;
68 	unsigned int target = id_map[cpu];
69 
70 	spin_lock(&ipi_lock);
71 	if (target == smp_id()) {
72 		function(data);
73 	} else {
74 		atomic_inc(&active_cpus);
75 		ipi_done = 0;
76 		ipi_function = function;
77 		ipi_data = data;
78 		ipi_wait = wait;
79 		apic_icr_write(ipi_icr, target);
80 		while (!ipi_done)
81 			;
82 	}
83 	spin_unlock(&ipi_lock);
84 }
85 
86 void on_cpu(int cpu, void (*function)(void *data), void *data)
87 {
88 	__on_cpu(cpu, function, data, 1);
89 }
90 
91 void on_cpu_async(int cpu, void (*function)(void *data), void *data)
92 {
93 	__on_cpu(cpu, function, data, 0);
94 }
95 
96 void on_cpus(void (*function)(void *data), void *data)
97 {
98 	int cpu;
99 
100 	for (cpu = cpu_count() - 1; cpu >= 0; --cpu)
101 		on_cpu_async(cpu, function, data);
102 
103 	while (cpus_active() > 1)
104 		pause();
105 }
106 
107 int cpus_active(void)
108 {
109 	return atomic_read(&active_cpus);
110 }
111 
112 void smp_init(void)
113 {
114 	int i;
115 	void ipi_entry(void);
116 
117 	_cpu_count = fwcfg_get_nb_cpus();
118 
119 	setup_idt();
120 	init_apic_map();
121 	set_idt_entry(IPI_VECTOR, ipi_entry, 0);
122 
123 	setup_smp_id(0);
124 	for (i = 1; i < cpu_count(); ++i)
125 		on_cpu(i, setup_smp_id, 0);
126 
127 	atomic_inc(&active_cpus);
128 }
129 
130 static void do_reset_apic(void *data)
131 {
132 	reset_apic();
133 }
134 
135 void smp_reset_apic(void)
136 {
137 	int i;
138 
139 	reset_apic();
140 	for (i = 1; i < cpu_count(); ++i)
141 		on_cpu(i, do_reset_apic, 0);
142 
143 	atomic_inc(&active_cpus);
144 }
145