summaryrefslogtreecommitdiff
path: root/scripts/image_signing/tofactory.sh
blob: 1c5f3bdd3869325e44dcb6d44595537d240e7ddc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
#!/bin/sh
#
# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
#
# This script converts a Chrome OS device to a pre-factory-install state:
#  * Firmware write protect disabled
#  * H2O BIOS, with RO VPD area copied from the current BIOS
#  * Original EC firmware
#  * Blank SSD (no GPT)
#
# Minimal usage:
#   tofactory.sh -b H2OBIOS.bin -e ec_shellball.sh

SCRIPT_BASE="$(dirname "$0")"
. "$SCRIPT_BASE/common_minimal.sh"
load_shflags || exit 1

# Constants used by DEFINE_*
VBOOT_BASE='/usr/share/vboot'

# DEFINE_string name default_value description flag
DEFINE_string bios "" "Path of system firmware (BIOS) binary to write" "b"
DEFINE_string ec "" "Path of EC shellball to execute" "e"
DEFINE_string backup_dir "" "Path of directory in whoch to store backups" "k"
DEFINE_string asset_tag "unspecified_tag" \
  "Asset tag of device, used to name backups" "a"
DEFINE_string ssd "/dev/sda" "Path to SSD / target drive" "s"
DEFINE_boolean wipe_ssd $FLAGS_TRUE "Wipe SSD after firmware updates" ""
DEFINE_boolean nothing $FLAGS_FALSE \
  "Print commands but do not modify device" "n"

# Parse command line
FLAGS "$@" || exit 1
eval set -- "$FLAGS_ARGV"

# Globals
# ----------------------------------------------------------------------------
set -e

# Flashrom commands with device overrides
FLASHROM_BIOS="flashrom -p internal:bus=spi"
FLASHROM_EC="flashrom -p internal:bus=lpc"

# A log file to keep the output results of executed command
EXEC_LOG="$(make_temp_file)"

# Temporary Work directory
WORK_DIR="$(make_temp_dir)"
OLD_BIOS="$WORK_DIR/old_bios.bin"
NEW_BIOS="$WORK_DIR/new_bios.bin"

# Functions
# ----------------------------------------------------------------------------

# Error message for write protect disable failure, with reminder
wp_error() {
  local which_rom=$1
  shift
  echo "ERROR: Unable to disable $which_rom write protect: $*" 1>&2
  echo "Is hardware write protect still enabled?" 1>&2
  exit 1
}

# Disable write protect for an EEPROM
disable_wp() {
  local which_rom=$1  # EC or BIOS
  shift
  local flash_rom="$*"  # Flashrom command to use

  debug_msg "Disabling $which_rom write protect"
  $NOTHING ${flash_rom} --wp-disable || wp_error "$which_rom" "--wp-disable"
  $NOTHING ${flash_rom} --wp-range 0 0 || wp_error "$which_rom" "--wp-range"

  # WP status bits should report WP: status: 0x00
  local wp_status="$(${flash_rom} --wp-status | grep "WP: status:")"
  if [ "$wp_status" != "WP: status: 0x00" ]; then
    if [ "$FLAGS_nothing" = $FLAGS_FALSE ]; then
      wp_error "$which_rom" "$wp_status"
    fi
  fi
}

# Back up current firmware and partition table
make_backups() {
  debug_msg "Backing up current firmware to $FLAGS_backup_dir"
  mkdir -p "$FLAGS_backup_dir"
  cp "$OLD_BIOS" "$FLAGS_backup_dir/$FLAGS_asset_tag.bios.bin"
  ${FLASHROM_EC} -r "$FLAGS_backup_dir/$FLAGS_asset_tag.ec.bin"

  # Copy the VPD info from RAM, since we can't extract it as text
  # from the BIOS binary.  Failure of this is only a warning, since
  # the information is still in the old BIOS.
  mosys vpd print all > "$FLAGS_backup_dir/$FLAGS_asset_tag.vpd.txt" ||
    echo "WARNING: unable to save VPD as text."

  # Copy the first part of the drive, so we can recreate the partition
  # table.
  local gpt_backup="$FLAGS_backup_dir/$FLAGS_asset_tag.gpt.bin"
  debug_msg "Backing up current GPT table."
  dd if="$FLAGS_ssd" of="$gpt_backup" bs=512 count=34

  # Add a script to restore the BIOS and GPT
  local restore_script="$FLAGS_backup_dir/$FLAGS_asset_tag.restore.sh"
  cat >"$restore_script" <<EOF
#!/bin/sh
echo "Restoring BIOS"
${FLASHROM_BIOS} -w "$FLAGS_asset_tag.bios.bin"
echo "Restoring EC"
${FLASHROM_EC} -w "$FLAGS_asset_tag.ec.bin"
echo "Restoring GPT"
dd of="$FLAGS_ssd" if="$FLAGS_asset_tag.gpt.bin" conv=notrunc
EOF
  echo "To restore original BIOS and SSD:"
  echo "  cd $FLAGS_backup_dir && sh $FLAGS_asset_tag.restore.sh"
}

# Main
# ----------------------------------------------------------------------------

main() {
  # Make sure the files we were passed exist
  [ -n "$FLAGS_bios" ] || err_die "Please specify a BIOS file (-b bios.bin)"
  [ -n "$FLAGS_ec" ] || err_die "Please specify an EC updater (-e updater.sh)"
  ensure_files_exist "$FLAGS_bios" "$FLAGS_ec" || exit 1

  # If --nothing was specified, keep flashrom from writing
  if [ "$FLAGS_nothing" = $FLAGS_TRUE ]; then
    NOTHING="echo Not executing: "
  fi

  # Stop update engine before calling flashrom.  Multiple copies of flashrom
  # interfere with each other.
  debug_msg "Stopping update engine"
  initctl stop update-engine

  # Read the current firmware
  debug_msg "Reading BIOS from EEPROM"
  ${FLASHROM_BIOS} -r "$OLD_BIOS"

  # Copy current info to the backup dir, if specified
  if [ -n "$FLAGS_backup_dir" ]; then
    make_backups
  fi

  # Find the RO VPD area in the current firmware
  local t="$(mosys -k eeprom map "$OLD_BIOS" | grep 'RO VPD')"
  local vpd_offset="$(echo $t | sed 's/.*area_offset="\([^"]*\)" .*/\1/' )"
  local vpd_size="$(echo $t | sed 's/.*area_size="\([^"]*\)" .*/\1/' )"
  debug_msg "Found VPD at offset $vpd_offset size $vpd_size"
  # Convert offset and size to decimal, since dd doesn't grok hex
  vpd_offset="$(printf "%d" $vpd_offset)"
  vpd_size="$(printf "%d" $vpd_size)"
  debug_msg "In decimal, VPD is at offset $vpd_offset size $vpd_size"

  # Copy the RO VPD from the old firmware to the new firmware
  debug_msg "Copying VPD from old firmware to new firmware"
  cp "$FLAGS_bios" "$NEW_BIOS"
  dd bs=1 seek=$vpd_offset skip=$vpd_offset count=$vpd_size conv=notrunc \
    if="$OLD_BIOS" of="$NEW_BIOS" || err_die "Unable to copy RO VPD"

  # Disable write protect
  disable_wp "EC" ${FLASHROM_EC}
  disable_wp "BIOS" ${FLASHROM_BIOS}

  # Write new firmware
  debug_msg "Writing EC"
  # TODO: if EC file ends in .bin, use flashrom to write it directly?
  $NOTHING sh "$FLAGS_ec" --factory || err_die "Unable to write EC"
  debug_msg "Writing BIOS"
  $NOTHING ${FLASHROM_BIOS} -w "$NEW_BIOS" || err_die "Unable to write BIOS"

  # Wipe SSD
  if [ "$FLAGS_wipe_ssd" = $FLAGS_TRUE ]; then
    debug_msg "Wiping SSD"
    $NOTHING cgpt create -z "$FLAGS_ssd" || err_die "Unable to wipe SSD"
  fi

  # Leave the update engine stopped.  We've mucked with the firmware
  # and SSD contents, so we shouldn't be attempting an autoupdate this
  # boot anyway.
}

main