1#!/bin/sh 2# 3# Copyright (c) 2011 The Chromium OS Authors. All rights reserved. 4# Use of this source code is governed by a BSD-style license that can be 5# found in the LICENSE file. 6# 7# This script converts a Chrome OS device to a pre-factory-install state: 8# * Firmware write protect disabled 9# * H2O BIOS, with RO VPD area copied from the current BIOS 10# * Original EC firmware 11# * Blank SSD (no GPT) 12# 13# Minimal usage: 14# tofactory.sh -b H2OBIOS.bin -e ec_shellball.sh 15 16SCRIPT_BASE="$(dirname "$0")" 17. "$SCRIPT_BASE/common_minimal.sh" 18load_shflags || exit 1 19 20# Constants used by DEFINE_* 21VBOOT_BASE='/usr/share/vboot' 22 23# DEFINE_string name default_value description flag 24DEFINE_string bios "" "Path of system firmware (BIOS) binary to write" "b" 25DEFINE_string ec "" "Path of EC shellball to execute" "e" 26DEFINE_string backup_dir "" "Path of directory in whoch to store backups" "k" 27DEFINE_string asset_tag "unspecified_tag" \ 28 "Asset tag of device, used to name backups" "a" 29DEFINE_string ssd "/dev/sda" "Path to SSD / target drive" "s" 30DEFINE_boolean wipe_ssd $FLAGS_TRUE "Wipe SSD after firmware updates" "" 31DEFINE_boolean nothing $FLAGS_FALSE \ 32 "Print commands but do not modify device" "n" 33 34# Parse command line 35FLAGS "$@" || exit 1 36eval set -- "$FLAGS_ARGV" 37 38# Globals 39# ---------------------------------------------------------------------------- 40set -e 41 42# Flashrom commands with device overrides 43FLASHROM_BIOS="flashrom -p host" 44FLASHROM_EC="flashrom -p ec" 45 46# A log file to keep the output results of executed command 47EXEC_LOG="$(make_temp_file)" 48 49# Temporary Work directory 50WORK_DIR="$(make_temp_dir)" 51OLD_BIOS="$WORK_DIR/old_bios.bin" 52NEW_BIOS="$WORK_DIR/new_bios.bin" 53 54# Functions 55# ---------------------------------------------------------------------------- 56 57# Error message for write protect disable failure, with reminder 58wp_error() { 59 local which_rom=$1 60 shift 61 echo "ERROR: Unable to disable $which_rom write protect: $*" 1>&2 62 echo "Is hardware write protect still enabled?" 1>&2 63 exit 1 64} 65 66# Disable write protect for an EEPROM 67disable_wp() { 68 local which_rom=$1 # EC or BIOS 69 shift 70 local flash_rom="$*" # Flashrom command to use 71 72 debug_msg "Disabling $which_rom write protect" 73 $NOTHING ${flash_rom} --wp-disable || wp_error "$which_rom" "--wp-disable" 74 $NOTHING ${flash_rom} --wp-range 0 0 || wp_error "$which_rom" "--wp-range" 75 76 # WP status bits should report WP: status: 0x00 77 local wp_status="$(${flash_rom} --wp-status | grep "WP: status:")" 78 if [ "$wp_status" != "WP: status: 0x00" ]; then 79 if [ "$FLAGS_nothing" = $FLAGS_FALSE ]; then 80 wp_error "$which_rom" "$wp_status" 81 fi 82 fi 83} 84 85# Back up current firmware and partition table 86make_backups() { 87 debug_msg "Backing up current firmware to $FLAGS_backup_dir" 88 mkdir -p "$FLAGS_backup_dir" 89 cp "$OLD_BIOS" "$FLAGS_backup_dir/$FLAGS_asset_tag.bios.bin" 90 ${FLASHROM_EC} -r "$FLAGS_backup_dir/$FLAGS_asset_tag.ec.bin" 91 92 # Copy the VPD info from RAM, since we can't extract it as text 93 # from the BIOS binary. Failure of this is only a warning, since 94 # the information is still in the old BIOS. 95 mosys vpd print all > "$FLAGS_backup_dir/$FLAGS_asset_tag.vpd.txt" || 96 echo "WARNING: unable to save VPD as text." 97 98 # Copy the first part of the drive, so we can recreate the partition 99 # table. 100 local gpt_backup="$FLAGS_backup_dir/$FLAGS_asset_tag.gpt.bin" 101 debug_msg "Backing up current GPT table." 102 dd if="$FLAGS_ssd" of="$gpt_backup" bs=512 count=34 103 104 # Add a script to restore the BIOS and GPT 105 local restore_script="$FLAGS_backup_dir/$FLAGS_asset_tag.restore.sh" 106 cat >"$restore_script" <<EOF 107#!/bin/sh 108echo "Restoring BIOS" 109${FLASHROM_BIOS} -w "$FLAGS_asset_tag.bios.bin" 110echo "Restoring EC" 111${FLASHROM_EC} -w "$FLAGS_asset_tag.ec.bin" 112echo "Restoring GPT" 113dd of="$FLAGS_ssd" if="$FLAGS_asset_tag.gpt.bin" conv=notrunc 114EOF 115 echo "To restore original BIOS and SSD:" 116 echo " cd $FLAGS_backup_dir && sh $FLAGS_asset_tag.restore.sh" 117} 118 119# Main 120# ---------------------------------------------------------------------------- 121 122main() { 123 # Make sure the files we were passed exist 124 [ -n "$FLAGS_bios" ] || err_die "Please specify a BIOS file (-b bios.bin)" 125 [ -n "$FLAGS_ec" ] || err_die "Please specify an EC updater (-e updater.sh)" 126 ensure_files_exist "$FLAGS_bios" "$FLAGS_ec" || exit 1 127 128 # If --nothing was specified, keep flashrom from writing 129 if [ "$FLAGS_nothing" = $FLAGS_TRUE ]; then 130 NOTHING="echo Not executing: " 131 fi 132 133 # Stop update engine before calling flashrom. Multiple copies of flashrom 134 # interfere with each other. 135 debug_msg "Stopping update engine" 136 initctl stop update-engine 137 138 # Read the current firmware 139 debug_msg "Reading BIOS from EEPROM" 140 ${FLASHROM_BIOS} -r "$OLD_BIOS" 141 142 # Copy current info to the backup dir, if specified 143 if [ -n "$FLAGS_backup_dir" ]; then 144 make_backups 145 fi 146 147 # Find the RO VPD area in the current firmware 148 local t="$(mosys -k eeprom map "$OLD_BIOS" | grep 'RO VPD')" 149 local vpd_offset="$(echo $t | sed 's/.*area_offset="\([^"]*\)" .*/\1/' )" 150 local vpd_size="$(echo $t | sed 's/.*area_size="\([^"]*\)" .*/\1/' )" 151 debug_msg "Found VPD at offset $vpd_offset size $vpd_size" 152 # Convert offset and size to decimal, since dd doesn't grok hex 153 vpd_offset="$(printf "%d" $vpd_offset)" 154 vpd_size="$(printf "%d" $vpd_size)" 155 debug_msg "In decimal, VPD is at offset $vpd_offset size $vpd_size" 156 157 # Copy the RO VPD from the old firmware to the new firmware 158 debug_msg "Copying VPD from old firmware to new firmware" 159 cp "$FLAGS_bios" "$NEW_BIOS" 160 dd bs=1 seek=$vpd_offset skip=$vpd_offset count=$vpd_size conv=notrunc \ 161 if="$OLD_BIOS" of="$NEW_BIOS" || err_die "Unable to copy RO VPD" 162 163 # Disable write protect 164 disable_wp "EC" ${FLASHROM_EC} 165 disable_wp "BIOS" ${FLASHROM_BIOS} 166 167 # Write new firmware 168 debug_msg "Writing EC" 169 # TODO: if EC file ends in .bin, use flashrom to write it directly? 170 $NOTHING sh "$FLAGS_ec" --factory || err_die "Unable to write EC" 171 debug_msg "Writing BIOS" 172 $NOTHING ${FLASHROM_BIOS} -w "$NEW_BIOS" || err_die "Unable to write BIOS" 173 174 # Wipe SSD 175 if [ "$FLAGS_wipe_ssd" = $FLAGS_TRUE ]; then 176 debug_msg "Wiping SSD" 177 $NOTHING cgpt create -z "$FLAGS_ssd" || err_die "Unable to wipe SSD" 178 fi 179 180 # Leave the update engine stopped. We've mucked with the firmware 181 # and SSD contents, so we shouldn't be attempting an autoupdate this 182 # boot anyway. 183} 184 185main 186