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