Skip to content

Commit 2ee9fea

Browse files
committed
CP-12082 Install and configure systemd-boot during appliance image build
CP-12084 Enable EFI firmware boot for ESX CP-12483 Handle upgrade scenarios, EFI to EFI and BIOS to BIOS PR URL: https://www.github.com/delphix/appliance-build/pull/816
1 parent 8d7c7ca commit 2ee9fea

File tree

4 files changed

+155
-65
lines changed

4 files changed

+155
-65
lines changed

live-build/config/hooks/vm-artifacts/90-raw-disk-image.binary

Lines changed: 51 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -89,17 +89,16 @@ truncate -s "${RAW_DISK_SIZE_GB}G" "$ARTIFACT_NAME.img"
8989
sgdisk --zap-all "$ARTIFACT_NAME.img"
9090

9191
#
92-
# Here we're creating the boot partition. When installing grub, this
93-
# partition will be used and automatically detected by "grub-install"
94-
# based on the partitions typecode. This partition is required since we're
95-
# partitioning using GPT; if we used MBR, an explicit boot partition
96-
# wouldn't be required. Also we leave the first 1MB unallocated since
97-
# some clouds (i.e. Azure) may require space for their own internal
98-
# purposes.
92+
# Here we're creating the boot partition. A 250MB EFI boot partition should
93+
# be sufficient to store 5 initrd and vmlinuz versions (if necessary). GRUB
94+
# files are ~11MB, initrd and vmlinuz are ~40MB, and all boot loaders EFI
95+
# directory are under 500K.
9996
#
100-
sgdisk "$ARTIFACT_NAME.img" \
101-
--set-alignment=1 --new=2:1m:+1m --typecode=2:EF02
102-
97+
# Also we leave the first 1MB unallocated since some clouds (i.e. Azure) may
98+
# require space for their own internal purposes.
99+
#
100+
sgdisk "$ARTIFACT_NAME.img" --set-alignment=1 \
101+
--new=2:1m:+250m --typecode=2:EF00 -c:2:"EFI Boot"
103102
#
104103
# Now we create the partition that we'll use for the zpool that will be
105104
# used for the root pool. We use a generic typecode for this partition
@@ -293,6 +292,9 @@ mount -t zfs "$FSNAME/ROOT/$FSNAME/vartmp" "$DIRECTORY/var/tmp"
293292
mkdir -p "/var/crash"
294293
mount -t zfs "$FSNAME/crashdump" "/var/crash"
295294

295+
# Make the vfat partition
296+
mkfs.vfat -F32 /dev/mapper/${LOOPNAME}p2
297+
296298
#
297299
# Populate the root filesystem with the contents of the "binary" directory
298300
# that (we assume) was previously generated by live-build.
@@ -343,13 +345,46 @@ for dir in /dev /proc /sys; do
343345
done
344346

345347
#
346-
# We need to use the dedicated grub dataset when running "grub-install"
347-
# and "grub-mkconfig", so we need to mount this dataset first.
348+
# Mount /boot/efi and do grub and bootctl install
348349
#
349-
chroot "$DIRECTORY" mount -t zfs "$FSNAME/grub" /mnt
350-
chroot "$DIRECTORY" grub-install --root-directory=/mnt "/dev/$LOOPNAME"
351-
chroot "$DIRECTORY" grub-mkconfig -o /mnt/boot/grub/grub.cfg
352-
chroot "$DIRECTORY" umount /mnt
350+
EFI_DIR="/mnt/boot/efi"
351+
chroot "$DIRECTORY" mkdir -p $EFI_DIR
352+
chroot "$DIRECTORY" mount /dev/mapper/${LOOPNAME}p2 $EFI_DIR
353+
354+
# Copy the latest kernel into EFI boot directory
355+
chroot "$DIRECTORY" cp /boot/initrd.img $EFI_DIR
356+
chroot "$DIRECTORY" cp /boot/vmlinuz $EFI_DIR
357+
chroot "$DIRECTORY" bootctl --esp-path=$EFI_DIR install --no-variables
358+
359+
# Use GRUB_CMDLINE_LINUX_DEFAULT boot options
360+
source $DIRECTORY/etc/default/grub.d/override.cfg
361+
cat <<-EOF >"${DIRECTORY}/${EFI_DIR}/loader/entries/delphix.conf"
362+
title Delphix Engine
363+
linux /vmlinuz
364+
initrd /initrd.img
365+
options root=ZFS=rpool/ROOT/${FSNAME}/root ro $GRUB_CMDLINE_LINUX_DEFAULT
366+
EOF
367+
368+
# Single user mode. Need the console options from GRUB
369+
console_options=$(awk -F'"' '/console=tty/ {print $2}' $DIRECTORY/etc/default/grub.d/override.cfg)
370+
cat <<-EOF >"${DIRECTORY}/${EFI_DIR}/loader/entries/delphix-single.conf"
371+
title Delphix Engine Single User mode
372+
linux /vmlinuz
373+
initrd /initrd.img
374+
options root=ZFS=rpool/ROOT/${FSNAME}/root ro single ${console_options} nomodeset dis_ucode_ldr
375+
EOF
376+
377+
# Set loader configurations
378+
cat <<-EOF >"${DIRECTORY}/${EFI_DIR}/loader/loader.conf"
379+
timeout 10
380+
console-mode max
381+
default delphix.conf
382+
editor yes
383+
auto-entries yes
384+
auto-firmware yes
385+
EOF
386+
387+
chroot "$DIRECTORY" umount $EFI_DIR
353388

354389
for dir in /dev /proc /sys; do
355390
retry 5 10 umount -R "${DIRECTORY}${dir}"

live-build/config/hooks/vm-artifacts/template.ovf

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,8 @@
132132
</Item>
133133
<vmw:Config ovf:required="false" vmw:key="cpuHotAddEnabled" vmw:value="true"/>
134134
<vmw:Config ovf:required="false" vmw:key="cpuHotRemoveEnabled" vmw:value="false"/>
135-
<vmw:Config ovf:required="false" vmw:key="firmware" vmw:value="bios"/>
135+
<vmw:Config ovf:required="false" vmw:key="firmware" vmw:value="efi"/>
136+
<vmw:Config ovf:required="false" vmw:key="bootOptions.efiSecureBootEnabled" vmw:value="false"/>
136137
<vmw:Config ovf:required="false" vmw:key="virtualICH7MPresent" vmw:value="false"/>
137138
<vmw:Config ovf:required="false" vmw:key="virtualSMCPresent" vmw:value="false"/>
138139
<vmw:Config ovf:required="false" vmw:key="memoryHotAddEnabled" vmw:value="true"/>

upgrade/upgrade-scripts/common.sh

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/bin/bash
22
#
3-
# Copyright 2018 Delphix
3+
# Copyright 2018, 2025 Delphix
44
#
55
# Licensed under the Apache License, Version 2.0 (the "License");
66
# you may not use this file except in compliance with the License.
@@ -19,6 +19,8 @@
1919
UPDATE_DIR="/var/dlpx-update"
2020
LOG_DIRECTORY="/var/tmp/delphix-upgrade"
2121
HOTFIX_PATH="/etc/hotfix"
22+
EFI_DIR="/boot/efi"
23+
EFI_PARTITION_UUID="c12a7328-f81f-11d2-ba4b-00a0c93ec93b"
2224

2325
#
2426
# The virtualization service uses a different umask than the default. Thus to
@@ -376,6 +378,10 @@ function verify_upgrade_not_in_progress() {
376378
[[ -z "$UPGRADE_TYPE" ]] || die "upgrade currently in-progress"
377379
}
378380

381+
function is_bootmode_uefi() {
382+
[ -d /sys/firmware/efi/efivars ] && return 0 || return 1
383+
}
384+
379385
function mask_service() {
380386
local svc="$1"
381387
local container="$2"

upgrade/upgrade-scripts/rootfs-container

Lines changed: 95 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/bin/bash
22
#
3-
# Copyright 2019 Delphix
3+
# Copyright 2019, 2025 Delphix
44
#
55
# Licensed under the Apache License, Version 2.0 (the "License");
66
# you may not use this file except in compliance with the License.
@@ -83,15 +83,19 @@ function get_bootloader_devices() {
8383
# devices. We determine this by listing the devices used by the
8484
# rpool. Additionally, we have to filter out devices that could
8585
# be attached to the rpool, but would never be used for the
86-
# bootloader. Finally, we need to strip off any parition
86+
# bootloader. Finally, we need to strip off any partition
8787
# information, since we want to install the bootloader directly
8888
# to the device, rather than to a partition of the device.
8989
#
9090
zpool list -vH rpool |
9191
awk '! /rpool|mirror|replacing|spare/ {print $1}' |
9292
while read -r part; do
93+
# Skip if device doesn't exist or is not a block device
94+
[[ -e "/dev/$dev" ]] || continue
95+
[[ -b "/dev/$dev" ]] || continue
96+
9397
#
94-
# If the rpool is not installed a parition, we throw
98+
# If the rpool is not installed a partition, we throw
9599
# an error. We expect this to never happen, and the
96100
# calling code is likely untested in that case, so we
97101
# throw an error rather than try to handle it.
@@ -102,6 +106,44 @@ function get_bootloader_devices() {
102106
done
103107
}
104108

109+
function mount_efi() {
110+
#
111+
# Find and mount the device and partition with EFI UUID
112+
#
113+
for dev in $(get_bootloader_devices); do
114+
for part in $(lsblk -ln -o NAME,FSTYPE,PARTTYPE "/dev/$dev" | grep vfat | awk '{print $1}'); do
115+
part_path="/dev/$part"
116+
part_type=$(blkid -s PARTTYPE -o value "$part_path")
117+
if [[ "$part_type" == $EFI_PARTITION_UUID ]]; then
118+
mount $part_path $EFI_DIR
119+
return
120+
fi
121+
done
122+
done
123+
}
124+
125+
function update_systemd_boot_entries() {
126+
cp "${EFI_DIR}/loader/entries/delphix.conf" "${EFI_DIR}/loader/entries/delphix.conf.bak"
127+
cp "${EFI_DIR}/loader/entries/delphix-single.conf" "${EFI_DIR}/loader/entries/delphix-single.conf.bak"
128+
129+
# Update default and single user entries
130+
source /etc/default/grub.d/override.cfg
131+
cat <<-EOF >"${EFI_DIR}/loader/entries/delphix.conf"
132+
title Delphix Engine
133+
linux /vmlinuz
134+
initrd /initrd.img
135+
options root=ZFS=rpool/ROOT/${CONTAINER}/root ro $GRUB_CMDLINE_LINUX_DEFAULT
136+
EOF
137+
138+
# Single user entry
139+
cat <<-EOF >"${EFI_DIR}/loader/entries/delphix-single.conf"
140+
title Delphix Engine Single User mode
141+
linux /vmlinuz
142+
initrd /initrd.img
143+
options root=ZFS=rpool/ROOT/${CONTAINER}/root ro single nomodeset dis_ucode_ldr
144+
EOF
145+
}
146+
105147
function set_bootfs_not_mounted_cleanup() {
106148
umount "/var/lib/machines/$CONTAINER/mnt" ||
107149
warn "'umount' of '/var/lib/machines/$CONTAINER/mnt' failed"
@@ -147,30 +189,33 @@ function set_bootfs_not_mounted() {
147189

148190
PLATFORM=$(get-appliance-platform)
149191

150-
for dev in $(get_bootloader_devices); do
151-
[[ -e "/dev/$dev" ]] ||
152-
die "bootloader device '/dev/$dev' not found"
153-
154-
[[ -b "/dev/$dev" ]] ||
155-
die "bootloader device '/dev/$dev' not block device"
156-
157-
opts="--root-directory=/mnt"
158-
159-
if [[ "$PLATFORM" == "esx" ]] || [[ "$PLATFORM" == "oci" ]]; then
160-
opts+=" --debug-image=all"
161-
opts+=" -v"
162-
fi
192+
# If using EFI firmware, update boot entries, vmlinuz, initrd, and bootloader
193+
if is_bootmode_uefi; then
194+
mount_efi()
195+
update_systemd_boot_entries
196+
cp "/var/lib/machines/${CONTAINER}/boot/initrd.img" "$EFI_DIR"
197+
cp "/var/lib/machines/${CONTAINER}/boot/vmlinuz" "$EFI_DIR"
198+
chroot "/var/lib/machines/$CONTAINER" bootctl update
199+
umount $EFI_DIR
200+
else
201+
for dev in $(get_bootloader_devices); do
202+
opts="--root-directory=/mnt"
203+
204+
if [[ "$PLATFORM" == "esx" ]] || [[ "$PLATFORM" == "oci" ]]; then
205+
opts+=" --debug-image=all"
206+
opts+=" -v"
207+
fi
208+
209+
# shellcheck disable=SC2086
210+
chroot "/var/lib/machines/$CONTAINER" \
211+
grub-install $opts "/dev/$dev" ||
212+
die "'grub-install' for '$dev' failed in '$CONTAINER'"
213+
done
163214

164-
# shellcheck disable=SC2086
165215
chroot "/var/lib/machines/$CONTAINER" \
166-
grub-install $opts "/dev/$dev" ||
167-
die "'grub-install' for '$dev' failed in '$CONTAINER'"
168-
done
169-
170-
chroot "/var/lib/machines/$CONTAINER" \
171-
grub-mkconfig -o /mnt/boot/grub/grub.cfg ||
172-
die "'grub-mkconfig' failed in '$CONTAINER'"
173-
216+
grub-mkconfig -o /mnt/boot/grub/grub.cfg ||
217+
die "'grub-mkconfig' failed in '$CONTAINER'"
218+
fi
174219
set_bootfs_not_mounted_cleanup
175220
trap - EXIT
176221

@@ -211,28 +256,31 @@ function set_bootfs_mounted() {
211256

212257
PLATFORM=$(get-appliance-platform)
213258

214-
for dev in $(get_bootloader_devices); do
215-
[[ -e "/dev/$dev" ]] ||
216-
die "bootloader device '/dev/$dev' not found"
217-
218-
[[ -b "/dev/$dev" ]] ||
219-
die "bootloader device '/dev/$dev' not block device"
220-
221-
opts="--root-directory=/mnt"
222-
223-
if [[ "$PLATFORM" == "esx" ]] || [[ "$PLATFORM" == "oci" ]]; then
224-
opts+=" --debug-image=all"
225-
opts+=" -v"
226-
fi
227-
228-
# shellcheck disable=SC2086
229-
grub-install $opts "/dev/$dev" ||
230-
die "'grub-install' for '$dev' failed in '$CONTAINER'"
231-
done
232-
233-
grub-mkconfig -o /mnt/boot/grub/grub.cfg ||
234-
die "'grub-mkconfig' failed in '$CONTAINER'"
259+
# If using EFI firmware, update boot entries, vmlinuz, initrd, and bootloader
260+
if is_bootmode_uefi; then
261+
mount_efi()
262+
update_systemd_boot_entries
263+
cp /boot/initrd.img "$EFI_DIR"
264+
cp /boot/vmlinuz "$EFI_DIR"
265+
bootctl update
266+
umount $EFI_DIR
267+
else
268+
for dev in $(get_bootloader_devices); do
269+
opts="--root-directory=/mnt"
270+
271+
if [[ "$PLATFORM" == "esx" ]] || [[ "$PLATFORM" == "oci" ]]; then
272+
opts+=" --debug-image=all"
273+
opts+=" -v"
274+
fi
275+
276+
# shellcheck disable=SC2086
277+
grub-install $opts "/dev/$dev" ||
278+
die "'grub-install' for '$dev' failed in '$CONTAINER'"
279+
done
235280

281+
grub-mkconfig -o /mnt/boot/grub/grub.cfg ||
282+
die "'grub-mkconfig' failed in '$CONTAINER'"
283+
fi
236284
set_bootfs_mounted_cleanup
237285
trap - EXIT
238286
}
@@ -367,7 +415,7 @@ set-bootfs)
367415
# We only have a single bootloader on any given appliance, so we
368416
# need to ensure that only a single process is attempting to
369417
# update the bootloader at any given time. The locking done here
370-
# is to help prevent accidential corruption of the bootloader,
418+
# is to help prevent accidental corruption of the bootloader,
371419
# by ensuring only a single invocation of this script can set
372420
# the boot filesystem at any given time.
373421
#

0 commit comments

Comments
 (0)