grub: the one bootloader to rule them all

This uses our new GRUB2 features to handle GPT priority partition
selection, terminal selection, OEM tweaks, etc. The old SYSLINUX and
PV-GRUB configs are now unused except for maintaining compatibility
with older installs. Of the old configs only the ones that
coreos-postinst copies are needed. The new setup supports using GRUB2
under Xen, giving us automatic fallback support on all of our platforms
for the very first time!

Since grub.cfg is copied into place instead of generated, build_image's
--boot_args option is no longer supported. It could be re-added later
with some sed goo but for now it is easy enough to just edit grub.cfg.
This commit is contained in:
Michael Marineau 2014-11-15 16:01:43 -08:00
parent 52872481f5
commit d443daa168
6 changed files with 124 additions and 119 deletions

View File

@ -22,8 +22,6 @@ DEFAULT_GROUP=developer
# Developer-visible flags.
DEFINE_string board "${DEFAULT_BOARD}" \
"The board to build an image for."
DEFINE_string boot_args "" \
"Additional boot arguments to pass to the commandline"
DEFINE_boolean enable_rootfs_verification ${FLAGS_TRUE} \
"Default all bootloaders to use kernel-based root fs integrity checking."
DEFINE_string output_root "${DEFAULT_BUILD_ROOT}/images" \

View File

@ -242,11 +242,7 @@ finish_image() {
if mountpoint -q "${root_fs_dir}"/boot; then
install_grub=1
${BUILD_LIBRARY_DIR}/configure_bootloaders.sh \
--arch=${ARCH} \
--disk_layout="${disk_layout}" \
--boot_dir="${root_fs_dir}"/usr/boot \
--esp_dir="${root_fs_dir}"/boot \
--boot_args="${FLAGS_boot_args}"
--boot_dir="${root_fs_dir}"/usr/boot
fi
if [[ -n "${FLAGS_developer_data}" ]]; then
@ -273,7 +269,7 @@ finish_image() {
# 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
for target in i386-pc x86_64-efi x86_64-xen; do
${BUILD_LIBRARY_DIR}/grub_install.sh \
--target="${target}" --disk_image="${disk_img}"
done

View File

@ -4,8 +4,8 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# Helper script to generate GRUB bootloader configuration files for
# x86 platforms.
# Helper script to generate bootloader configuration files for systems
# that predate our new GRUB2 based gptprio bootloader.
SCRIPT_ROOT=$(readlink -f $(dirname "$0")/..)
. "${SCRIPT_ROOT}/common.sh" || exit 1
@ -14,16 +14,8 @@ SCRIPT_ROOT=$(readlink -f $(dirname "$0")/..)
assert_inside_chroot
# Flags.
DEFINE_string arch "x86" \
"The boot architecture: arm or x86. (Default: x86)"
DEFINE_string boot_dir "/tmp/boot" \
"Path to boot directory in root filesystem (Default: /tmp/boot)"
DEFINE_string esp_dir "" \
"Path to ESP partition mount point (Default: none)"
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."
# Parse flags
FLAGS "$@" || exit 1
@ -34,17 +26,10 @@ switch_to_strict_mode
common_args="console=tty0 ro noswap cros_legacy"
common_args="${common_args} ${FLAGS_boot_args}"
# Get partition UUIDs from the json config
get_uuid() {
"${BUILD_LIBRARY_DIR}/disk_util" --disk_layout="${FLAGS_disk_layout}" \
readuuid "$1"
}
# Filesystem command line args.
root_args="root=LABEL=ROOT rootflags=subvol=root"
gptprio_args="${root_args} usr=gptprio:"
slot_a_args="${root_args} usr=PARTUUID=$(get_uuid USR-A)"
slot_b_args="${root_args} usr=PARTUUID=$(get_uuid USR-B)"
slot_a_args="${root_args} usr=PARTLABEL=USR-A"
slot_b_args="${root_args} usr=PARTLABEL=USR-B"
GRUB_DIR="${FLAGS_boot_dir}/grub"
SYSLINUX_DIR="${FLAGS_boot_dir}/syslinux"
@ -73,11 +58,6 @@ EOF
default 1
$(< "${GRUB_DIR}/menu.lst.A")
EOF
sudo cp "${GRUB_DIR}/menu.lst.A" "${GRUB_DIR}/menu.lst"
# menu.lst needs to go under boot/grub so pvgrub can find it reliably
sudo mkdir -p "${FLAGS_esp_dir}/boot/grub"
sudo cp "${GRUB_DIR}/menu.lst" "${FLAGS_esp_dir}/boot/grub"
}
# Build configuration files for syslinux
@ -87,45 +67,11 @@ configure_syslinux() {
# Add ttyS0 as a secondary console, useful for qemu -nographic
# This leaves /dev/console mapped to tty0 (vga) which is reasonable default.
if [[ ${common_args} == *console=ttyS* ]] ; then
syslinux_args="${common_args}"
else
syslinux_args="console=ttyS0,115200n8 ${common_args}"
fi
syslinux_args="console=ttyS0,115200n8 ${common_args}"
sudo_clobber "${SYSLINUX_DIR}/syslinux.cfg" <<EOF
PROMPT 1
# display boot: prompt for a half second
TIMEOUT 5
# never sit at the prompt longer than a minute
TOTALTIMEOUT 600
# controls which kernel is the default
include /syslinux/default.cfg
include /syslinux/boot_kernel.cfg
# coreos.A
include /syslinux/root.A.cfg
# coreos.B
include /syslinux/root.B.cfg
EOF
sudo_clobber "${SYSLINUX_DIR}/default.cfg" <<<"DEFAULT boot_kernel"
sudo_clobber "${SYSLINUX_DIR}/default.cfg.A" <<<"DEFAULT coreos.A"
sudo_clobber "${SYSLINUX_DIR}/default.cfg.B" <<<"DEFAULT coreos.B"
# Different files are used so that the updater can only touch the file it
# needs to for a given change. This will minimize any potential accidental
# updates issues, hopefully.
sudo_clobber "${SYSLINUX_DIR}/boot_kernel.cfg" <<EOF
label boot_kernel
menu label boot_kernel
kernel vmlinuz-boot_kernel
append ${syslinux_args} ${gptprio_args}
EOF
sudo_clobber "${SYSLINUX_DIR}/root.A.cfg" <<EOF
label coreos.A
menu label coreos.A
@ -139,21 +85,7 @@ label coreos.B
kernel vmlinuz.B
append ${syslinux_args} ${slot_b_args}
EOF
sudo cp -r "${SYSLINUX_DIR}/." "${FLAGS_esp_dir}/syslinux"
# Stage all kernels with the only one we built.
for kernel in syslinux/{vmlinuz-boot_kernel,vmlinuz.A,vmlinuz.B}
do
sudo cp "${FLAGS_boot_dir}/vmlinuz" "${FLAGS_esp_dir}/${kernel}"
done
}
[[ -d "${FLAGS_esp_dir}" ]] || die_notrace "--esp_dir is required"
if [[ "${FLAGS_arch}" = "x86" || "${FLAGS_arch}" = "amd64" ]]; then
configure_pvgrub
configure_syslinux
else
error "No bootloader configuration for ${FLAGS_arch}"
fi
configure_pvgrub
configure_syslinux

View File

@ -1,13 +1,75 @@
# Main GRUB config
# 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
# Default menuentry id and boot timeout
set default="coreos"
set timeout=1
# Re-use the existing syslinux configuration
echo "Loading SYSLINUX configuration..."
insmod syslinuxcfg
syslinux_source -s /syslinux/syslinux.cfg
# Default kernel args for root filesystem and console.
set linux_root="root=LABEL=ROOT"
set linux_console=""
# Anything else the OEM adds should use this variable.
set linux_append=""
# Search for the OEM partition, load additional configuration if found.
search --no-floppy --set oem --label OEM --hint "$root"
if [ -n "$oem" -a -f "($oem)/grub.cfg" ]; then
source "($oem)/grub.cfg"
fi
# If no specific console has been set by the OEM then select based on
# platform, most systems use vga text as primary and ttyS0 as secondary.
if [ -z "$linux_console" ]; then
if [ "$grub_platform" = pc ]; then
set linux_console="console=ttyS0,115200n8 console=tty0"
serial com0 --speed=115200 --word=8 --parity=no
terminal_input console serial_com0
terminal_output console serial_com0
elif [ "$grub_platform" = efi ]; then
set linux_console="console=ttyS0,115200n8 console=tty0"
elif [ "$grub_platform" = xen ]; then
set linux_console="console=hvc0"
fi
fi
# Load a kernel and boot! $root must point at USR-A or USR-B
function load_coreos {
# UEFI uses linuxefi/initrdefi instead of linux/initrd
if [ "$grub_platform" = efi ]; then
set suf="efi"
else
set suf=""
fi
echo "Loading ($root)/boot/vmlinuz"
linux$suf /boot/vmlinuz $linux_console $linux_root "$@" $linux_append
if [ -f /boot/initrd ]; then
echo "Loading ($root)/boot/initrd"
initrd$suf /boot/initrd
fi
echo "Booting CoreOS!"
}
# TODO: systemd 217 added mount.usr=, once we update we should switch
menuentry "CoreOS default" --id=coreos {
gptprio.next -d root -u usr_uuid
load_coreos usr=PARTUUID=$usr_uuid
}
menuentry "CoreOS USR-A" --id=coreos-a {
search --no-floppy --set root --part-label USR-A --hint "$root"
load_coreos usr=PARTLABEL=USR-A
}
menuentry "CoreOS USR-B" --id=coreos-b {
search --no-floppy --set root --part-label USR-B --hint "$root"
load_coreos usr=PARTLABEL=USR-B
}

View File

@ -29,11 +29,8 @@ 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 )
CORE_MODULES=( fat part_gpt search_fs_uuid gzio )
# Name of the core image, depends on target
CORE_NAME=
@ -46,6 +43,9 @@ case "${FLAGS_target}" in
x86_64-efi)
CORE_NAME="core.efi"
;;
x86_64-xen)
CORE_NAME="core.elf"
;;
*)
die_notrace "Unknown GRUB target ${FLAGS_target}"
;;
@ -57,14 +57,10 @@ esac
# 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}"
@ -77,24 +73,7 @@ cleanup() {
}
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##*/}"
info "Installing GRUB ${FLAGS_target} in ${FLAGS_disk_image##*/}"
LOOP_DEV=$(sudo losetup --find --show --partscan "${FLAGS_disk_image}")
ESP_DIR=$(mktemp --directory)
@ -117,7 +96,33 @@ if [[ ! -b "${LOOP_DEV}p1" ]]; then
fi
sudo mount -t vfat "${LOOP_DEV}p1" "${ESP_DIR}"
sudo cp -r "${STAGE_DIR}/." "${ESP_DIR}/."
sudo mkdir -p "${ESP_DIR}/${GRUB_DIR}"
info "Compressing modules in ${GRUB_DIR}"
for file in "/usr/lib/grub/${FLAGS_target}"/*{.lst,.mod}; do
out="${ESP_DIR}/${GRUB_DIR}/${file##*/}"
gzip --best --stdout "${file}" | sudo_clobber "${out}"
done
info "Generating ${GRUB_DIR}/load.cfg"
# Include a small initial config in the core image to search for the ESP
# by filesystem ID in case the platform doesn't provide the boot disk.
# The existing $root value is given as a hint so it is searched first.
ESP_FSID=$(sudo grub-probe -t fs_uuid -d "${LOOP_DEV}p1")
sudo_clobber "${ESP_DIR}/${GRUB_DIR}/load.cfg" <<EOF
search.fs_uuid ${ESP_FSID} root \$root
set prefix=(\$root)/coreos/grub
set
EOF
info "Generating ${GRUB_DIR}/${CORE_NAME}"
sudo grub-mkimage \
--compression=auto \
--format "${FLAGS_target}" \
--prefix "(,gpt1)/coreos/grub" \
--config "${ESP_DIR}/${GRUB_DIR}/load.cfg" \
--output "${ESP_DIR}/${GRUB_DIR}/${CORE_NAME}" \
"${CORE_MODULES[@]}"
# This script will get called a few times, no need to re-copy grub.cfg
if [[ ! -f "${ESP_DIR}/coreos/grub/grub.cfg" ]]; then
@ -136,9 +141,17 @@ case "${FLAGS_target}" in
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}" \
sudo cp "${ESP_DIR}/${GRUB_DIR}/${CORE_NAME}" \
"${ESP_DIR}/EFI/boot/bootx64.efi"
;;
x86_64-xen)
info "Installing default x86_64 Xen bootloader."
sudo mkdir -p "${ESP_DIR}/xen" "${ESP_DIR}/boot/grub"
sudo cp "${ESP_DIR}/${GRUB_DIR}/${CORE_NAME}" \
"${ESP_DIR}/xen/pvboot-x86_64.elf"
sudo cp "${BUILD_LIBRARY_DIR}/menu.lst" \
"${ESP_DIR}/boot/grub/menu.lst"
;;
esac
cleanup

4
build_library/menu.lst Normal file
View File

@ -0,0 +1,4 @@
timeout 0
title CoreOS GRUB2
root (hd0,0)
kernel /xen/pvboot-x86_64.elf