Home | History | Annotate | Download | only in image_signing
      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 
     16 SCRIPT_BASE="$(dirname "$0")"
     17 . "$SCRIPT_BASE/common_minimal.sh"
     18 load_shflags || exit 1
     19 
     20 # Constants used by DEFINE_*
     21 VBOOT_BASE='/usr/share/vboot'
     22 
     23 # DEFINE_string name default_value description flag
     24 DEFINE_string bios "" "Path of system firmware (BIOS) binary to write" "b"
     25 DEFINE_string ec "" "Path of EC shellball to execute" "e"
     26 DEFINE_string backup_dir "" "Path of directory in whoch to store backups" "k"
     27 DEFINE_string asset_tag "unspecified_tag" \
     28   "Asset tag of device, used to name backups" "a"
     29 DEFINE_string ssd "/dev/sda" "Path to SSD / target drive" "s"
     30 DEFINE_boolean wipe_ssd $FLAGS_TRUE "Wipe SSD after firmware updates" ""
     31 DEFINE_boolean nothing $FLAGS_FALSE \
     32   "Print commands but do not modify device" "n"
     33 
     34 # Parse command line
     35 FLAGS "$@" || exit 1
     36 eval set -- "$FLAGS_ARGV"
     37 
     38 # Globals
     39 # ----------------------------------------------------------------------------
     40 set -e
     41 
     42 # Flashrom commands with device overrides
     43 FLASHROM_BIOS="flashrom -p host"
     44 FLASHROM_EC="flashrom -p ec"
     45 
     46 # A log file to keep the output results of executed command
     47 EXEC_LOG="$(make_temp_file)"
     48 
     49 # Temporary Work directory
     50 WORK_DIR="$(make_temp_dir)"
     51 OLD_BIOS="$WORK_DIR/old_bios.bin"
     52 NEW_BIOS="$WORK_DIR/new_bios.bin"
     53 
     54 # Functions
     55 # ----------------------------------------------------------------------------
     56 
     57 # Error message for write protect disable failure, with reminder
     58 wp_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
     67 disable_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
     86 make_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
    108 echo "Restoring BIOS"
    109 ${FLASHROM_BIOS} -w "$FLAGS_asset_tag.bios.bin"
    110 echo "Restoring EC"
    111 ${FLASHROM_EC} -w "$FLAGS_asset_tag.ec.bin"
    112 echo "Restoring GPT"
    113 dd of="$FLAGS_ssd" if="$FLAGS_asset_tag.gpt.bin" conv=notrunc
    114 EOF
    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 
    122 main() {
    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 
    185 main
    186