From Trammell Hudson's Projects
This page attempts to document what I've found about the modern era of CPU bootstrap and how we can use it to make slightly more secure systems. Most discussion of the boot process assume that the CPUs startup exactly the same as they were in the 1970s, which is only sort of true.
The legacy reset vector at 0xF:FFF0 is no longer the first instruction the x86 CPU executes. Instead the Intel Management Engine (ME) arranges for the CPU microcode to validate and execute an "Authenticated Code Module" (ACM) from the Firmware Interface Table (FIT). Once all of the ACMs in the FIT have run, the legacy reset vector is invoked and the system startup proceeds like its 1979.
To be written. FITC, Profiles, CPU fuses, PCH straps, ME interaction.
When the OEM receives the CPU, the ME is still in "Manufacturing Mode" and runs a special part of the firmware that will copy the "OEM Public Key Hash" and "Boot Guard Profile Configuration" policy values from its section of the flash ROM into the CPU fuses so that they are permanent and unchangable. It then sets a fuse to indicate that it has exited from manufacturing mode so that this portion of the firmware will not run again.
The "OEM Public Key Hash" is a SHA-256 hash of the key that is used to sign the "BIOS-SM" ACM and the Boot Guard has four policy settings:
- Force Boot Guard ACM Enabled: if set, there *must* be an OEM signed ACM in the FIT
- Verified Boot Enabled: if set, the platform will do a "verified boot", not sure yet exactly what that entails. Thinkpads set this bit.
- Measured Boot Enabled: if set, the platform will do a "measured boot", which I believe means *setting* PCR0 to the literal value "0x3", then extending it with the normal hash of the boot block, the ACMs and the next stages. Thinkpads do not set this bit.
- Protect BIOS Environment Enabled: if set, I believe this means that the ME will copy the IBB into the CPU cache so that it runs in a "cache-as-RAM" mode and has all DMA disabled to prevent devices from being able to modify it.
These values can be adjusted in the flash image with the Intel Flash Image Tool (FITC), but unless you have a way to force the ME into manufacturing mode then the values in the flash image are ignored. Since it is the public key hash, instead of the public key, corrupting it won't yield a factorizable key.
Based on what I've heard, but not seen documented, the verification of the OEM public key hash and the OEM signature on the FIT is done by the CPU microcode before it comes out of reset. I need to find this documented somewhere.
Firmware Interface Table
The FIT structure is defined in edk2's FirmwareInterfaceTable.h and the FIT pointer is usually at 0xFFFFFFC0 in the ROM. Verifying that it points to the table is easy since it starts with the header '_FIT_ '. It contains entries for the microcode updates, boot policys, TXT policy and other things. The one that we are most concerned with is the STARTUP_ACM.
00e1ce00: 5f 46 49 54 5f 20 20 20 0a 00 00 00 00 01 80 20 _FIT_ ....... 00e1ce10: 00 22 df ff 00 00 00 00 00 00 00 00 00 01 01 00 .".............. 00e1ce20: 00 66 df ff 00 00 00 00 00 00 00 00 00 01 01 00 .f.............. 00e1ce30: 00 aa df ff 00 00 00 00 00 00 00 00 00 01 01 00 ................ 00e1ce40: 00 ea df ff 00 00 00 00 00 00 00 00 00 01 01 00 ................ 00e1ce50: 00 42 e0 ff 00 00 00 00 00 00 00 00 00 01 01 00 .B.............. 00e1ce60: 00 00 e2 ff 00 00 00 00 00 00 00 00 00 01 02 00 ................ 00e1ce70: 00 00 ed ff 00 00 00 00 00 30 01 00 00 01 07 00 .........0...... 00e1ce80: 00 d0 e1 ff 00 00 00 00 41 02 00 00 00 01 0b 00 ........A....... 00e1ce90: 00 e0 e1 ff 00 00 00 00 bb 02 00 00 00 01 0c 00 ................ 00e1cea0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
This table decodes (note that the length fields are in counts of 16-bytes each):
1: address ffdf2200 @ 00000000: ver 0100 type Microcode (0x01) 2: address ffdf6600 @ 00000000: ver 0100 type Microcode (0x01) 3: address ffdfaa00 @ 00000000: ver 0100 type Microcode (0x01) 4: address ffdfea00 @ 00000000: ver 0100 type Microcode (0x01) 5: address ffe04200 @ 00000000: ver 0100 type Microcode (0x01) 6: address ffe20000 @ 00000000: ver 0100 type Startup ACM (0x02) 7: address ffed0000 @ 00130000: ver 0100 type BIOS Startup Module (0x07) 8: address ffe1d000 @ 00002410: ver 0100 type Key Manifest (0x0b) 9: address ffe1e000 @ 00002bb0: ver 0100 type Boot Policy Manifest (0x0c)
More information on the Intel Microcode update format is available, although we're treating it as a black box. As long as it is measured, we don't care too much for now.
The Startup ACM doesn't have a length in this table, so it must be encoded in the entry itself. There appears to be a 512-byte public key at offset 0x80 with an exponent of 17 (0x11) and a 512-byte signature. This does not appear to match the hash in the boot guard profile, but the same 512-byte key appears in the Boot Policy in the FIT (at offset 0x2080).
This code appears to run in 32-bit mode with references to a stack. The ME might enable "cache-as-RAM" mode, which allows the CPU to use the cache in non-writeback mode as if it were RAM, long before the memory controllers have been brought online.
In the one that I've analyzed there are SHA1 and SHA256 implementations (easily spotted due to their constants 0x67452301 and 0x06a09e667), and it appears to talk directly to the TPM at address 0xfed40000 and interacts with the TXT private configuration registers at 0xfed20000.
When it is done I believe it calls GETSEC[EXITAC] (%eax=3), which will shutdown the ACM and jump to a near pointer to resume normal startup. There is a lengthy function that finds this address
Early device memory mappings (copied from Intel's "BIOS Memory Map"):
PCI Express: 0xE0000000 - 0x10000000 IO APIC: 0xFEC00000 - 0x1000 HPET: 0xFED00000 - 0x400 MCH BAR: 0xFED10000 - 0x8000 DMI BAR: 0xFED18000 - 0x1000 EGP BAR: 0xFED19000 - 0x1000 RCBA: 0xFED1C000 - 0x4000 Intel TXT priv: 0xFED20000 - 0x10000 (private config registers) Intel TXT pub: 0xFED30000 - 0x10000 (public status registers) TPM: 0xFED40000 - 0x5000 TPM locality x: 0xFED4x000 - 0x1000 (one page for localities 0-4) Intel PTT: 0xFED70000 - 0x1000 Intel VTd: 0xFED90000 - 0x4000 Local APIC: 0xFEE00000 - 0x100000 Flash: 0xFF000000 - 0x1000000
BIOS Startup Module
The "BIOS Startup Module" appears to be a normal UEFI FVH structure. There is probably a signature on the end, I haven't yet checked but the FITC tool says that this is supposed to be signed. This module is around 1.2MB and includes the usual list of PEI, S3 and SMM modules. It also includes the TPM driver, as well as graphics, USB, etc.
The one that I've examined (t550) starts at 0xFFED0000 and covers all the way to the top of 4GB, which includes the legacy reset vector at 0xF:FFF0. It is not clear to me where it is signed nor where the signature is checked, although it is definitely hashed and measured by the Startup ACM prior to being invoked.
The BIOS ACM has a public key that matches the startup ACM and what appears to be a signature, but it does not cover the entire module. It appears to only cover the 0x18c80 bytes starting at 0xFFFE0000, which does not include the legacy reset vector.
However, modifying the legacy reset vector prevents the system from starting, so it is included in a signature or hash somewhere that has yet to be determined.