Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 * Copyright (c) 2020, Peter Elliott <pelliott@serenityos.org>
4 *
5 * SPDX-License-Identifier: BSD-2-Clause
6 */
7
8#include <AK/Singleton.h>
9#include <Kernel/Arch/Processor.h>
10#if ARCH(X86_64)
11# include <Kernel/Arch/x86_64/Time/HPET.h>
12# include <Kernel/Arch/x86_64/Time/RTC.h>
13#elif ARCH(AARCH64)
14# include <Kernel/Arch/aarch64/ASM_wrapper.h>
15#endif
16#include <Kernel/Devices/RandomDevice.h>
17#include <Kernel/Random.h>
18#include <Kernel/Sections.h>
19#include <Kernel/Time/TimeManagement.h>
20
21namespace Kernel {
22
23static Singleton<KernelRng> s_the;
24static Atomic<u32, AK::MemoryOrder::memory_order_relaxed> s_next_random_value = 1;
25
26KernelRng& KernelRng::the()
27{
28 return *s_the;
29}
30
31UNMAP_AFTER_INIT KernelRng::KernelRng()
32{
33#if ARCH(X86_64)
34 if (Processor::current().has_feature(CPUFeature::RDSEED)) {
35 dmesgln("KernelRng: Using RDSEED as entropy source");
36
37 for (size_t i = 0; i < pool_count * reseed_threshold; ++i) {
38 add_random_event(Kernel::read_rdseed(), i % 32);
39 }
40 } else if (Processor::current().has_feature(CPUFeature::RDRAND)) {
41 dmesgln("KernelRng: Using RDRAND as entropy source");
42
43 for (size_t i = 0; i < pool_count * reseed_threshold; ++i) {
44 add_random_event(Kernel::read_rdrand(), i % 32);
45 }
46 } else if (TimeManagement::the().can_query_precise_time()) {
47 // Add HPET as entropy source if we don't have anything better.
48 dmesgln("KernelRng: Using HPET as entropy source");
49
50 for (size_t i = 0; i < pool_count * reseed_threshold; ++i) {
51 u64 hpet_time = HPET::the().read_main_counter_unsafe();
52 add_random_event(hpet_time, i % 32);
53 }
54 } else {
55 // Fallback to RTC
56 dmesgln("KernelRng: Using RTC as entropy source (bad!)");
57 auto current_time = static_cast<u64>(RTC::now());
58 for (size_t i = 0; i < pool_count * reseed_threshold; ++i) {
59 add_random_event(current_time, i % 32);
60 current_time *= 0x574au;
61 current_time += 0x40b2u;
62 }
63 }
64#elif ARCH(AARCH64)
65 if (Processor::current().has_feature(CPUFeature::RNG)) {
66 dmesgln("KernelRng: Using RNDRRS as entropy source");
67 for (size_t i = 0; i < pool_count * reseed_threshold; ++i) {
68 add_random_event(Aarch64::Asm::read_rndrrs(), i % 32);
69 }
70 } else {
71 // Fallback to TimeManagement as entropy
72 dmesgln("KernelRng: Using bad entropy source TimeManagement");
73 auto current_time = static_cast<u64>(TimeManagement::the().now().to_milliseconds());
74 for (size_t i = 0; i < pool_count * reseed_threshold; ++i) {
75 add_random_event(current_time, i % 32);
76 current_time *= 0x574au;
77 current_time += 0x40b2u;
78 }
79 }
80#else
81 dmesgln("KernelRng: No entropy source available!");
82#endif
83}
84
85void KernelRng::wait_for_entropy()
86{
87 SpinlockLocker lock(get_lock());
88 if (!is_ready()) {
89 dbgln("Entropy starvation...");
90 m_seed_queue.wait_forever("KernelRng"sv);
91 }
92}
93
94void KernelRng::wake_if_ready()
95{
96 VERIFY(get_lock().is_locked());
97 if (is_ready()) {
98 m_seed_queue.wake_all();
99 }
100}
101
102size_t EntropySource::next_source { static_cast<size_t>(EntropySource::Static::MaxHardcodedSourceIndex) };
103
104static void do_get_fast_random_bytes(Bytes buffer)
105{
106
107 union {
108 u8 bytes[4];
109 u32 value;
110 } u;
111 size_t offset = 4;
112 for (size_t i = 0; i < buffer.size(); ++i) {
113 if (offset >= 4) {
114 auto current_next = s_next_random_value.load();
115 for (;;) {
116 auto new_next = current_next * 1103515245 + 12345;
117 if (s_next_random_value.compare_exchange_strong(current_next, new_next)) {
118 u.value = new_next;
119 break;
120 }
121 }
122 offset = 0;
123 }
124 buffer[i] = u.bytes[offset++];
125 }
126}
127
128bool get_good_random_bytes(Bytes buffer, bool allow_wait, bool fallback_to_fast)
129{
130 bool result = false;
131 auto& kernel_rng = KernelRng::the();
132 // FIXME: What if interrupts are disabled because we're in an interrupt?
133 bool can_wait = Processor::are_interrupts_enabled();
134 if (!can_wait && allow_wait) {
135 // If we can't wait but the caller would be ok with it, then we
136 // need to definitely fallback to *something*, even if it's less
137 // secure...
138 fallback_to_fast = true;
139 }
140 if (can_wait && allow_wait) {
141 for (;;) {
142 {
143 if (kernel_rng.get_random_bytes(buffer)) {
144 result = true;
145 break;
146 }
147 }
148 kernel_rng.wait_for_entropy();
149 }
150 } else {
151 // We can't wait/block here, or we are not allowed to block/wait
152 if (kernel_rng.get_random_bytes(buffer)) {
153 result = true;
154 } else if (fallback_to_fast) {
155 // If interrupts are disabled
156 do_get_fast_random_bytes(buffer);
157 result = true;
158 }
159 }
160
161 // NOTE: The only case where this function should ever return false and
162 // not actually return random data is if fallback_to_fast == false and
163 // allow_wait == false and interrupts are enabled!
164 VERIFY(result || !fallback_to_fast);
165 return result;
166}
167
168void get_fast_random_bytes(Bytes buffer)
169{
170 // Try to get good randomness, but don't block if we can't right now
171 // and allow falling back to fast randomness
172 auto result = get_good_random_bytes(buffer, false, true);
173 VERIFY(result);
174}
175
176}