xref: /aosp_15_r20/external/vboot_reference/utility/chromeos-tpm-recovery (revision 8617a60d3594060b7ecbd21bc622a7c14f3cf2bc)
1*8617a60dSAndroid Build Coastguard Worker#!/bin/sh -u
2*8617a60dSAndroid Build Coastguard Worker# Copyright 2010 The ChromiumOS Authors
3*8617a60dSAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be
4*8617a60dSAndroid Build Coastguard Worker# found in the LICENSE file.
5*8617a60dSAndroid Build Coastguard Worker#
6*8617a60dSAndroid Build Coastguard Worker# Run TPM diagnostics in recovery mode, and attempt to fix problems.  This is
7*8617a60dSAndroid Build Coastguard Worker# specific to devices with chromeos firmware.
8*8617a60dSAndroid Build Coastguard Worker#
9*8617a60dSAndroid Build Coastguard Worker# Most of the diagnostics examine the TPM state and try to fix it.  This may
10*8617a60dSAndroid Build Coastguard Worker# require clearing TPM ownership.
11*8617a60dSAndroid Build Coastguard Worker
12*8617a60dSAndroid Build Coastguard Workertpmc=${USR_BIN:=/usr/bin}/tpmc
13*8617a60dSAndroid Build Coastguard Workercrossystem=${USR_BIN}/crossystem
14*8617a60dSAndroid Build Coastguard Workerdot_recovery=${DOT_RECOVERY:=/mnt/stateful_partition/.recovery}
15*8617a60dSAndroid Build Coastguard Workerawk=/usr/bin/awk
16*8617a60dSAndroid Build Coastguard Workerinitctl=/sbin/initctl
17*8617a60dSAndroid Build Coastguard Workerdaemon_was_running=
18*8617a60dSAndroid Build Coastguard Workererr=0
19*8617a60dSAndroid Build Coastguard Workersecdata_firmware=0x1007
20*8617a60dSAndroid Build Coastguard Workersecdata_kernel=0x1008
21*8617a60dSAndroid Build Coastguard Worker
22*8617a60dSAndroid Build Coastguard Workertpm2_target() {
23*8617a60dSAndroid Build Coastguard Worker  # This is not an ideal way to tell if we are running on a tpm2 target, but
24*8617a60dSAndroid Build Coastguard Worker  # it will have to do for now.
25*8617a60dSAndroid Build Coastguard Worker  if [ -f "/etc/init/trunksd.conf" ]; then
26*8617a60dSAndroid Build Coastguard Worker    return 0
27*8617a60dSAndroid Build Coastguard Worker  else
28*8617a60dSAndroid Build Coastguard Worker    return 1
29*8617a60dSAndroid Build Coastguard Worker  fi
30*8617a60dSAndroid Build Coastguard Worker}
31*8617a60dSAndroid Build Coastguard Worker
32*8617a60dSAndroid Build Coastguard Workeruse_v0_secdata_kernel() {
33*8617a60dSAndroid Build Coastguard Worker  local fwid="$(crossystem ro_fwid)"
34*8617a60dSAndroid Build Coastguard Worker  local major="$(printf "$fwid" | cut -d. -f2)"
35*8617a60dSAndroid Build Coastguard Worker  local minor="$(printf "$fwid" | cut -d. -f3)"
36*8617a60dSAndroid Build Coastguard Worker
37*8617a60dSAndroid Build Coastguard Worker  # TPM1 firmware never supports the v1 kernel space format.
38*8617a60dSAndroid Build Coastguard Worker  if ! tpm2_target; then
39*8617a60dSAndroid Build Coastguard Worker    return 0
40*8617a60dSAndroid Build Coastguard Worker  fi
41*8617a60dSAndroid Build Coastguard Worker
42*8617a60dSAndroid Build Coastguard Worker  # First some validity checks: X -eq X checks that X is a number. cut may
43*8617a60dSAndroid Build Coastguard Worker  # return the whole string if no delimiter found, so major != minor checks that
44*8617a60dSAndroid Build Coastguard Worker  # the version was at least somewhat correctly formatted.
45*8617a60dSAndroid Build Coastguard Worker  if [ $major -eq $major ] && [ $minor -eq $minor ] && [ $major -ne $minor ]; then
46*8617a60dSAndroid Build Coastguard Worker    # Now what we really care about: is this firmware older than CL:2041695?
47*8617a60dSAndroid Build Coastguard Worker    if [ $major -lt 12953 ]; then
48*8617a60dSAndroid Build Coastguard Worker      return 0
49*8617a60dSAndroid Build Coastguard Worker    else
50*8617a60dSAndroid Build Coastguard Worker      return 1
51*8617a60dSAndroid Build Coastguard Worker    fi
52*8617a60dSAndroid Build Coastguard Worker  else
53*8617a60dSAndroid Build Coastguard Worker    log "Cannot parse FWID. Assuming local build that supports v1 kernel space."
54*8617a60dSAndroid Build Coastguard Worker    return 1
55*8617a60dSAndroid Build Coastguard Worker  fi
56*8617a60dSAndroid Build Coastguard Worker}
57*8617a60dSAndroid Build Coastguard Worker
58*8617a60dSAndroid Build Coastguard Workerlog() {
59*8617a60dSAndroid Build Coastguard Worker  echo "$*"
60*8617a60dSAndroid Build Coastguard Worker}
61*8617a60dSAndroid Build Coastguard Worker
62*8617a60dSAndroid Build Coastguard Workerquit() {
63*8617a60dSAndroid Build Coastguard Worker  log "ERROR: $*"
64*8617a60dSAndroid Build Coastguard Worker  restart_daemon_if_needed
65*8617a60dSAndroid Build Coastguard Worker  log "exiting"
66*8617a60dSAndroid Build Coastguard Worker
67*8617a60dSAndroid Build Coastguard Worker  exit 1
68*8617a60dSAndroid Build Coastguard Worker}
69*8617a60dSAndroid Build Coastguard Worker
70*8617a60dSAndroid Build Coastguard Workerlog_tryfix() {
71*8617a60dSAndroid Build Coastguard Worker  log "$*: attempting to fix"
72*8617a60dSAndroid Build Coastguard Worker}
73*8617a60dSAndroid Build Coastguard Worker
74*8617a60dSAndroid Build Coastguard Workerlog_error() {
75*8617a60dSAndroid Build Coastguard Worker  err=$((err + 1))
76*8617a60dSAndroid Build Coastguard Worker  log "ERROR: $*"
77*8617a60dSAndroid Build Coastguard Worker}
78*8617a60dSAndroid Build Coastguard Worker
79*8617a60dSAndroid Build Coastguard Worker
80*8617a60dSAndroid Build Coastguard Workerlog_warn() {
81*8617a60dSAndroid Build Coastguard Worker  log "WARNING: $*"
82*8617a60dSAndroid Build Coastguard Worker}
83*8617a60dSAndroid Build Coastguard Worker
84*8617a60dSAndroid Build Coastguard Workertpm_clear_and_reenable () {
85*8617a60dSAndroid Build Coastguard Worker  $tpmc clear
86*8617a60dSAndroid Build Coastguard Worker
87*8617a60dSAndroid Build Coastguard Worker  # The below commands are are no-op on tpm2, but let's keep them here for
88*8617a60dSAndroid Build Coastguard Worker  # both TPM versions in case they are implemented in the future for
89*8617a60dSAndroid Build Coastguard Worker  # version 2.
90*8617a60dSAndroid Build Coastguard Worker  $tpmc enable
91*8617a60dSAndroid Build Coastguard Worker  $tpmc activate
92*8617a60dSAndroid Build Coastguard Worker}
93*8617a60dSAndroid Build Coastguard Worker
94*8617a60dSAndroid Build Coastguard Workerwrite_space () {
95*8617a60dSAndroid Build Coastguard Worker  # do not quote "$2", as we mean to expand it here
96*8617a60dSAndroid Build Coastguard Worker  if ! $tpmc write $1 $2; then
97*8617a60dSAndroid Build Coastguard Worker    log_error "writing to $1 failed"
98*8617a60dSAndroid Build Coastguard Worker  else
99*8617a60dSAndroid Build Coastguard Worker    log "$1 written successfully"
100*8617a60dSAndroid Build Coastguard Worker  fi
101*8617a60dSAndroid Build Coastguard Worker}
102*8617a60dSAndroid Build Coastguard Worker
103*8617a60dSAndroid Build Coastguard Workerreset_ro_space () {
104*8617a60dSAndroid Build Coastguard Worker  local index=$1
105*8617a60dSAndroid Build Coastguard Worker  local bytes="$2"
106*8617a60dSAndroid Build Coastguard Worker  local size=$(printf "$bytes" | wc -w)
107*8617a60dSAndroid Build Coastguard Worker  local permissions=0x8001
108*8617a60dSAndroid Build Coastguard Worker
109*8617a60dSAndroid Build Coastguard Worker  if tpm2_target; then
110*8617a60dSAndroid Build Coastguard Worker    log "Cannot redefine RO space for TPM2 (b/140958855). Let's just hope it looks good..."
111*8617a60dSAndroid Build Coastguard Worker  else
112*8617a60dSAndroid Build Coastguard Worker    if ! $tpmc definespace $index $size $permissions; then
113*8617a60dSAndroid Build Coastguard Worker      log_error "could not redefine RO space $index"
114*8617a60dSAndroid Build Coastguard Worker      # try writing it anyway, just in case it works...
115*8617a60dSAndroid Build Coastguard Worker    fi
116*8617a60dSAndroid Build Coastguard Worker  fi
117*8617a60dSAndroid Build Coastguard Worker
118*8617a60dSAndroid Build Coastguard Worker  write_space $index "$bytes"
119*8617a60dSAndroid Build Coastguard Worker}
120*8617a60dSAndroid Build Coastguard Worker
121*8617a60dSAndroid Build Coastguard Workerreset_rw_space () {
122*8617a60dSAndroid Build Coastguard Worker  local index=$1
123*8617a60dSAndroid Build Coastguard Worker  local bytes="$2"
124*8617a60dSAndroid Build Coastguard Worker  local size=$(printf "$bytes" | wc -w)
125*8617a60dSAndroid Build Coastguard Worker  local permissions=0x1
126*8617a60dSAndroid Build Coastguard Worker
127*8617a60dSAndroid Build Coastguard Worker  if tpm2_target; then
128*8617a60dSAndroid Build Coastguard Worker    permissions=0x40050001
129*8617a60dSAndroid Build Coastguard Worker  fi
130*8617a60dSAndroid Build Coastguard Worker
131*8617a60dSAndroid Build Coastguard Worker  if ! $tpmc definespace $index $size $permissions; then
132*8617a60dSAndroid Build Coastguard Worker    log_error "could not redefine RW space $index"
133*8617a60dSAndroid Build Coastguard Worker    # try writing it anyway, just in case it works...
134*8617a60dSAndroid Build Coastguard Worker  fi
135*8617a60dSAndroid Build Coastguard Worker
136*8617a60dSAndroid Build Coastguard Worker  write_space $index "$bytes"
137*8617a60dSAndroid Build Coastguard Worker}
138*8617a60dSAndroid Build Coastguard Worker
139*8617a60dSAndroid Build Coastguard Workerrestart_daemon_if_needed() {
140*8617a60dSAndroid Build Coastguard Worker  if [ "$daemon_was_running" = 1 ]; then
141*8617a60dSAndroid Build Coastguard Worker    log "Restarting ${DAEMON}..."
142*8617a60dSAndroid Build Coastguard Worker    $initctl start "${DAEMON}" >/dev/null
143*8617a60dSAndroid Build Coastguard Worker  fi
144*8617a60dSAndroid Build Coastguard Worker}
145*8617a60dSAndroid Build Coastguard Worker
146*8617a60dSAndroid Build Coastguard Worker# ------------
147*8617a60dSAndroid Build Coastguard Worker# MAIN PROGRAM
148*8617a60dSAndroid Build Coastguard Worker# ------------
149*8617a60dSAndroid Build Coastguard Worker
150*8617a60dSAndroid Build Coastguard Worker# validity check: are we executing in a recovery image?
151*8617a60dSAndroid Build Coastguard Worker
152*8617a60dSAndroid Build Coastguard Workerif [ -e $dot_recovery ]; then
153*8617a60dSAndroid Build Coastguard Worker  quit "This is a developer utility, it should never run on a (production) recovery image"
154*8617a60dSAndroid Build Coastguard Workerfi
155*8617a60dSAndroid Build Coastguard Worker
156*8617a60dSAndroid Build Coastguard Worker# Did the firmware keep the TPM unlocked?
157*8617a60dSAndroid Build Coastguard Worker
158*8617a60dSAndroid Build Coastguard Workerif ! $($crossystem mainfw_type?recovery); then
159*8617a60dSAndroid Build Coastguard Worker  quit "You must put a test image on a USB stick and boot it in recovery mode (this means Esc+Refresh+Power, *not* Ctrl-U!) to run this"
160*8617a60dSAndroid Build Coastguard Workerfi
161*8617a60dSAndroid Build Coastguard Worker
162*8617a60dSAndroid Build Coastguard Workerif tpm2_target; then
163*8617a60dSAndroid Build Coastguard Worker  DAEMON="trunksd"
164*8617a60dSAndroid Build Coastguard Workerelse
165*8617a60dSAndroid Build Coastguard Worker  DAEMON="tcsd"
166*8617a60dSAndroid Build Coastguard Workerfi
167*8617a60dSAndroid Build Coastguard Worker
168*8617a60dSAndroid Build Coastguard Worker# TPM daemon may or may not be running
169*8617a60dSAndroid Build Coastguard Worker
170*8617a60dSAndroid Build Coastguard Workerlog "Stopping ${DAEMON}..."
171*8617a60dSAndroid Build Coastguard Workerif $initctl stop "${DAEMON}" >/dev/null 2>/dev/null; then
172*8617a60dSAndroid Build Coastguard Worker  daemon_was_running=1
173*8617a60dSAndroid Build Coastguard Worker  log "done"
174*8617a60dSAndroid Build Coastguard Workerelse
175*8617a60dSAndroid Build Coastguard Worker  daemon_was_running=0
176*8617a60dSAndroid Build Coastguard Worker  log "(was not running)"
177*8617a60dSAndroid Build Coastguard Workerfi
178*8617a60dSAndroid Build Coastguard Worker
179*8617a60dSAndroid Build Coastguard Worker# Is the state of the PP enable flags correct?
180*8617a60dSAndroid Build Coastguard Worker
181*8617a60dSAndroid Build Coastguard Workerif ! tpm2_target; then
182*8617a60dSAndroid Build Coastguard Worker  if ! ($tpmc getpf | grep -q "physicalPresenceLifetimeLock 1" &&
183*8617a60dSAndroid Build Coastguard Worker      $tpmc getpf | grep -q "physicalPresenceHWEnable 0" &&
184*8617a60dSAndroid Build Coastguard Worker      $tpmc getpf | grep -q "physicalPresenceCMDEnable 1"); then
185*8617a60dSAndroid Build Coastguard Worker    log_tryfix "bad state of physical presence enable flags"
186*8617a60dSAndroid Build Coastguard Worker    if $tpmc ppfin; then
187*8617a60dSAndroid Build Coastguard Worker      log "physical presence enable flags are now correctly set"
188*8617a60dSAndroid Build Coastguard Worker    else
189*8617a60dSAndroid Build Coastguard Worker      quit "could not set physical presence enable flags"
190*8617a60dSAndroid Build Coastguard Worker    fi
191*8617a60dSAndroid Build Coastguard Worker  fi
192*8617a60dSAndroid Build Coastguard Worker
193*8617a60dSAndroid Build Coastguard Worker  # Is physical presence turned on?
194*8617a60dSAndroid Build Coastguard Worker
195*8617a60dSAndroid Build Coastguard Worker  if $tpmc getvf | grep -q "physicalPresence 0"; then
196*8617a60dSAndroid Build Coastguard Worker    log_tryfix "physical presence is OFF, expected ON"
197*8617a60dSAndroid Build Coastguard Worker    # attempt to turn on physical presence
198*8617a60dSAndroid Build Coastguard Worker    if $tpmc ppon; then
199*8617a60dSAndroid Build Coastguard Worker      log "physical presence is now on"
200*8617a60dSAndroid Build Coastguard Worker    else
201*8617a60dSAndroid Build Coastguard Worker      quit "could not turn physical presence on"
202*8617a60dSAndroid Build Coastguard Worker    fi
203*8617a60dSAndroid Build Coastguard Worker  fi
204*8617a60dSAndroid Build Coastguard Workerelse
205*8617a60dSAndroid Build Coastguard Worker  if ! $tpmc getvf | grep -q 'phEnable 1'; then
206*8617a60dSAndroid Build Coastguard Worker    quit "Platform Hierarchy is disabled, TPM can't be recovered"
207*8617a60dSAndroid Build Coastguard Worker  fi
208*8617a60dSAndroid Build Coastguard Workerfi
209*8617a60dSAndroid Build Coastguard Worker
210*8617a60dSAndroid Build Coastguard Worker# I never learned what this does, but it's probably good just in case...
211*8617a60dSAndroid Build Coastguard Workertpm_clear_and_reenable
212*8617a60dSAndroid Build Coastguard Worker
213*8617a60dSAndroid Build Coastguard Worker# Reset firmware and kernel spaces to default (rollback version 1/1)
214*8617a60dSAndroid Build Coastguard Workerreset_ro_space $secdata_firmware "02  0  1 0 1 0  0 0 0  4f"
215*8617a60dSAndroid Build Coastguard Worker
216*8617a60dSAndroid Build Coastguard Workerif use_v0_secdata_kernel; then
217*8617a60dSAndroid Build Coastguard Worker  reset_rw_space $secdata_kernel "02  4c 57 52 47  1 0 1 0  0 0 0  55"
218*8617a60dSAndroid Build Coastguard Workerelse
219*8617a60dSAndroid Build Coastguard Worker  reset_rw_space $secdata_kernel "10  28  0c  0  1 0 1 0  0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"
220*8617a60dSAndroid Build Coastguard Workerfi
221*8617a60dSAndroid Build Coastguard Worker
222*8617a60dSAndroid Build Coastguard Workerrestart_daemon_if_needed
223*8617a60dSAndroid Build Coastguard Worker
224*8617a60dSAndroid Build Coastguard Workerif [ "$err" -eq 0 ]; then
225*8617a60dSAndroid Build Coastguard Worker  log "TPM has successfully been reset to factory defaults"
226*8617a60dSAndroid Build Coastguard Workerelse
227*8617a60dSAndroid Build Coastguard Worker  log_error "TPM was not fully recovered."
228*8617a60dSAndroid Build Coastguard Worker  exit 1
229*8617a60dSAndroid Build Coastguard Workerfi
230