331 lines
9.3 KiB
Diff
331 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);
|
||
|
|