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

Attachment: pgpdRAWiJGkrY.pgp
Description: OpenPGP digital signature

_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel

Reply via email to