Fix syscall_async's signature
[akaros.git] / kern / arch / x86 / topology.c
1 /* Copyright (c) 2015 The Regents of the University of California
2  * Kevin Klues <klueska@cs.berkeley.edu>
3  * See LICENSE for details. */
4
5 #include <stdio.h>
6 #include <stdint.h>
7 #include <stddef.h>
8 #include <kmalloc.h>
9 #include <string.h>
10 #include <acpi.h>
11 #include <arch/arch.h>
12 #include <arch/apic.h>
13 #include <arch/topology.h>
14
15 struct topology_info cpu_topology_info;
16 int *os_coreid_lookup;
17
18 #define num_cpus            (cpu_topology_info.num_cpus)
19 #define num_sockets         (cpu_topology_info.num_sockets)
20 #define num_numa            (cpu_topology_info.num_numa)
21 #define cores_per_numa      (cpu_topology_info.cores_per_numa)
22 #define cores_per_socket    (cpu_topology_info.cores_per_socket)
23 #define cores_per_cpu       (cpu_topology_info.cores_per_cpu)
24 #define cpus_per_socket     (cpu_topology_info.cpus_per_socket)
25 #define cpus_per_numa       (cpu_topology_info.cpus_per_numa)
26 #define sockets_per_numa    (cpu_topology_info.sockets_per_numa)
27 #define max_apic_id         (cpu_topology_info.max_apic_id)
28 #define core_list           (cpu_topology_info.core_list)
29
30 /* Adjust the ids from any given node type to start at 0 and increase from
31  * there. We use the id_offset in the core_list to index the proper field. */
32 static void adjust_ids(int id_offset)
33 {
34         int new_id = 0, old_id = -1;
35         for (int i = 0; i < num_cores; i++) {
36                 for (int j = 0; j < num_cores; j++) {
37                         int *id_field = ((void*)&core_list[j] + id_offset);
38                         if (*id_field >= new_id) {
39                                 if (old_id == -1)
40                                         old_id = *id_field;
41                                 if (old_id == *id_field)
42                                         *id_field = new_id;
43                         }
44                 }
45                 old_id=-1;
46                 new_id++;
47         }
48 }
49
50 /* Set the actual socket id from the raw socket id extracted from cpuid.  This
51  * algorithm is adapted from the algorithm given at
52  * http://wiki.osdev.org/Detecting_CPU_Topology_(80x86) */
53 static void set_socket_ids(void)
54 {
55         int socket_id, raw_socket_id;
56         for (int numa_id = 0; numa_id < num_numa; numa_id++) {
57                 socket_id = 0;
58                 for (int i = 0; i < num_cores; i++) {
59                         if (core_list[i].numa_id == numa_id) {
60                                 if (core_list[i].socket_id == -1) {
61                                         core_list[i].socket_id = socket_id;
62                                         raw_socket_id = core_list[i].raw_socket_id;
63                                         for (int j = i; j < num_cores; j++) {
64                                                 if (core_list[j].numa_id == numa_id) {
65                                                         if (core_list[j].raw_socket_id == raw_socket_id) {
66                                                                 core_list[j].socket_id = socket_id;
67                                                         }
68                                                 }
69                                         }
70                                 }
71                                 socket_id++;
72                         }
73                 }
74         }
75 }
76
77 /* Loop through our Srat table to find a matching numa domain for the given
78  * apid_id. */
79 static int find_numa_domain(int apic_id)
80 {
81         struct Srat *temp = srat;
82         while (temp) {
83                 if (temp->type == SRlapic) {
84                         if (temp->lapic.apic == apic_id)
85                                 return temp->lapic.dom;
86                 }
87                 temp = temp->next;
88         }
89         return -1;
90 }
91
92 /* Figure out the maximum number of cores we actually have and set it in our
93  * cpu_topology_info struct. */
94 static void set_num_cores(void)
95 {
96         struct Apicst *temp = apics->st;
97         while (temp) {
98                 if (temp->type == ASlapic)
99                         num_cores++;
100                 temp = temp->next;
101         }
102 }
103
104 /* Figure out the maximum number of numa domains we actually have and set it in
105  * our cpu_topology_info struct. */
106 static void set_num_numa(void)
107 {
108         num_numa = -1;
109         struct Srat *temp = srat;
110         while (temp) {
111                 if (temp->type == SRlapic)
112                         if (temp->lapic.dom > num_numa)
113                                 num_numa++;
114                 temp = temp->next;
115         }
116         num_numa++;
117 }
118
119 /* Figure out what the max apic_id we will ever have is and set it in our
120  * cpu_topology_info struct. */
121 static void set_max_apic_id(void)
122 {
123         struct Apicst *temp = apics->st;
124         while (temp) {
125                 if (temp->type == ASlapic) {
126                         if (temp->lapic.id > max_apic_id)
127                                 max_apic_id = temp->lapic.id;
128                 }
129                 temp = temp->next;
130         }
131 }
132
133 static void init_os_coreid_lookup(void)
134 {
135         /* Allocate (max_apic_id+1) entries in our os_coreid_lookup table.
136          * There may be holes in this table because of the way apic_ids work, but
137          * a little wasted space is OK for a constant time lookup of apic_id ->
138          * logical core id (from the OS's perspective). Memset the array to -1 to
139          * to represent invalid entries (which it's very possible we might have if
140          * the apic_id space has holes in it).  */
141         os_coreid_lookup = kmalloc((max_apic_id + 1) * sizeof(int), 0);
142         memset(os_coreid_lookup, -1, (max_apic_id + 1) * sizeof(int));
143
144         /* Loop through and set all valid entries to 0 to start with (making them
145          * temporarily valid, but not yet set to the correct value). This step is
146          * necessary because there is no ordering to the linked list we are
147          * pulling these ids from. After this, loop back through and set the
148          * mapping appropriately. */
149         struct Apicst *temp = apics->st;
150         while (temp) {
151                 if (temp->type == ASlapic)
152                         os_coreid_lookup[temp->lapic.id] = 0;
153                 temp = temp->next;
154         }
155         int os_coreid = 0;
156         for (int i = 0; i <= max_apic_id; i++)
157                 if (os_coreid_lookup[i] == 0)
158                         os_coreid_lookup[i] = os_coreid++;
159 }
160
161 static void init_core_list(uint32_t core_bits, uint32_t cpu_bits)
162 {
163         /* Assuming num_cores and max_apic_id have been set, we can allocate our
164          * core_list to the proper size. Initialize all entries to 0s to being
165          * with. */
166         core_list = kzmalloc(num_cores * sizeof(struct core_info), 0);
167
168         /* Loop through all possible apic_ids and fill in the core_list array with
169          * *relative* topology info. We will change this relative info to absolute
170          * info in a future step. As part of this step, we update our
171          * os_coreid_lookup array to contain the proper value. */
172         int os_coreid = 0;
173         int max_cpus = (1 << cpu_bits);
174         int max_cores_per_cpu = (1 << core_bits);
175         int max_logical_cores = (1 << (core_bits + cpu_bits));
176         int raw_socket_id = 0, cpu_id = 0, core_id = 0;
177         for (int apic_id = 0; apic_id <= max_apic_id; apic_id++) {
178                 if (os_coreid_lookup[apic_id] != -1) {
179                         raw_socket_id = apic_id & ~(max_logical_cores - 1);
180                         cpu_id = (apic_id >> core_bits) & (max_cpus - 1);
181                         core_id = apic_id & (max_cores_per_cpu - 1);
182
183                         core_list[os_coreid].numa_id = find_numa_domain(apic_id);
184                         core_list[os_coreid].raw_socket_id = raw_socket_id;
185                         core_list[os_coreid].socket_id = -1;
186                         core_list[os_coreid].cpu_id = cpu_id;
187                         core_list[os_coreid].core_id = core_id;
188                         core_list[os_coreid].apic_id = apic_id;
189                         os_coreid++;
190                 }
191         }
192
193         /* In general, the various id's set in the previous step are all unique in
194          * terms of representing the topology (i.e. all cores under the same socket
195          * have the same socket_id set), but these id's are not necessarily
196          * contiguous, and are only relative to the level of the hierarchy they
197          * exist at (e.g.  cpu_id 4 may exist under *both* socket_id 0 and
198          * socket_id 1). In this step, we squash these id's down so they are
199          * contiguous. In a following step, we will make them all absolute instead
200          * of relative. */
201         adjust_ids(offsetof(struct core_info, numa_id));
202         adjust_ids(offsetof(struct core_info, raw_socket_id));
203         adjust_ids(offsetof(struct core_info, cpu_id));
204         adjust_ids(offsetof(struct core_info, core_id));
205
206         /* We haven't yet set the socket id of each core yet. So far, all we've
207          * extracted is a "raw" socket id from the top bits in our apic id, but we
208          * need to condense these down into something workable for a socket id, per
209          * numa domain. OSDev has an algorithm for doing so
210          * (http://wiki.osdev.org/Detecting_CPU_Topology_%2880x86%29). We adapt it
211          * for our setup. */
212         set_socket_ids();
213 }
214
215 static void init_core_list_flat(void)
216 {
217         /* Assuming num_cores and max_apic_id have been set, we can allocate our
218          * core_list to the proper size. Initialize all entries to 0s to being
219          * with. */
220         core_list = kzmalloc(num_cores * sizeof(struct core_info), 0);
221
222         /* Loop through all possible apic_ids and fill in the core_list array with
223          * flat topology info. */
224         int os_coreid = 0;
225         for (int apic_id = 0; apic_id <= max_apic_id; apic_id++) {
226                 if (os_coreid_lookup[apic_id] != -1) {
227                         core_list[os_coreid].numa_id = 0;
228                         core_list[os_coreid].raw_socket_id = 0;
229                         core_list[os_coreid].socket_id = 0;
230                         core_list[os_coreid].cpu_id = 0;
231                         core_list[os_coreid].core_id = os_coreid;
232                         core_list[os_coreid].apic_id = apic_id;
233                         os_coreid++;
234                 }
235         }
236 }
237
238 static void set_remaining_topology_info(void)
239 {
240         /* Assuming we have our core_list set up with relative topology info, loop
241          * through our core_list and calculate the other statistics that we hold
242          * in our cpu_topology_info struct. */
243         int last_numa = -1, last_socket = -1, last_cpu = -1, last_core = -1;
244         for (int i = 0; i < num_cores; i++) {
245                 if (core_list[i].socket_id > last_socket) {
246                         last_socket = core_list[i].socket_id;
247                         sockets_per_numa++;
248                 }
249                 if (core_list[i].cpu_id > last_cpu) {
250                         last_cpu = core_list[i].cpu_id;
251                         cpus_per_socket++;
252                 }
253                 if (core_list[i].core_id > last_core) {
254                         last_core = core_list[i].core_id;
255                         cores_per_cpu++;
256                 }
257         }
258         cores_per_socket = cpus_per_socket * cores_per_cpu;
259         cores_per_numa = sockets_per_numa * cores_per_socket;
260         cpus_per_numa = sockets_per_numa * cpus_per_socket;
261         num_sockets = sockets_per_numa * num_numa;
262         num_cpus = cpus_per_socket * num_sockets;
263 }
264
265 static void update_core_list_with_absolute_ids(void)
266 {
267         /* Fix up our core_list to have absolute id's at every level. */
268         for (int i = 0; i < num_cores; i++) {
269                 struct core_info *c = &core_list[i];
270                 c->socket_id = num_sockets/num_numa * c->numa_id + c->socket_id;
271                 c->cpu_id = num_cpus/num_sockets * c->socket_id + c->cpu_id;
272                 c->core_id = num_cores/num_cpus * c->cpu_id + c->core_id;
273         }
274 }
275
276 static void build_topology(uint32_t core_bits, uint32_t cpu_bits)
277 {
278         set_num_cores();
279         set_num_numa();
280         set_max_apic_id();
281         init_os_coreid_lookup();
282         init_core_list(core_bits, cpu_bits);
283         set_remaining_topology_info();
284         update_core_list_with_absolute_ids();
285 }
286
287 static void build_flat_topology(void)
288 {
289         set_num_cores();
290         num_numa = 1;
291         set_max_apic_id();
292         init_os_coreid_lookup();
293         init_core_list_flat();
294         set_remaining_topology_info();
295 }
296
297 void topology_init(void)
298 {
299         uint32_t eax, ebx, ecx, edx;
300         int smt_leaf, core_leaf;
301         uint32_t core_bits = 0, cpu_bits = 0;
302
303         eax = 0x0000000b;
304         ecx = 1;
305         cpuid(eax, ecx, &eax, &ebx, &ecx, &edx);
306         core_leaf = (ecx >> 8) & 0x00000002;
307         if (core_leaf == 2) {
308                 cpu_bits = eax;
309                 eax = 0x0000000b;
310                 ecx = 0;
311                 cpuid(eax, ecx, &eax, &ebx, &ecx, &edx);
312                 smt_leaf = (ecx >> 8) & 0x00000001;
313                 if (smt_leaf == 1) {
314                         core_bits = eax;
315                         cpu_bits = cpu_bits - core_bits;
316                 }
317         }
318         if (cpu_bits)
319                 build_topology(core_bits, cpu_bits);
320         else
321                 build_flat_topology();
322 }
323
324 void print_cpu_topology()
325 {
326         printk("num_numa: %d, num_sockets: %d, num_cpus: %d, num_cores: %d\n",
327                num_numa, num_sockets, num_cpus, num_cores);
328         for (int i = 0; i < num_cores; i++) {
329                 printk("OScoreid: %3d, HWcoreid: %3d, RawSocketid: %3d, "
330                        "Numa Domain: %3d, Socket: %3d, Cpu: %3d, Core: %3d\n",
331                        i,
332                        core_list[i].apic_id,
333                        core_list[i].numa_id,
334                        core_list[i].raw_socket_id,
335                        core_list[i].socket_id,
336                        core_list[i].cpu_id,
337                        core_list[i].core_id);
338         }
339 }