TPM on QEMU Arm Virt
This document describes the TPM 2.0 architecture for the QEMU Arm Virt platform. Arm Virt uses a dual-CRB design with an FF-A (Firmware Framework for Arm A-Profile) mediated communication path between the normal world and the secure world.
Table of Contents
- Requirements
- Build Configuration
- Platform Memory Layout
- Architecture Overview
- Secure Partitions
- CRB Regions
- FF-A Communication Protocol
- Hash Library Architecture
- Physical Presence Interface
- ACPI Integration
- swtpm Setup
- Communication Flow
- PCDs Reference
Requirements
| Requirement | Notes |
|---|---|
| Host OS | Linux (native) or WSL on Windows. Native Windows is not supported. |
| swtpm | TPM 2.0 emulator. Install via your distro's package manager (e.g. apt install swtpm swtpm-tools). |
| QEMU | Built with tpm-tis-device support (standard upstream QEMU includes this). |
| Build host | Same Linux/WSL environment used to run stuart_build and launch QEMU. |
See swtpm Setup for the full setup commands.
Build Configuration
The TPM is disabled by default. To enable it, set BLD_*_TPM2_ENABLE=TRUE on the command line or in a
BuildConfig.conf file placed at the root level of the repo:
stuart_build -c Platforms/QemuArmVirtPkg/PlatformBuild.py --FlashRom BLD_*_TPM2_ENABLE=TRUE
The following defines control TPM behavior in QemuArmVirtPkg.dsc:
| Define | Default | Purpose |
|---|---|---|
TPM2_ENABLE | FALSE | Master switch. Guards all TPM drivers, libraries, and PCDs. |
TPM2_CONFIG_ENABLE | FALSE | Enables Tcg2ConfigDxe HII configuration UI. |
When TPM2_ENABLE=TRUE, the build additionally passes -DTPM2_ENABLE to the C compiler
via build options, allowing C code to use #ifdef TPM2_ENABLE guards.
Platform Memory Layout
The Arm Virt platform defines two distinct TPM memory regions:
| Region | Address | Size | Visibility |
|---|---|---|---|
| Internal CRB | 0x40200000 | 0x10 pages | Normal world + Secure world |
| External CRB | 0x0c000000 | 0x10 pages | Secure world only |
The Internal CRB address is published via PCDs:
PcdTpmBaseAddress=0x40200000PcdTpmMaxAddress=0x40204FFF(5 localities × 0x1000)
The Internal CRB is marked as EfiACPIMemoryNVS via a HOB in ArmPlatformLibQemu.c
so the OS can locate it through the ACPI TPM2 table.
Architecture Overview
┌──────────────────────────────────────────────────────────────────────────────────┐
│ UEFI Firmware (AARCH64) │
│ │
│ ┌─── SEC Phase ──────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Tpm2StartupLib │ │
│ │ │ 1. Tpm2RequestUseTpm() │ │
│ │ │ 2. Tpm2Startup(TPM_SU_CLEAR) │ │
│ └────┼───────────────────────────────────────────────────────────────────────┘ │
│ ▼ │
│ ┌─── DXE Phase ──────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ HashLibBaseCryptoRouterDxe + HashInstanceLib* │ │
│ │ │ 1. Constructors register each enabled hash algorithm │ │
│ │ │ 2. Filtered by PcdTpm2HashMask → PcdTcg2HashAlgorithmBitmap │ │
│ │ ▼ │ │
│ │ Tcg2Dxe │ │
│ │ │ 1. Verify PcdTpmInstanceGuid is TPM 2.0 │ │
│ │ │ 2. Verify no TpmErrorHob is present │ │
│ │ │ 3. Tpm2RequestUseTpm() │ │
│ │ │ 4. Query TPM capabilities │ │
│ │ │ ├── Manufacturer │ │
│ │ │ ├── Firmware version │ │
│ │ │ └── Max cmd/resp size │ │
│ │ │ 5. Get supported/active PCR banks filtered by → HashAlgorithmBitmap │ │
│ │ │ 6. Decide SupportedEventLogs (TCG_1_2 only if SHA1 active) │ │
│ │ │ 7. SetupEventLog │ │
│ │ │ ├── Allocate log area(s) │ │
│ │ │ └── Acquire and log pre-DXE HOB(s) │ │
│ │ │ 8. Register events │ │
│ │ │ ├── ReadyToBoot │ │
│ │ │ ├── ExitBootServices │ │
│ │ │ └── ExitBootServices Failed │ │
│ │ │ 9. Register protocol notifies │ │
│ │ │ ├── VariableWriteArch (SecureBoot) │ │
│ │ │ └── ResetNotification (TPM shutdown) │ │
│ │ │ 10. Install Tcg2Protocol │ │
│ │ ▼ │ │
│ │ Tcg2AcpiFfa │ │
│ │ │ 1. Publish TPM2 ACPI table │ │
│ │ │ 2. Publish SSDT with TPM0 device node │ │
│ └────┼───────────────────────────────────────────────────────────────────────┘ │
│ ▼ │
│ ┌─── BDS Phase ──────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ DeviceBootManagerAfterConsole │ │
│ │ │ 1. Tcg2PhysicalPresenceLibProcessRequest (NULL) │ │
│ │ │ 2. Process any pending PP request before shell launch │ │
│ │ │ 3. Create TCG2_PHYSICAL_PRESENCE_VARIABLE if it doesn't exist │ │
│ └────┼───────────────────────────────────────────────────────────────────────┘ │
│ ▼ │
│ ┌─── UEFI Shell ─────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ UEFI Shell / OS / TpmShellApp │ │
│ │ │ 1. gBS->LocateProtocol(&gEfiTcg2ProtocolGuid) │ │
│ │ │ 2. Tcg2Protocol->GetCapability / SetActivePcrBanks / etc. │ │
│ └────┼───────────────────────────────────────────────────────────────────────┘ │
│ ▼ │
│ Tpm2DeviceLibFfa ─ writes to Internal CRB @ 0x40200000 │
│ │ then sends FF-A DirectReq2 to the TpmService │
│ │ │
├───────┼──────────────────────────────────────────────────────────────────────────┤
│ EL3 (SPMC / TF-A) ─ routes FF-A message to partition 0x8002 │
├───────┼──────────────────────────────────────────────────────────────────────────┤
│ ▼ │
│ SECURE WORLD (SEL1) │
│ │
│ MSSP (MsSecurePartition, id=0x8002) │
│ │ │
│ ▼ │
│ TpmServiceLib ─ State machine (IDLE → READY → COMPLETE → IDLE) │
│ │ │
│ ▼ │
│ TpmServiceStateTranslationLib ─ Translates CRB style communications │
│ │ to the style supported by the TPM │
│ │ i.e. FIFO for QEMU Arm Virt │
│ │ Library Responsibilities: │
│ │ 1. Copy command from Internal CRB → local buffer │
│ │ 2. Write command to External CRB @ 0x0c000000 │
│ │ 3. Trigger execution on external TPM through the CRB MMIO │
│ │ 4. Read response from External CRB │
│ │ 5. Copy response back to Internal CRB │
│ │ │
├───────┼──────────────────────────────────────────────────────────────────────────┤
│ ▼ │
│ QEMU TPM device (MMIO @ 0x0c000000) │
│ │ │
│ ▼ │
│ Unix socket ─ swtpm process (--tpm2) │
└──────────────────────────────────────────────────────────────────────────────────┘
Secure Partitions
Two FF-A Secure Partitions are involved in TPM operations.
MSSP — Microsoft Secure Services Partition (id=0x8002)
Configured in Platforms/QemuArmVirtPkg/fdts/qemu_virt_mssp_rust_config.dts:
| Property | Value |
|---|---|
| Partition ID | 0x8002 |
| Exception Level | SEL1 |
| Execution State | AARCH64 |
| Load Address | 0x0e700000 |
| Image Size | 4 MiB |
| Boot Order | 2 |
The MSSP hosts the TPM service and is granted access to both CRB regions:
device-regions {
internal_tpm_crb {
base-address = <0x40200000>;
pages-count = <0x10>;
attributes = <SECURE_RW>;
};
external_tpm_crb {
base-address = <0x0c000000>;
pages-count = <0x10>;
attributes = <SECURE_RW>;
};
};
The MSSP publishes three service UUIDs. The TPM service UUID is:
17b862a4-1806-4faf-86b3-089a58353861
Key libraries running inside the MSSP:
- TpmServiceLib — handles incoming FF-A messages, implements the CRB state machine (IDLE → cmdReady → READY → start → COMPLETE → goIdle → IDLE). Note that this service is based on the CRB over FF-A specification released by ARM. See: TPM Service Command Response Buffer Interface Over FF-A
- TpmServiceStateTranslationLib — translates between the Internal CRB (CRB interface) and the External CRB (which may be CRB or FIFO depending on QEMU configuration). On QEMU Arm Virt the external interface is FIFO.
StMM — Standalone MM Partition (id=0x8001)
Configured in Platforms/QemuArmVirtPkg/fdts/qemu_virt_stmm_config.dts:
| Property | Value |
|---|---|
| Partition ID | 0x8001 |
| Exception Level | SEL0 |
| Execution State | AARCH64 |
| Load Address | 0x0e400000 |
| Image Size | 3 MiB |
| Boot Order | 0 |
The StMM partition hosts secure variable storage (FTW, VariableRuntimeDxe), and the
Tcg2StandaloneMmArm driver which processes Physical Presence Interface commands from
the normal world for NV variable access.
CRB Regions
CRB Register Layout (PTP CRB Interface)
Each locality occupies 0x1000 bytes. The CRB register layout is defined by the TCG
PC Client Platform TPM Profile (PTP) specification (and mirrored in TpmPtp.h):
- TCG PC Client Platform TPM Profile (PTP) Specification —
Section 6 "Command Response Buffer Interface" describes
LocalityState,LocalityControl,InterfaceId,CrbControlRequest,CrbControlStart,CrbControlCommand*/CrbControlResponse*, and the sharedCrbDataBuffer.
Internal CRB (0x40200000)
This is the CRB visible to normal-world firmware (DXE drivers and UEFI applications).
Tpm2DeviceLibFfa writes TPM commands into this CRB's data buffer and reads responses
from it. The Internal CRB uses the standard CRB register interface. It is the TPM
service's responsibility to set up and maintain this region. The goal is for this region
to mimic a normal MMIO CRB region, with the added caveat that an FF-A message must be sent
for any register modification to take effect.
The normal-world code performs MMIO writes to the CRB control registers (cmdReady, Start, goIdle) and then sends FF-A messages to notify the secure partition. The secure partition reads the command data from the Internal CRB, proxies it to the External CRB, and writes the response back.
External CRB (0x0c000000)
This is the QEMU-emulated TPM device MMIO region. It is only accessible from the secure
world i.e. the TPM Service within the MSSP secure partition. On QEMU Arm Virt, this region
presents a FIFO interface (not CRB), which the TpmServiceStateTranslationLib handles by
detecting the interface type at initialization and using the appropriate FIFO
command/response protocol (burst-count reads, data register writes).
The external CRB connects to the swtpm process through QEMU's chardev/tpmdev
infrastructure:
QEMU args: -chardev socket,id=chrtpm,path={BUILD_OUTPUT_BASE}/swtpm-sock
-tpmdev emulator,id=tpm0,chardev=chrtpm
FF-A Communication Protocol
All TPM commands from normal world to secure world use FF-A Direct Request/Response messaging (FFA_MSG_SEND_DIRECT_REQ2 / FFA_MSG_SEND_DIRECT_RESP2) to/from the TPM Service.
Service Discovery
On first use, Tpm2DeviceLibFfa discovers the TPM service partition:
ArmFfaLibGetPartitionInfo(&gTpm2ServiceFfaGuid, &TpmPartInfo);
This queries the SPMC (EL3) for the partition hosting UUID 17b862a4-1806-4faf-86b3-089a58353861,
which returns partition ID 0x8002. This information can be found in the manifest of the secure
partition.
Function IDs
| ID | Name | Direction | Supported |
|---|---|---|---|
0x0f000001 | TPM2_FFA_GET_INTERFACE_VERSION | NW → SW | YES |
0x0f000101 | TPM2_FFA_GET_FEATURE_INFO | NW → SW | NO |
0x0f000201 | TPM2_FFA_START | NW → SW | YES |
0x0f000301 | TPM2_FFA_REGISTER_FOR_NOTIFICATION | NW → SW | NO |
0x0f000401 | TPM2_FFA_UNREGISTER_FROM_NOTIFICATION | NW → SW | NO |
0x0f000501 | TPM2_FFA_FINISH_NOTIFIED | NW → SW | NO |
0x1f000001 | TPM2_FFA_MANAGE_LOCALITY | TF-A → SW only | YES |
The TPM2_FFA_START function carries a qualifier in Arg1:
| Qualifier | Value | Purpose |
|---|---|---|
TPM2_FFA_START_FUNC_QUALIFIER_COMMAND | 0x0 | Execute a CRB state transition (cmdReady, start, or goIdle) |
TPM2_FFA_START_FUNC_QUALIFIER_LOCALITY | 0x1 | Request or relinquish locality access |
The TPM2_FFA_MANAGE_LOCALITY function carries a qualifier in Arg1:
| Qualifier | Value | Purpose |
|---|---|---|
TPM2_FFA_MANAGE_LOCALITY_OPEN | 0x0 | Allows access to a locality |
TPM2_FFA_MANAGE_LOCALITY_CLOSE | 0x1 | Prevents access to a locality |
Note that for both TPM2_FFA_START and TPM2_FFA_MANAGE_LOCALITY Arg2 specifies
the locality to take action upon.
FF-A Message Sequence (Per TPM Command)
A locality must first be requested before any commands can be sent to
the TPM via that locality's CRB region. The active locality must be
relinquished before another locality is requested. The entity currently
engaging with the TPM is responsible for relinquishing the active locality
when it is no longer in use. If the locality being requested is CLOSED, a
DENIED error is returned. If the locality being relinquished is not the
current active locality, a DENIED error is returned.
A single TPM command requires multiple FF-A round trips:
sequenceDiagram
participant NW as Normal World
participant SW as Secure World (MSSP)
Note over NW: Write LocalityControl = request access
NW->>SW: FFA DirectReq2(START, LOCALITY)
Note over SW: LocalityX becomes active
SW-->>NW: FFA DirectResp2(SUCCESS)
Note over NW: Write CrbControlRequest = cmdReady
NW->>SW: FFA DirectReq2(START, COMMAND)
Note over SW: State: IDLE → READY<br/>(prepare external TPM)
SW-->>NW: FFA DirectResp2(SUCCESS)
Note over NW: Write command to CrbDataBuffer<br/>Write CrbControlStart = 1
NW->>SW: FFA DirectReq2(START, COMMAND)
Note over SW: State: READY → COMPLETE<br/>• Copy cmd from Internal CRB<br/>• Write cmd to External CRB<br/>• Trigger external TPM<br/>• Read response from External CRB<br/>• Copy response to Internal CRB
SW-->>NW: FFA DirectResp2(SUCCESS)
Note over NW: Read response from CrbDataBuffer<br/>Write CrbControlRequest = goIdle
NW->>SW: FFA DirectReq2(START, COMMAND)
Note over SW: State: COMPLETE → IDLE<br/>(idle external TPM)
SW-->>NW: FFA DirectResp2(SUCCESS)
rect rgba(200, 200, 200, 0.2)
Note over NW,SW: OPTIONAL
Note over NW: Write LocalityControl = relinquish access
NW->>SW: FFA DirectReq2(START, LOCALITY)
Note over SW: LocalityX is relinquished
SW-->>NW: FFA DirectResp2(SUCCESS)
end
If the secure partition is preempted by a non-secure interrupt during processing, the FF-A
call returns EFI_INTERRUPT_PENDING. The normal-world code handles this by calling
ArmFfaLibRun() in a loop until the operation completes. Note that this can only happen if
ns-interrupts-action in the secure partition's manifest is set to 0x02, otherwise, the
non-secure interrupt is queued and the service continues execution until it completes or
responds with a YIELD.
Hash Library Architecture
Tcg2Dxe uses HashLibBaseCryptoRouterDxe with all hash instance libraries included.
Registration Flow
HashLibBaseCryptoRouterConstructorresetsPcdTcg2HashAlgorithmBitmapto 0.- Each
HashInstanceLibconstructor callsRegisterHashInterfaceLib(). RegisterHashInterfaceLib()checks the algorithm againstPcdTpm2HashMask(0x02= SHA256 only). Algorithms not in the mask returnEFI_UNSUPPORTED.
Hash Algorithm Bitmask Values
The bit positions used in PcdTpm2HashMask, PcdTcg2HashAlgorithmBitmap, and the
EFI_TCG2_BOOT_SERVICE_CAPABILITY.HashAlgorithmBitmap field are defined by the EFI
TCG2 protocol and the TCG algorithm registry:
- UEFI TCG2 Protocol Specification — see
EFI_TCG2_BOOT_HASH_ALG_*(SHA1= BIT0,SHA256= BIT1,SHA384= BIT2,SHA512= BIT3,SM3_256= BIT4). - TCG Algorithm Registry — canonical list of TPM hash algorithm IDs.
For this platform, PcdTpm2HashMask = 0x02 enables SHA256 only.
Filtering Chain
PcdTpm2HashMask (0x02)
│
▼
RegisterHashInterfaceLib() ── gates which HashInstanceLibs register
│
▼
PcdTcg2HashAlgorithmBitmap ── result of all successful registrations
│
▼
Tcg2Dxe intersects with TPM-reported capabilities
│
▼
Final ActivePcrBanks / HashAlgorithmBitmap in EFI_TCG2_BOOT_SERVICE_CAPABILITY
Physical Presence Interface
Library Selection
TPM2_ENABLE | Library | Behavior |
|---|---|---|
FALSE | Tcg2PhysicalPresenceLibNull | All functions stubbed |
TRUE | DxeTcg2PhysicalPresenceMinimumLib | Auto-confirms Clear; rejects all other operations |
The MinimumLib implementation:
- Auto-confirms TPM Clear operations without user prompting.
- Rejects SET_PCR_BANKS, LOG_ALL_DIGESTS, and other operations with
TCG_PP_RETURN_TPM_OPERATION_RESPONSE_FAILURE. - Does not create or use
TCG2_PHYSICAL_PRESENCE_FLAGS_VARIABLE.
ProcessRequest in BDS
Tcg2PhysicalPresenceLibProcessRequest() is invoked from the platform's
DeviceBootManagerLib during DeviceBootManagerAfterConsole(), before the shell
launches. It:
- Reads the
Tcg2PhysicalPresenceNV variable (creates it if missing). - Executes any pending PP request stored in the variable.
- Stores the result back for
ReturnOperationResponseToOsFunction()to report.
Tcg2StandaloneMmArm (Secure World)
The Tcg2StandaloneMmArm driver runs in the StMM partition (id=0x8001). It handles
Physical Presence NV variable operations when called from the DXE-phase PP library via
MM communicate. This is necessary because NV variable writes go through the secure
variable store in StMM.
ACPI Integration
TPM2 ACPI Table
Published by Tcg2AcpiFfa.c:
| Field | Value |
|---|---|
| Start Method | CRB with FF-A (0x0C) |
| Control Area Address | PcdTpmBaseAddress + 0x40 |
| Command Buffer | PcdTpmBaseAddress + 0x80, size 0xF80 |
| Response Buffer | PcdTpmBaseAddress + 0x80, size 0xF80 |
| Platform Parameters | Partition ID from PcdTpmServiceFfaPartitionId |
TPM ACPI Device (SSDT)
An SSDT is published with a TPM0 device node containing:
_HIDpatched with the TPM manufacturer ID read from hardware._CRSwith a QWordMemory resource pointing toPcdTpmBaseAddressthroughPcdTpmMaxAddress._DSMimplementing TCG PPI operations 1–8 for OS-initiated Physical Presence requests.- An
FFixedHwOperationRegion for FF-A DirectReq2 passthrough from the OS.
swtpm Setup
Installation
swtpm requires Unix sockets, so it must run in a Linux environment. On Windows, use WSL (Windows Subsystem for Linux).
# Windows (from a WSL terminal)
wsl --install # if WSL is not yet enabled
wsl # enter the WSL environment
# Ubuntu/Debian (native or WSL)
sudo apt install swtpm swtpm-tools
# Fedora
sudo dnf install swtpm swtpm-tools
Manual Setup
Create the TPM state directory and start swtpm before launching QEMU:
mkdir -p /tmp/mytpm1
swtpm socket \
--tpmstate dir=/tmp/mytpm1 \
--ctrl type=unixio,path=/tmp/mytpm1/swtpm-sock \
--tpm2 \
--log level=20
Automatic Setup (QemuRunner)
When SWTPM_ENABLE=TRUE, QemuRunner.py automatically starts swtpm as a subprocess before
launching QEMU. The swtpm state directory is set to BUILD_OUTPUT_BASE and the Unix socket
is placed at {BUILD_OUTPUT_BASE}/swtpm-sock:
# Platforms/QemuArmVirtPkg/Plugins/QemuRunner/QemuRunner.py
@staticmethod
def StartSwTpm(tpm_dir, tpm_sock):
"""Starts the swtpm emulator and returns its Popen handle."""
cmd = [
"swtpm", "socket",
"--tpmstate", f"dir={tpm_dir}",
"--ctrl", f"type=unixio,path={tpm_sock}",
"--tpm2",
"--log", "level=1",
]
return subprocess.Popen(cmd)
swtpm is started before QEMU launches. QemuRunner then waits (up to 30 seconds) for the
Unix socket to appear before starting QEMU, and terminates the swtpm process so it doesn't
outlive the run. SWTPM is enabled by default. Disable it by setting SWTPM_ENABLE=FALSE on
the command line or in the BuildConfig.conf file.
SWTPM is only available on Linux builds. QemuRunner automatically disables it on Windows
hosts even if SWTPM_ENABLE=TRUE.
QEMU Arguments
When SWTPM_ENABLE=TRUE, QemuRunner.py adds the following to the QEMU command line
(with the socket path under BUILD_OUTPUT_BASE):
-chardev socket,id=chrtpm,path={BUILD_OUTPUT_BASE}/swtpm-sock
-tpmdev emulator,id=tpm0,chardev=chrtpm
-device tpm-tis-device,tpmdev=tpm0
The -device tpm-tis-device argument is Arm-specific. It attaches a sysbus TIS-compatible
TPM device to the Arm Virt machine at the Internal CRB address 0x40200000. This differs
from Q35, which uses the ISA/PCI tpm-tis device instead (see TPM on QEMU Q35).
Communication Flow
Complete path from a shell application to swtpm:
TpmShellApp (UEFI Shell)
│ gBS->LocateProtocol(&gEfiTcg2ProtocolGuid)
│ Tcg2Protocol->SetActivePcrBanks(0x02)
▼
Tcg2Dxe (EFI_TCG2_PROTOCOL)
│ Validates bank mask against HashAlgorithmBitmap
│ Calls Tcg2PhysicalPresenceLibSubmitRequestToPreOSFunction()
│ ├── MinimumLib: rejects SET_PCR_BANKS → returns EFI_UNSUPPORTED
│ └── MinimumLib: NO_ACTION (already-active) → writes to NV variable → EFI_SUCCESS
▼
Tpm2CommandLib (for direct TPM commands like GetCapability)
│ Serializes TPM2_CC command structure into byte buffer
│ Calls Tpm2SubmitCommand(cmdBuffer, cmdSize, rspBuffer, &rspSize)
▼
Tpm2DeviceLibFfa — FfaTpm2SubmitCommand()
│ PtpCrbTpmCommand(CrbReg = PcdTpmBaseAddress)
│
│ ┌─ STEP 1: cmdReady ──────────────────────────────────────────┐
│ │ MmioWrite32(&CrbReg->CrbControlRequest, PTP_CRB_CONTROL │
│ │ _AREA_REQUEST_COMMAND_READY) │
│ │ Tpm2ServiceStart(QUALIFIER_COMMAND, 0) ── FF-A ──► MSSP │
│ └─────────────────────────────────────────────────────────────┘
│
│ ┌─ STEP 2: submit ────────────────────────────────────────────┐
│ │ CopyMem(CrbReg->CrbDataBuffer, cmdBuffer, cmdSize) │
│ │ MmioWrite32(&CrbReg->CrbControlStart, 1) │
│ │ Tpm2ServiceStart(QUALIFIER_COMMAND, 0) ── FF-A ──► MSSP │
│ └─────────────────────────────────────────────────────────────┘
│
│ ════ WORLD SWITCH ════
│
│ MSSP (secure world, partition 0x8002):
│ TpmServiceLib: state READY → execute
│ TpmServiceStateTranslationLib:
│ 1. Read command from Internal CRB data buffer
│ 2. CopyCommandData() → write to External CRB @ 0x0c000000 (FIFO)
│ 3. StartCommand() → trigger TPM execution
│ 4. CopyResponseData() → read response from External CRB
│ 5. Write response to Internal CRB data buffer
│
│ ════ QEMU MMIO ════
│
│ QEMU TPM device @ 0x0c000000
│ └── Unix socket ──── swtpm process
│
│ ════ WORLD SWITCH BACK ════
│
│ ┌─ STEP 3: goIdle ────────────────────────────────────────────┐
│ │ Read response from CrbReg->CrbDataBuffer │
│ │ MmioWrite32(&CrbReg->CrbControlRequest, PTP_CRB_CONTROL │
│ │ _AREA_REQUEST_GO_IDLE) │
│ │ Tpm2ServiceStart(QUALIFIER_COMMAND, 0) ── FF-A ──► MSSP │
│ └─────────────────────────────────────────────────────────────┘
▼
Response returned to caller
PCDs Reference
Required PCDs (set when TPM2_ENABLE=TRUE)
| PCD | Value | Type | Purpose |
|---|---|---|---|
PcdTpmBaseAddress | 0x40200000 | FixedAtBuild | Internal CRB base address |
PcdTpmMaxAddress | 0x40204FFF | FixedAtBuild | Internal CRB end address (5 localities) |
PcdTpm2HashMask | 0x02 | DynamicDefault | Hash algorithm filter (SHA256 only) |
PcdTpmInstanceGuid | gEfiTpmDeviceInstanceTpm20DtpmGuid | FixedAtBuild | Selects discrete TPM 2.0 device type |
PcdTpm2AcpiTableRev | 5 | DynamicHii | ACPI TPM2 table revision |
PcdUserPhysicalPresence | FALSE | FixedAtBuild | No physical user presence assertion |
Memory Type PCDs
| PCD | Value | Purpose |
|---|---|---|
PcdMemoryTypeEfiACPIReclaimMemory | 0x143 | ACPI reclaim memory pages (includes TPM ACPI tables) |
PcdMemoryTypeEfiACPIMemoryNVS | 0x3C | ACPI NVS pages (includes CRB region) |
PcdMemoryTypeEfiRuntimeServicesData | 0x642 | Runtime services data pages |
PcdMemoryTypeEfiRuntimeServicesCode | 0x424 | Runtime services code pages |
PcdMemoryTypeEfiReservedMemoryType | 0x505 | Reserved memory pages |
Conditional PCDs (TPM2_CONFIG_ENABLE=TRUE)
| PCD | Value | Purpose |
|---|---|---|
PcdTcgPhysicalPresenceInterfaceVer | "1.3" | TCG PPI specification version reported to OS |