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