Own fdt parsing implementation

This commit is contained in:
Francesco Lavra 2013-05-17 13:45:22 +02:00 committed by Vladimir 'phcoder' Serbinenko
parent 470038745c
commit c6a8472baf
4 changed files with 587 additions and 104 deletions

View file

@ -1536,11 +1536,10 @@ module = {
sparc64_ieee1275 = loader/sparc64/ieee1275/linux.c;
ia64_efi = loader/ia64/efi/linux.c;
arm = loader/arm/linux.c;
arm = lib/fdt.c;
common = loader/linux.c;
common = lib/cmdline.c;
enable = noemu;
fdt_cppflags = '$(CPPFLAGS_LIBFDT)';
};
module = {

389
grub-core/lib/fdt.c Normal file
View file

@ -0,0 +1,389 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2013 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/fdt.h>
#include <grub/misc.h>
#include <grub/mm.h>
#define FDT_SUPPORTED_VERSION 17
#define FDT_BEGIN_NODE 0x00000001
#define FDT_END_NODE 0x00000002
#define FDT_PROP 0x00000003
#define FDT_NOP 0x00000004
#define FDT_END 0x00000009
#define struct_end(fdt) \
((grub_addr_t) fdt + grub_fdt_get_off_dt_struct(fdt) \
+ grub_fdt_get_size_dt_struct(fdt))
/* Size needed by a node entry: 2 tokens (FDT_BEGIN_NODE and FDT_END_NODE), plus
the NULL-terminated string containing the name, plus padding if needed. */
#define node_entry_size(node_name) \
(2 * sizeof(grub_uint32_t) \
+ ALIGN_UP (grub_strlen (name) + 1, sizeof(grub_uint32_t)))
/* Size needed by a property entry: 1 token (FDT_PROPERTY), plus len and nameoff
fields, plus the property value, plus padding if needed. */
#define prop_entry_size(prop_len) \
(3 * sizeof(grub_uint32_t) + ALIGN_UP(prop_len, sizeof(grub_uint32_t)))
static grub_uint32_t *get_next_node (const void *fdt, char *node_name)
{
grub_uint32_t *end = (void *) struct_end (fdt);
grub_uint32_t *token;
if (node_name >= (char *) end)
return NULL;
while (*node_name)
{
if (++node_name >= (char *) end)
return NULL;
}
token = (grub_uint32_t *) ALIGN_UP ((grub_addr_t) node_name, 4);
while (token < end)
{
switch (grub_be_to_cpu32(*token))
{
case FDT_BEGIN_NODE:
token = get_next_node (fdt, (char *) (token + 1));
if (!token)
return NULL;
break;
case FDT_END_NODE:
token++;
if (token >= end)
return NULL;
return token;
case FDT_PROP:
/* Skip property token and following data (len, nameoff and property
value). */
token += 3 + grub_be_to_cpu32 (*(token + 1));
break;
case FDT_NOP:
token++;
break;
default:
return NULL;
}
}
return NULL;
}
static int get_mem_rsvmap_size (const void *fdt)
{
int size = 0;
grub_uint64_t *ptr = (void *) ((grub_addr_t) fdt
+ grub_fdt_get_off_mem_rsvmap (fdt));
do
{
size += 2 * sizeof(*ptr);
if (!*ptr && !*(ptr + 1))
return size;
ptr += 2;
} while ((grub_addr_t) ptr <= (grub_addr_t) fdt + grub_fdt_get_totalsize (fdt)
- 2 * sizeof(grub_uint64_t));
return -1;
}
static grub_uint32_t get_free_space (void *fdt)
{
int mem_rsvmap_size = get_mem_rsvmap_size (fdt);
if (mem_rsvmap_size < 0)
/* invalid memory reservation block */
return 0;
return (grub_fdt_get_totalsize (fdt) - sizeof(grub_fdt_header_t)
- mem_rsvmap_size - grub_fdt_get_size_dt_strings (fdt)
- grub_fdt_get_size_dt_struct (fdt));
}
static int add_subnode (void *fdt, int parentoffset, const char *name)
{
grub_uint32_t *begin = (void *) ((grub_addr_t) fdt
+ grub_fdt_get_off_dt_struct(fdt)
+ parentoffset);
grub_uint32_t *end = (void *) struct_end (fdt);
unsigned int entry_size = node_entry_size (name);
grub_uint32_t *token = begin;
/* Insert the new subnode just after the properties of the parent node (if
any).*/
while (1)
{
if (token >= end)
return -1;
switch (grub_be_to_cpu32(*token))
{
case FDT_PROP:
/* Skip len and nameoff. */
token += 2;
break;
case FDT_BEGIN_NODE:
case FDT_END_NODE:
goto insert;
case FDT_NOP:
break;
default:
/* invalid token */
return -1;
}
token++;
}
insert:
grub_memmove (token + entry_size, token,
(grub_addr_t) end - (grub_addr_t) token);
*token = grub_cpu_to_be32(FDT_BEGIN_NODE);
token[entry_size / sizeof(*token) - 2] = 0; /* padding bytes */
grub_strcpy((char *) (token + 1), name);
token += entry_size / sizeof(*token) - 1;
*token = grub_cpu_to_be32(FDT_END_NODE);
return ((grub_addr_t) token - (grub_addr_t) fdt
- grub_fdt_get_off_dt_struct(fdt));
}
/* Rearrange FDT blocks in the canonical order: first the memory reservation
block (just after the FDT header), then the structure block and finally the
strings block. No free space is left between the first and the second block,
while the space between the second and the third block is given by the
clearance argument. */
static int rearrange_blocks (void *fdt, unsigned int clearance)
{
grub_uint32_t off_mem_rsvmap = ALIGN_UP(sizeof(grub_fdt_header_t), 8);
grub_uint32_t off_dt_struct = off_mem_rsvmap + get_mem_rsvmap_size (fdt);
grub_uint32_t off_dt_strings = off_dt_struct
+ grub_fdt_get_size_dt_struct (fdt)
+ clearance;
grub_uint8_t *fdt_ptr = fdt;
grub_uint8_t *tmp_fdt;
if ((grub_fdt_get_off_mem_rsvmap (fdt) == off_mem_rsvmap)
&& (grub_fdt_get_off_dt_struct (fdt) == off_dt_struct))
{
/* No need to allocate memory for a temporary FDT, just move the strings
block if needed. */
if (grub_fdt_get_off_dt_strings (fdt) != off_dt_strings)
grub_memmove(fdt_ptr + off_dt_strings,
fdt_ptr + grub_fdt_get_off_dt_strings (fdt),
grub_fdt_get_size_dt_strings (fdt));
return 0;
}
tmp_fdt = grub_malloc (grub_fdt_get_totalsize (fdt));
if (!tmp_fdt)
return -1;
grub_memcpy (tmp_fdt + off_mem_rsvmap,
fdt_ptr + grub_fdt_get_off_mem_rsvmap (fdt),
get_mem_rsvmap_size (fdt));
grub_fdt_set_off_mem_rsvmap (fdt, off_mem_rsvmap);
grub_memcpy (tmp_fdt + off_dt_struct,
fdt_ptr + grub_fdt_get_off_dt_struct (fdt),
grub_fdt_get_size_dt_struct (fdt));
grub_fdt_set_off_dt_struct (fdt, off_dt_struct);
grub_memcpy (tmp_fdt + off_dt_strings,
fdt_ptr + grub_fdt_get_off_dt_strings (fdt),
grub_fdt_get_size_dt_strings (fdt));
grub_fdt_set_off_dt_strings (fdt, off_dt_strings);
/* Copy reordered blocks back to fdt. */
memcpy (fdt_ptr + off_mem_rsvmap, tmp_fdt + off_mem_rsvmap,
grub_fdt_get_totalsize (fdt) - off_mem_rsvmap);
grub_free(tmp_fdt);
return 0;
}
static grub_uint32_t *find_prop (void *fdt, unsigned int nodeoffset,
const char *name)
{
grub_uint32_t *prop = (void *) ((grub_addr_t) fdt
+ grub_fdt_get_off_dt_struct (fdt)
+ nodeoffset);
grub_uint32_t nameoff;
do
{
if (grub_be_to_cpu32(*prop) == FDT_PROP)
{
nameoff = grub_be_to_cpu32(*(prop + 2));
if ((nameoff + grub_strlen (name) < grub_fdt_get_size_dt_strings (fdt))
&& !grub_strcmp (name, (char *) fdt +
grub_fdt_get_off_dt_strings (fdt) + nameoff))
return prop;
prop += prop_entry_size(grub_be_to_cpu32(*prop + 1)) / sizeof (*prop);
}
else if (grub_be_to_cpu32(*prop) != FDT_NOP)
return NULL;
prop++;
} while ((grub_addr_t) prop < ((grub_addr_t) fdt
+ grub_fdt_get_off_dt_struct (fdt)
+ grub_fdt_get_size_dt_struct (fdt)));
return NULL;
}
/* Check the FDT header for consistency and adjust the totalsize field to match
the size allocated for the FDT; if this function is called before the other
functions in this file and returns success, the other functions are
guaranteed not to access memory locations outside the allocated memory. */
int grub_fdt_check_header (void *fdt, unsigned int size)
{
if (((grub_addr_t) fdt & 0x7) || (grub_fdt_get_magic (fdt) != FDT_MAGIC)
|| (grub_fdt_get_totalsize (fdt) > size)
|| (grub_fdt_get_version (fdt) < FDT_SUPPORTED_VERSION)
|| (grub_fdt_get_last_comp_version (fdt) > FDT_SUPPORTED_VERSION)
|| (grub_fdt_get_off_dt_struct (fdt) & 0x00000003)
|| (grub_fdt_get_size_dt_struct (fdt) & 0x00000003)
|| (grub_fdt_get_off_dt_struct (fdt) + grub_fdt_get_size_dt_struct (fdt)
> grub_fdt_get_totalsize (fdt))
|| (grub_fdt_get_off_dt_strings (fdt) + grub_fdt_get_size_dt_strings (fdt)
> grub_fdt_get_totalsize (fdt))
|| (grub_fdt_get_off_mem_rsvmap (fdt) & 0x00000007)
|| (grub_fdt_get_off_mem_rsvmap (fdt)
> grub_fdt_get_totalsize (fdt) - 2 * sizeof(grub_uint64_t)))
return -1;
return 0;
}
/* Find a direct sub-node of a given parent node. */
int grub_fdt_find_subnode (const void *fdt, unsigned int parentoffset,
const char *name)
{
grub_uint32_t *token, *end;
char *node_name;
if (parentoffset & 0x3)
return -1;
token = (void *) ((grub_addr_t) fdt + grub_fdt_get_off_dt_struct(fdt)
+ parentoffset);
end = (void *) struct_end (fdt);
while (token < end)
{
switch (grub_be_to_cpu32(*token))
{
case FDT_BEGIN_NODE:
node_name = (char *) (token + 1);
if (node_name + grub_strlen (name) >= (char *) end)
return -1;
if (!grub_strcmp (node_name, name))
return (int) ((grub_addr_t) token
+ ALIGN_UP(grub_strlen (name) + 1, 4)
- grub_fdt_get_off_dt_struct (fdt));
token = get_next_node (fdt, node_name);
if (!token)
return -1;
break;
case FDT_END_NODE:
return -1;
case FDT_PROP:
/* Skip property token and following data (len, nameoff and property
value). */
token += 3 + grub_be_to_cpu32 (*(token + 1));
break;
case FDT_NOP:
token++;
break;
default:
return -1;
}
}
return -1;
}
int grub_fdt_add_subnode (void *fdt, unsigned int parentoffset,
const char *name)
{
unsigned int entry_size = node_entry_size(name);
if ((parentoffset & 0x3) || (get_free_space (fdt) < entry_size))
return -1;
/* The new node entry will increase the size of the structure block: rearrange
blocks such that there is sufficient free space between the structure and
the strings block, then add the new node entry. */
if (rearrange_blocks (fdt, entry_size) < 0)
return -1;
return add_subnode (fdt, parentoffset, name);
}
int grub_fdt_set_prop (void *fdt, unsigned int nodeoffset, const char *name,
const void *val, grub_uint32_t len)
{
grub_uint32_t *prop;
int prop_name_present = 0;
grub_uint32_t nameoff = 0;
if ((nodeoffset >= grub_fdt_get_size_dt_struct (fdt)) || (nodeoffset & 0x3))
return -1;
prop = find_prop (fdt, nodeoffset, name);
if (prop)
{
grub_uint32_t prop_len = ALIGN_UP(grub_be_to_cpu32 (*(prop + 1)),
sizeof(grub_uint32_t));
grub_uint32_t i;
prop_name_present = 1;
for (i = 0; i < prop_len / sizeof(grub_uint32_t); i++)
*(prop + 3 + i) = grub_cpu_to_be32 (FDT_NOP);
if (len > prop_len)
{
/* Length of new property value is greater than the space allocated
for the current value: a new entry needs to be created, so save the
nameoff field of the current entry and replace the current entry
with NOP tokens. */
nameoff = grub_be_to_cpu32 (*(prop + 2));
*prop = *(prop + 1) = *(prop + 2) = grub_cpu_to_be32 (FDT_NOP);
prop = NULL;
}
}
if (!prop || !prop_name_present) {
unsigned int needed_space = 0;
if (!prop)
needed_space = prop_entry_size(len);
if (!prop_name_present)
needed_space += grub_strlen (name) + 1;
if (needed_space > get_free_space (fdt))
return -1;
if (rearrange_blocks (fdt, !prop ? prop_entry_size(len) : 0) < 0)
return -1;
}
if (!prop_name_present) {
/* Append the property name at the end of the strings block. */
nameoff = grub_fdt_get_size_dt_strings (fdt);
grub_strcpy ((char *) fdt + grub_fdt_get_off_dt_strings (fdt) + nameoff,
name);
grub_fdt_set_size_dt_strings (fdt, grub_fdt_get_size_dt_strings (fdt)
+ grub_strlen (name) + 1);
}
if (!prop) {
prop = (void *) ((grub_addr_t) fdt + grub_fdt_get_off_dt_struct (fdt)
+ nodeoffset);
grub_memmove (prop + prop_entry_size(len), prop,
grub_fdt_get_size_dt_struct (fdt) - nodeoffset);
*prop = grub_cpu_to_be32 (FDT_PROP);
*(prop + 1) = grub_cpu_to_be32 (len);
*(prop + 2) = grub_cpu_to_be32 (nameoff);
/* Insert padding bytes at the end of the value; if they are not needed,
they will be overwritten by the follozing memcpy. */
*(prop + prop_entry_size(len) / sizeof(grub_uint32_t) - 1) = 0;
grub_memcpy (prop + 3, val, len);
}
return 0;
}

View file

@ -18,6 +18,7 @@
*/
#include <grub/dl.h>
#include <grub/fdt.h>
#include <grub/file.h>
#include <grub/loader.h>
#include <grub/mm.h>
@ -27,23 +28,29 @@
#include <grub/cpu/linux.h>
#include <grub/lib/cmdline.h>
#include <libfdt.h>
GRUB_MOD_LICENSE ("GPLv3+");
static grub_dl_t my_mod;
static grub_addr_t initrd_start;
static grub_size_t initrd_end;
static grub_addr_t initrd_end;
static grub_addr_t linux_addr;
static grub_size_t linux_size;
static char *linux_args;
static grub_addr_t firmware_boot_data;
static grub_addr_t boot_data;
static grub_uint32_t machine_type;
static void *fdt_addr;
#define LINUX_ZIMAGE_OFFSET 0x24
#define LINUX_ZIMAGE_MAGIC 0x016f2818
#define ARM_FDT_MACHINE_TYPE 0xFFFFFFFF
#define LINUX_PHYS_OFFSET (0x00008000)
#define LINUX_INITRD_PHYS_OFFSET (LINUX_PHYS_OFFSET + 0x02000000)
#define LINUX_FDT_PHYS_OFFSET (LINUX_INITRD_PHYS_OFFSET - 0x10000)
/*
* linux_prepare_fdt():
@ -58,19 +65,20 @@ linux_prepare_fdt (void)
int tmp_size;
void *tmp_fdt;
tmp_size = fdt_totalsize ((void *) boot_data) + FDT_ADDITIONAL_ENTRIES_SIZE;
tmp_size = grub_fdt_get_totalsize (fdt_addr) + 0x100 + grub_strlen (linux_args);
tmp_fdt = grub_malloc (tmp_size);
if (!tmp_fdt)
return GRUB_ERR_OUT_OF_MEMORY;
return grub_errno;
fdt_open_into ((void *) boot_data, tmp_fdt, tmp_size);
grub_memcpy (tmp_fdt, fdt_addr, grub_fdt_get_totalsize (fdt_addr));
grub_fdt_set_totalsize (tmp_fdt, tmp_size);
/* Find or create '/chosen' node */
node = fdt_subnode_offset (tmp_fdt, 0, "chosen");
node = grub_fdt_find_subnode (tmp_fdt, 0, "chosen");
if (node < 0)
{
grub_printf ("No 'chosen' node in FDT - creating.\n");
node = fdt_add_subnode (tmp_fdt, 0, "chosen");
node = grub_fdt_add_subnode (tmp_fdt, 0, "chosen");
if (node < 0)
goto failure;
}
@ -78,8 +86,8 @@ linux_prepare_fdt (void)
grub_printf ("linux_args: '%s'\n", linux_args);
/* Generate and set command line */
retval = fdt_setprop (tmp_fdt, node, "bootargs", linux_args,
grub_strlen (linux_args) + 1);
retval = grub_fdt_set_prop (tmp_fdt, node, "bootargs", linux_args,
grub_strlen (linux_args) + 1);
if (retval)
goto failure;
@ -89,26 +97,22 @@ linux_prepare_fdt (void)
* We're using physical addresses, so even if we have LPAE, we're
* restricted to a 32-bit address space.
*/
grub_uint32_t fdt_initrd_start = cpu_to_fdt32 (initrd_start);
grub_uint32_t fdt_initrd_end = cpu_to_fdt32 (initrd_end);
grub_dprintf ("loader", "Initrd @ 0x%08x-0x%08x\n",
initrd_start, initrd_end);
retval = fdt_setprop (tmp_fdt, node, "linux,initrd-start",
&fdt_initrd_start, sizeof (fdt_initrd_start));
retval = grub_fdt_set_prop32 (tmp_fdt, node, "linux,initrd-start",
initrd_start);
if (retval)
goto failure;
retval = fdt_setprop (tmp_fdt, node, "linux,initrd-end",
&fdt_initrd_end, sizeof (fdt_initrd_end));
retval = grub_fdt_set_prop32 (tmp_fdt, node, "linux,initrd-end",
initrd_end);
if (retval)
goto failure;
}
/* Copy updated FDT to its launch location */
fdt_move (tmp_fdt, (void *) boot_data, fdt_totalsize (tmp_fdt));
grub_memcpy (fdt_addr, tmp_fdt, tmp_size);
grub_free (tmp_fdt);
fdt_pack ((void *) boot_data);
grub_dprintf ("loader", "FDT updated for Linux boot\n");
@ -116,52 +120,35 @@ linux_prepare_fdt (void)
failure:
grub_free (tmp_fdt);
return GRUB_ERR_BAD_ARGUMENT;
return grub_error (GRUB_ERR_BAD_ARGUMENT, "unable to prepare FDT");
}
static grub_err_t
linux_boot (void)
{
kernel_entry_t linuxmain;
grub_err_t err = GRUB_ERR_NONE;
grub_err_t err;
if (!fdt_addr && machine_type == ARM_FDT_MACHINE_TYPE)
return grub_error (GRUB_ERR_FILE_NOT_FOUND,
N_("device tree must be supplied"));
grub_arch_sync_caches ((void *) linux_addr, linux_size);
grub_dprintf ("loader", "Kernel at: 0x%x\n", linux_addr);
if (!boot_data)
{
if (firmware_boot_data)
{
grub_printf ("Using firmware-supplied boot data @ 0x%08x\n",
firmware_boot_data);
boot_data = firmware_boot_data;
}
else
{
return GRUB_ERR_FILE_NOT_FOUND;
}
}
grub_dprintf ("loader", "Boot data at: 0x%x\n", boot_data);
if (fdt32_to_cpu (*(grub_uint32_t *) (boot_data)) == FDT_MAGIC)
{
grub_dprintf ("loader", "FDT @ 0x%08x\n", (grub_addr_t) boot_data);
if (linux_prepare_fdt () != GRUB_ERR_NONE)
{
grub_dprintf ("loader", "linux_prepare_fdt() failed\n");
return GRUB_ERR_FILE_NOT_FOUND;
}
}
err = linux_prepare_fdt ();
if (err)
return err;
grub_dprintf ("loader", "FDT @ 0x%p\n", fdt_addr);
grub_dprintf ("loader", "Jumping to Linux...\n");
/* Boot the kernel.
* Arguments to kernel:
* r0 - 0
* r1 - machine type (possibly passed from firmware)
* r2 - address of DTB or ATAG list
* r1 - machine type
* r2 - address of DTB
*/
linuxmain = (kernel_entry_t) linux_addr;
@ -171,7 +158,7 @@ linux_boot (void)
return err;
#endif
linuxmain (0, machine_type, (void *) boot_data);
linuxmain (0, machine_type, fdt_addr);
return err;
}
@ -180,21 +167,18 @@ linux_boot (void)
* Only support zImage, so no relocations necessary
*/
static grub_err_t
linux_load (const char *filename)
linux_load (const char *filename, grub_file_t file)
{
grub_file_t file;
int size;
file = grub_file_open (filename);
if (!file)
return GRUB_ERR_FILE_NOT_FOUND;
size = grub_file_size (file);
if (size == 0)
return GRUB_ERR_FILE_READ_ERROR;
return grub_error (GRUB_ERR_BAD_OS, "empty kernel");
#ifdef GRUB_MACHINE_EFI
linux_addr = (grub_addr_t) grub_efi_allocate_loader_memory (LINUX_PHYS_OFFSET, size);
if (!linux_addr)
return grub_errno;
#else
linux_addr = LINUX_ADDRESS;
#endif
@ -203,8 +187,10 @@ linux_load (const char *filename)
if (grub_file_read (file, (void *) linux_addr, size) != size)
{
grub_printf ("Kernel read failed!\n");
return GRUB_ERR_FILE_READ_ERROR;
if (!grub_errno)
grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
filename);
return grub_errno;
}
if (*(grub_uint32_t *) (linux_addr + LINUX_ZIMAGE_OFFSET)
@ -235,7 +221,8 @@ static grub_err_t
grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
int argc, char *argv[])
{
int size, retval;
int size;
grub_err_t err;
grub_file_t file;
grub_dl_ref (my_mod);
@ -246,17 +233,20 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
if (!file)
goto fail;
retval = linux_load (argv[0]);
err = linux_load (argv[0], file);
grub_file_close (file);
if (retval != GRUB_ERR_NONE)
if (err)
goto fail;
grub_loader_set (linux_boot, linux_unload, 1);
grub_loader_set (linux_boot, linux_unload, 0);
size = grub_loader_cmdline_size (argc, argv);
linux_args = grub_malloc (size + sizeof (LINUX_IMAGE));
if (!linux_args)
goto fail;
{
grub_loader_unset();
goto fail;
}
/* Create kernel command line. */
grub_memcpy (linux_args, LINUX_IMAGE, sizeof (LINUX_IMAGE));
@ -288,16 +278,31 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
if (size == 0)
goto fail;
if (initrd_start)
grub_free ((void *) initrd_start);
#ifdef GRUB_MACHINE_EFI
initrd_start = (grub_addr_t) grub_efi_allocate_loader_memory (LINUX_INITRD_PHYS_OFFSET, size);
if (!initrd_start)
{
grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("memory allocation failed"));
goto fail;
}
#else
initrd_start = LINUX_INITRD_ADDRESS;
#endif
grub_dprintf ("loader", "Loading initrd to 0x%08x\n",
(grub_addr_t) initrd_start);
if (grub_file_read (file, (void *) initrd_start, size) != size)
goto fail;
{
initrd_start = 0;
if (!grub_errno)
grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
argv[0]);
goto fail;
}
initrd_end = initrd_start + size;
@ -309,28 +314,15 @@ fail:
return grub_errno;
}
static void *
static grub_err_t
load_dtb (grub_file_t dtb, int size)
{
void *fdt;
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"));
fdt = grub_malloc (size);
if (!fdt)
return NULL;
if (grub_file_read (dtb, fdt, size) != size)
{
grub_free (fdt);
return NULL;
}
if (fdt_check_header (fdt) != 0)
{
grub_free (fdt);
return NULL;
}
return fdt;
grub_fdt_set_totalsize (fdt_addr, size);
return GRUB_ERR_NONE;
}
static grub_err_t
@ -338,7 +330,6 @@ grub_cmd_devicetree (grub_command_t cmd __attribute__ ((unused)),
int argc, char *argv[])
{
grub_file_t dtb;
void *blob;
int size;
if (argc != 1)
@ -346,25 +337,34 @@ grub_cmd_devicetree (grub_command_t cmd __attribute__ ((unused)),
dtb = grub_file_open (argv[0]);
if (!dtb)
return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("failed to open file"));
goto out;
size = grub_file_size (dtb);
if (size == 0)
goto out;
blob = load_dtb (dtb, size);
if (!blob)
return GRUB_ERR_FILE_NOT_FOUND;
{
grub_error (GRUB_ERR_BAD_OS, "empty file");
goto out;
}
#ifdef GRUB_MACHINE_EFI
boot_data = (grub_addr_t) grub_efi_allocate_loader_memory (LINUX_FDT_PHYS_OFFSET, size);
fdt_addr = grub_efi_allocate_loader_memory (LINUX_FDT_PHYS_OFFSET, size);
if (!fdt_addr)
{
grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("memory allocation failed"));
goto out;
}
#else
boot_data = LINUX_FDT_ADDRESS;
fdt_addr = (void *) LINUX_FDT_ADDRESS;
#endif
grub_dprintf ("loader", "Loading device tree to 0x%08x\n",
(grub_addr_t) boot_data);
fdt_move (blob, (void *) boot_data, fdt_totalsize (blob));
grub_free (blob);
(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
@ -372,8 +372,6 @@ grub_cmd_devicetree (grub_command_t cmd __attribute__ ((unused)),
*/
machine_type = ARM_FDT_MACHINE_TYPE;
return GRUB_ERR_NONE;
out:
grub_file_close (dtb);
@ -391,9 +389,7 @@ GRUB_MOD_INIT (linux)
cmd_devicetree = grub_register_command ("devicetree", grub_cmd_devicetree,
0, N_("Load DTB file."));
my_mod = mod;
firmware_boot_data = firmware_get_boot_data ();
boot_data = (grub_addr_t) NULL;
fdt_addr = (void *) firmware_get_boot_data ();
machine_type = firmware_get_machine_type ();
}

99
include/grub/fdt.h Normal file
View file

@ -0,0 +1,99 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2013 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GRUB_FDT_HEADER
#define GRUB_FDT_HEADER 1
#include <grub/types.h>
#define FDT_MAGIC 0xD00DFEED
typedef struct {
grub_uint32_t magic;
grub_uint32_t totalsize;
grub_uint32_t off_dt_struct;
grub_uint32_t off_dt_strings;
grub_uint32_t off_mem_rsvmap;
grub_uint32_t version;
grub_uint32_t last_comp_version;
grub_uint32_t boot_cpuid_phys;
grub_uint32_t size_dt_strings;
grub_uint32_t size_dt_struct;
} grub_fdt_header_t;
#define grub_fdt_get_header(fdt, field) \
grub_be_to_cpu32(((const grub_fdt_header_t *)(fdt))->field)
#define grub_fdt_set_header(fdt, field, value) \
((grub_fdt_header_t *)(fdt))->field = grub_cpu_to_be32(value)
#define grub_fdt_get_magic(fdt) \
grub_fdt_get_header(fdt, magic)
#define grub_fdt_set_magic(fdt, value) \
grub_fdt_set_header(fdt, magic, value)
#define grub_fdt_get_totalsize(fdt) \
grub_fdt_get_header(fdt, totalsize)
#define grub_fdt_set_totalsize(fdt, value) \
grub_fdt_set_header(fdt, totalsize, value)
#define grub_fdt_get_off_dt_struct(fdt) \
grub_fdt_get_header(fdt, off_dt_struct)
#define grub_fdt_set_off_dt_struct(fdt, value) \
grub_fdt_set_header(fdt, off_dt_struct, value)
#define grub_fdt_get_off_dt_strings(fdt) \
grub_fdt_get_header(fdt, off_dt_strings)
#define grub_fdt_set_off_dt_strings(fdt, value) \
grub_fdt_set_header(fdt, off_dt_strings, value)
#define grub_fdt_get_off_mem_rsvmap(fdt) \
grub_fdt_get_header(fdt, off_mem_rsvmap)
#define grub_fdt_set_off_mem_rsvmap(fdt, value) \
grub_fdt_set_header(fdt, off_mem_rsvmap, value)
#define grub_fdt_get_version(fdt) \
grub_fdt_get_header(fdt, version)
#define grub_fdt_set_version(fdt, value) \
grub_fdt_set_header(fdt, version, value)
#define grub_fdt_get_last_comp_version(fdt) \
grub_fdt_get_header(fdt, last_comp_version)
#define grub_fdt_set_last_comp_version(fdt, value) \
grub_fdt_set_header(fdt, last_comp_version, value)
#define grub_fdt_get_boot_cpuid_phys(fdt) \
grub_fdt_get_header(fdt, boot_cpuid_phys)
#define grub_fdt_set_boot_cpuid_phys(fdt, value) \
grub_fdt_set_header(fdt, boot_cpuid_phys, value)
#define grub_fdt_get_size_dt_strings(fdt) \
grub_fdt_get_header(fdt, size_dt_strings)
#define grub_fdt_set_size_dt_strings(fdt, value) \
grub_fdt_set_header(fdt, size_dt_strings, value)
#define grub_fdt_get_size_dt_struct(fdt) \
grub_fdt_get_header(fdt, size_dt_struct)
#define grub_fdt_set_size_dt_struct(fdt, value) \
grub_fdt_set_header(fdt, size_dt_struct, value)
int grub_fdt_check_header (void *fdt, unsigned int size);
int grub_fdt_find_subnode (const void *fdt, unsigned int parentoffset,
const char *name);
int grub_fdt_add_subnode (void *fdt, unsigned int parentoffset,
const char *name);
int grub_fdt_set_prop (void *fdt, unsigned int nodeoffset, const char *name,
const void *val, grub_uint32_t len);
#define grub_fdt_set_prop32(fdt, nodeoffset, name, val) \
({ \
grub_uint32_t _val = grub_cpu_to_be32(val); \
grub_fdt_set_prop ((fdt), (nodeoffset), (name), &_val, 4); \
})
#endif /* ! GRUB_FDT_HEADER */