:: limine / common / lib / rand.c 3.7 KB raw

1
#include <stdint.h>
2
#include <stddef.h>
3
#include <stdbool.h>
4
#include <lib/misc.h>
5
#include <lib/print.h>
6
#include <lib/rand.h>
7
#include <sys/cpu.h>
8
#include <mm/pmm.h>
9
10
// TODO: Find where this mersenne twister implementation is inspired from
11
//       and properly credit the original author(s).
12
13
static bool rand_initialised = false;
14
15
#define n ((int)624)
16
#define m ((int)397)
17
#define matrix_a ((uint32_t)0x9908b0df)
18
#define msb ((uint32_t)0x80000000)
19
#define lsbs ((uint32_t)0x7fffffff)
20
21
static uint32_t *status;
22
static int ctr;
23
24
static uint32_t hw_entropy(void) {
25
#if defined (__x86_64__) || defined(__i386__)
26
    uint32_t eax, ebx, ecx, edx;
27
28
    if (cpuid(0x07, 0, &eax, &ebx, &ecx, &edx) && (ebx & (1 << 18))) {
29
        uint32_t val =
30
#if defined (__x86_64__)
31
            (uint32_t)rdseed(uint64_t); // Always do a 64-bit op on 64-bit to work around CPU bugs.
32
#elif defined (__i386__)
33
            rdseed(uint32_t);
34
#endif
35
        if (val != 0) return val;
36
    } else if (cpuid(0x01, 0, &eax, &ebx, &ecx, &edx) && (ecx & (1 << 30))) {
37
        uint32_t val =
38
#if defined (__x86_64__)
39
            (uint32_t)rdrand(uint64_t); // As above.
40
#elif defined (__i386__)
41
            rdrand(uint32_t);
42
#endif
43
        if (val != 0) return val;
44
    }
45
#elif defined (__aarch64__)
46
    // ARMv8.5-RNG: check ID_AA64ISAR0_EL1 RNDR field (bits [63:60])
47
    uint64_t isar0;
48
    asm volatile ("mrs %0, id_aa64isar0_el1" : "=r" (isar0));
49
    if ((isar0 >> 60) & 0xf) {
50
        uint64_t rndr;
51
        // RNDR register: s3_3_c2_c4_0
52
        bool ok;
53
        asm volatile (
54
            "mrs %0, s3_3_c2_c4_0\n\t"
55
            "cset %w1, ne"
56
            : "=r" (rndr), "=r" (ok)
57
            :
58
            : "cc"
59
        );
60
        if (ok) {
61
            return (uint32_t)rndr;
62
        }
63
    }
64
#endif
65
66
#if defined (UEFI)
67
    // Try EFI RNG protocol as a fallback for all UEFI platforms
68
    {
69
        EFI_GUID rng_guid = EFI_RNG_PROTOCOL_GUID;
70
        EFI_RNG_PROTOCOL *rng = NULL;
71
        if (gBS->LocateProtocol(&rng_guid, NULL, (void **)&rng) == EFI_SUCCESS && rng != NULL) {
72
            uint32_t val;
73
            if (rng->GetRNG(rng, NULL, sizeof(val), (UINT8 *)&val) == EFI_SUCCESS) {
74
                return val;
75
            }
76
        }
77
    }
78
#endif
79
80
    return 0;
81
}
82
83
static void init_rand(void) {
84
    uint32_t seed = ((uint32_t)0xc597060c * (uint32_t)rdtsc())
85
                  * ((uint32_t)0xce86d624)
86
                  ^ ((uint32_t)0xee0da130 * (uint32_t)rdtsc());
87
88
    uint32_t hw = hw_entropy();
89
    if (hw != 0) {
90
        seed *= (seed ^ hw);
91
    }
92
93
    status = ext_mem_alloc_counted(n, sizeof(uint32_t));
94
95
    srand(seed);
96
97
    rand_initialised = true;
98
}
99
100
void srand(uint32_t s) {
101
    status[0] = s;
102
    for (ctr = 1; ctr < n; ctr++)
103
        status[ctr] = (1812433253 * (status[ctr - 1] ^ (status[ctr - 1] >> 30)) + ctr);
104
}
105
106
uint32_t rand32(void) {
107
    if (!rand_initialised)
108
        init_rand();
109
110
    const uint32_t mag01[2] = {0, matrix_a};
111
112
    if (ctr >= n) {
113
        for (int kk = 0; kk < n - m; kk++) {
114
            uint32_t y = (status[kk] & msb) | (status[kk + 1] & lsbs);
115
            status[kk] = status[kk + m] ^ (y >> 1) ^ mag01[y & 1];
116
        }
117
118
        for (int kk = n - m; kk < n - 1; kk++) {
119
            uint32_t y = (status[kk] & msb) | (status[kk + 1] & lsbs);
120
            status[kk] = status[kk + (m - n)] ^ (y >> 1) ^ mag01[y & 1];
121
        }
122
123
        uint32_t y = (status[n - 1] & msb) | (status[0] & lsbs);
124
        status[n - 1] = status[m - 1] ^ (y >> 1) ^ mag01[y & 1];
125
126
        ctr = 0;
127
    }
128
129
    uint32_t res = status[ctr++];
130
131
    res ^= (res >> 11);
132
    res ^= (res << 7) & 0x9d2c5680;
133
    res ^= (res << 15) & 0xefc60000;
134
    res ^= (res >> 18);
135
136
    return res;
137
}
138
139
uint64_t rand64(void) {
140
    return (((uint64_t)rand32()) << 32) | (uint64_t)rand32();
141
}
tab: 248 wrap: offon