Inside UEFI: TPM initialization, PCRs and the event log
Tracing EDK II's TPM 2.0 initialization, PCR usage, measured byte streams, crypto-agile event logs, replay and the firmware-to-OS handoff.
On this page
- Source revision and scope
- Device implementation and host interface are separate choices
- Library resolution determines the actual call path
- PEI selects the TPM before dispatching its initializer
- The generic detector uses a TPM 1.2 probe
- OVMF uses a different detector
- Interface detection starts at the configured register window
- Acquiring a locality is separate from starting the TPM
- FIFO and legacy TIS access
- CRB access and command submission
- TPM2_Startup establishes the boot state
- The command packet is a big-endian byte stream
- S3 recovery changes the measurement history
- A PCR accumulates a measurement history
- Initialization, reset and locality affect replay
- A PCR selection also identifies its bank
- PCR-bank policy is part of initialization
- Self-test has its own policy and result contract
- Initialization markers describe different outcomes
- What the PCR indices typically measure
- Firmware-owned PCRs 0 through 7
- PCRs 8 through 15 belong to the static OS profile
- PCR 16, the dynamic range and PCR 23
- The in-memory PEI pass begins firmware measurement
- The measured bytes and the event description can differ
- Firmware volumes: contents versus an address-and-length record
- EFI images: structured PE/COFF measurement
- Variables: the wrapper sometimes matters
- GPT and separators have directly reproducible payloads
- DXE exposes capabilities and reconstructs the event log
- Event logs live outside the TPM
- The legacy SHA-1 representation
- The crypto-agile log starts with a different record shape
- Worked record: a separator with two digests
- Information events must not be extended during replay
- Parsing must preserve the original stream boundaries
- Common event types provide the interpretation layer
- GetEventLog and the final-events handoff
- The overlap starts at the first retrieval
- Extension and logging can fail independently
- Replaying a log and interpreting the result
- Replay consistency and measurement appraisal are separate checks
- A PCR read and a quote provide different evidence
- Following the history into Windows
- Platform policy and ACPI complete the handoff
- The OS receives a device description
- Reconstructing the path in a firmware image
A Trusted Platform Module (TPM) becomes usable during boot through a sequence of decisions and state transitions. Platform firmware selects an implementation, acquires its command interface, starts or resumes the TPM, reconciles PCR-bank policy, and exposes measurement services. The operating system later receives a device description and a record of the measurements made before it gained control.
EDK II makes these boundaries visible. The same Tpm2RequestUseTpm() name can acquire a hardware locality in one module and locate a UEFI protocol in another. A PEI marker can announce that initialization finished even when no TPM was found. A successful command transfer can still contain a TPM error response.
This article follows those distinctions through the TPM 2.0 implementation, from the platform’s device selection to the loader and OS interfaces. It also reconstructs what the PCRs accumulate, which bytes firmware measures, how event records are serialized, and how a consumer can replay that history. TPM 1.2 appears where it affects detection and log compatibility.
Source revision and scope
The source reference is TianoCore EDK II commit 02c1141a0e5b5c958630e81ac6ebb4b03c1e836f, dated October 24, 2026. The inspected SecurityPkg, MdePkg and OvmfPkg files match that revision. All EDK II source links below are pinned to it.
The main path is the PC-style TPM 2.0 stack in SecurityPkg, with OVMF as a concrete example of how a platform selects its components and libraries. PCR assignments are compared with the PC Client Platform Firmware Profile 1.06, revision 52 and Windows’ documented usage. This is source analysis: no physical TPM, firmware image or S3-resume sequence was exercised. Code listings are reduced reconstructions, with descriptive local names and omitted branches identified in the text. The worked event record below is constructed from the format and calculated hashes, not taken from a captured boot.
The board-level prerequisites are platform-specific. Chipset register decoding, device power and reset, and initialization of a security processor backing a firmware TPM must make the selected interface available. The generic TPM driver assumes that work is done.
Device implementation and host interface are separate choices
The TPM implementation and its host-facing interface describe different properties.
| Property | Examples | What firmware needs to establish |
|---|---|---|
| TPM implementation | Discrete TPM, firmware TPM, virtual TPM | Which provider backs the platform’s TPM service. |
| Command family | TPM 1.2 or TPM 2.0 | Which command and response formats are supported. |
| Host interface | Legacy TIS, PTP FIFO, PTP CRB, or a platform-specific transport | How command bytes and responses cross the firmware/device boundary. |
| UEFI service | EFI_TCG2_PROTOCOL |
How later firmware components and an OS loader access the established service. |
Intel describes PTT as an integrated firmware TPM implementing TPM 2.0. That identifies the backing technology. A firmware module’s use of a CRB register block, by itself, describes the host interface rather than proving whether the implementation is a separate chip or firmware in a security processor.
The TCG Platform TPM Profile specifies the PC-facing interfaces. EDK II’s Tpm2DeviceLibDTpm implements the ordinary TIS/FIFO/CRB paths. Its library name and gEfiTpmDeviceInstanceTpm20DtpmGuid are software selection identifiers; interpreting a particular machine still requires its platform build configuration and transport implementation.
Library resolution determines the actual call path
EDK II resolves a library class to an implementation through platform .dsc files and component overrides. Consequently, finding a call to Tpm2SubmitCommand() establishes only the library interface until its binding is known.
The OVMF library mapping assigns PEIMs an SVSM-aware TPM device library. Its constructor first checks whether the confidential-guest environment provides an SVSM vTPM. If selected, requests use that backend; otherwise it initializes the regular PTP implementation. The MMIO path below follows that fallback, not the SVSM command mechanism. The branch is explicit in the SVSM shim.
DXE has another distinction. Ordinary DXE consumers use Tpm2DeviceLibTcg2, which forwards commands to an installed EFI_TCG2_PROTOCOL. The component override for Tcg2Dxe instead supplies the router and a registered device instance, so the protocol provider can reach the device beneath it.
DXE consumer
Tpm2CommandLib
Tpm2DeviceLibTcg2
EFI_TCG2_PROTOCOL.SubmitCommand
Tcg2Dxe
Tpm2DeviceLibRouter + selected device instance
DTpm2SubmitCommand
FIFO/TIS or CRB transfer
The protocol-backed library also implements Tpm2RequestUseTpm() by locating the protocol and returning success when it is available. It assumes the provider already acquired access. The regular device implementation uses the same API name to perform locality arbitration against registers.
PEI selects the TPM before dispatching its initializer
PEI, the Pre-EFI Initialization phase, uses PPIs to publish services and dependency markers. HOBs, or hand-off blocks, carry state into later boot phases. Platform Configuration Database values, or PCDs, provide another part of the configuration contract.
PcdTpmInstanceGuid identifies the selected TPM provider. The standard instance definitions include an all-zero GUID for no instance, a TPM 1.2 GUID, and a TPM 2.0 dTPM GUID.
The generic Tcg2ConfigPeimEntryPoint() reads and validates the Tcg2Config setup variable. With auto-detection enabled it invokes DetectTpmDevice(), then converts the detected device type into PcdTpmInstanceGuid. The code distinguishes the requested setup type from the detected provider, builds an instance HOB, and installs gEfiTpmDeviceSelectedGuid.
That last GUID is a scheduling boundary. Tcg2Pei.inf requires both the master boot-mode PPI and the TPM-device-selected marker in its dependency expression. Device selection therefore precedes the TPM 2.0 initializer’s dispatch.
The generic detector uses a TPM 1.2 probe
The SecurityPkg detector first requests access through Tpm12RequestUseTpm(). Failure returns the no-device selection. If access succeeds, it issues a TPM 1.2 startup command: TPM_ST_CLEAR for an ordinary boot, or TPM_ST_STATE on S3 resume.
Successful TPM 1.2 startup selects TPM 1.2 and sets PcdTpmInitializationPolicy to zero, avoiding a second initialization. An error from that startup probe selects the TPM 2.0 candidate.
if (EFI_ERROR (Tpm12RequestUseTpm ())) {
return TPM_DEVICE_NULL;
}
if (EFI_ERROR (Tpm12Startup (TPM_ST_CLEAR))) {
return TPM_DEVICE_2_0_DTPM;
}
PcdSet8S (PcdTpmInitializationPolicy, 0);
return TPM_DEVICE_1_2;
This is a classification heuristic in this detector. The failing TPM 1.2 probe does not itself establish successful TPM 2.0 startup. The selected TPM 2.0 path must still acquire its own interface and execute its initialization sequence.
On S3, the generic detector first tries the saved device-detection variable. That preserves the normal-boot selection when valid detection state is available.
OVMF uses a different detector
The OVMF configuration PEIM performs hardware detection without the generic setup-variable policy. When TPM 1.2 support is included, InternalTpm12Detect() requests access and sends TPM_ORD_GetTicks. The TPM 2.0-only variant substitutes a helper that returns EFI_UNSUPPORTED.
If the TPM 1.2 path does not succeed, OVMF calls Tpm2RequestUseTpm(). Success selects the TPM 2.0 instance GUID. Failure leaves the no-instance selection and publishes the initialization-complete marker so dependent PEIMs can proceed. It publishes the device-selected marker afterward.
These are alternative platform configurations. The OVMF GetTicks probe and the generic Startup probe should not be combined into a single universal detection sequence.
Interface detection starts at the configured register window
For the regular PTP backend, the register base comes from PcdTpmBaseAddress. SecurityPkg.dec declares a default of 0xFED40000. A platform can override it; this is a configured base, not a range discovered by the generic library through PCI enumeration.
The common device-library constructor initializes two cached configuration values:
- If
PcdActiveTpmInterfaceTypeis0xFF, it callsTpm2GetPtpInterface()and records the result. - For CRB, if
PcdCRBIdleByPassis0xFF, it reads and caches the idle-bypass capability.
Tpm2IsPtpPresence() reads the first byte of the configured window and treats 0xFF as absent. This is a register-availability test. Interface classification then reads the identifier at base + 0x30 and the FIFO capability register at base + 0x14.
| Classification | Conditions checked by Tpm2GetPtpInterface() |
|---|---|
| CRB | CRB interface type, a recognized CRB interface version, and CapCRB set. |
| PTP FIFO | FIFO interface type, the FIFO interface version, CapFIFO set, and the PTP capability-register version. |
| Legacy TIS | The legacy TIS interface type. |
| Unavailable or unsupported | Presence test fails, or none of the supported combinations matches. |
The result drives both DTpm2RequestUseTpm() and DTpm2SubmitCommand(). An unsupported interface therefore surfaces as EFI_NOT_FOUND at the API boundary.
The firmware observes the interface already selected by the platform. The classification helper does not negotiate a different interface merely because the identifier advertises another supported mode.
Acquiring a locality is separate from starting the TPM
A locality identifies an access context at the TPM interface. It is separate from UEFI privilege levels and from the TPM’s storage, endorsement and platform hierarchies.
With the default PC register base, the paths here operate on the locality-zero window. Higher-locality operations depend on the platform and transport. The register definitions describe the access and control bits used by the library.
FIFO and legacy TIS access
TisPcRequestUseTpm() checks presence, writes the request-use bit to the access register, and polls for valid, active access. Its request is a register write, not a TPM command packet.
base + 0x00 ACCESS.requestUse = 1
poll ACCESS.valid and ACCESS.activeLocality
stop on success or the access timeout
The command transfer then follows a different state machine: set command-ready, obtain the FIFO burst count, write command bytes, wait for the TPM to stop expecting input, assert GO, and wait for response data. Response bytes are read from the FIFO using the advertised burst count.
| FIFO register | Offset | Role in this implementation |
|---|---|---|
| Access | 0x00 |
Request and observe locality ownership. |
| Interface capability | 0x14 |
Help classify the interface. |
| Status | 0x18 |
Command-ready, input expectation and response availability. |
| Burst count | 0x19 |
Number of bytes transferable in a burst. |
| Data FIFO | 0x24 |
Command and response byte stream. |
| Interface identifier | 0x30 |
Active interface type and capabilities. |
The inspected transfer path polls registers with bounded delays. An interrupt-enable register in the device definition does not imply this firmware library uses an interrupt-driven command-completion path.
CRB access and command submission
For CRB, PtpCrbRequestUseTpm() writes REQUEST_ACCESS to the locality-control register at +0x08, then polls the granted bit in locality status at +0x0C.
PtpCrbTpmCommand() subsequently manages the command-response buffer:
- Enforce the required idle transition, accounting for the cached idle-bypass capability.
- Request command-ready and wait for the ready transition.
- Copy the command bytes into the buffer and program command/response addresses and sizes.
- Set the start bit and wait for the TPM to clear it.
- Read the response header, check the caller’s response capacity, and copy the remaining response bytes.
- Request the return to idle. Timeout paths attempt cancellation and recovery.
| CRB register or area | Offset from locality base |
|---|---|
| Locality control / status | 0x08 / 0x0C |
| Interface identifier | 0x30 |
| Control request / status | 0x40 / 0x44 |
| Cancel / start | 0x48 / 0x4C |
| Command size / address | 0x58 / 0x5C and 0x60 |
| Response size / address | 0x64 / 0x68 |
| Data buffer in the EDK II register structure | 0x80 |
This implementation uses the data area embedded in its mapped CRB structure. Its state machine and buffer programming are concrete source behavior; other CRB backends can have different buffer-placement requirements.
At this layer, successful submission means that firmware obtained a response through the transport. Interpreting the TPM command’s result belongs to the command wrapper above it.
TPM2_Startup establishes the boot state
The main PEI initializer is PeimEntryMA() in Tcg2Pei.c. It first rejects a no-instance or TPM 1.2 selection and checks for an existing gTpmErrorHobGuid. On an ordinary boot it also registers for shadowing into permanent memory.
In its initial execution, before the in-memory measurement pass, it requests TPM access. When PcdTpm2InitializationPolicy is 1, it sends:
| Boot path | Startup command |
|---|---|
| Ordinary boot | TPM2_Startup(TPM_SU_CLEAR) |
| S3 resume | TPM2_Startup(TPM_SU_STATE) |
| S3 state startup fails | Retry with TPM_SU_CLEAR; record the recovered-error condition if that succeeds. |
Initialization policy is 0 |
Skip this startup command; initialization is delegated to an earlier/platform path. |
The PCD default is 1. Its declaration explicitly allows a platform to skip initialization when it was performed earlier, such as by a chipset SRTM solution.
The TPM Library architecture defines startup as the transition into operational state, with behavior depending on the startup type and preceding shutdown. Startup(CLEAR) and Startup(STATE) express different boot-state requirements. TPM_SU_CLEAR is a startup parameter; it is not an invocation of the separate TPM2_Clear command.
The command packet is a big-endian byte stream
Tpm2Startup() constructs a packed header and startup parameter, converts the multi-byte fields to big endian, and submits the bytes through Tpm2DeviceLib.
80 01 00 00 00 0C 00 00 01 44 00 00
----- ----------- ----------- -----
tag size command type
| Field | Value |
|---|---|
| Tag | TPM_ST_NO_SESSIONS, 0x8001 |
| Total size | 12 bytes |
| Command code | TPM_CC_Startup, 0x00000144 |
| Startup type | TPM_SU_CLEAR, 0x0000; TPM_SU_STATE is 0x0001. |
The wrapper examines the returned TPM response code after transport success. It maps TPM_RC_SUCCESS to EFI_SUCCESS and also accepts TPM_RC_INITIALIZE, accommodating the already-initialized case. Other response codes become EFI_DEVICE_ERROR.
Thus an EFI_SUCCESS return from this wrapper does not necessarily mean a fresh startup was performed at that call site. The mapping deliberately includes an already-started TPM state.
S3 recovery changes the measurement history
S3 resume follows its own path. The PEIM skips the ordinary shadow/firmware-measurement sequence and does not request the optional self-test.
If Startup(STATE) fails but Startup(CLEAR) succeeds, the initializer emits error separator measurements into PCRs 0 through 7. The event payload is the 32-bit value 1; the measurement path hashes that payload before extension. It is not a direct write of the integer 1 into the PCR.
Tcg2PlatformPei also registers an end-of-PEI callback on S3 resume. The callback looks for the event HOBs used by this recovery path and invokes platform-hierarchy configuration when the error was recorded. A successful fallback startup therefore continues with an explicit error history and additional platform handling.
A PCR accumulates a measurement history
A Platform Configuration Register holds a fixed-size digest inside the TPM. Its address has two parts: a bank algorithm and a PCR index. SHA-256 PCR 7 and SHA-1 PCR 7 are separate registers. Neither contains a filename, an executable, a certificate database or a list of prior measurements.
In the ordinary EDK II path, firmware first computes an event digest from selected bytes. It then submits that digest to the TPM, which extends the addressed PCR:
eventDigest = H(measuredByteStream)
PCRnext = H(PCRprevious || eventDigest)
There are two hash operations here. Hashing a firmware volume produces an input to the extend operation; extending hashes that input together with the old PCR. A final PCR value therefore differs from the hash of the last image measured into it.
Order matters within each PCR. Measuring A and then B normally produces a different result from measuring B and then A. Events directed to PCR 4 do not alter PCR 7, although a log can interleave records for both. Repetition also matters: extending an identical digest twice advances the register twice.
The PEI hash router and the TPM integrity command library expose the division of work. Tpm2PcrExtend() serializes a list of algorithm-tagged digests into a TPM2_PCR_Extend command. TPM2_PCR_Event is a separate command that can hash supplied event bytes inside the TPM; the ordinary router path does not use it to send firmware images to the TPM.
Initialization, reset and locality affect replay
PCR reset behavior comes from the TPM’s platform profile and PCR attributes. Static boot PCRs cannot be arbitrarily cleared by ordinary OS software midway through a boot. Debug, application and dynamic-launch PCRs have different reset rules. TPM2_PCR_Reset is therefore not a general instruction for returning any PCR to zero.
For ordinary cold-boot measurements, PCR 0 starts at zero, but a replay engine must use the actual startup state. The TPM Library architecture sets PCR 0 to the locality that sent TPM2_Startup(TPM_SU_CLEAR), as a digest-sized integer. A startup from locality 3 therefore leaves every bank’s PCR 0 at 00…03 before the first extend. An H-CRTM sequence before startup takes precedence: the TPM sets PCR 0 to 00…04 and immediately extends the hashed H-CRTM data, so PCR[0] = H(00…04 || H(hash_data)). Resume and dynamic launch have their own rules, which the same document defines along with the role of PCR attributes.
EDK II preserves startup context through gTpm2StartupLocalityHobGuid. SetupEventLog() can convert that HOB into a StartupLocality information event, the PC firmware profile’s TCG_EfiStartupLocalityEvent: the 16-byte signature StartupLocality\0 followed by one byte, 0, 3 or 4 for an H-CRTM. The profile requires it when the value is 3 or 4 and places it before any event that extends PCR 0. It tells a replay engine which starting value to use; it is not itself a digest to extend. Initializing every PCR to zero and extending every log record is wrong on these platforms.
A PCR selection also identifies its bank
Tpm2PcrRead() accepts a TPML_PCR_SELECTION. Each selection contains an algorithm identifier and a bitmap of PCR indices. In a three-byte bitmap, bit 7 of the first byte selects PCR 7, and bit 3 of the second byte selects PCR 11:
algorithm identifier = 0x000B
sizeofSelect = 3
pcrSelect = 80 08 00
That bitmap has no expected digest values in it. It identifies registers to read or include in an operation. Policy evaluation additionally needs the relevant state and authorization conditions. The response to a PCR read includes the returned selection, values and a PCR update counter; code should interpret those returned fields rather than assume every requested value fit into one response.
PCR-bank policy is part of initialization
TPM 2.0 maintains Platform Configuration Registers (PCRs) in banks associated with different hash algorithms. A supported bank, an allocated active bank and a hashing implementation linked into firmware are separate pieces of state.
SyncPcrAllocationsAndPcrMask() queries the TPM’s supported and active PCR selections, then reconciles them with two firmware masks:
| Value | Meaning in this path |
|---|---|
| TPM supported algorithms | Bank capability reported by the device. |
| TPM active banks | Banks currently allocated with PCR selections. |
PcdTpm2HashMask |
Platform measurement-mask policy. A zero value is initialized from the active banks. |
PcdTcg2HashAlgorithmBitmap |
Algorithms available to the firmware hashing implementation. |
When active banks exceed the platform mask or firmware hashing support, the helper derives a reduced set:
newActive = activeBanks & platformMask & firmwareHashAlgorithms
For a nonempty reduced set, it requests Tpm2PcrAllocateBanks() and calls ResetCold(). Allocation changes require a subsequent boot. The helper also removes unsupported algorithms from the platform mask.
The OVMF PEI component configuration links a hash router and separate hash instances into Tcg2Pei. That build-time composition contributes to the bitmap the policy code consumes. Device capability alone is insufficient to establish which measurements this firmware can produce.
The helper returns VOID; capability and allocation failures have debug/assertion handling rather than a status returned directly to PeimEntryMA(). This matters when interpreting a boot trace: the initialization marker must be read alongside the bank configuration, command responses and build’s assertion behavior.
Self-test has its own policy and result contract
On a non-S3 boot, PcdTpm2SelfTestPolicy == 1 causes Tpm2SelfTest(NO). The NO parameter requests testing of functions not previously tested, rather than an unconditional full retest. A platform can suppress this call through policy, including configurations where testing was already performed.
There is also a return-value detail in this revision: Tpm2SelfTest() constructs and submits the command but returns the transport status without separately decoding Res.Header.responseCode. Tpm2Startup() does decode that response code. A successful EFI return must therefore be interpreted according to the specific wrapper, not treated as a uniform certificate of TPM health.
Initialization markers describe different outcomes
The PEI dependency markers use NULL interfaces. Their presence is the signal; they do not provide a status method.
| Marker or record | Meaning established by the inspected paths |
|---|---|
gEfiTpmDeviceSelectedGuid |
Platform device selection has completed. |
gPeiTpmInitializedPpiGuid |
The initializer reached its successful initialization branch. |
gPeiTpmInitializationDonePpiGuid |
The initialization attempt completed, or configuration declared no TPM. This is not a success flag. |
gTpmErrorHobGuid |
An error was recorded for later consumers, including DXE. |
In PeimEntryMA(), the common error path builds the error HOB and reports an interface error. Its completion path installs the done PPI on both success and failure. The configuration modules also install the done marker when no TPM is selected, because the TPM initializer will not provide it in that case.
This gives dependent modules a way to wait for an outcome without waiting forever for success. The successful-initialization marker, error HOB and selected instance provide the additional context. None of these software records independently authenticates the TPM or proves complete measurement coverage.
What the PCR indices typically measure
PCR numbers are conventions imposed by a platform and its boot software. The TPM processes a register index and digest; it does not intrinsically recognize that PCR 4 concerns a boot application or that PCR 7 concerns Secure Boot.
For PC Client firmware, the firmware-profile allocation separates code, configuration, boot selection and authorization state. The concrete examples below follow the pinned EDK II implementation. Separators and other phase markers can also enter these registers, so the final value is rarely just one component’s hash.
Firmware-owned PCRs 0 through 7
| PCR | Typical PC firmware category | Measured material and EDK II examples |
|---|---|---|
| 0 | Platform firmware code and root-of-trust history | S-CRTM version and firmware-volume contents. Embedded modules can be covered by their containing measured volume. This is not necessarily a hash of the complete motherboard flash chip. |
| 1 | Platform configuration | Configuration affecting platform behavior. Tcg2Dxe measures BootOrder and referenced Boot#### variable data here; processor-location information can also produce events here. |
| 2 | Additional executable code | EFI boot-service and runtime drivers handled by the image-measurement path, including relevant add-in/option-ROM code. Manufacturer-controlled code already covered by a measured firmware volume can follow a different coverage path. |
| 3 | Configuration associated with additional code | Driver/option-ROM configuration and data under the platform’s measurement policy. A code event in PCR 2 does not imply a matching configuration event in PCR 3. |
| 4 | Boot application code and boot attempts | PE/COFF boot applications such as Windows Boot Manager, plus actions marking calls to and returns from boot applications. Several attempted boot targets can contribute to one history. |
| 5 | Boot metadata and transition data | The GPT measurement contains the partition header and valid entries. EDK II also records boot-services exit actions here. This is not a recursive digest of ESP files. |
| 6 | Manufacturer-specific state | The current PC firmware profile leaves the purpose to the platform manufacturer. Older descriptions centered on power-state transitions are not a universal modern definition. |
| 7 | Secure Boot policy and image-authority decisions | Configuration variables such as SecureBoot, PK, KEK, db and dbx, a policy separator, and the authority used to authorize relevant images. Image bytes themselves are measured through the code paths above. |
The ownership and measuring component matter as much as the category. A DXE driver embedded in an already-measured firmware volume need not appear as another PCR 2 image event. DxeTpm2MeasureBootHandler() consults measured/excluded firmware-volume information and distinguishes applications from drivers. Absence of an individual image event can reflect aggregate coverage; it can also reflect a coverage gap. The containing-volume record and policy resolve the difference.
There is also a documentation-profile difference: the Windows PCR reference labels PCR 6 for S4/S5 resume events, whereas the cited PC firmware profile makes it manufacturer-specific. The table above follows the latter allocation; the platform’s actual events determine what entered its PCR 6.
PCRs 8 through 15 belong to the static OS profile
The firmware profile assigns this range to the static operating-system environment. The meanings below are Windows’ documented native-UEFI usage, rather than portable names every OS must follow. Windows PCR descriptions
| PCR | Typical Windows description | Interpretation |
|---|---|---|
| 8 | Reserved; initially zero with no normal extends in the documented profile | Other boot stacks can use it. |
| 9 | Reserved; initially zero with no normal extends in the documented profile | Other boot stacks can use it. |
| 10 | Reserved; initially zero with no normal extends in the documented profile | Do not transfer a legacy BIOS PCR-10 boot-manager description to native UEFI. |
| 11 | BitLocker access control | Boot-phase events constrain when a TPM protector can be used. It is not the volume’s sector-data hash. |
| 12 | Data and highly volatile events | Boot-stack data events; not a universal hash of one fixed file. |
| 13 | Boot-module details | Details recorded by the Windows boot measurement stack. |
| 14 | Boot authorities | Authorization information recorded by that stack, separate from firmware’s PCR 7 policy history. |
| 15 | Reserved in the cited Windows profile | The PC firmware allocation still leaves its use to the static OS. |
For comparison, GRUB’s measurement profile places commands and kernel command lines in PCR 8 and files read by GRUB in PCR 9. That is a different measuring component with a different policy. “PCR 9 is unused” is meaningful only with the Windows profile qualified as above.
The BitLocker boot-chain analysis follows the Windows PCR 11 capping implementation and TPM-protector selection. A measurement being present in a PCR does not imply that BitLocker selected that PCR for its protector.
PCR 16, the dynamic range and PCR 23
| PCR | Profile role | Consequence for analysis |
|---|---|---|
| 16 | Debug | Resettable; unsuitable as a durable record of production boot history. |
| 17–22 | Dynamic root-of-trust measurement range | Dynamic-launch hardware and software define the measurements and locality restrictions. A register can represent launch components, policy or later dynamic-OS state, depending on the selected scheme. |
| 23 | Application support | Resettable application/OS use; interpret it within that application’s protocol. |
The PC firmware profile describes PCRs 16 and 23 and leaves PCRs 17–22 to the dynamic OS. The TCG D-RTM architecture defines how a dynamic launch uses them: the launch event makes a locality 4 access that resets PCRs 17–22 to zero, and the CPU’s first measurement goes into PCR 17. Beyond that, assignments vary between dynamic-launch implementations. For a concrete Windows-related example, Microsoft describes SMM protection components and policy being bound to PCR 17 during Secure Launch.
An unused or reset PCR must also be distinguished from a missing log event: resettable registers, dynamic launch and resume each need their own replay rules.
The in-memory PEI pass begins firmware measurement
On an ordinary boot, shadowing dispatches Tcg2Pei again from memory. RegisterForShadow() returning EFI_ALREADY_STARTED identifies that pass, and the entry point calls PeimEntryMP() instead of repeating device startup.
The in-memory pass installs gEdkiiTcgPpiGuid, optionally measures the S-CRTM version, measures the main boot firmware volume, and registers callbacks for additional firmware-volume information and the end of PEI.
The core-root-of-trust version measurement uses PCR 0 and EV_S_CRTM_VERSION. Firmware-volume measurement also targets PCR 0 in the inspected path, with event representations selected according to the platform firmware-profile revision. Coverage depends on the platform: the code supports excluded volumes, already-measured volumes and precomputed digests supplied through PPIs.
The regular measurement path connects hashing, TPM state and firmware memory:
MeasureMainBios / firmware-volume notification
MeasureFvImage
HashAndExtend
firmware hash instances compute digests
Tpm2PcrExtend submits the digest list
LogHashEvent
build event HOBs for the DXE handoff
The TPM retains the accumulated PCR state. Firmware retains the event records describing the measurements. The log is not a copy of the final PCR value; a verifier replays it from the bank’s starting value.
LogHashEvent() builds gTcgEventEntryHobGuid and gTcgEvent2EntryHobGuid records for the applicable log representations. The hash router supplies the host-side digest computation and PCR extension. The data handed to the TPM is normally the digest list rather than an entire firmware image.
These mechanics also locate the trust boundary: the TPM accumulates the values submitted to it; the platform’s measurement code determines which bytes those values describe. The root of trust and measurement coverage must be assessed separately from whether TPM2_Startup succeeded.
The measured bytes and the event description can differ
The measurement API carries two distinct inputs: DataToHash with its length, and an event structure containing the description to retain. The digest can describe bytes that are absent from the log. Conversely, some event payloads are the exact serialized bytes that were hashed.
Firmware volumes: contents versus an address-and-length record
In MeasureFvImage(), the ordinary path hashes the firmware-volume range at FvDataBase for FvLength bytes. The associated EV_EFI_PLATFORM_FIRMWARE_BLOB or BLOB2 payload describes a base address, length and, in the newer format, a description.
Hashing that short log payload would hash the location descriptor, not the firmware volume. Recomputing the measurement requires the original measured contents, including the relevant migration or pre-hash path. The code explicitly tracks the original FV address separately from the data address used for measurement.
EFI images: structured PE/COFF measurement
Tcg2MeasurePeImage() records the image location, size, link-time base and device path, but passes the image bytes to HashLogExtendEvent() with PE_COFF_IMAGE. The protocol dispatches that flag to MeasurePeImageAndExtend().
That routine hashes a structured PE/COFF byte stream. It skips the optional-header checksum and the Certificate Table directory entry, processes nonempty sections in raw-file order, and excludes certificate content through its remaining-data calculation. Consequently, three values must be kept separate:
- The whole-file hash used to identify a downloaded
.efisample. - The image digest produced by the PE/COFF measurement algorithm.
- The PCR value after that image digest and preceding events have been extended.
The device path in the event payload identifies the image for interpretation. It is not a substitute for the measured image, and hashing the path will not reproduce its PE digest.
Variables: the wrapper sometimes matters
Tcg2Dxe.MeasureVariable() builds a UEFI_VARIABLE_DATA payload containing the vendor GUID, name length, data length, UTF-16 variable name and variable data. Name length counts 16-bit characters; data length counts bytes. The serialized name excludes its terminator.
For EV_EFI_VARIABLE_DRIVER_CONFIG, the routine hashes that complete serialized wrapper. For ordinary boot-variable events, its alternate branch hashes the variable’s data bytes while still logging the richer wrapper. Thus the event type changes the relationship between the digest and payload.
Image-authorization events have another producer. DxeImageVerificationLib/Measurement.c constructs a variable-data wrapper for the EFI_SIGNATURE_DATA authority used to validate an image, then measures and logs the full wrapper into PCR 7 as EV_EFI_VARIABLE_AUTHORITY.
An authority event therefore does not mean “hash the entire executable certificate table.” It records a particular authorization input. The image’s code measurement and the authority measurement answer different questions.
GPT and separators have directly reproducible payloads
The GPT producer constructs EFI_GPT_DATA from the partition header, the count of valid entries, and those entries. It hashes and logs that constructed data into PCR 5. It does not hash free space, every disk sector or every ESP file.
MeasureSeparatorEvent() constructs a four-byte zero value, hashes it and records EV_SEPARATOR. The S3 recovery path described earlier uses a four-byte error value of one. A separator advances the PCR; it neither clears the register nor creates an empty boundary outside the hash history.
The important question for reproducing an event is therefore: which producer selected which byte stream under which event semantics? A general H(EventData) check is valid only for event types whose producer actually hashes that payload.
DXE exposes capabilities and reconstructs the event log
During DXE, the Driver Execution Environment, Tcg2Dxe.DriverEntry() checks the selected instance and rejects an existing TPM-error HOB before requesting access through its bound device library. The ordinary startup command belongs to the earlier PEI path; DXE’s access request does not imply another startup.
The driver queries manufacturer information, maximum command/response sizes, and supported and active PCR banks. It intersects the exposed algorithm information with firmware hashing support and prepares the EFI_TCG2_BOOT_SERVICE_CAPABILITY structure. The protocol version set here is 1.1, a version of the firmware service interface rather than the TPM command-family number.
When the SHA-1 bank is absent from the exposed active banks, it removes support for the legacy TCG 1.2 event-log format. A TPM 2.0 device can therefore expose the crypto-agile log without the SHA-1 log representation.
SetupEventLog() allocates the DXE log areas and copies the PEI event HOBs into them. For crypto-agile records it filters the PEI digest lists against the exposed active-bank mask and reconstructs their serialized sizes. This is log reconstruction, not a second extension of those PEI measurements. The function also establishes the final-events table.
The driver registers callbacks for later boot measurements. Its Secure Boot callback measures the policy variables and then a PCR 7 separator. On the first ready-to-boot notification, it measures handoff tables and boot variables, records a boot-attempt action in PCR 4, and emits separators for PCRs 0 through 6. Later boot attempts add their own return/call actions. Boot-services exit actions go to PCR 5. This event ordering is concrete source behavior; it should not be replaced by an assumed alphabetical list of measured files.
The resulting TCG2 protocol supplies these main operations:
| Operation | Role |
|---|---|
GetCapability() |
Report firmware/TPM capability information and presence state. |
GetEventLog() |
Return log locations, the last entry and truncation state. |
HashLogExtendEvent() |
Hash or measure data, extend the relevant TPM state, and optionally record an event. |
SubmitCommand() |
Submit a raw TPM command through the selected backend. |
| PCR-bank services | Query active banks and request a bank change through the supported policy path. |
The TCG EFI Protocol specification defines this loader-facing service boundary. It is a boot-services protocol, not the operating system’s permanent runtime TPM driver.
Event logs live outside the TPM
The TPM stores the current PCR digests. Firmware retains the event records in host memory, first as PEI HOBs and then as DXE log buffers. The OS can copy or persist the history after the handoff. The log is not an audit file kept inside the TPM, nor is the TPM responsible for recording which filename supposedly produced an incoming digest.
An ordinary event record identifies the destination PCR, event type, digest or digest list, and event-data length and bytes. Its digest field normally contains the input digest extended into the PCR, not the resulting PCR value. The latter must be calculated from the previous state.
The layouts and event-type constants are defined in UefiTcgPlatform.h. The TCG2 API’s EFI_TCG2_EVENT input structure is not itself the on-log record layout: the provider computes digests and serializes the chosen log representation.
The legacy SHA-1 representation
The legacy TCG_PCR_EVENT layout is a packed, little-endian header followed by variable-length event data:
UINT32 PCRIndex
UINT32 EventType
BYTE Digest[20] // SHA-1 input to the extend
UINT32 EventSize
BYTE Event[EventSize]
The fixed header occupies 32 bytes. A TPM 2.0 implementation can expose this representation for compatibility when the relevant SHA-1 bank is available. The format name does not mean the device is using the TPM 1.2 command family.
The crypto-agile log starts with a different record shape
For the TCG 2 format, EDK II writes a first record using the legacy-shaped header, PCR index 0, type EV_NO_ACTION and a zero SHA-1-sized digest field. Its payload is a TCG_EfiSpecIDEventStruct carrying the signature Spec ID Event03.
That payload describes the platform class, specification version fields, uintnSize, the number of algorithms, an (algorithmId, digestSize) table and optional vendor information. uintnSize encodes the platform’s UINTN width; it is not permission to reinterpret every subsequent length field as a native pointer-sized integer.
The algorithm table is the bootstrap information for parsing the remaining digest lists. In this implementation, entries can describe SHA-1, SHA-256, SHA-384, SHA-512 and SM3-256 according to active banks. The first record’s 20-byte placeholder does not prove that SHA-1 is an active bank: its legacy-shaped encoding is used to introduce a crypto-agile log even without SHA-1 measurements.
Subsequent records use the densely packed TCG_PCR_EVENT2 serialization:
UINT32 PCRIndex
UINT32 EventType
UINT32 DigestCount
repeat DigestCount times:
UINT16 AlgorithmId
BYTE Digest[DigestSizeForAlgorithm]
UINT32 EventSize
BYTE Event[EventSize]
The digest length comes from the algorithm’s declared size; there is no digest-length integer before each digest in this stream. The next algorithm identifier follows immediately after the preceding digest. Native TPML_DIGEST_VALUES storage includes fixed-capacity arrays/unions, so advancing by sizeof(TCG_PCR_EVENT2) or by the largest possible digest union skips the wrong bytes.
EDK II’s GetDigestListBinSize() and CopyDigestListBinToBuffer() walk these compact lists explicitly. Log integers are little endian, while TPM command integers are generally big endian. Sharing a TPM command unmarshaller with a log parser without accounting for that difference produces incorrect counts and algorithm IDs.
Worked record: a separator with two digests
Consider a constructed PCR 7 separator event with SHA-1 and SHA-256 digests for the four bytes 00 00 00 00. Its complete record occupies 76 bytes:
| Offset | Size | Value or field |
|---|---|---|
0x00 |
4 | PCR index: 07 00 00 00. |
0x04 |
4 | EV_SEPARATOR: 04 00 00 00. |
0x08 |
4 | Digest count: 02 00 00 00. |
0x0C |
2 | SHA-1 algorithm ID: 04 00. |
0x0E |
20 | SHA-1 digest of the separator payload. |
0x22 |
2 | SHA-256 algorithm ID: 0B 00. |
0x24 |
32 | SHA-256 digest of that same payload. |
0x44 |
4 | Event-data size: 04 00 00 00. |
0x48 |
4 | Separator payload: 00 00 00 00. |
SHA-1:
9069ca78e7450a285173431b3e52c5c25299e473
SHA-256:
df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119
Even though the payload is zero, both digests are nonzero. Extending the SHA-256 digest into a synthetic all-zero PCR produces 3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969. A real PCR 7 separator normally follows policy measurements, so its actual resulting PCR value has a different preceding state.
Information events must not be extended during replay
For ordinary PCR indices, TcgDxeHashLogExtendEvent() handles EV_NO_ACTION through its logging branch without calling HashAndExtend(). Examples include the Spec ID header, startup-locality information and platform-identification records. These records can be essential for interpretation even though they do not advance a PCR.
This is different from an EV_SEPARATOR, which is extended. Event names and an apparent digest field are insufficient to decide whether to replay a record; event semantics determine the operation. The inspected helper also has a separate NV-index path for an out-of-PCR-range destination, so its entire behavior should not be reduced to one rule for all possible indices.
Parsing must preserve the original stream boundaries
A parser needs a bounded buffer and explicit checks for each fixed field, digest count, algorithm ID, digest length and event-data length. Length additions must not wrap, and an unknown algorithm must not be assigned an assumed 32-byte digest. A complete, recognized Spec ID algorithm-size table can support skipping an algorithm the parser cannot evaluate; an unavailable or inconsistent size cannot.
Decoding the nested event data requires another layer of bounds checks. Variable-name counts are not byte counts; device paths contain their own node lengths; a firmware-blob event describes memory not included in the record. Preserve the original bytes and the order of extending events. Normalizing strings or rebuilding a variable wrapper with different padding can change the data whose digest is being checked.
Common event types provide the interpretation layer
These identifiers come from the pinned event definitions. The destination PCR is a separate field; an event type alone does not fix its destination for every producer.
| Type | Value | Typical interpretation |
|---|---|---|
EV_NO_ACTION |
0x00000003 |
Log metadata; do not perform an ordinary PCR extend. |
EV_SEPARATOR |
0x00000004 |
A measured phase/error marker; it advances the destination PCR. |
EV_S_CRTM_VERSION |
0x00000008 |
Root-of-trust version measurement. |
EV_TABLE_OF_DEVICES |
0x0000000B |
Platform configuration; on server-class platforms, Tcg2Dxe measures the processors’ EFI_CPU_PHYSICAL_LOCATION table into PCR 1 with it. |
EV_EFI_VARIABLE_DRIVER_CONFIG |
0x80000001 |
Variable configuration, including Secure Boot policy; EDK II hashes its serialized wrapper. |
EV_EFI_VARIABLE_BOOT |
0x80000002 |
Boot-variable measurement; distinguish logged wrapper from measured variable data. |
EV_EFI_BOOT_SERVICES_APPLICATION |
0x80000003 |
EFI application image measurement; the payload describes the image. |
EV_EFI_BOOT_SERVICES_DRIVER |
0x80000004 |
EFI boot-service driver image measurement. |
EV_EFI_RUNTIME_SERVICES_DRIVER |
0x80000005 |
EFI runtime driver image measurement. |
EV_EFI_GPT_EVENT |
0x80000006 |
Constructed GPT-header/partition-entry measurement. |
EV_EFI_ACTION |
0x80000007 |
A measured boot action, such as a boot attempt or boot-services transition. |
EV_EFI_PLATFORM_FIRMWARE_BLOB / BLOB2 |
0x80000008 / 0x8000000A |
Measured firmware range; payload describes the range. |
EV_EFI_VARIABLE_AUTHORITY |
0x800000E0 |
Authority used for an image-authorization decision. |
EV_EFI_SPDM_DEVICE_POLICY |
0x800000E3 |
The SPDM device signature database, measured into PCR 7 like a policy variable when SPDM device authentication is enabled. |
GetEventLog and the final-events handoff
Tcg2GetEventLog() returns the selected log’s starting physical address, the start of its last record, and a truncation flag. The last-entry pointer is not a one-past-the-end address or a byte length. To copy through that record, a consumer must parse its complete serialized size. Empty/no-log cases also need to be handled; this implementation can return success with zero addresses when no TPM is exposed.
The call does not stop measurement. In EDK II it marks the requested log format as having been retrieved. Subsequent ordinary log writes also enter the final-events area, published under gEfiTcg2FinalEventsTableGuid in the UEFI configuration tables.
UINT64 Version // 1 in this implementation
UINT64 NumberOfEvents
TCG_PCR_EVENT2 records // packed variable-length stream
This table has its own header. It is not another complete log beginning with a Spec ID record. The algorithm-size context comes from the corresponding main log.
The overlap starts at the first retrieval
The normal DXE log can continue growing while new events are mirrored to the final-events table. If another component called GetEventLog() earlier, some mirrored records can already be present in a later consumer’s main-log snapshot:
Main log copied by loader: A B C D E
Final-events table at copy: D E
Final-events table after exit: D E F G
Combined history: A B C D E F G
Appending the complete final table would extend D and E twice. Ignoring it would omit F and G. The consumer must track the overlap at its snapshot boundary and append only the later suffix. Removing all equal-looking records is also wrong because two identical extending events can be legitimate.
The Linux TPM event-log documentation describes its EFI stub saving the final-table size at the snapshot boundary so the kernel can skip that prefix during concatenation. It is a concrete example of the handoff problem, not a claim that Windows uses the same private table layout.
Extension and logging can fail independently
EFI_TCG2_EXTEND_ONLY, flag 0x1, requests extension without ordinary event logging. Raw TPM submission can also bypass the firmware log. Even without those paths, extension can succeed before the record is stored: Tcg2HashLogExtendEvent() documents EFI_VOLUME_FULL for a successful extend whose record could not be written to one or more logs.
The pinned DXE implementation attempts bounded dynamic log growth in its logging machinery and tracks truncation. Its final-events handling can append an EV_NO_ACTION truncation marker when space runs out. A marker explains incompleteness; it cannot reconstruct a missing extend input.
These conditions mean a readable, syntactically valid log can still be insufficient for replay. Keep the API’s truncation result, main/final-table boundary, boot mode and capture time with the bytes being analyzed.
Replaying a log and interpreting the result
For each bank and PCR, replay maintains one accumulator. It uses the recorded event digests in chronological order, with the correct initial state and event semantics. It does not hash the entire log file or concatenate all event payloads and hash once.
The following reduced example covers SHA-256 PCR 7 on a normal cold boot, after the full log has already been decoded and the final-event overlap resolved:
from hashlib import sha256
TPM_ALG_SHA256 = 0x000B
EV_NO_ACTION = 0x00000003
pcr = bytes(32)
for event in decoded_events:
if event.event_type == EV_NO_ACTION or event.pcr_index != 7:
continue
digest = event.digests[TPM_ALG_SHA256]
if len(digest) != 32:
raise ValueError("Invalid SHA-256 digest length")
pcr = sha256(pcr + digest).digest()
An absent required digest is an error here, not permission to use a zero digest. A general implementation additionally handles PCR 0 startup context, resume, resettable PCRs, dynamic launch, incomplete logs and the selected platform profile.
Replay consistency and measurement appraisal are separate checks
There are at least three distinct questions:
| Check | Evidence it establishes |
|---|---|
| Replay recorded digests and compare with PCR state | Whether those digest inputs explain that accumulated state. |
| Recompute a digest from the measured object under the producer’s rules | Whether an available object matches that event’s claimed measurement. |
| Appraise the resulting history against an accepted policy | Whether those versions, authorities, settings and transitions are acceptable. |
Replaying a PE event can succeed without possessing the PE file, because replay consumes its recorded digest. That alone does not authenticate the path string in its descriptive payload. An attacker who can edit only descriptive text may leave digest replay unchanged. A verifier must know which event fields are bound by the measurement and obtain reference objects or trusted manifests where needed.
Likewise, a matching digest history is not a proof that measured code contains no vulnerabilities, that all executable components were measured, or that firmware’s measuring code reported honest inputs. Those are root-of-trust, coverage and appraisal questions.
A PCR read and a quote provide different evidence
A local TPM2_PCR_Read supplies current register values. A remote verifier additionally needs an authenticated statement of the selected state and freshness. TPM2_Quote signs an attestation structure containing the PCR selection and a digest of selected PCR values, together with caller-supplied qualifying data used for a challenge. The TPM command specification defines that operation; EDK II’s TPMS_QUOTE_INFO and TPMS_ATTEST definitions show the separation of selection, digest and attestation context.
A verifier checks the attestation signature and trusted key identity, verifies the challenge and selection, and matches the replay result to the attested PCR digest under the quote’s algorithm rules. It then applies its acceptance policy. Signing a quote does not directly sign every byte of the external event log.
The capture point matters: a firmware-only log should not be expected to reproduce PCR values after the OS has added further measurements. Gather the matching history and PCR/quote state, or identify the boundary at which the comparison is valid.
Following the history into Windows
Firmware’s TCG log, Windows’ persisted measured-boot files and Windows Event Log diagnostics are related artifacts with different formats. A TPM error event in an .evtx file is not itself a TCG_PCR_EVENT2 record to extend during replay.
Microsoft documents persisted measurement histories under C:\Windows\Logs\MeasuredBoot and tools such as TBSLogGenerator.exe and PCPTool.exe for decoding collected logs. Windows measured-boot decoding
tpmtool gatherlogs can collect an analysis bundle, including available SRTM/DRTM boot and resume artifacts and TPM diagnostics. The following commands illustrate collection into a chosen local folder and inspection of the bank-selection metadata; they are not operations performed for this source analysis:
tpmtool getdeviceinformation
tpmtool gatherlogs C:\TpmResearch
Get-ItemProperty `
'HKLM:\SYSTEM\CurrentControlSet\Control\IntegrityServices' `
-Name TPMActivePCRBanks, TPMDigestAlgID
TPMActivePCRBanks is a bank bitmap; TPMDigestAlgID is the algorithm identifier Windows selected. Microsoft documents Windows preferring SHA-256 when firmware/log prerequisites are met, with SHA-1 fallback and a single bank used to continue Windows measurements. Windows bank selection
To diagnose a changed PCR, compare extending events for the same boot mode, bank and PCR in sequence. The first divergent digest often identifies the relevant measurement category; its type and producer identify how to interpret it. Comparing only the two final PCR strings reveals a difference but loses the information needed to explain it.
Platform policy and ACPI complete the handoff
TPM startup leaves additional platform responsibilities. Tcg2PlatformDxe registers a callback for the SMM-ready-to-lock protocol and invokes ConfigureTpmPlatformHierarchy() when that boundary is reached.
The hierarchy library bound by OVMF chooses its behavior through PcdRandomizePlatformHierarchy. One branch changes platform authorization to generated random bytes; the other disables the platform hierarchy. This is platform-hierarchy policy, separate from ordinary PCR extension and from OS provisioning of keys and storage.
The OS receives a device description
Platform ACPI code describes the TPM for the OS’s own driver. The path examined here is SecurityPkg/Tcg/Tcg2Acpi; its inclusion and dependencies are platform choices, separate from the OVMF component overrides discussed earlier. In Tcg2Acpi.PublishTpm2(), the cached active interface selects the ACPI start method:
| Regular interface | ACPI start method in this implementation |
|---|---|
| FIFO or legacy TIS | 6, the TIS start method. |
| CRB | 7, command-response buffer interface. |
For CRB, this code publishes the control area at PcdTpmBaseAddress + 0x40 and configures command and response buffers at base + 0x80, each with size 0xF80. The ACPI definitions also enumerate other start methods; these values describe the regular branches inspected here, not all TPM transports.
This publication is distinct from early firmware detection. PEI uses platform configuration and its bound device library to acquire the TPM. The loader consumes EFI_TCG2_PROTOCOL and the measurement log. After the boot-services handoff, the OS uses its native TPM stack and the platform’s device description.
An ACPI table therefore shows only that firmware published a description, not that every earlier startup and measurement succeeded.
Reconstructing the path in a firmware image
The source provides several independent anchors for binary analysis:
| Boundary | Useful source or binary anchors |
|---|---|
| Platform selection | Instance GUIDs, setup-variable access, device-selected PPI installation. |
| Device dispatch | PPI dependency expressions and the selected library/provider. |
| Transport | Configured MMIO base, access at +0x30, FIFO burst-count loops or CRB control writes. |
| Startup | Big-endian command code 0x00000144, the boot-mode branch and startup parameter. |
| Measurement policy | PCR capability queries, mask intersections, allocation requests and reset. |
| Phase handoff | TPM error HOB, event HOBs and shadowed PEIM entry. |
| DXE publication | TCG2 protocol installation, capability structure and log allocation. |
| OS discovery | ACPI TPM2 signature, start method and control-area address. |
The implementation is therefore best understood as a sequence of linked contracts: select a provider, obtain its transport, establish the appropriate startup state, configure usable measurement banks, record the measured boot, and publish the interfaces for the next boot stage. The platform bindings and boot mode decide which branches run.