diff options
author | Julius Werner <jwerner@chromium.org> | 2015-01-06 10:02:46 -0800 |
---|---|---|
committer | ChromeOS Commit Bot <chromeos-commit-bot@chromium.org> | 2015-05-16 01:42:28 +0000 |
commit | 07e56f22b852f4d96af2b2df57da11f6938778dd (patch) | |
tree | 5c55ca659e9962b20b55eda6d4633b9101f0c7f4 /utility | |
parent | fb4e4080112d9005f83b57551ab19cbf478da36a (diff) | |
download | vboot-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>
Diffstat (limited to 'utility')
-rwxr-xr-x | utility/chromeos-tpm-recovery | 284 |
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" |