The Pedigree Project 0.1
random.c
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include "pedigree/kernel/processor/types.h"
21#include "pedigree/kernel/utilities/lib.h"
22
23static uint64_t g_Seed = 1;
24
25#if X64
26static volatile int g_FeaturesChecked = 0;
27static int g_HasRdseed = 0;
28static int g_HasRdrand = 0;
29
30static void random_cpuid(uint32_t leaf, uint32_t subleaf, uint32_t* eax, uint32_t* ebx,
31 uint32_t* ecx, uint32_t* edx) {
32 uint32_t a = leaf;
33 uint32_t b = 0;
34 uint32_t c = subleaf;
35 uint32_t d = 0;
36 __asm__ volatile("cpuid" : "+a"(a), "=b"(b), "+c"(c), "=d"(d) : : "memory");
37 *eax = a;
38 *ebx = b;
39 *ecx = c;
40 *edx = d;
41}
42
43static void random_check_features(void) {
44 if (g_FeaturesChecked) {
45 return;
46 }
47
48 uint32_t eax = 0;
49 uint32_t ebx = 0;
50 uint32_t ecx = 0;
51 uint32_t edx = 0;
52 random_cpuid(0, 0, &eax, &ebx, &ecx, &edx);
53 const uint32_t maximumLeaf = eax;
54
55 if (maximumLeaf >= 1) {
56 random_cpuid(1, 0, &eax, &ebx, &ecx, &edx);
57 g_HasRdrand = (ecx & (1U << 30U)) != 0;
58 }
59 if (maximumLeaf >= 7) {
60 random_cpuid(7, 0, &eax, &ebx, &ecx, &edx);
61 g_HasRdseed = (ebx & (1U << 18U)) != 0;
62 }
63
64 __sync_synchronize();
65 g_FeaturesChecked = 1;
66}
67
68static int random_hardware_word(uint64_t* value) {
69 random_check_features();
70
71 if (g_HasRdrand) {
72 for (size_t attempt = 0; attempt < 10; ++attempt) {
73 unsigned char ok = 0;
74 uint64_t candidate = 0;
75 __asm__ volatile("rdrand %0; setc %1" : "=r"(candidate), "=qm"(ok) : : "cc");
76 if (ok) {
77 *value = candidate;
78 return 1;
79 }
80 __asm__ volatile("pause");
81 }
82 }
83
84 if (g_HasRdseed) {
85 for (size_t attempt = 0; attempt < 64; ++attempt) {
86 unsigned char ok = 0;
87 uint64_t candidate = 0;
88 __asm__ volatile("rdseed %0; setc %1" : "=r"(candidate), "=qm"(ok) : : "cc");
89 if (ok) {
90 *value = candidate;
91 return 1;
92 }
93 __asm__ volatile("pause");
94 }
95 }
96
97 return 0;
98}
99#endif
100
101void random_seed(uint64_t seed) {
102 g_Seed = seed;
103}
104
105uint64_t random_next() {
106 // This is a http://en.wikipedia.org/wiki/Linear_congruential_generator.
107 g_Seed = (g_Seed * 6364136223846793005ULL) + 1442695040888963407ULL;
108 return g_Seed;
109}
110
111size_t hardware_random_bytes(void* buffer, size_t length) {
112#if X64
113 uint8_t* output = (uint8_t*)buffer;
114 size_t produced = 0;
115 while (produced < length) {
116 uint64_t value = 0;
117 if (!random_hardware_word(&value)) {
118 break;
119 }
120
121 for (size_t byte = 0; byte < sizeof(value) && produced < length; ++byte) {
122 output[produced++] = (uint8_t)value;
123 value >>= 8U;
124 }
125 }
126 return produced;
127#else
128 (void)buffer;
129 (void)length;
130 return 0;
131#endif
132}