--- /dev/null
+/** @file\r
+ Support routines for RDRAND instruction access.\r
+\r
+Copyright (c) 2013, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution. The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "RdRand.h"\r
+#include "AesCore.h"\r
+\r
+//\r
+// Bit mask used to determine if RdRand instruction is supported.\r
+//\r
+#define RDRAND_MASK 0x40000000\r
+\r
+/**\r
+ Determines whether or not RDRAND instruction is supported by the host hardware.\r
+\r
+ @retval EFI_SUCCESS RDRAND instruction supported.\r
+ @retval EFI_UNSUPPORTED RDRAND instruction not supported.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IsRdRandSupported (\r
+ VOID\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT32 RegEax;\r
+ UINT32 RegEbx;\r
+ UINT32 RegEcx;\r
+ UINT32 RegEdx;\r
+ BOOLEAN IsIntelCpu;\r
+\r
+ Status = EFI_UNSUPPORTED;\r
+ IsIntelCpu = FALSE;\r
+ \r
+ //\r
+ // Checks whether the current processor is an Intel product by CPUID.\r
+ //\r
+ AsmCpuid (0, &RegEax, &RegEbx, &RegEcx, &RegEdx);\r
+ if ((CompareMem ((CHAR8 *)(&RegEbx), "Genu", 4) == 0) &&\r
+ (CompareMem ((CHAR8 *)(&RegEdx), "ineI", 4) == 0) &&\r
+ (CompareMem ((CHAR8 *)(&RegEcx), "ntel", 4) == 0)) {\r
+ IsIntelCpu = TRUE;\r
+ }\r
+\r
+ if (IsIntelCpu) {\r
+ //\r
+ // Determine RDRAND support by examining bit 30 of the ECX register returned by CPUID.\r
+ // A value of 1 indicates that processor supports RDRAND instruction.\r
+ //\r
+ AsmCpuid (1, 0, 0, &RegEcx, 0);\r
+\r
+ if ((RegEcx & RDRAND_MASK) == RDRAND_MASK) {\r
+ Status = EFI_SUCCESS;\r
+ }\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Calls RDRAND to obtain a 16-bit random number.\r
+\r
+ @param[out] Rand Buffer pointer to store the random result.\r
+ @param[in] NeedRetry Determine whether or not to loop retry.\r
+\r
+ @retval EFI_SUCCESS RDRAND call was successful.\r
+ @retval EFI_NOT_READY Failed attempts to call RDRAND.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+RdRand16 (\r
+ OUT UINT16 *Rand,\r
+ IN BOOLEAN NeedRetry\r
+ )\r
+{\r
+ UINT32 Index;\r
+ UINT32 RetryCount;\r
+\r
+ if (NeedRetry) {\r
+ RetryCount = RETRY_LIMIT;\r
+ } else {\r
+ RetryCount = 1;\r
+ }\r
+\r
+ //\r
+ // Perform a single call to RDRAND, or enter a loop call until RDRAND succeeds.\r
+ //\r
+ for (Index = 0; Index < RetryCount; Index++) {\r
+ if (RdRand16Step (Rand)) {\r
+ return EFI_SUCCESS;\r
+ }\r
+ }\r
+ \r
+ return EFI_NOT_READY;\r
+}\r
+\r
+/**\r
+ Calls RDRAND to obtain a 32-bit random number.\r
+\r
+ @param[out] Rand Buffer pointer to store the random result.\r
+ @param[in] NeedRetry Determine whether or not to loop retry.\r
+\r
+ @retval EFI_SUCCESS RDRAND call was successful.\r
+ @retval EFI_NOT_READY Failed attempts to call RDRAND.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+RdRand32 (\r
+ OUT UINT32 *Rand,\r
+ IN BOOLEAN NeedRetry\r
+ )\r
+{\r
+ UINT32 Index;\r
+ UINT32 RetryCount;\r
+\r
+ if (NeedRetry) {\r
+ RetryCount = RETRY_LIMIT;\r
+ } else {\r
+ RetryCount = 1;\r
+ }\r
+\r
+ //\r
+ // Perform a single call to RDRAND, or enter a loop call until RDRAND succeeds.\r
+ //\r
+ for (Index = 0; Index < RetryCount; Index++) {\r
+ if (RdRand32Step (Rand)) {\r
+ return EFI_SUCCESS;\r
+ }\r
+ }\r
+ \r
+ return EFI_NOT_READY;\r
+}\r
+\r
+/**\r
+ Calls RDRAND to obtain a 64-bit random number.\r
+\r
+ @param[out] Rand Buffer pointer to store the random result.\r
+ @param[in] NeedRetry Determine whether or not to loop retry.\r
+\r
+ @retval EFI_SUCCESS RDRAND call was successful.\r
+ @retval EFI_NOT_READY Failed attempts to call RDRAND.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+RdRand64 (\r
+ OUT UINT64 *Rand,\r
+ IN BOOLEAN NeedRetry\r
+ )\r
+{\r
+ UINT32 Index;\r
+ UINT32 RetryCount;\r
+\r
+ if (NeedRetry) {\r
+ RetryCount = RETRY_LIMIT;\r
+ } else {\r
+ RetryCount = 1;\r
+ }\r
+\r
+ //\r
+ // Perform a single call to RDRAND, or enter a loop call until RDRAND succeeds.\r
+ //\r
+ for (Index = 0; Index < RetryCount; Index++) {\r
+ if (RdRand64Step (Rand)) {\r
+ return EFI_SUCCESS;\r
+ }\r
+ }\r
+ \r
+ return EFI_NOT_READY;\r
+}\r
+\r
+/**\r
+ Calls RDRAND to fill a buffer of arbitrary size with random bytes.\r
+\r
+ @param[in] Length Size of the buffer, in bytes, to fill with.\r
+ @param[out] RandBuffer Pointer to the buffer to store the random result.\r
+\r
+ @retval EFI_SUCCESS Random bytes generation succeeded.\r
+ @retval EFI_NOT_READY Failed to request random bytes.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+RdRandGetBytes (\r
+ IN UINTN Length,\r
+ OUT UINT8 *RandBuffer\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT8 *Start;\r
+ UINT8 *ResidualStart;\r
+ UINTN *BlockStart;\r
+ UINTN TempRand;\r
+ UINTN Count;\r
+ UINTN Residual;\r
+ UINTN StartLen;\r
+ UINTN BlockNum;\r
+ UINTN Index;\r
+\r
+ ResidualStart = NULL;\r
+ TempRand = 0;\r
+\r
+ //\r
+ // Compute the address of the first word aligned (32/64-bit) block in the \r
+ // destination buffer, depending on whether we are in 32- or 64-bit mode.\r
+ //\r
+ Start = RandBuffer;\r
+ if (((UINT32)(UINTN)Start % (UINT32)sizeof(UINTN)) == 0) {\r
+ BlockStart = (UINTN *)Start;\r
+ Count = Length;\r
+ StartLen = 0;\r
+ } else {\r
+ BlockStart = (UINTN *)(((UINTN)Start & ~(UINTN)(sizeof(UINTN) - 1)) + (UINTN)sizeof(UINTN));\r
+ Count = Length - (sizeof (UINTN) - (UINT32)((UINTN)Start % sizeof (UINTN)));\r
+ StartLen = (UINT32)((UINTN)BlockStart - (UINTN)Start);\r
+ }\r
+\r
+ //\r
+ // Compute the number of word blocks and the remaining number of bytes.\r
+ //\r
+ Residual = Count % sizeof (UINTN);\r
+ BlockNum = Count / sizeof (UINTN);\r
+ if (Residual != 0) {\r
+ ResidualStart = (UINT8 *) (BlockStart + BlockNum);\r
+ }\r
+\r
+ //\r
+ // Obtain a temporary random number for use in the residuals. Failout if retry fails.\r
+ //\r
+ if (StartLen > 0) {\r
+ Status = RdRandWord ((UINTN *) &TempRand, TRUE);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Populate the starting mis-aligned block.\r
+ //\r
+ for (Index = 0; Index < StartLen; Index++) {\r
+ Start[Index] = (UINT8)(TempRand & 0xff);\r
+ TempRand = TempRand >> 8;\r
+ }\r
+\r
+ //\r
+ // Populate the central aligned block. Fail out if retry fails.\r
+ //\r
+ Status = RdRandGetWords (BlockNum, (UINTN *)(BlockStart));\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ //\r
+ // Populate the final mis-aligned block.\r
+ //\r
+ if (Residual > 0) {\r
+ Status = RdRandWord ((UINTN *)&TempRand, TRUE);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ for (Index = 0; Index < Residual; Index++) {\r
+ ResidualStart[Index] = (UINT8)(TempRand & 0xff);\r
+ TempRand = TempRand >> 8;\r
+ }\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Creates a 128bit random value that is fully forward and backward prediction resistant,\r
+ suitable for seeding a NIST SP800-90 Compliant, FIPS 1402-2 certifiable SW DRBG.\r
+ This function takes multiple random numbers through RDRAND without intervening\r
+ delays to ensure reseeding and performs AES-CBC-MAC over the data to compute the\r
+ seed value.\r
+ \r
+ @param[out] SeedBuffer Pointer to a 128bit buffer to store the random seed.\r
+\r
+ @retval EFI_SUCCESS Random seed generation succeeded.\r
+ @retval EFI_NOT_READY Failed to request random bytes.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+RdRandGetSeed128 (\r
+ OUT UINT8 *SeedBuffer\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT8 RandByte[16];\r
+ UINT8 Key[16];\r
+ UINT8 Ffv[16];\r
+ UINT8 Xored[16];\r
+ UINT32 Index;\r
+ UINT32 Index2;\r
+\r
+ //\r
+ // Chose an arbitary key and zero the feed_forward_value (FFV)\r
+ //\r
+ for (Index = 0; Index < 16; Index++) {\r
+ Key[Index] = (UINT8) Index;\r
+ Ffv[Index] = 0;\r
+ }\r
+\r
+ //\r
+ // Perform CBC_MAC over 32 * 128 bit values, with 10us gaps between 128 bit value\r
+ // The 10us gaps will ensure multiple reseeds within the HW RNG with a large design margin.\r
+ //\r
+ for (Index = 0; Index < 32; Index++) {\r
+ MicroSecondDelay (10);\r
+ Status = RdRandGetBytes (16, RandByte);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Perform XOR operations on two 128-bit value.\r
+ //\r
+ for (Index2 = 0; Index2 < 16; Index2++) {\r
+ Xored[Index2] = RandByte[Index2] ^ Ffv[Index2];\r
+ }\r
+\r
+ AesEncrypt (Key, Xored, Ffv);\r
+ }\r
+\r
+ for (Index = 0; Index < 16; Index++) {\r
+ SeedBuffer[Index] = Ffv[Index];\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Generate high-quality entropy source through RDRAND.\r
+\r
+ @param[in] Length Size of the buffer, in bytes, to fill with.\r
+ @param[out] Entropy Pointer to the buffer to store the entropy data.\r
+\r
+ @retval EFI_SUCCESS Entropy generation succeeded.\r
+ @retval EFI_NOT_READY Failed to request random data.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+RdRandGenerateEntropy (\r
+ IN UINTN Length,\r
+ OUT UINT8 *Entropy\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN BlockCount;\r
+ UINT8 Seed[16];\r
+ UINT8 *Ptr;\r
+\r
+ Status = EFI_NOT_READY;\r
+ BlockCount = Length / 16;\r
+ Ptr = (UINT8 *)Entropy;\r
+\r
+ //\r
+ // Generate high-quality seed for DRBG Entropy\r
+ //\r
+ while (BlockCount > 0) {\r
+ Status = RdRandGetSeed128 (Seed);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ CopyMem (Ptr, Seed, 16);\r
+\r
+ BlockCount--;\r
+ Ptr = Ptr + 16;\r
+ }\r
+\r
+ //\r
+ // Populate the remained data as request.\r
+ //\r
+ Status = RdRandGetSeed128 (Seed);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ CopyMem (Ptr, Seed, (Length % 16));\r
+\r
+ return Status;\r
+}\r