summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulius Werner <jwerner@chromium.org>2015-01-06 10:02:46 -0800
committerChromeOS Commit Bot <chromeos-commit-bot@chromium.org>2015-05-16 01:42:28 +0000
commit07e56f22b852f4d96af2b2df57da11f6938778dd (patch)
tree5c55ca659e9962b20b55eda6d4633b9101f0c7f4
parentfb4e4080112d9005f83b57551ab19cbf478da36a (diff)
downloadvboot-07e56f22b852f4d96af2b2df57da11f6938778dd.tar.gz
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>
-rwxr-xr-xutility/chromeos-tpm-recovery284
1 files changed, 35 insertions, 249 deletions
diff --git a/utility/chromeos-tpm-recovery b/utility/chromeos-tpm-recovery
index 5bb2ec6a..51900953 100755
--- a/utility/chromeos-tpm-recovery
+++ b/utility/chromeos-tpm-recovery
@@ -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"
-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
-
-# There is no point running unless this a ChromeOS device.
-
-if [ ! -e $BINF0 ]; then
- log "not a chromeos device, exiting"
- exit 0
+if [ -e $dot_recovery ]; then
+ quit "This is a developer utility, it should never run on a (production) recovery image"
fi
-BOOT_REASON=$(cat $BINF0)
-log "boot reason is $BOOT_REASON"
+# Did the firmware keep the TPM unlocked?
-# Sanity check: did we boot in recovery mode?
-
-if ! echo $BOOT_REASON | grep -q "^[345678]$"; then
- quit "unexpected boot reason $BOOT_REASON"
+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
-# Do we even have these tools in the image?
+# tcsd may or may not be running
-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)
-
-# 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
+# I never learned what this does, but it's probably good just in case...
+tpm_clear_and_reenable
-# 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"