Skip to content

Commit 192189f

Browse files
committed
Add Xen command line parsing
Enable GRUB to parse the Xen command line for parameters, and expose those parameters to the GRUB config file (or rescue shell) as environment variables.
1 parent ae7ee90 commit 192189f

2 files changed

Lines changed: 335 additions & 0 deletions

File tree

Lines changed: 334 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,334 @@
1+
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
2+
index 95fd18d..3a50f3c 100644
3+
--- a/grub-core/Makefile.core.def
4+
+++ b/grub-core/Makefile.core.def
5+
@@ -254,6 +254,7 @@ kernel = {
6+
xen = term/xen/console.c;
7+
xen = disk/xen/xendisk.c;
8+
xen = commands/boot.c;
9+
+ xen = kern/xen/cmdline.c;
10+
11+
i386_xen_pvh = commands/boot.c;
12+
i386_xen_pvh = disk/xen/xendisk.c;
13+
@@ -261,6 +262,7 @@ kernel = {
14+
i386_xen_pvh = kern/i386/xen/tsc.c;
15+
i386_xen_pvh = kern/i386/xen/pvh.c;
16+
i386_xen_pvh = kern/xen/init.c;
17+
+ i386_xen_pvh = kern/xen/cmdline.c;
18+
i386_xen_pvh = term/xen/console.c;
19+
20+
ia64_efi = kern/ia64/efi/startup.S;
21+
diff --git a/grub-core/kern/i386/xen/pvh.c b/grub-core/kern/i386/xen/pvh.c
22+
index 91fbca8..9fb0480 100644
23+
--- a/grub-core/kern/i386/xen/pvh.c
24+
+++ b/grub-core/kern/i386/xen/pvh.c
25+
@@ -352,6 +352,22 @@ grub_xen_setup_pvh (void)
26+
grub_xen_mm_init_regions ();
27+
28+
grub_rsdp_addr = pvh_start_info->rsdp_paddr;
29+
+ int xen_cmdline_fits = 0;
30+
+ char *xen_cmdline = (char *) pvh_start_info->cmdline_paddr;
31+
+ for (int i = 0; i < 1024; i++)
32+
+ {
33+
+ if (xen_cmdline[i] == '\0')
34+
+ {
35+
+ xen_cmdline_fits = 1;
36+
+ break;
37+
+ }
38+
+ }
39+
+ if (xen_cmdline_fits == 1)
40+
+ {
41+
+ grub_strncpy ((char *) grub_xen_start_page_addr->cmd_line,
42+
+ (char *) pvh_start_info->cmdline_paddr,
43+
+ MAX_GUEST_CMDLINE);
44+
+ }
45+
}
46+
47+
grub_err_t
48+
diff --git a/grub-core/kern/main.c b/grub-core/kern/main.c
49+
index 8e89763..c078075 100644
50+
--- a/grub-core/kern/main.c
51+
+++ b/grub-core/kern/main.c
52+
@@ -35,6 +35,10 @@
53+
#include <grub/machine/memory.h>
54+
#endif
55+
56+
+#if defined (GRUB_MACHINE_XEN) || defined (GRUB_MACHINE_XEN_PVH)
57+
+#include <grub/xen.h>
58+
+#endif
59+
+
60+
grub_addr_t
61+
grub_modules_get_end (void)
62+
{
63+
@@ -362,6 +366,14 @@ grub_main (void)
64+
grub_env_export ("root");
65+
grub_env_export ("prefix");
66+
67+
+ /*
68+
+ * Parse command line parameters from Xen and export them as environment
69+
+ * variables
70+
+ */
71+
+#if defined (GRUB_MACHINE_XEN) || defined (GRUB_MACHINE_XEN_PVH)
72+
+ grub_parse_xen_cmdline ();
73+
+#endif
74+
+
75+
/* Reclaim space used for modules. */
76+
reclaim_module_space ();
77+
78+
diff --git a/grub-core/kern/xen/cmdline.c b/grub-core/kern/xen/cmdline.c
79+
new file mode 100644
80+
index 0000000..7626799
81+
--- /dev/null
82+
+++ b/grub-core/kern/xen/cmdline.c
83+
@@ -0,0 +1,223 @@
84+
+/*
85+
+ * GRUB -- GRand Unified Bootloader
86+
+ * Copyright (C) 2025 Free Software Foundation, Inc.
87+
+ *
88+
+ * GRUB is free software: you can redistribute it and/or modify
89+
+ * it under the terms of the GNU General Public License as published by
90+
+ * the Free Software Foundation, either version 3 of the License, or
91+
+ * (at your option) any later version.
92+
+ *
93+
+ * GRUB is distributed in the hope that it will be useful,
94+
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
95+
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
96+
+ * GNU General Public License for more details.
97+
+ *
98+
+ * You should have received a copy of the GNU General Public License
99+
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
100+
+ */
101+
+
102+
+#include <grub/env.h>
103+
+#include <grub/misc.h>
104+
+#include <grub/mm.h>
105+
+#include <grub/xen.h>
106+
+
107+
+#define PARSER_HIT_BACKSLASH 0x1
108+
+#define PARSER_IN_SINGLE_QUOTES 0x2
109+
+#define PARSER_IN_DOUBLE_QUOTES 0x4
110+
+#define PARSER_BASE_WORD_LEN 16
111+
+
112+
+grub_size_t word_list_len;
113+
+char **word_list;
114+
+grub_size_t current_word_len;
115+
+grub_size_t current_word_pos;
116+
+char *current_word;
117+
+char current_char;
118+
+grub_size_t param_dict_len;
119+
+char **param_keys;
120+
+char **param_vals;
121+
+
122+
+static void
123+
+append_char_to_word (void)
124+
+{
125+
+ if (current_word_pos == current_word_len)
126+
+ {
127+
+ current_word_len *= 2;
128+
+ current_word = grub_realloc (current_word, current_word_len);
129+
+ }
130+
+
131+
+ current_word[current_word_pos] = current_char;
132+
+ current_word_pos++;
133+
+}
134+
+
135+
+static void
136+
+append_word_to_list (void)
137+
+{
138+
+ for (grub_size_t i = 0; i < current_word_pos; i++)
139+
+ {
140+
+ if (current_word[i] <= 0x1F || current_word[i] >= 0x7F)
141+
+ {
142+
+ grub_free (current_word);
143+
+ goto reset_word;
144+
+ }
145+
+ }
146+
+ current_char = '\0';
147+
+ append_char_to_word ();
148+
+ current_word_len = grub_strlen (current_word) + 1;
149+
+ current_word = grub_realloc (current_word, current_word_len);
150+
+ word_list_len++;
151+
+ word_list = grub_realloc (word_list, word_list_len * sizeof (char *));
152+
+ word_list[word_list_len - 1] = current_word;
153+
+
154+
+reset_word:
155+
+ current_word_len = PARSER_BASE_WORD_LEN;
156+
+ current_word_pos = 0;
157+
+ current_word = grub_malloc (current_word_len);
158+
+}
159+
+
160+
+int
161+
+check_key_is_safe (char *key, grub_size_t len)
162+
+{
163+
+ /*
164+
+ * Ensure only a-z, A-Z, and _ are allowed.
165+
+ */
166+
+ for (grub_size_t i = 0; i < len; i++)
167+
+ {
168+
+ if (! ((key[i] >= 'A' && key[i] <= 'Z')
169+
+ || (key[i] >= 'a' && key[i] <= 'z')
170+
+ || (key[i] == '_') ) )
171+
+ {
172+
+ return 0;
173+
+ }
174+
+ }
175+
+ return 1;
176+
+}
177+
+
178+
+void
179+
+grub_parse_xen_cmdline (void)
180+
+{
181+
+ word_list = grub_malloc (0);
182+
+ current_word_len = PARSER_BASE_WORD_LEN;
183+
+ current_word = grub_malloc (current_word_len);
184+
+ param_keys = grub_malloc (0);
185+
+ param_vals = grub_malloc (0);
186+
+
187+
+ grub_uint8_t parse_flags = 0;
188+
+ char *cmdline = (char *)grub_xen_start_page_addr->cmd_line;
189+
+
190+
+ for (grub_size_t i = 0; i < grub_strlen (cmdline); i++)
191+
+ {
192+
+ current_char = cmdline[i];
193+
+
194+
+ if (parse_flags & PARSER_HIT_BACKSLASH)
195+
+ {
196+
+ parse_flags ^= PARSER_HIT_BACKSLASH;
197+
+ append_char_to_word ();
198+
+ continue;
199+
+ }
200+
+
201+
+ if (current_char == '\\')
202+
+ {
203+
+ parse_flags ^= PARSER_HIT_BACKSLASH;
204+
+ continue;
205+
+ }
206+
+
207+
+ if (current_char == '\'')
208+
+ {
209+
+ if (parse_flags & PARSER_IN_DOUBLE_QUOTES)
210+
+ {
211+
+ append_char_to_word ();
212+
+ continue;
213+
+ }
214+
+
215+
+ parse_flags ^= PARSER_IN_SINGLE_QUOTES;
216+
+ continue;
217+
+ }
218+
+
219+
+ if (current_char == '"')
220+
+ {
221+
+ if (parse_flags & PARSER_IN_SINGLE_QUOTES)
222+
+ {
223+
+ append_char_to_word ();
224+
+ continue;
225+
+ }
226+
+
227+
+ parse_flags ^= PARSER_IN_DOUBLE_QUOTES;
228+
+ continue;
229+
+ }
230+
+
231+
+ if (current_char == ' ')
232+
+ {
233+
+ if (parse_flags & PARSER_IN_SINGLE_QUOTES
234+
+ || parse_flags & PARSER_IN_DOUBLE_QUOTES)
235+
+ {
236+
+ append_char_to_word ();
237+
+ continue;
238+
+ }
239+
+
240+
+ append_word_to_list ();
241+
+ continue;
242+
+ }
243+
+
244+
+ append_char_to_word ();
245+
+ }
246+
+
247+
+ if (current_word_pos > 0)
248+
+ {
249+
+ append_word_to_list ();
250+
+ }
251+
+
252+
+ param_keys = grub_realloc (param_keys, word_list_len * sizeof (char *));
253+
+ param_vals = grub_realloc (param_vals, word_list_len * sizeof (char *));
254+
+ for (grub_size_t i = 0; i < word_list_len; i++)
255+
+ {
256+
+ current_word = word_list[i];
257+
+ current_word_len = grub_strlen (current_word) + 1;
258+
+ char *current_word_eq_ptr = grub_strchr (current_word, '=');
259+
+ if (current_word_eq_ptr)
260+
+ {
261+
+ grub_size_t eq_idx
262+
+ = (grub_size_t)(current_word_eq_ptr - current_word);
263+
+ grub_size_t pre_eq_len
264+
+ = current_word_len - (current_word_len - eq_idx);
265+
+ grub_size_t post_eq_len = current_word_len - eq_idx - 1;
266+
+ if (check_key_is_safe (current_word, pre_eq_len))
267+
+ {
268+
+ param_keys[param_dict_len] = grub_malloc (pre_eq_len + 1);
269+
+ param_vals[param_dict_len] = grub_malloc (post_eq_len + 1);
270+
+ grub_strncpy (param_keys[param_dict_len],
271+
+ current_word, pre_eq_len);
272+
+ grub_strncpy (param_vals[param_dict_len],
273+
+ current_word + pre_eq_len + 1, post_eq_len);
274+
+ param_keys[param_dict_len][pre_eq_len] = '\0';
275+
+ param_vals[param_dict_len][post_eq_len] = '\0';
276+
+ param_dict_len++;
277+
+ }
278+
+ }
279+
+ else
280+
+ {
281+
+ if (check_key_is_safe (current_word, current_word_len - 1))
282+
+ {
283+
+ param_keys[param_dict_len] = grub_malloc (current_word_len + 1);
284+
+ param_vals[param_dict_len] = grub_malloc (2);
285+
+ grub_strncpy (param_keys[param_dict_len],
286+
+ current_word, current_word_len);
287+
+ param_keys[param_dict_len][current_word_len] = '\0';
288+
+ grub_strcpy (param_vals[param_dict_len], "1\0");
289+
+ param_dict_len++;
290+
+ }
291+
+ }
292+
+ }
293+
+
294+
+ for (grub_size_t i = 0; i < param_dict_len; i++)
295+
+ {
296+
+ grub_env_set (param_keys[i], param_vals[i]);
297+
+ grub_env_export (param_keys[i]);
298+
+ grub_free (param_keys[i]);
299+
+ grub_free (param_vals[i]);
300+
+ grub_free (word_list[i]);
301+
+ }
302+
+
303+
+ grub_free (param_keys);
304+
+ grub_free (param_vals);
305+
+ grub_free (word_list);
306+
+}
307+
diff --git a/grub-core/loader/i386/linux.c b/grub-core/loader/i386/linux.c
308+
index 90121e9..818ceb3 100644
309+
--- a/grub-core/loader/i386/linux.c
310+
+++ b/grub-core/loader/i386/linux.c
311+
@@ -64,6 +64,10 @@ GRUB_MOD_LICENSE ("GPLv3+");
312+
#define ACCEPTS_PURE_TEXT 1
313+
#endif
314+
315+
+#ifdef GRUB_MACHINE_XEN_PVH
316+
+#include <grub/xen.h>
317+
+#endif
318+
+
319+
static grub_dl_t my_mod;
320+
321+
static grub_size_t linux_mem_size;
322+
diff --git a/include/grub/xen.h b/include/grub/xen.h
323+
index 91cb7cf..7f9efee 100644
324+
--- a/include/grub/xen.h
325+
+++ b/include/grub/xen.h
326+
@@ -89,6 +89,8 @@ void grub_console_init (void);
327+
void grub_xendisk_fini (void);
328+
void grub_xendisk_init (void);
329+
330+
+void grub_parse_xen_cmdline (void);
331+
+
332+
#ifdef __x86_64__
333+
typedef grub_uint64_t grub_xen_mfn_t;
334+
#else

grub2-xen.spec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,7 @@ Patch0281: 0281-loader-efi-Fix-RISC-V-build.patch
300300
Patch0282: 0282-kern-riscv-efi-init-Use-time-register-in-grub_efi_ge.patch
301301
Patch0283: 0283-Use-medany-instead-of-large-model-for-RISCV.patch
302302
Patch0284: 0284-fs-xfs-Fix-large-extent-counters-incompat-feature-su.patch
303+
Patch0300: 0300-Experimental-Xen-hacking-with-GRUB-parameters.patch
303304

304305

305306
BuildRequires: autoconf

0 commit comments

Comments
 (0)