blob: 5bb2ec6aa86da579f18ece91292fdaf5f4345e9d [file] [log] [blame]
Luigi Semenzato3e3704f2010-10-25 12:36:03 -07001#!/bin/sh -u
2# Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5#
6# Run TPM diagnostics in recovery mode, and attempt to fix problems. This is
7# specific to devices with chromeos firmware.
8#
Luigi Semenzato900ce4b2010-11-02 14:04:24 -07009# Usage: chromeos-tpm-recovery <log file>
Luigi Semenzato3e3704f2010-10-25 12:36:03 -070010#
11# Most of the diagnostics examine the TPM state and try to fix it. This may
12# require clearing TPM ownership.
13
14tpmc=${USR_BIN:=/usr/bin}/tpmc
Luigi Semenzato35a6cb72010-11-02 10:37:16 -070015nvtool=${USR_LOCAL_BIN:=/usr/local/bin}/tpm-nvtool
16tpm_takeownership=${USR_LOCAL_SBIN:=/usr/local/sbin}/tpm_takeownership
17tcsd=${USR_SBIN:=/usr/sbin}/tcsd
Luigi Semenzato3e3704f2010-10-25 12:36:03 -070018dot_recovery=${DOT_RECOVERY:=/mnt/stateful_partition/.recovery}
19acpi=${ACPI_DIR:=/sys/devices/platform/chromeos_acpi}
20awk=/usr/bin/awk
21
22# At the time this script starts, we assume the following holds:
23#
24# - TPM may be owned, but not with the well-known password
25# - tcsd has not been started
26
27tpm_owned_with_well_known_password=0
28tpm_unowned=0
29tcsd_pid=0
30
31log() {
32 echo "$(date): $*" >> $RECOVERY_LOG
33}
34
35quit() {
36 log "ERROR: $*"
37 log "exiting"
38 exit 1
39}
40
41log_tryfix() {
42 log "$*: attempting to fix"
43}
44
45# bit <n> <i> outputs bit i of number n, with bit 0 being the lsb.
46
47bit () {
48 echo $(( ( $1 >> $2 ) & 1 ))
49}
50
51ensure_tcsd_is_running () {
52 if [ $tcsd_pid = 0 ]; then
53 $tcsd -f &
54 tcsd_pid=$!
Luigi Semenzato35a6cb72010-11-02 10:37:16 -070055 sleep 2 # give tcsd time to initialize
Luigi Semenzato3e3704f2010-10-25 12:36:03 -070056 fi
57}
58
59ensure_tcsd_is_not_running () {
60 if [ $tcsd_pid != 0 ]; then
61 kill $tcsd_pid
Luigi Semenzato35a6cb72010-11-02 10:37:16 -070062 sleep 0.5
63 kill $tcsd_pid > /dev/null 2>&1
64 sleep 0.5
Luigi Semenzato3e3704f2010-10-25 12:36:03 -070065 wait $tcsd_pid > /dev/null 2>&1 # we trust that tcsd will agree to die
66 tcsd_pid=0
67 fi
68}
69
70tpm_clear_and_reenable () {
71 ensure_tcsd_is_not_running
72 $tpmc clear
73 $tpmc enable
74 $tpmc activate
75 tpm_owned_with_well_known_password=0
76 tpm_unowned=1
77}
78
Luigi Semenzatoba04b8b2010-10-28 10:31:47 -070079# We want the TPM owned with the well-known password.
80
Luigi Semenzato3e3704f2010-10-25 12:36:03 -070081ensure_tpm_is_owned () {
Luigi Semenzatoba04b8b2010-10-28 10:31:47 -070082 if [ $tpm_owned_with_well_known_password = 0 ]; then
Luigi Semenzato3e3704f2010-10-25 12:36:03 -070083 tpm_clear_and_reenable
84 ensure_tcsd_is_running
85 $tpm_takeownership -y -z || log "takeownership failed with status $?"
86 tpm_owned_with_well_known_password=1
87 tpm_unowned=0
88 fi
89}
90
91ensure_tpm_is_unowned () {
92 if [ $tpm_unowned = 0 ]; then
93 tpm_clear_and_reenable
94 fi
95}
96
97remove_space () {
98 index=$1
99 log "removing space $index"
100 ensure_tpm_is_owned
101 ensure_tcsd_is_running
102 $nvtool --release --index "$index" --owner_password "" >> $RECOVERY_LOG 2>&1
103 log "nvtool --release: status $?"
104}
105
106# Makes some room by removing a TPM space it doesn't recognize. It would be
107# nice to let the user choose which space, but we may not have a UI.
108
109make_room () {
110
111 # Check NVRAM spaces.
112 AWK_PROGRAM=/tmp/tpm_recovery_$$.awk
113 cat > $AWK_PROGRAM <<"EOF"
114/# NV Index 0xffffffff/ { next } # NV_INDEX_LOCK
115/# NV Index 0x00000000/ { next } # NV_INDEX0
116/# NV Index 0x00000001/ { next } # NV_INDEX_DIR
117/# NV Index 0x0000f.../ { next } # reserved for TPM use
118/# NV Index 0x0001..../ { next } # reserved for TCG WGs
119/# NV Index 0x00001007/ { next } # firmware space index
120/# NV Index 0x00001008/ { next } # kernel space index
121/# NV Index / { print $4 } #unexpected space
122EOF
123
Luigi Semenzatoba04b8b2010-10-28 10:31:47 -0700124 local index
125
126 log "trying to make room by freeing one space"
Luigi Semenzato3e3704f2010-10-25 12:36:03 -0700127 ensure_tcsd_is_running
128 ensure_tpm_is_owned
129 unexpected_spaces=$($nvtool --list | $awk -f $AWK_PROGRAM)
130
131 status=1
132
Luigi Semenzatoba04b8b2010-10-28 10:31:47 -0700133 if [ "$unexpected_spaces" != "" ]; then
Luigi Semenzato3e3704f2010-10-25 12:36:03 -0700134 log_tryfix "unexpected spaces: $unexpected_spaces"
135 for index in $unexpected_spaces; do
Luigi Semenzatoba04b8b2010-10-28 10:31:47 -0700136 log "trying to remove space $index"
137 if remove_space $(printf "0x%x" $(( $index )) ); then
Luigi Semenzato3e3704f2010-10-25 12:36:03 -0700138 status=0
139 break;
140 fi
141 done
142 fi
143
144 return $status
145}
146
147# define_space <index> <size> <permissions>
148
149define_space () {
150 local index=$1
151 local size=$2
152 local permissions=$3
153 # 0xf004 is for testing if there is enough room without side effects.
154 local test_space=0xf004
155 local perm_ppwrite=0x1
156 local enough_room
157
158 ensure_tpm_is_unowned
159 while true; do
Luigi Semenzatoba04b8b2010-10-28 10:31:47 -0700160 log "checking for NVRAM room for space with size $size"
Luigi Semenzato3e3704f2010-10-25 12:36:03 -0700161 if $tpmc definespace $test_space $size $perm_ppwrite; then
Luigi Semenzatoba04b8b2010-10-28 10:31:47 -0700162 log "there is enough room"
Luigi Semenzato3e3704f2010-10-25 12:36:03 -0700163 enough_room=1
164 break
165 else
Luigi Semenzatoba04b8b2010-10-28 10:31:47 -0700166 log "definespace $test_space $size failed with status $?"
Luigi Semenzato3e3704f2010-10-25 12:36:03 -0700167 if ! make_room; then
168 enough_room=0
169 break
170 fi
171 fi
172 done
173
174 if [ $enough_room -eq 0 ]; then
175 log "not enough room to define space $index"
176 return 1
177 fi
178 $tpmc definespace $index $size $permissions
179}
180
181fix_space () {
182 local index=$1
183 local permissions=$2
184 local size=$3
185 local bytes="$4"
186
187 local space_exists=1
188
189 ensure_tcsd_is_not_running
190 observed_permissions=$($tpmc getp $index | $awk '{print $5;}')
191 if [ $? -ne 0 ]; then
192 space_exists=0
193 fi
194
195 # Check kernel space ID.
196 if [ $space_exists -eq 1 -a $index = 0x1008 ]; then
197 if ! $tpmc read 0x1008 0x5 | grep -q " 4c 57 52 47[ ]*$"; then
198 log "bad kernel space id"
199 remove_space $index
200 space_exists=0
201 fi
202 fi
203
204 # Check that space is large enough (we don't care if it's larger)
205 if [ $space_exists -eq 1 ]; then
206 if ! $tpmc read $index $size > /dev/null; then
207 log "space $index read of size $size failed"
208 remove_space $index
209 space_exists=0
210 fi
211 fi
212
213 # If space exists but permissions are bad, delete the space.
214 if [ $space_exists -eq 1 -a $observed_permissions != $permissions ]; then
215 log "space $index has unexpected permissions $permissions"
216 remove_space $index
217 space_exists=0
218 fi
219
220 # If space does not exist, reconstruct it.
221 if [ $space_exists -eq 0 ]; then
222 log_tryfix "space $index is gone"
223 if ! define_space $index $size $permissions; then
224 log "could not redefine space $index"
225 return 1
226 fi
227 # do not quote "$bytes", as we mean to expand it here
228 $tpmc write $index $bytes || log "writing to $index failed with code $?"
229 log "space $index was recreated successfully"
230 fi
231}
232
233
234# ------------
235# MAIN PROGRAM
236# ------------
237
238# Set up logging and announce ourselves.
239
240if [ $# = 1 ]; then
241 RECOVERY_LOG="$1"
242 /usr/bin/logger "$0 started, output in $RECOVERY_LOG"
Luigi Semenzatoba04b8b2010-10-28 10:31:47 -0700243 log "starting $0"
Luigi Semenzato3e3704f2010-10-25 12:36:03 -0700244else
245 /usr/bin/logger "$0 usage error"
246 echo "usage: $0 <log file>"
247 exit 1
248fi
249
250# Sanity check: are we executing in a recovery image?
251
252if [ ! -e $dot_recovery ]; then
253 quit "not a recovery image"
254fi
255
256# Mnemonic: "B, I, N, F, O, and BINFO was his name-o."
257# Except it's a zero (0), not an O.
258BINF0=$acpi/BINF.0
Luigi Semenzato35a6cb72010-11-02 10:37:16 -0700259CHSW=$acpi/CHSW
Luigi Semenzato3e3704f2010-10-25 12:36:03 -0700260
261# There is no point running unless this a ChromeOS device.
262
263if [ ! -e $BINF0 ]; then
264 log "not a chromeos device, exiting"
265 exit 0
266fi
267
268BOOT_REASON=$(cat $BINF0)
269log "boot reason is $BOOT_REASON"
270
271# Sanity check: did we boot in recovery mode?
272
273if ! echo $BOOT_REASON | grep -q "^[345678]$"; then
274 quit "unexpected boot reason $BOOT_REASON"
275fi
276
277# Do we even have these tools in the image?
278
279if [ ! -e $tpmc -o ! -e $nvtool -o ! -e $tpm_takeownership ]; then
280 quit "tpmc or nvtool or tpm_takeownership are missing"
281fi
282
283# Is the state of the PP enable flags correct?
284
285if ! ($tpmc getpf | grep -q "physicalPresenceLifetimeLock 1" &&
286 $tpmc getpf | grep -q "physicalPresenceHWEnable 0" &&
287 $tpmc getpf | grep -q "physicalPresenceCMDEnable 1"); then
288 log_tryfix "bad state of physical presence enable flags"
289 if $tpmc ppfin; then
290 log "physical presence enable flags are now correctly set"
291 else
292 quit "could not set physical presence enable flags"
293 fi
294fi
295
296# Is physical presence turned on?
297
298if $tpmc getvf | grep -q "physicalPresence 0"; then
299 log_tryfix "physical presence is OFF, expected ON"
300 # attempt to turn on physical presence
301 if $tpmc ppon; then
302 log "physical presence is now on"
303 else
304 quit "could not turn physical presence on"
305 fi
306fi
307
Luigi Semenzato35a6cb72010-11-02 10:37:16 -0700308DEV_MODE_NOW=$(bit $(cat $CHSW) 4)
309DEV_MODE_AT_BOOT=$(bit $(cat $CHSW) 5)
Luigi Semenzato3e3704f2010-10-25 12:36:03 -0700310
311# Check that bGlobalLock is unset
312
313if [ $DEV_MODE_NOW != $DEV_MODE_AT_BOOT ]; then
314 # this is either too weird or malicious, so we give up
315 quit "dev mode is $DEV_MODE_NOW, but was $DEV_MODE_AT_BOOT at boot"
316fi
317
318BGLOBALLOCK=$($tpmc getvf | $awk '/bGlobalLock/ {print $2;}')
319
320if [ 0 -ne $BGLOBALLOCK ]; then
321 # this indicates either TPM malfunction or firmware malfunction.
322 log "bGlobalLock is $BGLOBALLOCK (dev mode is $DEV_MODE_NOW)."
323fi
324
325# Check firmware and kernel spaces
326fix_space 0x1007 0x8001 0xa "01 00 00 00 00 00 00 00 00 00" || \
327 log "could not fix firmware space"
328fix_space 0x1008 0x1 0xd "01 4c 57 52 47 00 00 00 00 00 00 00 00" || \
329 log "could not fix kernel space"
330
331# Cleanup: don't leave the tpm owned with the well-known password.
332if [ $tpm_owned_with_well_known_password -eq 1 ]; then
333 tpm_clear_and_reenable
334fi
335
336ensure_tcsd_is_not_running
Luigi Semenzato35a6cb72010-11-02 10:37:16 -0700337log "tpm recovery has completed"