PatchworkOS  da8a090
A non-POSIX operating system.
Loading...
Searching...
No Matches
cpu.h
Go to the documentation of this file.
1#pragma once
2
3#include <kernel/config.h>
5#include <kernel/cpu/ipi.h>
6#include <kernel/cpu/tss.h>
7#include <kernel/defs.h>
10#include <kernel/mem/vmm.h>
11#include <kernel/sched/sched.h>
12#include <kernel/sched/timer.h>
13#include <kernel/sched/wait.h>
14
15#include <kernel/utils/map.h>
16#include <stdatomic.h>
17#include <stdint.h>
18#include <sys/bitmap.h>
19#include <sys/list.h>
20
21/**
22 * @brief CPU
23 * @defgroup kernel_cpu CPU
24 * @ingroup kernel
25 *
26 * CPU structures and functions.
27 *
28 * ## Events
29 *
30 * Each CPU can generate events which can be handled by registering event handlers
31 * using `cpu_handler_register()`.
32 *
33 * As an example, the `CPU_ONLINE` event allows other subsystems to perform per-CPU initialization on each CPU no matter
34 * when they are initialized or even if IPIs are available yet.
35 *
36 * This is what makes it possible for SMP to be in a module, at the cost of the system being perhaps slightly
37 * unintuitive and edge case heavy.
38 *
39 * For more details see `cpu_handler_register()`, `cpu_handler_unregister()`, and `cpu_handlers_check()`.
40 *
41 * ## Per-CPU Data
42 *
43 * @todo Implement per-CPU data. Use separate linker section?
44 *
45 * @{
46 */
47
48/**
49 * @brief Maximum number of CPUs supported.
50 */
51#define CPU_MAX (UINT8_MAX + 1)
52
53/**
54 * @brief ID of the bootstrap CPU.
55 */
56#define CPU_ID_BOOTSTRAP 0
57
58/**
59 * @brief Invalid CPU ID.
60 */
61#define CPU_ID_INVALID UINT16_MAX
62
63/**
64 * @brief Type used to identify a CPU.
65 */
67
68/**
69 * @brief CPU stack canary value.
70 *
71 * Placed at the bottom of CPU stacks, we then check in the interrupt handler if any of the stacks have overflowed by
72 * checking if its canary has been modified.
73 */
74#define CPU_STACK_CANARY 0x1234567890ABCDEFULL
75
76/**
77 * @brief CPU event types.
78 * @enum cpu_event_type_t
79 */
85
86/**
87 * @brief CPU event structure.
88 */
89typedef struct
90{
93
94/**
95 * @brief Maximum number of CPU event handlers that can be registered.
96 *
97 * We need to statically allocate the event handler array such that handlers can be registered before memory allocation
98 * has been initialized.
99 */
100#define CPU_MAX_EVENT_HANDLERS 32
101
102/**
103 * @brief CPU event function type.
104 */
105typedef void (*cpu_func_t)(cpu_t* cpu, const cpu_event_t* event);
106
107typedef struct
108{
110 BITMAP_DEFINE(initializedCpus, CPU_MAX);
112
113/**
114 * @brief CPU structure.
115 * @struct cpu_t
116 *
117 * We allocate the stack buffers inside the `cpu_t` structure to avoid memory allocation during early boot.
118 *
119 * Must be stored aligned to a page boundary.
120 */
146
147/**
148 * @brief Array of pointers to cpu_t structures for each CPU, indexed by CPU ID.
149 *
150 * We make this global since its accessed very frequently, so its a slight optimization.
151 */
152extern cpu_t* _cpus[CPU_MAX];
153
154/**
155 * @brief The number of CPUs currently identified.
156 *
157 * Use `cpu_amount()` over this variable.
158 */
159extern uint16_t _cpuAmount;
160
161/**
162 * @brief Only initialize the parts of the CPU structure needed for early boot.
163 *
164 * The only reason we need this is to split the initialization of the bootstrap CPU to avoid circular dependencies
165 * during early boot and since we cant use memory allocation yet.
166 *
167 * @param cpu The CPU structure to initialize.
168 */
169void cpu_init_early(cpu_t* cpu);
170
171/**
172 * @brief Initializes the CPU represented by the `cpu_t` structure.
173 *
174 * Must be called on the CPU that will be represented by the `cpu` structure, after setting the CPU ID MSR using
175 * `cpu_init_early()`.
176 *
177 * @param cpu The CPU structure to initialize.
178 */
179void cpu_init(cpu_t* cpu);
180
181/**
182 * @brief Registers a CPU event handler for all CPUs.
183 *
184 * The registered handler will be immediately invoked with a `CPU_ONLINE` event on the current CPU, then invoked on all
185 * others when they call `cpu_handlers_check()` and on any new cpus when they are initialized.
186 *
187 * @param func The event function to register a handler for.
188 * @return On success, `0`. On failure, `ERR` and `errno` is set to:
189 * - `EINVAL`: Invalid parameters.
190 * - `EBUSY`: Too many event handlers registered.
191 * - `EEXIST`: A handler with the same function is already registered.
192 */
194
195/**
196 * @brief Unregisters a previously registered CPU event handler.
197 *
198 * Will be a no-op if the handler was not registered.
199 *
200 * @param func The event function of the handler to unregister, or `NULL` for no-op.
201 */
203
204/**
205 * @brief Checks if any handlers have been registered since the last check, and invokes them if so.
206 *
207 * @param cpu The CPU to check, must be the current CPU.
208 */
209void cpu_handlers_check(cpu_t* cpu);
210
211/**
212 * @brief Checks for CPU stack overflows.
213 *
214 * Checks the canary values at the bottom of each CPU stack and if its been modified panics.
215 *
216 * @param cpu The CPU to check.
217 */
219
220/**
221 * @brief Halts all other CPUs.
222 *
223 * @return On success, `0`. On failure, `ERR` and `errno` is set.
224 */
226
227/**
228 * @brief Gets the number of identified CPUs.
229 *
230 * Use this over `_cpuAmount`.
231 *
232 * @return The number of identified CPUs.
233 */
234static inline uint16_t cpu_amount(void)
235{
236 return _cpuAmount;
237}
238
239/**
240 * @brief Gets a CPU structure by its ID.
241 *
242 * @param id The ID of the CPU to get.
243 * @return A pointer to the CPU structure, or `NULL` if no CPU with the given ID exists.
244 */
245static inline cpu_t* cpu_get_by_id(cpuid_t id)
246{
247 if (id >= _cpuAmount)
248 {
249 return NULL;
250 }
251 return _cpus[id];
252}
253
254/**
255 * @brief Gets the current CPU structure.
256 *
257 * Disables interrupts to prevent migration to another CPU.
258 *
259 * Should be followed be a call to `cpu_put()` to re-enable interrupts.
260 *
261 * @return A pointer to the current CPU structure.
262 */
263static inline cpu_t* cpu_get(void)
264{
267 cpu_t* cpu = _cpus[id];
268 assert(cpu != NULL && cpu->id == id);
269 return cpu;
270}
271
272/**
273 * @brief Releases the current CPU structure.
274 *
275 * Re-enables interrupts.
276 */
277static inline void cpu_put(void)
278{
280}
281
282/**
283 * @brief Gets the current CPU structure without disabling interrupts.
284 *
285 * @warning This function does not disable interrupts, it should thus only be used when interrupts are already disabled.
286 *
287 * @return A pointer to the current CPU structure.
288 */
289static inline cpu_t* cpu_get_unsafe(void)
290{
292
294 cpu_t* cpu = _cpus[id];
295 assert(cpu != NULL && cpu->id == id);
296 return cpu;
297}
298
299/**
300 * @brief Gets the current CPU ID.
301 *
302 * @warning This function does not disable interrupts, it should thus only be used when interrupts are already disabled.
303 *
304 * @return The current CPU ID.
305 */
306static inline cpuid_t cpu_get_id_unsafe(void)
307{
309
310 return (cpuid_t)msr_read(MSR_CPU_ID);
311}
312
313/**
314 * @brief Gets the next CPU in the CPU array.
315 *
316 * Wraps around to the first CPU if the current CPU is the last one.
317 *
318 * @param current The current CPU.
319 * @return A pointer to the next CPU.
320 */
321static inline cpu_t* cpu_get_next(cpu_t* current)
322{
323 cpuid_t nextId = current->id + 1;
324 if (nextId >= _cpuAmount)
325 {
326 nextId = 0;
327 }
328 return _cpus[nextId];
329}
330
331/**
332 * @brief Macro to iterate over all CPUs.
333 *
334 * The main reason for using this macro is to avoid changes the the internal implementation of how CPUs are stored
335 * affecting other parts of the code.
336 *
337 * @param cpu Loop variable, a pointer to the current `cpu_t`.
338 */
339#define CPU_FOR_EACH(cpu) \
340 for (cpuid_t _cpuId = 0; _cpuId < _cpuAmount; _cpuId++) \
341 for (cpu_t* cpu = _cpus[_cpuId]; cpu != NULL; cpu = NULL)
342
343/** @} */
#define assert(expression)
Definition assert.h:29
static char * id
Definition dwm.c:20
void interrupt_disable(void)
Disable interrupts and increment the disableDepth.
Definition interrupt.c:23
void interrupt_enable(void)
Decrement the CLI depth and enable interrupts if depth reaches zero and interrupts were previously en...
Definition interrupt.c:35
static cpu_t * cpu_get_next(cpu_t *current)
Gets the next CPU in the CPU array.
Definition cpu.h:321
uint64_t cpu_halt_others(void)
Halts all other CPUs.
Definition cpu.c:225
void cpu_stacks_overflow_check(cpu_t *cpu)
Checks for CPU stack overflows.
Definition cpu.c:193
uint64_t cpu_handler_register(cpu_func_t func)
Registers a CPU event handler for all CPUs.
Definition cpu.c:100
cpu_event_type_t
CPU event types.
Definition cpu.h:81
static cpu_t * cpu_get_unsafe(void)
Gets the current CPU structure without disabling interrupts.
Definition cpu.h:289
static cpu_t * cpu_get(void)
Gets the current CPU structure.
Definition cpu.h:263
void(* cpu_func_t)(cpu_t *cpu, const cpu_event_t *event)
CPU event function type.
Definition cpu.h:105
void cpu_init(cpu_t *cpu)
Initializes the CPU represented by the cpu_t structure.
Definition cpu.c:78
static void cpu_put(void)
Releases the current CPU structure.
Definition cpu.h:277
#define CPU_MAX
Maximum number of CPUs supported.
Definition cpu.h:51
cpu_t * _cpus[CPU_MAX]
Array of pointers to cpu_t structures for each CPU, indexed by CPU ID.
Definition cpu.c:23
static cpuid_t cpu_get_id_unsafe(void)
Gets the current CPU ID.
Definition cpu.h:306
static uint16_t cpu_amount(void)
Gets the number of identified CPUs.
Definition cpu.h:234
void cpu_handlers_check(cpu_t *cpu)
Checks if any handlers have been registered since the last check, and invokes them if so.
Definition cpu.c:172
static cpu_t * cpu_get_by_id(cpuid_t id)
Gets a CPU structure by its ID.
Definition cpu.h:245
void cpu_init_early(cpu_t *cpu)
Only initialize the parts of the CPU structure needed for early boot.
Definition cpu.c:30
void cpu_handler_unregister(cpu_func_t func)
Unregisters a previously registered CPU event handler.
Definition cpu.c:150
uint16_t _cpuAmount
The number of CPUs currently identified.
Definition cpu.c:24
uint16_t cpuid_t
Type used to identify a CPU.
Definition cpu.h:66
@ CPU_ONLINE
Definition cpu.h:82
@ CPU_OFFLINE
Definition cpu.h:83
#define CONFIG_INTERRUPT_STACK_PAGES
Interrupt stack configuration.
Definition config.h:14
#define PAGE_SIZE
The size of a memory page in bytes.
Definition proc.h:102
#define NULL
Pointer error value.
Definition NULL.h:23
#define MSR_CPU_ID
Definition regs.h:13
#define RFLAGS_INTERRUPT_ENABLE
Definition regs.h:32
static uint64_t msr_read(uint32_t msr)
Definition regs.h:63
static uint64_t rflags_read()
Definition regs.h:78
__UINT64_TYPE__ uint64_t
Definition stdint.h:17
__UINT8_TYPE__ uint8_t
Definition stdint.h:11
__UINT16_TYPE__ uint16_t
Definition stdint.h:13
CPU event structure.
Definition cpu.h:90
cpu_event_type_t type
Definition cpu.h:91
BITMAP_DEFINE(initializedCpus, CPU_MAX)
cpu_func_t func
Definition cpu.h:109
CPU structure.
Definition cpu.h:122
cpuid_t id
Definition cpu.h:123
rand_cpu_ctx_t rand
Definition cpu.h:135
interrupt_ctx_t interrupt
Definition cpu.h:130
timer_cpu_ctx_t timer
Definition cpu.h:132
atomic_bool needHandlersCheck
Definition cpu.h:127
vmm_cpu_ctx_t vmm
Definition cpu.h:129
uint8_t interruptStackBuffer[CONFIG_INTERRUPT_STACK_PAGES *PAGE_SIZE] ALIGNED(PAGE_SIZE)
stack_pointer_t exceptionStack
Definition cpu.h:137
tss_t tss
Definition cpu.h:128
uint8_t nmiStackBuffer[CONFIG_INTERRUPT_STACK_PAGES *PAGE_SIZE] ALIGNED(PAGE_SIZE)
uint8_t doubleFaultStackBuffer[CONFIG_INTERRUPT_STACK_PAGES *PAGE_SIZE] ALIGNED(PAGE_SIZE)
uint8_t exceptionStackBuffer[CONFIG_INTERRUPT_STACK_PAGES *PAGE_SIZE] ALIGNED(PAGE_SIZE)
wait_t wait
Definition cpu.h:133
perf_cpu_ctx_t perf
Definition cpu.h:131
stack_pointer_t interruptStack
Definition cpu.h:140
sched_t sched
Definition cpu.h:134
ipi_cpu_ctx_t ipi
Definition cpu.h:136
stack_pointer_t nmiStack
Definition cpu.h:139
stack_pointer_t doubleFaultStack
Definition cpu.h:138
Per-CPU Interrupt Context.
Definition interrupt.h:186
Per-CPU IPI context.
Definition ipi.h:92
Per-CPU performance context.
Definition perf.h:49
CPU random number generator context.
Definition rand.h:23
Per-CPU scheduler.
Definition sched.h:393
Structure to define a stack in memory.
Per-CPU system time context.
Definition timer.h:46
Task State Segment structure.
Definition tss.h:94
Per-CPU VMM context.
Definition vmm.h:109
Represents one instance of the waiting subsystem for a CPU.
Definition wait.h:208