Out-of-place FDT handling

This commit is contained in:
Vladimir Serbinenko committed 2016-02-21 20:27:28 +01:00
1 parent 0f3ae7324a
commit 192243b13e
4 files changed
+71 -66

No files matched your search

+2 -2
View File
@@ -29,10 +29,10 @@ grub_uboot_get_machine_type (void)
return grub_arm_saved_registers.r[1]; return grub_arm_saved_registers.r[1];
} }
grub_addr_t const void *
grub_uboot_get_boot_data (void) grub_uboot_get_boot_data (void)
{ {
return grub_arm_saved_registers.r[2]; return (const void *) grub_arm_saved_registers.r[2];
} }
int int
+67 -62
View File
@@ -42,7 +42,7 @@ static grub_size_t linux_size;
static char *linux_args; static char *linux_args;
static grub_uint32_t machine_type; static grub_uint32_t machine_type;
static void *fdt_addr; static const void *current_fdt;
typedef void (*kernel_entry_t) (int, unsigned long, void *); 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) #define LINUX_FDT_PHYS_OFFSET (LINUX_INITRD_PHYS_OFFSET - 0x10000)
static grub_size_t 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]) while (atag[0] && atag[1])
atag += atag[0]; atag += atag[0];
return atag - atag0; return atag - atag0;
@@ -68,10 +68,11 @@ get_atag_size (grub_uint32_t *atag)
* Merges in command line parameters and sets up initrd addresses. * Merges in command line parameters and sets up initrd addresses.
*/ */
static grub_err_t static grub_err_t
linux_prepare_atag (void) linux_prepare_atag (void *target_atag)
{ {
grub_uint32_t *atag_orig = (grub_uint32_t *) fdt_addr; const grub_uint32_t *atag_orig = (const grub_uint32_t *) current_fdt;
grub_uint32_t *tmp_atag, *from, *to; grub_uint32_t *tmp_atag, *to;
const grub_uint32_t *from;
grub_size_t tmp_size; grub_size_t tmp_size;
grub_size_t arg_size = grub_strlen (linux_args); grub_size_t arg_size = grub_strlen (linux_args);
char *cmdline_orig = NULL; char *cmdline_orig = NULL;
@@ -142,7 +143,7 @@ linux_prepare_atag (void)
to += 2; to += 2;
/* Copy updated FDT to its launch location */ /* 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_free (tmp_atag);
grub_dprintf ("loader", "ATAG updated for Linux boot\n"); 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. * Merges in command line parameters and sets up initrd addresses.
*/ */
static grub_err_t static grub_err_t
linux_prepare_fdt (void) linux_prepare_fdt (void *target_fdt)
{ {
int node; int node;
int retval; int retval;
int tmp_size; int tmp_size;
void *tmp_fdt; 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); tmp_fdt = grub_malloc (tmp_size);
if (!tmp_fdt) if (!tmp_fdt)
return grub_errno; 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); grub_fdt_set_totalsize (tmp_fdt, tmp_size);
/* Find or create '/chosen' node */ /* Find or create '/chosen' node */
@@ -209,7 +210,7 @@ linux_prepare_fdt (void)
} }
/* Copy updated FDT to its launch location */ /* 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_free (tmp_fdt);
grub_dprintf ("loader", "FDT updated for Linux boot\n"); grub_dprintf ("loader", "FDT updated for Linux boot\n");
@@ -226,16 +227,17 @@ linux_boot (void)
{ {
kernel_entry_t linuxmain; kernel_entry_t linuxmain;
int fdt_valid, atag_valid; int fdt_valid, atag_valid;
void *target_fdt = 0;
fdt_valid = (fdt_addr && grub_fdt_check_header_nosize (fdt_addr) == 0); fdt_valid = (current_fdt && grub_fdt_check_header_nosize (current_fdt) == 0);
atag_valid = ((((grub_uint16_t *) fdt_addr)[3] & ~3) == 0x5440 atag_valid = ((((const grub_uint16_t *) current_fdt)[3] & ~3) == 0x5440
&& *((grub_uint32_t *) fdt_addr)); && *((const grub_uint32_t *) current_fdt));
grub_dprintf ("loader", "atag: %p, %x, %x, %s, %s\n", grub_dprintf ("loader", "atag: %p, %x, %x, %s, %s\n",
fdt_addr, current_fdt,
((grub_uint16_t *) fdt_addr)[3], ((const grub_uint16_t *) current_fdt)[3],
*((grub_uint32_t *) fdt_addr), *((const grub_uint32_t *) current_fdt),
(char *) fdt_addr, (const char *) current_fdt,
(char *) fdt_addr + 1); (const char *) current_fdt + 1);
if (!fdt_valid && machine_type == GRUB_ARM_MACHINE_TYPE_FDT) if (!fdt_valid && machine_type == GRUB_ARM_MACHINE_TYPE_FDT)
return grub_error (GRUB_ERR_FILE_NOT_FOUND, 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); 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) if (fdt_valid)
{ {
grub_err_t err; grub_err_t err;
err = linux_prepare_fdt (); err = linux_prepare_fdt (target_fdt);
if (err) if (err)
return err; return err;
grub_dprintf ("loader", "FDT @ 0x%p\n", fdt_addr); grub_dprintf ("loader", "FDT @ %p\n", target_fdt);
} }
else if (atag_valid) else if (atag_valid)
{ {
grub_err_t err; grub_err_t err;
err = linux_prepare_atag (); err = linux_prepare_atag (target_fdt);
if (err) if (err)
return 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"); grub_dprintf ("loader", "Jumping to Linux...\n");
@@ -285,7 +304,7 @@ linux_boot (void)
grub_arm_disable_caches_mmu (); 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"); return grub_error (GRUB_ERR_BAD_OS, "Linux call returned");
} }
@@ -444,11 +463,26 @@ fail:
static grub_err_t static grub_err_t
load_dtb (grub_file_t dtb, int size) load_dtb (grub_file_t dtb, int size)
{ {
if ((grub_file_read (dtb, fdt_addr, size) != size) void *new_fdt = grub_zalloc (size);
|| (grub_fdt_check_header (fdt_addr, size) != 0)) if (!new_fdt)
return grub_error (GRUB_ERR_BAD_OS, N_("invalid device tree")); 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; return GRUB_ERR_NONE;
} }
@@ -464,42 +498,13 @@ grub_cmd_devicetree (grub_command_t cmd __attribute__ ((unused)),
dtb = grub_file_open (argv[0]); dtb = grub_file_open (argv[0]);
if (!dtb) if (!dtb)
goto out; return grub_errno;
size = grub_file_size (dtb); size = grub_file_size (dtb);
if (size == 0) if (size == 0)
{ grub_error (GRUB_ERR_BAD_OS, "empty file");
grub_error (GRUB_ERR_BAD_OS, "empty file"); else
goto out; load_dtb (dtb, size);
}
#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_file_close (dtb); grub_file_close (dtb);
return grub_errno; return grub_errno;
@@ -517,7 +522,7 @@ GRUB_MOD_INIT (linux)
/* TRANSLATORS: DTB stands for device tree blob. */ /* TRANSLATORS: DTB stands for device tree blob. */
0, N_("Load DTB file.")); 0, N_("Load DTB file."));
my_mod = mod; 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 (); machine_type = grub_arm_firmware_get_machine_type ();
} }
+1 -1
View File
@@ -40,7 +40,7 @@
# define LINUX_PHYS_OFFSET (0x00008000) # define LINUX_PHYS_OFFSET (0x00008000)
# define LINUX_INITRD_PHYS_OFFSET (LINUX_PHYS_OFFSET + 0x02000000) # define LINUX_INITRD_PHYS_OFFSET (LINUX_PHYS_OFFSET + 0x02000000)
# define LINUX_FDT_PHYS_OFFSET (LINUX_INITRD_PHYS_OFFSET - 0x10000) # 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) grub_arm_firmware_get_boot_data (void)
{ {
return 0; return 0;
+1 -1
View File
@@ -37,7 +37,7 @@ grub_err_t grub_uboot_probe_hardware (void);
extern grub_addr_t EXPORT_VAR (start_of_ram); extern grub_addr_t EXPORT_VAR (start_of_ram);
grub_uint32_t EXPORT_FUNC (grub_uboot_get_machine_type) (void); 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);
/* /*