1#!/bin/sh 2# 3# Copyright (c) 2012 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 can change key (usually developer keys) in a firmware binary 8# image or system live firmware (EEPROM), and assign proper HWID, FLAGS as well. 9 10SCRIPT_BASE="$(dirname "$0")" 11. "$SCRIPT_BASE/common_minimal.sh" 12load_shflags || exit 1 13 14# Constants used by DEFINE_* 15VBOOT_BASE='/usr/share/vboot' 16DEFAULT_KEYS_FOLDER="$VBOOT_BASE/devkeys" 17DEFAULT_BACKUP_FOLDER='/mnt/stateful_partition/backups' 18 19# DEFINE_string name default_value description flag 20DEFINE_string from "" "Path of input file (empty for system live firmware)" "f" 21DEFINE_string to "" "Path of output file (empty for system live firmware)" "t" 22DEFINE_string keys "$DEFAULT_KEYS_FOLDER" "Path to folder of dev keys" "k" 23DEFINE_string preamble_flags "" "Override preamble flags value. Known values: 24 0: None. (Using RW to boot in normal. aka, two-stop) 25 1: VB_FIRMWARE_PREAMBLE_USE_RO_NORMAL (one-stop)" "p" 26DEFINE_boolean mod_gbb_flags \ 27 $FLAGS_TRUE "Modify GBB flags to enable developer friendly features" "" 28DEFINE_boolean force_backup \ 29 $FLAGS_TRUE "Create backup even if source is not live" "" 30DEFINE_string backup_dir \ 31 "$DEFAULT_BACKUP_FOLDER" "Path of directory to store firmware backups" "" 32 33# Parse command line 34FLAGS "$@" || exit 1 35eval set -- "$FLAGS_ARGV" 36 37# Globals 38# ---------------------------------------------------------------------------- 39set -e 40 41# the image we are (temporary) working with 42IMAGE="$(make_temp_file)" 43IMAGE="$(readlink -f "$IMAGE")" 44 45# a log file to keep the output results of executed command 46EXEC_LOG="$(make_temp_file)" 47 48# Functions 49# ---------------------------------------------------------------------------- 50 51# Disables write protection status registers 52disable_write_protection() { 53 # No need to change WP status in file mode 54 if [ -n "$FLAGS_to" ]; then 55 return $FLAGS_TRUE 56 fi 57 58 # --wp-disable command may return success even if WP is still enabled, 59 # so we should use --wp-status to verify the results. 60 echo "Disabling system software write protection status..." 61 (flashrom --wp-disable && flashrom --wp-status) 2>&1 | 62 tee "$EXEC_LOG" | 63 grep -q '^WP: .* is disabled\.$' 64} 65 66# Reads $IMAGE from $FLAGS_from 67read_image() { 68 if [ -z "$FLAGS_from" ]; then 69 echo "Reading system live firmware..." 70 if is_debug_mode; then 71 flashrom -V -r "$IMAGE" 72 else 73 flashrom -r "$IMAGE" >"$EXEC_LOG" 2>&1 74 fi 75 else 76 debug_msg "reading from file: $FLAGS_from" 77 cp -f "$FLAGS_from" "$IMAGE" 78 fi 79} 80 81# Writes $IMAGE to $FLAGS_to 82write_image() { 83 if [ -z "$FLAGS_to" ]; then 84 echo "Writing system live firmware..." 85 # TODO(hungte) we can enable partial write to make this faster 86 if is_debug_mode; then 87 flashrom -V -w "$IMAGE" 88 else 89 flashrom -w "$IMAGE" >"$EXEC_LOG" 2>&1 90 fi 91 else 92 debug_msg "writing to file: $FLAGS_to" 93 cp -f "$IMAGE" "$FLAGS_to" 94 chmod a+r "$FLAGS_to" 95 fi 96} 97 98# Converts HWID from $1 to proper format with "DEV" extension 99echo_dev_hwid() { 100 local hwid="$1" 101 local hwid_no_dev="${hwid% DEV}" 102 103 # NOTE: Some DEV firmware image files may put GUID in HWID. 104 # These are not officially supported and they will see "{GUID} DEV". 105 106 if [ "$hwid" != "$hwid_no_dev" ]; then 107 hwid="$hwid_no_dev" 108 fi 109 local hwid_dev="$hwid DEV" 110 debug_msg "echo_dev_hwid: [$1] -> [$hwid_dev]" 111 echo "$hwid_dev" 112} 113 114# Main 115# ---------------------------------------------------------------------------- 116main() { 117 # Check parameters 118 local root_pubkey="$FLAGS_keys/root_key.vbpubk" 119 local recovery_pubkey="$FLAGS_keys/recovery_key.vbpubk" 120 local firmware_keyblock="$FLAGS_keys/firmware.keyblock" 121 local firmware_prvkey="$FLAGS_keys/firmware_data_key.vbprivk" 122 local dev_firmware_keyblock="$FLAGS_keys/dev_firmware.keyblock" 123 local dev_firmware_prvkey="$FLAGS_keys/dev_firmware_data_key.vbprivk" 124 local kernel_sub_pubkey="$FLAGS_keys/kernel_subkey.vbpubk" 125 local is_from_live=0 126 local backup_image= 127 128 debug_msg "Prerequisite check" 129 ensure_files_exist \ 130 "$root_pubkey" \ 131 "$recovery_pubkey" \ 132 "$firmware_keyblock" \ 133 "$firmware_prvkey" \ 134 "$kernel_sub_pubkey" || 135 exit 1 136 137 if [ -z "$FLAGS_from" ]; then 138 is_from_live=1 139 else 140 ensure_files_exist "$FLAGS_from" || exit 1 141 fi 142 143 debug_msg "Checking software write protection status" 144 disable_write_protection || 145 if is_debug_mode; then 146 err_die "Failed to disable WP. Diagnose Message: $(cat "$EXEC_LOG")" 147 else 148 err_die "Write protection is still enabled. " \ 149 "Please verify that hardware write protection is disabled." 150 fi 151 152 debug_msg "Pulling image to $IMAGE" 153 (read_image && [ -s "$IMAGE" ]) || 154 err_die "Failed to read image. Error message: $(cat "$EXEC_LOG")" 155 156 debug_msg "Prepare to backup the file" 157 if [ -n "$is_from_live" -o $FLAGS_force_backup = $FLAGS_TRUE ]; then 158 backup_image="$(make_temp_file)" 159 debug_msg "Creating backup file to $backup_image..." 160 cp -f "$IMAGE" "$backup_image" 161 fi 162 163 debug_msg "Detecting developer firmware keyblock" 164 local expanded_firmware_dir="$(make_temp_dir)" 165 local use_devfw_keyblock="$FLAGS_FALSE" 166 (cd "$expanded_firmware_dir"; dump_fmap -x "$IMAGE" >/dev/null 2>&1) || 167 err_die "Failed to extract firmware image." 168 if [ -f "$expanded_firmware_dir/VBLOCK_A" ]; then 169 local has_dev=$FLAGS_TRUE has_norm=$FLAGS_TRUE 170 # In output of vbutil_keyblock, "!DEV" means "bootable on normal mode" and 171 # "DEV" means "bootable on developer mode". Here we try to match the pattern 172 # in output of vbutil_block, and disable the flags (has_dev, has_norm) if 173 # the pattern was not found. 174 vbutil_keyblock --unpack "$expanded_firmware_dir/VBLOCK_A" | 175 grep -qw '!DEV' || has_norm=$FLAGS_FALSE 176 vbutil_keyblock --unpack "$expanded_firmware_dir/VBLOCK_A" | 177 grep -qw '[^!]DEV' || has_dev=$FLAGS_FALSE 178 if [ "$has_norm" = "$FLAGS_FALSE" -a "$has_dev" = "$FLAGS_TRUE" ]; then 179 use_devfw_keyblock=$FLAGS_TRUE 180 fi 181 fi 182 183 if [ "$use_devfw_keyblock" = "$FLAGS_TRUE" ]; then 184 echo "Using keyblocks (developer, normal)..." 185 else 186 echo "Using keyblocks (normal, normal)..." 187 dev_firmware_prvkey="$firmware_prvkey" 188 dev_firmware_keyblock="$firmware_keyblock" 189 fi 190 191 # TODO(hungte) We can use vbutil_firmware to check if the current firmware is 192 # valid so that we know keys and vbutil_firmware are all working fine. 193 194 echo "Preparing new firmware image..." 195 196 debug_msg "Resign the firmware code (A/B) with new keys" 197 # Note resign_firmwarefd.sh needs the original rootkey to determine firmware 198 # body size, so we must resign image before changing GBB rootkey. 199 200 local unsigned_image="$(make_temp_file)" 201 local optional_opts="" 202 if [ -n "$FLAGS_preamble_flags" ]; then 203 # optional_opts: VERSION FLAGS 204 debug_msg "Setting new VERSION=1, FLAGS=$FLAGS_preamble_flags" 205 optional_opts="1 $FLAGS_preamble_flags" 206 fi 207 cp -f "$IMAGE" "$unsigned_image" 208 "$SCRIPT_BASE/resign_firmwarefd.sh" \ 209 "$unsigned_image" \ 210 "$IMAGE" \ 211 "$firmware_prvkey" \ 212 "$firmware_keyblock" \ 213 "$dev_firmware_prvkey" \ 214 "$dev_firmware_keyblock" \ 215 "$kernel_sub_pubkey" \ 216 $optional_opts >"$EXEC_LOG" 2>&1 || 217 err_die "Failed to re-sign firmware. (message: $(cat "$EXEC_LOG"))" 218 if is_debug_mode; then 219 cat "$EXEC_LOG" 220 fi 221 222 debug_msg "Extract current HWID" 223 local old_hwid 224 old_hwid="$(gbb_utility --get --hwid "$IMAGE" 2>"$EXEC_LOG" | 225 sed -rne 's/^hardware_id: (.*)$/\1/p')" 226 227 debug_msg "Decide new HWID" 228 [ -z "$old_hwid" ] && 229 err_die "Cannot find current HWID. (message: $(cat "$EXEC_LOG"))" 230 local new_hwid="$(echo_dev_hwid "$old_hwid")" 231 232 local old_gbb_flags 233 old_gbb_flags="$(gbb_utility --get --flags "$IMAGE" 2>"$EXEC_LOG" | 234 sed -rne 's/^flags: (.*)$/\1/p')" 235 debug_msg "Decide new GBB flags from: $old_gbb_flags" 236 [ -z "$old_gbb_flags" ] && 237 err_die "Cannot find GBB flags. (message: $(cat "$EXEC_LOG"))" 238 # 0x30: GBB_FLAG_FORCE_DEV_BOOT_USB | GBB_FLAG_DISABLE_FW_ROLLBACK_CHECK 239 local new_gbb_flags="$((old_gbb_flags | 0x30))" 240 241 debug_msg "Replace GBB parts (gbb_utility allows changing on-the-fly)" 242 gbb_utility --set \ 243 --hwid="$new_hwid" \ 244 --rootkey="$root_pubkey" \ 245 --recoverykey="$recovery_pubkey" \ 246 "$IMAGE" >"$EXEC_LOG" 2>&1 || 247 err_die "Failed to change GBB Data. (message: $(cat "$EXEC_LOG"))" 248 249 # Old firmware does not support GBB flags, so let's make it an exception. 250 if [ "$FLAGS_mod_gbb_flags" = "$FLAGS_TRUE" ]; then 251 debug_msg "Changing GBB flags from $old_gbb_flags to $new_gbb_flags" 252 gbb_utility --set \ 253 --flags="$new_gbb_flags" \ 254 "$IMAGE" >"$EXEC_LOG" 2>&1 || 255 echo "Warning: GBB flags ($old_gbb_flags -> $new_gbb_flags) can't be set." 256 fi 257 258 # TODO(hungte) compare if the image really needs to be changed. 259 260 debug_msg "Check if we need to make backup file(s)" 261 if [ -n "$backup_image" ]; then 262 local backup_hwid_name="$(echo "$old_hwid" | sed 's/ /_/g')" 263 local backup_date_time="$(date +'%Y%m%d_%H%M%S')" 264 local backup_file_name="firmware_${backup_hwid_name}_${backup_date_time}.fd" 265 local backup_file_path="$FLAGS_backup_dir/$backup_file_name" 266 if mkdir -p "$FLAGS_backup_dir" && 267 cp -f "$backup_image" "$backup_file_path"; then 268 true 269 elif cp -f "$backup_image" "/tmp/$backup_file_name"; then 270 backup_file_path="/tmp/$backup_file_name" 271 else 272 backup_file_path='' 273 fi 274 if [ -n "$backup_file_path" -a -s "$backup_file_path" ]; then 275 # TODO(hungte) maybe we can wrap the flashrom by 'make_dev_firmware.sh -r' 276 # so that the only command to remember would be make_dev_firmware.sh. 277 echo " 278 Backup of current firmware image is stored in: 279 $backup_file_path 280 Please copy the backup file to a safe place ASAP. 281 282 To stop using devkeys and restore original firmware, execute command: 283 flashrom -w [PATH_TO_BACKUP_IMAGE] 284 Ex: flashrom -w $backup_file_path 285 " 286 else 287 echo "WARNING: Cannot create file in $FLAGS_backup_dir... Ignore backups." 288 fi 289 fi 290 291 # TODO(hungte) use vbutil_firmware to check if the new firmware is valid. 292 # Or, do verification in resign_firmwarefd.sh and trust it. 293 294 debug_msg "Write the image" 295 write_image || 296 err_die "Failed to write image. Error message: $(cat "$EXEC_LOG")" 297 298 debug_msg "Complete." 299 if [ -z "$FLAGS_to" ]; then 300 echo "Successfully changed firmware to Developer Keys. New HWID: $new_hwid" 301 else 302 echo "Firmware '$FLAGS_to' now uses Developer Keys. New HWID: $new_hwid" 303 fi 304} 305 306main 307