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
|