| Index: mod_image_for_recovery.sh
|
| diff --git a/mod_image_for_recovery.sh b/mod_image_for_recovery.sh
|
| index 7d59095753a2e78dd2e14084fb5e274e292a3c9c..6a0b261330ea9d1fdb98c28b1ff29f19bb228547 100755
|
| --- a/mod_image_for_recovery.sh
|
| +++ b/mod_image_for_recovery.sh
|
| @@ -4,35 +4,70 @@
|
| # Use of this source code is governed by a BSD-style license that can be
|
| # found in the LICENSE file.
|
|
|
| -# Script to modify a pristine/dev Chrome OS image to be used for recovery
|
| +# This script modifies a base image to act as a recovery installer.
|
| +# If no kernel image is supplied, it will build a devkeys signed recovery
|
| +# kernel. Alternatively, a signed recovery kernel can be used to
|
| +# create a Chromium OS recovery image.
|
|
|
| +# Load common constants. This should be the first executable line.
|
| +# The path to common.sh should be relative to your script's location.
|
| +. "$(dirname "$0")/common.sh"
|
| +
|
| +# Load functions and constants for chromeos-install
|
| +. "$(dirname "$0")/chromeos-common.sh"
|
| +
|
| +# For update_partition_table
|
| . "$(dirname "$0")/resize_stateful_partition.sh"
|
|
|
| -# Script must be run inside the chroot.
|
| -restart_in_chroot_if_needed $*
|
| +
|
| +# We need to be in the chroot to emerge test packages.
|
| +assert_inside_chroot
|
|
|
| get_default_board
|
|
|
| -# Constants
|
| -TEMP_IMG=$(mktemp "/tmp/temp_img.XXXXXX")
|
| -RECOVERY_IMAGE="recovery_image.bin"
|
| +DEFINE_string board "$DEFAULT_BOARD" "Board for which the image was built" b
|
| +DEFINE_integer statefulfs_sectors 4096 \
|
| + "Number of sectors to use for the stateful filesystem"
|
| +# Skips the build steps and just does the kernel swap.
|
| +DEFINE_string kernel_image "" \
|
| + "Path to a pre-built recovery kernel"
|
| +DEFINE_string kernel_outfile "" \
|
| + "Filename and path to emit the kernel outfile to. \
|
| +If empty, emits to IMAGE_DIR."
|
| +DEFINE_string image "" "Path to the image to use"
|
| +DEFINE_string to "" \
|
| + "Path to the image to create. If empty, defaults to \
|
| +IMAGE_DIR/recovery_image.bin."
|
| +DEFINE_boolean kernel_image_only $FLAGS_FALSE \
|
| + "Emit the recovery kernel image only"
|
| +DEFINE_boolean sync_keys $FLAGS_TRUE \
|
| + "Update the kernel to be installed with the vblock from stateful"
|
| +DEFINE_integer jobs -1 \
|
| + "How many packages to build in parallel at maximum." j
|
| +DEFINE_string build_root "/build" \
|
| + "The root location for board sysroots."
|
| +
|
| +DEFINE_string rootfs_hash "/tmp/rootfs.hash" \
|
| + "Path where the rootfs hash should be stored."
|
|
|
| -DEFINE_string board "$DEFAULT_BOARD" "Board for which the image was built"
|
| -DEFINE_string image "" "Location of the rootfs raw image file"
|
| -DEFINE_string output "${RECOVERY_IMAGE}" \
|
| - "(optional) output image name. Default: ${RECOVERY_IMAGE}"
|
| +# Keep in sync with build_image.
|
| +DEFINE_string keys_dir "/usr/share/vboot/devkeys" \
|
| + "Directory containing the signing keys."
|
|
|
| # Parse command line
|
| FLAGS "$@" || exit 1
|
| eval set -- "${FLAGS_ARGV}"
|
|
|
| +EMERGE_CMD="emerge"
|
| +EMERGE_BOARD_CMD="emerge-${FLAGS_board}"
|
| +
|
| # No board, no default and no image set then we can't find the image
|
| if [ -z $FLAGS_image ] && [ -z $FLAGS_board ] ; then
|
| setup_board_warning
|
| die "mod_image_for_recovery failed. No board set and no image set"
|
| fi
|
|
|
| -# We have a board name but no image set. Use image at default location
|
| +# We have a board name but no image set. Use image at default location
|
| if [ -z $FLAGS_image ] ; then
|
| IMAGES_DIR="${DEFAULT_BUILD_ROOT}/images/${FLAGS_board}"
|
| FILENAME="chromiumos_image.bin"
|
| @@ -40,7 +75,7 @@ if [ -z $FLAGS_image ] ; then
|
| fi
|
|
|
| # Turn path into an absolute path.
|
| -FLAGS_image=$(eval readlink -f ${FLAGS_image})
|
| +FLAGS_image=`eval readlink -f ${FLAGS_image}`
|
|
|
| # Abort early if we can't find the image
|
| if [ ! -f $FLAGS_image ] ; then
|
| @@ -48,73 +83,284 @@ if [ ! -f $FLAGS_image ] ; then
|
| exit 1
|
| fi
|
|
|
| -set -u
|
| -set -e
|
| +# What cross-build are we targeting?
|
| +. "${FLAGS_build_root}/${FLAGS_board}/etc/make.conf.board_setup"
|
| +# Figure out ARCH from the given toolchain.
|
| +# TODO: Move to common.sh as a function after scripts are switched over.
|
| +TC_ARCH=$(echo "${CHOST}" | awk -F'-' '{ print $1 }')
|
| +case "${TC_ARCH}" in
|
| + arm*)
|
| + ARCH="arm"
|
| + error "ARM recovery mode is still in the works. Use a normal image for now."
|
| + ;;
|
| + *86)
|
| + ARCH="x86"
|
| + ;;
|
| + *)
|
| + error "Unable to determine ARCH from toolchain: ${CHOST}"
|
| + exit 1
|
| +esac
|
|
|
| -# Constants
|
| -IMAGE_DIR="$(dirname "$FLAGS_image")"
|
| +get_install_vblock() {
|
| + # If it exists, we need to copy the vblock over to stateful
|
| + # This is the real vblock and not the recovery vblock.
|
| + local stateful_offset=$(partoffset "$FLAGS_image" 1)
|
| + local stateful_mnt=$(mktemp -d)
|
| + local out=$(mktemp)
|
| +
|
| + set +e
|
| + sudo mount -o ro,loop,offset=$((stateful_offset * 512)) \
|
| + "$FLAGS_image" $stateful_mnt
|
| + sudo cp "$stateful_mnt/vmlinuz_hd.vblock" "$out"
|
| + sudo chown $USER "$out"
|
| +
|
| + sudo umount -d "$stateful_mnt"
|
| + rmdir "$stateful_mnt"
|
| + set -e
|
| + echo "$out"
|
| +}
|
| +
|
| +emerge_recovery_kernel() {
|
| + echo "Emerging custom recovery initramfs and kernel"
|
| + local emerge_flags="-uDNv1 --usepkg=n --selective=n"
|
|
|
| -# Creates a dev recovery image using an existing dev install shim
|
| -# If successful, content of --payload_dir is copied to a directory named
|
| -# "dev_payload" under the root of stateful partition.
|
| -create_recovery_image() {
|
| - local src_img=$1 # base image
|
| - local src_state=$(mktemp "/tmp/src_state.XXXXXX")
|
| - local stateful_offset=$(partoffset ${src_img} 1)
|
| - local stateful_count=$(partsize ${src_img} 1)
|
| -
|
| - dd if="${src_img}" of="${src_state}" conv=notrunc bs=512 \
|
| - skip=${stateful_offset} count=${stateful_count}
|
| -
|
| - # Mount original stateful partition to figure out its actual size
|
| - local src_loop_dev=$(get_loop_dev)
|
| - trap "cleanup_loop_dev ${src_loop_dev}" EXIT
|
| -
|
| - # Setup loop dev
|
| - sudo losetup $src_loop_dev $src_state
|
| - local block_size=$(sudo /sbin/dumpe2fs $src_loop_dev | grep "Block size:" \
|
| - | tr -d ' ' | cut -f2 -d:)
|
| - echo "block_size = $block_size"
|
| - local min_size=$(sudo /sbin/resize2fs -P $src_loop_dev | tr -d ' ' \
|
| - | cut -f2 -d:)
|
| - echo "min_size = $min_size $block_size blocks"
|
| -
|
| - # Add 20%, convert to 512-byte sectors and round up to 2Mb boundary
|
| - local min_sectors=$(roundup $(((min_size * block_size * 120) / (512 * 100))))
|
| - echo "min_sectors = ${min_sectors} 512-byte blocks"
|
| - sudo e2fsck -fp "${src_loop_dev}"
|
| - # Resize using 512-byte sectors
|
| - sudo /sbin/resize2fs $src_loop_dev ${min_sectors}s
|
| -
|
| - # Delete the loop
|
| + $EMERGE_BOARD_CMD \
|
| + $emerge_flags --binpkg-respect-use=y \
|
| + chromeos-initramfs || die "no initramfs"
|
| + USE="initramfs" $EMERGE_BOARD_CMD \
|
| + $emerge_flags --binpkg-respect-use=y \
|
| + virtual/kernel
|
| +}
|
| +
|
| +create_recovery_kernel_image() {
|
| + local sysroot="${FLAGS_build_root}/${FLAGS_board}"
|
| + local vmlinuz="$sysroot/boot/vmlinuz"
|
| + local root_dev=$(sudo losetup -f)
|
| + local root_offset=$(partoffset "$FLAGS_image" 3)
|
| + local root_size=$(partsize "$FLAGS_image" 3)
|
| +
|
| + sudo losetup \
|
| + -o $((root_offset * 512)) \
|
| + --sizelimit $((root_size * 512)) \
|
| + "$root_dev" \
|
| + "$FLAGS_image"
|
| +
|
| + trap "sudo losetup -d $root_dev" EXIT
|
| +
|
| + cros_root=/dev/sd%D%P
|
| + if [[ "${ARCH}" = "arm" ]]; then
|
| + cros_root='/dev/${devname}${rootpart}'
|
| + fi
|
| + if grep -q enable_rootfs_verification "${IMAGE_DIR}/boot.desc"; then
|
| + cros_root=/dev/dm-0
|
| + fi
|
| + # TODO(wad) LOAD FROM IMAGE KERNEL AND NOT BOOT.DESC
|
| + local verity_args=$(grep -- '--verity_' "${IMAGE_DIR}/boot.desc")
|
| + # Convert the args to the right names and clean up extra quoting.
|
| + # TODO(wad) just update these everywhere
|
| + verity_args=$(echo $verity_args | sed \
|
| + -e 's/verity_algorithm/verity_hash_alg/g' \
|
| + -e 's/verity_depth/verity_tree_depth/g' \
|
| + -e 's/"//g')
|
| +
|
| + # Tie the installed recovery kernel to the final kernel. If we don't
|
| + # do this, a normal recovery image could be used to drop an unsigned
|
| + # kernel on without a key-change check.
|
| + # Doing this here means that the kernel and initramfs creation can
|
| + # be done independently from the image to be modified as long as the
|
| + # chromeos-recovery interfaces are the same. It allows for the signer
|
| + # to just compute the new hash and update the kernel command line during
|
| + # recovery image generation. (Alternately, it means an image can be created,
|
| + # modified for recovery, then passed to a signer which can then sign both
|
| + # partitions appropriately without needing any external dependencies.)
|
| + local kern_offset=$(partoffset "$FLAGS_image" 2)
|
| + local kern_size=$(partsize "$FLAGS_image" 2)
|
| + local kern_tmp=$(mktemp)
|
| + local kern_hash=
|
| +
|
| + dd if="$FLAGS_image" bs=512 count=$kern_size skip=$kern_offset of="$kern_tmp"
|
| + # We're going to use the real signing block.
|
| + if [ $FLAGS_sync_keys -eq $FLAGS_TRUE ]; then
|
| + dd if="$INSTALL_VBLOCK" of="$kern_tmp" conv=notrunc
|
| + fi
|
| + local kern_hash=$(sha1sum "$kern_tmp" | cut -f1 -d' ')
|
| + rm "$kern_tmp"
|
| +
|
| + # TODO(wad) add FLAGS_boot_args support too.
|
| + ${SCRIPTS_DIR}/build_kernel_image.sh \
|
| + --arch="${ARCH}" \
|
| + --to="$RECOVERY_KERNEL_IMAGE" \
|
| + --hd_vblock="$RECOVERY_KERNEL_VBLOCK" \
|
| + --vmlinuz="$vmlinuz" \
|
| + --working_dir="${IMAGE_DIR}" \
|
| + --boot_args="panic=60 cros_recovery kern_b_hash=$kern_hash" \
|
| + --keep_work \
|
| + --rootfs_image=${root_dev} \
|
| + --rootfs_hash=${FLAGS_rootfs_hash} \
|
| + --root=${cros_root} \
|
| + --keys_dir="${FLAGS_keys_dir}" \
|
| + --nouse_dev_keys \
|
| + ${verity_args}
|
| + sudo rm "$FLAGS_rootfs_hash"
|
| + sudo losetup -d "$root_dev"
|
| + trap - RETURN
|
| +
|
| + # Update the EFI System Partition configuration so that the kern_hash check
|
| + # passes.
|
| + local efi_dev=$(sudo losetup -f)
|
| + local efi_offset=$(partoffset "$FLAGS_image" 12)
|
| + local efi_size=$(partsize "$FLAGS_image" 12)
|
| +
|
| + sudo losetup \
|
| + -o $((efi_offset * 512)) \
|
| + --sizelimit $((efi_size * 512)) \
|
| + "$efi_dev" \
|
| + "$FLAGS_image"
|
| + local efi_dir=$(mktemp -d)
|
| + trap "sudo losetup -d $efi_dev && rmdir \"$efi_dir\"" EXIT
|
| + sudo mount "$efi_dev" "$efi_dir"
|
| + sudo sed -i -e "s/cros_legacy/cros_legacy kern_b_hash=$kern_hash/g" \
|
| + "$efi_dir/syslinux/usb.A.cfg" || true
|
| + # This will leave the hash in the kernel for all boots, but that should be
|
| + # safe.
|
| + sudo sed -i -e "s/cros_efi/cros_efi kern_b_hash=$kern_hash/g" \
|
| + "$efi_dir/efi/boot/grub.cfg" || true
|
| + sudo umount "$efi_dir"
|
| + sudo losetup -d "$efi_dev"
|
| + rmdir "$efi_dir"
|
| trap - EXIT
|
| - cleanup_loop_dev ${src_loop_dev}
|
| -
|
| - # Truncate the image at the new size
|
| - dd if=/dev/zero of=$src_state bs=512 seek=$min_sectors count=0
|
| -
|
| - # Mount and touch .recovery # Soon not to be needed :/
|
| - local new_mnt=$(mktemp -d "/tmp/src_mnt.XXXXXX")
|
| - mkdir -p "${new_mnt}"
|
| - local new_loop_dev=$(get_loop_dev)
|
| - trap "cleanup_loop_dev ${new_loop_dev} && rmdir ${new_mnt} && \
|
| - rm -f ${src_state}" EXIT
|
| - sudo mount -o loop=${new_loop_dev} "${src_state}" "${new_mnt}"
|
| - trap "umount_from_loop_dev ${new_mnt} && rm -f ${src_state}" EXIT
|
| - sudo touch "${new_mnt}/.recovery"
|
| -
|
| - (update_partition_table $src_img $src_state $min_sectors $TEMP_IMG)
|
| - # trap handler will handle unmount and clean up of loop device and temp files
|
| }
|
|
|
| -# Main
|
| -DST_PATH="${IMAGE_DIR}/${FLAGS_output}"
|
| -echo "Making a copy of original image ${FLAGS_image}"
|
| -(create_recovery_image $FLAGS_image)
|
| +install_recovery_kernel() {
|
| + local kern_a_offset=$(partoffset "$RECOVERY_IMAGE" 2)
|
| + local kern_a_size=$(partsize "$RECOVERY_IMAGE" 2)
|
| + local kern_b_offset=$(partoffset "$RECOVERY_IMAGE" 4)
|
| + local kern_b_size=$(partsize "$RECOVERY_IMAGE" 4)
|
| + # Backup original kernel to KERN-B
|
| + dd if="$RECOVERY_IMAGE" of="$RECOVERY_IMAGE" bs=512 \
|
| + count=$kern_a_size \
|
| + skip=$kern_a_offset \
|
| + seek=$kern_b_offset \
|
| + conv=notrunc
|
| +
|
| + # We're going to use the real signing block.
|
| + if [ $FLAGS_sync_keys -eq $FLAGS_TRUE ]; then
|
| + dd if="$INSTALL_VBLOCK" of="$RECOVERY_IMAGE" bs=512 \
|
| + seek=$kern_b_offset \
|
| + conv=notrunc
|
| + fi
|
| +
|
| + # Install the recovery kernel as primary.
|
| + dd if="$RECOVERY_KERNEL_IMAGE" of="$RECOVERY_IMAGE" bs=512 \
|
| + seek=$kern_a_offset \
|
| + count=$kern_a_size \
|
| + conv=notrunc
|
| +
|
| + # Repeat for the legacy bioses.
|
| + # Replace vmlinuz.A with the recovery version
|
| + local sysroot="${FLAGS_build_root}/${FLAGS_board}"
|
| + local vmlinuz="$sysroot/boot/vmlinuz"
|
| + local esp_offset=$(partoffset "$RECOVERY_IMAGE" 12)
|
| + local esp_mnt=$(mktemp -d)
|
| + set +e
|
| + local failed=0
|
| + sudo mount -o loop,offset=$((esp_offset * 512)) "$RECOVERY_IMAGE" "$esp_mnt"
|
| + sudo cp "$vmlinuz" "$esp_mnt/syslinux/vmlinuz.A" || failed=1
|
| + sudo umount -d "$esp_mnt"
|
| + rmdir "$esp_mnt"
|
| + set -e
|
| + if [ $failed -eq 1 ]; then
|
| + echo "Failed to copy recovery kernel to ESP"
|
| + return 1
|
| + fi
|
| + return 0
|
| +}
|
| +
|
| +maybe_resize_stateful() {
|
| + # Rebuild the image with a 1 sector stateful partition
|
| + local err=0
|
| + local small_stateful=$(mktemp)
|
| + dd if=/dev/zero of="$small_stateful" bs=512 \
|
| + count=${FLAGS_statefulfs_sectors}
|
| + trap "rm $small_stateful" RETURN
|
| + # Don't bother with ext3 for such a small image.
|
| + /sbin/mkfs.ext2 -F -b 4096 "$small_stateful"
|
| +
|
| + # If it exists, we need to copy the vblock over to stateful
|
| + # This is the real vblock and not the recovery vblock.
|
| + local new_stateful_mnt=$(mktemp -d)
|
| +
|
| + set +e
|
| + sudo mount -o loop $small_stateful $new_stateful_mnt
|
| + sudo cp "$INSTALL_VBLOCK" "$new_stateful_mnt/vmlinuz_hd.vblock"
|
| + sudo mkdir "$new_stateful_mnt/var"
|
| + sudo umount -d "$new_stateful_mnt"
|
| + rmdir "$new_stateful_mnt"
|
| + set -e
|
| +
|
| + # Create a recovery image of the right size
|
| + # TODO(wad) Make the developer script case create a custom GPT with
|
| + # just the kernel image and stateful.
|
| + update_partition_table "$FLAGS_image" "$small_stateful" 4096 "$RECOVERY_IMAGE"
|
| + return $err
|
| +}
|
| +
|
| +# main process begins here.
|
| +
|
| +# Make sure this is really what the user wants, before nuking the device
|
| +echo "Creating recovery image ${FLAGS_to} from ${FLAGS_image} . . . "
|
| +
|
| +set -e
|
| +set -u
|
| +
|
| +IMAGE_DIR="$(dirname "$FLAGS_image")"
|
| +IMAGE_NAME="$(basename "$FLAGS_image")"
|
| +RECOVERY_IMAGE="${FLAGS_to:-$IMAGE_DIR/recovery_image.bin}"
|
| +RECOVERY_KERNEL_IMAGE=\
|
| +"${FLAGS_kernel_outfile:-${IMAGE_DIR}/recovery_vmlinuz.image}"
|
| +RECOVERY_KERNEL_VBLOCK="${RECOVERY_KERNEL_IMAGE}.vblock"
|
| +STATEFUL_DIR="$IMAGE_DIR/stateful_partition"
|
| +SCRIPTS_DIR=$(dirname "$0")
|
|
|
| -if [ -n ${TEMP_IMG} ] && [ -f ${TEMP_IMG} ]; then
|
| - mv -f $TEMP_IMG $DST_PATH
|
| - echo "Recovery image created at ${DST_PATH}"
|
| +# Mounts gpt image and sets up var, /usr/local and symlinks.
|
| +# If there's a dev payload, mount stateful
|
| +# offset=$(partoffset "${FLAGS_from}/${filename}" 1)
|
| +# sudo mount ${ro_flag} -o loop,offset=$(( offset * 512 )) \
|
| +# "${FLAGS_from}/${filename}" "${FLAGS_stateful_mountpt}"
|
| +# If not, resize stateful to 1 sector.
|
| +#
|
| +
|
| +if [ $FLAGS_kernel_image_only -eq $FLAGS_TRUE -a \
|
| + -n "$FLAGS_kernel_image" ]; then
|
| + die "Cannot use --kernel_image_only with --kernel_image"
|
| +fi
|
| +
|
| +INSTALL_VBLOCK=$(get_install_vblock)
|
| +if [ -z "$INSTALL_VBLOCK" ]; then
|
| + die "Could not copy the vblock from stateful."
|
| +fi
|
| +
|
| +if [ -z "$FLAGS_kernel_image" ]; then
|
| + emerge_recovery_kernel
|
| + create_recovery_kernel_image
|
| else
|
| - echo "Failed to create recovery image"
|
| + RECOVERY_KERNEL_IMAGE="$FLAGS_kernel_image"
|
| +fi
|
| +echo "Kernel emitted: $RECOVERY_KERNEL_IMAGE."
|
| +
|
| +if [ $FLAGS_kernel_image_only -eq $FLAGS_TRUE ]; then
|
| + echo "Kernel emitted. Stopping there."
|
| + rm "$INSTALL_VBLOCK"
|
| + exit 0
|
| fi
|
| +
|
| +rm "$RECOVERY_IMAGE" || true # Start fresh :)
|
| +
|
| +trap "rm \"$RECOVERY_IMAGE\" && rm \"$INSTALL_VBLOCK\"" EXIT
|
| +
|
| +maybe_resize_stateful # Also copies the image
|
| +
|
| +install_recovery_kernel
|
| +
|
| +print_time_elapsed
|
| +trap - EXIT
|
|
|