Merge pull request #359 from marineam/grub

grub: the one bootloader to rule them all
This commit is contained in:
Michael Marineau 2014-12-08 11:46:27 -08:00
commit 3a94b02f1d
8 changed files with 198 additions and 169 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

51
build_library/qemu_xen.sh Executable file
View File

@ -0,0 +1,51 @@
#!/bin/sh
SCRIPT_DIR="`dirname "$0"`"
VM_NAME=
VM_IMAGE=
VM_MEMORY=
VM_NCPUS="`grep -c ^processor /proc/cpuinfo`"
SSH_PORT=2222
USAGE="Usage: $0 [-p PORT] [--] [qemu options...]
Options:
-p PORT The port on localhost to map to the VM's sshd. [2222]
-h this ;-)
QEMU wrapper script for a VM that is compatible with Xen:
- No x2apic, everything APIC related breaks when it is on.
- No virtio, simply does not work whatsoever under Xen.
Any arguments after -p will be passed through to qemu, -- may be
used as an explicit separator. See the qemu(1) man page for more details.
"
while [ $# -ge 1 ]; do
case "$1" in
-p|-ssh-port)
SSH_PORT="$2"
shift 2 ;;
-v|-verbose)
set -x
shift ;;
-h|-help|--help)
echo "$USAGE"
exit ;;
--)
shift
break ;;
*)
break ;;
esac
done
qemu-system-x86_64 \
-machine accel=kvm \
-cpu host,-x2apic \
-smp "${VM_NCPUS}" \
-name "$VM_NAME" \
-m ${VM_MEMORY} \
-net nic,vlan=0,model=e1000 \
-net user,vlan=0,hostfwd=tcp::"${SSH_PORT}"-:22,hostname="${VM_NAME}" \
-drive if=scsi,file="${SCRIPT_DIR}/${VM_IMAGE}" \
"$@"
exit $?

View File

@ -11,8 +11,8 @@ VALID_IMG_TYPES=(
iso
openstack
qemu
qemu_no_kexec
qemu_uefi
qemu_xen
rackspace
rackspace_onmetal
rackspace_vhd
@ -44,6 +44,7 @@ VALID_OEM_PACKAGES=(
hyperv
rackspace
rackspace-onmetal
xendom0
vagrant
vagrant-key
vmware
@ -69,9 +70,6 @@ VM_GENERATED_FILES=()
# If set to 0 then a partition skeleton won't be laid out on VM_TMP_IMG
IMG_DEFAULT_PARTITIONED_IMG=1
# If set to 0 boot_kernel is skipped
IMG_DEFAULT_BOOT_KERNEL=1
# If set install the given package name to the OEM partition
IMG_DEFAULT_OEM_PACKAGE=
@ -108,18 +106,17 @@ IMG_qemu_DISK_FORMAT=qcow2
IMG_qemu_DISK_LAYOUT=vm
IMG_qemu_CONF_FORMAT=qemu
IMG_qemu_no_kexec_BOOT_KERNEL=0
IMG_qemu_no_kexec_DISK_FORMAT=qcow2
IMG_qemu_no_kexec_DISK_LAYOUT=vm
IMG_qemu_no_kexec_CONF_FORMAT=qemu
IMG_qemu_uefi_BOOT_KERNEL=0
IMG_qemu_uefi_DISK_FORMAT=qcow2
IMG_qemu_uefi_DISK_LAYOUT=vm
IMG_qemu_uefi_CONF_FORMAT=qemu_uefi
IMG_qemu_xen_DISK_FORMAT=qcow2
IMG_qemu_xen_DISK_LAYOUT=vm
IMG_qemu_xen_CONF_FORMAT=qemu_xen
IMG_qemu_xen_OEM_PACKAGE=oem-xendom0
IMG_qemu_xen_MEM=2048
## xen
IMG_xen_BOOT_KERNEL=0
IMG_xen_CONF_FORMAT=xl
## virtualbox
@ -156,7 +153,6 @@ IMG_vmware_insecure_CONF_FORMAT=vmware_zip
IMG_vmware_insecure_OEM_PACKAGE=oem-vagrant-key
## ami
IMG_ami_BOOT_KERNEL=0
IMG_ami_OEM_PACKAGE=oem-ec2-compat
IMG_ami_OEM_USE=ec2
@ -186,12 +182,9 @@ IMG_iso_CONF_FORMAT=iso
IMG_gce_DISK_LAYOUT=vm
IMG_gce_CONF_FORMAT=gce
IMG_gce_OEM_PACKAGE=oem-gce
IMG_gce_FS_HOOK=gce
## rackspace
IMG_rackspace_BOOT_KERNEL=0
IMG_rackspace_OEM_PACKAGE=oem-rackspace
IMG_rackspace_vhd_BOOT_KERNEL=0
IMG_rackspace_vhd_DISK_FORMAT=vhd
IMG_rackspace_vhd_OEM_PACKAGE=oem-rackspace
@ -199,12 +192,9 @@ IMG_rackspace_vhd_OEM_PACKAGE=oem-rackspace
IMG_rackspace_onmetal_DISK_FORMAT=qcow2
IMG_rackspace_onmetal_DISK_LAYOUT=onmetal
IMG_rackspace_onmetal_OEM_PACKAGE=oem-rackspace-onmetal
IMG_rackspace_onmetal_FS_HOOK=onmetal
## cloudstack
IMG_cloudstack_BOOT_KERNEL=0
IMG_cloudstack_OEM_PACKAGE=oem-cloudstack
IMG_cloudstack_vhd_BOOT_KERNEL=0
IMG_cloudstack_vhd_DISK_FORMAT=vhd
IMG_cloudstack_vhd_OEM_PACKAGE=oem-cloudstack
@ -216,13 +206,11 @@ IMG_exoscale_DISK_FORMAT=qcow2
IMG_exoscale_OEM_PACKAGE=oem-exoscale
## azure
IMG_azure_BOOT_KERNEL=0
IMG_azure_DISK_FORMAT=vhd
IMG_azure_DISK_LAYOUT=azure
IMG_azure_OEM_PACKAGE=oem-azure
## hyper-v
IMG_hyperv_BOOT_KERNEL=0
IMG_hyperv_DISK_FORMAT=vhd
IMG_hyperv_OEM_PACKAGE=oem-hyperv
@ -351,11 +339,6 @@ setup_disk_image() {
"${BUILD_LIBRARY_DIR}/disk_util" --disk_layout="${disk_layout}" \
mount "${VM_TMP_IMG}" "${VM_TMP_ROOT}"
local SYSLINUX_DIR="${VM_TMP_ROOT}/boot/syslinux"
if [[ $(_get_vm_opt BOOT_KERNEL) -eq 0 ]]; then
sudo mv "${SYSLINUX_DIR}/default.cfg.A" "${SYSLINUX_DIR}/default.cfg"
fi
# The only filesystems after this point that may be modified are OEM
# and on rare cases ESP.
# Note: it would be more logical for disk_util to mount things read-only
@ -406,31 +389,6 @@ _run_box_fs_hook() {
cp -R "${VM_TMP_ROOT}/usr/share/oem/box/." "${VM_TMP_DIR}/box"
}
_run_onmetal_fs_hook() {
# HACKITY HACK until OEMs can customize bootloader configs
local arg='8250.nr_uarts=5 console=ttyS4,115200n8 modprobe.blacklist=mei_me'
local timeout=150 # 15 seconds
local totaltimeout=3000 # 5 minutes
sudo sed -i "${VM_TMP_ROOT}/boot/syslinux/boot_kernel.cfg" \
-e 's/console=[^ ]*//g' -e "s/\\(append.*$\\)/\\1 ${arg}/"
sudo sed -i "${VM_TMP_ROOT}/boot/syslinux/syslinux.cfg" \
-e "s/^TIMEOUT [0-9]*/TIMEOUT ${timeout}/g" \
-e "s/^TOTALTIMEOUT [0-9]*/TOTALTIMEOUT ${totaltimeout}/g"
}
_run_gce_fs_hook() {
# HACKITY HACK until OEMs can customize bootloader configs
local arg='console=ttyS0,115200n8'
sudo sed -i "${VM_TMP_ROOT}/boot/syslinux/boot_kernel.cfg" \
-e 's/console=[^ ]*//g' -e "s/\\(append.*$\\)/\\1 ${arg}/"
}
_run_azure_fs_hook() {
# HACKITY HACK until OEMs can customize bootloader configs
local arg='console=ttyS0,115200n8'
sudo sed -i "${VM_TMP_ROOT}/boot/syslinux/boot_kernel.cfg" \
-e 's/console=[^ ]*//g' -e "s/\\(append.*$\\)/\\1 ${arg}/"
}
# Write the vm disk image to the target directory in the proper format
write_vm_disk() {
if [[ $(_get_vm_opt PARTITIONED_IMG) -eq 1 ]]; then
@ -606,6 +564,21 @@ _write_qemu_uefi_conf() {
VM_GENERATED_FILES+=( "$(_dst_dir)/${ovmf_ro}" "$(_dst_dir)/${ovmf_rw}" )
}
_write_qemu_xen_conf() {
local script="$(_dst_dir)/$(_dst_name ".sh")"
local dst_name=$(basename "$VM_DST_IMG")
local vm_mem="$(_get_vm_opt MEM)"
sed -e "s%^VM_NAME=.*%VM_NAME='${VM_NAME}'%" \
-e "s%^VM_IMAGE=.*%VM_IMAGE='${dst_name}'%" \
-e "s%^VM_MEMORY=.*%VM_MEMORY='${vm_mem}'%" \
"${BUILD_LIBRARY_DIR}/qemu_xen.sh" > "${script}"
checkbashisms --posix "${script}" || die
chmod +x "${script}"
VM_GENERATED_FILES+=( "${script}" )
}
_write_pxe_conf() {
local script="$(_dst_dir)/$(_dst_name ".sh")"
local vmlinuz_name="$(_dst_name ".vmlinuz")"