I’ve been learning a bit about UEFI and the new TPM2 specifications these past few weeks. My work in this space isn’t ready for public consumption but I’ve run into some interesting data worth mentioning here. I got really stuck on this on account of my UEFI stills being very weak. Thankfully others ran into the same issue and shared their knowledge. I’m putting this up in the hopes of keeping others from falling down the same hole that took up so much of my time.

Dueling Specifications

When I started poking at the 2.0 TPM on my Minnowboard Max, knowing the interplay between the Microsoft TrEE and the TCG TPM 2.0 EFI Protocol specifications would have saved me a pile of time. AFAIK Microsoft is a very big contributor to the TPM 2.0 specs and Windows 8 and 10 both build on it for security infrastructure. The new TPM specs from the TCG are still in draft form and out for pubic review which is good, but Microsoft is shipping real live systems that use with version 2.0 TPMs.

I don’t participate in any of the TCG stuff other than writing software that uses the TPM so I’ve got no inside knowledge here, but I speculate that it’s hard to ship systems when a core component doesn’t have a published interface specification. So what to do? Microsoft published their own version of the TPM UEFI version 2 Protocol specification under the Trusted Execution Environment EFI Protocol name. You can argue that this isn’t the same as the TCG EFI Protocol Specification for TPM Family 2.0 Revision 1.0 but by UEFI law these two protocols have the same GUID and so they’re the same protocol. Period.

Same GUID, Subtle Differences

So Microsoft published a version of the specification early to support their customers and developer community. I don’t see that they had any other choice. But how does this cause problems you ask? Grab your Minnowboard Max, install the 32bit firmware so you get the Intel PTT (firmware TPM2), code up an EFI application that calls the GetCapability function from the protocol and you’ll see. The short version is that there is a subtle difference in the two specs that causes incompatibility. The TCG spec clearly states that all structures in the spec are packed. The Microsoft spec on the other hand says nothing about struct packing outside of the code samples. Their samples however use macros #pragma pack(1) to show which structures should be packed and the TREE_BOOT_SERVICE_CAPABILITY (synonymous with the TCG EFI_TCG2_BOOT_SERVICE_CAPABILITY structure) is not defined with this macro in effect.

The end result is that since Microsoft is (AFAIK) the only platform currently supporting TPM2, all TPM2 hardware / firmware out there implements their TrEE spec. The UEFI protocol implemented on these platforms will return to you an unpacked struct when you call the GetCapability function and if you’re like me and you’re working from the TCG draft spec you’ll be banging your head against the wall for a bit.

Lessons Learned

Pretty interesting first exposure to UEFI and TPM2. Painful in that my UEFI debugging skills are pretty weak. Interesting caus I’ve learned a ton. Checking structure packing is now at the top of my debugging check list. It’s also very interesting to see dueling specs like this. The reality of having to ship hardware / firmware / software before a specification is finalized has got to be a tricky business so it’s hard to fault Microsoft for this.

The relevant upstreams know about this issue thanks to Matthew Garrett and I’m sure they’ll resolve it before the TCG spec is finalized. Most likely the TCG draft spec will be updated and the capability structure will officially not be packed. Don’t bank on this though. YMMV and every other possible disclaimer.

An additional foot note is that the current TCG spec prescribes conflicting version numbers for the StructureVersion and ProtocolVersion fields in the capability structure too. I’ve reported this as part of the public review process as well.