| /** @file | |
| SVSM TPM communication | |
| Copyright (C) 2024 James.Bottomley@HansenPartnership.com | |
| Copyright (C) 2024 IBM Corporation | |
| Copyright (C) 2024 Red Hat | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include <Uefi.h> | |
| #include <Library/BaseLib.h> | |
| #include <Library/BaseMemoryLib.h> | |
| #include <Library/MemoryAllocationLib.h> | |
| #include <Library/AmdSvsmLib.h> | |
| #include <Library/PcdLib.h> | |
| #include "Tpm2Svsm.h" | |
| /** | |
| Platform commands (MSSIM commands) can be sent through the | |
| SVSM_VTPM_CMD operation. Each command can have its own | |
| request and response structures. | |
| **/ | |
| #define TPM_SEND_COMMAND 8 | |
| #pragma pack(1) | |
| typedef struct _TPM2_SEND_CMD_REQ { | |
| UINT32 Cmd; | |
| UINT8 Locality; | |
| UINT32 BufSize; | |
| UINT8 Buf[]; | |
| } TPM2_SEND_CMD_REQ; | |
| typedef struct _TPM2_SEND_CMD_RESP { | |
| UINT32 Size; | |
| UINT8 Buf[]; | |
| } TPM2_SEND_CMD_RESP; | |
| #pragma pack() | |
| /* Max req/resp buffer size */ | |
| #define TPM_PLATFORM_MAX_BUFFER 4096 | |
| typedef union { | |
| TPM2_SEND_CMD_REQ req; | |
| TPM2_SEND_CMD_RESP resp; | |
| } SVSM_TPM_CMD_BUFFER; | |
| STATIC_ASSERT (sizeof (SVSM_TPM_CMD_BUFFER) <= TPM_PLATFORM_MAX_BUFFER, "SVSM_TPM_CMD_BUFFER too large"); | |
| /** | |
| Probe the SVSM vTPM for TPM_SEND_COMMAND support. The | |
| TPM_SEND_COMMAND platform command can be used to execute a | |
| TPM command and get the result. | |
| @retval TRUE TPM_SEND_COMMAND is supported. | |
| @retval FALSE TPM_SEND_COMMAND is not supported. | |
| **/ | |
| BOOLEAN | |
| Tpm2SvsmQueryTpmSendCmd ( | |
| VOID | |
| ) | |
| { | |
| UINT64 PlatformCmdBitmap; | |
| UINT64 TpmSendMask; | |
| PlatformCmdBitmap = 0; | |
| TpmSendMask = 1 << TPM_SEND_COMMAND; | |
| if (!AmdSvsmVtpmQuery (&PlatformCmdBitmap, NULL)) { | |
| return FALSE; | |
| } | |
| return ((PlatformCmdBitmap & TpmSendMask) == TpmSendMask) ? TRUE : FALSE; | |
| } | |
| /** | |
| Send a TPM command to the SVSM vTPM and return the TPM response. | |
| @param[in] BufferIn It should contain the marshaled | |
| TPM command. | |
| @param[in] SizeIn Size of the TPM command. | |
| @param[out] BufferOut It will contain the marshaled | |
| TPM response. | |
| @param[in, out] SizeOut Size of the BufferOut; it will also | |
| be used to return the size of the | |
| TPM response | |
| @retval EFI_SUCCESS Operation completed successfully. | |
| @retval EFI_INVALID_PARAMETER Buffer not provided. | |
| @retval EFI_BUFFER_TOO_SMALL Response data buffer is too small. | |
| @retval EFI_DEVICE_ERROR Unexpected device behavior. | |
| @retval EFI_OUT_OF_RESOURCES Out of memory when allocating internal buffer. | |
| @retval EFI_UNSUPPORTED Unsupported TPM version | |
| **/ | |
| EFI_STATUS | |
| Tpm2SvsmTpmSendCommand ( | |
| IN UINT8 *BufferIn, | |
| IN UINT32 SizeIn, | |
| OUT UINT8 *BufferOut, | |
| IN OUT UINT32 *SizeOut | |
| ) | |
| { | |
| STATIC SVSM_TPM_CMD_BUFFER *Buffer = NULL; | |
| if ((SizeIn == 0) || !BufferIn || !SizeOut || !BufferOut) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| if (SizeIn > TPM_PLATFORM_MAX_BUFFER - sizeof (TPM2_SEND_CMD_REQ)) { | |
| return EFI_BUFFER_TOO_SMALL; | |
| } | |
| if (Buffer == NULL) { | |
| STATIC_ASSERT (sizeof (UINT64) >= sizeof (UINTN), "Pointer size larger than 64bit"); | |
| Buffer = (SVSM_TPM_CMD_BUFFER *)(UINTN)PcdGet64 (PcdSvsmVTpmBufferPtr); | |
| if (Buffer == NULL) { | |
| Buffer = (SVSM_TPM_CMD_BUFFER *)AllocatePages (EFI_SIZE_TO_PAGES (TPM_PLATFORM_MAX_BUFFER)); | |
| if (Buffer == NULL) { | |
| DEBUG ((DEBUG_ERROR, "Unable to allocate SVSM vTPM buffer: %r", EFI_OUT_OF_RESOURCES)); | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| PcdSet64S (PcdSvsmVTpmBufferPtr, (UINTN)(VOID *)Buffer); | |
| } | |
| } | |
| Buffer->req.Cmd = TPM_SEND_COMMAND; | |
| Buffer->req.Locality = 0; | |
| Buffer->req.BufSize = SizeIn; | |
| CopyMem (Buffer->req.Buf, BufferIn, SizeIn); | |
| if (!AmdSvsmVtpmCmd ((UINT8 *)Buffer)) { | |
| return EFI_DEVICE_ERROR; | |
| } | |
| if (Buffer->resp.Size > TPM_PLATFORM_MAX_BUFFER - sizeof (TPM2_SEND_CMD_RESP)) { | |
| return EFI_DEVICE_ERROR; | |
| } | |
| if (Buffer->resp.Size > *SizeOut) { | |
| return EFI_BUFFER_TOO_SMALL; | |
| } | |
| CopyMem (BufferOut, Buffer->resp.Buf, Buffer->resp.Size); | |
| *SizeOut = Buffer->resp.Size; | |
| return EFI_SUCCESS; | |
| } |