]> git.proxmox.com Git - mirror_edk2.git/blobdiff - ArmPkg/Library/ArmTrngLib/ArmTrngLib.c
ArmPkg/ArmTrngLib: Add Arm TRNG library
[mirror_edk2.git] / ArmPkg / Library / ArmTrngLib / ArmTrngLib.c
diff --git a/ArmPkg/Library/ArmTrngLib/ArmTrngLib.c b/ArmPkg/Library/ArmTrngLib/ArmTrngLib.c
new file mode 100644 (file)
index 0000000..b974a94
--- /dev/null
@@ -0,0 +1,388 @@
+/** @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