From 12a9900e0225d25bfef3d6d19fb59a8e138096f2 Mon Sep 17 00:00:00 2001 From: Michael Marineau Date: Fri, 5 Sep 2014 19:03:26 -0700 Subject: [PATCH 1/3] grub_install: new install script for grub, add UEFI bootloader This script replaces the standard grub-install tool to give us some more control over what is going and ensure grub-install's auto-detection magic doesn't make any incorrect choices. Also this script sets up a loopback device and mounts the EFI partition in just the right way for grub-bios-setup's auto-detection magic to work correctly. I've chosen not to adapt disk_util to use partitioned loop devices to make grub happy because ensuring loop devices get cleaned up properly for the general case gets tricky and less robust. --- build_library/grub.cfg | 8 ++- build_library/grub_install.sh | 127 ++++++++++++++++++++++++++++++++++ 2 files changed, 133 insertions(+), 2 deletions(-) create mode 100755 build_library/grub_install.sh diff --git a/build_library/grub.cfg b/build_library/grub.cfg index 69bf7d0a36..1dfc1e50aa 100644 --- a/build_library/grub.cfg +++ b/build_library/grub.cfg @@ -1,4 +1,8 @@ -# Use both default console and ttyS0 +# Load any and all video drivers. +# Required under UEFI to boot Linux with a working console. +insmod all_video + +# Use both default text console and ttyS0 serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1 terminal_input console serial terminal_output console serial @@ -6,4 +10,4 @@ terminal_output console serial # Re-use the existing syslinux configuration echo "Loading SYSLINUX configuration..." insmod syslinuxcfg -syslinux_configfile -s /syslinux/syslinux.cfg +syslinux_source -s /syslinux/syslinux.cfg diff --git a/build_library/grub_install.sh b/build_library/grub_install.sh new file mode 100755 index 0000000000..b43853fff0 --- /dev/null +++ b/build_library/grub_install.sh @@ -0,0 +1,127 @@ +#!/bin/bash + +# Copyright (c) 2014 The CoreOS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# Replacement script for 'grub-install' which does not detect drives +# properly when partitions are mounted via individual loopback devices. + +SCRIPT_ROOT=$(readlink -f $(dirname "$0")/..) +. "${SCRIPT_ROOT}/common.sh" || exit 1 + +# We're invoked only by build_image, which runs in the chroot +assert_inside_chroot + +# Flags. +DEFINE_string target "" \ + "The GRUB target to install such as i386-pc or x86_64-efi" +DEFINE_string esp_dir "" \ + "Path to EFI System partition mount point." +DEFINE_string disk_image "" \ + "The disk image containing the EFI System partition." + +# Parse flags +FLAGS "$@" || exit 1 +eval set -- "${FLAGS_ARGV}" +switch_to_strict_mode + +# Our GRUB lives under coreos/grub so new pygrub versions cannot find grub.cfg +GRUB_DIR="coreos/grub/${FLAGS_target}" + +# Assumes the ESP is the first partition, GRUB cannot search for it by type. +GRUB_PREFIX="(,gpt1)/coreos/grub" + +# Modules required to find and read everything else from ESP +CORE_MODULES=( fat part_gpt gzio ) + +# Name of the core image, depends on target +CORE_NAME= + +case "${FLAGS_target}" in + i386-pc) + CORE_MODULES+=( biosdisk ) + CORE_NAME="core.img" + ;; + x86_64-efi) + CORE_NAME="core.efi" + ;; + *) + die_notrace "Unknown GRUB target ${FLAGS_target}" + ;; +esac + +# In order for grub-setup-bios to properly detect the layout of the disk +# image it expects a normal partitioned block device. For most of the build +# disk_util maps individual loop devices to each partition in the image so +# the kernel can automatically detach the loop devices on unmount. When +# using a single loop device with partitions there is no such cleanup. +# That's the story of why this script has all this goo for loop and mount. +STAGE_DIR= +ESP_DIR= +LOOP_DEV= + +cleanup() { + if [[ -d "${STAGE_DIR}" ]]; then + rm -rf "${STAGE_DIR}" + fi + if [[ -d "${ESP_DIR}" ]]; then + if mountpoint -q "${ESP_DIR}"; then + sudo umount "${ESP_DIR}" + fi + rm -rf "${ESP_DIR}" + fi + if [[ -b "${LOOP_DEV}" ]]; then + sudo losetup --detach "${LOOP_DEV}" + fi +} +trap cleanup EXIT + +STAGE_DIR=$(mktemp --directory) +mkdir -p "${STAGE_DIR}/${GRUB_DIR}" + +info "Compressing modules in ${GRUB_DIR}" +for file in "/usr/lib/grub/${FLAGS_target}"/*{.lst,.mod}; do + out="${STAGE_DIR}/${GRUB_DIR}/${file##*/}" + gzip --best --stdout "${file}" > "${out}" +done + +info "Generating ${GRUB_DIR}/${CORE_NAME}" +grub-mkimage \ + --compression=auto \ + --format "${FLAGS_target}" \ + --prefix "${GRUB_PREFIX}" \ + --output "${STAGE_DIR}/${GRUB_DIR}/${CORE_NAME}" \ + "${CORE_MODULES[@]}" + +info "Installing GRUB ${FLAGS_target} to ${FLAGS_disk_image##*/}" +LOOP_DEV=$(sudo losetup --find --show --partscan "${FLAGS_disk_image}") +ESP_DIR=$(mktemp --directory) +sudo mount -t vfat "${LOOP_DEV}p1" "${ESP_DIR}" +sudo cp -r "${STAGE_DIR}/." "${ESP_DIR}/." + +# This script will get called a few times, no need to re-copy grub.cfg +if [[ ! -f "${ESP_DIR}/coreos/grub/grub.cfg" ]]; then + info "Installing grub.cfg" + sudo cp "${BUILD_LIBRARY_DIR}/grub.cfg" "${ESP_DIR}/coreos/grub/grub.cfg" +fi + +# Now target specific steps to make the system bootable +case "${FLAGS_target}" in + i386-pc) + info "Installing MBR and the BIOS Boot partition." + sudo cp "/usr/lib/grub/i386-pc/boot.img" "${ESP_DIR}/${GRUB_DIR}" + sudo grub-bios-setup --device-map=/dev/null \ + --directory="${ESP_DIR}/${GRUB_DIR}" "${LOOP_DEV}" + ;; + x86_64-efi) + info "Installing default x86_64 UEFI bootloader." + sudo mkdir -p "${ESP_DIR}/EFI/boot" + sudo cp "${STAGE_DIR}/${GRUB_DIR}/${CORE_NAME}" \ + "${ESP_DIR}/EFI/boot/bootx64.efi" + ;; +esac + +cleanup +trap - EXIT +command_completed From 5bfa0c8d2013d075b72907d0a9552877b66c542c Mon Sep 17 00:00:00 2001 From: Michael Marineau Date: Fri, 5 Sep 2014 19:24:41 -0700 Subject: [PATCH 2/3] build: switch from SYSLINUX to GRUB2 The new grub install script must be called after the image is unmounted and the old bootloaders script doesn't need to touch grub at all. For now we will continue to use the existing syslinux configs but interpreted by grub. Beyond the grub menu flashing by during boot everything should still be functionally equivalent. --- build_library/build_image_util.sh | 12 ++++- build_library/configure_bootloaders.sh | 67 +++++--------------------- build_library/disk_layout.json | 2 +- 3 files changed, 23 insertions(+), 58 deletions(-) diff --git a/build_library/build_image_util.sh b/build_library/build_image_util.sh index b909e1204e..742116e4ac 100755 --- a/build_library/build_image_util.sh +++ b/build_library/build_image_util.sh @@ -204,6 +204,7 @@ finish_image() { local disk_layout="$2" local root_fs_dir="$3" local image_contents="$4" + local install_grub=0 local disk_img="${BUILD_DIR}/${image_name}" @@ -220,10 +221,10 @@ finish_image() { # Only configure bootloaders if there is a boot partition if mountpoint -q "${root_fs_dir}"/boot; then + install_grub=1 ${BUILD_LIBRARY_DIR}/configure_bootloaders.sh \ --arch=${ARCH} \ --disk_layout="${disk_layout}" \ - --disk_image="${disk_img}" \ --boot_dir="${root_fs_dir}"/usr/boot \ --esp_dir="${root_fs_dir}"/boot \ --boot_args="${FLAGS_boot_args}" @@ -249,4 +250,13 @@ finish_image() { rm -rf "${BUILD_DIR}"/configroot cleanup_mounts "${root_fs_dir}" trap - EXIT + + # This script must mount the ESP partition differently, so run it after unmount + if [[ "${install_grub}" -eq 1 ]]; then + local target + for target in i386-pc x86_64-efi; do + ${BUILD_LIBRARY_DIR}/grub_install.sh \ + --target="${target}" --disk_image="${disk_img}" + done + fi } diff --git a/build_library/configure_bootloaders.sh b/build_library/configure_bootloaders.sh index 7458838646..09470c6f53 100755 --- a/build_library/configure_bootloaders.sh +++ b/build_library/configure_bootloaders.sh @@ -24,7 +24,6 @@ DEFINE_string boot_args "" \ "Additional boot arguments to pass to the commandline (Default: '')" DEFINE_string disk_layout "base" \ "The disk layout type to use for this image." -DEFINE_string disk_image "" "The disk image." # Parse flags FLAGS "$@" || exit 1 @@ -52,6 +51,7 @@ SYSLINUX_DIR="${FLAGS_boot_dir}/syslinux" # Build configuration files for pygrub/pvgrub configure_pvgrub() { + info "Installing legacy PV-GRUB configuration" sudo mkdir -p "${GRUB_DIR}" # Add hvc0 for hypervisors @@ -68,18 +68,21 @@ title CoreOS B Root root (hd0,0) kernel /syslinux/vmlinuz.B ${grub_args} ${slot_b_args} EOF - info "Emitted ${GRUB_DIR}/menu.lst.A" sudo_clobber "${GRUB_DIR}/menu.lst.B" < Date: Fri, 5 Sep 2014 19:29:20 -0700 Subject: [PATCH 3/3] qemu: adjust the meaning of the -s (simple) flag My primary use case for this flag is to fix booting with UEFI firmware which can have problems when mixed with KVM, adding kexec into the mix doesn't help matters either. The current version of OVMF can boot from virtio drives just fine so that is now enabled and KVM is disabled. So the -s option can also mean sloooooooow but boots! --- build_library/qemu_template.sh | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/build_library/qemu_template.sh b/build_library/qemu_template.sh index a4c107e366..30de34c9be 100755 --- a/build_library/qemu_template.sh +++ b/build_library/qemu_template.sh @@ -20,7 +20,7 @@ Options: -c FILE Config drive as an iso or fat filesystem image. -a FILE SSH public keys for login access. [~/.ssh/id_{dsa,rsa}.pub] -p PORT The port on localhost to map to the VM's sshd. [2222] - -s Safe settings: single simple cpu, ide disks. + -s Safe settings: single simple cpu and no KVM. -h this ;-) This script is a wrapper around qemu for starting CoreOS virtual machines. @@ -131,11 +131,11 @@ fi # Start assembling our default command line arguments if [ "${SAFE_ARGS}" -eq 1 ]; then - disk_type="ide" + # Disable KVM, for testing things like UEFI which don't like it + set -- -machine accel=tcg "$@" else - disk_type="virtio" # Emulate the host CPU closely in both features and cores. - set -- -cpu host -smp "${VM_NCPUS}" "$@" + set -- -machine accel=kvm -cpu host -smp "${VM_NCPUS}" "$@" fi # ${CONFIG_DRIVE} or ${CONFIG_IMAGE} will be mounted in CoreOS as /media/configdrive @@ -146,11 +146,11 @@ if [ -n "${CONFIG_DRIVE}" ]; then fi if [ -n "${CONFIG_IMAGE}" ]; then - set -- -drive if=${disk_type},file="${CONFIG_IMAGE}" "$@" + set -- -drive if=virtio,file="${CONFIG_IMAGE}" "$@" fi if [ -n "${VM_IMAGE}" ]; then - set -- -drive if=${disk_type},file="${SCRIPT_DIR}/${VM_IMAGE}" "$@" + set -- -drive if=virtio,file="${SCRIPT_DIR}/${VM_IMAGE}" "$@" fi if [ -n "${VM_KERNEL}" ]; then @@ -173,7 +173,6 @@ fi qemu-system-x86_64 \ -name "$VM_NAME" \ -m ${VM_MEMORY} \ - -machine accel=kvm:tcg \ -net nic,vlan=0,model=virtio \ -net user,vlan=0,hostfwd=tcp::"${SSH_PORT}"-:22,hostname="${VM_NAME}" \ "$@"