microsoft/mu_tiano_platforms

Public

mirrored fromhttps://github.com/microsoft/mu_tiano_platformsAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
main

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

docs/TPM/TpmQemuQ35.md

420lines · modepreview

# TPM on QEMU Q35

This document describes the TPM 2.0 architecture for the QEMU Q35 platform. Q35 uses a
direct CRB/FIFO path between firmware and the TPM device.

## Table of Contents

- [Requirements](#requirements)
- [Build Configuration](#build-configuration)
- [Platform Memory Layout](#platform-memory-layout)
- [Architecture Overview](#architecture-overview)
- [TPM Device Library Stack](#tpm-device-library-stack)
- [Hash Library Architecture](#hash-library-architecture)
- [Physical Presence Interface](#physical-presence-interface)
- [swtpm Setup](#swtpm-setup)
- [Communication Flow](#communication-flow)
- [PCDs Reference](#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](#swtpm-setup) for the full setup commands.

## Build Configuration

The TPM is disabled by default. To enable it, set `BLD_*_TPM_ENABLE=TRUE` on the command line or in a
BuildConfig.conf file placed at the root level of the repo:

```bash
stuart_build -c Platforms/QemuQ35Pkg/PlatformBuild.py --FlashRom \
  BLD_*_TPM_ENABLE=TRUE \
```

The following defines control TPM behavior in `QemuQ35Pkg.dsc`:

| Define | Default | Purpose |
| -------- | --------- | --------- |
| `TPM_ENABLE` | `FALSE` | Master switch. Guards all TPM drivers, libraries, and PCDs. |
| `TPM_CONFIG_ENABLE` | `FALSE` | Enables `Tcg2ConfigDxe` HII configuration UI. |
| `TPM_REPLAY_ENABLED` | `FALSE` | Enables TPM Replay overrides (uses `TpmTestingPkg` variants of Tcg2Dxe and DxeTpm2MeasureBootLib). |

## Platform Memory Layout

Q35 uses the standard x86 TPM memory-mapped I/O region:

| Region | Address | Size | Interface |
| -------- | --------- | ------ | ----------- |
| TPM TIS/CRB | `0xFED40000` | 0x5000 (20 KiB) | CRB or TIS (auto-detected) |

The firmware communicates directly with the TPM device via MMIO, and QEMU forwards the
I/O to swtpm over a Unix socket.

The base address comes from the SecurityPkg package declaration default
(`PcdTpmBaseAddress = 0xFED40000`). The Q35 DSC does not override it explicitly. The
`Tcg2ConfigPei` driver detects the TPM at this address during PEI.

## Architecture Overview

```text
┌──────────────────────────────────────────────────────────────────────────────────┐
│ UEFI Firmware (x86_64)                                                           │
│                                                                                  │
│  ┌─── PEI Phase ──────────────────────────────────────────────────────────────┐  │
│  │                                                                            │  │
│  │  Tcg2ConfigPei                                                             │  │
│  │    │ 1. Detect TPM 1.2 vs 2.0 at 0xFED40000                                │  │
│  │    │ 2. Set PcdTpmInstanceGuid                                             │  │
│  │    ▼                                                                       │  │
│  │  HashLibBaseCryptoRouterPei + HashInstanceLib*                             │  │
│  │    │ 1. Constructors register each enabled hash algorithm                  │  │
│  │    │ 2. Filtered by PcdTpm2HashMask → PcdTcg2HashAlgorithmBitmap           │  │
│  │    ▼                                                                       │  │
│  │  Tcg2Pei                                                                   │  │
│  │    │ 1. Tpm2RequestUseTpm()                                                │  │
│  │    │ 2. Tpm2Startup(TPM_SU_CLEAR)                                          │  │
│  │    │ 3. SyncPcrAllocationsAndPcrMask()                                     │  │
│  │    │ 4. Tpm2SelfTest()                                                     │  │
│  │    │ 5. Measure firmware volumes (CRTM) into PCR[0-7]                      │  │
│  │    │ 6. Install TpmInitializedPpi                                          │  │
│  └────┼───────────────────────────────────────────────────────────────────────┘  │
│       ▼                                                                          │
│  ┌─── 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                                              │  │
│  └────┼───────────────────────────────────────────────────────────────────────┘  │
│       ▼                                                                          │
│  ┌─── 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.             │  │
│  └────┼───────────────────────────────────────────────────────────────────────┘  │
│       ▼                                                                          │
│  Tpm2DeviceLibDTpm ─ direct MMIO reads/writes to 0xFED40000                      │
│       │                                                                          │
├───────┼──────────────────────────────────────────────────────────────────────────┤
│       ▼                                                                          │
│  QEMU TPM TIS device (-device tpm-tis,tpmdev=tpm0)                               │
│       │                                                                          │
│       ▼                                                                          │
│  Unix socket ─ swtpm process (--tpm2)                                            │
└──────────────────────────────────────────────────────────────────────────────────┘
```

## TPM Device Library Stack

Q35 uses two different patterns for accessing the TPM, depending on the firmware phase:

### PEI Phase: Direct MMIO (`Tpm2DeviceLibDTpm`)

```text
Tcg2Pei
  │ Tpm2SubmitCommand()
  ▼
Tpm2DeviceLibDTpm
  │ Auto-detects CRB vs TIS vs FIFO via InterfaceId register
  │ DTpm2SubmitCommand() dispatches:
  │   CRB  → PtpCrbTpmCommand()
  │   FIFO → Tpm2TisTpmCommand()
  │   TIS  → Tpm2TisTpmCommand()
  ▼
Direct MMIO to 0xFED40000
```

`Tpm2DeviceLibDTpm` reads the `InterfaceId` register at `PcdTpmBaseAddress + 0x30` to
determine the interface type. QEMU's `tpm-tis` device presents a TIS/FIFO interface.

### DXE Phase: Router Pattern (`Tpm2DeviceLibRouter`)

```text
Tcg2Dxe
  │ Tpm2SubmitCommand()
  ▼
Tpm2DeviceLibRouterDxe
  │ Delegates to registered TPM2_DEVICE_INTERFACE
  ▼
Tpm2InstanceLibDTpm (constructor registers with router)
  │ DTpm2SubmitCommand() — same dispatch as PEI
  ▼
Direct MMIO to 0xFED40000
```

The router pattern exists to support future scenarios where multiple TPM device types
could coexist. The router accepts only the instance whose `ProviderGuid` matches
`PcdTpmInstanceGuid`.

### Other DXE Drivers: TCG2 Protocol (`Tpm2DeviceLibTcg2`)

Most DXE drivers that need TPM access use `Tpm2DeviceLibTcg2`, which goes through the
TCG2 Protocol rather than direct MMIO. Only Tcg2Dxe itself uses `Tpm2DeviceLibRouter`
with direct MMIO.

### CRB Register Layout

If the TPM presents a CRB interface (as opposed to TIS/FIFO), the register layout is
defined by the TCG PC Client Platform TPM Profile (PTP) specification. See:

- [TCG PC Client Platform TPM Profile (PTP) Specification][ptp-spec] —
  *Section 6 "Command Response Buffer Interface"* describes `LocalityState`,
  `LocalityControl`, `InterfaceId`, `CrbControlRequest`, `CrbControlStart`,
  `CrbControlCommand*`/`CrbControlResponse*`, and the shared `CrbDataBuffer`.

The key register for this platform is `InterfaceId` at `PcdTpmBaseAddress + 0x30`, which
`Tpm2DeviceLibDTpm` reads to decide between CRB and TIS/FIFO dispatch paths.

### TIS Register Layout

QEMU's `tpm-tis` device presents a TIS (TPM Interface Specification) / FIFO interface.
The register layout is defined by:

- [TCG PC Client Specific TPM Interface Specification (TIS)][tis-spec] —
  defines `Access`, `IntEnable`/`IntVector`, `STS` (with `commandReady`, `tpmGo`,
  `dataAvail`, `burstCount`), `DataFifo`, and the `Vid`/`Did`/`Rid` identification
  registers.

The TIS flow uses `BurstCount` from the STS register to pace reads and writes through
the `DataFifo` register, one burst at a time.

[ptp-spec]: https://trustedcomputinggroup.org/resource/pc-client-platform-tpm-profile-ptp-specification/
[tis-spec]: https://trustedcomputinggroup.org/resource/pc-client-work-group-pc-client-specific-tpm-interface-specification-tis/

## Hash Library Architecture

Tcg2Dxe uses `HashLibBaseCryptoRouterDxe` while Tcg2Pei uses `HashLibBaseCryptoRouterPei`
with all hash instance libraries included.

### Registration Flow

1. `HashLibBaseCryptoRouterConstructor` resets `PcdTcg2HashAlgorithmBitmap` to 0.
2. Each `HashInstanceLib` constructor calls `RegisterHashInterfaceLib()`.
3. `RegisterHashInterfaceLib()` checks the algorithm against `PcdTpm2HashMask` (`0x02` =
   SHA256 only). Algorithms not in the mask return `EFI_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][tcg2-proto] — see `EFI_TCG2_BOOT_HASH_ALG_*`
  (`SHA1` = BIT0, `SHA256` = BIT1, `SHA384` = BIT2, `SHA512` = BIT3, `SM3_256` = BIT4).
- [TCG Algorithm Registry][tcg-algreg] — canonical list of TPM hash algorithm IDs.

For this platform, `PcdTpm2HashMask = 0x02` enables SHA256 only.

[tcg2-proto]: https://trustedcomputinggroup.org/resource/tcg-efi-protocol-specification/
[tcg-algreg]: https://trustedcomputinggroup.org/resource/tcg-algorithm-registry/

### Filtering Chain

```text
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

| `TPM_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:

1. Reads the `Tcg2PhysicalPresence` NV variable (creates it if missing).
2. Executes any pending PP request stored in the variable.
3. Stores the result back for `ReturnOperationResponseToOsFunction()` to report.

## swtpm Setup

### Installation

swtpm requires Unix sockets, so it must run in a Linux environment. On Windows,
use [WSL](https://learn.microsoft.com/en-us/windows/wsl/install) (Windows
Subsystem for Linux).

```bash
# 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:

```bash
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 in a background thread
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`:

```python
# Platforms/QemuQ35Pkg/Plugins/QemuRunner/QemuRunner.py
@staticmethod
def RunSwTpmThread(tpm_dir, tpm_sock):
    """Runs TPM in a separate thread"""
    tpm_cmd = "swtpm"
    tpm_args = f"socket --tpmstate dir={tpm_dir} --ctrl type=unixio,path={tpm_sock} --tpm2 --log level=1"
```

The thread is launched before QEMU starts and joined after QEMU exits. Note that the SWTPM
is enabled by default. You can disable it by setting `SWTPM_ENABLE=FALSE` from the command
line or in the BuildConfig.conf file.

### QEMU Arguments

When `SWTPM_ENABLE=TRUE`, `QemuRunner.py` adds the following to the QEMU command line
(with the socket path under `BUILD_OUTPUT_BASE`):

```text
-chardev socket,id=chrtpm,path={BUILD_OUTPUT_BASE}/swtpm-sock
-tpmdev emulator,id=tpm0,chardev=chrtpm
-device tpm-tis,tpmdev=tpm0
```

The `-device tpm-tis` argument is Q35-specific — it attaches a TIS-compatible TPM device
to the Q35 chipset at the standard address `0xFED40000`.

## Communication Flow

Complete path from a shell application to swtpm:

```text
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 NV variable → EFI_SUCCESS
  ▼
Tpm2CommandLib (for direct TPM commands like GetCapability)
  │ Serializes TPM2 command structure into byte buffer
  │ Calls Tpm2SubmitCommand(cmdBuffer, cmdSize, rspBuffer, &rspSize)
  ▼
Tpm2DeviceLibRouter → Tpm2InstanceLibDTpm
  │ DTpm2SubmitCommand() — detects interface type (TIS on QEMU)
  ▼
Tpm2TisTpmCommand (Tpm2Tis.c)
  │ 1. TisPcPrepareCommand: set TIS_PC_STS_COMMAND_READY
  │ 2. Write command bytes to DataFifo (paced by BurstCount)
  │ 3. Set TIS_PC_STS_GO to start execution
  │ 4. Poll STS for DataAvail (90 second timeout)
  │ 5. Read response header from DataFifo
  │ 6. Read remaining response bytes (paced by BurstCount)
  │ 7. Set TIS_PC_STS_READY (return to idle)
  ▼
MMIO to 0xFED40000 (QEMU TIS device)
  │
  ▼
QEMU tpm-tis device ──── Unix socket ──── swtpm process
```

## PCDs Reference

### Required PCDs (set when TPM_ENABLE=TRUE)

| PCD | Value | Type | Purpose |
| ----- | ------- | ------ | --------- |
| `PcdTpmBaseAddress` | `0xFED40000` (package default) | DynamicDefault | TPM TIS/CRB MMIO base address |
| `PcdTpm2HashMask` | `0x02` | DynamicDefault | Hash algorithm filter (SHA256 only) |
| `PcdTpmInstanceGuid` | `gEfiTpmDeviceInstanceTpm20DtpmGuid` | DynamicDefault | Selects discrete TPM 2.0 device type (set by Tcg2ConfigPei at runtime) |
| `PcdTpm2AcpiTableRev` | `4` | DynamicHii | ACPI TPM2 table revision |
| `PcdUserPhysicalPresence` | `FALSE` | FixedAtBuild | No physical user presence assertion |

### Memory Type PCDs

| PCD | Value (pages) | Purpose |
| ----- | --------------- | --------- |
| `PcdMemoryTypeEfiACPIReclaimMemory` | `0x2B` (43) | Includes TPM ACPI tables |
| `PcdMemoryTypeEfiACPIMemoryNVS` | `0x80` (128) | ACPI NVS memory |
| `PcdMemoryTypeEfiReservedMemoryType` | `0x510` | Reserved memory |
| `PcdMemoryTypeEfiRuntimeServicesCode` | `0x100` | Runtime code |
| `PcdMemoryTypeEfiRuntimeServicesData` | `0x700` | Runtime data |

### Conditional PCDs (TPM_CONFIG_ENABLE=TRUE)

| PCD | Value | Purpose |
| ----- | ------- | --------- |
| `PcdTcgPhysicalPresenceInterfaceVer` | `"1.3"` | TCG PPI specification version reported to OS |