1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
|
/** @file
Random number generator service that uses the SEED instruction
to provide pseudorandom numbers.
Copyright (c) 2024, Rivos, Inc.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <Uefi.h>
#include <Library/BaseLib.h>
#include <Library/DebugLib.h>
#include <Library/RngLib.h>
#include <Register/RiscV64/RiscVEncoding.h>
#include "BaseRngLibInternals.h"
#define RISCV_CPU_FEATURE_ZKR_BITMASK 0x8
#define SEED_RETRY_LOOPS 100
// 64-bit Mersenne Twister implementation
// A widely used pseudo random number generator. It performs bit shifts etc to
// achieve the random number. It's output is determined by SEED value generated
// by RISC-V SEED CSR"
#define STATE_SIZE 312
#define MIDDLE 156
#define INIT_SHIFT 62
#define TWIST_MASK 0xb5026f5aa96619e9ULL
#define INIT_FACT 6364136223846793005ULL
#define SHIFT1 29
#define MASK1 0x5555555555555555ULL
#define SHIFT2 17
#define MASK2 0x71d67fffeda60000ULL
#define SHIFT3 37
#define MASK3 0xfff7eee000000000ULL
#define SHIFT4 43
#define LOWER_MASK 0x7fffffff
#define UPPER_MASK (~(UINT64)LOWER_MASK)
static UINT64 mState[STATE_SIZE];
static UINTN mIndex = STATE_SIZE + 1;
/**
Initialize mState to defualt state.
@param[in] S Input seed value
**/
STATIC
VOID
SeedRng (
IN UINT64 S
)
{
UINTN I;
mIndex = STATE_SIZE;
mState[0] = S;
for (I = 1; I < STATE_SIZE; I++) {
mState[I] = (INIT_FACT * (mState[I - 1] ^ (mState[I - 1] >> INIT_SHIFT))) + I;
}
}
/**
Initializes mState with entropy values. The initialization is based on the
Seed value populated in mState[0] which then influences all the other values
in the mState array. Later values are retrieved from the same array instead
of calling trng instruction every time.
**/
STATIC
VOID
TwistRng (
VOID
)
{
UINTN I;
UINT64 X;
for (I = 0; I < STATE_SIZE; I++) {
X = (mState[I] & UPPER_MASK) | (mState[(I + 1) % STATE_SIZE] & LOWER_MASK);
X = (X >> 1) ^ (X & 1 ? TWIST_MASK : 0);
mState[I] = mState[(I + MIDDLE) % STATE_SIZE] ^ X;
}
mIndex = 0;
}
// Defined in Seed.S
extern UINT64
ReadSeed (
VOID
);
/**
Gets seed value by executing trng instruction (CSR 0x15) amd returns
the see to the caller 64bit value.
@param[out] Out Buffer pointer to store the 64-bit random value.
@retval TRUE Random number generated successfully.
@retval FALSE Failed to generate the random number.
**/
STATIC
BOOLEAN
Get64BitSeed (
OUT UINT64 *Out
)
{
UINT64 Seed;
UINTN Retry;
UINTN ValidSeeds;
UINTN NeededSeeds;
UINT16 *Entropy;
Retry = SEED_RETRY_LOOPS;
Entropy = (UINT16 *)Out;
NeededSeeds = sizeof (UINT64) / sizeof (UINT16);
ValidSeeds = 0;
if (!ArchIsRngSupported ()) {
DEBUG ((DEBUG_ERROR, "Get64BitSeed: HW not supported!\n"));
return FALSE;
}
do {
Seed = ReadSeed ();
switch (Seed & SEED_OPST_MASK) {
case SEED_OPST_ES16:
Entropy[ValidSeeds++] = Seed & SEED_ENTROPY_MASK;
if (ValidSeeds == NeededSeeds) {
return TRUE;
}
break;
case SEED_OPST_DEAD:
DEBUG ((DEBUG_ERROR, "Get64BitSeed: Unrecoverable error!\n"));
return FALSE;
case SEED_OPST_BIST: // fallthrough
case SEED_OPST_WAIT: // fallthrough
default:
continue;
}
} while (--Retry);
return FALSE;
}
/**
Constructor library which initializes Seeds and mStatus array.
@retval EFI_SUCCESS Intialization was successful.
@retval EFI_UNSUPPORTED Feature not supported.
**/
EFI_STATUS
EFIAPI
BaseRngLibConstructor (
VOID
)
{
UINT64 Seed;
if (Get64BitSeed (&Seed)) {
SeedRng (Seed);
return EFI_SUCCESS;
} else {
return EFI_UNSUPPORTED;
}
}
/**
Generates a 16-bit random number.
@param[out] Rand Buffer pointer to store the 16-bit random value.
@retval TRUE Random number generated successfully.
@retval FALSE Failed to generate the random number.
**/
BOOLEAN
EFIAPI
ArchGetRandomNumber16 (
OUT UINT16 *Rand
)
{
UINT64 Rand64;
if (ArchGetRandomNumber64 (&Rand64)) {
*Rand = Rand64 & MAX_UINT16;
return TRUE;
}
return FALSE;
}
/**
Generates a 32-bit random number.
@param[out] Rand Buffer pointer to store the 32-bit random value.
@retval TRUE Random number generated successfully.
@retval FALSE Failed to generate the random number.
**/
BOOLEAN
EFIAPI
ArchGetRandomNumber32 (
OUT UINT32 *Rand
)
{
UINT64 Rand64;
if (ArchGetRandomNumber64 (&Rand64)) {
*Rand = Rand64 & MAX_UINT32;
return TRUE;
}
return FALSE;
}
/**
Generates a 64-bit random number.
@param[out] Rand Buffer pointer to store the 64-bit random value.
@retval TRUE Random number generated successfully.
@retval FALSE Failed to generate the random number.
**/
BOOLEAN
EFIAPI
ArchGetRandomNumber64 (
OUT UINT64 *Rand
)
{
UINT64 Y;
// Never initialized.
if (mIndex > STATE_SIZE) {
return FALSE;
}
// Mersenne Twister
if (mIndex == STATE_SIZE) {
TwistRng ();
}
Y = mState[mIndex];
Y ^= (Y >> SHIFT1) & MASK1;
Y ^= (Y << SHIFT2) & MASK2;
Y ^= (Y << SHIFT3) & MASK3;
Y ^= Y >> SHIFT4;
mIndex++;
*Rand = Y;
return TRUE;
}
/**
Checks whether SEED is supported.
@retval TRUE SEED is supported.
**/
BOOLEAN
EFIAPI
ArchIsRngSupported (
VOID
)
{
return ((PcdGet64 (PcdRiscVFeatureOverride) & RISCV_CPU_FEATURE_ZKR_BITMASK) != 0);
}
|