Blame SOURCES/0311-loader-Add-support-for-grub-emu-to-kexec-Linux-menu-.patch

5593c8
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
fd0330
From: Raymund Will <rw@suse.com>
fd0330
Date: Mon, 24 Oct 2022 14:33:50 -0400
fd0330
Subject: [PATCH] loader: Add support for grub-emu to kexec Linux menu entries
5593c8
fd0330
The GRUB emulator is used as a debugging utility but it could also be
fd0330
used as a user-space bootloader if there is support to boot an operating
fd0330
system.
fd0330
fd0330
The Linux kernel is already able to (re)boot another kernel via the
fd0330
kexec boot mechanism. So the grub-emu tool could rely on this feature
fd0330
and have linux and initrd commands that are used to pass a kernel,
fd0330
initramfs image and command line parameters to kexec for booting
fd0330
a selected menu entry.
fd0330
fd0330
By default the systemctl kexec option is used so systemd can shutdown
fd0330
all of the running services before doing a reboot using kexec. But if
fd0330
this is not present, it can fall back to executing the kexec user-space
fd0330
tool directly. The ability to force a kexec-reboot when systemctl kexec
fd0330
fails must only be used in controlled environments to avoid possible
fd0330
filesystem corruption and data loss.
fd0330
fd0330
Signed-off-by: Raymund Will <rw@suse.com>
fd0330
Signed-off-by: John Jolly <jjolly@suse.com>
fd0330
Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
fd0330
Signed-off-by: Robbie Harwood <rharwood@redhat.com>
fd0330
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
fd0330
(cherry picked from commit e364307f6acc2f631b4c1fefda0791b9ce1f205f)
fd0330
[rharwood: conflicts around makefile and grub_exit return code]
5593c8
---
fd0330
 grub-core/Makefile.core.def  |   3 -
5593c8
 grub-core/kern/emu/main.c    |   4 +
5593c8
 grub-core/kern/emu/misc.c    |  18 ++++-
fd0330
 grub-core/loader/emu/linux.c | 178 +++++++++++++++++++++++++++++++++++++++++++
5593c8
 include/grub/emu/exec.h      |   4 +-
5593c8
 include/grub/emu/hostfile.h  |   3 +-
5593c8
 include/grub/emu/misc.h      |   3 +
fd0330
 docs/grub.texi               |  30 ++++++--
5593c8
 grub-core/Makefile.am        |   1 +
fd0330
 9 files changed, 230 insertions(+), 14 deletions(-)
5593c8
 create mode 100644 grub-core/loader/emu/linux.c
5593c8
5593c8
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
fd0330
index 741a033978..f21da23213 100644
5593c8
--- a/grub-core/Makefile.core.def
5593c8
+++ b/grub-core/Makefile.core.def
fd0330
@@ -1864,11 +1864,8 @@ module = {
fd0330
   riscv32 = loader/riscv/linux.c;
fd0330
   riscv64 = loader/riscv/linux.c;
fd0330
   emu = loader/emu/linux.c;
fd0330
-
5593c8
   common = loader/linux.c;
5593c8
   common = lib/cmdline.c;
5593c8
-  enable = noemu;
fd0330
-
5593c8
   efi = loader/efi/linux.c;
5593c8
 };
fd0330
 
5593c8
diff --git a/grub-core/kern/emu/main.c b/grub-core/kern/emu/main.c
fd0330
index 12277c34d2..68e2b283bb 100644
5593c8
--- a/grub-core/kern/emu/main.c
5593c8
+++ b/grub-core/kern/emu/main.c
5593c8
@@ -107,6 +107,7 @@ static struct argp_option options[] = {
5593c8
    N_("use GRUB files in the directory DIR [default=%s]"), 0},
5593c8
   {"verbose",     'v', 0,      0, N_("print verbose messages."), 0},
5593c8
   {"hold",     'H', N_("SECS"),      OPTION_ARG_OPTIONAL, N_("wait until a debugger will attach"), 0},
fd0330
+  {"kexec",       'X', 0,      0, N_("use kexec to boot Linux kernels via systemctl (pass twice to enable dangerous fallback to non-systemctl)."), 0},
5593c8
   { 0, 0, 0, 0, 0, 0 }
5593c8
 };
5593c8
 
5593c8
@@ -164,6 +165,9 @@ argp_parser (int key, char *arg, struct argp_state *state)
5593c8
     case 'v':
5593c8
       verbosity++;
5593c8
       break;
5593c8
+    case 'X':
fd0330
+      grub_util_set_kexecute ();
5593c8
+      break;
5593c8
 
5593c8
     case ARGP_KEY_ARG:
5593c8
       {
5593c8
diff --git a/grub-core/kern/emu/misc.c b/grub-core/kern/emu/misc.c
fd0330
index d278c2921f..02d27c3440 100644
5593c8
--- a/grub-core/kern/emu/misc.c
5593c8
+++ b/grub-core/kern/emu/misc.c
5593c8
@@ -39,6 +39,7 @@
5593c8
 #include <grub/emu/misc.h>
5593c8
 
5593c8
 int verbosity;
5593c8
+int kexecute;
5593c8
 
5593c8
 void
5593c8
 grub_util_warn (const char *fmt, ...)
5593c8
@@ -82,7 +83,7 @@ grub_util_error (const char *fmt, ...)
5593c8
   vfprintf (stderr, fmt, ap);
5593c8
   va_end (ap);
5593c8
   fprintf (stderr, ".\n");
5593c8
-  exit (1);
5593c8
+  grub_exit (1);
5593c8
 }
5593c8
 
5593c8
 void *
5593c8
@@ -154,6 +155,9 @@ void
5593c8
 __attribute__ ((noreturn))
5593c8
 grub_exit (int rc)
5593c8
 {
5593c8
+#if defined (GRUB_KERNEL)
fd0330
+  grub_reboot ();
5593c8
+#endif
5593c8
   exit (rc < 0 ? 1 : rc);
5593c8
 }
5593c8
 #endif
5593c8
@@ -215,3 +219,15 @@ grub_util_load_image (const char *path, char *buf)
5593c8
 
5593c8
   fclose (fp);
5593c8
 }
5593c8
+
5593c8
+void
fd0330
+grub_util_set_kexecute (void)
5593c8
+{
5593c8
+  kexecute++;
5593c8
+}
5593c8
+
5593c8
+int
fd0330
+grub_util_get_kexecute (void)
5593c8
+{
5593c8
+  return kexecute;
5593c8
+}
5593c8
diff --git a/grub-core/loader/emu/linux.c b/grub-core/loader/emu/linux.c
5593c8
new file mode 100644
fd0330
index 0000000000..0cf378a376
5593c8
--- /dev/null
5593c8
+++ b/grub-core/loader/emu/linux.c
fd0330
@@ -0,0 +1,178 @@
5593c8
+/*
5593c8
+ *  GRUB  --  GRand Unified Bootloader
fd0330
+ *  Copyright (C) 2022  Free Software Foundation, Inc.
5593c8
+ *
5593c8
+ *  GRUB is free software: you can redistribute it and/or modify
5593c8
+ *  it under the terms of the GNU General Public License as published by
5593c8
+ *  the Free Software Foundation, either version 3 of the License, or
5593c8
+ *  (at your option) any later version.
5593c8
+ *
5593c8
+ *  GRUB is distributed in the hope that it will be useful,
5593c8
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
5593c8
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
5593c8
+ *  GNU General Public License for more details.
5593c8
+ *
5593c8
+ *  You should have received a copy of the GNU General Public License
5593c8
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
5593c8
+ */
5593c8
+
5593c8
+#include <grub/loader.h>
5593c8
+#include <grub/dl.h>
5593c8
+#include <grub/command.h>
5593c8
+#include <grub/time.h>
5593c8
+
5593c8
+#include <grub/emu/exec.h>
5593c8
+#include <grub/emu/hostfile.h>
5593c8
+#include <grub/emu/misc.h>
5593c8
+
5593c8
+GRUB_MOD_LICENSE ("GPLv3+");
5593c8
+
5593c8
+static grub_dl_t my_mod;
5593c8
+
5593c8
+static char *kernel_path;
5593c8
+static char *initrd_path;
5593c8
+static char *boot_cmdline;
5593c8
+
5593c8
+static grub_err_t
5593c8
+grub_linux_boot (void)
5593c8
+{
5593c8
+  grub_err_t rc = GRUB_ERR_NONE;
5593c8
+  char *initrd_param;
fd0330
+  const char *kexec[] = {"kexec", "-la", kernel_path, boot_cmdline, NULL, NULL};
fd0330
+  const char *systemctl[] = {"systemctl", "kexec", NULL};
fd0330
+  int kexecute = grub_util_get_kexecute ();
fd0330
+
fd0330
+  if (initrd_path)
fd0330
+    {
fd0330
+      initrd_param = grub_xasprintf ("--initrd=%s", initrd_path);
fd0330
+      kexec[3] = initrd_param;
fd0330
+      kexec[4] = boot_cmdline;
fd0330
+    }
fd0330
+  else
fd0330
+    initrd_param = grub_xasprintf ("%s", "");
fd0330
+
fd0330
+  grub_dprintf ("linux", "%serforming 'kexec -la %s %s %s'\n",
fd0330
+                (kexecute) ? "P" : "Not p",
fd0330
+                kernel_path, initrd_param, boot_cmdline);
5593c8
+
5593c8
+  if (kexecute)
fd0330
+    rc = grub_util_exec (kexec);
5593c8
+
fd0330
+  grub_free (initrd_param);
fd0330
+
fd0330
+  if (rc != GRUB_ERR_NONE)
fd0330
+    {
fd0330
+      grub_error (rc, N_("error trying to perform kexec load operation"));
fd0330
+      grub_sleep (3);
fd0330
+      return rc;
fd0330
+    }
5593c8
+
5593c8
+  if (kexecute < 1)
fd0330
+    grub_fatal (N_("use '"PACKAGE"-emu --kexec' to force a system restart"));
5593c8
+
fd0330
+  grub_dprintf ("linux", "Performing 'systemctl kexec' (%s) ",
5593c8
+		(kexecute==1) ? "do-or-die" : "just-in-case");
5593c8
+  rc = grub_util_exec (systemctl);
5593c8
+
5593c8
+  if (kexecute == 1)
fd0330
+    grub_fatal (N_("error trying to perform 'systemctl kexec': %d"), rc);
fd0330
+
fd0330
+  /*
fd0330
+   * WARNING: forcible reset should only be used in read-only environments.
fd0330
+   * grub-emu cannot check for these - users beware.
fd0330
+   */
fd0330
+  grub_dprintf ("linux", "Performing 'kexec -ex'");
fd0330
+  kexec[1] = "-ex";
5593c8
+  kexec[2] = NULL;
fd0330
+  rc = grub_util_exec (kexec);
fd0330
+  if (rc != GRUB_ERR_NONE)
fd0330
+    grub_fatal (N_("error trying to directly perform 'kexec -ex': %d"), rc);
5593c8
+
5593c8
+  return rc;
5593c8
+}
5593c8
+
5593c8
+static grub_err_t
5593c8
+grub_linux_unload (void)
5593c8
+{
fd0330
+  /* Unloading: we're no longer in use. */
5593c8
+  grub_dl_unref (my_mod);
fd0330
+  grub_free (boot_cmdline);
5593c8
+  boot_cmdline = NULL;
5593c8
+  return GRUB_ERR_NONE;
5593c8
+}
5593c8
+
5593c8
+static grub_err_t
fd0330
+grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), int argc,
fd0330
+		char *argv[])
5593c8
+{
5593c8
+  int i;
5593c8
+  char *tempstr;
5593c8
+
fd0330
+  /* Mark ourselves as in-use. */
5593c8
+  grub_dl_ref (my_mod);
5593c8
+
5593c8
+  if (argc == 0)
5593c8
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
5593c8
+
fd0330
+  if (!grub_util_is_regular (argv[0]))
fd0330
+    return grub_error (GRUB_ERR_FILE_NOT_FOUND,
fd0330
+		       N_("cannot find kernel file %s"), argv[0]);
5593c8
+
fd0330
+  grub_free (kernel_path);
fd0330
+  kernel_path = grub_xasprintf ("%s", argv[0]);
5593c8
+
fd0330
+  grub_free (boot_cmdline);
fd0330
+  boot_cmdline = NULL;
5593c8
+
fd0330
+  if (argc > 1)
fd0330
+    {
fd0330
+      boot_cmdline = grub_xasprintf ("--command-line=%s", argv[1]);
fd0330
+      for (i = 2; i < argc; i++)
fd0330
+        {
fd0330
+          tempstr = grub_xasprintf ("%s %s", boot_cmdline, argv[i]);
fd0330
+          grub_free (boot_cmdline);
fd0330
+          boot_cmdline = tempstr;
fd0330
+        }
5593c8
+    }
5593c8
+
5593c8
+  grub_loader_set (grub_linux_boot, grub_linux_unload, 0);
5593c8
+
5593c8
+  return GRUB_ERR_NONE;
5593c8
+}
5593c8
+
5593c8
+static grub_err_t
fd0330
+grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), int argc,
fd0330
+		 char *argv[])
5593c8
+{
5593c8
+  if (argc == 0)
5593c8
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
5593c8
+
fd0330
+  if (!grub_util_is_regular (argv[0]))
fd0330
+    return grub_error (GRUB_ERR_FILE_NOT_FOUND,
fd0330
+		       N_("Cannot find initrd file %s"), argv[0]);
5593c8
+
fd0330
+  grub_free (initrd_path);
fd0330
+  initrd_path = grub_xasprintf ("%s", argv[0]);
5593c8
+
fd0330
+  /* We are done - mark ourselves as on longer in use. */
5593c8
+  grub_dl_unref (my_mod);
5593c8
+
5593c8
+  return GRUB_ERR_NONE;
5593c8
+}
5593c8
+
5593c8
+static grub_command_t cmd_linux, cmd_initrd;
5593c8
+
fd0330
+GRUB_MOD_INIT (linux)
5593c8
+{
fd0330
+  cmd_linux = grub_register_command ("linux", grub_cmd_linux, 0,
fd0330
+				     N_("Load Linux."));
fd0330
+  cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd, 0,
fd0330
+				      N_("Load initrd."));
5593c8
+  my_mod = mod;
5593c8
+}
5593c8
+
fd0330
+GRUB_MOD_FINI (linux)
5593c8
+{
5593c8
+  grub_unregister_command (cmd_linux);
5593c8
+  grub_unregister_command (cmd_initrd);
5593c8
+}
5593c8
diff --git a/include/grub/emu/exec.h b/include/grub/emu/exec.h
fd0330
index d1073ef86a..1b61b4a2e5 100644
5593c8
--- a/include/grub/emu/exec.h
5593c8
+++ b/include/grub/emu/exec.h
5593c8
@@ -23,6 +23,8 @@
5593c8
 #include <stdarg.h>
5593c8
 
5593c8
 #include <sys/types.h>
5593c8
+#include <grub/symbol.h>
5593c8
+
5593c8
 pid_t
5593c8
 grub_util_exec_pipe (const char *const *argv, int *fd);
5593c8
 pid_t
5593c8
@@ -32,7 +34,7 @@ int
5593c8
 grub_util_exec_redirect_all (const char *const *argv, const char *stdin_file,
5593c8
 			     const char *stdout_file, const char *stderr_file);
5593c8
 int
5593c8
-grub_util_exec (const char *const *argv);
5593c8
+EXPORT_FUNC(grub_util_exec) (const char *const *argv);
5593c8
 int
5593c8
 grub_util_exec_redirect (const char *const *argv, const char *stdin_file,
5593c8
 			 const char *stdout_file);
5593c8
diff --git a/include/grub/emu/hostfile.h b/include/grub/emu/hostfile.h
fd0330
index cfb1e2b566..a61568e36e 100644
5593c8
--- a/include/grub/emu/hostfile.h
5593c8
+++ b/include/grub/emu/hostfile.h
5593c8
@@ -22,6 +22,7 @@
5593c8
 #include <grub/disk.h>
5593c8
 #include <grub/partition.h>
5593c8
 #include <sys/types.h>
5593c8
+#include <grub/symbol.h>
5593c8
 #include <grub/osdep/hostfile.h>
5593c8
 
5593c8
 int
5593c8
@@ -29,7 +30,7 @@ grub_util_is_directory (const char *path);
5593c8
 int
5593c8
 grub_util_is_special_file (const char *path);
5593c8
 int
5593c8
-grub_util_is_regular (const char *path);
5593c8
+EXPORT_FUNC(grub_util_is_regular) (const char *path);
5593c8
 
5593c8
 char *
5593c8
 grub_util_path_concat (size_t n, ...);
5593c8
diff --git a/include/grub/emu/misc.h b/include/grub/emu/misc.h
fd0330
index ff9c48a649..01056954b9 100644
5593c8
--- a/include/grub/emu/misc.h
5593c8
+++ b/include/grub/emu/misc.h
5593c8
@@ -57,6 +57,9 @@ void EXPORT_FUNC(grub_util_warn) (const char *fmt, ...) __attribute__ ((format (
5593c8
 void EXPORT_FUNC(grub_util_info) (const char *fmt, ...) __attribute__ ((format (GNU_PRINTF, 1, 2)));
5593c8
 void EXPORT_FUNC(grub_util_error) (const char *fmt, ...) __attribute__ ((format (GNU_PRINTF, 1, 2), noreturn));
5593c8
 
5593c8
+void EXPORT_FUNC(grub_util_set_kexecute) (void);
5593c8
+int EXPORT_FUNC(grub_util_get_kexecute) (void) WARN_UNUSED_RESULT;
5593c8
+
5593c8
 grub_uint64_t EXPORT_FUNC (grub_util_get_cpu_time_ms) (void);
5593c8
 
5593c8
 #ifdef HAVE_DEVICE_MAPPER
fd0330
diff --git a/docs/grub.texi b/docs/grub.texi
fd0330
index a4da9c2a1b..1750b72ee9 100644
fd0330
--- a/docs/grub.texi
fd0330
+++ b/docs/grub.texi
fd0330
@@ -923,17 +923,17 @@ magic.
fd0330
 @node General boot methods
fd0330
 @section How to boot operating systems
fd0330
 
fd0330
-GRUB has two distinct boot methods. One of the two is to load an
fd0330
-operating system directly, and the other is to chain-load another boot
fd0330
-loader which then will load an operating system actually. Generally
fd0330
-speaking, the former is more desirable, because you don't need to
fd0330
-install or maintain other boot loaders and GRUB is flexible enough to
fd0330
-load an operating system from an arbitrary disk/partition. However,
fd0330
-the latter is sometimes required, since GRUB doesn't support all the
fd0330
-existing operating systems natively.
fd0330
+GRUB has three distinct boot methods: loading an operating system
fd0330
+directly, using kexec from userspace, and chainloading another
fd0330
+bootloader. Generally speaking, the first two are more desirable
fd0330
+because you don't need to install or maintain other boot loaders and
fd0330
+GRUB is flexible enough to load an operating system from an arbitrary
fd0330
+disk/partition. However, chainloading is sometimes required, as GRUB
fd0330
+doesn't support all existing operating systems natively.
fd0330
 
fd0330
 @menu
fd0330
 * Loading an operating system directly::
fd0330
+* Kexec::
fd0330
 * Chain-loading::
fd0330
 @end menu
fd0330
 
fd0330
@@ -959,6 +959,20 @@ use more complicated instructions. @xref{DOS/Windows}, for more
fd0330
 information.
fd0330
 
fd0330
 
fd0330
+@node Kexec
fd0330
+@subsection Kexec with grub2-emu
fd0330
+
fd0330
+GRUB can be run in userspace by invoking the grub2-emu tool. It will
fd0330
+read all configuration scripts as if booting directly (see @xref{Loading
fd0330
+an operating system directly}). With the @code{--kexec} flag, and
fd0330
+kexec(8) support from the operating system, the @command{linux} command
fd0330
+will directly boot the target image. For systems that lack working
fd0330
+systemctl(1) support for kexec, passing the @code{--kexec} flag twice
fd0330
+will fallback to invoking kexec(8) directly; note however that this
fd0330
+fallback may be unsafe outside read-only environments, as it does not
fd0330
+invoke shutdown machinery.
fd0330
+
fd0330
+
fd0330
 @node Chain-loading
fd0330
 @subsection Chain-loading an OS
fd0330
 
5593c8
diff --git a/grub-core/Makefile.am b/grub-core/Makefile.am
fd0330
index c2e8a82bce..dd49939aaa 100644
5593c8
--- a/grub-core/Makefile.am
5593c8
+++ b/grub-core/Makefile.am
fd0330
@@ -309,6 +309,7 @@ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/emu/net.h
5593c8
 KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/emu/hostdisk.h
5593c8
 KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/emu/hostfile.h
5593c8
 KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/extcmd.h
5593c8
+KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/emu/exec.h
5593c8
 if COND_GRUB_EMU_SDL
5593c8
 KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/sdl.h
5593c8
 endif