My Kernel v0.1.0
cpu.h
1#ifndef KERNEL_ARCH_I686_UTILS_CPU_OPS_H
2#define KERNEL_ARCH_I686_UTILS_CPU_OPS_H
3
4#include <kernel/types.h>
5
6#include <utils/bits.h>
7#include <utils/compiler.h>
8#include <utils/map.h>
9
10#define CPU_CACHE_ALIGN 64 /* 64B L1 cache lines */
11
12#define X86_FEATURE_WORDS 2 /* Number of CPUID leaves that contain features. */
13
14struct x86_cpuinfo {
15 const char *vendor;
16 u32 features[X86_FEATURE_WORDS];
17};
18
19extern struct x86_cpuinfo cpuinfo;
20
21/*
22 * Register read/write wrappers.
23 */
24
25// Read from a 32-bits register
26#define READ_REGISTER_OPS(_reg) \
27 static ALWAYS_INLINE u32 read_##_reg() \
28 { \
29 u32 res; \
30 ASM("movl %%" #_reg ", %0" : "=r"(res)); \
31 return res; \
32 }
33
34// Write into a 32-bits register
35#define WRITE_REGISTER_OPS(_reg) \
36 static ALWAYS_INLINE void write_##_reg(u32 value) \
37 { \
38 ASM("movl %0, %%" #_reg : : "r"(value)); \
39 }
40
41#define CPU_32BIT_REGISTERS \
42 cr0, cr1, cr2, cr3, cr4, esp, cs, ds, es, fs, gs, ss, eax
43
44MAP(READ_REGISTER_OPS, CPU_32BIT_REGISTERS)
45MAP(WRITE_REGISTER_OPS, CPU_32BIT_REGISTERS)
46
47#undef CPU_32BIT_REGISTERS
48#undef WRITE_REGISTER_OPS
49#undef READ_REGISTER_OPS
50
51/*
52 * CPU control registers.
53 */
54
55#define CR0_PG BIT(31) /* Paging enable */
56#define CR0_CD BIT(30) /* Cache disable */
57#define CR0_NW BIT(29) /* Not write-through */
58
59#define CR4_PAE BIT(5) /* PAE paging enable */
60
61/*
62 * ASM instruction wrappers.
63 */
64
65/* Write a single byte at a given I/O port address. */
66static ALWAYS_INLINE void outb(uint16_t port, uint8_t val)
67{
68 ASM("out %0,%1" : : "a"(val), "Nd"(port) : "memory");
69}
70
71/* Write 2 bytes at a given I/O port address. */
72static ALWAYS_INLINE void outw(uint16_t port, uint16_t val)
73{
74 ASM("out %0,%1" : : "a"(val), "Nd"(port) : "memory");
75}
76
77/* Write 4 bytes at a given I/O port address. */
78static ALWAYS_INLINE void outl(uint16_t port, uint32_t val)
79{
80 ASM("out %0,%1" : : "a"(val), "Nd"(port) : "memory");
81}
82
83/* Read a single byte from a given I/O port address. */
84static ALWAYS_INLINE uint8_t inb(uint16_t port)
85{
86 uint8_t val;
87 ASM("in %1, %0" : "=a"(val) : "Nd"(port) : "memory");
88 return val;
89}
90
91/* Read 2 bytes from a given I/O port address. */
92static ALWAYS_INLINE uint16_t inw(uint16_t port)
93{
94 uint16_t val;
95 ASM("in %1, %0" : "=a"(val) : "Nd"(port) : "memory");
96 return val;
97}
98
99/* Read 4 bytes from a given I/O port address. */
100static ALWAYS_INLINE uint32_t inl(uint16_t port)
101{
102 uint32_t val;
103 ASM("in %1,%0" : "=a"(val) : "Nd"(port) : "memory");
104 return val;
105}
106
107static ALWAYS_INLINE void hlt(void)
108{
109 ASM("hlt");
110}
111
112static ALWAYS_INLINE void insb(uint16_t port, uint8_t *buffer, size_t size)
113{
114 size_t count = size / sizeof(*buffer);
115
116 asm volatile("cld; rep insb"
117 : "+D"(buffer), "+c"(count)
118 : "d"(port)
119 : "memory");
120}
121
122static ALWAYS_INLINE void insw(uint16_t port, uint16_t *buffer, size_t size)
123{
124 size_t count = size / sizeof(*buffer);
125
126 asm volatile("cld; rep insw"
127 : "+D"(buffer), "+c"(count)
128 : "d"(port)
129 : "memory");
130}
131
132static ALWAYS_INLINE void insl(uint16_t port, uint32_t *buffer, size_t size)
133{
134 size_t count = size / sizeof(*buffer);
135
136 asm volatile("cld; rep insl"
137 : "+D"(buffer), "+c"(count)
138 : "d"(port)
139 : "memory");
140}
141
142#include <cpuid.h> /* provided by GCC */
143
144#define cpuid(leaf, eax, ebx, ecx, edx) __get_cpuid(leaf, eax, ebx, ecx, edx)
145
146/*
147 * Define quick helper functions for CPUID calls that only need to access one
148 * of the result registers.
149 */
150#define CPUID_FUNCTION(_reg) \
151 static inline uint32_t cpuid_##_reg(uint32_t leaf) \
152 { \
153 uint32_t eax; \
154 uint32_t ebx; \
155 uint32_t ecx; \
156 uint32_t edx; \
157 \
158 cpuid(leaf, &eax, &ebx, &ecx, &edx); \
159 return _reg; \
160 }
161
162CPUID_FUNCTION(eax)
163CPUID_FUNCTION(ebx)
164CPUID_FUNCTION(ecx)
165CPUID_FUNCTION(edx)
166
167#undef CPUID_FUNCTION
168
169#define CPUID_LEAF_GETVENDOR 0
170#define CPUID_LEAF_GETFEATURES 1
171#define CPUID_LEAF_GETFEATURES_EXT 7
172
173/* Vendor codes used by popular hypervisors. */
174#define signature_QEMU_ebx 0x47435443 // [TCGT]CGTCGTCG
175#define signature_KVM_ebx 0x4D564B20 // [ KVM]KVMKVM
176#define signature_VMWARE_ebx 0x61774D56 // [VMwa]reVMware
177#define signature_VIRTUALBOX_ebx 0x786F4256 // [VBox]VBoxVBox
178#define signature_XEN_ebx 0x566E6558 // [XenV]MMXenVMM
179#define signature_HYPERV_ebx 0x7263694D // [Micr]osoft Hv
180#define signature_PARALLELS_ebx 0x6C727020 // [ prl] hyperv
181#define signature_PARALLELS_ALT_ebx 0x6570726C // [lrpe]pyh vr
182#define signature_BHYVE_ebx 0x76796862 // [bhyv]e bhyve
183#define signature_QNX_ebx 0x20584E51 // [ QNX]QVMBSQG
184
185#define X86_FEATURES(F) \
186 \
187 /* Features in %ecx for leaf 1 */ \
188 F(SSE3, 0, 0), \
189 F(PCLMUL, 0, 1), \
190 F(DTES64, 0, 2), \
191 F(MONITOR, 0, 3), \
192 F(DSCPL, 0, 4), \
193 F(VMX, 0, 5), \
194 F(SMX, 0, 6), \
195 F(EIST, 0, 7), \
196 F(TM2, 0, 8), \
197 F(SSSE3, 0, 9), \
198 F(CNXTID, 0, 10), \
199 F(FMA, 0, 12), \
200 F(CMPXCHG16B, 0, 13), \
201 F(xTPR, 0, 14), \
202 F(PDCM, 0, 15), \
203 F(PCID, 0, 17), \
204 F(DCA, 0, 18), \
205 F(SSE41, 0, 19), \
206 F(SSE42, 0, 20), \
207 F(x2APIC, 0, 21), \
208 F(MOVBE, 0, 22), \
209 F(POPCNT, 0, 23), \
210 F(TSCDeadline, 0, 24), \
211 F(AES, 0, 25), \
212 F(XSAVE, 0, 26), \
213 F(OSXSAVE, 0, 27), \
214 F(AVX, 0, 28), \
215 F(F16C, 0, 29), \
216 F(RDRND, 0, 30), \
217 \
218 /* Features in %edx for leaf 1 */ \
219 F(FPU, 1, 0), \
220 F(VME, 1, 1), \
221 F(DE, 1, 2), \
222 F(PSE, 1, 3), \
223 F(TSC, 1, 4), \
224 F(MSR, 1, 5), \
225 F(PAE, 1, 6), \
226 F(MCE, 1, 7), \
227 F(CMPXCHG8B, 1, 8), \
228 F(APIC, 1, 9), \
229 F(SEP, 1, 11), \
230 F(MTRR, 1, 12), \
231 F(PGE, 1, 13), \
232 F(MCA, 1, 14), \
233 F(CMOV, 1, 15), \
234 F(PAT, 1, 16), \
235 F(PSE36, 1, 17), \
236 F(PSN, 1, 18), \
237 F(CLFSH, 1, 19), \
238 F(DS, 1, 21), \
239 F(ACPI, 1, 22), \
240 F(MMX, 1, 23), \
241 F(FXSAVE, 1, 24), \
242 F(SSE, 1, 25), \
243 F(SSE2, 1, 26), \
244 F(SS, 1, 27), \
245 F(HTT, 1, 28), \
246 F(TM, 1, 29), \
247 F(PBE, 1, 31), \
248
249#define X86_FEATURE_NAME(_feature) X86_FEATURE_##_feature
250#define X86_FEATURE_VAL(_word, _bit) ((_word << X86_FEATURE_WORD_OFF) | (_bit & 0xff))
251#define X86_FEATURE_WORD_OFF 8
252
253enum x86_cpu_feature {
254#define DEFINE_X86_FEATURE(_name, _word, _bit) \
255 X86_FEATURE_NAME(_name) = X86_FEATURE_VAL(_word, _bit)
256X86_FEATURES(DEFINE_X86_FEATURE)
257#undef DEFINE_X86_FEATURE
258};
259
260static inline bool cpu_test_feature(enum x86_cpu_feature feature)
261{
262 int leaf = (feature >> X86_FEATURE_WORD_OFF);
263 int bit = feature & (BIT(X86_FEATURE_WORD_OFF) - 1);
264
265 return BIT_READ(cpuinfo.features[leaf], bit);
266}
267
268#define cpu_has_feature(_feature) cpu_test_feature(X86_FEATURE_NAME(_feature))
269
270enum x86_msr {
271 MSR_PAT = 0x277,
272};
273
274/* Read from specific register */
275static inline uint64_t rdmsr(uint32_t msr)
276{
277 uint32_t eax;
278 uint32_t edx;
279 ASM("rdmsr" : "=a"(eax), "=d"(edx) : "c"(msr));
280 return (((uint64_t)edx) << 32) | eax;
281}
282
283/* Write into model specific register */
284static inline void wrmsr(uint32_t msr, uint64_t val)
285{
286 uint32_t eax = val;
287 uint32_t edx = val >> 32;
288 ASM("wrmsr" : : "a"(eax), "d"(edx), "c"(msr));
289}
290
291#endif /* KERNEL_I686_UTILS_CPU_OPS_H */
#define BIT(_n)
Generate the nth power of 2 (nth bit set)
Definition: bits.h:20
#define BIT_READ(_x, _n)
Read the nth bit.
Definition: bits.h:30
#define MAP(f,...)
Applies the function macro f to each of the remaining parameters.
Definition: map.h:60