grub2/0332-emu-Add-switch-root-to-grub-emu.patch
Robbie Harwood dc5c4e3f52 Add switch-root support to grub-emu
Signed-off-by: Robbie Harwood <rharwood@redhat.com>
2023-04-12 15:23:39 +00:00

330 lines
9.3 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Nicolas Frayer <nfrayer@redhat.com>
Date: Fri, 31 Mar 2023 20:47:58 +0200
Subject: [PATCH] emu: Add switch-root to grub-emu
If the kernel running grub emu is the same as the one we want to
boot, it makes sense that we just switch-root instead of kexec
the same kernel again by doing grub2-emu --switch-root
Signed-off-by: Nicolas Frayer <nfrayer@redhat.com>
---
grub-core/kern/emu/main.c | 5 +-
grub-core/kern/emu/misc.c | 13 +++
grub-core/loader/emu/linux.c | 209 +++++++++++++++++++++++++++++++++++++++++--
include/grub/emu/exec.h | 2 +-
include/grub/emu/misc.h | 2 +
5 files changed, 223 insertions(+), 8 deletions(-)
diff --git a/grub-core/kern/emu/main.c b/grub-core/kern/emu/main.c
index 68e2b283bb..ccb2863f5b 100644
--- a/grub-core/kern/emu/main.c
+++ b/grub-core/kern/emu/main.c
@@ -108,6 +108,7 @@ static struct argp_option options[] = {
{"verbose", 'v', 0, 0, N_("print verbose messages."), 0},
{"hold", 'H', N_("SECS"), OPTION_ARG_OPTIONAL, N_("wait until a debugger will attach"), 0},
{"kexec", 'X', 0, 0, N_("use kexec to boot Linux kernels via systemctl (pass twice to enable dangerous fallback to non-systemctl)."), 0},
+ {"switch-root", 'W', 0, 0, N_("use switch-root to only switch root filesystem without restarting the kernel."), 0},
{ 0, 0, 0, 0, 0, 0 }
};
@@ -168,7 +169,9 @@ argp_parser (int key, char *arg, struct argp_state *state)
case 'X':
grub_util_set_kexecute ();
break;
-
+ case 'W':
+ grub_util_set_switch_root ();
+ break;
case ARGP_KEY_ARG:
{
/* Too many arguments. */
diff --git a/grub-core/kern/emu/misc.c b/grub-core/kern/emu/misc.c
index 02d27c3440..4b5123ef96 100644
--- a/grub-core/kern/emu/misc.c
+++ b/grub-core/kern/emu/misc.c
@@ -40,6 +40,7 @@
int verbosity;
int kexecute;
+int switchroot = 0;
void
grub_util_warn (const char *fmt, ...)
@@ -231,3 +232,15 @@ grub_util_get_kexecute (void)
{
return kexecute;
}
+
+void
+grub_util_set_switch_root (void)
+{
+ switchroot = 1;
+}
+
+int
+grub_util_get_switch_root (void)
+{
+ return switchroot;
+}
diff --git a/grub-core/loader/emu/linux.c b/grub-core/loader/emu/linux.c
index 7de3f7f861..6feb0412c5 100644
--- a/grub-core/loader/emu/linux.c
+++ b/grub-core/loader/emu/linux.c
@@ -15,7 +15,6 @@
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
-
#include <grub/loader.h>
#include <grub/dl.h>
#include <grub/command.h>
@@ -33,6 +32,196 @@ static char *kernel_path;
static char *initrd_path;
static char *boot_cmdline;
+static grub_err_t
+grub_switch_root (void)
+{
+ char *tmp = NULL;
+ char *options_cmd = NULL;
+ char *options = NULL;
+ char *subvol = NULL;
+ char *root_uuid = NULL;
+ char *kernel_release = NULL;
+ grub_err_t rc = GRUB_ERR_NONE;
+ const char *subvol_param = "subvol=";
+ const char *kernel_release_prefix = "/boot/vmlinuz-";
+ const char *root_prefix = "root=";
+ const char *systemctl[] = {"systemctl", "--force", "switch-root", "/sysroot", NULL};
+ const char *mountrootfs[] = {"mount", root_uuid, "/sysroot", options_cmd, options, NULL};
+ const char *unamer[] = {"uname", "-r", NULL};
+ char *uname_buf = NULL;
+ int i = 0;
+
+ /* Extract the kernel release tag from kernel_path */
+ if (!kernel_path)
+ {
+ rc = GRUB_ERR_BAD_ARGUMENT;
+ grub_dprintf ("linux", "switch_root: No kernel_path found\n");
+ goto out;
+ }
+
+ if ((kernel_release = grub_xasprintf ("%s", (kernel_path + grub_strlen (kernel_release_prefix)))) == NULL)
+ {
+ grub_dprintf ("linux", "switch_root: Failed to allocate memory\n");
+ rc = GRUB_ERR_BAD_ARGUMENT;
+ goto out;
+ }
+
+
+ /* Check for kernel mismatch */
+ /* Retrieve the current kernel relase tag */
+ grub_util_exec_redirect (unamer, NULL, "/tmp/version");
+
+ grub_file_t f = grub_file_open ("/tmp/version", GRUB_FILE_TYPE_FS_SEARCH);
+
+ if (f == NULL)
+ {
+ grub_dprintf ("linux", "failed opening file.\n");
+ rc = GRUB_ERR_FILE_NOT_FOUND;
+ goto out;
+ }
+
+ if ((uname_buf = grub_malloc (f->size)) == NULL)
+ {
+ grub_dprintf ("linux", "switch_root: Failed to allocate memory\n");
+ rc = GRUB_ERR_OUT_OF_MEMORY;
+ goto out;
+ }
+
+ if (grub_file_read (f, uname_buf, f->size) < 0)
+ {
+ grub_dprintf ("linux", "switch_root: failed to read from file\n");
+ rc = GRUB_ERR_FILE_READ_ERROR;
+ goto out;
+ }
+
+ grub_file_close (f);
+
+ if (grub_strstr (uname_buf, kernel_release) == NULL)
+ {
+ grub_dprintf ("linux", "switch_root: kernel mismatch, not performing switch-root ...\n");
+ rc = GRUB_ERR_NO_KERNEL;
+ goto out;
+ }
+
+ /* Extract the root partition from boot_cmdline */
+ if (!boot_cmdline)
+ {
+ rc = GRUB_ERR_BAD_ARGUMENT;
+ goto out;
+ }
+
+ tmp = grub_strdup (boot_cmdline);
+
+ if (tmp == NULL)
+ {
+ rc = GRUB_ERR_OUT_OF_MEMORY;
+ goto out;
+ }
+
+ if ((root_uuid = grub_strstr (tmp, root_prefix)) == NULL)
+ {
+ rc = GRUB_ERR_BAD_ARGUMENT;
+ grub_dprintf ("linux", "switch_root: Can't find rootfs\n");
+ goto out;
+ }
+
+ root_uuid += grub_strlen (root_prefix);
+
+ while (root_uuid[i] != ' ' && root_uuid[i] != '\0')
+ i++;
+
+ root_uuid[i] = '\0';
+
+ /* Allocate a new buffer holding root_uuid */
+ root_uuid = grub_xasprintf ("%s", root_uuid);
+
+ if (root_uuid == NULL)
+ {
+ grub_dprintf ("linux", "switch_root: Failed to allocated memory\n");
+ rc = GRUB_ERR_OUT_OF_MEMORY;
+ goto out;
+ }
+
+ /* Check for subvol parameter */
+ grub_strcpy (tmp, boot_cmdline);
+
+ if ((subvol = grub_strstr(tmp, subvol_param)) != NULL)
+ {
+ i = 0;
+
+ while (subvol[i] != ' ' && subvol[i] != '\0')
+ i++;
+
+ subvol[i] = '\0';
+
+ /* Allocate a new buffer holding subvol */
+ subvol = grub_xasprintf("%s", subvol);
+
+ if (subvol == NULL)
+ {
+ grub_dprintf ("linux", "switch_root: Failed to allocated memory\n");
+ rc = GRUB_ERR_OUT_OF_MEMORY;
+ goto out;
+ }
+
+ options_cmd = grub_xasprintf("%s", "-o");
+ options = grub_xasprintf("%s", subvol);
+ }
+
+ if (options == NULL)
+ {
+ mountrootfs[3] = NULL;
+ }
+ else
+ {
+ mountrootfs[3] = options_cmd;
+ mountrootfs[4] = options;
+ }
+
+ mountrootfs[1] = root_uuid;
+
+ grub_dprintf ("linux", "Executing:\n");
+ grub_dprintf ("linux", "%s %s %s %s %s\n", mountrootfs[0], mountrootfs[1],
+ mountrootfs[2], mountrootfs[3], mountrootfs[4]);
+
+ /* Mount the rootfs */
+ rc = grub_util_exec (mountrootfs);
+
+ if (rc != GRUB_ERR_NONE)
+ {
+ grub_dprintf ("linux", "switch_root: Failed.\n");
+ rc = GRUB_ERR_INVALID_COMMAND;
+ goto out;
+ }
+
+ grub_dprintf ("linux", "Done.\n");
+
+ grub_dprintf ("linux", "%s %s %s %s\n", systemctl[0], systemctl[1],
+ systemctl[2], systemctl[3]);
+
+ /* Switch root */
+ rc = grub_util_exec (systemctl);
+
+ if (rc != GRUB_ERR_NONE)
+ {
+ grub_dprintf ("linux", "switch_root: Failed.\n");
+ rc = GRUB_ERR_INVALID_COMMAND;
+ goto out;
+ }
+
+ grub_dprintf ("linux", "Done.\n");
+
+out:
+ grub_free (tmp);
+ grub_free (options_cmd);
+ grub_free (options);
+ grub_free (subvol);
+ grub_free (root_uuid);
+ grub_free (uname_buf);
+ grub_free (kernel_release);
+ return rc;
+}
+
static grub_err_t
grub_linux_boot (void)
{
@@ -51,12 +240,20 @@ grub_linux_boot (void)
else
initrd_param = grub_xasprintf ("%s", "");
- grub_dprintf ("linux", "%serforming 'kexec -la %s %s %s'\n",
- (kexecute) ? "P" : "Not p",
- kernel_path, initrd_param, boot_cmdline);
+ if (grub_util_get_switch_root() == 1)
+ {
+ rc = grub_switch_root();
+ if (rc != GRUB_ERR_NONE)
+ grub_fatal (N_("Failed to execute switch_root\n"));
+ }
+ else if (kexecute)
+ {
+ grub_dprintf ("linux", "%serforming 'kexec -la %s %s %s'\n",
+ (kexecute) ? "P" : "Not p",
+ kernel_path, initrd_param, boot_cmdline);
- if (kexecute)
- rc = grub_util_exec (kexec);
+ rc = grub_util_exec (kexec);
+ }
grub_free (initrd_param);
diff --git a/include/grub/emu/exec.h b/include/grub/emu/exec.h
index 1b61b4a2e5..e82f13215e 100644
--- a/include/grub/emu/exec.h
+++ b/include/grub/emu/exec.h
@@ -36,7 +36,7 @@ grub_util_exec_redirect_all (const char *const *argv, const char *stdin_file,
int
EXPORT_FUNC(grub_util_exec) (const char *const *argv);
int
-grub_util_exec_redirect (const char *const *argv, const char *stdin_file,
+EXPORT_FUNC(grub_util_exec_redirect) (const char *const *argv, const char *stdin_file,
const char *stdout_file);
int
grub_util_exec_redirect_null (const char *const *argv);
diff --git a/include/grub/emu/misc.h b/include/grub/emu/misc.h
index 01056954b9..f3a712a8b2 100644
--- a/include/grub/emu/misc.h
+++ b/include/grub/emu/misc.h
@@ -59,6 +59,8 @@ void EXPORT_FUNC(grub_util_error) (const char *fmt, ...) __attribute__ ((format
void EXPORT_FUNC(grub_util_set_kexecute) (void);
int EXPORT_FUNC(grub_util_get_kexecute) (void) WARN_UNUSED_RESULT;
+void EXPORT_FUNC(grub_util_set_switch_root) (void);
+int EXPORT_FUNC(grub_util_get_switch_root) (void);
grub_uint64_t EXPORT_FUNC (grub_util_get_cpu_time_ms) (void);