1 /*
2 * Copyright (c) 2024 Travis Geiselbrecht
3 *
4 * Use of this source code is governed by a MIT-style
5 * license that can be found in the LICENSE file or at
6 * https://opensource.org/licenses/MIT
7 */
8 #pragma once
9
10 #include <sys/types.h>
11 #include <arch/x86.h>
12
13 // per cpu pointer pointed to by gs:
14 typedef struct x86_percpu {
15 // pointer back to ourselves so we can get a raw pointer via segment:0
16 struct x86_percpu *self;
17
18 uint cpu_num;
19 uint32_t apic_id;
20
21 struct thread *current_thread;
22
23 // per cpu bootstrap stack
24 uint8_t bootstrap_stack[PAGE_SIZE] __ALIGNED(sizeof(uintptr_t) * 2);
25
26 // XXX add more stuff:
27 // per cpu TSS
28 // per cpu doublefault/nmi stacks
29 } x86_percpu_t;
30
31 #define X86_PERCPU_FIELD_OFFSET(field) offsetof(x86_percpu_t, field)
32
33 // called extremely early on the boot cpu and each secondary cpu to set
34 // up the percpu struct and segment descriptors pointing to it
35 void x86_configure_percpu_early(uint cpu_num, uint apic_id);
36
37 // C entry point for secondary cpus
38 __NO_RETURN void x86_secondary_entry(uint cpu_num);
39
40 // allocate and initialize secondary cpu percpu structs
41 status_t x86_allocate_percpu_array(uint num_cpus);
42
43 // get the percpu struct for the current cpu
x86_get_percpu(void)44 static inline x86_percpu_t *x86_get_percpu(void) {
45 x86_percpu_t *percpu;
46 __asm__ volatile("mov %%gs:0, %0" : "=r" (percpu));
47 return percpu;
48 }
49
50 // get the percpu struct for a specific cpu
51 x86_percpu_t *x86_get_percpu_for_cpu(uint cpu_num);
52
53 #if 0
54 #define X86_PERCPU_GET(field) (_Generic(((x86_get_percpu())->field), \
55 uint32_t: x86_read_gs_offset32, \
56 uint64_t: x86_read_gs_offset64, \
57 struct thread*: x86_read_gs_offset_ptr) \
58 (X86_PERCPU_FIELD_OFFSET(field)))
59
60 #define X86_PERCPU_SET(field, value) (_Generic(((x86_get_percpu())->field), \
61 uint32_t: x86_write_gs_offset32, \
62 uint64_t: x86_write_gs_offset64, \
63 struct thread*: x86_write_gs_offset_ptr) \
64 (X86_PERCPU_FIELD_OFFSET(field), value))
65 #endif
66
67 // get the current cpu number
x86_get_cpu_num(void)68 static inline uint x86_get_cpu_num(void) {
69 return x86_read_gs_offset32(X86_PERCPU_FIELD_OFFSET(cpu_num));
70 }
71
72 // get the current apic id
x86_get_apic_id(void)73 static inline uint32_t x86_get_apic_id(void) {
74 return x86_read_gs_offset32(X86_PERCPU_FIELD_OFFSET(apic_id));
75 }
76
77 // read it from hardware directly
78 uint32_t x86_get_apic_id_from_hardware(void);
79
80 // get/set the current thread
81 struct thread;
82
x86_get_current_thread(void)83 static inline struct thread *x86_get_current_thread(void) {
84 return (struct thread *)x86_read_gs_offset_ptr(X86_PERCPU_FIELD_OFFSET(current_thread));
85 }
86
x86_set_current_thread(struct thread * t)87 static inline void x86_set_current_thread(struct thread *t) {
88 x86_write_gs_offset_ptr(X86_PERCPU_FIELD_OFFSET(current_thread), t);
89 }
90