blob: 6109dc792ea63ffc9534ab10c305dfb6da5dd089 [file] [log] [blame]
# Copyright 2016 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.
# Notes:
# (1) Usage: This script gets used as a "library" from other parts of the
# build scripts. Specifically, the board_specific_setup.sh script sources
# this script as part of the board_make_image_bootable() function. The
# "cros_make_image_bootable" script calls the board_make_image_bootable()
# function.
# (2) In order to install GRUB2 on GPT-partitioned disks, there must be a
# designated "BIOS boot partition".
#
# On MBR-only disks, the disk sectors immediately following the MBR are
# used for storing "stage 2" of GRUB2. On GPT disks, the sectors
# immediately after MBR are used up to hold the actual partition table.
# In such case, a well-known GUID can be used to mark a partition as the
# BIOS boot partition, which the grub-install command uses to store the
# second stage of the bootloader. For more details on BIOS boot partition,
# see https://en.wikipedia.org/wiki/BIOS_boot_partition.
#
# The disk layout of Lakitu is inherited from the ChromeOS disk layout,
# which has a partition 11 for firmware and a partition 12 for legacy
# bootloader configurations. Since Lakitu doesn't use a custom firmware,
# we can reuse partition 11 as the BIOS boot partition without having to
# change the disk layout. We continue to use partition 12 for storing
# bootloader configurations.
IMAGE=
GRUB_DIR="boot/grub"
GRUB_TARGET="i386-pc"
GRUB_MODULES="part_gpt gptpriority test fat ext2 normal boot chain configfile
linux search search_fs_uuid search_label terminal memdisk echo biosdisk serial"
# Use partition #11 (originally meant for firmware RW) as BIOS Boot Partition.
BIOS_PART_NUM="11"
# See https://www.gnu.org/software/grub/manual/html_node/BIOS-installation.html
# for the UUID.
BIOS_PART_UUID="21686148-6449-6E6F-744E-656564454649"
ESP_DIR=""
LOOP_DEV=
cleanup() {
if [[ -d "${ESP_DIR}" ]]; then
if mountpoint -q "${ESP_DIR}"; then
sudo umount "${ESP_DIR}"
fi
# ESP_DIR should be empty after the umount, so rmdir should work.
rmdir "${ESP_DIR}"
fi
if [[ -b "${LOOP_DEV}" ]]; then
sudo losetup --detach "${LOOP_DEV}"
fi
}
# Installs bootloaderes on the given disk image.
#
# Args:
# $1: absolute path to the disk image.
bootloader_install() {
trap cleanup EXIT
IMAGE="$1"
info "Installing bootloaders on ${IMAGE}"
LOOP_DEV="$(sudo losetup --find --show --partscan "${IMAGE}")"
if ! sudo udevadm settle --timeout=10; then
warn "Error running 'udevadm settle'"
fi
local -r esp_node=${LOOP_DEV}p12
# Wait till udevd finishes the work.
for i in {1..10}; do
info "Probing ${esp_node}"
if [[ -b "${esp_node}" ]]; then
break
fi
sleep 1
sudo blockdev --rereadpt "${LOOP_DEV}"
done
if [[ ! -b "${esp_node}" ]]; then
error "EFI partition ${esp_node} not available"
return 1
fi
ESP_DIR="$(mktemp --directory)"
if ! sudo mount -t vfat "${esp_node}" "${ESP_DIR}"; then
error "Could not mount EFI partition"
return 1
fi
info "Installing GRUB2 ${GRUB_TARGET} on ${IMAGE}"
if ! sudo mkdir -p "${ESP_DIR}/${GRUB_DIR}"; then
error "Could not create dir for grub config"
return 1
fi
if [[ ! -f ${ESP_DIR}/efi/boot/grub.cfg ]]; then
error "Could not find grub.cfg from EFI installation."
return 1
fi
if ! sudo sed -i -e 's|/sbin/init|/usr/lib/systemd/systemd|' \
-e '/^set timeout=/s|=.*|=0|' \
"${ESP_DIR}/efi/boot/grub.cfg"; then
error "Failed to update grub configuration file."
return 1
fi
# Create a minimal grub.cfg that sets up environment variables and loads the
# configuration from EFI installation.
cat <<EOF | sudo dd of="${ESP_DIR}/${GRUB_DIR}/grub.cfg" 2>/dev/null
# The configuration relies on |grubdisk| environment variable to find the GPT
# disk. When booting with EFI, the efidisk module exports the env var. In case
# of BIOS, we need to hard-code it and export.
set grubdisk=hd0
export grubdisk
configfile /efi/boot/grub.cfg
EOF
# Mark the firmware partition as the BIOS boot partition.
# Note that this must be done prior to running grub-install, or else the
# grub-install command will fail.
info "Setting BIOS boot partition ..."
if ! sudo cgpt add -i "${BIOS_PART_NUM}" \
-t "${BIOS_PART_UUID}" "${LOOP_DEV}"; then
error "Error running cgpt"
return 1
fi
info "Running grub-install ..."
if ! sudo grub-install --target="${GRUB_TARGET}" \
--boot-directory="${ESP_DIR}/boot" \
--modules="${GRUB_MODULES}" \
"${LOOP_DEV}"; then
error "Error running grub-install"
return 1
fi
info "Successfully installed GRUB2 ${GRUB_TARGET} on ${IMAGE}"
info "Setting up UEFI booting on ${IMAGE}"
# Directory for GSetup data.
sudo mkdir -p "${ESP_DIR}/EFI/Google/GSetup"
# Boot sequence.
echo "\\efi\\boot\\shimx64.efi" | \
sudo tee "${ESP_DIR}/EFI/Google/GSetup/Boot" > /dev/null
info "Successfully set up UEFI booting on ${IMAGE}"
cleanup
trap - EXIT
info "Successfully installed bootloaders on ${IMAGE}"
}