bc092b9
From 656c3b0d7fbe23f0e1ab561c8faa4ac4a37364d1 Mon Sep 17 00:00:00 2001
bc092b9
From: Vladimir Serbinenko <phcoder@gmail.com>
bc092b9
Date: Mon, 8 May 2017 22:00:06 +0200
78e1a10
Subject: [PATCH 026/216] arm_coreboot: Support loading linux images.
bc092b9
bc092b9
---
bc092b9
 grub-core/loader/arm/linux.c | 132 ++++++++++++++++++++++---------------------
bc092b9
 include/grub/arm/linux.h     |  16 ++++++
bc092b9
 2 files changed, 83 insertions(+), 65 deletions(-)
bc092b9
bc092b9
diff --git a/grub-core/loader/arm/linux.c b/grub-core/loader/arm/linux.c
ec4acbb
index 01374ee78fa..260cbf06861 100644
bc092b9
--- a/grub-core/loader/arm/linux.c
bc092b9
+++ b/grub-core/loader/arm/linux.c
bc092b9
@@ -31,8 +31,6 @@
bc092b9
 
bc092b9
 GRUB_MOD_LICENSE ("GPLv3+");
bc092b9
 
bc092b9
-#ifndef GRUB_MACHINE_COREBOOT
bc092b9
-
bc092b9
 static grub_dl_t my_mod;
bc092b9
 
bc092b9
 static grub_addr_t initrd_start;
bc092b9
@@ -44,7 +42,7 @@ static grub_size_t linux_size;
bc092b9
 static char *linux_args;
bc092b9
 
bc092b9
 static grub_uint32_t machine_type;
bc092b9
-static void *fdt_addr;
bc092b9
+static const void *current_fdt;
bc092b9
 
bc092b9
 typedef void (*kernel_entry_t) (int, unsigned long, void *);
bc092b9
 
bc092b9
@@ -56,9 +54,9 @@ typedef void (*kernel_entry_t) (int, unsigned long, void *);
bc092b9
 #define LINUX_FDT_PHYS_OFFSET    (LINUX_INITRD_PHYS_OFFSET - 0x10000)
bc092b9
 
bc092b9
 static grub_size_t
bc092b9
-get_atag_size (grub_uint32_t *atag)
bc092b9
+get_atag_size (const grub_uint32_t *atag)
bc092b9
 {
bc092b9
-  grub_uint32_t *atag0 = atag;
bc092b9
+  const grub_uint32_t *atag0 = atag;
bc092b9
   while (atag[0] && atag[1])
bc092b9
     atag += atag[0];
bc092b9
   return atag - atag0;
bc092b9
@@ -70,10 +68,11 @@ get_atag_size (grub_uint32_t *atag)
bc092b9
  *   Merges in command line parameters and sets up initrd addresses.
bc092b9
  */
bc092b9
 static grub_err_t
bc092b9
-linux_prepare_atag (void)
bc092b9
+linux_prepare_atag (void *target_atag)
bc092b9
 {
bc092b9
-  grub_uint32_t *atag_orig = (grub_uint32_t *) fdt_addr;
bc092b9
-  grub_uint32_t *tmp_atag, *from, *to;
bc092b9
+  const grub_uint32_t *atag_orig = (const grub_uint32_t *) current_fdt;
bc092b9
+  grub_uint32_t *tmp_atag, *to;
bc092b9
+  const grub_uint32_t *from;
bc092b9
   grub_size_t tmp_size;
bc092b9
   grub_size_t arg_size = grub_strlen (linux_args);
bc092b9
   char *cmdline_orig = NULL;
bc092b9
@@ -144,7 +143,7 @@ linux_prepare_atag (void)
bc092b9
   to += 2;
bc092b9
 
bc092b9
   /* Copy updated FDT to its launch location */
bc092b9
-  grub_memcpy (atag_orig, tmp_atag, sizeof (grub_uint32_t) * (to - tmp_atag));
bc092b9
+  grub_memcpy (target_atag, tmp_atag, sizeof (grub_uint32_t) * (to - tmp_atag));
bc092b9
   grub_free (tmp_atag);
bc092b9
 
bc092b9
   grub_dprintf ("loader", "ATAG updated for Linux boot\n");
bc092b9
@@ -158,19 +157,19 @@ linux_prepare_atag (void)
bc092b9
  *   Merges in command line parameters and sets up initrd addresses.
bc092b9
  */
bc092b9
 static grub_err_t
bc092b9
-linux_prepare_fdt (void)
bc092b9
+linux_prepare_fdt (void *target_fdt)
bc092b9
 {
bc092b9
   int node;
bc092b9
   int retval;
bc092b9
   int tmp_size;
bc092b9
   void *tmp_fdt;
bc092b9
 
bc092b9
-  tmp_size = grub_fdt_get_totalsize (fdt_addr) + 0x100 + grub_strlen (linux_args);
bc092b9
+  tmp_size = grub_fdt_get_totalsize (current_fdt) + 0x100 + grub_strlen (linux_args);
bc092b9
   tmp_fdt = grub_malloc (tmp_size);
bc092b9
   if (!tmp_fdt)
bc092b9
     return grub_errno;
bc092b9
 
bc092b9
-  grub_memcpy (tmp_fdt, fdt_addr, grub_fdt_get_totalsize (fdt_addr));
bc092b9
+  grub_memcpy (tmp_fdt, current_fdt, grub_fdt_get_totalsize (current_fdt));
bc092b9
   grub_fdt_set_totalsize (tmp_fdt, tmp_size);
bc092b9
 
bc092b9
   /* Find or create '/chosen' node */
bc092b9
@@ -211,7 +210,7 @@ linux_prepare_fdt (void)
bc092b9
     }
bc092b9
 
bc092b9
   /* Copy updated FDT to its launch location */
bc092b9
-  grub_memcpy (fdt_addr, tmp_fdt, tmp_size);
bc092b9
+  grub_memcpy (target_fdt, tmp_fdt, tmp_size);
bc092b9
   grub_free (tmp_fdt);
bc092b9
 
bc092b9
   grub_dprintf ("loader", "FDT updated for Linux boot\n");
bc092b9
@@ -228,16 +227,17 @@ linux_boot (void)
bc092b9
 {
bc092b9
   kernel_entry_t linuxmain;
bc092b9
   int fdt_valid, atag_valid;
bc092b9
+  void *target_fdt = 0;
bc092b9
 
bc092b9
-  fdt_valid = (fdt_addr && grub_fdt_check_header_nosize (fdt_addr) == 0);
bc092b9
-  atag_valid = ((((grub_uint16_t *) fdt_addr)[3] & ~3) == 0x5440
bc092b9
-		&& *((grub_uint32_t *) fdt_addr));
bc092b9
+  fdt_valid = (current_fdt && grub_fdt_check_header_nosize (current_fdt) == 0);
bc092b9
+  atag_valid = ((((const grub_uint16_t *) current_fdt)[3] & ~3) == 0x5440
bc092b9
+		&& *((const grub_uint32_t *) current_fdt));
bc092b9
   grub_dprintf ("loader", "atag: %p, %x, %x, %s, %s\n",
bc092b9
-		fdt_addr,
bc092b9
-		((grub_uint16_t *) fdt_addr)[3],
bc092b9
-		*((grub_uint32_t *) fdt_addr),
bc092b9
-		(char *) fdt_addr,
bc092b9
-		(char *) fdt_addr + 1);
bc092b9
+		current_fdt,
bc092b9
+		((const grub_uint16_t *) current_fdt)[3],
bc092b9
+		*((const grub_uint32_t *) current_fdt),
bc092b9
+		(const char *) current_fdt,
bc092b9
+		(const char *) current_fdt + 1);
bc092b9
 
bc092b9
   if (!fdt_valid && machine_type == GRUB_ARM_MACHINE_TYPE_FDT)
bc092b9
     return grub_error (GRUB_ERR_FILE_NOT_FOUND,
bc092b9
@@ -247,23 +247,40 @@ linux_boot (void)
bc092b9
 
bc092b9
   grub_dprintf ("loader", "Kernel at: 0x%x\n", linux_addr);
bc092b9
 
bc092b9
+  if (fdt_valid || atag_valid)
bc092b9
+    {
bc092b9
+#ifdef GRUB_MACHINE_EFI
bc092b9
+      grub_size_t size;
bc092b9
+      if (fdt_valid)
bc092b9
+	size = grub_fdt_get_totalsize (fdt_addr);
bc092b9
+      else
bc092b9
+	size = 4 * get_atag_size (atag_orig);
bc092b9
+      size += grub_strlen (linux_args) + 256;
bc092b9
+      target_fdt = grub_efi_allocate_loader_memory (LINUX_FDT_PHYS_OFFSET, size);
bc092b9
+      if (!fdt_addr)
bc092b9
+	return grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
bc092b9
+#else
bc092b9
+      target_fdt = (void *) LINUX_FDT_ADDRESS;
bc092b9
+#endif
bc092b9
+    }
bc092b9
+
bc092b9
   if (fdt_valid)
bc092b9
     {
bc092b9
       grub_err_t err;
bc092b9
 
bc092b9
-      err = linux_prepare_fdt ();
bc092b9
+      err = linux_prepare_fdt (target_fdt);
bc092b9
       if (err)
bc092b9
 	return err;
bc092b9
-      grub_dprintf ("loader", "FDT @ 0x%p\n", fdt_addr);
bc092b9
+      grub_dprintf ("loader", "FDT @ %p\n", target_fdt);
bc092b9
     }
bc092b9
   else if (atag_valid)
bc092b9
     {
bc092b9
       grub_err_t err;
bc092b9
 
bc092b9
-      err = linux_prepare_atag ();
bc092b9
+      err = linux_prepare_atag (target_fdt);
bc092b9
       if (err)
bc092b9
 	return err;
bc092b9
-      grub_dprintf ("loader", "ATAG @ 0x%p\n", fdt_addr);
bc092b9
+      grub_dprintf ("loader", "ATAG @ %p\n", target_fdt);
bc092b9
     }
bc092b9
 
bc092b9
   grub_dprintf ("loader", "Jumping to Linux...\n");
bc092b9
@@ -287,7 +304,7 @@ linux_boot (void)
bc092b9
 
bc092b9
   grub_arm_disable_caches_mmu ();
bc092b9
 
bc092b9
-  linuxmain (0, machine_type, fdt_addr);
bc092b9
+  linuxmain (0, machine_type, target_fdt);
bc092b9
 
bc092b9
   return grub_error (GRUB_ERR_BAD_OS, "Linux call returned");
bc092b9
 }
bc092b9
@@ -446,11 +463,26 @@ fail:
bc092b9
 static grub_err_t
bc092b9
 load_dtb (grub_file_t dtb, int size)
bc092b9
 {
bc092b9
-  if ((grub_file_read (dtb, fdt_addr, size) != size)
bc092b9
-      || (grub_fdt_check_header (fdt_addr, size) != 0))
bc092b9
-    return grub_error (GRUB_ERR_BAD_OS, N_("invalid device tree"));
bc092b9
+  void *new_fdt = grub_zalloc (size);
bc092b9
+  if (!new_fdt)
bc092b9
+    return grub_errno;
bc092b9
+  grub_dprintf ("loader", "Loading device tree to %p\n",
bc092b9
+		new_fdt);
bc092b9
+  if ((grub_file_read (dtb, new_fdt, size) != size)
bc092b9
+      || (grub_fdt_check_header (new_fdt, size) != 0))
bc092b9
+    {
bc092b9
+      grub_free (new_fdt);
bc092b9
+      return grub_error (GRUB_ERR_BAD_OS, N_("invalid device tree"));
bc092b9
+    }
bc092b9
+
bc092b9
+  grub_fdt_set_totalsize (new_fdt, size);
bc092b9
+  current_fdt = new_fdt;
bc092b9
+  /* 
bc092b9
+   * We've successfully loaded an FDT, so any machine type passed
bc092b9
+   * from firmware is now obsolete.
bc092b9
+   */
bc092b9
+  machine_type = GRUB_ARM_MACHINE_TYPE_FDT;
bc092b9
 
bc092b9
-  grub_fdt_set_totalsize (fdt_addr, size);
bc092b9
   return GRUB_ERR_NONE;
bc092b9
 }
bc092b9
 
bc092b9
@@ -466,42 +498,13 @@ grub_cmd_devicetree (grub_command_t cmd __attribute__ ((unused)),
bc092b9
 
bc092b9
   dtb = grub_file_open (argv[0]);
bc092b9
   if (!dtb)
bc092b9
-    goto out;
bc092b9
+    return grub_errno;
bc092b9
 
bc092b9
   size = grub_file_size (dtb);
bc092b9
   if (size == 0)
bc092b9
-    {
bc092b9
-      grub_error (GRUB_ERR_BAD_OS, "empty file");
bc092b9
-      goto out;
bc092b9
-    }
bc092b9
-
bc092b9
-#ifdef GRUB_MACHINE_EFI
bc092b9
-  fdt_addr = grub_efi_allocate_loader_memory (LINUX_FDT_PHYS_OFFSET, size);
bc092b9
-  if (!fdt_addr)
bc092b9
-    {
bc092b9
-      grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
bc092b9
-      goto out;
bc092b9
-    }
bc092b9
-#else
bc092b9
-  fdt_addr = (void *) LINUX_FDT_ADDRESS;
bc092b9
-#endif
bc092b9
-
bc092b9
-  grub_dprintf ("loader", "Loading device tree to 0x%08x\n",
bc092b9
-		(grub_addr_t) fdt_addr);
bc092b9
-  load_dtb (dtb, size);
bc092b9
-  if (grub_errno != GRUB_ERR_NONE)
bc092b9
-    {
bc092b9
-      fdt_addr = NULL;
bc092b9
-      goto out;
bc092b9
-    }
bc092b9
-
bc092b9
-  /* 
bc092b9
-   * We've successfully loaded an FDT, so any machine type passed
bc092b9
-   * from firmware is now obsolete.
bc092b9
-   */
bc092b9
-  machine_type = GRUB_ARM_MACHINE_TYPE_FDT;
bc092b9
-
bc092b9
- out:
bc092b9
+    grub_error (GRUB_ERR_BAD_OS, "empty file");
bc092b9
+  else
bc092b9
+    load_dtb (dtb, size);
bc092b9
   grub_file_close (dtb);
bc092b9
 
bc092b9
   return grub_errno;
bc092b9
@@ -519,7 +522,7 @@ GRUB_MOD_INIT (linux)
bc092b9
 					  /* TRANSLATORS: DTB stands for device tree blob.  */
bc092b9
 					  0, N_("Load DTB file."));
bc092b9
   my_mod = mod;
bc092b9
-  fdt_addr = (void *) grub_arm_firmware_get_boot_data ();
bc092b9
+  current_fdt = grub_arm_firmware_get_boot_data ();
bc092b9
   machine_type = grub_arm_firmware_get_machine_type ();
bc092b9
 }
bc092b9
 
bc092b9
@@ -529,4 +532,3 @@ GRUB_MOD_FINI (linux)
bc092b9
   grub_unregister_command (cmd_initrd);
bc092b9
   grub_unregister_command (cmd_devicetree);
bc092b9
 }
bc092b9
-#endif
bc092b9
diff --git a/include/grub/arm/linux.h b/include/grub/arm/linux.h
ec4acbb
index a66caad13db..f217f8281ad 100644
bc092b9
--- a/include/grub/arm/linux.h
bc092b9
+++ b/include/grub/arm/linux.h
bc092b9
@@ -46,6 +46,22 @@ grub_arm_firmware_get_machine_type (void)
bc092b9
 {
bc092b9
   return GRUB_ARM_MACHINE_TYPE_FDT;
bc092b9
 }
bc092b9
+#elif defined (GRUB_MACHINE_COREBOOT)
bc092b9
+#include <grub/fdtbus.h>
bc092b9
+#include <grub/machine/kernel.h>
bc092b9
+# define LINUX_ADDRESS        (start_of_ram + 0x8000)
bc092b9
+# define LINUX_INITRD_ADDRESS (start_of_ram + 0x02000000)
bc092b9
+# define LINUX_FDT_ADDRESS    (LINUX_INITRD_ADDRESS - 0x10000)
bc092b9
+static inline const void *
bc092b9
+grub_arm_firmware_get_boot_data (void)
bc092b9
+{
bc092b9
+  return grub_fdtbus_get_fdt ();
bc092b9
+}
bc092b9
+static inline grub_uint32_t
bc092b9
+grub_arm_firmware_get_machine_type (void)
bc092b9
+{
bc092b9
+  return GRUB_ARM_MACHINE_TYPE_FDT;
bc092b9
+}
bc092b9
 #endif
bc092b9
 
bc092b9
 #define FDT_ADDITIONAL_ENTRIES_SIZE	0x300
bc092b9
-- 
ec4acbb
2.15.0
bc092b9