From 4a675e18a05568a9048ef734b428f291317dd3e0 Mon Sep 17 00:00:00 2001 From: Will Drewry Date: Sat, 3 Jul 2010 13:35:02 -0500 Subject: [PATCH] [PATCH 5/5] build_image: refactor bootloaders and enable vboot config paths This change adds make_image_bootable which combines the prior changes to make ARM, x86-legacy, x86-efi, and x86-fw share kernel commandlines (where possible) and not rely on keeping data stored on the rootfs to do so. It does not enable a cut over to syslinux yet, though. TEST=manually built image and tested boot and install (in progress) BUG=chromium-os:327 Review URL: http://codereview.chromium.org/2834038 --- build_image | 188 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 138 insertions(+), 50 deletions(-) diff --git a/build_image b/build_image index c4f94dfe4e..41d03db46e 100755 --- a/build_image +++ b/build_image @@ -13,6 +13,9 @@ # The path to common.sh should be relative to your script's location. . "$(dirname "$0")/common.sh" +. "$(dirname "$0")/chromeos-common.sh" # for partoffset +locate_gpt + # Script must be run inside the chroot. restart_in_chroot_if_needed $* @@ -56,6 +59,20 @@ kernel/firmware not updated)" DEFINE_boolean fast ${FLAGS_FALSE} \ "Call many emerges in parallel (unstable)" +DEFINE_string usb_disk /dev/sdb3 \ + "Path syslinux should use to do a usb boot. Default: /dev/sdb3" + +DEFINE_boolean use_vboot ${FLAGS_FALSE} \ + "Default the bootloaders to booting a verifying kernel. Default: False." +DEFINE_integer vboot_behavior 2 \ + "Verified boot error behavior (0: I/O errors, 1: reboot, 2: nothing) \ +Default: 2" +DEFINE_integer vboot_depth 1 \ + "Verified boot hash tree depth. Default: 1" +DEFINE_integer vboot_max_ios 1024 \ + "Number of outstanding I/O operations dm-verity caps at. Default: 1024" +DEFINE_string vboot_algorithm "sha1" \ + "Cryptographic hash algorithm used for vboot. Default : sha1" # Parse command line. FLAGS "$@" || exit 1 @@ -108,6 +125,7 @@ BOARD_ROOT="${FLAGS_build_root}/${BOARD}" ROOT_FS_IMG="${OUTPUT_DIR}/rootfs.image" ROOT_FS_DIR="${OUTPUT_DIR}/rootfs" +ROOT_FS_HASH="${OUTPUT_DIR}/rootfs.hash" STATEFUL_FS_IMG="${OUTPUT_DIR}/stateful_partition.image" STATEFUL_FS_DIR="${OUTPUT_DIR}/stateful_partition" @@ -207,6 +225,7 @@ cleanup() { if [[ -n "${STATEFUL_LOOP_DEV}" ]]; then cleanup_stateful_fs_loop + STATEFUL_LOOP_DEV= fi if [[ -n "${OEM_LOOP_DEV}" ]]; then @@ -215,10 +234,12 @@ cleanup() { if [[ -n "${LOOP_DEV}" ]]; then cleanup_rootfs_loop + LOOP_DEV= fi if [[ -n "${ESP_LOOP_DEV}" ]]; then cleanup_esp_loop + ESP_LOOP_DEV= fi # Turn die on error back on. @@ -241,22 +262,107 @@ delete_prompt() { # $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() { - "${SCRIPTS_DIR}/mount_gpt_image.sh" -u -r "$1" -s "$2" + local rootfs="${1-$ROOT_FS_DIR}" + local statefs="${2-$STATEFUL_FS_DIR}" + local espfs="${3-$ESP_FS_DIR}" + "${SCRIPTS_DIR}/mount_gpt_image.sh" \ + -u -r "${rootfs}" -s "${statefs}" -e "${espfs}" delete_prompt } +make_image_bootable() { + local image_name="$1" + cros_root=/dev/sd%D%P + if [[ "${ARCH}" = "arm" ]]; then + # TODO(wad) assumed like in build_gpt for now. + cros_root=/dev/mmcblk1p3 + fi + if [[ ${FLAGS_use_vboot} -eq ${FLAGS_TRUE} ]]; then + cros_root=/dev/dm-0 + fi + + # TODO(wad) mount the root fs to LOOP_DEV from the image + trap "mount_gpt_cleanup" EXIT + ${SCRIPTS_DIR}/mount_gpt_image.sh --from "${OUTPUT_DIR}" \ + --image "${image_name}" -r "${ROOT_FS_DIR}" \ + -s "${STATEFUL_FS_DIR}" -e "${ESP_FS_DIR}" + + sudo mount -o remount,ro "${ROOT_FS_DIR}" + root_dev=$(mount | grep -- "${ROOT_FS_DIR}" | cut -f1 -d' ' | tail -1) + + # 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="${ARCH}" \ + --to="${OUTPUT_DIR}/vmlinuz.image" \ + --vmlinuz="${OUTPUT_DIR}/boot/vmlinuz" \ + --working_dir="${OUTPUT_DIR}" \ + --keep_work \ + --rootfs_image=${root_dev} \ + --rootfs_hash=${OUTPUT_DIR}/rootfs.hash \ + --vboot_hash_alg=${FLAGS_vboot_algorithm} \ + --vboot_tree_depth=${FLAGS_vboot_depth} \ + --vboot_max_ios=${FLAGS_vboot_max_ios} \ + --vboot_error_behavior=${FLAGS_vboot_behavior} \ + --root=${cros_root} \ + --keys_dir="${SRC_ROOT}/platform/vboot_reference/tests/testkeys" + + # START_KERN_A is set by the first call to install the gpt. + local koffset="$(partoffset ${OUTPUT_DIR}/${image_name} 2)" + sudo dd if="${OUTPUT_DIR}/vmlinuz.image" of="${OUTPUT_DIR}/${image_name}" \ + conv=notrunc bs=512 seek=${koffset} + + # Populate the legacy/efi bootloader partition. + local kernel_part="--kernel_partition='${OUTPUT_DIR}/vmlinuz.image'" + local bootloader_to="${ESP_FS_IMG}" + local usb_disk="${FLAGS_usb_disk}" + local bootloader_to="$(mount | grep ${ESP_FS_DIR} | cut -f1 -d' ')" + if [[ "${ARCH}" == "arm" ]]; then + # TODO(wad) mmcblk1p3 is hardcoded for arm for now! + usb_disk="/dev/mmcblk1p3" + kernel_part="--kernel_cmdline='" + kernel_part="${kernel_part}\ +$(cat ${OUTPUT_DIR}/boot.config | tr -s '\n' ' ')" + kernel_part="${kernel_part} ${FLAGS_arm_extra_bootargs}'" + local kpart_offset="--kernel_partition_offset=${koffset}" + local kpart_size="--kernel_partition_sectors=$(partsize ${image_name} 2)" + kernel_part="${kernel_part} ${kpart_size} ${kpart_offset}" + bootloader_to="${OUTPUT_DIR}/arm.mbr" + fi + + # Update partition 12 / legacy bootloaders and arm. + ${SCRIPTS_DIR}/update_bootloaders.sh \ + --arch=${ARCH} \ + --to="${bootloader_to}" \ + --from="${OUTPUT_DIR}"/boot \ + --vmlinuz="${OUTPUT_DIR}"/boot/vmlinuz \ + --usb_disk="${usb_disk}" \ + $kernel_part + + if [[ "${ARCH}" == "arm" ]]; then + sudo dd bs=1 conv=notrunc if="${bootloader_to}" \ + of="${OUTPUT_DIR}/${image_name}" + sudo rm "${bootloader_to}" + fi + + trap - EXIT + ${SCRIPTS_DIR}/mount_gpt_image.sh -u -r "${ROOT_FS_DIR}" \ + -s "${STATEFUL_FS_DIR}" -e "${ESP_FS_DIR}" +} + # Modifies an existing image to add development packages update_dev_packages() { local image_name=$1 echo "Adding developer packages to ${image_name}" - trap "mount_gpt_cleanup \"${ROOT_FS_DIR}\" \"${STATEFUL_FS_DIR}\"" EXIT + trap "mount_gpt_cleanup" EXIT ${SCRIPTS_DIR}/mount_gpt_image.sh --from "${OUTPUT_DIR}" \ --image "${image_name}" -r "${ROOT_FS_DIR}" \ - -s "${STATEFUL_FS_DIR}" + -s "${STATEFUL_FS_DIR}" -e "${ESP_FS_DIR}" # Determine the root dir for developer packages. local root_dev_dir="${ROOT_FS_DIR}" @@ -304,7 +410,7 @@ update_dev_packages() { trap - EXIT ${SCRIPTS_DIR}/mount_gpt_image.sh -u -r "${ROOT_FS_DIR}" \ - -s "${STATEFUL_FS_DIR}" + -s "${STATEFUL_FS_DIR}" -e "${ESP_FS_DIR}" } # Update the base package on an existing image. @@ -314,11 +420,11 @@ update_base_packages() { echo "Updating base packages on ${image_name}" # Create stateful partition of the same size as the rootfs. - trap "mount_gpt_cleanup \"${ROOT_FS_DIR}\" \"${STATEFUL_FS_DIR}\"" EXIT + trap "mount_gpt_cleanup" EXIT ${SCRIPTS_DIR}/mount_gpt_image.sh --from "${OUTPUT_DIR}" \ --image "${image_name}" -r "${ROOT_FS_DIR}" \ - -s "${STATEFUL_FS_DIR}" + -s "${STATEFUL_FS_DIR}" -e "${ESP_FS_DIR}" # Emerge updated packages, exactly like when creating base image sudo INSTALL_MASK="${INSTALL_MASK}" ${EMERGE_BOARD_CMD} \ @@ -332,7 +438,7 @@ update_base_packages() { trap - EXIT ${SCRIPTS_DIR}/mount_gpt_image.sh -u -r "${ROOT_FS_DIR}" \ - -s "${STATEFUL_FS_DIR}" + -s "${STATEFUL_FS_DIR}" -e "${ESP_FS_DIR}" } create_base_image() { @@ -444,20 +550,11 @@ create_base_image() { --root="${ROOT_FS_DIR}" --root-deps=rdeps \ --usepkg chromeos ${EMERGE_JOBS} - # Create EFI System Partition to boot stock EFI BIOS (but not ChromeOS EFI - # BIOS). We only need this for x86, but it's simpler and safer to keep the - # disk images the same for both x86 and ARM. - # NOTE: The size argument for mkfs.vfat is in 1024-byte blocks. - # We'll hard-code it to 16M for now. - ESP_BLOCKS=16384 - /usr/sbin/mkfs.vfat -C ${OUTPUT_DIR}/esp.image ${ESP_BLOCKS} - ESP_LOOP_DEV=$(sudo losetup -f) - if [ -z "${ESP_LOOP_DEV}" ] ; then - echo "No free loop device. Free up a loop device or reboot. exiting. " - exit 1 - fi - sudo losetup "${ESP_LOOP_DEV}" "${ESP_FS_IMG}" - sudo mount "${ESP_LOOP_DEV}" "${ESP_FS_DIR}" + # Perform any customizations on the root file system that are needed. + "${SCRIPTS_DIR}/customize_rootfs" \ + --root="${ROOT_FS_DIR}" \ + --target="${ARCH}" \ + --board="${BOARD}" # Populates the root filesystem with legacy bootloader templates # appropriate for the platform. The autoupdater and installer will @@ -465,6 +562,7 @@ create_base_image() { # on update. # (This script does not populate vmlinuz.A and .B needed by syslinux.) use_vboot= + [[ ${FLAGS_use_vboot} -eq ${FLAGS_TRUE} ]] && use_vboot="--use_vboot" ${SCRIPTS_DIR}/create_legacy_bootloader_templates.sh \ --arch=${ARCH} \ --to="${ROOT_FS_DIR}"/boot \ @@ -477,27 +575,6 @@ create_base_image() { # Like the current vmlinuz. sudo cp -r "${ROOT_FS_DIR}"/boot/. "${OUTPUT_DIR}"/boot/ - # Until bootloader management is unified, copy EFI in here. - sudo mkdir -p "${ESP_FS_IMG}"/efi - sudo cp -r "${ROOT_FS_DIR}"/boot/efi/. "${ESP_FS_IMG}"/efi - - # 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. - # TODO(wad) add dm-verity boot args (--boot_args, --root) - ${SCRIPTS_DIR}/build_kernel_image.sh \ - --arch="${ARCH}" \ - --to="${OUTPUT_DIR}/vmlinuz.image" \ - --vmlinuz="${ROOT_FS_DIR}/boot/vmlinuz" \ - --working_dir="${OUTPUT_DIR}" \ - --keep_work \ - --keys_dir="${SRC_ROOT}/platform/vboot_reference/tests/testkeys" - - # Perform any customizations on the root file system that are needed. - "${SCRIPTS_DIR}/customize_rootfs" \ - --root="${ROOT_FS_DIR}" \ - --target="${ARCH}" \ - --board="${BOARD}" - # Don't test the factory install shim. if [[ ${FLAGS_factory_install} -eq ${FLAGS_FALSE} ]] ; then # Check that the image has been correctly created. @@ -511,7 +588,15 @@ create_base_image() { # create /usr/local or /var on host (already exist on target). setup_symlinks_on_root "/usr/local" "/var" "${STATEFUL_FS_DIR}" - # Cleanup loop devices. + # make_image_bootable will clobber vmlinuz.image for x86. + # Until then, just copy the kernel to vmlinuz.image. It is + # expected in build_gpt.sh and needed by ARM until it supports the + # full, signed kernel partition format. + cp "${ROOT_FS_DIR}/boot/vmlinuz" "${OUTPUT_DIR}/vmlinuz.image" + + # Create an empty esp image to be updated in by update_bootloaders.sh. + ${SCRIPTS_DIR}/create_esp.sh --to="${ESP_FS_IMG}" + cleanup trap delete_prompt EXIT @@ -526,13 +611,6 @@ create_base_image() { "${OUTPUT_DIR}/${image_name}" trap - EXIT - - # FIXME: only signing things for x86 right now. - if [[ "${ARCH}" = "x86" ]]; then - # Verify the final image. - load_kernel_test "${OUTPUT_DIR}/${image_name}" \ - "${OUTPUT_DIR}/kernel_subkey.vbpubk" - fi } # Create the output directory. @@ -548,6 +626,7 @@ if [[ $FLAGS_preserve -eq ${FLAGS_TRUE} ]] ; then # Copy forward pristine image, and associated files cp ${PREVIOUS_DIR}/*.sh ${PREVIOUS_DIR}/config.txt ${OUTPUT_DIR} cp ${PREVIOUS_DIR}/${PRISTINE_IMAGE_NAME} ${OUTPUT_DIR} + cp -r ${PREVIOUS_DIR}/boot ${OUTPUT_DIR}/boot # Copy forward the developer image, if we already copied forward the base. if [[ ${FLAGS_withdev} -eq ${FLAGS_TRUE} ]] && \ @@ -562,6 +641,14 @@ if [[ -f ${PRISTINE_IMG} ]] ; then else create_base_image ${PRISTINE_IMAGE_NAME} fi +make_image_bootable ${PRISTINE_IMAGE_NAME} + +# FIXME: only signing things for x86 right now. +if [[ "${ARCH}" = "x86" ]]; then + # Verify the final image. + load_kernel_test "${OUTPUT_DIR}/${PRISTINE_IMAGE_NAME}" \ + "${OUTPUT_DIR}/kernel_subkey.vbpubk" +fi # Create a developer image based on the chromium os base image. if [ "${FLAGS_withdev}" -eq "${FLAGS_TRUE}" ] ; then @@ -571,6 +658,7 @@ if [ "${FLAGS_withdev}" -eq "${FLAGS_TRUE}" ] ; then fi update_dev_packages ${DEVELOPER_IMAGE_NAME} + make_image_bootable ${DEVELOPER_IMAGE_NAME} fi # Clean up temporary files.