flatcar-scripts/bin/cros_make_image_bootable
Brandon Philips 4057d5590d feat(disk_layout): follow new CoreOS partition layout
As outlined here we need a new partition layout, this patch makes the
necessary changes:
https://groups.google.com/forum/#!topic/coreos-dev/bA7gwGGoTng

The first big change is making all of the scripts obey partition numbers
based on labels in the disk_layout.json. This makes it much easier to
change later on.

The second big change is in the layout itself. The json file was updated
to reflect the document above.

And finally the grub boot configuration needed for pv-grub and pygrub
were added to the create_legacy_bootloader_templates.sh library utlity.

Everything seems to work and boot now.
2013-05-12 12:31:03 -07:00

376 lines
14 KiB
Bash
Executable File

#!/bin/bash
#
# Copyright (c) 2010 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.
#
# Script which ensures that a given image has an up-to-date
# kernel partition, rootfs integrity hashes, and legacy bootloader configs.
# --- BEGIN COMMON.SH BOILERPLATE ---
# Load common CrOS utilities. Inside the chroot this file is installed in
# /usr/lib/crosutils. Outside the chroot we find it relative to the script's
# location.
find_common_sh() {
local common_paths=("$(dirname "$(readlink -f "$0")")/.." /usr/lib/crosutils)
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
# --- END COMMON.SH BOILERPLATE ---
# Need to be inside the chroot to load chromeos-common.sh
assert_inside_chroot
# Load functions and constants for chromeos-install
. /usr/lib/installer/chromeos-common.sh || exit 1
. "${BUILD_LIBRARY_DIR}/build_image_util.sh" || exit 1
. "${BUILD_LIBRARY_DIR}/disk_layout_util.sh" || exit 1
switch_to_strict_mode
if [ $# -lt 2 ]; then
echo "Usage: ${0} /PATH/TO/IMAGE IMAGE.BIN [shflags overrides]"
exit 1
fi
IMAGE_DIR="$(readlink -f "${1}")"
BOOT_DESC_FILE="${IMAGE_DIR}/boot.desc"
IMAGE="${IMAGE_DIR}/${2}"
shift
shift
FLAG_OVERRIDES="${@}"
if [ ! -r "${BOOT_DESC_FILE}" ]; then
warn "${BOOT_DESC_FILE} cannot be read!"
warn "Falling back to command line parsing"
BOOT_DESC="${@}"
else
BOOT_DESC="$(cat ${BOOT_DESC_FILE} | tr -s '\n' ' ')"
info "Boot-time configuration for $(dirname "${IMAGE}"): "
cat ${BOOT_DESC_FILE} | while read line; do
info " ${line}"
done
fi
if [ ! -r "${IMAGE}" ]; then
die "${IMAGE} cannot be read!"
fi
locate_gpt
set +e
# Now parse the build settings from ${OUTPUT_DIR}/boot.desc
DEFINE_string adjust_part "" \
"Adjustments to apply to the partition table"
DEFINE_string board "${DEFAULT_BOARD}" \
"Board we're building for."
DEFINE_string image_type "base" \
"Type of image we're building for (base/factory_install)."
DEFINE_string output_dir "/tmp" \
"Directory to place output in."
DEFINE_string image "chromiumos_base.img" \
"Full path to the chromiumos image to make bootable."
DEFINE_string arch "x86" \
"Architecture to make bootable for: arm, x86, or amd64"
DEFINE_boolean cleanup_dirs ${FLAGS_TRUE} \
"Whether the mount dirs should be removed on completion."
DEFINE_string boot_args "noinitrd" \
"Additional boot arguments to pass to the commandline"
DEFINE_integer rootfs_size 720 \
"rootfs filesystem size in MBs."
# ceil(0.1 * rootfs_size) is a good minimum.
DEFINE_integer rootfs_hash_pad 8 \
"MBs reserved at the end of the rootfs image."
DEFINE_string rootfs_hash "/tmp/rootfs.hash" \
"Path where the rootfs hash should be stored."
# TODO(taysom): when we turn on boot cache, both verification and
# bootcache should have their default be FLAGS_TRUE.
DEFINE_boolean enable_rootfs_verification ${FLAGS_FALSE} \
"Default all bootloaders to NOT use kernel-based root fs integrity checking."
DEFINE_boolean enable_bootcache ${FLAGS_FALSE} \
"Default all bootloaders to NOT use bootcache."
DEFINE_integer verity_error_behavior 3 \
"Kernel verified boot error behavior (0: I/O errors, 1: reboot, 2: nothing)"
DEFINE_integer verity_max_ios -1 \
"Number of outstanding I/O operations dm-verity caps at."
DEFINE_string verity_algorithm "sha1" \
"Cryptographic hash algorithm used for kernel vboot."
DEFINE_string verity_salt "" \
"Salt for rootfs hash tree."
DEFINE_string keys_dir "/usr/share/vboot/devkeys" \
"Directory containing the signing keys."
DEFINE_string rootfs_mountpoint "/tmp/rootfs" \
"Path where the rootfs can be safely mounted"
DEFINE_string statefulfs_mountpoint "/tmp/statefulfs" \
"Path where the statefulfs can be safely mounted"
DEFINE_string espfs_mountpoint "/tmp/espfs" \
"Path where the espfs can be safely mounted"
DEFINE_boolean use_dev_keys ${FLAGS_FALSE} \
"Use developer keys for signing. (Default: false)"
DEFINE_boolean fsck_rootfs ${FLAGS_FALSE} \
"Check integrity of the rootfs on the modified image."
# TODO(pkumar): Remove once known that no images are using this flag
DEFINE_boolean crosbug12352_arm_kernel_signing ${FLAGS_FALSE} \
"This flag is deprecated but the bots still need parse old images."
# TODO(sosa): Remove once known images no longer use this in their config.
DEFINE_string arm_extra_bootargs "" "DEPRECATED FLAG. Do not use."
DEFINE_boolean force_developer_mode ${FLAGS_FALSE} \
"Add cros_debug to boot args."
DEFINE_boolean enable_squashfs ${FLAGS_FALSE} \
"Make the rootfs of the image squashfs."
DEFINE_string squash_sort_file "" \
"Specify the priority of files when squashing the rootfs."
DEFINE_string enable_serial "" \
"Enable serial port for printks. Example values: ttyS0"
# Parse the boot.desc and any overrides
eval set -- "${BOOT_DESC} ${FLAG_OVERRIDES}"
FLAGS "${@}" || exit 1
[ -z "${FLAGS_verity_salt}" ] && FLAGS_verity_salt=$(make_salt)
. "${BUILD_LIBRARY_DIR}/board_options.sh" || exit 1
# Only now can we die on error. shflags functions leak non-zero error codes,
# so will die prematurely if 'switch_to_strict_mode' is specified before now.
switch_to_strict_mode -u
# $1 - Directory where developer rootfs is mounted.
# $2 - Directory where developer stateful_partition is mounted.
# $3 - Directory where the ESP partition is mounted.
mount_gpt_cleanup() {
local rootfs="${1-$FLAGS_rootfs_mountpoint}"
local statefs="${2-$FLAGS_statefulfs_mountpoint}"
local espfs="${3-$FLAGS_espfs_mountpoint}"
"${SCRIPTS_DIR}/mount_gpt_image.sh" \
-u -r "${rootfs}" -s "${statefs}" -e "${espfs}"
}
make_image_bootable() {
local image="$1"
local use_dev_keys=
# Default to non-verified
local enable_rootfs_verification_flag=--noenable_rootfs_verification
if [[ ${FLAGS_enable_rootfs_verification} -eq ${FLAGS_TRUE} ]]; then
enable_rootfs_verification_flag=--enable_rootfs_verification
fi
local enable_bootcache_flag=--noenable_bootcache
if [[ ${FLAGS_enable_bootcache} -eq ${FLAGS_TRUE} ]]; then
enable_bootcache_flag=--enable_bootcache
fi
trap "mount_gpt_cleanup" EXIT
"${SCRIPTS_DIR}/mount_gpt_image.sh" --from "$(dirname "${image}")" \
--image "$(basename ${image})" -r "${FLAGS_rootfs_mountpoint}" \
-s "${FLAGS_statefulfs_mountpoint}"
legacy_offset_size_export ${image}
# The rootfs should never be mounted rw again after this point without
# re-calling make_image_bootable.
sudo mount -o remount,ro "${FLAGS_rootfs_mountpoint}"
# Newer `mount` will decode the filename backing the loop device,
# so we need to dig deeper and find the answer ourselves.
root_dev=$(awk -v mnt="${FLAGS_rootfs_mountpoint}" \
'$2 == mnt { print $1 }' /proc/mounts)
# Make the filesystem un-mountable as read-write.
# mount_gpt_image.sh will undo this as needed.
# TODO(wad) make sure there is parity in the signing scripts.
if [ ${FLAGS_enable_rootfs_verification} -eq ${FLAGS_TRUE} ]; then
# TODO(wad) this would be a good place to reset any other ext2 metadata.
warn "Disabling r/w mount of the root filesystem"
disable_rw_mount "$root_dev"
fi
if [ ${FLAGS_use_dev_keys} -eq ${FLAGS_TRUE} ]; then
use_dev_keys="--use_dev_keys"
fi
if [ ${FLAGS_force_developer_mode} -eq ${FLAGS_TRUE} ]; then
FLAGS_boot_args="${FLAGS_boot_args} cros_debug"
fi
local squash_sort_flag=
if [ -n "${FLAGS_squash_sort_file}" ]; then
squash_sort_flag="-sort ${FLAGS_squash_sort_file}"
fi
if [ $FLAGS_enable_squashfs -eq $FLAGS_TRUE ]; then
local squashfs_img="${FLAGS_output_dir}/squashfs.image"
sudo mksquashfs "${FLAGS_rootfs_mountpoint}" ${squashfs_img} -comp lzo \
-noI -noF -ef ${SCRIPTS_DIR}/exclude-list -wildcards ${squash_sort_flag}
root_dev=$squashfs_img
fi
# Builds the kernel partition image. The temporary files are kept around
# so that we can perform a load_kernel_test later on the final image.
${SCRIPTS_DIR}/build_kernel_image.sh \
--arch="${FLAGS_arch}" \
--to="${FLAGS_output_dir}/vmlinuz.image" \
--hd_vblock="${FLAGS_output_dir}/vmlinuz_hd.vblock" \
--vmlinuz="${FLAGS_rootfs_mountpoint}/boot/vmlinuz" \
--working_dir="${FLAGS_output_dir}" \
--boot_args="${FLAGS_boot_args}" \
--keep_work \
--rootfs_image=${root_dev} \
--rootfs_hash=${FLAGS_rootfs_hash} \
--verity_hash_alg=${FLAGS_verity_algorithm} \
--verity_max_ios=${FLAGS_verity_max_ios} \
--verity_error_behavior=${FLAGS_verity_error_behavior} \
--verity_salt=${FLAGS_verity_salt} \
--keys_dir="${FLAGS_keys_dir}" \
--enable_serial="${FLAGS_enable_serial}" \
${enable_rootfs_verification_flag} \
${enable_bootcache_flag} \
${use_dev_keys}
# Check the size of kernel image and issue warning when image size is
# near the limit.
local kernel_image_size=$(stat -c '%s' ${FLAGS_output_dir}/vmlinuz.image)
info "Kernel image size is ${kernel_image_size} bytes."
if [[ ${kernel_image_size} -gt $((16 * 1024 * 1024)) ]]; then
die "Kernel image is larger than 16 MB."
elif [[ ${kernel_image_size} -gt $((14 * 1024 * 1024)) ]]; then
warn "Kernel image is larger than 14 MB. Limit is 16 MB."
fi
local rootfs_hash_size=$(stat -c '%s' ${FLAGS_rootfs_hash})
local rootfs_fs_size=$(get_filesystem_size ${FLAGS_image_type} ${NUM_ROOTFS_A})
local rootfs_partition_size=$(get_partition_size ${FLAGS_image_type} ${NUM_ROOTFS_A})
local rootfs_hash_pad=$(( rootfs_partition_size - rootfs_fs_size ))
info "Appending rootfs.hash (${rootfs_hash_size} bytes) to the root fs"
if [[ ${rootfs_hash_size} -gt ${rootfs_hash_pad} ]]
then
die "rootfs_partition_size - rootfs_fs_size is less than the needed " \
"rootfs_hash_size (${rootfs_hash_size}), update your disk layout " \
"configuration"
fi
# Unfortunately, mount_gpt_image uses mount and not losetup to create the
# loop devices. This means that they are not the correct size. We have to
# write directly to the image to append the hash tree data.
local hash_offset="$(partoffset ${image} ${NUM_ROOTFS_A})"
if [ $FLAGS_enable_squashfs -eq $FLAGS_TRUE ]; then
rootfs_file_size=$(stat -c '%s' ${root_dev})
hash_offset=$((hash_offset + (${rootfs_file_size} / 512)))
else
hash_offset=$((hash_offset + (${rootfs_fs_size} / 512)))
fi
sudo dd bs=512 \
seek=${hash_offset} \
if="${FLAGS_rootfs_hash}" \
of="${image}" \
conv=notrunc \
status=none
# Move the verification block needed for the hard disk install to the
# stateful partition. Mount stateful fs, copy file, and umount fs.
# In original CL: http://codereview.chromium.org/2868044, this was done in
# create_base_image(). However, it could break the build if it is a clean
# build because vmlinuz_hd.vblock hasn't been created by build_kernel_image.sh
sudo cp "${FLAGS_output_dir}/vmlinuz_hd.vblock" \
"${FLAGS_statefulfs_mountpoint}"
# We should update the esp in place in the image.
local bootloader_to="${image}"
local esp_offset="$(partoffset ${image} ${NUM_ESP})"
esp_offset=$((esp_offset * 512)) # sectors to bytes
local esp_size="$(partsize ${image} ${NUM_ESP})"
esp_size=$((esp_size * 512)) # sectors to bytes
local bootloader_to_flags="--to_offset=${esp_offset} --to_size=${esp_size}"
# Update partition 12
${SCRIPTS_DIR}/update_bootloaders.sh \
--arch=${FLAGS_arch} \
--to="${bootloader_to}" \
--from="${FLAGS_rootfs_mountpoint}"/boot \
--vmlinuz="${FLAGS_rootfs_mountpoint}"/boot/vmlinuz \
${bootloader_to_flags}
# We don't need to keep these files around anymore.
sudo rm "${FLAGS_rootfs_hash}" "${FLAGS_output_dir}/vmlinuz.image" \
"${FLAGS_output_dir}/vmlinuz_hd.vblock"
trap - EXIT
${SCRIPTS_DIR}/mount_gpt_image.sh -u -r "${FLAGS_rootfs_mountpoint}" \
-s "${FLAGS_statefulfs_mountpoint}"
# I can only copy the squashfs image to the image only when it is umounted.
if [ $FLAGS_enable_squashfs -eq $FLAGS_TRUE ]; then
# copy the squashfs image to the partition
info "copy the squashfs to the partition"
local part_offset="$(partoffset ${image} ${NUM_ROOTFS_A})"
sudo dd bs=512 if="${squashfs_img}" of="${image}" \
seek=${part_offset} conv=notrunc status=none
sudo rm "${squashfs_img}"
fi
}
verify_image_rootfs() {
local image=$1
local rootfs_offset="$(partoffset ${image} 3)"
local rootfs_tmp_file=$(mktemp)
trap "rm ${rootfs_tmp_file}" EXIT
sudo dd if="${image}" of="${rootfs_tmp_file}" bs=512 skip="${rootfs_offset}" \
status=none
# This flips the read-only compatibility flag, so that
# e2fsck does not complain about unknown file system capabilities.
enable_rw_mount "${rootfs_tmp_file}"
info "Running e2fsck to check root file system for errors"
sudo e2fsck -fn "${rootfs_tmp_file}" ||
die "Root file system has errors, please ensure boot.desc and/or \
command line parameters are correct"
}
# Store output and temporary files next to image.
FLAGS_output_dir="${IMAGE_DIR}"
FLAGS_rootfs_hash="${IMAGE_DIR}/rootfs.hash"
FLAGS_rootfs_mountpoint="${IMAGE_DIR}/rootfs_dir"
FLAGS_statefulfs_mountpoint="${IMAGE_DIR}/stateful_dir"
FLAGS_espfs_mountpoint="${IMAGE_DIR}/esp"
# Create the directories if they don't exist.
mkdir -p ${FLAGS_rootfs_mountpoint}
mkdir -p ${FLAGS_statefulfs_mountpoint}
mkdir -p ${FLAGS_espfs_mountpoint}
make_image_bootable "${IMAGE}"
# We can't verify the image if squashfs is enabled because the kernel
# on the host does not support squashfs with LZO
if [ ${FLAGS_fsck_rootfs} -eq ${FLAGS_TRUE} \
-a ${FLAGS_enable_squashfs} -eq ${FLAGS_FALSE} ]; then
verify_image_rootfs "${IMAGE}"
fi
if [ ${FLAGS_cleanup_dirs} -eq ${FLAGS_TRUE} ]; then
rmdir ${FLAGS_rootfs_mountpoint}
rmdir ${FLAGS_statefulfs_mountpoint}
rmdir ${FLAGS_espfs_mountpoint}
fi