1/* @title: Per-CPU structure */
2#pragma once
3#include <sch/irql.h>
4#include <smp/topology.h>
5#include <stdatomic.h>
6#include <stdbool.h>
7#include <stddef.h>
8#include <stdint.h>
9#include <thread/dpc.h>
10#include <types/types.h>
11
12#define CPU_FEAT_SSE2 (1ULL << 0)
13#define CPU_FEAT_AVX (1ULL << 1)
14#define CPU_FEAT_AVX2 (1ULL << 2)
15#define CPU_FEAT_AVX512F (1ULL << 3)
16
17enum cpu_class {
18 CPU_CLASS_UNKNOWN,
19 CPU_CLASS_PERFORMANCE,
20 CPU_CLASS_EFFICIENCY,
21};
22
23enum {
24 UARCH_UNKNOWN,
25 UARCH_GOLDEN_COVE,
26 UARCH_GRACEMONT,
27 UARCH_SKYLAKE,
28};
29
30struct cpu_capability {
31 enum cpu_class class;
32
33 uint32_t uarch_id; /* e.g. golden cove, gracemont */
34
35 uint32_t issue_width;
36 uint32_t retire_width;
37
38 cpu_perf_t perf_score; /* Relative to everyone else on a 0-255
39 * scale, how performant are we? The
40 * higher this number is, the more "performant"
41 * this CPU currently is, and the more likely
42 * the scheduler will decide to migrate a
43 * thread that needs such perf scores onto here. */
44
45 uint32_t energy_score; /* lower is better */
46
47 uint64_t feature_bits; /* ISA features, vector width, etc */
48};
49
50/* Let's put commonly accessed fields up here
51 * to make the cache a bit happier */
52struct core {
53 struct core *self;
54 cpu_id_t id;
55 struct thread *current_thread;
56 struct cpu_capability cap;
57
58 size_t domain_cpu_id; /* what CPU in the domain? */
59
60 /* array [domain_levels_enabled] -> domain reference */
61 struct scheduler_domain *domains[TOPOLOGY_LEVEL_MAX];
62
63 /* index within each domain's groups */
64 int32_t group_index[TOPOLOGY_LEVEL_MAX];
65
66 atomic_bool executing_dpcs;
67 atomic_bool idle;
68
69 /* This scratch buffer is stack allocated, and is set upon
70 * IRQ entry, allowing the top half of the IRQ to modify it
71 *
72 * For exception_sync_cb, it is passed into the callback as a parameter */
73 uint8_t *irq_stack_scratch_buf;
74 bool in_interrupt;
75 enum irql current_irql;
76
77 /* Remains valid in the top half, once the bottom half is
78 * reached, this becomes IRQL_NONE */
79 enum irql irq_entered_irql; /* What IRQL were we at before
80 * entering an ISR (if !in_interrupt,
81 * this should be IRQL_NONE */
82
83 enum dpc_event dpc_event;
84
85 atomic_bool needs_resched;
86 atomic_bool in_resched; /* in scheduler_yield() */
87 uint32_t preempt_disable_depth;
88
89 struct domain *domain;
90 struct domain_arena *domain_arena;
91 size_t rr_current_domain;
92
93 struct tss *tss;
94
95 uint32_t lapic_freq;
96
97 struct topology_node *topo_node;
98 struct topology_cache_info llc;
99
100 numa_node_t numa_node;
101 uint32_t package_id;
102 uint32_t smt_mask;
103 uint32_t smt_id;
104 uint32_t core_id;
105
106 uint64_t tsc_hz;
107 uint64_t last_us;
108 uint64_t last_tsc; /* For time.c */
109
110 _Atomic uint64_t pt_seen_epoch;
111 bool reclaiming_page_tables;
112};
113
114static inline uint64_t smp_core_id() {
115 uint64_t id;
116 asm volatile("movq %%gs:%c1, %0"
117 : "=r"(id)
118 : "i"(offsetof(struct core, id)));
119 return id;
120}
121
122static inline struct core *smp_core(void) {
123 uintptr_t core;
124 asm volatile("movq %%gs:%c1, %0"
125 : "=r"(core)
126 : "i"(offsetof(struct core, self)));
127 return (struct core *) core;
128}
129
130#define for_each_cpu_struct(__iter) \
131 for (size_t __id = 0; \
132 ((__iter = global.cores[__id]), __id < global.core_count); __id++)
133
134#define for_each_cpu_id(__id) for (__id = 0; __id < global.core_count; __id++)
135