--- /dev/null
+/** @file\r
+ Arm Firmware TRNG interface library.\r
+\r
+ Copyright (c) 2021 - 2022, Arm Limited. All rights reserved.<BR>\r
+\r
+ SPDX-License-Identifier: BSD-2-Clause-Patent\r
+\r
+ @par Reference(s):\r
+ - [1] Arm True Random Number Generator Firmware, Interface 1.0,\r
+ Platform Design Document.\r
+ (https://developer.arm.com/documentation/den0098/latest/)\r
+ - [2] NIST Special Publication 800-90B, Recommendation for the Entropy\r
+ Sources Used for Random Bit Generation.\r
+ (https://csrc.nist.gov/publications/detail/sp/800-90b/final)\r
+\r
+ @par Glossary:\r
+ - TRNG - True Random Number Generator\r
+ - FID - Function ID\r
+**/\r
+\r
+#include <Base.h>\r
+#include <Library/ArmLib.h>\r
+#include <Library/ArmMonitorLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/DebugLib.h>\r
+\r
+#include "ArmTrngDefs.h"\r
+\r
+/** Convert TRNG status codes to RETURN status codes.\r
+\r
+ @param [in] TrngStatus TRNG status code.\r
+\r
+ @retval RETURN_SUCCESS Success.\r
+ @retval RETURN_UNSUPPORTED Function not implemented or\r
+ negative return code.\r
+ @retval RETURN_INVALID_PARAMETER A parameter is invalid.\r
+ @retval RETURN_NOT_READY No Entropy available.\r
+**/\r
+STATIC\r
+RETURN_STATUS\r
+TrngStatusToReturnStatus (\r
+ IN INT32 TrngStatus\r
+ )\r
+{\r
+ switch (TrngStatus) {\r
+ case TRNG_STATUS_NOT_SUPPORTED:\r
+ return RETURN_UNSUPPORTED;\r
+\r
+ case TRNG_STATUS_INVALID_PARAMETER:\r
+ return RETURN_INVALID_PARAMETER;\r
+\r
+ case TRNG_STATUS_NO_ENTROPY:\r
+ return RETURN_NOT_READY;\r
+\r
+ case TRNG_STATUS_SUCCESS:\r
+ return RETURN_SUCCESS;\r
+\r
+ default:\r
+ if (TrngStatus < 0) {\r
+ return RETURN_UNSUPPORTED;\r
+ }\r
+\r
+ return RETURN_SUCCESS;\r
+ }\r
+}\r
+\r
+/** Get the version of the Arm TRNG backend.\r
+\r
+ A TRNG may be implemented by the system firmware, in which case this\r
+ function shall return the version of the Arm TRNG backend.\r
+ The implementation must return NOT_SUPPORTED if a Back end is not present.\r
+\r
+ @param [out] MajorRevision Major revision.\r
+ @param [out] MinorRevision Minor revision.\r
+\r
+ @retval RETURN_SUCCESS The function completed successfully.\r
+ @retval RETURN_INVALID_PARAMETER Invalid parameter.\r
+ @retval RETURN_UNSUPPORTED Backend not present.\r
+**/\r
+RETURN_STATUS\r
+EFIAPI\r
+GetArmTrngVersion (\r
+ OUT UINT16 *MajorRevision,\r
+ OUT UINT16 *MinorRevision\r
+ )\r
+{\r
+ RETURN_STATUS Status;\r
+ ARM_MONITOR_ARGS Parameters;\r
+ INT32 Revision;\r
+\r
+ if ((MajorRevision == NULL) || (MinorRevision == NULL)) {\r
+ return RETURN_INVALID_PARAMETER;\r
+ }\r
+\r
+ ZeroMem (&Parameters, sizeof (Parameters));\r
+\r
+ Parameters.Arg0 = ARM_SMC_ID_TRNG_VERSION;\r
+ ArmMonitorCall (&Parameters);\r
+\r
+ Revision = (INT32)Parameters.Arg0;\r
+ Status = TrngStatusToReturnStatus (Revision);\r
+ if (RETURN_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ *MinorRevision = (Revision & TRNG_REV_MINOR_MASK);\r
+ *MajorRevision = ((Revision >> TRNG_REV_MAJOR_SHIFT) & TRNG_REV_MAJOR_MASK);\r
+ return RETURN_SUCCESS;\r
+}\r
+\r
+/** Get the features supported by the Arm TRNG backend.\r
+\r
+ The caller can determine if functions defined in the Arm TRNG ABI are\r
+ present in the ABI implementation.\r
+\r
+ @param [in] FunctionId Function Id.\r
+ @param [out] Capability Function specific capability if present.\r
+\r
+ @retval RETURN_SUCCESS The function completed successfully.\r
+ @retval RETURN_INVALID_PARAMETER Invalid parameter.\r
+ @retval RETURN_UNSUPPORTED Function not implemented.\r
+**/\r
+STATIC\r
+RETURN_STATUS\r
+EFIAPI\r
+GetArmTrngFeatures (\r
+ IN CONST UINT32 FunctionId,\r
+ OUT UINT32 *Capability OPTIONAL\r
+ )\r
+{\r
+ ARM_MONITOR_ARGS Parameters;\r
+ RETURN_STATUS Status;\r
+\r
+ ZeroMem (&Parameters, sizeof (Parameters));\r
+\r
+ Parameters.Arg0 = ARM_SMC_ID_TRNG_FEATURES;\r
+ Parameters.Arg1 = FunctionId;\r
+ ArmMonitorCall (&Parameters);\r
+\r
+ Status = TrngStatusToReturnStatus (Parameters.Arg0);\r
+ if (RETURN_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ if (Capability != NULL) {\r
+ *Capability = (UINT32)Parameters.Arg0;\r
+ }\r
+\r
+ return RETURN_SUCCESS;\r
+}\r
+\r
+/** Get the UUID of the Arm TRNG backend.\r
+\r
+ A TRNG may be implemented by the system firmware, in which case this\r
+ function shall return the UUID of the TRNG backend.\r
+ Returning the Arm TRNG UUID is optional and if not implemented,\r
+ RETURN_UNSUPPORTED shall be returned.\r
+\r
+ Note: The caller must not rely on the returned UUID as a trustworthy Arm TRNG\r
+ Back end identity\r
+\r
+ @param [out] Guid UUID of the Arm TRNG backend.\r
+\r
+ @retval RETURN_SUCCESS The function completed successfully.\r
+ @retval RETURN_INVALID_PARAMETER Invalid parameter.\r
+ @retval RETURN_UNSUPPORTED Function not implemented.\r
+**/\r
+RETURN_STATUS\r
+EFIAPI\r
+GetArmTrngUuid (\r
+ OUT GUID *Guid\r
+ )\r
+{\r
+ ARM_MONITOR_ARGS Parameters;\r
+\r
+ if (Guid == NULL) {\r
+ return RETURN_INVALID_PARAMETER;\r
+ }\r
+\r
+ ZeroMem (&Parameters, sizeof (Parameters));\r
+\r
+ Parameters.Arg0 = ARM_SMC_ID_TRNG_GET_UUID;\r
+ ArmMonitorCall (&Parameters);\r
+\r
+ // Only invalid value is TRNG_STATUS_NOT_SUPPORTED (-1).\r
+ if ((INT32)Parameters.Arg0 == TRNG_STATUS_NOT_SUPPORTED) {\r
+ return TrngStatusToReturnStatus ((INT32)Parameters.Arg0);\r
+ }\r
+\r
+ Guid->Data1 = (Parameters.Arg0 & MAX_UINT32);\r
+ Guid->Data2 = (Parameters.Arg1 & MAX_UINT16);\r
+ Guid->Data3 = ((Parameters.Arg1 >> 16) & MAX_UINT16);\r
+\r
+ Guid->Data4[0] = (Parameters.Arg2 & MAX_UINT8);\r
+ Guid->Data4[1] = ((Parameters.Arg2 >> 8) & MAX_UINT8);\r
+ Guid->Data4[2] = ((Parameters.Arg2 >> 16) & MAX_UINT8);\r
+ Guid->Data4[3] = ((Parameters.Arg2 >> 24) & MAX_UINT8);\r
+\r
+ Guid->Data4[4] = (Parameters.Arg3 & MAX_UINT8);\r
+ Guid->Data4[5] = ((Parameters.Arg3 >> 8) & MAX_UINT8);\r
+ Guid->Data4[6] = ((Parameters.Arg3 >> 16) & MAX_UINT8);\r
+ Guid->Data4[7] = ((Parameters.Arg3 >> 24) & MAX_UINT8);\r
+\r
+ DEBUG ((DEBUG_INFO, "FW-TRNG: UUID %g\n", Guid));\r
+\r
+ return RETURN_SUCCESS;\r
+}\r
+\r
+/** Returns maximum number of entropy bits that can be returned in a single\r
+ call.\r
+\r
+ @return Returns the maximum number of Entropy bits that can be returned\r
+ in a single call to GetArmTrngEntropy().\r
+**/\r
+UINTN\r
+EFIAPI\r
+GetArmTrngMaxSupportedEntropyBits (\r
+ VOID\r
+ )\r
+{\r
+ return MAX_ENTROPY_BITS;\r
+}\r
+\r
+/** Returns N bits of conditioned entropy.\r
+\r
+ See [2] Section 2.3.1 GetEntropy: An Interface to the Entropy Source\r
+ GetEntropy\r
+ Input:\r
+ bits_of_entropy: the requested amount of entropy\r
+ Output:\r
+ entropy_bitstring: The string that provides the requested entropy.\r
+ status: A Boolean value that is TRUE if the request has been satisfied,\r
+ and is FALSE otherwise.\r
+\r
+ @param [in] EntropyBits Number of entropy bits requested.\r
+ @param [in] BufferSize Size of the Buffer in bytes.\r
+ @param [out] Buffer Buffer to return the entropy bits.\r
+\r
+ @retval RETURN_SUCCESS The function completed successfully.\r
+ @retval RETURN_INVALID_PARAMETER Invalid parameter.\r
+ @retval RETURN_UNSUPPORTED Function not implemented.\r
+ @retval RETURN_BAD_BUFFER_SIZE Buffer size is too small.\r
+ @retval RETURN_NOT_READY No Entropy available.\r
+**/\r
+RETURN_STATUS\r
+EFIAPI\r
+GetArmTrngEntropy (\r
+ IN UINTN EntropyBits,\r
+ IN UINTN BufferSize,\r
+ OUT UINT8 *Buffer\r
+ )\r
+{\r
+ RETURN_STATUS Status;\r
+ ARM_MONITOR_ARGS Parameters;\r
+ UINTN EntropyBytes;\r
+ UINTN LastValidBits;\r
+ UINTN BytesToClear;\r
+ UINTN EntropyData[3];\r
+\r
+ if ((EntropyBits == 0) ||\r
+ (EntropyBits > MAX_ENTROPY_BITS) ||\r
+ (Buffer == NULL))\r
+ {\r
+ return RETURN_INVALID_PARAMETER;\r
+ }\r
+\r
+ EntropyBytes = (EntropyBits + 7) >> 3;\r
+ if (EntropyBytes > BufferSize) {\r
+ return RETURN_BAD_BUFFER_SIZE;\r
+ }\r
+\r
+ ZeroMem (Buffer, BufferSize);\r
+ ZeroMem (&Parameters, sizeof (Parameters));\r
+\r
+ Parameters.Arg0 = ARM_SMC_ID_TRNG_RND;\r
+ Parameters.Arg1 = EntropyBits;\r
+ ArmMonitorCall (&Parameters);\r
+\r
+ Status = TrngStatusToReturnStatus ((INT32)Parameters.Arg0);\r
+ if (RETURN_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ // The entropy data is returned in the Parameters.Arg<3..1>\r
+ // With the lower order bytes in Parameters.Arg3 and the higher\r
+ // order bytes being stored in Parameters.Arg1.\r
+ EntropyData[0] = Parameters.Arg3;\r
+ EntropyData[1] = Parameters.Arg2;\r
+ EntropyData[2] = Parameters.Arg1;\r
+\r
+ CopyMem (Buffer, EntropyData, EntropyBytes);\r
+\r
+ // Mask off any unused top bytes, in accordance with specification.\r
+ BytesToClear = BufferSize - EntropyBytes;\r
+ if (BytesToClear != 0) {\r
+ ZeroMem (&Buffer[EntropyBytes], BytesToClear);\r
+ }\r
+\r
+ // Clear the unused MSB bits of the last byte.\r
+ LastValidBits = EntropyBits & 0x7;\r
+ if (LastValidBits != 0) {\r
+ Buffer[EntropyBytes - 1] &= (0xFF >> (8 - LastValidBits));\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/** The constructor checks that the FW-TRNG interface is supported\r
+ by the host firmware.\r
+\r
+ It will ASSERT() if FW-TRNG is not supported.\r
+ It will always return RETURN_SUCCESS.\r
+\r
+ @retval RETURN_SUCCESS The constructor always returns RETURN_SUCCESS.\r
+**/\r
+RETURN_STATUS\r
+EFIAPI\r
+ArmTrngLibConstructor (\r
+ VOID\r
+ )\r
+{\r
+ ARM_MONITOR_ARGS Parameters;\r
+ RETURN_STATUS Status;\r
+ UINT16 MajorRev;\r
+ UINT16 MinorRev;\r
+ GUID Guid;\r
+\r
+ ZeroMem (&Parameters, sizeof (Parameters));\r
+\r
+ Parameters.Arg0 = SMCCC_VERSION;\r
+ ArmMonitorCall (&Parameters);\r
+ Status = TrngStatusToReturnStatus ((INT32)Parameters.Arg0);\r
+ if (RETURN_ERROR (Status)) {\r
+ ASSERT_RETURN_ERROR (Status);\r
+ goto ErrorHandler;\r
+ }\r
+\r
+ // Cf [1] s2.1.3 'Caller responsibilities',\r
+ // SMCCC version must be greater or equal than 1.1\r
+ if ((INT32)Parameters.Arg0 < 0x10001) {\r
+ ASSERT_RETURN_ERROR (RETURN_UNSUPPORTED);\r
+ goto ErrorHandler;\r
+ }\r
+\r
+ Status = GetArmTrngVersion (&MajorRev, &MinorRev);\r
+ if (RETURN_ERROR (Status)) {\r
+ ASSERT_RETURN_ERROR (Status);\r
+ goto ErrorHandler;\r
+ }\r
+\r
+ // Check that the required features are present.\r
+ Status = GetArmTrngFeatures (ARM_SMC_ID_TRNG_RND, NULL);\r
+ if (RETURN_ERROR (Status)) {\r
+ ASSERT_RETURN_ERROR (Status);\r
+ goto ErrorHandler;\r
+ }\r
+\r
+ // Check if TRNG UUID is supported and if so trace the GUID.\r
+ Status = GetArmTrngFeatures (ARM_SMC_ID_TRNG_GET_UUID, NULL);\r
+ if (RETURN_ERROR (Status)) {\r
+ ASSERT_RETURN_ERROR (Status);\r
+ goto ErrorHandler;\r
+ }\r
+\r
+ DEBUG_CODE_BEGIN ();\r
+\r
+ Status = GetArmTrngUuid (&Guid);\r
+ if (RETURN_ERROR (Status)) {\r
+ ASSERT_RETURN_ERROR (Status);\r
+ goto ErrorHandler;\r
+ }\r
+\r
+ DEBUG ((\r
+ DEBUG_INFO,\r
+ "FW-TRNG: Version %d.%d, GUID {%g}\n",\r
+ MajorRev,\r
+ MinorRev,\r
+ Guid\r
+ ));\r
+\r
+ DEBUG_CODE_END ();\r
+\r
+ return RETURN_SUCCESS;\r
+\r
+ErrorHandler:\r
+ DEBUG ((DEBUG_ERROR, "ArmTrngLib could not be correctly initialized.\n"));\r
+ return RETURN_SUCCESS;\r
+}\r