The linux_xen template orders the "early" initrd file(s) _first_
(i.e., before the "real" initrd files) and that seems reasonable,
as microcode updates usually come first.
However, this usually breaks Linux boot with initrd under Xen
because Xen assumes the real initrd is the first multiboot[2]
module after the kernel, passing its address over to Linux in
Xen's start_info struct.
So, if a microcode-only initrd (i.e., without init/userspace)
is found by grub-mkconfig, it ends up considered as a normal
initrd by the Linux kernel, which cannot do anything with it
(as it has no other files) and panic()s unable to mount root
if it depends on a initrd to do that (e.g., root=UUID=...).
...
Well, since Xen doesn't actually use the provided microcode
by default / unless the 'ucode=<module number|scan>' option
is enabled, this isn't used in the general case (and breaks).
Additionally, if an user enables the 'ucode=' option, that
either specifies which module is to be used for microcode,
or scans all modules (regardless of being first) for that.
Thus, for Xen:
- it is *not required* to have microcode first,
- but it is *required* to have real initrd first
So, fix it by ordering the real initrd before early initrd(s).
After:
# touch /boot/xen /boot/microcode.cpio
# grub-mkconfig 2>/dev/null | grep -P '^\t(multiboot|module)'
multiboot /boot/xen ...
module /boot/vmlinuz-5.4.0-122-generic ...
module --nounzip /boot/initrd.img-5.4.0-122-generic
module --nounzip /boot/microcode.cpio
...
Corner case specific to Xen implementation details:
It is actually _possible_ to have a microcode initrd first,
but that requires a non-default option (so can't rely on it),
and it turns out to be inconsistent with its counterpart
(really shouldn't rely on it, as it may get confusing; below).
'ucode=1' does manually specify the first module is microcode
_AND_ clears its bit in the module bitmap. The next module is
now the 'new first', and gets passed to Linux as initrd. Good.
'ucode=scan' checks all modules for microcode, but does _NOT_
clear a bit if it finds one (reasonable, as it can find that
prepended in a "real" initrd anyway, which needs to be used).
The first module still gets passed to Linux as initrd. Bad.
Fixes: e86f6aafb8de (grub-mkconfig/20_linux_xen: Support multiple early initrd images)
Signed-off-by: Mauricio Faria de Oliveira <mfo@canonical.com>
Acked-by: Juergen Gross <jgross@suse.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
375 lines
14 KiB
Bash
375 lines
14 KiB
Bash
#! /bin/sh
|
|
set -e
|
|
|
|
# grub-mkconfig helper script.
|
|
# Copyright (C) 2006,2007,2008,2009,2010 Free Software Foundation, Inc.
|
|
#
|
|
# GRUB is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# GRUB is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with GRUB. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
prefix="@prefix@"
|
|
exec_prefix="@exec_prefix@"
|
|
datarootdir="@datarootdir@"
|
|
|
|
. "$pkgdatadir/grub-mkconfig_lib"
|
|
|
|
export TEXTDOMAIN=@PACKAGE@
|
|
export TEXTDOMAINDIR="@localedir@"
|
|
|
|
CLASS="--class gnu-linux --class gnu --class os --class xen"
|
|
|
|
if [ "x${GRUB_DISTRIBUTOR}" = "x" ] ; then
|
|
OS=GNU/Linux
|
|
else
|
|
OS="${GRUB_DISTRIBUTOR} GNU/Linux"
|
|
CLASS="--class $(echo ${GRUB_DISTRIBUTOR} | tr 'A-Z' 'a-z' | cut -d' ' -f1|LC_ALL=C sed 's,[^[:alnum:]_],_,g') ${CLASS}"
|
|
fi
|
|
|
|
# loop-AES arranges things so that /dev/loop/X can be our root device, but
|
|
# the initrds that Linux uses don't like that.
|
|
case ${GRUB_DEVICE} in
|
|
/dev/loop/*|/dev/loop[0-9])
|
|
GRUB_DEVICE=`losetup ${GRUB_DEVICE} | sed -e "s/^[^(]*(\([^)]\+\)).*/\1/"`
|
|
;;
|
|
esac
|
|
|
|
if [ "x${GRUB_CMDLINE_LINUX_RECOVERY}" = "x" ] ; then
|
|
GRUB_CMDLINE_LINUX_RECOVERY=single
|
|
fi
|
|
|
|
# Default to disabling partition uuid support to maintian compatibility with
|
|
# older kernels.
|
|
GRUB_DISABLE_LINUX_PARTUUID=${GRUB_DISABLE_LINUX_PARTUUID-true}
|
|
|
|
# btrfs may reside on multiple devices. We cannot pass them as value of root= parameter
|
|
# and mounting btrfs requires user space scanning, so force UUID in this case.
|
|
if ( [ "x${GRUB_DEVICE_UUID}" = "x" ] && [ "x${GRUB_DEVICE_PARTUUID}" = "x" ] ) \
|
|
|| ( [ "x${GRUB_DISABLE_LINUX_UUID}" = "xtrue" ] \
|
|
&& [ "x${GRUB_DISABLE_LINUX_PARTUUID}" = "xtrue" ] ) \
|
|
|| ( ! test -e "/dev/disk/by-uuid/${GRUB_DEVICE_UUID}" \
|
|
&& ! test -e "/dev/disk/by-partuuid/${GRUB_DEVICE_PARTUUID}" ) \
|
|
|| ( test -e "${GRUB_DEVICE}" && uses_abstraction "${GRUB_DEVICE}" lvm ); then
|
|
LINUX_ROOT_DEVICE=${GRUB_DEVICE}
|
|
elif [ "x${GRUB_DEVICE_UUID}" = "x" ] \
|
|
|| [ "x${GRUB_DISABLE_LINUX_UUID}" = "xtrue" ]; then
|
|
LINUX_ROOT_DEVICE=PARTUUID=${GRUB_DEVICE_PARTUUID}
|
|
else
|
|
LINUX_ROOT_DEVICE=UUID=${GRUB_DEVICE_UUID}
|
|
fi
|
|
|
|
# Allow overriding GRUB_CMDLINE_LINUX and GRUB_CMDLINE_LINUX_DEFAULT.
|
|
if [ "${GRUB_CMDLINE_LINUX_XEN_REPLACE}" ]; then
|
|
GRUB_CMDLINE_LINUX="${GRUB_CMDLINE_LINUX_XEN_REPLACE}"
|
|
fi
|
|
if [ "${GRUB_CMDLINE_LINUX_XEN_REPLACE_DEFAULT}" ]; then
|
|
GRUB_CMDLINE_LINUX_DEFAULT="${GRUB_CMDLINE_LINUX_XEN_REPLACE_DEFAULT}"
|
|
fi
|
|
|
|
case x"$GRUB_FS" in
|
|
xbtrfs)
|
|
rootsubvol="`make_system_path_relative_to_its_root /`"
|
|
rootsubvol="${rootsubvol#/}"
|
|
if [ "x${rootsubvol}" != x ]; then
|
|
GRUB_CMDLINE_LINUX="rootflags=subvol=${rootsubvol} ${GRUB_CMDLINE_LINUX}"
|
|
fi;;
|
|
xzfs)
|
|
rpool=`${grub_probe} --device ${GRUB_DEVICE} --target=fs_label 2>/dev/null || true`
|
|
bootfs="`make_system_path_relative_to_its_root / | sed -e "s,@$,,"`"
|
|
LINUX_ROOT_DEVICE="ZFS=${rpool}${bootfs%/}"
|
|
;;
|
|
esac
|
|
|
|
title_correction_code=
|
|
|
|
linux_entry ()
|
|
{
|
|
linux_entry_xsm "$@" false
|
|
linux_entry_xsm "$@" true
|
|
}
|
|
linux_entry_xsm ()
|
|
{
|
|
os="$1"
|
|
version="$2"
|
|
xen_version="$3"
|
|
type="$4"
|
|
args="$5"
|
|
xen_args="$6"
|
|
xsm="$7"
|
|
# If user wants to enable XSM support, make sure there's
|
|
# corresponding policy file.
|
|
if ${xsm} ; then
|
|
xenpolicy="xenpolicy-$xen_version"
|
|
if test ! -e "${xen_dirname}/${xenpolicy}" ; then
|
|
return
|
|
fi
|
|
xen_args="$xen_args flask=enforcing"
|
|
xen_version="$(gettext_printf "%s (XSM enabled)" "$xen_version")"
|
|
# xen_version is used for messages only; actual file is xen_basename
|
|
fi
|
|
if [ -z "$boot_device_id" ]; then
|
|
boot_device_id="$(grub_get_device_id "${GRUB_DEVICE}")"
|
|
fi
|
|
if [ x$type != xsimple ] ; then
|
|
if [ x$type = xrecovery ] ; then
|
|
title="$(gettext_printf "%s, with Xen %s and Linux %s (recovery mode)" "${os}" "${xen_version}" "${version}")"
|
|
else
|
|
title="$(gettext_printf "%s, with Xen %s and Linux %s" "${os}" "${xen_version}" "${version}")"
|
|
fi
|
|
replacement_title="$(echo "Advanced options for ${OS}" | sed 's,>,>>,g')>$(echo "$title" | sed 's,>,>>,g')"
|
|
if [ x"Xen ${xen_version}>$title" = x"$GRUB_ACTUAL_DEFAULT" ]; then
|
|
quoted="$(echo "$GRUB_ACTUAL_DEFAULT" | grub_quote)"
|
|
title_correction_code="${title_correction_code}if [ \"x\$default\" = '$quoted' ]; then default='$(echo "$replacement_title" | grub_quote)'; fi;"
|
|
grub_warn "$(gettext_printf "Please don't use old title \`%s' for GRUB_DEFAULT, use \`%s' (for versions before 2.00) or \`%s' (for 2.00 or later)" "$GRUB_ACTUAL_DEFAULT" "$replacement_title" "gnulinux-advanced-$boot_device_id>gnulinux-$version-$type-$boot_device_id")"
|
|
fi
|
|
echo "menuentry '$(echo "$title" | grub_quote)' ${CLASS} \$menuentry_id_option 'xen-gnulinux-$version-$type-$boot_device_id' {" | sed "s/^/$submenu_indentation/"
|
|
else
|
|
title="$(gettext_printf "%s, with Xen hypervisor" "${os}")"
|
|
echo "menuentry '$(echo "$title" | grub_quote)' ${CLASS} \$menuentry_id_option 'xen-gnulinux-simple-$boot_device_id' {" | sed "s/^/$submenu_indentation/"
|
|
fi
|
|
if [ x$type != xrecovery ] ; then
|
|
save_default_entry | grub_add_tab | sed "s/^/$submenu_indentation/"
|
|
fi
|
|
|
|
if [ -z "${prepare_boot_cache}" ]; then
|
|
prepare_boot_cache="$(prepare_grub_to_access_device ${GRUB_DEVICE_BOOT} | grub_add_tab)"
|
|
fi
|
|
printf '%s\n' "${prepare_boot_cache}" | sed "s/^/$submenu_indentation/"
|
|
xmessage="$(gettext_printf "Loading Xen %s ..." ${xen_version})"
|
|
lmessage="$(gettext_printf "Loading Linux %s ..." ${version})"
|
|
sed "s/^/$submenu_indentation/" << EOF
|
|
echo '$(echo "$xmessage" | grub_quote)'
|
|
if [ "\$grub_platform" = "pc" -o "\$grub_platform" = "" ]; then
|
|
xen_rm_opts=
|
|
else
|
|
xen_rm_opts="no-real-mode edd=off"
|
|
fi
|
|
${xen_loader} ${rel_xen_dirname}/${xen_basename} placeholder ${xen_args} \${xen_rm_opts}
|
|
echo '$(echo "$lmessage" | grub_quote)'
|
|
${module_loader} ${rel_dirname}/${basename} placeholder root=${linux_root_device_thisversion} ro ${args}
|
|
EOF
|
|
if test -n "${initrd}" ; then
|
|
# TRANSLATORS: ramdisk isn't identifier. Should be translated.
|
|
message="$(gettext_printf "Loading initial ramdisk ...")"
|
|
initrd_path=
|
|
for i in ${initrd}; do
|
|
initrd_path="${rel_dirname}/${i}"
|
|
sed "s/^/$submenu_indentation/" << EOF
|
|
echo '$(echo "$message" | grub_quote)'
|
|
${module_loader} --nounzip $(echo $initrd_path)
|
|
EOF
|
|
done
|
|
fi
|
|
if test -n "${xenpolicy}" ; then
|
|
message="$(gettext_printf "Loading XSM policy ...")"
|
|
sed "s/^/$submenu_indentation/" << EOF
|
|
echo '$(echo "$message" | grub_quote)'
|
|
${module_loader} ${rel_dirname}/${xenpolicy}
|
|
EOF
|
|
fi
|
|
sed "s/^/$submenu_indentation/" << EOF
|
|
}
|
|
EOF
|
|
}
|
|
|
|
linux_list=
|
|
for i in /boot/vmlinu[xz]-* /vmlinu[xz]-* /boot/kernel-*; do
|
|
if grub_file_is_not_garbage "$i"; then
|
|
basename=$(basename $i)
|
|
version=$(echo $basename | sed -e "s,^[^0-9]*-,,g")
|
|
dirname=$(dirname $i)
|
|
config=
|
|
for j in "${dirname}/config-${version}" "${dirname}/config-${alt_version}" "/etc/kernels/kernel-config-${version}" ; do
|
|
if test -e "${j}" ; then
|
|
config="${j}"
|
|
break
|
|
fi
|
|
done
|
|
if (grep -qx "CONFIG_XEN_DOM0=y" "${config}" 2> /dev/null || grep -qx "CONFIG_XEN_PRIVILEGED_GUEST=y" "${config}" 2> /dev/null); then linux_list="$linux_list $i" ; fi
|
|
fi
|
|
done
|
|
if [ "x${linux_list}" = "x" ] ; then
|
|
exit 0
|
|
fi
|
|
|
|
file_is_not_xen_garbage () {
|
|
case "$1" in
|
|
*/xen-syms-*)
|
|
return 1;;
|
|
*/xenpolicy-*)
|
|
return 1;;
|
|
*/*.config)
|
|
return 1;;
|
|
*)
|
|
return 0;;
|
|
esac
|
|
}
|
|
|
|
xen_list=
|
|
for i in /boot/xen*; do
|
|
if grub_file_is_not_garbage "$i" && file_is_not_xen_garbage "$i" ; then xen_list="$xen_list $i" ; fi
|
|
done
|
|
prepare_boot_cache=
|
|
boot_device_id=
|
|
|
|
title_correction_code=
|
|
|
|
machine=`uname -m`
|
|
|
|
case "$machine" in
|
|
i?86) GENKERNEL_ARCH="x86" ;;
|
|
mips|mips64) GENKERNEL_ARCH="mips" ;;
|
|
mipsel|mips64el) GENKERNEL_ARCH="mipsel" ;;
|
|
arm*) GENKERNEL_ARCH="arm" ;;
|
|
*) GENKERNEL_ARCH="$machine" ;;
|
|
esac
|
|
|
|
# Extra indentation to add to menu entries in a submenu. We're not in a submenu
|
|
# yet, so it's empty. In a submenu it will be equal to '\t' (one tab).
|
|
submenu_indentation=""
|
|
|
|
# Perform a reverse version sort on the entire xen_list and linux_list.
|
|
# Temporarily replace the '.old' suffix by ' 1' and append ' 2' for all
|
|
# other files to order the '.old' files after their non-old counterpart
|
|
# in reverse-sorted order.
|
|
|
|
reverse_sorted_xen_list=$(echo ${xen_list} | tr ' ' '\n' | sed -e 's/\.old$/ 1/; / 1$/! s/$/ 2/' | version_sort -r | sed -e 's/ 1$/.old/; s/ 2$//')
|
|
reverse_sorted_linux_list=$(echo ${linux_list} | tr ' ' '\n' | sed -e 's/\.old$/ 1/; / 1$/! s/$/ 2/' | version_sort -r | sed -e 's/ 1$/.old/; s/ 2$//')
|
|
|
|
is_top_level=true
|
|
|
|
for current_xen in ${reverse_sorted_xen_list}; do
|
|
xen_basename=`basename ${current_xen}`
|
|
xen_dirname=`dirname ${current_xen}`
|
|
rel_xen_dirname=`make_system_path_relative_to_its_root $xen_dirname`
|
|
xen_version=`echo $xen_basename | sed -e "s,.gz$,,g;s,^xen-,,g"`
|
|
if [ -z "$boot_device_id" ]; then
|
|
boot_device_id="$(grub_get_device_id "${GRUB_DEVICE}")"
|
|
fi
|
|
if [ "x$is_top_level" != xtrue ]; then
|
|
echo " submenu '$(gettext_printf "Xen hypervisor, version %s" "${xen_version}" | grub_quote)' \$menuentry_id_option 'xen-hypervisor-$xen_version-$boot_device_id' {"
|
|
fi
|
|
if ($grub_file --is-arm64-efi $current_xen); then
|
|
xen_loader="xen_hypervisor"
|
|
module_loader="xen_module"
|
|
else
|
|
if ($grub_file --is-x86-multiboot2 $current_xen); then
|
|
xen_loader="multiboot2"
|
|
module_loader="module2"
|
|
else
|
|
xen_loader="multiboot"
|
|
module_loader="module"
|
|
fi
|
|
fi
|
|
|
|
initrd_early=
|
|
for i in ${GRUB_EARLY_INITRD_LINUX_STOCK} \
|
|
${GRUB_EARLY_INITRD_LINUX_CUSTOM}; do
|
|
if test -e "${xen_dirname}/${i}" ; then
|
|
initrd_early="${initrd_early} ${i}"
|
|
fi
|
|
done
|
|
|
|
for linux in ${reverse_sorted_linux_list}; do
|
|
gettext_printf "Found linux image: %s\n" "$linux" >&2
|
|
basename=`basename $linux`
|
|
dirname=`dirname $linux`
|
|
rel_dirname=`make_system_path_relative_to_its_root $dirname`
|
|
version=`echo $basename | sed -e "s,^[^0-9]*-,,g"`
|
|
alt_version=`echo $version | sed -e "s,\.old$,,g"`
|
|
linux_root_device_thisversion="${LINUX_ROOT_DEVICE}"
|
|
|
|
initrd_real=
|
|
for i in "initrd.img-${version}" "initrd-${version}.img" \
|
|
"initrd-${alt_version}.img.old" "initrd-${version}.gz" \
|
|
"initrd-${alt_version}.gz.old" "initrd-${version}" \
|
|
"initramfs-${version}.img" "initramfs-${alt_version}.img.old" \
|
|
"initrd.img-${alt_version}" "initrd-${alt_version}.img" \
|
|
"initrd-${alt_version}" "initramfs-${alt_version}.img" \
|
|
"initramfs-genkernel-${version}" \
|
|
"initramfs-genkernel-${alt_version}" \
|
|
"initramfs-genkernel-${GENKERNEL_ARCH}-${version}" \
|
|
"initramfs-genkernel-${GENKERNEL_ARCH}-${alt_version}" ; do
|
|
if test -e "${dirname}/${i}" ; then
|
|
initrd_real="$i"
|
|
break
|
|
fi
|
|
done
|
|
|
|
initrd=
|
|
if test -n "${initrd_early}" || test -n "${initrd_real}"; then
|
|
# Xen assumes the real initrd is the first module after the kernel.
|
|
# Additional (later) initrds can also be used for microcode update,
|
|
# with Xen option 'ucode=<scan|module number> (non-default anyway).
|
|
initrd="${initrd_real} ${initrd_early}"
|
|
|
|
initrd_display=
|
|
for i in ${initrd}; do
|
|
initrd_display="${initrd_display} ${dirname}/${i}"
|
|
done
|
|
gettext_printf "Found initrd image: %s\n" "$(echo $initrd_display)" >&2
|
|
fi
|
|
|
|
if test -z "${initrd_real}"; then
|
|
# "UUID=" magic is parsed by initrds. Since there's no initrd, it can't work here.
|
|
if [ "x${GRUB_DEVICE_PARTUUID}" = "x" ] \
|
|
|| [ "x${GRUB_DISABLE_LINUX_PARTUUID}" = "xtrue" ]; then
|
|
|
|
linux_root_device_thisversion=${GRUB_DEVICE}
|
|
else
|
|
linux_root_device_thisversion=PARTUUID=${GRUB_DEVICE_PARTUUID}
|
|
fi
|
|
fi
|
|
|
|
# The GRUB_DISABLE_SUBMENU option used to be different than others since it was
|
|
# mentioned in the documentation that has to be set to 'y' instead of 'true' to
|
|
# enable it. This caused a lot of confusion to users that set the option to 'y',
|
|
# 'yes' or 'true'. This was fixed but all of these values must be supported now.
|
|
if [ "x${GRUB_DISABLE_SUBMENU}" = xyes ] || [ "x${GRUB_DISABLE_SUBMENU}" = xy ]; then
|
|
GRUB_DISABLE_SUBMENU="true"
|
|
fi
|
|
|
|
if [ "x$is_top_level" = xtrue ] && [ "x${GRUB_DISABLE_SUBMENU}" != xtrue ]; then
|
|
linux_entry "${OS}" "${version}" "${xen_version}" simple \
|
|
"${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT}" "${GRUB_CMDLINE_XEN} ${GRUB_CMDLINE_XEN_DEFAULT}"
|
|
|
|
submenu_indentation="$grub_tab$grub_tab"
|
|
|
|
if [ -z "$boot_device_id" ]; then
|
|
boot_device_id="$(grub_get_device_id "${GRUB_DEVICE}")"
|
|
fi
|
|
# TRANSLATORS: %s is replaced with an OS name
|
|
echo "submenu '$(gettext_printf "Advanced options for %s (with Xen hypervisor)" "${OS}" | grub_quote)' \$menuentry_id_option 'gnulinux-advanced-$boot_device_id' {"
|
|
echo " submenu '$(gettext_printf "Xen hypervisor, version %s" "${xen_version}" | grub_quote)' \$menuentry_id_option 'xen-hypervisor-$xen_version-$boot_device_id' {"
|
|
is_top_level=false
|
|
fi
|
|
|
|
linux_entry "${OS}" "${version}" "${xen_version}" advanced \
|
|
"${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT}" "${GRUB_CMDLINE_XEN} ${GRUB_CMDLINE_XEN_DEFAULT}"
|
|
if [ "x${GRUB_DISABLE_RECOVERY}" != "xtrue" ]; then
|
|
linux_entry "${OS}" "${version}" "${xen_version}" recovery \
|
|
"${GRUB_CMDLINE_LINUX_RECOVERY} ${GRUB_CMDLINE_LINUX}" "${GRUB_CMDLINE_XEN}"
|
|
fi
|
|
done
|
|
if [ x"$is_top_level" != xtrue ]; then
|
|
echo ' }'
|
|
fi
|
|
done
|
|
|
|
# If at least one kernel was found, then we need to
|
|
# add a closing '}' for the submenu command.
|
|
if [ x"$is_top_level" != xtrue ]; then
|
|
echo '}'
|
|
fi
|
|
|
|
echo "$title_correction_code"
|