:: limine / common / lib / rng_seed.c 5.5 KB raw

1
#if defined (UEFI)
2
3
#include <stdint.h>
4
#include <stddef.h>
5
#include <stdbool.h>
6
#include <efi.h>
7
#include <lib/rng_seed.h>
8
#include <lib/misc.h>
9
#include <lib/print.h>
10
#include <lib/libc.h>
11
12
#define LINUX_EFI_RANDOM_SEED_TABLE_GUID \
13
    { 0x1ce1e5bc, 0x7ceb, 0x42f2, { 0x81, 0xe5, 0x8a, 0xad, 0xf1, 0x80, 0xf5, 0x7b } }
14
15
#define EFI_RANDOM_SEED_SIZE 32
16
17
struct linux_efi_random_seed {
18
    uint32_t size;
19
    uint8_t  bits[];
20
} __attribute__((packed));
21
22
// Pull entropy from EFI_RNG_PROTOCOL while boot services are alive, mix in
23
// the NVRAM-resident "RandomSeed" variable if the OS left one for us, and
24
// publish the result as the LINUX_EFI_RANDOM_SEED_TABLE configuration table
25
// for the kernel's RNG to consume during early boot. Limine bypasses the
26
// EFI stub that would normally do this.
27
void rng_seed_install(void) {
28
    EFI_GUID rng_table_guid = LINUX_EFI_RANDOM_SEED_TABLE_GUID;
29
30
    // The RNG protocol is optional; the NVRAM seed alone is also a valid
31
    // source.
32
    EFI_GUID rng_guid = EFI_RNG_PROTOCOL_GUID;
33
    EFI_RNG_PROTOCOL *rng = NULL;
34
    if (gBS->LocateProtocol(&rng_guid, NULL, (void **)&rng) != EFI_SUCCESS) {
35
        rng = NULL;
36
    }
37
38
    // Probe the NVRAM "RandomSeed" variable; if the OS left one for us,
39
    // we'll consume and delete it so it isn't reused on the next boot.
40
    UINTN nv_seed_size = 0;
41
    EFI_STATUS probe = gRT->GetVariable(L"RandomSeed", &rng_table_guid,
42
                                        NULL, &nv_seed_size, NULL);
43
    if (probe != EFI_BUFFER_TOO_SMALL || nv_seed_size > 512) {
44
        nv_seed_size = 0;
45
    }
46
47
    // A prior boot stage (shim, another stub) may have installed a seed
48
    // already. Preserve it by concatenating rather than overwriting.
49
    struct linux_efi_random_seed *prev_seed = NULL;
50
    uint32_t prev_seed_size = 0;
51
    for (UINTN i = 0; i < gST->NumberOfTableEntries; i++) {
52
        if (memcmp(&gST->ConfigurationTable[i].VendorGuid,
53
                   &rng_table_guid, sizeof(EFI_GUID)) == 0) {
54
            prev_seed = gST->ConfigurationTable[i].VendorTable;
55
            if (prev_seed->size <= 512) {
56
                prev_seed_size = prev_seed->size;
57
            }
58
            break;
59
        }
60
    }
61
62
    UINTN rng_bytes = (rng != NULL) ? EFI_RANDOM_SEED_SIZE : 0;
63
64
    if (rng_bytes == 0 && nv_seed_size == 0 && prev_seed_size == 0) {
65
        return;
66
    }
67
68
    UINTN total_size = sizeof(struct linux_efi_random_seed)
69
                     + rng_bytes + nv_seed_size + prev_seed_size;
70
71
    struct linux_efi_random_seed *seed = NULL;
72
    EFI_STATUS status = gBS->AllocatePool(EfiACPIReclaimMemory, total_size,
73
                                          (void **)&seed);
74
    if (status != EFI_SUCCESS) {
75
        printv("rng: failed to allocate random seed table: %X\n", (uint64_t)status);
76
        return;
77
    }
78
79
    memset(seed, 0, total_size);
80
81
    UINTN offset = 0;
82
83
    // EFI_RNG_PROTOCOL output. Prefer the raw algorithm.
84
    if (rng != NULL) {
85
        EFI_GUID rng_algo_raw = EFI_RNG_ALGORITHM_RAW;
86
        status = rng->GetRNG(rng, &rng_algo_raw, EFI_RANDOM_SEED_SIZE, seed->bits);
87
        if (status == EFI_UNSUPPORTED) {
88
            status = rng->GetRNG(rng, NULL, EFI_RANDOM_SEED_SIZE, seed->bits);
89
        }
90
        if (status == EFI_SUCCESS) {
91
            offset += EFI_RANDOM_SEED_SIZE;
92
        } else {
93
            printv("rng: GetRNG failed: %X\n", (uint64_t)status);
94
        }
95
    }
96
97
    // NVRAM "RandomSeed" variable, then delete it so the same bytes
98
    // aren't reused on the next boot.
99
    if (nv_seed_size > 0) {
100
        UINTN got_size = nv_seed_size;
101
        status = gRT->GetVariable(L"RandomSeed", &rng_table_guid, NULL,
102
                                  &got_size, seed->bits + offset);
103
        if (status == EFI_SUCCESS) {
104
            gRT->SetVariable(L"RandomSeed", &rng_table_guid, 0, 0, NULL);
105
            offset += got_size;
106
        } else {
107
            // Read failed despite probe succeeding. Wipe the slot to avoid
108
            // publishing stale heap contents.
109
            volatile uint8_t *p = (volatile uint8_t *)(seed->bits + offset);
110
            for (size_t i = 0; i < nv_seed_size; i++) {
111
                p[i] = 0;
112
            }
113
            asm volatile ("" ::: "memory");
114
        }
115
    }
116
117
    // Previous-stage seed.
118
    if (prev_seed_size > 0) {
119
        memcpy(seed->bits + offset, prev_seed->bits, prev_seed_size);
120
        offset += prev_seed_size;
121
    }
122
123
    if (offset == 0) {
124
        volatile uint8_t *p = (volatile uint8_t *)seed;
125
        for (size_t i = 0; i < total_size; i++) {
126
            p[i] = 0;
127
        }
128
        asm volatile ("" ::: "memory");
129
        gBS->FreePool(seed);
130
        return;
131
    }
132
133
    seed->size = (uint32_t)offset;
134
135
    status = gBS->InstallConfigurationTable(&rng_table_guid, seed);
136
    if (status != EFI_SUCCESS) {
137
        printv("rng: failed to install random seed table: %X\n", (uint64_t)status);
138
        volatile uint8_t *p = (volatile uint8_t *)seed;
139
        for (size_t i = 0; i < total_size; i++) {
140
            p[i] = 0;
141
        }
142
        asm volatile ("" ::: "memory");
143
        gBS->FreePool(seed);
144
        return;
145
    }
146
147
    if (prev_seed_size > 0) {
148
        volatile uint8_t *p = (volatile uint8_t *)prev_seed;
149
        size_t prev_total = sizeof(struct linux_efi_random_seed) + prev_seed_size;
150
        for (size_t i = 0; i < prev_total; i++) {
151
            p[i] = 0;
152
        }
153
        asm volatile ("" ::: "memory");
154
        // Assumes the prior publisher used AllocatePool.
155
        gBS->FreePool(prev_seed);
156
    }
157
158
    printv("rng: installed %u-byte random seed as configuration table\n",
159
           seed->size);
160
}
161
162
#endif
tab: 248 wrap: offon