[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH 1/1] Add Xen command line parsing
From: |
Aaron Rainbolt |
Subject: |
[PATCH 1/1] Add Xen command line parsing |
Date: |
Wed, 23 Apr 2025 21:47:48 -0500 |
Enable GRUB to parse the Xen command line for parameters, and expose
certain of those parameters to the GRUB config file (or rescue shell)
as environment variables.
---
grub-core/Makefile.core.def | 2 +
grub-core/kern/i386/xen/pvh.c | 16 ++
grub-core/kern/main.c | 12 ++
grub-core/kern/xen/cmdline.c | 270 ++++++++++++++++++++++++++++++++++
include/grub/xen.h | 2 +
5 files changed, 302 insertions(+)
create mode 100644 grub-core/kern/xen/cmdline.c
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index f70e02e69..79e681a7b 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -248,6 +248,7 @@ kernel = {
xen = term/xen/console.c;
xen = disk/xen/xendisk.c;
xen = commands/boot.c;
+ xen = kern/xen/cmdline.c;
i386_xen_pvh = commands/boot.c;
i386_xen_pvh = disk/xen/xendisk.c;
@@ -255,6 +256,7 @@ kernel = {
i386_xen_pvh = kern/i386/xen/tsc.c;
i386_xen_pvh = kern/i386/xen/pvh.c;
i386_xen_pvh = kern/xen/init.c;
+ i386_xen_pvh = kern/xen/cmdline.c;
i386_xen_pvh = term/xen/console.c;
ia64_efi = kern/ia64/efi/startup.S;
diff --git a/grub-core/kern/i386/xen/pvh.c b/grub-core/kern/i386/xen/pvh.c
index 91fbca859..9fb048082 100644
--- a/grub-core/kern/i386/xen/pvh.c
+++ b/grub-core/kern/i386/xen/pvh.c
@@ -352,6 +352,22 @@ grub_xen_setup_pvh (void)
grub_xen_mm_init_regions ();
grub_rsdp_addr = pvh_start_info->rsdp_paddr;
+ int xen_cmdline_fits = 0;
+ char *xen_cmdline = (char *) pvh_start_info->cmdline_paddr;
+ for (int i = 0; i < 1024; i++)
+ {
+ if (xen_cmdline[i] == '\0')
+ {
+ xen_cmdline_fits = 1;
+ break;
+ }
+ }
+ if (xen_cmdline_fits == 1)
+ {
+ grub_strncpy ((char *) grub_xen_start_page_addr->cmd_line,
+ (char *) pvh_start_info->cmdline_paddr,
+ MAX_GUEST_CMDLINE);
+ }
}
grub_err_t
diff --git a/grub-core/kern/main.c b/grub-core/kern/main.c
index 143a232b8..f96b16f73 100644
--- a/grub-core/kern/main.c
+++ b/grub-core/kern/main.c
@@ -40,6 +40,10 @@
static bool cli_disabled = false;
static bool cli_need_auth = false;
+#if defined (GRUB_MACHINE_XEN) || defined (GRUB_MACHINE_XEN_PVH)
+#include <grub/xen.h>
+#endif
+
grub_addr_t
grub_modules_get_end (void)
{
@@ -351,6 +355,14 @@ grub_main (void)
grub_env_export ("root");
grub_env_export ("prefix");
+ /*
+ * Parse command line parameters from Xen and export them as environment
+ * variables
+ */
+#if defined (GRUB_MACHINE_XEN) || defined (GRUB_MACHINE_XEN_PVH)
+ grub_parse_xen_cmdline ();
+#endif
+
/* Reclaim space used for modules. */
reclaim_module_space ();
diff --git a/grub-core/kern/xen/cmdline.c b/grub-core/kern/xen/cmdline.c
new file mode 100644
index 000000000..0084f3e77
--- /dev/null
+++ b/grub-core/kern/xen/cmdline.c
@@ -0,0 +1,270 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/env.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/xen.h>
+
+#define PARSER_HIT_BACKSLASH 0x1
+#define PARSER_IN_SINGLE_QUOTES 0x2
+#define PARSER_IN_DOUBLE_QUOTES 0x4
+#define PARSER_BASE_WORD_LEN 16
+
+grub_size_t word_list_len;
+char **word_list;
+grub_size_t current_word_len;
+grub_size_t current_word_pos;
+char *current_word;
+char current_char;
+grub_size_t param_dict_len;
+char **param_keys;
+char **param_vals;
+
+static bool
+append_char_to_word (bool allow_null)
+{
+ if (!(current_char >= 0x20) || !(current_char <= 0x7E))
+ {
+ if (current_char != '\0' || !allow_null)
+ {
+ return false;
+ }
+ }
+ if (current_word_pos == current_word_len)
+ {
+ current_word_len *= 2;
+ current_word = grub_realloc (current_word, current_word_len);
+ }
+
+ current_word[current_word_pos] = current_char;
+ current_word_pos++;
+ return true;
+}
+
+static void
+append_word_to_list (void)
+{
+ for (grub_size_t i = 0; i < current_word_pos; i++)
+ {
+ if (current_word[i] <= 0x1F || current_word[i] >= 0x7F)
+ {
+ grub_free (current_word);
+ goto reset_word;
+ }
+ }
+ current_char = '\0';
+ if (!append_char_to_word (true))
+ {
+ grub_error (GRUB_ERR_BUG, N_("couldn't append null terminator to word
during Xen cmdline parsing"));
+ grub_print_error ();
+ grub_exit ();
+ }
+ current_word_len = grub_strlen (current_word) + 1;
+ current_word = grub_realloc (current_word, current_word_len);
+ word_list_len++;
+ word_list = grub_realloc (word_list, word_list_len * sizeof (char *));
+ word_list[word_list_len - 1] = current_word;
+
+reset_word:
+ current_word_len = PARSER_BASE_WORD_LEN;
+ current_word_pos = 0;
+ current_word = grub_malloc (current_word_len);
+}
+
+int
+check_key_is_safe (char *key, grub_size_t len)
+{
+ /*
+ * Ensure only a-z, A-Z, and _ are allowed.
+ */
+ for (grub_size_t i = 0; i < len; i++)
+ {
+ if (! ((key[i] >= 'A' && key[i] <= 'Z')
+ || (key[i] >= 'a' && key[i] <= 'z')
+ || (key[i] == '_') ) )
+ {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+void
+grub_parse_xen_cmdline (void)
+{
+ word_list = grub_malloc (0);
+ current_word_len = PARSER_BASE_WORD_LEN;
+ current_word = grub_malloc (current_word_len);
+ param_keys = grub_malloc (0);
+ param_vals = grub_malloc (0);
+
+ grub_uint8_t parse_flags = 0;
+ char *cmdline = (char *)grub_xen_start_page_addr->cmd_line;
+
+ for (grub_size_t i = 0; i < grub_strlen (cmdline); i++)
+ {
+ current_char = cmdline[i];
+
+ if (parse_flags & PARSER_HIT_BACKSLASH)
+ {
+ parse_flags ^= PARSER_HIT_BACKSLASH;
+ if (!append_char_to_word (false))
+ {
+ goto cleanup;
+ }
+ continue;
+ }
+
+ if (current_char == '\\')
+ {
+ parse_flags ^= PARSER_HIT_BACKSLASH;
+ continue;
+ }
+
+ if (current_char == '\'')
+ {
+ if (parse_flags & PARSER_IN_DOUBLE_QUOTES)
+ {
+ if (!append_char_to_word (false))
+ {
+ goto cleanup;
+ }
+ continue;
+ }
+
+ parse_flags ^= PARSER_IN_SINGLE_QUOTES;
+ continue;
+ }
+
+ if (current_char == '"')
+ {
+ if (parse_flags & PARSER_IN_SINGLE_QUOTES)
+ {
+ if (!append_char_to_word (false))
+ {
+ goto cleanup;
+ }
+ continue;
+ }
+
+ parse_flags ^= PARSER_IN_DOUBLE_QUOTES;
+ continue;
+ }
+
+ if (current_char == ' ')
+ {
+ if (parse_flags & PARSER_IN_SINGLE_QUOTES
+ || parse_flags & PARSER_IN_DOUBLE_QUOTES)
+ {
+ if (!append_char_to_word (false))
+ {
+ goto cleanup;
+ }
+ continue;
+ }
+
+ append_word_to_list ();
+ continue;
+ }
+
+ if (!append_char_to_word (false))
+ {
+ goto cleanup;
+ }
+ }
+
+ if (current_word_pos > 0)
+ {
+ append_word_to_list ();
+ }
+
+ param_keys = grub_realloc (param_keys, word_list_len * sizeof (char *));
+ param_vals = grub_realloc (param_vals, word_list_len * sizeof (char *));
+ for (grub_size_t i = 0; i < word_list_len; i++)
+ {
+ current_word = word_list[i];
+ current_word_len = grub_strlen (current_word) + 1;
+ char *current_word_eq_ptr = grub_strchr (current_word, '=');
+ if (current_word_eq_ptr)
+ {
+ grub_size_t eq_idx
+ = (grub_size_t)(current_word_eq_ptr - current_word);
+ grub_size_t pre_eq_len
+ = current_word_len - (current_word_len - eq_idx);
+ grub_size_t post_eq_len = current_word_len - eq_idx - 1;
+ if (check_key_is_safe (current_word, pre_eq_len))
+ {
+ param_keys[param_dict_len] = grub_malloc (pre_eq_len + 1);
+ param_vals[param_dict_len] = grub_malloc (post_eq_len + 1);
+ grub_strncpy (param_keys[param_dict_len],
+ current_word, pre_eq_len);
+ grub_strncpy (param_vals[param_dict_len],
+ current_word + pre_eq_len + 1, post_eq_len);
+ param_keys[param_dict_len][pre_eq_len] = '\0';
+ param_vals[param_dict_len][post_eq_len] = '\0';
+ param_dict_len++;
+ }
+ }
+ else
+ {
+ if (check_key_is_safe (current_word, current_word_len - 1))
+ {
+ param_keys[param_dict_len] = grub_malloc (current_word_len + 1);
+ param_vals[param_dict_len] = grub_malloc (2);
+ grub_strncpy (param_keys[param_dict_len],
+ current_word, current_word_len);
+ param_keys[param_dict_len][current_word_len] = '\0';
+ grub_strcpy (param_vals[param_dict_len], "1\0");
+ param_dict_len++;
+ }
+ }
+ }
+
+ for (grub_size_t i = 0; i < param_dict_len; i++)
+ {
+ /*
+ * Find keys that start with "xen_grub_env_" and export them
+ * as environment variables.
+ */
+ if (grub_strlen (param_keys[i]) < (sizeof ("xen_grub_env_") - 1))
+ {
+ continue;
+ }
+ if (grub_strncmp (param_keys[i],
+ "xen_grub_env_",
+ sizeof ("xen_grub_env_") - 1) != 0)
+ {
+ continue;
+ }
+ grub_env_set (param_keys[i], param_vals[i]);
+ grub_env_export (param_keys[i]);
+ grub_free (param_keys[i]);
+ grub_free (param_vals[i]);
+ }
+
+cleanup:
+ for (grub_size_t i = 0; i < word_list_len; i++)
+ {
+ grub_free (word_list[i]);
+ }
+
+ grub_free (param_keys);
+ grub_free (param_vals);
+ grub_free (word_list);
+}
diff --git a/include/grub/xen.h b/include/grub/xen.h
index 91cb7cf81..7f9efee80 100644
--- a/include/grub/xen.h
+++ b/include/grub/xen.h
@@ -89,6 +89,8 @@ void grub_console_init (void);
void grub_xendisk_fini (void);
void grub_xendisk_init (void);
+void grub_parse_xen_cmdline (void);
+
#ifdef __x86_64__
typedef grub_uint64_t grub_xen_mfn_t;
#else
--
2.49.0
pgpQSDai0CJGC.pgp
Description: OpenPGP digital signature