--- grub-core/Makefile.am | 1 grub-core/Makefile.core.def | 2 grub-core/kern/emu/main.c | 4 grub-core/kern/emu/misc.c | 18 ++++ grub-core/loader/emu/linux.c | 173 +++++++++++++++++++++++++++++++++++++++++++ include/grub/emu/exec.h | 4 include/grub/emu/hostfile.h | 3 include/grub/emu/misc.h | 3 8 files changed, 204 insertions(+), 4 deletions(-) --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -1674,9 +1674,9 @@ module = { arm = loader/arm/linux.c; arm64 = loader/arm64/linux.c; fdt = lib/fdt.c; + emu = loader/emu/linux.c; common = loader/linux.c; common = lib/cmdline.c; - enable = noemu; }; module = { --- /dev/null +++ b/grub-core/loader/emu/linux.c @@ -0,0 +1,173 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007,2008,2009,2010 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include + +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_dl_t my_mod; + +static char *kernel_path; +static char *initrd_path; +static char *boot_cmdline; + +static grub_err_t +grub_linux_boot (void) +{ + grub_err_t rc = GRUB_ERR_NONE; + char *initrd_param; + const char *kexec[] = { "kexec", "-l", kernel_path, boot_cmdline, NULL, NULL }; + const char *systemctl[] = { "systemctl", "kexec", NULL }; + int kexecute = grub_util_get_kexecute(); + + if (initrd_path) { + initrd_param = grub_xasprintf("--initrd=%s", initrd_path); + kexec[3] = initrd_param; + kexec[4] = boot_cmdline; + } else { + initrd_param = grub_xasprintf("%s", ""); + //return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("initrd required!")); + } + + grub_printf("%serforming 'kexec -l %s %s %s'\n", + (kexecute) ? "P" : "Not p", + kernel_path, initrd_param, boot_cmdline); + + if (kexecute) + rc = grub_util_exec(kexec); + + grub_free(initrd_param); + + if (rc != GRUB_ERR_NONE) { + grub_error (rc, N_("Error trying to perform kexec load operation.")); + grub_sleep (3); + return rc; + } + if (kexecute < 1) + grub_fatal (N_("Use '"PACKAGE"-emu --kexec' to force a system restart.")); + + grub_printf("Performing 'systemctl kexec' (%s) ", + (kexecute==1) ? "do-or-die" : "just-in-case"); + rc = grub_util_exec (systemctl); + + if (kexecute == 1) + grub_fatal (N_("Error trying to perform 'systemctl kexec'")); + + /* need to check read-only root before resetting hard!? */ + grub_printf("Performing 'kexec -e'"); + kexec[1] = "-e"; + kexec[2] = NULL; + rc = grub_util_exec(kexec); + if ( rc != GRUB_ERR_NONE ) + grub_fatal (N_("Error trying to directly perform 'kexec -e'.")); + + return rc; +} + +static grub_err_t +grub_linux_unload (void) +{ + grub_dl_unref (my_mod); + if ( boot_cmdline != NULL ) + grub_free (boot_cmdline); + boot_cmdline = NULL; + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), int argc, char *argv[]) +{ + int i; + char *tempstr; + + grub_dl_ref (my_mod); + + if (argc == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + + if ( !grub_util_is_regular(argv[0]) ) + return grub_error(GRUB_ERR_FILE_NOT_FOUND, N_("Cannot find kernel file %s"), argv[0]); + + if ( kernel_path != NULL ) + grub_free(kernel_path); + + kernel_path = grub_xasprintf("%s", argv[0]); + + if ( boot_cmdline != NULL ) { + grub_free(boot_cmdline); + boot_cmdline = NULL; + } + + if ( argc > 1 ) + { + boot_cmdline = grub_xasprintf("--command-line=%s", argv[1]); + for ( i = 2; i < argc; i++ ) { + tempstr = grub_xasprintf("%s %s", boot_cmdline, argv[i]); + grub_free(boot_cmdline); + boot_cmdline = tempstr; + } + } + + grub_loader_set (grub_linux_boot, grub_linux_unload, 0); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), int argc, char *argv[]) +{ + if (argc == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + + if ( !grub_util_is_regular(argv[0]) ) + return grub_error(GRUB_ERR_FILE_NOT_FOUND, N_("Cannot find initrd file %s"), argv[0]); + + if ( initrd_path != NULL ) + grub_free(initrd_path); + + initrd_path = grub_xasprintf("%s", argv[0]); + + grub_dl_unref (my_mod); + + return GRUB_ERR_NONE; +} + +static grub_command_t cmd_linux, cmd_initrd; + +GRUB_MOD_INIT(linux) +{ + cmd_linux = grub_register_command ("linux", grub_cmd_linux, 0, N_("Load Linux.")); + cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd, 0, N_("Load initrd.")); + my_mod = mod; + kernel_path = NULL; + initrd_path = NULL; + boot_cmdline = NULL; +} + +GRUB_MOD_FINI(linux) +{ + grub_unregister_command (cmd_linux); + grub_unregister_command (cmd_initrd); +} --- a/include/grub/emu/hostfile.h +++ b/include/grub/emu/hostfile.h @@ -22,6 +22,7 @@ #include #include #include +#include #include int @@ -29,7 +30,7 @@ grub_util_is_directory (const char *path int grub_util_is_special_file (const char *path); int -grub_util_is_regular (const char *path); +EXPORT_FUNC(grub_util_is_regular) (const char *path); char * grub_util_path_concat (size_t n, ...); --- a/include/grub/emu/exec.h +++ b/include/grub/emu/exec.h @@ -23,6 +23,8 @@ #include #include +#include + pid_t grub_util_exec_pipe (const char *const *argv, int *fd); pid_t @@ -32,7 +34,7 @@ int grub_util_exec_redirect_all (const char *const *argv, const char *stdin_file, const char *stdout_file, const char *stderr_file); int -grub_util_exec (const char *const *argv); +EXPORT_FUNC(grub_util_exec) (const char *const *argv); int grub_util_exec_redirect (const char *const *argv, const char *stdin_file, const char *stdout_file); --- a/grub-core/Makefile.am +++ b/grub-core/Makefile.am @@ -243,6 +243,7 @@ KERNEL_HEADER_FILES += $(top_srcdir)/inc KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/emu/hostdisk.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/emu/hostfile.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/extcmd.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/emu/exec.h if COND_GRUB_EMU_SDL KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/sdl.h endif --- a/grub-core/kern/emu/main.c +++ b/grub-core/kern/emu/main.c @@ -95,6 +95,7 @@ static struct argp_option options[] = { N_("use GRUB files in the directory DIR [default=%s]"), 0}, {"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_("try the untryable."), 0}, { 0, 0, 0, 0, 0, 0 } }; @@ -148,6 +149,9 @@ argp_parser (int key, char *arg, struct case 'v': verbosity++; break; + case 'X': + grub_util_set_kexecute(); + break; case ARGP_KEY_ARG: { --- a/grub-core/kern/emu/misc.c +++ b/grub-core/kern/emu/misc.c @@ -38,6 +38,7 @@ #include int verbosity; +int kexecute; void grub_util_warn (const char *fmt, ...) @@ -81,7 +82,7 @@ grub_util_error (const char *fmt, ...) vfprintf (stderr, fmt, ap); va_end (ap); fprintf (stderr, ".\n"); - exit (1); + grub_exit (); } void * @@ -138,6 +139,9 @@ xasprintf (const char *fmt, ...) void grub_exit (void) { +#if defined (GRUB_KERNEL) + grub_reboot(); +#endif exit (1); } @@ -150,3 +154,15 @@ grub_get_time_ms (void) return (tv.tv_sec * 1000 + tv.tv_usec / 1000); } + +void +grub_util_set_kexecute(void) +{ + kexecute++; +} + +int +grub_util_get_kexecute(void) +{ + return kexecute; +} --- a/include/grub/emu/misc.h +++ b/include/grub/emu/misc.h @@ -59,6 +59,9 @@ void EXPORT_FUNC(grub_util_warn) (const void EXPORT_FUNC(grub_util_info) (const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); void EXPORT_FUNC(grub_util_error) (const char *fmt, ...) __attribute__ ((format (printf, 1, 2), noreturn)); +void EXPORT_FUNC(grub_util_set_kexecute) (void); +int EXPORT_FUNC(grub_util_get_kexecute) (void) WARN_UNUSED_RESULT; + grub_uint64_t EXPORT_FUNC (grub_util_get_cpu_time_ms) (void); extern char * canonicalize_file_name (const char *path);