TPM2_GetCapability from UEFI
Tags: uefi, tpm2, edk2, tutorial
Published: 2022-05-22 19:58:03

In my [previous post](https://dev.to/machinehunter/accessing-tpm20-from-uefi-module-sending-tpm2getrandom-140b), I introduced how to submit very easy command TPM2_GetRandom to TPM from UEFI. TPM2_GetCapability is also an easy command, but we need to specify more parameters and those meanings are bit more complex than TPM2_GetRandom. TPM2_GetCapability is a command frequently used to check the current status of your TPM, so it is helpful to have one program already implemented.

## parameters of TPM2_GetCapability
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1cn5d0ydypal3hkc63sx.png)
Put simply, we specify what kind of information we want in *capability*. and request the result in the range of [*property*:*property+propertyCount*].

The types of *capability* we can specify is listed in the command desription.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/egr1gyx1frqzh5eaipy9.png)
In this post, **we will use TPM2_GetCapability to list out supported commands by specifying TPM_CAP_COMMANDS as a *capability* parameter**.


## Implementation
The whole code looks like this.
```c
#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;
}
```

### command parameter description
Again, we have to send data in BigEndian to TPM so there are lots of `SwapBytes`. Macros like TPM_CAP_COMMANDS and TPM_CAP_FIRST are searched in the Tpm20.h which is located in `MdePkg/Include/IndustryStandard/Tpm20.h`. Notice we're requesting MAX_CAP_CC as *propertyCount* parameter, which we can guess *moreData* parameter in the response will be 0.

### response description
Parsing response is the main part of this article.
We can find out `typedef BYTE TPMI_YES_NO;` in Tpm20.h and since it says "YES_NO", we can easily tell *moreData* takes 1 or 0 as a value. However, *capabilityData* parameter which has a type TPMS_CAPABILITY_DATA is more complex. By looking it up on Tpm20.h, we can find out TPMS_CAPABILITY_DATA structure is as follows.

```
* [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
    * ...
```

#### Name Prefix Convention
Looking at TPM macros above, we notice there are some convention in the prefix of its name. Name Prefix Convention is defined in TCG spec Part2 4.16. Here are the part of it.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/avogo7nhtjbjkybxutes.png)
The prefix you must be aware of is **TPM2B_** prefix. Structures with this prefix vary in size depending on the data they hold. There aren't any in TPMS_CAPABILITY_DATA, but if this structure is included, you can't just define response structure and access via that structure just like what I'm doing in the above code. This will be described afterwards when this actually appears. For now, it is enough to know that, you can know what type of data it is from the prefix.

#### bitfield to UINT32
This is not TPM specific thing, but we often see bitfield (the one with TPMA_ prefix) in TPM structure. Since TPM treats data in big endians, we have to swap it, but SwapBytes32 is for UINT32 and not for UINT32 bitfields. Therefore, we need to first convert it to UINT32. In order to convert bitfield to UINT32, you can define union as follows.
```c
union {
  UINT32 tpma_cc_uint32;
  TPMA_CC buf;
} castbuf;
castbuf.buf = /*TPMA_CC object*/;
Print(L"%d", SwapBytes32(castbuf.tpma_cc_uint32));
```


## Running the code
You will get this kind of output if it succeeded. If you get errors, you can check my previous posts on how to evaluate them.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n94486lp08x672wje76u.jpg)
`0x240012A` is `00 0 0 001 0 0 1 000000 0000000100101010` so TPMA_CC values for TPM2_NV_DefineSpace are as follows.
* commandIndex: 0x12A
* nv: 1
* extensive: 0
* flushed: 0
* cHandles: 1
* rHandle: 0
* V: 0
* Res: 0

## Summary
* TPM2_GetCapability is a command frequently used to check the current status of TPM
* How to set parameters of TPM2_GetCapability
* Name Prefix Convention of TPM macros
* How to convert bitfields to integer value in order to swap bytes