mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-11-24 18:25:10 +00:00
chromeos-tpm-recovery: Convert to manual TPM reset script for developers
chromeos-tpm-recovery has not been used for anything in forever (see CL:238236), but it is still installed on every image. Resetting the TPM (e.g. to resolve rollback issues when reflashing an MP-signed device to dev firmware) is a common request by developers, and I get tired of always digging out the required tpmc commands manually again. Let's repurpose this script as a simple one-shot tool for developers to reset their TPM, so the next time someone asks we can just tell them 'boot a test image in recovery mode and run chromeos-tpm-recovery'. BRANCH=none BUG=chromium:419942 TEST=Ran on a Jerry, confirmed that TPM spaces were reset. Change-Id: Ia95246cfed3dc9b0c6fdb0481218e3ae14d8318a Signed-off-by: Julius Werner <jwerner@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/271512 Reviewed-by: Bill Richardson <wfrichar@chromium.org> Reviewed-by: Luigi Semenzato <semenzato@chromium.org>
This commit is contained in:
committed by
ChromeOS Commit Bot
parent
fb4e408011
commit
07e56f22b8
@@ -6,30 +6,17 @@
|
||||
# Run TPM diagnostics in recovery mode, and attempt to fix problems. This is
|
||||
# specific to devices with chromeos firmware.
|
||||
#
|
||||
# Usage: chromeos-tpm-recovery <log file>
|
||||
#
|
||||
# Most of the diagnostics examine the TPM state and try to fix it. This may
|
||||
# require clearing TPM ownership.
|
||||
|
||||
tpmc=${USR_BIN:=/usr/bin}/tpmc
|
||||
nvtool=${USR_LOCAL_BIN:=/usr/local/bin}/tpm-nvtool
|
||||
tpm_takeownership=${USR_LOCAL_SBIN:=/usr/local/sbin}/tpm_takeownership
|
||||
tcsd=${USR_SBIN:=/usr/sbin}/tcsd
|
||||
crossystem=${USR_BIN}/crossystem
|
||||
dot_recovery=${DOT_RECOVERY:=/mnt/stateful_partition/.recovery}
|
||||
acpi=${ACPI_DIR:=/sys/devices/platform/chromeos_acpi}
|
||||
awk=/usr/bin/awk
|
||||
|
||||
# At the time this script starts, we assume the following holds:
|
||||
#
|
||||
# - TPM may be owned, but not with the well-known password
|
||||
# - tcsd has not been started
|
||||
|
||||
tpm_owned_with_well_known_password=0
|
||||
tpm_unowned=0
|
||||
tcsd_pid=0
|
||||
initctl=/sbin/initctl
|
||||
|
||||
log() {
|
||||
echo "$(date): $*" >> $RECOVERY_LOG
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
quit() {
|
||||
@@ -42,192 +29,25 @@ log_tryfix() {
|
||||
log "$*: attempting to fix"
|
||||
}
|
||||
|
||||
# bit <n> <i> outputs bit i of number n, with bit 0 being the lsb.
|
||||
|
||||
bit () {
|
||||
echo $(( ( $1 >> $2 ) & 1 ))
|
||||
}
|
||||
|
||||
ensure_tcsd_is_running () {
|
||||
if [ $tcsd_pid = 0 ]; then
|
||||
$tcsd -f &
|
||||
tcsd_pid=$!
|
||||
sleep 2 # give tcsd time to initialize
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_tcsd_is_not_running () {
|
||||
if [ $tcsd_pid != 0 ]; then
|
||||
kill $tcsd_pid
|
||||
sleep 0.5
|
||||
kill $tcsd_pid > /dev/null 2>&1
|
||||
sleep 0.5
|
||||
wait $tcsd_pid > /dev/null 2>&1 # we trust that tcsd will agree to die
|
||||
tcsd_pid=0
|
||||
fi
|
||||
}
|
||||
|
||||
tpm_clear_and_reenable () {
|
||||
ensure_tcsd_is_not_running
|
||||
$tpmc clear
|
||||
$tpmc enable
|
||||
$tpmc activate
|
||||
tpm_owned_with_well_known_password=0
|
||||
tpm_unowned=1
|
||||
}
|
||||
|
||||
# We want the TPM owned with the well-known password.
|
||||
|
||||
ensure_tpm_is_owned () {
|
||||
if [ $tpm_owned_with_well_known_password = 0 ]; then
|
||||
tpm_clear_and_reenable
|
||||
ensure_tcsd_is_running
|
||||
$tpm_takeownership -y -z || log "takeownership failed with status $?"
|
||||
tpm_owned_with_well_known_password=1
|
||||
tpm_unowned=0
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_tpm_is_unowned () {
|
||||
if [ $tpm_unowned = 0 ]; then
|
||||
tpm_clear_and_reenable
|
||||
fi
|
||||
}
|
||||
|
||||
remove_space () {
|
||||
index=$1
|
||||
log "removing space $index"
|
||||
ensure_tpm_is_owned
|
||||
ensure_tcsd_is_running
|
||||
$nvtool --release --index "$index" --owner_password "" >> $RECOVERY_LOG 2>&1
|
||||
log "nvtool --release: status $?"
|
||||
}
|
||||
|
||||
# Makes some room by removing a TPM space it doesn't recognize. It would be
|
||||
# nice to let the user choose which space, but we may not have a UI.
|
||||
|
||||
make_room () {
|
||||
|
||||
# Check NVRAM spaces.
|
||||
AWK_PROGRAM=/tmp/tpm_recovery_$$.awk
|
||||
cat > $AWK_PROGRAM <<"EOF"
|
||||
/# NV Index 0xffffffff/ { next } # NV_INDEX_LOCK
|
||||
/# NV Index 0x00000000/ { next } # NV_INDEX0
|
||||
/# NV Index 0x00000001/ { next } # NV_INDEX_DIR
|
||||
/# NV Index 0x0000f.../ { next } # reserved for TPM use
|
||||
/# NV Index 0x0001..../ { next } # reserved for TCG WGs
|
||||
/# NV Index 0x00001007/ { next } # firmware space index
|
||||
/# NV Index 0x00001008/ { next } # kernel space index
|
||||
/# NV Index / { print $4 } #unexpected space
|
||||
EOF
|
||||
|
||||
local index
|
||||
|
||||
log "trying to make room by freeing one space"
|
||||
ensure_tcsd_is_running
|
||||
ensure_tpm_is_owned
|
||||
unexpected_spaces=$($nvtool --list | $awk -f $AWK_PROGRAM)
|
||||
|
||||
status=1
|
||||
|
||||
if [ "$unexpected_spaces" != "" ]; then
|
||||
log_tryfix "unexpected spaces: $unexpected_spaces"
|
||||
for index in $unexpected_spaces; do
|
||||
log "trying to remove space $index"
|
||||
if remove_space $(printf "0x%x" $(( $index )) ); then
|
||||
status=0
|
||||
break;
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
return $status
|
||||
}
|
||||
|
||||
# define_space <index> <size> <permissions>
|
||||
|
||||
define_space () {
|
||||
local index=$1
|
||||
local size=$2
|
||||
local permissions=$3
|
||||
# 0xf004 is for testing if there is enough room without side effects.
|
||||
local test_space=0xf004
|
||||
local perm_ppwrite=0x1
|
||||
local enough_room
|
||||
|
||||
ensure_tpm_is_unowned
|
||||
while true; do
|
||||
log "checking for NVRAM room for space with size $size"
|
||||
if $tpmc definespace $test_space $size $perm_ppwrite; then
|
||||
log "there is enough room"
|
||||
enough_room=1
|
||||
break
|
||||
else
|
||||
log "definespace $test_space $size failed with status $?"
|
||||
if ! make_room; then
|
||||
enough_room=0
|
||||
break
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if [ $enough_room -eq 0 ]; then
|
||||
log "not enough room to define space $index"
|
||||
return 1
|
||||
fi
|
||||
$tpmc definespace $index $size $permissions
|
||||
}
|
||||
|
||||
fix_space () {
|
||||
reset_space () {
|
||||
local index=$1
|
||||
local permissions=$2
|
||||
local size=$3
|
||||
local bytes="$4"
|
||||
|
||||
local space_exists=1
|
||||
|
||||
ensure_tcsd_is_not_running
|
||||
observed_permissions=$($tpmc getp $index | $awk '{print $5;}')
|
||||
if [ $? -ne 0 ]; then
|
||||
space_exists=0
|
||||
fi
|
||||
|
||||
# Check kernel space ID.
|
||||
if [ $space_exists -eq 1 -a $index = 0x1008 ]; then
|
||||
if ! $tpmc read 0x1008 0x5 | grep -q " 4c 57 52 47[ ]*$"; then
|
||||
log "bad kernel space id"
|
||||
remove_space $index
|
||||
space_exists=0
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check that space is large enough (we don't care if it's larger)
|
||||
if [ $space_exists -eq 1 ]; then
|
||||
if ! $tpmc read $index $size > /dev/null; then
|
||||
log "space $index read of size $size failed"
|
||||
remove_space $index
|
||||
space_exists=0
|
||||
fi
|
||||
fi
|
||||
|
||||
# If space exists but permissions are bad, delete the space.
|
||||
if [ $space_exists -eq 1 -a $observed_permissions != $permissions ]; then
|
||||
log "space $index has unexpected permissions $permissions"
|
||||
remove_space $index
|
||||
space_exists=0
|
||||
fi
|
||||
|
||||
# If space does not exist, reconstruct it.
|
||||
if [ $space_exists -eq 0 ]; then
|
||||
log_tryfix "space $index is gone"
|
||||
if ! define_space $index $size $permissions; then
|
||||
log "could not redefine space $index"
|
||||
return 1
|
||||
fi
|
||||
# do not quote "$bytes", as we mean to expand it here
|
||||
$tpmc write $index $bytes || log "writing to $index failed with code $?"
|
||||
log "space $index was recreated successfully"
|
||||
if ! $tpmc definespace $index $size $permissions; then
|
||||
log "could not redefine space $index"
|
||||
return 1
|
||||
fi
|
||||
# do not quote "$bytes", as we mean to expand it here
|
||||
$tpmc write $index $bytes || log "writing to $index failed with code $?"
|
||||
log "space $index was recreated successfully"
|
||||
}
|
||||
|
||||
|
||||
@@ -235,49 +55,27 @@ fix_space () {
|
||||
# MAIN PROGRAM
|
||||
# ------------
|
||||
|
||||
# Set up logging and announce ourselves.
|
||||
|
||||
if [ $# = 1 ]; then
|
||||
RECOVERY_LOG="$1"
|
||||
/usr/bin/logger "$0 started, output in $RECOVERY_LOG"
|
||||
log "starting $0"
|
||||
else
|
||||
/usr/bin/logger "$0 usage error"
|
||||
echo "usage: $0 <log file>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Sanity check: are we executing in a recovery image?
|
||||
|
||||
if [ ! -e $dot_recovery ]; then
|
||||
quit "not a recovery image"
|
||||
if [ -e $dot_recovery ]; then
|
||||
quit "This is a developer utility, it should never run on a (production) recovery image"
|
||||
fi
|
||||
|
||||
# Mnemonic: "B, I, N, F, O, and BINFO was his name-o."
|
||||
# Except it's a zero (0), not an O.
|
||||
BINF0=$acpi/BINF.0
|
||||
CHSW=$acpi/CHSW
|
||||
# Did the firmware keep the TPM unlocked?
|
||||
|
||||
# There is no point running unless this a ChromeOS device.
|
||||
|
||||
if [ ! -e $BINF0 ]; then
|
||||
log "not a chromeos device, exiting"
|
||||
exit 0
|
||||
if ! $($crossystem mainfw_type?recovery); then
|
||||
quit "You must put a test image on a USB stick and boot it in recovery mode to run this"
|
||||
fi
|
||||
|
||||
BOOT_REASON=$(cat $BINF0)
|
||||
log "boot reason is $BOOT_REASON"
|
||||
# tcsd may or may not be running
|
||||
|
||||
# Sanity check: did we boot in recovery mode?
|
||||
|
||||
if ! echo $BOOT_REASON | grep -q "^[345678]$"; then
|
||||
quit "unexpected boot reason $BOOT_REASON"
|
||||
fi
|
||||
|
||||
# Do we even have these tools in the image?
|
||||
|
||||
if [ ! -e $tpmc -o ! -e $nvtool -o ! -e $tpm_takeownership ]; then
|
||||
quit "tpmc or nvtool or tpm_takeownership are missing"
|
||||
log "Stopping tcsd..."
|
||||
if $initctl stop tcsd >/dev/null 2>/dev/null; then
|
||||
tcsd_was_running=1
|
||||
log "...done"
|
||||
else
|
||||
tcsd_was_running=0
|
||||
log "(already stopped)"
|
||||
fi
|
||||
|
||||
# Is the state of the PP enable flags correct?
|
||||
@@ -305,33 +103,21 @@ if $tpmc getvf | grep -q "physicalPresence 0"; then
|
||||
fi
|
||||
fi
|
||||
|
||||
DEV_MODE_NOW=$(bit $(cat $CHSW) 4)
|
||||
DEV_MODE_AT_BOOT=$(bit $(cat $CHSW) 5)
|
||||
# I never learned what this does, but it's probably good just in case...
|
||||
tpm_clear_and_reenable
|
||||
|
||||
# Check that bGlobalLock is unset
|
||||
|
||||
if [ $DEV_MODE_NOW != $DEV_MODE_AT_BOOT ]; then
|
||||
# this is either too weird or malicious, so we give up
|
||||
quit "dev mode is $DEV_MODE_NOW, but was $DEV_MODE_AT_BOOT at boot"
|
||||
fi
|
||||
|
||||
BGLOBALLOCK=$($tpmc getvf | $awk '/bGlobalLock/ {print $2;}')
|
||||
|
||||
if [ 0 -ne $BGLOBALLOCK ]; then
|
||||
# this indicates either TPM malfunction or firmware malfunction.
|
||||
log "bGlobalLock is $BGLOBALLOCK (dev mode is $DEV_MODE_NOW)."
|
||||
fi
|
||||
|
||||
# Check firmware and kernel spaces
|
||||
fix_space 0x1007 0x8001 0xa "01 00 00 00 00 00 00 00 00 00" || \
|
||||
# Reset firmware and kernel spaces to default (rollback version 1/1)
|
||||
reset_space 0x1007 0x8001 0xa "02 00 01 00 01 00 00 00 00 4f" || \
|
||||
log "could not fix firmware space"
|
||||
fix_space 0x1008 0x1 0xd "01 4c 57 52 47 00 00 00 00 00 00 00 00" || \
|
||||
reset_space 0x1008 0x1 0xd "02 4c 57 52 47 01 00 01 00 00 00 00 55" || \
|
||||
log "could not fix kernel space"
|
||||
# Don't need valid data in backup space, vboot can reset it as long as it exists
|
||||
reset_space 0x1009 0x1 0x10 "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" || \
|
||||
log "could not fix backup space"
|
||||
|
||||
# Cleanup: don't leave the tpm owned with the well-known password.
|
||||
if [ $tpm_owned_with_well_known_password -eq 1 ]; then
|
||||
tpm_clear_and_reenable
|
||||
if [ $tcsd_was_running != 0 ]; then
|
||||
echo Restarting tcsd...
|
||||
$initctl start tcsd >/dev/null
|
||||
fi
|
||||
|
||||
ensure_tcsd_is_not_running
|
||||
log "tpm recovery has completed"
|
||||
log "TPM has successfully been reset to factory defaults"
|
||||
|
||||
Reference in New Issue
Block a user