diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0255ecf45..b994e4f2e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,17 +1,17 @@ -name: Test build and package QubesOS RPMs +name: QubesOS RPMs on: push: branches: - 'intel-txt-aem*' + - 'xen-uefi' tags: - '*' jobs: qubes-dom0-package: - uses: TrenchBoot/.github/.github/workflows/qubes-dom0-package.yml@master + uses: TrenchBoot/.github/.github/workflows/qubes-dom0-packagev2.yml@master with: - base-commit: '594fe4760a33158e7cd500825ee26a0c9aef5f6b' - patch-start: 1120 qubes-component: 'grub2' - spec-pattern: '/^Patch1119:/' + qubes-pkg-src-dir: '.' + qubes-pkg-version: '2.13' diff --git a/.gitignore b/.gitignore index 2105d87c8..0c7d7d404 100644 --- a/.gitignore +++ b/.gitignore @@ -32,7 +32,6 @@ Makefile Makefile.in ascii.bitmaps genkernsyms.sh -gensymlist.sh grub-bin2h grub-emu grub-emu-lite @@ -101,7 +100,6 @@ widthspec.bin /docs/*.info-[0-9]* /docs/stamp-1 /docs/stamp-vti -/docs/version-dev.texi /docs/version.texi /ehci_test /erofs_test diff --git a/0010-re-write-.gitignore.patch b/0010-re-write-.gitignore.patch new file mode 100644 index 000000000..74cb0dae9 --- /dev/null +++ b/0010-re-write-.gitignore.patch @@ -0,0 +1,248 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Peter Jones +Date: Mon, 8 Jul 2019 12:55:29 +0200 +Subject: [PATCH] re-write .gitignore + +--- + .gitignore | 152 ++++++++++++++++++++++++++++++++++++++ + docs/.gitignore | 5 ++ + grub-core/.gitignore | 16 ++++ + grub-core/lib/.gitignore | 1 + + include/grub/gcrypt/.gitignore | 2 + + po/.gitignore | 5 ++ + util/bash-completion.d/.gitignore | 2 + + 7 files changed, 183 insertions(+) + create mode 100644 docs/.gitignore + create mode 100644 grub-core/.gitignore + create mode 100644 grub-core/lib/.gitignore + create mode 100644 include/grub/gcrypt/.gitignore + create mode 100644 po/.gitignore + create mode 100644 util/bash-completion.d/.gitignore + +diff --git a/.gitignore b/.gitignore +index f6a1bd05175..594d0134d33 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -275,3 +275,155 @@ widthspec.bin + /xfs_test + /xzcompress_test + /zfs_test ++======= ++# things ./autogen.sh will create ++/Makefile.utilgcry.def ++/ABOUT-NLS ++/aclocal.m4 ++/autom4te.cache ++/build-aux ++/configure ++/gnulib ++/grub-core/lib/gnulib/ ++/Makefile ++ ++# things very common editors create that we never want ++*~ ++.*.sw? ++*.patch ++ ++# stuff you're likely to make while building test trees ++grub.cfg ++/build*/ ++ ++# built objects across the whole tree ++Makefile.in ++*.a ++*.am ++*.efi ++*.exec ++*.image ++*.img ++*.info ++*.lst ++*.marker ++/m4 ++*.mod ++*.module ++*.o ++*.pf2 ++*.yy.[ch] ++.deps/ ++.deps-core/ ++.deps-util/ ++.dirstamp ++ ++# next are things you get if you do ./configure in the topdir (for e.g. ++# "make dist" invocation. ++/config-util.h ++/config.h ++/include/grub/cpu ++/include/grub/machine ++/INSTALL ++/INSTALL.grub ++/po/Makefile.in.in ++/po/Makevars ++/po/Makevars.template ++/po/POTFILES ++/po/Rules-quot ++/stamp-h ++/stamp-h1 ++bootstrap.log ++config.log ++config.status ++ ++# stuff "make dist" creates ++ChangeLog ++grub-*.tar ++grub-*.tar.* ++ ++# stuff "make" creates ++/[[:digit:]][[:digit:]]_?* ++/ascii.h ++/build-grub-gen-asciih ++/build-grub-gen-widthspec ++/build-grub-mkfont ++/config-util.h.in ++/garbage-gen ++/grub*-bios-setup ++/grub*-bios-setup.8 ++/grub*-editenv ++/grub*-editenv.1 ++/grub*-file ++/grub*-file.1 ++/grub*-fs-tester ++/grub*-fstest ++/grub*-fstest.1 ++/grub*-get-kernel-settings ++/grub*-get-kernel-settings.3 ++/grub*-glue-efi ++/grub*-glue-efi.1 ++/grub*-install ++/grub*-install.8 ++/grub*-kbdcomp ++/grub*-kbdcomp.1 ++/grub*-macbless ++/grub*-macbless.8 ++/grub*-menulst2cfg ++/grub*-menulst2cfg.1 ++/grub*-mount ++/grub*-mount.1 ++/grub*-mkconfig ++/grub*-mkconfig.8 ++/grub*-mkconfig_lib ++/grub*-mkfont ++/grub*-mkfont.1 ++/grub*-mkimage ++/grub*-mkimage.1 ++/grub*-mklayout ++/grub*-mklayout.1 ++/grub*-mknetdir ++/grub*-mknetdir.1 ++/grub*-mkpasswd-pbkdf2 ++/grub*-mkpasswd-pbkdf2.1 ++/grub*-mkrelpath ++/grub*-mkrelpath.1 ++/grub*-mkrescue ++/grub*-mkrescue.1 ++/grub*-mkstandalone ++/grub*-mkstandalone.1 ++/grub*-ofpathname ++/grub*-ofpathname.8 ++/grub*-probe ++/grub*-probe.8 ++/grub*-reboot ++/grub*-reboot.8 ++/grub*-render-label ++/grub*-render-label.1 ++/grub*-rpm-sort ++/grub*-rpm-sort.8 ++/grub*-script-check ++/grub*-script-check.1 ++/grub*-set-bootflag ++/grub*-set-bootflag.1 ++/grub*-set-default ++/grub*-set-default.8 ++/grub*-set-password ++/grub*-set-password.8 ++/grub*-shell ++/grub*-shell-tester ++/grub*-sparc64-setup ++/grub*-sparc64-setup.8 ++/grub*-syslinux2cfg ++/grub*-syslinux2cfg.1 ++/grub*-switch-to-blscfg ++/grub*-switch-to-blscfg.8 ++/grub_fstest.pp ++/grub_fstest_init.c ++/grub_fstest_init.lst ++/grub_script.tab.[ch] ++/libgrub.pp ++/libgrub_a_init.c ++/libgrub_a_init.lst ++/stamp-h.in ++/widthspec.h +diff --git a/docs/.gitignore b/docs/.gitignore +new file mode 100644 +index 00000000000..e1d849ef95b +--- /dev/null ++++ b/docs/.gitignore +@@ -0,0 +1,5 @@ ++/*.in ++/Makefile ++/stamp-1 ++/stamp-vti ++/version*.texi +diff --git a/grub-core/.gitignore b/grub-core/.gitignore +new file mode 100644 +index 00000000000..2acce281159 +--- /dev/null ++++ b/grub-core/.gitignore +@@ -0,0 +1,16 @@ ++/*.lst ++/Makefile ++/Makefile.gcry.def ++/unidata.c ++/build-grub-module-verifier ++/gdb_grub ++/genmod.sh ++/gensyminfo.sh ++/gentrigtables ++/gmodule.pl ++/grub_script.tab.[ch] ++/modinfo.sh ++/rs_decoder.h ++/symlist.c ++/symlist.h ++/trigtables.c +diff --git a/grub-core/lib/.gitignore b/grub-core/lib/.gitignore +new file mode 100644 +index 00000000000..68154591404 +--- /dev/null ++++ b/grub-core/lib/.gitignore +@@ -0,0 +1 @@ ++/libgcrypt-grub/ +diff --git a/include/grub/gcrypt/.gitignore b/include/grub/gcrypt/.gitignore +new file mode 100644 +index 00000000000..8fbf5646246 +--- /dev/null ++++ b/include/grub/gcrypt/.gitignore +@@ -0,0 +1,2 @@ ++g10lib.h ++gcrypt.h +diff --git a/po/.gitignore b/po/.gitignore +new file mode 100644 +index 00000000000..f507e7741e3 +--- /dev/null ++++ b/po/.gitignore +@@ -0,0 +1,5 @@ ++/Makefile ++/POTFILES*.in ++/grub.pot ++/remove-potcdate.sed ++/stamp-po +diff --git a/util/bash-completion.d/.gitignore b/util/bash-completion.d/.gitignore +new file mode 100644 +index 00000000000..6813a527ad3 +--- /dev/null ++++ b/util/bash-completion.d/.gitignore +@@ -0,0 +1,2 @@ ++Makefile ++grub diff --git a/20-grub.install b/20-grub.install new file mode 100755 index 000000000..f2de7760c --- /dev/null +++ b/20-grub.install @@ -0,0 +1,167 @@ +#!/bin/bash + +if ! [[ $KERNEL_INSTALL_MACHINE_ID ]]; then + exit 0 +fi + +[[ -f /etc/default/grub ]] && . /etc/default/grub +[[ -f /etc/os-release ]] && . /etc/os-release + +COMMAND="$1" +KERNEL_VERSION="$2" +BOOT_DIR_ABS="$3" +KERNEL_IMAGE="$4" + +KERNEL_DIR="${KERNEL_IMAGE%/*}" + +MACHINE_ID=$KERNEL_INSTALL_MACHINE_ID + +# If ${BOOT_DIR_ABS} exists, some other boot loader is active. +[[ -d "${BOOT_DIR_ABS}" ]] && exit 0 + +BLS_DIR="/boot/loader/entries" + +mkbls() { + local kernelver=$1 && shift + local datetime=$1 && shift + + local debugname="" + local debugid="" + local flavor="" + + if [[ "$kernelver" == *\+* ]] ; then + local flavor=-"${kernelver##*+}" + if [[ "${flavor}" == "-debug" ]]; then + local debugname=" with debugging" + local debugid="-debug" + fi + fi + + cat </dev/null && \ + restorecon -R "/boot/${i##*/}-${KERNEL_VERSION}" + done + # hmac is .vmlinuz-.hmac so needs a special treatment + i="$KERNEL_DIR/.${KERNEL_IMAGE##*/}.hmac" + if [[ -e "$i" ]]; then + rm -f "/boot/.${KERNEL_IMAGE##*/}-${KERNEL_VERSION}.hmac" + cp -a "$i" "/boot/.${KERNEL_IMAGE##*/}-${KERNEL_VERSION}.hmac" + command -v restorecon &>/dev/null && \ + restorecon "/boot/.${KERNEL_IMAGE##*/}-${KERNEL_VERSION}.hmac" + fi + fi + + if [[ "x${GRUB_ENABLE_BLSCFG}" = "xtrue" ]] || [[ ! -f /sbin/new-kernel-pkg ]]; then + eval "$(grub2-get-kernel-settings)" || true + [[ -d "$BLS_DIR" ]] || mkdir -m 0700 -p "$BLS_DIR" + BLS_ID="${MACHINE_ID}-${KERNEL_VERSION}" + BLS_TARGET="${BLS_DIR}/${BLS_ID}.conf" + if [[ -f "${KERNEL_DIR}/bls.conf" ]]; then + cp -aT "${KERNEL_DIR}/bls.conf" "${BLS_TARGET}" || exit $? + else + mkbls "${KERNEL_VERSION}" \ + "$(date -u +%Y%m%d%H%M%S -d "$(stat -c '%y' "${KERNEL_DIR}")")" \ + >"${BLS_TARGET}" + fi + + LINUX="$(grep '^linux[ \t]' "${BLS_TARGET}" | sed -e 's,^linux[ \t]*,,')" + INITRD="$(grep '^initrd[ \t]' "${BLS_TARGET}" | sed -e 's,^initrd[ \t]*,,')" + LINUX_RELPATH="$(grub2-mkrelpath /boot${LINUX})" + BOOTPREFIX="$(dirname ${LINUX_RELPATH})" + ROOTPREFIX="$(dirname "/boot${LINUX}")" + + if [[ $LINUX != $LINUX_RELPATH ]]; then + sed -i -e "s,^linux.*,linux ${BOOTPREFIX}${LINUX},g" "${BLS_TARGET}" + sed -i -e "s,^initrd.*,initrd ${BOOTPREFIX}${INITRD},g" "${BLS_TARGET}" + fi + + if [[ "$KERNEL_VERSION" == *\+* ]] && [ "x$GRUB_DEFAULT_TO_DEBUG" != "xtrue" ]; then + GRUB_UPDATE_DEFAULT_KERNEL=false + fi + + if [ "x$GRUB_UPDATE_DEFAULT_KERNEL" = "xtrue" ]; then + NEWDEFAULT="${BLS_ID}" + fi + + if [ "x$GRUB_LINUX_MAKE_DEBUG" = "xtrue" ]; then + ARCH="$(uname -m)" + BLS_DEBUG_ID="$(echo ${BLS_ID} | sed -e "s/${KERNEL_VERSION}/${KERNEL_VERSION}~debug/")" + BLS_DEBUG="$(echo ${BLS_TARGET} | sed -e "s/${KERNEL_VERSION}/${KERNEL_VERSION}~debug/")" + cp -aT "${BLS_TARGET}" "${BLS_DEBUG}" + TITLE="$(grep '^title[ \t]' "${BLS_DEBUG}" | sed -e 's/^title[ \t]*//')" + sed -i -e "s/^title.*/title ${TITLE}${GRUB_LINUX_DEBUG_TITLE_POSTFIX}/" "${BLS_DEBUG}" + sed -i -e "s/^id.*/id ${BLS_DEBUG_ID}/" "${BLS_DEBUG}" + sed -i -e "s/^options.*/options \$kernelopts ${GRUB_CMDLINE_LINUX_DEBUG}/" "${BLS_DEBUG}" + if [ -n "$NEWDEFAULT" -a "x$GRUB_DEFAULT_TO_DEBUG" = "xtrue" ]; then + NEWDEFAULT="${BLS_DEBUG_ID}" + fi + fi + if [ -n "$NEWDEFAULT" ]; then + grub2-editenv - set "saved_entry=${NEWDEFAULT}" + fi + + # this probably isn't the best place to do this, but it will do for now. + if [ -e "${ROOTPREFIX}${INITRD}" -a -e "${ROOTPREFIX}${LINUX}" -a \ + "${ROOTPREFIX}${INITRD}" -ot "${ROOTPREFIX}${LINUX}" -a \ + -x /usr/lib/kernel/install.d/50-dracut.install ]; then + rm -f "${ROOTPREFIX}${INITRD}" + fi + exit 0 + fi + + /sbin/new-kernel-pkg --package "kernel${flavor}" --install "$KERNEL_VERSION" || exit $? + /sbin/new-kernel-pkg --package "kernel${flavor}" --mkinitrd --dracut --depmod --update "$KERNEL_VERSION" || exit $? + /sbin/new-kernel-pkg --package "kernel${flavor}" --rpmposttrans "$KERNEL_VERSION" || exit $? + # If grubby is used there's no need to run other installation plugins + exit 77 + ;; + remove) + + if [[ "x${GRUB_ENABLE_BLSCFG}" = "xtrue" ]] || [[ ! -f /sbin/new-kernel-pkg ]]; then + ARCH="$(uname -m)" + BLS_TARGET="${BLS_DIR}/${MACHINE_ID}-${KERNEL_VERSION}.conf" + BLS_DEBUG="$(echo ${BLS_TARGET} | sed -e "s/${KERNEL_VERSION}/${KERNEL_VERSION}~debug/")" + rm -f "${BLS_TARGET}" "${BLS_DEBUG}" + + for i in vmlinuz System.map config zImage.stub dtb; do + rm -rf "/boot/${i}-${KERNEL_VERSION}" + done + # hmac is .vmlinuz-.hmac so needs a special treatment + rm -f "/boot/.vmlinuz-${KERNEL_VERSION}.hmac" + + exit 0 + fi + + /sbin/new-kernel-pkg --package "kernel${flavor+-$flavor}" --rminitrd --rmmoddep --remove "$KERNEL_VERSION" || exit $? + # If grubby is used there's no need to run other installation plugins + exit 77 + ;; + *) + ;; +esac diff --git a/99-grub-mkconfig.install b/99-grub-mkconfig.install new file mode 100755 index 000000000..e37089936 --- /dev/null +++ b/99-grub-mkconfig.install @@ -0,0 +1,24 @@ +#!/bin/bash + +if ! [[ $KERNEL_INSTALL_MACHINE_ID ]]; then + exit 0 +fi + +ARCH=$(uname -m) + +# Is only needed for ppc64* since we can't assume a BLS capable bootloader there +if [[ $ARCH != "ppc64" && $ARCH != "ppc64le" ]]; then + exit 0 +fi + +[[ -f /etc/default/grub ]] && . /etc/default/grub + +COMMAND="$1" + +case "$COMMAND" in + add|remove) + grub2-mkconfig --no-grubenv-update -o /boot/grub2/grub.cfg >& /dev/null + ;; + *) + ;; +esac diff --git a/Makefile.util.def b/Makefile.util.def index 038253b37..f832e3f74 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -483,6 +483,30 @@ script = { installdir = grubconf; }; +script = { + name = '08_fallback_counting'; + common = util/grub.d/08_fallback_counting.in; + installdir = grubconf; +}; + +script = { + name = '12_menu_auto_hide'; + common = util/grub.d/12_menu_auto_hide.in; + installdir = grubconf; +}; + +script = { + name = '14_menu_show_once'; + common = util/grub.d/14_menu_show_once.in; + installdir = grubconf; +}; + +script = { + name = '01_users'; + common = util/grub.d/01_users.in; + installdir = grubconf; +}; + script = { name = '10_windows'; common = util/grub.d/10_windows.in; @@ -525,6 +549,12 @@ script = { condition = COND_HOST_LINUX; }; +script = { + name = '10_reset_boot_success'; + common = util/grub.d/10_reset_boot_success.in; + installdir = grubconf; +}; + script = { name = '10_xnu'; common = util/grub.d/10_xnu.in; @@ -569,6 +599,27 @@ script = { installdir = grubconf; }; +script = { + name = 'grub-systemd-integration.service'; + common = util/systemd/grub-systemd-integration.service.in; + installdir = systemdunit; + condition = COND_HOST_LINUX; +}; + +script = { + name = 'systemd-integration.sh'; + common = util/systemd/systemd-integration.sh.in; + installdir = grublibexec; + condition = COND_HOST_LINUX; +}; + +script = { + name = '10-grub-logind-service.conf'; + common = util/systemd/10-grub-logind-service.conf.in; + installdir = systemd_logind_service_d; + condition = COND_HOST_LINUX; +}; + program = { mansection = 1; name = grub-mkrescue; @@ -740,6 +791,13 @@ script = { installdir = sbin; }; +script = { + name = grub-get-kernel-settings; + common = util/grub-get-kernel-settings.in; + mansection = 3; + installdir = sbin; +}; + script = { name = grub-set-default; common = util/grub-set-default.in; @@ -754,6 +812,13 @@ script = { installdir = sbin; }; +script = { + name = grub-set-password; + common = util/grub-set-password.in; + mansection = 8; + installdir = sbin; +}; + script = { name = grub-mkconfig_lib; common = util/grub-mkconfig_lib.in; @@ -1468,3 +1533,10 @@ program = { ldadd = grub-core/lib/gnulib/libgnu.a; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; + +program = { + name = grub-set-bootflag; + installdir = sbin; + mansection = 1; + common = util/grub-set-bootflag.c; +}; diff --git a/acinclude.m4 b/acinclude.m4 index fa7840f09..0231e64e3 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -136,6 +136,25 @@ if test "x$grub_cv_prog_ld_build_id_none" = xyes; then fi ]) +dnl Supply --build-id=sha1 to ld if building modules. +dnl This suppresses warnings from ld on some systems +AC_DEFUN([grub_PROG_LD_BUILD_ID_SHA1], +[AC_MSG_CHECKING([whether linker accepts --build-id=sha1]) +AC_CACHE_VAL(grub_cv_prog_ld_build_id_sha1, +[save_LDFLAGS="$LDFLAGS" +LDFLAGS="$LDFLAGS -Wl,--build-id=sha1" +AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[]])], + [grub_cv_prog_ld_build_id_sha1=yes], + [grub_cv_prog_ld_build_id_sha1=no]) +LDFLAGS="$save_LDFLAGS" +]) +AC_MSG_RESULT([$grub_cv_prog_ld_build_id_sha1]) + +if test "x$grub_cv_prog_ld_build_id_sha1" = xyes; then + TARGET_LDFLAGS="$TARGET_LDFLAGS -Wl,--build-id=sha1" +fi +]) + dnl Check nm AC_DEFUN([grub_PROG_NM_WORKS], [AC_MSG_CHECKING([whether nm works]) diff --git a/bootstrap.conf b/bootstrap.conf index 7a7813d28..60de0597c 100644 --- a/bootstrap.conf +++ b/bootstrap.conf @@ -94,7 +94,7 @@ bootstrap_post_import_hook () { patch -d po -p3 \ < "po/gettext-patches/$patchname.patch" done - FROM_BOOTSTRAP=1 ./autogen.sh + PYTHON=python3 FROM_BOOTSTRAP=1 ./autogen.sh set +e # bootstrap expects this } diff --git a/conf/Makefile.common b/conf/Makefile.common index c60f55386..7fbe4b3dd 100644 --- a/conf/Makefile.common +++ b/conf/Makefile.common @@ -41,7 +41,7 @@ CFLAGS_KERNEL = $(CFLAGS_PLATFORM) -ffreestanding LDFLAGS_KERNEL = $(LDFLAGS_PLATFORM) -nostdlib $(TARGET_LDFLAGS_OLDMAGIC) CPPFLAGS_KERNEL = $(CPPFLAGS_CPU) $(CPPFLAGS_PLATFORM) -DGRUB_KERNEL=1 CCASFLAGS_KERNEL = $(CCASFLAGS_CPU) $(CCASFLAGS_PLATFORM) -STRIPFLAGS_KERNEL = -R .rel.dyn -R .reginfo -R .note -R .comment -R .drectve -R .note.gnu.gold-version -R .MIPS.abiflags -R .ARM.exidx +STRIPFLAGS_KERNEL = -R .eh_frame -R .rel.dyn -R .reginfo -R .note -R .comment -R .drectve -R .note.gnu.gold-version -R .MIPS.abiflags -R .ARM.exidx -R .note.gnu.property -R .gnu.build.attributes if !COND_emu if COND_HAVE_ASM_USCORE LDFLAGS_KERNEL += -Wl,--defsym=_malloc=_grub_malloc -Wl,--defsym=_free=_grub_free @@ -50,10 +50,10 @@ else endif endif -CFLAGS_MODULE = $(CFLAGS_PLATFORM) -ffreestanding -LDFLAGS_MODULE = $(LDFLAGS_PLATFORM) -nostdlib $(TARGET_LDFLAGS_OLDMAGIC) -Wl,-r -CPPFLAGS_MODULE = $(CPPFLAGS_CPU) $(CPPFLAGS_PLATFORM) -CCASFLAGS_MODULE = $(CCASFLAGS_CPU) $(CCASFLAGS_PLATFORM) +CFLAGS_MODULE = $(TARGET_CFLAGS) $(CFLAGS_PLATFORM) -ffreestanding +LDFLAGS_MODULE = $(TARGET_LDFLAGS) $(LDFLAGS_PLATFORM) -nostdlib $(TARGET_LDFLAGS_OLDMAGIC) -Wl,-r +CPPFLAGS_MODULE = $(TARGET_CPPFLAGS) $(CPPFLAGS_DEFAULT) $(CPPFLAGS_CPU) $(CPPFLAGS_PLATFORM) +CCASFLAGS_MODULE = $(TARGET_CCASFLAGS) $(CCASFLAGS_DEFAULT) $(CCASFLAGS_CPU) $(CCASFLAGS_PLATFORM) CFLAGS_IMAGE = $(CFLAGS_PLATFORM) -fno-builtin LDFLAGS_IMAGE = $(LDFLAGS_PLATFORM) -nostdlib $(TARGET_LDFLAGS_OLDMAGIC) -Wl,-S @@ -72,10 +72,13 @@ CCASFLAGS_LIBRARY = # Other variables grubconfdir = $(sysconfdir)/grub.d +grublibexecdir = $(libexecdir)/$(grubdirname) platformdir = $(pkglibdir)/$(target_cpu)-$(platform) starfielddir = $(pkgdatadir)/themes/starfield +systemdunitdir = ${prefix}/lib/systemd/system +systemd_logind_service_ddir = $(systemdunitdir)/systemd-logind.service.d -CFLAGS_GNULIB = -Wno-undef -Wno-sign-compare -Wno-unused -Wno-unused-parameter -Wno-redundant-decls -Wno-unreachable-code -Wno-conversion -Wno-error=attributes +CFLAGS_GNULIB = -Wno-undef -Wno-unused -Wno-unused-parameter -Wno-redundant-decls -Wno-unreachable-code -Wno-error=attributes -Werror=trampolines -fno-trampolines CPPFLAGS_GNULIB = -I$(top_builddir)/grub-core/lib/gnulib -I$(top_srcdir)/grub-core/lib/gnulib CFLAGS_POSIX = -fno-builtin @@ -133,6 +136,9 @@ platform_SCRIPTS = platform_PROGRAMS = sbin_SCRIPTS = sbin_PROGRAMS = +grublibexec_SCRIPTS = +systemdunit_SCRIPTS = +systemd_logind_service_d_SCRIPTS = EXTRA_DIST = CLEANFILES = diff --git a/conf/Makefile.extra-dist b/conf/Makefile.extra-dist index d9e2b8cc7..f35f69d68 100644 --- a/conf/Makefile.extra-dist +++ b/conf/Makefile.extra-dist @@ -15,6 +15,9 @@ EXTRA_DIST += docs/man EXTRA_DIST += docs/autoiso.cfg EXTRA_DIST += docs/grub.cfg EXTRA_DIST += docs/osdetect.cfg +EXTRA_DIST += docs/org.gnu.grub.policy +EXTRA_DIST += docs/grub-boot-success.service +EXTRA_DIST += docs/grub-boot-success.timer EXTRA_DIST += conf/i386-cygwin-img-ld.sc diff --git a/configure.ac b/configure.ac index ad1e7bea5..170655df5 100644 --- a/configure.ac +++ b/configure.ac @@ -70,6 +70,7 @@ grub_TRANSFORM([grub-install]) grub_TRANSFORM([grub-mkconfig]) grub_TRANSFORM([grub-mkfont]) grub_TRANSFORM([grub-mkimage]) +grub_TRANSFORM([grub-get-kernel-settings]) grub_TRANSFORM([grub-glue-efi]) grub_TRANSFORM([grub-mklayout]) grub_TRANSFORM([grub-mkpasswd-pbkdf2]) @@ -78,6 +79,7 @@ grub_TRANSFORM([grub-mkrescue]) grub_TRANSFORM([grub-probe]) grub_TRANSFORM([grub-protect]) grub_TRANSFORM([grub-reboot]) +grub_TRANSFORM([grub-set-password]) grub_TRANSFORM([grub-script-check]) grub_TRANSFORM([grub-set-default]) grub_TRANSFORM([grub-sparc64-setup]) @@ -320,6 +322,14 @@ AC_SUBST(grubdirname) AC_DEFINE_UNQUOTED(GRUB_DIR_NAME, "$grubdirname", [Default grub directory name]) +PKG_PROG_PKG_CONFIG +AS_IF([$($PKG_CONFIG --exists bash-completion)], [ + bashcompletiondir=$($PKG_CONFIG --variable=completionsdir bash-completion) +] , [ + bashcompletiondir=${datadir}/bash-completion/completions +]) +AC_SUBST(bashcompletiondir) + # # Checks for build programs. # @@ -535,6 +545,9 @@ HOST_CFLAGS="$HOST_CFLAGS $grub_cv_cc_w_extra_flags" # Check for target programs. # +# This makes sure pkg.m4 is available. +m4_pattern_forbid([^_?PKG_[A-Z_]+$],[*** pkg.m4 missing, please install pkg-config]) + # Find tools for the target. if test "x$target_alias" != x && test "x$host_alias" != "x$target_alias"; then tmp_ac_tool_prefix="$ac_tool_prefix" @@ -861,6 +874,18 @@ if ( test "x$target_cpu" = xi386 || test "x$target_cpu" = xx86_64 ) && test "x$p TARGET_CFLAGS="$TARGET_CFLAGS -mno-mmx -mno-sse -mno-sse2 -mno-sse3 -mno-3dnow" fi +# Should grub utils get the host CFLAGS, or the target CFLAGS? +AC_ARG_WITH([utils], + AS_HELP_STRING([--with-utils=host|target|build], + [choose which flags to build utilities with. (default=target)]), + [have_with_utils=y], + [have_with_utils=n]) +if test x"$have_with_utils" = xy ; then + with_utils="$withval" +else + with_utils=target +fi + if ( test "x$target_cpu" = xi386 || test "x$target_cpu" = xx86_64 ); then AC_CACHE_CHECK([whether -Wa,-mx86-used-note works], [grub_cv_cc_mx86_used_note], [ CFLAGS="$TARGET_CFLAGS -Wa,-mx86-used-note=no -Werror" @@ -917,7 +942,7 @@ fi # that floats are a good fit to run instead of what's written in the code. # Given that floating point unit is disabled (if present to begin with) # when GRUB is running which may result in various hard crashes. -if test x"$platform" != xemu ; then +if test x"$platform" != xemu -a x"$with_utils" == xtarget ; then AC_CACHE_CHECK([for options to get soft-float], grub_cv_target_cc_soft_float, [ grub_cv_target_cc_soft_float=no if test "x$target_cpu" = xarm64; then @@ -1522,7 +1547,7 @@ CPPFLAGS="$TARGET_CPPFLAGS" # Check for libgcc symbols if test x"$platform" = xemu; then -CFLAGS="$TARGET_CFLAGS -Wno-error" +CFLAGS="$TARGET_CFLAGS" AC_CHECK_FUNCS(__udivsi3 __umodsi3 __divsi3 __modsi3 __divdi3 __moddi3 __udivdi3 __umoddi3 __ctzdi2 __ctzsi2 __clzdi2 __aeabi_uidiv __aeabi_uidivmod __aeabi_idiv __aeabi_idivmod __aeabi_ulcmp __muldi3 __aeabi_lmul __aeabi_memcpy __aeabi_memcpy4 __aeabi_memcpy8 __aeabi_memclr __aeabi_memclr4 __aeabi_memclr8 __aeabi_memset __aeabi_lasr __aeabi_llsl __aeabi_llsr _restgpr_14_x __ucmpdi2 __ashldi3 __ashrdi3 __lshrdi3 __bswapsi2 __bswapdi2 __bzero __register_frame_info __deregister_frame_info ___chkstk_ms __chkstk_ms) fi @@ -1539,7 +1564,15 @@ grub_PROG_TARGET_CC if test "x$TARGET_APPLE_LINKER" != x1 ; then grub_PROG_OBJCOPY_ABSOLUTE fi + +AC_ARG_ENABLE([build-id], + [AS_HELP_STRING([--enable-build-id], + [ask the linker to supply build-id notes (default=no)])]) +if test x$enable_build_id = xyes; then +grub_PROG_LD_BUILD_ID_SHA1 +else grub_PROG_LD_BUILD_ID_NONE +fi if test "x$target_cpu" = xi386; then if test "$platform" != emu && test "x$TARGET_APPLE_LINKER" != x1 ; then if test ! -z "$TARGET_IMG_LDSCRIPT"; then @@ -2116,6 +2149,17 @@ if test x"$enable_werror" != xno ; then fi fi +AC_ARG_ENABLE([wextra], + [AS_HELP_STRING([--disable-wextra], + [do not use -Wextra when building GRUB])]) +if test x"$enable_wextra" != xno ; then + TARGET_CFLAGS="$TARGET_CFLAGS -Wextra" + HOST_CFLAGS="$HOST_CFLAGS -Wextra" +fi + +TARGET_CFLAGS="$TARGET_CFLAGS -Werror=trampolines -fno-trampolines" +HOST_CFLAGS="$HOST_CFLAGS -Werror=trampolines -fno-trampolines" + TARGET_CPP="$TARGET_CC -E" TARGET_CCAS=$TARGET_CC @@ -2125,6 +2169,41 @@ HOST_CPPFLAGS="$HOST_CPPFLAGS -I\$(top_builddir)/include" TARGET_CPPFLAGS="$TARGET_CPPFLAGS -I\$(top_srcdir)/include" TARGET_CPPFLAGS="$TARGET_CPPFLAGS -I\$(top_builddir)/include" +case "$with_utils" in + host) + UTILS_CFLAGS=$HOST_CFLAGS + UTILS_CPPFLAGS=$HOST_CPPFLAGS + UTILS_CCASFLAGS=$HOST_CCASFLAGS + UTILS_LDFLAGS=$HOST_LDFLAGS + ;; + target) + UTILS_CFLAGS=$TARGET_CFLAGS + UTILS_CPPFLAGS=$TARGET_CPPFLAGS + UTILS_CCASFLAGS=$TARGET_CCASFLAGS + UTILS_LDFLAGS=$TARGET_LDFLAGS + ;; + build) + UTILS_CFLAGS=$BUILD_CFLAGS + UTILS_CPPFLAGS=$BUILD_CPPFLAGS + UTILS_CCASFLAGS=$BUILD_CCASFLAGS + UTILS_LDFLAGS=$BUILD_LDFLAGS + ;; + *) + AC_MSG_ERROR([--with-utils must be either host, target, or build]) + ;; +esac +AC_MSG_NOTICE([Using $with_utils flags for utilities.]) + +unset CFLAGS +unset CPPFLAGS +unset CCASFLAGS +unset LDFLAGS + +AC_SUBST(UTILS_CFLAGS) +AC_SUBST(UTILS_CPPFLAGS) +AC_SUBST(UTILS_CCASFLAGS) +AC_SUBST(UTILS_LDFLAGS) + GRUB_TARGET_CPU="${target_cpu}" GRUB_PLATFORM="${platform}" diff --git a/docs/grub-boot-indeterminate.service b/docs/grub-boot-indeterminate.service new file mode 100644 index 000000000..6c8dcb186 --- /dev/null +++ b/docs/grub-boot-indeterminate.service @@ -0,0 +1,11 @@ +[Unit] +Description=Mark boot as indeterminate +DefaultDependencies=false +Requires=sysinit.target +After=sysinit.target +Wants=system-update-pre.target +Before=system-update-pre.target + +[Service] +Type=oneshot +ExecStart=/usr/bin/grub2-editenv - incr boot_indeterminate diff --git a/docs/grub-boot-success.service b/docs/grub-boot-success.service new file mode 100644 index 000000000..80e79584c --- /dev/null +++ b/docs/grub-boot-success.service @@ -0,0 +1,6 @@ +[Unit] +Description=Mark boot as successful + +[Service] +Type=oneshot +ExecStart=/usr/sbin/grub2-set-bootflag boot_success diff --git a/docs/grub-boot-success.timer b/docs/grub-boot-success.timer new file mode 100644 index 000000000..406f17200 --- /dev/null +++ b/docs/grub-boot-success.timer @@ -0,0 +1,7 @@ +[Unit] +Description=Mark boot as successful after the user session has run 2 minutes +ConditionUser=!@system +ConditionVirtualization=!container + +[Timer] +OnActiveSec=2min diff --git a/docs/grub.texi b/docs/grub.texi index a2ae57cea..d9389ebb0 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -9596,6 +9596,13 @@ grub-install @var{install_device} The device name @var{install_device} is an OS device name or a GRUB device name. +In order to support UEFI Secure Boot, the resulting GRUB EFI binary must +be signed by a recognized private key. For this reason, for EFI +platforms, most distributions also ship prebuilt GRUB EFI binaries +signed by a distribution-specific private key. In this case, however, +@command{grub2-install} should not be used because it would overwrite +the signed EFI binary. + @command{grub-install} accepts the following options: @table @option diff --git a/docs/man/grub-get-kernel-settings.h2m b/docs/man/grub-get-kernel-settings.h2m new file mode 100644 index 000000000..b8051f01f --- /dev/null +++ b/docs/man/grub-get-kernel-settings.h2m @@ -0,0 +1,2 @@ +[NAME] +grub-get-kernel-settings \- Evaluate the system's kernel installation settings for use while making a grub configuration file diff --git a/docs/man/grub-set-bootflag.h2m b/docs/man/grub-set-bootflag.h2m new file mode 100644 index 000000000..94ec0b92e --- /dev/null +++ b/docs/man/grub-set-bootflag.h2m @@ -0,0 +1,2 @@ +[NAME] +grub-set-bootflag \- set a bootflag in the GRUB environment block diff --git a/docs/man/grub-set-password.h2m b/docs/man/grub-set-password.h2m new file mode 100644 index 000000000..10ee82f4d --- /dev/null +++ b/docs/man/grub-set-password.h2m @@ -0,0 +1,2 @@ +[NAME] +grub-set-password \- generate the user.cfg file containing the hashed grub bootloader password diff --git a/gentpl.py b/gentpl.py old mode 100644 new mode 100755 index d8c6965d8..7ce9f2eb5 --- a/gentpl.py +++ b/gentpl.py @@ -52,6 +52,7 @@ GROUPS["riscv64"] = [ "riscv64_efi" ] # Groups based on firmware +GROUPS["pc"] = [ "i386_pc" ] GROUPS["efi"] = [ "i386_efi", "x86_64_efi", "ia64_efi", "arm_efi", "arm64_efi", "loongarch64_efi", "riscv32_efi", "riscv64_efi" ] GROUPS["ieee1275"] = [ "i386_ieee1275", "sparc64_ieee1275", "powerpc_ieee1275" ] @@ -594,11 +595,21 @@ def platform_conditional(platform, closure): # }; # def foreach_enabled_platform(defn, closure): + enabled = False + disabled = False if 'enable' in defn: + enabled = True for platform in GRUB_PLATFORMS: if platform_tagged(defn, platform, "enable"): platform_conditional(platform, closure) - else: + + if 'disable' in defn: + disabled = True + for platform in GRUB_PLATFORMS: + if not platform_tagged(defn, platform, "disable"): + platform_conditional(platform, closure) + + if not enabled and not disabled: for platform in GRUB_PLATFORMS: platform_conditional(platform, closure) @@ -660,6 +671,8 @@ def first_time(defn, snippet): def is_platform_independent(defn): if 'enable' in defn: return False + if 'disable' in defn: + return False for suffix in [ "", "_head", "_nodist" ]: template = platform_values(defn, GRUB_PLATFORMS[0], suffix) for platform in GRUB_PLATFORMS[1:]: @@ -689,10 +702,10 @@ def module(defn, platform): var_set(cname(defn) + "_SOURCES", platform_sources(defn, platform) + " ## platform sources") var_set("nodist_" + cname(defn) + "_SOURCES", platform_nodist_sources(defn, platform) + " ## platform nodist sources") var_set(cname(defn) + "_LDADD", platform_ldadd(defn, platform)) - var_set(cname(defn) + "_CFLAGS", "$(AM_CFLAGS) $(CFLAGS_MODULE) " + platform_cflags(defn, platform)) - var_set(cname(defn) + "_LDFLAGS", "$(AM_LDFLAGS) $(LDFLAGS_MODULE) " + platform_ldflags(defn, platform)) - var_set(cname(defn) + "_CPPFLAGS", "$(AM_CPPFLAGS) $(CPPFLAGS_MODULE) " + platform_cppflags(defn, platform)) - var_set(cname(defn) + "_CCASFLAGS", "$(AM_CCASFLAGS) $(CCASFLAGS_MODULE) " + platform_ccasflags(defn, platform)) + var_set(cname(defn) + "_CFLAGS", "$(CFLAGS_MODULE) " + platform_cflags(defn, platform)) + var_set(cname(defn) + "_LDFLAGS", "$(LDFLAGS_MODULE) " + platform_ldflags(defn, platform)) + var_set(cname(defn) + "_CPPFLAGS", "$(CPPFLAGS_MODULE) " + platform_cppflags(defn, platform)) + var_set(cname(defn) + "_CCASFLAGS", "$(CCASFLAGS_MODULE) " + platform_ccasflags(defn, platform)) var_set(cname(defn) + "_DEPENDENCIES", "$(TARGET_OBJ2ELF) " + platform_dependencies(defn, platform)) gvar_add("dist_noinst_DATA", extra_dist(defn)) diff --git a/gitignore b/gitignore new file mode 100644 index 000000000..5de5f5f30 --- /dev/null +++ b/gitignore @@ -0,0 +1,275 @@ +# +# Ignore patterns in this directory and all subdirectories. +# +*.1 +*.8 +*.a +*.exec +*.exec.exe +*.image +*.image.exe +*.img +*.log +*.lst +*.marker +*.mod +*.o +*.pf2 +*.pp +*.trs +*~ +.deps-core/ +.deps-util/ +.deps/ +.dirstamp +DISTLIST +GPATH +GRTAGS +GSYMS +GTAGS +Makefile +Makefile.in +ascii.bitmaps +genkernsyms.sh +grub-bin2h +grub-emu +grub-emu-lite +grub-emu-lite.exe +grub-emu.exe +grub-macho2img +grub_emu_init.c +grub_emu_init.h +grub_probe_init.c +grub_probe_init.h +grub_script.tab.c +grub_script.tab.h +grub_script.yy.c +grub_script.yy.h +grub_script_check_init.c +grub_script_check_init.h +grub_setup_init.c +grub_setup_init.h +mdate-sh +mod-*.c +update-grub_lib +widthspec.bin + +# +# Ignore patterns relative to this .gitignore file's directory. +# +/00_header +/10_* +/20_linux_xen +/30_os-prober +/30_uefi-firmware +/40_custom +/41_custom +/ABOUT-NLS +/ChangeLog +/INSTALL.grub +/Makefile.util.am +/Makefile.utilgcry.def +/aclocal.m4 +/ahci_test +/ascii.h +/autom4te.cache/ +/btrfs_test +/build-aux/ +/build-grub-gen-asciih +/build-grub-gen-widthspec +/build-grub-mkfont +/cdboot_test +/cmp_test +/compile +/config-util.h +/config-util.h.in +/config.cache +/config.guess +/config.h +/config.log +/config.status +/config.sub +/configure +/contrib +/core_compress_test +/cpio_test +/date_test +/depcomp +/docs/*.info +/docs/*.info-[0-9]* +/docs/stamp-1 +/docs/stamp-vti +/docs/version.texi +/ehci_test +/example_grub_script_test +/example_scripted_test +/example_unit_test +/exfat_test +/ext234_test +/f2fs_test +/fat_test +/fddboot_test +/file_filter_test +/garbage-gen +/garbage-gen.exe +/gettext_strings_test +/gnulib/ +/grub-2.[0-9]*/ +/grub-2.[0-9]*.tar.gz +/grub-bios-setup +/grub-bios-setup.exe +/grub-core/*.module +/grub-core/*.module.exe +/grub-core/*.pp +/grub-core/Makefile.core.am +/grub-core/Makefile.gcry.def +/grub-core/bootinfo.txt +/grub-core/build-grub-module-verifier +/grub-core/build-grub-pe2elf.exe +/grub-core/contrib +/grub-core/gdb_grub +/grub-core/genmod.sh +/grub-core/gensyminfo.sh +/grub-core/gentrigtables +/grub-core/gentrigtables.exe +/grub-core/gmodule.pl +/grub-core/grub.chrp +/grub-core/kernel.img.bin +/grub-core/lib/gnulib +/grub-core/lib/libgcrypt-grub +/grub-core/modinfo.sh +/grub-core/rs_decoder.h +/grub-core/symlist.c +/grub-core/symlist.h +/grub-core/trigtables.c +/grub-core/unidata.c +/grub-editenv +/grub-editenv.exe +/grub-file +/grub-file.exe +/grub-fs-tester +/grub-fstest +/grub-fstest.exe +/grub-glue-efi +/grub-glue-efi.exe +/grub-install +/grub-install.exe +/grub-kbdcomp +/grub-macbless +/grub-macbless.exe +/grub-menulst2cfg +/grub-menulst2cfg.exe +/grub-mk* +/grub-mount +/grub-ofpathname +/grub-ofpathname.exe +/grub-probe +/grub-probe.exe +/grub-reboot +/grub-render-label +/grub-render-label.exe +/grub-script-check +/grub-script-check.exe +/grub-set-default +/grub-shell +/grub-shell-tester +/grub-sparc64-setup +/grub-sparc64-setup.exe +/grub-syslinux2cfg +/grub-syslinux2cfg.exe +/grub_cmd_date +/grub_cmd_echo +/grub_cmd_regexp +/grub_cmd_set_date +/grub_cmd_sleep +/grub_cmd_test +/grub_cmd_tr +/grub_fstest_init.c +/grub_fstest_init.h +/grub_func_test +/grub_script_blanklines +/grub_script_blockarg +/grub_script_break +/grub_script_comments +/grub_script_continue +/grub_script_dollar +/grub_script_echo1 +/grub_script_echo_keywords +/grub_script_escape_comma +/grub_script_eval +/grub_script_expansion +/grub_script_final_semicolon +/grub_script_for1 +/grub_script_functions +/grub_script_gettext +/grub_script_if +/grub_script_leading_whitespace +/grub_script_no_commands +/grub_script_not +/grub_script_return +/grub_script_setparams +/grub_script_shift +/grub_script_strcmp +/grub_script_test +/grub_script_vars1 +/grub_script_while1 +/gzcompress_test +/hddboot_test +/help_test +/hfs_test +/hfsplus_test +/include/grub/cpu +/include/grub/gcrypt/g10lib.h +/include/grub/gcrypt/gcrypt.h +/include/grub/machine +/install-sh +/iso9660_test +/jfs_test +/lib/libgcrypt-grub +/libgrub_a_init.c +/lzocompress_test +/m4/ +/minixfs_test +/missing +/netboot_test +/nilfs2_test +/ntfs_test +/ohci_test +/partmap_test +/pata_test +/po/*.gmo +/po/*.mo +/po/*.po +/po/LINGUAS +/po/Makefile.in.in +/po/Makevars +/po/Makevars.template +/po/POTFILES +/po/POTFILES-shell.in +/po/POTFILES.in +/po/Rules-quot +/po/grub.pot +/po/remove-potcdate.sed +/po/stamp-po +/printf_test +/priority_queue_unit_test +/pseries_test +/reiserfs_test +/romfs_test +/squashfs_test +/stamp-h +/stamp-h.in +/stamp-h1 +/syslinux_test +/tar_test +/test_sha512sum +/test_unset +/tests/syslinux/ubuntu10.04_grub.cfg +/texinfo.tex +/udf_test +/uhci_test +/util/bash-completion.d/grub +/widthspec.h +/xfs_test +/xzcompress_test +/zfs_test diff --git a/gnulib-9f48fb992a3d7e96610c4ce8be969cff2d61a01b.tar.gz b/gnulib-9f48fb992a3d7e96610c4ce8be969cff2d61a01b.tar.gz new file mode 100644 index 000000000..1a0cf7dba Binary files /dev/null and b/gnulib-9f48fb992a3d7e96610c4ce8be969cff2d61a01b.tar.gz differ diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index ac48cd8aa..a7fa07fa7 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -199,6 +199,9 @@ kernel = { softdiv = lib/division.c; + x86 = lib/i386/backtrace.c; + x86 = lib/backtrace.c; + i386 = kern/i386/dl.c; i386_xen = kern/i386/dl.c; i386_xen_pvh = kern/i386/dl.c; @@ -219,6 +222,7 @@ kernel = { efi = kern/efi/acpi.c; efi = kern/efi/sb.c; efi = kern/lockdown.c; + efi = lib/envblk.c; i386_coreboot = kern/i386/pc/acpi.c; i386_multiboot = kern/i386/pc/acpi.c; i386_coreboot = kern/acpi.c; @@ -409,6 +413,11 @@ kernel = { extra_dist = kern/mips/cache_flush.S; }; +module = { + name = increment; + common = commands/increment.c; +}; + program = { name = grub-emu; mansection = 1; @@ -828,6 +837,12 @@ module = { enable = efi; }; +module = { + name = efienv; + common = commands/efi/env.c; + enable = efi; +}; + module = { name = efifwsetup; efi = commands/efi/efifwsetup.c; @@ -1888,6 +1903,7 @@ module = { x86 = loader/slaunch/skl.c; x86 = loader/slaunch/dlstub.c; x86 = loader/efi/dltrampoline.S; + i386_efi = loader/slaunch/x86_efi_linux.c; x86_64_efi = loader/slaunch/x86_efi_linux.c; enable = x86; }; diff --git a/grub-core/commands/efi/env.c b/grub-core/commands/efi/env.c new file mode 100644 index 000000000..4080f24fd --- /dev/null +++ b/grub-core/commands/efi/env.c @@ -0,0 +1,170 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2012 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 . + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static const grub_guid_t grub_env_guid = GRUB_EFI_GRUB_VARIABLE_GUID; + +static grub_err_t +grub_efi_export_env(grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + const char *value; + char *old_value; + struct grub_envblk envblk_s = { NULL, 0 }; + grub_envblk_t envblk = &envblk_s; + grub_err_t err; + int changed = 1; + grub_efi_status_t status; + + grub_dprintf ("efienv", "argc:%d\n", argc); + for (int i = 0; i < argc; i++) + grub_dprintf ("efienv", "argv[%d]: %s\n", i, argv[i]); + + if (argc != 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("variable name expected")); + + grub_efi_get_variable ("GRUB_ENV", &grub_env_guid, &envblk_s.size, + (void **) &envblk_s.buf); + if (!envblk_s.buf || envblk_s.size < 1) + { + char *buf = grub_malloc (1025); + if (!buf) + return grub_errno; + + grub_memcpy (buf, GRUB_ENVBLK_SIGNATURE, sizeof (GRUB_ENVBLK_SIGNATURE) - 1); + grub_memset (buf + sizeof (GRUB_ENVBLK_SIGNATURE) - 1, '#', + DEFAULT_ENVBLK_SIZE - sizeof (GRUB_ENVBLK_SIGNATURE) + 1); + buf[1024] = '\0'; + + envblk_s.buf = buf; + envblk_s.size = 1024; + } + else + { + char *buf = grub_realloc (envblk_s.buf, envblk_s.size + 1); + if (!buf) + return grub_errno; + + envblk_s.buf = buf; + envblk_s.buf[envblk_s.size] = '\0'; + } + + err = grub_envblk_get(envblk, argv[0], &old_value); + if (err != GRUB_ERR_NONE) + { + grub_dprintf ("efienv", "grub_envblk_get returned %d\n", err); + return err; + } + + value = grub_env_get(argv[0]); + if ((!value && !old_value) || + (value && old_value && !grub_strcmp(old_value, value))) + changed = 0; + + if (old_value) + grub_free(old_value); + + if (changed == 0) + { + grub_dprintf ("efienv", "No changes necessary\n"); + return 0; + } + + if (value) + { + grub_dprintf ("efienv", "setting \"%s\" to \"%s\"\n", argv[0], value); + grub_envblk_set(envblk, argv[0], value); + } + else + { + grub_dprintf ("efienv", "deleting \"%s\" from envblk\n", argv[0]); + grub_envblk_delete(envblk, argv[0]); + } + + grub_dprintf ("efienv", "envblk is %lu bytes:\n\"%s\"\n", envblk_s.size, envblk_s.buf); + + grub_dprintf ("efienv", "removing GRUB_ENV\n"); + status = grub_efi_set_variable ("GRUB_ENV", &grub_env_guid, NULL, 0); + if (status != GRUB_EFI_SUCCESS) + grub_dprintf ("efienv", "removal returned %ld\n", status); + + grub_dprintf ("efienv", "setting GRUB_ENV\n"); + status = grub_efi_set_variable ("GRUB_ENV", &grub_env_guid, + envblk_s.buf, envblk_s.size); + if (status != GRUB_EFI_SUCCESS) + grub_dprintf ("efienv", "setting GRUB_ENV returned %ld\n", status); + + return 0; +} + +static int +set_var (const char *name, const char *value, + void *whitelist __attribute__((__unused__))) +{ + grub_env_set (name, value); + return 0; +} + +static grub_err_t +grub_efi_load_env(grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[] __attribute__((__unused__))) +{ + struct grub_envblk envblk_s = { NULL, 0 }; + grub_envblk_t envblk = &envblk_s; + + grub_efi_get_variable ("GRUB_ENV", &grub_env_guid, &envblk_s.size, + (void **) &envblk_s.buf); + if (!envblk_s.buf || envblk_s.size < 1) + return 0; + + if (argc > 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unexpected argument")); + + grub_envblk_iterate (envblk, NULL, set_var); + grub_free (envblk_s.buf); + + return GRUB_ERR_NONE; +} + +static grub_command_t export_cmd, loadenv_cmd; + +GRUB_MOD_INIT(lsefi) +{ + export_cmd = grub_register_command ("efi-export-env", grub_efi_export_env, + N_("VARIABLE_NAME"), N_("Export environment variable to UEFI.")); + loadenv_cmd = grub_register_command ("efi-load-env", grub_efi_load_env, + NULL, N_("Load the grub environment from UEFI.")); +} + +GRUB_MOD_FINI(lsefi) +{ + grub_unregister_command (export_cmd); + grub_unregister_command (loadenv_cmd); +} diff --git a/grub-core/commands/increment.c b/grub-core/commands/increment.c new file mode 100644 index 000000000..79cf13765 --- /dev/null +++ b/grub-core/commands/increment.c @@ -0,0 +1,105 @@ +/* increment.c - Commands to increment and decrement variables. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007,2008 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +typedef enum { + INCREMENT, + DECREMENT, +} operation; + +static grub_err_t +incr_decr(operation op, int argc, char **args) +{ + const char *old; + char *new; + long value; + + if (argc < 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_ ("no variable specified")); + if (argc > 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_ ("too many arguments")); + + old = grub_env_get (*args); + if (!old) + return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("No such variable \"%s\""), + *args); + + value = grub_strtol (old, NULL, 0); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; + + switch (op) + { + case INCREMENT: + value += 1; + break; + case DECREMENT: + value -= 1; + break; + } + + new = grub_xasprintf ("%ld", value); + if (!new) + return grub_errno; + + grub_env_set (*args, new); + grub_free (new); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_incr(struct grub_command *cmd UNUSED, + int argc, char **args) +{ + return incr_decr(INCREMENT, argc, args); +} + +static grub_err_t +grub_cmd_decr(struct grub_command *cmd UNUSED, + int argc, char **args) +{ + return incr_decr(DECREMENT, argc, args); +} + +static grub_command_t cmd_incr, cmd_decr; + +GRUB_MOD_INIT(increment) +{ + cmd_incr = grub_register_command ("increment", grub_cmd_incr, N_("VARIABLE"), + N_("increment VARIABLE")); + cmd_decr = grub_register_command ("decrement", grub_cmd_decr, N_("VARIABLE"), + N_("decrement VARIABLE")); +} + +GRUB_MOD_FINI(increment) +{ + grub_unregister_command (cmd_incr); + grub_unregister_command (cmd_decr); +} diff --git a/grub-core/commands/menuentry.c b/grub-core/commands/menuentry.c index 720e6d8ea..d7b04522a 100644 --- a/grub-core/commands/menuentry.c +++ b/grub-core/commands/menuentry.c @@ -29,7 +29,7 @@ static const struct grub_arg_option options[] = { {"class", 1, GRUB_ARG_OPTION_REPEATABLE, N_("Menu entry type."), N_("STRING"), ARG_TYPE_STRING}, - {"users", 2, 0, + {"users", 2, GRUB_ARG_OPTION_OPTIONAL, N_("List of users allowed to boot this entry."), N_("USERNAME[,USERNAME]"), ARG_TYPE_STRING}, {"hotkey", 3, 0, @@ -271,7 +271,7 @@ grub_cmd_menuentry (grub_extcmd_context_t ctxt, int argc, char **args) if (! ctxt->state[3].set && ! ctxt->script) return grub_error (GRUB_ERR_BAD_ARGUMENT, "no menuentry definition"); - if (ctxt->state[1].set) + if (ctxt->state[1].set && ctxt->state[1].arg) users = ctxt->state[1].arg; else if (ctxt->state[5].set) users = NULL; diff --git a/grub-core/commands/search.c b/grub-core/commands/search.c index 263f1501c..9dd937e6d 100644 --- a/grub-core/commands/search.c +++ b/grub-core/commands/search.c @@ -30,6 +30,8 @@ #include #include #include +#include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -54,6 +56,100 @@ struct search_ctx int is_cache; }; +static int +is_device_usb (const char *name) +{ + int ret = 0; + + grub_device_t dev = grub_device_open(name); + + if (dev) + { + struct grub_efidisk_data + { + grub_efi_handle_t handle; + grub_efi_device_path_t *device_path; + grub_efi_device_path_t *last_device_path; + grub_efi_block_io_t *block_io; + struct grub_efidisk_data *next; + }; + + if (dev->disk && dev->disk->data) + { + struct grub_efidisk_data *dp = dev->disk->data; + + if ( GRUB_EFI_DEVICE_PATH_TYPE (dp->last_device_path) == GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE && + GRUB_EFI_DEVICE_PATH_SUBTYPE (dp->last_device_path) == GRUB_EFI_USB_DEVICE_PATH_SUBTYPE) + { + ret = 1; + } + } + grub_device_close(dev); + } + + return ret; +} + +static int +get_device_uuid(const char *name, char** quid) +{ + int ret = 0; + + grub_device_t dev_part = grub_device_open(name); + + if (dev_part) + { + grub_fs_t fs; + + fs = grub_fs_probe (dev_part); + +#ifdef DO_SEARCH_FS_UUID +#define read_fn fs_uuid +#else +#define read_fn fs_label +#endif + if (fs && fs->read_fn) + { + fs->read_fn (dev_part, quid); + + if (grub_errno == GRUB_ERR_NONE && *quid) + { + ret = 1; + } + + } + grub_device_close (dev_part); + } + + return ret; +} +struct uuid_context { + char* name; + char* uuid; +}; + +static int +check_for_duplicate (const char *name, void *data) +{ + int ret = 0; + struct uuid_context * uuid_ctx = (struct uuid_context *)data; + char *quid = 0; + + get_device_uuid(name, &quid); + + if (quid == NULL) + return 0; + + if (!grub_strcasecmp(quid, uuid_ctx->uuid) && grub_strcasecmp(name, uuid_ctx->name)) + { + ret = 1; + } + + grub_free(quid); + + return ret; +} + /* Helper for FUNC_NAME. */ static int iterate_device (const char *name, void *data) @@ -86,6 +182,64 @@ iterate_device (const char *name, void *data) grub_device_close (dev); } + /* Skip it if it's not the root device when requested. */ + if (ctx->flags & SEARCH_FLAGS_ROOTDEV_ONLY) + { + const char *root_dev; + root_dev = grub_env_get ("root"); + if (root_dev != NULL && *root_dev != '\0') + { + char *root_disk = grub_malloc (grub_strlen(root_dev) + 1); + char *name_disk = grub_malloc (grub_strlen(name) + 1); + char *rem_1 = grub_malloc(grub_strlen(root_dev) + 1); + char *rem_2 = grub_malloc(grub_strlen(name) + 1); + + if (root_disk != NULL && name_disk != NULL && + rem_1 != NULL && rem_2 != NULL) + { + /* get just the disk name; partitions will be different. */ + grub_str_sep (root_dev, root_disk, ',', rem_1); + grub_str_sep (name, name_disk, ',', rem_2); + if (root_disk != NULL && *root_disk != '\0' && + name_disk != NULL && *name_disk != '\0') + { + grub_device_t dev, dev_part; + + if (is_device_usb(name) && !is_device_usb(root_dev)) + { + char *quid_name = NULL; + int longlist = 0; + struct uuid_context uuid_ctx; + int ret = 0; + + get_device_uuid(name, &quid_name); + if (!grub_strcmp(quid_name, ctx->key)) + { + uuid_ctx.name = name; + uuid_ctx.uuid = quid_name; + + ret = grub_device_iterate (check_for_duplicate, &uuid_ctx); + + if (ret) + { + grub_printf("Duplicated media UUID found, rebooting ...\n"); + grub_sleep(10); + grub_reboot(); + } + } + + if (quid_name) grub_free (quid_name); + + } + } + } + grub_free (root_disk); + grub_free (name_disk); + grub_free (rem_1); + grub_free (rem_2); + } + } + #ifdef DO_SEARCH_FS_UUID #define compare_fn grub_strcasecmp #else diff --git a/grub-core/commands/search_wrap.c b/grub-core/commands/search_wrap.c index 318581f3b..4fe6440c6 100644 --- a/grub-core/commands/search_wrap.c +++ b/grub-core/commands/search_wrap.c @@ -41,6 +41,7 @@ static const struct grub_arg_option options[] = ARG_TYPE_STRING}, {"no-floppy", 'n', 0, N_("Do not probe any floppy drive."), 0, 0}, {"efidisk-only", 0, 0, N_("Only probe EFI disks."), 0, 0}, + {"root-dev-only", 'r', 0, N_("Only probe root device."), 0, 0}, {"hint", 'h', GRUB_ARG_OPTION_REPEATABLE, N_("First try the device HINT. If HINT ends in comma, " "also try subpartitions"), N_("HINT"), ARG_TYPE_STRING}, @@ -75,6 +76,7 @@ enum options SEARCH_SET, SEARCH_NO_FLOPPY, SEARCH_EFIDISK_ONLY, + SEARCH_ROOTDEV_ONLY, SEARCH_HINT, SEARCH_HINT_IEEE1275, SEARCH_HINT_BIOS, @@ -189,6 +191,9 @@ grub_cmd_search (grub_extcmd_context_t ctxt, int argc, char **args) if (state[SEARCH_EFIDISK_ONLY].set) flags |= SEARCH_FLAGS_EFIDISK_ONLY; + if (state[SEARCH_ROOTDEV_ONLY].set) + flags |= SEARCH_FLAGS_ROOTDEV_ONLY; + if (state[SEARCH_LABEL].set) grub_search_label (id, var, flags, hints, nhints); else if (state[SEARCH_FS_UUID].set) diff --git a/grub-core/disk/ieee1275/ofdisk.c b/grub-core/disk/ieee1275/ofdisk.c index c6cba0c8a..c4f2971fd 100644 --- a/grub-core/disk/ieee1275/ofdisk.c +++ b/grub-core/disk/ieee1275/ofdisk.c @@ -24,6 +24,7 @@ #include #include #include +#include static char *last_devpath; static grub_ieee1275_ihandle_t last_ihandle; diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index ba0c58352..77157c642 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -38,6 +38,10 @@ #include #include #include +#include +#include +#include +#include #include #include #include @@ -79,9 +83,11 @@ struct grub_btrfs_superblock grub_uint64_t generation; grub_uint64_t root_tree; grub_uint64_t chunk_tree; - grub_uint8_t dummy2[0x20]; + grub_uint8_t dummy2[0x18]; + grub_uint64_t bytes_used; grub_uint64_t root_dir_objectid; - grub_uint8_t dummy3[0x41]; + grub_uint64_t num_devices; + grub_uint8_t dummy3[0x39]; struct grub_btrfs_device this_device; char label[0x100]; grub_uint8_t dummy4[0x100]; @@ -121,6 +127,7 @@ struct grub_btrfs_data grub_uint64_t exttree; grub_size_t extsize; struct grub_btrfs_extent_data *extent; + grub_uint64_t fs_tree; }; struct grub_btrfs_chunk_item @@ -191,6 +198,14 @@ struct grub_btrfs_leaf_descriptor } *data; }; +struct grub_btrfs_root_ref +{ + grub_uint64_t dirid; + grub_uint64_t sequence; + grub_uint16_t name_len; + const char name[0]; +} __attribute__ ((packed)); + struct grub_btrfs_time { grub_int64_t sec; @@ -203,7 +218,7 @@ struct grub_btrfs_inode grub_uint64_t size; grub_uint8_t dummy2[0x70]; struct grub_btrfs_time mtime; -} GRUB_PACKED; +} GRUB_PACKED __attribute__ ((aligned(8))); struct grub_btrfs_extent_data { @@ -236,6 +251,14 @@ struct grub_btrfs_extent_data #define GRUB_BTRFS_OBJECT_ID_CHUNK 0x100 +#define GRUB_BTRFS_ROOT_TREE_OBJECTID 1ULL +#define GRUB_BTRFS_FS_TREE_OBJECTID 5ULL +#define GRUB_BTRFS_ROOT_REF_KEY 156 +#define GRUB_BTRFS_ROOT_ITEM_KEY 132 + +static grub_uint64_t btrfs_default_subvolid = 0; +static char *btrfs_default_subvol = NULL; + static grub_disk_addr_t superblock_sectors[] = { 64 * 2, 64 * 1024 * 2, 256 * 1048576 * 2, 1048576ULL * 1048576ULL * 2 }; @@ -244,6 +267,12 @@ static grub_err_t grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr, void *buf, grub_size_t size, int recursion_depth); +static grub_err_t +get_root (struct grub_btrfs_data *data, struct grub_btrfs_key *key, + grub_uint64_t *tree, grub_uint8_t *type); + +grub_uint64_t +find_mtab_subvol_tree (const char *path, char **path_in_subvol); static grub_err_t read_sblock (grub_disk_t disk, struct grub_btrfs_superblock *sb) @@ -1252,11 +1281,115 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr, return GRUB_ERR_NONE; } +static grub_err_t +get_fs_root(struct grub_btrfs_data *data, grub_uint64_t tree, + grub_uint64_t objectid, grub_uint64_t offset, + grub_uint64_t *fs_root); + +static grub_err_t +lookup_root_by_id(struct grub_btrfs_data *data, grub_uint64_t id) +{ + grub_err_t err; + grub_uint64_t tree; + + err = get_fs_root(data, data->sblock.root_tree, id, -1, &tree); + if (!err) + data->fs_tree = tree; + return err; +} + +static grub_err_t +find_path (struct grub_btrfs_data *data, + const char *path, struct grub_btrfs_key *key, + grub_uint64_t *tree, grub_uint8_t *type); + +static grub_err_t +lookup_root_by_name(struct grub_btrfs_data *data, const char *path) +{ + grub_err_t err; + grub_uint64_t tree = 0; + grub_uint8_t type; + grub_uint64_t saved_tree; + struct grub_btrfs_key key; + + if (path[0] == '\0') + { + data->fs_tree = 0; + return GRUB_ERR_NONE; + } + + err = get_root (data, &key, &tree, &type); + if (err) + return err; + + saved_tree = data->fs_tree; + data->fs_tree = tree; + + err = find_path (data, path, &key, &tree, &type); + + data->fs_tree = saved_tree; + + if (err) + return grub_error(GRUB_ERR_FILE_NOT_FOUND, "couldn't locate %s\n", path); + + if (key.object_id != grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK) || tree == 0) + return grub_error(GRUB_ERR_BAD_FILE_TYPE, "%s: not a subvolume\n", path); + + data->fs_tree = tree; + return GRUB_ERR_NONE; +} + +static grub_err_t +lookup_root_by_name_fallback(struct grub_btrfs_data *data, const char *path) +{ + grub_err_t err; + grub_uint64_t tree = 0; + grub_uint8_t type; + struct grub_btrfs_key key; + + err = find_path (data, path, &key, &tree, &type); + if (err) + return grub_error(GRUB_ERR_FILE_NOT_FOUND, "couldn't locate %s\n", path); + + if (key.object_id != grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK) || tree == 0) + return grub_error(GRUB_ERR_BAD_FILE_TYPE, "%s: not a subvolume\n", path); + + data->fs_tree = tree; + return GRUB_ERR_NONE; +} + +static grub_err_t +btrfs_handle_subvol(struct grub_btrfs_data *data __attribute__ ((unused))) +{ + if (btrfs_default_subvol) + { + grub_err_t err; + err = lookup_root_by_name(data, btrfs_default_subvol); + + /* Fallback to old schemes */ + if (err == GRUB_ERR_FILE_NOT_FOUND) + { + err = GRUB_ERR_NONE; + return lookup_root_by_name_fallback(data, btrfs_default_subvol); + } + return err; + } + + if (btrfs_default_subvolid) + return lookup_root_by_id(data, btrfs_default_subvolid); + + data->fs_tree = 0; + + return GRUB_ERR_NONE; +} + + static struct grub_btrfs_data * grub_btrfs_mount (grub_device_t dev) { struct grub_btrfs_data *data; grub_err_t err; + const char *relpath = grub_env_get ("btrfs_relative_path"); if (!dev->disk) { @@ -1287,6 +1420,16 @@ grub_btrfs_mount (grub_device_t dev) data->devices_attached[0].dev = dev; data->devices_attached[0].id = data->sblock.this_device.device_id; + if (relpath && (relpath[0] == '1' || relpath[0] == 'y')) + { + err = btrfs_handle_subvol (data); + if (err) + { + grub_free (data); + return NULL; + } + } + return data; } @@ -1784,6 +1927,91 @@ get_root (struct grub_btrfs_data *data, struct grub_btrfs_key *key, return GRUB_ERR_NONE; } +static grub_err_t +find_pathname(struct grub_btrfs_data *data, grub_uint64_t objectid, + grub_uint64_t fs_root, const char *name, char **pathname) +{ + grub_err_t err; + struct grub_btrfs_key key = { + .object_id = objectid, + .type = GRUB_BTRFS_ITEM_TYPE_INODE_REF, + .offset = 0, + }; + struct grub_btrfs_key key_out; + struct grub_btrfs_leaf_descriptor desc; + char *p = grub_strdup (name); + grub_disk_addr_t elemaddr; + grub_size_t elemsize; + grub_size_t alloc = grub_strlen(name) + 1; + + err = lower_bound(data, &key, &key_out, fs_root, + &elemaddr, &elemsize, &desc, 0); + if (err) + return grub_error(err, "lower_bound caught %d\n", err); + + if (key_out.type != GRUB_BTRFS_ITEM_TYPE_INODE_REF) + next(data, &desc, &elemaddr, &elemsize, &key_out); + + if (key_out.type != GRUB_BTRFS_ITEM_TYPE_INODE_REF) + { + return grub_error(GRUB_ERR_FILE_NOT_FOUND, + "Can't find inode ref for {%"PRIuGRUB_UINT64_T + ", %u, %"PRIuGRUB_UINT64_T"} %"PRIuGRUB_UINT64_T + "/%"PRIuGRUB_SIZE"\n", + key_out.object_id, key_out.type, + key_out.offset, elemaddr, elemsize); + } + + + while (key_out.type == GRUB_BTRFS_ITEM_TYPE_INODE_REF && + key_out.object_id != key_out.offset) { + struct grub_btrfs_inode_ref *inode_ref; + char *new; + + inode_ref = grub_malloc(elemsize + 1); + if (!inode_ref) + return grub_error(GRUB_ERR_OUT_OF_MEMORY, + "couldn't allocate memory for inode_ref (%"PRIuGRUB_SIZE")\n", elemsize); + + err = grub_btrfs_read_logical(data, elemaddr, inode_ref, elemsize, 0); + if (err) + return grub_error(err, "read_logical caught %d\n", err); + + alloc += grub_le_to_cpu16 (inode_ref->n) + 2; + new = grub_malloc(alloc); + if (!new) + return grub_error(GRUB_ERR_OUT_OF_MEMORY, + "couldn't allocate memory for name (%"PRIuGRUB_SIZE")\n", alloc); + + grub_memcpy(new, inode_ref->name, grub_le_to_cpu16 (inode_ref->n)); + if (p) + { + new[grub_le_to_cpu16 (inode_ref->n)] = '/'; + grub_strcpy (new + grub_le_to_cpu16 (inode_ref->n) + 1, p); + grub_free(p); + } + else + new[grub_le_to_cpu16 (inode_ref->n)] = 0; + grub_free(inode_ref); + + p = new; + + key.object_id = key_out.offset; + + err = lower_bound(data, &key, &key_out, fs_root, &elemaddr, + &elemsize, &desc, 0); + if (err) + return grub_error(err, "lower_bound caught %d\n", err); + + if (key_out.type != GRUB_BTRFS_ITEM_TYPE_INODE_REF) + next(data, &desc, &elemaddr, &elemsize, &key_out); + + } + + *pathname = p; + return 0; +} + static grub_err_t find_path (struct grub_btrfs_data *data, const char *path, struct grub_btrfs_key *key, @@ -1796,31 +2024,66 @@ find_path (struct grub_btrfs_data *data, grub_size_t allocated = 0; struct grub_btrfs_dir_item *direl = NULL; struct grub_btrfs_key key_out; + int follow_default; const char *ctoken; grub_size_t ctokenlen; char *path_alloc = NULL; char *origpath = NULL; unsigned symlinks_max = 32; + const char *relpath = grub_env_get ("btrfs_relative_path"); - err = get_root (data, key, tree, type); - if (err) - return err; - + follow_default = 0; origpath = grub_strdup (path); if (!origpath) return grub_errno; + if (relpath && (relpath[0] == '1' || relpath[0] == 'y')) + { + if (data->fs_tree) + { + *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY; + *tree = data->fs_tree; + /* This is a tree root, so everything starts at objectid 256 */ + key->object_id = grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK); + key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; + key->offset = 0; + } + else + { + *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY; + *tree = data->sblock.root_tree; + key->object_id = data->sblock.root_dir_objectid; + key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; + key->offset = 0; + follow_default = 1; + } + } + else + { + err = get_root (data, key, tree, type); + if (err) + return err; + } + while (1) { - while (path[0] == '/') - path++; - if (!path[0]) - break; - slash = grub_strchr (path, '/'); - if (!slash) - slash = path + grub_strlen (path); - ctoken = path; - ctokenlen = slash - path; + if (!follow_default) + { + while (path[0] == '/') + path++; + if (!path[0]) + break; + slash = grub_strchr (path, '/'); + if (!slash) + slash = path + grub_strlen (path); + ctoken = path; + ctokenlen = slash - path; + } + else + { + ctoken = "default"; + ctokenlen = sizeof ("default") - 1; + } if (*type != GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY) { @@ -1831,7 +2094,9 @@ find_path (struct grub_btrfs_data *data, if (ctokenlen == 1 && ctoken[0] == '.') { - path = slash; + if (!follow_default) + path = slash; + follow_default = 0; continue; } if (ctokenlen == 2 && ctoken[0] == '.' && ctoken[1] == '.') @@ -1862,8 +2127,9 @@ find_path (struct grub_btrfs_data *data, *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY; key->object_id = key_out.offset; - path = slash; - + if (!follow_default) + path = slash; + follow_default = 0; continue; } @@ -1932,7 +2198,9 @@ find_path (struct grub_btrfs_data *data, return err; } - path = slash; + if (!follow_default) + path = slash; + follow_default = 0; if (cdirel->type == GRUB_BTRFS_DIR_ITEM_TYPE_SYMLINK) { struct grub_btrfs_inode inode; @@ -1982,13 +2250,37 @@ find_path (struct grub_btrfs_data *data, path = path_alloc = tmp; if (path[0] == '/') { - err = get_root (data, key, tree, type); - if (err) + if (relpath && (relpath[0] == '1' || relpath[0] == 'y')) { - grub_free (direl); - grub_free (path_alloc); - grub_free (origpath); - return err; + if (data->fs_tree) + { + *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY; + *tree = data->fs_tree; + /* This is a tree root, so everything starts at objectid 256 */ + key->object_id = grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK); + key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; + key->offset = 0; + } + else + { + *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY; + *tree = data->sblock.root_tree; + key->object_id = data->sblock.root_dir_objectid; + key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; + key->offset = 0; + follow_default = 1; + } + } + else + { + err = get_root (data, key, tree, type); + if (err) + { + grub_free (direl); + grub_free (path_alloc); + grub_free (origpath); + return err; + } } } continue; @@ -2078,11 +2370,20 @@ grub_btrfs_dir (grub_device_t device, const char *path, grub_uint64_t tree; grub_uint8_t type; grub_size_t est_size = 0; + char *new_path = NULL; if (!data) return grub_errno; - err = find_path (data, path, &key_in, &tree, &type); + tree = find_mtab_subvol_tree (path, &new_path); + + if (tree) + data->fs_tree = tree; + + err = find_path (data, new_path ? new_path : path, &key_in, &tree, &type); + if (new_path) + grub_free (new_path); + if (err) { grub_btrfs_unmount (data); @@ -2209,11 +2510,21 @@ grub_btrfs_open (struct grub_file *file, const char *name) struct grub_btrfs_inode inode; grub_uint8_t type; struct grub_btrfs_key key_in; + grub_uint64_t tree; + char *new_path = NULL; if (!data) return grub_errno; - err = find_path (data, name, &key_in, &data->tree, &type); + tree = find_mtab_subvol_tree (name, &new_path); + + if (tree) + data->fs_tree = tree; + + err = find_path (data, new_path ? new_path : name, &key_in, &data->tree, &type); + if (new_path) + grub_free (new_path); + if (err) { grub_btrfs_unmount (data); @@ -2256,6 +2567,20 @@ grub_btrfs_read (grub_file_t file, char *buf, grub_size_t len) data->tree, file->offset, buf, len); } +static char * +btrfs_unparse_uuid(struct grub_btrfs_data *data) +{ + return grub_xasprintf ("%04x%04x-%04x-%04x-%04x-%04x%04x%04x", + grub_be_to_cpu16 (data->sblock.uuid[0]), + grub_be_to_cpu16 (data->sblock.uuid[1]), + grub_be_to_cpu16 (data->sblock.uuid[2]), + grub_be_to_cpu16 (data->sblock.uuid[3]), + grub_be_to_cpu16 (data->sblock.uuid[4]), + grub_be_to_cpu16 (data->sblock.uuid[5]), + grub_be_to_cpu16 (data->sblock.uuid[6]), + grub_be_to_cpu16 (data->sblock.uuid[7])); +} + static grub_err_t grub_btrfs_uuid (grub_device_t device, char **uuid) { @@ -2267,15 +2592,7 @@ grub_btrfs_uuid (grub_device_t device, char **uuid) if (!data) return grub_errno; - *uuid = grub_xasprintf ("%04x%04x-%04x-%04x-%04x-%04x%04x%04x", - grub_be_to_cpu16 (data->sblock.uuid[0]), - grub_be_to_cpu16 (data->sblock.uuid[1]), - grub_be_to_cpu16 (data->sblock.uuid[2]), - grub_be_to_cpu16 (data->sblock.uuid[3]), - grub_be_to_cpu16 (data->sblock.uuid[4]), - grub_be_to_cpu16 (data->sblock.uuid[5]), - grub_be_to_cpu16 (data->sblock.uuid[6]), - grub_be_to_cpu16 (data->sblock.uuid[7])); + *uuid = btrfs_unparse_uuid(data); grub_btrfs_unmount (data); @@ -2396,6 +2713,618 @@ grub_btrfs_embed (grub_device_t device __attribute__ ((unused)), } #endif +static grub_err_t +grub_cmd_btrfs_info (grub_command_t cmd __attribute__ ((unused)), int argc, + char **argv) +{ + grub_device_t dev; + char *devname; + struct grub_btrfs_data *data; + char *uuid; + + if (argc < 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required"); + + devname = grub_file_get_device_name(argv[0]); + + if (!devname) + return grub_errno; + + dev = grub_device_open (devname); + grub_free (devname); + if (!dev) + return grub_errno; + + data = grub_btrfs_mount (dev); + if (!data) + { + grub_device_close(dev); + return grub_error (GRUB_ERR_BAD_ARGUMENT, "failed to open fs"); + } + + if (data->sblock.label) + grub_printf("Label: '%s' ", data->sblock.label); + else + grub_printf("Label: none "); + + uuid = btrfs_unparse_uuid(data); + + grub_printf(" uuid: %s\n\tTotal devices %" PRIuGRUB_UINT64_T + " FS bytes used %" PRIuGRUB_UINT64_T "\n", + uuid, grub_cpu_to_le64(data->sblock.num_devices), + grub_cpu_to_le64(data->sblock.bytes_used)); + + grub_btrfs_unmount (data); + + return 0; +} + +struct grub_btrfs_mtab +{ + struct grub_btrfs_mtab *next; + struct grub_btrfs_mtab **prev; + char *path; + char *subvol; + grub_uint64_t tree; +}; + +typedef struct grub_btrfs_mtab* grub_btrfs_mtab_t; + +static struct grub_btrfs_mtab *btrfs_mtab; + +#define FOR_GRUB_MTAB(var) FOR_LIST_ELEMENTS (var, btrfs_mtab) +#define FOR_GRUB_MTAB_SAFE(var, next) FOR_LIST_ELEMENTS_SAFE((var), (next), btrfs_mtab) + +static void +add_mountpoint (const char *path, const char *subvol, grub_uint64_t tree) +{ + grub_btrfs_mtab_t m = grub_malloc (sizeof (*m)); + + m->path = grub_strdup (path); + m->subvol = grub_strdup (subvol); + m->tree = tree; + grub_list_push (GRUB_AS_LIST_P (&btrfs_mtab), GRUB_AS_LIST (m)); +} + +static grub_err_t +grub_cmd_btrfs_mount_subvol (grub_command_t cmd __attribute__ ((unused)), int argc, + char **argv) +{ + char *devname, *dirname, *subvol; + struct grub_btrfs_key key_in; + grub_uint8_t type; + grub_uint64_t tree; + grub_uint64_t saved_tree; + grub_err_t err; + struct grub_btrfs_data *data = NULL; + grub_device_t dev = NULL; + + if (argc < 3) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "required and "); + + devname = grub_file_get_device_name(argv[0]); + dev = grub_device_open (devname); + grub_free (devname); + + if (!dev) + { + err = grub_errno; + goto err_out; + } + + dirname = argv[1]; + subvol = argv[2]; + + data = grub_btrfs_mount (dev); + if (!data) + { + err = grub_errno; + goto err_out; + } + + err = find_path (data, dirname, &key_in, &tree, &type); + if (err) + goto err_out; + + if (type != GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY) + { + err = grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory")); + goto err_out; + } + + err = get_root (data, &key_in, &tree, &type); + + if (err) + goto err_out; + + saved_tree = data->fs_tree; + data->fs_tree = tree; + err = find_path (data, subvol, &key_in, &tree, &type); + data->fs_tree = saved_tree; + + if (err) + goto err_out; + + if (key_in.object_id != grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK) || tree == 0) + { + err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "%s: not a subvolume\n", subvol); + goto err_out; + } + + grub_btrfs_unmount (data); + grub_device_close (dev); + add_mountpoint (dirname, subvol, tree); + + return GRUB_ERR_NONE; + +err_out: + + if (data) + grub_btrfs_unmount (data); + + if (dev) + grub_device_close (dev); + + return err; +} + +grub_uint64_t +find_mtab_subvol_tree (const char *path, char **path_in_subvol) +{ + grub_btrfs_mtab_t m, cm; + grub_uint64_t tree; + + if (!path || !path_in_subvol) + return 0; + + *path_in_subvol = NULL; + tree = 0; + cm = NULL; + + FOR_GRUB_MTAB (m) + { + if (grub_strncmp (path, m->path, grub_strlen (m->path)) == 0) + { + if (!cm) + cm = m; + else + if (grub_strcmp (m->path, cm->path) > 0) + cm = m; + } + } + + if (cm) + { + const char *s = path + grub_strlen (cm->path); + *path_in_subvol = (s[0] == '\0') ? grub_strdup ("/") : grub_strdup (s); + tree = cm->tree; + } + + return tree; +} + +static grub_err_t +get_fs_root(struct grub_btrfs_data *data, grub_uint64_t tree, + grub_uint64_t objectid, grub_uint64_t offset, + grub_uint64_t *fs_root) +{ + grub_err_t err; + struct grub_btrfs_key key_in = { + .object_id = objectid, + .type = GRUB_BTRFS_ROOT_ITEM_KEY, + .offset = offset, + }, key_out; + struct grub_btrfs_leaf_descriptor desc; + grub_disk_addr_t elemaddr; + grub_size_t elemsize; + struct grub_btrfs_root_item ri; + + err = lower_bound(data, &key_in, &key_out, tree, + &elemaddr, &elemsize, &desc, 0); + + if (err) + return err; + + if (key_out.type != GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM || elemaddr == 0) + return grub_error(GRUB_ERR_FILE_NOT_FOUND, + N_("can't find fs root for subvol %"PRIuGRUB_UINT64_T"\n"), + key_in.object_id); + + err = grub_btrfs_read_logical (data, elemaddr, &ri, sizeof (ri), 0); + if (err) + return err; + + *fs_root = ri.tree; + + return GRUB_ERR_NONE; +} + +static const struct grub_arg_option options[] = { + {"output", 'o', 0, N_("Output to a variable instead of the console."), + N_("VARNAME"), ARG_TYPE_STRING}, + {"path-only", 'p', 0, N_("Show only the path of the subvolume."), 0, 0}, + {"id-only", 'i', 0, N_("Show only the id of the subvolume."), 0, 0}, + {0, 0, 0, 0, 0, 0} +}; + +static grub_err_t +grub_cmd_btrfs_list_subvols (struct grub_extcmd_context *ctxt, + int argc, char **argv) +{ + struct grub_btrfs_data *data; + grub_device_t dev; + char *devname; + grub_uint64_t tree; + struct grub_btrfs_key key_in = { + .object_id = grub_cpu_to_le64_compile_time (GRUB_BTRFS_FS_TREE_OBJECTID), + .type = GRUB_BTRFS_ROOT_REF_KEY, + .offset = 0, + }, key_out; + struct grub_btrfs_leaf_descriptor desc; + grub_disk_addr_t elemaddr; + grub_uint64_t fs_root = 0; + grub_size_t elemsize; + grub_size_t allocated = 0; + int r = 0; + grub_err_t err; + char *buf = NULL; + int print = 1; + int path_only = ctxt->state[1].set; + int num_only = ctxt->state[2].set; + char *varname = NULL; + char *output = NULL; + + if (ctxt->state[0].set) { + varname = ctxt->state[0].arg; + print = 0; + } + + if (argc < 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required"); + + devname = grub_file_get_device_name(argv[0]); + if (!devname) + return grub_errno; + + dev = grub_device_open (devname); + grub_free (devname); + if (!dev) + return grub_errno; + + data = grub_btrfs_mount(dev); + if (!data) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "could not open device"); + + tree = data->sblock.root_tree; + err = get_fs_root(data, tree, grub_cpu_to_le64_compile_time (GRUB_BTRFS_FS_TREE_OBJECTID), + 0, &fs_root); + if (err) + goto out; + + err = lower_bound(data, &key_in, &key_out, tree, + &elemaddr, &elemsize, &desc, 0); + + if (err) + { + grub_btrfs_unmount(data); + return err; + } + + if (key_out.type != GRUB_BTRFS_ITEM_TYPE_ROOT_REF || elemaddr == 0) + { + r = next(data, &desc, &elemaddr, &elemsize, &key_out); + } + + if (key_out.type != GRUB_BTRFS_ITEM_TYPE_ROOT_REF) { + err = GRUB_ERR_FILE_NOT_FOUND; + grub_error(GRUB_ERR_FILE_NOT_FOUND, N_("can't find root refs")); + goto out; + } + + do + { + struct grub_btrfs_root_ref *ref; + char *p = NULL; + + if (key_out.type != GRUB_BTRFS_ITEM_TYPE_ROOT_REF) + { + r = 0; + break; + } + + if (elemsize > allocated) + { + grub_free(buf); + allocated = 2 * elemsize; + buf = grub_malloc(allocated + 1); + if (!buf) + { + r = -grub_errno; + break; + } + } + ref = (struct grub_btrfs_root_ref *)buf; + + err = grub_btrfs_read_logical(data, elemaddr, buf, elemsize, 0); + if (err) + { + r = -err; + break; + } + buf[elemsize] = 0; + + find_pathname(data, ref->dirid, fs_root, ref->name, &p); + + if (print) + { + if (num_only) + grub_printf("ID %"PRIuGRUB_UINT64_T"\n", key_out.offset); + else if (path_only) + grub_printf("%s\n", p); + else + grub_printf("ID %"PRIuGRUB_UINT64_T" path %s\n", key_out.offset, p); + } else { + char *old = output; + if (num_only) + output = grub_xasprintf("%s%"PRIuGRUB_UINT64_T"\n", + old ?: "", key_out.offset); + else if (path_only) + output = grub_xasprintf("%s%s\n", old ?: "", p); + else + output = grub_xasprintf("%sID %"PRIuGRUB_UINT64_T" path %s\n", + old ?: "", key_out.offset, p); + + if (old) + grub_free(old); + } + + r = next(data, &desc, &elemaddr, &elemsize, &key_out); + } while(r > 0); + + if (output) + grub_env_set(varname, output); + +out: + free_iterator(&desc); + grub_btrfs_unmount(data); + + grub_device_close (dev); + + return 0; +} + +static grub_err_t +grub_btrfs_get_parent_subvol_path (struct grub_btrfs_data *data, + grub_uint64_t child_id, + const char *child_path, + grub_uint64_t *parent_id, + char **path_out) +{ + grub_uint64_t fs_root = 0; + struct grub_btrfs_key key_in = { + .object_id = child_id, + .type = GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF, + .offset = 0, + }, key_out; + struct grub_btrfs_root_ref *ref; + char *buf; + struct grub_btrfs_leaf_descriptor desc; + grub_size_t elemsize; + grub_disk_addr_t elemaddr; + grub_err_t err; + char *parent_path; + + *parent_id = 0; + *path_out = 0; + + err = lower_bound(data, &key_in, &key_out, data->sblock.root_tree, + &elemaddr, &elemsize, &desc, 0); + if (err) + return err; + + if (key_out.type != GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF || elemaddr == 0) + next(data, &desc, &elemaddr, &elemsize, &key_out); + + if (key_out.type != GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF) + { + free_iterator(&desc); + return grub_error(GRUB_ERR_FILE_NOT_FOUND, N_("can't find root backrefs")); + } + + buf = grub_malloc(elemsize + 1); + if (!buf) + { + free_iterator(&desc); + return grub_errno; + } + + err = grub_btrfs_read_logical(data, elemaddr, buf, elemsize, 0); + if (err) + { + grub_free(buf); + free_iterator(&desc); + return err; + } + + buf[elemsize] = 0; + ref = (struct grub_btrfs_root_ref *)buf; + + err = get_fs_root(data, data->sblock.root_tree, grub_le_to_cpu64 (key_out.offset), + 0, &fs_root); + if (err) + { + grub_free(buf); + free_iterator(&desc); + return err; + } + + find_pathname(data, grub_le_to_cpu64 (ref->dirid), fs_root, ref->name, &parent_path); + + if (child_path) + { + *path_out = grub_xasprintf ("%s/%s", parent_path, child_path); + grub_free (parent_path); + } + else + *path_out = parent_path; + + *parent_id = grub_le_to_cpu64 (key_out.offset); + + grub_free(buf); + free_iterator(&desc); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_btrfs_get_default_subvolume_id (struct grub_btrfs_data *data, grub_uint64_t *id) +{ + grub_err_t err; + grub_disk_addr_t elemaddr; + grub_size_t elemsize; + struct grub_btrfs_key key, key_out; + struct grub_btrfs_dir_item *direl = NULL; + const char *ctoken = "default"; + grub_size_t ctokenlen = sizeof ("default") - 1; + + *id = 0; + key.object_id = data->sblock.root_dir_objectid; + key.type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; + key.offset = grub_cpu_to_le64 (~grub_getcrc32c (1, ctoken, ctokenlen)); + err = lower_bound (data, &key, &key_out, data->sblock.root_tree, &elemaddr, &elemsize, + NULL, 0); + if (err) + return err; + + if (key_cmp (&key, &key_out) != 0) + return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file not found")); + + struct grub_btrfs_dir_item *cdirel; + direl = grub_malloc (elemsize + 1); + err = grub_btrfs_read_logical (data, elemaddr, direl, elemsize, 0); + if (err) + { + grub_free (direl); + return err; + } + for (cdirel = direl; + (grub_uint8_t *) cdirel - (grub_uint8_t *) direl + < (grub_ssize_t) elemsize; + cdirel = (void *) ((grub_uint8_t *) (direl + 1) + + grub_le_to_cpu16 (cdirel->n) + + grub_le_to_cpu16 (cdirel->m))) + { + if (ctokenlen == grub_le_to_cpu16 (cdirel->n) + && grub_memcmp (cdirel->name, ctoken, ctokenlen) == 0) + break; + } + if ((grub_uint8_t *) cdirel - (grub_uint8_t *) direl + >= (grub_ssize_t) elemsize) + { + grub_free (direl); + err = grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file not found")); + return err; + } + + if (cdirel->key.type != GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM) + { + grub_free (direl); + err = grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file not found")); + return err; + } + + *id = grub_le_to_cpu64 (cdirel->key.object_id); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_btrfs_get_default_subvol (struct grub_extcmd_context *ctxt, + int argc, char **argv) +{ + char *devname; + grub_device_t dev; + struct grub_btrfs_data *data; + grub_err_t err; + grub_uint64_t id; + char *subvol = NULL; + grub_uint64_t subvolid = 0; + char *varname = NULL; + char *output = NULL; + int path_only = ctxt->state[1].set; + int num_only = ctxt->state[2].set; + + if (ctxt->state[0].set) + varname = ctxt->state[0].arg; + + if (argc < 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required"); + + devname = grub_file_get_device_name(argv[0]); + if (!devname) + return grub_errno; + + dev = grub_device_open (devname); + grub_free (devname); + if (!dev) + return grub_errno; + + data = grub_btrfs_mount(dev); + if (!data) + { + grub_device_close (dev); + grub_dprintf ("btrfs", "failed to open fs\n"); + grub_errno = GRUB_ERR_NONE; + return 0; + } + + err = grub_btrfs_get_default_subvolume_id (data, &subvolid); + if (err) + { + grub_btrfs_unmount (data); + grub_device_close (dev); + return err; + } + + id = subvolid; + while (id != GRUB_BTRFS_ROOT_VOL_OBJECTID) + { + grub_uint64_t parent_id; + char *path_out; + + err = grub_btrfs_get_parent_subvol_path (data, grub_cpu_to_le64 (id), subvol, &parent_id, &path_out); + if (err) + { + grub_btrfs_unmount (data); + grub_device_close (dev); + return err; + } + + if (subvol) + grub_free (subvol); + subvol = path_out; + id = parent_id; + } + + if (num_only && path_only) + output = grub_xasprintf ("%"PRIuGRUB_UINT64_T" /%s", subvolid, subvol); + else if (num_only) + output = grub_xasprintf ("%"PRIuGRUB_UINT64_T, subvolid); + else + output = grub_xasprintf ("/%s", subvol); + + if (varname) + grub_env_set(varname, output); + else + grub_printf ("%s\n", output); + + grub_free (output); + grub_free (subvol); + + grub_btrfs_unmount (data); + grub_device_close (dev); + + return GRUB_ERR_NONE; +} + static struct grub_fs grub_btrfs_fs = { .name = "btrfs", .fs_dir = grub_btrfs_dir, @@ -2411,12 +3340,101 @@ static struct grub_fs grub_btrfs_fs = { #endif }; +static grub_command_t cmd_info; +static grub_command_t cmd_mount_subvol; +static grub_extcmd_t cmd_list_subvols; +static grub_extcmd_t cmd_get_default_subvol; + +static char * +subvolid_set_env (struct grub_env_var *var __attribute__ ((unused)), + const char *val) +{ + unsigned long long result = 0; + + grub_errno = GRUB_ERR_NONE; + if (*val) + { + result = grub_strtoull(val, NULL, 10); + if (grub_errno) + return NULL; + } + + grub_free (btrfs_default_subvol); + btrfs_default_subvol = NULL; + btrfs_default_subvolid = result; + return grub_strdup(val); +} + +static const char * +subvolid_get_env (struct grub_env_var *var __attribute__ ((unused)), + const char *val __attribute__ ((unused))) +{ + if (btrfs_default_subvol) + return grub_xasprintf("subvol:%s", btrfs_default_subvol); + else if (btrfs_default_subvolid) + return grub_xasprintf("%"PRIuGRUB_UINT64_T, btrfs_default_subvolid); + else + return ""; +} + +static char * +subvol_set_env (struct grub_env_var *var __attribute__ ((unused)), + const char *val) +{ + grub_free (btrfs_default_subvol); + btrfs_default_subvol = grub_strdup (val); + btrfs_default_subvolid = 0; + return grub_strdup(val); +} + +static const char * +subvol_get_env (struct grub_env_var *var __attribute__ ((unused)), + const char *val __attribute__ ((unused))) +{ + if (btrfs_default_subvol) + return btrfs_default_subvol; + else if (btrfs_default_subvolid) + return grub_xasprintf("subvolid:%" PRIuGRUB_UINT64_T, + btrfs_default_subvolid); + else + return ""; +} + GRUB_MOD_INIT (btrfs) { grub_fs_register (&grub_btrfs_fs); + cmd_info = grub_register_command("btrfs-info", grub_cmd_btrfs_info, + "DEVICE", + "Print BtrFS info about DEVICE."); + cmd_mount_subvol = grub_register_command("btrfs-mount-subvol", grub_cmd_btrfs_mount_subvol, + "DEVICE DIRECTORY SUBVOL", + "Set btrfs DEVICE the DIRECTORY a mountpoint of SUBVOL."); + cmd_list_subvols = grub_register_extcmd("btrfs-list-subvols", + grub_cmd_btrfs_list_subvols, 0, + "[-p|-n] [-o var] DEVICE", + "Print list of BtrFS subvolumes on " + "DEVICE.", options); + cmd_get_default_subvol = grub_register_extcmd("btrfs-get-default-subvol", + grub_cmd_btrfs_get_default_subvol, 0, + "[-p|-n] [-o var] DEVICE", + "Print default BtrFS subvolume on " + "DEVICE.", options); + grub_register_variable_hook ("btrfs_subvol", subvol_get_env, + subvol_set_env); + grub_register_variable_hook ("btrfs_subvolid", subvolid_get_env, + subvolid_set_env); + grub_env_export ("btrfs_subvol"); + grub_env_export ("btrfs_subvolid"); + grub_env_export ("btrfs_relative_path"); } GRUB_MOD_FINI (btrfs) { + grub_register_variable_hook ("btrfs_subvol", NULL, NULL); + grub_register_variable_hook ("btrfs_subvolid", NULL, NULL); + grub_unregister_command (cmd_info); + grub_unregister_extcmd (cmd_list_subvols); grub_fs_unregister (&grub_btrfs_fs); } + +// vim: si et sw=2: diff --git a/grub-core/gettext/gettext.c b/grub-core/gettext/gettext.c index 7a1c14e4f..631af7a94 100644 --- a/grub-core/gettext/gettext.c +++ b/grub-core/gettext/gettext.c @@ -424,6 +424,13 @@ grub_gettext_init_ext (struct grub_gettext_context *ctx, grub_free (lang); } + /* If no translations are available, fall back to untranslated text. */ + if (err == GRUB_ERR_FILE_NOT_FOUND) + { + grub_errno = GRUB_ERR_NONE; + return 0; + } + if (locale[0] == 'e' && locale[1] == 'n' && (locale[2] == '\0' || locale[2] == '_')) grub_errno = err = GRUB_ERR_NONE; diff --git a/grub-core/kern/efi/efi.c b/grub-core/kern/efi/efi.c index b93ae3aba..38e508ff6 100644 --- a/grub-core/kern/efi/efi.c +++ b/grub-core/kern/efi/efi.c @@ -222,6 +222,9 @@ grub_efi_set_variable_with_attributes (const char *var, const grub_guid_t *guid, if (status == GRUB_EFI_SUCCESS) return GRUB_ERR_NONE; + if (status == GRUB_EFI_NOT_FOUND && datasize == 0) + return GRUB_ERR_NONE; + return grub_error (GRUB_ERR_IO, "could not set EFI variable `%s'", var); } diff --git a/grub-core/kern/efi/init.c b/grub-core/kern/efi/init.c index 1637077e1..b780e5717 100644 --- a/grub-core/kern/efi/init.c +++ b/grub-core/kern/efi/init.c @@ -28,8 +28,11 @@ #include #include #include + #include +#include + #ifdef GRUB_STACK_PROTECTOR static grub_efi_char16_t stack_chk_fail_msg[] = @@ -94,6 +97,31 @@ grub_stack_protector_init (void) grub_addr_t grub_modbase; +/* Helper for grub_efi_env_init */ +static int +set_var (const char *name, const char *value, + void *whitelist __attribute__((__unused__))) +{ + grub_env_set (name, value); + return 0; +} + +static void +grub_efi_env_init (void) +{ + grub_guid_t efi_grub_guid = GRUB_EFI_GRUB_VARIABLE_GUID; + struct grub_envblk envblk_s = { NULL, 0 }; + grub_envblk_t envblk = &envblk_s; + + grub_efi_get_variable ("GRUB_ENV", &efi_grub_guid, &envblk_s.size, + (void **) &envblk_s.buf); + if (!envblk_s.buf || envblk_s.size < 1) + return; + + grub_envblk_iterate (envblk, NULL, set_var); + grub_free (envblk_s.buf); +} + void grub_efi_init (void) { @@ -117,6 +145,7 @@ grub_efi_init (void) grub_efi_system_table->boot_services->set_watchdog_timer (0, 0, 0, NULL); + grub_efi_env_init (); grub_efidisk_init (); grub_efi_register_debug_commands (); diff --git a/grub-core/kern/emu/misc.c b/grub-core/kern/emu/misc.c index 1db24fde7..181386e29 100644 --- a/grub-core/kern/emu/misc.c +++ b/grub-core/kern/emu/misc.c @@ -192,7 +192,7 @@ grub_util_get_image_size (const char *path) sz = ftello (f); if (sz < 0) grub_util_error (_("cannot open `%s': %s"), path, strerror (errno)); - if (sz != (size_t) sz) + if (sz > (off_t)(GRUB_SIZE_MAX >> 1)) grub_util_error (_("file `%s' is too big"), path); ret = (size_t) sz; diff --git a/grub-core/kern/fs.c b/grub-core/kern/fs.c index 80d325868..9f6f4ef9d 100644 --- a/grub-core/kern/fs.c +++ b/grub-core/kern/fs.c @@ -74,6 +74,7 @@ grub_fs_probe (grub_device_t device) if (grub_errno == GRUB_ERR_NONE) return p; + grub_dprintf ("fs", _("error: %s.\n"), grub_errmsg); grub_error_push (); /* The grub_error_push() does not touch grub_errmsg. */ grub_dprintf ("fs", _("error: %s.\n"), grub_errmsg); diff --git a/grub-core/kern/main.c b/grub-core/kern/main.c index d29494d54..d599faeb6 100644 --- a/grub-core/kern/main.c +++ b/grub-core/kern/main.c @@ -132,16 +132,15 @@ grub_set_prefix_and_root (void) grub_machine_get_bootlocation (&fwdevice, &fwpath); - if (fwdevice) + if (fwdevice && fwpath) { - char *cmdpath; + char *fw_path; - cmdpath = grub_xasprintf ("(%s)%s", fwdevice, fwpath ? : ""); - if (cmdpath) + fw_path = grub_xasprintf ("(%s)/%s", fwdevice, fwpath); + if (fw_path) { - grub_env_set ("cmdpath", cmdpath); - grub_env_export ("cmdpath"); - grub_free (cmdpath); + grub_env_set ("fw_path", fw_path); + grub_free (fw_path); } } diff --git a/grub-core/kern/misc.c b/grub-core/kern/misc.c index 7cee5d75c..62168d7de 100644 --- a/grub-core/kern/misc.c +++ b/grub-core/kern/misc.c @@ -26,6 +26,7 @@ #include #include #include +#include union printf_arg { @@ -182,6 +183,19 @@ int grub_err_printf (const char *fmt, ...) __attribute__ ((alias("grub_printf"))); #endif +/* Return 1 if 'debug' is set and not empty */ +int +grub_debug_is_enabled (void) +{ + const char *debug; + + debug = grub_env_get ("debug"); + if (!debug || debug[0] == '\0') + return 0; + + return 1; +} + int grub_debug_enabled (const char * condition) { @@ -611,6 +625,36 @@ grub_reverse (char *str) } } +/* Separate string into two parts, broken up by delimiter delim. */ +void +grub_str_sep (const char *s, char *p, char delim, char *r) +{ + char* t = grub_strndup(s, grub_strlen(s)); + + if (t != NULL && *t != '\0') + { + char* tmp = t; + + while (((*p = *t) != '\0') && ((*p = *t) != delim)) + { + p++; + t++; + } + *p = '\0'; + + if (*t != '\0') + { + t++; + while ((*r++ = *t++) != '\0') + ; + *r = '\0'; + } + grub_free (tmp); + } + else + grub_free (t); +} + /* Divide N by D, return the quotient, and store the remainder in *R. */ grub_uint64_t grub_divmod64 (grub_uint64_t n, grub_uint64_t d, grub_uint64_t *r) @@ -1301,6 +1345,11 @@ grub_printf_fmt_check (const char *fmt, const char *fmt_expected) void __attribute__ ((noreturn)) grub_abort (void) { +#ifndef GRUB_UTIL +#if defined(__i386__) || defined(__x86_64__) + grub_backtrace(); +#endif +#endif grub_printf ("\nAborted."); #ifndef GRUB_UTIL diff --git a/grub-core/lib/arm64/backtrace.c b/grub-core/lib/arm64/backtrace.c new file mode 100644 index 000000000..1079b5380 --- /dev/null +++ b/grub-core/lib/arm64/backtrace.c @@ -0,0 +1,62 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2009 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +#define MAX_STACK_FRAME 102400 + +void +grub_backtrace_pointer (int frame) +{ + while (1) + { + void *lp = __builtin_return_address (frame); + if (!lp) + break; + + lp = __builtin_extract_return_addr (lp); + + grub_printf ("%p: ", lp); + grub_backtrace_print_address (lp); + grub_printf (" ("); + for (i = 0; i < 2; i++) + grub_printf ("%p,", ((void **)ptr) [i + 2]); + grub_printf ("%p)\n", ((void **)ptr) [i + 2]); + nptr = *(void **)ptr; + if (nptr < ptr || (void **) nptr - (void **) ptr > MAX_STACK_FRAME + || nptr == ptr) + { + grub_printf ("Invalid stack frame at %p (%p)\n", ptr, nptr); + break; + } + ptr = nptr; + } +} + +void +grub_backtrace (void) +{ + grub_backtrace_pointer (1); +} + diff --git a/grub-core/lib/backtrace.c b/grub-core/lib/backtrace.c index 825a8800e..c0ad6ab8b 100644 --- a/grub-core/lib/backtrace.c +++ b/grub-core/lib/backtrace.c @@ -29,6 +29,7 @@ GRUB_MOD_LICENSE ("GPLv3+"); void grub_backtrace_print_address (void *addr) { +#ifndef GRUB_UTIL grub_dl_t mod; FOR_DL_MODULES (mod) @@ -44,6 +45,7 @@ grub_backtrace_print_address (void *addr) } } +#endif grub_printf ("%p", addr); } diff --git a/grub-core/lib/envblk.c b/grub-core/lib/envblk.c index 2e4e78b13..874506da1 100644 --- a/grub-core/lib/envblk.c +++ b/grub-core/lib/envblk.c @@ -223,6 +223,49 @@ grub_envblk_delete (grub_envblk_t envblk, const char *name) } } +struct get_var_state { + const char * const name; + char * value; + int found; +}; + +static int +get_var (const char * const name, const char * const value, void *statep) +{ + struct get_var_state *state = (struct get_var_state *)statep; + + if (!grub_strcmp(state->name, name)) + { + state->found = 1; + state->value = grub_strdup(value); + if (!state->value) + grub_errno = grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); + + return 1; + } + + return 0; +} + +grub_err_t +grub_envblk_get (grub_envblk_t envblk, const char * const name, char ** const value) +{ + struct get_var_state state = { + .name = name, + .value = NULL, + .found = 0, + }; + + grub_envblk_iterate(envblk, (void *)&state, get_var); + + *value = state.value; + + if (state.found && !state.value) + return grub_errno; + + return GRUB_ERR_NONE; +} + void grub_envblk_iterate (grub_envblk_t envblk, void *hook_data, diff --git a/grub-core/lib/i386/backtrace.c b/grub-core/lib/i386/backtrace.c index c3e03c727..c67273db3 100644 --- a/grub-core/lib/i386/backtrace.c +++ b/grub-core/lib/i386/backtrace.c @@ -15,11 +15,23 @@ * You should have received a copy of the GNU General Public License * along with GRUB. If not, see . */ +#include +#ifdef GRUB_UTIL +#define REALLY_GRUB_UTIL GRUB_UTIL +#undef GRUB_UTIL +#endif + +#include +#include + +#ifdef REALLY_GRUB_UTIL +#define GRUB_UTIL REALLY_GRUB_UTIL +#undef REALLY_GRUB_UTIL +#endif #include #include #include -#include #include #include #include diff --git a/grub-core/lib/reed_solomon.c b/grub-core/lib/reed_solomon.c index 562bd2e3e..5fee7f2a1 100644 --- a/grub-core/lib/reed_solomon.c +++ b/grub-core/lib/reed_solomon.c @@ -162,7 +162,7 @@ static void rs_encode (gf_single_t *data, grub_size_t s, grub_size_t rs) { gf_single_t *rs_polynomial; - int i, j; + unsigned int i, j; gf_single_t *m; m = xcalloc (s + rs, sizeof (gf_single_t)); grub_memcpy (m, data, s * sizeof (gf_single_t)); @@ -333,7 +333,7 @@ static void encode_block (gf_single_t *ptr, grub_size_t s, gf_single_t *rptr, grub_size_t rs) { - int i, j; + unsigned int i, j; for (i = 0; i < SECTOR_SIZE; i++) { grub_size_t ds = (s + SECTOR_SIZE - 1 - i) / SECTOR_SIZE; diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c index 869307bf3..0b29870b9 100644 --- a/grub-core/loader/efi/chainloader.c +++ b/grub-core/loader/efi/chainloader.c @@ -36,6 +36,7 @@ #include #include #include +#include #if defined (__i386__) || defined (__x86_64__) #include #include @@ -45,6 +46,26 @@ GRUB_MOD_LICENSE ("GPLv3+"); static grub_dl_t my_mod; +static void *image_mem; +static grub_efi_physical_address_t image_address; +static grub_efi_uintn_t image_pages; + +static struct grub_slaunch_params slparams = {0}; + +static void +free_image (void) +{ + if (image_address) + { + grub_efi_system_table->boot_services->free_pages (image_address, + image_pages); + + image_mem = NULL; + image_address = 0; + image_pages = 0; + } +} + static grub_err_t grub_chainloader_unload (void *context) { @@ -52,6 +73,8 @@ grub_chainloader_unload (void *context) grub_efi_loaded_image_t *loaded_image; grub_efi_boot_services_t *b; + free_image (); + loaded_image = grub_efi_get_loaded_image (image_handle); if (loaded_image != NULL) grub_free (loaded_image->load_options); @@ -71,6 +94,25 @@ grub_chainloader_boot (void *context) grub_efi_status_t status; grub_efi_uintn_t exit_data_size; grub_efi_char16_t *exit_data = NULL; + grub_err_t err; + grub_efi_loaded_image_t *loaded_image; + + loaded_image = grub_efi_get_loaded_image (image_handle); + if (loaded_image == NULL) + return grub_error (GRUB_ERR_BUG, "Couldn't query EFI loaded image"); + + if (grub_slaunch_platform_type () == SLP_INTEL_TXT) + { + err = grub_sl_efi_txt_setup (&slparams, image_mem, loaded_image, /*is_linux=*/false); + if (err != GRUB_ERR_NONE) + return grub_error (err, "Secure Launch setup TXT failed"); + } + else if (grub_slaunch_platform_type () == SLP_AMD_SKINIT) + { + err = grub_sl_efi_skinit_setup (&slparams, image_mem, loaded_image, /*is_linux=*/false); + if (err != GRUB_ERR_NONE) + return grub_error (err, "Secure Launch setup SKINIT failed"); + } b = grub_efi_system_table->boot_services; status = b->start_image (image_handle, &exit_data_size, &exit_data); @@ -220,10 +262,7 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), grub_efi_device_path_t *dp = NULL, *file_path = NULL; grub_efi_loaded_image_t *loaded_image; char *filename; - void *boot_image = 0; grub_efi_handle_t dev_handle = 0; - grub_efi_physical_address_t address = 0; - grub_efi_uintn_t pages = 0; grub_efi_char16_t *cmdline = NULL; grub_efi_handle_t image_handle = NULL; @@ -231,6 +270,8 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); filename = argv[0]; + free_image (); + grub_dl_ref (my_mod); b = grub_efi_system_table->boot_services; @@ -280,21 +321,21 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), filename); goto fail; } - pages = (grub_efi_uintn_t) GRUB_EFI_BYTES_TO_PAGES (size); + image_pages = (grub_efi_uintn_t) GRUB_EFI_BYTES_TO_PAGES (size); status = b->allocate_pages (GRUB_EFI_ALLOCATE_ANY_PAGES, GRUB_EFI_LOADER_CODE, - pages, &address); + image_pages, &image_address); if (status != GRUB_EFI_SUCCESS) { grub_dprintf ("chain", "Failed to allocate %u pages\n", - (unsigned int) pages); + (unsigned int) image_pages); grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); goto fail; } - boot_image = (void *) ((grub_addr_t) address); - if (grub_file_read (file, boot_image, size) != size) + image_mem = (void *) ((grub_addr_t) image_address); + if (grub_file_read (file, image_mem, size) != size) { if (grub_errno == GRUB_ERR_NONE) grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), @@ -306,7 +347,7 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), #if defined (__i386__) || defined (__x86_64__) if (size >= (grub_ssize_t) sizeof (struct grub_macho_fat_header)) { - struct grub_macho_fat_header *head = boot_image; + struct grub_macho_fat_header *head = image_mem; if (head->magic == grub_cpu_to_le32_compile_time (GRUB_MACHO_FAT_EFI_MAGIC)) { @@ -333,14 +374,14 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), filename); goto fail; } - boot_image = (char *) boot_image + grub_cpu_to_le32 (archs[i].offset); + image_mem = (char *) image_mem + grub_cpu_to_le32 (archs[i].offset); size = grub_cpu_to_le32 (archs[i].size); } } #endif status = b->load_image (0, grub_efi_image_handle, file_path, - boot_image, size, + image_mem, size, &image_handle); if (status != GRUB_EFI_SUCCESS) { @@ -396,8 +437,9 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), grub_file_close (file); grub_device_close (dev); - /* We're finished with the source image buffer and file path now. */ - b->free_pages (address, pages); + /* We're typically finished with the source image buffer and file path here. */ + if (grub_slaunch_platform_type () == SLP_NONE) + free_image (); grub_free (file_path); grub_loader_set_ex (grub_chainloader_boot, grub_chainloader_unload, image_handle, 0); @@ -414,8 +456,7 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), grub_free (cmdline); grub_free (file_path); - if (address) - b->free_pages (address, pages); + free_image (); if (image_handle != NULL) b->unload_image (image_handle); diff --git a/grub-core/loader/efi/linux.c b/grub-core/loader/efi/linux.c index f158b62f9..f19de49b0 100644 --- a/grub-core/loader/efi/linux.c +++ b/grub-core/loader/efi/linux.c @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -134,6 +135,9 @@ grub_arch_efi_linux_load_image_header (grub_file_t file, else initrd_use_loadfile2 = false; + if (grub_env_get ("slaunch-legacy-linux")) + initrd_use_loadfile2 = false; + grub_dprintf ("linux", "LoadFile2 initrd loading %sabled\n", initrd_use_loadfile2 ? "en" : "dis"); @@ -242,7 +246,7 @@ grub_arch_efi_linux_boot_image (grub_addr_t addr, grub_size_t size, char *args) if (grub_slaunch_platform_type () == SLP_INTEL_TXT) { - err = grub_sl_efi_txt_setup (&slparams, kernel_addr, loaded_image); + err = grub_sl_efi_txt_setup (&slparams, kernel_addr, loaded_image, /*is_linux=*/true); if (err != GRUB_ERR_NONE) { grub_error (err, "Secure Launch setup TXT failed"); @@ -251,7 +255,7 @@ grub_arch_efi_linux_boot_image (grub_addr_t addr, grub_size_t size, char *args) } else if (grub_slaunch_platform_type () == SLP_AMD_SKINIT) { - err = grub_sl_efi_skinit_setup (&slparams, kernel_addr, loaded_image); + err = grub_sl_efi_skinit_setup (&slparams, kernel_addr, loaded_image, /*is_linux=*/true); if (err != GRUB_ERR_NONE) { grub_error (err, "Secure Launch setup SKINIT failed"); diff --git a/grub-core/loader/multiboot_elfxx.c b/grub-core/loader/multiboot_elfxx.c index ee05a4d99..9025bfc8e 100644 --- a/grub-core/loader/multiboot_elfxx.c +++ b/grub-core/loader/multiboot_elfxx.c @@ -176,7 +176,6 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld) if (grub_slaunch_platform_type () != SLP_NONE) { slparams->mle_start = mld->load_base_addr; - slparams->mle_mem = source; slparams->mle_ptab_size = 0; } @@ -298,6 +297,7 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld) slparams->mle_header_offset = mle_hdr_offset; slparams->mle_size = mle_hdr->mle_end - mle_hdr->mle_start; + slparams->mle_entry = mle_hdr->entry_point; } #endif diff --git a/grub-core/loader/multiboot_mbi2.c b/grub-core/loader/multiboot_mbi2.c index 9b480c0c2..9bd48bf30 100644 --- a/grub-core/loader/multiboot_mbi2.c +++ b/grub-core/loader/multiboot_mbi2.c @@ -1244,9 +1244,20 @@ grub_multiboot2_perform_slaunch (grub_uint32_t mbi_target, if (slparams->platform_type == SLP_INTEL_TXT) { + slparams->slr_table_base = GRUB_SLAUNCH_STORE_IN_OS2MLE; + slparams->slr_table_size = GRUB_PAGE_SIZE; + + slparams->slr_table_mem = grub_zalloc (slparams->slr_table_size); + if (slparams->slr_table_mem == NULL) + return grub_error (grub_errno, N_("Failed to allocate SLRT")); + err = grub_txt_boot_prepare (slparams); if (err != GRUB_ERR_NONE) return grub_error (err, "TXT boot preparation failed"); + + grub_memcpy ((void *)(grub_addr_t) slparams->slr_table_base, + slparams->slr_table_mem, + slparams->slr_table_size); } else if (slparams->platform_type == SLP_AMD_SKINIT) { @@ -1267,8 +1278,11 @@ grub_multiboot2_perform_slaunch (grub_uint32_t mbi_target, (unsigned) slparams->slr_table_size); dlinfo = grub_slr_next_entry_by_tag (slparams->slr_table_mem, NULL, GRUB_SLR_ENTRY_DL_INFO); + if (dlinfo == NULL) + return grub_error (GRUB_ERR_BUG, N_("Failed to find DL-info SLRT entry")); + dl_entry ((grub_uint64_t)(grub_addr_t) &dlinfo->bl_context); /* If this returns, something failed miserably */ - return GRUB_ERR_BAD_DEVICE; + return grub_error (GRUB_ERR_BAD_DEVICE, N_("Failed to start D-RTM")); } diff --git a/grub-core/loader/slaunch/i386_linux.c b/grub-core/loader/slaunch/i386_linux.c index 2a830c0b3..576af841d 100644 --- a/grub-core/loader/slaunch/i386_linux.c +++ b/grub-core/loader/slaunch/i386_linux.c @@ -41,6 +41,7 @@ grub_sl_find_kernel_info (struct grub_slaunch_params *slparams, grub_file_t kern { struct linux_kernel_info *linux_info; + struct grub_txt_mle_header mle_hdr; /* Not a Secure Launch, do nothing */ if (grub_slaunch_platform_type () == SLP_NONE) @@ -48,7 +49,7 @@ grub_sl_find_kernel_info (struct grub_slaunch_params *slparams, grub_file_t kern if (grub_le_to_cpu16 (lh->version) < 0x020f) { - grub_error (GRUB_ERR_BAD_OS, N_("not slaunch kernel: boot protocol too old")); + grub_error (GRUB_ERR_BAD_OS, N_("not an slaunch kernel: boot protocol too old")); goto fail; } @@ -104,12 +105,24 @@ grub_sl_find_kernel_info (struct grub_slaunch_params *slparams, grub_file_t kern if (OFFSET_OF (mle_header_offset, linux_info) >= grub_le_to_cpu32 (linux_info->size)) { if (!grub_errno) - grub_error (GRUB_ERR_BAD_OS, N_("not slaunch kernel: lack of mle_header_offset")); + grub_error (GRUB_ERR_BAD_OS, N_("not an slaunch kernel: lack of mle_header_offset")); goto fail; } slparams->mle_header_offset = grub_le_to_cpu32 (linux_info->mle_header_offset); + if (grub_file_seek (kernel_file, slparams->mle_header_offset + real_size + + GRUB_DISK_SECTOR_SIZE) == ((grub_off_t) -1)) + goto fail; + if (grub_file_read (kernel_file, &mle_hdr, sizeof (mle_hdr)) != sizeof (mle_hdr)) + { + if (!grub_errno) + grub_error (GRUB_ERR_BAD_OS, N_("premature end of kernel file")); + goto fail; + } + + slparams->mle_entry = mle_hdr.entry_point; + return GRUB_ERR_NONE; fail: @@ -159,7 +172,6 @@ grub_sl_txt_setup_linux (struct grub_slaunch_params *slparams, struct grub_reloc *prot_mode_mem = (char *)*prot_mode_mem + slparams->mle_ptab_size; *prot_mode_target += slparams->mle_ptab_size; - slparams->mle_mem = *prot_mode_mem; slparams->mle_start = *prot_mode_target; slparams->mle_size = prot_size; @@ -191,8 +203,8 @@ grub_sl_txt_setup_linux (struct grub_slaunch_params *slparams, struct grub_reloc slparams->tpm_evt_log_base = get_physical_target_address (ch); slparams->tpm_evt_log_size = GRUB_SLAUNCH_TPM_EVT_LOG_SIZE; - - grub_memset (get_virtual_current_address (ch), 0, slparams->tpm_evt_log_size); + grub_txt_init_tpm_event_log (get_virtual_current_address (ch), + slparams->tpm_evt_log_size); grub_dprintf ("linux", "tpm_evt_log_base = %lx, tpm_evt_log_size = %x\n", (unsigned long) slparams->tpm_evt_log_base, @@ -232,7 +244,6 @@ grub_sl_skinit_setup_linux (struct grub_slaunch_params *slparams, struct grub_re /* Zero out memory to get stable MLE measurements. */ grub_memset (prot_mode_mem, 0, total_size); - slparams->mle_mem = prot_mode_mem; slparams->mle_start = prot_mode_target; slparams->mle_size = prot_file_size; @@ -245,8 +256,9 @@ grub_sl_skinit_setup_linux (struct grub_slaunch_params *slparams, struct grub_re slparams->tpm_evt_log_base = get_physical_target_address (ch); slparams->tpm_evt_log_size = GRUB_SLAUNCH_TPM_EVT_LOG_SIZE; - - grub_memset (get_virtual_current_address (ch), 0, slparams->tpm_evt_log_size); + /* It's OK to call this for AMD SKINIT because SKL erases the log before use. */ + grub_txt_init_tpm_event_log (get_virtual_current_address (ch), + slparams->tpm_evt_log_size); grub_dprintf ("linux", "tpm_evt_log_base = %lx, tpm_evt_log_size = %x\n", (unsigned long) slparams->tpm_evt_log_base, diff --git a/grub-core/loader/slaunch/skl.c b/grub-core/loader/slaunch/skl.c index 465f2fb7e..f7afb43ef 100644 --- a/grub-core/loader/slaunch/skl.c +++ b/grub-core/loader/slaunch/skl.c @@ -98,9 +98,9 @@ grub_skl_set_module (const void *skl_base, grub_uint32_t size) if (module->skl_info_offset > module->length - sizeof (info->uuid)) { grub_dprintf ("slaunch", - "Possible SKL module doesn't measure info: %u > %u\n", + "Possible SKL module doesn't measure info: %u > %lu\n", module->skl_info_offset, - module->length - sizeof (info->uuid)); + (unsigned long)(module->length - sizeof (info->uuid))); return 0; } diff --git a/grub-core/loader/slaunch/slaunch.c b/grub-core/loader/slaunch/slaunch.c index 15a2b5a09..39e6e0ad8 100644 --- a/grub-core/loader/slaunch/slaunch.c +++ b/grub-core/loader/slaunch/slaunch.c @@ -57,6 +57,12 @@ grub_cmd_slaunch (grub_command_t cmd __attribute__ ((unused)), grub_uint32_t eax; grub_err_t err; + if (argc == 1) + { + if (grub_strcmp (argv[0], "--legacy-linux") == 0) + grub_env_set("slaunch-legacy-linux", "use-legacy-linux"); + } + if (!grub_cpu_is_cpuid_supported ()) return grub_error (GRUB_ERR_BAD_DEVICE, N_("CPUID is unsupported")); @@ -203,7 +209,8 @@ static grub_command_t cmd_slaunch, cmd_slaunch_module, cmd_slaunch_state; GRUB_MOD_INIT (slaunch) { cmd_slaunch = grub_register_command ("slaunch", grub_cmd_slaunch, - NULL, N_("Enable secure launcher")); + N_("[--legacy-linux]"), + N_("Enable secure launcher")); cmd_slaunch_module = grub_register_command ("slaunch_module", grub_cmd_slaunch_module, NULL, N_("Load secure launcher module from file")); cmd_slaunch_state = grub_register_command ("slaunch_state", grub_cmd_slaunch_state, diff --git a/grub-core/loader/slaunch/slrt.c b/grub-core/loader/slaunch/slrt.c index 0fe10041f..de5c32cf1 100644 --- a/grub-core/loader/slaunch/slrt.c +++ b/grub-core/loader/slaunch/slrt.c @@ -201,9 +201,8 @@ grub_setup_slrt_policy (struct grub_slaunch_params *slparams, void grub_setup_slrt_dl_info (struct grub_slaunch_params *slparams) { - struct grub_txt_mle_header *mle_header; - - mle_header = (struct grub_txt_mle_header *)((grub_addr_t) slparams->mle_mem + slparams->mle_header_offset); + grub_dprintf ("slaunch", "DLME size: 0x%x\n", slparams->mle_size); + grub_dprintf ("slaunch", "DLME entry point: 0x%x\n", slparams->mle_entry); /* Setup DL entry point, DCE and DLME information */ slr_dl_info_staging.bl_context.bootloader = GRUB_SLR_BOOTLOADER_GRUB; @@ -211,7 +210,7 @@ grub_setup_slrt_dl_info (struct grub_slaunch_params *slparams) slr_dl_info_staging.dl_handler = (grub_addr_t)dl_entry_trampoline; slr_dl_info_staging.dlme_size = slparams->mle_size; slr_dl_info_staging.dlme_base = slparams->mle_start; - slr_dl_info_staging.dlme_entry = mle_header->entry_point; + slr_dl_info_staging.dlme_entry = slparams->mle_entry; slr_dl_info_staging.dce_base = slparams->dce_base; slr_dl_info_staging.dce_size = slparams->dce_size; } @@ -223,15 +222,14 @@ grub_setup_slrt_log_info (struct grub_slaunch_params *slparams) slr_log_info_staging.size = slparams->tpm_evt_log_size; slr_log_info_staging.format = (grub_get_tpm_ver () == GRUB_TPM_20) ? - GRUB_SLR_DRTM_TPM20_LOG : GRUB_SLR_DRTM_TPM20_LOG; + GRUB_SLR_DRTM_TPM20_LOG : GRUB_SLR_DRTM_TPM12_LOG; } void grub_setup_slr_table (struct grub_slaunch_params *slparams, struct grub_slr_entry_hdr *platform_info) { - struct grub_slr_table *slrt = - (struct grub_slr_table *)(grub_addr_t)slparams->slr_table_base; + struct grub_slr_table *slrt = slparams->slr_table_mem; grub_slr_add_entry (slrt, &slr_dl_info_staging.hdr); grub_slr_add_entry (slrt, &slr_log_info_staging.hdr); diff --git a/grub-core/loader/slaunch/txt.c b/grub-core/loader/slaunch/txt.c index 3b36e0ca4..5336f72ad 100644 --- a/grub-core/loader/slaunch/txt.c +++ b/grub-core/loader/slaunch/txt.c @@ -59,6 +59,7 @@ #include #include #include +#include #include #include #include @@ -82,6 +83,19 @@ static grub_err_t enable_smx_mode (void) { grub_uint32_t caps; + grub_uint64_t feat_ctrl = grub_rdmsr (GRUB_MSR_X86_FEATURE_CONTROL); + + if (!(feat_ctrl & GRUB_MSR_X86_FEATURE_CTRL_LOCK)) + { + grub_dprintf ("slaunch", "Firmware didn't lock FEATURE_CONTROL MSR," + "locking it now\n"); + /* Not setting SENTER_FUNCTIONS and SENTER_ENABLE because they were tested + * in grub_txt_verify_platform() */ + feat_ctrl |= GRUB_MSR_X86_ENABLE_VMX_OUT_SMX | + GRUB_MSR_X86_ENABLE_VMX_IN_SMX | + GRUB_MSR_X86_FEATURE_CTRL_LOCK; + grub_wrmsr (GRUB_MSR_X86_FEATURE_CONTROL, feat_ctrl); + } /* Enable SMX mode. */ grub_write_cr4 (grub_read_cr4 () | GRUB_CR4_X86_SMXE); @@ -115,12 +129,12 @@ enable_smx_mode (void) return grub_errno; } -static void +static grub_err_t grub_txt_smx_parameters (struct grub_smx_parameters *params) { grub_uint32_t index = 0, eax, ebx, ecx, param_type; - grub_memset (params, 0, sizeof(struct grub_smx_supported_versions)); + grub_memset (params, 0, sizeof(*params)); params->max_acm_size = GRUB_SMX_DEFAULT_MAX_ACM_SIZE; params->acm_memory_types = GRUB_SMX_DEFAULT_ACM_MEMORY_TYPE; @@ -137,11 +151,8 @@ grub_txt_smx_parameters (struct grub_smx_parameters *params) break; /* This means done. */ case GRUB_SMX_PARAMETER_ACM_VERSIONS: - if (params->version_count == GRUB_SMX_PARAMETER_MAX_VERSIONS) - { - grub_error (GRUB_ERR_OUT_OF_RANGE, N_("Too many ACM versions")); - break; - } + if (params->version_count >= GRUB_SMX_PARAMETER_MAX_VERSIONS) + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("Too many ACM versions")); params->versions[params->version_count].mask = ebx; params->versions[params->version_count++].version = ecx; break; @@ -163,8 +174,7 @@ grub_txt_smx_parameters (struct grub_smx_parameters *params) break; default: - grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Unknown SMX parameter")); - param_type = GRUB_SMX_PARAMETER_NULL; + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Unknown SMX parameter")); } ++index; @@ -178,6 +188,8 @@ grub_txt_smx_parameters (struct grub_smx_parameters *params) params->versions[0].version = GRUB_SMX_DEFAULT_VERSION; params->version_count++; } + + return GRUB_ERR_NONE; } grub_err_t @@ -187,6 +199,7 @@ grub_txt_prepare_cpu (void) grub_uint32_t i; grub_uint64_t mcg_cap, mcg_stat; unsigned long cr0; + grub_err_t err; cr0 = grub_read_control_register (GRUB_CR0); @@ -211,12 +224,14 @@ grub_txt_prepare_cpu (void) return grub_error (GRUB_ERR_BAD_DEVICE, N_("machine check in progress during secure launch")); - grub_txt_smx_parameters (¶ms); + err = grub_txt_smx_parameters (¶ms); + if (err != GRUB_ERR_NONE) + return err; if (params.txt_feature_ext_flags & GRUB_SMX_PROCESSOR_BASE_SCRTM) grub_dprintf ("slaunch", "CPU supports processor-based S-CRTM\n"); - if (params.txt_feature_ext_flags & GRUB_SMX_MACHINE_CHECK_HANLDING) + if (params.txt_feature_ext_flags & GRUB_SMX_MACHINE_CHECK_HANDLING) grub_dprintf ("slaunch", "CPU supports preserving machine check errors\n"); else { @@ -264,6 +279,12 @@ save_mtrrs (struct grub_slr_txt_mtrr_state *saved_bsp_mtrrs) saved_bsp_mtrrs->mtrr_pair[i].mtrr_physbase = grub_rdmsr (GRUB_MSR_X86_MTRR_PHYSBASE0 + i * 2); } + /* Zero unused array items. */ + for ( ; i < GRUB_TXT_VARIABLE_MTRRS_LENGTH; ++i) + { + saved_bsp_mtrrs->mtrr_pair[i].mtrr_physmask = 0; + saved_bsp_mtrrs->mtrr_pair[i].mtrr_physbase = 0; + } } static void @@ -283,6 +304,12 @@ set_all_mtrrs (int enable) #define SINIT_MTRR_MASK 0xFFFFFF /* SINIT requires 36b mask */ +/* + * Note: bitfields in following structures are assumed to work on x86 and + * nothing else. All compilers supported by GRUB agree when it comes to layout + * of bits that is consistent with hardware implementation. It was decided to + * use bitfields for better readability instead of manual shifting and masking. + */ union mtrr_physbase_t { grub_uint64_t raw; @@ -362,7 +389,7 @@ set_mtrr_mem_type (const grub_uint8_t *base, grub_uint32_t size, grub_dprintf ("slaunch", "setting MTRRs for acmod: base=%p, size=%x, num_pages=%d\n", base, size, num_pages); - /* Each VAR MTRR base must be a multiple if that MTRR's Size */ + /* Each VAR MTRR base must be a multiple of that MTRR's Size */ base_v = (unsigned long)base; /* MTRR size in pages */ mtrr_s = 1; @@ -480,8 +507,6 @@ grub_set_mtrrs_for_acmod (struct grub_txt_acm_header *hdr) /* Set MTRRs for AC mod and rest of memory */ err = set_mtrr_mem_type ((grub_uint8_t*)hdr, hdr->size*4, GRUB_MTRR_MEMORY_TYPE_WB); - if ( err ) - return err; /* Undo some of earlier changes and enable our new settings */ @@ -491,7 +516,7 @@ grub_set_mtrrs_for_acmod (struct grub_txt_acm_header *hdr) /* Enable MTRRs */ set_all_mtrrs (1); - /* Restore CR0 (cacheing) */ + /* Restore CR0 (caching) */ grub_write_control_register (GRUB_CR0, cr0); /* Restore CR4 (global pages) */ @@ -500,7 +525,7 @@ grub_set_mtrrs_for_acmod (struct grub_txt_acm_header *hdr) /* Restore flags */ grub_write_flags_register (eflags); - return GRUB_ERR_NONE; + return err; } void @@ -533,12 +558,14 @@ static void set_txt_info_ptr (struct grub_slaunch_params *slparams, struct grub_txt_os_mle_data *os_mle_data) { + struct grub_slr_table *slr_table = slparams->slr_table_mem; struct grub_slr_entry_hdr *txt_info; - txt_info = grub_slr_next_entry_by_tag ((struct grub_slr_table *)(grub_addr_t)slparams->slr_table_base, + txt_info = grub_slr_next_entry_by_tag (slr_table, NULL, GRUB_SLR_ENTRY_INTEL_INFO); - os_mle_data->txt_info = (grub_addr_t)txt_info; + os_mle_data->txt_info = (grub_addr_t) slparams->slr_table_base + + ((grub_addr_t) txt_info - (grub_addr_t) slparams->slr_table_mem); } static grub_err_t @@ -547,6 +574,7 @@ init_txt_heap (struct grub_slaunch_params *slparams, struct grub_txt_acm_header grub_uint8_t *txt_heap; grub_uint32_t os_sinit_data_ver, sinit_caps; grub_uint64_t *size; + grub_uint64_t size_total; struct grub_txt_os_mle_data *os_mle_data; struct grub_txt_os_sinit_data *os_sinit_data; struct grub_txt_heap_end_element *heap_end_element; @@ -555,12 +583,12 @@ init_txt_heap (struct grub_slaunch_params *slparams, struct grub_txt_acm_header #ifdef GRUB_MACHINE_EFI struct grub_acpi_rsdp_v20 *rsdp; #endif - struct grub_slr_txt_mtrr_state saved_mtrrs_state = {0}; grub_err_t err; /* BIOS data already verified in grub_txt_verify_platform(). */ txt_heap = grub_txt_get_heap (); + grub_dprintf ("slaunch", "TXT heap %p\n", txt_heap); /* Prepare SLR table staging area */ grub_init_slrt_storage (); @@ -568,13 +596,35 @@ init_txt_heap (struct grub_slaunch_params *slparams, struct grub_txt_acm_header /* OS/loader to MLE data. */ os_mle_data = grub_txt_os_mle_data_start (txt_heap); + grub_dprintf ("slaunch", "OS MLE data: %p\n", os_mle_data); size = (grub_uint64_t *) ((grub_addr_t) os_mle_data - sizeof (grub_uint64_t)); *size = sizeof (*os_mle_data) + sizeof (grub_uint64_t); + if (slparams->slr_table_base == GRUB_SLAUNCH_STORE_IN_OS2MLE) + { + /* SLRT needs to be at least 4-byte aligned per specification. */ + slparams->slr_table_base = + ALIGN_UP ((grub_addr_t) os_mle_data + sizeof (*os_mle_data), 4); + + /* Recompute size including SLRT table in it. */ + *size = (slparams->slr_table_base + slparams->slr_table_size) + - ((grub_addr_t) os_mle_data - sizeof (grub_uint64_t)); + + /* Size of heap sections should be a multiple of 8. */ + *size = ALIGN_UP (*size, 8); + } + + if (grub_add (grub_txt_bios_data_size (txt_heap), *size, &size_total) || + size_total > grub_txt_get_heap_size ()) + { + *size = 0; + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + N_("not enough TXT HEAP space for OsMleData")); + } + grub_memset (os_mle_data, 0, sizeof (*os_mle_data)); os_mle_data->version = GRUB_SL_OS_MLE_STRUCT_VERSION; - os_mle_data->boot_params_addr = slparams->boot_params_base; os_mle_data->slrt = slparams->slr_table_base; os_mle_data->ap_wake_block = slparams->ap_wake_block; @@ -582,17 +632,17 @@ init_txt_heap (struct grub_slaunch_params *slparams, struct grub_txt_acm_header grub_setup_slrt_log_info (slparams); - /* Save the BSPs MTRR state so post launch can restore itt */ - save_mtrrs (&saved_mtrrs_state); - - /* Setup the TXT specific SLR information and policy entry */ + /* Setup the TXT specific SLR information */ slr_intel_info_staging.hdr.tag = GRUB_SLR_ENTRY_INTEL_INFO; slr_intel_info_staging.hdr.size = sizeof(struct grub_slr_entry_intel_info); + slr_intel_info_staging.boot_params_base = slparams->boot_params_base; slr_intel_info_staging.txt_heap = (grub_addr_t)txt_heap; slr_intel_info_staging.saved_misc_enable_msr = grub_rdmsr (GRUB_MSR_X86_MISC_ENABLE); - grub_memcpy (&(slr_intel_info_staging.saved_bsp_mtrrs), &saved_mtrrs_state, - sizeof(struct grub_slr_txt_mtrr_state)); + + /* Save the BSPs MTRR state so post launch can restore it. */ + grub_dprintf ("slaunch", "Saving MTRRs to OS MLE data\n"); + save_mtrrs (&slr_intel_info_staging.saved_bsp_mtrrs); slr_intel_policy_staging.pcr = 18; slr_intel_policy_staging.entity_type = GRUB_SLR_ET_TXT_OS2MLE; @@ -607,6 +657,7 @@ init_txt_heap (struct grub_slaunch_params *slparams, struct grub_txt_acm_header /* OS/loader to SINIT data. */ + grub_dprintf ("slaunch", "Get supported OS SINIT data version\n"); os_sinit_data_ver = grub_txt_supported_os_sinit_data_ver (sinit); if (os_sinit_data_ver < OS_SINIT_DATA_MIN_VER) @@ -615,6 +666,7 @@ init_txt_heap (struct grub_slaunch_params *slparams, struct grub_txt_acm_header " expected >= %d"), os_sinit_data_ver, OS_SINIT_DATA_MIN_VER); os_sinit_data = grub_txt_os_sinit_data_start (txt_heap); + grub_dprintf ("slaunch", "OS SINIT data: %p\n", os_sinit_data); size = (grub_uint64_t *) ((grub_addr_t) os_sinit_data - sizeof (grub_uint64_t)); *size = sizeof(grub_uint64_t) + sizeof (struct grub_txt_os_sinit_data) + @@ -627,15 +679,23 @@ init_txt_heap (struct grub_slaunch_params *slparams, struct grub_txt_acm_header else return grub_error (GRUB_ERR_BAD_DEVICE, N_("unsupported TPM version")); + if (grub_add (size_total, *size, &size_total) || + size_total > grub_txt_get_heap_size ()) + { + *size = 0; + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + N_("not enough TXT HEAP space for OsSinitData")); + } + grub_memset (os_sinit_data, 0, *size); #ifdef GRUB_MACHINE_EFI rsdp = grub_acpi_get_rsdpv2 (); if (rsdp == NULL) - return grub_printf ("WARNING: ACPI RSDP 2.0 missing\n"); + return grub_error (GRUB_ERR_BAD_DEVICE, N_("ACPI RSDP 2.0 missing\n")); - os_sinit_data->efi_rsdt_ptr = (grub_uint64_t)(grub_addr_t) rsdp; + os_sinit_data->efi_rsdp_ptr = (grub_uint64_t)(grub_addr_t) rsdp; #endif os_sinit_data->mle_ptab = slparams->mle_ptab_target; @@ -666,12 +726,23 @@ init_txt_heap (struct grub_slaunch_params *slparams, struct grub_txt_acm_header grub_dprintf ("slaunch", "SINIT capabilities 0x%08x\n", sinit_caps); - /* CBnT bits 5:4 must be 11b, since D/A mapping is the only one supported. */ - os_sinit_data->capabilities = GRUB_TXT_CAPS_TPM_12_NO_LEGACY_PCR_USAGE | - GRUB_TXT_CAPS_TPM_12_AUTH_PCR_USAGE; + /* + * In the latest TXT Software Development Guide as of now (April 2023, + * Revision 017.4) bits 4 and 5 (used to be "no legacy PCR usage" and + * "auth PCR usage" respectively) of capabilities field bit are reserved. + * Bit 4 is ignored, but it is returned as 0 by SINIT[CAPABILITIES], + * while bit 5 is forced to 1 for compatibility reasons. This is related + * to support for TPM 1.2 devices, which always had this bit set. + * + * There are TPM 2.0 platforms that will have both bits set. TXT + * specification doesn't specify when this has changed, so we can't + * reliably test for those. + */ + os_sinit_data->capabilities = GRUB_TXT_CAPS_TPM_12_AUTH_PCR_USAGE; if (grub_get_tpm_ver () == GRUB_TPM_20) { + /* CBnT bits 5:4 must be 11b, since D/A mapping is the only one supported. */ if ((sinit_caps & os_sinit_data->capabilities) != os_sinit_data->capabilities) return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Details/authorities PCR usage is not supported")); @@ -689,7 +760,12 @@ init_txt_heap (struct grub_slaunch_params *slparams, struct grub_txt_acm_header } } - /* Choose monitor RLP wakeup mechanism first. */ + /* + * APs (application processors) can't be brought up by usual INIT-SIPI-SIPI + * sequence after Measured Launch, otherwise the MLE integrity is lost. + * Choose monitor RLP (responding logical processor, fancy name for AP) wakeup + * mechanism first, if that isn't supported fall back to GETSEC[WAKEUP]. + */ if (sinit_caps & GRUB_TXT_CAPS_MONITOR_SUPPORT) os_sinit_data->capabilities |= GRUB_TXT_CAPS_MONITOR_SUPPORT; else if (sinit_caps & GRUB_TXT_CAPS_GETSEC_WAKE_SUPPORT) @@ -746,10 +822,14 @@ init_txt_heap (struct grub_slaunch_params *slparams, struct grub_txt_acm_header heap_end_element->size = sizeof (*heap_end_element); } - /* - * TODO: TXT spec: Note: BiosDataSize + OsMleDataSize + OsSinitDataSize + SinitMleDataSize - * must be less than or equal to TXT.HEAP.SIZE, TXT spec, p. 102. - */ + /* SinitMleDataSize isn't known at this point, it is crafted by SINIT ACM. + * We can only test if size field fits and hope that ACM checks the rest. */ + if (grub_add (size_total, sizeof (grub_uint64_t), &size_total) || + size_total > grub_txt_get_heap_size ()) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + N_("not enough TXT HEAP space for SinitMleDataSize")); + + grub_dprintf ("slaunch", "TXT HEAP init done\n"); return GRUB_ERR_NONE; } @@ -768,11 +848,15 @@ grub_txt_get_mle_ptab_size (grub_uint32_t mle_size) /* * #PT + 1 PT + #PD + 1 PD + 1 PDT * - * Why do we need 2 extra PTEs and PDEs? Yes, because MLE image may not + * Why do we need 2 extra PTEs and PDEs? Because MLE image may not * start and end at PTE (page) and PDE (2 MiB) boundary... */ - return ((((mle_size / GRUB_PAGE_SIZE) + 2) / 512) + 1 + - (((mle_size / (512 * GRUB_PAGE_SIZE)) + 2) / 512) + 1 + 1) * GRUB_PAGE_SIZE; + return ((((mle_size / GRUB_PAGE_SIZE) + 2) / 512) + /* Number of PTs */ + 1 + /* PT */ + (((mle_size / (512 * GRUB_PAGE_SIZE)) + 2) / 512) + /* Number of PDs */ + 1 + /* PD */ + 1) /* PDT */ + * GRUB_PAGE_SIZE; } /* Page directory and table entries only need Present set */ @@ -785,10 +869,10 @@ grub_txt_get_mle_ptab_size (grub_uint32_t mle_size) * pages that can cover up to 36M . * can only contain 4k pages * - * TODO: TXT Spec p.32; List section name and number with PT MLE requirments here. + * TODO: TXT Spec p.32; List section name and number with PT MLE requirements here. * * TODO: This function is not able to cover MLEs larger than 1 GiB. Fix it!!! - * After fixing inrease GRUB_TXT_MLE_MAX_SIZE too. + * After fixing increase GRUB_TXT_MLE_MAX_SIZE too. */ void grub_txt_setup_mle_ptab (struct grub_slaunch_params *slparams) @@ -813,6 +897,7 @@ grub_txt_setup_mle_ptab (struct grub_slaunch_params *slparams) do { + /* mle_start may be unaligned, handled by mask in MAKE_PT_MLE_ENTRY */ *pte = MAKE_PT_MLE_ENTRY(slparams->mle_start + mle_off); pte++; @@ -826,7 +911,8 @@ grub_txt_setup_mle_ptab (struct grub_slaunch_params *slparams) pde++; *pde = MAKE_PT_MLE_ENTRY(pte); } - } while (mle_off < slparams->mle_size); + /* Add one page in case mle_start isn't aligned */ + } while (mle_off - GRUB_PAGE_SIZE + 1 < slparams->mle_size); } grub_err_t @@ -961,7 +1047,6 @@ grub_txt_boot_prepare (struct grub_slaunch_params *slparams) grub_err_t err; grub_uint8_t *txt_heap; struct grub_txt_os_mle_data *os_mle_data; - struct grub_txt_mle_header *mle_header; struct grub_txt_acm_header *sinit_base; struct grub_slr_table *slrt = slparams->slr_table_mem; @@ -973,15 +1058,13 @@ grub_txt_boot_prepare (struct grub_slaunch_params *slparams) if (sinit_base == NULL) return grub_errno; + grub_dprintf ("slaunch", "Init TXT heap\n"); err = init_txt_heap (slparams, sinit_base); if (err != GRUB_ERR_NONE) return err; - /* Update the MLE header. */ - mle_header = (struct grub_txt_mle_header *)(grub_addr_t) (slparams->mle_start + slparams->mle_header_offset); - mle_header->first_valid_page = 0; - mle_header->mle_end = slparams->mle_size; + grub_dprintf ("slaunch", "TXT heap successfully prepared\n"); slparams->dce_base = (grub_uint32_t)(grub_addr_t) sinit_base; slparams->dce_size = sinit_base->size * 4; @@ -996,6 +1079,12 @@ grub_txt_boot_prepare (struct grub_slaunch_params *slparams) set_txt_info_ptr (slparams, os_mle_data); grub_tpm_relinquish_locality (0); + grub_dprintf ("slaunch", "Relinquished TPM locality 0\n"); + + grub_dprintf ("slaunch", "CPU prepared for secure launch\n"); + + if (!(grub_rdmsr (GRUB_MSR_X86_APICBASE) & GRUB_MSR_X86_APICBASE_BSP)) + return grub_error (GRUB_ERR_BAD_DEVICE, N_("secure launch must run on BSP")); return GRUB_ERR_NONE; } diff --git a/grub-core/loader/slaunch/x86_efi_linux.c b/grub-core/loader/slaunch/x86_efi_linux.c index 4ead208c7..c88ec42f6 100644 --- a/grub-core/loader/slaunch/x86_efi_linux.c +++ b/grub-core/loader/slaunch/x86_efi_linux.c @@ -59,21 +59,55 @@ sl_efi_install_slr_table (struct grub_slaunch_params *slparams) } static grub_err_t -sl_efi_locate_mle_offset (struct grub_slaunch_params *slparams, - void *kernel_addr, grub_ssize_t kernel_start) +sl_efi_load_mle_data (struct grub_slaunch_params *slparams, + void *kernel_addr, grub_ssize_t kernel_start, + bool is_linux) { struct linux_kernel_params *lh = (struct linux_kernel_params *)kernel_addr; struct linux_kernel_info kernel_info; + struct grub_txt_mle_header *mle_hdr; + grub_uint32_t mle_hdr_offset; - /* Locate the MLE header offset in kernel_info section */ - grub_memcpy ((void *)&kernel_info, - (char *)kernel_addr + kernel_start + grub_le_to_cpu32 (lh->kernel_info_offset), - sizeof (struct linux_kernel_info)); + if (is_linux) + { + /* Locate the MLE header offset in kernel_info section */ + grub_memcpy ((void *)&kernel_info, + (char *)kernel_addr + kernel_start + grub_le_to_cpu32 (lh->kernel_info_offset), + sizeof (struct linux_kernel_info)); + + if (OFFSET_OF (mle_header_offset, &kernel_info) >= grub_le_to_cpu32 (kernel_info.size)) + return grub_error (GRUB_ERR_BAD_OS, N_("not an slaunch kernel: lack of mle_header_offset")); + + mle_hdr_offset = grub_le_to_cpu32 (kernel_info.mle_header_offset); + mle_hdr = (struct grub_txt_mle_header *)((grub_addr_t)kernel_addr + slparams->mle_header_offset); + } + else + { + for (mle_hdr_offset = 0; mle_hdr_offset < 0x1000; mle_hdr_offset += 16) + { + mle_hdr = (struct grub_txt_mle_header *)((grub_addr_t)kernel_addr + mle_hdr_offset); + if (!grub_memcmp (mle_hdr->uuid, GRUB_TXT_MLE_UUID, 16)) + break; + } + + if (mle_hdr_offset >= 0x1000) + { + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("not an slaunch kernel: no MLE header found")); + } + } - if (OFFSET_OF (mle_header_offset, &kernel_info) >= grub_le_to_cpu32 (kernel_info.size)) - return grub_error (GRUB_ERR_BAD_OS, N_("not slaunch kernel: lack of mle_header_offset")); + slparams->mle_header_offset = mle_hdr_offset; + slparams->mle_entry = mle_hdr->entry_point; - slparams->mle_header_offset = grub_le_to_cpu32 (kernel_info.mle_header_offset); + if (!is_linux) { + /* + * The previous value of the field is covering the whole EFI image which + * can include a lot of useless padding. Limit the size used for measuring + * MLE to that reported by the header. Don't change the behaviour for + * Linux. + */ + slparams->mle_size = mle_hdr->mle_end - mle_hdr->mle_start; + } return GRUB_ERR_NONE; } @@ -96,29 +130,88 @@ sl_efi_txt_setup_slmem (struct grub_slaunch_params *slparams, grub_memset (slmem, 0, slmem_size); - slparams->slr_table_base = (grub_uint64_t)slmem; + slparams->slr_table_base = (grub_addr_t)slmem; slparams->slr_table_size = GRUB_EFI_PAGE_SIZE; slparams->slr_table_mem = slmem; - slparams->tpm_evt_log_base = (grub_uint64_t)(slmem + GRUB_EFI_PAGE_SIZE); + slparams->tpm_evt_log_base = (grub_addr_t)(slmem + GRUB_EFI_PAGE_SIZE); slparams->tpm_evt_log_size = GRUB_EFI_SLAUNCH_TPM_EVT_LOG_SIZE; + grub_txt_init_tpm_event_log (slmem + GRUB_EFI_PAGE_SIZE, + slparams->tpm_evt_log_size); - slparams->ap_wake_block = (grub_uint32_t)(grub_uint64_t)(slmem + GRUB_EFI_PAGE_SIZE + GRUB_EFI_SLAUNCH_TPM_EVT_LOG_SIZE); + slparams->ap_wake_block = (grub_uint32_t)(grub_addr_t)(slmem + GRUB_EFI_PAGE_SIZE + GRUB_EFI_SLAUNCH_TPM_EVT_LOG_SIZE); slparams->ap_wake_block_size = GRUB_EFI_MLE_AP_WAKE_BLOCK_SIZE; *slmem_size_out = slmem_size; return slmem; } +static void * +allocate_aligned_efi_pages (grub_efi_physical_address_t max_address, + grub_efi_uintn_t pages, + grub_efi_memory_type_t memtype, + grub_efi_uintn_t alignment) +{ + void *mem; + grub_efi_uintn_t total_pages; + grub_efi_uintn_t prefix; + grub_efi_uintn_t suffix; + grub_efi_status_t status; + grub_efi_boot_services_t *b; + grub_efi_physical_address_t address; + + b = grub_efi_system_table->boot_services; + + if (pages % alignment == 0) + { + total_pages = pages; + } + else + { + /* Overallocate and then free unused */ + total_pages = pages + alignment; + } + + address = max_address; + status = b->allocate_pages (GRUB_EFI_ALLOCATE_MAX_ADDRESS, memtype, + total_pages, &address); + if (status != GRUB_EFI_SUCCESS) + return NULL; + + if (address == 0) + { + address = max_address; + status = b->allocate_pages (GRUB_EFI_ALLOCATE_MAX_ADDRESS, memtype, + total_pages, &address); + b->free_pages (0, total_pages); + if (status != GRUB_EFI_SUCCESS) + return NULL; + } + + mem = alignment == 0 + ? (void*)address + : (void *)ALIGN_UP (address, alignment * GRUB_EFI_PAGE_SIZE); + + prefix = GRUB_EFI_BYTES_TO_PAGES ((grub_addr_t)mem - address); + if (prefix != 0) + b->free_pages (address, prefix); + + suffix = total_pages - prefix - pages; + if (suffix != 0) + b->free_pages ((grub_addr_t)mem + pages * GRUB_EFI_PAGE_SIZE, suffix); + + return mem; +} + grub_err_t grub_sl_efi_txt_setup (struct grub_slaunch_params *slparams, void *kernel_addr, - grub_efi_loaded_image_t *loaded_image) + grub_efi_loaded_image_t *loaded_image, bool is_linux) { struct linux_kernel_params *lh = (struct linux_kernel_params *)kernel_addr; - grub_uint64_t image_base = (grub_uint64_t)loaded_image->image_base; + grub_addr_t image_base = (grub_addr_t)loaded_image->image_base; grub_efi_uint64_t image_size = loaded_image->image_size; - grub_efi_physical_address_t requested; - grub_ssize_t start; + grub_efi_physical_address_t max_pmap_addr; + grub_ssize_t start = 0; grub_err_t err; void *addr; void *slmem = NULL; @@ -127,39 +220,44 @@ grub_sl_efi_txt_setup (struct grub_slaunch_params *slparams, void *kernel_addr, slparams->boot_type = GRUB_SL_BOOT_TYPE_EFI; slparams->platform_type = grub_slaunch_platform_type (); - /* - * Dummy empty boot params structure for now. EFI stub will create a boot params - * and populate it. The SL code in EFI stub will update the boot params structure - * in the OSMLE data and SLRT. - */ - slparams->boot_params = &boot_params; - slparams->boot_params_base = (grub_uint64_t)&boot_params; - - /* - * Note that while the boot params on the zero page are not used or updated during a Linux - * UEFI boot through the PE header, the values placed there in the bzImage during the build - * are still valid and can be treated as boot params for certain things. - */ - start = (lh->setup_sects + 1) * 512; + if (is_linux) + { + /* + * Dummy empty boot params structure for now. EFI stub will create a boot + * params and populate it. The SL code in EFI stub will update the boot + * params structure in the OSMLE data and SLRT. + */ + slparams->boot_params = &boot_params; + slparams->boot_params_base = (grub_addr_t)&boot_params; + + /* + * Note that while the boot params on the zero page are not used or + * updated during a Linux UEFI boot through the PE header, the values + * placed there in the bzImage during the build are still valid and can + * be treated as boot params for certain things. + */ + start = (lh->setup_sects + 1) * 512; + } - /* Allocate page tables for TXT just in front of the kernel image */ + /* Allocate page tables for TXT somewhere below the kernel image */ slparams->mle_ptab_size = grub_txt_get_mle_ptab_size (image_size); slparams->mle_ptab_size = ALIGN_UP (slparams->mle_ptab_size, GRUB_TXT_PMR_ALIGN); - requested = ALIGN_DOWN ((image_base - slparams->mle_ptab_size), - GRUB_TXT_PMR_ALIGN); - addr = grub_efi_allocate_pages_real (requested, - GRUB_EFI_BYTES_TO_PAGES(slparams->mle_ptab_size), - GRUB_EFI_ALLOCATE_ADDRESS, - GRUB_EFI_LOADER_DATA); + max_pmap_addr = ALIGN_DOWN (image_base - slparams->mle_ptab_size, + GRUB_TXT_PMR_ALIGN); + addr = allocate_aligned_efi_pages (max_pmap_addr, + GRUB_EFI_BYTES_TO_PAGES(slparams->mle_ptab_size), + GRUB_EFI_LOADER_DATA, + GRUB_EFI_BYTES_TO_PAGES(GRUB_TXT_PMR_ALIGN)); if (!addr) { - grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); + grub_dprintf ("slaunch", "failed to allocate pmap below 0x%llx\n", + (unsigned long long)max_pmap_addr); return GRUB_ERR_OUT_OF_MEMORY; } slparams->mle_ptab_mem = addr; - slparams->mle_ptab_target = (grub_uint64_t)addr; + slparams->mle_ptab_target = (grub_addr_t)addr; /* * For the MLE, skip the zero page and startup section of the binary. The MLE @@ -171,7 +269,6 @@ grub_sl_efi_txt_setup (struct grub_slaunch_params *slparams, void *kernel_addr, * header is in the startup section before the protected mode piece begins. * In legacy world this part of the image would have been stripped off. */ - slparams->mle_mem = image_base + start; slparams->mle_start = image_base + start; slparams->mle_size = image_size - start; @@ -187,54 +284,65 @@ grub_sl_efi_txt_setup (struct grub_slaunch_params *slparams, void *kernel_addr, goto fail; } - err = sl_efi_locate_mle_offset (slparams, kernel_addr, start); + err = sl_efi_load_mle_data (slparams, kernel_addr, start, is_linux); if (err != GRUB_ERR_NONE) - goto fail; + { + grub_dprintf ("slaunch", N_("failed to load MLE data")); + goto fail; + } /* Final stage for secure launch, setup TXT and install the SLR table */ err = grub_txt_boot_prepare (slparams); if (err != GRUB_ERR_NONE) - goto fail; + { + grub_dprintf ("slaunch", N_("failed to prepare TXT")); + goto fail; + } err = sl_efi_install_slr_table (slparams); if (err != GRUB_ERR_NONE) - goto fail; + { + grub_dprintf ("slaunch", N_("failed to register SLRT with UEFI")); + goto fail; + } return GRUB_ERR_NONE; fail: if (slmem && slmem_size) - grub_efi_free_pages ((grub_addr_t)slmem, slmem_size); + grub_efi_free_pages ((grub_addr_t)slmem, GRUB_EFI_BYTES_TO_PAGES(slmem_size)); - grub_efi_free_pages ((grub_addr_t)addr, slparams->mle_ptab_size); + grub_efi_free_pages ((grub_addr_t)addr, GRUB_EFI_BYTES_TO_PAGES(slparams->mle_ptab_size)); return err; } grub_err_t grub_sl_efi_skinit_setup (struct grub_slaunch_params *slparams, void *kernel_addr, - grub_efi_loaded_image_t *loaded_image) + grub_efi_loaded_image_t *loaded_image, bool is_linux) { struct linux_kernel_params *lh = (struct linux_kernel_params *)kernel_addr; - grub_uint64_t image_base = (grub_uint64_t)loaded_image->image_base; + grub_addr_t image_base = (grub_addr_t)loaded_image->image_base; grub_efi_uint64_t image_size = loaded_image->image_size; grub_uint8_t *logmem; grub_addr_t max_addr; - grub_ssize_t start; + grub_ssize_t start = 0; grub_err_t err; slparams->boot_type = GRUB_SL_BOOT_TYPE_EFI; slparams->platform_type = grub_slaunch_platform_type(); - /* See comment in TXT setup function grub_efi_slaunch_setup_txt() */ - slparams->boot_params = &boot_params; - slparams->boot_params_base = (grub_uint64_t)&boot_params; + /* See comment in TXT setup function grub_sl_efi_txt_setup () */ + if (is_linux) + { + slparams->boot_params = &boot_params; + slparams->boot_params_base = (grub_addr_t)&boot_params; - start = (lh->setup_sects + 1) * 512; + start = (lh->setup_sects + 1) * 512; + } - /* See comment in TXT setup function grub_efi_slaunch_setup_txt() */ - slparams->mle_mem = image_base + start; + /* See comment in TXT setup function grub_sl_efi_txt_setup () */ slparams->mle_start = image_base + start; slparams->mle_size = image_size - start; @@ -251,11 +359,12 @@ grub_sl_efi_skinit_setup (struct grub_slaunch_params *slparams, void *kernel_add return GRUB_ERR_OUT_OF_MEMORY; } - grub_memset (logmem, 0, GRUB_EFI_SLAUNCH_TPM_EVT_LOG_SIZE); - slparams->tpm_evt_log_base = (grub_uint64_t)logmem; + slparams->tpm_evt_log_base = (grub_addr_t)logmem; slparams->tpm_evt_log_size = GRUB_EFI_SLAUNCH_TPM_EVT_LOG_SIZE; + /* It's OK to call this for AMD SKINIT because SKL erases the log before use. */ + grub_txt_init_tpm_event_log (logmem, slparams->tpm_evt_log_size); - err = sl_efi_locate_mle_offset (slparams, kernel_addr, start); + err = sl_efi_load_mle_data (slparams, kernel_addr, start, is_linux); if (err != GRUB_ERR_NONE) goto fail; @@ -278,7 +387,7 @@ grub_sl_efi_skinit_setup (struct grub_slaunch_params *slparams, void *kernel_add return GRUB_ERR_NONE; fail: - grub_efi_free_pages ((grub_addr_t)logmem, GRUB_EFI_SLAUNCH_TPM_EVT_LOG_SIZE); + grub_efi_free_pages ((grub_addr_t)logmem, GRUB_EFI_BYTES_TO_PAGES(GRUB_EFI_SLAUNCH_TPM_EVT_LOG_SIZE)); return err; } diff --git a/grub-core/net/http.c b/grub-core/net/http.c index f389bf03d..f730655dc 100644 --- a/grub-core/net/http.c +++ b/grub-core/net/http.c @@ -26,6 +26,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); diff --git a/grub-core/normal/context.c b/grub-core/normal/context.c index ba185e915..b4baa4392 100644 --- a/grub-core/normal/context.c +++ b/grub-core/normal/context.c @@ -99,7 +99,7 @@ grub_env_new_context (int export_all) grub_err_t grub_env_context_open (void) { - return grub_env_new_context (0); + return grub_env_new_context (1); } int grub_extractor_level = 0; diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c index bd4431000..250dd8859 100644 --- a/grub-core/normal/main.c +++ b/grub-core/normal/main.c @@ -207,9 +207,10 @@ grub_normal_init_page (struct grub_term_output *term, grub_uint32_t *unicode_msg; grub_uint32_t *last_position; - grub_term_cls (term); + if (! grub_debug_is_enabled ()) + grub_term_cls (term); - msg_formatted = grub_xasprintf (_("GNU GRUB version %s"), PACKAGE_VERSION); + msg_formatted = grub_xasprintf (_("GRUB version %s"), PACKAGE_VERSION); if (!msg_formatted) return; @@ -320,7 +321,30 @@ grub_cmd_normal (struct grub_command *cmd __attribute__ ((unused)), /* Guess the config filename. It is necessary to make CONFIG static, so that it won't get broken by longjmp. */ char *config; - const char *prefix; + const char *prefix, *fw_path; + + fw_path = grub_env_get ("fw_path"); + if (fw_path) + { + config = grub_xasprintf ("%s/grub.cfg", fw_path); + if (config) + { + grub_file_t file; + + file = grub_file_open (config, GRUB_FILE_TYPE_CONFIG); + if (file) + { + grub_file_close (file); + grub_enter_normal_mode (config); + } + else + { + /* Ignore all errors. */ + grub_errno = 0; + } + grub_free (config); + } + } prefix = grub_env_get ("prefix"); if (prefix) diff --git a/grub-core/normal/menu.c b/grub-core/normal/menu.c index f24544b27..15cb98cd3 100644 --- a/grub-core/normal/menu.c +++ b/grub-core/normal/menu.c @@ -163,12 +163,92 @@ grub_menu_set_timeout (int timeout) } } +static int +menuentry_eq (const char *id, const char *spec, int limit) +{ + const char *ptr1, *ptr2; + ptr1 = id; + ptr2 = spec; + while (limit == -1 || ptr1 - id <= limit) + { + if (*ptr2 == '>' && ptr2[1] != '>' && *ptr1 == 0) + return ptr2 - spec; + if (*ptr2 == '>' && ptr2[1] != '>') + return 0; + if (*ptr2 == '>') + ptr2++; + if (*ptr1 != *ptr2) + { + if (limit > -1 && ptr1 - id == limit && !*ptr1 && grub_isspace(*ptr2)) + return ptr1 -id -1; + return 0; + } + if (*ptr1 == 0) + return ptr1 - id; + ptr1++; + ptr2++; + } + return 0; +} + +static int +get_entry_number_helper(grub_menu_t menu, + const char * const val, const char ** const tail) +{ + /* See if the variable matches the title of a menu entry. */ + int entry = -1; + grub_menu_entry_t e; + int i; + + for (i = 0, e = menu->entry_list; e; i++) + { + int l = 0; + while (val[l] && !grub_isspace(val[l])) + l++; + + if (menuentry_eq (e->id, val, l)) + { + if (tail) + *tail = val + l; + return i; + } + e = e->next; + } + + for (i = 0, e = menu->entry_list; e; i++) + { + + if (menuentry_eq (e->title, val, -1)) + { + if (tail) + *tail = NULL; + return i; + } + e = e->next; + } + + if (tail) + *tail = NULL; + + entry = (int) grub_strtoul (val, tail, 0); + if (grub_errno == GRUB_ERR_BAD_NUMBER || + (*tail && **tail && !grub_isspace(**tail))) + { + entry = -1; + if (tail) + *tail = NULL; + grub_errno = GRUB_ERR_NONE; + } + + return entry; +} + /* Get the first entry number from the value of the environment variable NAME, which is a space-separated list of non-negative integers. The entry number which is returned is stripped from the value of NAME. If no entry number can be found, -1 is returned. */ static int -get_and_remove_first_entry_number (const char *name) +get_and_remove_first_entry_number (grub_menu_t menu, const char *name) { const char *val, *tail; int entry; @@ -179,20 +259,24 @@ get_and_remove_first_entry_number (const char *name) grub_error_push (); - entry = (int) grub_strtoul (val, &tail, 0); + entry = get_entry_number_helper(menu, val, &tail); + if (!(*tail == 0 || grub_isspace(*tail))) + entry = -1; - if (grub_errno == GRUB_ERR_NONE) + if (entry >= 0) { - /* Skip whitespace to find the next digit. */ + /* Skip whitespace to find the next entry. */ while (*tail && grub_isspace (*tail)) tail++; - grub_env_set (name, tail); + if (*tail) + grub_env_set (name, tail); + else + grub_env_unset (name); } else { grub_env_unset (name); grub_errno = GRUB_ERR_NONE; - entry = -1; } grub_error_pop (); @@ -291,8 +375,6 @@ grub_menu_execute_entry(grub_menu_entry_t entry, int auto_boot) if (ptr && ptr[0] && ptr[1]) grub_env_set ("default", ptr + 1); - else - grub_env_unset ("default"); grub_script_execute_new_scope (entry->sourcecode, entry->argc, entry->args); @@ -346,7 +428,7 @@ grub_menu_execute_with_fallback (grub_menu_t menu, grub_menu_execute_entry (entry, 1); /* Deal with fallback entries. */ - while ((fallback_entry = get_and_remove_first_entry_number ("fallback")) + while ((fallback_entry = get_and_remove_first_entry_number (menu, "fallback")) >= 0) { grub_print_error (); @@ -464,35 +546,12 @@ grub_menu_register_viewer (struct grub_menu_viewer *viewer) viewers = viewer; } -static int -menuentry_eq (const char *id, const char *spec) -{ - const char *ptr1, *ptr2; - ptr1 = id; - ptr2 = spec; - while (1) - { - if (*ptr2 == '>' && ptr2[1] != '>' && *ptr1 == 0) - return 1; - if (*ptr2 == '>' && ptr2[1] != '>') - return 0; - if (*ptr2 == '>') - ptr2++; - if (*ptr1 != *ptr2) - return 0; - if (*ptr1 == 0) - return 1; - ptr1++; - ptr2++; - } -} - - /* Get the entry number from the variable NAME. */ static int get_entry_number (grub_menu_t menu, const char *name) { const char *val; + const char *tail; int entry; val = grub_env_get (name); @@ -500,38 +559,9 @@ get_entry_number (grub_menu_t menu, const char *name) return -1; grub_error_push (); - - entry = (int) grub_strtoul (val, 0, 0); - - if (grub_errno == GRUB_ERR_BAD_NUMBER) - { - /* See if the variable matches the title of a menu entry. */ - grub_menu_entry_t e = menu->entry_list; - int i; - - grub_errno = GRUB_ERR_NONE; - - for (i = 0; e; i++) - { - if (menuentry_eq (e->title, val) - || menuentry_eq (e->id, val)) - { - entry = i; - break; - } - e = e->next; - } - - if (! e) - entry = -1; - } - - if (grub_errno != GRUB_ERR_NONE) - { - grub_errno = GRUB_ERR_NONE; - entry = -1; - } - + entry = get_entry_number_helper(menu, val, &tail); + if (tail && *tail != '\0') + entry = -1; grub_error_pop (); return entry; diff --git a/grub-core/osdep/linux/blocklist.c b/grub-core/osdep/linux/blocklist.c index 2efee2c2a..902cfee53 100644 --- a/grub-core/osdep/linux/blocklist.c +++ b/grub-core/osdep/linux/blocklist.c @@ -109,7 +109,7 @@ grub_install_get_blocklist (grub_device_t root_dev, else { struct fiemap *fie2; - int i; + unsigned int i; fie2 = xmalloc (sizeof (*fie2) + fie1.fm_mapped_extents * sizeof (fie1.fm_extents[1])); diff --git a/grub-core/osdep/linux/getroot.c b/grub-core/osdep/linux/getroot.c index 7dd775d2a..b32582eb3 100644 --- a/grub-core/osdep/linux/getroot.c +++ b/grub-core/osdep/linux/getroot.c @@ -103,6 +103,14 @@ struct btrfs_ioctl_search_key grub_uint32_t unused[9]; }; +struct btrfs_ioctl_search_header { + grub_uint64_t transid; + grub_uint64_t objectid; + grub_uint64_t offset; + grub_uint32_t type; + grub_uint32_t len; +}; + struct btrfs_ioctl_search_args { struct btrfs_ioctl_search_key key; grub_uint64_t buf[(4096 - sizeof(struct btrfs_ioctl_search_key)) @@ -225,7 +233,7 @@ grub_find_root_devices_from_btrfs (const char *dir) { int fd; struct btrfs_ioctl_fs_info_args fsi; - int i, j = 0; + unsigned int i, j = 0; char **ret; fd = open (dir, 0); @@ -373,6 +381,110 @@ get_btrfs_fs_prefix (const char *mount_path) return NULL; } +int use_relative_path_on_btrfs = 0; + +static char * +get_btrfs_subvol (const char *path) +{ + struct btrfs_ioctl_ino_lookup_args args; + grub_uint64_t tree_id; + int fd = -1; + char *ret = NULL; + + fd = open (path, O_RDONLY); + + if (fd < 0) + return NULL; + + memset (&args, 0, sizeof(args)); + args.objectid = GRUB_BTRFS_TREE_ROOT_OBJECTID; + + if (ioctl (fd, BTRFS_IOC_INO_LOOKUP, &args) < 0) + goto error; + + tree_id = args.treeid; + + while (tree_id != GRUB_BTRFS_ROOT_VOL_OBJECTID) + { + struct btrfs_ioctl_search_args sargs; + struct grub_btrfs_root_backref *br; + struct btrfs_ioctl_search_header *search_header; + char *old; + grub_uint16_t len; + grub_uint64_t inode_id; + + memset (&sargs, 0, sizeof(sargs)); + + sargs.key.tree_id = 1; + sargs.key.min_objectid = tree_id; + sargs.key.max_objectid = tree_id; + + sargs.key.min_offset = 0; + sargs.key.max_offset = ~0ULL; + sargs.key.min_transid = 0; + sargs.key.max_transid = ~0ULL; + sargs.key.min_type = GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF; + sargs.key.max_type = GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF; + + sargs.key.nr_items = 1; + + if (ioctl (fd, BTRFS_IOC_TREE_SEARCH, &sargs) < 0) + goto error; + + if (sargs.key.nr_items == 0) + goto error; + + search_header = (struct btrfs_ioctl_search_header *)sargs.buf; + br = (struct grub_btrfs_root_backref *) (search_header + 1); + + len = grub_le_to_cpu16 (br->n); + inode_id = grub_le_to_cpu64 (br->inode_id); + tree_id = search_header->offset; + + old = ret; + ret = malloc (len + 1); + memcpy (ret, br->name, len); + ret[len] = '\0'; + + if (inode_id != GRUB_BTRFS_TREE_ROOT_OBJECTID) + { + char *s; + + memset(&args, 0, sizeof(args)); + args.treeid = search_header->offset; + args.objectid = inode_id; + + if (ioctl (fd, BTRFS_IOC_INO_LOOKUP, &args) < 0) + goto error; + + s = xasprintf ("%s%s", args.name, ret); + free (ret); + ret = s; + } + + if (old) + { + char *s = xasprintf ("%s/%s", ret, old); + free (ret); + free (old); + ret = s; + } + } + + close (fd); + return ret; + +error: + + if (fd >= 0) + close (fd); + if (ret) + free (ret); + + return NULL; +} + +void (*grub_find_root_btrfs_mount_path_hook)(const char *mount_path); char ** grub_find_root_devices_from_mountinfo (const char *dir, char **relroot) @@ -515,7 +627,16 @@ grub_find_root_devices_from_mountinfo (const char *dir, char **relroot) else if (grub_strcmp (entries[i].fstype, "btrfs") == 0) { ret = grub_find_root_devices_from_btrfs (dir); - fs_prefix = get_btrfs_fs_prefix (entries[i].enc_path); + if (use_relative_path_on_btrfs) + { + fs_prefix = xstrdup ("/"); + if (grub_find_root_btrfs_mount_path_hook) + grub_find_root_btrfs_mount_path_hook (entries[i].enc_path); + } + else + { + fs_prefix = get_btrfs_fs_prefix (entries[i].enc_path); + } } else if (!retry && grub_strcmp (entries[i].fstype, "autofs") == 0) { @@ -1140,6 +1261,32 @@ grub_util_get_grub_dev_os (const char *os_dev) return grub_dev; } +static void *mp = NULL; +static void +btrfs_mount_path_hook(const char *m) +{ + mp = strdup (m); +} + +char * +grub_util_get_btrfs_subvol (const char *path, char **mount_path) +{ + if (mount_path) + *mount_path = NULL; + + grub_find_root_btrfs_mount_path_hook = btrfs_mount_path_hook; + grub_free (grub_find_root_devices_from_mountinfo (path, NULL)); + grub_find_root_btrfs_mount_path_hook = NULL; + + if (!mp) + return NULL; + + if (mount_path) + *mount_path = mp; + + return get_btrfs_subvol (mp); +} + char * grub_make_system_path_relative_to_its_root_os (const char *path) { diff --git a/grub-core/osdep/unix/config.c b/grub-core/osdep/unix/config.c index 0b1f7618d..0ce0e309a 100644 --- a/grub-core/osdep/unix/config.c +++ b/grub-core/osdep/unix/config.c @@ -82,6 +82,19 @@ grub_util_load_config (struct grub_util_config *cfg) if (v) cfg->grub_distributor = xstrdup (v); + v = getenv ("SUSE_BTRFS_SNAPSHOT_BOOTING"); + if (v) + { + if (grub_strncmp(v, "true", sizeof ("true") - 1) == 0) + { + cfg->is_suse_btrfs_snapshot_enabled = 1; + } + else + { + cfg->is_suse_btrfs_snapshot_enabled = 0; + } + } + cfgfile = grub_util_get_config_filename (); if (!grub_util_is_regular (cfgfile)) return; @@ -105,8 +118,8 @@ grub_util_load_config (struct grub_util_config *cfg) *ptr++ = *iptr; } - strcpy (ptr, "'; printf \"GRUB_ENABLE_CRYPTODISK=%s\\nGRUB_DISTRIBUTOR=%s\\n\" " - "\"$GRUB_ENABLE_CRYPTODISK\" \"$GRUB_DISTRIBUTOR\""); + strcpy (ptr, "'; printf \"GRUB_ENABLE_CRYPTODISK=%s\\nGRUB_DISTRIBUTOR=%s\\nSUSE_BTRFS_SNAPSHOT_BOOTING=%s\\n\" " + "\"$GRUB_ENABLE_CRYPTODISK\" \"$GRUB_DISTRIBUTOR\" \"$SUSE_BTRFS_SNAPSHOT_BOOTING\""); argv[2] = script; argv[3] = '\0'; diff --git a/grub-core/term/at_keyboard.c b/grub-core/term/at_keyboard.c index f8a129eb7..e667d0a86 100644 --- a/grub-core/term/at_keyboard.c +++ b/grub-core/term/at_keyboard.c @@ -25,12 +25,14 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); static grub_uint8_t grub_keyboard_controller_orig; static grub_uint8_t grub_keyboard_orig_set; struct grub_ps2_state ps2_state; +static int fallback_set; static int ping_sent; @@ -86,6 +88,8 @@ at_command (grub_uint8_t data) break; return 0; } + if (i == GRUB_AT_TRIES) + grub_dprintf ("atkeyb", "at_command() timed out! (stopped after %d tries)\n", i); return (i != GRUB_AT_TRIES); } @@ -115,6 +119,21 @@ grub_keyboard_controller_read (void) #endif +static int +resend_last_result (void) +{ + grub_uint8_t ret; + keyboard_controller_wait_until_ready (); + grub_dprintf ("atkeyb", "resend_last_result: sending 0xfe\n"); + grub_outb (0xfe, KEYBOARD_REG_DATA); + ret = wait_ack (); + grub_dprintf ("atkeyb", "resend_last_result: wait_ack() returned 0x%x\n", ret); + keyboard_controller_wait_until_ready (); + ret = grub_inb (KEYBOARD_REG_DATA); + grub_dprintf ("atkeyb", "resend_last_result: read 0x%x from controller\n", ret); + return ret; +} + static int write_mode (int mode) { @@ -123,11 +142,14 @@ write_mode (int mode) { grub_uint8_t ack; keyboard_controller_wait_until_ready (); + grub_dprintf ("atkeyb", "write_mode: sending 0xf0\n"); grub_outb (0xf0, KEYBOARD_REG_DATA); keyboard_controller_wait_until_ready (); + grub_dprintf ("atkeyb", "write_mode: sending mode %d\n", mode); grub_outb (mode, KEYBOARD_REG_DATA); keyboard_controller_wait_until_ready (); ack = wait_ack (); + grub_dprintf ("atkeyb", "write_mode: wait_ack() returned 0x%x\n", ack); if (ack == GRUB_AT_NACK) continue; if (ack == GRUB_AT_ACK) @@ -135,6 +157,9 @@ write_mode (int mode) return 0; } + if (i == GRUB_AT_TRIES) + grub_dprintf ("atkeyb", "write_mode() timed out! (stopped after %d tries)\n", i); + return (i != GRUB_AT_TRIES); } @@ -142,23 +167,66 @@ static int query_mode (void) { grub_uint8_t ret; + grub_uint64_t endtime; + unsigned i; int e; + char *envvar; - e = write_mode (0); - if (!e) - return 0; + for (i = 0; i < GRUB_AT_TRIES; i++) { + grub_dprintf ("atkeyb", "query_mode: sending command to controller\n"); + e = write_mode (0); + if (!e) { + grub_dprintf ("atkeyb", "query_mode: write_mode(0) failed\n"); + return 0; + } - do { - keyboard_controller_wait_until_ready (); - ret = grub_inb (KEYBOARD_REG_DATA); - } while (ret == GRUB_AT_ACK); - /* QEMU translates the set even in no-translate mode. */ - if (ret == 0x43 || ret == 1) - return 1; - if (ret == 0x41 || ret == 2) - return 2; - if (ret == 0x3f || ret == 3) - return 3; + endtime = grub_get_time_ms () + 20; + do { + keyboard_controller_wait_until_ready (); + ret = grub_inb (KEYBOARD_REG_DATA); + grub_dprintf ("atkeyb", "query_mode/loop: read 0x%x from controller\n", ret); + } while ((ret == GRUB_AT_ACK || ret == GRUB_AT_NACK) && grub_get_time_ms () < endtime); + if (ret == 0xfe) { + grub_dprintf ("atkeyb", "query_mode: asking controller to resend last result\n"); + ret = resend_last_result(); + grub_dprintf ("atkeyb", "query_mode: read 0x%x from controller\n", ret); + } + /* QEMU translates the set even in no-translate mode. */ + if (ret == 0x43 || ret == 1) { + grub_dprintf ("atkeyb", "query_mode: controller returned 0x%x, returning 1\n", ret); + return 1; + } + if (ret == 0x41 || ret == 2) { + grub_dprintf ("atkeyb", "query_mode: controller returned 0x%x, returning 2\n", ret); + return 2; + } + if (ret == 0x3f || ret == 3) { + grub_dprintf ("atkeyb", "query_mode: controller returned 0x%x, returning 3\n", ret); + return 3; + } + grub_dprintf ("atkeyb", "query_mode: controller returned unexpected value 0x%x, retrying\n", ret); + } + + /* + * Falling here means we tried querying and the controller returned something + * we don't understand, try to use 'at_keyboard_fallback_set' if it exists, + * otherwise return 0. + */ + envvar = grub_env_get ("at_keyboard_fallback_set"); + if (envvar) { + fallback_set = grub_strtoul (envvar, 0, 10); + if ((grub_errno) || (fallback_set < 1) || (fallback_set > 3)) { + grub_dprintf ("atkeyb", "WARNING: ignoring unexpected value '%s' for '%s' variable\n", + envvar, "at_keyboard_fallback_set"); + fallback_set = 0; + } else { + grub_dprintf ("atkeyb", "query_mode: '%s' specified in environment, returning %d\n", + "at_keyboard_fallback_set", fallback_set); + } + return fallback_set; + } + grub_dprintf ("atkeyb", "WARNING: no '%s' specified in environment, returning 0\n", + "at_keyboard_fallback_set"); return 0; } @@ -167,15 +235,32 @@ set_scancodes (void) { /* You must have visited computer museum. Keyboard without scancode set knowledge. Assume XT. */ - if (!grub_keyboard_orig_set) - { - grub_dprintf ("atkeyb", "No sets support assumed\n"); - ps2_state.current_set = 1; + if (!grub_keyboard_orig_set) { + if (fallback_set) { + grub_dprintf ("atkeyb", "No sets support assumed but set forced to %d\n", fallback_set); + ps2_state.current_set = fallback_set; return; } + grub_dprintf ("atkeyb", "No sets support assumed, forcing to set 1\n"); + ps2_state.current_set = 1; + return; + } #if !USE_SCANCODE_SET - ps2_state.current_set = 1; + if (fallback_set) { + grub_dprintf ("atkeyb", "queried set is %d but set forced to %d\n", + grub_keyboard_orig_set, fallback_set); + ps2_state.current_set = fallback_set; + return; + } + + if ((grub_keyboard_controller_orig & KEYBOARD_AT_TRANSLATE) == KEYBOARD_AT_TRANSLATE) { + grub_dprintf ("atkeyb", "queried set is %d but keyboard in Translate mode, so actually in set 1\n", grub_keyboard_orig_set); + ps2_state.current_set = 1; + } else { + grub_dprintf ("atkeyb", "using queried set %d\n", grub_keyboard_orig_set); + ps2_state.current_set = grub_keyboard_orig_set; + } return; #else @@ -257,6 +342,7 @@ grub_at_keyboard_getkey (struct grub_term_input *term __attribute__ ((unused))) static void grub_keyboard_controller_init (void) { + grub_dprintf ("atkeyb", "initializing the controller\n"); ps2_state.at_keyboard_status = 0; /* Drain input buffer. */ while (1) @@ -276,7 +362,9 @@ grub_keyboard_controller_init (void) grub_keyboard_orig_set = 2; #else grub_keyboard_controller_orig = grub_keyboard_controller_read (); + grub_dprintf ("atkeyb", "grub_keyboard_controller_orig = 0x%x\n", grub_keyboard_controller_orig); grub_keyboard_orig_set = query_mode (); + grub_dprintf ("atkeyb", "grub_keyboard_orig_set = %d\n", grub_keyboard_orig_set); #endif set_scancodes (); keyboard_controller_led (ps2_state.led_status); @@ -285,11 +373,15 @@ grub_keyboard_controller_init (void) static grub_err_t grub_keyboard_controller_fini (struct grub_term_input *term __attribute__ ((unused))) { +/* In !USE_SCANCODE_SET mode, we didn't change anything, so nothing to restore */ +#if USE_SCANCODE_SET if (ps2_state.current_set == 0) return GRUB_ERR_NONE; + grub_dprintf ("atkeyb", "restoring set %d, controller 0x%x\n", grub_keyboard_orig_set, grub_keyboard_controller_orig); if (grub_keyboard_orig_set) write_mode (grub_keyboard_orig_set); grub_keyboard_controller_write (grub_keyboard_controller_orig); +#endif return GRUB_ERR_NONE; } @@ -320,7 +412,6 @@ grub_at_restore_hw (void) return GRUB_ERR_NONE; } - static struct grub_term_input grub_at_keyboard_term = { .name = "at_keyboard", diff --git a/grub2.spec.in b/grub2.spec.in new file mode 100644 index 000000000..00f365291 --- /dev/null +++ b/grub2.spec.in @@ -0,0 +1,1296 @@ +# This package calls binutils components directly and would need to pass +# in flags to enable the LTO plugins +# Disable LTO +%global _lto_cflags %{nil} + +%undefine _hardened_build +%undefine _package_note_file + +%global tarversion @VERSION@ +%undefine _missing_build_ids_terminate_build +%global _configure_gnuconfig_hack 0 + +%global efi_vendor qubes +%global efi_esp_dir /boot/efi/EFI/%{efi_vendor} +%global gnulibversion 9f48fb992a3d7e96610c4ce8be969cff2d61a01b +Name: grub2 +Epoch: 1000 +Version: @VERSION@ +Release: @REL@%{?dist} +Summary: Bootloader with support for Linux, Multiboot and more +License: GPLv3+ +URL: http://www.gnu.org/software/grub/ +Obsoletes: grub < 1:0.98 +Source0: %{name}-%{version}.tar.gz +Source3: 99-grub-mkconfig.install +Source4: http://unifoundry.com/unifont-5.1.20080820.pcf.gz +Source5: theme.tar.bz2 +Source6: gitignore +Source9: strtoull_test.c +Source10: 20-grub.install +Source12: sbat.csv.in +Source13: 99-grub-mkconfig.install + + +### begin grub.macros +# vim:filetype=spec +# Modules always contain just 32-bit code +%global _libdir %{_exec_prefix}/lib +%global _binaries_in_noarch_packages_terminate_build 0 +#%%undefine _missing_build_ids_terminate_build +%{expand:%%{!?buildsubdir:%%global buildsubdir grub2-%{tarversion}}} +%{expand:%%{!?_licensedir:%%global license %%%%doc}} + +%global _configure ../configure + +%if %{?_with_ccache: 1}%{?!_with_ccache: 0} +%global ccpath /usr/%{_lib}/ccache/gcc +%else +%global ccpath %{__cc} +%endif + +# gnulib actively ignores CFLAGS because it's terrible +%if 0%{?fedora} > 39 +%global cc_equals "CC=%{ccpath} -fPIE -Wl,-z,noexecstack -Wl,--no-warn-rwx-segments" +%else +%global cc_equals "CC=%{ccpath} -fPIE -Wl,-z,noexecstack" +%endif + +%global cflags_sed \\\ + sed \\\ + -e 's/-O. //g' \\\ + -e 's/-fplugin=annobin//g' \\\ + -e 's,-specs=[[:alnum:]/_-]*annobin[[:alnum:]_-]*,,g' \\\ + -e 's/-fstack-protector[[:alpha:]-]\\+//g' \\\ + -e 's/-[^ ]*D_FORTIFY_SOURCE=[[:digit:]][^ ]*\\+//g' \\\ + -e 's/--param=ssp-buffer-size=4//g' \\\ + -e 's/-mregparm=3/-mregparm=4/g' \\\ + -e 's/-fexceptions//g' \\\ + -e 's/-fcf-protection//g' \\\ + -e 's/-fasynchronous-unwind-tables//g' \\\ + -e 's/^/ -fno-strict-aliasing /' \\\ + %{nil} + +%global host_cflags_ %{expand:%%(echo %{build_cflags} %{?_hardening_cflags} | %{cflags_sed})} -fstack-protector-strong +%ifarch x86_64 +%global host_cflags %{host_cflags_} -fcf-protection +%else +%global host_cflags %{host_cflags_} +%endif +%global legacy_host_cflags \\\ + %{expand:%%(echo %{host_cflags} | \\\ + sed \\\ + -e 's/-m64//g' \\\ + -e 's/-mcpu=power[[:alnum:]]\\+/-mcpu=power6/g' \\\ + )} +%global efi_host_cflags %{expand:%%(echo %{host_cflags})} + +%global target_cflags %{expand:%%(echo %{build_cflags} | %{cflags_sed})} +%global legacy_target_cflags \\\ + %{expand:%%(echo %{target_cflags} | \\\ + %{cflags_sed} \\\ + -e 's/-m64//g' \\\ + -e 's/-mcpu=power[[:alnum:]]\\+/-mcpu=power6/g' \\\ + )} +%global efi_target_cflags %{expand:%%(echo %{target_cflags})} + +%global ldflags_sed \\\ + sed \\\ + -e 's,-specs=[[:alnum:]/_-]*annobin[[:alnum:]_-]*,,g' \\\ + -e 's/^$//' \\\ + %{nil} + +%global host_ldflags %{expand:%%(echo %{build_ldflags} %{?_hardening_ldflags} | %{ldflags_sed})} +%global legacy_host_ldflags \\\ + %{expand:%%(echo %{host_ldflags} | \\\ + %{ldflags_sed} \\\ + )} +%global efi_host_ldflags %{expand:%%(echo %{host_ldflags})} + +%if 0%{?fedora} > 39 +%global target_ldflags %{expand:%%(echo %{build_ldflags} -Wl,--no-warn-rwx-segments -static | %{ldflags_sed})} +%else +%global target_ldflags %{expand:%%(echo %{build_ldflags} -static | %{ldflags_sed})} +%endif +%global legacy_target_ldflags \\\ + %{expand:%%(echo %{target_ldflags} | \\\ + %{ldflags_sed} \\\ + )} +%global efi_target_ldflags %{expand:%%(echo %{target_ldflags})} + +%global with_efi_arch 0 +%global with_alt_efi_arch 0 +%global with_legacy_arch 0 +%global with_emu_arch 0 +%global emuarch %{_arch} +%global grubefiarch %{nil} +%global grublegacyarch %{nil} + +# sparc is always compiled 64 bit +%ifarch %{sparc} +%global target_cpu_name sparc64 +%global _target_platform %{target_cpu_name}-%{_vendor}-%{_target_os}%{?_gnu} +%global legacy_target_cpu_name %{_arch} +%global legacy_package_arch ieee1275 +%global platform ieee1275 +%endif +# ppc is always compiled 64 bit +%ifarch ppc ppc64 ppc64le +# GRUB emu fails to build on ppc64le +%global with_emu_arch 0 +%global target_cpu_name %{_arch} +%global legacy_target_cpu_name powerpc +%global legacy_package_arch %{_arch} +%global legacy_grub_dir powerpc-ieee1275 +%global _target_platform %{target_cpu_name}-%{_vendor}-%{_target_os}%{?_gnu} +%global platform ieee1275 +%endif + + +%global efi_only aarch64 %{arm} +%global efi_arch x86_64 ia64 %{efi_only} +%ifarch %{efi_arch} +%global with_efi_arch 1 +%else +%global with_efi_arch 0 +%endif +%ifarch %{efi_only} +%global with_efi_only 1 +%else +%global with_efi_only 0 +%endif +%{!?with_efi_arch:%global without_efi_arch 0} +%{?with_efi_arch:%global without_efi_arch 1} +%{!?with_efi_only:%global without_efi_only 0} +%{?with_efi_only:%global without_efi_only 1} + +### fixme +%ifarch aarch64 %{arm} +%global efi_modules " " +%else +%global efi_modules " backtrace chain usb usbserial_common usbserial_pl2303 usbserial_ftdi usbserial_usbdebug keylayouts at_keyboard " +%endif + +%ifarch aarch64 %{arm} +%global legacy_provides -l +%endif + +%ifarch %{ix86} +%global efiarch ia32 +%global target_cpu_name i386 +%global grub_target_name i386-efi +%global package_arch efi-ia32 + +%global legacy_target_cpu_name i386 +%global legacy_package_arch pc +%global platform pc +%endif + +%ifarch x86_64 +%global efiarch x64 +%global target_cpu_name %{_arch} +%global grub_target_name %{_arch}-efi +%global package_arch efi-x64 + +%global legacy_target_cpu_name i386 +%global legacy_package_arch pc +%global platform pc + +%global alt_efi_arch ia32 +%global alt_target_cpu_name i386 +%global alt_grub_target_name i386-efi +%global alt_platform efi +%global alt_package_arch efi-ia32 + +%global alt_efi_host_cflags %{expand:%%(echo %{efi_host_cflags})} +%global alt_efi_target_cflags \\\ + %{expand:%%(echo %{target_cflags} | \\\ + %{cflags_sed} \\\ + -e 's/-m64//g' \\\ + )} +%endif + +%ifarch aarch64 +%global emuarch arm64 +%global efiarch aa64 +%global target_cpu_name aarch64 +%global grub_target_name arm64-efi +%global package_arch efi-aa64 +%endif + +%ifarch %{arm} +%global efiarch arm +%global target_cpu_name arm +%global grub_target_name arm-efi +%global package_arch efi-arm +%global efi_target_cflags \\\ + %{expand:%%(echo %{optflags} | \\\ + %{cflags_sed} \\\ + -e 's/-march=armv7-a[[:alnum:]+-]*/&+nofp/g' \\\ + -e 's/-mfpu=[[:alnum:]-]\\+//g' \\\ + -e 's/-mfloat-abi=[[:alpha:]]\\+/-mfloat-abi=soft/g' \\\ + )} +%endif + +%global _target_platform %{target_cpu_name}-%{_vendor}-%{_target_os}%{?_gnu} +%global _alt_target_platform %{alt_target_cpu_name}-%{_vendor}-%{_target_os}%{?_gnu} + +%ifarch %{efi_arch} +%global with_efi_arch 1 +%global grubefiname grub%{efiarch}.efi +%global grubeficdname gcd%{efiarch}.efi +%global grubefiarch %{target_cpu_name}-efi +%ifarch %{ix86} +%global with_efi_modules 0 +%global without_efi_modules 1 +%else +%global with_efi_modules 1 +%global without_efi_modules 0 +%endif +%endif + +%if 0%{?alt_efi_arch:1} +%global with_alt_efi_arch 1 +%global grubaltefiname grub%{alt_efi_arch}.efi +%global grubalteficdname gcd%{alt_efi_arch}.efi +%global grubaltefiarch %{alt_target_cpu_name}-efi +%endif + +%ifnarch %{efi_only} +%global with_legacy_arch 1 +%global grublegacyarch %{legacy_target_cpu_name}-%{platform} +%global moduledir %{legacy_target_cpu_name}-%{platform} +%endif + +%global evr %{epoch}:%{version}-%{release} + +%ifarch x86_64 +%global with_efi_common 1 +%global with_legacy_modules 1 +%global with_legacy_common 1 +%else +%global with_efi_common 0 +%global with_legacy_common 1 +%global with_legacy_modules 1 +%endif + +%define define_legacy_variant() \ +%{expand:%%package %%{1}} \ +Summary: Bootloader with support for Linux, Multiboot, and more \ +Provides: grub2 = %{evr} \ +Obsoletes: grub2 < %{evr} \ +Requires: grub2-common = %{evr} \ +Requires: grub2-tools-minimal = %{evr} \ +Requires: grub2-%{1}-modules = %{evr} \ +Requires: gettext which file \ +Requires: grub2-tools = %{evr} \ +Requires(pre): dracut \ +Requires(post): dracut \ +%{expand:%%description %%{1}} \ +%{desc} \ +This subpackage provides support for %{1} systems. \ + \ +%{expand:%%{?!buildsubdir:%%define buildsubdir grub-%%{1}-%{tarversion}}}\ +%{expand:%%if 0%%{with_legacy_modules} \ +%%package %%{1}-modules \ +Summary: Modules used to build custom grub images \ +BuildArch: noarch \ +Requires: grub2-common = %%{evr} \ +%%description %%{1}-modules \ +%%{desc} \ +This subpackage provides support for rebuilding your own grub.efi. \ +%%endif \ +} \ + \ +%{expand:%%{?!buildsubdir:%%define buildsubdir grub-%%{1}-%{tarversion}}}\ +%{expand:%%package %%{1}-tools} \ +Summary: Support tools for GRUB. \ +Requires: gettext which file system-logos \ +Requires: grub2-common = %{evr} \ +Requires: grub2-tools-minimal = %{evr} \ +Requires: gettext which file \ + \ +%{expand:%%description %%{1}-tools} \ +%{desc} \ +This subpackage provides tools for support of %%{1} platforms. \ +%{nil} + +%define define_efi_variant(o) \ +%{expand:%%package %{1}} \ +Summary: GRUB for EFI systems. \ +Requires: efi-filesystem \ +Requires: grub2-common = %{evr} \ +Requires: grub2-tools-minimal >= %{evr} \ +Requires: grub2-tools = %{evr} \ +Provides: grub2-efi = %{evr} \ +%{?legacy_provides:Provides: grub2 = %{evr}} \ +%{-o:Obsoletes: grub2-efi < %{evr}} \ + \ +%{expand:%%description %{1}} \ +%{desc} \ +This subpackage provides support for %{1} systems. \ + \ +%{expand:%%{?!buildsubdir:%%define buildsubdir grub-%{1}-%{tarversion}}}\ +%{expand:%if 0%{?with_efi_modules} \ +%{expand:%%package %{1}-modules} \ +Summary: Modules used to build custom grub.efi images \ +BuildArch: noarch \ +Requires: grub2-common = %{evr} \ +Provides: grub2-efi-modules = %{evr} \ +Obsoletes: grub2-efi-modules < %{evr} \ +%{expand:%%description %{1}-modules} \ +%{desc} \ +This subpackage provides support for rebuilding your own grub.efi. \ +%endif} \ + \ +%{expand:%%package %{1}-cdboot} \ +Summary: Files used to boot removeable media with EFI \ +Requires: grub2-common = %{evr} \ +Provides: grub2-efi-cdboot = %{evr} \ +%{expand:%%description %{1}-cdboot} \ +%{desc} \ +This subpackage provides optional components of grub used with removeable media on %{1} systems.\ +%{nil} + +%global do_common_setup() \ +%setup -q -n %{name}-%{tarversion} \ +rm -fv docs/*.info \ +cp %{SOURCE6} .gitignore \ +cp %{SOURCE9} ./grub-core/tests/strtoull_test.c \ +tar -zxf gnulib-%{gnulibversion}.tar.gz \ +mv gnulib-%{gnulibversion} gnulib \ +git init \ +echo '![[:digit:]][[:digit:]]_*.in' > util/grub.d/.gitignore \ +echo '!*.[[:digit:]]' > util/.gitignore \ +echo '!config.h' > include/grub/emu/.gitignore \ +git config user.email "grub2-owner@fedoraproject.org" \ +git config user.name "Fedora Ninjas" \ +git config gc.auto 0 \ +rm -f configure \ +git add . \ +git commit -a -q -m "%{tarversion} baseline." \ +#git apply --index --whitespace=nowarn %{SOURCE3} \ +#git commit -a -q -m "%{tarversion} master." \ +git am --whitespace=nowarn %%{patches} %{1}.conf \ +install -d -m 755 ${RPM_BUILD_ROOT}/etc/dnf/protected.d/ \ +install -m 644 %{1}.conf ${RPM_BUILD_ROOT}/etc/dnf/protected.d/ \ +rm -f %{1}.conf \ +%{nil} + +%ifarch x86_64 aarch64 %{arm} +%define mkimage() \ +%{4}./grub-mkimage -O %{1} -o %{2}.orig \\\ + -p /EFI/%{efi_vendor} -d grub-core ${GRUB_MODULES} \\\ + --sbat %{4}./sbat.csv \ +%{4}./grub-mkimage -O %{1} -o %{3}.orig \\\ + -p /EFI/BOOT -d grub-core ${GRUB_MODULES} \\\ + --sbat %{4}./sbat.csv \ +%{expand:%%define ___pesign_client_cert %{?___pesign_client_cert}%{!?___pesign_client_cert:%{__pesign_client_cert}}} \ +%{?__pesign_client_cert:%{expand:%%define __pesign_client_cert %{___pesign_client_cert}}} \ +%{expand:%%{pesign -s -i %%{2}.orig -o %%{2}.onesig -a %%{5} -c %%{6} -n %%{7}}} \ +%{expand:%%{pesign -s -i %%{3}.orig -o %%{3}.onesig -a %%{5} -c %%{6} -n %%{7}}} \ +%{expand:%%define __pesign_client_cert grub2-signer} \ +%{expand:%%{pesign -s -i %%{2}.onesig -o %%{2} -a %%{5} -c %%{6} -n %%{7}}} \ +%{expand:%%{pesign -s -i %%{3}.onesig -o %%{3} -a %%{5} -c %%{6} -n %%{7}}} \ +%{nil} +%else +%define mkimage() \ +%{4}./grub-mkimage -O %{1} -o %{2} \\\ + -p /EFI/%{efi_vendor} -d grub-core ${GRUB_MODULES} \ +%{4}./grub-mkimage -O %{1} -o %{3} \\\ + -p /EFI/BOOT -d grub-core ${GRUB_MODULES} \ +%{nil} +%endif + +### QUBES OS: -blscfg -efi_netns -version +multiboot +multiboot2 + +%define do_efi_build_images() \ +GRUB_MODULES=" all_video boot btrfs \\\ + cat configfile cryptodisk \\\ + echo efifwsetup efinet ext2 f2fs \\\ + fat font gcry_rijndael gcry_rsa gcry_serpent \\\ + gcry_sha256 gcry_twofish gcry_whirlpool \\\ + gfxmenu gfxterm gzio \\\ + halt hfsplus http increment iso9660 jpeg \\\ + loadenv loopback linux lvm lsefi lsefimmap luks \\\ + luks2 mdraid09 mdraid1x minicmd net \\\ + multiboot multiboot2 \\\ + normal part_apple part_msdos part_gpt \\\ + password_pbkdf2 pgp png reboot \\\ + regexp search search_fs_uuid search_fs_file \\\ + search_label serial sleep syslinuxcfg test tftp \\\ + video xfs zstd " \ +GRUB_MODULES+=%{efi_modules} \ +%{expand:%%{mkimage %{1} %{2} %{3} %{4}}} \ +%{nil} + +%define do_primary_efi_build() \ +cd %{name}-%{1}-%{tarversion} \ +%{expand:%%do_efi_configure %%{4} %%{5} %%{6}} \ +%do_efi_build_all \ +%{expand:%%do_efi_build_images %{grub_target_name} %{2} %{3} ./ } \ +cd .. \ +%{nil} + +%define do_alt_efi_build() \ +cd %{name}-%{1}-%{tarversion} \ +%{expand:%%do_efi_configure %%{4} %%{5} %%{6}} \ +%do_efi_build_modules \ +%{expand:%%do_efi_link_utils %{grubefiarch}} \ +%{expand:%%do_efi_build_images %{alt_grub_target_name} %{2} %{3} ../%{name}-%{grubefiarch}-%{tarversion}/ } \ +cd .. \ +%{nil} + +%define do_legacy_build() \ +cd %{name}-%{1}-%{tarversion} \ +%configure \\\ + %{cc_equals} \\\ + HOST_CFLAGS="%{legacy_host_cflags}" \\\ + HOST_CPPFLAGS="-I$(pwd)" \\\ + HOST_LDFLAGS="%{legacy_host_ldflags}" \\\ + TARGET_CFLAGS="%{legacy_target_cflags}" \\\ + TARGET_CPPFLAGS="-I$(pwd)" \\\ + TARGET_LDFLAGS="%{legacy_target_ldflags}" \\\ + CFLAGS= \\\ + --with-platform=%{platform} \\\ + --with-utils=host \\\ + --target=%{_target_platform} \\\ + --with-grubdir=grub2 \\\ + --program-transform-name=s,grub,grub2, \\\ + --disable-werror || ( cat config.log ; exit 1 ) \ +CFLAGS= \ +CPPFLAGS= \ +git add . \ +git commit -m "After legacy configure" \ +make %{?_smp_mflags} \ +cd .. \ +%{nil} + +%define do_emu_build() \ +cd %{name}-emu-%{tarversion} \ +%configure \\\ + %{cc_equals} \\\ + HOST_CFLAGS="%{legacy_host_cflags}" \\\ + HOST_CPPFLAGS="-I$(pwd)" \\\ + HOST_LDFLAGS="%{legacy_host_ldflags}" \\\ + --with-platform=emu \\\ + --with-grubdir=grub2 \\\ + --program-transform-name=s,grub,grub2, \\\ + --disable-werror || ( cat config.log ; exit 1 ) \ +git add . \ +git commit -m "After emu configure" \ +make %{?_smp_mflags} ascii.h widthspec.h \ +make %{?_smp_mflags} -C grub-core/lib/gnulib \ +make %{?_smp_mflags} -C grub-core \ +cd .. \ +%{nil} + +%define do_alt_efi_install() \ +cd %{name}-%{1}-%{tarversion} \ +install -d -m 755 $RPM_BUILD_ROOT/usr/lib/grub/%{grubaltefiarch}/ \ +find . '(' -iname gdb_grub \\\ + -o -iname kernel.exec \\\ + -o -iname kernel.img \\\ + -o -iname config.h \\\ + -o -iname gmodule.pl \\\ + -o -iname modinfo.sh \\\ + -o -iname '*.lst' \\\ + -o -iname '*.mod' \\\ + ')' \\\ + -exec cp {} $RPM_BUILD_ROOT/usr/lib/grub/%{grubaltefiarch}/ \\\; \ +find $RPM_BUILD_ROOT -type f -iname "*.mod*" -exec chmod a-x {} '\;' \ +install -m 700 %{2} $RPM_BUILD_ROOT%{efi_esp_dir}/%{2} \ +install -m 700 %{3} $RPM_BUILD_ROOT%{efi_esp_dir}/%{3} \ +%{expand:%%do_install_protected_file grub2-%{alt_package_arch}} \ +cd .. \ +%{nil} + +%define do_efi_install() \ +cd %{name}-%{1}-%{tarversion} \ +make DESTDIR=$RPM_BUILD_ROOT install \ +if [ -f $RPM_BUILD_ROOT%{_infodir}/grub.info ]; then \ + rm -f $RPM_BUILD_ROOT%{_infodir}/grub.info \ +fi \ +if [ -f $RPM_BUILD_ROOT%{_infodir}/grub-dev.info ]; then \ + rm -f $RPM_BUILD_ROOT%{_infodir}/grub-dev.info \ +fi \ +find $RPM_BUILD_ROOT -iname "*.module" -exec chmod a-x {} '\;' \ +ln -s ../boot/grub2/grub.cfg \\\ + $RPM_BUILD_ROOT%{_sysconfdir}/grub2-efi.cfg \ +install -m 700 %{2} $RPM_BUILD_ROOT%{efi_esp_dir}/%{2} \ +install -m 700 %{3} $RPM_BUILD_ROOT%{efi_esp_dir}/%{3} \ +%ifarch %{arm} \ +install -D -m 700 %{2} $RPM_BUILD_ROOT%{efi_esp_boot}/BOOTARM.EFI \ +%endif \ +install -D -m 700 unicode.pf2 \\\ + $RPM_BUILD_ROOT%{efi_esp_dir}/fonts/unicode.pf2 \ +${RPM_BUILD_ROOT}/%{_bindir}/grub2-editenv \\\ + ${RPM_BUILD_ROOT}/boot/grub2/grubenv create \ +%{expand:%%do_install_protected_file grub2-%{package_arch}} \ +cd .. \ +%{nil} + +# TODO: switch to %upper macro with rpm 4.19+ +%define upper_() %{lua:print(string.upper(rpm.expand("%{1}")))} + +# This is supposed to be case-insensitive anyway. But all-uppercase (even for +# the x in BOOTx64.EFI) is what both 'grub2-install --removable' and systemd's +# 'bootctl install' do, and it might be safer: +# https://github.com/systemd/systemd/commit/00f69504a2c1861d98a027afdebc22c873f09083 +%define efi_default_image() /boot/efi/EFI/BOOT/BOOT%{upper_ %{1}}.EFI + +%global efi_startup_script /boot/efi/startup.nsh + +%define do_fallback_efi_install() \ +cd %{name}-%{1}-%{tarversion} \ +install -D -m 700 %{2} \\\ + $RPM_BUILD_ROOT%{expand:%%efi_default_image %{3}} \ +%if "%{3}" != "%{alt_efi_arch}" \ +printf '%s\\n' '\\EFI\\%{efi_vendor}\\%{2}' > \\\ + $RPM_BUILD_ROOT%{efi_startup_script} \ +%endif \ +cd .. \ +%{nil} + +%define do_legacy_install() \ +cd %{name}-%{1}-%{tarversion} \ +make DESTDIR=$RPM_BUILD_ROOT install \ +if [ -f $RPM_BUILD_ROOT%{_infodir}/grub.info ]; then \ + rm -f $RPM_BUILD_ROOT%{_infodir}/grub.info \ +fi \ +if [ -f $RPM_BUILD_ROOT%{_infodir}/grub-dev.info ]; then \ + rm -f $RPM_BUILD_ROOT%{_infodir}/grub-dev.info \ +fi \ +if [ -f $RPM_BUILD_ROOT/%{_libdir}/grub/%{1}/grub2.chrp ]; then \ + mv $RPM_BUILD_ROOT/%{_libdir}/grub/%{1}/grub2.chrp \\\ + $RPM_BUILD_ROOT/%{_libdir}/grub/%{1}/grub.chrp \ +fi \ +if [ %{3} -eq 0 ]; then \ + ${RPM_BUILD_ROOT}/%{_bindir}/grub2-editenv \\\ + ${RPM_BUILD_ROOT}/boot/grub2/grubenv create \ +fi \ +%{expand:%%do_install_protected_file grub2-%{legacy_package_arch}} \ +cd .. \ +%{nil} + +%define do_emu_install() \ +cd %{name}-emu-%{tarversion} \ +make DESTDIR=$RPM_BUILD_ROOT install -C grub-core \ +if [ -f $RPM_BUILD_ROOT%{_infodir}/grub.info ]; then \ + rm -f $RPM_BUILD_ROOT%{_infodir}/grub.info \ +fi \ +if [ -f $RPM_BUILD_ROOT%{_infodir}/grub-dev.info ]; then \ + rm -f $RPM_BUILD_ROOT%{_infodir}/grub-dev.info \ +fi \ +if [ -f $RPM_BUILD_ROOT/%{_libdir}/grub/%{1}/grub2.chrp ]; then \ + mv $RPM_BUILD_ROOT/%{_libdir}/grub/%{1}/grub2.chrp \\\ + $RPM_BUILD_ROOT/%{_libdir}/grub/%{1}/grub.chrp \ +fi \ +cd .. \ +%{nil} + +%define do_common_install() \ +install -d -m 0755 \\\ + $RPM_BUILD_ROOT%{_datarootdir}/locale/en\@quot \\\ + $RPM_BUILD_ROOT%{_datarootdir}/locale/en \\\ + $RPM_BUILD_ROOT%{_infodir}/ \ +cp -a $RPM_BUILD_ROOT%{_datarootdir}/locale/en\@quot \\\ + $RPM_BUILD_ROOT%{_datarootdir}/locale/en \ +cp docs/grub.info $RPM_BUILD_ROOT%{_infodir}/grub2.info \ +cp docs/grub-dev.info \\\ + $RPM_BUILD_ROOT%{_infodir}/grub2-dev.info \ +install -d -m 0700 ${RPM_BUILD_ROOT}%{efi_esp_dir}/ \ +install -d -m 0700 ${RPM_BUILD_ROOT}/boot/grub2/ \ +install -d -m 0700 ${RPM_BUILD_ROOT}/boot/loader/entries \ +install -d -m 0700 ${RPM_BUILD_ROOT}/boot/grub2/themes/system \ +install -d -m 0700 ${RPM_BUILD_ROOT}%{_sysconfdir}/default \ +install -d -m 0700 ${RPM_BUILD_ROOT}%{_sysconfdir}/sysconfig \ +touch ${RPM_BUILD_ROOT}%{_sysconfdir}/default/grub \ +ln -sf ../default/grub \\\ + ${RPM_BUILD_ROOT}%{_sysconfdir}/sysconfig/grub \ +touch ${RPM_BUILD_ROOT}/boot/grub2/grub.cfg \ +ln -s ../boot/grub2/grub.cfg \\\ + ${RPM_BUILD_ROOT}%{_sysconfdir}/grub2.cfg \ +%{nil} + +%define define_legacy_variant_files() \ +%{expand:%%files %{1}} \ +%defattr(-,root,root,-) \ +%config(noreplace) %{_sysconfdir}/grub2.cfg \ +%ghost %config(noreplace) /boot/grub2/grub.cfg \ +%dir %attr(0700,root,root)/boot/loader/entries \ +%attr(0644,root,root) %config(noreplace) /etc/dnf/protected.d/grub2-%{1}.conf \ + \ +%{expand:%if 0%{?with_legacy_modules} \ +%{expand:%%files %{1}-modules} \ +%defattr(-,root,root) \ +%dir %{_libdir}/grub/%{2}/ \ +%{_libdir}/grub/%{2}/* \ +%exclude %{_libdir}/grub/%{2}/*.module \ +%exclude %{_libdir}/grub/%{2}/{boot,boot_hybrid,cdboot,diskboot,lzma_decompress,pxeboot}.image \ +%exclude %{_libdir}/grub/%{2}/*.o \ +%else \ +%%exclude %%{_libdir}/grub/%%{grublegacyarch}/* \ +%endif} \ +%{nil} + +%define define_efi_variant_files() \ +%{expand:%%files %{1}} \ +%defattr(0700,root,root,-) \ +%config(noreplace) %{_sysconfdir}/grub2.cfg \ +%config(noreplace) %{_sysconfdir}/grub2-efi.cfg \ +%attr(0700,root,root)%{efi_esp_dir}/%{2} \ +%ifarch %{arm} \ +%attr(0700,root,root)%{efi_esp_boot}/BOOTARM.EFI \ +%endif \ +%dir %attr(0700,root,root)%{efi_esp_dir}/fonts \ +%dir %attr(0700,root,root)/boot/loader/entries \ +%ghost %config(noreplace) /boot/grub2/grub.cfg \ +%ghost %config(noreplace) %attr(0700,root,root)%{efi_esp_dir}/grub.cfg \ +%config(noreplace) %verify(not size mode md5 mtime) /boot/grub2/grubenv \ +%attr(0644,root,root) %config(noreplace) /etc/dnf/protected.d/grub2-%{1}.conf \ +%{expand:%if 0%{?without_efi_modules} \ +%exclude %{_libdir}/grub/%{6} \ +%exclude %{_libdir}/grub/%{6}/* \ +%endif} \ +%attr(0700,root,root) %config(noreplace) %{expand:%%efi_default_image %{7}} \ +%if "%{1}" != "%{alt_package_arch}" \ +%attr(0700,root,root) %config(noreplace) %{efi_startup_script} \ +%endif \ + \ +%{expand:%if 0%{?with_efi_modules} \ +%{expand:%%files %{1}-modules} \ +%defattr(-,root,root,-) \ +%dir %{_libdir}/grub/%{6}/ \ +%{_libdir}/grub/%{6}/* \ +%exclude %{_libdir}/grub/%{6}/*.module \ +%endif} \ + \ +%{expand:%%files %{1}-cdboot} \ +%defattr(0700,root,root,-) \ +%attr(0700,root,root)%{efi_esp_dir}/%{3} \ +%attr(0700,root,root)%{efi_esp_dir}/fonts \ +%{nil} +### end grub.macros + +# XXX: having this as a commit doesn't work due to some ordering issue (this +# .spec-file extracts parts of .gitignore) +Patch0010: 0010-re-write-.gitignore.patch + +BuildRequires: autoconf +BuildRequires: automake +BuildRequires: binutils +BuildRequires: bison +BuildRequires: bzip2-devel +BuildRequires: dejavu-sans-fonts +BuildRequires: device-mapper-devel +BuildRequires: efi-srpm-macros +BuildRequires: flex +BuildRequires: freetype-devel +BuildRequires: freetype-devel +BuildRequires: fuse-devel +BuildRequires: gcc +BuildRequires: gettext-devel +BuildRequires: git +BuildRequires: help2man +BuildRequires: ncurses-devel +BuildRequires: python3 +BuildRequires: rpm-devel +BuildRequires: rpm-libs +BuildRequires: squashfs-tools +BuildRequires: texinfo +BuildRequires: xz-devel + +# For %%_userunitdir and %%systemd_* macros +BuildRequires: systemd-rpm-macros + +%ifarch %{efi_arch} +BuildRequires: pesign >= 0.99-8 +%endif + +%if %{?_with_ccache: 1}%{?!_with_ccache: 0} +BuildRequires: ccache +%endif + +ExcludeArch: s390 s390x %{ix86} +Obsoletes: grub2 <= %{evr} + +%if 0%{with_legacy_arch} +Requires: grub2-%{legacy_package_arch} = %{evr} +%else +Requires: grub2-%{package_arch} = %{evr} +%endif + +%global desc \ +The GRand Unified Bootloader (GRUB) is a highly configurable and \ +customizable bootloader with modular architecture. It supports a rich \ +variety of kernel formats, file systems, computer architectures and \ +hardware devices.\ +%{nil} + +%description +%{desc} + +%package common +Summary: grub2 common layout +BuildArch: noarch +Conflicts: grubby < 8.40-18 +%if 0%{?fedora} >= 37 +Requires(posttrans): util-linux-core +%else +Requires(posttrans): util-linux +%endif +Requires(posttrans): coreutils +Requires(posttrans): grep + +%description common +This package provides some directories which are required by various grub2 +subpackages. + +%package tools +Summary: Support tools for GRUB. +Obsoletes: grub2-tools < %{evr} +Requires: grub2-common = %{epoch}:%{version}-%{release} +%if 0%{?fedora} >= 37 +Requires: gettext-runtime file +%else +Requires: gettext file +%endif +Requires(pre): dracut +Requires(post): dracut +Requires(pre): grep +Requires(pre): sed + +%description tools +%{desc} +This subpackage provides tools for support of all platforms. + +%ifarch x86_64 +%package tools-efi +Summary: Support tools for GRUB. +%if 0%{?fedora} >= 37 +Requires: gettext-runtime file +%else +Requires: gettext file +%endif +Requires: grub2-common = %{epoch}:%{version}-%{release} +Obsoletes: grub2-tools < %{evr} + +%description tools-efi +%{desc} +This subpackage provides tools for support of EFI platforms. +%endif + +%package tools-minimal +Summary: Support tools for GRUB. +%if 0%{?fedora} >= 37 +Requires: gettext-runtime +%else +Requires: gettext +%endif +Requires: grub2-common = %{epoch}:%{version}-%{release} +Obsoletes: grub2-tools < %{evr} + +%description tools-minimal +%{desc} +This subpackage provides tools for support of all platforms. + +%package tools-extra +Summary: Support tools for GRUB. +%if 0%{?fedora} >= 37 +Requires: gettext-runtime file +%else +Requires: gettext file +%endif +Requires: grub2-tools-minimal = %{epoch}:%{version}-%{release} +Requires: grub2-common = %{epoch}:%{version}-%{release} +Obsoletes: grub2-tools < %{evr} + +%description tools-extra +%{desc} +This subpackage provides tools for support of all platforms. + +%if 0%{with_efi_arch} +%{expand:%define_efi_variant %%{package_arch} -o} +%endif +%if 0%{with_alt_efi_arch} +%{expand:%define_efi_variant %%{alt_package_arch}} +%endif +%if 0%{with_legacy_arch} +%{expand:%define_legacy_variant %%{legacy_package_arch}} +%endif + +%if 0%{with_emu_arch} +%package emu +Summary: GRUB user-space emulation. +Requires: grub2-tools-minimal = %{epoch}:%{version}-%{release} + +%description emu +%{desc} +This subpackage provides the GRUB user-space emulation support of all platforms. + +%package emu-modules +Summary: GRUB user-space emulation modules. +Requires: grub2-tools-minimal = %{epoch}:%{version}-%{release} + +%description emu-modules +%{desc} +This subpackage provides the GRUB user-space emulation modules. +%endif + +%prep +%do_common_setup +%if 0%{with_efi_arch} +mkdir %{name}-%{grubefiarch}-%{tarversion} +grep -A100000 '# stuff "make" creates' .gitignore > %{name}-%{grubefiarch}-%{tarversion}/.gitignore +cp %{SOURCE4} %{name}-%{grubefiarch}-%{tarversion}/unifont.pcf.gz +sed -e "s,@@VERSION@@,%{version},g" -e "s,@@VERSION_RELEASE@@,%{version}-%{release},g" \ + %{SOURCE12} > %{name}-%{grubefiarch}-%{tarversion}/sbat.csv +git add %{name}-%{grubefiarch}-%{tarversion} +%endif +%if 0%{with_alt_efi_arch} +mkdir %{name}-%{grubaltefiarch}-%{tarversion} +grep -A100000 '# stuff "make" creates' .gitignore > %{name}-%{grubaltefiarch}-%{tarversion}/.gitignore +cp %{SOURCE4} %{name}-%{grubaltefiarch}-%{tarversion}/unifont.pcf.gz +git add %{name}-%{grubaltefiarch}-%{tarversion} +%endif +%if 0%{with_legacy_arch} +mkdir %{name}-%{grublegacyarch}-%{tarversion} +grep -A100000 '# stuff "make" creates' .gitignore > %{name}-%{grublegacyarch}-%{tarversion}/.gitignore +cp %{SOURCE4} %{name}-%{grublegacyarch}-%{tarversion}/unifont.pcf.gz +git add %{name}-%{grublegacyarch}-%{tarversion} +%endif +%if 0%{with_emu_arch} +mkdir %{name}-emu-%{tarversion} +grep -A100000 '# stuff "make" creates' .gitignore > %{name}-emu-%{tarversion}/.gitignore +cp %{SOURCE4} %{name}-emu-%{tarversion}/unifont.pcf.gz +git add %{name}-emu-%{tarversion} +%endif +git commit -m "After making subdirs" + +%build +%if 0%{with_efi_arch} +%{expand:%do_primary_efi_build %%{grubefiarch} %%{grubefiname} %%{grubeficdname} %%{_target_platform} %%{efi_target_cflags} %%{efi_host_cflags}} +%endif +%if 0%{with_alt_efi_arch} +%{expand:%do_alt_efi_build %%{grubaltefiarch} %%{grubaltefiname} %%{grubalteficdname} %%{_alt_target_platform} %%{alt_efi_target_cflags} %%{alt_efi_host_cflags}} +%endif +%if 0%{with_legacy_arch} +%{expand:%do_legacy_build %%{grublegacyarch}} +%endif +%if 0%{with_emu_arch} +%{expand:%do_emu_build} +%endif +%ifarch ppc64le +%{expand:%do_ieee1275_build_images %%{grublegacyarch} %{grubelfname} %{sb_cer} %{sb_key}} +%endif +makeinfo --info --no-split -I docs -o docs/grub-dev.info \ + docs/grub-dev.texi +makeinfo --info --no-split -I docs -o docs/grub.info \ + docs/grub.texi +makeinfo --html --no-split -I docs -o docs/grub-dev.html \ + docs/grub-dev.texi +makeinfo --html --no-split -I docs -o docs/grub.html \ + docs/grub.texi + +%install +set -e +rm -fr $RPM_BUILD_ROOT + +%do_common_install +%if 0%{with_efi_arch} +%{expand:%do_efi_install %%{grubefiarch} %%{grubefiname} %%{grubeficdname}} +%{expand:%do_fallback_efi_install %%{grubefiarch} %%{grubefiname} %%{efiarch}} +%endif +%if 0%{with_alt_efi_arch} +%{expand:%do_alt_efi_install %%{grubaltefiarch} %%{grubaltefiname} %%{grubalteficdname}} +%{expand:%do_fallback_efi_install %%{grubaltefiarch} %%{grubaltefiname} %%{alt_efi_arch}} +%endif +%if 0%{with_legacy_arch} +%{expand:%do_legacy_install %%{grublegacyarch} %%{alt_grub_target_name} 0%{with_efi_arch}} +%endif +%if 0%{with_emu_arch} +%{expand:%do_emu_install %%{package_arch}} +%endif +rm -f $RPM_BUILD_ROOT%{_infodir}/dir +ln -s grub2-set-password ${RPM_BUILD_ROOT}/%{_sbindir}/grub2-setpassword +echo '.so man8/grub2-set-password.8' > ${RPM_BUILD_ROOT}/%{_datadir}/man/man8/grub2-setpassword.8 +%ifnarch x86_64 +rm -vf ${RPM_BUILD_ROOT}/%{_bindir}/grub2-render-label +rm -vf ${RPM_BUILD_ROOT}/%{_sbindir}/grub2-bios-setup +rm -vf ${RPM_BUILD_ROOT}/%{_sbindir}/grub2-macbless +%endif +%{expand:%%do_install_protected_file grub2-tools-minimal} + +# XXX: uncommenting the next line breaks the build (changing to "grub2" doesn't help) +#%find_lang grub + +# Make selinux happy with exec stack binaries. +mkdir ${RPM_BUILD_ROOT}%{_sysconfdir}/prelink.conf.d/ +cat << EOF > ${RPM_BUILD_ROOT}%{_sysconfdir}/prelink.conf.d/grub2.conf +# these have execstack, and break under selinux +-b /usr/bin/grub2-script-check +-b /usr/bin/grub2-mkrelpath +-b /usr/bin/grub2-mount +-b /usr/bin/grub2-fstest +-b /usr/sbin/grub2-bios-setup +-b /usr/sbin/grub2-probe +-b /usr/sbin/grub2-sparc64-setup +EOF + +# Install kernel-install scripts +install -d -m 0755 %{buildroot}%{_prefix}/lib/kernel/install.d/ +install -D -m 0755 -t %{buildroot}%{_prefix}/lib/kernel/install.d/ %{SOURCE10} +install -D -m 0755 -t %{buildroot}%{_prefix}/lib/kernel/install.d/ %{SOURCE3} +install -d -m 0755 %{buildroot}%{_sysconfdir}/kernel/install.d/ +# Install systemd user service to set the boot_success flag +install -D -m 0755 -t %{buildroot}%{_userunitdir} \ + docs/grub-boot-success.{timer,service} +install -d -m 0755 %{buildroot}%{_userunitdir}/timers.target.wants +ln -s ../grub-boot-success.timer \ + %{buildroot}%{_userunitdir}/timers.target.wants +# Install systemd system-update unit to set boot_indeterminate for offline-upd +install -D -m 0755 -t %{buildroot}%{_unitdir} docs/grub-boot-indeterminate.service +install -d -m 0755 %{buildroot}%{_unitdir}/system-update.target.wants +install -d -m 0755 %{buildroot}%{_unitdir}/reboot.target.wants +ln -s ../grub-boot-indeterminate.service \ + %{buildroot}%{_unitdir}/system-update.target.wants +ln -s ../grub2-systemd-integration.service \ + %{buildroot}%{_unitdir}/reboot.target.wants + +# Don't run debuginfo on all the grub modules and whatnot; it just +# rejects them, complains, and slows down extraction. +%global finddebugroot "%{_builddir}/%{?buildsubdir}/debug" + +%global dip RPM_BUILD_ROOT=%{finddebugroot} %{__debug_install_post} +%define __debug_install_post ( \ + mkdir -p %{finddebugroot}/usr \ + mv ${RPM_BUILD_ROOT}/usr/bin %{finddebugroot}/usr/bin \ + mv ${RPM_BUILD_ROOT}/usr/sbin %{finddebugroot}/usr/sbin \ + %{dip} \ + install -m 0755 -d %{buildroot}/usr/lib/ %{buildroot}/usr/src/ \ + cp -al %{finddebugroot}/usr/lib/debug/ \\\ + %{buildroot}/usr/lib/debug/ \ + cp -al %{finddebugroot}/usr/src/debug/ \\\ + %{buildroot}/usr/src/debug/ ) \ + mv %{finddebugroot}/usr/bin %{buildroot}/usr/bin \ + mv %{finddebugroot}/usr/sbin %{buildroot}/usr/sbin \ + %{nil} + +%undefine buildsubdir + +%pre tools +if [ -f /boot/grub2/user.cfg ]; then + if grep -q '^GRUB_PASSWORD=' /boot/grub2/user.cfg ; then + sed -i 's/^GRUB_PASSWORD=/GRUB2_PASSWORD=/' /boot/grub2/user.cfg + fi +elif [ -f %{efi_esp_dir}/user.cfg ]; then + if grep -q '^GRUB_PASSWORD=' %{efi_esp_dir}/user.cfg ; then + sed -i 's/^GRUB_PASSWORD=/GRUB2_PASSWORD=/' \ + %{efi_esp_dir}/user.cfg + fi +elif [ -f /etc/grub.d/01_users ] && \ + grep -q '^password_pbkdf2 root' /etc/grub.d/01_users ; then + if [ -f %{efi_esp_dir}/grub.cfg ]; then + # on EFI we don't get permissions on the file, but + # the directory is protected. + grep '^password_pbkdf2 root' /etc/grub.d/01_users | \ + sed 's/^password_pbkdf2 root \(.*\)$/GRUB2_PASSWORD=\1/' \ + > %{efi_esp_dir}/user.cfg + fi + if [ -f /boot/grub2/grub.cfg ]; then + install -m 0600 /dev/null /boot/grub2/user.cfg + chmod 0600 /boot/grub2/user.cfg + grep '^password_pbkdf2 root' /etc/grub.d/01_users | \ + sed 's/^password_pbkdf2 root \(.*\)$/GRUB2_PASSWORD=\1/' \ + > /boot/grub2/user.cfg + fi +fi + +%triggerun -- grub2 < 1:1.99-4 +# grub2 < 1.99-4 removed a number of essential files in postun. To fix upgrades +# from the affected grub2 packages, we first back up the files in triggerun and +# later restore them in triggerpostun. +# https://bugzilla.redhat.com/show_bug.cgi?id=735259 + +# Back up the files before uninstalling old grub2 +mkdir -p /boot/grub2.tmp && +mv -f /boot/grub2/*.mod \ + /boot/grub2/*.img \ + /boot/grub2/*.lst \ + /boot/grub2/device.map \ + /boot/grub2.tmp/ || : + +%triggerpostun -- grub2 < 1:1.99-4 +# ... and restore the files. +test ! -f /boot/grub2/device.map && +test -d /boot/grub2.tmp && +mv -f /boot/grub2.tmp/*.mod \ + /boot/grub2.tmp/*.img \ + /boot/grub2.tmp/*.lst \ + /boot/grub2.tmp/device.map \ + /boot/grub2/ && +rm -r /boot/grub2.tmp/ || : + +%posttrans common +set -eu + +EFI_HOME=%{efi_esp_dir} +GRUB_HOME=/boot/grub2 +ESP_PATH=/boot/efi + +if ! mountpoint -q ${ESP_PATH}; then + exit 0 # no ESP mounted, nothing to do +fi + +if test ! -f ${EFI_HOME}/grub.cfg; then + # there's no config in ESP, create one + grub2-mkconfig -o ${EFI_HOME}/grub.cfg + cp -a ${EFI_HOME}/grub.cfg ${EFI_HOME}/grub.cfg.rpmsave + cp -a ${EFI_HOME}/grub.cfg ${GRUB_HOME}/ +fi + +if (((grep -q "configfile" ${EFI_HOME}/grub.cfg && grep -q "root-dev-only" ${EFI_HOME}/grub.cfg) || grep -q "source" ${EFI_HOME}/grub.cfg) && ! grep -q "# It is automatically generated by grub2-mkconfig using templates" ${EFI_HOME}/grub.cfg); then + exit 0 #Already unified +fi + +# create a stub grub2 config in EFI +BOOT_UUID=$(grub2-probe --target=fs_uuid ${GRUB_HOME}) +GRUB_DIR=$(grub2-mkrelpath ${GRUB_HOME}) + +cat << EOF > ${EFI_HOME}/grub.cfg.stb +search --no-floppy --root-dev-only --fs-uuid --set=dev ${BOOT_UUID} +set prefix=(\$dev)${GRUB_DIR} +export \$prefix +configfile \$prefix/grub.cfg +EOF + +if test -f ${EFI_HOME}/grubenv; then + cp -a ${EFI_HOME}/grubenv ${EFI_HOME}/grubenv.rpmsave + mv --force ${EFI_HOME}/grubenv ${GRUB_HOME}/grubenv +fi + +cp -a ${EFI_HOME}/grub.cfg ${EFI_HOME}/grub.cfg.rpmsave +if test ! -f ${GRUB_HOME}/grub.cfg; then + cp -a ${EFI_HOME}/grub.cfg ${GRUB_HOME}/ +fi +mv ${EFI_HOME}/grub.cfg.stb ${EFI_HOME}/grub.cfg + +# XXX: the following line is missing `-f grub.lang` at the end +%files common +%dir %{_libdir}/grub/ +%dir %{_datarootdir}/grub/ +%attr(0700,root,root) %dir %{_sysconfdir}/grub.d +%{_prefix}/lib/kernel/install.d/20-grub.install +%{_prefix}/lib/kernel/install.d/99-grub-mkconfig.install +%dir %{_datarootdir}/grub +%exclude %{_datarootdir}/grub/* +%dir /boot/grub2 +%dir /boot/grub2/themes/ +%dir /boot/grub2/themes/system +%attr(0700,root,root) %dir /boot/grub2 +%exclude /boot/grub2/* +%dir %attr(0700,root,root) %{efi_esp_dir} +%exclude %{efi_esp_dir}/* +%ghost %config(noreplace) %verify(not size mode md5 mtime) /boot/grub2/grubenv +%license COPYING +%doc THANKS +%doc docs/grub.html +%doc docs/grub-dev.html +%doc docs/font_char_metrics.png + +%files tools-minimal +%{_sysconfdir}/prelink.conf.d/grub2.conf +%{_sbindir}/grub2-get-kernel-settings +%{_sbindir}/grub2-probe +%attr(4755, root, root) %{_sbindir}/grub2-set-bootflag +%{_sbindir}/grub2-set-default +%{_sbindir}/grub2-set*password +%{_bindir}/grub2-editenv +%{_bindir}/grub2-mkpasswd-pbkdf2 +%{_bindir}/grub2-mount +%attr(0644,root,root) %config(noreplace) /etc/dnf/protected.d/grub2-tools-minimal.conf + +%{_datadir}/man/man3/grub2-get-kernel-settings* +%{_datadir}/man/man8/grub2-set-default* +%{_datadir}/man/man8/grub2-set*password* +%{_datadir}/man/man1/grub2-editenv* +%{_datadir}/man/man1/grub2-mkpasswd-* + +%ifarch x86_64 +%files tools-efi +%{_bindir}/grub2-glue-efi +%{_bindir}/grub2-render-label +%{_sbindir}/grub2-macbless +%{_datadir}/man/man1/grub2-glue-efi* +%{_datadir}/man/man1/grub2-render-label* +%{_datadir}/man/man8/grub2-macbless* +%endif + +%files tools +%attr(0644,root,root) %ghost %config(noreplace) %{_sysconfdir}/default/grub +%config %{_sysconfdir}/grub.d/??_* +%{_sysconfdir}/grub.d/README +%{_userunitdir}/grub-boot-success.timer +%{_userunitdir}/grub-boot-success.service +%{_userunitdir}/timers.target.wants +%{_unitdir}/grub-boot-indeterminate.service +%{_unitdir}/system-update.target.wants +%{_unitdir}/grub2-systemd-integration.service +%{_unitdir}/reboot.target.wants +%{_unitdir}/systemd-logind.service.d +%{_infodir}/grub* +%{_datarootdir}/grub/* +%{_sbindir}/grub2-install +%exclude %{_datarootdir}/grub/themes +%exclude %{_datarootdir}/grub/*.h +%{_datarootdir}/bash-completion/completions/grub* +%{_sbindir}/grub2-mkconfig +%{_sbindir}/grub2-reboot +%{_bindir}/grub2-file +%{_bindir}/grub2-menulst2cfg +%{_bindir}/grub2-mkimage +%{_bindir}/grub2-mkrelpath +%{_bindir}/grub2-script-check +%{_libexecdir}/grub2 +%{_datadir}/man/man?/* + +# exclude man pages from tools-extra +%exclude %{_datadir}/man/man8/grub2-sparc64-setup* +%exclude %{_datadir}/man/man1/grub2-fstest* +%exclude %{_datadir}/man/man1/grub2-glue-efi* +%exclude %{_datadir}/man/man1/grub2-kbdcomp* +%exclude %{_datadir}/man/man1/grub2-mkfont* +%exclude %{_datadir}/man/man1/grub2-mklayout* +%exclude %{_datadir}/man/man1/grub2-mknetdir* +%exclude %{_datadir}/man/man1/grub2-mkrescue* +%exclude %{_datadir}/man/man1/grub2-mkstandalone* +%exclude %{_datadir}/man/man1/grub2-syslinux2cfg* + +# exclude man pages from tools-minimal +%exclude %{_datadir}/man/man3/grub2-get-kernel-settings* +%exclude %{_datadir}/man/man8/grub2-set-default* +%exclude %{_datadir}/man/man8/grub2-set*password* +%exclude %{_datadir}/man/man1/grub2-editenv* +%exclude %{_datadir}/man/man1/grub2-mkpasswd-* +%exclude %{_datadir}/man/man8/grub2-macbless* +%exclude %{_datadir}/man/man1/grub2-render-label* + +%if %{with_legacy_arch} +%{_sbindir}/grub2-install +%ifarch x86_64 +%{_sbindir}/grub2-bios-setup +%else +%exclude %{_sbindir}/grub2-bios-setup +%exclude %{_datadir}/man/man8/grub2-bios-setup* +%endif +%ifarch %{sparc} +%{_sbindir}/grub2-sparc64-setup +%else +%exclude %{_sbindir}/grub2-sparc64-setup +%exclude %{_datadir}/man/man8/grub2-sparc64-setup* +%endif +%ifarch %{sparc} ppc ppc64 ppc64le +%{_sbindir}/grub2-ofpathname +%else +%exclude %{_sbindir}/grub2-ofpathname +%exclude %{_datadir}/man/man8/grub2-ofpathname* +%endif +%endif + +%files tools-extra +%{_bindir}/grub2-fstest +%{_bindir}/grub2-kbdcomp +%{_bindir}/grub2-mkfont +%{_bindir}/grub2-mklayout +%{_bindir}/grub2-mknetdir +%ifnarch %{sparc} +%{_bindir}/grub2-mkrescue +%{_datadir}/man/man1/grub2-mkrescue* +%else +%exclude %{_datadir}/man/man1/grub2-mkrescue* +%endif +%{_bindir}/grub2-mkstandalone +%{_bindir}/grub2-syslinux2cfg +%{_sysconfdir}/sysconfig/grub +%{_datadir}/man/man1/grub2-fstest* +%{_datadir}/man/man1/grub2-kbdcomp* +%{_datadir}/man/man1/grub2-mkfont* +%{_datadir}/man/man1/grub2-mklayout* +%{_datadir}/man/man1/grub2-mknetdir* +%{_datadir}/man/man1/grub2-mkstandalone* +%{_datadir}/man/man1/grub2-syslinux2cfg* +%exclude %{_bindir}/grub2-glue-efi +%exclude %{_sbindir}/grub2-sparc64-setup +%exclude %{_sbindir}/grub2-ofpathname +%exclude %{_datadir}/man/man1/grub2-glue-efi* +%exclude %{_datadir}/man/man8/grub2-ofpathname* +%exclude %{_datadir}/man/man8/grub2-sparc64-setup* +%exclude %{_datarootdir}/grub/themes/starfield + +%if 0%{with_efi_arch} +%{expand:%define_efi_variant_files %%{package_arch} %%{grubefiname} %%{grubeficdname} %%{grubefiarch} %%{target_cpu_name} %%{grub_target_name} %%{efiarch}} +%endif +%if 0%{with_alt_efi_arch} +%{expand:%define_efi_variant_files %%{alt_package_arch} %%{grubaltefiname} %%{grubalteficdname} %%{grubaltefiarch} %%{alt_target_cpu_name} %%{alt_grub_target_name} %%{alt_efi_arch}} +%endif +%if 0%{with_legacy_arch} +%{expand:%define_legacy_variant_files %%{legacy_package_arch} %%{grublegacyarch}} +%endif + +%if 0%{with_emu_arch} +%files emu +%{_bindir}/grub2-emu* +%{_datadir}/man/man1/grub2-emu* + +%files emu-modules +%{_libdir}/grub/%{emuarch}-emu/* +%exclude %{_libdir}/grub/%{emuarch}-emu/*.module +%endif + +%changelog +@CHANGELOG@ diff --git a/include/grub/at_keyboard.h b/include/grub/at_keyboard.h index bcb4d9ba7..9414dc1b9 100644 --- a/include/grub/at_keyboard.h +++ b/include/grub/at_keyboard.h @@ -19,6 +19,10 @@ #ifndef GRUB_AT_KEYBOARD_HEADER #define GRUB_AT_KEYBOARD_HEADER 1 +/* + * Refer to https://wiki.osdev.org/%228042%22_PS/2_Controller for details. + */ + /* Used for sending commands to the controller. */ #define KEYBOARD_COMMAND_ISREADY(x) !((x) & 0x02) #define KEYBOARD_COMMAND_READ 0x20 diff --git a/include/grub/btrfs.h b/include/grub/btrfs.h index 9d93fb6c1..234ad9767 100644 --- a/include/grub/btrfs.h +++ b/include/grub/btrfs.h @@ -29,6 +29,7 @@ enum GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM = 0x84, GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF = 0x90, GRUB_BTRFS_ITEM_TYPE_DEVICE = 0xd8, + GRUB_BTRFS_ITEM_TYPE_ROOT_REF = 0x9c, GRUB_BTRFS_ITEM_TYPE_CHUNK = 0xe4 }; diff --git a/include/grub/compiler.h b/include/grub/compiler.h index 0c5519387..441a9eca0 100644 --- a/include/grub/compiler.h +++ b/include/grub/compiler.h @@ -56,4 +56,6 @@ # define CLANG_PREREQ(maj,min) 0 #endif +#define UNUSED __attribute__((__unused__)) + #endif /* ! GRUB_COMPILER_HEADER */ diff --git a/include/grub/efi/efi.h b/include/grub/efi/efi.h index a5cd99e5a..3edf44dbd 100644 --- a/include/grub/efi/efi.h +++ b/include/grub/efi/efi.h @@ -25,6 +25,11 @@ #include #include +#define GRUB_EFI_GRUB_VARIABLE_GUID \ + { 0x91376aff, 0xcba6, 0x42be, \ + { 0x94, 0x9d, 0x06, 0xfd, 0xe8, 0x11, 0x28, 0xe8 } \ + } + #define GRUB_LINUX_ARM_MAGIC_SIGNATURE 0x016f2818 struct linux_arch_kernel_header { diff --git a/include/grub/emu/config.h b/include/grub/emu/config.h index 875d5896c..c9a7e5f4a 100644 --- a/include/grub/emu/config.h +++ b/include/grub/emu/config.h @@ -37,6 +37,7 @@ struct grub_util_config { int is_cryptodisk_enabled; char *grub_distributor; + int is_suse_btrfs_snapshot_enabled; }; void diff --git a/include/grub/emu/getroot.h b/include/grub/emu/getroot.h index 73fa2d34a..9c642ae3f 100644 --- a/include/grub/emu/getroot.h +++ b/include/grub/emu/getroot.h @@ -53,6 +53,11 @@ char ** grub_find_root_devices_from_mountinfo (const char *dir, char **relroot); #endif +#ifdef __linux__ +char * +grub_util_get_btrfs_subvol (const char *path, char **mount_path); +#endif + /* Devmapper functions provided by getroot_devmapper.c. */ void grub_util_pull_devmapper (const char *os_dev); diff --git a/include/grub/gpt_partition.h b/include/grub/gpt_partition.h index 292ea03f1..59c7ceba4 100644 --- a/include/grub/gpt_partition.h +++ b/include/grub/gpt_partition.h @@ -67,7 +67,7 @@ struct grub_gpt_partentry grub_uint64_t end; grub_uint64_t attrib; char name[72]; -}; +} GRUB_PACKED __attribute__ ((aligned(8))); grub_err_t grub_gpt_partition_map_iterate (grub_disk_t disk, diff --git a/include/grub/i386/msr.h b/include/grub/i386/msr.h index 726aba9d4..9adb44cd4 100644 --- a/include/grub/i386/msr.h +++ b/include/grub/i386/msr.h @@ -37,7 +37,9 @@ #define GRUB_MSR_X86_ICR_DELIVER_EXCL_SELF (3<<18) #define GRUB_MSR_X86_FEATURE_CONTROL 0x0000003a +#define GRUB_MSR_X86_FEATURE_CTRL_LOCK (1<<0) /* Lock this (feature control) MSR */ #define GRUB_MSR_X86_ENABLE_VMX_IN_SMX (1<<1) /* Enable VMX inside SMX */ +#define GRUB_MSR_X86_ENABLE_VMX_OUT_SMX (1<<2) /* Enable VMX outside SMX */ #define GRUB_MSR_X86_SENTER_FUNCTIONS (0x7f<<8) /* Bitmap of SENTER function enables */ #define GRUB_MSR_X86_SENTER_ENABLE (1<<15) /* SENTER global enable */ diff --git a/include/grub/i386/txt.h b/include/grub/i386/txt.h index d2ae7b21e..66ada0a99 100644 --- a/include/grub/i386/txt.h +++ b/include/grub/i386/txt.h @@ -354,7 +354,6 @@ struct grub_txt_os_mle_data { grub_uint32_t version; grub_uint32_t reserved; - grub_uint64_t boot_params_addr; grub_uint64_t slrt; grub_uint64_t txt_info; grub_uint32_t ap_wake_block; @@ -376,8 +375,9 @@ struct grub_txt_os_sinit_data grub_uint64_t lcp_po_base; grub_uint64_t lcp_po_size; grub_uint32_t capabilities; - /* Version = 5 */ - grub_uint64_t efi_rsdt_ptr; + /* Versions >= 5 */ + /* Warning: version 5 has pointer to RSDT here, not RSDP */ + grub_uint64_t efi_rsdp_ptr; /* Versions >= 6 */ /* Ext Data Elements */ grub_uint8_t ext_data_elts[]; @@ -451,6 +451,12 @@ struct grub_txt_heap_event_log_ptr_elt2_1 /* TXT register and heap access */ +static inline grub_uint32_t +grub_txt_reg_pub_readd (grub_uint32_t reg) +{ + return grub_read32 (GRUB_TXT_CFG_REGS_PUB + reg); +} + static inline grub_uint64_t grub_txt_reg_pub_readq (grub_uint32_t reg) { @@ -463,6 +469,12 @@ grub_txt_get_heap (void) return (grub_uint8_t *)(grub_addr_t) grub_txt_reg_pub_readq (GRUB_TXT_HEAP_BASE); } +static inline grub_uint32_t +grub_txt_get_heap_size (void) +{ + return grub_txt_reg_pub_readd (GRUB_TXT_HEAP_SIZE); +} + static inline grub_uint64_t grub_txt_bios_data_size (grub_uint8_t *heap) { @@ -611,8 +623,8 @@ grub_txt_getsec_sexit (void) #define GRUB_SMX_GET_SENTER_CONTROLS(v) ((v & 0x7f00) >> 8) #define GRUB_SMX_PROCESSOR_BASE_SCRTM 0x00000020 -#define GRUB_SMX_MACHINE_CHECK_HANLDING 0x00000040 -#define GRUB_SMX_GET_TXT_EXT_FEATURES(v) (v & (GRUB_SMX_PROCESSOR_BASE_SCRTM|GRUB_SMX_MACHINE_CHECK_HANLDING)) +#define GRUB_SMX_MACHINE_CHECK_HANDLING 0x00000040 +#define GRUB_SMX_GET_TXT_EXT_FEATURES(v) (v & (GRUB_SMX_PROCESSOR_BASE_SCRTM|GRUB_SMX_MACHINE_CHECK_HANDLING)) #define GRUB_SMX_DEFAULT_VERSION 0x0 #define GRUB_SMX_DEFAULT_VERSION_MASK 0xffffffff diff --git a/include/grub/lib/envblk.h b/include/grub/lib/envblk.h index c3e655921..ab969af24 100644 --- a/include/grub/lib/envblk.h +++ b/include/grub/lib/envblk.h @@ -22,6 +22,8 @@ #define GRUB_ENVBLK_SIGNATURE "# GRUB Environment Block\n" #define GRUB_ENVBLK_DEFCFG "grubenv" +#define DEFAULT_ENVBLK_SIZE 1024 + #ifndef ASM_FILE struct grub_envblk @@ -33,6 +35,7 @@ typedef struct grub_envblk *grub_envblk_t; grub_envblk_t grub_envblk_open (char *buf, grub_size_t size); int grub_envblk_set (grub_envblk_t envblk, const char *name, const char *value); +grub_err_t grub_envblk_get (grub_envblk_t envblk, const char * const name, char ** const value); void grub_envblk_delete (grub_envblk_t envblk, const char *name); void grub_envblk_iterate (grub_envblk_t envblk, void *hook_data, diff --git a/include/grub/misc.h b/include/grub/misc.h index 1578f36c3..23bdaee12 100644 --- a/include/grub/misc.h +++ b/include/grub/misc.h @@ -335,6 +335,7 @@ char *EXPORT_FUNC(grub_strdup) (const char *s) WARN_UNUSED_RESULT; char *EXPORT_FUNC(grub_strndup) (const char *s, grub_size_t n) WARN_UNUSED_RESULT; void *EXPORT_FUNC(grub_memset) (void *s, int c, grub_size_t n); grub_size_t EXPORT_FUNC(grub_strlen) (const char *s) WARN_UNUSED_RESULT; +void EXPORT_FUNC(grub_str_sep) (const char *s, char *p, char delim, char *r); /* Replace all `ch' characters of `input' with `with' and copy the result into `output'; return EOS address of `output'. */ @@ -369,6 +370,7 @@ grub_puts (const char *s) } int EXPORT_FUNC(grub_puts_) (const char *s); +int EXPORT_FUNC(grub_debug_is_enabled) (void); int EXPORT_FUNC(grub_debug_enabled) (const char *condition); void EXPORT_FUNC(grub_real_dprintf) (const char *file, const int line, diff --git a/include/grub/search.h b/include/grub/search.h index ffd2411ca..9343c66b1 100644 --- a/include/grub/search.h +++ b/include/grub/search.h @@ -23,7 +23,8 @@ enum search_flags { SEARCH_FLAGS_NONE = 0, SEARCH_FLAGS_NO_FLOPPY = 1, - SEARCH_FLAGS_EFIDISK_ONLY = 2 + SEARCH_FLAGS_EFIDISK_ONLY = 2, + SEARCH_FLAGS_ROOTDEV_ONLY = 4 }; void grub_search_fs_file (const char *key, const char *var, diff --git a/include/grub/slaunch.h b/include/grub/slaunch.h index e85700c69..e9c295eaa 100644 --- a/include/grub/slaunch.h +++ b/include/grub/slaunch.h @@ -28,6 +28,19 @@ #define GRUB_SLAUNCH_TPM_EVT_LOG_SIZE (8 * GRUB_PAGE_SIZE) +/* + * Special value for slr_table_base of struct grub_slaunch_params that indicates + * that the table should be stored near OS2MLE data (right after it). + * + * In this case: + * 1. Platform-specific code (e.g., TXT-code) is responsible for setting + * slr_table_base to its final value + * 2. SLRT should be copied from slr_table_mem to slr_table_base after invoking + * grub_slaunch_finish_slr_table () by the code which used this special + * value. + */ +#define GRUB_SLAUNCH_STORE_IN_OS2MLE ((grub_uint64_t) 0xFFFFFFFFFFFFFFFF) + #ifndef ASM_FILE #define GRUB_SL_BOOT_TYPE_INVALID 0 @@ -58,7 +71,7 @@ struct grub_slaunch_params grub_uint64_t slr_table_base; grub_uint32_t slr_table_size; void *slr_table_mem; - void *mle_mem; + grub_uint32_t mle_entry; grub_uint32_t mle_start; grub_uint32_t mle_size; grub_uint64_t mle_ptab_target; @@ -127,11 +140,11 @@ grub_err_t grub_sl_skinit_setup_linux (struct grub_slaunch_params *slparams, grub_size_t total_size, grub_size_t prot_file_size, void *prot_mode_mem, grub_addr_t prot_mode_target); -/* Linux EFI functions */ +/* EFI functions */ grub_err_t grub_sl_efi_txt_setup (struct grub_slaunch_params *slparams, void *kernel_addr, - grub_efi_loaded_image_t *loaded_image); + grub_efi_loaded_image_t *loaded_image, bool is_linux); grub_err_t grub_sl_efi_skinit_setup (struct grub_slaunch_params *slparams, void *kernel_addr, - grub_efi_loaded_image_t *loaded_image); + grub_efi_loaded_image_t *loaded_image, bool is_linux); #endif /* ASM_FILE */ diff --git a/include/grub/slr_table.h b/include/grub/slr_table.h index 0bd15858e..117018d67 100644 --- a/include/grub/slr_table.h +++ b/include/grub/slr_table.h @@ -189,6 +189,7 @@ struct grub_slr_txt_mtrr_state struct grub_slr_entry_intel_info { struct grub_slr_entry_hdr hdr; + grub_uint64_t boot_params_base; grub_uint64_t txt_heap; grub_uint64_t saved_misc_enable_msr; struct grub_slr_txt_mtrr_state saved_bsp_mtrrs; diff --git a/qubesos/bootstrap b/qubesos/bootstrap new file mode 100755 index 000000000..5b08e7e2d --- /dev/null +++ b/qubesos/bootstrap @@ -0,0 +1,1073 @@ +#! /bin/sh +# Print a version string. +scriptversion=2019-01-04.17; # UTC + +# Bootstrap this package from checked-out sources. + +# Copyright (C) 2003-2019 Free Software Foundation, Inc. + +# This program 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. + +# This program 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 this program. If not, see . + +# Originally written by Paul Eggert. The canonical version of this +# script is maintained as build-aux/bootstrap in gnulib, however, to +# be useful to your project, you should place a copy of it under +# version control in the top-level directory of your project. The +# intent is that all customization can be done with a bootstrap.conf +# file also maintained in your version control; gnulib comes with a +# template build-aux/bootstrap.conf to get you started. + +# Please report bugs or propose patches to bug-gnulib@gnu.org. + +nl=' +' + +# Ensure file names are sorted consistently across platforms. +LC_ALL=C +export LC_ALL + +# Ensure that CDPATH is not set. Otherwise, the output from cd +# would cause trouble in at least one use below. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +local_gl_dir=gl + +# Honor $PERL, but work even if there is none. +PERL="${PERL-perl}" + +me=$0 + +default_gnulib_url=git://git.sv.gnu.org/gnulib + +usage() { + cat <&2 +} + +# warn_ WORD1... +warn_ () +{ + # If IFS does not start with ' ', set it and emit the warning in a subshell. + case $IFS in + ' '*) warnf_ '%s\n' "$*";; + *) (IFS=' '; warn_ "$@");; + esac +} + +# die WORD1... +die() { warn_ "$@"; exit 1; } + +# Configuration. + +# Name of the Makefile.am +gnulib_mk=gnulib.mk + +# List of gnulib modules needed. +gnulib_modules= + +# Any gnulib files needed that are not in modules. +gnulib_files= + +: ${AUTOPOINT=autopoint} +: ${AUTORECONF=autoreconf} + +# A function to be called right after gnulib-tool is run. +# Override it via your own definition in bootstrap.conf. +bootstrap_post_import_hook() { :; } + +# A function to be called after everything else in this script. +# Override it via your own definition in bootstrap.conf. +bootstrap_epilogue() { :; } + +# The command to download all .po files for a specified domain into a +# specified directory. Fill in the first %s with the destination +# directory and the second with the domain name. +po_download_command_format=\ +"wget --mirror --level=1 -nd -q -A.po -P '%s' \ + https://translationproject.org/latest/%s/" + +# Prefer a non-empty tarname (4th argument of AC_INIT if given), else +# fall back to the package name (1st argument with munging) +extract_package_name=' + /^AC_INIT(\[*/{ + s/// + /^[^,]*,[^,]*,[^,]*,[ []*\([^][ ,)]\)/{ + s//\1/ + s/[],)].*// + p + q + } + s/[],)].*// + s/^GNU // + y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ + s/[^abcdefghijklmnopqrstuvwxyz0123456789_]/-/g + p + } +' +package=$(sed -n "$extract_package_name" configure.ac) \ + || die 'cannot find package name in configure.ac' +gnulib_name=lib$package + +build_aux=build-aux +source_base=lib +m4_base=m4 +doc_base=doc +tests_base=tests +gnulib_extra_files=" + build-aux/install-sh + build-aux/mdate-sh + build-aux/texinfo.tex + build-aux/depcomp + build-aux/config.guess + build-aux/config.sub + doc/INSTALL +" + +# Additional gnulib-tool options to use. Use "\newline" to break lines. +gnulib_tool_option_extras= + +# Other locale categories that need message catalogs. +EXTRA_LOCALE_CATEGORIES= + +# Additional xgettext options to use. Use "\\\newline" to break lines. +XGETTEXT_OPTIONS='\\\ + --flag=_:1:pass-c-format\\\ + --flag=N_:1:pass-c-format\\\ + --flag=error:3:c-format --flag=error_at_line:5:c-format\\\ +' + +# Package bug report address and copyright holder for gettext files +COPYRIGHT_HOLDER='Free Software Foundation, Inc.' +MSGID_BUGS_ADDRESS=bug-$package@gnu.org + +# Files we don't want to import. +excluded_files= + +# File that should exist in the top directory of a checked out hierarchy, +# but not in a distribution tarball. +checkout_only_file=README-hacking + +# Whether to use copies instead of symlinks. +copy=false + +# Set this to '.cvsignore .gitignore' in bootstrap.conf if you want +# those files to be generated in directories like lib/, m4/, and po/. +# Or set it to 'auto' to make this script select which to use based +# on which version control system (if any) is used in the source directory. +vc_ignore=auto + +# Set this to true in bootstrap.conf to enable --bootstrap-sync by +# default. +bootstrap_sync=false + +# Use git to update gnulib sources +use_git=true + +check_exists() { + if test "$1" = "--verbose"; then + ($2 --version /dev/null 2>&1 + if test $? -ge 126; then + # If not found, run with diagnostics as one may be + # presented with env variables to set to find the right version + ($2 --version /dev/null 2>&1 + fi + + test $? -lt 126 +} + +# find_tool ENVVAR NAMES... +# ------------------------- +# Search for a required program. Use the value of ENVVAR, if set, +# otherwise find the first of the NAMES that can be run. +# If found, set ENVVAR to the program name, die otherwise. +# +# FIXME: code duplication, see also gnu-web-doc-update. +find_tool () +{ + find_tool_envvar=$1 + shift + find_tool_names=$@ + eval "find_tool_res=\$$find_tool_envvar" + if test x"$find_tool_res" = x; then + for i; do + if check_exists $i; then + find_tool_res=$i + break + fi + done + fi + if test x"$find_tool_res" = x; then + warn_ "one of these is required: $find_tool_names;" + die "alternatively set $find_tool_envvar to a compatible tool" + fi + eval "$find_tool_envvar=\$find_tool_res" + eval "export $find_tool_envvar" +} + +# Override the default configuration, if necessary. +# Make sure that bootstrap.conf is sourced from the current directory +# if we were invoked as "sh bootstrap". +case "$0" in + */*) test -r "$0.conf" && . "$0.conf" ;; + *) test -r "$0.conf" && . ./"$0.conf" ;; +esac + +if test "$vc_ignore" = auto; then + vc_ignore= + test -d .git && vc_ignore=.gitignore + test -d CVS && vc_ignore="$vc_ignore .cvsignore" +fi + +if test x"$gnulib_modules$gnulib_files$gnulib_extra_files" = x; then + use_gnulib=false +else + use_gnulib=true +fi + +# Translate configuration into internal form. + +# Parse options. + +for option +do + case $option in + --help) + usage + exit;; + --gnulib-srcdir=*) + GNULIB_SRCDIR=${option#--gnulib-srcdir=};; + --skip-po) + SKIP_PO=t;; + --force) + checkout_only_file=;; + --copy) + copy=true;; + --bootstrap-sync) + bootstrap_sync=true;; + --no-bootstrap-sync) + bootstrap_sync=false;; + --no-git) + use_git=false;; + *) + die "$option: unknown option";; + esac +done + +$use_git || test -d "$GNULIB_SRCDIR" \ + || die "Error: --no-git requires --gnulib-srcdir" + +if test -n "$checkout_only_file" && test ! -r "$checkout_only_file"; then + die "Bootstrapping from a non-checked-out distribution is risky." +fi + +# Strip blank and comment lines to leave significant entries. +gitignore_entries() { + sed '/^#/d; /^$/d' "$@" +} + +# If $STR is not already on a line by itself in $FILE, insert it at the start. +# Entries are inserted at the start of the ignore list to ensure existing +# entries starting with ! are not overridden. Such entries support +# whitelisting exceptions after a more generic blacklist pattern. +insert_if_absent() { + file=$1 + str=$2 + test -f $file || touch $file + test -r $file || die "Error: failed to read ignore file: $file" + duplicate_entries=$(gitignore_entries $file | sort | uniq -d) + if [ "$duplicate_entries" ] ; then + die "Error: Duplicate entries in $file: " $duplicate_entries + fi + linesold=$(gitignore_entries $file | wc -l) + linesnew=$( { echo "$str"; cat $file; } | gitignore_entries | sort -u | wc -l) + if [ $linesold != $linesnew ] ; then + { echo "$str" | cat - $file > $file.bak && mv $file.bak $file; } \ + || die "insert_if_absent $file $str: failed" + fi +} + +# Adjust $PATTERN for $VC_IGNORE_FILE and insert it with +# insert_if_absent. +insert_vc_ignore() { + vc_ignore_file="$1" + pattern="$2" + case $vc_ignore_file in + *.gitignore) + # A .gitignore entry that does not start with '/' applies + # recursively to subdirectories, so prepend '/' to every + # .gitignore entry. + pattern=$(echo "$pattern" | sed s,^,/,);; + esac + insert_if_absent "$vc_ignore_file" "$pattern" +} + +# Die if there is no AC_CONFIG_AUX_DIR($build_aux) line in configure.ac. +found_aux_dir=no +grep '^[ ]*AC_CONFIG_AUX_DIR(\['"$build_aux"'\])' configure.ac \ + >/dev/null && found_aux_dir=yes +grep '^[ ]*AC_CONFIG_AUX_DIR('"$build_aux"')' configure.ac \ + >/dev/null && found_aux_dir=yes +test $found_aux_dir = yes \ + || die "configure.ac lacks 'AC_CONFIG_AUX_DIR([$build_aux])'; add it" + +# If $build_aux doesn't exist, create it now, otherwise some bits +# below will malfunction. If creating it, also mark it as ignored. +if test ! -d $build_aux; then + mkdir $build_aux + for dot_ig in x $vc_ignore; do + test $dot_ig = x && continue + insert_vc_ignore $dot_ig $build_aux + done +fi + +# Note this deviates from the version comparison in automake +# in that it treats 1.5 < 1.5.0, and treats 1.4.4a < 1.4-p3a +# but this should suffice as we won't be specifying old +# version formats or redundant trailing .0 in bootstrap.conf. +# If we did want full compatibility then we should probably +# use m4_version_compare from autoconf. +sort_ver() { # sort -V is not generally available + ver1="$1" + ver2="$2" + + # split on '.' and compare each component + i=1 + while : ; do + p1=$(echo "$ver1" | cut -d. -f$i) + p2=$(echo "$ver2" | cut -d. -f$i) + if [ ! "$p1" ]; then + echo "$1 $2" + break + elif [ ! "$p2" ]; then + echo "$2 $1" + break + elif [ ! "$p1" = "$p2" ]; then + if [ "$p1" -gt "$p2" ] 2>/dev/null; then # numeric comparison + echo "$2 $1" + elif [ "$p2" -gt "$p1" ] 2>/dev/null; then # numeric comparison + echo "$1 $2" + else # numeric, then lexicographic comparison + lp=$(printf "$p1\n$p2\n" | LANG=C sort -n | tail -n1) + if [ "$lp" = "$p2" ]; then + echo "$1 $2" + else + echo "$2 $1" + fi + fi + break + fi + i=$(($i+1)) + done +} + +get_version_sed=' +# Move version to start of line. +s/.*[v ]\([0-9]\)/\1/ + +# Skip lines that do not start with version. +/^[0-9]/!d + +# Remove characters after the version. +s/[^.a-z0-9-].*// + +# The first component must be digits only. +s/^\([0-9]*\)[a-z-].*/\1/ + +#the following essentially does s/5.005/5.5/ +s/\.0*\([1-9]\)/.\1/g +p +q' + +get_version() { + app=$1 + + $app --version >/dev/null 2>&1 || { $app --version; return 1; } + + $app --version 2>&1 | sed -n "$get_version_sed" +} + +check_versions() { + ret=0 + + while read app req_ver; do + # We only need libtoolize from the libtool package. + if test "$app" = libtool; then + app=libtoolize + fi + # Exempt git if --no-git is in effect. + if test "$app" = git; then + $use_git || continue + fi + # Honor $APP variables ($TAR, $AUTOCONF, etc.) + appvar=$(echo $app | LC_ALL=C tr '[a-z]-' '[A-Z]_') + test "$appvar" = TAR && appvar=AMTAR + case $appvar in + GZIP) ;; # Do not use $GZIP: it contains gzip options. + PERL::*) ;; # Keep perl modules as-is + *) eval "app=\${$appvar-$app}" ;; + esac + + # Handle the still-experimental Automake-NG programs specially. + # They remain named as the mainstream Automake programs ("automake", + # and "aclocal") to avoid gratuitous incompatibilities with + # pre-existing usages (by, say, autoreconf, or custom autogen.sh + # scripts), but correctly identify themselves (as being part of + # "GNU automake-ng") when asked their version. + case $app in + automake-ng|aclocal-ng) + app=${app%-ng} + ($app --version | grep '(GNU automake-ng)') >/dev/null 2>&1 || { + warn_ "Error: '$app' not found or not from Automake-NG" + ret=1 + continue + } ;; + # Another check is for perl modules. These can be written as + # e.g. perl::XML::XPath in case of XML::XPath module, etc. + perl::*) + # Extract module name + app="${app#perl::}" + if ! $PERL -m"$app" -e 'exit 0' >/dev/null 2>&1; then + warn_ "Error: perl module '$app' not found" + ret=1 + fi + continue + ;; + esac + if [ "$req_ver" = "-" ]; then + # Merely require app to exist; not all prereq apps are well-behaved + # so we have to rely on $? rather than get_version. + if ! check_exists --verbose $app; then + warn_ "Error: '$app' not found" + ret=1 + fi + else + # Require app to produce a new enough version string. + inst_ver=$(get_version $app) + if [ ! "$inst_ver" ]; then + warn_ "Error: '$app' not found" + ret=1 + else + latest_ver=$(sort_ver $req_ver $inst_ver | cut -d' ' -f2) + if [ ! "$latest_ver" = "$inst_ver" ]; then + warnf_ '%s\n' \ + "Error: '$app' version == $inst_ver is too old" \ + " '$app' version >= $req_ver is required" + ret=1 + fi + fi + fi + done + + return $ret +} + +print_versions() { + echo "Program Min_version" + echo "----------------------" + printf %s "$buildreq" + echo "----------------------" + # can't depend on column -t +} + +# Find sha1sum, named gsha1sum on MacPorts, shasum on Mac OS X 10.6. +# Also find the compatible sha1 utility on the BSDs +if test x"$SKIP_PO" = x; then + find_tool SHA1SUM sha1sum gsha1sum shasum sha1 +fi + +use_libtool=0 +# We'd like to use grep -E, to see if any of LT_INIT, +# AC_PROG_LIBTOOL, AM_PROG_LIBTOOL is used in configure.ac, +# but that's not portable enough (e.g., for Solaris). +grep '^[ ]*A[CM]_PROG_LIBTOOL' configure.ac >/dev/null \ + && use_libtool=1 +grep '^[ ]*LT_INIT' configure.ac >/dev/null \ + && use_libtool=1 +if test $use_libtool = 1; then + find_tool LIBTOOLIZE glibtoolize libtoolize +fi + +# gnulib-tool requires at least automake and autoconf. +# If either is not listed, add it (with minimum version) as a prerequisite. +case $buildreq in + *automake*) ;; + *) buildreq="automake 1.9 +$buildreq" ;; +esac +case $buildreq in + *autoconf*) ;; + *) buildreq="autoconf 2.59 +$buildreq" ;; +esac + +# When we can deduce that gnulib-tool will require patch, +# and when patch is not already listed as a prerequisite, add it, too. +if test -d "$local_gl_dir" \ + && ! find "$local_gl_dir" -name '*.diff' -exec false {} +; then + case $buildreq in + *patch*) ;; + *) buildreq="patch - +$buildreq" ;; + esac +fi + +if ! printf "$buildreq" | check_versions; then + echo >&2 + if test -f README-prereq; then + die "See README-prereq for how to get the prerequisite programs" + else + die "Please install the prerequisite programs" + fi +fi + +# Warn the user if autom4te appears to be broken; this causes known +# issues with at least gettext 0.18.3. +probe=$(echo 'm4_quote([hi])' | autom4te -l M4sugar -t 'm4_quote:$%' -) +if test "x$probe" != xhi; then + warn_ "WARNING: your autom4te wrapper eats stdin;" + warn_ "if bootstrap fails, consider upgrading your autotools" +fi + +echo "$0: Bootstrapping from checked-out $package sources..." + +# See if we can use gnulib's git-merge-changelog merge driver. +if $use_git && test -d .git && check_exists git; then + if git config merge.merge-changelog.driver >/dev/null ; then + : + elif check_exists git-merge-changelog; then + echo "$0: initializing git-merge-changelog driver" + git config merge.merge-changelog.name 'GNU-style ChangeLog merge driver' + git config merge.merge-changelog.driver 'git-merge-changelog %O %A %B' + else + echo "$0: consider installing git-merge-changelog from gnulib" + fi +fi + + +cleanup_gnulib() { + status=$? + rm -fr "$gnulib_path" + exit $status +} + +git_modules_config () { + test -f .gitmodules && git config --file .gitmodules "$@" +} + +if $use_gnulib; then + if $use_git; then + gnulib_path=$(git_modules_config submodule.gnulib.path) + test -z "$gnulib_path" && gnulib_path=gnulib + fi + + # Get gnulib files. Populate $GNULIB_SRCDIR, possibly updating a + # submodule, for use in the rest of the script. + + case ${GNULIB_SRCDIR--} in + -) + # Note that $use_git is necessarily true in this case. + if git_modules_config submodule.gnulib.url >/dev/null; then + echo "$0: getting gnulib files..." + git submodule init -- "$gnulib_path" || exit $? + git submodule update -- "$gnulib_path" || exit $? + + elif [ ! -d "$gnulib_path" ]; then + echo "$0: getting gnulib files..." + + trap cleanup_gnulib 1 2 13 15 + + shallow= + if test -z "$GNULIB_REVISION"; then + git clone -h 2>&1 | grep -- --depth > /dev/null && shallow='--depth 2' + fi + git clone $shallow ${GNULIB_URL:-$default_gnulib_url} "$gnulib_path" \ + || cleanup_gnulib + + trap - 1 2 13 15 + fi + GNULIB_SRCDIR=$gnulib_path + ;; + *) + # Use GNULIB_SRCDIR directly or as a reference. + if $use_git && test -d "$GNULIB_SRCDIR"/.git && \ + git_modules_config submodule.gnulib.url >/dev/null; then + echo "$0: getting gnulib files..." + if git submodule -h|grep -- --reference > /dev/null; then + # Prefer the one-liner available in git 1.6.4 or newer. + git submodule update --init --reference "$GNULIB_SRCDIR" \ + "$gnulib_path" || exit $? + else + # This fallback allows at least git 1.5.5. + if test -f "$gnulib_path"/gnulib-tool; then + # Since file already exists, assume submodule init already complete. + git submodule update -- "$gnulib_path" || exit $? + else + # Older git can't clone into an empty directory. + rmdir "$gnulib_path" 2>/dev/null + git clone --reference "$GNULIB_SRCDIR" \ + "$(git_modules_config submodule.gnulib.url)" "$gnulib_path" \ + && git submodule init -- "$gnulib_path" \ + && git submodule update -- "$gnulib_path" \ + || exit $? + fi + fi + GNULIB_SRCDIR=$gnulib_path + fi + ;; + esac + + if test -d "$GNULIB_SRCDIR"/.git && test -n "$GNULIB_REVISION" \ + && ! git_modules_config submodule.gnulib.url >/dev/null; then + (cd "$GNULIB_SRCDIR" && git checkout "$GNULIB_REVISION") || cleanup_gnulib + fi + + # $GNULIB_SRCDIR now points to the version of gnulib to use, and + # we no longer need to use git or $gnulib_path below here. + + if $bootstrap_sync; then + cmp -s "$0" "$GNULIB_SRCDIR/build-aux/bootstrap" || { + echo "$0: updating bootstrap and restarting..." + case $(sh -c 'echo "$1"' -- a) in + a) ignored=--;; + *) ignored=ignored;; + esac + exec sh -c \ + 'cp "$1" "$2" && shift && exec "${CONFIG_SHELL-/bin/sh}" "$@"' \ + $ignored "$GNULIB_SRCDIR/build-aux/bootstrap" \ + "$0" "$@" --no-bootstrap-sync + } + fi + + gnulib_tool=$GNULIB_SRCDIR/gnulib-tool + <$gnulib_tool || exit $? +fi + +# Get translations. + +download_po_files() { + subdir=$1 + domain=$2 + echo "$me: getting translations into $subdir for $domain..." + cmd=$(printf "$po_download_command_format" "$subdir" "$domain") + eval "$cmd" +} + +# Mirror .po files to $po_dir/.reference and copy only the new +# or modified ones into $po_dir. Also update $po_dir/LINGUAS. +# Note po files that exist locally only are left in $po_dir but will +# not be included in LINGUAS and hence will not be distributed. +update_po_files() { + # Directory containing primary .po files. + # Overwrite them only when we're sure a .po file is new. + po_dir=$1 + domain=$2 + + # Mirror *.po files into this dir. + # Usually contains *.s1 checksum files. + ref_po_dir="$po_dir/.reference" + + test -d $ref_po_dir || mkdir $ref_po_dir || return + download_po_files $ref_po_dir $domain \ + && ls "$ref_po_dir"/*.po 2>/dev/null | + sed 's|.*/||; s|\.po$||' > "$po_dir/LINGUAS" || return + + langs=$(cd $ref_po_dir && echo *.po | sed 's/\.po//g') + test "$langs" = '*' && langs=x + for po in $langs; do + case $po in x) continue;; esac + new_po="$ref_po_dir/$po.po" + cksum_file="$ref_po_dir/$po.s1" + if ! test -f "$cksum_file" || + ! test -f "$po_dir/$po.po" || + ! $SHA1SUM -c "$cksum_file" < "$new_po" > /dev/null 2>&1; then + echo "$me: updated $po_dir/$po.po..." + cp "$new_po" "$po_dir/$po.po" \ + && $SHA1SUM < "$new_po" > "$cksum_file" || return + fi + done +} + +case $SKIP_PO in +'') + if test -d po; then + update_po_files po $package || exit + fi + + if test -d runtime-po; then + update_po_files runtime-po $package-runtime || exit + fi;; +esac + +symlink_to_dir() +{ + src=$1/$2 + dst=${3-$2} + + test -f "$src" && { + + # If the destination directory doesn't exist, create it. + # This is required at least for "lib/uniwidth/cjk.h". + dst_dir=$(dirname "$dst") + if ! test -d "$dst_dir"; then + mkdir -p "$dst_dir" + + # If we've just created a directory like lib/uniwidth, + # tell version control system(s) it's ignorable. + # FIXME: for now, this does only one level + parent=$(dirname "$dst_dir") + for dot_ig in x $vc_ignore; do + test $dot_ig = x && continue + ig=$parent/$dot_ig + insert_vc_ignore $ig "${dst_dir##*/}" + done + fi + + if $copy; then + { + test ! -h "$dst" || { + echo "$me: rm -f $dst" && + rm -f "$dst" + } + } && + test -f "$dst" && + cmp -s "$src" "$dst" || { + echo "$me: cp -fp $src $dst" && + cp -fp "$src" "$dst" + } + else + # Leave any existing symlink alone, if it already points to the source, + # so that broken build tools that care about symlink times + # aren't confused into doing unnecessary builds. Conversely, if the + # existing symlink's timestamp is older than the source, make it afresh, + # so that broken tools aren't confused into skipping needed builds. See + # . + test -h "$dst" && + src_ls=$(ls -diL "$src" 2>/dev/null) && set $src_ls && src_i=$1 && + dst_ls=$(ls -diL "$dst" 2>/dev/null) && set $dst_ls && dst_i=$1 && + test "$src_i" = "$dst_i" && + both_ls=$(ls -dt "$src" "$dst") && + test "X$both_ls" = "X$dst$nl$src" || { + dot_dots= + case $src in + /*) ;; + *) + case /$dst/ in + *//* | */../* | */./* | /*/*/*/*/*/) + die "invalid symlink calculation: $src -> $dst";; + /*/*/*/*/) dot_dots=../../../;; + /*/*/*/) dot_dots=../../;; + /*/*/) dot_dots=../;; + esac;; + esac + + echo "$me: ln -fs $dot_dots$src $dst" && + ln -fs "$dot_dots$src" "$dst" + } + fi + } +} + +version_controlled_file() { + parent=$1 + file=$2 + if test -d .git; then + git rm -n "$file" > /dev/null 2>&1 + elif test -d .svn; then + svn log -r HEAD "$file" > /dev/null 2>&1 + elif test -d CVS; then + grep -F "/${file##*/}/" "$parent/CVS/Entries" 2>/dev/null | + grep '^/[^/]*/[0-9]' > /dev/null + else + warn_ "no version control for $file?" + false + fi +} + +# NOTE: we have to be careful to run both autopoint and libtoolize +# before gnulib-tool, since gnulib-tool is likely to provide newer +# versions of files "installed" by these two programs. +# Then, *after* gnulib-tool (see below), we have to be careful to +# run autoreconf in such a way that it does not run either of these +# two just-pre-run programs. + +# Import from gettext. +with_gettext=yes +grep '^[ ]*AM_GNU_GETTEXT_VERSION(' configure.ac >/dev/null || \ + with_gettext=no + +if test $with_gettext = yes || test $use_libtool = 1; then + + tempbase=.bootstrap$$ + trap "rm -f $tempbase.0 $tempbase.1" 1 2 13 15 + + > $tempbase.0 > $tempbase.1 && + find . ! -type d -print | sort > $tempbase.0 || exit + + if test $with_gettext = yes; then + # Released autopoint has the tendency to install macros that have been + # obsoleted in current gnulib, so run this before gnulib-tool. + echo "$0: $AUTOPOINT --force" + $AUTOPOINT --force || exit + fi + + # Autoreconf runs aclocal before libtoolize, which causes spurious + # warnings if the initial aclocal is confused by the libtoolized + # (or worse out-of-date) macro directory. + # libtoolize 1.9b added the --install option; but we support back + # to libtoolize 1.5.22, where the install action was default. + if test $use_libtool = 1; then + install= + case $($LIBTOOLIZE --help) in + *--install*) install=--install ;; + esac + echo "running: $LIBTOOLIZE $install --copy" + $LIBTOOLIZE $install --copy + fi + + find . ! -type d -print | sort >$tempbase.1 + old_IFS=$IFS + IFS=$nl + for file in $(comm -13 $tempbase.0 $tempbase.1); do + IFS=$old_IFS + parent=${file%/*} + version_controlled_file "$parent" "$file" || { + for dot_ig in x $vc_ignore; do + test $dot_ig = x && continue + ig=$parent/$dot_ig + insert_vc_ignore "$ig" "${file##*/}" + done + } + done + IFS=$old_IFS + + rm -f $tempbase.0 $tempbase.1 + trap - 1 2 13 15 +fi + +# Import from gnulib. + +if $use_gnulib; then + gnulib_tool_options="\ + --no-changelog\ + --aux-dir=$build_aux\ + --doc-base=$doc_base\ + --lib=$gnulib_name\ + --m4-base=$m4_base/\ + --source-base=$source_base/\ + --tests-base=$tests_base\ + --local-dir=$local_gl_dir\ + $gnulib_tool_option_extras\ + " + if test $use_libtool = 1; then + case "$gnulib_tool_options " in + *' --libtool '*) ;; + *) gnulib_tool_options="$gnulib_tool_options --libtool" ;; + esac + fi + echo "$0: $gnulib_tool $gnulib_tool_options --import ..." + $gnulib_tool $gnulib_tool_options --import $gnulib_modules \ + || die "gnulib-tool failed" + + for file in $gnulib_files; do + symlink_to_dir "$GNULIB_SRCDIR" $file \ + || die "failed to symlink $file" + done +fi + +bootstrap_post_import_hook \ + || die "bootstrap_post_import_hook failed" + +# Don't proceed if there are uninitialized submodules. In particular, +# the next step will remove dangling links, which might be links into +# uninitialized submodules. +# +# Uninitialized submodules are listed with an initial dash. +if $use_git && git submodule | grep '^-' >/dev/null; then + die "some git submodules are not initialized. " \ + "Run 'git submodule init' and bootstrap again." +fi + +# Remove any dangling symlink matching "*.m4" or "*.[ch]" in some +# gnulib-populated directories. Such .m4 files would cause aclocal to fail. +# The following requires GNU find 4.2.3 or newer. Considering the usual +# portability constraints of this script, that may seem a very demanding +# requirement, but it should be ok. Ignore any failure, which is fine, +# since this is only a convenience to help developers avoid the relatively +# unusual case in which a symlinked-to .m4 file is git-removed from gnulib +# between successive runs of this script. +find "$m4_base" "$source_base" \ + -depth \( -name '*.m4' -o -name '*.[ch]' \) \ + -type l -xtype l -delete > /dev/null 2>&1 + +# Invoke autoreconf with --force --install to ensure upgrades of tools +# such as ylwrap. +AUTORECONFFLAGS="--verbose --install --force -I $m4_base $ACLOCAL_FLAGS" + +# Some systems (RHEL 5) are using ancient autotools, for which the +# --no-recursive option had not been invented. Detect that lack and +# omit the option when it's not supported. FIXME in 2017: remove this +# hack when RHEL 5 autotools are updated, or when they become irrelevant. +case $($AUTORECONF --help) in + *--no-recursive*) AUTORECONFFLAGS="$AUTORECONFFLAGS --no-recursive";; +esac + +# Tell autoreconf not to invoke autopoint or libtoolize; they were run above. +echo "running: AUTOPOINT=true LIBTOOLIZE=true $AUTORECONF $AUTORECONFFLAGS" +AUTOPOINT=true LIBTOOLIZE=true $AUTORECONF $AUTORECONFFLAGS \ + || die "autoreconf failed" + +# Get some extra files from gnulib, overriding existing files. +for file in $gnulib_extra_files; do + case $file in + */INSTALL) dst=INSTALL;; + build-aux/*) dst=$build_aux/${file#build-aux/};; + *) dst=$file;; + esac + symlink_to_dir "$GNULIB_SRCDIR" $file $dst \ + || die "failed to symlink $file" +done + +if test $with_gettext = yes; then + # Create gettext configuration. + echo "$0: Creating po/Makevars from po/Makevars.template ..." + rm -f po/Makevars + sed ' + /^EXTRA_LOCALE_CATEGORIES *=/s/=.*/= '"$EXTRA_LOCALE_CATEGORIES"'/ + /^COPYRIGHT_HOLDER *=/s/=.*/= '"$COPYRIGHT_HOLDER"'/ + /^MSGID_BUGS_ADDRESS *=/s|=.*|= '"$MSGID_BUGS_ADDRESS"'| + /^XGETTEXT_OPTIONS *=/{ + s/$/ \\/ + a\ + '"$XGETTEXT_OPTIONS"' $${end_of_xgettext_options+} + } + ' po/Makevars.template >po/Makevars \ + || die 'cannot generate po/Makevars' + + # If the 'gettext' module is in use, grab the latest Makefile.in.in. + # If only the 'gettext-h' module is in use, assume autopoint already + # put the correct version of this file into place. + case $gnulib_modules in + *gettext-h*) ;; + *gettext*) + cp $GNULIB_SRCDIR/build-aux/po/Makefile.in.in po/Makefile.in.in \ + || die "cannot create po/Makefile.in.in" + ;; + esac + + if test -d runtime-po; then + # Similarly for runtime-po/Makevars, but not quite the same. + rm -f runtime-po/Makevars + sed ' + /^DOMAIN *=.*/s/=.*/= '"$package"'-runtime/ + /^subdir *=.*/s/=.*/= runtime-po/ + /^MSGID_BUGS_ADDRESS *=/s/=.*/= bug-'"$package"'@gnu.org/ + /^XGETTEXT_OPTIONS *=/{ + s/$/ \\/ + a\ + '"$XGETTEXT_OPTIONS_RUNTIME"' $${end_of_xgettext_options+} + } + ' po/Makevars.template >runtime-po/Makevars \ + || die 'cannot generate runtime-po/Makevars' + + # Copy identical files from po to runtime-po. + (cd po && cp -p Makefile.in.in *-quot *.header *.sed *.sin ../runtime-po) + fi +fi + +bootstrap_epilogue + +echo "$0: done. Now you can run './configure'." + +# Local variables: +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC0" +# time-stamp-end: "; # UTC" +# End: diff --git a/qubesos/bootstrap.conf b/qubesos/bootstrap.conf new file mode 100644 index 000000000..6b043fc35 --- /dev/null +++ b/qubesos/bootstrap.conf @@ -0,0 +1,101 @@ +# Bootstrap configuration. + +# Copyright (C) 2006-2019 Free Software Foundation, Inc. + +# This program 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. + +# This program 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 this program. If not, see . + + +GNULIB_REVISION=d271f868a8df9bbec29049d01e056481b7a1a263 + +# gnulib modules used by this package. +# mbswidth is used by gnulib-fix-width.diff's changes to argp rather than +# directly. +gnulib_modules=" + argp + base64 + error + fnmatch + getdelim + getline + gettext-h + gitlog-to-changelog + mbswidth + progname + realloc-gnu + regex + save-cwd +" + +gnulib_tool_option_extras="\ + --no-conditional-dependencies \ + --no-vc-files \ +" + +gnulib_name=libgnu +source_base=grub-core/lib/gnulib +gnulib_extra_files=" + build-aux/install-sh + build-aux/mdate-sh + build-aux/texinfo.tex + build-aux/depcomp + build-aux/config.guess + build-aux/config.sub +" + +# Additional xgettext options to use. Use "\\\newline" to break lines. +XGETTEXT_OPTIONS=$XGETTEXT_OPTIONS'\\\ + --from-code=UTF-8\\\ +' + +checkout_only_file= +copy=true +vc_ignore= + +SKIP_PO=t + +# Build prerequisites +buildreq="\ +autoconf 2.63 +automake 1.11 +gettext 0.18.3 +git 1.5.5 +tar - +" + +# bootstrap doesn't give us a reasonable way to stop Automake from +# overwriting this, so we just copy our version aside and put it back later. +cp -a INSTALL INSTALL.grub + +bootstrap_post_import_hook () { + set -e + for patchname in fix-base64 fix-null-deref fix-null-state-deref fix-regcomp-uninit-token \ + fix-regexec-null-deref fix-uninit-structure fix-unused-value fix-width no-abort; do + patch -d grub-core/lib/gnulib -p2 \ + < "grub-core/lib/gnulib-patches/$patchname.patch" + done + for patchname in \ + 0001-Support-POTFILES-shell \ + 0002-Handle-gettext_printf-shell-function \ + 0003-Make-msgfmt-output-in-little-endian \ + 0004-Use-SHELL-rather-than-bin-sh; do + patch -d po -p3 \ + < "po/gettext-patches/$patchname.patch" + done + FROM_BOOTSTRAP=1 ./autogen.sh + set +e # bootstrap expects this +} + +bootstrap_epilogue () { + mv INSTALL.grub INSTALL +} diff --git a/sbat.csv.in b/sbat.csv.in new file mode 100755 index 000000000..ea697f51a --- /dev/null +++ b/sbat.csv.in @@ -0,0 +1,3 @@ +sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md +grub,1,Free Software Foundation,grub,@@VERSION@@,https//www.gnu.org/software/grub/ +grub.fedora,1,The Fedora Project,grub2,@@VERSION_RELEASE@@,https://src.fedoraproject.org/rpms/grub2 diff --git a/strtoull_test.c b/strtoull_test.c new file mode 100644 index 000000000..5488ab26b --- /dev/null +++ b/strtoull_test.c @@ -0,0 +1,63 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2016 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 . + */ + +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static void +strtoull_testcase (const char *input, int base, unsigned long long expected, + int num_digits, grub_err_t error) +{ + const char *output; + unsigned long long value; + grub_errno = 0; + value = grub_strtoull(input, &output, base); + grub_test_assert (grub_errno == error, + "unexpected error. Expected %d, got %d. Input \"%s\"", + error, grub_errno, input); + if (grub_errno) + { + grub_errno = 0; + return; + } + grub_test_assert (input + num_digits == output, + "unexpected number of digits. Expected %d, got %d, input \"%s\"", + num_digits, (int) (output - input), input); + grub_test_assert (value == expected, + "unexpected return value. Expected %llu, got %llu, input \"\%s\"", + expected, value, input); +} + +static void +strtoull_test (void) +{ + strtoull_testcase ("9", 0, 9, 1, GRUB_ERR_NONE); + strtoull_testcase ("0xaa", 0, 0xaa, 4, GRUB_ERR_NONE); + strtoull_testcase ("0xff", 0, 0xff, 4, GRUB_ERR_NONE); + strtoull_testcase ("0", 10, 0, 1, GRUB_ERR_NONE); + strtoull_testcase ("8", 8, 0, 0, GRUB_ERR_BAD_NUMBER); + strtoull_testcase ("38", 8, 3, 1, GRUB_ERR_NONE); + strtoull_testcase ("7", 8, 7, 1, GRUB_ERR_NONE); + strtoull_testcase ("1]", 16, 1, 1, GRUB_ERR_NONE); + strtoull_testcase ("18446744073709551616", 10, 0, 0, GRUB_ERR_OUT_OF_RANGE); +} + + +GRUB_FUNCTIONAL_TEST (strtoull_test, strtoull_test); diff --git a/tests/util/grub-shell-tester.in b/tests/util/grub-shell-tester.in index 8a87109b1..9a4319d4f 100644 --- a/tests/util/grub-shell-tester.in +++ b/tests/util/grub-shell-tester.in @@ -56,7 +56,7 @@ for option in "$@"; do usage exit 0 ;; -v | --version) - echo "$0 (GNU GRUB ${PACKAGE_VERSION})" + echo "$0 (GRUB ${PACKAGE_VERSION})" exit 0 ;; --modules=*) ms=`echo "$option" | sed -e 's/--modules=//'` diff --git a/tests/util/grub-shell.in b/tests/util/grub-shell.in index 15c5f45a5..a75896928 100644 --- a/tests/util/grub-shell.in +++ b/tests/util/grub-shell.in @@ -339,7 +339,7 @@ for option in "$@"; do usage exit 0 ;; -v | --version) - echo "$0 (GNU GRUB ${PACKAGE_VERSION})" + echo "$0 (GRUB ${PACKAGE_VERSION})" exit 0 ;; --trim) trim=1 ;; diff --git a/theme.tar.bz2 b/theme.tar.bz2 new file mode 100644 index 000000000..c1035b08f Binary files /dev/null and b/theme.tar.bz2 differ diff --git a/unifont-5.1.20080820.pcf.gz b/unifont-5.1.20080820.pcf.gz new file mode 100644 index 000000000..2b1ce37cf Binary files /dev/null and b/unifont-5.1.20080820.pcf.gz differ diff --git a/util/bash-completion.d/Makefile.am b/util/bash-completion.d/Makefile.am index 33fff9546..2218eb5a4 100644 --- a/util/bash-completion.d/Makefile.am +++ b/util/bash-completion.d/Makefile.am @@ -58,7 +58,6 @@ CLEANFILES = $(bash_completion_script) \ $(grub_sparc64_setup_script) \ config.log -bashcompletiondir = $(datarootdir)/bash-completion/completions bashcompletion_DATA = $(bash_completion_script) \ $(grub_bios_setup_script) \ $(grub_editenv_script) \ diff --git a/util/bash-completion.d/grub-completion.bash.in b/util/bash-completion.d/grub-completion.bash.in index 749a5d3cf..c07d47daf 100644 --- a/util/bash-completion.d/grub-completion.bash.in +++ b/util/bash-completion.d/grub-completion.bash.in @@ -235,6 +235,28 @@ __grub_setup () { fi } +# +# grub-get-kernel-settings +# +_grub_get_kernel_settings () { + local cur + + COMPREPLY=() + cur=`_get_cword` + + if [[ "$cur" == -* ]]; then + __grubcomp "$(__grub_get_options_from_help)" + else + # Default complete with a filename + _filedir + fi +} +__grub_get_kernel_settings_program="@grub_get_kernel_settings@" +have ${__grub_get_kernel_settings_program} && \ + complete -F _grub_get_kernel_settings -o filenames ${__grub_get_kernel_settings_program} +unset __grub_get_kernel_settings_program + + # # grub-install # diff --git a/util/config.c b/util/config.c index ebcdd8f5e..f044a880a 100644 --- a/util/config.c +++ b/util/config.c @@ -42,6 +42,16 @@ grub_util_parse_config (FILE *f, struct grub_util_config *cfg, int simple) cfg->is_cryptodisk_enabled = 1; continue; } + if (grub_strncmp (ptr, "SUSE_BTRFS_SNAPSHOT_BOOTING=", + sizeof ("SUSE_BTRFS_SNAPSHOT_BOOTING=") - 1) == 0) + { + ptr += sizeof ("SUSE_BTRFS_SNAPSHOT_BOOTING=") - 1; + if (*ptr == '"' || *ptr == '\'') + ptr++; + if (grub_strncmp(ptr, "true", sizeof ("true") - 1) == 0) + cfg->is_suse_btrfs_snapshot_enabled = 1; + continue; + } if (grub_strncmp (ptr, "GRUB_DISTRIBUTOR=", sizeof ("GRUB_DISTRIBUTOR=") - 1) == 0) { diff --git a/util/grub-editenv.c b/util/grub-editenv.c index db6f187cc..948eec8a1 100644 --- a/util/grub-editenv.c +++ b/util/grub-editenv.c @@ -53,6 +53,9 @@ static struct argp_option options[] = { /* TRANSLATORS: "unset" is a keyword. It's a summary of "unset" subcommand. */ {N_("unset [NAME ...]"), 0, 0, OPTION_DOC|OPTION_NO_USAGE, N_("Delete variables."), 0}, + /* TRANSLATORS: "incr" is a keyword. It's a summary of "incr" subcommand. */ + {N_("incr [NAME ...]"), 0, 0, OPTION_DOC|OPTION_NO_USAGE, + N_("Increase value of integer variables."), 0}, {0, 0, 0, OPTION_DOC, N_("Options:"), -1}, {"verbose", 'v', 0, 0, N_("print verbose messages."), 0}, @@ -253,6 +256,51 @@ unset_variables (const char *name, int argc, char *argv[]) grub_envblk_close (envblk); } +struct get_int_value_params { + char *varname; + int value; +}; + +static int +get_int_value (const char *varname, const char *value, void *hook_data) +{ + struct get_int_value_params *params = hook_data; + + if (strcmp (varname, params->varname) == 0) { + params->value = strtol (value, NULL, 10); + return 1; + } + return 0; +} + +static void +incr_variables (const char *name, int argc, char *argv[]) +{ + grub_envblk_t envblk; + char buf[16]; + + envblk = open_envblk_file (name); + while (argc) + { + struct get_int_value_params params = { + .varname = argv[0], + .value = 0, /* Consider unset variables 0 */ + }; + + grub_envblk_iterate (envblk, ¶ms, get_int_value); + snprintf(buf, sizeof(buf), "%d", params.value + 1); + + if (! grub_envblk_set (envblk, argv[0], buf)) + grub_util_error ("%s", _("environment block too small")); + + argc--; + argv++; + } + + write_envblk (name, envblk); + grub_envblk_close (envblk); +} + int main (int argc, char *argv[]) { @@ -292,6 +340,8 @@ main (int argc, char *argv[]) set_variables (filename, argc - curindex, argv + curindex); else if (strcmp (command, "unset") == 0) unset_variables (filename, argc - curindex, argv + curindex); + else if (strcmp (command, "incr") == 0) + incr_variables (filename, argc - curindex, argv + curindex); else { char *program = xstrdup(program_name); diff --git a/util/grub-fstest.c b/util/grub-fstest.c index 7ff9037b8..b04d30ca7 100644 --- a/util/grub-fstest.c +++ b/util/grub-fstest.c @@ -330,7 +330,7 @@ cmd_cmp (char *src, char *dest) read_file (src, cmp_hook, ff); { - grub_uint64_t pre; + long long pre; pre = ftell (ff); fseek (ff, 0, SEEK_END); if (pre != ftell (ff)) diff --git a/util/grub-get-kernel-settings.in b/util/grub-get-kernel-settings.in new file mode 100644 index 000000000..7e87dfccc --- /dev/null +++ b/util/grub-get-kernel-settings.in @@ -0,0 +1,88 @@ +#!/bin/sh +set -e + +# Evaluate new-kernel-pkg's configuration file. +# Copyright (C) 2016 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 . + +PACKAGE_NAME=@PACKAGE_NAME@ +PACKAGE_VERSION=@PACKAGE_VERSION@ +datadir="@datadir@" +if [ "x$pkgdatadir" = x ]; then + pkgdatadir="${datadir}/@PACKAGE@" +fi + +self=`basename $0` + +export TEXTDOMAIN=@PACKAGE@ +export TEXTDOMAINDIR="@localedir@" + +. "${pkgdatadir}/grub-mkconfig_lib" + +# Usage: usage +# Print the usage. +usage () { + gettext_printf "Usage: %s [OPTION]\n" "$self" + gettext "Evaluate new-kernel-pkg configuration"; echo + echo + print_option_help "-h, --help" "$(gettext "print this message and exit")" + print_option_help "-v, --version" "$(gettext "print the version information and exit")" + echo +} + +# Check the arguments. +while test $# -gt 0 +do + option=$1 + shift + + case "$option" in + -h | --help) + usage + exit 0 ;; + -v | --version) + echo "$self (${PACKAGE_NAME}) ${PACKAGE_VERSION}" + exit 0 ;; + -*) + gettext_printf "Unrecognized option \`%s'\n" "$option" 1>&2 + usage + exit 1 + ;; + # Explicitly ignore non-option arguments, for compatibility. + esac +done + +if test -f /etc/sysconfig/kernel ; then + . /etc/sysconfig/kernel +fi + +if [ "$MAKEDEBUG" = "yes" ]; then + echo GRUB_LINUX_MAKE_DEBUG=true + echo export GRUB_LINUX_MAKE_DEBUG + echo GRUB_CMDLINE_LINUX_DEBUG=\"systemd.log_level=debug systemd.log_target=kmsg\" + echo export GRUB_CMDLINE_LINUX_DEBUG + echo GRUB_LINUX_DEBUG_TITLE_POSTFIX=\" with debugging\" + echo export GRUB_LINUX_DEBUG_TITLE_POSTFIX +fi +if [ "$DEFAULTDEBUG" = "yes" ]; then + echo GRUB_DEFAULT_TO_DEBUG=true +else + echo GRUB_DEFAULT_TO_DEBUG=false +fi +echo export GRUB_DEFAULT_TO_DEBUG +if [ "$UPDATEDEFAULT" = "yes" ]; then + echo GRUB_UPDATE_DEFAULT_KERNEL=true + echo export GRUB_UPDATE_DEFAULT_KERNEL +fi diff --git a/util/grub-install.c b/util/grub-install.c index 7dc5657bb..d7f9e54b1 100644 --- a/util/grub-install.c +++ b/util/grub-install.c @@ -843,6 +843,8 @@ try_open (const char *path) } #endif +extern int use_relative_path_on_btrfs; + int main (int argc, char *argv[]) { @@ -876,6 +878,9 @@ main (int argc, char *argv[]) grub_util_load_config (&config); + if (config.is_suse_btrfs_snapshot_enabled) + use_relative_path_on_btrfs = 1; + if (!bootloader_id && config.grub_distributor) { char *ptr; @@ -910,6 +915,25 @@ main (int argc, char *argv[]) platform = grub_install_get_target (grub_install_source_directory); + switch (platform) + { + case GRUB_INSTALL_PLATFORM_I386_EFI: + case GRUB_INSTALL_PLATFORM_X86_64_EFI: + case GRUB_INSTALL_PLATFORM_ARM_EFI: + case GRUB_INSTALL_PLATFORM_ARM64_EFI: + case GRUB_INSTALL_PLATFORM_LOONGARCH64_EFI: + case GRUB_INSTALL_PLATFORM_RISCV32_EFI: + case GRUB_INSTALL_PLATFORM_RISCV64_EFI: + case GRUB_INSTALL_PLATFORM_IA64_EFI: + is_efi = 1; + grub_util_error (_("this utility cannot be used for EFI platforms" + " because it does not support UEFI Secure Boot")); + break; + default: + is_efi = 0; + break; + } + { char *platname = grub_install_get_platform_name (platform); fprintf (stderr, _("Installing for %s platform.\n"), platname); @@ -1024,27 +1048,6 @@ main (int argc, char *argv[]) grub_hostfs_init (); grub_host_init (); - switch (platform) - { - case GRUB_INSTALL_PLATFORM_I386_EFI: - case GRUB_INSTALL_PLATFORM_X86_64_EFI: - case GRUB_INSTALL_PLATFORM_ARM_EFI: - case GRUB_INSTALL_PLATFORM_ARM64_EFI: - case GRUB_INSTALL_PLATFORM_LOONGARCH64_EFI: - case GRUB_INSTALL_PLATFORM_RISCV32_EFI: - case GRUB_INSTALL_PLATFORM_RISCV64_EFI: - case GRUB_INSTALL_PLATFORM_IA64_EFI: - is_efi = 1; - break; - default: - is_efi = 0; - break; - - /* pacify warning. */ - case GRUB_INSTALL_PLATFORM_MAX: - break; - } - switch (platform) { case GRUB_INSTALL_PLATFORM_I386_IEEE1275: @@ -1060,7 +1063,6 @@ main (int argc, char *argv[]) } /* Find the EFI System Partition. */ - if (is_efi) { grub_fs_t fs; @@ -1421,6 +1423,15 @@ main (int argc, char *argv[]) debug_image); } + if (config.is_suse_btrfs_snapshot_enabled + && grub_strncmp(grub_fs->name, "btrfs", sizeof ("btrfs") - 1) == 0) + { + if (!load_cfg_f) + load_cfg_f = grub_util_fopen (load_cfg, "wb"); + have_load_cfg = 1; + fprintf (load_cfg_f, "set btrfs_relative_path='y'\n"); + } + if (!have_abstractions) { if ((disk_module && grub_strcmp (disk_module, "biosdisk") != 0) @@ -1601,6 +1612,55 @@ main (int argc, char *argv[]) prefix_drive = xasprintf ("(%s)", grub_drives[0]); } +#ifdef __linux__ + + if (config.is_suse_btrfs_snapshot_enabled + && grub_strncmp(grub_fs->name, "btrfs", sizeof ("btrfs") - 1) == 0) + { + char *subvol = NULL; + char *mount_path = NULL; + char **rootdir_devices = NULL; + char *rootdir_path = grub_util_path_concat (2, "/", rootdir); + + if (grub_util_is_directory (rootdir_path)) + rootdir_devices = grub_guess_root_devices (rootdir_path); + + free (rootdir_path); + + if (rootdir_devices && rootdir_devices[0]) + if (grub_strcmp (rootdir_devices[0], grub_devices[0]) == 0) + subvol = grub_util_get_btrfs_subvol (platdir, &mount_path); + + if (subvol && mount_path) + { + char *def_subvol; + + def_subvol = grub_util_get_btrfs_subvol ("/", NULL); + + if (def_subvol) + { + if (!load_cfg_f) + load_cfg_f = grub_util_fopen (load_cfg, "wb"); + have_load_cfg = 1; + + if (grub_strcmp (subvol, def_subvol) != 0) + fprintf (load_cfg_f, "btrfs-mount-subvol ($root) %s %s\n", mount_path, subvol); + free (def_subvol); + } + } + + for (curdev = rootdir_devices; *curdev; curdev++) + free (*curdev); + if (rootdir_devices) + free (rootdir_devices); + if (subvol) + free (subvol); + if (mount_path) + free (mount_path); + } + +#endif + char mkimage_target[200]; const char *core_name = NULL; diff --git a/util/grub-menulst2cfg.c b/util/grub-menulst2cfg.c index b80e15cc3..1ea2a86e1 100644 --- a/util/grub-menulst2cfg.c +++ b/util/grub-menulst2cfg.c @@ -34,7 +34,7 @@ main (int argc, char **argv) char *buf = NULL; size_t bufsize = 0; char *suffix = xstrdup (""); - int suffixlen = 0; + size_t suffixlen = 0; const char *out_fname = 0; grub_util_host_init (&argc, &argv); diff --git a/util/grub-mkconfig.in b/util/grub-mkconfig.in index 32c480dae..592d99338 100644 --- a/util/grub-mkconfig.in +++ b/util/grub-mkconfig.in @@ -45,6 +45,7 @@ grub_probe="${sbindir}/@grub_probe@" grub_file="${bindir}/@grub_file@" grub_editenv="${bindir}/@grub_editenv@" grub_script_check="${bindir}/@grub_script_check@" +grub_get_kernel_settings="${sbindir}/@grub_get_kernel_settings@" export TEXTDOMAIN=@PACKAGE@ export TEXTDOMAINDIR="@localedir@" @@ -161,6 +162,8 @@ if test -f ${sysconfdir}/default/grub ; then . ${sysconfdir}/default/grub fi +eval "$("${grub_get_kernel_settings}")" || true + if [ "x${GRUB_DISABLE_UUID}" = "xtrue" ]; then if [ -z "${GRUB_DISABLE_LINUX_UUID}" ]; then GRUB_DISABLE_LINUX_UUID="true" @@ -255,7 +258,8 @@ export GRUB_DEFAULT \ GRUB_ENABLE_CRYPTODISK \ GRUB_BADRAM \ GRUB_OS_PROBER_SKIP_LIST \ - GRUB_DISABLE_SUBMENU + GRUB_DISABLE_SUBMENU \ + SUSE_BTRFS_SNAPSHOT_BOOTING if test "x${grub_cfg}" != "x"; then rm -f "${grub_cfg}.new" @@ -282,6 +286,8 @@ for i in "${grub_mkconfig_dir}"/* ; do *~) ;; # emacsen autosave files. FIXME: support other editors */\#*\#) ;; + # rpm config files of yore. + *.rpmsave|*.rpmnew|*.rpmorig) ;; *) if grub_file_is_not_garbage "$i" && test -x "$i" ; then echo diff --git a/util/grub-mkconfig_lib.in b/util/grub-mkconfig_lib.in index 08953287c..1b988c957 100644 --- a/util/grub-mkconfig_lib.in +++ b/util/grub-mkconfig_lib.in @@ -49,7 +49,11 @@ grub_warn () make_system_path_relative_to_its_root () { + if [ "x${SUSE_BTRFS_SNAPSHOT_BOOTING}" = "xtrue" ] ; then + "${grub_mkrelpath}" -r "$1" + else "${grub_mkrelpath}" "$1" + fi } is_path_readable_by_grub () diff --git a/util/grub-mkfont.c b/util/grub-mkfont.c index 7624d7808..e8a914021 100644 --- a/util/grub-mkfont.c +++ b/util/grub-mkfont.c @@ -143,7 +143,8 @@ add_glyph (struct grub_font_info *font_info, FT_UInt glyph_idx, FT_Face face, int width, height; int cuttop, cutbottom, cutleft, cutright; grub_uint8_t *data; - int mask, i, j, bitmap_size; + int mask, i, bitmap_size; + unsigned int j; FT_GlyphSlot glyph; int flag = FT_LOAD_RENDER | FT_LOAD_MONOCHROME; FT_Error err; @@ -188,7 +189,7 @@ add_glyph (struct grub_font_info *font_info, FT_UInt glyph_idx, FT_Face face, cuttop = cutbottom = cutleft = cutright = 0; else { - for (cuttop = 0; cuttop < glyph->bitmap.rows; cuttop++) + for (cuttop = 0; cuttop < (long)glyph->bitmap.rows; cuttop++) { for (j = 0; j < glyph->bitmap.width; j++) if (glyph->bitmap.buffer[j / 8 + cuttop * glyph->bitmap.pitch] @@ -208,10 +209,10 @@ add_glyph (struct grub_font_info *font_info, FT_UInt glyph_idx, FT_Face face, break; } cutbottom = glyph->bitmap.rows - 1 - cutbottom; - if (cutbottom + cuttop >= glyph->bitmap.rows) + if (cutbottom + cuttop >= (long)glyph->bitmap.rows) cutbottom = 0; - for (cutleft = 0; cutleft < glyph->bitmap.width; cutleft++) + for (cutleft = 0; cutleft < (long)glyph->bitmap.width; cutleft++) { for (j = 0; j < glyph->bitmap.rows; j++) if (glyph->bitmap.buffer[cutleft / 8 + j * glyph->bitmap.pitch] @@ -230,7 +231,7 @@ add_glyph (struct grub_font_info *font_info, FT_UInt glyph_idx, FT_Face face, break; } cutright = glyph->bitmap.width - 1 - cutright; - if (cutright + cutleft >= glyph->bitmap.width) + if (cutright + cutleft >= (long)glyph->bitmap.width) cutright = 0; } @@ -267,7 +268,7 @@ add_glyph (struct grub_font_info *font_info, FT_UInt glyph_idx, FT_Face face, mask = 0; data = &glyph_info->bitmap[0] - 1; - for (j = cuttop; j < height + cuttop; j++) + for (j = cuttop; j < (long)height + cuttop; j++) for (i = cutleft; i < width + cutleft; i++) add_pixel (&data, &mask, glyph->bitmap.buffer[i / 8 + j * glyph->bitmap.pitch] & diff --git a/util/grub-mkrelpath.c b/util/grub-mkrelpath.c index 47a241a39..5db7a9a7d 100644 --- a/util/grub-mkrelpath.c +++ b/util/grub-mkrelpath.c @@ -40,9 +40,12 @@ struct arguments }; static struct argp_option options[] = { + {"relative", 'r', 0, 0, "use relative path on btrfs", 0}, { 0, 0, 0, 0, 0, 0 } }; +extern int use_relative_path_on_btrfs; + static error_t argp_parser (int key, char *arg, struct argp_state *state) { @@ -52,6 +55,9 @@ argp_parser (int key, char *arg, struct argp_state *state) switch (key) { + case 'r': + use_relative_path_on_btrfs = 1; + break; case ARGP_KEY_ARG: if (state->arg_num == 0) arguments->pathname = xstrdup (arg); diff --git a/util/grub-probe.c b/util/grub-probe.c index 65c1ca3f8..08ae915e8 100644 --- a/util/grub-probe.c +++ b/util/grub-probe.c @@ -799,7 +799,7 @@ argp_parser (int key, char *arg, struct argp_state *state) case 't': { - int i; + unsigned int i; for (i = PRINT_FS; i < ARRAY_SIZE (targets); i++) if (strcmp (arg, targets[i]) == 0) diff --git a/util/grub-set-bootflag.c b/util/grub-set-bootflag.c new file mode 100644 index 000000000..3b4c25ca2 --- /dev/null +++ b/util/grub-set-bootflag.c @@ -0,0 +1,242 @@ +/* grub-set-bootflag.c - tool to set boot-flags in the grubenv. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2018 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 . + */ + +/* + * NOTE this gets run by users as root (its suid root), so this does not + * use any grub library / util functions to allow for easy auditing. + * The grub headers are only included to get certain defines. + */ + +#include /* For *_DIR_NAME defines */ +#include +#include +#include /* For GRUB_ENVBLK_DEFCFG define */ +#include +#include +#include +#include +#include +#include + +#include "progname.h" + +#define GRUBENV "/" GRUB_BOOT_DIR_NAME "/" GRUB_DIR_NAME "/" GRUB_ENVBLK_DEFCFG +#define GRUBENV_SIZE 1024 + +const char *bootflags[] = { + "boot_success", + "menu_show_once", + NULL +}; + +static void usage(FILE *out) +{ + int i; + + fprintf (out, "Usage: 'grub-set-bootflag ', where is one of:\n"); + for (i = 0; bootflags[i]; i++) + fprintf (out, " %s\n", bootflags[i]); +} + +int main(int argc, char *argv[]) +{ + /* NOTE buf must be at least the longest bootflag length + 4 bytes */ + char env[GRUBENV_SIZE + 1], buf[64], *s; + /* +1 for 0 termination, +6 for "XXXXXX" in tmp filename */ + char env_filename[PATH_MAX + 1], tmp_filename[PATH_MAX + 6 + 1]; + const char *bootflag; + int i, fd, len, ret; + FILE *f; + + if (argc != 2) + { + usage (stderr); + return 1; + } + else if (!strcmp (argv[1], "--help")) + { + usage (stdout); + return 0; + } + else if (!strcmp (argv[1], "--version")) + { + printf ("grub-set-bootflag (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION); + return 0; + } + + for (i = 0; bootflags[i]; i++) + if (!strcmp (argv[1], bootflags[i])) + break; + if (!bootflags[i]) + { + fprintf (stderr, "Invalid bootflag: '%s'\n", argv[1]); + usage (stderr); + return 1; + } + + bootflag = bootflags[i]; + len = strlen (bootflag); + + /* + * Really become root. setuid avoids an user killing us, possibly leaking + * the tmpfile. setgid avoids the new grubenv's gid being that of the user. + */ + ret = setuid(0); + if (ret) + { + perror ("Error setuid(0) failed"); + return 1; + } + + ret = setgid(0); + if (ret) + { + perror ("Error setgid(0) failed"); + return 1; + } + + /* Canonicalize GRUBENV filename, resolving symlinks, etc. */ + if (!realpath(GRUBENV, env_filename)) + { + perror ("Error canonicalizing " GRUBENV " filename"); + return 1; + } + + f = fopen (env_filename, "r"); + if (!f) + { + perror ("Error opening " GRUBENV " for reading"); + return 1; + } + + ret = fread (env, 1, GRUBENV_SIZE, f); + fclose (f); + if (ret != GRUBENV_SIZE) + { + errno = EINVAL; + perror ("Error reading from " GRUBENV); + return 1; + } + + /* 0 terminate env */ + env[GRUBENV_SIZE] = 0; + + if (strncmp (env, GRUB_ENVBLK_SIGNATURE, strlen (GRUB_ENVBLK_SIGNATURE))) + { + fprintf (stderr, "Error invalid environment block\n"); + return 1; + } + + /* Find a pre-existing definition of the bootflag */ + s = strstr (env, bootflag); + while (s && s[len] != '=') + s = strstr (s + len, bootflag); + + if (s && ((s[len + 1] != '0' && s[len + 1] != '1') || s[len + 2] != '\n')) + { + fprintf (stderr, "Pre-existing bootflag '%s' has unexpected value\n", bootflag); + return 1; + } + + /* No pre-existing bootflag? -> find free space */ + if (!s) + { + for (i = 0; i < (len + 3); i++) + buf[i] = '#'; + buf[i] = 0; + s = strstr (env, buf); + } + + if (!s) + { + fprintf (stderr, "No space in grubenv to store bootflag '%s'\n", bootflag); + return 1; + } + + /* The grubenv is not 0 terminated, so memcpy the name + '=' , '1', '\n' */ + snprintf(buf, sizeof(buf), "%s=1\n", bootflag); + memcpy(s, buf, len + 3); + + + /* + * Create a tempfile for writing the new env. Use the canonicalized filename + * for the template so that the tmpfile is in the same dir / on same fs. + */ + snprintf(tmp_filename, sizeof(tmp_filename), "%sXXXXXX", env_filename); + fd = mkstemp(tmp_filename); + if (fd == -1) + { + perror ("Creating tmpfile failed"); + return 1; + } + + f = fdopen (fd, "w"); + if (!f) + { + perror ("Error fdopen of tmpfile failed"); + unlink(tmp_filename); + return 1; + } + + ret = fwrite (env, 1, GRUBENV_SIZE, f); + if (ret != GRUBENV_SIZE) + { + perror ("Error writing tmpfile"); + unlink(tmp_filename); + return 1; + } + + ret = fflush (f); + if (ret) + { + perror ("Error flushing tmpfile"); + unlink(tmp_filename); + return 1; + } + + ret = fsync (fileno (f)); + if (ret) + { + perror ("Error syncing tmpfile"); + unlink(tmp_filename); + return 1; + } + + ret = fclose (f); + if (ret) + { + perror ("Error closing tmpfile"); + unlink(tmp_filename); + return 1; + } + + /* + * And finally rename the tmpfile with the new env over the old env, the + * linux kernel guarantees that this is atomic (from a syscall pov). + */ + ret = rename(tmp_filename, env_filename); + if (ret) + { + perror ("Error renaming tmpfile to " GRUBENV " failed"); + unlink(tmp_filename); + return 1; + } + + return 0; +} diff --git a/util/grub-set-password.in b/util/grub-set-password.in new file mode 100644 index 000000000..5ebf50576 --- /dev/null +++ b/util/grub-set-password.in @@ -0,0 +1,128 @@ +#!/bin/sh -e + +EFIDIR=$(grep ^ID= /etc/os-release | sed -e 's/^ID=//' -e 's/rhel/redhat/') +if [ -d /sys/firmware/efi/efivars/ ]; then + grubdir=`echo "/@bootdirname@/efi/EFI/${EFIDIR}/" | sed 's,//*,/,g'` +else + grubdir=`echo "/@bootdirname@/@grubdirname@" | sed 's,//*,/,g'` +fi + +PACKAGE_VERSION="@PACKAGE_VERSION@" +PACKAGE_NAME="@PACKAGE_NAME@" +self=`basename $0` +bindir="@bindir@" +grub_mkpasswd="${bindir}/@grub_mkpasswd_pbkdf2@" + +# Usage: usage +# Print the usage. +usage () { + cat < put user.cfg in a user-selected directory + +Report bugs at https://bugzilla.redhat.com. +EOF +} + +argument () { + opt=$1 + shift + + if test $# -eq 0; then + gettext_printf "%s: option requires an argument -- \`%s'\n" "$self" "$opt" 1>&2 + exit 1 + fi + echo $1 +} + +# Ensure that it's the root user running this script +if [ "${EUID}" -ne 0 ]; then + echo "The grub bootloader password may only be set by root." + usage + exit 2 +fi + +# Check the arguments. +while test $# -gt 0 +do + option=$1 + shift + + case "$option" in + -h | --help) + usage + exit 0 ;; + -v | --version) + echo "$self (${PACKAGE_NAME}) ${PACKAGE_VERSION}" + exit 0 ;; + -o | --output) + OUTPUT_PATH=`argument $option "$@"`; shift ;; + --output=*) + OUTPUT_PATH=`echo "$option" | sed 's/--output=//'` ;; + -o=*) + OUTPUT_PATH=`echo "$option" | sed 's/-o=//'` ;; + esac +done + +# set user input or default path for user.cfg file +if [ -z "${OUTPUT_PATH}" ]; then + OUTPUT_PATH="${grubdir}" +fi + +if [ ! -d "${OUTPUT_PATH}" ]; then + echo "${OUTPUT_PATH} does not exist." + usage + exit 2; +fi + +ttyopt=$(stty -g) +fixtty() { + stty ${ttyopt} +} + +trap fixtty EXIT +stty -echo + +# prompt & confirm new grub2 root user password +echo -n "Enter password: " +read PASSWORD +echo +echo -n "Confirm password: " +read PASSWORD_CONFIRM +echo +stty ${ttyopt} + +getpass() { + local P0 + local P1 + P0="$1" && shift + P1="$1" && shift + + ( echo ${P0} ; echo ${P1} ) | \ + LC_ALL=C ${grub_mkpasswd} | \ + grep -v '[eE]nter password:' | \ + sed -e "s/PBKDF2 hash of your password is //" +} + +MYPASS="$(getpass "${PASSWORD}" "${PASSWORD_CONFIRM}")" +if [ -z "${MYPASS}" ]; then + echo "${self}: error: empty password" 1>&2 + exit 1 +fi + +# on the ESP, these will fail to set the permissions, but it's okay because +# the directory is protected. +install -m 0600 /dev/null "${OUTPUT_PATH}/user.cfg" 2>/dev/null || : +chmod 0600 "${OUTPUT_PATH}/user.cfg" 2>/dev/null || : +echo "GRUB2_PASSWORD=${MYPASS}" > "${OUTPUT_PATH}/user.cfg" + +if ! grep -q "^### BEGIN /etc/grub.d/01_users ###$" "${OUTPUT_PATH}/grub.cfg"; then + echo "WARNING: The current configuration lacks password support!" + echo "Update your configuration with @grub_mkconfig@ to support this feature." +fi diff --git a/util/grub.d/00_header.in b/util/grub.d/00_header.in index f86b69bad..64e054829 100644 --- a/util/grub.d/00_header.in +++ b/util/grub.d/00_header.in @@ -27,6 +27,14 @@ export TEXTDOMAINDIR="@localedir@" . "$pkgdatadir/grub-mkconfig_lib" +if [ "x${SUSE_BTRFS_SNAPSHOT_BOOTING}" = "xtrue" ] && + [ "x${GRUB_FS}" = "xbtrfs" ] ; then + cat </dev/null || true` @@ -79,6 +84,32 @@ case x"$GRUB_FS" in ;; esac +mktitle () +{ + local title_type + local version + local OS_NAME + local OS_VERS + + title_type=$1 && shift + version=$1 && shift + + OS_NAME="$(eval $(grep ^NAME= /etc/os-release) ; echo ${NAME})" + OS_VERS="$(eval $(grep ^VERSION= /etc/os-release) ; echo ${VERSION})" + + case $title_type in + recovery) + title=$(printf '%s (%s) %s (recovery mode)' \ + "${OS_NAME}" "${version}" "${OS_VERS}") + ;; + *) + title=$(printf '%s (%s) %s' \ + "${OS_NAME}" "${version}" "${OS_VERS}") + ;; + esac + echo -n ${title} +} + title_correction_code= linux_entry () @@ -86,23 +117,21 @@ linux_entry () os="$1" version="$2" type="$3" - args="$4" + isdebug="$4" + args="$5" if [ -z "$boot_device_id" ]; then boot_device_id="$(grub_get_device_id "${GRUB_DEVICE}")" fi if [ x$type != xsimple ] ; then - case $type in - recovery) - title="$(gettext_printf "%s, with Linux %s (recovery mode)" "${os}" "${version}")" ;; - *) - title="$(gettext_printf "%s, with Linux %s" "${os}" "${version}")" ;; - esac + title=$(mktitle "$type" "$version") if [ x"$title" = x"$GRUB_ACTUAL_DEFAULT" ] || [ x"Previous Linux versions>$title" = x"$GRUB_ACTUAL_DEFAULT" ]; then replacement_title="$(echo "Advanced options for ${OS}" | sed 's,>,>>,g')>$(echo "$title" | sed 's,>,>>,g')" 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 + if [ x$isdebug = xdebug ]; then + title="$title${GRUB_LINUX_DEBUG_TITLE_POSTFIX}" fi echo "menuentry '$(echo "$title" | grub_quote)' ${CLASS} \$menuentry_id_option 'gnulinux-$version-$type-$boot_device_id' {" | sed "s/^/$submenu_indentation/" else @@ -284,11 +313,15 @@ for linux in ${reverse_sorted_list}; do fi if [ "x$is_top_level" = xtrue ] && [ "x${GRUB_DISABLE_SUBMENU}" != xtrue ]; then - linux_entry "${OS}" "${version}" simple \ + linux_entry "${OS}" "${version}" simple standard \ "${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT}" + if [ "x$GRUB_LINUX_MAKE_DEBUG" = "xtrue" ]; then + linux_entry "${OS}" "${version}" simple debug \ + "${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT} ${GRUB_CMDLINE_LINUX_DEBUG}" + fi submenu_indentation="$grub_tab" - + if [ -z "$boot_device_id" ]; then boot_device_id="$(grub_get_device_id "${GRUB_DEVICE}")" fi @@ -297,10 +330,15 @@ for linux in ${reverse_sorted_list}; do is_top_level=false fi - linux_entry "${OS}" "${version}" advanced \ + linux_entry "${OS}" "${version}" advanced standard \ "${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT}" + if [ "x$GRUB_LINUX_MAKE_DEBUG" = "xtrue" ]; then + linux_entry "${OS}" "${version}" advanced debug \ + "${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT} ${GRUB_CMDLINE_LINUX_DEBUG}" + fi + if [ "x${GRUB_DISABLE_RECOVERY}" != "xtrue" ]; then - linux_entry "${OS}" "${version}" recovery \ + linux_entry "${OS}" "${version}" recovery standard \ "${GRUB_CMDLINE_LINUX_RECOVERY} ${GRUB_CMDLINE_LINUX}" fi done diff --git a/util/grub.d/10_reset_boot_success.in b/util/grub.d/10_reset_boot_success.in new file mode 100644 index 000000000..737e1ae5b --- /dev/null +++ b/util/grub.d/10_reset_boot_success.in @@ -0,0 +1,25 @@ +#! /bin/sh -e +# Reset Boot Success +# +# The 08_fallback_counting and 12_menu_auto_hide snippets rely on this one +# and need to be kept in sync. +# +# The boot_success var needs to be set to 1 from userspace to mark a boot successful. +cat << EOF +# Hiding the menu is ok if last boot was ok or if this is a first boot attempt to boot the entry +if [ "\${boot_success}" = "1" -o "\${boot_indeterminate}" = "1" ]; then + set menu_hide_ok=1 +else + set menu_hide_ok=0 +fi +# Reset boot_indeterminate after a successful boot +if [ "\${boot_success}" = "1" ] ; then + set boot_indeterminate=0 +# Avoid boot_indeterminate causing the menu to be hidden more then once +elif [ "\${boot_indeterminate}" = "1" ]; then + set boot_indeterminate=2 +fi +# Reset boot_success for current boot +set boot_success=0 +save_env boot_success boot_indeterminate +EOF diff --git a/util/grub.d/12_menu_auto_hide.in b/util/grub.d/12_menu_auto_hide.in new file mode 100644 index 000000000..6a7c0fa0d --- /dev/null +++ b/util/grub.d/12_menu_auto_hide.in @@ -0,0 +1,35 @@ +#! /bin/sh +# Menu Auto Hide +# +# This snippet depends on 10_reset_boot_success and needs to be kept in sync. +# +# Disable / skip generating menu-auto-hide config parts on serial terminals +for x in ${GRUB_TERMINAL_INPUT} ${GRUB_TERMINAL_OUTPUT}; do + case "$x" in + serial*) + exit 0 + ;; + esac +done + +cat << EOF +if [ x\$feature_timeout_style = xy ] ; then + if [ "\${menu_show_once}" ]; then + unset menu_show_once + save_env menu_show_once + set timeout_style=menu + set timeout=60 + elif [ "\${menu_auto_hide}" -a "\${menu_hide_ok}" = "1" ]; then + set orig_timeout_style=\${timeout_style} + set orig_timeout=\${timeout} + if [ "\${fastboot}" = "1" ]; then + # timeout_style=menu + timeout=0 avoids the countdown code keypress check + set timeout_style=menu + set timeout=0 + else + set timeout_style=hidden + set timeout=1 + fi + fi +fi +EOF diff --git a/util/grub.d/14_menu_show_once.in b/util/grub.d/14_menu_show_once.in new file mode 100755 index 000000000..1cd7f3614 --- /dev/null +++ b/util/grub.d/14_menu_show_once.in @@ -0,0 +1,13 @@ +#! /bin/sh +# Force the menu to be shown once, with a timeout of ${menu_show_once_timeout} +# if requested by ${menu_show_once_timeout} being set in the env. +cat << EOF +if [ x\$feature_timeout_style = xy ]; then + if [ "\${menu_show_once_timeout}" ]; then + set timeout_style=menu + set timeout="\${menu_show_once_timeout}" + unset menu_show_once_timeout + save_env menu_show_once_timeout + fi +fi +EOF diff --git a/util/grub.d/20_linux_xen.in b/util/grub.d/20_linux_xen.in index 94dd8be13..4ecf5deea 100644 --- a/util/grub.d/20_linux_xen.in +++ b/util/grub.d/20_linux_xen.in @@ -29,9 +29,9 @@ export TEXTDOMAINDIR="@localedir@" CLASS="--class gnu-linux --class gnu --class os --class xen" if [ "x${GRUB_DISTRIBUTOR}" = "x" ] ; then - OS=GNU/Linux + OS="$(sed 's, release .*$,,g' /etc/system-release)" else - OS="${GRUB_DISTRIBUTOR} GNU/Linux" + OS="${GRUB_DISTRIBUTOR}" CLASS="--class $(echo ${GRUB_DISTRIBUTOR} | tr 'A-Z' 'a-z' | cut -d' ' -f1|LC_ALL=C sed 's,[^[:alnum:]_],_,g') ${CLASS}" fi @@ -75,10 +75,14 @@ fi case x"$GRUB_FS" in xbtrfs) + if [ "x${SUSE_BTRFS_SNAPSHOT_BOOTING}" = "xtrue" ]; then + GRUB_CMDLINE_LINUX="${GRUB_CMDLINE_LINUX} \${extra_cmdline}" + else 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 fi;; xzfs) rpool=`${grub_probe} --device ${GRUB_DEVICE} --target=fs_label 2>/dev/null || true` @@ -152,6 +156,7 @@ linux_entry_xsm () else xen_rm_opts="no-real-mode edd=off" fi + insmod ${xen_module} ${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} @@ -164,6 +169,7 @@ EOF initrd_path="${rel_dirname}/${i}" sed "s/^/$submenu_indentation/" << EOF echo '$(echo "$message" | grub_quote)' + insmod ${xen_module} ${module_loader} --nounzip $(echo $initrd_path) EOF done @@ -265,13 +271,16 @@ for current_xen in ${reverse_sorted_xen_list}; do 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_module="xen_boot" xen_loader="xen_hypervisor" module_loader="xen_module" else if ($grub_file --is-x86-multiboot2 $current_xen); then + xen_module="multiboot2" xen_loader="multiboot2" module_loader="module2" else + xen_module="multiboot" xen_loader="multiboot" module_loader="module" fi diff --git a/util/grub.d/30_os-prober.in b/util/grub.d/30_os-prober.in index 656301eaf..13db23dd2 100644 --- a/util/grub.d/30_os-prober.in +++ b/util/grub.d/30_os-prober.in @@ -27,7 +27,6 @@ export TEXTDOMAINDIR="@localedir@" . "$pkgdatadir/grub-mkconfig_lib" if [ "x${GRUB_DISABLE_OS_PROBER}" = "xtrue" ]; then - grub_warn "$(gettext_printf "os-prober will not be executed to detect other bootable partitions.\nSystems on them will not be added to the GRUB boot configuration.\nCheck GRUB_DISABLE_OS_PROBER documentation entry.")" exit 0 fi @@ -45,68 +44,26 @@ if [ -z "${OSPROBED}" ] ; then fi osx_entry() { - if [ x$2 = x32 ]; then - # TRANSLATORS: it refers to kernel architecture (32-bit) - bitstr="$(gettext "(32-bit)")" - else - # TRANSLATORS: it refers to kernel architecture (64-bit) - bitstr="$(gettext "(64-bit)")" - fi + found_other_os=1 # TRANSLATORS: it refers on the OS residing on device %s onstr="$(gettext_printf "(on %s)" "${DEVICE}")" - cat << EOF -menuentry '$(echo "${LONGNAME} $bitstr $onstr" | grub_quote)' --class osx --class darwin --class os \$menuentry_id_option 'osprober-xnu-$2-$(grub_get_device_id "${DEVICE}")' { + hints="" + for hint in `"${grub_probe}" --device ${device} --target=efi_hints 2> /dev/null` ; do + hints="${hints} --hint=${hint}" + done + cat << EOF +menuentry '$(echo "${LONGNAME} $onstr" | grub_quote)' --class osx --class darwin --class os \$menuentry_id_option 'osprober-xnu-$2-$(grub_get_device_id "${DEVICE}")' { EOF save_default_entry | grub_add_tab prepare_grub_to_access_device ${DEVICE} | grub_add_tab cat << EOF + set gfxpayload=keep load_video - set do_resume=0 - if [ /var/vm/sleepimage -nt10 / ]; then - if xnu_resume /var/vm/sleepimage; then - set do_resume=1 - fi - fi - if [ \$do_resume = 0 ]; then - xnu_uuid ${OSXUUID} uuid - if [ -f /Extra/DSDT.aml ]; then - acpi -e /Extra/DSDT.aml - fi - if [ /kernelcache -nt /System/Library/Extensions ]; then - $1 /kernelcache boot-uuid=\${uuid} rd=*uuid - elif [ -f /System/Library/Kernels/kernel ]; then - $1 /System/Library/Kernels/kernel boot-uuid=\${uuid} rd=*uuid - xnu_kextdir /System/Library/Extensions - else - $1 /mach_kernel boot-uuid=\${uuid} rd=*uuid - if [ /System/Library/Extensions.mkext -nt /System/Library/Extensions ]; then - xnu_mkext /System/Library/Extensions.mkext - else - xnu_kextdir /System/Library/Extensions - fi - fi - if [ -f /Extra/Extensions.mkext ]; then - xnu_mkext /Extra/Extensions.mkext - fi - if [ -d /Extra/Extensions ]; then - xnu_kextdir /Extra/Extensions - fi - if [ -f /Extra/devprop.bin ]; then - xnu_devprop_load /Extra/devprop.bin - fi - if [ -f /Extra/splash.jpg ]; then - insmod jpeg - xnu_splash /Extra/splash.jpg - fi - if [ -f /Extra/splash.png ]; then - insmod png - xnu_splash /Extra/splash.png - fi - if [ -f /Extra/splash.tga ]; then - insmod tga - xnu_splash /Extra/splash.tga - fi - fi + insmod part_gpt + insmod hfsplus + search --no-floppy --fs-uuid --set=root ${hints} $(grub_get_device_id "${DEVICE}") + chainloader (\$root)/System/Library/CoreServices/boot.efi + boot } EOF } @@ -152,6 +109,7 @@ for OS in ${OSPROBED} ; do case ${BOOT} in chain) + found_other_os=1 onstr="$(gettext_printf "(on %s)" "${DEVICE}")" cat << EOF @@ -182,6 +140,7 @@ EOF EOF ;; efi) + found_other_os=1 EFIPATH=${DEVICE#*@} DEVICE=${DEVICE%@*} @@ -226,6 +185,7 @@ EOF LINITRD="${LINITRD#/boot}" fi + found_other_os=1 onstr="$(gettext_printf "(on %s)" "${DEVICE}")" recovery_params="$(echo "${LPARAMS}" | grep single)" || true counter=1 @@ -299,13 +259,15 @@ EOF echo "$title_correction_code" ;; macosx) - if [ "${UUID}" ]; then - OSXUUID="${UUID}" - osx_entry xnu_kernel 32 - osx_entry xnu_kernel64 64 - fi + for subdevice in ${DEVICE%[[:digit:]]*}* ; do + parttype="`"${grub_probe}" --device ${device} --target=gpt_parttype "${subdevice}" 2> /dev/null`" + if [[ "$parttype" = "426f6f74-0000-11aa-aa11-00306543ecac" ]]; then + DEVICE="${subdevice}" osx_entry + fi + done ;; hurd) + found_other_os=1 onstr="$(gettext_printf "(on %s)" "${DEVICE}")" cat << EOF menuentry '$(echo "${LONGNAME} $onstr" | grub_quote)' --class hurd --class gnu --class os \$menuentry_id_option 'osprober-gnuhurd-/boot/gnumach.gz-false-$(grub_get_device_id "${DEVICE}")' { @@ -332,6 +294,7 @@ EOF EOF ;; minix) + found_other_os=1 cat << EOF menuentry "${LONGNAME} (on ${DEVICE}, Multiboot)" { EOF @@ -348,3 +311,15 @@ EOF ;; esac done + +# We override the results of the menu_auto_hide code here, this is a bit ugly, +# but grub-mkconfig writes out the file linearly, so this is the only way +if [ "${found_other_os}" = "1" ]; then + cat << EOF +# Other OS found, undo autohiding of menu unless menu_auto_hide=2 +if [ "\${orig_timeout_style}" -a "\${menu_auto_hide}" != "2" ]; then + set timeout_style=\${orig_timeout_style} + set timeout=\${orig_timeout} +fi +EOF +fi diff --git a/util/grub.d/30_uefi-firmware.in b/util/grub.d/30_uefi-firmware.in old mode 100644 new mode 100755 diff --git a/util/setup.c b/util/setup.c index 87a889ff7..7aab468b0 100644 --- a/util/setup.c +++ b/util/setup.c @@ -406,7 +406,7 @@ SETUP (const char *dir, int is_ldm; grub_err_t err; grub_disk_addr_t *sectors; - int i; + unsigned int i; grub_fs_t fs; unsigned int nsec, maxsec; diff --git a/util/systemd/10-grub-logind-service.conf.in b/util/systemd/10-grub-logind-service.conf.in new file mode 100644 index 000000000..f2d4ac007 --- /dev/null +++ b/util/systemd/10-grub-logind-service.conf.in @@ -0,0 +1,2 @@ +[Service] +Environment=SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU=true diff --git a/util/systemd/grub-systemd-integration.service.in b/util/systemd/grub-systemd-integration.service.in new file mode 100644 index 000000000..c81fb594c --- /dev/null +++ b/util/systemd/grub-systemd-integration.service.in @@ -0,0 +1,8 @@ +[Unit] +Description=Grub2 systemctl reboot --boot-loader-menu=... support +Before=umount.target systemd-reboot.service +DefaultDependencies=no +ConditionPathExists=/run/systemd/reboot-to-boot-loader-menu + +[Service] +ExecStart=@libexecdir@/@grubdirname@/systemd-integration.sh diff --git a/util/systemd/systemd-integration.sh.in b/util/systemd/systemd-integration.sh.in new file mode 100755 index 000000000..a4c071c5b --- /dev/null +++ b/util/systemd/systemd-integration.sh.in @@ -0,0 +1,11 @@ +#!/bin/sh + +TIMEOUT_USEC=$(cat /run/systemd/reboot-to-boot-loader-menu) +TIMEOUT=$(((TIMEOUT_USEC + 500000) / 1000000)) + +@grub_editenv@ - set menu_show_once_timeout=$TIMEOUT + +# Downstream RH / Fedora patch for compatibility with old, not (yet) +# regenerated grub.cfg files which miss the menu_show_once_timeout check +# this older grubenv variable leads to a fixed timeout of 60 seconds +@grub_editenv@ - set menu_show_once=1