UEFI의 TPM2_GetCapability

16005 단어 edk2tpm2uefitutorial
내 글에서는 UEFI에서 TPM에 TPM2_GetRandom 명령을 매우 쉽게 제출하는 방법을 소개했습니다. TPM2_GetCapability도 쉬운 명령이지만 더 많은 매개변수를 지정해야 하며 이러한 의미는 TPM2_GetRandom보다 조금 더 복잡합니다. TPM2_GetCapability는 TPM의 현재 상태를 확인하는 데 자주 사용되는 명령이므로 하나의 프로그램이 이미 구현되어 있으면 도움이 됩니다.

TPM2_GetCapability의 매개변수




간단히 말해서, 우리는 Capability에서 원하는 정보의 종류를 지정합니다. [property:property+propertyCount] 범위의 결과를 요청합니다.

지정할 수 있는 기능 유형은 명령 설명에 나열되어 있습니다.

이 게시물에서는 TPM2_GetCapability를 사용하여 TPM_CAP_COMMANDS를 기능 매개 변수로 지정하여 지원되는 명령을 나열합니다.

구현



전체 코드는 다음과 같습니다.

#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Protocol/Tcg2Protocol.h>
#include <IndustryStandard/Tpm20.h>

#pragma pack(1)
    typedef struct {
        TPM2_COMMAND_HEADER Header;
        TPM_CAP capability;
        UINT32 property;
        UINT32 propertyCount;
    } TPM2_GET_CAPABILITY_COMMAND;

    typedef struct {
        TPM2_RESPONSE_HEADER Header;
        TPMI_YES_NO moreData;
        TPMS_CAPABILITY_DATA capabilityData;
    } TPM2_GET_CAPABILITY_RESPONSE;
#pragma pack()

EFI_STATUS EFIAPI UefiMain(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) {
    EFI_TCG2_PROTOCOL *Tcg2Protocol;
    SystemTable->BootServices->LocateProtocol(&gEfiTcg2ProtocolGuid, NULL, (VOID**)&Tcg2Protocol);


    TPM2_GET_CAPABILITY_COMMAND CmdBuffer;
    UINT32 CmdBufferSize;
    TPM2_GET_CAPABILITY_RESPONSE RecvBuffer;
    UINT32 RecvBufferSize;

    // set command parameters
    CmdBuffer.Header.tag         = SwapBytes16(TPM_ST_NO_SESSIONS);
    CmdBuffer.Header.commandCode = SwapBytes32(TPM_CC_GetCapability);
    CmdBuffer.capability         = SwapBytes32(TPM_CAP_COMMANDS);
    CmdBuffer.property           = SwapBytes32(TPM_CAP_FIRST);
    CmdBuffer.propertyCount      = SwapBytes32(MAX_CAP_CC);
    CmdBufferSize = sizeof(CmdBuffer.Header) + sizeof(CmdBuffer.capability) + sizeof(CmdBuffer.property) + sizeof(CmdBuffer.propertyCount);
    CmdBuffer.Header.paramSize = SwapBytes32(CmdBufferSize);


    // send TPM command
    RecvBufferSize = sizeof(RecvBuffer);
    EFI_STATUS stats = Tcg2Protocol->SubmitCommand(Tcg2Protocol, CmdBufferSize, (UINT8*)&CmdBuffer, RecvBufferSize, (UINT8*)&RecvBuffer);
    if(stats==EFI_SUCCESS)
        Print(L"SubmitCommand Success!\r\n");
    else
        Print(L"stats: 0x%x (EFI_DEVICE_ERROR:0x%x, EFI_INVALID_PARAMETER:0x%x, EFI_BUFFER_TOO_SMALL:0x%x)\r\n", stats, EFI_DEVICE_ERROR, EFI_INVALID_PARAMETER, EFI_BUFFER_TOO_SMALL);


    // parse response
    UINT16 res = SwapBytes32(RecvBuffer.Header.responseCode);
    Print(L"ResponseCode is %d\r\n", res);
    Print(L"moreData: %d\r\n", RecvBuffer.moreData);

    UINT32 i;
    UINT32 count = SwapBytes32(RecvBuffer.capabilityData.data.command.count);
    Print(L"--- %d commands supported ---\r\n", count);

    TPMA_CC* tpmacc = (TPMA_CC*)RecvBuffer.capabilityData.data.command.commandAttributes;
    for(i=0; i<count; i++) {
        union {
            UINT32 tpma_cc_uint32;
            TPMA_CC buf;
        } castbuf;
        castbuf.buf = tpmacc[i];
        UINT32 tpmacc32 = SwapBytes32(castbuf.tpma_cc_uint32);

        if((UINT16)tpmacc32 == TPM_CC_NV_DefineSpace) {
            Print(L"TPM2_NV_DefineSpace is supported: %X\r\n", tpmacc32);
        }
    }

    while(1);
    return 0;
}


명령 매개변수 설명



다시 한 번 BigEndian의 데이터를 TPM으로 보내야 하므로 SwapBytes 가 많이 있습니다. TPM_CAP_COMMANDS 및 TPM_CAP_FIRST와 같은 매크로는 MdePkg/Include/IndustryStandard/Tpm20.h에 있는 Tpm20.h에서 검색됩니다. MAX_CAP_CC를 propertyCount 매개변수로 요청하고 있으므로 응답에서 moreData 매개변수가 0이 될 것이라고 추측할 수 있습니다.

응답 설명



구문 분석 응답은 이 문서의 주요 부분입니다.
Tpm20.h에서 typedef BYTE TPMI_YES_NO;를 찾을 수 있으며 "YES_NO"라고 표시되어 있기 때문에 moreData가 값으로 1 또는 0을 취한다는 것을 쉽게 알 수 있습니다. 그러나 TPMS_CAPABILITY_DATA 유형이 있는 capabilityData 매개변수는 더 복잡합니다. Tpm20.h에서 찾아보면 TPMS_CAPABILITY_DATA 구조가 다음과 같다는 것을 알 수 있습니다.

* [struct] TPMS_CAPABILITY_DATA
  * [UINT32] TPM_CAP capability
  * [union] TPMU_CAPABILITIES
    * [struct] TPML_ALG_PROPERTY algorithms;
      * [UINT32] UINT32 count
      * [struct] TPMS_ALG_PROPERTY algProperties[MAX_CAP_ALGS];
        * [UINT16] TPM_ALG_ID alg
        * [UINT32(bitfield)] TPMA_ALGORITHM algProperties
    * ...


이름 접두사 규칙



위의 TPM 매크로를 살펴보면 이름의 접두사에 몇 가지 규칙이 있음을 알 수 있습니다. 이름 접두사 규칙은 TCG 사양 Part2 4.16에 정의되어 있습니다. 여기에 그 일부가 있습니다.

알아야 할 접두사는 TPM2B_ 접두사입니다. 이 접두사가 있는 구조는 보유하고 있는 데이터에 따라 크기가 다릅니다. TPMS_CAPABILITY_DATA에는 아무것도 없지만, 이 구조가 포함되어 있으면 위의 코드에서 내가 하고 있는 것처럼 응답 구조를 정의하고 그 구조를 통해 액세스할 수 없습니다. 이것은 나중에 이것이 실제로 나타날 때 설명될 것이다. 지금은 접두사에서 어떤 유형의 데이터인지 알 수 있다는 것만 알면 충분합니다.

비트 필드를 UINT32로



이것은 TPM과 관련된 것은 아니지만 TPM 구조에서 비트 필드(TPMA_ 접두사가 있는 것)를 자주 볼 수 있습니다. TPM은 빅 엔디안으로 데이터를 처리하기 때문에 교환해야 하지만 SwapBytes32는 UINT32용이고 UINT32 비트필드용은 아닙니다. 따라서 먼저 UINT32로 변환해야 합니다. bitfield를 UINT32로 변환하기 위해서는 다음과 같이 union을 정의하면 됩니다.

union {
  UINT32 tpma_cc_uint32;
  TPMA_CC buf;
} castbuf;
castbuf.buf = /*TPMA_CC object*/;
Print(L"%d", SwapBytes32(castbuf.tpma_cc_uint32));


코드 실행



성공하면 이런 종류의 출력이 표시됩니다. 오류가 발생하면 오류를 평가하는 방법에 대한 이전 게시물을 확인할 수 있습니다.
0x240012A00 0 0 001 0 0 1 000000 0000000100101010이므로 TPM2_NV_DefineSpace에 대한 TPMA_CC 값은 다음과 같습니다.
  • commandIndex: 0x12A
  • nv: 1
  • 광범위한: 0
  • 플러시: 0
  • c핸들: 1
  • r핸들: 0
  • V: 0
  • 해상도: 0

  • 요약


  • TPM2_GetCapability는 TPM의 현재 상태를 확인하는 데 자주 사용되는 명령입니다
  • .
  • TPM2_GetCapability의 매개 변수를 설정하는 방법
  • TPM 매크로의 이름 접두사 규칙
  • 바이트를 교환하기 위해 비트 필드를 정수 값으로 변환하는 방법
  • 좋은 웹페이지 즐겨찾기