]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
Out-of-place FDT handling
authorVladimir Serbinenko <phcoder@gmail.com>
Sun, 21 Feb 2016 19:26:40 +0000 (20:26 +0100)
committerVladimir Serbinenko <phcoder@gmail.com>
Sun, 21 Feb 2016 19:27:28 +0000 (20:27 +0100)
grub-core/kern/arm/uboot/init.c
grub-core/loader/arm/linux.c
include/grub/arm/linux.h
include/grub/uboot/uboot.h

index 2a6aa3fdd3dd049848015f6c67dfc0f30a79a9c4..c98e0e029995d3e9a8a63d633d25ddca261f7dbd 100644 (file)
@@ -29,10 +29,10 @@ grub_uboot_get_machine_type (void)
   return grub_arm_saved_registers.r[1];
 }
 
-grub_addr_t
+const void *
 grub_uboot_get_boot_data (void)
 {
-  return grub_arm_saved_registers.r[2];
+  return (const void *) grub_arm_saved_registers.r[2];
 }
 
 int
index 5b39f02bb2e592d85639891db6eb68a97ce12dfa..260cbf06861e7471d9e872ba60375fdfb44d0eec 100644 (file)
@@ -42,7 +42,7 @@ static grub_size_t linux_size;
 static char *linux_args;
 
 static grub_uint32_t machine_type;
-static void *fdt_addr;
+static const void *current_fdt;
 
 typedef void (*kernel_entry_t) (int, unsigned long, void *);
 
@@ -54,9 +54,9 @@ typedef void (*kernel_entry_t) (int, unsigned long, void *);
 #define LINUX_FDT_PHYS_OFFSET    (LINUX_INITRD_PHYS_OFFSET - 0x10000)
 
 static grub_size_t
-get_atag_size (grub_uint32_t *atag)
+get_atag_size (const grub_uint32_t *atag)
 {
-  grub_uint32_t *atag0 = atag;
+  const grub_uint32_t *atag0 = atag;
   while (atag[0] && atag[1])
     atag += atag[0];
   return atag - atag0;
@@ -68,10 +68,11 @@ get_atag_size (grub_uint32_t *atag)
  *   Merges in command line parameters and sets up initrd addresses.
  */
 static grub_err_t
-linux_prepare_atag (void)
+linux_prepare_atag (void *target_atag)
 {
-  grub_uint32_t *atag_orig = (grub_uint32_t *) fdt_addr;
-  grub_uint32_t *tmp_atag, *from, *to;
+  const grub_uint32_t *atag_orig = (const grub_uint32_t *) current_fdt;
+  grub_uint32_t *tmp_atag, *to;
+  const grub_uint32_t *from;
   grub_size_t tmp_size;
   grub_size_t arg_size = grub_strlen (linux_args);
   char *cmdline_orig = NULL;
@@ -142,7 +143,7 @@ linux_prepare_atag (void)
   to += 2;
 
   /* Copy updated FDT to its launch location */
-  grub_memcpy (atag_orig, tmp_atag, sizeof (grub_uint32_t) * (to - tmp_atag));
+  grub_memcpy (target_atag, tmp_atag, sizeof (grub_uint32_t) * (to - tmp_atag));
   grub_free (tmp_atag);
 
   grub_dprintf ("loader", "ATAG updated for Linux boot\n");
@@ -156,19 +157,19 @@ linux_prepare_atag (void)
  *   Merges in command line parameters and sets up initrd addresses.
  */
 static grub_err_t
-linux_prepare_fdt (void)
+linux_prepare_fdt (void *target_fdt)
 {
   int node;
   int retval;
   int tmp_size;
   void *tmp_fdt;
 
-  tmp_size = grub_fdt_get_totalsize (fdt_addr) + 0x100 + grub_strlen (linux_args);
+  tmp_size = grub_fdt_get_totalsize (current_fdt) + 0x100 + grub_strlen (linux_args);
   tmp_fdt = grub_malloc (tmp_size);
   if (!tmp_fdt)
     return grub_errno;
 
-  grub_memcpy (tmp_fdt, fdt_addr, grub_fdt_get_totalsize (fdt_addr));
+  grub_memcpy (tmp_fdt, current_fdt, grub_fdt_get_totalsize (current_fdt));
   grub_fdt_set_totalsize (tmp_fdt, tmp_size);
 
   /* Find or create '/chosen' node */
@@ -209,7 +210,7 @@ linux_prepare_fdt (void)
     }
 
   /* Copy updated FDT to its launch location */
-  grub_memcpy (fdt_addr, tmp_fdt, tmp_size);
+  grub_memcpy (target_fdt, tmp_fdt, tmp_size);
   grub_free (tmp_fdt);
 
   grub_dprintf ("loader", "FDT updated for Linux boot\n");
@@ -226,16 +227,17 @@ linux_boot (void)
 {
   kernel_entry_t linuxmain;
   int fdt_valid, atag_valid;
+  void *target_fdt = 0;
 
-  fdt_valid = (fdt_addr && grub_fdt_check_header_nosize (fdt_addr) == 0);
-  atag_valid = ((((grub_uint16_t *) fdt_addr)[3] & ~3) == 0x5440
-               && *((grub_uint32_t *) fdt_addr));
+  fdt_valid = (current_fdt && grub_fdt_check_header_nosize (current_fdt) == 0);
+  atag_valid = ((((const grub_uint16_t *) current_fdt)[3] & ~3) == 0x5440
+               && *((const grub_uint32_t *) current_fdt));
   grub_dprintf ("loader", "atag: %p, %x, %x, %s, %s\n",
-               fdt_addr,
-               ((grub_uint16_t *) fdt_addr)[3],
-               *((grub_uint32_t *) fdt_addr),
-               (char *) fdt_addr,
-               (char *) fdt_addr + 1);
+               current_fdt,
+               ((const grub_uint16_t *) current_fdt)[3],
+               *((const grub_uint32_t *) current_fdt),
+               (const char *) current_fdt,
+               (const char *) current_fdt + 1);
 
   if (!fdt_valid && machine_type == GRUB_ARM_MACHINE_TYPE_FDT)
     return grub_error (GRUB_ERR_FILE_NOT_FOUND,
@@ -245,23 +247,40 @@ linux_boot (void)
 
   grub_dprintf ("loader", "Kernel at: 0x%x\n", linux_addr);
 
+  if (fdt_valid || atag_valid)
+    {
+#ifdef GRUB_MACHINE_EFI
+      grub_size_t size;
+      if (fdt_valid)
+       size = grub_fdt_get_totalsize (fdt_addr);
+      else
+       size = 4 * get_atag_size (atag_orig);
+      size += grub_strlen (linux_args) + 256;
+      target_fdt = grub_efi_allocate_loader_memory (LINUX_FDT_PHYS_OFFSET, size);
+      if (!fdt_addr)
+       return grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
+#else
+      target_fdt = (void *) LINUX_FDT_ADDRESS;
+#endif
+    }
+
   if (fdt_valid)
     {
       grub_err_t err;
 
-      err = linux_prepare_fdt ();
+      err = linux_prepare_fdt (target_fdt);
       if (err)
        return err;
-      grub_dprintf ("loader", "FDT @ 0x%p\n", fdt_addr);
+      grub_dprintf ("loader", "FDT @ %p\n", target_fdt);
     }
   else if (atag_valid)
     {
       grub_err_t err;
 
-      err = linux_prepare_atag ();
+      err = linux_prepare_atag (target_fdt);
       if (err)
        return err;
-      grub_dprintf ("loader", "ATAG @ 0x%p\n", fdt_addr);
+      grub_dprintf ("loader", "ATAG @ %p\n", target_fdt);
     }
 
   grub_dprintf ("loader", "Jumping to Linux...\n");
@@ -285,7 +304,7 @@ linux_boot (void)
 
   grub_arm_disable_caches_mmu ();
 
-  linuxmain (0, machine_type, fdt_addr);
+  linuxmain (0, machine_type, target_fdt);
 
   return grub_error (GRUB_ERR_BAD_OS, "Linux call returned");
 }
@@ -444,11 +463,26 @@ fail:
 static grub_err_t
 load_dtb (grub_file_t dtb, int size)
 {
-  if ((grub_file_read (dtb, fdt_addr, size) != size)
-      || (grub_fdt_check_header (fdt_addr, size) != 0))
-    return grub_error (GRUB_ERR_BAD_OS, N_("invalid device tree"));
+  void *new_fdt = grub_zalloc (size);
+  if (!new_fdt)
+    return grub_errno;
+  grub_dprintf ("loader", "Loading device tree to %p\n",
+               new_fdt);
+  if ((grub_file_read (dtb, new_fdt, size) != size)
+      || (grub_fdt_check_header (new_fdt, size) != 0))
+    {
+      grub_free (new_fdt);
+      return grub_error (GRUB_ERR_BAD_OS, N_("invalid device tree"));
+    }
+
+  grub_fdt_set_totalsize (new_fdt, size);
+  current_fdt = new_fdt;
+  /* 
+   * We've successfully loaded an FDT, so any machine type passed
+   * from firmware is now obsolete.
+   */
+  machine_type = GRUB_ARM_MACHINE_TYPE_FDT;
 
-  grub_fdt_set_totalsize (fdt_addr, size);
   return GRUB_ERR_NONE;
 }
 
@@ -464,42 +498,13 @@ grub_cmd_devicetree (grub_command_t cmd __attribute__ ((unused)),
 
   dtb = grub_file_open (argv[0]);
   if (!dtb)
-    goto out;
+    return grub_errno;
 
   size = grub_file_size (dtb);
   if (size == 0)
-    {
-      grub_error (GRUB_ERR_BAD_OS, "empty file");
-      goto out;
-    }
-
-#ifdef GRUB_MACHINE_EFI
-  fdt_addr = grub_efi_allocate_loader_memory (LINUX_FDT_PHYS_OFFSET, size);
-  if (!fdt_addr)
-    {
-      grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
-      goto out;
-    }
-#else
-  fdt_addr = (void *) LINUX_FDT_ADDRESS;
-#endif
-
-  grub_dprintf ("loader", "Loading device tree to 0x%08x\n",
-               (grub_addr_t) fdt_addr);
-  load_dtb (dtb, size);
-  if (grub_errno != GRUB_ERR_NONE)
-    {
-      fdt_addr = NULL;
-      goto out;
-    }
-
-  /* 
-   * We've successfully loaded an FDT, so any machine type passed
-   * from firmware is now obsolete.
-   */
-  machine_type = GRUB_ARM_MACHINE_TYPE_FDT;
-
- out:
+    grub_error (GRUB_ERR_BAD_OS, "empty file");
+  else
+    load_dtb (dtb, size);
   grub_file_close (dtb);
 
   return grub_errno;
@@ -517,7 +522,7 @@ GRUB_MOD_INIT (linux)
                                          /* TRANSLATORS: DTB stands for device tree blob.  */
                                          0, N_("Load DTB file."));
   my_mod = mod;
-  fdt_addr = (void *) grub_arm_firmware_get_boot_data ();
+  current_fdt = grub_arm_firmware_get_boot_data ();
   machine_type = grub_arm_firmware_get_machine_type ();
 }
 
index 2d4bc5770685f5abe59963929e42d24e840361f6..1ae161d07063e4b50d1048c0735f433c8f7d810e 100644 (file)
@@ -40,7 +40,7 @@
 # define LINUX_PHYS_OFFSET        (0x00008000)
 # define LINUX_INITRD_PHYS_OFFSET (LINUX_PHYS_OFFSET + 0x02000000)
 # define LINUX_FDT_PHYS_OFFSET    (LINUX_INITRD_PHYS_OFFSET - 0x10000)
-static inline grub_addr_t
+static inline const void *
 grub_arm_firmware_get_boot_data (void)
 {
   return 0;
index c122de6abf68a8ecff6f62c230e93ec005551a3e..5ca1a7f635b6c64e0ec168862b810a653f4932c1 100644 (file)
@@ -37,7 +37,7 @@ grub_err_t grub_uboot_probe_hardware (void);
 extern grub_addr_t EXPORT_VAR (start_of_ram);
 
 grub_uint32_t EXPORT_FUNC (grub_uboot_get_machine_type) (void);
-grub_addr_t EXPORT_FUNC (grub_uboot_get_boot_data) (void);
+const void *EXPORT_FUNC (grub_uboot_get_boot_data) (void);
 
 
 /*