Blob Blame Raw
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Javier Martinez Canillas <javierm@redhat.com>
Date: Wed, 30 Jan 2019 15:08:22 +0100
Subject: [PATCH] blscfg: add support for prepend early initrds to the BLS
 entries

There are cases where is needed one or more initramfs besides the one that
is defined in the BLS entry. For example, a user may want to add an early
initramfs to override some ACPI tables, load CPU microcode, firmware, etc.

Add support to preprend initrds if they are defined as an early_initrd var
in grubenv. Also honor GRUB_EARLY_INITRD_LINUX_CUSTOM in /etc/default/grub
and use that value to set the early_initrd var when running grub2-mkconfig.

Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
---
 grub-core/commands/blscfg.c | 79 +++++++++++++++++++++++++++++++++++++++++++--
 util/grub.d/10_linux.in     |  3 ++
 util/grub.d/10_linux_bls.in |  3 ++
 3 files changed, 83 insertions(+), 2 deletions(-)

diff --git a/grub-core/commands/blscfg.c b/grub-core/commands/blscfg.c
index aa5bf0d3220..1ef2ae06cff 100644
--- a/grub-core/commands/blscfg.c
+++ b/grub-core/commands/blscfg.c
@@ -660,6 +660,33 @@ static char *expand_val(char *value)
   return buffer;
 }
 
+static char **early_initrd_list (const char *initrd)
+{
+  int nlist = 0;
+  char **list = NULL;
+  char *separator;
+
+  while ((separator = grub_strchr (initrd, ' ')))
+    {
+      list = grub_realloc (list, (nlist + 2) * sizeof (char *));
+      if (!list)
+        return NULL;
+
+      list[nlist++] = grub_strndup(initrd, separator - initrd);
+      list[nlist] = NULL;
+      initrd = separator + 1;
+  }
+
+  list = grub_realloc (list, (nlist + 2) * sizeof (char *));
+  if (!list)
+    return NULL;
+
+  list[nlist++] = grub_strndup(initrd, grub_strlen(initrd));
+  list[nlist] = NULL;
+
+  return list;
+}
+
 static void create_entry (struct bls_entry *entry)
 {
   int argc = 0;
@@ -670,6 +697,9 @@ static void create_entry (struct bls_entry *entry)
   char *options = NULL;
   char **initrds = NULL;
   char *initrd = NULL;
+  const char *early_initrd = NULL;
+  char **early_initrds = NULL;
+  char *initrd_prefix = NULL;
   char *id = entry->filename;
   char *dotconf = id;
   char *hotkey = NULL;
@@ -716,13 +746,47 @@ static void create_entry (struct bls_entry *entry)
     argv[i] = args[i-1];
   argv[argc] = NULL;
 
+  early_initrd = grub_env_get("early_initrd");
+
   grub_dprintf ("blscfg", "adding menu entry for \"%s\" with id \"%s\"\n",
 		title, id);
-  if (initrds)
+  if (early_initrd)
+    {
+      early_initrds = early_initrd_list(early_initrd);
+      if (!early_initrds)
+      {
+	grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
+	goto finish;
+      }
+
+      if (initrds != NULL && initrds[0] != NULL)
+	{
+	  initrd_prefix = grub_strrchr (initrds[0], '/');
+	  initrd_prefix = grub_strndup(initrds[0], initrd_prefix - initrds[0] + 1);
+	}
+      else
+	{
+	  initrd_prefix = grub_strrchr (clinux, '/');
+	  initrd_prefix = grub_strndup(clinux, initrd_prefix - clinux + 1);
+	}
+
+      if (!initrd_prefix)
+	{
+	  grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
+	  goto finish;
+	}
+    }
+
+  if (early_initrds || initrds)
     {
       int initrd_size = sizeof ("initrd");
       char *tmp;
 
+      for (i = 0; early_initrds != NULL && early_initrds[i] != NULL; i++)
+	initrd_size += sizeof (" " GRUB_BOOT_DEVICE) \
+		       + grub_strlen(initrd_prefix)  \
+		       + grub_strlen (early_initrds[i]) + 1;
+
       for (i = 0; initrds != NULL && initrds[i] != NULL; i++)
 	initrd_size += sizeof (" " GRUB_BOOT_DEVICE) \
 		       + grub_strlen (initrds[i]) + 1;
@@ -736,7 +800,16 @@ static void create_entry (struct bls_entry *entry)
 	}
 
 
-      tmp = grub_stpcpy(initrd, "initrd ");
+      tmp = grub_stpcpy(initrd, "initrd");
+      for (i = 0; early_initrds != NULL && early_initrds[i] != NULL; i++)
+	{
+	  grub_dprintf ("blscfg", "adding early initrd %s\n", early_initrds[i]);
+	  tmp = grub_stpcpy (tmp, " " GRUB_BOOT_DEVICE);
+	  tmp = grub_stpcpy (tmp, initrd_prefix);
+	  tmp = grub_stpcpy (tmp, early_initrds[i]);
+	  grub_free(early_initrds[i]);
+	}
+
       for (i = 0; initrds != NULL && initrds[i] != NULL; i++)
 	{
 	  grub_dprintf ("blscfg", "adding initrd %s\n", initrds[i]);
@@ -759,6 +832,8 @@ static void create_entry (struct bls_entry *entry)
 
 finish:
   grub_free (initrd);
+  grub_free (initrd_prefix);
+  grub_free (early_initrds);
   grub_free (initrds);
   grub_free (options);
   grub_free (classes);
diff --git a/util/grub.d/10_linux.in b/util/grub.d/10_linux.in
index da2992ac9f1..9c240f92625 100644
--- a/util/grub.d/10_linux.in
+++ b/util/grub.d/10_linux.in
@@ -167,6 +167,9 @@ EOF
 
     if [ "x${GRUB_GRUBENV_UPDATE}" = "xyes" ]; then
 	${grub_editenv} - set kernelopts="root=${linux_root_device_thisversion} ro ${args}"
+	if [ -n "${GRUB_EARLY_INITRD_LINUX_CUSTOM}" ]; then
+	    ${grub_editenv} - set early_initrd="${GRUB_EARLY_INITRD_LINUX_CUSTOM}"
+	fi
     fi
 
     exit 0
diff --git a/util/grub.d/10_linux_bls.in b/util/grub.d/10_linux_bls.in
index 175bedd0763..b14951daf82 100644
--- a/util/grub.d/10_linux_bls.in
+++ b/util/grub.d/10_linux_bls.in
@@ -227,6 +227,9 @@ linux_entry ()
 
     if [ "x${GRUB_GRUBENV_UPDATE}" = "xyes" ]; then
 	${grub_editenv} - set kernelopts="root=${linux_root_device_thisversion} ro ${args}"
+	if [ -n "${GRUB_EARLY_INITRD_LINUX_CUSTOM}" ]; then
+	    ${grub_editenv} - set early_initrd="${GRUB_EARLY_INITRD_LINUX_CUSTOM}"
+	fi
     fi
 
     exit 0