:: limine / common / sys / cpu.h 12.6 KB raw

1
#ifndef SYS__CPU_H__
2
#define SYS__CPU_H__
3
4
#include <stdint.h>
5
#include <stddef.h>
6
#include <stdbool.h>
7
8
#if defined(__x86_64__) || defined(__i386__)
9
10
static inline bool cpuid(uint32_t leaf, uint32_t subleaf,
11
          uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) {
12
    uint32_t cpuid_max;
13
    asm volatile ("cpuid"
14
                  : "=a" (cpuid_max)
15
                  : "a" (leaf & 0x80000000)
16
                  : "ebx", "ecx", "edx");
17
    if (leaf > cpuid_max)
18
        return false;
19
    asm volatile ("cpuid"
20
                  : "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx)
21
                  : "a" (leaf), "c" (subleaf));
22
    return true;
23
}
24
25
static inline void outb(uint16_t port, uint8_t value) {
26
    asm volatile ("outb %%al, %1"  : : "a" (value), "Nd" (port) : "memory");
27
}
28
29
static inline void outw(uint16_t port, uint16_t value) {
30
    asm volatile ("outw %%ax, %1"  : : "a" (value), "Nd" (port) : "memory");
31
}
32
33
static inline void outd(uint16_t port, uint32_t value) {
34
    asm volatile ("outl %%eax, %1" : : "a" (value), "Nd" (port) : "memory");
35
}
36
37
static inline uint8_t inb(uint16_t port) {
38
    uint8_t value;
39
    asm volatile ("inb %1, %%al"  : "=a" (value) : "Nd" (port) : "memory");
40
    return value;
41
}
42
43
static inline uint16_t inw(uint16_t port) {
44
    uint16_t value;
45
    asm volatile ("inw %1, %%ax"  : "=a" (value) : "Nd" (port) : "memory");
46
    return value;
47
}
48
49
static inline uint32_t ind(uint16_t port) {
50
    uint32_t value;
51
    asm volatile ("inl %1, %%eax" : "=a" (value) : "Nd" (port) : "memory");
52
    return value;
53
}
54
55
static inline void mmoutb(uintptr_t addr, uint8_t value) {
56
    asm volatile (
57
        "movb %1, (%0)"
58
        :
59
        : "r" (addr), "ir" (value)
60
        : "memory"
61
    );
62
}
63
64
static inline void mmoutw(uintptr_t addr, uint16_t value) {
65
    asm volatile (
66
        "movw %1, (%0)"
67
        :
68
        : "r" (addr), "ir" (value)
69
        : "memory"
70
    );
71
}
72
73
static inline void mmoutd(uintptr_t addr, uint32_t value) {
74
    asm volatile (
75
        "movl %1, (%0)"
76
        :
77
        : "r" (addr), "ir" (value)
78
        : "memory"
79
    );
80
}
81
82
#if defined (__x86_64__)
83
static inline void mmoutq(uintptr_t addr, uint64_t value) {
84
    asm volatile (
85
        "movq %1, (%0)"
86
        :
87
        : "r" (addr), "r" (value)
88
        : "memory"
89
    );
90
}
91
#endif
92
93
static inline uint8_t mminb(uintptr_t addr) {
94
    uint8_t ret;
95
    asm volatile (
96
        "movb (%1), %0"
97
        : "=r" (ret)
98
        : "r"  (addr)
99
        : "memory"
100
    );
101
    return ret;
102
}
103
104
static inline uint16_t mminw(uintptr_t addr) {
105
    uint16_t ret;
106
    asm volatile (
107
        "movw (%1), %0"
108
        : "=r" (ret)
109
        : "r"  (addr)
110
        : "memory"
111
    );
112
    return ret;
113
}
114
115
static inline uint32_t mmind(uintptr_t addr) {
116
    uint32_t ret;
117
    asm volatile (
118
        "movl (%1), %0"
119
        : "=r" (ret)
120
        : "r"  (addr)
121
        : "memory"
122
    );
123
    return ret;
124
}
125
126
#if defined (__x86_64__)
127
static inline uint64_t mminq(uintptr_t addr) {
128
    uint64_t ret;
129
    asm volatile (
130
        "movq (%1), %0"
131
        : "=r" (ret)
132
        : "r"  (addr)
133
        : "memory"
134
    );
135
    return ret;
136
}
137
#endif
138
139
static inline uint64_t rdmsr(uint32_t msr) {
140
    uint32_t edx, eax;
141
    asm volatile ("rdmsr"
142
                  : "=a" (eax), "=d" (edx)
143
                  : "c" (msr)
144
                  : "memory");
145
    return ((uint64_t)edx << 32) | eax;
146
}
147
148
static inline void wrmsr(uint32_t msr, uint64_t value) {
149
    uint32_t edx = value >> 32;
150
    uint32_t eax = (uint32_t)value;
151
    asm volatile ("wrmsr"
152
                  :
153
                  : "a" (eax), "d" (edx), "c" (msr)
154
                  : "memory");
155
}
156
157
static inline bool disable_interrupts(void) {
158
    uintptr_t flags;
159
    asm volatile (
160
        "pushf\n\t"
161
        "pop %0\n\t"
162
        "cli\n\t"
163
        : "=r" (flags)
164
        :
165
        : "memory"
166
    );
167
    return !!(flags & ((uintptr_t)1 << 9));
168
}
169
170
static inline bool enable_interrupts(void) {
171
    uintptr_t flags;
172
    asm volatile (
173
        "pushf\n\t"
174
        "pop %0\n\t"
175
        "sti\n\t"
176
        : "=r" (flags)
177
        :
178
        : "memory"
179
    );
180
    return !!(flags & ((uintptr_t)1 << 9));
181
}
182
183
static inline uint64_t rdtsc(void) {
184
    uint32_t edx, eax;
185
    asm volatile ("rdtsc" : "=a" (eax), "=d" (edx) :: "memory");
186
    return ((uint64_t)edx << 32) | eax;
187
}
188
189
static inline uint64_t tsc_freq_arch(void) {
190
    uint32_t eax, ebx, ecx, edx;
191
    if (!cpuid(0x15, 0, &eax, &ebx, &ecx, &edx))
192
        return 0;
193
    if (eax == 0 || ebx == 0 || ecx == 0)
194
        return 0;
195
    return (uint64_t)ecx * ebx / eax;
196
}
197
198
#define rdrand(type) ({ \
199
    type rdrand__ret = 0; \
200
    for (int rdrand__i = 0; rdrand__i < 10; rdrand__i++) { \
201
        bool rdrand__ok; \
202
        asm volatile ( \
203
            "rdrand %0; setc %1" \
204
            : "=r" (rdrand__ret), "=qm" (rdrand__ok) \
205
        ); \
206
        if (rdrand__ok) break; \
207
    } \
208
    rdrand__ret; \
209
})
210
211
#define rdseed(type) ({ \
212
    type rdseed__ret = 0; \
213
    for (int rdseed__i = 0; rdseed__i < 10; rdseed__i++) { \
214
        bool rdseed__ok; \
215
        asm volatile ( \
216
            "rdseed %0; setc %1" \
217
            : "=r" (rdseed__ret), "=qm" (rdseed__ok) \
218
        ); \
219
        if (rdseed__ok) break; \
220
    } \
221
    rdseed__ret; \
222
})
223
224
#define write_cr(reg, val) do { \
225
    asm volatile ("mov %0, %%cr" reg :: "r" (val) : "memory"); \
226
} while (0)
227
228
#define read_cr(reg) ({ \
229
    size_t read_cr__cr; \
230
    asm volatile ("mov %%cr" reg ", %0" : "=r" (read_cr__cr) :: "memory"); \
231
    read_cr__cr; \
232
})
233
234
#define locked_read(var) ({ \
235
    typeof(*var) locked_read__ret = 0; \
236
    asm volatile ( \
237
        "lock xadd %0, %1" \
238
        : "+r" (locked_read__ret), "+m" (*(var)) \
239
        :: "memory" \
240
    ); \
241
    locked_read__ret; \
242
})
243
244
#define locked_write(var, val) do { \
245
    __auto_type locked_write__ret = val; \
246
    asm volatile ( \
247
        "lock xchg %0, %1" \
248
        : "+r" ((locked_write__ret)), "+m" (*(var)) \
249
        :: "memory" \
250
    ); \
251
} while (0)
252
253
#elif defined (__aarch64__)
254
255
static inline uint64_t rdtsc(void) {
256
    uint64_t v;
257
    asm volatile ("mrs %0, cntpct_el0" : "=r" (v));
258
    return v;
259
}
260
261
static inline uint64_t tsc_freq_arch(void) {
262
    uint64_t v;
263
    asm volatile ("mrs %0, cntfrq_el0" : "=r" (v));
264
    return v;
265
}
266
267
#define locked_read(var) ({ \
268
    typeof(*var) locked_read__ret = 0; \
269
    asm volatile ( \
270
        "ldar %0, %1" \
271
        : "=r" (locked_read__ret) \
272
        : "Q" (*(var)) \
273
        : "memory" \
274
    ); \
275
    locked_read__ret; \
276
})
277
278
static inline size_t icache_line_size(void) {
279
    uint64_t ctr;
280
    asm volatile ("mrs %0, ctr_el0" : "=r"(ctr));
281
282
    return 4 << (ctr & 0b1111);
283
}
284
285
static inline size_t dcache_line_size(void) {
286
    uint64_t ctr;
287
    asm volatile ("mrs %0, ctr_el0" : "=r"(ctr));
288
289
    return 4 << ((ctr >> 16) & 0b1111);
290
}
291
292
static inline bool is_icache_pipt(void) {
293
    uint64_t ctr;
294
    asm volatile ("mrs %0, ctr_el0" : "=r"(ctr));
295
296
    return ((ctr >> 14) & 0b11) == 0b11;
297
}
298
299
// Clean D-Cache to Point of Coherency
300
static inline void clean_dcache_poc(uintptr_t start, uintptr_t end) {
301
    size_t dsz = dcache_line_size();
302
303
    uintptr_t addr = start & ~(dsz - 1);
304
    while (addr < end) {
305
        asm volatile ("dc cvac, %0" :: "r"(addr) : "memory");
306
        addr += dsz;
307
    }
308
309
    asm volatile ("dsb sy\n\tisb");
310
}
311
312
// Invalidate I-Cache to Point of Unification
313
static inline void inval_icache_pou(uintptr_t start, uintptr_t end) {
314
    if (!is_icache_pipt()) {
315
        asm volatile ("ic ialluis" ::: "memory");
316
        asm volatile ("dsb sy\n\tisb");
317
        return;
318
    }
319
320
    size_t isz = icache_line_size();
321
322
    uintptr_t addr = start & ~(isz - 1);
323
    while (addr < end) {
324
        asm volatile ("ic ivau, %0" :: "r"(addr) : "memory");
325
        addr += isz;
326
    }
327
328
    asm volatile ("dsb sy\n\tisb");
329
}
330
331
static inline int current_el(void) {
332
    uint64_t v;
333
334
    asm volatile ("mrs %0, currentel" : "=r"(v));
335
    v = (v >> 2) & 0b11;
336
337
    return v;
338
}
339
340
#elif defined (__riscv)
341
342
static inline uint64_t rdtsc(void) {
343
    uint64_t v;
344
    asm volatile ("rdtime %0" : "=r"(v));
345
    return v;
346
}
347
348
uint64_t riscv_time_base_frequency(void);
349
350
static inline uint64_t tsc_freq_arch(void) {
351
    return riscv_time_base_frequency();
352
}
353
354
#define csr_read(csr) ({\
355
    size_t v;\
356
    asm volatile ("csrr %0, " csr : "=r"(v));\
357
    v;\
358
})
359
360
#define csr_write(csr, v) ({\
361
    size_t old;\
362
    asm volatile ("csrrw %0, " csr ", %1" : "=r"(old) : "r"(v));\
363
    old;\
364
})
365
366
#define make_satp(mode, ppn) (((size_t)(mode) << 60) | ((size_t)(ppn) >> 12))
367
368
#define locked_read(var) ({ \
369
    typeof(*var) locked_read__ret; \
370
    asm volatile ( \
371
        "ld %0, (%1); fence r, rw" \
372
        : "=r"(locked_read__ret) \
373
        : "r"(var) \
374
        : "memory" \
375
    ); \
376
    locked_read__ret; \
377
})
378
379
extern size_t bsp_hartid;
380
381
struct riscv_hart {
382
    struct riscv_hart *next;
383
    const char *isa_string;
384
    size_t hartid;
385
    uint32_t acpi_uid;
386
    uint8_t mmu_type;
387
    uint8_t flags;
388
};
389
390
#define RISCV_HART_COPROC  ((uint8_t)1 << 0)  // is a coprocessor
391
#define RISCV_HART_HAS_MMU ((uint8_t)1 << 1)  // `mmu_type` field is valid
392
393
extern struct riscv_hart *hart_list;
394
extern struct riscv_hart *bsp_hart;
395
396
bool riscv_check_isa_extension_for(size_t hartid, const char *ext, size_t *maj, size_t *min);
397
398
static inline bool riscv_check_isa_extension(const char *ext, size_t *maj, size_t *min) {
399
    return riscv_check_isa_extension_for(bsp_hartid, ext, maj, min);
400
}
401
402
void init_riscv(const char *config);
403
404
#elif defined (__loongarch64)
405
406
#define csr_read64(reg) ({ \
407
    uint64_t csr_read64__ret; \
408
    asm volatile ( \
409
        "csrrd %0, %1" \
410
        : "=r"(csr_read64__ret) \
411
        : "i"(reg) \
412
    ); \
413
    csr_read64__ret; \
414
})
415
416
#define csr_write64(val, reg) do { \
417
    __auto_type csr_write64__val = (val); \
418
    asm volatile ( \
419
        "csrwr %0, %1" \
420
        : \
421
        : "r"(csr_write64__val), "i"(reg) \
422
        : "memory" \
423
    ); \
424
} while (0)
425
426
#define csr_read32(reg) ((uint32_t)csr_read64(reg))
427
428
#define csr_write32(val, reg) do { \
429
    csr_write64((uint64_t)(val), reg); \
430
} while (0)
431
432
#define csr_xchg64(val, mask, reg) ({ \
433
    uint64_t csr_xchg64__ret = (uint64_t)(val); \
434
    uint64_t csr_xchg64__mask = (uint64_t)(mask); \
435
    asm volatile ( \
436
        "csrxchg %0, %1, %2" \
437
        : "+r"(csr_xchg64__ret) \
438
        : "r"(csr_xchg64__mask), "i"(reg) \
439
        : "memory" \
440
    ); \
441
    csr_xchg64__ret; \
442
})
443
444
#define locked_read(var) ({ \
445
    typeof(*var) locked_read__ret; \
446
    asm volatile ( \
447
        "ld.d %0, %1\n\t" \
448
        "dbar 0" \
449
        : "=r"(locked_read__ret) \
450
        : "m"(*(var)) \
451
        : "memory" \
452
    ); \
453
    locked_read__ret; \
454
})
455
456
static inline uint32_t iocsr_read32(uint64_t reg) {
457
    uint32_t val;
458
    asm volatile (
459
        "iocsrrd.w %0, %1"
460
        : "=r"(val)
461
        : "r"(reg)
462
        : "memory"
463
    );
464
    return val;
465
}
466
467
static inline void iocsr_write32(uint32_t val, uint64_t reg) {
468
    asm volatile (
469
        "iocsrwr.w %0, %1"
470
        :
471
        : "r"(val), "r"(reg)
472
        : "memory"
473
    );
474
}
475
476
static inline uint64_t iocsr_read64(uint64_t reg) {
477
    uint64_t val;
478
    asm volatile (
479
        "iocsrrd.d %0, %1"
480
        : "=r"(val)
481
        : "r"(reg)
482
        : "memory"
483
    );
484
    return val;
485
}
486
487
static inline void iocsr_write64(uint64_t val, uint64_t reg) {
488
    asm volatile (
489
        "iocsrwr.d %0, %1"
490
        :
491
        : "r"(val), "r"(reg)
492
        : "memory"
493
    );
494
}
495
496
static inline uint64_t rdtsc(void) {
497
    uint64_t v;
498
    asm volatile ("rdtime.d %0, $zero" : "=r" (v));
499
    return v;
500
}
501
502
static inline uint32_t loongarch_cpucfg(uint32_t reg) {
503
    uint32_t v;
504
    asm volatile ("cpucfg %0, %1" : "=r" (v) : "r" (reg));
505
    return v;
506
}
507
508
static inline uint64_t tsc_freq_arch(void) {
509
    uint32_t cc_freq = loongarch_cpucfg(4);
510
    uint32_t cc_cfg = loongarch_cpucfg(5);
511
    uint32_t cc_mul = cc_cfg & 0xFFFF;
512
    uint32_t cc_div = (cc_cfg >> 16) & 0xFFFF;
513
    if (cc_freq == 0 || cc_mul == 0 || cc_div == 0) {
514
        return 0;
515
    }
516
    return (uint64_t)cc_freq * cc_mul / cc_div;
517
}
518
519
#else
520
#error Unknown architecture
521
#endif
522
523
extern uint64_t tsc_freq;
524
void calibrate_tsc(void);
525
526
static inline uint64_t rdtsc_usec(void) {
527
    uint64_t exec_ticks = rdtsc();
528
    if (tsc_freq == 0) {
529
        return 0;
530
    }
531
    return exec_ticks / tsc_freq * 1000000
532
         + exec_ticks % tsc_freq * 1000000 / tsc_freq;
533
}
534
535
static inline void stall(uint64_t us) {
536
    uint64_t ticks = (tsc_freq * us + 999999) / 1000000;
537
    uint64_t next_stop = rdtsc() + ticks;
538
    while (rdtsc() < next_stop);
539
}
540
541
static inline const char *current_arch(void) {
542
#if defined (__x86_64__)
543
    return "x86-64";
544
#elif defined (__i386__)
545
    uint32_t eax, ebx, ecx, edx;
546
    if (!cpuid(0x80000001, 0, &eax, &ebx, &ecx, &edx) || !(edx & (1 << 29))) {
547
        return "ia-32";
548
    } else {
549
        return "x86-64";
550
    }
551
#elif defined (__aarch64__)
552
    return "aarch64";
553
#elif defined (__riscv)
554
    return "riscv64";
555
#elif defined (__loongarch64)
556
    return "loongarch64";
557
#else
558
#error "Unspecified architecture"
559
#endif
560
}
561
562
#endif
tab: 248 wrap: offon