blob: d143d686a2b21c56640c37d317880a86d0f8dbd2 [file] [log] [blame] [edit]
#!/bin/bash
# Copyright (c) 2012 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.
# Given a test image, a firmware version, and 5 modified firmware binaries
# this script will generate 5 auto update payloads with incremented OS
# and firmware versions. The payloads can then be applied one after another
# to test firmware update and kernel version increments.
find_common_sh() {
local thisdir="$(dirname "$(readlink -f "$0")")"
local common_paths=(/usr/lib/crosutils "${thisdir}")
local path
SCRIPT_ROOT="${common_paths[0]}"
for path in "${common_paths[@]}"; do
if [ -r "${path}/common.sh" ]; then
SCRIPT_ROOT="${path}"
break
fi
done
}
find_common_sh
. "${SCRIPT_ROOT}/common.sh" || exit 1
cleanup() {
"${SCRIPTS_DIR}/mount_gpt_image.sh" -u \
-i "${IMAGE_NAME}" -f "${WORKING_DIR}" \
-r "${ROOT_FS_DIR}" -s "${STATEFUL_FS_DIR}"
}
on_exit() {
cleanup
if "${IS_ABORT}"; then
error "Failed to generate all payloads."
fi
}
replace_fmap_section() {
local image="$1"
local section="$2"
local data="$3"
# format: NAME OFFSET SIZE
local info="$(dump_fmap -p "${image}" "${section}")"
local name offset size
read name offset size <<<"${info}"
[ -n "${offset}" ] || die_notrace "Invalid firmware image: No ${section}."
dd if=/dev/zero of="$image" bs=1 seek="${offset}" count="${size}" \
conv=notrunc
echo -n "${data}" | dd of="$image" bs=1 seek="${offset}" conv=notrunc
}
replace_firmware_id() {
local image="$1"
local new_id="$2"
replace_fmap_section "${image}" RO_FRID "$new_id"
replace_fmap_section "${image}" RW_FWID_A "$new_id"
replace_fmap_section "${image}" RW_FWID_B "$new_id"
}
# (name, expected, actual): Checks if input argument 'actual' is same as
# 'expected'. Otherwise die with message showing argument 'name'.
assert_value() {
if [[ "$2" != "$3" ]]; then
die "Value [$1] incorrect. Expected: [$2], Actual: [$3]."
fi
}
# Need to be inside the chroot to load chromeos-common.sh
assert_inside_chroot
DEFINE_integer iterations 5 "Iterations to run" x
DEFINE_string board "$FLAGS_board" "Board for which the image was built" b
DEFINE_string image "$FLAGS_image" "Location of the test image file" i
DEFINE_string firmware_ver "$FLAGS_firmware_ver" "New firmware version" f
DEFINE_string firmware_src "$FLAGS_firmware_src" \
"Location of the source firmware file" s
DEFINE_string replace_src "$FLAGS_replace_src" "Version string to replace" v
# Parse command line
FLAGS "$@" || exit 1
eval set -- "$FLAGS_ARGV"
# Convert args to paths
FLAGS_image=`eval readlink -f ${FLAGS_image}`
FLAGS_firmware_src=`eval readlink -f ${FLAGS_firmware_src}`
WORKING_DIR=/tmp/key_increment_working_folder
BIOS_WORKING_DIR="${WORKING_DIR}/bios"
IMAGE_DIR=$(dirname "${FLAGS_image}")
IMAGE_NAME=$(basename "${FLAGS_image}")
ROOT_FS_DIR="${IMAGE_DIR}/rootfs"
STATEFUL_FS_DIR="${IMAGE_DIR}/stateful"
FM_VER_PREFIX=${FLAGS_firmware_ver}
ITERATIONS=${FLAGS_iterations}
info "Checking input parameters..."
[ -f "${FLAGS_image}" ] || die "Failed to find test image file: ${FLAGS_image}"
[ -f "${FLAGS_firmware_src}" ] || die "No firmware file: ${FLAGS_firmware_src}"
# Setup working dir
if [ -d $WORKING_DIR ]; then
info "Cleaning up work directory..."
rm -rf $WORKING_DIR
fi
mkdir ${WORKING_DIR}
mkdir ${BIOS_WORKING_DIR}
info "Creating firmware binaries"
for i in $(seq 1 1 ${ITERATIONS})
do
output="${BIOS_WORKING_DIR}/${FLAGS_board}_${FM_VER_PREFIX}.${i}.bin"
cp "${FLAGS_firmware_src}" "${output}"
replace_firmware_id "${output}" "${FLAGS_replace_src}.test${i}"
done
# Check we have all 5 new firmware binaries
# And verify file size should be same as the
# source firmware binary
info "Checking ${BIOS_WORKING_DIR} for binaries..."
info "Using pattern: ${FLAGS_board}_${FM_VER_PREFIX}.1.bin"
SRC_SIZE=$(stat -c%s ${FLAGS_firmware_src})
for i in $(seq 1 1 ${ITERATIONS})
do
BIN_FILE="${BIOS_WORKING_DIR}/${FLAGS_board}_${FM_VER_PREFIX}.${i}.bin"
if [ ! -f ${BIN_FILE} ]; then
die_notrace "Unable to locate ${BIN_FILE} firmware binary, exiting."
fi
MOD_SIZE=$(stat -c%s ${BIN_FILE})
if [ ${SRC_SIZE} -ne ${MOD_SIZE} ]; then
die_notrace "Src (${SRC_SIZE}) and modified (${MOD_SIZE}) firmware \
file sizes are not the same, exiting."
fi
done
# Abort on error.
IS_ABORT=true
set -e
trap on_exit EXIT
info "Copying ${FLAGS_image} to ${WORKING_DIR}"
cp $FLAGS_image $WORKING_DIR
# Pull out the shellball we want to use
"$SCRIPTS_DIR/mount_gpt_image.sh" -i "$IMAGE_NAME" -f "$WORKING_DIR" \
-r "$ROOT_FS_DIR" -s "$STATEFUL_FS_DIR"
IMAGE_UPDATER="${ROOT_FS_DIR}/usr/sbin/chromeos-firmwareupdate"
WORKING_UPDATER="${BIOS_WORKING_DIR}/chromeos-firmwareupdate"
for i in $(seq 1 1 ${ITERATIONS})
do
cp ${IMAGE_UPDATER} ${WORKING_UPDATER}-test$i
chmod 755 ${WORKING_UPDATER}-test$i
NEW_VER="${FLAGS_board}_${FM_VER_PREFIX}.${i}"
FM_VER="Google_${NEW_VER}"
info "Updating the firmware version to ${FM_VER}"
sed -i \
"/^TARGET_FWID=/c TARGET_FWID=\"${FM_VER}\"" "${WORKING_UPDATER}-test$i"
# Clear the UNSTABLE flag.
sed -i \
's/^TARGET_UNSTABLE=.*/TARGET_UNSTABLE=""/' "${WORKING_UPDATER}-test$i"
# Workaround issue crosbug.com/p/33719
sed -i \
's/shar -Q -q -x -m -w/shar -Q -q -x -m --no-character-count/' \
"${WORKING_UPDATER}-test$i"
# Resign the provided firmware binaries
PROVIDED_BIN="${BIOS_WORKING_DIR}/${NEW_VER}.bin"
SIGNED_BIN="${BIOS_WORKING_DIR}/${NEW_VER}_signed.bin"
info "Resigning ${PROVIDED_BIN} to ${SIGNED_BIN}"
cd ~/trunk/src/platform/vboot_reference/scripts/image_signing
./resign_firmwarefd.sh \
"${PROVIDED_BIN}" \
"${SIGNED_BIN}" \
../../tests/devkeys/firmware_data_key.vbprivk \
../../tests/devkeys/firmware.keyblock \
../../tests/devkeys/dev_firmware_data_key.vbprivk \
../../tests/devkeys/dev_firmware.keyblock \
../../tests/devkeys/kernel_subkey.vbpubk 1 0
cd "${BIOS_WORKING_DIR}"
mkdir work
./chromeos-firmwareupdate-test$i --sb_extract work/
info "Copying new bios ${SIGNED_BIN} into ${WORKING_UPDATER}-test$i"
sudo cp ${SIGNED_BIN} work/bios.bin
info "Dumping keys of the firmware image:"
vbutil_what_keys work/bios.bin
./chromeos-firmwareupdate-test$i --sb_repack work/
rm -r work
done
# Get the OS version
LSB_RELEASE="${ROOT_FS_DIR}/etc/lsb-release"
CHROMEOS_VER=$(grep ^"CHROMEOS_RELEASE_VERSION" ${LSB_RELEASE} | cut -d = -f 2-)
CHROMEOS_VER_PREFIX=${CHROMEOS_VER%?}
CHROMEOS_TRACK=$(grep ^"CHROMEOS_RELEASE_TRACK" ${LSB_RELEASE} | cut -d = -f 2-)
cleanup
# Make a copy of the key directories
KEYS_DIR="${WORKING_DIR}/keys"
mkdir "${KEYS_DIR}"
cd ~/trunk/src/platform/vboot_reference
# Enable firmware update
scripts/image_signing/tag_image.sh --from="${WORKING_DIR}/${IMAGE_NAME}" \
--update_firmware 1
cp -r tests/devkeys/* "${KEYS_DIR}"
cp -r scripts/keygeneration/* "${KEYS_DIR}"
# Load keygeneration helper methods
. "${KEYS_DIR}/common.sh"
# Make a directory to store the new payloads
PAYLOAD_DIR="${WORKING_DIR}/payloads"
mkdir "${PAYLOAD_DIR}"
FW_TEMP_DIR="${WORKING_DIR}/fw_temp"
mkdir "${FW_TEMP_DIR}"
# Create a copy of the test image that will be convert to a payload.
for i in $(seq 1 1 ${ITERATIONS})
do
NEW_IMAGE_NAME="chromiumos-key-image-${CHROMEOS_VER_PREFIX}${i}.bin"
cp "${WORKING_DIR}/${IMAGE_NAME}" "${WORKING_DIR}/${NEW_IMAGE_NAME}"
"$SCRIPTS_DIR/mount_gpt_image.sh" -i "$NEW_IMAGE_NAME" -f "$WORKING_DIR" \
-r "$ROOT_FS_DIR" -s "$STATEFUL_FS_DIR"
info "Copying ${WORKING_UPDATER}-test${i} to ${IMAGE_UPDATER}"
sudo cp ${WORKING_UPDATER}-test${i} ${IMAGE_UPDATER}
NEW_CHROME_VERSION=${CHROMEOS_VER_PREFIX}${i}
info "Updating chrome version to ${NEW_CHROME_VERSION}"
sudo sed -i "s/${CHROMEOS_VER}/${NEW_CHROME_VERSION}/g" \
"${ROOT_FS_DIR}/etc/lsb-release"
sudo sed -i 's/tools/omaha.sandbox/g' "${ROOT_FS_DIR}/etc/lsb-release"
info "Contents of the new lsb-release file"
more "${ROOT_FS_DIR}/etc/lsb-release"
cleanup
expected_data_key_version=1
expected_firmware_version=1
cd "${KEYS_DIR}"
if [[ ${i} == 1 ]]; then
load_current_versions "${KEYS_DIR}"
new_kern_ver=$(increment_version "${KEYS_DIR}" "kernel_version")
write_updated_version_file ${CURR_FIRMKEY_VER} ${CURR_FIRM_VER} \
${CURR_KERNKEY_VER} ${new_kern_ver}
elif [[ ${i} == 2 ]]; then
"${KEYS_DIR}"/increment_kernel_data_key.sh "${KEYS_DIR}"
elif [[ ${i} == 3 ]]; then
"${KEYS_DIR}"/increment_kernel_subkey.sh "${KEYS_DIR}"
expected_firmware_version=2
elif [[ ${i} == 4 ]]; then
"${KEYS_DIR}"/increment_firmware_data_key.sh "${KEYS_DIR}"
expected_data_key_version=2
expected_firmware_version=2
elif [[ ${i} == 5 ]]; then
load_current_versions "${KEYS_DIR}"
new_kern_ver=$(increment_version "${KEYS_DIR}" "kernel_version")
write_updated_version_file ${CURR_FIRMKEY_VER} ${CURR_FIRM_VER} \
${CURR_KERNKEY_VER} ${new_kern_ver}
"${KEYS_DIR}"/increment_kernel_subkey_and_key.sh "${KEYS_DIR}"
"${KEYS_DIR}"/increment_firmware_data_key.sh "${KEYS_DIR}"
expected_data_key_version=3
expected_firmware_version=3
fi
SIGNED_IMAGE_NAME="chromiumos-key-image-${CHROMEOS_VER_PREFIX}${i}_signed.bin"
info "Resigning the image to ${SIGNED_IMAGE_NAME}..."
cd ~/trunk/src/platform/vboot_reference
sudo scripts/image_signing/sign_official_build.sh \
ssd "${WORKING_DIR}/${NEW_IMAGE_NAME}" \
"${KEYS_DIR}" \
"${WORKING_DIR}/${SIGNED_IMAGE_NAME}" \
"${KEYS_DIR}/key.versions"
info "Generating payload..."
sudo cros_generate_update_payload \
--image="${WORKING_DIR}/${SIGNED_IMAGE_NAME}" \
--output="${PAYLOAD_DIR}/chromeos_${CHROMEOS_VER_PREFIX}${i}\
_${FLAGS_board}_testimage-channel_full_test.bin-000${i}.signed"
info "Checking signed image"
"${SCRIPTS_DIR}/mount_gpt_image.sh" --safe --read_only \
-i "${SIGNED_IMAGE_NAME}" -f "${WORKING_DIR}" \
-r "${ROOT_FS_DIR}" -s "${STATEFUL_FS_DIR}"
sh "${ROOT_FS_DIR}"/usr/sbin/chromeos-firmwareupdate \
--sb_extract "${FW_TEMP_DIR}"
(cd "${FW_TEMP_DIR}"; dump_fmap -x bios.bin >/dev/null 2>&1)
gbb_utility --rootkey="${FW_TEMP_DIR}/rootkey" "${FW_TEMP_DIR}/GBB"
# In Alex/ZGB, A is signed with dev keyblock and B is normal block so we want
# to check B first.
for slot in B A; do
key_versions="$(vbutil_firmware --signpubkey "${FW_TEMP_DIR}/rootkey" \
--fv "${FW_TEMP_DIR}/FW_MAIN_${slot}" \
--verify "${FW_TEMP_DIR}/VBLOCK_${slot}")"
data_key_version="$(echo "${key_versions}" |
sed -n 's/.*Data key version: *\(.*\)/\1/p')"
firmware_version="$(echo "${key_versions}" |
sed -n 's/.*Firmware version: *\(.*\)/\1/p')"
assert_value "Data key version in slot ${slot}, iteration ${i}" \
"${expected_data_key_version}" "${data_key_version}"
assert_value "Firmware version in slot ${slot}, iteration ${i}" \
"${expected_firmware_version}" "${firmware_version}"
done
cleanup
done
info "All payloads are available at ${PAYLOAD_DIR}"
IS_ABORT=false